pax_global_header00006660000000000000000000000064151071115320014506gustar00rootroot0000000000000052 comment=f043ac898105a5b87da92226813fda96d76050da yt-project-yt-f043ac8/000077500000000000000000000000001510711153200146525ustar00rootroot00000000000000yt-project-yt-f043ac8/.codecov.yml000066400000000000000000000003001510711153200170660ustar00rootroot00000000000000comment: false coverage: status: project: default: threshold: 1% patch: default: threshold: 100% codecov: token: 4c73bd46-5ea2-4c6f-8dd1-8422f5490455 yt-project-yt-f043ac8/.coveragerc000066400000000000000000000003611510711153200167730ustar00rootroot00000000000000[run] plugins = Cython.Coverage branch = True [report] omit=*.yml */api.py */__init__.py */__config__.py */tests/* yt/analysis_modules/* yt/mods.py yt/utilities/fits_image.py yt/utilities/lodgeit.py ignore_errors = True yt-project-yt-f043ac8/.git-blame-ignore-revs000066400000000000000000000031151510711153200207520ustar00rootroot00000000000000# transition to isort 7edfcee093cca277307aabdb180e0ffc69768291 81418e459f16c48d6b7a75d6ef8035dfe9651b39 60f670d75a23a6d094879437a8df455a66acbeaf 556636e64712a4e161b1d09aeba5833540d05994 # transition to black ebadee629414aed2c7b6526e22a419205329ec38 6f0e37c4b926f1411b14bd35da85c7e7e27d0cd9 # update to black 22.1.0 4036201ab59ac4072defa416ed610c200556b49f # automated trailing whitespace removal 3ee548b04a41dfbc009921c492fba6a0682651ca # converting to f-strings ad898e8e3954bc348daaa449d5ed73db778785e9 ef51ad5199692afcf1a8ab491aa115c00c423113 323ac4ddd4e99d6b951666736d4e9b03b6cfa21e f7445f02022293f1b089cd8907000301516354bf # f-strings, black & isort on doc e6276f25fc5a570d886b6bbdaa7e3062da2d35ca # fix EOF 5e5ce1487f9b04344d297b7901cf5f82c6fee3cb # fix trailing whitespace 12068b7a38ec17cb3d83baca32c1dc8d07613f9a # auto-fixes from pre-commit f3a0720bb713aead0f0d50888f2176d20fe02ef4 7931b96a90c20d835a4dc96b81019382f9461351 # legacy conversion commits authored by "convert-repo" b7112d4ccbcd309ae2c5c2ee0f9352466a0cecf2 33f3988bbfa97399114e3df9ac491c828ae59a8c cae222aec845d0b400e2aa2804e75b2adef17ccd 993162d6fb38f55b0bf372da1c3b984ee2d4f085 ef783151bfd7c6777fa25e9e06f95fe47653b3aa 063e2fb630932cbdcbbbdba603c100a37e7e40f6 279b0551ccc9d9d4c114904b94bc705381c61105 # apply linting to ipynb files ec8bb45ea1603f3862041fa9e8ec274afd9bbbfd # auto upgrade typing idioms from Python 3.8 to 3.9 4cfd370a8445abd4620e3853c2c047ee3d649fd7 # migration: black -> ruff-format 3214662dc7d53f7ac56b3589c5a27ef075f83ab4 # auto upgrade syntax from Python 3.9 to 3.10 e86625e780b77a42aedad711e5a9a945ce650974 yt-project-yt-f043ac8/.github/000077500000000000000000000000001510711153200162125ustar00rootroot00000000000000yt-project-yt-f043ac8/.github/ISSUE_TEMPLATE.md000066400000000000000000000030671510711153200207250ustar00rootroot00000000000000 ### Bug report **Bug summary** **Code for reproduction** ```python # Paste your code here # # ``` **Actual outcome** ``` # If applicable, paste the console output here # # ``` **Expected outcome** **Version Information** * Operating System: * Python Version: * yt version: * Other Libraries (if applicable): yt-project-yt-f043ac8/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000024501510711153200220140ustar00rootroot00000000000000 ## PR Summary ## PR Checklist - [ ] New features are documented, with docstrings and narrative docs - [ ] Adds a test for any bugs fixed. Adds tests for new features. yt-project-yt-f043ac8/.github/config.yml000066400000000000000000000021461510711153200202050ustar00rootroot00000000000000# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome # Comment to be posted to on first time issues newIssueWelcomeComment: > Hi, and welcome to yt! Thanks for opening your first issue. We have an issue template that helps us to gather relevant information to help diagnosing and fixing the issue. # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome # Comment to be posted to on PRs from first time contributors in your repository newPRWelcomeComment: > Hi! Welcome, and thanks for opening this pull request. We have some guidelines for new pull requests, and soon you'll hear back about the results of our tests and continuous integration checks. Thank you for your contribution! # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge # Comment to be posted to on pull requests merged by a first time user firstPRMergeComment: > Hooray! Congratulations on your first merged pull request! We hope we keep seeing you around! :fireworks: # It is recommend to include as many gifs and emojis as possible yt-project-yt-f043ac8/.github/dependabot.yml000066400000000000000000000004721510711153200210450ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: pip directory: /requirements schedule: interval: monthly groups: actions: patterns: - '*' - package-ecosystem: github-actions directory: /.github/workflows schedule: interval: monthly groups: actions: patterns: - '*' yt-project-yt-f043ac8/.github/mergeable.yml000066400000000000000000000007651510711153200206700ustar00rootroot00000000000000# config file for mergeable: https://github.com/mergeability/mergeable version: 2 mergeable: - when: pull_request.* validate: - do: title must_exclude: regex: ^\[?WIP\b message: "WIP pull requests can't be merged." - do: label must_include: regex: 'bug|enhancement|new feature|docs|infrastructure|dead code|refactor' message: "Please label this pull request with one of: bug, enhancement, new feature, docs or infrastructure." yt-project-yt-f043ac8/.github/workflows/000077500000000000000000000000001510711153200202475ustar00rootroot00000000000000yt-project-yt-f043ac8/.github/workflows/bleeding-edge.yaml000066400000000000000000000054511510711153200236130ustar00rootroot00000000000000name: CI (bleeding edge) # this workflow is heavily inspired from pandas, see # https://github.com/pandas-dev/pandas/blob/master/.github/workflows/python-dev.yml # goals: check stability against # - dev version of Python, numpy, matplotlib, and unyt # - Cython and pytest pre-releases # - building with future pip default options on: push: branches: - main pull_request: paths: - .github/workflows/bleeding-edge.yaml schedule: # run this every Wednesday at 3 am UTC - cron: 0 3 * * 3 workflow_dispatch: jobs: build: runs-on: ubuntu-latest name: "tests with bleeding-edge crucial deps" timeout-minutes: 60 concurrency: # auto-cancel any in-progress job *on the same branch* group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true steps: - name: Checkout repo uses: actions/checkout@v5 - name: Set up Python (newest testable version) uses: actions/setup-python@v6 with: # this version should be upgraded as often as possible, typically once a year when # Cython, numpy and matplotlib are known to be compatible python-version: '3.14' allow-prereleases: true - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade setuptools wheel python -m pip install --pre --only-binary ":all:" numpy matplotlib Cython \ --extra-index https://pypi.anaconda.org/scientific-python-nightly-wheels/simple python -m pip install --pre ewah-bool-utils python -m pip install git+https://github.com/yt-project/unyt.git python -m pip install git+https://github.com/pytest-dev/pytest.git - name: Build # --no-build-isolation is used to guarantee that build time dependencies # are not installed by pip as specified from pyproject.toml, hence we get # to use the dev version of numpy at build time. run: | python -m pip -v install -e .[test] --no-build-isolation - run: python -m pip list - name: Run Tests run: | yt config set --local yt log_level 50 # Disable excessive output pytest yt -vvv --color=yes create-issue: if: ${{ failure() && github.event_name == 'schedule' }} needs: [build] permissions: issues: write runs-on: ubuntu-latest name: Create issue on failure steps: - name: Create issue on failure uses: imjohnbo/issue-bot@v3 with: title: 'TST: Upcoming dependency test failures' body: | The weekly build with future dependencies has failed. Check the logs https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} pinned: false close-previous: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} yt-project-yt-f043ac8/.github/workflows/build-test.yaml000066400000000000000000000126231510711153200232130ustar00rootroot00000000000000name: Build and Test on: push: branches: - main pull_request: workflow_dispatch: defaults: run: shell: bash env: HOMEBREW_NO_AUTO_UPDATE: 1 jobs: build: name: "${{ matrix.tests-type }} tests: py${{ matrix.python-version }} on ${{ matrix.os }} (${{ matrix.test-runner }})" strategy: # run all tests even if e.g. image tests fail early fail-fast: false matrix: os: - ubuntu-latest - macos-latest python-version: ['3.13'] dependencies: [full] tests-type: [unit] test-runner: [pytest] venv-loc: [bin] include: - os: windows-latest python-version: '3.13' dependencies: full tests-type: unit test-runner: pytest venv-loc: Scripts - os: ubuntu-22.04 python-version: '3.10.4' dependencies: minimal tests-type: unit test-runner: pytest venv-loc: bin - os: ubuntu-latest # this job is necessary for non-answer, 'yield' based tests # because pytest doesn't support such tests python-version: '3.10' dependencies: full tests-type: unit test-runner: nose venv-loc: bin - os: ubuntu-latest # answer tests use 'yield', so they require nose # they are also attached to a specific, occasionally updated, Python version # but it does *not* have to match the current minimal supported version python-version: '3.10' dependencies: full tests-type: answer test-runner: nose venv-loc: bin - os: ubuntu-latest # minimal tests with latest Python and no optional dependencies python-version: '3.x' dependencies: '' tests-type: unit test-runner: pytest venv-loc: bin runs-on: ${{ matrix.os }} concurrency: # auto-cancel any in-progress job *on the same branch* group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.tests-type }}-py${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.test-runner }} cancel-in-progress: true steps: - name: Set up Python uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Checkout repo (bare) if: matrix.tests-type != 'answer' uses: actions/checkout@v5 - name: Checkout repo (with submodules) if: matrix.tests-type == 'answer' uses: actions/checkout@v5 with: submodules: true - name: Install dependencies and yt shell: bash env: dependencies: ${{ matrix.dependencies }} run: | python -m venv .venv source .venv/${{matrix.venv-loc}}/activate source ./tests/ci_install.sh - name: Install and patch nosetest if: matrix.test-runner == 'nose' run: | source .venv/${{matrix.venv-loc}}/activate python -m pip install -r nose_requirements.txt find .venv/lib/python${{matrix.python-version}}/site-packages/nose -name '*.py' \ -exec sed -i -e s/collections.Callable/collections.abc.Callable/g '{}' ';' - name: Show final env run: | source .venv/${{matrix.venv-loc}}/activate python -m pip list - name: Run Unit Tests (pytest) if: matrix.test-runner == 'pytest' run: | source .venv/${{matrix.venv-loc}}/activate pytest yt --color=yes - name: Run Tests (nose) if: matrix.test-runner == 'nose' run: | source .venv/${{matrix.venv-loc}}/activate cat nose_ignores.txt | xargs python -m nose -c nose_unit.cfg --traverse-namespace image-tests: name: Image tests runs-on: ubuntu-latest concurrency: # auto-cancel any in-progress job *on the same branch* group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true steps: - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.11' - name: Checkout repo (with submodules) uses: actions/checkout@v5 with: submodules: true - name: Install dependencies and yt shell: bash env: dependencies: 'cartopy' run: | source ./tests/ci_install.sh - run: python -m pip list - name: Run Image Tests run: | pytest yt --color=yes --mpl -m mpl_image_compare \ --mpl-generate-summary=html \ --mpl-results-path=pytest_mpl_results \ --mpl-baseline-path=tests/pytest_mpl_baseline \ -rxXs # show extra info on xfailed, xpassed, and skipped tests - name: Generate new image baseline if: failure() run: | pytest yt --color=yes --mpl -m mpl_image_compare \ --mpl-generate-path=pytest_mpl_new_baseline \ --last-failed # always attempt to upload artifacts, even # (and especially) in case of failure. - name: Upload pytest-mpl report if: always() uses: actions/upload-artifact@v5 with: name: yt_pytest_mpl_results path: pytest_mpl_results/* - name: Upload pytest-mpl baseline if: always() uses: actions/upload-artifact@v5 with: name: yt_pytest_mpl_new_baseline path: pytest_mpl_new_baseline/* if-no-files-found: ignore yt-project-yt-f043ac8/.github/workflows/type-checking.yaml000066400000000000000000000020021510711153200236570ustar00rootroot00000000000000name: type checking on: push: branches: - main pull_request: paths: - yt/**/*.py - pyproject.toml - requirements/typecheck.txt - .github/workflows/type-checking.yaml workflow_dispatch: jobs: build: runs-on: ubuntu-latest name: type check timeout-minutes: 60 concurrency: # auto-cancel any in-progress job *on the same branch* group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true steps: - name: Checkout repo uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v6 with: # run with oldest supported python version # so that we always get compatible versions of # core dependencies at type-check time python-version: '3.10' - name: Build run: | python3 -m pip install --upgrade pip python3 -m pip install -e . -r requirements/typecheck.txt - run: python -m pip list - name: Run mypy run: mypy yt yt-project-yt-f043ac8/.github/workflows/wheels.yaml000066400000000000000000000073271510711153200224330ustar00rootroot00000000000000name: Build CI Wheels on: push: branches: - main - stable tags: - 'yt-*' pull_request: paths: - '.github/workflows/wheels.yaml' - MANIFEST.in - pyproject.toml - setup.py - setupext.py workflow_dispatch: jobs: build_wheels: name: Build ${{ matrix.select }}-${{ matrix.archs }} wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest archs: x86_64 select: '*manylinux*' id: manylinux_x86_64 - os: ubuntu-latest archs: x86_64 select: '*musllinux*' id: musllinux_x86_64 - os: macos-latest archs: x86_64 select: '*' id: macos_x86_64 - os: macos-latest archs: arm64 select: '*' id: macos_arm64 - os: windows-latest archs: AMD64 select: '*' id: windows_AMD64 steps: - name: Checkout repo uses: actions/checkout@v5 - name: Build wheels for CPython uses: pypa/cibuildwheel@v3.2.1 with: output-dir: dist env: CIBW_ARCHS: ${{ matrix.archs }} CIBW_BUILD: ${{ matrix.select }} - uses: actions/upload-artifact@v5 with: name: wheels-${{ matrix.os }}-${{ matrix.id }} path: ./dist/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.10' - name: Build sdist run: pipx run build --sdist - name: Test sdist run: | python -m pip install "$(echo dist/*.tar.gz)[test]" python -m pip list project_dir=$(pwd) cd ../../ pytest -c $project_dir/pyproject.toml --rootdir . --color=yes --pyargs yt - name: Upload sdist uses: actions/upload-artifact@v5 with: name: sdist path: dist/*.tar.gz check_manifest: name: Check MANIFEST.in runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v5 with: submodules: true - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.13' - name: install check-manifest run: | python -m pip install --upgrade pip python -m pip install check-manifest - name: Install build time dependencies shell: bash # keep in sync with pyproject.toml run: | python -m pip install Cython>=3.0.3 python -m pip install numpy>=2.0.0 python -m pip install ewah-bool-utils>=1.2.0 python -m pip install --upgrade setuptools - name: build yt shell: bash run: | python -m pip install --no-build-isolation . - name: run check-manifest run: check-manifest -vvv deploy: name: Publish to PyPI needs: [build_wheels, build_sdist, check_manifest] runs-on: ubuntu-latest # upload to PyPI on every tag starting with 'yt-' if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/yt-') steps: - name: Download sdist uses: actions/download-artifact@v6 with: name: sdist path: dist - name: Download wheels uses: actions/download-artifact@v6 with: path: dist pattern: wheels-* merge-multiple: true - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@v1.13.0 with: user: __token__ password: ${{ secrets.pypi_token }} yt-project-yt-f043ac8/.gitignore000066400000000000000000000066231510711153200166510ustar00rootroot00000000000000build yt.egg-info __config__.py freetype.cfg hdf5.cfg png.cfg rockstar.cfg yt_updater.log yt/frontends/artio/_artio_caller.c yt/frontends/gamer/cfields.c yt/analysis_modules/halo_finding/rockstar/rockstar_groupies.c yt/analysis_modules/halo_finding/rockstar/rockstar_interface.c yt/analysis_modules/ppv_cube/ppv_utils.c yt/analysis_modules/photon_simulator/utils.c yt/frontends/ramses/_ramses_reader.cpp yt/frontends/ramses/io_utils.c yt/frontends/sph/smoothing_kernel.c yt/geometry/fake_octree.c yt/geometry/grid_container.c yt/geometry/grid_visitors.c yt/geometry/oct_container.c yt/geometry/oct_visitors.c yt/geometry/particle_deposit.c yt/geometry/particle_oct_container.c yt/geometry/particle_oct_container.cpp yt/geometry/particle_smooth.c yt/geometry/selection_routines.c yt/utilities/amr_utils.c yt/utilities/cython_fortran_utils.c yt/utilities/lib/autogenerated_element_samplers.c yt/utilities/kdtree/forthonf2c.h yt/utilities/libconfig_wrapper.c yt/utilities/spatial/ckdtree.c yt/utilities/lib/allocation_container.c yt/utilities/lib/alt_ray_tracers.c yt/utilities/lib/amr_kdtools.c yt/utilities/lib/basic_octree.c yt/utilities/lib/bitarray.c yt/utilities/lib/bounded_priority_queue.c yt/utilities/lib/bounding_volume_hierarchy.c yt/utilities/lib/contour_finding.c yt/utilities/lib/cykdtree/kdtree.cpp yt/utilities/lib/cykdtree/utils.cpp yt/utilities/lib/cyoctree.c yt/utilities/lib/depth_first_octree.c yt/utilities/lib/distance_queue.c yt/utilities/lib/element_mappings.c yt/utilities/lib/fnv_hash.c yt/utilities/lib/fortran_reader.c yt/utilities/lib/freetype_writer.c yt/utilities/lib/geometry_utils.c yt/utilities/lib/image_samplers.c yt/utilities/lib/image_utilities.c yt/utilities/lib/interpolators.c yt/utilities/lib/kdtree.c yt/utilities/lib/lenses.c yt/utilities/lib/line_integral_convolution.c yt/utilities/lib/embree_mesh/mesh_construction.cpp yt/utilities/lib/embree_mesh/mesh_intersection.cpp yt/utilities/lib/embree_mesh/mesh_samplers.cpp yt/utilities/lib/embree_mesh/mesh_traversal.cpp yt/utilities/lib/mesh_triangulation.c yt/utilities/lib/mesh_utilities.c yt/utilities/lib/pixelization_routines.cpp yt/utilities/lib/misc_utilities.c yt/utilities/lib/particle_kdtree_tools.cpp yt/utilities/lib/particle_mesh_operations.c yt/utilities/lib/partitioned_grid.c yt/utilities/lib/primitives.c yt/utilities/lib/origami.c yt/utilities/lib/particle_mesh_operations.c yt/utilities/lib/pixelization_routines.c yt/utilities/lib/pixelization_routines.cpp yt/utilities/lib/png_writer.c yt/utilities/lib/points_in_volume.c yt/utilities/lib/quad_tree.c yt/utilities/lib/ragged_arrays.c yt/utilities/lib/cosmology_time.c yt/utilities/lib/grid_traversal.c yt/utilities/lib/marching_cubes.c yt/utilities/lib/png_writer.h yt/utilities/lib/write_array.c yt/utilities/lib/partitioned_grid.c yt/utilities/lib/volume_container.c yt/utilities/lib/lenses.c yt/utilities/lib/image_samplers.c yt/utilities/lib/_octree_raytracing.cpp yt/utilities/lib/bounding_volume_hierarchy.cpp yt/utilities/lib/geometry_utils.cpp yt/utilities/lib/grid_traversal.cpp yt/utilities/lib/image_samplers.cpp yt/utilities/lib/marching_cubes.cpp yt/utilities/lib/misc_utilities.cpp yt/utilities/lib/partitioned_grid.cpp *.pyc *.pyd .*.swp *.so .idea/* tests/results/* doc/build/* doc/source/reference/api/generated/* doc/source/reference/api/yt* doc/cheatsheet.aux doc/cheatsheet.log doc/cheatsheet.pdf doc/_temp/* doc/source/quickstart/.ipynb_checkpoints/ dist .python-version answer_nosetests.xml yt-project-yt-f043ac8/.gitmodules000066400000000000000000000003711510711153200170300ustar00rootroot00000000000000[submodule "answer-store"] path = answer-store url = https://github.com/yt-project/answer-store branch = yt-4.0 [submodule "tests/pytest_mpl_baseline"] path = tests/pytest_mpl_baseline url = https://github.com/yt-project/yt_pytest_mpl_baseline yt-project-yt-f043ac8/.hgchurn000066400000000000000000000045401510711153200163140ustar00rootroot00000000000000stephenskory@yahoo.com = s@skory.us "Stephen Skory stephenskory@yahoo.com" = s@skory.us bear0980@gmail.com = yuan@astro.columbia.edu juxtaposicion@gmail.com = cemoody@ucsc.edu chummels@gmail.com = chummels@astro.columbia.edu jwise@astro.princeton.edu = jwise@physics.gatech.edu sam.skillman@gmail.com = samskillman@gmail.com casey@thestarkeffect.com = caseywstark@gmail.com chiffre = chiffre@posteo.de Christian Karch = chiffre@posteo.de atmyers@berkeley.edu = atmyers2@gmail.com atmyers = atmyers2@gmail.com drudd = drudd@uchicago.edu awetzel = andrew.wetzel@yale.edu David Collins (dcollins4096@gmail.com) = dcollins4096@gmail.com dcollins4096 = dcollins4096@gmail.com dcollins@physics.ucsd.edu = dcollins4096@gmail.com tabel = tabel@slac.stanford.edu sername=kayleanelson = kaylea.nelson@yale.edu kayleanelson = kaylea.nelson@yale.edu jcforbes@ucsc.edu = jforbes@ucolick.org biondo@wisc.edu = Biondo@wisc.edu samgeen@googlemail.com = samgeen@gmail.com fbogert = fbogert@ucsc.edu bwoshea = oshea@msu.edu mornkr@slac.stanford.edu = me@jihoonkim.org kbarrow = kssbarrow@gatech.edu kssbarrow@gmail.com = kssbarrow@gatech.edu kassbarrow@gmail.com = kssbarrow@gatech.edu antoine.strugarek@cea.fr = strugarek@astro.umontreal.ca rosen@ucolick.org = alrosen@ucsc.edu jzuhone = jzuhone@gmail.com karraki@nmsu.edu = karraki@gmail.com hckr@eml.cc = astrohckr@gmail.com julian3@illinois.edu = astrohckr@gmail.com aj@hckr.eml.cc = astrohckr@gmail.com cosmosquark = bthompson2090@gmail.com chris.m.malone@lanl.gov = chris.m.malone@gmail.com jnaiman@ucolick.org = jnaiman@cfa.harvard.edu jnaiman = jnaiman@cfa.harvard.edu migueld.deval = miguel.deval@gmail.com miguel@archlinux.net = miguel.deval@gmail.com slevy@ncsa.illinois.edu = salevy@illinois.edu malzraa@gmail.com = kellerbw@mcmaster.ca None = convert-repo dfenn = df11c@my.fsu.edu langmm = langmm.astro@gmail.com jmt354 = jmtomlinson95@gmail.com desika = dnarayan@haverford.edu Ben Thompson = bthompson2090@gmail.com goldbaum@ucolick.org = ngoldbau@illinois.edu ngoldbau@ucsc.edu = ngoldbau@illinois.edu NTAuthority@honeypot.fritz.box = anokfireball@poseto.de NTAuthority@guest053.fz-rossendorf.de = anokfireball@poseto.de NTAuthority@guest692.fz-rossendorf.de = anokfireball@poseto.de Fabian Koller = anokfireball@poseto.de Rafael Ruggiero = rafael.ruggiero@usp.br john.regan@helsinki.fi = john.a.regan@durham.ac.uk code@andre-bubel.de = a.huebl@hzdr.de yt-project-yt-f043ac8/.hgtags000066400000000000000000007657371510711153200161610ustar00rootroot00000000000000008b15bd5122e910e321316fae6122da9a46e10f svn.1636 009bca8c4c596f01526bc8159c90786e7c011d67 svn.347 00d699ef358745ba38f4381c35f2e7d5a6306161 svn.394 00e034fabfd073790aaa4ef82bbb666582ae9f57 svn.1265 01165f5c21dd23b8fadfb36a736c1af7f2f51bea svn.1687 011e9277ab04e996ac2d710f64cd361235f4103b svn.658 0157ff037651bf0146f57adaf95a173222a3937c svn.6 0161f0770fe9d9a6b45cfff81c6c948bf5ac0d23 svn.112 0173810f90aa32e9f41f822a74f86d7f9aff9406 svn.417 019c2ed6f48651f2dbcf680d35869da9aad5d990 svn.1007 01a6acd7b8084564c58060b14f54609e88f173bf svn.1483 01e8bd44c26797472f364c787dc5cce782d086e5 svn.1204 022d3c06297845aec35f5649a3237710d8b9f55f svn.1802 023b31f57812c07152cd34d79f1f9909cae1e556 svn.83 028e083af0708acaa2050533839115d8a69b9a0c svn.1696 02f2b6cd7ea1d2d2d357475f2204f2410cf892ab svn.1205 03164a14f6b23fc6b8bc7abc6c90ab9680740084 svn.646 03c59e910c318794f99952c90dd146dc7e01e197 svn.258 040638d12856f79baa0d005d14e11cd36ba8a331 svn.330 0484b002a76a99a55a13454b6ce17ef45400316b svn.1356 048721d682f8148428a77d82c59d904f3d9c8504 svn.884 04a629b15da3ff3678e362065cf884834bd02a22 svn.1197 04bff5f55cb9e853b6183000e065b0080bcea8dd svn.1002 04c2b901d8a05070295cbe351fa0c43291ef624c svn.1173 04c660cac68b46d16341e9c455dc372b1334efda svn.1224 04db5f9b3122cc2bf749cc2e53383eb86c853a51 svn.769 04dff7a15ba545a16a93f50d7b125a6fca0ed10f svn.1707 04e29cfda98d62bab16d2053c3b7a774b04298f0 svn.1445 059a6f91c2e580b60123f7ac45983801fbd512bc svn.1257 05a1599e4916c970d398b641bbdb3953f0907ea8 svn.8 05b44aec7b74826628f5bf03adec7d84511efc65 svn.1411 05f3e58bc4b9a0b136659a69df4f1d4d05d0702d svn.1613 060a01faf6ae89a6efb3f6d16fdfee0360e36ecd svn.1757 060cccf0ba19182d334bbdeb576d8f58bf0d71c7 svn.200 0637b5808587b7c6e5425330d61045a0ebfc3c7a svn.1035 0669ff70d50dc275904335d6e5560d8fba1bd524 svn.1014 0682073503fda2662e7a5d1fdf205787cec2ef29 svn.653 06d5806a711f394f6a5889da1214051550ff66c6 svn.802 06e806dbd816945b98448239023700b9ce1feb4f svn.410 06e83f7e374489e7e930bbb3233a7d48da2a4325 svn.930 0763601a5bcc18dffc999f8889aaa24a3954b855 svn.234 07780c9b1b42083cbf29fa4aad7195c65a7155cb svn.1044 0791309fea40c03510ad3513de4fddb084697d80 svn.1058 07981139bfd9fdf363c872de11db226c8431841a svn.1318 079f4596e62fc77247141ff779774a785061abfc svn.1633 07db277ec95af7cb9849096730666b335bcc9c5e svn.1094 07dddd86d8fe37233ea506155e3dea8e4280a38d svn.1579 07f45d81abfb32c883e9f29ff2d6ca442c62faac svn.968 083153346e4989561bf77e67bcf8b07d6d8ee0d7 svn.475 085616db39c365408b3b2713cec63cdc75c1b0d3 svn.189 08930d496c9b0e758bf53d5b8024ded0179cf44f svn.370 08954e47091923482a4ef97dde985554cc9a23df svn.288 08b7d403a90b550912cf513433a5d093d5745250 svn.332 08d3aad985e07d28e217cce7e8849b24d1c18ae3 svn.1435 08d7629cc529e025b1f85fb5d45326975f48b84b svn.45 08dca756db3206be21541d869dfccd5dc43247f9 svn.1379 08febc793acd54cf4b76bbeddcb79637f1553470 svn.1645 0927e0c91ca66d1d41500c9fd08cde5014ceacf8 svn.1139 0954054bc59272f7974c63cd77933ea344c64619 svn.744 0963334d761e9d8db2acf74639cb938c250c8302 svn.1495 099aa96010cdda52c5f2fc83d72e7e03275acd86 svn.1700 099b4e43b52a38fa49164d22726650111cb89f30 svn.1329 09b6347351e9fa337cddc10d39c929b497cc7634 svn.1778 09e730a2c4fb77abfff2ae0dc0e9ab753683815f svn.854 09f95c184d9e12dfdfdce597b3812f81976c68fd svn.898 09fd3ab58e68418a1cceb9db1938655723a8732d svn.1222 0a3cd2ca46aa4a913dc6cca250deba0aba2f7661 svn.536 0a661cfd8bcf95d37bcba79cd5eb0b8d194a4228 svn.24 0a70307856e719657be483e103df1fb3803e6344 svn.182 0a722d174c5e259d343b64af2dff46682815c84b svn.298 0a929e7c80049aa2dba28242c3cfa97b2e3a7bb1 svn.402 0a9a9685cb883fddbba2ad166cfec47924102134 svn.499 0a9e87864df2ff4434732ae7eba7734630cba360 svn.1192 0ae3ae67d23e4014f104007447a53bfe770fbbd8 svn.951 0b0aeb725d723795421475cd3bd285bb0c94c713 svn.1432 0b0be6afa3e6db0011dbc2d132c0f2ede7c1c905 svn.983 0b3713cd3b3dafc68054cbcfdaa80442c11ff172 svn.88 0b78bcc9c1b17e85cd5a57242d3657c2c1bb7a0c svn.559 0ba47ee1dc4abf6efc06cb04b5994c728572fefe svn.1178 0bb40f1e128c4eb6cfc42c4fc043f7cb54550de5 svn.1130 0bc0bdac8bc20d2fb314ae00ccad146883a3311f svn.725 0beb7e783548eb33c55ffe1ceb0c64fb895b511a svn.341 0bf4ca209b1f9fc18947bd604d05ac624094d8a6 svn.1304 0c004242b3f8c110502e4ea6ee1bdb24d6bc75cf svn.271 0c4173bd9d0801ed84dd884c76874bf96c66cc12 svn.720 0c561365081c119bd4d7240258909a38e2f4d03f svn.427 0c7909677b752a79eadcb4325aab9b34f0cb101d svn.1190 0c987873f61cce2b19b2f227520252c624e7f180 svn.123 0c9fa0cfd51283b0531891e486402110005dc0cc svn.1022 0cb628110a0948ef3b8d5f352695321800ec2ff2 svn.1060 0ce364970738c599e6f1aa27aebf59ddbde79faa svn.253 0cf48554d6a7d95cae64d5a28fe4ffdcdf6e5efb svn.1766 0d4a60cf51319720ecd77e7a58dbf59d1a018f45 svn.1017 0d53d3fa1c6aedc57a4e9b9683f0245f8ee1a65e svn.1492 0d5c2d29d28dbd71905afe6889d79c8339519beb svn.1026 0da3c07627aae61a53ca2a8fe5a728acd1831553 svn.1051 0da429b7799c4e9a251921e59006199ae6e58c29 svn.144 0e1919bee94c9b5095f334c9cd7caea4aa0dc4e6 svn.710 0e20a44102dafc5fcc23b4d2429e37a38b218282 svn.668 0e5d5039e575561eb8dc9c8afd89aca00d586489 svn.1730 0e5dd9c049a1f89c0d8821dc46f6d8aa29ed1341 svn.412 0e5f9e70d3a04d88f9b454d8777177aafc48a3a1 svn.64 0e81d2275949f038fd9874b0394e67976705021a svn.363 0e938dd99255dbb30683aa7eb73704a60aabcdbd svn.158 0e9c51767a9ebb496f2ac88bb1de995953b49b59 svn.122 0ec1bb8306a20d164ad38e02e7b7df60382ac77d svn.118 0ecf19fa27577dd5f386ad6153da79966eb19f04 svn.1027 0ed17274b6410a68dd2bf6d82499d5b79a7fbd60 svn.974 0ee8ebbbafaf6a2966dfaaf8e58c559832dd1797 svn.539 0ee914b3cb3b97c478c6e10a1fceda690b04af45 svn.522 0ef34494c8ed215eaeeb41775d671b3da55a23fc svn.445 0f0c7cb6975400e566e78aa88ea15dec8e937778 svn.447 0f1154a13b23e757639ace21e41596b061f50133 svn.661 0f15cd632305fe76dfc6d1639ba093c6a2cf652e svn.1605 0f214513456482b170e01e3226ac34b6e9cc7532 svn.1165 0f57be523b3ec9f88963f1964ab63c7e5f4def4b svn.1625 0f8d2d911aa1c061320825dafda295b56cca2933 svn.1400 0fd851c1be1b98520ac273b5ccfc6510a7593006 svn.761 0fdd63f1c8fd2a6063c0f92bc9024b7ac9ade49a svn.1157 0ff85e6c512a4d14e9dd71ea373efa0c76b24da0 svn.324 105de8e9c3b4083203db5dd507b2063b6ef9f1bd svn.738 1098a40cd1bf368573e2178218a4bada315b99a3 svn.979 111afe4ac34dd880df57aa7c32651d1150bbf625 svn.1765 11614afba0f7ba29930970da8a55708bdbc14254 svn.864 1162e6328a888cf4b1d83fe5c10aaec511a680c8 svn.129 118e1fd5a88e88e9b8efdfe273df0b8cb1a10838 svn.733 11a938db9e02a14bccf251c1de2f564844afa5a0 svn.579 120f50838cd6598bd4cdcef4e2bf87a22a502207 svn.1320 122a58cfd7b4842fbb9317ab01ba2c39f5b0c444 svn.285 127e39229e9a70084beb16bfde1bf1bde81e0429 svn.560 12b70ef667d79c49bce71cec7bfe6289bd073e24 svn.722 12db47fb546b07451b51c62a855e15977ccfbbff svn.1783 13335553c66d4f48e9ac2ec49cd064d1c0e0f1c7 svn.827 133f6efb6e1b4ab30e989c793a72123f76d8fc34 svn.830 1343353d1ff63c13432208a5b7c933e8c947e1c0 svn.1255 1356fca5f8ff55d7c3a8aff88e6519d5c5637f0f svn.403 13576ff5249f3cb5a0c41f6186850801c11295f9 svn.114 136169c5ee3d74488d2f08929d58d2919f053bd7 svn.829 1371fe7d04067853541990eb05c04b2cd8b0cce2 svn.858 137d069b382d7175cf5d425fae13d9f20a8d9005 svn.1433 13bc25d9c7e68124d74e7d3ff85dc0475a12cc37 svn.1366 143e18f3ce685a33208ce4f470b3ec79e1c2a5ab svn.385 149036420e91872c752c4ce518bdfeff70330a62 svn.1135 1490fcfb24c70f19f8821224994b546915f5b4b0 svn.1563 149c0fa98e192e90bed74344a8ae5ebbf407064d svn.120 14ac1c49a658eef1e9ee29e22fa2e0d4b91a3237 svn.704 14b6fa5edc2f5453e9e8ab8113930d53e961374f svn.1789 14bdaf98da0d90eb722eeaec6157de8d0d0408ac svn.1046 14f866bbf50c4cd99474e150129144f9c67c9ea2 svn.437 150f74c95526a7387bc828f957348029f71e61a6 svn.897 153daa22d893d844ae6797ce8b33bacf016b81f3 svn.91 159874fa0df3577327f99ed11df19c03bd7d9926 svn.487 15a63d9a7b7190464132e8f36820b1c69f121458 svn.786 15cac1fda9ddd07988f4b0a222d09bb89beda8cb svn.1279 164274e4466e0a8cabd81f8342ccebc918a5121a svn.477 165bddcbfa004dea70fabe80f82da668831760d0 svn.329 1665a35cd4ea1b29b6bd93b0d82d423bed317294 svn.541 166738006c3a058f301632619fbaa9fdd7800e2b svn.495 167dd46aa411490021352403f7e408ca176e640d svn.556 16876332fbaab496fa75d8b4cef80d32a096bf15 svn.1091 16ac4b5438f69ba8320cde2e7c86bf1e50696c1e svn.1249 17539cf7cec90818d553f47d103067a66b6ece5f svn.75 177a80a5c2cb7737fd16802e2623d72762e09a33 svn.1183 17853da5da9438d1549e0b7a74cc590c78102497 svn.1300 178f1ce70fbd58e636295d8d74de81c2e481046d svn.32 1790cffb14fc49234d98115217d574f918d05b90 svn.878 1799a24c3cc76b221815808e883ac018e7cd987a svn.428 17d411ced9321408ea900b0e5eb89b46c3f43eed svn.214 17e94e863ca4a724e2e0cd4ab12023872ec4a74c svn.759 17ffb91f1954a49e66d0809489b74d5f42abffa0 svn.104 181f4e0072c301c4fbb3146a1558fbf82ab8b4e7 svn.1270 183ed087726cb1158a7ada1fd9ea59e726dff4de svn.1259 18495205f69e0f05998f0760c9a3ebf1d58fd1a9 svn.1391 186b361ed7e6197c89f71dd5a32db8da0178cb89 svn.826 18880acd09ee8e6dae8fca4442c426bddec35784 svn.1037 189409797cf12cbefb0028a1f969149fdd7d17ed svn.1628 18a1dc2a911eeddea1fe71d83759f4eef9b73cc4 svn.709 18ea5b4ac63865d808f56b04b0c97a08fb314c58 svn.1132 190508aa312959c217130a0dabd459ac4b85c043 svn.1099 190731ac2ebeeed836dd407f8f6ffe966ec95016 svn.950 191e425284f01d40d1b4e1c7b40bd86611ff0fde svn.1401 19400f194230d1b999065ef031279aff724a0590 svn.1195 1959f7bb7ef09e346e60ffdefc72de8b56b8139e svn.1063 195a8ad57107ac668cb656f26cdbb6f8b96b9dc1 svn.1156 1974365b127a40e9e62b041b6252c902f204c0b7 svn.1640 19aa76db8a96347d67f1b07cd1fc8a1f26dce3b6 svn.1229 19d2296f62d8f2e2ebbc7f823608d60683ddbc65 svn.161 19e098d6fbf349a90d146cb8be3142e418851ba1 svn.393 1a29c93406b03f5f51bdfde9d1d8e71a5fd0a014 svn.1598 1a6fa6bbf1d839de617cf6d731f124780505c854 svn.1396 1aa14a655fceeb59dc35419dadaa3eb9b3ade3a9 svn.16 1af453e638d1761025e8dadd053d4ceb8d79e90e svn.167 1b0b000a008adecc82d6fbfb2ba683770140c849 svn.1422 1b0db0bf5599a565815ae8de98254b653afc8a94 svn.159 1b0f3eaf60133f5daabf8876df225d7e17a2d7ca svn.273 1b1fbe998eb3a689db2b2cead578f0bad2bee23a svn.855 1b2d2b328f1a2268014054a1a106131524b9a58e svn.1732 1b41f55e39f627f352939c4c40d223666cadd40f svn.108 1b7c2ab8c72587e372905ba952d301c02fcfa35d svn.191 1b7d38aae84dfc9a011ad76c2542c138f4bf720b svn.514 1b8a4c4629be438e8e48b7d5f471b61f7e224eeb svn.294 1bb32ee8b90599f4e01a894eb8f4e1edb7161782 svn.1036 1bf7e34e171d1a8758f2b7176b12ab995241c4cc svn.700 1c37aebefb76e9e26438089b588c1111f1b6d1d7 svn.796 1c40d66bae3c4bf0d1f101ffeb0acea168926cad svn.1762 1cb76ee3636da871dedcf57a83da3866fc73bbfd svn.1021 1cc9cf146664844e360b073dc070fa05d08eaf20 svn.679 1cf9d0e83cff3079412fd45aa2ee0cac66672588 svn.910 1d0c7035c1bacb1f256e0a47534c19aeffeee35d svn.946 1d26ca33c8b44c8c5e8524fed63736780a9e721d svn.1283 1d30cfbd7a6696dd86c635b3faa263e95194733f svn.991 1d3e14608e450f1c05fbbd63e3bae8fe8498531a svn.509 1d9947e7c68242f9a8cb1845c22199d3aabf2dd0 svn.1065 1da8c1d4b86bf7fe632bab0fa19bd5a0860b6414 svn.909 1db0d4260c049fa27e83c262405e37e672f09f70 svn.142 1dd91bfad08fe300ed3202e0b9885434069db507 svn.1446 1e39a8267b63e6c937d0d9bca0be7ba96e884d9a svn.1214 1e4ad371a3e51fc6cf8bff8832e307408ea23fb0 svn.1443 1e5b976a4d43a87b53f6a10ecea1556ede6abb89 svn.729 1e5bad3af0c62d39a4ff1c997c3d69ce6e93f739 svn.1082 1e6351ec0582687c7177e9cdca621c18d9c36df7 svn.932 1e6640c409dd5d809c24df8a1b296359ced079ab svn.1708 1e844764a83b00bae1a1990b8a30fbcac149de66 svn.1015 1ea258dafaa4ce328fb224cb7e04b670e3c0856f svn.1239 1eb85913bbe94398eb973722914de95b28a8f69a svn.902 1ec3baa5fbf66a9caca0a9b224563d5b28374b22 svn.380 1ecf758054d3328567440ee82fa62fe0c316deac svn.1790 1ee66345f10b4429ed1c5b5d3ddc7fd63f05c0ab svn.1710 1f122ef5ebd5f88e6cc8bae94fa39db423620120 svn.1233 1f182f60f8208c83a22f0d617941a4d1408b649f svn.376 1f2849d4a56fa5d7909ecf56a1c5643fe76d7e34 svn.113 1f583c98024f4582ff3b16447f439663b4ae287e svn.1702 1f8bab2426fb59f2f4243844626b3ca6b11d75ec svn.896 1f8c62426fffedfd9471ceedc1096174461c97e7 svn.215 1fd4998e77ffd91041d43840b7a70c1be34cc759 svn.611 1fd4b02732eb20bbf3c3f5c4c46d8533f14c166a svn.349 2048acb78d9f9e117a0841b6a9229f01f56b063f svn.972 204b732a1ad80ad9f6c2f6e904e418ff1556b23a svn.1244 2054ca11283e92918cf372404bf773e6c09ab140 svn.782 206c669b2af6d8c568531423ce447cfd2fd1e122 svn.96 208f087b08e1f353ea1d96d735e53c0d84a2fec2 svn.1218 20d3e642f40308d6ce2bf2352d19bd60046bd474 svn.30 20d866b7ac2534392e54ea71c724940a91e926c4 svn.386 20e6708ceec570278362d6d32ad64228e8d104af svn.774 2105424868d0ddfaac6c68356bcc296903e5e2c6 svn.1243 211283d83105ce19eb95f5c9f26b2b8e9577ab66 svn.756 212bf834fc7f2a96f407eaca2fc11107a28c7b95 svn.1061 21a01aef37253089ca9607afbe6bee41617cee94 svn.795 21ab4b3e629c084ba8f6e39568efc7cb0bea893c svn.1221 21bfc577a0793032cf3180f3210b80be465a9b47 svn.1597 21c80a55ccdfeee4164df15049b6ac883dd42886 svn.591 21ed3bd276caee7cad233138cd85a77985ebfdf4 svn.1184 224c097dde0c7544d7aebd0c7fc4f3b398ee4ead svn.564 2260c13777ed3b984596262a7abcf3d74949b74a svn.1112 2267dbefda0b97873e7e212a9ff953f123c79dbc svn.791 2283ca6161ff49a0a06f25cf2c4718d3f6a8dd6c svn.1461 22d90c022f15abbb36d598f0fc12634b7922da39 svn.1030 22dbd0b1ca710d8534f6bcdfc3d473b1d6c3e0d4 svn.325 22e6c80fe419953eec283f3f4583ed609c9f4d08 svn.1654 23262ad8a41ebca12807a1ca65b1cfd95c2d076d svn.1160 2384a711554a63d4cf9e354848065244b4731419 svn.1503 23a1a6bf68f52048a0831f583593073751306164 svn.1127 23e1b62af018845944937b5cca4a15866647e644 svn.1223 23fae50a1832b41d61b3b0a0a2fb1a8f476821b2 svn.666 245b211eedfa9ab0f96b24de757b65435075feac svn.1185 2476faf0f3ea9fb561ff3789d4324e755524f73a svn.1458 247ff6398810d16538afa9b8cba01c792edc416a svn.1068 24a8cd53314b5547ef2e524c77fee58d6334ec8e svn.1459 24afb3cb63a0a1d7f539c942e746a0746b73bcc5 svn.1427 24be36f110bc8c3fb2167e74f09f7a99f6d20803 svn.272 24c57902ed27f8cb3a4ca1aafc3b66fbd44fa68e svn.1407 24e7e09a8e48bf287b911e5e71243a3892427107 svn.913 25056d70ffd70f590ad36853beeab02ab05b6a90 svn.1303 25064dde309e79255dc027ccac12860b311f97da svn.1264 250cd77c08d85ebab76ecc9e4990cc33ee344767 svn.1293 257c5a7b6c8ef95ccf53da55f692f517a28e6342 svn.282 25d4c96964f04c8790e12ab8b8144939180ba462 svn.922 263dad5f5c2b1571208e7f52f8488c0f189e95a2 svn.1796 266d5d83cf04ff59050e884077f9dabb56b65b2d svn.179 267d7c34121d8fc3579ba5d148e7080074a0e927 svn.772 2724aaaa917db3e5d80d8bf547aa6be9f0f2d17e svn.401 2733c3d4b6355f77b6602b72999a006407d10e3a svn.571 278c2284d6f7e979a8f7260ea27bcc450303f8d1 svn.1337 27ba903abf81b7964ae2a50d051ba42183ae1461 svn.740 27d17ab221f275c243449bc575417a25a70474e4 svn.382 2815181de43f5b2991aaa55f3e1b86b03d57f892 svn.213 2824b91af84c42c2690c935dea1991d87b6d0302 svn.1576 28661086eb26791d5ed1817b47d2e810da311a98 svn.914 2884b80f3126f6cae7b874abf66b88c81db8db6a svn.243 2890691c8b0ce589f0c362b1867e594c3ec4fd0c svn.1424 28e7cbf47cf394681cdc554f46ec13f67d42d5d3 svn.513 28f9faaa116b4e0c8505883f73160aebecdd49a1 svn.35 29101d2bc32703f921c90b8c47ab679d3ff1efe7 svn.1600 29243d349999d43d02851a450f53f031bb40cbb8 svn.803 2952becb97669b5b69c1d2d9442df916f472f408 svn.1416 29694b1e1a5ff373db84659e392c64e74933fcba svn.249 296968b4e2bddcf37cdebb652b2eaf9d662a87d7 svn.1623 2977040634d83c6aa28ab1cce4ba9f397ff6f18f svn.63 298d12183d3757c6439d66ef136852f7fa5c30e5 svn.1451 29acc14771291d9456c9dfb72967a3232b50595f svn.1421 29b2b49c3210d1cb1b6400dba253957aa2fbf497 svn.1711 29bc4b09998c5e58e5e881617d6e4c0735bccf47 svn.526 29e4edf612fbbf9815d8d674f899d89bd19c72e6 svn.1397 29e77b0d7290dc0add9223c50a656208ca270bc4 svn.235 2a1293f17651f5a8b96b837366b06e7b366d4406 svn.1364 2a5783e9aef1edf1a2f5dc9da94b20ada176db28 svn.1614 2a6029d1a826032c2bb80d38023a6fd37e3ac895 svn.1181 2a74e5b6ed96bfab3a334059fc6fbfdbe4bda782 svn.583 2a9a084f0ec1c135287fc824291cf582e2763d1a svn.1808 2aea310b02d8eb288828b1e38457527d03a2150c svn.1202 2b0b1ad509c4507b1ee6d4d7215c1dc13f86d323 svn.562 2b3f5df5d18db2808c64b9fe865af6d081b1b1e0 svn.1351 2b53901a7520aa476ed751bd0269fee64c015043 svn.480 2b5da1b0e6f68f6b24eee859c6a5c073b39cce79 svn.1607 2b616bac66eb2e928e58fdcd260a263cc1e6af77 svn.821 2ba2575991fe70542ec0b1e6491a401220e707f5 svn.467 2bd4b3dbcdb6edb2b971bc78f584ec1534010a5c svn.1460 2bfef661b95619faa38099f97e303917441c9a44 svn.895 2c2e4f27039f9eed5ddf9d08fec08ae079f3e1d3 svn.1609 2c775f41d49b7525484622ccda6033e0497c2cef svn.1486 2cf4d4f200f58d1d4a38eb10f25a305da25dab74 svn.788 2cfc0b9e83e1bb5a71b274b1293932c25fecb268 svn.201 2dc572c5ba89d3e2f234adc0e90fe3c379dc556a svn.434 2e05790c7ff265722b5e610fa8b4c902dbfdbb81 svn.237 2e114ae2bedf49862289dee0df21fbc8961d58df svn.694 2e36f21bfbec38ffe5d016fc24a16fa8c08cf634 svn.1482 2e56f05a2f5c2d1affe804672a96d4a64112fab7 svn.1398 2e6c33866a213869ef4970cbb9a5fa4251246b41 svn.486 2e9604d280d1372305aa1e451fb0ff366908070a svn.227 2f3dc3382cf79dd18095a1d08ae7d86c5ee11fca svn.1242 2f7c6f7c38ca0b4a2d59246e98f0785af7c586eb svn.531 2f7c6f7c38ca0b4a2d59246e98f0785af7c586eb svn.532 2f7c6f7c38ca0b4a2d59246e98f0785af7c586eb svn.533 2fa342f70f1ab638e7ff6d29ba0c3f27fc136ca7 svn.614 2fbffc4b553feb03c2f36b1141e0f272478768e4 svn.994 2fc8ce9aeb98d5e20f7cd2bf04e74d717728b1ec svn.1228 2feee8280510e184223912daf17898a6b9bd8881 svn.1317 300b28b4879e45345ebeef6f215e8828074c6f22 svn.934 3020489da36639adddce2b555be63c0dd19bd838 svn.7 302bb5f79a4e895853d420e82d9ed656a71b9214 svn.1590 3059f8a36287ce425e801b8af84ed6135be18280 svn.1594 306126f9d0f1834277b17adc5b38ac567da555b7 svn.842 30738047b2640774652e1a5bb403c52589ed9747 svn.1232 3076d47821899c6ec96abd622a60ff6b6cda0352 svn.1572 307f6a434baf2c19ed2f04b81559adfc32a19829 svn.1237 30810691843db9af3ac30247b3cd2ee2983ae8cb svn.453 309bf66bbf164ad0d715097eeed41a115c50021a svn.293 30a8cf7bd40464cc8b769f88d51c8c1c3cc14763 svn.260 30c8d69035cd57735a3f86c6dae40e4a6cf5f5bc svn.1578 30c9e3ed1cd0af1220a397c058bed58c34d8acd9 svn.889 30d4b4532a3036cf9839ac248d10454337c49876 svn.1477 30e0ce89424861f1280edc2f0a40c4d6592b9119 svn.49 3122e32e962449ef55ae4dbc65478e5af779c4bd svn.1346 3234b6e46b7bc7a18bc8186d8645784f7a5ccb9c svn.408 326567793ef0406d889de032e525a66d1924f575 svn.464 326dc0e28cdffdab9e0cb8018b751a7f7741a93e svn.51 32a5f8a01d909e39b60f87146383831187ae4b7c svn.153 32d9ef9dafab130232dfdfd629de466a79ffa2cd svn.1748 33d50969dd9576bbdf7c28354d26c854cdc2b3cb svn.537 341577d087f3ea35d05ec6634bbcc956bdb20269 svn.527 342b79f69394e1ce02d5dd8c24788127aeafb88c svn.1560 34b94e7914142149e465a83e458b6c8afccd9a86 svn.166 3510fc0075849ac1601288b8cfd516cc359a86ee svn.1052 353817e819e90bf135e4577de32eb6fccee9670e svn.473 35575098e21b39de79637d8a698efe95f61ea750 svn.226 35609ba9b54cb9a4065d956fc359d49d6a1c3645 svn.1469 356c0e2a81b234b2d99794f680e7380b0e1987ad svn.1369 35759321ad5ba59bcc727870fa0b7634c0efc070 svn.372 35bb46b3e28fba203eb93a8f0904e70b015978b7 svn.1284 35c3413f27068c320d95f4201f7f0cd240d44cea svn.1360 35f42dba0eee8a79ef8221e9032072d7f922eabb svn.1660 3648f2ee0493f7538f15721cafb438e075da0a5f svn.768 366a30324964d2c88c2731b027861897d5c2d98c svn.496 366e5cd0fd14dc9192b82e7f5a18397d74e64314 svn.599 367338cde81885a08621a3b1359140a3fda977cf svn.582 3677c19e25bb9311ca1b714f7ad350347afbf323 svn.773 36b3012bbbc073ed5b2c0de776a2738593167bc6 svn.50 36b804344505a201df09da75721c99f6ba8a9264 svn.132 36df8de9d0bc05b7ab6382e3e497b3ff92d4d2ba svn.291 36fdd3794888b2d9feae5e5ab8392ffdaedcb1ed svn.985 37246737f6859e4117c9a2d6a1d13b2674678cf0 svn.843 3767f3d630ab1693ce5886653da52fe320be4432 svn.1806 37ad56d4f517181aeff99df835d5e5adb0f85e8e svn.1639 3864b1286cf26976ed94cf6a3ec4059edfe08575 svn.1662 3885cc7d11fcdbe9656d8c2148bc07a30cdbf494 svn.405 38e412a867fb28db8d00a4e88a37906c76aab33e svn.812 38f78a2da49df80bbb58119a235811c50aecdad1 svn.69 391ee7617763b5904068181ec383952cdd7ee142 svn.1006 3951ca57fc0dae3a992fb790654b04dacf71bd03 svn.86 396464816d2328b92330a78c22846c56e96a429e svn.1226 39c43000ec6db4e62ff01772d7521afb796a8af9 svn.155 39d85e6ced187b299c627269f0b6d52c691fb24d svn.1635 3a13d92a0679b693d25142b17cbae857c79c0b2d svn.1697 3a3a0158bafeb652c5e7a4f74552e180534d1572 svn.40 3a477287d9d2332aa9a924d8282256751bb60712 svn.916 3a8ff8b2db71d8537af42c5c5cb5ec968af75218 svn.13 3aae41cd34983023a70726fc5cf78e8ffd3badfb svn.151 3b037108ef0ea89914009142b1e8ecaf70e1121c svn.1367 3b1782228da51a8db7c41c35791cfb503ed28a94 svn.1537 3b51aaeb60e63b2235d486c3b01f08d6d7e99d7d svn.604 3b7885d4f4e8e6923fdac19ce7a13996a0d53f06 svn.71 3b9205676f5ac264154b4098908b8e0e81f03cdb svn.1106 3b9a7834cb3009820861484876f55918f2475053 svn.787 3beab6acbd602831fa8c3b0c7dcdb52b330f38f9 svn.682 3bf0dc8a1b82fedad25e742882e1da7c137a53c0 svn.176 3bfb0809df65d84e820c9d0ec5800b2569589a4a svn.390 3cd57220b85ed48e04157f86edd19f22cb2f11f3 svn.92 3d0e66a5066ff4fc4bce6c2717cb1d1ccc840391 svn.662 3d2e410265d5d695bc76081e4d8bd8c9cfa28777 svn.862 3d5d909133b1d65527b63a91ec0a331636775a88 svn.638 3d7c445b1418482e2b20e9332c3f9b948c33493f svn.589 3da7fe78c823506fa20fe57c772ad1842e922b72 svn.1225 3e2e7f6475694accf8d6c8b796cb063c3dfac0ac svn.547 3e41764d307c55c40e69c1d9fbb2240af170ef5f svn.551 3e77557675c97a0cedbe4863c54eb832568b3c8d svn.538 3e908dc26ae65cf3c9dc28c49421e578ae3fb49f svn.212 3e9992635c90bf6809a116a9a91933f950113b34 svn.859 3eb75d1abf615466c5b4687587d2d72463d8b7e7 svn.1561 3ebca2e17e8605067905976ce45aa33fd85a2175 svn.1389 3ec6641e57e5041411874bced46bc471cfa6f556 svn.420 3ed79db929a9ae296e569b6a0a662b62c03f8430 svn.554 3ef4c392b225abbaadbc9ec69a59eb6d38ae673b svn.1071 3f2c22dbcb10b0162c94d32df80a8592d8f7cab8 svn.1551 3f5d67c3b191ad30069da0a959492e85623437b3 svn.1133 3fa2f5f309318b5467f0bbeb5542095a393a0578 svn.219 3fb3e19746293cba3e51ee7dadef127b0064136e svn.871 3fcf225f835cf7b90d8854b50c994435bf9a0c7c svn.34 3fee01e8ac38487fdac22bffc36450a18035adf3 svn.361 4018fe8a6af67d663dcfdc4faf477b1d513388fb svn.781 4049858156fb8795c270d6815556fb10442580d2 svn.1322 4056ab55e28813eaf3ea4b702ef1c5980b680088 svn.1532 40c221438429013119d8d0c6ce987e354f6abb33 svn.1528 40d77e888c26f923e7731771b92d93aaf1a9228c svn.494 40e5005d6d7e1f70583840824cc1f80bb70640c6 svn.377 40e5eb8bfcf1e9f0c9b0ceae0f8a663afce2997f svn.409 40e8cd5a0a4e2d6d6c6a0fd53590d3e64cd08724 svn.648 410babe5b4166e31f8fc82cbc2d7e8c156fa1e4e svn.38 4118634b4393366efbd911b28f190d7260ce418d svn.1767 4163cad7070177da69b53cd4b9887615da6b50c7 svn.1497 416d80a781f033c2ca3cdb1243407ceec601875d svn.1278 417ad747dd1f8672e37a5e51d936dd1052d3713e svn.1273 41a5bc7c2e91022cc0b99bff3c27d028273f5c15 svn.880 41b920254644108c6280e941dcdec28f213e320c svn.504 41d0d10781c8fc39a11f8d863a6c60d9c73316c5 svn.1231 41e6266889a81500a983fcc598d30c3cd41b016b svn.870 4201883836c6384f77136fcea4899eac3da1112e svn.202 420ce4ef78b82b520f2a97ba35b27d792b9d8d29 svn.9 4213a3828fa82a2f928c979b6c094a14591f1359 svn.828 423219f35f50085b079baff29646c18928578bd6 svn.1358 4246c5028815bd713859ac7194531b0dafea0d8e svn.1072 426da8fd1b6f2d0fb1aacd860b83814b0e6d7e7f svn.736 427a362a641904fe9956779a75158137d13e648d svn.366 42aa893a70233f327826b6b88d2595ec58be068c svn.1176 4359c081ace30b9ee341a5e74af0060ca9625ee6 svn.1701 4382b85f7f2976cd95d044e5ce3200209556f46b svn.901 43b5b08e082d211c858fcefa0abc633fa2e2adbb svn.988 43c47f66811d1c3c54ebcbe645151a6938b51803 svn.1487 43f8a7df78eb3b1f54ab6557f32c72842e59c6f9 svn.1000 44482c79545e737aaa40ad828e2b8bc9c32bec7b svn.1666 4461b8732fbbf0ab6200a6fed0c56b995823ea53 svn.178 447813e1e520b5a4789ac3553331a36889724e73 svn.1143 4497b6c8a28f87678baffb8e5ea6a4ab076d3f60 svn.350 449832bda2e9a04c55df98bd146942a3f77bbe00 svn.1498 44bc0a7fdd004cceb19e556222ab6a22aaa10aa7 svn.174 44d880af55ff3e44daeb87c3b6cc5b3e16bf64e8 svn.1008 44edb74af00db2ffb08016f94b0cd399a5ccfd8d svn.85 4515bae5095328873b3ddf34bbd4ccc16a4275a4 svn.1705 451cbca5a3e5304928314da6573264ffe7e0e230 svn.406 4531a562f716d16bf1eba3f8bd14ea9c2dd8a2a4 svn.1381 45343776eb76b76496f01462fecfe65c4bc8ed8a svn.131 457bec224e4b18e0ba380b6956120aa9e55d61e2 svn.173 45a9d7e5fb62c1ba8ffebc8e5585e993b7bd310e svn.623 464e7e577c5c69cd2c709d7fc9dc8bd15dcb16b9 svn.639 4660ab2a5ad8048474c2dfdce74822da12cd5186 svn.1533 46b682802892389cb8d3b8f9b89584f6dc565d9c svn.1545 46cfc58ea9781625cd81fb960d2d6a59b96acce8 svn.1758 46f5d3f78178e6cfc069079e32f13aecb310f24f svn.259 4822375a0361431ea8760efdb9deb181d2418b92 svn.1661 48267b66ccee0e9c7618b7ff1ce0e33a33b9e376 svn.1319 4847223c05ae4e6a6aa1d144bed9a28463cdbbfd svn.1161 485ffe9198e1bf33c5c3714e80734763b772e098 svn.21 48637151be9336a68446115339761b22bfc74a79 svn.1426 488e52ad69c3f3004c4d88e7826894ec3a2a776b svn.469 489b2b01018eece9a3da626ba0ac2a5e5a5ee68b svn.1041 48d00abbf8977b912efe5a52bcf442e5ba0474c0 svn.207 48ee2d9c752ff8436f30f8ad270beccc5a34ef1e svn.1247 48fa57b1bee688c3523a5763a52d721dc2af6e0e svn.1678 49045125c9484e0a60487b1cc65a4db189e5cbc0 svn.750 49218e93b171aa3ff698c921d442b52bb150d487 svn.125 4969b297df682192273b2e7514dfd93b496a032c svn.1167 496d7770984285a5db947fbbdfe4ea77802a0631 svn.938 49712828f5b40038a56b3359ec73397344a413f3 svn.415 4981eb69b4f367a74842be377dfc8bbd4ca2b762 svn.1115 4982554bb736c2985de96401f31aa6e3d9fcb433 svn.1734 49bf13b03356b24d99eab44a38fdae7631a1c41b svn.1043 49c0c0aa33ab9f6222de994a30c1c8c9d71a5f50 svn.552 49e282f62ffaec2781f1c4b32a7ae145ad5e18da svn.185 49eb634fdea7222cd9b28dc9a57b13158077c999 svn.247 49f6e043889adeafb8b36acb4fe109dee6eea2d1 svn.1810 4a01a8ab2deaf4b6c368469064d86676974ca5e2 svn.1333 4a292bf63670c8a83efd41c939a7402235918b48 svn.596 4a2e5c12b4a23e7d9699c85d4e40b2b0cfbaed1b svn.33 4a9245b8d1230a4aeebc5b996401848ef2e8aae7 svn.1737 4aaeb47d11c0f86a6e2ca3c1c3711527e23bd284 svn.27 4aba19b0b4eb4c840aef00f9663fb1b825b48d6c svn.208 4af3e4b9f35b438cd71c732c287b05904b0fc508 svn.1109 4b6e066a9b733af6adf096779d72e14875afc15b svn.357 4ba988999535326cc253a3049470eefaedf87f90 svn.1722 4bbb3be5be5b43f84916368d762c83b0698dad1a svn.1454 4c3d3810975dc9a0948a37e033a3d049658bcfba svn.1743 4c5e134b91396e80b184dca51144f39f9560343f svn.1549 4c9e1ab18d0878e9e4c7d551c13cb68baef38bfb svn.1012 4cc9fa1449811b09112e1e97089be590b7444432 svn.1476 4cd40c4e44534c0f3a173874891b6dc192302cea svn.18 4cf5381992a384aabac16831800c7410ac6929a5 svn.115 4cff5bde76e2deac628cda176628a3cec5a66cc3 svn.645 4d3cd3e470113b37c3bac7d2052d79d7a097ec96 svn.246 4d58ac7afb5ee8358d6989989ac04602de8c0a45 svn.342 4d93b8fb4362acb2626035c0aa12516a81d90fbb svn.148 4d9628abe1955d19050a2d86dcaf0c4ecede96d7 svn.1077 4e25a3ec9fe26987dcf14d93f5f0b0ec07a449fb svn.502 4e7029901b08b617051901d95a383f6242eaa88b svn.1382 4e74cc5353eb286d9f6697636ceb4c35c02fbcb1 svn.1632 4e99c598a17d61bcda0604b2a9da8488bc429a2c svn.1695 4eecc5a5eb0ee949a906d633e73567aa9642e889 svn.1215 4ef9e46137e79825ad2edae673ab6ee70bc7b44a svn.146 4f283440ba805368be141e02ea6cf3ca1544efb5 svn.1241 4fb515e030cd9960c13e228064d8f532653213e8 svn.1134 4fe3cf7dcc79f8ef6cf60148a573008dbd1b5b7a svn.1402 503116d6f3e29bb6baccdd808054579fcf9cafa9 svn.1799 50728409da5a1c155ffafaa2d0f917332766ce91 svn.1749 508bc08810045e1cf1e6970513c9de8fa028846c svn.1430 50b1a45d4899fbe5b643fbcf780037b7edb25ca6 svn.1354 50f6b731870ba3ae773b772050bfb7d27db2e4c7 svn.1615 511379ab4cd6e5e5ee4144890f7f5343fb87aa4c svn.93 516801d12403aaeb3be7a1fa3418b4edee09198f svn.218 517a757f2e616abc924087c336c6a28192649050 svn.1209 520c515ac241d8ecb87692f4ae5c5f2ac5fa975e svn.835 521936dbc055716daa6df19efa7e34e857271f05 svn.216 523845d265b6f21192bf920428ac9a8d7dc066ee svn.903 5257ab69b04aba9315f60d276ba5854b2f182174 svn.1323 526d95c3398d2d8a7d6442693704b93542b1221c svn.1102 5280eb09b8f1cc2c84bb785b19ff807a47254d4b svn.1680 5287daeb9be1f9c97319242ad4d2c226e266341a svn.1101 530003732acb6b8004875550c44824850136bcf5 svn.451 5305e65e9aedf67da3f8028255e21497cbb844f9 svn.77 5340452bb3df18ee1befcb4daf62b3ec5b9c88c1 svn.66 5345173dbe7e26057de5a7fc46e787d47495cbd4 svn.1512 5355b66f4dab824d6a9dec27432b5993ff3d6ccf svn.545 5363b892e7a0402e9417eb7b3f9ce23ff6fc2f6c svn.686 5365f158410c88448e3bc294bb15823895be3b07 svn.435 53cb10663fef331a2252a4da6b96e7e41c5e7474 svn.1620 53dfcf3deef8c8fde2404b620fcb138fc6d2ec13 svn.1754 540e50771aa0b70808479222e27dd4239b3c8612 svn.879 5412bcfdf5df0e75f91f7c242f4f51a094efb62d svn.1004 542c2f66bc5d0cfce5272b1b4056f097b2950e24 svn.1573 5504d339253a7af01a6c9005afe29ca7cbe705f7 svn.5 551dde528c1e313b6ff50bf4d79e3b18e7ac0f31 svn.809 55e939ca948bfbb641d4ff5892a82280518cb0db svn.250 55fecb6fc0d3ae3f40cfeded90d1743f595a7732 svn.400 5616db9bc36e94343e2b92df68c4520d62b38cfe svn.60 5620cdedb5130bbdbdccef255791b98e83bafbef svn.558 563a941879b75768d6d73b60277c85964cdf3f60 svn.4 563fe5a94cabd170cc0cde3ae3d3989a6d1141f7 svn.1110 564a081accc74d3b805b393d5fa681607f45b916 svn.917 56931af41cceed819df913b9b2493682405e941f svn.1098 56abf9195ec818eaa2e835c5d6e5eab7b8f86026 svn.1301 56b1bec77233c6a2931ae689f41cae9653df9256 svn.665 571cb5de9bb60035283627328e04e71c7b4de775 svn.1150 5740382d416f07ccff6d90fae77d808657351836 svn.438 575c627130dc876053158837c6256830f3fb42d1 svn.850 58132b46dc40cb7e24fe03d86014f3075df28110 svn.1728 582f665ff1e445d2fd9d13ab5b4d664e870ac93d svn.874 583d35ad9048b887d4c133101ae9c887bf6a3ee6 svn.629 586f3a9bab88e5bec339980ab203fa5158f77632 svn.1326 587722d62ac08a1bea1dc44bd3558ae627be6b13 svn.521 589441fe2a35347857e3eee6c4b34f6df60325b4 svn.106 58c0b0001072e37b6bba4cc3f388988c596ccc5f svn.193 58e27b76bcc5f805251ab9cb27b3259862b1e9a8 svn.82 58f2a83ce4bd569a69dec333810dabfbf6f3d4d1 svn.163 58f3151c7be6b192acc553c9231d671e11c45d49 svn.1760 58fdd26cbf5ac4b554a0cf795d21e137c9321734 svn.230 5910084f8704411c3302f85073b08323465cbb6b svn.1261 59394c9e003e1ade5b90330307f96520b8dd7f67 svn.1515 595b63f60ba812ff9063a78734ed181dfff09b83 svn.799 5977f55517263d38698d0fb3568e7e9d0865ae23 svn.987 59ee86c8657bf5db81aaf4051507e4c330d5d54e svn.362 59f08140ee87a987e11d5d5788f20f9e899817c1 svn.1627 5a007c385c4a933cb1efecbb1c5c189f479775d3 svn.945 5ab8c1db3dbdf9f6d302ef17da48942a289c1583 svn.1013 5b222b5000d46e6ea65c7d67d6fca5f3a6f5d483 svn.1070 5b636e141f7273bbe593163202da5ee626b65c75 svn.1388 5b82a0336225d5317d86d3cfd63571c3676598eb svn.256 5ba2e572a3f18a22cf11eb07f5c857856f8104ab svn.989 5bb22493e5e79a48fed30ff120d32233dbec294c svn.391 5bbaba649fbf8628a729bc6a208b791d2ec092a1 svn.680 5bd0a4b6acd292e5e40527a43aac64379c19fe48 svn.1410 5bfc0b24ec3c698ac3e0da0954df1f2d68404a86 svn.29 5c269386e9fb9dfbe0b3a622d0ac2a40eababe1b svn.954 5c3608dee30a07df0460ddac10b5ff333e021de5 svn.327 5c4990c765ac586cb341b41508300c5a88b1126d svn.592 5cea2a1eaaa054e8e4e204eb27117ab3215adb25 svn.659 5cf867e52b3db29da760ef52ce2f5e8e8211d3c2 svn.1535 5cfc7ef09092f692c0dd25a33260952db53afe3f svn.266 5d563977f72716f03f98049a416d315636723103 svn.621 5d6603bc222175178fc7da0c5668bc4c54ed3021 svn.1394 5d7dba2c1c644a3715f716d1ca260db24a768637 svn.275 5e047513c0ea6bd422ac2690e5b5dde28d7b0cc4 svn.1419 5e5997d5cb03c6c7181fb513b07850d524a5082e svn.1585 5ebb6b6af2ad27ce2eeec087b8963239a155725d svn.1669 5efc0b3122438f19ba247a468922513ff3dc3f4d svn.635 5f0b8e82d58651c951c356a705e3e4526ba8dbe4 svn.580 5f4ad86c159e3397e6187c9fd01f229fa72a819d svn.1604 5f5bda1a0b4af8978f5cd03fdba4d72d114ac1b5 svn.713 5f5ce1d0313d39aba65fb360c2b43b0f2b6a516a svn.1116 5f6ee3f0aaee445dfb9bb26c8dbef30bd4f2f63c svn.681 5f86cdef685f59a577fbf8b91ce8b6419ba83f1c svn.183 5fa4de6cb71b9e6b9827a25d7612fb4faa0a51f5 svn.966 5fd9f895384805dfd3ebc54e4b27f1acbe452064 svn.140 5fe392f15c2ad1970936acae1c40efa804a71276 svn.181 60864a7b7dce7563b202c1406adca49d761c6504 svn.1276 60d13dff6a0039f4fa6623a034bd94db7ee7b9ad svn.764 60d1b329e1a41b4518091df6131b84b0ef37ad90 svn.542 6131ac5ee202b715f9aa5851f86e1142ef531877 svn.1158 6146454a0fe14b8258bf030278a01db0f1df4404 svn.1033 61df1dd85a78eb81890b01fbe2fa91ef4315ecf7 svn.1593 61f2232514ecd36ae07e1e7d4c199c207de9f047 svn.190 61f44c6c06abdd3a459eaf4cb84e4ce5689f5b60 svn.845 6222bb8d5b37ae0132d8ebd7bd4b7a48db94e684 svn.1703 62631b24fc1de5dacd71f9e72db09763c3cf6268 svn.573 628d1287a686c1410b25f5911527f11700800d71 svn.1556 62aa37991851a45af59b39bca7a205454c62ff4b svn.1032 62bc90b08b309e14eafe31ce70ba0b4b50714a10 svn.67 62db9cb4103179bdc81fc4aa6dc2c06b51ecdc15 svn.117 630f4d0b6c7f16d54298d5d9950cdad53ed0cb6d svn.1251 63150727b1c7fecfa9ce6464779818683fb19487 svn.905 63283a4591bd46de9a1d814d068f68cc43a9c976 svn.1120 6374e52f7c0628aa7e52154913ccbd9fa9c24330 svn.279 6399da8dd8baccfe0d3c23e1a79e38e6c5830f30 svn.737 63b2f81eede89f8ffca37bfc9346336a1947b862 svn.1659 6404f6fc8d5b21a924d342f6acc3cea816f7f23f svn.1755 640a15ddafccbebe106d2b21793cea1c68946a49 svn.1055 6468c80e3d4a0ee4abea5b54c373e48d503ba593 svn.1683 646c70e7e5f886dd105d69a66acd1f5c98bcc5c0 svn.72 64a713646ea00b15ff2939c56b194137cac32e5b svn.442 64d34a6c43b5d5ced5ea8c59070b9c2c10995f8b svn.97 64eb4a5f8d329069fb4f3275806980653f0d2276 svn.229 64fc2c2c26c8db2336b1e0ed2b797f59a63a742d svn.784 654597a4199eed35dfa820947a59a7c18d723474 svn.1113 654ad8eff684ed6ca368a00610e5dc2ce86ae3ff svn.970 65602799686748be921aac98aa0c2ef5a1fffa2f svn.723 6593d04b9f7cbd1f04d6fd89f3a999c9090caa9d svn.1526 65b6222af795244499cd3c0202b5114dda0045e0 svn.1801 65bb67f50da97f1a23c3f36bb9b62496658e9548 svn.1570 65fd080af335c1b0e73c167521e24e9fc48c5d1d svn.1584 661648ac517628eb254a31e2bc703d92b999782e svn.1294 662560be753e9b4a9623791070baf0b86f1b84d0 svn.1488 6662340b2f89c68c21db7a8299a7082e1dfe8d76 svn.1727 667d55685a4a9007c7a142f2121fd1eee585b93b svn.727 6687e9fcf14eceda46be690d854e88c645ea5f04 svn.1691 67096d5c2f230606afb316251be252c698cdfe7b svn.609 6723708753a0619323dd98434f752422f0a02cb9 svn.1325 681fea5f217b1434801bf4693c5d891f0d0902d1 svn.883 6859b5230d62685f9a1be0e18f4f3e69b6afcaca svn.908 686a4b9d4f6b43f78a50735357253ab86c8c67b8 svn.1080 687342e70dd55711de85c904f60f3b15f291f60c svn.413 687bcd6b35ab8d1e106f0e92520641bb20c6d464 svn.1025 689b60640d60e1a36429e6f979a6cc06f2be0a11 svn.1686 68b5418c549309274ab2caf5872b1bcab3ef89ab svn.937 68c17c12fc55b70c72ff10571f1dd1b0bfec03ce svn.1207 68c3e7c4f2039b94b3b35150d1f5bd040081359f svn.1440 68d2052f11c58ed5274309bcb2fabdf46cbd15be svn.566 68eba7c3b1dc0b8f9a546acf7722c9b7223919f2 svn.236 68ffb81df06dfe917f1515bbf66ec957c53ee51a svn.520 692c0b3e97df1a6324a0a97dbe792bc602c499ce svn.172 697a25de00456d499e7e738e8fc8e7998621b277 svn.617 698232e5ae717bd73a966b3d7534de356230423d svn.801 699ed7fe298ed364faa6b6bfdd9bf07705f3ede6 svn.804 69af6d8252c18596851585164364b63034963663 svn.1311 6a4c7518d9d62ec15209325b3332b4c6be89801b svn.503 6a81cc9d3b086744e03f22ab687f2b3fae123b72 svn.1220 6a9c3420d3ee2c6319c5c3a754bdee278d1c9373 svn.130 6b089c0f0a634d5638c21a3194a385592ee0946c svn.1362 6ba57547250acbac6bcff8e2f8938e7e65125ff3 svn.605 6bbb4ccb07cd993eb5c92481dc46b1f73639c10a svn.717 6bf4f8d5eff7a64b6d905f0255ba9f79c492851b svn.948 6c1246ac86ccd1d01c56e80d256758b64837e72c svn.220 6c545b458dc785a1d22929fe38fc9ef122ed3e62 svn.918 6c6edf1ce01f47f9a93ced398c14e44e287a65df svn.1653 6c81db75b3713c0c889b70aca2e1f79ca358d0d1 svn.931 6c8b5837bc619afb424e773811a2812ce7e2d1b8 svn.619 6ca95e61d7bbf5829ffb21a4e53569ac9ed9109b svn.239 6cb0fd747334aadec07001c234477c6c432c8b9a svn.647 6cdeac9c78459431afd34a2889c791f8136caf91 svn.1694 6ce14ad1e51f11d3a12f9e87336a824027db1d5e svn.691 6ce695d22d29d5830eb088c3ed4e8b0686e8161c svn.919 6cf9a71903dd8c412769486fd3e49d883f0a662d svn.443 6d2a962a88f101661e49a204a2b927b1ff691f48 svn.678 6d3b895da46ff0df28d07ff1b44aeafab92420e1 svn.632 6d619bcb275f9b26471116f080ec40806fb39f57 svn.1287 6e103b5715f877a532d24467a72ce798e14d7917 svn.1406 6e1088ed75b3e709e1c1303d4a6174a14092e315 svn.650 6e36c78fa82cd595dffb857cfcc61974596ffda5 svn.1547 6e4b1b54fb1655d880c73c0b586fa9846e3e8406 svn.1688 6e7bbb959a509d936fec8de148068fd5eb4e0e65 svn.1587 6ea12918249c2998d5322490e2d10c2ebfb850b1 svn.837 6ea7649495f915e64fa6774b9c3432ccea684619 svn.381 6ec1218a11b3cceaea156fdc3925b95b5c16ea8b svn.915 6ef8fcc1c3cb89dadd48075eca10ecbf1521b343 svn.1081 6eff9c0a4e808a63f5a4ddcdfef919e04d289596 svn.493 6f38cb6673f788712881c4f21a4976347a4704b1 svn.1511 6f4eaaa712be7e5e560908b01de6e21a64dff80d svn.127 6f67d9ddf8d17e4274170ac479cd38e8eeedd5f9 svn.1142 6f984ee68a329be4627566d3cfb74e902b9478ef svn.46 6fc6d87835f7cda31110259830cb290751782550 svn.1641 702eaddefa978d541c1827b8aaccadd1fb936637 svn.433 70372e292703c1ee793ee744230c04aa2394616a svn.780 708164359b80fd2fd9ab08eda67d8cd12bcae4b7 svn.343 709dc1862b92b31553ccc62257d14a1e7d94df39 svn.923 70a1a0a74e029a222b621a0b51d34aa1eed96c95 svn.491 70b7809f00af3a82fbe52f4a31bbed429c57fedc svn.1403 70d83691afa7c302cf779c4318c155711f18a1c4 svn.1657 70e2a6cfac20b5db6fad69c089c04fe85c28ce66 svn.1539 711eff34e373cf3adb738ac08b5e3d6bef81167d svn.1349 712da1b22a9adcb3376d45aab846fea2863dc643 svn.677 71d855ee2ff5db8d2031e21ef1110f8d1e25d349 svn.947 71df7de83845443319030e411ca63311b94cb2b7 svn.1478 71ed0fd7b8b04dfb59e9d042ad5c16fe03e4603c svn.17 71f40c26df79f4b2abe46202b8702e1e7401f1cd svn.311 72142f3034df364a1b4c6744029df460e5dd08b7 svn.708 7237c3e3741bccf2b48c8761b29ec61f7a74140c svn.1453 72f6e65b00f97472581f40f699677f3295d02f74 svn.1274 735372855c47bebac5cefb53729706320c3e5db5 svn.933 73595cab8615e185936ee4f2f5ce8ffd0ba00193 svn.1093 736b7d80d82e8c49990daa69d6ee152c3f549964 svn.1246 7394cb16a2a56787d29fb2c1238cfd4375a32609 svn.578 73a5f43fbe5bf5a022fbaff1c1f681759b24caab svn.1665 74a4cd9f3ae216250fec79d31d996cf1ca77b84c svn.969 74cac79b667c492030da162ecd3d1a7dc67f1a08 svn.101 74cdf93273b4464daaeffab3a89d3e78da35bc0d svn.53 74e0d6b46fd1d62791d1733a06eb09e4b8fe831f svn.240 74f83c44f9a21237d01ab53bd7abb8b8c2d69e05 svn.1168 750a31f01a28f9d0b0d10acbb3ae2c44443484f0 svn.1562 75210b13d831d6480f07f3972ebe7efca4518663 svn.876 75309fa251bb4ff0900cbf37a584ec031ee33b16 svn.630 755d312a7136f6f5a569fdfaec07ad68bac257bf svn.1103 75a2b0dd253f65fe75245d39f99497dad64da56a svn.1085 75ac4861c335b9aff04c88be698b230090b03670 svn.839 75c736efedb4d64cdb5f027e91ed8ad21347dd2e svn.79 75d8c13524ccb9288d4da283aa7ba549b2a3abcd svn.1761 760e0159100ea5fefea7242935982079f2602887 svn.1794 761f86756a571580948a3150ff87221385366a3e svn.1558 7666c6bbca4e4e3919706326334cae0053f31b16 svn.766 766e39ca35babe04b4b3250a5b4f0376ecdef01d svn.1746 767a773a9e84eba48e07336691f67c1edbaf5548 svn.671 76979776bfc1ec51682e9b97fbeb9d17ab50d32a svn.335 76e08fa2a955c3e00c1fd0fee294d843aafdeaa5 svn.448 76fe7986694b008839ebbab7f57fa496fdcf6d25 svn.1189 7729282bc88011ed2fab9462b4b31098d0b83b82 svn.1079 772a2004987d3cf59b96d2d9b2975616af016e58 svn.296 7737bf992702e8ea55f9b8c9aa445ca679eafc9d svn.806 773914366850d4cdf88ba6357f438cd2427f4756 svn.1090 77480357e062086dfa46e00a21d21254509573ff svn.1010 7752741ad8b4c001ade2fe52d850be728c9a6d8f svn.576 7777f6ab1b807c8ea8374e6ce51c7e23d1a64a2b svn.735 77ad4648ed8d7caf993cbe9a0c9fa98cbd0c7f22 svn.1429 77faf7f8d8624cb1cc71a9abbb389264633f245c svn.1599 78684d161c28f74eb080c8e347cc07affe5e2811 svn.11 78714479ec4b1655ed63ab753e7eb64194bbce49 svn.1731 7888876baa70393e25dc7ee3f5d5fc0060cbfc69 svn.396 78c7a2cd4c6f48e839680c2e0768f61452a1d69a svn.925 78c9ba1b013b12576663205993f06b43339a75b6 svn.31 78eeba413131359509eea37964279674e88921bd svn.1664 78f011aadd008a23a3cf1c16e659a7bfd01f0b80 svn.1525 78f0aea62cd4526c40099b890429cda11414d684 svn.1213 79375d6353efca8288ef5673c48e7c465b8a72e2 svn.1003 79bc3d944298a10b1621e3dafeaa4a46f6d18069 svn.721 79cc0fb85cd94fb15ea9e2ae09c44c638355aa05 svn.43 79f2c675f9dbf256fd7c730bd00f742124be519e svn.126 7a38f6e17dc8c0350c274892ddd761bf5b35cca7 svn.310 7a4795f2964b41b738e697cce7cec0bbeef758e1 svn.1009 7a4a905ac83dc3dce44a6b8954221e7a937b1b39 svn.936 7abdf2775d80674580dcb2ee742af8ce3a28bf80 svn.1439 7ae0e86d0ae77ac1e40bf9fa7b42cdcebb32ea11 svn.698 7af1b07907d92d18a1891139956cc0acc63a34f3 svn.1339 7af46f2766cee10bb777ec28057d33027f54e582 svn.1148 7b093458569729c245fcbbcff0264a7ec9ddb0aa svn.280 7b3988fc85a4f3547220effcd9fdc01c85ae29d3 svn.1258 7b4ea78d32f125a8ed976d98fd7a8270c61ba145 svn.265 7b79ca45ed746cda3e4be06913a4fc938aac2f2e svn.1169 7bc1265533579cb4e7542be7376d77a2bbeb42f2 svn.119 7bea2bddb35fe07eaeea753cab6edc8486df96b3 svn.1286 7c29c4151f71a24a11af92a03468db6656575db0 svn.1792 7c2a177f330a773e3378db85d194dec33f84043a svn.1733 7c341522bf86c7e10bb655f7f6314aa34eca4b16 svn.465 7c3e587ecad52c6e36daf00795fdb3f7eaa065b2 svn.1616 7c5785af82057e3a3854e5a3f757f9ba0b7d3d63 svn.697 7c73ea93d3baf7668938452a5c6ec66aeb8288d8 svn.1105 7c7d8e04c7806fea442209f75fec5b5b5d753c94 svn.831 7c8b269f72c0cf2e3a672b0d21b931b89776e4e9 svn.10 7c903124520810e58dcdac13bd2ed621e12f428b svn.817 7c9cac9f2405fbcf56e45a7a557b1ac8062dfb19 svn.1050 7caae7bbcb13c6b8485c0db265f7f69dbe1db4e3 svn.1368 7ccb68386f7d9364e65edba381b48fc14050b311 svn.1781 7d0f2b21b8275bf67204c564ea9ac3924d09d24c svn.1066 7d2a9e665687315a5d1d69e5e9376442f79dae77 svn.1235 7d2f5d03022601376011f87f26bc0c36ae0ec1c6 svn.1315 7d309c66861fce2272a43bca230d4ba836f73883 svn.1763 7d6ad92040f2d8b47aaad7c7abbfff718b66c57b svn.431 7d6cf76197562ee72b1974a83a30f93c82435a2e svn.471 7db82d6cda20f252f4733db0ef9102d6ec286e06 svn.489 7e0d7092347ea2b7624d564cf13d71256b0b60ec svn.452 7e2d16dd5cf4f8d8172cc7e5751439aad3ecd447 svn.1418 7e2e758ee8c97c7ce30278510e2a98f7d6ca395e svn.1341 7e45cc2e299134dcf3ad2acf71201fe776d67454 svn.1752 7e499a54d7b873e421c947ca39dbefaefaaa320c svn.598 7e6075aa2eccd5b67e3e5b7b315d2278b2e935e1 svn.1345 7e65dffd52574ce677cbcef4ce759075cf3fda5a svn.960 7e71cef8e31d2c47a572349bfc239fefac96667d svn.164 7e7368e614c8032e21271a16632ef023a49ee8b0 svn.601 7e7a74d7ccb5b49b1748b3edeae17ff57781bd35 svn.999 7e84bbe40c5b7cffc577533ccea6a6fd61dc3c1b svn.656 7e93a63d6a63caf65d7de26223500352cea35c14 svn.1508 7ee5f2feb9d59aad4eb6af41de7ba2d62494acc1 svn.652 7ef6994a5fadebf07519c641cf4b72d824cff518 svn.1693 7f3fd5b887bb0fd1a54a948aa1b5d1026e6bdb96 svn.89 7f538f497ed08bd28b75ce150f261436e7dddce3 svn.1145 7fa3ac5cade9adaccefa58726779de6adb1087a1 svn.868 8015f503c5ec815b64e60a3a67881cfc4c6df7cc svn.597 80ac83abd52bc4809da224207756eb3da581449d svn.505 80bad282cbf8785f907a1c198a8161be5a950057 svn.124 80cc3028703002db687c12bb3ceb64f5d6a53ed7 svn.474 80cf6aa16a2702f47990aef67530abf723df077a svn.701 80e43391ea11ba35c3ade9a0427f3b76a1db756e svn.688 80e7766e9707df387d86d6557efd16208da60b96 svn.1083 8118a10e79c3287b3df44eb9bb3d83a3499f20a1 svn.673 812a377719680b1817d6b4d78d88cec45ed11f0e svn.1254 81393d6765455575a8fb15e37dfba5eaf9f9e3ee svn.1472 814b7ff8d33c2aeeff8d2f309e05f541201bb6a3 svn.615 81556cdc5f31b00d5c0cc126799cb8b5885ff312 svn.866 815662a641f5755da44ff6727ca0f90933f9979f svn.340 8182c58ad0f3406cf84c30634976f45df119e87f svn.1677 81938e1ae18d988e40ee758b8e7885d5ca6832b0 svn.612 81a8b445cc4308eb0e45c9e15b88769bb823a1e9 svn.1018 81b3007d285a41c18068d8f486bc53359e20a842 svn.1684 82063f87059b4cf0eb15fda9372c18348a6eeb6f svn.156 824e85d24342fcbd328df485425677a2bc510066 svn.1342 825e70252776616ce9d6a6e50d0bc3d55b7197cf svn.432 828c01a7da237f57e9bdf7846b6dc1e7310bfd78 svn.861 82951c5f6967e055c4df8413a7ef801b1503a5cc svn.418 829e66a249048caf82f38cbed01567a37b6e54e4 svn.808 82c3945c1bbe5e4757e485df57b7d7af4e25d87f svn.557 83263e58fd561ac31176f8257cf93e0f99306a33 svn.867 833234d863be03580d95c3978aa7ab5800b7fbdb svn.1586 833a684b19f01a76e6e083ef4e337f5b5671fbdc svn.1496 833af911bb6950ed186807b033858421dc9998e9 svn.425 839a901fc4ee1d5bb5f297d218daf712db35c2e2 svn.848 83b99a433087db9d4dab352bee880eaf505a9c50 svn.205 83e37d75ef4005c9accb6b292078cc153fb68808 svn.1019 83fd1be26619c2509822c713280d1c9719800d38 svn.872 8406869a75731e17bb48da3dbba9dd22e036cc51 svn.1038 84135ca746d9115dcf06505e5dc271502515667c svn.1245 844549edbfeb810f38de00b3d37162bdd121e2ec svn.407 845b5fda8672799a9a9809b02d3e44eb9dc1b905 svn.524 84694fb038902d50ac9a399ad4d0ac9d4f3a825a svn.620 84938b94351b46f0e04c1ae2a30dafe143c8ca8c svn.1280 849f899f96745f6dfb201a0f08174b5240008c90 svn.1256 84a09db171e3d5289d3416b606c0ae58727787fc svn.1365 84a29238ece32992a21439cc1af6d4773739d47c svn.705 8541a3f921131d33fd10cb03c4af2c0605766be8 svn.712 858f614dd5289a2905e13f5a0d7e56519f65d359 svn.984 85a52aea151770555ca64a81003de926b4254de1 svn.731 861a4b3e654b4af71a3fd84216e91805e93619a0 svn.1536 862306a8cc96362c083ba6ef874375894d9e1482 svn.649 8628c6c2dd265f2cf0c86ca9e33c3e86a4baec11 svn.1340 864e21d9fd45d24ef74b6a81ea7d8eecfcdbe56a svn.978 86c8c50cb6298a23fddd6b00c6dbf9554561924e svn.734 86fc114fd878141c10a5829421b1a5bdcc36703b svn.198 86fd19b1e4db6116e660bad44a8659510bc88350 svn.171 871854458a1e4c7254aca166b60b38e5fd997812 svn.500 875dae7f06e77ed6cf9237bdc3165d97f79b503d svn.771 87a40ec50379ad21cc634f4067552d304a0acbd2 svn.860 87b4f439784c7827a45f67aca66ea41a9355d9c8 svn.1568 87c95800fb46da2cb4607a1573e3756f0251c5a2 svn.498 883691116c1d36a77176710660eb116493b532b5 svn.1514 886d4605de6ca099390b7b8cc2f61fa332efb084 svn.1393 88763cfdd8c05e6cfd4280ce9e3f1c9e8ba1f1de svn.245 8886880992615e4e64d9424b2713a3cde6ecc363 svn.813 88c2900c197f200bb02dd81bb6dd192f676af620 svn.459 89845d23e7587f3afdffb2f08db4855f92a5427b svn.758 89a10337dd0662d72d8590e21e80426a915d7856 svn.1363 89aabab3d202d8a59c48e95b4953e76bc07e849d svn.1332 89c0ca6419b02e56437552b95cb1f46de616b375 svn.1355 89df9a19d2e091086c1dba55d9adfd0dca52a17f svn.1726 8a0b4b881ee9cc567f8505cf6479b707d318d161 svn.819 8a1ad6b1f8798ad22c2ccce5dcd67921b7b6845e svn.1277 8a6463a94c8cffe4fb5b58ba68da7a207446715c svn.168 8a86a5a85de9e27968db5c173005b42ece4c917d svn.965 8ab0cf44a1e7f376822f17c166d625ef2174f085 svn.590 8b3a5ec0bc0818d54abac3652fb23efb28eda087 svn.627 8be3fe249382ae5799d2f9d8908b550b5a218b9e svn.94 8be7ecf34c2c2f7e0dcc4f9911183d4b6de7a49f svn.1291 8c1e1d0828136953953642d54dc1779e192d01ae svn.1310 8c2230b937c3ee7ce03b391e4a461bd666517591 svn.463 8c85a4d78312dfe9268477441d51a75421fa4ec1 svn.636 8cb2595cf85ae6153c354ab362072709d02f930a svn.1674 8cb3788caf6b60b69fd1fa6871365ad77155c976 svn.553 8cc439cd4908a37b9a6a003fab64604646439970 svn.877 8cf9ef42a2ee8743e4dcc0266c137d7290347dbf svn.1803 8d92c38d2f5071ba04168fa8be79c15a1690a218 svn.711 8da7a5197b7205998ee9e1b66ace90707dca4372 svn.1324 8dc392af99318b977258aed56bb551801b964a9e svn.943 8ddbfa657c154329561a7f7301fe63af1bee5de8 svn.631 8e09cf9dc462012fa145b992a3762a395a45a6c2 svn.586 8e1ad8b63c7189647b21a93f31726d5bc989386d svn.337 8e1f65b8f1fffaeb3012697ea6563b9ad3518df0 svn.1179 8e46d277270ec44270ba64a0e9b58f049f8f218a svn.1447 8ea4d84c99709c94fafa5b75d92d8dee46e4dfdc svn.1129 8ec1f002099a56763ac301023acfea7daa143caa svn.482 8ecf50d7068b1fcdcd4bc00b8584cdf8e3d6d858 svn.1725 8ed67117aece3f5023f28a377e3489f706100215 svn.785 8f1c5ce262983f962bae8390bc319ca7856089fa svn.1107 8f536aa935a0fef92ad77d8b4dff108d99d07ac3 svn.961 8f8379ae5ba9cd8c61d259b4bf7e4529f061c7dc svn.906 8fa7c84becd0341ed84dc67d585e65f3b03f13aa svn.468 8fb8e05bdb973fd43d9ce5c8b682680aa5199726 svn.1773 8fceba5b01781a0fd7f4fc3fab2c5a2f6e44423c svn.911 8fe94e553994a68e484f871b857824b9b8def4a1 svn.1385 901c52a909f9c90d3ef0997c1e3b57f6df63da01 svn.392 905743992786d73e673b909a33e0edb12a2e5d8a svn.345 9057a1191c47b0184bbf355f21cf1fcebaf16311 svn.217 907c568da1bf769526447f02f8b4a57a62079b55 svn.996 907feca5bf0c6ac06642cb576bf2254b8b62ac7b svn.1481 909e90b6ff19245a54a064ae75e378a0d91800cc svn.270 90b9e95b83b8ca6acdaf5bd13e4f4d1f1361588f svn.488 91357ef20a1f3f11419c4da9fde1e12708ac838b svn.1444 913fcb4f0c79472bbd93e9824c62c8aa662b58d1 svn.770 913fe489ac4e60e9930281b59c0dea68a4bcd323 svn.637 9159ca04bf16f61e63e01bd20a8c4afffbc699c2 svn.1153 916a361287e99135ddb8e33b2818aed0ffec6127 svn.351 91b2973186627526d836f21b172e5ed2377cbabd svn.1699 91d0807d5270f3006ca34f0ccd9c4a2b81a47cf8 svn.565 91d52e06894d13f2b8da13baa2d8ddfeb1e049a1 svn.1206 91e09807e2ae7128e2f5e32b8906935a6bad8685 svn.1395 92098b0fdd7a4d1d5af9a1bf419709a3f4ac83e2 svn.1502 92239fdd340a536e2cf989d3f32922d48a83aedb svn.1494 927a21cab86adc02d2117edf55e2a4ab8b83d005 svn.73 92820090a70b3d146ccaf34087fd5960e883419f svn.510 92e482a7a589de11eba163373bca29010a5358c7 svn.823 9319791488d21c4f716cd76246a50a3c3a93ce13 svn.567 93321a880a99433b2efc5feb81ec6bd92cfbb4eb svn.810 936376b8be15747cb0cb070de24d03e69d492029 svn.257 9377e463277a33a9d103212940ae6b032680e505 svn.210 9385dddafe44b9aa0b96699d658c07d389de2e13 svn.857 93f9d91f08e3e8f153a08c83db665283b6a627e6 svn.479 941cb047d4aaf379d4066c975b68fb831b7a9008 svn.693 945069070553349eb8ee87bb1cbfda780cbb7ac8 svn.975 94b38e8ff8b357d66128c0cf658961c5f509b7de svn.865 94dbd328cae27a2fc2bdfb5fc08bdfbde2d0eeb7 svn.299 94f4cd4f4a35dca050dc279a66791adca6f0ce40 svn.1642 951195779aff62f5bb0cb10e2aa06c731e972ab6 svn.1031 9535b96f0104968ecf5ab3c9e7468ce23a904a96 svn.1689 9559bda18b4e856a9749c744165735c5155ebfe6 svn.1267 9559d07fbce62a85ccf4b20db90e9ab3ac61c51e svn.516 955a7a3e4c97e9e098e9f11b822946a96cd4c2ca svn.74 95636bc3bc1bc1cdfdc960cd5845d26bc1570a02 svn.1555 957781635f75d753bd6383ab20fcfdabc3600f31 svn.672 95bf6395b37c4ba5ae0a5b101682ab5ca5663d69 svn.1739 95d6cbc8a254c02e9371c64111cf6ea865ad8322 svn.1216 95d858cd57c1d58933493c48116f1034ab3a8e62 svn.15 95ec3bcb988f378331758ab60a4be3df7569bcc4 svn.967 960fc803e2df4ede8b2b874d86107f21c4015c6d svn.508 9640f12726ac9ff6f41e3367088128958e61e212 svn.232 965aeb33157cb092f6c567d70cd03b33cac03d9a svn.238 968eb2518b5e3ec10d5f97929c4826527f824f91 svn.355 96a8ac92924149e0b53b409da391b2c3157fcb94 svn.353 96d05f286624040171e9b951269b48d7207c1837 svn.1521 96d1c770f30975d456e726181723fc236418e52e svn.1771 9704d8eee09e9aa7ed8a25bb370290e7c165e226 svn.128 971a354621a1d9d8cdb55c5984b9bc0b04a20bd1 svn.348 9732bcae276ae99e9e0811f3c1363c88e7d37fdb svn.608 9734bf27770c8267984a34566db598b3e5728aae svn.1152 97588038bbf47ca913de4f468442e1cd86472d19 svn.610 977e4a4d07b40a3b25bd1d70c59224684fae3441 svn.667 97b4a4692fada54dadb2d1f5a20d10bebc2f2caf svn.188 97df23113a045033329931b2d5a4fac8cef9c6b3 svn.714 97fba1eba19a95247d655c901ffe6e182b501d19 svn.1442 9800c5ba2109682bc8cc6a3ccb43f4df78196423 svn.339 9876f685c311b200fe993f176ac7e689862dce5d svn.763 988ec6d271a4678be3f522b5adaa569e78e62a85 svn.14 98911d91b9f92be65ead0ec3c76ddc1ec64f08fa svn.588 98a119a759a65fdc3737917dca91e10cd2614595 svn.1589 98a64b855f10fb4c4ec0fb26b400c930486db61f svn.278 98ad0b1eea20941012842a7cc35bdc057ea6eeab svn.1194 98f12553b78be97ab4a82288e826457a83e59a1f svn.600 9921dc9ed98bcb362576f96422ab51833890a3eb svn.1040 9931de9a9d88ddd1ef0cad7d0cb3eeb3de899a4e svn.211 993773404e8211c08aba35c481f7f8f8af95cf2e svn.26 99652ca45322ff8219909a55c6aea132ab8fc846 svn.1096 99c15130eb2c9aca7a058421f04342790b77e48b svn.1309 99f8230eb4fd93cbef225aeae1d025ca77101047 svn.1436 9a1ebd1808dc4fbc99cd536fa5bef7e562394a5d svn.894 9a71ecc65e66c69b2993aec0873dac3a3b3940c7 svn.460 9a93ef3bf70e27d9e625e9b0e8a79ff009be3b0c svn.52 9a9ada9118516edcea975924ade034bd26dc13ab svn.957 9aa25fb8967e427a27d50791eb2483d614832461 svn.998 9aa3a97b4d50e0ff9dfd27a31de00f7c9feeed3a svn.1076 9ab95a974154bda38fc2619ac6f637ea2b067531 svn.1180 9aec4be13f8ca6fcb94c8d76d234f9f5fbbd61e5 svn.1428 9aef30a081b44a2e20669c2b9efc560138e78cea svn.492 9b10206065a80a8620f63cac765e9afd99d979ee svn.956 9b29a595eb213a3d8b624ffcfec1264daaeb6297 svn.157 9b41c90daf4749214ecb173a00f43135246e6781 svn.1714 9bc1e5dd9832a063fee0f272231d1fcb4fa4c2f4 svn.792 9c112da285602525338629860292979a2d26b201 svn.869 9c3344556df0a135022fc51b4550268e0ff8644d svn.379 9c49782e515a9305eb2520cdb216ce6a3a519115 svn.1608 9c6a4a5c7f86d77a97aed9365a0d805af7f5d6d2 svn.154 9cb29d1f9f10f0d2965be923f870f94a529fafd5 svn.55 9d1e7ed6a6fb317b20bda798ca4d316e4a458a09 svn.1282 9d92e040e9fae5172dd79cddaf35f70f8989f69f svn.886 9d95522b58084ed44db218f5f9b9b8120a4429d7 svn.39 9da0fc2dc4ac8a32ef5dba9721d6ecaf8b556799 svn.715 9e4f7f432edcb71b2fc63de0aa9ab7d2a4dc48a2 svn.887 9e632e3d9ab45e6a235bc32a36167c8846bdfd0b svn.1583 9e6721a9642c652026f903c1d73eee4a112bc642 svn.1200 9ec9c7afba9db271050517210dfeac944e06ea68 svn.228 9ee5ca4720a15232000e029f62003dbccf4a90eb svn.1378 9f1515882b0eb1e9330a2a7bced32d7465db70f3 svn.663 9f207ebe8da55f96872590bc541aeb1c0e95dc05 svn.1108 9f38bbd2a7e0b2a946d449cb92bccaa74cedc1de svn.429 9f8ff6b8d63271ea46fbd5f1c1cc86c011448549 svn.1670 9f9e9b6346e4b4f2e55d0a73f2620442f6adf92b svn.824 9fabaecbb5b52610ac55462dc9527d09d6fbfc41 svn.833 9ff912bc3aa5668736ec642092e9052e4cb5641e svn.940 a0248c89297d1fbcfcc978234971a334d189a509 svn.1078 a02e8681556852d390ca987883bc1f0bf68a883c svn.1095 a06c2aeefa343f95e6114ef42373b41d5168a6fe svn.1234 a0c953d3de30977dfd049bbdb9583babf492f9cd svn.643 a0d79c04569a2fab16b9dcdad2c2279a8d97087f svn.1629 a0d7df05f9a857975a3c5227f3b03f30e9dc984a svn.416 a0f418349af76aa0d155e417b42e62186249d7c8 svn.1648 a10259dbd29b3f03c2b7dc6119e7f646f0cc24f9 svn.1172 a12649dbc73d2cbb659f5c7aac8d018344e0a536 svn.1338 a1295d81ae39fc5a57d0504041c89d2fa48ca6fd svn.644 a163dac0359fbe271c1ca0245d3540857029c3b3 svn.1404 a16838b5cb05592e23262655527f197656796f81 svn.703 a16d588a8e9cf4ec88fd1cb7142c3834f9bc3b73 svn.1804 a18c760d50f79b93a9add8effe453a59432c7775 svn.1759 a1d74d0fc790a3491a52939587410556f6b0a906 svn.1489 a1f4a88de6091601fccaa7d750fc630d89484173 svn.1685 a219cceec164504f0652afba415fcc41eb94dd50 svn.754 a297f869ee4234578f31afdf259e696aa5abac97 svn.1753 a34717eab5696aad8a316ee7e76df8c533b73d4b svn.1720 a3741bbe1e9377d661298a8dbca15698846b80c2 svn.426 a3a91388da54b7cb33dba5f72030690d115686b5 svn.1557 a3e2cea497289ce066176e23cb4300ef42fd2032 svn.268 a3f68e21a03e4f6de50db0a5de6e085147c41ba1 svn.450 a402d09c999716f33b1d5505646bb308d4e39ec5 svn.169 a43969b73291aa0750bfcf156403ea1be4da194e svn.1800 a460319b2b547acc3ba3feaa74b96e1530c7888c svn.838 a48a47a1f51f173205dd227d420ece3611a6ec50 svn.1637 a4fc7736d18490a194bc4a15b778920aee81aa47 svn.1793 a50fe3cbce4e6073c6a645d6b137cdd68e46eefd svn.798 a510c9b3e200cebe7a43326fc9b366952020b70d svn.752 a5267c2eb1d03a1d9c8a699462c7e67533f055e0 svn.1045 a526bb77ca3346dfbf829f14a8b1f8ff31588fdd svn.641 a56aa4ee391728ced5cdf4e896c1b43ae7bfb09d svn.1118 a572e5965967eb1aa33cc2e7d23873258907117a svn.274 a578bb6121dc06b382231f4fa8d5d348bb1271d7 svn.399 a5857bae39ee445ea0c62ba6f4ecdb915230f090 svn.1723 a585956b12cf252a9d003079335feb5c06c99328 svn.800 a586ebd2e3196ff432ce42c22a7f96ce56a40b76 svn.111 a58c3ef148b51785910c57118a514f00ea45c5ab svn.724 a5b10446d760508bf5e8d5aa9a5c9e436249ec8e svn.726 a5b4b425141cf01a7f13ba109ca1f911c7d94d56 svn.1438 a5bab3cf3e5bb59a451209daf738e0d115e4e6f6 svn.1054 a5babafdf8d0662adfbea9079b0c4e3f056bd860 svn.65 a5e219997db4f32ca5c81a2b4aa231ea53a798b3 svn.152 a5ea9c50a0651be227b8b26717cc571749d26604 svn.1201 a5ed10e8bc88fe86cae5592cac69814be8590098 svn.959 a6074bdf26e5e73d9deafb77562d41d63c5e46e1 svn.133 a628cc0521eaf1a5fa06ad4e127359d33ba331f0 svn.1671 a632651a151bbffb850430cf60127d8cbebca142 svn.1336 a646b50a6c312814fbda9365ddb1e2378b601451 svn.1062 a67ce0a27572b4914685b3677f7255886ee53085 svn.755 a6aabdb308ada1f68f6795cac2f3bb1b3b3a270e svn.98 a70125dc712b2cd6f4038c0cc71bdd0726b706a0 svn.1375 a7aac27988511020c8a4b43d97ac296436e85a08 svn.344 a7ff6a46cb6d7d0dc38d744c01ba6d25ce77dcf3 svn.546 a80f3cab483e45ea8c6e7df58780079013a41220 svn.676 a81914dad2eb58c8fa6d345bc5ccc2031774f447 svn.1742 a81931a0ce18e6abe7c31f7a8eda97545f8c4a5e svn.942 a84e91abda7d74e41168d7447ae96bbe1a48e6f6 svn.593 a85b7fd37b4359a40a185d7972411cb171a10f98 svn.196 a86267e508fd91907ed795f277c13849cbfbaa95 svn.1302 a8899e645d975ab0dd8042fd1ba76e660ed84fd2 svn.1591 a8a6f843926816b7d9bdf8a2dbf9423973c4e285 svn.1715 a8d37c1e3ea9582e532abc92c254d7784795eff3 svn.971 a914357b7ae6ae9d06b75cd66b6a90dd612dc4bf svn.1565 a92157febfedda7525b97a3ae3ae102c3f1f9397 svn.570 a9295fc1fe38a604a738dfd593b61251a8876b64 svn.1522 a94d3306a44ee7f0aa98a2f6027ea54b77f21311 svn.501 a94f2e04147d37b0c86e220b47eda0bb842c6693 svn.421 a9997f587f4d1d280852eb4d5a7be43c34d91bd2 svn.36 a99b6b5ef6a1a3a47f2f55b95c622f5cfdfb9ca4 svn.1100 a9c31088a3e164c86f37d3aceec9e8ffd85224a0 svn.928 a9e519c8b5d3010d6ef71a21f1d37a70d302f24b svn.1510 a9f54d68329cef4dbb0fc4aec462a81790a5867c svn.1074 aa1dd68a313f90ed2bed7104cc455049f1d93134 svn.177 aa5a58db2daeacef35ddc3bfe3fb42e2f62dedab svn.1272 aa8c48d95687e4328d713ad09e0a2d5fa8f675d7 svn.25 aaedf208a3c7da4c2f9e4e7090f9a79b6346ef7f svn.233 ab00496b77f0132955f409bd81e661ac498f3fa9 svn.1042 ab16b63dbb36e2fd83ff62f903b7076372ae2280 svn.1501 ab2e41d6b3fbe3fa1b3efab14bd3a269a9c8f2fb svn.1199 ab5719ba28841242625841fde7606293252a1f81 svn.1290 ab5d94b1312c6a7b384630b78b1fca91748567c1 svn.625 abe1b3db045589ce79ddcb349a73cb165a04db18 svn.692 abf2124837c9a76b3165f9db1ff627c6ddada1dc svn.221 ac0ea6d8448371423c5981d840ed6dc1a21c66b7 svn.1186 ac1083dddca99afb1d13aab044e0a1ea1621036a svn.1768 ac9413ddb79055da702d50c9150a6172416108d7 svn.1048 ac970ffcd2cdc118cc58f2601c055b36ef016b47 svn.1655 aca911cccf567b3be747ca4e9d2ca2b9e2dab800 svn.747 acc1571c360ccb3f6b74739da17a84a3aca4281b svn.134 acccfc516c72622744b6278347abac10650db670 svn.976 acec51d08d7624fe76a9aadfe3155ee2483506e3 svn.1490 ad0abbc94dd620f6971e040413801bdf7f5dc5cd svn.1675 ad57f1d5390122313bea130fa196e36f86d7ccd0 svn.1769 addf0f93ae5c8ec8989160d02185e1fcb71e4a43 svn.958 adf2aa0ce50c1ceeb7ba15d3b621139765b35f15 svn.1047 adfcc0995620eee56fb64502c3c748058c968072 svn.378 adffe1af3e0932544c25ac7dfeecce81035b80a4 svn.707 ae058e5b5b0b59b5821b2caa27955794fc5c2820 svn.1651 ae09f960b038793877a44a52e639ff5a6eab6f57 svn.346 ae618970c5d2b233fa46f001a8da2e4d27673214 svn.748 ae7eac9f61672eab28817c79a4c3021577f3bb3a svn.606 aea28bf27fb2695947563966b77625dffc562448 svn.149 aeefcef453f3b17e5bc516ae2ef5783090db3020 svn.622 aef2e9f0f0775830621511191cb1d90cbf31e2dc svn.1348 aef516e1116594cd80a37197723b554b7ed14dd9 svn.1509 af64be205ea2ab36a0aa9fae890406b01f0b1aef svn.814 af78ba12e16cd1c350d7b8d276b63dea1828ff6f svn.523 af86a6b540270ec0b34313cf9a63a959c1c8a903 svn.1735 af9009caf15aece591529c028ba693c4718599c8 svn.818 afd4ae9b829680a7e437ba429f1d5ef583977955 svn.534 aff586a05569925695625366d3994efdbfda75fb svn.48 b017b61136d48d1e975282721dc088ec042ee90b svn.1676 b02e9e71d58e839ce45a0b61472d9a3231b0bb1d svn.529 b034778c7095acd6c1a231d6a8359d4e1557353f svn.1592 b09e8dc65e69b08904691e6fc2da1a3e227b7320 svn.1467 b0b62515d53ea57e9a74f252cb25d98741f57ef1 svn.476 b0c19e4145a3ff2410db03cbd552c9e79c5c6cd1 svn.365 b100edb40a33571f3a8ad85db86fa726f552f7bc svn.1357 b10baedc76153edc3cf9f62609923b031a0c0743 svn.102 b15d74802fe758c9d2661854e0a3d98b65f42564 svn.853 b178007c7e0eeaf82d720f7d67a278581fa68cb6 svn.1162 b187516681a232ffac94cc6741db31b40089f1cb svn.616 b1e2a89b438c98b7cfec2bf0d1788656400b65e9 svn.856 b1f6ec05492ee42c05be7bd552e6a854c1852c0c svn.1016 b254a7fbdf6e8914ba1b20c72efd82ea2895f233 svn.1177 b29b47b078d90d27e17172dbd64371663d753f99 svn.982 b2a797990913cb34b89db24cd90fa0661fce0d10 svn.684 b2efb3cb1ae8e165a00e4ea416061e54b4375e37 svn.1136 b2fc28865d5ada46e419fdcdb6138a48946f5c03 svn.1210 b3508917a48f60870b653906c507c863ecfc6d68 svn.628 b41bea1bcb1de1c425ca3e0c37af428ddb89f7cf svn.1208 b49dcbe2d0cb373270e7afbd936a3c425da25a9f svn.389 b4b34c928a45a00eac301c3eee0056d9e275927a svn.1449 b4c889499f4c41b9bb6e40cf59a1425d1788f779 svn.41 b4cce6b7eacf19d77e87b9c9a329770775936a6a svn.543 b4e3de65979dd083e428846079e509a3041202ad svn.1638 b4fe74aaa8f6b8d44bb333b9f8e3bab72d8a2649 svn.1196 b50805f852a8e2be7ea527f7aa361074f461936c svn.269 b516c2a4c8f925f48f51d4adb3b7f731b586877d svn.1307 b54cc4d9ea3be89229352b78e540f36302b1b67d svn.87 b56cd0e4430d71b6628e34b9141b67770e4c189c svn.1716 b576dbf4b3df62b8fef0fc5b42c1ebb854ebeece svn.670 b57ef455693f2177dc245989457480095481102c svn.369 b5aefdd801963043c02bdda4ee3c581d78325709 svn.607 b6091f03067325dca04b154c82d4ba9473203967 svn.286 b60b25246b1ea4f377f4ec8218bb6eef2774b267 svn.292 b6133494d191fe7c524b134c0ac49c73ebf28409 svn.1595 b62bd324f6a76148938a9f75562bbcc75bad25e0 svn.882 b62dfdcbd5ed48b6dd97c6c08baf763729af853e svn.105 b65ee17c29656ad491d5926c92ce6c1511cf59be svn.461 b66f1ebf55000be1e02f95cc84e2141ab13ac378 svn.760 b68dbe7d587599bed4f5033cddf6b200b3c0c797 svn.1039 b6afde5ee4d5012e2041ac8305d4951021fce11a svn.888 b6df060b80323de2866a13e0fa671652fb0713dd svn.1125 b6e2c5b077fc4816032af9f272a70633bd8bbc62 svn.1253 b72c9063105fc37dc44f5b1927303cf435bb512c svn.674 b7600c714b84873c4ad1c7637de51dcb0595a20f svn.683 b7763d12ffd2ee7fa08d814a66d77e316334a846 svn.834 b7dbd20136811c23c0cb14899acd320067f50f28 svn.360 b7f4ea520c43cfe5409afb6a745ebf32d2f668d9 svn.470 b8007496c5864fe41804232cae36f938910ea5a6 svn.203 b80893f1505443f521f049d263d7c587cfcfa1c3 svn.1747 b8276b8dd4e7ea3eac5b560b0dbb2ab9cbc9e941 svn.252 b83eccc16f7e7b7e0a40fa7829acba11ee9e55a9 svn.312 b853cd61ddc4b7690bbdf1951ba2b97915dbf7a4 svn.1140 b87a153ca34719ca3f7c7bfab49ac497b0fe3575 svn.1374 b8b1484aeb9371e0ec57133badd8e401451dbd04 svn.138 b8be5dfcc9256a3021339d39219e5a92e752d07c svn.1149 b8c88dcfb1690770c3547e51aef90f80eeb25e57 svn.47 b9004cecfea6859995441ccff047349ac2146372 svn.19 b9043b843697016c7929edda4c68aa19b1b34538 svn.549 b90a5e0b349eb2d63def509fd2a1a972714a4827 svn.441 b92d169eb5e10b1f0240084c6e88e69ff7bc9b71 svn.1334 b94c331cba4d1d825d907c64eee88ec0d5c6f197 svn.587 b94fc22d5df709e061d6b45afa4f82962f984cba svn.561 b988ab9044a405bd54a8b82abad6841abf14e5c3 svn.1530 b9c726dc2011338c128420ac8a4564373a350c72 svn.977 b9d559d3c93827503a08f63cd377bb89ba96f94f svn.1174 b9e6a95cadee468ead8fc31de24f615e397c89d0 svn.1729 ba26a56b3af92dc544aa3bdfc85807f6efc072a1 svn.1787 ba52065cb28fd4cabc1d1b62a2c97e970bca272e svn.23 ba72d14db98d4aec227e6b688a70153393514145 svn.1390 ba88dacae29e8a70d19e53bdd1de023f84ef8cce svn.702 bad10987839488295256e8d9b4c98bd41d33c144 svn.921 baed8f20853f4fa7df7ca7b4e810e0fd57f64ebd svn.135 bb127bd1be4a29d863e13ea3bb708758bb18af75 svn.1137 bb1e87ef23ac5ccb79d99a157d7884b0d5acd320 svn.436 bb86dfb4578f0ad377a6ab15e9eec729960a9602 svn.1306 bba760a55e02033fd0055bf67b1c893e6b4082db svn.1011 bba8232865e81643be4c343d4a6bca6687bad827 svn.742 bbd1d3461d4ae66137c197de5135d3852fe10e1c svn.1230 bbd2038ed37182bc4ddb3c574c8113b418999d22 svn.414 bc1400e920f5bed116136d53722700715c07ff54 svn.336 bc42cad69e953bb617aa31d7a374a1ce8a5f925e svn.544 bc8afbc57b67f51384ccaedad69ad024a17e55c1 svn.281 bc9ea94aa2cf29fb36f56ec6d994cba0d9be29ac svn.1166 bcec0692b627b770f3e5b6bab4a22c830746c5f1 svn.822 bceeaee5e4975fb7c5b448257fde44af4d13f384 svn.944 bd0590a19af0a3b7e4f99f7ece4770713b8631f6 svn.397 bd07a43b624c9e249c76af770a1bb82bc89c72e3 svn.655 bd0b543a76b1be2416908cc8c002e516254170fd svn.1474 bd20ff182a70680ed102319d756558b2e6931fbd svn.424 bd37fb10c639aef30c2634843981e03cb922031d svn.863 bd518f331c660af28f9653af0c00bb2b2b377191 svn.186 bd58a3cc636648b5c4165f72df502718001e12e9 svn.446 bd674100764d4566a7df25e2960efea627c425ce svn.1431 bd76d70e13036a042d53118dacdee7017ba24470 svn.1553 bd778de72222fbc5dd4f8f1ca7e2f8e6df9cd8db svn.1456 bd972fcd4ae0b295afac1b1167edefcff7a799d8 svn.512 bdb57322f46908ed60e5d8d5344ae9290baa525f svn.528 be2016c79358602e569cec87eeaf7c8146873384 svn.569 be34fecdf22396fc117ce041d3e350e6bf8460f8 svn.1236 be4a915563973368ff28084c8201f37af10ad46d svn.1193 be6acb7b9827ff8481db261ddd7f59df1a68025d svn.550 be7d1bb42c34dace8162a84a0b9fa85405d580e3 svn.790 be8eaf0b54747c56beb61fa29f5a1e52efbe7e30 svn.1425 be9c17ec40a22a0f2ac629eae2942c47a1e2e46e svn.455 bed7ea6f0af813c6bdbeb6ac61ff4622fb24043a svn.78 bee31785688b4b3ad4b9e754e7fadc6f73dafe07 svn.1724 beefaf3b957aa33f06304fbb5bd8d165f10dbca9 svn.484 bf196d6db35e67bad09d04c5517191d9545f3bb2 svn.175 bf5176323aac3d989544ca00e830a6ee030e2296 svn.289 bf78c745075753faf220e6a1127803633e5d74e2 svn.375 bf9ca3f4fc137b6dbf5f1ecc2f37731f5ba30899 svn.1240 bfa60de05a0572cc29caa94e0200684fea4dc46b svn.1119 bfadc2919b665414db3781ee35f6f78ec96571b7 svn.696 c02c67948ea3bc03cc631a66b6c8a3bbeb7a9a8b svn.199 c06cb14312e16fe08ed3b49d2dd2961b67823f49 svn.757 c098f2b1d264b43305edd5e67b84f838759c0df6 svn.139 c0b88e210dc389abc84d1a5a6b38b468580b9888 svn.753 c0de8e602dca315eaafdc99f0570a6205f36a799 svn.1117 c0ef10d55142de04b6552639be0ef3beaecb8268 svn.1650 c10095fc0b3379a4230af3086c2c82d99fb08f78 svn.383 c136b560a49ef16431d5ebda46bb181d3f30c5ba svn.1053 c13e3d84f3a9a779d232194a736472c0c7dd30b6 svn.1238 c163713e415ac6f33299f8773d95e2872b353910 svn.404 c167a9fb66504c0527d85bbbb2539550e9ef43a0 svn.2 c198ce1f5adb895f14699d3c28f1400ecd209222 svn.624 c19f7596a4c38ccc1376343243b5043a230b8f67 svn.511 c1b1502bbbc8ae1e989ec9139cdd4d1cc3844f7d svn.1405 c1c93678d868e72993bcc2f3476ca75b791b1164 svn.1203 c233bc3385ee77913c98866c10bad6f153f8daaa svn.540 c246630d322cb58675ddef6ebafaca4b73216df9 svn.506 c2a41082a6be8099136177ff717fa82cbae9758a svn.1308 c2b3988e4f9b7530829e3b432df02e7ffe6743dd svn.1582 c2bb172fd266f6ee32d3d4ba4a68e8ab72d21878 svn.885 c2bfbeb2a6189251637353781c52cbbf226d1bb4 svn.290 c2f22be977587da957824faa9aee58ff23f7bc55 svn.58 c3196a368da7180614190efc0395a63929bc6c08 svn.699 c332476fd1461f2dbc3467113e0d311b3b1ae528 svn.81 c3376fa426b1bd1328d044a74f8d98ffe822596f svn.563 c34c1e6c30e51ec604bcfeaca99a384ba6a08cbb svn.577 c368b0a6a927578a598c73058e7a089066fe0f2a svn.231 c3916e50b59483a7d5477a3d2830f7ebaa4e88b4 svn.44 c39afe8c02faf8e017fec2b555f588eae92caea9 svn.1475 c3a8df412c3c5a6d85cff623f3c0bff309892c39 svn.1520 c3cb6c6ea42d39bdff323e454767c966fbe273d6 svn.109 c3ffe484fd397106b02b346a57d4be5538b29bb7 svn.255 c415207cceeaa18d4159f1bf54b4622d257f97fa svn.1634 c44554134dd77398bbdc9ed9d0c6d07ddd9bb0c8 svn.981 c4c5cddc0193b5f2f539a1164f6ceb64f2db3337 svn.881 c4d967b289e88afd7aaf513a69137c9ce9256d33 svn.195 c4ffdb58f38773e05bdb4cba2c07b11f13960a6a svn.333 c50fc0696ef444d7d29476442993549e858c5daa svn.1275 c52bb6ff37c1dad3b2d4bbf9ad1523fdb8b28402 svn.1281 c53e9e42bf066ea0d6cc8727434d0b4b2a475780 svn.1736 c558481a7b1cb461664e002d6276a4a45bec895b svn.194 c5799f4131404885f9f5519469ddc7776db6acd3 svn.1673 c5c15377573c533f5fc534fbf8f41ec86a050410 svn.411 c6088d1d3e29aa86a5afc947eb35f1e1748cadad svn.832 c6096476eeadc2981ea9bcc2583602eb9b65e9fa svn.170 c623c08ff5354afc9dcdaebc4c48e63db16c2925 svn.519 c6859bf896efd71340811b384ebe188adfc0ae95 svn.1335 c68a59d35ff4aab7c379c6901a228c7046b9f9df svn.1104 c6ef0f1ad18b66d53d89f594eb409da7c3f88c11 svn.602 c709cb0943774485ba12513d41f005dad4e1a73b svn.1121 c7162a837fec86e35015ff1106affed097263e8d svn.1408 c74f26086149a38489c2c4dd4d893430513e7d31 svn.633 c75b8dcb044876ed256dfa807fd6c0140d9533e7 svn.1296 c773f6d895653c3ecbdf47ba449adaaac871078e svn.440 c778a0ae6038c46a24c0e8e13039475075ae661c svn.1656 c80d72a952f4c7dc149862d334656d4f29494606 svn.825 c81f9bdea878709507f333f0dac8d3edac0ca837 svn.706 c846c8c22f53d3813d5f48cbbda31848358c73dc svn.585 c84ea36f4cda9bae146389588b04158d93297f5b svn.1709 c8713311b73b6b948194bf185a1973b535eb32ba svn.515 c871904bc45d64e143a583a41c7f57fc8dbf0313 svn.1097 c89c86f10a3bc43e3a6daa573f91138cc16a0d47 svn.1064 c8ee9b5d34430df0ed14cf3f0f1dbc7f1d2b4b16 svn.223 c9773ae3b01374d1b3f9fdf518ca7fd54b573177 svn.1658 c99bb50a463011e62a54fbbea512e004c7f8c460 svn.20 c9ca0c16cf8eb49e0e314f65a7a70de87e870bf9 svn.1380 c9d500439ff90d14fe4769d91134d2f436a1fe50 svn.1706 ca15fea2e6e3e32a908ff6acc3206b7d92667ece svn.204 ca1e28f7895a7a54dc98911762161f370b44f6b5 svn.1386 ca2da2130ce94193b4641865b4b58d278772d9e5 svn.1611 ca5dc5e9e08b86f309231af045059019a9fda402 svn.574 ca6269080027653b454ce5144c0f95c448ec7bbf svn.1024 ca8b8620885703755c186ab7d84dd37157594b2f svn.1601 caea955bfc4a4bc41d45f9062f9611d4e1d0229d svn.358 caf0c2b42ca9839ed7c717de26c99509062c4f8d svn.811 caf4508acac58c6ada5a6e9e192af171711e1a2b svn.206 cb33bfaa936dd21013a66f1e67845305b028e6e3 svn.581 cb55e7475f222fe03bbef195d1b4492f482eabb9 svn.242 cbb1deea25e3a6c5106ca32b9acaad37231b711b svn.472 cbe7888df0dc04f3349262dd716114bf60584352 svn.1566 cc1f76d8fe23fea528b8f58c37d15a005c165820 svn.1212 cc2e1ec4536f68860d74e3d0ff320f5b88fa9237 svn.603 cc3224d42aebab5e87bcb500a56577bd6e7cf2a8 svn.1059 cc38929af6efa218b77da7c8a8ad84f0c39f5bb5 svn.1128 cc74a5c1fe3c598da6cdec23b389ca6e706a8eea svn.1785 cc7bd4cd0d1b9f9a1091bd678047256d975ecb8d svn.1123 cc8f3d28257506a3bc3fb7e14aa924ad38d59071 svn.90 cc91980a1e29913f02f3513fab75f8f23855be4f svn.900 ccf7b27b64988bfa187b588e40db01975e687159 svn.287 cd0c3084edc9eb40f8310068d6ff6d6c7aad00a8 svn.719 cd416a203e19971ad4d604ca44aecec2d2917e46 svn.387 cda04daf48c6309f71c060158c6c5180d90136f6 svn.84 cdc4f7e698242ed2a17946fa3697e1fc7bc7d1c5 svn.1698 cdf65e688238eedbc9f1036a903b6f03735c89c2 svn.1384 ce014fc1956a54e948c897831312b28ff2d40db5 svn.1330 ce2a2203d4f5ed56479a7ef201996ca4863a9c18 svn.517 ce2f3a72ca9da939ab91df6d5cf193a093273a03 svn.836 ce4bf5a027a32c44bcd3bbcf217bc00a06c310bb svn.1505 ce7a63e6fe40b68add31be83d48bcae92237fb02 svn.924 cebe6439577033f1a6727ae2422dccd487729e2a svn.980 cec5c2f0131a6a68816f3cd653bd86846942777d svn.165 cf048d23f9bb70efa03d93aa29281ed5bc489d56 svn.1151 cf0b8fa619131998ad2d9797f62e6d4fdf80dd25 svn.1649 cf10ea8151cb6d668dd33e5547bd4377c0e699e5 svn.990 cf26d7b1d28c8c5561998b8ce1df420dca9e75a7 svn.654 cf5ef451015e5b73967dcba66dda2942d0738030 svn.973 cfd3034246e5374810351f319e7be0bccc989b2c svn.398 d00dff1f4de7ee734b29f596b70e2a86ac22b93a svn.507 d02415804062ce29d1817d6637c2c83ae8c6a985 svn.613 d03329f07d150def647b13629f2076b7e7fed0d3 svn.295 d08bb1b3cfb3773e3ae72796242be9cd722efdf2 svn.1271 d095eeb54e5be45074634e633045237f575e28e6 svn.485 d0b50a7c7a4b2a071640ed4545a9bef6fd0c5494 svn.1164 d0ef00548e9d059f838033cf4f9af66de0a5d11f svn.141 d0f7efcca67d22b8670e257e765fb784dc7f5a98 svn.338 d12d8030ada4bdd6753f9526ceeee4b87e758081 svn.793 d16aa2935585fa8422e5b297f5fcd2315b51fdd1 svn.1415 d1a89f0f04ae32433d86fad2e3d58cc018570315 svn.1524 d1a8a2656a93d9b645072b1cf6ab5c4e3dd232d4 svn.244 d1add6a890d9e5f433efe2f4af3f820e94e5ee6e svn.730 d1b72540f662098dbafd526dabf48f09b5b9b2d2 svn.1745 d1ecf43dff68904f9123a3a29892c4acc4c2ef26 svn.584 d1f98a7c06d1921d36afa0741486ea7f765b9aec svn.767 d2034da217433177c89e79436b59c38144cc9794 svn.1784 d2173c1262774efeef1291f7062521389b7530f7 svn.890 d2282685ac5d1e06a7229f8c39db1d93dbe4edbd svn.1005 d250a805dc0618e8ec8060ed8c1b244d3fd98c09 svn.1056 d279bbbbc82cdf1c4885a4655f901116146df845 svn.789 d2893ea69beeefcfc5c9d56019ac5057dbfeeb6e svn.1779 d294da29169a427c450192c84a94c4dc36917324 svn.535 d2a61719517d9749934b6ca8b91ef87eab5da8fe svn.1756 d2cce60885eecfaa844f23a26f0fae43ac34c600 svn.572 d30456a99475a462bfd2ea3e831e15e27f4463ac svn.1564 d323733a5c1667fd08cfda8f65df8adfa30b49ef svn.1617 d34667423b3940c6105dbb70018eba21a0c66524 svn.1371 d38fe2486bb57ddd100be7ac6d1597fe2a74fb01 svn.1182 d3bf19cb7ce33da131cb742641b947af098964c5 svn.1260 d3c0d5a01e96de0fb8357bd98fd53bcd78e3d8c7 svn.57 d409892c82ab05ee7f678687b756ae192b21cecd svn.1359 d452bb73bf7d898707ffee6b8388800d4d4c32bb svn.1305 d46c717574272bd2cc4b97238f42c410f2d9723e svn.664 d489475826609f4c8013a28bc31b72383a4c896e svn.1441 d49d0a7941ed7725c7a3382573d06d0447e08503 svn.1631 d4edf1cf60d10e3593223705033f460f10c70312 svn.634 d4fac6ea54a62e42d54f846ec3c0df741bd50888 svn.1321 d5057e6c68d784cc5893da72e5ac5ac72f85b535 svn.575 d52d833924ed0f1074688d72c4ce2f644bb46f79 svn.525 d5820ec3227beb823a8a71b1ea5b1648d258dddc svn.1471 d5941dda907940915cd2b05964593d096c6a2fc2 svn.261 d5969b00fa9734a4da0e33ce9c13e46abf3c8648 svn.1268 d5a49ed72a6eb24831dabeed25d2568c7e61a6b9 svn.1131 d63459c7fbabbdc9c1801175bb5444ed40b5569f svn.136 d6dcf317e3417fda2e633885be378e116945b9f5 svn.1146 d6f2fd43ee48e2710a80bbdd99be983aeaa9967d svn.1504 d76b0bdd379455f66e124a2d968a082dba710dde svn.1719 d77977e676fdf1c0e2c05e4296f68933747e70ee svn.462 d77b4d4789f4e93914bb5b435665a77d65e1512e svn.805 d7900a6464c942596365347e0dac9d225f8efe73 svn.56 d7b9cd53e639ca63ff18350a898fdb9262305150 svn.1069 d7c60eeed511fcedfc20d05815ef985ef9b00586 svn.331 d7d86ab3b0d4690912a88421518e2779e2be717a svn.1452 d7eaf968bd14944c4399ca18e3b829489e278b87 svn.1376 d8068096efa1a7478739d255bdc6995b649247d2 svn.1473 d81716777b972787bf80f27bf34d33f40631ff90 svn.939 d8226f2dcf0a14dc2edd0704b8ca1d71288f0beb svn.743 d86244661ec585e3ce0088e7394dbb4e5d530152 svn.1681 d87671d34c248fbf7b4340eb1d77fd5baf93cb46 svn.367 d87bf7bd5791e7facd0e88e76e0b9902c4fb8f90 svn.1712 d8945d2c82435de57ec6a5aadf3fdbcefe89da51 svn.548 d89a70b3f6b24c62fecd63e30e79730460ed3fb3 svn.1028 d8d5d0f85bcd1d0c07758d7056f2a007f906e122 svn.1328 d8f61775a015be47edb16712a599b1831e0cd263 svn.99 d9236d69454cfafe89415698c11e0fc167dc49cf svn.1777 d94c1c5eac0a2775caacf5136617e4f6360ef886 svn.150 d9627b379d0d36e3ca302943cea334cfe052fc8c svn.1554 d96b5c00a521c6c436d8a6cd506482a24e55e823 svn.222 d97109d30a699b5ad60025583df7420df09c7c96 svn.457 d9a2f6d083d569f07828720deb87372fceeca32c svn.1741 d9a557f509a5ad811568db27ae676efcba75599e svn.248 d9bbea43918ee4509e12583fd4603f02576707c2 svn.59 d9ce9b6db9d314a7a518f90d28db3c79686fbeff svn.1764 d9e3844aafe7c3bbf0b18539bac4b24de5acd0b7 svn.1252 da1e5cb40d297b11a5728e70b997a20a7f5a4d7d svn.815 da2fbf9d6dbe9531efc6eec2f99fef06e48b4fb4 svn.1546 da5a324608caba5c8d1dd13fca193ea7cb3fbc57 svn.690 da5b5aade6b6c9aaac226c17cf61329043cce3bb svn.1298 da9027e61c2574ca042f003b2db7a9390340c772 svn.741 da9a9555e9dd1a7132ba696c7139b5af79e81e8d svn.651 daa8e9f1533930e9fe004f28c6a3d8bbaa3873c1 svn.1289 dace5ca959f3d429a5d8422f8ade8ff2020885ee svn.751 dad0938a6a73c2cb20fe741ed5885a631bacbb1a svn.875 db0c05c310ce7bde74d48f39ed1e4867564e10c6 svn.1191 db128cb36550851c7d25f7982a982b335118667c svn.1513 db2716d7aacaf947331b437b6f9259a6c3718fed svn.388 db2d8340dc8c3f4db047424dbce84829600b4f4c svn.1353 db428cad7fb72268daeed2a89e28ef1f4d1642aa svn.1373 db56f738ed6ac4634ab1bce96b763d703986c9e2 svn.1144 db725052b6e3cd8b708a225b35f7d6df48a2730e svn.1744 dbb9baaf6763db6c1ec069bda87eb822e4bb2850 svn.1523 dc3413343f3a6e0f3adb3f5c851611256825278d svn.76 dc3cbb2f3c8b1d30c1bec6551f29661850c79dc0 svn.192 dc5c031766069237f69c3fb997e20a0cc1c9a1d4 svn.1227 dcade05709ccd8d3556c082b9bc17a70d690de71 svn.277 dcb0b183591b93e51f3c7c084e681a575dc9b43e svn.444 dcd3605a556003cdd44f15d61fcbd002437b2882 svn.955 dd14ef8ddc2d8b522b76ea7f186a38ab3b364fca svn.779 dd198266f3cb029f0d5bf63586693e79e0614a4d svn.595 dd258ffab1d84f097561d6e2332cef413a531211 svn.929 dd441b4d3485d9f76b04557b2081407e7b390cbf svn.675 dd6713a2070d4786faa56ee1b5de99de057b9a25 svn.1567 dd852bef6ce6f0af3815cb2844ed2425fc387201 svn.1644 dd8c06cbfd201a05501a77a29ba089b6e7405b6b svn.121 dd9b5bdb90ae7f0fa40a52256c664c4286a9f00f svn.1692 dde9bbaaa7f77a7d3a3ffb0a9ac11ed18665d63f svn.1122 de12d958ec8d61256474cbae0699b46bc245f1c8 svn.1740 de5a16dd44a65e1864acdd8bf0697f28adb682cd svn.187 de5df261f5d9f6a66cc5e8dcc6217d28ae3e02f8 svn.1171 de790db535467baf165508a67ea51f567ef68b4b svn.1155 de989f9e00795ce23acb6447fe09316d5dc991f2 svn.1713 deb4493b2fc41bd98753f9f66a12b4bef8c683a2 svn.1588 deb5472bace0f20d2abb196ee608947f4b24e1a7 svn.449 dec1498ac58f2c3b2d8b4dea3a41330c6942f346 svn.1531 dee00b9b86e0ff5538b2e973a60753d2798f3995 svn.1020 df19be55ad5532cb4a831e24a8c7d29282a90c29 svn.1250 df19fde1fafb23c8e6d8eeea7b4a71c3663d939f svn.1602 df31e314e96595054761ac488c18cab48607337c svn.276 df630d602734c833d58c17456dd0054e4c81dc7e svn.374 df9eee094325daa18d3d8443f71c7bb76e7776e2 svn.1717 dfa1661607fa5daa6f98700397f518ace136fbab svn.776 e0186afc0c8e2fffa8abf02e4414ce5fc887b1a8 svn.1087 e060d802c624e7f6b9dcffbd2f4b24c02797939f svn.907 e08a54b5f3aecea5b384fb7126981c9c9dccf959 svn.594 e1401f51fca72b608abb63a3cb352c1295b65bf1 svn.12 e14986fc7821ce361b2a6c1ba13ac90f5dcd1429 svn.1138 e14a71b2045a992785bb3f3326770efbd02a602e svn.1299 e175cf81425877f3bc1e6b419bfd90d2bcf43086 svn.1412 e20489266f4ab2b653edd84cf071748f571e8cf7 svn.851 e20dca56fba1a0808a13fc700a93527d0b82090c svn.1409 e22471b904aa14082fa012133b648926bf438c43 svn.992 e24d68fd580a8526b5976caf4b805cb73bf69f16 svn.765 e272895dc859c84fa35245a1c186bf4a252defbd svn.1581 e27c6d750b6b94c4ca510903d46d984028b3b631 svn.1797 e286ee5b821fc57e18214c2ff70f75cce9cd11d4 svn.354 e2a5af31a55fac49c28b369e46c03ac0032729dc svn.1571 e2c4af6d557fd7530e53c6b8ce95986183a5aee8 svn.22 e2e61b10359f50643a8478d3b497975e369010c8 svn.430 e2fcb3571d13044d75501ffc65691fd290c988b4 svn.1507 e31e7ae5250c8356021f9972781c3b7911f41fe2 svn.395 e381af52bfacd5303d5818f8825db36145412dfe svn.1343 e38c05dfa50e1f3981da387ee1d151dff5a9dc16 svn.941 e38cbe2eab336984ca2a298b44c97f4031845096 svn.716 e3a860dc2b21287ba888135f8892e4b469d17383 svn.1175 e3aa0614a4d655db7ecabf95adeb4b09f3e07fe2 svn.224 e3c36f35db79a36c92df45b4b2a7334a2c77fe01 svn.762 e3ded6a4190ab349961ad3b103f14290bedd35a9 svn.110 e3f501f80bc2dce414273f63f73f5f6c33aba297 svn.1075 e3fcc61e983e4bb2c7a5141f5278744e3f2c2c1e svn.1266 e3fd071777c105975f82c15a7f45b53111871fb2 svn.490 e42999672ae5fab1bdfdccc0b758405407282bc6 svn.852 e45f96c679ac12778209b30973538c4313478ce4 svn.893 e4c6f2afb5e60e5fc975f8ad197b9e05977ad8d5 svn.1479 e4cb89f2f2b7af049bb785275e75aec6345ee40e svn.184 e4cfc303bb1f0eec319d337ae4f6d3aaa09e4d3e svn.1392 e4efa5208288ef858f2ccbfe4211a0a6a2944785 svn.1111 e53152d529b0834ffb2ba75947ebd8a03ec10e6a svn.660 e562e09b6f3cebd6cdf17f64fdf6328455b37962 svn.1807 e5959ab6348453899a6e8bb3bf49de951d03d4d0 svn.454 e596a16a40a2a897633797103f6d2b30025a8663 svn.1126 e5bf9353cae7206a2f4fcaaf06db42c853a0e45c svn.297 e649c5da9087385ded87e7908622a7a3aa432cf0 svn.775 e65114d76ec41069db902fb50adc1a6caaf59c18 svn.356 e68e2fb451471695ee1e1ac0895a44847be41b8a svn.364 e6ca392f44d89c82ed47827cc351e62b93ecf471 svn.95 e70bb034f46bb63de837c054b9c0082a93eba286 svn.1652 e71694eebd5988be6220b165de355ad6f5639ed8 svn.1086 e7308687c3771a5ce5aad9de3c7960678f2d2c64 svn.952 e746beb215617b69a96729b6fe62d3c16ab2bc1a svn.68 e764b36940ad11559c8fb355200a5be3dc3e5227 svn.422 e79547d8e6826ba37bb81bb572f0f116304a0532 svn.1770 e809a113e46e5f9bcb9e426b722ef93112da30e3 svn.1493 e80ed81b5533cc74f9b9a3f78cb194a09a2eb9f0 svn.687 e8257b5e96d8fa36db504f469fedac7aad1c8936 svn.986 e829e697b9056d623104f8c58c7a17674b15981f svn.1577 e8460c5500b005428e85959558665ec48edca809 svn.1538 e854fcfd3bb2f14339c957d2c8348936ae4db763 svn.518 e865de13f4a9b9ec7d06ba8089c4d5a3f5c70ead svn.61 e8676721e034172dff52af911b38c952fd3b0f33 svn.1414 e877277c9bd2d4708ee8376b6884d49742f1178c svn.847 e880a7d4823404c5e3c23b8e85e5abaae7f989ca svn.1544 e88577b2e9f7b5cc4b6a71467ddecf926d324d39 svn.1262 e8be9d29f2ebd43865b52006a536720d34475659 svn.926 e8c552566b7e611e0b1cf5507fab4c0a71641441 svn.689 e90d98ceddd5099b8e59776cea78818a4e963cef svn.334 e943ea00d2b15c1198088741c69aac8a5d1ea97b svn.423 e951fea3033250e64089a704e19bd9db162f6ae7 svn.1295 e97ce937fcc7314713f79db741948cdc79939a6d svn.1809 e9882484b7cdc4fa2408f82a6026df3459fa4c95 svn.1057 e9c3d7d08b1f72942ee18335ece0c83170d34040 svn.1163 e9c64f18e08b97a9c2a54180789f65013d61873d svn.419 e9c7c5702922b8a61da9d0bf435351e7f5e6861b svn.1690 e9fb3e756f074d3e17131d1315eca56359a3b8ef svn.1073 ea01607fed384e779d95ba04c70d71f2afc3efe1 svn.1269 ea203317990ef509bca17d28c03b924b4d190398 svn.1550 ea21e74c0a987a9b21bb42780b8e5fe427f2a544 svn.749 ea30a44e004f71caf90f435b9a94ed9b447170e3 svn.904 ea3443fc287b6369bcd03204df4881ca25154ada svn.1630 ea3cea63f40d84cc71c1134c35ec0855b53af719 svn.1001 ea9848a0989f43d9443227126e04b57980d5114d svn.1667 eaf6f737155f7883178b450261f306e96f07ada3 svn.695 eb4a739d738bedcd955a47f678793475812429ee svn.1263 eb820e77d4b93ed6c8795aff991f12609bf022ae svn.251 ebc6d7fcbe348258a599674b79925ebcbb3c8e11 svn.1327 ebe4ce0fe92be979d2480f48faedce28a8181fc5 svn.1344 ebe9411ef03491934c417c019a8d251d74ce2de7 svn.899 ec43da2bc1208370cf8306a529bd705bf4490496 svn.1297 ec4cf5d62741efc76e9d0202f302e4e9dd99fc28 svn.1288 ec6b12d6999dc1ca296dcac72bca469d9c26fa0b svn.466 ec84905bae92fe79db17866c3b72deff84efa51f svn.797 ec99f6513ccc04d2649415a033e0e832a2c69fe0 svn.1067 ecbad3699297b914ebc10cebcf803150491faec4 svn.254 ecc98904c752084403545715b184cf55b8781b39 svn.718 ed216bcd32d70aee3bcff4babcdb2b88b01c8902 svn.1679 ed436c5ee36728b29111f713dd0f242b6ad77c49 svn.368 ed945662fb56dac71f20159d917c458c5a698995 svn.359 ed96e2ae3cba35e2132ecaabf74720cf1b2b4961 svn.777 edef3025dc5c36b041481564f09873f383a55acf svn.456 edf4eb2310b560a3832bbecc68544318c8a11f1a svn.891 edf78a84101a105b2387ecf031bb5cbbc534011c svn.1023 ee7ab4e92c74972fa5ece220604ad7d2f5908fcc svn.143 eeb699ff980833c34f5c8b2b4219d194d73178bc svn.1542 eec38ccb6c6cf3b3ea24e86790e52afee26a21ad svn.1124 eed10c69c338f4379b4a2842a0aca95d478b335d svn.497 eed9343db961250471a573539bdc4a82fe4e79d8 svn.669 eee672ed42fb075df1900810565659776e536bb5 svn.1114 ef0b08e329dd1467460a7a9c7bf3e53d4010ae0c svn.739 ef3a39e8fd9b8985c6b0c66375adc2ed000ef4ab svn.807 ef3bfe42a50abc9cedf6528545c73d627f390157 svn.103 ef56cb3058242b6841511103d2e067f09933814c svn.107 ef70305750bd1da1b24174a8ee9507770e9fe3b5 svn.160 ef9eacafbae7825e35d64c77f367bbee0947ae03 svn.1485 efde317797334ba555386283b8e00fca5a77fec8 svn.1612 efe0af4d7cb04614b52bf99845c489915c73f047 svn.1491 f014b0f1317696d0743507d02beb5920b075de59 svn.618 f037671d57fd06d6cb9be940aafaf0e4e7ef37a8 svn.1219 f06de9448050dfaeb6eb120dfc5859cb4411bfa8 svn.481 f06eea2eeee19f26d81fcd1580e44da160e7f874 svn.1084 f0837e1ecbe583af4d8991451dda24383d2833e7 svn.1529 f089bf123355e4f58cccae286daa8341b0faede8 svn.1285 f0bfbf4f0ff8234095f4e9260554d47b0d6c7e6d svn.997 f0c6ace804246be6499292171b3c2a25a2d98469 svn.1455 f0cbaf7bafdda306990b6d7380d2588078a449f2 svn.1575 f0d1af1d9f74a64cc2f3ba41c4791f8ceec4154f svn.568 f0d54897e223f01b8b4f0af89c4c1260b7e5dd1e svn.1352 f1114268a7373a1decbea482e7d1d042756761ae svn.1534 f14a3e397ca98de852fa7cce40243435da79ff78 svn.816 f157b1a713d1f6fd5e2fd6781ed133f248f5ffab svn.1372 f162e5645d5c5e4fe5022a50b8c4dedd960b6401 svn.657 f19bf1ae6de86f7ae40021ab70f97976f5e8ca65 svn.37 f1b05bcc11f5d4e75e88d6314a03f44993124b94 svn.892 f1eb186c43debebfd70fd7d16ae2a582698077fa svn.1034 f1ff6856474c9ff16b490b8383b00045b1aca3bf svn.1423 f249bcbc653b330265ec31240211c662d77b7a29 svn.1434 f259010e70d330e0a78843d5af6e080c42d598aa svn.1543 f29c306f22336754779b19213871262f0f8ba7cd svn.28 f2bc5e164d73596939d721b2a579ec55a58d1274 svn.1788 f2d3a4034d896bf6b4ecb9960aa2f5b9b671f34e svn.920 f2e36095b95676637a36541b79f06f6213984bb0 svn.62 f2ffa05cb5671eb0bad62fcd95d3ba9ecbee3acb svn.1088 f310923770b1b1341ca9163498b0f01ca04ca84c svn.728 f37e8c96a1cfdc199dafc786e656188c8f618fd0 svn.1606 f37f6dcc2fada56360587c2ad701ebf4778e46c9 svn.352 f38b4427c24b586efc68101d772f6d08fa27f810 svn.1470 f3978c2cfb085d876d2d56ebb70d8be183043f30 svn.1089 f3a1522ad4de815aa5b652f140a62a7daa67911b svn.80 f3b88e440ac38fea149a4203b51d4e4656affb6f svn.100 f3da9130ae74c98e353cd540f6904d4979a37491 svn.1704 f40234b616dde3de2d3a9c1c6cd2ec733b9afb7d svn.197 f4793542a3d86170546bfd2ce6b93214dc1aa753 svn.209 f487178c028c014939dd70e7b5be731045293465 svn.1559 f48ce9105d52596c8d44c6398150fbd6aa5ab0b1 svn.1574 f4b6a2cfa7a672dcfc108ea4287f43bd48927dee svn.1361 f4d9bb935e4e95550c4044ca22d31558411f0113 svn.962 f4fa817475a03cfbbcb7f9c359f2142ed09b4556 svn.1211 f50108a56ae7020f7c1de5364db721956416f5c8 svn.384 f52b3c8eee5a72a545b2aee5c541e7745fc9129f svn.42 f59f115516f715da789795a1b6f39c9815c6077b svn.1198 f6366f0ed82d609d1eedfa18d330b4056fe57515 svn.1188 f639a64b8d4cd31d7d2f98ee8f71613b613bd1ae svn.145 f657ebf67d707a3b313b69cabe68d54fcdc2cab7 svn.478 f6e445f88d811e70314f902456dcf1753b4161cc svn.1527 f6f3ef09895ad1890e0125dfdc544f8e5f4ad943 svn.927 f771f2707bd7e55861e4a451dfa7ed4193053486 svn.794 f773270afc6330b8d95b1da050a0f4d59c40e135 svn.732 f7862e1b9fade96044ed6c6d07fb80fe4b472e74 svn.1750 f7d162799d92a2d0fac3185a96dad3aae2c7c0f6 svn.1248 f7f92abef4aebe413aa4d17fa2018a6da52bd649 svn.1791 f811e99c84648f894fa79d6e54bc96339e61af9a svn.1029 f84de6dbd29c9686d91e3e9a818033962fedfff5 svn.555 f8792cdf3ba035cee963c95858cae239df55af31 svn.116 f89742203405b18315a5fb2b688b63ab0068f6cb svn.1468 f8fce3c3ef6220fc74b60123177de796918b4bff svn.912 f91c56b9314ca1653f870f8d0494a97dd317bc04 svn.1187 f936f724467761ab6d8c402e2cc52352b267b830 svn.1569 f938afbd0ef3d4d3ae3f6d82f1039b9d84f2ddf0 svn.1370 f9caeee4910376f6748008a70712f08b76a5b932 svn.849 f9e599e709a08f177a0e9c4a73ed3ea2dc883990 svn.1772 fa0bbf57fc4c71c4890822f1d927eb8c2a39df20 svn.1154 fa19b4c0a2c0f2e9c94261256bc28fb7cc421e7f svn.162 fa2f76a678e5aec01e25d9d2d81702225d28287e svn.1738 fa5f7353102a01fc485e7d5d83aaae50f60c62b7 svn.373 fa8a351202d659f65fb62e5720f31e73e046a67a svn.1292 fabe2f3d8e695775b59a0b43edcec84988230cb8 svn.1159 fac6f6a5ceb212eb3d9b54a28459d6e9e1425aca svn.685 fad4f97a8a85bb4274f876f0d05ea0506562a130 svn.873 fad60561bfd2e187de42f3557dedfcf91fe5a35f svn.844 faeb3b49abaff460913359e420c0ffbd3b18f2d0 svn.778 faff3e0a69c548eef84546f92fce514b1ffeb21c svn.3 fb4fae98bac891bf929eb44fc1a4e37fb55d678d svn.1718 fb56a1daf0b6f7583c2eba1aa3ec5c1155584101 svn.935 fb6ccc3cccc25c03bef4c242428687ca0e233cb9 svn.820 fb7dfa722237f83e639e50e6975b71095c168862 svn.640 fb8e171d63c993cad20c02658ceba5a17d04abe8 svn.1413 fb9021a8268a9162a239877d4d4980265b40107e svn.180 fba75965849daca6545bf08ec091ce0f0727446b svn.1643 fbb08d177e3541d70e36347191970557cc07b95e svn.1170 fc0d258b65596f92647a4c906141ffd514ca6e25 svn.626 fc2c570351bd40022d7e58d6d03c3f9a6afca8ef svn.241 fc3bc9d6eb6bf3f7f12cab13dbb1ab20a0f479df svn.54 fc41bafd040579e1c799ee4cce9e2128fed3ee15 svn.1 fc45caaf89a5c4f916f8bf014f15a5cd48ae3a75 svn.963 fc549023f806baf5442569d6599de2f7d1e08b9c svn.995 fc60e042b59b43410254fef7c87c83dafa26f012 svn.1437 fc645ca33d5515b3e453aafa4f84f1c0287b1459 svn.846 fd07495d021768bc0b40ce4e7033bb8f4c13d6c0 svn.1383 fd0d1b1fa0e1d577a6fe62c1103f2446a989eb89 svn.1049 fd0f837bb381b6819bd11233838282c3b941960a svn.1610 fd12c95c101bb1ef71b4b64fffe25e4be8e087e3 svn.949 fd150370126ae7eeeee46ab7a672a6c9dc08ed7f svn.1417 fd221c9cac3f6ba41f1be8aeaea2a67de0c2a68f svn.1480 fd614c5118ed8cf0866a13cec5a008eeeb7ecad9 svn.1798 fd6d75b921fd5581e7a13596c313afc242ecb7cc svn.137 fd910d5f2741be52641a7162a2ccbc6bfde91cf2 svn.964 fda445ffad25d980c9de21be18ca2f8d6a8c2185 svn.147 fdb1f6fe5de782bf8ecafd7edafda49587b3747d svn.1147 fdb3c581672de44f67fb43351db58b6ab543b1d1 svn.483 fdf9b52fe83d221f74dfa77b86df18f52294a320 svn.1217 fe02c21efeeeb64afa613396d73baf95c16cc636 svn.225 fe491964b2f1cefd84dc01e728f17d4dcbd24526 svn.1751 fe54d44addf4db308f24d45765bf2333aa5f033a svn.458 fef75ecd30caa3b331dd89e5d47afda1c662d9a6 svn.642 fef950f2d9d99551e46a72006947ad6c14b7718d svn.439 ff2bd18d3a67d9ea19832775d904f9e8b9b3bce7 svn.1721 ff9f8fc0a2748cbb63d2b0d7407dbd71b1935106 svn.1668 ffe0cbfa8a2e1981cf071b180872333043fde7db svn.993 fff7118f00e25731ccf37cba3082b8fcb73cf90e svn.371 008b15bd5122e910e321316fae6122da9a46e10f svn.1636 0000000000000000000000000000000000000000 svn.1636 009bca8c4c596f01526bc8159c90786e7c011d67 svn.347 0000000000000000000000000000000000000000 svn.347 00d699ef358745ba38f4381c35f2e7d5a6306161 svn.394 0000000000000000000000000000000000000000 svn.394 00e034fabfd073790aaa4ef82bbb666582ae9f57 svn.1265 0000000000000000000000000000000000000000 svn.1265 01165f5c21dd23b8fadfb36a736c1af7f2f51bea svn.1687 0000000000000000000000000000000000000000 svn.1687 011e9277ab04e996ac2d710f64cd361235f4103b svn.658 0000000000000000000000000000000000000000 svn.658 0157ff037651bf0146f57adaf95a173222a3937c svn.6 0000000000000000000000000000000000000000 svn.6 0161f0770fe9d9a6b45cfff81c6c948bf5ac0d23 svn.112 0000000000000000000000000000000000000000 svn.112 0173810f90aa32e9f41f822a74f86d7f9aff9406 svn.417 0000000000000000000000000000000000000000 svn.417 019c2ed6f48651f2dbcf680d35869da9aad5d990 svn.1007 0000000000000000000000000000000000000000 svn.1007 01a6acd7b8084564c58060b14f54609e88f173bf svn.1483 0000000000000000000000000000000000000000 svn.1483 01e8bd44c26797472f364c787dc5cce782d086e5 svn.1204 0000000000000000000000000000000000000000 svn.1204 022d3c06297845aec35f5649a3237710d8b9f55f svn.1802 0000000000000000000000000000000000000000 svn.1802 023b31f57812c07152cd34d79f1f9909cae1e556 svn.83 0000000000000000000000000000000000000000 svn.83 028e083af0708acaa2050533839115d8a69b9a0c svn.1696 0000000000000000000000000000000000000000 svn.1696 02f2b6cd7ea1d2d2d357475f2204f2410cf892ab svn.1205 0000000000000000000000000000000000000000 svn.1205 03164a14f6b23fc6b8bc7abc6c90ab9680740084 svn.646 0000000000000000000000000000000000000000 svn.646 03c59e910c318794f99952c90dd146dc7e01e197 svn.258 0000000000000000000000000000000000000000 svn.258 040638d12856f79baa0d005d14e11cd36ba8a331 svn.330 0000000000000000000000000000000000000000 svn.330 0484b002a76a99a55a13454b6ce17ef45400316b svn.1356 0000000000000000000000000000000000000000 svn.1356 048721d682f8148428a77d82c59d904f3d9c8504 svn.884 0000000000000000000000000000000000000000 svn.884 04a629b15da3ff3678e362065cf884834bd02a22 svn.1197 0000000000000000000000000000000000000000 svn.1197 04bff5f55cb9e853b6183000e065b0080bcea8dd svn.1002 0000000000000000000000000000000000000000 svn.1002 04c2b901d8a05070295cbe351fa0c43291ef624c svn.1173 0000000000000000000000000000000000000000 svn.1173 04c660cac68b46d16341e9c455dc372b1334efda svn.1224 0000000000000000000000000000000000000000 svn.1224 04db5f9b3122cc2bf749cc2e53383eb86c853a51 svn.769 0000000000000000000000000000000000000000 svn.769 04dff7a15ba545a16a93f50d7b125a6fca0ed10f svn.1707 0000000000000000000000000000000000000000 svn.1707 04e29cfda98d62bab16d2053c3b7a774b04298f0 svn.1445 0000000000000000000000000000000000000000 svn.1445 059a6f91c2e580b60123f7ac45983801fbd512bc svn.1257 0000000000000000000000000000000000000000 svn.1257 05a1599e4916c970d398b641bbdb3953f0907ea8 svn.8 0000000000000000000000000000000000000000 svn.8 05b44aec7b74826628f5bf03adec7d84511efc65 svn.1411 0000000000000000000000000000000000000000 svn.1411 05f3e58bc4b9a0b136659a69df4f1d4d05d0702d svn.1613 0000000000000000000000000000000000000000 svn.1613 060a01faf6ae89a6efb3f6d16fdfee0360e36ecd svn.1757 0000000000000000000000000000000000000000 svn.1757 060cccf0ba19182d334bbdeb576d8f58bf0d71c7 svn.200 0000000000000000000000000000000000000000 svn.200 0637b5808587b7c6e5425330d61045a0ebfc3c7a svn.1035 0000000000000000000000000000000000000000 svn.1035 0669ff70d50dc275904335d6e5560d8fba1bd524 svn.1014 0000000000000000000000000000000000000000 svn.1014 0682073503fda2662e7a5d1fdf205787cec2ef29 svn.653 0000000000000000000000000000000000000000 svn.653 06d5806a711f394f6a5889da1214051550ff66c6 svn.802 0000000000000000000000000000000000000000 svn.802 06e806dbd816945b98448239023700b9ce1feb4f svn.410 0000000000000000000000000000000000000000 svn.410 06e83f7e374489e7e930bbb3233a7d48da2a4325 svn.930 0000000000000000000000000000000000000000 svn.930 0763601a5bcc18dffc999f8889aaa24a3954b855 svn.234 0000000000000000000000000000000000000000 svn.234 07780c9b1b42083cbf29fa4aad7195c65a7155cb svn.1044 0000000000000000000000000000000000000000 svn.1044 0791309fea40c03510ad3513de4fddb084697d80 svn.1058 0000000000000000000000000000000000000000 svn.1058 07981139bfd9fdf363c872de11db226c8431841a svn.1318 0000000000000000000000000000000000000000 svn.1318 079f4596e62fc77247141ff779774a785061abfc svn.1633 0000000000000000000000000000000000000000 svn.1633 07db277ec95af7cb9849096730666b335bcc9c5e svn.1094 0000000000000000000000000000000000000000 svn.1094 07dddd86d8fe37233ea506155e3dea8e4280a38d svn.1579 0000000000000000000000000000000000000000 svn.1579 07f45d81abfb32c883e9f29ff2d6ca442c62faac svn.968 0000000000000000000000000000000000000000 svn.968 083153346e4989561bf77e67bcf8b07d6d8ee0d7 svn.475 0000000000000000000000000000000000000000 svn.475 085616db39c365408b3b2713cec63cdc75c1b0d3 svn.189 0000000000000000000000000000000000000000 svn.189 08930d496c9b0e758bf53d5b8024ded0179cf44f svn.370 0000000000000000000000000000000000000000 svn.370 08954e47091923482a4ef97dde985554cc9a23df svn.288 0000000000000000000000000000000000000000 svn.288 08b7d403a90b550912cf513433a5d093d5745250 svn.332 0000000000000000000000000000000000000000 svn.332 08d3aad985e07d28e217cce7e8849b24d1c18ae3 svn.1435 0000000000000000000000000000000000000000 svn.1435 08d7629cc529e025b1f85fb5d45326975f48b84b svn.45 0000000000000000000000000000000000000000 svn.45 08dca756db3206be21541d869dfccd5dc43247f9 svn.1379 0000000000000000000000000000000000000000 svn.1379 08febc793acd54cf4b76bbeddcb79637f1553470 svn.1645 0000000000000000000000000000000000000000 svn.1645 0927e0c91ca66d1d41500c9fd08cde5014ceacf8 svn.1139 0000000000000000000000000000000000000000 svn.1139 0954054bc59272f7974c63cd77933ea344c64619 svn.744 0000000000000000000000000000000000000000 svn.744 0963334d761e9d8db2acf74639cb938c250c8302 svn.1495 0000000000000000000000000000000000000000 svn.1495 099aa96010cdda52c5f2fc83d72e7e03275acd86 svn.1700 0000000000000000000000000000000000000000 svn.1700 099b4e43b52a38fa49164d22726650111cb89f30 svn.1329 0000000000000000000000000000000000000000 svn.1329 09b6347351e9fa337cddc10d39c929b497cc7634 svn.1778 0000000000000000000000000000000000000000 svn.1778 09e730a2c4fb77abfff2ae0dc0e9ab753683815f svn.854 0000000000000000000000000000000000000000 svn.854 09f95c184d9e12dfdfdce597b3812f81976c68fd svn.898 0000000000000000000000000000000000000000 svn.898 09fd3ab58e68418a1cceb9db1938655723a8732d svn.1222 0000000000000000000000000000000000000000 svn.1222 0a3cd2ca46aa4a913dc6cca250deba0aba2f7661 svn.536 0000000000000000000000000000000000000000 svn.536 0a661cfd8bcf95d37bcba79cd5eb0b8d194a4228 svn.24 0000000000000000000000000000000000000000 svn.24 0a70307856e719657be483e103df1fb3803e6344 svn.182 0000000000000000000000000000000000000000 svn.182 0a722d174c5e259d343b64af2dff46682815c84b svn.298 0000000000000000000000000000000000000000 svn.298 0a929e7c80049aa2dba28242c3cfa97b2e3a7bb1 svn.402 0000000000000000000000000000000000000000 svn.402 0a9a9685cb883fddbba2ad166cfec47924102134 svn.499 0000000000000000000000000000000000000000 svn.499 0a9e87864df2ff4434732ae7eba7734630cba360 svn.1192 0000000000000000000000000000000000000000 svn.1192 0ae3ae67d23e4014f104007447a53bfe770fbbd8 svn.951 0000000000000000000000000000000000000000 svn.951 0b0aeb725d723795421475cd3bd285bb0c94c713 svn.1432 0000000000000000000000000000000000000000 svn.1432 0b0be6afa3e6db0011dbc2d132c0f2ede7c1c905 svn.983 0000000000000000000000000000000000000000 svn.983 0b3713cd3b3dafc68054cbcfdaa80442c11ff172 svn.88 0000000000000000000000000000000000000000 svn.88 0b78bcc9c1b17e85cd5a57242d3657c2c1bb7a0c svn.559 0000000000000000000000000000000000000000 svn.559 0ba47ee1dc4abf6efc06cb04b5994c728572fefe svn.1178 0000000000000000000000000000000000000000 svn.1178 0bb40f1e128c4eb6cfc42c4fc043f7cb54550de5 svn.1130 0000000000000000000000000000000000000000 svn.1130 0bc0bdac8bc20d2fb314ae00ccad146883a3311f svn.725 0000000000000000000000000000000000000000 svn.725 0beb7e783548eb33c55ffe1ceb0c64fb895b511a svn.341 0000000000000000000000000000000000000000 svn.341 0bf4ca209b1f9fc18947bd604d05ac624094d8a6 svn.1304 0000000000000000000000000000000000000000 svn.1304 0c004242b3f8c110502e4ea6ee1bdb24d6bc75cf svn.271 0000000000000000000000000000000000000000 svn.271 0c4173bd9d0801ed84dd884c76874bf96c66cc12 svn.720 0000000000000000000000000000000000000000 svn.720 0c561365081c119bd4d7240258909a38e2f4d03f svn.427 0000000000000000000000000000000000000000 svn.427 0c7909677b752a79eadcb4325aab9b34f0cb101d svn.1190 0000000000000000000000000000000000000000 svn.1190 0c987873f61cce2b19b2f227520252c624e7f180 svn.123 0000000000000000000000000000000000000000 svn.123 0c9fa0cfd51283b0531891e486402110005dc0cc svn.1022 0000000000000000000000000000000000000000 svn.1022 0cb628110a0948ef3b8d5f352695321800ec2ff2 svn.1060 0000000000000000000000000000000000000000 svn.1060 0ce364970738c599e6f1aa27aebf59ddbde79faa svn.253 0000000000000000000000000000000000000000 svn.253 0cf48554d6a7d95cae64d5a28fe4ffdcdf6e5efb svn.1766 0000000000000000000000000000000000000000 svn.1766 0d4a60cf51319720ecd77e7a58dbf59d1a018f45 svn.1017 0000000000000000000000000000000000000000 svn.1017 0d53d3fa1c6aedc57a4e9b9683f0245f8ee1a65e svn.1492 0000000000000000000000000000000000000000 svn.1492 0d5c2d29d28dbd71905afe6889d79c8339519beb svn.1026 0000000000000000000000000000000000000000 svn.1026 0da3c07627aae61a53ca2a8fe5a728acd1831553 svn.1051 0000000000000000000000000000000000000000 svn.1051 0da429b7799c4e9a251921e59006199ae6e58c29 svn.144 0000000000000000000000000000000000000000 svn.144 0e1919bee94c9b5095f334c9cd7caea4aa0dc4e6 svn.710 0000000000000000000000000000000000000000 svn.710 0e20a44102dafc5fcc23b4d2429e37a38b218282 svn.668 0000000000000000000000000000000000000000 svn.668 0e5d5039e575561eb8dc9c8afd89aca00d586489 svn.1730 0000000000000000000000000000000000000000 svn.1730 0e5dd9c049a1f89c0d8821dc46f6d8aa29ed1341 svn.412 0000000000000000000000000000000000000000 svn.412 0e5f9e70d3a04d88f9b454d8777177aafc48a3a1 svn.64 0000000000000000000000000000000000000000 svn.64 0e81d2275949f038fd9874b0394e67976705021a svn.363 0000000000000000000000000000000000000000 svn.363 0e938dd99255dbb30683aa7eb73704a60aabcdbd svn.158 0000000000000000000000000000000000000000 svn.158 0e9c51767a9ebb496f2ac88bb1de995953b49b59 svn.122 0000000000000000000000000000000000000000 svn.122 0ec1bb8306a20d164ad38e02e7b7df60382ac77d svn.118 0000000000000000000000000000000000000000 svn.118 0ecf19fa27577dd5f386ad6153da79966eb19f04 svn.1027 0000000000000000000000000000000000000000 svn.1027 0ed17274b6410a68dd2bf6d82499d5b79a7fbd60 svn.974 0000000000000000000000000000000000000000 svn.974 0ee8ebbbafaf6a2966dfaaf8e58c559832dd1797 svn.539 0000000000000000000000000000000000000000 svn.539 0ee914b3cb3b97c478c6e10a1fceda690b04af45 svn.522 0000000000000000000000000000000000000000 svn.522 0ef34494c8ed215eaeeb41775d671b3da55a23fc svn.445 0000000000000000000000000000000000000000 svn.445 0f0c7cb6975400e566e78aa88ea15dec8e937778 svn.447 0000000000000000000000000000000000000000 svn.447 0f1154a13b23e757639ace21e41596b061f50133 svn.661 0000000000000000000000000000000000000000 svn.661 0f15cd632305fe76dfc6d1639ba093c6a2cf652e svn.1605 0000000000000000000000000000000000000000 svn.1605 0f214513456482b170e01e3226ac34b6e9cc7532 svn.1165 0000000000000000000000000000000000000000 svn.1165 0f57be523b3ec9f88963f1964ab63c7e5f4def4b svn.1625 0000000000000000000000000000000000000000 svn.1625 0f8d2d911aa1c061320825dafda295b56cca2933 svn.1400 0000000000000000000000000000000000000000 svn.1400 0fd851c1be1b98520ac273b5ccfc6510a7593006 svn.761 0000000000000000000000000000000000000000 svn.761 0fdd63f1c8fd2a6063c0f92bc9024b7ac9ade49a svn.1157 0000000000000000000000000000000000000000 svn.1157 0ff85e6c512a4d14e9dd71ea373efa0c76b24da0 svn.324 0000000000000000000000000000000000000000 svn.324 105de8e9c3b4083203db5dd507b2063b6ef9f1bd svn.738 0000000000000000000000000000000000000000 svn.738 1098a40cd1bf368573e2178218a4bada315b99a3 svn.979 0000000000000000000000000000000000000000 svn.979 111afe4ac34dd880df57aa7c32651d1150bbf625 svn.1765 0000000000000000000000000000000000000000 svn.1765 11614afba0f7ba29930970da8a55708bdbc14254 svn.864 0000000000000000000000000000000000000000 svn.864 1162e6328a888cf4b1d83fe5c10aaec511a680c8 svn.129 0000000000000000000000000000000000000000 svn.129 118e1fd5a88e88e9b8efdfe273df0b8cb1a10838 svn.733 0000000000000000000000000000000000000000 svn.733 11a938db9e02a14bccf251c1de2f564844afa5a0 svn.579 0000000000000000000000000000000000000000 svn.579 120f50838cd6598bd4cdcef4e2bf87a22a502207 svn.1320 0000000000000000000000000000000000000000 svn.1320 122a58cfd7b4842fbb9317ab01ba2c39f5b0c444 svn.285 0000000000000000000000000000000000000000 svn.285 127e39229e9a70084beb16bfde1bf1bde81e0429 svn.560 0000000000000000000000000000000000000000 svn.560 12b70ef667d79c49bce71cec7bfe6289bd073e24 svn.722 0000000000000000000000000000000000000000 svn.722 12db47fb546b07451b51c62a855e15977ccfbbff svn.1783 0000000000000000000000000000000000000000 svn.1783 13335553c66d4f48e9ac2ec49cd064d1c0e0f1c7 svn.827 0000000000000000000000000000000000000000 svn.827 133f6efb6e1b4ab30e989c793a72123f76d8fc34 svn.830 0000000000000000000000000000000000000000 svn.830 1343353d1ff63c13432208a5b7c933e8c947e1c0 svn.1255 0000000000000000000000000000000000000000 svn.1255 1356fca5f8ff55d7c3a8aff88e6519d5c5637f0f svn.403 0000000000000000000000000000000000000000 svn.403 13576ff5249f3cb5a0c41f6186850801c11295f9 svn.114 0000000000000000000000000000000000000000 svn.114 136169c5ee3d74488d2f08929d58d2919f053bd7 svn.829 0000000000000000000000000000000000000000 svn.829 1371fe7d04067853541990eb05c04b2cd8b0cce2 svn.858 0000000000000000000000000000000000000000 svn.858 137d069b382d7175cf5d425fae13d9f20a8d9005 svn.1433 0000000000000000000000000000000000000000 svn.1433 13bc25d9c7e68124d74e7d3ff85dc0475a12cc37 svn.1366 0000000000000000000000000000000000000000 svn.1366 143e18f3ce685a33208ce4f470b3ec79e1c2a5ab svn.385 0000000000000000000000000000000000000000 svn.385 149036420e91872c752c4ce518bdfeff70330a62 svn.1135 0000000000000000000000000000000000000000 svn.1135 1490fcfb24c70f19f8821224994b546915f5b4b0 svn.1563 0000000000000000000000000000000000000000 svn.1563 149c0fa98e192e90bed74344a8ae5ebbf407064d svn.120 0000000000000000000000000000000000000000 svn.120 14ac1c49a658eef1e9ee29e22fa2e0d4b91a3237 svn.704 0000000000000000000000000000000000000000 svn.704 14b6fa5edc2f5453e9e8ab8113930d53e961374f svn.1789 0000000000000000000000000000000000000000 svn.1789 14bdaf98da0d90eb722eeaec6157de8d0d0408ac svn.1046 0000000000000000000000000000000000000000 svn.1046 14f866bbf50c4cd99474e150129144f9c67c9ea2 svn.437 0000000000000000000000000000000000000000 svn.437 150f74c95526a7387bc828f957348029f71e61a6 svn.897 0000000000000000000000000000000000000000 svn.897 153daa22d893d844ae6797ce8b33bacf016b81f3 svn.91 0000000000000000000000000000000000000000 svn.91 159874fa0df3577327f99ed11df19c03bd7d9926 svn.487 0000000000000000000000000000000000000000 svn.487 15a63d9a7b7190464132e8f36820b1c69f121458 svn.786 0000000000000000000000000000000000000000 svn.786 15cac1fda9ddd07988f4b0a222d09bb89beda8cb svn.1279 0000000000000000000000000000000000000000 svn.1279 164274e4466e0a8cabd81f8342ccebc918a5121a svn.477 0000000000000000000000000000000000000000 svn.477 165bddcbfa004dea70fabe80f82da668831760d0 svn.329 0000000000000000000000000000000000000000 svn.329 1665a35cd4ea1b29b6bd93b0d82d423bed317294 svn.541 0000000000000000000000000000000000000000 svn.541 166738006c3a058f301632619fbaa9fdd7800e2b svn.495 0000000000000000000000000000000000000000 svn.495 167dd46aa411490021352403f7e408ca176e640d svn.556 0000000000000000000000000000000000000000 svn.556 16876332fbaab496fa75d8b4cef80d32a096bf15 svn.1091 0000000000000000000000000000000000000000 svn.1091 16ac4b5438f69ba8320cde2e7c86bf1e50696c1e svn.1249 0000000000000000000000000000000000000000 svn.1249 17539cf7cec90818d553f47d103067a66b6ece5f svn.75 0000000000000000000000000000000000000000 svn.75 177a80a5c2cb7737fd16802e2623d72762e09a33 svn.1183 0000000000000000000000000000000000000000 svn.1183 17853da5da9438d1549e0b7a74cc590c78102497 svn.1300 0000000000000000000000000000000000000000 svn.1300 178f1ce70fbd58e636295d8d74de81c2e481046d svn.32 0000000000000000000000000000000000000000 svn.32 1790cffb14fc49234d98115217d574f918d05b90 svn.878 0000000000000000000000000000000000000000 svn.878 1799a24c3cc76b221815808e883ac018e7cd987a svn.428 0000000000000000000000000000000000000000 svn.428 17d411ced9321408ea900b0e5eb89b46c3f43eed svn.214 0000000000000000000000000000000000000000 svn.214 17e94e863ca4a724e2e0cd4ab12023872ec4a74c svn.759 0000000000000000000000000000000000000000 svn.759 17ffb91f1954a49e66d0809489b74d5f42abffa0 svn.104 0000000000000000000000000000000000000000 svn.104 181f4e0072c301c4fbb3146a1558fbf82ab8b4e7 svn.1270 0000000000000000000000000000000000000000 svn.1270 183ed087726cb1158a7ada1fd9ea59e726dff4de svn.1259 0000000000000000000000000000000000000000 svn.1259 18495205f69e0f05998f0760c9a3ebf1d58fd1a9 svn.1391 0000000000000000000000000000000000000000 svn.1391 186b361ed7e6197c89f71dd5a32db8da0178cb89 svn.826 0000000000000000000000000000000000000000 svn.826 18880acd09ee8e6dae8fca4442c426bddec35784 svn.1037 0000000000000000000000000000000000000000 svn.1037 189409797cf12cbefb0028a1f969149fdd7d17ed svn.1628 0000000000000000000000000000000000000000 svn.1628 18a1dc2a911eeddea1fe71d83759f4eef9b73cc4 svn.709 0000000000000000000000000000000000000000 svn.709 18ea5b4ac63865d808f56b04b0c97a08fb314c58 svn.1132 0000000000000000000000000000000000000000 svn.1132 190508aa312959c217130a0dabd459ac4b85c043 svn.1099 0000000000000000000000000000000000000000 svn.1099 190731ac2ebeeed836dd407f8f6ffe966ec95016 svn.950 0000000000000000000000000000000000000000 svn.950 191e425284f01d40d1b4e1c7b40bd86611ff0fde svn.1401 0000000000000000000000000000000000000000 svn.1401 19400f194230d1b999065ef031279aff724a0590 svn.1195 0000000000000000000000000000000000000000 svn.1195 1959f7bb7ef09e346e60ffdefc72de8b56b8139e svn.1063 0000000000000000000000000000000000000000 svn.1063 195a8ad57107ac668cb656f26cdbb6f8b96b9dc1 svn.1156 0000000000000000000000000000000000000000 svn.1156 1974365b127a40e9e62b041b6252c902f204c0b7 svn.1640 0000000000000000000000000000000000000000 svn.1640 19aa76db8a96347d67f1b07cd1fc8a1f26dce3b6 svn.1229 0000000000000000000000000000000000000000 svn.1229 19d2296f62d8f2e2ebbc7f823608d60683ddbc65 svn.161 0000000000000000000000000000000000000000 svn.161 19e098d6fbf349a90d146cb8be3142e418851ba1 svn.393 0000000000000000000000000000000000000000 svn.393 1a29c93406b03f5f51bdfde9d1d8e71a5fd0a014 svn.1598 0000000000000000000000000000000000000000 svn.1598 1a6fa6bbf1d839de617cf6d731f124780505c854 svn.1396 0000000000000000000000000000000000000000 svn.1396 1aa14a655fceeb59dc35419dadaa3eb9b3ade3a9 svn.16 0000000000000000000000000000000000000000 svn.16 1af453e638d1761025e8dadd053d4ceb8d79e90e svn.167 0000000000000000000000000000000000000000 svn.167 1b0b000a008adecc82d6fbfb2ba683770140c849 svn.1422 0000000000000000000000000000000000000000 svn.1422 1b0db0bf5599a565815ae8de98254b653afc8a94 svn.159 0000000000000000000000000000000000000000 svn.159 1b0f3eaf60133f5daabf8876df225d7e17a2d7ca svn.273 0000000000000000000000000000000000000000 svn.273 1b1fbe998eb3a689db2b2cead578f0bad2bee23a svn.855 0000000000000000000000000000000000000000 svn.855 1b2d2b328f1a2268014054a1a106131524b9a58e svn.1732 0000000000000000000000000000000000000000 svn.1732 1b41f55e39f627f352939c4c40d223666cadd40f svn.108 0000000000000000000000000000000000000000 svn.108 1b7c2ab8c72587e372905ba952d301c02fcfa35d svn.191 0000000000000000000000000000000000000000 svn.191 1b7d38aae84dfc9a011ad76c2542c138f4bf720b svn.514 0000000000000000000000000000000000000000 svn.514 1b8a4c4629be438e8e48b7d5f471b61f7e224eeb svn.294 0000000000000000000000000000000000000000 svn.294 1bb32ee8b90599f4e01a894eb8f4e1edb7161782 svn.1036 0000000000000000000000000000000000000000 svn.1036 1bf7e34e171d1a8758f2b7176b12ab995241c4cc svn.700 0000000000000000000000000000000000000000 svn.700 1c37aebefb76e9e26438089b588c1111f1b6d1d7 svn.796 0000000000000000000000000000000000000000 svn.796 1c40d66bae3c4bf0d1f101ffeb0acea168926cad svn.1762 0000000000000000000000000000000000000000 svn.1762 1cb76ee3636da871dedcf57a83da3866fc73bbfd svn.1021 0000000000000000000000000000000000000000 svn.1021 1cc9cf146664844e360b073dc070fa05d08eaf20 svn.679 0000000000000000000000000000000000000000 svn.679 1cf9d0e83cff3079412fd45aa2ee0cac66672588 svn.910 0000000000000000000000000000000000000000 svn.910 1d0c7035c1bacb1f256e0a47534c19aeffeee35d svn.946 0000000000000000000000000000000000000000 svn.946 1d26ca33c8b44c8c5e8524fed63736780a9e721d svn.1283 0000000000000000000000000000000000000000 svn.1283 1d30cfbd7a6696dd86c635b3faa263e95194733f svn.991 0000000000000000000000000000000000000000 svn.991 1d3e14608e450f1c05fbbd63e3bae8fe8498531a svn.509 0000000000000000000000000000000000000000 svn.509 1d9947e7c68242f9a8cb1845c22199d3aabf2dd0 svn.1065 0000000000000000000000000000000000000000 svn.1065 1da8c1d4b86bf7fe632bab0fa19bd5a0860b6414 svn.909 0000000000000000000000000000000000000000 svn.909 1db0d4260c049fa27e83c262405e37e672f09f70 svn.142 0000000000000000000000000000000000000000 svn.142 1dd91bfad08fe300ed3202e0b9885434069db507 svn.1446 0000000000000000000000000000000000000000 svn.1446 1e39a8267b63e6c937d0d9bca0be7ba96e884d9a svn.1214 0000000000000000000000000000000000000000 svn.1214 1e4ad371a3e51fc6cf8bff8832e307408ea23fb0 svn.1443 0000000000000000000000000000000000000000 svn.1443 1e5b976a4d43a87b53f6a10ecea1556ede6abb89 svn.729 0000000000000000000000000000000000000000 svn.729 1e5bad3af0c62d39a4ff1c997c3d69ce6e93f739 svn.1082 0000000000000000000000000000000000000000 svn.1082 1e6351ec0582687c7177e9cdca621c18d9c36df7 svn.932 0000000000000000000000000000000000000000 svn.932 1e6640c409dd5d809c24df8a1b296359ced079ab svn.1708 0000000000000000000000000000000000000000 svn.1708 1e844764a83b00bae1a1990b8a30fbcac149de66 svn.1015 0000000000000000000000000000000000000000 svn.1015 1ea258dafaa4ce328fb224cb7e04b670e3c0856f svn.1239 0000000000000000000000000000000000000000 svn.1239 1eb85913bbe94398eb973722914de95b28a8f69a svn.902 0000000000000000000000000000000000000000 svn.902 1ec3baa5fbf66a9caca0a9b224563d5b28374b22 svn.380 0000000000000000000000000000000000000000 svn.380 1ecf758054d3328567440ee82fa62fe0c316deac svn.1790 0000000000000000000000000000000000000000 svn.1790 1ee66345f10b4429ed1c5b5d3ddc7fd63f05c0ab svn.1710 0000000000000000000000000000000000000000 svn.1710 1f122ef5ebd5f88e6cc8bae94fa39db423620120 svn.1233 0000000000000000000000000000000000000000 svn.1233 1f182f60f8208c83a22f0d617941a4d1408b649f svn.376 0000000000000000000000000000000000000000 svn.376 1f2849d4a56fa5d7909ecf56a1c5643fe76d7e34 svn.113 0000000000000000000000000000000000000000 svn.113 1f583c98024f4582ff3b16447f439663b4ae287e svn.1702 0000000000000000000000000000000000000000 svn.1702 1f8bab2426fb59f2f4243844626b3ca6b11d75ec svn.896 0000000000000000000000000000000000000000 svn.896 1f8c62426fffedfd9471ceedc1096174461c97e7 svn.215 0000000000000000000000000000000000000000 svn.215 1fd4998e77ffd91041d43840b7a70c1be34cc759 svn.611 0000000000000000000000000000000000000000 svn.611 1fd4b02732eb20bbf3c3f5c4c46d8533f14c166a svn.349 0000000000000000000000000000000000000000 svn.349 2048acb78d9f9e117a0841b6a9229f01f56b063f svn.972 0000000000000000000000000000000000000000 svn.972 204b732a1ad80ad9f6c2f6e904e418ff1556b23a svn.1244 0000000000000000000000000000000000000000 svn.1244 2054ca11283e92918cf372404bf773e6c09ab140 svn.782 0000000000000000000000000000000000000000 svn.782 206c669b2af6d8c568531423ce447cfd2fd1e122 svn.96 0000000000000000000000000000000000000000 svn.96 208f087b08e1f353ea1d96d735e53c0d84a2fec2 svn.1218 0000000000000000000000000000000000000000 svn.1218 20d3e642f40308d6ce2bf2352d19bd60046bd474 svn.30 0000000000000000000000000000000000000000 svn.30 20d866b7ac2534392e54ea71c724940a91e926c4 svn.386 0000000000000000000000000000000000000000 svn.386 20e6708ceec570278362d6d32ad64228e8d104af svn.774 0000000000000000000000000000000000000000 svn.774 2105424868d0ddfaac6c68356bcc296903e5e2c6 svn.1243 0000000000000000000000000000000000000000 svn.1243 211283d83105ce19eb95f5c9f26b2b8e9577ab66 svn.756 0000000000000000000000000000000000000000 svn.756 212bf834fc7f2a96f407eaca2fc11107a28c7b95 svn.1061 0000000000000000000000000000000000000000 svn.1061 21a01aef37253089ca9607afbe6bee41617cee94 svn.795 0000000000000000000000000000000000000000 svn.795 21ab4b3e629c084ba8f6e39568efc7cb0bea893c svn.1221 0000000000000000000000000000000000000000 svn.1221 21bfc577a0793032cf3180f3210b80be465a9b47 svn.1597 0000000000000000000000000000000000000000 svn.1597 21c80a55ccdfeee4164df15049b6ac883dd42886 svn.591 0000000000000000000000000000000000000000 svn.591 21ed3bd276caee7cad233138cd85a77985ebfdf4 svn.1184 0000000000000000000000000000000000000000 svn.1184 224c097dde0c7544d7aebd0c7fc4f3b398ee4ead svn.564 0000000000000000000000000000000000000000 svn.564 2260c13777ed3b984596262a7abcf3d74949b74a svn.1112 0000000000000000000000000000000000000000 svn.1112 2267dbefda0b97873e7e212a9ff953f123c79dbc svn.791 0000000000000000000000000000000000000000 svn.791 2283ca6161ff49a0a06f25cf2c4718d3f6a8dd6c svn.1461 0000000000000000000000000000000000000000 svn.1461 22d90c022f15abbb36d598f0fc12634b7922da39 svn.1030 0000000000000000000000000000000000000000 svn.1030 22dbd0b1ca710d8534f6bcdfc3d473b1d6c3e0d4 svn.325 0000000000000000000000000000000000000000 svn.325 22e6c80fe419953eec283f3f4583ed609c9f4d08 svn.1654 0000000000000000000000000000000000000000 svn.1654 23262ad8a41ebca12807a1ca65b1cfd95c2d076d svn.1160 0000000000000000000000000000000000000000 svn.1160 2384a711554a63d4cf9e354848065244b4731419 svn.1503 0000000000000000000000000000000000000000 svn.1503 23a1a6bf68f52048a0831f583593073751306164 svn.1127 0000000000000000000000000000000000000000 svn.1127 23e1b62af018845944937b5cca4a15866647e644 svn.1223 0000000000000000000000000000000000000000 svn.1223 23fae50a1832b41d61b3b0a0a2fb1a8f476821b2 svn.666 0000000000000000000000000000000000000000 svn.666 245b211eedfa9ab0f96b24de757b65435075feac svn.1185 0000000000000000000000000000000000000000 svn.1185 2476faf0f3ea9fb561ff3789d4324e755524f73a svn.1458 0000000000000000000000000000000000000000 svn.1458 247ff6398810d16538afa9b8cba01c792edc416a svn.1068 0000000000000000000000000000000000000000 svn.1068 24a8cd53314b5547ef2e524c77fee58d6334ec8e svn.1459 0000000000000000000000000000000000000000 svn.1459 24afb3cb63a0a1d7f539c942e746a0746b73bcc5 svn.1427 0000000000000000000000000000000000000000 svn.1427 24be36f110bc8c3fb2167e74f09f7a99f6d20803 svn.272 0000000000000000000000000000000000000000 svn.272 24c57902ed27f8cb3a4ca1aafc3b66fbd44fa68e svn.1407 0000000000000000000000000000000000000000 svn.1407 24e7e09a8e48bf287b911e5e71243a3892427107 svn.913 0000000000000000000000000000000000000000 svn.913 25056d70ffd70f590ad36853beeab02ab05b6a90 svn.1303 0000000000000000000000000000000000000000 svn.1303 25064dde309e79255dc027ccac12860b311f97da svn.1264 0000000000000000000000000000000000000000 svn.1264 250cd77c08d85ebab76ecc9e4990cc33ee344767 svn.1293 0000000000000000000000000000000000000000 svn.1293 257c5a7b6c8ef95ccf53da55f692f517a28e6342 svn.282 0000000000000000000000000000000000000000 svn.282 25d4c96964f04c8790e12ab8b8144939180ba462 svn.922 0000000000000000000000000000000000000000 svn.922 263dad5f5c2b1571208e7f52f8488c0f189e95a2 svn.1796 0000000000000000000000000000000000000000 svn.1796 266d5d83cf04ff59050e884077f9dabb56b65b2d svn.179 0000000000000000000000000000000000000000 svn.179 267d7c34121d8fc3579ba5d148e7080074a0e927 svn.772 0000000000000000000000000000000000000000 svn.772 2724aaaa917db3e5d80d8bf547aa6be9f0f2d17e svn.401 0000000000000000000000000000000000000000 svn.401 2733c3d4b6355f77b6602b72999a006407d10e3a svn.571 0000000000000000000000000000000000000000 svn.571 278c2284d6f7e979a8f7260ea27bcc450303f8d1 svn.1337 0000000000000000000000000000000000000000 svn.1337 27ba903abf81b7964ae2a50d051ba42183ae1461 svn.740 0000000000000000000000000000000000000000 svn.740 27d17ab221f275c243449bc575417a25a70474e4 svn.382 0000000000000000000000000000000000000000 svn.382 2815181de43f5b2991aaa55f3e1b86b03d57f892 svn.213 0000000000000000000000000000000000000000 svn.213 2824b91af84c42c2690c935dea1991d87b6d0302 svn.1576 0000000000000000000000000000000000000000 svn.1576 28661086eb26791d5ed1817b47d2e810da311a98 svn.914 0000000000000000000000000000000000000000 svn.914 2884b80f3126f6cae7b874abf66b88c81db8db6a svn.243 0000000000000000000000000000000000000000 svn.243 2890691c8b0ce589f0c362b1867e594c3ec4fd0c svn.1424 0000000000000000000000000000000000000000 svn.1424 28e7cbf47cf394681cdc554f46ec13f67d42d5d3 svn.513 0000000000000000000000000000000000000000 svn.513 28f9faaa116b4e0c8505883f73160aebecdd49a1 svn.35 0000000000000000000000000000000000000000 svn.35 29101d2bc32703f921c90b8c47ab679d3ff1efe7 svn.1600 0000000000000000000000000000000000000000 svn.1600 29243d349999d43d02851a450f53f031bb40cbb8 svn.803 0000000000000000000000000000000000000000 svn.803 2952becb97669b5b69c1d2d9442df916f472f408 svn.1416 0000000000000000000000000000000000000000 svn.1416 29694b1e1a5ff373db84659e392c64e74933fcba svn.249 0000000000000000000000000000000000000000 svn.249 296968b4e2bddcf37cdebb652b2eaf9d662a87d7 svn.1623 0000000000000000000000000000000000000000 svn.1623 2977040634d83c6aa28ab1cce4ba9f397ff6f18f svn.63 0000000000000000000000000000000000000000 svn.63 298d12183d3757c6439d66ef136852f7fa5c30e5 svn.1451 0000000000000000000000000000000000000000 svn.1451 29acc14771291d9456c9dfb72967a3232b50595f svn.1421 0000000000000000000000000000000000000000 svn.1421 29b2b49c3210d1cb1b6400dba253957aa2fbf497 svn.1711 0000000000000000000000000000000000000000 svn.1711 29bc4b09998c5e58e5e881617d6e4c0735bccf47 svn.526 0000000000000000000000000000000000000000 svn.526 29e4edf612fbbf9815d8d674f899d89bd19c72e6 svn.1397 0000000000000000000000000000000000000000 svn.1397 29e77b0d7290dc0add9223c50a656208ca270bc4 svn.235 0000000000000000000000000000000000000000 svn.235 2a1293f17651f5a8b96b837366b06e7b366d4406 svn.1364 0000000000000000000000000000000000000000 svn.1364 2a5783e9aef1edf1a2f5dc9da94b20ada176db28 svn.1614 0000000000000000000000000000000000000000 svn.1614 2a6029d1a826032c2bb80d38023a6fd37e3ac895 svn.1181 0000000000000000000000000000000000000000 svn.1181 2a74e5b6ed96bfab3a334059fc6fbfdbe4bda782 svn.583 0000000000000000000000000000000000000000 svn.583 2a9a084f0ec1c135287fc824291cf582e2763d1a svn.1808 0000000000000000000000000000000000000000 svn.1808 2aea310b02d8eb288828b1e38457527d03a2150c svn.1202 0000000000000000000000000000000000000000 svn.1202 2b0b1ad509c4507b1ee6d4d7215c1dc13f86d323 svn.562 0000000000000000000000000000000000000000 svn.562 2b3f5df5d18db2808c64b9fe865af6d081b1b1e0 svn.1351 0000000000000000000000000000000000000000 svn.1351 2b53901a7520aa476ed751bd0269fee64c015043 svn.480 0000000000000000000000000000000000000000 svn.480 2b5da1b0e6f68f6b24eee859c6a5c073b39cce79 svn.1607 0000000000000000000000000000000000000000 svn.1607 2b616bac66eb2e928e58fdcd260a263cc1e6af77 svn.821 0000000000000000000000000000000000000000 svn.821 2ba2575991fe70542ec0b1e6491a401220e707f5 svn.467 0000000000000000000000000000000000000000 svn.467 2bd4b3dbcdb6edb2b971bc78f584ec1534010a5c svn.1460 0000000000000000000000000000000000000000 svn.1460 2bfef661b95619faa38099f97e303917441c9a44 svn.895 0000000000000000000000000000000000000000 svn.895 2c2e4f27039f9eed5ddf9d08fec08ae079f3e1d3 svn.1609 0000000000000000000000000000000000000000 svn.1609 2c775f41d49b7525484622ccda6033e0497c2cef svn.1486 0000000000000000000000000000000000000000 svn.1486 2cf4d4f200f58d1d4a38eb10f25a305da25dab74 svn.788 0000000000000000000000000000000000000000 svn.788 2cfc0b9e83e1bb5a71b274b1293932c25fecb268 svn.201 0000000000000000000000000000000000000000 svn.201 2dc572c5ba89d3e2f234adc0e90fe3c379dc556a svn.434 0000000000000000000000000000000000000000 svn.434 2e05790c7ff265722b5e610fa8b4c902dbfdbb81 svn.237 0000000000000000000000000000000000000000 svn.237 2e114ae2bedf49862289dee0df21fbc8961d58df svn.694 0000000000000000000000000000000000000000 svn.694 2e36f21bfbec38ffe5d016fc24a16fa8c08cf634 svn.1482 0000000000000000000000000000000000000000 svn.1482 2e56f05a2f5c2d1affe804672a96d4a64112fab7 svn.1398 0000000000000000000000000000000000000000 svn.1398 2e6c33866a213869ef4970cbb9a5fa4251246b41 svn.486 0000000000000000000000000000000000000000 svn.486 2e9604d280d1372305aa1e451fb0ff366908070a svn.227 0000000000000000000000000000000000000000 svn.227 2f3dc3382cf79dd18095a1d08ae7d86c5ee11fca svn.1242 0000000000000000000000000000000000000000 svn.1242 2f7c6f7c38ca0b4a2d59246e98f0785af7c586eb svn.531 0000000000000000000000000000000000000000 svn.531 2f7c6f7c38ca0b4a2d59246e98f0785af7c586eb svn.532 0000000000000000000000000000000000000000 svn.532 2f7c6f7c38ca0b4a2d59246e98f0785af7c586eb svn.533 0000000000000000000000000000000000000000 svn.533 2fa342f70f1ab638e7ff6d29ba0c3f27fc136ca7 svn.614 0000000000000000000000000000000000000000 svn.614 2fbffc4b553feb03c2f36b1141e0f272478768e4 svn.994 0000000000000000000000000000000000000000 svn.994 2fc8ce9aeb98d5e20f7cd2bf04e74d717728b1ec svn.1228 0000000000000000000000000000000000000000 svn.1228 2feee8280510e184223912daf17898a6b9bd8881 svn.1317 0000000000000000000000000000000000000000 svn.1317 300b28b4879e45345ebeef6f215e8828074c6f22 svn.934 0000000000000000000000000000000000000000 svn.934 3020489da36639adddce2b555be63c0dd19bd838 svn.7 0000000000000000000000000000000000000000 svn.7 302bb5f79a4e895853d420e82d9ed656a71b9214 svn.1590 0000000000000000000000000000000000000000 svn.1590 3059f8a36287ce425e801b8af84ed6135be18280 svn.1594 0000000000000000000000000000000000000000 svn.1594 306126f9d0f1834277b17adc5b38ac567da555b7 svn.842 0000000000000000000000000000000000000000 svn.842 30738047b2640774652e1a5bb403c52589ed9747 svn.1232 0000000000000000000000000000000000000000 svn.1232 3076d47821899c6ec96abd622a60ff6b6cda0352 svn.1572 0000000000000000000000000000000000000000 svn.1572 307f6a434baf2c19ed2f04b81559adfc32a19829 svn.1237 0000000000000000000000000000000000000000 svn.1237 30810691843db9af3ac30247b3cd2ee2983ae8cb svn.453 0000000000000000000000000000000000000000 svn.453 309bf66bbf164ad0d715097eeed41a115c50021a svn.293 0000000000000000000000000000000000000000 svn.293 30a8cf7bd40464cc8b769f88d51c8c1c3cc14763 svn.260 0000000000000000000000000000000000000000 svn.260 30c8d69035cd57735a3f86c6dae40e4a6cf5f5bc svn.1578 0000000000000000000000000000000000000000 svn.1578 30c9e3ed1cd0af1220a397c058bed58c34d8acd9 svn.889 0000000000000000000000000000000000000000 svn.889 30d4b4532a3036cf9839ac248d10454337c49876 svn.1477 0000000000000000000000000000000000000000 svn.1477 30e0ce89424861f1280edc2f0a40c4d6592b9119 svn.49 0000000000000000000000000000000000000000 svn.49 3122e32e962449ef55ae4dbc65478e5af779c4bd svn.1346 0000000000000000000000000000000000000000 svn.1346 3234b6e46b7bc7a18bc8186d8645784f7a5ccb9c svn.408 0000000000000000000000000000000000000000 svn.408 326567793ef0406d889de032e525a66d1924f575 svn.464 0000000000000000000000000000000000000000 svn.464 326dc0e28cdffdab9e0cb8018b751a7f7741a93e svn.51 0000000000000000000000000000000000000000 svn.51 32a5f8a01d909e39b60f87146383831187ae4b7c svn.153 0000000000000000000000000000000000000000 svn.153 32d9ef9dafab130232dfdfd629de466a79ffa2cd svn.1748 0000000000000000000000000000000000000000 svn.1748 33d50969dd9576bbdf7c28354d26c854cdc2b3cb svn.537 0000000000000000000000000000000000000000 svn.537 341577d087f3ea35d05ec6634bbcc956bdb20269 svn.527 0000000000000000000000000000000000000000 svn.527 342b79f69394e1ce02d5dd8c24788127aeafb88c svn.1560 0000000000000000000000000000000000000000 svn.1560 34b94e7914142149e465a83e458b6c8afccd9a86 svn.166 0000000000000000000000000000000000000000 svn.166 3510fc0075849ac1601288b8cfd516cc359a86ee svn.1052 0000000000000000000000000000000000000000 svn.1052 353817e819e90bf135e4577de32eb6fccee9670e svn.473 0000000000000000000000000000000000000000 svn.473 35575098e21b39de79637d8a698efe95f61ea750 svn.226 0000000000000000000000000000000000000000 svn.226 35609ba9b54cb9a4065d956fc359d49d6a1c3645 svn.1469 0000000000000000000000000000000000000000 svn.1469 356c0e2a81b234b2d99794f680e7380b0e1987ad svn.1369 0000000000000000000000000000000000000000 svn.1369 35759321ad5ba59bcc727870fa0b7634c0efc070 svn.372 0000000000000000000000000000000000000000 svn.372 35bb46b3e28fba203eb93a8f0904e70b015978b7 svn.1284 0000000000000000000000000000000000000000 svn.1284 35c3413f27068c320d95f4201f7f0cd240d44cea svn.1360 0000000000000000000000000000000000000000 svn.1360 35f42dba0eee8a79ef8221e9032072d7f922eabb svn.1660 0000000000000000000000000000000000000000 svn.1660 3648f2ee0493f7538f15721cafb438e075da0a5f svn.768 0000000000000000000000000000000000000000 svn.768 366a30324964d2c88c2731b027861897d5c2d98c svn.496 0000000000000000000000000000000000000000 svn.496 366e5cd0fd14dc9192b82e7f5a18397d74e64314 svn.599 0000000000000000000000000000000000000000 svn.599 367338cde81885a08621a3b1359140a3fda977cf svn.582 0000000000000000000000000000000000000000 svn.582 3677c19e25bb9311ca1b714f7ad350347afbf323 svn.773 0000000000000000000000000000000000000000 svn.773 36b3012bbbc073ed5b2c0de776a2738593167bc6 svn.50 0000000000000000000000000000000000000000 svn.50 36b804344505a201df09da75721c99f6ba8a9264 svn.132 0000000000000000000000000000000000000000 svn.132 36df8de9d0bc05b7ab6382e3e497b3ff92d4d2ba svn.291 0000000000000000000000000000000000000000 svn.291 36fdd3794888b2d9feae5e5ab8392ffdaedcb1ed svn.985 0000000000000000000000000000000000000000 svn.985 37246737f6859e4117c9a2d6a1d13b2674678cf0 svn.843 0000000000000000000000000000000000000000 svn.843 3767f3d630ab1693ce5886653da52fe320be4432 svn.1806 0000000000000000000000000000000000000000 svn.1806 37ad56d4f517181aeff99df835d5e5adb0f85e8e svn.1639 0000000000000000000000000000000000000000 svn.1639 3864b1286cf26976ed94cf6a3ec4059edfe08575 svn.1662 0000000000000000000000000000000000000000 svn.1662 3885cc7d11fcdbe9656d8c2148bc07a30cdbf494 svn.405 0000000000000000000000000000000000000000 svn.405 38e412a867fb28db8d00a4e88a37906c76aab33e svn.812 0000000000000000000000000000000000000000 svn.812 38f78a2da49df80bbb58119a235811c50aecdad1 svn.69 0000000000000000000000000000000000000000 svn.69 391ee7617763b5904068181ec383952cdd7ee142 svn.1006 0000000000000000000000000000000000000000 svn.1006 3951ca57fc0dae3a992fb790654b04dacf71bd03 svn.86 0000000000000000000000000000000000000000 svn.86 396464816d2328b92330a78c22846c56e96a429e svn.1226 0000000000000000000000000000000000000000 svn.1226 39c43000ec6db4e62ff01772d7521afb796a8af9 svn.155 0000000000000000000000000000000000000000 svn.155 39d85e6ced187b299c627269f0b6d52c691fb24d svn.1635 0000000000000000000000000000000000000000 svn.1635 3a13d92a0679b693d25142b17cbae857c79c0b2d svn.1697 0000000000000000000000000000000000000000 svn.1697 3a3a0158bafeb652c5e7a4f74552e180534d1572 svn.40 0000000000000000000000000000000000000000 svn.40 3a477287d9d2332aa9a924d8282256751bb60712 svn.916 0000000000000000000000000000000000000000 svn.916 3a8ff8b2db71d8537af42c5c5cb5ec968af75218 svn.13 0000000000000000000000000000000000000000 svn.13 3aae41cd34983023a70726fc5cf78e8ffd3badfb svn.151 0000000000000000000000000000000000000000 svn.151 3b037108ef0ea89914009142b1e8ecaf70e1121c svn.1367 0000000000000000000000000000000000000000 svn.1367 3b1782228da51a8db7c41c35791cfb503ed28a94 svn.1537 0000000000000000000000000000000000000000 svn.1537 3b51aaeb60e63b2235d486c3b01f08d6d7e99d7d svn.604 0000000000000000000000000000000000000000 svn.604 3b7885d4f4e8e6923fdac19ce7a13996a0d53f06 svn.71 0000000000000000000000000000000000000000 svn.71 3b9205676f5ac264154b4098908b8e0e81f03cdb svn.1106 0000000000000000000000000000000000000000 svn.1106 3b9a7834cb3009820861484876f55918f2475053 svn.787 0000000000000000000000000000000000000000 svn.787 3beab6acbd602831fa8c3b0c7dcdb52b330f38f9 svn.682 0000000000000000000000000000000000000000 svn.682 3bf0dc8a1b82fedad25e742882e1da7c137a53c0 svn.176 0000000000000000000000000000000000000000 svn.176 3bfb0809df65d84e820c9d0ec5800b2569589a4a svn.390 0000000000000000000000000000000000000000 svn.390 3cd57220b85ed48e04157f86edd19f22cb2f11f3 svn.92 0000000000000000000000000000000000000000 svn.92 3d0e66a5066ff4fc4bce6c2717cb1d1ccc840391 svn.662 0000000000000000000000000000000000000000 svn.662 3d2e410265d5d695bc76081e4d8bd8c9cfa28777 svn.862 0000000000000000000000000000000000000000 svn.862 3d5d909133b1d65527b63a91ec0a331636775a88 svn.638 0000000000000000000000000000000000000000 svn.638 3d7c445b1418482e2b20e9332c3f9b948c33493f svn.589 0000000000000000000000000000000000000000 svn.589 3da7fe78c823506fa20fe57c772ad1842e922b72 svn.1225 0000000000000000000000000000000000000000 svn.1225 3e2e7f6475694accf8d6c8b796cb063c3dfac0ac svn.547 0000000000000000000000000000000000000000 svn.547 3e41764d307c55c40e69c1d9fbb2240af170ef5f svn.551 0000000000000000000000000000000000000000 svn.551 3e77557675c97a0cedbe4863c54eb832568b3c8d svn.538 0000000000000000000000000000000000000000 svn.538 3e908dc26ae65cf3c9dc28c49421e578ae3fb49f svn.212 0000000000000000000000000000000000000000 svn.212 3e9992635c90bf6809a116a9a91933f950113b34 svn.859 0000000000000000000000000000000000000000 svn.859 3eb75d1abf615466c5b4687587d2d72463d8b7e7 svn.1561 0000000000000000000000000000000000000000 svn.1561 3ebca2e17e8605067905976ce45aa33fd85a2175 svn.1389 0000000000000000000000000000000000000000 svn.1389 3ec6641e57e5041411874bced46bc471cfa6f556 svn.420 0000000000000000000000000000000000000000 svn.420 3ed79db929a9ae296e569b6a0a662b62c03f8430 svn.554 0000000000000000000000000000000000000000 svn.554 3ef4c392b225abbaadbc9ec69a59eb6d38ae673b svn.1071 0000000000000000000000000000000000000000 svn.1071 3f2c22dbcb10b0162c94d32df80a8592d8f7cab8 svn.1551 0000000000000000000000000000000000000000 svn.1551 3f5d67c3b191ad30069da0a959492e85623437b3 svn.1133 0000000000000000000000000000000000000000 svn.1133 3fa2f5f309318b5467f0bbeb5542095a393a0578 svn.219 0000000000000000000000000000000000000000 svn.219 3fb3e19746293cba3e51ee7dadef127b0064136e svn.871 0000000000000000000000000000000000000000 svn.871 3fcf225f835cf7b90d8854b50c994435bf9a0c7c svn.34 0000000000000000000000000000000000000000 svn.34 3fee01e8ac38487fdac22bffc36450a18035adf3 svn.361 0000000000000000000000000000000000000000 svn.361 4018fe8a6af67d663dcfdc4faf477b1d513388fb svn.781 0000000000000000000000000000000000000000 svn.781 4049858156fb8795c270d6815556fb10442580d2 svn.1322 0000000000000000000000000000000000000000 svn.1322 4056ab55e28813eaf3ea4b702ef1c5980b680088 svn.1532 0000000000000000000000000000000000000000 svn.1532 40c221438429013119d8d0c6ce987e354f6abb33 svn.1528 0000000000000000000000000000000000000000 svn.1528 40d77e888c26f923e7731771b92d93aaf1a9228c svn.494 0000000000000000000000000000000000000000 svn.494 40e5005d6d7e1f70583840824cc1f80bb70640c6 svn.377 0000000000000000000000000000000000000000 svn.377 40e5eb8bfcf1e9f0c9b0ceae0f8a663afce2997f svn.409 0000000000000000000000000000000000000000 svn.409 40e8cd5a0a4e2d6d6c6a0fd53590d3e64cd08724 svn.648 0000000000000000000000000000000000000000 svn.648 410babe5b4166e31f8fc82cbc2d7e8c156fa1e4e svn.38 0000000000000000000000000000000000000000 svn.38 4118634b4393366efbd911b28f190d7260ce418d svn.1767 0000000000000000000000000000000000000000 svn.1767 4163cad7070177da69b53cd4b9887615da6b50c7 svn.1497 0000000000000000000000000000000000000000 svn.1497 416d80a781f033c2ca3cdb1243407ceec601875d svn.1278 0000000000000000000000000000000000000000 svn.1278 417ad747dd1f8672e37a5e51d936dd1052d3713e svn.1273 0000000000000000000000000000000000000000 svn.1273 41a5bc7c2e91022cc0b99bff3c27d028273f5c15 svn.880 0000000000000000000000000000000000000000 svn.880 41b920254644108c6280e941dcdec28f213e320c svn.504 0000000000000000000000000000000000000000 svn.504 41d0d10781c8fc39a11f8d863a6c60d9c73316c5 svn.1231 0000000000000000000000000000000000000000 svn.1231 41e6266889a81500a983fcc598d30c3cd41b016b svn.870 0000000000000000000000000000000000000000 svn.870 4201883836c6384f77136fcea4899eac3da1112e svn.202 0000000000000000000000000000000000000000 svn.202 420ce4ef78b82b520f2a97ba35b27d792b9d8d29 svn.9 0000000000000000000000000000000000000000 svn.9 4213a3828fa82a2f928c979b6c094a14591f1359 svn.828 0000000000000000000000000000000000000000 svn.828 423219f35f50085b079baff29646c18928578bd6 svn.1358 0000000000000000000000000000000000000000 svn.1358 4246c5028815bd713859ac7194531b0dafea0d8e svn.1072 0000000000000000000000000000000000000000 svn.1072 426da8fd1b6f2d0fb1aacd860b83814b0e6d7e7f svn.736 0000000000000000000000000000000000000000 svn.736 427a362a641904fe9956779a75158137d13e648d svn.366 0000000000000000000000000000000000000000 svn.366 42aa893a70233f327826b6b88d2595ec58be068c svn.1176 0000000000000000000000000000000000000000 svn.1176 4359c081ace30b9ee341a5e74af0060ca9625ee6 svn.1701 0000000000000000000000000000000000000000 svn.1701 4382b85f7f2976cd95d044e5ce3200209556f46b svn.901 0000000000000000000000000000000000000000 svn.901 43b5b08e082d211c858fcefa0abc633fa2e2adbb svn.988 0000000000000000000000000000000000000000 svn.988 43c47f66811d1c3c54ebcbe645151a6938b51803 svn.1487 0000000000000000000000000000000000000000 svn.1487 43f8a7df78eb3b1f54ab6557f32c72842e59c6f9 svn.1000 0000000000000000000000000000000000000000 svn.1000 44482c79545e737aaa40ad828e2b8bc9c32bec7b svn.1666 0000000000000000000000000000000000000000 svn.1666 4461b8732fbbf0ab6200a6fed0c56b995823ea53 svn.178 0000000000000000000000000000000000000000 svn.178 447813e1e520b5a4789ac3553331a36889724e73 svn.1143 0000000000000000000000000000000000000000 svn.1143 4497b6c8a28f87678baffb8e5ea6a4ab076d3f60 svn.350 0000000000000000000000000000000000000000 svn.350 449832bda2e9a04c55df98bd146942a3f77bbe00 svn.1498 0000000000000000000000000000000000000000 svn.1498 44bc0a7fdd004cceb19e556222ab6a22aaa10aa7 svn.174 0000000000000000000000000000000000000000 svn.174 44d880af55ff3e44daeb87c3b6cc5b3e16bf64e8 svn.1008 0000000000000000000000000000000000000000 svn.1008 44edb74af00db2ffb08016f94b0cd399a5ccfd8d svn.85 0000000000000000000000000000000000000000 svn.85 4515bae5095328873b3ddf34bbd4ccc16a4275a4 svn.1705 0000000000000000000000000000000000000000 svn.1705 451cbca5a3e5304928314da6573264ffe7e0e230 svn.406 0000000000000000000000000000000000000000 svn.406 4531a562f716d16bf1eba3f8bd14ea9c2dd8a2a4 svn.1381 0000000000000000000000000000000000000000 svn.1381 45343776eb76b76496f01462fecfe65c4bc8ed8a svn.131 0000000000000000000000000000000000000000 svn.131 457bec224e4b18e0ba380b6956120aa9e55d61e2 svn.173 0000000000000000000000000000000000000000 svn.173 45a9d7e5fb62c1ba8ffebc8e5585e993b7bd310e svn.623 0000000000000000000000000000000000000000 svn.623 464e7e577c5c69cd2c709d7fc9dc8bd15dcb16b9 svn.639 0000000000000000000000000000000000000000 svn.639 4660ab2a5ad8048474c2dfdce74822da12cd5186 svn.1533 0000000000000000000000000000000000000000 svn.1533 46b682802892389cb8d3b8f9b89584f6dc565d9c svn.1545 0000000000000000000000000000000000000000 svn.1545 46cfc58ea9781625cd81fb960d2d6a59b96acce8 svn.1758 0000000000000000000000000000000000000000 svn.1758 46f5d3f78178e6cfc069079e32f13aecb310f24f svn.259 0000000000000000000000000000000000000000 svn.259 4822375a0361431ea8760efdb9deb181d2418b92 svn.1661 0000000000000000000000000000000000000000 svn.1661 48267b66ccee0e9c7618b7ff1ce0e33a33b9e376 svn.1319 0000000000000000000000000000000000000000 svn.1319 4847223c05ae4e6a6aa1d144bed9a28463cdbbfd svn.1161 0000000000000000000000000000000000000000 svn.1161 485ffe9198e1bf33c5c3714e80734763b772e098 svn.21 0000000000000000000000000000000000000000 svn.21 48637151be9336a68446115339761b22bfc74a79 svn.1426 0000000000000000000000000000000000000000 svn.1426 488e52ad69c3f3004c4d88e7826894ec3a2a776b svn.469 0000000000000000000000000000000000000000 svn.469 489b2b01018eece9a3da626ba0ac2a5e5a5ee68b svn.1041 0000000000000000000000000000000000000000 svn.1041 48d00abbf8977b912efe5a52bcf442e5ba0474c0 svn.207 0000000000000000000000000000000000000000 svn.207 48ee2d9c752ff8436f30f8ad270beccc5a34ef1e svn.1247 0000000000000000000000000000000000000000 svn.1247 48fa57b1bee688c3523a5763a52d721dc2af6e0e svn.1678 0000000000000000000000000000000000000000 svn.1678 49045125c9484e0a60487b1cc65a4db189e5cbc0 svn.750 0000000000000000000000000000000000000000 svn.750 49218e93b171aa3ff698c921d442b52bb150d487 svn.125 0000000000000000000000000000000000000000 svn.125 4969b297df682192273b2e7514dfd93b496a032c svn.1167 0000000000000000000000000000000000000000 svn.1167 496d7770984285a5db947fbbdfe4ea77802a0631 svn.938 0000000000000000000000000000000000000000 svn.938 49712828f5b40038a56b3359ec73397344a413f3 svn.415 0000000000000000000000000000000000000000 svn.415 4981eb69b4f367a74842be377dfc8bbd4ca2b762 svn.1115 0000000000000000000000000000000000000000 svn.1115 4982554bb736c2985de96401f31aa6e3d9fcb433 svn.1734 0000000000000000000000000000000000000000 svn.1734 49bf13b03356b24d99eab44a38fdae7631a1c41b svn.1043 0000000000000000000000000000000000000000 svn.1043 49c0c0aa33ab9f6222de994a30c1c8c9d71a5f50 svn.552 0000000000000000000000000000000000000000 svn.552 49e282f62ffaec2781f1c4b32a7ae145ad5e18da svn.185 0000000000000000000000000000000000000000 svn.185 49eb634fdea7222cd9b28dc9a57b13158077c999 svn.247 0000000000000000000000000000000000000000 svn.247 49f6e043889adeafb8b36acb4fe109dee6eea2d1 svn.1810 0000000000000000000000000000000000000000 svn.1810 4a01a8ab2deaf4b6c368469064d86676974ca5e2 svn.1333 0000000000000000000000000000000000000000 svn.1333 4a292bf63670c8a83efd41c939a7402235918b48 svn.596 0000000000000000000000000000000000000000 svn.596 4a2e5c12b4a23e7d9699c85d4e40b2b0cfbaed1b svn.33 0000000000000000000000000000000000000000 svn.33 4a9245b8d1230a4aeebc5b996401848ef2e8aae7 svn.1737 0000000000000000000000000000000000000000 svn.1737 4aaeb47d11c0f86a6e2ca3c1c3711527e23bd284 svn.27 0000000000000000000000000000000000000000 svn.27 4aba19b0b4eb4c840aef00f9663fb1b825b48d6c svn.208 0000000000000000000000000000000000000000 svn.208 4af3e4b9f35b438cd71c732c287b05904b0fc508 svn.1109 0000000000000000000000000000000000000000 svn.1109 4b6e066a9b733af6adf096779d72e14875afc15b svn.357 0000000000000000000000000000000000000000 svn.357 4ba988999535326cc253a3049470eefaedf87f90 svn.1722 0000000000000000000000000000000000000000 svn.1722 4bbb3be5be5b43f84916368d762c83b0698dad1a svn.1454 0000000000000000000000000000000000000000 svn.1454 4c3d3810975dc9a0948a37e033a3d049658bcfba svn.1743 0000000000000000000000000000000000000000 svn.1743 4c5e134b91396e80b184dca51144f39f9560343f svn.1549 0000000000000000000000000000000000000000 svn.1549 4c9e1ab18d0878e9e4c7d551c13cb68baef38bfb svn.1012 0000000000000000000000000000000000000000 svn.1012 4cc9fa1449811b09112e1e97089be590b7444432 svn.1476 0000000000000000000000000000000000000000 svn.1476 4cd40c4e44534c0f3a173874891b6dc192302cea svn.18 0000000000000000000000000000000000000000 svn.18 4cf5381992a384aabac16831800c7410ac6929a5 svn.115 0000000000000000000000000000000000000000 svn.115 4cff5bde76e2deac628cda176628a3cec5a66cc3 svn.645 0000000000000000000000000000000000000000 svn.645 4d3cd3e470113b37c3bac7d2052d79d7a097ec96 svn.246 0000000000000000000000000000000000000000 svn.246 4d58ac7afb5ee8358d6989989ac04602de8c0a45 svn.342 0000000000000000000000000000000000000000 svn.342 4d93b8fb4362acb2626035c0aa12516a81d90fbb svn.148 0000000000000000000000000000000000000000 svn.148 4d9628abe1955d19050a2d86dcaf0c4ecede96d7 svn.1077 0000000000000000000000000000000000000000 svn.1077 4e25a3ec9fe26987dcf14d93f5f0b0ec07a449fb svn.502 0000000000000000000000000000000000000000 svn.502 4e7029901b08b617051901d95a383f6242eaa88b svn.1382 0000000000000000000000000000000000000000 svn.1382 4e74cc5353eb286d9f6697636ceb4c35c02fbcb1 svn.1632 0000000000000000000000000000000000000000 svn.1632 4e99c598a17d61bcda0604b2a9da8488bc429a2c svn.1695 0000000000000000000000000000000000000000 svn.1695 4eecc5a5eb0ee949a906d633e73567aa9642e889 svn.1215 0000000000000000000000000000000000000000 svn.1215 4ef9e46137e79825ad2edae673ab6ee70bc7b44a svn.146 0000000000000000000000000000000000000000 svn.146 4f283440ba805368be141e02ea6cf3ca1544efb5 svn.1241 0000000000000000000000000000000000000000 svn.1241 4fb515e030cd9960c13e228064d8f532653213e8 svn.1134 0000000000000000000000000000000000000000 svn.1134 4fe3cf7dcc79f8ef6cf60148a573008dbd1b5b7a svn.1402 0000000000000000000000000000000000000000 svn.1402 503116d6f3e29bb6baccdd808054579fcf9cafa9 svn.1799 0000000000000000000000000000000000000000 svn.1799 50728409da5a1c155ffafaa2d0f917332766ce91 svn.1749 0000000000000000000000000000000000000000 svn.1749 508bc08810045e1cf1e6970513c9de8fa028846c svn.1430 0000000000000000000000000000000000000000 svn.1430 50b1a45d4899fbe5b643fbcf780037b7edb25ca6 svn.1354 0000000000000000000000000000000000000000 svn.1354 50f6b731870ba3ae773b772050bfb7d27db2e4c7 svn.1615 0000000000000000000000000000000000000000 svn.1615 511379ab4cd6e5e5ee4144890f7f5343fb87aa4c svn.93 0000000000000000000000000000000000000000 svn.93 516801d12403aaeb3be7a1fa3418b4edee09198f svn.218 0000000000000000000000000000000000000000 svn.218 517a757f2e616abc924087c336c6a28192649050 svn.1209 0000000000000000000000000000000000000000 svn.1209 520c515ac241d8ecb87692f4ae5c5f2ac5fa975e svn.835 0000000000000000000000000000000000000000 svn.835 521936dbc055716daa6df19efa7e34e857271f05 svn.216 0000000000000000000000000000000000000000 svn.216 523845d265b6f21192bf920428ac9a8d7dc066ee svn.903 0000000000000000000000000000000000000000 svn.903 5257ab69b04aba9315f60d276ba5854b2f182174 svn.1323 0000000000000000000000000000000000000000 svn.1323 526d95c3398d2d8a7d6442693704b93542b1221c svn.1102 0000000000000000000000000000000000000000 svn.1102 5280eb09b8f1cc2c84bb785b19ff807a47254d4b svn.1680 0000000000000000000000000000000000000000 svn.1680 5287daeb9be1f9c97319242ad4d2c226e266341a svn.1101 0000000000000000000000000000000000000000 svn.1101 530003732acb6b8004875550c44824850136bcf5 svn.451 0000000000000000000000000000000000000000 svn.451 5305e65e9aedf67da3f8028255e21497cbb844f9 svn.77 0000000000000000000000000000000000000000 svn.77 5340452bb3df18ee1befcb4daf62b3ec5b9c88c1 svn.66 0000000000000000000000000000000000000000 svn.66 5345173dbe7e26057de5a7fc46e787d47495cbd4 svn.1512 0000000000000000000000000000000000000000 svn.1512 5355b66f4dab824d6a9dec27432b5993ff3d6ccf svn.545 0000000000000000000000000000000000000000 svn.545 5363b892e7a0402e9417eb7b3f9ce23ff6fc2f6c svn.686 0000000000000000000000000000000000000000 svn.686 5365f158410c88448e3bc294bb15823895be3b07 svn.435 0000000000000000000000000000000000000000 svn.435 53cb10663fef331a2252a4da6b96e7e41c5e7474 svn.1620 0000000000000000000000000000000000000000 svn.1620 53dfcf3deef8c8fde2404b620fcb138fc6d2ec13 svn.1754 0000000000000000000000000000000000000000 svn.1754 540e50771aa0b70808479222e27dd4239b3c8612 svn.879 0000000000000000000000000000000000000000 svn.879 5412bcfdf5df0e75f91f7c242f4f51a094efb62d svn.1004 0000000000000000000000000000000000000000 svn.1004 542c2f66bc5d0cfce5272b1b4056f097b2950e24 svn.1573 0000000000000000000000000000000000000000 svn.1573 5504d339253a7af01a6c9005afe29ca7cbe705f7 svn.5 0000000000000000000000000000000000000000 svn.5 551dde528c1e313b6ff50bf4d79e3b18e7ac0f31 svn.809 0000000000000000000000000000000000000000 svn.809 55e939ca948bfbb641d4ff5892a82280518cb0db svn.250 0000000000000000000000000000000000000000 svn.250 55fecb6fc0d3ae3f40cfeded90d1743f595a7732 svn.400 0000000000000000000000000000000000000000 svn.400 5616db9bc36e94343e2b92df68c4520d62b38cfe svn.60 0000000000000000000000000000000000000000 svn.60 5620cdedb5130bbdbdccef255791b98e83bafbef svn.558 0000000000000000000000000000000000000000 svn.558 563a941879b75768d6d73b60277c85964cdf3f60 svn.4 0000000000000000000000000000000000000000 svn.4 563fe5a94cabd170cc0cde3ae3d3989a6d1141f7 svn.1110 0000000000000000000000000000000000000000 svn.1110 564a081accc74d3b805b393d5fa681607f45b916 svn.917 0000000000000000000000000000000000000000 svn.917 56931af41cceed819df913b9b2493682405e941f svn.1098 0000000000000000000000000000000000000000 svn.1098 56abf9195ec818eaa2e835c5d6e5eab7b8f86026 svn.1301 0000000000000000000000000000000000000000 svn.1301 56b1bec77233c6a2931ae689f41cae9653df9256 svn.665 0000000000000000000000000000000000000000 svn.665 571cb5de9bb60035283627328e04e71c7b4de775 svn.1150 0000000000000000000000000000000000000000 svn.1150 5740382d416f07ccff6d90fae77d808657351836 svn.438 0000000000000000000000000000000000000000 svn.438 575c627130dc876053158837c6256830f3fb42d1 svn.850 0000000000000000000000000000000000000000 svn.850 58132b46dc40cb7e24fe03d86014f3075df28110 svn.1728 0000000000000000000000000000000000000000 svn.1728 582f665ff1e445d2fd9d13ab5b4d664e870ac93d svn.874 0000000000000000000000000000000000000000 svn.874 583d35ad9048b887d4c133101ae9c887bf6a3ee6 svn.629 0000000000000000000000000000000000000000 svn.629 586f3a9bab88e5bec339980ab203fa5158f77632 svn.1326 0000000000000000000000000000000000000000 svn.1326 587722d62ac08a1bea1dc44bd3558ae627be6b13 svn.521 0000000000000000000000000000000000000000 svn.521 589441fe2a35347857e3eee6c4b34f6df60325b4 svn.106 0000000000000000000000000000000000000000 svn.106 58c0b0001072e37b6bba4cc3f388988c596ccc5f svn.193 0000000000000000000000000000000000000000 svn.193 58e27b76bcc5f805251ab9cb27b3259862b1e9a8 svn.82 0000000000000000000000000000000000000000 svn.82 58f2a83ce4bd569a69dec333810dabfbf6f3d4d1 svn.163 0000000000000000000000000000000000000000 svn.163 58f3151c7be6b192acc553c9231d671e11c45d49 svn.1760 0000000000000000000000000000000000000000 svn.1760 58fdd26cbf5ac4b554a0cf795d21e137c9321734 svn.230 0000000000000000000000000000000000000000 svn.230 5910084f8704411c3302f85073b08323465cbb6b svn.1261 0000000000000000000000000000000000000000 svn.1261 59394c9e003e1ade5b90330307f96520b8dd7f67 svn.1515 0000000000000000000000000000000000000000 svn.1515 595b63f60ba812ff9063a78734ed181dfff09b83 svn.799 0000000000000000000000000000000000000000 svn.799 5977f55517263d38698d0fb3568e7e9d0865ae23 svn.987 0000000000000000000000000000000000000000 svn.987 59ee86c8657bf5db81aaf4051507e4c330d5d54e svn.362 0000000000000000000000000000000000000000 svn.362 59f08140ee87a987e11d5d5788f20f9e899817c1 svn.1627 0000000000000000000000000000000000000000 svn.1627 5a007c385c4a933cb1efecbb1c5c189f479775d3 svn.945 0000000000000000000000000000000000000000 svn.945 5ab8c1db3dbdf9f6d302ef17da48942a289c1583 svn.1013 0000000000000000000000000000000000000000 svn.1013 5b222b5000d46e6ea65c7d67d6fca5f3a6f5d483 svn.1070 0000000000000000000000000000000000000000 svn.1070 5b636e141f7273bbe593163202da5ee626b65c75 svn.1388 0000000000000000000000000000000000000000 svn.1388 5b82a0336225d5317d86d3cfd63571c3676598eb svn.256 0000000000000000000000000000000000000000 svn.256 5ba2e572a3f18a22cf11eb07f5c857856f8104ab svn.989 0000000000000000000000000000000000000000 svn.989 5bb22493e5e79a48fed30ff120d32233dbec294c svn.391 0000000000000000000000000000000000000000 svn.391 5bbaba649fbf8628a729bc6a208b791d2ec092a1 svn.680 0000000000000000000000000000000000000000 svn.680 5bd0a4b6acd292e5e40527a43aac64379c19fe48 svn.1410 0000000000000000000000000000000000000000 svn.1410 5bfc0b24ec3c698ac3e0da0954df1f2d68404a86 svn.29 0000000000000000000000000000000000000000 svn.29 5c269386e9fb9dfbe0b3a622d0ac2a40eababe1b svn.954 0000000000000000000000000000000000000000 svn.954 5c3608dee30a07df0460ddac10b5ff333e021de5 svn.327 0000000000000000000000000000000000000000 svn.327 5c4990c765ac586cb341b41508300c5a88b1126d svn.592 0000000000000000000000000000000000000000 svn.592 5cea2a1eaaa054e8e4e204eb27117ab3215adb25 svn.659 0000000000000000000000000000000000000000 svn.659 5cf867e52b3db29da760ef52ce2f5e8e8211d3c2 svn.1535 0000000000000000000000000000000000000000 svn.1535 5cfc7ef09092f692c0dd25a33260952db53afe3f svn.266 0000000000000000000000000000000000000000 svn.266 5d563977f72716f03f98049a416d315636723103 svn.621 0000000000000000000000000000000000000000 svn.621 5d6603bc222175178fc7da0c5668bc4c54ed3021 svn.1394 0000000000000000000000000000000000000000 svn.1394 5d7dba2c1c644a3715f716d1ca260db24a768637 svn.275 0000000000000000000000000000000000000000 svn.275 5e047513c0ea6bd422ac2690e5b5dde28d7b0cc4 svn.1419 0000000000000000000000000000000000000000 svn.1419 5e5997d5cb03c6c7181fb513b07850d524a5082e svn.1585 0000000000000000000000000000000000000000 svn.1585 5ebb6b6af2ad27ce2eeec087b8963239a155725d svn.1669 0000000000000000000000000000000000000000 svn.1669 5efc0b3122438f19ba247a468922513ff3dc3f4d svn.635 0000000000000000000000000000000000000000 svn.635 5f0b8e82d58651c951c356a705e3e4526ba8dbe4 svn.580 0000000000000000000000000000000000000000 svn.580 5f4ad86c159e3397e6187c9fd01f229fa72a819d svn.1604 0000000000000000000000000000000000000000 svn.1604 5f5bda1a0b4af8978f5cd03fdba4d72d114ac1b5 svn.713 0000000000000000000000000000000000000000 svn.713 5f5ce1d0313d39aba65fb360c2b43b0f2b6a516a svn.1116 0000000000000000000000000000000000000000 svn.1116 5f6ee3f0aaee445dfb9bb26c8dbef30bd4f2f63c svn.681 0000000000000000000000000000000000000000 svn.681 5f86cdef685f59a577fbf8b91ce8b6419ba83f1c svn.183 0000000000000000000000000000000000000000 svn.183 5fa4de6cb71b9e6b9827a25d7612fb4faa0a51f5 svn.966 0000000000000000000000000000000000000000 svn.966 5fd9f895384805dfd3ebc54e4b27f1acbe452064 svn.140 0000000000000000000000000000000000000000 svn.140 5fe392f15c2ad1970936acae1c40efa804a71276 svn.181 0000000000000000000000000000000000000000 svn.181 60864a7b7dce7563b202c1406adca49d761c6504 svn.1276 0000000000000000000000000000000000000000 svn.1276 60d13dff6a0039f4fa6623a034bd94db7ee7b9ad svn.764 0000000000000000000000000000000000000000 svn.764 60d1b329e1a41b4518091df6131b84b0ef37ad90 svn.542 0000000000000000000000000000000000000000 svn.542 6131ac5ee202b715f9aa5851f86e1142ef531877 svn.1158 0000000000000000000000000000000000000000 svn.1158 6146454a0fe14b8258bf030278a01db0f1df4404 svn.1033 0000000000000000000000000000000000000000 svn.1033 61df1dd85a78eb81890b01fbe2fa91ef4315ecf7 svn.1593 0000000000000000000000000000000000000000 svn.1593 61f2232514ecd36ae07e1e7d4c199c207de9f047 svn.190 0000000000000000000000000000000000000000 svn.190 61f44c6c06abdd3a459eaf4cb84e4ce5689f5b60 svn.845 0000000000000000000000000000000000000000 svn.845 6222bb8d5b37ae0132d8ebd7bd4b7a48db94e684 svn.1703 0000000000000000000000000000000000000000 svn.1703 62631b24fc1de5dacd71f9e72db09763c3cf6268 svn.573 0000000000000000000000000000000000000000 svn.573 628d1287a686c1410b25f5911527f11700800d71 svn.1556 0000000000000000000000000000000000000000 svn.1556 62aa37991851a45af59b39bca7a205454c62ff4b svn.1032 0000000000000000000000000000000000000000 svn.1032 62bc90b08b309e14eafe31ce70ba0b4b50714a10 svn.67 0000000000000000000000000000000000000000 svn.67 62db9cb4103179bdc81fc4aa6dc2c06b51ecdc15 svn.117 0000000000000000000000000000000000000000 svn.117 630f4d0b6c7f16d54298d5d9950cdad53ed0cb6d svn.1251 0000000000000000000000000000000000000000 svn.1251 63150727b1c7fecfa9ce6464779818683fb19487 svn.905 0000000000000000000000000000000000000000 svn.905 63283a4591bd46de9a1d814d068f68cc43a9c976 svn.1120 0000000000000000000000000000000000000000 svn.1120 6374e52f7c0628aa7e52154913ccbd9fa9c24330 svn.279 0000000000000000000000000000000000000000 svn.279 6399da8dd8baccfe0d3c23e1a79e38e6c5830f30 svn.737 0000000000000000000000000000000000000000 svn.737 63b2f81eede89f8ffca37bfc9346336a1947b862 svn.1659 0000000000000000000000000000000000000000 svn.1659 6404f6fc8d5b21a924d342f6acc3cea816f7f23f svn.1755 0000000000000000000000000000000000000000 svn.1755 640a15ddafccbebe106d2b21793cea1c68946a49 svn.1055 0000000000000000000000000000000000000000 svn.1055 6468c80e3d4a0ee4abea5b54c373e48d503ba593 svn.1683 0000000000000000000000000000000000000000 svn.1683 646c70e7e5f886dd105d69a66acd1f5c98bcc5c0 svn.72 0000000000000000000000000000000000000000 svn.72 64a713646ea00b15ff2939c56b194137cac32e5b svn.442 0000000000000000000000000000000000000000 svn.442 64d34a6c43b5d5ced5ea8c59070b9c2c10995f8b svn.97 0000000000000000000000000000000000000000 svn.97 64eb4a5f8d329069fb4f3275806980653f0d2276 svn.229 0000000000000000000000000000000000000000 svn.229 64fc2c2c26c8db2336b1e0ed2b797f59a63a742d svn.784 0000000000000000000000000000000000000000 svn.784 654597a4199eed35dfa820947a59a7c18d723474 svn.1113 0000000000000000000000000000000000000000 svn.1113 654ad8eff684ed6ca368a00610e5dc2ce86ae3ff svn.970 0000000000000000000000000000000000000000 svn.970 65602799686748be921aac98aa0c2ef5a1fffa2f svn.723 0000000000000000000000000000000000000000 svn.723 6593d04b9f7cbd1f04d6fd89f3a999c9090caa9d svn.1526 0000000000000000000000000000000000000000 svn.1526 65b6222af795244499cd3c0202b5114dda0045e0 svn.1801 0000000000000000000000000000000000000000 svn.1801 65bb67f50da97f1a23c3f36bb9b62496658e9548 svn.1570 0000000000000000000000000000000000000000 svn.1570 65fd080af335c1b0e73c167521e24e9fc48c5d1d svn.1584 0000000000000000000000000000000000000000 svn.1584 661648ac517628eb254a31e2bc703d92b999782e svn.1294 0000000000000000000000000000000000000000 svn.1294 662560be753e9b4a9623791070baf0b86f1b84d0 svn.1488 0000000000000000000000000000000000000000 svn.1488 6662340b2f89c68c21db7a8299a7082e1dfe8d76 svn.1727 0000000000000000000000000000000000000000 svn.1727 667d55685a4a9007c7a142f2121fd1eee585b93b svn.727 0000000000000000000000000000000000000000 svn.727 6687e9fcf14eceda46be690d854e88c645ea5f04 svn.1691 0000000000000000000000000000000000000000 svn.1691 67096d5c2f230606afb316251be252c698cdfe7b svn.609 0000000000000000000000000000000000000000 svn.609 6723708753a0619323dd98434f752422f0a02cb9 svn.1325 0000000000000000000000000000000000000000 svn.1325 681fea5f217b1434801bf4693c5d891f0d0902d1 svn.883 0000000000000000000000000000000000000000 svn.883 6859b5230d62685f9a1be0e18f4f3e69b6afcaca svn.908 0000000000000000000000000000000000000000 svn.908 686a4b9d4f6b43f78a50735357253ab86c8c67b8 svn.1080 0000000000000000000000000000000000000000 svn.1080 687342e70dd55711de85c904f60f3b15f291f60c svn.413 0000000000000000000000000000000000000000 svn.413 687bcd6b35ab8d1e106f0e92520641bb20c6d464 svn.1025 0000000000000000000000000000000000000000 svn.1025 689b60640d60e1a36429e6f979a6cc06f2be0a11 svn.1686 0000000000000000000000000000000000000000 svn.1686 68b5418c549309274ab2caf5872b1bcab3ef89ab svn.937 0000000000000000000000000000000000000000 svn.937 68c17c12fc55b70c72ff10571f1dd1b0bfec03ce svn.1207 0000000000000000000000000000000000000000 svn.1207 68c3e7c4f2039b94b3b35150d1f5bd040081359f svn.1440 0000000000000000000000000000000000000000 svn.1440 68d2052f11c58ed5274309bcb2fabdf46cbd15be svn.566 0000000000000000000000000000000000000000 svn.566 68eba7c3b1dc0b8f9a546acf7722c9b7223919f2 svn.236 0000000000000000000000000000000000000000 svn.236 68ffb81df06dfe917f1515bbf66ec957c53ee51a svn.520 0000000000000000000000000000000000000000 svn.520 692c0b3e97df1a6324a0a97dbe792bc602c499ce svn.172 0000000000000000000000000000000000000000 svn.172 697a25de00456d499e7e738e8fc8e7998621b277 svn.617 0000000000000000000000000000000000000000 svn.617 698232e5ae717bd73a966b3d7534de356230423d svn.801 0000000000000000000000000000000000000000 svn.801 699ed7fe298ed364faa6b6bfdd9bf07705f3ede6 svn.804 0000000000000000000000000000000000000000 svn.804 69af6d8252c18596851585164364b63034963663 svn.1311 0000000000000000000000000000000000000000 svn.1311 6a4c7518d9d62ec15209325b3332b4c6be89801b svn.503 0000000000000000000000000000000000000000 svn.503 6a81cc9d3b086744e03f22ab687f2b3fae123b72 svn.1220 0000000000000000000000000000000000000000 svn.1220 6a9c3420d3ee2c6319c5c3a754bdee278d1c9373 svn.130 0000000000000000000000000000000000000000 svn.130 6b089c0f0a634d5638c21a3194a385592ee0946c svn.1362 0000000000000000000000000000000000000000 svn.1362 6ba57547250acbac6bcff8e2f8938e7e65125ff3 svn.605 0000000000000000000000000000000000000000 svn.605 6bbb4ccb07cd993eb5c92481dc46b1f73639c10a svn.717 0000000000000000000000000000000000000000 svn.717 6bf4f8d5eff7a64b6d905f0255ba9f79c492851b svn.948 0000000000000000000000000000000000000000 svn.948 6c1246ac86ccd1d01c56e80d256758b64837e72c svn.220 0000000000000000000000000000000000000000 svn.220 6c545b458dc785a1d22929fe38fc9ef122ed3e62 svn.918 0000000000000000000000000000000000000000 svn.918 6c6edf1ce01f47f9a93ced398c14e44e287a65df svn.1653 0000000000000000000000000000000000000000 svn.1653 6c81db75b3713c0c889b70aca2e1f79ca358d0d1 svn.931 0000000000000000000000000000000000000000 svn.931 6c8b5837bc619afb424e773811a2812ce7e2d1b8 svn.619 0000000000000000000000000000000000000000 svn.619 6ca95e61d7bbf5829ffb21a4e53569ac9ed9109b svn.239 0000000000000000000000000000000000000000 svn.239 6cb0fd747334aadec07001c234477c6c432c8b9a svn.647 0000000000000000000000000000000000000000 svn.647 6cdeac9c78459431afd34a2889c791f8136caf91 svn.1694 0000000000000000000000000000000000000000 svn.1694 6ce14ad1e51f11d3a12f9e87336a824027db1d5e svn.691 0000000000000000000000000000000000000000 svn.691 6ce695d22d29d5830eb088c3ed4e8b0686e8161c svn.919 0000000000000000000000000000000000000000 svn.919 6cf9a71903dd8c412769486fd3e49d883f0a662d svn.443 0000000000000000000000000000000000000000 svn.443 6d2a962a88f101661e49a204a2b927b1ff691f48 svn.678 0000000000000000000000000000000000000000 svn.678 6d3b895da46ff0df28d07ff1b44aeafab92420e1 svn.632 0000000000000000000000000000000000000000 svn.632 6d619bcb275f9b26471116f080ec40806fb39f57 svn.1287 0000000000000000000000000000000000000000 svn.1287 6e103b5715f877a532d24467a72ce798e14d7917 svn.1406 0000000000000000000000000000000000000000 svn.1406 6e1088ed75b3e709e1c1303d4a6174a14092e315 svn.650 0000000000000000000000000000000000000000 svn.650 6e36c78fa82cd595dffb857cfcc61974596ffda5 svn.1547 0000000000000000000000000000000000000000 svn.1547 6e4b1b54fb1655d880c73c0b586fa9846e3e8406 svn.1688 0000000000000000000000000000000000000000 svn.1688 6e7bbb959a509d936fec8de148068fd5eb4e0e65 svn.1587 0000000000000000000000000000000000000000 svn.1587 6ea12918249c2998d5322490e2d10c2ebfb850b1 svn.837 0000000000000000000000000000000000000000 svn.837 6ea7649495f915e64fa6774b9c3432ccea684619 svn.381 0000000000000000000000000000000000000000 svn.381 6ec1218a11b3cceaea156fdc3925b95b5c16ea8b svn.915 0000000000000000000000000000000000000000 svn.915 6ef8fcc1c3cb89dadd48075eca10ecbf1521b343 svn.1081 0000000000000000000000000000000000000000 svn.1081 6eff9c0a4e808a63f5a4ddcdfef919e04d289596 svn.493 0000000000000000000000000000000000000000 svn.493 6f38cb6673f788712881c4f21a4976347a4704b1 svn.1511 0000000000000000000000000000000000000000 svn.1511 6f4eaaa712be7e5e560908b01de6e21a64dff80d svn.127 0000000000000000000000000000000000000000 svn.127 6f67d9ddf8d17e4274170ac479cd38e8eeedd5f9 svn.1142 0000000000000000000000000000000000000000 svn.1142 6f984ee68a329be4627566d3cfb74e902b9478ef svn.46 0000000000000000000000000000000000000000 svn.46 6fc6d87835f7cda31110259830cb290751782550 svn.1641 0000000000000000000000000000000000000000 svn.1641 702eaddefa978d541c1827b8aaccadd1fb936637 svn.433 0000000000000000000000000000000000000000 svn.433 70372e292703c1ee793ee744230c04aa2394616a svn.780 0000000000000000000000000000000000000000 svn.780 708164359b80fd2fd9ab08eda67d8cd12bcae4b7 svn.343 0000000000000000000000000000000000000000 svn.343 709dc1862b92b31553ccc62257d14a1e7d94df39 svn.923 0000000000000000000000000000000000000000 svn.923 70a1a0a74e029a222b621a0b51d34aa1eed96c95 svn.491 0000000000000000000000000000000000000000 svn.491 70b7809f00af3a82fbe52f4a31bbed429c57fedc svn.1403 0000000000000000000000000000000000000000 svn.1403 70d83691afa7c302cf779c4318c155711f18a1c4 svn.1657 0000000000000000000000000000000000000000 svn.1657 70e2a6cfac20b5db6fad69c089c04fe85c28ce66 svn.1539 0000000000000000000000000000000000000000 svn.1539 711eff34e373cf3adb738ac08b5e3d6bef81167d svn.1349 0000000000000000000000000000000000000000 svn.1349 712da1b22a9adcb3376d45aab846fea2863dc643 svn.677 0000000000000000000000000000000000000000 svn.677 71d855ee2ff5db8d2031e21ef1110f8d1e25d349 svn.947 0000000000000000000000000000000000000000 svn.947 71df7de83845443319030e411ca63311b94cb2b7 svn.1478 0000000000000000000000000000000000000000 svn.1478 71ed0fd7b8b04dfb59e9d042ad5c16fe03e4603c svn.17 0000000000000000000000000000000000000000 svn.17 71f40c26df79f4b2abe46202b8702e1e7401f1cd svn.311 0000000000000000000000000000000000000000 svn.311 72142f3034df364a1b4c6744029df460e5dd08b7 svn.708 0000000000000000000000000000000000000000 svn.708 7237c3e3741bccf2b48c8761b29ec61f7a74140c svn.1453 0000000000000000000000000000000000000000 svn.1453 72f6e65b00f97472581f40f699677f3295d02f74 svn.1274 0000000000000000000000000000000000000000 svn.1274 735372855c47bebac5cefb53729706320c3e5db5 svn.933 0000000000000000000000000000000000000000 svn.933 73595cab8615e185936ee4f2f5ce8ffd0ba00193 svn.1093 0000000000000000000000000000000000000000 svn.1093 736b7d80d82e8c49990daa69d6ee152c3f549964 svn.1246 0000000000000000000000000000000000000000 svn.1246 7394cb16a2a56787d29fb2c1238cfd4375a32609 svn.578 0000000000000000000000000000000000000000 svn.578 73a5f43fbe5bf5a022fbaff1c1f681759b24caab svn.1665 0000000000000000000000000000000000000000 svn.1665 74a4cd9f3ae216250fec79d31d996cf1ca77b84c svn.969 0000000000000000000000000000000000000000 svn.969 74cac79b667c492030da162ecd3d1a7dc67f1a08 svn.101 0000000000000000000000000000000000000000 svn.101 74cdf93273b4464daaeffab3a89d3e78da35bc0d svn.53 0000000000000000000000000000000000000000 svn.53 74e0d6b46fd1d62791d1733a06eb09e4b8fe831f svn.240 0000000000000000000000000000000000000000 svn.240 74f83c44f9a21237d01ab53bd7abb8b8c2d69e05 svn.1168 0000000000000000000000000000000000000000 svn.1168 750a31f01a28f9d0b0d10acbb3ae2c44443484f0 svn.1562 0000000000000000000000000000000000000000 svn.1562 75210b13d831d6480f07f3972ebe7efca4518663 svn.876 0000000000000000000000000000000000000000 svn.876 75309fa251bb4ff0900cbf37a584ec031ee33b16 svn.630 0000000000000000000000000000000000000000 svn.630 755d312a7136f6f5a569fdfaec07ad68bac257bf svn.1103 0000000000000000000000000000000000000000 svn.1103 75a2b0dd253f65fe75245d39f99497dad64da56a svn.1085 0000000000000000000000000000000000000000 svn.1085 75ac4861c335b9aff04c88be698b230090b03670 svn.839 0000000000000000000000000000000000000000 svn.839 75c736efedb4d64cdb5f027e91ed8ad21347dd2e svn.79 0000000000000000000000000000000000000000 svn.79 75d8c13524ccb9288d4da283aa7ba549b2a3abcd svn.1761 0000000000000000000000000000000000000000 svn.1761 760e0159100ea5fefea7242935982079f2602887 svn.1794 0000000000000000000000000000000000000000 svn.1794 761f86756a571580948a3150ff87221385366a3e svn.1558 0000000000000000000000000000000000000000 svn.1558 7666c6bbca4e4e3919706326334cae0053f31b16 svn.766 0000000000000000000000000000000000000000 svn.766 766e39ca35babe04b4b3250a5b4f0376ecdef01d svn.1746 0000000000000000000000000000000000000000 svn.1746 767a773a9e84eba48e07336691f67c1edbaf5548 svn.671 0000000000000000000000000000000000000000 svn.671 76979776bfc1ec51682e9b97fbeb9d17ab50d32a svn.335 0000000000000000000000000000000000000000 svn.335 76e08fa2a955c3e00c1fd0fee294d843aafdeaa5 svn.448 0000000000000000000000000000000000000000 svn.448 76fe7986694b008839ebbab7f57fa496fdcf6d25 svn.1189 0000000000000000000000000000000000000000 svn.1189 7729282bc88011ed2fab9462b4b31098d0b83b82 svn.1079 0000000000000000000000000000000000000000 svn.1079 772a2004987d3cf59b96d2d9b2975616af016e58 svn.296 0000000000000000000000000000000000000000 svn.296 7737bf992702e8ea55f9b8c9aa445ca679eafc9d svn.806 0000000000000000000000000000000000000000 svn.806 773914366850d4cdf88ba6357f438cd2427f4756 svn.1090 0000000000000000000000000000000000000000 svn.1090 77480357e062086dfa46e00a21d21254509573ff svn.1010 0000000000000000000000000000000000000000 svn.1010 7752741ad8b4c001ade2fe52d850be728c9a6d8f svn.576 0000000000000000000000000000000000000000 svn.576 7777f6ab1b807c8ea8374e6ce51c7e23d1a64a2b svn.735 0000000000000000000000000000000000000000 svn.735 77ad4648ed8d7caf993cbe9a0c9fa98cbd0c7f22 svn.1429 0000000000000000000000000000000000000000 svn.1429 77faf7f8d8624cb1cc71a9abbb389264633f245c svn.1599 0000000000000000000000000000000000000000 svn.1599 78684d161c28f74eb080c8e347cc07affe5e2811 svn.11 0000000000000000000000000000000000000000 svn.11 78714479ec4b1655ed63ab753e7eb64194bbce49 svn.1731 0000000000000000000000000000000000000000 svn.1731 7888876baa70393e25dc7ee3f5d5fc0060cbfc69 svn.396 0000000000000000000000000000000000000000 svn.396 78c7a2cd4c6f48e839680c2e0768f61452a1d69a svn.925 0000000000000000000000000000000000000000 svn.925 78c9ba1b013b12576663205993f06b43339a75b6 svn.31 0000000000000000000000000000000000000000 svn.31 78eeba413131359509eea37964279674e88921bd svn.1664 0000000000000000000000000000000000000000 svn.1664 78f011aadd008a23a3cf1c16e659a7bfd01f0b80 svn.1525 0000000000000000000000000000000000000000 svn.1525 78f0aea62cd4526c40099b890429cda11414d684 svn.1213 0000000000000000000000000000000000000000 svn.1213 79375d6353efca8288ef5673c48e7c465b8a72e2 svn.1003 0000000000000000000000000000000000000000 svn.1003 79bc3d944298a10b1621e3dafeaa4a46f6d18069 svn.721 0000000000000000000000000000000000000000 svn.721 79cc0fb85cd94fb15ea9e2ae09c44c638355aa05 svn.43 0000000000000000000000000000000000000000 svn.43 79f2c675f9dbf256fd7c730bd00f742124be519e svn.126 0000000000000000000000000000000000000000 svn.126 7a38f6e17dc8c0350c274892ddd761bf5b35cca7 svn.310 0000000000000000000000000000000000000000 svn.310 7a4795f2964b41b738e697cce7cec0bbeef758e1 svn.1009 0000000000000000000000000000000000000000 svn.1009 7a4a905ac83dc3dce44a6b8954221e7a937b1b39 svn.936 0000000000000000000000000000000000000000 svn.936 7abdf2775d80674580dcb2ee742af8ce3a28bf80 svn.1439 0000000000000000000000000000000000000000 svn.1439 7ae0e86d0ae77ac1e40bf9fa7b42cdcebb32ea11 svn.698 0000000000000000000000000000000000000000 svn.698 7af1b07907d92d18a1891139956cc0acc63a34f3 svn.1339 0000000000000000000000000000000000000000 svn.1339 7af46f2766cee10bb777ec28057d33027f54e582 svn.1148 0000000000000000000000000000000000000000 svn.1148 7b093458569729c245fcbbcff0264a7ec9ddb0aa svn.280 0000000000000000000000000000000000000000 svn.280 7b3988fc85a4f3547220effcd9fdc01c85ae29d3 svn.1258 0000000000000000000000000000000000000000 svn.1258 7b4ea78d32f125a8ed976d98fd7a8270c61ba145 svn.265 0000000000000000000000000000000000000000 svn.265 7b79ca45ed746cda3e4be06913a4fc938aac2f2e svn.1169 0000000000000000000000000000000000000000 svn.1169 7bc1265533579cb4e7542be7376d77a2bbeb42f2 svn.119 0000000000000000000000000000000000000000 svn.119 7bea2bddb35fe07eaeea753cab6edc8486df96b3 svn.1286 0000000000000000000000000000000000000000 svn.1286 7c29c4151f71a24a11af92a03468db6656575db0 svn.1792 0000000000000000000000000000000000000000 svn.1792 7c2a177f330a773e3378db85d194dec33f84043a svn.1733 0000000000000000000000000000000000000000 svn.1733 7c341522bf86c7e10bb655f7f6314aa34eca4b16 svn.465 0000000000000000000000000000000000000000 svn.465 7c3e587ecad52c6e36daf00795fdb3f7eaa065b2 svn.1616 0000000000000000000000000000000000000000 svn.1616 7c5785af82057e3a3854e5a3f757f9ba0b7d3d63 svn.697 0000000000000000000000000000000000000000 svn.697 7c73ea93d3baf7668938452a5c6ec66aeb8288d8 svn.1105 0000000000000000000000000000000000000000 svn.1105 7c7d8e04c7806fea442209f75fec5b5b5d753c94 svn.831 0000000000000000000000000000000000000000 svn.831 7c8b269f72c0cf2e3a672b0d21b931b89776e4e9 svn.10 0000000000000000000000000000000000000000 svn.10 7c903124520810e58dcdac13bd2ed621e12f428b svn.817 0000000000000000000000000000000000000000 svn.817 7c9cac9f2405fbcf56e45a7a557b1ac8062dfb19 svn.1050 0000000000000000000000000000000000000000 svn.1050 7caae7bbcb13c6b8485c0db265f7f69dbe1db4e3 svn.1368 0000000000000000000000000000000000000000 svn.1368 7ccb68386f7d9364e65edba381b48fc14050b311 svn.1781 0000000000000000000000000000000000000000 svn.1781 7d0f2b21b8275bf67204c564ea9ac3924d09d24c svn.1066 0000000000000000000000000000000000000000 svn.1066 7d2a9e665687315a5d1d69e5e9376442f79dae77 svn.1235 0000000000000000000000000000000000000000 svn.1235 7d2f5d03022601376011f87f26bc0c36ae0ec1c6 svn.1315 0000000000000000000000000000000000000000 svn.1315 7d309c66861fce2272a43bca230d4ba836f73883 svn.1763 0000000000000000000000000000000000000000 svn.1763 7d6ad92040f2d8b47aaad7c7abbfff718b66c57b svn.431 0000000000000000000000000000000000000000 svn.431 7d6cf76197562ee72b1974a83a30f93c82435a2e svn.471 0000000000000000000000000000000000000000 svn.471 7db82d6cda20f252f4733db0ef9102d6ec286e06 svn.489 0000000000000000000000000000000000000000 svn.489 7e0d7092347ea2b7624d564cf13d71256b0b60ec svn.452 0000000000000000000000000000000000000000 svn.452 7e2d16dd5cf4f8d8172cc7e5751439aad3ecd447 svn.1418 0000000000000000000000000000000000000000 svn.1418 7e2e758ee8c97c7ce30278510e2a98f7d6ca395e svn.1341 0000000000000000000000000000000000000000 svn.1341 7e45cc2e299134dcf3ad2acf71201fe776d67454 svn.1752 0000000000000000000000000000000000000000 svn.1752 7e499a54d7b873e421c947ca39dbefaefaaa320c svn.598 0000000000000000000000000000000000000000 svn.598 7e6075aa2eccd5b67e3e5b7b315d2278b2e935e1 svn.1345 0000000000000000000000000000000000000000 svn.1345 7e65dffd52574ce677cbcef4ce759075cf3fda5a svn.960 0000000000000000000000000000000000000000 svn.960 7e71cef8e31d2c47a572349bfc239fefac96667d svn.164 0000000000000000000000000000000000000000 svn.164 7e7368e614c8032e21271a16632ef023a49ee8b0 svn.601 0000000000000000000000000000000000000000 svn.601 7e7a74d7ccb5b49b1748b3edeae17ff57781bd35 svn.999 0000000000000000000000000000000000000000 svn.999 7e84bbe40c5b7cffc577533ccea6a6fd61dc3c1b svn.656 0000000000000000000000000000000000000000 svn.656 7e93a63d6a63caf65d7de26223500352cea35c14 svn.1508 0000000000000000000000000000000000000000 svn.1508 7ee5f2feb9d59aad4eb6af41de7ba2d62494acc1 svn.652 0000000000000000000000000000000000000000 svn.652 7ef6994a5fadebf07519c641cf4b72d824cff518 svn.1693 0000000000000000000000000000000000000000 svn.1693 7f3fd5b887bb0fd1a54a948aa1b5d1026e6bdb96 svn.89 0000000000000000000000000000000000000000 svn.89 7f538f497ed08bd28b75ce150f261436e7dddce3 svn.1145 0000000000000000000000000000000000000000 svn.1145 7fa3ac5cade9adaccefa58726779de6adb1087a1 svn.868 0000000000000000000000000000000000000000 svn.868 8015f503c5ec815b64e60a3a67881cfc4c6df7cc svn.597 0000000000000000000000000000000000000000 svn.597 80ac83abd52bc4809da224207756eb3da581449d svn.505 0000000000000000000000000000000000000000 svn.505 80bad282cbf8785f907a1c198a8161be5a950057 svn.124 0000000000000000000000000000000000000000 svn.124 80cc3028703002db687c12bb3ceb64f5d6a53ed7 svn.474 0000000000000000000000000000000000000000 svn.474 80cf6aa16a2702f47990aef67530abf723df077a svn.701 0000000000000000000000000000000000000000 svn.701 80e43391ea11ba35c3ade9a0427f3b76a1db756e svn.688 0000000000000000000000000000000000000000 svn.688 80e7766e9707df387d86d6557efd16208da60b96 svn.1083 0000000000000000000000000000000000000000 svn.1083 8118a10e79c3287b3df44eb9bb3d83a3499f20a1 svn.673 0000000000000000000000000000000000000000 svn.673 812a377719680b1817d6b4d78d88cec45ed11f0e svn.1254 0000000000000000000000000000000000000000 svn.1254 81393d6765455575a8fb15e37dfba5eaf9f9e3ee svn.1472 0000000000000000000000000000000000000000 svn.1472 814b7ff8d33c2aeeff8d2f309e05f541201bb6a3 svn.615 0000000000000000000000000000000000000000 svn.615 81556cdc5f31b00d5c0cc126799cb8b5885ff312 svn.866 0000000000000000000000000000000000000000 svn.866 815662a641f5755da44ff6727ca0f90933f9979f svn.340 0000000000000000000000000000000000000000 svn.340 8182c58ad0f3406cf84c30634976f45df119e87f svn.1677 0000000000000000000000000000000000000000 svn.1677 81938e1ae18d988e40ee758b8e7885d5ca6832b0 svn.612 0000000000000000000000000000000000000000 svn.612 81a8b445cc4308eb0e45c9e15b88769bb823a1e9 svn.1018 0000000000000000000000000000000000000000 svn.1018 81b3007d285a41c18068d8f486bc53359e20a842 svn.1684 0000000000000000000000000000000000000000 svn.1684 82063f87059b4cf0eb15fda9372c18348a6eeb6f svn.156 0000000000000000000000000000000000000000 svn.156 824e85d24342fcbd328df485425677a2bc510066 svn.1342 0000000000000000000000000000000000000000 svn.1342 825e70252776616ce9d6a6e50d0bc3d55b7197cf svn.432 0000000000000000000000000000000000000000 svn.432 828c01a7da237f57e9bdf7846b6dc1e7310bfd78 svn.861 0000000000000000000000000000000000000000 svn.861 82951c5f6967e055c4df8413a7ef801b1503a5cc svn.418 0000000000000000000000000000000000000000 svn.418 829e66a249048caf82f38cbed01567a37b6e54e4 svn.808 0000000000000000000000000000000000000000 svn.808 82c3945c1bbe5e4757e485df57b7d7af4e25d87f svn.557 0000000000000000000000000000000000000000 svn.557 83263e58fd561ac31176f8257cf93e0f99306a33 svn.867 0000000000000000000000000000000000000000 svn.867 833234d863be03580d95c3978aa7ab5800b7fbdb svn.1586 0000000000000000000000000000000000000000 svn.1586 833a684b19f01a76e6e083ef4e337f5b5671fbdc svn.1496 0000000000000000000000000000000000000000 svn.1496 833af911bb6950ed186807b033858421dc9998e9 svn.425 0000000000000000000000000000000000000000 svn.425 839a901fc4ee1d5bb5f297d218daf712db35c2e2 svn.848 0000000000000000000000000000000000000000 svn.848 83b99a433087db9d4dab352bee880eaf505a9c50 svn.205 0000000000000000000000000000000000000000 svn.205 83e37d75ef4005c9accb6b292078cc153fb68808 svn.1019 0000000000000000000000000000000000000000 svn.1019 83fd1be26619c2509822c713280d1c9719800d38 svn.872 0000000000000000000000000000000000000000 svn.872 8406869a75731e17bb48da3dbba9dd22e036cc51 svn.1038 0000000000000000000000000000000000000000 svn.1038 84135ca746d9115dcf06505e5dc271502515667c svn.1245 0000000000000000000000000000000000000000 svn.1245 844549edbfeb810f38de00b3d37162bdd121e2ec svn.407 0000000000000000000000000000000000000000 svn.407 845b5fda8672799a9a9809b02d3e44eb9dc1b905 svn.524 0000000000000000000000000000000000000000 svn.524 84694fb038902d50ac9a399ad4d0ac9d4f3a825a svn.620 0000000000000000000000000000000000000000 svn.620 84938b94351b46f0e04c1ae2a30dafe143c8ca8c svn.1280 0000000000000000000000000000000000000000 svn.1280 849f899f96745f6dfb201a0f08174b5240008c90 svn.1256 0000000000000000000000000000000000000000 svn.1256 84a09db171e3d5289d3416b606c0ae58727787fc svn.1365 0000000000000000000000000000000000000000 svn.1365 84a29238ece32992a21439cc1af6d4773739d47c svn.705 0000000000000000000000000000000000000000 svn.705 8541a3f921131d33fd10cb03c4af2c0605766be8 svn.712 0000000000000000000000000000000000000000 svn.712 858f614dd5289a2905e13f5a0d7e56519f65d359 svn.984 0000000000000000000000000000000000000000 svn.984 85a52aea151770555ca64a81003de926b4254de1 svn.731 0000000000000000000000000000000000000000 svn.731 861a4b3e654b4af71a3fd84216e91805e93619a0 svn.1536 0000000000000000000000000000000000000000 svn.1536 862306a8cc96362c083ba6ef874375894d9e1482 svn.649 0000000000000000000000000000000000000000 svn.649 8628c6c2dd265f2cf0c86ca9e33c3e86a4baec11 svn.1340 0000000000000000000000000000000000000000 svn.1340 864e21d9fd45d24ef74b6a81ea7d8eecfcdbe56a svn.978 0000000000000000000000000000000000000000 svn.978 86c8c50cb6298a23fddd6b00c6dbf9554561924e svn.734 0000000000000000000000000000000000000000 svn.734 86fc114fd878141c10a5829421b1a5bdcc36703b svn.198 0000000000000000000000000000000000000000 svn.198 86fd19b1e4db6116e660bad44a8659510bc88350 svn.171 0000000000000000000000000000000000000000 svn.171 871854458a1e4c7254aca166b60b38e5fd997812 svn.500 0000000000000000000000000000000000000000 svn.500 875dae7f06e77ed6cf9237bdc3165d97f79b503d svn.771 0000000000000000000000000000000000000000 svn.771 87a40ec50379ad21cc634f4067552d304a0acbd2 svn.860 0000000000000000000000000000000000000000 svn.860 87b4f439784c7827a45f67aca66ea41a9355d9c8 svn.1568 0000000000000000000000000000000000000000 svn.1568 87c95800fb46da2cb4607a1573e3756f0251c5a2 svn.498 0000000000000000000000000000000000000000 svn.498 883691116c1d36a77176710660eb116493b532b5 svn.1514 0000000000000000000000000000000000000000 svn.1514 886d4605de6ca099390b7b8cc2f61fa332efb084 svn.1393 0000000000000000000000000000000000000000 svn.1393 88763cfdd8c05e6cfd4280ce9e3f1c9e8ba1f1de svn.245 0000000000000000000000000000000000000000 svn.245 8886880992615e4e64d9424b2713a3cde6ecc363 svn.813 0000000000000000000000000000000000000000 svn.813 88c2900c197f200bb02dd81bb6dd192f676af620 svn.459 0000000000000000000000000000000000000000 svn.459 89845d23e7587f3afdffb2f08db4855f92a5427b svn.758 0000000000000000000000000000000000000000 svn.758 89a10337dd0662d72d8590e21e80426a915d7856 svn.1363 0000000000000000000000000000000000000000 svn.1363 89aabab3d202d8a59c48e95b4953e76bc07e849d svn.1332 0000000000000000000000000000000000000000 svn.1332 89c0ca6419b02e56437552b95cb1f46de616b375 svn.1355 0000000000000000000000000000000000000000 svn.1355 89df9a19d2e091086c1dba55d9adfd0dca52a17f svn.1726 0000000000000000000000000000000000000000 svn.1726 8a0b4b881ee9cc567f8505cf6479b707d318d161 svn.819 0000000000000000000000000000000000000000 svn.819 8a1ad6b1f8798ad22c2ccce5dcd67921b7b6845e svn.1277 0000000000000000000000000000000000000000 svn.1277 8a6463a94c8cffe4fb5b58ba68da7a207446715c svn.168 0000000000000000000000000000000000000000 svn.168 8a86a5a85de9e27968db5c173005b42ece4c917d svn.965 0000000000000000000000000000000000000000 svn.965 8ab0cf44a1e7f376822f17c166d625ef2174f085 svn.590 0000000000000000000000000000000000000000 svn.590 8b3a5ec0bc0818d54abac3652fb23efb28eda087 svn.627 0000000000000000000000000000000000000000 svn.627 8be3fe249382ae5799d2f9d8908b550b5a218b9e svn.94 0000000000000000000000000000000000000000 svn.94 8be7ecf34c2c2f7e0dcc4f9911183d4b6de7a49f svn.1291 0000000000000000000000000000000000000000 svn.1291 8c1e1d0828136953953642d54dc1779e192d01ae svn.1310 0000000000000000000000000000000000000000 svn.1310 8c2230b937c3ee7ce03b391e4a461bd666517591 svn.463 0000000000000000000000000000000000000000 svn.463 8c85a4d78312dfe9268477441d51a75421fa4ec1 svn.636 0000000000000000000000000000000000000000 svn.636 8cb2595cf85ae6153c354ab362072709d02f930a svn.1674 0000000000000000000000000000000000000000 svn.1674 8cb3788caf6b60b69fd1fa6871365ad77155c976 svn.553 0000000000000000000000000000000000000000 svn.553 8cc439cd4908a37b9a6a003fab64604646439970 svn.877 0000000000000000000000000000000000000000 svn.877 8cf9ef42a2ee8743e4dcc0266c137d7290347dbf svn.1803 0000000000000000000000000000000000000000 svn.1803 8d92c38d2f5071ba04168fa8be79c15a1690a218 svn.711 0000000000000000000000000000000000000000 svn.711 8da7a5197b7205998ee9e1b66ace90707dca4372 svn.1324 0000000000000000000000000000000000000000 svn.1324 8dc392af99318b977258aed56bb551801b964a9e svn.943 0000000000000000000000000000000000000000 svn.943 8ddbfa657c154329561a7f7301fe63af1bee5de8 svn.631 0000000000000000000000000000000000000000 svn.631 8e09cf9dc462012fa145b992a3762a395a45a6c2 svn.586 0000000000000000000000000000000000000000 svn.586 8e1ad8b63c7189647b21a93f31726d5bc989386d svn.337 0000000000000000000000000000000000000000 svn.337 8e1f65b8f1fffaeb3012697ea6563b9ad3518df0 svn.1179 0000000000000000000000000000000000000000 svn.1179 8e46d277270ec44270ba64a0e9b58f049f8f218a svn.1447 0000000000000000000000000000000000000000 svn.1447 8ea4d84c99709c94fafa5b75d92d8dee46e4dfdc svn.1129 0000000000000000000000000000000000000000 svn.1129 8ec1f002099a56763ac301023acfea7daa143caa svn.482 0000000000000000000000000000000000000000 svn.482 8ecf50d7068b1fcdcd4bc00b8584cdf8e3d6d858 svn.1725 0000000000000000000000000000000000000000 svn.1725 8ed67117aece3f5023f28a377e3489f706100215 svn.785 0000000000000000000000000000000000000000 svn.785 8f1c5ce262983f962bae8390bc319ca7856089fa svn.1107 0000000000000000000000000000000000000000 svn.1107 8f536aa935a0fef92ad77d8b4dff108d99d07ac3 svn.961 0000000000000000000000000000000000000000 svn.961 8f8379ae5ba9cd8c61d259b4bf7e4529f061c7dc svn.906 0000000000000000000000000000000000000000 svn.906 8fa7c84becd0341ed84dc67d585e65f3b03f13aa svn.468 0000000000000000000000000000000000000000 svn.468 8fb8e05bdb973fd43d9ce5c8b682680aa5199726 svn.1773 0000000000000000000000000000000000000000 svn.1773 8fceba5b01781a0fd7f4fc3fab2c5a2f6e44423c svn.911 0000000000000000000000000000000000000000 svn.911 8fe94e553994a68e484f871b857824b9b8def4a1 svn.1385 0000000000000000000000000000000000000000 svn.1385 901c52a909f9c90d3ef0997c1e3b57f6df63da01 svn.392 0000000000000000000000000000000000000000 svn.392 905743992786d73e673b909a33e0edb12a2e5d8a svn.345 0000000000000000000000000000000000000000 svn.345 9057a1191c47b0184bbf355f21cf1fcebaf16311 svn.217 0000000000000000000000000000000000000000 svn.217 907c568da1bf769526447f02f8b4a57a62079b55 svn.996 0000000000000000000000000000000000000000 svn.996 907feca5bf0c6ac06642cb576bf2254b8b62ac7b svn.1481 0000000000000000000000000000000000000000 svn.1481 909e90b6ff19245a54a064ae75e378a0d91800cc svn.270 0000000000000000000000000000000000000000 svn.270 90b9e95b83b8ca6acdaf5bd13e4f4d1f1361588f svn.488 0000000000000000000000000000000000000000 svn.488 91357ef20a1f3f11419c4da9fde1e12708ac838b svn.1444 0000000000000000000000000000000000000000 svn.1444 913fcb4f0c79472bbd93e9824c62c8aa662b58d1 svn.770 0000000000000000000000000000000000000000 svn.770 913fe489ac4e60e9930281b59c0dea68a4bcd323 svn.637 0000000000000000000000000000000000000000 svn.637 9159ca04bf16f61e63e01bd20a8c4afffbc699c2 svn.1153 0000000000000000000000000000000000000000 svn.1153 916a361287e99135ddb8e33b2818aed0ffec6127 svn.351 0000000000000000000000000000000000000000 svn.351 91b2973186627526d836f21b172e5ed2377cbabd svn.1699 0000000000000000000000000000000000000000 svn.1699 91d0807d5270f3006ca34f0ccd9c4a2b81a47cf8 svn.565 0000000000000000000000000000000000000000 svn.565 91d52e06894d13f2b8da13baa2d8ddfeb1e049a1 svn.1206 0000000000000000000000000000000000000000 svn.1206 91e09807e2ae7128e2f5e32b8906935a6bad8685 svn.1395 0000000000000000000000000000000000000000 svn.1395 92098b0fdd7a4d1d5af9a1bf419709a3f4ac83e2 svn.1502 0000000000000000000000000000000000000000 svn.1502 92239fdd340a536e2cf989d3f32922d48a83aedb svn.1494 0000000000000000000000000000000000000000 svn.1494 927a21cab86adc02d2117edf55e2a4ab8b83d005 svn.73 0000000000000000000000000000000000000000 svn.73 92820090a70b3d146ccaf34087fd5960e883419f svn.510 0000000000000000000000000000000000000000 svn.510 92e482a7a589de11eba163373bca29010a5358c7 svn.823 0000000000000000000000000000000000000000 svn.823 9319791488d21c4f716cd76246a50a3c3a93ce13 svn.567 0000000000000000000000000000000000000000 svn.567 93321a880a99433b2efc5feb81ec6bd92cfbb4eb svn.810 0000000000000000000000000000000000000000 svn.810 936376b8be15747cb0cb070de24d03e69d492029 svn.257 0000000000000000000000000000000000000000 svn.257 9377e463277a33a9d103212940ae6b032680e505 svn.210 0000000000000000000000000000000000000000 svn.210 9385dddafe44b9aa0b96699d658c07d389de2e13 svn.857 0000000000000000000000000000000000000000 svn.857 93f9d91f08e3e8f153a08c83db665283b6a627e6 svn.479 0000000000000000000000000000000000000000 svn.479 941cb047d4aaf379d4066c975b68fb831b7a9008 svn.693 0000000000000000000000000000000000000000 svn.693 945069070553349eb8ee87bb1cbfda780cbb7ac8 svn.975 0000000000000000000000000000000000000000 svn.975 94b38e8ff8b357d66128c0cf658961c5f509b7de svn.865 0000000000000000000000000000000000000000 svn.865 94dbd328cae27a2fc2bdfb5fc08bdfbde2d0eeb7 svn.299 0000000000000000000000000000000000000000 svn.299 94f4cd4f4a35dca050dc279a66791adca6f0ce40 svn.1642 0000000000000000000000000000000000000000 svn.1642 951195779aff62f5bb0cb10e2aa06c731e972ab6 svn.1031 0000000000000000000000000000000000000000 svn.1031 9535b96f0104968ecf5ab3c9e7468ce23a904a96 svn.1689 0000000000000000000000000000000000000000 svn.1689 9559bda18b4e856a9749c744165735c5155ebfe6 svn.1267 0000000000000000000000000000000000000000 svn.1267 9559d07fbce62a85ccf4b20db90e9ab3ac61c51e svn.516 0000000000000000000000000000000000000000 svn.516 955a7a3e4c97e9e098e9f11b822946a96cd4c2ca svn.74 0000000000000000000000000000000000000000 svn.74 95636bc3bc1bc1cdfdc960cd5845d26bc1570a02 svn.1555 0000000000000000000000000000000000000000 svn.1555 957781635f75d753bd6383ab20fcfdabc3600f31 svn.672 0000000000000000000000000000000000000000 svn.672 95bf6395b37c4ba5ae0a5b101682ab5ca5663d69 svn.1739 0000000000000000000000000000000000000000 svn.1739 95d6cbc8a254c02e9371c64111cf6ea865ad8322 svn.1216 0000000000000000000000000000000000000000 svn.1216 95d858cd57c1d58933493c48116f1034ab3a8e62 svn.15 0000000000000000000000000000000000000000 svn.15 95ec3bcb988f378331758ab60a4be3df7569bcc4 svn.967 0000000000000000000000000000000000000000 svn.967 960fc803e2df4ede8b2b874d86107f21c4015c6d svn.508 0000000000000000000000000000000000000000 svn.508 9640f12726ac9ff6f41e3367088128958e61e212 svn.232 0000000000000000000000000000000000000000 svn.232 965aeb33157cb092f6c567d70cd03b33cac03d9a svn.238 0000000000000000000000000000000000000000 svn.238 968eb2518b5e3ec10d5f97929c4826527f824f91 svn.355 0000000000000000000000000000000000000000 svn.355 96a8ac92924149e0b53b409da391b2c3157fcb94 svn.353 0000000000000000000000000000000000000000 svn.353 96d05f286624040171e9b951269b48d7207c1837 svn.1521 0000000000000000000000000000000000000000 svn.1521 96d1c770f30975d456e726181723fc236418e52e svn.1771 0000000000000000000000000000000000000000 svn.1771 9704d8eee09e9aa7ed8a25bb370290e7c165e226 svn.128 0000000000000000000000000000000000000000 svn.128 971a354621a1d9d8cdb55c5984b9bc0b04a20bd1 svn.348 0000000000000000000000000000000000000000 svn.348 9732bcae276ae99e9e0811f3c1363c88e7d37fdb svn.608 0000000000000000000000000000000000000000 svn.608 9734bf27770c8267984a34566db598b3e5728aae svn.1152 0000000000000000000000000000000000000000 svn.1152 97588038bbf47ca913de4f468442e1cd86472d19 svn.610 0000000000000000000000000000000000000000 svn.610 977e4a4d07b40a3b25bd1d70c59224684fae3441 svn.667 0000000000000000000000000000000000000000 svn.667 97b4a4692fada54dadb2d1f5a20d10bebc2f2caf svn.188 0000000000000000000000000000000000000000 svn.188 97df23113a045033329931b2d5a4fac8cef9c6b3 svn.714 0000000000000000000000000000000000000000 svn.714 97fba1eba19a95247d655c901ffe6e182b501d19 svn.1442 0000000000000000000000000000000000000000 svn.1442 9800c5ba2109682bc8cc6a3ccb43f4df78196423 svn.339 0000000000000000000000000000000000000000 svn.339 9876f685c311b200fe993f176ac7e689862dce5d svn.763 0000000000000000000000000000000000000000 svn.763 988ec6d271a4678be3f522b5adaa569e78e62a85 svn.14 0000000000000000000000000000000000000000 svn.14 98911d91b9f92be65ead0ec3c76ddc1ec64f08fa svn.588 0000000000000000000000000000000000000000 svn.588 98a119a759a65fdc3737917dca91e10cd2614595 svn.1589 0000000000000000000000000000000000000000 svn.1589 98a64b855f10fb4c4ec0fb26b400c930486db61f svn.278 0000000000000000000000000000000000000000 svn.278 98ad0b1eea20941012842a7cc35bdc057ea6eeab svn.1194 0000000000000000000000000000000000000000 svn.1194 98f12553b78be97ab4a82288e826457a83e59a1f svn.600 0000000000000000000000000000000000000000 svn.600 9921dc9ed98bcb362576f96422ab51833890a3eb svn.1040 0000000000000000000000000000000000000000 svn.1040 9931de9a9d88ddd1ef0cad7d0cb3eeb3de899a4e svn.211 0000000000000000000000000000000000000000 svn.211 993773404e8211c08aba35c481f7f8f8af95cf2e svn.26 0000000000000000000000000000000000000000 svn.26 99652ca45322ff8219909a55c6aea132ab8fc846 svn.1096 0000000000000000000000000000000000000000 svn.1096 99c15130eb2c9aca7a058421f04342790b77e48b svn.1309 0000000000000000000000000000000000000000 svn.1309 99f8230eb4fd93cbef225aeae1d025ca77101047 svn.1436 0000000000000000000000000000000000000000 svn.1436 9a1ebd1808dc4fbc99cd536fa5bef7e562394a5d svn.894 0000000000000000000000000000000000000000 svn.894 9a71ecc65e66c69b2993aec0873dac3a3b3940c7 svn.460 0000000000000000000000000000000000000000 svn.460 9a93ef3bf70e27d9e625e9b0e8a79ff009be3b0c svn.52 0000000000000000000000000000000000000000 svn.52 9a9ada9118516edcea975924ade034bd26dc13ab svn.957 0000000000000000000000000000000000000000 svn.957 9aa25fb8967e427a27d50791eb2483d614832461 svn.998 0000000000000000000000000000000000000000 svn.998 9aa3a97b4d50e0ff9dfd27a31de00f7c9feeed3a svn.1076 0000000000000000000000000000000000000000 svn.1076 9ab95a974154bda38fc2619ac6f637ea2b067531 svn.1180 0000000000000000000000000000000000000000 svn.1180 9aec4be13f8ca6fcb94c8d76d234f9f5fbbd61e5 svn.1428 0000000000000000000000000000000000000000 svn.1428 9aef30a081b44a2e20669c2b9efc560138e78cea svn.492 0000000000000000000000000000000000000000 svn.492 9b10206065a80a8620f63cac765e9afd99d979ee svn.956 0000000000000000000000000000000000000000 svn.956 9b29a595eb213a3d8b624ffcfec1264daaeb6297 svn.157 0000000000000000000000000000000000000000 svn.157 9b41c90daf4749214ecb173a00f43135246e6781 svn.1714 0000000000000000000000000000000000000000 svn.1714 9bc1e5dd9832a063fee0f272231d1fcb4fa4c2f4 svn.792 0000000000000000000000000000000000000000 svn.792 9c112da285602525338629860292979a2d26b201 svn.869 0000000000000000000000000000000000000000 svn.869 9c3344556df0a135022fc51b4550268e0ff8644d svn.379 0000000000000000000000000000000000000000 svn.379 9c49782e515a9305eb2520cdb216ce6a3a519115 svn.1608 0000000000000000000000000000000000000000 svn.1608 9c6a4a5c7f86d77a97aed9365a0d805af7f5d6d2 svn.154 0000000000000000000000000000000000000000 svn.154 9cb29d1f9f10f0d2965be923f870f94a529fafd5 svn.55 0000000000000000000000000000000000000000 svn.55 9d1e7ed6a6fb317b20bda798ca4d316e4a458a09 svn.1282 0000000000000000000000000000000000000000 svn.1282 9d92e040e9fae5172dd79cddaf35f70f8989f69f svn.886 0000000000000000000000000000000000000000 svn.886 9d95522b58084ed44db218f5f9b9b8120a4429d7 svn.39 0000000000000000000000000000000000000000 svn.39 9da0fc2dc4ac8a32ef5dba9721d6ecaf8b556799 svn.715 0000000000000000000000000000000000000000 svn.715 9e4f7f432edcb71b2fc63de0aa9ab7d2a4dc48a2 svn.887 0000000000000000000000000000000000000000 svn.887 9e632e3d9ab45e6a235bc32a36167c8846bdfd0b svn.1583 0000000000000000000000000000000000000000 svn.1583 9e6721a9642c652026f903c1d73eee4a112bc642 svn.1200 0000000000000000000000000000000000000000 svn.1200 9ec9c7afba9db271050517210dfeac944e06ea68 svn.228 0000000000000000000000000000000000000000 svn.228 9ee5ca4720a15232000e029f62003dbccf4a90eb svn.1378 0000000000000000000000000000000000000000 svn.1378 9f1515882b0eb1e9330a2a7bced32d7465db70f3 svn.663 0000000000000000000000000000000000000000 svn.663 9f207ebe8da55f96872590bc541aeb1c0e95dc05 svn.1108 0000000000000000000000000000000000000000 svn.1108 9f38bbd2a7e0b2a946d449cb92bccaa74cedc1de svn.429 0000000000000000000000000000000000000000 svn.429 9f8ff6b8d63271ea46fbd5f1c1cc86c011448549 svn.1670 0000000000000000000000000000000000000000 svn.1670 9f9e9b6346e4b4f2e55d0a73f2620442f6adf92b svn.824 0000000000000000000000000000000000000000 svn.824 9fabaecbb5b52610ac55462dc9527d09d6fbfc41 svn.833 0000000000000000000000000000000000000000 svn.833 9ff912bc3aa5668736ec642092e9052e4cb5641e svn.940 0000000000000000000000000000000000000000 svn.940 a0248c89297d1fbcfcc978234971a334d189a509 svn.1078 0000000000000000000000000000000000000000 svn.1078 a02e8681556852d390ca987883bc1f0bf68a883c svn.1095 0000000000000000000000000000000000000000 svn.1095 a06c2aeefa343f95e6114ef42373b41d5168a6fe svn.1234 0000000000000000000000000000000000000000 svn.1234 a0c953d3de30977dfd049bbdb9583babf492f9cd svn.643 0000000000000000000000000000000000000000 svn.643 a0d79c04569a2fab16b9dcdad2c2279a8d97087f svn.1629 0000000000000000000000000000000000000000 svn.1629 a0d7df05f9a857975a3c5227f3b03f30e9dc984a svn.416 0000000000000000000000000000000000000000 svn.416 a0f418349af76aa0d155e417b42e62186249d7c8 svn.1648 0000000000000000000000000000000000000000 svn.1648 a10259dbd29b3f03c2b7dc6119e7f646f0cc24f9 svn.1172 0000000000000000000000000000000000000000 svn.1172 a12649dbc73d2cbb659f5c7aac8d018344e0a536 svn.1338 0000000000000000000000000000000000000000 svn.1338 a1295d81ae39fc5a57d0504041c89d2fa48ca6fd svn.644 0000000000000000000000000000000000000000 svn.644 a163dac0359fbe271c1ca0245d3540857029c3b3 svn.1404 0000000000000000000000000000000000000000 svn.1404 a16838b5cb05592e23262655527f197656796f81 svn.703 0000000000000000000000000000000000000000 svn.703 a16d588a8e9cf4ec88fd1cb7142c3834f9bc3b73 svn.1804 0000000000000000000000000000000000000000 svn.1804 a18c760d50f79b93a9add8effe453a59432c7775 svn.1759 0000000000000000000000000000000000000000 svn.1759 a1d74d0fc790a3491a52939587410556f6b0a906 svn.1489 0000000000000000000000000000000000000000 svn.1489 a1f4a88de6091601fccaa7d750fc630d89484173 svn.1685 0000000000000000000000000000000000000000 svn.1685 a219cceec164504f0652afba415fcc41eb94dd50 svn.754 0000000000000000000000000000000000000000 svn.754 a297f869ee4234578f31afdf259e696aa5abac97 svn.1753 0000000000000000000000000000000000000000 svn.1753 a34717eab5696aad8a316ee7e76df8c533b73d4b svn.1720 0000000000000000000000000000000000000000 svn.1720 a3741bbe1e9377d661298a8dbca15698846b80c2 svn.426 0000000000000000000000000000000000000000 svn.426 a3a91388da54b7cb33dba5f72030690d115686b5 svn.1557 0000000000000000000000000000000000000000 svn.1557 a3e2cea497289ce066176e23cb4300ef42fd2032 svn.268 0000000000000000000000000000000000000000 svn.268 a3f68e21a03e4f6de50db0a5de6e085147c41ba1 svn.450 0000000000000000000000000000000000000000 svn.450 a402d09c999716f33b1d5505646bb308d4e39ec5 svn.169 0000000000000000000000000000000000000000 svn.169 a43969b73291aa0750bfcf156403ea1be4da194e svn.1800 0000000000000000000000000000000000000000 svn.1800 a460319b2b547acc3ba3feaa74b96e1530c7888c svn.838 0000000000000000000000000000000000000000 svn.838 a48a47a1f51f173205dd227d420ece3611a6ec50 svn.1637 0000000000000000000000000000000000000000 svn.1637 a4fc7736d18490a194bc4a15b778920aee81aa47 svn.1793 0000000000000000000000000000000000000000 svn.1793 a50fe3cbce4e6073c6a645d6b137cdd68e46eefd svn.798 0000000000000000000000000000000000000000 svn.798 a510c9b3e200cebe7a43326fc9b366952020b70d svn.752 0000000000000000000000000000000000000000 svn.752 a5267c2eb1d03a1d9c8a699462c7e67533f055e0 svn.1045 0000000000000000000000000000000000000000 svn.1045 a526bb77ca3346dfbf829f14a8b1f8ff31588fdd svn.641 0000000000000000000000000000000000000000 svn.641 a56aa4ee391728ced5cdf4e896c1b43ae7bfb09d svn.1118 0000000000000000000000000000000000000000 svn.1118 a572e5965967eb1aa33cc2e7d23873258907117a svn.274 0000000000000000000000000000000000000000 svn.274 a578bb6121dc06b382231f4fa8d5d348bb1271d7 svn.399 0000000000000000000000000000000000000000 svn.399 a5857bae39ee445ea0c62ba6f4ecdb915230f090 svn.1723 0000000000000000000000000000000000000000 svn.1723 a585956b12cf252a9d003079335feb5c06c99328 svn.800 0000000000000000000000000000000000000000 svn.800 a586ebd2e3196ff432ce42c22a7f96ce56a40b76 svn.111 0000000000000000000000000000000000000000 svn.111 a58c3ef148b51785910c57118a514f00ea45c5ab svn.724 0000000000000000000000000000000000000000 svn.724 a5b10446d760508bf5e8d5aa9a5c9e436249ec8e svn.726 0000000000000000000000000000000000000000 svn.726 a5b4b425141cf01a7f13ba109ca1f911c7d94d56 svn.1438 0000000000000000000000000000000000000000 svn.1438 a5bab3cf3e5bb59a451209daf738e0d115e4e6f6 svn.1054 0000000000000000000000000000000000000000 svn.1054 a5babafdf8d0662adfbea9079b0c4e3f056bd860 svn.65 0000000000000000000000000000000000000000 svn.65 a5e219997db4f32ca5c81a2b4aa231ea53a798b3 svn.152 0000000000000000000000000000000000000000 svn.152 a5ea9c50a0651be227b8b26717cc571749d26604 svn.1201 0000000000000000000000000000000000000000 svn.1201 a5ed10e8bc88fe86cae5592cac69814be8590098 svn.959 0000000000000000000000000000000000000000 svn.959 a6074bdf26e5e73d9deafb77562d41d63c5e46e1 svn.133 0000000000000000000000000000000000000000 svn.133 a628cc0521eaf1a5fa06ad4e127359d33ba331f0 svn.1671 0000000000000000000000000000000000000000 svn.1671 a632651a151bbffb850430cf60127d8cbebca142 svn.1336 0000000000000000000000000000000000000000 svn.1336 a646b50a6c312814fbda9365ddb1e2378b601451 svn.1062 0000000000000000000000000000000000000000 svn.1062 a67ce0a27572b4914685b3677f7255886ee53085 svn.755 0000000000000000000000000000000000000000 svn.755 a6aabdb308ada1f68f6795cac2f3bb1b3b3a270e svn.98 0000000000000000000000000000000000000000 svn.98 a70125dc712b2cd6f4038c0cc71bdd0726b706a0 svn.1375 0000000000000000000000000000000000000000 svn.1375 a7aac27988511020c8a4b43d97ac296436e85a08 svn.344 0000000000000000000000000000000000000000 svn.344 a7ff6a46cb6d7d0dc38d744c01ba6d25ce77dcf3 svn.546 0000000000000000000000000000000000000000 svn.546 a80f3cab483e45ea8c6e7df58780079013a41220 svn.676 0000000000000000000000000000000000000000 svn.676 a81914dad2eb58c8fa6d345bc5ccc2031774f447 svn.1742 0000000000000000000000000000000000000000 svn.1742 a81931a0ce18e6abe7c31f7a8eda97545f8c4a5e svn.942 0000000000000000000000000000000000000000 svn.942 a84e91abda7d74e41168d7447ae96bbe1a48e6f6 svn.593 0000000000000000000000000000000000000000 svn.593 a85b7fd37b4359a40a185d7972411cb171a10f98 svn.196 0000000000000000000000000000000000000000 svn.196 a86267e508fd91907ed795f277c13849cbfbaa95 svn.1302 0000000000000000000000000000000000000000 svn.1302 a8899e645d975ab0dd8042fd1ba76e660ed84fd2 svn.1591 0000000000000000000000000000000000000000 svn.1591 a8a6f843926816b7d9bdf8a2dbf9423973c4e285 svn.1715 0000000000000000000000000000000000000000 svn.1715 a8d37c1e3ea9582e532abc92c254d7784795eff3 svn.971 0000000000000000000000000000000000000000 svn.971 a914357b7ae6ae9d06b75cd66b6a90dd612dc4bf svn.1565 0000000000000000000000000000000000000000 svn.1565 a92157febfedda7525b97a3ae3ae102c3f1f9397 svn.570 0000000000000000000000000000000000000000 svn.570 a9295fc1fe38a604a738dfd593b61251a8876b64 svn.1522 0000000000000000000000000000000000000000 svn.1522 a94d3306a44ee7f0aa98a2f6027ea54b77f21311 svn.501 0000000000000000000000000000000000000000 svn.501 a94f2e04147d37b0c86e220b47eda0bb842c6693 svn.421 0000000000000000000000000000000000000000 svn.421 a9997f587f4d1d280852eb4d5a7be43c34d91bd2 svn.36 0000000000000000000000000000000000000000 svn.36 a99b6b5ef6a1a3a47f2f55b95c622f5cfdfb9ca4 svn.1100 0000000000000000000000000000000000000000 svn.1100 a9c31088a3e164c86f37d3aceec9e8ffd85224a0 svn.928 0000000000000000000000000000000000000000 svn.928 a9e519c8b5d3010d6ef71a21f1d37a70d302f24b svn.1510 0000000000000000000000000000000000000000 svn.1510 a9f54d68329cef4dbb0fc4aec462a81790a5867c svn.1074 0000000000000000000000000000000000000000 svn.1074 aa1dd68a313f90ed2bed7104cc455049f1d93134 svn.177 0000000000000000000000000000000000000000 svn.177 aa5a58db2daeacef35ddc3bfe3fb42e2f62dedab svn.1272 0000000000000000000000000000000000000000 svn.1272 aa8c48d95687e4328d713ad09e0a2d5fa8f675d7 svn.25 0000000000000000000000000000000000000000 svn.25 aaedf208a3c7da4c2f9e4e7090f9a79b6346ef7f svn.233 0000000000000000000000000000000000000000 svn.233 ab00496b77f0132955f409bd81e661ac498f3fa9 svn.1042 0000000000000000000000000000000000000000 svn.1042 ab16b63dbb36e2fd83ff62f903b7076372ae2280 svn.1501 0000000000000000000000000000000000000000 svn.1501 ab2e41d6b3fbe3fa1b3efab14bd3a269a9c8f2fb svn.1199 0000000000000000000000000000000000000000 svn.1199 ab5719ba28841242625841fde7606293252a1f81 svn.1290 0000000000000000000000000000000000000000 svn.1290 ab5d94b1312c6a7b384630b78b1fca91748567c1 svn.625 0000000000000000000000000000000000000000 svn.625 abe1b3db045589ce79ddcb349a73cb165a04db18 svn.692 0000000000000000000000000000000000000000 svn.692 abf2124837c9a76b3165f9db1ff627c6ddada1dc svn.221 0000000000000000000000000000000000000000 svn.221 ac0ea6d8448371423c5981d840ed6dc1a21c66b7 svn.1186 0000000000000000000000000000000000000000 svn.1186 ac1083dddca99afb1d13aab044e0a1ea1621036a svn.1768 0000000000000000000000000000000000000000 svn.1768 ac9413ddb79055da702d50c9150a6172416108d7 svn.1048 0000000000000000000000000000000000000000 svn.1048 ac970ffcd2cdc118cc58f2601c055b36ef016b47 svn.1655 0000000000000000000000000000000000000000 svn.1655 aca911cccf567b3be747ca4e9d2ca2b9e2dab800 svn.747 0000000000000000000000000000000000000000 svn.747 acc1571c360ccb3f6b74739da17a84a3aca4281b svn.134 0000000000000000000000000000000000000000 svn.134 acccfc516c72622744b6278347abac10650db670 svn.976 0000000000000000000000000000000000000000 svn.976 acec51d08d7624fe76a9aadfe3155ee2483506e3 svn.1490 0000000000000000000000000000000000000000 svn.1490 ad0abbc94dd620f6971e040413801bdf7f5dc5cd svn.1675 0000000000000000000000000000000000000000 svn.1675 ad57f1d5390122313bea130fa196e36f86d7ccd0 svn.1769 0000000000000000000000000000000000000000 svn.1769 addf0f93ae5c8ec8989160d02185e1fcb71e4a43 svn.958 0000000000000000000000000000000000000000 svn.958 adf2aa0ce50c1ceeb7ba15d3b621139765b35f15 svn.1047 0000000000000000000000000000000000000000 svn.1047 adfcc0995620eee56fb64502c3c748058c968072 svn.378 0000000000000000000000000000000000000000 svn.378 adffe1af3e0932544c25ac7dfeecce81035b80a4 svn.707 0000000000000000000000000000000000000000 svn.707 ae058e5b5b0b59b5821b2caa27955794fc5c2820 svn.1651 0000000000000000000000000000000000000000 svn.1651 ae09f960b038793877a44a52e639ff5a6eab6f57 svn.346 0000000000000000000000000000000000000000 svn.346 ae618970c5d2b233fa46f001a8da2e4d27673214 svn.748 0000000000000000000000000000000000000000 svn.748 ae7eac9f61672eab28817c79a4c3021577f3bb3a svn.606 0000000000000000000000000000000000000000 svn.606 aea28bf27fb2695947563966b77625dffc562448 svn.149 0000000000000000000000000000000000000000 svn.149 aeefcef453f3b17e5bc516ae2ef5783090db3020 svn.622 0000000000000000000000000000000000000000 svn.622 aef2e9f0f0775830621511191cb1d90cbf31e2dc svn.1348 0000000000000000000000000000000000000000 svn.1348 aef516e1116594cd80a37197723b554b7ed14dd9 svn.1509 0000000000000000000000000000000000000000 svn.1509 af64be205ea2ab36a0aa9fae890406b01f0b1aef svn.814 0000000000000000000000000000000000000000 svn.814 af78ba12e16cd1c350d7b8d276b63dea1828ff6f svn.523 0000000000000000000000000000000000000000 svn.523 af86a6b540270ec0b34313cf9a63a959c1c8a903 svn.1735 0000000000000000000000000000000000000000 svn.1735 af9009caf15aece591529c028ba693c4718599c8 svn.818 0000000000000000000000000000000000000000 svn.818 afd4ae9b829680a7e437ba429f1d5ef583977955 svn.534 0000000000000000000000000000000000000000 svn.534 aff586a05569925695625366d3994efdbfda75fb svn.48 0000000000000000000000000000000000000000 svn.48 b017b61136d48d1e975282721dc088ec042ee90b svn.1676 0000000000000000000000000000000000000000 svn.1676 b02e9e71d58e839ce45a0b61472d9a3231b0bb1d svn.529 0000000000000000000000000000000000000000 svn.529 b034778c7095acd6c1a231d6a8359d4e1557353f svn.1592 0000000000000000000000000000000000000000 svn.1592 b09e8dc65e69b08904691e6fc2da1a3e227b7320 svn.1467 0000000000000000000000000000000000000000 svn.1467 b0b62515d53ea57e9a74f252cb25d98741f57ef1 svn.476 0000000000000000000000000000000000000000 svn.476 b0c19e4145a3ff2410db03cbd552c9e79c5c6cd1 svn.365 0000000000000000000000000000000000000000 svn.365 b100edb40a33571f3a8ad85db86fa726f552f7bc svn.1357 0000000000000000000000000000000000000000 svn.1357 b10baedc76153edc3cf9f62609923b031a0c0743 svn.102 0000000000000000000000000000000000000000 svn.102 b15d74802fe758c9d2661854e0a3d98b65f42564 svn.853 0000000000000000000000000000000000000000 svn.853 b178007c7e0eeaf82d720f7d67a278581fa68cb6 svn.1162 0000000000000000000000000000000000000000 svn.1162 b187516681a232ffac94cc6741db31b40089f1cb svn.616 0000000000000000000000000000000000000000 svn.616 b1e2a89b438c98b7cfec2bf0d1788656400b65e9 svn.856 0000000000000000000000000000000000000000 svn.856 b1f6ec05492ee42c05be7bd552e6a854c1852c0c svn.1016 0000000000000000000000000000000000000000 svn.1016 b254a7fbdf6e8914ba1b20c72efd82ea2895f233 svn.1177 0000000000000000000000000000000000000000 svn.1177 b29b47b078d90d27e17172dbd64371663d753f99 svn.982 0000000000000000000000000000000000000000 svn.982 b2a797990913cb34b89db24cd90fa0661fce0d10 svn.684 0000000000000000000000000000000000000000 svn.684 b2efb3cb1ae8e165a00e4ea416061e54b4375e37 svn.1136 0000000000000000000000000000000000000000 svn.1136 b2fc28865d5ada46e419fdcdb6138a48946f5c03 svn.1210 0000000000000000000000000000000000000000 svn.1210 b3508917a48f60870b653906c507c863ecfc6d68 svn.628 0000000000000000000000000000000000000000 svn.628 b41bea1bcb1de1c425ca3e0c37af428ddb89f7cf svn.1208 0000000000000000000000000000000000000000 svn.1208 b49dcbe2d0cb373270e7afbd936a3c425da25a9f svn.389 0000000000000000000000000000000000000000 svn.389 b4b34c928a45a00eac301c3eee0056d9e275927a svn.1449 0000000000000000000000000000000000000000 svn.1449 b4c889499f4c41b9bb6e40cf59a1425d1788f779 svn.41 0000000000000000000000000000000000000000 svn.41 b4cce6b7eacf19d77e87b9c9a329770775936a6a svn.543 0000000000000000000000000000000000000000 svn.543 b4e3de65979dd083e428846079e509a3041202ad svn.1638 0000000000000000000000000000000000000000 svn.1638 b4fe74aaa8f6b8d44bb333b9f8e3bab72d8a2649 svn.1196 0000000000000000000000000000000000000000 svn.1196 b50805f852a8e2be7ea527f7aa361074f461936c svn.269 0000000000000000000000000000000000000000 svn.269 b516c2a4c8f925f48f51d4adb3b7f731b586877d svn.1307 0000000000000000000000000000000000000000 svn.1307 b54cc4d9ea3be89229352b78e540f36302b1b67d svn.87 0000000000000000000000000000000000000000 svn.87 b56cd0e4430d71b6628e34b9141b67770e4c189c svn.1716 0000000000000000000000000000000000000000 svn.1716 b576dbf4b3df62b8fef0fc5b42c1ebb854ebeece svn.670 0000000000000000000000000000000000000000 svn.670 b57ef455693f2177dc245989457480095481102c svn.369 0000000000000000000000000000000000000000 svn.369 b5aefdd801963043c02bdda4ee3c581d78325709 svn.607 0000000000000000000000000000000000000000 svn.607 b6091f03067325dca04b154c82d4ba9473203967 svn.286 0000000000000000000000000000000000000000 svn.286 b60b25246b1ea4f377f4ec8218bb6eef2774b267 svn.292 0000000000000000000000000000000000000000 svn.292 b6133494d191fe7c524b134c0ac49c73ebf28409 svn.1595 0000000000000000000000000000000000000000 svn.1595 b62bd324f6a76148938a9f75562bbcc75bad25e0 svn.882 0000000000000000000000000000000000000000 svn.882 b62dfdcbd5ed48b6dd97c6c08baf763729af853e svn.105 0000000000000000000000000000000000000000 svn.105 b65ee17c29656ad491d5926c92ce6c1511cf59be svn.461 0000000000000000000000000000000000000000 svn.461 b66f1ebf55000be1e02f95cc84e2141ab13ac378 svn.760 0000000000000000000000000000000000000000 svn.760 b68dbe7d587599bed4f5033cddf6b200b3c0c797 svn.1039 0000000000000000000000000000000000000000 svn.1039 b6afde5ee4d5012e2041ac8305d4951021fce11a svn.888 0000000000000000000000000000000000000000 svn.888 b6df060b80323de2866a13e0fa671652fb0713dd svn.1125 0000000000000000000000000000000000000000 svn.1125 b6e2c5b077fc4816032af9f272a70633bd8bbc62 svn.1253 0000000000000000000000000000000000000000 svn.1253 b72c9063105fc37dc44f5b1927303cf435bb512c svn.674 0000000000000000000000000000000000000000 svn.674 b7600c714b84873c4ad1c7637de51dcb0595a20f svn.683 0000000000000000000000000000000000000000 svn.683 b7763d12ffd2ee7fa08d814a66d77e316334a846 svn.834 0000000000000000000000000000000000000000 svn.834 b7dbd20136811c23c0cb14899acd320067f50f28 svn.360 0000000000000000000000000000000000000000 svn.360 b7f4ea520c43cfe5409afb6a745ebf32d2f668d9 svn.470 0000000000000000000000000000000000000000 svn.470 b8007496c5864fe41804232cae36f938910ea5a6 svn.203 0000000000000000000000000000000000000000 svn.203 b80893f1505443f521f049d263d7c587cfcfa1c3 svn.1747 0000000000000000000000000000000000000000 svn.1747 b8276b8dd4e7ea3eac5b560b0dbb2ab9cbc9e941 svn.252 0000000000000000000000000000000000000000 svn.252 b83eccc16f7e7b7e0a40fa7829acba11ee9e55a9 svn.312 0000000000000000000000000000000000000000 svn.312 b853cd61ddc4b7690bbdf1951ba2b97915dbf7a4 svn.1140 0000000000000000000000000000000000000000 svn.1140 b87a153ca34719ca3f7c7bfab49ac497b0fe3575 svn.1374 0000000000000000000000000000000000000000 svn.1374 b8b1484aeb9371e0ec57133badd8e401451dbd04 svn.138 0000000000000000000000000000000000000000 svn.138 b8be5dfcc9256a3021339d39219e5a92e752d07c svn.1149 0000000000000000000000000000000000000000 svn.1149 b8c88dcfb1690770c3547e51aef90f80eeb25e57 svn.47 0000000000000000000000000000000000000000 svn.47 b9004cecfea6859995441ccff047349ac2146372 svn.19 0000000000000000000000000000000000000000 svn.19 b9043b843697016c7929edda4c68aa19b1b34538 svn.549 0000000000000000000000000000000000000000 svn.549 b90a5e0b349eb2d63def509fd2a1a972714a4827 svn.441 0000000000000000000000000000000000000000 svn.441 b92d169eb5e10b1f0240084c6e88e69ff7bc9b71 svn.1334 0000000000000000000000000000000000000000 svn.1334 b94c331cba4d1d825d907c64eee88ec0d5c6f197 svn.587 0000000000000000000000000000000000000000 svn.587 b94fc22d5df709e061d6b45afa4f82962f984cba svn.561 0000000000000000000000000000000000000000 svn.561 b988ab9044a405bd54a8b82abad6841abf14e5c3 svn.1530 0000000000000000000000000000000000000000 svn.1530 b9c726dc2011338c128420ac8a4564373a350c72 svn.977 0000000000000000000000000000000000000000 svn.977 b9d559d3c93827503a08f63cd377bb89ba96f94f svn.1174 0000000000000000000000000000000000000000 svn.1174 b9e6a95cadee468ead8fc31de24f615e397c89d0 svn.1729 0000000000000000000000000000000000000000 svn.1729 ba26a56b3af92dc544aa3bdfc85807f6efc072a1 svn.1787 0000000000000000000000000000000000000000 svn.1787 ba52065cb28fd4cabc1d1b62a2c97e970bca272e svn.23 0000000000000000000000000000000000000000 svn.23 ba72d14db98d4aec227e6b688a70153393514145 svn.1390 0000000000000000000000000000000000000000 svn.1390 ba88dacae29e8a70d19e53bdd1de023f84ef8cce svn.702 0000000000000000000000000000000000000000 svn.702 bad10987839488295256e8d9b4c98bd41d33c144 svn.921 0000000000000000000000000000000000000000 svn.921 baed8f20853f4fa7df7ca7b4e810e0fd57f64ebd svn.135 0000000000000000000000000000000000000000 svn.135 bb127bd1be4a29d863e13ea3bb708758bb18af75 svn.1137 0000000000000000000000000000000000000000 svn.1137 bb1e87ef23ac5ccb79d99a157d7884b0d5acd320 svn.436 0000000000000000000000000000000000000000 svn.436 bb86dfb4578f0ad377a6ab15e9eec729960a9602 svn.1306 0000000000000000000000000000000000000000 svn.1306 bba760a55e02033fd0055bf67b1c893e6b4082db svn.1011 0000000000000000000000000000000000000000 svn.1011 bba8232865e81643be4c343d4a6bca6687bad827 svn.742 0000000000000000000000000000000000000000 svn.742 bbd1d3461d4ae66137c197de5135d3852fe10e1c svn.1230 0000000000000000000000000000000000000000 svn.1230 bbd2038ed37182bc4ddb3c574c8113b418999d22 svn.414 0000000000000000000000000000000000000000 svn.414 bc1400e920f5bed116136d53722700715c07ff54 svn.336 0000000000000000000000000000000000000000 svn.336 bc42cad69e953bb617aa31d7a374a1ce8a5f925e svn.544 0000000000000000000000000000000000000000 svn.544 bc8afbc57b67f51384ccaedad69ad024a17e55c1 svn.281 0000000000000000000000000000000000000000 svn.281 bc9ea94aa2cf29fb36f56ec6d994cba0d9be29ac svn.1166 0000000000000000000000000000000000000000 svn.1166 bcec0692b627b770f3e5b6bab4a22c830746c5f1 svn.822 0000000000000000000000000000000000000000 svn.822 bceeaee5e4975fb7c5b448257fde44af4d13f384 svn.944 0000000000000000000000000000000000000000 svn.944 bd0590a19af0a3b7e4f99f7ece4770713b8631f6 svn.397 0000000000000000000000000000000000000000 svn.397 bd07a43b624c9e249c76af770a1bb82bc89c72e3 svn.655 0000000000000000000000000000000000000000 svn.655 bd0b543a76b1be2416908cc8c002e516254170fd svn.1474 0000000000000000000000000000000000000000 svn.1474 bd20ff182a70680ed102319d756558b2e6931fbd svn.424 0000000000000000000000000000000000000000 svn.424 bd37fb10c639aef30c2634843981e03cb922031d svn.863 0000000000000000000000000000000000000000 svn.863 bd518f331c660af28f9653af0c00bb2b2b377191 svn.186 0000000000000000000000000000000000000000 svn.186 bd58a3cc636648b5c4165f72df502718001e12e9 svn.446 0000000000000000000000000000000000000000 svn.446 bd674100764d4566a7df25e2960efea627c425ce svn.1431 0000000000000000000000000000000000000000 svn.1431 bd76d70e13036a042d53118dacdee7017ba24470 svn.1553 0000000000000000000000000000000000000000 svn.1553 bd778de72222fbc5dd4f8f1ca7e2f8e6df9cd8db svn.1456 0000000000000000000000000000000000000000 svn.1456 bd972fcd4ae0b295afac1b1167edefcff7a799d8 svn.512 0000000000000000000000000000000000000000 svn.512 bdb57322f46908ed60e5d8d5344ae9290baa525f svn.528 0000000000000000000000000000000000000000 svn.528 be2016c79358602e569cec87eeaf7c8146873384 svn.569 0000000000000000000000000000000000000000 svn.569 be34fecdf22396fc117ce041d3e350e6bf8460f8 svn.1236 0000000000000000000000000000000000000000 svn.1236 be4a915563973368ff28084c8201f37af10ad46d svn.1193 0000000000000000000000000000000000000000 svn.1193 be6acb7b9827ff8481db261ddd7f59df1a68025d svn.550 0000000000000000000000000000000000000000 svn.550 be7d1bb42c34dace8162a84a0b9fa85405d580e3 svn.790 0000000000000000000000000000000000000000 svn.790 be8eaf0b54747c56beb61fa29f5a1e52efbe7e30 svn.1425 0000000000000000000000000000000000000000 svn.1425 be9c17ec40a22a0f2ac629eae2942c47a1e2e46e svn.455 0000000000000000000000000000000000000000 svn.455 bed7ea6f0af813c6bdbeb6ac61ff4622fb24043a svn.78 0000000000000000000000000000000000000000 svn.78 bee31785688b4b3ad4b9e754e7fadc6f73dafe07 svn.1724 0000000000000000000000000000000000000000 svn.1724 beefaf3b957aa33f06304fbb5bd8d165f10dbca9 svn.484 0000000000000000000000000000000000000000 svn.484 bf196d6db35e67bad09d04c5517191d9545f3bb2 svn.175 0000000000000000000000000000000000000000 svn.175 bf5176323aac3d989544ca00e830a6ee030e2296 svn.289 0000000000000000000000000000000000000000 svn.289 bf78c745075753faf220e6a1127803633e5d74e2 svn.375 0000000000000000000000000000000000000000 svn.375 bf9ca3f4fc137b6dbf5f1ecc2f37731f5ba30899 svn.1240 0000000000000000000000000000000000000000 svn.1240 bfa60de05a0572cc29caa94e0200684fea4dc46b svn.1119 0000000000000000000000000000000000000000 svn.1119 bfadc2919b665414db3781ee35f6f78ec96571b7 svn.696 0000000000000000000000000000000000000000 svn.696 c02c67948ea3bc03cc631a66b6c8a3bbeb7a9a8b svn.199 0000000000000000000000000000000000000000 svn.199 c06cb14312e16fe08ed3b49d2dd2961b67823f49 svn.757 0000000000000000000000000000000000000000 svn.757 c098f2b1d264b43305edd5e67b84f838759c0df6 svn.139 0000000000000000000000000000000000000000 svn.139 c0b88e210dc389abc84d1a5a6b38b468580b9888 svn.753 0000000000000000000000000000000000000000 svn.753 c0de8e602dca315eaafdc99f0570a6205f36a799 svn.1117 0000000000000000000000000000000000000000 svn.1117 c0ef10d55142de04b6552639be0ef3beaecb8268 svn.1650 0000000000000000000000000000000000000000 svn.1650 c10095fc0b3379a4230af3086c2c82d99fb08f78 svn.383 0000000000000000000000000000000000000000 svn.383 c136b560a49ef16431d5ebda46bb181d3f30c5ba svn.1053 0000000000000000000000000000000000000000 svn.1053 c13e3d84f3a9a779d232194a736472c0c7dd30b6 svn.1238 0000000000000000000000000000000000000000 svn.1238 c163713e415ac6f33299f8773d95e2872b353910 svn.404 0000000000000000000000000000000000000000 svn.404 c167a9fb66504c0527d85bbbb2539550e9ef43a0 svn.2 0000000000000000000000000000000000000000 svn.2 c198ce1f5adb895f14699d3c28f1400ecd209222 svn.624 0000000000000000000000000000000000000000 svn.624 c19f7596a4c38ccc1376343243b5043a230b8f67 svn.511 0000000000000000000000000000000000000000 svn.511 c1b1502bbbc8ae1e989ec9139cdd4d1cc3844f7d svn.1405 0000000000000000000000000000000000000000 svn.1405 c1c93678d868e72993bcc2f3476ca75b791b1164 svn.1203 0000000000000000000000000000000000000000 svn.1203 c233bc3385ee77913c98866c10bad6f153f8daaa svn.540 0000000000000000000000000000000000000000 svn.540 c246630d322cb58675ddef6ebafaca4b73216df9 svn.506 0000000000000000000000000000000000000000 svn.506 c2a41082a6be8099136177ff717fa82cbae9758a svn.1308 0000000000000000000000000000000000000000 svn.1308 c2b3988e4f9b7530829e3b432df02e7ffe6743dd svn.1582 0000000000000000000000000000000000000000 svn.1582 c2bb172fd266f6ee32d3d4ba4a68e8ab72d21878 svn.885 0000000000000000000000000000000000000000 svn.885 c2bfbeb2a6189251637353781c52cbbf226d1bb4 svn.290 0000000000000000000000000000000000000000 svn.290 c2f22be977587da957824faa9aee58ff23f7bc55 svn.58 0000000000000000000000000000000000000000 svn.58 c3196a368da7180614190efc0395a63929bc6c08 svn.699 0000000000000000000000000000000000000000 svn.699 c332476fd1461f2dbc3467113e0d311b3b1ae528 svn.81 0000000000000000000000000000000000000000 svn.81 c3376fa426b1bd1328d044a74f8d98ffe822596f svn.563 0000000000000000000000000000000000000000 svn.563 c34c1e6c30e51ec604bcfeaca99a384ba6a08cbb svn.577 0000000000000000000000000000000000000000 svn.577 c368b0a6a927578a598c73058e7a089066fe0f2a svn.231 0000000000000000000000000000000000000000 svn.231 c3916e50b59483a7d5477a3d2830f7ebaa4e88b4 svn.44 0000000000000000000000000000000000000000 svn.44 c39afe8c02faf8e017fec2b555f588eae92caea9 svn.1475 0000000000000000000000000000000000000000 svn.1475 c3a8df412c3c5a6d85cff623f3c0bff309892c39 svn.1520 0000000000000000000000000000000000000000 svn.1520 c3cb6c6ea42d39bdff323e454767c966fbe273d6 svn.109 0000000000000000000000000000000000000000 svn.109 c3ffe484fd397106b02b346a57d4be5538b29bb7 svn.255 0000000000000000000000000000000000000000 svn.255 c415207cceeaa18d4159f1bf54b4622d257f97fa svn.1634 0000000000000000000000000000000000000000 svn.1634 c44554134dd77398bbdc9ed9d0c6d07ddd9bb0c8 svn.981 0000000000000000000000000000000000000000 svn.981 c4c5cddc0193b5f2f539a1164f6ceb64f2db3337 svn.881 0000000000000000000000000000000000000000 svn.881 c4d967b289e88afd7aaf513a69137c9ce9256d33 svn.195 0000000000000000000000000000000000000000 svn.195 c4ffdb58f38773e05bdb4cba2c07b11f13960a6a svn.333 0000000000000000000000000000000000000000 svn.333 c50fc0696ef444d7d29476442993549e858c5daa svn.1275 0000000000000000000000000000000000000000 svn.1275 c52bb6ff37c1dad3b2d4bbf9ad1523fdb8b28402 svn.1281 0000000000000000000000000000000000000000 svn.1281 c53e9e42bf066ea0d6cc8727434d0b4b2a475780 svn.1736 0000000000000000000000000000000000000000 svn.1736 c558481a7b1cb461664e002d6276a4a45bec895b svn.194 0000000000000000000000000000000000000000 svn.194 c5799f4131404885f9f5519469ddc7776db6acd3 svn.1673 0000000000000000000000000000000000000000 svn.1673 c5c15377573c533f5fc534fbf8f41ec86a050410 svn.411 0000000000000000000000000000000000000000 svn.411 c6088d1d3e29aa86a5afc947eb35f1e1748cadad svn.832 0000000000000000000000000000000000000000 svn.832 c6096476eeadc2981ea9bcc2583602eb9b65e9fa svn.170 0000000000000000000000000000000000000000 svn.170 c623c08ff5354afc9dcdaebc4c48e63db16c2925 svn.519 0000000000000000000000000000000000000000 svn.519 c6859bf896efd71340811b384ebe188adfc0ae95 svn.1335 0000000000000000000000000000000000000000 svn.1335 c68a59d35ff4aab7c379c6901a228c7046b9f9df svn.1104 0000000000000000000000000000000000000000 svn.1104 c6ef0f1ad18b66d53d89f594eb409da7c3f88c11 svn.602 0000000000000000000000000000000000000000 svn.602 c709cb0943774485ba12513d41f005dad4e1a73b svn.1121 0000000000000000000000000000000000000000 svn.1121 c7162a837fec86e35015ff1106affed097263e8d svn.1408 0000000000000000000000000000000000000000 svn.1408 c74f26086149a38489c2c4dd4d893430513e7d31 svn.633 0000000000000000000000000000000000000000 svn.633 c75b8dcb044876ed256dfa807fd6c0140d9533e7 svn.1296 0000000000000000000000000000000000000000 svn.1296 c773f6d895653c3ecbdf47ba449adaaac871078e svn.440 0000000000000000000000000000000000000000 svn.440 c778a0ae6038c46a24c0e8e13039475075ae661c svn.1656 0000000000000000000000000000000000000000 svn.1656 c80d72a952f4c7dc149862d334656d4f29494606 svn.825 0000000000000000000000000000000000000000 svn.825 c81f9bdea878709507f333f0dac8d3edac0ca837 svn.706 0000000000000000000000000000000000000000 svn.706 c846c8c22f53d3813d5f48cbbda31848358c73dc svn.585 0000000000000000000000000000000000000000 svn.585 c84ea36f4cda9bae146389588b04158d93297f5b svn.1709 0000000000000000000000000000000000000000 svn.1709 c8713311b73b6b948194bf185a1973b535eb32ba svn.515 0000000000000000000000000000000000000000 svn.515 c871904bc45d64e143a583a41c7f57fc8dbf0313 svn.1097 0000000000000000000000000000000000000000 svn.1097 c89c86f10a3bc43e3a6daa573f91138cc16a0d47 svn.1064 0000000000000000000000000000000000000000 svn.1064 c8ee9b5d34430df0ed14cf3f0f1dbc7f1d2b4b16 svn.223 0000000000000000000000000000000000000000 svn.223 c9773ae3b01374d1b3f9fdf518ca7fd54b573177 svn.1658 0000000000000000000000000000000000000000 svn.1658 c99bb50a463011e62a54fbbea512e004c7f8c460 svn.20 0000000000000000000000000000000000000000 svn.20 c9ca0c16cf8eb49e0e314f65a7a70de87e870bf9 svn.1380 0000000000000000000000000000000000000000 svn.1380 c9d500439ff90d14fe4769d91134d2f436a1fe50 svn.1706 0000000000000000000000000000000000000000 svn.1706 ca15fea2e6e3e32a908ff6acc3206b7d92667ece svn.204 0000000000000000000000000000000000000000 svn.204 ca1e28f7895a7a54dc98911762161f370b44f6b5 svn.1386 0000000000000000000000000000000000000000 svn.1386 ca2da2130ce94193b4641865b4b58d278772d9e5 svn.1611 0000000000000000000000000000000000000000 svn.1611 ca5dc5e9e08b86f309231af045059019a9fda402 svn.574 0000000000000000000000000000000000000000 svn.574 ca6269080027653b454ce5144c0f95c448ec7bbf svn.1024 0000000000000000000000000000000000000000 svn.1024 ca8b8620885703755c186ab7d84dd37157594b2f svn.1601 0000000000000000000000000000000000000000 svn.1601 caea955bfc4a4bc41d45f9062f9611d4e1d0229d svn.358 0000000000000000000000000000000000000000 svn.358 caf0c2b42ca9839ed7c717de26c99509062c4f8d svn.811 0000000000000000000000000000000000000000 svn.811 caf4508acac58c6ada5a6e9e192af171711e1a2b svn.206 0000000000000000000000000000000000000000 svn.206 cb33bfaa936dd21013a66f1e67845305b028e6e3 svn.581 0000000000000000000000000000000000000000 svn.581 cb55e7475f222fe03bbef195d1b4492f482eabb9 svn.242 0000000000000000000000000000000000000000 svn.242 cbb1deea25e3a6c5106ca32b9acaad37231b711b svn.472 0000000000000000000000000000000000000000 svn.472 cbe7888df0dc04f3349262dd716114bf60584352 svn.1566 0000000000000000000000000000000000000000 svn.1566 cc1f76d8fe23fea528b8f58c37d15a005c165820 svn.1212 0000000000000000000000000000000000000000 svn.1212 cc2e1ec4536f68860d74e3d0ff320f5b88fa9237 svn.603 0000000000000000000000000000000000000000 svn.603 cc3224d42aebab5e87bcb500a56577bd6e7cf2a8 svn.1059 0000000000000000000000000000000000000000 svn.1059 cc38929af6efa218b77da7c8a8ad84f0c39f5bb5 svn.1128 0000000000000000000000000000000000000000 svn.1128 cc74a5c1fe3c598da6cdec23b389ca6e706a8eea svn.1785 0000000000000000000000000000000000000000 svn.1785 cc7bd4cd0d1b9f9a1091bd678047256d975ecb8d svn.1123 0000000000000000000000000000000000000000 svn.1123 cc8f3d28257506a3bc3fb7e14aa924ad38d59071 svn.90 0000000000000000000000000000000000000000 svn.90 cc91980a1e29913f02f3513fab75f8f23855be4f svn.900 0000000000000000000000000000000000000000 svn.900 ccf7b27b64988bfa187b588e40db01975e687159 svn.287 0000000000000000000000000000000000000000 svn.287 cd0c3084edc9eb40f8310068d6ff6d6c7aad00a8 svn.719 0000000000000000000000000000000000000000 svn.719 cd416a203e19971ad4d604ca44aecec2d2917e46 svn.387 0000000000000000000000000000000000000000 svn.387 cda04daf48c6309f71c060158c6c5180d90136f6 svn.84 0000000000000000000000000000000000000000 svn.84 cdc4f7e698242ed2a17946fa3697e1fc7bc7d1c5 svn.1698 0000000000000000000000000000000000000000 svn.1698 cdf65e688238eedbc9f1036a903b6f03735c89c2 svn.1384 0000000000000000000000000000000000000000 svn.1384 ce014fc1956a54e948c897831312b28ff2d40db5 svn.1330 0000000000000000000000000000000000000000 svn.1330 ce2a2203d4f5ed56479a7ef201996ca4863a9c18 svn.517 0000000000000000000000000000000000000000 svn.517 ce2f3a72ca9da939ab91df6d5cf193a093273a03 svn.836 0000000000000000000000000000000000000000 svn.836 ce4bf5a027a32c44bcd3bbcf217bc00a06c310bb svn.1505 0000000000000000000000000000000000000000 svn.1505 ce7a63e6fe40b68add31be83d48bcae92237fb02 svn.924 0000000000000000000000000000000000000000 svn.924 cebe6439577033f1a6727ae2422dccd487729e2a svn.980 0000000000000000000000000000000000000000 svn.980 cec5c2f0131a6a68816f3cd653bd86846942777d svn.165 0000000000000000000000000000000000000000 svn.165 cf048d23f9bb70efa03d93aa29281ed5bc489d56 svn.1151 0000000000000000000000000000000000000000 svn.1151 cf0b8fa619131998ad2d9797f62e6d4fdf80dd25 svn.1649 0000000000000000000000000000000000000000 svn.1649 cf10ea8151cb6d668dd33e5547bd4377c0e699e5 svn.990 0000000000000000000000000000000000000000 svn.990 cf26d7b1d28c8c5561998b8ce1df420dca9e75a7 svn.654 0000000000000000000000000000000000000000 svn.654 cf5ef451015e5b73967dcba66dda2942d0738030 svn.973 0000000000000000000000000000000000000000 svn.973 cfd3034246e5374810351f319e7be0bccc989b2c svn.398 0000000000000000000000000000000000000000 svn.398 d00dff1f4de7ee734b29f596b70e2a86ac22b93a svn.507 0000000000000000000000000000000000000000 svn.507 d02415804062ce29d1817d6637c2c83ae8c6a985 svn.613 0000000000000000000000000000000000000000 svn.613 d03329f07d150def647b13629f2076b7e7fed0d3 svn.295 0000000000000000000000000000000000000000 svn.295 d08bb1b3cfb3773e3ae72796242be9cd722efdf2 svn.1271 0000000000000000000000000000000000000000 svn.1271 d095eeb54e5be45074634e633045237f575e28e6 svn.485 0000000000000000000000000000000000000000 svn.485 d0b50a7c7a4b2a071640ed4545a9bef6fd0c5494 svn.1164 0000000000000000000000000000000000000000 svn.1164 d0ef00548e9d059f838033cf4f9af66de0a5d11f svn.141 0000000000000000000000000000000000000000 svn.141 d0f7efcca67d22b8670e257e765fb784dc7f5a98 svn.338 0000000000000000000000000000000000000000 svn.338 d12d8030ada4bdd6753f9526ceeee4b87e758081 svn.793 0000000000000000000000000000000000000000 svn.793 d16aa2935585fa8422e5b297f5fcd2315b51fdd1 svn.1415 0000000000000000000000000000000000000000 svn.1415 d1a89f0f04ae32433d86fad2e3d58cc018570315 svn.1524 0000000000000000000000000000000000000000 svn.1524 d1a8a2656a93d9b645072b1cf6ab5c4e3dd232d4 svn.244 0000000000000000000000000000000000000000 svn.244 d1add6a890d9e5f433efe2f4af3f820e94e5ee6e svn.730 0000000000000000000000000000000000000000 svn.730 d1b72540f662098dbafd526dabf48f09b5b9b2d2 svn.1745 0000000000000000000000000000000000000000 svn.1745 d1ecf43dff68904f9123a3a29892c4acc4c2ef26 svn.584 0000000000000000000000000000000000000000 svn.584 d1f98a7c06d1921d36afa0741486ea7f765b9aec svn.767 0000000000000000000000000000000000000000 svn.767 d2034da217433177c89e79436b59c38144cc9794 svn.1784 0000000000000000000000000000000000000000 svn.1784 d2173c1262774efeef1291f7062521389b7530f7 svn.890 0000000000000000000000000000000000000000 svn.890 d2282685ac5d1e06a7229f8c39db1d93dbe4edbd svn.1005 0000000000000000000000000000000000000000 svn.1005 d250a805dc0618e8ec8060ed8c1b244d3fd98c09 svn.1056 0000000000000000000000000000000000000000 svn.1056 d279bbbbc82cdf1c4885a4655f901116146df845 svn.789 0000000000000000000000000000000000000000 svn.789 d2893ea69beeefcfc5c9d56019ac5057dbfeeb6e svn.1779 0000000000000000000000000000000000000000 svn.1779 d294da29169a427c450192c84a94c4dc36917324 svn.535 0000000000000000000000000000000000000000 svn.535 d2a61719517d9749934b6ca8b91ef87eab5da8fe svn.1756 0000000000000000000000000000000000000000 svn.1756 d2cce60885eecfaa844f23a26f0fae43ac34c600 svn.572 0000000000000000000000000000000000000000 svn.572 d30456a99475a462bfd2ea3e831e15e27f4463ac svn.1564 0000000000000000000000000000000000000000 svn.1564 d323733a5c1667fd08cfda8f65df8adfa30b49ef svn.1617 0000000000000000000000000000000000000000 svn.1617 d34667423b3940c6105dbb70018eba21a0c66524 svn.1371 0000000000000000000000000000000000000000 svn.1371 d38fe2486bb57ddd100be7ac6d1597fe2a74fb01 svn.1182 0000000000000000000000000000000000000000 svn.1182 d3bf19cb7ce33da131cb742641b947af098964c5 svn.1260 0000000000000000000000000000000000000000 svn.1260 d3c0d5a01e96de0fb8357bd98fd53bcd78e3d8c7 svn.57 0000000000000000000000000000000000000000 svn.57 d409892c82ab05ee7f678687b756ae192b21cecd svn.1359 0000000000000000000000000000000000000000 svn.1359 d452bb73bf7d898707ffee6b8388800d4d4c32bb svn.1305 0000000000000000000000000000000000000000 svn.1305 d46c717574272bd2cc4b97238f42c410f2d9723e svn.664 0000000000000000000000000000000000000000 svn.664 d489475826609f4c8013a28bc31b72383a4c896e svn.1441 0000000000000000000000000000000000000000 svn.1441 d49d0a7941ed7725c7a3382573d06d0447e08503 svn.1631 0000000000000000000000000000000000000000 svn.1631 d4edf1cf60d10e3593223705033f460f10c70312 svn.634 0000000000000000000000000000000000000000 svn.634 d4fac6ea54a62e42d54f846ec3c0df741bd50888 svn.1321 0000000000000000000000000000000000000000 svn.1321 d5057e6c68d784cc5893da72e5ac5ac72f85b535 svn.575 0000000000000000000000000000000000000000 svn.575 d52d833924ed0f1074688d72c4ce2f644bb46f79 svn.525 0000000000000000000000000000000000000000 svn.525 d5820ec3227beb823a8a71b1ea5b1648d258dddc svn.1471 0000000000000000000000000000000000000000 svn.1471 d5941dda907940915cd2b05964593d096c6a2fc2 svn.261 0000000000000000000000000000000000000000 svn.261 d5969b00fa9734a4da0e33ce9c13e46abf3c8648 svn.1268 0000000000000000000000000000000000000000 svn.1268 d5a49ed72a6eb24831dabeed25d2568c7e61a6b9 svn.1131 0000000000000000000000000000000000000000 svn.1131 d63459c7fbabbdc9c1801175bb5444ed40b5569f svn.136 0000000000000000000000000000000000000000 svn.136 d6dcf317e3417fda2e633885be378e116945b9f5 svn.1146 0000000000000000000000000000000000000000 svn.1146 d6f2fd43ee48e2710a80bbdd99be983aeaa9967d svn.1504 0000000000000000000000000000000000000000 svn.1504 d76b0bdd379455f66e124a2d968a082dba710dde svn.1719 0000000000000000000000000000000000000000 svn.1719 d77977e676fdf1c0e2c05e4296f68933747e70ee svn.462 0000000000000000000000000000000000000000 svn.462 d77b4d4789f4e93914bb5b435665a77d65e1512e svn.805 0000000000000000000000000000000000000000 svn.805 d7900a6464c942596365347e0dac9d225f8efe73 svn.56 0000000000000000000000000000000000000000 svn.56 d7b9cd53e639ca63ff18350a898fdb9262305150 svn.1069 0000000000000000000000000000000000000000 svn.1069 d7c60eeed511fcedfc20d05815ef985ef9b00586 svn.331 0000000000000000000000000000000000000000 svn.331 d7d86ab3b0d4690912a88421518e2779e2be717a svn.1452 0000000000000000000000000000000000000000 svn.1452 d7eaf968bd14944c4399ca18e3b829489e278b87 svn.1376 0000000000000000000000000000000000000000 svn.1376 d8068096efa1a7478739d255bdc6995b649247d2 svn.1473 0000000000000000000000000000000000000000 svn.1473 d81716777b972787bf80f27bf34d33f40631ff90 svn.939 0000000000000000000000000000000000000000 svn.939 d8226f2dcf0a14dc2edd0704b8ca1d71288f0beb svn.743 0000000000000000000000000000000000000000 svn.743 d86244661ec585e3ce0088e7394dbb4e5d530152 svn.1681 0000000000000000000000000000000000000000 svn.1681 d87671d34c248fbf7b4340eb1d77fd5baf93cb46 svn.367 0000000000000000000000000000000000000000 svn.367 d87bf7bd5791e7facd0e88e76e0b9902c4fb8f90 svn.1712 0000000000000000000000000000000000000000 svn.1712 d8945d2c82435de57ec6a5aadf3fdbcefe89da51 svn.548 0000000000000000000000000000000000000000 svn.548 d89a70b3f6b24c62fecd63e30e79730460ed3fb3 svn.1028 0000000000000000000000000000000000000000 svn.1028 d8d5d0f85bcd1d0c07758d7056f2a007f906e122 svn.1328 0000000000000000000000000000000000000000 svn.1328 d8f61775a015be47edb16712a599b1831e0cd263 svn.99 0000000000000000000000000000000000000000 svn.99 d9236d69454cfafe89415698c11e0fc167dc49cf svn.1777 0000000000000000000000000000000000000000 svn.1777 d94c1c5eac0a2775caacf5136617e4f6360ef886 svn.150 0000000000000000000000000000000000000000 svn.150 d9627b379d0d36e3ca302943cea334cfe052fc8c svn.1554 0000000000000000000000000000000000000000 svn.1554 d96b5c00a521c6c436d8a6cd506482a24e55e823 svn.222 0000000000000000000000000000000000000000 svn.222 d97109d30a699b5ad60025583df7420df09c7c96 svn.457 0000000000000000000000000000000000000000 svn.457 d9a2f6d083d569f07828720deb87372fceeca32c svn.1741 0000000000000000000000000000000000000000 svn.1741 d9a557f509a5ad811568db27ae676efcba75599e svn.248 0000000000000000000000000000000000000000 svn.248 d9bbea43918ee4509e12583fd4603f02576707c2 svn.59 0000000000000000000000000000000000000000 svn.59 d9ce9b6db9d314a7a518f90d28db3c79686fbeff svn.1764 0000000000000000000000000000000000000000 svn.1764 d9e3844aafe7c3bbf0b18539bac4b24de5acd0b7 svn.1252 0000000000000000000000000000000000000000 svn.1252 da1e5cb40d297b11a5728e70b997a20a7f5a4d7d svn.815 0000000000000000000000000000000000000000 svn.815 da2fbf9d6dbe9531efc6eec2f99fef06e48b4fb4 svn.1546 0000000000000000000000000000000000000000 svn.1546 da5a324608caba5c8d1dd13fca193ea7cb3fbc57 svn.690 0000000000000000000000000000000000000000 svn.690 da5b5aade6b6c9aaac226c17cf61329043cce3bb svn.1298 0000000000000000000000000000000000000000 svn.1298 da9027e61c2574ca042f003b2db7a9390340c772 svn.741 0000000000000000000000000000000000000000 svn.741 da9a9555e9dd1a7132ba696c7139b5af79e81e8d svn.651 0000000000000000000000000000000000000000 svn.651 daa8e9f1533930e9fe004f28c6a3d8bbaa3873c1 svn.1289 0000000000000000000000000000000000000000 svn.1289 dace5ca959f3d429a5d8422f8ade8ff2020885ee svn.751 0000000000000000000000000000000000000000 svn.751 dad0938a6a73c2cb20fe741ed5885a631bacbb1a svn.875 0000000000000000000000000000000000000000 svn.875 db0c05c310ce7bde74d48f39ed1e4867564e10c6 svn.1191 0000000000000000000000000000000000000000 svn.1191 db128cb36550851c7d25f7982a982b335118667c svn.1513 0000000000000000000000000000000000000000 svn.1513 db2716d7aacaf947331b437b6f9259a6c3718fed svn.388 0000000000000000000000000000000000000000 svn.388 db2d8340dc8c3f4db047424dbce84829600b4f4c svn.1353 0000000000000000000000000000000000000000 svn.1353 db428cad7fb72268daeed2a89e28ef1f4d1642aa svn.1373 0000000000000000000000000000000000000000 svn.1373 db56f738ed6ac4634ab1bce96b763d703986c9e2 svn.1144 0000000000000000000000000000000000000000 svn.1144 db725052b6e3cd8b708a225b35f7d6df48a2730e svn.1744 0000000000000000000000000000000000000000 svn.1744 dbb9baaf6763db6c1ec069bda87eb822e4bb2850 svn.1523 0000000000000000000000000000000000000000 svn.1523 dc3413343f3a6e0f3adb3f5c851611256825278d svn.76 0000000000000000000000000000000000000000 svn.76 dc3cbb2f3c8b1d30c1bec6551f29661850c79dc0 svn.192 0000000000000000000000000000000000000000 svn.192 dc5c031766069237f69c3fb997e20a0cc1c9a1d4 svn.1227 0000000000000000000000000000000000000000 svn.1227 dcade05709ccd8d3556c082b9bc17a70d690de71 svn.277 0000000000000000000000000000000000000000 svn.277 dcb0b183591b93e51f3c7c084e681a575dc9b43e svn.444 0000000000000000000000000000000000000000 svn.444 dcd3605a556003cdd44f15d61fcbd002437b2882 svn.955 0000000000000000000000000000000000000000 svn.955 dd14ef8ddc2d8b522b76ea7f186a38ab3b364fca svn.779 0000000000000000000000000000000000000000 svn.779 dd198266f3cb029f0d5bf63586693e79e0614a4d svn.595 0000000000000000000000000000000000000000 svn.595 dd258ffab1d84f097561d6e2332cef413a531211 svn.929 0000000000000000000000000000000000000000 svn.929 dd441b4d3485d9f76b04557b2081407e7b390cbf svn.675 0000000000000000000000000000000000000000 svn.675 dd6713a2070d4786faa56ee1b5de99de057b9a25 svn.1567 0000000000000000000000000000000000000000 svn.1567 dd852bef6ce6f0af3815cb2844ed2425fc387201 svn.1644 0000000000000000000000000000000000000000 svn.1644 dd8c06cbfd201a05501a77a29ba089b6e7405b6b svn.121 0000000000000000000000000000000000000000 svn.121 dd9b5bdb90ae7f0fa40a52256c664c4286a9f00f svn.1692 0000000000000000000000000000000000000000 svn.1692 dde9bbaaa7f77a7d3a3ffb0a9ac11ed18665d63f svn.1122 0000000000000000000000000000000000000000 svn.1122 de12d958ec8d61256474cbae0699b46bc245f1c8 svn.1740 0000000000000000000000000000000000000000 svn.1740 de5a16dd44a65e1864acdd8bf0697f28adb682cd svn.187 0000000000000000000000000000000000000000 svn.187 de5df261f5d9f6a66cc5e8dcc6217d28ae3e02f8 svn.1171 0000000000000000000000000000000000000000 svn.1171 de790db535467baf165508a67ea51f567ef68b4b svn.1155 0000000000000000000000000000000000000000 svn.1155 de989f9e00795ce23acb6447fe09316d5dc991f2 svn.1713 0000000000000000000000000000000000000000 svn.1713 deb4493b2fc41bd98753f9f66a12b4bef8c683a2 svn.1588 0000000000000000000000000000000000000000 svn.1588 deb5472bace0f20d2abb196ee608947f4b24e1a7 svn.449 0000000000000000000000000000000000000000 svn.449 dec1498ac58f2c3b2d8b4dea3a41330c6942f346 svn.1531 0000000000000000000000000000000000000000 svn.1531 dee00b9b86e0ff5538b2e973a60753d2798f3995 svn.1020 0000000000000000000000000000000000000000 svn.1020 df19be55ad5532cb4a831e24a8c7d29282a90c29 svn.1250 0000000000000000000000000000000000000000 svn.1250 df19fde1fafb23c8e6d8eeea7b4a71c3663d939f svn.1602 0000000000000000000000000000000000000000 svn.1602 df31e314e96595054761ac488c18cab48607337c svn.276 0000000000000000000000000000000000000000 svn.276 df630d602734c833d58c17456dd0054e4c81dc7e svn.374 0000000000000000000000000000000000000000 svn.374 df9eee094325daa18d3d8443f71c7bb76e7776e2 svn.1717 0000000000000000000000000000000000000000 svn.1717 dfa1661607fa5daa6f98700397f518ace136fbab svn.776 0000000000000000000000000000000000000000 svn.776 e0186afc0c8e2fffa8abf02e4414ce5fc887b1a8 svn.1087 0000000000000000000000000000000000000000 svn.1087 e060d802c624e7f6b9dcffbd2f4b24c02797939f svn.907 0000000000000000000000000000000000000000 svn.907 e08a54b5f3aecea5b384fb7126981c9c9dccf959 svn.594 0000000000000000000000000000000000000000 svn.594 e1401f51fca72b608abb63a3cb352c1295b65bf1 svn.12 0000000000000000000000000000000000000000 svn.12 e14986fc7821ce361b2a6c1ba13ac90f5dcd1429 svn.1138 0000000000000000000000000000000000000000 svn.1138 e14a71b2045a992785bb3f3326770efbd02a602e svn.1299 0000000000000000000000000000000000000000 svn.1299 e175cf81425877f3bc1e6b419bfd90d2bcf43086 svn.1412 0000000000000000000000000000000000000000 svn.1412 e20489266f4ab2b653edd84cf071748f571e8cf7 svn.851 0000000000000000000000000000000000000000 svn.851 e20dca56fba1a0808a13fc700a93527d0b82090c svn.1409 0000000000000000000000000000000000000000 svn.1409 e22471b904aa14082fa012133b648926bf438c43 svn.992 0000000000000000000000000000000000000000 svn.992 e24d68fd580a8526b5976caf4b805cb73bf69f16 svn.765 0000000000000000000000000000000000000000 svn.765 e272895dc859c84fa35245a1c186bf4a252defbd svn.1581 0000000000000000000000000000000000000000 svn.1581 e27c6d750b6b94c4ca510903d46d984028b3b631 svn.1797 0000000000000000000000000000000000000000 svn.1797 e286ee5b821fc57e18214c2ff70f75cce9cd11d4 svn.354 0000000000000000000000000000000000000000 svn.354 e2a5af31a55fac49c28b369e46c03ac0032729dc svn.1571 0000000000000000000000000000000000000000 svn.1571 e2c4af6d557fd7530e53c6b8ce95986183a5aee8 svn.22 0000000000000000000000000000000000000000 svn.22 e2e61b10359f50643a8478d3b497975e369010c8 svn.430 0000000000000000000000000000000000000000 svn.430 e2fcb3571d13044d75501ffc65691fd290c988b4 svn.1507 0000000000000000000000000000000000000000 svn.1507 e31e7ae5250c8356021f9972781c3b7911f41fe2 svn.395 0000000000000000000000000000000000000000 svn.395 e381af52bfacd5303d5818f8825db36145412dfe svn.1343 0000000000000000000000000000000000000000 svn.1343 e38c05dfa50e1f3981da387ee1d151dff5a9dc16 svn.941 0000000000000000000000000000000000000000 svn.941 e38cbe2eab336984ca2a298b44c97f4031845096 svn.716 0000000000000000000000000000000000000000 svn.716 e3a860dc2b21287ba888135f8892e4b469d17383 svn.1175 0000000000000000000000000000000000000000 svn.1175 e3aa0614a4d655db7ecabf95adeb4b09f3e07fe2 svn.224 0000000000000000000000000000000000000000 svn.224 e3c36f35db79a36c92df45b4b2a7334a2c77fe01 svn.762 0000000000000000000000000000000000000000 svn.762 e3ded6a4190ab349961ad3b103f14290bedd35a9 svn.110 0000000000000000000000000000000000000000 svn.110 e3f501f80bc2dce414273f63f73f5f6c33aba297 svn.1075 0000000000000000000000000000000000000000 svn.1075 e3fcc61e983e4bb2c7a5141f5278744e3f2c2c1e svn.1266 0000000000000000000000000000000000000000 svn.1266 e3fd071777c105975f82c15a7f45b53111871fb2 svn.490 0000000000000000000000000000000000000000 svn.490 e42999672ae5fab1bdfdccc0b758405407282bc6 svn.852 0000000000000000000000000000000000000000 svn.852 e45f96c679ac12778209b30973538c4313478ce4 svn.893 0000000000000000000000000000000000000000 svn.893 e4c6f2afb5e60e5fc975f8ad197b9e05977ad8d5 svn.1479 0000000000000000000000000000000000000000 svn.1479 e4cb89f2f2b7af049bb785275e75aec6345ee40e svn.184 0000000000000000000000000000000000000000 svn.184 e4cfc303bb1f0eec319d337ae4f6d3aaa09e4d3e svn.1392 0000000000000000000000000000000000000000 svn.1392 e4efa5208288ef858f2ccbfe4211a0a6a2944785 svn.1111 0000000000000000000000000000000000000000 svn.1111 e53152d529b0834ffb2ba75947ebd8a03ec10e6a svn.660 0000000000000000000000000000000000000000 svn.660 e562e09b6f3cebd6cdf17f64fdf6328455b37962 svn.1807 0000000000000000000000000000000000000000 svn.1807 e5959ab6348453899a6e8bb3bf49de951d03d4d0 svn.454 0000000000000000000000000000000000000000 svn.454 e596a16a40a2a897633797103f6d2b30025a8663 svn.1126 0000000000000000000000000000000000000000 svn.1126 e5bf9353cae7206a2f4fcaaf06db42c853a0e45c svn.297 0000000000000000000000000000000000000000 svn.297 e649c5da9087385ded87e7908622a7a3aa432cf0 svn.775 0000000000000000000000000000000000000000 svn.775 e65114d76ec41069db902fb50adc1a6caaf59c18 svn.356 0000000000000000000000000000000000000000 svn.356 e68e2fb451471695ee1e1ac0895a44847be41b8a svn.364 0000000000000000000000000000000000000000 svn.364 e6ca392f44d89c82ed47827cc351e62b93ecf471 svn.95 0000000000000000000000000000000000000000 svn.95 e70bb034f46bb63de837c054b9c0082a93eba286 svn.1652 0000000000000000000000000000000000000000 svn.1652 e71694eebd5988be6220b165de355ad6f5639ed8 svn.1086 0000000000000000000000000000000000000000 svn.1086 e7308687c3771a5ce5aad9de3c7960678f2d2c64 svn.952 0000000000000000000000000000000000000000 svn.952 e746beb215617b69a96729b6fe62d3c16ab2bc1a svn.68 0000000000000000000000000000000000000000 svn.68 e764b36940ad11559c8fb355200a5be3dc3e5227 svn.422 0000000000000000000000000000000000000000 svn.422 e79547d8e6826ba37bb81bb572f0f116304a0532 svn.1770 0000000000000000000000000000000000000000 svn.1770 e809a113e46e5f9bcb9e426b722ef93112da30e3 svn.1493 0000000000000000000000000000000000000000 svn.1493 e80ed81b5533cc74f9b9a3f78cb194a09a2eb9f0 svn.687 0000000000000000000000000000000000000000 svn.687 e8257b5e96d8fa36db504f469fedac7aad1c8936 svn.986 0000000000000000000000000000000000000000 svn.986 e829e697b9056d623104f8c58c7a17674b15981f svn.1577 0000000000000000000000000000000000000000 svn.1577 e8460c5500b005428e85959558665ec48edca809 svn.1538 0000000000000000000000000000000000000000 svn.1538 e854fcfd3bb2f14339c957d2c8348936ae4db763 svn.518 0000000000000000000000000000000000000000 svn.518 e865de13f4a9b9ec7d06ba8089c4d5a3f5c70ead svn.61 0000000000000000000000000000000000000000 svn.61 e8676721e034172dff52af911b38c952fd3b0f33 svn.1414 0000000000000000000000000000000000000000 svn.1414 e877277c9bd2d4708ee8376b6884d49742f1178c svn.847 0000000000000000000000000000000000000000 svn.847 e880a7d4823404c5e3c23b8e85e5abaae7f989ca svn.1544 0000000000000000000000000000000000000000 svn.1544 e88577b2e9f7b5cc4b6a71467ddecf926d324d39 svn.1262 0000000000000000000000000000000000000000 svn.1262 e8be9d29f2ebd43865b52006a536720d34475659 svn.926 0000000000000000000000000000000000000000 svn.926 e8c552566b7e611e0b1cf5507fab4c0a71641441 svn.689 0000000000000000000000000000000000000000 svn.689 e90d98ceddd5099b8e59776cea78818a4e963cef svn.334 0000000000000000000000000000000000000000 svn.334 e943ea00d2b15c1198088741c69aac8a5d1ea97b svn.423 0000000000000000000000000000000000000000 svn.423 e951fea3033250e64089a704e19bd9db162f6ae7 svn.1295 0000000000000000000000000000000000000000 svn.1295 e97ce937fcc7314713f79db741948cdc79939a6d svn.1809 0000000000000000000000000000000000000000 svn.1809 e9882484b7cdc4fa2408f82a6026df3459fa4c95 svn.1057 0000000000000000000000000000000000000000 svn.1057 e9c3d7d08b1f72942ee18335ece0c83170d34040 svn.1163 0000000000000000000000000000000000000000 svn.1163 e9c64f18e08b97a9c2a54180789f65013d61873d svn.419 0000000000000000000000000000000000000000 svn.419 e9c7c5702922b8a61da9d0bf435351e7f5e6861b svn.1690 0000000000000000000000000000000000000000 svn.1690 e9fb3e756f074d3e17131d1315eca56359a3b8ef svn.1073 0000000000000000000000000000000000000000 svn.1073 ea01607fed384e779d95ba04c70d71f2afc3efe1 svn.1269 0000000000000000000000000000000000000000 svn.1269 ea203317990ef509bca17d28c03b924b4d190398 svn.1550 0000000000000000000000000000000000000000 svn.1550 ea21e74c0a987a9b21bb42780b8e5fe427f2a544 svn.749 0000000000000000000000000000000000000000 svn.749 ea30a44e004f71caf90f435b9a94ed9b447170e3 svn.904 0000000000000000000000000000000000000000 svn.904 ea3443fc287b6369bcd03204df4881ca25154ada svn.1630 0000000000000000000000000000000000000000 svn.1630 ea3cea63f40d84cc71c1134c35ec0855b53af719 svn.1001 0000000000000000000000000000000000000000 svn.1001 ea9848a0989f43d9443227126e04b57980d5114d svn.1667 0000000000000000000000000000000000000000 svn.1667 eaf6f737155f7883178b450261f306e96f07ada3 svn.695 0000000000000000000000000000000000000000 svn.695 eb4a739d738bedcd955a47f678793475812429ee svn.1263 0000000000000000000000000000000000000000 svn.1263 eb820e77d4b93ed6c8795aff991f12609bf022ae svn.251 0000000000000000000000000000000000000000 svn.251 ebc6d7fcbe348258a599674b79925ebcbb3c8e11 svn.1327 0000000000000000000000000000000000000000 svn.1327 ebe4ce0fe92be979d2480f48faedce28a8181fc5 svn.1344 0000000000000000000000000000000000000000 svn.1344 ebe9411ef03491934c417c019a8d251d74ce2de7 svn.899 0000000000000000000000000000000000000000 svn.899 ec43da2bc1208370cf8306a529bd705bf4490496 svn.1297 0000000000000000000000000000000000000000 svn.1297 ec4cf5d62741efc76e9d0202f302e4e9dd99fc28 svn.1288 0000000000000000000000000000000000000000 svn.1288 ec6b12d6999dc1ca296dcac72bca469d9c26fa0b svn.466 0000000000000000000000000000000000000000 svn.466 ec84905bae92fe79db17866c3b72deff84efa51f svn.797 0000000000000000000000000000000000000000 svn.797 ec99f6513ccc04d2649415a033e0e832a2c69fe0 svn.1067 0000000000000000000000000000000000000000 svn.1067 ecbad3699297b914ebc10cebcf803150491faec4 svn.254 0000000000000000000000000000000000000000 svn.254 ecc98904c752084403545715b184cf55b8781b39 svn.718 0000000000000000000000000000000000000000 svn.718 ed216bcd32d70aee3bcff4babcdb2b88b01c8902 svn.1679 0000000000000000000000000000000000000000 svn.1679 ed436c5ee36728b29111f713dd0f242b6ad77c49 svn.368 0000000000000000000000000000000000000000 svn.368 ed945662fb56dac71f20159d917c458c5a698995 svn.359 0000000000000000000000000000000000000000 svn.359 ed96e2ae3cba35e2132ecaabf74720cf1b2b4961 svn.777 0000000000000000000000000000000000000000 svn.777 edef3025dc5c36b041481564f09873f383a55acf svn.456 0000000000000000000000000000000000000000 svn.456 edf4eb2310b560a3832bbecc68544318c8a11f1a svn.891 0000000000000000000000000000000000000000 svn.891 edf78a84101a105b2387ecf031bb5cbbc534011c svn.1023 0000000000000000000000000000000000000000 svn.1023 ee7ab4e92c74972fa5ece220604ad7d2f5908fcc svn.143 0000000000000000000000000000000000000000 svn.143 eeb699ff980833c34f5c8b2b4219d194d73178bc svn.1542 0000000000000000000000000000000000000000 svn.1542 eec38ccb6c6cf3b3ea24e86790e52afee26a21ad svn.1124 0000000000000000000000000000000000000000 svn.1124 eed10c69c338f4379b4a2842a0aca95d478b335d svn.497 0000000000000000000000000000000000000000 svn.497 eed9343db961250471a573539bdc4a82fe4e79d8 svn.669 0000000000000000000000000000000000000000 svn.669 eee672ed42fb075df1900810565659776e536bb5 svn.1114 0000000000000000000000000000000000000000 svn.1114 ef0b08e329dd1467460a7a9c7bf3e53d4010ae0c svn.739 0000000000000000000000000000000000000000 svn.739 ef3a39e8fd9b8985c6b0c66375adc2ed000ef4ab svn.807 0000000000000000000000000000000000000000 svn.807 ef3bfe42a50abc9cedf6528545c73d627f390157 svn.103 0000000000000000000000000000000000000000 svn.103 ef56cb3058242b6841511103d2e067f09933814c svn.107 0000000000000000000000000000000000000000 svn.107 ef70305750bd1da1b24174a8ee9507770e9fe3b5 svn.160 0000000000000000000000000000000000000000 svn.160 ef9eacafbae7825e35d64c77f367bbee0947ae03 svn.1485 0000000000000000000000000000000000000000 svn.1485 efde317797334ba555386283b8e00fca5a77fec8 svn.1612 0000000000000000000000000000000000000000 svn.1612 efe0af4d7cb04614b52bf99845c489915c73f047 svn.1491 0000000000000000000000000000000000000000 svn.1491 f014b0f1317696d0743507d02beb5920b075de59 svn.618 0000000000000000000000000000000000000000 svn.618 f037671d57fd06d6cb9be940aafaf0e4e7ef37a8 svn.1219 0000000000000000000000000000000000000000 svn.1219 f06de9448050dfaeb6eb120dfc5859cb4411bfa8 svn.481 0000000000000000000000000000000000000000 svn.481 f06eea2eeee19f26d81fcd1580e44da160e7f874 svn.1084 0000000000000000000000000000000000000000 svn.1084 f0837e1ecbe583af4d8991451dda24383d2833e7 svn.1529 0000000000000000000000000000000000000000 svn.1529 f089bf123355e4f58cccae286daa8341b0faede8 svn.1285 0000000000000000000000000000000000000000 svn.1285 f0bfbf4f0ff8234095f4e9260554d47b0d6c7e6d svn.997 0000000000000000000000000000000000000000 svn.997 f0c6ace804246be6499292171b3c2a25a2d98469 svn.1455 0000000000000000000000000000000000000000 svn.1455 f0cbaf7bafdda306990b6d7380d2588078a449f2 svn.1575 0000000000000000000000000000000000000000 svn.1575 f0d1af1d9f74a64cc2f3ba41c4791f8ceec4154f svn.568 0000000000000000000000000000000000000000 svn.568 f0d54897e223f01b8b4f0af89c4c1260b7e5dd1e svn.1352 0000000000000000000000000000000000000000 svn.1352 f1114268a7373a1decbea482e7d1d042756761ae svn.1534 0000000000000000000000000000000000000000 svn.1534 f14a3e397ca98de852fa7cce40243435da79ff78 svn.816 0000000000000000000000000000000000000000 svn.816 f157b1a713d1f6fd5e2fd6781ed133f248f5ffab svn.1372 0000000000000000000000000000000000000000 svn.1372 f162e5645d5c5e4fe5022a50b8c4dedd960b6401 svn.657 0000000000000000000000000000000000000000 svn.657 f19bf1ae6de86f7ae40021ab70f97976f5e8ca65 svn.37 0000000000000000000000000000000000000000 svn.37 f1b05bcc11f5d4e75e88d6314a03f44993124b94 svn.892 0000000000000000000000000000000000000000 svn.892 f1eb186c43debebfd70fd7d16ae2a582698077fa svn.1034 0000000000000000000000000000000000000000 svn.1034 f1ff6856474c9ff16b490b8383b00045b1aca3bf svn.1423 0000000000000000000000000000000000000000 svn.1423 f249bcbc653b330265ec31240211c662d77b7a29 svn.1434 0000000000000000000000000000000000000000 svn.1434 f259010e70d330e0a78843d5af6e080c42d598aa svn.1543 0000000000000000000000000000000000000000 svn.1543 f29c306f22336754779b19213871262f0f8ba7cd svn.28 0000000000000000000000000000000000000000 svn.28 f2bc5e164d73596939d721b2a579ec55a58d1274 svn.1788 0000000000000000000000000000000000000000 svn.1788 f2d3a4034d896bf6b4ecb9960aa2f5b9b671f34e svn.920 0000000000000000000000000000000000000000 svn.920 f2e36095b95676637a36541b79f06f6213984bb0 svn.62 0000000000000000000000000000000000000000 svn.62 f2ffa05cb5671eb0bad62fcd95d3ba9ecbee3acb svn.1088 0000000000000000000000000000000000000000 svn.1088 f310923770b1b1341ca9163498b0f01ca04ca84c svn.728 0000000000000000000000000000000000000000 svn.728 f37e8c96a1cfdc199dafc786e656188c8f618fd0 svn.1606 0000000000000000000000000000000000000000 svn.1606 f37f6dcc2fada56360587c2ad701ebf4778e46c9 svn.352 0000000000000000000000000000000000000000 svn.352 f38b4427c24b586efc68101d772f6d08fa27f810 svn.1470 0000000000000000000000000000000000000000 svn.1470 f3978c2cfb085d876d2d56ebb70d8be183043f30 svn.1089 0000000000000000000000000000000000000000 svn.1089 f3a1522ad4de815aa5b652f140a62a7daa67911b svn.80 0000000000000000000000000000000000000000 svn.80 f3b88e440ac38fea149a4203b51d4e4656affb6f svn.100 0000000000000000000000000000000000000000 svn.100 f3da9130ae74c98e353cd540f6904d4979a37491 svn.1704 0000000000000000000000000000000000000000 svn.1704 f40234b616dde3de2d3a9c1c6cd2ec733b9afb7d svn.197 0000000000000000000000000000000000000000 svn.197 f4793542a3d86170546bfd2ce6b93214dc1aa753 svn.209 0000000000000000000000000000000000000000 svn.209 f487178c028c014939dd70e7b5be731045293465 svn.1559 0000000000000000000000000000000000000000 svn.1559 f48ce9105d52596c8d44c6398150fbd6aa5ab0b1 svn.1574 0000000000000000000000000000000000000000 svn.1574 f4b6a2cfa7a672dcfc108ea4287f43bd48927dee svn.1361 0000000000000000000000000000000000000000 svn.1361 f4d9bb935e4e95550c4044ca22d31558411f0113 svn.962 0000000000000000000000000000000000000000 svn.962 f4fa817475a03cfbbcb7f9c359f2142ed09b4556 svn.1211 0000000000000000000000000000000000000000 svn.1211 f50108a56ae7020f7c1de5364db721956416f5c8 svn.384 0000000000000000000000000000000000000000 svn.384 f52b3c8eee5a72a545b2aee5c541e7745fc9129f svn.42 0000000000000000000000000000000000000000 svn.42 f59f115516f715da789795a1b6f39c9815c6077b svn.1198 0000000000000000000000000000000000000000 svn.1198 f6366f0ed82d609d1eedfa18d330b4056fe57515 svn.1188 0000000000000000000000000000000000000000 svn.1188 f639a64b8d4cd31d7d2f98ee8f71613b613bd1ae svn.145 0000000000000000000000000000000000000000 svn.145 f657ebf67d707a3b313b69cabe68d54fcdc2cab7 svn.478 0000000000000000000000000000000000000000 svn.478 f6e445f88d811e70314f902456dcf1753b4161cc svn.1527 0000000000000000000000000000000000000000 svn.1527 f6f3ef09895ad1890e0125dfdc544f8e5f4ad943 svn.927 0000000000000000000000000000000000000000 svn.927 f771f2707bd7e55861e4a451dfa7ed4193053486 svn.794 0000000000000000000000000000000000000000 svn.794 f773270afc6330b8d95b1da050a0f4d59c40e135 svn.732 0000000000000000000000000000000000000000 svn.732 f7862e1b9fade96044ed6c6d07fb80fe4b472e74 svn.1750 0000000000000000000000000000000000000000 svn.1750 f7d162799d92a2d0fac3185a96dad3aae2c7c0f6 svn.1248 0000000000000000000000000000000000000000 svn.1248 f7f92abef4aebe413aa4d17fa2018a6da52bd649 svn.1791 0000000000000000000000000000000000000000 svn.1791 f811e99c84648f894fa79d6e54bc96339e61af9a svn.1029 0000000000000000000000000000000000000000 svn.1029 f84de6dbd29c9686d91e3e9a818033962fedfff5 svn.555 0000000000000000000000000000000000000000 svn.555 f8792cdf3ba035cee963c95858cae239df55af31 svn.116 0000000000000000000000000000000000000000 svn.116 f89742203405b18315a5fb2b688b63ab0068f6cb svn.1468 0000000000000000000000000000000000000000 svn.1468 f8fce3c3ef6220fc74b60123177de796918b4bff svn.912 0000000000000000000000000000000000000000 svn.912 f91c56b9314ca1653f870f8d0494a97dd317bc04 svn.1187 0000000000000000000000000000000000000000 svn.1187 f936f724467761ab6d8c402e2cc52352b267b830 svn.1569 0000000000000000000000000000000000000000 svn.1569 f938afbd0ef3d4d3ae3f6d82f1039b9d84f2ddf0 svn.1370 0000000000000000000000000000000000000000 svn.1370 f9caeee4910376f6748008a70712f08b76a5b932 svn.849 0000000000000000000000000000000000000000 svn.849 f9e599e709a08f177a0e9c4a73ed3ea2dc883990 svn.1772 0000000000000000000000000000000000000000 svn.1772 fa0bbf57fc4c71c4890822f1d927eb8c2a39df20 svn.1154 0000000000000000000000000000000000000000 svn.1154 fa19b4c0a2c0f2e9c94261256bc28fb7cc421e7f svn.162 0000000000000000000000000000000000000000 svn.162 fa2f76a678e5aec01e25d9d2d81702225d28287e svn.1738 0000000000000000000000000000000000000000 svn.1738 fa5f7353102a01fc485e7d5d83aaae50f60c62b7 svn.373 0000000000000000000000000000000000000000 svn.373 fa8a351202d659f65fb62e5720f31e73e046a67a svn.1292 0000000000000000000000000000000000000000 svn.1292 fabe2f3d8e695775b59a0b43edcec84988230cb8 svn.1159 0000000000000000000000000000000000000000 svn.1159 fac6f6a5ceb212eb3d9b54a28459d6e9e1425aca svn.685 0000000000000000000000000000000000000000 svn.685 fad4f97a8a85bb4274f876f0d05ea0506562a130 svn.873 0000000000000000000000000000000000000000 svn.873 fad60561bfd2e187de42f3557dedfcf91fe5a35f svn.844 0000000000000000000000000000000000000000 svn.844 faeb3b49abaff460913359e420c0ffbd3b18f2d0 svn.778 0000000000000000000000000000000000000000 svn.778 faff3e0a69c548eef84546f92fce514b1ffeb21c svn.3 0000000000000000000000000000000000000000 svn.3 fb4fae98bac891bf929eb44fc1a4e37fb55d678d svn.1718 0000000000000000000000000000000000000000 svn.1718 fb56a1daf0b6f7583c2eba1aa3ec5c1155584101 svn.935 0000000000000000000000000000000000000000 svn.935 fb6ccc3cccc25c03bef4c242428687ca0e233cb9 svn.820 0000000000000000000000000000000000000000 svn.820 fb7dfa722237f83e639e50e6975b71095c168862 svn.640 0000000000000000000000000000000000000000 svn.640 fb8e171d63c993cad20c02658ceba5a17d04abe8 svn.1413 0000000000000000000000000000000000000000 svn.1413 fb9021a8268a9162a239877d4d4980265b40107e svn.180 0000000000000000000000000000000000000000 svn.180 fba75965849daca6545bf08ec091ce0f0727446b svn.1643 0000000000000000000000000000000000000000 svn.1643 fbb08d177e3541d70e36347191970557cc07b95e svn.1170 0000000000000000000000000000000000000000 svn.1170 fc0d258b65596f92647a4c906141ffd514ca6e25 svn.626 0000000000000000000000000000000000000000 svn.626 fc2c570351bd40022d7e58d6d03c3f9a6afca8ef svn.241 0000000000000000000000000000000000000000 svn.241 fc3bc9d6eb6bf3f7f12cab13dbb1ab20a0f479df svn.54 0000000000000000000000000000000000000000 svn.54 fc41bafd040579e1c799ee4cce9e2128fed3ee15 svn.1 0000000000000000000000000000000000000000 svn.1 fc45caaf89a5c4f916f8bf014f15a5cd48ae3a75 svn.963 0000000000000000000000000000000000000000 svn.963 fc549023f806baf5442569d6599de2f7d1e08b9c svn.995 0000000000000000000000000000000000000000 svn.995 fc60e042b59b43410254fef7c87c83dafa26f012 svn.1437 0000000000000000000000000000000000000000 svn.1437 fc645ca33d5515b3e453aafa4f84f1c0287b1459 svn.846 0000000000000000000000000000000000000000 svn.846 fd07495d021768bc0b40ce4e7033bb8f4c13d6c0 svn.1383 0000000000000000000000000000000000000000 svn.1383 fd0d1b1fa0e1d577a6fe62c1103f2446a989eb89 svn.1049 0000000000000000000000000000000000000000 svn.1049 fd0f837bb381b6819bd11233838282c3b941960a svn.1610 0000000000000000000000000000000000000000 svn.1610 fd12c95c101bb1ef71b4b64fffe25e4be8e087e3 svn.949 0000000000000000000000000000000000000000 svn.949 fd150370126ae7eeeee46ab7a672a6c9dc08ed7f svn.1417 0000000000000000000000000000000000000000 svn.1417 fd221c9cac3f6ba41f1be8aeaea2a67de0c2a68f svn.1480 0000000000000000000000000000000000000000 svn.1480 fd614c5118ed8cf0866a13cec5a008eeeb7ecad9 svn.1798 0000000000000000000000000000000000000000 svn.1798 fd6d75b921fd5581e7a13596c313afc242ecb7cc svn.137 0000000000000000000000000000000000000000 svn.137 fd910d5f2741be52641a7162a2ccbc6bfde91cf2 svn.964 0000000000000000000000000000000000000000 svn.964 fda445ffad25d980c9de21be18ca2f8d6a8c2185 svn.147 0000000000000000000000000000000000000000 svn.147 fdb1f6fe5de782bf8ecafd7edafda49587b3747d svn.1147 0000000000000000000000000000000000000000 svn.1147 fdb3c581672de44f67fb43351db58b6ab543b1d1 svn.483 0000000000000000000000000000000000000000 svn.483 fdf9b52fe83d221f74dfa77b86df18f52294a320 svn.1217 0000000000000000000000000000000000000000 svn.1217 fe02c21efeeeb64afa613396d73baf95c16cc636 svn.225 0000000000000000000000000000000000000000 svn.225 fe491964b2f1cefd84dc01e728f17d4dcbd24526 svn.1751 0000000000000000000000000000000000000000 svn.1751 fe54d44addf4db308f24d45765bf2333aa5f033a svn.458 0000000000000000000000000000000000000000 svn.458 fef75ecd30caa3b331dd89e5d47afda1c662d9a6 svn.642 0000000000000000000000000000000000000000 svn.642 fef950f2d9d99551e46a72006947ad6c14b7718d svn.439 0000000000000000000000000000000000000000 svn.439 ff2bd18d3a67d9ea19832775d904f9e8b9b3bce7 svn.1721 0000000000000000000000000000000000000000 svn.1721 ff9f8fc0a2748cbb63d2b0d7407dbd71b1935106 svn.1668 0000000000000000000000000000000000000000 svn.1668 ffe0cbfa8a2e1981cf071b180872333043fde7db svn.993 0000000000000000000000000000000000000000 svn.993 fff7118f00e25731ccf37cba3082b8fcb73cf90e svn.371 0000000000000000000000000000000000000000 svn.371 6528c562fed6f994b8d1ecabaf375ddc4707dade mpi-opaque 0000000000000000000000000000000000000000 mpi-opaque f15825659f5af3ce64aaad30062aff3603cbfb66 hop callback 0000000000000000000000000000000000000000 hop callback a71dffe4bc813fdadc506ccad9efb632e23dc843 yt-3.0a1 954d1ffcbf04c3d1b394c2ea05324d903a9a07cf yt-3.0a2 f4853999c2b5b852006d6628719c882cddf966df yt-3.0a3 079e456c38a87676472a458210077e2be325dc85 last_gplv3 ca6e536c15a60070e6988fd472dc771a1897e170 yt-2.0 882c41eed5dd4a3cdcbb567bcb79b833e46b1f42 yt-2.0.1 a2b3521b1590c25029ca0bc602ad6cb7ae7b8ba2 yt-2.1 41bd8aacfbc81fa66d7a3f2cd2880f10c3e237a4 yt-2.2 3836676ee6307f9caf5ccdb0f0dd373676a68535 yt-2.3 076cec2c57d2e4b508babbfd661f5daa1e34ec80 yt-2.4 bd285a9a8a643ebb7b47b543e9343da84cd294c5 yt-2.5 34a5e6774ceb26896c9d767563951d185a720774 yt-2.5.1 2197c101413723de13e1d0dea153b182342ff719 yt-2.5.2 59aa6445b5f4a26ecb2449f913c7f2b5fee04bee yt-2.5.3 4da03e5f00b68c3a52107ff75ce48b09360b30c2 yt-2.5.4 21c0314cee16242b6685e42a74d16f7a993c9a88 yt-2.5.5 053487f48672b8fd5c43af992e92bc2f2499f31f yt-2.6 d43ff9d8e20f2d2b8f31f4189141d2521deb341b yt-2.6.1 f1e22ef9f3a225f818c43262e6ce9644e05ffa21 yt-2.6.2 816186f16396a16853810ac9ebcde5057d8d5b1a yt-2.6.3 f327552a6ede406b82711fb800ebcd5fe692d1cb yt-3.0a4 73a9f749157260c8949f05c07715305aafa06408 yt-3.0.0 0cf350f11a551f5a5b4039a70e9ff6d98342d1da yt-3.0.1 511887af4c995a78fe606e58ce8162c88380ecdc yt-3.0.2 fd7cdc4836188a3badf81adb477bcc1b9632e485 yt-3.1.0 28733726b2a751e774c8b7ae46121aa57fd1060f yt-3.2 425ff6dc64a8eb92354d7e6091653a397c068167 yt-3.2.1 425ff6dc64a8eb92354d7e6091653a397c068167 yt-3.2.1 0000000000000000000000000000000000000000 yt-3.2.1 0000000000000000000000000000000000000000 yt-3.2.1 f7ca21c7b3fdf25d2ccab139849ae457597cfd5c yt-3.2.1 a7896583c06585be66de8404d76ad5bc3d2caa9a yt-3.2.2 80aff0c49f40e04f00d7b39149c7fc297b8ed311 yt-3.2.3 80aff0c49f40e04f00d7b39149c7fc297b8ed311 yt-3.2.3 0000000000000000000000000000000000000000 yt-3.2.3 0000000000000000000000000000000000000000 yt-3.2.3 83d2c1e9313e7d83eb5b96888451ff2646fd8ff3 yt-3.2.3 7edbfde96c3d55b227194394f46c0b2e6ed2b961 yt-3.3.0 9bc3d0e9b750c923d44d73c447df64fc431f5838 yt-3.3.1 0d0af4016c88476e134c46ce6c25d9ef88c84614 yt-3.3.2 732bbecb894e4e9c8d2d8d0a5f10810acab72d85 yt-3.3.2 732bbecb894e4e9c8d2d8d0a5f10810acab72d85 yt-3.3.2 3bdb0ca67dcaa5786d433a148e638687b4be7cdd yt-3.3.3 ca2f21bad5329d35912c4efb5d8d784339b32f3b yt-3.3.4 7d2618d9ae601f6923415053cddd855c7bdc05e4 yt-3.3.5 bf36e108e911f0c415b6cc23691723c2d31ec69f yt-3.3.5 yt-project-yt-f043ac8/.mailmap000066400000000000000000000311731510711153200163000ustar00rootroot00000000000000Stephen Skory Stephen Skory Stephen Skory "Stephen Skory stephenskory@yahoo.com" <"Stephen Skory stephenskory@yahoo.com"> Yuan Li Yuan Li Cameron Hummels Cameron Hummels Cameron Hummels chummels John Wise John Wise John Wise John Wise Sam Skillman Sam Skillman Sam Skillman samskillman Casey Stark Casey Stark Christian Karch chiffre Christian Karch Christian Karch Andrew Myers Andrew Myers Andrew Myers atmyers Douglas Rudd drudd Andrew Wetzel awetzel Andrew Wetzel Andrew Wetzel David Collins David Collins David Collins dcollins4096 David Collins David Collins (dcollins4096@gmail.com) Tom Abel tabel Tom Abel Tom Abel Kaylea Nelson sername=kayleanelson Kaylea Nelson kayleanelson kayleanelson Stephanie Tonnesen stonnes John Forbes John Forbes Elliott Biondo Elliott Biondo Sam Geen Sam Geen Alex Bogert fbogert Brian O'Shea bwoshea Ji-hoon Kim Ji-hoon Kim Kirk Barrow Kirk Barrow Kirk Barrow Kirk Barrow Kirk Barrow kbarrow Kirk Barrow kbarrow Antoine Strugarek Antoine Strugarek Antoine Strugarek astrugarek Antoine Strugarek Antoine Strugarek Anna Rosen Anna Rosen John ZuHone jzuhone John ZuHone jzuhone John ZuHone John Zuhone John ZuHone John Zuhone Kenz Arraki Kenza Arraki Kenz Arraki Kenza Arraki Allyson Julian Allyson Julian Allyson Julian Allyson Julian Allyson Julian Allyson Julian Allyson Julian AJ Allyson Julian Al Jul Allyson Julian AJ Allyson Julian AJ Brian Crosby bcrosby Ben Thompson Ben Thompson Ben Thompson cosmosquark Ben Thompson Benjamin Thompson Chris Malone Chris Malone Chris Malone ChrisMalone Chris Malone ChrisMalone Jill Naiman Jill Naiman Jill Naiman Jill Naiman Jill Naiman jnaiman Jill Naiman jnaiman Miguel de Val-Borro Miguel de Val-Borro Stuary Levy Stuart Levy Ben Keller Ben Keller Daniel Fenn dfenn Meagan Lang langmm Joseph Tomlinson jmt354 Desika Narayanan desika Desika Narayanan dnarayanan Desika Narayanan dnarayanan Nathan Goldbaum Nathan Goldbaum Nathan Goldbaum Nathan Goldbaum Nathan Goldbaum Nathan Goldbaum Fabian Koller NTAuthority@honeypot.fritz.box Fabian Koller NTAuthority@guest053.fz-rossendorf.de Fabian Koller NTAuthority@guest692.fz-rossendorf.de Fabian Koller NTAuthority@guest692.fz-rossendorf.de Fabian Koller Fabian Koller Fabian Koller Fabian Koller Fabian Koller C0nsultant Rafael Ruggiero Rafael Ruggiero Rafael Ruggiero Rafael Ruggiero John Regan John Regan John Regan John Regan John Regan John Regan <32193880+fearmayo@users.noreply.github.com> John Regan John Regan Dave Grote David Grote Gabriel Altay galtay Mark Richardson Mark Richardson Jared Coughlin Jared Jared Coughlin Jared Jared Coughlin Jared William Coughlin Jared Coughlin Jared Coughlin Jared Coughlin Jared Coughlin <30010597+jcoughlin11@users.noreply.github.com> Corentin Cadiou cphyc Corentin Cadiou ccc@pingouin.local Corentin Cadiou ccc@pingouin-2.local Corentin Cadiou Corentin Cadiou convert-repo None Patrick Shriwise pshriwise Patrick Shriwise Patrick Shriwise Michael Zingale Michael Zingale Sam Leitner Sam Leitner Geoffrey So gsiisg Matthew Turk Matt Turk Matthew Turk Matthew Turk Stephanie Ho Stephanie Ho Stephanie Ho stephaniehho <20467246+stephaniehho@users.noreply.github.com> Stephanie Ho Stephanie Ho Salvatore Cielo sacielo Nicholas Earl nmearl Marianne Corvellec Marianne Corvellec Hugo Pfister Hugo Pfister André-Patrick Bubel Moredread Abhishek Singh git-abhishek <36498066+git-abhishek@users.noreply.github.com> Abhishek Singh Abhishek Singh <36498066+git-abhishek@users.noreply.github.com> Ashley Kelly ash Ashley Kelly Ashley Kelly Max Katz Max Katz Suoqing Ji Suoqing JI Bili Dong qobilidop Donald E Willcox dwillcox Donald E Willcox Donald E. Willcox Yingchao Lu yingchaolu Andrew Myers atmyers Ricarda Beckmann Ricarda Beckmann Ricarda Beckmann Ricarda Beckmann Ricarda Beckmann RicardaBeckmann Ricarda Beckmann Ricarda Beckmann Ricarda Beckmann RicardaBeckmann Corentin Cadiou Corentin Cadiou Chris Moody Christopher Moody Chris Moody Chris Moody Chris Moody Christopher Moody Chris Moody Christopher Erick Moody Casey W. Stark Casey W. Stark BW Keller Ben Keller Benjamin Thompson Ben Thompson Alex Lindsay Alex Lindsay Alex Lindsay Alex Lindsay Yi-Hao Chen (ychen@astro.wisc.edu) Yi-Hao Chen (yihaochentw@gmail.com) Kacper Kowalik Kacper Kowalik (Xarthisius) John ZuHone John Zuhone John ZuHone John Zuhone John ZuHone John Zuhone John ZuHone John Zuhone John ZuHone John Zuhone Clayton Strawn Clayton Strawn <33767568+claytonstrawn@users.noreply.github.com> Hilary Egan Hilary Egan Yi-Hao Chen Yi-Hao Chen Prateek Gupta Prateekgidolia Max Gronke maxbeegee Ashley Kelly ashkelly Sean Larkin sflarkin <41403179+sflarkin@users.noreply.github.com> Chris Havlin chrishavlin Michael Ryan mtryan83 Ryan Farber Ryan Farber Ryan Farber Ryan Farber Max Gronke Max Gronke Martin Alvarez Sergio Sergio Martin Alvarez Sergio MartinAlvarezSergio <51377626+MartinAlvarezSergio@users.noreply.github.com> Claire Kopenhafer Claire Kopenhafer Clément Robert Clément Robert Corentin Cadiou Corentin Cadiou Niels Claes Niels Claes RevathiJambunathan Revathi Jambunathan Matthew Abruzzo mabruzzo Austin Gilbert Austin Gilbert Josh Borrow Josh Borrow Rick Wagner Rick Wagner yt-project-yt-f043ac8/.pre-commit-config.yaml000066400000000000000000000024431510711153200211360ustar00rootroot00000000000000# pre-commit 1.1.0 is required for `exclude` # however `minimum_pre_commit_version` itself requires 1.15.0 minimum_pre_commit_version: "1.15.0" exclude: "^(\ yt/extern\ |yt/frontends/stream/sample_data\ |yt/units\ |scripts\ |benchmark\ |setupext.py\ |yt/visualization/_colormap_data.py\ )" ci: autofix_prs: false autoupdate_schedule: monthly repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: no-commit-to-branch - id: check-shebang-scripts-are-executable - id: check-executables-have-shebangs - id: check-yaml # TODO: replace this with ruff when it supports embedded python blocks # see https://github.com/astral-sh/ruff/issues/8237 - repo: https://github.com/adamchainz/blacken-docs rev: 1.19.1 hooks: - id: blacken-docs additional_dependencies: [black==24.3.0] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.7.2 hooks: - id: ruff-format - id: ruff args: [ --fix, --show-fixes, ] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: rst-backticks - repo: https://github.com/MarcoGorelli/cython-lint rev: v0.16.2 hooks: - id: cython-lint args: [--no-pycodestyle] yt-project-yt-f043ac8/.tours/000077500000000000000000000000001510711153200161045ustar00rootroot00000000000000yt-project-yt-f043ac8/.tours/particle-indexing.tour000066400000000000000000000223421510711153200224300ustar00rootroot00000000000000{ "$schema": "https://aka.ms/codetour-schema", "title": "particle indexing", "steps": [ { "title": "Introduction", "description": "We're going to walk through how particle indexing works in yt 4.0.\n\nImagine you've got a bunch of LEGO blocks that you know build up a model. (For fun, let's pretend it's the Arendelle castle.) They're all separated into little bags, and you don't exactly know which pieces are in which bags, but maybe you vaguely know that there's some organizational scheme to them.\n\nWhen you build your castle, you *could* read the instructions step by step and inspect every single piece in every single bag until you find the one you're looking for. But that wouldn't be terribly efficient! Wouldn't it be easier if you had some way of saying, \"I know that this piece is in one of *these* bags, so I'll only look in those bags until I find it.\"\n\nThat's what we do with indexing in yt, and why particle indexing is particularly tricky -- because when we want to select data, we want to minimize the amount of time we spend searching, checking, and discarding the parts of the dataset that we don't need.\n\nSo how do we do this in yt?" }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "The `ParticleIndex` class is used by all of the frontends that have particles as their principle component. In `yt`, there are basically four main management classes for a given dataset -- the `Dataset` object itself, which contains metadata and pointers to the other managers, along with a `FieldInfoContainer` that describes the different \"fields\" that `yt` knows how to generate, an `IOHandler` that manages access to the actual data, and -- most relevant for our purposes today! -- the `Index` object.\n\nThere are a few different types, but the one we're going to look at today is the `ParticleIndex` class.", "line": 18, "selection": { "start": { "line": 18, "character": 1 }, "end": { "line": 18, "character": 28 } } }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "The `_initialize_index` method is called only once on each `Index` object. It's where we set up the in-memory data structures that tell us how to map spatial regions in the domain of the dataset to places \"on disk\" (but not necessarily on an actual disk! They could be in dictionaries, in remote URLs, etc) that the data can be found. This is where the bulk of the operations occur, and it can be expensive for some situations, so we try to minimize the incurred cost of time.", "line": 110 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "With particle datasets, if we can't guess the left and right edges, we do a pass over the data to figure them out.\n\nThis isn't amazing! It's definitely not my personal favorite. But, it is what we do. There are ways out there of avoiding this -- in fact, it would be completely feasible to just set the domain to be all the way from the minimum double precision representation to the maximum double precision representation, and then only occupy a small portion in a hierarchical mapping. But we don't do this now.", "line": 121 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "When we don't have a lot of data, for instance if the `data_files` attribute only contains one thing, we don't do any indexing. We'll have to read the whole thing anyway, so, whatever, right?\n\nOne thing that's worth keeping in mind here is that `data_files` are not always just the \"files\" that live on disk. Some data formats, for instance, use just a single file, but include a huge number of particles in it. We sub-chunk these so they appear as several \"virtual\" data files.\n\nThis helps us keep our indexing efficient, since there's no point to doing a potentially-expensive indexing operation on a small dataset, and we also don't want to give up all of our ability to set the size we read from disk.", "line": 145 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "And here's where we start actually setting up our index.\n\nBecause the indexing operation can be expensive, we build in ways of caching. That way, the *second* time you load a dataset, it won't have to conduct the indexing operation unless it really needs to -- it'll just use what it came up with the first time.", "line": 190 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "This is where we build our new index. Let's take a look at what that looks like!\n\nWe have two steps to this process. The first is to build a \"coarse\" index -- this is a reasonably low-resolution index to let us know, \"Hey, this bit might be relevant!\") and the second is a much more \"refined\" index for when we need to do very detail-oriented subselection.", "line": 200 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "To generate a coarse index, we loop over all of our files, and look at the particles. (This sure does sound expensive, right? Good thing we're going to cache the results!)\n\nEach `IOHandler` for the particle objects has to provide a `_yield_coordinates` method. This method just has to yield a bunch of tuples of the particle types and their positions.", "line": 214 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "We won't be diving in to this routine, but let's talk about what it does.\n\nThe `regions` attribute on a `ParticleIndex` object is where we set \"on\" and \"off\" bits that correspond between spatial locations and `data_file` objects.\n\nIf we think about our domain as a big 3D volume, we could divide it up into a grid of positions. Each of these positions -- `i, j, k` -- can be turned into a single number. To do this we use a morton index, a way of mapping regions of physical space to unique binary representations of each `i, j, k`.\n\nThe purpose of this loop is to turn each `data_file` into a set of \"on\" and \"off\" marks, where \"off\" indicates that no particles exist, and \"on\" indicates they do.\n\nSo, going *in*, each `data_file` is given an array of all zeros, and the array has `I*J*K` *bits*, where `I` is the number of subdivisions in x, `J` is the number in y and `K` is the number in z. The way we set it up, these are always the same, and they are always equal to `2**index_order1`. So if your `index_order1` is 3, this would be `I==J==K==8`, and you'd have a total of `8*8*8` bits in the array, corresponding to a total size of 64 bytes.\n\nOne important thing to keep in mind is that we save on memory by only storing the *bits*, not the counts! That's because this is just an indexing system meant to tell us where to look, so we want to keep it as lightweight as possible.", "line": 224 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "This line, which happens *outside* the loop over particle positions, compresses the bitarrays. That way we keep our memory usage to a minimum.", "line": 225 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "This line is crucial for the next step of computing the refined indexes. It conducts a set of logical operations to see which bits in the array are set *more than once* -- which means that there's more than one place you'll have to look if you're looking for particles in that region. It's in those places that we make a second, more refined index.", "line": 227 }, { "file": "yt/geometry/particle_geometry_handler.py", "description": "This is the tricky part, where we conduct a second bitmapping operation.\n\nHere, we look at those places where collisions in the coarse index have occurred. We want to do our best to disambiguate the regions that different data files encompass, so in all of those regions, we insert a new *subdivided* region. So for instance, if the region in `i,j,k` of the coarse index is touched by a couple files, we insert in `i,j,k` a whole new index, this time based on `index_order2`. And, we index that. Because we're using compressed indexes, this usually does not take up that much memory, but it can get unwieldy in those cases where you have particles with big smoothing lengths that need to be taken into account.\n\nThis loop requires a bit more of a dance, because for each collision in the coarse index, we have to construct a refined index. So the memory usage is a bit higher here, because we can't compress the arrays until we're totally done with them.\n\nBut, at the end of this, what we have is a set of bitmaps -- one for each data file -- that tell us where the data file exerts its influence. And then, wherever more than one data file exerts its influence, we have *another* set of bitmaps for each of *those* data files that tell us which sub-regions are impacted.\n\nThis then gets used whenever we want to read data, to tell us which data files we have to look at. For sub-selecting from a really big set of particles, we can save an awful lot of time and IO!", "line": 251 } ], "ref": "511c82da16e8b86e09a45ac9bb108bc0c47dd87c" } yt-project-yt-f043ac8/CITATION000066400000000000000000000036551510711153200160200ustar00rootroot00000000000000To cite yt in publications, please use: Turk, M. J., Smith, B. D., Oishi, J. S., et al. 2011, ApJS, 192, 9 In the body of the text, please add a footnote to the yt webpage: http://yt-project.org/ For LaTex and BibTex users: \bibitem[Turk et al.(2011)]{2011ApJS..192....9T} Turk, M.~J., Smith, B.~D., Oishi, J.~S., et al.\ 2011, The Astrophysical Journal Supplement Series, 192, 9 @ARTICLE{2011ApJS..192....9T, author = {{Turk}, M.~J. and {Smith}, B.~D. and {Oishi}, J.~S. and {Skory}, S. and {Skillman}, S.~W. and {Abel}, T. and {Norman}, M.~L.}, title = "{yt: A Multi-code Analysis Toolkit for Astrophysical Simulation Data}", journal = {The Astrophysical Journal Supplement Series}, archivePrefix = "arXiv", eprint = {1011.3514}, primaryClass = "astro-ph.IM", keywords = {cosmology: theory, methods: data analysis, methods: numerical}, year = 2011, month = jan, volume = 192, eid = {9}, pages = {9}, doi = {10.1088/0067-0049/192/1/9}, adsurl = {http://adsabs.harvard.edu/abs/2011ApJS..192....9T}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } Using yt can also utilize other functionality. If you utilize ORIGAMI, we ask that you please cite the ORIGAMI paper: @ARTICLE{2012ApJ...754..126F, author = {{Falck}, B.~L. and {Neyrinck}, M.~C. and {Szalay}, A.~S.}, title = "{ORIGAMI: Delineating Halos Using Phase-space Folds}", journal = {\apj}, archivePrefix = "arXiv", eprint = {1201.2353}, primaryClass = "astro-ph.CO", keywords = {dark matter, galaxies: halos, large-scale structure of universe, methods: numerical}, year = 2012, month = aug, volume = 754, eid = {126}, pages = {126}, doi = {10.1088/0004-637X/754/2/126}, adsurl = {http://adsabs.harvard.edu/abs/2012ApJ...754..126F}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } The main homepage for ORIGAMI can be found here: http://icg.port.ac.uk/~falckb/origami.html yt-project-yt-f043ac8/CONTRIBUTING.rst000066400000000000000000001072141510711153200173200ustar00rootroot00000000000000.. This document is rendered in HTML with cross-reference links filled in at https://yt-project.org/doc/developing/developing.html .. _getting-involved: Getting Involved ================ There are *lots* of ways to get involved with yt, as a community and as a technical system -- not all of them just contributing code, but also participating in the community, helping us with designing the websites, adding documentation, and sharing your scripts with others. Coding is only one way to be involved! Communication Channels ---------------------- There are three main communication channels for yt: * Many yt developers participate in the yt Slack community. Slack is a free chat service that many teams use to organize their work. You can get an invite to yt's Slack organization by clicking the "Join us @ Slack" button on this page: https://yt-project.org/community.html * `yt-users `_ is a relatively high-traffic mailing list where people are encouraged to ask questions about the code, figure things out and so on. * `yt-dev `_ is a much lower-traffic mailing list designed to focus on discussions of improvements to the code, ideas about planning, development issues, and so on. The easiest way to get involved with yt is to read the mailing lists, hang out in IRC or slack chat, and participate. If someone asks a question you know the answer to (or have your own question about!) write back and answer it. If you have an idea about something, suggest it! We not only welcome participation, we encourage it. Documentation ------------- The yt documentation is constantly being updated, and it is a task we would very much appreciate assistance with. Whether that is adding a section, updating an outdated section, contributing typo or grammatical fixes, adding a FAQ, or increasing coverage of functionality, it would be very helpful if you wanted to help out. The easiest way to help out is to fork the main yt repository and make changes in it to contribute back to the ``yt-project``. A fork is a copy of a repository; in this case the fork will live in the space under your username on github, rather than the ``yt-project``. If you have never made a fork of a repository on github, or are unfamiliar with this process, here is a short article about how to do so: https://help.github.com/en/github/getting-started-with-github/fork-a-repo . The documentation for ``yt`` lives in the ``doc`` directory in the root of the yt git repository. To make a contribution to the yt documentation you will make your changes in your own fork of ``yt``. When you are done, issue a pull request through the website for your new fork, and we can comment back and forth and eventually accept your changes. See :ref:`sharing-changes` for more information about contributing your changes to yt on GitHub. Gallery Images and Videos ------------------------- If you have an image or video you'd like to display in the image or video galleries, getting it included it easy! You can either fork the `yt homepage repository `_ and add it there, or email it to us and we'll add it to the `Gallery `_. We're eager to show off the images and movies you make with yt, so please feel free to drop `us `_ a line and let us know if you've got something great! Technical Contributions ----------------------- Contributing code is another excellent way to participate -- whether it's bug fixes, new features, analysis modules, or a new code frontend. See :ref:`creating_frontend` for more details. The process is pretty simple: fork on GitHub, make changes, issue a pull request. We can then go back and forth with comments in the pull request, but usually we end up accepting. For more information, see :ref:`contributing-code`, where we spell out how to get up and running with a development environment, how to commit, and how to use GitHub. When you're ready to share your changes with the community, refer to :ref:`sharing-changes` to see how to contribute them back upstream. Online Presence --------------- Some of these fall under the other items, but if you'd like to help out with the website or any of the other ways yt is presented online, please feel free! Almost everything is kept in git repositories on GitHub, and it is very easy to fork and contribute back changes. Please feel free to dig in and contribute changes. Word of Mouth ------------- If you're using yt and it has increased your productivity, please feel encouraged to share that information. Cite our `paper `_, tell your colleagues, and just spread word of mouth. By telling people about your successes, you'll help bring more eyes and hands to the table -- in this manner, by increasing participation, collaboration, and simply spreading the limits of what the code is asked to do, we hope to help scale the utility and capability of yt with the community size. Feel free to `blog `_ about, `tweet `_ about and talk about what you are up to! Long-Term Projects ------------------ There are some out-there ideas that have been bandied about for the future directions of yt -- stuff like fun new types of visualization, remapping of coordinates, new ways of accessing data, and even new APIs to make life easier. yt is an ambitious project. Let's be ambitious together! yt Community Code of Conduct ---------------------------- The community of participants in open source Scientific projects is made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences success and continued growth. We expect everyone in our community to follow these guidelines when interacting with others both inside and outside of our community. Our goal is to keep ours a positive, inclusive, successful, and growing community. As members of the community, - We pledge to treat all people with respect and provide a harassment- and bullying-free environment, regardless of sex, sexual orientation and/or gender identity, disability, physical appearance, body size, race, nationality, ethnicity, and religion. In particular, sexual language and imagery, sexist, racist, or otherwise exclusionary jokes are not appropriate. - We pledge to respect the work of others by recognizing acknowledgment/citation requests of original authors. As authors, we pledge to be explicit about how we want our own work to be cited or acknowledged. - We pledge to welcome those interested in joining the community, and realize that including people with a variety of opinions and backgrounds will only serve to enrich our community. In particular, discussions relating to pros/cons of various technologies, programming languages, and so on are welcome, but these should be done with respect, taking proactive measure to ensure that all participants are heard and feel confident that they can freely express their opinions. - We pledge to welcome questions and answer them respectfully, paying particular attention to those new to the community. We pledge to provide respectful criticisms and feedback in forums, especially in discussion threads resulting from code contributions. - We pledge to be conscientious of the perceptions of the wider community and to respond to criticism respectfully. We will strive to model behaviors that encourage productive debate and disagreement, both within our community and where we are criticized. We will treat those outside our community with the same respect as people within our community. - We pledge to help the entire community follow the code of conduct, and to not remain silent when we see violations of the code of conduct. We will take action when members of our community violate this code such as contacting confidential@yt-project.org (all emails sent to this address will be treated with the strictest confidence) or talking privately with the person. This code of conduct applies to all community situations online and offline, including mailing lists, forums, social media, conferences, meetings, associated social events, and one-to-one interactions. The yt Community Code of Conduct was adapted from the `Astropy Community Code of Conduct `_, which was partially inspired by the PSF code of conduct. .. _contributing-code: How to Develop yt ================= yt is a community project! We are very happy to accept patches, features, and bugfixes from any member of the community! yt is developed using git, primarily because it enables very easy and straightforward submission of revisions. We're eager to hear from you, and if you are developing yt, we encourage you to subscribe to the `developer mailing list `_. Please feel free to hack around, commit changes, and send them upstream. .. note:: If you already know how to use the `git version control system `_ and are comfortable with handling it yourself, the quickest way to contribute to yt is to `fork us on GitHub `_, make your changes, push the changes to your fork and issue a `pull request `_. The rest of this document is just an explanation of how to do that. See :ref:`code-style-guide` for more information about coding style in yt and :ref:`docstrings` for an example docstring. Please read them before hacking on the codebase, and feel free to email any of the mailing lists for help with the codebase. Keep in touch, and happy hacking! .. _open-issues: Open Issues ----------- If you're interested in participating in yt development, take a look at the `issue tracker on GitHub `_. You can search by labels, indicating estimated level of difficulty or category, to find issues that you would like to contribute to. Good first issues are marked with a label of *new contributor friendly*. While we try to triage the issue tracker regularly to assign appropriate labels to every issue, it may be the case that issues not marked as *new contributor friendly* are actually suitable for new contributors. Here are some predefined issue searches that might be useful: * Unresolved issues `marked "new contributor friendly" `_. * `All unresolved issues `_. Submitting Changes ------------------ We provide a brief introduction to submitting changes here. yt thrives on the strength of its communities (https://arxiv.org/abs/1301.7064 has further discussion) and we encourage contributions from any user. While we do not discuss version control, git, or the advanced usage of GitHub in detail here, we do provide an outline of how to submit changes and we are happy to provide further assistance or guidance. Licensing +++++++++ yt is `licensed `_ under the BSD 3-clause license. Versions previous to yt-2.6 were released under the GPLv3. All contributed code must be BSD-compatible. If you'd rather not license in this manner, but still want to contribute, please consider creating an external package, which we'll happily link to. How To Get The Source Code For Editing ++++++++++++++++++++++++++++++++++++++ yt is hosted on GitHub, and you can see all of the yt repositories at https://github.com/yt-project/. To fetch and modify source code, make sure you have followed the steps above for bootstrapping your development (to assure you have a GitHub account, etc.). In order to modify the source code for yt, we ask that you make a "fork" of the main yt repository on GitHub. A fork is simply an exact copy of the main repository (along with its history) that you will now own and can make modifications as you please. You can create a personal fork by visiting the yt GitHub webpage at https://github.com/yt-project/yt/ . After logging in, you should see an option near the top right labeled "fork". You now have a forked copy of the yt repository for your own personal modification. This forked copy exists on the GitHub repository, so in order to access it locally you must clone it onto your machine from the command line: .. code-block:: bash $ git clone https://github.com//yt ./yt-git This downloads that new forked repository to your local machine, so that you can access it, read it, make modifications, etc. It will put the repository in a local directory of the same name as the repository in the current working directory. .. code-block:: bash $ cd yt-git Verify that you are on the ``main`` branch of yt by running: .. code-block:: bash $ git branch You can see any past state of the code by using the git log command. For example, the following command would show you the last 5 revisions (modifications to the code) that were submitted to that repository. .. code-block:: bash $ git log -n 5 Using the revision specifier (the number or hash identifier next to each changeset), you can update the local repository to any past state of the code (a previous changeset or version) by executing the command: .. code-block:: bash $ git checkout revision_specifier You can always return to the most recent version of the code by executing the same command as above with the most recent revision specifier in the repository. However, using ``git log`` when you're checked out to an older revision specifier will not show more recent changes to the repository. An alternative option is to use ``checkout`` on a branch. In yt the ``main`` branch is our primary development branch, so checking out ``main`` should return you to the tip (or most up-to-date revision specifier) on the ``main`` branch. .. code-block:: bash $ git checkout main Lastly, if you want to use this new downloaded version of your yt repository as the *active* version of yt on your computer (i.e. the one which is executed when you run yt from the command line or the one that is loaded when you do ``import yt``), then you must "activate" by building yt from source as described in :ref:`install-from-source`. .. _reading-source: How To Read The Source Code +++++++++++++++++++++++++++ The root directory of the yt git repository contains a number of subdirectories with different components of the code. Most of the yt source code is contained in the yt subdirectory. This directory itself contains the following subdirectories: ``frontends`` This is where interfaces to codes are created. Within each subdirectory of yt/frontends/ there must exist the following files, even if empty: * ``data_structures.py``, where subclasses of AMRGridPatch, Dataset and AMRHierarchy are defined. * ``io.py``, where a subclass of IOHandler is defined. * ``fields.py``, where fields we expect to find in datasets are defined * ``misc.py``, where any miscellaneous functions or classes are defined. * ``definitions.py``, where any definitions specific to the frontend are defined. (i.e., header formats, etc.) ``fields`` This is where all of the derived fields that ship with yt are defined. ``geometry`` This is where geometric helpler routines are defined. Handlers for grid and oct data, as well as helpers for coordinate transformations can be found here. ``visualization`` This is where all visualization modules are stored. This includes plot collections, the volume rendering interface, and pixelization frontends. ``data_objects`` All objects that handle data, processed or unprocessed, not explicitly defined as visualization are located in here. This includes the base classes for data regions, covering grids, time series, and so on. This also includes derived fields and derived quantities. ``units`` This used to be where all the unit-handling code resided, but as of now it's mostly just a thin wrapper around unyt. ``utilities`` All broadly useful code that doesn't clearly fit in one of the other categories goes here. If you're looking for a specific file or function in the yt source code, use the unix find command: .. code-block:: bash $ find -name '' The above command will find the FILENAME in any subdirectory in the DIRECTORY_TREE_TO_SEARCH. Alternatively, if you're looking for a function call or a keyword in an unknown file in a directory tree, try: .. code-block:: bash $ grep -R This can be very useful for tracking down functions in the yt source. .. _building-yt: Building yt +++++++++++ If you have made changes to any C or Cython (``.pyx``) modules, you have to rebuild yt before your changes are usable. See :ref:`install-from-source`. .. _requirements-for-code-submission: Requirements for Code Submission -------------------------------- Modifications to the code typically fall into one of three categories, each of which have different requirements for acceptance into the code base. These requirements are in place for a few reasons -- to make sure that the code is maintainable, testable, and that we can easily include information about changes in changelogs during the release procedure. (See `YTEP-0008 `_ for more detail.) For all types of contributions, it is required that all tests pass, or that all non-passing tests are specifically accounted for. * New Features * New unit tests (possibly new answer tests) (See :ref:`testing`) * Docstrings in the source code for the public API * Addition of new feature to the narrative documentation (See :ref:`writing_documentation`) * Addition of cookbook recipe (See :ref:`writing_documentation`) * Extension or Breakage of API in Existing Features * Update existing narrative docs and docstrings (See :ref:`writing_documentation`) * Update existing cookbook recipes (See :ref:`writing_documentation`) * Modify of create new unit tests (See :ref:`testing`) * Bug fixes * Unit test is encouraged, to ensure breakage does not happen again in the future. (See :ref:`testing`) * At a minimum, a minimal, self-contained example demonstrating the bug should because included in the body of the Pull Request, or as part of an independent issue. When submitting, you will be asked to make sure that your changes meet all of these requirements. They are pretty easy to meet, and we're also happy to help out with them. See :ref:`code-style-guide` for how to easily conform to our style guide. .. _git-with-yt: How to Use git with yt ---------------------- If you're new to git, the following resource is pretty great for learning the ins and outs: * https://git-scm.com/ There also exist a number of step-by-step git tutorials to help you get used to version controlling files with git. Here are a few resources that you may find helpful: * http://swcarpentry.github.io/git-novice/ * https://git-scm.com/docs/gittutorial * https://try.github.io/ The commands that are essential for using git include: * ``git --help`` which provides help for any git command. For example, you can learn more about the ``log`` command by doing ``git log --help``. * ``git add `` which stages changes to the specified paths for subsequent committing (see below). * ``git commit`` which commits staged changes (stage using ``git add`` as above) in the working directory to the repository, creating a new "revision." * ``git merge `` which merges the revisions from the specified branch into the current branch, creating a union of their lines of development. This updates the working directory. * ``git pull `` which pulls revisions from the specified branch of the specified remote repository into the current local branch. Equivalent to ``git fetch `` and then ``git merge /``. This updates the working directory. * ``git push `` which sends revisions on local branches to matching branches on the specified remote. ``git push `` will only push changes for the specified branch. * ``git log`` which shows a log of all revisions on the current branch. There are many options you can pass to ``git log`` to get additional information. One example is ``git log --oneline --decorate --graph --all``. We are happy to answer questions about git use on our IRC, slack chat or on the mailing list to walk you through any troubles you might have. Here are some general suggestions for using git with yt: * Although not necessary, a common development work flow is to create a local named branch other than ``main`` to address a feature request or bugfix. If the dev work addresses a specific yt GitHub issue, you may include that issue number in the branch name. For example, if you want to work on issue number X regarding a cool new slice plot feature, you might name the branch: ``cool_new_plot_feature_X``. When you're ready to share your work, push your feature branch to your remote and create a pull request to the ``main`` branch of the yt-project's repository. * When contributing changes, you might be asked to make a handful of modifications to your source code. We'll work through how to do this with you, and try to make it as painless as possible. * Your test may fail automated style checks. See :ref:`code-style-guide` for more information about automatically verifying your code style. * You should only need one fork. To keep it in sync, you can sync from the website. See :ref:`sharing-changes` for a description of the basic workflow and :ref:`multiple-PRs` for a discussion about what to do when you want to have multiple open pull requests at the same time. * If you run into any troubles, stop by IRC (see :ref:`irc`), Slack, or the mailing list. .. _sharing-changes: Making and Sharing Changes -------------------------- The simplest way to submit changes to yt is to do the following: * Build yt from the git repository * Navigate to the root of the yt repository * Make some changes and commit them * Fork the `yt repository on GitHub `_ * Push the changesets to your fork * Issue a pull request. Here's a more detailed flowchart of how to submit changes. #. Fork yt on GitHub. (This step only has to be done once.) You can do this at: https://github.com/yt-project/yt/fork. #. Follow :ref:`install-from-source` for instructions on how to build yt from the git repository. (Below, in :ref:`reading-source`, we describe how to find items of interest.) If you have already forked the repository then you can clone your fork locally:: git clone https://github.com//yt ./yt-git This will create a local clone of your fork of yt in a folder named ``yt-git``. #. Edit the source file you are interested in and test your changes. (See :ref:`testing` for more information.) #. Create a uniquely named branch to track your work. For example: ``git checkout -b my-first-pull-request`` #. Stage your changes using ``git add ``. This command take an argument which is a series of filenames whose changes you want to commit. After staging, execute ``git commit -m ". Addresses Issue #X"``. Note that supplying an actual GitHub issue # in place of ``X`` will cause your commit to appear in the issue tracker after pushing to your remote. This can be very helpful for others who are interested in what work is being done in connection to that issue. #. Remember that this is a large development effort and to keep the code accessible to everyone, good documentation is a must. Add in source code comments for what you are doing. Add in docstrings if you are adding a new function or class or keyword to a function. Add documentation to the appropriate section of the online docs so that people other than yourself know how to use your new code. #. If your changes include new functionality or cover an untested area of the code, add a test. (See :ref:`testing` for more information.) Commit these changes as well. #. Add your remote repository with a unique name identifier. It can be anything but it is conventional to call it ``origin``. You can see names and URLs of all the remotes you currently have configured with:: git remote -v If you already have an ``origin`` remote, you can set it to your fork with:: git remote set-url origin https://github.com//yt If you do not have an ``origin`` remote you will need to add it:: git remote add origin https://github.com//yt In addition, it is also useful to add a remote for the main yt repository. By convention we name this remote ``upstream``:: git remote add upstream https://github.com/yt-project/yt Note that if you forked the yt repository on GitHub and then cloned from there you will not need to add the ``origin`` remote. #. Push your changes to your remote fork using the unique identifier you just created and the command:: git push origin my-first-pull-request Where you should substitute the name of the feature branch you are working on for ``my-first-pull-request``. .. note:: Note that the above approach uses HTTPS as the transfer protocol between your machine and GitHub. If you prefer to use SSH - or perhaps you're behind a proxy that doesn't play well with SSL via HTTPS - you may want to set up an `SSH key`_ on GitHub. Then, you use the syntax ``ssh://git@github.com//yt``, or equivalent, in place of ``https://github.com//yt`` in git commands. For consistency, all commands we list in this document will use the HTTPS protocol. .. _SSH key: https://help.github.com/en/articles/connecting-to-github-with-ssh/ #. Issue a pull request at https://github.com/yt-project/yt/pull/new/main A pull request is essentially just asking people to review and accept the modifications you have made to your personal version of the code. During the course of your pull request you may be asked to make changes. These changes may be related to style issues, correctness issues, or requesting tests. The process for responding to pull request code review is relatively straightforward. #. Make requested changes, or leave a comment indicating why you don't think they should be made. #. Commit those changes to your local repository. #. Push the changes to your fork:: git push origin my-first-pull-request #. Your pull request will be automatically updated. Once your pull request is merged, sync up with the main yt repository by pulling from the ``upstream`` remote:: git checkout main git pull upstream main You might also want to sync your fork of yt on GitHub:: # sync my fork of yt with upstream git push origin main And delete the branch for the merged pull request:: # delete branch for merged pull request git branch -d my-first-pull-request git push origin --delete my-first-pull-request These commands are optional but are nice for keeping your branch list manageable. You can also delete the branch on your fork of yt on GitHub by clicking the "delete branch" button on the page for the merged pull request on GitHub. .. _multiple-PRs: Working with Multiple GitHub Pull Requests ------------------------------------------ Dealing with multiple pull requests on GitHub is straightforward. Development on one feature should be isolated in one named branch, say ``feature_1`` while development of another feature should be in another named branch, say ``feature_2``. A push to remote ``feature_1`` will automatically update any active PR for which ``feature_1`` is a pointer to the ``HEAD`` commit. A push to ``feature_1`` *will not* update any pull requests involving ``feature_2``. .. _code-style-guide: Coding Style Guide ================== Automatically checking and fixing code style -------------------------------------------- We use the `pre-commit `_ framework to validate and automatically fix code styling. It is recommended (though not required) that you install ``pre-commit`` on your machine (see their documentation) and, from the top level of the repo, run .. code-block:: bash $ pre-commit install So that our hooks will run and update your changes on every commit. If you do not want to/are unable to configure ``pre-commit`` on your machine, note that after opening a pull request, it will still be run as a static checker as part of our CI. Some hooks also come with auto-fixing capabilities, which you can trigger manually in a PR by commenting ``pre-commit.ci autofix`` (see ` `_). We use a combination of `black `_, `ruff `_ and `cython-lint `_. See ``.pre-commit-config.yaml`` and ``pyproject.toml`` for the complete configuration details. Note that formatters should not be run directly on the command line as, for instance .. code-block:: bash $ black yt But it can still be done as .. code-block:: bash $ pre-commit run black --all-files The reason is that you may have a specific version of ``black`` installed which can produce different results, while the one that's installed with pre-commit is guaranteed to be in sync with the rest of contributors. Below are a list of additional guidelines for coding in yt, that are not automatically enforced. Source code style guide ----------------------- * In general, follow PEP-8 guidelines. https://www.python.org/dev/peps/pep-0008/ * Classes are ``ConjoinedCapitals``, methods and functions are ``lowercase_with_underscores``. * Do not use nested classes unless you have a very good reason to, such as requiring a namespace or class-definition modification. Classes should live at the top level. ``__metaclass__`` is exempt from this. * Avoid copying memory when possible. * In general, avoid all double-underscore method names: ``__something`` is usually unnecessary. * When writing a subclass, use the super built-in to access the super class, rather than explicitly. Ex: ``super().__init__()`` rather than ``SpecialGrid.__init__()``. * Docstrings should describe input, output, behavior, and any state changes that occur on an object. See :ref:`docstrings` below for a fiducial example of a docstring. * Unless there is a good reason not to (e.g., to avoid circular imports), imports should happen at the top of the file. * If you are comparing with a numpy boolean array, just refer to the array. Ex: do ``np.all(array)`` instead of ``np.all(array == True)``. * Only declare local variables if they will be used later. If you do not use the return value of a function, do not store it in a variable. API Style Guide --------------- * Internally, only import from source files directly -- instead of: ``from yt.visualization.api import ProjectionPlot`` do: ``from yt.visualization.plot_window import ProjectionPlot`` * Import symbols from the module where they are defined, avoid transitive imports. * Import standard library modules, functions, and classes from builtins, do not import them from other yt files. * Numpy is to be imported as ``np``. * Do not use too many keyword arguments. If you have a lot of keyword arguments, then you are doing too much in ``__init__`` and not enough via parameter setting. * Don't create a new class to replicate the functionality of an old class -- replace the old class. Too many options makes for a confusing user experience. * Parameter files external to yt are a last resort. * The usage of the ``**kwargs`` construction should be avoided. If they cannot be avoided, they must be documented, even if they are only to be passed on to a nested function. .. _docstrings: Docstrings ---------- The following is an example docstring. You can use it as a template for docstrings in your code and as a guide for how we expect docstrings to look and the level of detail we are looking for. Note that we use NumPy style docstrings written in `Sphinx restructured text format `_. .. code-block:: rest r"""A one-line summary that does not use variable names or the function name. Several sentences providing an extended description. Refer to variables using back-ticks, e.g. ``var``. Parameters ---------- var1 : array_like Array_like means all those objects -- lists, nested lists, etc. -- that can be converted to an array. We can also refer to variables like ``var1``. var2 : int The type above can either refer to an actual Python type (e.g. ``int``), or describe the type of the variable in more detail, e.g. ``(N,) ndarray`` or ``array_like``. Long_variable_name : {'hi', 'ho'}, optional Choices in brackets, default first when optional. Returns ------- describe : type Explanation output : type Explanation tuple : type Explanation items : type even more explaining Other Parameters ---------------- only_seldom_used_keywords : type Explanation common_parameters_listed_above : type Explanation Raises ------ BadException Because you shouldn't have done that. See Also -------- otherfunc : relationship (optional) newfunc : Relationship (optional), which could be fairly long, in which case the line wraps here. thirdfunc, fourthfunc, fifthfunc Notes ----- Notes about the implementation algorithm (if needed). This can have multiple paragraphs. You may include some math: .. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n} And even use a greek symbol like :math:`omega` inline. References ---------- Cite the relevant literature, e.g. [1]_. You may also cite these references in the notes section above. .. [1] O. McNoleg, "The integration of GIS, remote sensing, expert systems and adaptive co-kriging for environmental habitat modelling of the Highland Haggis using object-oriented, fuzzy-logic and neural-network techniques," Computers & Geosciences, vol. 22, pp. 585-588, 1996. Examples -------- These are written in doctest format, and should illustrate how to use the function. Use the variables 'ds' for the dataset, 'pc' for a plot collection, 'c' for a center, and 'L' for a vector. >>> a = [1, 2, 3] >>> print([x + 3 for x in a]) [4, 5, 6] >>> print("a\n\nb") a b """ Variable Names and Enzo-isms ---------------------------- Avoid Enzo-isms. This includes but is not limited to: * Hard-coding parameter names that are the same as those in Enzo. The following translation table should be of some help. Note that the parameters are now properties on a ``Dataset`` subclass: you access them like ds.refine_by . - ``RefineBy `` => `` refine_by`` - ``TopGridRank `` => `` dimensionality`` - ``TopGridDimensions `` => `` domain_dimensions`` - ``InitialTime `` => `` current_time`` - ``DomainLeftEdge `` => `` domain_left_edge`` - ``DomainRightEdge `` => `` domain_right_edge`` - ``CurrentTimeIdentifier `` => `` unique_identifier`` - ``CosmologyCurrentRedshift `` => `` current_redshift`` - ``ComovingCoordinates `` => `` cosmological_simulation`` - ``CosmologyOmegaMatterNow `` => `` omega_matter`` - ``CosmologyOmegaLambdaNow `` => `` omega_lambda`` - ``CosmologyHubbleConstantNow `` => `` hubble_constant`` * Do not assume that the domain runs from 0 .. 1. This is not true everywhere. * Variable names should be short but descriptive. * No globals! yt-project-yt-f043ac8/COPYING.txt000066400000000000000000000066501510711153200165320ustar00rootroot00000000000000=============================== The yt project licensing terms =============================== yt is licensed under the terms of the Modified BSD License (also known as New or Revised BSD), as follows: Copyright (c) 2013-, yt Development Team Copyright (c) 2006-2013, Matthew Turk All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the yt Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. About the yt Development Team ----------------------------- Matthew Turk began yt in 2006 and remains the project lead. Over time yt has grown to include contributions from a large number of individuals from many diverse institutions, scientific, and technical backgrounds. Until the fall of 2013, yt was licensed under the GPLv3. However, with consent from all developers and on a public mailing list, yt has been relicensed under the BSD 3-clause under a shared copyright model. For more information, see: https://mail.python.org/archives/list/yt-dev@python.org/thread/G4DJDDGB4PSZFJVPWRSHNOSUMTISXC4X/ and https://yt-project.github.io/blog/posts/relicensing/ . All versions of yt prior to this licensing change are available under the GPLv3; all subsequent versions (yt versions >= 2.6.0) are available under the BSD 3-clause license. The yt Development Team is the set of all contributors to the yt project. This includes all of the yt subprojects. The core team that coordinates development on GitHub can be found here: https://github.com/yt-project/yt Our Copyright Policy -------------------- yt uses a shared copyright model. Each contributor maintains copyright over their contributions to yt. But, it is important to note that these contributions are typically only changes to the repositories. Thus, the yt source code, in its entirety is not the copyright of any single person or institution. Instead, it is the collective copyright of the entire yt Development Team. If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when they commit the change to one of the yt repositories. yt-project-yt-f043ac8/CREDITS000066400000000000000000000154611510711153200157010ustar00rootroot00000000000000yt is a group effort. Contributors: Tom Abel (tabel@slac.stanford.edu) Gabriel Altay (gabriel.altay@gmail.com) Kenza Arraki (karraki@nmsu.edu) Kirk Barrow (kssbarrow@gatech.edu) Ricarda Beckmann (ricarda.beckmann@astro.ox.ac.uk) Christoph Behrens (cbehren2@gwdu101.global.gwdg.cluster) Elliott Biondo (biondo@wisc.edu) Alex Bogert (fbogert@ucsc.edu) Josh Borrow (joshua.borrow@durham.ac.uk) Robert Bradshaw (robertwb@gmail.com) André-Patrick Bubel (code@andre-bubel.de) Corentin Cadiou (corentin.cadiou@iap.fr) Pengfei Chen (madcpf@gmail.com) Yi-Hao Chen (ychen@astro.wisc.edu) Yi-Hao Chen (yihaochentw@gmail.com) Salvatore Cielo (cielo@iap.fr) David Collins (dcollins4096@gmail.com) Marianne Corvellec (marianne.corvellec@ens-lyon.org) Jared Coughlin (jcoughl2@nd.edu) Brian Crosby (bcrosby.bd@gmail.com) Weiguang Cui (weiguang.cui@uwa.edu.au) Andrew Cunningham (ajcunn@gmail.com) Bili Dong (qobilidop@gmail.com) Donald E Willcox (eugene.willcox@gmail.com) Nicholas Earl (nchlsearl@gmail.com) Hilary Egan (hilaryye@gmail.com) Daniel Fenn (df11c@my.fsu.edu) John Forbes (jcforbes@ucsc.edu) Enrico Garaldi (egaraldi@uni-bonn.de) Sam Geen (samgeen@gmail.com) Austin Gilbert (agilbert39@gatech.edu) Adam Ginsburg (keflavich@gmail.com) Nick Gnedin (ngnedin@gmail.com) Nathan Goldbaum (ngoldbau@illinois.edu) William Gray (graywilliamj@gmail.com) Philipp Grete (mail@pgrete.de) Max Gronke (max.groenke@gmail.com) Markus Haider (markus.haider@uibk.ac.at) Eric Hallman (hallman13@gmail.com) David Hannasch (David.A.Hannasch@gmail.com) Stephanie Ho (stephaniehkho@gmail.com) Axel Huebl (a.huebl@hzdr.de) Cameron Hummels (chummels@gmail.com) Suoqing Ji (jisuoqing@gmail.com) Allyson Julian (astrohckr@gmail.com) Anni Järvenpää (anni.jarvenpaa@gmail.com) Christian Karch (chiffre@posteo.de) Max Katz (maximilian.katz@stonybrook.edu) BW Keller (kellerbw@mcmaster.ca) Ashley Kelly (a.j.kelly@durham.ac.uk) Chang-Goo Kim (changgoo@princeton.edu) Ji-hoon Kim (me@jihoonkim.org) Steffen Klemer (sklemer@phys.uni-goettingen.de) Fabian Koller (anokfireball@posteo.de) Claire Kopenhafer (clairekope@gmail.com) Kacper Kowalik (xarthisius.kk@gmail.com) Matthew Krafczyk (krafczyk.matthew@gmail.com) Mark Krumholz (mkrumhol@ucsc.edu) Michael Kuhlen (mqk@astro.berkeley.edu) Avik Laha (al3510@moose.cc.columbia.edu) Meagan Lang (langmm.astro@gmail.com) Erwin Lau (ethlau@gmail.com) Doris Lee (dorislee@berkeley.edu) Eve Lee (elee@cita.utoronto.ca) Sam Leitner (sam.leitner@gmail.com) Yuan Li (yuan@astro.columbia.edu) Alex Lindsay (al007@illinois.edu) Yingchao Lu (yingchao.lu@gmail.com) Yinghe Lu (yinghelu@lbl.gov) Chris Malone (chris.m.malone@gmail.com) John McCann (mccann@ucsb.edu) Jonah Miller (jonah.maxwell.miller@gmail.com) Joshua Moloney (joshua.moloney@colorado.edu) Christopher Moody (cemoody@ucsc.edu) Chris Moody (juxtaposicion@gmail.com) Stuart Mumford (stuart@mumford.me.uk) Madicken Munk (madicken.munk@gmail.com) Andrew Myers (atmyers2@gmail.com) Jill Naiman (jnaiman@cfa.harvard.edu) Desika Narayanan (dnarayan@haverford.edu) Kaylea Nelson (kaylea.nelson@yale.edu) Brian O'Shea (oshea@msu.edu) J.S. Oishi (jsoishi@gmail.com) JC Passy (jcpassy@uvic.ca) Hugo Pfister (pfister@loginhz02.iap.fr) David Pérez-Suárez (dps.helio@gmail.com) John Regan (john.regan@helsinki.fi) Mark Richardson (Mark.Richardson.Work@gmail.com) Sherwood Richers (srichers@tapir.caltech.edu) Thomas Robitaille (thomas.robitaille@gmail.com) Anna Rosen (rosen@ucolick.org) Chuck Rozhon (rozhon2@illinois.edu) Douglas Rudd (drudd@uchicago.edu) Rafael Ruggiero (rafael.ruggiero@usp.br) Hsi-Yu Schive (hyschive@gmail.com) Anthony Scopatz (scopatz@gmail.com) Noel Scudder (noel.scudder@stonybrook.edu) Patrick Shriwise (shriwise@wisc.edu) Devin Silvia (devin.silvia@gmail.com) Abhishek Singh (abhisheksing@umass.edu) Sam Skillman (samskillman@gmail.com) Stephen Skory (s@skory.us) Joseph Smidt (josephsmidt@gmail.com) Aaron Smith (asmith@astro.as.utexas.edu) Britton Smith (brittonsmith@gmail.com) Geoffrey So (gsiisg@gmail.com) Josh Soref (jsoref@users.noreply.github.com) Antoine Strugarek (antoine.strugarek@cea.fr) Elizabeth Tasker (tasker@astro1.sci.hokudai.ac.jp) Ben Thompson (bthompson2090@gmail.com) Benjamin Thompson (bthompson2090@gmail.com) Robert Thompson (rthompsonj@gmail.com) Joseph Tomlinson (jmtomlinson95@gmail.com) Stephanie Tonnesen (stonnes@gmail.com) Matthew Turk (matthewturk@gmail.com) Miguel de Val-Borro (miguel.deval@gmail.com) Kausik Venkat (kvenkat2@illinois.edu) Casey W. Stark (caseywstark@gmail.com) Rick Wagner (rwagner@physics.ucsd.edu) Mike Warren (mswarren@gmail.com) Charlie Watson (charlie.watson95@gmail.com) Andrew Wetzel (andrew.wetzel@yale.edu) John Wise (jwise@physics.gatech.edu) Michael Zingale (michael.zingale@stonybrook.edu) John ZuHone (jzuhone@gmail.com) The PasteBin interface code (as well as the PasteBin itself) was written by the Pocoo collective (pocoo.org). developed by Oliver Hahn. Thanks to everyone for all your contributions! yt-project-yt-f043ac8/MANIFEST.in000066400000000000000000000065261510711153200164210ustar00rootroot00000000000000include README* CREDITS CITATION setupext.py CONTRIBUTING.rst include yt/visualization/mapserver/html/map.js include yt/visualization/mapserver/html/map_index.html include yt/visualization/mapserver/html/Leaflet.Coordinates-0.1.5.css include yt/visualization/mapserver/html/Leaflet.Coordinates-0.1.5.src.js include yt/utilities/tests/cosmology_answers.yml include yt/utilities/mesh_types.yaml exclude yt/utilities/lib/cykdtree/c_kdtree.cpp prune tests prune answer-store recursive-include yt *.py *.pyx *.pxi *.pxd README* *.txt *.cu recursive-include doc *.rst *.txt *.py *.ipynb *.png *.jpg *.css *.html recursive-include doc *.h *.c *.sh *.svgz *.pdf *.svg *.pyx # start with excluding all C/C++ files recursive-exclude yt *.h *.c *.hpp *.cpp # then include back every non-generated C/C++ source file # the list can be generated by the following command # git ls-files | grep -E '\.(h|c)(pp)?$' include yt/frontends/artio/artio_headers/artio.c include yt/frontends/artio/artio_headers/artio.h include yt/frontends/artio/artio_headers/artio_endian.c include yt/frontends/artio/artio_headers/artio_endian.h include yt/frontends/artio/artio_headers/artio_file.c include yt/frontends/artio/artio_headers/artio_grid.c include yt/frontends/artio/artio_headers/artio_internal.h include yt/frontends/artio/artio_headers/artio_mpi.c include yt/frontends/artio/artio_headers/artio_mpi.h include yt/frontends/artio/artio_headers/artio_parameter.c include yt/frontends/artio/artio_headers/artio_particle.c include yt/frontends/artio/artio_headers/artio_posix.c include yt/frontends/artio/artio_headers/artio_selector.c include yt/frontends/artio/artio_headers/artio_sfc.c include yt/frontends/artio/artio_headers/cosmology.c include yt/frontends/artio/artio_headers/cosmology.h include yt/geometry/vectorized_ops.h include yt/utilities/lib/_octree_raytracing.hpp include yt/utilities/lib/cykdtree/c_kdtree.cpp include yt/utilities/lib/cykdtree/c_kdtree.hpp include yt/utilities/lib/cykdtree/c_utils.cpp include yt/utilities/lib/cykdtree/c_utils.hpp include yt/utilities/lib/cykdtree/windows/stdint.h include yt/utilities/lib/endian_swap.h include yt/utilities/lib/fixed_interpolator.cpp include yt/utilities/lib/fixed_interpolator.hpp include yt/utilities/lib/marching_cubes.h include yt/utilities/lib/mesh_triangulation.h include yt/utilities/lib/origami_tags.c include yt/utilities/lib/origami_tags.h include yt/utilities/lib/pixelization_constants.cpp include yt/utilities/lib/pixelization_constants.hpp include yt/utilities/lib/platform_dep.h include yt/utilities/lib/platform_dep_math.hpp include yt/utilities/lib/tsearch.c include yt/utilities/lib/tsearch.h include doc/README doc/activate doc/activate.csh doc/cheatsheet.tex exclude doc/cheatsheet.pdf include doc/extensions/README doc/Makefile prune doc/source/reference/api/generated prune doc/build prune .tours recursive-include yt/visualization/volume_rendering/shaders *.fragmentshader *.vertexshader include yt/sample_data_registry.json include conftest.py include yt/py.typed include yt/default.mplstyle prune yt/frontends/_skeleton recursive-include yt/frontends/amrvac *.par recursive-exclude requirements *.txt exclude minimal_requirements.txt exclude .codecov.yml .coveragerc .git-blame-ignore-revs .gitmodules .hgchurn .mailmap exclude .pre-commit-config.yaml clean.sh exclude nose_answer.cfg nose_unit.cfg nose_ignores.txt nose_requirements.txt yt-project-yt-f043ac8/README.md000066400000000000000000000163001510711153200161310ustar00rootroot00000000000000# The yt Project [![PyPI](https://img.shields.io/pypi/v/yt)](https://pypi.org/project/yt) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/yt)](https://pypi.org/project/yt/) [![Latest Documentation](https://img.shields.io/badge/docs-latest-brightgreen.svg)](http://yt-project.org/docs/dev/) [![Users' Mailing List](https://img.shields.io/badge/Users-List-lightgrey.svg)](https://mail.python.org/archives/list/yt-users@python.org//) [![Devel Mailing List](https://img.shields.io/badge/Devel-List-lightgrey.svg)](https://mail.python.org/archives/list/yt-dev@python.org//) [![Data Hub](https://img.shields.io/badge/data-hub-orange.svg)](https://hub.yt/) [![Powered by NumFOCUS](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](http://numfocus.org) [![Sponsor our Project](https://img.shields.io/badge/donate-to%20yt-blueviolet)](https://numfocus.org/donate-to-yt) [![Build and Test](https://github.com/yt-project/yt/actions/workflows/build-test.yaml/badge.svg)](https://github.com/yt-project/yt/actions/workflows/build-test.yaml) [![CI (bleeding edge)](https://github.com/yt-project/yt/actions/workflows/bleeding-edge.yaml/badge.svg)](https://github.com/yt-project/yt/actions/workflows/bleeding-edge.yaml) [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/yt-project/yt/main.svg)](https://results.pre-commit.ci/latest/github/yt-project/yt/main) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/charliermarsh/ruff) yt is an open-source, permissively-licensed Python library for analyzing and visualizing volumetric data. yt supports structured, variable-resolution meshes, unstructured meshes, and discrete or sampled data such as particles. Focused on driving physically-meaningful inquiry, yt has been applied in domains such as astrophysics, seismology, nuclear engineering, molecular dynamics, and oceanography. Composed of a friendly community of users and developers, we want to make it easy to use and develop - we'd love it if you got involved! We've written a [method paper](https://ui.adsabs.harvard.edu/abs/2011ApJS..192....9T) you may be interested in; if you use yt in the preparation of a publication, please consider citing it. ## Code of Conduct yt abides by a code of conduct partially modified from the PSF code of conduct, and is found [in our contributing guide](http://yt-project.org/docs/dev/developing/developing.html#yt-community-code-of-conduct). ## Installation You can install the most recent stable version of yt either with conda from [conda-forge](https://conda-forge.org/): ```shell conda install -c conda-forge yt ``` or with pip: ```shell python -m pip install yt ``` More information on the various ways to install yt, and in particular to install from source, can be found on [the project's website](https://yt-project.org/docs/dev/installing.html). ## Getting Started yt is designed to provide meaningful analysis of data. We have some Quickstart example notebooks in the repository: * [Introduction](https://github.com/yt-project/yt/tree/main/doc/source/quickstart/1\)_Introduction.ipynb) * [Data Inspection](https://github.com/yt-project/yt/tree/main/doc/source/quickstart/2\)_Data_Inspection.ipynb) * [Simple Visualization](https://github.com/yt-project/yt/tree/main/doc/source/quickstart/3\)_Simple_Visualization.ipynb) * [Data Objects and Time Series](https://github.com/yt-project/yt/tree/main/doc/source/quickstart/4\)_Data_Objects_and_Time_Series.ipynb) * [Derived Fields and Profiles](https://github.com/yt-project/yt/tree/main/doc/source/quickstart/5\)_Derived_Fields_and_Profiles.ipynb) * [Volume Rendering](https://github.com/yt-project/yt/tree/main/doc/source/quickstart/6\)_Volume_Rendering.ipynb) If you'd like to try these online, you can visit our [yt Hub](https://hub.yt/) and run a notebook next to some of our example data. ## Contributing We love contributions! yt is open source, built on open source, and we'd love to have you hang out in our community. We have developed some [guidelines](CONTRIBUTING.rst) for contributing to yt. **Imposter syndrome disclaimer**: We want your help. No, really. There may be a little voice inside your head that is telling you that you're not ready to be an open source contributor; that your skills aren't nearly good enough to contribute. What could you possibly offer a project like this one? We assure you - the little voice in your head is wrong. If you can write code at all, you can contribute code to open source. Contributing to open source projects is a fantastic way to advance one's coding skills. Writing perfect code isn't the measure of a good developer (that would disqualify all of us!); it's trying to create something, making mistakes, and learning from those mistakes. That's how we all improve, and we are happy to help others learn. Being an open source contributor doesn't just mean writing code, either. You can help out by writing documentation, tests, or even giving feedback about the project (and yes - that includes giving feedback about the contribution process). Some of these contributions may be the most valuable to the project as a whole, because you're coming to the project with fresh eyes, so you can see the errors and assumptions that seasoned contributors have glossed over. (This disclaimer was originally written by [Adrienne Lowe](https://github.com/adriennefriend) for a [PyCon talk](https://www.youtube.com/watch?v=6Uj746j9Heo), and was adapted by yt based on its use in the README file for the [MetPy project](https://github.com/Unidata/MetPy)) ## Resources We have some community and documentation resources available. * Our latest documentation is always at http://yt-project.org/docs/dev/ and it includes recipes, tutorials, and API documentation * The [discussion mailing list](https://mail.python.org/archives/list/yt-users@python.org//) should be your first stop for general questions * The [development mailing list](https://mail.python.org/archives/list/yt-dev@python.org//) is better suited for more development issues * You can also join us on Slack at yt-project.slack.com ([request an invite](https://yt-project.org/slack.html)) Is your code compatible with yt ? Great ! Please consider giving us a shoutout as a shiny badge in your README - markdown ```markdown [![yt-project](https://img.shields.io/static/v1?label="works%20with"&message="yt"&color="blueviolet")](https://yt-project.org) ``` - rst ```reStructuredText |yt-project| .. |yt-project| image:: https://img.shields.io/static/v1?label="works%20with"&message="yt"&color="blueviolet" :target: https://yt-project.org ``` ## Powered by NumFOCUS yt is a fiscally sponsored project of [NumFOCUS](https://numfocus.org/). If you're interested in supporting the active maintenance and development of this project, consider [donating to the project](https://numfocus.salsalabs.org/donate-to-yt/index.html). yt-project-yt-f043ac8/answer-store/000077500000000000000000000000001510711153200173035ustar00rootroot00000000000000yt-project-yt-f043ac8/clean.sh000077500000000000000000000000521510711153200162700ustar00rootroot00000000000000#!/usr/bin/env bash git clean -f -d -x yt yt-project-yt-f043ac8/conftest.py000066400000000000000000000424021510711153200170530ustar00rootroot00000000000000import os import shutil import sys import tempfile from importlib.metadata import version from importlib.util import find_spec from pathlib import Path import pytest import yaml from packaging.version import Version from yt.config import ytcfg from yt.utilities.answer_testing.testing_utilities import ( _compare_raw_arrays, _hash_results, _save_raw_arrays, _save_result, _streamline_for_io, data_dir_load, ) MPL_VERSION = Version(version("matplotlib")) NUMPY_VERSION = Version(version("numpy")) PILLOW_VERSION = Version(version("Pillow")) # setuptools does not ship with the standard lib starting in Python 3.12, so we need to # be resilient if it's not available at runtime if find_spec("setuptools") is not None: SETUPTOOLS_VERSION = Version(version("setuptools")) else: SETUPTOOLS_VERSION = None if find_spec("pandas") is not None: PANDAS_VERSION = Version(version("pandas")) else: PANDAS_VERSION = None def pytest_addoption(parser): """ Lets options be passed to test functions. """ parser.addoption( "--with-answer-testing", action="store_true", ) parser.addoption( "--answer-store", action="store_true", ) parser.addoption( "--answer-raw-arrays", action="store_true", ) parser.addoption( "--raw-answer-store", action="store_true", ) parser.addoption( "--force-overwrite", action="store_true", ) parser.addoption( "--no-hash", action="store_true", ) parser.addoption("--local-dir", default=None, help="Where answers are saved.") # Tell pytest about the local-dir option in the ini files. This # option is used for creating the answer directory on CI parser.addini( "local-dir", default=str(Path(__file__).parent / "answer-store"), help="answer directory.", ) parser.addini( "test_data_dir", default=ytcfg.get("yt", "test_data_dir"), help="Directory where data for tests is stored.", ) def pytest_configure(config): r""" Reads in the tests/tests.yaml file. This file contains a list of each answer test's answer file (including the changeset number). """ # Register custom marks for answer tests and big data config.addinivalue_line("markers", "answer_test: Run the answer tests.") config.addinivalue_line( "markers", "big_data: Run answer tests that require large data files." ) for value in ( # treat most warnings as errors "error", # >>> warnings emitted by testing frameworks, or in testing contexts # we still have some yield-based tests, awaiting for transition into pytest "ignore::pytest.PytestCollectionWarning", # matplotlib warnings related to the Agg backend which is used in CI, not much we can do about it "ignore:Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.:UserWarning", r"ignore:tight_layout.+falling back to Agg renderer:UserWarning", # # >>> warnings from wrong values passed to numpy # these should normally be curated out of the test suite but they are too numerous # to deal with in a reasonable time at the moment. "ignore:invalid value encountered in log10:RuntimeWarning", "ignore:divide by zero encountered in log10:RuntimeWarning", # # >>> there are many places in yt (most notably at the frontend level) # where we open files but never explicitly close them # Although this is in general bad practice, it can be intentional and # justified in contexts where reading speeds should be optimized. # It is not clear at the time of writing how to approach this, # so I'm going to ignore this class of warnings altogether for now. "ignore:unclosed file.*:ResourceWarning", ): config.addinivalue_line("filterwarnings", value) if SETUPTOOLS_VERSION is not None and SETUPTOOLS_VERSION >= Version("67.3.0"): # may be triggered by multiple dependencies # see https://github.com/glue-viz/glue/issues/2364 # see https://github.com/matplotlib/matplotlib/issues/25244 config.addinivalue_line( "filterwarnings", r"ignore:(Deprecated call to `pkg_resources\.declare_namespace\('.*'\)`\.\n)?" r"Implementing implicit namespace packages \(as specified in PEP 420\) " r"is preferred to `pkg_resources\.declare_namespace`\.:DeprecationWarning", ) if SETUPTOOLS_VERSION is not None and SETUPTOOLS_VERSION >= Version("67.5.0"): # may be triggered by multiple dependencies # see https://github.com/glue-viz/glue/issues/2364 # see https://github.com/matplotlib/matplotlib/issues/25244 config.addinivalue_line( "filterwarnings", "ignore:pkg_resources is deprecated as an API:DeprecationWarning", ) if MPL_VERSION < Version("3.5.2") and PILLOW_VERSION >= Version("9.1"): # see https://github.com/matplotlib/matplotlib/pull/22766 config.addinivalue_line( "filterwarnings", r"ignore:NONE is deprecated and will be removed in Pillow 10 \(2023-07-01\)\. " r"Use Resampling\.NEAREST or Dither\.NONE instead\.:DeprecationWarning", ) config.addinivalue_line( "filterwarnings", r"ignore:ADAPTIVE is deprecated and will be removed in Pillow 10 \(2023-07-01\)\. " r"Use Palette\.ADAPTIVE instead\.:DeprecationWarning", ) if NUMPY_VERSION >= Version("1.25"): if find_spec("h5py") is not None and ( Version(version("h5py")) < Version("3.9") ): # https://github.com/h5py/h5py/pull/2242 config.addinivalue_line( "filterwarnings", "ignore:`product` is deprecated as of NumPy 1.25.0" ":DeprecationWarning", ) if PILLOW_VERSION >= Version("11.3.0") and MPL_VERSION <= Version("3.10.3"): # patched upstream: https://github.com/matplotlib/matplotlib/pull/30221 config.addinivalue_line( "filterwarnings", r"ignore:'mode' parameter is deprecated:DeprecationWarning", ) if PANDAS_VERSION is not None and PANDAS_VERSION >= Version("2.2.0"): config.addinivalue_line( "filterwarnings", r"ignore:\s*Pyarrow will become a required dependency of pandas:DeprecationWarning", ) if sys.version_info >= (3, 12): # already patched (but not released) upstream: # https://github.com/dateutil/dateutil/pull/1285 config.addinivalue_line( "filterwarnings", r"ignore:datetime\.datetime\.utcfromtimestamp\(\) is deprecated:DeprecationWarning", ) if find_spec("ratarmount"): # On Python 3.12+, there is a deprecation warning when calling os.fork() # in a multi-threaded process. We use this mechanism to mount archives. config.addinivalue_line( "filterwarnings", r"ignore:This process \(pid=\d+\) is multi-threaded, use of fork\(\) " r"may lead to deadlocks in the child\." ":DeprecationWarning", ) if find_spec("datatree"): # the cf_radial dependency arm-pyart<=1.9.2 installs the now deprecated # xarray-datatree package (which imports as datatree), which triggers # a bunch of runtimewarnings when importing xarray. # https://github.com/yt-project/yt/pull/5042#issuecomment-2457797694 config.addinivalue_line( "filterwarnings", "ignore:" r"Engine.*loading failed.*" ":RuntimeWarning", ) def pytest_collection_modifyitems(config, items): r""" Decide which tests to skip based on command-line options. """ # Set up the skip marks skip_answer = pytest.mark.skip(reason="--with-answer-testing not set.") skip_unit = pytest.mark.skip(reason="Running answer tests, so skipping unit tests.") skip_big = pytest.mark.skip(reason="--answer-big-data not set.") # Loop over every collected test function for item in items: # If it's an answer test and the appropriate CL option hasn't # been set, skip it if "answer_test" in item.keywords and not config.getoption( "--with-answer-testing" ): item.add_marker(skip_answer) # If it's an answer test that requires big data and the CL # option hasn't been set, skip it if ( "big_data" in item.keywords and not config.getoption("--with-answer-testing") and not config.getoption("--answer-big-data") ): item.add_marker(skip_big) if "answer_test" not in item.keywords and config.getoption( "--with-answer-testing" ): item.add_marker(skip_unit) def pytest_itemcollected(item): # Customize pytest-mpl decorator to add sensible defaults mpl_marker = item.get_closest_marker("mpl_image_compare") if mpl_marker is not None: # in a future version, pytest-mpl may gain an option for doing this: # https://github.com/matplotlib/pytest-mpl/pull/181 mpl_marker.kwargs.setdefault("tolerance", 0.5) def _param_list(request): r""" Saves the non-ds, non-fixture function arguments for saving to the answer file. """ # pytest treats parameterized arguments as fixtures, so there's no # clean way to separate them out from other other fixtures (that I # know of), so we do it explicitly blacklist = [ "hashing", "answer_file", "request", "answer_compare", "temp_dir", "orbit_traj", "etc_traj", ] test_params = {} for key, val in request.node.funcargs.items(): if key not in blacklist: # For plotwindow, the callback arg is a tuple and the second # element contains a memory address, so we need to drop it. # The first element is the callback name, which is all that's # needed if key == "callback": val = val[0] test_params[key] = str(val) # Convert python-specific data objects (such as tuples) to a more # io-friendly format (in order to not have python-specific anchors # in the answer yaml file) test_params = _streamline_for_io(test_params) return test_params def _get_answer_files(request): """ Gets the path to where the hashed and raw answers are saved. """ answer_file = f"{request.cls.__name__}_{request.cls.answer_version}.yaml" raw_answer_file = f"{request.cls.__name__}_{request.cls.answer_version}.h5" # Add the local-dir aspect of the path. If there's a command line value, # have that override the ini file value clLocalDir = request.config.getoption("--local-dir") iniLocalDir = request.config.getini("local-dir") if clLocalDir is not None: answer_file = os.path.join(os.path.expanduser(clLocalDir), answer_file) raw_answer_file = os.path.join(os.path.expanduser(clLocalDir), raw_answer_file) else: answer_file = os.path.join(os.path.expanduser(iniLocalDir), answer_file) raw_answer_file = os.path.join(os.path.expanduser(iniLocalDir), raw_answer_file) # Make sure we don't overwrite unless we mean to overwrite = request.config.getoption("--force-overwrite") storing = request.config.getoption("--answer-store") raw_storing = request.config.getoption("--raw-answer-store") raw = request.config.getoption("--answer-raw-arrays") if os.path.exists(answer_file) and storing and not overwrite: raise FileExistsError( "Use `--force-overwrite` to overwrite an existing answer file." ) if os.path.exists(raw_answer_file) and raw_storing and raw and not overwrite: raise FileExistsError( "Use `--force-overwrite` to overwrite an existing raw answer file." ) # If we do mean to overwrite, do so here by deleting the original file if os.path.exists(answer_file) and storing and overwrite: os.remove(answer_file) if os.path.exists(raw_answer_file) and raw_storing and raw and overwrite: os.remove(raw_answer_file) print(os.path.abspath(answer_file)) return answer_file, raw_answer_file @pytest.fixture(scope="function") def hashing(request): r""" Handles initialization, generation, and saving of answer test result hashes. """ no_hash = request.config.getoption("--no-hash") store_hash = request.config.getoption("--answer-store") raw = request.config.getoption("--answer-raw-arrays") raw_store = request.config.getoption("--raw-answer-store") # This check is so that, when checking if the answer file exists in # _get_answer_files, we don't continuously fail. With this check, # _get_answer_files is called once per class, despite this having function # scope if request.cls.answer_file is None: request.cls.answer_file, request.cls.raw_answer_file = _get_answer_files( request ) if not no_hash and not store_hash and request.cls.saved_hashes is None: try: with open(request.cls.answer_file) as fd: request.cls.saved_hashes = yaml.safe_load(fd) except FileNotFoundError: module_filename = f"{request.function.__module__.replace('.', os.sep)}.py" with open(f"generate_test_{os.getpid()}.txt", "a") as fp: fp.write(f"{module_filename}::{request.cls.__name__}\n") pytest.fail(msg="Answer file not found.", pytrace=False) request.cls.hashes = {} # Load the saved answers if we're comparing. We don't do this for the raw # answers because those are huge yield # Get arguments and their values passed to the test (e.g., axis, field, etc.) params = _param_list(request) # Hash the test results. Don't save to request.cls.hashes so we still have # raw data, in case we want to work with that hashes = _hash_results(request.cls.hashes) # Add the other test parameters hashes.update(params) # Add the function name as the "master" key to the hashes dict hashes = {request.node.name: hashes} # Save hashes if not no_hash and store_hash: _save_result(hashes, request.cls.answer_file) # Compare hashes elif not no_hash and not store_hash: try: for test_name, test_hash in hashes.items(): assert test_name in request.cls.saved_hashes assert test_hash == request.cls.saved_hashes[test_name] except AssertionError: pytest.fail(f"Comparison failure: {request.node.name}", pytrace=False) # Save raw data if raw and raw_store: _save_raw_arrays( request.cls.hashes, request.cls.raw_answer_file, request.node.name ) # Compare raw data. This is done one test at a time because the # arrays can get quite large and storing everything in memory would # be bad if raw and not raw_store: _compare_raw_arrays( request.cls.hashes, request.cls.raw_answer_file, request.node.name ) @pytest.fixture(scope="function") def temp_dir(): r""" Creates a temporary directory needed by certain tests. """ curdir = os.getcwd() if int(os.environ.get("GENERATE_YTDATA", 0)): tmpdir = os.getcwd() else: tmpdir = tempfile.mkdtemp() os.chdir(tmpdir) yield tmpdir os.chdir(curdir) if tmpdir != curdir: shutil.rmtree(tmpdir) @pytest.fixture(scope="class") def ds(request): # data_dir_load can take the cls, args, and kwargs. These optional # arguments, if present, are given in a dictionary as the second # element of the list if isinstance(request.param, str): ds_fn = request.param opts = {} else: ds_fn, opts = request.param try: return data_dir_load( ds_fn, cls=opts.get("cls"), args=opts.get("args"), kwargs=opts.get("kwargs") ) except FileNotFoundError: return pytest.skip(f"Data file: `{request.param}` not found.") @pytest.fixture(scope="class") def field(request): """ Fixture for returning the field. Needed because indirect=True is used for loading the datasets. """ return request.param @pytest.fixture(scope="class") def dobj(request): """ Fixture for returning the ds_obj. Needed because indirect=True is used for loading the datasets. """ return request.param @pytest.fixture(scope="class") def axis(request): """ Fixture for returning the axis. Needed because indirect=True is used for loading the datasets. """ return request.param @pytest.fixture(scope="class") def weight(request): """ Fixture for returning the weight_field. Needed because indirect=True is used for loading the datasets. """ return request.param @pytest.fixture(scope="class") def ds_repr(request): """ Fixture for returning the string representation of a dataset. Needed because indirect=True is used for loading the datasets. """ return request.param @pytest.fixture(scope="class") def Npart(request): """ Fixture for returning the number of particles in a dataset. Needed because indirect=True is used for loading the datasets. """ return request.param yt-project-yt-f043ac8/doc/000077500000000000000000000000001510711153200154175ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/Makefile000066400000000000000000000120471510711153200170630ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " clean to remove the build directory" @echo " recipeclean to remove files produced by running the cookbook scripts" clean: -rm -rf $(BUILDDIR)/* -rm -rf source/reference/api/yt.* -rm -rf source/reference/api/modules.rst fullclean: clean recipeclean: -rm -rf _temp/*.done source/cookbook/_static/* html: ifneq ($(READTHEDOCS),True) SPHINX_APIDOC_OPTIONS=members,undoc-members,inherited-members,show-inheritance sphinx-apidoc \ -o source/reference/api/ \ -e ../yt $(shell find ../yt -name "*tests*" -type d) ../yt/utilities/voropp* ../yt/analysis_modules/* endif $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/yt.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/yt.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/yt" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/yt" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." yt-project-yt-f043ac8/doc/README000066400000000000000000000005601510711153200163000ustar00rootroot00000000000000This directory contains the uncompiled yt documentation. It's written to be used with Sphinx, a tool designed for writing Python documentation. Sphinx is available at this URL: http://www.sphinx-doc.org/en/master/ Because the documentation requires a number of dependencies, we provide pre-built versions online, accessible here: https://yt-project.org/docs/dev/ yt-project-yt-f043ac8/doc/activate000066400000000000000000000057121510711153200171470ustar00rootroot00000000000000### Adapted from virtualenv's activate script # This file must be used with "source bin/activate" *from bash* # you cannot run it directly deactivate () { # reset old environment variables if [ -n "$_OLD_VIRTUAL_PATH" ] ; then PATH="$_OLD_VIRTUAL_PATH" export PATH unset _OLD_VIRTUAL_PATH fi if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" export PYTHONHOME unset _OLD_VIRTUAL_PYTHONHOME fi ### Begin extra yt vars if [ -n "$_OLD_VIRTUAL_YT_DEST" ] ; then YT_DEST="$_OLD_VIRTUAL_YT_DEST" export YT_DEST unset _OLD_VIRTUAL_PYTHONHOME fi if [ -n "$_OLD_VIRTUAL_PYTHONPATH" ] ; then PYTHONPATH="$_OLD_VIRTUAL_PYTHONPATH" export PYTHONPATH unset _OLD_VIRTUAL_PYTHONPATH fi if [ -n "$_OLD_VIRTUAL_LD_LIBRARY_PATH" ] ; then LD_LIBRARY_PATH="$_OLD_VIRTUAL_LD_LIBRARY_PATH" export LD_LIBRARY_PATH unset _OLD_VIRTUAL_LD_LIBRARY_PATH fi ### End extra yt vars # This should detect bash and zsh, which have a hash command that must # be called to get it to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then hash -r fi if [ -n "$_OLD_VIRTUAL_PS1" ] ; then PS1="$_OLD_VIRTUAL_PS1" export PS1 unset _OLD_VIRTUAL_PS1 fi unset VIRTUAL_ENV if [ ! "$1" = "nondestructive" ] ; then # Self destruct! unset -f deactivate fi } # unset irrelevant variables deactivate nondestructive VIRTUAL_ENV="__YT_DIR__" export VIRTUAL_ENV _OLD_VIRTUAL_PATH="$PATH" PATH="$VIRTUAL_ENV/bin:$PATH" export PATH ### Begin extra env vars for yt _OLD_VIRTUAL_YT_DEST="$YT_DEST" YT_DEST="$VIRTUAL_ENV" export YT_DEST _OLD_VIRTUAL_PYTHONPATH="$PYTHONPATH" _OLD_VIRTUAL_LD_LIBRARY_PATH="$LD_LIBRARY_PATH" LD_LIBRARY_PATH="$VIRTUAL_ENV/lib:$LD_LIBRARY_PATH" export LD_LIBRARY_PATH ### End extra env vars for yt # unset PYTHONHOME if set # this will fail if PYTHONHOME is set to the empty string (which is bad anyway) # could use `if (set -u; : $PYTHONHOME) ;` in bash if [ -n "$PYTHONHOME" ] ; then _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" unset PYTHONHOME fi if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then _OLD_VIRTUAL_PS1="$PS1" if [ "x" != x ] ; then PS1="$PS1" else if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then # special case for Aspen magic directories # see http://www.zetadev.com/software/aspen/ PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" else PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" fi fi export PS1 fi # This should detect bash and zsh, which have a hash command that must # be called to get it to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then hash -r fi yt-project-yt-f043ac8/doc/activate.csh000066400000000000000000000036541510711153200177260ustar00rootroot00000000000000# This file must be used with "source bin/activate.csh" *from csh*. # You cannot run it directly. # Created by Davide Di Blasi . alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; test $?_OLD_VIRTUAL_YT_DEST != 0 && setenv YT_DEST "$_OLD_VIRTUAL_YT_DEST" && unset _OLD_VIRTUAL_YT_DEST; test $?_OLD_VIRTUAL_PYTHONPATH != 0 && setenv PYTHONPATH "$_OLD_VIRTUAL_PYTHONPATH" && unset _OLD_VIRTUAL_PYTHONPATH; test $?_OLD_VIRTUAL_LD_LIBRARY_PATH != 0 && setenv LD_LIBRARY_PATH "$_OLD_VIRTUAL_LD_LIBRARY_PATH" && unset _OLD_VIRTUAL_LD_LIBRARY_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' # Unset irrelevant variables. deactivate nondestructive setenv VIRTUAL_ENV "__YT_DIR__" if ($?PATH == 0) then setenv PATH endif set _OLD_VIRTUAL_PATH="$PATH" setenv PATH "${VIRTUAL_ENV}/bin:${PATH}" ### Begin extra yt vars if ($?YT_DEST == 0) then setenv YT_DEST endif set _OLD_VIRTUAL_YT_DEST="$YT_DEST" setenv YT_DEST "${VIRTUAL_ENV}" if ($?PYTHONPATH == 0) then setenv PYTHONPATH endif set _OLD_VIRTUAL_PYTHONPATH="$PYTHONPATH" setenv PYTHONPATH "${VIRTUAL_ENV}/lib/python2.7/site-packages:${PYTHONPATH}" if ($?LD_LIBRARY_PATH == 0) then setenv LD_LIBRARY_PATH endif set _OLD_VIRTUAL_LD_LIBRARY_PATH="$LD_LIBRARY_PATH" setenv LD_LIBRARY_PATH "${VIRTUAL_ENV}/lib:${LD_LIBRARY_PATH}" ### End extra yt vars set _OLD_VIRTUAL_PROMPT="$prompt" if ("" != "") then set env_name = "" else if (`basename "$VIRTUAL_ENV"` == "__") then # special case for Aspen magic directories # see http://www.zetadev.com/software/aspen/ set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` else set env_name = `basename "$VIRTUAL_ENV"` endif endif set prompt = "[$env_name] $prompt" unset env_name rehash yt-project-yt-f043ac8/doc/cheatsheet.tex000066400000000000000000000464061510711153200202700ustar00rootroot00000000000000\documentclass[10pt,landscape]{article} \usepackage{multicol} \usepackage{calc} \usepackage{ifthen} \usepackage[landscape]{geometry} \usepackage[hyphens]{url} % To make this come out properly in landscape mode, do one of the following % 1. % pdflatex cheatsheet.tex % % 2. % latex cheatsheet.tex % dvips -P pdf -t landscape cheatsheet.dvi % ps2pdf cheatsheet.ps % If you're reading this, be prepared for confusion. Making this was % a learning experience for me, and it shows. Much of the placement % was hacked in; if you make it better, let me know... % 2008-04 % Changed page margin code to use the geometry package. Also added code for % conditional page margins, depending on paper size. Thanks to Uwe Ziegenhagen % for the suggestions. % 2006-08 % Made changes based on suggestions from Gene Cooperman. % 2012-11 - Stephen Skory % Converted the latex cheat sheet to a yt cheat sheet, taken from % http://www.stdout.org/~winston/latex/ % This sets page margins to .5 inch if using letter paper, and to 1cm % if using A4 paper. (This probably isn't strictly necessary.) % If using another size paper, use default 1cm margins. \ifthenelse{\lengthtest { \paperwidth = 11in}} { \geometry{top=.5in,left=.5in,right=.5in,bottom=0.85in} } {\ifthenelse{ \lengthtest{ \paperwidth = 297mm}} {\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} } {\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} } } % Turn off header and footer \pagestyle{empty} % Redefine section commands to use less space \makeatletter \renewcommand{\section}{\@startsection{section}{1}{0mm}% {-1ex plus -.5ex minus -.2ex}% {0.5ex plus .2ex}%x {\normalfont\large\bfseries}} \renewcommand{\subsection}{\@startsection{subsection}{2}{0mm}% {-1explus -.5ex minus -.2ex}% {0.5ex plus .2ex}% {\normalfont\normalsize\bfseries}} \renewcommand{\subsubsection}{\@startsection{subsubsection}{3}{0mm}% {-1ex plus -.5ex minus -.2ex}% {1ex plus .2ex}% {\normalfont\small\bfseries}} \makeatother % Define BibTeX command \def\BibTeX{{\rm B\kern-.05em{\sc i\kern-.025em b}\kern-.08em T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}} % Don't print section numbers \setcounter{secnumdepth}{0} \setlength{\parindent}{0pt} \setlength{\parskip}{0pt plus 0.5ex} % ----------------------------------------------------------------------- \begin{document} \raggedright \fontsize{3mm}{3mm}\selectfont \begin{multicols}{3} % multicol parameters % These lengths are set only within the two main columns %\setlength{\columnseprule}{0.25pt} \setlength{\premulticols}{1pt} \setlength{\postmulticols}{1pt} \setlength{\multicolsep}{1pt} \setlength{\columnsep}{2pt} \begin{center} \Large{\textbf{yt Cheat Sheet}} \\ \end{center} \subsection{General Info} For everything yt please see \url{http://yt-project.org}. Documentation \url{http://yt-project.org/doc/index.html}. Need help? Start here \url{http://yt-project.org/doc/help/} and then try the IRC chat room \url{http://yt-project.org/irc.html}, or the mailing list \url{https://mail.python.org/archives/list/yt-users@python.org/}. \\ \subsection{Installing yt} The easiest way to install yt is to use the installation script found on the yt homepage or the docs linked above. If you already have python set up with \texttt{numpy}, \texttt{scipy}, \texttt{matplotlib}, \texttt{h5py}, and \texttt{cython}, you can also use \texttt{pip install yt} \subsection{Command Line yt} yt, and its convenience functions, are launched from a command line prompt. Many commands have flags to control behavior. Commands can be followed by {\bf {-}{-}help} (e.g. {\bf yt render {-}{-}help}) for detailed help for that command including a list of the available flags. \texttt{yt load} \textit{dataset} \textemdash\ Load a single dataset. \\ \texttt{yt help} \textemdash\ Print yt help information. \\ \texttt{yt stats} \textit{dataset} \textemdash\ Print stats of a dataset. \\ \texttt{yt update} \textemdash\ Update yt to most recent version.\\ \texttt{yt update --all} \textemdash\ Update yt and dependencies to most recent version. \\ \texttt{yt version} \textemdash\ yt installation information. \\ \texttt{yt upload\_image} \textit{image.png} \textemdash\ Upload PNG image to imgur.com. \\ \texttt{yt upload\_notebook} \textit{notebook.nb} \textemdash\ Upload IPython notebook to \url{https://girder.hub.yt}.\\ \texttt{yt plot} \textit{dataset} \textemdash\ Create a set of images.\\ \texttt{yt render} \textit{dataset} \textemdash\ Create a simple volume rendering. \\ \texttt{yt mapserver} \textit{dataset} \textemdash\ View a plot/projection in a Gmaps-like interface. \\ \texttt{yt pastebin} \textit{text.out} \textemdash\ Post text to the pastebin at paste.yt-project.org. \\ \texttt{yt pastebin\_grab} \textit{identifier} \textemdash\ Print content of pastebin to STDOUT. \\ \texttt{yt bugreport} \textemdash\ Report a yt bug. \\ \texttt{yt hop} \textit{dataset} \textemdash\ Run hop on a dataset. \\ \subsection{yt Imports} In order to use yt, Python must load the relevant yt modules into memory. The import commands are entered in the Python/IPython shell or used as part of a script. \newlength{\MyLen} \settowidth{\MyLen}{\texttt{letterpaper}/\texttt{a4paper} \ } \texttt{import yt} \textemdash\ Load yt. \\ \texttt{from yt.config import ytcfg} \textemdash\ Used to set yt configuration options. If used, must be called before importing any other module.\\ \texttt{from yt.analysis\_modules.\emph{halo\_finding}.api import \textasteriskcentered} \textemdash\ Load halo finding modules. Other modules are loaded in a similar way by swapping the \emph{emphasized} text. See the \textbf{Analysis Modules} section for a listing and short descriptions of each. \subsection{YTArray} Simulation data in yt is returned as a YTArray. YTArray is a numpy array that has unit data attached to it and can automatically handle unit conversions and detect unit errors. Just like a numpy array, YTArray provides a wealth of built-in functions to calculate properties of the data in the array. Here is a very brief list of some useful ones. \settowidth{\MyLen}{\texttt{multicol} }\\ \texttt{v = a.in\_cgs()} \textemdash\ Return the array in CGS units \\ \texttt{v = a.in\_units('Msun/pc**3')} \textemdash\ Return the array in solar masses per cubic parsec \\ \texttt{v = a.max(), a.min()} \textemdash\ Return maximum, minimum of \texttt{a}. \\ \texttt{index = a.argmax(), a.argmin()} \textemdash\ Return index of max, min value of \texttt{a}.\\ \texttt{v = a[}\textit{index}\texttt{]} \textemdash\ Select a single value from \texttt{a} at location \textit{index}.\\ \texttt{b = a[}\textit{i:j}\texttt{]} \textemdash\ Select the slice of values from \texttt{a} between locations \textit{i} to \textit{j-1} saved to a new Numpy array \texttt{b} with length \textit{j-i}. \\ \texttt{sel = (a > const)} \textemdash\ Create a new boolean Numpy array \texttt{sel}, of the same shape as \texttt{a}, that marks which values of \texttt{a > const}. Other operators (e.g. \textless, !=, \%) work as well.\\ \texttt{b = a[sel]} \textemdash\ Create a new Numpy array \texttt{b} made up of elements from \texttt{a} that correspond to elements of \texttt{sel} that are \textit{True}. In the above example \texttt{b} would be all elements of \texttt{a} that are greater than \texttt{const}.\\ \texttt{a.write\_hdf5(\textit{filename.h5})} \textemdash\ Save \texttt{a} to the hdf5 file \textit{filename.h5}.\\ \subsection{IPython Tips} \settowidth{\MyLen}{\texttt{multicol} } These tips work if IPython has been loaded, typically either by invoking \texttt{yt load} on the command line. \texttt{Tab complete} \textemdash\ IPython will attempt to auto-complete a variable or function name when the \texttt{Tab} key is pressed, e.g. \textit{HaloFi}\textendash\texttt{Tab} would auto-complete to \textit{HaloFinder}. This also works with imports, e.g. \textit{from numpy.random.}\textendash\texttt{Tab} would give you a list of random functions (note the trailing period before hitting \texttt{Tab}).\\ \texttt{?, ??} \textemdash\ Appending one or two question marks at the end of any object gives you detailed information about it, e.g. \textit{variable\_name}?.\\ Below a few IPython ``magics'' are listed, which are IPython-specific shortcut commands.\\ \texttt{\%paste} \textemdash\ Paste content from the system clipboard into the IPython shell.\\ \texttt{\%hist} \textemdash\ Print recent command history.\\ \texttt{\%quickref} \textemdash\ Print IPython quick reference.\\ \texttt{\%pdb} \textemdash\ Automatically enter the Python debugger at an exception.\\ \texttt{\%debug} \textemdash\ Drop into a debugger at the location of the last unhandled exception. \\ \texttt{\%time, \%timeit} \textemdash\ Find running time of expressions for benchmarking.\\ \texttt{\%lsmagic} \textemdash\ List all available IPython magics. Hint: \texttt{?} works with magics.\\ Please see \url{http://ipython.org/documentation.html} for the full IPython documentation. \subsection{Load and Access Data} The first step in using yt is to reference a simulation snapshot. After that, simulation data is generally accessed in yt using \textit{Data Containers} which are Python objects that define a region of simulation space from which data should be selected. \settowidth{\MyLen}{\texttt{multicol} } \texttt{ds = yt.load(}\textit{dataset}\texttt{)} \textemdash\ Reference a single snapshot.\\ \texttt{dd = ds.all\_data()} \textemdash\ Select the entire volume.\\ \texttt{a = dd[}\textit{field\_name}\texttt{]} \textemdash\ Copies the contents of \textit{field} into the YTArray \texttt{a}. Similarly for other data containers.\\ \texttt{ds.field\_list} \textemdash\ A list of available fields in the snapshot. \\ \texttt{ds.derived\_field\_list} \textemdash\ A list of available derived fields in the snapshot. \\ \texttt{val, loc = ds.find\_max("Density")} \textemdash\ Find the \texttt{val}ue of the maximum of the field \texttt{Density} and its \texttt{loc}ation. \\ \texttt{sp = ds.sphere(}\textit{cen}\texttt{,}\textit{radius}\texttt{)} \textemdash\ Create a spherical data container. \textit{cen} may be a coordinate, or ``max'' which centers on the max density point. \textit{radius} may be a float in code units or a tuple of (\textit{length, unit}).\\ \texttt{re = ds.region(\textit{cen}, \textit{left edge}, \textit{right edge})} \textemdash\ Create a rectilinear data container. \textit{cen} is required but not used. \textit{left} and \textit{right edge} are coordinate values that define the region. \texttt{di = ds.disk(\textit{cen}, \textit{normal}, \textit{radius}, \textit{height})} \textemdash\ Create a cylindrical data container centered at \textit{cen} along the direction set by \textit{normal},with total length 2$\times$\textit{height} and with radius \textit{radius}. \\ \texttt{ds.save\_object(sp, \textit{``sp\_for\_later''})} \textemdash\ Save an object (\texttt{sp}) for later use.\\ \texttt{sp = ds.load\_object(\textit{``sp\_for\_later''})} \textemdash\ Recover a saved object.\\ \subsection{Defining New Fields} \texttt{yt} expects on-disk fields, fields generated on-demand and in-memory. Field can either be created before a dataset is loaded using \texttt{add\_field}: \texttt{def \_metal\_mass(\textit{field},\textit{data})}\\ \texttt{\hspace{4 mm} return data["metallicity"]*data["cell\_mass"]}\\ \texttt{add\_field("metal\_mass", units='g', function=\_metal\_mass)}\\ Or added to an existing dataset using \texttt{ds.add\_field}: \texttt{ds.add\_field("metal\_mass", units='g', function=\_metal\_mass)}\\ \subsection{Slices and Projections} \settowidth{\MyLen}{\texttt{multicol} } \texttt{slc = yt.SlicePlot(ds, \textit{axis or normal vector}, \textit{fields}, \textit{center=}, \textit{width=}, \textit{weight\_field=}, \textit{additional parameters})} \textemdash\ Make a slice plot perpendicular to \textit{axis} (specified via 'x', 'y', or 'z') or a normal vector for an off-axis slice of \textit{fields} weighted by \textit{weight\_field} at (code-units) \textit{center} with \textit{width} in code units or a (value, unit) tuple. Hint: try \textit{yt.SlicePlot?} in IPython to see additional parameters.\\ \texttt{slc.save(\textit{file\_prefix})} \textemdash\ Save the slice to a png with name prefix \textit{file\_prefix}. \texttt{.save()} works similarly for the commands below.\\ \texttt{prj = yt.ProjectionPlot(ds, \textit{axis or normal vector}, \textit{fields}, \textit{additional params})} \textemdash\ Same as \texttt{yt.SlicePlot} but for projections.\\ \subsection{Plot Annotations} \settowidth{\MyLen}{\texttt{multicol} } Plot callbacks are functions itemized in a registry that is attached to every plot object. They can be accessed and then called like \texttt{ prj.annotate\_velocity(factor=16, normalize=False)}. Most callbacks also accept a \textit{plot\_args} dict that is fed to matplotlib annotator. \\ \texttt{velocity(\textit{factor=},\textit{scale=},\textit{scale\_units=}, \textit{normalize=})} \textemdash\ Uses field "x-velocity" to draw quivers\\ \texttt{magnetic\_field(\textit{factor=},\textit{scale=},\textit{scale\_units=}, \textit{normalize=})} \textemdash\ Uses field "Bx" to draw quivers\\ \texttt{quiver(\textit{field\_x},\textit{field\_y},\textit{factor=},\textit{scale=},\textit{scale\_units=}, \textit{normalize=})} \\ \texttt{contour(\textit{field=},\textit{levels=},\textit{factor=},\textit{clim=},\textit{take\_log=}, \textit{additional parameters})} \textemdash Plots a number of contours \textit{ncont} to interpolate \textit{field} optionally using \textit{take\_log}, upper and lower \textit{c}ontour\textit{lim}its and \textit{factor} number of points in the interpolation.\\ \texttt{grids(\textit{alpha=}, \textit{draw\_ids=}, \textit{periodic=}, \textit{min\_level=}, \textit{max\_level=})} \textemdash Add grid boundaries. \\ \texttt{streamlines(\textit{field\_x},\textit{field\_y},\textit{factor=},\textit{density=})}\\ \texttt{clumps(\textit{clumplist})} \textemdash\ Generate \textit{clumplist} using the clump finder and plot. \\ \texttt{arrow(\textit{pos}, \textit{code\_size})} Add an arrow at a \textit{pos}ition. \\ \texttt{point(\textit{pos}, \textit{text})} \textemdash\ Add text at a \textit{pos}ition. \\ \texttt{marker(\textit{pos}, \textit{marker=})} \textemdash\ Add a matplotlib-defined marker at a \textit{pos}ition. \\ \texttt{sphere(\textit{center}, \textit{radius}, \textit{text=})} \textemdash\ Draw a circle and append \textit{text}.\\ \texttt{hop\_circles(\textit{hop\_output}, \textit{max\_number=}, \textit{annotate=}, \textit{min\_size=}, \textit{max\_size=}, \textit{font\_size=}, \textit{print\_halo\_size=}, \textit{fixed\_radius=}, \textit{min\_mass=}, \textit{print\_halo\_mass=}, \textit{width=})} \textemdash\ Draw a halo, printing it's ID, mass, clipping halos depending on number of particles (\textit{size}) and optionally fixing the drawn circle radius to be constant for all halos.\\ \texttt{hop\_particles(\textit{hop\_output},\textit{max\_number=},\textit{p\_size=},\\ \textit{min\_size},\textit{alpha=})} \textemdash\ Draw particle positions for member halos with a certain number of pixels per particle.\\ \texttt{particles(\textit{width},\textit{p\_size=},\textit{col=}, \textit{marker=}, \textit{stride=}, \textit{ptype=}, \textit{stars\_only=}, \textit{dm\_only=}, \textit{minimum\_mass=}, \textit{alpha=})} \textemdash\ Draw particles of \textit{p\_size} pixels in a slab of \textit{width} with \textit{col}or using a matplotlib \textit{marker} plotting only every \textit{stride} number of particles.\\ \texttt{title(\textit{text})}\\ \subsection{The $\sim$/.yt/ Directory} \settowidth{\MyLen}{\texttt{multicol} } yt will automatically check for configuration files in a special directory (\texttt{\$HOME/.yt/}) in the user's home directory. The \texttt{config} file \textemdash\ Settings that control runtime behavior. \\ The \texttt{my\_plugins.py} file \textemdash\ Add functions, derived fields, constants, or other commonly-used Python code to yt. \subsection{Analysis Modules} \settowidth{\MyLen}{\texttt{multicol}} The import name for each module is listed at the end of each description (see \textbf{yt Imports}). \texttt{Absorption Spectrum} \textemdash\ (\texttt{absorption\_spectrum}). \\ \texttt{Clump Finder} \textemdash\ Find clumps defined by density thresholds (\texttt{level\_sets}). \\ \texttt{Halo Finding} \textemdash\ Locate halos of dark matter particles (\texttt{halo\_finding}). \\ \texttt{Light Cone Generator} \textemdash\ Stitch datasets together to perform analysis over cosmological volumes. \\ \texttt{Light Ray Generator} \textemdash\ Analyze the path of light rays.\\ \texttt{Rockstar Halo Finding} \textemdash\ Locate halos of dark matter using the Rockstar halo finder (\texttt{halo\_finding.rockstar}). \\ \texttt{Star Particle Analysis} \textemdash\ Analyze star formation history and assemble spectra (\texttt{star\_analysis}). \\ \texttt{Sunrise Exporter} \textemdash\ Export data to the sunrise visualization format (\texttt{sunrise\_export}). \\ \subsection{Parallel Analysis} \settowidth{\MyLen}{\texttt{multicol}} Nearly all of yt is parallelized using MPI\@. The \textit{mpi4py} package must be installed for parallelism in yt. To install \textit{pip install mpi4py} on the command line usually works. Execute python in parallel similar to this:\\ \textit{mpirun -n 12 python script.py}\\ The file \texttt{script.py} must call the \texttt{yt.enable\_parallelism()} to turn on yt's parallelism. If this doesn't happen, all cores will execute the same serial yt script. This command may differ for each system on which you use yt; please consult the system documentation for details on how to run parallel applications. \texttt{parallel\_objects()} \textemdash\ A way to parallelize analysis over objects (such as halos or clumps).\\ \subsection{Git} \settowidth{\MyLen}{\texttt{multicol}} Please see \url{https://git-scm.com/} for the latest Git documentation. \texttt{git clone https://github.com/yt-project/yt} \textemdash\ Clone the yt repository. \\ \texttt{git status} \textemdash\ Show status of working tree.\\ \texttt{git diff} \textemdash\ Show changed files in the working tree. \\ \texttt{git log} \textemdash\ Show a log of changes in reverse chronological order.\\ \texttt{git revert } \textemdash\ Revert the changes in an existing commit and create a new commit with reverted changes. \\ \texttt{git add } \textemdash\ Stage changes in the working tree to the index. \\ \texttt{git commit} \textemdash\ Commit staged changes to the repository. \\ \texttt{git merge } Merge the revisions from the specified branch on top of the current branch.\\ \texttt{git push } \textemdash\ Push changes to remote repository. \\ \texttt{git push } \textemdash\ Push changes in specified branch to remote repository. \\ \texttt{git pull } \textemdash\ Pull changes from the specified branch of the remote repository. This is equivalent to \texttt{git fetch } and then \texttt{git merge /}.\\ \subsection{FAQ} \settowidth{\MyLen}{\texttt{multicol}} \texttt{slc.set\_log('field', False)} \textemdash\ When plotting \texttt{field}, use linear scaling instead of log scaling. %\rule{0.3\linewidth}{0.25pt} %\scriptsize % Can put some final stuff here like copyright etc... \end{multicols} \end{document} yt-project-yt-f043ac8/doc/docstring_idioms.txt000066400000000000000000000032271510711153200215240ustar00rootroot00000000000000Idioms for Docstrings in yt =========================== For a full list of recognized constructs for marking up docstrings, see the Sphinx documentation: http://www.sphinx-doc.org/en/master/ Specifically, this section: http://www.sphinx-doc.org/en/master/usage/restructuredtext/ http://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#cross-referencing-syntax Variables in Examples --------------------- In order to construct short, useful examples, some variables must be specified. However, because often examples require a bit of setup, here is a list of useful variable names that correspond to specific instances that the user is presupposed to have created. * `ds`: a dataset, loaded successfully * `sp`: a sphere * `c`: a 3-component "center" * `L`: a 3-component vector that corresponds to either angular momentum or a normal vector Cross-Referencing ----------------- To enable sufficient linkages between different sections of the documentation, good cross-referencing is key. To reference a section of the documentation, you can use this construction: For more information, see :ref:`image_writer`. This will insert a link to the section in the documentation which has been identified with `image_writer` as its name. Referencing Classes and Functions --------------------------------- To indicate the return type of a given object, you can reference it using this construction: This function returns a :class:`ProjectionPlot`. To reference a function, you can use: To write out this array, use :func:`save_image`. To reference a method, you can use: To add a projection, use :meth:`ProjectionPlot.set_width`. yt-project-yt-f043ac8/doc/extensions/000077500000000000000000000000001510711153200176165ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/extensions/README000066400000000000000000000002311510711153200204720ustar00rootroot00000000000000This includes a version of the Numpy Documentation extension that has been slightly modified to emit extra TOC tree items. -- Matt Turk, March 25, 2011 yt-project-yt-f043ac8/doc/extensions/config_help.py000066400000000000000000000020251510711153200224440ustar00rootroot00000000000000import re import subprocess from docutils import statemachine from docutils.parsers.rst import Directive def setup(app): app.add_directive("config_help", GetConfigHelp) setup.app = app setup.config = app.config setup.confdir = app.confdir retdict = dict(version="1.0", parallel_read_safe=True, parallel_write_safe=True) return retdict class GetConfigHelp(Directive): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True def run(self): rst_file = self.state_machine.document.attributes["source"] data = ( subprocess.check_output(self.arguments[0].split(" ") + ["-h"]) .decode("utf8") .split("\n") ) ind = next( (i for i, val in enumerate(data) if re.match(r"\s{0,3}\{.*\}\s*$", val)) ) lines = [".. code-block:: none", ""] + data[ind + 1 :] self.state_machine.insert_input( statemachine.string2lines("\n".join(lines)), rst_file ) return [] yt-project-yt-f043ac8/doc/extensions/pythonscript_sphinxext.py000066400000000000000000000053211510711153200250510ustar00rootroot00000000000000import errno import glob import os import shutil import subprocess import tempfile import time import uuid from docutils import nodes from docutils.parsers.rst import Directive class PythonScriptDirective(Directive): """Execute an inline python script and display images. This uses exec to execute an inline python script, copies any images produced by the script, and embeds them in the document along with the script. """ required_arguments = 0 optional_arguments = 0 has_content = True def run(self): cwd = os.getcwd() tmpdir = tempfile.mkdtemp() os.chdir(tmpdir) rst_file = self.state_machine.document.attributes["source"] rst_dir = os.path.abspath(os.path.dirname(rst_file)) image_dir, image_rel_dir = make_image_dir(setup, rst_dir) # Construct script from cell content content = "\n".join(self.content) with open("temp.py", "w") as f: f.write(content) # Use sphinx logger? uid = uuid.uuid4().hex[:8] print("") print(f">> Contents of the script: {uid}") print(content) print("") start = time.time() subprocess.call(["python", "temp.py"]) print(f">> The execution of the script {uid} took {time.time() - start:f} s") text = "" for im in sorted(glob.glob("*.png")): text += get_image_tag(im, image_dir, image_rel_dir) code = content literal = nodes.literal_block(code, code) literal["language"] = "python" attributes = {"format": "html"} img_node = nodes.raw("", text, **attributes) # clean up os.chdir(cwd) shutil.rmtree(tmpdir, True) return [literal, img_node] def setup(app): app.add_directive("python-script", PythonScriptDirective) setup.app = app setup.config = app.config setup.confdir = app.confdir retdict = dict(version="0.1", parallel_read_safe=True, parallel_write_safe=True) return retdict def get_image_tag(filename, image_dir, image_rel_dir): my_uuid = uuid.uuid4().hex shutil.move(filename, image_dir + os.path.sep + my_uuid + filename) relative_filename = image_rel_dir + os.path.sep + my_uuid + filename return f'
' def make_image_dir(setup, rst_dir): image_dir = os.path.join(setup.app.builder.outdir, "_images") rel_dir = os.path.relpath(setup.confdir, rst_dir) image_rel_dir = os.path.join(rel_dir, "_images") thread_safe_mkdir(image_dir) return image_dir, image_rel_dir def thread_safe_mkdir(dirname): try: os.makedirs(dirname) except OSError as e: if e.errno != errno.EEXIST: raise yt-project-yt-f043ac8/doc/extensions/yt_colormaps.py000066400000000000000000000037751510711153200227170ustar00rootroot00000000000000# This extension is quite simple: # 1. It accepts a script name # 2. This script is added to the document in a literalinclude # 3. Any _static images found will be added import glob import os import shutil from docutils.parsers.rst import Directive, directives # Some of this magic comes from the matplotlib plot_directive. def setup(app): app.add_directive("yt_colormaps", ColormapScript) setup.app = app setup.config = app.config setup.confdir = app.confdir retdict = dict(version="0.1", parallel_read_safe=True, parallel_write_safe=True) return retdict class ColormapScript(Directive): required_arguments = 1 optional_arguments = 0 def run(self): rst_file = self.state_machine.document.attributes["source"] rst_dir = os.path.abspath(os.path.dirname(rst_file)) script_fn = directives.path(self.arguments[0]) script_bn = os.path.basename(script_fn) # This magic is from matplotlib dest_dir = os.path.abspath( os.path.join(setup.app.builder.outdir, os.path.dirname(script_fn)) ) if not os.path.exists(dest_dir): os.makedirs(dest_dir) # no problem here for me, but just use built-ins rel_dir = os.path.relpath(rst_dir, setup.confdir) place = os.path.join(dest_dir, rel_dir) if not os.path.isdir(place): os.makedirs(place) shutil.copyfile( os.path.join(rst_dir, script_fn), os.path.join(place, script_bn) ) im_path = os.path.join(rst_dir, "_static") images = sorted(glob.glob(os.path.join(im_path, "*.png"))) lines = [] for im in images: im_name = os.path.join("_static", os.path.basename(im)) lines.append(f".. image:: {im_name}") lines.append(" :width: 400") lines.append(f" :target: ../../_images/{os.path.basename(im)}") lines.append("\n") lines.append("\n") self.state_machine.insert_input(lines, rst_file) return [] yt-project-yt-f043ac8/doc/extensions/yt_cookbook.py000066400000000000000000000054771510711153200225270ustar00rootroot00000000000000# This extension is quite simple: # 1. It accepts a script name # 2. This script is added to the document in a literalinclude # 3. Any _static images found will be added import glob import os import shutil from docutils.parsers.rst import Directive, directives # Some of this magic comes from the matplotlib plot_directive. def setup(app): app.add_directive("yt_cookbook", CookbookScript) setup.app = app setup.config = app.config setup.confdir = app.confdir retdict = dict(version="0.1", parallel_read_safe=True, parallel_write_safe=True) return retdict data_patterns = ["*.h5", "*.out", "*.dat", "*.mp4"] class CookbookScript(Directive): required_arguments = 1 optional_arguments = 0 def run(self): rst_file = self.state_machine.document.attributes["source"] rst_dir = os.path.abspath(os.path.dirname(rst_file)) script_fn = directives.path(self.arguments[0]) script_bn = os.path.basename(script_fn) script_name = os.path.basename(self.arguments[0]).split(".")[0] # This magic is from matplotlib dest_dir = os.path.abspath( os.path.join(setup.app.builder.outdir, os.path.dirname(script_fn)) ) if not os.path.exists(dest_dir): os.makedirs(dest_dir) # no problem here for me, but just use built-ins rel_dir = os.path.relpath(rst_dir, setup.confdir) place = os.path.join(dest_dir, rel_dir) if not os.path.isdir(place): os.makedirs(place) shutil.copyfile( os.path.join(rst_dir, script_fn), os.path.join(place, script_bn) ) im_path = os.path.join(rst_dir, "_static") images = sorted(glob.glob(os.path.join(im_path, f"{script_name}__*.png"))) lines = [] lines.append(f"(`{script_bn} <{script_fn}>`__)") lines.append("\n") lines.append("\n") lines.append(f".. literalinclude:: {self.arguments[0]}") lines.append("\n") lines.append("\n") for im in images: im_name = os.path.join("_static", os.path.basename(im)) lines.append(f".. image:: {im_name}") lines.append(" :width: 400") lines.append(f" :target: ../_images/{os.path.basename(im)}") lines.append("\n") lines.append("\n") for ext in data_patterns: data_files = sorted( glob.glob(os.path.join(im_path, f"{script_name}__*.{ext}")) ) for df in data_files: df_bn = os.path.basename(df) shutil.copyfile( os.path.join(rst_dir, df), os.path.join(dest_dir, rel_dir, df_bn) ) lines.append(f" * Data: `{df_bn} <{df}>`__)") lines.append("\n") self.state_machine.insert_input(lines, rst_file) return [] yt-project-yt-f043ac8/doc/extensions/yt_showfields.py000066400000000000000000000014711510711153200230560ustar00rootroot00000000000000import subprocess import sys from docutils.parsers.rst import Directive def setup(app): app.add_directive("yt_showfields", ShowFields) setup.app = app setup.config = app.config setup.confdir = app.confdir retdict = dict(version="1.0", parallel_read_safe=True, parallel_write_safe=True) return retdict class ShowFields(Directive): required_arguments = 0 optional_arguments = 0 parallel_read_safe = True parallel_write_safe = True def run(self): rst_file = self.state_machine.document.attributes["source"] lines = subprocess.check_output( [sys.executable, "./helper_scripts/show_fields.py"] ) lines = lines.decode("utf8") lines = lines.split("\n") self.state_machine.insert_input(lines, rst_file) return [] yt-project-yt-f043ac8/doc/helper_scripts/000077500000000000000000000000001510711153200204455ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/helper_scripts/code_support.py000066400000000000000000000050621510711153200235300ustar00rootroot00000000000000vals = [ "FluidQuantities", "Particles", "Parameters", "Units", "ReadOnDemand", "LoadRawData", "LevelOfSupport", "ContactPerson", ] class CodeSupport: def __init__(self, **kwargs): self.support = {} for v in vals: self.support[v] = "N" for k, v in kwargs.items(): if k in vals: self.support[k] = v Y = "Y" N = "N" code_names = ["Enzo", "Orion", "FLASH", "RAMSES", "Chombo", "Gadget", "ART", "ZEUS"] codes = dict( Enzo=CodeSupport( FluidQuantities=Y, Particles=Y, Parameters=Y, Units=Y, ReadOnDemand=Y, LoadRawData=Y, ContactPerson="Matt Turk", LevelOfSupport="Full", ), Orion=CodeSupport( FluidQuantities=Y, Particles=N, Parameters=Y, Units=Y, ReadOnDemand=Y, LoadRawData=Y, ContactPerson="Jeff Oishi", LevelOfSupport="Full", ), FLASH=CodeSupport( FluidQuantities=Y, Particles=N, Parameters=N, Units=Y, ReadOnDemand=Y, LoadRawData=Y, ContactPerson="John !ZuHone", LevelOfSupport="Partial", ), RAMSES=CodeSupport( FluidQuantities=Y, Particles=N, Parameters=N, Units=N, ReadOnDemand=Y, LoadRawData=Y, ContactPerson="Matt Turk", LevelOfSupport="Partial", ), Chombo=CodeSupport( FluidQuantities=Y, Particles=N, Parameters=N, Units=N, ReadOnDemand=Y, LoadRawData=Y, ContactPerson="Jeff Oishi", LevelOfSupport="Partial", ), Gadget=CodeSupport( FluidQuantities=N, Particles=Y, Parameters=Y, Units=Y, ReadOnDemand=N, LoadRawData=N, ContactPerson="Chris Moody", LevelOfSupport="Partial", ), ART=CodeSupport( FluidQuantities=N, Particles=N, Parameters=N, Units=N, ReadOnDemand=N, LoadRawData=N, ContactPerson="Matt Turk", LevelOfSupport="None", ), ZEUS=CodeSupport( FluidQuantities=N, Particles=N, Parameters=N, Units=N, ReadOnDemand=N, LoadRawData=N, ContactPerson="Matt Turk", LevelOfSupport="None", ), ) print("|| . ||", end=" ") for c in code_names: print(f"{c} || ", end=" ") print() for vn in vals: print(f"|| !{vn} ||", end=" ") for c in code_names: print(f"{codes[c].support[vn]} || ", end=" ") print() yt-project-yt-f043ac8/doc/helper_scripts/parse_cb_list.py000066400000000000000000000025451510711153200236360ustar00rootroot00000000000000import inspect from textwrap import TextWrapper import yt ds = yt.load("RD0005-mine/RedshiftOutput0005") output = open("source/visualizing/_cb_docstrings.inc", "w") template = """ .. function:: %(clsname)s%(sig)s: (This is a proxy for :class:`~%(clsproxy)s`.) %(docstring)s """ tw = TextWrapper(initial_indent=" ", subsequent_indent=" ", width=60) def write_docstring(f, name, cls): if not hasattr(cls, "_type_name") or cls._type_name is None: return for clsi in inspect.getmro(cls): docstring = inspect.getdoc(clsi.__init__) if docstring is not None: break clsname = cls._type_name sig = inspect.formatargspec(*inspect.getargspec(cls.__init__)) sig = sig.replace("**kwargs", "**field_parameters") clsproxy = f"yt.visualization.plot_modifications.{cls.__name__}" # docstring = "\n".join([" %s" % line for line in docstring.split("\n")]) # print(docstring) f.write( template % dict( clsname=clsname, sig=sig, clsproxy=clsproxy, docstring="\n".join(tw.wrap(docstring)), ) ) # docstring = docstring)) for n, c in sorted(yt.visualization.api.callback_registry.items()): write_docstring(output, n, c) print(f".. autoclass:: yt.visualization.plot_modifications.{n}") print(" :members:") print() yt-project-yt-f043ac8/doc/helper_scripts/parse_dq_list.py000066400000000000000000000020231510711153200236450ustar00rootroot00000000000000import inspect from textwrap import TextWrapper import yt ds = yt.load("RD0005-mine/RedshiftOutput0005") output = open("source/analyzing/_dq_docstrings.inc", "w") template = """ .. function:: %(funcname)s%(sig)s: (This is a proxy for :func:`~%(funcproxy)s`.) %(docstring)s """ tw = TextWrapper(initial_indent=" ", subsequent_indent=" ", width=60) def write_docstring(f, name, func): docstring = inspect.getdoc(func) funcname = name sig = inspect.formatargspec(*inspect.getargspec(func)) sig = sig.replace("data, ", "") sig = sig.replace("(data)", "()") funcproxy = f"yt.data_objects.derived_quantities.{func.__name__}" docstring = "\n".join(" %s" % line for line in docstring.split("\n")) f.write( template % dict(funcname=funcname, sig=sig, funcproxy=funcproxy, docstring=docstring) ) # docstring = "\n".join(tw.wrap(docstring)))) dd = ds.all_data() for n, func in sorted(dd.quantities.functions.items()): print(n, func) write_docstring(output, n, func[1]) yt-project-yt-f043ac8/doc/helper_scripts/parse_object_list.py000066400000000000000000000020461510711153200245140ustar00rootroot00000000000000import inspect from textwrap import TextWrapper import yt ds = yt.load("RD0005-mine/RedshiftOutput0005") output = open("source/analyzing/_obj_docstrings.inc", "w") template = """ .. class:: %(clsname)s%(sig)s: For more information, see :ref:`%(docstring)s` (This is a proxy for :class:`~%(clsproxy)sBase`.) """ tw = TextWrapper(initial_indent=" ", subsequent_indent=" ", width=60) def write_docstring(f, name, cls): for clsi in inspect.getmro(cls): docstring = inspect.getdoc(clsi.__init__) if docstring is not None: break clsname = name sig = inspect.formatargspec(*inspect.getargspec(cls.__init__)) sig = sig.replace("**kwargs", "**field_parameters") clsproxy = f"yt.data_objects.data_containers.{cls.__name__}" f.write( template % dict( clsname=clsname, sig=sig, clsproxy=clsproxy, docstring="physical-object-api" ) ) for n, c in sorted(ds.__dict__.items()): if hasattr(c, "_con_args"): print(n) write_docstring(output, n, c) yt-project-yt-f043ac8/doc/helper_scripts/run_recipes.py000077500000000000000000000046611510711153200233470ustar00rootroot00000000000000#!/usr/bin/env python3 import glob import os import shutil import subprocess import sys import tempfile import traceback from multiprocessing import Pool import matplotlib from yt.config import ytcfg matplotlib.use("Agg") FPATTERNS = ["*.png", "*.txt", "*.h5", "*.dat", "*.mp4"] DPATTERNS = ["LC*", "LR", "DD0046"] BADF = [ "cloudy_emissivity.h5", "apec_emissivity.h5", "xray_emissivity.h5", "AMRGridData_Slice_x_density.png", ] CWD = os.getcwd() ytcfg["yt", "serialize"] = False BLACKLIST = ["opengl_ipython", "opengl_vr"] def prep_dirs(): for directory in glob.glob(f"{ytcfg.get('yt', 'test_data_dir')}/*"): os.symlink(directory, os.path.basename(directory)) def run_recipe(payload): (recipe,) = payload module_name, ext = os.path.splitext(os.path.basename(recipe)) dest = os.path.join(os.path.dirname(recipe), "_static", module_name) if module_name in BLACKLIST: return 0 if not os.path.exists(f"{CWD}/_temp/{module_name}.done"): sys.stderr.write(f"Started {module_name}\n") tmpdir = tempfile.mkdtemp() os.chdir(tmpdir) prep_dirs() try: subprocess.check_call(["python", recipe]) except Exception as exc: trace = "".join(traceback.format_exception(*sys.exc_info())) trace += f" in module: {module_name}\n" trace += f" recipe: {recipe}\n" raise Exception(trace) from exc open(f"{CWD}/_temp/{module_name}.done", "wb").close() for pattern in FPATTERNS: for fname in glob.glob(pattern): if fname not in BADF: shutil.move(fname, f"{dest}__{fname}") for pattern in DPATTERNS: for dname in glob.glob(pattern): shutil.move(dname, dest) os.chdir(CWD) shutil.rmtree(tmpdir, True) sys.stderr.write(f"Finished with {module_name}\n") return 0 for path in [ "_temp", "source/cookbook/_static", "source/visualizing/colormaps/_static", ]: fpath = os.path.join(CWD, path) if os.path.exists(fpath): shutil.rmtree(fpath) os.makedirs(fpath) os.chdir("_temp") recipes = [] for rpath in ["source/cookbook", "source/visualizing/colormaps"]: fpath = os.path.join(CWD, rpath) sys.path.append(fpath) recipes += glob.glob(f"{fpath}/*.py") WPOOL = Pool(processes=6) RES = WPOOL.map_async(run_recipe, ((recipe,) for recipe in recipes)) RES.get() os.chdir(CWD) yt-project-yt-f043ac8/doc/helper_scripts/show_fields.py000066400000000000000000000236241510711153200233340ustar00rootroot00000000000000import inspect import numpy as np import yt.frontends as frontends_module from yt.config import ytcfg from yt.fields.derived_field import NullFunc from yt.frontends.api import _frontends from yt.frontends.stream.fields import StreamFieldInfo from yt.funcs import obj_length from yt.testing import fake_random_ds from yt.units import dimensions from yt.units.yt_array import Unit from yt.utilities.cosmology import Cosmology fields, units = [], [] for fname, (code_units, _aliases, _dn) in StreamFieldInfo.known_other_fields: fields.append(("gas", fname)) units.append(code_units) base_ds = fake_random_ds(4, fields=fields, units=units) base_ds.index base_ds.cosmological_simulation = 1 base_ds.cosmology = Cosmology() ytcfg["yt", "internals", "within_testing"] = True np.seterr(all="ignore") def _strip_ftype(field): if not isinstance(field, tuple): return field elif field[0] == "all": return field return field[1] np.random.seed(int(0x4D3D3D3)) units = [base_ds._get_field_info(f).units for f in fields] fields = [_strip_ftype(f) for f in fields] ds = fake_random_ds(16, fields=fields, units=units, particles=1) ds.parameters["HydroMethod"] = "streaming" ds.parameters["EOSType"] = 1.0 ds.parameters["EOSSoundSpeed"] = 1.0 ds.conversion_factors["Time"] = 1.0 ds.conversion_factors.update({f: 1.0 for f in fields}) ds.gamma = 5.0 / 3.0 ds.current_redshift = 0.0001 ds.cosmological_simulation = 1 ds.hubble_constant = 0.7 ds.omega_matter = 0.27 ds.omega_lambda = 0.73 ds.cosmology = Cosmology( hubble_constant=ds.hubble_constant, omega_matter=ds.omega_matter, omega_lambda=ds.omega_lambda, unit_registry=ds.unit_registry, ) for my_unit in ["m", "pc", "AU", "au"]: new_unit = f"{my_unit}cm" my_u = Unit(my_unit, registry=ds.unit_registry) ds.unit_registry.add( new_unit, my_u.base_value, dimensions.length, "\\rm{%s}/(1+z)" % my_unit, prefixable=True, ) header = r""" .. _field-list: Field List ========== This is a list of many of the fields available in yt. We have attempted to include most of the fields that are accessible through the plugin system, as well as the fields that are known by the frontends, however it is possible to generate many more permutations, particularly through vector operations. For more information about the fields framework, see :ref:`fields`. Some fields are recognized by specific frontends only. These are typically fields like density and temperature that have their own names and units in the different frontend datasets. Often, these fields are aliased to their yt-named counterpart fields (typically 'gas' fieldtypes). For example, in the ``FLASH`` frontend, the ``dens`` field (i.e. ``(flash, dens)``) is aliased to the gas field density (i.e. ``(gas, density)``), similarly ``(flash, velx)`` is aliased to ``(gas, velocity_x)``, and so on. In what follows, if a field is aliased it will be noted. Try using the ``ds.field_list`` and ``ds.derived_field_list`` to view the native and derived fields available for your dataset respectively. For example to display the native fields in alphabetical order: .. notebook-cell:: import yt ds = yt.load("Enzo_64/DD0043/data0043") for i in sorted(ds.field_list): print(i) To figure out out what all of the field types here mean, see :ref:`known-field-types`. .. contents:: Table of Contents :depth: 1 :local: :backlinks: none .. _yt-fields: Universal Fields ---------------- """ footer = """ Index of Fields --------------- .. contents:: :depth: 3 :backlinks: none """ print(header) seen = [] def fix_units(units, in_cgs=False): unit_object = Unit(units, registry=ds.unit_registry) if in_cgs: unit_object = unit_object.get_cgs_equivalent() latex = unit_object.latex_representation() return latex.replace(r"\ ", "~") def print_all_fields(fl): for fn in sorted(fl): df = fl[fn] f = df._function s = f"{df.name}" print(s) print("^" * len(s)) print() if obj_length(df.units) > 0: # Most universal fields are in CGS except for these special fields if df.name[1] in [ "particle_position", "particle_position_x", "particle_position_y", "particle_position_z", "entropy", "kT", "metallicity", "dx", "dy", "dz", "cell_volume", "x", "y", "z", ]: print(f" * Units: :math:`{fix_units(df.units)}`") else: print(f" * Units: :math:`{fix_units(df.units, in_cgs=True)}`") print(f" * Sampling Method: {df.sampling_type}") print() print("**Field Source**") print() if f == NullFunc: print("No source available.") print() continue else: print(".. code-block:: python") print() for line in inspect.getsource(f).split("\n"): print(" " + line) print() ds.index print_all_fields(ds.field_info) class FieldInfo: """a simple container to hold the information about fields""" def __init__(self, ftype, field, ptype): name = field[0] self.units = "" u = field[1][0] if len(u) > 0: self.units = r":math:`\mathrm{%s}`" % fix_units(u) a = [f"``{f}``" for f in field[1][1] if f] self.aliases = " ".join(a) self.dname = "" if field[1][2] is not None: self.dname = f":math:`{field[1][2]}`" if ftype != "particle_type": ftype = f"'{ftype}'" self.name = f"({ftype}, '{name}')" self.ptype = ptype current_frontends = [f for f in _frontends if f not in ["stream"]] for frontend in current_frontends: this_f = getattr(frontends_module, frontend) field_info_names = [fi for fi in dir(this_f) if "FieldInfo" in fi] dataset_names = [dset for dset in dir(this_f) if "Dataset" in dset] if frontend == "gadget": # Drop duplicate entry for GadgetHDF5, add special case for FieldInfo # entry dataset_names = ["GadgetDataset"] field_info_names = ["GadgetFieldInfo"] elif frontend == "boxlib": field_info_names = [] for d in dataset_names: if "Maestro" in d: field_info_names.append("MaestroFieldInfo") elif "Castro" in d: field_info_names.append("CastroFieldInfo") else: field_info_names.append("BoxlibFieldInfo") elif frontend == "chombo": # remove low dimensional field info containers for ChomboPIC field_info_names = [ f for f in field_info_names if "1D" not in f and "2D" not in f ] for dset_name, fi_name in zip(dataset_names, field_info_names): fi = getattr(this_f, fi_name) nfields = 0 if hasattr(fi, "known_other_fields"): known_other_fields = fi.known_other_fields nfields += len(known_other_fields) else: known_other_fields = [] if hasattr(fi, "known_particle_fields"): known_particle_fields = fi.known_particle_fields if "Tipsy" in fi_name: known_particle_fields += tuple(fi.aux_particle_fields.values()) nfields += len(known_particle_fields) else: known_particle_fields = [] if nfields > 0: print(f".. _{dset_name.replace('Dataset', '')}_specific_fields:\n") h = f"{dset_name.replace('Dataset', '')}-Specific Fields" print(h) print("-" * len(h) + "\n") field_stuff = [] for field in known_other_fields: field_stuff.append(FieldInfo(frontend, field, False)) for field in known_particle_fields: if frontend in ["sph", "halo_catalogs", "sdf"]: field_stuff.append(FieldInfo("particle_type", field, True)) else: field_stuff.append(FieldInfo("io", field, True)) # output len_name = 10 len_units = 5 len_aliases = 7 len_part = 9 len_disp = 12 for f in field_stuff: len_name = max(len_name, len(f.name)) len_aliases = max(len_aliases, len(f.aliases)) len_units = max(len_units, len(f.units)) len_disp = max(len_disp, len(f.dname)) fstr = "{nm:{nw}} {un:{uw}} {al:{aw}} {pt:{pw}} {dp:{dw}}" header = fstr.format( nm="field name", nw=len_name, un="units", uw=len_units, al="aliases", aw=len_aliases, pt="particle?", pw=len_part, dp="display name", dw=len_disp, ) div = fstr.format( nm="=" * len_name, nw=len_name, un="=" * len_units, uw=len_units, al="=" * len_aliases, aw=len_aliases, pt="=" * len_part, pw=len_part, dp="=" * len_disp, dw=len_disp, ) print(div) print(header) print(div) for f in field_stuff: print( fstr.format( nm=f.name, nw=len_name, un=f.units, uw=len_units, al=f.aliases, aw=len_aliases, pt=f.ptype, pw=len_part, dp=f.dname, dw=len_disp, ) ) print(div) print("") print(footer) yt-project-yt-f043ac8/doc/helper_scripts/split_auto.py000066400000000000000000000033701510711153200232050ustar00rootroot00000000000000import collections templates = dict( autoclass=r""" %(name)s %(header)s .. autoclass:: %(name)s :members: :inherited-members: :undoc-members: """, autofunction=r""" %(name)s %(header)s .. autofunction:: %(name)s """, index_file=r""" %(title)s %(header)s .. autosummary:: :toctree: generated/%(dn)s """, ) file_names = dict( ft=("Field Types", "source/api/field_types/%s.rst"), pt=("Plot Types", "source/api/plot_types/%s.rst"), cl=("Callback List", "source/api/callback_list/%s.rst"), ee=("Extension Types", "source/api/extension_types/%s.rst"), dd=("Derived Datatypes", "source/api/derived_datatypes/%s.rst"), mt=("Miscellaneous Types", "source/api/misc_types/%s.rst"), fl=("Function List", "source/api/function_list/%s.rst"), ds=("Data Sources", "source/api/data_sources/%s.rst"), dq=("Derived Quantities", "source/api/derived_quantities/%s.rst"), ) to_include = collections.defaultdict(list) for line in open("auto_generated.txt"): ftype, name, file_name = (s.strip() for s in line.split("::")) cn = name.split(".")[-1] if cn[0] == "_": cn = cn[1:] # For leading _ fn = file_names[file_name][1] % cn # if not os.path.exists(os.path.dirname(fn)): # os.mkdir(os.path.dirname(fn)) header = "-" * len(name) dd = dict(header=header, name=name) # open(fn, "w").write(templates[ftype] % dd) to_include[file_name].append(name) for key, val in file_names.items(): title, file = val fn = file.rsplit("/", 1)[0] + ".rst" print(fn) f = open(fn, "w") dn = fn.split("/")[-1][:-4] dd = dict(header="=" * len(title), title=title, dn=dn) f.write(templates["index_file"] % dd) for obj in sorted(to_include[key]): f.write(f" {obj}\n") yt-project-yt-f043ac8/doc/helper_scripts/table.py000066400000000000000000000063201510711153200221070ustar00rootroot00000000000000contents = [ ( "Getting Started", [ ("welcome/index.html", "Welcome to yt!", "What's yt all about?"), ( "orientation/index.html", "yt Orientation", "Quickly get up and running with yt: zero to sixty.", ), ( "help/index.html", "How to Ask for Help", "Some guidelines on how and where to ask for help with yt", ), ( "workshop.html", "Workshop Tutorials", "Videos, slides and scripts from the 2012 workshop covering many " + "aspects of yt, from beginning to advanced.", ), ], ), ( "Everyday yt", [ ( "analyzing/index.html", "Analyzing Data", "An overview of different ways to handle and process data: loading " + "data, using and manipulating objects and fields, examining and " + "manipulating particles, derived fields, generating processed data, " + "time series analysis.", ), ( "visualizing/index.html", "Visualizing Data", "An overview of different ways to visualize data: making projections, " + "slices, phase plots, streamlines, and volume rendering; modifying " + "plots; the fixed resolution buffer.", ), ( "interacting/index.html", "Interacting with yt", "Different ways -- scripting, GUIs, prompts, explorers -- to explore " + "your data.", ), ], ), ( "Advanced Usage", [ ( "advanced/index.html", "Advanced yt usage", "Advanced topics: parallelism, debugging, ways to drive yt, " + "developing", ), ( "getting_involved/index.html", "Getting Involved", "How to participate in the community, contribute code and share " + "scripts", ), ], ), ( "Reference Materials", [ ( "cookbook/index.html", "The Cookbook", "A bunch of illustrated examples of how to do things", ), ( "reference/index.html", "Reference Materials", "A list of all bundled fields, API documentation, the Change Log...", ), ("faq/index.html", "FAQ", "Frequently Asked Questions: answered for you!"), ], ), ] heading_template = r"""

%s

%s
""" subheading_template = r"""

%s

%s

""" t = "" for heading, items in contents: s = "" for subheading in items: s += subheading_template % subheading t += heading_template % (heading, s) print(t) yt-project-yt-f043ac8/doc/source/000077500000000000000000000000001510711153200167175ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/_static/000077500000000000000000000000001510711153200203455ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/_static/apiKey01.jpg000066400000000000000000002034541510711153200224420ustar00rootroot00000000000000ÿØÿàJFIFHHÿÛC    #%$""!&+7/&)4)!"0A149;>>>%.DIC;ÿÛC  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÿÀ3 ÿÄÿÄT!1AQ"aq23U‘“Ñ#RS¡¢±$4BTr²56csÁ7bt‚’³á%CDð&EVuñ”ÂÒÿÄÿÄ2!1AQ"a2q¡ÁB‘±#3ÑðRñSÿÚ ?ñ*aª»5Z£A¬–ÏZ“Ëñ¼m®Ðˆtþ‡vê‰ÿ‡ñ@þ‡vê‰ÿ‡ñ@þ‡vê‰ÿ‡ñ@þ‡vê‰ÿ‡ñ@þ‡vê™ÿ‡ñ@þ‡ö‹ê™þÖþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐîÑýQ?ðþ(ÐþÑ}S?ÚßÅúÚ?ª'þÅúÚ?ª'þÅúÚ?ª'þÅL†'Šc~”µÚó¦¹ÀhŸ-„Q" µWf«Th "·{5’Çàêæl²6U¶@×õù‚Fƹr*Zöc ÎÐgëã; lÁǼkxˆÓIéðA#¶œgf3 ¡—Ù…²q¹¡§™#\½ÈB׳ƒ‡´=›*ü„:7ÈÞí±‚®ÐxÍìDž(‡Ð§ìŽŸ“çYâ÷¢2Nó½v¸‰<;Ò!óÏK(‘ðóž1æöÿ4CôD XÐá PÝôš OØÿî;ù “Àß$ù p7ɾH ò@ào’|8äÀß$ù p7ɾH ò@ào’|8äÀß$ù p7ɾH ò@ào’|BvM~£êƒ(<·åØû€H{5ìõ‚(X@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@D‘"DAj®ÍV¨ÐAí?&mÆ[ÌOÉQ¯dÏXÈszïD´½ÙvʃpÑÄY¬6PÀ9ˆâ#ÝÈ„}·˜Ãæñ”åÃÄ`Ó§°ÊÌ s‡F4ž\·²TSÚÌŽ‚«.O-Õ25‘ÂÒxN¹\€Ò!ñ\­ŠÖòÖ¬Sƒ¸­,¥ÑE 8à4%{ù7ÿ~¨ÿ–OôÛ¶½‚Èv›6ËÕmÖ‰±ðÉŽ‚O€ö©™ìõžÌöNÕ SE+É–N(÷­û}ȇÏ&úy.ÐÏ ê±XµàÙZâo4L¡ö²j½½ž¤Gqb&ˆ˜Ý4±¥Ò~TðøÜe,sèP¯YÒLðãa»ö)!è±ì ÿÉUO΅“i±ó†õsAÞ¾:×Å‹‹©Ù>Üá,CKÊn€ð"kdˆ‘ê¸Ô~>GfÕµ-iF¤…îÃÚòP—4H€€ƒx?´Eþvÿ0ˆ~ˆ‹èYîR†è<öo?/N+YGßJùÜ[ ð'^'É,oh2Õóqa{ENg²Òê¶+8˜å×2Ý…¦@AÒÑ?þc¿š %zÔÓJÚu™#!w$œ(ÛkîîH¸˜öµÁîÐ#÷è Ì·«C;`’v6GkL'Ÿ>ˆ9Ö¾$wÎk°øX÷´y|Pw±f¬žVÆÒt7âPhûõc“ºÃoù®ß'{f[Õ¡…“>vßó]½‡{ êÍ…“ˆÞ6×ñr(4“%NFù,1¢A¶lõh8þu†+V!³,qÞÖ°“Ì‚Ðyý¨,ØÚ   Áè‚ ¿Û¤ÿ#êƒ(!œ¾1¤ƒ’¦: Øg#ö ”DzXÛ$okØá¶¹§`0PyŸÊ7ûgüÌÿPAñ• =ß䳊È߸ûñE<ð±½ÌR´£¾'kÄô÷mJ%ò™Æc{AqÑÇ ¤‡Šh£k]¾G^bñʰ‡zÍJ–k5“²ÜþŽÑÛ™'ƒ\<69b!¼XBû¶«I“Ç@*»òÉ9 yéêòÛºeònÌIŠca}†@l4¶M²Fk`´øìtA/ÙX­? Ù3óè• ÜQÎKC¶3®ƒÄ0¤v‡ žÇ¶Q=_NHä»,œ,,àÚÙôûD­µS=&>zµm> “_¡M¶Œ ³?¥ :$  òæƒ#5Œ^/º­]޹nHY?xî7´iàcD{Pp‹ nkש°ÅÞPŽY%Û¹j3§k—4£ììÆŒV§¿B©ž#,0Ï? ’3Ÿ04G=lóAR9ëCdô-ó}•Ëv~½yïÀ†‚ðÇ~«Aœ'erÙø,ÏB¸tuÚIs{¿U¾e>ˆ:=B$A„ªìÕjñ1z¹·Å^Pýâ>#h‡Ý[Œ­s´u;Co º6;Ì8‚ØJ”>-Ú¼·çžÓݼ¸Ìœ1ûÞCùoâ¡/¥~RêY½Ù~‰“–NÇ8FÒâÐ÷…(|†x%­3àž'Å+žÇ·E§Ú%é&ÿïÕòÉþ‚‚çòŸ•ÈÒíDqT¿fÍV;‚)\Ѿ'sÐRCÑvÝ›½…·-»Ï ’aÇ+ËŽ¸G-”CÉþI^Öö¦f’u7hY¨™wí7gr—¿)‡º§3¢žhd†Ð²OA­ß–ö~/þ{ÿÒ…Œ4§ÈþGâ©U†I¤Ç·ƒ«ˆç¯ÜˆBü”b®ãëdlܯ-vÊö5•¥¤ðïgGރ晹Ùk=±’Ú‘í#Ä(J$@@A¼Ú"ÿ;˜D?DEô,÷)CtO-GÚ§ö‚• r-×l¡ƒœ‘–žNhñ)ò9é­ö“ ”Êb®bðôåwÖ#Ñ2¹ºÃû­èƒèmpsCšààFÁ`„AÒÑ?þc¿š %DV›ˆYj9D}ë¤dasK\wÔt;óAɰMZ*–e‰çúË啌EÀëòØÞbFMh%ןº†( ¼M®Hg.CZ÷ûtŠãe|MÌÞò>ð3A¾Ãä}ˆ; Ê ˆ ËýºOò7þ¨2ƒÈdÿ&øŒ–~<‘ýN%Ö+1º»Ï~ñóöl õ±ÆÈclQ1¬€5­hÐt™ü£¹ÖÌÏõP°€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ¢¿`pØÏèÆ>»kÅ$B<87{q.Øñ$•*¢vËŽ‹ÈF`Š&Å–7pë…ã¡ß™éíAðÕ =ofrTðÒûVœ]–ræC .«w¤x?gòßš!®7R­,Œ0Ɉ·”†ËXÇÜ•†¾{8 Ù×´y ±±’¢îÒÉ;/U,üÀè„‘¸1†NìŽ<ú¨…eüëMóÅ—1φ3Ã_ÄÒqä:KkoŽÄÅAÓÂëeds™Ç]Þ¸†ºòßDñd)þ~ÇÈmÁÀÜtçwƒAýÓ‡ >{å¤û>Ó62µ|Äø¹ðºq=톉êï{à<óáÑxoÔ­†ìëŒÍ>‹“–Y/k8˜A#Ã`QÖ¯C+Ú óe1îŠíK"¨ŽË\éxÏä>o/?ñ,bb‡;>.|Sk¹ÑÊl7ÒkÐ=mñt‚ïP>ÙØV·Kú5ÚN©Ê;¸füß&¸ÿ'uƒµý°­R—ôk³\0Ó‰½Ü³D~w›Z|G›¼T¥à$A„ªìÕj¨ã/å$tt)ÍiìNlLâ y”CëìÛìŸä¶8nÈ{¸î#nùµÎ'CÛÂÓû”¡ñÍ {%ìq_”Üæ/.îµ–ÄÐÖ>fž @H<ÐyŒ•ù²™ìp÷¶^þ¡¿b¸\½œV,•FÆé¢ [Ìhòy ëÚÐ\í&A·®²JØÄ`DÒ'ÄŸ2‚~¶ùL"LeHªº ç+]ë Ž^H)±¹X‹ñ]¥)ŠxŽÚíoØA  õ7ÿ*9û´]Y­UÏo ¦…§Û­ž^ô4©Ïö¿%Ú:µk^dµNÚèÚCœu®{'h$ÃÛüí\=\]Y!¯^É#aã!¾gDH%d¿)¹ì–9ôõ^°‘¼2I Hy:Ù:ø ò‘ðh‹üíþaýгܥ Ðs³Z •ä¯fMƒOcÆÃ‡µÌc#±ÆÐÖ0µ h:ƒd(ýÿæ;ù ’ƒcdc˜ö‡5ÃDƒXad6(ÛÂÆ 4oz7Ð@Ò4¤ iH@Ò   Áè‚ ¿Û¤ÿ#êƒ(<·åýγþf¨ øÒ…„c·j&Gff4tkdpà ’Õ™›Ã-‰dn÷§ÈHýå$HˆaPaÚ€€ã´K(†e  UvjµF‚ \hïöj̶1ý×ÌàwzÎ!­ïÌ"æûE”í í—%g¼îƘƎ3ÜóAX‰e®,{\5¶FÐ}N¯åK*Æ,W²ÉC}vµœ@aÚ•]~Tð±·÷_ü |©àÿcoî¿ù@ùSÁþÆßÝòò§ƒý¿ºÿååKûuÿÊ  üªàác›Ü[vÜ]ô~~VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüP>VðáíýØüPpåKéÝ/slm kºòß·Úò§ƒý¿ºÿååOûuÿÊÊžö6þëÿ”=°íå,æãèÁ(ï ß(Ö€ òû/¡" µWf«Th "H€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ-UÙ¾ò9Së«rßÅU}Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÙò9Së«rßÅ Ÿ#•>º±÷-üPÛSù¨?þ3cî[ø¡±¿‘êŽnÿ<Øû–þ(m·ÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCgÈåO®¬}Ë6|ŽTúêÇÜ·ñCl|ŽTúêÇܵ ¼ÿôr?ñ/ÿÒ”}¥Ua@‹èÇÅè9Ë/v‡'@y”Yò‹í(Ö¿á~ôë_ð¿zõ¯ø_½ú×ü/Þýkþï@þµÿ ÷ Zÿ…ûÐ?­ÂýèÖ¿á~ôë_ð¿zõ¯ø_½ú×ü/Þýkþï@þµÿ ÷ Zÿ…ûÐ?­ÂýèÖ¿á~ôë_ð¿zõ¯ø_½ú×ü/Þýkþï@þµÿ ÷ Zÿ…ûÐ?­yEûÐf9^c{x^ùD ꀃæ ʾ ª°€€€€€€€€€€€€€€€€€€€€€€€€€€ƒG Eôcâƒt%ÑO]Ï j׫Z¡™®>^?bÆÕš÷vÓ-/øeßj­YA„“{]þ÷åÿñ’ÿ¨ û÷ä‹þͱ¾ù÷ƒÚ   ]ÑGŸë•ÿÎÒPNAåûcœÏaPá1G es„¿¢{ø5­|Þ›ö òÿÓ¾Þÿü¦ÿoÅî#´}¢·zŒV±ü,Ú¿›ç‹¹$ïÇ„óÐø õòÉÝBùØcKˆ÷  ¨ì·hãí6(ÝewVs_Àø^àâÞ@ƒ±æ(9a;U_3{%]˜c¢öµ³>AÃ0%ÍysiAzécah{ÚÒó¦‚u³äTv—´,ìånIZK ’vÄ[æÐA%ÞÝy í5 :ô'¾‘ë1BÇ1ÚG{B Ó[¯X´O»€Ùb h4z_F>(7@A‚PyŒíçOh×aýGž¼JëÃMFÞ?¬Ë6¿í ‡¼1¼N: KÞ)^VsâÅl¶áHêŒûŽß¨Ðµy÷õ¶þX{8ÿ…S_îOöj.I⩳'Ù¬ÿ ÃâeÑ·÷ØG¹mO[ÍlŸÂ­‚ßÝÞ9šOrhŽš:!uÓ-2v¼ÜžŸ.ú£Kjyëi³~™žßœ>*¶ÅÙ®/Yzô·UýL…{¬Ü/ÙñiäGÁsÚ“^ïOjdü2’¨Õù7µßï~_ÿ/úŠ¿~H¿ìÛï—ÿqÈ=¢   Áäxì÷å; ÙܼøË•î:h8KŒQ‚Ó¶‡ sò(=];L»RQ‚4m‘ õý¨(;SÛÌ_d­ÁZü6^ùã/i…€®{!ž;[´Xxr”Ù#!˜¸4H4îDƒÈ{yìßåG €ËÙÆ[¯qÓV <Ç-;hw.~E¯­;l׊v+Æúè ó½¨íî+²wa©~/|Ñ÷0°õÏdy ¶Áf«öƒR£dl3‚Z$pÑ#ŸØƒÎf¿*X<^Æ2Õ{®š»ƒ^c¥§`\ý¨!³òÍÙ§;Nƒ Ñç܃üœƒÓ`{[…í(?šî¶W°mñ8½£Ì´ø º@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A£ú †ÿí•ÿÎÒPOAóÿÊ“*>,hµ—›ëIÃÝBé8ùÕ#Zÿª'‰Ÿ;º1_äyà2ã¬:Gà8e=&"“`í5òG%?ÓÒ1ÓÂ%<õ Ÿ#‡·§‚ ÛþÅ7ü·$7Ç]—³¸Š×iâÌaâŠ[L¬ûZñÿ¡•ÎÃ-ÎÑ`õ>¤,qðpc´ïÚ 8;’v›7ŽžvóÄÓ.¤|ÛO%„{Àcþd]«É‚´@g ÐyË¡øLŽ?³’l×Zö1Î?².Z8ÛÆ u{#ŒÌ؆Ó#½$npq,xà I=GFóë¤î±ØÌ·iaÁÆØCq •ÑÀÐÖ²[DÈ8´$µKCÙ»˜x`ŽÜ–«¶)b>f»é8ˆæàFÉÚ hb²3öšlÔ0K4VžÇ¾v‚è` $üÑ­RÍ›ŸÙ jü£êØîÝ3µÏºäãæuÓ}N¶‚Ò=,Ïeq£øC¢ŸÒ æñ‰å‰0ê]²v|.;+jµŠv¡‡:ZÖ]ˆ!ѼÐ$´€kÁò6?Ú ÿ”˜A*>ˆ7Aóe_PUX@A„îåjQ‘‘Ìé Þ ƒYžtËÖ‹+–Ìf­±²ØmÇÖ‡Œoºšä<·´«°vw&Úì.cx¸ZLŽä ÷SÜ»Ú<eòëZ€9¾“V:ü&¸¶»{$oÅ{D;ò‰Œp;)ÿ0A “fÚ|Œ/ò384œt¤OiïãÅ+¶³X˱ؕŒžœ¡Ð‡x´‡íxì ´»Êä{A>#bŒ§_bÌ‘w„¹ÝѽtæIAµ0æc£‡Žlœ.‘×ãcÜÊÚw-v·Ðhlxù °½jþ;!©=ˆl¾Í™4½ÀnÇ #„lðùuA«ïeó›Ô±vá£_ZÉ&|ë¤ i~ñÀæûñ2X‘#æ7Ôq âk€>x ‡g'ÚJÝ´oµTF#ŽWÒî:°ë~¾÷ÅÏ}4‚ns3r<Å }{°ã!µ ”[ž0þ'lj1²:;æ‚óèªpä,Åfn#úH£à¾¶y ˜€€€€€ƒG Eôcâƒt=|kò©Ú.×vW´m42ÒǸÎ8ZÒáÉÍÞ¾?ýƒí•ü®^j9kfwÌÞ8^àœ:Ž^cù.œW™KÍõx+ç_êö–ž]/ƒW«¿,œ|CÒþŠ)‡Ÿ™B’Ë#<#Ö?¹qM¢æ?M{õí 0]ŸœPyëñ\öõ-g ~)vr'õ¼…Ÿúºü©3é^W¶ùÌŽ õàx†ÄÄ¿º$4~%wzKF]ÏËÕN(ˆöüª{)ÚNÑåóÐV›%+ë³×˜pŽm×EéÎi¤DÏg‘>’™&b#«ê•ìmí|O-p>D.ºe¦Xèòrú|¾žßTWÉm~S»g©XÌÜ¡­‘ÀôÙö.9é/j¿†%ä­Úží¹mYÉ4Ï/{Ï‹R¡eî+·½§ÂããÇã²²AZ-ðFÓ­ž£Í¿•.Ú}y/Ý·ðAì?%ݹí&{¶‘QÉäßb³ ‘ÅŽc@غleDž¿*_ö“ÿ,ûmA÷l ÿöö7ÿ ú”þ[Û¸¿ü+ÿÖ{oÉ_ýŸPÿ4¿ûŽAòOÊ_ûý˜ÿ3?öÚƒïøŸöM?ü<é>Cùlÿy1ßø3þ²ƒÞþL?ì÷î“ÿqÈ>?ùGÿ²ÿó[þ† ú3?$}œ·‹ŠHä¹ ÒÂ׉¸€qÞˆèƒæ½ŒšÅÝbýä¿ÒÄNá<œÒx]ðÒÒPhþˆ!¿ûmóŸô”ÐWäðXÌÐŒd¨ÃhDIgxÝðï®@†ì¸;J€Îh$U쮕¨íVÆAñ±à´ûZ¹í-p¢ˆAóEX8{ºn¬Þ¨ˆ¾Z–`ô?H½GÉîÊÄfP9 ¸ùPEìÎ&lm[3[lM¹~ÃìÎ";k\îÇ@¿z ; ´èŒð²C ‚Hø†ø\:íæƒ[Xúwd‚K5¢™õ¤ï!sÛ³¼Ç‘A!€ÅežÉ2úö_Ó]#6@ò÷{v¹Š¡¦)Û§ ÕÛ®œßUºé¯- ás³ØŒ„As^hë´6&¹Ÿ0­ànü.J°Õ~>³ ®ÒØ£1‚Ö4t=Ä Ûˆ¡‰…ÐãêEYw„m×öù åW³¸Š7]v¦6´6½ÈÈÀ<úëË~Ä ÝžÄd¬2ÍÜuk3@>HÁ:~ßJ¼“Á;àÒ×ßrò9DZ£¯-ŽH!Øìæ"ÛdmŒmy²÷ÏÛ:É­q{õâ‚U}LmfÖ¥Z:ð´ì26è{PJ@@@@AÇûAŸòó%GÑè>`¬«ê «< Ù\ˆíaÔÎ3÷ p£ãéÑ]•ÈŽÖŸÓ?•Žã\èøútA†år?ÒϦw;θÑñôèƒÞžˆ<£aÊök1zjxçäq·åïË!pC!ùÜŽ›Ñ\íWg/Ò›65ò!ô‡4—§CO!±¤rOÍö‹Ü+ð³Ó|ŵfW°ÆÖ‚ ‹tvíë—/3бý5ÇÜŽ°Ò’'I±¦’F‡Ÿ‚özÕÜ7hèXa®o\’HíhŽE®åá°ƒ;Æšõ¬v(6ÃKY,á÷C¦Þ×Û­m‹Pä0½¦µ•«—!VüLl¬€´IÙÈ6@ÏG’ÊáhÜ‹+,V¹ƒQÏokIñÞ·®zAµø®å²=Ÿ¼Ì|ð2 2>fKÂxHèøû6ƒAþÏç26`ÆÏ©‘seŽæñÅ  ‚G#×h8ņȻÚ×á½—{k5ÁŃƒ…ßMù “•Ç\Ÿòxq±Wsízq÷[â»uàƒ¶Vg²¨ÚìíŒgÂÞ#cÀwB \F½è6ì} ˜ì;á·¡ï‚»äã0DOªÂ}ŸõA|€€€€€ƒG Eôcâƒt7ò£Ù¯éc¬™ÅjŸõˆ<·¬ßˆßîAù¾…ÙqÙ.@tø$oÃÁMgSµmXµf³åöØmÅ~¼w ;Žv‡·â?ýÅŸñÙÙé#XkáœD –ÌŽ@ùíy¦Ó{>ªóZDG•¼Ö\Éèä¦;^z<‹äŠwFu÷“ê±£Þº#ÓÇ™a>¢Þ!ñ~Øæší‹[Ž3ÝG®œ-åüö½ïMŠ1cŠÂ“i·Y{ÉÖ ³ .E§V,;LiñŒ~'bâõ~¢i’+á×ém­mçôz¸ä;ØÛ\0z‚´¥ÿš²Û.-}6ë«gd;7,„ÏŽis²îñÝ~Õ݇=g¥ÿ»Éõ>Ÿ-cxgúÓäùˆ"­™¹-áŠ9ÜÖ %¤ë}MÍc}ßdü›ö ²¹îÅS½Ç2{o/?¼p<œ@ä–”i113§©ù(ìWÔ¬û×þ(”ü7`»5€È6þ3ÚöÒÐð÷G¯RƒÑ ÁèƒóßåGþÐr{ò‹ÿm¨:V¿ùGmX…oÏÀcD|8·‡\µêôÒ ^ÐÍÚ)ìB{Eé½ðaî½-…§‡|õ°9mÚÿ%ö}CüÒÿî9É?)ïöcüÌÿÛj¿âÙ4ÿðñÿ¤ ùå³ýäÇàÏúÊ{ù0ÿ³ÜgºOýÇ ùåýþËï§zßô5«7(§[7ç¦ÓîÀ$BCx5æM —ù,±ÙêÝ¢„äDã ãÁQï×rׯ¶<}Õ½l€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒGôA ÿÛkÿœÿ¤ ž€€€€€ƒ@Ò   ƒcý ÏùGù„£èƒt0VUõU„AŽŽ ùé€ozóÒâÞ†üôƒ(2€€€€ƒ2€ƒ2€€ƒ2€€€€€€ƒG Eôcâƒt#cH?0~R;4{1Û Uãg [¿¯åÂãÌ| ÏÉÖ[¿£.2GmõÏ{ýS×ì?Ísf¯^Nœ3áìkÉè—ÃÏ(åõ]ì+ËõXùW£Õ¯û¸xù„Ûì+×´ÅbewÛiWe °×ƒÕl- n½‹Ä¾¯¾JoS¸L|l»§°ˆìðzæ­­‚wáê`õ1há~±ÿ»#5ÎlÜ,uiñ÷/GH¼nßÜu‡Å³íÛßø‡ÿ5éSðÃË¿â—Ø&–å©Ù*2Âí_±à}cÈ®ÚV-MKÆõ/‹<ÚOÇäb¿9<|æùð¹ïI¬½9«–»ŽéŠ„=~zü©ÚOü±ÿíµݰîö7ÿ ú”þ[Û¸¿ü+ÿÖ{_É_ýŸPÿ4¿ûŽAòÿÊ­*öîäi µ%cˆäGiýí(>³Ù>×aò]§!ÈWŠhák&ŠIZÇ1Àhò'§.¨>UùRÏÒÎö¡Ž£3f¯RzÃê¸ì—hø°6ƒë?“úRãû ‹¯;KdîxÜÓÔq8¸~â|[òþþæ?æ·ÿm¨>×Wµ]Ÿ©ˆ®ùóTZ#œC¿i#Mžýˆ> ùGµÆ\le‚Åþ*ìDnM·’ÓaPhþˆ!¿ûmóŸô”ÐSö´´{1mËÂW5ﱑ7nqÖÿA³Ý½Ãv‰Ó2IYð€Klð³ˆw¢ƒÐCnµ‚D3Ç!o^‡kìAÑïllsÜtÖ‚IA˜¡›¤Û¸ÛÅwÐö‚9Ž£GE)f±Ù–êT²Ùf¤àË ŽwËdhô=`¬«ê «4z_F>(7@@AóŸË?f¿Èö¶Fï`ŽD5Ãzëu—£‡7kÑ&œýüFœÿHÑê»õ‚òrãœWå }F*Þ¼ëÚGÍ¿)wÝéðbÚy@;É5úǧîþkØôQN/ŒÖu._“œ_‘—"öú•Ç 9|þù«z»ê¼~Q/£¯9Pr;$ƈ§äñó$ñ\ó[cžTìô=7ªšÎ¥ðÌëK3×Úã²,<ñ_CŽwHŸ³›&¦ó§Óû+¢ì­B:mûùЧ»ly7 ¿¦¦|Z·÷{:w±´áÿëEz•µ2ÓpùûW'¦É©îö/Gz¸•‡G£›äW-é5=l9c-w J„=|Ƕ’ìŸi;Ko+_#Rì²F<¸i¼õîAô\mgRÆU¨÷:Y#¡-o÷ ñŸ”ÀÞí~Fš—k×mx]„­q$—oÁ ìn ~Íöf¶*ÄÑÍ$%ä¾0CNÜO½¬ì~;µ´[ ¾(¦‹f ´dõ÷ƒäƒæv"™¶Ë¨/К=òsøšuîÑþh/»5ù¯Bävó–ÛpÆàæ×‰¤FO‡<Ï»’¦è ƒå½©ü•e3Ý¥»”ƒ%R(ì¼9¬{HÓ@ç¯r ¦þD²ÜC‹-D1öƒØöGòeŽìͦߞw^ºß˜÷7…‘ûZß?iAíº Ê Ñ7ÿm¯þsþ’‚z~Y?Øñ_ÿ¡AñóϨ=ä­Îon h%¡ÐJ-òÚ¶ÛþÅ?ü·$=ì¾E½šÄ¿èmâb¿ZV´1íÓú3ñAÓ‰¿Lö†…IßF:Udïy™È{ÝïÛ¶½Ý\Ô¹ìÆS™ñ@*¶šÓ­ñzŒaÿÍÄuÿu½¹¦lE‰>“b-äëǨŸÃÕß;§QàP;›9>ÑYÃþs½^®6´'ŠCd™ïâõœýlè7§š ËwlÛÀA ¹{ù¨ö‚*¦b4e ”iÇ^:#h,ãeÜïh2àe-Ó+!¯w†·‹€<½à[™G–‚ 9;u;ÙªcÓ_0™Ôš Ŭ.$3}6uÏÀm¹rª}˜íç(c¯d¡=àÜǬÒG]ÈõÑAi‘Çfkàß-;÷mÚžX¤°#C„ßl 0óåîë´»1b¤ôçô[÷¬–KÃ$w‰ï`v¾i<üP^  ƒcý ÏùGù„£èƒt0VUõU„=/£  ç<1Ø‚Heh|r4µí> ƒò—kð2vg´÷qn‚'î"½æÓö$Dì>Kó¿f£c»4¿Dÿ2ß—Á2b÷)ʽáŽ,þÆ^ü6ý%oeÍŽNçpw »Ë\×™jsé/s_o{íåñl®B\¶VÅéO“È]ø³KÐÇHÇH¬xywžV™}g³8“†ÁW­#8es{É7úÇŸÿ ÊË’2^fÚ&'ªÕd¨€: ø¦sžvöÿÄ?ù¯gà†¤öÿþתÂ4}r=£ˆ®\–raèãÇ>Ì^;=42˜Ÿ¿Ô-0æœV߇/ªôÕÏMyð¹Çßu+ ™¤–;ç0½‹V/WÍã½°dëý^Æ9,m{Úá°Wƺ=ÊÌZ7 Ô$@@@A‚tyl¯åŒ¼êŽk/Ü2µÂÓâ6O2»qú,—¯.Î,ž·-ǺÓÚ|NhNÓL‡ÿ´ÿUãáãðXåôù1~(mÔcÉøej Á»(4Dßý¶¿ùÏúJ è)»MÙŠ]©Ç6ÇËŽA#´ë^<¼PyO‘¼?ÖW¿ƒÿùAmÙ¿ÉÖ/³YOÎ0Y³<Á…ïKtÝõ<€AêäŒK£<ƒšZuíADþÇc¥£‡©+å{pîi…ļ4kNåÌu®ˆ$ËZ–æC;4ÒIlM”—†ðl ÿyb±‚¬ò¼~p²é"ŠQ§G'€hôêçkÉÎSUµ›3ÞÁZÌv[Á®naØ~"ä{>._nBµû8û}ßtùkðžñ›Øtz Óú+E¸ªØöI0Ž LµÆ\ ä;ˆ—9ìõÿ¢¾ÍG>NkÕòéA­µo À ì¹lhé6öFŒxJXȧ± <¾µ†8 bvÏ0u£È‘¢9ލ:IÙÇZÃ\ÆßÊܶ.7…ÒÉÀÁäÐÖ€>Äoãå·QÁ~Å'ÆàD°ð’t5¢#àƒ–# (Ù”ÙšÕ›ožyˆâyC€,ÐA±þÐgü£ü QôAº˜+*ú‚ªÂ q7zØß’      Ñè}ø Ý|ŸòçÙŸJÅÖí ý%CÜÏ¡Ö2yþh>mù?Ìþií$qÈíAsô/òüÓöÿ5®+jÎ_Wž?Éìÿ)ÛÁw1»†[®îô?TsqþAW6ÅùÂ=©½©8çÃçÝ’Å®~Èý_¥ùÿΖgT—¡†>¸Ûì±Zdú‚Þ™'÷d ùûá¾å^ÏG/§®Zò£Y |'Ö ôÉy7Ç4îæ´f¨>)›ÿnÞÿÄ?ù¯kà†úŸbëzG`é9ŸK´ÿæ<—‰êrN?U/OÑäŠÆ§´­bJÀáËÀ%ÙYÜm|¸ý»qM§&ÁŒûÂôý^ô—ÏÁ©Œ±ùKÕörßoªãó=fû¼Vù«×“/E“q4•âçz ŽÓå†bØpë‚/óCñø-°c÷rE|0õ}¼sgÎ`Å\ƒ.B2[ݼ œ>{œîgŸØòW¹)9"“ç³ç2Ó/·9+Ò"u?»¾Cø»‹2𾽆‡Em§»-$r#>ÓËÚ©LÕ¶ë&Ôïúãí@ïúãí@ïúãí@ïúãí@ïúãíAŽ8ÿX}¨qïµ{Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûP;Æ~¸ûPBÁ×Ù¢èó%ÇÑè>`¬«ê «9Î^Ø^cxiág\|©Ö­›fÃ¥YâÙvÏ—Ûàƒêu]#ëDé›Ã!cKÇ‘×4OD›·fÎS=ó¾FA{‚&¸ì1¼ è{b+v>P,T3¼×n5’‰õCŒ„o^zA˜{_ẒѱÙë:NìZ޾â'zØ;Þ·ã¤ò} ¯µ1^ÍËr0¼AV>7ïhÎÓã߃±—oza­±4eš‘Ž‹KOBƒvšH•°Øm:ñ ÇǨ\^ãö ×ÚfdlCqy(cœn)實8k{ÞùróAv€€€€€€€€€€€€ƒG Eôcâƒt2˜ø2ØË8ûM†ÌNãØG_ú ü˜ÆXÀæíc¬m³T”°‘ã£ÈüFŠ Ý¨íûEb¤¥®`‚³csO‹ÿ¼~%Z×›weqï^^Ûòs‚0`ß‘xÔ¶ÝêoõO´íEý<ä¦ãº¿ëk‡/ŽO$}Xöü ó¯Y¬ñ´=ŒYb~¼rÌVf¬8Hï¢ýS̅×Ónw^îŸöòô·Iýʶùד…Þ->ÍîdÇÒðáÍèæ¾5þ:¤Ìþ縭#5&;¹' áñ~ÖT’—iïE#KI”¼lx;˜^î EñÄÂ51Ýìÿ&½©£Zƒ°×§dÂéšíõð;^gñM{[ܬoåÑŠñ©z’æG™Œ{Kv4y(Áiã{cž¼÷†õr‘eVY‰ÓOv×uð^§Üd‰xž·°Z>ÏK…“»ÊïïÓö/[/ZËç½,ë,=zâ{‚öÊ󲣃ï#¥§½ƒ£¥v¸Gï½zÞSÞ|ÿ‡ërîükãü³mæÔU0ÐÌ#DZî,ï]ë›ëHïòƒûÈZcŽ3l³ÖÞ#ãâ?7IçZ`¬êžgæ|Ïä‹Heêc˜øíÓq!Ð=œc»ðwðökLžÍïÖ>¯žÝí–?=ÖßOÇ~Ÿ:H‡+ù 6¶AµrÉÇ‚Cóh<ÀöµVØ}ÝÛëhùé¿ÏM+ŸØÕ2êÔŸ¾ô¦Ëz-éÜÑ–½w±Æ31â ½ƒåìÙ[R&µú§söcîŲDR³ŸŸÙî;jÍ®Ë@ë.s‹æ1ÎêZ/ú‚ò=mk\ÓÅôŠÖ¶ÛÓ.G`€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒGôAÏöˆ?ÎÒPt@AÆJKPT³rg²u Ov'¸x ’€°zG˜@ØßQ˯±Þ€€€€€€€€ƒ‘þÚÏùgù„ãèƒt0VUõU„O¶¹ Õa•щ{ËN‰Í—üëo|\Q™uôÝÛLŸúµ¿T—±Y+>Å9¤|kDŒ/vÈç¢=ÝzïOn¾7´ÙêwfdÏdXˆHàÞñ…£˜ß]¤c,EíæFõY-ZôY]ó4í…üEÄã¡ÕçþŠÃ ¬.V+xÙgkF=Î#Üû§}NôP.E4}¾¾×fŠôš±ÀÂ% ØsAx#‘çËÍ28Úô;ÚIaÊþq’È/šOWÕ~€×«ÈrÒKÜÑwf™Ó*­dœnáhih|QÔµk™Æâ¢Ê3)FÙ1Æ×f€5»ˆ|æò×4Å@‹èÇÅè>%ùuìßuj§h n›7õ{ýaóOÙ±ðAòü..lÖb¦2¸Ü–¥lcÙ¾§à6P~œwe«Ö£ ýF ¬kOB×ÁoL¼zK‡?¤ç3j÷RÚ§$NîìDZ}¿ŠÚÕ¦XÔ¸i|ÞšÝ:J •9Æv<ŠàÉè­iÕë`þ)KF²Æ¿÷è‹$‹Öik‡ˆäWñx´=¬>§qôNá³,Ý€z² Z<9ý«Žþ’“ÖòÁÅ—–í¶^Ð1–à¯Ávðž|¥o—¼x.I¼¬ö–9½%oÇmËæ–jØ©!ŠÌ…ãÁí!z‘hžÏ2՚Υ –Mp‰¯ âš„n^Ûòg¹ùùןZQ]°¸w®ig§5¶*îÛqúÌœqÍ|ËìØ( ¹6;^¬`¸ŸÜùgUpzJòË¿‡¬\oh@@A -Œ±z_› µæ|Û¥|tœ—ŠÇ–y/é6Ÿ—âbôË[·XNãbÌÎvŽŽô´ý¾ÅïZxWéø‡ÍOû™"-mGy••»4ïçÚµçŸNƲ49’ïfùŸ=,é[ÓͧVŸ“%ñäÏ"f•tÿß/IÚ/ÄKŽÜ%ß/~üò\SŽñ‘êÆ\s‹«ÎWÙnJz͆´Q÷â0ßZRy0‘æãÌ »mÊq{uÌôü¾³Ë¯Ïm]V#ŸÇ÷”Ûïä²Ì„zm·ˆ#‰½"oˆølñòVÇÃ>£¯æ¶²eÍ6¿~Ñþ>Ž£;8G© G·Ú¼;Þoi´ù})¬V<%*. Ñý@³ý¢óŸô”Rö·3³¶-Nl¼wMumqµÎ ä5æƒåÝìžc´˜3šniѾƒœÚ{œâÒßXúÛõyŸú ô¿’~Ð6Õ{©æ·5¾'X.•ÜL :®É;Aô_Ù'ÿ”ÿô”°Y…ÁKVÓµ]”cÉÄIÿí¹¿¤ÜöŸýH#à$ÉbÚLƒ!l·ÝVµéb“z%Üosyx†rZüá›+ˆ©A±Ëè_jW»~¤ ¯â?j Y‹«QïjØ£]ÁÀ>[®"67Ï‘>Í„U»böaòö­6­¹q’¶6¾“÷‚ð84NõÌèóA5ùÆw'œôÃ^¿z#¦×‡~®ÜtGA´‚öjò™¿@eHë:s`þò2ÉÑåã¡ÍFçsT࣒ÊAE´oIf8xûÚýçÌ$žNç t^H-1ÙI®e35$Ž6·3#Œ·{p1‡óó>)ÙÚ¬”ø^ÏÚ‚¥gÙËÊbs\ç l ï_4%Ëã,z½N£lÃN¥ìªÿ bŽ÷ßZkšòDÇÂ[ÂcsAiZòA–Ž‘ÒRÇÖ¬÷r.Š ÒG– è4z_F>(7@A„A‚tƒà–^×Ç›Í3JPú˜ò{Ç4ì>S×ì½ûA?òÙ“c#g´V#ý`a®Hêóóˆ÷_pA¤°E; %c^ÓàB˜™ŽÊÚ±hÕ¡Oo³‘»n­'ý×ójÞ¹¦;¸rz(ŸÁ:SÙÅÚ€ö¹-óÂו/Ò\^Þl3¸éù :¬Né¶ŸbÆÞ“»ttÓø–ztž¿›‘¤GG…Ï>†|K²¿Å«üÕý\䯶qÃ3"‘¾OoˆôY>ZOñLSü²Ò>'q •ÿîÄÑÿEµ}]ÞvåÉüNf5ŽºüÖµ«I;Û xö|‡àºþšWO6"ù¯ó/[ŒÇ¶…~ñHîorã½ùËÙÁ†1W^SUˆ)ûSŠ—3€³N®Ìèõñ[ú|‘$Z{0õç&9¬>iYðÑ»YzNl°€ÇC91‡Èáÿö+Üå6¤ûsÒ|Ãçm†#$ZõßÚz= Ðе,vðrÉBß"j‰;±(ÿ¸î„þ⹫kÖ8æŽUùïýÛ^˜íh¶¦ß·ùOdyq7mÒ¹>>è–3ëY­#{¹æóæÞ›öŽªñš•½kzê|OxeoO“%-lvÜy‰é0•ŒÜðXíEÒÇÊeh‡NðuÿëĬ²ý3ž¼þM°}U·«Éß´GßÈ¡ùÇ7g.ò_Pb…Çûï<ÜïÞOÅSÖäãHÇLJ_¡Å»MçÇùòúä”õÙ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A£ú gûDç?é(: áz»­Qž»#t‘¹­y` #@èò(>PÿÉ×i±’ÕÇSÈLú—¹^}wÇ=zÀ‘ÅêóèƒÝö+³ö[5;¢°];žÃ|<->Þ§zß± ™†H$Œ ØæûFy)»f|N¡µrQˆVº[½M"æŽ^mÞº”u1rUíW%+¢î.ÇÍ󃿽òæ‚›°Øñï܉«Ç#¨ÑüÞãËÙÄâ?ò„]¢Ä[ÈÙÆ[¨Ê³º„ÎÖ¶HŽ^&ë|ÑG$Ù;vig!½b¼re±šì<09=@ {ýˆ&ÉC3šÅÞÆæÛF¬@clµ÷8¿õ´à49¤AK5zŒøÌÛ(²Vt–´2HHÖøHÓyoÄóA ¸<åØ(crrÒô2Ç#¦„¼Ëg»ù€´3˜ó=v“š©šÉYÆIEÕòe{§.á¼$€œ47¢G4¨öbÝJš¬é¡s°óÌA>¸-p<ºúê ç +ûA¾é ·A•@âl릴áâƒ=™¥’Æb!ÇdØ¢’¸÷«G§!´è9í¬ÿ–˜A>>ˆ7Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñAºùVí;û7Ùг:+·\!…Í:s|\áîÍÉñ¿–^×câlRÏÆ´hmß5´ó•îÕfj¾¨ž*Qüü3ÿAO™t³ÔØvþòO{”Nk/_EŠ;õXÁ^*ìàŠ6°{Êfg»ª”­#U‡U ˆ"߯ÓÉ@a¹Z9Ùäöô÷z^ÔÖt¥é[Æ­y äùðñI„¹Ý´šÖ=fq]øýwÿÒ?¬<쾂&>í/=5Œžø’ô3ÑœðîH¤o‘>_»k8ò×QÖ?W›|91_”n'û²mGQ­8˜Ë'qDƒÈÖöG—@µ´DÏ)tïöeŠ—§}7¸ˆù}S³Ø¦á°µé q1»ù¼ó?½x²NL“gÒàÇñÅVk&Â Ñ ?Ú ÿ9ÿIAÑtê߬ú× dð¿\Q¼lsA´CVA^6EmádlhkZ<€AÑ/ÿ5¿òÏó'ÇÑè>`¬«ê «4z_F>(7@A 'ˆÇfjš¹*pÛ„ÿvV¯hòø ð÷ÿ"=”·!}w]§³¾¥(9Sü†vb¼¡óÙ¿i£û‘­ÿKAAï18\n˜©‹§XG÷cn¶|ÉêO½ä¥¯ñ¦²1ÝZö‚ÁLLÄî1”}œÃãìúMLtKàðÞcÝä´¶|—ZÝW:NëVK&Ì  ÑýB“Êÿç?é('p·È}ˆ-òb |‡ØÂß!ö p·È}ˆ-òb |‡ØÂß!ö p·È}ˆ-òb |‡ØÂß!ö p·È}ˆ-òb |‡ØÂß!ö p·È}ˆ-òb |‡ØÂß!ö p·È}ˆ-òb |‡ØÂß!ö p·È}ˆ!Xd#ÐÿíæK¢ Ð|ÁYWÔVah2€€€€€€€€€€€€€€€€€€€€ƒG Eôcâƒt?¢R’-× lñž_ùJ |rþËø8åý—ñ qËû/â@ã—ö_ÄÇ/쿉Ž_Ù¿²þ$9eüHrþËø8åý—ñ qËû/â@ã—ö_ÄÇ/쿉Ž_Ù¿²þ$9eüHrþËø8åý—ñ qËû/â@ã—ö_ÄÇ/쿉Ž_Ù¿²þ$9eüHrþËøD˜¸ßgxD|wâL¢ Ð|ÁYWÔVa’í4ù““¸÷Lbh9úþ!ÚøuAÑݶ‚ •Ÿ)` –F8k‹ûÚ#{A¹©;BÛI} ïâ<¡îîéñÚià‚†÷i'91xŒs²áÌ{Ápï s° •‰½—±4‘dñ-¦Ð[$v°r ±2ÆÝmíGCg©A—ÊÈõÆö·ˆèlëh2\Ù 2ƒ%Á£dïA‚à7²†ù 1í{Cšàà|AØA,fNì=¥ã«wÏìAº ÑŠ hfŒ´kÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨ü_´oÚßÅûFý¨5|Ñkéö ˆé#uêÁ¯ñŸû¥Š ûOc2ñ8Ѿð‚\Nk›¶G±D0VUõU„1‰µÉÍ’žÃ#óñµÍ$¹ÜöÃÿè ©’­iet°Þ…¸ïRì=¾Î9üKf=ù©;¬düm‰ow7¨@¸¼Açö úv::ÑÆ÷q9¬Ÿ3¤k±§G<¯JIxøºô;ö íŒÌeßÚ?ÍJ´â>Œgk«ÈçrâÖ¹€‚·±˜ZwkK~ä~‘,çûÂH„ õG™<ÉA¾G´²äòzí·)»$ l»"ÙÈ5£ÃÏh+nX™ÿ“\Íie|¢×VŽG¹Ìl­ÖÏ#¤ݹv°µ9ÿùõÿÔ‚6V„?ÊzÖ|› Ÿq “Rr#Ç^ÄÇTf/¶ðøÓèõlãņÆÞm†N"Þ <7×H+𴱸‹tiçpþ’Geì³&ù0v ýW ÷è4z_F>(6(#Åôaè5Döúßç?é(,ÐW\ÿhÇÿ(ÿ0‚E^FOóä‚B˜+*ú‚ªÂ ó]³£nåZέæ¼—5ƒdlr:Aä?4侯µ÷Nü_vG~ ±žjÒÃÍ&F–ìk¯¹¶ðAEìÓåÊ;)‹ÈË·+Cf,`{%§Oµ€µW0r×ò½c¸îúÆÖ·{è=¨%à°íÂÒ’³g3 '’n"Þq»zø …?gmEvÍœF^J ¸î9âî[#KõÍÍßÍ%QÙŠ#³Rà·!†Pxä'×sÉÙy>{æ‚ ý“»‚²Y鬊Ò2HuXin×Î:å¿  –§-ßÊq·Ôž(ç½?t%•œb6†—ñ³Ë—½›Évî&*Ïn–W#Á¢ ‹—n<úóAéû=Ú ]¤Æ²í'ìOŒüèÝäýsAaa¯oV°‘ö ®ìÆBÆS³ëö‹]=Ší|…­Ð$ù]ÚìH°cï&ɵÜ?¸Þ¸LšáëËËh:ä»KCeõå&–(»é[^KÝ3õ¡Ë¡û xÜŒYJ1Ý®Ù2³¼Œ°¸xà‚®ÖâêYš;™]Ü'Ž»ß.ò{ÀÐðß—Š ß+~sLò@gh %¥€½ôñAY˜íe|v'%rä±% {‡°Fí 4Ùý_XsAk¼2a±Mw.âtnÿÒîh%  ®¹þÑþQþaŠÝd÷ä‚B˜+*ú‚ªÂ&¬еÝ3¿ àpúÜ;Þ·å´hô¾Œ|PlPG‹èÚƒt´ó•îeoãDrÇ5w…úÓñ=GÆvª†Ss# edTø»Àýl€7±£â‚v3'SEŒt1LÞ&‰H ëŸ= áÚ ÀÁáeÉ{ñn™ÅýuA:Û%Hì8†±®æy  éÄÞ-Ž»ß$ö¹¼AÀ·ÌH1ÆÍÆÝ» ß3î@k‰À–õïH3ÆÐî!Äz7|Ê øéL~NžR'ËN^ñŒ‘Ñ“­sþè$‡°¸´8­ß1ðA—85¥Î 4 ’|2îÝÓ’I=½ GOž¶Ï‡š Yòv&?¸ÇM4wïHi€Ü¿§±{¾'4È’z á~ãhѱd€÷C¤ âÑwÚ8\§ç|5|‰‹¹ï˜\Yž:üMkÚæq5À·Ì„.ÞŠ)­?ol1KXG€ë¤b²Qeñd!cãŽvñ5²k`o\õîAÆŽd]»n©£<™Ô¯‘Ì#zØDžcš arvrÕ…ÇÒmzÒ·ŽÞñ9Ãg¨×/>¨&Çj mÍUܰ™ú¼]?’ølËc"»Ý÷]éw©Å½iÄÑ<†ó ôgˆÄaýþÇ ;מüPvØ;ÑÞº ã4òG4 egÊÙ\Z÷´D5½ýœvØÞ¼|66±æ€€€9ôAKrå¡njØ·KZ»ß fI\Ó£¦‘ :øø ¹ß-ž^{ðAŽ\úôAŶà}ÇÓk÷4l9¾M$ü-[‚”BYßÀ×=¬]\ã >Ôtò6ë žèÜÇ8ϱÂÒ&ë®ÊÛæ‰èƒ;ýˆç¯ºA#oÐ1¶npq÷:N ë‹CzA½YŚϮö6¿‡{ÖÆÐvñÏ¢õÍc[Ø@@@Ú]랺ûc|õâ°ƒYä²+>¹dŽcCÈ<`tp×A‹¶…L}‹a½à†'Iÿ N·ðAµ;"Ý(,pð¢lœ;ß#hÛ‚k3Wû’¿x5óx†ÇîAÙ69sëÓÚƒ(8Ùž*õd±+Ãb‰…îwd ŒÉ-ª’0í¯<@û J T¥¢ÚÄäddޝRÑ3:6–5ѹ 9ëd ùÞk°µ.P»˜Âd-d%uŽUý·›ÝÓž z ö݃ìk{-EòNþòý–Žøƒê°zóÿ¢OlN`ÉßÉ“ì^z‹;=‰Ä<ÚŽà®Ø‹]RPíx¸·_½_|cì$’5l¹c«D.!Î.:“\<:<[ßïAß3?æþÒY¯ é ôœ|qÛ’*Ž´\á°4óÂO^»¹ õ¸ãݤÜ\½í8âlq;žôßWžùï—4VŒÃá2ø;u¬É~i¬÷1²»Þ- I,ppþðg–uofòøK>÷¸jI3#t²‚ӣ ÑÑö ‰,V²–í{£§a¯±pÉOŒµïod>` öؼlXª÷>3êíÌsÇ^D‚b ëŸíÿåæH­ÖOxþH$ ù‚²¯¨*¬ Ñè}ø Ø ѵè<jì»ÚKWYÈd1oŒ{^9D9dìå[¸–r9*¸@ñ“a®ÿª åY‡i18CZ;U ¤;ºòÌbŽG€A;âPPËŒŠ.Ãã3 |ͼëB1(í­Û€ËZAè©ãáÁþQkÕ Èç¢çÈÂòî7sæwí((PÈg1öîE^ÞKÙnpIzáðýM|Ö(Ápq á—…Äl랈öí‡ì…h*öK#•ªÀ2Ldìcƒ‰:êíw£Ö£ÙÜ.j„Ï9Y¬#»Ò]&ÉÛH߸|P}´‘Í/fò1ÀxêïÐO.cìÚ)O-jŸa)ÚÂZ§¢Dãj)GË·à<ùíL¥¹o[ìe©È2Ï/ËF'‡Á´ðõ³=·ÎGqÒº%d­‰¯-i@N¼¿ê‚,•*dò½©·“•ͱOˆW&RÓ@: oŸ@>(#ÜšqØ®ÌÕhý‰\&i“»kôîM.ðÊ l~+!flº¼)ÍEîmX¬÷¡ ê<·Í½—ÁW—²É:GºÝšRÂ,ºcÌχ4—³]K³T+=ñ½ÑÂtoi;'‘PB¥V2}¥†hÃØécÛOCú PuìeJõû1FHaložºBÑóÏ™AÇ¥ý1ËËè±÷ºÆísisôQc*ÇØeZÃéÑ9ÒE?âf¥äׇµ š7ÏÚÛвWDé1-k^?¸KÜ6=È8`kWÄ䢡kkîÜ¡ys,4kˆóèy ›šßçÜ?ÿ*Oý§ ­e°ùfM“ Û"ÍÍÁ‘kÉ{ãêµàøƒ[-»KZIqž¸>‘Ií³¼\ò>#câ‚¢ÕÙ/~wÍÒq-¥CºªGë¹¼opö€Z>3b…ÔØì~S+Ž#^Nuƒ!¾ëdHÒÍ¥¼<†‡ôƒÖö‡ýÝÉrÿñdåÿ”  8Ú¸è{=v³ -KbK7â•®ŒñË÷ 5¹«ù’(Abh¬ašKnõ€há-hi×]ï|ÐMšƒ¯vS({ßþ˜ãaqà•Áï˜ß4®Ô–÷i¬ÑÚ÷+Ô«`‚k&ÆÓ½¹ 4ìò~A}‚‚Ý,=j¹ë1‚͉8¶åÌõ:Ò¦íÇÇ‘„6:VÙh\ÑÉãâÒ~ÄùrÍ[?œ©!›R´=4^ññwîA&Æ>ž!…“‰ö,ˆdáqý4e„’ï=h Ò´‚>ÍvÎvƒlÛæON\_aظÿü4_ééÖ[+'!Ä™@`vÝæwãíAüG³—ÜvèÛšq– í™ÞX=§c—ŠìIJµnÐd¢š£›oÜc'`xsAy‰©^”xöVµú{ƒF¶ã2ƒÐ Æ‚‚4¡¤R`.ùk—uFÝx’hÝ]²úá¡»i$kŽÇ$ŒT8|lt¡sÞ\ç>Cë=Î$¹ÇÞJ º!H2€€€€€€€€€€€‚ºçûF?ùGù„+u“Þ?’ >`¬«ê «4z_F>(6(#ÅômAº96;.a7ê¶~áÜQñ8OÀ ]Âã²V`³r«fš¹ÜO$Ž{ð>a'…Çfck2Y?ÛIØ-÷ÍvoÌT˜±I‚¤Žã||NõæN÷à67Jó·\™è ¸@Ë]VSìî#zKµ)2)å9Àž‡¨Öôs¯Ù\Kþ:&NÆôÓæ@P[ ¤Ÿ±±aÓÉ‹‹Çg…Îh'ÜM~#©UgõºÚ$w}:hûTׯR«vÅØ` žÖ»ç‚}}|uö k³Xk·ý>Î>)l¢÷oŸ-lŽ„ èì1ø¦bÝMŽ¦Ï›‰ |IÞý»AÎfñØgŠ¥&ÆÛ,à—Öq/o–Éé͈±4aÅ~kŽ»[O€³ºÙ#„ïcgŸ‰AÖ•*øêqS©Š†˜Ðw¡ñA®JÄÖ+ÀæI8"W^î?x$ •V¬ªÇV´b8bo  :Фü“r&-4Þ5înÀé°Ž·âù¢‡æÇc=z#.#£³³Ï{êƒiqµ&žYä„:I¡îíŸY›Þ¿½*8<~:s=h”·ƒò9î òÄè{Iš¤'‚yc’³‹âvϪâ4OØPB‹³Ø¸m¶Ó+#ÆÀéæµÞa¤è‚ 7ìÏV¿j2\ž GÄ’z €Å~iÂCIá…ú.›‡¡sŽÏÞ½ÁjvFËlW¨#7Á·¹Â=õá鿱Ùü]»Ž·=@ù^ãp×N&ƒ§|BÑâéEèú"¿3ú0F>^h% †q ¨šãÑçsŸ$|GÖ$ìï}Pv닦àŒwæ!ÏÌzûPC±Ùì]«N³=c#ÞàçµÒ¿Ät%»ÑûvŸJÅø¯Éôˆ€ {^æì½cÞƒ­ÚU²_VÜBXŸ­´’:sc˜;ñAÎ ]:ÒÃ,qòÌQ½Ïsˆi;#dóçæƒš(~müÛèãÑ7¾ëˆë|\]w¾¼Ðv’¤Û†Ûãx„oÙõCºýºA NÎ⤸mº®¥sÄŽá‘íkœ e­¾éÛ>¦ÆîAO™ÄEŠtÌõhÍv³§M±àIýml ã‹Ãõ ùhc¦¬a’´³œOÎÐ'‡Cc®ÎÐ^?RH+ÂèAŽ«ØøFϨ[óOÃÚƒC‰¤rC#Ü–ÚÐí{€v††À:<¼ÐoW'TÕ¹šC‹I#˜;blm;ëÚ–¾j»î^wêo—¹{ŸÅÚ¶ûST’QúO]Á¯å \ÝèŸxA!˜Úq¾«Û¦ÃgÔi#¯PaØúÖž`iuÀûÙïâ‚%*±“V†«È„„éÏsµê‘È’t=ˆ.öƒ(0ƒ(0‚¾ß<Œ~Èó$Vë'¼$|ÁYWÔVhô¾Œ|PlPG‹èÚƒt¬Ú¯N#-©ã†1ýé?zµk6D+kEcs.u2¯‡vá°ó»·‡kÞ¦Ôµ½møe%Qg(ìÃ,Òít$hêÝüÌLFåh™Ô15¸+ËR¿…öÁ×Î:'_`H¬Îõáho˲…„Aa w`¤ØÝaüY[y·;;  Á kd ôÚ1Ü‚[“Tc÷5pÓ#ty¯äPw@AÁ× mæRsõ<‘ºF³G›AûÊèÐÚUmAv³,Ö•³C ÛÓ°B¨#^¿W_Ò-ËÝÇÄ4ÒKœz2Pe—`}Mâp€0¼¹Ì- ¼?ƒ¥yãµ^;»Š9Zî „9 ®¼:  Ù:8>ä܆›ß©çkœÆèó Öÿ˜AÝ .AØ)½úšÀs£n¬­óø„­n½ÈŒµ¦l¬s švу²6-שšÄÌŽ2æ´9Ç–ÉЃ² IÐPVI°?]Qûö ±ŽFMe‰áìxkšv< º áRì¢t•߯ÆÈèÉѳNˆûPw@@@@A‡tA Äaí „$šÄmqôÙCX6yƒ®[òAƵÙí֎̦|R´9ŽÐàƒ¯{südßÃø w·?ÆMü?‚VnÏR´–g¿3"‰¥ÏvÐ|tlöÞÆ½·f-p]ÁÍpu¹0ÿÓø Ï{südßÃø w·?ÆMü?‚1_šygŠ+ó9õÞ(Ð$ùy íÞÜÿ7ðþíÏñ“àÞÜÿ7ðþ0&¸FÅÉ»‡ðAžöçøÉ¿‡ðAÆ{óU0‰¯ÌÓ4‚&rn=ObÝíÏñ“àÞÜÿ7ðþíÏñ“àÞÜÿ7ðþ8Ù¿5:²Ùžü¬Š&ñ=ÜŽ‡À ê&¶æ‡ “hŽŸ‚pÞšÁC’CÌoá#Õpê.¨:÷·?ÆMü?‚ „–Ïÿ™7îüw¯.qsžïœç’‚uaÎOóä‚B˜+*ú‚ªÂ ÑŠ Šñ}Pn‚±²ïkìúCD‚•hÌ pØiq;p|€ÚèÜ× kÌõýœÚ‹fø†Ç,Ç7!T¥§jÆ^)#h.ÃO.¼ÇŠˆÇøw;‰•½ÏŨÔÄ2™ˆÄNÙ5%™k¶WpX8zÊkJÍíâ'ôE¯h¥gçN6rÓÒvvPÿD1÷--›š:‘Ìó*kŠ-Â>{¢Ù&¼ç㳪÷ëåpž•{Òƒ¬’w[ÂîíÝ5áïS[Vi~1®ŠÚ¶‹S”ïª=¬õ¹$½%{3FêÒº8+2›¤l¼=xœêwÐ+× b"&;ùÚ¶ËiÞ§·=(|–h5ìs«É$AÀèÂFúí\s;"wyL]»ØÞÅAq–ÌòØsb…’±¼1¹òï`lõß2¡+fMš¥NÕózÁí|Mc£{G. äÚ¯ìØÿÿ©WÿQA]‘ÎÙ“)‚ÓÔœ"¤é„¯áÙã<'C˜åæ‚]Œ«ìPÇI6F\\ö!ã}xkw’“Ëz;ðAgrOìÍ[QHÇ[9T½ñð‰nË|7Ëÿ„òOËc¿6T‹%ßMnáóI FšZNƒG–¹~ôñv/C·‹·lÛc Žxå|mkÇ ´ðò=GÏÃf^ÑaY é+‡º]p1§„ˆÏ>cŸ#®h9GVõžÖeb¯uP!¯Ç#ckœã§k¯ :“Év’çô{ù%ÚµeÕä—ð†q80od†ôö ŸÙì•›W-ÕšYìÃXølÍXÂço{i GP9ÄMõÍ’ZÞ›èKÊ;+ŒÇÀùçiŸúŦ×c¤Š-rÓ@ÑçÔë¢ ,<¢l{enHdXâKgákN¼ˆ!{’ËeoÃS hÃEí…¼15æG–‡î/`h ª¹jÖg€¶lšÒ>ø‰â64#xÁxØör9 ô¹&¹¸+m{ÌŽdä\x>H<ý9²˜ÜFã¯÷‘Nè!}^é¡c††×c’ â\ž_%ަCС£(l-xþIw‡044ƒ…ÐY±&&Õ‡6:Öà™³0M–>{®ˆk¹ ‹hr‡ pË#EÙ] ©úƒÕlÇLñ×4}¥l£³îã{+<—´ »M;Í ß_§ÄІó>Aíkg’&þ…‚>"Dèhoͼ}›•³’âmÙ6Úk‹Læ¸.Ó®GÀ‚ƒ­ë¶!ÏâªG&¡²&ï[¡ëp·cš–2?KÏCL d©N­äç7gßî(9SÌqw¹V÷מÅ$N¡A›¾ì†<:Ý‹¶xlFê.‰݇5Ü#<¹“´»f&8b°èGphˆãåÕ|k¿Ò<5v_wÜYâ²è›Å¯Wzh;è: Ãówqt3m³(¹.5ìl2¹¥üm¡Á¾Dø Î++qÙªõM»7áž7w–‹ ¼ êžcGh-óV[Z“K²>€_ hF$s¿îµ¼öO¸ ¢fvó0ùòÛožL{àžX;··~³HùéÇYÊc¬c,Y¾,ÅzfÃ,=ÓZØÜæ’ çËZ罠癜Œ¹cLWm+/޶»‹MNØéÏß4Ö¾r™ŽÍ^sC=Iܿހé»A¤9›îÃÂØ¤¶í䤪ÉLcQ´=Þ·äH ø”s6|f.wÜθmÍá³, Ü~ÀÐ4â|9 ®ÇdmÙ·‘Ç·#<ÍeF͉kwR0’A-ŽCžE¥5Ê…¡i·)•Õƒ[$L"6¹à9sëÔóAjeÈåsëU¾ê0Qs#‘5î‘å¼Dž/±È †ìÞJÖ/úïŽ 6nº¬çƒ‰¾¯$ínÐNÇOz¾~Î*ÝÃr1Y“Ç#ØÖ¸mŤ.ú„~ÄÿMé3¹„Иðð |ö Ö9r™Y²S¾)ÅNgAM…®-eûç¢N´5¤¸\‡ç\=;åœxÚç4xö ó8æå"ÁäoTÈEkV^È;¦–ÉÂòO<ùôå­ °›!Èe1õiYa»6îí®sÇÍß=s䃫¤È_ÌM¯‘}hèÃ’fÄ×>g¿~c@hxêæ¯¾\¤HÞzZøZ{À<]:{PF´×[O+fW‚±“xÌmÞ|'Ÿë ívîF¶A•òY1Ðw1ˆ¬¶»]²k×ãqo>ƒAœ®fQ™v5·ç©Ø÷Ë^¡Ò=Ý:4€öíŽÝŒž"1ÎK˜ðXY½8¸O1¾¿˜Ê–]s<ùoË#cìs mÿ£=65ÓCÉ*#%ŽìÆ'".ñD{ˆÍ^é¼<!¿;¯=휎NweïTŽìôÛL5¬îi™»Ç–ïÖ ƒ\^b$–þ*­«˜%–0^ÂÒ8O#Íš¿ùË'ÙL–SÓ!s%k*÷MáîÚKy»¯-í­÷¶(©CùÚJo|ˆ ¯ßHþCž´HSMw'ÅXu·E#²-…îd`qòˆ=-ë§>h-²dÖ}zÏÎMÝÞËbª$’OûÄpA#³Ö&ÉàëÚŸFWq5ä7‡e®#zðéÑ]j7ìeóÞ‹|ÔØi±µÅÎîÆ·ÅáîAÒ,Ü<,0ÈÚÓä£t’ÌÖp| òÙ:ëÑÏŸ+Xå1ñ;ÓmÖ®ÉëHæ¹áÄ94uæƒXûX|ƒáÎMbF@K™%vG,“±¡­ôçð(#¶ÅœOeñ„[|’]î£cÝwpÒÝ5£nз²ƒh36*Ô{=Ø*VïážzÆ]ÌC~’ ^£’ŠL—/úP–ìEì15¡Ž-'Õ#èÑAu›°ü^ÅÈØ# éÄâ7ìÙA ·!ŠÈããµxÝŠô¦‡DÖßÂH-ׇ"4PAïs1™Lƒ2"FiÛbö°ôq<ýœ´ƒÑÖc­Ñ†aú7M]°7ÂHÇÞƒÈÅZÀìVÄ·$˜8N;·± %;vÀß?±«[’Çä1-ž÷¤GyÆ)"15¢3À\ Hçá®h9ºÖIØü¤•AtdL@Çs™áÙ þó€>(,°œk>Hò‡"Î=:6±Ìók€Ÿ¼ ³h ìÈôƒ¼N“Þ?’¥Ì•}AUa@‹èÇÅÅx¾¨7A]{ûz¥§T¶Æðq†µìëÂæž¼Ö´É¨ãhÜ2¶=Ï(Kl4¢Å›î /³Á‘Ào?ió*m–5XÖº¢¸§s6ï¢;;?hÅNòŽ’R1ñ4BON#¾|¹+{ѹ˜¯u}›j"mÒ¤ÂC;²biædCCš‹4Ýr?½R2Ìq×…çO-ùrfÜ–éOs&g4¤â`†ùó>ßgEiË]LVºÚ±ŠÛ‰´ïMŽ&ä'~;#èÑØy$n„HÏRÞc[ø¨÷+1zï_tÎ;DÌÖu¿²Ô<\¹Ÿ5‹e~͹˜‰q3Ý2UÞëðÆøHw<[æAöx ‘[góŒW²Wý2Jís`kaµ¼_9ÄvHA#'(«°Êcî,G>øwÅÂw¯ŠÓâ.2ü÷1y/Cu­ã|"V¹Àh8s:׿H1c mÙr²}Í–Öå{à ï`lhí(û4Øñ‘Q$pŠï¥÷Žh.qââÑûzþäïã…ëTg2–zýðhnøýR5ìê€ÌpfnlŸzI–»!îøzp¸ï⃞W-ùªY­kѬÔ{ÝOxH#cÁõ1¦¶RÝ÷MÞ:Ó"io€,ŸÇ™Ù¦³ 6\{'­aÖ ²Ö ±Ä“ÓÄiÄkÅ…/ÃÆoßm¢íp†@# ýç{A!‡šÖNk®­<º&~Œ=§ˆìñyŽH#?³í ã’óv{-´ë]Ø×xÝpŽÕk[AÚ¾Û22äld̶¤¬`Ûa k=m‚ѳÓÈï~ᇶìœWò9Ù’¼oŽ!08µ²yô胓{7Ýb¨U†ë£³Ž%ÐYƒÌï`·|ÁZÚ &ždV`n^.ü<—?ÑiMáÞÆ¼öƒ¦#ù®¼­tÆyg™ÓK'`.=tÑÐrAÆп=¼nGÐÍ®ý®„H AÍÙ:÷ôA‘Ùø#§Œ« ®c1ó¶`HÙ€íïÞ\NÐXÛƒÒ©Ï_‹ƒ¾ÌâÖõ°Fÿz ùp—£é 2BðþŸÝøk|¶ƒKkBí›8܉§éz3°Â$@k‰¼ÆŽ½è9ÝìÅ{8*ø¨'}vÖ ²]q;¡ß´‡µkxlå¨Þl¦6ÔhÞRp‡ %ûµzŠr’Ô1øÜ'Å™Žþ¤#…±º¸pÖ€s÷Ï^ä2øÑ–ÆÉLÊb.-sde®ixó93;ò¯Z¸%ž¬r±Üp5üzçÔëZA¤˜,;*,HçÇ“àâh1ð´4hùòÚ”)å`”zfXZ…­Ðg£†9ÞEÎß3î-‹“!%KYôk4å2DóyFÇAO—ľ‡góö¦¸û2Ü® …Ì ´k¼=ž(&TÂY–J]È›TH!îƒOsõ´A»nÖtTÉšŒ’ûã”Cùp7›NÇ Ñ>~.aÁÃ^Ö2XY:ÂÈÈßpdü?zã³l¯CÞÙYiÖ¡¬åÅ׈æGµláoÞ¦ØíåûNÉà••ƒ[›Ómß­ö ëK ,9)òï3X€Bñ݆4I#|ºûPE‹³s· ÜL¹òd‰Ðà5¬w>{Ö·É›8‹C!5ÜnCÐßd4N×B$kˆƯ‡±Ùèb­Œ¯ Ïkqö;ý¸lÊíïËeÄ ìaüï6E– _%A]­à‡Díøõè‚Eg¯JlÙ6fctù‹CKÏž‚cD™È2}é*áëÄAÞþ!σ´,[~?&êq]wÑ÷!ä80ìh‘ïAgF¤8úPT®ÒØ `c;:)?£™VÍ(²ïŽ­¹ùÙÜ‚ï\솻~®Ç/4MÄÇV½ØßÀÚõMfÄËDƒ½û4ƒ•¼M‡d]ÇÞ'’1ÜQ  4ëcDlóAÊ^ÍÆü ÆGií”ËßúKš »Þ."ò=ûäƒû3‘‹kf1³ÒÐÝ÷¬'„ùsh(:å1y ±É²*vYÀø`çFŽ¿häƒYðrÇf;X»Þ‡3`lãˆJÙß›±±Ìyí•X¦Š³fH”|éx8¸tA_hr7gŠàî’ù 1l‡–ðì;}9tÒ Ùæ¿³Õ1ÉÕcïx9»»p=7ËzA½œ=¡‘šö3 )É`4NÇÂ$cÈä­rö ±‚9#…–S4ŠBÐ8ž¼PÏÙYŸJÞ:£¡ÇÙ/=È„0»™Ûù»ç­{6‚TØ; ÈGzŽCÑ¥›ZN(DÍ`‘¢ƒìÀ >lÚô˜§{ˆwpØñ뤿 xd=> ŠÄ°6Á $‚ÐOªyŸ40ø¦â1±Ñd®•¬sˆ{†‰Û‰çö ÅLP«g!?z_éÒ‰áך¯oM †ÎÍw8Ìux.:;XàD6;°w±¢Ýó›AÒ -˜»#Å´ Žàp±­èÐÍôëãâƒðR›V­ß¶,Ïf¿£“B6µœü6vyõ(9ÇÙÉŽ"äKßQìuIã„1Ñ ïj ,ÅÛ³RÍlµöÜŽÄf>àƒÔõ<ÿÙÛ’:¦eÝ;(JÉ"h€7‹„k×;æuË|Zä1Ðd¨MJp{¹›ÂKyíЂ KN½ZÎK#鞉³Ö#\NÑ;:÷E‚lX¬…H$]’gñð|ÎóÙ¾zA>¥aV¤o‹¹¬âÖ· ýS7³2Œeügçê–¸ÌmîGEÎâ<÷ë¾H,-b…›é{Þ@—¼‡|~¡n½vƒ„xk5a¶)ßKfÛ¬qºðÝ€8u¾c—T1x—ÑžÕ«E‹6Ü×JöÆ#oª4hßÛÕ—@Ò ¡ùò{ÇòAРù‚²¯¨*¬ Ñè}ø Ø ѵè0(2€€€€ƒC—1͸e hù ÆÆõ¾h2€€€€ƒ¨#h3ÑCw Î·T@@@@@@@@@@ØÞ·Í ×òA” ë|ü ™:ƒâ€€€€€€€€€€€€ƒ0üù=ãù èP|ÁYWÔVhô¾Œ|PlPG‹èÚƒtö36ÎV|~?Ë2Vc/y`DOõÂ4wÓ¯DÜËØŽð¡F“mY‰¥˜FØÚN€ÞŽÉ  ŒîÔ1Ô±ö ¥,®½3 æHÐy­Öþ(%ÓÊX“&ÜuÚ­;«÷íà—®õ¸HÞ‡NGâ‚/ô“¼¯¯LË%‹O¯UàPÍíäëÕo#æ€îÒ: ~JkTû»8Ð °¶^ æ-vºìAc޵jÛ_-Š~чrq9Í>.õO³š ²W£Æcl^•Ž{ Œ¼µ½N@9L·æË6œ0as-‡µÜ¼}_ÏÛÑ1y ´ø->ŒsX|l-À@@ÛÉáõ}ÜÐa •µò~•HEkzø™/^ÒÒA׳]EÉgrìí¼•z ‚>鯂GÌ8ôz¸·\µËC|÷à‚ú”–e¬×ÛÊz±’qyïAL½ ²E«1†Å*s%›¾ s¸~wuÌxÞt·žs-Ò¯F¡¶ëÐ:hÞ4¯œuÈhï÷iŒFQùf)ëz5š’÷RÇÇÆ7­‚â(!ÙÉåÚ¸ñðUŽJþŒd!ÓpñzÀ|ÓÌsñóAŽFõ)³®«Žô¨á½$’¹ÓráiÓFŽÎ†üZÏžŽ€¡[Òg¿{oxŒ5š¹ÇžºïA#“üåÁð'­)†x‹ƒ¸\9ò#¨ ƒ´§l¹|íŠ.±4TéÆÂöÂòÇJ÷sæG=Dk8¶ºÏøsÎòdšï¤”‰ 8 ZˆX°é¦k!ŠI‹‰{º4t*±–ÝziiÖ*ôêÞ®JgÜ} µEk#+8dãkÛ½ |M"#•gpšÞfxÚ5*~µòsŠëÇËvd«R~fÄŒ” Ò·¼<|\d´k„w¨ø¨ákE#å1’µ›Løuƒ)e¶à¯ *úVÄ.lÂAÄø]ÈhéVq׌ÍgzLd¶â-Ú+»Ch×µjY}z’=’¸ÎHiæZ5Ï—?§³]ÅfÝgíò§½i‰˜¯o¿ÂMŒÄ¾™ J5<²À,~’^ìp“Ë\ŽÊ¤bé3i×…ç$î"±÷Y@ç¾&:Xû·¸&qqpŸ-ø¬§[èÖ7®¯>;Oiô¦¿!ΩVI3ûð¦8‚ZÝzÚü>*³·rø?B;qÈÎ>ñöcŸ@9’‚#{F'ÇcìUªé'È<Ç/xo €%ÜNò'§TEÚI[#=ú-®1ïîÜØæï Þu ‡] ‘O1eù6còEI¦‰ÒBY08 qÈiÃa²;N'ÇÚ¼¹ˆÏ1†Ÿ/²î'ñqr$ÐxrA˱p:JOÈON6O3äÝ¡/åý#¶× 4÷ ¶‡,Ã>J+÷'Cœx·Åoóö ˆÎÐY±(êã¸îZ¯é&ÍÂØ£è ¯ŽZA—öG‹žÔ•Ël2¼õ̃ÔsœÑ°ísp!ë9_1OÝqzS$wÍàôñÞÐböGЯP®è¸™rWEÞqkÁ¤Ž^;Ñ!EÚx$~WŠÆcØçµåܦkv ¹zÍ#Åi29GU¯=\K$ïaF·ñA³3wÜ…yqìŠõ8D¢>ÿ‰iÞˆv½‡–vìåË×ðµ¬^‰­{âc„“ˆÉ±½‘¡Ãîæ‚=LžNnÔ]¤êÑú, Ÿ}¢ÀvxµÃÌŸ-òÒ i¦ôWdÛ'×lwÃŒ´ðÍ|Ýûwâ‚îņV©-§mÌŠ7Hxy’ß$ؼµËж݊1ANHŒ¬™–D„º ºòòAÒË'¢X›a£vA3™vÏÍ.f¹ï:AÛ+—¿‹X8±%*ú/”Xå¾$7^ÿ½›š”XútM©æ¯ßǹ8õërß‚ M™ºë¯¥CÛ3Á_`ºq#. Ge ¬Ý‹¶Rz514ÏÆÇ¸Ý7ëíÚ>îCš œFKó­'Lè G+á–"íð½§GŸˆAG4RßíËãµB)¡¯^2Î9ùF Ïé×S®žÎ¨%ÓLj»&1¤âƒÈôŽøqðï\|ù»öï\ÐI¹š±Tã)Pô©Ì ®2†3„’ÎŽ¼5ç´27ÙŒÅÏzf#ãsÔû>ß#Ê_m 7-㢎8 30ÅhHËzÞ†½üÂöŠÁ¬YÆô¯9ŒŽnø9Ís‡«ÄÝrûPmo?b9® XïJ†‡+ƒõÄZÁ¯X€}ˆ:Osä©2¯¦MjHhtÛY/YÇGÄëH4šÛÿ;b[f›£µ,SÆXÛ@–¾Z><ü^µ~Ðå2Ö½ÑK3L¬´DÖ‡vÒ!¯ï~ä’Î^h­AÇÒô«.€Làùx: »G™=¼YW|²Wö"ÊFßiòߊ%žÍZÈö^ôðãÈ ðcŽÇz8Ξ¾nÇžüPzvr0¶&ÑÇÇdÁs¤°#ÙÐì ©·Ú+³ÒÄZÇU7l†=²ÈAÛ:#ÏØ‚ÒÝÌ´La¯‹ŠcÝñH]h05ߪ9s÷ò$c/Ç”Æ×½\ÆÎÎ ×uoâWMž´é-º†3Ò«Ryd²wÁŽsš6àÆëž½¤l ÚÏh8dÇ2Slä¢tžð0 }mŽCG÷x ‘ŠÊI~KuìÖôkU$ •üm; ‚C¿“ÊEÚzÔkUŽX]òé¸xô@$ò:ÖúxíwïÕÊgý-†\ãy|Â0vÞMävyoÁÉ×»ka‘JEÞ7Ä{ÂÞÑìñ4rw’ ±Ù»y9Ëëã˜i ¥uÞA%šåÌtÞÐJÌe&“l»×>VDÆ—Ž' ¸ôÔeËX£‹žîNˆÑÈfw¤è†‰'\Ðb¦fÁÈzB“jÎèLÑpL$kÚˆÞ†ˆØA >ÔZ“ríÄE¬s8âùÚv›®`s;ñÒ ;9VWÉU¦Ö ;øß3ÞÊ8Ú>w·d€‚¥–OD±.?º¡vA÷À»nù¥Ì× ïŠðæ¬ÛÊÙ£WÆÚ–SÌùCZ@;¹ž}=P\ Ì?>OxþH:0VUõU„=/£âú6 Ýœí ;7ß# Ã9Ößê·Ùa±˜Ï™?8|9í,– ç*܄ظ³-’³"•ŽsZæ=¿Þo-þà‚@ÅJNÕ±±Ò޵§Ë4 {HŒ8o—S²:y íÚ*W¦nbã¹YÏkZ\ ÒÓÌù‚™ÍÿôÌLPÕŠèÆòuiH`[§s<·¾|Ði6Óös)^–C¹¤ìe+]k:`ðe;õÀi!£\¹uAÆJ9y°ê’cŸªRFÛ…†Jc[®D›ÑÑÖÐ ÁÜlÞë6Þ¦#¯oo'¸hë<Ç>œú µÉã,\씸èÃE‡UkqåÄå¿xA>„ÓÏU¯³QÕdŒo{\yxí¼Q ™|u;غ”b;2Jè,™šÖ°IÌñƒÏ‘'§Tkáæ©•ÄÇzT_]òߪ.¼ôPHÆR±[-—žVj;S±ñ x€Œîæq»^ì=¥¯’¯PÙˆÕuydkK xw>£¯D Xë0ÖͲHÀuË2ɬ=f¹€ìæ]{¬†O!FKTE8©Èeq2µæGðuáÌ•MÒ•·ÞÖÕïjòi¬8ÛlÀå*º0&±%ƒx‡0ï›ÏÁZrWÜ­¼F‘´c´yµ½Ri)V«&Û1ÀÐÙY;X螎Žö=áMmi´[]QzÌÖ+Çk\dV`Ç֊ܽí†1¢Gï{+ ÌM¦c³jDÅb'»Éã¿:Øìõì}J-‘¶¬Y– ­ Œ9™×3Ë{U]6ÞË2qñã†V›*G ’PÖÂæò.sO]òæ6yi7еWˆÃ÷yNé„qIÀCã²×ÿw“€÷ ïèC%‹ÈàQº8,»¿›¾’í‚çŽgeº(&á±ñÃtKý‡ö0Žý²0ìôÓxyèù ·ÇÍnzL–õaVs¾(ƒøÃyòçîæ‚¶„7™ÚŒ…¹hº:Ö#Œ”ÊÃó8¹èóÚýœ§c„†µ–JÇÈKA¬Ž#˜ö‚¯µd›)N*î 96šv‰Œòï€âùN½VÝLÔYZ5})¦·£I^บæ“Èùið™XŒœŽŽ8îÞ²Ë ƒŒÐÂÞtÙ ëÓš ‹)´T2ãý½x¦kšéšçñ8|¼9iÎÐÓ³o &Ú¯4sÂÒàs]Óg§- ¥»Ù«¦†6½f‡DkÞ%àzŽ{^ãíæÓͬž6Ôùé'“Ü•WÂÆWkå ew ì–Ÿ>Gcg’ (a/V£ÙØd‰¼T'{§ápÓA>¡÷c¬?=°Z(2?cçíòëâoÙÆ\¯‡‚•Úf¼•cl\]ã\$ÐÖÆ¼=è92µÊ½¨µ`T3U½L2¶FŽè´v Ùëà‚·ónaŸfÛI¦'BÛ½ëxEÛÙoÎâÑÖzwCL¶û×2=1…ÚâÐä7íAçiblÉ“•ñ㉧5wÇbù®½ÝkI\ùòÚ8Œ¤êµ¬vb³å€´:û^Îäý|íû5Õ|Ž!n<¬2c#µjÄt¥™¼,aù­´Ž5Ï{A} -í{†1ܳÜ9ÜC“øÖ½Ãª2C‘Åæ®Ü©@Þ‚ðcˆd­c£{G÷¹BðTµý&“#$!‘IE‘|àtðâHýýPoƒ¥=(î‹ à3^–VzÀí®<Š GFqÚ{W\ÁèòÓŽ î!ÍÁÎ$k¯B‚Ÿóf]¸öm´šc-0‹½ëxEÛÙoÎâ×-y ¶ƒ4=¦’ØgõoAd vÆøƒ‰Ö½ÚA>ã¤mIL5Å—ë”%Á¼~cg—O4`cìU§™™”_ ú/£¥Üš$¸HhׇРë×Êeq˜ŠSRl5á0M%žô½¬´5½vNºôA‹8_+zy;=^;r÷̽tdŽm<^ØóA:z7(dªähcÙ+OF–¬r5¦0p-'‘×Dd­væc~J½È†9ÄÌ25Æ2àG.½<D—yø ýF ×lLøÆ=f»ZçáñAÞÅ[ô2ìÉÔ¨n6J¯4-‘¬{KNÆù¤ß,µ£|ð÷2=¾¼|AÜ'Ëc‘Aå%Æf™Ù‰»;<5®dvŒÍ {8¸‡.¡ÚååíA/#·.k¾ŸÜc]Œ‰Ž˜5<Ä\Ó×|¹žH8ÆÈÓìö"6×d¶1÷;çÂÇÆÝ»æ“Ë¡Ú îãîÚ˾ͬ8½ Ð1°G,íáªþ|\C|ùv7ѯféOŽìý:vZ4,-pioˆ”í¯–ÅŒ…:t¨íÍ$°Íß5¢2þ¡àóäyòÞÐt­„ž•ÌcÔãëK²le­ë¯2 xúV Îe­HÀ"²èLNØ<\, òðæƒŽJ½ØûAO%Z¡µ ’ ×µ®o;ŸQÉøêkØÍ>V-Ø/„ñÄÞì û9ù …Cv{4Ù! 8ö·#îAç›Ùë¶°y fFŠI™%:“KÞøtKIÐqðÒ Ø||QZtìÌ8Ƕ7ù²1Ä“à8|=¥œ1ðv^¶6ô`;¸1J̓×{æ=…óYgó¨þŒÚ1¼;q4[—Bv9{FÄà…#V­ŽËÖ’Xi¾ÙÂuÑúùÛökª ¬E)êßËK3Yfßyâmà~ÎaL6FL®<[|"&¾GˆÀ;â`qßmô‡çÉïÉBƒæ ʾ ª°€€€€€€€€€€€€€€€€€€€€€€€€€€ƒG Eôcâƒb‚<_FÔ  ÁHŽžÄ@@@@@@@@@@@@@@@@@@@@@@@@A•1ð+4µ…î‚wÍÇg÷”C¿‹©‘tOBOw$r=»ë§|ÐfŽ2¦8IèѸ>R ’=åïy6ãÌ –€€€‚Xš‘d_ {¬<Ç$…Üõ “G¹ÔìAªÒ×”n9˜Xð¶Ñ@‚W‚8#dL nÎù ƒ¢ :ƒ(3Ï“Þ?’…Ì•}AUa@‹èÇÅÅx¾¨7AÅ—+¾ì”›&ç‰{٣ɧ¡ßO‚3óxèéºÛ¬e0ì5Ä™ÑhÙ;¢ ÇšÇÉB[ÝùdHéæŸ-ˆAµ ­,™U˜¹Ñëc˜æï¡!À4²½ ­‰½R¬Íy6 Ùlnw @'|ÙØèC£Š¦s#Ëîcd.‚6Äç8m»qÐ×½£òô#Ç3"m3Ñ^dƒg‹}Iö Ú†N¦N7¾¤¼}Û¸^ÒÒ×0õÑGÈß´ÛðcqíˆØ•Ž‘òJ lL[ÐêIèÔ¥xÍíÙïnQJ÷iëõ2ÐÉ: [d;¸ž&mÍæZæ’|9ƒ´šÖÔ›WÁµm·”Æäj:¬Ö›6ြHîêðüï±gÂÛŠù_u6ñ~˜“­vYÝͤÏÇ-8;À ôSÇUÇdrÝ£Sá†fñò]ôHç/—ƒÕż^\ZÖþ*gâ9i–“n1(ØÜ¸dšüüý6H#õy»NÓZꯓժǫ'Ó»|¥åï»бq±÷Ž…› <;×?g5ž:ó¼U|—áY³Z,Ë îZ©4/fõ NiÃGgaM櫊sþi†#Îc¥¶*²È24ðž8uÚÑ>ͤâ¼G-–“:Ùk9¥;¡±9c›®3ݸµ›é·kA+ŠöÂm–µK{™z4^bp×–whiqsw®@|ü”V–·h-’µï,ZÍP§ÝwóéYÆÖ679Ü>e ½MqÞÝ ¶Z×¼¡XͲ¶qŒ’r*ÉLJÆÉsœ]àßOxż{ˆë¶s’"úžÚZS»^ýqb¬¢HÉ#`¢:‚B²µf³«CjÚ-…DÙæQí%Ê·'pµ¢|Q²2÷î#¦‚O VY;/n5¹#n?DpØ”t>®ýˆ9ÁÇY¯<ñNîÍâ™®‰ÁìvZFõðAØdé*ÆÙÃq¥ð‚xÚÉåÐkÄ ÚåÚøúýý§–3a» .$ž€ ( ÉÚLpÅZ¿ Ž‘µF¤»s^×k`‘±ïè‚V/%R“,B6sÝ;@8€ØçÕ:¢«w%n„bF¾pµÆ'iLJgž´5íê‚& µ5'ÆPmë›·;C^ó /'.„fÐZ_Íãñ’6+S–½Íãàlny ýb‡´ ™ÑÏ &†FÉÛÄÇ´ì8‚¯Ú:™[aŒ=†Ö—FàÖ·F‡SÈóAÖ¯hqWl¶½{\O“}Ù1¹­“]x\FðA‹=¢ÅTšXe´{È]©ØÞâÎ@ìè ½XC,v!dÑ=²G cšvB@µÚU;N­= ×Ç¡! s›úq8 7âPWö‡4êù8ø.ÉTOÆé¦Š¹•Àí¡¼ˆæOµ–[&Ì&K“ñLbgƒ~{½º·çÐ ®½ÚÙ&&ÌSº*³NöOÇ›°N´á¾ºéÕ¥ ½“e5f.0H×°±Ì÷‡Btóø»öEzÖƒäp%›cš$©i#Nø “võlucbÜ¢8Á Þ‰$ž€ÌŸ`AÆ Í éÍlNY JéXèø=áÀ åhq“VžËgxŠ»C¤sá{tCÌsä³9|g@†©Ô’–Ï×­ðÚ èe¨ä»ÁVbçE®6=Žc›¾„‡uíAΧh1w­ Õ­ÈàK6ÇÉ®¼$;àƒœÝ§Ã×|Œ–ÞŒO,—†7¸FAÑâ rñ(-æ½¡Í µÃ`‚³´9 8ÜXž§wÞºx¢F—4q87zy ŽìŽSr´y_Ežµ™D"zìs o?49¤žG¦ÁA2î{ŽœÁjÏÚÐç€Ç;»¡qðz _­=©*Å(|ѱ²9£5ß4ƒâ¼G“;Š»¬:ÐîÛ1ƒ“Kž:µ  “îA /œk»3>Gd1ìg6ž0-#‘ÑèPOÈf(c%lVf"G‚æÆÈÝ#´Üb´Î d»õI==Ý71Ú=œ#Úƒœ} ÅÍtSŽØ2¹å<às‡V‡kDûA™s˜è®fÆì F6±ÎpqóÐä9Ž}=¨15ÑN9ÜùL†0[Ë ‡QÇ­o—š V.Wªø<œÄ‚(¶¬â6³¡A¬y’ÞžŒsµÖ+´:XôvÐzôAM/ik·3Míµ¼}Šo‘ºŒ’÷‡€45Ä|y ¶¯—¡j„—¢²ßG‹}ãÜ xê0B (æñùŒ5§&@Þ>Ææ7̱í$Mr ö ¯,²Yql@ƒë6FýÈ9Ç”¥,ÖáŽv¹ô¿´7Gô|·ñéà‚<Ý¡ÆA >i ,GÞÇÁÜK?X€9z“fñ°T¯nKqˆ,ý œÈ"èƒH{AŽžµ™ã™îFåa‰áí©çîAœ&fÕY‰Žc‹Z^Ç1ÄŸH^ðƒ”]¡«&vlW ñþíä9Ç{4ÐæyûnþÑb™lÖuÀÝ—p;€;õKõ³h,œàÆ;h$ ˆrÔF:<‰œz,¼<pž|GC–·Ì ås?¡iÕ§ÂV€^ÖD÷ðÓˆ´>(6½šÇãdlv§-{›ÆÈÜòúÄ4iA2)¢žO ÚøžÞ&½§`4qöš õòm­Þ2Z­”F]´îÐçày ßÚJ6«Q†k{· éž Rµ˜–ôÉrvq÷ZÞ-ð´sÞÐtí ñÓËa.Øw^)äJz3Š2ü¶ƒ8žö³33Fø£­Âñâ8 A@È¥of°ÖD²Á[ó:i"Œ<Ä Þ¸H#@Ÿ.[A}ÙøéÉzíêÙ‰2RÊØÙ+ÜÆµ£[á× Û# Æv†¤áÞ‰%c^II. N¼šè¤sÇ4ŽûÛžóÃ$^{kMnÞr¢þú½>Y¦h ïÏxp6æ×ù|Å”ÏÑoͬG×_ÉžÎd*T¡[<ÕèÜc|.ˆ»dïÜzí3ÒÖ´Þ;oZÖ)=Õ˜àúWVÇéj6äð‘Ãýœ¹ÿH<öyà4µ¾­#¾¢?³*n¶ç=·?ÓîõWg­^œ’Û-ôp4ý·ˆhòéä¸é›j½Ý–šÅwnÏ?5ÆXWÀÙ{é¾ ;æ]NרZOBNùÓ}ðÞHêæ¤Ç=cžˆxØ«IJ–6îbÄ3Á#?©Z diß#ý{wЭ2M¢Ój׿ž¬ñÄLEmn±ã£®c%=˜rõf¸ø$gpÒŽL­áäâH$ƒÏ¦µ¥\TŠÍ&#ú§%æbÑ3ýØÚÔwSMs¾¦ÁÏŒ4Ëú7@±áÑ£Á^­{YÕ¦l¢8˜Çëû® È ¯¡jý¥ÍÔšN ¬ÈÉ!c·úFˆ†ÈóèPU÷|?’úk4BíkǽÐwÈ»Ð{W~kyi±‘ZŠ.æQÉA»sNˆ<õã´¸ Õéá*ÁRy'­&9$.“Ó’ 8ßTí.)8oÏ=‡ÅØ.kš4G°ôA‰òr´qì{œmÅb: ÂX3çñrõt:TËPÅöƒ>nÈ!ãš2×’cÕê}žÔ}•‚Z½™¥ñ˜Þ]ÀáÍ­.$î ¨†ý\V?/È rÏ;›a.°ó yzÛë×±Vÿe`±ÅÞÅVfÉã¢#o/ú ±íTRKÙl”q°½æi­'\ÐWÚ·W'w³“V•³Ä-;no0"?È ÖýiífsðVß{6.6³Ãn<` ‹‰nÏŠŽLõ™,Ôs]'ÀÆ:'è´é €Ç2‚÷´  êŒ…§Ôh™®Švä-=·¯$6mÝÊö~ß½>w"p±e¾“\ïWÄr:A;?—Çå»+–m-±ÁÜ-ž^}ˆ:vž´‡S¸ïYá‘ýÓœÆ7© ;\޵à‚Uëäÿ9OC;.Nëè:¶5¡ óo6´sÞþÔ1"•Çâa›;eÖj9®eÀƘÞÖè´é ­Ž¨&ÑŒ 7i½Noµk|¾w«É†#!^z9ÂÅŠmtmà:!­ÙðAö. Áµî:k-Àç $”òWàí$ôèã8ç–™<öŽ6°ï[#™'CA<ó9ˆ²9™±Œ¹ {BÇ2hËä\ÓÌsAÞÿg!Ædê:I¢eGT.s}gŽ(‰ýa¯Š7qÒâ"À>KrÕŠ³$m‹1Æc•àâ<‰â׊ .ÃPög/jžJL‰³<.–W0ÄÑË„Ó_b Y.WÃö²õœŒÄ6àˆA3áõwÄÝùìï^;AM`{5‰ x†lÛlEaâîËɯ·DëÚ‚×Ò¢Ív¢ñ’÷‘×¥3%™ †‡<ŽïÏ–õà‚ŸYqô1Wó¶`±^fP0°È×lhðì‚yï}T‡ À;CŸ“‡O6"Zê;±ËíA]Rür•êá2>—^Í’%¢æ`’çµÚÛ@<ô|ù ¶íÚÞŽÄ‚'\¯¯Å¿ÒÄÑ´ÂÔö2ÆPã”"H}€—¾G<á˘<´ÄG ¢"q⓹á'Ìðëù ñ.ÉVwb¨cç:ä2@É¡ ÒP’NNšÄ­ˆïna`Ó‡˜@±odû:Ö³\6)èÓ˜ßýPF³ÃO7˜!™›ÉÙú&9“ÆX KO1Ìi¨Ä׊®&¥x%|°Ç ZǼiÎn¹oàƒÎP·G‰~3fZA+ƒ$s;Ö—5Íx÷è ãYóM€Â¾I%—ÿ¬ŽÊIqg¸NϳH.°¬´yù8tãb!ůìrAFë•©vc3ˆ´éåöOqÀK¥â%ÍxóÑ߆v¼Vöô׿¡[ósé1Æ×†?Õ:<@ëcÇØƒKU)ÿGÚê·$ÈEg/’HöâáÅ äƒÚ¥aùò{ÇòAРù‚²¯¨*¬ Ñè}ø Ø ѵèmßÍHÁ:r@A,lPd¦»ÞÃ;@’ }G8{^zä¯7™¬V|)â-6)šTÒâT h{47´j5¾HÐ@×- iA\Ðc@ò<ýˆ2ƒH3ÿT(ãMKù FPñrfȯ›¦†ë÷ ž€€€€9t䀮{ñó@רÿD5¡¤ sÚ¿  k’ls²QWce˜lÇ9$o|'zA?\ϽÚ€¡²|úûP: iH@×=ø 5¤ $¨zóÒŽÐ5ËH3Ï“Þ?’…Ì•}AUa@‹èÇÅÅx¾¨7AWw9è™/@‹näÂ3»Ž 5¤‘ýâñ·§5Û×-.÷ œÆÇDØ'[AÆÔÒA]ÒG]ó¸ú6 Ù×—U5ˆ™ÔΕ´ÌFâ‰BÎ6-CR.ögp´¹­ß´ö•5¬Úu Í¢#rêHy{Ô,ÎÐcˆsçÓª í+"«^K;†8š\÷y¦"ft‰˜ˆÛY,=²@Øà|•ÚsÚF£Ø'g%1÷=‘6í¨wÚªÆÿOj z¤ŽzH1wÿ9c`¹Ý÷}óx¸7½sÒ¾Jð¼ÕLvåX”ÅEÐkåëÙÊØÆ°;½®ÐKˆõ]Ó`{¶6´¶9ŠEþY×$Mæ±áÊÆo¹¿-8±öì¾&5ï0†·®¤jâÝbÓ1VrêÓX‰4›´5ãÇÁzó؆wˆÇ-q:‡£¾Ifm5™ÔÁ9¢+ˆÜJÕ®.cKšXHÙiê=œ–-‘2yÍÕDýßy¹Y·­q87½_9ΙÞüci‡—#ÉQ ¾[ðó@h3Ï“Þ?’…Ì•}AUa@‹èÇÅÅx¾¨7AåòG&;_;±^Žgn1§†pHw®í¯ ƒe±?°ÑÍZw>[W£–YÝ3¥@·ÃG–½ˆ=#q–f«j¦V𻠆ȴ÷ÌuðA ´¹6N–2݆ݫk‰JYÁ4e­Þ®Nñäƒx?ß›ÿN‹ýnAc•¾Üf.Í×sîc.hýgx‰ÐAäé·!…›fæ5õ™5íX31ÝçzíìÌzú?¢nUݧÎ7êŒÀûÄ´÷gD×Ǫ ®ã™‰©ÙÚ1½Òr1‚ò9¸âOÚP^äêËwf¬3˜%š'1’Ž­$uAç¨EZ€»BLLt/úœçBþ(ç`qç¿6‚xÚÔ;5‚ÉÀÂÛ¦Z»Ÿˆñ¸8€Z}š:Ò Hqõ3YÜÀÊB'ôi,y:Š2Àî!äIß?bû3X²òO+¤>•;Ž<ÞÖµÍi'Ç`,8;9Ú&ƒ¾l`#§ ^òösq½åCﻢ}Y$ hóÐ$éÞÍ׆¦g=^¹=ÔsÄ8·Ãú1êü: Úýhr­Ž…æ÷µb¥ßG‰á{Ëô\Gކ¾Ô2Xªoí‡rU•ì~ˆ¢ª}ˆ!Y|˜|wj+ã‹¡Ž»¢15¤ê.67ˆ·Ë©>Ä1x{Ô²µ­VÇÖ¥XDöÌ"¶eïùz¤‚ѳ¿j–ÆR¹Ž©˜±Ÿ!#Ý#çy%ÁüD×4‚§ãbõzwÚèèú]—ÁÏmžq#Žå¡Ì’GÚ«Õìݹk½Ñ¿Mi{y4¸G—"PAŸS šÃœ\]ɳ3¡•­'ô±ð·y@;ö á^@ÎÉö…Îv¸l\“ÓÉŽÆÓ¹˜ìè³Y’÷˜÷ññ ñp±œ?fÊ ŽÕÃݕȉ]Ïh>Gà‚«#Œ¨Ê}Ÿ« "fºÇH#Ûx·ß?oD›…ÌåáÆD"ŒbņBÏ›Þà‚&{q9´«ÄvÙ'¸.¾Ã=n!ÃÌï¯- ¾íG,ÚåëÅÿ¸Õ¿§ÿ’?¯øcŸþ9þŸåJïöºÔvØ%‰µ"=Û‰á'‰ÜÈV‹ÍpÇ™S„[4ïáW-x¥ìÔqÊÞñµò½Ì|džwº×ÙÉmþîþcöc1Ö¾'÷MÊU'5Sg©WHÊÒJcaw·ÐïCÃÛµž;G ¼Ï]÷i’¿\R#¦»8Ú§v¦ ¶Ü“’ºÊ\cŒžMÐéÌ)­«l“1ð‰¥£V~S²1õßW_Ë.¾VB^XÁ¡§9Çžúù-yÝím/zÒ5H®ÕlšZø ”1žáŒÈˆu…Â8·ˆ5Þ\ÏÚ·˜‰ÉYŸ†Q¸Çh•†w ¥Ù¼‰¯N8Ïs½ó-è}üú¬°å½²×rÓ.:Wµ ­U¯NÞ *Ñ6(ÝeÎ-oBLgj+i´^gã÷M«µ">d'q²ŒœcdÎaÇZ'k–Óæjê¬O‹<Ÿ›êõØ]ßa¡|®õ|oáhÿÒ×ÙªÖÓ?ýiÇ»Z±ü¤^µ,´³JÎ!³Mf<;ZŒp‡|9¸óðU­b-ZOÊoišÚñçPŸŽÆ]§”ïÙJ½Ž…Ì’8¬ÝծџQµžL•µu3¹ßÃJcµm¸F•R­b!ÊÄ5v ÁLj8?\>ï -í{Ny¤öc¬`çÞÂݶR¥-¹½VÅ{‡¸t\5¬ÚÑXòה¼œ!ŽŽ†BÖ9ñ¦síNeiâl§ÖÛG1¢[îÒíž›V-ùGäâŽTÕ¦¿úV.üáý+Èz¬£ÃÆgâåóµ :¬¾j¼·ÞZý~í¸ýšäqãÙê•»Ã+…èœ÷ë\Nt›'^Ò—ç’gí?à½8ãˆûþëëÒI +2EÎFD÷4{@:\ôˆ›FÝéYÓÉØÇÒ‹³xÛìþÓ4ÕÜù¸¥ÎïÏÏà»k{N[V{j\v¥cmç¢Ñ´ëå{C’!™µ„M†7¦µÀ’à<Éñö,yM1ÖkÓ{kÆ/’Ño ø$}ÊXª6e{êËvhœâãúV3‹¤øƒ¯Ž–“[ZÕï¨gUkYí¹Yäp1š×ÇW…¬dâgÖq-dܺ/䱦Y‰Ý§úü5¾(ãªÇôùvÁËLT–:õ}Ã1dиüÇòèw¢9iW,[q¹Úئ51¥ªÉ°€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ0üù=ãù èP|ÁYWÔVhô¾Œ|PlPG‹èÚƒtE:âñº#èÄEû<Úõ®PE—Œš IT¬Ê&•¡îÏÞäyC¢ ìî*¼SEgpXgtÏwÞü]Ëàƒj8n:ǤW®{î$’:Gä ‰Ð÷ ”)×mç]H|b'?g›AØ]I@·N½è„Vc1¯kÃI q4ì^Ô¹R õd«j1$2 9¤‘¿±!¥^½™ìE ¶8{×lž.¡ûp¿‡£“’)-Äé ÜdJöðŸ?TŽ~ÔlU†ÕgÖœqH8\Þ"6=ãšµ0xê]ïsâ™ÛÞù÷ùl’tƒ£±tJ N€õË Lâ>©gÍç×’W°xì”ýýªåÒpð—²G0¹¾G„aA½¬E ”ã§5f÷ck gºp‘¢>5­ƒÆÔl ¡½ü}ܧ‰ÄÈÞ}I<Ï3ϪŒ.:ÕH*Ë_ôU€ð½Ítzäàv9rꃭ],oyèuÛ{¢ýxˆìõö ×!‰£”îý.7DIcÚòÇ7}tàAæ*”¬èáÓª±Ì…ÅÄ–µß;©ç¿j‚…VÉfA K­ë¿ß0ý°ytä‚57`OV1í­Ü¯p`?ª Ðø 3ŒŽÿ§G[‚n>óÕ‘Á¼_­Ã¾ünp¸ãPÔ5‡sßwÁ¼G“ø¸¸ÞÁß4䉓Dø¥c^Ç‚×5Ã`ƒÔ©`±¸éûúÕ¸d ákœ÷<±¾MÙ<#܃œý›ÄY³-‰ª¾~rŽ7¼ë[-Dûtƒµ¬- ׊x F¡-{šæ k@ƒ¾ˆ%Ë s@è%`|oikšy‚">Ä ÁãëGqÀî ò÷±Hçp;ZØÙò守$ŠuÅç]Žýш‹öy´®PCƒ³¸ª¶Ûj ¦7µÜmh‘ü w˜føGØ‚mª\®kØgNÑ-ÙÑØéí ki¬îµbÑ©V\}¶° ä`cŸ³Í ìÞœ§\|c{qv&‹éËL× ^d{6y¸“½ìsæ­î[—-õG·]q×F³a¨OZ*òÂ\ØyÆã#¸Ú}ŽÞÿz˜ËxÄ¢qVb"a³q4YZ:â Ç¢f‚÷Æ:“²¢rZg{Lc¬F´ÍÜeL‰ŒÚˆ¹Ñ’Xæ¼µÍß^`‚•½©½¥o­‘b¨ÃZjÑÖ`†s¹­‡rù“’Ó13=`Œuˆ˜ˆîç_ ­ÑG,¼käsÁo—2tÎ[ÌÄÌöDb¥bb#»h1«¶²"Ew—ÅÅ#œZH׉òðQ9-33ò˜ÇX×ÙÐ㪀¾ao¤µœMžCù(çn`¬«ê «4z_F>(6(#ÅômAº Ãóä÷䃡Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñA±A/£j ÐfŸ'¼$ ˜+*ú‚ªÂ ÑŠ Šñ}Pnƒ„+Ùšxa”>Jïà•¼Ácµ°Á¥Ê÷¢2Ö”HƽÌ.×:#ÛÍtñ³»ão¸¸wÏ^zA•ø/¶g@\D3:q zÍë¯b (#:ü É3K»÷Âf¹p‚ç粂J×oÁB8Ÿ9pÌÈ[Â7ë8è ëfÄu+KbgÇKÞ@'@uäm–6Èö½¡ÃÑA² ^öG¤‘Íc6\ã šY\Õ,=Þ´÷æµ®¼[âéðA]ÕVkÆÞ>!Å­ðïž¼ÐÛd‡°¼°9¥ÍÖÀ<Æú Ùaùò{ÇòAРù‚²¯¨*¬ Ñè}ø Ø ѵè<ÏhÛkz;Øí r-dÙÖž~ŽO‡¬ƒvÖ{rpvz­¹ªS¥I²¹Ð85ò¸¸´lt9|ÉAÆRü|„ÜžŽB(csØç0ó×-éÚ(-¯Zž>Óâë2W6¢œÈÀy8´7[÷m\ݘkÑ[v*™$kk7r¼þ«F2‚’–Fã!ÏAßÝ-«XK­´ ˜K]¾~#caÓZÈc;*rï½=‹–a„5¤ny·]yøže\=Œƒ3L„7--)!q‘ùƒx$ O‘çËÜ‚nrÅŸKÆãkX}cvW‰&Œ&±­Ù ßBysAÆûlâbÆÅBÔÝöF6=ó<9Å„´\v’Ôãµf¨™ÝÏæã/w¾\\doߤø‡ß›²îÌYÉÙ’wS±€€Æè;Zæî[Ú _k!Žì‘ÍIzkìWˆ5ŽÇyh­×^~'™AÓc"ÌÌP––œ»¾~BÞ 0ZGæ5îA_Œ«{$Ìûòö]häáàŽ«£Ë܃Ñç§–¶!áåìÒ Lìì¬úŸÎÙck»%̦Àùf¬âzsDYü£ÿ'Ù /¹7¤Ô¶"dÇBN·‘öó!Œå¤]‰»væfißa‘Èøƒ@`iÐàÏ[÷ ÚrÔüžÕï.Ík½–»Ûßkô`·“F¼YS“)Ší½,uœ¤×a»YÒ½²¦¸oæÐl}ˆ#ã¯gn`»@iX’kÛs`Þ‹šÝó öépY"üfPŒ¶E÷!¨âú×…àssO½weòMì·f§¦Ù·Á3ùÈ8#ö ¹uëÇò…fŒVܶ‡!'ÔÐÑÒ ®ÍÞº3‘VÍå2Uòîâ­3Gs8ç ÓáÿÂxƒËö†+trM—C˜h§!Þ¸$þìŸñ€AØW|™Qƒ«nzT±ôãwõrù ‰dƒÈñ%Y2·Ù‰’'Z{§©–Ž£§+ Û×ÃdÖlÎÎÖQªÙ\ ’¬Ï|c£ˆs@? ”ÏZ²ÃB•YÍwÞ²"tÍ–44¸ë~'ZA¥¸ãÄâÞÉóÿK+ZÉ6$›gûŒõyïÜ‚²¦FÜc9XOx²µ!<&àhÜZíûÇ- Ù’djVÂd¤ÊXšK“Cð¿]ÑlƒÀk‘¹í¿h¥µ[ %Ê’=²Usfs[ýö4íÍ>öí;9 ídìšvÊÔ±æWpô|³~æüPWÍ—¹%L^òéôª~‘bJlâ™ú ä<¶NÉA´Y ¼"žÁ¹5²LŠÌaš»ý_Xy‚áÏØƒfg­W½¹<Îu)"°j°ôi„€uþngà‚ò„ßÙø ±jOK}`>ý`òÞ¿r Šù[—êaé¶WÇvYËn9¼ˆý'þ£¡ÿ™6K‘ÈbnæÙ“ž "|Æ[Ý5±’suëoGg~(&ÕÉÏo=ÓÜÈ,bÝaÐøqq7Gá²B½”½+´“GeâJ–ÚØ ?0iœ‡³™ûPz }IjÆ÷Ov{RJCÜd#…§]ä=œÐVð[¿ŸÈUüãb ð²"ÆÂ@!ÎÇG—.‹£u¦:Û]z¹µkÞѾ]šºüm( ç3KjX%–¼aÒ9±ïe£¦Ï/w5¤b¬^Ó®š‰ë÷gîÚk¿3ÛìÙײPb3úë#‚ú³Úg ›#˜ö莾ÕJMéÛ¯}'ëKwéÛi¶«å!Å:h-ÙžÄÎò†–‚ÖxDÐ:>;è©[c›êb"?÷v–­â»‰ÜÿîÈïÉwi_Fý™¦}ˆàþ¶=zîq˜ {ù«E7xå^šßO*óÕ'ŒÏõð”ßIÅæ©V7gµ Î6¸Nàâ×4l@>J/IjcáhåKÄo{V 2OìýÌ©ÊYÖ’cG yäá®~^í-µHÉãtËvöæónË,¨æ–¼BåöLèD† -ÙÑþñ:ä<:¬iYˆ™ÔkîÖö‰˜ÎþÈ[šö7=‡—ËùÇ…Î#EÚã>ÞKY¬V׈øÿ¦qiµi3óÿkœõÉ¨áæš¹ ˜–±Ž#a¥Î ßÃk U‹^"[æ´Ö“0­žœÔûA‡k¯Oi®t¿NA!ÜArö-kh¶;ô×oòÊk5É^»ïþò¹'Ä'»BþFS À aëXys÷ù«c¦õ[D~êd¼ÆíYŸÙ/Ñ]'lä>—a¼5ðÖ¼óþoNŠœµƒ´w˜i©œÝü"e2NˆKvìŒÆ9À:`ôaëYÓáµ|tÞ«hޱýT½õ»Vg§özKÞ“è6>g»wuÅÓ‹\—-5Ê9vu[|gu>.Ç«a½Ó2V·­´þ³ysò:[äŽÚˆÖûà OIë;ø”6XÈWìõ<ãòSË):H®ííqk¯>»ê¯1IÉ8øü©»F8ɾ½sv¿KlÕ½’ï«G²ÊÌ(­Ž.\÷Ôó詊½#qŸ•²Û¬êgpÝÖíä§ÆSm™*¶Å?I™ðè8ò¨' ÙÚq­"ÖÖõ:O+^k]ëq¶™IìÒšŽ5–/ÊÙ’K,-Ín´.]yŸbcˆ´Zóû&k1H™ýÓp3]’;ÛežåÔYg ÞÂ7ÏÜv6©š+EñM¦'’<ž“–íÊBýŠ•éG…g¹ïx'dyà±l¯“-““ W‚×¶eE'ÍÂ5 pÙÑ£¯0ƒÓRªêuû—ZžÉâ$É;ƒœwî’#ríÙ¤·rWbÊ6ðŠ*p7ºâ:Ñä[³ÄP[ÙÉg-ãÆFÕjô"ˆ €|¯p'‰Î ïC\¼ÐJìõéîãž-8I5{Wtš×yÀíkÚ5¿j ìugGڜ݃rÓ„'øÇ ÁŒ]‡DM¬”}˜giŽJwLZÙÝX‘Ü–|Îo¡ë½í¯`>h2€ƒ0üù=ãù èP|ÁYWÔVhô¾Œ|PlPG‹èÚƒt2èò"°’G³Ñì2vðë™nô³šWñ"ݸîAnZv£aŒKk¸˜Nø\×ß?b?ÑÚŽÃÏ’IŸéï%œ¸wŽ“`ñï¦ÁÙɾǒƒ#k#f݈ö4ÈÖð¸tÓ@û|PHÊbÛ“d¿’¼Õ¥Ã4z%ŽÑ"%h{=~žé.Xš\„")¤ù6Ó§$¤ÅVŸÜTÁÒ@"l[Þ¦£±Ðò”qÖªKÇ>ZÕÆðµ’µ€i-’ƒ|ž.<œqnY š»ûÈfˆŽ(Ý­xò Ž  > Öñµ¬•™¦dÂhìð±®Ã¦€×âƒj˜1_"ü„׬Z²ø t pïc@¿êƒ­\D5pCÉdtBEÆuÅ£¾~[æƒgbkI…n&`é+ˆ[ $éÄ<º@ çGj¤ÜSeí[cZZØåkÒ@Ÿz_ÑêÿÑßÌü½ÎµÞrâùü^ZꃭŒG{•‹% ÉkJÈÄR5®lŒz;¹ø4.GÁâîE`Ë;oÉ+ÄA…Î{ž>` fÐHìÞ>Lf µy¶l÷““Ô½ÜÏáðA%ØèÝ—fH½Ýã tœ¸H.~þH+‡eâ¶¡½dãšðñHððuØo¸‹wá´·eoô‚ô×271r´6(Ex‹»øÀØvËNÎÉH8Ë=ÙÆ,æ¬YÇ÷Q>VÛŽ/]Ï$´5ÃD4–h‘¯±Ž:¬¹ª7k]žÌôÄÍ4íJÎî^@!ÈtwC®h,êã&³2îJ{ì•Ùd¬cZëó@ÙA ½—oheÊ\– r2Jñ;‡Lá<:Û¹rçà‚ÖýFd(X§#œÆXѹÍê䃃112ýKbG—Õ¬kµ¼´ZuÌû}T)㣧jí†=îuÉD¯ÖšCCt>ĬàûÜ”— ¿b£§±Î؃u nõÌ´ó#a‘`ŸOZž?'f£+Cþ Nù‡‚f3.˜­ß&ÞéùÜ÷8íÄûÊ ™»BÐȺÜóØšùÓ?‡Š-€ÍZA›=‘ŽÅŠ–†RìV«CÜ™ã-‘žÞ]uËac(3s‹ ‚ÜÝñ;Ì<¹G>ž<ÐZ\Ä׿†~*ró â—§rÖ¿ARþÆÃ6óUŒ¥Ù£²F=夰4h4rÖXÏ„‚~ÐT̺iÕb161®¾gÇÅZý–†­;µëä-Âë“÷æV·ÐréïA­>ÊAY×ežõ«v®Ba|óÄÖë\†µåö ÖnÇÒŸ³Õpα8mGqÃ0 <“îñAµÉ×£‘v@ß¹=™ t2I+Á/ß÷·®Dt5©Ù(áÈÖ»s)vûê}l8ÃçÈsAoF£é²V¾ÜÖ{É] 2KþèöÞÇG~J‘ïi©8¼:õˆhû9 ã{é7zµÉ©Zk;£$A®ãf÷¢:= NìíGaŸ2M§Éß:~!Þx¸¸÷Ó{3[Ýe#ÉYÈX·f8­o ׃@×OŠ 9 ïa[bÅk5mKFzј˜øšÒ8½R× ƒ€ìÅa¿DÚ°æßK,Ž ¸;–õËÇ…Ö´4ƒÏ¶ŒÖ{K”’sÔxŽA;p×ÇÁuMâ1Ö&7ÝÍÂg%¦']“N¨ÇW©’Äk?¼Šf‘Æ×óÛ¶yìígï[”Ú|¯íWŒV<2ìD“ã­S·‘žÀ²Î÷5,À¾Õ’"ÑjÆ´™Ç3Y­§{I³VI«6(mÉYÌ#R0N†´A!R¶ˆÌm{DÌj'HÀÀêÖ£³<¶$¶Zéfq vÛóu¡¡¯§»115é¦~ÌjwåÒ®$Åq–í]šäÑ4²#(kCëÉ lŸ5[dÝxÄi1SÊgl ,-ÄXÆ÷²wv …ÏåÄ8É'^*}Ùçø=¨á4ùk.:ÛmW»=W÷-†Ciï:uG¯0‘“éã1²quÜN˜‡Z½juÙ,¼ìÙ½O>GÙë)œÓ33óDaˆˆˆñ;M»N…9jXÇ(ÑÑÑÑíYÖÓ[r†–¬Z5(1a»Z包‹2Ößv^ˆÑ>Õ¤åúf±Û8ÅÖ&gzqwfb}y*z}‘QÒáÓ]Å×[#~ Þüï–£}‘ìF¸ï¢løÎó'ømI­`á¡®4èìr÷…œ_Uã1µæ›´Z% ý˜‰õßPß²* Œ€péŽââë­‘¿´Œóå¨ßË?b5Çs¯…¼ñºh_e|Npå#5ÄßhÚ³=›Ú7W S¡t÷,\šÜ⻣a{ZÐÆžgA£Ä­}Íê±²öõ3iÎ0¸OHÂã}"݇Wk/¢x‡>g[Öùéi—.¯mG_–xñn•ÜôøNŸÉ¦´[vÄP\ k^Íx%rjg§IðN=ÄuëR©Ö–´E³[–ÓË·Ç h×°Ò¥­Ú4½k1Þv‹s g¼nÔ½=ŒG#¢ p‘£¦ÃäUVj;?Q´*Scåk*ØmâAt’\O]’v‚Ñ…m™£¯nÄy+õr³Jù>*üMï7¦‚ç½{zIpóX–;ñÝš…×ÂØì:×5úçÌ8°IÑ&c±ðc)¶­~"Ö’Kžv縗âIAÄb{¼Ì™mËœ4O K%áÌluðA v^.WÓ¬œsdã^.-qpïÃh/fŸ'¼$ ˜+*ú‚ªÂ ÑŠ Šñ}Pn€€€€€€€€€€€€€€€€€€€€Ñ¢Ïš Ãóä÷䃡Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñA±A/£j ÐVG—໓¯mŒˆQh™®çÄZO°ì‚YÌ…¨èW‚œ »n¹²öÊ÷Cùo\É;½è17h篊½,µc(O2ÄKššzè‡oà‚ÆÖEõó8ú ®m±/É;owËÞ‚&++‘ÊØ‘Ì«U`±$2=Ï%Ïá$öooß};"kájÇtâOÍ$ì}‹JS”Z~!ïÆb>eÆ|³áÈ[­Üq¶½1`pŸYÇgÕ×ÁLcÝbwÞt‰É«Lk´m®/#rÜ™?¡z+¢/â¯#œæº;[ûä¥k³btƒZïžZÐInÂ0ò?¼Zþ _f9Íwãlýéá×;×Èä8IµfÎ`ï¡toqaÑÑØØæ|mJqåYoI­íËŒÇV™³·ŠŽ{މáà–¹¤—Xï{åîÒœñZÞb¦ µ©-ìä¯~x~6•xæÀÙ»É^@$h€¢´¯vŸ:M¯n|k ªäí\ÆÍ$pBËpJèeŽI®™â×MsKRµ´G‰+{Mf|Â< {ÙÝF?Y8šöysèy+[}<|ü«§ê߆`ÉåNF•iá¦Ö[ŒÊ òZ¶=üúôI¦>6˜™éÐŒ—å1QŸÚ—µ†èeCHKÝð÷ÿ§-âáãáé­ó×]+{xõß诿ӗM~¯H¹]B Ãóä÷䃡Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñA±A/£j ÐyžÔÒu¬–>(dá’÷Yš:ºOqøpÿ ÷è[‹+S2½òW‘üÌ$C´tA!KÙû¶ñ9NþHc½‘’€ÒK#àáànõ³óyx îÊy{¹Ü~Fì«GQ²µÑG1{‰suŽðt'ÇTž)ø8¤·4Íá;õ\âGÅ3NÍ‘Rj‚7MRq0dŽá ør+\vŠî-æ4Ë%fÚ˜ñ(õ©åFVÎJhëµòÕlQÄ$$4‚HÚýêÓjpŠGʱ[ó›OÃJ¸«2ääµ=H(Ç$Šhà—‹¾.ñ<€åÏÛÍM²DWQ;êˆÇ3mÌk£Lv*Õ!^«ñxÙY ô½€òÑÐððï‹â§%ëmÏ)ü‘Lv®£Œtò±ÇRš­¼Œ²ððÙ±ÞG£³®9ùs +Ú-ˆñ )Y¬ÚgËžv„÷ëÀ dr÷3¶GÁ+¸[(õIýêq^+3¿„e¤Ú#Hp`§’L—¤Å^´W«6&Ç\ý¸‡ßPv´¶XŽ:™J‘Šfm½FÒjÓ–±¡¯\Cܘ2qZk÷Þçþšº¼Ø¯ÎY;PW‚)«²&C vÈòüáÍLZ/Æ•ž»8Í9Z~c!ŸfŒ6±Õšë®&ŽÃžðu¾Ž‡.zKÌd‹MfzuV‘8æ"b:ôu©†³>ŒÌf:ÔBBYbRÃIÞœ8NÈ÷ªÛ%m×sð½qÚ½""aw¹éÓ6Hâ[ܹ§×'ûÛ ãÆ5ݼrßÙ!Uaaùò{ÇòAРù‚²¯¨*¬ Ñè}ø Ø ѵè"ÅŒ¥ é/GY‚̼Ÿ/2â<¹ôø ”€€€€€€€€€€€€€€€€€ƒ«¶ëB1ß9‚2ÿÐvÚ§”ë^ãÛª…šK sÄè¦cdãNk†Á bf'p‰ˆ˜Ô¢ÕÃã©ËÞרÆHƒù’'¢µ²ZÑ©•k޵Ä&ª. Ì?>OxþH:0VUõU„=/£âú6 Ýaùò{ÇòAРù‚²¯¨*¬ Ñè}ø Ø ѵè3Ï“Þ?’…Ì•}AUa@‹èÇÅÅx¾¨7@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A˜~|žñüt(>`¬«ê «4z_F>(6(#ÅômAº7ÚrlÔ’¾jìM¹u˜Ú#á®æí»ðñA®Fk|¦7Ú¹ó‚ÞEòÂÝ ¯oD›ŠÖ+)Ǻ3Rˆ÷Ñé½ì`·aÍ; òèƒwfÛ ­V½KWì>³g-gg!·@Ù>H- Í r˜Ÿx‚A§7Ø}¨) ÎÕ¥†%’äñ¾ëà<8¼hwcCÇH%UÎG5¹jÙ©bŒÑÅßpÎëG½ IéâbŽq×ÚÙcÅ^I’)\©QË‹`Ÿ é &zÎG%ÉñÖov{Í8€ÖŽ-ìkÇH%RÍ WŸJzViYl}ëY0o®Íë`´‘ðA ®¬ú¬º(]œðÇÙ,o nÞ¹ì€y9 ›4Ú—} v.Ù ïpßQ»ÖÉqŸ€A­œØ‚XkÅFÕ‹RÅßñ†ñFß7t9òÖù”ñù2tc·\»ûäá§4ƒ¢ð ƒg´ÃbÄPQ·mµ¬KZ[ÖõÌ‚â2 þÐä]dâa¨ËSU»/uW†›ÀHh;yžžôíG†ÁËiͲ¬Ü/qsއ O‰ß- ®ƒ”³R;W{Ab­‰ZÝÀ#lQïŸlëÏh':Ãh`¬g°^ên0p÷NÀq/܃–;´Pd£ïâ«eµžû2µŒ-êÞ»'ÝÉ^K>ûc Kµb±zêg€ÖÌÎ.c‘ØØç£­„׳ñÔµ4ÒµpÖh}ƒA67²6uÏA,vޤ.¨ØcšÓ®Âe®Ø “ZåÌòë¾|¹ìßôLL™ «ÊQ_ÑxlŽºØ÷ á6zœ7hU!îuöñFæÂÐzqyo ö Ïç–?ÓìØuI„lM½Úëg ß2Pq´u…{Ò[‚zo Ðùâ• ¸4ô#Dƒ¿z 42\{›.6Õ=49¦n8.yû ìÝé)v‡ÀÙå2qÜCÖGiºå°9sæz ÞÑWô Öf¯b1ãv+¼6òØ×=އh%ØÉÃ]ôZæ<út‚8õ®D´»Ÿ>š(²·œp9w׳k¼†ûc%î°ñ³miÝÑþh'œÕ¡Úy1ÇÌøY ^^Þk‹›¾oïö °Èä ÆTu›e †µ¬sÜNƒ@ñ$ áG.ÛW )êX§gƒ¼lsðúíÞ‰¤ŽG¨ðÚ,íui*²ð¡pR/à}’ÖðÆx¸yìø¤“Æé`|l•Ñ9Í Hέö ó–ëdje1Õ Ï_š[ñ=’øDMæòtßpø ±»ny3ôqõ¥,kZë6Ku̓Õk~.?¹fYʳµAjxÅi ™Ì¯Ø¥ 9Î<ËŽýÁof39 ðdg¥ ÙÜíò9¼D»`ì ŽH3z_èqÊÊÖúHÌàoGLX÷»\H8Ì„µ«2|ݘLp)‰¬|ž.. òöh «ƒ1‘·‚ÅÆ,pZÈZ0 M`ßvÒâd¦Ë[ûö‚„Öéæìbfµ%Æz0³æ×õ¸\Ò@Ñ^(4ÃXÊ;;­‘,†MÓ"..äSÐs=}ˆ!Ë–ŽöJÓlçN2¼!Ž'µ®·“žâAå¾@rèƒÔ7 ç¾]|ÐEÊßn3fëÆû˜Ë€ýgx‰ÒšµuÑØ¡”—½»RFñ¸€8šðÓËâ>(Ù{-fž9™Ÿ+%p¥c r!hhäÈö ¹/´{dÊþaµÝLØ5öÞ. ÝtÞ¾=PwÎ[±Ÿm’–ÖD6YËE¯äÒ}Î×ÚƒKWåþ’EU“÷UiÕ}›g–Žù0ðqø Ú§h¡³b´o§j´w?³M3@lÜ·àv ÆÀÚ ÕíWm>õ,½Èøç›„D[¾§|÷¯ õA¶76ro‰Ðãm²´ÃŠ+/ àpð:c~#ÓÌÖ­„Ý%»`¬«ê «4z_F>(6(#ÅômAº ŽÐVšÇæ¾æ'ÉÝdb‘ü#|-Û'ÙÍrU¦—´Xi™&s#ÀäͰ¿yAýrMÚbÊÒ8Y§!Ó~‘ÁŽ>h5ÈãSl˜ü˜» Vˆ¬Òaâaáa ùõ’ ÜW¦~kªr:û¶÷ÚÖ¸¼z ó‘ã®þc© «/xÌÏ|æó ï\x½ÚÑAij ‡jâ¶+I$ ÇÊÒZ6 ¸Á ÷‚"9aÍ@ÜeL…Lyk˜-0¶6î÷`ƒ¾ rA°ËTì´øêÕ.Er¼®.{YóØe$˜ÝОÉlU9i™rWâ«èo¼¸÷9Î=Ioîß4~n¸?'>ƒè²úWÐðúßK¾žîh&ÌgÄvŠÕóFͪ×!¡Õ£ãsͽèƒÕ<;6Ü­ŠY+XªÆÔ{Û,/i'Nk nˆAw„«^®5Œ«VÅXÜç?º°Ix$ó'dõê‚®'ÛÂË“¬q¶­zU‰'®ø×qê¸ïÕÑóðA¬8›TàìÅc¤4å&w3˜gèÝû¶t‚ë-@e1¨pzDEÞDô?j5’’l† Ô2=ž·&B(Od"HÄœ:kÁä$u)Ù?Ž•ðh&×Çz>g êÔ冥zS3OçÝl·M'}z ¿–&MâxÛÒ×a(·Òo§»šZ‚Ÿ^yóWòv¢|\ÅjÍxÑ7™wþgü °ÕæuÌŽJÌNŠK3pF×ÄÎMûNÏÅÍyÚºŒ1ÕU¤–è~Š®’Î5‘œcí[†÷±šìâÔo k¼· wÑz8›Œ ޱ »¨û¶œ9·¼Ù-füy»ùPví›s[(_}4:̵câ2Ã|‡™òäƒ7D¶éãïÒÆÙˆãl‡Š²F÷3„´ð·zèy{uÇ6{Ý Ÿ*ú³V•›Z&ÎÞ¼ñq9Ü> æƒ½H&gj²6„RW¬yœGÀ>Í„.‚ldYlæyí›ÒÉ$2Es$ƒÝýÞçà‚ãûYÅK^iKj;Mçs@'Ì Ú Så§£kemgÊf±4g\!ƒmnü˵ö ã*lGhªÛ¯-Ë‘Zc ²éŸÞkÖc·à7±ñAϳ=©­\Ƶ·[,Žâ•»põÏ Ñä9iw:oé»&ô;]À¦`ﻣÁÄ_Å×Ë^(,²ôFOj‘:3DCO“º´üRc1—²8<¤·ãukÙ6ødþàk8¿ŽÏÅ,E ¦Åìârì·\µÅÓK#áí8âòÐñAgÙúóT¥¿®ö¹×g‘¬#›ÚO-{Ð@ÅÇ$YªÿšéߥIÜfÜ™Ãyrà;~\q¯Œ¿„þ‰+¦£’’ëëO{ ž=_n°‚ll±”ÍI’,V‚/‚vp>W¸ï“zèkí(#¿oú ¦*ÉéšÜqpúÍáx.Øö¤õ>ôT+Íi2Ó¾'6)[ärvšw¯r‡'!­fGâ­‘ýÐŒ09Ò·—®Ñ¾a}FI´±äáÇÏNk>'ºhÄnÄ‚] u(3’ÌdŸ††Jx«±OeåŽýë´ðø“àƒ|]¸©b抦%­“†x¸_;ºž{æâz ¹‚C5xåtnˆ½¡ÅêÝŽ‡Úƒ¢ Ãóä÷䃡Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñA±A/£j Ð4€€€€€¯  Ì?>OxþH:0VUõU„=/£âú6 Ýaùò{ÇòAРù‚²¯¨*¬ Ñè}ø Ø ѵè3Ï“Þ?’…Ì•}AUa@‹èÇÅÅx¾¨7@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Ñò@çä£äP4|Š?"€€€€ð@Ñò@Ñò(>Gì@Ñò?b^ˆ5lsÜÀ}fkcËj±h™˜øN›mY Ãóä÷䃡Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñA±A/£j ÐBÌ6ãðö›@‘hÄDDûš˜Dª"2º;PâhÍ^SDðÏ3^Óßx7ÖäOŽÔþh±BœŽ9ô«Ûƒºc…ã0sC½^‡‹ç;‹žÂx<¡à*_­n»íÃÁÃÇÌñ|ÞZýȆ٠و•³PM4V&1º¹'a¼-Ô‘û¹ìx„è/òÒYf=ì¦Âû‘dti?Þ'ÀÏj«©"¥‘v؉߯͘5“Æòâb'ˆ8<õÖô}ÊÞUðãj≮ìÜvaØÅo‡z›DíͦÁæYã p̾jÏ^‰®öÊÐùx¹×sOµD¦; ö¦µ©ï³¹dîi¥#Zcîçáˆá>ÓÑL"vÚÔs»?Ö«ËÝšQ‚;§ÌÖÉÅÌm§‘ö”ðyJv*7v­²z3Ì©q#÷ä5ÉSÓáÍkLDN¡L¹qR#r‹_9Y×äo¦Í\ø>w?­iü?=okÏùgþ³ ý)µnÉ5ßHѱ m-20¿ír×Þ®Y›ÇHÿÛtn–¬q•œw«Í8†)8Ü[ÅêŽ@{WE3RóªÎÑ5˜îîµT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A˜~|žñüt(>`¬«ê «4z_F>(6(#ÅômAºŠ ]$l‘‘¹íkß¾“¢íuך i±ÌÞ(Þ×·dm§cc‘7A†á¶AèB   Ñ’Ç#žÖ=®tg…à–Ÿ#äƒ=ì}ïuÆÞð7‹ƒ|õÓzòA²VœöT™ñ9¬qi#|ÀSÕ´ÌDÌ<¯g{SzþU•20ÙÙ¸ËYÃϨ÷ƒÍm“DnŸÕ^÷ã/^°z/+Ú~Ó[Æ\mj!‡ Ê糈z³šÛ8´n\§ÔÚ–ÕV²‘ã0ì»uûwv4^â:œWs¨tÛ,cÇÊÈ{/´ë1ã`xÜq²>7‘æIWžû±¯½–9oŒ~¨÷ïç;<æÏfHòÉ s¸ß±MkKôŽ’­òfÁÖÓÊô®Á~œv«¿Š'ïËÌhYLLN¥×KÅëÊ£5w3zZ¸QpBu%¹Ä7ÿtx­8Ecvs{×Ëi®.ÑåÖÅÐ׈Ë[,ËR4oº–€ï`)Iï µ3Ö7[m¾´,̇Á$b ‘|øüü6?§±Ù83Æ^“Òa[†Éf³6íÁéÑWôs­¶íó#ÇÜ­jÖ±¦reËië_f×2ù~Ïäk²üñ\«9Ð{càs|ú{Ò+[GE¯—. Ä^wµí%û8Ì;íÔsØö]»¥Lu‹[RßÔd¶:rª.9ùì†: ÈUŒLÎ Ó[zýêÖá­)Žs^‘nQ×ì²Æ·$ÖÊÜ”HàÿѺèëÄyíRÜ|6Ç#|ÓUZˆ8Mn(Àòàu°8zû–Y2×âZ+2ó™ æ;-d3ñ¼~€Ô ï@{uµ§¥ôµÉ>ôÏOóÿã ù¦‘ÂWºA½‚Ý’|Éó^ÄLvyY7½²Ú­2‡¸7dì—5LÞ5¨aXî\mKn„Žž´o¼¶Î¤{¼U-Ž™kÆÎª^ÕÂÓzDeÞ'´Ë¸æà1ÀïŠùì˜éè/Ægq?ûogç=v¾­j;q÷‘o@–‘ÅícÚºix¼n1©vW@€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ0üù=ãù èP|ÁYWÔVhô¾Œ|PlPG‹èÚƒtW´øLa³BÇ¡ÇÞÚÈÄÉÏr°yû äé ^c_V&; sˆ`%ƒn>:A2,üõaÊŒ¤1 qŒkÜk“Ã#\Ýo˜>8ãûI4™*•-ÉŽ]€SŸÑ8 ð¿Ï—ˆñ,òY 1öhm5¬OÜÊó½°‘êq<¾(*3ù›â–m´{¸£¡é‰<\npo†À#âP_Òô¿EïqÞÿÁâáׇ^{Aç±™ˆ±ù<ÔRW¹)98+º@=VòØAË–}, ëðÅÄ_—sC&¤ÈÑÌuo¢ û ÏÑǵ,²É^çí¼:׿h<Þ"öV†ÕÊõ«>¥[6#^ç $B\[®C^ÞºAì"•– dÌæÉßqAåªçS³øE¯N˜ºç°:W8A‰:óÙ=6PzJn´ê­7{ŽûžÌ–àFùôASc#s/j­aF6Õ›»t3¹Âg·–Þå¯/r ´r2Z¿“®æ5­¥3XÂ7·Àîj ÆöŠì¸¼,ðÔ…ÓåXXçÖAß]ræ‚L9{°Ízø`kVô˜ß wŒçày‚ÒÑgòŒ£G)n¥fQ¶ck˜Ç¸É ã¾Dlôò($É“Ê[»r,T'÷ou‡8­Ýt}OŠRö’i«b&¡Q²;&ç02Gk»p©ƒ¿`AÊíŒôy¼]a- ù!•ÏhãàqÞüyËâ´ÓErì5_Žkhh=¶§à|ÏÖÈ`ðòÙñAÝÙëßF DP™.Uô¢ûðÇ!ѼÉ$ëàƒ^Ì>gÞÎ:ÌmŽoMíiØßvÑÈùxüPr¶ë£¶ò6ƒ 2;;rxZ;Ãà9’ƒ¤}¢°!†K¢ŒÃ{Ðîð¸‘<šæŸ"Kzù •>JüÙ …:ØýêÕLK<µåI‡…Ädä¯Ù,¤AÄ&.>Gù.›Ww‰y8²MpZ?÷W¨ìl ‡³8 ®sÝöëùŽYÝþޱaz²u¼=ŸþùD‰Ñú­šF“¯ûãG÷®˜ú±<«}®5ç÷c²ù xü¦PܲÈߦ—oNrŒ‘3¤úl•¥ïÊt±»ô«!QÕÁüÝUÅϘ .Mû:ªÖxDï»[×ýE£á„ÎÙ»V?ÎÏõ\_ŽúÏøgú"àûE‰«…©^ka²ÇoŽÀ+^–›K<£qÖ³=^‚­¨®@&‡ˆ°ô.anþe1§emò…„/ÍZWEYÒ´“(â w0^<—|¸w´ùiH·Y‡‰ÀdèÞÈß­m†k7$mkª[É­qß?>Ï%î{s‹}¾Ñ¿7 Ì^Ó˺ÁÂÓm6 øãsë´4€žG®"¯¸˜‰¬ôq^³‹BeÊSÂ%µÝ,/Å¢88u˟ߚ–åÓËkÓ„ïŽêÅ€dÄÈè^çIé°<44lt×Úçu¼DöN-[Ú![Š’7å,S¶ÐÞ$çÍÜÚǪâþ#⌗ŸËüºý5­ãötmÁbÄOg`Ûæß‚æÃš™kYojÌOT•º¢ Ãóä÷䃡Aóe_PUX@@@@@@@@@@@@@@@@@@@@@@@@@@A£Ð"ú1ñA±A/£j ÐWf1ód=¹,r9ßÄuê´ëÛÍî>k¬eÆê{ÀO3ÄÐD³}Û¡;ÚØ2PÇ NÜÒÖ‘²=úA¶2žFâm¬v.6F4ë]ç\ˆoáß4;E2à.‰älMdFA!þë›ë4ý  ®­‡±w±“Õ™Â;¹:i\î‚Gž-w!ðAuA×U¾ PÌ9Å!{uàv@ABj62RJZE»†vp饭ý¼Š é;=nL.B˜–&Í=ç[…Ä’ßžÐﳚÐÒËYÏÒÉÞeXY^)#1E#žvísÙ˧‚1ás°bmbcu3 É%.˜½ÜPµî;:õŽ˜êƒÓA kÖŽ|ؘÝù¤•1¹õé1«4WŸÞ‘;Üà šž@ñ uɰv~ZÁÅ­{1¯{¥s¹—4‚@ÿ1(;å¨Þ“%G#A°Ë%fÈÇE+˃Àæ¦D›r®JÝšt±÷#¸ñ#›hðº'ëGG„í§[×$ìã²_©’¡WOs^jîq…¤‡m¤­b¸LuÚSd'ºø]%ÉÄúޛê®~Zø çr†N>Ð;+DV•¾ŠØ 2¼´»Ö'{ë\¼÷͈öw&̤ñ6K¦K½›àŒë–·ÏÕÐ÷éE˜}¸ÛÉÞÉïvÆà8AñÐh/¢ï;¦w¼=çãáé½s×±È#ßw:Ë„/?¸©¯xRÿ†Tô¨ —aà¨u·Ö;ðpæ?zÒÓ¬›s㧹颿eV"ë²xª˜'Þ ‹lÄÓ¿ßÈ+Ú8ÚlæÅr‘‡ÏŸÉ'·úª téQ‡¼´õÿ‚½¤Ý\Æ/%ÿÛt1;å<ÿqWÇÖ&¬=GÓ’—üžõ®kÚÓ¶¸lâ+׉Ú6NÃjc,ÎóÉ‘8üuÉZ±¹ˆg’Üi2ðØü\’v+!c„íò5ía™]·×ò±âŸôö·þèô}вÙû>ÈP=Ìpøì5–Xú¾ŽÛů‡ Y;2V~sü¢3»õ™T´¼Žƒ„~<—D}8ždǹêºxýšöR¬/f`±dîÑoûÎL“1¤zZÅ­x–q–%ì¶qø»o&”îÜO>èèRÑ+Ê;§§ÓäöíÚ{.;eþíXÿ;?Ôx¿£ÕÿÃ?Ñß³qÇý¢îìÄ9ðõ*/3ÊWôÑÕej¨èUdéPc¡µ,,o Íã kˆ{u×Ípúªâ¯—ŽÓþZRm;ˆ|— ¡zÝfÙkžÛ²9¬ põÌë_½}7¥Í\øëzu®´óscšÌÄü½f3´£#FZÚ­°é€;||'DûA?•ðM&f½§ü“x½u=ÝßÚZ˜ù‘ĵÝä…¢RG-´òo=|=ª³†mº«[M-ߢ¯óíÙÇÑšrëN³œ=Ó‹Hoùë[äV¶ÇiË×Hƒ«Žk¾­»5b½Œ½ùl¹²2*î×wÅ#ŽÀåâ5û×›ü[6 X«'Ïö~î¿M[M¦Ðú:ju"ÆÎ"ÁÅ&‡ι’|W6t¥"*ÞÓ3=RÖªˆ3Ï“Þ?’…Ì•}AUa@‹èÇÅÅx¾¨7@@@@AÞ:•÷Dëu£œÄx™Æ7ÂPI@@@@@@@@@@AÊj°O$2Ë^øÇV;ZØø ÞHÙ,nícÁkñ¨Aˆa޼Á #¡¬hèÐ9ƒt®ã©ä[nVŽq¸š$ûHCH2€‚Z¼ö±V+Ö,ÊÎ^túþåjΧlòÖmIеÂÔž†"½K¦H[ÂKÁçÉ/;£ f˜â³áÇ…ІVõÖëvœ ?º:ŸÞ¦Ö™¬B¸ðÅ/k|£v§ o5^¼UŸDo.q‘Äo–‡‡½[â½Ùúœ6Ëu~ ·»?_-ïb`h‘‡|.[æ-¸ZpEñE.‰FÐá¡ ìÝçš<¹©™¥ºögJçÅu¸mg•Ï9¬É:*tÚàL»Ï÷»¢E«_ÃÝ6Å“7Kô…ìPCvÖlMo`µä³Ü÷uDDGyÑÙûøkï·„–7E'Ï­1Ð×?ÉkÎ-³Ž0_¹bíð—$Ý£´Îæ*U©r3>n>p º¤yi3žÑ¨$ap°á«¿»w}bCÅ$®êóø(µ¦Ò¾5ÅtÎ`ïâoÛžÓ¡s, þÄw¿/jµïˆˆcé°_¦må;?„7Gº.k&aâŽB7£ä}Š´´Ö[gÃk¯*Ùðù«]8©ß]ÒµÍᓌó`óå×¢¼Z±nPÂØrÛ·3 8úùüv>‘ÃAí…¼!ÆWìþå[M&v¾:ç¥"±Ñ*„Y“uÒäf®!à!‘A½o}I=y(ž:Ô4ÇynóYª7C»Œ¯|îwKÈhpÈ@o·]6¹óz|ycë­[Í{<m;8ÛþŒ1µÏç*ù,c@° ƒþmsÿôýñ×ý<Çöñ÷ÿ´fÁ7¯'Š¡”“p‰šøÞͶF–éÍøôÿNJîlÖk,I‘qs¸Üâã×}U´§b•\†zߢc«¾wënáš<ÉðXæÏL5å-iŠm/©vo Bb89ÿxïñÚÛñY|·­»Gåglâ"ao‹Æº†AØö@Ú5gÇ ‡¬-~<ÏUÏŽ¹çÔ[Ýž“×çõi3^¥Õ|]:“‰kEÜžcŽï+²˜qÒÜ«Rm3Ý1j¨€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ0üù=ãù èP|ÁYWÔVhô¾Œ|PlPG‹èÚƒt Ÿ4 ûP6|ÊÏš¹i6AsI3[§Ë®3¾º ±X‰™)Û}sÚ²@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A˜~|žñüt(>`¬«ê «4z_F>(6(#ÅômAº Ãóä÷䃡Aóe_ÿÙyt-project-yt-f043ac8/doc/source/_static/apiKey02.jpg000066400000000000000000002474711510711153200224520ustar00rootroot00000000000000ÿØÿàJFIFHHÿÛC    #%$""!&+7/&)4)!"0A149;>>>%.DIC;ÿÛC  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÿÀÊÿÄÿÄU !1AQRq‘Ñ"2Ta’“”±3Srá#BUbs¡²4Á$56ct‚âð%7CEuDd¢³&ƒñÒÿÄÿÄ5Q!1A3Rq‘"2Ba¡4±ÁÑð#CábÿÚ ?ñ*cc΃($¯^[V#¯fIdpkÛ½Èdø/Ïþò§Ûw¹ ŸùÿÞTûn÷!“àÃ>;ä©öÝîC#Eù÷´9²T ƒÎïrgà·ˆ}:¿mÞä2|ñ§Wí»Ü†O‚Þ!ôêý·{Éð[Ä>_¶ïr> x‡Ó«öÝîC'Áoúu~Û½Èdø-âN¯Ûw¹ Ÿ¼CéÕûn÷!“à·ˆ}:¿mÞä2|ñ§Wí»Ü†O‚Þ!ôêý·{Éð[Ä>_¶ïr> x‡Ó«öÝîC'Áoúu~Û½Èdø-âN¯Ûw¹ Ÿ¼CéÕûn÷!“à·ˆ}:¿mÞä2|ñ§Wí»Ü†O‚Þ!ôêý·{Éð[Ä>_¶ïr> x‡Ó«öÝîC'Áoúu~Û½Èdø-âN¯Ûw¹ µ£ñ$ *|S£ñÝÐûË?ùÿÞTûn÷!“à¿?ûÊŸmÞä2|çÿySí»Ü†\<÷ dxvXÙyŒå”|I#;i>oZÊDˆ6HæóÞGœ4éÔ㦂Iðh$ey¤š8DNí$pkF‰$éïq_MÂòÔˆÙ6Ýb78òDG!tï;ïD<ê[ú<áÜwäîA‘ïd0·’BÞ¼ÚðAÆâjñ|K~V–Á¼¬ì {þ´¶^÷ôh3¢)|´Õl¥Ý©×1 ww)?lo~ùçk¿•¤¨HâîPÒO˜¨c˜àÒÓæ#H>£úMÅcépÍI*P­^WYcK¢…­qø§¦ÀRˆ|ÂXe…Á²ÄøÉêÚ[¿j¢$@@@AØáûYÿ<~Eû²”0{Š ‡ý à&Ðó hy4<Èd 2‡™CÌ¡æ@Ðó hy4<Èd 2‡™CÌ¡æ@Ðó hy4<Èd 2‡™C̃žßÚÍþaü‚ Ð|ûô³ÿWãÿÎwûJ&2P‘ñÃ,Ĉ¢|„ Æ—hyú Ñ´»éD0ƒ(–<eÔ?D™VËVîbÈ{hÁñièáíÑúÑ < æ¯éüonÙ‹í9v<\tÏÿ‰%J¬ñKOéU•ÙV k™F7¿¾#½½Íúvuõ ìqÏÙák`‚œ3¶Änq28háëAñsÕÄùÊ„¾ƒúÿ®ò?øfÿ¹ w3£ì>S5jôùÇA,òs:0cø§C§^ªP½ÄT!Åþ‹íQ‚sl†KR{­øØal¼­kàÒêz|£×ª=\_”ànå;nSVXcìùz;Ÿ}wá­ »›Áãð½¥Yr­wfjê)6ø¯æÙ=úÒU ¾]~ ´pvÒv’5›=ä íqg\ák-çqžœ¿²° O‹\<æ‚y8å^“;zÄu4èëJ4^Óôø8ø] ìpž_õÒ¼]¨Ãù%þt>ÿ©û=ÿ&áøsóËÍ,M{¿ÄXÞV¬¥‹pí­qn6Õ—õ7Xù|åÝO´¢_Gý#ð¶[ˆ-ãåÆ@Ù„M{ ¼» ƒ×â""p-qiïEB_@ý×yü3ܤ—›ãp?¦Ù^ƒþ#ÍþƒèkàQ£ÿØ3ýÁÐíˆÃ2µË‡i;%½Fý¨Kþ8ƒ#ýo@´èØg½J;ôŸ–ÃÝáÈà‚õkE†º6Å yGg§pÒ”(YêëæpÐcãá÷s?«š`”÷?™ß']~Oz ÃÄ8èó¸›/t¦´8¶Ó°æ°í„±í$sô ­$øj3{[#%ÉìØ†NÐWsÊÒz õÞŽú÷ïAÖ24 áÛX÷æ_—kÚÑNÕsMg]Ìîî›ûAåÐ}„HW¥ú¯ˆÁž¼Cš œÎr9z†‘ãÔ CÎq_Ûâ‹ý¤›Š¤dö÷Ñ£Î|î>tK„‰w¸{ƒr|M¥¦è $ìä|ÑoMïZëÑJËô•ކŸÁ1|„1ÒìünF 7I=~¤!òõ wcã~&Ѝ¬ÌÅ€Æt\ñkh‡’NÉÙ(•Ìnc#‡•òã®IUò7•îfº7P‚ vì^µ%«Rºiå<Ï‘Ýî(.ž"Ì;ú¬äf4¹;œ¼£Ã»h…J7íãm6ÕW™ÏŒèú½H:7¸³?’켫)3Ä/0 4ã :‘ô ¥’Ëd2ó¶|¹,ÊÆòµÏÖÀÞõÐ}(-MÅYéìAb\µ—KX“ù€,ßpñ׊ rœI™Í±±ärÏÚàÐ|ú(9ˆ‘c„íf3üñùCîÊP Áî(,Rÿ‚‡ø aÍkƒÞ è t@Ð@Ð@@Ð@èÑA¢‚   Â{?i7ù‡ò7@@AóïÒÏü?üç´¢aó% c§™PaÊ$D– ¢D¨fò¸¸Ÿ …Ьí͉úùÑ sK%‰4Ò>Ivç½Ä’~’Q-POFäØûÐÜ®@–‡´‘Óh‡ºoéfÀh:êDßúT˜gáfî–}÷þ”0|,ÏýÒϾÿÒ†…™ÿºY÷ßúPÁð³?÷K>ûÿJ>gþègßéC £ý.ÙŽ6±¸˜ôÑ¡¹¿’mðÁkû¦?½þH`ø`µýÓÞÿ$0|0Zþéï’>-tÇ÷¿É  ¿ºcûß䆆 _Ý1ýïòCïî˜þ÷ù!ƒá‚×÷L{üÁðÁkû¦?½þH`ø`µýÓÞÿ$0|0Zþéï’>-tÇ÷¿É  ¿ºcûß䆆 _Ý1ýïòCïî˜þ÷ù!ƒá‚×÷L{üÁðÁkû¦?½þH`ø`µýÓÞÿ$0|0Zþéï’>-tÇ÷¿É  ¿ºcûß䆆 _Ý1ýïòCïîˆþûù!„Cô±8sê–mÎæ?×ÿéC ü,ÏýÒϾÿÒ†…™ÿºY÷ßúPÁðµ?÷K>ûÿJyÎ(âÛ\NøD¶aÙky‰qñ%BD@€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒíß<-ó)¿'½Jƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&Oz ú3áfìù߉½íýð±h>G7wÎ_ï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ |ð·Ì¦üLžôƒù”߉“ÞðcÂß2›ñ2{Ð> x[æS~&OzÁ |ÊoÄÉï@ø1áo™Mø™=è<-ó)¿'½àÇ…¾e7âd÷ õÈ"“¸ Ý¿ zl€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©P`=¤è¿6Ð Ú‹€õ” Á:$ô ÀppØ úŠ^×~‚ƒd= ÊQeæÑu¸E§wB_ñr ÎB º)PùIo0‡œsëϤœ¥|P6áÿG›ã÷o»Ô‚Ú¤î(7oȤ(>iBü8ïÒ6jå™ a’¹Ý‡ úP\áZ¸›9/äšD-~«DOMŽï©¿êPv³\Wc’}¸;—žÆ¹ñ7GÍ PZá®(«ÄJèb’ ¡ IÎÈßqÄt(9ÿHuâ¿-†e¥—š³Ú/Rý÷ô àEúE¯†KØku*N«²ã¶Ÿ§»¯Ôƒ­Ä+ƒKBÒ4ƒ9®/¯†ÆÑ»äϰ.±Œp]ïýPbï֧ó†»ÞË%¡±€A;ß_£E™.&“‹¥m¸›V%ºl1u,:Þ‰þH+axÖ,–[õUÌ|øûdVJwÍÓzîuAÄÈó~§ð³ýŽA~i±? ‘Âì|Æÿ(ÕŽ×â|Žþ_>º £7ÿ8£þÿôwêqty!~+MöYþ²Èx´óÝׯO¥£È2€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒI_ÙÄçë| zyÌ7¿)’Ž£©¶1 '˜?zÐß™¦AƒÐ áZâÚq^–•J·23ÁÒQNqó°7ô éc2,ÉÔYðâÒË–<Ó¸ ¶ƒ™hO—ÉcûÓAŒy6ùùšOw‡r 0yQšÃVȈL"v“È]Í®¤wýH:0‚-Áˆk¾V¶Yù»6÷hlëÔ‚#}Ã,(y,ú0ö¾QËýXë®]ùüP[A”@A”IÜPnß=H2P|øFçÿHj/ÐŽiß5­ý(;ߣì¼Ð>~½ñ,Us» ï0?¿Qê>‚‚ ÙÜçñÚ•r‘bªÕæ;ìÉÚß_VÏ€A[ôsÌü–h6^Ñ΋¤Üç™Ý~¾ô~ŒlV¬rqX•‘X.aÓÜHߘþh:¿¤+ÙàþÖ¼¬–3e€=Ž ßPƒ“‘»Eߢª²hÌœ±±¬ÁáÛ==¥ô6èÛKO#^K‡˜î_ôA~^Éæ°0QQô9ZöFf†‡_8îA§é»ªàpU ‘Ñ4°¼w;LhÚ¡R†8)ÃL c#hk@èsx¿þÉäÿðîAó*¸Ç ·ˆéÎþÞ­“¶Žæ5§åQÑ(=¾C,x‹ôsrÝa©L:•þËÃÕ­ŸQAÎáûØèÿFVâ–XÚæ²fÈÂá²ã¾^ž;ØAW‡ñ²ßýä¢p$ö®–|ì =>°PUá×ÿH2x*/øÑѧ0~úèíÍèZƒ ¥ÈÖÃðÓ‰æ†ô­{|Ø~_®ân!ÉÃÄu84TÄ¡€Îöƒ®m뿸qˆ?IÔb³’ýa3ÐùƒuñOÅÐó ¿ÿ›Õ?…Ÿìr ÏÿÎ8¿„ý"ƒ›Äð_µúEž¶4†Ùš&ÆÒ]Ë cë׿Ðv?F×k׎ÞX{ñH\ýô2Ó¯ð÷k̃Þ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A„»9ÉêWÆÉg±Há#Z>0Þºý8xªÆžVÔµðr!pk[Û·ú­·»©ë´7{vI]õåácˆ=àÔ}÷$|T§‘Ÿ-‘¹Íõ€ƒ…ÀÆÎ£+Gǰ,®ñsËŽÉA'^µ^*4©Ì`›#mµû`6ci¸§A+0ÏÃLcàÈ\³Võ&ž+Sv\ kÚOqzoCþØq7ù° ã×ËM‹ýáÅy_ –äv¬a{£is‹œÑât: ±‹É üIF¾6ÖZÝK!ì²Û±I¦;[kܺô!¬}k>t&M<s¯qíñE)róå¯V|“HÚÑ×›‘µ¤µ¿_ôÙÚ ·3Ù+|^üs˜o Œò3 s„¼¤ëÌuÔ ¹’©k‡ò«ånØu›­‚Ë'—™’Ô7¹º=ÚAf^ω,7;#Cæ°Rš´…ƒ¯ÎáÜíùúi®¢Á(XÛ²ÀÏps¤éÞHïÚ "“¸ Ý¿ zd äÕáœ}<Üùˆ„¾U?792ÞºßO©vxWk6ÌË„±Üaž9 A#γ›Êc#¦¼=H<àýð𗜲Én÷Ù™/½[5ÃXüôPEu²r×$°G!n¶5õ÷ 걡 Ài 0ä¨ÍNÇ7e; Êtt~”±˜*X¬c±ÕØçWqqsdw6ù»ÐA…áŒ~¶m.Ø2p9Ù$œÍéôbtߣ®šÉ›°–0NÌqÊC=ž¢¯N½ZŒ«,Ž7•¬hÐ3 Âx¼©lÑd¢IYÈKä.ÐÞú Å^ÅTÎ;1ryKžçõ–‚îý­sœ'‹â c–ë%¼¡ñ?”‘æ>tÕàÌ5;tíW®èå§¾BןŒNú»Îz”%á¬|Ùøóoù\`‘'Åè5Ýê(á¬{ø€g—Ë4iñ~O/w©Ü5AÜ@3„Kå€hÓâüž^ïRçá\tù¶f@–+ óE!hq:Ž‡Îƒ´:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A¤¡Æ'†ü®S¯Z ‚ÆeàË6I«ÙcyçoDòzú `q™ˆ2\òÖ³1<9Ï'Dò?×H%áŒvV¶j9,×±|®çs÷¢uÓ~tØ´‚7Ñ—«†Ïðû¥¯„–•Œ{ä/Ž eÍt$ž Þ[¿ˆÈå±ŹëÔ­8± °µÆ6¼¡£Ô7˜Êe)ÙÍ:œPPy–8j—;´“ZqpÌ‚Íl=ˆs¹{î|f;ÑÆÈÀ'™¥­ ï§Ò‚Œ\+?ôB–)öYÚ.Ã<`¹­‘®$wõ#®Š…Ä®¶ÏÖ-ÆÇ] óšî{žó®šß@6‚‹19Ü=ێ¾ŒµnJgì­4ÂóߢÞð|È%Ëbò³_Æehº«®ÓcÙ$r—67‡ˆލ'pñH’ðúåÇdh 8Ôw Íž‘¼)[ Nf9ðÏÏ–]ŽrÎãЧª†{>TãûÆß%ºËçßV·{C¿ªrqqï³^¼8©jL XéËÚׯh9ì3õ>¦;µ2ùÆHª×«-™´8V÷l’Pb{-}Œi•–a’YËpSÑãÄ ³Vó->ËZÇ7É¥1»zê@§µAœôªØŠ¼²>Ù"(AÇ[ÞÎô;]©<–".–´•Ü  ý`ƒÜ‚¤î(7oȤqÐÙðA曑ý~j»•ÏìÌŸÇwsë¿›þH$²é_“´Ëlºñ *Gpc†»ÉoŽûöPi^´íÅàXè$Špd§âtw™ìÛ^ÇP²#{Ùù9\@å#z´ƒ¦âÙlrvO£ ç0޼û×_¡;ç4¸…¢ •ç³§ãüAÝçA.@Tå€XŽävì篋›Ó»`}ÅÛÓ$¶âlÙe<¿'â®ï,?w# •¬=óÎd‡³ˆðZpêÂoÿw'Ø@íÿîäû¿ýÜŸa·ÿ»“ì vÿ÷r}„ßþîO°ÛÿÝÉö;û¹>Âoÿw'Ø@íÿîäû¿ýÜŸap{Cšv s8‡™økÇ#Y,­å`/ .>aô”V×ë8KXøŒ25Ï{Ç(ŒÔ’{}:7¶F‡1ÁÍ=A` ËþC½H<çÙ ßÇ/ûÜ‚ž&ôx»|_~V¹ÑÖ·Ú9­ï FDQþ?ù  ŸqŒÏ—<èÜO,Q´0zú’ƒ%™åŒG,ò=ƒ¹®y }Eµày¤“4N$¶)tÍøl¤•Ãm ø ñXÍ~ ü6]– š¼²vnl{faq-- }=È-`ƒ«ÑÍf²%Ž ö8Ñ?² hó½ ¡Œ–¤zóÆùDr1Ýuñ:s´òUhÇØVbkLʧ)cØî` ‰$ƒÔuó ôyùñ°bÜ2ЙjJáÇdd~$ tïðAÁáyµžš¶&Õ«8FÁÍ»űKÍѬs†Èׂb€€€€€€€€€€€€‚);Š Ûò©³~ÂOá(7oɤ@A©s@$ï%¯kÆÚàáçhŒå”I°´@A‚tÌ<è2€€‚´¿ýoSPX2€‚9fŠÌ’ÈØØÞ÷8è­LDÏhDÌGyb 5íGÚWš9YÝÌÇôI‰ŽÒE¢|’(I°ƒ(0€=ÅPGcöþƒ ÿˆ“ø[ÿ4  ÒIc‰…òHÖ4w¹Ç@ G,r°>7µí=ŧ` Ùh@ØÞ·ÕPEí%þ/ù  ©kJëÃìÕŠW4h7ª£øîø>Ê •ëAR!x™c¹¬$@Ò5ô ké(úJ@Ò   ŠNâƒvüêA¬ß°“øJ ÛòG©P`÷ ù®S#kˆx¨Ql±²´SG¯-ü½üÚï'_ê½M:WKKŠc¼¼½KÛWW‡=•¤ÉÛá¾(›±c!h’´.&246üæ¯uÖÒî¬ÞÚZ³£êQ¸=èpØ^F1ÙêÄç»ÏfË$ÏP¯=¹k×|2¹Ü– @‘­l‚<ë§K¶¦#3ÙÏ«ßR"gÝ ,˱øüŒîšKôêÙlpÊ]·9§”¯¢~µkiñZ±¦cºµÔá­§Î!Ó—1bµA-œydÒÌ"¯eÒݳÜß÷ëK(Ó‰œDök:“XïÙ¯—°ûS±DEm±v±°LÙ½tvºh÷ôQm:ÄqDæ>Eu&g†có¸âÖ`‘·H™%™¡ÓÇd‰$&muÐ览 ªùH¤O”mú9©1Ëâ˜óýWe–[‰rÓJþXâ§ Ü|ä®yŒéV#yoœjÚghI[=;ìUm¼{«Ew¤2‡ë`8ñIµ¥™¬çkF¬æ3Ï“²Âų(+KÿÖõ5€ƒ(<Ú£?ch_±©<–çèo_™vèVñ§kV;ú—½«:•­§²,]¬U.2ìqvamKUÆÙþ!”€}:S©]KhæñÞ%v¤kb“Ú^›7#âÁÞ’79mw–¹§DSÔ.](Íâ'wV¤â’àW•µlb<‡'=‰¬¹¢ÅwØ3ÂݹÚ;åÑ]Ž(¿qåèæ¬ðÍxg9ýÝkù‹˜âù¦Æ#ᮘN9´HܺîÙóícM:ß´Ov÷Ôµ{Ìvf|Å“’š• °úík¤.œGòºŽPA߯¹#N8bÖœgô'Vx¦µŒà±™³úÅôiPlóEd>q·ÜC¾îþä®xx­8-©h· CgfÛ×£±eäp6rK·ÎÒäA #Nf"cÖpždDÌLy"‡ˆ{ZT¦òRÙ­Xòs XÜ æÙ׆¶¦t±iŒùFUlÖ'}š?ˆ¦ì¦¹9ÒãàsšùÄ 8†9Ífº×ÇÁZ4cµf{Ê'Zq3Ú[;+ÑS©LÚ–x;hÈ5¤oÄ‘ÐhïjµÒÍx­8ï…­«‹pÖ3Û$¹‹Fë©ÕÇöò@ƾÇõÁ¡…ÝCA×SìQ˯ÏŸ‘Ì´Û†#ËÍ <ÉcÏ0æšS§wŽåÔÇÉs§—ms·Gcöþƒ ÿˆ“ø[ÿ4  •qæ^ —CHÜq£\ËÙnWoã;‰H/þŒeœd25á| Æ‡4¼kNæÐéàHïõ õNû6¼—FwÁbãœã#¢Æ0l;¹GÖ‚®O+5ÏÑü™Hø'14<±Å®cÃÃ^6;ºíµŸN%¥O”žä3Ç!µì™Ä`Êîc²Ó¾ýPZáh‹gÊ—MbNÊóá`–w¼5€Ù>sÕ™+°ãøº;Vd,†,lŽqÿüðñ>d¥ÎÙŽµ0q®®“ÙTt r´ ’÷k¦†·ßÔéCŠ9"q±IÐɶV´ÎÐ?Éx:øÍê<Ýè&ÉqxÛVbuwHÚµ ™ž×|žºkuç:>Ä1—¯ÛÛ­ã㯘ã²%߇@=È:("‹ö’ÿü‚ PE'qA»~@õ ÖoØIü%íù#Ôƒ(0zŽˆ<W‡eÅñ7ëSN{t!˜¶¿W±ÝýG›}W¥§­ÒàÎ%æêhðjqã0Š\4¼SÄ"äT-Rªý²9I#ÑHÐS±£§Ã3™ôÂ'Nuµ8¢1«èlo+C@Ðó›“źîzŒòÖŽj±E+_Ú@qÖºQ[Rü:s=û1µ8µ"f;woœÇÉg ê”áo7PæßO£aR5"µ®ñ+[Nmkm0«ŠÅ,Vl¼7 3C®kM‘…»å4wõõx«êjf'Ïè­4ñ1š~ïJ;—+©”¥ÿëzš‚ÀA”-ãšóŽ%žnÆNÉÌg+ùO)ø¾uëøKG*!äø¨ždÊ Wž^" öC#ÚÉÚç84Ѿò|šöˆÓ·vz3©WÕ2ÐKkr¼-æ–XÆ ëd‚ñôæ+x™zú‘6¤Ä5Æc £N&²¬0ËÙ5²0HvGU7¼ÚÓݤV#³ÌÚÁdlÕµ ˜ÖÏuó‹²L-æØ Øé¡­Þº««H´LN#g-´¯5˜Ç}Ý,åK7d{ ļÙcu^ó'k9»Éß6ð뵎¢±Þݽa®­fÞUïé,f)Ú´Áê§Ïe‘ Øæle×S½‚úë®ÔéZµüØJÚݸ{îÆO zÕš$jA$M‚óÀø¡ÍvþGÖ§OVµ‰úÁ}+Zcé) ÄZg¾Ã£BÇ:xÏ0ý£ÚÖ‘®ÿ~µYÔ‰Òǯ—ì˜Ó˜ÕϧŸîƒÈrµ±v0°Ò2C#"µÚ´1¬y'nû=ÁLÚ–¼jLþß%b·­f‘ºüÉ«ç+JÖî¼4 ä•ÌÝtõI¼M&=fr¼Rbñ>‘FèoãóVì×§åPÝŸ‹#Zc{G/]ø£Ñ3[R"g-[ÌÄf%c‡iÙ¡ˆdØÖÌ$‘Î ;^OüÓZÑkæ¾KhÖkLYÔXµGcöþƒ ÿˆ“ø[ÿ4  ò¤ü~8ñüí}™ o{.kyI:×vû¶ƒÉpgÉ—œ]¼ÂÚq»ä8u‘Àõi¨ñAôúXú˜Úý…*ÑÁëËt6ƒ‘'Œ®vÝ̤0ÆÖCP6bß‹Þç|SâN´|È(ÚáÛ°bs8š5¹êØs%ª ƒ¡$s´’wÞ7×΃ÓW¥V“*ÔŠõ"&ïØƒ—Ã^¯6HÜ¢úâÅ·OtŒvÁkâ“×¢s|:ìÎv)ek›*=¬™¯ÑŠn`XàÕl¬óGBLn=ñ*º`àdßÊkA!£]>Ÿ2F€‚(¿i/ñÈ •Rw·äR fý„ŸÂPnß’=H2€ƒ@Ò         ­/ÿ[ÔÔ   Áh=<`4¡Ð}6@A„ Ê iPaPGcöþƒ ÿˆ“ø[ÿ4  Æ4ƒ(0‚®GR¸‚Á—³ÞÈŽW3›¦´uÞ>„à pDÈb`dq´5­hÐw Ê¢ý¤¿Åÿ ‚T^Þr6¾¥È5lŒ{‹ZàK{À;Ò ÐaPE'qA»~@õ ÖoØIü%íù#Ôƒ(+KÿÖõ5€ƒ(#±ûÿ A†ÄIü-ÿš PEí%þ/ù  Áè (>m¢ÎzL|„O‰¯2ósêF†–†÷ø ¼mqöi LÛ4f>üHõ Æ‹ÏnÍø6íÇÊÉ9Œ®=Ûó}h=É!­$ÞJ-S%žâS%¬Tõ±Øæ½Ì†Ya2É>Ž‹µ°ÚÆ<äiQ™ù«u¥1’á,Q–À7· ž½ýÈ3fó)Az)lIhØÙ²yu½ý: Šn)ÁÁtÒ—)]“‡r–—tióÜÖ‚õ›õª>X‘ºÌ‚(C¿¶â7¡ì@šõjö`­,ìdÖI°÷¿Cg_R vø“AÒ²ÖBß ÃÒO0qC¯wT¡ÊQ±CËâ· êò—vÁã”ßÕ\áò¶|š–B)¦Ñp`Ø$ò6:„TIÜPnß=Ho;Þí Ð6P5Ú7ì4Yxß±üÐ9eýã~Çó@å—÷ûÍ–_Þ7ì4Yxß±üÐ9eýã~Çó@å—÷ûÍ–_Þ7ì4Yxß±üÐ9eýã~Çó@å—÷ûÍ–_Þ7ì4Yxß±üÐ9eýã~ÇóA£¡‘ÜÛ”|moâ4òËûÆýæË/ïö?š,¿¼oØþh²þñ¿cù rËûÆýæË/ïö?š,¿¼oØþh²þñ¿cù rËûÆýæË/ïö?š,¿¼oØþh²þñ¿cù rËûÆýæË/ïö?š,¿¼oØþh²þñ¿cù rËûÆý惎G´´Èݯ‘üÐm×9Îpqv»†H€€€€€€€€€€€€€€€‚#ÃÜæ<ã² vË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæË7ï[ö?š,ß½oØþh³~õ¿cù rÍûÖýæƒvóüb ó¤ Õícšz4ƒçùu~·t޶÷´ÃñoqÞ»÷ÔtAÎk±‘È%Äš;¼=EÀ÷}H;bƒÚÛÓRš&9ñ¹£ÖB3Á®u®‚•[&­ºÍt809м8ïâŸùùÐENÖEÕxžŽBñ¹äQF÷FÖ‰= œ3XUàJŽ©lÏ¥ÚÕÏ-ÞÏœí^‚‹ÿF±‰XÇC-G¾rà:»©q?Nÿ$æÉ;ð%’îÓË"æ.ï×#€ßÕ¤œì¤    ÕÏc5Îö·˜èlëg̃(2€€€€€€ƒW=Œ-{Z\tÐN¶|Áv<è2€ƒé¿@@@@@@A„@@A„A«dc˃×;Gz>bƒd½ì¼Ò=¬nõ· Ô=Ž{˜Òæë™ õ´Ç<3¦ŽBç8Sô빈1_öCÖ2‚TIÜPnß=H17ì$þù ÔwAåxÄM”žŸÖ°è`>Ì’5Ü¥­Œ|^¿KË}ˆ*粿­?F_¬ãä ´î^W ׎×1£‹+Öá»N±Aõåý`Èçt°·»³;$€âv:äx&”Ù—|L--ÉMÛÜí1¤r·©îAW%_ÆÎ»ce¬ÄrµŒtŽ3V4x’|Z»Ä©E«-j‘dï<Ç-ŽX`kz’çkdõ@u;ð+ÃÆ:¸’ZÐ_ Úw]ÜìŒ;äÈÓ®­$·¿»h-eøøÛY&GOÊ"ÆÓló88ƒÎ㦳ա²| ˜[y;Œ2Ýf<ÀöC-)Ý vûÇP=¨#Ëæ-WÉVÅc*Å=ÛºRg²8ãi¹ÄORlÚÎCŽ®þÃË$‘;¥²æÅðÑåÙßÕ¤ÓÅò³…ìå]J7Ø©lU’¥æcÝÌѶ;ÄàB •³8³Õñ™JU¢"’H^g?”³[k¶]8uWã—¾,v:Hâ2½¹Jűƒ®cÌt6{OW9v¾YØìÝjÕÞúî³ ÕäsØæ´Žpy€ ïÌB}n5šGQ·=zlÇß™±DkšÄ|Ýç³ZÑó±´Þ É¿ ·’dM•Õ£ç sˆêxA¦C,úy EVÂ× ŒÎÎ.#“L.Øó÷ £‰Ïdó$uzÔ[R/‚F>ü¡¤ŽbÝho[}ÅKÜe<.¿f­zRRÇHèåÚäšbß–cnµÓéïÒ Ös×-e"Çá+WþLÛRËfG1ŒcÄ%ǯÐ4‚‘òÒÊI$=ŒŽÊØ.{å;ñëâüÔ¼uj‹ESNñ?•Ò8×9ß uwNîíÕn⌓èÙÌÕÆÁ&&³Þ tÄO#HsÚÝrë¡Ð'¨=·¢n]eÚ‚<[­þH<ÌKšžL<†WÌ8ö;åñŽRÿ¦èžQáÓh.c³y<žVÕzô eZW^y¤•ÛpÜ€®óïAW8‡'ŒÈ@ÛÕ±â´ö[_–EÓ³˜é®- ;¶o&o1g1’Æã1õ^h-™ÜÖ»™œÁ O7Ѥ/qRý»‹Ž[²øæ‘Àóí°ÅØ={úˆ=4ý©ÄÊg öç $´S½oý–ÁM xÞ ‰õ[#æ…â9KËLZˆ’@ÇN¨'›Š²æ¦Ní|]gUÅÏ,s'p|­a,kzó µä›司"§•qd“Kä.o8ÖºàNs2™K0|Og^*Ó׺Y$¦WíµŸyÔtî胫=*³ÃDdn½Ü›°[Ð sœ@> h¤ ‚>2 Ää&ž´o»Bv×0Ö›“=úä-w˜óx÷h óeÆØòµkDöÅeÌ}i ØvÆí¿öÜæXâjÂè ò‹6fmzñsr‡=Þsà“ô ­K1y¹Gâ²Õ ŠÃ«›¾´Žtr´8|` ‘õpÞk'ž¯÷Ñ‚µc:=©tŽx:Ø×/yßЂ õüÌMˆ§»+ ™Å³H懖·¯6ÐØ×Š ¦Ìemd­ÓÃÒ«/r¶y,LæH[ÍÈÍᮧ΃ˆò7ãáûvCw¬¹’G<„vµûc¹Aè=GR@ðAÕÊä34˜Ç׃"æy±iÌç‹[ÓýJmŒ‹2÷x;$ÈËfi$ wRÝÂîˆ=-Ù,ÅNGÓŠ)ghÛ,œŒïñ::ó âÐâiE»õr­¨×R¬-v´¦21Ñõu‡ w}(&Åe3"!r•J˜éb3 NçLÆëmæ¼½G˜ôA®-˜Ëˆ/š5 ÅØì/™ÆpÏ츷——¯~·Óh9£Ž$1Ç’0T¹'úWý$4»”HY­k~Þ^9ì¼íÌ~>µ(ÈÖHË3¹’ȹ­ü^½ ïÒ³¹;9Û˜ìmj'ÈœÀöY°æI(p™ ñzëgÄ ëeæ±_jz‚3bשPkÄ|в ;w¢â[ÓÖ6‚,þEù‘‰¹<Í5wË ªÙsÚ u¶¿cc¿¼ äA•ÉYà,–C1ZµºàLyÏy‘Êtš;†ŽôõÌÅÓ“f'V¼¶\X™ö$sc‰¤é£ $“£ìAVÇIÏ|Ðݺ·RÅQ&ôòð+µÔàG­𹬔YرyZUâ6`|Ð>´®“äÌ×^£¨è‚>Îdó¶× Ê/sƒšÛtðë`7—[ØîðÚ†s,q4ã|pyE‹6½xy¹CäwvÏ€OЂ¥<ÍèòŽÅå«WŠÃëºx$­#iӇƂ6=¨9pqvUøZ™Ùqu™Ž™Ìd€Nã0'8Ö¶GBw¤‹7vÆ7jõh‰+°ÉÙG3GWwxëh+Ožo—bkÔc%fAŽÏsµÙÂÖƒÍÓé-9c‹2?«xã ýMͽöÇÊ;.n^Ó—\ºñåÞôƒ±_,éø’Î+³o$5cœJvîbFµõ åËųEƒ“%ä!îfLÒì˜ó·Ó“céú;w1ïÈIT»% xfæ:d€ß ’^ÿ¡”ÅÚÌW½ÄnÆP­a‘d¤{ûy‹ Ï#~+4]óÓª•ž.d•qGÈ ÙHÌÑù\½œq0Iqóì¡âƒ“Ï¿ßí!ŒZ£z¦Ž 9Øâ$cc¼A¦ûŠílÎN,ôÜ­*Ћ‘I$¯3ŸÊY®f¿`uÑB ù!Éâ®ÆnVÇù$–Y$V‰¡ÎåkËHÑêGD%}˜sa%AlØ ½Å Rí€NüÈ/p5#W†jHêtëºh#w=py¥»y |n§ÏßރѠ ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜeœv8‡×hè‚<{ö‚ŽS–È]Æä¬biÛÇ#%ÇËglcœG+Ëtâ×wDÛÂÙ6ðÎCYPOg&ÛLlG–6³Ž té®R5ô î_ÆØ³ÄØœ„a…FXmÚ;xhn‡qAâîåhÕŽƒ¢lð\ŠÀt¤ò€ÂO‡‡DbÅd²Ù¿ÖzÐÓŠ’VŠgíKŒšçq:L6Þ,Õ£/b'ewþ±ÛCÜÁÜî^Móë^=è;ÜAŒ~c{‚7Ø„±Žwp=ãFÂGñK+…·v¥Z‘c¥s¤k,vŽ~ã-æ‡wNþ¿B gÅer9ÚWЧ–µ€ùoCg™òÆ7ñ9CA ôùG¢OYÇÞ»Ø`1YXmØtñÍeÍl—u-vØy€=Ú=Åû8ìž?=ú×R½†OU•ç®é».BÂK\Ó£Ó©Ò øc{Vër‰ÓX»-ÄIn£ãõ ÒJÜ^ìjñX«j´pJ]7#¢åq;Ö0Ós;[ k‡*ÃUÔ§2¶;¯˜‡E„’ zêáÌ@ÑÑèƒÑØš¶ ù¥.òj5þ7MžV·Íã܃Æb+Ûá˜ñ÷2Z®ÈØa–Üéew{c#•½ýCOäƒÔa1vh;-Û–·ÊïË9ª[Ži¯ö¼ÒÚÓöNùv NÉîÒOÆÙ«žÍ]”3²ºø³¦ÇÊv<: ãGùJ¼5„†(¡–î.ß”:/+d~Àv»ôáàƒÓ8OcæÉcšHH1óó¸·»~=|Pp(pýúÑp«doǶΟ¾øËG/N½PlÌáÙúEÛd&²ø~?MH>.κ}(,[ÃÛ›ú=È#ÿáÓµóíþ"Þž~¥;\5vÎ#‰jsD×äì™k’ír³\Þn­Aœ¦#!–ìœøªN·H½¯£bQ$r±Àñùt°èƒ3`lä8~Åxñt0ö{hææ—0‡7œµ£ÄÐtŒ¥œ¿Ä˜Ì¥Ú•ªCM“1Ñ2Çhâ^Ññ· 5ÓZA{ˆqv25êËMÑ‹tl²Ì"RCFÁi#¸O_iãòws¿­òUá©ØU}zõ㛵;qÎs´<À‚ß ã¬bxn• A´–¿‘Ûä÷ýh ÎÐÈI”ÅäñðEeôL¡ðI/gÌ×´ ‡hõA¥šÅe/ÛÆS­n<‰d®d¶;3 ¡¡§¯)æiÐîÑAYÜ3ˆÂ¶—enÞ2ѱ+þͳ‡sèèë«úõ[x<œùËW_Œ¡p]‚6Fë2ó D7N¥§˜l禮‚J;~µN‰â"ìIw”ÿ æÓá›3äoÏj•<}[Øï$U ˜ÎÏS =wÓè.âaͲ¤XŒ•ÞMsíÅd“ å3—`‘߳ѸÙÌmz¸{TªËN³;!q–4^À4ßêùz;Zß] çc8nÖ'³¡ýÄ]Š)~%ù [''6Ç3K .§] ±Ä8¬®fs xªÉ ÖÊ :’‚ÃCw¾þ›Ò q+)š‘ÐGŠ©Øðkdü§–HFÁß(n÷ßÓz(=-ºæÍ«sèË™ÌGœ´b&f^ÇT– õïa¤‰õmÏüƒGzh#§Š ô(dîq3:ðÔ«º ŽnÔ’â œçh쀭¹ìmÛ1ùp‰ö±ò¹ÍŠWr¶V¹¼®nõÐ÷t‚ ÔòùŠØçØ§Ykd£Ñ ¦£o6†Ï^ä[áyò,âH&{#fRHÝÁæÑk6áüMîó — ³ØÝc†±4;6žk5^Òç;_Ù€€~’‚¨á̇ÁÓðZ‹Ë H×?Äë/7~¼È/Y¡‘ƒŠáÊÔ­ˆe¨Ú³5óvnOææ7MôA'âíe±QÃQ±Êc³²W‘å°Æž¬'ÀžÄÊx[•/e2SQ¥F ñÆ&W¬Aä-æ=t$ï{èðACÍfx?Š’ Ì Y’[í3£i ä×Gt{Ò ¬›+g¤ÆÔ‚ßõUƒâ’nȃÊí8I[†.Ö¥„Œ¾)&­‘uÛnÓvàýòùô\Ôƒ68^媙èL‘Å%Ë͵Uûæ´0·˜zÛ¯R rUø‡;¿Fzu±óI½–Ëć{pèÐZÒ:yú ‹‚»ÒÉ»C^%ˆÁ]áÎ%ÚÑ$4ݯ£ëA\`3?Ñ,§y=}?µ5¬vý$ç¸Ý|]ô é\Çd©g_^fj­¯b¼“vGâµÍvˆñ „¦áœŒ¸1¼ÀëײQÝ™­y `i厺k@úJ­üm¹øŸ’„Fb­^v?™Ú<Ïååéæèƒ''k‰)å%ÅÕźï)–½žshè4€ÑÓ}vz êq.|•Jï¦ö6Ý+,³×9»ø¤ŽàA#~)ÖÇdòÁ–ÉÖ†§“Õ|AÝ©%ä9ÎЂ©áÜýÖÁê/,ˆBñþ/Å”8õט õnà´ƒÐ:Á˜Ò,äe‘âjõìmGšâ竃ò Ôà3¿Ñÿè¯e[ÈuÙyly»më³×Ë×Nýx è[Çå¨ñ#²˜ªµíÅ=FW|rÏÙËHvôv4zø £ å[ÃÍ£+ }ŸÖâãœ×ÒÎלë§~¼{:)?ˆñ–3•Zv‘´ùb/°XaÛCApå<æô:ôAüúµ0¦µj™âà0I^Ø“µÚÙƒÊàá±ÑÖð—/pÔµ"ÄÐÆO%˜¤Á å-kÚâ\àÐ7Ðø éßÆØ³ÄØ¬„|¢‘Øl„»NÛÃCt<{Š/å¿WÁIøüxž lžL—š[ IÍèí§]û>è=ÃÛ9"ŸLäÈÁ øÝv#-;óu(%¥_-Âá©W‚¼…±EožB9§ùÎÂÊ   ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜe,–"†^&C®,FÇs8>°Qôˆ-µ­cCZZÑ ÐÈ4–(æ‰ñJƾ7´µÍpØp=à æÔáŒ-Ï^„m’/Ù’ç;³þI úuPk$m–7Fñ¶½¥¤yÁAJ°Q©Jшᅑ°e£¸ Ö*5¡¹=Èâ žÈh•àŸÊo³e„îR¯ªú¶£í!“å7˜·X  Í:u¨UŽ­HY  1Œ +þÈzÏæPJ€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©&ý„ŸÂ$Žàƒ(<öC#“»Ÿ8а8é­k{‰:'g¹lãóX~|“eü¢cr²XàlN t[}Pu¢¼ÑžÈÀûIJ´Èè+a›ãsøï]ÞA?Šq™‘V„ØÖugÍ£e€;ËG^}H “pñ¶WîÓâ‚GG<̪÷2ÊyÝ­wTÈqC)gêcNÌížJé"ÏôuË®ñÔìøtA;¸–—¾œpܘÇ/c$°Õ{âcýáÓ¦ÆüÈ:v,CR¼–,HØ¢‰¥Ï{Žƒ@ï(9Øþ"©’í{ÑÆÈŒ¢Iª={|í$uõw Å%£~û)­×žVÄÛUjÑÞ[¾ô¤ã\išØä¶èÙIÐi~µ¾àƒ·’â*8»>M+lÍ0µ|u t¦6uøÎ×pè}ˆ1g‰qÐERH-³u…õÙR'JéÛ´<Çz ÍÄ4kã«ÜXo”»’;vÏw_Šß¾…ø¼µL¼–«ž o1ËŒ,|nÙsOPPsdʺ¯^ŽÕ¾Î|k'pyÓXyÜ ½©¸ŽzU,½¶I»ÿ`qšN›èÁÔtëÕ%ÆYeWÇ,€ZœÖhtdJ%äžž(6½Ä8Üu‰á³3šêð6y4Âàçr´tþÑ=ï(%Æå¢É™ZÊ·+º-s6ÕwDNûµ¾þ₆G‰…"­Šò+Sv°ºG>(þâå×xêv|: –÷c¨Z–»Å™\aÕë¾FÀßÇ té×^dXfŠÄ,ž$r49iØp=Ä åž& rN£ vì>9D2Kg¾(Þ²ç |þdgãåÉ;³5ˆç0Ê"Î\{ƒz÷úÐFx³ÛžN]?'mäþSØ»°o\§v÷Ó×ѹ>#§‰™ñØ‚óÛå’¯{#oœ¸ «“⨨d1•¢­bÔwšéBçí»ºï=Ûó¨,d8ŸŽÐJÛRÉbY[wIذ÷ëäøût¢³õYj‰a‘ìs:‡4‚<è8¸N*‹+Éf­b¤Ud—rË šÀÆuqèâG‚ èñF:ý˜«Æ-Dëšî±]Ѷpþ!#¯N¾¤±ÙñU™yrV${cË>µvó8üžV5£©ï(/世†2Á¯+lË+#ÊÊõÝ)‰‡ûOå;%ÅÓe'¾gÈÛì.«ØÆé;]tï'cC܃q>2z.:Ik¶‰Õ–O™$]66Ó×®úyÐMŒÌÓ|ŒŽ­ØƇf³¢Ø=ÄÞ‚–W‰›ŒÏÓÆy©„ñ>Gº(ò5­rë¿¿¯›§¸›È³ùÊöÍË,¯,f( Ò˜™ÙIt>(;Rçi³_!žÔ6´aòh]#Ÿ±¾áÝÓ΃Ÿ{‹êCÃW2õ"žWÕ.о'5ÑÈ<;Ú:”|}áª'l3ó®Yá1»ÃÀõÒ Sq5ò/£vìÉÄr¾½gÈÈœ|à4_«Å2 :’ƒÉðÎw¶ÇÜÎå¯ßdmtœÌš.Zìo9 éÕÃZï'ewÄò6F2Í{£bµ¢sÙÝÌï’ qÜKÊØl4E‰¶Í Â8ÈßÅs»éÝêAk#“‹n’3ÊÆWÒ¸žþáÝõ ¨Þ'Æ?.W´•°C(ŠVº"$üÁ¼®iêÈArÎF½KÕiJ]Û[1ÒAäw_…žoI“Âg[Ue©ŽÊSš#zmÄh?ÙAoÅt¬ÅŽ­3¬ö¶£kYbHئ“—d5úÑ=þÄ/ñV3jh&tîõåÅŸîçpèßù ’ÿãñöã©!š[EÛEºGHÝëâë¿Ïêê‚–CŒ*ÁÃsæ)E4ý“û#¢sLroD ±”â*˜™_õïH#gi$Õ{Ù|åÀi÷¸ƒB­iß+æõä̯‘ótßÅhêzuA¼KB¾>“2Ôfy qWuw žáà#ÖÐeüEF,coÎËP5òö,†Zîl¯¢ÞJ EÄø¹1ö®¾I e3Ëb9¢s$ǸwììkÎRuKV¬Er”UX!µYÑô=Äo¿êA633Qï•n×s«UÁî#}è/H׺7µäqi ~·Ê|¼PyAÄyðüu‡f3ε伿²Ž¦Mz<ŸëAÓµÅ8êË^Sfo%г<5œøá:ßÇp:ëÁ÷ej6õZ}¡t·#t°–µÍn¶wÝâf¶F½«ÖéÄçi¹­˜sy†B‚Ú WýõŸÌ •Rw·äR Mû ?„þH5ÁPy[“?‡¸ºÎRÅyäÇä+Çæ†#'c${×0nΈ=úï5â<„9Ît˜øìJÖÝ®:×{IÔ$€@$}:AKk!˜âªÐ5Íu¬l1Äòk©:oëÔpc.]Å2ÅìëîÓpx«f7@ðÒ' wŽþ¨$­ZfðÇØH$’[Ŭä<ÏÙv´;ÎüM<£œáëÖ£™µ†9õÜöÄçò<†h$oE7mÇG2÷ðü·Eé­´YǺ³Ì2ì€÷ì3âõæG]È;¼[FÆK…ò*³´šHÇ#==y~½iI¸ŸË0—N ÈÁTÈØeªör8tÑæô2>=î¹Å8PÛÊ_cݼ֢,Ž7˜þH£G¡óŽá½ –½i›ú=⼞A$’^!œ‡™ÛqÖ‡yß‚]ޏÚÀ‚!fÁ鯊|úKnôu'yÇd <’Â+¿M^bîmhy‰ïAÙâkjdl[ÇÙ½S6Ø€k!¬ù#·¡¶5ÃE®ïÖöÚ ùX¦ƒˆ¢ÊäìdqðYÇÇ¥¡·¥i%Ìvšã®½¼ CÃ5)UÃ5´mðK,’ó[i8¹Ä’AžäìtͧÅús²VKvVÍ_ú§Èч®„Ԡ庬ÃôMB^Q+|ŸqˆÏ0þ¸±­ —)ÇñvFÝûùZ5®²# ÔZ\Ç´´±Úcˆ#¼zÐo=\?Š–r´_Ou+mæF‡–¸‘×ÑpAVàÊ]«€Ìå|º¸€ÌË2ÓŒ¶V5Ý'&‰Ô±´þ¯Žo—[¡k!hÙ‘¦Y®´´¸µºÛ[Ó_B7áïdø®Ì•C‰¯F ™ÙýU—²BDn:ëêóè ÎfO/¿ˆâ;'N‘­$3:³–³ÉiøÍå'[€ðA+0ÔîpÆJLD÷g±$âÜsÜik<` [Ðè ë΃ZŽW”Îå«Ìke¥i cç²&i¬#—¨ÓvÇ­C…nÛ±rìÍ‹Øèƒ {v¡1È\wÌÍ9ÀÐøÚññA®jfãøÇ°ÉEo%ž#"sÀ{‹H”oEz×ãáÛ™ªù Ö_%«o³XÇ]ò s@  ‚4AÒÇ Q±Œá|m;C–xah{w¾S߯«zAÈáÜ„X6I„ÈEf;žY)amw¹³5ï.kÀ#Z=vF´‚ï @èïñß™ÚdÜCœÒ9‡#tGœw òÔqµ#Ç~¡ÍÞÎÇ;f-uX˜ãŸÖs5Í!„rž‡åwíK5v팖b«8‹YË«NËa¥\_ÊAøÛdhgcªðmëQLÚõkº9ÜØ\ãt ´GPGr®C>$ÊO‘¿—¥_ c– )±Å’@Ò×iŽ!Ã]Ý:·Vµ%:õ;q^8€ŒN4ðÞñÍÐh òqV–ßqǬȾ{227Ææµísù›§ÊA]þ(.ZÈ3ˆlájЭe²Õ¹‹=­wÆ+µ ‚Hd7¿Rl5.c³W¸„Å5ˆjå&k꘷ËÃAš1­—ŸùA÷áe'ÉZ¿-JµöÅ%yh°¹’ÎR×iŽ ë¡An |2|%¬šEg•Ó°ó0 stèzøéÓC[õÇœ¥iŸBhªµü‘9ÜÕÀòèlè뻹Ü/r̹+5a·jþ*(šèlZ…Ì{d$î>bxÖŽõÑÙù›â¬6FÃeòVCbÉN+ÊZh'®Š 0±=œCIJ:'4I<\Ž-#˜Gqñë´ V¯ã¸c‡+É%º¤l‚ÜðÀ]+$±ºå% Ÿ Ö:Vìp¿Ç7du‡óAå-=¬­ìÛ£Ü6N¿$Û‘­’£šÅæ><ñ¹‡` 8ƒÏ`ò`&¹ŠÈÅi–d¿,‘9•ÞöÎÙ¶¸8>ƒ²5¤Ž µ{!W=NÔÙAlc©F´$Døyz<»—ã6O_¡z´23£)‰áÂró–sŽ›ð?B øb9"·žçÌÊHæó4ŽaÊÞ£Î>”¬^ÇwnÛ‚À¨úÇå ïk\ãÊyAïÿ’LØ»óðàÈ.BÇfÝìbn§l$ë˜4”>V´ƒ·Ãpã&ÊØ¿NþRôýƒb|—Xæ´7›` ±½wëïA·L1ÜO‡ÊO¦¤qO ’GŸÙ¹Á¼» èè çÕ}K§Š,e)[n>Ôð s9À›¼o§Q¤änO5„Îb©X³‘¦+7É,ZˆÇ#Ÿ½º=9úÔð±âïæ)L2ÙîVkÜÈ®FàØ nœyú;ÐrlNêߣ»œ4ú6“†9#t-®÷л´Dk¯~ü}/Ù³ÔxX(\“ô{\2¬¯’¶@Ùu~B#`¸€Žºâƒ¦Ëlâ,ÅZDzs^ƒ&tóIãx0sIèI×r «ð¼,’'DþÞræ¹¥§¬®ë×èÒøžýª™ |&ͪxÙD†Åš±¼<È݆»”½uá¤Ò¹7ñ##­vIFÛ =´¬odíë]Iþ]è:ÏÊGšâìŠ0ZuhŽÒg×{×9ƒMøÀuéêAR¼†'âÂãŸrf3±~žÇ ´µÚÑÞûßЂæB¼¿©8YŒ…äÅr¡sCØ÷æAÈž¤¯æjf/檲í™%•çEb7€<ïàA>dº´…~2Ƕ(¦5áØùv>;t ×~‡reêv¥áî0Ž:ò¹ïÈ™#`aÛÚD–ãÝæAÓ³j<§ðÝêm•õÚû<Ï0¹¼¿Õ×`kªxs! ’a2YŽç–ÊX[]îlÁò5áÀ­»=4ƒ‰žµÉcsUmM“7Û,Œ†„‘bÓñ\O/Æuß6ü4ƒÐZ°Üoйi“6¼ØÓ]¯lNxí;FÓ uÓ΃„úªK–Çfïf«‹V¥{bªÇ:+¼ìkLw_ È:ì’ây§µŸ#µF¼Uæl/ƒÁaå‚AÎd¬ÄÛ3V’³Ÿ°þÊFò örównÏ“Ëѵc'+qõiÂylΤ¿”ïãt;#H5¬$Å3†2Ö«Îj×Ç:´Å‘9ÎÎ !Å o_Žîˆ.äïÇ.O ÄQEf\usNœ·×GdkH4lpãøZÆA’˜©Ò’µ‰#ÒvOs[­†‚uñHßЃ£Ã¾Îw?pך¬O ¡3FX^ÑÑõ ôÈ1_öCÖ2‚TIÜPnß=H17ì$þù Ôw@@@@@@@@@@@@@@ëç@@@@@@úÐsóûy*¾O_"úM~Û+™sœÒ5 OÉ?J TêÃBœ5+³’#cG€AÈýèZwr WýõŸÌ •Rw·äR Mû ?„þH5ÁPE%˜!2Íaºæ/xßwz P6FFéâm/w¨x Mj½}vóÅ7w;ÃwíA¿hÎpÎvó̱²<þ¤1Ï,isu°ØßŸÌƒ’ÜÔîÊåè²³qõã–=ÉËÚ9áÇDžƒ»½B+M5£’ÁŽº0÷0ÈoA¾¾ o½MÊ¢6HlÂ!Ódqúú Ò{3G~´ ¯ŠPó$¦PÓ€#M=]½øw —¥“t¤ìÁ+¢pÛz<ãé@³—¥RügŒžÃ\æ44­“æï肵<ë&Éå*Ùì`Ž”ì…tší9˜ãã׸ êI#"a’GµŒoRç´åŽXÄ‘=¯aîsNÁú ’eèÇ•n1ó´Yt]¯) tØõ’z_fålOš6Èÿ’Ç<;ÔÔã®Éj¬o³uçx$ÂÙ›&€$oc½‘+9œÞvíƒnÓæA¬v`š3,SG#{˜ð@úÂmWt¢&Ï·˜0V±ÇAÄ è šµ¦Ï^¼-cç¯ ßœuçïA»lBù fŒÊÑ·F ‡¬w Ù²Æè»FÈÂΧ˜8kÚƒY¬Ã^³ìË#[ ^ç“Ð>ÐEG#W#F;µæk¢’1&Éh#}|ÝM ñXg<Ç+{¹˜àá¿©ÒHȘ_#ÚÆ7©sŽ€´2ÌBfdѺ1½½¯£ë@eˆd‡¶d±º-o¯¾ÔršÊ“Y³a°ÎÖ¸ ÷túPXuÊ­‡¶u˜„[×9ûw¤]¹$4|¢œQÚq-åi™¬iÀÌzt~”ÉfCŒ³Çn¹‹Þ·Ý½ Ý®hsHsHØ ô(4’Õxdlr؉wÉkž>¡´—´H\Þb6¾ºõ ­{'SFÅË35±Vitš ‘ÓzןèAJîu°\ÄÅŠh²:7IÏòŒ¿}:x êCÒc•›×3=¡¶a|³F^æó†‡‚K|úó}(X†»9çš8›½nG†õAÏÎfŠ¥ZÄ1²a=¨aêv¶Aµ,¤–³ÙLs¢kYDCÊðz¿¤£H/Gf žöE,cìýh9wôr|gÿââÿúr ŒR­-Â0Ú…’Ä1R8±ãmq [#ÇGÌGG‡ñ2ÉÅqIñÃ;™ À[1AýŸŒwѸ×:ÍŽ|ÿÖ9øù¹‹ºóTÎôx8 ÈÍX#™¹s²0Ñ»uÝÑ34)XãÜ?”T¯/iRÇ7i]Ì[É­ìu×]yCÃc²\IÄï½N:š8Ú%`phì†õ¾ãô÷ ã1Öíðÿ×"´ÑKÎËÎwe#ÚÓÙµÚÙ>:pzžÃÝÅO‘òƒJ8¬HÙ#«L»’˧t k}½h+É£7é$ªWœ`—o‰¤—‰@æê;ÀÐßzd±—pbßñë»~%Ù6T—ðñÖ~2Äïw%Âá’m¢R%ÜÜÝ{‡TÉáîâ¸S‰Ý`ÑŠ+16FU¤]É »œt@×7CÓÅFî.–+ˆ8fJ5£‚I&’)$c@t1C{ºõAtaáܬÜ£K!^öCuï€<¢)ãÊ¿”é°zyuxæI#áiù^æFé"dïiÑlEà?¯€ÖÐSâ¬^+ÁysŽ¥V±}]misAèzwúÐCÅøq|)t †Xµ]·$vÚ$apÙ•ÃãIêzƒj¸û °ù ð-e«Ø¨)°? V’A¯ßG/+ØÞЂÞMñäøs‰³mÌ·f¡v»ãÌÿä\ƒµä¥ý%Lùi×{†69Z]IçÌ:w÷uAɇ ޵ÃÜUnÍ8¦±åw ${As9v[ÊOÉë×¢cˆ™ÏÀQžW8Õîqï'PxK’Çf¦5[ŒÇÅk' |dùKù¤ÑÙ]Fö6tƒÙñlN›…rŒdýòwžÓ¯@Ïw^ õ ãpö.+Yø3,ÄTÇC!cc£{ÜòAø»åÐØßyÚf!¢pÞm­ lIäþŒ²9§@w)A¦@Oc†çËÿV™ËGÚ™\C<˜;‘Äu <£C¾”JØKØûK’³V ñÏcªÐ.ås€:)w6>„ÖâéQàÞ¿^´qÜòŠn6¬<ÄmÝähë^m õ¼ZGôG0?ýœ¿í(<öóö÷å½.j:-u&Éñ{=øóôëÃH(ðþõÌn#Xaë˜äŽgÜcßå“òØòGRvA÷„.±ÕXp|_dn1Õµá^R].¾†ò¼æ=ff¬áëµÌ,tMªðØËAM:éôh òo­R. á˜Û 1S»5A|±¡¢PYý²;öí´Zõëc¸ú:ØØc¯¸ç¾ÜP´5»7Ž€õpß™üRÈlæ°ïº„Ö$ícÈ’@Âck¼ýwÑ>-§ J˜ºú´ ©>D 8vp¹Ü¤´?”tÀzÎVµ†½‹Âñ4Ó7^4 (—r1á¤sr5±íÒ ³ølu_ÑÔ’ÇJÕÑ@÷Ìæ÷»™»qqêOSíAÑÌGI¹~–<¯d–" M! “±Ü>”qš —…¢Î,ÓZÆ@I‰£¶Œé›þÎöƒ¿ú²–Gô‰’7*Åe±Ñ¯ÊÉXÐKŸ×G¦ô‚ÇŽÏ$-ØŽ ¶b¾‹!  âñ#àÊCÄ3G‰Å†Pa†{–ÉíKÃ69465±¢HÙAvŒ…ÜMÃ’Lí¾\#Ç3»ÜïêÉõŸ̘­oÇ2†Ç3Yk™®Ð 9±°>‘£ÕO7ŠÇº× RmHYVKŽsâ¬wõ.'`t;×_:,DìNW‰âÃÂÚçõLs28Ê;MH9€7 ;¼È4³GC ÷qÃÉ-VlSD<Áß´w{o1;ó ¿‡ÆR·ÆAfÕhç|¢ì»F‡Ïf p¸÷uïè‚¥ èÕáüÌשÅq£1c²®øÃûYK€k@>$ø •Ô+pÇ 6½¬ukv²–ØÙ`Ðd.™ç =4ÝÝáô ©Fˆó´&©Ž®Å±ÒצÒbæÛ´H |mkÃÌ‚V¬_£Œ°W…šz.‘Ì`îçIçÖƒ±Z§gÛ—­‡@!l,°ÐàÈK6KAîÛ¹¶G™(ÿXðç AhºjîÌ:8ˉøðŽÐ7¯ˆå}!{R¾;®W¥^:ÐËŒŽGÅ X\$ ›×Dñ&lÊW+ÇNÔ”ÚÿúѸå×_8k¡ÑPq;|vb^ƒÈc­Š–KÔݮϷ` 4ëâž¼Äx ýYÁã›ZqSdå±±¹ü„±®§Wxó¤: ôx“3OqUv#µ± M ceÛƒN‡@KwëÒC)ã«ð %B(FKµ«ÙNÀ;WÊ\›¾óÓ˜æB ìcnfø‹ˆau\TîkÛ âþÒ‹)fÐÙ'c]P_³Ã÷»mÙE ÍŠT{ ëÚêÉGhÇÑÇZÙ(;Ø,–:Þ%6²¬V!悹!¤Þñ×Ђ‡r¾ UyÏý |ŒQÚé®f‰ßAph(+ÇZ¾;Ž\lWŠ\kßnZÐààâÑÐ ø„à e8x[uµã6¥ƒãÎZ ÈßÉßxh wtA.vHîç«âcÅP¹e•c´¿Ö8˜]ËÐh’IVyºŽàŠŒiadyö²1%hŸ nÿ³æútrÓY‚ÇËL¼LÚµË\Îñý[¶GÕ´\£ŒÆ?†gÃA3Ën8ÚøšÃÏÌGÊë³âƒ–ì{¯¶^`Ój[µ8¦™ËÍÿÎAöPvxVÙÏf§ÍH:ÃF £§sÈí$ÿRÔ‚\Í V8ó å+ËÚU³ÍÚD×sòk{u×^dPÃã²\MÄνNZš8Ú%`ph1 èãô÷ ã1Öíp÷Ö"¼ÑJde×;²{ÚÓÙµúÙ>:pzžÃÝÅXÈ›ŒQX‘’GV‘w$'—Nè@Öú¥¡@AŠÿ²³ù”  ŠNâƒvüêA‰¿a'ðŸÉ£¸ ÊN³n>ãa`°öÝ(´€}[AêŒICÈáòYK‹áåø®$ìì}'ªíàq7íGjÞ> §ˆÉÍP>‘ëAÎÎãmÍŠ8,N>êÚaŽI‹Ã[\ï¦kn:$¥r¼Õ­x[ËL `ó4Düm)eï«m‚9Éoí8Ñ>Ôn>›%¯+kF$­ŠÖ6Ñæ±YB¤fÁexÚm;š}ÚkgÏÐi‘ã(Äj˜êDÃM…•ôÝvM æè aÄc«deÈAN(íÌ5$Ísý~Àƒ9 F?*ØÛ~œVDNægh6Z~‚‚X©V‚Yå†1öt®hêò?WD¿ Œ“ÜcèÀêMlƒxô3ŽÄãñº}H«1îæpºæ>sçA‹¸|vFxg¹N)å®wÞÞ¬õZáì=ë¢í¬ei¬ XøÁ']ÛóëéAqÔë¾ÜvÝ ñ5ÌŽM|f´ë`zô=ˆ+¿ ‹’¤ÕB»«Ï!’H‹+ž{ݯ?Ò‚88{Z„Ô!Æ×ek¶ˆ3¤ž¿: rÒ­<°K, {ë;š8lÆu­7Nˆ)×á¼-KÞ[_^;ÅÂ@Πžò<ÅBX£š'E+øÞ9\× ‡19µøcV A‹¬È¬´6ftxó£èAÑ–¼3Àè&‰’Döòº7·m#ÌG™=‡Åö¾CޝlÞY9Yò›æ?GЃøs {_SZ1üísYÕ§Dt> ö ™¸|srg&Úq Žo)œ7ã­uóôè‚Ü7…©ËëbëEgd‰ÍOyo©lpîÕ×ܱŒ­-‡·•ò:0K†µ×ÏÓÅø¢Ž›L cÖŽàÐ!‡J¼¶e†¬Q¾Û¹§s[£)Ö¶ï?D7 f0cFÒþœ7âwï»×Õmâ1×íAjÝ8¦ž±ÜR9¿wÐúÆÐHÜu6W±]µ£lV\÷LÀÞ’|¢|ûñA,PÇ,†&GCXÖ÷4€ø¸gÏ–,UV½ï.ÿh88æêèƒIJæC %z.W=…ÌsË¬ÌæÜÃch8ØÞ{sÔoWÁVÁÃSŸ´ì¥kŸ>Û Í7§/_0Aè¿TãÿVœo‘Ãän¦_ˆA;==}PIä5 DÖˆÕ ö% ³—ͯ2 ´x{Œl­¥Ž‚;y$äoËo˜ýB ÝŒ¢êpÓuHzå†(‹~+ ~N‡†Mb¼6ëÉ^Äm–)ZXö+ˆî>´RÃãq§t©CääÛ¢[½èŸ½z †ÃW‰ñC®È䕲¹¡½ ÚvÓ¯8(7Èà±Yg±ù Y|cMtÙÍ¿7ЂÃèS‘µÚúÑUáðQ¨œ´xt%E:âéº!g”˜Äf]|bÀw­ù·Õ|–—tnÈQ†Ë£0¼u÷ùm6cÜt´`}6€`än»´<k 1ØÆãàÜIt›i'®úøý(#n• Mª˜ÊqWí£xå`×3‹H>?Z \=Âô1ôqóÙÆ×nN ìd’†‚CÃtH=ÛúP_Èðþ#/#%Èc«Ù{šç³¨mù¾„Üá¼.@Ämã+ËØ°Fͳä°w7§‡Ðƒ# \f«dFš*Õux!klaÄGÔú]³V •ß^Ì,šŸÛ\>‚¾? ÅFøèR†»d;#t]ë=åõj×£V:µad0Ä9YƒG˜ ­ÂbòÏù 0Ùt_!Ò7e¾qêúoú£Ø þEˆL' ¢@wͯ>ÐJÊu£±=†@ÆÍ`4Lð:ÈÐߟAZ\?‡ÆÚuªXÚðLàA{¢ïÍõ °Ì}8¯Ky•£m©š$Á¿ÍÀŸ2 Ó¡SDzhë¶I ·AÏ=çÖPGÄãò¬—éÅdDîfvÙiúK*ÐM<ÐÀÈä°A•Í^@ÐßÕÑ/Ãc$Æ cèÀêMlƒußAáÕqØœ~"'E§f=Üη\ÇÎ|è. ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜe£;Nϼúæåß]yôƒm ªì•fecÆï(’NÑËÓ”_>Ê [é´  mah@ÚÐU“%Z,¤×yEˆŸ+oNVë}~°‚Ò d1‡´½ Ðz€{¿$Ú  ÆÐ6´A„  Ê #$o36FÚwÔt(6Ú  ÆÐ6´ „  ÃdcžXÒöëm¨ßr ØÌ•lµ¯T.0˾Ræèô$ž°Pl/ÖvEØñ!ò–D%s9OF“ wÝÞ;XØ@@@ÚÆÐ6€€°°´  w m½Vd«I•—Òï(ŠÌáËÓ•Ä×Ö ]è ŽýYnÍI“4Ø­|‘øµ®î>£¢Zý[rXŽ¼Í‘Õ¤ì¥ ßÄ~·¯_P‚t  l mh@Ú '•«Ë<›ä‰…îÐÙÐ?’é\‡!F b±dapÑ-#c¢ öÞ€‚¬Ù:ÕòUqò9ݽ¶½ÑÝ‚vχz H2ƒ2ƒQ# †0öó²ÝõןH+c²U²µMš®qŒHøþ3tyšâÓþ  ´€ƒ(1_öCÖ2‚TIÜPnß=H17ì$þù ÔwAä›FII6]úÂã(Ã'#ÐÒ;G|O“ò|ußÔõAZ9ò¹, î#/b´ºgWªÐÞÄ22@kÁq<½Nüz ­›â#G5ŽÌ2d±†.ޱ…ïaæyÍnöOЂîfîCÌ> Þ¿fK}¤–mÔˆ>g†€tÀš pðè Óeó•¸S6òëÑ:¤±ù «°òJö9ÍØpÖŽŽÆüAAÔþ#Š1PI–³r,ˆ™³G?/+\Öó3@rŽñ¤q6m34È3y<\ƒ§g]Á¢­†lò¶3Ë×âë¦ù¶uø§!k‰g‘H#±jÌU™+›Ì#/pÚñÐAK+å¼1ÃöìÔ·vÄŽŽ(pµâ'9Á¼À:|mëèËpÌTÊZÈWÈÈø%m®RZðÂàæ‡BúPq_o4þÊgFjÓf¡f~ÂÙ¹¬“\¯Û·ÝßÐiYŽÉc3øQ.VŶdûFXŠPÞF¸GÎ ¯MlôAZ°Ëå ÎYËUÝFäìªÈÃ9G(sìa¾šéÑÔòNÉñ ä¤g+¬b'•Íoœödé.¦{9v¬z£3=™gÉ›Su %úå[Øo^m÷ ìcªIý;ÎLry`ewö|íåx-qå#—¸uÖ¼è8ô³Ù»•keë73=‰§kfÔÿ¢K´Z·°Þ¼Ûï=æNÌ51–gžß’Edº}Ùý:=å–ÄäìÁÄõ*2ÞZÅKu¥‘Ã%iæfˆs:®§§©¾Žþj¥\ý¬½¶›2 ‘–ˆZÍF¶zw÷ Æ·óí©òöàa²ñHKDB6<´5À‚I:êw⃋k9˜¼üºNÌyEk2EN µyë80ëO:ê\AÙØÖÂŽjíædc'o%‹Æ>´f9i´Ã)ùbn„ xw ß=fÜá–ÖG#_ ä­-¹A­:“}]/Å'—Z#CH=4 ã£k-¾A$@ ,#™Û,k~(]€Îbà~VÖB Œ‚VÙ-%¯ .n€ÐèAÝPYâü­œ^&PÈÙ­Yޏ|Qö`qêZßh:$V3òG”«FLÀˆSíëXÈWä‘“4ì°`áôyÐt[ž—+‘Ú2½•Í7d-5ŸÚn´ÖüÛû(8XüærÝj9Xf{&c¥¯äŸôNÅÎêuÓMÑæßRw(~±Êñ^9r¶a©BãQ@Z ¶Æ’H'—ès×¹C—É Ä›Oýp2#ÛôæÖù»NïÝuA>„ŸÓ|ì‡#qÂÀy ÛË ,'NéÜ<5¤‰3¶%³%ãW±ºmsòâß]õ×r Ÿ.SŽÆf¥ËÙ¶û@ÛUä 숔ñÚZ\5×Á²ñAåø¹Ó᥯Ĵ£M]¦´Ñ“®Ñ:nÿ†NSê%vSµ›Ã0^’³¬E5«¶¡×i+Áܤƒ­¹ÛÞ»€A ì¦GŒâj_4Òãj¶zÖžhÚNœ@Ñ ƒ×^(:ù[¶ µÃ¬Šw0Z¶8þ±½“ŽÖ6ƒ~1»k·-R˜Ãb>NGŽº%í9W+e©ñ?Ìý×E‘ŠSaÎ %…€Çñ~&÷¯RÉKJ·TÈåælXùÚÈ®½¡Òµ¯` tqètñA[nô¹é±1_Ëmcß$oÈF$Rs:{ˆðA5|Ýì< 6NèoO;›yÌÖÚ ý¨ÿÌCGþdìõ¸)â¤|÷¥¤æ±²ÂÞi9‰èÖ“Ý܃ÍQ¿hØÎc_>FjÍÆ™ãÃei!í tiÐïúPFÙoÃÃÜ='m~P ß)“ÐéXþVò— É®nàƒ¡=é­?ŠÇæ&|7™$²dÊe‘ŒÐ:Ð$ ׂ ˜K6ëçr8K6ä¸ÊñE<3M®Ð5û® è·¡úPG“–îKŠ#ÂÁ~z5ã§åR¾¾„’þV´8ƒ 5³Ñ½ Ò^¤Ì•€ñëG”Ê[¦ï®µ¿ é’fr8<Äëoºüc¢òYl\;FôJ rþ°áü–%ÏËZ¿ûµˆìræ’ÍË¢;ºôA^2™ªy<´Y{4ÝZy£«A½ôøà‚]²úŽô]Ë_»[”³>B–&ÅË,˜æ‚è¦=vþ„òk»CÖƒ|ÆfakŒ©w!=yêºÄ–±ñ‰'™£AºÐÐ{$0AÖáKY+4l³#¡ØØs –Ü]œ’Å Z\<ãd}H9ôiI'é)'ë A]ý˜{y\ Ä#_$ÌõAƧF§äs²ö`};¥†Ã{"+‰mÛ;ñéÑS3šµc;9ÉÃ]´›bOÕóÊç¸èttÐõ”žµ’µ…k²±LË ‘ñóMfùÅyo#H;(1_öCÖ2‚TIÜPnß=H17ì$þù ÔwAʳ„t¹Ø²õïKVQ†f5sf`w0c§yêRŸ„Y(³^ºøëosç¥g+‹ŽÜˆæhwˆÄ ¿& ¤¹8î;|±ÓuNÃC‘ÌqƒìÒ ÑHÛŽ§Z,…¨æÇÈçS´9L´ôäê4æë§_ yI?Kw gËY´l9®39Œifˆ:k@Öºª –ñQÛÉоé^×Ñ24kNço)Ú -áÉå¹Zl†fÕØªMÛC ‘ÆÐ¸´íl èeqUòø÷Ò±ÎÖ8‡±ÚsÚà|à€‚›x~KëTrÙK(¬´7úÆ23A |mèïè1O‡ŸF ×ò–r2Õimq3XÑƉÓ@ۈ鲃_è½èýì7”ÍÙ]|¯tšÍçw1ׇDìaâ³wiÒ½®Ç9Îc@~ÙË×êó óXœŒëÖ·)r”V2s²hãku#zw ´HØA虫F…¸\èŠέ@^Wr÷øôå*ÖᩨKÉC5nµÐÉädnkvvZ×ÌO†üPY8G3ˆ–¯zX Ìc,@×2`Íòõ#c¿Áj¼55 Dt36ëQ™6²2Öìì´8Ž`Ò|7⃣—ÆA™ÅXÇXsÛíÑsœÒ Q ¡S‡V¶RæRÍÛU£|M25o+µÓ•£[é½÷” ;.2ËE,½¨¨6S £ÈÂÁ²IhqÁ»;ÖÐ+ðä´n:J{Uª>s;鵌s ‰Û€$s4Þñ(1' K»cs6ññÚ“µž™Ú^{ÜÞ`KIñÒ 2x;™Ks–«Õž>ÎXnØÖŽœFÆüPkk‡ìIkSÌÙ¥Q°;GÀhè\ H:”iÃŽ£^•vòÃ^6Ç'zhuAÇ­ÂC·‹–äÓã¬VsZ;ç×½‚v7Ü‚Z|<ø²Þ¿”³‘’«\Úâf±¢=|P9®›(.e±Pf(š–öf½’FtøÞÓ¶¹§Î 1¸ëÔä{îf,_æ5²Gß§Me| UÁKnHe’_)wA&µq n¼6ãíA>›#"¥š· IÎÚaŒphÞËCÈæ ú6‚õ,\Xëy­•î7eH˜CCzkè8XÈ+fxÚ|å6ÈjW®ØÄŽikeœì4ý–|]ý(; Â:. —-^ô°‹‚Åpƹ’òvFÇCáæAájã…yL݉vºÿ/ŸÕÞ‚Ó°±;->DÌþÒzb©f‡(h$ì}=PWw ×wSÂyD½•C lšÎìÜ7á×H<í«-A”±s1w gœE5 çíãhøk¹‰Ùè=H=FÕÏèåkY‘ÙYóØÛtF·ÔÜu¢B?Öƒ!Åy,ýfÈ*=­Š»žÂÐ÷;G´º<¬ñÑAv× ™f·ä™[t«Þq}ªñ÷§’6ÒG~Kw†¢›Èµ.6ls P>µÀF@¥®Ð{@8B¸Æå©:õ™Xƒ<¯å.å‘Ó]uÝÜwãggXù@õ‹¼>ùòîÉÓÉÙ¡4±6ÄMc„­i$| tFÏP‚:Ü5&; fZÕA¯?‘’y\4@ßz 7„ªœdÕfµbIæ³ån·°ÙÃZ{t44Ö´‚x°¶Nåkù›7E¨L;tQ°F ^¾>dèÕmé±Åͯbwy lûPÉ`M̃24ïϸØû#,MkÃÙ½ò¹®ãà‚¸ngÝ‚ô9›p[e'š`ÈÜffùº‚4üB CÂU+ãëQŽÌýkÞZ×8‡8»˜»DøŽ½ýè/œTg<̹•ý£+â=]soϾˆ5ÌaãËÅíä­b¬½­{kš7hŽãЂ ipÌUîÛ¹jä÷¦»\A?læ‚{ƒ@ÐÑÖ‡æ‚ü&#}HìemÚ¥Eí}j²†r°·äó8 »—Ãh:µhË^ýË/½<̲æ–@ýrA¡¢ëï(4Íb£Íc_FY_æ8¹šØåppïúB 2Øvää¯b;2Ó¹UÎ0؈Z4àA|ÇÌ‚¼3Y´2íÏ5É2`‹sË çŽ^P€;€A(ñf…‹™›—Ž“š¸‘¬h”·G”u:=ýýt³8¨óX©±ÓJøÙ7.ÞÀ64à|}H±QÚÌRÉ:W¶Jm‘­`ÖÎ;õi œ)Vë2aö&iÈÍåÍÖá{ -éþz C„f:ùÎä2–-OwÇ$’1­ha ôkGMkÿh)ðÍ8mq[?$m[Õçio7@dx¨p½œÖ™š‘Âéä¯$2xfEѽ§¡Ñè}E:ü0Øí[·c!bÍ›µ|šY/]€45¾ä ¸)S©Íڨʰ6‘Fþp;œy‡CêA¯ôR¬XÚ5jY±Zl{œúöšCžíól¢¾£H-bpÍÆÉfÌ–e¹rÙk€ ƒFšÐæA¦Så×!¿VôÔ.ÂÇF&„5ÜÌ'e®ko¯Ðƒ,Áîö:í‹ÓØž„r3íhíyõ²t:wx ÃøvœÒå`ºhòÍcfÚ¡­åèG_§Ö‚¼6øîÖ³{+k")oÉY3XÑ#\Ç”gk¦Êìð§k%¶VÊÛ§RóËìÖˆ0µäôqkˆÛ9¼t‚ż%—ˆâÇåìcë²aQ½¡£¦Ç0$tAœ)Y•1ñP³=ñ¬1×±œîSÞ× 8Ô écªX§XÇfü·¥..2ÊÖ´ú€hdÂ;õÿëz×¥®çÆÈìB×6f´’Ýìl§¨Axf»xnÞ X—²·ÛsI¡ÌÞÑÅÇ^6ƒ7¸xX±^å;óѹ=€ž&µÜñú.k¨Øó èQ­-Z­Š{rÛlºiCC³æhPb¿ì‡¬þe¨"“¸ Ý¿ zboØIü'òA¨î2€€€ƒ Ù@A”@A”Øó Ê DŒtŽŒ=¥í×3Aê7ݰƒdA”‰ç¹í/n‹šQ¾í„ Õò2&óHö±»n:=È6@ßÒƒ u'»Ä ÃÉǵÃaÍ;zÐl€€ƒ2€€ƒWÈÈÀ/{X lžàƒ(2€ƒ2ƒt ¹¬pš2tÂ4ãæ~âƒi$dL/‘íc{œtÖƒd@ðÚ 2FJÀøÞ×±ÃaÍ;zÐlƒI%Ž%‘±±½î{€ë(2ǶF±ÁípØsNÁúÐlƒ2€ƒ2ƒ¸„A„ Ží Ê \æ±¥Ï!­d¸èÅ4SÆ$†VHÃÜæ88¬ ÝPaPjÉ(&7µà ißQÞl€€€€ƒÿd=gó(%@@@@@@@@@@@@@@@@@@@@@@@@@@@@AÅíùÔƒ~ÂOá?’ GpA”~Þk(î!±†ÆÒ«#â¯ýµ‰\Ö€âFˆôéõ æ·Œ2îÂ;7ú¦¼të<ÇeŽœö„µÜ¯,ÐÖîßS¢ƒµ˜½”ªXê-ƈ‹9‹îØt|Îô@Íã´ÛÅNv›uPÊHÜ^K«o w˜´;¡>í\žhÞᙲ6q±ÉI÷"mV>G4ÊÎÑ HuÝר`7Þ‚õ¬ÎZN ·‡ÆR¬÷W†9ŒÖ%s[ñ·ÓMïc§‡zkxÃ*ü+s§^:¼GaŽœ™vÈç3CZ»}N¼wáÊ>^'·‰ìš^´S 6vâ÷8k_ùPrfâË`ß‘mI#r†ˆˆHG0r¿?ú ÉÎq3?©¤ÆÐ6¥‡Ê"•³¿²kÓ¹¾.ö€Ðë´>ÊÍ–§1µ ³VÃëLÈÜ\Þfžö“×G½9—³rqåŠ,òSN*Ñ<±ÏvÃ\÷mÀk\ý;»´T+çÿVpãmÒųråŸW°l®øÄÊæ—sFüÃh,ŒçŒÃ°¯ÆÐòÇAåÌ'b#ß)æø¼ÜÛÐ{Ðe¼\è¸v[öª1—!¶iºa˜;—£Ïs|v{‚ ðÜC5¬Ä˜‹Â‘œAåÉJs$onôAßPAöí\G{1_;†­5¹,É -•îh{ƒ Ó´Åñõ ¥™fñÆa˜êµ_;ªU2:yØÛ îƒ@’Iÿ@ƒ«K7w)ÃQäiÕ­—=Ì•–f-Ž2×¼ó²rìq.Jl}±¶›/c#Ù– ‹âs\ÂCšu¾a£Ðø„ü™ qPI‘v®cKL.s¹šZ:×½“Ë¿;›òÓTQ§0k¹dy14F9FºùÎõÕœO“e Ù›Xêñâl¹šå™Æx˜ó¦½Ã\¾#`›A^\¨Äqnu͇Ê-OH«WiÑšB×h}w“à_Èñ Ê·«â¢n=·_·ög1ÂÁ½Ó­¸“¿¨ ¯ý2’\- Õh¶[/y º6Mtîâ6ÌPZ«žÈA~å ­8;x)ùdFœŽse`$éÀí„ðö[)˜Š+²ÇŽ4¦˜Ó¹ïÞ p#[óùˆAc9—›jU§]¶.Þ”ÅþV ç9Ç©ÐÁ<†o+†Æ×šõÓXžë+ˆªÊâ½\ŽÇqA‹ùìŽ6*umEA¹+²<0yC›lhÙsœFüÃ@u%ø® µ“§Ž(*»!BNÍÍeÀý€Zàýl 6ÆGÏä,ämbæûQÖòˆd­;Ÿ†õÊî›0üI‘§ÁMÌåD3ó€ å‡È÷<´4ѽuè^ÇñKÿYÉC'ä\⫬²ZS™XZߔӾ Žÿ¥9–Ëe8j­ÛúñP¹n³â嘺XÛÚ´´¼k]@ð=6zÞ!ÉK‡ÂÙÈà f5À{˜âGÅæǧ˜l §/rqU|K!k«Ë³ÌvÙ\×9×ÒÖ“ìAÏ9¶ä¥ÄØ››&^X*¿´p-kñÚ}$éÃG¢ âïfî]ÏÇoÉMjó>"÷—GýSH ÑvIñ%Íå1\)„±5 ß«Øk’%=°æ<¡ú×.·á½é¹”ÈØžc`|§•¤è8ø ø óÔx‚øÎ×ÅäcÇ“j9©X2ÜÁ²×‚‡Ðƒ¡žÊËŠ«‚KbÕ†V…²;•Îñqt5àƒ‹>g#-~!©”¥FHñÕI‘±K'õ¼Ìæ¨è5°|Pt1‚ûñc]•â8ø¬Ös^NÛ­9½}¯¨  8Æi©Ót5ê²lŒÓy1±1dm‡]£Ï_<èã9#Àdn:¬2[ÆXd2żÑÉÌ[¢Ç}!Ý7â4‚õ|ÆRí\vR•X™y’>W•Ï,,–¿`uÑïdqž†?] R©$÷ëô§Xœ†mõµŒ€ÑŸ²$³×\À¥ÄOr*ñ=÷d±9ö-Á$•|’Ñ‘¼ììßÓc¿¼yŠ Çº|.&åjíuŒœÑ sˆäw^Ó~?•ßè‚þg)&(ÑD×Ãbã+ÊâH1‡ì¯CëAB(kóÙ3BØëT‰îŽ~cýa]¨×ÑÌWÅeK7ŠšÎ>8mZÅ>~ÐHâXÒöüMwì÷ ããþðwAÿZ»Ãü䇸''¾¿Õ·ýíAg1w)SÒn5±rmÏ»aÑó;ÑõÚlœ_,ج=šµëÃ&UÎhu¹‹b…Í`¸¤@ó ×;â*ÐaÚÖR†{Û¡“<±Ýå£|»å uñAé?®8òlĽ‘ç’[½uÖúéŠáÜÖOÁ˜«ÒЮqQÇop•ݰiw/>µË­žíïH;9>$³^Æ;ÚôãkæuÛ=qpØcï:ë³Ðl ãç23qn±R­Y+]³Ì!´âA1ÿÀ A¯^ tAÙýg’}Ù±xL}\s#læYÈùÜ݈ã ÄùÂ¥âé$ÇâlÒÇ™dÈÎêæIÊclæoSæê‚KùûôG+1Ã'i¯{ÜùÜÊñ±§¿˜ŽbNÀ^txw9úî­‚öFË'0L"“…À¶»ÄAAG)ÄÖbÊÙ¡ŽeêQµó:ížË™Î†3^:ç Ú Ž.ŒQÅåßy„M3ñ«?Û×`ùõçAüO~ \™Æ7wn²ð9å¯1¼Ç8‘ ã uà 5sÙ•~+1R´rËYóÀúÒ9Ì{[ÑÍ<ÀFǵ6‡>—p÷êüTüHº(ë‰N£<®#ãºØë¿  ëÛµœ¯F»Œx–NwÛ¾klM;èÓgcϤKÜK”½Ã´.ÐŽ¼É’eYÁ”–ó yt×Õ®×Sæ(:‚ÉgÒŠýJâÙÇK#çŽWÀÝ´­ƒ½ìôAPqVKõ`Ï»Ão¤®ò7/i­rëÇ—{Ò –óY7ñ Øle:²9•™c·žW5€8‘¢$÷ kéÚ ¼?–“1Œò‰«ŠóÇ,Mw0c‹Nˆè‚ÝúðÛ¡<°>7°ó4’7ãàƒç²C+?E¸¦×Š2ä=¬ŽÜ…Ò4uïØ:Ñú{†Eg‚U£M–£‰Ïmx6Ø‹ú€õõ ¤x“·§„š”-–L¼ c‰›yyžò€G­Wñ6RZ¶òÔ±ÕåÅÓ|<ó8M+c:{š5ÊC O] éA›³ÐP†6º¼øñq²ììíÀ¯QA&(ü”™&>&Æ)]}fò’y€ ;>nômæ2RçeÅâ*V•Õ"d¶d³+šÐ]¾V7@õ ³Ñ‡³/Æð™™•9í[ÊM5Ëõ¹!è]æ$Ÿ0AÝ¡—¼ÜÀÄå«×Žya3Á%g¹Ì‘ éÃãAéAÛ@@@AŠÿ²³ù”  ŠNâƒvüêA‰¿a'ðŸÉ£¸ ÊDËñuÌ£¹<žj‘BÝ;ãs5Î'§Öƒ’xo!ý¿†þ§Ê¬I3™ýgÄÓ¤.uæAµìAÜE5æãèäbž´pÆm¿þ·|Ä4´ìƒÓG¢ +pÖHpÖ/‡çtB« …÷±ýd`q!éÜî›>alpþYü;.:)Y¨RWÉ¢akù]Ó½ <ú:õq¶!⼎MüžOf¼ǧ|m°»{XAÈ<7<>Pù\yâhÌ^:ëÌ‚åÚ9jœM&[Z½¶YªÈ$ŽYû#k‰ÞŽÇÆê;ÐP†2ƒ‡™JW×}‘—渆–v¼ç];õàƒ±&2ø¾,¨äòvQ|ã|nbðáÓͤÀclc“69?éY lGÊíüGk[óˆ!–†F¿ ¥h"žµšÑ×›š^GEÊòy€Ñæèîîˆ9ÍᬈÂW¦{Ö,ɺïÓ³í\ï7~r¹ÆØ<`2¿ÉÅ_å|n~Ó›»Í¤™¸VÝœ5ú¯0 ß•}úÁÿ7|`Z×w°GÒƒ¡ƒ§b2>nÆbÇ&ƒê½®{Îþ†7MAž ¡~{˜¼Ž>(§–„Ïy†Y;0ö¹…§NÑÑýÈ7ÇcmÁÄ™”í±Ú‚»XþbÀy·ÓéïñAÄöHh肤3“Ÿ„]Ãö¡­¨ö¾­ŽÓ´lÎl…㙚è<ëÞP_Æâæ”ØŠßc1‘É£2Õ{\÷t:Ó†·Þ|È9²á8’| <á¤#¥$?ô‘9&vFðGÅåø§C®Éê>”ºõV^£b¤ƒlž7FïS†y*ü-—‹†d|4,Çb'ö‡“q±€wr´øh ¼Þ±>­ŒŒ\ÂIÉv·ñ ŒäUÆäéå²ímxd§’ÌÙ»m:7ÃyKuרïߊ òpí÷pF3^STÖí>?ÅøÚ:ó¬ý<ê5gì'±˜É6F‰õ{>´wßnoyؼ~2 L’7ÅYüÎys4HhØß‡Rv‚×óZ¡[ l’ÝéÚ"d‡•§“ã»nïo@z޽z çA ƒ[†?UU§rÕÎÙ"´éC˾'Çs‡0;×~ú ·›áüœV4ã]/Ô€×sžý7‘ñò?G^=H7ËpÇÅÄËB¥[ªã0ж€ –2ïÑÓh è ÒæíþµJ*G:ÆŸ±¢ÇËÓ¯RM›ÃÝŸˆkäb¡S'+:^Ô¼¢'—oœm¤އÅ(ø_' ŠMnŸ–ÆÖ’ÈÞœá½ß§±Q¸Û×8†¦RÝx¡ŒP– ¢l¼å®sÁÐ:è9¨3‡‡Ç êùÔF÷lyÌ!Û×gËòõÓ{׊Õ|\ðñe¬–˜+KJ(Y§|`æ¹Äôóh„1ô3XŒdñÔ†¬¶&ÉÉ1HCD/~ÉØ+^;—Lâ”ÞMf˜°†1Ïäý'G^ÄZÊ»‚ha¥ŠX©<‘73^ÆHNõÐë}{RÃXî^*É9¯§Œ‘ñT¹®”‰$ù{¾´œ.vž2îŒ5NÓåì­É1k¡d„—Í|b9Ž´G‚ 1fZ…ìExmÇ^‡¾¦ì( µÁÚ>n¡¾Æ_Ƴ"ì„Ënëì %ºs[Ó¯^š#êߊlQËPâ+9<]zöã¿lš9fìŒofÀp:;=GDkp¾N.ŠKÈÕÉ:ô.ÙìÞyÉÑñ‡ã¤ T2Wx‚<ÆR¼jî‚"›µ$¸‚çhy€}+þÈzÏæPJ€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©&ý„ŸÂ$Žàƒ(0ƒ(*d1”²ˆoWdìk¹šÞ×yÁAõ ׇÇâƒü†£!2kÃeÎ×vÜvJ ¨#šìBøeo4r4µÃzØ(!Çãiâ«y5ì‚.bâÖø“ÞI=IúJ H+Û£Zóbm˜„‚[+7¿Šö÷Eb¼VëÉ^vsÅ#K^Ý‘±ë#£©ŒªÚ´«²ZI `ñ=äùÏÒ‚Ê WýõŸÌ •Rw·äR Mû ?„þH5ÁPr/q%9ãÜËSÛlmA^Hç4ï¨×€×_XA³x“ì ÌyAIåÙaççß/'/6úi*qK†xÛ È¬Wµui«¹’–x·ûC犸cˆOcÅ—TžNß #˜Êãòއ_1A%þ$§Bãêv7-M’¶­gKÙ4÷sk»}zw ]âJ4®6“™j[O„LÈa®ç½Í']ók¯™ìâ»› ®ó5ç§_: íñ= —§¡ÙÛ±jµÒC^»¤pk†ÃºxÍC~¶R„7©ÉÚA3y˜íkWAO‰3?¨pv2ÌèÛñZÖ’7à]®áç(99N#t6ð6ur­yìJÉ¡’5òj3Ê9u³ñµ¤|v~ŽI¶¹{jï§£0FÁ øh¿B±üSÈÛŠ´BÌFÃK«¾x,Ô–ßÓ¯©ì–J®&‹î\“’&ht’IÐòOpA­Ÿ~CŒêT„Ú‚/"•òÖ±ÜÜÍåqèÞŠ ­âÌ[®6gìß7`Û]ƒ»I½r‰;·¾žm åœ¥Œ‡KC·ÊA^¤q–²yXç’âL„ƒñHh¸¨:°qM ¯CQÐÝ€ØqdOUѲWºi>=]¯”­j[±Äç“FNÎm·Zw(wO?BqìæÝrÿ Ø¡<©’G9¤rö’´àø—õÅÛµÍPŠó¾6½ð87M ùD÷;dôóiü¦b®$B'É,î-†#2I!gMaâ‚°âŒaƳ!ÚHØ â¼…Ñ`y:Ô€õnŽ'΂IóôkÉc!ý[keÍŒ–°½oÅÚë¤Õâ¬m»uë³ÊX-t¯4°9‘Nu½5Ç¡:ö Ò<åjpåíÛ¿$ÐS·Ù¿pòöñF¾PÛæúO™íâ*¯¥-ÆV¾è£1„Ty3o¸°w¸}=È+KÅ•?TdnC®Úƒ­­$dŒ:Ûv<ó÷ilÏ›¸ìU£%œ{¬ZŠ'5õOõÅÍ'”o¹§Òú[½Äô([–³™jw×huƒZ»¤lõäwtë®ý Þïã(ÇNY&sÙx&0°ÉÚôØ^$žtb³u2æxàlñMYÁ³Cb#ŒØØØ>x âñ^ZÄ9|f.20ÇaÏtÏ¥ sÜ݆´èøõ:î/ÙâJÉ]MÞ[iõci°ø«º^Èk¾B#ª îñ2„U&–rèî‚kº&öšÐ×y#¸x ½NȹR;¡íû9ã,{}`÷ ¢x‹úƒõç<žE­óro•Ëò}h5¿Ä”è\uNÆå©£`’VÕ¬é{&è»]ÛÑúz ß!Äñî‚72ÌóÎÃ# ¯¤“w¸Ü:ø Ž^(ÄE«’u¯ú-©;8ä ?+DèŽð~)¾ˆ/P¼Ü…VXe{9,DcC®­=È8¸|؃•½”¶ãl”ñ5Î!¡À5 §¿@w èc¸‚žFËêˆìÖ°Æv¨LN,ô€=ãòALqž5ò×lP_‘–žc¯+*8²b;ùOˆèO¨ Ý™Êô£ÌÛ·~Y`£g’@`åì+~(×Êw¿§èA5>&¡w Ê-m˜¤™¥ð:jîc'hï,'¿§_R y<¥lMVز_§½±±‘°½ò<÷5­I(+ÇÄÎ6ÆBÃ,Ó†±ÔžU£pîÖïÞÆ´ƒZ\IBëçÂz²W‹¶’;pº'ý0xèƒZ„$ÎÖ·n€­zXšëòVs¹;nÏpo˜yqïeò1`x’v\•²ÕÊap=XÍÇñGÑÔûPzì‹Ý:Ô‘¸µí‰å¤xÓ¤w‡xº¬ØœK.¾Ñ–ÌLŒÛ’¤”Ž­çÖ·½Z–G‰ñøËRW•¶e| œ×Ò{‹Èîñ> ƒ« ±Ø†9¡{_cšvq9£ˆhÙ®y<Ž~wvg˜r8´ôïï·¸’•"°†Ý©û!3£«]Ò–0÷k»z(7½Ÿ§EµƒÙfi­7ž*ðÀçÊæŽòZ:€7×h5f¹Æ¸·û2¸ßõ4¼{zºõ™CZ¬•áÄÞàCF€ÿD=t÷²•±rY›-c"Ûð¾åw@Ym?¯NP:tÑùÊeÅ´ld¸[#R«9æ’ÈÏH‚¾½iæN<ÖS‡M*VùkÜæÏ¬öOfáÊI®üÛ;ûb;§kˆèÍRÓì]°ùª¶8á3]NC¨ë²4ƒ\UYÄœ_^AØR”I¸ÈìÏdß›ëAo5zÔ|FjZ³‘«CÉšêþCqžRHsKƒN´5¡Ó½*µ{4ø+-жieDÖÙ—=Œ?dÔ÷ø êÉò¼KrJÂFÅo#ŠgFæ¹î×xèzï]è9²X~G…qü5 +Qä£5╎Íl›š\òýrëâôÑ;ØAÞÅÃ#x×?3¢{Y#*ò¼·AÚiÞŽTá{ìÄà1tíÃ;g¹rx¢`ŒìÑîÙ¸hoh:eZ{œ#“‚¼N–W@yXѲítŸ¢}«Qå²Ü1n¤sI veçs {ybGP@×^›AKo!–âzõØæºÖ.â{ s´þ›ö{PWÂWÆÜ¿‹löóϽL‡¶µ¦;³á¤ž@Ýwת×Ã3±ô­C“¶ølËmæs˜Ó×CÄï_B Ûn[Ž*\©Ÿ%n>hü¦H..iÐæôÿßr&/M˜úØ<ͼôvb”5Õ#kŒ.!ûkÚCåîvùº ô‘2Ô|_ž± s;f×¼sã×H<ôSZÉÍ€žIòÖí²ôR]dИá®í@ wô'§Rƒ«ã‡Ëñ iêZ|×§TÀ牉­Ðpc®ÈÒ øº¶#§ÀíuyZ`‚Â;?êˆøÞn¾t>µ3œlñÌËä$±âw+ã-nœ­x ¯ÅuÌö7)$×á§2Ã,Ô/…Î- <§D‚¶n¾1ßÉÉ–œÄz7ÖG47™Ûh!€u'^+²¬¸^â ¦=óAVY›oêÓÓñ‰ô#}À ‘×?]UáüeJ¶Yb¼õæ±ÏšØzžb4wÜ4Nö‚•Kp÷F+JçM“cDd—êúŽGC܃¿ÅW.R«Oɤš½i,†[ž¼]¤Å£Ô uoGH84iÏiÜUØ6üÌ·B6Ö–ãH|ßAÓ`x÷ ~h,:Ër8^¢Î«¬ÉšøÓldìw?r ëßgd3Pß­iî·mÖk:*ïN×1£”ƒ¢#GH+cñVèŽ ¯fv•Œæm ˆ‰Œ ·¤ŒlR7³RÞØßZ°kËHkˆçÞqÖÐ3Hþ+áéÜÈÝgÁ¤†î>›>(V¾ÎÈæ¢¿VÓÝnÙ³YÑW|‚pæ4r‚Ð@ hél~&Ýðe[P8¾³¦2€ „DÆâ=ÃD€ƒÛ ù¼–¤ô}7ù ×d¡qd‘ ÏÓZ&æçæÖˆ×vT~$žµKóÚÇØÈTÍ6†+>HíhmpÑkºo` ƒ+ðq9LœÙ*0ÙÇÇåÇ‚îÊPIs¦¸èï¡×x(6v:¬xìq¬»<Í Üë1»Ÿ¨y. ¡¿l:i„4n; nfUšCSˆ]qІickÁ< ÷ôê<úAÑbÏñM;Õ£˜Q£Vqby¢tM&MÁÌ:“àpðé†b™’¹n:Ô§>Gé¸FK›4ÀiãG¡èÐZÊU°üF+JçMy¦6ˆÉ/±õ]{ræfÄÜ8øâ{™Óó¹­$4µÔø ³Ä­Æ¿Æå‘jÒÉk1åð¼ukÁ`$kÏ܃ÍZfc5Â×cç·n—b’¬Æ.Æ{10µÎÓHuÑ×R;[ÅÅ…³z{âÖk#$4äd‚ä/Ќ뙚,nÜuÜ6ƒLE¦Ã}Ô±ÒÞ½ƒ$3Ef»÷_CⱎpÛ½t‚N²aÌÃG bíœ?bó$và{EB5ÈÖ½À¾£—®´‚Ž>µš|=Ã9*ÎèñÖfuˆ›‹Ø×´?—[:Øðî(.½ÏÌd³ZN*7ê¬|‘9†i6ç|V »»¼” •¦þ‡ðÌM‚Nx¬Q/`aÛ4ö<5â‚<µ˜éeìMÃï¾Ì¤–Ù¨šÏ0Y;¸’9[ñ¶Aéó´Sz‡Œð=>gk¡ö錄_~ ñ\”ìyd7"@cwhbd}“šßÔ¹çýP]ƒ=œ ¢{¤ŽÓ¥°CÅsã{œO›«¼PWÈU²îâˆÛ^béráÌhŒíãš. xŽè=¦Lc-´I†@ßÅ(<,s¾÷bxr:6£ÈÿуØúîkbk×—kZÐóïª nÕŽ‡fNîb¤¥l°¾“\èæi`iiåc´àF´uÓH=Ž­z8zuª‰DBÖÆ&ùaºèô ðÓY’¿å8tѸü‹_`vM®ò ]!pxv´G)óïÁSˆ%§^À±^ÆBŽa•Z#|Ÿ#'ÛXáÊZî¾¢7Þ‚¾R+ƒ/Ìe_‘£ØÖÃ;ñû& ¶Zà)ß›¼ KB½\N?%nBÝZùo-³å¸ÌðAkž@$C»¼è/Q¸ÛüprëYòFâÝf|`‰6@æ ô8Ì„y\t7¡Šh£˜s5“3‘㮺Ðb¿ì‡¬þe¨"“¸ Ý¿ zboØIü'òA¨î2€€€€³çAϦŽ!v`LîwU»=tÐw6öƒ €€€ƒ’ü,–3qämÞ’xë:­nÍ­lN#EÄ÷¸ëzßvÐuPgëA„úÐA%H&µ ©"šnÉ罜ÃG^°‚t­L­”ÄÛǺCmBè‹Àß/0Öô‚jÐù5Hk‡£k>:A*r_:wr/T‹!FzsìÅb7Fð+†ŽqG Úš)ßÍMjŒalF_ÈAh{‡R»[ÒB€€€õ  }hZr WýõŸÌ •Rw·äR Mû ?„þH5ÁPb¿ì‡¬þe¨"“¸ Ý¿ zboØIü'òA¨î2€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒÿd=gó(%@@@@@@@@@@@@@@@@@@@@@@@@@@@@AÅíùÔƒ~ÂOá?’ GpA”©¯˜ Õò26ó=í`îÛŽ‚ ‚Øêƒ(1_öCÖ2‚TIÜPnß=H17ì$þù ÔwAãòóÉÜ[JIdý_u¥“7d†;]à}@ûPC„¾êõ2¤c‚m:<¶F9jZ‹Ëd2ÆçÂC@ÇDø¨:|{³pÙlQ>Gvì<¬iqñð9xºö¸O53f±G#L’veÎÿN‡>Ãô ©w7ˆï¿+W úÖ^d†j…Þ'z:õ îQÃR»Â©Ó«f¼vKœÆYù|ãZw^íè æp4Sdr’dí šuÙV=ùÀÑÿAþ¨(å;'ä<£G'K(étøÛ»¯Síú}.~ɦ¹ùG6»·®¨7@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AŠÿ²³ù”  ŠNâƒvüêA‰¿a'ðŸÉ£¸ ÊÖ€€€€€õ  }eë@AÏÌÓ¿z«bÇßò)9¾;ù9¶Ýk_BÊp¼¿D¹ò;½î=åCe+þÈzÏæPJ€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©&ý„ŸÂ$Žàƒ(<õ¾9Àѹ5IìLÙaycÀÄ;ú­ãBóˆiv˜Ê„>ùÌ߇¹OO©²yV>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69V>øsç3~þäéõ69Vd~xuÎ fÛˆþŽïrŽŸSc•g¥X2e+þÈzÏæPJ€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©&ý„ŸÂ$Žà€ƒâœA ¶8¿# 1ºId¸ö±.;î ÕÓ˜8™ÙÛ^Õ… è\«iµg«,V­Döã¾îŸJ¼Z&3˜˜˜Ì#±^j“º >)XtæEm=áÒf†¦â Ž õSØa‡¶ò1æowÅhðk>=H¤_*ñ_l¸¹X{ürøüw‘K ¡»Bþqç;õ…­fõÔá´åx›E±2îÿD±$ƒb”ðîk϶+^|ZÝ÷,yטÌOí…9–žïY„Wµ4áâ9Ðá㣭®èœÆ[Çxu8?þÖãÎÿ‘Yë{¹WSðËÖñDgƒÈÏŒ{e}ë®méØ~4cÁŸG~¾¿¥si}ûÄ[Ò;2§Þ´D«âxo j||d»ñætö».ɇÑê@V¾­¦Ó8Âmy̪žĹÜAŒ®ÖÍf¬bÅ9ƒöK4 oC£®ï­[™o»iòõOö—;Š1Ôq±Tဠ®®&µ&ÎÉ=ÃÛµ}+ZÓ3>KRfÓ3èó‹v‚q½[‡*á)å‹ÉW‘ÎD†Ï²N‰={¾É©^ Íæ3 m¶f3 ØÜD0VÍåø‚»®ÏF^GBd#žCÞIýáM­35­;d›Lâ*Ó GžË\¸qæ­U{WÕd¥Üîëãß®‡ýêZô¬Fs3ê›M«Ë,~/!ÃUó¸ú>@E¡°6Bæ¸Þ ú”ÖÖ®¤ÒÓ’³h· ˹&†Ç»‡Æ)ÁÖ+öœLïêλš>¢}kÔÔàãÊœWáâË›‚ẬÁM‘šœ9 Rè#Žyû(ÚtNüçEi©«g£2·‹Á ÿ`Y/0­ä–n_”á·i£é*¶ÔåêZQ6á´¹ V3=—É[ñ:qî*¡g1ë®wÓ»ªÓRöÓ¬F{Êö™¬D$Êà±­8‹ ­^¬“Ûl6jAcµn‰ïŽƒýTSRßz?DVÓÞÔœ;>s'‹æI &VÙí‰-:@x°©Ç©ÃʼVÄ[/ ЧúÃ+V™qhžV°¸xz®ËÏ f[Ìâ&_CÆŒ=\ÞcŽÇÒU¦ö™D…ݨÐÞÁî;×_Zâ·Ö¶´ùËžÜS3/'cMŸ£ú&¹l¹—ge£›¦»¼è­§›5ôm™ã˜w&áÌSxöŽ1´Ú*ËP½ñór»®÷¿²KòfÙîÏŠÜ.X­½Ÿ‚†#%–T26f¾~S89‹¿³£ù­3jÖfÖó[3ÌÊßáñµ°U2ÃmžÊx*ÙçdëÓ›®EM;Úm5Ê+i™Â~6u•ÇãÛŽh“’ N$=#æ#“_óïQ¡ŠÍ²<âg-çáÌlÜm’dñ¼Ó£U³º.Ñļ뻘øKF”cÎdãžròT1Y‹;Ž£äe‘°—5Àžþ¾=Gú­+kWS‚Ó•¢má™wŸ„á¨ø©¸ÕÆÌ§mÛ;ú¾‡£GÔNþ•‡§/*q_‡‹/žYˆAjhAØŽG3gÇDù.èœÆ]Ý‘ë¥ÿåT?þCÞ¹£ßþÌ¿ö9ü/u·Ù؈òDÁ·M?eGÎOO}[cœ-yÇ«µ“Ààêñ·cjÓ½|Ѷnf5ÃZþbOzʺ—š[æ‹Zk(ó¸Ú˜ü­ÉñÅYöCD°Ùqe†€}Äú”éÚf³÷½6+31=Ð~ìUvyõ£¢ØìGÊ_`<“ åè9{†“ÃDðg)Ò‰áTÅc)ØàÌÍù`³]ÌI³¶ï[è¯kLjV±ê›LÅ¢gÃáá¥ÃY`‚;»ò¹ˆæ5¾½UX½æoDq[ï%â|0ÉZ ˜XkCbË#†ü3—µÍ=4áà|~¥W™ï6ý‘Kz̺¶8S ÉfÇ:”DÈ~%ç\©“^,ßrÊ5¯ÚÙý°¯¼Üj´pø~¡‘ÈcÝvÅùˆöMõoÒ5õ­fo{Ík8ÂÙ´Û¯úDëÅ’‘äU¼?»[Kð¼ºèh éðæ;õ¯R§­±Ò‡?øGSù,õ-ÃI•o8¬½¿Ô½“áü¬–ê>/!·ÚÖ.*-uþ¥riZµ¼c×Í&"Ñú¸¯«ƒáì.{ØŸÖ62 2=Δ·‘½: zÂ×7Ô¼ÄN0¾mkN'YÄØÆWà ¶dĉ™åml±ºb;C±Êwá­÷}J­âÓ­”LOwP~*¼üŠ–6Ok b.çqáݽxbÒ-1«hÚÌÅåØýGóggáxñÏdÑAÎÛ½©./å¨î×U¤R53çèÏŠÜ*úâõÊÚsŸW™] Ãÿñ·ó 'È—èÞ}kÆp1_öCÖ2‚TIÜPnß=H17ì$þù ÔwÉÝ8Þ;·y±‰ {Î0sh÷mz•¯”GèìˆÍ0Í+±â;YˆëµÂÑq’¹B Þ·¯ô%´bÔŠlM"k„ðq‹ãÌd®KŽX2L š¹Žá¯•¯_µDèæ±òG/´FPÜâ“f uxñÐׇcµ‰‘¼ë”w7¯æ•ÒÄÌÌù‘LgºX¸ÆH¸ŽægÈX]nÈÅÚ7 ë½uîIÑû‘Lù¿»…g\Ηäà¶K·mÏÔwtÖ¾:¼Óïñ­Ã÷¸ñe[±Bì¶µûP01³ºW7˜î`;Õ#JkømˆWƒRón;q:gzÁlÑo8¬­{íˆJ`0av·ÓΫzñVaŒÆZÜY$V²®š“'«”%ÒWt„“â»þ¯2Îtbb1=áI§hýÕâh?TÃÊâaÈÁX“|…ŽŒ Žð¦t§ŠmYÆS4þ‚Å®$vb­zôñ𸋠ç‘°·äõê|ûYëLE8'¼«yˆ®'ÍÃâ,ŸëŒõ»­;ïÔÀ:7ýþµ¶x)½#Ú´XAè±|OK^·>¬öêêì—–’| u+ iM¦~÷ig4™õiG‹lAc ëµb»_"îk<–‚|4|<ßR›hÄÄc¶4ŒF= ÜN1ù—]¡Œ‚½wÅØÉS˜–½¾$Ÿ?Ò“¥Å\L÷&™ŒL±‘â!«WJ„XúQL$ì£qqs·ÞIJépÌÚg2E1Þ^§Šx™¸>$”ÅŒ‚[‚³[¢âÀíô#ǪçÒÒã§›*Sнåå1œKä¸é±¹ d©Ë'kÉ#ËK^{È!t[O6â¬âZÍ39ŽÎvJÕ{–ûZ”c¥hhŠ7wx’{ÊÒ±1™ÊÑÞU’ ë×â ©ðô˜š±]4ݬ–Y!?áÐîŠÊtâoÅ*Íbm™nÞ'´pG3]3Ûa¶!²ùItNãÞ:yüTr£ŠÁÊÆ[Š+eëÊë:Âü±†:ØyØ×ˆoE4¦“ÚÝ‘Ǫ+¼IC>Î2'Ø­e ®r Óû=ÛèÆ— æÑ)Šbs×)Äóds•2¬®ÚòUkZ\)'¿^;JéEk5ÜŠb&Ïñ4ùËõíEQYºŒ0iÞÉÞ‡]ëØ§OJ)+H¬aj÷ÒɇOw‡êÍ}Ñò£€'Z—»jµÒµ{E»")1å/6·h íe¸šÆK-S%-­5HØÖiÜÛ-$ï¸yû–TÓŠÖk>ªV‘0¿kL°[uLUz—/3’Í–<’á­HÐòÌæ!§úªEÅ–ëc1tëD!v6S#d'´ÞúùºéZt¢miŸTðDÌÎéŦݬ„Ø™ ÖrZªç—2O9Þºþüê9Y¬Vgɱ•[ŠWN|~B´ÂR+žd#] =â´Rؘµ³”ÅgÖVáã !â;™Ÿ!au¨»3jtÞ€ozëÜ«Éû‘\ù#—Ú!Á­bJ–¢³ Ô¼=‡ékiˆ˜Ä´˜ÌaêÇmò«bÂVŽkp˜ìH$<Ï:Ð ë ú<~¥ÏÈíÅäË—é•g¶ž bmã ¿rv°öŽ#‘Ýý@ïëÕ^ÚY·Nµ39‰Lþ4šN%­œ}(ÌCÙÄ„t#{×NþåXш¤Ó>håýÜ)áxŽL>bÅñY“6È{d‰Î×Gô*÷Óã¬BmLÆäx‘—pc_ :ÌŸµŒFòKF»ŽûÎÉ;Jéâüs9’)‰ÎRå8¦<´ud›nÀcÝ‘!ÛšÓ½kÃj´Òà™Äö"˜õlî3´8–\Ó*ÆÑeŸ¹4Ê9w…çìKÛÙ–bÞ^ÒG?[îÙ'_궈Äa¤#R=7Š¡¥‚n&ÖжS&ä”·©ú4°¶”͸¢pÎi™ÌKj¼WZ»/Õ8XBñi5D®„:lƒ­¤éLâx»Á4žÝÑÜâ–dlÐ’æ&´Ó‰ñvÄ1í:×Ò5®é])¬N'Í1Lg»LŸy]´(Peµeí˜Á!ówŽ§Ã¯ršébfÓ9™"˜™™–¼EÄ0çÜÉÎ2*Öö;YÙ!qx@kÀ&žœÓ¶{§«>"Š–èØ£Ú—í"{Ëz¤)ÔÓ㘘œL¦g)òVÜ—êæÏНØÐ.ãÈö‘ ß£@ªµÒáÎ'ÌŠc=Úä8²â£ÆcqÌÇWdÂm6WHîaÔhžá¾ªk¥‹qZrE;æg)nqU,6/pýY¯º>Cg´pëAܽÛQ])¯h·dE&<¥)òl$x»xÈ.¶¼¥gÊâ;7} wõ*m¥›qDá3LÎr¥ŸÌ¿?”7ä°¹Ìk Z펞*Út௠k^Ú´X@@AÕÀfÎÌöc¬Ù¦’_ËÙïÇ»ªÏRœq­«Ä—Ä÷1’ÏåKÐÏ ‰ñM3µ×ÄwõU¾•mŒvÂ-HŸ%ŠœU qU±ù<<Sÿ‡|--cçQ:SÅ6¬ã(šwÌJG¦{l§/Ó=œ—gîýD`½¹›¶çë¿6µÿ5¯ßã_‡ïq-]âx²9 –nb¡š*Õ»¥:xô·®…V4±>hŠb1‹5Ä-ÉÐ«Ž«E”©Õqs#l…çgé>²¦š|36™Ì•¦'2â­WoüD_ÆßÌ(Ÿ"_ yõ¯À ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜ|_‰)[dÜÊ–×Zy±8ƒ×Ô½];GwvRc†ß »ó+?rïr¿w[1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7À)ÚÑï‹ôÑ8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆìº•çº¥§9…çþIÅ™†<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆìyß™Yû—{“Š»™Ùò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜQ¹˜Üò ¿2³÷.÷'nf7<‚ï̬ý˽ÉÅ]ÌÆìyß™Yû—{“Š»™Ùò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆç]ù•Ÿ¹w¹8«¹˜Üò ¿2³÷.÷'w3žAwæV~åÞäâ®æcsÈ.üÊÏܻܜUÜÌnyß™Yû—{“Š»™Ï »ó+?rïrqWs1¹ä~egî]îN*îf7<‚ï̬ý˽ÉÅ]ÌÆíá£tOò;?-¿ýyÇТm\y™ßxñ>µä8D¯û!ë?™A*¤î(7oȤ›ö üj;‚  lùÐ6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵgÎ}¨>sí@ÙóŸjÏœûP6|çÚ³ç>Ô Ÿ9ö lùϵ‰x„pý(ävÓLâØØ\@è6I+} Õ¶ëkFrò¿ù?˜Ôûo]wqõ¶ØøFÉüƧÛzžŠ»m¶>²1©öÞw:Ûl|#dþcSí½:*îu¶ØøFÊ|ƧÛzŽŠ»m¶^Ãqü÷2PÕ»R(Ù;ÃøžïŠãÐl ¬õ|' fÕŸ&š^/ŠÑ‡©Éä|‚&Þw¼é Zñ|W‰äV/[CG›lzC—ý#·ûˆ½®^oÚZ¿ ;:*o'ôŽßî"ûNO´µ~:*o'ôŽßî"ûNO´µ~:*o'ôŽßî"ûNO´µ~:*o'ôŽßî"ûNO´µ~:*o)é礚Ë"š&4HyC˜ãЭô=¡kÞ+zùìÏWÂEk6¬ù3Ä|AúŠ´Nl]¬Ó8†4»@k©%z:šœ§ƒð½E¦3ˆ‡šþŸäþgWí=sõÙê}•¥ñOðOò3«öžE¶>ÊÒø§ø?§ù?™ÕûON¢Ûei|SüÓüŸÌêý§§Qm²´¾)þéþOæu~ÓÓ¨¶ÇÙZ_ÿÇñl™{¦šìŠRÒö:7®ñÕm¥«Ç8—Œð1¡N:Naé¶|çÚ·yg_9ö Â  ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜezW«dj2ÝID°?|¯èè{þPX@@@@@A›0Ô­%‰Þmæ{€SZͧ¬Ú+”psCØ#aBr"DAJÖkBnÆæNiu¾I§k¯Q(,Ã<6adðJÉbs1ìps\<àŽôݽWUÖ®J"…„<‚@$èw}$ 6õgß’ƒeÌQ¶GÇ£°×ÿB‚Â Ê ¶`u‡×lÑ™£hsã͸‘àf e’(æòB@‘p%„ˆ%@@@@@A„Ãf½Ž~ÂxåìÞXþGr¸wƒ®ãô ‚¾2ÅÇR‡#VK-ï…“4¼yúoh-ømoV©-xç”1öd좎í¯`(,    §o1Œ¡3!¹‘«^Wü–K3Zãõ‚ËäŽ8Ý#ÞÖ±£nsŽ€}ù!š+²hdl±¼s5ì;k‡œ ¯RõkÍ•Õeˆet/ Šö÷©è2ƒ2ƒ2€€€€ƒÄ~’¿aŽÿ2Oö…ß࿜>3ðÃÂ/MæŠÝ”Ÿ»Ù)˜N%®¾„@¤[Ä×xÿüT_ï -owo”´Ò÷•ù¾—ÄÕ¿‰ß_íOÉû¾³ÀþgxïDA ê:¢2Â$A5?øêßç3óZèûÚ|ãû³Õ÷vùKOÒÿnõËù5}‰ôWÙŸöÿ/¹^Ø€†DÎ ÿ´ð•'äÚñÁíé§ç¥.çÌ1_öCÖ2‚TIÜPnß=H17ì$þù ÔwA[!0ƒ<¦ÔUy#'·”m±ý$ll óX,í‰xšqË~´¯b³å:§`Xæ‘ÝÐ4ƒõi|Ï#ýÐwëhñ…Ò½½«¢¹ßÖ?âµ¾.ú©O…322äªÏkË)˜Øì .{\Þn­ hƒ±ÝÕ£@@@AG3=ºØ¹e¤Âù›­i¼Ä ßΖšqY´E¼™êM¢¹¯›rsg…2’Œ›o·± ÈFæ@ÿ˜[V¼:ÕŽ1´çJÓÅ”ñÏ’£o'¶É¡¸îÉу{3ÈH-=ç»]UqKE±_í17¬×3æÀ¹”½^æB­¸áŠ $lPC„üÎïë£ÝÜœ:u˜­£ÏüœW´M¢|¿Ã-Ͻ²ºÄ„ ÒãE¸›®¡Ãå ø÷„äöÇ®ps§9ôÆQC–É0Ö•ìy•ÞC¹Hç'_À­:tâ™,e©n‰óÎ.WSÅÚ±~=É™pö2[¥_B''W÷ó­ÿÉ^®FwqL8æEäõ‹Œ5Ìi­tîéá܃‰œÈY¿Âùáaá®Y°E¦¦ # }=çªÅ_þaå?üu÷È‚~"½ä‘Àß×LÆGHÒKÓ¹­ëÝãРâÖ✋øqå’E=×dÿWA;á,k¶àŽg††úy†JÎ[‡p–lÏnJÄ’GnÒ»bk÷õåï;ú/ÙÊpÎ þNþHdC²a®Øƒ^N»ÛÔ‘ìACÄW_œ«HäV;Q<½ÞBúâ 64KFÚz޽B ¸k)È^krÆs§Ôlf?7+RO×h?”ÈÞÉöY ¸­q¯y›úf›³®G®ǘm¬A渥Çf·Cž*´Án6wÉ »¾¶¿—ÚPræ¹c†¨ãêÍeµ¯æ&’Õë}˜Æt hdm­×â<ÄøL§’s\šœñ¶;b›šd‰Ú.xˆ·4stúAÜá»FåY&Æe£.®6'F|Zà<~‚AÙ@@@Aʬ»RZÒ>V6Vò¹Ñ<±À}w àðuX*75Z¼M޲r1Œopè‚ ½*3e1X\UHbž­–[•ÐÆäÑ4“Ôž€xõ(;|A“8l Ü‹c>¼EÍa= »€õl„k#_1_+ÃG%‘Žãd¾ €€GÙ¿³wFë½½O^ˆ6ân ±Œ}éêqN– O´o@6×¼oDùö5ÑY™‰ªñõïJÖÓšˆ·]ܺä é+IñÖÚ~´Gç3C‹·zéÆÔºÉ&–Ûj‰;vâÞ §«ˆAëñÅîÇÀd¸ËŽ,ß”1¡­“éO2 H8J1”èZ’Jعoab0d²ò4Öú‡~üÚÀFȽö<Š»yÀy •Ígö‡ö†úèù‚ x;þÆáÏÿ²ý¨ ¹o'âiq8û­£:Ìši{Èç¹ä†´ÐIñAËÀY–§ e峃0ÊNeÌæc]Ì7¦“×~é@£›Ë[“/¥“f‚›lU±%NÉÜÄXZ@^ýx è Ÿ'6 *Ó=‡ÑpaÑõ¬µc5ˆýa®—kgô—Gsy›Œe­Gô\l\{ôÝž§^ÁÕc­ÊÓÃù4ÒæjOâþXâk´›ËŠ¢,0;˜Ê[²×Ÿ”îòyߊhRߎÞr{V>å|¡ç—[•oÿ]ãÿñQ¼,µ½Ý¾RÓKÞWæú_wVþ'~A|_µ?'îúÏù¿íÜEã½µ¡ò‹1BNƒÜ+M*qÞ+ºš–á¬Ù輺2k6)gBDoŠ÷yú?øñ=¿G—ÊÕÔûÙþ\¬Ä²H¬@9Y;v[­uõ/7ÆéÒ³§”»<5í15·£š¸i©ÿÇVÿ9¿š×GÞÓçÝž¯»·ÊZ~¿ûo®_É«èCƒÈc_’2>õÁiÓº. í¤çoÑßô êÅŠ1ñ¬¿m¿(­—/ÉåsŽ÷¾»æóx ¯•ÂÛ³—«•ÇÝŠµªñ>ÛCÚ±Ìqôv<è*³„ÅZ§&FWÏ5ß-Ž× Ž]‚»PzyŽO6þOfŽg%Æ^S•«öF'4ì;©;;ýH2p·òët3™mCb>;O_±sO6ù]èùº –…ô2ÝÍAf0µ¡•9ã­8óÓ¿ ê‚´x•Œµ+Ù\œ クd\ç7—n%ǦpÐÚ JÖJ¬®N ¡)– ^ÍîvˆøÎæ=4{€AèPs³ø£›ÂYƉ»ÜÚróréÀ÷lyG—ÃI}ôíT·ä—h¸º¹9Û§ 9®nÆÚF¼wÐ Ãjgüí~b±¸dd‚Ÿõmhïo/6ÎüûA® 6>íëöí2ŻšŠ.Í€4hhlõêIAØ@@@A„Ün)øïÖ.e€çÝ´û <Ÿ³.hwõÖr1|5ŸÅˆ³õ^f—µžGãöù\{Éw?› óü¦:¶2Î>Æû+–8·¼oÄ}!¼5“žÞ6|Že–6a$MŽ·'h9K~1æ?¨ê:wôê‚<vZ™Lt9†ÃÈÉ$Î`¯¹Zç÷Ž}õnþë¦ÐAÅÐÕÉÉ‹ÂW´Ó‘í„Olgã23R—[þºAßÈSʼÆÜ]êµaò:)ªöƒè#NéÓ]È7Âb£Âb+㣑Ò6‘Î@Ä’OAÜ6ODnßæ$ârõ³Uãqgg &¥Úö ñ <ã¼÷u胳 {_«|žå–M;˜æ¾VEÈÓ½÷7g]þta±Ç†§Ž2ö¾K bí5®m o^)ßÂ\~`eqwã©aðˆ'lÐv¬‘ í§[8l  86Xñ¤y2ë Èœ„sÉ#Ÿ{Ó›¾£¿»H-UÆK‹ÉÚÏerÑÈék²)I`Š(Ã\HÖÉÐë®§¿Ø‚‡cãtùlY“O3á ò6qq-ó´ÈçkÎA~J|½,†[%^Á¡ÎaUì‹‹›ÊK‰qé¯â‚Îw .WȦ­eµìѰ'…Ï„è‚Ýç胋”£kÌå¬]6ìd"Š^ÆvMƒ½¼ãDÖ‡oÏÓ¿ª0¶eÆeqøºY\vV ÁlñÕªÖ˜Öt¹„Žð^ý ½ýµä Ü«F»}‡aýw'77gÚo\»é½o]z€@h€yeˆý%~ÃþdŸí ¿Á~+8|oᇄ^›ÍYÇÛ­v®µc˜èÞÀîRZᣣàVw§az[†rèC—¡^£êÃæDò\@›Ù:<›:tYÛNöž)˜ÏÊÛJêV#õÿã~лmÓ619ZÆ0òµ 7ãÐ-i^á•íÅ9WWUoÿ]ãÿñQ¼,µ½Ý¾RÓKÞWæú_wVþ'~A|_µ?'îúÏù¿íÜEã½Ì{£{^ç4ì2˜™¬æ1—~"ƒ²Ü±½²x†‚½ª{KO‡ïDåæ[Á_=§³“¼ëö;BÞV´i­ó/3Äø‰×¾}º:Q¥\*.vé©ÿÇVÿ9¿š×GÞÓçÝž¯»·ÊZ~¿ûo®_É«è8ÿÞÉþл¼â—ü0ð‹Ôy¢ ˜q¼æ`@@AŠÿ²³ù”  ŠNâƒvüêA‰¿a'ðŸÉ£¸ Ê ™eL­SZì"XÉØÑÎV¥íIÍeKÒ·ŒZoè÷6?å¿U«»—Kcú€ýÌÿˆzž«Wá.–Çô û™ÿôêµwþ—Kcú€ýÌÿˆzuZ»ÿK¥±ýÀ~æÄ9GU«¿ðž—Ke¬w á±v›j½gvÌù.’Bþ_¤oÅRúú—ŒL¯M :NbK5a·g39›½ ¡rjéSV¼7‡M5-§9¬©þ¡¡èI÷…rýŸ¡´ý[õz¿ô¨hz}éO³ô6Ÿ©ÕêÿÐ~¡¡èI÷¥>ÏÐÚ~§W«ÿAú†‡¡'Þ”û?Ciú^¯ýꄟzSìý §êuš¿ô%¯‰§VQ,qžaÜ\ât´Òðz:Vâ¬wù©©xÄËl†2¦V·“܈HÍìuÑió‚;—M«ŒJº:×Ñ·'Oô# û©ÿå—"ŽÏ´üNñôƒú„ýÔÿˆrr(}§âw¤ÐŒ'î§üC“‘Cí?¼} þ„a?u?✊iøãéô# û©ÿääPûOÄïH_Åàq؇=ôà-{Æ‹Þâçkͳദiäç×ñZºý¯=%w0€€ƒÿd=gó(%@@@@@@@@@@@@@@@@@@@@@@@@@@@@AÅíùÔƒ~ÂOá?’ GpA”wŒsÖptaò@ÞÚÃËCÞ6Ùéç]>J5-ßÑÍâ5gN½½^3úkÄ??oáÙî]ý&–ΫWsúkÄ??oáÙîN“KcªÕÜþšñÏÛøv{“¤ÒØêµw?¦¼Cóöþžäé4¶:­]Ïé¯ÿx7ðì÷'I¥±Õjã\¼™zÐ]•–!žFÆáÙ5¥»:ô¬µ|-"“5ôk¥âo7ˆ·«Úf/ËJ(Ä:‘²7 ÎøßmÇœ½Ï £]KO”8ÿ®r?8vßròºïñïétvþO×9œ»o¹:ïñtº;'ëœÎÝ·Üwˆø¿ˆ:]¿“õÎGçîÛîN»Ä|_Ä.ŽßÉúç#ó÷m÷']â>/â—GoåbŽfÛ­ÅÏ2Gü€ßE¿‡ñÚ³©¼æ%–¯†ÓŠLÖ1†x·9g ZTåXs‡;›¾PßO?P½}kÍ#³?᫯i›ùCÉÿKóÿ?oÜ3ܹ¹Ú›½³ü7ÃüÉý/Ï|ü}Ã=ÉÎÔÜû?Ã|?ÌŸÒü÷ÏÇÜ3ÜœíMϳü7ÃüÉý/Ï|ü}Ã=ÉÎÔÜû?Ã|?ÌŸÒü÷ÏÇÜ3ÜœíMϳü7Ãü˹¼O~þOÈo=“±ÎcÃH#À릖Ú:¶µ±gŽðZzZ|Í>Ïbº^0€€ƒÿd=gó(%@@@@@@@@@@@@@@@@@@@@@@@@@@@@AÅíùÔƒ~ÂOá?’ GpA”Øž:µ¥±3¹c……ïw™ lŸb x|·ëоRÚ6ªÄà°ÐÞѤl?ꃠ€€€€€€‚½ë±ãèÍnV¸²¸4uÐó+R¼vŠÂ·· fÒ™c^;œ ¾¸N{e”H€€ƒ‘Š1xëî£;¬ºÃ×¹Õ’]ÜIh>d*¶c¹V;†JÐæ‡°±ÚúAê>´òÙX0Ø÷ްǺ69­!€·84wý%cÉÃ&f|SZþÚY3œGÅ-q kéø¥Ôõ mPaVäê¿(ühyò¦D&,-#l'[¸õó Í\[¶-A]åï©'e7Å kz¸Ü‚Ê   ­![UÖ­ÈYH .$ž€$”q¼AÊØ’´š;´=ÐØ…Ñ?”à6>”ž#lj¡§±3dÈ`®ùƒž‡·¢ /ñN/zJSºË牭sÛ I%å»e  êÅ#f…’·|¯hpæ:>p{C‘½7füÍs£­¥xhêCFÎKͱ^9Øl¬÷è ßh@A”Œ‡bñ–d¯aó—¦1V’FÂP^ZOw9¡NsOÍ‚‰¦GLHØ kzžPIÊÓËWtôäsšÇ–=¯acãpïkšzƒëA”€æÝ‰ånÚÂÇ6¾/)w.½{Au6´A„A„  ÊúJý†;üÉ?Ú‚üVpøßý7šÞ(e±+b†7I#Κ֒¢f"3)ˆ™œB÷ô1ýÝ?°{Ö\í?‰§'Se9+OL–H^ÈÞHkÜ:8އGèZE¢gg11Â%d-â?ë¼þ*/÷…–·»·ÊZi{ÊüßKâNêßÄïÈ/‹ö§äýßYà7ý»ˆ¼w¢ š:–ež8$s|á«ZèêZ3ZÎÎ¥+8™DAƒÐŽñæYc A5?øêßç7óZè{Ú|ãû³Õ÷vùKOÒÿmõËù5}‰ôWÙŸöÿ/¹^Ø€€€ƒ¹ÁŸöžò¤ü‚×CÞ8=£ý4üáô¥Þù+þÈzÏæPJ€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©&ý„ŸÂ$Žàƒ(8üWŸ…r2HÎZ²¿ú·–ïL'G]ãèAÁ~:ô<=†4Ý’³K³\†½· Ü måä$ƒÊþ !£áééØÃÄú6¬Y‡nö\çHÒV»}vM4s1Ü—+(8¶s­iܤŽ`#}VšsX¼qy3Ô‹Mg‡Í³%sÃYˆâ}ÖË?ÖÁqåΌ릷à|àë¢è¬[›\ãçyšòíŒþëŒØë؉v̆Ԕí’Bæ8°Þá¢<3­ã_ílM-YÏŸúi'ÉS¿’uë0ͳ6Ç)k" $[Üw®»ó©™ŠMkŽÝ¿”DMâmžýÚœÌõËoÈçr[ÅöÌŒŸŠ%n»‡ÓÌò¢~ìzI͘ûÓë !žë!‡ -©]l_c/9æ1òö‡¯›[ f+3:‘±üù"&ØŠL÷ÏñæõKØòO”ƒrŸ«)AiÆ•~q-ƒ/Wë]Ð_†ÕÇq´Uì<Æ×b{GÀÙ £vº$yüÛAÂÊÙšÏ qm;åì³"8ùœO#D‘é£Ì;ú îÕÿæ&OÿÇWÿ{ÐmÄ–*¾z´üœ–%æ‘•±òvn{F.vÆš6þ^ݽkÃH;¶±™Ú™|ŽSj‰Äg°±öy­stß_L&M¹¬-<‹c1 1‡¾SÜFüz틵œÇ泓䭲w6Èd-˜ˆ™ š#äî;¿¿e^äÕgƒ.ËMgÈÛ/“c¥ìþ&€çyØý_©?“ÈG‚Æå§µaÑÒÈÉVÓe~Ëá/,úè\ÓË×Ö‚Ý×9x³–ñ–e¶YZ¼L—“™‘ëKs\â\¾„.ž“Ùj*òä[4NomW!#$ŽšÞú8$ô^"É:?VR`Ÿ%}Ždx4kFGùšÝý}AÌn><_ð¶88¾*´¬27;ÅÍký›Ao±Æ¼BÖ|‚Ê®wñòõÖSÈPýcúBt.³b†)®“Ècsÿ­:è=SnO#_‡ç¤ËÓ™oõk-¼óHÈ‹Ç]ø¸ €J¤Ƀâ¼~> ¶¬UÈC/=àOQ½Gwr`É]ø'žñ¹7•5²j~Ðóë´:÷÷tA~ü6r\eú´ä-W§úµ³IyK Ú4áÝôë©ÐAW.Éhæ§±˜±•ýX«j‡ë€"FŽ».þÑuðAê¯Z¯S=«3˜`Š'=ò´õkuÞ>”Gfj¼Oˆe×1Ö¾Ù['ë)¹ÄÀ3™®˜–žžaÞ‚œŽ»–H<¯*s)ÙËÈÈY?^­ø¡½ž~öƒ¶Êògx‹'R{÷kÖÆö0Á{Œ’æs¸Ž®=Úß™þ¿g!d–äífŠi`tº×iÈòÐï¬õ í ñ¤¯Øc¿Ì“ý¡wø/Ågü0ð‹Óy®†bÕ‚;ÅI´Gð–®18k¥ç?)Yáì$9 „÷تµÅ» éÄb Ã]zë}Ë=}Y¯jù­£¥ï>I¸›*ÙæòqºMåxiíº<삇z§ÃVó[_S3Ã_'u9–ñõÞ?ÿûÂË[ÝÛå-4½å~o¥ñ'uoâwäÅûSò~ï¬ð?›þÝÄ^;ÑMN6Írßò\ð ×F±mJÖ|²ÏVf´™‡¥}»ÈèãÇHö3£\×ú½mkÒxk§3òcN–ŒÍÜœëÏÝŸg$­<ìñ^w¬f·Æ&|ݾg®så/5ÚšŸüuoó›ù­t}í>qýÙêû»|¥§é ÿ¶úåüš¾ƒÄú+ìÏû—\¯lõ ú,+)^žPãÝ_‘`£õݱãôÛ]:Z"Ø|Þ§ˆÖѵôbÙý^),SdìI _%ayÔ:×/Õáê\—œÚ{=í Ít«9ýUU[;œÿiàÿ*OÈ-´=ãƒÚ?ÓOÎJ]Ϙb¿ì‡¬þe¨"“¸ Ý¿ zboØIü'òA¨î2‚ µ#¿BÅ9‹„v"tO-::pÑ×Ó¢‚•ŒkZ¬òW© ±ˆÚkÈÐ\ÐøÛÓ½Œ^2¾"«[´-æsÜùÌç¹Çnq>$”An±µˆO4`‡Âí8iZ¶áœ«hÌaKõgV·ÓX™×,Ò<–Žà:h×ÃÅ_lÄÇl3ä×ê³>:ßQï/›ùãÑï<¥½|ý ¬^b'õ^iÑV~­<“bÔQX<ÓÁšd‡Ç}67ã¢6¯Óí…'F&|ñ”×põ/ ­•®kj¼:6°ètð?GAÓèU¦¥©œz­m:ÛôdâkÀÊnݱò¿‹ëןD…©nC—^>?UåFŠQbë׳”iof&DðOÅÓ7­?R‚¾OJì7›nÝ;P°Æ&« kœÂvZv#}P@ÞƳkרì-X$&M¿œžò@ÿ(3ŽÑ¤x‹­}ê‚\^ \¶'سfÉok=‡‡=Á½:`tPTÈÐýa[±ò«5Hpp’´œ}:=>‚‚¦?‡à£|ä$·nõ¾NͳZ8±›Ùk@ <6‚°àú¨QsÈÄݰ¡ÚÿQϾo“­ë}yw­ šÿ‹óÌ÷eòÅ?í+Ã8kM:m»ú Z°R©ZѶ(a`dlos@î8¶x6…Ÿ+ˆÚ»KŽsæ©ܱ9îïvµ±×®·­ø šÏ ÖžÍ{0ܹNx+ŠÝ¥yL‘Žàíƒí((e)RÀðÝŒ-V­­—°…¬tƒÇÅßÙv|È:ðOÃu1-¹f»àcK§®ð×—÷¸ìƒ½’w´â°°bä±8žÅ«6KLÓØxsÜ4@ÌŠ’ž‚|­œœy\¥iì€ØNÖà6Ó¡ô}(,Ùáè-Q­^[—5GóÃsµ»]×g›Z=µ­iøœ=|DR¶Ë4³ÈdšyÝÍ$®îÙ>®€äŒd2rÀ¿·uq\ü^Pînï>ÐUw ãßFí)Ší‡Y“oÓ›! í¤wh€B ãø~7é-Û½k“³d¶¤1³{! ô6{ÎP›ñÓVž›®_¦{¤òFÎlq;ÛF·ßÔHß‚»qp7.rÒv港­ü^Pînï>ÐRÈpÌY)f2YVr ”ÄÀDáæîØ]@(:7hWÈcæ¡f>jóFc{GOŠFºyrëpµzÖêÝ’íë–ib`yAio.€_O~ÀêƒÊEÖc¦}i3ÕóóÈé]VÜÚ혞þ£““ÎwÔ}(=}¾ŽåŸ-mËtmÉcžJr†v ƒÝ³£Þƒ¡¡[F*U#ìà…ºkw¿Y'Ä“×h, ñ¤¯Øc¿Ì“ý¡wø/Ågü0ð‹Óy©éÛ’•4aŽ:--xÛ\Ò4Ab/X´bV­¦³•ÆæšØ] q•þS5Þ±ÍÕRt¦gõÙ²7e·?/i)ÙåÁRÖ›NeÓ¥¥]*E+å ê­Î ÿ´ð•'äÚñÁíé§ç¥.çÌ1_öCÖ2‚TIÜPnß=H17ì$þù Ôw@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@úx¯ÒCiГ”ò2W‡;ÀѯÉwx)ûÒáñ‘÷aà—¦óDpŒt¹ü{ÒçyLgCÌ ü–ZÓ;|šè÷ÔŒ>•Ä%•߯Šá¿X ã=©ŠKêü÷´8kÇz" è‚ëõƒzžÕ§§ÐVÞ3­Lo õ{iÛäÓô„Çvxù5ñù]à ×侃ÄyBžÈ˜Íãåþ^1r=Á{‚˜çñ,nhØd2á°[h~7Ÿí)ˆðóáô…Üù‘+þÈzÏæPJ€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚);Š Ûò©&ý„ŸÂ$Žàƒ(#š¬D觉’Æî…¯nÁú”ÄÌNa1‰QþŽa?¹éýÃ}ÊüÝOŠTåSh?£˜OîŠpßrsµ>)úœª|0G0ŸÝþá¾äçj|Sõ9Tø`þŽa?º)ýÃ}ÉÎÔø§êr©ðÁýÂtSû†û“©ñOÔåShOW£!’­ð<Gi×­V×µ»L­¬yBËãd­,‘í=áÃ`¬íX´bј^&k9„«i|λ .›Gà£N~¯Å?Sõm/™Ã÷a:m‚>‡?W⟩ú¶—Ìáû°6ÁCŸ«ñOÔý[KæpýØN›Gà¡ÏÕø§ê~­¥ó8~ì'M£ðGÐçêüSõoJð;š#Þv´jhéÒsZÄ~ÊÛRöŒL¶šlÄbž&K»Úöì¦"c ÖÓYÍg§ýÃtÔû†û•yuÙ¿W¯ñÏÔþῺj}Ã}ÉË®ÇW¯ñÏÔþῺj}Ã}ÉË®ÇW¯ñÏÔþῺj}Ã}ÉË®ÇW¯ñÏÔþῺj}Ã}ÉË®ÇW¯ñÏÕb­”Z[V´P5ÇdFÀÝû¢"<™_Rúï3?5…*1_öCÖ2‚TIÜPnß=H17ì$þù Ôw@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Aåxó+oB´Tæt.±#ƒžÃ§ô‡zëðšu½§‹ÑÉâµ-JÇ«Á~·ÊyÝüCýëÒåiü0󹺟úß)ýçwñ÷§+Oმ©ñ­òŸÞwÿzr´þ9ºŸúß)ýçwñ÷§+Oმ©ñ­òŸÞwÿzr´þ9ºŸ¥Ãùü¤ºuëG4ÍŽFK!x!Ç^=ǪË[Fœ¹˜Œa¶Žµâñ9{üõ©«Ã"yghã·4õÐËå}¡«}:Ö+8Ëè¼mi™ôpüªÏΧû×/«ñOÖ^—.Ÿ }*³ó©þð§7W⟬œº|1ô<ªÏΧûœÝ_Š~²réðÇÐò«?:Ÿï su~)úÉ˧ÃCʬüê¼)ÍÕø§ë'.Ÿ }1÷¬²ô-3Èö½á®kÜHÑéâº<7ˆÕ®­cŠg3êÇ[J“§3&xß'n…j°Õ™ÐùCÎö;M >ëÛ×´Ö"!OfhSRöµã8ÃÅþ³ÈÿxÜüCýë—ŠÛ½ÎF—ÁH?Yä¼n~!þôâ¶ç#Kबò?Þ7?ÿzq[s‘¥ðGÒÖy½8­¹ÈÒø#éë<÷ÏÄ?ÞœVÜäi|ô‡ àìÅù3"”ö¥žcsµ+Ë‹Hñõ[h^ÜX™y¾Ñðúq£ÇXÄÄǓޮǀ ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜe-ÁBœ×,¼G 2=ÇÀ²ƒhlG5vÎÇŽG48|5¾¾dµÍsCÚàæ‘°AØA›õªÒšì“3°Ž|=È%‚xìÂÉbw3Ðà~‚6‚DÞ»ÐSÉdU›±µ³v—ò‡t:ðØW¥8¯žÙg{ðÖmÖb•²1¤ÌZ núŽŠ³Z'-‹Ú\p'©Q‰NaëcaÇ3u¾a¯>ÑeáÜâ9áÌÍ‹¥†µ~X"d²:)#hÛ×Ê#ÌPuªÎùëE$к¼¯`s¡{s>ƒ®žÄóùs†ÄKy‘6sØÎBí|§†÷ýh3TÉÄ–±=ˆ ¯Z9ÄœÝ]Ìç kèåAÐ{ÙKžæ±£ÅÇAA¡À‚ˆA«$dƒl{^?Âv€Éc“|k´tyNôƒ-{_¾G5Ú:: é±—–´¸w´8l}HF aíçíæö Ø÷ ¡úÞ1žýQ$/dƒ·ŠBG, 8úÐ1Ùˆ²R]ì£sa©9ƒ·q²9£ãkè¦üáàö–ó u½ƒÓH0É#y˜ö¼yÚvƒt[·²Úµ Š›Ì÷žàRÁç"ÎÁbX«O\A9„¶vò¸è½xt=Ǫ›œÒIÜsbËö¼G.)±4±•`J½ó8·Zút,n%­‘®-ïÀëÖƒ<íåææóí ±µÍk¤c\îà\6}H7@@@@@@@@@@@AÆÉç-Кa í¸k·žY˜æ5ºÖþ(qÛÈd*7!ÈQ‚ígsC*åjL»$_Ô=åàïDyÇxØê7Þ¦5+kpÄ÷DéÚ+Å0¦´Qoÿ]ãÿñQ¼,µ½Ý¾RÓKÞWæú_wVþ'~A|_µ?'îúÏù¿íÜEã½èp÷fˆHØ€¨v‰]tðZׯCžÞ'N³‰•I"|2äik›Þ æµ-IÅ£Ú¶‹Fa¢ªÉ©ÿÇVÿ9¿š×GÞÓçÝž¯»·ÊZ~¿ûo®_É«è8œ>PÑ Ó] J W¢ Îâ,lظì…z]´®¢îG´èuñ°ƒØ`éT£‰®Êuâp‰¡Î-=;ÏÒƒ¢€€‚Žf‹ò8ÉjÇ(Ï-ÑwV»DS¯­¦ø-0ÏR¼UÆ\ F°Yºƒ QWVÂAcÁ•ÀQï]‰JNsæ´ÄRñŒJy1õq÷°sU‰±K$Ýœ²7åH džcãÔxªÅ­zÞ'þî´Ö´µ&?îÈ¡§Rî3-vüm6Ù4áÒ»åC˾@ÓýÒ™µ«jÖ¾Xþ¢+[VÓo<Ê9®Y¤Ø2OÙý&a®_¬ó«Å+lÖ<¢„M¦¸´ùÌ,E[°šß3Yy’õñˆ3´?ÿ B‰œÄêþŸÏ‘‰/×øózõÄíy ÊOǹAŒ»W UùÌÐv¼ÃoÖº µ’?Ò -±#d˜á´çµ¼¡îõ ÉãÙ/ q3ãpsNs¡Aøñ ô•ù‰”ÿñÕÿÞôñëÜÊRÄþ¨¯’´øß;·òÅF'¡Ùë­i˜îg ÏD½Ó<@*Ì ˜ã„¼s5®èCwÓëAÝâ|m, Ûº±Pe‰aŽËë·ˆ‹Ã\v;¾)#h3ÄXê|=Â9K*PҙкHÊy7¢v<À“´â8~õLÍ+µéã1ÕŽo"²÷› #â¸í€ß7TaqÐðæ^…Ž2¬–'t«•‡åÌt\D€õ#}zŽž1‹ÇCÙzPd±•¬IfÃÛW-Y^÷íH]ë}A#ÔƒÚ ó|tÉ+àÿ[ÕxŠî5âX­ü¯ˆæŸ ‡ AËÍc_HðÖ ¬YªL…Ñ[²9åkä²Isµ®§Ô‚X¸W&ü>S#éSŠÍˆæ­V$LÖ‹Øvy\Gpó”.}X¬ß Ì4«µË ˜ëc~ÁåsH׆ú D€€€ƒ4F Äg¿ü´¿íj¸±ŒµBÏZ6®–ÎÂtÙÎcOœxxé6!Œáþ/ÍK ":µñ –X"ñœHÃcÁJÕ›SˆxnÌ8Š8¦Û{ÀmyK¤’3:ÅøuÙêƒyXè«ÏÁM$vù²/ü#÷+¾ ›õ„38¸1 ™¬†2®S#ØçHïÛRM ô-®ªle;z{s;‹pòÉãÿ¤MÞÚ‘…ÇüG¨húû‚½pãèÁN»ya‚6ÆÀNôÐAᇡoÅ·¬Vl¶a¹mÐÊ´sÃýž½zw ´qµ1Òð¥ú°†[±aŒžÀùsÄâîsÞíž½Pf¦7–©Ä7³²Kq[†güºÌ`øœ§û#^´G³v¸?õ³ ÆjÉ;Ý!äaøÃÄoD‚‚;´*ׯq¶>ìmZñ¶haâFóÉhðê6‚Ö[A¸þ© há‚{ñ:FB9÷ÞõçñA<ªõx§+‹Ç‚Þ%®"ÊÖH\æóÜ¿$áÊÐa2ðâ­áªU¾k8År¯É²Æ‘Ͱ~0vôzïÖ‚÷ËZÅÜ~)ؘ2Vgç–&Yw,Q†·‡}úE˜‰¯gñ%vº!©’ýŒ2 ,/‰§[ÓºŽîòƒ§Wú–òùÚXX±¤Pä­+ û@K´Í†ø o®T’†>‡ asèØ2O–©m–Ÿë'{ÜÎïvÁvÇÑô úŠúJý†;üÉ?Ú‚üVpøßý7šèa¿âlàæÿaYjÎ">p×KÎ~R»Ã•qçyŒ£°…äߊ@ß1ì­õYkÚÿ‚‘澌R~õ§É§䦽’Ó£BÇ20vH#m.ó»GêîVðôŠ×s^ókl⮇:Þ#þ»Çÿâ¢ÿxYk{»|¥¦—¼¯Íô¾$î­üNü‚ø¿j~OÝõžóÛ¸‹Çz)é°‰5ÉÎ7µ®†9µâòË=\ðN–S“í]ز±˜ëé^ýçÄñOFMcC{9rsß*¹4LXyù{¿÷Þ¼ïhy×?‹ÝžÊØòÏg%yŽäÔÿ㫜ßÍk£ïióîÏWÝÛå-?H_ý·×/äÕô'Ñ_d~Ûü¼jå{`ïB_O¥U– ¥s#Jò°ölØtðë¯èV"b&ÑÝòš—šZôÒ´ðÏýÿnùöjKRæ,¾ä" Ëþ4mNž¾ž>+Šù✾ÃE#F±IÌ(ª:Î ÿ´ð•'äÚñÁíé§ç¥.çÌ1_öCÖ2‚TIÜPnß=H17ì$þù ÔwA¤±Gdf)©2Œ ­`“,!ŸÇ@w}At¸{¯=zÔclV¥c‰xxóbz cø{Š T™¼’Kƒ›æøÄôëÜ‚:¤u*D"†1¦0@ë¿ UZÕaY‚Ž{ì3®¤.èâ}h6~6”ª××aÜ\ÿV@å}GH*Ýá¬.Fß•ÛÇE4Ç\Î;úîæéÚúv‚ãèU’ÜŸLÕšæÄÿt zôƒQ¦$µ'“³šàÁ#}¨”oê肵nÄÓ†`¤ÆG^nÞ&ó8ò?ZØÙótA<تfšiª²I'ƒÉäs¿µÉå?FÉA;‡ñX™5mŠW7”¿™Îw/›d’ЂL–˜lBýVÏØ’èÜIk˜OC¢#h9Y¾ Å ØŠ1vRYd–ê1ݘ³n‹wáÐ7ͽ ­„À¾·G~®ajÇÙ$]°s¬8ë[kI7G®÷Õ^¿ áj_ ÇCáÅÍpÞšOyzéUˆý%~ÃþdŸí ¿Á~+8|oᇄ^›ÍI^ÄÕglðHèägÉs{­«ŒJbf³˜\9ì‘;íÙ¿?aÿò©É¦ßݧ6ûÿe9ç–ÌÏšy$;sÞJÒ"+†s33™F¥ xúïÿŠ‹ýáe­îíò–š^ò¿7Òø“º·ñ;ò âý©ù?wÖxÍÿnâ/èˆ:fîÅgÎ×ë .nÈ]µñÚÕ¯\¶ðºS9”ÓIbS,¯.yï%r^ö½¸­=ݬV1 VMOþ:·ùÍüÖº>öŸ8þìõ}ݾRÓô…ÿÛ}rþM_Aâ}öGçý¿ËÆ®W¶ ·&RüÖ!±%¹],Þ]ÕºV›Úf',cCNµšÅ{Oš %µ;çžGI#ÎÜçw•33™iZV•ŠÖ1Ô,îpgý§ƒü©? µÐ÷ŽhÿM?8})w¾`@@AŠÿ²³ù”  ŠNâƒvüêA‰¿a'ðŸÉ£¸ ÊúC§bÅ “äd8ÉÊ6ZÐ>®‹³ÁÚ+y‰qøºÌÒ&=߯ØW«˜y˜“Ûì)˜1'·ØS0bOo°¦`ÄžßaLÁ‰t¸v‹™êM‚';³’<ò5­ ’JÃ^õ9̵ѥ§R;>‘ÄQ=ðÃ#ZKXã͡ݰ¾CÚT´Ö¶ˆò}G‚´E¦'Õçö?öŠôÎaÿ°†aÿ°†aÿ°†aÿ°†±Ñ>kð4žYãÓ¸µ¿†¥¯­\G¬1Ö´WNs±ÇÕg–µ;ÆçÇ ž$-åæGý¿¯1 {+Rµµ«3ÞpðÛûr=ócÿ` lì ý‚±ÿ°Pz>«<¹Ñi±»±Š'‡< 6·Ð¬ñeæ{OR±¡Ã3ÞfD]¯›b¿ì‡¬þe¨"“¸ Ý¿ zboØIü'òA¨î2€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒ2€€€€€€€€€€ƒ@A”A”¯û!ë?™A*¤î(7oȤ›ö üj;‚   uó uó uó uó uó uó uó uó uó uó uó uó uó uó uó uó uó uó    ÅÙYüÊ PE'qA»~@õ Äß°“øOäƒQÜe g”½åïw+\ZÖ´‘ÝêA¿‘Cç“ï]ï@ò(|ò}ë½èEžO½w½È¡óÉ÷®÷ y>y>õÞô"‡Ï'Þ»ÞäPùäû×{Ð<Š<Ÿzïz‘Cç“ï]ï@ò(|ò}ë½èEžO½w½È¡óÉ÷®÷ y>y>õÞô"‡Ï'Þ»ÞäPùäû×{Ð<Š<Ÿzïz‘Cç“ï]ï@ò(|ò}ë½è#š!Z#4oxäêZçúÐXñ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A«ÜÒãÜÐ` ÈÞ£A%–4_êË?š/õ@åŸÍú rÏæ‹ýP9góEþ¨³ù¢ÿTYüѪ7æí`(Eb8a”É/&œ\5Пù.¥¶˜™sëêΕbaæ>²0«öÜ»:*näë-±ðù…_¶ä詹Ö[cá!ó ¿mÉÑSs¬¶ÇÂ6Cæ~Û“¢¦çYm„l‡Ì*ý·'EMβÛÙ˜UûnNŠ›e¶>²0«öÜ7:Ël|#d>aWí¹:*nu–Ø?¤|€þ¯«öœ£¢®ç[mžú>Ýñµúã|W›/J'0Û–4_ê¡',þh¿Õ–4_êË?š/õ@åŸÍú rÏæ‹ýP9góEþ¨0çKæxg(ïåÚ b¿ì‡¬þe¨"“¸ Ý¿ zboØIü'òA¨î2‚:ÒÿšÿÍ„Ofкk²(™ÕÏ‘Á­²PAG-ŽÉ‡ëZ ù]Œ­/¯EÄ@Ú   ¯fýj“W†yC$´ó-#å»DëØ Ðe{ßðRÿ G?ì$þah2€€€ƒÈ~’?êZ¿ø‘þ×.ßøçäâñŸ‚>oœ/Uæ H (?$ú”Àûøxÿ„~Kç§ÍïÇ“u 6ƒ(!µÿÿR z WýõŸÌ •Rw·äR Mû ?„þH5ÁPGSº_ó_ù °ƒÊeà+ÇXìmÖ¶ZuéÉl@ñ¶É'0h$xòŽ¿Z ù÷G„Àd²XúðCf*åÁ퉻<½@>tÜŽc/„áã’¹r)gºøc†1õu‹ûû¾3ôþ ƈlO™š„™¯ä½³m¾™ƒ³p:-p 4AZ ÔøŽËs¸ÚÌÎÇ–†ìŽŠ@Ú}›XyIcÇG wžˆ6ÂÏ~Ž$ÉZÉ:x)Z”ÉbÑÏË ÷š:}(+b¸¶ü·qo~CË|¾F²ÅFQ|b·0$¿]@:g¯zGŹ;˜žžíß(câ çiæ‘­ ýEX­fq¼OB…ì„w`ÈÇ)ЀGؽ€;âë¼h‘ת>î8»$OÉÁ{›–Ák1b‹ÝÏ/í@ùZÛºx êÚâ ¤Qeè²f~°møëÒ{£ ”4°‘ã¡ÍëåA>3ˆmåo`á‰Íhš¤–/7”lÎ_£ãïØƒk~¾.îõ§Ã^ãÞòÏì_ÜÔa܃Èþ’?êZ¿ø‘þÇ.ßøçäâñŸ‚>oœ/Uæ;\;ånDÃ^9çenh›$mxæç¸ôîÚçטŽÎ#?ᾌLç™Çùo‘«ZK´£–.ÊËá&ÔTcÓöy@h:oÀ(¥¦+3žÞ™MëhŒwõÃpûv̳²k>ÉD­ ÞÛ˽o¢s§†wÎ?Bt£Š#ÓÏõ‚ÍŠ1ÔšÁ¡tîí"Ìcw½zŸŠuçØNtÄLÚ#·c•1õÜtì4Xlv šf.ÎF¹ºïÚë°Uôõx¦kÛ¶ÝÔÔÓàˆ÷ìßk?‘ˆÔÓ6³¤íñˆÑóëQ©‹Vsêµ15´cÑÈ[°`ü“êA÷:ÿ°øGä¾z|Þüy(ñ ß Ó£‘ñ»iì:pê;Š„¹î»c°¯ViÛu®ÅÄv'£½Në´Zâf׳0ÿ£ö5å=®›R¸ôÙkuÔ ýz(,Ï—š ¦“sX‘ÍòñdaùN'׮ǫ΃4-_Ÿ-~)D]„2·N;áô šÞZS:9™'Å`“`l9»Óˆþóô ¼Ç5íkƒšFÁ` Ž×ü;ýH6(1_öCÖ2‚TIÜPnß=H17ì$þù ÔwANéÍæ‚Â6kü•Š·©Üu+ôË»)ÃÁk¾SÓÞÒ‚ ð¹l–!Éå+ÈmÂbc ªX#ßyÑq.ö„2xFdðÌ éß‘>)Ø>4r3E®Ö;Wn'-rº™Œ¤3ÃbBzÆ"7Ó˜’ã×^È)Õá|mŠ}ì´r³ðaŽ*ݘpä-Û¾1ë£áÓ¿§T áéàÈä•ÄünMÎ’z΄óó9‡OßwAà‹Ã汦½C™Šl}pÖ:¯õÎ` /æ×Nu×H-ç±7‡’€Ÿ±ç|nçåæ×+û¶<Ú@»ˆ6ó˜ÜŸoÉä"QÙòw領ƒŸW–ŽձyxaǺWHØe«ÎøƒÌæµÜÀkdë`ëh&±ÃMŸŠàÍùG+"ŒWäß;Àpk·¿÷tÒ†›ˆËä/‹«m;ú¨ù5Ø´¸¸·{ë·8Ÿ²˜ÖW±¶LÁ‚ŒæbÂÎnÓlsußÓåm;ú".|c/>:í°,P-gƦày€¡ÛÐó\«úÏÙlç2ôß䃱kzü§ãêð9B+Éå q}Yl>*$Ž‚âãËþ÷;_@=z ÷¿à¥þ ŽØIü(, †Õh®W}y›ÍƈÿŸ­U(GMò>9Lº/= €Ñw¬ø CDѵo´ß”66òë\¼»ñúÐ<‹ÿŠ›Ý§|M‹{Ú £f R¾­˜[ Òö¯ds¹¤ü®S±ßô ÝøpüËoöÚ`ÓŒ<½ €‡oÔР莈<é#þ¥«ÿ‰ìríð_Ž~N/ø#æùÂõ^bÕ;¾KZì=Ÿ7•CÙozåøÀïéîTµx¦'eën˜Ý¾/ Ü|³—ÄçÇ<.‰ý›ù×VŸÑF¥8â1èiÛ†g.¾6ôw/×|œÓ¬ñ "m¾bIÖœ{œ9‰úºÏ©I­f&|æ?gE/´LzDµ»8ÆY­yñÎ,LÙ#±VÍŽÑÝ™ù@Ýìè} k^:Í};wˆE­Á1o]œk“T”°T†Ä`o›¶›´'ÕÐitV-ù§øsÚk?…šw<’+lìù¼¦÷®]wô÷%«ÅÒS[pÄþªªê0~Iõ ûØGü#ò_=>o~<ä©þ°ÇÍS´ìûA®mo]wÜ¡*Ù ;nß«q“¤í/lHÐvõãëA¯ê»0[•õlÂÈf—´{$‡œ´srŽý ÄøWÏi÷ ¢-´×xoHš?³­õg~}ýðQ–¾NÍ–Ìà ’øË:‡Ðï»§™Þ& ³:IŸ!æ`@èïnÍÍÜ~„ZÐÆ†´Ðt#µÿÿR Š WýõŸÌ •Rw·äR Mû ?„þH5ÁPESº_ó_ù ²€€€€ƒ2€ƒ2€€€ƒI"ŽhÌr±¯c»Úá°~¤kZƆµ¡  €A² ÷¿à¥þ ŽØ?øPNƒ(<‡é#þ¥«ÿ‰íríð_Ž~N/ø#æùÂõ`€€Ü€€€ƒù'Ô¤}οü<Â?%óÓæ÷ãÉ"„ˆ!µÿÿR z WýõŸÌ •Rw·äR Mû ?„þH5ÁPCÉ,Os¡,!çe¯ØÑú ö–ÿwÛ>äÒßîáûgÜÚ[ýÜ?lû;K»‡íŸrio÷pý³î@í-þî¶}È¥¿ÝÃöϹ´·û¸~Ù÷ v–ÿwÛ>äÒßîáûgÜÚ[ýÜ?lû;K»‡íŸrio÷pý³î@í-þî¶}È¥¿ÝÃöϹ´·û¸~Ù÷ v–ÿwÛ>äÒßîáûg܃W6y€l¼föCI$ý'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A‚#h4ì€ît€yƒÊd=9>Ù@쇧'Û(ôäûe²œŸl vCÓ“í”Èzr}²ÙNO¶PSÉá)eàd7D¯cÎ”Žº×üÖšz¶ÓœÕž¦u#s? ˜ÜM÷î[uz»±é4¶? ˜ÜM÷îN¯Ws¤ÒØþ‚`q7ß¹:½]ΓKcú ýÄß~äêõw:M-è&÷}û“«ÕÜé4¶? ˜ÜM÷îN¯Ws¤ÒØþ‚`q7ß¹:½]ΓKcú ýÄß~äêõw:M-èÿô&û÷'W«¹Òilï6µ¡¡òh ŽW+ª;3ÙNO¶P;!éÉöÊd=9>Ù@쇧'Û(ôäûe²œŸl vCÓ“í”É»—;^“‰A" WýõŸÌ •Rw·äR Mû ?„þH5ÁPaPb¿ì‡¬þe¨"“¸ Ý¿ zk7ì_ü%põ Ê Aû!ë?š PG èz Ý¿$zØÑîAg#z5í ws¨“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ7¤Ïa÷ rMé3Ø}è“zLöz$Þ“=‡ÞÉ1è^Ð>ÕŒhcCGpA² òPlß’e¯îA3¼çÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#í@æw¤}¨Îôµ™Þ‘ö s;Ò>ÔgzGÚÌïHûP9éj3½#íAÿÙyt-project-yt-f043ac8/doc/source/_static/apiKey03.jpg000066400000000000000000001415231510711153200224420ustar00rootroot00000000000000ÿØÿàJFIFHHÿÛC    #%$""!&+7/&)4)!"0A149;>>>%.DIC;ÿÛC  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÿÀÁÿÄÿÄW !1‘AQRT"2Sa’“ÑÒ4Uqr”¡±á#35st²Á6B$b‚¤âð%DEƒ¢ñ&'7CcdÂÃÿÄÿÄ0 Q2!13ARq¡ðab‘Áá"Bñ±ÿÚ ?ùÁ N.à µ¶(c|’;s—UûÊ¢š¢’Dަ !z¥Ñ²5Zª;@òlð1šÇDéV8ãng*o_2ÑüF¢íSñOa(OÄj.Õ?öøEÚ§âžÀ¨»TüSØâ5jŸŠ{ÅÚBÔEå3íTMéìÓâ'iŸŠ{|B¤í3ñO`ˆT¦~)ìñ “´ÏÅ=€>!Rv™ø§°Ä*NÓ?öø…IÚgâžÀ©;LüSØâ'iŸŠ{|B¤í3ñO`ˆT¦~)ìñ “´ÏÅ=€>!Rv™ø§°Ä*NÓ?öø…IÚgâžÀ©;LüSØâ'iŸŠ{|B¤í3ñO`ˆT¦~)ìñ “´ÏÅ=€>!Rv™ø§°Ä*NÓ?öø…IÚgâžÀ!ÚFÖªò™ö%÷§° [¡Nj/)Ÿj_z{ŸˆÔ]ª~)ìñ‹µOÅ=€>#Qv™ø§°SÃ× Ä$¥WçFÙZî”R¦œ¤¡åÓS¢SÙ«‹±w.Ϥ<;®ÅVD£‡Y«DÍá"Zû·³À´fz¼YÑVÓ»“Ó¹ReGlW"x¨©Ï~Ž‚PÇѺœ>¢ª¦ |”¹2;X‹d['M÷–„óDá…ú-+ßät»\ÔUÜJE ^²šé{ÊÄÛô¡ v QAG«‰Œ¼¯¾V¢_b‡4Ü v¸’@œ•¯Ï7"Ûq yáØMn,éEµcDWxH–¾íà_Ѩd¦Òêx%nY#{Úä¾åF¨)Áë±|y©Gt޹œåÊÔÚ¼êJ¶!†Vas$5¬nT»vÝže!* O ßÄ*–ŸšÛ€W±®F¹ÍE]È«µ@c&æýdüÀ´ Àtÿª xGû¶ý€ |óK¿ÔýF–”´MÅ—åÜ•u9sÛ2fËÓ—}‚°eo–FFÆÝÒ*5©Òª¶f…ôóÉ ©•ñ¹Zä½ì¨¶P&Jy¢†)Ÿ›È«—s¬¶_Ć*&d0±_$ŽFµ©½Uy€ÅÍsæ9ÕTT^eÞhô‰hŒøsÖòD×EöZí_ýtƒEšÜ#Eª1›erºEÿ§b'ž„WÕOˆTÁ,ÎtJÇK‘Wfur]@×éV+^ìZ¶j¤ZTz"EþÝÈ¿™Ÿ } BÕ‰£.YìI$ÌžnrP©n†,Ñ$TG«›‘u.Ø·Ùø€îƒòz.cÿ$Úÿòݦw÷G¹÷Ê+þ¤š+aßþ¡»ú‰%þ˜c•øv#s¬-HõŽÊ‰á-×›a"Ηµ•š- cšˆöº7§›2YSÿ]p!%À\ M ËÿSü´üÀí®Jµ¦¡žv·3£ÎDéT@48fÐâxdUµÎ’¢ª©ºÇM¬TVªô}]Ѫ©¦ –¥Y––wÂÙWnv¦åqp1‘v7ë'æ«^ºW²žÑ»+žö³2s]mp<¦”z©as“öc‘\ªŽE[s›«Úz™2¶ð½Z‰ÓkªHe©~®ùÅØ«á_r[åm2:¡bDIc_û<$½îŠyÝ+ZÚJ{gr*ª»sQ7ªôÄšµ)RhÿnŒË›2j•mkß}¼Ö¹(|Óê0ª×RÔåΈŽG7s‘yЄ«ÓÉU¾h’hÚäWƪ©™9Òé¸ ¶-‡Rat¨Í4•ŽÖS¹×M\\×Nw*­¾Ï9(ZL29(êY>#á¦YX䩼·DE𘫹vó 'ކ‹ ¡—‘¤ÕP¹\ç½ÈÖYʈäD]ÿ†À•ºÙ`Zœ8裉Ί£Úç*µÞ-¯eúw&©”ئ-KÈ‘’D“JÊc•êæ­ÖéºË·› ”*ÅI@‘`¯«UŽ)õšçf[lr¢} ¹Á/E¢jã8|áqR²Y‘3Á3œÉšª›–ëÅ„*ËGpšÊ„gí#®H˜ë®ÆÙÛ?%/x¨pæM„ò”H㨥Y$W=Qû¹uæE²&©‹Àè%¯ÃYDåj­â‘^É™QUWð {èÜ]N"°bŠëHܱxVneé^eèón] ÄŒ%$~;¼$¨TØóÿÍæÇIèðš ˆip÷=f‰¹g\×Kùüÿ@0 ÐÌC‘ã…Ëû:¤Õ¯ÖÞßÇgÚÝiµK(°ˆ0è|sî¨T[þkø4º[6©;Ò6ÍÆŽrÙ3]6ý„ †•hüHµ˜Ç-mÝg$6KªìM÷$qä%Þ蔑·EfGHÆ®iv+‘q(pô+jÊkìý«?4!.ǺŒ|hǵߴŠä^d% 9=&+¢«…¾f²F±Ñ=·DTEUTTN}à{èý <”I^ÉêæMc·"5©Í¿Î=‡½ðõsQ¼¢O û7/9{avâ-cqÇQNÔlŒm—3WjX‘KMq*hðè°ªy÷ݪôjß+Z›/çO¢ú‰él•LÃ¥¦iQŠý{ÜÛæ½­d^ª„ºóZOÚ°Ï\ÿp„æ´Ÿµaž¹þàóZMÚpÏ\ÿp‘=æ´›´áž¹þà]îa¤8EL²Ë6ä{Q,Ïéú€oþ(c]j?Zïtè~4¨¨®¢T]Š‹+¶ÿô¤^å˜ìYâ¢ÅÙKJõUX+•ûì¹vïƒ÷=Ò,%d¥Iè%£UWGy\j®ô_hOŠ×ZֻݡØÓ‘<*-Š‹û×{ {|TÆzhýk½Ð<çÐì^xÖ’ËÎ’ºè¼Ëâ⚺f,óÑI~S;›wyü DšÈ“1*)®Ì­Îå[ý9@Íú‹È³f}%¥k[²Gl·?ŠÚŽËI§¢}ž×"£œ›–ýP=!Ðìfè×Ñäs®Öë]àß}¼<Ý¡˜ãæc䚉ÍÙšˆç"ñÊ Ç`Tk'£Ô¢Ý®uÓÍ|»€Áº5Ý}­’k¹Ý¾÷Ú¹|àz³C1v²V9Ônl®UTÖ»Ÿ›ÅÍt'u3à}M˜¬ÊÕÌäTÿéÚ«´;uC%ÏFŠÆ9¨šÇsÛþ_0è†<‰jŠ7eEGønðú?Û° £Ñ,q±£d–ŽG&÷k—ÿé?Š˜ÏM­wºâ¦3ÓGë]î€ø©ŒôÑú×{ >*c=4~µÞ芘ÏM­wºâ¦3ÓGë]î€ø©ŒôÑú×{ >*c=4~µÞèíÆœÅKÑíK~õÞèMÐìm­DÍE±<«½Ð'â†5Ö¢õ®÷@Æ= ÅánH’º­›#‘/è—Äük­Eë]îÌã]ÊtÄSøsZæ¢YÓ>û?èy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€;Íi7iÃ=sýÀæ´›´áž¹þàóZMÚpÏ\ÿpy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€;Íi7iÃ=sýÀæ´›´áž¹þàóZMÚpÏ\ÿpy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€;Íi7iÃ=sýÀæ´›´áž¹þàóZMÚpÏ\ÿpy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€;Íi7iÃ=sýÀæ´›´áž¹þàóZMÚpÏ\ÿpy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€;Íi7iÃ=sýÀæ´›´áž¹þàóZMÚpÏ\ÿpy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€;Íi7iÃ=sýÀæ´›´áž¹þàóZMÚpÏ\ÿpy­&í8g®¸¼Ö“vœ3×?ÜÞkI»NëŸîï5¤Ý§ õÏ÷wšÒnÓ†zçû€m4{¹†‘àøƒª$“ {]±rÌû¦Ôÿ—â¦3ÓGë]îÍés#ÆkÙîU½¯v¦Í›BZIq©ê¡•µ-I^é’xž«mS¹ì ˆ‰o2,ËÂ骪#ØÉë#{&zÊ«ã%®Ô¶Îžp• ªÕ©¥£cFòXÕˆ¨·Íw*ý›È‹G$TJúKÔQ£ÙREDsZ·DVÛ5ÀÆ)bª‚ª“_LºÜ©"±X佬¶èUBFU¬sS,ø{xV$‰’+v^è·éB~ ʪx)`§ÔSÓæV5dW¹UÛÕU~€)¿‹L1°…¢½åñ[P«á5½Oœ! UUUUUU]ê¼á Ùhþ„QÃKŒÕb Όϫ{šˆÇ~kbPÐé*˜¾,ùã¾¥ˆŒŠý Ïö© jÀ^ö¿6àE‘w¢ B"&äDyï΀E‘w dè7nlÔ{†íªÆþ¤œ„¡õàH@@€$@ à;³lШÿ­‹òpj” "É{Ù/ô €1và>¥Ü3åXçÔƒó”>¼çýÙÿÑ1ÿ[äà>Ж|ä Ffõ“ˆ Íë'›ÖN 37¬œ@foY8€ÌÞ²q™½dâ3zÉÄfõ“ˆ Íë'›ÖN 37¬œ@foY8›ÖN bç6Þ2q$}K¸^Ú¬sêAùÈúø€ÒSK%#ú©êžæÆÅvÖ±©uUM P“MêbÑúŠçá쎯¬m5};žª‘¢¹^ÕçM·KùÀ¹¤ºZì  §Š³Ç5¤©z¹SS½¬G'Úà.Ôcs7K©0HacÙ%+ê'‘UnÄE³lžuo#ÙN‘îÊÆ¢«•y“œ™á“MOŠÐé¬ÊæÅŒWIO**ìl³a_±[ø½î3É£ñT±’BüV4‘²"+U2­ÑoÌ FÃôÛMŽ*å™ÉU%‘«]ªö·`[CIßiËÉa¿ÁI-õiãëU3}>}àv/ýÛ¾…çÚ/I…è. ECN•x¥k¤Jzuu’É#³=ËÌÔé£ÅñÚêZÚ,¦†§ªdv±êÈ¢c|g*í[_b'8iô—¾)†TÐS³¡¦å29±T1oeEµÓjX OÒ¸ ÿÄV­6µ#¿û÷eô¶¸Ã¥¨Ÿ§š®6GQ$MtŒbª£\©uD¸@U¯•"§TXÖE‚¶ÅP(Aè'ŽI£ÌK"¦Õb¸@$[º ZìsQÉÊãØ©~°u4Û÷1úÆÓÁoÜEêÓØ\ž!«o°'ƒÈEêÛìÉàòz¶ûrx<„^­¾Àž!«o°'ƒÈEêÛìÉàòz¶ûrx<„^­¾Àž!«o°'ƒÈEêÛìÉàòz¶ûrx<„^­¾Àž!«o°'ƒÈEêÛìЀ*Ôxªë¹÷Ëñ?©æð;!PæhV•b©Š2HðÌRd©†­¬W1µœ×[p&’áìUqa•‰R°Ä®zµŽF¥ÑyÕ,ª.ç;4 ºmÕ»ûÜ#‡aÚ3ƒÍ[‡éCê)*ã©‘ñÈ³ÌÆM–ísr­¼ËÎ×–“ Ñ3à)j¨°JŠÔåSÇŸXÈUVꊾQU7V™0giÎ&¯ž3eª|’HÇ¿%Ѩç®ÕDº­º@ÚG‰ÑàÑñuĦJf×ÓÓ%3ž‹i.Šˆ½7TSU‡àxf–ãÖ¢ž:É’¢Ž¥³JÈÞŠžnŵÑz@ëtFŸ‹š]IšY•^÷¹ë舊¨¯Ú¿H´Á/¤:-³ÿˆ¯öÒ½ºQ¢–í’`,KÁ°ý4Å&ÒT¨†–»W5%K%•Œº%œÇ+EÀÛP`ú;‰è¶3O£È÷ÃZ×FùéžDnÅE~û]6ªÑúI4ÃÆ+*ìÒÐE‡Ä«½ÆfrúÅNl;ŸÏ.5[_N×#Ö(Û~ec/'ÿR³Óº¹£ÀS¤Uåx¤­£‰zf_ ~ÆÜ f!ÜâØ$´”ø¶,ådK¨ŠJ¬Ñ#š—oƒm×_‹WE¤Ú=¢rÕÆ’kñ⪉éþäEkÑ~Ôü@î0Ì Áó|‡SÒ«¶9bU<ë¼U¸ÞßeËËb²áéI{ì×kUr}>`;—þíßBò}¤ŸÑÌ7KéuÓµˆø±s~Ã:íbse]¶çº·Òªl6]%ñÜG]. QG©uE;ÞÔ‰×Ì×*³nUEk¢hŠâ“Ï£òI=C!FÉ*É,FªìK½m}œÀhy©¤ Ь‹ÈÓøE:95³åõ›úZ YrýÐ?€Ãý\äB›pZ@U¨ñT ×sï—âR/Íàw ÅZ×"¢¢*.ôP ŒnVµ–DÜ:&=;QÖÚ—K*ÔTT]¨»5j"5¨ˆ›‘pèØåEsQU7*¦ànWµ —$DMÜÀBµ Ê—¸º6½¹^Ôrt*\ F5È–NdN`ÔMÛ”DMÀBµn M®d@$ÈÞ€%ÀFDµ¹€dLªÛlP+Õ¬ôÔ2:‚™“NÖÞ8\ý[\½¶À4؈ü1Yc,†:Ê–6 …ùÛMÛlÖÚª»TŒåû ‡ú¸ÿÈ…6à.´ €«Q⨮çß/Äþ¤_›Àî@€$Ë÷@þõqÿ9 mÀ]h–1Q-.ùavW£š—µí´ Ø mEcfågÊ©e²%€ÛãYW $µSºÑÄÜËì[^=STEEInK²)¤vuNkªl@6ž8!I*$dIdÌ®r"_¢êQËѤ‘HÙ»œÇ" …ë>+&!¼¡fË›"ZÚËnúßK4PF²M##bos܈ˆ,’9£I"{^ÅÜæ­Ñ~Ð)TVSku¢-nì™ÒüÜ÷7¬Š§ÆbQÚ–ÂŽr***®p>€—îüêãÿ rÛ€ºÐ2 ¦Žž%’W£›Õʪéêi:×ÒÆÔñ˜±_%÷"í΂yY†µìÄ)£dig5bºµUVÈ»@ÙaÕ­¤d™Ø²#SXÿjGJÑ~{·µ’ÆçýTr\ Â9®j9«vª]-ΜÀif‰•šW©ªcdŠ D’&=.ÜÊë*Ø$l¤Òzªjv$pILÙ]RÍkïk¢s]Ö'ú¿ÔÿP.b‹<úK-£Ž±°Ó,ŒŠY­º­•ÛQn¨Á_‡Râõ<š:HŸÉQIôK*¦Í {PaÔQat²6ž'?V×먮W*^÷û@Ýw.jEŽéc‰,ŽƒcQc€úFwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼Pwù%â€3¿É/þIx  ïòKÅg’^(;ü’ñ@ßä—ŠÎÿ$¼PcO\åÀ¡»¿ñqó§üÀrtÛ€ºÐ2®%HúÚÀÇ5®r¢¢»vÅY;(ª YcÏ#š­ßmŠ<vPOÍiÅj¥ì–¿´ ˜F.ÙRW±ÊõKe¾Ë~X£š'E+Qìz*9«¹P S0…ºª|V¾tM‘ª—°«0Èë$ŠfÍ,¢µ“D¾—z-ö*•¥—Y$óÍûÉ¥[¹Ý Јx|OðBaºÉuHüù®™¯›7GHÕØdUÏŽ]d°O õsDë9¨»Ó΀)h’6j©êÖ]ŽY•-ºÈ‰ô®L(Õ© uRAÝ´ùÑZžkÚö§îuMX¾13ošfA™9¶gÚz˜ÓÿàPÿWù¦ÜÖ@j9§ ¢ÔUâ3â+ÕΞdDU¿2"lD`Ë÷@þõqÿ9 mÀ]hCŽÍMVöÀÉ‘&…˱Û·æ¿2S tuZMU;QU¨Ö.müꩱêQQQè»Q@ÔéGúv«èo÷  ¾A'òWû@çᬞDp·Âõ‰F¶iÑ™µL[øViM+©h'¬“øB±^Çej*Y.»SìJìzXh™ˆü3³l{èQ­¶U_}Ó¤ e]t¸ÜT4“¤QMK¬W«QÊͻӥmdû@òĪj¨¥¢¤—}<cµ•‹*¹é¹«ÌÐ-ÄÉc¥´Õ|©UnÙr¢]¿fÅ¢î}òüOêEù¼ä€¿tà0ÿWù¦ÜÖ8šŠz†ÔH’DüÙ–þ ôæË{jŸè¨–É#ÃéÙ**=#DT]è8b8tôŠìšÖÙмËÄ s¦Ç¤£Z?ƒ¢d®f­j5É‘6[5·ý€[Hª0¬:ššŽ—•¶&£šÄbÚÛöì]¼ÀT¡Â¤|˜‹æ¦m‘¤i‹e¶×­¶"í)S£§ŠøD5 #t•¨×"lº¢¥ï`-ºŽeÒ8뉨m*Æ®¿û³^Ö:ùª˜äŽ<5+ {|/Ú5ý]è .†j)[+²WHÈZìÉWsnYÜûåøŸÔ‹óxȹrµW¡«j&²ò…ftº5›8 j*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{Sýûj*{SýûjjSuS¾Ö·Ø­<‹4 zìUßùê/Ýø ?ÕÇþ@ä)·u d ¸ö€€*Ôxªë¹÷Ëñ?©æð;0—÷Nú )>IÔoä°«d‹p$œ³ÅMlf±èÆf[frîDó˜¯CòF}.üÔ rýÐ?€Ãý\äB›pZ@U¨ñT ×sï—âR/Íàw a/îô(R|’¨ßÈYjÛ…Ô>…È•,bº;¥ÑU6ÚÞ}Àsø®/Qˆáõï ¨Xi¡Â–w9Šª÷¢«[uÝà¢ñ Â£–,:ÍPéß‘;š[[u“  {æÄ+ñšÊjzîG ­«–&½Ò9É™o}Ék&Ï8*±NH1\Fž­Ã‡Hö2™bEIR4Evg.Ô¾ÔKZÛ7œÕ¸~'QbRC U ]S^ìÎͳo6Í yaøž*ï«*j˜ø±5ÊúvĈ‘ø äV»ûvߤbxßÁß 2¶$ÿZfÀ°"µZ²äEUßtºn¶à.³ª ©Äé+ñ&¹”ôñÌʧĈ¬Í™,­M‹µ»>›R:ºØ¤Ô$h‰ÑÊÔEÞ»6-ÀìSp¯CòF}.üÔ rýÐ?€Ãý\äB›pZ@U¨ñT ×sï—âR/Íàw a/îô(R|’¨ßÈTËO¥He™[ÿíĈ®wЊ¨£Á SOUGËŸ#¥‰|uÑ3l€{Ç#†ÄÚj8[Yd•5vѱ‹±Iƒ6®¡+-ELŒkfJYì¶ä]›m·m‘v5Z;GU<Ò:J†GP¨³Á™c™Q-w'Ј‹k_œ I†ÀÚÙªÛ™$žÂäEÙ•·µ“›ÆPIª–:iRX!t·dK·b'FÕM nz’3éwæ X—îüêãÿ rÛ€ºÐ2¹. V¦Äi*ätpLsvÚÛþŽ=¦š:x–Y\Œcwª…-\‘¬?;Ql»,¨ {6^…V£ÅP7]Ͼ_‰ýH¿7ÜŒ‰x܉½Q@ñ¥{–$Ì—F¢*_r ºÆu›Ä±fñ¬gY¼@c:ÍâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ 5Œë·ˆ c:íâXλx€Ö3®Þ BË%ÕíDO8TH©HË¥·¯â X—îüêãÿ rÛ€ºÐ2¾!PÊZe‘ŠöÛ.T[*ßgù—ŽHéc}E$²k.ŒLÍDÈ‹{óíÝ`UËR§©žGÄôW*ªÝZ¨—EÚÏGç¦d¯¦‹Xç=3«Üˆ›¹­Í¼ †3^ì7 –¥GH–lm]Êå[ #Ñô–4}mudµ*—sÛ2±¿ò¢lD@-ÕVCƒQ@³¾I™±,Ž[»oû—¤ [‹5´sVUSMI VV¬¶»ÑwYŸÌœxÒ뢎ª‚¢‘³®X¤—.W/2-—Á_¤ M®c±Gáù’2•]Íe[[éëã–ºª‘äu3Zç9w-ÒûÓ¿H[=+†‚©ôè—’DDð:yöÛÌaÜæVMUˆK‘Ì|P¹ªœè¹ÀîÀ€<ÝOÜ®t,r¯:µä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €9%7g‹Ð@’›³Åè IMÙâô$¦ìñz’Svx½É)»<^‚ä”Ýž/ArJnÏ €JRÓ¢Ý ê €H·t¾ mù\{—é¦-½Þ’q±§Yþ’ž©½gúJTÞ—úJTÞ—úJTÞ—úJTÞ—úJTÞ—úJTÞ—úJTÞ—úJTÞ—úJTÞ—úJTÞ—úJVÞ—úJ`@󨧊ªC3s1ÛÒàr•S2šªjzxÙ©jåV½3g·Jïöâ•z´]L1Å™,å¶e_6Ûì¡ÁiiV•‘E–GµZíª¨6-!¤–³•74±¹²±½*Õ½€«Qˆ`xÅ >¦¦6¹ˆ®Õ¾UÌu·*]×øÚ%„g[Þ®+ª®ÿ @Ûé4O“ sØÕ~ªfJæµ.ªÔvÐ*ãuÔ¸-=- ñÏ4õ¹×V¢-ÕW£gH“TCG¥ÒIS+adÔhØÞõ²9Qʪ—P<ðʈª±Ì^X]ž5dh×&ç""¥ÓÍ~p5øf)E‹µ’ÌÆIoEb¯„ëªÚÉÏ0rhŸ5LR¢µí§†è¼Û^Ñ€rÝÐØ 7íqÿ9 g6Þ2që^Þ²q,íë'ÛÖN 3·¬œ@goY8€ÎÞ²q½dâ;zÉÄvõ“ˆ íë'ÛÖN 3·¬œ@”T]Ê‹ô(ž«Gcž¥ó6¡Ìι•¹oeÇâºvµõ¨ŠJVQÒ²*«[λÔ`<ŸIK#õ’SB÷õ*šÇšV1QªŠˆ­K%€È8éà‰ÎtpFÇ;zµˆŠ¼™`†tDš&J‰µíG~`BÆÔk²1¨åm®ˆ‰Í±Òá¸w&Áéb«§_ U6¢9[µWbÖ÷>ù~'õ"üÞrºt€@HH@ -Ý/€Ãý\ä:•ɱ8u¨ À ¬ À“¡8²t'N„àÉМY:€ 'BpdèN, À“¡8²t'!Ȉ™‘,¨¨€*Ôxªë¹÷Ëñ?©æð;1r*µQËÒÍ+¢ÒªM0 ÀKª•±>]w'gkìµ¶î{U¦QàR¦è+±ªÚ8úÉ©âO-us¿;¯Ó= JýÁ1<"²h⪯/•ŽTÚŠÕ·7ÑQéU~3[AK²C@е›97¶ûÕwðP5´ýÑpùå†GaõÑaõ3j!¯|h‘=÷·MÑ<à{bzsK‡cXK0ÚêºÊv5èÊv#³¢¥Õ|Ȉ©{ôµÑì~“Hð˜ñ4{XåV¹’%œÇ&ôP5xÞšÒÐb³`ðÑVÕÏ*ùßMdª›×ŸeÑ@Ñè¶•ü ÜöŸÄÝSZ²Vº çÌ몭¶¹wl¬ŸHéàÒzLÐʳÕ@³6D¶DD¾ÅÛ{ìYK§Ôu¸¿Át¸}l³²­Ôò«YvDˆ¶ÎåOöªß€ˆiÔxUb²»Ä £lúŽXøÓV®½®›n©ç¦9¦µ˜f˜RáT¸]UTZ§>FEß6͋ݭK-Ô pb?º,ÔΪ¯l‰@×­;ÜÞNÔØ·¶üÛ@­ß/ ×k9 wÁºíG¿Ùgã{ÓJL'n”5uu2@“BÚf#õ—]É·¡oºÈ½ÒZM&¡’¢š)a|2,RÃ*YÌrB¢¶&÷H££Zªô‘ÔNr@×''TÛµS~m€R›ºm #©’,'´s::—24Ë"ÙVöÛÌ]G[ }”îWC;#Öº*]à´³Nâ«Áñš<2ž½¼Ÿö_DÛFÙɳ2-ÒûRàoáÆêpíÂ%f]ŠOQNË¥;QÊ‹‘UÊ«²ý W^èxzhûñŽEW’ „§©…ZÔ|.é]»P …V•ÑScÔ¸BE$²TÀµ +-’8Ñní·ÜœÀTÁ4ݘí\M¥ÁqI;ÜÈ뉫º^÷Û³p4ÛŸÑ×ÍGnYQ#iéÕRö{Ö×ûêž‘E¢Ôx} O*Å1Û‘‘ÂÔY&rxÎ]ȉp"“Mè*ðù~'õ"üÞr;ëf×ÇI+é!¦‘²N𪎲*ý¨²ªŸÑ­*Æë)°ZœRŸ5Nr>ÛЗP5µÚ!‹Ów?Á°ÆÓM%RWë¦l ™aG_ŸÍ³íoถ ./¢‚i0Ú¸ÞúJôeѪæÙZõNgœ £N† |'ÑœjzˆæF¬±TªS[6Çï²Y™ê¨pÚÖwMÆ+ßI+ie¢k#™[à¹|¢/اsl:· ѹië©e¦•jår2VÙl¶²ý×OMŒ`Z{ŠVÓ`Óâ0b°5±I¢$nDMUÜ—EûÔüZÆ_ܦ:ÃåJÚzõiÜ–{šŠ»“Ÿ}ÀÚQ·Åû¡áXÕFUCI+⼨—j¢.×[uÕvtØ ú †×ÐTéê)$§ZŒAï…Ò6Èöí²§JÄÖàzIˆá5,­Â1jœQ•YäžY•bXïdHÙ{9vó&Ä@:Í"§Äðý5Â1êl&«‚7BöS"+ÑË}÷úÉÁ@õø&º«ºEe[é'Š’£ Õk•¾ 9Q6_¥æ× Ò?Šk¡_ϬZ«ò릣&lÙ³tÓ&[tÜ:©)¥}%>©Zœ¾9R×é×@°êÚ ½ ue$°6|EÒD²6ÙÚ·Úž`&«­wuZA´²­#(\ÇNðÞÅ^ hðüCô¶øtíž®¦GA³l©}Š(i¢ÔóRh¶ñ:)c¦c^Ç¥œÕ¶ÔP>|Ü3H0ÝÇôf<¦¥j%|±U2Ú·3g²lDÿ_Æ(±Æ&ŽDúR£ Љ­ª¥¡z±úÔMβ¢¢nçæP=´SEj߀i‰ÑIJ•³ªFvdñ|GºËm¾`+÷6¦ª¬v!ŽVů– vÐBÝŠŽHÛá"/žÍN a€áØŒ]G6ƒâ¸=Ü®Ä «TÔ[þD¾ßþÜÀo»¥±ìÀ¨ë‘ªæPbÏ.ÍÌETUüP(£¯Iðm)è¤Ä¡¦Ì’,¯Êä[9¨»ü`4ˆb €i†;ˆQÉCð”j±SIã£SÝ ´ Ô4ؾÑh¦Ü"x)èÉä­UEÌØ¨­^›so¸Ô‡Á»¦WW¦UWKˆÅlšEl~-ÕËÍk( ÑœNº-6¤u<.!PŽ¥|‰•²Ù\©eèÝÄ ñQc˜ôú7‡OÔáñàÏkê*&²5ÙQ̶ûÛñó´Ã0¬B-%Ó‰(¦luq¢S½[²_|^ i›†i-s|’š’¾'2©Ë[O:Æ®]‰m©ÿØ z9‚WSi7R˜Me%-VjvÔ=dzªÿµÎU_ eí}€y²9ðNâ³R×ÓIL|-†FÙÊç¿b[ñ¿ÁiŸE‚PÒÉãÃOô£Q ÀrÝÐ?€Ãý\ä>—ru»€È &•C$Ò-™UÎ^„Dº€ŠVO &nǵÕµ®‹¸ ÀñuT-¬e"»öÒ1Ïj[{QQoÚ€{¸×¢nzʺ‰lŒTjª¹Ulˆ‰¾àX‚fTBÉXŽF½.ˆæ«W‚í@=Œž"ê»À€V£ÅP7]Ͼ_‰ýH¿7Ü€ $¿Âáî£}UM+\är¾šLŽÙÍ~…<£ÀðذúòA-‘VꪫuU^uP.çSM e<”õ¶X¥jµìr]‹½ŠZXh©b¥¦#†&£XÄÜÔMÈŽ-†ÅŒaU8tî{#¨Xç3z"ôÂðè°œ2›ÎttѤms÷ª'HÀk0ʪ*¤z^F¢9Žj_e¹” n%WñFª»\¼¢4—+ò¦Ë=Q6nÜŠ™k§Æa¡‚­iâ’Z÷µˆ®EÍm—éî&™pJ¶«•Ö¦z+—zø+´ D2WaØfZê×KÖä§V5Ö¹*¦Û¦Íë´ k1Z©+kÙ MT+Hý\,†‘elŽDºç[.õÙm€g+ªk±œ-í{¨å–…î’ÌLÌÚÛ¢_rߤ Õ³EK‰ÃY_•hæÈÚ§1/eDTºnUÛo8xu|Ë‹>‘jª*a}2ÊŽ©ƒVæ¹Û6%ÑQ@×Ç’`=’¦H¯PÆÙ­jÙ|/ joüë#k›Zç«ÕÊõÞï:1“ÄP=Wx€*Ôxªë¹÷Ëñ?©æð;!@¯Ëv²^Þg5Ëø­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rµìópOhV½žn íÊ׳ÍÁ= 9Zöy¸'´+^Ï7ö€åkÙæàžÐ­{<ÜÚ•¯g›‚{@rÅç§™>ÄöalŒGµnŠ›ÈCº]3*ôe½U¨ê¸ÕÝíTº¢§Ú‡áò¥\uuukS$MVÄš´b6ûÖɽTôzI)*([ˆÈÊ9œçj’&ݪ«z¥öØ ’P516Vëì§Ôd˳}ïp=ª åT“S«²¤±¹™­{]-p5Ñ`Oo%†jù&¤¤s].U[»3“z ’áu ªš¢‚½Ô«P¨éX±6DWZÙ’û”u ¾#OZéœçá²¢xWTUrùöZ§eDuÍZ‡µjçdèäj~ÍÍD·Ó¸ àÂån!Ëêk]Q:±/ìÑ­DU¾ÄMÀc Èè0úNPåJ[#]•.û_bôoÛ ™µòNµNt.­l Ô³U7­üàX2xŠªïZ@Ýw>ù~'õ"üÞr~íßB…%¹$;?ØŸ¶`````@`ddd`````````````````````````````````````N€<(~HÏ¥5À·tà0ÿWù¥Ü€]nà2 dñUÞ µ*ºî}òüOêEù¼ä %ýÓ¾… O’Cõù52:*idnö±ÊŸb¯Ãë$–$šZøfý–wEÝ—éÚ ZßZÉ‘&mâs™d~ËÙ›l´’TK±5V¥Û|ÞTDé_0ü+`šY[,Z‹köÙÍEÜ¿@içåçÕÈĽ‘Û*ùþ€*³§‘YfLŒ{õi"³ÁG^Ö¿Ò³«¡lU2.|´ª©&΄¾Î yËŠE‹G4HÒEk{5yÀAŠÁ<±5­—,ß»‘Yf½m{"ñµ&,ˆÅå *¢NèÖ\ž|+"*ðÕF)<®c›+’;kÆ]±ß¥@•Ä#å‹JÖJù©›+nˆŠ›W Jª¶R£È÷¾E³Ä»œ»öJ»TÃ$š$lˆôQYáF·Mè¿H+i¨u³É&V5\çJ‰™Ï`*3|ؤ5’ÄÇD÷9’2ʶËeüTdÄÙ¬Xß Dnʯj>;gDßm»ÀÍØ„ Žæ\³µ\ÕDæDºªý€M%jU¦fÃ3­G5Ò2Èä^ Z¯CòF}.üÔ rÝЀÃý\äžC‰USTÀʪHÙ ò¤MsfÌæªÞ×Kn[uq:¹jç‚‚‰“¶™ÈÉd–l‰š×ÊÝ‹{\ ¥Äªä­š–‚“­5’gÉ6DG*_*l[­€Tâ• Y%-$sI hù–I²"_sSfÕÙôæ¸êÏ"’µŽsi5mn]è«eºß™ôªÄê©’Ž¤‰*ê•ÈŒ|öcr¦ß ÛW¡,êwNøê˜[ »QX×çDûv[§žz-+ÜÚˆU%‹*ªfsvÙzQS`ø±HëêÖ=”gr"ªf‘RꊜùSeºT 1bÕL}3«h[OS‘‘¹²æV¹RíG%¶_Íp,Ób÷×·U—‘ʱøÞ5šŽ¿˜ ðê¾_‡SÕäÕë£Gå½í~kd dñUÞ µ*ºî}òüOêEù¼ä %ýÓ¾… O’Cõùªc¤¥–6øÎc‘>•@5ôpÎÊMCðöÂäƒ&±Õ̶·0²’fÅ…µZ—¦¶·ÂÝû5oÛµ@®ì>¦L>ZGDˆ¬eÙì’xjëlÚ›À‡:J–EB°K"1<ù•È‹}÷[X Öà5)C:aPÓäMc*ꙹµ™·ýcSK[“‚öÕª¹’+Ñ.ÔK*}€YŠšVÖK+š™]LÈÓoû“5ÿ4Ê*)ÙM…Æ­LÔîE’˹2ª}»TGZê9hV£e™ÎYu‰àµ_}Ûîφ/,¨s¨–ª:‡#®“«-±QRûwz–ñWÔÊ­Dd‰3m÷%”t3-E=L,Ir,wµÑÉm‹ÒI(jf¥ªr±­š¢f=#Ìž Z­M«ÓdP/bî« š*#žßWu÷ :êŠèçXO’ —z:Î[YvslÆŽ†©µ”³Kµcc’W>lê÷*oúœ>Í«©ðRHésE _bÝs*}›ìß§ž‡ªBêzebZH³¯½-¹-Ì̽Éô»óP,ÊwDG;GcF;+–¥ˆŽè[8–ÐáJÚ\>¥§š¡Ò#-·ª.ÿ>Ð7QE_†ÖÕ­=j ª—ZÕI‘‹•ûÓeö’E_‡ârÓQ¶®Ç6M“#7ÙQo½6áY„¹1Ik]„Áˆ²¡ŒÌǹ¨èœÔ¶ÌÛzŠeZ)~†hUª®’µ©¯ÍÍóe%\X5=fÜKÆÎÕ™·o‚—vû&Ëù€õâÄèhi tM™Vgk3Mu†%º¢]|knlœÀiép‰]ÖáóÚ%©–ejµodrªµv}€`ê|KätÕTŒ§ŽšVI,©29$V&Äj&ÔºíÚò|J’³m=+&޵ùÙ*ÊHÕZTro]ÜÀ^Á饣Áé)¦DI"‰äEº]¸2xŠªïZ@Ýw>ù~'õ"üÞrþíßB…'É!úü€õ` €6` €6ر²1Ìr"µÉeN” Šž4ŠÚÆ&äDÓ`€<(~Hߥߚ`[ºð«üÇÒø¨Öî ÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@…DT²]!š4FG;Q‰¹˪''%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWê%O—Õ~ 2TùxýWêWR»5ìû#ý@õŠ6Å1·³z@ÌKºDì¦ÑØdz9S•Æ–cËþîdЧčÙDÊ·NÔîF£ù®«m½z@¿EY l+,Õˆì·s·ó¥ÓjyÀñ¨Æ¨)gt3Hö«\sõNÈÕ^eu¬€bú©›¤<•ûD²e²xÙí~yaØ«Y£Ô•¸„þ­²®[«Ýu؈›×g0ŒÐ:šJrµ‘9"9Žk˜«±.Š—M軫©™^ÊJ‰Pö+ÚË.Ö§ŸpSÕCT’,ΑÈèܶTð“z¯§Ç"¬–¶™+]dc–'YlÛÝURÉ·™@òÂt‚–j&ÔÔ9j&cZç¬jsÕ7fµ¯æíf/EC.ªy]™ÈÈÜü­é["Ù>-±ì–6É‘Ìz"µÈ»È dñUÞ µ*ºî}òüOêEù¼ä%Ý¢*m†I¤HÙÊãLËù€ùjDé0idXÜ‘TâL’69¿ìW¦Ûyö¨¢o’ū竡Äàš®fL×=Œ¡Š-ŠÄÜç-•U6Ý ²#¾2Fë-¾ µíϘ mé°¼¶f¹ ¦Ök—*®¯5Ñ©ÑÌ ‰¸Õ^)-5ÝO-`l¶TI$EUÙ}öÙÄ ±Í+èþ1=ŽÖC<^ –ú¶·#¶}.r¼ÀéM‚Ó1éiÝcþ³—2þ€©'Ž*üj–G«fšWKÃn­6§70žÅM ÂÚ[£©ÖÖ[¢ç@v£Ç1UâUÕdtnc¬‘¨Û*]Z»S£Îó ‚l.š w¾HY#"YÊžp-Œž"ê»À€V£ÅP7]Ͼ_‰ýH¿7܀廠‡ú¸ÿÈ}6à.´ ¾Ðh €u½îÀ_œc'ˆ z®ð U¨ñT ×sï—âR/Íàw BÆÖwH¢¢Ä+i_…b22‚]\óÄÆ¹Œó®ÛØ¢“¤¬Ã⯂fºžf#Ù"ìK/Ó¸ 4k±Õe¯šû-ôø®¨²2íKªfM‰Ò&‰dÕ¤WÚùs%íÓ`0uC]’À­™X‹fµÉµQ7_˜ ˜6#Q]„Ç[_F¸|®ÍžÈŽÉeçTÙçóf‰Ñë#\Ë_2**q­}{¡Âç«¢…+dŠæEˆ™×¢üÀgCXùðØjª¡ä’4|‘=躵^e]ÀXl±¹™Úö«-|È»8ø•íbHÅs’índº§HÜ6TC"¹+­Þr-¾'_KªÖ3Y¿.d¿ž&ÈØÝ#Z÷x­UDUú YŠˆ®DWnE]ª2hånhÞ×¶ö»Vè'‰Ò:6È×=¾3QȪŸJ_Ä"ÂðºœBf«£¦‰Ò9­Þ¨‰¹Ôè®Wi«jð¶ÐÓ¹­tå &±÷º&ëyú@ßG4r·4okÛ{]«t¯ŠíMcnýð“Âú:@«‹ãX% Ö×ʱÂk.ºªªÙÀk±ý&L»‚:vÔ7ªÔgI-“vÞ{ïu®cãs¢sdËtð\‹µ9€¡‚bu8ŽÊñ äG¹'ÈŽ²"ì[§HåŽVgí{W«tG.’,zmŽò[¤´«>¿>ë_e­æxÙcrª5èªÝö[Ø YQ ŒW²V9­Þär*'Ú¬Ñ#ë#r®ç]-Ä Ñn—@$ã»§Õr-å93êêc\·µ÷ËRª*oM›À¼Šˆ¨—K¯02xŠªïZ@Ýw>ù~'õ"üÞròjl7ÅôK°ü&¢’**uuO\\Þ*'Ú¶•áòáµZ/£lk*hcc¿gQ*Åò'YSvýÞp%ØN#„h†•CPÊh)%‰$†– j@ª»S¥@ö\އ¹uf*Ýl•µØ{yd‘\®K¢¢[r"lO° EfÒAO¡õ1:fTânk*æI]šFª6é{ìØ¶ÙÌç ƒÓM(Âèè¨ÓÎØ³*¢.TéúWˆì7ªÇ;˜aQ϶*©%ä³É‘µ(Ž_ÿ_€ihét‹Bq/ ¦«Žµ©<V)$DñQ×Ü¶ÝæÆšjhž”áìÁ™…â4ôè•LŠE|oèVí[}Mb6®A°º÷«pºˆ‘fnek^äD²*ÿëx˜z2†}5Âpç«°¨)\øÚŽW67«v¢/j¥Â©è4CFqèFâ2Õ±¯ŸXåUmÖÍß±¬ãtëU‚VÓ¶©)],/bN®²Fª–½Àùæ‰ÃcU`ÑEŒ6ŽWSÖS̯lí²®Ô¾õ¶ÿÁöî{„`X†Obl¸¿,s–YgTz>û×ÛÓ¸ :ÒPb˜>•bøÄËð½-S’:Uk¢²ø(Ô¿Nϰ ¸ä¯ÄbÐWâÒ¹œ¡-Põz±UªEU]–ºo_8G#ðLCM(ð9))©RHZÇ+’9-e·.î`*èæ _,X+†ÓÐÓ9“5ÒÕòåY*QWÂkÚ©¿~À;ý;£‚¯C1-|yõ0:VmT³š›`},XgrÜ%Ôu4xœð®!+·r*.ÕèÜœÛát´XtøpìÙhj(õ12E{QÉ|«µWnî y÷2À(êàv3R²ÉSI[,tè²*6$ç³wmÌ·çuºH€ÑÕ=вGVÆ#³-‘®E¾ÍÜÛÀ§¦˜FNº/†aÍHh߈+- жG+sYn«Î¶AO‚wEǰ¼=‹ Áí‘!G*¢;ÁéúWˆ|¨Ç;—ÃIISr·|‰ ÏÊÚ‹±Wñûê{TP:,J’ŸL.®šf¶®H¯bºÊˆ­UUÙ±vÏéÅN'IÝeÁát•Ÿ9­ÞÄð³9<舖'%%r:y0)dÉW;[2»öŽrß:=~”Dú>/èþŒÕDZéG‡Ðá•tO†¢žšµeI‘Sc쩵v¦ßh*xêkcô erº›“_ü–íEúîüû+Zj5©dD²'@?uïô4»mûxÿÈ$tpPãS©£HÝ3¤ŽW&ù%ü. W††¿§«ž:Zeªå/FÕ>w6HU®ØˆˆÝˆˆ›¯¶àv Í‘¹íšÛmÒ€†7RÒ9JçYŠœé¶üêúº8+©%¥ª‰²Á3U’1Ûœ‹½Öá:'àsº£ â§•É•^Š®[t"ª­€Áš£ÑâÉŠ³ •m~±$mÒÎéµí°v‡hþ%ˆü!Y…SÍStUz¢øKçD[/Ú«L4z§Ç´yÑÑ6zi]Ê‘råkÉeEÞ–EÜû Ñü+¦}>E7ÕFŽjóïކžd†Y#G>WF½UT²þg6 ‡TεÒ¢ÈïQÎno¥l¿hÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@@d@·tà0ÿWùˆåÔ”MjÕTÇ;ÅWº×ÍF)GIªI$r¬ÉxÛõrtÙvÐ.ï<ªªb£¦’¢weŠ4»kÙõEº\ÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@rÝÐ?€Ãý\äã ÿOý\?Ü€zÏ ÎÒøUµ’Æ‹HçY­nÄG¥Ûµ7/Nð/bõ3QG\o´1ÌÔ¨m¯x×eþÅTP)ÖâÕ4òb•ª:(Ù©±Ó.ÕU_5ÐG>» ¬¡JŠçUÇW.¦F¾6·#•Q[nm›”‡©v ŠT:OÚÁ,íÙSÁFø¼ÙQHé()å‘nçBǹzW**ÍWI_]¢•8“ëlÉšª”Ú¶äk3Y6ï¿=îÚijªñ_ƒàªu,pÓ6W½ŒkœårÙ3lDضU5Du0Ô¹5$î…Ò5-žÈŠ‹ne²° dñUÞ µ*ºî}òüOêEù¼ä€¸ À@$@ºt ‹ -Ýø ?ÕÇþ@રù1"•:„d“÷hû«Vé΀_m ñ(«Ÿ*«Ù­FÙuE¿›và#©¢ƒ••¯DdÌs¼ïÙ¹¤ øn‹£©E\вT±]P·ð³;jý©³€áS­LV׺©)¶ÂÝR2ε³:ÛÖÀxË€ÊöÖSLj>*JÇ9ï‰"Er+·ÙÜÈÒž$‚ž(o™#cYuK^Éoð¢M‘ôRáìÄdeÕU±$MW2ë{fçmù€¹U†Èú¦UÒUrj„Tç,hö½»Òè½ Î®BÚÍc¥’G¬’Èô²½Ë½@´Éâ(«¼@j4xî‘6¯C+1.i –™³1Y$NÖ5ÕEܶ_Ä tõuÓºÊ7Lõ§eR6+ø(å{‘W‚ O dnu5UC¤vV²š‘Ý<Û¾Ð+E¤ø\˜4¸³¥|Tð9c•$ZøÞ‹lŠÝù®©³Î KL[M€Öâ1a˜ƒL‰••Ϋ¹~¯Jó} {.3Ê+0ÚÕÑ-L²3“ËKe›+/e¾Ö¢oEç:½.Ãi*'̪’:gd¨¨Š>(䨖çèçßÒJ 6¦žšMt³UFé d,‹*%¶%¹ößèºç•á’aU8‹,1Òɪš)"VÊÇìDnM÷[¥ºn¬/f)­FÑÖRº+]µP,j·è¾ð0ÄñêL.¢*gÇQQS+U삚%‘ùS{¬›“Πy?Jp¦`¿ ºg6•²$OUb££z¹g7z**íA¤´Uø‡ Hj©çXÖXÛS£Ö±Êæß~ôóí¶˜bRaTxuC*…²¹»s1Un–ç¿@0Ý!¤Äª¥£lU4Õ11$Xj¡Xœ¬U¶dEÞ—Øz}/Âêj✡#žEŠ §Bä†g¥ü¿r®Å·M¶¬¦Ò…¡Æ±èjÒ¶­´õ-V2V]Lz¶ªªÛr^þ}ào'Ç©bÃ髠Ц²*¤E…)aYÈ©{Ù7%º@ÔcšD•z!W_…Í4ÃQ/G1Y$NÖ5ÕEܶ_ÄW¨«ÓX0†ËŠSÓGO¬^I\ïW¢#œåEð9÷\›Äépš'UÖH¬ªK5\ç9VÈÔDÚª«Ì:-$¤­|ñ$pTAµÔóÓ¹’«9•­ÿr_fÎp5_S‹­N#U>"çºIW+2Óµ¹ÕŒKoDm—o:µ®ÒŠB\?SWQWm‘ÐÓ@é•×Û³™-·éN*·NpgÅñò§Ò½Ícª’Ú¨œ«l¯u¶-Õz/´ µ6)MU]YE¹&¢sRV¹¶ñ’è©Ò–çpÌRŸ Žº—:Á*®E{ræDUKÛ£fÀ*·Hð÷`uÊ,œ’dG®EÍà9Zë'>ÔÝ&£¢¨e;ië*§tI2ÇKÈæ1w9ÖÝ{/Ÿ`Wi S5ÑTÍ=S5‘SC ¾Uj"*ª·™é¼ ¥xDxU>&ê‡%-Dº”~E»_·c“z*eTö^ÜF•*OQUUµ,oÙÏeæP-€[ºð«üÇÒî@.µQSb*ˆªŠ¨‹mÞ`$Éâ(«¼@j·ÑÝ ÀrÔÕµ²GSM*β6G¢½<µÑÈ»¤ ÕuEf•âñÆ×±*0VÂÉU«—:¹ékôí@5“NüWG0Í‚†ª,B'Ó6v¾Ím:F¨®r½S*§ƒ²Ë¶è½VPºpê§“UÏ##ò.K±ŠÕj®ë«‘‹`2Ñ ¦cs2ªZÌ"h`|TIds³5WzdF%À½¥5•tØŽ §¬¥Ã%lœ¢z8•ïΈ™±®V¢íÚ‰Ì<´uk¡ºE&­W͉2H›;e{sD¨åéØŸgØMO ©Ýºeé°èZ’e\ª¨÷]/ºàxé=m\¥O[K†JÉièâW¿X–ÈÕTEV¢í]‰Ì† :¹Ú=[+hë$u>:ÚÍLéy¦‰¹í[mû,çÄ#Ò=Åáà ª’Fž 韹o{72%ÖÉÍæSTÜ[ѪÊ8ª sÌsà{2~ÉSj*%¶ì¾à(ÃRìÅðJš*Ékfš¥iÒ*w=µ)*ªµQÈ™S}–ê–°è°Êš- ÑÈfc¤ä˜L±I*"«Qé«K_ìP<ÝOIÊt¥1jJ‡ÑOYØâ{•STÏ ¹Rû7¦ë{Dêëg©®…j*«0زrZšÈV9«|ÍÚˆ®DÙá*só…|Ÿi‹ñJ¸§}U `Ib…Òjž×ªÙQ¨ªˆ¨»íÌžª†®£F±J®G;[ˆcTC£\ú½di™[½/•Wo6ð7ø”2;MðI[Ý ªG=ªº6×^kŽ›2wQa¯‚–Z§E‰A#£‰™•ZÕUP)µË¤ZR•t1O5>4žXi$T³Qˆ«k]@Óà¸uÓa¸N)><ÚÊi#E¤Êõ…¯bìr9—&ËÞû€è°Jy#Å´™òBö¤µmV9ÌTΚ¦¦Î”½÷£¤ž¾ƒFôj–g×PкÉW-4t¬z"dj¦UV¢íÛn`Î}ÀtëðõÚ·ê¾ Ë¬Ê¹skQm}×ó:YOPúj È`’¡´ÑÔËmÌç1.‹dçT½íæ­ß é6%Gí£££’'Í,.‹X÷¹ªDr"­‘ª«³œ : hÛ4Oü¢uÊö«VË+­±|ÀE 7NñYÝÒ7QÓ5²+VʨçÝx¡äU ܪ®)eIÝ4ª‘$kâ/º×Ý´ š`Ú¼:º:ڪɊӮësHå¼nû.ð:º8°ú z(RÑÓÆØÙô"Xyæ¥Ð¼cGÖ‚±øŠÉS–6S=Zæ9êäz:Ùmeé¸LuôtòEù~'õ"üÞrZ§ ¥«z>h‘^ŸîE²ñ@2¦¡§£EH#F_zó¯Úºí@–2í—ÎÚÀ.À,e—Ϊ\Pó|àM€X·@›T`#/œ T¸ yÀ[Îeó62ùÀ[ÎÛ` ° |àj¤ÁSňU×I4TÎWÓRêÚÖFåKfUM®[^×ÝulÀ,ÀF_8€;ºti.ŒÄŤmUêãý›Õ7;nÐ88 ª£Ä–®–•*#šFøÛ"1X­Ü©~kg‡Ãðù¿ácš¢ZÇH¬I,Ü®vÕ¿›  ×8c'ˆ z®ð U¨ñT ×sï—âR/Íàw €H .€H Ð .€H\ ‹¢ €9nèÀaþ®?òK¹ºÝÀdÉâ(«¼@jI"|jæîÌÜÈ—O:çñ«\C¥gíV]J;VíZÉÔÏl¹¼×,GI°¼*«’ÕNýv\îŽ_+˜Þ—#QlŸHoÆ#ƒ®ÖV5ðÓÒÇ7'm;³³2»ÂUM÷¶äÚ–A€cµu˜]!SŽ=²UÔÄÇÃ5¤h®W~Î5F¥î‰l×TKÑWé>†Ö-DïYÑÎdP¾Ub.åvT\·ó°¨ªŠ––Z©–(˜¯{¬»‰u[o]K¥x5edT°U«Ÿ?î\±=¬•m{5ê™UmÌŠâQ„auN¦ªªVÈÄGI–7½±"îWª"£SéÒ¿H°Ì2FGWS•òG­cÇ=^Û¢x(Ô\ËuM‰´*ÂiØåé3¡lÍ…°=Ò+{.TKómèçËã®­‰é^ŽŽL¿´lOV1Ulˆ÷ZÌ[ó-€´êôHNúèÛ(µÎ§X×2xJšÌû­dµ¾Ð0 Ò|+•c¦šEðD{à‘ŒsS{‘ÎDEO´¤¸f+RQË+Üæ«˜çSÈÖ=zµÊˆŠŸBåCŽÒÁ£±bUø”SF繺öBæ$‹QÖoUÙksØ 4ZA‡bTMËÿ —™’Fæ>4µöµÈŠ›£h Ó ªx!†±]ÊU š§¤or¥Ñ¨õL¹¼×óoÖ¯J0ª:×ÑË<‹4j‰&ª $lj»‘ÊÔTEÛμN½˜n=dˆª‘1]djºëm›hîiSG¾Æ1y¥IÌí–›VØÞ»rƈÔWïDK^öyAáø“'u4ν:^häÌ|ik¢«\ˆ c†é‹Ïª ÓþÉ%ÎØœŒ²ÚÉ™R×Ú›7 q*¤Ãô‚Ÿ ’9•ÒÄéöB÷eÝ•+V÷ºÝw%¶ï…>—ACŠãPb•OVÓU剱Àç¬qêÚª«•Éu]ªÞ£ÕøO'Ä!lUªõk5jþPÜ™“+“cm¾ë¼ bÒŒ*jô¢Ši"ȱ#›‹tÞÜöË} a¥õõ8fTÕÑ˪™Žë"Úïj.ÿ2¨ÛŠQ®!5fEžÒIRËhÚ»³;r/š÷¶Ð)Qé^_:ÁMTç=Xç²ð½©+[½Xªž¼×ñì5¸;1u©NE#Z­•«|ËdÙkÝUQ-`=hñZJúººZysËE"G;rªer¥Ñ.»ötpËw@þõqÿ8ú]ÈÖî ÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@ÂF«£sSbª*  5ÿ†dv³ãLoB Ky'§…›dt­r'B"ÝWÿ] o×jÊâs·Ó$Æ+ôÃë(›NùÚÅrC#\®LÖÜŠ‹¿¥Æ*È´—K°ê¼;4”8\r¹õ9U¬|j5Û¦Û%ÕmæW Vî]Ž¢F¨ç¾µm—j®±Àmá$ÓJ4‘Šæ;s]tØ·‘»Öà4óËRà“5ËŽ,®ÌíÏͲû®àÿKq:¬+ †ZiÉSST¬yù¦Ç´Ï — zÌÊgZ™QŠˆÌèˆÖ*ªoº*ÛÌ=†ÒR.Œi u%Lsåu¡›^’fkš¹Êж[ßœ‚ NGtŸv/*ÓrÙ#šžg1Ê’±#FåEDÞŠ‹³ÎŠwfÓLfDEÊì2Ql©}²§¦k“B4A2¹+in–]ž0 R‹Gqìn,^U¦’®­'‚G1Ê“F¬j"5Q6ª**X 摪;Eñ5MËG/ö(—Â4¸¶ £˜Eu®ŠjY­Q`kîvË"[ŸžàyT2*[¥Åñê¼.:ê‡Hƶ9•9ˆ›¬rÝ6µQ›pjZ(iô»†5’X©°y’6ÎÞÄE^…T¸àeû¤VI—v #­ÿò;eÀж½Ìqæ¶5EuEZÙµj»lÖD‡ã|«TÕZ€%ðU|bÝ6y€§ƒbPÅX˜M"ì_är,™ãr¾‘­DDj½3"¢ª"/…°]Ä™1M„á’â¸RÀ峨¹è‘¨™S=’軑jX ~×ShæŒbS1ëIC[3êlÅUéTßdUü@ض¦,_Å1L=VZ&a+N鑪’[«¬—ßdçóZX²w9ÑÆ5Š™f¡["nðÛp3Ʊ*l#«ªÁ1'|%$ÍJŒ-Ñ+ÛR퉱-v®_÷"ÛfÐ:ìNëƒÕìÛ¨~ÍÿíP8öµðh¶‡âÆ÷RÐ,RT¢5UXÕ‰ZTßfª¥ú· LXÆ“Öbxs–Z(°¥§’v¢£d‘\®F¢®û'æÏBblZ„1“þŠ­µ¶ª])cuPášm…ÖÖ?SLê9áIU.ustܪˆXið–•9Yµõ–º¦ôÔ·Ú j0f»’hÚï9/t]Ÿ²]àzÑâTøv5MM£Ø“ªà««T¨Ã_ªÀŽUW½®²+j¢ìÛ° Æž#×C«‰wfŠÞ öëÌ¢*:º|'H4aȯħ†Y᪵–µ¯EÚ«ÖEðU9’Ày`‰†â5¸CdÒ*éªè—ù~'õ"üÞr–îüêãÿ qô» ­Ü@Œž"ê»À€V£ÅP7]Ͼ_‰ýH¿7Ü€ Sd•ˆõ™ÍͶȉdùsëœë*lr%”  k1ÎüN–š*§ÀÉ#{œ¬FÝmkoEé¡ðͪÜíl¨‰™½)e^b˜£Rxã––¢Ê쌑íLª½î—œR6:x™ Ò$-´’1·kÜûoÀL&G?¥{Üç¹Ñ5UUnª¶ð¤-vWäM™-á^öµºnM‰²9ŸtóÎèÑM[Qr_nÛ®ÿ2šb0;’«ÏmRªFäM›¯´ ]fž%kóAHí›Ño»€Ñb ®ð¢†T‰ZŽlŽDF»Ì›n§*—-%¬Hõ—¶Ë^Àx?°Ë&IW6£*7kŸÐ›|àd˜‹M,õËNØ·¤ˆ—_¢Ê·)ñ&M2Àø&‚Tftd¨ˆ®oJYT$ÊõE† µjÛëÔFߣ~ð&YQ¸¥<:É_Õˆ™VÖÚ¼÷¿Ã°j–d§¨XXål’dK1Qm·oäŽ#<“b´ô¨ÚRÆç.¥Ùs®Ë-î›àZ“Š'H†ycrË+v±S=ÖÜö:ŒN hÙ%tìWF‘¥óZÞÐ1“lr²M;§|i"FÔK¢^ÛvÛ`_À°¬Í§©tq­¥vOÝ/:.Þo5ÀµQX°åÉK<ù’÷‰ÈŸJªV£µ=$ÔÐI+j%FîDTßtÚ»öíB;F9Ó1VŸ:ÄäL­ð·ý `Ìf«©°Häk'sS#•wsÞËÓ`3›duRRÇO<ÓFˆålmMËÏuP,RTÇYLÉâ¾Wó9,©æP=€Ëw@þõqÿ8ú]ÈÖî ÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@]îô3þåÞ{'âÄ ^!DÚÌV‘&d…±É™m±e€¨ê‚jp¶ÄëÀät/µ’V݉~žeÖ’]Q­Šš¹³Ç²º¥\­j¯6Õü€«U3­Ÿ k§dìtî•UÞ›&ë}À[bÍFúè–iVy$Ocn×fMʼÖós ñaT±ÈÕc›QÍT²¢AÔïøk‘"~ÁÒrÅólµ½+(Í|˜uebºšy›<‰,n‰™¹‘£pÒ’¢Š ‘Ñ:GA+Ÿ+#LÊÔr.äçµÀôcgž«›“JÆKLÖǶWYÍÓ·p 65¥c˜¬saj**YQlZ§IKŒ¶©`–XŸO«¼L̨¨ëíD¼ÀÕrê)œÙªÜý[ZªäEµ—gæ€yºš²¯ ©‰w1²1Ôé7ƒ#‘,ª—ßô*bŠwÕkYO\Ù[“5B½Q/½êœ'ÄSÇ#ÚÛ+\–TÚ DñHìn’Tc–6Å"9֨ж°’–tѪ¨u/Ö¹e³-µnåTÒÅ/ÃrêÝ‘”ïkmˆ·nÀ+5g¢‚®i&•òI#¢sv¹µ.¼ÛùÀô†ŽX+0Æ9ªæÁLö=è›|ba"Ãë.­Ú¾JÏm—Í{M)¦øRüòI2±¹v¹v[éfå U&eZÓ¤ ÈÚ{¥ßÏ™Sj~@yGMQD®§‘_V±ìDÌ칷Ͻ¶èd©Äß"G##–‹"9ÍT²«·} UVÏ>”“2dÈǽ[à5¨ªävåÜúxž˜Íl®b£ÈÑ®Tض½ìŠHpäd¬s¬zÙÉmîU`7º…jPh¼S¬k"%di•ÜÎ:¦S’b3—Í!Ó1>Bõÿæ§°·ƒõwÃYM7ø{ýj{ à}N~;Çó{ýj{wOê|wæ÷úÔöîpþ§Çxþo­O`àNçê|wæ÷úÔöîpþ§Çxþo­O`àNçê|wæ÷úÔöîpþ§Çxþo­O`àNçê|wæ÷úÔöîpþ§Çxþo­O`àNç껄é31Zä¥m#¢Uc™dEÝö×kLgš*£(o U±“ÄP=Wx€*Ôxªë¹÷Ëñ?©æð;òu%;Ü®tLU_0Ò@Ç#›SrØ`*]-{yÀ­KBÚi$•ÒÉ4²Y$Š—²nD¶äÐ »ú6?ëbüœYo™Õ=¯‹0× Þɸé! y¢?Ç›ü§þHS{•ÅÎÇtcPÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@HÝ‹ýõ±~N,·Ìêž×Řl…ïdÜJB@óD7ùOü¦÷+‹ŽèÆ¡Œž"ê»À€V£ÅP7]Ͼ_‰ýH¿7Ü€FË,G¤¹QÛ‘‹`–X›eG¢oEj%À¸¶¾Z¥Äi©iê‘s—VŽÝnŸ¤{ÚÚÆÖÈÅä³¥jY–¾î=©±(*¥Õ5²G&\ÈÙX­W7¥/½Å˜¬¹“F²9ZÇIµ®^„U*qX"t±&±Î¾ØÅVƶÙuæ<*Y&©e•Êç¾&«•wª^¿†:z¦D²æ‰ŽjÊÖ*±Ž¶ÄWìÊíM3¤Ži_$m[G¹odº­€ò¨Æbeu01ò¶I4Dbì[ÙoР{Ɉ2(ãrÁRå‘.Œl*®O¥9€ö¦ªŠ®Ï ³1Û–ÖØp]ؿѱÿ[äâË|Ωí|Y†È^öMĤ$o4Gøó”ÿÉ or¸¹ØîŒjÉâ(«¼@j,c=‰}c}£‹Næ¸>,c=‰}c}£‹Næ¸>,c=‰}c}£‹Næ¸>,c=‰}c}£‹Næ¸>,c}c}£‹Fæ¸>,c=}c}£‹FæºO‹Çc_Xßhâѹ®–ÓG0LFƒIêiõq¤nLÙÚ»Wvå+¹]5S”KŠê‰‡XfTÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@Åhàrª«7ô9Phàk‘ÈÍ©ºîUÜT```k° °9éotz7˜ë/+o¤Uz©¦Œáe¸‰«)|òž¶¦ÉûeàžÃΛ÷cþMqj–Òº«Ë/¢žÃž‘wÌëƒoeì:®IÜøå\ÊÔº:Öç5á¯W\Í5x3Þ·M1 V5VAˆIMM&©‘YQ¨ªåµùþ’êëª'(jÃa­Õn*ª3ͯøsíŽôì8×SGE³åÿéðæ)Ûè7Ø5Ôt[>SáÌS¶;Ðo°k¨è¶|§Ã˜§lw ß`×QÑlùO‡1NØïA¾Á®£¢Ùòº,—£{¦¶²7åUD¶mˆ·üK­Õ5G[ÎÅZ‹uÄSÙ-‘c( dñUÞ µ*ºî}òüOêEù¼ä"à.€ HÝ5tf+'þ÷äâ›ü‹-s>qä<š›áe7»†Ã ùDŸËÿ&¼<ú~Ù±<°ÐcŸÆª¾²jnsK~¹§çЉÂð@Òè¯Èꜟچ‹]/ÏŸ¶ôµ€2xŠªïZ@Ýw>ù~'õ"üÞr@[4Ò5×1¨»Qª¿ä¦–4Îç1ÍMèT_Ì @SmT‰‹¾‘ùun…$y÷ÙÉù[Å]RÚ§Ìkbý¤vçm•}:|YŒ¢‚J絓K³+Z»•vY:v¦À=9sQ#2jÛdÕ¤k™.«·ô)1x*h9[•bkZŽ~dTFßÏm¿`ôµôõŠä…ê®e³5ÍVª_vÅj« £c]3òæ[5UW̉µ@©Gˆ¤õUÏÖæ§…­[[/‚·óïÞ N’¡Îdr;3[™Zæ9«n”EM yQãUÅ$ž5uî×x¨¶½íøíM‰RÕʱDõΉ›+˜­UN”ºnØå{¡*¦ ›šõq¢ýè˜|ÙÍk'{[¹ò/DS\Ä= yÍ1›Ñ7­†Ã ùDŸËÿ&¼<ú~Ù±<±óÁ Ç?U}dþÔ4Üæ–ü7sOÏ…ëXl‹…Î§å šÙ-{S9TªôLÑ1“¢¬Ã¨ðêZª¨èõË"x‹µ‹ÑÐ…ÓLDLäó­Ý¹vªiš²ÉəްK¢Ÿ#©þrj-v<¼wŽƒ¨®¥s‡·1]Š' À:]ùOó“ûPÑk±åã¹ãÓöÞ–°ÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@ºÑEuTW¶ûlת ”Q#‘U^ës9ꨀ!Rà,À,À-``@@D@Ý;ý3õ‘þN(¿È¶×;æðx¨yU7BÒ.Þ§pØa(«4޲Ù‰´Ùƒ÷2͉Ÿó-<Õf5P®KfÊæß2¢.s7ag;4µ÷N”8iȺt 2.( ‹§J"éÒ€ÉÔh«”9Scå»W¦ÈˆhµØòqÓâ>Ùk dñUÞ µ*ºî}òüOêEù¼ä€H¯t&µø(棓•DZê"b'ªH™ŽÇMMµ?`ÏExTycðë‰Vë­¦ƒÈGè!+~Xü%[û½Ʊ2±¨ÔèD±ÜSõD"fgµŒ´ÐT[]rÛvv£­ÄLgÚškªžÉÉçðmbƒÕ!cg\kžiüÿOƒh»©˜Øã\óOçú|EØ õH4ÆÇçš?ÓàÚ.ŪA¦68×<ÓùþŸÑv(=R{˜Øã\óOçú÷kZÆ£ZÔkSb"%‘»çŸ\¤1“ÄP=Wx€*Ôxªë¹÷Ëñ?©æð;¬Ùæ‘31#F®ëªÜª%3=#V¦ü·¸€××UÕG[OKJØUÒµÎU•VÉkt} eK]+êGUc­ÎÕc®×·uÓíæ8ñ:)§ÔGS¤ÜEÞ a-KQY-+$nxÝdÛãlºÛè8q:*‰µ1TÆù9š‹¼ /r1ªç*""]Uw áÄ©*sêj#z±.äEÜ?@-m:E«;2L¨‘ºû«ºÀL•”ð¹í’f5cj9È«¹lŠ a#IQ2ÃDo‘?Ú‹·Îø$U)O%Dm•Ú«·nà&¢¾–•U&‘«QQË·nïÉ@Æ\NŠ’*cl‹oWnÝÀWv+>!UTÑÇhÍ]÷íEUú@³5WƒNøf‡$²"]Ëã"¢ìm¹À‰±J(­–¦69®Ê¨«¹ô Yk‘ÍG5QQRè©Î@;ºjªhÔJŠ©ÿåúÅ7óÑÔ²×3ç0I%“öôÔò檷ŸËtDl´’IådôÔãU[ÏåÔS68\ò=ïÎW5™.·¶Óvº¦f™–lE1 .=]R¸¤°6g²8¬k­æE¾Âû•Ny6amQ¢©Ž¹kyMOjŸÖ»ÚWœµh£hü¦§µOë]íÉ¢£ðršžÕ?­w´g&Š6ÁÊj{TþµÞÑœš(Ú?)©íSú×{Fs¹¢£ðê4r®jªw«Ö)2£œ»U,Š_nsŽ·•Œ¢)®2ñm‹XÀc'ˆ z®ð U¨ñT ×sï—âR/Íàw ®‚ú†mæ:Ú}Ÿ˜ 55óÃOÑI<¬‰š©|'¹?Ú µ˜¿+§E’hÜè›å[Ù:v –±Õ‰†Êê¨\çÔ±Úˆ™¶?¥o³  ÐT2ŸÅåE•\dj¾ÑÌœû€¤•nª— ‘ÕPÈ®™®ÕDÏÝ]b­ÀÚcŒ{ð™QW'‚¯joV¢¥ÿ<&¨§«ÄðþG#%V+œõn-¬¶Ý¶Û¡;êY-o|=’åúÊïð)dJÌÄ9§ÊÖý DOÍT …[ÌC F¢%žæ¥º2.ÏÀ ccGÃ[KU‰ÅMžg«Ù$i™n·EEUÛ²Öe møÄõw†æR2Î]þ2í¦¢<>¢i©j¡—Y*ki\¨®W-“Á翘 4ŒjãØƒ•\Œ‰.½P(Sª&…¢ZÉ\¨‰Ñµà{ÓUQÓâX¯)’8ï"_:Û3r%ÓÏôyÀ¹µìÁ©›"*;&åÞ‰}Ÿ…€Ø8þéŒsôj$j]R®5üSz&hêYnb*ë|Þ؇“S|,¦ã—pØa?(“ùä׃çŸOÛ6'–>x4çñª¯¬ŸÚ†›œÒ߆îiùâ¢p¼DW*""ª®ä@Lä"*ÞÈ«dºìÜ ˜€.Š|ާùÉý¨hµØòñÜñéûoKXc'ˆ z®ð U¨ñT ×sï—âR/Íàw [‘1­¹úÖKñQ­EUDK®õé5mDDF¦Åºl­=®¦ªYQI¥Gµ-{Y?À,€9^è9þƒ%¯Êã½ú<"'?Æ^/›?/(“.ìÇ‘{-s“еžˆÍ蛊VÃa„ü¢Oåÿ“^ž}?lØžXùàÐcŸÆª¾²jnsK~¹§çЉÂõÌ%Õ Ä¡u4i$ˆ¾*î·?ÑôQž®¥7ôðçT墲$‚š®Z"uCÿx‰¿wGÑÌh˜êœž]ºµUL\ž¯e{I¥ÑO‘Ôÿ9?µ »^;ž=?mék dñUÞ µ*ºî}òüOêEù¼äÝ1îfD­[/+/ö8ªôÌQÔ²ÜDÕÖù¼ò*o…”Ürî 'å/üšð|óéûfÄòÇσþ5Uõ“ûPÓsš[ðÝÍ?GSüäþÔ4Zìyxîxôý·¥¬1“ÄP=Wx€*Ôxªë¹÷Ëñ?©æð;wtïôÌ_ÖGù8¢ÿ"Û\ïœAâ¦ÅàyRݲ—¶åàrê,&7ë$‘Z¨Ü¶E^}¦Ü3ªjðeÄUD4Xý<±âÓJæ;$¶V9è»?Á¢äN¬Û°µÄÚŠbzá­²õ]è©[Qeê»ÑP’ËÕw¢ ,½Wz*ËÕw¢¡«Fiå‚‚GJÅf¶LÍG%–ÖD4ÚŒ©y8Úâ«‘á Ác dñUÞ µ*ºî}òüOêEù¼ä-ÝQ0UUþ.=ÿi9:šFY<6úH5ÆéÊv\lŒë·Òãé¦Y¢¢¥Ñn$æÏkî{[~•°LDË|^]žšœ'ooᯋ˳ÓAœ'Dííü5ñyvzh3ƒDííü5ñyvzh3ƒDííü5ñyvzh3„h½¿Œï}·¹(Ë 1“ÄP=Wx€*Ôxªë¹÷Ëñ?©æð;wtïôÌ_ÖGÿú(Är-µÌù¼ K&ÄàyUD7ÂÊ5¶ñSÆPê&[!Ê’ÈËø9oo=͸9S;Yq1ÕÑiÝ.3;^¹›šÔ]È–E4\æ–ì$DY‰mnFuÀá«923¨ÞÎLŒê7€3“#:à äÈΣx9uZ/#ßA+åVÇ-›~dTE4Zìy8êb+‰›¢ÖÉâ(«¼@j»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅkcë·ŠÖÇ×o­®Þ([]¼P¶>»x  l}vñ@Øúíâ€5±õÛÅ令7”hÜMÍWr¸Ö×ó8®í3U9C»uE5g/›ÂŠ›b¡äTô)•”Üpî 'å/üšð|óéûfÄòÇσþ5Uõ“ûPÓsš[ðÝÍ?׉À:]ùOó“ûPÑk±åã¹ãÓöÞ–°ÆO@õ]à@«Q⨮çè‹]‰Ý|¿7ÛäoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœdoU8ÈÞªp‘½Tà#z©ÀFõS€ ê§ÕN27ªœå» ³ÿ`Á•Q¶«WgÖ"}S›9Í|ïsw+¶E鉮f…¸Ê˜ÍꛊVÃa„ü¢Oåÿ“^ž}?lØžXùàÐcŸÆª¾²jnsK~¹§çЉÂõÌ%µÄáJi9/½w[Ÿg?ÐuFz£%8‰¦-ÌÕ謕*iªâ ¨µ OÚ*oÝ·oÑÏÌh™Î''—n5S7#©Å™^ÐK¢Ÿ#©þrj-v<¼wyôý³bycçƒAŽªúÉý¨i¹Í-øn柞*' ÄUEEETTç@•/e]»öï@:]ùOó“ûPÑk±åã¹ãÓöÞ–°ÆO@õ]à@«Q⨮çß/Äþ¤_›Àî@àéäsÜØbÏ•l®WY/Ðk*¼„~·ôž«ÈGë@꼄~·ôž«ÈGë@꼄~·ôž«ÈGë@꼄~·ôž«ÈGë@꼄~·ôiØŠçÀ™S~WÝxXv¹Ôr-ÑRè yÕTÃGM-DïFEUÏró"5NP‰˜ˆÎ\T½ÒÚ’*C…9ìæsçʪŸE–ÆèÁLÇ\°Î23ìaß-ÿ3ÿÜÿâOAû½‘Ó#os¾[þgÿ¹ÿÄt»Øé‘·¹ß-ÿ3ÿÜÿâ:ÝìtÈÛÜï–ÿ™ÿîñîö:dmîwËÌÿ÷?øŽƒ÷{26÷;å¿æûŸüGAû½Ž™{òßó?ýÏþ# ýÞÇL½ÎùoùŸþçÿÐ~ïc¦FÞç|·üÏÿsÿˆè?w±Ó#os¾[þgÿ¹ÿÄt»Øé‘·¹ß-ÿ3ÿÜÿâ:ÝìtÈÛÜï–ÿ™ÿîñîö:dmî”î˜ëíÂ6sÚ§ÿÐgÌtØÙÖàØÅ67@ÚÊlÈÕUkšíìroE1\·6êÓ-¶ëŠéÕ Ã°?ºcý‰ÕròÈö'Ðâ›Ñ3FP²ÔÄUÖùäµSöp<é³vã-‘rÖR’¦ß'“Ïï–]q(ݰÃi¥‰ï’Vä»lˆ»Íx[US3UQ“=û‘1 >7…V?’¢3%²ø ujÚÛxWDç›^ý¸·Õ9d¡ðV#Ø'ôN4Õ³OÏš‚±Á?¢4ձdzæƒà¬G°Oè5lqìù ø+ìú#M[{>hBáX`ŸÑ*Ù{>xt˜ Ô4OIÛ•ò?6[îK[i}ºrŽ·™Š»MÊÿÏd6…Œ 1“ÄP=Wx€*Ôxªë¹÷Ëñ?©æð;xR~íÿÌ÷(à@_#‹ê ­3_ÿ â?ËOîBì?]ÚTb'+rù2ï=ÇŒBîmü2·ú”þÄ<¬oV«I_Rär³tŒÕ¹Qí@:@$ ô_#‹ê ­3ÿIâ?ËOîBü7{ 1ܾL»ÏiãŽÇ@ð¬>²*Êšè"›T浺ԻZŠ›v.Ã.ºé˜Še» E5DÍPÝÀ˜ØýV¸Uc‚&½&Êß VÛ7yÌõq"ÜWªz×ÇnMaËéÆI‡c%M‰“CZÏ÷Tº0•Õ]é“E4×þZl.øž%—Zë+—ý¨‰u^_r½ÍJ-Ñ®¨¥j¦«FË6#‘[CêW2¯3•¶µ¼ÇÓvršªÿ¬ÕU¾¸Šï7‹°j¸¨’²WAO‰%<¨‹"t57ªìÜuŦjÓfÝQN©fܽÔé*$9Ö=j@²¦µY¿6Mö¶Ò&õ9âbÍsÿë·Ÿ¯ŠjV+Ý•Œ–v±Ï^„EúI®äSÛŸáÛš»(ð *h릖x`’•éIeFÙÙ¬¹¯¹::Tæ«ÑLÓĺ¦ÔÍ339L<¨ðJºØ[+O$z²%šdfµÉÌÛï&«´Ó9J)µUQœ(KÊø¥b²F9Zæ®ôTÞ…±11œ+˜˜œ¥‰(} ¹¯ðÊßêSûyXÞxôzx>Iõv†&Ð[ºð«üÇÒø¨Öc'ˆ z®ð U¨ñT ×sï—âR/Íàw ð¤ýÛÿ˜ÿîP=”_YpM,ZY§¡¨¤äÕ‚5‘й®Ì×+Sjµn©³pŒi$µxõ8U¦®hÙ,ÜèæF«á¹r]ʉæP)`lY4¾z˜$Ī©†+SZÕLîÖmFÝ©dû6ó_‘Ô'rZ t¦™&jSªÅ«\ȺäU»m8'V3G´³©Ä!¨äøƒ!tÅåK±ªÕbåET^t¿H1H’ŸJ'Äë*±j**êXuSQ1ÛÛÝ’"5Ê‹µ6s¨ÑáÐS.г ޱô¬­–LÕ vv£˜õ»¶%®«Î‰¼ט ½Èâú¨«LÿÒxòÓû¿ ÞÂŒGw/“.óÚxÀ¾‚RÓb8V'CR™ã•ìÎÄu•RÞo Á‹ªi®š¡¿ LUETÊÔH¸ýS&¢Á©u+­]¯Ù~{ôœN*®LOúwZx“œu5=Ð[xµ$ªZ*Tn[ß*]mø`óš&gu8Ì¢¸ˆÙ¥À+£Ã±ªz™¯ªEVÈ©½äTUû._z‰®ÜÓ ,×W,ê´~¦™’LÉ©e¦b*¶VT3ÂNk%ï5ˆ¦õ5uLN~‰ªÍTõÆYz§‘’Sa(×µÙ(ÕDT\«™n‹ÑÌ-FSV{Évz©Ëfú¢¶¢Z´Å0Êl%ñ¬M]|®D–5Éek®ä[¦ÔÜf¦˜ŠtW3óþšjªf­tD(á´ôÌ¡Ãê †êç««&©z^GlDmÓ›u‘K.UVª¢©Ÿ¦^*íÓLSLÆ_\ü¿-túIKO$K-Dì|HéÔz5ê«eU·:Ëæ{ 曑²ÊšWÕaÑRáµQ£™+*•Ñ®k£‘UÈŠ‹~(*ˆ¦ºµLÄNɦfªiÊ"f7sø”óTâu3T,k+äUzÄ·b¯›Ìj¢")ˆ†jæjªfUNÜ>…Ü×øeoõ)ý<¬oW¬’=Ï{·¹Êª«öDDFPæffs–$ ²t N„o0 '@ '@} ¹¯ðÊßêSûyXÞxôzx>Iõv†&к÷ú>?ëbüœ[k™]ÞWÇãEË{l7C½SqÜ8–×FÿSÿÕýªS»•Ö;Èw'žôTED¾ÕÝçt½¯· $ýã>Ð$ dñUÞ µ*ºî}òüOêEù¼ä"•”êøår1s«‘]±n¯,¦ízh•Óvˆ½4Êé»D^šåtÝ¢/MrºnѦ€9]7h‹Ó@®›´Eé WMÚ"ôÐ+¦ízh.¬rÈ×»™¬[ª4kÐ$ dñUÞ ¸ •*»î}òüOêEù¼à Ý{ýõ±~N-³Îªï+ãñî7ýPêËi£ÇiÿêþÕ*¿ÝÊÛä;›§Iç=éÒéÒéÒéÒéÒéÒéÒÛ+-Ì‹p$ dñUÞ ¸ ³¥Ñ@Œ'ŸGªje†–:{ZŠŽ‘[l·ó/HÝÝ.¹¿ü"¼;ÝùÕß3Á÷—{ ;çW|ÏÞ]î€ï]ó<ywº¾uwÌð}åÞèùÕß3Á÷—{ ;çW|ÏÞ]î€ï]ó<ywº¾uwÌð}åÞèùÕß3Á÷—{ ;çW|ÏÞ]î€ï]ó<ywºwϯEþ Þ]î=ó«þgƒï.÷@…îŸ^–ÿØÐmÿû.÷@wϯùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐó«þfƒï÷@wίùš¼;Ýß:¿æh>ðït|êÿ™ ûýÐóëþfƒï÷@”î›\¿ü¼»Ýž7¤Ói5 (ªpØc²¶K¤ªý׿TN’bf;1Ú£O…Ð[äPz´'][¹ÑNËMÂðþÃOêÐunh§g¤xuOGÇI ›•¬DT"jª|SÓdŽ>£xº5lê7€ [:àVΣxÕ³¨Þ5lê7€ [:àVΣx(Ö·ÅDO  >>%.DIC;ÿÛC  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÿÀó8ÿÄÿÄX  !1AQÑ"2SUaq‘’“RTr”±#3¡²Á45Bst¢Ò$V6b‚C³áâ%7cuð&£ÂDGñÿÄÿÄ5Q!1ð23ARq‘¡"a±ÁÑ4#BñCáÿÚ ?ñ*c—4AäAA”MŸÙú½£¯4”¥ŒÜnóä&b!ê<Ü='OòÏj“'‚{‡¤éþYíC'‚{‡¤éþYíC'‚{‡¤éþYíC'‚{‡¤éþYíC,;ôSphÜéõ ~ìôýèe¿‚KŸ¤`ùgµ ž .~‘ƒåžÔ2x$¹úF–{PÉà’çé>YíC'‚KŸ¤`ùgµ ž .~‘ƒåžÔ2x$¹úF–{PÉà’çé>YíC'‚KŸ¤`ùgµ ž .~‘ƒåžÔ2x$¹úF–{PÉà’çé>YíC'‚KŸ¤`ùgµ ž .~‘ƒåžÔ2x$¹úF–{PÉà’çé>YíC'‚KŸ¤`ùgµ ž .~‘ƒåžÔ2x$¹úF–{PÉà’çé>YíC'‚KŸ¤`ùgµ ž .~‘ƒåžÔ2x$¹úF–{PËýÜšÒMÆ “û3Ú†Xoè¦àæ‡ >Èý™íC'‚{‡¤éþYíC'‚{‡¤éþYíC'‚‹‡¤©þYíC/vµTÙ®2ÐÕÄ·“äBM ³5²¾ž<ô51E§íƒuå® )¨ª«K…-4Ó–Œ¸Ev=¸AÖÙ­™¨¾ím“6jv°NíÌ:0™‘:sA¦ÑìåM’éW ©’’à ‡ÄC\8Ç2ƒŒ‰}'bvbËtعëëmñÍR×ÌŽ'#NEJ¯ž[£lÕô‘ÈÐæÉ4mp= ¸¡/wúLÙËEŽŽöÊS:Y^ZIÈc™R<0·W^ëuŸÜ^·1לa@Öš†®´¸RÒÍPY«„Q—cÛ„ý‚„·o(!ž2×¼9n î;˜(;?¤ËuEfÖÓÁo£’i;¤²É>SµÀRCÂÔÓTQÌè*`’ [Í’4´¸¨¢DïôQýñ]þC2ˆ—ÔÔ @@@AÞKÌoætp€¢0„ a@Â0„ a@Âi?î$û'òAJÜÇöGäƒtý#ç ÿÊò*ò葊;umÅÎm $Õ.hË„Q—`zðˆ@æ¹,{K\Ó‚Ò0AD°€€€€ƒwÃ,lcßÚÙXç7ÖGZ! i Ô9 "B× d‘‘‘Ì"H€€€€€€€ƒë¶Y¿[E³Ð½Åõ0Bè^ó5aûÀ UCú1‚;NÉW^ªãesžO/1Û¼‚ÑžÑÖ\î· IÙä›õeá¾9{ž4'¨¢ÌÛý°¹É]sÙÒÈ;Œ=­ ;ø®çž´KÀ¢_cý±’l,Œ•Û¬tóã©UÏ¥Ù ƒŠ¦Ã}.‘’5ÌØÃ’Àåׄý1`µÿ'ò„L'§ÿòLÿ¢ó”C—úþÝuÿ*/ærFÍÿç#ÿÖÏü®D½.Üí¥FË^)à ¤§|’Â$ò—4F9r>õ(EúM£§¸ì¥%í‘îË£!Ý;è>ÂADÃäÊ ÷¢ïŠïòù”D¾¦¥ õõm ·ÔV<6ž'H@éÀÎy e’÷´V¸o5[IYGQTÎ,Ó`E N­tôegd.õw[\͸nšÚ*‡ÓNæ ¹¿âÔäKä·üÆþaAKL”´›Ð€e{Û7¹N2PW”Õ[Ÿ ¯«}Do‘±È×´(ঽ'uɦ©œ±Ø¦yc†™8Ç/z ]]Õ¾GÉ» âÒ9åÓ’‚*ºº—RµÎ‚JcLj ¼ApÏ#¢ ”ÕB¨=ñ±Ü0ì5ç“ýcÔ‚(kÝ=KãŠî1º]æàêÎPGÙ¯•ƒƒ †Gð㘑‡;^Žx88(7}ÁÑT²9i¤do ²—4‚z48( ¹ %˜^#Îl²œ´}¥jºùä´ÔÎÊy`h„¾9 †OÜB TõÆJ‘O-<¹Ì/aqx g—#¨ÑÄ@@@@@@@@AÿÙäû'òAJÜÇöG䂿ñMb·ºº®9Ý \Œ1ï–ç¤õZum-[C\i-ì«sÚÒ÷9ðnµ£Ös§© AñŸÒ7þpŸü¨ÿ"¡/.‰}{ôza°:+¥´o¨ÌÉ\ÂD­ c$gc‘Ç5*¼wéŽ¤í —N÷ËOGUºØä{wx„ IÁ8äptÊ„¼’%èèönŠ²Ž ¨¬‘–ÖDã\ã»Ä†Fÿ€ìß¿©J¬–ûmx"zkÄÏ2îÿÑD×¶&žEÇ'Õ§%Ó6b’«ìuõ²²;;Û—ÄÀL€»ô1Ñ”ÑÓlãöjëV`¸ÇU ìÄdkNHòæ5û°‚z¸ìÎÙýšï¤µ€¾šF1´¡¾(✸—~AH¨ë<ÖÁµB¶PÚš·ˆ0·:(],–ËM¶Íßf¨§l±UFÖw3žFw3ÏNG\ú”‹"Ëß+ŽŽ¦ºw²¢×Æi;¹ˆ÷7NYoNº¨Z+du[?s¹>W¶J#c7]¾H9éÓ:7Ë%²ÍN"w|d¨|L|5;¬îi‰Þœ õç!Žª+!†iÛO |΂u%¤Úýˆ›g‹*èÞê»t Ê5,qè8è=Ól{ö>kÅÚ³¸ªŽ îÿôžãÕÑÓꀉ{ÑM߸¶†KtŽÄuÌñGÿ¨ÝG¼eô¤ âÙ퉊ÍJC{®BÀÐÉ{¿0>õ(y¿Ñ=LPíLÑHà=+šÌôAǸL§Û펹6ãtÚ ø ‹'–wÿÂÜch< „¾½ú:ÿòò§üÉÿ%*¾SjÀ¹ÐéÿÇ‹ù‚„¾™úa?ô¿ó¤þP¤…í–†=¡ý÷² ZÙx2S»?à~ñ#>­B!¿èÿeçÙwU6á<²¬Ø£~pÆó>÷"^NÍÿç#ÿÖÏü®CÜô›u±u[Sv§¨·USµñF!¨dŽÕ€œ‡hA:{iúM«§¶ì•%•¯YZÞÆ~ðù*…„ïôQýñ]þC2ˆ—ÔÔ¡¤ÐÇQLÐèåicÚzA(<=‹k¬Ôýì³Üè_@ÒD2T°ñ`ièÓCŽ„ìtöºŸ¯¥sËœê¨ëØÒY>qãÐïWÿ¹\ƒI|–ÿ˜ßÌ è ¯_Jk)LM~ãÁc±×‚„®ªž¾š–¶8áŒ;ˆ d¸JæêËN¿¹¢‚µÑVSEÁáÔ¼¼=Î9ÆF1êæ‚Yí²T:³ÇkìŒ0óÛ®¾¬á¦š®ºÃQ ý£òC€v]Ñ¢ è©å¥c sƒ¡c¿bs¨oQör¤:Ž¡÷Tp¡‡qùt¬yÞ‘½DcóA%¨ÒÊÆw$ùŒäiŽz j©Æç¶æTq]1q/xÉÓ–šà‚äT.îzÈe#2=À· 8cÞ‚)kç¶ËE+`øÙÏŒzÈÆŸÅ™)¤u †îîÇØA:äã’ ¢5¤AÅd#-ãž&¶&H@ñÉÞ9-:ŒT…>ÓRSݬu|_¥¨n.Îøqn½OéP#}ÂÉE³÷]½õ³ÉXøŸÆš6°Ç»€OA:õô Ü]í4:ê J»…c*ãÜe5LMlp; ïä8øÃ`Z‘æÔk±›|ë&Ýtkê(ÌXΈóÀ›OðCÓm5nÓÜMMOìáfDâÆ?©='ú ã¢DFØ‚ŸºmÛCUW¦  ™ÞÏ@$èE\/Òú+æÑ‘M ’–‘œÜ59Ëœ=YÓîD¼Är>$OsÖ¹§°Q+ÕwûÅ}?sÖ]*çˆód’’´týè‡=·Ov¹QÓšzk…Tœ“s9­9ç 8D*±ÎÍsZæZAÁrAf²ép¸ŠÚꚠÖ‰¥s÷O«%†ç_lÉA[=3œ0LRçÛÖƒcyºÓZnU}Ô[ºf8?w«9åêA +jã¬îÆULÊâî3^CòyîyA<7»´¬ŠåTÊ™”LíçË'§ïA^ª®¦ºwTUÔI<®æù\OÞPD‰{¿ÑG÷Åwù þbˆ—ÔÔ @@@A¤¾KÌoæte0:0„ ` a@@Âesÿg“ìŸÉ(¿sÙ’ Ð|gôÿœgÿ*?ê¡/.‰aP$@D‘"D@€‰X7 ÓL)MeA€ p¸ÎÜÇVîpˆWDˆ:V+õfÏW÷]i.nëØñ£‚!é|*Ýþ¥Iïr“…K¿Ô©=îC…K¿Ô©=îC…K¿Ô©=îC…K¿Ô©=îC ÒÙØÍ !Á›Â_ w¿ªR¹ ïT¤ÿr<-Þþ©Iþä0x[½ýR“ýÈ`ð·{ú¥'ûÁán÷õJO÷!ƒÂÝïꔟîC…»ßÕ)?܆ w¿ªR¹ ïT¤ÿr<-Þþ©Iþä0x[½ýR“ýÈ`ð·{ú¥'ûÁán÷õJO÷!ƒÂÝïꔟîC…»ßÕ)?܆ w¿ªR¹ ïT¤ÿr<-Þþ©Iþä0x[½ýR“ýÈ`ð·{ú¥'ûÁán÷õJO÷!ƒÂÝïꔟîC ;ô³zsKM!`êä0Œ~”îíhh¢¤À¹ 3áRïõ*O{ÁáRïõ*O{ÁáVïõ*O{ÃÉ\îU7{„•µn–N¡€äU a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0„ a@Â0ƒï>öSÐÐ{ÝÚ¥SÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µÁþÊz{»P<ì§¡ ÷»µèÿe} ½Ý¨4;²¡Àw–ŸSÖîÔx?ÙOBÁïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v x?ÙOCAïwjƒý”ô4÷v ôh0PDï-¾Ð‚T@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ ÃœÒâ@d“ЃHja¨itG+AÁ,ppþ0ʺye1Gô|ñÇò1‘ý78ï@Šx§füR2FòÞcÁˆ0PP·ß-·Yfކ­“:ÞÝæ:}…m÷«uÔÊ(jâ¨àœI¸|’ƒÛݺðeMŸ‚@~è#w>ßb‚ ;Ëo´ •K·÷=oúy?” ù~Ïíxv:­´ÿÛjª8p; ˱êèõá´Ø½™;q¨¨nkê†ôÎ<Ú9†çóõ ¡[¶wêFKR혒:8]‡I$„3Ë—¯’õ6ÒÐͳ}ýqttá…ÎiÕÀƒ‚ßntAç ÛËÅgýM&ÍK5~îø{‰çŽ`aSh¶ÃõzçGK5!|s³~Gµþ35ÆÆ¨)ÒíåCopÐ]lò[ÙP@‰ïqÞ8‚9gM9 ém&Õ²Å<PR¾²º§÷p´ãLàgÚzU´íD·–Yï6Ç[ª¥‹ÇÞk½_À Òó·l³_§¶ËDéAÌsã=äŒ}šºÖ^-Ϊ­¡u‚RÁÞÔ0u­šý+ÞšŒþÙßÈPR¼UTm]‹f-ÎÅ<±ÕR @!£9õ7—´ õ—+…»bö~-Ø 0#†&àŸo¼’ƒ…é ®žX$»Y$¤¤¨ýÜÁÄéר:תu»i¨mo¥c©ëCK*ŸH‘Ëx÷ ©zÛƒiÚö &Ë bY¸˜ÜÞõc£(.]¶¢J ¦¢²SÒ6wÔî—<ÈFà$ôc¨ƒ[ÍþùIq}%·gå¬cf/Ã\èÓŸ©»+µ1í$3SšzŠr‘—dkȃ÷sôiýçzûMþg èlucîbŠÓc›¾Y!~ÿ•×È rh8ÿ£û…5ª’ù]W&ä19„ž³—`YAëv^ý[´0IW-½´”¹ÄN2:CÓ¦9zúÐwÐs¯·)-V·ÕDƽís@Î58ASfï³Þ[QÇŠ8ÌE¸ÜιÏ_±qU5RSIQ<Ž(š\÷¸à:Pyèöº¦¦.ê¡ÙËM6ÌZ^:ÚÒrBGËãkË\Âàë¹QA±8<¿ëlÿ©ÂûÜ‘ñ Ü>ùÇï73ŸâƒÔA„ᮂ¢®¢–'ïKM»Ån<á‘üiCYQTê=éDSØ^àx­ž1ÐP[A”A”("w–ßhA* —oîzßôò)AòK6ÍwÛek®åæ²–A¸Àts@oHö ö¶m®}VÄÕWi%u$HÓþ"Šãê<þâƒË¾®®é²µ—;–ÒËÄ$ÆÚ&–€ýF…½Gš °Ã$ߢ8`ÉÜ÷ôDš îl†ÐYévJ“×ÁáilŒ{Àp;Ç£ž¹AÇý!M;OgžMcc÷}‘&Jö꺖ï{´An™•o'oyO{Ðk¶q>-¾¤šZ·ÑE,lܪhÏýÇóAчgiä¿PUTmkkªb˜w\çsƒËš*˜Ù?LP‡48ÓƒÖ"8Aô@0ƒÁþ•Ž-t'ªw)AÈ«¡“aîV»Í’JIãkf9$‘—7ïXAÒý%ží³ZîîâRÞGy£tÿ‚=½¼Û.;5C ñM#äkÚÆ–44‚èæ íÔ»1b®p¦Ÿà‚ û)#¯ûs5Õùsii˜ÿÔZÿ"‚*kÍÃhoõñU_ßg¥¥.-de­$c<Ι($ýë­Øµæ@ZÜ<ópÞv¨7ýyÞ½£ùžƒ?£Þ^>Ó?7 ò4V›Îßr}! G ¥‹:¸ë¨8(>¥±×škÍŽ'BÆDø"–&h@Ó¨Bú \àÐI8AÁumeÞƒ½4ÒÒ¿P%©Ç#¡#wÔ‚­„ÖRZ™%%²˜µíˤ5¥øÎ¤a ·V6¾‚¦·pJÀíÜç¤Òœ6VFäˆäžKö ÆPzHšÆFÖFÐÖ4a r¡™¸°ÞöǼÕÌÊ*j1Pøâ³Š÷;$`àÉmö­©«±Ç4²Qº•µ02W—˜Žw\ÐN¸äPp?ÿS³ýXÿç êm=wi©íSÃ_=tÆya¡zW`o`ƒº1ïA²á]j¢¾½”µñÐÓCÆ¢Í;Í;§-É$‘œh:6-š§}% Öª¦®¢½ílï™Õ/ÜFq»œnëË)lõ–ˆí]ñäL]KU¢=Ñ'2ÜœøÞ6½yAwZ«nÏmElR9ÓA_+a/%Á™ÝîÊ ®öq³û:ëåe_|)XɤšJ‡ÏÝYb­§u1I,Nk7ÇŒsÓìAØÙ em¶*¡Y‹ˆæ–‚às€sÉbço§ºÛ§¡ªfô3·uÀhõå žÕµtP6’ž÷G$ ¬–¢”™Cz3ƒ‚P\¹Ùjå¸Áv¶UÇO]\ñc.Žfg8 t:‚ƒ6‹-E-}UÒãRÊŠê–62cfã#`äÖƒ“÷”TçýN.ëˆ&âqw?y¿ŒgîAvïe©©¸Au¶Õ¶–ºÌY’=øäa×uß=A&¤¡¹TRUÁ{©§©eCwtñ5­ ‚2I'9AJÙiÚ osÑ‹­4Öúr/€ñŒc“sœrÓ8A$vkÑÔÜ(ª©Å-s£uLRÆKhÁÝ ãQÖK³L7z*ÉÐÜê$”† §´cš ’líæº–;]Êë¶æ‡ðà-šf´è×àrÀÕN¢Ï$ÛKEul­lt´òDbÝÔïcVh˜í3®í­i£îpÍÜw³½Ÿèƒ›Ua¾Ýㆎíq¢}$r²Wšzw6I7N@Ôá¿rPe å·ÚJƒ(0€ƒ(0€9Á ÕÑF÷9s‡"@$ Ý å·ÚJ‚ Ù䦤’x 3¹ƒ<0pHéÇ­j{¬U•qÃHÞ3 BY%FäiêA÷ZºGÔÛŒtÆ@Î ˜ œ[Õ÷ Ú¦ë,wÐSÑ™çlbAãî·rz?®PBÊûƒ¯òR÷3LL‰„Ž(É>7-z°‚­mU0¸÷= ¨lur¹î2uh:Î]ž¶ž¡Ö™øop¨“z#¾[»–¨éö Ù—JšŠ‰›KAÅŠLOy˜4äsÀÇ$QW;‹›¸ÀǽÜC‡ 8€G$ËvYb¹pIÏ{–ñŸ«(1QrœWÉGGF* äÞ”3CȾH:9ή{7@%¹ÎR Ð` ‰Þ[}¡¨+×Í5=#ä§ÓÊ4cÒO,úºÐrí´5VŠƒNš:–—ºPÝ#›çÿIèAÌ}Lô1±öê§×¶Fºi¥';]Ótè’ìpJ6’¢s¸N¦cCñ¡!Ç!ORí§4ÓID Œ>6ä4‡}CT[©æŠšâׯæ™*esP#BT‚Ž¥´v˜NáÅòô$ÏºÝ Œµ“Ó±Œy8áÙüÐseîé¶~kmµ-š¹£w päs¯$o-Žiœ;ßXê–7ö54íÆ½6yg  êQ Gª Î8„rÞÆ¨, ÁA¼¶ûB Peu”ô0êelQŽnrh.´W6¸ÒT6Mß(r#î(. ÒY¢‚3$Ò667›žàûÐd½¡¥ÅÀ4 ’N˜Aˆ¥ŽhÛ$R6F8e®iÈ>ƒd „   Ê ;Ëo´ •àÆäiÅwšy÷v ÏÞeÿõŠï2ÿáÚÅw™ðí@â»Ì¿øv q]æ_ü;P8®ó/þ¨Wy—ÿÔ+¼Ëÿ‡jÞeÿõŠï2ÿáÚÅw™ðí@â»Ì¿øv q]æ_ü;P`ÊF®àuèPléXNsË(1Åw™ðí@â»Ì¿øv q]æ_ü;P8®ó/þ¨Wy—ÿÔ+¼Ëÿ‡jÞeÿõŠï2ÿáÚÅw™ðí@â»Ì¿øv q]æ_ü;P8®ó/þ¨Wy—ÿÔ+¼Ëÿ‡jÞeÿõŠï2ÿáÚÅw™ðíA–ÈwH-v3‚ƒS7ŒZÖ9äsÝè@ã?ÌIü;P8ÏóÔ3üğõŒÿ1'ðí@ã?ÌIü;P8ÏóÔ3üğõŒÿ1'ðí@ã?ÌIü;P8ÏóÔ3üğõŒÿ1'ðí@ã?ÌIü;P8ÏóÔ3üğõ ûº¾7´uœ ” Ê ò»dÞíd0SJÉ&…Åï®ø#ž:pƒ›²±>†è*k)cs &;¦Bq ØƒÞ ó»|?þL¸{üáZ«ûžoôîþTRÕy–ϰ[>)ã‰ÓUîA¦qlQ““—Ñ¢CIUt¤§ªžø(›ñ”¥Ú€ vC¹c8ÏÚkÕ=²;õM #mo-q‰²;ŽÈÜpÂyƒ„n—êøoñZ-Ô°Ï%E/7ÈâÓ½‚]ÿ§Zç4®¼ÝéªíÖ–2€\*¢t’K+œ!½ ÉÕfÚë‹©}#¦láÄÓ¸–‘ÐuÔR ¨0PDï-¾Ð‚TËåGöÿ¡A"   ÁA^?þûH, ʤýô^Óù Å7îG¬ŸÌ ™¯òìA¬¸ìɈ0ƒäÕr>Jéäž!•ÅÙæPDç—縸ô— ú…’I%²Ñ¾lï˜[œûA´öÉo9[CicýžyoÁ]§¨žÔú&Y.]ò|F.©È`yÎÿ-Þœ ±ÜÔ–=“ µ]h¦®±6)xTæf´œ5ÆzB ktõ^¨hÛY žz~ ¬’C·C¼mÎ\Ðsè(lQÁEsÙ:³qcDrµ”Ï{dw-ààwpy ôn¥•»M+ x–Ç3|403Ë8èA¶ÒÏjwŽïlª©‰Íßd±S:@ÃËÍÕ®Amsms ¾éàŠ‡÷'uþôC¦î÷O_=Pz("w–ßhA* %ò£ûР‘3UÓScQYå¾ðÜûÔÄLøB&Ñ2‘¯kÚǨ ä ËdÊ “„Ç,rŒÇ#^:Úrž(‰‰ðe¯kÆZàáË å<–PeW‹ÿƒÿ¹”ën¶ûv謬†ÿ$=Ø%^ºv¿«RÚ•¯­+1KѶHž×±Ã-sNAÕY‰‰Ä­†ÙP“!Paö±¥ÎpkG2N8g9A”Éûؽ§òA­7î´þeÈ*ÔÜè(äluU°@÷y-’@Ò}è,‡zBP2”kÚüî¸EPe¯ò±°gìɈ0‚¼–ú)^d’’¼ós£”‹]¼ŠpÊj @`` a´ a´¢   ÁA¼¶ûB Pi/•Ûþ…ˆ*]+;‚×SW»½Á‰Ï¬€¯Jî´UKÛmf_9ÙøêoW ÚÉ祚 Ds[ ò2Ñ1ýW©­1§X¬Dãôyš1:“6œgõI°—zŠkÃ-ÅåÔõ„è×GW,*ñzušo÷Âx]I‹í÷Ké™È^SÕx¹Í¨Ý/O¸Ögd£€[Pæ¼ Áä€u×Ô·G3e6ÆcßÝú°ÎÍÖÝ=þOw¹SÒZid¥VÕÓ¹Î2;suÍWi¦„’¹N&ml÷DºÆ¥â+®uæª: zzwÕˆDÓ¹ò8G' 2rsÑЩ®Ù¼Ïró{glG{›z¹ÔWlµil-ŠX¤àT°È|S¼Üî5#«BºéR+­^þïrÕ¼ÛJ{¾ktÑÇGw·Rº‚šŸ ÎÍ;ˆk0[È`g#‘Щ9µ-l÷wx¯\Võ‰ŽþõKeϽ»=c$ÓÖKMs·Z\dv¤ô*ú”ß©ßÒ¥öé÷uþÝKUÞJʺŠ*†BÚˆ×ïA&û×r óNKôâ+ u¦¤ÌÍgÆUÉÔA^/þþäP`”–ë`›h®µwI¡‘Žsc§2°¸n®:סm=XÒ¬R1ÕçÖúS©i¼ü ‡®¦|uÔ4ó6*‡¾k¤Dè}™\øªÚ&¶žŸwNÕÄÖv¨Á¹mmTœ:wV4JL…ƒwuÜÈ#EÏC?›8tׯ+Ÿ ¢²ÈÖÝ+Yj•õ6ö@ÒÍéKØ&ÉËZã1Œ«jçloŒOô®”ÆéÙáý­RÞ+ÒØiŽs <Åû¥ºà@è*–Ó®Ùµ}ÝW®¥·Em=µ]«®Žlì‚•´…îk€˜™cÁ#Æn0«:&¥)Nì÷ýŠjZýøîû±m»×\æ2EOJ)„®í3+0HÉn09rÊ^•¤bg¿ìSRלÄw}ÐMµEfг¹ª+ã’ÿ's;ç8è Ê´hÄÞkžågZb›±Þ³W´ ¤©©=úzjVÎ÷´êKŽÐ=jµÑ›Du™ÂÖÖŠÌôˆÉÖ¾ ºXîTÂÊ·nFè¥.,~2ìУ¥DÒ³¶|½¢ctx”—jÚêù⊒&ÁMP襕ò>ˆS×”µ+ZÄÌøÂk©kLÄGtK“yºVÜvf®©´q Fìnâž&îöÈÆ1§^W}-:×V+ž÷]K[JmŽç­g=‹\6D£“÷±{OäƒZoÜiüÊ W¬¬§ ¥’ªªVÅ C.{Ž€ ø…uu=Îåq­­}Cß.ó©ËqÏ>(vy47  ú‡èÿºÆÉÓ÷V÷”î÷>|_ê‚¥âºc}–éÎÖWÇŒÑûÿ½8éÃK}ŧîÞ¬Ì¸ÎØèÞ'/.˜ÆÒwFîH#¥:fGQfÚàšj‹\-.¢‘Ò8áÂ2]ºîe¡ØÂE³ðG ‚„FÞ9q9qh$ëëAÄ·\ßESr¤¥€TVÔÝ%áÄ]º@nóÜz?‰ÑÚí¢©Žºªš†Y;‰ Ìj*8{Î#;¬:㤠žß´-¯­¤°îÁ[IÝH]© øÌ#¬däÚ™Ýn‚šŠšÉ)éåžRØËYþ'i’v…õ¦º(¢›$Äòæã ‚@(, ÕþAö Öìñýù ‘ å·ÚJƒI|¨þßô($@A UíBë{ÄÖ±èq¥&-iŸ{zº)å¾ÐÖ1­áA­y'\»w}Ê"ñs^¸M©3©CWG[Ow7*(QÅ„C,N“pèIkÁ$­[Vi²Ó„Z¶‹î¯z¤ÖJÙl5ñ;‡Ý•ÓqœÐﺷÅϨ7ŸZ¼jÖ5"cÂåZi1>2èOC;ïôU àà Œ~ºåÛ¸ÀûŠåˆÓšõäÖfño›–vv¡öX"|p>¢š­ó¶9NôrçÓ§H?q]¹Ñ¾f<&0ãÉ‘ãéÚ)Ÿ ¤sí–ü€Íqw· –¤çÿi—m8Çþ±¢äê ñð÷ ²€ƒ’‹ÕÚîõlÔU ;ÇœN×UïWR“ÒðíKÄ÷ÃÕ~¨+)ëê§ž–X¢t!­tŒ-ïgL¬\eë5ˆ‰÷µð”´Zfaë.ôÖÍo1±ŽdBIøwHûù¬ZwÛýa³R³i¯é).´sUYêi)!–HËXá îä£NÑ[ÄÛ½kÖf³äQYêÛs ªïm% tÍ{Èž œKq’@Ô{WkjWe«™œáºs‹b# o¯¨¼ÓV¾ß âq3MûÆvàÜ23ƒ“Ë &õŠMsŸÙ1KMâØÇî:ß_Ww¦ª6ø(¤†Méjc›xÊÌy8g>¾I¾±I®sú-kÄã«h,S å\³š'‰CµÞ4?#ÿoñ)mhÙ?âcJwÌχú†‹g*d®¤­‘­¨¨ cÓ½†°Â}Ù#Ö­mxß_þÕ¦Œì˜·ŒÿK"–éq«¢5ÔðÓÅG'Å’ï™^RuTÝJDíœçì¶ÛÞcwv­T3Rþ8¨«|¬ÁÏŠ@ý5-ÛtD/JLnϾeÇ–ÓylqANbh-Ž Ëæod Ühz3œ.ñ©§ÌIú8Ξ§.tâ?w©`! k#[dÉûؽ§òA­7î´þeÈ>eúDuæ¶ð(á§©–ŠÚöˆ¢qiqÎI#™üv6Cb  ¦w8cž¢Q¤ohsX4 àÖƒÙ8DxlÍoŠÞ@õr‡c¨$¶ŽúÑÃ=táϨ—™ßq$àú³ìAšK5ÁÒØÝ\ȤîMÎ.ÞÞ°ã¤zuo0ÔKf©¦£…²I,N­/Ü#Ï©lÑTÃh¥‚ªÅ,1675¯Þtœúðƒ†6r² šË¥+beÇ»4$»I¢ Ï@:û ªìsÅw©®ŽÍEsefãË*Ðø^ iËNÞƒ;I ´–*:šh¡¦­¥•­‚(¼çø…å§ÕЂä¶ùhlÔ¶ÚkdŽXå38õœ ›gmõ6Ûg¤€L¯{"kËÄ,'FxAÖA«üƒìA¬ÙãûòA" ;Ëo´ •’ùQý¿èPH€ƒe2€€€€€€€€ƒ+ÅÿÁÿÜ‚Ê a2€€€€€€€€€€€‚9?{´þH5¦ýÀöŸÌ ™0€e9m43\#¯–ú˜†÷w}`rΧTjÿ ûköxþÀüH€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚(ê`™ÎlSG#™å¸ßj Üö±¥ÏpkZ2I8°ÏCÈÙy9‡!ˆ1”@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò_*?·ý ÐWÿƒÿ¹„¿÷±{Oäƒ߸Óù” ÕþAö ÖÜGöGäƒ| e(@ÊP2”  e(@ÊP2”  e(@ÊP2”  e(@ÊP2”  e(@ÊP2”  e(@ÊP2”  e(2€€‚Φ*K|òÏ!Ž0Ü4dŒè1ïAá($6S-š® ‡ÂcY½®ö¹p urëA(½×^©å·UËD£y²nà7wÆÁÇFˆ:ûúzc=jÙ4Ò~Óu™-hs<ϱjùwŠÉg¨¸JÒñ|Voq8hûÉAȆߵ5´í©¨¿2†gáO+\ÈýD»Rƒ°êæZ¨iMÒ­†G¹™C7Zù« h‚:=¡¶×ÓÔÔÁQû cûIžÂÆc¬Ìz¨v®Ïqªe5=QâI“’'°KöK€îAx\©]s}µ²æ©‘ Œ'ç—0Î’jÚš6K™éZ×LÍÓ⇠PrÎÛX#“»³Àw±<µ€òÞ8Ã~ôî»}®š:Šº€ÖJqè/2gÅ$ýÈ3k¼Ñ^#‘ôr9Ü'½¯ÌsN3‚ è0PDï-¾Ð‚TÈÂð0íÒӜỒyãð„ É|ñøBä¾xü!r_<~¹/ž?@Ü—Ï„ nKçÂ7%óÇá’ùãð„ É|ñøBä¾xü!r_<~¹/ž?@Ü—Ï„ nIçÂh pÝħÅÎËLø˜^iåŽw5£$µ®ÿë@¾Úb«·]禋tÊ$¥-ñÆ9‚ƒËVO-Ó`,2WHê‡Ï_•Ïæð^às÷ îmãq³8Æ)ÛQB0ñŸ»’ 6à°ÚhDwA¯ƒ¹wN¹ÞÿÝ”ÄCI•98޵Ǻ:ñ!ÎEjš9¶ËiŒn Ž’HaÈA_dEà×/1ˆ„3 ŒãÉÎ~ì ZlõW“Ùú˜ªÍ%ÊŠ${wÁ!Íê-DZfÁxª¯ž¶†áqVÐHÖJavôoi×—A䃶€€€€€€€€€ƒNòÛí%@@A”A”A”Aã.[_Qpžzy¡s%yxßqg\rAWõ&ëôé¾aìAëì¶÷ZíqR>A#™’çY': ¼@ ‚2 ›³²Jé;Ü^ræÇ+ØÒ~È ¼lVÇPÓÐ÷#5,’ÚH sNAÐõ ¹,Oá™’7‚×5à ƒÌƒ™C²¶[mSjihƒe`"79î ¢ !¿r nv eâH¤­¦âI!’5îc€<ÆZA ÓYmÖ¶Ìú F@é" væ|`Ðq§^§^eÙ}’¶Uì;K´ŠÂ$cË™½‡o·#?xAè®6mÕ°Šº}î""ǹ…€ôÒ:‚ m¶šD†‚°±ÎÞv %Ǭ“©>Ô` ‰Þ[}¡¨4™Ä€p\qž¤p›ëøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&uˆ p™Ô~"ÂgQøŠ Gâ(&ôd½âƒx\]'˜Ð ‘ å·ÚJ‚9yÇö¿¡A”?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р ÆFqP ™ï@$dj ò9ö Ò)â›…+$á¼±Û®Îë‡0}h$@@@@@@@A.8§â³Œ[¿ÃÞñ·sŒã©ˆ0ƒ(4’FC¤•íc7œç޲PelŒcƒšà \A‘A”@@A„@@AÓÃNÐé¥dmsƒA{€žCÚPlÒç487˜QíA² $æJ\ÐæàFA   ÊÙ<2I$qÊǾ"k²XHÈÈèш0ƒ(#lð¾gÂÉXéc½Ù-Ï,ŽŒ ‘ee|xx柊Î0nù{Æ Î3ޤ5í{C˜àæ‘AÈ(6@A„@A„AtAÆ|$Q ÐéqÐIAfëµÓAs¯¤£ª´ÁÞðÅtÛ¨~îñkF02sª ¶­¥uÂãBÃIr 4Ï>Vø>;F€‚>ôùvº¹Ôtµ²ŽšêÙa‚ª§{„È›£\ü)ÄjOnuc¨Úê÷S:sœº›<2:μqûëy¹Üëa³²…”Ô%Vùt²`È “Pi´W«• Í<5VhâŠ.# ª‘Íšl 5uh3W¸IYg¥¶Ó@MÒ•óïTb,qÌxܽˆ-Ø.••³\(®1ÂÚ«|Íïƒ;’49®ê4ë#k„Žx´7$à78MœwkÕ}7˜h¸7 ,u6ø0ɺ\íï(‘P]Ú´¶›|rÓÆÇÍ=DtññI kžp ±Ð;r£½Þ⬽SÕ÷¾NõÒqƒ¡ãˆK\惗y'#ž¡»eÂ÷_juÊX¨ dÔÍ–š'¹ùi#9‘ܱӀ4A›hªªorÚêj-µ‡¹MDsÐ<–Œ;ŽuÈÕ6¦úv~“hf¤ mŽce¥üM×?sy§8œã]:P]eMíû}UHÊŠaI,N1¹¯>!{²F¸ßÓŸ,a»mý×Eÿí*_þ`A×Ná¹mUD4t횆š)x˜;ÓˆׯA&Ðm×+è¨˱Ã^ðè^[¼ ÈŒg8ˆ®õîuêÛu‚’Ih©ít!Â9Xæ»B ÈòH:ò(9ÝÛvt›)°ÑÒCUJçˆ ^X‹8 $ ëæ‚ÍÓk* ¹ÖQRÕZ)ÍÄîé‹ ï-ÞÝ`ÈÀœêPL6¢¦êËLVXaÜ©ÝRçTä²Û€tn®;ÇT ”uSïÛFkcŽ:QxŒ’Óˆ€Èθ>´.7[„—±g´2˜LÊqQ<Õ!Ŭi8h n $ƒÓ AF£k*éö~ª­ôqšêÖÒO\K\âæŒ´óÔ8ž](;¶Þù^ëŸs KòÖÓ‡a­ê$ó>½p­u·ƒµÁUSKÜT`{7_–·‡¼7uÀç®y”¥¼Gi‡hg¥£mªR×Z]ÇŽ';äù$ê åÒ‚jJ›Ô»up¦¦Ž`&75ú1Åú·\i©:rA~Ò\-W™QSgš)*™ ¥†WwCçn‡ju# ‘„íCs´äÒÿ+\¾VVÑÇ©j플q!ò×¸ê ŒûÐraÚꊟ¦©†š .U†Š66Bat€_½Ïs(-Ö]nökK¦¯ŽŠ¦®iã‚‘”áìkœó½¼Iõt¬»]l6J»…äPÌè÷[i·ØçaÅÄàdzARÛµs>êëuDöÚùJúˆl—xeœØàI×]OR ¶z÷t»Ó ÷Ém–œÆç>š› úìœg ä Pm³›ñ±ÕÉ-°Ó9§‰OÿÝ vO>¼êA°©¹ÓGl颋_O/×'A¡òt×­ÕK»®qYè›EÝŒ¦SÊ×𙓺ZNH<ÎÛ Ö[=C*¡dUtu§¨c ,Þ ´pATê®—jËÝU²Í{…Œ3ÍVA{ÆCZŽI=h+‚ñ·ff±ÏFÒwÝ‘ìAN ¦mÍlütÑÛèe¸C–qžYONÆŒžœž`rƒo×Ja¹Õ1”uUvÚˆ¢&žMèfspZs¦„Q:0Ý/4×Ú[mÊ*"Úø¥tƒ1¹€×gÊ<Æ9 ‡cªoŒ­–áQO,B²vÆ»x8?œnŽÍBù[[FØKYk¤Ù’½Ä ô€G惓úÝY&ÌQ\ᤧ–¦záFøÄ‡†ã¾ZK]ÔHzŠ Ô—[=õ¶›¼t¥óÀù鿦ÞîÒu(9PíEõÛ;ÑËIB(Y¬Ð´¿ŠXº^ÓÈuàôh'™ìnÔm»Œ•¢Ï·\4xý¦‡ÔPoOy«ï}ŽÝh¤¥Ž¦¶ˆM‰7¸4ñµ­ÎÔêàÊeMâ’Ó“TÚ`©ß"i¦/l-n¸À'9å¡(<íÃinu»vžžjFTÑTp$ž™ÅÌ{Nï΄ïtòÕb¢j˜¯–.4ôSULúŒM8X>&IÔò9A]ÛCy¨£¬¼PSQºÝI$H]Ʊœ=ÀÈà`òA=Uþ¾{µ¾†Ñ 3Ù_Bj›5Fö#Œ=.¾”ݵ7 kÎjŠH[m«Ò˸8;¸óphɤ›,õõ4îš²¦ßSˆ0ËC½ºáÓœ“ü ×¥ÆKسÚYL&ŽœTO5Hs𯗵¡­ÆIÁ<ô ¬¬‚Å=GqDêúJöQOyÜs‹€ËO<àFyg\ ¹Gt»A´QZ®ÑѸTÓ¾hd¥Þ»„5ÁÜü¡®ˆ-Üîs[®vÈœÆJÙ]’ï2B2ÏVöá2³jf§uÚHéY4T•QÓ –™ª€A<€Àh: 4×;­%ê–Ùye#»¹0MHs.cƒ‰è9ÕÉví5YØŠÛï=?v=wãËFuÏ ‚JûÅÔ_`´ÛiéL“Ð÷OríØÎö@9#‡_J ÷ÐÿÕkˆ”´¿¸eß-g†sR¶ñwµÙìµ°Q÷¶¡°SîÆ]Æ‹xÇt:ã#en›e4÷é*-0²Úw]lû²T<7yÁƒ# œ”Õ\*.;Qb®´²ú»T¯a¨Îìmqa$ãSÕÖƒ­l»Üjé.O#.éŒ%ÛÎl/;¡ÍwX:„Yö†ª®÷-ªª¢ÛWšS;' y-nºZàI×\„i±ÖK±[# ”Å%\Cvv¹ÁÏËñ4-Ó^žH>K¡¦Ž76&¹­Â&î³=8(<þÒ‡\oV‹ ¥’:Z¾,Õ<7™fF¸$ëìA%fÎÙí6‹•E¾ß ,Ž¡•Ž1 dn“¨þ¨)Z§“l„–¾[{Ëfp;ñb6ä7ÛÒ‚œ»~æG%ɳÚû†:Žr:ú·0;t¿À=;¸åÒƒ¯=ÚóS´5¶‹dTMm41J'¨ß#ÇΛ ú¹ôzÐs.ëµÃg­´œ I¦¹2š¡‡xñ!nñIn½$ ½_{»Áx§´6Ke=Céļjñï. ²1žŒrI×’Is‹º7÷3»½qp‚D?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р Ò^ ‰Æ Ó&éÜÞ8Æ™õ óö팶Gfe5ÊŠ’®²F;º*L@¹ÏvKˆqæNko°Üá©°ÍY<2:×O42¹®$¼8Â4êåRÿKW_d«££l&Z˜fykZ'@Nˆ$³ÁUKi¦¦¬lMšÛá<¹§t`:yñ²uPKUp¤}¾„ÓlÅEUe΢á%w Næ’ 'ÿÄq íp4ö ±g·^h¤¦‚ªŽÐè`®ªˆ8K îîàYÔ ¥.Îß!¶Ü¬´2Ñ ÇLè绉“$³t I×:ËDšßNúKu-<„à íÞD†§¹§ÙŠçY®v*§À(ª$–Zz˜ÜLÎ~øÞaÐúõAa–ËÝÊáo–òh£†Ý'¢™îqž]ÒÐN@Ý$ãTÛ7íPÚcÜãÜçl1ñN#ÓÇ!Ç Ò4×]s¨¢©ŠK†Ì:†ÛµtJÙ(žò#sö›Ã=#]êAÒ¹ìýUnÆCgŽX…DQBñ<9 0KONéÆ9 Òßd¸·hu«Ž†‚ôž—$3.àg‘èÓNh"ýY­ýA‚Áăº¢‚üû qÁÆy¤g¶ÜaÚÎûQŠy)ê)ÙCeyk˜âw›€AÑÇC„m®{µ<0>6º*¸gqy n±Ù<‡4ë6~®¢]¤{$„ µ3"‡$ø¤1Í;ÚužŒ –ªËS;l¯Œw²fI6Iñ€Œ³ÅÓ¬ôák%L—k½X|A•Ô ¦Œdä8ä9xÁWØ.tÔ›?%¥–ªÑñÊ÷5’G¸pà ÇRE’çKx«¸[émµb»qÓEXKxr5»¹kƒNAiÉÕ¶‹›k-×ZÑ÷m,‚h\Ȥk°Né‘‚22K`´Ü(k®•· `|•òÇ ç Ã7KuǧÔp¶\b¾ ͨSI,”âžx*æíæ¸8‚2tÆ¡ ¶Zºk LžWÖײ²w ˆÆÓºÞœ´ÖPz®’ƒ…ª¾Ÿhî3µ´òÛîa†mç¹²FZÍÂÆté9Ãg/RYáÙɦ£6¸‹XjZçqŸ \næ0€”7[.4ûU-ÊS¾–²¢œH÷5ñnjÜ£¹rA‡d/ µÓZȶ²:Z¶Nj›¼e©Ý“ÆÓÅ8éÉ×=%²z}¤ºÜžæ« ch'xn}è)]ì÷ vŠžíGPe3 0Ö9ÀFK³¾Ü¯AüÐSƒd«à²Ç j©ûáKq}t$î¸s‚Fœ´Av¶Ùx¼ÚLu¢ŽŽ² ˆç¤0=Ò°9‡#{ hyiÐYl»_ìÕTWVQRHâÇSºî”´ïíà4Èue¶ê{Ôo{ª(,ôîlN }9s‹ßÐOŠ0Þ±©ANžËv›h"¼TÓÛ¨¦† #=Ê÷¼Ô¹ÃMòZWEP÷?‰‡¸»t·œog—Bó-U Úh.eñðc·šRÜííðsËÑË­+ë­”q‡ÏO$m.8¹¤ úµA磰^ê)m–ªùh‘Hé£.âÍÃò[ºF¨99 f±Ü¨nµÕ6Ê[md5ò œÊܵÐÉ€iËN3^š“´û‘05”ôRA#‘㸴ø£OŠPs.;+[YM}“À ®*ˆšâK\ÖÇéÈt(,[ì—!´ ºÕÇCLÁFêaOK’—p3Èô iÍtÛ3[ †Á@é`2[*£šg87{;ºsñ‡< îGß.ûMÄî~÷ð›ÂÝωŸ= Wë=MÂJ:ë|ì‚áA!|.•¤±áÃc±®éhØoõô5Ô·môâjgÇ‚g¼ïFNZ05õ”RØ*à«ÙéœøHµÒ>€'ÆqcZ7tå‘Ó„hl[Fõ%¦¦‹ŒçÇ5Nð–69Ù- 4ï’ÈAÖ£µÍO´×+›œÃ \01ïÍìçâAÈýW¸E³pÑÅ-9«¦¹è÷‰á»ö¥á¤ã#CŽ\ÐY¼Q_.´N£’Ýhš)¢ÃøÓ<ðŸ®Hñ|lhAРìÛ)Ak¥£–wNø!ln•ÜÞ@ÆPZ@@ƒ÷gí͈0PDï-¾Ð‚TKÎ?µý  ޵Ð:âÛ‹¨áuc[ºÙËsG¨ ´ƒ(+ÖPÒÜiÍ=m–®Ž¡ÑñcŽª-Ã#:Ku<²29„íLôõöÃ3ãl×ÉN7Û¸óƒÖ2 FÓ[e~ôÉI' Ϲ–ÊìããWjqíAÚÊAYžŠºšjH;¡ôòÂŽ‹é4g}ú ½Qx¤§¾NøÈØàáŒç-.Þö2J –ý¥†çZÖÔU=̆„ Ï-æqœrJ «µ5ÞòÀ$c¢Å,R·uñß´WÖMDúJº:˜¢ãª£ .fq¼0H:èúÉCú»÷vnä”0´n ÿÁ£Lõž´¿Y']%˜[j_p±ÆV°yNqÇ_#Ÿ<å.Óµ‚†Žâúè®QÓWÔ gŽ-ö@Àý$ò g ¸_é¨d§†8j+j*XdŽVo¸°cÆ9 ¨æ‚ 6²×¦žè_1§¨œS€"%ì“QºæóF:ÐmI´UMSLê ø*©áw4Ð$ŒÎ2ܺsAɃj$¹lt—*˜ëm¤n<14ç2î€Ì“ž€sŽefå´4öúÑBÊZÊê®ÐÒE¾XÌã.ÉdòëAšËÙ¤d.mªåRécâA%ƒ©Ù êAϹms!·Ú«mÔ³ÕÅp¨d`¶=@ÉÞn >„ê(.Õm4¯§€P×OW4\nå† é#g,»\ tç©äƒmMµ¶škŒFiÛVþÅe‘úå»§‘9Ï, £e»ËrÛ ”õ0ÅôÓ·tÄòççNZŒj9 ìÜ®&ÞÈËhk+! 2–0â1ÖIcÚ{kì³]¤t°ÃÌr²XÈ‘’æïÒÉÏ(3ÑCÜ5U•´U¶èiZ÷UÄz°NO©)¶Ž©ª*ªhk­ôôñq]-\!-ë'_W4 ÚZZêÆR>š²ŽYc2Â*¢Üâ°s-ÔòÈÈ8>¤ fÒEuš1Mo¯îy²b«| Dð:sœ€q¦F¨ý¥‚çQ)mõî§•Îk*Ì Bìg'9Ès!e™›h&²\.ð]$2Ç]ÙDw@/É1é̇àuáÁ”÷ª›]Í•W¼Ñ冖 K?p Ýç„¿Y­ýêŽäÞ1‰õ ¦,ÜÃÙ!vîëšyN¨.Ks‚½=±ÁüzˆŸ+o‹†O_ŒGx¼ÒY)âž³‰¹4Í…¼6oãœi÷ ç7l)Pú>öÜÅsp¤4ãˆæñq»Ñ©æ‚Ü;CKUemÒ– º†pc‡2‡ºZ[<õA\m} )*稦¬§u˜ÚˆeˆÆp×c8-õ‚ƒ£Qs‚žãG@í÷MY¾c ­.' jÞƒŸ©ž ÎÏÇÏ“V¹²µ§ã†ãƒÖ2Gþ–ßGw®ªªªš:ã›ÑÙn·Ú2Nº”¶4lª²P\£¨•»Ôñ:ŸÆ¨l×ï×1W´Tµ{5s¬ŽZÊQæ9ñãÀáð“‚pG«3>ÒMO´ðÚ[nªž7Rñ]##’\ÐÌx£'>´v¢ó-†Å=|4îžFր܆’q—j4ÿ胙v¿KÖÁ9‚¾ž)ŸP×Ò~ÒRâÐH:ê5AÔ¢Ú:*ªzÙelÔN·ëUS7]ÆAÐA£(5·í--}ltަ¬¤’xÌ”ýÕà™£™n§ ƒƒƒªÊ»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò^qý¯èPa•Ùš·Ùi¢°Ö[ëEDu5²ÇNçE#\òàýñ :ç–pnìºÜ­w* ¼ÍsX×¶™‘ iñ\7@ë$ õT”Ó7n«ê Ä. ”´î—¸_$Ùyé­Õž"s®/h{KIiy ŒôW5—jîòKo¬•âÓ> ÚòÖn–’4iÏ^5AΡ¤§ƒc-4÷ª ”sÆéÉ)ay–™ûÇ\·Vä¬ÕQÞn[?o«­e|Ɔàé1áUIO‚Ö¿“ðs‚BÆÍRÛ»¶ª¶Ž;¹•Ѳ7Íqâ à 7]5A‹ã¤¢Ú«MÍÔÕSGO6Ö:S–†íxÙÛÍ9®ž‘¦'к¹¦9¤-;ÏfH='¥BÅOm©½ATÈo7á÷)d[ÀÜ¿LŸVy ±¶6Ùn¦ÑLÎèlf»öÒSèèØcx'=ñŸZ]UºçU²/²º’^é´TDZ`1U1žAŽ´¬Tvº›£ªc†ù$̧tN}Ë‹ºâ2Á¿ÌûTÙŠIÍñÔµ!Ï‹g#}4?ã/9i±hûÐfÚLÚVX)®tô’Êã]IUNöApNóK‡ŠíìhÓƒÔƒ¯¶TóÏbk ‚IÝTº8›¼âÖÈ °:N:Tªªt;CK´M£¬–†zS<2æH]¿¼ Ø8#—R [5Cêî×I©ä¦ŽáR×íÝ~ãX¼æôŒãš Õ2ÉcÚÚËŒôuSÒWSDÆËM¥1½„å®kuÈ òA3-VÚÑ׊Y™ ­OÒFFãŒ;§©ØèAʹÐwÔÜ++EäR×2'E-±Ò`9­Ý-{Y¯¬¦¨3Sí´öZ:X®ô–N †FÓ1ΨkÉËZüeÍž]: ªÛ]Áû´P Á4µæxb›.•ì7s>QÀ?~B —èûç[j¾G ÔQÄÉ¡™´­’*ˆ÷±‡nŒ8Œ·¤}—¥¡† ªŠ(®-3Hû†þü„4 á¿®1„Þé(6öJ‰iªu0Å,P¹íkÝâ<ž`ä胒ÃWnÙzí™u®²zÙ ñ@æBLR¶G_È ;\ë¢×eëkîÔ´ÑÉ#{’ÑY)na’hä­~yƒŒãïA-á•WYo“Ñ\àd –*È)wÙ<v<`®n[Ñœ‚ «-´Òì•ÕöªK¬’ÕÔBç÷cd2ÊZöxÀ;ÆÀ’õÚžgím†xá‘ñB*xkI Ë=(8ô0ß©[`§ºSÁ4î5Ô•4îe2½Ð‘ b74—‰<’0ÜŒkªõ®šfm}þ¡ð½±L)„r9¤á‡8=8(%Ú©n0Ø¥}°KÆß`q…»Ò6=á¾X:\”[-+äÚ÷UCÐÑºÚø›Qp/%Ïßi#Õ£Õ¦uÂa}wê$;:ËEy­¦’6MÿNíÀÖÌ pw'9ªE+¤¡ÛçTËKPø+(£‚9b…Ïhx’@ñt<Έ)ÒÐÕ·böŽœÒÊ&žZãf3½&öwp:sÑÖ‚¥u²Jk•¶ãUÔR›dtÒ:Þç‰!‘§8sYãœýÄ öÈ[hµ eÄëÔu ¦¼ËÌï=ÙÔœ”i)¦vÞ2£‚þzÝtîïAÆzñ®yØâªèâ[( ¬mu#˜×ÆiÝã89iƦºt ìO4–«¸WÔQUOIp‚Éi t»xÝFsysAÒ®²kóLwˆíϤc飢cÚçÊIÞl…º´  zê=Œ±ºjŽ%ÌO<-a|`‘ù8æî`éÏš îÔáÛDÛÔ̼6†®‰‘‡Ðñ$Ok‰ÃØß4Є4Û!²]-ÔÒRÕO$ðÌÇ:p%7N§]qÏë+ç¬Û•Ǹjiéf¢¾x‹8›®~t:Ž|ޏÁéA¶ÓMZË­ o7Z݆gÛØçHdÓpÐ\3Ë¥ EÉÛ9\YCUÅŠôÚÖSÎìË4ms]Œ“©#×ÌaZõ;ö£g+`·QV6h¡•4ZðâÁ½Œ1ÕÉïPý¬Ù»•º†Ž¶ ‡BU;¡ÁÀîdó'Ñx-ÓÝiªM6к®•püW2"[‡7.ñI<´æƒKKO|¥ŽÃÎ {ËÝYKWÙ #‚Íñâ»{ü ‘ª¹Î†ýHÛ=ÒžšYœk©*iÜÈ"i—4¸x®ÞÆ89Aê-·øÃ,ÉQM™ñnÎÍÒíÓáÖAAÈÚ‹wvܶ~AJéÄ7ç¸0¸1›¤äõ †óê4­t–}¬–ë5-Lôutl‡~ž+¢{N [®w>°ƒ•=²á.Î\+… âJ›³. ¤ÇíxM{?Ãôˆi8AÑŠ®k¶Ú[«¡ ¬ŠŽ*IØfžF ÜZqƒ¨åÌóשݨ§š£¼ü_/ëÜi;­ËPh5Žža·ÒÔ˜^ 6Æ0KºwK¸„ã=xÕ ­»PYØá­‚/S:­Ôñ0€¸æ gM@åÉ›¯ºî—ÆOM^(+©bŠ9+·‹än«µù}H7ØÖUÖÖÏ[^ –Û mlwÒsdwßâ{t¯ôóÍzÙù"…ïd5®tŽkI ' ž¡”*û}l›=´Ñ6Žw>{°’&É231øÀt¾¤Û<Òm¥š¡½ÐÅOR nZÂCq“Ñœ[¥d–½´dt“¹ÕS4À&_Ù°x½zƒË©F±ÒPm|´µ2SÉn4ÛðÂé7dßiÀ 4Ψ.m•,õ»'_,Nšbƹ±°d»uíq×€PS¨•×KþÎWSÒU#}Nù–FcÌxÀ2yu §v³Vܪ¶®žžÓWILØàZÙÐIh<½^¬ Í’–ßSv¢•ÐmvS5ÎÜe1ÀâÜ8eÞ)Ï!Œå±eÝŸ´4 ÁA¼¶ûB Pi/8þ×ô(0ƒ8@A„A„@@A„@@@A„UÅ,ôÒE CéÞá†ÊÀ ië肵¢Ó¢•ð²Y&’Y,ÓJA|¯<Üq§Vƒ’ þÔA”A”A”A”A”A”@A”A”A”äƒ=(0ƒ(0ƒ(0ƒ(0ƒ(+WSÏUJè©ê䣑ÄbhÚ×8}ÎA¥ªÙ¢ß9{šÂ\_!Ëžârç8ô’I(-  Ê Ê»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò^qý¯èP`Ö—ÉèAε_í·—LÊ*¨ä|2=…ฆœ’O"‚Å=ʆ®WÃM[O<‘ùlŽV¸·ÚÑeèÔ¾ìk 0EAXèË·FèkN\Oµê‹• ŒUVSÁÅýßV·Ù“ª æ«§¦ÇtTEZ]ã¼7AÌëÐ2ƒWRUSššz¨&€gö±ÈÝ9ê4AŠK• ~÷qÖÓÔîy\Zýßn êªé¨¡3UÔEO8/•á­÷”6ãBøY3k)ÌRæÒ‚MêQ:keM SÙ$mpuSZÖ‡8 Hθä:Pt#ž)wÌR±â7<µÀî‘Ì¢:A-Òß3*¦®¦Ž <‰]3C]ì9ÁA+ªéØÆHéâk$ò^v™Ðôè ûkI_G^Ç:Ž® †´áÆÀ>¼ ¥sºMEw´QÆÆWÍ#$s³–†°¸cï/ ÊmÙ]Ýb‰Nøý™çãuiÖƒHî–ù©»¦*êi  2¶f–ïug8Ê Ÿ6I+éNëç\yàu”®—Ê ;©›[PÈL¢6o< g9qÉÐ j}ˆ'žçAMOEEu4PËû¹$•­kúF 8(,1í‘{× ‡G©z»• ¿w»kié·üž4­f÷³%jn44Mcªë)éÛ&Œ2ÊÖ‡{2uA¼•PBež8Ã-/xd‘ì çKµh«)©Æ˜š¨Ý#$7sžzIӯɮ¶úz¦ÒÍ_MîÀlO™­yϨœ ÃêæmÞ*@È /Òq p Ò:ÏB»[LñÀ.¼YFcg»Ïö ê‚ÞPU§ºÛêçtµôÓÌÍ]s5ÎÐ ›U™$š72"DŽ0Ž`žŒzÐP¬Ú;]e%5Ed,u[\æ8ÈÐÐÐ3¼I­mg¼¶³f(®õï‚›d—n±¤úÊ ¬¸ÑIJÚ¦V@èCD¢V–NÎq͆¢P)̱ñœÝáðÞ#¤ãžf9â˜8Å#$Üqk·\æ¯Ôƒ’ݧ·UAqî*˜džˆH7 €ï–·xÉn¸Ï¨ –Í}¦¹PÑq*©›[QLÉŸNÙFð.h' Îqª •7*9#Žª²)Ã,­iw°ª ÜàÖ—8€É'@síWûmèIÜ51ÈèÞö–‚ì5Û»ØÉ'‘AbšåCY#㥭§ñùmŠV¸·ÚÑU뺙t’°ÃT5ÒS‡—nÖã“Óª Õ*7FʪÊx/îÄ’µ¥þÌPo=e5/öŠˆ¡ñK¿hðßc'^‘ïAˆk©*i»¦ ¨eƒSÅdÌÓž£D¤¸ÐÜe=Nç•Á•¯Ýöà Úª²š†.5]DTñçò¼0gÚPjë(Åk«)Å)å1•»‡ÿvpƒfÖÒ>—ºÙS ©ÈÏH 1ö¹ ˆ]­Î|ÑŠúRøeh™¤Æ?õk§ÞƒxnU®«†® iÛéY+K9øÜb+õ.¥Š²žJ† º&ÊÒà=` ©hºË_QvŽfÆÆÐÖGKCZrsí(-Ct·ÔÃ$ÐWSK_¼{&kšÏiDÚoÖëÕuQÔFæ€K›¾ÝæHˆtAf’¾Ž½®u\-aÃŒ2€}x(, Aû³öæ‚D("w–ßhA* %çÚþ…p=(>NÎÃmp±Œ¬eEcd‘¬ßÔi¯“”«£µÅ>Ì:ÊÚfÌj˜#4àdÁ¸x™ÇøqÏ=(9Gy·úén-kìl½8Nå1k8ntÆ4ë žH.USUVíò¾Ò ¢‰±¶ãžx%œÙ‡ âìúðƒqickö:ß[4WU´²MÖ Ó®r9{Hmt•W ­µ[CMPÚl¹˜kXç3ž9j@ÈéAÑÙê·Gx©´ÔÒP6®vHj¨%a$áͧ9Ó$u ŽîÚi6êÖËÔ½Ç1§lØÜ3ï7<ôÞÝÎE{£·Õßöf™ÀúC5AáÆáÄyÆ„djV¼Á0ÛzH"ïlQ6Þ{™µÑE½¿ãî€@ÞÆ>äÆ×X¬ÔV—w¶âúÛ‹Ûp=ÍMÍøÁÉÓPzPT§`|[k »Ž\S±ÄRG»ÿ Ù uÓSÖKxk?V¶k¹M)¤žPé[˜K¸~/27±Ï§4º[+¨lÛIV+h\êšXø´öæ†pçã'RÌû\¸ÅfhvPÛ[J×™¹ÀÝÖ>玌ãõ ëm”N6ìŒf[|ñU³ÿc†÷ûIAåç¯}¹òíkumÍÕp4õ´7þŸøÆ~$J k-›C³6÷4®v<Ìø›Þó”裦gèÒýÃdM™®¬c·@JpOR¥u¾’‚·dŸIM\=æ4Zaq žœƒ­µí¶Rù)aªãË9ÈeîÀ/ÿÒ9 ãXÃãÛú¸žê'{[Ä–3"M7'.Çð ;ZöÒÝìu®d3ðæ–1ï a.g•¼ítóΈ9U¶öÐìDÎ&”š›¬s–Ó8:8÷§i sÇõ(&¾TKc¹^¨©|WÞâcéÿ®â"~>â×{ÐiWn–k)-Ôî·2 {[c¥mÂ"ö;Ã÷@#ÆÐgÔƒJ‹3il¶+mME=l.¾ !Ï ­;çpd£rÍ-44Ÿ¤I™M p¶[K\öÆÐÐâ%À8¤m£kÝr±2Öè™Z駺_%®à»TŠçÓ»b­L¡c ‰·(Ùpm^\ '{Œãã'§#¡F™–ºk»*km„WѶ'A@ÂÆµà’ÉH$랟PA›d›Iz£«¨n¶Š=ÉAèªy-ÞÃñ »¶@ê«“Ço}×9í`±údôg!ltOÛª–W²œÆÛ|]ÂÙCw77ÄÝN{¹ÇFXØ’ÞöVˆ?±6á8£Ç“ÂÞÿ«;ØAZŠ;|Û]´öe3æiˆB*CN áÿ‡{£{{>´k!¨©Û{ŒAÖ­Þã„S¶ãx1w¸xpÞç÷ ÔÙÙv>×YQ Â8çœo³VHÐÇ9œ Ç©JãICÝYØúzvFê*€˜Ð „ttjƒ‹´Õ­¸Ú¶ŽfÓÚ©â¦{éÜéã.©šF‚0FïFï>YAÚ…ÅÛihsŽ\l¯$õøÌAçÙn£gè–Z¶ÓEÝΔMº7Ää1€ƒè ¶ÑZêk_A ¥sÍÀ7$9Kï[+SôÓ÷DÅñÃBþÈÝ´s³‚9t P[§¬¥¤Ùí®dóGÛYW–9ÀƒwO^tA¬pҚ͋uDpî:†F¸ÈÑ‚D-ÀÕûuºŠ³mvŠJŠhg-m4mâ48˜õÀ=h8vþ¶{dc­Ý6áY+%yâ ìéŒòÏNuÚxöŽúËpŒSw –Äà›ÇÝÎ4ÞÜþAVZ Z_Ðü’Cm’[S]$›£yä´“Ìÿÿt6€RÝ*)l,lJˆ›%dî Þ†œty9ÇAÕ©A›T.¾2ÑIIj´”M”ÔW3y­ˆœn° iâêrƒ@Ø$Ù š’fÄèÙy Þs|PÃ#ôדyiìAè*™¶“Š´H,.†Œñ<\}è8e¹Ÿ£»\ÔM„\÷éÌ.hcQ¾7²yç;ÙûÐvé((êÿH7¹*)¢˜²š™­â48C³¡ö ŸaFîËEÑ‘ÔT1ƒ 4Là°Ë„qlý}UÏr‚çEWZ×M¦¢ ñ»Ø8ñt#¡‹dvé¶“hٔϪmC{¤4â Á»ïðç{—J é# ›lo}õm;œ#ƒ¹Å@iŸs]Üéìç8°vÊl÷v–šý‘“—z@Ìçü8Ç܃³3[×W6ÈØÚóey”@ï{öYÆ›ØÎ=H9{?jž²Ý`©ŠºË i¤ŽPè¢sjìxìs‹µqÔ9 ¾ØáÙÛœNÝ ¹P×\rÇᦦ ^ã®ußû=„tõ/™LÉør†»;ŽçƒÔPyƲ–]½¸6èÈ]Š({N™ww?ú±Ÿ¹ÑGQ²pSŒšmŽ”§ˆp~”©Ðং’! 4Ã:26÷;Újñq°^«#§µRÅC¡ñæªIà7²Ý:iÏD€œíµY''¼mþrƒl>ß°ì® 4&ùcp͸733åcÖ‚ZÛ|uõ[WElk?rC l@n ¦‚í1¦pœ …·&ÏYÛ8ÿÓÁS6÷T,?ÿê??ûPz}Ž¥u>ÌR¾fâj½ê©½n‘Åçø>ä‹T4±Qí`lp²fUÔ€@s[¼ ¤ë}%ÆlµM=4qÎ*hœ%k@~]íyë” èê®I´tòÉhkžæ³œçˆ ~)aÞnw¾ôÆÉJêk$õ,¬tp67LÝ[.3ëÊ_£«çq±ªlõM Œw†šù9AÒ¯ŽÙÓfM•´Í•Õos†ëOÃ;ùÇøyséAȇy—ê™®Mk¬Œ½Ì×€|šƒ»Ã|ƒ¥€è=d‚ÕE-U~Õm <²ZZ\ØØÑp‰Ïw³Bü03½ŸZ Å­‚ñ²45ÒÅpîz:Úã-µ¬ÁןB ‹U%]VÖڌ̡§šZr pÖµå€ò:j@ÈéAÓÙÚÇ ­e®¦Ž…•tñF÷TP€+ !¹ÚF„žz †æÊi6ö—1©û‚CLÙ±¸fßØÎ›Û¿Á—&P¨°Å+iûÚ"¨à´ð¸ãÓ–q½^PqîÂ&[vÚ:Á@"‹/ L[ããgÉÎ:Pt®–kk/û- h k ¥kšøo泌Tê᎜mÌñ²8û–7†0Ýã ²qëÀA‰Yke³e$µ[ªéÄf,o–îþ×8×Îr‚µÔÎ,[YÁ 0ÝÚ&'$ñþq®1ÏÕ”kežS~ŽºZ»1Ôo…Ôô‰˜pZH. ýPqZßÑ,f˜B×9ím[€å‡oã\ÏÕ”ë=ª¢=£ŽâêÛ@i¤tF teœFä¸ãœtZV€€€€€îÏÚ?š ` ‰Þ[}¡¨4—œkúAZ;e 5²WGG*¦’fÆÞ:‰éäiKg¶PÎùé-ôÔòÉå¾(š×;﮷Ѿ)âu$&:’Lì1ŒJN„¸tòÐEYfµÜ+mÔµ-ˆb1,Mvàê ™´4tm4 Ó‚ØH`0FoVH5–ÝCP'QÁ ©L<@9ouãÖƒ6Ê dnނРV¸åÂÃw¯Ðm[AGqƒ[K LYÎä¬ëÁA¬6Ú vÀØh ŒSg€,óÝêÏ©kmÔW(x5ÔÕFC&Œ8תÝfµ¾€P:ÝJir à·p°1Œ –ž‚Ž‘¥´Ô CŽ0Ü´gN“ïA{e5,´[飧˜æH›C}c(3Ei·[c|t4ô¬“Ël1‡{qÍ´ÖKMƒ©m´8?ˆ pµ¤;Q<ïArHÙ,nŽF5ìx-s\20B ï¶ÐÉI#èàuâu4F›ss‚X77z·yaz›¢¶^-]®Ž¢LïË\p9 ƒ3Y-U=¶–NæC¿ O u7M©’[(&¤u”p>™ä¹Ðº0XI9'yÕÐÐQÓ9Ž‚–|&0Ög;£¨g¡L³Û#®5ñÛéYVìævÂÐóžzã(,2šêPÈXÙ¤>@Ðà9dôã¡ h)báSÂÈcÉvë2NIÀë: ¬,–‘_ÝâÙIÝ{ÛÜ~ wóלsõ ÍmžÙq‘’W[éjŸé¢k‹}„„­´Ûn[ÝAMUÃò8Ñnû2‚öÑ%ÀÚ™qi+Y,¬vᆸO1¢… ¶‚Û£¡£‚•Ž;Îl1†zÎFÛ¥•ýÞËe#jóž8…»ùëÎ9úÐb¦ž°ÖCl¤Ž¤’LÌ…¡Ù<ÎqÒƒ6»\V¨§dnsÝQPú‰^ìeÎqÏGPÀûo]k·ÜÚÖ×ÑSÕ5‡-F»ìÊ EJøc…ÔÑ:8Htl,¬#‘£'AEöKL•2ÔÉm¤|Ó4¶I KžÁã\„;’›ŒgàGÅt|2ýѼYôsÕêAí´PŠÑÀê@D0Xå†òAµ% %8§£¦ŠžŸÙÄÀÖû‚ E²€PšEN)s ¼3““âãõAaŒkƆµ£€R ε[ŸXúÇPÓš™XùŒC}Í:OH ÝCHêx©ÝM †Óeƒu…¾N!Ž„WYí—71Õöúj§3É3D×–û2‚Û#dQ¶8ÚÆ€ÖŒ@A^+e“VEGu3ŒK+cϳ҃JK=²‚gÍGo¦¦’O-ñDÖ—}à ‘Öú'Ã4¤ÑT8ºfÁl¤ó.$ã¥U–k]ÄF+­ÔµäåPc(@ÊP2ƒGÍ$dn‘¡ògq¤êìjp‚½5Ξ®á[C…Ìl¥Ãï7xcîA½Mt4“SE(“zªN{¬.Á:‘ÈiÌ ±€€” „ „ „ ƒÉ(@È@ÊtÊP22€‚¥UΞ’ºŠŽ@ó%sÜÈ‹F@-nñÏVˆ- Ê Õ ZZÊjIåÜš¬¹°‚Ó‡2FyŽ´ï/|»ÝÅÍWŒc ' Î2O!ª 9@Èë@@ÊP22€”-×8.B¤À;–¡ôòo xíÆqêÕ´ åK¥ÎžÓHÚš€òÇKCpdåî vJ y#©Pc(5|ÑÆö1ïk]!ÃN Ž3÷©î”õ7JËsøÔmÒ4;ã#äÐ3ªP2”VÖEAE5\ûæ8[¼íÆ; 5(&kÃÚ3‚3” „  ÊÌѶfÂ^Ñ#š\HÉÇÞ=è"¥®†±õ ˆIšiL/ßaoŒ:g˜×˜Ac(5–h¡ 2ÈÖ81»Çqä=¨+ ¤òmXS÷FqâîïnóëÊ yá±Mñ6Xdl‘¼e¯iÈ#¬Fft!í249™ÔÈãî(7ÈAP\à7—Z°þ;iÅA8ñwK·yõ弄 „  eP ýÙûGóA" ;Ëo´ •’óíB€ƒ<Êêcs.ÖªËÜÌ5ÌgWä{L›®`iÆG0úPuÄ5A´7hd¹VÑÓÛŸ1EI7—o¸Ž|ð-fHªo{M]n}ʲ––Û!­¦—†ùðIsÌàH)Þ+*´M³·¿3SRQ2Gw½àK#ÜH{‰ÞC™ÎPjã´Óìõª¢¸ D¬dluRÂ2áƒÏ‘ PK;ê*ör’Kmʶ••GºÀ›v¯pdìZq§åµ›FÎøVR±€K.㋸^Q<Î:¹ Ö†ïpª´ìŒòÕIÄ©«1T9§` ñ±Ï;  EO]u—hä’ó_ hªäe+`—pG†kô†zœúÐk=Þ¢ªƒfªnU•´5´Åõ2Ò—5Λp‚[¨Æ:u ëìt5m´É=dõ²ºiä1¹çðƒˆaÁòrÜ £Yyª²;hi¦–I¥kSA¾rH“Ä fOÌ GO[Wy†ÁSs¬Ž: trÍ$3nIQ+‰—óÀÁÓÖ‚œ×[Œ6™éMlÏ–†ù ©Î,EÍ8q΃փ»UU;6êßHÙÞ } ïtAÞ+œÐ ¬”¶2ºê®Õ·:¹äš6DdÄa¡îhÞoø§3êAvïUQÓØ Žg²)äœJÀìFzðPSž{­EãihíõDOÈ×»ÅîkµÐgOà‚•%Ò;]®ë8žëß*J#,”W)Kð@Ñíè#= ãØƒK4צÜmsE òfNq^êÒà ¹ß`ñpì`ÉýšŠ²åWY]Ys«{i.5AdÝ`hqò‡ø¹è,öš¦§j¶RÔÉKߣ³Äpö±¬.!§ œc(4¸CKe³w<×K³»¦¡­ˆ²c%CÜÀÃŒààû5Õ–á]OO´Ô­šã)([=8®xtÑ9Ív|`N™h#T×ÛâÙë›®õ³Í]QU1Ë&b{da' äÒЄͬ¸TZöjªª‘Á“ØãyÜ.pnö=YÊ-UªK^ÕìÛ{åWVÇM0pª—ˆw„GƘÏW$ˆW^m·K×}«)f§žvÓE»±D"$æãÎîNzÐz{5kîVZ*ç·qõ4ì•ÍŒ”¶–Ü땞A ÄuTÎÒðHÍAöAõb:ê±³/Ú¥° ?`g¹ËÏ<ꃨù'¿í$Ô.­«¢¤¥£Šn4¼7Hù3©p× c­.è¾VÙ&§‚zª—[®§¨};Äu7èž[Ú·<³‚ƒ—†Ò쳞¶ãQ%E{)\gñªiËŒÀŒ;LõóA{gtŽõ<.§» c k®e®{%AëÒi´ÖñYµ–šª¸D¦vþÆbÍÜGœŒr:àž¤ÁjuÇmo‘w²ž(á¥Ïs˸ç»pà— têõ ¤o÷I6ZÉš®Yë+$§šj`Þ;Ù~ws€CF¾Ô­—’èÚêè*)î- ¥}Ä´Ê È{r $r#=h5» ºÍ²£¶²áSMK%²Êȸ\CÚG>c\ äÅOržÓ~‘÷ëŽõ¢i£¤seÁñ¾ ôñθסF¶á –Õ³Ý]Y=#jMlvãœsŽ˜å¨ARŽºåQ±W›„±U[*fp}CÃx¬änK[§·t®¬Ø‹ÞÑ6²Xež'šx!ÈÖh©ùÉw·.Ö6®áµ´¶îøÕSÒ¾×Æ•I¸^àüžcž¤jƒ”Ø®rlýò±÷ë‡Ñ5Dt…²ã"=A~ž9<µèAÛ¦¸TϵVÆ:gˆg³ºwÄŠ_¼ÝqשA˸Ý+â±ílÐÖÌÙi+ƒ v÷î†#Ðu Nˆ'­ ®¤Ú;e wÛ‡ äÉ»§z\œ°ešx™å§BŽÌÉQÆõlš®j¨¨ª!|îÞxk˜º]Ó‚‚m¼Tþ’!Íe\c½Æ\E9h iþGJ w ¥Æ=žÚy¢­™“S]p?{X›˜ôCS§­õ–úÚ]§·Ûc¾\LfuNôÙvYƒ–x™ÏGB%¼\,–=¥Š:©ª¤·T²*Yg;ïh‘¬ÆIò°\q”¬Ž»C´ñ² Û¨%…â¥×2×¼ µÍ!ÇÔÉÛkH*d±ƒQQuÎ(ÏRÞy×N‘B õ6·TmÌT¯«Š8í}ñÊD².€¿Ÿ>g¥ŽåÀÙû•=ÊçZE5ÅôPÏÞ¨“QºÐ@ÕÇ8Ê(d©}mæÔé®ÑSpž6ÖÊ Ñ».uÀ’Àþ(:Û H)öRß(ž¢S=4o-–RàÍ94„zS ·µß¤ ¼Æ®³ö0ÓÈ'!®ÈŠGKG@AE³\fØù¶·¾Õl¬kd©d"L@ÖµÇöeœˆÀÆyçTßïs[öœÕS°Ç-Mª& žÂc¦—Yކçï8AnõQWG]j±G5ÖªO$óËHðj&Ý ‘““PAZ¦ºÿM²•"G×RKÆé'ªÝã:=¸ßÁ ó õ„H£ª³í…w*ʨ+if|Œª—ˆöáëž h‚­óSÝa§½Ö]"º¼¿Ä–lÒÕsýÞ4Ðk»¡=’»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò^qý¯èPa›nÄQŠvÒ…s¨â”K1‘»¸;{O'\ó'Ar»gYSp’¾–ãYn¨¡“º•ÍÄ rÈp##¬j[³‘ÕV6¶žáYCWÂÉ5;Û™Z9o |Ðf·gcªšž¦*úÊJÈ"à÷T/nüŒêx ‡k¯.h6’Ë;© †+ÝÆ)!.&pö9ÒçéÜt` Š=šm5 i讕ôÒqŸ4• {\ùžï(¿- û†0‚å¦Ñ ¢žHâ’YŸ4®ši¦p/‘ç™8t AxØËóîðUÔBùšÖÏ KLsn‚FF3ÐG$¿S¨ð)û¶³½Â^(·ïŽs½Ž[Û¹×w8Aß‘Œ–7G#C˜ðZæ‘Aæq­û7ÞÉa÷{r@eFéchènwwˆ”¢;D/›Ò÷@fïxpó»»¿Œg{wLç’ë6q“×Ë]Gq­·MPœÒ¹¸—æq¦B ³ÂÛ-ÅÓLù©iNÒ÷¼×Iv™'Åæƒ‹‚¡¿[­±Á;»Š¤ËW1Ícc;›ÄaÛÇwAÔƒ±v²2éQKT*ê)*i Œs@[0àC –ŽÕ }mlrHé+‹ qÖîŒiÕÍJ}˜£¦¥µÓ2YË-s:hI#.q6œ¼sË,ÓY©é[pÉ)–\‘â—44îé˘ºÛâ·VZmóOp¥·[¨ñMYJÂé žIkœÖœx¸<µÏ©ce_^øë;¦jɨøàQÉZÝÙžÍÑœŒ 3Ë#()×ÁÐí}»ƒO9ŽÖ羦gÄæ0Fäy#Æñ†öšiëA׸Øã¯«Ž¶*ÊšÈØcÓ¹¹s Îëƒg^Z …Û-o}ŽKQtå’KÆ|æOÚ™s½ÄÞúYÔE³qÒÝcºÍ_YYYN‡‰;›‚ÒAÆcg9AvÓk†Íoe ;žèØç¸'.qqä:ÉAâËÜS¼ÔOKQK' ˆa#˜ ‚ „©vbGWK»ºkØÁ5Iq2ÜáÀãëŒcDRlÜÔÏS[WQržx ;ŸT[9-  zP-»:ëdÐðï) Š–IXÑŒNîñ£%»e®S'd{„õT;|ƒ‡<䎄»Z)ïìŠgÉâK лuñoûkGW<â \oãK$ncd“>#@ÞÝ;ÎÈëAéªéa®¢š’¡»ðÏ£‘¹æÒ0Pq©vJ*(gšã]Vûs³MÆ{pÁº[»€Ñ3®ãg™=DT— ÊWÔUº­ÒÄæï;›uiºr#ïA¬{-CÜ5´ÕRÏZ멚w ÷0Ü`ÜcL Ú‡g™K^Êê›…e¢áuSÚxM<ð§$ê‚ÕÚÓOx¤õFn=²G,NÝ|OœÓÐB oÙÁQo4u×JêÀgŽq$®fóKÃ@ÆFº »5®ï·G=âjXßÝ!øÎtõ å»c¨Ý½­¬m¹òñ]os¼G-íÜ뻜 ¹p°Ç[ZÊè+*h*ÛÍL[—³9Ýpp àòÓ!ÙØéí±QÐWÖQätŽš7µÏ•ÎÕÅûÀ‡dëÉÙZöTQLùçuLÜyjüJeÃÁ`Œ ` µlµMA#äší]^\ÐÐ*\Ì4zƒZ5õ ÖïeŽìúi{ªzJŠG—Ã<o7#tŒAzIIi†’åW^Ùd|Õl²oGˆ0ÓÞ‚Ÿê­³ElNîžÚð%ŠBâíà@Ç7Ë’ ¶Ët´C5β½òcÆ©-ñ@è €û\/½Ev/(h#wuÎôsÓ­QØ©£¥¹Ó %ܹÉ$“FZ^ÝÓ»§W^PU—e¡2ÒMM_YG5-(¤âBææH‡ ì´Œç\Œ G²Vø¨"·‰j $uf¨Âç‚$q;Øq#%¡ÚãÕª *vbަ;¬\iãŠêÐ'Ž2C±‚ö飈ÆzñÉ¦Ú mÞ;˜|œhé{”4‘»¹½½ž\òBÝŸ¥mºçB$›…s’Y&9Í2 ;wOvr‚:Í›†¥ôSA[UGQC9àsw‹0k@òæ‚&ì…mW o©Ñ\eÌ÷Hýí2A#§w?yAЩµÃUt¢¸½ïÑ `ºwÆtAšK\4UõÕ±¾C%sØùˆÀ-nèÇÜ‚ …•×:{Œu••00Ç¿oŽÂA-!Àé‚ö^Šzåœ2ãSÝ2á–»-8rñB ³ÚᨻR\žùÔŒ‘ŒÒŒçÜ@vz†FÝ0|ÑÝ\PÇâã–€j [l’Ûê¼Ü+#cKYCÚZÑëÃA'ÖJ oˆ¯4±Ã$ÒÀèelÑK ѽ¼ˆÈ#Þƒöhà¹2âú™ç©m(¥.»ÛÛÄ5ÏÜ‚¤û)E=-DälÑ>Y"dŒt‘c}¡À–ç–GFPn€€‚36I+éNìaθã8h$@@@@@@@@@A«œÖŒ¹Á œjqªj+¨é$Ž:Џ!|§¶IKϨh, ³ÄéŸ ea–0 Ø«AäHèA" Ã\× µÁÀhsª FÉâ’Y"d¬t‘ã}¡À–ç–GB GÑL^"•1¸±û®Îë‡Aê($A^*ê9ªd¦Šª '‹Ë²æ{G0‚ÂÛ4O–H™#$xß`vKs¨ÈèA"»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò^qý¯èPažµ]oW¹Eu$T1ZÝ;˜Ñ.ùšF5Å¥ã pf×w»Ý®ULŽ Hhè«$§–G—ÈËtr2OÜqÆß8DË×ܨw/tÕ†îñ1œz÷qË¥Wi/w+9žxª,톸Œ‚¦W6ið2@ÔÔ9 «p¹^*6‚Æ-²ÓE]$“¶9ƒÎ|V“½ƒ®3§Þƒ[¦ØO}ÂIíQ6Û†½•³–IPýÝâÖ è:2sª NÚJ«œ¶Ú{$Pqk¨û±ÒUd¶(ôaº’IÇ>„ì{êqÚ'UDÈçîð$k–äDÑz?½õ›G%]‚ž:Š|g2‡0÷ŒiÖ‚]¦¬ž–ÎÃM kêë]MS$¶.÷ŽŸðŒg­"[sv^ž[TT´KË {Xƒ™ñž}$eßÁÊ÷]Û¶¶ÆÄÚ7×:Û0{Èp…ž;rqåfPv¶~ëUqmlñCUI§ÂIcôÔd¢ /Õ÷ ´ÒÕÚ)¢,'5ò9¥îê#O^¾Ä¬NýXèî\.¨Œ8³9 9 ëÕƒÏÜv¢ñ%ÂãGih«{“‡8y‘ä9­.Ѐ5vƒ¤t –ºãYCxº:ž‚’k…5ª)Ý \9Ùg>Ztä èTmuMž:!Œ¸1Õ=äþγx»Nœ–„86ñïµÒKkî:ª†Ä)YQš¨šçnµîÁè$c@PtŸx¾ÕÝ®Ô6Êz¶Üö-Iyâo0;w‘篳DkÓ¿V:K„j#Þ,ÎwO"3í<Ųí--Æím·Å÷»¼ÎcNìq€Íép9’P\»mTÐݪ­ôu6ªwQF×Hë„ûœW¸gq€ÑŒy„-;RëU¬ðÊK¥¥…ÙË„¬>; äF5ĬWInñUÕ9Œe;j¤Š™ÍÎdcNéqö¸ äTmU|VzÇÇI —8n¢Ž »uäxÀõêÍPKU´&¯r*x!žšk<•ÓoçÉ# nÆÏ±jW¶[¾ÆJÈc…¯ ™ÍŽ?%€ÇƒÔ]Øîj¯ÿhÕóJ m¦FíVÔ:à Å»ç Ïã'©¶Í¢¬~ÐCj¬ž×UݾF¾ß!w ÌÆZàIè:9 òí|y™¦Û!ïl´¹t@F@õä è=HUx¬ml· é創“°noµÍ~02qº:4ßZ6îÖÛ{!tï œOÆ æä5>À‚Sµ“PZ®Ò\éâ–©Ûñ¥øáO’ðÎy` Ò×µU†Z¥©´UOSNù`’Šbö1ÍÆXñ’zsžœ­­¨²ìÅUÚzjIä’®Háá´¶YftŸsÏøs¸ ô4×K­%ê–ÛyŽŒŠæ¿-&ðí-pw«$R,—ë¥ÒÜj!¤¦·ÀéÛ)ËŒÜ.ÍèAÏS¯$(¶æYf·Ï<¶·S\&lM¦†£z¦ ÿ ¼g£#Aèï÷†X¬ÓÜ^Àþ¬.ÝÞsˆ ô O@Aǵm=EUàZfªµTÍ=;¥‚ZKØ×7›^2ONAÓ: ’Ý´µ·*Ú[c(™t.w|ÚìîSµº ÓÒ\p[êÊ[~Ú ¨®uTt<mD±ÊçïN®˜Ñ§': ±Òžã´ 㥉¶†i£™ÙâD0ú:5ξİ^/7¶÷Y§£‚Ž9&‰Ù./‘Íq·¡£Aœç¥ÛG]úŠß[=¦ V=ñ–ÐÊçIÃK†öN£B3¦¨!´Ö^j´/®}ÔÔóM£Ãó‘Ã[®7zúu($¢½TºÙa·Z))b©¬¡î¿{ƒOZ9©Ô€PI6ÓÖÑÙïnª¦ƒ¾6v‡9¬.áJ× æ¸tŒŒéÐB #¼Þ)®–Ø®4Ôm¦¹¸²1 q’î€âtv€ê0ƒÒ ò•ôý$[å4ÑMÒoê^×05ÞÑ vNž²M ¾TÕGBâÚç5ò67qƒ€×MÇGµs¾’Å´ýê‘¶©xôò åÎk°öž*-¨®ª£¤u%,¢çU,t\BàÁ 3ûGô!ŽaŽÚ…Wjjø)uuÄèK¸S3A¸: µU{¨‚‚ÉRØ£.¸ÔA€ç iq#ÝÒ‚íîèË-šªã$fANÍàÁÍÇ8ï$ óUÒ^Ù¡v†ŒÖ9í4ÅÞ!á;-;ÜùóGD}½U²¾º×KDú*$rgÄ;]ñö[%þãUG}ÞÎ â;q£Æú9×=H/ÚöšªªìûCªm5U/¦3ÓÍE)t`‚kÆIéÈ=!-Že}6É:¶6ÚÛ+ÜòÉ¥aýë·ÌŽéõ}È/[vš¦g]©æ}T´¢9¨ž]€‡x§RA½h,O´1ìõžä!ˆÉp’™²4çu¢\gôgDºéêé+6Ö¢…ìdÑCÞ€BrF:z½h=Ï:½öjwÜd†I ]v­-VñÕÈ:ˆ~ìý£ù ‘ å·ÚJƒIyÇö¿¡@A„vÑm¾XÜÛu;h§¶¶w=’É#›,q¹ÅÅ» `‘’ÈAvÅlžØ."wFáU]-C 8k±€tç¢M¶Át³µ–úz+MUrLù¶2ìà·tï8€sÔ‚:͘ºÉUzd ·ÉÕŲ|™¡i`nàn5j1”ê,·H]cª¡³T[iÝO,RÈæ5áÌh$8È·«’ &±\h®µµvê;elUu„µÑI€iËNƘA=e¦çÊŠímeªb¥4ÓӽΎ7´ï€q‡ÑÈ ›g­Uöê‹EÂHd’¾¤Oûá¾ n5êÇÞ‚½÷g'ºÞè*â’6ÀÜ2±'21¯4 5ñ‡J ÑìäôÛa=ÕÒDhÈ|F Þl²’9c Ó_ñNÌ\³=ôæ®+‰­‹.<7~Ô¼4œdhqÉB+mÂ}¢£»Õ6¸tRA,qÈ]‡¹àŒF«Ø‚{=²z û¼ò¹…µÕ|xÃI$7q­××Ps«lw/ÖJ›•4T,ª‘U“½M»œ–ŒAÎq‘ªŽÍ[&³lõ¶¡ì|´ì-.g#ãŸâƒÇŽ<ޏmtT6ºŠ^3¦ŒÖHæJÑÝÆ--8κêƒÔÛèæ©¾¾úæµ´õ–èc8ðr\Aêr ›;²óÚêª][$RÂØM-a$¶÷8‡dsÔ 3ä„Úlw[C ·2ŠÑQI·[W&DÜ<é–îภ3œt ê[­u—kÍ\Žae|¬|A¤äiÞû ¶jÙ=ŸghíÕÒÀÂ×É->1:gh8îÙ:¨ç­¹R¾ž;¡¸ºª–]pèÈÅ!Æp@=xРš®Ép‚õSs ¤·UŠæ³c‹xohÆó]ºr1Ìc¡{XÉ©¶b–fšxnt³Æêf@0Ó+ŽîëG<⃿i·²Óh¥·Ç«iâk3Ö@Ôýç%!»;8Û7]‹ã4Gö¢,î>ç œcÝþ(+Ú6V®ÝKwŠY¢y©„ÓRO‰ßÝÓž_ÑžH,ÒXjà©ÙÉ^øˆµR>€'Æqc[–é¨ËO< ¹³¶¹íSÔ9Žt•sL #uï.úpPsn;3[\6Œ2xâï´p¶dø»ÁÓ‘å§AAŠÓ¿”)éíÔQÒA,]ÏK“å‡gtgQË tœ Ël×jÛý ʶ–ÝFú78¾zI\é'ii‡-/N¤òArÁm¸Újkiåm;èe¨–¢)[!âeîÎén1¦ºç«D]­WI6ŠŽïnu1îZi"tS¹Íâo9§ãAÏø ¨í•ª¸Zîâã­óëAØÚ‹MEæÏÜ´ÎŒHÙ£—‡6xr†¸Çc  ©j²Ü!Ú']êã¡§cèûœSÒä†ýàsœë®4Ajûl¬©«·Ü­®‡ºèüG9!’±ãi €ƒŽ„`ÙûUUê¢ç%;;ëHÊpØ ÁÀpÆHæ}º €YvŠª==a·Ç®¢) ‰ïs§ ¹Õ£wNu<ÂÅ®×= âïY#£,®’FN@ ×׃ͿcïÕQjkm¡¯©3÷i.3MûMðâèz3“Ë’ô–ë>ÖºëHÚyiª`ŽÛ#Ë_ë‰ÞnÞÐòÑlµkl­…³ÀÊÚ{œ•ôïÔ³%䆻§V¸ƒŽH.Q[.uÁw»6š'ÃNêzx)äsÀÞ ¹Îq])˜­?£øö‰u1­ûÇsI7¹ã<½H:ôu•µ7딉¬¡¦²'–éFóµä@ÈÔï¶ë„÷+eÊÚÚy&¡t€Å;Ëö½¸>0`t –Ûl¨¥¾Ýn:=ÊîãXâKwC³‘ÖtAÇfÉVEa¶B×R¾¾ÛQ$Ì‚èds²Ã¦uãBvìÐ×FÙ]]Co£$€ÆQ¸»#§x–„.6Ë”7öÞm-¦•òS÷4ðT=Ìo5ÁÀFN˜AA»'[=ŽçIYSuU×÷lR±¤±®ñH§£-Æ:u,ô÷8êKë­¶ªV5˜£qsœì޶Œzjƒ’핸7d¨-¡Ô³TQU÷C¢‘ǃ8ßs·\qÿ¨ts,Ðlõo|îU•¢’WѶœCKœE‚î’tvs€‚›lE=¶×l©u½['ÂHÞòéÛÓMßã^œž¤)v~zŠ£/–6Çw…‘ÄFIf#,$Œuž„¬q\ií‘SÜ¢§d°5±´Á!xsZΠ`út ýÙûGóA" ;Ëo´ •’óíB€€€€€€€€€€€€€ƒ2€ƒ9¯Ù»$•f­öªWLçoÆ®ëÇ,úÐte9mT\#¸KIêâ±ÌæåÍ®®gÞ‚Ú   ‚šŽžJ)ál|Y+÷G”óÌŸYA: †Õ@ë¹:’#Xº'-Ë€õ„ÐAKGODÇ2šÄ×½Ò84ssŽIûÊ Ði,QÏ â•ñ½¥®iäAæCCn£¶SŠjhéá;‘·=h,  Aû³öæ‚D("w–ßhA* %çÚþ…M¯·GRæê3'à>´C˜&qº]ž½3Œg¥™öŠ‚’[„uO|·Æ%—ˆÜo0òs>ΞÝES´ô´²ÒÓ¾’µÕUpqâ§dŒ‚3¡ÉÉûÐW­Ú*Z­™®­ŠzºJáØ„qà~ðÓtéGÜPY¯ÚZK}{Ìu5fÌÈiâßsÚIô`ç>¤¹m,6·Hg·\] ,–xéó`Œês®:qœ ‚å´î£¾PPÁASUT/˜É {ÛÀÝG,ë÷ š µ<w¹*kåàн†Q+YÜ #R=ztAµÒÓVÖCJúZÚ9*]OÝPî €Ôîœq® Óm­¾&Ô½´µóEG+£©–*|¶Ó‚IÏ.3¢è®§Ç™Y¾ÝÁ’áŒéí<~ÎÞ ÒÝ/WIîpÅO4ß¾;´ì®Ý Ë÷”Ú ¤¦­­Ž’JJÚ)ga|ªÁ0÷u:® Rm-%}ÁÔT´Õs:9Ÿ Ò6/4‘ã;:gc=]­¹ÁA=3ïVÏÀ‹u¹Û¥Úõ 4 ŠkÝ=}MÏsKJ*¥{›âˆòG>½ ­—æ]$Ãm÷ hÝ“TBÇ·¬œsÎW¦Úë}UL¶*¶AU!Šž­ðâ®vzpqA+v–’[¬–Úzjº‰á›…7 ,¶-ÞqÎ×Û¡Ñ{_U=%¦ )æ|O5ÔÌ.aÁ-2G°„Ü6ŠšŠ½ôQÒÖVÏ’²’þO"íG=p9 M´–öÐÑÕÀeªîݦŽeòdèqŒ`片Pêö9ôž=ó.8ÐA÷òA{Wnu¾²²fÔSw Ã* š,J×nÑœç#(-Ûn®¸º@ë}mQog¨‚AAžüRŠúÊ'—²J(<…ÍÃKAéÆéÊ mM¾¦ÏKsŒNbª¨m$sÏìooA:g:exvºÝ=DLU6žyx0Ö:A#ù`;=$0Pm_´ô´ÕUT‘ÁY;©YÿQ,ﲜ‘‘¼sÏè öB¦j½’¶TTJ饒çÈã’㮹AÙ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ƒ÷gí͈0PDï-¾Ð‚TKÎ?µý  ùÍžšš•ökÌ[@ù8î:gJêyš^\× ß A9ÆAÒÚ;uuæïÝ4tYe‘¡Ì0Žî~CŒc<ÚçË{H6¯º¶=±´Ü %[¢’Ù1{.2Æ ›©g=‡*×RVÜ,Q_ KÊh4ˆ‰Ön íÞc8:sÀAÝŠšo×Ùª¸/áS#–Ýî!%¹ëå¢ðÜ«}¤©eáóöPAJ×6ǹ¡.sœŸV_¨âÛëöfá5%Kà‚…ðLbÏtnsŒ´ ŽD †¾Ï[pf×C ;Ãêfôûãu³n1§ž²Ü µ=T›Ev³6š‚²ÐÔ÷ML•4îˆG†î|¢Kº24A­SvSiau4­–yëLl1œÈé§=h= •ŠÉAsÚhÚá‚hÈ!•Šê­Œ¼ÒGNöÔ:å4ñÅ Ýâ0x=N„e©~Ñ_l¬‚: ŸQQ%E;¢ ñ C÷2IèÓÍ“¦–ž ˜šÄé.•7y¥»Í.ÑÃ<Á(4ÚÆLÇZ+ã§–xèkÛ,Í……ï ,sI œoH(ÓÌêݦ¹Ü%µÖ{팉¢JwN¼N¼#©A¢)ä­šÝiuÄY䣑®m|/h¦èÖÆ^ˆÁ:jQ²Û)86ÛmΠîÚWÆ %Òº™aÑáÞFæ™ ô{=M,7m ’X_f¯Îiíá´d‘Ì ÎØSÍSh‚8!|®ÔÎ-cKˆh‘¤:AU³¿g¶’íQUGY==ÁÑM´Ôî›%¬Ý,;¼Ž™ÓTI©ª­VK&ôsCt5ÓT6*v e†9 ‹À“€ô  –¥´õ{!y£¡et÷ÌÉê™QNY3œç· Ç“†éŽ@ ô0ÓÍúýURaÖØ˜$Ý;¥ÜG3׌hƒ)O55-ÄM ‘ܪÐö‘¼Òíõ‹ûø7:Š‹$H/EÍiltÏ4õXÀäÂ1þ,‚0ƒ[• £ÚŠÚÚóxe5lqå¶:BšÝ×1ášúÁõ ’¦ŽËIg¶Â)oT¼&=ôµÃ#¦„“«\@8'žë† HóSd¶Ë´P\â¸7ˆèjèéßÅ‹\ àÀwK›‚ZFf&Þkiöbk…<Ξ+‹#Œ[®î<5ïÉ$c>²ƒh*f°2óožÛ[S-UTÓÒº#' pѤpƒ4Š»}vÉSËŸÜTsÇ¿k*îFÙYÜ=éá4ËN晈y.§^Gh!²²As6nù2Ðúi±ÖÀö¶™øÃeà;¤øº„û=¦’:JEÖ 5tÒ1¦ºWSo1ÙkÁ f€óÑZ æ²Wß(æ·ÖNúú—TR¾ Ù(sÝ.4‚:q¢ 3S-«gv~ÝQAUÆž>·b8€´ãýÔƒÓ Aû³öæ‚D("w–ßhA* %çÚþ… -L’ýØÊá$4ï€3 ‡8ûtA}eee.çeîêØ+éëf¢¬Ž²ÆÖ¸;´µÀƒ¨6µÙÙnš¢¥õ3UÕÕnñg›!¾K@Nž´$A”A”A”@A”?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р Aû³öæ‚D("w–ßhA* %çÚþ…ÝŸ´4 ÁA¼¶ûB Pi/8þ×ô(=H#tð²VÄéclòX\° ‘ÝŸ´4 ÁA¼¶ûB Pi/8þ×ô(5{#Ç µÀ‚:žºçQ²¼Yœé÷øÔN9'ÆÓòþ  êÃXvFÑCom;ë.u‡{…¿þ#ŽgÕ ûŠ•—h䯸Ík¯¢4UÑ7sx8zÞikÚg\mW*ÓHÖðü~îH!~Öº=”†öhÛ™&ᘸœ†HÎqêA~Éx«»I4’[d¥¥À4òIÎPzqЂ¶Ú׺gdŠ"DÕN³õçü?4vLIj¼\l3ÈçÍqÎA?˜AÇž––¿k¯L®¸II ¾7¶]Ü;NƒÏØ‚jGOwýUM],Î}Üè$ß œÌôHAÞØŠHáÙØjZé%W!{˵Ž ‚ýÌfßY^òÖÆKœtì éÚ¶‰×WÕÔ²˜Em§ÎíKÜrü œ®”ï×JÎäï ³;½\MÎ7oóÆwPt]´{›EKm4íîzÈ„ToyYÆWýnÿ¨»ãÞ¥¶ dkõ{³€=Zå–KíÖé,/–ÐØèçÍ“‡nã¬uúz?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р ò{OG,ûUc–:gÊÆ?ö¯ld†€àFOGO4mM%\7›mîš™õL¤8–(Æ\s>óüEeŠ®ë¶ß_G5%+!áF&nëžqŽ^ÿàƒ—Aß;]=âÊ-52ÍVçðäk|LFIåŒ Ú¢†±ß£JzaI9œTdÅÂvøÇ]Üeº¢im -Ý"&‚:´<¶ÒPÕßvš†ÞÁ<4ÐFduCXwZ㮇–tïAZk5vÏí¶àÊŠ»“dqŽg–9­:kŒé®~äÒXa¹m¥Ù÷*ÉOåDéàÒr‡§L ìm(mÍMGGC+ÄÌ0Ç4Y ÏIA®ÅË'x"¤š–¢žJ_ÜhËC²IÈÏ4ͪ´ËvÚ«l&9Å;â,’XÚH`Ééä>ôY`¯†’á²õÈöÓTðÎãšAлéϼ áÑÛéi)M×gîsV0‘ûíÉG#„‡kh{–ËAp¤ad–§±Ìê Á>£âƒ=IÜ=\ôO¬}atÒÀÖåÒ4œëÓTë 4±íD2Y©.¶â ©eKHhç Ï>Zzƒ(~ìý£ù ‘ å·ÚJƒIyÇö¿¡@@@@@@@@@@@@@@@@A½ìíEî©­–ç$Tný+åyåj8Û LŠ6†± hrAº»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò^qý¯èPES;)i¥¨“<8˜^ì œ’¦#3æ|#ìù]WÈ+GfÔuå\ð³ÿN«äšþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³ÿN«äì×ó'*ç„}Ÿúu_ §f¿™9W<#ìÿÓªù;5üÉʹágþWÈ)Ù¯æNUÏû?ôê¾ANÍ2r®xGÙÿ§Uò vkù“•sÂ>Ïý:¯S³_Ìœ«žöéÕ|‚šþdå\ð³äã~«äì×9Vz r³¹2îÏÚ?š ` ‰Þ[}¡¨4—œkúo_ÜWô²ÿ!WÓõãç Äè-µ·7˜¨id¨{æÆ2@ë^­­ZúÒÛkDx”öúʺ‡ÓÓÓI,Ñ‚^ÆZ‡>Ä›DFfI˜…edˆ €@@@@@@@@@Ab[}d4°ÕKM# ¨8ŠB4±V-8Ê3 ÖÛë-Ò¶*Úi)Þöï5¯$u¥mð’&'ÁYY" i©§¬¨m=4/šWèÖ1¹%DÌDfLÄx¬WÙîV°Ó_E-8yÃKÆ„ûTVõ·„¢-ब‘¹mWmì¸KG+)$ÆäÄx§<•wÖglOz7Fp¨¬–Ícß½¸Ç;teÛ œ²‚Ô6ªÉí•(âš™Á²¿x ËN}!Voh¬ø¢f"p¦¬‘Xl®Ð‘gª ôîÕ˧Õ]õêæÔSÍK;à¨ÑKÝ{5i]"s…¢bc0³l´Ö^'|QµïŽ3#ƒœâj‹Þ)”Zb±™RèʲDh.p™8Anék«³Öw%kÉwð×hyj=е¼^3‰‰ðSVH„67¹®{XâÖùD íêAk½U½éï¯ þ“‹ÂßÞW³š®èÝ·ÞÑœ)«$@@@Ab²‚®ß+b¬§|9»Á¯$uªÖÑhÌ"&'Á]Y" ˜ÇÈàÆ1ÎqèhÉ÷$÷xys@@@@Ar×k«¼Vw%ù·K°çè9êUmh¬fQ3•G4µÅ§˜8*ÉaÑÆùdlq´½ïpkZ9’yÀžºÛ[l•°×SIO#›¼ñ©j+jÚ3YDLO‚²²[pßÃn;pœocLõe=ø«-U”ôµ1GVÎ$$8æýܹ…ZÚ-3îDLO‚š²Do”=¡èÉxŒ ©?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р £zþ⸥—ù ¾Ÿ¯8Lx¾m°¾šK½DXߊÞç·<²BÝÄÆb±ú´êÆpôTT°Ï}CDÜS\è$sÀÿ  àýù‚¸Úf)Ë·ŒKœÎ#lûžkf-ÅÖ÷UÔYègÒ† šÚŽ@äZÑÒ}k¾­»ñôuµ»ñ—V™´·o«-¦Þ£ffÆIñ Ç/f¸\¹·åE³Þ¦ùÙ”-nÎͲ’ÞûÂÆ:Ž£„Ø„Îļ€ß=<ó÷+~xÔÙ»Å?›v2ÍvÍ[+6šÉ4ËOq§ãK, àug’ŠêÚ)iŸ"ÖŠÏè’÷e´›UÈŠk}Ô§4®§ª|€CÛži§{nŽüåµ³¶;ˆ>¥b££«Ùý›uT‘™"}ûÉpµWOˆfsÉ,ή=8_à¹×R"'» Å£Ýß ÛG-¢:Ú‹- ’y"™±GUÄ%ÙÈÉ#¥[Ov7ÚSLãt˺lû?ÐDz®µ9ôûÆ·ˆx›û¤çÙ§ÿEÇ}ös3û9î¶ÝÙr­öÛeÊ×Ü+h[=q‰¹yhvn 9]-kÚñXœf™™´D.Ͳöºý¥³÷<&žŽáJj$§–è«9ÅV5mZ[>1(ß1Yýß-§X«göú*šgƒN)ªƒÌ¬Î0áÖ§Nöß3VÓºkvMIM%m\4± ¾ií' &q&púÆÕ[YOr³÷3›@Ê(ÛI!#Fk ç®žåçRÕ‰‹g¿,±1ïy+m%²×±í¾VÛY_Q=O‘ÈòÀ3ÕÓ¡Zmk_SdN!ÖffûbW¶Z®Þú £ªŠÓÈ ¸fWc þÏÙ–“Ÿ_©SZ¶‰¬e[Äæ;ÜúZz½‘¾\ÛCOŽ¥†ØÆ[âúùŸzé31©ZåiÌZ!ÕfÎÚª¯ô5ñS1¶‰h T±Œî‚Ñ‚?ˆ÷.|ÛE&'Ç*ïœL{ÐÙ¬ÔXe½ 9å©©sa†ª~QF Ðuê^Ñ}™LÚs‡ k­ôûÀmµñ˜%ˆI¹›â7r-ÏܺèÚÖ¯æ^“3îì»ÛYîÏØ Üî¬ÒÇ#d;Í/#в޵æ×¹ÆÑáPÛ-{;Av¹[ûéUs”ïºiˆßWYÂLÚךÖqŸÍ6˜s¯g³CgÛÊú:<ðŸ@_ òwˆÓ>йÞóm(™ê¥­šDË…}²RÙã¶ÙJ[Sºé«œ58ÝgFgZí§©7ÍóÝÑÒ¶™Ìûúš±ÅS5²JZzvCâUº¬ Ž&9–“Ép®­ñ»=ÿ'(½¼\JJkE¿bé.õv¶VÔ—E‡<´8dêzðuµ­mI¬N!ÒfÓ|B†ÛZèíw¶2†.3@ÙD`èÒsœ{—M ZÕÍ–ÓœÇzí--¦Ó±ôw:Ûkk§®œ·%å¼6Œù8éÑs´ÞÚ“XœaY›M±ím¾ŽçúIîzØ<=ïÝXäW*Úk£˜ê¤LÆžaæh­”rìEÞ¹ôíuL‘Ès–Oâ´ZÓµ¯¹Ótïˆ^¼Áb³Xíòw¨KW_B<}ò·F_Ž—dÿJMïiïî‰V»¦ÓÞèØövšxà ºÙ(iÝ-9vÿuf¡ÎÇ”Ð?%Îú“š²­¯>1*–)¨¨¶ë$–Øêx5 dÍ{ÈãàŒg«äõ"ÖÕŽõ­™¼w¹ÂßK>À ÖÂÖTIqáµÛÇÅãåÒ¯ºcWì'3ølö¶‚-–u¨9ÒSïÞ!âoà¸ïÔšs3û)ºÛweË·Ûmt[-q¸WP6²j*ã2âÐüÐ:2rºZÖ¶¤DN"ay›M¢!Sl­ô¦ÙYo¦Ì®¦âº rtåïþ Ú´æ'Ü9™ÌKÍ-ŒŽa¬ý"ÿ}Ò¢gæV~ÕŸ›–—‚ÖÌÙ£}%¸Y(LUo TTÔîË+O-Æuþjš·ï³à‹ÛÇÍžËgeæõ@è ¨¬§”²Š¹ Xñ÷s)}Km­½ÓâZÓˆ•™†*m´­†k#(fe1{%.èÇAÏ?R®¬ÌéDîÊ·ï¬w¼=Ʀ º×ÍMGncq c§'­k¬LF&rïX˜ŒKÓWZ-ñ3ext¬ovî÷F3ûM[ÏÞVx½¿?ƒœZ2í=¦Äͱ»ÐÉOL[ÜPNâØœâÐO·_ÍVoyÓ¬ýU›[lJ*-†»lK_ie¾*zn3飔–JAÆCº‰?ÁMµ&ºy¬ç)›b½ÒÒõe·OhŠxá ¡®î¦Eä©5ìq:ÆTÒö‹cÆ ÚrîÛc³ÚöѶj+`ŠX)‰î ó¼ò[’:téë\m7¶žéŸz“6šæeó¿'Ûwæ½sL4@@@Aé6 Ük¶ž) K£¤i™ÃÖ4h÷Ÿà¸qÛO›ž¤â®¾×PÜj¶^–ãp§tUtÕŽ@â ,{‰iÈû‚å£jÅæµžéR“lB½þ;&Í2_y£«šJP÷ÕI!9¼+iÍõ3lãôMw[¿)kj¨iÿGV×w¦ò= :GxÓ='Lã’­bgZ{ȉ›Ïz9¬Uge)šÎvÂLî9vIÆy|ºÕ¹“]óÐÝ1¹4¶ë%Ý—ºKS(¦µµÎ†v<’ýÒAÞÏ^?ЬZôÛ39ɺщ™ñk -Žßb°VOheTõøûÏ `\GIä¦föµ¢'º Úm1Ÿc²Xi6êºßTÈ„" êH§y—é?’N¥çJ-ël‰‡ jèßEpŽ9-[\cÔC!{%×ÊþK®•³^éÊôœÇ‹†». Ë|¡í?@ŽKÅ`eÝŸ´4 ÁA¼¶ûB Pi/8þ×ô((Þ¿¸®éeþB¯§ëÇÎ/ŽY¯RYÙX#’÷]9ÛÎ#t½=JoÇèÙjî]°íu]†ß= Ž¢I =Än0q޵MM½·"Ô‹NX Ú—RYYlšÝMVÈ%âÀé‰ý›¹çž *m¥›n‰Á4Ìç)›¶uChd¼ºŽ'K%0€Ç¾Cz5åêä«É›2Ž\mßòHön¢È aŽy¸Æ]㼚cî]6~x¾VÛù²±UµU“U[*a‰KmˆG¸8G­VºQ1>ôE#KžÐR\bœ‹ =UF²T0’sH*i§5˜üÓ‚)1ïp×Uĉv’©öËeq¶[¿­qÉ>°¹Fœn´õSg|ÏU·m•H¿¼4PE$±pª"És'¾£ÉW“6Ì£—Â¥MU5êZz m’’†Ie,yËÉÓžCUh‰§}­”Ämï™vö¾á-.ÎÚì3M•q°:«†àà7F ëì\´+y¼x{”ÓŒÚlñ‹S°ƒ¡gºGk¨‘òÐS×G+7ãNyÈê*—¬Ú;§ Ú¹÷®Üvª¦¬Ñ2Žš|/âA:€î³ŸËÖ©](Œç¿(ŠDg>önûKÞK¬ÔUÌZdªŒãŒrÏ.IM9§¿¹L{×·u- ¨u¶•×&CÁ§;۾ε^Dxg»¡Ëý{—,µÍ¤ýÜ*'§Š¬ÑÄŠnOw=¹T½wkDGwr–ŒÞzͯ¯¨¾S]!Ž:sHÍÈan­kzAëÊë5ŠÍgÞéqŒ"º_(®Ê"±QÒÏ3·¤ž7ìç'òÊšR՘ͲEf=î2글€€‚õšçÞ{¬5⓺KX÷3ŒgN¬ª^»ëµ[Wtaf‹i®wÎúñ+¸Žy‰Ò;pïg#øÿ[iVkµHšáf“jø4õTuºjªç3¶žGDâs¡êU.ø˜œJ&™ï‰ioÚƒo¯¯™¶ÚgSW·rJA–°7øûÔÛKtG|{Ó4Ìc(ÎЖÚî6èha†ùD˜k챌:FŠyš-3àlï‰oÕÕÁ³2XÛ  DÛÇy¬'%£øûÔNgSy4‰¶æ¶½£u ¶Ke]  '¿‰Â”‘ºî°B›iæÛ¢q%©™Ì9÷¸kjÌÐQCGèhŠàc§^e^±1ó•¢1 ªÉu)/’RX+m Žecƒ!q¸Ç!÷.s§›Åº+5Í¢Ví›Tê+lvú»u=Â$âÀ&$Ýê=YU¶–mº'µ39‰f›lk¡¿U^%†9¦¨ˆÅ¹’Ö±ºcÌ(šEIÓŒaYûC<Ö-sÀÙJýúz’â$Õëþý ܸ‹îy²7er¯já¸7]b¢¨®áîwK‰錖òÊ­t¦½Õ·r"“¡%òI6n !¢8f2‰wŽñ:é½^)ùæù[oæÜÅú÷%ú²*™`d&8D@1Ä‚u×ÚštÙ+]°µAµ¤³ eE¾š¶(äâ@fÏìÝìéÕVÚY¶èœ"i™ÌJWí•c¶–;ãiâl­ˆDè²K^Ýsìÿ裓6#—v±[µ¦ªÕWl‚×KIMRàüDNC³’}yÓÙ„®Ž-™ï‚)‰‰™R»_%»SÛá| ‹¸a5Íq%ÃMNyrW¥6ÌÎ|V­q—`mô¶þôÒÆGÂ’bçeíêG]WÏÆ{”åwc.}§im¥¬£ž‚ÊZÇñ„Œ;Ú>ïré}-Ó‰…­L¡’ÿ#ö}ÖfÓFÈRjÚã–œçu)<_~S·ón—M›wRÐÚ‡[ird\&Öœïnû:×>Ïîè§.:÷9qߦfÏÕYÝ ^Ú™ÄΘ¸ïg òû—YÓñeö÷勽òK½5@Ø… <–¸ñ¦§Ü”¦Ù™êV»f\µÑc¥©­ÛX®u^ÏÐÍ#cá¶G¸—îY«£5ð³”iãÂQA¶sEGAí´ÓT[Àl'-o.\³2¦ts39ñLé÷Ïzí,•õ•uÖJj³S7½ÀÄz Ó§8ˆ‹a;g‰Jݳ®HoN‚9ÑpL:†îugŸ¯*95Ù°Ùpãܪ᭬tðQEGb$´zõë]k‰œ­XÄ;TÛe,ú*y-´µPg¨“$°{:ýkŒèÆfb|T>ÿm=UÒ¶¶áf¦«e§†÷¸pËF4<õV9ŠÄE±„ìœDD¤“m..¿EuŽ(£áEÁl%†?¢zOµDh×fÙG.6áRãx£«ˆ6ŽËKo“ˆ$2Äâ]‘Ð3ÈgUzÒcÆÙZ+1ã.¸ÛùÅlUÆÓFjÄ|9gÉ‘½^®µË³Æ1žå9^켣ݾ÷?Þ$áiuj€€€ƒ©l¾Íj¶×ÒSÂÝúÖ†º}âÀ:½åsµ7Z&}ÊÍs1-¨ö†¦šÕ_n™Õ k10“ºßiçÈ+iéÅ3ú¦µÚå.‹2ß({BÐ#’ñX@ƒ÷gí͈0PDï-¾Ð‚TKÎ?µý  W†¹öZæ5¥Îu4€2IÝ*ôõá1âø ´Ü°?ü:³—Õߨ½]õêÛº:é¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹ú:³ðïìMõênƒ½7?GV~ý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Stu;ÕsÆ;ÝYŽ®û}z™ƒ½7/GV~ý‰¾½MÑÔïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bo¯Sté¹z:¯ðïìMõênƒ½7/GUþý‰¾½MÐw¦åèê¿Ã¿±7שºôܽWøwö&úõ7AÞ›—£ªÿþÄß^¦è;Órôu_áߨ›ëÔÝzn^Ž«ü;û}z› ïMËÑÕ‡bõênƒ½7/GV~ýŠwשº:é¹ú6³ðïìQ¾½MÕêw¦çèÚÏÿ±7שº½NôÜýYøwö&úõ7W©Þ›Ÿ£k?þÄß^¦êõ;Ósômgáߨ›ëÔÝ^§zn^ެü;û}z›£©Þ›Ÿ£k?þÄß^¦èêw¦çèÚÏÿ±Núõ7GS½7?FÖ~ý‰¾½MÑÔïMÏѵŸ‡bo¯Stu;Ósômgáߨ›ëÔÝNôÜýYøwö&úõ7GS½7?FÖ~ýŠ7שº:é¹ú:³ðïìMõênާzn~¬ü;ûï¯Stu;Ósômgáߨ›ëÔÝNôÜýYøwö&úõ7GS½7?FÖ~ý‰¾½MÑÔïMÏѵŸ‡bo¯Stu;Órôugáߨ£}z›£©Þ›—£ªÿþÅ;ëÔÝ ¶ÓrÞþWÏêïìQ¾½MÑÕ÷aÉy , Aû³öæ‚D("w–ßhA* %çÚþ…g^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖP5ë(õ” zʽe^²¯Y@׬ kÖPy½ªÚ©,/†žšËQ+KÉÖ´tjI?’ÓÃèss3,Úúü¬DCÎøF»ýR‹ýÿòZ»:Ë/l¿H<#]þ©Eþÿù)ìTë'l¿H<#]þ©Eþÿù'b§Y;eúAáïõJ/÷ÿÉ;:ÉÛ/ÒתQ¿þIØ©ÖNÙ~ëìÖÚÔ]®m ¬§Ž7JÒc|EØÈÁÕV}~4뺲ï£ÄÍí¶aܺÝ_BöGäpÞñ‰À Ãâø¹Ð˜­c3/_‡áãV&m=ʬUžj÷v¬?‰kt¿úÓØôúÉúÅYæ¡ÿwj~%­Ò>ÿéØôúÉúÅYæ¡ÿwj~%­Ò>ÿéØôúÉúÅYæ¡ÿwj~%­Ò>ÿéØôúÉúÅYæ¡ÿwj~%­Ò>ÿéØôúÊݶó-]H‚f5¥À––ÑѪÕÂñ¶Õ¾ËÆ>N:ü4i×ueOi¶ž[,ÑSSBÉ&‘›äÈNë[œ\Î…mÕÕÙ8‡N ‚x›ZqÎëõÛêÔïí\»Eº7þ¡ñOÛü?_®ßV£ÿjv‹…è|Söÿ×ë·Õ¨ÿßÚ¢Çázý¿Ãõúíõj?÷ö§h±ø^‡Å?oðý~»}Zýý©Ú-Ðü+C⟷øô;3´¯¾ ¢žÅ< 8îºæ:yjm-MùyÜo>&³˜—^²»<ó^²ƒ2îÏÚ?š ` ‰Þ[}¡¨4—œkúA}}5²ŠJʹ8PÇçàœd€9zÈA:   †*¸'žxb~óéÜ Ç’HÈþ f³>õbÑ31äÊa(!«¬¥ §uMeDTð·ÒJàÖŒè5( ¡½Z®’>;}Ê–­ñ€ç¶CËGY ¨9ï¾Ûc¥¨ª}@STw4®Ý>,™wÞáïAÑ(0€ƒ(0€ƒ(!©ª§£‹‹Uõ^Trç¯ì·2y‘ÓÁrÅS=u´UÎìñ¤{£v=â=ÁSV±[írúV›W3ïs6ùÛ».çn⪜îný«t\PÝoU4Û7t­¤´TZ§§ˆ9’OxqÞœýè:K…U5âÃO¡±ÖNöN7AÞ"áìÔt ó¿ù[hý¾?ù± ÷ÕC$Ž‘±´¸ÈîLÇIö ñÖÛíHÚ m4w¹.´õÜF¼ÉEÂkHnðtnÝVJ ÖYn÷÷¨ºšZNê{"¤Ž8:6<·Æs†rpyc]ïÕu•P]M %5céâ‚8þ aÅåÙ:œòÆrî›O[%ÆêÚ[„ô†‚C<ÛÝ;g{Z ßpiÀ$ãŒsAfå|¸¹öéê*ç²[ê(Û+êL$ܘócËÜuž´ÞîW )¨%ÖJ[k©ó5Î fÈ.F7´!#\ã´–€¹Ô9ÕM«&0xíhOý@ 5õ Öåo‚ëm¨ ©†¢2Çz³ÓíþäB×UYY4•÷h÷¿V ’' þú 4ïH?ö[Êí›Or–¢×9¬¨¬îéZÚšAn{#¯Iº3ºq’IÎr‚ý%ʾ]£’–ãx’ß0ªp‚…ÔÌáÔB›#.$uޤµPa•»[ii¶ÆÇ[j*ªäâH÷N3ɾ ƒ³užÙk÷JØc/kaÜ0é$ÉÒ6ô’OB6NÛ=«g)éªX#”¹ò˜È‹}ÅÁƒØPî‹Åâévî;ˆ£§·IÀŽ. _Æ~às‹Éצ:ÐP²Ö6—ô}e.»w±Ò7t9‰_&®ñXÒ¾¼H1Ð]’ë}v¨¼Þv"ïy7ÇM$sµ”\îpšKuw•½¡9åêAÛºVˆ" ·Ù-ï}8"zQ<²hôíûEq–ÓeºTJ×A%cé+Hˆ7.s&­ñ€Èõ ’¾ï{”^j-¥î§¥©Š–1"G·ãÈÑþ"3€=GDm›©me“2ön±—á¯tMñé«\û@(*m yб”ñí”2ð‹…==¨y9ÑΤ†ôrÔæm%}]—gç–±–öÜKÅU`cpÂÐpö.#¥t÷Û´êÃ^+¤}ÀPÐÌøXØÈ. x o O¨–胫EÎÏ´úÛ‰¸Aql¯|-c⑃{MÝ Hϳ=œþ‘¿¾é?Óç+Óà½Yù¼Î3Ö‡“[Ø+T²SÑ\§…îŽVBÀ×´áÍËÆpz4\u"&Õ‰óÜëI˜­¦<÷ºÛ=MqºÈe¬ºÎÊV´¹Í’¥ÃˆÑ£´Ï’9rá¯ji÷V½ÿ'mÛS¾ÓÜçmÊ Ú¦SÒ3þš—,…în[ÕöGBë¡I¬fÞ2å­x´â¾ä-.ÖÆÿæÊkÿ‘Ë7ì§Ï½£…ö°ö»Cý¾/ò©_é/m/õõœ³ŸŸõZó›Ví”쪯dRy:’:ñдpºq©«·ƒ†½æšs0+[Âón…ìö­(»gèó¹“ߘú¸·jhékKbcšÔ¼ž3Jºz¸¯„÷½ózwû”VF…Û7÷´>Ç)Zøú+ûÿ üO±ŸÛùqö÷ûúô­þg/_ˆõš}ì'çýCÍ®Q$Q¨0ï g=Þœ)Œg½[îÛ;|}Ïjv^ÊÇ:ìémÜ'^®yçWZ×ʦw{žnâ&91Ÿ8ËÄÊc3?‚"Þ;ÜñÑŸZÉ8ÏsÞ®q¼Z(KÕl÷où þb´pþ´¼ŸK{:üÞñl|ø€€îÏÚ?š ` ‰Þ[}¡¨4—œkú{Å–zúê+…h¤«¢ß sââ1ÍxÀ·#«žPQvǹö«­·'Èû¤ÌšIË\7s€1âýÞ´*v~³¿5W §r èÙK î;€€Xâ|S‚y‚‚õŽÖÛ%ššÚÉL­¦fày$dŸêƒ €€€€€€€€€€ƒ‘Uh¬®‘¬ª¸5ô­˜J#m8kô9{?Ç]«©ZÆkÿ7 iÚÓù§»äµKoÕuÕ]îìx~î1¹††ýü•-|Åc Ö˜™žªQìû ·ÐÃYަƒ<)ø`‚0[žGÛкs³i™ŽéS“ŠÄD÷ÂÅ-£‡ÝrUÏÝ3Ö7rWînÀ  jUm©œEc WOÝ9™TfÍîXnîÇ ”Tîj1ƒŒôŽjÓ¯3}Øý” ií˯INÊ:8i£ò!¬À0¸ÚÓi™—j×lD*_-]ú· 31„q£—x7{Èxv1‘ÏP²K½¶+ŪªÝ9sc©Œ°¹¼ÛžD{r¡ÙË„—uuÊóÝO·=Æ62˜F×ÂÓO¨×ÕËTŸeDÖ»…v+kû³‡äxÍvî3¯‘ÏÖƒ­u·ÇvµU[çs›TNÎiÔ9„Šmš®îûme}ߺ]m$DÆSˆÚZY»®§^ZòÓ’ é,öÚ·‹uØCo’s;©_LZ\ræ±ùç˜8Ê ·gë¨ëê%µÝ…--Tæy©ä¦açÅŽÈÆqÒ Íb¸Aqª«³Ý›BÚ× 'ŠJq3wðóu$žcDÜ-·Š–ˆé¯1Å¡ÊÙhÄ…Ç.0Á=Z„¶Åq¢ ¥ ´Ý™MMON ,ž”L]ñg#_V¡FÏlŠÍh¦·B÷>:vn‡;™é'ÞP\AË¡±ÇHë§N;.u™ì-ÀhsCKyë çëA^×d»[ =3oœ[u>!}0âM2gëÆtA¥VÏÜn5¶áwdÔpT¶¡‘22BZrÐ_žC¬J@€€€€ƒŸ_jîë•¶³¹Ü¾@ÍÜïï0·è惟wÙÚûêžåOxm8¦f!†JQ+XãÍã.1g¡VÝO[MJc¯­md»Ä‰ˆc«tƒ™6ÏU¶åWUoºH«È5QpCÉp¥Ì$ÒFšƒÖ‚´{#5% ž:—ªÐئ’ö½¯Ð‡7=XÔACG³P\jî÷FÉÂPù¤¨h`.ÜÝ c¬ ݪ »h©§ÙW½“ÉMUZwã–H÷‹-<ðÆ=eFßcªŠðëµÆ½•U<ç`ŠœDÖ°Nu$œŽ½vi+^øžØßÃyi ~3ºq¡ÇJ5jÙ‹Õ¦>;Ešù8“ÛÚ_3‰Ô¹Ûú“ËÔƒ±wµwÕ”ãp»š®:íÃß¿­ÿÍ•¨ØÚ™-Õ¶˜/.‚×Vdpƒ¹ÃŸqÉùòwµÆ3Ñ”gÙê±uŽão¹ŠY»•´³oÓ‰šÓ[¨ÁÔõ„ë•·g62¢ÏUZftÂgSµß½–G;y¡ spqýè:Vë=‹fi(éë[M\ßÚÏ+áÈâ\ðFG2N¹èA=šÍ%¶¢¶²¦¬UU×=®•ìˆFÁº0nO¼œ ‚ªÁXoU* ¡¤îÈ™C ä†ç„Ÿàô‚ƒr ŽÓ“g¤¹2Šž˜I;kªǸe§Å'ÎIèÑËlrí-Î×U\ÚÊHrÜ)áxãÆñ@Ðî5k„Z;g}a¹]nB¶jf9”íŽY½ç“—1Ôƒ¶€€ƒç?¤oïºOôÇùÊôø/V~o3Œõ¡äÖö%ºÈé„ñÏšØö‡î8`‚8=#©s½fؘžø^¶ˆÌO½}·ª&Ò2RUr#5C\àÌàžŒ®\«gvcéÿõÓ›\cõrªjWU5L˜ß™åîÇ,“•Þ±¶"­;§(••v¶7ÿ6Pû_üŽY¸¯e>}í/µ‡µÚíñ“ýJøI{hù¯¬à½œüÿ¨rלڒžwÓNÉ£òšsíWÓÔ;E«îRô‹Ök.èÚ(8y0É¿ôtǽ{é*mðœ¼é௞éîq*êŸWP餯]È€¼m[jß|½ =8Ó®ØB¹:.Ù¿½¡ö;ùJ×ÀÿÑ_ßøgâ}ŒþßË·¿ßÐ¥oó9züG¬Óè¿a??êmpz‰)åTG1’pܸñìt18œ«zî¬Æq—£ýx¬71†”·w¹óüs×ý~|çùæ~N^Üþn¯74‚YŸ cXâíÖ äò¥Â{Þ¥cmb2ÑB^«`?¼ëÈgó£‡õ¥äú[Ù׿÷‹cçÄ?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р Õìd€´8œPl€€€€€€€€€€€€€€€€€ƒW1®s\Z ›ä’5Ä Õñ²FîÈÆ½½N2Ö†´5 91„@@@Aó¯Ò0=ù£8ÐÓŸýåz|«??éæñž´<’Þ íìh'k(p9oŸö—ŠöRÑÂûX{M¡þÝÿô¿©_é/k/íõœÿã˜ý¨r×œÚ ½fþö‡Øïå+_ÿE៊Ÿü3û.>Þƒßè4îQüÎ^Çë4ú/ØOÏú‡šYÞ €€€ƒÕ~Áï•iÆœ ÿî+OëKÈô·³¤~²÷‹[À ýÙûGóA" ;Ëo´ •’óíB€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€‚ÎÍo¼DØë©Ä¡‡- –¹¾Â5W¦¥´ç5—;éÖñ‹C™ú‹³ÿT—ñí]{V¯_áϳitûŸ¨»=õIþÔíZ½ƒ³itûŸ¨»=õIþÔíZ½ƒ³itûŸ¨»=õIþÔíZ½ƒ³itûŸ¨»=õIþÔíZ½ƒ³itû¯Zöv×g•ÒÑSnHá‚÷<½ØêÉä©}kêGæ•é¥JOå…ºª*zÆ<{Û¼ŽpBÇ­¡§«žtõo§?–U{ÇoóOùŽ\?áú}åÛµëuûAÞ;šÌrv§ÞN×­×íxíþiÿ1ÉØ8~Ÿy;^·_´ã·ù§üÇ'`áú}äízÝ~ÐwŽßæŸóƒ‡é÷“µëuûBÅ-º–ÅÐE‡ ‰$ÿ×K†ÒÒœÒ;Üõ5µ5#óJ;ž‚îÆ¶¶&ç’àâ×7ï µ©[zÉÑâ5t':s‡;õ*ÅõY?þÕNMâzÇÒ?Ãõ*ÅõY?þÔäSÎOĸž±ôðýJ±}VOÄ?µ9ó“ñ.'¬}#ü?R¬_U“ñíNE<äüK‰ëHÿÔ«Õ¥üCûS‘Cñ.'⤎ºÕEj…ÑQB#kŽ\rIqõ“Ít­+XÄ2ëkêkNo9\Vq ýÙûGóA" ;Ëo´ •’óíB€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ƒÈí®Ñ×Zf§£ sb|Œ2>BÐãŒàkg £]L͘økiâ*ò¿®[Cé#òcÿŠÛÙtº,}«W©úå´>’?&?ø§eÒéü«W©úå´>’?&?ø§eÒéü«W©úå´>’?&?ø§eÒéü«W©úå´>’?&?ø§eÒéü«W«¹²{Ys­½GA_3j8vë·\Â=ÓB³q=)MÕiáõïkí³Ò^®3ÒË080¹»ÅØÏܾoŽâ¯¥h¥;½ïs…Ц¤M¯ÞæwâãõŸö7±yý·ˆø¾ÐÙÙ´~äïÅÃë?ìobvÞ#âûAÙ´~äïÅÃë?ìobvÞ#âûAÙ´~äïÅÃë?ìobvÞ#âûAÙ´~äïÅÃë?ìobvÞ#âûAÙ´~åzÓu©šµ´ó¼Hè3еð|^¥õ6^s–~#‡¥iº‘Œ9û_´5ÖʸhèžØœèø´8ã$3§A^޶¥«8«¯£øM=ZÍõ;ûðó¿­—ÿI’ÎÅÛ~¯O°pßÞOÖËÿ¤ÉgbsoÔì7Á÷“õ²ÿé#òYØœÛõ; ð}äýl¿úHü–v'6ýNÁÃ|y?[/þ’?%‰Î¿S°pßÞ^Ÿd/õ—GTSW9²IZöÈ œ`ì]ôu-lļ¯Hpºz1[iøKÔ-,@@@ƒ÷gí͈0PDï-¾Ð‚TKÎ?µý  ‹´6zXæ’g|Ó6a„ùî@dÐyõ ³I3êi£–Jy)ÞᓘÞg¨à‚dZ ´ÕÑÕUÕӱޤxcÉäIÓÞ­5˜ˆžªÅ¢fc¢ÒªÌ e9ARët¥³P:¶±Ïl-sZw^IqÀ N¥6Ëå-ÖY#‚ÈÌ`j):ãBà2PtPq¥ÚŠHmõµ®†bÊ*Îãxd¿y­È×–\v4A”òA”A”.W*kMëkæAïk ·rq“Žu=w:Zib™ç‰Y/ ±¥ÅîÁ= ’‚Ò   áϵ֪ižÙ{©°Ç'Jžå“‚Çg/Æ9éžH.Üï4V–DjžòùÝ» Q0É$§©­n¥UAEIheΦ:¨b{Äa§p—x»nc:”Úï÷a)§Šª>ÝÑLø³ž­à3Éäé.Ö\«èXlj(\ÆÈçcÞnðÇÜ‚ïÜPaP`Ö—€J56ÖZªªá§cªÝ.ݧ–Zg²)Ôׂ4ÜvžÛkª}4æy$‰‚I¸:A '<´x¡Rcž&KÃã‘¡ÌsNCäB –«¬7zi'’5±Ï$$ ÕŸ›Ìã=hy5½‰55%El¼*h]+ñÖŽŽµ[Z+™Zµ›N!iö¬lsÝDðÖ‚IÞnƒÞ©ÚsÝ•§Jñ˜SššzqšÆ%`{ ›æžD+Å¢|˜˜ñF¬‡ocóeµÿÈ囊öSçÞÑÂûX{M¡þßù?Ô¯ˆô—¶—úúÎ ÙÏÏú‡-yÍ¢ Å Q‹ˆ)äÜÆsº»r5vîÛ8sæéç@¸º.Ù¿½¡ö;ùJ×ÀÿÑ_ßøgâ}ŒþßË·¿ßÐ¥oó9züG¬Óè¿a??êmpz‚U°Þu¿ä3ùŠÑÃúÒò=-ìëó{űà~ìý£ù ‘ å·ÚJƒIyÇö¿¡@@Aå¶Ö†:©ìEòNÝëœq‘Î`µç#C§>h)Þ©¤·Ü娺¾èm,Š6ÓÔÒU¢x®½_8•µ’³tC)ÆáN9þI7ÛJb<åM×¾gÎRÖU\é,t“TÊÑUžHÜZé79 Fyœ+Z±K^Ñ­¦õ¤Lø±Xj©™w ¦©Í¥‚:¨é œÍI,ÞæAÝéë)]³¶ÖâÛ«º±>é ÊWßc¯lÏ {)\Àï—³{¢Z2«ËˆÓšã¿Çé)ß;âùîðúº[<馴2ªw½Ï©{æÇ;­sŽè¬as׈‹â=κ9šf}î~Þ—˜&6‡˜Xöž–‘‚x› ¬‡¾Uµ¯mD›9”T½;å­Þ/>²ÝÆýÇ­VnþÈëE K³äì}tÕQº cpñ‹Y¾wqFÐ ¸ÆIA´.}úK“5oý%\U.îg4Ÿ'07«kÖƒÙ Ê H<ÆÙGu–ÕXÜSºÒ"ÞªcEC£¼4ŸhUÕ×í¬.¶Í ²ÎÇÃ,Ñ—–1ï×u¹œ4Nˆ&kn;Mlu<“ÓÓ×Ú®`>QtRõw Œ‚4΄ ¿j¹Ü…ò¢Ëu4óJÊvÔÅQNÒÀöæ’pAz ‚ød¯Úkm•ÕÁI$ÔL ÆéKH nðÔ s¢u¹ôöZ½­ueuGsÀèŸxºPÓÀ™pÈóAž-¥¦·±·z:k…ûí­ªâ8–îáíñ‰k†}H7¦¸×\(lö7ÔJÚöÖ:éòY®9øþ'·yÑ6J  Þ¿Érd“֞䫊¥ÝÌö“âD惆é¦×­³A”Aæë_úÇ|¦¢¦ÖŽ×RÚŠªŽƒ+|˜šzH'.= ìðc®ÛMÆÁq¯Ùú&ãîÆPK°…Çbí{Ç8‹ û;Çwøa—§ xÙ+ÝÖ:úÈj)*ªä§Îæ22דä“Ï9AÜšIo›AIm¨©ž VÛXöSÊb2½ÎÆ®n¸]e:²¾º—g¯”M­¨y¶Ü`†ƒ!âpÜøÎéw3â3Ò9 ïÝ*%fÙØ©Ù3Û¬©/Œ8†¿ ÈéÂvÌRTÜ"ª¸Ïpª’xk*b¦k¦w0à2ÜáÚõçA¦ÍŽâ¹ÁIwuλ£vLÕN’ž¬)Ì×wNxÀ![ijiY5$æ¹òÕK» 2I)’7²0Üju<í-]Ö+.ÔRÒ¾®)(K ;*§K \Àç ü‘Ë$dœe‹DMuð×ÛÝu’Û~*æ˜5óô`<øÇw9è儉ªƒbáÚÁs«}ı•/wœá˜ø~Nî9e½#8Ç©P|çôý÷Iþ˜ÿ9^ŸêÏÍæqž´<šÞÄèPikºzáŒý@¹_Ö¯ïü:SÕ·Ÿ{¥³–j-Ñs¹½±ÀÐ_,$Ó‚sŒg<‡Þ¸kêÛÔ£¶†}{©mÎJê÷ÅÂu<=Â8uŒž~Ìõt.º:qZçÆeÏZókcÂ!Ê]Ü]­ÿÍ”>×ÿ#–n+ÙOŸ{G íaív‡û|_äÿR¾#Ò^Ú>_ëë8/g??êµç6®Ú"Žk”mH¤…«ƒ¬[Z"Yø›Mt¦aÝ5µ™ [d gˆ±:úÑ8åÏÖw+Oãq¯qGÀîÝæ‡8‚¼®:•®¯w½¿…´ÎŸ{œ±5.Ù¿½¡ö;ùJ×ÀÿÑ_ßøgâ}ŒþßË·¿ßÐ¥oó9züG¬Óè¿a??êmpz‰)ÞØªb{â5¯ÆÆ:”ÇŠ·‰šÌDáôGYmŒ.½w±æN÷rîŽxú<³í•õðù¨âu¦9û³ãçÜùÔÏl“=ìŒF×8ÁÉ£«îXg¾_MXÅb3–Šõ[ýç[þC?˜­?­/'ÒÞο7¼[> Aû³öæ‚D("w–ßhA*åçÚþ…PS¯¶Aq}#§/ÿ¤¨mD{ŽÇŽ=cS¢ Wš†ç4Æ¢áqóàKJÊŒDñÒ1Œ€z@(:íkXƱ 5­t æ>Ç ³‡ÍWW,bN óf0àr4ÆpFWXÕ˜Žè‡.TLæfV ¡†ž¢ªvooÕ¸:L24êÑRo3ŠDLÏU^ðÒ }=]3)Ì2±ø‘‡]AûÊ¿6Û¦Ý|TåWlW¢j;\l˜I3ê?{$ÎÞsôƧ« ¶Ô›côMtâ±?ª»vz‰–gZšeà9ÛÅÛþ>sœç¯@­:×›ï÷«5Šl÷:0BÊx#†1†FÐÖŽ ç339—XŒF!^él‚ïF)jKø|FIâ;-ppþ!BRWQAr¡žŠ©‚H*XöòÈ(9T»'IOYIW%eu\ÔN&TO¿¸ KwqŒcÛ ÕÒìÍ´5tn3pë*û®L?]ýæ»CZ4AЭ£†áC5SÎÃëšU&ÊQÒÕÒU¾®ºªj"x¨Ÿ{q¥»»¸Æ1ƒíÐjƒfìÍ4WÕÓVWR¶I¸ÒÓÃ>ìR?¤–ãLôàŒ Ì»3Jë„•´õ•ÔnâI⦟r9\1©:é®1” Íš§ª®–¶Úê §NhæÜã@\0uÆ™(6­ÙöÖ9¸º\©Ú"9T`=£¤äuæ5A¬»5†)+ëè!‚ b¥Ÿu¥£–A__4 {e 4TŒáÁ wXÜçÛÒ‚Ê TVªj+_ñ5Ó™¦9ÄqÔ09 £C²ôÖéâu5}Å”ð»z:>èÌ,õcÇ«8@;/O%[&¨¯¸TÅÜfSMQ½\F˜Éò í  àM²TÕ!ñTÜî“RÈ쾕õDÆáœî3»êÊ — 5ÆH&ÔRTS´²9édÜ{Zy·‘º èAvj–+k(ijk)we3ᜉ^ó̹Ç;Ùõ š×c¦µË5Ce¨©©¨À–¢¦M÷¸C<€@ ÍÖËOu|¾Yéç¦q0ÏO&ãÙ‘‚3Òæ ŒÙî¯ãÊÛŽé¨t’—9ÎhÑÙè=(ìõ%º©·ijk«jib{D“Hdya´4 yisÖ‚®ÏÐ÷NÑ\ö„ÒMLÊÈ©Ù;Kàß~éå¼CGþÔÿUéŸWõ5÷ ¨â›ŒÊyê7¢kÁÈ8ÆN:(;H2€€€€€€€ƒIcâÂø÷œÍö–ï0àŒŒd´~‹c)íÑÇ-ÞñQ¶!WâóÉÈÝ×==h-\vb’ãW-OtÖR¾vê,Ü1;G í:‰8AÕ§§Š’ž*zxÄqDÐÆ1¼š ƒžÍŸ¢ŽÏYj^ç¬t®—Çñ¿hIv4ç¢ kvz–³¹^Ùêi§¤‡E<›²`e¤ãa³VÑdžÒöI$$¾g¾Bd‘ää¼»éd }H4¤ÙzZ[=ÅõuÕutÍs#–¦}òá‚9ŽŸj t6Šk}Ô:PÉä’Gÿ9ä—`ôjtêAZ‹g ¤®Š¶jêêé k› «Ÿ|G¼0H HÓ%×k,sLù&žžzW—Ã=;÷^ÂFA#˜(8ןÛÁ¶Øj*Ÿp|b·xñd|~KÜÐy»t”Ùâ·Ú>õÏ{’…¬x«7þîîÞÎ9teI»nlo¬Ò2^3hLÙ§k³Ÿ'³®3„ô9ý#}Ò¦?ÎW§Áz³óyœg­&·±,ÑÖ¾‰ÒnǬ•»’G+wšáœþc*–¤[ VÓU±|-cXÛuk¼Öî<´¼aså{÷JüßÒée’yŸ4®.|Ž.sI<×hˆˆÄ9ÌÌÎe¢”;[ÿ›(}¯þG,ÜW²Ÿ>öŽÚÃÚíöø¿Éþ¥|G¤½´|¿×Öp^Î~Ô9kÎmm†FÉ·\ÓU«i¤æ¨´E£ë ¢›‡ƒNÂüy[ÇåéG¤¯·{Øg‚®|{œ¹¦’¢WK+·œîey×½µ-ºÞ-µ¬V»a¢Ë¶oïh}ŽþRµð?ôW÷þøŸc?·òãíï÷ôé[üÎ^¿ë4ú/ØOÏú‡›\¢H'’šxç‰Û²Fàæœg)‰Äå[V/Yð—TmUØ\woîð±û<{?ŽWNu÷e“°hòùxýýîLÒ¾yŸ,‡yò8¹Ç¬•Ê{Û+X¬EcÜÑõ[ýç[þC?˜­?­/'ÒÞο7¼[> Aû³öæ‚DKPȈ%ÎäÖŒ’‚>êÕgøGj ÷Sþ«?Â;P;©ÿUŸá¨ÔÿªÏðŽÔ5Oú¬ÿíA¨“xäŸCÔ;PIÝOú¬ÿíA¤µ8³³èìŸu{P;¡ÿUŸá¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÐÿªÏî¨ÒÿªOî¨2*š‘ÉñÀ/n½È2€ƒ2€€€€€€€€€€€€€€€€€€€€€ƒ2€€ƒçŸ¤xÞ.´r–ᎀ°;£!ÙÇñ^Ÿ1¶aæñ‘ù¢^C#­oa2:Ð2:Ð2:Ð2:Ðwv*7ɵt…$Fçãü#pŒŸ¼¬¼\ãJZxXΤ=žÐ‚+"q)úò{WÅzJ?òÖOíõ|擯ôådu¯5´Èë@Èë@Èë@ÈëAzÊ ®±3ºN:4ÿê¶p1ž"¿¿ðÍÅN4¥ÈÛæ9·ªy0ÇSn‡D‡â½~#ÖËO¢¦'FÑïÏôóagz¦GX@Èëa#¬"^·ôut ~ÌDÆotg$ãÜ´ðþ3/ÒÓ)¬½ÒÖðD?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€ƒW;j<>Z‰O•Ä,öÐ‚Ò   ˆ Ê $²ÆèÞ2× ‚¥ ¦JXËŽN0O^4AePCQKOWЦç9Ý‘¡ÃÜTŦ½ñ(˜‰î•^ðÙýGøvö+soñOÕN]:}ŽðYýGøvö'6ýgêréÓìw‚Ïèš?÷±9·ë?S—NŸc¼DÑþ½‰Í¿Yúœºtûà³ú&ðíìNmþ)úœºtû,RÐQÐäRÒCO½Ï…n}ʳi·Œ¯ŠøBY"ŽfîÊÆ½½N\ïJÞ1hÊÑi¯|N÷¾êü°¹öm‚>‹óµ>)úï£ú¤?,'fÑø#èsµ>)úï£ú¤?,'fÑø#èsµ>)úï£ú¤?,'fÑø#èsµ>)úï£ú¤?,'fÑø#èsµ>)ú¥Š`Š&Gž{­*ôÓ¥=X¶½­ã9kQKOW ¦æg=Ù?мÄO‰[Ú“šÎ»ÅiôUÈob¯.{V¿Ç?Y;ÅiôUÈobréÑ=«_㟬â´ú*ä7±9tèv­Ž~²wŠÓèª?ÞÄåÓ¡Úµþ9úÉÞ+O¢©>C{—H÷#µküsõ•¨) ¥ŒEO !`äÆ44¸+DDwC•­kÎm9J¥QÝŸ´4 Šcâ”í§-Ÿü÷DP`œ eËöãôÇв[e’ë*â;²Í&xq»¨åàƒçîý3m£ž\+àh'ɬÀ÷„ »ké? ÎÄ »ké? ÎÄ »ké? ÎÄ »ké? ÎÄ »ké? ÎÄ »ké? ÎÄ »ké? ÎÄ »ké? ÎÄ?¦Mµ#ãõŠXûiá‹mý-ábÿІ-·ô´…‹þ(¶ßÒÑþ/ø xbÛKGøX¿âá‹mý-ábÿІ-·ô´…‹þ(¶ßÒÑþ/ø xbÛKGøX¿âá‹mý-ábÿŠ xaÛK³ð‘ÅÃÜz]Ÿ„‹þ(vãÒìü$_ñ@ð÷—gá"ÿІ¸ô»? üP<0íÇ¥ÙøH¿âá‡n=.ÏÂEÿ ;qév~/ø xaÛK³ð‘ůý/mÃÆ;òÙ¦ˆý¨#ð¯¶þœÉþ(ößÓù1ÿžÛúqÿ&?ø xWÛN?äÇÿ ûoéÇü˜ÿâá_mý8ÿ“üP<+í¿§òcÿŠ…}·ôãþLñA‡~•vÙÃû öEÿûPEá7lÿî Ÿs{cÂnÙÿÜ>æö xLÛ?û‚§ÜÞÄ »gÿpTû›Øá7lÿî Ÿs{<&íŸýÁSîob„ͳÿ¸*}Íì@ð™¶÷O¹½ˆvÏþà©÷7±ªÒÖÙÑÌ뱩oK'‰®ÜAöOÑÿé“m!}<‘ [Œ-Þ’ìµãé7ÕêAí2ƒ(0PríŽÍ,æPt   ðÛv­¥ª¦¢¦¨’ß‘æ7n¹Ç8#\¿ƒÓ­¢faƒ‹Ôµf"%ãûçqôgâÚ·ò©Ò>Œ<Ëõ“¾wHÖ~!ý©Ê§HúËõ“¾wHÖ~!ý©Ê§HúËõ“¾wHÖ~!ý©Ê§HúËõ“¾wHÖ~!ý©Ê§HúËõ— Ø»ÕÃõ‚9jæš €àæÊòìÒAåÉdâ´©{¢;Ú¸m[Mñ3Üõ—ú¹£ž8#‘ÌifñÝ8ÉÎÊzGZõ¼R³ˆÆ_IÁéÖk6˜Ë“ÝX›æ;µyœÝOŠ~²Û²Ÿ }è¨úÄß1ݪ9ºŸýdÙO†>‡tT}bo˜îÔæê|Sõ“e>úÑQõ‰¾c»S›©ñOÖM”øcèwEGÖ&ùŽíNn§Å?Y6Sá¢ýš²~ø2J÷²@râp@Îu[¸}NtVg19fâ´«ËÝßnÜ]+ ¯‚Š ‰!‹ƒÄw ťĒ9q¢õ5í18‡oFhiÚ“{Fg8ïy~øWúB³ñíY·OW­ÊÓø#é|+ý!Yø‡ö¦éêrtþúAß ÿHV~!ý©ºzœ?‚>w¿ÒŸˆjnž§'Oà¤ð¯ô…gâÚ§tõO'Oà¤=nÃ]+*eª£¨ó265ì2;yÍÉ Œž…§BÓ9‰xÞ”ÐÓ¤VõŒg¹ìV—Œ Aû³öæ‚DAÞAA^ÙäOþ{¿¢ È ¬ã÷ÝÌ@Ÿ†î<·±§ñAó9«­vÍ•¬¸wMt[KEJçOUL‚C)¤îºæää`c’ƒ½î{‹œIq9$ó%w¶é%Ÿl­uq¸·-cõÆZãºAûŠÑÛ;$’^¶…¯‘îk+šâCG º¤BM­Ã¹ØÜŒàœg^eþ#×= 7Ê%ÀcÛÔƒfHÉZÚöž–œ„ Á;­'àr8-Úiã«¥e}’®Šš²Q 3Êöù!Ì-ÎwœàÑ’p2zj&ÏsŒ.n®nðȰ±ÊÝèÞ×·­¤üa³F÷¹‘Žs|¦µÀ‘íž&yr±ºîêà5êö ‚®åKE=4†IVóC¬€Iû°U†ô$¿W[¤dqÅK2‰‹ü­ýí:¿Âƒ¦éÖo¹ÍkF¥Ä€=è {$htokÚyœ„ºx˜ö±ò1®w’à ö Ì“Gw¤{XÜã.pø 6hÞ kÚà9àp¬•»Ñ½¯o[H!è>súFþû¤ÿLœ¯O‚õgæó8ÏZMobX£¢’µï tq¶6ï¾I]ºÖ ãSíTµâ«V³dæÔ;·;{ÝÐÖLIqêwš¯3¬JÜ¿ÖÖ[*è"‚Zˆ÷YPÝæsí¨Ž¦º•¼ÌG¹¥«3ïT]v¶7ÿ6Pû_üŽY¸¯e>}í/µ‡µÚíñÿ“ýJøI{hù¯¬à½œüÿ¨rלÚȤô$DÏ€¼,•Æ-þåÝá•¶8y®pÍÚ´³Œ¨¹¥Ž-p!ÃBÔ,s‰h‰‰ï†%vÍýí±ßÊV¾þŠþÿÃ?ìgöþ\}½þþƒý+™Ë×â=fŸEû ùÿPókƒÔmǸíÿ£ƒŸrœ#tc>æª õ[ýç[þC?˜­?­/'ÒÞο7¼[> Aû³öæ‚DA ÞB-žDÿç»ú ¼‚*ƒ+iät l’†’Æ9Ø=ž„#il7¶£Ž†¶Ñµ±?ºPÙ\Né­ÀÐFsÔƒó½} E¶¶j*¸ŒSÀòÉzA]µýìcö®þQ»ÝJ §,’0^uû}Ëcí7[P¸÷Ùì–YªdÍvx­kCˆè$”• ž[äî=¿Êì1­u%[ÝæÞãºO±À{ÐMMb¨½ØqS]^nž©¤Å3927ã\nžœ„í–ž’J:¨)­Œ¶ÉMRèª)ãÆëdÀ$‚4 ‚ â Py¦<íMÞ †a¶‹tÜHßõ¹ÛÿÐÝué> ‚ö×k±÷ôRÿ)A櫬Øß²Ü:V±ÕSê^Ý;LEÄ=ÜÜ š Õ1öZ­†Í¦ ·ES<Z½5Yò˜à ÕéªÏ”ÄV¯MV|¦ Ï€+O¦«~[<Z}5[òØà Óéªß–ÄVŸMVü¶ x´új·å±À§ÓU¿-ˆ­>š­ùl@ðiôÕoËb€+O¦«~[oàËéŠÿ…ˆ1àÉ鋇ÂÎÄv_LWü,ì@ðeôÅÂÎÄvOL\=Ìì@ðdôÅÂÎÄv_LWü,ì@ðeôÅÂÎÄðdôÅÃÜÎÄhA{5O0}MUu[Gøö°„eÐmÖÊE(íôÑÓSÆ0ØãnÿTÐ` ñU¶úªëv 6Ž¡¡’Ôl,ßË€o2ì =¨=m’Ûq¥†š®‘’Å8M9˜ÐŒ¦ˆ& ·QÛ)…5 ; „w9“Ìž³ëAe’ÄÉ¢|R49ikšzAЄXö'f¡{ž…„ã{LréAת¥‚¶šZj˜Û,34²F;“æ ä·QÈiKéØM· ÈýÙÆîGÝ¢ ™CJʹjÛ󱬒Ljö·8Ù“ïARfìÖúÃWIn‚ð@{G“ž{£“sê qPRÀú‡Åê§oÎGÿØÆOÜ0‚´û?i¨¶Cm–†'RAŽZ9`óÊ éíÔtµ¨‚²gÆÈÜñ̵¾Hû²‚¬û7g¨¥m4´:&ÊéZÑ‘‡¸åÄs®P[¡·Ò[)[KENÈ!i$1ƒ¤ó>³ëAR£fìÕUî®žÝ •sÜ<­1¨äN:J=âÄ÷Ýas쬻[!¥lTô¢V³¹Þ áÄ–îŒç#´¬: ´ÖÑÜháe5]A‘”;üF@Ìž¼Œé 'DKmšÝhkž’88‡/-É.êÉ:”|çôý÷Iþ˜ÿ9^ŸêÏÍæqž´<šÞÄš–²¢ŠS-4®‰än’:GQB«jÅ£µm5œÂØ¿ÝȪÁè"6>ü.|>‹ó¯ÕÏs‹œ\âIqÉ'™+¯‡s”ÎXR;[ÿ›(}¯þG,ÜW²Ÿ>öŽÚÃÚíöøÿÊþ¥|G¤½´|¿×Öp^Î~Ô9kÎmd×4F Ž„‰˜ï„Ot¯‹Ýps}¤ý"ÝVØãõñŒ³O ¥3œ(¹î{‹œâç8ä“Ò±Ìͧ2ÓˆÄ5P•Û7÷´>Ç)Zøú+ûÿ üO±ŸÛùqö÷ûúô­þg/__Öiô_°ŸŸõ6¸=FÌ{£{^ǹ§!Àà‚‘ÝÞ‰ˆ˜Ä¬÷Ö¼Wšî듺HÇ:ã«Ø­¾ÙÎ\»>–Î^ÞåW9Ïys‰sœrI9$ª»DDF!„«`?¼ëÈgó£‡õ¥äz[Ù׿÷‹cÀ ýÙûGóA" †o%{c€5,>P˜’=D ¼ƒ(0€€€€€€€€€€€€€€€€€€ƒ(1€€€€€€€€€€€€€€€€€€€€€€€€€ƒ{XÂ÷5£$õ å[±äc#?ÅH ÊúE¢¨um-kb{ Üæ·;§{:û×£Á^"&²óøÊÌÌL<^ÑwÂW¡˜`Û&ÑwÂS0m“è»á)˜6Ƀô]ð”ÌdÁú.øJf ²ô[EQ6ÒARÈŸÁ§/yie¤ž½VN.õåÌe«…¥¹‘8{ ¡†NèŠ`ÂY¹ºHÁΪøïIRÛâøîÃéø+ÆÙ¬¹öû—”߃>ßr3í÷!ƒ>ßr3í÷)0èY"‘÷Jw#Þv4Ôanà)iÖ‹DwFYx«DiÌuröö’~ùSÖÜ`0pËÀÈ'«šõ¸ˆœÄ´z+R¼¹¦{òò™õ;á+3×>ç|%îwÂP>ç|%>§|%°Ø9Û=][ãsa{áâ ' W™—‹é]JÌV‘=ýòöËSà ýÙûGóA"¤jeD/l¼hžè¤ÍéDt „Ô܇ÿå7äŽÔê¹}i¿$v wUËëMù#µº®_Zoɨ0j®XþÔß’;PiÝ—=à;©šŸ0;PoÝW/­7äŽÔÉYsnî*™©ÇîGjv\¾´Ï’;P;²åõ¦|‘ÚÝ—/­3äŽÔì¹}iŸ$v weËëLù#µ»._ZgɨÙrúÓ>Hí@îË—Ö™òGjv\¾´Ï’;P;²åõ¦|‘ÚÝ—/­3äŽÔì¹}iŸ$v weËëLù#µ»._ZgɨÙrúÓ>Hí@îË—Ö™òGjv\¾´Ï’;P;²åõ¦üÚ‚íºá+ˆ§« Ê|‡†à?ÕŽ‚ƒ¨‚9¦dºIÆŒ’Pq$¸WË3Ÿ†3ä°Æ}¤çŸ©½ÙrúÓ>Hí@îË—Ö™òGjv\¾´Ï’;P;²åõ¦|‘ÚÝ—/­3äŽÔì¹}iŸ$v weËëLù#µ»._ZgɨÙrúÓ>Hí@îË—Ö™òGjv\¾´Ï’;P;²åõ¦|‘ÚÝ—/­3äŽÔì¹}iŸ$v weËëLù#µ»._ZgɨÙrúÓ>Hí@îË—Ö™òGjv\¾´Ï’;P;²åõ¦|‘ÚÝ—/­3äŽÔì¹}iŸ$v weËëLù#µ»._ZgɨÙrúÓ>HíA“ÝUXmLåìάkCAöõ é@¨,„@@@@@@@@@@@@@@@@@@@@@@@@@@@A«žØÚ\÷ÒNDÎ÷]?Ö"ùƒµNÙèÕ;®Ÿë|ÁÚ›g¡º§uÓýb/˜;Slô7TEójmž†ê×Oõˆ¾`íM³ÐÝVì‘’Œ²F¼–¸ê¶sàØ¸4HÖ¢f#¼†œVyÆüAFøê³Ðâ³Î7â ¾:›g¡ÅgœoÄ|u6ÏCŠÏ8߈&øêmž‡žq¿MñÔÛ=5ÍpË\°å"b|1ïk\àÑÖN±38„}ÓÖ#ùFèê·.Ý$î˜>±Ì º:œ»t“º`úÄ0&èêríÒNéƒëüÀ›£©Ë·I;ª¬Gónާ.Ý%#\×´9®¤…*Ìa²»?hþh$@@@@@@@@@@@@@@@@@@@A„#!2B‚L3É;”u w(ê@îQÔ€iF9 ˆÒöéþ ‚^åH#š”f=?Åý rޤåHÊ:;”u w(ê@îQÔÜ£©¹GRrޤåHÊ:;”u w(ê@îQÔÜ£©¹GRrޤåH0ê6½»¤DET`:§r,ŸKÔ}š¥ŽJ·‰%­iË#êõŸ_äÜ£©¹GRrޤåHÊ:;”u w(êþÊ:;”u w(ê@îQÔÜ£©¹GRrޤåHÊ:;”u w(ê@îQÔÜ£©¹GRrޤåHÊ:;”u w(êA#)ÀèAa­ÝA²   ù÷éy]p£¥/< “s »{=zépQ™yÜe§1Ãg›g ÀpÙæÙð„<Û>Ãg›gÂ8qù¶|! Øyä§Úzh¢vã&kÛ#G'ÒFžÐ²qu‰Ò™jám<È{=¢{UDå‚=ìzóÿÑ|g¤í3©Zû±Ÿ»êx(ˆ¤Ûõq÷ôG¹y{c£vg«;ú#Ü›c¢s=MÆýîM±ÐÌõ7ôG¹6ÇC3ÔÜoÑäÛ ÏUû#‹.q±š5áÁà镻€´×^"=ùþx¸Î”Ìû°äíü¯uÖž™Î&&Á¾Ð\\F}ÁzÜDþh†EV#JÖÿO+Âͳá ;×Ìõ8Qù¶|! ÏS…›gÂÌõ8Qù¶|! ÏS…›gÂÌõ{ÑüÒ šÊ`ãÁµá䌅§‡žù‡‹éjÆÚÛßßrµ¼!ÝŸ´4  ` ` ` ¢œû~ÐA.æÇö¿¡@À@ÀA «¦mkhŒÌŒÊ؉ñ‹AÁ#ïA̼íMªË†z†>X¦dO‰®Õ®v9õa§{؃©ISO]LÊšY[,2 µíääîW‹mŒ}ƶa!Ã7Ý«½ƒš j/–šJkª+àŽž|p¤.ÒLýÌ Ù·{s­Í¸¶¶Hâœ?ÄÉ8ûNVnÔØ'-»R‘OûÏÚr×Û®šeˆ¯V¹í®¹E]èÙåLâ´çõsAÚ‹ Ÿ3Ú—z]'í1œg×®šeŠKŲº†Jêjèd¦‹øˆ q8õ•3[DãäZ&3”沕µÍ tìNa‘±gÆ-ã©F'Nc8fž¦ž¬Hiåd‚7˜ßºs‡cÚLLx‘1>~Ó]'³YWKRMÅŠ&¶RC|w†äã^”KUÝ(c¨©¿Él‚–&‚$§tž)Îö÷BŒµ”ÐMO ³±’T¸¶“¬„ >íPyš¡¸Ec»ÕµÑñi.¢–/Nû¯YÃŽ¨=cËçÖ´IämÐÙ®•&š†ãó]¸×j@æGXö M´z{‹mÒÜiÛVçˆw²ìž@õR^ÐÙ¨+[EUq§Š¥Ø';Æ×–z³ëAµuúÑmªm-mÆž Ÿ‚÷ààò'«ïA›îÕh1‹…|4æ@KÝ©x´Õ힆'©¸Áu Þ„—gˆ:Æ:=h.ÓÏ]ö»Ç“z³êAÒÀ@À@À@À@Â2˶ÐÖÜnq[©-¯†‚§€4’5òx¡ÜÀ sAÒµßin6‡Ü&Æ sã©d΀öœ8ËN¾œ„Ómž²–zªkŒÃN3+ƒ¼ëêÊ&ÍmCkm“^.wz6AŸŹÜä¸î‚ìåÄÔƒ½I}µ×QÍ[K_ ´ôà™ž×~ï'=Zj‚gÜhゞgÔFØêœÖ@ât¸e {B$›P-óm··¹í’1°µ­Ã˜Ã·sÒI䃳lºR]èÛUG3eŒèKNw]€Hþ(. ó›'µÚÍU;iªcq{XÒH’"âÐñŸ[H>°‚Õ¢üʽŸ7kƒ¡¥²H׸» hkËAÉö š¢³ÔQ:²+;àcÛß½£\N=Y$ ¹=e==DòÌÖKPâØ˜N¯ dàz‚ ”ûAh«®u =ÆžZ–äpÚü’GsúFþû¤ÿLœ¯O‚õgæó8ÏZMobZ¡£eWóOÀ†ÈýÂã©Àtœ•Î÷Ûˆˆï•é]Ù™Ÿ†PÛgxŠžáS,®ÑŒî}í/µ‡µÚíñ“ýJøI{hù¯¬à½œüÿ¨rלÚÚ6:YsœpéSZͧ≘¬f]oÕÙøY30?N=ëÓü6ûs»½ŠxÚçùʖ •ÑÈÒ×´ê›zM-5·‹emŒÃEU—lßÞÐûü¥kàè¯ïü3ñ>ÆoåÇÛßïè?Ò·ùœ½~#Öiô_°ŸŸõ6¸=FXÇHðÆ4¹Î8 IBgò²-uæ¸ÐŠI{¤ ðñ®9å[m³Œw¸óôös7Fœ×1Å®®¨Uv‰Ïƒ=VÀyÖÿÏæ+GëKÉô·³¯ÍïÇψ~ìý£ù ‘ å·ÚJƒIyÇö¿¡@@A綨w  ¿¶Ûàsþ+ýÚ;îAÁ¬…ñl0ºT·rZë”5ó¸ÿ®•»¹õ ÷PTÁWžšhæ‰ùÝ’7‡4ûAæªj©m›~ú«¤ÑSÅ5½Œ¤šw°òdhqÐAÇHAŠšê6Æ‚çQQ -ó[Ÿ%KœBMðNÈÞ]` áÜøU;9µ4øuºªã ÃÈïFÙÞ°]zuAè.”ð~¿X?gc¥ªÜÐx¸ Æ=™AÄ»¶É·Œ î–’Y™AÙ»ÒÓ ¨Ù6pcÄfpÆî1F=‡(Õ\e®Û Ø)Ù3¢4®Üpñræ`¹ÀttŸb [ªNßÛœnô÷"(f’š Ö7VÜ‚s׌é÷ ¢Ø¡gèj­á¬ÜI ÓÊãsÏ^ƒÜƒ¶k)-›yYQt¨Š³[â²ÎðÖ×;} 3’ 7Ø ³VvîÂnU&6ãnþšt õ$rC =]mß7ˆñs¯tVejzf_*v”ÛÞÙ)¦¤‰’3–>¡¡Ç ò$x «²+Ÿé3ù¦ØðUeÃÛ'¨ØOT"=ÇýÛî'îVÛÿæŒÿú=>ËR¾—g(Ä£ö²´Ï.yï¼—û×IÍ¥ÛN1XRÛæïìÁfùfõ]8ÞiÁ•ºZ溶ÒÙ¦£ØûÈmÂá^_OÚ‰÷@ œ`Éu×›uÏi6]”ÕbiãÃs ° ‰×NzÚÒ?U¶‡Qýþ:ýX{kÕkí¶jÚèà5§…ò6!þ2$2ž½ÕÛQ³R:ïI\âdqe$!¬€‰ÝÞõr:éœ él•ÆÛo¤¨·Ü*©éî}𛯠’GºBZ@:» · ÆÏÜmöÊ›Å-Ò®žšºK”sfxk¥kˆá‘Ÿ(cc’\ì-¼mÂÿIjs’æUS±ÜhKikœá5ˆõ ½XÊK;i*èö’Ž…-¹‘X[¹WÕ¤‚r2AÔ÷ ÍMm<ì·_™u¥²] =ÍXFã¢qÎéªGa¸>ëb£®}7s:xƒŒ] özºG¨ èH¤0ƒÀÛ)fdmwêÖ[_3‹ÎxL¯âGî°‚sª6ÓWü:F]]4³:. Ž>#ð÷7¤ZJµ5šš÷5ܤ¥ºO[DØ$ѱ¡„Xóºã¨("´UUm<Ò× '·ZÍ+FyU¼'Þ7Z>ô¬PA[o±SË´ô±IE,nŽƒ¹˜ÉÙ+ts½Ö ƼÐz•†.üm$á­â›‘aN@öjƒÓ ùÏéûî“ý1þr½> ÕŸ›Ìã=hy5½‰ÒµÅ$ô78ac¤‘а†0d$Àé\u&"Õ™ý‡ZFkhóâîX#m¦”Ttµ²å¢7Dðæ»>(åÜjO5›Zy“Œâ<ýÚ4cdg—ž¼O=MÞªJŠQ ´È9 zº–­(ˆ¤b0Í©37œÊ’êæíloþl¡ö¿ù³q^Ê|ûÚ8_kk´?Ûâÿ'ú•ñ’öÑòÿ_YÁ{9ùÿPå¯9µvÑ#"¹Dd8 ÐO%¯ƒ´WZ&YøšÍ´§飮.?þ$à3œpÂõçG_=ÚŸhyÜÍ/ƒîã^äd—AÜhkˆé+Êã­YÖîèßÂÖcO½ÎXš—lßÞÐûü¥kàè¯ïü3ñ>ÆoåÇÛßïè?Ò·ùœ½~#Öiô_°ŸŸõ6¸=DÔm™Õ°6™ü9Œ€Fâq‡gC•5Îcz³X¤Í¼0úk§z…•tÝ÷î+s§¯Y× ÐÏ»Þùh§þóYååó ‘+j¦»~Pó¾ìç.Χ=+Ïœç½õzsY¬M|¨Yê¶ûηü†1Z8Z^O¥½~ox¶>|@@@ƒ÷gí͈0PDï-¾Ð‚TKÎ?µý 6Ð[n7ˆ{ß ôðÐNjœCŒ¥¹Õ­èÔi“Öƒª"ƒÂ,o»„dc©:¢ÑVù‰¤¼TQC€0ŸßfZ‚äTc¸ÙOXþí-òŸ3KYa¯‚"á>&:? æ‚ßr ð£áˆømÜÃwF4õ É…ÁÅ ¸r$j`Ã6ÿ(ÛÖƒ&64–ò$rö Ç <¸î7/qÇ•íëA¬tðBŠã Î3Ï’ ¸1ðø|6n}Ñr K3%‰’œö‡`õêƒf±¬Îë@ÉÉÀæzÐl‚!M˜Ì ŒHy¼0o½Ne†áiq »™š„°aˆÅÂ1°ÆF77F1ìA˜ãdL c&´`¹81pø|6nðîŒ{‘†À09"X{ ÃÚ9àŒ ÎG-<#Aw¼V¯^(60ÄZZca9#tjzÐmŒ Ž:h"ŠØ7·¼V¯_µÔÐ>VÊøctò^X ‡° IM²6I ïg’ç0ßaè@–š ËLÐG!o“¾Àì{Ð%¥§™Ít´ñH[ä—°=™@––žr ÐE)o"öcÞ‚@1ÉPjÖ1¹ÝhÇ'™ëA ¦§Ä O97ñûù 6–“›M”óx`?z PEÜÐq8œ÷óíÁŸz 0‚8© ƒ<#{S¸ÀÜûeÐBèx.†7FtÜ-¾ädQÇÆ5¸ ÐäjÊZxË‹ ‰¥ÎÞ$0 ž¿j à ˆhÞQŸµ±C ,†&FÒs†44gîA" ÃXÆ45 häÀA†ÅY¸Ö43膀=È5†ž pD0Ç:ÆþH6lq³{qnñÉÝÉëA¯s@'ãð#âùÍÁ½ïæƒv±¬Ék@Þ98Ê |ëôý÷Iþ˜ÿ9^ŸêÏÍæqž´<’ÞÄÙ|ncœ×E§}ê&3âfa!¬ª ƒU9˜2»µFÚô[uº¡VTAÛØßüÙCíò9fâ½”ù÷´p¾ÖÓh·ÇþWõ+â=%í£åþ¾³‚ösóþ¡Ë^sh‚À¯«ðÅL›¼±¼»G«ÛºpåÉÓÎp®¸ºˆ.Ù¿½ ö;ùJ×ÀÿÑ_ßøgâ}ŒþßË·¿ßÐ¥oó9züG¬Óè¿a??êmpz‚ ¸²q8œGoóÞÞ9÷©Ìç(ÛÆ;š¨HƒÕl÷où þb´pþ´¼K{:üÞñlx»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@A‚‚'ymö„ Ò^qý¯èPx½º±WÜ*i«h u@df7±š¸kqÒ·pšÕ¤Mm8bâ´­y‰«É~®ÞýYòÖî~—ÄÅÈÔøOÕÛߢ+>Zsô¾#‘©ðŸ«·¿DV|´çé|G#Sá?Wo~ˆ¬ùiÏÒøŽF§Â~®ÞýYòÓŸ¥ñO…ÞØý¹Ã}жª’Jh©ÃíF ‰iïYxzZ›k-6ë|Ú¦ùCQ<ÑÏ f@ºàÞ|ó•òÜ{Ú/HËèxMZÖ&¶œ9]ï­úœß ó{>·Á-¼í/Š÷ÖýNo…;>·Á';K⃽õ¿S›áNÏ­ðIÎÒø ï}oÔæøS³ë|s´¾(;ß[õ9¾ìúßœí/Ьöú¦W6ybtlŒ/BI[x.R5böŒD3q:ÔšMk9˶–JúÊØki uCD\72=\ÝIÎ:y¯KZ–™ÌC¿£x=:Nçùy®ñ^=Wò×]º=NÕ¡ñÇÔïçÑ5-6[¢{V‡ÇS¼WŸDÕü´Ùn‡jÐøãêwŠóèš¿–›-ÐíZ}Nñ^}WòÓ—n‡jÐøáê¶*Í[A%M]d.ƒŠÆ±‘»ÊÐ’I N…-\̼Iq:z‘ZRsŽ÷­Z@€€îÏÚ?š ` ‰Þ[}¡¨4—œkúÀêC`u!ˆ0:ÄHb ¤1  Æ2€€€€Ô€ƒ(~ìý£ù ‘ å·ÚJƒIyÇö¿¡@@A‚æ´à¸iAŽ#>›~ ÄgÓoÄ8Œúmø‚ŸM¿@â3é·âF}6üAˆÏ¦ßˆ qôÛñ#>›~ ÄgÓoÄ8Œúmø‚ŸM¿@â3é·âF}6üAˆÏ¦ßˆ qôÛñ#>›~ ÄgÓoÄdFAzeÝŸ´4 ÁA¼¶ûB Pi/8þ×ô(4•Å‘=ã›ZOðA¬4Иš_^â.pÉ%ËOæ#ørÓùˆþÜ´þb?€ w-?˜àËOæ#ørÓùˆþÜ´þb?€ w-?˜àËOæ#ørÓùˆþÜ´þb?€ w-?˜àËOæ#ørÓùˆþÜ´þb?€ w-?˜àËOæ#ørÓùˆþ‚ÆÃUŒn6@Cš4 åè5{·ç}”J@&R3Ðgƒ'Ÿ?@àÉçÏÂ82yóð„ ž|ü!ƒ'Ÿ?@àÉçÏÂ82yóð„ ¯¸×ÙíÔÑÔnÈéÚÂLm:OO±iá´ë©|[£7©jS5xÏ×} úë>C;þË¥Óï,§W©úï´]gÈgbv].Ÿy;N¯Sõßh>ºÏÎÄìº]>òv^§ë¾Ð}uŸ!‰Ùtº}äí:½O×} úë>C;²étûÉÚuzŸ®ûAõÖ|†v'eÒé÷“´êõ?]öƒë¬ù ìNË¥Óï'iÕê~»í×YòØ—K§ÞNÓ«Ô;o´§þµœ¼Ã;²étû«W«é”¢Y©b‘Óç±®>(æBò-™zÕœÄ&àÉçÏÂ,pdóçá<ùøBO>~Á“ÏŸ„ pdóçá<ùøB ^Ù"a{wR F¨7A”?v~ÑüÐH€€€€€€€€€€€€€€€€€€€€€€€€€€ƒNòÛí%A¤¼ãû_Р Ž£û4¿aß’ býË>Èünƒ•sÚKmª¥”µHú—·y°A ¥“w¯u =e»}ÆžéFÚªRó‰3ÙÇÏý|ÕzÏ-ÚÙ£ uÈÅP)ßÜOÝ”¿t0ï sгkãÌg½£CÆq8îMVöܪ¨)Éï¥D¿º%d›ã9yš9•ü‘3êÄøñ6Ŧ±ã>ÿþ·uš‰õÖð¸Ê¨¤q†: sÛœ5¯ÿÕ¢Žm¶ÛôÂyuÝ_× ³ÑTÖÛé»™ÔrK“TÆg$°78ù9:òÊN¥¢¶œæ#ø:Í«ÄÏŠ•ÞŠŽ JzŠs r=îlGT'À!Ù|—M;Zfb~¸Ãž¥kÎRY*^(®4­kÃG+ÜàßÇLz†º(Õ¬n¬þ°+NÛGé.*Ðàü“ì(>ßAý‚Ÿü¦þA|ý½i{ÔõaSh±ú½]‘‘ÁvŠ«9S½ôÐGj˜—:žªB÷s|EãxòOÝÖƒz»ÕuÕVHæÒËÃl-¥sĸÆö^9sÓØ‚Ímʲ “mìs7ë7]N÷û6/xt4ëÎ:oAÝN¿\wê‹¢Ì=Áȳ#^Žz n7Y('1ð€ÄeaÆwux=Zj#ÉB[<,•™Ü‘¡ÍÈÆ…*³ÉöPeÝŸ´4 ÑÞXAºåçÚþ…uÙ¥ûüKîYöGäƒtZý˜§¬¸ºãOYYo¬{C$–’@Þ(·uã($?…¼ÏWWQ%¾S4rM sžâñ´Ô`ôa×[=ÖH'3ÔRÕSg…SLð×´cPAMAû1 Uº*YîÒË â¢*·J ¬xéÆ4Æ0ƒz 𤡩ª©3TÔÍYŠwÎðã Ç 1¡ÆšzCÈÑÇf6™jë'¦iiƒ‰ Þ§Ýòw#g(.Zí/·>GÉt®®sÀõR4†ÔûÐIQj‚¦íKr{ž&¤dŒŒ7H~3=A'gè-Íó5Ó6è*#yËH ÝuhƒKm€Ûgl÷¹TÇKc†¢`æ4}Í‘ë%´›-m£eÍ‘‰Û¡q™®p ŸºhÉA;|ƒcdacÀs\0A‚zzWoCaÜlyêÖòvP#£Ž:ékwXÚÇŒ4’?˜ 8Íx­Ë¸‚#ÓçÞ‚¼–†:ªI᪩¦2¸:VDàò4ÉÈ88hƒymtòÜâ¸8¿‹p>)ç‚GXÉǵÄSô‰ÿ—¢ÿTßårÙÁ{IùŒ|g³ŸúùªõžZzZÉiPØÃH¨ˆÄýáÈž½-X´Çèµm5ÏêÚ†ºJ Ÿ#’7G$r µí<ÁÂ^›ãóMm¶{¾N¥¾î'­…Ó÷-7qà ¤nél[Îÿ ާŠá}Ä ¬ÔÕ¨|ŽÉ;šî&Fô{¾Nîš`äý匷2;„•¬šVºPŒÈÝy‘Ž~ÄÏo¥©{ß4-{žÐÇÒÐr³(,„Ôÿg“ì Ê»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@@A©n\R G/8þ×ô(#¨þÍ/Ø?’ aýË>Èün€€€€€€€€€€€€€€ƒ+Ï­L×~H%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A¤Ã0¼¢PJ×ÀADÈë@Èë@Èë@Èë@Èë@Èë@ÈëAå?H‡;?ú¦ÿ+–Î ÚOÉŒö»æ¸^³Ë0„ ( ) a仨R }º€Žà§ÿ)Ÿ_?oZ^õ=XXÈëUXÈë@Èë@Èë@Èë@Èë@ÈëAIŒó6@@ƒ÷gí͈#—œkúö ˆC,cv9ËZ93{zåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡jåGÖ[ò‡j ²×ñ$y{ñ€q€¨ •f‰É¾ä^ižä^ižä^ižä^ižä^ižä^ižä^ižäT[¨ªãÔRC3ÈkØʵmjÎk8VÕ‹F-Wý^²ú*ä…~v¯Å?U9: }Õë/¢¨þHNv¯Å?S“§ðÇÐý^²ú*ä„çjüSõ9: }Õë/¢¨þHNv¯Å?S“§ðÇÐý^²ú*ä„çjüSõ9: }Õë/¢¨þHNv¯Å?S“§ðÇÐý^²ú*ä„çjüSõ9: }Õë/¢¨þHNv¯Å?S“§ðÇу³¶R?ºhþHNv§Å''Oá…ÖÓ@Ö†ˆ˜ “«<¼Ó=ȼÓ=ȼÓ=ȼÓ=ȼÓ=ȼÓ=ȼÓ=È2Ø£iËXÐzÀAº»?hþh$@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A¼ãû_Р Aû³öæ‚DMÎ?·ý   Ä»ÿÜ4  Õìné Ó„ï:ïáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pç]îˆ'y×{‡b ÞuÞáØÂww¸v pIç#ˆû‚ ÐÐPÿÙyt-project-yt-f043ac8/doc/source/_static/custom.css000066400000000000000000000035521510711153200223760ustar00rootroot00000000000000blockquote { font-size: 16px; border-left: none; } dd { margin-left: 30px; } /* Collapse the navbar when its width is less than 1200 pixels. This may need to be adjusted if the navbar menu changes. */ @media (max-width: 1200px) { .navbar-header { float: none; } .navbar-toggle { display: block; } .navbar-collapse { border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); } .navbar-collapse.collapse { display: none!important; } .navbar-nav { float: none!important; margin: 7.5px -15px; } .navbar-nav>li { float: none; } .navbar-nav>li>a { padding-top: 10px; padding-bottom: 10px; } /* since 3.1.0 */ .navbar-collapse.collapse.in { display: block!important; } .collapsing { overflow: hidden!important; } } /* Sphinx code literals conflict with the notebook code tag, so we special-case literals that are inside text. */ p code { color: #d14; white-space: nowrap; font-size: 90%; background-color: #f9f2f4; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; } /* Nicer, controllable formatting for tables that have multi-line headers. */ th.head { white-space: pre; } /* labels have a crappy default color that is almost invisible in our doc theme so we use a darker color. */ .label { color: #333333; } /* Hack to prevent internal link targets being positioned behind the navbar. See: https://github.com/twbs/bootstrap/issues/1768 */ *[id]:before :not(p) { display: block; content: " "; margin-top: -45px; height: 45px; visibility: hidden; } /* Make tables span only half the page. */ .table { width: 50% } .navbar-form.navbar-right:last-child { margin-right: -60px; float: left !important; } yt-project-yt-f043ac8/doc/source/_static/yt_icon.png000066400000000000000000000076511510711153200225300ustar00rootroot00000000000000‰PNG  IHDR2+:æÛâgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“tIMEà. ¬‘}ˆIDAThÞÍ™kŒ]×UÇkŸ×}ͽw®ç;~'~%vë8qšÒT$iBÛ@K ¥BT¤‚DUPÅ'>!„ø‚D¿Q>D* E¥-¡A%4JižNœ&Ž]ÛqìIüÛóº3sŸçµι“q<~µEbKG÷œ}t÷Ù¿ý_{ïµÖn ÔŸxöªïæ¿ôÈ4qÍâA(å 4ã0Œn¦ ¹‰Î;€›ß[ É?ú ƒyAð—ÀòÇ>ð•8 _¿™6Ü•*—AMÀmÀX>jÄ@ ˜& @ˆ—ÀM@Ý Ü™ß‡@ùfã eÙ|Øø×h#Ú9Ðñüšì ­²W¹ÿù@–Aì~¨å [E°":°'DÕ´ÔÀð.pxèßÐÏ]V2­Àg—A`5¨Œ'‹Þšh¡PMû®ŠhË’i¯ϸå¸küTGµ&èÝÀNà4ð:ð6Ðý¿ú ˆ<”îÄæh¶ø[̓kvõ&µ´¸š€DÛ5~<í–{ÁèâÒÚù#ÅÕ¦[ŠõŒêV`sô pëO<ûK‡q½ XzÑ[nÕæìf’ؤ"º§snè«Ó/n]5«Š,­T®ZS´±;’´KÛûS‡[ï$¼jç@iýÌËõ-³…±n,¨n"MÖ¡:¼àÞ÷à„74d‰–VØå«ŸÉò~­Tâ0¼B‘-dsBúýµ;{¶¹÷=xVýÀÞ·ü?žyå¶uQ³j" ŠI­1ijLšQ5€zÆø·__¼ÔH÷=ç¥Ú˜;ê4­_ˆû<ëTkw¢ï~ë1n_ M¾Üþ>= ¬fåíÁæêN~dø°:yãeIèJµþ}îþøÅGfn gë'I × ün¿à…‘gRëÖ`#*ÆI÷Ÿo»¿81§Ö$Š®^36sëîO¿]­Ï©ãtnú“jÓÇ@ cø€yýZbŸ_ äð_Àh·SŽ_ynÛЇ÷Nïîž6¨XD ínq¨9_uÃØ'qÐê<‹Øž5/<>[íD©qŒ Àg¶4ºŸë¼ºõùK/=5²çüIÅ¢ê)™©|y1ך+)åæ„O¿C¶R‘?º¥ðÞÑ# ·(àĉSkÖ$ñt³¯zGÃxrYD„“çºÎ«“m_D° k+Aú‰ ^5é]8ºaO²ñdìöX*uC׋ ˆÆa@’xËæÈ,ÙÆ¸RI€Þ qâÁ~à9à1DÐÖ|=Þ÷ãÛ% ê$‰câÔaÜUýh ’YªB7Z©ÐI!ÃÓ/7ƒ‹S¡c¬U~õžz´kURYìôã´TׄÍêWÊZÝó{ýniûÙcþh;ùÎã÷Û‰£ÛóQ Qý"û¯¢Àŕ̿ùgàÓ@QT¥søàÖÙÝ÷Ÿ¸%uŒªˆ¥æ8DdátÏa*B+(Ðï§òãWgƒÔ*"B©äêÞ‡n Ým¾W}m¡±ØÑ¹°\ ®ð`- v¹§Öü Q=ód!ˆ{hf1"ÈÈø¬_;‘ÿÄ7æ;.·ÅŸû6ÛŸ9|üôˆÑÄõ’Ä÷béYH3½N÷ ½ÂáÌÉE÷Ü©EODPU6l®ÅfÍpü®ë!{}·æ/6Š­NQT¡·Ê_œzmënm®²šÛ¨ã¸Þ'>ûhñë{ù›ß§üø‡á׊ ù‹Eà_ò9ƒMSgß‘Sã(1ê핻̥ÐÊH›}x0ET•ƒoLºÝDDÀq wÝ;Ö÷‹®žìNz>ú‘À­VÛÊüÂXEÔÓÔ”I¼¥DAÊ•ÕöØȵˆ•ø!ph0qßžœ¯<;WvíUJ½8ñB9™I]u-Ž &9ÌÍöÍÛ?›õ³mF/&w~hU¨63êS]Äx${ ¦².¬Õæ†8q¹"°ŠE©Ìõ`–@rU¦oV€N;Ï9·*IU¬ëÚv½º¨ï&–Ù”º¯Ô\]2«c‡çü™©ž›™ÜùáUac¤`5w18Û3<Ú;‹Rü–ë½¹U~·,'qQsO|±^´±(R~¸p®³ÒzýïÀ‰Ìl„Ÿžš©½3Ý  _)õ{Rjs¨—(ŠO S9øÆt!I,ªP©xö®½ã¡\¹?0 ?k;L¯-àÝë5™_UŒ"`£F”Gûï­ÿÊÔ K6‹À§€_¹–2—䪜¾¨̶Cï»G§K± ÐnÔZÑ´ÓãhHõ¬/)“gÛÎɉ…¥I¾iK=^¿q(±ö «A€v*m9œ¯ù˜û7¨ã/÷¶<¬ydñغ¯N½°iȆfÌÞ«Í™•QàIà|öa•_~µôŒ€uŒ]l Ï'ÇmdNF¬/¦œ>4´Z±×5ì¹w¬ïå*E€ÐÂñ¶ÃißÃ;—¹Š`TåÖÄ­2ýÒ†’D3wæQ²˜çŠPü \•w€ÿ€lÒ÷ç›õÇßiÉ%¿ÞT’ÀK«ÃM{(‰wÌ¡·fTQ«Œ¯.%Ûw6"µWåX‚IÞëæ"YÑ;TX<±ög_[çjŠBø °æFHÉ&ý,€XkÞ;tdÃ?y[';N‰*Q1ˆÚåá¹}Ï4åÝ3×w†õá‚Õks\ö¡HeiE‚0òsVT>¹ðöºÏÍËCˆq²»´\•ArUÞþ;k]àÒäšg](<µjÏ{‘¸V€vàG?8×1ýÈŠ* ×}û«éËUs3++sÙ³@-jW‹­N1334q»ypÓ=Ý3C©˜ØÜ·üï×ò2#² ² @{Éþçïx²´cú™ÚŽÓj{rºU84Ù¬d¢rïíÕèÓÛ‹Éj?å&X.+ê€l÷L%Z¬û½ÐÏ`D‡Ónዳ?ÝØHºž"ø°z Š¹N»//†NÏœÜykÕ·Æï?ó\mÛÙçOL 5;¡/ëØ‡kõVíD¤[ý„ÛË)žY!ñu]Ð1g‡ëÍÏ×Lš…ÕÑ­ý©áGŽŒZKGÝ7`¸*Hn^2g2AûÝRúÊsÛû8úÞŽ‹OM̪ŠUeݪroÛ¶ ÚNÛÙßÓõIÂΡ”aïý ñ†Y¬¢·ùx´Pn¶*ƒzkhX=·ü|¾ìFëO<{]E~¼1xHOÙ¢§OT[G.ÎÌ6D#ÂÇnŸ®—Ã…ÆðBk®<¯/õÓ‘K!»* ›K–¢É¼€•€Ä¼_¤*Í®m¥~§â…‘7Peu¼X¾»s¦šfªÔ€­×5­\•&Yà•f±ÊB-þÑSãéþç×G¾ªR({»wl¼dTEE´SjÏK}&z=ízl¶1®¥l(f@…”ly/Ý%8QNžé¸(踋©¨S\l/e=Mý 5“-‹B–¾rœëÉá¸.dññ§ÈÒ¦b'OtæÒ(ªô¶ÇÛ¿þ¥ý·&‹A#é *©ï¥¡_ìë´µîÙžWkÃÂHJŽbÈ|2Œ0u©ç98@æpÎ/Ææî;jñøX`Í¥g&6QµÜ×±ijeÞÑ µ­3šEbpðº 6Mq\·E6¹Õ:ª™íº^â=ò¹W/îúøäëŵM£š¬Ž[¥¢=ŒÑ¨Xˆ" úr6Æì»Z© £ee¬ ŒúJ݇_›)t{©#ÌÎÇæ§‡ünßJ23?™ã½„—NΔŸzóÔX:<:wdÝî ùžÛ\d™*É2õË^zÞ›þ¾ü=§T®÷ÄçÍòú…c…ñfÑÆ¬J:…‚&®uÝ´_*öãÄ‹ä\Œs&tÌbjûŵYrüÛ@ï†@rˆbþ§Çòê²£‡çÉ€¸ÖB¶Êíʯ[ñRë¨e$éx·‡Ó¥íý©ÊÆp¶2–´ŠCièš8.*"Fcã¤óN)˜4Îó]Ë«þøæ2³ú7à÷ã0ì¯ÿ¨ ÙzYv~8‡R@=µ”lèTÓЩØÐñmbR1Ú1~ÚtKÉ¢)¤*‚£Ö§€§ó_–@òæ#x‚lm”€,nþ°.¯›Í•ya¥ÔÌ5€„l7^Gv¤·X”\EÉ+QE²'%ËîLÉÎ\ƒ† ü°8œw¶ÜM–¯å±Àߤ׹Ô`j9ÌÙ^UÉë…,\éòþYåy²³K–C,'Kdßuýyø*0w37µ¼OƒKYáLq¥C¢Ƚy7]ãÛód>×ßS¿ÄM‚]µó+xd^äCd ±ÀP>"sdG? ‹Ob€_6È/Z–Ï‘AqrˆAe‡A”øÿ`Pþ _+»/Ã%tEXtdate:create2016-06-14T20:46:12-05:00 ØLh%tEXtdate:modify2016-06-14T20:46:12-05:00Q…ôÔtEXtSoftwareAdobe ImageReadyqÉe<IEND®B`‚yt-project-yt-f043ac8/doc/source/_static/yt_logo.png000066400000000000000000001651611510711153200225410ustar00rootroot00000000000000‰PNG  IHDR ³æõi1bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEà 2ž°X° IDATxÚìÝwœœÕ}/þÏ9Ïô²½H»ZUTPolÓD/Ƹ`bŒmŒÇvÛIî+=ʽñ}åÚ\“žŸãëà Š-Û¡ju+ ê]Ú>;[¦—ç9ç÷ÇŠ¸€`µ;;;Ï<Ÿ÷륗(ÒìÌ÷<í3§DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"X"""* ¯ây'}`)å#–e­cÓ ‹% ""¢B> !nrØg^Ãf'º„‹K@DDDDD DDDDDÄBDDDDDÄBDDDDD DDDDDD DDDDDÄBDDDDD DDDDDD DDDDDÄBDDDDDÄBDDDDD DDDDDÄBDDDDDÄBDDDDD DDDDDDçb ¨l­Yã¦û‘pÁ/|Èe´ôAJC{‘ׯ¾†på!T–6!­<òþ¡ß\Ɇ4Zžg¡‰ˆˆˆ†O°dZKüû¦0ü¨T « eBWBë4üÐ:Hcìß”² DÐih™†@Z¥!‘€– ‡iÅáv'ÎÇaÕÅZˆ¨Ìù¥”)G=L ñ˲~Ȧ'b!;ZµÅ…YV-L«BÖA¨:(Qz@T@‹28fuèƒÀ,9eB¢F`Ç7bÕ*Ńˆ@@ˆ@ˆ éÑ-Up[M°Ð©› ÐÚò£¡¬¡p""P2aF!EiÅ—o퇚1€0€1€½ŸµkôO…RSLD3 ü,Ì¥ž±Ê„ݪÚÕ %ºáIvãØÞ>öš9×nª…PÓc„š  =E&†è„D;²¹h;\™aqˆˆ„„ˆ„ÊÏ?®ó¢Æ3³5JÔ±(%! ¡Û¡¬£>×yÜ»2Á²ÙÏšmõÈçæÃR³L+ΪS4jú qJô9¤kÛ¸21€0€1€Piúá³ðB¨…Pr" RW­ t¤8 K©Oã·oëcaˆˆ„„ˆ„ÆÇ¬«‡t-!B‹ ,ˆ#® ƒ8 ¥ÏÀ­Oáè®.Nr'"";kÖx /‚a¬€S9Üá¤Î@ë3Pò$<Ö ÝÝÎ@BD D 4z¯›WA‹%ÐÒË‚ÐE®Y@ƒ”Ç sÇqÿííÜŸ„ˆ@ˆˆ„†gÕfª¥Ðú(p^äJ’ô CÆ:Æ9$D DÄBïö/kB¨ª¹ WC£’¡ŠBZÇ¡õq˜Þ£Ü„ˆ„„ˆ@œlͶzäÒ×AÉe€p³ 4¦¤² Ä@À!|ö¦n…ˆ„„ˆ„œàÿ½TŸ¸Z®€’¡ñ¹ê¨>} 0ÁÓw÷Þk±(D D TNžØ^ º‘ÁƒJŽT9@œpÙÔA|ùî8‹BÄÂBÄBvµ¦µ¹èÍ€¸Š»”S©_´p†>„Œy_º#¢1€0€1€ü ÕÐÀÕPê&.¥K6…°CŠƒpœâP-""*EOnZÂB ‹Aet¥JúmÀxÞÞ #D D 4Þ~øl#üA‰Ù,•7†Ð‡ 䛜ÄNÄÂBÄBÅöƒV7üƒ·Aèk Á æä0* !Ö›8¹çV­R¬ •ÇÖOƒ÷BËzƒx5S)‡!䛸ÿÆ#‚a„ˆ„„ˆ„ â­núop=´`1Œ1€0€1€ÐYýòlhëÓ€¨b1ˆ†CÅ`ˆ7‘Uûð¥[ϳD D 4ZK<ñòMÐêfözôЧ»!ä›Èä[ñÛ·õ± D D ô^žØ^ •¹ZLe1ˆ D4€³hE_~?~ÿŽ,‹BÄÂBÄBðøÆP¸~ƒh,è< yZïʼnG¸’ˆ3­ÚâÂe¹CW±DE #¢ùÜ^|éŽëAÄÂBÄâ ­B@a:‹A4na¤ ÂÚ‹Xl?¾~o‚õ ba!b)Ok67#£ä*WD%s•4!ÅA˜úU|áÆãB³(D D åáÉM aêûáf1ˆJñŠ©±¦k'\9À‚1€0€1€Ø“Ö«7Ý›¸Ä.‘ Heò´ÜƒÏ­<Æ^""ûXµJbÆGî+X "[^EÍ^"";„-.Ì0? -²Dv¿šj è€ÚƒÏÝú„àr¾ÄÂBD %ä×yQíy³X ¢²»²õŠHß.ÜM? B DÄ2¾þeMáêßÄ$ƒ¨¬¯° ЇaªÝøâ-G9W„@@ˆˆ¤ø~°±~ñ€ƒÈIW[;aµµâÁ3,1€0€1€ÐØ[³%„¬ù»Ð¢‘Å rìU7 aíGÞ³®ìbAˆ„„ˆ„ÆÆ÷wùQ—ü* šY "ºë3€µ“Ö‰„„ˆ„ ëÑ->¸¬‡ ÑÂbÑ{ˆÂÀTT½Š»–§Xba!b¡‘[³Æƒ\Íï@a‹ADïEV&€7ß+øüµ,1€0€1€Ð¥ùA«þ¾¯’჈.ñê¬OB™;ñ…ÛÞæð,ba!b¡¦µÀã›>ÏM‰h”“@o‡Õþ*WÏ""º¸ÿ\ÿQãz‚ˆ sµVY@¿Óó \9À‚ýÒc믌O³D4Wm¡@a¾pó9„@@ˆìÆÅØ£ëç@ÈOûÑXÐÐb.€¹X½ñ „x÷ßx˜»¬‘mB;KP@?Úо-½,¡» ÅVœ0öcÕJ“¡qÆ"b)Š5»üȤ¾  –Å ¢ñ¹¢ë„Ü…ÊÊÜO„@@ˆJ•d @klâ~†"ßk‘Aé[0Ð÷WX½á>¬ÙVÏ¢QÉ…v– Voº·²DTbWx¡ ‹—ñå[:X*ö€Ñûâ$ôÑzbÃL(ÜÌBQÉÑÐr1ÜXŒÕÏ@êõ¸ÿ–ã, khg FáÑ-U0òßdÅ "{\õ5W΢±Æ"bkÖÈÔ|ÀdƒˆìG·Ab3î¿émba!b±ƒÕ›nƒÆM,Ù›…Ð;pÒ»›KøH©úõ-ðˆo@IƒÅ ¢² u?´µ ©º=xhyž!"RñƒV7}߆–\Þ’ˆÊð®pa/‘ÿv|ûÃi„@@ˆ û€\òeµÿ.†"*[ïì%Òü3<¶ñV¬ÙågQˆˆ¨ ¡%¸ÿ¹i¤þhÁºÑ¥gW•—^{ÏП”~+/$'S)Þ%²€Þ…ªÌÜuwW§a]îØBD …°f—™Ä²‚Åpð]ÕÊ͹~o}.î©Éǽuù¤7h¥ÝA+ïòYyW@g]~+çòhÓåµLCj-Ü0 Cëak9é¶”†ÎnS +ÜfÚåɧ…ÇL ¯™t{Ì´ô˜ƒ._~Àä¼Á|»2qWä`hlé l‡;¸ ÷rh1€0€7"®lò†g¨É§ÜÓr=þI©¾À„쀿!÷׿ãþj3í÷¨ü˜.<ðÎëûÌü/ÏÍÜðþnJzòI—'7`³QW(õ„3½îp¶ËÎtzj3ÝÞpŽ­K£xÄòÁÂÍP©k±z#{Dˆˆhäw–`Û8¿Ç¡Wå§%õÍIu…¦¦zB-™¾ðÄÜ`0de¼åøYóÒ°¢®`º×[‘êq…Óžªt{ &}ÊÛŠ¹ü\~•.õîÁ¡Yt1ì!"QÑZâ±ß„M,†½¹´)f¤{óâ³Ò]•S3=U•fy†K•Þ\¯"Ùí®Hµyj“gýµ©C¡Iñ´t+V‡Dˆ„„ˆ¤˜Vo¼Z|”…°§)©^ÿ•ñ“5 m5“³½•eqï–aR:â®Hž÷ÔÅOëâGñcæ¤É¹&tÑ b½Ž\f3¾|wœaa!"‘xtK\ÖCƒß’Û„K›âŠØ™ÊeñSµ—':kkÌ$—- ¼ªÛS‘8ç­Ÿ Lˆ 5ÇÎøêÒì&¡_ÞUTÛQ•ÞÆ"b¹T?Þø ¤˜ÇB”6 `IìtåÕƒÇ&ÛêfÖêOVºÍv_Uüœ§6~<ÐÛW1)6à q'mÞ]††f™®Íxpe†aa!"²ú¥Ë ]_e!J×ôL¯ÿ¶È&Ï5p.GéÐZ ÇNœôÕ OØž2È@â仌JAÊ-HTíÀCËy0€0€ÈEŸ Voø&`4³¥Å­•XÙ°îº#§¥z«8ÁúÝÌIcÿ[¡Iý­3ú\>ˆ:în£b+ŽÛ±j%W]ca!b¡_óãõË ûXˆÒј{>y½ùŠØ©‰A•s³"öÎ÷ì!q0©ûlƉ¯aÕ*Nba!b!¬Yc Só?Ô²ãoJª×wtoóòØé‰n­$+Rž¤Û[‘8œØÿzxZtÅ´A>•:ák>wëÁ®L"{rÃu0å],Äøš›<úDdßäyñŽz>›8KBzsÇ‚ûÞ ·ôí©¸¬Ÿ›$–9‰Nhs>Û›,ˆó|—õ‰?…–c|4å¼÷uíž²4~f‚ԚǧÃ)!t»"ùVhRôÕŠÑáIqöŽ”kÑg¡­ñùÛN° D Îñ؆;y Q|µù„ûþ®SVÄN5 tƒ†/{,41úFpJßýÜ©½,ƒÈQXr¸±Å`a!b)onñÁ0ÿ>£x\Z‰{»_m¾µïí©•ç.å4lyiXGúZ+¦GvTÍìKJŸÅª”ËIkHy¦^‡nв D åiõ¦›¡q+ Q<Ëb§+¿ÐµsfC.d5hTaDHu:Pßÿf`Jtk͜ި›«j•©,@¿Žlf=¾|wœaa!b)?hu#ÐÿçÐ"ÄbŒ½š|ÊýåŽW¦/‰ŸÀ}<¨Ð,!ô_Cÿ¾Š)½[«FÊäN5´«z~~ÿŽ, ÂÂBÄbOl¾–þ 1ö®î?Ró@çÎÙ!•õ°4Ö´hóVÆöUM‹¼Rqy¤ËWŇW[ß±t°6ãs·î„œÿÃÂBÄbSkÖÈTÿ) ªx8ŒáÝHååï´¿<ýªÁSÍìõ ñ#«t³gÄÎw.ÔKÜC„„„ˆÄžžzùJäÕ§y(ŒÅɳ_9ÿÊœj3ég5¨XBª“úþÖðôȦšù®¦e×;˜>—ëyÜ·ò ‹ÁÂBÄbOlü#Xb…±ñÉîÖ‰Ÿì}}&—Ö¥R•‘nëh`bé‘ÕsúL!ùºíîdê8à{Ÿ¿¶“Å`a!b)m?Ù<9ý»< /¨2Æ7Ïo˜5?ÞÖÀj]$ _ö­PsïÎÊY‘½ÓY[ÝÍ _E.µ+f1€0€1€”®'6–^Äà°¦gzýß<·n^}.Áåuɶ"žP²5|Y÷¦Ú¹=N^· ©r€ñ ’•[ðÐrÎóaa!b)!?Ú†7õP’›ßЇcÇ«§í•y>n*HeB ¡Oùú·VÏêÚZ=¯7Ï!Z6¹»©A±÷ßô*'ª3€0€1€”nÖýÆ ~ÑHNÑå©Ll¯œÝ±¡~~OBú,V¤ÄI}2ó>{W/‹ÁÂBÄR|?~i¤ëA6îéøÆÙõ—}hðd3«AN”—†õV°%òrÝÜνÁ©1V¤”) 0vÀçß„{?œf=@@ˆ@Ї“Ï >þèôºÙKâg¸Œ1€^w8µ§jZ׆šÅ]ntXÊw¾$¶à¸±«Vš,ÈØzôQŒI 7›áÀ7Ï¿8óŠÁÓM¬ѯ³„ЇƒM½[ªævÙÏ]Kõ¨"°\Ïã‹7d1@@ˆ@Æ0€¼x ÷o±é>ˆŠ!ê ¥vTÍì\[·¤3ɹ"%J€ð=Ë @@ˆ@ÆÆêM_Æ,6ýÈÃÇž]7{)‡]]’Œt[o„'w¿P·¸ý„¿1ÅŠ”ÚÝPk@ïCl`-¾~/W8ca!b)5[BÈš-$›~d¾v~Óôkµ°D#×å©L¼\3·í¥ÚE=ÜW¤Ôè4„µŸ»u'„àè9"QzlãÕ€ø8›}d>ÕózÓ==¯Ïd%ˆ #áòæö†§u?W¿´»­—œ¸ŒçðÙ•GX "‘{|Óס0Í~énì?X÷¥Žms%7$*¸w&­¿T¿¨Kù–õ6´\‹nв D —fíÚü« ÁáW—haòlÅ}i¡[Y«A4¶º<‰—kæqxVi… Bï†Ùñ|0Ãz0€0€1€ Ï×/ƒ4îc“_šÆì çoOþ|YHe=¬Qñ$\ÞÜîŠË:Ÿi¸¢½Ïàž"¥@"è ¸ÿ¦W!Ã!ÈX½þ~hc ›|øÜZ‰ïÿÉâ–Ü@«A4>òBªCÁI‘_4^qî8WÏ*‘ ¢ÏÂPÏà¾[ϳ D ïmÕ*‰^-lòáû6÷ú *Z œÖõo¬š×¾­fn”Ë3÷Ýó²½^÷ZÜ»’Ëö2€0€1€ü†Ç¶Nò_gsß‘ýŸëÞ=‡• *=ÝžŠÄ¶Ê9/Ö/êNK7³ÈøFáe{OîÙ‰U«Ø DÄrÁêwB‹•lîá¹<ÕüÓÓÏ-ukÅ ûD%,áòævTÎnº~IGÌ4Y‘q "í°\OãÁ•gX "b[ÿ-Àhfs0/òò»Ç~º´! ²Dö—†ÕžÚõóÆ+Ïs?‘ñ¼£j %Zá7^pø°,"rxyôQ\-ÿ“ËïÏ×Ú6θfàø$V‚È~,!ôÛÁ–NXgR§¡½›:9=€¬ŸÃøm6õ[;]ùíó/-âfƒDö¦µÀñPCôÙºeçö…¹±á8Þa; ­§ñ…[O3€0€‘“Èã›o‡Ò7²©ß_…™v}ïÄSË*ÌŒÕ *ç=U±uu‹Ïqå¬ñºËj ) '×âÁ• D' KÒÓØÌì+[§3|•Ÿ–Ü@ÅC¯Ìÿþ±Ç—Ý=ÐàÒŠ=œE½ K/‚aþÛx5V­âp`"r¼ò¾­ÚâÂŒüßBK›úâ–%ÏTüáé—pc_¢r7è d6ÖÌ;ÿ|Ãâ®,¸„ï8Üu; Å/pÿgËøS²„ˆ @æ¹\®‰vûpzÎ’ jŵwô5W\ÛŽ@ù,wéÒJ<|üÉ¥¹Xˆ§Qñ%3yãH[´è«Îe¤;ÿ¦§¡÷5ߤÞä¸~û ?|Ãyx¼Î C[kåñCbÛ†]b ·ìV-ÓZ{µÖÏ;ìT~DJ¹ŽW4ç2Ms'€4+1ÌçÏáü!Ã0(¥ž²Ý§;´âÐÞ¾f×ùú3_9P.ÀgzöLbø ?öŸ¬ûùÎ#-ãùƵ+¼¦>¢¯¿ý1'µ¹xî©+pô­y`¿sùø–Rê[,ƒ£Mp†e(`±,ëgRÊï˜îô‚‰=[––KiÊ xo‰¾5…§ ëØWZ¸•%]¦’†RRj%…áRJHeIiAJ(!ÕÐó¬PJRbX›ZJmIhH¡5¤¶ © 5,C(H©M)´%¥² C™B(Ëúg%…­Ÿávn¯uòq¥—_û&Ï.""÷Ì þÀß;¾bÑZñÊ SôõwÚ~üîƒ][g¸•eð4pp¨€nÓ’®œéòXJº,Ëpç-ÃmZ†¡´4,Ë0,% ¥ ¡Jsyf-¡-iXy·aå ò\Ò2¥aå܆eº +çq™9—Ë*Å÷~è\$ØÑ8÷äÎë;ï9Ì3‘ˆˆä=)¥þŸ”ò/Ô:¾j›×.…ÍȂĹð¼DG=Or nSžœéòæL—;o¹Ü¦éòX¦á6•Ëm)©”½÷} Â¥,—Ë´\þ÷ )y·aæÝn3ç2̬Ûeæ=.3íqçóãN¶¼u®ÎÉǧž½à0*ksï6ò—šéEÐÒÒ•Êù©œïW ­3O>í÷dÓ>w.åõæ²WAW¿Ûqð|u&g:wdýÄN,ùp7@""Ò à _vüƒà¡7æéh÷NÔ6fìô¾%€OEZ¹9£-†¾lÞHg=þlÎãÍ… ¡ÀäÆš†ðes_6穾ðŸ,—a¥=î\ÊïÉÆ¾LÆçÉæGl{Ûáï®¼Ž½DD ã”úž”òA8bõ÷a™n±ö©ùú‹ßlµÓÛ¾©ï­ú \v·$ys¦+ÉzüÙ¼'Íz<Ù¼‡a£t¦e„LËJeü Ñ,·a%½žL"èË$Þì¥Ì%éè‹{Ou„[L'«o¿ç(*""á:ªµ~Aq—Ó‹'Zw,ÑŸûÝ}pÙg­Û{Læa_Ç_6ç ¦³ž`*ëõçò^#ÏÉlHò–Q‘O+é ä¼î\"èKýéÔôŽlܪNkçÎÁÒs—D°"Ï£ˆˆˆdø7­fˆUˆ ÏÌÐwüÖq;¼ÝÇŽWOÈ ²÷cœø³9O8™öúÓY_(“÷@iɪ”O6ï©Éæ=5}ñJÓe˜±? ûÒIŸ/÷ë×O…Wu8{ï›?ÁáWDD —l›bÖú*§Pl[¿Ô.䣽o´ð/_ÎtSi_(•õú3YŸa1p8æÂjZ®šDEÍ@¢Âræ`(ê«ð§²Oþµã•ƒÉ¬Ç±Å™8© ó–Fy”1€ŒÄ÷¬q|»Ú&áõpÅÕ]¥ü6ç&χ¦¦z«yÈ -‚‰¬7”ÎxCÉ´ß“·Ü¬ yËUÓ¯¨éWd½îÜ?ì?åèèô‡nàÎçDD #cYÖI)O¸ÌéE›ž^¢¯¸úÅR~Ÿˆìoá¾cp™–Q‘Hù+’Y_ “õ ›oêGc+Kùö·÷9w¤×—Ö·~ò8""gàŸ@Žœ­;ÏnÃÄ)ÉR|5ù”ûòdG÷Âð˜¦Q‘HûCÉ´?˜Îyßo£?¢_õ̹¨/ïà:qþÜ“íÞ€Å#ˆÈÙF5&])õ(€^ÇWQ)C<ûä¢R}{·÷í›`hÅù£  }±ðŒs=3Ow55F«ƒ©œáƒ.Å mQ¿S?»ð ªôw=¹äŠØéJ DDÎ5ÚÝ“Sþ À_:½bÿîÅ:•|  YjïíªÓx¨ +%+)e2ã%³>­9¼ŠFîhÂ}"–vìŽõ³&Õ ¶ÔUd‘¨øö¹Ÿ Ô÷?>á#'š’<:ˆˆöŒ5ÚPJý3€´ã+™ÍøÅº5³Kím­—ÆÐ3g;ÄH ¡oZ}&âØÉ熔ú¦ÅÓ¢?ϵ’+ûM¾ràÄÄuu‹Î<[¿¤Ó.žîDD PJýXJùל½ÿDl Jl^;Mßò‰SÅþÑ3SÁ°Ê:fÒ«€õÑx¨~ ^¥¹|çXò¨P)†~  .©åŠˆÞõ¾Þçç¦40`P1 ýÔÀÀ… s ú³y¹«'æØÉç §Ö÷׆ù±zý€Ê¹ïéy}æÊÃÍ?k¼âäÖÊËûx‚94€`è;Æð7ޝî+/,Å8%‰³Žéý§2ÞÆžþjoÞâf…|h¯P  J ý^  Bõ^”³€ ,ß#¤Ä5Õ@ß…ßû14ôë=-/À]p…2ÄðQ4éŒ/ïìsì^‹§7öU}%±04wc©>!€/À°Pu ¥äÙ Çö~L¬ §çM©/ÉU¨„ÐX”8×øð±§V|¦g÷$—V¼æø¨4€[$0[õ€m(¥ñÒ–¨c‡_-ŸÙ ú<–ÝÞ÷üD{ýßÿÉŠ¯¶¿<=¨2êIDT&–e­ÐéørêèLœ;1f“Ä›òý¶ †Rrrg´N–û°+À õt|Án0€É‚C«llëžAoO4çØØ›O±íR·n­äuýGZ9úäwõîoäÑLDT@À¿8¾ÚZI±öÉÅcõò Ù˜íHSwµÛ´Êw·z× à‹rh9{:ÊÆÓë{»÷Çä†Êäìæº”Ý?GØÊx?Ûµ{Îÿ>¹fÑÌt7¯&"²yRê_$œ^pñæë É#g·õ ³*žòW$ÒåwÓ÷˜waŽOÀ| x¹O9éÈËÝ{<ù|JYmô7-Ý[µêÔ/–ÿþùõ—…LçîhODdû À8¾â¹¬O¼ðÔå…ÆÍËJ3mÛ‰”RiÑU•U[WahcÀ/ÀurhGr*KϼÔëËç¹A¦Ïã²®›×ÒWnŸKjˆ žl~äøSËo‹¾ÕÀ£œˆÈžJ©G˜N/ºØýrÁ—ämÊ z¥¶ïPC4VáÊ›åñMãwIྠûtpˆUÙ{aKıﮚÝÜëó¸U¹~¾•ñ>йýò¿;¹fÑìwS'"²]pÀ/_õÁþñÊ S ù’Yû.#i(%kc‰½Så…àñé s;Z¸9 Sì{;î>yƹÃtnZ<µ× ŸsJº·ê/O?»ŒÃ²ˆˆì@ ”úË`óÚ‚.É[›‹Û6€4DcaÛn6h˜+€û/³rœg^tnïÇŒ ÕñiÕi§|^Ckñ¡Á“Íß?6´Z­#"²I°Wk½Åé…çOMÃá7 ¶ o­™¶åX©”¨Š%íÙûq™~K×K ‚ÁÉRi%6íìsìÞ7,šqâ竬ç³]»ç|ç8‡eÙ%€@Jù0Kˆuk–굪L{ö€ÔÄRÛíù1À½rhÃÀ*'{qK¯/•¶yTy\êÖ©µi'·ÿÔìଇÚ^žæWyvˆ•r±,ëEo:>€zc¢Ýùö´ÒÌØ²¤*–´Ï·‡! møQ¨cð à¹Çö~|lrmzFÏ@me"åwò1`h-®82ù‘cO\qÝàážDD%@h!Ä?8¾ú–ékŸš_ˆ—òéœí&Ezs¦ËŸ7K?8– àþ 8}.m8œpäÞÀ'§Ô§…‚˜ÔÕW[K8~R¥™ò}õü–súó›rö]„ˆ¨œ,Ëz@›Ó@´îX3;êvðYö[¶2‘ô+UâK×ø¤VÈ¡ BtÁ½Ôã×Ú™ŸýÊúpvjÈg4Dsd°ºv âQÌJv×þÝñŸ¬øRÇÖ©n­ØUJDTJ@À?;¾± ±á™£}¯ÎÛîñ8˜Ê–ö𕥸—+[Ñ»Y–ƺ—£ŽztÏÔ†_›û¡”MÑXU}_,Ì£pk%oî;8å{ÇŸ\º4~¦‚!"*¥Ô¿tz#ˆmëG½$¯ÇÌ»ìuàiáÏæJs˜‚À¸JŽóB¥jëž~o´ß™“Žë}nkåĪ컯çZ4DcU ÑA>p_И‹…þèì‹KþäÌósjÌ·$%"*… àGŽo…®¶Ix}Ç„Ñ=3›¶ê ¦²¡Jp«¾Ÿ’ÀTözÐÅ=³¾×±½wO®KââçG}_¼’!ä—4%Î5~ïÄSËïˆh`EˆˆJàû]¥Ô#Žåì›ÔƧGµ$¯ÛR¶ þL¶ô&ï6àã—Ö¥÷íËË]­Îœdl{¦ÖàÒ»õ}ñʉ‘J-¿0³žÏwî¸üžüůPœQFD 㬠ÀÇg£ýlp¤(„½fÃz³%¶úU €»%àãEÞß3">Óræìó4Vd'ø=j8¶f Q1±·¿ŠG̯›™î®Y`õ×±Däd%1o@)õ])åg8÷«g¥ ±öÉEú«ºë’ˆ6mW7_¾„Víªp›p„6 ÃÚÎ~õé© ©Kùó5ýɰÐ@G}õœ_úÝÛ—œËß²ø|;”\]Õ‰c¾údY}ÀLÆþ;_uX³~S)õnGd l@ÐZoBÜääÆûw/Ö©äkÍKú{6œ)í)•pà ÏÞ·bî3mi—?ûÄ€×úHCeîRÿ^õ@2,D[cu? !~[ùT çûëmó¶WÍi{´ùš3Y¸UY|ÀTÜqçˆ"€Ç8Ñ0•Ì“«aßs|kd3~±î§s.¹…e«·RJÿ±'Ü( /4<ϼqlïÇ'¦Ö¦å¯4•±d¨)2ÀáXïuýÖ×õiyäèW|hðkDD Ådšæo8½AÄŽMË.¹mö™;W"+v-@3'œÓð¤ÒJlÞÙïÈYB.—Пújc£ˆ_Õ‰ð¤îþjI©O>åû½óýÉ™ççT™ öÉHѾ…ø¾ã[d ·Û_j¹”¿¢l6Ë0K`˜€e 4|ë^îõ¥Ò–#šë¯ªÎÖÎö(Üc£ˆ•±dhRwCÈÅî–ìý.¿±ÿ 'ªH1X–õçÚüÜ%mL˜…[)-ƒ%u D¦kàf¡á{n£s‡_Ýsç…ÏÃ>aÍ£ !©Ps{BÞOXe=¿Ý¾uÞßœþÅü¦œ3—|&¢òVjÅòþ €£çƒˆ³'gè«pÙ¼a¯c ©¤¶l±¶¼a©ñ}ò¯0•[œ—³œÈY–Èk@kÀÔ¦ÐÈ+ú–P†1 .ã—YÞ ÐÙž0Þ:âÌa1Sš}æ•K~eò¹À]°I'Fö½GÕ`2$µÆùÆNÚ}³’ݵß9ñ³ªõuóϬ™ð¡6¥X"bJ©H)ÿ€s'ãi-ÄókëoþÍ+ÃOn†rÃdÜýW'/ølkY%ÌKdÕ;¿²–DÖÈiŒ’ÈZ…î\÷¿væÖøäí éwm|.Ü$—ÙëVÄR¡f¥EûÄÚ>ÙçSyãîžý3–ÆÎÖý{óÊc'ü)V…ˆ@ /à‡þØÉ #¶.ЃÑ]¨¬Ö²—yaŸ©èZëñ{ü¯0™½¥2Ry‰„åBÊHšIÓ@Ò20ûÿ)KáÀž.G¿òz¤¾û–ºÌE¿IXi~ìaOH"Ý}º=!¨%ÓWùW§ž^öJõåçWO¼æl^HͪH!oøJýƒ”òxÛ2ù¼G¬ýÉ|ý¹¯ïÎ7 —Ë&ŸÍÇù*óØûQ2ç¹âyƒ¦ ±œÄ i i¹. *GÞˆxãƒYG¦Ö›®®ÉTU¾ÏÞÀ‡$àQÀ«#;­+c© E[#{B>ˆ[+ysßÁ)‹çë~Ô|ͱÁ)1V…ˆ@ §ÀSprãˆ×¶.Ñ÷=´†ëïìá¶Kü€…qúæN˜ÉÞqis ÄrÂFÞÀ`Þ@Ü4`‡¯p[·¶sòùY& ¶(Œ¤Q+cé CÈð4äbÁ?9µnÉÞÊ©ÿÚ|ãÉ´á±X"²“’}SJ= ÀÙ]̱*±ù¹éÃù£I—'g—eI1>ßq7ð¤/S tgÜ8óc{o뻪°«/Œƒ1?Χ=ˆÙ$|Ä2òøÁ^G®BtÙÔ€¹d~8?ì¿0G mì9âÍ ÓÁIÝÑž=Ã#„ÆòØé‰ÿ÷ē˯ˆ¬dEˆˆ¤0ÞÖZ¯w| mY7¬%y“ÒŸ·ËGÒã5kÇ^•Ü…Àq0æÇöHë»+ÑÚÄ餱¼aÛoönïð+˙߃ÜsGÃ¥Ovž%€[åÐòa# !S:£µ\îi¸ªó)ß·ÎnXü‡ç^œå·r+BD £dÆÃNo Ñyn2¼ZÿA.!=¶ j¼&Ì71€.Eý9‡ã>l‹„±ñBà8“ô"f–Ï3ÐþŽÜùÜï“ú£7]dòù™.€ÛĈø†é@s÷` CÈ%Ü'~¥7dÅà‰*V„ˆ@FÁ4ÍÍö9½‘ÄK¿XòA&éò™vù<–‡9 ^µ £j7 tgÜ80À¦ž 슆q*áCÜ,Ï/]Oís÷v%]NlëÛ®¯Í„‚ÆÈÏÓ)¸MŽ8„TÆ’ÁIýåûæù‹ØBD £}ø‚½ GÞš‹®¶÷½sùm3$ï2,)‹< «\ýj²Jà\ʋףlèªBkçÓdUùOæßëàÉçŸÉð«ß4YwʯeX‘H&uFB.õ~¡Þ‡?µlYòL+BD #`YÖ§ÝJÊ2ÄÚ§¼ßéum@”:gyVc†K_èéØ×ÀæîJ¼5èGOÎã¨1¹Œ)îíväð«ùsBùù³C…éQmÀbT!dJgo„æ |‰j̤ÿO½¸äkç7M÷"ÏåÿˆˆäR3€rzC‰ý»#›¹h—z—¯:c§Ï“wÅ2ÆQÑh0gà`ÌM=hí¢3ãqì2toìîôå²–#zï¹½¡°;mO”À]#ï &2þÉÑZ†Ü7„Æ5ƒÇZ>úÔ²y‰¶0+BD —@)õCQG·T*/ý|ÖÅþw›§:k§“s9€ðÖûÞí N&¼Ø cG4Œ3I/rŠ_–îÛáÌáW¡ ¡o½®¶ð×’FÜ-‡æb0„LiÖ Å2uùDàÏÎ>·ä«í/OwkÅÈ0%ü»ÓKl_¿ìbÿ/mx¬”VÂʹ\Å !Þs-ÏZc~lî©Ä‘¸ “óUßéHçO ºøÙ?zc]Úï£E"êðÑ‘‡@*ãŸÒ©cáÍ^C\פåïNüdÉeÉnîˆDD á”úG·Vow#öliºØÿ޹ü¶éɹÝÅ žìÀÐÒ¹{ûx¥§g’^(Íšü¦×_ió;uìÙ§n¯OéhÀÇFÑ’Îù¦uDê¥â·ø#Õ”ÿÕ™§—}¡sW‹dg'1€| nO:½ÁÄÆg.º1aŸ;”¶ËçÈzŠÜâvöóJwÆ]½!슆Ñåà¹DY oîétäð«%óC¹™Óƒc^Ö.„øÓ9ïÔŽÞz©8VpÄ—C­äíÑ7¦ÿíñ5‹&e£>V„ˆ@Þïá@©ïÎÞJœ::g¿çÒŠOØ6=DŸ;¯‹¹¯Ë™ÇK_ÖÀîh­ýAôç] ÷wxÄ›ˆåù`ûé;‹÷FBÈô¶H½Á2*ÓÒ½Uß9ù_ËïŽì›ÀjÈÅÕZ¿èèÓJŠçŸZô^ÿ«Ý[´ÍǀЯ»8K»á¸=@’y‰×¢Aìî £/ç OëÖ6Gö~BnU3R6[Ì‘Mï„~ÿîÍæ=ÓÏu7¸MN` ÊŸéÞ3û¯O==¯&ŸâÅ‚ˆ@ÞóÁUëï9½ÑÄ›¯/D<ö®ÅYMÚNŸ#íógΊƒ¾ø·4p$îÃ¶Þ D<.I| #OŠzøÙ—]ÝœŽZ>½=Fw¦ˆÇM½Z¢w„U÷ä-÷´ó‘oÎd÷Þ(ÍIuÖ}ïäS˯<\ÃjÈ»mBìqt«å²>ñÂSsó?Ÿò6¤´¾êO«ÄtÆaÑŸ3°£7Œ“ Ÿ³Ç)ŽPë¶v¿²œ7;F+VNJ@VI´ö±¯?³XQý…%zGØâ6-×ÔöH½!dÔfÖóÐùWüá¹gù7/$"ßôˆÓNìÚü®%y.¿™4¼¶Y +é÷'€äržu­5p8îÃîh˜ËéŽÂ»:9wÆÜšlmcÐúÕÿÖ™ñ`[o}Ù"Ou£Žå ! »ýF}oÆòØé‰ß=þÔ²¹Éö+BD X–õ 'Ýr±jñòÚ©¿ùŸ{Ü)»|„¼ËeYÅÚ0Wž $§^ë âTÂÇ•­Fáô‘>woWÒ‘ß ¯XÙòžC7Ó–=}aûŠàGB Ó2&wD‚™ Ý.Dsä?;³vÉg»vObW1€\È þÞñ­÷òÚw-ÉÛî«LÚé#$‹5 +_~ÍŸ´$vô†ÐË/}GÍ©“Ï+ª¼ÖÜ%õí5ÕŽ%|ØÛ€YŒ ê£ !–%'·GëC錗GõèZÉ»z÷ÏøÛS?[Ø”`M߇^qÍ2¬YÃ.h¢2 PJý€^'7žh;3 ‡÷ÿÚ„ÁSþƸ­¢ý¾â,œ+¯¶ç%v÷†¶x¿­lÚ‡öõ8røÕÒkšÓÂøàÛ@WÖƒÑŘP'€OH`„{uK¥åäŽh}E"Íý- dZ*Rý?»âŽèVã"¤¦a:²Õ_Á¿¬á°5¢r Rþ?§7 X÷³%¿úïÇ‚M¶ ±?#‹±Hª|(¥-WûBÈr „‚xcw‡/—µ·S¥”+®Ÿ4ì•ó¦Ñ t¦‹ÐãV}abúCˆPZLîê««%‚< çòÆç;w\þÇg×Íö[9~óñž)DÌ@EÕ·°fs3‹AT¾J©vrŠCoÌC´û¿¿é;ã«Kg¤Û²Ëû7]†•r»Æ~€Ô`y´·ÒÀë µG‡#‡_ÍZP—­¬õ_ÒZW–ö q2Q„Î…j|\#ŒZkѬnè ó(/œ¥ñ3æõ÷9ðd%²úkxlý|ƒ¨L€«Ý‚–éÏ=¹à¿Pt{+9 ë7ÅÊ£¹'|ˆs¥«‚‰t$Œó§9‰fÅ“G¼`Å‘¸oø¡Çºc±JŸ4€Šv¥E}4^Õé¯âÑ^85fÒÿggž[ú¥Ž­S%¿ y/„|o¾…Å *Ï¥Ôÿ½ðÜíX¢uÇb˜ÙÿnË6ouÂNï?ôŽ}´ÿ¬x^ç›gymËy¿—«ªó[³æ×ŒjfT[Ú‹×ú‚c¿_HCñ*FþÕÉpKg´F@ õ…ah-nî;8åøÙÂÆì W{WJß‚Ç7|?håJ!Då@ÓZ¯ut+&ãbýÓ—½ó¯'|lÕ’x²¦!Æö1¦ †` p©ÝR–ÂW»9üjùµÍiQ€¯®{sn쌆‘ë)4á ñFÑQ‘H§tFk¥b)¤é©Hõßžüù²¯f5ÞëB##ØÿUüh‡•YÖúa§7¤Ø¾þ¿—ä}# «Ëž$–—ˆr³Á‚ŠdäÉÃQGn¬våõ-cösJâÕ¾0z³cü\ï¿0«qä/áÉ[îim½ ÞO¬B›˜ÿŹ–°ïAIò&§k$²`YÖ3Ž;:€œ88m§Bp<Ðh«uŸ4„N©1ý!Ýöl×ö4çwZëÖv¿²œ7£¦nBМqyí˜mËiiàõ¾0ºÆz¯¯>f“Fþ çÊ›®émцPŠ»¦š[åÙ»tћ݅ÉéOl¾Ÿ“Ó‰¤L¾[ðˆ£[S))žj¼j¶Ý´ëŠ1 =ö|àìÎòUhûwu8rLÛ×OJõU_ah¯ó©1În· `òÈCˆ´,9¥£·¾šRñŸX89@Êçù[= Û~Ï]bÿ«‹JºŽ‡š’)éÉÛé½'üÞ¬é6Ì1û½š b#IK"É¡êuêpÔíN9nF¿Ë-õÒ4en˜ÆÐªmcBp‡¦êÍŠI‘ÁêÆè`Ï*îC‹˜Oæ›øõ-,1€Ø[À¿9ºE³¿X÷Ó9J§ ýv{û± ìzA4l× ɰ÷£ÐönsæäóùË3°»¨{& àljŒG8I·ÀœQ<*-êúâ•-ÝýÜ+„ŠK£ñ»øñKóX b±ó Jý€¤“UìØ´ Þ6Û.€ †ÇxV‡½H,ÏÕB šÏÓ¦8´¯Ç‘ïVÜÐ’Ÿûö §“cB€•0otÙ¡"– NmÔI.ÓKE}p‘®°ú¥kX b±¯>9ºUzëäö -¯Wΰ]Iù<ù¬×=f“dqÖ^$ÁáWõÆ®_.k9îîÆæ9efõ¸ @<óãd¢!ä: \5ºæ ¤r>.ÓKE§!¡]wã± Ÿ†wß IDATÖ ÀÄbË/”z€åèkÙæg–vxª²}®`Únソ*˜³HÛ'„$,Þ‡ ißÎG¿ZqCKj¼ßѸ'Eè|Z*kÅP !oÞrO?i dr\‚ŽŠýXv%žØôe<ú(7":¥µ~ÆÉ +Ξœ«Ž'ôÙí½÷…‚)k¬öÑÎÙ#€ä”@ž#A ¦»=áj;=è¸I5¯¡—|¸©$6&=÷ád1BÈ| Ü*€Q,5`˜–1­=R_™HqS=*.%fÃÝòu<º¥ŠÅ ›ÑZÿG·¬ÖB<ÿÓÅoVL¶]ÑRèX80vóxl@òœ [P¯¿rÞçmýEš˜öú]%óÉÄ}8/B™.;%0š> ¥eKWm}_ŒK¥R‘C&ÂÈÿ>Ölnf1ˆÄfÏBˆNn\qpï‚׬êt^¶ŽÖWNH)Ææ¡é†6+(q–ê^n)¼õj—3÷þ¸nRÉ Ã<–(RiÀÇ$0Š> ­µhˆÆª¦tFk…â·TÔG´ dõ×ðø¦¹¬1€ØËÃŽnÝ|Þ“yþg—Ÿò5 Øí­g<.3îsgÇäų:Kÿ«pSñY§Pííñ&b9Çgk™Q™ožVi–â{;–ðáH1BHƒ>n¡Ñ½L(‘Ìèè©wsr:“†_Äã>ÂbˆMX–õ€ÃNn`ñúÖ¥ûƒÍQ;¾÷ŠðØMF?QúÄÒ iuèÞW\7þ“ÏßÏÉbõ„Tø¤ÔŒîe¼é¼wz'§SÑCˆ„’ŸÀêõ‡f/1€Øâ´B<âèŽÇ*_ުŽ׬Á°?mÕÎè'tÉÃ’¼ÍD¬?#OŠzö¹}—Z°¢1[êïóX‡Ãñ"äÃÐ…2yt'–+oÓÚ# U±T€g÷‰Æ¸«7>€´r‡Zb)u–e= Óɜغia‡¯:f»k-€¾Š1Z’7‹’ŸŒîb)ˆÖmí~¥œ×´øÃMÏe‹~*áÅáXBˆÀ˜;Ê“KiÑ鯙¨äFE~l›ÀÀïaM+=b)qYÿêäFç&oèÊÛr_”hU(aÆØôU/õÂ1X…ðÆ®çM>ÀU7”öð«w…d‘Bˆpýè7,TJ‹šDÅÔÎÞ:ƒ;§S1i4!×ÿ <ºe‹A %L)õ¯Nnè»öO²ãû¶¤ÔáÀØ´Ý äJ÷³» Q?ÔŽº£Ý)—Ó>÷ÔYU¹ú¦í¾t8•ôâíX‘¦ë,•À å”ò`"ãŸ~®»Á—3]<ã¨x6¢nóëxbÓtƒ@JW€GÜЙ£‡gÎ9[6^m8ޱX’7àtéNñ ŽÂ֭Μ|¾âúÉi»¾÷³Å !s$p§F9CÈ“·ÜSÛºÉ w¯¦b†?¾‚'6,f1ˆ¤TÏS¥¾ÀtlK+ËXÓz¶ÂŽo='¥« ”n/ƒ€Ï°@#“M›âðþÇ=†*ñæ¡é9Ëž›KôV…ãc°u;€¾Ò !Aƒ»ŽÔþ]¾\Ör\'Ò’4¥¥Ëþ—ööŒûŠBjpFý0(ê#ÕS:£œBE !B@‹r™^b)QJ©ï9¹±U:xîP·-‡¤d=.3ôͰ’ƒ e@v8oø•+V¶¤ËåótfK¯w½+‘ŠÖÊ–,™b%H"ÅŒ0$ˆH9$âäp熹9tW½†”DŠ f0=7tß'„™¾}ªêVŸ× ý)Kÿ¥„tÎßœÅÁ|°Þeª¸½Ñ‚ cîÒˆ+€*°zú/‘mEÝ:w \_CýBˆ’б ùàñðÔD©(ƒå!™'<‰Žõ„ÝÕxï9‡­˜pÚ»[p®2ý«bêìµOÊäón7ÏéÇ;^>ä­ØuC/…aV+À0ݺV\(­#ÑPëH,À@¡1D©DæÁÚöøþ6êB©t]À)™'ý—{‚ÕzïÃA_‚±89Y™aX~›N•°¦êóé'÷K÷öÏpè‹VÔåÍj_¤`ÅþˆZ©ÒØæ¾•œnÀëššDÚ3«?\gÕ4 !J´¢ .ñØ´³Žƒ R!ïcßyÒ/^¨Œ&íÕxïy‡­˜p;Œo°0Ry"De€×B•°¦Â©C£öT¢ ÝÞ¶ê––,SÍmv´hÁkQ7 ¥!u ¸W~r:g¶`ŸÓ7J¥z‰z;JùÂWðäöN ‚H ëú¥Ý“„`ϺX[­÷?ªIÌHE¬c•y  0¬)qpG¿táWŠÂ°z}KV[E ^x/•ñø„ ,˜þç©WÛ‡Çjë£ã^ú¦¥yàÃ…¢ö§x¼{1 A¤üäü³Ì¿÷Lm¶P¬Êù/Ú,ÚøLô¹ €x剿N@&íœÆrÊÅ3Q»lvÏ_V›¯ 9¥©XÐTì+¥QÜ®72L;&R€ÕE’þޱZ…Jõ%Y!ð9<Ò½–Æ‚ Rf8çßÕþb¡h}ùØ¥PµÞÿHÈ—0¼;ºp´òH£H;Õ$9¸cÀɹÎîooÏÈfsJS±g̃´^¢ÇÐ¥`›þå<™œsNïH½ƒJõ¥@@ýx¬û4 ò’ðï2Oþ–£=õBTçKӢŢÇ|®”á~Céʲծø(dR}mPºøú@­SŸ{]° ã|gu¯y(åan‡Ü£¾é_ÊVÔ­s ¡xÊMß^¢DBäCx¬›$@Ê çüë¤}½O;÷Ÿ®Ú2}£_ 3VAéŽWž(«§S«ráTÄÉXd³ûúõ­Y¦È»•繂½Æò%,0UË€ûT ÝŽ ÖŽ[Fc…Jõ¥!ëðøÖOSÃB‚Hùèðs™`óá õÕzïšEÕÃ~OÒð Ÿ@®²Âxêl$@®Æ¡]Ò~¨ª"VÝÒœ•}îuˆz1œ-a4“À‡ Ê àO{fõŽÖÛ š…¾ÍÄÌ‹u% ?Æ÷R A¤pο @Èjÿ›ý‘šË#ñªuÜÂ_R³¨Æ–‰*¼%B*ˆ€M‡M¡®èW"ŸÕØ™#£Ò E]õy¯ßA p8îFo¦„5~;/Ä€uä‹¶Îþ‘o*K¥z‰|iØb¸ã_ÂÃÛi½$@ÊÀq!Ä+2À‹‡.4Të½ …‰ÑoÜð µÒ*Æ€F;‚\‰#{…¼.]øÊ ··fhök?pb܉ ©ûS p¿bH¿‹.”ΑhmS8^Ã@Ú’˜qå>jáϰi»‡ƒ RbTU}Pfû÷¿9OçªöØ?æsgòv«±r!àDe=ü›ú²^Q€ H×û£¶Ñ­Í^"UúœM:p<î‚(åA¦‡ŸTÅÓ×Áœ Œ§|ýcuM§8}b¦½ äô¯à‘îA¤„hš¶ÀQYí/jº²åpõ6&€¡úš¸btYÞcbBˆT!›‡JoDgî{–þK éâ˜WßÖš¥üÊôem8uCã%<Sܦw0À€éÊsûGë]™œf”˜éG TüÛÜDCA)!Œ±oÊlÿö=õšÎ«6„%ípâ.»±É¸yG+Èág@3UÃúî”ïôÃbUD×Í-9šý÷'\°boă\©£óWªW-ê–YƒcuÔ=˜q8|€åÏðÄË4 ¡ëú“zeµ?‘ÉÛöœéóW³ £!ÿ¸P .(pB™ÊIHoqæéËú[hšÎN¼>,]åÒÕ9—×JÇa“ ©©Øñ–¶WÔXª—º§¥BÀ®ÿ<¶y. A¤4|WæØväRC5ßÞfÑb5—å-8R9ÄgåÔ”ð·8s(lK' ÒícknoËÒìOžœ®`_´Ä½B€ß”ê]mL©Þ‰îé£Ô=˜a¢ØËñÈKËh0 %€sþ}ã²Úi$î9Ý®ê޼#A_R·ªÆz觪Òá¦dô·9¸³_ºð«†VÖ>×O±xS}—ÀýQoiËôÂcµ|L\Ó¿œ­¨Yç „ëý‰´‹f•˜9 ëgñÄËkh0 3OÀ¿É<Õܘ¸¢ðá`MÜЋjTV–J=A0É*ÎD¥KνaC•Þ½fŸj¢LïÙ¤£ôÝŸZpŸ1¥zÁ…Ò2 µ E‚ŒS÷tbƾ0 8ÿ=<Ù} Ad¦Xο…‰VtRräâHp4ž¶U³ qŸ+“vÙŒMÐ=+€peˆ•mNz~p×€Sp¹zˆÚìªXqS3%ŸO“ )öÇJ\! øM©Þ.c>חʺçôÖY‹•ê%fH„0†">ŽÇ¶}˜ƒ 2³ ø™¼L°Í‡/ÖU»ÃuÁ˜¡ éÀžÊ9uèpKžŒÎ#{¥ ¿Z¾¶)kwZˆiÎOTÈÊè%~ *nT€»ŒéžnÏísûF¼Tª—˜Y!²DAdÆpþ J@_1ì:ÝW›+jU½6r6‹ó{†^tÀ¥Ê!•#d•÷䙈-ÎJ÷Öw͆VJ>7¤¦bï˜Ñ|–Òleâ4$hÀƒ\çj;•ê%J!BÝöI û#H€Ì'„[e5>“+X_=Þ¬v;FB¾dÁj1ÖKß+€ 9™ã‘7瀄Éçísýſ޶gcÉsû¢^\N—á!à^˜g„s8Qª·òBˆ…݄Ǻ?A"„ 2C¨ªúÌöo=z±¡Ú€8˜®78!}ÀÉÊP u]Ê’¼¹L‘=–.ÜdõúVJ>Ÿ!€S 'NÄ¥Þø¬> ë`ÀAŒw"/¤ÞZÐ,4³ÄŒ‰'º?… ’ b4š¦u8,«ý#±´óèÅáª?ÎOºœ¹”Ûa¬ãv@rø0Ç#_.È‘½ƒŽbA—êí›ÓmåK×4Rʦ7kÇþ¨^†åµD>iL÷t{¾h›Û?ZOy!ÄŒÁYæÞôiA~$AÄpÏØ×e¶ÿ¥ƒçÌ`Ç`½?ÎfܱEÀþÊ8iràRå:9²[¾äó75å¬v•’ÏKÀXÁŠ]a/b…2ä…üº{ºwÊ !f\„(+ðD÷çðÀv:m#H€‰®ë›ôÈjÿé¾HÍÀX¢êß -}4ä3¶Áä©Ê(ËË0[¢Æ„C½ ËÀå„\] õþ(59®`_¤ M ·º§«ÀtOÿ­¼…òBˆ!l1æéŒí$B F¢ø¶¬Æ !Øó«»1áÛDüÞTÖi3.„EØÁ+¢VZ›+»*Gcƒ;äK>Ÿ5?P¨köÈ—ìSn¿ M Ç(y» K6Sª×›ÊºgS^Ȍҥ…CÒ¯c´Ïãû­´ F=„8ÿ!€¸¬ö¿öÆ@(•-˜¢Üé@}0 #C±Fœ.¿ã¯0`ž}A4MgÇ_vÈö\½¡Jï–‘¾¬{Ƽ¥ï ð{ `€kûë¼TÎA³j<‹Š‘Ðßônžo\Γ&ÎÀÿ¾ó"å$@ " à_e5¾XÔ,ÛŽ^ª5ƒ-y›E ÝdŸ²å?iwåMŸ rêà¨=“*Jµgy|6¾ôúê|^fšŠ=c„sexã{«{úÜéûµŠÎÕöá±ZÊ ™V%.6=pé©¥ž“³3=Çl¬_ÂÓÈ%H€òâü[¨˜ºG¥gëñžZ›ã­ÎhÈ—Ì9­Æä¼V¹ sM^ëÐÎéj]77g mÓ•@+ØóâlÒQúÐK+€(À:ãòBÚ†"!Ê 1ž9™ÑÀ?\xjy]>i“rë„Úö°i¯“VAdúŒxRVãÉŒs߃5f±§¿>Š.ÄY ”_„´: ð˜´/Èx$«\<•êhŸ1FáWÈ…”ûcnh¥~'Ã,S€)€®/•uÍê×[5M¥Y5–æ|Üû—~¹rnzÄ%é´#›ú3<ºÅM« 2M8ç_CÅôÀ.=¿:|É4 vy›­ñûŒ ÅÚÉ˾:æ›´;úÁNÁåªB;wI(¬wQòyÎ[±£\¥z[ØD^HÝô/åÈl³ûà ®\ÁF³j,5ZÆñ÷—Ÿ]¹fü¼_Ê`J3˜ú§xî ‹VAdzœBl–ÕøÁ¡HàÍÁˆi6’Ñ/‘·[«_p¤üú´ÉQ„ߪ™LýGöÊ×ûc͆V:ý¨`r\Ákå*ÕëaÀ=Æä…XŠºÚ90ZïO¤ÉQ4‡(Zþ²¿{éÆÈñz)@ Ñè—I„$@¦û]âA™íßt¨×4or€Áú@ÌÀP¬ƒˆ•ù-=ûÌå·ž?=f‹…³R…‰ø}ÑŠ:ê|^ûȉq'ŽÆ\ÐË‘òAX?ý¼ÆÁÚÂñ`CdÜG³j,ªàÊg‡ö,úìÐÞ6)€)͈ÅþÏ=G"„ 2 ^eŒ½.«ñgÏõ7D’ÓÔùÎ8lű€¡X:€WÊß$`ÓÑè(šfÝØ9 ÝéǪ[[²L¥í¹ZÈÙ°kÌ‹²i[bL^ç‚ÕF“5ƒc!…sJN7Ò‡À‡#GgÿUß–yŠ”_kÖ‚˜ƒDAdš|SVÃ×ÕŸô˜É¦‘/ahƒÂ'ÊŠµÈ—…<ˆ\ºÈÞ8–*ù\QV¯o¡ð«*#­©Øña([†w4æ…xÒ9לþp½’Ó çÆñ Íþ™%v%ô½X b¶?¥êX kD×õ_¸ «ýN\h*èæz;fxƒÂ×(¯M.•c¶ Òìt ºToc,¯ËÕÒ¼¨êçƒÇÝ8wB”ú$ÔÀ¼[¾h›Ýnpçr”œn0‹3µ\xz©GËJØ•^mA!ýEêB¹Æg €oKk|6ëzòtÄj&›ò6‹6\Wc\·û"€Wô²‡bÍóä`WªÛ=²G¾äón§Ò»ÕNoÖŽ}ò¥ÖÎo÷ ¹qúy!jQW;Æê‰•R5˜Îì˜ÿ/>µ¼®˜²Jg'ËŠå±rÌrW×Õ©£öLJ®J1‹ºêóž;%Ÿ›”¡œ {ƼȔ:/ÄÅ€O¨ÀBc’ÓçöÖ{29r ÄÊuõÏûº—Þ5vB†…Ê,m_À¦MTð€1iñÎù×Èé0Ážß{²nÜâ0](Ö`]M¼`Uk¤ñš"å=™ïÍ©VÏÕ¡]ò…_ݰ¡5ÂÔ$5;ǼÉ•8µIp»¬›~rº¢sµ}h¬ÎŸÈPOQ…`4¼{ѧ‡_k•Ï™Âl䃟Ã÷Zi%!&ÇBˆç¥`'.}Þ5¿ßtv)Šh E„bP𔆉…e”ª*WIBúx$«\<“êmXm£[›½ Tazt®à`̳É2D.S€ ˜æùÅDçôuN7Ž™óåWfKçœ Ì‡kü±iõŸ!BLê;#ÄCÒ_(Ø_Ú}¢vÔæK›Í´ŒÃV Xš7 à`yËEÔÛ+ßÇ=°sÀ)¸ê«´fCk–vc¹¸r”'/¤C™HNŸ¦tx»szÛH$È ¨sº¬mû}/É×5]ˆ…(?‹ Ý1 v1Æ^“V€íÛÞõ‹P×%3Ú6æ÷¦R§q… ` ¼Žõ2ÖJî £’õþ°ÚTѵŽ:ŸËÈPΆ]c^¤KîbÀ}*Ð<ýKùYwçUÈ2šÕã—šÿóÅgY—KÜq,Åœ›î‡ QK„˜ _—ÖòD<°ûØïEG}ÌŒæõ7¢šÕbL[‰ªXeìò`WUp(ÖùÓ[l,+ÕüÒÕ 9§Û*@HIZS±+ê)}^ˆÀÇT`ÑôýPï2… 1 `yŒV*£*Ö`ϸe°')UxÁò³6‡…’ω÷$§+Øña0[⯅"¤4D%R£eÿõò3+dÝR‰} ¼´ŒV ⽉xLVãÙÝ]ùÇ“ 7˜6!?éræÂ~qù GÐW>Ô¡ \ç+ÿKøƒ;¤‹_³’ω«ø]ŽÄßjZXÊmÂ`£,&R‰xxÞö·—_\¾8Ýç‘èË @Q?Ç6Ï¥@„x8çÐ¥4>“ò°-¿œ·;°0ú¦«!bV3GB5‰ŒËaŒó(tó‰¾2Ñì, ÉQ¾ÚÀš¦³û‡2}UÚçú Mí> 1 .¤8/qrºà6XG"¤qó‚õ?õl^¾*}YžnôB±@¨_À£[fÑ Bü.…ÏÈj<Ûµµ ~ØrÛy³&¤@oC0ªYUcÈ,€­zißp¾‹e58Ê4]§öÚ3)¹êܯ¾­•N?ˆ)ñvrz¶ÔÉéËÞªe¡9¨4ì¼hù«ž-ËnŠŸ Èc5³ÊðÈË-´H€ïéB|UZãG›q`gcŸ=”3sBºnQxOch Fåƒ 8\>½fQ€åþò¤/ÜÕ'Uø•ÓmåKW7æi§$¦JBS±gÌ‹X©“Óç(ÀÇ”‰æ…DEaåºúçÝKožI$BPø—ðè–zZ$@ˆw²Ÿ1¶GVãÙÖ§»ÀÌ ésØŠÃu5Æu€ß/€¡òƒÔÚutºKëÇÆ²ê¥7âR••\ySSÖjW)ùœ¸&ò\Á¾ˆC¥NNodÀ'TÀGsPi¨B°?Ú±äŽØ©ZiŒÌEùÞî§@„x'I+@.œY€Þ Þ<¬ÜÌ éñyÒqÓ˜£` /kµEÞ,|–Ò¥0ÜÑï\"_œQò91}8€Ãq7Î%K|$pŸ 4Q^H%ŠÏî\ü™DgX´/á¹ç\´H€o¡ëú3ÎÈùtä {þ§Ë`w`aô WSÄÌæ5cy»Õ˜,î €måë¢0 +˜.M—t{mHªð«Y …ºfœE*Ãy3åÀñ¸ ¢”Þ‰p¬Y$B*Q„|npçâcǤ1Z°Œ;¾€ï´Ò B¼ýµ`ì[²Ï޽¾™¤~кá\QQMëtq0ÑÛãªbŒƒö•/Ä­r,ñÍ|Y®Ä©Þ IDATó§"¶ØXV•é{qÆö Â@ú²6캡•²÷Ÿ à.XB"¤âœ:!ØgFö.øðØyDgpÅ>!hA’!@×õG Ki|>çdÏÿl ÚüùWý ûÌlnÁbÑûQÆ JJ?*€ å!m®Z3[šwÿN¹’Ï=>_²ªž’Ï Ã+X±'â)m…,`½ÜH>_%ŠOì[ðQ™Dˆ`Ëðx÷'iöI€o¹á¾'«ñlï+]oÿù‰æ›úÌœL4) ؤðÄÊ—q]MîÊÉ¥‹ìÍccv™¾]ëš³Š…¶\bfH½U!k¼PâCÅ.¸]™$DE‰O í[øñðáF‰DÈZ<ºåvš} Îùÿ’ÒøøX­²ksû„3B:Œ†j Ó˜0›"€Í|BÆ–•«üéÙ$ït º4. c «o£äsbfÉs¯E½Í•8~!î¦^!•·ïÜ?üú‚{›ä1Z¹¿´šfŸDüXVãŶg} ²;°0zÄÝ>bv›û‚QÃ’Òcv”/ËkåX\c¼ß|d÷€Tæ. åƒõ.J>'f]cnôdJ|ÀØ©÷P¯J!¿?r`þý#¯ËѸO0«zÞ²fŸˆôpο@“ÑvÖ{~Þ8ñë.­?h»íBÆb/˜Ùf¡L$¥ëƒ’ÒÏ àxùDH‡+V§qÇ0ƒ=ã–ÁÞ¤TKn¸N?ˆîANŽ;q*áœøK©¨g"ÄMsPQÏa|<|xÖ*‡Ó¥¨P•?Æ/wÐì“‘Kž–vó{iÓŠ·ÿ·xŠ?«_cúP¬‚Å¢÷6#`=þ÷ §|"diM>«1zêÀ«ýR%Ÿû}áòZJ>'JÎå´‡â.”´ÕNŸ X¬J!=:çÓÃ’ˆ0+8ÿ<~²½–fŸˆÔpο&íÆwêðuˆÿ&áxkpiø´«eÌìvg\ŽüPmMÜ‹ ݈—')]a@W ë4w MÓÙÉ#Ri\kK–©´Õåa8gÃþH‰Ëô:iÜ+U„|dìèœß—'Ë­ð%üè/Í> ™9ÈÛ!¥åÅ¢=ûäu¿ýO?h¹ý\ŽYM–õ{Sã>·1Eò^*_Rº[åXQ3½6í'÷Ø3©¢4ûŽ¢0\¿¾…¯ˆ²)N”éÍéTªŠDˆÀ=áÃsï=Ð,‡ÅjV÷ç±i“fŸˆÌ<$í¦·Gtí×O¿»·ðl}×lhðÇ3N[΋żÂK×ý[Ô;Š˜ã¹vSí”+üjÁŠº\MÐÉAe&¥©Øñ@"ýO¼ùäèÁyŸ”§:V;ŠÁ?„´øI€È‰®ë/8%çÓ/ácÛ~5ç·ÿ険UÃõ1³›.ÀDcmD³¨Æœø\Àòù´ <9ÔÚŠS×NcYõÒ›q©ÞBݸ¡N?ˆŠ!««Øõ`œD‰Ü;z`ÞÆ±cr4+Ô±¿r?uK'"+‚1öMi­ßñb×oÿ•ø—¶ oÕôåI‹…÷5…Æ 0c”ÃÁòuJg XÈÀ¥Níóîèw.¤YîZ§>wI¨‚¨ ò\ÁÞˆáœJƒ!»(ûÌÈÞwENÔËá‰Uxüå;iæI€H‰®ë’Ñv6Ô߆#ûÞ±ÑõÛC¹ÍÁe—e°?ã°{‚Ƙ1^øË—Ç¡·)«)¨Ê$E޽6$UøÕšÛZ3´Ã•À˜½ ‹'"Øg‡w/¼=z6$‡ÅâƒxrÛJšy 2’ðϲ϶üâw¾ø?m^Ûßgó'd°?éqæFk}ÆTÆÒlá@®<¶x¬+'Ù¤ðÜ©1[l,+Í+WÕ¢ˆ®[Zr ˆJuÜwá\’:ÊŽ*û“¡íKn‹ž6¿ŒA÷S RÂ9ÿW)mgožZ„á~×;ÇøAËo™"E²î¨ß›ŠU+‰ÊXe¹Gó'‘”~`‡\Éç‹WÕç=5vJ>'*ž7SœNPÝ\!‚}qhÇ’[bgƒæ!Šœo÷ÓÌ“‘€É©¾t•=û“eïþçóî†Ì«E½² Ã`CM,ír“ <$€îòUÆšçÍ¡ÉqåT‡\ºÈÞ8¶Ë´Ìo¸½-CÛQ-\JÛq4æ‚4²‹/ ¾ºdÍøyó;æ‚y¡ÿßyÑN3OD.?œó¯(Êh;;¼g%ò™ß Çy´é–Þ{ )Ã(èk F v«1IÊçp¨|/Ü—û3ðZÞ»–À¡ÝN­È¥©bþSl•߃Ç6Ï¥Y'" ªª>(«ílÛ¯Þó„ø^ëod‹&ËXĽÎìhÀ;nÈÅr˜¨Œ•+Ü䜨Œ¥i:;upD®ð« mtúA˜‚DQÅkr:‰™ñè9û¹ôü²†|ÒÜMc¸¢–?ÂO¶×Ò¬“‘MÓ68&¥¹üæ<\8ûž‰nÃöšÂM7¿!ÓxŒ}ɘßmL~ Àæò•ççÍ¡ÿÈ =“*J³Ç´túŠísü”|N˜†´¦boÄ‹´N®‚Ì´´ó¿ô<³Ì¯§-¦6TÀ½ølÚKu©I€Hâˆ3öM) ‚±¶üJÿýr`ÉØ1_ûˆLC2Xˆ§ÜcJ¸ØQ¾ò¼‡wõIµ‰_OÉç„ Éê ^ó Y$wAfê ÷ß_|f©S/˜»¡¬PêPÈ|›6©4ë$@L®ë?ÁDU,ùÄ׉ËŒ_1Iù{ÍwœOXRu”îkE³N[Þ‹ÀÒƒ ŒäÕ#'S6YæÌî´ˆk›¨ó9aJò\ÁkQÆI„HMk>îû»ÞçY„É“ƒ8f#¸—fœˆ |WJË ;{îÉ%Wúï„Å©ý°yÃY!ä‰Cæ`¢§¹v̰!p¢´"ä—/Ž:¹D –ߨ˜µ9,”|N˜÷!żN"Dzæ§GBÛûÂBÅìË@°5x²ûšq æw:9ÿWã2ÚÎömï‚®]QaðÍߘÛ/Ó˜èŠÂ/7…Æ4‹A=Bv àbiDˆÀKÛ#R%Ÿ¯¡äsBòZÔƒX¢SdfY²¯þ+=[æ™ÞP Åc¯, 'bv~(§åñÛñRçûýÈ¿µÞviÔæKKõ°·Zôž¦ÐWÕé+ [Ã3ÿ’~ÏÁqÛÐH^¥cž¿ÐÔîÓ@ sû¢^Dò$BdæÆñ ÍŸÜÑaj#€›6iÆI€˜Îù7¤4þ•çºÞï¿ó°òï·¬?«3&U˜KÎa+ö7Æ„b@*¹àE>Q!kyzó¨TÉç«×Óé!Ù³Jûc$BdçƒÑS¿?òz‹ÉEˆ YåsøþA+Í8 33`“Œ†³þË8~à}ëoŸv·¥^ ­¸(ÛØ$]Žü`]0jH£Â€t 33:.‘ÔÙîýq»,sãòXùu«ò IEȉ©¹gôðÜ»"ÇëÍí (ÍpEï£Ù&bîMó¯¡l…SËüßòÔŠ«ýÌO×ö¿ájŠÈ66qŸ+cX£Â€øDéƒyfkØ™/ÈÓ>yåMÍY«]¥äsBZr êÅhŽ^Ë cŸÞ³ð¦Ä¹€© Ê*<Ò½–fœˆ™9!„Ø&åFvöØbŒ½oøð­Žž•­4/`p£Â0€-ºá ŸëË~Å€Õ¨÷!¹p0æÆ‰iQ…`Úÿê’Åé>É=æãÑmí4ã$@ÌûeVÕ‡¤4\׬ì¹'—^íÇâª[ûa󆳜ÉwR4XˆÇ=Nc’ñ{¼¢vÞvö\ÚòæÅŒE–¹˜½ P¨kòè´c²#޹é$Dbl¼¨þuï¶ëšs&Á°@aŸÃã/úhÆI€˜MÓ¶8"£íìÐî•(䯺.øfw–ôÈ8FƒM¨aÝÒß°Ó˜cM/È•|¾æöö ‚ð›“!òâÑsö¿ë}v©OK[L¼Ð}Ö?¢Né$@Ìëˆ3öu) Ϥ âwöeã‰éÌŽùÿ¦çù…æÞ!Åz<ºõzšm fDðm)-lÆþM“ùѰÝ[øQÓú3B0é†IWq¹µ6\°«ÆÔ³Ú#€3×&B~%Qï¦0\¿¾…’Ï âJ"„ÓÈβTýçwtšüip/y¹…f›ˆ 7qþoâ2Úζ>½r²?»¯fn|gpAŸŒã¤+ ¿ÜTÖ¬ª1¸_À…©y}C9õðɤM–1Ÿ·$”Ö»(ùœ â}¸3zºããáÃ&öT¬°ˆ?Æs]4Û$@ÌFÀ÷¥ Ï.@ïïdþ‡Í·]ê³ù2ŽUÑjÑ{šëºE¾S,t  oò¥±žÞv ‰ê‘­Ù@Ï ‚ ®ú‡Àï¾>]ìlдFê"ÿ,x€üi æ‚sþ WØó?]>Ùט"¾Õ¾ñtV±h2®“œÍ¢õ6…ºªN?øA°…£WW\/¼<&Mò¹/àÐ.¯¥äs‚ ˆI  Á¾4´c±©{„ÌGçMwÒl“1ƒ~"£áìØëËINºœß ÃŸ´qÝY&åBÉ8lžæ@ ›¾)`¢[zìýEÈÞã¶‘°<%o®_ß’e*m›A“ÅÆuõ?öl]Ú\0q•}tϧÙ&b*8ç_äkº‡|ÎÉžÿÙ¢©üÊ«ÁÅ‘Ýþ¹ý²®•´ÃQèm F„bÀzÉx†ïSèø)‰’Ï•áú[)ùœ bªxyÞöÿ]~~©›çÌùÂJ0E|šš’1g…›e4œí}yÕTç-w\¼l¯ËºX’.gn !â3žÓ'²‘ÞE|¼¨ìÞoâ7ZïbÁŠº\MÐIõ}‚ ®úBÂý·—_Z¤˜ÕóÌaý åƒ1ªª>(¥áñHHÙµ¹}*¿¢1E|£sãé¤ê6VÜãÊÔ£Œ±é‹€§u ùÎK=»-â(¹4ñnkoo§Ó‚ ˆi° 3úbÿŽNÓ(ØÌYwÍ4 Ó iÚvÆØ~mÛžéšêï„­žâ÷Zo?­á€W«vó¹2ƒõ51¦$Bžã'"oñ\wXšð«@SŸ³(TA1-n‹žîØ9^ob¯åƒxbë<ši fâ›2Íz/ÌÁ™£S.ãwÔÛ™x¶¶ë‚Ì &êó¤‡êjbŠ"$މp¬pâLÊzîRÆ"Ë8®¹­-C»%A„ÏtüÁðÞËÒ=æÌ—Œ³OãG»½4Û$@L®ë? ¥CÍ6ÿbùµüÞ¦†Žx;‡e^7Ÿ'=ò“𬎧^ KSz×bUĪ[›s´AƒUpå+=ÝKL[K0/l¹OAF³MÄÀw¤ §\‡øØ5mTßîøÀ¹a›/%óÂó{S£‰Ü¨Îº_H#@–¬jȹ½vJ>'‚0ÏÛþöòó×9yÑœ¾¨À|<Þ½žfšˆ)àœÿï¡å¢X´±gŸ¸îZ~5+ÿzÇݧdmRø6á /ñ{§Ý-~Û@Üž,èÒìk6´Rò9AäLÍ…„çÿí}q¡ilj»ñè¶všiúΘ €ïËh8Û¿« ºvMÇ™ýöPNs‰“Ò`¸®f<ê÷LK„<}YžäóúÖ¹ X¤m‡ ˆ÷£.™õP¬Íµq]j î³C{ÛLiWT(ø4¾ó¢fšHõ¯gο @¾˜ôTÂǶýjεþú^ß¼ØË޲¯Ÿ¡:ÿx¼Æ}M!i}™¼z$–²É2V«×·fhÇ!âj84ÍÚ: ÒH\w›}{ôtÈœN«EÀúqše f`ÀãRZþê‹]Óùõ·®¿|ÎÙ•} ÔbqßÔEÈS=a§ä ÉfWE×:J>'brøiwËh,@#1uøÜð®E‹RƒnS(Ø<±}Í4 êÔœ?@ºÄX6Ü߆#ûê¯}Ü€ot|èì¸Å%½c9Ð05Âðb_Tšäó¥«s—U€ b’øÇÓžúȸFbêX¹®~e`ëuA-c5§ÑîÅ»H ’©zÞB¼ £álË/VNç÷ãOñëwŸ((VDH ó{’“ùÙ=£ã¶‘lA•el(ùœ ˆk¡.𬩋&¨Ä5(fÓûÂ"‹ÐÌ—RÙzöÓxàò½I€T¹˜â!)È›§a¸ß5kœs6di¼å •èëüñÉä„<Õ3&Mòyë,_±uŽŸ’Ï ‚¸&bÉš@2í¢‘˜:³2áÀ í1iå(eæ­£Ò¼$@ªžŒ±}ÒYÍu•=ûÄòé^æ•àÂÈ–Ðu—iM䄼ßIH,_TvÄ¥©â±êV:ý âÚ\°æ‘XЛÉ:h4¦Î‘Óëbg͙ԯó»ð£­Í$@ˆjçë2Íï]LÚ2Ýë<Ö|KÏqOë(-£‰“+‰gû¢Ž"—ã¸Èá´ˆk›(ùœ ˆiª°ö¡hÈ•+Xi0¦øŒg_Úµ¨5oƦ·Š û ¶o·È<Ç$@ª]HëúÓÎKgx.ëb[~>º—á¾Ñy÷ƒö@’VÓÛ"Äý;cñ|Ÿ<áWË×6em %Ÿ1}¸PÚ†#u6MSi0¦†ƒ-Ý»u±&ì”.Xúµ‘!ªZƒø–Œ†³ÝÝ]F\'+ÿZûÆ“IÕ‘§å ÖÞ!BŽGSÖs‰¬4ojVßFáWA‡¥¨«ýá:•sò¹¦Hs>æýrß+sLjÞmøñ‹sd[ú2˜ÎùÃÆ¤3<2Ú€=[[¸Ôˆ½¦ðö,2…ÓŠz§yZ¢äóŽyþBS»O£@„‘ØŠºµcp¬VU>™*7Ž_hþèØ‘Ó&ƒj¹_Ö.é$@ÌAÀ÷d4œu?×eÔµNº[R7Ý|V€žo‹3Õ=“fs\}[~1#8³{ÛP”º¥_¿7ºÞ‚Œ › %¿åc$@ˆª…sþ]Ò9Oìò›sqálQ×Û\Þá_ÐK+j‚Ÿ$”dQ—bŸpy¬|éš Ã#bÆð¤²®¦p¼†FbjX¹®þeï¶%Í„áÀŒ­Ác¯, BT+aJgµŒ½ðÓF^òßÚo¿ô†«)BK Øq²·V[WÞÜœµXUJ>'bF ÆS¾úxÊC#1ÅqÓÒοêß:ßt† ÆÀôûd Å"b"8ç_ÇDa'©`'.E2n5n;ï>3jó¥e^OƒÑ¤ýâH\Žn¾Œ’Ï ‚(õ‘q¿?™uÒHLëRu÷¼Þb>޲~˜Q­œB<#Õ…‚=÷ä#/™VúƒwŸL+6i»aw½\+„³ uM¶‚ Jà‚µŒFC®LÎN£15>:vxÎÒT¯ù^Žq¬Å#Ýóe™G fÛÔ„ø?2ÚÍömï‚®š=Þoå¾ÝþÁEEÕå[G¯¿1’ÅÞ6´gh÷ ¢´§`mÑZGA³Ð`LUög¯,öðœ¹z«Æ`4¡X$@ÌÇ~ÆØ^é¬NÄlû F_ö„§=ùï·œáŒI•°ÿÜpM<³É`k0`å·® Rò9A%Ç¢ ¥}€z„L•@1ãørÿ«æë¡¡#ˆõ. Dµò”Vo¾k&.ûjpqäÅЊ‹2 å«Ç{¤I>¿çCuÙë2hw’!¢ôX5ÝÒ18V˨GÈ”X•¸Øtר‰zÓƱOnï$BTŸ€Öõgœ‘Ín6ÐÓ‰ãfÄq~¢qmÿk5sdÇñtÎrº/ì—ÁVEaøä]õYÆ€¥þ,æ{r´QrœÙ‚½e$ ‘˜0ºo^s!n®%Á´âýx`»©CóH€˜àÛ2ζüråL]û»:ÖÕdúŽó¯¿Òt.Å›¸›®÷å[›ì¿Îñ™çÍa‰Ša1E X­Idݵñ$•çv^´üeïæE¡™ë™%”:ÌÑ7!ªOpþcÃÒ ³ÇctÈ93c |­ó#g줙Çpïy’ÏïÛØø;j£ÓÇõ4mŽALžÅ 0 |ãXÂïMe4 “§#­ùƒáý­&4í<º¥Þ¬óFÏXó’ð/ÒY­köÜ“KgêòYÅÊlÿÈÉ„ÅaÊXÓ½a÷`4é’a©Ô×Úô[o¨yÏÄG7†’°)AWÅ àŽé»TBÖ> 9r+ êä¹+zlÖ²tÏTF X *÷B˜37ˆˆ‰áœ€tÍôØ¡Ý+QÈÏØÚ±{ u|øDŽY5³Ýöòt>¿÷îú¬¢\y_ØtÜLÁ¡’!bÌfÀõøŠ\(ÑZ+UÆš4ªìË}¯.t›­4¯Îæà±m«H€ÕFÀ¥³:“ò°-¿œ7“qÎÙù—Ö §tfžWä¹¢¦¾0”a‰X,LÜ»±îªÉ^+ÇM¡$<êQHÄ$X­ìQEÝÒ68V«Pe¬IÐÒÎ/÷o7_i^†aÓvÓå‘19œó¯Î{b»¶vÍôg쯙¼ñ¦7Ìñ|Ø}ª/+hª ëcý|mÐ6)ñèTÖÕ&Qg+Ò†BÄ՜ʼnP¬šé_Š*cMë—š>=Qg*£„âBQÿ ¢Ú¸$„xZ:«G›±GÓLÌæÐ²Ñîàâ3 ÙΓò„_Ý·±~J¥®T¬¦©WAWÇÁ€»C’Ò}‰¬».šðÒ Nž?~m~c~Ü\t¹X…Ÿ¾lªÓ  „øšŒv³­O¯,Åçü¸yýåC¾ÙCÕT;íÊMÎ"Ö©L/AWa­´à¬q¡´ FjUªŒ5iî šÛœ3Y—twáñ}æ0…]ןpZ6»ÙÑ}Ë‘N”¤žzVþ?;?~rØæKUÓí?7TOçl2¬‡;næü5VCTCÀ®ã¦Ú\*UÈ"â}¼¬(€5ŒlEÝÚ:LIé“ÅÁ‹ê_ôw/0•£Ë™°ÝM„¨&cì›ÒY]È;Ø ›–êãR§öO³î9³¸«&QàÕã=ò$Ÿ¤ÞÐyq«7Õ¦à7_K‚ ŒÂÅ€)€ÑûžtÎU§¤ôIbÊP,!®Ç£;f‘!ª]×0$›ÝlïË%mâ¶zŠv~øDZ©üº­ãéœåtߘ_†u0§Ó©u]ç5|NìŠÀÚP ­Î‚ Þ“ÜlLæX},UãMe4¨“ãžð¡¹f ÅŒ…{ðÀUíÓ‘‹<€ïJgu<Rvmn/åG^rÔf¿Þ¹ñX^©ìWã/»Òt.E>õ}23¶‘2`¹?ƒ…Þ‚ ޓ딉ÄôéúŸ\°–ÑhÈ^Ð()}8xQýó—ç›Ë*Ö‚·®&BT œó’Ín±í™®RæWsú_[*»[úkgåH>w:ñÑ;kg\Ìñä°ÊŸ†B%²‚x/nUΜU](íÑ©Súd˜— Þi¶…š~7~¸jOÂH€ÈG À¿Ëf4ë½0gŽKý¹ûjæÆn¾å g¬âjÜžî »£I— óÿ¡õ¡œÇ­–dE¬ %àT©BAïÂàƒ `ÀÙ…-_´µR§ôIsÿèþ¹~=mžS#Á<°´ÝF„¨8çß ]Ö,{é+Êñ¹/–Œ=UwýùJí'.SçóÁ_>– IDATÂk帹6 ¿•*dñ.jp£1ÞTÖŠ'=4¨WÇ£åm_Ø9ÛdÝmxbWUŠP rrÀ/¤ gŽ,A|¬,‰h¿¬_=ØXÔS)c‘+jÊá #Aæ}É|wñº…ž’˜HNO¢ÅAÉéA¼‹e 0ÛÒ86î÷dsvÔ«³*q¹iíøyó^Š<³‘Q=š™sù‹6öÌוëãÔ²áò¾š9ƒ•0»NõrM•aÚ?¹¬®l%‘¬d0ßCÉéA¼‹ 0¢¥œkŽ…¬š{út`øÌÈÞyvMäÿ*+ðäöN DµpH±]ºÍçÀ®.èZÙ’öþ¹íCçzf—½òΓ½R„_y¬ªØ˜ ä°‡eÌ™çÍau •:§ñ6v6‘b€lP5]mŽh¹¡BÊõ©á×[Mc` šöˆê*H@DbEyP:£S ÛúÔÜr}<ðíŽ;Ïô´Ž–ë£Iû¥á¸¬6¶„²NU8&€8PÆh¨zG·SðPçt‚ ~½10àzcüFW¶à¨¤|4¨WçöÈÉŽ¶|Ä<½TëÄÝ×UÓ-“‘]×_pL6»ÙÎÍ]åü|YÄ×:>röMWC¤Ÿ¿íÈÅZ!É\ß7«ö7áW½xZRå³Þm帹6…{A€.è0æRuñ¤Ï›¡|«a\ù“sLe”!DÕøõ$@$‡1ö-éŒêoÅ¡Ý å¼…"SÄÿœõ‰Sœ ÑÒîO¯¿9(EïAwa¾ÏõÎjo¿â@¬|"Ä¢\LS^Ao=ˆÜ®EŸhR ªœ“wf†jן1O1¡Ô቗W‘!ª]ןÐ'Ý~¿íW+Ë}E¦ˆÿ5ëc§zÁñR}æëç†jÆÓy› s|_çJï&<žòžÍóæÐEM ‚'n3Æ%S‹º¥}(¤A½:÷¿n®„t»ðýƒV D5Pð¥ ožZˆ¡w¹ï#«Xù?u~üä°­¦$Ýé_=Þ#EòyÍÂ?ØÌ_ñòžçÀáò&l6ýºi!å…„ôt2`‘Aù ™œ³.šðÒ ¾?bÆñéÁ½m& 5ðÅo$BTœó0.—Ѻʞûé²J¸•„Å©ýã¬OÛ<é™üœñtÎr¦oÌ/Ãô~¬=”µ)Wé>/ìÀ«å,ãµr¬«M¡ÖFy!!=ëÀ ]º>š¨qçr6Ô÷gCìL{s!nž¼™"¿?\ñ ö$@`"(åG²Íï]LÚR ÷µºŠÿ8ë“Ç"6Of¦>ãåc—CšÎMðÃÜÛ1…Þ§ð¬”1%次¼‚ ¬6(Ùt`­C±åƒ\eÈWþpp÷,óX¤¸am¿•Qpp,yÈe]lóÏçWÊí„­žâ?µìø¸Å5#^èÞ³R„_­®ó:=Ž©Å4 xJbeNì7ýB¬Ô/„ 䥉+ŒyWdÑtKëp4@ƒúþ,Oõ5,K÷˜¨„1¿Ï=çªä;$B¼M?€M²ÍötWTňA‡?ÿÕÎOYì†v¬8ÕvE“Næô÷:ë¯í)þ–é/¯ó_ï(âæÚ¼Ê !i¹A ªWèIç\µñ”›õ}|üáàž9¦qŠ9s ḅQë•󯡬ý¢Ë@d´{¶VTGÔKŽÚìÿîüøÑ”bœÙ~â²§!»•ohòç¯ùyÏ àDyEˆ[åXW›D›³‚ $Dp‡1]Ò !2pä VØ+Óšû>9QoƒnÁ¦½ÎJ^âñ6Ç…ݲͺŸëª´{ºè¨Í~mÖGŽe 8 É5åÈ…)J2~¼#”µ°i†.»ÊŸœ®0`™?ƒ¥5YP¥^‚Z㺤ƒ Ö6 2ÚNÞ‡O„Ì2MY^ÎȤ*ö„ñTU}H:rù͹¸p¶¦Òî뜳!ó¿ 8 Ùu²7+hªÙçQaLܾ|Öˆa<-€u _ÞCÁvW7“°«”BÒÑ¥µÍµå‹¶†ð¸õÊø´œãÞáƒÍ&z0ÞŠoTæ) âhš¶À©Œ‚±~º¢oí¢£6ûÕŽ›ŽÙyªOŠð«Åm¡qÖÑhF„bP(a/€_r Z^´ë¸¥6I¥z B6€ÛUÃB±ji¯'“³ÓÀ^™;¢§Ú}ZÖb’ä@]f ¢:–+cßÎæ—aϸšÓun<–~²íØå¦›¿ÊI½ß•[1»19ÙŸO9ùË-õ£»jœ§~@ÛøDæT9ý’·B²ys´±„™™£³ŒÛÞÇÆÖ¢ù{E] ªìþ‚(ü DUÀ9e¿[bröÄbŒ9«á^ϸšÓÿ§ã£GSªãiÑûÎH~uë’ŽðTÃ9›E»ØÚ0’ô8KN?/€§u Qþ1™íÉá–Ú¼A˜”[`P†ŸªëJëh<@ƒúÞ¬HšèhÇÃÛ;I€ÕÀI!Ä©,Ö5 {î‰eÕr»çÝ ™šõ±c ‹#§zÂî¡hÒiöi²ª ¿cYG䚦XQDoSh,òƙŒØc6éÀÅòçbx¬ëj“ètçA„ ñ0`•q§ ®LÎY“¤P¬÷hf;±êëI€ÕñåSÕe³™Ú³…|Õ|?.9j³ÿ0û“GcwvûñËRœ~¬œÓõ¹Út®1¬Iö4"\aƨ†€Í¢"ú…( XâËâú@6…Ô Ât,W3ýšÆÆý*§ªXïÅŠä¥ÆæBÜä–à‡›Ë^|€qU4M{…1¶_*£3iÛüóùÕt˃6þ¿ÕàÔ¡‹#RT5¹sŬ°×Iz\ÙKm #«y!‡ð¬d˽Øà(âÖ:JP'Ó¡¸É87NÕtµ9¯¡}±‚Ý3zÀÝÑ8¬7!ª…oÉf0ÛÝÝUm÷}eKgA‚dÂæ 7³¤½.mÔõÞÊ M;m9ÃnrÀ&Œ–_„Ø5Á4zsÔ3„ ÌD'ÚûVûSY·—¾'7$.6ù´´ÅÆpýÆr7&$BL ]×7¸(•Ñ£ƒMØ¿£©ªDÓÁ]×É05ë—v„ _ã…÷´Ö† mZ˜ð4ŽW@漕 î£u‚0ë`Ðk'Îk‹ùzWñ.l\W?>Òlk7úŠe­øI„˜´àŸe3šm}ºzNAÞ8Àp«ÙçÄfQù†¥íÑ™¸öÛM ‚aTrº`·^æ@±ü§!^+Ç͵IÌvçé4„ Ì€ŸKû6ÛŠºµ>œðÑÀþ.·Äζ:yÑ$¾³¸…QpÎÿ @D&›Ù…³óÑ{Á[÷Úý´§«ç7GÜÛŒ¾Âû\™Ë-u#šUÕ »èø¯ˆR½ ù²XJÂ¥ÒiAT=×+€5¬BãI¯+W°ÒÀ¾7/Xïk0‡5j ÝV¶ê^$@ˆ©ð©,\aÏ?¹¼âïSË+ìÔ‘Å2LÉd;ŸO—ŒÃV<ßÞ0’q˜ðs詌ªT›ŽõuT®— ª€ <Ó`Í£±UÐ{7â§[Íã<ó›H€Õ±T9ÿ6€œL6³£¯/G:QÑo‚Ø+/v"“ö˜}.:ëý©-¡L©>OW~¹¥n,îs§ »hÀ o•ê­€gûÛåz×Òp¨älÄ5:ìåg¡Ôw9{¾hÆÍÿ\™*µÅ”ë†ñó~sX£,Ç7ö–¥o bªŒxB*‹ y{þ§‹*ú÷¾,EøÕ­KÛÃ¥þL&±†€qy!ÀD©Þ§t Yc[ç(b}m-Žír1ERz¸S Àc3»ê#‰‹¦«4Ãï䃑-æ°„YQŸ.K” bÊpÎä:—e¯m¯Üdôá~ë=?Çìsà°YôõKÚbåúü¸Ï¹ØV7\°Ø/d’eQV2XHÃAÍ bÒDrâ£w(€®±Â…Ò¡Þ ïf~v8Ô–˜¥\ñj Dµð†âE©,ŽGBìÕ—:*RmþÅbHнöÆ-c›µ¬^qÎfÓ.¶6Œ¤ÜãÂÀr˜ÉÚÃ+FÖ×;ŠX_—D›“rCb2Œç¦ñ ©+gð)ˆ?•s¹r9Íòo9ÏB°3GI^Î:ðØÎ’· B\Bˆ‡¤3ú•g+ò„}]Šð«;WtŽUÂ}èŠ"zšk#á7Θ!YÇ*+$Ë¢,óg±6H•²âªû‚&0š¯TÁ&0°¶‘à‚5ûi–ߥóÆÏ7Ùa’’¼¢x= ¢ZØÁÛ'“Á¬÷Âlœ9¬¨›Ú·½ñHÈìc?§1œÕÈVÒ=k’=Í¡°n1Ð;¯°,ÚuÜZ—ÄuQ'ˆ+Á¹ÀP®‚šdß`¬{gÏíDÚõÿ³÷ÞaR\w¾÷÷œª®î®ŽÓ“g``Èå,@€dke¯-Ë–¼öúÚ{½ÞëÝv×»Þ×zîóz_ï^‡u¶×AW¶e…‚•da Iˆ ‘AÀ0À “»§sªªsÞ?zP„îžîªßçyô¿–9é/€Ù¶çm~Åê%3êf}Ѿlogóˆ¡*fùN6žÀPCç}Ÿ"pqc‹BYhT²—p8Å|I€ŒUÈZz]r>+«¬u4N éoâ’DO›*Íúw K®BíšO„¨²~⨧“A¶áY“ú^ÛÝ€¡v7õœÎH¢#¨«Z°9V<<­u8é÷”·cû üΆkË4U/bYs ]T²—p0ùœÉÀ)³†ŽVat—÷lìÏæ½tÎC³^"`åÝ¥zí!ʘ¨Zž+ â¬Bü@ÎIcfÏ=1©%yÙÓë‘|¾bq÷h]î ÎE_{St¸9CùCÀØ*€Ò!*—X8Q²×¯R’ºÓQU&6æ\Æxý”+ºjëË--ÿËù¶X’š¾‰ËǵØc$r.y¤*ÕÎH€å`À½Žñ`ÿlß49qŸf³½¯Î·»‰Cº»xÉÜŽD=a,ìÏ™ÚRÞ, `«¶€tm7â¶peS syz¸8¦”7ù¹È$‹¯/ùh±ÆR$ZÐ^Þ Ñ †Nf©,ï 2'šlÑDpI½*/8éA”gÍ ñmÔLÑÐ*=d7¬_2)ŸûÇG»‘ÍøínßËLS8¯û7©9w…B²N ’ÕS[ÛŽ3`–?e- tzŠtst(œ;KdÒoñ¢R{_°O«æX"HÕðJ¸…¡^;Ô`¬J ¢\”R>ì(rhß< óUýƒ_|ÚöáWœ1¹rq·mŠT,$«àIYs êàQ$ÎkÈâ Ër$ŠÂ†•Ï™Ü4K‰èÁ‘±jìx5en£«–+’Lûhµ—¸4yØaXLÌÄÚ+^Z˜Q6¤”ßrÔ€…¥°‡~³¸ªŸ9Ô¯³ãGfØÝ´ ºšã-aŸí^ŸW$$ (%¨ßo£µwækt[¸ª)…yz]êœæÑüëgªD­yA€Å埓¦h*È!ig˜›hòZµèþ:ݳ WËT¼)! ¢œ¼À{ÑIf;6/F6Sµ€_öøºp@'ÚçMµëØ*’5à~l5 É0Ó_ °,' Åy‰èñ±üë‡ÏŒYƒçй(sð®jZjSÉç§Âɬ”ß›)ë…M›%ð $kó,Øî5pus óy(ÔMÝVx¼ªã&tl8÷ºêH[5*@üº*ðR!ž *¦pü™RE×¢tÀ£©l ¢ü Dˆÿ `ØIcfÏ<\Y/H*éb‡öε»w·Œ7ø½Ž‹Íœ‹ãí±ÁÖp œ•÷à6`­öÖæyPaÀLËšS˜ê¥ü» 9Ð2:~=PH oÕè‹ð „aqËâÍã©­|àÂÔ{D*X$@ˆú#à'Ž GÍBϾŠu†eO®› ÃÐìnÇ‹í›|~*Ä‚þÌÁ©­Ãy¯«PÖ üI@¦6Çîæ‹ÂY\Ù”D£f€¨oÜ^Õq$:’UO–â£VÓ"¦q iãáTÖO¹ À‚t¿]B¥ñ›GšH€u…â¨Ù£N’±GW±æ=lÛ&Û‡_µ„õüâm)§ïCSÍ#SZGÆ"ce®$t\ÖdóÂ7t \Ò˜ÁÅ‘‚.Ê!RGÏ=Kbt ózì•Y«Õi9€¹åÿnŠeñH<íø\æbÚ×QŒ»í±¨=³H€õF ÀÝN0Û½}Ñò{)^ÛÝ€¡v·ßÕ ¦P• = `¸1”ìÒXþõ³ÀÙ¾WçÛÝnKf¶Å‚^^s¿ çâX{ct  ê”rCµ€tíž–ºô–µ$1—*fÕz@sä$õI¾.@ YãG¬Ù•¹ls<íødô™Ù¡{Œ„û°æù6 DÝ!¥ü7G 8lä¯+Û›öÇ?t#gÿ.³+Ïë¥ÝòþŒý™ž©­Ãy·«ü¥¢Ž¸O»j³oÈIÌòç±¼9…þ<ŠÙ«Ya·#ÈÈ@Z-äJ/¡j¾|we°´‚¡Ò9“×ÈÊ»§eÇìá âFE¤* ¢ÒleŒ=ï(ÑõÀÝÓ!Å@Y.öâ3¶O>ó»š3´U>˜¼¦šGºZFb $ãe>Ýl’ÀC¯íƒ“Æ%Î ä±¼%9~òˆÔ¤ 9Ó"„Dï˜.VãN]*†M *É»$sÔaXRT$„Q ¾å¤Á2!–ã¿ÿåó`òèY]h¨_gÇ̰»½®Y8¼§ó,“ƒM¡Ä‘öÆQÓ¥”¿^íJ}C^©moÈI!2;Pòˆ©1ÒàqìdÚ3¦€Z'¬îÊ\VÏ=¾|^sò˜™ Úd(Ý•È!BT˲°ÏIcæã'>lÃÏÀpðŒ…Ìãk@Ø»³¬Û¥X×,œ£]rúduOápWëp2¨§Ë~ñ“]Ô°€ñÚÏ%~³™éσShÖ¤ »›ÓÕ³oÂR½gT& "ãG{Aº 1{Œ_0/Ö=_ö~ $@ˆj cÿá°1ߎ/^‚;öK0¹ëŒÈŽ-¶¿º`vGÔç¡fg,î9—}­‘ñ¾ÎÆQË¥”¿ƒü0JÞP½5.1o"4k†¯@9"“ˆÇç’.Mqd%¬Ñ¡ŒÉ*.VÃ÷h©Ì¥ƒ™œW5-Å©{ ¹òùEÞãÏç§‘!êó dY÷tнœóÿ‚›o¶ÐóÂpl=­ß~ñ™N$b»iÅâéc´;Ξ¤îÍìjNù½åÏ¥±ì”ÀZ«ÔȰps‰s‚9\Û’À‚ •ï´³mЙy ÀÞ-Cn7¯ý5ƒUÊ,’ÌèN]ÿŒIÌOõÛà ¢°. D½Rð#‡ùË\¸óN[W¬…"_<å×sOÚÞûÁ[:]3Ï9A[£<ÎÅñöÆXk$j*¬ü¿€?àidëã`¥r‰é¾RùÞùÁ< ‘jjpnÖþW†ë§ Twå\…¡dÖçä=0'?bb< D„ø1€´ƒ†Ü¡(ÊÇKŠ‚Iܺò(xô+w±ž½sìnë’kv|}æGw?šÓG»£Œ:!¨g{ºÚ†2º'W‘8(ßÖ~ÉÞ7£0 ÛWÀòæ$†rÔY½J4¶èŽ5ôñÞ¤«°Pá7aV ŪšaºüÙ¼Û©ë`Z~Ìo‹0ÖŽï?VÖy$BT“qw9lÌÿã-ºõÚgÁåý`xÏW±ìñûçÁ0ì]=DÓ rõÇ™L•?žzí‘»Û®Üop:– SU¬£Mci^”ü™›$p¿ŒÖO˜ÿë ›S¸ !ƒ&Í ÅRA" R>1R?^ŽÊ]º!‘q¬¤¥´ÇØ%8µN DÝ"„øJ5vò’¸ä-?¼måfXæÝ€|×ÓÛ¶ÉöáWrÁù{à ¾>þ'šŽ|sÚ‡v$T=O»¤ŒŠ?èÏžÚ:œ÷º ù€Q÷×O’ú› hõ¸¸1ƒË“h÷Aùêå§©Íg:yü=5æ¢Nz{åv@0›×›Wt|/´W•·K–U¦’!ªÍQ¿wÒ€9ç_yÇÿ⺽~ˆ·& ¿¶»#v·‰¼ö#»ßþ³}¾©é¿ŸõÉíû|Ô¤Œ4Õì™Ò:2Ü\!oˆD)Iýwp´þr,4¥ Y¬h-55tQÂzÙˆ´zíÕ•ç^NÔGøQGÏÈB²°C“Ñ)xW!f®ð\´‘!ê!Ä·6ä›L}ÇOïXy*ÿ1 ã'Ä6<°Ðö⣽ë8,¾Ûß%U¯ùîì{¨eIؤÝR>ÆÂ%oHF×*ãeJxL‹ºèòvÜoê%2?Hy"å ©Õg9ݵtïïêãàÝÀJÑ+%FœŒÞ¶I'BÔ=Û¤”4^•sþ_ßõo>}í0¸ñ}0 À,p¶oÇ|Û[ã²åïÛE¸¯åÒþïM]µ3ÍÝEÚ.壠©æÑΖÑÁæÐ¸¨„7úåO¶»² IDAT½CŠõ¸Y%º},kIáâHŠÂ³ÎFÔyUºíRÚ¾;¥íÞŸvÕüeÚ*·Òµ‚¡éù¢#;£OÉÅì!@ÚÊÙ1)pÎæù"Þ«ÎÈmJ­ÿ„?ü[r6OÖóx³rÕÇŸÊ?Ýœ™øÇÙŸÜvÄÓ2N;¦¼ÄÂôái­Ci¿·2•²Nö¹Ï×ïù³ÉmaiC×´$1ÓŸ‡FáY§Ms‡n:Ý¿Z['^ÎÊ^>”vfV‹‘²G–`üß! D]cYÖcö:hÈEQn}Ï¿½ù²œ|ä·öÏýXtán¸=§Ûuù¯Ïüèî§" Ž•ñÅ ÀPUëX{ãXk$*^™x£4€ xÈÆê7¢NWæòXÑ’ÄâP .“Ð)ÒÞt|©±/{ê ÒXi’÷:qþÃFÖ>eˆ³Q÷gQÆØ·5`)¿¼g4G+“rµ­ À˜”«oÚ}º¿f2Uþªãê£?èZ¹3«RHV¹Iõì¡®Öá¤ß“­Ø‡œ°NPÇuÎ8¦èE\Ö”ÆÕMItû ÔeýƒˆãÕš”À·~¬öûA4UöH¨–ª;°'HØÌx쳘YÙò@H€“†eYkô;hÈóUU½ö]7"çÀeçÁË)ݽ˜9/q¦¿ÿRhVü_f|ü•~w8I»§¼˜ªbõµ7EO´EÆ,µBÙ×À> üÖö×OÃ÷Âï˜ÌaEK4d(Wä=è˜FvìMkϾ¯í÷@…ƒ¤Â™œã¼ +g#ˆU6? b21üÈa¢ë+ïñWaûÁ_¹j×Ù^b@ þiÖ§v¼žÝ/é¸Wvâ=whjëPÒïÍTìCrž•ÀZ è¯ï›è)²´!‹e-¥R¾TAë ZÚuÓ¥)TÑÀ7ÜÈdk¼'D¸Â‡q†a¹¤àa3mŒR‰!lâÇN/cìCæ½íÇW¼ËÏì…/’+nò2þpoál¯D¢è°ÎAãÕ9çŸEQ>…ª¼sšÄîôÙ=˜2#]©ë ´^0øî_Ñ‚ÚNå'?ÑÀp 9«XÉ^(xIk,àµúOT;*—˜¢qac+[XÊ¢E3lÿ î>§B%ßÄX¬Èÿ雇ƒ¢×·^yåï1LMÂQçOŸYPm3¯(Ky@ˆÚ8¤Jy˜1öW€c2‹çJ)È9ÿ>€©¶éGo{ÓçT<Ï'ê Ï6ž3ÔZH¨S ãAÚU"Íù2.KpÝ0]²R•Š(½…=!0ö»-p]:uݾ’g„A"+ìçq{U¹ã…Ao.kÐKÏ ú‡ j"aò+. ×–8+¢T­®¢|°‚ÛUÌ»ÓP爯9±Ç7Õ…ìÁƒkI€vaÀŒ±o˜1–ð[‹®@(.¿øÁ«sî° È—C³bq—ž9's"â’‚<å>;0&“~o>ëuô\AS„¬Üs$ à€F$ÐȪòvv2ÅH‡×ÀL_ . *“È Ë&Q…G“êP_ÊE;è ö̸t¯*Ï÷×NŽŒ°«òX(\¤|Þ¼SæºÏݘz50=n‹Á¸p¿¿÷ Â6¨ª:"¥¼ÍAC^›‡AÊK—oÁÒËOTûs{½-Ù­¡™#³3þ3ã¥ÝU~Š.Õû3`z¡èf•¬‹œ@é­ì8€fnûjvÆŸ*Ðê11ÃW@³Û€¦HX‚¡PÇz:›*ò;FÝ´sÞÊæWîPÀ%Îó׆7@2àÕÊ .$‹…ýi§Ìó€ÖÚš1n‹Áyëï=D„° BˆÆØG´9dÈö7ãÜ’õÕ'àŸœ 8)ÅcmŒÌ’çä‡ÂŠ”ä ©À~=IÝ2U­hVö w À^YòŒ´p1Ûß!¼ŠD“ÛÄ4_Ó|„]\L¢ 9Ì:òŽxt—|é©ã:í™wòⶸ»1¬‰sj \±ðJåˆ"„éÁ¹#j1÷4&m#@ G°þÞ}$@›YyÀŸ“%lp‹š5ÿ5|èæ}“úì÷w¤·»Gæä†ýa3K•x*€©pèÙ¼[+z‚¦X Ë’(UÌÚ+K­L› :ÃÎ*®7¼#­î"tU‚A¢Pã¹#^ŸKîÙ2ìɤŠô"à]Ø´5îf`¸`Ñ$—,f¶Ug%å½Z!¯9#¤ßIm 䋉cý½;H€ö:´J¹1ö!²FÏåÇ?óGtͬ‰¤»¤ª›ÏF7¤Â5Õù3Bá–?_t£’aYÀ J9" €&渺ŽE"¢™èÔK¹#Ín¾$ã£Yåøá„F;åÝÙ¶+©õ ”«.n(*Ê$y·dõˆ©*fÚç)8anûµ†Ô–Ь˜-ÃYܳ•a7cL°ŠLQÇ„"1ù…x®¦JÞW|ÓGçäý!3GÞŠÀõ¸‹ñ€žu™wÍÊ8MÇQÊ‘Z˜# ̳‰p­7 ’VOE@ã¦ÌIÖݪÊå+›('ë}8Ô›u=¿%®¿0Pl¹ª¯!‹¨JÈÄ­BŽ}Y'Ìk¿'’zÙ.²ˆõ÷¾H„°RÊÝŒ±ÿ €ˆõ:‡W®z ‹.ªÅï–p鿯†ùÃ>Yݹ‘0wNéçê¾IP¸Lô\Þ­½… WË:)DúIˆ¼Yx‰°f•š NäD4 U@„)YU«l…Übóût£(hϽc1Cyäé1ocØeÍ›Uå¼C;ª#@\BðÑH0UÏse Á™‚êÕTñ~ÿî„'’¶aXïŸH€v¤Èkp9™¢QTS~韞€î¯ÙØ^Ávø»ûýcs3A¿U °JmfM5ÇôP˜Ðó®–õf!r@–rCšIÌ TøUf·‰)º™þ¦ê4jüª€Ê$*ç)aœaàXÒ5r"­Òl|€0%Û¸yÜs '«^²4dxÜUJÖ᪔áH°x@Ï …‹z›ŸLÞPÝz¸åGnŸáÑTsNgä}=9½žæ¸m’ЙÖßûG „-™Èù2­Ñ:œ»síÅÊ?­¾ë˜+`<9wHa–937¢ÜÊ‘õ¸‹ñ /ë’y Ó…J À1ûeÉÒälÈ{áâ%QÒè6Ñ1á)™éÏ£ÝSœ&´‰ó¡)ùÙç•H`ÏÖaònŸ"Gûó꓊yº»¼fW‡ÇªøÆdI¼W‰œO+ê(}x<£Ý·i_ûÏŸ|uÆ®£#á¼a* §·$>H€ôµ¿˜ž°Çª” ëï}ú¬_ˆÐö&j”îp™¢ÎnMËnÜUOß×d\Þ×riÿ ¡9c_<ñ윙ّšÅ ÙZU¬þÖ†ñXÈ—i‹‡½¹båûBdl’À 8Ÿçp"€Â€ K èzg“î‚`Èš9‹#k)ÈšyÁP°8 §Ð«dÞÒæ¢×ç¹ uE?UG Ê_ÿó†ËÎþ᯻SÓ:+(DrÕM;ñ 5á«ý988ÕÝr¸õÕ#CKœ~Übžk–m$cR20vV‹…Q³!þçüvPEýÐØ<‚ ¯ªÇ¯ÞçnÌmÆM»VÅv7rèåÙº(R׿ ‘õhÅ#SZFB™¬·m$VM«òÏ¢4€?I`›œÇ€ù >mÜ\­Yh€…’›ém/ dI¤ƒ! ÁQ”¼ôÿ°$“^ØXxnã%£Ÿ&/nOºoúâ.í–´e¿x딌O¯@XV•[º Q³»0_4øó{ûžÛÓ×|dxÜV×b.ûÉÖ­ãÎjL$@ˆZf”rcl5™¢NîK]½³Þǰ!²ptKhÆø_žxnÆùÉ£í ’&¶B$|z.ÙíÍ7ÆÓ¾æh2ÄEBà2^ÀV 8—çq*wQFN&¿{”“ûæg”Û?Ô#rf†d÷Ü?è{ô™1ïníLÿùê–¼¦±òݤªœ¥ FÍC÷õ=»ûhÓöÃÑ‚a–% <¯ÚÈC~õlÅ×5ªªƒRJ ê\®¢üÒ¿< ·îo´y®‰—B³£ƒZ89'7ò ƒ^ÖTîÈŠ¬Ç]LýÁ¼CC5¼žJ}DöH  @#gk5èhu‹‡Ÿó¦3…a!¹¼`›¶ÄÝëö&ÓŸ;C7=o¨¾3g‡RÕÜý@´!žl{¦sE婽M¿ØðêôG·îèKù,!Ni}žJȦЬ¡ãÞ¦œm Ïaýš³jšI„¨i„½Œ±´“5j¹`én,»á°ÆÔçiÌÿ1²`0lfùÔB4H%{+¸×9“)Ÿ7Ÿô{rÃR\†YÐ `xBˆdDà¦ù¨è¡“ÑX‘ïØ›¦êsgI¾ Ø«{RÚïÖG¢ŸÑ嵂~õÌ„ˆð¢<Ë÷Ú§}e£‘@j2n­ÑTÖõì®c÷=¿§óÞg÷LÛut8œÊ~èí©'÷iÃ6 /Ç6á÷žUIz«GÔÁÊ}[Jùk²D å7î¶ã¸rÜ%~Ú¹üȆÆEƒŸØ8›’Ô+KAÓŒÞΦ±@:ïi‹%BZÉ#Ry,{%°ÏfXÌK½DˆŠpÓ‡[swß?ä‚BËrŸÊ ¶ö‘aý÷è->´¼9¿âŠHA÷žFžHLUîK.…dªaqÃ¥VEö ÄRîÍN„_ínè‰û¥¬Îú;á ٫㻚=ëÒÉ$@ˆšÇ²¬µœó0¬Q£´užÀ’KFì<Ä#ž¦Ü×fÜ´ëšØ¾ÆO ož²òôž¼‚¤üž|Ú¯åãY_c<R «:{ à€Cè°˜ÓÉ÷Un:ÛÜÖ† ϽEy[³_yóN ˪–ÊÅ`s81ö§[c©`(•ñU¼£ú›)Ø)K ë³°Í$DÎÆ€OÜКý÷Ÿ ’5ªËÈXQy|cÔûøÆ¨t·"§{=欀ǜÔÍÙ!¯97è5Ý.QéïÂߣԹ0–ȸci÷h"ëIfµh"« '2žÁXZÏͺ(¤4ì æìµq¥ÀwRá„ÿÁ9ÿ[PÕþÚ «oÚåtlŒÌn ψß1ðüôˇ;),«².Õêomk¤š£ñP8[ðŠjzD,dé¿fó0S]É3࣫›ó?[ÓïO¤L*É;‰d ÛWȸöÅ3. úúϽ —-^Í kªhö¸D“Û%Z¼šÕèVE£Gœ¥”èÐTÁø\ŠTXi;B g YËb†UÚ§iK²TÑ` ÃâGûcMæé|QMçM%W0ÔD. nÓu/íwG26 eyáH„¨'†üÀçÈ5">Ú»ŽcîÂq²æëÇSVö<Þ´dàÓC/tŸ›>ÑLV©,yM5ûÚ›¢Ñ|Qk&Bz¶Pý—£(uXß<žµˆš›SE÷rù©[³?ûõ ?Y£öÈY‚KçÕc•ý[{ÀŽ{šì%@$Ê"@èQW!þJ•û‰ZàŠkw’ÞJ¯§)÷éÙ÷ý©«vŽhÁ Y¤òd=Z±·³yôxgãhÁíšœr—'óÖXÀ#V)¡—*Ìž·¼=ð)t_'lÉ!½9k³!•å¹F„¨7H)'3Ô_F®üóÃdˆwç¥Ð¬øßÍ»mÛoÚ.=UÝE²HåIéÞüá®Ö‘ãíM“'D$€> à·°K”:­ï‰ß§È›nhÍ‘%»‘æîb\õ¶ãeé\O„¨;¤”ß"+ÔÀ<,º`747½µ|„iZ2üßæÜºå©È‚cãd¯j¿çu!’wk“'þâ(UϺÛ6 ¼"ïÅg?Ñ‘ñé Y‡°'<‘”íeʲxtH€õÈFÆØËd†I„1)¯¿i7âÔÈpõ«Ž«þãìOmÙéï&‹TOˆôtµ ŸhŒÜ®É"€ÃxD÷XÀf¤h~ÞL0 È›ohÉ’%;qØÛš°Ý 8y@gCM 'Ù1õºç&ɧǀ.|sú ¾;íºÃZ0M©q¿ž;ÜÕ:<š5¹áp¯LäŠ<*€#’²Ú&øü-S2 .²aöù;ì'@`Qá\,Ëú=€²ÄäÀãñ·6“%ÎŒ-ñÿ>ï¶íwu\¹oÜ¥S†@•˜Íîå'[ˆHÇ$ð„(…h½ €QgG ùt.ÿË-$Ì {œS{6\Ï*%¡ßÛ¾Gf˜ú¬Tüaܶú~ë·à’Ðg€À†ÈÂѯ̹c˺æ‹f¸fUªC§çzºZ‡O´GÆ&5Gä$9”*h­›H\ß*€¤3ÅÈÍÖ’›1ÕkÒ*%ê~w8U€ =z–R–R DàÄ/Œ‘%ªÎN@à/Voó|L%³œãòÖ ÿçÜ[¶ü©a^%ªW¸_Ïõtµ ›Ò4’ñ{j£ Ó8€­X#€uΫ¢Å9Ãß|®‹¼ DÝsÈÛ·ç&}S§Ê³€ú¶õ}vc,à*2EÕ0…ŸÁ›Sh¸+îÙ†=G% gŒº€Ÿy®‰mÁîñ—‚³†›Ì´ÒVŒû@¶¬E—jÅz6íóä!˜Ç´\5`û,€ãvËRsj Èlÿê°{ªÇÚ¹/åê,P³d¢æY8½%1§3òŽ ÷µ]rdD Ù¯»´Ãú5gí±'Q×!~€RQûŽ”xÇ_0&pÛŠ €õ3@PrúY0ä ¾ÝuýÁ¯Oÿø¶=þÎQ²HõÈy´b_{cìpgópÒïÍ€ÕHÑ\¥*ZOàW𔎈“~H[òµ¯ÌHy=œÊòuIš»‹{ý]ö«uÇew¬.Ky@ˆz'Ûà|2EåQå+Bˆ#ïùÖ¯‰á–Ïm‡eµ¬‰,væÄ4¿ñ|xÞh·y¼«ÕCfÎCV©Òy_UD2 ç’~=«HÉ܆áb²F¼Q@ Àa”òF†&z‹„˜­žèA¿*…Ë—_M¸iEµÌ»y@vºF^ ÍŽÚn°ÃXoY Є¨{„ß²¬=¦iþñÿÕÍËÒ¸}Õ/q˜(ÙÎŽéɯÎúÔŽ_t^½wD fÈ"Õ£ ©fkÃø¡imƒ±°?)¥¶î3&€cž™hvø´ôÀ&»î3ŸhÏ.˜ã£â DݱÝ?-jË)¼ly·ä!ì@ÀRÆØ<2EEùÿ¤”/œò¿^ïnú«FÀ"d¾3Gèõ¶d74/HsO®+õ{…á"ËTÁ¹Lû<…XÈ—ªbj–¡*–¬­ç§5q'ì°Kƒ0è´úL%b ˜7K7Þ0¦ Æ"j”·{@ ®X?rí!“)ö[µRìÃú{‘!ˆ7ègŒ}ŽÌP1 Éç§×©¸” ¾{Ž$Áä,HF÷œ³¹÷Kà°Þšy¢å¼IÅ“ž#!RUû3†¬G3b¡@ÚÐ\†f™ŠjŠÚK”–’(yGvJ Gé‰'~•6hiÔ„Ûv¥4ZD=]úÔÑ?Eαgþž›ñàšÁr\Š*Lvacì%)å¥dŠŠ°À™ÝP“6ãO¼›Ìy¶Ï€R‘gŒ]ÝÙr}tçô#K9"U$ðæâoNϵÆñ”?˜Éé5z´ˆÉRv€.tèd€§öÕÈn›’yyGB{uOšDQó<™7dÛÁiJ¹.Eo# ÛÀ9¸™,Q‰¯øk” ‚ž9Þ“Ã÷lÇÞÞ"€ ´³ŸÆpPoÏlh\4`‚ºòc·´è¾^E U±’=êÉ™p-KY»§z¥îI=vHà¨,Õ–µéa ¸di¨øðScÞBQPij¢¦x³$©zòÿÙµ¢GÚ2dP8üÂ#ظ±,££LØLƒðf“)ÊÊ~!Ä‚‰#Jy¸ëÙ6(Å[¥“Ì[>Ü0ø‡‡w¶^?¾sºß,ÐÛâɸ Á›’Y=Où5ÃrÕÙ¦2 @jëëmøSÔý÷ÿz8L«Œ¨%>}õ¹Ç>|á¬Qx*²àد:®>jË2y·¯üa¹.GoÊ[ÉsƘ àÃdвò¿¥”[ÊzŇîNã–·Bz‹`b&$#oH° ÈýþŽô³‘ùƒniYùq¿‹<"Õ¾ ÉŒG+ÆÂtN× \æ6LõðÂÏB)\ë€=Ø+K—y ¸àܯ7sºnÅ&ß{0CyODÍpÒ"Áðóök&\ºiOÂö`ý=Êv9Z:„ÍðrÎh&S”…¬b €ñŠ}ÂoŸœ Sù$:ÈÜeÞ VQùHt{ûò±}S‚<"“…Ë4•H"ãkHfüŠ)êWê(å´hc@cõ»² !ñW_=аeG’Ö3Qœô€Ô[£_Ÿññ=¶¨eüŸ½~k¹.GoÆ»a2Æ|®!S”…»¥”k+ú ¿_“Ä-7l©›P¥¬²m®È=¾©É Í  ©º Q¿[šT|¤ÊÎeF÷b þ´áR Í´Õõ7J’ãöÉR…­ã(%¸çd)„Ë]Ù÷šŒ1\yQ¸øÔó1O*m‘甘tNz@îé¸òÐ wCÞ¶Uä“X¿&]¶½LK‡°!^™â,NB\`[õäÎÓàø8$æõËK FU³jo¡¨5ÆSþP:ï…ö9H´0  @3š*ºuèhNýÌßîdscˆIåÓWŸ{ìüË—öþݜ۶Ûv YܶâëU-Ë£ghé6$Ç›àB2ÅY±CJùõª~âƒ÷ä°þžíøØ§‡ÁØL€Q˜E9囪fåW®3?î£>"“ƒ©*VÒ¯ç¢aF¨Š©ZBQ-äëPò’ô8(K•¶öK _qE ( ÐpV¯@Ã.1£K7ŸÙóP“Bb2Y8½%±÷¼U{émû ¹‹gî,ç%I€¶DJ¹Ÿ1öeP©×3¿ß0ö/RÊW&åÃׯÆçnÞ‚"÷Bò)4•"y"“{¯bLf=š1òe’OŒ ·aªLÚÈ+R0à0JÚwHàÈDÇöqy”‰ûÔ…I÷TÕÞâ¶6¾4NÞº««¥˜¤ÐÅI†C²@2çmH¥}¼áB:çY­Lüd¥®{ãÏîwþÊš†ôoýìX€V1œ³rùÖ½·þãsö¥õï¸cõ ‚85.äœo!3œ?BüMÍ|›ŸmsÁ—XX+ 8yn+ÈùÉÞÐÑW¦ÎÉ 7’5&wÑT#‰´/˜ÎéªiQ7JÕ¸|¬”å§ðßy¢ÏÏ3C$ž‰ê¿0¸î¦§ÍO}a§}ˆ$n»ö—û²$@[£(Ê&)ååd‰ÓC±Àîšûbw?Ó E| ‚M£Yª,çfNøoól’ IDAT}uÊÂL_KMwövþ\ÞLç¼TÖ§Z’ÂK߯÷öõûï:D"„¨.òº›ž’ŸúÂ.Û±í¸}ÅoË~>£¥CØÎyÀ§È§s¯a›¤”߬É/÷à=)üþž­ØÓ;È锤^9F´`qSxÎØv÷ˆOäÑVLø™LŠ.ÕJù¼ùX(.j.Ca’iõÒä° \Ò,rlKÑ}¨³æÁ¹çÛw€â9¬¿w ÜW%w.ak,Ëz˜s¾À9dSæg5®$€­øÎ‹{МZ ð˨“zå8¦7徯¯>ÜVHÿØÈÖ)§z;4aÐË«IDr&ãA=êYż!õ†ÒŸ^05G勼 _˜Û‘aŒáGûOøi¥ÄYÂ……Pa_%.MÂþû‡sÀŸ‘%N‰¨âóÌšÿ¦OþÒÄú5pÃí{¡È6€5ÐôUŽ´ê±¶†fŒ?ݸà„d0¦äã>šÖ„Éz4c<èÏ$ýž¬P¸P…¥(–tìóýüÆ€áw)róhÊMz‰Šck;ŒO\¿¹"W¦•C87çü(€62Åò-!ÄÿªËo¾æéùòc Ó4VaSÁàÞÙºb|ïÔˆ™ñ’Ej O¾èjÈä¼þLV× –#K,??œÐþqÛ‘pƤf…D_Ø9„ËûqÛÊŠò€NÀbŒy,'S¼ÿ}Tñ9ѺüöÜ3Š[þl3„nAŠé…eUtSA‘ûýé'šÎ;1 …“ VFYNzµ©*"­{ ±P ôyrBå–Ó<#ÓüëŠÖPá…‘„;mXt? *ƒ]= LJ½÷ãá_+ryZ9„CˆpΠ¸à÷RR>%¥\e‹Á¬}®…âŸCbÍlõ˜‘óÞ0º½ó‚do»K :ðÕ z¾è ¦rz —õ:Å3-üï^î ïOS³M¢üÏN»z@8Žà¶k\©Ë“„p 9ÆX€‹Èïq¯áü¤”ûm1˜uwe±þží¸é³#¢ `Ô)¹ Œ«ºùrhVlSüAUšV»‘Ô]¢çL a¨ŠHû&<#=khŠÉ ™fZ lúRRWycWcΔ’툥©BQ^l›b>‡õkŽWêêä!œD7çü ¨úÛ»1$„è`Øndk×*(/SVC©&.)زñ½M«£{¦vÆ©Su £ÁCé¬7Î{ü¹‚ž}F^Ij_{åHh¼`’‡Ž( ¶ô€0)ÀŒÿ·}(Y±{-ÂAÄcçX@¦xß•R>cË‘­['±~Íq|ô¶—Á¤ ŒO½|© ‚1ôx[³õ4ŃVNi2Ò:#û×Þ!Š1™skF" ç¢‘@:ïÖ à\ª–àÜFb¤Ëç¶VuF »c×p¾Hg âì±¥DîÅí×½\QC+‡ppη’ÞzNBÌpÌ£ýùC­p{nê 3 tçǼ7ŽnŸ²4u¼•ú‰ÔîbÑLç=z.ïñç 7lÐkDH`ÝÑïw÷öò– ³qæGu[æ€È_⎕ ɦMG8EQ6J)¯&KLÜf¤|DJy£ãþë ³!ùh§UP}|"¯¬ÝÓrUâ@Gk1IÅ!êÉ|é‚ÛŸË»}¹‚Çk˜®zn~x<¤ÿçkþyCˆÓÁíɉۿü0®XÕoŸ· ln[±¡âgZ=„ÏÜœóÝ \8>~e9Ú ß̈ûjHk9$§"“Ds!¥]ÛÝzYü@gÈÊ»É"ux+šª?›w빂[ÏݪY?‚d8_äßÙÓØp"æ¡êÄû+oWQžwñyË—¶ ÒT°ÏéHJpÏ¿âÖ+ÇI€DPåsRÊ_’%ð5!Ä7È Üýt#¸ü0€…Œî“çÀUcûWÆ÷vvgÇè8hAâÍÝ®:$;bi׿ïî ì‹g¨oñVT—!—\üªí„Çëª@Àí«~Q•¢ÕD8õ¹È9? ÃÁ60…Ó Ðrx¿}r*LåC˜MƘ\fäǼ«¢»Ú/Nô´{„AÞ©:G‚{³y·7WÔ¼…¢¦ ­Kþ )ñûccÞ_ô ç¨Z–ãq¹ŠrÉe¯ÈO~~;[ó¶'3ŠÛ¯;L„ *çü«þÕÁ&X'„¸™VÂûðë ³a±l cr9™´~yò`{G!N=EìrÞ€;oºô|Á­LÍ“ËkS¨²Fr !ÙïzG¼wòEóõqœbÖ rñ%;ä'þr;ZÚs6ߌ}¸ýÚïUïã¹4pÎpäa†s~­išÏÐ2ø¤dXóä"€_É›É “ÏüLŸel_û’Ôñ7yEìw’éÅ‚Ë74½`ºÜCsE“wfÉY»ïȨwMϰ/Z !b{Á„¼ðêWäGnÛƒPCÑϼ·_·“ATçþ]ëÀ¡÷!f\B¤T1KÈU’A&7 ~uì@ãÕñí”+bó³w¾èò‹š§`ªî¢éÒŠ†K³„RMo‰!${´?êþuϰïP2Gâ×ng‚¶ÎÖ«_‘×ßtŠê¤JG6ý[¥Kï’!ˆ7˜2‘ â´dÃÿ)„ø6Mÿð³m.øãWÀ’Ëæ%ƒÔÝù1ïêè®ö S½­ºY žN90B2OÞPµ¢éò†ª —fZªf˜j¥sK^Mj÷Ô7¦Ü’´oÝâÑTkÉìÎÑÞU·?;°ø*gæD2ëܾúÅj¿T GÃ[Ç»ÉACÎ !¦ˆÒìŸ<¢#¦-ã—ŒªåÔª4ÙE‰Þð²ø¾öù™M\ÒsαkÁ´·i(®¢P݆©ºLCÕ Kq–ªZ–‚2­Ù‚òà±1Ïú£ÞÁ,%¬×És3ÛÃÉ+æO^¼`Zô'³þìÕéIgC¦‘mø¾xA„ ª¸8ç{œã 1ß+„¸ƒ¦¾L¬ÝB>± L\É(6¼†˜RˆzVíj»0ÕÛ2©¯ñN¢š×L¡¸„©”þ,Õ°E ®˜W$¸bYüTÄŠÀ汤öÀÑQï¦á„;OM kŽöH wáì¶è5 §ÇZþ¢Á¸øéÔ{^ Îw®UÄ#¸cÕŸª~ø¢åH8ú¤ª× !wÔ­Fˆ+¼@³_f~ñD¶’_DB¤¶àXï ]?к(}¼E½¥&No ÁT!¸j ®Á$c¦ä €*$ãB0&Á¸¬`Zì¥1}cïp`ÛÀ¸/kXtO˜$ZþÜÒ™mãWÌŸ›Þ~½„®Å¸øEûUû6Fæ;7€ËqVÿ w.3I€D57cO0ÆV;hÈû„Ôž„ˆc9™¸~yâ`ë¬ìp„B´ˆJS4-öÊá¡àöžð¾h0–&o\%q©Š˜ÝN.ênMœ?«=Ù ¼£a y>N"îë¶MÊù‹–*á`ærÎ÷;l|Yñ#šú*°ö‰Šl9¤r$HˆÔ m…„vMb_Ë%ñžÖÖbÒO!ªÁñѸgûááÐîc#Á#Cñ€a’wälP8“S›B™Ù ©s§5§NkI»]ê{Vs2?í\±çŰÃÅ“Cèyá;Õ¬|E„ pÎ à‹rFÑ A³_E~þP+<Þå_BB¤vY˜>X6¾¿uqª¯EE**@TÓlÿ¨ï@_ÔðDÌß;’ðç ÔÛæýÐTEtµÓ³Û"ésºšÓó»Ó^ÍuJ‡è"wY?é\¶gshVœAøn»vߤéZÊ„Ciàœ÷ð9é(,„øM= â½9YEëªøk­ó3ýÍ.)h®ˆª!÷:óõŽ$ôþѤ>KéE‡zIÎd[ƒ?×ÙÈNo gçM‰¤gµGr ç§]ø8ÍÝÅïN»n÷>_gšÄzqÛµ“ A„pæÞãü«þÕIcB\`;Íþ$s׳mPÍUB2º×0A3£^?ØxAêHóÌìHD©bÃ;‚8‰%;>šðNx&¼CãÏp"ãKä<–°G¥-ΘŒø=ÅÖ_®½ÁŸïh æ§5³³Úrª¢œu—•QÍŸùæôwhá‚㓜ÿ·®8F„ ª‹‹sÞ`ªcî7Œmµ,ë"šúZ"Æ2€-%!Rû„Í´ëÊø¡È©#ͳ²#Nb„¨a2M»ÆSîÑDNKf´h*§§òîX*§e †Ë¬RÀŒ1øÜ.Ãçu™~·fuÍh x‹Ía½ÐöZÃz±­!PÐT¥"í{½MñoNû³½IÕkÒªñ2îXµnÒ×Má4Eù´”ò×N3cìó–eý’f¿¦…ȪšUtãîkbû›ÏOmé(ŒÈ"D­’ΕX:늧 j2o¨‰LÞ•Í•BÑâyÃTò¦É³S1 “­7 -KrcâÏœ1©¹¸—ËâŒAQ˜ôhJégª*…IÍ¥Š G3C>Ô53äs›aŸ×û=æd67‡füxÊÊÃãÔ«˜ÈBÊë3$@¢úd³”òb 91‘|ž¡Ù¯aÖ>A]°K 9%¡Ö‘¹*¾¿yi¼·yj1$‹Ääc0.~ßrᡇšÏ"k¼ Ëø>{ýÖšÐB4„ø‚sþ¼ÃÆü!Ä£©¯~ùPš~˜¼‚kdúavnX¿rü@ËÒô±æÆbZ'‹DõW}¹t­Ü»_ï —no†£·®ø1« o ÂQ0ÆcŒ]ï¤1 !ØC³_gÜó¤\½B\ 0/¤¾8';à»4~¨iQº¯‰zŒDu8 ·}·kÕkIÕGùo9üH¢üü媚ùJ4+„ƒXÌ9ÕIëž1ö¼eYWÑÔ×1wÝåÒu˜¸ ’ÑA¶™Rˆz®HlZ˜îLφ©û:A”ƒqñXóâÞûZ.í'k¼«y·¯|´¦¾Í á8çk|ÂQ÷Ænµ,ë74û6àÎgUÌ0ÎäJ@i$ƒÔ'3ëº<þZ¤TÚw´A¡>#qVœpG’?šzí^OSެñ®âcSÔïbÙ²šò ‘!œÂ\Îù>ÀQ àÆ„SPÝs;±v­‚|x À—h!ƒÔ/A3§^–89?ÕÛ47;q K!«Ä©a1.6DýMÛEý&S©ÊÕ»ŸòMÅ÷k)ôŠá(8çwø ‡ û›Bˆ¯ÒìÛ)~ûÌBy5›F©oÜ0ø¥±C ç§Ž6ÍË Füf Ä{Ы7ÿ¢cÙ¡#äõø€çÅg®}¶6µAØŸ©œóÜô@·„³¥éwk›©^I½DìÃÜì€ïâÄÑÈÜ܉†i¹±0ua' ­º‹7-=òhÓ’aAæx8zqxÓOpç5i*º¡ö߃œÿÀ—4f)åz)åÇhöÆÝO7‚[W‚±‹¨„¯}ˆY×%‰C‘%éc‘™Ù¡ˆW˜Ô'†p‚1¹Å?cð®Î+z©ÂÕ©|dyë;øüu±ZýŠ$@»ÓÂ9? ÀQeL9ç+LÓü#M¿Cyd›ŽXü2py9$£NݶÚÛÀ9ÉþÀù©ãásSG›¦A 'ì˽y|MÛå=Ô×ã´$Û}¸cÕ¶Zþ†$@»Ä¿ à6ì}Bˆs:•8ž“ ëŒ]ÉÚÈ ö£­Ð.MŽœ›êoœ•iЄA‰ì„-èw‡“÷µ^zd{°;AÖ8-ññ2îXµ®Ö¿% ÂÎ4sÎ{8í ð—„?¡é'ÞÂ=Ovƒóe8’ѽ߆¨R°sÓÇ‹S} ssƒá)ùXÐEe~‰:cX ¦i^r왆cdÓDŠä#?À/0H€Ä$Á9ÿ>€¿qذS¥w“´ˆwå—: áj0œÁém¹ñ ƒÏO÷dƒ”ÌNÔ:CZ0ýxÓâãOGŽR‚ùè³p›ÿ›k7ïƒá¦qÎ_àvظ¿'„ø[š~âY»-„bâ2Hq1uXw>‘W'û‚‹2} s3ƒ ­Å¤ŸòGˆÉ¦O 'k:ïøÆÈü(YãLOóRÂÅ~ŽO]{°~ôA؇öý°„sôÐ N]ˆ¬UPž (WB²édçÐZHiç§{à Òý ݹÑPƒ™ñ’Uˆj`pÅÚéïy¬iñ J./Ò|Ÿ¹îéºÒL4k„ ™Ã9ß ÀQ¥*¥”÷K)?AÓOœ1¿~f„¼R,(<Ëi4RÚ‚L_`^n(4#3ê0’~…rHˆ22®úr›C³n^4WýY¤ˆ=¸}åÝ`¬®Ü™$@ÛÁ[Ç»Éq· !.ð"­â¬ùåCx|À’—,Lq&^aðsÓÇ ²ƒ¡™áÐÔ|,è‘õ !N ƒ+Ö^½sì¹ðÜá—óÇ)¿£¬§ø>¸c?ÁÍ7ëï«„½8Ÿs¾Õik›1¶Õ²¬‹hú‰²òzx¿’Ï&ƒÓ²cÞEÙ¾àœìPhzn4ÔhdtÊ#!ÞŽ” ýîPrshÎІæsGÒÜc‘UÊýà1s?À_~$UŸÚ‰ ìu‚1¶Úãþ¤eYki•#Ït¢h^ ÁϘ‹ B¥<’yù~ÿÌÌH + tÆý+ï&Ë8Á yš;ýÓÆþš;6ä È*Y¸=?ÀÍWÖíh » ªê2!„»BÌ`Ò* */Džõ#W¸œ_ ÉCdâíÍŒ:;;â› LÏFý…X€<%öÄ`\ôêÍã;õiÑçÃs££î@‘¬Ri¤Ký>»ìh]k(šHÂ&(œóW,ràØÿ‡â;´ˆªrç³®˜ÈK Ù¢$§ÎΑ(±ã.=ßãmßíŸ2þbxvŒÂ«ªyj—¼Ÿ¹~WÝ…f“°œó/ø‘‡BL5$&“5Á´ó\ 2qª¢dFnXïÎGõöü¸Þ^Løš”4s&µCTóg¹›’{}ñ­ãäå˜LñÁÀg®}Éá%l@çü €&ŽýBˆ¯Ñ j)~óÔ,@¹B,„dä!N¯UTfäFôéùQ}Jq\oÏ'|-Ť²²NÜ+ŠÁ¸t‡SGô–Äkž¶ä®ÀôdÌ¥S¹ÜZàëqû ÛTº¤LÔ=œóø’‡žBt¢U@Ôä!ÊŒŸ™Ò»rq½ÍLxóiOÄL{ÂfƲrnêYrzd¹f yB鮆ôQ½%}HoM÷zs&SÉýTkâƒÉÿÿöî<Øî²¾ãøçûý»%7ÉMH‰A ˜B:"¨`‹D VV)hÇ"èh;­ÓNÛ©êè×Z(A*Á¥A0E– ²*(&A K 7¹Û9¿ï·äBQAB–{Ïò~Í0a˜!÷œÏóœœç“ßïù=:}ñMõ¶Y4¸}ÝýµØ¡ƒÃ¾ç3P׸*‚à’vÜÐ>}h]çnC;§ mìœ\ÝÔ9ihsgOms縲¿£­ J˜rC1vàù¶îþ5ãûžnïé_Ý1±oU甾g:&p+U#(ô]¶hyÓõ*F =Ín2³£Zð­—ñ&I¿d apU£hêà†ö©µõ“7·M*ûÚ{ª›ÛÇ•í=Õ¾ö}lYm늡¶ÎhŒÃK³ì³öj¥£º±è\ß6fðùJ÷àóíÝkÚÇ<ÝÑ3ølûÄÁª9W4¶YÇ :}ñ-M¹~ctѰ)Pœ™W·êR."Î` !½ô-¢ˆy’„‚ºYó¹Ô3Ø×ÖSöUzª/´¯öµ‰j¥³¬ú˜ªtÄ`Ñ‘µ¢+jEgT )mL­Z‘§Š(½#ÊB’Ú£V˜½úž•A«Ô$SÍ,†Š¶R’¬½n9`m忢£6hme_Ñ^ë÷¶²¿ÒQ[_Œ©®+ÆT{;ÆU{+ìÍhZ–)•×êŒcnkÚ·È(£Au»ûC’öhÁ÷ž1_ÒCL4¼ÏÝÙ¥]7 Ô¥Í$-^>BfWëôE?nê·ÉH£¹û$}¸ß{f^“™ïb é\tÍTuŽY iÒÆ€ÖZÜļík:õˆGš¾g1Úh@ov÷Û¥Ö<ø,"Jú1ÓMkÉ×ìC÷RhÒæKÖF(š[ö«¬üG£ŸpNA³êp÷û%ÍmÉ?ž2¯ÍÌ™hËî£uë’´@²  ¿Ý×k°ï"}àÄg[åS@ÐPÜý3’þ¢Uß?W?ÐÒ¸E @Ó­Äs•†º.Ó9‡mj©·ÍÈ£,t÷;%µäs2óºÌ<ži€–—éúú-s”¹@©¹Ü¢ AËÇÝêX÷-rJÙroÑGƒhw÷{%íתDÄJº›©¼ÌWïiÓ˜ççÉŠƒ¥|“‚Gú¨ûÕwÈuC30HASq÷OIúD«¾ÿ̼>3ßÉL~eËÆh]Ç<™–lo¥ñ ÞVÞ}ò¸\§-~¬µcê\¥R9""¾¯½õJ[Îýx³¤»˜ ÀVºty¼@ÔÁ×ù“J»\g-ZKêÛTw@Òn-ûÇUæ73ód¦°.ºfªººP–*} ÙÕv¦w¨còVÜïAA£q3»Ñ̵peDì/éçL`¸tùnj‹ùÊrÒ'€+6Kö yßã4Dûp_"éï[<†K"âf°ƒeš®¸q/©8@Ò~<ÖÀN(ËkWêôã6’ámî~‹Zw߇$ EÄI+™ÀN´d‰köa3¥Ü_¥ö—¬‡PlÇ꺦,oÒ‹—Ë, „‚Æ0mø‘»ÓZ<‡ #â£L`„½t›V ´©`«y>!·¥:uѳ„AAãèv÷[%Ôâ9lŠˆ½%­aJ£\FÚ˹ŠÜWi3 À+˪ »IÝ~«–, ò € qfö-3;‘(ô·ñOÄÔ‘‹¿7IÚwxßÈœ3`Ëj:W©£ã*òÖ烂ãt>IhuDì#©(€:uéòµýTh…ÍRÊ h5±Yn7è´Ew±×ƒ‚Æ,“ôY’Ì쬲,¿F@ƒøÒÒnë™#i®ÜöQX¡ÍüE!Åêw£N9´Ÿ@( h@EQœ”™ß”øDI÷GÄIÜ? 4¢%K\{/œ.uÌSÄ<Éf ÐLâqY\£3Žyš,( hP•J娈¸VR'iHî~T­V»™$€&qÙÍ»Èr®ÌæIÚS© ¡¸bŽ^™-ÓéGý”0( hìòñŽˆX&‰Û$eæ²Ì<$€&õÕ{ÚÔÝ;S‘ûJ¶¿Ò' Pï+å|An·jzq›Ž8¢F 4vùXß‘4†4$Iƒ±¿¤Çˆh/=â7ö‘l/¥q*P?+ä>™Ý®ÞÁ[uÁqƒBAƒ+ŠâÄÌü†¸íêå>C @‹Zº¼[em¶†lYÎæ4v`´VÆ1(+nUõW?ÔÙgMÀÝÏ“t¡¤‚4^òdDÌ‘´™(l)$?œ¢¾}äš-·½x²°³Åfyq‡¢v‡Î<šïc šD‡»QÒ¹Dñ[B³?)Ëò*’ðŠ^|²–µÏÞr»–f)ÍìÀŽP¨WQ»MïÒ)§ äµâîÇGÄ…’Ö2”umFQßÊÌ…Dñ›2syfI¶ÚK›Ùm¥fËôNe^÷ ø)eüPguŸÌxô=dë¸û’N“´IÒW"â³’Ö0¤uç­î¾TÒT¢øµˆ8PÒCD`›-]Þ­ªöVÔö‘ro¥O"à•Q*ía©¼Sgó8P@^¯ÝÜý Ií/ûo}’.ŠˆÏHZÍÐŽºŠ»ÿ¥¤OJj#ŽWôéˆø+b°C]qýx©}†2gq…¤Ø(³{U«Ü¡³XOm+°î7¼°}%ƒ’.‰ˆOKZÅŠùEQ\’™Å«Zû gØy.]Þ©¢6Kî3åž2íÎ4ÿ 7SÊGe~§¿ý-YÂmVíÒæî+%½áµênfþÀÝ?_–åu’’ᑱù˜¤Ðo^ÂoðÌŽž—0²2]_¿aºÒgÉÛf*ÊÙJçL&4KñxVæj zÎ=¦—@( ;DQï>CâõxDÒ—"â2mÙ3‚ï-îþo’ö#Š×ôˆx1¨—ݼ‹,fÉs¦¢˜-iBAµêõ²xHQù±Îz·áS@vJ¹-3ÛÆÿ}“¤¯EÄ—$ýœáß!ö7³2³ã‰b«¬‹ˆy’ž! uëÅ}$‘»«°-¿¦u êgA¨^)V?Ñ‹WÉŒ;]( ;͸ûý;ä›Ý™WFÄU,·É^îþIIï“äıÕóîeY^L¶”ÈfH1C©7RJ0²_¢ù¬¤Ÿ)ãg” Ȉq÷‹%³ƒÛrx¯È•eY~GÒ¦Åï5×Ý?*élñt«×%3oÌÌcÅ~$ÍâÊe“•Ý3TÆî*b†"g(½ƒ`°ƒ¾9«r­P–hP³§ƒ2&¹û“’ºvâÏÈÌïºû´Rj ªIDAT·Ë²ü¾¤ç˜"’¤¶¢(NŠˆ™ÙÛ‰c›lˆˆý%ýš(4ïz1M_»«Ú:g¨Íg¨Ìi2Mg“;¶nUš)Ù“ÊxL¿Ð/;ViÉ5‚¡€Œšá3%>=‚?2Ììþ̼1"n”t§¤VûÌt÷s$+i7>.ÛñA3;»,Ëÿ$ -éŠëÇ+;§Iå4)§)4M¦©’„Óâ…Ãò™­T¿TÏøÇtüOO©…»?&iÖ(¾†™y‹™-ˆ{$=¨æ;ÃÁ$ìî'H:AÒ|D¶_f^—™lÒ€—[º´PmÒdU«S%ÛU^™ªÌ]%í*·ø6çªsPÒ¯”¹JeÉ×Êò9•åµUÖh°X£'ô<ÅÑtÄÝ¿(é<† æñˆ8H[¿ÔƒK/íTÇî”1^¥&H9^VŒWFÜÇ*£{ËÙ&ÖÕR¹˜jÊ|A® 2_¯ÌuªÅ:U|ªE¯6õ¯ÓÇ 2Ð*dÜð£wÇ3Th Õˆ8LÒÝD héÒBCÝc•ÝcåÝrëVUceÖ¥´NYtIꔲKi]RtIÖ)Yûèo ÏªÜú•Ù/Ó€2û%ï—g¿ÒûUä R¾ ÁêFU«/h”:åÐ~aî~¤ &4˜GÄçˆZÔç¯ïЬ²PoW—†6WÔÕÖ¦š:ÔVñ-ÁÛ”µß,*VüÿU—ZTUäïÞÖ”Vª°A•5ùæªú«Uµ­©«”:Jüæö]€²¯mxïdž bø‘»'ˆÓÎ^‘×ë «T*‹)h0Odæû) X@ʲü0Ã2ïÖ–§š Á Èfv,ÃFafçIº$°€¸û‡%q˜ÅÅeY^B ¯­7¡w ?zwúÿ™ÝY–å‘’x>:ÀV¨»+ EQœNù@ƒx¢,ËwS>¶^Ý]q÷%ÍghPçú#âpI÷ÀëXï×Ùëy+å Íìý”€/ î~C‚ð‰²,—ÀëWO·`Mw÷U’ÚÔ±/GE`ÕÍwÿåu8Ÿ¶]½\iw÷_IšÊ eæòÌïd(PG~GJZM;Ö¨^q÷R>Pg¾òAùØ Fó HÅÝWHÚa@]|Ìî.Ëò8IkI`çµ+ EQ¼‹òz‘™Ë˲\DùhÒç?êÄ×3óI›ˆ 9 Ⱦfö6âÇ(KIÿgH"€¯2*­ÇýÕÇ$h]CfvnY–—ÀÈÐãîOJKü%ÏEÄ{$ÝF#kÄoÁr÷?¥|`Ý )­Q@LÒŸ;FÉåq˜¤UDФ(Šc%íCìaföÁˆ8SR?qŒžÝ„ç›±÷#êçqª¤ˆ`ôdØÛÝÕ(ž=‚–syD|HÒf¢¨#Y†$ý—¤’ر“=of' ßrEù¨#Åþ¬ ™ùí̼Ü̺$ÍáŸÖpuD¼33ï# €ú3š2f¹û_K:KRCíô”™ýyY–×@ýÍ+ë3óºÌ¼ØÌ%í'©‹!Áë’.Šˆ“2óAâ¨oõôHª±EQœ–™êÅÖL^³{ʲ<_ÒH€²­¼R©Y–åGÌìuú1ºÖJúLjø‚¶\d‡˜ëîçI:SÒ8†«åm’ô¹ˆøìð¿€²SŒs÷SÍìœÌ<„ak9’¾ÿ,é9â €Œ¤ùî~®¤Ó%Md›¾x\:\<~MÑÔYÅEÄ{‡÷Šð­æ±QÒ—#â_%=Kz3®(ІËÈbq®H£úõpñøŠ¤ Ä@i=Ãeäd3;ZRÃ\ç“ÐìI–eùI5 €4ª)îþžÌ|™.®ŒÔ“§%]ÿ.éÄ@i6]•Jå-±HÒñ’æ1ü#n@Ò23»¼,ËïIª ¤UÌv÷c3ó3{»ØÄ¾³l’tƒ™]S–å2q~êªT*oˆÅfvxf(©B,ÛìII×›Ù—eùIƒD È«[©TFÄá™y¨™-´ ±¼ªõ™ù?fvsDÜ,éQ"dûÌ,Šâ Ì<832³ù’¦·`©-›Æï’ô£ˆø_I?•T2E@Ù¹z$Í)ŠbßÌœ“™sÍl–¤=%u6Áûë7³‡3ó'’r÷ŸÖjµû$õ2ô €Ô—i’ö,ŠbVfî.i·Ìœîî»eæ4m¹z2Úßk’Ö˜ÙÓ±ÒÌVHZáî+jµÚ I«Ä• P@šÆI“$M”4©(ŠI’&ff÷p9/©}ø×1zÙ9&™é’&¼ì÷zÁÌ^|¬mhËIâ’6JÚdfë%m0³õµZí)Ik†ÿI†v‚ÿ^/vM®YIIEND®B`‚yt-project-yt-f043ac8/doc/source/_static/yt_logo.svg000066400000000000000000000040731510711153200225460ustar00rootroot00000000000000 yt-project-yt-f043ac8/doc/source/_templates/000077500000000000000000000000001510711153200210545ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/_templates/autosummary/000077500000000000000000000000001510711153200234425ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/_templates/autosummary/class.rst000066400000000000000000000006101510711153200252760ustar00rootroot00000000000000{% extends "!autosummary/class.rst" %} {% block methods %} {% if methods %} .. autosummary:: :toctree: {% for item in methods %} ~{{ name }}.{{ item }} {%- endfor %} {% endif %} {% endblock %} {% block attributes %} {% if attributes %} .. autosummary:: :toctree: {% for item in attributes %} ~{{ name }}.{{ item }} {%- endfor %} {% endif %} {% endblock %} yt-project-yt-f043ac8/doc/source/_templates/layout.html000066400000000000000000000031331510711153200232570ustar00rootroot00000000000000{% extends '!layout.html' %} {%- block linktags %} {{ super() }} {%- endblock %} {%- block extrahead %} {{ super() }} {%- endblock %} {%- block footer %} {%- endblock %} {# Custom CSS overrides #} {% set bootswatch_css_custom = ['_static/custom.css'] %} yt-project-yt-f043ac8/doc/source/about/000077500000000000000000000000001510711153200200315ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/about/index.rst000066400000000000000000000065761510711153200217100ustar00rootroot00000000000000.. _aboutyt: About yt ======== .. contents:: :depth: 1 :local: :backlinks: none What is yt? ----------- yt is a toolkit for analyzing and visualizing quantitative data. Originally written to analyze 3D grid-based astrophysical simulation data, it has grown to handle any kind of data represented in a 2D or 3D volume. yt is an Python-based open source project and is open for anyone to use or contribute code. The entire source code and history is available to all at https://github.com/yt-project/yt . .. _who-is-yt: Who is yt? ---------- As an open-source project, yt has a large number of user-developers. In September of 2014, the yt developer community collectively decided to endow the title of *member* on individuals who had contributed in a significant way to the project. For a list of those members and a description of their contributions to the code, see `our members website. `_ History of yt ------------- yt was originally created to study datasets generated by cosmological simulations of galaxy and star formation conducted by the simulation code Enzo. After expanding to address data output by other simulation platforms, it further broadened to include alternate, grid-free methods of simulating -- particularly, particles and unstructured meshes. With the release of yt 4.0, we are proud that the community has continued to expand, that yt continues to participate in the broader ecosystem, and that the development process is continuing to improve in both inclusivity and openness. For a more personal retrospective by the original author, Matthew Turk, you can see this `blog post from 2017 `_. How do I contact yt? -------------------- If you have any questions about the code, please contact the `yt users email list `_. If you're having other problems, please follow the steps in :ref:`asking-for-help`, particularly including Slack and GitHub issues. How do I cite yt? ----------------- If you use yt in a publication, we'd very much appreciate a citation! You should feel free to cite the `ApJS paper `_ with the following BibTeX entry: :: @ARTICLE{2011ApJS..192....9T, author = {{Turk}, M.~J. and {Smith}, B.~D. and {Oishi}, J.~S. and {Skory}, S. and {Skillman}, S.~W. and {Abel}, T. and {Norman}, M.~L.}, title = "{yt: A Multi-code Analysis Toolkit for Astrophysical Simulation Data}", journal = {The Astrophysical Journal Supplement Series}, archivePrefix = "arXiv", eprint = {1011.3514}, primaryClass = "astro-ph.IM", keywords = {cosmology: theory, methods: data analysis, methods: numerical }, year = 2011, month = jan, volume = 192, eid = {9}, pages = {9}, doi = {10.1088/0067-0049/192/1/9}, adsurl = {https://ui.adsabs.harvard.edu/abs/2011ApJS..192....9T}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } While this paper is somewhat out of date -- and certainly does not include the appropriate list of authors -- we are preparing a new method paper as well as preparing a new strategy for ensuring equal credit distribution for contributors. Some of this work can be found at the `yt-4.0-paper `_ repository. yt-project-yt-f043ac8/doc/source/analyzing/000077500000000000000000000000001510711153200207135ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/analyzing/Particle_Trajectories.ipynb000066400000000000000000000306341510711153200262450ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Particle Trajectories" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One can create particle trajectories from a `DatasetSeries` object for a specified list of particles identified by their unique indices using the `particle_trajectories` method. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "%matplotlib inline\n", "import glob\n", "from os.path import join\n", "\n", "import yt\n", "from yt.config import ytcfg\n", "\n", "path = ytcfg.get(\"yt\", \"test_data_dir\")\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's start off with a FLASH dataset containing only two particles in a mutual circular orbit. We can get the list of filenames this way:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "my_fns = glob.glob(join(path, \"Orbit\", \"orbit_hdf5_chk_00[0-9][0-9]\"))\n", "my_fns.sort()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And let's define a list of fields that we want to include in the trajectories. The position fields will be included by default, so let's just ask for the velocity fields:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fields = [\"particle_velocity_x\", \"particle_velocity_y\", \"particle_velocity_z\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are only two particles, but for consistency's sake let's grab their indices from the dataset itself:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load(my_fns[0])\n", "dd = ds.all_data()\n", "indices = dd[\"all\", \"particle_index\"].astype(\"int\")\n", "print(indices)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is what we expected them to be. Now we're ready to create a `DatasetSeries` object and use it to create particle trajectories: " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ts = yt.DatasetSeries(my_fns)\n", "# suppress_logging=True cuts down on a lot of noise\n", "trajs = ts.particle_trajectories(indices, fields=fields, suppress_logging=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `ParticleTrajectories` object `trajs` is essentially a dictionary-like container for the particle fields along the trajectory, and can be accessed as such:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(trajs[\"all\", \"particle_position_x\"])\n", "print(trajs[\"all\", \"particle_position_x\"].shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that each field is a 2D NumPy array with the different particle indices along the first dimension and the times along the second dimension. As such, we can access them individually by indexing the field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.figure(figsize=(6, 6))\n", "plt.plot(trajs[\"all\", \"particle_position_x\"][0], trajs[\"all\", \"particle_position_y\"][0])\n", "plt.plot(trajs[\"all\", \"particle_position_x\"][1], trajs[\"all\", \"particle_position_y\"][1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can plot the velocity fields as well:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.figure(figsize=(6, 6))\n", "plt.plot(trajs[\"all\", \"particle_velocity_x\"][0], trajs[\"all\", \"particle_velocity_y\"][0])\n", "plt.plot(trajs[\"all\", \"particle_velocity_x\"][1], trajs[\"all\", \"particle_velocity_y\"][1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to access the time along the trajectory, we use the key `\"particle_time\"`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.figure(figsize=(6, 6))\n", "plt.plot(trajs[\"particle_time\"], trajs[\"particle_velocity_x\"][1])\n", "plt.plot(trajs[\"particle_time\"], trajs[\"particle_velocity_y\"][1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, if we know the particle index we'd like to examine, we can get an individual trajectory corresponding to that index:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "particle1 = trajs.trajectory_from_index(1)\n", "plt.figure(figsize=(6, 6))\n", "plt.plot(particle1[\"all\", \"particle_time\"], particle1[\"all\", \"particle_position_x\"])\n", "plt.plot(particle1[\"all\", \"particle_time\"], particle1[\"all\", \"particle_position_y\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's look at a more complicated (and fun!) example. We'll use an Enzo cosmology dataset. First, we'll find the maximum density in the domain, and obtain the indices of the particles within some radius of the center. First, let's have a look at what we're getting:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load(\"enzo_tiny_cosmology/DD0046/DD0046\")\n", "slc = yt.SlicePlot(\n", " ds,\n", " \"x\",\n", " [(\"gas\", \"density\"), (\"gas\", \"dark_matter_density\")],\n", " center=\"max\",\n", " width=(3.0, \"Mpc\"),\n", ")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, so good--it looks like we've centered on a galaxy cluster. Let's grab all of the dark matter particles within a sphere of 0.5 Mpc (identified by `\"particle_type == 1\"`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sp = ds.sphere(\"max\", (0.5, \"Mpc\"))\n", "indices = sp[\"all\", \"particle_index\"][sp[\"all\", \"particle_type\"] == 1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we'll get the list of datasets we want, and create trajectories for these particles:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "my_fns = glob.glob(join(path, \"enzo_tiny_cosmology/DD*/*.hierarchy\"))\n", "my_fns.sort()\n", "ts = yt.DatasetSeries(my_fns)\n", "trajs = ts.particle_trajectories(indices, fields=fields, suppress_logging=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Matplotlib can make 3D plots, so let's pick three particle trajectories at random and look at them in the volume:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure(figsize=(8.0, 8.0))\n", "ax = fig.add_subplot(111, projection=\"3d\")\n", "ax.plot(\n", " trajs[\"all\", \"particle_position_x\"][100],\n", " trajs[\"all\", \"particle_position_y\"][100],\n", " trajs[\"all\", \"particle_position_z\"][100],\n", ")\n", "ax.plot(\n", " trajs[\"all\", \"particle_position_x\"][8],\n", " trajs[\"all\", \"particle_position_y\"][8],\n", " trajs[\"all\", \"particle_position_z\"][8],\n", ")\n", "ax.plot(\n", " trajs[\"all\", \"particle_position_x\"][25],\n", " trajs[\"all\", \"particle_position_y\"][25],\n", " trajs[\"all\", \"particle_position_z\"][25],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It looks like these three different particles fell into the cluster along different filaments. We can also look at their x-positions only as a function of time:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.figure(figsize=(6, 6))\n", "plt.plot(trajs[\"all\", \"particle_time\"], trajs[\"all\", \"particle_position_x\"][100])\n", "plt.plot(trajs[\"all\", \"particle_time\"], trajs[\"all\", \"particle_position_x\"][8])\n", "plt.plot(trajs[\"all\", \"particle_time\"], trajs[\"all\", \"particle_position_x\"][25])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose we wanted to know the gas density along the particle trajectory, but there wasn't a particle field corresponding to that in our dataset. Never fear! If the field exists as a grid field, yt will interpolate this field to the particle positions and add the interpolated field to the trajectory. To add such a field (or any field, including additional particle fields) we can call the `add_fields` method:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "trajs.add_fields([(\"gas\", \"density\")])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also could have included `\"density\"` in our original field list. Now, plot up the gas density for each particle as a function of time:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.figure(figsize=(6, 6))\n", "plt.plot(trajs[\"all\", \"particle_time\"], trajs[\"gas\", \"density\"][100])\n", "plt.plot(trajs[\"all\", \"particle_time\"], trajs[\"gas\", \"density\"][8])\n", "plt.plot(trajs[\"all\", \"particle_time\"], trajs[\"gas\", \"density\"][25])\n", "plt.yscale(\"log\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, the particle trajectories can be written to disk. Two options are provided: ASCII text files with a column for each field and the time, and HDF5 files:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "trajs.write_out(\n", " \"halo_trajectories\"\n", ") # This will write a separate file for each trajectory\n", "trajs.write_out_h5(\n", " \"halo_trajectories.h5\"\n", ") # This will write all trajectories to a single file" ] } ], "metadata": { "anaconda-cloud": {}, "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/analyzing/_images/000077500000000000000000000000001510711153200223175ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/analyzing/_images/fields_ipywidget.png000066400000000000000000000576271510711153200264010ustar00rootroot00000000000000‰PNG  IHDR€À” Òf_^IDATxÚí½tW–¦9gÍœ3;gfvÎÎv÷ÌVoOmwWuUWKª’-•T’J¦$QŽ*YÊ{‘EI)Gï½÷Þ‹Þ{ï½@‚0$aH€ <@ïÆ÷/‘3„(‚üÿsÞ22âÅ‹È̈/î½ïÞg’$I’$IÒ ¥§S I’$I’$”$I’$I’€?žrssÕÔÔÔÔÔÔÔÔ® •””4.5[_2555555µë±â%gÙ†„Ó®Õ=ìõ]’èà1rY$”–Û·3ãê@ܾÏ>û¬¥¤¤ØÄ‰ÃصkWËÊʲ-[¶D@\µÀÙÇC·Xö™+ ÖÁÌÿmÆì­,k>d‹edåYïYû-5ãÌE˜xâ´=òÍŠ`Eöfß—îHu™lw0YA555555µë-O CÞ²ý§ê €,Ë-:gïŒÚíÜÂѰ¾.àôôt»ÿþûä­Zµ* €7n´7ß|ÓzöìÀ^aÔèZØvÑò¯ÆítOœÌµ§:®¶–÷ÙÇöº×5Ë`ßÙÂÛ€ù…6fi‚=€á¢­)ú’©©©©©©©5N<•WjÓ·ž°¯frñz‘H\_}pGÒ™`Ý“¶ýè™Z¸ÀfCwºÉ!µ p÷ÒK/¹ø¿öíÛ;ÌÌÌ´ï¿ÿÞöïßo/¿ü²³z«àäÉ“ë €@åÛý6Ú†ýi¶0½œÜÐäþ»,Á¹‡“Òr졯VØæƒéöøw+ÎÙ˜d«v·µ{OØÓ@ibˆššššššZ#@&q¬Ëv ˆCI§ŠÜßÊóçíT~iL¹:Å KËó|·ɱÅ{OZÉÙŠp\`\zÁâ"Ž6ã¸f 0mÚ4Û»w¯ÅÇLJ-€ÀÞÁƒÃëíÞ½ÛRSS/˜ ÄÕݾÿþ{KHHpP8mÚ4›;w®-Z´ÈN:e“'Ov7ŒY³f9à7nœ-\¸Ð–-[æn6›7o¶Aƒ¹~o$äüÍž=Û¯“'OÚüùó]ãç¼}ûöî&èlß¾Ýݸ ³NÇŽ ._¾ÜmÀ`Å:vì˜{"1bÄ €XFùò>c글Ü88‡X­}¼çÏ»“ÆŒ#ŒÐ3Ï.n#GŽt1‚ýû÷wV>,/€7±ÄâÁuòšë{$ó½‘Bê@,Μ;~±ìsïÑ}F(¼ Hà ÀÍ8paJô£»´I À PÃLj,y€ 6€7n@‹ŸÝ‹ „çŸõ9ÿ¼f{ÜÊÜløËgs#ÅÔXV°LñK7 –sƒæs^i€4ç—sÄÍõFŒ#ª —.]ê@Àã¡„õù.ržø øË{tqq±°ä&ÌõÑ/<)4Î%ßS\é¼Ç:|ù]Bœg¿q`Ž;Ïœ~×þAN÷ ð* ÚÕËH|ЮYÀ×2á©§ž²&MšX~~¾®ÚW€jÊ(”€ÀKn<í*íðÇHCœq€’P(”€jª"T@I(J×3›¢vå : Ó˜ä¡óÐ0©áPß©†i<˜è<4L#Þ™xpµ§={¶ñ $I’$I’té:sæŒP’$I’$I(”$I’$I %I’$I’€@I’$I’$ P’$I’$I(”$I’$I %I’$I’€@I’$I’$ P’$I’$Ixxº¸Òºm,µÞ›ÏÆl]×Ú›£÷\ÐÚ¯8]ë6jj?dûdiIø;¼+£Â¶§©5T㢧Ö0M’$à5 €´ÚTQQa§OŸ¾ •••éÛ!ýh½îYh7 Vk¨öc¾¿ÞÚÖ­[õC•$`ã@Iº–Pá ' Eð&”$I(I@ šP’€@I Õ€’$¼.°²²Ò¾ùæ{î¹çlùòå¶zõj÷?mÚ´iÖ¯_?·þùóçmøðáúfHÇŽk]»vµøøx¸ËÀ´´4;xð`øuVV–íÙ³'&üL˜0ÁRSS/Z>eÊËÌÌ ¿^²d‰=zT(I’ðÇÀ¤¤${÷ÝwíìÙ³¶cÇz¥¥¥öÚk¯YBB‚ 2Ä]Ô/^l³fÍÒ7Cjx×]wÙ‚ ¬|øá‡VPPà¾×ëÖ­³C‡¹ïùñãÇÃë§§§;¸á»ž‘‘aEEE¶fÍ· eggÛÊ•+-??ÿ†À.]ºØ©S§ÜëM›6ÙæÍ›íÈ‘#¶sçNw~Nž<é^s®÷ïßï^s·oßn)))n»Áƒ;ä}&“}ÿý÷îúº»ví à ÈëÄÄD $IÀÑÂ… {ܧNjëׯwïqÑnݺµ¼¼<}3¤F€Ï<󌃎½{÷Zqq±[Æwðøè£lÛ¶möù矇×ïÔ©“7ÎÞyç‹‹‹së`¿ï¾ûœEëùçŸwAwÞy§ƒÁi\vïÞí`¯cÇŽÜ&MšäÎÿ AƒÜëîÝ»»×x ’““mâĉ®ÏÞ½{Û‰'¬W¯^¶hÑ"·çÑ à 68ï¾}ûlòäɶqãF·œ‡S $IÀ8à.Ȥ|yöÙgÝÓ=@ñ÷¡‡²è[!5D¸~¹¿ýÛ¿u–;þ÷êÓ§ÏE8~üxgÅBï½÷^ø=~X›7on·Ýv› •¸‘ðØxàX6þ|ØŸ~ú©>|ØÁ3Ë=bÅê¾ýö[· ˆU‹àÀÃØ¹sg×'Ûc±ÅËÀº\—d”$Iø`yy¹{ºÇâ‡K —®`.àÇŽsë\Ô%©1 î[`‡„%ØÀèÜÇgŸ}ú-œ>míÚµsˆ{½ýöÛî/n`âÕZµjå,¼_RRrC mĈ6wî\ç¢þp¥s@4ˆÅÎ ×®¬;tèP€¼æÚ”Ó—ÀQ£F9 !VVþÒ'ÿ³¯Æ:1E(IÀF€u ·Í×_í@Q’bµîÙ³§}ùå—nÈÇìÜÀ#GŽ´¾}û:KÖ¨Ù³g;ðÃ"ò]÷:Æ ³nݺÙ믿îâY‹W‹-Üë ñ xÈíŽ[xéÒ¥ìxX ykñ{€Ý²eËœEoÕªU®±¤ââÅÕ‹•¸¿+V¸&‰GÈg@c½Æ(”$àu€’ÔØPjxTJ’P(I@ šP’€Àº5gΜ¨Ëq#ý"í é-üd—X"O"1KÄ%E»°³—˜——€@ $IÀk ]"hb¤È“Fvnn®[‡¿Ìžd2 D#›u}>4Nï#xîÜ9×ÁܤŒ¡¿§Ÿ~ÚÅL[lÑ'ûÀ³/kŠ~Øž~Ø– }ò…ù÷Øžeˆý²ÆÄXÐg¿€™ßcå5Û²ꌃØ%ÆyŒ5E ú«¯¾êú$bV$f˜d@®3âœ|^9& ´iÓF¿  P(I’ðÚ@Ò-üéOr³óš5kf£Gvé`€'ÞIÓжm[KÌÜcæäûï¿ï ‘¤®ä[#ÝA÷Ìöûë_ÿêúxã7ÝrË-.è›í€¨—^zɈ¿øâ‹ ¢ ù¿ž|òIˆÿÔSO¹@þ»ï¾ÛAyÅèÿñÇwùÚÆŒã‚ø™¥ùÅ_8‹â|à ™ @9 Yï…^pÛÓ\c¿ùÍo¾ùæ›.-Aÿ5ÅlÄ&Mš¸DÁ² Ï Qúgâ€@&tèÐÁ!2½ˆ$l bæ.çSMMMízlLFÖ@é•W^q,R^ƒ‹™XòH›Õ +ðÇl` Ö¾}{=€%·€+¬_"ÿé8€K²°Ô¿kÚ´©ƒÌXŒ2¦¯¾ú*tãÀt5@' «6àŒ|meeenß$¬&eÛ³-ëð—q3F,y¤ò ™-Ç2Û—-c‹¬ áÅ2r!ãÆ%L@.ȹyðÁà2³‘Ù§’P’$Iº6Då"` ļáæÄâ ‘€3f̧ƒ–}ºëk'ã@Ž‹t ,§"A͘ÀšÈ¹á8éüg‘È9$•…P(I’$ ¯iDÀ…Ë—ü^¸|1—úX6` +Ù¼yóað!Ü­.T€PC=zôp`Fª+° p bA%¬ˆ5E¼ž‡(úElƒõ#oý°-@‰K¨±bˆ·0b`—0cb9®\àhkÙ²¥ëŸ1×1+q•q^ø+ ÇÆx°œR%Z% $I’$¼f*0þHXù‹ûaÃêG!x’³„Äݱœmp}úI">a´ß–÷ùŸÆú¬Ëÿ¬ç[MÑßÞ¿Ï_ß¿ß}¯^½ÚMâX¹r¥BÆé­x¾þ29ÄW†ðcâY—ÿé3ÚŒ`ÿ>ò}×î¿ÄØ1Ó•‰Lx Ù3ëúªÌ’å}?1‚þ˜­Ëvô÷裺Ü{€ %è“}"EKƒxŸŒþ˜Àá×§B3wùK?¾" û#5K41^’Q3`‡1{Àbþx9>Ö£Òý±œãäøü–ñšÉ(üÏ,hÆ@¥DõRÏø '‘æH¥CšÆÀþHLí¡ã˜8À _BRØD;7Œqp.³ÿÂr¬¤¦ñÕ\8wôÉgÀ:þ˜ü9NÞc3³²9?ц÷8n>sù!ý¹¦O>s $I’$l„Ȭއ~ØÝøI‡Bªäâ#= 9÷¶lÙâ¬u$d& CÉ#R«Ü™*¾.òõ:¿ÿýïXöŧ`!% ¹cUHxà7!£yóæì¼õ‰™¾0Bú•wÞyÇ9ÿõX±º1vRÒ°o*9Œ‘ã¥@Š48äú# û#i]°^’ÿX–9Þ?þØU¡" JxŸ1¼O…hõ€9ª„0RÈpžFŽéà‹¿IiäþF@Î;y©ÀBEÆCBމý“ ‘||†“Y8?¤®ásà3äøHÙCÿ|g8Ÿ+V¬pÇÊ;ãd}ª²¼üòË.—#ÐËñq¢•ÏJ’$IÀkkV!Çåݼ»’÷}"hªr° p£ü[»víüP²¼€€ ŠV „¾ÈÛQê-V"hÇ»†ù‹E Ò€…*ì—êX×hi\³–y¨$E ë‘ô äýå/qVË›nºÉ8^–cÕ{î¹ç\ù;€”ýÞ|óÍÎrGY8¬l,”|êh©`È &8Ь8Ïä ´9€>ÿb, çØæ˜ÈqȘïºë.7 ™ó{ûí·»Ï ,ëq™-MßX}²mú'™·ÿžxâ‰0ú73¾rJê‘þäxÙ‡¯Ñ,”$I’€+`0JbUñyî€#J²á†Ä¥ˆ±®J Ö(ú£t\,¤O` ay¢obì%UãÞÄ2é÷‹%Ñ— ‹&,dÈ[É@ håx9nÆNeÆ´nݺ0²Ü I©±Ì±_¶ço$b)¤ÚÂ…[b=Å¢F?™@Ÿ‹€XU°çäÖlËùňu«'ï3vàõîòhÈ9ñn>K’„µ$×¶±nò0fŽ'š«ûZÀ£§Ûô½b¾_QYn£¶ ¶þº[qYÑU=¶¡‚ãëŠÛþ ¯QJ^òl4kšÙ‰ãµoËu€ºÙ^|·±ó{ð99ùž¦¤¤ÄìƒõùžGž„K¿ÿ`u9¢ÂO/I’P1 ·!àð`)¢ªÅâÅ‹Ô @nùò匀0\­Tö¾ ¶á‚ÌàÊDÀ! \â@°$y·".c¯)n:¸c>Ü£ˆ›Üð?€‰5‘~د@MäD¸|Ù–>±‚áöŒKn|ŒÛ[79€@haã¸9VŽXÄÆqŒ=ÚU0Á†•ŒsUSœ¯ 8Ë#®g ‘}ÐxÍrú { Ä¢WSX%ÙbÖ9\ÑX§L™â`‹.u—/®wr826â*ZŽ›þVª©ˆœS¶a=>ú`=@œÏš†?÷@z,«ëµ€ç*ÎÙ¬ýSíæ~? /++/»àFob÷¿Åº®þÆÊ+˯ê±ååš}ù‰Yî™+ë§kûè8e¼YjríÛòÀ¢îÅCßKÀЋ LüÖc‰4n¢‰ïú¥ŠpƒhTî3 ~ûüvj³¾šOõù8ï~¿’$ oh¼”'iN"P Ü.W nGßü„KÖ¨È~h—2Qð­ (ÀW"nL5Ç­êÉ}LÀ6Ö\ò€xc×å 7û¥ ì»þW»kð¯VTVX»å_¸eÏOzÔJÎ[ϵì'þûŸÿ³½<¥I½Ç4{†ÙÓ›Ýýof#‡îý×Ì¿ßlÙ¢èÛ,˜¥MK³ï4›0*hèÛÖÕ¸6xnÉ«úPÈ n f›=ùg³7_”Šcàùà§½}kOÁú)IfOã{ೃûBë¼ÿºY‡¯Ìî¹ÅlßžÐ2Šxpðâ¡Â7„xÐÀî/¦ÀĘFÎ`'TËzÚÄò­ôc,ºñì³ÏºHþÇºÍØ¸Hãzÿý÷»q‚><…1òÝ÷pÏö<ôxaQ¼õÖ[¯º[’$`£@,sœD\†%Wê§ŠnVf¥úv¹Õ7£È~hÜ ê+¬‹¸¨ØîJ'7±5Çr9ç +EÍ~üÌçú«%“U¨®rµ'l\+Xz®Ä~ÕëØ„#lä¶W%.±›zÿĆnék?íúÚÊ#K,¿4Ï^›öŒõXÓÞNeÕ{L_Æ r³g YñŒ³3€¯äD³¶ŸÆ€›‰fs¿7+?göû_™åœ¾έ^Þ.jo¿Ú–g¤XÆWpÍ ³‘CBý³œÓ£S0®m¡užx ßÑP{õ¹Ð2¬½‘“—ø¾`±æ÷Èÿ¾$$a< "À‹ ,‚Mš4 U$bqç·Åï“ eõKÔæ¦?¬óÛsaƇ€ñ`™Ä î³ø‹7xÀÐÿþ±Ž„‘h<È^m ¶$IÀF €’ÔëÞÏ{üÍ=8ÃÆïîpYÂûY÷ÿf“w±1Û‡XZÞ±elÖË6lKßKS·¸Æ2›0:[=:„,Xð¶nŠ €+–„Àñ¶ÏTUVüîËj\4Ï,­ÊhåA’þé³{ûÐöÑôU¤z™½þ×ê~ˆu¾³N™e¦›=ß$´ŒØRÂ"ÅLûš}ðÈd.¬åÔ çë!, %ñ å]À¼&”!ÖC>„" fÍ€<ȃ lzK%3Ý}˜ˆ=ˆ‘Åêè–€=fÝG>,²I’€ÀX‘qE‘ºkÖ!,õq93þËqá6„|N¿º„µãÇÈÍw­ °Ñ~ykû×>ÿ`ú[ûËè»­ò|¥½>½©Ý1ðÎ-œ‘Ê ùÕÒ–޾¤1µna6u‚ÙŒÉfÉIfñ‡ÌÞ|14ébîÌØøÁë!×îw­«—Ïœj6"`¤ƒ›Økö^³à˜»„‰åŸ¾ê—e[6Dï{HU8ìÉŒL#"5èwÿÞÐÿo½DèEÛ5;´ k1¤‘ÂÚ†9RLä".ZLÁrÈr/à —,ÖCÒ iÄ´ú„ðüîp-GK—äåÓ ábfÒˆµ‹ Ÿ)å´M@"ž òk¿Ëvd ð@R#EÂ&ãºçž{ä–$à €$þ¡D¬"¾.šb%‚¾ZâØ™Q—°H«ÂS:©"~ûÛßÚúõëÝÄ údv+û`f®ØŽf±aßÌJæ/ûõéU¸a‚„åì ëóקމ³| GÄù13F¬Y‡™‚4‚ű¤T/ëáöâ\áò"ŸåËÍ…øºš"]ŒO ͱ3VƵ‘ Ì&Æ:Á9aúÄâµ1“sí96n”|&¤dá\â–óPÍ8‰ÓbÜXJ@Î;¯™Ñ|½Å8ý؈Û4#½ºáñ\² äþ73ø]e_¼ †cÀÌo“Ÿgv$øÊO½òñ\8žòsWÖ±«ñÃ~"Ö¶>âw-/§$IÀ}@.È€œ*Ììãf@\ ¨~è!ß ‚ÛÁ 2C‹-W3 °‚EK0ÃëhP‘^…±á^b¬ÌnìÔ$¸t€&öóÒK/Eͱ‘‹ë ÇŒ¨ØB_ÌxäócÜŒùøƒ›-̸ëS£X(I’$ oP8H©È‘o/Z"h,r^@È,@@„uëJÍkJÅñéb%‚f&®O€Œ’x# + ã†(YÆ~iŒ¯¶DÐ@#V8à ¤ÛZX@’CcD€Ö7  èäE "‰q11š€6„ë˜~Ø7ç Ë%V°ÖÑ—·žÍ™ýˆøÐXHCÆà1ê¿üå/Ãç÷±P’$I /@‡ÙrÀ€”aM‹€¼Å ·)@‡[Ô'aÅ- ±F±Öàˆ1f(¿ ™åÇØÎ[ø°6mÚÔ¯©V‚HdŒ%®6ä8-úÃâ¶jÕ*×8ïÆÝ‹Å76P‡+•¾>ryô³;¥Üpy_ ÞqÇ.ï!–9ÆÄ_¬¢Ó×€¬K0¼O²ûÈ#8‹,0 ÀS•k#UAp%cÝô®s`ãV]ÉŽë7ü–Áø±Åw•ß=fµ%”þ1Äx¢%_¿jÂñ1e¯_oeÁ %<…õtyŸ˜ù½ínÑÜr¶_zŒä™àF—½aCƒŸ›“«WÙÙ¬,ËÞšÚއëp¬$Ý’$¼Æ¯Xð—†Û—¥¿€X pDü) pÍrSñq‚”kl|P7V<,gXò¸@»†•‹í&Öê¢ûºÄÄ4^@¨ÏGÆûì˜cÙ†Z.xì‡õë2c‹0 ôIÌò¥?–û˜!ÎãÊp­œÿˆÅŠ-òûŠ™éLXë8Ï@È_@›¿Üt(EÇkÿEe¯é‹sÍXBÀ›óÎqðÙð™ÓçPT9ÿ¾ðê ‹s´‹ÈåÊ?D¿‘º&rñ="æ.R¤Kù1Åo¶®ÒpÇêÈÇ>pK¹c€µ_ý…K÷MÝÖ}c#wp-³0TR®"øeFIÑÃï’0‘X¢ R]ª9“zÇ›oXQp­Yqó¿ÙùzÎ:®0ÓÌ·´à7|©* 8 "â8S‚‡Ð’ŒŒ+þ¼¶½ò²ÙµËö´ü$¼¬fzÎkò¬ ·Ë žA‹ª*ù-/µÁ[{Ú°í}­óÚ¶Qc”Ë+ÎYÛå-lâÞ‘Öcýw"IøC`}üb‹`´Rg—+À ‡o”C»‘Æ"²ZÍ !b±$ú´X5#÷y)€w:VHŸ"Cº¶\vXs à·;î|ß7¾ÏTÐ \!–ÅíÀ@³åÏT·sQò•Óû &—R„ˆ3¬ëXÔy°üà œ‹–_ †u°|{ [$/J¸d¶ÁJ͘‰HÖ«heÒx¸ÀŠHc\ôƒšõy̓@Àq怅›ë1Ä쇾#?³º° %¸æ¼ú{.J¦¦‚³ùöàØßYjn’-MœoS÷ÚÏ7+[ÚžŒíŸuÀ*ÏWXá ³ôàùlý{nåV$—J%Ä6{ä³ã5ç’c¥LÞ<àŽ…Ï˜Ïžk!‚H@¯ €Û·³Šànc“&!h9Â*«â†¿yþO;Ö¶ûÎâ{twë:‹ÛŠå`rpØŒç`°ÿòàZw.øüãºv±ýmÛXê¤I®ŸCÁ{û¿lí¬(3ç†G±½Á±éÛ×*ƒÂ䱡ZÏç‚ïÄɪ2™5•Œh=<ÈäUyâ:w²Â`Ù‘ý£`q¦Ù‰fóï ¾+ëƒþÏn;Ú™%Mö®È«,·§'ÿ)ø{ñôîômØw·„ìCî³ãó•$à €’$4wÃßÈ‚›.ðƒÕ|Ïž=á¼v@q®DXz}»HÀà Ãʈ°Œ×LÿÙøOŸØ9š025 –j@8_±¦°,ûm˜•îÓñà !LàâP±Ê¿Ñ/ÍÇÐÖçÿ)8¶ßï €-Êó&Ö¼wç½hM&ÝÀß8뱡]ôkbÊ ;–›bŸ-}Ï6¤®¶ò³í_™Mûy,ËCîñÛn»ÍA^¤ë9`ÈÌŸIÀ‡ÅD~þ<Wìó ÖÀš:@`v6;ËN­Ym¹ûöÚ¯ÚZap~RƳS«V]€•l&WÅóbá×:q¢s÷^ô€zâ„›ZÐh,:v¬úûÓ¥³=}ÚŽOÓ2XËö7ß  ³ö’Ÿ‘Héì==Bç5¥Š[ ’ÍæÜ<¸¼œçˆ¤ä fÙìCS£ö¹*i‰Û=Ì~Úç?[ßMmÛ‰MºðI@ $¼6,€‘@Ì(á4lXÊ %ˆ¥cKÌöõªn奯àù$à„\$/¼ð‚ÛÇø!B"-òX¥¸1s›4iRLŒŒ›Θ¥î#Z耇EXðþašaÒQ4ÄÚHˆý‹õ@´þýØï•'íî¿´²Š2›?Ö ÁwnéÛ|¬ú»AÝg”WškÓLp¸ì™Ræ†,Sàí·ßî,z„¤€X_±Œºµ•ðYáHÆìkmû„òu V¸}ŸnÛ_{ÍÎååYÞÁƒ¶ë£ÅV^Õ ˜6;äO­ t]•Ë>ïÐAË 6Žç6;"D&$sAD¬%q|q]ºØ62+ĈI, ¾w‡{õºÀÚWbIÝØ<×ÍÁEUtAn|èÿ-Ÿç¹Ê3-€õÑ»Ùy«vÿ¦œ9®Æs8ûuXÓÚY [.zÓ²‹²tá“€c]èwíÚuÉ}q¡ÆbQ—¸)]õo¥k ±ÌEŠXK¬mXÎ<ôñý¯dò1·L:¢__ïàêˆß[Yå¾Ì|îMÜÂüeÂëùu¢ “pûú\šô ¸aÍó3â/°\ÇæÃ|`àk ûÂê v”‡C„C0 Š0`p‰KÀâ:Bמmƒ¶ö´žÚ»ê.hMò2k±ðµð:ö÷±‘;úÛgKÞµŒ‚€ž+¨‚ê¯0 øQ«ðÆšG^Q?ÑÁW2"¦™sDÜ%çƒÏ‡e¸‰ëktÖË¥K-±*ž;:l¨‹ÕK7Ö¢µBŸwÚœÙn\ÀÉxêØÁ¹€Ë‚1Åuêh©'¸åaÀ Æuryu VÂøîÝ\?¾ï¸Î-wcã»øÛ?àá¯ÏzÚÅŽ>·9jç@º­Oxÿý6wqË&쮋ž$ü¡XfîòÄXñ¤K,»ø¹¨áþð“/°H°®¿Ø±ïss¢Ñ'÷é´/ÜL¸IùâíìÃç΋6 8òæC?lÇÓµßÿûØ*nlôçAmyÍzüe[b{¸€û'tÞ‹UƒT^â»Ï:ZÒòXt°pÑbUò¹ñ[FÂK¹(7…-ª$-Í"ê'Ÿ\µÊâ‚ÏØ·s……õê‹.±ä4•$`#@‚È©B\ä<á“~ÈÃ݃ë‰@hf¯òc¦pƒ°·éZ°tdÜ\þÆ’À:ßrË-.6Š'dà7OÚÄÒp#ˆ€ìÇW#`?ÄR‘š¤ËÜŒ<2vrò?7+‘ü‚XK°lð$h’ІÿIÎ ü×SÓŠ# ¯gñ „u°>u¯Ã–¤à÷ʃ-Ú¤+ íǪ¯ýCЇ\Ž-šõZîãs5؉/$æÏ·úVòÇ©*+’°‘ OÍ_ŸZ…*XÖ€+.büÈ&Ê“ñcç¢ Ðá.Áýƒ hÄíE‚cĺ5A_¸ŽHgBÐt¬<€€#¹ ±Ú[ƒ› û!ÐÜ ÛúŠå¸›VUPÓn0ÆJÕޏ,b{Ûüˆ  $”$I’€7$ò”Up"Ar¬J @Û‹Äì>òûáî –¯®J ¸±â;DEŽXÈèc=æ€vXï8.¿€¾ž.¢?âŽüñûÄÉ‘ˆU‘˜Æ ¯D…g õaH’$ /"*Zà¦ÅŠèá ì¼5È"ñ0Áå€n`,säý"ùð€1ÜÈݺusÛ8õÊÔã%}PðáfƺH±_¿¦p9S«p#–À£Úû T§®.×Ä6ávöF°b’6ƒñ€XqûâÖ¾÷Þ{/H.- /UÛo¶¿ïô/x`ÙœºÎžÿ€ ÛÒ¯Öm™eJìT4ÑÏ•$î~½ï&+*½4—jRf¡]Q=C”ÿKËÞ-›_|ÎÞ¸Õ*®`RÖÒéöt§µ¶b÷…3C¶&dÛôõ©áוÁ~Î_Rߤ£«ËóÉ$“Òò’ð¬bI’€™Íæ«U[¸y)Ÿúº>,pœJ§qÃ">Y~Xó¸yù´þÒ'ÿŸÇúXqÃbUÄÅë×&ŸÄ9r?@%ãa{–“‚™‡>–ˆ}±c\Y‡ý0¬œäôÒà•ëßÚ¤á×¹%gì§]þ‹­<²ÄRÎÔž´–0…X3Øù^ú„Ï—£[?^ä@ëRt05×:LÙ~}<«è’á©>:@Ùþ”3D]~߀ݱSE6kÓ± /Ö…e–žS]dÎæã¶+1ç’úÞœöãuÌeH:“h­–¼kNî¹ÚýuR’$`£Àú Øb.Tþo(áâ%.Ð7¬‘ -+á¥Ì‚”€5Õg}g{tô]ö‹ž^öÌøíoÚý¯öȨ;mCòê¨ß=Ò´ðÛ!ŒôVl~KLtBL\¢:éYWàáˆ[Âb•'ø>µÓ†,‘ï@5áDtº#_Ýéà4,ib–›P”$I(”$`Ôõ § ="gHâd!ªðóŸÿÜÁ¡ Ä«ú4ĸb go%D¤Iò §½EÛÓ\üÚo?Zèí¨øãy6lQ¨¤a>93–°ÿï9V@nÙú`\Ж;¬z4àÎ`AÉ9»ç‹V˱¼õœyÐÅçÕÀ”“…öb ¡ý07iuR†*>’Œ¿ó´îïš}'mÝ“aœÀem8fy¢­Ý2üº( û{ íŠ˜)N¶m¶ºÙ…åÊê€|6Äô’$ úšžWKÄãIRcÀ‚³ùöÞ¬—ª/ŧí™/X~itë1«@î^Òñ{c¶=³èI¯DžM8á}òj2 pd&<•&ðÂìkÿVÝ¿?hƒ$8·'à…Õ kWŸÙ‡,îX®[œm>÷&ÂÚÖqê>¾ø°M_WíÝa9›¶.Å2¸Â‡¥à›¼&Ù¹\G¯ïð0³òJ¶¿Û-èúΉ³®3Úé‚Ðç…Û·õØ8áÞeÒãÆZÇ{«÷e:ÀD¸…TàëÝü­'ì`Õq„_ï3î0ûX°íDø5øíÄ=6pA¼–„b"ç'þxlßnÖ®êª ±DÚn뿱î뿵õ©¡tSÄ"3AMi¥$IØè $H¦F'ÿc%àæÂ…œÙ»œ4¬Ìðe¶/±.ÔÀܾ}»{ícýzôöÌvdæ-“Cø‹ë‹ Ë—/w5H¹¹1±aáIš}0C—`xÖÇÒáK0IÒµ€RãÒæø,•’$ €vq@ŒdÎÀ– ЉGbæ/¯ L }þ?ŠÁ“Îâpëää)ø7¿ùëãñÇw'‘\|@î.râÒ"øƒÄÓÞ¢ÁºþóŸbí R‚æ%I(]®’2 ê]ÝB’$à €¤L!‡U@pi{üÏ…“êXý2òîõë×Ï%eæ=¢é”`cý'žxÂY©âOz‹-\z \Pµ 9ëÓ¼’$”$I’€ €@çX혩âÆÅ"øÑG¹u©ÆÁlÄHdñ/$VÆõK… ¶ÅÅëKÅ¡‡~ØÅ;{ï¾û®«ßKº ,‹”xc?ÈsÂ;vìè¶!Ç }ÓnhIJ’$IÀ@Dm\À”nWà CÀ©,°ì <Ø ë´lÙ2\UƒÊÄöM˜0ÁA%.cD¿¬Çr,‹À%•F8`o½õ–Eß>ÖAL ?þøck(I7*CË Þ†–‹ñÍÜmñYb®“WzÆöfît-§¤ödÇ\úôéó}ÂC*ë¨B¥ÂKÈ ŠÒ‚ëQqððx¨C«¨gòöüøxÛ<È&qÉç¤$8†ÈíØgyqñŸëÔ‰­4#úw·óîFDu}­¯Žå¥ØÎ´-v²0#æ:ÅçŠlWúV;ž—ª‹…$ ë@n>F&òÿX±ÌE®Çÿ±.ð~¹ï»>ûªÏ8$àõ®-[¶8 zm"uɶ¶—ÖïÈl]ò ›¼w´m?±)ê:sâ¦Ù¢Ã³-.k¿å–†.ˆTAÛÓýâu™ÙÌ`,‘̺®™²<4úô6èÀWm­ 1Ñ–ü쟭¼¨¨Þ`[€\\”ãun\§*JKïO­Yc§V­ºâÏpÇÛoYqð½â–›Ý>¼”Ǭ·ÙºwèŒ`ý¼£fÉsª_Ù58€¿tû㨛¬¸,úùxsNSKÏ?aŸ/}ßÍV—$IX+^ª˜¨QÜOÆ’t# À4nÜ8B•ÉOÌ|'5 1¸XÕ°¬“†ITÑT˜fvtºÙâG[jVü, 5ÛÕ)ÔÒVFy ;_iÿ6øÿu–"Úk³žŽÚ÷„=#lÀ–î6l{_+,+°‚Ôà"ÚÞl᡾‹2ÌÕÛ?~¼›ìåãY·n›DÆ1P}‡x^²àMp©b&MrŸ5Å}9Èš˜4|˜U–—ÛúGvÛ¤ÍkGB?÷îí`*®CËX²Ø’FŒ°Üª B¥'O^€Gú÷³Ì`LüÍÝ»×Ξ>m»>üÀ}ºëá¶­vjõj;ܧw¨ìl‹ïÞÝ÷íkYÁ8ÃÄAƒÜxPêÄ QrwóæVÜ<ØÇþðwBï^V|¾[_~9¼n$Ÿá¶¯ÌfÝœßÁXöšÅ –o¯~Õlw—P¢j¸ÏN} *Üe´Wg>eóã¿·…¸Ï80Q I6,J’ðÊÅä§Hs <Ñf̘á–Q„Z×1Çû̶c6šÍ A_ÊœPË9xñ6•åö›A? a‚-=2Ïžò@Ô¾Óó[Fþ Ûvb£ ÞÚÓAÈÑi¨ü.”Ñ!Ëþm·ÝæÒ;EZ<`˜b&‹¡.]º„-€@ “ÁH9a匀5Øínþ‘•p•6k–ƒ¬µ>àà.uò$K¬*—WzöÿtØ0·~FUR욊\7}Þ\;¹²ºæsÎöív,ÞSÁ÷îLŒr•¥§N:°<1s¦åÇÅÅ<–HÄú—¶ÊlÎf{{†^Wž @ú‰¶ •©óZtx޳Fý¼ N؇ šÙ×+>±yq3lüžaº`H’°nä¢ìŸÄRôùCÄ0IÀÆ€„RDŠIP€²˜…°¤‘º¤–¸’l³õïW¿ÎXov|I¨Å*köæì¦–r樃»!ÛB±{…g là–jÿnvÑ)+>Wl§ƒôÝÔÉɚ׃õŽ›íéºfP»8==Ý¥mòHPˆuÓ×õæX±²œ‹kÓ¦MÝÿÄýnÞ¼¹^¸¡¶÷³VVì“ø¼õ?dç°ÄE\YuÎΞζß|í¬vXåuhïÞ/ÍH·ãÓ¦Úé8zõtïc­‹ 5‰@àïø÷߇ûAÛ}g}ÄYôbiÛ¶nœµ©¦ xo+gG Cá¡€ßRç`Wõ5Ÿ?ú¬ýÚYÿ**C•GRs“mê¾±îÿò€÷{+*+´¡ÁgJŒ§$IÀZ*›×Ãs¹ÂÅà L’€jUø2&ZJLÈÎUä’˜\AuXåÆ°Ì•—\Ú¾qéöÚØÞ¹Š8Qâ®õÒÂëìËÜiÖ´¶^:¸÷Pe÷Tñ‡6ÜÔ¸­gÍšå–‘€I;vìWâØºvíêÞã8ø (âòNJJª7l©UÍPqÐ?nÚ£C‡Zyaaøa6eü8çÎe"GABBv=-yÔ(LÄ8µn­Åuéì&šD`æòeáÿ½Û÷pß>2Ýu3€U\ŵ)}þ|;QeÁ­/r>å¯âÂÏÕµªá}·ê³à3ë`½7vtpŽâ²X Õ–ä£9‡Ýzsã¦+†Z’€µ pöÐC¹¿Ì¼%Á33sBR·p!§)U;HÛB07u–“Î…‹ñ<€q+W®t3…¹ØSÅã믿v3~kŠý±}“cj!#GŽt9çT]”¹ñ 6,<³˜²XÄ‘’†%öC‚in˜Ü,XÕåJO\˜ì3 \kÊZ¿ÎuêX q‰‰–LBüªVœ–fé د¾ c4‘8îܹ.ª$IÀ }"h€ï¥—^r¯IǼ‘·§sŽ‹ÐçƒÑ½µð¹çžsëj»vírÐHI7,\äbà'Ÿ|â‚ÃÉ H`îèÑ£ÎÕÕ¬Y3wÀ¢@îÁ×_Ýï³Ï>sÛªX Ø–†¸¸y{4¤*HJRCŠØB? $–H[S©I66ô€L»G¾>þ§ãú””~Co¼ñ†¹\~Ì\¼ë®»\ÜîÜB±úûÓ7âÌ€$ÿÖ;ƃÕ>y¨d¹Ï3öÔSO¹ñ/Wͬ£òëËí!”$I’$`0=ýôÓÎÒW793£¯>ˆuðç?ÿ¹;Ñô…;—„ÑÑw/ñ?Ìr¤ÿšxúôi7s«õ‡U€C‡uÖAÜÑq[3ëP^ eä§éÃ$I6^DÌÔÃÕï^OÇÌ]f"¨Øf¤£ð³“““Ý_BS5„ o ‘¿Ë–-³œœçÆÅÍKÀsMÑ7–½õë×»í™Iˆ ŵ‹€N¬„ô…#òûØ´i“Ë/æ’u%à­]iÛí:ÿ§ðk¾³Ý×´³‡FÞa}×w©u[~KGªrÙÕýøßàå¨I‡5VXziîÇÃiù6p~õTáa‹[ÉÙò?gyEeöJ¯V^qù©§ö$åXÇ©ûmÑö á{ÃÁS6quRøuV^©Ûߥ¨ ¥:×^,ÈKµ)ûÆØÊ£K³p%I6jü¡…•g¾1aäJD9¹×^{ÍYÿˆ$~K£$ü¡Ålع©vôôáð²™{ì'ÿƒíIß³B”HƒÂD*l°hcåf™OùÂñ¯XÂyÐb~êJ¼žSpÖ2rJì– -¿øœ=Waé9Å–y¦$&pUTžwëO¦ìöuÞõA‹„¶ôÓÅ–[Xæö€CÖÉ­,þÒÒ‚õØ/b ô_j•Ô–œsÛœd½f²ì¼Ò‹¶ÁÚöËwç9È:˜šëÐköæj@bybQ´OFì°]œ½Þw“Z–hßNÜë^‰ÇN¹ýþñ‹P½Y¹}ÖŸ«`üñ<·½û ÷Ÿtc°*É6 ?àíïHsË;O;`뫎1½Òî?¯Úu>tÑaÛŸ~=}}J¨™ôn³ÙÑ-´ä7<8Älõk fÅþì÷ŸÜmV·¶òÊrÝQ%IØøpåÁSQo¶«‚åÛŽ_ ‰½ÆÌž¤$jÀz `GŠ%ܽĪ€ä³dpø‹_ü À÷׿þÕ­ÏÿĸrŒÄ¼F†8 8ÐÅÞ¢U{3mØâ#v,«È~õÞ|bñ'ò\Ö4bú.èN:9K9V±;>]bGÒòmÁ¶õ@öñF€@XRf³ÀyÄxO€,;’ë¬~í&íµm ÙàÉܯˆëvÀüx[½/³ CäõÇÖË~5~;ŽQKË;¸ÿ¼¸ ÜÉ5púúT›´&Ém˱ž+¯´f½6¹±ÇÒÖ/Ìöö2«8ûsßzbƒ½9ç9KÍM²Ü’œ0¤“ÆŠ¿’$ >ñüëQo¶O½ô¶m=vö¢åo·üÆÆ/Ú" Q»!°¤¬Øúoè~Mi.&•ņodbIÕ%fÉcÄèCÈ¥ùå—_:w0®cªlP˜å^$k÷Õ3 °¸ÍÜxÌnÓÏGï´v“÷†cÞè‹Ùó^¬÷Åè]6ëqg…óŠ;žW}~ƒå€mÉδ*+±Oœ°2´ÿ…@+Hc–uËÖÛ}4d›[‘䬌Ãr蜵 úÁuŒö&Ÿq€éŽoy¢[ÉŽtûrìnµ©§BU>è¯ßœxë:£:³ð¹5¡ÚâGÜaÏ™­íøÝ.ŽqnJÎÆ=9_’I{GZ ß¹¶!eU¬ÉV r—’$lt¸xç1ûÕoçþ>üäó6|æj{õÃ/lUð4þ»»î³kØ×=GX·áÓìÃ6­ÓàI1ðë^#¬÷˜9ömŸÑÖ¦Û4e©½ñq[qïózmÂ{òÅ7Ýû/½ÛÒ–íM·ÍÉÅö»»ÿÀb¾}òmOë5zVŸÓ?j@鲄µQ â%I(Œ€ëçÚ‹o}ìbþFÎ^kÃf®r¸%µÔþç?ÿ‹­ØÒúŽ›g¯¼×*&6}õ}·>¸Bpáö”6mf³ÖÇ}Ìu¯—ïË öuÖþúÆGî5–ÂE;SmåSvótËîúÓ#öF”ª© %I’$` À÷ë»ÍÚõç\¿wÐuç}ÛÆ¤B{ñOìé—߱ךiwÝÿ¨½ýé·ö³_ý6&Ž¿É~ùÛ[íßnÿƒÈš¸9¥ØþøÐØ=êúÝ€¢À¹›ŽØ/~s³½ðöÇÁ¾qËÚ÷kÏ6{Oà£&”$I’€ €ÜT±ØùÙ¾„ÿç/.Yþ²œe>Áï׸ ù¾6§”Ôz÷}E¶Yâ­ÇÈï­ÿÄ…ö—g›ÙŠý™4¾b“–íø¨Ý°È…ˆœ -*YôÝÔÉFîs#ÙqáIN?’³wèÐ!æû$l¯k¢ÄôéÓmåÊ•n–´Ûÿ€Vo›Ÿ}ÆÊëyJ23-uÒ$;Ü·ï%Ÿ“’à ¿.Íʲ’ŒŒ+>ׇڷ·¢ädÛöÊ+VYUõˆ H;w¾¤~žcß­úÌ6‹ý½Ï(>‡5­mé‘yºãK’°n¼œ6}Í~û²ë  Ú•ÜØ±~×w´µî2ȹ£™@‚+ZУv½ ia€J"ú40[¶l±;v8Xb¶°ÏȲ˜à’e¶áƒê×'·@¶2Ôò£oÓvE ‹;µß–'.´Ù‡¦D]gæÁIÁ9ßh§Š2­ô\‰FGú;R¦ð?d쌙ü†_|ñ…ÛŽÏ”¼#U ±wÌXnÓ¦++yðàA· `$ÍMdð±cÇÚ©SÕ©t~÷­§°%?ûg«(-µâ'±|…IGí< æKJìôÖ­–»o_ø½Ò“'-®S§p?e¹¹–½q£åîÞmç«’ÇvÙÁóãB°™»wŽ£¨ªÄeÙ™;:t¨%d9AÿgÏZñ±êüƒE©©1!’~hùqqnÙ®>°Òà¸VÝ~[xŒ$úæœ w.+Í–=cV^úŸ– íÈĪ×Á:«’–Xiy‰=:áNË?›wѾÉMøìÔ]5š.ë¾²c¹ÉºëK’0:®M.³]içÔÔMk·öúÀW^yÅ/^ì,|¤éÖ­›+G-ìE‹9œ9s¦Å +ϰ»›ÙüûB¥ÌÊòð[m–:?ÔΊnýûÕ€¿uV¢õ)+­é´£ö=qïHkµä]k¾ðu;U˜éúZÚÄlöm¬mÚ4WÞ«!Ë£౩!0ÝþÆë!ëÖ’Å–ô %ôêåpKÓgHL³´¹s,38—Ñp_0®²œ;>c† ´(¹MO?e•ee–SuS(Æ‚•1¡G÷KÝñÓ-}á+Î=û:2 ¿Nþ?6mjÔsFŸô|–¹U5Í“Gv๧å'áõ"H_ôÙ÷¿ ×ôàk½­­ÙògÍËW¾ô[Q yŒ¿ÝŠÏE±þ°æ¿bÝ×g‹ϵñ{†ë®/IÀèxµJÁIRCéÇ.×êQ^Ô&É3máÂ…nu´ScX›ìœ`–½'”Èx[Àk^ µ£Ó£@ÊùJûõ€ÿn YíЩ}öâôG£ö]TVè¬IÀ_ïœ*en6 Yÿn½õVªÇ µ¹Çï\œ{÷†*~+/??¿ÊâuÞÁÞèŒ:vìh›7oŽ €Y¸ÛúòKV˜˜h™K—ÚÙ¬,[sï-18_ᱩS£àáÞ½Âÿ'béÁ9=µfMÔ}$ô¬þLÒç͵“+W„_0Ë~RƳ‚#‡cŽ3yô¨`|KjýìkZOm ð³ýýª-‚Kž0[ü˜Ù¹ï÷m½Âeèjêdaº½3ïÛºÚV]d“÷ÖC’€—€X$HX[—°PÔ*Wš5##Æî’æ&&&Æ\JÏž=ï¹± 0 ÎZÇv7ÖËn¹þýûÛÖ­[«K^®úõëwÁk¾Ë»wïvñrüî ¡±&'Çvç•å™ñ»8\ÀâB­ädômznho«’–Ú´ýãlé‘ùnYZÞ1{{î_Ãë¬:ºÄŽœŽ·e‰ lÌ®!LâFš ®Ç…~oï¿ÿ¾³øñ{òˆUhÅ:ÈøÑˆ#\2k–çååÙ_þò÷?ÕIê €nŒ”mõUçÖÅÚ¶ýÕf–ìw+@ˆÎpµçãÎM[\‹¶oç\½éX —-³‚à÷¿û£­(%ÅN­^å,zÑ×2±„Åܲ/wí^o{åå°+¹¦N­]cqøæÇÇYarRÌô3‘ˆŽ-Æ]ö| CaƺÔçV±f§5m¬ÏÆN¶+}›•œ+ÝÔÒ¶ZËÅo¹ÿ±ì¾:óI»ÙnÕç––\ IÆ@÷*À«,¸ÐŸsäÈX¢5¸àò>ÀÃÍÉ»qpÝS¸r(†Nûøã]ì¿ãÚÊÉɹø¦ì¸ã&€ ̯C¸À>ùä·Œ±1.öÍúÀ%ñRükìå—_vã!°šõ¸Qúcá@Èr^ãŽV tј"ûa¿¬Ï~éÏï“›Ø[o½åÎëûãd?ÜØüvœ·º@Tº±Зs‹üþM˜0ÁUúð?øMœ8ÑYÕúû4n÷P›Wm"*Æ WmQʰ~›»Øä½cbÖ¾å÷Mœ"“p#þçxÌÎUèàx¨D⼸S/>>þ0eïj{€Ä›½¹º¤€—2~¼›2Źmà ¶f%îbϿˤ‘#üyØÃmŒK÷tÕ˜½r«€5lœ;×’‚‡Ñò*üéV BÉà7O,¡oÄ âff,´ŒÜ£ Ÿ#ç&ë£ {†ÛØà3ãs;SUŠ.=€¼ù 3/°ÚöÞØÑv(I’°V$X—€óâ‹/º§ùÏ>ûÌÁ_Ë–-ÃHÝÒaƹ×÷Þ{¯ƒ$ê“L@7¨uëÖÙ3Ï<ã É×.%ð›mk ʰzp³ *KÊeð2ÄV¯^íê¨U¸•ˆb[\KÜLØ7V“O?ýÔ]Tï¼óNޏŸØæøƒ‹§âæBÀ=±H±0²@–cÅu…kïÏ™3Ç 7½mÛ¶…/âX1¸QcÑÀzÃ8k Ü—€ÒÅ¿?®1³gϾ&ÇînÑ<üšI)ÁµÅ·Ò* d}“ë’$IÀ ±Ä5oÞÜnžÊyݽ{wkÚ´©³R€Àྪ™v¿ýío Îk¾,`ò¹çžsV1Þ€š4iÕøî»ïº}ž4À ðXÀÁƒ;|ï½÷Ü>°0°ìÙgŸu h+V¬pc¹ýöÛmÖ¬YyU£ß>}úØë¯¿}?ˆíqÑ}ðÁ`o¹ågZ#º­‘ÈöŸþ¹;—X% t}ˆÉ‘îbI’€Ö;€wî÷ß;\žÞˆ¥Ä~ýë_‡-‚¸kHSáK`†ek MXq›âÆe¦`BBBØET‰3‰»cðÈ>H)áõ<bÅä˜+PöÑG9xe?u ýàÆ½ãŽ;ܱõíÛ×Å`=òÈ#ÎUå]À‘È~'OžìöÅñà#Àwž$”$I’€×$â½çž{ðMï¼óŽ+w®0à 71 æ!î©§žr9À ¬`X¾°š!,aXõ¢‰ØŸ‡~ØYóˆÆRwÛm·¹õ±²VX#‰'jÕª•Û¿"nð±Ç³Ö­[;ÃJÉz€Øƒ>è,ŠX,±>ªË—/wAçôùè£Ú¤I“\?5ÙVÐçŸÞö Ó` PwŒW/Ç€µpêÔ©6cÆ g}òÉ'],¥$”$I’€×$Ö”ŸôP3€ËVyD 5ïÑÖÃýK0ž±Ö‘ðÕ·„ª jÞc]\¥K–,±)S¦Ôkì±Æm/@ÏrÖÇÈ1Ñ"ƒìY'rmŸ5Ïßo¥\E74NÜ5ÊNdþhûç¡GßAI’$à%`CK1s×+®Uß|Ú/¬~ܰ®4}Ì¥ˆ˜ÇÈ1Ñ®æþ¥Æ €ùé¶(~Ž}¿o’µ[þ…«q0sŸuZÙÖFobgËÏÚìÓìç=þÆÚ,navްܒ°±»œ-°…q³mÆn+ þg=^·üs÷z®‘¶4a¾uXñ¥KÉM~&>h?Ɉ°^ûYÅ„,`]'$Áçá“$I ¯ JÒõ€;OlµèüŸìÍÏYϵ¸ýº×ÿc=Ö´·[ûý£-ˆ›åñÿn÷¿X—U_;ÈK:}Äþ{‡oéù'ìMmÜÎáH¦ÙßwúöÒäÇ­ïú.8–Ú¯zýk1÷ »sЯ¬×ÚŽQ÷ÜZáS$1áŠ8[Ò¬«Ë v,ê„ Z¡‡I’$ P6ÞÕ4ÐþŒ=öwíÿwûãÐ Àí—Î2ˆþ¦ýÿf‡N†,raÌ»¾ŠÊê°^Ÿ.ζ6‹?¶o–µŠ €Xü¼HKĤ(Ò}ýõ×.Q9zúé§/$I / ™pñæ›oÖ¹q}ä$ïŸ/ùt¹ÂeÌÄâòj›Hµƒ8C/âïHSWì®3fG3¡ÉÓEÂÚD¼"f¤ïò›ðësçìá‘wثӞ±wf¾` §BÅw›NxÈžs¯sSJí'þƒ5wŸÝÔûïÃxSïŸ\Ð7˜`Û%µ ßÏÈ 97™}Î$oñ#uî_fñK’$ € ¹õ-à‰Ô)@œO CZŸw©^˜= @YÓ§Ow³d9±¤^a9³lIäꓹ↢Ÿš¢_f Sò“@ɤ‰d¶-qyÌf¿€%é|¥ŽY·ÌVR33~IÜÌä ÆÄ1cPd?ŒP•†½ÜLW®\imÛ¶uû%-ÇJªŽ×î]wÝekÖ¬q3¨é›dÚ¾êtý`nÉ[Ÿ¼ªÆ²›¾w‚-ŽŸëb.Ý©{ƹ˜>´åØïw8+Ύ妸ØÁU‰&H_™¸ÄÊ‚í±Æ<uÿL¨ò±µ^Ôáå{éót"¾‹, %I’€À‹x" Ö,ª_ÊÜdÞ~ûmpöêÕËéXþéŸþÉA‡Åœw5AS9ƒ÷€¶H‹…)YˆUâ¦Ýÿýn}À‹”+‘yI?CšŽ Ø" 7BàÐç$àÝç¤?R½pL=zôpAǺ÷á‡Æ@n¤@$_Î"ÞŠsð /¸s˜èïî»ïvqW_€—T8Òõ €’$I’ðº@ é›o¾q¹÷€‹äÇÄQXÀ-Z8K!ºùæ›/*ÙTÉ  bŒˆNn½²ªë~{,{XÒêJMîA> ö-4Ö¿… †ûæ8¨´²ßú ã@¸Ç• sNèë¥@ð9VƯÄÏ@I’$IØ(WD 9•.€, Ç§šð.`\´ p/’*ó>ë_¤@Ö¥¬{¤¢ð¥Õjªÿþn}þÒ'.+/à^bU¤_\¶Ô ® €”ÇÉúÄBQf $FŠÙ‘¤¨‰€ÐÓ?nhÿ>ÇÈØ¨‚û˜d,X,©DBÒkÆ‹ë™qI@I’$IØ(<|óçÏÇañ­Ë ¬\>†pÂÕêgâ¾e=ÖÁm‹[–m(rNÜÜwß}ç\·ÑÄr&RnˆÄÌÔ&~;ʬáâÅ*Hœ"Ë4–±ÆAÌ!ÇKŒë1~@}…ãÇwûáøø€d7ú‰& ‘õ|ì”ϵâ÷_"ú`ßÀË9*î.”$I’€á2õ3m*JÈù†;9RX)íF<âÕÀ9&]$ $I’$^¦"-Xìp›F¶Haݾj+íÖÐÂJXsLWsÿ’P’$I^w(I@I’$IØHÔ'äÀ«KÌfò3~¯´=e®˜ËäjŸÆqƒ‘3‘±à1I£®ý§ÈL`I(I’$ €Qäg×%\¾¸QÉ»w¥ ‘ý,`ßgÌc8}Ú¥‰@>¸º\¹±ÒÀH@I’$IxC –6Ò©`5# 3€I Í2’?{ÄÂ×±cG7›¶M›6nf-é_¨ÀÑ­[7—™¾HÂL\j“"fË’?¯¦˜uÌìYBS±€t+€ia|%*o0ó˜q1‘„ýr|Ìä¥Êïû<€Œ™Å<ð€«>Ò¡C÷>ýbId<ä8ÄB 9VŽ›$Ðäö“€’$I’ðº@ܨäÊcÀ”a$MnÖ¬™+uRÒ~ýë_;Êx pÕLM2i€Š*¸†kŠüyX÷¨ÖAc_¤R2kæY‡t0,#_ nbÊÂy>}@à·5¯qaÓ¨Q hR׸6 ûæX% $I’$¼nQõ€"¯ G]_/oÄRG’f ê¿ø…³¤Q‹×»]k yö¨ºÑ¼yó¨û=üÑè›í±$Ö•Ú `­È‚ \l ¯qcÑ^É#øÎ;ïÔ €ô§t0@I’$IxÝ ÉŽï½÷^>@9üpï[±¶m­[·vn_âó«V­Z¹’o;wîìܯÀýÄšÄAµ À޾YëßwÞé:×€XYÆ~ÈßW±j²=ù±fRÑðcl>ø +ç q?þøã®^0NI(I’$ o˜I Äd5'SϸŠ\÷oÍõ˜ Œuëÿ³eæ|2–?údb »víZ¯±³?`•ü±ÄØ"-y>/!Û°=@9&Ú•Î^–€’$I’°Ñ`Cˆ Xó·Üù†E.RÀWŸ>}^-Q¶.rL´«¹I(I’$ ¯;”$ $I’$J’P’$I6d‚ibêiVHÃú—âJÅMܳgO—v†Ü‚’$”$I’€?02ù‚x8&FlܸѥW!e 3b3? ˜ ôÍ `&‚°>³o™ ¸%$$¸öä“Oº<|Û¶msûaÒéejŠÜ|Ä 2s¸S§Nn[öí×e©©©n2 û.ÃæÍ›Ý,^&©¬yÅŠ.!“I˜≠َñ1^ÒÛÌš5˃I!'Nt3é‹1Œ5ÊÅ(ÖU=DJ’$IÀë©üAòefâ’¶¨bB3H󒜜ì¼4€ð¶Ûnsð×½{w[½{÷vi`HÅÒ¤IsmÛ¶uû¡²ÀUSß}÷%%%¹*! ¨Éøõ×_‡ß?räˆ}úé§n,íÚµsc}íµ×è‘çx`ÿå_þÅ“”4¬KþA^7mÚÔŽ?îÆ (2&òäüóŸÿìê6Ì¥–‘€’$I’ð†@,_€Ö>*e`A¶Fmß~û­+å’SØB7ß|³ƒ& Ì«f"húš7ož=öØcQ+S‘ƒT,¸Œ#XÃÚG^B`ˆc,T:}•¶Ú×›nºÉYú€CÖ%ß G¡€æO|XMMMMMMMMí[qqqã@I’$I’$©á$”$I’$IJ’$I’$I׳þótjglç^IEND®B`‚yt-project-yt-f043ac8/doc/source/analyzing/_static/000077500000000000000000000000001510711153200223415ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/analyzing/_static/axes.c000066400000000000000000000010211510711153200234370ustar00rootroot00000000000000#include "axes.h" void calculate_axes(ParticleCollection *part, double *ax1, double *ax2, double *ax3) { int i; for (i = 0; i < part->npart; i++) { if (ax1[0] > part->xpos[i]) ax1[0] = part->xpos[i]; if (ax2[0] > part->ypos[i]) ax2[0] = part->ypos[i]; if (ax3[0] > part->zpos[i]) ax3[0] = part->zpos[i]; if (ax1[1] < part->xpos[i]) ax1[1] = part->xpos[i]; if (ax2[1] < part->ypos[i]) ax2[1] = part->ypos[i]; if (ax3[1] < part->zpos[i]) ax3[1] = part->zpos[i]; } } yt-project-yt-f043ac8/doc/source/analyzing/_static/axes.h000066400000000000000000000003521510711153200234520ustar00rootroot00000000000000typedef struct structParticleCollection { long npart; double *xpos; double *ypos; double *zpos; } ParticleCollection; void calculate_axes(ParticleCollection *part, double *ax1, double *ax2, double *ax3); yt-project-yt-f043ac8/doc/source/analyzing/_static/axes_calculator.pyx000066400000000000000000000020601510711153200262520ustar00rootroot00000000000000import numpy as np cimport numpy as np cdef extern from "axes.h": ctypedef struct ParticleCollection: long npart double *xpos double *ypos double *zpos void calculate_axes(ParticleCollection *part, double *ax1, double *ax2, double *ax3) def examine_axes(np.ndarray[np.float64_t, ndim=1] xpos, np.ndarray[np.float64_t, ndim=1] ypos, np.ndarray[np.float64_t, ndim=1] zpos): cdef double ax1[3] cdef double ax2[3] cdef double ax3[3] cdef ParticleCollection particles cdef int i particles.npart = len(xpos) particles.xpos = xpos.data particles.ypos = ypos.data particles.zpos = zpos.data for i in range(particles.npart): particles.xpos[i] = xpos[i] particles.ypos[i] = ypos[i] particles.zpos[i] = zpos[i] calculate_axes(&particles, ax1, ax2, ax3) return ( (ax1[0], ax1[1], ax1[2]), (ax2[0], ax2[1], ax2[2]), (ax3[0], ax3[1], ax3[2]) ) yt-project-yt-f043ac8/doc/source/analyzing/_static/axes_calculator_setup.txt000066400000000000000000000011271510711153200274740ustar00rootroot00000000000000NAME = "axes_calculator" EXT_SOURCES = ["axes.c"] EXT_LIBRARIES = [] EXT_LIBRARY_DIRS = [] EXT_INCLUDE_DIRS = [] DEFINES = [] from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext ext_modules = [Extension(NAME, [NAME+".pyx"] + EXT_SOURCES, libraries = EXT_LIBRARIES, library_dirs = EXT_LIBRARY_DIRS, include_dirs = EXT_INCLUDE_DIRS, define_macros = DEFINES) ] setup( name = NAME, cmdclass = {'build_ext': build_ext}, ext_modules = ext_modules ) yt-project-yt-f043ac8/doc/source/analyzing/astropy_integrations.rst000066400000000000000000000050451510711153200257400ustar00rootroot00000000000000.. _astropy-integrations: AstroPy Integrations ==================== yt enables a number of integrations with the AstroPy package. These are listed below, but more detailed descriptions are given at the given documentation links. Round-Trip Unit Conversions Between yt and AstroPy -------------------------------------------------- AstroPy has a `symbolic units implementation `_ similar to that in yt. For this reason, we have implemented "round-trip" conversions between :class:`~yt.units.yt_array.YTArray` objects and AstroPy's :class:`~astropy.units.Quantity` objects. These are implemented in the :meth:`~yt.units.yt_array.YTArray.from_astropy` and :meth:`~yt.units.yt_array.YTArray.to_astropy` methods. FITS Image File Reading and Writing ----------------------------------- Reading and writing FITS files is supported in yt using `AstroPy's FITS file handling. `_ yt has basic support for reading two and three-dimensional image data from FITS files. Some limited ability to parse certain types of data (e.g., spectral cubes, images with sky coordinates, images written using the :class:`~yt.visualization.fits_image.FITSImageData` class described below) is possible. See :ref:`loading-fits-data` for more information. Fixed-resolution two-dimensional images generated from datasets using yt (such as slices or projections) and fixed-resolution three-dimensional grids can be written to FITS files using yt's :class:`~yt.visualization.fits_image.FITSImageData` class and its subclasses. Multiple images can be combined into a single file, operations can be performed on the images and their coordinates, etc. See :doc:`../visualizing/FITSImageData` for more information. Converting Field Container and 1D Profile Data to AstroPy Tables ---------------------------------------------------------------- Data in field containers, such as spheres, rectangular regions, rays, cylinders, etc., are represented as 1D YTArrays. A set of these arrays can then be exported to an `AstroPy Table `_ object, specifically a `QTable `_. ``QTable`` is unit-aware, and can be manipulated in a number of ways and written to disk in several formats, including ASCII text or FITS files. Similarly, 1D profile objects can also be exported to AstroPy ``QTable``, optionally writing all of the profile bins or only the ones which are used. For more details, see :ref:`profile-astropy-export`. yt-project-yt-f043ac8/doc/source/analyzing/domain_analysis/000077500000000000000000000000001510711153200240655ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/analyzing/domain_analysis/XrayEmissionFields.ipynb000066400000000000000000000220171510711153200307130ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# X-ray Emission Fields" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Note: If you came here trying to figure out how to create simulated X-ray photons and observations,\n", " you should go [here](http://hea-www.cfa.harvard.edu/~jzuhone/pyxsim/) instead." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This functionality provides the ability to create metallicity-dependent X-ray luminosity, emissivity, and photon emissivity fields for a given photon energy range. This works by interpolating from emission tables created from the photoionization code [Cloudy](https://www.nublado.org/) or the collisional ionization database [AtomDB](http://www.atomdb.org). These can be downloaded from https://yt-project.org/data from the command line like so:\n", "\n", "`# Put the data in a directory you specify` \n", "`yt download cloudy_emissivity_v2.h5 /path/to/data`\n", "\n", "`# Put the data in the location set by \"supp_data_dir\"` \n", "`yt download apec_emissivity_v3.h5 supp_data_dir`\n", "\n", "The data path can be a directory on disk, or it can be \"supp_data_dir\", which will download the data to the directory specified by the `\"supp_data_dir\"` yt configuration entry. It is easiest to put these files in the directory from which you will be running yt or `\"supp_data_dir\"`, but see the note below about putting them in alternate locations." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Emission fields can be made for any energy interval between 0.1 keV and 100 keV, and will always be created for luminosity $(\\rm{erg~s^{-1}})$, emissivity $\\rm{(erg~s^{-1}~cm^{-3})}$, and photon emissivity $\\rm{(photons~s^{-1}~cm^{-3})}$. The only required arguments are the\n", "dataset object, and the minimum and maximum energies of the energy band. However, typically one needs to decide what will be used for the metallicity. This can either be a floating-point value representing a spatially constant metallicity, or a prescription for a metallicity field, e.g. `(\"gas\", \"metallicity\")`. For this first example, where the dataset has no metallicity field, we'll just assume $Z = 0.3~Z_\\odot$ everywhere:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import yt\n", "\n", "ds = yt.load(\n", " \"GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150\", default_species_fields=\"ionized\"\n", ")\n", "\n", "xray_fields = yt.add_xray_emissivity_field(\n", " ds, 0.5, 7.0, table_type=\"apec\", metallicity=0.3\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Note: If you place the HDF5 emissivity tables in a location other than the current working directory or the location \n", " specified by the \"supp_data_dir\" configuration value, you will need to specify it in the call to \n", " `add_xray_emissivity_field`: \n", " `xray_fields = yt.add_xray_emissivity_field(ds, 0.5, 7.0, data_dir=\"/path/to/data\", table_type='apec', metallicity=0.3)`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Having made the fields, one can see which fields were made:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(xray_fields)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The luminosity field is useful for summing up in regions like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sp = ds.sphere(\"c\", (2.0, \"Mpc\"))\n", "print(sp.quantities.total_quantity((\"gas\", \"xray_luminosity_0.5_7.0_keV\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whereas the emissivity fields may be useful in derived fields or for plotting:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc = yt.SlicePlot(\n", " ds,\n", " \"z\",\n", " [\n", " (\"gas\", \"xray_emissivity_0.5_7.0_keV\"),\n", " (\"gas\", \"xray_photon_emissivity_0.5_7.0_keV\"),\n", " ],\n", " width=(0.75, \"Mpc\"),\n", ")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The emissivity and the luminosity fields take the values one would see in the frame of the source. However, if one wishes to make projections of the X-ray emission from a cosmologically distant object, the energy band will be redshifted. For this case, one can supply a `redshift` parameter and a `Cosmology` object (either from the dataset or one made on your own) to compute X-ray intensity fields along with the emissivity and luminosity fields.\n", "\n", "This example shows how to do that, Where we also use a spatially dependent metallicity field and the Cloudy tables instead of the APEC tables we used previously:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ds2 = yt.load(\"D9p_500/10MpcBox_HartGal_csf_a0.500.d\", default_species_fields=\"ionized\")\n", "\n", "# In this case, use the redshift and cosmology from the dataset,\n", "# but in theory you could put in something different\n", "xray_fields2 = yt.add_xray_emissivity_field(\n", " ds2,\n", " 0.5,\n", " 2.0,\n", " redshift=ds2.current_redshift,\n", " cosmology=ds2.cosmology,\n", " metallicity=(\"gas\", \"metallicity\"),\n", " table_type=\"cloudy\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, one can see that two new fields have been added, corresponding to X-ray intensity / surface brightness when projected:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(xray_fields2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note also that the energy range now corresponds to the *observer* frame, whereas in the source frame the energy range is between `emin*(1+redshift)` and `emax*(1+redshift)`. Let's zoom in on a galaxy and make a projection of the energy intensity field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds2, \"x\", (\"gas\", \"xray_intensity_0.5_2.0_keV\"), center=\"max\", width=(40, \"kpc\")\n", ")\n", "prj.set_zlim(\"xray_intensity_0.5_2.0_keV\", 1.0e-32, 5.0e-24)\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Warning: The X-ray fields depend on the number density of hydrogen atoms, given by the yt field\n", " `H_nuclei_density`. In the case of the APEC model, this assumes that all of the hydrogen in your\n", " dataset is ionized, whereas in the Cloudy model the ionization level is taken into account. If \n", " this field is not defined (either in the dataset or by the user), it will be constructed using\n", " abundance information from your dataset. Finally, if your dataset contains no abundance information,\n", " a primordial hydrogen mass fraction (X = 0.76) will be assumed." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, if you want to place the source at a local, non-cosmological distance, you can forego the `redshift` and `cosmology` arguments and supply a `dist` argument instead, which is either a `(value, unit)` tuple or a `YTQuantity`. Note that here the redshift is assumed to be zero. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xray_fields3 = yt.add_xray_emissivity_field(\n", " ds2,\n", " 0.5,\n", " 2.0,\n", " dist=(1.0, \"Mpc\"),\n", " metallicity=(\"gas\", \"metallicity\"),\n", " table_type=\"cloudy\",\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds2,\n", " \"x\",\n", " (\"gas\", \"xray_photon_intensity_0.5_2.0_keV\"),\n", " center=\"max\",\n", " width=(40, \"kpc\"),\n", ")\n", "prj.set_zlim(\"xray_photon_intensity_0.5_2.0_keV\", 1.0e-24, 5.0e-16)\n", "prj.show()" ] } ], "metadata": { "anaconda-cloud": {}, "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/analyzing/domain_analysis/clump_finding.rst000066400000000000000000000171061510711153200274420ustar00rootroot00000000000000.. _clump_finding: Clump Finding ============= The clump finder uses a contouring algorithm to identified topologically disconnected structures within a dataset. This works by first creating a single contour over the full range of the contouring field, then continually increasing the lower value of the contour until it reaches the maximum value of the field. As disconnected structures are identified as separate contours, the routine continues recursively through each object, creating a hierarchy of clumps. Individual clumps can be kept or removed from the hierarchy based on the result of user-specified functions, such as checking for gravitational boundedness. A sample recipe can be found in :ref:`cookbook-find_clumps`. Setting up the Clump Finder --------------------------- The clump finder requires a data object (see :ref:`data-objects`) and a field over which the contouring is to be performed. The data object is then used to create the initial :class:`~yt.data_objects.level_sets.clump_handling.Clump` object that acts as the base for clump finding. .. code:: python import yt from yt.data_objects.level_sets.api import * ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") data_source = ds.disk([0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8, "kpc"), (1, "kpc")) master_clump = Clump(data_source, ("gas", "density")) Clump Validators ---------------- At this point, every isolated contour will be considered a clump, whether this is physical or not. Validator functions can be added to determine if an individual contour should be considered a real clump. These functions are specified with the :func:`~yt.data_objects.level_sets.clump_handling.Clump.add_validator` function. Current, two validators exist: a minimum number of cells and gravitational boundedness. .. code:: python master_clump.add_validator("min_cells", 20) master_clump.add_validator("gravitationally_bound", use_particles=False) As many validators as desired can be added, and a clump is only kept if all return True. If not, a clump is remerged into its parent. Custom validators can easily be added. A validator function must only accept a ``Clump`` object and either return True or False. .. code:: python def _minimum_gas_mass(clump, min_mass): return clump["gas", "mass"].sum() >= min_mass add_validator("minimum_gas_mass", _minimum_gas_mass) The :func:`~yt.data_objects.level_sets.clump_validators.add_validator` function adds the validator to a registry that can be accessed by the clump finder. Then, the validator can be added to the clump finding just like the others. .. code:: python master_clump.add_validator("minimum_gas_mass", ds.quan(1.0, "Msun")) Running the Clump Finder ------------------------ Clump finding then proceeds by calling the :func:`~yt.data_objects.level_sets.clump_handling.find_clumps` function. This function accepts the :class:`~yt.data_objects.level_sets.clump_handling.Clump` object, the initial minimum and maximum of the contouring field, and the step size. The lower value of the contour finder will be continually multiplied by the step size. .. code:: python c_min = data_source["gas", "density"].min() c_max = data_source["gas", "density"].max() step = 2.0 find_clumps(master_clump, c_min, c_max, step) Calculating Clump Quantities ---------------------------- By default, a number of quantities will be calculated for each clump when the clump finding process has finished. The default quantities are: ``total_cells``, ``mass``, ``mass_weighted_jeans_mass``, ``volume_weighted_jeans_mass``, ``max_grid_level``, ``min_number_density``, and ``max_number_density``. Additional items can be added with the :func:`~yt.data_objects.level_sets.clump_handling.Clump.add_info_item` function. .. code:: python master_clump.add_info_item("total_cells") Just like the validators, custom info items can be added by defining functions that minimally accept a :class:`~yt.data_objects.level_sets.clump_handling.Clump` object and return a format string to be printed and the value. These are then added to the list of available info items by calling :func:`~yt.data_objects.level_sets.clump_info_items.add_clump_info`: .. code:: python def _mass_weighted_jeans_mass(clump): jeans_mass = clump.data.quantities.weighted_average_quantity( "jeans_mass", ("gas", "mass") ).in_units("Msun") return "Jeans Mass (mass-weighted): %.6e Msolar." % jeans_mass add_clump_info("mass_weighted_jeans_mass", _mass_weighted_jeans_mass) Then, add it to the list: .. code:: python master_clump.add_info_item("mass_weighted_jeans_mass") Once you have run the clump finder, you should be able to access the data for the info item you have defined via the ``info`` attribute of a ``Clump`` object: .. code:: python clump = leaf_clumps[0] print(clump.info["mass_weighted_jeans_mass"]) Besides the quantities calculated by default, the following are available: ``center_of_mass`` and ``distance_to_main_clump``. Working with Clumps ------------------- After the clump finding has finished, the master clump will represent the top of a hierarchy of clumps. The ``children`` attribute within a :class:`~yt.data_objects.level_sets.clump_handling.Clump` object contains a list of all sub-clumps. Each sub-clump is also a :class:`~yt.data_objects.level_sets.clump_handling.Clump` object with its own ``children`` attribute, and so on. .. code:: python print(master_clump["gas", "density"]) print(master_clump.children) print(master_clump.children[0]["gas", "density"]) The entire clump tree can traversed with a loop syntax: .. code:: python for clump in master_clump: print(clump.clump_id) The ``leaves`` attribute of a ``Clump`` object will return a list of the individual clumps that have no children of their own (the leaf clumps). .. code:: python # Get a list of just the leaf nodes. leaf_clumps = master_clump.leaves print(leaf_clumps[0]["gas", "density"]) print(leaf_clumps[0]["all", "particle_mass"]) print(leaf_clumps[0].quantities.total_mass()) Visualizing Clumps ------------------ Clumps can be visualized using the ``annotate_clumps`` callback. .. code:: python prj = yt.ProjectionPlot(ds, 2, ("gas", "density"), center="c", width=(20, "kpc")) prj.annotate_clumps(leaf_clumps) prj.save("clumps") Saving and Reloading Clump Data ------------------------------- The clump tree can be saved as a reloadable dataset with the :func:`~yt.data_objects.level_sets.clump_handling.Clump.save_as_dataset` function. This will save all info items that have been calculated as well as any field values specified with the *fields* keyword. This function can be called for any clump in the tree, saving that clump and all those below it. .. code:: python fn = master_clump.save_as_dataset(fields=["density", "particle_mass"]) The clump tree can then be reloaded as a regular dataset. The ``tree`` attribute associated with the dataset provides access to the clump tree. The tree can be iterated over in the same fashion as the original tree. .. code:: python ds_clumps = yt.load(fn) for clump in ds_clumps.tree: print(clump.clump_id) The ``leaves`` attribute returns a list of all leaf clumps. .. code:: python print(ds_clumps.leaves) Info items for each clump can be accessed with the ``"clump"`` field type. Gas or grid fields should be accessed using the ``"grid"`` field type and particle fields should be access using the specific particle type. .. code:: python my_clump = ds_clumps.leaves[0] print(my_clumps["clump", "mass"]) print(my_clumps["grid", "density"]) print(my_clumps["all", "particle_mass"]) yt-project-yt-f043ac8/doc/source/analyzing/domain_analysis/cosmology_calculator.rst000066400000000000000000000055571510711153200310570ustar00rootroot00000000000000.. _cosmology-calculator: Cosmology Calculator ==================== The cosmology calculator can be used to calculate cosmological distances and times given a set of cosmological parameters. A cosmological dataset, ``ds``, will automatically have a cosmology calculator configured with the correct parameters associated with it as ``ds.cosmology``. A standalone :class:`~yt.utilities.cosmology.Cosmology` calculator object can be created in the following way: .. code-block:: python from yt.utilities.cosmology import Cosmology co = Cosmology( hubble_constant=0.7, omega_matter=0.3, omega_lambda=0.7, omega_curvature=0.0, omega_radiation=0.0, ) Once created, various distance calculations as well as conversions between redshift and time are available: .. notebook-cell:: from yt.utilities.cosmology import Cosmology co = Cosmology() # Hubble distance (c / h) print("hubble distance", co.hubble_distance()) # distance from z = 0 to 0.5 print("comoving radial distance", co.comoving_radial_distance(0, 0.5).in_units("Mpccm/h")) # transverse distance print("transverse distance", co.comoving_transverse_distance(0, 0.5).in_units("Mpccm/h")) # comoving volume print("comoving volume", co.comoving_volume(0, 0.5).in_units("Gpccm**3")) # angular diameter distance print("angular diameter distance", co.angular_diameter_distance(0, 0.5).in_units("Mpc/h")) # angular scale print("angular scale", co.angular_scale(0, 0.5).in_units("Mpc/degree")) # luminosity distance print("luminosity distance", co.luminosity_distance(0, 0.5).in_units("Mpc/h")) # time between two redshifts print("lookback time", co.lookback_time(0, 0.5).in_units("Gyr")) # critical density print("critical density", co.critical_density(0)) # Hubble parameter at a given redshift print("hubble parameter", co.hubble_parameter(0).in_units("km/s/Mpc")) # convert time after Big Bang to redshift my_t = co.quan(8, "Gyr") print("z from t", co.z_from_t(my_t)) # convert redshift to time after Big Bang print("t from z", co.t_from_z(0.5).in_units("Gyr")) .. warning:: Cosmological distance calculations return values that are either in the comoving or proper frame, depending on the specific quantity. For simplicity, the proper and comoving frames are set equal to each other within the cosmology calculator. This means that for some distance value, x, x.to("Mpc") and x.to("Mpccm") will be the same. The user should take care to understand which reference frame is correct for the given calculation. The helper functions, ``co.quan`` and ``co.arr`` exist to create unitful ``YTQuantities`` and ``YTArray`` with the unit registry of the cosmology calculator. For more information on the usage and meaning of each calculation, consult the reference documentation at :ref:`cosmology-calculator-ref`. yt-project-yt-f043ac8/doc/source/analyzing/domain_analysis/index.rst000066400000000000000000000042711510711153200257320ustar00rootroot00000000000000.. _domain-analysis: Domain-Specific Analysis ======================== yt powers a number modules that provide specialized analysis tools relevant to one or a few domains. Some of these are internal to yt, but many exist as external packages, either maintained by the yt project or independently. Internal Analysis Modules ------------------------- These modules exist within yt itself. .. note:: As of yt version 3.5, most of the astrophysical analysis tools have been moved to the :ref:`yt-astro` and :ref:`attic` packages. See below for more information. .. toctree:: :maxdepth: 2 cosmology_calculator clump_finding XrayEmissionFields xray_data_README External Analysis Modules ------------------------- These are external packages maintained by the yt project. .. _yt-astro: yt Astro Analysis ^^^^^^^^^^^^^^^^^ Source: https://github.com/yt-project/yt_astro_analysis Documentation: https://yt-astro-analysis.readthedocs.io/ The ``yt_astro_analysis`` package houses most of the astrophysical analysis tools that were formerly in the ``yt.analysis_modules`` import. These include halo finding, custom halo analysis, synthetic observations, and exports to radiative transfer codes. See :ref:`yt_astro_analysis:modules` for a list of available functionality. .. _attic: yt Attic ^^^^^^^^ Source: https://github.com/yt-project/yt_attic Documentation: https://yt-attic.readthedocs.io/ The ``yt_attic`` contains former yt analysis modules that have fallen by the wayside. These may have small bugs or were simply not kept up to date as yt evolved. Tools in here are looking for a new owner and a new home. If you find something in here that you'd like to bring back to life, either by adding it to :ref:`yt-astro` or as part of your own package, you are welcome to it! If you'd like any help, let us know! See :ref:`yt_attic:attic-modules` for a list of inventory of the attic. Extensions ---------- There are a number of independent, yt-related packages for things like visual effects, interactive widgets, synthetic absorption spectra, X-ray observations, and merger-trees. See the `yt Extensions `_ page for a list of available extension packages. yt-project-yt-f043ac8/doc/source/analyzing/domain_analysis/xray_data_README.rst000066400000000000000000000042311510711153200276100ustar00rootroot00000000000000.. _xray_data_README: Auxiliary Data Files for use with yt's Photon Simulator ======================================================= Included in the `xray_data `_ package are a number of files that you may find useful when working with yt's X-ray `photon_simulator `_ analysis module. They have been tested to give spectral fitting results consistent with input parameters. Spectral Model Tables --------------------- * tbabs_table.h5: Tabulated values of the galactic absorption cross-section in HDF5 format, generated from the routines at http://pulsar.sternwarte.uni-erlangen.de/wilms/research/tbabs/ ARFs and RMFs ------------- We have tested the following ARFs and RMFs with the photon simulator. These can be used to generate a very simplified representation of an X-ray observation, using a uniform, on-axis response. For more accurate models of X-ray observations we suggest using MARX or SIMX (detailed below_). * Chandra: chandra_ACIS-S3_onaxis_arf.fits, chandra_ACIS-S3_onaxis_rmf.fits Generated from the CIAO tools, on-axis on the ACIS-S3 chip. * XMM-Newton: pn-med.arf, pn-med.rmf EPIC pn CCDs (medium filter), taken from SIMX * Astro-H: sxt-s_100208_ts02um_intall.arf, ah_sxs_7ev_basefilt_20090216.rmf SXT-S+SXS responses taken from http://astro-h.isas.jaxa.jp/researchers/sim/response.html * NuSTAR: nustarA.arf, nustarA.rmf Averaged responses for NuSTAR telescope A generated by Dan Wik (NASA/GSFC) .. _below: Other Useful Things Not Included Here ------------------------------------- * AtomDB: http://www.atomdb.org FITS table data for emission lines and continuum emission. Must have it installed to use the TableApecModel spectral model. * PyXspec: https://heasarc.gsfc.nasa.gov/xanadu/xspec/python/html/ Python interface to the XSPEC spectral-fitting program. Two of the spectral models for the photon simulator use it. * MARX: https://space.mit.edu/ASC/MARX/ Detailed ray-trace simulations of Chandra. * SIMX: http://hea-www.harvard.edu/simx/ Simulates a photon-counting detector's response to an input source, including a simplified models of telescopes. yt-project-yt-f043ac8/doc/source/analyzing/fields.rst000066400000000000000000001153601510711153200227210ustar00rootroot00000000000000.. _fields: Fields in yt ============ Fields are spatially-dependent quantities associated with a parent dataset. Examples of fields are gas density, gas temperature, particle mass, etc. The fundamental way to query data in yt is to access a field, either in its raw form (by examining a data container) or a processed form (derived quantities, projections, aggregations, and so on). "Field" is something of a loaded word, as it can refer to quantities that are defined everywhere, which we refer to as "mesh" or "fluid" fields, or discrete points that populate the domain, traditionally thought of as "particle" fields. The word "particle" here is gradually falling out of favor, as these discrete fields can be any type of sparsely populated data. If you are developing a frontend or need to customize what yt thinks of as the fields for a given datast, see both :ref:`per-field-plotconfig` and :ref:`per-field-config` for information on how to change the display units, on-disk units, display name, etc. .. _what-are-fields: What are fields? ---------------- Fields in yt are denoted by a two-element string tuple, of the form ``(field_type, field_name)``. The first element, the "field type" is a category for a field. Possible field types used in yt include ``'gas'`` (for fluid mesh fields defined on a mesh) or ``'io'`` (for fields defined at particle locations). Field types can also correspond to distinct particle of fluid types in a single simulation. For example, a plasma physics simulation using the Particle in Cell method might have particle types corresponding to ``'electrons'`` and ``'ions'``. See :ref:`known-field-types` below for more info about field types in yt. The second element of field tuples, the ``field_name``, denotes the specific field to select, given the field type. Possible field names include ``'density'``, ``'velocity_x'`` or ``'pressure'`` --- these three fields are examples of field names that might be used for a fluid defined on a mesh. Examples of particle fields include ``'particle_mass'``, ``'particle_position'`` or ``'particle_velocity_x'``. In general, particle field names are prefixed by ``particle_``, which makes it easy to distinguish between a particle field or a mesh field when no field type is provided. What fields are available? -------------------------- We provide a full list of fields that yt recognizes by default at :ref:`field-list`. If you want to create additional custom derived fields, see :ref:`creating-derived-fields`. Every dataset has an attribute, ``ds.fields``. This attribute possesses attributes itself, each of which is a "field type," and each field type has as its attributes the fields themselves. When one of these is printed, it returns information about the field and things like units and so on. You can use this for tab-completing as well as easier access to information. Additionally, if you have `ipywidgets `_ installed and are in a `Jupyter environment `_, you can view the rich representation of the fields (including source code) by either typing ``ds.fields`` as the last item in a cell or by calling ``display(ds.fields)``. The resulting output will have tabs and source: .. image:: _images/fields_ipywidget.png :scale: 50% As an example, you might browse the available fields like so: .. code-block:: python print(dir(ds.fields)) print(dir(ds.fields.gas)) print(ds.fields.gas.density) On an Enzo dataset, the result from the final command would look something like this::: Alias Field for ('enzo', 'Density') ('gas', 'density'): (units: 'g/cm**3') You can use this to easily explore available fields, particularly through tab-completion in Jupyter/IPython. It's also possible to iterate over the list of fields associated with each field type. For example, to print all of the ``'gas'`` fields, one might do: .. code-block:: python for field in ds.fields.gas: print(field) You can also check if a given field is associated with a field type using standard python syntax: .. code-block:: python # these examples evaluate to True for a dataset that has ('gas', 'density') "density" in ds.fields.gas ("gas", "density") in ds.fields.gas ds.fields.gas.density in ds.fields.gas For a more programmatic method of accessing fields, you can utilize the ``ds.field_list``, ``ds.derived_field_list`` and some accessor methods to gain information about fields. The full list of fields available for a dataset can be found as the attribute ``field_list`` for native, on-disk fields and ``derived_field_list`` for derived fields (``derived_field_list`` is a superset of ``field_list``). You can view these lists by examining a dataset like this: .. code-block:: python ds = yt.load("my_data") print(ds.field_list) print(ds.derived_field_list) By using the ``field_info()`` class, one can access information about a given field, like its default units or the source code for it. .. code-block:: python ds = yt.load("my_data") ds.index print(ds.field_info["gas", "pressure"].get_units()) print(ds.field_info["gas", "pressure"].get_source()) Using fields to access data --------------------------- .. warning:: These *specific* operations will load the entire field -- which can be extremely memory intensive with large datasets! If you are looking to compute quantities, see :ref:`Data-objects` for methods for computing aggregates, averages, subsets, regriddings, etc. The primary *use* of fields in yt is to access data from a dataset. For example, if I want to use a data object (see :ref:`Data-objects` for more detail about data objects) to access the ``('gas', 'density')`` field, one can do any of the following: .. code-block:: python ad = ds.all_data() # just a field name density = ad["density"] # field tuple with no parentheses density = ad["gas", "density"] # full field tuple density = ad["gas", "density"] # through the ds.fields object density = ad[ds.fields.gas.density] The first data access example is the simplest. In that example, the field type is inferred from the name of the field. However, an error will be raised if there are multiple field names that could be meant by this simple string access. The next two examples use the field type explicitly, this might be necessary if there is more than one field type with a ``'density'`` field defined in the same dataset. The third example is slightly more verbose but is syntactically identical to the second example due to the way indexing works in the Python language. The final example uses the ``ds.fields`` object described above. This way of accessing fields lends itself to interactive use, especially if you make heavy use of IPython's tab completion features. Any of these ways of denoting the ``('gas', 'density')`` field can be used when supplying a field name to a yt data object, analysis routines, or plotting and visualization function. Accessing Fields without a Field Type ------------------------------------- In previous versions of yt, there was a single mechanism of accessing fields on a data container -- by their name, which was mandated to be a single string, and which often varied between different code frontends. yt 3.0 allows for datasets containing multiple different types of fluid fields, mesh fields, particles (with overlapping or disjoint lists of fields). However, to preserve backward compatibility and make interactive use simpler, yt 4.1 and newer will still accept field names given as a string *if and only if they match exactly one existing field*. As an example, we may be in a situation where have multiple types of particles which possess the ``'particle_position'`` field. In the case where a data container, here called ``ad`` (short for "all data") contains a field, we can specify which particular particle type we want to query: .. code-block:: python print(ad["dark_matter", "particle_position"]) print(ad["stars", "particle_position"]) print(ad["black_holes", "particle_position"]) Each of these three fields may have different sizes. In order to enable falling back on asking only for a field by the name, yt will use the most recently requested field type for subsequent queries. (By default, if no field has been queried, it will look for the special field ``'all'``, which concatenates all particle types.) For example, if I were to then query for the velocity: .. code-block:: python print(ad["particle_velocity"]) it would select ``black_holes`` as the field type, since the last field accessed used that field type. The same operations work for fluid and mesh fields. As an example, in some cosmology simulations, we may want to examine the mass of particles in a region versus the mass of gas. We can do so by examining the special "deposit" field types (described below) versus the gas fields: .. code-block:: python print(ad["deposit", "dark_matter_density"] / ad["gas", "density"]) The ``'deposit'`` field type is a mesh field, so it will have the same shape as the gas density. If we weren't using ``'deposit'``, and instead directly querying a particle field, this *wouldn't* work, as they are different shapes. This is the primary difference, in practice, between mesh and particle fields -- they will be different shapes and so cannot be directly compared without translating one to the other, typically through a "deposition" or "smoothing" step. How are fields implemented? --------------------------- There are two classes of fields in yt. The first are those fields that exist external to yt, which are immutable and can be queried -- most commonly, these are fields that exist on disk. These will often be returned in units that are not in a known, external unit system (except possibly by design, on the part of the code that wrote the data), and yt will take every effort possible to use the names by which they are referred to by the data producer. The default field type for mesh fields that are "on-disk" is the name of the code frontend. (For example, ``'art'``, ``'enzo'``, ``'pyne'``, and so on.) The default name for particle fields, if they do not have a particle type affiliated with them, is ``'io'``. The second class of field is the "derived field." These are fields that are functionally defined, either *ab initio* or as a transformation or combination of other fields. For example, when dealing with simulation codes, often the fields that are evolved and output to disk are not the fields that are the most relevant to researchers. Rather than examining the internal gas energy, it is more convenient to think of the temperature. By applying one or multiple functions to on-disk quantities, yt can construct new derived fields from them. Derived fields do not always have to relate to the data found on disk; special fields such as ``'x'``, ``'y'``, ``'phi'`` and ``'dz'`` all relate exclusively to the geometry of the mesh, and provide information about the mesh that can be used elsewhere for further transformations. For more information, see :ref:`creating-derived-fields`. There is a third, borderline class of field in yt, as well. This is the "alias" type, where a field on disk (for example, (``'*frontend*''``, ``'Density'``)) is aliased into an internal yt-name (for example, (``'gas'``, ``'density'``)). The aliasing process allows universally-defined derived fields to take advantage of internal names, and it also provides an easy way to address what units something should be returned in. If an aliased field is requested (and aliased fields will always be lowercase, with underscores separating words) it will be returned in the units specified by the unit system of the database, whereas if the frontend-specific field is requested, it will not undergo any unit conversions from its natural units. (This rule is occasionally violated for fields which are mesh-dependent, specifically particle masses in some cosmology codes.) .. _known-field-types: Field types known to yt ----------------------- Recall that fields are formally accessed in two parts: ``('*field type*', '*field name*')``. Here we describe the different field types you will encounter: * frontend-name -- Mesh or fluid fields that exist on-disk default to having the name of the frontend as their type name (e.g., ``'enzo'``, ``'flash'``, ``'pyne'`` and so on). The units of these types are whatever units are designated by the source frontend when it writes the data. * ``'index'`` -- This field type refers to characteristics of the mesh, whether that mesh is defined by the simulation or internally by an octree indexing of particle data. A few handy fields are ``'x'``, ``'y'``, ``'z'``, ``'theta'``, ``'phi'``, ``'radius'``, ``'dx'``, ``'dy'``, ``'dz'`` and so on. Default units are in CGS. * ``'gas'`` -- This is the usual default for simulation frontends for fluid types. These fields are typically aliased to the frontend-specific mesh fields for grid-based codes or to the deposit fields for particle-based codes. Default units are in the unit system of the dataset. * particle type -- These are particle fields that exist on-disk as written by individual frontends. If the frontend designates names for these particles (i.e. particle type) those names are the field types. Additionally, any particle unions or filters will be accessible as field types. Examples of particle types are ``'Stars'``, ``'DM'``, ``'io'``, etc. Like the front-end specific mesh or fluid fields, the units of these fields are whatever was designated by the source frontend when written to disk. * ``'io'`` -- If a data frontend does not have a set of multiple particle types, this is the default for all particles. * ``'all'`` and ``'nbody'`` -- These are special particle field types that represent a concatenation of several particle field types using :ref:`particle-unions`. ``'all'`` contains every base particle types, while ``'nbody'`` contains only the ones for which a ``'particle_mass'`` field is defined. * ``'deposit'`` -- This field type refers to the deposition of particles (discrete data) onto a mesh, typically to compute smoothing kernels, local density estimates, counts, and the like. See :ref:`deposited-particle-fields` for more information. While it is best to be explicit access fields by their full names (i.e. ``('*field type*', '*field name*')``), yt provides an abbreviated interface for accessing common fields (i.e. ``'*field name*'``). In the abbreviated case, yt will assume you want the last *field type* accessed. If you haven't previously accessed a *field type*, it will default to *field type* = ``'all'`` in the case of particle fields and *field type* = ``'gas'`` in the case of mesh fields. Field Plugins ------------- Derived fields are organized via plugins. Inside yt are a number of field plugins, which take information about fields in a dataset and then construct derived fields on top of them. This allows them to take into account variations in naming system, units, data representations, and most importantly, allows only the fields that are relevant to be added. This system will be expanded in future versions to enable much deeper semantic awareness of the data types being analyzed by yt. The field plugin system works in this order: * Available, inherent fields are identified by yt * The list of enabled field plugins is iterated over. Each is called, and new derived fields are added as relevant. * Any fields which are not available, or which throw errors, are discarded. * Remaining fields are added to the list of derived fields available for a dataset * Dependencies for every derived field are identified, to enable data preloading Field plugins can be loaded dynamically, although at present this is not particularly useful. Plans for extending field plugins to dynamically load, to enable simple definition of common types (divergence, curl, etc), and to more verbosely describe available fields, have been put in place for future versions. The field plugins currently available include: * Angular momentum fields for particles and fluids * Astrophysical fields, such as those related to cosmology * Vector fields for fluid fields, such as gradients and divergences * Particle vector fields * Magnetic field-related fields * Species fields, such as for chemistry species (yt can recognize the entire periodic table in field names and construct ionization fields as need be) Field Labeling -------------- By default yt formats field labels nicely for plots. To adjust the chosen format you can use the ``ds.set_field_label_format`` method like so: .. code-block:: python ds = yt.load("my_data") ds.set_field_label_format("ionization_label", "plus_minus") The first argument accepts a ``format_property``, or specific aspect of the labeling, and the second sets the corresponding ``value``. Currently available format properties are * ``ionization_label``: sets how the ionization state of ions are labeled. Available options are ``"plus_minus"`` and ``"roman_numeral"`` .. _efields: Energy and Momentum Fields -------------------------- Fields in yt representing energy and momentum quantities follow a specific naming convention (as of yt-4.x). In hydrodynamic simulations, the relevant quantities are often energy per unit mass or volume, momentum, or momentum density. To distinguish clearly between the different types of fields, the following naming convention is adhered to: * Energy per unit mass fields are named as ``'specific_*_energy'`` * Energy per unit volume fields are named as ``'*_energy_density'`` * Momentum fields should be named ``'momentum_density_*'`` for momentum per unit density, or ``'momentum_*'`` for momentum, where the ``*`` indicates one of three coordinate axes in any supported coordinate system. For example, in the case of kinetic energy, the fields should be ``'kinetic_energy_density'`` and ``'specific_kinetic_energy'``. In versions of yt previous to v4.0.0, these conventions were not adopted, and so energy fields in particular could be ambiguous with respect to units. For example, the ``'kinetic_energy'`` field was actually kinetic energy per unit volume, whereas the ``'thermal_energy'`` field, usually defined by various frontends, was typically thermal energy per unit mass. The above scheme rectifies these problems, but for the time being the previous field names are mapped to the current field naming scheme with a deprecation warning. These aliases were removed in yt v4.1.0. .. _bfields: Magnetic Fields --------------- Magnetic fields require special handling, because their dimensions are different in different systems of units, in particular between the CGS and MKS (SI) systems of units. Superficially, it would appear that they are in the same dimensions, since the units of the magnetic field in the CGS and MKS system are gauss (:math:`\rm{G}`) and tesla (:math:`\rm{T}`), respectively, and numerically :math:`1~\rm{G} = 10^{-4}~\rm{T}`. However, if we examine the base units, we find that they do indeed have different dimensions: .. math:: \rm{1~G = 1~\frac{\sqrt{g}}{\sqrt{cm}\cdot{s}}} \\ \rm{1~T = 1~\frac{kg}{A\cdot{s^2}}} It is easier to see the difference between the dimensionality of the magnetic field in the two systems in terms of the definition of the magnetic pressure and the Alfvén speed: .. math:: p_B = \frac{B^2}{8\pi}~\rm{(cgs)} \\ p_B = \frac{B^2}{2\mu_0}~\rm{(MKS)} .. math:: v_A = \frac{B}{\sqrt{4\pi\rho}}~\rm{(cgs)} \\ v_A = \frac{B}{\sqrt{\mu_0\rho}}~\rm{(MKS)} where :math:`\mu_0 = 4\pi \times 10^{-7}~\rm{N/A^2}` is the vacuum permeability. This different normalization in the definition of the magnetic field may show up in other relevant quantities as well. For certain frontends, a third definition of the magnetic field and the magnetic pressure may be useful. In many MHD simulations and in some physics areas (such as particle physics/GR) it is more common to use the "Lorentz-Heaviside" convention, which results in: .. math:: p_B = \frac{B^2}{2} \\ v_A = \frac{B}{\sqrt{\rho}} Using this convention is currently only available for :ref:`Athena`, :ref:`Athena++`, and :ref:`AthenaPK` datasets, though it will likely be available for more datasets in the future. yt automatically detects on a per-frontend basis what units the magnetic should be in, and allows conversion between different magnetic field units in the different unit systems as well. To determine how to set up special magnetic field handling when designing a new frontend, check out :ref:`bfields-frontend`. .. _species-fields: Species Fields -------------- For many types of data, yt is able to detect different chemical elements and molecules within the dataset, as well as their abundances and ionization states. Examples include: * CO (Carbon monoxide) * Co (Cobalt) * OVI (Oxygen ionized five times) * H:math:`^{2+}` (Molecular Hydrogen ionized once) * H:math:`^{-}` (Hydrogen atom with an additional electron) The naming scheme for the fields starts with prefixes in the form ``MM[_[mp][NN]]``. ``MM`` is the molecule, defined as a concatenation of atomic symbols and numbers, with no spaces or underscores. The second sequence is only required if ionization states are present in the dataset, and is of the form ``p`` and ``m`` to indicate "plus" or "minus" respectively, followed by the number. If a given species has no ionization states given, the prefix is simply ``MM``. For the examples above, the prefixes would be: * ``CO`` * ``Co`` * ``O_p5`` * ``H2_p1`` * ``H_m1`` The name ``El`` is used for electron fields, as it is unambiguous and will not be utilized elsewhere. Neutral ionic species (e.g. H I, O I) are represented as ``MM_p0``. Additionally, the isotope of :math:`^2`H will be included as ``D``. Finally, in those frontends which are single-fluid, these fields for each species are defined: * ``MM[_[mp][NN]]_fraction`` * ``MM[_[mp][NN]]_number_density`` * ``MM[_[mp][NN]]_density`` * ``MM[_[mp][NN]]_mass`` To refer to the number density of the entirety of a single atom or molecule (regardless of its ionization state), please use the ``MM_nuclei_density`` fields. Many datasets do not have species defined, but there may be an underlying assumption of primordial abundances of H and He which are either fully ionized or fully neutral. This will also determine the value of the mean molecular weight of the gas, which will determine the value of the temperature if derived from another quantity like the pressure or thermal energy. To allow for these possibilities, there is a keyword argument ``default_species_fields`` which can be passed to :func:`~yt.loaders.load`: .. code-block:: python import yt ds = yt.load( "GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150", default_species_fields="ionized" ) By default, the value of this optional argument is ``None``, which will not initialize any default species fields. If the ``default_species_fields`` argument is not set to ``None``, then the following fields are defined: * ``H_nuclei_density`` * ``He_nuclei_density`` More specifically, if ``default_species_fields="ionized"``, then these additional fields are defined: * ``H_p1_number_density`` (Ionized hydrogen: equal to the value of ``H_nuclei_density``) * ``He_p2_number_density`` (Doubly ionized helium: equal to the value of ``He_nuclei_density``) * ``El_number_density`` (Free electrons: assuming full ionization) Whereas if ``default_species_fields="neutral"``, then these additional fields are defined: * ``H_p0_number_density`` (Neutral hydrogen: equal to the value of ``H_nuclei_density``) * ``He_p0_number_density`` (Neutral helium: equal to the value of ``He_nuclei_density``) In this latter case, because the gas is neutral, ``El_number_density`` is not defined. The ``mean_molecular_weight`` field will be constructed from the abundances of the elements in the dataset. If no element or molecule fields are defined, the value of this field is determined by the value of ``default_species_fields``. If it is set to ``None`` or ``"ionized"``, the ``mean_molecular_weight`` field is set to :math:`\mu \approx 0.6`, whereas if ``default_species_fields`` is set to ``"neutral"``, then the ``mean_molecular_weight`` field is set to :math:`\mu \approx 1.14`. Some frontends do not directly store the gas temperature in their datasets, in which case it must be computed from the pressure and/or thermal energy as well as the mean molecular weight, so check this carefully! Particle Fields --------------- Naturally, particle fields contain properties of particles rather than grid cells. By examining the particle field in detail, you can see that each element of the field array represents a single particle, whereas in mesh fields each element represents a single mesh cell. This means that for the most part, operations cannot operate on both particle fields and mesh fields simultaneously in the same way, like filters (see :ref:`filtering-data`). However, many of the particle fields have corresponding mesh fields that can be populated by "depositing" the particle values onto a yt grid as described below. .. _field_parameters: Field Parameters ---------------- Certain fields require external information in order to be calculated. For example, the radius field has to be defined based on some point of reference and the radial velocity field needs to know the bulk velocity of the data object so that it can be subtracted. This information is passed into a field function by setting field parameters, which are user-specified data that can be associated with a data object. The :meth:`~yt.data_objects.data_containers.YTDataContainer.set_field_parameter` and :meth:`~yt.data_objects.data_containers.YTDataContainer.get_field_parameter` functions are used to set and retrieve field parameter values for a given data object. In the cases above, the field parameters are ``center`` and ``bulk_velocity`` respectively -- the two most commonly used field parameters. .. code-block:: python ds = yt.load("my_data") ad = ds.all_data() ad.set_field_parameter("wickets", 13) print(ad.get_field_parameter("wickets")) If a field parameter is not set, ``get_field_parameter`` will return None. Within a field function, these can then be retrieved and used in the same way. .. code-block:: python def _wicket_density(field, data): n_wickets = data.get_field_parameter("wickets") if n_wickets is None: # use a default if unset n_wickets = 88 return data["gas", "density"] * n_wickets For a practical application of this, see :ref:`cookbook-radial-velocity`. .. _gradient_fields: Gradient Fields --------------- yt provides a way to compute gradients of spatial fields using the :meth:`~yt.data_objects.static_output.Dataset.add_gradient_fields` method. If you have a spatially-based field such as density or temperature, and want to calculate the gradient of that field, you can do it like so: .. code-block:: python ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") grad_fields = ds.add_gradient_fields(("gas", "temperature")) where the ``grad_fields`` list will now have a list of new field names that can be used in calculations, representing the 3 different components of the field and the magnitude of the gradient, e.g., ``"temperature_gradient_x"``, ``"temperature_gradient_y"``, ``"temperature_gradient_z"``, and ``"temperature_gradient_magnitude"``. To see an example of how to create and use these fields, see :ref:`cookbook-complicated-derived-fields`. .. _relative_fields: Relative Vector Fields ---------------------- yt makes use of "relative" fields for certain vector fields, which are fields which have been defined relative to a particular origin in the space of that field. For example, relative particle positions can be specified relative to a center coordinate, and relative velocities can be specified relative to a bulk velocity. These origin points are specified by setting field parameters as detailed below (see :ref:`field_parameters` for more information). The relative fields which are currently supported for gas fields are: * ``('gas', 'relative_velocity_x')``, defined by setting the ``'bulk_velocity'`` field parameter * ``('gas', 'relative_magnetic_field_x')``, defined by setting the ``'bulk_magnetic_field'`` field parameter Note that fields ending in ``'_x'`` are defined for each component. For particle fields, for a given particle type ``ptype``, the relative fields which are supported are: * ``(*ptype*, 'relative_particle_position')``, defined by setting the ``'center'`` field parameter * ``(*ptype*, 'relative_particle_velocity')``, defined by setting the ``'bulk_velocity'`` field parameter * ``(*ptype*, 'relative_particle_position_x')``, defined by setting the ``'center'`` field parameter * ``(*ptype*, 'relative_particle_velocity_x')``, defined by setting the ``'bulk_velocity'`` field parameter These fields are in use when defining magnitude fields, line-of-sight fields, etc.. The ``'bulk_*'`` field parameters are ``[0.0, 0.0, 0.0]`` by default, and the ``'center'`` field parameter depends on the data container in use. There is currently no mechanism to create new relative fields, but one may be added at a later time. .. _los_fields: Line of Sight Fields -------------------- In astrophysics applications, one often wants to know the component of a vector field along a given line of sight. If you are doing a projection of a vector field along an axis, or just want to obtain the values of a vector field component along an axis, you can use a line-of-sight field. For projections, this will be handled automatically: .. code-block:: python prj = yt.ProjectionPlot( ds, "z", fields=("gas", "velocity_los"), weight_field=("gas", "density"), ) Which, because the axis is ``'z'``, will give you the same result if you had projected the ``'velocity_z'`` field. This also works for off-axis projections, using an arbitrary normal vector .. code-block:: python prj = yt.ProjectionPlot( ds, [0.1, -0.2, 0.3], fields=("gas", "velocity_los"), weight_field=("gas", "density"), ) This shows that the projection axis can be along a principle axis of the domain or an arbitrary off-axis 3-vector (which will be automatically normalized). If you want to examine a line-of-sight vector within a 3-D data object, set the ``'axis'`` field parameter: .. code-block:: python dd = ds.all_data() # Set to one of [0, 1, 2] for ["x", "y", "z"] axes dd.set_field_parameter("axis", 1) print(dd["gas", "magnetic_field_los"]) # Set to a three-vector for an off-axis component dd.set_field_parameter("axis", [0.3, 0.4, -0.7]) print(dd["gas", "velocity_los"]) # particle fields are supported too! print(dd["all", "particle_velocity_los"]) .. warning:: If you need to change the axis of the line of sight on the *same* data container (sphere, box, cylinder, or whatever), you will need to delete the field using (e.g.) ``del dd['velocity_los']`` and re-generate it. At this time, this functionality is enabled for the velocity and magnetic vector fields, ``('gas', 'velocity_los')`` and ``('gas', 'magnetic_field_los')`` for the ``"gas"`` field type, as well as every particle type with a velocity field, e.g. ``("all", "particle_velocity_los")``. The following fields built into yt make use of these line-of-sight fields: * ``('gas', 'sz_kinetic')`` uses ``('gas', 'velocity_los')`` * ``('gas', 'rotation_measure')`` uses ``('gas', 'magnetic_field_los')`` General Particle Fields ----------------------- Every particle will contain both a ``'particle_position'`` and ``'particle_velocity'`` that tracks the position and velocity (respectively) in code units. .. _deposited-particle-fields: Deposited Particle Fields ------------------------- In order to turn particle (discrete) fields into fields that are deposited in some regular, space-filling way (even if that space is empty, it is defined everywhere) yt provides mechanisms for depositing particles onto a mesh. These are in the special field-type space ``'deposit'``, and are typically of the form ``('deposit', 'particletype_depositiontype')`` where ``depositiontype`` is the mechanism by which the field is deposited, and ``particletype`` is the particle type of the particles being deposited. If you are attempting to examine the cloud-in-cell (``cic``) deposition of the ``all`` particle type, you would access the field ``('deposit', 'all_cic''')``. yt defines a few particular types of deposition internally, and creating new ones can be done by modifying the files ``yt/geometry/particle_deposit.pyx`` and ``yt/fields/particle_fields.py``, although that is an advanced topic somewhat outside the scope of this section. The default deposition types available are: * ``count`` - this field counts the total number of particles of a given type in a given mesh zone. Note that because, in general, the mesh for particle datasets is defined by the number of particles in a region, this may not be the most useful metric. This may be made more useful by depositing particle data onto an :ref:`arbitrary-grid`. * ``density`` - this field takes the total sum of ``particle_mass`` in a given mesh field and divides by the volume. * ``mass`` - this field takes the total sum of ``particle_mass`` in each mesh zone. * ``cic`` - this field performs cloud-in-cell interpolation (see `Section 2.2 `_ for more information) of the density of particles in a given mesh zone. * ``smoothed`` - this is a special deposition type. See discussion below for more information, in :ref:`sph-fields`. You can also directly use the :meth:`~yt.data_objects.static_outputs.add_deposited_particle_field` function defined on each dataset to depose any particle field onto the mesh like so: .. code-block:: python import yt ds = yt.load("output_00080/info_00080.txt") fname = ds.add_deposited_particle_field( ("all", "particle_velocity_x"), method="nearest" ) print(f"The velocity of the particles are (stored in {fname}") print(ds.r[fname]) .. note:: In this example, we are using the returned field name as our input. You *could* also access it directly, but it might take a slightly different form than you expect -- in this particular case, the field name will be ``("deposit", "all_nn_velocity_x")``, which has removed the prefix ``particle_`` from the deposited name! Possible deposition methods are: * ``'simple_smooth'`` - perform an SPH-like deposition of the field onto the mesh optionally accepting a ``kernel_name``. * ``'sum'`` - sums the value of the particle field for all particles found in each cell. * ``'std'`` - computes the standard deviation of the value of the particle field for all particles found in each cell. * ``'cic'`` - performs cloud-in-cell interpolation (see `Section 2.2 `_ for more information) of the particle field on a given mesh zone. * ``'weighted_mean'`` - computes the mean of the particle field, weighted by the field passed into ``weight_field`` (by default, it uses the particle mass). * ``'count'`` - counts the number of particles in each cell. * ``'nearest'`` - assign to each cell the value of the closest particle. In addition, the :meth:`~yt.data_objects.static_outputs.add_deposited_particle_field` function returns the name of the newly created field. Deposited particle fields can be useful for visualizing particle data, including particles without defined smoothing lengths. See :ref:`particle-plotting-workarounds` for more information. .. _mesh-sampling-particle-fields: Mesh Sampling Particle Fields ----------------------------- In order to turn mesh fields into discrete particle field, yt provides a mechanism to do sample mesh fields at particle locations. This operation is the inverse operation of :ref:`deposited-particle-fields`: for each particle the cell containing the particle is found and the value of the field in the cell is assigned to the particle. This is for example useful when using tracer particles to have access to the Eulerian information for Lagrangian particles. The particle fields are named ``('*ptype*', 'cell_*ftype*_*fname*')`` where ``ptype`` is the particle type onto which the deposition occurs, ``ftype`` is the mesh field type (e.g. ``'gas'``) and ``fname`` is the field (e.g. ``'temperature'``, ``'density'``, ...). You can directly use the :meth:`~yt.data_objects.static_output.Dataset.add_mesh_sampling_particle_field` function defined on each dataset to impose a field onto the particles like so: .. code-block:: python import yt ds = yt.load("output_00080/info_00080.txt") ds.add_mesh_sampling_particle_field(("gas", "temperature"), ptype="all") print("The temperature at the location of the particles is") print(ds.r["all", "cell_gas_temperature"]) For octree codes (e.g. RAMSES), you can trigger the build of an index so that the next sampling operations will be mush faster .. code-block:: python import yt ds = yt.load("output_00080/info_00080.txt") ds.add_mesh_sampling_particle_field(("gas", "temperature"), ptype="all") ad = ds.all_data() ad[ "all", "cell_index" ] # Trigger the build of the index of the cell containing the particles ad["all", "cell_gas_temperature"] # This is now much faster .. _sph-fields: SPH Fields ---------- See :ref:`yt4differences`. In previous versions of yt, there were ways of computing the distance to the N-th nearest neighbor of a particle, as well as computing the nearest particle value on a mesh. Unfortunately, because of changes to the way that particles are regarded in yt, these are not currently available. We hope that this will be rectified in future versions and are tracking this in `Issue 3301 `_. You can read a bit more about the way yt now handles particles in the section :ref:`demeshening`. **But!** It is possible to compute the smoothed values from SPH particles on grids. For example, one can construct a covering grid that extends over the entire domain of a simulation, with resolution 256x256x256, and compute the gas density with this reasonable terse command: .. code-block:: python import yt ds = yt.load("snapshot_033/snap_033.0.hdf5") cg = ds.r[::256j, ::256j, ::256j] smoothed_values = cg["gas", "density"] This will work for any smoothed field; any field that is under the ``'gas'`` field type will be a smoothed field in an SPH-based simulation. Here we have used the ``ds.r[]`` notation, as described in :ref:`quickly-selecting-data` for creating what's called an "arbitrary grid" (:class:`~yt.data_objects.construction_data_containers.YTArbitraryGrid`). You can, of course, also supply left and right edges to make the grid take up a much smaller portion of the domain, as well, by supplying the arguments as detailed in :ref:`arbitrary-grid-selection` and supplying the bounds as the first and second elements in each element of the slice. yt-project-yt-f043ac8/doc/source/analyzing/filtering.rst000066400000000000000000000311351510711153200234330ustar00rootroot00000000000000.. _filtering-data: Filtering your Dataset ====================== Large datasets are oftentimes too overwhelming to deal with in their entirety, and it can be useful and faster to analyze subsets of these datasets. Furthermore, filtering the dataset based on some field condition can reveal subtle information not easily accessible by looking at the whole dataset. Filters can be generated based on spatial position, say in a sphere in the center of your dataset space, or more generally they can be defined by the properties of any field in the simulation. Because *mesh fields* are internally different from *particle fields*, there are different ways of filtering each type as indicated below; however, filtering fields by spatial location (i.e. geometric objects) will apply to both types equally. .. _filtering-mesh: Filtering Mesh Fields ---------------------- Mesh fields can be filtered by two methods: cut region objects (:class:`~yt.data_objects.selection_data_containers.YTCutRegion`) and NumPy boolean masks. Boolean masks are simpler, but they only work for examining datasets, whereas cut regions objects create wholly new data objects suitable for full analysis (data examination, image generation, etc.) Boolean Masks ^^^^^^^^^^^^^ NumPy boolean masks can be used with any NumPy array simply by passing the array a conditional. As a general example of this: .. notebook-cell:: import numpy as np a = np.arange(5) bigger_than_two = a > 2 print("Original Array: a = \n%s" % a) print("Boolean Mask: bigger_than_two = \n%s" % bigger_than_two) print("Masked Array: a[bigger_than_two] = \n%s" % a[bigger_than_two]) Similarly, if you've created a yt data object (e.g. a region, a sphere), you can examine its field values as a NumPy array by simply indexing it with the field name. Thus, it too can be masked using a NumPy boolean mask. Let's set a simple mask based on the contents of one of our fields. .. notebook-cell:: import yt ds = yt.load("Enzo_64/DD0042/data0042") ad = ds.all_data() hot = ad["gas", "temperature"].in_units("K") > 1e6 print( 'Temperature of all data: ad["gas", "temperature"] = \n%s' % ad["gas", "temperature"] ) print("Boolean Mask: hot = \n%s" % hot) print( 'Temperature of "hot" data: ad["gas", "temperature"][hot] = \n%s' % ad["gas", "temperature"][hot] ) This was a simple example, but one can make the conditionals that define a boolean mask have multiple parts, and one can stack masks together to make very complex cuts on one's data. Once the data is filtered, it can be used if you simply need to access the NumPy arrays: .. notebook-cell:: import yt ds = yt.load("Enzo_64/DD0042/data0042") ad = ds.all_data() overpressure_and_fast = ( (ad["gas", "pressure"] > 1e-14) & (ad["gas", "velocity_magnitude"].in_units("km/s") > 1e2) ) density = ad["gas", "density"] print('Density of all data: ad["gas", "density"] = \n%s' % density) print( 'Density of "overpressure and fast" data: overpressure_and_fast["gas", "density"] = \n%s' % density[overpressure_and_fast] ) .. _cut-regions: Cut Regions ^^^^^^^^^^^ Cut regions are a more general solution to filtering mesh fields. The output of a cut region is an entirely new data object, which can be treated like any other data object to generate images, examine its values, etc. See `this `_. In addition to inputting string parameters into cut_region to specify filters, wrapper functions exist that allow the user to use a simplified syntax for filtering out unwanted regions. Such wrapper functions are methods of :func: ``YTSelectionContainer3D``. .. notebook-cell:: import yt ds = yt.load("Enzo_64/DD0042/data0042") ad = ds.all_data() overpressure_and_fast = ad.include_above(("gas", "pressure"), 1e-14) # You can chain include_xx and exclude_xx to produce the intersection of cut regions overpressure_and_fast = overpressure_and_fast.include_above( ("gas", "velocity_magnitude"), 1e2, "km/s" ) print('Density of all data: ad["gas", "density"] = \n%s' % ad["gas", "density"]) print( 'Density of "overpressure and fast" data: overpressure_and_fast["gas", "density"] = \n%s' % overpressure_and_fast["gas", "density"] ) The following exclude and include functions are supported: - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.include_equal` - Only include values equal to given value - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.exclude_equal`- Exclude values equal to given value - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.include_inside` - Only include values inside closed interval - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.exclude_inside` - Exclude values inside closed interval - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.include_outside` - Only include values outside closed interval - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.exclude_outside` - Exclude values outside closed interval - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.exclude_nan` - Exclude NaN values - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.include_above` - Only include values above given value - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.exclude_above` - Exclude values above given value - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.include_below` - Only include values below given balue - :func:`~yt.data_objects.data_containers.YTSelectionContainer3D.exclude_below` - Exclude values below given value .. warning:: Cut regions are unstable when used on particle fields. Though you can create a cut region using a mesh field or fields as a filter and then obtain a particle field within that region, you cannot create a cut region using particle fields in the filter, as yt will currently raise an error. If you want to filter particle fields, see the next section :ref:`filtering-particles` instead. .. _filtering-particles: Filtering Particle Fields ------------------------- Particle filters create new particle fields based on the manipulation and cuts on existing particle fields. You can apply cuts to them to effectively mask out everything except the particles with which you are concerned. Creating a particle filter takes a few steps. You must first define a function which accepts a data object (e.g. all_data, sphere, etc.) as its argument. It uses the fields and information in this geometric object in order to produce some sort of conditional mask that is then returned to create a new particle type. Here is a particle filter to create a new ``star`` particle type. For Enzo simulations, stars have ``particle_type`` set to 2, so our filter will select only the particles with ``particle_type`` (i.e. field = ``('all', 'particle_type')`` equal to 2. .. code-block:: python @yt.particle_filter(requires=["particle_type"], filtered_type="all") def stars(pfilter, data): filter = data[pfilter.filtered_type, "particle_type"] == 2 return filter The :func:`~yt.data_objects.particle_filters.particle_filter` decorator takes a few options. You must specify the names of the particle fields that are required in order to define the filter --- in this case the ``particle_type`` field. Additionally, you must specify the particle type to be filtered --- in this case we filter all the particle in dataset by specifying the ``all`` particle type. In addition, you may specify a name for the newly defined particle type. If no name is specified, the name for the particle type will be inferred from the name of the filter definition --- in this case the inferred name will be ``stars``. As an alternative syntax, you can also define a new particle filter via the :func:`~yt.data_objects.particle_filter.add_particle_filter` function. .. code-block:: python def stars(pfilter, data): filter = data[pfilter.filtered_type, "particle_type"] == 2 return filter yt.add_particle_filter( "stars", function=stars, filtered_type="all", requires=["particle_type"] ) This is equivalent to our use of the ``particle_filter`` decorator above. The choice to use either the ``particle_filter`` decorator or the ``add_particle_filter`` function is a purely stylistic choice. Lastly, the filter must be applied to our dataset of choice. Note that this filter can be added to as many datasets as we wish. It will only actually create new filtered fields if the dataset has the required fields, though. .. code-block:: python import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") ds.add_particle_filter("stars") And that's it! We can now access all of the ('stars', field) fields from our dataset ``ds`` and treat them as any other particle field. In addition, it created some ``deposit`` fields, where the particles were deposited on to the grid as mesh fields. We can create additional filters building on top of the filters we have. For example, we can identify the young stars based on their age, which is the difference between current time and their creation_time. .. code-block:: python def young_stars(pfilter, data): age = data.ds.current_time - data[pfilter.filtered_type, "creation_time"] filter = np.logical_and(age.in_units("Myr") <= 5, age >= 0) return filter yt.add_particle_filter( "young_stars", function=young_stars, filtered_type="stars", requires=["creation_time"], ) If we properly define all the filters using the decorator ``yt.particle_filter`` or the function ``yt.add_particle_filter`` in advance. We can add the filter we need to the dataset. If the ``filtered_type`` is already defined but not added to the dataset, it will automatically add the filter first. For example, if we add the ``young_stars`` filter, which is filtered from ``stars``, to the dataset, it will also add ``stars`` filter to the dataset. .. code-block:: python import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") ds.add_particle_filter("young_stars") Additional example of particle filters can be found in the `notebook `_. .. _particle-unions: Particle Unions --------------- Multiple types of particles can be combined into a single, conceptual type. As an example, the NMSU-ART code has multiple "species" of dark matter, which we union into a single ``darkmatter`` field. The ``all`` particle type is a special case of this. To create a particle union, you need to import the ``ParticleUnion`` class from ``yt.data_objects.unions``, which you then create and pass into ``add_particle_union`` on a dataset object. Here is an example, where we union the ``halo`` and ``disk`` particle types into a single type, ``star``. yt will then determine which fields are accessible to this new particle type and it will add them. .. code-block:: python from yt.data_objects.unions import ParticleUnion u = ParticleUnion("star", ["halo", "disk"]) ds.add_particle_union(u) .. _filtering-by-location: Filtering Fields by Spatial Location: Geometric Objects ------------------------------------------------------- Creating geometric objects for a dataset provides a means for filtering a field based on spatial location. The most commonly used of these are spheres, regions (3D prisms), ellipsoids, disks, and rays. The ``all_data`` object which gets used throughout this documentation section is an example of a geometric object, but it defaults to including all the data in the dataset volume. To see all of the geometric objects available, see :ref:`available-objects`. Consult the object documentation section for all of the different objects one can use, but here is a simple example using a sphere object to filter a dataset. Let's filter out everything not within 10 Mpc of some random location, say [0.2, 0.5, 0.1], in the simulation volume. The resulting object will only contain grid cells with centers falling inside of our defined sphere, which may look offset based on the presence of different resolution elements distributed throughout the dataset. .. notebook-cell:: import yt ds = yt.load("Enzo_64/DD0042/data0042") center = [0.20, 0.50, 0.10] sp = ds.sphere(center, (10, "Mpc")) prj = yt.ProjectionPlot( ds, "x", ("gas", "density"), center=center, width=(50, "Mpc"), data_source=sp ) # Mark the center with a big X prj.annotate_marker(center, "x", s=100) prj.show() slc = yt.SlicePlot( ds, "x", ("gas", "density"), center=center, width=(50, "Mpc"), data_source=sp ) slc.show() yt-project-yt-f043ac8/doc/source/analyzing/generating_processed_data.rst000066400000000000000000000341501510711153200266330ustar00rootroot00000000000000.. _generating-processed-data: Generating Processed Data ========================= Although yt provides a number of built-in visualization methods that can process data and construct from that plots, it is often useful to generate the data by hand and construct plots which can then be combined with other plots, modified in some way, or even (gasp) created and modified in some other tool or program. .. _exporting-container-data: Exporting Container Data ------------------------ Fields from data containers such as regions, spheres, cylinders, etc. can be exported tabular format using either a :class:`~pandas.DataFrame` or an :class:`~astropy.table.QTable`. To export to a :class:`~pandas.DataFrame`, use :meth:`~yt.data_objects.data_containers.YTDataContainer.to_dataframe`: .. code-block:: python sp = ds.sphere("c", (0.2, "unitary")) df2 = sp.to_dataframe([("gas", "density"), ("gas", "temperature")]) To export to a :class:`~astropy.table.QTable`, use :meth:`~yt.data_objects.data_containers.YTDataContainer.to_astropy_table`: .. code-block:: python sp = ds.sphere("c", (0.2, "unitary")) at2 = sp.to_astropy_table(fields=[("gas", "density"), ("gas", "temperature")]) For exports to :class:`~pandas.DataFrame` objects, the unit information is lost, but for exports to :class:`~astropy.table.QTable` objects, the :class:`~yt.units.yt_array.YTArray` objects are converted to :class:`~astropy.units.Quantity` objects. .. _generating-2d-image-arrays: 2D Image Arrays --------------- When making a slice, a projection or an oblique slice in yt, the resultant :class:`~yt.data_objects.data_containers.YTSelectionContainer2D` object is created and contains flattened arrays of the finest available data. This means a set of arrays for the x, y, (possibly z), dx, dy, (possibly dz) and data values, for every point that constitutes the object. This presents something of a challenge for visualization, as it will require the transformation of a variable mesh of points consisting of positions and sizes into a fixed-size array that appears like an image. This process is that of pixelization, which yt handles transparently internally. You can access this functionality by constructing a :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer` and supplying to it your :class:`~yt.data_objects.data_containers.YTSelectionContainer2D` object, as well as some information about how you want the final image to look. You can specify both the bounds of the image (in the appropriate x-y plane) and the resolution of the output image. You can then have yt pixelize any field you like. .. note:: In previous versions of yt, there was a special class of FixedResolutionBuffer for off-axis slices. This is still used for off-axis SPH data projections: OffAxisFixedResolutionBuffer. To create :class:`~yt.data_objects.data_containers.YTSelectionContainer2D` objects, you can access them as described in :ref:`data-objects`, specifically the section :ref:`available-objects`. Here is an example of how to window into a slice of resolution(512, 512) with bounds of (0.3, 0.5) and (0.6, 0.8). The next step is to generate the actual 2D image array, which is accomplished by accessing the desired field. .. code-block:: python sl = ds.slice(0, 0.5) frb = FixedResolutionBuffer(sl, (0.3, 0.5, 0.6, 0.8), (512, 512)) my_image = frb["density"] This image may then be used in a hand-constructed Matplotlib image, for instance using :func:`~matplotlib.pyplot.imshow`. The buffer arrays can be saved out to disk in either HDF5 or FITS format: .. code-block:: python frb.save_as_dataset("my_images.h5", fields=[("gas", "density"), ("gas", "temperature")]) frb.export_fits( "my_images.fits", fields=[("gas", "density"), ("gas", "temperature")], clobber=True, units="kpc", ) In the HDF5 case, the created file can be reloaded just like a regular dataset with ``yt.load`` and will, itself, be a first-class dataset. For more information on this, see :ref:`saving-grid-data-containers`. In the FITS case, there is an option for setting the ``units`` of the coordinate system in the file. If you want to overwrite a file with the same name, set ``clobber=True``. The :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer` (and its :class:`~yt.visualization.fixed_resolution.OffAxisProjectionFixedResolutionBuffer` subclass) can even be exported as a 2D dataset itself, which may be operated on in the same way as any other dataset in yt: .. code-block:: python ds_frb = frb.export_dataset( fields=[("gas", "density"), ("gas", "temperature")], nprocs=8 ) sp = ds_frb.sphere("c", (100.0, "kpc")) where the ``nprocs`` parameter can be used to decompose the image into ``nprocs`` number of grids. .. _generating-profiles-and-histograms: Profiles and Histograms ----------------------- Profiles and histograms can also be generated using the :class:`~yt.visualization.profile_plotter.ProfilePlot` and :class:`~yt.visualization.profile_plotter.PhasePlot` functions (described in :ref:`how-to-make-1d-profiles` and :ref:`how-to-make-2d-profiles`). These generate profiles transparently, but the objects they handle and create can be handled manually, as well, for more control and access. The :func:`~yt.data_objects.profiles.create_profile` function can be used to generate 1, 2, and 3D profiles. Profile objects can be created from any data object (see :ref:`data-objects`, specifically the section :ref:`available-objects` for more information) and are best thought of as distribution calculations. They can either sum up or average one quantity with respect to one or more other quantities, and they do this over all the data contained in their source object. When calculating average values, the standard deviation will also be calculated. To generate a profile, one need only specify the binning fields and the field to be profiled. The binning fields are given together in a list. The :func:`~yt.data_objects.profiles.create_profile` function will guess the dimensionality of the profile based on the number of fields given. For example, a one-dimensional profile of the mass-weighted average temperature as a function of density within a sphere can be created in the following way: .. code-block:: python import yt ds = yt.load("galaxy0030/galaxy0030") source = ds.sphere("c", (10, "kpc")) profile = source.profile( [("gas", "density")], # the bin field [ ("gas", "temperature"), # profile field ("gas", "radial_velocity"), ], # profile field weight_field=("gas", "mass"), ) The binning, weight, and profile data can now be access as: .. code-block:: python print(profile.x) # bin field print(profile.weight) # weight field print(profile["gas", "temperature"]) # profile field print(profile["gas", "radial_velocity"]) # profile field The ``profile.used`` attribute gives a boolean array of the bins which actually have data. .. code-block:: python print(profile.used) If a weight field was given, the profile data will represent the weighted mean of a field. In this case, the weighted standard deviation will be calculated automatically and can be access via the ``profile.standard_deviation`` attribute. .. code-block:: python print(profile.standard_deviation["gas", "temperature"]) A two-dimensional profile of the total gas mass in bins of density and temperature can be created as follows: .. code-block:: python profile2d = source.profile( [ ("gas", "density"), ("gas", "temperature"), ], # the x bin field # the y bin field [("gas", "mass")], # the profile field weight_field=None, ) Accessing the x, y, and profile fields work just as with one-dimensional profiles: .. code-block:: python print(profile2d.x) print(profile2d.y) print(profile2d["gas", "mass"]) One of the more interesting things that is enabled with this approach is the generation of 1D profiles that correspond to 2D profiles. For instance, a phase plot that shows the distribution of mass in the density-temperature plane, with the average temperature overplotted. The :func:`~matplotlib.pyplot.pcolormesh` function can be used to manually plot the 2D profile. If you want to generate a default profile plot, you can simply call::: profile.plot() Three-dimensional profiles can be generated and accessed following the same procedures. Additional keyword arguments are available to control the following for each of the bin fields: the number of bins, min and max, units, whether to use a log or linear scale, and whether or not to do accumulation to create a cumulative distribution function. For more information, see the API documentation on the :func:`~yt.data_objects.profiles.create_profile` function. For custom bins the other keyword arguments can be overridden using the ``override_bins`` keyword argument. This accepts a dictionary with an array for each bin field or ``None`` to use the default settings. .. code-block:: python custom_bins = np.array([1e-27, 1e-25, 2e-25, 5e-25, 1e-23]) profile2d = source.profile( [("gas", "density"), ("gas", "temperature")], [("gas", "mass")], override_bins={("gas", "density"): custom_bins, ("gas", "temperature"): None}, ) .. _profile-dataframe-export: Exporting Profiles to DataFrame ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ One-dimensional profile data can be exported to a :class:`~pandas.DataFrame` object using the :meth:`yt.data_objects.profiles.Profile1D.to_dataframe` method. Bins which do not have data will have their fields filled with ``NaN``, except for the bin field itself. If you only want to export the bins which are used, set ``only_used=True``, and if you want to export the standard deviation of the profile as well, set ``include_std=True``: .. code-block:: python # Adds all of the data to the DataFrame, but non-used bins are filled with NaNs df = profile.to_dataframe() # Only adds the used bins to the DataFrame df_used = profile.to_dataframe(only_used=True) # Only adds the density and temperature fields df2 = profile.to_dataframe(fields=[("gas", "density"), ("gas", "temperature")]) # Include standard deviation df3 = profile.to_dataframe(include_std=True) The :class:`~pandas.DataFrame` can then analyzed and/or written to disk using pandas methods. Note that unit information is lost in this export. .. _profile-astropy-export: Exporting Profiles to QTable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ One-dimensional profile data also can be exported to an AstroPy :class:`~astropy.table.QTable` object. This table can then be written to disk in a number of formats, such as ASCII text or FITS files, and manipulated in a number of ways. Bins which do not have data will have their mask values set to ``False``. If you only want to export the bins which are used, set ``only_used=True``. If you want to include the standard deviation of the field in the export, set ``include_std=True``. Units are preserved in the table by converting each :class:`~yt.units.yt_array.YTArray` to an :class:`~astropy.units.Quantity`. To export the 1D profile to a Table object, simply call :meth:`yt.data_objects.profiles.Profile1D.to_astropy_table`: .. code-block:: python # Adds all of the data to the Table, but non-used bins are masked t = profile.to_astropy_table() # Only adds the used bins to the Table t_used = profile.to_astropy_table(only_used=True) # Only adds the density and temperature fields t2 = profile.to_astropy_table(fields=[("gas", "density"), ("gas", "temperature")]) # Export the standard deviation t3 = profile.to_astropy_table(include_std=True) .. _generating-line-queries: Line Queries and Planar Integrals --------------------------------- To calculate the values along a line connecting two points in a simulation, you can use the object :class:`~yt.data_objects.selection_data_containers.YTRay`, accessible as the ``ray`` property on a index. (See :ref:`data-objects` for more information on this.) To do so, you can supply two points and access fields within the returned object. For instance, this code will generate a ray between the points (0.3, 0.5, 0.9) and (0.1, 0.8, 0.5) and examine the density along that ray: .. code-block:: python ray = ds.ray((0.3, 0.5, 0.9), (0.1, 0.8, 0.5)) print(ray["gas", "density"]) The points are not ordered, so you may need to sort the data (see the example in the :class:`~yt.data_objects.selection_data_containers.YTRay` docs). Also note, the ray is traversing cells of varying length, as well as taking a varying distance to cross each cell. To determine the distance traveled by the ray within each cell (for instance, for integration) the field ``dt`` is available; this field will sum to 1.0, as the ray's path will be normalized to 1.0, independent of how far it travels through the domain. To determine the value of ``t`` at which the ray enters each cell, the field ``t`` is available. For instance: .. code-block:: python print(ray["dts"].sum()) print(ray["t"]) These can be used as inputs to, for instance, the Matplotlib function :func:`~matplotlib.pyplot.plot`, or they can be saved to disk. The volume rendering functionality in yt can also be used to calculate off-axis plane integrals, using the :class:`~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction` in a manner similar to that described in :ref:`volume_rendering`. .. _generating-xarray: Regular Grids to xarray ----------------------- Objects that subclass from :class:`~yt.data_objects.construction_data_containers.YTCoveringGrid` are able to export to `xarray `_. This enables interoperability with anything that can take xarray data. The classes that can do this are :class:`~yt.data_objects.construction_data_containers.YTCoveringGrid`, :class:`~yt.data_objects.construction_data_containers.YTArbitraryGrid`, and :class:`~yt.data_objects.construction_data_containers.YTSmoothedCoveringGrid`. For example, you can: .. code-block:: python grid = ds.r[::256j, ::256j, ::256j] obj = grid.to_xarray(fields=[("gas", "density"), ("gas", "temperature")]) The returned object, ``obj``, will now have the correct labelled axes and so forth. yt-project-yt-f043ac8/doc/source/analyzing/index.rst000066400000000000000000000014601510711153200225550ustar00rootroot00000000000000.. _analyzing: General Data Analysis ===================== This documentation describes much of the yt infrastructure for manipulating one's data to extract the relevant information. Fields, data objects, and units are at the heart of how yt represents data. Beyond this, we provide a full description for how to filter your datasets based on specific criteria, how to analyze chronological datasets from the same underlying simulation or source (i.e. time series analysis), and how to run yt in parallel on multiple processors to accomplish tasks faster. .. toctree:: :maxdepth: 2 fields ../developing/creating_derived_fields objects units filtering generating_processed_data saving_data time_series_analysis Particle_Trajectories parallel_computation astropy_integrations yt-project-yt-f043ac8/doc/source/analyzing/ionization_cube.py000066400000000000000000000026741510711153200244570ustar00rootroot00000000000000import time import h5py import numpy as np import yt from yt.utilities.parallel_tools.parallel_analysis_interface import communication_system @yt.derived_field( name="IonizedHydrogen", units="", display_name=r"\frac{\rho_{HII}}{\rho_H}" ) def IonizedHydrogen(field, data): return data["gas", "HII_Density"] / ( data["gas", "HI_Density"] + data["gas", "HII_Density"] ) ts = yt.DatasetSeries("SED800/DD*/*.index", parallel=8) ionized_z = np.zeros(ts[0].domain_dimensions, dtype="float32") t1 = time.time() for ds in ts.piter(): z = ds.current_redshift for g in yt.parallel_objects(ds.index.grids, njobs=16): i1, j1, k1 = g.get_global_startindex() # Index into our domain i2, j2, k2 = g.get_global_startindex() + g.ActiveDimensions # Look for the newly ionized gas newly_ion = (g["IonizedHydrogen"] > 0.999) & ( ionized_z[i1:i2, j1:j2, k1:k2] < z ) ionized_z[i1:i2, j1:j2, k1:k2][newly_ion] = z g.clear_data() print(f"Iteration completed {time.time() - t1:0.3e}") comm = communication_system.communicators[-1] for i in range(ionized_z.shape[0]): ionized_z[i, :, :] = comm.mpi_allreduce(ionized_z[i, :, :], op="max") print("Slab % 3i has minimum z of %0.3e" % (i, ionized_z[i, :, :].max())) t2 = time.time() print(f"Completed. {t2 - t1:0.3e}") if comm.rank == 0: f = h5py.File("IonizationCube.h5", mode="w") f.create_dataset("/z", data=ionized_z) yt-project-yt-f043ac8/doc/source/analyzing/mesh_filter.ipynb000066400000000000000000000151761510711153200242710ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Filtering Grid Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us demonstrate this with an example using the same dataset as we used with the boolean masks." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import yt\n", "\n", "ds = yt.load(\"Enzo_64/DD0042/data0042\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The only argument to a cut region is a conditional on field output from a data object. The only catch is that you *must* denote the data object in the conditional as \"obj\" regardless of the actual object's name. \n", "\n", "Here we create three new data objects which are copies of the all_data object (a region object covering the entire spatial domain of the simulation), but we've filtered on just \"hot\" material, the \"dense\" material, and the \"overpressure and fast\" material." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ad = ds.all_data()\n", "hot_ad = ad.cut_region(['obj[\"gas\", \"temperature\"] > 1e6'])\n", "dense_ad = ad.cut_region(['obj[\"gas\", \"density\"] > 5e-30'])\n", "\n", "# you can chain cut regions in two ways:\n", "dense_and_cool_ad = dense_ad.cut_region(['obj[\"gas\", \"temperature\"] < 1e5'])\n", "overpressure_and_fast_ad = ad.cut_region(\n", " [\n", " '(obj[\"gas\", \"pressure\"] > 1e-14) & (obj[\"gas\", \"velocity_magnitude\"].in_units(\"km/s\") > 1e2)'\n", " ]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also construct a cut_region using the include_ and exclude_ functions as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ad = ds.all_data()\n", "hot_ad = ad.include_above((\"gas\", \"temperature\"), 1e6)\n", "dense_ad = ad.include_above((\"gas\", \"density\"), 5e-30)\n", "\n", "# These can be chained as well\n", "dense_and_cool_ad = dense_ad.include_below((\"gas\", \"temperature\"), 1e5)\n", "overpressure_and_fast_ad = ad.include_above((\"gas\", \"pressure\"), 1e-14)\n", "overpressure_and_fast_ad = overpressure_and_fast_ad.include_above(\n", " (\"gas\", \"velocity_magnitude\"), 1e2, \"km/s\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Upon inspection of our \"hot_ad\" object, we can still get the same results as we got with the boolean masks example above:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\n", " \"Temperature of all cells:\\n ad['temperature'] = \\n%s\\n\" % ad[\"gas\", \"temperature\"]\n", ")\n", "print(\n", " \"Temperatures of all \\\"hot\\\" cells:\\n hot_ad['temperature'] = \\n%s\"\n", " % hot_ad[\"gas\", \"temperature\"]\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\n", " \"Density of dense, cool material:\\n dense_and_cool_ad['density'] = \\n%s\\n\"\n", " % dense_and_cool_ad[\"gas\", \"density\"]\n", ")\n", "print(\n", " \"Temperature of dense, cool material:\\n dense_and_cool_ad['temperature'] = \\n%s\"\n", " % dense_and_cool_ad[\"gas\", \"temperature\"]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we've constructed a `cut_region`, we can use it as a data source for further analysis. To create a plot based on a `cut_region`, use the `data_source` keyword argument provided by yt's plotting objects.\n", "\n", "Here's an example using projections:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "proj1 = yt.ProjectionPlot(ds, \"x\", (\"gas\", \"density\"), weight_field=(\"gas\", \"density\"))\n", "proj1.annotate_title(\"No Cuts\")\n", "proj1.set_figure_size(5)\n", "proj1.show()\n", "\n", "proj2 = yt.ProjectionPlot(\n", " ds, \"x\", (\"gas\", \"density\"), weight_field=(\"gas\", \"density\"), data_source=hot_ad\n", ")\n", "proj2.annotate_title(\"Hot Gas\")\n", "proj2.set_zlim((\"gas\", \"density\"), 3e-31, 3e-27)\n", "proj2.set_figure_size(5)\n", "proj2.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `data_source` keyword argument is also accepted by `SlicePlot`, `ProfilePlot` and `PhasePlot`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc1 = yt.SlicePlot(ds, \"x\", (\"gas\", \"density\"), center=\"m\")\n", "slc1.set_zlim((\"gas\", \"density\"), 3e-31, 3e-27)\n", "slc1.annotate_title(\"No Cuts\")\n", "slc1.set_figure_size(5)\n", "slc1.show()\n", "\n", "slc2 = yt.SlicePlot(ds, \"x\", (\"gas\", \"density\"), center=\"m\", data_source=dense_ad)\n", "slc2.set_zlim((\"gas\", \"density\"), 3e-31, 3e-27)\n", "slc2.annotate_title(\"Dense Gas\")\n", "slc2.set_figure_size(5)\n", "slc2.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ph1 = yt.PhasePlot(\n", " ad, (\"gas\", \"density\"), (\"gas\", \"temperature\"), (\"gas\", \"mass\"), weight_field=None\n", ")\n", "ph1.set_xlim(3e-31, 3e-27)\n", "ph1.annotate_title(\"No Cuts\")\n", "ph1.set_figure_size(5)\n", "ph1.show()\n", "\n", "ph1 = yt.PhasePlot(\n", " dense_ad,\n", " (\"gas\", \"density\"),\n", " (\"gas\", \"temperature\"),\n", " (\"gas\", \"mass\"),\n", " weight_field=None,\n", ")\n", "ph1.set_xlim(3e-31, 3e-27)\n", "ph1.annotate_title(\"Dense Gas\")\n", "ph1.set_figure_size(5)\n", "ph1.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.3" } }, "nbformat": 4, "nbformat_minor": 1 } yt-project-yt-f043ac8/doc/source/analyzing/objects.rst000066400000000000000000001041541510711153200231030ustar00rootroot00000000000000.. _Data-objects: Data Objects ============ What are Data Objects in yt? ---------------------------- Data objects (also called *Data Containers*) are used in yt as convenience structures for grouping data in logical ways that make sense in the context of the dataset as a whole. Some of the data objects are geometrical groupings of data (e.g. sphere, box, cylinder, etc.). Others represent data products derived from your dataset (e.g. slices, streamlines, surfaces). Still other data objects group multiple objects together or filter them (e.g. data collection, cut region). To generate standard plots, objects rarely need to be directly constructed. However, for detailed data inspection as well as hand-crafted derived data, objects can be exceptionally useful and even necessary. How to Create and Use an Object ------------------------------- To create an object, you usually only need a loaded dataset, the name of the object type, and the relevant parameters for your object. Here is a common example for creating a ``Region`` object that covers all of your data volume. .. code-block:: python import yt ds = yt.load("RedshiftOutput0005") ad = ds.all_data() Alternatively, we could create a sphere object of radius 1 kpc on location [0.5, 0.5, 0.5]: .. code-block:: python import yt ds = yt.load("RedshiftOutput0005") sp = ds.sphere([0.5, 0.5, 0.5], (1, "kpc")) After an object has been created, it can be used as a data_source to certain tasks like ``ProjectionPlot`` (see :class:`~yt.visualization.plot_window.ProjectionPlot`), one can compute the bulk quantities associated with that object (see :ref:`derived-quantities`), or the data can be examined directly. For example, if you want to figure out the temperature at all indexed locations in the central sphere of your dataset you could: .. code-block:: python import yt ds = yt.load("RedshiftOutput0005") sp = ds.sphere([0.5, 0.5, 0.5], (1, "kpc")) # Show all temperature values print(sp["gas", "temperature"]) # Print things in a more human-friendly manner: one temperature at a time print("(x, y, z) Temperature") print("-----------------------") for i in range(sp["gas", "temperature"].size): print( "(%f, %f, %f) %f" % ( sp["gas", "x"][i], sp["gas", "y"][i], sp["gas", "z"][i], sp["gas", "temperature"][i], ) ) Data objects can also be cloned; for instance: .. code-block:: python import yt ds = yt.load("RedshiftOutput0005") sp = ds.sphere([0.5, 0.5, 0.5], (1, "kpc")) sp_copy = sp.clone() This can be useful for when manually chunking data or exploring different field parameters. .. _quickly-selecting-data: Slicing Syntax for Selecting Data --------------------------------- yt provides a mechanism for easily selecting data while doing interactive work on the command line. This allows for region selection based on the full domain of the object. Selecting in this manner is exposed through a slice-like syntax. All of these attributes are exposed through the ``RegionExpression`` object, which is an attribute of a ``DataSet`` object, called ``r``. Getting All The Data ^^^^^^^^^^^^^^^^^^^^ The ``.r`` attribute serves as a persistent means of accessing the full data from a dataset. You can access this shorthand operation by querying any field on the ``.r`` object, like so: .. code-block:: python ds = yt.load("RedshiftOutput0005") rho = ds.r["gas", "density"] This will return a *flattened* array of data. The region expression object (``r``) doesn't have any derived quantities on it. This is completely equivalent to this set of statements: .. code-block:: python ds = yt.load("RedshiftOutput0005") dd = ds.all_data() rho = dd["gas", "density"] .. warning:: One thing to keep in mind with accessing data in this way is that it is *persistent*. It is loaded into memory, and then retained until the dataset is deleted or garbage collected. Selecting Multiresolution Regions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To select rectilinear regions, where the data is selected the same way that it is selected in a :ref:`region-reference`, you can utilize slice-like syntax, supplying start and stop, but not supplying a step argument. This requires that three components of the slice must be specified. These take a start and a stop, and are for the three axes in simulation order (if your data is ordered z, y, x for instance, this would be in z, y, x order). The slices can have both position and, optionally, unit values. These define the value with respect to the ``domain_left_edge`` of the dataset. So for instance, you could specify it like so: .. code-block:: python ds.r[(100, "kpc"):(200, "kpc"), :, :] This would return a region that included everything between 100 kpc from the left edge of the dataset to 200 kpc from the left edge of the dataset in the first dimension, and which spans the entire dataset in the second and third dimensions. By default, if the units are unspecified, they are in the "native" code units of the dataset. This works in all types of datasets, as well. For instance, if you have a geographic dataset (which is usually ordered latitude, longitude, altitude) you can easily select, for instance, one hemisphere with a region selection: .. code-block:: python ds.r[:, -180:0, :] If you specify a single slice, it will be repeated along all three dimensions. For instance, this will give all data: .. code-block:: python ds.r[:] And this will select a box running from 0.4 to 0.6 along all three dimensions: .. code-block:: python ds.r[0.4:0.6] .. _arbitrary-grid-selection: Selecting Fixed Resolution Regions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ yt also provides functionality for selecting regions that have been turned into voxels. This returns an :ref:`arbitrary-grid` object. It can be created by specifying a complex slice "step", where the start and stop follow the same rules as above. This is similar to how the numpy ``mgrid`` operation works. For instance, this code block will generate a grid covering the full domain, but converted to being 21x35x100 dimensions: .. code-block:: python region = ds.r[::21j, ::35j, ::100j] The left and right edges, as above, can be specified to provide bounds as well. For instance, to select a 10 meter cube, with 24 cells in each dimension, we could supply: .. code-block:: python region = ds.r[(20, "m"):(30, "m"):24j, (30, "m"):(40, "m"):24j, (7, "m"):(17, "m"):24j] This can select both particles and mesh fields. Mesh fields will be 3D arrays, and generated through volume-weighted overlap calculations. Selecting Slices ^^^^^^^^^^^^^^^^ If one dimension is specified as a single value, that will be the dimension along which a slice is made. This provides a simple means of generating a slice from a subset of the data. For instance, to create a slice of a dataset, you can very simply specify the full domain along two axes: .. code-block:: python sl = ds.r[:, :, 0.25] This can also be very easily plotted: .. code-block:: python sl = ds.r[:, :, 0.25] sl.plot() This accepts arguments the same way: .. code-block:: python sl = ds.r[(20.1, "km"):(31.0, "km"), (504.143, "m"):(1000.0, "m"), (900.1, "m")] sl.plot() Making Image Buffers ^^^^^^^^^^^^^^^^^^^^ Using the slicing syntax above for choosing a slice, if you also provide an imaginary step value you can obtain a :class:`~yt.visualization.api.FixedResolutionBuffer` of the chosen resolution. For instance, to obtain a 1024 by 1024 buffer covering the entire domain but centered at 0.5 in code units, you can do: .. code-block:: python frb = ds.r[0.5, ::1024j, ::1024j] This ``frb`` object then can be queried like a normal fixed resolution buffer, and it will return arrays of shape (1024, 1024). Making Rays ^^^^^^^^^^^ The slicing syntax can also be used select 1D rays of points, whether along an axis or off-axis. To create a ray along an axis: .. code-block:: python ortho_ray = ds.r[(500.0, "kpc"), (200, "kpc"):(300.0, "kpc"), (-2.0, "Mpc")] To create a ray off-axis, use a single slice between the start and end points of the ray: .. code-block:: python start = [0.1, 0.2, 0.3] # interpreted in code_length end = [0.4, 0.5, 0.6] # interpreted in code_length ray = ds.r[start:end] As for the other slicing options, combinations of unitful quantities with even different units can be used. Here's a somewhat convoluted (yet working) example: .. code-block:: python start = ((500.0, "kpc"), (0.2, "Mpc"), (100.0, "kpc")) end = ((1.0, "Mpc"), (300.0, "kpc"), (0.0, "kpc")) ray = ds.r[start:end] Making Fixed-Resolution Rays ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Rays can also be constructed to have fixed resolution if an imaginary step value is provided, similar to the 2 and 3-dimensional cases described above. This works for rays directed along an axis: .. code-block:: python ortho_ray = ds.r[0.1:0.6:500j, 0.3, 0.2] or off-axis rays as well: .. code-block:: python start = [0.1, 0.2, 0.3] # interpreted in code_length end = [0.4, 0.5, 0.6] # interpreted in code_length ray = ds.r[start:end:100j] Selecting Points ^^^^^^^^^^^^^^^^ Finally, you can quickly select a single point within the domain by providing a single coordinate for every axis: .. code-block:: python pt = ds.r[(10.0, "km"), (200, "m"), (1.0, "km")] Querying this object for fields will give you the value of the field at that point. .. _available-objects: Available Objects ----------------- As noted above, there are numerous types of objects. Here we group them into: * *Geometric Objects* Data is selected based on spatial shapes in the dataset * *Filtering Objects* Data is selected based on other field criteria * *Collection Objects* Multiple objects grouped together * *Construction Objects* Objects represent some sort of data product constructed by additional analysis If you want to create your own custom data object type, see :ref:`creating-objects`. .. _geometric-objects: Geometric Objects ^^^^^^^^^^^^^^^^^ For 0D, 1D, and 2D geometric objects, if the extent of the object intersects a grid cell, then the cell is included in the object; however, for 3D objects the *center* of the cell must be within the object in order for the grid cell to be incorporated. 0D Objects """""""""" **Point** | Class :class:`~yt.data_objects.selection_data_containers.YTPoint` | Usage: ``point(coord, ds=None, field_parameters=None, data_source=None)`` | A point defined by a single cell at specified coordinates. 1D Objects """""""""" **Ray (Axis-Aligned)** | Class :class:`~yt.data_objects.selection_data_containers.YTOrthoRay` | Usage: ``ortho_ray(axis, coord, ds=None, field_parameters=None, data_source=None)`` | A line (of data cells) stretching through the full domain aligned with one of the x,y,z axes. Defined by an axis and a point to be intersected. Please see this :ref:`note about ray data value ordering `. **Ray (Arbitrarily-Aligned)** | Class :class:`~yt.data_objects.selection_data_containers.YTRay` | Usage: ``ray(start_coord, end_coord, ds=None, field_parameters=None, data_source=None)`` | A line (of data cells) defined by arbitrary start and end coordinates. Please see this :ref:`note about ray data value ordering `. 2D Objects """""""""" **Slice (Axis-Aligned)** | Class :class:`~yt.data_objects.selection_data_containers.YTSlice` | Usage: ``slice(axis, coord, center=None, ds=None, field_parameters=None, data_source=None)`` | A plane normal to one of the axes and intersecting a particular coordinate. **Slice (Arbitrarily-Aligned)** | Class :class:`~yt.data_objects.selection_data_containers.YTCuttingPlane` | Usage: ``cutting(normal, coord, north_vector=None, ds=None, field_parameters=None, data_source=None)`` | A plane normal to a specified vector and intersecting a particular coordinate. .. _region-reference: 3D Objects """""""""" **All Data** | Function :meth:`~yt.data_objects.static_output.Dataset.all_data` | Usage: ``all_data(find_max=False)`` | ``all_data()`` is a wrapper on the Box Region class which defaults to creating a Region covering the entire dataset domain. It is effectively ``ds.region(ds.domain_center, ds.domain_left_edge, ds.domain_right_edge)``. **Box Region** | Class :class:`~yt.data_objects.selection_data_containers.YTRegion` | Usage: ``region(center, left_edge, right_edge, fields=None, ds=None, field_parameters=None, data_source=None)`` | Alternatively: ``box(left_edge, right_edge, fields=None, ds=None, field_parameters=None, data_source=None)`` | A box-like region aligned with the grid axis orientation. It is defined by a left_edge, a right_edge, and a center. The left_edge and right_edge are the minimum and maximum bounds in the three axes respectively. The center is arbitrary and must only be contained within the left_edge and right_edge. By using the ``box`` wrapper, the center is assumed to be the midpoint between the left and right edges. **Disk/Cylinder** | Class: :class:`~yt.data_objects.selection_data_containers.YTDisk` | Usage: ``disk(center, normal, radius, height, fields=None, ds=None, field_parameters=None, data_source=None)`` | A cylinder defined by a point at the center of one of the circular bases, a normal vector to it defining the orientation of the length of the cylinder, and radius and height values for the cylinder's dimensions. Note: ``height`` is the distance from midplane to the top or bottom of the cylinder, i.e., ``height`` is half that of the cylinder object that is created. **Ellipsoid** | Class :class:`~yt.data_objects.selection_data_containers.YTEllipsoid` | Usage: ``ellipsoid(center, semi_major_axis_length, semi_medium_axis_length, semi_minor_axis_length, semi_major_vector, tilt, fields=None, ds=None, field_parameters=None, data_source=None)`` | An ellipsoid with axis magnitudes set by ``semi_major_axis_length``, ``semi_medium_axis_length``, and ``semi_minor_axis_length``. ``semi_major_vector`` sets the direction of the ``semi_major_axis``. ``tilt`` defines the orientation of the semi-medium and semi_minor axes. **Sphere** | Class :class:`~yt.data_objects.selection_data_containers.YTSphere` | Usage: ``sphere(center, radius, ds=None, field_parameters=None, data_source=None)`` | A sphere defined by a central coordinate and a radius. **Minimal Bounding Sphere** | Class :class:`~yt.data_objects.selection_data_containers.YTMinimalSphere` | Usage: ``minimal_sphere(points, ds=None, field_parameters=None, data_source=None)`` | A sphere that contains all the points passed as argument. .. _collection-objects: Filtering and Collection Objects ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ See also the section on :ref:`filtering-data`. **Intersecting Regions** | Most Region objects provide a data_source parameter, which allows you to subselect | one region from another (in the coordinate system of the DataSet). Note, this can | easily lead to empty data for non-intersecting regions. | Usage: ``slice(axis, coord, ds, data_source=sph)`` **Union Regions** | Usage: ``union()`` | See :ref:`boolean_data_objects`. **Intersection Regions** | Usage: ``intersection()`` | See :ref:`boolean_data_objects`. **Filter** | Class :class:`~yt.data_objects.selection_data_containers.YTCutRegion` | Usage: ``cut_region(base_object, conditionals, ds=None, field_parameters=None)`` | A ``cut_region`` is a filter which can be applied to any other data object. The filter is defined by the conditionals present, which apply cuts to the data in the object. A ``cut_region`` will work for either particle fields or mesh fields, but not on both simultaneously. For more detailed information and examples, see :ref:`cut-regions`. **Collection of Data Objects** | Class :class:`~yt.data_objects.selection_data_containers.YTDataCollection` | Usage: ``data_collection(center, obj_list, ds=None, field_parameters=None)`` | A ``data_collection`` is a list of data objects that can be sampled and processed as a whole in a single data object. .. _construction-objects: Construction Objects ^^^^^^^^^^^^^^^^^^^^ **Fixed-Resolution Region** | Class :class:`~yt.data_objects.construction_data_containers.YTCoveringGrid` | Usage: ``covering_grid(level, left_edge, dimensions, fields=None, ds=None, num_ghost_zones=0, use_pbar=True, field_parameters=None)`` | A 3D region with all data extracted to a single, specified resolution. See :ref:`examining-grid-data-in-a-fixed-resolution-array`. **Fixed-Resolution Region with Smoothing** | Class :class:`~yt.data_objects.construction_data_containers.YTSmoothedCoveringGrid` | Usage: ``smoothed_covering_grid(level, left_edge, dimensions, fields=None, ds=None, num_ghost_zones=0, use_pbar=True, field_parameters=None)`` | A 3D region with all data extracted and interpolated to a single, specified resolution. Identical to covering_grid, except that it interpolates as necessary from coarse regions to fine. See :ref:`examining-grid-data-in-a-fixed-resolution-array`. **Fixed-Resolution Region** | Class :class:`~yt.data_objects.construction_data_containers.YTArbitraryGrid` | Usage: ``arbitrary_grid(left_edge, right_edge, dimensions, ds=None, field_parameters=None)`` | When particles are deposited on to mesh fields, they use the existing mesh structure, but this may have too much or too little resolution relative to the particle locations (or it may not exist at all!). An `arbitrary_grid` provides a means for generating a new independent mesh structure for particle deposition and simple mesh field interpolation. See :ref:`arbitrary-grid` for more information. **Projection** | Class :class:`~yt.data_objects.construction_data_containers.YTQuadTreeProj` | Usage: ``proj(field, axis, weight_field=None, center=None, ds=None, data_source=None, method="integrate", field_parameters=None)`` | A 2D projection of a 3D volume along one of the axis directions. By default, this is a line integral through the entire simulation volume (although it can be a subset of that volume specified by a data object with the ``data_source`` keyword). Alternatively, one can specify a weight_field and different ``method`` values to change the nature of the projection outcome. See :ref:`projection-types` for more information. **Streamline** | Class :class:`~yt.data_objects.construction_data_containers.YTStreamline` | Usage: ``streamline(coord_list, length, fields=None, ds=None, field_parameters=None)`` | A ``streamline`` can be traced out by identifying a starting coordinate (or list of coordinates) and allowing it to trace a vector field, like gas velocity. See :ref:`streamlines` for more information. **Surface** | Class :class:`~yt.data_objects.construction_data_containers.YTSurface` | Usage: ``surface(data_source, field, field_value)`` | The surface defined by all an isocontour in any mesh field. An existing data object must be provided as the source, as well as a mesh field and the value of the field which you desire the isocontour. See :ref:`extracting-isocontour-information`. .. _derived-quantities: Processing Objects: Derived Quantities -------------------------------------- Derived quantities are a way of calculating some bulk quantities associated with all of the grid cells contained in a data object. Derived quantities can be accessed via the ``quantities`` interface. Here is an example of how to get the angular momentum vector calculated from all the cells contained in a sphere at the center of our dataset. .. code-block:: python import yt ds = yt.load("my_data") sp = ds.sphere("c", (10, "kpc")) print(sp.quantities.angular_momentum_vector()) Some quantities can be calculated for a specific particle type only. For example, to get the center of mass of only the stars within the sphere: .. code-block:: python import yt ds = yt.load("my_data") sp = ds.sphere("c", (10, "kpc")) print( sp.quantities.center_of_mass( use_gas=False, use_particles=True, particle_type="star" ) ) Quickly Processing Data ^^^^^^^^^^^^^^^^^^^^^^^ Most data objects now have multiple numpy-like methods that allow you to quickly process data. More of these methods will be added over time and added to this list. Most, if not all, of these map to other yt operations and are designed as syntactic sugar to slightly simplify otherwise somewhat obtuse pipelines. These operations are parallelized. You can compute the extrema of a field by using the ``max`` or ``min`` functions. This will cache the extrema in between, so calling ``min`` right after ``max`` will be considerably faster. Here is an example. .. code-block:: python import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") reg = ds.r[0.3:0.6, 0.2:0.4, 0.9:0.95] min_rho = reg.min(("gas", "density")) max_rho = reg.max(("gas", "density")) This is equivalent to: .. code-block:: python min_rho, max_rho = reg.quantities.extrema(("gas", "density")) The ``max`` operation can also compute the maximum intensity projection: .. code-block:: python proj = reg.max(("gas", "density"), axis="x") proj.plot() This is equivalent to: .. code-block:: python proj = ds.proj(("gas", "density"), "x", data_source=reg, method="max") proj.plot() The same can be done with the ``min`` operation, computing a minimum intensity projection: .. code-block:: python proj = reg.min(("gas", "density"), axis="x") proj.plot() This is equivalent to: .. code-block:: python proj = ds.proj(("gas", "density"), "x", data_source=reg, method="min") proj.plot() You can also compute the ``mean`` value, which accepts a field, axis, and weight function. If the axis is not specified, it will return the average value of the specified field, weighted by the weight argument. The weight argument defaults to ``ones``, which performs an arithmetic average. For instance: .. code-block:: python mean_rho = reg.mean(("gas", "density")) rho_by_vol = reg.mean(("gas", "density"), weight=("gas", "cell_volume")) This is equivalent to: .. code-block:: python mean_rho = reg.quantities.weighted_average( ("gas", "density"), weight_field=("index", "ones") ) rho_by_vol = reg.quantities.weighted_average( ("gas", "density"), weight_field=("gas", "cell_volume") ) If an axis is provided, it will project along that axis and return it to you: .. code-block:: python rho_proj = reg.mean(("gas", "temperature"), axis="y", weight=("gas", "density")) rho_proj.plot() You can also compute the ``std`` (standard deviation), which accepts a field, axis, and weight function. If the axis is not specified, it will return the standard deviation of the specified field, weighted by the weight argument. The weight argument defaults to ``ones``. For instance: .. code-block:: python std_rho = reg.std(("gas", "density")) std_rho_by_vol = reg.std(("gas", "density"), weight=("gas", "cell_volume")) This is equivalent to: .. code-block:: python std_rho = reg.quantities.weighted_standard_deviation( ("gas", "density"), weight_field=("index", "ones") ) std_rho_by_vol = reg.quantities.weighted_standard_deviation( ("gas", "density"), weight_field=("gas", "cell_volume") ) If an axis is provided, it will project along that axis and return it to you: .. code-block:: python vy_std = reg.std(("gas", "velocity_y"), axis="y", weight=("gas", "density")) vy_std.plot() The ``sum`` function will add all the values in the data object. It accepts a field and, optionally, an axis. If the axis is left unspecified, it will sum the values in the object: .. code-block:: python vol = reg.sum(("gas", "cell_volume")) If the axis is specified, it will compute a projection using the method ``sum`` (which does *not* take into account varying path length!) and return that to you. .. code-block:: python cell_count = reg.sum(("index", "ones"), axis="z") cell_count.plot() To compute a projection where the path length *is* taken into account, you can use the ``integrate`` function: .. code-block:: python proj = reg.integrate(("gas", "density"), "x") All of these projections supply the data object as their base input. Often, it can be useful to sample a field at the minimum and maximum of a different field. You can use the ``argmax`` and ``argmin`` operations to do this. .. code-block:: python reg.argmin(("gas", "density"), axis=("gas", "temperature")) This will return the temperature at the minimum density. If you don't specify an ``axis``, it will return the spatial position of the maximum value of the queried field. Here is an example:: x, y, z = reg.argmin(("gas", "density")) Available Derived Quantities ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Angular Momentum Vector** | Class :class:`~yt.data_objects.derived_quantities.AngularMomentumVector` | Usage: ``angular_momentum_vector(use_gas=True, use_particles=True, particle_type='all')`` | The mass-weighted average angular momentum vector of the particles, gas, or both. The quantity can be calculated for all particles or a given particle_type only. **Bulk Velocity** | Class :class:`~yt.data_objects.derived_quantities.BulkVelocity` | Usage: ``bulk_velocity(use_gas=True, use_particles=True, particle_type='all')`` | The mass-weighted average velocity of the particles, gas, or both. The quantity can be calculated for all particles or a given particle_type only. **Center of Mass** | Class :class:`~yt.data_objects.derived_quantities.CenterOfMass` | Usage: ``center_of_mass(use_cells=True, use_particles=False, particle_type='all')`` | The location of the center of mass. By default, it computes of the *non-particle* data in the object, but it can be used on particles, gas, or both. The quantity can be calculated for all particles or a given particle_type only. **Extrema** | Class :class:`~yt.data_objects.derived_quantities.Extrema` | Usage: ``extrema(fields, non_zero=False)`` | The extrema of a field or list of fields. **Maximum Location Sampling** | Class :class:`~yt.data_objects.derived_quantities.SampleAtMaxFieldValues` | Usage: ``sample_at_max_field_values(fields, sample_fields)`` | The value of sample_fields at the maximum value in fields. **Minimum Location Sampling** | Class :class:`~yt.data_objects.derived_quantities.SampleAtMinFieldValues` | Usage: ``sample_at_min_field_values(fields, sample_fields)`` | The value of sample_fields at the minimum value in fields. **Minimum Location** | Class :class:`~yt.data_objects.derived_quantities.MinLocation` | Usage: ``min_location(fields)`` | The minimum of a field or list of fields as well as the x,y,z location of that minimum. **Maximum Location** | Class :class:`~yt.data_objects.derived_quantities.MaxLocation` | Usage: ``max_location(fields)`` | The maximum of a field or list of fields as well as the x,y,z location of that maximum. **Spin Parameter** | Class :class:`~yt.data_objects.derived_quantities.SpinParameter` | Usage: ``spin_parameter(use_gas=True, use_particles=True, particle_type='all')`` | The spin parameter for the baryons using the particles, gas, or both. The quantity can be calculated for all particles or a given particle_type only. **Total Mass** | Class :class:`~yt.data_objects.derived_quantities.TotalMass` | Usage: ``total_mass()`` | The total mass of the object as a tuple of (total gas, total particle) mass. **Total of a Field** | Class :class:`~yt.data_objects.derived_quantities.TotalQuantity` | Usage: ``total_quantity(fields)`` | The sum of a given field (or list of fields) over the entire object. **Weighted Average of a Field** | Class :class:`~yt.data_objects.derived_quantities.WeightedAverageQuantity` | Usage: ``weighted_average_quantity(fields, weight)`` | The weighted average of a field (or list of fields) over an entire data object. If you want an unweighted average, then set your weight to be the field: ``ones``. **Weighted Standard Deviation of a Field** | Class :class:`~yt.data_objects.derived_quantities.WeightedStandardDeviation` | Usage: ``weighted_standard_deviation(fields, weight)`` | The weighted standard deviation of a field (or list of fields) over an entire data object and the weighted mean. If you want an unweighted standard deviation, then set your weight to be the field: ``ones``. .. _arbitrary-grid: Arbitrary Grids Objects ----------------------- The covering grid and smoothed covering grid objects mandate that they be exactly aligned with the mesh. This is a holdover from the time when yt was used exclusively for data that came in regularly structured grid patches, and does not necessarily work as well for data that is composed of discrete objects like particles. To augment this, the :class:`~yt.data_objects.construction_data_containers.YTArbitraryGrid` object was created, which enables construction of meshes (onto which particles can be deposited or smoothed) in arbitrary regions. This eliminates any assumptions on yt's part about how the data is organized, and will allow for more fine-grained control over visualizations. An example of creating an arbitrary grid would be to construct one, then query the deposited particle density, like so: .. code-block:: python import yt ds = yt.load("snapshot_010.hdf5") obj = ds.arbitrary_grid([0.0, 0.0, 0.0], [0.99, 0.99, 0.99], dims=[128, 128, 128]) print(obj["deposit", "all_density"]) While these cannot yet be used as input to projections or slices, slices and projections can be taken of the data in them and visualized by hand. These objects, as of yt 3.3, are now also able to "voxelize" mesh fields. This means that you can query the "density" field and it will return the density field as deposited, identically to how it would be deposited in a fixed resolution buffer. Note that this means that contributions from misaligned or partially-overlapping cells are added in a volume-weighted way, which makes it inappropriate for some types of analysis. .. _boolean_data_objects: Combining Objects: Boolean Data Objects --------------------------------------- A special type of data object is the *boolean* data object, which works with data selection objects of any dimension. It is built by relating already existing data objects with the bitwise operators for AND, OR and XOR, as well as the subtraction operator. These are created by using the operators ``&`` for an intersection ("AND"), ``|`` for a union ("OR"), ``^`` for an exclusive or ("XOR"), and ``+`` and ``-`` for addition ("OR") and subtraction ("NEG"). Here are some examples: .. code-block:: python import yt ds = yt.load("snapshot_010.hdf5") sp1 = ds.sphere("c", (0.1, "unitary")) sp2 = ds.sphere(sp1.center + 2.0 * sp1.radius, (0.2, "unitary")) sp3 = ds.sphere("c", (0.05, "unitary")) new_obj = sp1 + sp2 cutout = sp1 - sp3 sp4 = sp1 ^ sp2 sp5 = sp1 & sp2 Note that the ``+`` operation and the ``|`` operation are identical. For when multiple objects are to be combined in an intersection or a union, there are the data objects ``intersection`` and ``union`` which can be called, and which will yield slightly higher performance than a sequence of calls to ``+`` or ``&``. For instance: .. code-block:: python import yt ds = yt.load("Enzo_64/DD0043/data0043") sp1 = ds.sphere((0.1, 0.2, 0.3), (0.05, "unitary")) sp2 = ds.sphere((0.2, 0.2, 0.3), (0.10, "unitary")) sp3 = ds.sphere((0.3, 0.2, 0.3), (0.15, "unitary")) isp = ds.intersection([sp1, sp2, sp3]) usp = ds.union([sp1, sp2, sp3]) The ``isp`` and ``usp`` objects will act the same as a set of chained ``&`` and ``|`` operations (respectively) but are somewhat easier to construct. .. _extracting-connected-sets: Connected Sets and Clump Finding -------------------------------- The underlying machinery used in :ref:`clump_finding` is accessible from any data object. This includes the ability to obtain and examine topologically connected sets. These sets are identified by examining cells between two threshold values and connecting them. What is returned to the user is a list of the intervals of values found, and extracted regions that contain only those cells that are connected. To use this, call :meth:`~yt.data_objects.data_containers.YTSelectionContainer3D.extract_connected_sets` on any 3D data object. This requests a field, the number of levels of levels sets to extract, the min and the max value between which sets will be identified, and whether or not to conduct it in log space. .. code-block:: python sp = ds.sphere("max", (1.0, "pc")) contour_values, connected_sets = sp.extract_connected_sets( ("gas", "density"), 3, 1e-30, 1e-20 ) The first item, ``contour_values``, will be an array of the min value for each set of level sets. The second (``connected_sets``) will be a dict of dicts. The key for the first (outer) dict is the level of the contour, corresponding to ``contour_values``. The inner dict returned is keyed by the contour ID. It contains :class:`~yt.data_objects.selection_data_containers.YTCutRegion` objects. These can be queried just as any other data object. The clump finder (:ref:`clump_finding`) differs from the above method in that the contour identification is performed recursively within each individual structure, and structures can be kept or remerged later based on additional criteria, such as gravitational boundedness. .. _object-serialization: Storing and Loading Objects --------------------------- Often, when operating interactively or via the scripting interface, it is convenient to save an object to disk and then restart the calculation later or transfer the data from a container to another filesystem. This can be particularly useful when working with extremely large datasets. Field data can be saved to disk in a format that allows for it to be reloaded just like a regular dataset. For information on how to do this, see :ref:`saving-data-containers`. yt-project-yt-f043ac8/doc/source/analyzing/parallel_computation.rst000066400000000000000000000714751510711153200257010ustar00rootroot00000000000000.. _parallel-computation: Parallel Computation With yt ============================ yt has been instrumented with the ability to compute many -- most, even -- quantities in parallel. This utilizes the package `mpi4py `_ to parallelize using the Message Passing Interface, typically installed on clusters. .. _capabilities: Capabilities ------------ Currently, yt is able to perform the following actions in parallel: * Projections (:ref:`projection-plots`) * Slices (:ref:`slice-plots`) * Cutting planes (oblique slices) (:ref:`off-axis-slices`) * Covering grids (:ref:`examining-grid-data-in-a-fixed-resolution-array`) * Derived Quantities (total mass, angular momentum, etc) * 1-, 2-, and 3-D profiles (:ref:`generating-profiles-and-histograms`) * Halo analysis (:ref:`halo-analysis`) * Volume rendering (:ref:`volume_rendering`) * Isocontours & flux calculations (:ref:`extracting-isocontour-information`) This list covers just about every action yt can take! Additionally, almost all scripts will benefit from parallelization with minimal modification. The goal of Parallel-yt has been to retain API compatibility and abstract all parallelism. Setting Up Parallel yt -------------------------- To run scripts in parallel, you must first install `mpi4py `_ as well as an MPI library, if one is not already available on your system. Instructions for doing so are provided on the mpi4py website, but you may have luck by just running: .. code-block:: bash $ python -m pip install mpi4py If you have an Anaconda installation of yt and there is no MPI library on the system you are using try: .. code-block:: bash $ conda install mpi4py This will install `MPICH2 `_ and will interfere with other MPI libraries that are already installed. Therefore, it is preferable to use the ``pip`` installation method. Once mpi4py has been installed, you're all done! You just need to launch your scripts with ``mpirun`` (or equivalent) and signal to yt that you want to run them in parallel by invoking the ``yt.enable_parallelism()`` function in your script. In general, that's all it takes to get a speed benefit on a multi-core machine. Here is an example on an 8-core desktop: .. code-block:: bash $ mpirun -np 8 python script.py Throughout its normal operation, yt keeps you aware of what is happening with regular messages to the stderr usually prefaced with: .. code-block:: bash yt : [INFO ] YYY-MM-DD HH:MM:SS However, when operating in parallel mode, yt outputs information from each of your processors to this log mode, as in: .. code-block:: bash P000 yt : [INFO ] YYY-MM-DD HH:MM:SS P001 yt : [INFO ] YYY-MM-DD HH:MM:SS in the case of two cores being used. It's important to note that all of the processes listed in :ref:`capabilities` work in parallel -- and no additional work is necessary to parallelize those processes. Running a yt Script in Parallel ------------------------------- Many basic yt operations will run in parallel if yt's parallelism is enabled at startup. For example, the following script finds the maximum density location in the simulation and then makes a plot of the projected density: .. code-block:: python import yt yt.enable_parallelism() ds = yt.load("RD0035/RedshiftOutput0035") v, c = ds.find_max(("gas", "density")) print(v, c) p = yt.ProjectionPlot(ds, "x", ("gas", "density")) p.save() If this script is run in parallel, two of the most expensive operations - finding of the maximum density and the projection will be calculated in parallel. If we save the script as ``my_script.py``, we would run it on 16 MPI processes using the following Bash command: .. code-block:: bash $ mpirun -np 16 python my_script.py .. note:: If you run into problems, the you can use :ref:`remote-debugging` to examine what went wrong. How do I run my yt job on a subset of available processes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ You can set the ``communicator`` keyword in the :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.enable_parallelism` call to a specific MPI communicator to specify a subset of available MPI processes. If none is specified, it defaults to ``COMM_WORLD``. Creating Parallel and Serial Sections in a Script +++++++++++++++++++++++++++++++++++++++++++++++++ Many yt operations will automatically run in parallel (see the next section for a full enumeration), however some operations, particularly ones that print output or save data to the filesystem, will be run by all processors in a parallel script. For example, in the script above the lines ``print(v, c)`` and ``p.save()`` will be run on all 16 processors. This means that your terminal output will contain 16 repetitions of the output of the print statement and the plot will be saved to disk 16 times (overwritten each time). yt provides two convenience functions that make it easier to run most of a script in parallel but run some subset of the script on only one processor. The first, :func:`~yt.funcs.is_root`, returns ``True`` if run on the 'root' processor (the processor with MPI rank 0) and ``False`` otherwise. One could rewrite the above script to take advantage of :func:`~yt.funcs.is_root` like so: .. code-block:: python import yt yt.enable_parallelism() ds = yt.load("RD0035/RedshiftOutput0035") v, c = ds.find_max(("gas", "density")) p = yt.ProjectionPlot(ds, "x", ("gas", "density")) if yt.is_root(): print(v, c) p.save() The second function, :func:`~yt.funcs.only_on_root` accepts the name of a function as well as a set of parameters and keyword arguments to pass to the function. This is useful when the serial component of your parallel script would clutter the script or if you like writing your scripts as a series of isolated function calls. I can rewrite the example from the beginning of this section once more using :func:`~yt.funcs.only_on_root` to give you the flavor of how to use it: .. code-block:: python import yt yt.enable_parallelism() def print_and_save_plot(v, c, plot, verbose=True): if verbose: print(v, c) plot.save() ds = yt.load("RD0035/RedshiftOutput0035") v, c = ds.find_max(("gas", "density")) p = yt.ProjectionPlot(ds, "x", ("gas", "density")) yt.only_on_root(print_and_save_plot, v, c, plot, verbose=True) Types of Parallelism -------------------- In order to divide up the work, yt will attempt to send different tasks to different processors. However, to minimize inter-process communication, yt will decompose the information in different ways based on the task. Spatial Decomposition +++++++++++++++++++++ During this process, the index will be decomposed along either all three axes or along an image plane, if the process is that of projection. This type of parallelism is overall less efficient than grid-based parallelism, but it has been shown to obtain good results overall. The following operations use spatial decomposition: * :ref:`halo-analysis` * :ref:`volume_rendering` Grid Decomposition ++++++++++++++++++ The alternative to spatial decomposition is a simple round-robin of data chunks, which could be grids, octs, or whatever chunking mechanism is used by the code frontend begin used. This process allows yt to pool data access to a given data file, which ultimately results in faster read times and better parallelism. The following operations use chunk decomposition: * Projections (see :ref:`available-objects`) * Slices (see :ref:`available-objects`) * Cutting planes (see :ref:`available-objects`) * Covering grids (see :ref:`construction-objects`) * Derived Quantities (see :ref:`derived-quantities`) * 1-, 2-, and 3-D profiles (see :ref:`generating-profiles-and-histograms`) * Isocontours & flux calculations (see :ref:`surfaces`) Parallelization over Multiple Objects and Datasets ++++++++++++++++++++++++++++++++++++++++++++++++++ If you have a set of computational steps that need to apply identically and independently to several different objects or datasets, a so-called `embarrassingly parallel `_ task, yt can do that easily. See the sections below on :ref:`parallelizing-your-analysis` and :ref:`parallel-time-series-analysis`. Use of ``piter()`` ^^^^^^^^^^^^^^^^^^ If you use parallelism over objects or datasets, you will encounter the :func:`~yt.data_objects.time_series.DatasetSeries.piter` function. :func:`~yt.data_objects.time_series.DatasetSeries.piter` is a parallel iterator, which effectively doles out each item of a DatasetSeries object to a different processor. In serial processing, you might iterate over a DatasetSeries by: .. code-block:: python for dataset in dataset_series: ... # process But in parallel, you can use ``piter()`` to force each dataset to go to a different processor: .. code-block:: python yt.enable_parallelism() for dataset in dataset_series.piter(): ... # process In order to store information from the parallel processing step to a data structure that exists on all of the processors operating in parallel we offer the ``storage`` keyword in the :func:`~yt.data_objects.time_series.DatasetSeries.piter` function. You may define an empty dictionary and include it as the keyword argument ``storage`` to :func:`~yt.data_objects.time_series.DatasetSeries.piter`. Then, during the processing step, you can access this dictionary as the ``sto`` object. After the loop is finished, the dictionary is re-aggregated from all of the processors, and you can access the contents: .. code-block:: python yt.enable_parallelism() my_dictionary = {} for sto, dataset in dataset_series.piter(storage=my_dictionary): ... # process sto.result = ... # some information processed for this dataset sto.result_id = ... # some identifier for this dataset print(my_dictionary) By default, the dataset series will be divided as equally as possible among the cores. Often some datasets will require more work than others. We offer the ``dynamic`` keyword in the :func:`~yt.data_objects.time_series.DatasetSeries.piter` function to enable dynamic load balancing with a task queue. Dynamic load balancing works best with more cores and a variable workload. Here one process will act as a server to assign the next available dataset to any free client. For example, a 16 core job will have 15 cores analyzing the data with 1 core acting as the task manager. .. _parallelizing-your-analysis: Parallelizing over Multiple Objects ----------------------------------- It is easy within yt to parallelize a list of tasks, as long as those tasks are independent of one another. Using object-based parallelism, the function :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_objects` will automatically split up a list of tasks over the specified number of processors (or cores). Please see this heavily-commented example: .. code-block:: python # As always... import yt yt.enable_parallelism() import glob # The number 4, below, is the number of processes to parallelize over, which # is generally equal to the number of MPI tasks the job is launched with. # If num_procs is set to zero or a negative number, the for loop below # will be run such that each iteration of the loop is done by a single MPI # task. Put another way, setting it to zero means that no matter how many # MPI tasks the job is run with, num_procs will default to the number of # MPI tasks automatically. num_procs = 4 # fns is a list of all the simulation data files in the current directory. fns = glob.glob("./plot*") fns.sort() # This dict will store information collected in the loop, below. # Inside the loop each task will have a local copy of the dict, but # the dict will be combined once the loop finishes. my_storage = {} # In this example, because the storage option is used in the # parallel_objects function, the loop yields a tuple, which gets used # as (sto, fn) inside the loop. # In the loop, sto is essentially my_storage, but a local copy of it. # If data does not need to be combined after the loop is done, the line # would look like: # for fn in parallel_objects(fns, num_procs): for sto, fn in yt.parallel_objects(fns, num_procs, storage=my_storage): # Open a data file, remembering that fn is different on each task. ds = yt.load(fn) dd = ds.all_data() # This copies fn and the min/max of density to the local copy of # my_storage sto.result_id = fn sto.result = dd.quantities.extrema(("gas", "density")) # Makes and saves a plot of the gas density. p = yt.ProjectionPlot(ds, "x", ("gas", "density")) p.save() # At this point, as the loop exits, the local copies of my_storage are # combined such that all tasks now have an identical and full version of # my_storage. Until this point, each task is unaware of what the other # tasks have produced. # Below, the values in my_storage are printed by only one task. The other # tasks do nothing. if yt.is_root(): for fn, vals in sorted(my_storage.items()): print(fn, vals) This example above can be modified to loop over anything that can be saved to a Python list: halos, data files, arrays, and more. .. _parallel-time-series-analysis: Parallelization over Multiple Datasets (including Time Series) -------------------------------------------------------------- The same ``parallel_objects`` machinery discussed above is turned on by default when using a :class:`~yt.data_objects.time_series.DatasetSeries` object (see :ref:`time-series-analysis`) to iterate over simulation outputs. The syntax for this is very simple. As an example, we can use the following script to find the angular momentum vector in a 1 pc sphere centered on the maximum density cell in a large number of simulation outputs: .. code-block:: python import yt yt.enable_parallelism() # Load all of the DD*/output_* files into a DatasetSeries object # in this case it is a Time Series ts = yt.load("DD*/output_*") # Define an empty storage dictionary for collecting information # in parallel through processing storage = {} # Use piter() to iterate over the time series, one proc per dataset # and store the resulting information from each dataset in # the storage dictionary for sto, ds in ts.piter(storage=storage): sphere = ds.sphere("max", (1.0, "pc")) sto.result = sphere.quantities.angular_momentum_vector() sto.result_id = str(ds) # Print out the angular momentum vector for all of the datasets for L in sorted(storage.items()): print(L) Note that this script can be run in serial or parallel with an arbitrary number of processors. When running in parallel, each output is given to a different processor. You can also request a fixed number of processors to calculate each angular momentum vector. For example, the following script will calculate each angular momentum vector using 4 workgroups, splitting up the pool available processors. Note that parallel=1 implies that the analysis will be run using 1 workgroup, whereas parallel=True will run with Nprocs workgroups. .. code-block:: python import yt yt.enable_parallelism() ts = yt.DatasetSeries("DD*/output_*", parallel=4) for ds in ts.piter(): sphere = ds.sphere("max", (1.0, "pc")) L_vecs = sphere.quantities.angular_momentum_vector() If you do not want to use ``parallel_objects`` parallelism when using a DatasetSeries object, set ``parallel = False``. When running python in parallel, this will use all of the available processors to evaluate the requested operation on each simulation output. Some care and possibly trial and error might be necessary to estimate the correct settings for your simulation outputs. Note, when iterating over several large datasets, running out of memory may become an issue as the internal data structures associated with each dataset may not be properly de-allocated at the end of an iteration. If memory use becomes a problem, it may be necessary to manually delete some of the larger data structures. .. code-block:: python import yt yt.enable_parallelism() ts = yt.DatasetSeries("DD*/output_*", parallel=4) for ds in ts.piter(): # do analysis here ds.index.clear_all_data() Multi-level Parallelism ----------------------- By default, the :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_objects` and :func:`~yt.data_objects.time_series.DatasetSeries.piter` functions will allocate a single processor to each iteration of the parallelized loop. However, there may be situations in which it is advantageous to have multiple processors working together on each loop iteration. Like with any traditional for loop, nested loops with multiple calls to :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.enable_parallelism` can be used to parallelize the functionality within a given loop iteration. In the example below, we will create projections along the x, y, and z axis of the density and temperature fields. We will assume a total of 6 processors are available, allowing us to allocate to processors to each axis and project each field with a separate processor. .. code-block:: python import yt yt.enable_parallelism() # assume 6 total cores # allocate 3 work groups of 2 cores each for ax in yt.parallel_objects("xyz", njobs=3): # project each field with one of the two cores in the workgroup for field in yt.parallel_objects([("gas", "density"), ("gas", "temperature")]): p = yt.ProjectionPlot(ds, ax, field, weight_field=("gas", "density")) p.save("figures/") Note, in the above example, if the inner :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_objects` call were removed from the loop, the two-processor work group would work together to project each of the density and temperature fields. This is because the projection functionality itself is parallelized internally. The :func:`~yt.data_objects.time_series.DatasetSeries.piter` function can also be used in the above manner with nested :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_objects` loops to allocate multiple processors to work on each dataset. As discussed above in :ref:`parallel-time-series-analysis`, the ``parallel`` keyword is used to control the number of workgroups created for iterating over multiple datasets. Parallel Performance, Resources, and Tuning ------------------------------------------- Optimizing parallel jobs in yt is difficult; there are many parameters that affect how well and quickly the job runs. In many cases, the only way to find out what the minimum (or optimal) number of processors is, or amount of memory needed, is through trial and error. However, this section will attempt to provide some insight into what are good starting values for a given parallel task. Chunk Decomposition +++++++++++++++++++ In general, these types of parallel calculations scale very well with number of processors. They are also fairly memory-conservative. The two limiting factors is therefore the number of chunks in the dataset, and the speed of the disk the data is stored on. There is no point in running a parallel job of this kind with more processors than chunks, because the extra processors will do absolutely nothing, and will in fact probably just serve to slow down the whole calculation due to the extra overhead. The speed of the disk is also a consideration - if it is not a high-end parallel file system, adding more tasks will not speed up the calculation if the disk is already swamped with activity. The best advice for these sort of calculations is to run with just a few processors and go from there, seeing if it the runtime improves noticeably. **Projections, Slices, Cutting Planes and Covering Grids** Projections, slices and cutting planes are the most common methods of creating two-dimensional representations of data. All three have been parallelized in a chunk-based fashion. * **Projections**: projections are parallelized utilizing a quad-tree approach. Data is loaded for each processor, typically by a process that consolidates open/close/read operations, and each grid is then iterated over and cells are deposited into a data structure that stores values corresponding to positions in the two-dimensional plane. This provides excellent load balancing, and in serial is quite fast. However, the operation by which quadtrees are joined across processors scales poorly; while memory consumption scales well, the time to completion does not. As such, projections can often be done very fast when operating only on a single processor! The quadtree algorithm can be used inline (and, indeed, it is for this reason that it is slow.) It is recommended that you attempt to project in serial before projecting in parallel; even for the very largest datasets (Enzo 1024^3 root grid with 7 levels of refinement) in the absence of IO the quadtree algorithm takes only three minutes or so on a decent processor. * **Slices**: to generate a slice, chunks that intersect a given slice are iterated over and their finest-resolution cells are deposited. The chunks are decomposed via standard load balancing. While this operation is parallel, **it is almost never necessary to slice a dataset in parallel**, as all data is loaded on demand anyway. The slice operation has been parallelized so as to enable slicing when running *in situ*. * **Cutting planes**: cutting planes are parallelized exactly as slices are. However, in contrast to slices, because the data-selection operation can be much more time consuming, cutting planes often benefit from parallelism. * **Covering Grids**: covering grids are parallelized exactly as slices are. Object-Based ++++++++++++ Like chunk decomposition, it does not help to run with more processors than the number of objects to be iterated over. There is also the matter of the kind of work being done on each object, and whether it is disk-intensive, cpu-intensive, or memory-intensive. It is up to the user to figure out what limits the performance of their script, and use the correct amount of resources, accordingly. Disk-intensive jobs are limited by the speed of the file system, as above, and extra processors beyond its capability are likely counter-productive. It may require some testing or research (e.g. supercomputer documentation) to find out what the file system is capable of. If it is cpu-intensive, it's best to use as many processors as possible and practical. For a memory-intensive job, each processor needs to be able to allocate enough memory, which may mean using fewer than the maximum number of tasks per compute node, and increasing the number of nodes. The memory used per processor should be calculated, compared to the memory on each compute node, which dictates how many tasks per node. After that, the number of processors used overall is dictated by the disk system or CPU-intensity of the job. Domain Decomposition ++++++++++++++++++++ The various types of analysis that utilize domain decomposition use them in different enough ways that they are discussed separately. **Halo-Finding** Halo finding, along with the merger tree that uses halo finding, operates on the particles in the volume, and is therefore mostly chunk-agnostic. Generally, the biggest concern for halo finding is the amount of memory needed. There is subtle art in estimating the amount of memory needed for halo finding, but a rule of thumb is that the HOP halo finder is the most memory intensive (:func:`HaloFinder`), and Friends of Friends (:func:`FOFHaloFinder`) being the most memory-conservative. For more information, see :ref:`halo-analysis`. **Volume Rendering** The simplest way to think about volume rendering, is that it load-balances over the i/o chunks in the dataset. Each processor is given roughly the same sized volume to operate on. In practice, there are just a few things to keep in mind when doing volume rendering. First, it only uses a power of two number of processors. If the job is run with 100 processors, only 64 of them will actually do anything. Second, the absolute maximum number of processors is the number of chunks. In order to keep work distributed evenly, typically the number of processors should be no greater than one-eighth or one-quarter the number of processors that were used to produce the dataset. For more information, see :ref:`volume_rendering`. Additional Tips --------------- * Don't be afraid to change how a parallel job is run. Change the number of processors, or memory allocated, and see if things work better or worse. After all, it's just a computer, it doesn't pass moral judgment! * Similarly, human time is more valuable than computer time. Try increasing the number of processors, and see if the runtime drops significantly. There will be a sweet spot between speed of run and the waiting time in the job scheduler queue; it may be worth trying to find it. * If you are using object-based parallelism but doing CPU-intensive computations on each object, you may find that setting ``num_procs`` equal to the number of processors per compute node can lead to significant speedups. By default, most mpi implementations will assign tasks to processors on a 'by-slot' basis, so this setting will tell yt to do computations on a single object using only the processors on a single compute node. A nice application for this type of parallelism is calculating a list of derived quantities for a large number of simulation outputs. * It is impossible to tune a parallel operation without understanding what's going on. Read the documentation, look at the underlying code, or talk to other yt users. Get informed! * Sometimes it is difficult to know if a job is cpu, memory, or disk intensive, especially if the parallel job utilizes several of the kinds of parallelism discussed above. In this case, it may be worthwhile to put some simple timers in your script (as below) around different parts. .. code-block:: python import time import yt yt.enable_parallelism() ds = yt.load("DD0152") t0 = time.time() bigstuff, hugestuff = StuffFinder(ds) BigHugeStuffParallelFunction(ds, bigstuff, hugestuff) t1 = time.time() for i in range(1000000): tinystuff, ministuff = GetTinyMiniStuffOffDisk("in%06d.txt" % i) array = TinyTeensyParallelFunction(ds, tinystuff, ministuff) SaveTinyMiniStuffToDisk("out%06d.txt" % i, array) t2 = time.time() if yt.is_root(): print( "BigStuff took {:.5e} sec, TinyStuff took {:.5e} sec".format(t1 - t0, t2 - t1) ) * Remember that if the script handles disk IO explicitly, and does not use a built-in yt function to write data to disk, care must be taken to avoid `race-conditions `_. Be explicit about which MPI task writes to disk using a construction something like this: .. code-block:: python if yt.is_root(): file = open("out.txt", "w") file.write(stuff) file.close() * Many supercomputers allow users to ssh into the nodes that their job is running on. Many job schedulers send the names of the nodes that are used in the notification emails, or a command like ``qstat -f NNNN``, where ``NNNN`` is the job ID, will also show this information. By ssh-ing into nodes, the memory usage of each task can be viewed in real-time as the job runs (using ``top``, for example), and can give valuable feedback about the resources the task requires. An Advanced Worked Example -------------------------- Below is a script used to calculate the redshift of first 99.9% ionization in a simulation. This script was designed to analyze a set of 100 outputs on Gordon, running on 128 processors. This script goes through three phases: #. Define a new derived field, which calculates the fraction of ionized hydrogen as a function only of the total hydrogen density. #. Load a time series up, specifying ``parallel = 8``. This means that it will decompose into 8 jobs. So if we ran on 128 processors, we would have 16 processors assigned to each output in the time series. #. Creating a big cube that will hold our results for this set of processors. Note that this will be only for each output considered by this processor, and this cube will not necessarily be filled in every cell. #. For each output, distribute the grids to each of the sixteen processors working on that output. Each of these takes the max of the ionized redshift in their zone versus the accumulation cube. #. Iterate over slabs and find the maximum redshift in each slab of our accumulation cube. At the end, the root processor (of the global calculation) writes out an ionization cube that contains the redshift of first reionization for each zone across all outputs. .. literalinclude:: ionization_cube.py yt-project-yt-f043ac8/doc/source/analyzing/particle_filter.ipynb000066400000000000000000000130201510711153200251220ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Filtering Particle Data\n", "Let us go through a full worked example. Here we have a Tipsy SPH dataset. By general\n", "inspection, we see that there are stars present in the dataset, since\n", "there are fields with field type: `Stars` in the `ds.field_list`. Let's look \n", "at the `derived_field_list` for all of the `Stars` fields. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "\n", "import yt\n", "\n", "ds = yt.load(\"TipsyGalaxy/galaxy.00300\")\n", "for field in ds.derived_field_list:\n", " if field[0] == \"Stars\":\n", " print(field)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will filter these into young stars and old stars by masking on the ('Stars', 'creation_time') field. \n", "\n", "In order to do this, we first make a function which applies our desired cut. This function must accept two arguments: `pfilter` and `data`. The first argument is a `ParticleFilter` object that contains metadata about the filter its self. The second argument is a yt data container.\n", "\n", "Let's call \"young\" stars only those stars with ages less 5 million years. Since Tipsy assigns a very large `creation_time` for stars in the initial conditions, we need to also exclude stars with negative ages. \n", "\n", "Conversely, let's define \"old\" stars as those stars formed dynamically in the simulation with ages greater than 5 Myr. We also include stars with negative ages, since these stars were included in the simulation initial conditions.\n", "\n", "We make use of `pfilter.filtered_type` so that the filter definition will use the same particle type as the one specified in the call to `add_particle_filter` below. This makes the filter definition usable for arbitrary particle types. Since we're only filtering the `\"Stars\"` particle type in this example, we could have also replaced `pfilter.filtered_type` with `\"Stars\"` and gotten the same result." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def young_stars(pfilter, data):\n", " age = data.ds.current_time - data[pfilter.filtered_type, \"creation_time\"]\n", " filter = np.logical_and(age.in_units(\"Myr\") <= 5, age >= 0)\n", " return filter\n", "\n", "\n", "def old_stars(pfilter, data):\n", " age = data.ds.current_time - data[pfilter.filtered_type, \"creation_time\"]\n", " filter = np.logical_or(age.in_units(\"Myr\") >= 5, age < 0)\n", " return filter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we define these as particle filters within the yt universe with the\n", "`add_particle_filter()` function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "yt.add_particle_filter(\n", " \"young_stars\",\n", " function=young_stars,\n", " filtered_type=\"Stars\",\n", " requires=[\"creation_time\"],\n", ")\n", "\n", "yt.add_particle_filter(\n", " \"old_stars\", function=old_stars, filtered_type=\"Stars\", requires=[\"creation_time\"]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us now apply these filters specifically to our dataset.\n", "\n", "Let's double check that it worked by looking at the derived_field_list for any new fields created by our filter." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds.add_particle_filter(\"young_stars\")\n", "ds.add_particle_filter(\"old_stars\")\n", "\n", "for field in ds.derived_field_list:\n", " if \"young_stars\" in field or \"young_stars\" in field[1]:\n", " print(field)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see all of the new `young_stars` fields as well as the 4 deposit fields. These deposit fields are `mesh` fields generated by depositing particle fields on the grid. Let's generate a couple of projections of where the young and old stars reside in this simulation by accessing some of these new fields." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "p = yt.ProjectionPlot(\n", " ds,\n", " \"z\",\n", " [(\"deposit\", \"young_stars_cic\"), (\"deposit\", \"old_stars_cic\")],\n", " width=(40, \"kpc\"),\n", " center=\"m\",\n", ")\n", "p.set_figure_size(5)\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that young stars are concentrated in regions of active star formation, while old stars are more spatially extended." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/analyzing/saving_data.rst000066400000000000000000000172131510711153200237310ustar00rootroot00000000000000.. _saving_data: Saving Reloadable Data ====================== Most of the data loaded into or generated with yt can be saved to a format that can be reloaded as a first-class dataset. This includes the following: * geometric data containers (regions, spheres, disks, rays, etc.) * grid data containers (covering grids, arbitrary grids, fixed resolution buffers) * spatial plots (projections, slices, cutting planes) * profiles * generic array data In the case of projections, slices, and profiles, reloaded data can be used to remake plots. For information on this, see :ref:`remaking-plots`. .. _saving-data-containers: Geometric Data Containers ------------------------- Data from geometric data containers can be saved with the :func:`~yt.data_objects.data_containers.YTDataContainer.save_as_dataset` function. .. notebook-cell:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") sphere = ds.sphere([0.5] * 3, (10, "Mpc")) fn = sphere.save_as_dataset(fields=[("gas", "density"), ("all", "particle_mass")]) print(fn) This function will return the name of the file to which the dataset was saved. The filename will be a combination of the name of the original dataset and the type of data container. Optionally, a specific filename can be given with the ``filename`` keyword. If no fields are given, the fields that have previously been queried will be saved. The newly created dataset can be loaded like all other supported data through ``yt.load``. Once loaded, field data can be accessed through the traditional data containers or through the ``data`` attribute, which will be a data container configured like the original data container used to make the dataset. Grid data is accessed by the ``grid`` data type and particle data is accessed with the original particle type. As with the original dataset, grid positions and cell sizes are accessible with, for example, ("grid", "x") and ("grid", "dx"). Particle positions are accessible as (, "particle_position_x"). All original simulation parameters are accessible in the ``parameters`` dictionary, normally associated with all datasets. .. code-block:: python sphere_ds = yt.load("DD0046_sphere.h5") # use the original data container print(sphere_ds.data["grid", "density"]) # create a new data container ad = sphere_ds.all_data() # grid data print(ad["grid", "density"]) print(ad["grid", "x"]) print(ad["grid", "dx"]) # particle data print(ad["all", "particle_mass"]) print(ad["all", "particle_position_x"]) Note that because field data queried from geometric containers is returned as unordered 1D arrays, data container datasets are treated, effectively, as particle data. Thus, 3D indexing of grid data from these datasets is not possible. .. _saving-grid-data-containers: Grid Data Containers -------------------- Data containers that return field data as multidimensional arrays can be saved so as to preserve this type of access. This includes covering grids, arbitrary grids, and fixed resolution buffers. Saving data from these containers works just as with geometric data containers. Field data can be accessed through geometric data containers. .. code-block:: python cg = ds.covering_grid(level=0, left_edge=[0.25] * 3, dims=[16] * 3) fn = cg.save_as_dataset(fields=[("gas", "density"), ("all", "particle_mass")]) cg_ds = yt.load(fn) ad = cg_ds.all_data() print(ad["grid", "density"]) Multidimensional indexing of field data is also available through the ``data`` attribute. .. code-block:: python print(cg_ds.data["grid", "density"]) Fixed resolution buffers work just the same. .. code-block:: python my_proj = ds.proj(("gas", "density"), "x", weight_field=("gas", "density")) frb = my_proj.to_frb(1.0, (800, 800)) fn = frb.save_as_dataset(fields=[("gas", "density")]) frb_ds = yt.load(fn) print(frb_ds.data["gas", "density"]) .. _saving-spatial-plots: Spatial Plots ------------- Spatial plots, such as projections, slices, and off-axis slices (cutting planes) can also be saved and reloaded. .. code-block:: python proj = ds.proj(("gas", "density"), "x", weight_field=("gas", "density")) proj.save_as_dataset() Once reloaded, they can be handed to their associated plotting functions to make images. .. code-block:: python proj_ds = yt.load("DD0046_proj.h5") p = yt.ProjectionPlot(proj_ds, "x", ("gas", "density"), weight_field=("gas", "density")) p.save() .. _saving-profile-data: Profiles -------- Profiles created with :func:`~yt.data_objects.profiles.create_profile`, :class:`~yt.visualization.profile_plotter.ProfilePlot`, and :class:`~yt.visualization.profile_plotter.PhasePlot` can be saved with the :func:`~yt.data_objects.profiles.save_as_dataset` function, which works just as above. Profile datasets are a type of non-spatial grid datasets. Geometric selection is not possible, but data can be accessed through the ``.data`` attribute. .. notebook-cell:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") ad = ds.all_data() profile_2d = yt.create_profile(ad, [("gas", "density"), ("gas", "temperature")], ("gas", "mass"), weight_field=None, n_bins=(128, 128)) profile_2d.save_as_dataset() prof_2d_ds = yt.load("DD0046_Profile2D.h5") print (prof_2d_ds.data["gas", "mass"]) The x, y (if at least 2D), and z (if 3D) bin fields can be accessed as 1D arrays with "x", "y", and "z". .. code-block:: python print(prof_2d_ds.data["gas", "x"]) The bin fields can also be returned with the same shape as the profile data by accessing them with their original names. This allows for boolean masking of profile data using the bin fields. .. code-block:: python # density is the x bin field print(prof_2d_ds.data["gas", "density"]) For 1, 2, and 3D profile datasets, a fake profile object will be constructed by accessing the ".profile" attribute. This is used primarily in the case of 1 and 2D profiles to create figures using :class:`~yt.visualization.profile_plotter.ProfilePlot` and :class:`~yt.visualization.profile_plotter.PhasePlot`. .. code-block:: python p = yt.PhasePlot( prof_2d_ds.data, ("gas", "density"), ("gas", "temperature"), ("gas", "mass"), weight_field=None, ) p.save() .. _saving-array-data: Generic Array Data ------------------ Generic arrays can be saved and reloaded as non-spatial data using the :func:`~yt.frontends.ytdata.utilities.save_as_dataset` function, also available as ``yt.save_as_dataset``. As with profiles, geometric selection is not possible, but the data can be accessed through the ``.data`` attribute. .. notebook-cell:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") region = ds.box([0.25]*3, [0.75]*3) sphere = ds.sphere(ds.domain_center, (10, "Mpc")) my_data = {} my_data["region_density"] = region["gas", "density"] my_data["sphere_density"] = sphere["gas", "density"] yt.save_as_dataset(ds, "test_data.h5", my_data) array_ds = yt.load("test_data.h5") print (array_ds.data["data", "region_density"]) print (array_ds.data["data", "sphere_density"]) Array data can be saved with or without a dataset loaded. If no dataset has been loaded, as fake dataset can be provided as a dictionary. .. notebook-cell:: import numpy as np import yt my_data = {"density": yt.YTArray(np.random.random(10), "g/cm**3"), "temperature": yt.YTArray(np.random.random(10), "K")} fake_ds = {"current_time": yt.YTQuantity(10, "Myr")} yt.save_as_dataset(fake_ds, "random_data.h5", my_data) new_ds = yt.load("random_data.h5") print (new_ds.data["data", "density"]) yt-project-yt-f043ac8/doc/source/analyzing/time_series_analysis.rst000066400000000000000000000171521510711153200256660ustar00rootroot00000000000000.. _time-series-analysis: Time Series Analysis ==================== Often, one wants to analyze a continuous set of outputs from a simulation in a uniform manner. A simple example would be to calculate the peak density in a set of outputs that were written out. The problem with time series analysis in yt is general an issue of verbosity and clunkiness. Typically, one sets up a loop: .. code-block:: python for dsi in range(30): fn = "DD%04i/DD%04i" % (dsi, dsi) ds = load(fn) process_output(ds) But this is not really very nice. This ends up requiring a lot of maintenance. The :class:`~yt.data_objects.time_series.DatasetSeries` object has been designed to remove some of this clunkiness and present an easier, more unified approach to analyzing sets of data. Even better, :class:`~yt.data_objects.time_series.DatasetSeries` works in parallel by default (see :ref:`parallel-computation`), so you can use a ``DatasetSeries`` object to quickly and easily parallelize your analysis. Since doing the same analysis task on many simulation outputs is 'embarrassingly' parallel, this naturally allows for almost arbitrary speedup - limited only by the number of available processors and the number of simulation outputs. The idea behind the current implementation of time series analysis is that the underlying data and the operators that act on that data can and should be distinct. There are several operators provided, as well as facilities for creating your own, and these operators can be applied either to datasets on the whole or to subregions of individual datasets. The simplest mechanism for creating a ``DatasetSeries`` object is to pass a glob pattern to the ``yt.load`` function. .. code-block:: python import yt ts = yt.load("DD????/DD????") This will create a new time series, populated with all datasets that match the pattern "DD" followed by four digits. This object, here called ``ts``, can now be analyzed in bulk. Alternately, you can specify an already formatted list of filenames directly to the :class:`~yt.data_objects.time_series.DatasetSeries` initializer: .. code-block:: python import yt ts = yt.DatasetSeries(["DD0030/DD0030", "DD0040/DD0040"]) Analyzing Each Dataset In Sequence ---------------------------------- The :class:`~yt.data_objects.time_series.DatasetSeries` object has two primary methods of iteration. The first is a very simple iteration, where each object is returned for iteration: .. code-block:: python import yt ts = yt.load("*/*.index") for ds in ts: print(ds.current_time) This can also operate in parallel, using :meth:`~yt.data_objects.time_series.DatasetSeries.piter`. For more examples, see: * :ref:`parallel-time-series-analysis` * The cookbook recipe for :ref:`cookbook-time-series-analysis` * :class:`~yt.data_objects.time_series.DatasetSeries` In addition, the :class:`~yt.data_objects.time_series.DatasetSeries` object allows to select an output based on its time or by its redshift (if defined) as follows: .. code-block:: python import yt ts = yt.load("*/*.index") # Get output at 3 Gyr ds = ts.get_by_time((3, "Gyr")) # This will fail if no output is found within 100 Myr ds = ts.get_by_time((3, "Gyr"), tolerance=(100, "Myr")) # Get the output at the time right before and after 3 Gyr ds_before = ts.get_by_time((3, "Gyr"), prefer="smaller") ds_after = ts.get_by_time((3, "Gyr"), prefer="larger") # For cosmological simulations, you can also select an output by its redshift # with the same options as above ds = ts.get_by_redshift(0.5) For more information, see :meth:`~yt.data_objects.time_series.DatasetSeries.get_by_time` and :meth:`~yt.data_objects.time_series.DatasetSeries.get_by_redshift`. .. _analyzing-an-entire-simulation: Analyzing an Entire Simulation ------------------------------ .. note:: Implemented for the Enzo, Gadget, OWLS, and Exodus II frontends. The parameter file used to run a simulation contains all the information necessary to know what datasets should be available. The ``simulation`` convenience function allows one to create a ``DatasetSeries`` object of all or a subset of all data created by a single simulation. To instantiate, give the parameter file and the simulation type. .. code-block:: python import yt my_sim = yt.load_simulation("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo") Then, create a ``DatasetSeries`` object with the :meth:`frontends.enzo.simulation_handling.EnzoSimulation.get_time_series` function. With no additional keywords, the time series will include every dataset. If the ``find_outputs`` keyword is set to ``True``, a search of the simulation directory will be performed looking for potential datasets. These datasets will be temporarily loaded in order to figure out the time and redshift associated with them. This can be used when simulation data was created in a non-standard way, making it difficult to guess the corresponding time and redshift information .. code-block:: python my_sim.get_time_series() After this, time series analysis can be done normally. .. code-block:: python for ds in my_sim.piter(): all_data = ds.all_data() print(all_data.quantities.extrema(("gas", "density"))) Additional keywords can be given to :meth:`frontends.enzo.simulation_handling.EnzoSimulation.get_time_series` to select a subset of the total data: * ``time_data`` (*bool*): Whether or not to include time outputs when gathering datasets for time series. Default: True. (Enzo only) * ``redshift_data`` (*bool*): Whether or not to include redshift outputs when gathering datasets for time series. Default: True. (Enzo only) * ``initial_time`` (*float*): The earliest time for outputs to be included. If None, the initial time of the simulation is used. This can be used in combination with either ``final_time`` or ``final_redshift``. Default: None. * ``final_time`` (*float*): The latest time for outputs to be included. If None, the final time of the simulation is used. This can be used in combination with either ``initial_time`` or ``initial_redshift``. Default: None. * ``times`` (*list*): A list of times for which outputs will be found. Default: None. * ``initial_redshift`` (*float*): The earliest redshift for outputs to be included. If None, the initial redshift of the simulation is used. This can be used in combination with either ``final_time`` or ``final_redshift``. Default: None. * ``final_redshift`` (*float*): The latest redshift for outputs to be included. If None, the final redshift of the simulation is used. This can be used in combination with either ``initial_time`` or ``initial_redshift``. Default: None. * ``redshifts`` (*list*): A list of redshifts for which outputs will be found. Default: None. * ``initial_cycle`` (*float*): The earliest cycle for outputs to be included. If None, the initial cycle of the simulation is used. This can only be used with final_cycle. Default: None. (Enzo only) * ``final_cycle`` (*float*): The latest cycle for outputs to be included. If None, the final cycle of the simulation is used. This can only be used in combination with initial_cycle. Default: None. (Enzo only) * ``tolerance`` (*float*): Used in combination with ``times`` or ``redshifts`` keywords, this is the tolerance within which outputs are accepted given the requested times or redshifts. If None, the nearest output is always taken. Default: None. * ``parallel`` (*bool*/*int*): If True, the generated ``DatasetSeries`` will divide the work such that a single processor works on each dataset. If an integer is supplied, the work will be divided into that number of jobs. Default: True. yt-project-yt-f043ac8/doc/source/analyzing/units.rst000066400000000000000000000316211510711153200226120ustar00rootroot00000000000000.. _units: Symbolic Units ============== This section describes yt's symbolic unit capabilities. This is provided as quick introduction for those who are already familiar with yt but want to learn more about the unit system. Please see :ref:`analyzing` and :ref:`visualizing` for more detail about querying, analyzing, and visualizing data in yt. Originally the unit system was a part of yt proper but since the yt 4.0 release, the unit system has been split off into `its own library `_, ``unyt``. For a detailed discussion of how to use ``unyt``, we suggest taking a look at the unyt documentation available at https://unyt.readthedocs.io/, however yt adds additional capabilities above and beyond what is provided by ``unyt`` alone, we describe those capabilities below. Selecting data from a data object --------------------------------- The data returned by yt will have units attached to it. For example, let's query a data object for the ``('gas', 'density')`` field: >>> import yt >>> ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030') >>> dd = ds.all_data() >>> dd['gas', 'density'] unyt_array([4.92775113e-31, 4.94005233e-31, 4.93824694e-31, ..., 1.12879234e-25, 1.59561490e-25, 1.09824903e-24], 'g/cm**3') We can see how we get back a ``unyt_array`` instance. A ``unyt_array`` is a subclass of NumPy's NDarray type that has units attached to it: >>> dd['gas', 'density'].units g/cm**3 It is straightforward to convert data to different units: >>> dd['gas', 'density'].to('Msun/kpc**3') unyt_array([7.28103608e+00, 7.29921182e+00, 7.29654424e+00, ..., 1.66785569e+06, 2.35761291e+06, 1.62272618e+07], 'Msun/kpc**3') For more details about working with ``unyt_array``, see the `the documentation `__ for ``unyt``. Applying Units to Data ---------------------- A ``unyt_array`` can be created from a list, tuple, or NumPy array using multiplication with a ``Unit`` object. For convenience, each yt dataset has a ``units`` attribute one can use to obtain unit objects for this purpose: >>> data = np.random.random((100, 100)) >>> data_with_units = data * ds.units.gram All units known to the dataset will be available via ``ds.units``, including code units and comoving units. Derived Field Units ------------------- Special care often needs to be taken to ensure the result of a derived field will come out in the correct units. The yt unit system will double-check for you to make sure you are not accidentally making a unit conversion mistake. To see what that means in practice, let's define a derived field corresponding to the square root of the gas density: >>> import yt >>> import numpy as np >>> def root_density(field, data): ... return np.sqrt(data['gas', 'density']) >>> ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030') >>> ds.add_field(("gas", "root_density"), units="(g/cm**3)**(1/2)", ... function=root_density, sampling_type='cell') >>> ad = ds.all_data() >>> ad['gas', 'root_density'] unyt_array([7.01979425e-16, 7.02855059e-16, 7.02726614e-16, ..., 3.35975050e-13, 3.99451486e-13, 1.04797377e-12], 'sqrt(g)/cm**(3/2)') No special unit logic needs to happen inside of the function: the result of ``np.sqrt`` will have the correct units: >>> np.sqrt(ad['gas', 'density']) unyt_array([7.01979425e-16, 7.02855059e-16, 7.02726614e-16, ..., 3.35975050e-13, 3.99451486e-13, 1.04797377e-12], 'sqrt(g)/cm**(3/2)') One could also specify any other units that have dimensions of square root of density and yt would automatically convert the return value of the field function to the specified units. An error would be raised if the units are not dimensionally equivalent to the return value of the field function. Code Units ---------- All yt datasets are associated with a "code" unit system that corresponds to whatever unit system the data is represented in on-disk. Let's take a look at the data in an Enzo simulation, specifically the ``("enzo", "Density")`` field: >>> import yt >>> ds = yt.load('Enzo_64/DD0043/data0043') >>> ad = ds.all_data() >>> ad["enzo", "Density"] unyt_array([6.74992726e-02, 6.12111635e-02, 8.92988636e-02, ..., 9.09875931e+01, 5.66932465e+01, 4.27780263e+01], 'code_mass/code_length**3') we see we get back data from yt in units of ``code_mass/code_length**3``. This is the density unit formed out of the base units of mass and length in the internal unit system in the simulation. We can see the values of these units by looking at the ``length_unit`` and ``mass_unit`` attributes of the dataset object: >>> ds.length_unit unyt_quantity(128, 'Mpccm/h') >>> ds.mass_unit unyt_quantity(4.89045159e+50, 'g') And we can see that both of these have values of 1 in the code unit system. >>> ds.length_unit.to('code_length') unyt_quantity(1., 'code_length') >>> ds.mass_unit.to('code_mass') unyt_quantity(1., 'code_mass') In addition to ``length_unit`` and ``mass_unit``, there are also ``time_unit``, ``velocity_unit``, and ``magnetic_unit`` attributes for this dataset. Some frontends also define a ``density_unit``, ``pressure_unit``, ``temperature_unit``, and ``specific_energy`` attribute. If these are not defined then the corresponding unit is calculated from the base length, mass, and time unit. Each of these attributes corresponds to a unit in the code unit system: >>> [un for un in dir(ds.units) if un.startswith('code')] ['code_density', 'code_length', 'code_magnetic', 'code_mass', 'code_metallicity', 'code_pressure', 'code_specific_energy', 'code_temperature', 'code_time', 'code_velocity'] You can use these unit names to convert arbitrary data into a dataset's code unit system: >>> u = ds.units >>> data = 10**-30 * u.g / u.cm**3 >>> data.to('code_density') unyt_quantity(0.36217187, 'code_density') Note how in this example we used ``ds.units`` instead of the top-level ``unyt`` namespace or ``yt.units``. This is because the units from ``ds.units`` know about the dataset's code unit system and can convert data into it. Unit objects from ``unyt`` or ``yt.units`` will not know about any particular dataset's unit system. .. _cosmological-units: Comoving units for Cosmological Simulations ------------------------------------------- The length unit of the dataset I used above uses a cosmological unit: >>> print(ds.length_unit) 128 Mpccm/h In English, this says that the length unit is 128 megaparsecs in the comoving frame, scaled as if the hubble constant were 100 km/s/Mpc. Although :math:`h` isn't really a unit, yt treats it as one for the purposes of the unit system. As an aside, `Darren Croton's research note `_ on the history, use, and interpretation of :math:`h` as it appears in the astronomical literature is pretty much required reading for anyone who has to deal with factors of :math:`h` every now and then. In yt, comoving length unit symbols are named following the pattern ``< length unit >cm``, i.e. ``pccm`` for comoving parsec or ``mcm`` for a comoving meter. A comoving length unit is different from the normal length unit by a factor of :math:`(1+z)`: >>> u = ds.units >>> print((1*u.Mpccm)/(1*u.Mpc)) 0.9986088499304777 dimensionless >>> 1 / (1 + ds.current_redshift) 0.9986088499304776 As we saw before, h is treated like any other unit symbol. It has dimensionless units, just like a scalar: >>> (1*u.Mpc)/(1*u.Mpc/u.h) unyt_quantity(0.71, '(dimensionless)') >>> ds.hubble_constant 0.71 Using parsec as an example, * ``pc`` Proper parsecs, :math:`\rm{pc}`. * ``pccm`` Comoving parsecs, :math:`\rm{pc}/(1+z)`. * ``pccm/h`` Comoving parsecs normalized by the scaled hubble constant, :math:`\rm{pc}/h/(1+z)`. * ``pc/h`` Proper parsecs, normalized by the scaled hubble constant, :math:`\rm{pc}/h`. Overriding Code Unit Definitions -------------------------------- On occasion, you might have a dataset for a supported frontend that does not have the conversions to code units accessible or you may want to change them outright. ``yt`` provides a mechanism so that one may provide their own code unit definitions to ``yt.load``, which override the default rules for a given frontend for defining code units. This is provided through the ``units_override`` argument to ``yt.load``. We'll use an example of an Athena dataset. First, a call to ``yt.load`` without ``units_override``: >>> ds = yt.load("MHDSloshing/virgo_low_res.0054.vtk") >>> ds.length_unit unyt_quantity(1., 'cm') >>> ds.mass_unit unyt_quantity(1., 'g') >>> ds.time_unit unyt_quantity(1., 's') >>> sp1 = ds1.sphere("c", (0.1, "unitary")) >>> print(sp1["gas", "density"]) [0.05134981 0.05134912 0.05109047 ... 0.14608461 0.14489453 0.14385277] g/cm**3 This particular simulation is of a galaxy cluster merger so these density values are way, way too high. This is happening because Athena does not encode any information about the unit system used in the simulation or the output data, so yt cannot infer that information and must make an educated guess. In this case it incorrectly assumes the data are in CGS units. However, we know *a priori* what the unit system *should* be, and we can supply a ``units_override`` dictionary to ``yt.load`` to override the incorrect assumptions yt is making about this dataset. Let's define: >>> units_override = {"length_unit": (1.0, "Mpc"), ... "time_unit": (1.0, "Myr"), ... "mass_unit": (1.0e14, "Msun")} The ``units_override`` dictionary can take the following keys: * ``length_unit`` * ``time_unit`` * ``mass_unit`` * ``magnetic_unit`` * ``temperature_unit`` and the associated values can be ``(value, "unit")`` tuples, ``unyt_quantity`` instances, or floats (in the latter case they are assumed to have the corresponding cgs unit). Now let's reload the dataset using our ``units_override`` dict: >>> ds = yt.load("MHDSloshing/virgo_low_res.0054.vtk", ... units_override=units_override) >>> sp = ds.sphere("c",(0.1,"unitary")) >>> print(sp["gas", "density"]) [3.47531683e-28 3.47527018e-28 3.45776515e-28 ... 9.88689766e-28 9.80635384e-28 9.73584863e-28] g/cm**3 and we see how the data now have much more sensible values for a galaxy cluster merge simulation. Comparing Units From Different Simulations ------------------------------------------ The code units from different simulations will have different conversions to physical coordinates. This can get confusing when working with data from more than one simulation or from a single simulation where the units change with time. As an example, let's load up two enzo datasets from different redshifts in the same cosmology simulation, one from high redshift: >>> ds1 = yt.load('Enzo_64/DD0002/data0002') >>> ds1.current_redshift 7.8843748886903 >>> ds1.length_unit unyt_quantity(128, 'Mpccm/h') >>> ds1.length_unit.in_cgs() unyt_quantity(6.26145538e+25, 'cm') And another from low redshift: >>> ds2 = yt.load('Enzo_64/DD0043/data0043') >>> ds2.current_redshift 0.0013930880640796 >>> ds2.length_unit unyt_quantity(128, 'Mpccm/h') >>> ds2.length_unit.in_cgs() unyt_quantity(5.55517285e+26, 'cm') Now despite the fact that ``'Mpccm/h'`` means different things for the two datasets, it's still a well-defined operation to take the ratio of the two length units: >>> ds2.length_unit / ds1.length_unit unyt_quantity(8.87201539, '(dimensionless)') Because code units and comoving units are defined relative to a physical unit system, ``unyt`` is able to give the correct answer here. So long as the result comes out dimensionless or in a physical unit then the answer will be well-defined. However, if we want the answer to come out in the internal units of one particular dataset, additional care must be taken. For an example where this might be an issue, let's try to compute the sum of two comoving distances from each simulation: >>> d1 = 12 * ds1.units.Mpccm >>> d2 = 12 * ds2.units.Mpccm >>> d1 + d2 unyt_quantity(118.46418468, 'Mpccm') >>> d2 + d1 unyt_quantity(13.35256754, 'Mpccm') So this is definitely weird - addition appears to not be associative anymore! However, both answers are correct, the confusion is arising because ``"Mpccm"`` is ambiguous in these expressions. In situations like this, ``unyt`` will use the definition for units from the leftmost term in an expression, so the first example is returning data in high-redshift comoving megaparsecs, while the second example returns data in low-redshift comoving megaparsecs. Wherever possible it's best to do calculations in physical units when working with more than one dataset. If you need to use comoving units or code units then extra care must be taken in your code to avoid ambiguity. yt-project-yt-f043ac8/doc/source/conf.py000066400000000000000000000211121510711153200202130ustar00rootroot00000000000000# # yt documentation build configuration file, created by # sphinx-quickstart on Tue Jan 11 09:46:53 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import glob import os import sys import sphinx_bootstrap_theme on_rtd = os.environ.get("READTHEDOCS", None) == "True" # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("../extensions/")) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinx.ext.napoleon", "yt_cookbook", "yt_colormaps", "config_help", "yt_showfields", "nbsphinx", ] if not on_rtd: extensions.append("sphinx.ext.autosummary") extensions.append("pythonscript_sphinxext") # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "The yt Project" copyright = "2013-2021, the yt Project" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = "4.4" # The full version, including alpha/beta/rc tags. release = "4.4.2" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "bootstrap" html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = dict( bootstrap_version="3", bootswatch_theme="readable", navbar_links=[ ("", ""), # see https://github.com/yt-project/yt/pull/3423 ("How to get help", "help/index"), ("Quickstart notebooks", "quickstart/index"), ("Cookbook", "cookbook/index"), ], navbar_sidebarrel=False, globaltoc_depth=2, ) # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = "_static/yt_icon.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static", "analyzing/_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. html_domain_indices = False # If false, no index is generated. html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "ytdoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). # latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). # latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ("index", "yt.tex", "yt Documentation", "The yt Project", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "yt", "yt Documentation", ["The yt Project"], 1)] nbsphinx_allow_errors = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), "ipython": ("https://ipython.readthedocs.io/en/stable/", None), "numpy": ("https://numpy.org/doc/stable/", None), "matplotlib": ("https://matplotlib.org/stable/", None), "astropy": ("https://docs.astropy.org/en/stable", None), "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None), "trident": ("https://trident.readthedocs.io/en/latest/", None), "yt_astro_analysis": ("https://yt-astro-analysis.readthedocs.io/en/latest/", None), "yt_attic": ("https://yt-attic.readthedocs.io/en/latest/", None), "pytest": ("https://docs.pytest.org/en/stable", None), } if not on_rtd: autosummary_generate = glob.glob("reference/api/api.rst") # as of Sphinx 3.1.2 this is the supported way to link custom style sheets def setup(app): app.add_css_file("custom.css") yt-project-yt-f043ac8/doc/source/cookbook/000077500000000000000000000000001510711153200205255ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/cookbook/amrkdtree_downsampling.py000066400000000000000000000050661510711153200256460ustar00rootroot00000000000000# Using AMRKDTree Homogenized Volumes to examine large datasets # at lower resolution. # In this example we will show how to use the AMRKDTree to take a simulation # with 8 levels of refinement and only use levels 0-3 to render the dataset. # Currently this cookbook is flawed in that the data that is covered by the # higher resolution data gets masked during the rendering. This should be # fixed by changing either the data source or the code in # yt/utilities/amr_kdtree.py where data is being masked for the partitioned # grid. Right now the quick fix is to create a data_collection, but this # will only work for patch based simulations that have ds.index.grids. # We begin by loading up yt, and importing the AMRKDTree import numpy as np import yt from yt.utilities.amr_kdtree.api import AMRKDTree # Load up a dataset and define the kdtree ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") im, sc = yt.volume_render(ds, ("gas", "density"), fname="v0.png") sc.camera.set_width(ds.arr(100, "kpc")) render_source = sc.get_source() kd = render_source.volume # Print out specifics of KD Tree print("Total volume of all bricks = %i" % kd.count_volume()) print("Total number of cells = %i" % kd.count_cells()) new_source = ds.all_data() new_source.max_level = 3 kd_low_res = AMRKDTree(ds, data_source=new_source) print(kd_low_res.count_volume()) print(kd_low_res.count_cells()) # Now we pass this in as the volume to our camera, and render the snapshot # again. render_source.set_volume(kd_low_res) render_source.set_field(("gas", "density")) sc.save("v1.png", sigma_clip=6.0) # This operation was substantially faster. Now lets modify the low resolution # rendering until we find something we like. tf = render_source.transfer_function tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=np.ones(4, dtype="float64"), colormap="RdBu_r", ) sc.save("v2.png", sigma_clip=6.0) # This looks better. Now let's try turning on opacity. tf.grey_opacity = True sc.save("v3.png", sigma_clip=6.0) # ## That seemed to pick out some interesting structures. Now let's bump up the ## opacity. # tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=10.0 * np.ones(4, dtype="float64"), colormap="RdBu_r", ) tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=10.0 * np.ones(4, dtype="float64"), colormap="RdBu_r", ) sc.save("v4.png", sigma_clip=6.0) # ## This looks pretty good, now lets go back to the full resolution AMRKDTree # render_source.set_volume(kd) sc.save("v5.png", sigma_clip=6.0) # This looks great! yt-project-yt-f043ac8/doc/source/cookbook/annotate_timestamp_and_scale.py000066400000000000000000000004141510711153200267630ustar00rootroot00000000000000import yt ts = yt.load("enzo_tiny_cosmology/DD000?/DD000?") for ds in ts: p = yt.ProjectionPlot(ds, "z", ("gas", "density")) p.annotate_timestamp(corner="upper_left", redshift=True, draw_inset_box=True) p.annotate_scale(corner="upper_right") p.save() yt-project-yt-f043ac8/doc/source/cookbook/annotations.py000066400000000000000000000013011510711153200234270ustar00rootroot00000000000000import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") p = yt.ProjectionPlot(ds, "z", ("gas", "density")) p.annotate_sphere([0.54, 0.72], radius=(1, "Mpc"), coord_system="axis", text="Halo #7") p.annotate_sphere( [0.65, 0.38, 0.3], radius=(1.5, "Mpc"), coord_system="data", circle_args={"color": "green", "linewidth": 4, "linestyle": "dashed"}, ) p.annotate_arrow([0.87, 0.59, 0.2], coord_system="data", color="red") p.annotate_text([10, 20], "Some halos", coord_system="plot") p.annotate_marker([0.45, 0.1, 0.4], coord_system="data", color="yellow", s=500) p.annotate_line([0.2, 0.4], [0.3, 0.9], coord_system="axis") p.annotate_timestamp(redshift=True) p.annotate_scale() p.save() yt-project-yt-f043ac8/doc/source/cookbook/average_value.py000066400000000000000000000010521510711153200237030ustar00rootroot00000000000000import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # load data field = ("gas", "temperature") # The field to average weight = ("gas", "mass") # The weight for the average ad = ds.all_data() # This is a region describing the entire box, # but note it doesn't read anything in yet! # We now use our 'quantities' call to get the average quantity average_value = ad.quantities.weighted_average_quantity(field, weight) print( "Average %s (weighted by %s) is %0.3e %s" % (field[1], weight[1], average_value, average_value.units) ) yt-project-yt-f043ac8/doc/source/cookbook/calculating_information.rst000066400000000000000000000076371510711153200261670ustar00rootroot00000000000000Calculating Dataset Information ------------------------------- These recipes demonstrate methods of calculating quantities in a simulation, either for later visualization or for understanding properties of fluids and particles in the simulation. Average Field Value ~~~~~~~~~~~~~~~~~~~ This recipe is a very simple method of calculating the global average of a given field, as weighted by another field. See :ref:`derived-quantities` for more information. .. yt_cookbook:: average_value.py Mass Enclosed in a Sphere ~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe constructs a sphere and then sums the total mass in particles and fluids in the sphere. See :ref:`available-objects` and :ref:`derived-quantities` for more information. .. yt_cookbook:: sum_mass_in_sphere.py Global Phase Plot ~~~~~~~~~~~~~~~~~ This is a simple recipe to show how to open a dataset and then plot a couple global phase diagrams, save them, and quit. See :ref:`how-to-make-2d-profiles` for more information. .. yt_cookbook:: global_phase_plots.py .. _cookbook-radial-velocity: Radial Velocity Profile ~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to subtract off a bulk velocity on a sphere before calculating the radial velocity within that sphere. See :ref:`how-to-make-1d-profiles` for more information on creating profiles and :ref:`field_parameters` for an explanation of how the bulk velocity is provided to the radial velocity field function. .. yt_cookbook:: rad_velocity.py Simulation Analysis ~~~~~~~~~~~~~~~~~~~ This uses :class:`~yt.data_objects.time_series.DatasetSeries` to calculate the extrema of a series of outputs, whose names it guesses in advance. This will run in parallel and take advantage of multiple MPI tasks. See :ref:`parallel-computation` and :ref:`time-series-analysis` for more information. .. yt_cookbook:: simulation_analysis.py .. _cookbook-time-series-analysis: Time Series Analysis ~~~~~~~~~~~~~~~~~~~~ This recipe shows how to calculate a number of quantities on a set of parameter files. Note that it is parallel aware, and that if you only wanted to run in serial the operation ``for pf in ts:`` would also have worked identically. See :ref:`parallel-computation` and :ref:`time-series-analysis` for more information. .. yt_cookbook:: time_series.py .. _cookbook-simple-derived-fields: Simple Derived Fields ~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to create a simple derived field, ``thermal_energy_density``, and then generate a projection from it. See :ref:`creating-derived-fields` and :ref:`projection-plots` for more information. .. yt_cookbook:: derived_field.py .. _cookbook-complicated-derived-fields: Complicated Derived Fields ~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to use the :meth:`~yt.frontends.flash.data_structures.FLASHDataset.add_gradient_fields` method to generate gradient fields and use them in a more complex derived field. .. yt_cookbook:: hse_field.py Using Particle Filters to Calculate Star Formation Rates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to use a particle filter to calculate the star formation rate in a galaxy evolution simulation. See :ref:`filtering-particles` for more information. .. yt_cookbook:: particle_filter_sfr.py Making a Turbulent Kinetic Energy Power Spectrum ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe shows how to use ``yt`` to read data and put it on a uniform grid to interface with the NumPy FFT routines and create a turbulent kinetic energy power spectrum. (Note: the dataset used here is of low resolution, so the turbulence is not very well-developed. The spike at high wavenumbers is due to non-periodicity in the z-direction). .. yt_cookbook:: power_spectrum_example.py Downsampling an AMR Dataset ~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe shows how to use the ``max_level`` attribute of a yt data object to only select data up to a maximum AMR level. .. yt_cookbook:: downsampling_amr.py yt-project-yt-f043ac8/doc/source/cookbook/camera_movement.py000066400000000000000000000013001510711153200242330ustar00rootroot00000000000000import numpy as np import yt ds = yt.load("MOOSE_sample_data/out.e-s010") sc = yt.create_scene(ds) cam = sc.camera # save an image at the starting position frame = 0 sc.save("camera_movement_%04i.png" % frame) frame += 1 # Zoom out by a factor of 2 over 5 frames for _ in cam.iter_zoom(0.5, 5): sc.save("camera_movement_%04i.png" % frame) frame += 1 # Move to the position [-10.0, 10.0, -10.0] over 5 frames pos = ds.arr([-10.0, 10.0, -10.0], "code_length") for _ in cam.iter_move(pos, 5): sc.save("camera_movement_%04i.png" % frame) frame += 1 # Rotate by 180 degrees over 5 frames for _ in cam.iter_rotate(np.pi, 5): sc.save("camera_movement_%04i.png" % frame) frame += 1 yt-project-yt-f043ac8/doc/source/cookbook/changing_label_formats.py000066400000000000000000000005261510711153200255520ustar00rootroot00000000000000import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Set the format of the ionozation_label to be `plus_minus` # instead of the default `roman_numeral` ds.set_field_label_format("ionization_label", "plus_minus") slc = yt.SlicePlot(ds, "x", ("gas", "H_p1_number_density")) slc.save("plus_minus_ionization_format_sliceplot.png") yt-project-yt-f043ac8/doc/source/cookbook/colormaps.py000066400000000000000000000011501510711153200230730ustar00rootroot00000000000000import yt # Load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a projection and save it with the default colormap ('cmyt.arbre') p = yt.ProjectionPlot(ds, "z", ("gas", "density"), width=(100, "kpc")) p.save() # Change the colormap to 'cmyt.dusk' and save again. We must specify # a different filename here or it will save it over the top of # our first projection. p.set_cmap(field=("gas", "density"), cmap="cmyt.dusk") p.save("proj_with_dusk_cmap.png") # Change the colormap to 'hot' and save again. p.set_cmap(field=("gas", "density"), cmap="hot") p.save("proj_with_hot_cmap.png") yt-project-yt-f043ac8/doc/source/cookbook/complex_plots.rst000066400000000000000000000377721510711153200241670ustar00rootroot00000000000000A Few Complex Plots ------------------- The built-in plotting functionality covers the very simple use cases that are most common. These scripts will demonstrate how to construct more complex plots or publication-quality plots. In many cases these show how to make multi-panel plots. Multi-Width Image ~~~~~~~~~~~~~~~~~ This is a simple recipe to show how to open a dataset and then plot slices through it at varying widths. See :ref:`slice-plots` for more information. .. yt_cookbook:: multi_width_image.py .. _image-resolution-primer: Varying the resolution of an image ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This illustrates the various parameters that control the resolution of an image, including the (deprecated) refinement level, the size of the :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer`, and the number of pixels in the output image. In brief, there are three parameters that control the final resolution, with a fourth entering for particle data that is deposited onto a mesh (i.e. pre-4.0). Those are: 1. ``buff_size``, which can be altered with :meth:`~yt.visualization.plot_window.PlotWindow.set_buff_size`, which is inherited by :class:`~yt.visualization.plot_window.AxisAlignedSlicePlot`, :class:`~yt.visualization.plot_window.OffAxisSlicePlot`, :class:`~yt.visualization.plot_window.AxisAlignedProjectionPlot`, and :class:`~yt.visualization.plot_window.OffAxisProjectionPlot`. This controls the number of resolution elements in the :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer`, which can be thought of as the number of individually colored squares (on a side) in a 2D image. ``buff_size`` can be set after creating the image with :meth:`~yt.visualization.plot_window.PlotWindow.set_buff_size`, or during image creation with the ``buff_size`` argument to any of the four preceding classes. 2. ``figure_size``, which can be altered with either :meth:`~yt.visualization.plot_container.PlotContainer.set_figure_size`, or can be set during image creation with the ``window_size`` argument. This sets the size of the final image (including the visualization and, if applicable, the axes and colorbar as well) in inches. 3. ``dpi``, i.e. the dots-per-inch in your final file, which can also be thought of as the actual resolution of your image. This can only be set on save via the ``mpl_kwargs`` parameter to :meth:`~yt.visualization.plot_container.PlotContainer.save`. The ``dpi`` and ``figure_size`` together set the true resolution of your image (final image will be ``dpi`` :math:`*` ``figure_size`` pixels on a side), so if these are set too low, then your ``buff_size`` will not matter. On the other hand, increasing these without increasing ``buff_size`` accordingly will simply blow up your resolution elements to fill several real pixels. 4. (only for meshed particle data) ``n_ref``, the maximum number of particles in a cell in the oct-tree allowed before it is refined (removed in yt-4.0 as particle data is no longer deposited onto an oct-tree). For particle data, ``n_ref`` effectively sets the underlying resolution of your simulation. Regardless, for either grid data or deposited particle data, your image will never be higher resolution than your simulation data. In other words, if you are visualizing a region 50 kpc across that includes data that reaches a resolution of 100 pc, then there's no reason to set a ``buff_size`` (or a ``dpi`` :math:`*` ``figure_size``) above 50 kpc/ 100 pc = 500. The below script demonstrates how each of these can be varied. .. yt_cookbook:: image_resolution.py Multipanel with Axes Labels ~~~~~~~~~~~~~~~~~~~~~~~~~~~ This illustrates how to use a SlicePlot to control a multipanel plot. This plot uses axes labels to illustrate the length scales in the plot. See :ref:`slice-plots` and the `Matplotlib AxesGrid Object `_ for more information. .. yt_cookbook:: multiplot_2x2.py The above example gives you full control over the plots, but for most purposes, the ``export_to_mpl_figure`` method is a simpler option, allowing us to make a similar plot as: .. yt_cookbook:: multiplot_export_to_mpl.py Multipanel with PhasePlot ~~~~~~~~~~~~~~~~~~~~~~~~~~~ This illustrates how to use PhasePlot in a multipanel plot. See :ref:`how-to-make-2d-profiles` and the `Matplotlib AxesGrid Object `_ for more information. .. yt_cookbook:: multiplot_phaseplot.py Time Series Multipanel ~~~~~~~~~~~~~~~~~~~~~~ This illustrates how to create a multipanel plot of a time series dataset. See :ref:`projection-plots`, :ref:`time-series-analysis`, and the `Matplotlib AxesGrid Object `_ for more information. .. yt_cookbook:: multiplot_2x2_time_series.py Multiple Slice Multipanel ~~~~~~~~~~~~~~~~~~~~~~~~~ This illustrates how to create a multipanel plot of slices along the coordinate axes. To focus on what's happening in the x-y plane, we make an additional Temperature slice for the bottom-right subpanel. See :ref:`slice-plots` and the `Matplotlib AxesGrid Object `_ for more information. .. yt_cookbook:: multiplot_2x2_coordaxes_slice.py Multi-Plot Slice and Projections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This shows how to combine multiple slices and projections into a single image, with detailed control over colorbars, titles and color limits. See :ref:`slice-plots` and :ref:`projection-plots` for more information. .. yt_cookbook:: multi_plot_slice_and_proj.py .. _advanced-multi-panel: Advanced Multi-Plot Multi-Panel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This produces a series of slices of multiple fields with different color maps and zlimits, and makes use of the FixedResolutionBuffer. While this is more complex than the equivalent plot collection-based solution, it allows for a *lot* more flexibility. Every part of the script uses matplotlib commands, allowing its full power to be exercised. See :ref:`slice-plots` and :ref:`projection-plots` for more information. .. yt_cookbook:: multi_plot_3x2_FRB.py Time Series Movie ~~~~~~~~~~~~~~~~~ This shows how to use matplotlib's animation framework with yt plots. .. yt_cookbook:: matplotlib-animation.py .. _cookbook-offaxis_projection: Off-Axis Projection (an alternate method) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to take an image-plane line integral along an arbitrary axis in a simulation. This uses alternate machinery than the standard :ref:`PlotWindow interface ` to create an off-axis projection as demonstrated in this :ref:`recipe `. .. yt_cookbook:: offaxis_projection.py Off-Axis Projection with a Colorbar (an alternate method) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe shows how to generate a colorbar with a projection of a dataset from an arbitrary projection angle (so you are not confined to the x, y, and z axes). This uses alternate machinery than the standard :ref:`PlotWindow interface ` to create an off-axis projection as demonstrated in this :ref:`recipe `. .. yt_cookbook:: offaxis_projection_colorbar.py .. _thin-slice-projections: Thin-Slice Projections ~~~~~~~~~~~~~~~~~~~~~~ This recipe is an example of how to project through only a given data object, in this case a thin region, and then display the result. See :ref:`projection-plots` and :ref:`available-objects` for more information. .. yt_cookbook:: thin_slice_projection.py Plotting Particles Over Fluids ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to overplot particles on top of a fluid image. See :ref:`annotate-particles` for more information. .. yt_cookbook:: overplot_particles.py Plotting Grid Edges Over Fluids ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to overplot grid boxes on top of a fluid image. Each level is represented with a different color from white (low refinement) to black (high refinement). One can change the colormap used for the grids colors by using the cmap keyword (or set it to None to get all grid edges as black). See :ref:`annotate-grids` for more information. .. yt_cookbook:: overplot_grids.py Overplotting Velocity Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to plot velocity vectors on top of a slice. See :ref:`annotate-velocity` for more information. .. yt_cookbook:: velocity_vectors_on_slice.py Overplotting Contours ~~~~~~~~~~~~~~~~~~~~~ This is a simple recipe to show how to open a dataset, plot a slice through it, and add contours of another quantity on top. See :ref:`annotate-contours` for more information. .. yt_cookbook:: contours_on_slice.py Simple Contours in a Slice ~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes it is useful to plot just a few contours of a quantity in a dataset. This shows how one does this by first making a slice, adding contours, and then hiding the colormap plot of the slice to leave the plot containing only the contours that one has added. See :ref:`annotate-contours` for more information. .. yt_cookbook:: simple_contour_in_slice.py Styling Radial Profile Plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates a method of calculating radial profiles for several quantities, styling them and saving out the resultant plot. See :ref:`how-to-make-1d-profiles` for more information. .. yt_cookbook:: radial_profile_styles.py Customized Profile Plot ~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to create a fully customized 1D profile object using the :func:`~yt.data_objects.profiles.create_profile` function and then create a :class:`~yt.visualization.profile_plotter.ProfilePlot` using the customized profile. This illustrates how a ``ProfilePlot`` created this way inherits the properties of the profile it is constructed from. See :ref:`how-to-make-1d-profiles` for more information. .. yt_cookbook:: customized_profile_plot.py Customized Phase Plot ~~~~~~~~~~~~~~~~~~~~~ Similar to the recipe above, this demonstrates how to create a fully customized 2D profile object using the :func:`~yt.data_objects.profiles.create_profile` function and then create a :class:`~yt.visualization.profile_plotter.PhasePlot` using the customized profile object. This illustrates how a ``PhasePlot`` created this way inherits the properties of the profile object from which it is constructed. See :ref:`how-to-make-2d-profiles` for more information. .. yt_cookbook:: customized_phase_plot.py .. _cookbook-camera_movement: Moving a Volume Rendering Camera ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this recipe, we move a camera through a domain and take multiple volume rendering snapshots. This recipe uses an unstructured mesh dataset (see :ref:`unstructured_mesh_rendering`), which makes it easier to visualize what the Camera is doing, but you can manipulate the Camera for other dataset types in exactly the same manner. See :ref:`camera_movement` for more information. .. yt_cookbook:: camera_movement.py Volume Rendering with Custom Camera ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this recipe we modify the :ref:`cookbook-simple_volume_rendering` recipe to use customized camera properties. See :ref:`volume_rendering` for more information. .. yt_cookbook:: custom_camera_volume_rendering.py .. _cookbook-custom-transfer-function: Volume Rendering with a Custom Transfer Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this recipe we modify the :ref:`cookbook-simple_volume_rendering` recipe to use customized camera properties. See :ref:`volume_rendering` for more information. .. yt_cookbook:: custom_transfer_function_volume_rendering.py .. _cookbook-sigma_clip: Volume Rendering with Sigma Clipping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this recipe we output several images with different values of sigma_clip set in order to change the contrast of the resulting image. See :ref:`sigma_clip` for more information. .. yt_cookbook:: sigma_clip.py Zooming into an Image ~~~~~~~~~~~~~~~~~~~~~ This is a recipe that takes a slice through the most dense point, then creates a bunch of frames as it zooms in. It's important to note that this particular recipe is provided to show how to be more flexible and add annotations and the like -- the base system, of a zoomin, is provided by the "yt zoomin" command on the command line. See :ref:`slice-plots` and :ref:`callbacks` for more information. .. yt_cookbook:: zoomin_frames.py .. _cookbook-various_lens: Various Lens Types for Volume Rendering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This example illustrates the usage and feature of different lenses for volume rendering. .. yt_cookbook:: various_lens.py .. _cookbook-opaque_rendering: Opaque Volume Rendering ~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to make semi-opaque volume renderings, but also how to step through and try different things to identify the type of volume rendering you want. See :ref:`opaque_rendering` for more information. .. yt_cookbook:: opaque_rendering.py Volume Rendering Multiple Fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can render multiple fields by adding new ``VolumeSource`` objects to the scene for each field you want to render. .. yt_cookbook:: render_two_fields.py .. _cookbook-amrkdtree_downsampling: Downsampling Data for Volume Rendering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to downsample data in a simulation to speed up volume rendering. See :ref:`volume_rendering` for more information. .. yt_cookbook:: amrkdtree_downsampling.py .. _cookbook-volume_rendering_annotations: Volume Rendering with Bounding Box and Overlaid Grids ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to overplot a bounding box on a volume rendering as well as overplotting grids representing the level of refinement achieved in different regions of the code. See :ref:`volume_rendering_annotations` for more information. .. yt_cookbook:: rendering_with_box_and_grids.py Volume Rendering with Annotation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to write the simulation time, show an axis triad indicating the direction of the coordinate system, and show the transfer function on a volume rendering. Please note that this recipe relies on the old volume rendering interface. While one can continue to use this interface, it may be incompatible with some of the new developments and the infrastructure described in :ref:`volume_rendering`. .. yt_cookbook:: vol-annotated.py .. _cookbook-render_two_fields_tf: Volume Rendering Multiple Fields And Annotation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe shows how to display the transfer functions when rendering multiple fields in a volume render. .. yt_cookbook:: render_two_fields_tf.py .. _cookbook-vol-points: Volume Rendering with Points ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to make a volume rendering composited with point sources. This could represent star or dark matter particles, for example. .. yt_cookbook:: vol-points.py .. _cookbook-vol-lines: Volume Rendering with Lines ~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to make a volume rendering composited with line sources. .. yt_cookbook:: vol-lines.py Plotting Streamlines ~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to display streamlines in a simulation. (Note: streamlines can also be queried for values!) See :ref:`streamlines` for more information. .. yt_cookbook:: streamlines.py Plotting Isocontours ~~~~~~~~~~~~~~~~~~~~ This recipe demonstrates how to extract an isocontour and then plot it in matplotlib, coloring the surface by a second quantity. See :ref:`surfaces` for more information. .. yt_cookbook:: surface_plot.py Plotting Isocontours and Streamlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe plots both isocontours and streamlines simultaneously. Note that this will not include any blending, so streamlines that are occluded by the surface will still be visible. See :ref:`streamlines` and :ref:`surfaces` for more information. .. yt_cookbook:: streamlines_isocontour.py yt-project-yt-f043ac8/doc/source/cookbook/constructing_data_objects.rst000066400000000000000000000023271510711153200265070ustar00rootroot00000000000000Constructing Data Objects ------------------------- These recipes demonstrate a few uncommon methods of constructing data objects from a simulation. Creating Particle Filters ~~~~~~~~~~~~~~~~~~~~~~~~~ Create particle filters based on the age of star particles in an isolated disk galaxy simulation. Determine the total mass of each stellar age bin in the simulation. Generate projections for each of the stellar age bins. .. yt_cookbook:: particle_filter.py .. _cookbook-find_clumps: Identifying Clumps ~~~~~~~~~~~~~~~~~~ This is a recipe to show how to find topologically connected sets of cells inside a dataset. It returns these clumps and they can be inspected or visualized as would any other data object. More detail on this method can be found in `Smith et al. 2009 `_. .. yt_cookbook:: find_clumps.py .. _extract_frb: Extracting Fixed Resolution Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a recipe to show how to open a dataset and extract it to a file at a fixed resolution with no interpolation or smoothing. Additionally, this recipe shows how to insert a dataset into an external HDF5 file using h5py. .. yt_cookbook:: extract_fixed_resolution_data.py yt-project-yt-f043ac8/doc/source/cookbook/contours_on_slice.py000066400000000000000000000006701510711153200246310ustar00rootroot00000000000000import yt # first add density contours on a density slice ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") # add density contours on the density slice. p = yt.SlicePlot(ds, "x", ("gas", "density")) p.annotate_contour(("gas", "density")) p.save() # then add temperature contours on the same density slice p = yt.SlicePlot(ds, "x", ("gas", "density")) p.annotate_contour(("gas", "temperature")) p.save(str(ds) + "_T_contour") yt-project-yt-f043ac8/doc/source/cookbook/count.sh000066400000000000000000000001751510711153200222140ustar00rootroot00000000000000for fn in *.py do COUNT=`cat *.rst | grep --count ${fn}` if [ $COUNT -lt 1 ] then echo ${fn} fi done yt-project-yt-f043ac8/doc/source/cookbook/custom_camera_volume_rendering.py000066400000000000000000000012161510711153200273450ustar00rootroot00000000000000import yt # Load the dataset ds = yt.load("Enzo_64/DD0043/data0043") # Create a volume rendering sc = yt.create_scene(ds, field=("gas", "density")) # Now increase the resolution sc.camera.resolution = (1024, 1024) # Set the camera focus to a position that is offset from the center of # the domain sc.camera.focus = ds.arr([0.3, 0.3, 0.3], "unitary") # Move the camera position to the other side of the dataset sc.camera.position = ds.arr([0, 0, 0], "unitary") # save to disk with a custom filename and apply sigma clipping to eliminate # very bright pixels, producing an image with better contrast. sc.render() sc.save("custom.png", sigma_clip=4) yt-project-yt-f043ac8/doc/source/cookbook/custom_colorbar_tickmarks.ipynb000066400000000000000000000054141510711153200270410ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Custom Colorbar Tickmarks" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import yt" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load(\"IsolatedGalaxy/galaxy0030/galaxy0030\")\n", "slc = yt.SlicePlot(ds, \"x\", (\"gas\", \"density\"))\n", "slc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`PlotWindow` plots are containers for plots, keyed to field names. Below, we get a copy of the plot for the `Density` field." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plot = slc.plots[\"gas\", \"density\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The plot has a few attributes that point to underlying `matplotlib` plot primitives. For example, the `colorbar` object corresponds to the `cb` attribute of the plot." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "colorbar = plot.cb" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we call `_setup_plots()` to ensure the plot is properly initialized. Without this, the custom tickmarks we are adding will be ignored." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "slc._setup_plots()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To set custom tickmarks, simply call the `matplotlib` [`set_ticks`](https://matplotlib.org/stable/api/colorbar_api.html#matplotlib.colorbar.ColorbarBase.set_ticks) and [`set_ticklabels`](https://matplotlib.org/stable/api/colorbar_api.html#matplotlib.colorbar.ColorbarBase.set_ticklabels) functions." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "colorbar.set_ticks([1e-28])\n", "colorbar.set_ticklabels([\"$10^{-28}$\"])\n", "slc" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/cookbook/custom_transfer_function_volume_rendering.py000066400000000000000000000012361510711153200316500ustar00rootroot00000000000000import numpy as np import yt # Load the dataset ds = yt.load("Enzo_64/DD0043/data0043") # Create a volume rendering sc = yt.create_scene(ds, field=("gas", "density")) # Modify the transfer function # First get the render source, in this case the entire domain, # with field ('gas','density') render_source = sc.get_source() # Clear the transfer function render_source.transfer_function.clear() # Map a range of density values (in log space) to the Reds_r colormap render_source.transfer_function.map_to_colormap( np.log10(ds.quan(5.0e-31, "g/cm**3")), np.log10(ds.quan(1.0e-29, "g/cm**3")), scale=30.0, colormap="RdBu_r", ) sc.save("new_tf.png") yt-project-yt-f043ac8/doc/source/cookbook/customized_phase_plot.py000066400000000000000000000013341510711153200255040ustar00rootroot00000000000000import yt import yt.units as u ds = yt.load("HiresIsolatedGalaxy/DD0044/DD0044") center = [0.53, 0.53, 0.53] normal = [0, 0, 1] radius = 40 * u.kpc height = 2 * u.kpc disk = ds.disk(center, [0, 0, 1], radius, height) profile = yt.create_profile( data_source=disk, bin_fields=[("index", "radius"), ("gas", "velocity_cylindrical_theta")], fields=[("gas", "mass")], n_bins=256, units=dict(radius="kpc", velocity_cylindrical_theta="km/s", mass="Msun"), logs=dict(radius=False, velocity_cylindrical_theta=False), weight_field=None, extrema=dict(radius=(0, 40), velocity_cylindrical_theta=(-250, 250)), ) plot = yt.PhasePlot.from_profile(profile) plot.set_cmap(("gas", "mass"), "YlOrRd") plot.save() yt-project-yt-f043ac8/doc/source/cookbook/customized_profile_plot.py000066400000000000000000000013231510711153200260420ustar00rootroot00000000000000import yt import yt.units as u ds = yt.load("HiresIsolatedGalaxy/DD0044/DD0044") center = [0.53, 0.53, 0.53] normal = [0, 0, 1] radius = 40 * u.kpc height = 5 * u.kpc disk = ds.disk(center, [0, 0, 1], radius, height) profile = yt.create_profile( data_source=disk, bin_fields=[("index", "radius")], fields=[("gas", "velocity_cylindrical_theta")], n_bins=256, units=dict(radius="kpc", velocity_cylindrical_theta="km/s"), logs=dict(radius=False), weight_field=("gas", "mass"), extrema=dict(radius=(0, 40)), ) plot = yt.ProfilePlot.from_profiles(profile) plot.set_log(("gas", "velocity_cylindrical_theta"), False) plot.set_ylim(("gas", "velocity_cylindrical_theta"), 60, 160) plot.save() yt-project-yt-f043ac8/doc/source/cookbook/derived_field.py000066400000000000000000000016471510711153200236740ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # You can create a derived field by manipulating any existing derived fields # in any way you choose. In this case, let's just make a simple one: # thermal_energy_density = 3/2 nkT # First create a function which yields your new derived field def thermal_energy_dens(field, data): return (3 / 2) * data["gas", "number_density"] * data["gas", "kT"] # Then add it to your dataset and define the units ds.add_field( ("gas", "thermal_energy_density"), units="erg/cm**3", function=thermal_energy_dens, sampling_type="cell", ) # It will now show up in your derived_field_list for i in sorted(ds.derived_field_list): print(i) # Let's use it to make a projection ad = ds.all_data() yt.ProjectionPlot( ds, "x", ("gas", "thermal_energy_density"), weight_field=("gas", "density"), width=(200, "kpc"), ).save() yt-project-yt-f043ac8/doc/source/cookbook/downsampling_amr.py000066400000000000000000000014651510711153200244460ustar00rootroot00000000000000import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # The maximum refinement level of this dataset is 8 print(ds.max_level) # If we ask for *all* of the AMR data, we get back field # values sampled at about 3.6 million AMR zones ad = ds.all_data() print(ad["gas", "density"].shape) # Let's only sample data up to AMR level 2 ad.max_level = 2 # Now we only sample from about 200,000 zones print(ad["gas", "density"].shape) # Note that this includes data at level 2 that would # normally be masked out. There aren't any "holes" in # the downsampled AMR mesh, the volume still sums to # the volume of the domain: print(ad["gas", "volume"].sum()) print(ds.domain_width.prod()) # Now let's make a downsampled plot plot = yt.SlicePlot(ds, "z", ("gas", "density"), data_source=ad) plot.save("downsampled.png") yt-project-yt-f043ac8/doc/source/cookbook/extract_fixed_resolution_data.py000066400000000000000000000020541510711153200272050ustar00rootroot00000000000000# For this example we will use h5py to write to our output file. import h5py import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") level = 2 dims = ds.domain_dimensions * ds.refine_by**level # We construct an object that describes the data region and structure we want # In this case, we want all data up to the maximum "level" of refinement # across the entire simulation volume. Higher levels than this will not # contribute to our covering grid. cube = ds.covering_grid( level, left_edge=[0.0, 0.0, 0.0], dims=dims, # And any fields to preload (this is optional!) fields=[("gas", "density")], ) # Now we open our output file using h5py # Note that we open with 'w' (write), which will overwrite existing files! f = h5py.File("my_data.h5", mode="w") # We create a dataset at the root, calling it "density" f.create_dataset("/density", data=cube["gas", "density"]) # We close our file f.close() # If we want to then access this datacube in the h5 file, we can now... f = h5py.File("my_data.h5", mode="r") print(f["density"][()]) yt-project-yt-f043ac8/doc/source/cookbook/find_clumps.py000066400000000000000000000046561510711153200234150ustar00rootroot00000000000000import numpy as np import yt from yt.data_objects.level_sets.api import Clump, find_clumps ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") data_source = ds.disk([0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8, "kpc"), (1, "kpc")) # the field to be used for contouring field = ("gas", "density") # This is the multiplicative interval between contours. step = 2.0 # Now we set some sane min/max values between which we want to find contours. # This is how we tell the clump finder what to look for -- it won't look for # contours connected below or above these threshold values. c_min = 10 ** np.floor(np.log10(data_source[field]).min()) c_max = 10 ** np.floor(np.log10(data_source[field]).max() + 1) # Now find get our 'base' clump -- this one just covers the whole domain. master_clump = Clump(data_source, field) # Add a "validator" to weed out clumps with less than 20 cells. # As many validators can be added as you want. master_clump.add_validator("min_cells", 20) # Calculate center of mass for all clumps. master_clump.add_info_item("center_of_mass") # Begin clump finding. find_clumps(master_clump, c_min, c_max, step) # Save the clump tree as a reloadable dataset fn = master_clump.save_as_dataset(fields=[("gas", "density"), ("all", "particle_mass")]) # We can traverse the clump hierarchy to get a list of all of the 'leaf' clumps leaf_clumps = master_clump.leaves # Get total cell and particle masses for each leaf clump leaf_masses = [leaf.quantities.total_mass() for leaf in leaf_clumps] # If you'd like to visualize these clumps, a list of clumps can be supplied to # the "clumps" callback on a plot. First, we create a projection plot: prj = yt.ProjectionPlot(ds, 2, field, center="c", width=(20, "kpc")) # Next we annotate the plot with contours on the borders of the clumps prj.annotate_clumps(leaf_clumps) # Save the plot to disk. prj.save("clumps") # Reload the clump dataset. cds = yt.load(fn) # Clump annotation can also be done with the reloaded clump dataset. # Remove the original clump annotation prj.clear_annotations() # Get the leaves and add the callback. leaf_clumps_reloaded = cds.leaves prj.annotate_clumps(leaf_clumps_reloaded) prj.save("clumps_reloaded") # Query fields for clumps in the tree. print(cds.tree["clump", "center_of_mass"]) print(cds.tree.children[0]["grid", "density"]) print(cds.tree.children[1]["all", "particle_mass"]) # Get all of the leaf clumps. print(cds.leaves) print(cds.leaves[0]["clump", "cell_mass"]) yt-project-yt-f043ac8/doc/source/cookbook/fits_radio_cubes.ipynb000066400000000000000000000305761510711153200251070ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Analyzing FITS Radio Cubes" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline\n", "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook demonstrates some of the capabilities of yt on some FITS \"position-position-spectrum\" cubes of radio data.\n", "\n", "Note that it depends on some external dependencies, including `astropy` and `regions`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## M33 VLA Image" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dataset `\"m33_hi.fits\"` has `NaN`s in it, so we'll mask them out by setting `nan_mask` = 0:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load(\"radio_fits/m33_hi.fits\", nan_mask=0.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we'll take a slice of the data along the z-axis, which is the velocity axis of the FITS cube:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"z\", (\"fits\", \"intensity\"), origin=\"native\")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The x and y axes are in units of the image pixel. When making plots of FITS data, to see the image coordinates as they are in the file, it is helpful to set the keyword `origin = \"native\"`. If you want to see the celestial coordinates along the axes, you can import the `PlotWindowWCS` class and feed it the `SlicePlot`. For this to work, a version of AstroPy >= 1.3 needs to be installed." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from yt.frontends.fits.misc import PlotWindowWCS\n", "\n", "PlotWindowWCS(slc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generally, it is best to get the plot in the shape you want it before feeding it to `PlotWindowWCS`. Once it looks the way you want, you can save it just like a normal `PlotWindow` plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc.save()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also take slices of this dataset at a few different values along the \"z\" axis (corresponding to the velocity), so let's try a few. To pick specific velocity values for slices, we will need to use the dataset's `spec2pixel` method to determine which pixels to slice on:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import yt.units as u\n", "\n", "new_center = ds.domain_center\n", "new_center[2] = ds.spec2pixel(-250000.0 * u.m / u.s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use this new center to create a new slice:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"z\", (\"fits\", \"intensity\"), center=new_center, origin=\"native\")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do this a few more times for different values of the velocity:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "new_center[2] = ds.spec2pixel(-100000.0 * u.m / u.s)\n", "slc = yt.SlicePlot(ds, \"z\", (\"fits\", \"intensity\"), center=new_center, origin=\"native\")\n", "slc.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "new_center[2] = ds.spec2pixel(-150000.0 * u.m / u.s)\n", "slc = yt.SlicePlot(ds, \"z\", (\"fits\", \"intensity\"), center=new_center, origin=\"native\")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These slices demonstrate the intensity of the radio emission at different line-of-sight velocities. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also make a projection of all the emission along the line of sight:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "prj = yt.ProjectionPlot(ds, \"z\", (\"fits\", \"intensity\"), origin=\"native\")\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also look at the slices perpendicular to the other axes, which will show us the structure along the velocity axis:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"x\", (\"fits\", \"intensity\"), origin=\"native\", window_size=(8, 8))\n", "slc.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"y\", (\"fits\", \"intensity\"), origin=\"native\", window_size=(8, 8))\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In these cases, we needed to explicitly declare a square `window_size` to get a figure that looks good. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## $^{13}$CO GRS Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This next example uses one of the cubes from the [Boston University Galactic Ring Survey](http://www.bu.edu/galacticring/new_index.htm). " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load(\"radio_fits/grs-50-cube.fits\", nan_mask=0.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the `quantities` methods to determine derived quantities of the dataset. For example, we could find the maximum and minimum temperature:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dd = ds.all_data() # A region containing the entire dataset\n", "extrema = dd.quantities.extrema((\"fits\", \"temperature\"))\n", "print(extrema)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can compute the average temperature along the \"velocity\" axis for all positions by making a `ProjectionPlot`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds, \"z\", (\"fits\", \"temperature\"), origin=\"native\", weight_field=(\"index\", \"ones\")\n", ") # \"ones\" weights each cell by 1\n", "prj.set_zlim((\"fits\", \"temperature\"), zmin=(1e-3, \"K\"))\n", "prj.set_log((\"fits\", \"temperature\"), True)\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also make a histogram of the temperature field of this region:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pplot = yt.ProfilePlot(\n", " dd, (\"fits\", \"temperature\"), [(\"index\", \"ones\")], weight_field=None, n_bins=128\n", ")\n", "pplot.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see from this histogram and our calculation of the dataset's extrema that there is a lot of noise. Suppose we wanted to make a projection, but instead make it only of the cells which had a positive temperature value. We can do this by doing a \"field cut\" on the data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fc = dd.cut_region(['obj[\"fits\", \"temperature\"] > 0'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's check the extents of this region:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(fc.quantities.extrema((\"fits\", \"temperature\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks like we were successful in filtering out the negative temperatures. To compute the average temperature of this new region:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fc.quantities.weighted_average_quantity((\"fits\", \"temperature\"), (\"index\", \"ones\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's make a projection of the dataset, using the field cut `fc` as a `data_source`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds,\n", " \"z\",\n", " [(\"fits\", \"temperature\")],\n", " data_source=fc,\n", " origin=\"native\",\n", " weight_field=(\"index\", \"ones\"),\n", ") # \"ones\" weights each cell by 1\n", "prj.set_log((\"fits\", \"temperature\"), True)\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can also take an existing [ds9](http://ds9.si.edu/site/Home.html) region and use it to create a \"cut region\" as well, using `ds9_region` (the [regions](https://astropy-regions.readthedocs.io/) package needs to be installed for this):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from yt.frontends.fits.misc import ds9_region" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this example we'll create a ds9 region from scratch and load it up:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "region = 'galactic;box(+49:26:35.150,-0:30:04.410,1926.1927\",1483.3701\",0.0)'\n", "box_reg = ds9_region(ds, region)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This region may now be used to compute derived quantities:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(box_reg.quantities.extrema((\"fits\", \"temperature\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or in projections:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds,\n", " \"z\",\n", " (\"fits\", \"temperature\"),\n", " origin=\"native\",\n", " data_source=box_reg,\n", " weight_field=(\"index\", \"ones\"),\n", ") # \"ones\" weights each cell by 1\n", "prj.set_zlim((\"fits\", \"temperature\"), 1.0e-2, 1.5)\n", "prj.set_log((\"fits\", \"temperature\"), True)\n", "prj.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/cookbook/fits_xray_images.ipynb000066400000000000000000000331241510711153200251300ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# X-ray FITS Images" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline\n", "import numpy as np\n", "\n", "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows how to use yt to make plots and examine FITS X-ray images and events files. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sloshing, Shocks, and Bubbles in Abell 2052" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example uses data provided by [Scott Randall](http://hea-www.cfa.harvard.edu/~srandall/), presented originally in [Blanton, E.L., Randall, S.W., Clarke, T.E., et al. 2011, ApJ, 737, 99](https://ui.adsabs.harvard.edu/abs/2011ApJ...737...99B). They consist of two files, a \"flux map\" in counts/s/pixel between 0.3 and 2 keV, and a spectroscopic temperature map in keV. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load(\n", " \"xray_fits/A2052_merged_0.3-2_match-core_tmap_bgecorr.fits\",\n", " auxiliary_files=[\"xray_fits/A2052_core_tmap_b1_m2000_.fits\"],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the flux and projected temperature images are in two different files, we had to use one of them (in this case the \"flux\" file) as a master file, and pass in the \"temperature\" file with the `auxiliary_files` keyword to `load`. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, let's derive some new fields for the number of counts, the \"pseudo-pressure\", and the \"pseudo-entropy\":" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def _counts(field, data):\n", " exposure_time = data.get_field_parameter(\"exposure_time\")\n", " return data[\"fits\", \"flux\"] * data[\"fits\", \"pixel\"] * exposure_time\n", "\n", "\n", "ds.add_field(\n", " (\"gas\", \"counts\"),\n", " function=_counts,\n", " sampling_type=\"cell\",\n", " units=\"counts\",\n", " take_log=False,\n", ")\n", "\n", "\n", "def _pp(field, data):\n", " return np.sqrt(data[\"gas\", \"counts\"]) * data[\"fits\", \"projected_temperature\"]\n", "\n", "\n", "ds.add_field(\n", " (\"gas\", \"pseudo_pressure\"),\n", " function=_pp,\n", " sampling_type=\"cell\",\n", " units=\"sqrt(counts)*keV\",\n", " take_log=False,\n", ")\n", "\n", "\n", "def _pe(field, data):\n", " return data[\"fits\", \"projected_temperature\"] * data[\"gas\", \"counts\"] ** (-1.0 / 3.0)\n", "\n", "\n", "ds.add_field(\n", " (\"gas\", \"pseudo_entropy\"),\n", " function=_pe,\n", " sampling_type=\"cell\",\n", " units=\"keV*(counts)**(-1/3)\",\n", " take_log=False,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we're deriving a \"counts\" field from the \"flux\" field by passing it a `field_parameter` for the exposure time of the time and multiplying by the pixel scale. Second, we use the fact that the surface brightness is strongly dependent on density ($S_X \\propto \\rho^2$) to use the counts in each pixel as a \"stand-in\". Next, we'll grab the exposure time from the primary FITS header of the flux file and create a `YTQuantity` from it, to be used as a `field_parameter`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "exposure_time = ds.quan(ds.primary_header[\"exposure\"], \"s\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can make the `SlicePlot` object of the fields we want, passing in the `exposure_time` as a `field_parameter`. We'll also set the width of the image to 250 pixels." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(\n", " ds,\n", " \"z\",\n", " [\n", " (\"fits\", \"flux\"),\n", " (\"fits\", \"projected_temperature\"),\n", " (\"gas\", \"pseudo_pressure\"),\n", " (\"gas\", \"pseudo_entropy\"),\n", " ],\n", " origin=\"native\",\n", " field_parameters={\"exposure_time\": exposure_time},\n", ")\n", "slc.set_log((\"fits\", \"flux\"), True)\n", "slc.set_zlim((\"fits\", \"flux\"), 1e-5)\n", "slc.set_log((\"gas\", \"pseudo_pressure\"), False)\n", "slc.set_log((\"gas\", \"pseudo_entropy\"), False)\n", "slc.set_width(250.0)\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To add the celestial coordinates to the image, we can use `PlotWindowWCS`, if you have a recent version of AstroPy (>= 1.3) installed:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from yt.frontends.fits.misc import PlotWindowWCS\n", "\n", "wcs_slc = PlotWindowWCS(slc)\n", "wcs_slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can make use of yt's facilities for profile plotting as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v, c = ds.find_max((\"fits\", \"flux\")) # Find the maximum flux and its center\n", "my_sphere = ds.sphere(c, (100.0, \"code_length\")) # Radius of 150 pixels\n", "my_sphere.set_field_parameter(\"exposure_time\", exposure_time)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Such as a radial profile plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "radial_profile = yt.ProfilePlot(\n", " my_sphere,\n", " \"radius\",\n", " [\"counts\", \"pseudo_pressure\", \"pseudo_entropy\"],\n", " n_bins=30,\n", " weight_field=\"ones\",\n", ")\n", "radial_profile.set_log(\"counts\", True)\n", "radial_profile.set_log(\"pseudo_pressure\", True)\n", "radial_profile.set_log(\"pseudo_entropy\", True)\n", "radial_profile.set_xlim(3, 100.0)\n", "radial_profile.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or a phase plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "phase_plot = yt.PhasePlot(\n", " my_sphere, \"pseudo_pressure\", \"pseudo_entropy\", [\"counts\"], weight_field=None\n", ")\n", "phase_plot.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can also take an existing [ds9](http://ds9.si.edu/site/Home.html) region and use it to create a \"cut region\", using `ds9_region` (the [regions](https://astropy-regions.readthedocs.io/) package needs to be installed for this):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from yt.frontends.fits.misc import ds9_region\n", "\n", "reg_file = [\n", " \"# Region file format: DS9 version 4.1\\n\",\n", " \"global color=green dashlist=8 3 width=3 include=1 source=1\\n\",\n", " \"FK5\\n\",\n", " 'circle(15:16:44.817,+7:01:19.62,34.6256\")',\n", "]\n", "f = open(\"circle.reg\", \"w\")\n", "f.writelines(reg_file)\n", "f.close()\n", "circle_reg = ds9_region(\n", " ds, \"circle.reg\", field_parameters={\"exposure_time\": exposure_time}\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This region may now be used to compute derived quantities:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(\n", " circle_reg.quantities.weighted_average_quantity(\"projected_temperature\", \"counts\")\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or used in projections:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds,\n", " \"z\",\n", " [\n", " (\"fits\", \"flux\"),\n", " (\"fits\", \"projected_temperature\"),\n", " (\"gas\", \"pseudo_pressure\"),\n", " (\"gas\", \"pseudo_entropy\"),\n", " ],\n", " origin=\"native\",\n", " field_parameters={\"exposure_time\": exposure_time},\n", " data_source=circle_reg,\n", " method=\"sum\",\n", ")\n", "prj.set_log((\"fits\", \"flux\"), True)\n", "prj.set_zlim((\"fits\", \"flux\"), 1e-5)\n", "prj.set_log((\"gas\", \"pseudo_pressure\"), False)\n", "prj.set_log((\"gas\", \"pseudo_entropy\"), False)\n", "prj.set_width(250.0)\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Bullet Cluster" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example uses an events table file from a ~100 ks exposure of the \"Bullet Cluster\" from the [Chandra Data Archive](http://cxc.harvard.edu/cda/). In this case, the individual photon events are treated as particle fields in yt. However, you can make images of the object in different energy bands using the `setup_counts_fields` function. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from yt.frontends.fits.api import setup_counts_fields" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`load` will handle the events file as FITS image files, and will set up a grid using the WCS information in the file. Optionally, the events may be reblocked to a new resolution. by setting the `\"reblock\"` parameter in the `parameters` dictionary in `load`. `\"reblock\"` must be a power of 2. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds2 = yt.load(\"xray_fits/acisf05356N003_evt2.fits.gz\", parameters={\"reblock\": 2})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`setup_counts_fields` will take a list of energy bounds (emin, emax) in keV and create a new field from each where the photons in that energy range will be deposited onto the image grid. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ebounds = [(0.1, 2.0), (2.0, 5.0)]\n", "setup_counts_fields(ds2, ebounds)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The \"x\", \"y\", \"energy\", and \"time\" fields in the events table are loaded as particle fields. Each one has a name given by \"event\\_\" plus the name of the field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dd = ds2.all_data()\n", "print(dd[\"io\", \"event_x\"])\n", "print(dd[\"io\", \"event_y\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we'll make a plot of the two counts fields we made, and pan and zoom to the bullet:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(\n", " ds2, \"z\", [(\"gas\", \"counts_0.1-2.0\"), (\"gas\", \"counts_2.0-5.0\")], origin=\"native\"\n", ")\n", "slc.pan((100.0, 100.0))\n", "slc.set_width(500.0)\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The counts fields can take the field parameter `\"sigma\"` and use [AstroPy's convolution routines](https://astropy.readthedocs.io/en/latest/convolution/) to smooth the data with a Gaussian:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(\n", " ds2,\n", " \"z\",\n", " [(\"gas\", \"counts_0.1-2.0\"), (\"gas\", \"counts_2.0-5.0\")],\n", " origin=\"native\",\n", " field_parameters={\"sigma\": 2.0},\n", ") # This value is in pixel scale\n", "slc.pan((100.0, 100.0))\n", "slc.set_width(500.0)\n", "slc.set_zlim((\"gas\", \"counts_0.1-2.0\"), 0.01, 100.0)\n", "slc.set_zlim((\"gas\", \"counts_2.0-5.0\"), 0.01, 50.0)\n", "slc.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/cookbook/geographic_xforms_and_projections.ipynb000066400000000000000000000353461510711153200305520ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Geographic Transforms and Projections" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading the GEOS data " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this analysis we'll be loading some global climate data into yt. A frontend does not exist for this dataset yet, so we'll load it in as a uniform grid with netcdf4." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import re\n", "\n", "import netCDF4 as nc4\n", "import numpy as np\n", "\n", "import yt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def get_data_path(arg):\n", " if os.path.exists(arg):\n", " return arg\n", " else:\n", " return os.path.join(yt.config.ytcfg.get(\"yt\", \"test_data_dir\"), arg)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n = nc4.Dataset(get_data_path(\"geos/GEOS.fp.asm.inst3_3d_aer_Nv.20180822_0900.V01.nc4\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the loaded data we'll fill arrays with the data dimensions and limits. We'll also rename `vertical level` to `altitude` to be clearer. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dims = []\n", "sizes = []\n", "bbox = []\n", "ndims = len(n.dimensions)\n", "for dim in n.dimensions.keys():\n", " size = n.variables[dim].size\n", " if size > 1:\n", " bbox.append([n.variables[dim][:].min(), n.variables[dim][:].max()])\n", " dims.append(n.variables[dim].long_name)\n", " sizes.append(size)\n", "dims.reverse() # Fortran ordering\n", "sizes.reverse()\n", "bbox.reverse()\n", "dims = [f.replace(\"vertical level\", \"altitude\") for f in dims]\n", "bbox = np.array(bbox)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll also load the data into a container dictionary and create a lookup for the short to the long names " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "w_regex = re.compile(r\"([a-zA-Z]+)(.*)\")\n", "\n", "\n", "def regex_parser(s):\n", " try:\n", " return \"**\".join(filter(None, w_regex.search(s).groups()))\n", " except AttributeError:\n", " return s" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = {}\n", "names = {}\n", "for field, d in n.variables.items():\n", " if d.ndim != ndims:\n", " continue\n", " units = n.variables[field].units\n", " units = \" * \".join(map(regex_parser, units.split()))\n", " data[field] = (np.squeeze(d), str(units))\n", " names[field] = n.variables[field].long_name.replace(\"_\", \" \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the data can be loaded with yt's `load_uniform_grid` function. We also need to say that the geometry is a `geographic` type. This will ensure that the axes created are matplotlib GeoAxes and that the transform functions are available to use for projections. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ds = yt.load_uniform_grid(\n", " data, sizes, 1.0, geometry=\"geographic\", bbox=bbox, axis_order=dims\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Default projection with geographic geometry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the data is loaded, we can plot it with a yt SlicePlot along the altitude. This will create a figure with latitude and longitude as the plot axes and the colormap will correspond to the air density. Because no projection type has been set, the geographic geometry type assumes that the data is of the `PlateCarree` form. The resulting figure will be a `Mollweide` plot. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = yt.SlicePlot(ds, \"altitude\", \"AIRDENS\")\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that this doesn't have a lot of contextual information. We can add annotations for the coastlines just as we would with matplotlib. Before the annotations are set, we need to call `p._setup_plots` to make the axes available for annotation. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = yt.SlicePlot(ds, \"altitude\", \"AIRDENS\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using geographic transforms to project data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If a projection other than the default `Mollweide` is desired, then we can pass an argument to the `set_mpl_projection()` function to set a different projection than the default. This will set the projection to a Robinson projection. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = yt.SlicePlot(ds, \"altitude\", \"AIRDENS\")\n", "p.set_mpl_projection(\"Robinson\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`geo_projection` accepts a string or a 2- to 3- length sequence describing the projection the second item in the sequence are the args and the third item is the kwargs. This can be used for further customization of the projection. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = yt.SlicePlot(ds, \"altitude\", \"AIRDENS\")\n", "p.set_mpl_projection((\"Robinson\", (37.5,)))\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We don't actually need to keep creating a SlicePlot to change the projection type. We can use the function `set_mpl_projection()` and pass in a string of the transform type that we desire after an existing `SlicePlot` instance has been created. This will set the figure to an `Orthographic` projection. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"Orthographic\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`set_mpl_projection()` can be used in a number of ways to customize the projection type. \n", "* If a **string** is passed, then the string must correspond to the transform name, which is exclusively cartopy transforms at this time. This looks like: `set_mpl_projection('ProjectionType')`\n", "\n", "* If a **tuple** is passed, the first item of the tuple is a string of the transform name and the second two items are args and kwargs. These can be used to further customize the transform (by setting the latitude and longitude, for example. This looks like: \n", " * `set_mpl_projection(('ProjectionType', (args)))`\n", " * `set_mpl_projection(('ProjectionType', (args), {kwargs}))`\n", "* A **transform object** can also be passed. This can be any transform type -- a cartopy transform or a matplotlib transform. This allows users to either pass the same transform object around between plots or define their own transform and use that in yt's plotting functions. With a standard cartopy transform, this would look like:\n", " * `set_mpl_projection(cartopy.crs.PlateCarree())`\n", " \n", "To summarize:\n", "The function `set_mpl_projection` can take one of several input types:\n", "* `set_mpl_projection('ProjectionType')`\n", "* `set_mpl_projection(('ProjectionType', (args)))`\n", "* `set_mpl_projection(('ProjectionType', (args), {kwargs}))`\n", "* `set_mpl_projection(cartopy.crs.MyTransform())`\n", "\n", "For example, we can make the same Orthographic projection and pass in the central latitude and longitude for the projection: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection((\"Orthographic\", (90, 45)))\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or we can pass in the arguments to this function as kwargs by passing a three element tuple. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\n", " (\"Orthographic\", (), {\"central_latitude\": -45, \"central_longitude\": 275})\n", ")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A few examples of different projections" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This next section will show a few of the different projections that one can use. This isn't meant to be complete, but it'll give you a visual idea of how these transforms can be used to illustrate geographic data for different purposes. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection((\"RotatedPole\", (177.5, 37.5)))\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\n", " (\"RotatedPole\", (), {\"pole_latitude\": 37.5, \"pole_longitude\": 177.5})\n", ")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"NorthPolarStereo\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"AlbersEqualArea\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"InterruptedGoodeHomolosine\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"Robinson\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"Gnomonic\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Modifying the data transform" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While the data projection modifies how the data is displayed in our plot, the data transform describes the coordinate system that the data is actually described by. By default, the data is assumed to have a `PlateCarree` data transform. If you would like to change this, you can access the dictionary in the coordinate handler and set it to something else. The dictionary is structured such that each axis has its own default transform, so be sure to set the axis you intend to change. This next example changes the transform to a Miller type. Because our data is not in Miller coordinates, it will be skewed. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ds.coordinates.data_transform[\"altitude\"] = \"Miller\"\n", "p = yt.SlicePlot(ds, \"altitude\", \"AIRDENS\")\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because the transform type shouldn't change as we make subsequent figures, once it is changed it will be the same for all other figures made with the same dataset object. Note that this particular dataset is not actually in a Miller system, which is why the data now doesn't span the entire globe. Setting the new projection to Robinson results in Miller-skewed data in our next figure. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p.set_mpl_projection(\"Robinson\")\n", "p._setup_plots()\n", "p.plots[\"AIRDENS\"].axes.set_global()\n", "p.plots[\"AIRDENS\"].axes.coastlines()\n", "p.show()" ] } ], "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.10" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/cookbook/global_phase_plots.py000066400000000000000000000006221510711153200247400ustar00rootroot00000000000000import yt # load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # This is an object that describes the entire box ad = ds.all_data() # We plot the average velocity magnitude (mass-weighted) in our object # as a function of density and temperature plot = yt.PhasePlot( ad, ("gas", "density"), ("gas", "temperature"), ("gas", "velocity_magnitude") ) # save the plot plot.save() yt-project-yt-f043ac8/doc/source/cookbook/hse_field.py000066400000000000000000000030141510711153200230170ustar00rootroot00000000000000import numpy as np import yt # Open a dataset from when there's a lot of sloshing going on. ds = yt.load("GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0350") # Define the components of the gravitational acceleration vector field by # taking the gradient of the gravitational potential grad_fields = ds.add_gradient_fields(("gas", "gravitational_potential")) # We don't need to do the same for the pressure field because yt already # has pressure gradient fields. Now, define the "degree of hydrostatic # equilibrium" field. def _hse(field, data): # Remember that g is the negative of the potential gradient gx = -data["gas", "density"] * data["gas", "gravitational_potential_gradient_x"] gy = -data["gas", "density"] * data["gas", "gravitational_potential_gradient_y"] gz = -data["gas", "density"] * data["gas", "gravitational_potential_gradient_z"] hx = data["gas", "pressure_gradient_x"] - gx hy = data["gas", "pressure_gradient_y"] - gy hz = data["gas", "pressure_gradient_z"] - gz h = np.sqrt((hx * hx + hy * hy + hz * hz) / (gx * gx + gy * gy + gz * gz)) return h ds.add_field( ("gas", "HSE"), function=_hse, units="", take_log=False, display_name="Hydrostatic Equilibrium", sampling_type="cell", ) # The gradient operator requires periodic boundaries. This dataset has # open boundary conditions. ds.force_periodicity() # Take a slice through the center of the domain slc = yt.SlicePlot(ds, 2, [("gas", "density"), ("gas", "HSE")], width=(1, "Mpc")) slc.save("hse") yt-project-yt-f043ac8/doc/source/cookbook/image_background_colors.py000066400000000000000000000023131510711153200257400ustar00rootroot00000000000000import yt # This shows how to save ImageArray objects, such as those returned from # volume renderings, to pngs with varying backgrounds. # First we use the simple_volume_rendering.py recipe from above to generate # a standard volume rendering. ds = yt.load("Enzo_64/DD0043/data0043") im, sc = yt.volume_render(ds, ("gas", "density")) im.write_png("original.png", sigma_clip=8.0) # Our image array can now be transformed to include different background # colors. By default, the background color is black. The following # modifications can be used on any image array. # write_png accepts a background keyword argument that defaults to 'black'. # Other choices include: # black (0.,0.,0.,1.) # white (1.,1.,1.,1.) # None (0.,0.,0.,0.) <-- Transparent! # any rgba list/array: [r,g,b,a], bounded by 0..1 # We include the sigma_clip=8 keyword here to bring out more contrast between # the background and foreground, but it is entirely optional. im.write_png("black_bg.png", background="black", sigma_clip=8.0) im.write_png("white_bg.png", background="white", sigma_clip=8.0) im.write_png("green_bg.png", background=[0.0, 1.0, 0.0, 1.0], sigma_clip=8.0) im.write_png("transparent_bg.png", background=None, sigma_clip=8.0) yt-project-yt-f043ac8/doc/source/cookbook/image_resolution.py000066400000000000000000000051211510711153200244430ustar00rootroot00000000000000import numpy as np import yt # Load the dataset. We'll work with a some Gadget data to illustrate all # the different ways in which the effective resolution can vary. Specifically, # we'll use the GadgetDiskGalaxy dataset available at # http://yt-project.org/data/GadgetDiskGalaxy.tar.gz # load the data with a refinement criteria of 2 particle per cell # n.b. -- in yt-4.0, n_ref no longer exists as the data is no longer # deposited only a grid. At present (03/15/2019), there is no way to # handle non-gas data in Gadget snapshots, though that is work in progress if int(yt.__version__[0]) < 4: # increasing n_ref will result in a "lower resolution" (but faster) image, # while decreasing it will go the opposite way ds = yt.load("GadgetDiskGalaxy/snapshot_200.hdf5", n_ref=16) else: ds = yt.load("GadgetDiskGalaxy/snapshot_200.hdf5") # Create projections of the density (max value in each resolution element in the image): prj = yt.ProjectionPlot( ds, "x", ("gas", "density"), method="max", center="max", width=(100, "kpc") ) # nicen up the plot by using a better interpolation: plot = prj.plots[list(prj.plots)[0]] ax = plot.axes img = ax.images[0] img.set_interpolation("bicubic") # nicen up the plot by setting the background color to the minimum of the colorbar prj.set_background_color(("gas", "density")) # vary the buff_size -- the number of resolution elements in the actual visualization # set it to 2000x2000 buff_size = 2000 prj.set_buff_size(buff_size) # set the figure size in inches figure_size = 10 prj.set_figure_size(figure_size) # if the image does not fill the plot (as is default, since the axes and # colorbar contribute as well), then figuring out the proper dpi for a given # buff_size and figure_size is non-trivial -- it requires finding the bbox # for the actual image: bounding_box = ax.get_position() # we're going to scale to the larger of the two sides image_size = figure_size * max([bounding_box.width, bounding_box.height]) # now save with a dpi that's scaled to the buff_size: dpi = np.rint(np.ceil(buff_size / image_size)) prj.save("with_axes_colorbar.png", mpl_kwargs=dict(dpi=dpi)) # in the case where the image fills the entire plot (i.e. if the axes and colorbar # are turned off), it's trivial to figure out the correct dpi from the buff_size and # figure_size (or vice versa): # hide the colorbar: prj.hide_colorbar() # hide the axes, while still keeping the background color correct: prj.hide_axes(draw_frame=True) # save with a dpi that makes sense: dpi = np.rint(np.ceil(buff_size / figure_size)) prj.save("no_axes_colorbar.png", mpl_kwargs=dict(dpi=dpi)) yt-project-yt-f043ac8/doc/source/cookbook/index.rst000066400000000000000000000026231510711153200223710ustar00rootroot00000000000000.. _cookbook: The Cookbook ============ yt provides a great deal of functionality to the user, but sometimes it can be a bit complex. This section of the documentation lays out examples recipes for how to do a variety of tasks. Most of the early, simple code demonstrations are small scripts which you can easily copy and paste into your own code; however, as we move to more complex tasks, the recipes move to iPython notebooks to display intermediate steps. All of these recipes are available for download in a link next to the recipe. Getting the Sample Data ----------------------- All of the data used in the cookbook is freely available `here `_, where you will find links to download individual datasets. .. note:: To contribute your own recipes, please follow the instructions on how to contribute documentation code: :ref:`writing_documentation`. Example Scripts --------------- .. toctree:: :maxdepth: 2 simple_plots calculating_information complex_plots constructing_data_objects .. _example-notebooks: Example Notebooks ----------------- .. toctree:: :maxdepth: 1 notebook_tutorial custom_colorbar_tickmarks yt_gadget_analysis yt_gadget_owls_analysis ../visualizing/TransferFunctionHelper_Tutorial fits_radio_cubes fits_xray_images geographic_xforms_and_projections tipsy_and_yt ../visualizing/Volume_Rendering_Tutorial yt-project-yt-f043ac8/doc/source/cookbook/matplotlib-animation.py000066400000000000000000000012711510711153200252240ustar00rootroot00000000000000from matplotlib import rc_context from matplotlib.animation import FuncAnimation import yt ts = yt.load("GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_*") plot = yt.SlicePlot(ts[0], "z", ("gas", "density")) plot.set_zlim(("gas", "density"), 8e-29, 3e-26) fig = plot.plots["gas", "density"].figure # animate must accept an integer frame number. We use the frame number # to identify which dataset in the time series we want to load def animate(i): ds = ts[i] plot._switch_ds(ds) animation = FuncAnimation(fig, animate, frames=len(ts)) # Override matplotlib's defaults to get a nicer looking font with rc_context({"mathtext.fontset": "stix"}): animation.save("animation.mp4") yt-project-yt-f043ac8/doc/source/cookbook/multi_plot_3x2_FRB.py000066400000000000000000000047771510711153200244730ustar00rootroot00000000000000import numpy as np from matplotlib.colors import LogNorm import yt from yt.visualization.api import get_multi_plot fn = "Enzo_64/RD0006/RedshiftOutput0006" # dataset to load # load data and get center value and center location as maximum density location ds = yt.load(fn) v, c = ds.find_max(("gas", "density")) # set up our Fixed Resolution Buffer parameters: a width, resolution, and center width = (1.0, "unitary") res = [1000, 1000] # get_multi_plot returns a containing figure, a list-of-lists of axes # into which we can place plots, and some axes that we'll put # colorbars. # it accepts: # of x-axis plots, # of y-axis plots, and how the # colorbars are oriented (this also determines where they go: below # in the case of 'horizontal', on the right in the case of # 'vertical'), bw is the base-width in inches (4 is about right for # most cases) orient = "horizontal" fig, axes, colorbars = get_multi_plot(2, 3, colorbar=orient, bw=6) # Now we follow the method of "multi_plot.py" but we're going to iterate # over the columns, which will become axes of slicing. plots = [] for ax in range(3): sli = ds.slice(ax, c[ax]) frb = sli.to_frb(width, res) den_axis = axes[ax][0] temp_axis = axes[ax][1] # here, we turn off the axes labels and ticks, but you could # customize further. for ax in (den_axis, temp_axis): ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) # converting our fixed resolution buffers to NDarray so matplotlib can # render them dens = np.array(frb["gas", "density"]) temp = np.array(frb["gas", "temperature"]) plots.append(den_axis.imshow(dens, norm=LogNorm())) plots[-1].set_clim((5e-32, 1e-29)) plots[-1].set_cmap("bds_highcontrast") plots.append(temp_axis.imshow(temp, norm=LogNorm())) plots[-1].set_clim((1e3, 1e8)) plots[-1].set_cmap("hot") # Each 'cax' is a colorbar-container, into which we'll put a colorbar. # the zip command creates triples from each element of the three lists # . Note that it cuts off after the shortest iterator is exhausted, # in this case, titles. titles = [ r"$\mathrm{density}\ (\mathrm{g\ cm^{-3}})$", r"$\mathrm{temperature}\ (\mathrm{K})$", ] for p, cax, t in zip(plots, colorbars, titles): # Now we make a colorbar, using the 'image' we stored in plots # above. note this is what is *returned* by the imshow method of # the plots. cbar = fig.colorbar(p, cax=cax, orientation=orient) cbar.set_label(t) # And now we're done! fig.savefig(f"{ds}_3x2.png") yt-project-yt-f043ac8/doc/source/cookbook/multi_plot_slice_and_proj.py000066400000000000000000000056251510711153200263320ustar00rootroot00000000000000import numpy as np from matplotlib.colors import LogNorm import yt from yt.visualization.base_plot_types import get_multi_plot fn = "GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150" # dataset to load orient = "horizontal" ds = yt.load(fn) # load data # There's a lot in here: # From this we get a containing figure, a list-of-lists of axes into which we # can place plots, and some axes that we'll put colorbars. # We feed it: # Number of plots on the x-axis, number of plots on the y-axis, and how we # want our colorbars oriented. (This governs where they will go, too. # bw is the base-width in inches, but 4 is about right for most cases. fig, axes, colorbars = get_multi_plot(3, 2, colorbar=orient, bw=4) slc = yt.SlicePlot( ds, "z", fields=[("gas", "density"), ("gas", "temperature"), ("gas", "velocity_magnitude")], ) proj = yt.ProjectionPlot(ds, "z", ("gas", "density"), weight_field=("gas", "density")) slc_frb = slc.data_source.to_frb((1.0, "Mpc"), 512) proj_frb = proj.data_source.to_frb((1.0, "Mpc"), 512) dens_axes = [axes[0][0], axes[1][0]] temp_axes = [axes[0][1], axes[1][1]] vels_axes = [axes[0][2], axes[1][2]] for dax, tax, vax in zip(dens_axes, temp_axes, vels_axes): dax.xaxis.set_visible(False) dax.yaxis.set_visible(False) tax.xaxis.set_visible(False) tax.yaxis.set_visible(False) vax.xaxis.set_visible(False) vax.yaxis.set_visible(False) # Converting our Fixed Resolution Buffers to numpy arrays so that matplotlib # can render them slc_dens = np.array(slc_frb["gas", "density"]) proj_dens = np.array(proj_frb["gas", "density"]) slc_temp = np.array(slc_frb["gas", "temperature"]) proj_temp = np.array(proj_frb["gas", "temperature"]) slc_vel = np.array(slc_frb["gas", "velocity_magnitude"]) proj_vel = np.array(proj_frb["gas", "velocity_magnitude"]) plots = [ dens_axes[0].imshow(slc_dens, origin="lower", norm=LogNorm()), dens_axes[1].imshow(proj_dens, origin="lower", norm=LogNorm()), temp_axes[0].imshow(slc_temp, origin="lower"), temp_axes[1].imshow(proj_temp, origin="lower"), vels_axes[0].imshow(slc_vel, origin="lower", norm=LogNorm()), vels_axes[1].imshow(proj_vel, origin="lower", norm=LogNorm()), ] plots[0].set_clim((1.0e-27, 1.0e-25)) plots[0].set_cmap("bds_highcontrast") plots[1].set_clim((1.0e-27, 1.0e-25)) plots[1].set_cmap("bds_highcontrast") plots[2].set_clim((1.0e7, 1.0e8)) plots[2].set_cmap("hot") plots[3].set_clim((1.0e7, 1.0e8)) plots[3].set_cmap("hot") plots[4].set_clim((1e6, 1e8)) plots[4].set_cmap("gist_rainbow") plots[5].set_clim((1e6, 1e8)) plots[5].set_cmap("gist_rainbow") titles = [ r"$\mathrm{Density}\ (\mathrm{g\ cm^{-3}})$", r"$\mathrm{Temperature}\ (\mathrm{K})$", r"$\mathrm{Velocity Magnitude}\ (\mathrm{cm\ s^{-1}})$", ] for p, cax, t in zip(plots[0:6:2], colorbars, titles): cbar = fig.colorbar(p, cax=cax, orientation=orient) cbar.set_label(t) # And now we're done! fig.savefig(f"{ds}_3x2") yt-project-yt-f043ac8/doc/source/cookbook/multi_width_image.py000066400000000000000000000016261510711153200245770ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a slice plot for the dataset. With no additional arguments, # the width will be the size of the domain and the center will be the # center of the simulation box slc = yt.SlicePlot(ds, "z", ("gas", "density")) # Create a list of a couple of widths and units. # (N.B. Mpc (megaparsec) != mpc (milliparsec) widths = [(1, "Mpc"), (15, "kpc")] # Loop through the list of widths and units. for width, unit in widths: # Set the width. slc.set_width(width, unit) # Write out the image with a unique name. slc.save("%s_%010d_%s" % (ds, width, unit)) zoomFactors = [2, 4, 5] # recreate the original slice slc = yt.SlicePlot(ds, "z", ("gas", "density")) for zoomFactor in zoomFactors: # zoom in slc.zoom(zoomFactor) # Write out the image with a unique name. slc.save("%s_%i" % (ds, zoomFactor)) yt-project-yt-f043ac8/doc/source/cookbook/multiplot_2x2.py000066400000000000000000000027611510711153200236310ustar00rootroot00000000000000import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import AxesGrid import yt fn = "IsolatedGalaxy/galaxy0030/galaxy0030" ds = yt.load(fn) # load data fig = plt.figure() # See http://matplotlib.org/mpl_toolkits/axes_grid/api/axes_grid_api.html # These choices of keyword arguments produce a four panel plot that includes # four narrow colorbars, one for each plot. Axes labels are only drawn on the # bottom left hand plot to avoid repeating information and make the plot less # cluttered. grid = AxesGrid( fig, (0.075, 0.075, 0.85, 0.85), nrows_ncols=(2, 2), axes_pad=1.0, label_mode="1", share_all=True, cbar_location="right", cbar_mode="each", cbar_size="3%", cbar_pad="0%", ) fields = [ ("gas", "density"), ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_magnitude"), ] # Create the plot. Since SlicePlot accepts a list of fields, we need only # do this once. p = yt.SlicePlot(ds, "z", fields) # Velocity is going to be both positive and negative, so let's make these # slices use a linear colorbar scale p.set_log(("gas", "velocity_x"), False) p.set_log(("gas", "velocity_y"), False) p.zoom(2) # For each plotted field, force the SlicePlot to redraw itself onto the AxesGrid # axes. for i, field in enumerate(fields): plot = p.plots[field] plot.figure = fig plot.axes = grid[i].axes plot.cax = grid.cbar_axes[i] # Finally, redraw the plot on the AxesGrid axes. p.render() plt.savefig("multiplot_2x2.png") yt-project-yt-f043ac8/doc/source/cookbook/multiplot_2x2_coordaxes_slice.py000066400000000000000000000034041510711153200270520ustar00rootroot00000000000000import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import AxesGrid import yt fn = "IsolatedGalaxy/galaxy0030/galaxy0030" ds = yt.load(fn) # load data fig = plt.figure() # See http://matplotlib.org/mpl_toolkits/axes_grid/api/axes_grid_api.html # These choices of keyword arguments produce two colorbars, both drawn on the # right hand side. This means there are only two colorbar axes, one for Density # and another for temperature. In addition, axes labels will be drawn for all # plots. grid = AxesGrid( fig, (0.075, 0.075, 0.85, 0.85), nrows_ncols=(2, 2), axes_pad=1.0, label_mode="all", share_all=True, cbar_location="right", cbar_mode="edge", cbar_size="5%", cbar_pad="0%", ) cuts = ["x", "y", "z", "z"] fields = [ ("gas", "density"), ("gas", "density"), ("gas", "density"), ("gas", "temperature"), ] for i, (direction, field) in enumerate(zip(cuts, fields)): # Load the data and create a single plot p = yt.SlicePlot(ds, direction, field) p.zoom(40) # This forces the ProjectionPlot to redraw itself on the AxesGrid axes. plot = p.plots[field] plot.figure = fig plot.axes = grid[i].axes # Since there are only two colorbar axes, we need to make sure we don't try # to set the temperature colorbar to cbar_axes[4], which would if we used i # to index cbar_axes, yielding a plot without a temperature colorbar. # This unnecessarily redraws the Density colorbar three times, but that has # no effect on the final plot. if field == ("gas", "density"): plot.cax = grid.cbar_axes[0] elif field == ("gas", "temperature"): plot.cax = grid.cbar_axes[1] # Finally, redraw the plot. p.render() plt.savefig("multiplot_2x2_coordaxes_slice.png") yt-project-yt-f043ac8/doc/source/cookbook/multiplot_2x2_time_series.py000066400000000000000000000025631510711153200262210ustar00rootroot00000000000000import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import AxesGrid import yt fns = [ "Enzo_64/DD0005/data0005", "Enzo_64/DD0015/data0015", "Enzo_64/DD0025/data0025", "Enzo_64/DD0035/data0035", ] fig = plt.figure() # See http://matplotlib.org/mpl_toolkits/axes_grid/api/axes_grid_api.html # These choices of keyword arguments produce a four panel plot with a single # shared narrow colorbar on the right hand side of the multipanel plot. Axes # labels are drawn for all plots since we're slicing along different directions # for each plot. grid = AxesGrid( fig, (0.075, 0.075, 0.85, 0.85), nrows_ncols=(2, 2), axes_pad=0.05, label_mode="L", share_all=True, cbar_location="right", cbar_mode="single", cbar_size="3%", cbar_pad="0%", ) for i, fn in enumerate(fns): # Load the data and create a single plot ds = yt.load(fn) # load data p = yt.ProjectionPlot(ds, "z", ("gas", "density"), width=(55, "Mpccm")) # Ensure the colorbar limits match for all plots p.set_zlim(("gas", "density"), 1e-4, 1e-2) # This forces the ProjectionPlot to redraw itself on the AxesGrid axes. plot = p.plots["gas", "density"] plot.figure = fig plot.axes = grid[i].axes plot.cax = grid.cbar_axes[i] # Finally, this actually redraws the plot. p.render() plt.savefig("multiplot_2x2_time_series.png") yt-project-yt-f043ac8/doc/source/cookbook/multiplot_export_to_mpl.py000066400000000000000000000007751510711153200261140ustar00rootroot00000000000000import yt ds = yt.load_sample("IsolatedGalaxy") fields = [ ("gas", "density"), ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_magnitude"), ] p = yt.SlicePlot(ds, "z", fields) p.set_log(("gas", "velocity_x"), False) p.set_log(("gas", "velocity_y"), False) # this returns a matplotlib figure with an ImageGrid and the slices # added to the grid of axes (in this case, 2x2) fig = p.export_to_mpl_figure((2, 2)) fig.tight_layout() fig.savefig("multiplot_export_to_mpl.png") yt-project-yt-f043ac8/doc/source/cookbook/multiplot_phaseplot.py000066400000000000000000000026521510711153200252140ustar00rootroot00000000000000import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import AxesGrid import yt fig = plt.figure() # See http://matplotlib.org/mpl_toolkits/axes_grid/api/axes_grid_api.html grid = AxesGrid( fig, (0.085, 0.085, 0.83, 0.83), nrows_ncols=(1, 2), axes_pad=0.05, label_mode="L", share_all=True, cbar_location="right", cbar_mode="single", cbar_size="3%", cbar_pad="0%", aspect=False, ) for i, SnapNum in enumerate([10, 40]): # Load the data and create a single plot ds = yt.load("enzo_tiny_cosmology/DD00%2d/DD00%2d" % (SnapNum, SnapNum)) ad = ds.all_data() p = yt.PhasePlot( ad, ("gas", "density"), ("gas", "temperature"), [ ("gas", "mass"), ], weight_field=None, ) # Ensure the axes and colorbar limits match for all plots p.set_xlim(1.0e-32, 8.0e-26) p.set_ylim(1.0e1, 2.0e7) p.set_zlim(("gas", "mass"), 1e42, 1e46) # This forces the ProjectionPlot to redraw itself on the AxesGrid axes. plot = p.plots["gas", "mass"] plot.figure = fig plot.axes = grid[i].axes if i == 0: plot.cax = grid.cbar_axes[i] # Actually redraws the plot. p.render() # Modify the axes properties **after** p.render() so that they # are not overwritten. plot.axes.xaxis.set_minor_locator(plt.LogLocator(base=10.0, subs=[2.0, 5.0, 8.0])) plt.savefig("multiplot_phaseplot.png") yt-project-yt-f043ac8/doc/source/cookbook/notebook_tutorial.rst000066400000000000000000000025401510711153200250230ustar00rootroot00000000000000.. _notebook-tutorial: Notebook Tutorial ----------------- The IPython notebook is a powerful system for literate coding - a style of writing code that embeds input, output, and explanatory text into one document. yt has deep integration with the IPython notebook, explained in-depth in the other example notebooks and the rest of the yt documentation. This page is here to give a brief introduction to the notebook itself. To start the notebook, enter the following command at the bash command line: .. code-block:: bash $ ipython notebook Depending on your default web browser and system setup this will open a web browser and direct you to the notebook dashboard. If it does not, you might need to connect to the notebook manually. See the `IPython documentation `_ for more details. For the notebook tutorial, we rely on example notebooks that are part of the IPython documentation. We link to static nbviewer versions of the 'evaluated' versions of these example notebooks. If you would like to run them locally on your own computer, simply download the notebook by clicking the 'Download Notebook' link in the top right corner of each page. 1. `IPython Notebook Tutorials `_ yt-project-yt-f043ac8/doc/source/cookbook/offaxis_projection.py000066400000000000000000000024151510711153200247740ustar00rootroot00000000000000import numpy as np import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Choose a center for the render. c = [0.5, 0.5, 0.5] # Our image plane will be normal to some vector. For things like collapsing # objects, you could set it the way you would a cutting plane -- but for this # dataset, we'll just choose an off-axis value at random. This gets normalized # automatically. L = [1.0, 0.0, 0.0] # Our "width" is the width of the image plane as well as the depth. # The first element is the left to right width, the second is the # top-bottom width, and the last element is the back-to-front width # (all in code units) W = [0.04, 0.04, 0.4] # The number of pixels along one side of the image. # The final image will have Npixel^2 pixels. Npixels = 512 # Create the off axis projection. # Setting no_ghost to False speeds up the process, but makes a # slightly lower quality image. image = yt.off_axis_projection(ds, c, L, W, Npixels, ("gas", "density"), no_ghost=False) # Write out the final image and give it a name # relating to what our dataset is called. # We save the log of the values so that the colors do not span # many orders of magnitude. Try it without and see what happens. yt.write_image(np.log10(image), f"{ds}_offaxis_projection.png") yt-project-yt-f043ac8/doc/source/cookbook/offaxis_projection_colorbar.py000066400000000000000000000032401510711153200266540ustar00rootroot00000000000000import yt fn = "IsolatedGalaxy/galaxy0030/galaxy0030" # dataset to load ds = yt.load(fn) # load data # Now we need a center of our volume to render. Here we'll just use # 0.5,0.5,0.5, because volume renderings are not periodic. c = [0.5, 0.5, 0.5] # Our image plane will be normal to some vector. For things like collapsing # objects, you could set it the way you would a cutting plane -- but for this # dataset, we'll just choose an off-axis value at random. This gets normalized # automatically. L = [0.5, 0.4, 0.7] # Our "width" is the width of the image plane as well as the depth. # The first element is the left to right width, the second is the # top-bottom width, and the last element is the back-to-front width # (all in code units) W = [0.04, 0.04, 0.4] # The number of pixels along one side of the image. # The final image will have Npixel^2 pixels. Npixels = 512 # Now we call the off_axis_projection function, which handles the rest. # Note that we set no_ghost equal to False, so that we *do* include ghost # zones in our data. This takes longer to calculate, but the results look # much cleaner than when you ignore the ghost zones. # Also note that we set the field which we want to project as "density", but # really we could use any arbitrary field like "temperature", "metallicity" # or whatever. image = yt.off_axis_projection(ds, c, L, W, Npixels, ("gas", "density"), no_ghost=False) # Image is now an NxN array representing the intensities of the various pixels. # And now, we call our direct image saver. We save the log of the result. yt.write_projection( image, "offaxis_projection_colorbar.png", colorbar_label="Column Density (cm$^{-2}$)", ) yt-project-yt-f043ac8/doc/source/cookbook/opaque_rendering.py000066400000000000000000000040341510711153200244270ustar00rootroot00000000000000import numpy as np import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # We start by building a default volume rendering scene im, sc = yt.volume_render(ds, field=("gas", "density"), fname="v0.png", sigma_clip=6.0) sc.camera.set_width(ds.arr(0.1, "code_length")) tf = sc.get_source().transfer_function tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=np.logspace(-3, 0, 4), colormap="RdBu_r" ) sc.save("v1.png", sigma_clip=6.0) # In this case, the default alphas used (np.logspace(-3,0,Nbins)) does not # accentuate the outer regions of the galaxy. Let's start by bringing up the # alpha values for each contour to go between 0.1 and 1.0 tf = sc.get_source().transfer_function tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=np.logspace(0, 0, 4), colormap="RdBu_r" ) sc.save("v2.png", sigma_clip=6.0) # Now let's set the grey_opacity to True. This should make the inner portions # start to be obscured tf.grey_opacity = True sc.save("v3.png", sigma_clip=6.0) # That looks pretty good, but let's start bumping up the opacity. tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=10.0 * np.ones(4, dtype="float64"), colormap="RdBu_r", ) sc.save("v4.png", sigma_clip=6.0) # Let's bump up again to see if we can obscure the inner contour. tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=30.0 * np.ones(4, dtype="float64"), colormap="RdBu_r", ) sc.save("v5.png", sigma_clip=6.0) # Now we are losing sight of everything. Let's see if we can obscure the next # layer tf.clear() tf.add_layers( 4, 0.01, col_bounds=[-27.5, -25.5], alpha=100.0 * np.ones(4, dtype="float64"), colormap="RdBu_r", ) sc.save("v6.png", sigma_clip=6.0) # That is very opaque! Now lets go back and see what it would look like with # grey_opacity = False tf.grey_opacity = False sc.save("v7.png", sigma_clip=6.0) # That looks pretty different, but the main thing is that you can see that the # inner contours are somewhat visible again. yt-project-yt-f043ac8/doc/source/cookbook/overplot_grids.py000066400000000000000000000007071510711153200241450ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("Enzo_64/DD0043/data0043") # Make a density projection. p = yt.ProjectionPlot(ds, "y", ("gas", "density")) # Modify the projection # The argument specifies the region along the line of sight # for which particles will be gathered. # 1.0 signifies the entire domain in the line of sight. p.annotate_grids() # Save the image. # Optionally, give a string as an argument # to name files with a keyword. p.save() yt-project-yt-f043ac8/doc/source/cookbook/overplot_particles.py000066400000000000000000000012031510711153200250130ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("Enzo_64/DD0043/data0043") # Make a density projection centered on the 'm'aximum density location # with a width of 10 Mpc.. p = yt.ProjectionPlot(ds, "y", ("gas", "density"), center="m", width=(10, "Mpc")) # Modify the projection # The argument specifies the region along the line of sight # for which particles will be gathered. # 1.0 signifies the entire domain in the line of sight # p.annotate_particles(1.0) # but in this case we only go 10 Mpc in depth p.annotate_particles((10, "Mpc")) # Save the image. # Optionally, give a string as an argument # to name files with a keyword. p.save() yt-project-yt-f043ac8/doc/source/cookbook/particle_filter.py000066400000000000000000000044761510711153200242620ustar00rootroot00000000000000import numpy as np import yt from yt.data_objects.particle_filters import add_particle_filter # Define filter functions for our particle filters based on stellar age. # In this dataset particles in the initial conditions are given creation # times arbitrarily far into the future, so stars with negative ages belong # in the old stars filter. def stars_10Myr(pfilter, data): age = data.ds.current_time - data["Stars", "creation_time"] filter = np.logical_and(age >= 0, age.in_units("Myr") < 10) return filter def stars_100Myr(pfilter, data): age = (data.ds.current_time - data["Stars", "creation_time"]).in_units("Myr") filter = np.logical_and(age >= 10, age < 100) return filter def stars_old(pfilter, data): age = data.ds.current_time - data["Stars", "creation_time"] filter = np.logical_or(age < 0, age.in_units("Myr") >= 100) return filter # Create the particle filters add_particle_filter( "stars_young", function=stars_10Myr, filtered_type="Stars", requires=["creation_time"], ) add_particle_filter( "stars_medium", function=stars_100Myr, filtered_type="Stars", requires=["creation_time"], ) add_particle_filter( "stars_old", function=stars_old, filtered_type="Stars", requires=["creation_time"] ) # Load a dataset and apply the particle filters filename = "TipsyGalaxy/galaxy.00300" ds = yt.load(filename) ds.add_particle_filter("stars_young") ds.add_particle_filter("stars_medium") ds.add_particle_filter("stars_old") # What are the total masses of different ages of star in the whole simulation # volume? ad = ds.all_data() mass_young = ad["stars_young", "particle_mass"].in_units("Msun").sum() mass_medium = ad["stars_medium", "particle_mass"].in_units("Msun").sum() mass_old = ad["stars_old", "particle_mass"].in_units("Msun").sum() print(f"Mass of young stars = {mass_young:g}") print(f"Mass of medium stars = {mass_medium:g}") print(f"Mass of old stars = {mass_old:g}") # Generate 4 projections: gas density, young stars, medium stars, old stars fields = [ ("stars_young", "particle_mass"), ("stars_medium", "particle_mass"), ("stars_old", "particle_mass"), ] prj1 = yt.ProjectionPlot(ds, "z", ("gas", "density"), center="max", width=(100, "kpc")) prj1.save() prj2 = yt.ParticleProjectionPlot(ds, "z", fields, center="max", width=(100, "kpc")) prj2.save() yt-project-yt-f043ac8/doc/source/cookbook/particle_filter_sfr.py000066400000000000000000000020571510711153200251250ustar00rootroot00000000000000import numpy as np from matplotlib import pyplot as plt import yt from yt.data_objects.particle_filters import add_particle_filter def formed_star(pfilter, data): filter = data["all", "creation_time"] > 0 return filter add_particle_filter( "formed_star", function=formed_star, filtered_type="all", requires=["creation_time"] ) filename = "IsolatedGalaxy/galaxy0030/galaxy0030" ds = yt.load(filename) ds.add_particle_filter("formed_star") ad = ds.all_data() masses = ad["formed_star", "particle_mass"].in_units("Msun") formation_time = ad["formed_star", "creation_time"].in_units("yr") time_range = [0, 5e8] # years n_bins = 1000 hist, bins = np.histogram( formation_time, bins=n_bins, range=time_range, ) inds = np.digitize(formation_time, bins=bins) time = (bins[:-1] + bins[1:]) / 2 sfr = np.array( [masses[inds == j + 1].sum() / (bins[j + 1] - bins[j]) for j in range(len(time))] ) sfr[sfr == 0] = np.nan plt.plot(time / 1e6, sfr) plt.xlabel("Time [Myr]") plt.ylabel(r"SFR [M$_\odot$ yr$^{-1}$]") plt.savefig("filter_sfr.png") yt-project-yt-f043ac8/doc/source/cookbook/particle_one_color_plot.py000066400000000000000000000004271510711153200260020ustar00rootroot00000000000000import yt # load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # create our plot p = yt.ParticlePlot( ds, ("all", "particle_position_x"), ("all", "particle_position_y"), color="b" ) # zoom in a little bit p.set_width(500, "kpc") # save result p.save() yt-project-yt-f043ac8/doc/source/cookbook/particle_xvz_plot.py000066400000000000000000000006661510711153200246570ustar00rootroot00000000000000import yt # load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # create our plot p = yt.ParticlePlot( ds, ("all", "particle_position_x"), ("all", "particle_velocity_z"), [("all", "particle_mass")], ) # pick some appropriate units p.set_unit(("all", "particle_position_x"), "Mpc") p.set_unit(("all", "particle_velocity_z"), "km/s") p.set_unit(("all", "particle_mass"), "Msun") # save result p.save() yt-project-yt-f043ac8/doc/source/cookbook/particle_xy_plot.py000066400000000000000000000005741510711153200244660ustar00rootroot00000000000000import yt # load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # create our plot p = yt.ParticlePlot( ds, ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_mass"), width=(0.5, 0.5), ) # pick some appropriate units p.set_axes_unit("kpc") p.set_unit(("all", "particle_mass"), "Msun") # save result p.save() yt-project-yt-f043ac8/doc/source/cookbook/power_spectrum_example.py000066400000000000000000000057041510711153200256760ustar00rootroot00000000000000import matplotlib.pyplot as plt import numpy as np import yt """ Make a turbulent KE power spectrum. Since we are stratified, we use a rho**(1/3) scaling to the velocity to get something that would look Kolmogorov (if the turbulence were fully developed). Ultimately, we aim to compute: 1 ^ ^* E(k) = integral - V(k) . V(k) dS 2 n ^ where V = rho U is the density-weighted velocity field, and V is the FFT of V. (Note: sometimes we normalize by 1/volume to get a spectral energy density spectrum). """ def doit(ds): # a FFT operates on uniformly gridded data. We'll use the yt # covering grid for this. max_level = ds.index.max_level ref = int(np.prod(ds.ref_factors[0:max_level])) low = ds.domain_left_edge dims = ds.domain_dimensions * ref nx, ny, nz = dims nindex_rho = 1.0 / 3.0 Kk = np.zeros((nx // 2 + 1, ny // 2 + 1, nz // 2 + 1)) for vel in [("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z")]: Kk += 0.5 * fft_comp( ds, ("gas", "density"), vel, nindex_rho, max_level, low, dims ) # wavenumbers L = (ds.domain_right_edge - ds.domain_left_edge).d kx = np.fft.rfftfreq(nx) * nx / L[0] ky = np.fft.rfftfreq(ny) * ny / L[1] kz = np.fft.rfftfreq(nz) * nz / L[2] # physical limits to the wavenumbers kmin = np.min(1.0 / L) kmax = np.min(0.5 * dims / L) kbins = np.arange(kmin, kmax, kmin) N = len(kbins) # bin the Fourier KE into radial kbins kx3d, ky3d, kz3d = np.meshgrid(kx, ky, kz, indexing="ij") k = np.sqrt(kx3d**2 + ky3d**2 + kz3d**2) whichbin = np.digitize(k.flat, kbins) ncount = np.bincount(whichbin) E_spectrum = np.zeros(len(ncount) - 1) for n in range(1, len(ncount)): E_spectrum[n - 1] = np.sum(Kk.flat[whichbin == n]) k = 0.5 * (kbins[0 : N - 1] + kbins[1:N]) E_spectrum = E_spectrum[1:N] index = np.argmax(E_spectrum) kmax = k[index] Emax = E_spectrum[index] plt.loglog(k, E_spectrum) plt.loglog(k, Emax * (k / kmax) ** (-5.0 / 3.0), ls=":", color="0.5") plt.xlabel(r"$k$") plt.ylabel(r"$E(k)dk$") plt.savefig("spectrum.png") def fft_comp(ds, irho, iu, nindex_rho, level, low, delta): cube = ds.covering_grid(level, left_edge=low, dims=delta, fields=[irho, iu]) rho = cube[irho].d u = cube[iu].d nx, ny, nz = rho.shape # do the FFTs -- note that since our data is real, there will be # too much information here. fftn puts the positive freq terms in # the first half of the axes -- that's what we keep. Our # normalization has an '8' to account for this clipping to one # octant. ru = np.fft.fftn(rho**nindex_rho * u)[ 0 : nx // 2 + 1, 0 : ny // 2 + 1, 0 : nz // 2 + 1 ] ru = 8.0 * ru / (nx * ny * nz) return np.abs(ru) ** 2 ds = yt.load("maestro_xrb_lores_23437") doit(ds) yt-project-yt-f043ac8/doc/source/cookbook/profile_with_standard_deviation.py000066400000000000000000000021121510711153200275100ustar00rootroot00000000000000import matplotlib.pyplot as plt import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a sphere of radius 1 Mpc centered on the max density location. sp = ds.sphere("max", (1, "Mpc")) # Calculate and store the bulk velocity for the sphere. bulk_velocity = sp.quantities.bulk_velocity() sp.set_field_parameter("bulk_velocity", bulk_velocity) # Create a 1D profile object for profiles over radius # and add a velocity profile. prof = yt.create_profile( sp, "radius", ("gas", "velocity_magnitude"), units={"radius": "kpc"}, extrema={"radius": ((0.1, "kpc"), (1000.0, "kpc"))}, weight_field=("gas", "mass"), ) # Create arrays to plot. radius = prof.x mean = prof["gas", "velocity_magnitude"] std = prof.standard_deviation["gas", "velocity_magnitude"] # Plot the average velocity magnitude. plt.loglog(radius, mean, label="Mean") # Plot the standard deviation of the velocity magnitude. plt.loglog(radius, std, label="Standard Deviation") plt.xlabel("r [kpc]") plt.ylabel("v [cm/s]") plt.legend() plt.savefig("velocity_profiles.png") yt-project-yt-f043ac8/doc/source/cookbook/rad_velocity.py000066400000000000000000000024141510711153200235640ustar00rootroot00000000000000import matplotlib.pyplot as plt import yt ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") # Get the first sphere sp0 = ds.sphere(ds.domain_center, (500.0, "kpc")) # Compute the bulk velocity from the cells in this sphere bulk_vel = sp0.quantities.bulk_velocity() # Get the second sphere sp1 = ds.sphere(ds.domain_center, (500.0, "kpc")) # Set the bulk velocity field parameter sp1.set_field_parameter("bulk_velocity", bulk_vel) # Radial profile without correction rp0 = yt.create_profile( sp0, ("index", "radius"), ("gas", "radial_velocity"), units={("index", "radius"): "kpc"}, logs={("index", "radius"): False}, ) # Radial profile with correction for bulk velocity rp1 = yt.create_profile( sp1, ("index", "radius"), ("gas", "radial_velocity"), units={("index", "radius"): "kpc"}, logs={("index", "radius"): False}, ) # Make a plot using matplotlib fig = plt.figure() ax = fig.add_subplot(111) ax.plot( rp0.x.value, rp0["gas", "radial_velocity"].in_units("km/s").value, rp1.x.value, rp1["gas", "radial_velocity"].in_units("km/s").value, ) ax.set_xlabel(r"$\mathrm{r\ (kpc)}$") ax.set_ylabel(r"$\mathrm{v_r\ (km/s)}$") ax.legend(["Without Correction", "With Correction"]) fig.savefig(f"{ds}_profiles.png") yt-project-yt-f043ac8/doc/source/cookbook/radial_profile_styles.py000066400000000000000000000023661510711153200254650ustar00rootroot00000000000000import matplotlib.pyplot as plt import yt ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") # Get a sphere object sp = ds.sphere(ds.domain_center, (500.0, "kpc")) # Bin up the data from the sphere into a radial profile rp = yt.create_profile( sp, "radius", [("gas", "density"), ("gas", "temperature")], units={"radius": "kpc"}, logs={"radius": False}, ) # Make plots using matplotlib fig = plt.figure() ax = fig.add_subplot(111) # Plot the density as a log-log plot using the default settings dens_plot = ax.loglog(rp.x.value, rp["gas", "density"].value) # Here we set the labels of the plot axes ax.set_xlabel(r"$\mathrm{r\ (kpc)}$") ax.set_ylabel(r"$\mathrm{\rho\ (g\ cm^{-3})}$") # Save the default plot fig.savefig("density_profile_default.png" % ds) # The "dens_plot" object is a list of plot objects. In our case we only have one, # so we index the list by '0' to get it. # Plot using dashed red lines dens_plot[0].set_linestyle("--") dens_plot[0].set_color("red") fig.savefig("density_profile_dashed_red.png") # Increase the line width and add points in the shape of x's dens_plot[0].set_linewidth(5) dens_plot[0].set_marker("x") dens_plot[0].set_markersize(10) fig.savefig("density_profile_thick_with_xs.png") yt-project-yt-f043ac8/doc/source/cookbook/render_two_fields.py000066400000000000000000000013131510711153200245730ustar00rootroot00000000000000import yt from yt.visualization.volume_rendering.api import Scene, create_volume_source filePath = "Sedov_3d/sedov_hdf5_chk_0003" ds = yt.load(filePath) ds.force_periodicity() sc = Scene() # set up camera cam = sc.add_camera(ds, lens_type="perspective") cam.resolution = [400, 400] cam.position = ds.arr([1, 1, 1], "cm") cam.switch_orientation() # add rendering of density field dens = create_volume_source(ds, field=("flash", "dens")) dens.use_ghost_zones = True sc.add_source(dens) sc.save("density.png", sigma_clip=6) # add rendering of x-velocity field vel = create_volume_source(ds, field=("flash", "velx")) vel.use_ghost_zones = True sc.add_source(vel) sc.save("density_any_velocity.png", sigma_clip=6) yt-project-yt-f043ac8/doc/source/cookbook/render_two_fields_tf.py000066400000000000000000000020651510711153200252710ustar00rootroot00000000000000import numpy as np import yt from yt.visualization.volume_rendering.api import Scene, create_volume_source ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # create a scene and add volume sources to it sc = Scene() # Add density field = "density" vol = create_volume_source(ds, field=field) vol.use_ghost_zones = True tf = yt.ColorTransferFunction([-28, -25]) tf.clear() tf.add_layers(4, 0.02, alpha=np.logspace(-3, -1, 4), colormap="winter") vol.set_transfer_function(tf) sc.add_source(vol) # Add temperature field = "temperature" vol2 = create_volume_source(ds, field=field) vol2.use_ghost_zones = True tf = yt.ColorTransferFunction([4.5, 7.5]) tf.clear() tf.add_layers(4, 0.02, alpha=np.logspace(-0.2, 0, 4), colormap="autumn") vol2.set_transfer_function(tf) sc.add_source(vol2) # setup the camera cam = sc.add_camera(ds, lens_type="perspective") cam.resolution = (1600, 900) cam.zoom(20.0) # Render the image. sc.render() sc.save_annotated( "render_two_fields_tf.png", sigma_clip=6.0, tf_rect=[0.88, 0.15, 0.03, 0.8], render=False, ) yt-project-yt-f043ac8/doc/source/cookbook/rendering_with_box_and_grids.py000066400000000000000000000011101510711153200267620ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, ("gas", "density")) # You may need to adjust the alpha values to get a rendering with good contrast # For annotate_domain, the fourth color value is alpha. # Draw the domain boundary sc.annotate_domain(ds, color=[1, 1, 1, 0.01]) sc.save(f"{ds}_vr_domain.png", sigma_clip=4) # Draw the grid boundaries sc.annotate_grids(ds, alpha=0.01) sc.save(f"{ds}_vr_grids.png", sigma_clip=4) # Draw a coordinate axes triad sc.annotate_axes(alpha=0.01) sc.save(f"{ds}_vr_coords.png", sigma_clip=4) yt-project-yt-f043ac8/doc/source/cookbook/show_hide_axes_colorbar.py000066400000000000000000000004771510711153200257630ustar00rootroot00000000000000import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "x", ("gas", "density")) slc.save("default_sliceplot.png") slc.hide_axes() slc.save("no_axes_sliceplot.png") slc.hide_colorbar() slc.save("no_axes_no_colorbar_sliceplot.png") slc.show_axes() slc.save("no_colorbar_sliceplot.png") yt-project-yt-f043ac8/doc/source/cookbook/sigma_clip.py000066400000000000000000000011511510711153200232040ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("enzo_tiny_cosmology/RD0009/RD0009") # Create a volume rendering, which will determine data bounds, use the first # acceptable field in the field_list, and set up a default transfer function. # Render and save output images with different levels of sigma clipping. # Sigma clipping removes the highest intensity pixels in a volume render, # which affects the overall contrast of the image. sc = yt.create_scene(ds, field=("gas", "density")) sc.save("clip_0.png") sc.save("clip_2.png", sigma_clip=2) sc.save("clip_4.png", sigma_clip=4) sc.save("clip_6.png", sigma_clip=6) yt-project-yt-f043ac8/doc/source/cookbook/simple_1d_line_plot.py000066400000000000000000000006641510711153200250270ustar00rootroot00000000000000import yt # Load the dataset ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1) # Create a line plot of the variables 'u' and 'v' with 1000 sampling points evenly # spaced between the coordinates (0, 0, 0) and (0, 1, 0) plot = yt.LinePlot( ds, [("all", "v"), ("all", "u")], (0.0, 0.0, 0.0), (0.0, 1.0, 0.0), 1000 ) # Add a legend plot.annotate_legend(("all", "v")) # Save the line plot plot.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_contour_in_slice.py000066400000000000000000000016511510711153200260110ustar00rootroot00000000000000import yt # Load the data file. ds = yt.load("Sedov_3d/sedov_hdf5_chk_0002") # Make a traditional slice plot. sp = yt.SlicePlot(ds, "x", ("gas", "density")) # Overlay the slice plot with thick red contours of density. sp.annotate_contour( ("gas", "density"), levels=3, clim=(1e-2, 1e-1), label=True, plot_args={"colors": "red", "linewidths": 2}, ) # What about some nice temperature contours in blue? sp.annotate_contour( ("gas", "temperature"), levels=3, clim=(1e-8, 1e-6), label=True, plot_args={"colors": "blue", "linewidths": 2}, ) # This is the plot object. po = sp.plots["gas", "density"] # Turn off the colormap image, leaving just the contours. po.axes.images[0].set_visible(False) # Remove the colorbar and its label. po.figure.delaxes(po.figure.axes[1]) # Save it and ask for a close fit to get rid of the space used by the colorbar. sp.save(mpl_kwargs={"bbox_inches": "tight"}) yt-project-yt-f043ac8/doc/source/cookbook/simple_off_axis_projection.py000066400000000000000000000011031510711153200264750ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a 15 kpc radius sphere, centered on the center of the sim volume sp = ds.sphere("center", (15.0, "kpc")) # Get the angular momentum vector for the sphere. L = sp.quantities.angular_momentum_vector() print(f"Angular momentum vector: {L}") # Create an off-axis ProjectionPlot of density centered on the object with the L # vector as its normal and a width of 25 kpc on a side p = yt.ProjectionPlot( ds, L, fields=("gas", "density"), center=sp.center, width=(25, "kpc") ) p.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_off_axis_slice.py000066400000000000000000000010441510711153200254240ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a 15 kpc radius sphere, centered on the center of the sim volume sp = ds.sphere("center", (15.0, "kpc")) # Get the angular momentum vector for the sphere. L = sp.quantities.angular_momentum_vector() print(f"Angular momentum vector: {L}") # Create an OffAxisSlicePlot of density centered on the object with the L # vector as its normal and a width of 25 kpc on a side p = yt.OffAxisSlicePlot(ds, L, ("gas", "density"), sp.center, (25, "kpc")) p.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_pdf.py000066400000000000000000000010701510711153200232170ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GalaxyClusterMerger/fiducial_1to3_b0.273d_hdf5_plt_cnt_0175") # Create a data object that represents the whole box. ad = ds.all_data() # This is identical to the simple phase plot, except we supply # the fractional=True keyword to divide the profile data by the sum. plot = yt.PhasePlot( ad, ("gas", "density"), ("gas", "temperature"), ("gas", "mass"), weight_field=None, fractional=True, ) # Save the image. # Optionally, give a string as an argument # to name files with a keyword. plot.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_phase.py000066400000000000000000000012751510711153200235550ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a sphere of radius 100 kpc in the center of the domain. my_sphere = ds.sphere("c", (100.0, "kpc")) # Create a PhasePlot object. # Setting weight to None will calculate a sum. # Setting weight to a field will calculate an average # weighted by that field. plot = yt.PhasePlot( my_sphere, ("gas", "density"), ("gas", "temperature"), ("gas", "mass"), weight_field=None, ) # Set the units of mass to be in solar masses (not the default in cgs) plot.set_unit(("gas", "mass"), "Msun") # Save the image. # Optionally, give a string as an argument # to name files with a keyword. plot.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_plots.rst000066400000000000000000000225551510711153200240020ustar00rootroot00000000000000Making Simple Plots ------------------- One of the easiest ways to interact with yt is by creating simple visualizations of your data. Below we show how to do this, as well as how to extend these plots to be ready for publication. Simple Slices ~~~~~~~~~~~~~ This script shows the simplest way to make a slice through a dataset. See :ref:`slice-plots` for more information. .. yt_cookbook:: simple_slice.py Simple Projections (Non-Weighted) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the simplest way to make a projection through a dataset. There are several different :ref:`projection-types`, but non-weighted line integrals and weighted line integrals are the two most common. Here we create density projections (non-weighted line integral). See :ref:`projection-plots` for more information. .. yt_cookbook:: simple_projection.py Simple Projections (Weighted) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ And here we produce density-weighted temperature projections (weighted line integral) for the same dataset as the non-weighted projections above. See :ref:`projection-plots` for more information. .. yt_cookbook:: simple_projection_weighted.py Simple Projections (Methods) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ And here we illustrate different methods for projection plots (integrate, minimum, maximum). .. yt_cookbook:: simple_projection_methods.py Simple Projections (Weighted Standard Deviation) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ And here we produce a density-weighted projection (weighted line integral) of the line-of-sight velocity from the same dataset (see :ref:`projection-plots` for more information). .. yt_cookbook:: simple_projection_stddev.py Simple Phase Plots ~~~~~~~~~~~~~~~~~~ This demonstrates how to make a phase plot. Phase plots can be thought of as two-dimensional histograms, where the value is either the weighted-average or the total accumulation in a cell. See :ref:`how-to-make-2d-profiles` for more information. .. yt_cookbook:: simple_phase.py Simple 1D Line Plotting ~~~~~~~~~~~~~~~~~~~~~~~ This script shows how to make a ``LinePlot`` through a dataset. See :ref:`manual-line-plots` for more information. .. yt_cookbook:: simple_1d_line_plot.py .. note:: Not every data types have support for ``yt.LinePlot`` yet. Currently, this operation is supported for grid based data with cartesian geometry. Simple Probability Distribution Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Often, one wants to examine the distribution of one variable as a function of another. This shows how to see the distribution of mass in a simulation, with respect to the total mass in the simulation. See :ref:`how-to-make-2d-profiles` for more information. .. yt_cookbook:: simple_pdf.py Simple 1D Histograms (Profiles) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a "profile," which is a 1D histogram. This can be thought of as either the total accumulation (when weight_field is set to ``None``) or the average (when a weight_field is supplied.) See :ref:`how-to-make-1d-profiles` for more information. .. yt_cookbook:: simple_profile.py Simple Radial Profiles ~~~~~~~~~~~~~~~~~~~~~~ This shows how to make a profile of a quantity with respect to the radius. See :ref:`how-to-make-1d-profiles` for more information. .. yt_cookbook:: simple_radial_profile.py 1D Profiles Over Time ~~~~~~~~~~~~~~~~~~~~~ This is a simple example of overplotting multiple 1D profiles from a number of datasets to show how they evolve over time. See :ref:`how-to-make-1d-profiles` for more information. .. yt_cookbook:: time_series_profiles.py .. _cookbook-profile-stddev: Profiles with Standard Deviation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This shows how to plot a 1D profile with error bars indicating the standard deviation of the field values in each profile bin. In this example, we manually create a 1D profile object, which gives us access to the standard deviation data. See :ref:`how-to-make-1d-profiles` for more information. .. yt_cookbook:: profile_with_standard_deviation.py Making Plots of Multiple Fields Simultaneously ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By adding multiple fields to a single :class:`~yt.visualization.plot_window.SlicePlot` or :class:`~yt.visualization.plot_window.ProjectionPlot` some of the overhead of creating the data object can be reduced, and better performance squeezed out. This recipe shows how to add multiple fields to a single plot. See :ref:`slice-plots` and :ref:`projection-plots` for more information. .. yt_cookbook:: simple_slice_with_multiple_fields.py Off-Axis Slicing ~~~~~~~~~~~~~~~~ One can create slices from any arbitrary angle, not just those aligned with the x,y,z axes. See :ref:`off-axis-slices` for more information. .. yt_cookbook:: simple_off_axis_slice.py .. _cookbook-simple-off-axis-projection: Off-Axis Projection ~~~~~~~~~~~~~~~~~~~ Like off-axis slices, off-axis projections can be created from any arbitrary viewing angle. See :ref:`off-axis-projections` for more information. .. yt_cookbook:: simple_off_axis_projection.py .. _cookbook-simple-particle-plot: Simple Particle Plot ~~~~~~~~~~~~~~~~~~~~ You can also use yt to make particle-only plots. This script shows how to plot all the particle x and y positions in a dataset, using the particle mass to set the color scale. See :ref:`particle-plots` for more information. .. yt_cookbook:: particle_xy_plot.py .. _cookbook-non-spatial-particle-plot: Non-spatial Particle Plots ~~~~~~~~~~~~~~~~~~~~~~~~~~ You are not limited to plotting spatial fields on the x and y axes. This example shows how to plot the particle x-coordinates versus their z-velocities, again using the particle mass to set the colorbar. See :ref:`particle-plots` for more information. .. yt_cookbook:: particle_xvz_plot.py .. _cookbook-single-color-particle-plot: Single-color Particle Plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you don't want to display a third field on the color bar axis, simply pass in a color string instead of a particle field. See :ref:`particle-plots` for more information. .. yt_cookbook:: particle_one_color_plot.py .. _cookbook-simple_volume_rendering: Simple Volume Rendering ~~~~~~~~~~~~~~~~~~~~~~~ Volume renderings are 3D projections rendering isocontours in any arbitrary field (e.g. density, temperature, pressure, etc.) See :ref:`volume_rendering` for more information. .. yt_cookbook:: simple_volume_rendering.py .. _show-hide-axes-colorbar: Showing and Hiding Axis Labels and Colorbars ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This example illustrates how to create a SlicePlot and then suppress the axes labels and colorbars. This is useful when you don't care about the physical scales and just want to take a closer look at the raw plot data. See :ref:`hiding-colorbar-and-axes` for more information. .. yt_cookbook:: show_hide_axes_colorbar.py .. _cookbook_label_formats: Setting Field Label Formats --------------------------- This example illustrates how to change the label format for ion species from the default roman numeral style. .. yt_cookbook:: changing_label_formats.py .. _matplotlib-primitives: Accessing and Modifying Plots Directly ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ While often the Plot Window, and its affiliated :ref:`callbacks` can cover normal use cases, sometimes more direct access to the underlying Matplotlib engine is necessary. This recipe shows how to modify the plot window :class:`matplotlib.axes.Axes` object directly. See :ref:`matplotlib-customization` for more information. .. yt_cookbook:: simple_slice_matplotlib_example.py Changing the Colormap used in a Plot ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ yt has sensible defaults for colormaps, but there are over a hundred available for customizing your plots. Here we generate a projection and then change its colormap. See :ref:`colormaps` for a list and for images of all the available colormaps. .. yt_cookbook:: colormaps.py Image Background Colors ~~~~~~~~~~~~~~~~~~~~~~~ Here we see how to take an image and save it using different background colors. In this case we use the :ref:`cookbook-simple_volume_rendering` recipe to generate the image, but it works for any NxNx4 image array (3 colors and 1 opacity channel). See :ref:`volume_rendering` for more information. .. yt_cookbook:: image_background_colors.py .. _annotations-recipe: Annotating Plots to Include Lines, Text, Shapes, etc. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It can be useful to add annotations to plots to show off certain features and make it easier for your audience to understand the plot's purpose. There are a variety of available :ref:`plot modifications ` one can use to add annotations to their plots. Below includes just a handful, but please look at the other :ref:`plot modifications ` to get a full description of what you can do to highlight your figures. .. yt_cookbook:: annotations.py Annotating Plots with a Timestamp and Physical Scale ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When creating movies of multiple outputs from the same simulation (see :ref:`time-series-analysis`), it can be helpful to include a timestamp and the physical scale of each individual output. This is simply achieved using the :ref:`annotate_timestamp() ` and :ref:`annotate_scale() ` callbacks on your plots. For more information about similar plot modifications using other callbacks, see the section on :ref:`Plot Modifications `. .. yt_cookbook:: annotate_timestamp_and_scale.py yt-project-yt-f043ac8/doc/source/cookbook/simple_profile.py000066400000000000000000000010631510711153200241100ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a 1D profile within a sphere of radius 100 kpc # of the average temperature and average velocity_x # vs. density, weighted by mass. sphere = ds.sphere("c", (100.0, "kpc")) plot = yt.ProfilePlot( sphere, ("gas", "density"), [("gas", "temperature"), ("gas", "velocity_x")], weight_field=("gas", "mass"), ) plot.set_log(("gas", "velocity_x"), False) # Save the image. # Optionally, give a string as an argument # to name files with a keyword. plot.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_projection.py000066400000000000000000000005261510711153200246270ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GalaxyClusterMerger/fiducial_1to3_b0.273d_hdf5_plt_cnt_0175") # Create projections of the gas density (non-weighted line integrals). yt.ProjectionPlot(ds, "x", ("gas", "density")).save() yt.ProjectionPlot(ds, "y", ("gas", "density")).save() yt.ProjectionPlot(ds, "z", ("gas", "density")).save() yt-project-yt-f043ac8/doc/source/cookbook/simple_projection_methods.py000066400000000000000000000005241510711153200263500ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GalaxyClusterMerger/fiducial_1to3_b0.273d_hdf5_plt_cnt_0175") # Create projections of temperature (with different methods) for method in ["integrate", "min", "max"]: proj = yt.ProjectionPlot(ds, "x", ("gas", "temperature"), method=method) proj.save(f"projection_method_{method}.png") yt-project-yt-f043ac8/doc/source/cookbook/simple_projection_stddev.py000066400000000000000000000006141510711153200261760ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GalaxyClusterMerger/fiducial_1to3_b0.273d_hdf5_plt_cnt_0175") # Create density-weighted projections of standard deviation of the velocity # (weighted line integrals) for normal in "xyz": yt.ProjectionPlot( ds, normal, ("gas", f"velocity_{normal}"), weight_field=("gas", "density"), moment=2, ).save() yt-project-yt-f043ac8/doc/source/cookbook/simple_projection_weighted.py000066400000000000000000000005011510711153200265000ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GalaxyClusterMerger/fiducial_1to3_b0.273d_hdf5_plt_cnt_0175") # Create density-weighted projections of temperature (weighted line integrals) for normal in "xyz": yt.ProjectionPlot( ds, normal, ("gas", "temperature"), weight_field=("gas", "density") ).save() yt-project-yt-f043ac8/doc/source/cookbook/simple_radial_profile.py000066400000000000000000000011141510711153200254210ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a sphere of radius 100 kpc in the center of the box. my_sphere = ds.sphere("c", (100.0, "kpc")) # Create a profile of the average density vs. radius. plot = yt.ProfilePlot( my_sphere, ("index", "radius"), ("gas", "density"), weight_field=("gas", "mass"), ) # Change the units of the radius into kpc (and not the default in cgs) plot.set_unit(("index", "radius"), "kpc") # Save the image. # Optionally, give a string as an argument # to name files with a keyword. plot.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_slice.py000066400000000000000000000005421510711153200235500ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") # Create gas density slices in all three axes. yt.SlicePlot(ds, "x", ("gas", "density"), width=(800.0, "kpc")).save() yt.SlicePlot(ds, "y", ("gas", "density"), width=(800.0, "kpc")).save() yt.SlicePlot(ds, "z", ("gas", "density"), width=(800.0, "kpc")).save() yt-project-yt-f043ac8/doc/source/cookbook/simple_slice_matplotlib_example.py000066400000000000000000000020431510711153200275100ustar00rootroot00000000000000import numpy as np import yt # Load the dataset. ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") # Create a slice object slc = yt.SlicePlot(ds, "x", ("gas", "density"), width=(800.0, "kpc")) # Rendering should be performed explicitly *before* any modification is # performed directly with matplotlib. slc.render() # Get a reference to the matplotlib axes object for the plot ax = slc.plots["gas", "density"].axes # Let's adjust the x axis tick labels for label in ax.xaxis.get_ticklabels(): label.set_color("red") label.set_fontsize(16) # Get a reference to the matplotlib figure object for the plot fig = slc.plots["gas", "density"].figure # And create a mini-panel of a gaussian histogram inside the plot rect = (0.2, 0.2, 0.2, 0.2) new_ax = fig.add_axes(rect) n, bins, patches = new_ax.hist( np.random.randn(1000) + 20, 50, facecolor="black", edgecolor="black" ) # Make sure its visible new_ax.tick_params(colors="white") # And label it la = new_ax.set_xlabel("Dinosaurs per furlong") la.set_color("white") slc.save() yt-project-yt-f043ac8/doc/source/cookbook/simple_slice_with_multiple_fields.py000066400000000000000000000004641510711153200300470ustar00rootroot00000000000000import yt # Load the dataset ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") # Create gas density slices of several fields along the x axis simultaneously yt.SlicePlot( ds, "x", [("gas", "density"), ("gas", "temperature"), ("gas", "pressure")], width=(800.0, "kpc"), ).save() yt-project-yt-f043ac8/doc/source/cookbook/simple_volume_rendering.py000066400000000000000000000005411510711153200260140ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("Enzo_64/DD0043/data0043") # Create a volume rendering, which will determine data bounds, use the first # acceptable field in the field_list, and set up a default transfer function. # This will save a file named 'data0043_Render_density.png' to disk. im, sc = yt.volume_render(ds, field=("gas", "density")) yt-project-yt-f043ac8/doc/source/cookbook/simulation_analysis.py000066400000000000000000000024041510711153200251660ustar00rootroot00000000000000import yt yt.enable_parallelism() # Enable parallelism in the script (assuming it was called with # `mpirun -np ` ) yt.enable_parallelism() # By using wildcards such as ? and * with the load command, we can load up a # Time Series containing all of these datasets simultaneously. ts = yt.load("enzo_tiny_cosmology/DD????/DD????") # Calculate and store density extrema for all datasets along with redshift # in a data dictionary with entries as tuples # Create an empty dictionary data = {} # Iterate through each dataset in the Time Series (using piter allows it # to happen in parallel automatically across available processors) for ds in ts.piter(): ad = ds.all_data() extrema = ad.quantities.extrema(("gas", "density")) # Fill the dictionary with extrema and redshift information for each dataset data[ds.basename] = (extrema, ds.current_redshift) # Sort dict by keys data = {k: v for k, v in sorted(data.items())} # Print out all the values we calculated. print("Dataset Redshift Density Min Density Max") print("---------------------------------------------------------") for key, val in data.items(): print( "%s %05.3f %5.3g g/cm^3 %5.3g g/cm^3" % (key, val[1], val[0][0], val[0][1]) ) yt-project-yt-f043ac8/doc/source/cookbook/streamlines.py000066400000000000000000000023421510711153200234260ustar00rootroot00000000000000import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D import yt from yt.units import Mpc from yt.visualization.api import Streamlines # Load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Define c: the center of the box, N: the number of streamlines, # scale: the spatial scale of the streamlines relative to the boxsize, # and then pos: the random positions of the streamlines. c = ds.domain_center N = 100 scale = ds.domain_width[0] pos_dx = np.random.random((N, 3)) * scale - scale / 2.0 pos = c + pos_dx # Create streamlines of the 3D vector velocity and integrate them through # the box defined above streamlines = Streamlines( ds, pos, ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z"), length=1.0 * Mpc, get_magnitude=True, ) streamlines.integrate_through_volume() # Create a 3D plot, trace the streamlines through the 3D volume of the plot fig = plt.figure() ax = Axes3D(fig, auto_add_to_figure=False) fig.add_axes(ax) for stream in streamlines.streamlines: stream = stream[np.all(stream != 0.0, axis=1)] ax.plot3D(stream[:, 0], stream[:, 1], stream[:, 2], alpha=0.1) # Save the plot to disk. plt.savefig("streamlines.png") yt-project-yt-f043ac8/doc/source/cookbook/streamlines_isocontour.py000066400000000000000000000046721510711153200257220ustar00rootroot00000000000000import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.mplot3d.art3d import Poly3DCollection import yt from yt.visualization.api import Streamlines # Load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Define c: the center of the box, N: the number of streamlines, # scale: the spatial scale of the streamlines relative to the boxsize, # and then pos: the random positions of the streamlines. c = ds.arr([0.5] * 3, "code_length") N = 30 scale = ds.quan(15, "kpc").in_units("code_length") # 15 kpc in code units pos_dx = np.random.random((N, 3)) * scale - scale / 2.0 pos = c + pos_dx # Create the streamlines from these positions with the velocity fields as the # fields to be traced streamlines = Streamlines( ds, pos, ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z"), length=1.0, ) streamlines.integrate_through_volume() # Create a 3D matplotlib figure for visualizing the streamlines fig = plt.figure() ax = Axes3D(fig, auto_add_to_figure=False) fig.add_axes(ax) # Trace the streamlines through the volume of the 3D figure for stream in streamlines.streamlines: stream = stream[np.all(stream != 0.0, axis=1)] # Make the colors of each stream vary continuously from blue to red # from low-x to high-x of the stream start position (each color is R, G, B) # can omit and just set streamline colors to a fixed color x_start_pos = ds.arr(stream[0, 0], "code_length") x_start_pos -= ds.arr(0.5, "code_length") x_start_pos /= scale x_start_pos += 0.5 color = np.array([x_start_pos, 0, 1 - x_start_pos]) # Plot the stream in 3D ax.plot3D(stream[:, 0], stream[:, 1], stream[:, 2], alpha=0.3, color=color) # Create a sphere object centered on the highest density point in the simulation # with radius = 1 Mpc sphere = ds.sphere("max", (1.0, "Mpc")) # Identify the isodensity surface in this sphere with density = 1e-24 g/cm^3 surface = ds.surface(sphere, ("gas", "density"), 1e-24) # Color this isodensity surface according to the log of the temperature field colors = yt.apply_colormap(np.log10(surface["gas", "temperature"]), cmap_name="hot") # Render this surface p3dc = Poly3DCollection(surface.triangles, linewidth=0.0) colors = colors[0, :, :] / 255.0 # scale to [0,1] colors[:, 3] = 0.3 # alpha = 0.3 p3dc.set_facecolors(colors) ax.add_collection(p3dc) # Save the figure plt.savefig("streamlines_isocontour.png") yt-project-yt-f043ac8/doc/source/cookbook/sum_mass_in_sphere.py000066400000000000000000000012041510711153200247570ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("Enzo_64/DD0029/data0029") # Create a 1 Mpc radius sphere, centered on the max density. sp = ds.sphere("max", (1.0, "Mpc")) # Use the total_quantity derived quantity to sum up the # values of the mass and particle_mass fields # within the sphere. baryon_mass, particle_mass = sp.quantities.total_quantity( [("gas", "mass"), ("all", "particle_mass")] ) print( "Total mass in sphere is %0.3e Msun (gas = %0.3e Msun, particles = %0.3e Msun)" % ( (baryon_mass + particle_mass).in_units("Msun"), baryon_mass.in_units("Msun"), particle_mass.in_units("Msun"), ) ) yt-project-yt-f043ac8/doc/source/cookbook/surface_plot.py000066400000000000000000000027421510711153200235720ustar00rootroot00000000000000import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D # noqa: F401 from mpl_toolkits.mplot3d.art3d import Poly3DCollection import yt # Load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create a sphere object centered on the highest density point in the simulation # with radius 1 Mpc sphere = ds.sphere("max", (1.0, "Mpc")) # Identify the isodensity surface in this sphere with density = 1e-24 g/cm^3 surface = ds.surface(sphere, ("gas", "density"), 1e-24) # Color this isodensity surface according to the log of the temperature field colors = yt.apply_colormap(np.log10(surface["gas", "temperature"]), cmap_name="hot") # Create a 3D matplotlib figure for visualizing the surface fig = plt.figure() ax = fig.add_subplot(projection="3d") p3dc = Poly3DCollection(surface.triangles, linewidth=0.0) # Set the surface colors in the right scaling [0,1] p3dc.set_facecolors(colors[0, :, :] / 255.0) ax.add_collection(p3dc) # Let's keep the axis ratio fixed in all directions by taking the maximum # extent in one dimension and make it the bounds in all dimensions max_extent = (surface.vertices.max(axis=1) - surface.vertices.min(axis=1)).max() centers = (surface.vertices.max(axis=1) + surface.vertices.min(axis=1)) / 2 bounds = np.zeros([3, 2]) bounds[:, 0] = centers[:] - max_extent / 2 bounds[:, 1] = centers[:] + max_extent / 2 ax.auto_scale_xyz(bounds[0, :], bounds[1, :], bounds[2, :]) # Save the figure plt.savefig(f"{ds}_Surface.png") yt-project-yt-f043ac8/doc/source/cookbook/tests/000077500000000000000000000000001510711153200216675ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/cookbook/tests/test_cookbook.py000066400000000000000000000022641510711153200251120ustar00rootroot00000000000000"""Module for cookbook testing This test should be run from main yt directory. Example: $ sed -e '/where/d' -i nose.cfg setup.cfg $ nosetests doc/source/cookbook/tests/test_cookbook.py -P -v """ import subprocess import sys from pathlib import Path BLACKLIST = [ "matplotlib-animation.py", ] def test_recipe(): """Dummy test grabbing all cookbook's recipes""" COOKBOOK_DIR = Path("doc", "source", "cookbook") for fname in sorted(COOKBOOK_DIR.glob("*.py")): if fname.name in BLACKLIST: continue check_recipe.description = f"Testing recipe: {fname.name}" yield check_recipe, ["python", str(fname)] def check_recipe(cmd): """Run single recipe""" proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate() if out: sys.stdout.write(out.decode("utf8")) if err: sys.stderr.write(err.decode("utf8")) if proc.returncode != 0: retstderr = " ".join(cmd) retstderr += "\n\nTHIS IS THE REAL CAUSE OF THE FAILURE:\n" retstderr += err.decode("UTF-8") + "\n" raise subprocess.CalledProcessError(proc.returncode, retstderr) yt-project-yt-f043ac8/doc/source/cookbook/thin_slice_projection.py000066400000000000000000000021531510711153200254550ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("Enzo_64/DD0030/data0030") # Make a projection that is the full width of the domain, # but only 5 Mpc in depth. This is done by creating a # region object with this exact geometry and providing it # as a data_source for the projection. # Center on the domain center center = ds.domain_center.copy() # First make the left and right corner of the region based # on the full domain. left_corner = ds.domain_left_edge.copy() right_corner = ds.domain_right_edge.copy() # Now adjust the size of the region along the line of sight (x axis). depth = ds.quan(5.0, "Mpc") left_corner[0] = center[0] - 0.5 * depth right_corner[0] = center[0] + 0.5 * depth # Create the region region = ds.box(left_corner, right_corner) # Create a density projection and supply the region we have just created. # Only cells within the region will be included in the projection. # Try with another data container, like a sphere or disk. plot = yt.ProjectionPlot( ds, "x", ("gas", "density"), weight_field=("gas", "density"), data_source=region ) # Save the image with the keyword. plot.save("Thin_Slice") yt-project-yt-f043ac8/doc/source/cookbook/time_series.py000066400000000000000000000032441510711153200234120ustar00rootroot00000000000000import matplotlib.pyplot as plt import numpy as np import yt # Enable parallelism in the script (assuming it was called with # `mpirun -np ` ) yt.enable_parallelism() # By using wildcards such as ? and * with the load command, we can load up a # Time Series containing all of these datasets simultaneously. # The "entropy" field that we will use below depends on the electron number # density, which is not in these datasets by default, so we assume full # ionization using the "default_species_fields" kwarg. ts = yt.load( "GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0*", default_species_fields="ionized", ) storage = {} # By using the piter() function, we can iterate on every dataset in # the TimeSeries object. By using the storage keyword, we can populate # a dictionary where the dataset is the key, and sto.result is the value # for later use when the loop is complete. # The serial equivalent of piter() here is just "for ds in ts:" . for store, ds in ts.piter(storage=storage): # Create a sphere of radius 100 kpc at the center of the dataset volume sphere = ds.sphere("c", (100.0, "kpc")) # Calculate the entropy within that sphere entr = sphere["gas", "entropy"].sum() # Store the current time and sphere entropy for this dataset in our # storage dictionary as a tuple store.result = (ds.current_time.in_units("Gyr"), entr) # Convert the storage dictionary values to a Nx2 array, so the can be easily # plotted arr = np.array(list(storage.values())) # Plot up the results: time versus entropy plt.semilogy(arr[:, 0], arr[:, 1], "r-") plt.xlabel("Time (Gyr)") plt.ylabel("Entropy (ergs/K)") plt.savefig("time_versus_entropy.png") yt-project-yt-f043ac8/doc/source/cookbook/time_series_profiles.py000066400000000000000000000015541510711153200253170ustar00rootroot00000000000000import yt # Create a time-series object. sim = yt.load_simulation("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo") sim.get_time_series(redshifts=[5, 4, 3, 2, 1, 0]) # Lists to hold profiles, labels, and plot specifications. profiles = [] labels = [] plot_specs = [] # Loop over each dataset in the time-series. for ds in sim: # Create a data container to hold the whole dataset. ad = ds.all_data() # Create a 1d profile of density vs. temperature. profiles.append( yt.create_profile(ad, [("gas", "density")], fields=[("gas", "temperature")]) ) # Add labels and linestyles. labels.append(f"z = {ds.current_redshift:.2f}") plot_specs.append(dict(linewidth=2, alpha=0.7)) # Create the profile plot from the list of profiles. plot = yt.ProfilePlot.from_profiles(profiles, labels=labels, plot_specs=plot_specs) # Save the image. plot.save() yt-project-yt-f043ac8/doc/source/cookbook/tipsy_and_yt.ipynb000066400000000000000000000116131510711153200243000ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading Tipsy Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alright, let's start with some basics. Before we do anything, we will need to load a snapshot. You can do this using the ```load_sample``` convenience function. yt will autodetect that you want a tipsy snapshot and download it from the yt hub." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will be looking at a fairly low resolution dataset." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ">This dataset is available for download at https://yt-project.org/data/TipsyGalaxy.tar.gz (10 MB)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load_sample(\"TipsyGalaxy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now have a `TipsyDataset` object called `ds`. Let's see what fields it has." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds.field_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt also defines so-called \"derived\" fields. These fields are functions of the on-disk fields that live in the `field_list`. There is a `derived_field_list` attribute attached to the `Dataset` object - let's take look at the derived fields in this dataset:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds.derived_field_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All of the field in the `field_list` are arrays containing the values for the associated particles. These haven't been smoothed or gridded in any way. We can grab the array-data for these particles using `ds.all_data()`. For example, let's take a look at a temperature-colored scatterplot of the gas particles in this output." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad = ds.all_data()\n", "xcoord = ad[\"Gas\", \"Coordinates\"][:, 0].v\n", "ycoord = ad[\"Gas\", \"Coordinates\"][:, 1].v\n", "logT = np.log10(ad[\"Gas\", \"Temperature\"])\n", "plt.scatter(\n", " xcoord, ycoord, c=logT, s=2 * logT, marker=\"o\", edgecolor=\"none\", vmin=2, vmax=6\n", ")\n", "plt.xlim(-20, 20)\n", "plt.ylim(-20, 20)\n", "cb = plt.colorbar()\n", "cb.set_label(r\"$\\log_{10}$ Temperature\")\n", "plt.gcf().set_size_inches(15, 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Making Smoothed Images" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt will automatically generate smoothed versions of these fields that you can use to plot. Let's make a temperature slice and a density projection." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "yt.SlicePlot(ds, \"z\", (\"gas\", \"density\"), width=(40, \"kpc\"), center=\"m\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "yt.ProjectionPlot(ds, \"z\", (\"gas\", \"density\"), width=(40, \"kpc\"), center=\"m\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not only are the values in the tipsy snapshot read and automatically smoothed, the auxiliary files that have physical significance are also smoothed. Let's look at a slice of Iron mass fraction." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "yt.SlicePlot(ds, \"z\", (\"gas\", \"Fe_fraction\"), width=(40, \"kpc\"), center=\"m\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/cookbook/various_lens.py000066400000000000000000000111121510711153200236040ustar00rootroot00000000000000import numpy as np import yt from yt.visualization.volume_rendering.api import Scene, create_volume_source field = ("gas", "density") # normal_vector points from camera to the center of the final projection. # Now we look at the positive x direction. normal_vector = [1.0, 0.0, 0.0] # north_vector defines the "top" direction of the projection, which is # positive z direction here. north_vector = [0.0, 0.0, 1.0] # Follow the simple_volume_rendering cookbook for the first part of this. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sc = Scene() vol = create_volume_source(ds, field=field) tf = vol.transfer_function tf.grey_opacity = True # Plane-parallel lens cam = sc.add_camera(ds, lens_type="plane-parallel") # Set the resolution of the final projection. cam.resolution = [250, 250] # Set the location of the camera to be (x=0.2, y=0.5, z=0.5) # For plane-parallel lens, the location info along the normal_vector (here # is x=0.2) is ignored. cam.position = ds.arr(np.array([0.2, 0.5, 0.5]), "code_length") # Set the orientation of the camera. cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) # Set the width of the camera, where width[0] and width[1] specify the length and # height of final projection, while width[2] in plane-parallel lens is not used. cam.set_width(ds.domain_width * 0.5) sc.add_source(vol) sc.save("lens_plane-parallel.png", sigma_clip=6.0) # Perspective lens cam = sc.add_camera(ds, lens_type="perspective") cam.resolution = [250, 250] # Standing at (x=0.2, y=0.5, z=0.5), we look at the area of x>0.2 (with some open angle # specified by camera width) along the positive x direction. cam.position = ds.arr([0.2, 0.5, 0.5], "code_length") cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) # Set the width of the camera, where width[0] and width[1] specify the length and # height of the final projection, while width[2] specifies the distance between the # camera and the final image. cam.set_width(ds.domain_width * 0.5) sc.add_source(vol) sc.save("lens_perspective.png", sigma_clip=6.0) # Stereo-perspective lens cam = sc.add_camera(ds, lens_type="stereo-perspective") # Set the size ratio of the final projection to be 2:1, since stereo-perspective lens # will generate the final image with both left-eye and right-eye ones jointed together. cam.resolution = [500, 250] cam.position = ds.arr([0.2, 0.5, 0.5], "code_length") cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) cam.set_width(ds.domain_width * 0.5) # Set the distance between left-eye and right-eye. cam.lens.disparity = ds.domain_width[0] * 1.0e-3 sc.add_source(vol) sc.save("lens_stereo-perspective.png", sigma_clip=6.0) # Fisheye lens dd = ds.sphere(ds.domain_center, ds.domain_width[0] / 10) cam = sc.add_camera(dd, lens_type="fisheye") cam.resolution = [250, 250] v, c = ds.find_max(field) cam.set_position(c - 0.0005 * ds.domain_width) cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) cam.set_width(ds.domain_width) cam.lens.fov = 360.0 sc.add_source(vol) sc.save("lens_fisheye.png", sigma_clip=6.0) # Spherical lens cam = sc.add_camera(ds, lens_type="spherical") # Set the size ratio of the final projection to be 2:1, since spherical lens # will generate the final image with length of 2*pi and height of pi. # Recommended resolution for YouTube 360-degree videos is [3840, 2160] cam.resolution = [500, 250] # Standing at (x=0.4, y=0.5, z=0.5), we look in all the radial directions # from this point in spherical coordinate. cam.position = ds.arr([0.4, 0.5, 0.5], "code_length") cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) # In (stereo)spherical camera, camera width is not used since the entire volume # will be rendered sc.add_source(vol) sc.save("lens_spherical.png", sigma_clip=6.0) # Stereo-spherical lens cam = sc.add_camera(ds, lens_type="stereo-spherical") # Set the size ratio of the final projection to be 1:1, since spherical-perspective lens # will generate the final image with both left-eye and right-eye ones jointed together, # with left-eye image on top and right-eye image on bottom. # Recommended resolution for YouTube virtual reality videos is [3840, 2160] cam.resolution = [500, 500] cam.position = ds.arr([0.4, 0.5, 0.5], "code_length") cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) # In (stereo)spherical camera, camera width is not used since the entire volume # will be rendered # Set the distance between left-eye and right-eye. cam.lens.disparity = ds.domain_width[0] * 1.0e-3 sc.add_source(vol) sc.save("lens_stereo-spherical.png", sigma_clip=6.0) yt-project-yt-f043ac8/doc/source/cookbook/velocity_vectors_on_slice.py000066400000000000000000000003371510711153200263600ustar00rootroot00000000000000import yt # Load the dataset. ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") p = yt.SlicePlot(ds, "x", ("gas", "density")) # Draw a velocity vector every 16 pixels. p.annotate_velocity(factor=16) p.save() yt-project-yt-f043ac8/doc/source/cookbook/vol-annotated.py000066400000000000000000000016721510711153200236600ustar00rootroot00000000000000import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") source = sc[0] source.set_field(("gas", "density")) source.set_log(True) # Set up the camera parameters: focus, width, resolution, and image orientation sc.camera.focus = ds.domain_center sc.camera.resolution = 1024 sc.camera.north_vector = [0, 0, 1] sc.camera.position = [1.7, 1.7, 1.7] # You may need to adjust the alpha values to get an image with good contrast. # For the annotate_domain call, the fourth value in the color tuple is the # alpha value. sc.annotate_axes(alpha=0.02) sc.annotate_domain(ds, color=[1, 1, 1, 0.01]) text_string = f"T = {float(ds.current_time.to('Gyr'))} Gyr" # save an annotated version of the volume rendering including a representation # of the transfer function and a nice label showing the simulation time. sc.save_annotated( "vol_annotated.png", sigma_clip=6, text_annotate=[[(0.1, 0.95), text_string]] ) yt-project-yt-f043ac8/doc/source/cookbook/vol-lines.py000066400000000000000000000007251510711153200230130ustar00rootroot00000000000000import numpy as np import yt from yt.units import kpc from yt.visualization.volume_rendering.api import LineSource ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sc = yt.create_scene(ds) np.random.seed(1234567) nlines = 50 vertices = (np.random.random([nlines, 2, 3]) - 0.5) * 200 * kpc colors = np.random.random([nlines, 4]) colors[:, 3] = 0.1 lines = LineSource(vertices, colors) sc.add_source(lines) sc.camera.width = 300 * kpc sc.save(sigma_clip=4.0) yt-project-yt-f043ac8/doc/source/cookbook/vol-points.py000066400000000000000000000011401510711153200232050ustar00rootroot00000000000000import numpy as np import yt from yt.units import kpc from yt.visualization.volume_rendering.api import PointSource ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sc = yt.create_scene(ds) np.random.seed(1234567) npoints = 1000 # Random particle positions vertices = np.random.random([npoints, 3]) * 200 * kpc # Random colors colors = np.random.random([npoints, 4]) # Set alpha value to something that produces a good contrast with the volume # rendering colors[:, 3] = 0.1 points = PointSource(vertices, colors=colors) sc.add_source(points) sc.camera.width = 300 * kpc sc.save(sigma_clip=5) yt-project-yt-f043ac8/doc/source/cookbook/yt_gadget_analysis.ipynb000066400000000000000000000142531510711153200254470ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading Gadget data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we set up our imports:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "\n", "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we load the data set, specifying both the unit length/mass/velocity, as well as the size of the bounding box (which should encapsulate all the particles in the data set)\n", "\n", "At the end, we flatten the data into \"ad\" in case we want access to the raw simulation data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ">This dataset is available for download at https://yt-project.org/data/GadgetDiskGalaxy.tar.gz (430 MB)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fname = \"GadgetDiskGalaxy/snapshot_200.hdf5\"\n", "\n", "unit_base = {\n", " \"UnitLength_in_cm\": 3.08568e21,\n", " \"UnitMass_in_g\": 1.989e43,\n", " \"UnitVelocity_in_cm_per_s\": 100000,\n", "}\n", "\n", "bbox_lim = 1e5 # kpc\n", "\n", "bbox = [[-bbox_lim, bbox_lim], [-bbox_lim, bbox_lim], [-bbox_lim, bbox_lim]]\n", "\n", "ds = yt.load(fname, unit_base=unit_base, bounding_box=bbox)\n", "ds.index\n", "ad = ds.all_data()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make a projection plot to look at the entire volume" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "px = yt.ProjectionPlot(ds, \"x\", (\"gas\", \"density\"))\n", "px.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's print some quantities about the domain, as well as the physical properties of the simulation\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(\"left edge: \", ds.domain_left_edge)\n", "print(\"right edge: \", ds.domain_right_edge)\n", "print(\"center: \", ds.domain_center)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also see the fields that are available to query in the dataset" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sorted(ds.field_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a data object that represents the full simulation domain, and find the total mass in gas and dark matter particles contained in it:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad = ds.all_data()\n", "\n", "# total_mass returns a list, representing the total gas and dark matter + stellar mass, respectively\n", "print([tm.in_units(\"Msun\") for tm in ad.quantities.total_mass()])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's say we want to zoom in on the box (since clearly the bounding we chose initially is much larger than the volume containing the gas particles!), and center on wherever the highest gas density peak is. First, let's find this peak:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "density = ad[\"PartType0\", \"density\"]\n", "wdens = np.where(density == np.max(density))\n", "coordinates = ad[\"PartType0\", \"Coordinates\"]\n", "center = coordinates[wdens][0]\n", "print(\"center = \", center)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set up the box to zoom into" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "new_box_size = ds.quan(250, \"code_length\")\n", "\n", "left_edge = center - new_box_size / 2\n", "right_edge = center + new_box_size / 2\n", "\n", "print(new_box_size.in_units(\"Mpc\"))\n", "print(left_edge.in_units(\"Mpc\"))\n", "print(right_edge.in_units(\"Mpc\"))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad2 = ds.region(center=center, left_edge=left_edge, right_edge=right_edge)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this new data object, let's confirm that we're only looking at a subset of the domain by first calculating the total mass in gas and particles contained in the subvolume:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print([tm.in_units(\"Msun\") for tm in ad2.quantities.total_mass()])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then by visualizing what the new zoomed region looks like" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "px = yt.ProjectionPlot(ds, \"x\", (\"gas\", \"density\"), center=center, width=new_box_size)\n", "px.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool - there's a disk galaxy there!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/cookbook/yt_gadget_owls_analysis.ipynb000066400000000000000000000122071510711153200265100ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading Gadget OWLS Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first thing you will need to run these examples is a working installation of yt. The author or these examples followed the instructions under \"Get yt: from source\" at https://yt-project.org/ to install an up to date development version of yt.\n", "\n", "We will be working with an OWLS snapshot: snapshot_033" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we will tell the notebook that we want figures produced inline. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we will load the snapshot." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load_sample(\"snapshot_033\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set a ``YTRegion`` that contains all the data." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad = ds.all_data()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inspecting " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dataset can tell us what fields it knows about, " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds.field_list" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds.derived_field_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the ion fields follow the naming convention described in YTEP-0003 http://ytep.readthedocs.org/en/latest/YTEPs/YTEP-0003.html#molecular-and-atomic-species-names" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Accessing Particle Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The raw particle data can be accessed using the particle types. This corresponds directly with what is in the hdf5 snapshots. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad[\"PartType0\", \"Coordinates\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad[\"PartType4\", \"IronFromSNIa\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad[\"PartType1\", \"ParticleIDs\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad[\"PartType0\", \"Hydrogen\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Projection Plots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The projection plots make use of derived fields that store the smoothed particle data (particles smoothed onto an oct-tree). Below we make a projection of all hydrogen gas followed by only the neutral hydrogen gas. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pz = yt.ProjectionPlot(ds, \"z\", (\"gas\", \"H_density\"))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pz.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pz = yt.ProjectionPlot(ds, \"z\", (\"gas\", \"H_p0_density\"))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pz.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/cookbook/zoomin_frames.py000066400000000000000000000023351510711153200237520ustar00rootroot00000000000000import numpy as np import yt # load data fn = "IsolatedGalaxy/galaxy0030/galaxy0030" ds = yt.load(fn) # This is the number of frames to make -- below, you can see how this is used. n_frames = 5 # This is the minimum size in smallest_dx of our last frame. # Usually it should be set to something like 400, but for THIS # dataset, we actually don't have that great of resolution. min_dx = 40 frame_template = "frame_%05i" # Template for frame filenames p = yt.SlicePlot(ds, "z", ("gas", "density")) # Add our slice, along z p.annotate_contour(("gas", "temperature")) # We'll contour in temperature # What we do now is a bit fun. "enumerate" returns a tuple for every item -- # the index of the item, and the item itself. This saves us having to write # something like "i = 0" and then inside the loop "i += 1" for ever loop. The # argument to enumerate is the 'logspace' function, which takes a minimum and a # maximum and the number of items to generate. It returns 10^power of each # item it generates. for i, v in enumerate( np.logspace(0, np.log10(ds.index.get_smallest_dx() * min_dx), n_frames) ): # We set our width as necessary for this frame p.set_width(v, "unitary") # save p.save(frame_template % (i)) yt-project-yt-f043ac8/doc/source/developing/000077500000000000000000000000001510711153200210535ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/developing/building_the_docs.rst000066400000000000000000000217631510711153200252630ustar00rootroot00000000000000.. _documentation: Documentation ============= .. _writing_documentation: How to Write Documentation -------------------------- Writing documentation is one of the most important but often overlooked tasks for increasing yt's impact in the community. It is the way in which the world will understand how to use our code, so it needs to be done concisely and understandably. Typically, when a developer submits some piece of code with new functionality, the developer should also include documentation on how to use that functionality (as per :ref:`requirements-for-code-submission`). Depending on the nature of the code addition, this could be a new narrative docs section describing how the new code works and how to use it, it could include a recipe in the cookbook section, or it could simply be adding a note in the relevant docs text somewhere. The documentation exists in the main code repository for yt in the ``doc`` directory (i.e. ``$YT_GIT/doc/source`` where ``$YT_GIT`` is the path of the yt git repository). It is organized hierarchically into the main categories of: * Visualizing * Analyzing * Analysis Modules * Examining * Cookbook * Quickstart * Developing * Reference * FAQ * Help You will have to figure out where your new/modified doc fits into this, but browsing through the existing documentation is a good way to sort that out. All the source for the documentation is written in `Sphinx `_, which uses ReST for markup. ReST is very straightforward to markup in a text editor, and if you are new to it, we recommend just using other .rst files in the existing yt documentation as templates or checking out the `ReST reference documentation `_. New cookbook recipes (see :ref:`cookbook`) are very helpful for the community as they provide simple annotated recipes on how to use specific functionality. To add one, create a concise Python script which demonstrates some functionality and pare it down to its minimum. Add some comment lines to describe what it is that you're doing along the way. Place this ``.py`` file in the ``source/cookbook/`` directory, and then link to it explicitly in one of the relevant ``.rst`` files in that directory (e.g. ``complex_plots.rst``, etc.), and add some description of what the script actually does. We recommend that you use one of the `sample data sets `_ in your recipe. When the full docs are built, each of the cookbook recipes is executed dynamically on a system which has access to all of the sample datasets. Any output images generated by your script will then be attached inline in the built documentation directly following your script. After you have made your modifications to the docs, you will want to make sure that they render the way you expect them to render. For more information on this, see the section on :ref:`docs_build`. Unless you're contributing cookbook recipes or notebooks which require a dynamic build, you can probably get away with just doing a 'quick' docs build. When you have completed your documentation additions, commit your changes to your repository and make a pull request in the same way you would contribute a change to the codebase, as described in the section on :ref:`sharing-changes`. .. _docs_build: Building the Documentation -------------------------- The yt documentation makes heavy use of the Sphinx documentation automation suite. Sphinx, written in Python, was originally created for the documentation of the Python project and has many nice capabilities for managing the documentation of Python code. While much of the yt documentation is static text, we make heavy use of cross-referencing with API documentation that is automatically generated at build time by Sphinx. We also use Sphinx to run code snippets (e.g. the cookbook and the notebooks) and embed resulting images and example data. Essential tools for building the docs can be installed alongside yt itself. From the top level of a local copy, run .. code-block:: bash $ python -m pip install -e . -r requirements/docs.txt Quick versus Full Documentation Builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Building the entire set of yt documentation is a laborious task, since you need to have a large number of packages in order to successfully execute and render all of the notebooks and yt recipes drawing from every corner of the yt source. As a quick alternative, one can do a ``quick`` build of the documentation, which eschews the need for downloading all of these dependencies, but it only produces the static docs. The static docs do not include the cookbook outputs and the notebooks, but this is good enough for most cases of people testing out whether or not their documentation contributions look OK before submitting them to the yt repository. If you want to create the full documentation locally, then you'll need to follow the instructions for building the ``full`` docs, so that you can dynamically execute and render the cookbook recipes, the notebooks, etc. Building the Docs (Quick) ^^^^^^^^^^^^^^^^^^^^^^^^^ In order to tell Sphinx not to do all of the dynamic building, you must set the ``$READTHEDOCS`` environment variable to be ``True`` by run from the command line (using bash syntax for example), as .. code-block:: bash export READTHEDOCS=True This variable is set for automated builds on the free ReadTheDocs service but can be used by anyone to force a quick, minimal build. Now all you need to do is execute Sphinx on the yt doc source. Go to the documentation directory and build the docs: .. code-block:: bash cd $YT_GIT/doc make html This will produce an html version of the documentation locally in the ``$YT_GIT/doc/build/html`` directory. You can now go there and open up ``index.html`` or whatever file you wish in your web browser. Building the Docs (Full) ^^^^^^^^^^^^^^^^^^^^^^^^ As alluded to earlier, building the full documentation is a bit more involved than simply building the static documentation. The full documentation makes heavy use of custom Sphinx extensions to transform recipes, notebooks, and inline code snippets into Python scripts, IPython_ notebooks, or notebook cells that are executed when the docs are built. To do this, we use Jupyter's nbconvert module to transform notebooks into HTML. to simplify versioning of the notebook JSON format, we store notebooks in an unevaluated state. To build the full documentation, you will need yt, jupyter, and all dependencies needed for yt's analysis modules installed. The following dependencies were used to generate the yt documentation during the release of yt 3.2 in 2015. * Sphinx_ 1.3.1 * Jupyter 1.0.0 * RunNotebook 0.1 * pandoc_ 1.13.2 * Rockstar halo finder 0.99.6 * SZpack_ 1.1.1 * ffmpeg_ 2.7.1 (compiled with libvpx support) * Astropy_ 0.4.4 .. _SZpack: http://www.jb.man.ac.uk/~jchluba/Science/SZpack/SZpack.html .. _Astropy: https://www.astropy.org/ .. _Sphinx: http://www.sphinx-doc.org/en/master/ .. _pandoc: https://pandoc.org/ .. _ffmpeg: http://www.ffmpeg.org/ .. _IPython: https://ipython.org/ You will also need the full yt suite of `yt test data `_, including the larger datasets that are not used in the answer tests. You will need to ensure that your testing configuration is properly configured and that all of the yt test data is in the testing directory. See :ref:`run_answer_testing` for more details on how to set up the testing configuration. Now that you have everything set up properly, go to the documentation directory and build it using Sphinx: .. code-block:: bash cd $YT_GIT/doc make html If all of the dependencies are installed and all of the test data is in the testing directory, this should churn away for a while (several hours) and eventually generate a docs build. We suggest setting :code:`suppress_stream_logging = True` in your yt configuration (See :ref:`configuration-file`) to suppress large amounts of debug output from yt. To clean the docs build, use :code:`make clean`. Building the Docs (Hybrid) ^^^^^^^^^^^^^^^^^^^^^^^^^^ It's also possible to create a custom Sphinx build that builds a restricted set of notebooks or scripts. This can be accomplished by editing the Sphinx :code:`conf.py` file included in the :code:`source` directory at the top level of the docs. The extensions included in the build are contained in the :code:`extensions` list. To disable an extension, simply remove it from the list. Doing so will raise a warning when Sphinx encounters the directive in the docs and will prevent Sphinx from evaluating the directive. As a concrete example, if one wanted to include the :code:`notebook`, and :code:`notebook-cell` directives, but not the :code:`python-script` or :code:`autosummary` directives, one would just need to comment out the lines that append these extensions to the :code:`extensions` list. The resulting docs build will be significantly quicker since it would avoid executing the lengthy API autodocumentation as well as a large number of Python script snippets in the narrative docs. yt-project-yt-f043ac8/doc/source/developing/creating_datatypes.rst000066400000000000000000000051141510711153200254600ustar00rootroot00000000000000.. _creating-objects: Creating Data Objects ===================== The three-dimensional datatypes in yt follow a fairly simple protocol. The basic principle is that if you want to define a region in space, that region must be identifiable from some sort of cut applied against the cells -- typically, in yt, this is done by examining the geometry. Creating a new data object requires modifications to two different files, one of which is in Python and the other in Cython. First, a subclass of :class:`~yt.data_objects.data_containers.YTDataContainer` must be defined; typically you actually want to subclass one of: :class:`~yt.data_objects.data_containers.YTSelectionContainer0D` :class:`~yt.data_objects.data_containers.YTSelectionContainer1D` :class:`~yt.data_objects.data_containers.YTSelectionContainer2D` :class:`~yt.data_objects.data_containers.YTSelectionContainer3D`. The following attributes must be defined: * ``_type_name`` - this is the short name by which the object type will be known as. Remember this for later, as we will have to use it when defining the underlying selector. * ``_con_args`` - this is the set of arguments passed to the object, and their names as attributes on the data object. * ``_container_fields`` - any fields that are generated by the object, rather than by another derived field in yt. The rest of the object can be defined in Cython, in the file ``yt/geometry/selection_routines.pyx``. You must define a subclass of ``SelectorObject``, which will require implementation of the following methods: * ``fill_mask`` - this takes a grid object and fills a mask of which zones should be included. It must take into account the child mask of the grid. * ``select_cell`` - this routine accepts a position and a width, and returns either zero or one for whether or not that cell is included in the selector. * ``select_sphere`` - this routine returns zero or one whether a sphere (point and radius) is included in the selector. * ``select_point`` - this identifies whether or not a point is included in the selector. It should be identical to selecting a cell or a sphere with zero extent. * ``select_bbox`` - this returns whether or not a bounding box (i.e., grid) is included in the selector. * ``_hash_vals`` - this must return some combination of parameters that semi-uniquely identifies the selector. Once the object has been defined, it must then be aliased within ``selection_routines.pyx`` as ``typename_selector``. For instance, ``ray_selector`` or ``sphere_selector`` for ``_type_name`` values of ``ray`` and ``sphere``, respectively. yt-project-yt-f043ac8/doc/source/developing/creating_derived_fields.rst000066400000000000000000000377111510711153200264420ustar00rootroot00000000000000.. _creating-derived-fields: Creating Derived Fields ======================= One of the more powerful means of extending yt is through the usage of derived fields. These are fields that describe a value at each cell in a simulation. Defining a New Field -------------------- Once a new field has been conceived of, the best way to create it is to construct a function that performs an array operation -- operating on a collection of data, neutral to its size, shape, and type. A simple example of this is the pressure field, which demonstrates the ease of this approach. .. code-block:: python import yt def _pressure(field, data): return ( (data.ds.gamma - 1.0) * data["gas", "density"] * data["gas", "specific_thermal_energy"] ) Note that we do a couple different things here. We access the ``gamma`` parameter from the dataset, we access the ``density`` field and we access the ``specific_thermal_energy`` field. ``specific_thermal_energy`` is, in fact, another derived field! We don't do any loops, we don't do any type-checking, we can simply multiply the three items together. In this example, the ``density`` field will return data with units of ``g/cm**3`` and the ``specific_thermal_energy`` field will return data units of ``erg/g``, so the result will automatically have units of pressure, ``erg/cm**3``. This assumes the unit system is set to the default, which is CGS: if a different unit system is selected, the result will be in the same dimensions of pressure but different units. See :ref:`units` for more information. Once we've defined our function, we need to notify yt that the field is available. The :func:`add_field` function is the means of doing this; it has a number of fairly specific parameters that can be passed in, but here we'll only look at the most basic ones needed for a simple scalar baryon field. .. note:: There are two different :func:`add_field` functions. For the differences, see :ref:`faq-add-field-diffs`. .. code-block:: python yt.add_field( name=("gas", "pressure"), function=_pressure, sampling_type="local", units="dyne/cm**2", ) We feed it the name of the field, the name of the function, the sampling type, and the units. The ``sampling_type`` keyword determines which elements are used to make the field (i.e., grid cell or particles) and controls how volume is calculated. It can be set to "cell" for grid/mesh fields, "particle" for particle and SPH fields, or "local" to use the primary format of the loaded dataset. In most cases, "local" is sufficient, but "cell" and "particle" can be used to specify the source for datasets that have both grids and particles. In a dataset with both grids and particles, using "cell" will ensure a field is created with a value for every grid cell, while using "particle" will result in a field with a value for every particle. The units parameter is a "raw" string, in the format that yt uses in its :ref:`symbolic units implementation ` (e.g., employing only unit names, numbers, and mathematical operators in the string, and using ``"**"`` for exponentiation). For cosmological datasets and fields, see :ref:`cosmological-units `. We suggest that you name the function that creates a derived field with the intended field name prefixed by a single underscore, as in the ``_pressure`` example above. Field definitions return array data with units. If the field function returns data in a dimensionally equivalent unit (e.g. a ``"dyne"`` versus a ``"N"``), the field data will be converted to the units specified in ``add_field`` before being returned in a data object selection. If the field function returns data with dimensions that are incompatible with units specified in ``add_field``, you will see an error. To clear this error, you must ensure that your field function returns data in the correct units. Often, this means applying units to a dimensionless float or array. If your field definition includes physical constants rather than defining a constant as a float, you can import it from ``yt.units`` to get a predefined version of the constant with the correct units. If you know the units your data is supposed to have ahead of time, you can also import unit symbols like ``g`` or ``cm`` from the ``yt.units`` namespace and multiply the return value of your field function by the appropriate combination of unit symbols for your field's units. You can also convert floats or NumPy arrays into :class:`~yt.units.yt_array.YTArray` or :class:`~yt.units.yt_array.YTQuantity` instances by making use of the :func:`~yt.data_objects.static_output.Dataset.arr` and :func:`~yt.data_objects.static_output.Dataset.quan` convenience functions. Lastly, if you do not know the units of your field ahead of time, you can specify ``units='auto'`` in the call to ``add_field`` for your field. This will automatically determine the appropriate units based on the units of the data returned by the field function. This is also a good way to let your derived fields be automatically converted to the units of the unit system in your dataset. If ``units='auto'`` is set, it is also required to set the ``dimensions`` keyword argument so that error-checking can be done on the derived field to make sure that the dimensionality of the returned array and the field are the same: .. code-block:: python import yt from yt.units import dimensions def _pressure(field, data): return ( (data.ds.gamma - 1.0) * data["gas", "density"] * data["gas", "specific_thermal_energy"] ) yt.add_field( ("gas", "pressure"), function=_pressure, sampling_type="local", units="auto", dimensions=dimensions.pressure, ) If ``dimensions`` is not set, an error will be thrown. The ``dimensions`` keyword can be a SymPy ``symbol`` object imported from ``yt.units.dimensions``, a compound dimension of these, or a string corresponding to one of these objects. :func:`add_field` can be invoked in two other ways. The first is by the function decorator :func:`derived_field`. The following code is equivalent to the previous example: .. code-block:: python from yt import derived_field @derived_field(name="pressure", sampling_type="cell", units="dyne/cm**2") def _pressure(field, data): return ( (data.ds.gamma - 1.0) * data["gas", "density"] * data["gas", "specific_thermal_energy"] ) The :func:`derived_field` decorator takes the same arguments as :func:`add_field`, and is often a more convenient shorthand in cases where you want to quickly set up a new field. Defining derived fields in the above fashion must be done before a dataset is loaded, in order for the dataset to recognize it. If you want to set up a derived field after you have loaded a dataset, or if you only want to set up a derived field for a particular dataset, there is an :func:`~yt.data_objects.static_output.Dataset.add_field` method that hangs off dataset objects. The calling syntax is the same: .. code-block:: python ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0100") ds.add_field( ("gas", "pressure"), function=_pressure, sampling_type="cell", units="dyne/cm**2", ) If you specify fields in this way, you can take advantage of the dataset's unit system to define the units for you, so that the units will be returned in the units of that system: .. code-block:: python ds.add_field( ("gas", "pressure"), function=_pressure, sampling_type="cell", units=ds.unit_system["pressure"], ) Since the :class:`yt.units.unit_systems.UnitSystem` object returns a :class:`yt.units.unit_object.Unit` object when queried, you're not limited to specifying units in terms of those already available. You can specify units for fields using basic arithmetic if necessary: .. code-block:: python ds.add_field( ("gas", "my_acceleration"), function=_my_acceleration, sampling_type="cell", units=ds.unit_system["length"] / ds.unit_system["time"] ** 2, ) If you find yourself using the same custom-defined fields over and over, you should put them in your plugins file as described in :ref:`plugin-file`. A More Complicated Example -------------------------- But what if we want to do something a bit more fancy? Here's an example of getting parameters from the data object and using those to define the field; specifically, here we obtain the ``center`` and ``bulk_velocity`` parameters and use those to define a field for radial velocity (there is already a ``radial_velocity`` field in yt, but we create this one here just as a transparent and simple example). .. code-block:: python import numpy as np from yt.fields.api import ValidateParameter def _my_radial_velocity(field, data): if data.has_field_parameter("bulk_velocity"): bv = data.get_field_parameter("bulk_velocity").in_units("cm/s") else: bv = data.ds.arr(np.zeros(3), "cm/s") xv = data["gas", "velocity_x"] - bv[0] yv = data["gas", "velocity_y"] - bv[1] zv = data["gas", "velocity_z"] - bv[2] center = data.get_field_parameter("center") x_hat = data["gas", "x"] - center[0] y_hat = data["gas", "y"] - center[1] z_hat = data["gas", "z"] - center[2] r = np.sqrt(x_hat * x_hat + y_hat * y_hat + z_hat * z_hat) x_hat /= r y_hat /= r z_hat /= r return xv * x_hat + yv * y_hat + zv * z_hat yt.add_field( ("gas", "my_radial_velocity"), function=_my_radial_velocity, sampling_type="cell", units="cm/s", take_log=False, validators=[ValidateParameter(["center", "bulk_velocity"])], ) Note that we have added a few optional arguments to ``yt.add_field``; we specify that we do not wish to display this field as logged, that we require both the ``bulk_velocity`` and ``center`` field parameters to be present in a given data object we wish to calculate this for, and we say that it should not be displayed in a drop-down box of fields to display. This is done through the parameter *validators*, which accepts a list of :class:`~yt.fields.derived_field.FieldValidator` objects. These objects define the way in which the field is generated, and when it is able to be created. In this case, we mandate that parameters ``center`` and ``bulk_velocity`` are set before creating the field. These are set via :meth:`~yt.data_objects.data_containers.set_field_parameter`, which can be called on any object that has fields: .. code-block:: python ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0100") sp = ds.sphere("max", (200.0, "kpc")) sp.set_field_parameter("bulk_velocity", yt.YTArray([-100.0, 200.0, 300.0], "km/s")) In this case, we already know what the ``center`` of the sphere is, so we do not set it. Also, note that ``center`` and ``bulk_velocity`` need to be :class:`~yt.units.yt_array.YTArray` objects with units. If you are writing a derived field that uses a field parameter that changes the behavior of the field depending on the value of the field parameter, you can make yt test to make sure the field handles all possible values for the field parameter using a special form of the ``ValidateParameter`` field validator. In particular, ``ValidateParameter`` supports an optional second argument, which takes a dictionary mapping from parameter names to parameter values that you would like yt to test. This is useful when a field will select different fields to access based on the value of a field parameter. This option allows you to force yt to select *all* needed dependent fields for your derived field definition at field detection time. This can avoid errors related to missing fields. For example, let's write a field that depends on a field parameter named ``'axis'``: .. code-block:: python def my_axis_field(field, data): axis = data.get_field_parameter("axis") if axis == 0: return data["gas", "velocity_x"] elif axis == 1: return data["gas", "velocity_y"] elif axis == 2: return data["gas", "velocity_z"] else: raise ValueError ds.add_field( "my_axis_field", function=my_axis_field, units="cm/s", validators=[ValidateParameter("axis", {"axis": [0, 1, 2]})], ) In this example, we've told yt's field system that the data object we are querying ``my_axis_field`` must have the ``axis`` field parameter set. In addition, it forces yt to recognize that this field might depend on any one of ``x-velocity``, ``y-velocity``, or ``z-velocity``. By specifying that ``axis`` might be 0, 1, or 2 in the ``ValidataParameter`` call, this ensures that this field will only be valid and available for datasets that have all three fields available. Other examples for creating derived fields can be found in the cookbook recipe :ref:`cookbook-simple-derived-fields`. .. _derived-field-options: Field Options ------------- The arguments to :func:`add_field` are passed on to the constructor of :class:`DerivedField`. There are a number of options available, but the only mandatory ones are ``name``, ``units``, and ``function``. ``name`` This is the name of the field -- how you refer to it. For instance, ``pressure`` or ``magnetic_field_strength``. ``function`` This is a function handle that defines the field ``units`` This is a string that describes the units, or a query to a UnitSystem object, e.g. ``ds.unit_system["energy"]``. Powers must be in Python syntax (``**`` instead of ``^``). Alternatively, it may be set to ``"auto"`` to have the units determined automatically. In this case, the ``dimensions`` keyword must be set to the correct dimensions of the field. ``display_name`` This is a name used in the plots, for instance ``"Divergence of Velocity"``. If not supplied, the ``name`` value is used. ``take_log`` This is *True* or *False* and describes whether the field should be logged when plotted. ``particle_type`` Is this field a *particle* field? ``validators`` (*Advanced*) This is a list of :class:`FieldValidator` objects, for instance to mandate spatial data. ``display_field`` (*Advanced*) Should this field appear in the dropdown box in Reason? ``not_in_all`` (*Advanced*) If this is *True*, the field may not be in all the grids. ``output_units`` (*Advanced*) For fields that exist on disk, which we may want to convert to other fields or that get aliased to themselves, we can specify a different desired output unit than the unit found on disk. ``force_override`` (*Advanced*) Overrides the definition of an old field if a field with the same name has already been defined. ``dimensions`` Set this if ``units="auto"``. Can be either a string or a dimension object from ``yt.units.dimensions``. Debugging a Derived Field ------------------------- If your derived field is not behaving as you would like, you can insert a call to ``data._debug()`` to spawn an interactive interpreter whenever that line is reached. Note that this is slightly different from calling ``pdb.set_trace()``, as it will *only* trigger when the derived field is being called on an actual data object, rather than during the field detection phase. The starting position will be one function lower in the stack than you are likely interested in, but you can either step through back to the derived field function, or simply type ``u`` to go up a level in the stack. For instance, if you had defined this derived field: .. code-block:: python @yt.derived_field(name=("gas", "funthings")) def funthings(field, data): return data["sillythings"] + data["humorousthings"] ** 2.0 And you wanted to debug it, you could do: .. code-block:: python @yt.derived_field(name=("gas", "funthings")) def funthings(field, data): data._debug() return data["sillythings"] + data["humorousthings"] ** 2.0 And now, when that derived field is actually used, you will be placed into a debugger. yt-project-yt-f043ac8/doc/source/developing/creating_frontend.rst000066400000000000000000000402371510711153200253060ustar00rootroot00000000000000.. _creating_frontend: Creating A New Code Frontend ============================ yt is designed to support analysis and visualization of data from multiple different simulation codes. For a list of codes and the level of support they enjoy, see :ref:`code-support`. We'd like to support a broad range of codes, both Adaptive Mesh Refinement (AMR)-based and otherwise. To add support for a new code, a few things need to be put into place. These necessary structures can be classified into a couple categories: * Data meaning: This is the set of parameters that convert the data into physically relevant units; things like spatial and mass conversions, time units, and so on. * Data localization: These are structures that help make a "first pass" at data loading. Essentially, we need to be able to make a first pass at guessing where data in a given physical region would be located on disk. With AMR data, this is typically quite easy: the grid patches are the "first pass" at localization. * Data reading: This is the set of routines that actually perform a read of either all data in a region or a subset of that data. Note that a frontend can be built as an external package. This is useful to develop and maintain a maturing frontend at your own pace. For technical details, see :ref:`frontends-as-extensions`. If you are interested in adding a new code, be sure to drop us a line on `yt-dev `_! Bootstrapping a new frontend ---------------------------- To get started * make a new directory in ``yt/frontends`` with the name of your code and add the name into ``yt/frontends/api.py:_frontends`` (in alphabetical order). * copy the contents of the ``yt/frontends/_skeleton`` directory, and replace every occurrence of ``Skeleton`` with your frontend's name (preserving case). This adds a lot of boilerplate for the required classes and methods that are needed. Data Meaning Structures ----------------------- You will need to create a subclass of ``Dataset`` in the ``data_structures.py`` file. This subclass will need to handle conversion between the different physical units and the code units (typically in the ``_set_code_unit_attributes()`` method), read in metadata describing the overall data on disk (via the ``_parse_parameter_file()`` method), and provide a ``classmethod`` called ``_is_valid()`` that lets the ``yt.load`` method help identify an input file as belonging to *this* particular ``Dataset`` subclass (see :ref:`data-format-detection`). For the most part, the examples of ``yt.frontends.amrex.data_structures.OrionDataset`` and ``yt.frontends.enzo.data_structures.EnzoDataset`` should be followed, but ``yt.frontends.chombo.data_structures.ChomboDataset``, as a slightly newer addition, can also be used as an instructive example. A new set of fields must be added in the file ``fields.py`` in your new directory. For the most part this means subclassing ``FieldInfoContainer`` and adding the necessary fields specific to your code. Here is a snippet from the base BoxLib field container (defined in ``yt.frontends.amrex.fields``): .. code-block:: python from yt.fields.field_info_container import FieldInfoContainer class BoxlibFieldInfo(FieldInfoContainer): known_other_fields = ( ("density", (rho_units, ["density"], None)), ("eden", (eden_units, ["energy_density"], None)), ("xmom", (mom_units, ["momentum_x"], None)), ("ymom", (mom_units, ["momentum_y"], None)), ("zmom", (mom_units, ["momentum_z"], None)), ("temperature", ("K", ["temperature"], None)), ("Temp", ("K", ["temperature"], None)), ("x_velocity", ("cm/s", ["velocity_x"], None)), ("y_velocity", ("cm/s", ["velocity_y"], None)), ("z_velocity", ("cm/s", ["velocity_z"], None)), ("xvel", ("cm/s", ["velocity_x"], None)), ("yvel", ("cm/s", ["velocity_y"], None)), ("zvel", ("cm/s", ["velocity_z"], None)), ) known_particle_fields = ( ("particle_mass", ("code_mass", [], None)), ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_momentum_x", (mom_units, [], None)), ("particle_momentum_y", (mom_units, [], None)), ("particle_momentum_z", (mom_units, [], None)), ("particle_angmomen_x", ("code_length**2/code_time", [], None)), ("particle_angmomen_y", ("code_length**2/code_time", [], None)), ("particle_angmomen_z", ("code_length**2/code_time", [], None)), ("particle_id", ("", ["particle_index"], None)), ("particle_mdot", ("code_mass/code_time", [], None)), ) The tuples, ``known_other_fields`` and ``known_particle_fields`` contain entries, which are tuples of the form ``("name", ("units", ["fields", "to", "alias"], "display_name"))``. ``"name"`` is the name of a field stored on-disk in the dataset. ``"units"`` corresponds to the units of that field. The list ``["fields", "to", "alias"]`` allows you to specify additional aliases to this particular field; for example, if your on-disk field for the x-direction velocity were ``"x-direction-velocity"``, maybe you'd prefer to alias to the more terse name of ``"xvel"``. By convention in yt we use a set of "universal" fields. Currently these fields are enumerated in the stream frontend. If you take a look at ``yt/frontends/stream/fields.py``, you will see a listing of fields following the format described above with field names that will be recognized by the rest of the built-in yt field system. In the example from the boxlib frontend above many of the fields in the ``known_other_fields`` tuple follow this convention. If you would like your frontend to mesh nicely with the rest of yt's built-in fields, it is probably a good idea to alias your frontend's field names to the yt "universal" field names. Finally, "display_name"`` is an optional parameter that can be used to specify how you want the field to be displayed on a plot; this can be LaTeX code, for example the density field could have a display name of ``r"\rho"``. Omitting the ``"display_name"`` will result in using a capitalized version of the ``"name"``. .. _data-format-detection: How to make ``yt.load`` magically detect your data format ? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``yt.load`` takes in a file or directory name, as well as any number of positional and keyword arguments. On call, ``yt.load`` attempts to determine what ``Dataset`` subclasses are compatible with the set of arguments it received. It does so by passing its arguments to *every* ``Dataset`` subclasses' ``_is_valid`` method. These methods are intended to be heuristics that quickly determine whether the arguments (in particular the file/directory) can be loaded with their respective classes. In some cases, more than one class might be detected as valid. If all candidate classes are siblings, ``yt.load`` will select the most specialized one. When writing a new frontend, it is important to write ``_is_valid`` methods to be as specific as possible, otherwise one might constrain the design space for future frontends or in some cases deny their ability to leverage ``yt.load``'s magic. Performance is also critical since the new method is going to get called every single time along with ``yt.load``, even for unrelated data formats. Note that ``yt.load`` knows about every ``Dataset`` subclass because they are automatically registered on creation. .. _bfields-frontend: Creating Aliases for Magnetic Fields ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setting up access to the magnetic fields in your dataset requires special handling, because in different unit systems magnetic fields have different dimensions (see :ref:`bfields` for an explanation). If your dataset includes magnetic fields, you should include them in ``known_other_fields``, but do not set up aliases for them--instead use the special handling function :meth:`~yt.fields.magnetic_fields.setup_magnetic_field_aliases`. It takes as arguments the ``FieldInfoContainer`` instance, the field type of the frontend, and the list of magnetic fields from the frontend. Here is an example of how this is implemented in the FLASH frontend: .. code-block:: python class FLASHFieldInfo(FieldInfoContainer): known_other_fields = ( ("magx", (b_units, [], "B_x")), # Note there is no alias here ("magy", (b_units, [], "B_y")), ("magz", (b_units, [], "B_z")), ..., ) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases ... setup_magnetic_field_aliases(self, "flash", ["mag%s" % ax for ax in "xyz"]) This function should always be imported and called from within the ``setup_fluid_fields`` method of the ``FieldInfoContainer``. If this function is used, converting between magnetic fields in different unit systems will be handled automatically. Data Localization Structures ---------------------------- These functions and classes let yt know about how the arrangement of data on disk corresponds to the physical arrangement of data within the simulation. yt has grid datastructures for handling both patch-based and octree-based AMR codes. The terms 'patch-based' and 'octree-based' are used somewhat loosely here. For example, traditionally, the FLASH code used the paramesh AMR library, which is based on a tree structure, but the FLASH frontend in yt utilizes yt's patch-based datastructures. It is up to the frontend developer to determine which yt datastructures best match the datastructures of their simulation code. Both approaches -- patch-based and octree-based -- have a concept of a *Hierarchy* or *Index* (used somewhat interchangeably in the code) of datastructures and something that describes the elements that make up the Hierarchy or Index. For patch-based codes, the Index is a collection of ``AMRGridPatch`` objects that describe a block of zones. For octree-based codes, the Index contains datastructures that hold information about the individual octs, namely an ``OctreeContainer``. Hierarchy or Index ^^^^^^^^^^^^^^^^^^ To set up data localization, a ``GridIndex`` subclass for patch-based codes or an ``OctreeIndex`` subclass for octree-based codes must be added in the file ``data_structures.py``. Examples of these different types of ``Index`` can be found in, for example, the ``yt.frontends.chombo.data_structures.ChomboHierarchy`` for patch-based codes and ``yt.frontends.ramses.data_structures.RAMSESIndex`` for octree-based codes. For the most part, the ``GridIndex`` subclass must override (at a minimum) the following methods: * ``_detect_output_fields()``: ``self.field_list`` must be populated as a list of strings corresponding to "native" fields in the data files. * ``_count_grids()``: this must set ``self.num_grids`` to be the total number of grids (equivalently ``AMRGridPatch``'es) in the simulation. * ``_parse_index()``: this must fill in ``grid_left_edge``, ``grid_right_edge``, ``grid_particle_count``, ``grid_dimensions`` and ``grid_levels`` with the appropriate information. Each of these variables is an array, with an entry for each of the ``self.num_grids`` grids. Additionally, ``grids`` must be an array of ``AMRGridPatch`` objects that already know their IDs. * ``_populate_grid_objects()``: this initializes the grids by calling ``_prepare_grid()`` and ``_setup_dx()`` on all of them. Additionally, it should set up ``Children`` and ``Parent`` lists on each grid object. The ``OctreeIndex`` has somewhat analogous methods, but often with different names; both ``OctreeIndex`` and ``GridIndex`` are subclasses of the ``Index`` class. In particular, for the ``OctreeIndex``, the method ``_initialize_oct_handler()`` setups up much of the oct metadata that is analogous to the grid metadata created in the ``GridIndex`` methods ``_count_grids()``, ``_parse_index()``, and ``_populate_grid_objects()``. Grids ^^^^^ .. note:: This section only applies to the approach using yt's patch-based datastructures. For the octree-based approach, one does not create a grid object, but rather an ``OctreeSubset``, which has methods for filling out portions of the octree structure. Again, see the code in ``yt.frontends.ramses.data_structures`` for an example of the octree approach. A new grid object, subclassing ``AMRGridPatch``, will also have to be added in ``data_structures.py``. For the most part, this may be all that is needed: .. code-block:: python class ChomboGrid(AMRGridPatch): _id_offset = 0 __slots__ = ["_level_id"] def __init__(self, id, index, level=-1): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level Even one of the more complex grid objects, ``yt.frontends.amrex.BoxlibGrid``, is still relatively simple. Data Reading Functions ---------------------- In ``io.py``, there are a number of IO handlers that handle the mechanisms by which data is read off disk. To implement a new data reader, you must subclass ``BaseIOHandler``. The various frontend IO handlers are stored in an IO registry - essentially a dictionary that uses the name of the frontend as a key, and the specific IO handler as a value. It is important, therefore, to set the ``dataset_type`` attribute of your subclass, which is what is used as the key in the IO registry. For example: .. code-block:: python class IOHandlerBoxlib(BaseIOHandler): _dataset_type = "boxlib_native" ... At a minimum, one should also override the following methods * ``_read_fluid_selection()``: this receives a collection of data "chunks", a selector describing which "chunks" you are concerned with, a list of fields, and the size of the data to read. It should create and return a dictionary whose keys are the fields, and whose values are numpy arrays containing the data. The data should actually be read via the ``_read_chunk_data()`` method. * ``_read_chunk_data()``: this method receives a "chunk" of data along with a list of fields we want to read. It loops over all the grid objects within the "chunk" of data and reads from disk the specific fields, returning a dictionary whose keys are the fields and whose values are numpy arrays of the data. If your dataset has particle information, you'll want to override the ``_read_particle_coords()`` and ``read_particle_fields()`` methods as well. Each code is going to read data from disk in a different fashion, but the ``yt.frontends.amrex.io.IOHandlerBoxlib`` is a decent place to start. And that just about covers it. Please feel free to email `yt-users `_ or `yt-dev `_ with any questions, or to let us know you're thinking about adding a new code to yt. How to add extra dependencies ? ------------------------------- .. note:: This section covers the technical details of how optional runtime dependencies are implemented and used in yt. If your frontend has specific or complicated dependencies other than yt's, we advise writing your frontend as an extension package :ref:`frontends-as-extensions` It is required that a specific target be added to ``pyproject.toml`` to define a list of additional requirements (even if empty), see :ref:`install-additional`. At runtime, extra third party dependencies should be loaded lazily, meaning their import needs to be delayed until actually needed. This is achieved by importing a wrapper from ``yt.utitilies.on_demand_imports.py``, instead of the actual package like so .. code-block:: python from yt.utilities.on_demand_imports import _mypackage as mypackage Such import statements can live at the top of a module without generating overhead or errors in case the actual package isn't installed. If the extra third party dependency is new, a new import wrapper must also be added. To do so, follow the example of the existing wrappers in ``yt.utilities.on_demand_imports.py``. yt-project-yt-f043ac8/doc/source/developing/debugdrive.rst000066400000000000000000000104421510711153200237260ustar00rootroot00000000000000.. _debug-drive: Debugging yt ============ There are several different convenience functions that allow you to control yt in perhaps unexpected and unorthodox manners. These will allow you to conduct in-depth debugging of processes that may be running in parallel on multiple processors, as well as providing a mechanism of signalling to yt that you need more information about a running process. Additionally, yt has a built-in mechanism for optional reporting of errors to a central server. All of these allow for more rapid development and debugging of any problems you might encounter. Additionally, yt is able to leverage existing developments in the IPython community for parallel, interactive analysis. This allows you to initialize multiple yt processes through ``mpirun`` and interact with all of them from a single, unified interactive prompt. This enables and facilitates parallel analysis without sacrificing interactivity and flexibility. .. _pastebin: Pastebin -------- A pastebin is a website where you can easily copy source code and error messages to share with yt developers or your collaborators. At http://paste.yt-project.org/ a pastebin is available for placing scripts. With yt the script ``yt_lodgeit.py`` is distributed and wrapped with the ``pastebin`` and ``pastebin_grab`` commands, which allow for commandline uploading and downloading of pasted snippets. To upload a script you would supply it to the command: .. code-block:: bash $ yt pastebin some_script.py The URL will be returned. If you'd like it to be marked 'private' and not show up in the list of pasted snippets, supply the argument ``--private``. All snippets are given either numbers or hashes. To download a pasted snippet, you would use the ``pastebin_grab`` option: .. code-block:: bash $ yt pastebin_grab 1768 The snippet will be output to the window, so output redirection can be used to store it in a file. Use the Python Debugger ----------------------- yt is almost entirely composed of python code, so it makes sense to use the `python debugger`_ as your first stop in trying to debug it. .. _python debugger: https://docs.python.org/3/library/pdb.html Signaling yt to Do Something ---------------------------- During startup, yt inserts handlers for two operating system-level signals. These provide two diagnostic methods for interacting with a running process. Signalling the python process that is running your script with these signals will induce the requested behavior. SIGUSR1 This will cause the python code to print a stack trace, showing exactly where in the function stack it is currently executing. SIGUSR2 This will cause the python code to insert an IPython session wherever it currently is, with all local variables in the local namespace. It should allow you to change the state variables. If your yt-running process has PID 5829, you can signal it to print a traceback with: .. code-block:: bash $ kill -SIGUSR1 5829 Note, however, that if the code is currently inside a C function, the signal will not be handled, and the stacktrace will not be printed, until it returns from that function. .. _remote-debugging: Remote and Disconnected Debugging --------------------------------- If you are running a parallel job that fails, often it can be difficult to do a post-mortem analysis to determine what went wrong. To facilitate this, yt has implemented an `XML-RPC `_ interface to the Python debugger (``pdb``) event loop. Running with the ``--rpdb`` command will cause any uncaught exception during execution to spawn this interface, which will sit and wait for commands, exposing the full Python debugger. Additionally, a frontend to this is provided through the yt command. So if you run the command: .. code-block:: bash $ mpirun -np 4 python some_script.py --parallel --rpdb and it reaches an error or an exception, it will launch the debugger. Additionally, instructions will be printed for connecting to the debugger. Each of the four processes will be accessible via: .. code-block:: bash $ yt rpdb 0 where ``0`` here indicates the process 0. For security reasons, this will only work on local processes; to connect on a cluster, you will have to execute the command ``yt rpdb`` on the node on which that process was launched. yt-project-yt-f043ac8/doc/source/developing/deprecating_features.rst000066400000000000000000000104041510711153200257670ustar00rootroot00000000000000.. how-to-deprecate:: How to deprecate a feature -------------------------- Since the 4.0.0 release, deprecation happens on a per-release basis. A functionality can be marked as deprecated using ``~yt._maintenance.deprecation.issue_deprecation_warning``, which takes a warning message and two version numbers, indicating the earliest release deprecating the feature and the one in which it will be removed completely. The message should indicate a viable alternative to replace the deprecated feature at the user level. ``since`` and ``removal`` arguments should indicate in which release something was first deprecated, and when it's expected to be removed. While ``since`` is required, ``removal`` is optional. Here's an example call. .. code-block::python def old_function(*args, **kwargs): from yt._maintenance.deprecation import issue_deprecation_warning issue_deprecation_warning( "`old_function` is deprecated, use `replacement_function` instead." stacklevel=3, since="4.0", removal="4.1.0", ) ... If a whole function or class is marked as deprecated, it should be removed from ``doc/source/reference/api/api.rst``. Deprecating Derived Fields -------------------------- Occasionally, one may want to deprecate a derived field in yt, normally because naming conventions for fields have changed, or simply because a field has outlived its usefulness. There are two ways to mark fields as deprecated in yt. The first way is if you simply want to mark a specific derived field as deprecated. In that case, you call :meth:`~yt.fields.field_info_container.FieldInfoContainer.add_deprecated_field`: .. code-block:: python def _cylindrical_radial_absolute(field, data): """This field is deprecated and will be removed in a future version""" return np.abs(data[ftype, f"{basename}_cylindrical_radius"]) registry.add_deprecated_field( (ftype, f"cylindrical_radial_{basename}_absolute"), sampling_type="local", function=_cylindrical_radial_absolute, since="4.0", removal="4.1.0", units=field_units, validators=[ValidateParameter("normal")], ) Note that the signature for :meth:`~yt.fields.field_info_container.FieldInfoContainer.add_deprecated_field` is the same as :meth:`~yt.fields.field_info_container.FieldInfoContainer.add_field`, with the exception of the ``since`` and ``removal`` arguments which indicate in what version the field was deprecated and in what version it will be removed. The effect is to add a warning to the logger when the field is first used: .. code-block:: python import yt ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0100") sp = ds.sphere("c", (100.0, "kpc")) print(sp["gas", "cylindrical_radial_velocity_absolute"]) .. code-block:: pycon yt : [WARNING ] 2021-03-09 16:30:47,460 The Derived Field ('gas', 'cylindrical_radial_velocity_absolute') is deprecated as of yt v4.0.0 and will be removed in yt v4.1.0 The second way to deprecate a derived field is to take an existing field definition and change its name. In order to mark the original name as deprecated, use the :meth:`~yt.fields.field_info_container.FieldInfoContainer.alias` method and pass the ``since`` and ``removal`` arguments (see above) as a tuple in the ``deprecate`` keyword argument: .. code-block:: python registry.alias( (ftype, "kinetic_energy"), (ftype, "kinetic_energy_density"), deprecate=("4.0.0", "4.1.0"), ) Note that the old field name which is to be deprecated goes first, and the new, replacement field name goes second. In this case, the log message reports to the user what field they should use: .. code-block:: python print(sp["gas", "kinetic_energy"]) .. code-block:: pycon yt : [WARNING ] 2021-03-09 16:29:12,911 The Derived Field ('gas', 'kinetic_energy') is deprecated as of yt v4.0.0 and will be removed in yt v4.1.0 Use ('gas', 'kinetic_energy_density') instead. In most cases, the ``since`` and ``removal`` arguments should have a delta of one minor release, and that should be the minimum value. However, the developer is free to use their judgment about whether or not the delta should be multiple minor releases if the field has a long provenance. yt-project-yt-f043ac8/doc/source/developing/developing.rst000066400000000000000000000000471510711153200237420ustar00rootroot00000000000000.. include:: ../../../CONTRIBUTING.rst yt-project-yt-f043ac8/doc/source/developing/extensions.rst000066400000000000000000000062211510711153200240050ustar00rootroot00000000000000.. _extensions: Extension Packages ================== .. note:: For some additional discussion, see `YTEP-0029 `_, where this plan was designed. As of version 3.3 of yt, we have put into place new methods for easing the process of developing "extensions" to yt. Extensions might be analysis packages, visualization tools, or other software projects that use yt as a base engine but that are versioned, developed and distributed separately. This brings with it the advantage of retaining control over the versioning, contribution guidelines, scope, etc, while also providing a mechanism for disseminating information about it, and potentially a method of interacting with other extensions. We have created a few pieces of infrastructure for developing extensions, making them discoverable, and distributing them to collaborators. If you have a module you would like to retain some external control over, or that you don't feel would fit into yt, we encourage you to build it as an extension module and distribute and version it independently. Hooks for Extensions -------------------- Starting with version 3.3 of yt, any package named with the prefix ``yt_`` is importable from the namespace ``yt.extensions``. For instance, the ``yt_interaction`` package ( https://bitbucket.org/data-exp-lab/yt_interaction ) is importable as ``yt.extensions.interaction``. In subsequent versions, we plan to include in yt a catalog of known extensions and where to find them; this will put discoverability directly into the code base. .. _frontends-as-extensions: Frontends as extensions ----------------------- Starting with version 4.2 of yt, any externally installed package that exports :class:`~yt.data_objects.static_output.Dataset` subclass as an entrypoint in ``yt.frontends`` namespace in ``setup.py`` or ``pyproject.toml`` will be automatically loaded and immediately available in :func:`~yt.loaders.load`. To add an entrypoint in an external project's ``setup.py``: .. code-block:: python setup( # ..., entry_points={ "yt.frontends": [ "myFrontend = my_frontend.api.MyFrontendDataset", "myOtherFrontend = my_frontend.api.MyOtherFrontendDataset", ] } ) or ``pyproject.toml``: .. code-block:: toml [project.entry-points."yt.frontends"] myFrontend = "my_frontend.api:MyFrontendDataset" myOtherFrontend = "my_frontend.api:MyOtherFrontendDataset" Extension Template ------------------ A template for starting an extension module (or converting an existing set of code to an extension module) can be found at https://github.com/yt-project/yt_extension_template . To get started, download a zipfile of the template ( https://codeload.github.com/yt-project/yt_extension_template/zip/master ) and follow the directions in ``README.md`` to modify the metadata. Distributing Extensions ----------------------- We encourage you to version on your choice of hosting platform (Bitbucket, GitHub, etc), and to distribute your extension widely. We are presently working on deploying a method for listing extension modules on the yt webpage. yt-project-yt-f043ac8/doc/source/developing/external_analysis.rst000066400000000000000000000364621510711153200253450ustar00rootroot00000000000000.. _external-analysis-tools: Using yt with External Analysis Tools ===================================== yt can be used as a ``glue`` code between simulation data and other methods of analyzing data. Its facilities for understanding units, disk IO and data selection set it up ideally to use other mechanisms for analyzing, processing and visualizing data. Calling External Python Codes ----------------------------- Calling external Python codes very straightforward. For instance, if you had a Python code that accepted a set of structured meshes and then post-processed them to apply radiative feedback, one could imagine calling it directly: .. code-block:: python import radtrans import yt ds = yt.load("DD0010/DD0010") rt_grids = [] for grid in ds.index.grids: rt_grid = radtrans.RegularBox( grid.LeftEdge, grid.RightEdge, grid["density"], grid["temperature"], grid["metallicity"], ) rt_grids.append(rt_grid) grid.clear_data() radtrans.process(rt_grids) Or if you wanted to run a population synthesis module on a set of star particles (and you could fit them all into memory) it might look something like this: .. code-block:: python import pop_synthesis import yt ds = yt.load("DD0010/DD0010") ad = ds.all_data() star_masses = ad["StarMassMsun"] star_metals = ad["StarMetals"] pop_synthesis.CalculateSED(star_masses, star_metals) If you have a code that's written in Python that you are having trouble getting data into from yt, please feel encouraged to email the users list and we'll help out. Calling Non-Python External Codes --------------------------------- Independent of its ability to process, analyze and visualize data, yt can also serve as a mechanism for reading and selecting simulation data. In this way, it can be used to supply data to an external analysis routine written in Fortran, C or C++. This document describes how to supply that data, using the example of a simple code that calculates the best axes that describe a distribution of particles as a starting point. (The underlying method is left as an exercise for the reader; we're only currently interested in the function specification and structs.) If you have written a piece of code that performs some analysis function, and you would like to include it in the base distribution of yt, we would be happy to do so; drop us a line or see :ref:`contributing-code` for more information. To accomplish the process of linking Python with our external code, we will be using a language called `Cython `_, which is essentially a superset of Python that compiles down to C. It is aware of NumPy arrays, and it is able to massage data between the interpreted language Python and C, Fortran or C++. It will be much easier to utilize routines and analysis code that have been separated into subroutines that accept data structures, so we will assume that our halo axis calculator accepts a set of structs. Our Example Code ++++++++++++++++ Here is the ``axes.h`` file in our imaginary code, which we will then wrap: .. code-block:: c typedef struct structParticleCollection { long npart; double *xpos; double *ypos; double *zpos; } ParticleCollection; void calculate_axes(ParticleCollection *part, double *ax1, double *ax2, double *ax3); There are several components to this analysis routine which we will have to wrap. #. We have to wrap the creation of an instance of ``ParticleCollection``. #. We have to transform a set of NumPy arrays into pointers to doubles. #. We have to create a set of doubles into which ``calculate_axes`` will be placing the values of the axes it calculates. #. We have to turn the return values back into Python objects. Each of these steps can be handled in turn, and we'll be doing it using Cython as our interface code. Setting Up and Building Our Wrapper +++++++++++++++++++++++++++++++++++ To get started, we'll need to create two files: .. code-block:: bash axes_calculator.pyx axes_calculator_setup.py These can go anywhere, but it might be useful to put them in their own directory. The contents of ``axes_calculator.pyx`` will be left for the next section, but we will need to put some boilerplate code into ``axes_calculator_setup.pyx``. As a quick sidenote, you should call these whatever is most appropriate for the external code you are wrapping; ``axes_calculator`` is probably not the best bet. Here's a rough outline of what should go in ``axes_calculator_setup.py``: .. code-block:: python NAME = "axes_calculator" EXT_SOURCES = [] EXT_LIBRARIES = ["axes_utils", "m"] EXT_LIBRARY_DIRS = ["/home/rincewind/axes_calculator/"] EXT_INCLUDE_DIRS = [] DEFINES = [] from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext ext_modules = [ Extension( NAME, [NAME + ".pyx"] + EXT_SOURCES, libraries=EXT_LIBRARIES, library_dirs=EXT_LIBRARY_DIRS, include_dirs=EXT_INCLUDE_DIRS, define_macros=DEFINES, ) ] setup(name=NAME, cmdclass={"build_ext": build_ext}, ext_modules=ext_modules) The only variables you should have to change in this are the first six, and possibly only the first one. We'll go through these variables one at a time. ``NAME`` This is the name of our source file, minus the ``.pyx``. We're also mandating that it be the name of the module we import. You're free to modify this. ``EXT_SOURCES`` Any additional sources can be listed here. For instance, if you are only linking against a single ``.c`` file, you could list it here -- if our axes calculator were fully contained within a file called ``calculate_my_axes.c`` we could link against it using this variable, and then we would not have to specify any libraries. This is usually the simplest way to do things, and in fact, yt makes use of this itself for things like HEALPix and interpolation functions. ``EXT_LIBRARIES`` Any libraries that will need to be linked against (like ``m``!) should be listed here. Note that these are the name of the library minus the leading ``lib`` and without the trailing ``.so``. So ``libm.so`` would become ``m`` and ``libluggage.so`` would become ``luggage``. ``EXT_LIBRARY_DIRS`` If the libraries listed in ``EXT_LIBRARIES`` reside in some other directory or directories, those directories should be listed here. For instance, ``["/usr/local/lib", "/home/rincewind/luggage/"]`` . ``EXT_INCLUDE_DIRS`` If any header files have been included that live in external directories, those directories should be included here. ``DEFINES`` Any define macros that should be passed to the C compiler should be listed here; if they just need to be defined, then they should be specified to be defined as "None." For instance, if you wanted to pass ``-DTWOFLOWER``, you would set this to equal: ``[("TWOFLOWER", None)]``. To build our extension, we would run: .. code-block:: bash $ python axes_calculator_setup.py build_ext -i Note that since we don't yet have an ``axes_calculator.pyx``, this will fail. But once we have it, it ought to run. Writing and Calling our Wrapper +++++++++++++++++++++++++++++++ Now we begin the tricky part, of writing our wrapper code. We've already figured out how to build it, which is halfway to being able to test that it works, and we now need to start writing Cython code. For a more detailed introduction to Cython, see the Cython documentation at http://docs.cython.org/en/latest/ . We'll cover a few of the basics for wrapping code however. To start out with, we need to open up and edit our file, ``axes_calculator.pyx``. Open this in your favorite version of vi (mine is vim) and we will get started by declaring the struct we need to pass in. But first, we need to include some header information: .. code-block:: cython import numpy as np cimport numpy as np cimport cython from stdlib cimport malloc, free These lines simply import and "Cython import" some common routines. For more information about what is already available, see the Cython documentation. For now, we need to start translating our data. To do so, we tell Cython both where the struct should come from, and then we describe the struct itself. One fun thing to note is that if you don't need to set or access all the values in a struct, and it just needs to be passed around opaquely, you don't have to include them in the definition. For an example of this, see the ``png_writer.pyx`` file in the yt repository. Here's the syntax for pulling in (from a file called ``axes_calculator.h``) a struct like the one described above: .. code-block:: cython cdef extern from "axes_calculator.h": ctypedef struct ParticleCollection: long npart double *xpos double *ypos double *zpos So far, pretty easy! We've basically just translated the declaration from the ``.h`` file. Now that we have done so, any other Cython code can create and manipulate these ``ParticleCollection`` structs -- which we'll do shortly. Next up, we need to declare the function we're going to call, which looks nearly exactly like the one in the ``.h`` file. (One common problem is that Cython doesn't know what ``const`` means, so just remove it wherever you see it.) Declare it like so: .. code-block:: cython void calculate_axes(ParticleCollection *part, double *ax1, double *ax2, double *ax3) Note that this is indented one level, to indicate that it, too, comes from ``axes_calculator.h``. The next step is to create a function that accepts arrays and converts them to the format the struct likes. We declare our function just like we would a normal Python function, using ``def``. You can also use ``cdef`` if you only want to call a function from within Cython. We want to call it from Python, too, so we just use ``def``. Note that we don't here specify types for the various arguments. In a moment we'll refine this to have better argument types. .. code-block:: cython def examine_axes(xpos, ypos, zpos): cdef double ax1[3], ax2[3], ax3[3] cdef ParticleCollection particles cdef int i particles.npart = len(xpos) particles.xpos = malloc(particles.npart * sizeof(double)) particles.ypos = malloc(particles.npart * sizeof(double)) particles.zpos = malloc(particles.npart * sizeof(double)) for i in range(particles.npart): particles.xpos[i] = xpos[i] particles.ypos[i] = ypos[i] particles.zpos[i] = zpos[i] calculate_axes(&particles, ax1, ax2, ax3) free(particles.xpos) free(particles.ypos) free(particles.zpos) return ( (ax1[0], ax1[1], ax1[2]), (ax2[0], ax2[1], ax2[2]), (ax3[0], ax3[1], ax3[2]) ) This does the rest. Note that we've weaved in C-type declarations (ax1, ax2, ax3) and Python access to the variables fed in. This function will probably be quite slow -- because it doesn't know anything about the variables xpos, ypos, zpos, it won't be able to speed up access to them. Now we will see what we can do by declaring them to be of array-type before we start handling them at all. We can do that by annotating in the function argument list. But first, let's test that it works. From the directory in which you placed these files, run: .. code-block:: bash $ python2.6 setup.py build_ext -i Now, create a sample file that feeds in the particles: .. code-block:: python import axes_calculator axes_calculator.examine_axes(xpos, ypos, zpos) Most of the time in that function is spent in converting the data. So now we can go back and we'll try again, rewriting our converter function to believe that its being fed arrays from NumPy: .. code-block:: cython def examine_axes(np.ndarray[np.float64_t, ndim=1] xpos, np.ndarray[np.float64_t, ndim=1] ypos, np.ndarray[np.float64_t, ndim=1] zpos): cdef double ax1[3], ax2[3], ax3[3] cdef ParticleCollection particles cdef int i particles.npart = len(xpos) particles.xpos = malloc(particles.npart * sizeof(double)) particles.ypos = malloc(particles.npart * sizeof(double)) particles.zpos = malloc(particles.npart * sizeof(double)) for i in range(particles.npart): particles.xpos[i] = xpos[i] particles.ypos[i] = ypos[i] particles.zpos[i] = zpos[i] calculate_axes(&particles, ax1, ax2, ax3) free(particles.xpos) free(particles.ypos) free(particles.zpos) return ( (ax1[0], ax1[1], ax1[2]), (ax2[0], ax2[1], ax2[2]), (ax3[0], ax3[1], ax3[2]) ) This should be substantially faster, assuming you feed it arrays. Now, there's one last thing we can try. If we know our function won't modify our arrays, and they are C-Contiguous, we can simply grab pointers to the data: .. code-block:: cython def examine_axes(np.ndarray[np.float64_t, ndim=1] xpos, np.ndarray[np.float64_t, ndim=1] ypos, np.ndarray[np.float64_t, ndim=1] zpos): cdef double ax1[3], ax2[3], ax3[3] cdef ParticleCollection particles cdef int i particles.npart = len(xpos) particles.xpos = xpos.data particles.ypos = ypos.data particles.zpos = zpos.data for i in range(particles.npart): particles.xpos[i] = xpos[i] particles.ypos[i] = ypos[i] particles.zpos[i] = zpos[i] calculate_axes(&particles, ax1, ax2, ax3) return ( (ax1[0], ax1[1], ax1[2]), (ax2[0], ax2[1], ax2[2]), (ax3[0], ax3[1], ax3[2]) ) But note! This will break or do weird things if you feed it arrays that are non-contiguous. At this point, you should have a mostly working piece of wrapper code. And it was pretty easy! Let us know if you run into any problems, or if you are interested in distributing your code with yt. A complete set of files is available with this documentation. These are slightly different, so that the whole thing will simply compile, but they provide a useful example. * `axes.c <../_static/axes.c>`_ * `axes.h <../_static/axes.h>`_ * `axes_calculator.pyx <../_static/axes_calculator.pyx>`_ * `axes_calculator_setup.py <../_static/axes_calculator_setup.txt>`_ Exporting Data from yt ---------------------- yt is installed alongside h5py. If you need to export your data from yt, to share it with people or to use it inside another code, h5py is a good way to do so. You can write out complete datasets with just a few commands. You have to import, and then save things out into a file. .. code-block:: python import h5py f = h5py.File("some_file.h5", mode="w") f.create_dataset("/data", data=some_data) This will create ``some_file.h5`` if necessary and add a new dataset (``/data``) to it. Writing out in ASCII should be relatively straightforward. For instance: .. code-block:: python f = open("my_file.txt", "w") for halo in halos: x, y, z = halo.center_of_mass() f.write("%0.2f %0.2f %0.2f\n", x, y, z) f.close() This example could be extended to work with any data object's fields, as well. yt-project-yt-f043ac8/doc/source/developing/index.rst000066400000000000000000000016421510711153200227170ustar00rootroot00000000000000Developing in yt ================ yt is an open-source project with a community of contributing scientists. While you can use the existing framework within yt to help answer questions about your own datasets, yt thrives by the addition of new functionality by users just like yourself. Maybe you have a new data format that you would like supported, a new derived quantity that you feel should be included, or a new way of visualizing data--please add them to the code base! We are eager to help you make it happen. There are many ways to get involved with yt -- participating in the mailing list, helping people out in IRC, providing suggestions for the documentation, and contributing code! .. toctree:: :maxdepth: 2 developing building_the_docs testing extensions debugdrive releasing creating_datatypes creating_derived_fields creating_frontend external_analysis deprecating_features yt-project-yt-f043ac8/doc/source/developing/releasing.rst000066400000000000000000000244271510711153200235670ustar00rootroot00000000000000How to Do a Release ------------------- Periodically, the yt development community issues new releases. yt loosely follows `semantic versioning `_. The type of release can be read off from the version number used. Version numbers should follow the scheme ``MAJOR.MINOR.PATCH``. There are three kinds of possible releases: * Bugfix releases These releases should contain only fixes for bugs discovered in earlier releases and should not contain new features or API changes. Bugfix releases only increment the ``PATCH`` version number. Bugfix releases should *not* be generated by merging from the ``main`` branch, instead bugfix pull requests should be backported to a dedicated branch. See :ref:`doing-a-bugfix-release`. Version ``3.2.2`` is a bugfix release. * Minor releases These releases happen when new features are deemed ready to be merged into the ``stable`` branch and should not happen on a regular schedule. Minor releases can also include fixes for bugs if the fix is determined to be too invasive for a bugfix release. Minor releases should *not* include backwards-incompatible changes and should not change APIs. If an API change is deemed to be necessary, the old API should continue to function but might trigger deprecation warnings. Minor releases should happen by merging the ``main`` branch into the ``stable`` branch. Minor releases should increment the ``MINOR`` version number and reset the ``PATCH`` version number to zero. Version ``3.3.0`` is a minor release. * Major releases These releases happen when the development community decides to make major backwards-incompatible changes intentionally. In principle a major version release could include arbitrary changes to the library. Major version releases should only happen after extensive discussion and vetting among the developer and user community. Like minor releases, a major release should happen by merging the ``main`` branch into the ``stable`` branch. Major releases should increment the ``MAJOR`` version number and reset the ``MINOR`` and ``PATCH`` version numbers to zero. If it ever happens, version ``4.0.0`` will be a major release. The job of doing a release differs depending on the kind of release. Below, we describe the necessary steps for each kind of release in detail. .. _doing-a-bugfix-release: Doing a Bugfix Release ~~~~~~~~~~~~~~~~~~~~~~ As described above, bugfix releases are regularly scheduled updates for minor releases to ensure fixes for bugs make their way out to users in a timely manner. Since bugfix releases should not include new features, we do not issue bugfix releases by simply merging from the development ``main`` branch into the ``stable`` branch. Instead, commits are cherry-picked from the ``main`` branch to a backport branch, which is itself merged into ``stable`` when a release happens. Backport branches are named after the minor version then descend from, followed by an ``x``. For instance, ``yt-4.0.x`` is the backport branch for all releases in the 4.0 series. Backporting bugfixes can be done automatically using the `MeeseeksBox bot `_. This necessitates having a Github milestone dedicated to the release, configured with a comment in its description such as ``on-merge: backport to yt-4.0.x``. Then, every PR that was triaged into the milestone will be replicated as a backport PR by the bot when it's merged into main. Some backports are non-trivial and require human attention; if conflicts occur, the bot will provide detailed instructions to perfom the task manually. In short, a manual backport consist of 4 steps - checking out the backport branch locally - create a new branch from there - cherry-picking the merge commit from the original PR with ``git cherry-pick -m1 `` - opening a PR to the backport branch Doing a Minor or Major Release ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is much simpler than a bugfix release. First, make sure that every deprecated features targeted for removal in the new release are removed from the ``main`` branch, ideally in a single PR. Such a PR can be issued at any point between the previous minor or major release and the new one. Then, all that needs to happen is the ``main`` branch must get merged into the ``stable`` branch, and any conflicts that happen must be resolved, almost always in favor of the state of the code on the ``main`` branch. Incrementing Version Numbers and Tagging a Release ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before creating the tag for the release, you must increment the version numbers that are hard-coded in a few files in the yt source so that version metadata for the code is generated correctly. This includes things like ``yt.__version__`` and the version that gets read by the Python Package Index (PyPI) infrastructure. The paths relative to the root of the repository for the three files that need to be edited are: * ``doc/source/conf.py`` The ``version`` and ``release`` variables need to be updated. * ``setup.py`` The ``VERSION`` variable needs to be updated * ``yt/__init__.py`` The ``__version__`` variable must be updated. Once these files have been updated, commit these updates. This is the commit we will tag for the release. To actually create the tag, issue the following command from the ``stable`` branch: .. code-block:: bash git tag Where ```` follows the project's naming scheme for tags (e.g. ``yt-3.2.1``). Once you are done, you will need to push the tag to github:: git push origin --tag This assumes that you have configured the remote ``origin`` to point at the main yt git repository. If you are doing a minor or major version number release, you will also need to update back to the development branch and update the development version numbers in the same files. Uploading to yt-project.org ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before uploading the release to the Python Package Index (pypi.org) we will first upload the package to yt-project.org. This facilitates building binary wheels for pypi and binary conda packages on conda-forge before doing the "official" release. This also ensures that there isn't a period of time when users do ``pip install yt`` and end up downloading the source distribution instead of one of the binary wheels. To create the source distribution, issue the following command in the root of the yt repository:: $ python setup.py sdist This will generate a tarball in a ``dist/`` directory located in the root of the repository. Access to yt-project.org mediated via SSH login. Please contact one of the current yt developers for access to the webserver running yt-project.org if you do not already have it. You will need a copy of your SSH public key so that your key can be added to the list of authorized keys. Once you login, use e.g. ``scp`` to upload a copy of the source distribution tarball to https://yt-project.org/sdist, like so:: $ scp dist/yt-3.5.1.tar.gz yt_analysis@dickenson.dreamhost.com:yt-project.org/sdist You may find it helpful to set up an ssh config for dickenson to make this command a bit easier to execute. Publishing ~~~~~~~~~~ We distribute yt on two main channels: PyPI.org and conda-forge, in this order. PyPI ++++ The publication process for PyPI is automated for the most part, via Github actions, using ``.github/workflows/wheels.yaml``. Specifically, a release is pushed to PyPI when a new git tag starting with ``yt-`` is pushed to the main repo. Let's review the details here. PyPI releases contain the source code (as a tarball), and wheels. Wheels are compiled distributions of the source code. They are OS specific as well as Python-version specific. Producing wheels for every supported combination of OS and Python versions is done with `cibuildwheels `_ Upload to PyPI is automated via Github Actions `upload-artifact `_ and `download-artifact `_. Note that automated uploads are currently perfomed using Matt Turk's credentials. If that worked, you can skip to the next section. Otherwise, upload can be perfomed manually by first downloading the artifacts ``wheels`` and ``tarball`` from the workflow webpage, then at the command line (make sure that the ``dist`` directory doesn't exist or is empty) .. code-block:: bash unzip tarball.zip -d dist unzip wheels.zip -d dist python -m pip install --upgrade twine twine upload dist/* You will be prompted for your PyPI credentials and then the package should upload. Note that for this to complete successfully, you will need an account on PyPI and that account will need to be registered as an "owner" or "maintainer" of the yt package. ``conda-forge`` +++++++++++++++ Conda-forge packages for yt are managed via the yt feedstock, located at https://github.com/conda-forge/yt-feedstock. When a release is pushed to PyPI a bot should detect a new version and issue a PR to the feedstock with the new version automatically. When this feedstock is updated, make sure that the SHA256 hash of the tarball matches the one you uploaded to PyPI and that the version number matches the one that is being released. In case the automated PR fails CI, feedstock maintainers are allowed to push to the bot's branch with any fixes required. Should you need to update the feedstock manually, you will need to update the ``meta.yaml`` file located in the ``recipe`` folder in the root of the feedstock repository. Most likely you will only need to update the version number and the SHA256 hash of the tarball. If yt's dependencies change you may also need to update the recipe. Once you have updated the recipe, propose a pull request on github and merge it once all builds pass. Announcing ~~~~~~~~~~ After the release is uploaded to `PyPI `_ and `conda-forge `_, you should send out an announcement e-mail to the yt mailing lists as well as other possibly interested mailing lists for all but bugfix releases. Creating a Github release attached to the tag also offers a couple advantages. Auto-generated release notes can be a good starting point, though it's best to edit out PRs that not directly affecting users, and these notes can be edited before (draft mode) and after the release, so errors can be corrected after the fact. yt-project-yt-f043ac8/doc/source/developing/testing.rst000066400000000000000000000620541510711153200232710ustar00rootroot00000000000000.. _testing: Testing ======= yt includes a testing suite that one can run on the code base to ensure that no breaks in functionality have occurred. This suite is based on the `pytest `_ testing framework and consists of two types of tests: * Unit tests. These make sure that small pieces of code run (or fail) as intended in predictable contexts. See :ref:`unit_testing`. * Answer tests. These generate outputs from the user-facing yt API and compare them against the outputs produced using an older, "known good", version of yt. See :ref:`answer_testing`. These tests ensure consistency in results as development proceeds. We recommend that developers run tests locally on changed features when developing to help ensure that the new code does not break any existing functionality. To further this goal and ensure that changes do not propagate errors or have unintentional consequences on the rest of the codebase, the full test suite is run through our continuous integration (CI) servers. CI is run on push on open pull requests on a variety of computational platforms using Github Actions and a `continuous integration server `_ at the University of Illinois. The full test suite may take several hours to run, so we do not recommend running it locally. .. _unit_testing: Unit Testing ------------ What Do Unit Tests Do ^^^^^^^^^^^^^^^^^^^^^ Unit tests are tests that operate on some small piece of code and verify that it behaves as intended. In practice, this means that we write simple assertions (or ``assert`` statements) about results and then let pytest go through them. A test is considered a success when no error (in particular ``AssertionError``) occurs. How to Run the Unit Tests ^^^^^^^^^^^^^^^^^^^^^^^^^ One can run the unit tests by navigating to the base directory of the yt git repository and invoking ``pytest``: .. code-block:: bash $ cd $YT_GIT $ pytest where ``$YT_GIT`` is the path to the root of the yt git repository. If you only want to run tests in a specific file, you can do so by specifying the path of the test relative to the ``$YT_GIT/`` directory. For example, if you want to run the ``plot_window`` tests, you'd run: .. code-block:: bash $ pytest yt/visualization/tests/test_plotwindow.py from the yt source code root directory. Additionally, if you only want to run a specific test in a test file (rather than all of the tests contained in the file), such as, ``test_all_fields`` in ``plot_window.py``, you can do so by running: .. code-block:: bash $ pytest yt/visualization/tests/test_plotwindow.py::test_all_fields from the yt source code rood directory See the pytest documentation for more on how to `invoke pytest `_ and `select tests `_. Unit Test Tools ^^^^^^^^^^^^^^^ yt provides several helper functions and decorators to write unit tests. These tools all reside in the ``yt.testing`` module. Describing them all in detail is outside the scope of this document, as in some cases they belong to other packages. How To Write New Unit Tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^ To create new unit tests: #. Create a new ``tests/`` directory next to the file containing the functionality you want to test and add an empty ``__init__.py`` file to it. #. If a ``tests/`` directory already exists, there is no need to create a new one. #. Inside this new ``tests/`` directory, create a new python file prefixed with ``test_`` and including the name of the functionality or source file being tested. #. If a file testing the functionality you're interested in already exists, please add your tests to the existing there. #. Inside this new ``test_`` file, create functions prefixed with ``test_`` that accept no arguments. #. Each test function should do some work that tests some functionality and should also verify that the results are correct using assert statements or functions. #. If a dataset is needed, use ``fake_random_ds``, ``fake_amr_ds``, or ``fake_particle_ds`` (the former two of which have support for particles that may be utilized) and be sure to test for several combinations of ``nproc`` so that domain decomposition can be tested as well. #. To iterate over multiple options, or combinations of options, use the `@pytest.mark.parametrize decorator `_. For an example of how to write unit tests, look at the file ``yt/data_objects/tests/test_covering_grid.py``, which covers a great deal of functionality. Debugging Failing Tests ^^^^^^^^^^^^^^^^^^^^^^^ When writing new tests, one often exposes bugs or writes a test incorrectly, causing an exception to be raised or a failed test. To help debug issues like this, ``pytest`` can `drop into a debugger `_ whenever a test fails or raises an exception. In addition, one can debug more crudely using print statements. To do this, you can add print statements to the code as normal. However, the test runner will capture all print output by default. To ensure that output gets printed to your terminal while the tests are running, pass ``-s`` (which will disable stdout and stderr capturing) to the ``pytest`` executable. .. code-block:: bash $ pytest -s Lastly, to quickly debug a specific failing test, it is best to only run that one test during your testing session. This can be accomplished by explicitly passing the name of the test function or class to ``pytest``, as in the following example: .. code-block:: bash $ pytest yt/visualization/tests/test_plotwindow.py::TestSetWidth This pytest invocation will only run the tests defined by the ``TestSetWidth`` class. See the `pytest documentation `_ for more on the various ways to invoke pytest. Finally, to determine which test is failing while the tests are running, it helps to run the tests in "verbose" mode. This can be done by passing the ``-v`` option to the ``pytest`` executable. .. code-block:: bash $ pytest -v All of the above ``pytest`` options can be combined. So, for example, to run the ``TestSetWidth`` tests with verbose output, letting the output of print statements come out on the terminal prompt, and enabling pdb debugging on errors or test failures, one would do: .. code-block:: bash $ pytest yt/visualization/tests/test_plotwindow.py::TestSetWidth -v -s --pdb More pytest options can be found by using the ``--help`` flag .. code-block:: bash $ pytest --help .. _answer_testing: Answer Testing -------------- .. note:: This section documents answer tests run with ``pytest``. The plan is to switch to using ``pytest`` for answer tests at some point in the future, but currently (July 2024), answer tests are still implemented and run with ``nose``. We generally encourage developers to use ``pytest`` for any new tests, but if you need to change or update one of the older ``nose`` tests, or are, e.g., writing a new frontend, an `older version of this documentation `_ decribes how the ``nose`` tests work. .. note:: Given that nose never had support for Python 3.10 (which as of yt 4.4 is our oldest supported version), it is necessary to patch it to get tests running. This is the command we run on CI to this end ``find .venv/lib/python3.10/site-packages/nose -name '*.py' -exec sed -i -e s/collections.Callable/collections.abc.Callable/g '{}' ';'`` What Do Answer Tests Do ^^^^^^^^^^^^^^^^^^^^^^^ Answer tests use `actual data `_ to test reading, writing, and various manipulations of that data. Answer tests are how we test frontends, as opposed to operations, in yt. In order to ensure that each of these operations are performed correctly, we store gold standard versions of yaml files called answer files. More generally, an answer file is a yaml file containing the results of having run the answer tests, which can be compared to a reference, enabling us to control that results do not drift over time. .. _run_answer_testing: How to Run the Answer Tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order to run the answer tests locally: * Create a directory to hold the data you'll be using for the answer tests you'll be writing or the answer tests you'll be running. This directory should be outside the yt git repository in a place that is logical to where you would normally store data. * Add folders of the required data to this directory. Other yt data, such as ``IsolatedGalaxy``, can be downloaded to this directory as well. * Tell yt where it can find the data. This is done by setting the config parameter ``test_data_dir`` to the path of the directory with the test data downloaded from https://yt-project.org/data/. For example, .. code-block:: bash $ yt config set yt test_data_dir /Users/tomservo/src/yt-data this should only need to be done once (unless you change where you're storing the data, in which case you'll need to repeat this step so yt looks in the right place). * Generate or obtain a set of gold standard answer files. In order to generate gold standard answer files, wwitch to a "known good" version of yt and then run the answer tests as described below. Once done, switch back to the version of yt you wish to test. * Now you're ready to run the answer tests! As an example, let's focus on running the answer tests for the tipsy frontend. Let's also assume that we need to generate a gold standard answer file. To do this, we first switch to a "known good" version of yt and run the following command from the top of the yt git directory (i.e., ``$YT_GIT``) in order to generate the gold standard answer file: .. note:: It's possible to run the answer tests for **all** the frontends, but due to the large number of test datasets we currently use this is not normally done except on the yt project's contiguous integration server. .. code-block:: bash $ cd $YT_GIT $ pytest --with-answer-testing --answer-store --local-dir="$HOME/Documents/test" -k "TestTipsy" The ``--with-answer-testing`` tells pytest that we want to run answer tests. Without this option, the unit tests will be run instead of the answer tests. The ``--answer-store`` option tells pytest to save the results produced by each test to a local gold standard answer file. Omitting this option is how we tell pytest to compare the results to a gold standard. The ``--local-dir`` option specifies where the gold standard answer file will be saved (or is already located, in the case that ``--answer-store`` is omitted). The ``-k`` option tells pytest that we only want to run tests whose name matches the given pattern. .. note:: The path specified by ``--local-dir`` can, but does not have to be, the same directory as the ``test_data_dir`` configuration variable. It is best practice to keep the data that serves as input to yt separate from the answers produced by yt's tests, however. .. note:: The value given to the `-k` option (e.g., `"TestTipsy"`) is the name of the class containing the answer tests. You do not need to specify the path. The newly generated gold standard answer file will be named ``tipsy_answers_xyz.yaml``, where ``xyz`` denotes the version number of the gold standard answers. The answer version number is determined by the ``answer_version`` attribute of the class being tested (e.g., ``TestTipsy.answer_version``). .. note:: Changes made to yt sometimes result in known, expected changes to the way certain operations behave. This necessitates updating the gold standard answer files. This process is accomplished by changing the version number specified in each answer test class (e.g., ``TestTipsy.answer_version``). The answer version for each test class can be found as the attribute `answer_version` of that class. Once the gold standard answer file has been generated we switch back to the version of yt we want to test, recompile if necessary, and run the tests using the following command: .. code-block:: bash $ pytest --with-answer-testing --local-dir="$HOME/Documents/test" -k "TestTipsy" The result of each test is printed to STDOUT. If a test passes, pytest prints a period. If a test fails, encounters an exception, or errors out for some reason, then an F is printed. Explicit descriptions for each test are also printed if you pass ``-v`` to the ``pytest`` executable. Similar to the unit tests, the ``-s`` and ``--pdb`` options can be passed, as well. How to Write Answer Tests ^^^^^^^^^^^^^^^^^^^^^^^^^ To add a new answer test: #. Create a new directory called ``tests`` inside the directory where the component you want to test resides and add an empty ``__init__.py`` file to it. #. Create a new file in the ``tests`` directory that will hold the new answer tests. The name of the file should begin with ``test_``. #. Create a new class whose name begins with ``Test`` (e.g., ``TestTipsy``). #. Decorate the class with ``pytest.mark.answer_test``. This decorator is used to tell pytest which tests are answer tests. .. note:: Tests that do not have this decorator are considered to be unit tests. #. Add the following three attributes to the class: ``answer_file=None``, ``saved_hashes=None``, and ``answer_version=000``. These attributes are used by the ``hashing`` fixture (discussed below) to automate the creation of new answer files as well as facilitate the comparison to existing answers. #. Add methods to the class that test a number of different fields and data objects. #. If these methods are performing calculations or data manipulation, they should store the result in a ``ndarray``, if possible. This array should be be added to the ``hashes`` (see below) dictionary like so: ``self.hashes.update(:)``, where ```` is the name of the function from ``yt/utilities/answer_testing/answer_tests.py`` that is being used and ```` is the ``ndarray`` holding the result If you are adding to a frontend that has tests already, simply add methods to the existing test class. There are several things that can make the test writing process easier: * ``yt/utilities/answer_testing/testing_utilities.py`` contains a large number of helper functions. * Most frontends end up needing to test much of the same functionality as other frontends. As such, a list of functions that perform such work can be found in ``yt/utilities/answer_testing/answer_tests.py``. * `Fixtures `_! You can find the set of fixtures that have already been built for yt in ``$YT_GIT/conftest.py``. If you need/want to add additional fixtures, please add them there. * The `parametrize decorator `_ is extremely useful for performing iteration over various combinations of test parameters. It should be used whenever possible. * The use of this decorator allows pytest to write the names and values of the test parameters to the generated answer files, which can make debugging failing tests easier, since one can easily see exactly which combination of parameters were used for a given test. * It is also possible to employ the ``requires_ds`` decorator to ensure that a test does not run unless a specific dataset is found, but not necessary. If the dataset is parametrized over, then the ``ds`` fixture found in the root ``conftest.py`` file performs the same check and marks the test as failed if the dataset isn't found. Here is what a minimal example might look like for a new frontend: .. code-block:: python # Content of yt/frontends/new_frontend/tests/test_outputs.py import pytest from yt.utilities.answer_testing.answer_tests import field_values # Parameters to test with ds1 = "my_first_dataset" ds2 = "my_second_dataset" field1 = ("Gas", "Density") field2 = ("Gas", "Temperature") obj1 = None obj2 = ("sphere", ("c", (0.1, "unitary"))) @pytest.mark.answer_test class TestNewFrontend: answer_file = None saved_hashes = None answer_version = "000" @pytest.mark.usefixtures("hashing") @pytest.mark.parametrize("ds", [ds1, ds2], indirect=True) @pytest.mark.parametrize("field", [field1, field2], indirect=True) @pytest.mark.parametrize("dobj", [obj1, obj2], indirect=True) def test_fields(self, ds, field, dobj): self.hashes.update({"field_values": field_values(ds, field, dobj)}) Answer test examples can be found in ``yt/frontends/enzo/tests/test_outputs.py``. .. _update_image_tests: Creating and Updating Image Baselines for pytest-mpl Tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We use `pytest-mpl `_ for image comparison tests. These tests take the form of functions, which must be decorated with ``@pytest.mark.mpl_image_compare`` and return a ``matplotlib.figure.Figure`` object. The collection of reference images is kept as git submodule in ``tests/pytest_mpl_baseline/``. There are 4 situations where updating reference images may be necessary - adding new tests - bugfixes - intentional change of style in yt - old baseline fails with a new version of matplotlib, but changes are not noticeable to the human eye The process of updating images is the same in all cases. It involves opening two Pull Requests (PR) that we'll number PR1 and PR2. 1. open a Pull Request (PR1) to yt's main repo with the code changes 2. wait for tests jobs to complete 3. go to the "Checks" tab on the PR page (``https://github.com/yt-project/yt/pull//checks``) 4. if all tests passed, you're done ! 5. if tests other than image tests failed, fix them, and go back to step 2. Otherwise, if only image tests failed, navigate to the "Build and Tests" job summary page. 6. at the bottom of the page, you'll find "Artifacts". Download ``yt_pytest_mpl_results.zip``, unzip it and open ``fig_comparison.html`` therein; This document is an interactive report of the test job. Inspect failed tests results and verify that any differences are either intended or insignificant. If they are not, fix the code and go back to step 2 7. clone ``https://github.com/yt-project/yt_pytest_mpl_baseline.git`` and unzip the new baseline 8. Download the other artifact (``yt_pytest_mpl_new_baseline.zip``), unzip it within your clone of ``yt_pytest_mpl_baseline``. 9. create a branch, commit all changes, and open a Pull Request (PR2) to ``https://github.com/yt-project/yt_pytest_mpl_baseline`` (PR2 should link to PR1) 10. wait for this second PR to be merged 11. Now it's time to update PR1: navigate back to your local copy of ``yt``'s main repository. 12. run the following commands .. code-block:: bash $ git submodule update --init $ cd tests/pytest_mpl_baseline $ git checkout main $ git pull $ cd ../ $ git add pytest_mpl_baseline $ git commit -m "update image test baseline" $ git push 13. go back to step 2. This time everything should pass. If not, ask for help ! .. note:: Though it is technically possible to (re)generate reference images locally, it is best not to, because at a pixel level, matplotlib's behaviour is platform-dependent. By letting CI runners generate images, we ensure pixel-perfect comparison is possible in CI, which is where image comparison tests are most often run. .. _deprecated_generic_image: How to Write Image Comparison Tests (deprecated API) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. warning:: this section describes deprecated API. New test code should follow :ref:`_update_image_tests` Many of yt's operations involve creating and manipulating images. As such, we have a number of tests designed to compare images. These tests employ functionality from matplotlib to automatically compare images and detect differences, if any. Image comparison tests are used in the plotting and volume rendering machinery. The easiest way to use the image comparison tests is to make use of the ``generic_image`` function. As an argument, this function takes a function the test machinery can call which will save an image to disk. The test will then find any images that get created and compare them with the stored "correct" answer. Here is an example test function (from ``yt/visualization/tests/test_raw_field_slices.py``): .. code-block:: python import pytest import yt from yt.utilities.answer_testing.answer_tests import generic_image from yt.utilities.answer_testing.testing_utilities import data_dir_load, requires_ds # Test data raw_fields = "Laser/plt00015" def compare(ds, field): def slice_image(im_name): sl = yt.SlicePlot(ds, "z", field) sl.set_log("all", False) image_file = sl.save(im_name) return image_file gi = generic_image(slice_image) # generic_image returns a list. In this case, there's only one entry, # which is a np array with the data we want assert len(gi) == 1 return gi[0] @pytest.mark.answer_test @pytest.mark.usefixtures("temp_dir") class TestRawFieldSlices: answer_file = None saved_hashes = None answer_version = "000" @pytest.mark.usefixtures("hashing") @requires_ds(raw_fields) def test_raw_field_slices(self, field): ds = data_dir_load(raw_fields) gi = compare(ds, field) self.hashes.update({"generic_image": gi}) .. note:: The inner function ``slice_image`` can create any number of images, as long as the corresponding filenames conform to the prefix. Another good example of an image comparison test is the ``plot_window_attribute`` defined in the ``yt/utilities/answer_testing/answer_tests.py`` and used in ``yt/visualization/tests/test_plotwindow.py``. This sort of image comparison test is more useful if you are finding yourself writing a ton of boilerplate code to get your image comparison test working. The ``generic_image`` function is more useful if you only need to do a one-off image comparison test. Updating Answers ~~~~~~~~~~~~~~~~ In order to regenerate answers for a particular set of tests it is sufficient to change the ``answer_version`` attribute in the desired test class. When adding tests to an existing set of answers (like ``local_owls_000.yaml`` or ``local_varia_000.yaml``), it is considered best practice to first submit a pull request adding the tests WITHOUT incrementing the version number. Then, allow the tests to run (resulting in "no old answer" errors for the missing answers). If no other failures are present, you can then increment the version number to regenerate the answers. This way, we can avoid accidentally covering up test breakages. .. _handling_dependencies: Handling yt Dependencies ------------------------ Our dependencies are specified in ``pyproject.toml``. Hard dependencies are found in ``project.dependencies``, while optional dependencies are specified in ``project.optional-dependencies``. The ``full`` target contains the specs to run our test suite, which are intended to be as modern as possible (we don't set upper limits to versions unless we need to). The ``test`` target specifies the tools needed to run the tests, but not needed by yt itself. Documentation and typechecking requirements are found in ``requirements/``, and used in ``tests/ci_install.sh``. **Python version support.** We vow to follow numpy's deprecation plan regarding our supported versions for Python and numpy, defined formally in `NEP 29 `_, but generally support larger version intervals than recommended in this document. **Third party dependencies.** We attempt to make yt compatible with a wide variety of upstream software versions. However, sometimes a specific version of a project that yt depends on causes some breakage and must be blacklisted in the tests or a more experimental project that yt depends on optionally might change sufficiently that the yt community decides not to support an old version of that project. **Note.** Some of our optional dependencies are not trivial to install and their support may vary across platforms. If you would like to add a new dependency for yt (even an optional dependency) or would like to update a version of a yt dependency, you must edit the ``pyproject.toml`` file. For new dependencies, simply append the name of the new dependency to the end of the file, along with a pin to the latest version number of the package. To update a package's version, simply update the version number in the entry for that package. Finally, we also run a set of tests with "minimal" dependencies installed. When adding tests that depend on an optional dependency, you can wrap the test with the ``yt.testing.requires_module decorator`` to ensure it does not run during the minimal dependency tests (see ``yt/frontends/amrvac/tests/test_read_amrvac_namelist.py`` for a good example). If for some reason you need to update the listing of packages that are installed for the "minimal" dependency tests, you will need to update ``minimal_requirements.txt``. yt-project-yt-f043ac8/doc/source/examining/000077500000000000000000000000001510711153200206765ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/examining/Loading_Data_via_Functions.ipynb000066400000000000000000000254541510711153200271500ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "id": "35cb9c22-04eb-476c-a7b6-afd27690be4d", "metadata": {}, "source": [ "# Loading Data via Functions\n", "\n", "One of the things that yt provides is the notion of \"derived fields.\" These are fields that exist only in memory, and are defined by a functional form, typically transforming other fields. Starting with version 4.1 of yt, however, we have made it much, much easier to define so-called \"on-disk\" fields through their functional forms, akin to how derived fields are generated. **At present, this is only available for grid-based frontends. Extension to other types is anticipated in future versions of yt.**\n", "\n", "What this means is that if you have a way to grab data -- at any resolution -- but don't want to either load it into memory in advance or write a complete \"frontend\", you can just write some functions and use those to construct a fully-functional dataset using the existing `load_amr_grids` and `load_uniform_grid` functions, supplying *functions* instead of arrays.\n", "\n", "There are a few immediate use cases that can be seen for this:\n", "\n", " - Data is accessible through another library, for instance if a library exists that reads subsets of data (or regularizes that data to given regions) or if you are calling yt from an *in situ* analysis library\n", " - Data can be remotely accessed on-demand\n", " - You have a straightforward data format for which a frontend does not exist\n", " - All of the data can be generated through an analytical definition\n", " \n", "The last one is what we'll use to demonstrate this. Let's imagine that I had a grid structure that I wanted to explore, but I wanted all of my data to be generated through functions that were exclusively dependent on the spatial position." ] }, { "cell_type": "code", "execution_count": null, "id": "fd2b8726-5e3b-4641-8b13-7c1163336c8b", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import yt" ] }, { "cell_type": "markdown", "id": "2f2074a7-a71a-4032-8e92-11e908e0f15c", "metadata": {}, "source": [ "The example we've cooked up is going to be a bit silly, and we'll demonstrate it a little bit with one and two dimensional setups before getting into the full yt demonstration. (If you have ideas for a better one, please do suggest them!) We'll start with some overlapping trigonometric functions, which we'll attenuate by their distance from the center.\n", "\n", "So we'll set up some coefficients for different periods of the functions (the `coefficients` variable) and we'll sum up those functions. The next thing we'll do, so that we have some global attenuation we can see, is use a Gaussian function centered at the center of our domain." ] }, { "cell_type": "code", "execution_count": null, "id": "27180398-def2-4a65-bc3f-c807ee0c13a4", "metadata": {}, "outputs": [], "source": [ "x = np.mgrid[0.0:1.0:512j]\n", "coefficients = (100, 50, 30, 10)\n", "y = sum(c * np.sin(2 ** (2 + i) * (x * np.pi * 2)) for i, c in enumerate(coefficients))\n", "atten = np.exp(-20 * (1.1 * (x - 0.5)) ** 2)" ] }, { "cell_type": "markdown", "id": "22c9b8e5-32ca-4402-95e6-452b3d71418c", "metadata": {}, "source": [ "Now let's plot it! The top right is the attenuation, bottom left is the base sum of trig functions, and then the bottom right is the product." ] }, { "cell_type": "code", "execution_count": null, "id": "dbd391fb-10ff-4f2e-8b3e-bbd11f4cc4a6", "metadata": {}, "outputs": [], "source": [ "fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, dpi=150)\n", "ax4.plot(x, y * atten)\n", "ax2.plot(x, atten)\n", "ax3.plot(x, y)\n", "ax1.axis(False);" ] }, { "cell_type": "markdown", "id": "958c652a-987a-40cf-86ff-fa0bd7050996", "metadata": {}, "source": [ "Well, that looks like it might have some structure at different scales! We should be able to use something like this to show sampling errors and so on in AMR, and it'll have structure down to a reasonably high level of detail. Let's briefly demonstrate in 2D before moving on to 3D, using similar functions. This is all basically the same as the previous cells, except we're overlaying along a couple different dimensions." ] }, { "cell_type": "code", "execution_count": null, "id": "9e7897e1-b593-43bc-aeaa-1e86ff94566a", "metadata": {}, "outputs": [], "source": [ "x, y = np.mgrid[0.0:1.0:512j, 0.0:1.0:512j]\n", "\n", "x_coefficients = (100, 50, 30, 10)\n", "y_coefficients = (20, 90, 80, 30)\n", "\n", "z = sum(\n", " c * np.sin(2 ** (1 + i) * (x * np.pi * 2 + 2**i))\n", " for i, c in enumerate(x_coefficients)\n", ") * sum(\n", " c * np.sin(2 ** (1 + i) * (y * np.pi * 2 + 2**i))\n", " for i, c in enumerate(y_coefficients)\n", ")\n", "r = np.sqrt(((x - 0.5) ** 2) + ((y - 0.5) ** 2))\n", "atten = np.exp(-20 * (1.1 * r**2))\n", "\n", "plt.pcolormesh(x, y, z * atten)" ] }, { "cell_type": "markdown", "id": "2fc53e45-ff35-4d15-a2eb-b74a32be9609", "metadata": {}, "source": [ "This is an image of the full dataset, but what happens if we coarsely sample, as we would in AMR simulations? We can stride along the axes (let's say, every 32nd point) to get an idea of what that looks like." ] }, { "cell_type": "code", "execution_count": null, "id": "797c884d-3ccb-412b-af13-a46de54e5ab9", "metadata": {}, "outputs": [], "source": [ "plt.pcolormesh(x[::32, ::32], y[::32, ::32], (z * atten)[::32, ::32])" ] }, { "cell_type": "markdown", "id": "9355880c-8579-4dd9-b24f-bf931f1d84f3", "metadata": {}, "source": [ "For moving to 3D, I'm going to add on some higher-frequency modes, which I'll also amplify a bit more. We'll use the standard attenuation (although a directionally-dependent attenuation would be nice, wouldn't it?)\n", "\n", "And this time, we'll write them into a *special* function. This is the function we'll use to supply to our `load_amr_grids` function -- it has different arguments than a derived field; because it is assumed to always return three-dimensional data, it accepts a proper grid object (which may have spatial or other attributes) and it also receives the field name.\n", "\n", "Using this, we will compute the cell-centers for all of the cells in the grid, and use them to compute our overlapping functions and apply the attenuation. In doing so, we should be able to see structure at different levels. This is the same way you would write a function that loaded a file from disk, or received over HTTP, or used a library to read data; by having access to the grid and the field name, it should be completely determinative of how to access the data." ] }, { "cell_type": "code", "execution_count": null, "id": "9ce0d056-2e08-4ff2-bddf-1594cfa4a801", "metadata": {}, "outputs": [], "source": [ "x_coefficients = (100, 50, 30, 10, 20)\n", "y_coefficients = (20, 90, 80, 30, 30)\n", "z_coefficients = (50, 10, 90, 40, 40)\n", "\n", "\n", "def my_function(grid, field_name):\n", " # We want N points from the cell-center to the cell-center on the other side\n", " x, y, z = (\n", " np.linspace(\n", " grid.LeftEdge[i] + grid.dds[i] / 2,\n", " grid.RightEdge[i] - grid.dds[i] / 2,\n", " grid.ActiveDimensions[i],\n", " )\n", " for i in (0, 1, 2)\n", " )\n", " r = np.sqrt(\n", " ((x.d - 0.5) ** 2)[:, None, None]\n", " + ((y.d - 0.5) ** 2)[None, :, None]\n", " + ((z.d - 0.5) ** 2)[None, None, :]\n", " )\n", " atten = np.exp(-20 * (1.1 * r**2))\n", " xv = sum(\n", " c * np.sin(2 ** (1 + i) * (x.d * np.pi * 2))\n", " for i, c in enumerate(x_coefficients)\n", " )\n", " yv = sum(\n", " c * np.sin(2 ** (1 + i) * (y.d * np.pi * 2))\n", " for i, c in enumerate(y_coefficients)\n", " )\n", " zv = sum(\n", " c * np.sin(2 ** (1 + i) * (z.d * np.pi * 2))\n", " for i, c in enumerate(z_coefficients)\n", " )\n", " return atten * (xv[:, None, None] * yv[None, :, None] * zv[None, None, :])" ] }, { "cell_type": "markdown", "id": "479d7e97-a3ad-409e-a067-e722b3acab0f", "metadata": {}, "source": [ "We'll use a standard grid hierarchy -- which is used internally in yt testing -- and fill it up with a single field that provides this function rather than any arrays. We'll then use `load_amr_grids` to read it; note that we're not creating any arrays ahead of time." ] }, { "cell_type": "code", "execution_count": null, "id": "132c3f99-5b6e-4042-8fc5-49acb8cd9d8c", "metadata": {}, "outputs": [], "source": [ "from yt.testing import _amr_grid_index\n", "\n", "grid_data = []\n", "for level, le, re, dims in _amr_grid_index:\n", " grid_data.append(\n", " {\n", " \"level\": level,\n", " \"left_edge\": le,\n", " \"right_edge\": re,\n", " \"dimensions\": dims,\n", " \"density\": my_function,\n", " }\n", " )\n", "ds = yt.load_amr_grids(\n", " grid_data, [32, 32, 32], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]])\n", ")" ] }, { "cell_type": "markdown", "id": "2972eb49-823b-41d2-a132-4a0fd4be2904", "metadata": {}, "source": [ "And finally, we'll demonstrate it with a slice along the y axis." ] }, { "cell_type": "code", "execution_count": null, "id": "d35407c6-da5d-47e1-93bd-54672c33fbe8", "metadata": {}, "outputs": [], "source": [ "p = ds.r[:, 0.5, :].plot(\"density\").set_log(\"density\", False)" ] }, { "cell_type": "markdown", "id": "cfd5d56e-5d3a-4537-98a5-22f659268f80", "metadata": {}, "source": [ "And with a quick zoom, we can see that the structure is indeed present *and* subject to the sampling effects we discussed earlier." ] }, { "cell_type": "code", "execution_count": null, "id": "aa420efd-536f-45e2-90ae-51bc8dc68c5f", "metadata": {}, "outputs": [], "source": [ "p.zoom(4)" ] } ], "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.10.12" } }, "nbformat": 4, "nbformat_minor": 5 } yt-project-yt-f043ac8/doc/source/examining/Loading_Generic_Array_Data.ipynb000066400000000000000000000646571510711153200270630ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading Generic Array Data\n", "\n", "Even if your data is not strictly related to fields commonly used in\n", "astrophysical codes or your code is not supported yet, you can still feed it to\n", "yt to use its advanced visualization and analysis facilities. The only\n", "requirement is that your data can be represented as three-dimensional NumPy arrays with a consistent grid structure. What follows are some common examples of loading in generic array data that you may find useful. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generic Unigrid Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The simplest case is that of a single grid of data spanning the domain, with one or more fields. The data could be generated from a variety of sources; we'll just give three common examples:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Data generated \"on-the-fly\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The most common example is that of data that is generated in memory from the currently running script or notebook. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "import numpy as np\n", "from numpy.random import default_rng # we'll be generating random numbers here\n", "\n", "import yt\n", "\n", "prng = default_rng(seed=42)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example, we'll just create a 3-D array of random floating-point data using NumPy:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "arr = prng.random(size=(64, 64, 64))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To load this data into yt, we need associate it with a field. The `data` dictionary consists of one or more fields, each consisting of a tuple of a NumPy array and a unit string. Then, we can call `load_uniform_grid`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "data = {\"density\": (arr, \"g/cm**3\")}\n", "bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])\n", "ds = yt.load_uniform_grid(data, arr.shape, length_unit=\"Mpc\", bbox=bbox, nprocs=64)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`load_uniform_grid` takes the following arguments and optional keywords:\n", "\n", "* `data` : This is a dict of numpy arrays, where the keys are the field names\n", "* `domain_dimensions` : The domain dimensions of the unigrid\n", "* `length_unit` : The unit that corresponds to `code_length`, can be a string, tuple, or floating-point number\n", "* `bbox` : Size of computational domain in units of `code_length`\n", "* `nprocs` : If greater than 1, will create this number of subarrays out of data\n", "* `sim_time` : The simulation time in seconds\n", "* `mass_unit` : The unit that corresponds to `code_mass`, can be a string, tuple, or floating-point number\n", "* `time_unit` : The unit that corresponds to `code_time`, can be a string, tuple, or floating-point number\n", "* `velocity_unit` : The unit that corresponds to `code_velocity`\n", "* `magnetic_unit` : The unit that corresponds to `code_magnetic`, i.e. the internal units used to represent magnetic field strengths. NOTE: if you want magnetic field units to be in the SI unit system, you must specify it here, e.g. `magnetic_unit=(1.0, \"T\")`\n", "* `periodicity` : A tuple of booleans that determines whether the data will be treated as periodic along each axis\n", "* `geometry` : The geometry of the dataset, can be `cartesian`, `cylindrical`, `polar`, `spherical`, `geographic` or `spectral_cube`\n", "* `default_species_fields` : if set to `ionized` or `neutral`, default species fields are accordingly created for H and He which also set mean molecular weight\n", "* `axis_order` : The order of the axes in the data array, e.g. `(\"z\", \"y\", \"x\")` with cartesian geometry\n", "* `cell_widths` : If set, specify the cell widths along each dimension. Must be consistent with the `domain_dimensions` argument\n", "* `parameters` : A dictionary of dataset parameters, , useful for storing dataset metadata\n", "* `dataset_name` : The name of the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.)\n", "\n", "This example creates a yt-native dataset `ds` that will treat your array as a\n", "density field in cubic domain of 3 Mpc edge size and simultaneously divide the \n", "domain into `nprocs` = 64 chunks, so that you can take advantage\n", "of the underlying parallelism. \n", "\n", "The optional unit keyword arguments allow for the default units of the dataset to be set. They can be:\n", "* A string, e.g. `length_unit=\"Mpc\"`\n", "* A tuple, e.g. `mass_unit=(1.0e14, \"Msun\")`\n", "* A floating-point value, e.g. `time_unit=3.1557e13`\n", "\n", "In the latter case, the unit is assumed to be cgs. \n", "\n", "The resulting `ds` functions exactly like a dataset like any other yt can handle--it can be sliced, and we can show the grid boundaries:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"z\", (\"gas\", \"density\"))\n", "slc.set_cmap((\"gas\", \"density\"), \"Blues\")\n", "slc.annotate_grids(cmap=None)\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Particle fields are detected as one-dimensional fields. Particle fields are then added as one-dimensional arrays in\n", "a similar manner as the three-dimensional grid fields:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "posx_arr = prng.uniform(low=-1.5, high=1.5, size=10000)\n", "posy_arr = prng.uniform(low=-1.5, high=1.5, size=10000)\n", "posz_arr = prng.uniform(low=-1.5, high=1.5, size=10000)\n", "data = {\n", " \"density\": (prng.random(size=(64, 64, 64)), \"Msun/kpc**3\"),\n", " \"particle_position_x\": (posx_arr, \"code_length\"),\n", " \"particle_position_y\": (posy_arr, \"code_length\"),\n", " \"particle_position_z\": (posz_arr, \"code_length\"),\n", "}\n", "bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])\n", "ds = yt.load_uniform_grid(\n", " data,\n", " data[\"density\"][0].shape,\n", " length_unit=(1.0, \"Mpc\"),\n", " mass_unit=(1.0, \"Msun\"),\n", " bbox=bbox,\n", " nprocs=4,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example only the particle position fields have been assigned. If no particle arrays are supplied, then the number of particles is assumed to be zero. Take a slice, and overlay particle positions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"z\", (\"gas\", \"density\"))\n", "slc.set_cmap((\"gas\", \"density\"), \"Blues\")\n", "slc.annotate_particles(0.25, p_size=12.0, col=\"Red\")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### HDF5 data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "HDF5 is a convenient format to store data. If you have unigrid data stored in an HDF5 file, it is possible to load it into memory and then use `load_uniform_grid` to get it into yt:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "from os.path import join\n", "\n", "import h5py\n", "\n", "from yt.config import ytcfg\n", "\n", "data_dir = ytcfg.get(\"yt\", \"test_data_dir\")\n", "from yt.utilities.physical_ratios import cm_per_kpc\n", "\n", "f = h5py.File(\n", " join(data_dir, \"UnigridData\", \"turb_vels.h5\"), \"r\"\n", ") # Read-only access to the file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The HDF5 file handle's keys correspond to the datasets stored in the file:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(f.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to add some unit information. It may be stored in the file somewhere, or we may know it from another source. In this case, the units are simply cgs:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "units = [\n", " \"gauss\",\n", " \"gauss\",\n", " \"gauss\",\n", " \"g/cm**3\",\n", " \"erg/cm**3\",\n", " \"K\",\n", " \"cm/s\",\n", " \"cm/s\",\n", " \"cm/s\",\n", " \"cm/s\",\n", " \"cm/s\",\n", " \"cm/s\",\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can iterate over the items in the file handle and the units to get the data into a dictionary, which we will then load:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "data = {k: (v[()], u) for (k, v), u in zip(f.items(), units)}\n", "bbox = np.array([[-0.5, 0.5], [-0.5, 0.5], [-0.5, 0.5]])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_uniform_grid(\n", " data,\n", " data[\"Density\"][0].shape,\n", " length_unit=250.0 * cm_per_kpc,\n", " bbox=bbox,\n", " nprocs=8,\n", " periodicity=(False, False, False),\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, the data came from a simulation which was 250 kpc on a side. An example projection of two fields:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds, \"z\", [\"z-velocity\", \"Temperature\", \"Bx\"], weight_field=\"Density\"\n", ")\n", "prj.set_log(\"z-velocity\", False)\n", "prj.set_log(\"Bx\", False)\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Volume Rendering Loaded Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Volume rendering requires defining a `TransferFunction` to map data to color and opacity and a `camera` to create a viewport and render the image." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "# Find the min and max of the field\n", "mi, ma = ds.all_data().quantities.extrema(\"Temperature\")\n", "# Reduce the dynamic range\n", "mi = mi.value + 1.5e7\n", "ma = ma.value - 0.81e7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define the properties and size of the `camera` viewport:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "# Choose a vector representing the viewing direction.\n", "L = [0.5, 0.5, 0.5]\n", "# Define the center of the camera to be the domain center\n", "c = ds.domain_center[0]\n", "# Define the width of the image\n", "W = 1.5 * ds.domain_width[0]\n", "# Define the number of pixels to render\n", "Npixels = 512" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a `camera` object and " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sc = yt.create_scene(ds, \"Temperature\")\n", "dd = ds.all_data()\n", "\n", "source = sc[0]\n", "\n", "source.log_field = False\n", "\n", "tf = yt.ColorTransferFunction((mi, ma), grey_opacity=False)\n", "tf.map_to_colormap(mi, ma, scale=15.0, colormap=\"cmyt.algae\")\n", "\n", "source.set_transfer_function(tf)\n", "\n", "sc.add_source(source)\n", "\n", "cam = sc.add_camera()\n", "cam.width = W\n", "cam.center = c\n", "cam.normal_vector = L\n", "cam.north_vector = [0, 0, 1]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sc.show(sigma_clip=4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### FITS image data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The FITS file format is a common astronomical format for 2-D images, but it can store three-dimensional data as well. The [AstroPy](https://www.astropy.org) project has modules for FITS reading and writing." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "import astropy.io.fits as pyfits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using `pyfits` we can open a FITS file. If we call `info()` on the file handle, we can figure out some information about the file's contents. The file in this example has a primary HDU (header-data-unit) with no data, and three HDUs with 3-D data. In this case, the data consists of three velocity fields:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "f = pyfits.open(join(data_dir, \"UnigridData\", \"velocity_field_20.fits\"))\n", "f.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can put it into a dictionary in the same way as before, but we slice the file handle `f` so that we don't use the `PrimaryHDU`. `hdu.name` is the field name and `hdu.data` is the actual data. Each of these velocity fields is in km/s. We can check that we got the correct fields. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "data = {}\n", "for hdu in f:\n", " name = hdu.name.lower()\n", " data[name] = (hdu.data, \"km/s\")\n", "print(data.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The velocity field names in this case are slightly different than the standard yt field names for velocity fields, so we will reassign the field names:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "data[\"velocity_x\"] = data.pop(\"x-velocity\")\n", "data[\"velocity_y\"] = data.pop(\"y-velocity\")\n", "data[\"velocity_z\"] = data.pop(\"z-velocity\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we load the data into yt. Let's assume that the box size is a Mpc. Since these are velocity fields, we can overlay velocity vectors on slices, just as if we had loaded in data from a supported code. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_uniform_grid(data, data[\"velocity_x\"][0].shape, length_unit=(1.0, \"Mpc\"))\n", "slc = yt.SlicePlot(\n", " ds, \"x\", [(\"gas\", \"velocity_x\"), (\"gas\", \"velocity_y\"), (\"gas\", \"velocity_z\")]\n", ")\n", "for ax in \"xyz\":\n", " slc.set_log((\"gas\", f\"velocity_{ax}\"), False)\n", "slc.annotate_velocity()\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generic AMR Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In a similar fashion to unigrid data, data gridded into rectangular patches at varying levels of resolution may also be loaded into yt. In this case, a list of grid dictionaries should be provided, with the requisite information about each grid's properties. This example sets up two grids: a top-level grid (`level == 0`) covering the entire domain and a subgrid at `level == 1`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "grid_data = [\n", " {\n", " \"left_edge\": [0.0, 0.0, 0.0],\n", " \"right_edge\": [1.0, 1.0, 1.0],\n", " \"level\": 0,\n", " \"dimensions\": [32, 32, 32],\n", " },\n", " {\n", " \"left_edge\": [0.25, 0.25, 0.25],\n", " \"right_edge\": [0.75, 0.75, 0.75],\n", " \"level\": 1,\n", " \"dimensions\": [32, 32, 32],\n", " },\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll just fill each grid with random density data, with a scaling with the grid refinement level." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "for g in grid_data:\n", " g[\"density\"] = (prng.random(g[\"dimensions\"]) * 2 ** g[\"level\"], \"g/cm**3\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Particle fields are supported by adding 1-dimensional arrays to each `grid`. If a grid has no particles, the particle fields still have to be defined since they are defined elsewhere; set them to empty NumPy arrays:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "grid_data[0][\"particle_position_x\"] = (\n", " np.array([]),\n", " \"code_length\",\n", ") # No particles, so set empty arrays\n", "grid_data[0][\"particle_position_y\"] = (np.array([]), \"code_length\")\n", "grid_data[0][\"particle_position_z\"] = (np.array([]), \"code_length\")\n", "grid_data[1][\"particle_position_x\"] = (\n", " prng.uniform(low=0.25, high=0.75, size=1000),\n", " \"code_length\",\n", ")\n", "grid_data[1][\"particle_position_y\"] = (\n", " prng.uniform(low=0.25, high=0.75, size=1000),\n", " \"code_length\",\n", ")\n", "grid_data[1][\"particle_position_z\"] = (\n", " prng.uniform(low=0.25, high=0.75, size=1000),\n", " \"code_length\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, call `load_amr_grids`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_amr_grids(grid_data, [32, 32, 32])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`load_amr_grids` also takes the same keywords `bbox` and `sim_time` as `load_uniform_grid`. We could have also specified the length, time, velocity, and mass units in the same manner as before. Let's take a slice:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds, \"z\", (\"gas\", \"density\"))\n", "slc.annotate_particles(0.25, p_size=15.0, col=\"Pink\")\n", "slc.show()" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Species fields" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "One can also supply species fields to a stream dataset, in the form of mass fractions. These will then be used to generate derived fields for mass, number, and nuclei densities of the separate species. The naming conventions for the mass fractions should correspond to the format specified in [the yt documentation for species fields](https://yt-project.org/doc/analyzing/fields.html#species-fields)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "arr = prng.random(size=(64, 64, 64))\n", "data = {\n", " \"density\": (arr, \"g/cm**3\"),\n", " \"H_p0_fraction\": (0.37 * np.ones_like(arr), \"dimensionless\"),\n", " \"H_p1_fraction\": (0.37 * np.ones_like(arr), \"dimensionless\"),\n", " \"He_fraction\": (0.24 * np.ones_like(arr), \"dimensionless\"),\n", " \"CO_fraction\": (0.02 * np.ones_like(arr), \"dimensionless\"),\n", "}\n", "bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])\n", "ds = yt.load_uniform_grid(data, arr.shape, length_unit=\"Mpc\", bbox=bbox, nprocs=64)\n", "dd = ds.all_data()\n", "print(dd.mean((\"gas\", \"CO_density\")))\n", "print(dd.min((\"gas\", \"H_nuclei_density\")))\n", "print(dd.max((\"gas\", \"He_number_density\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple Particle Types" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For both uniform grid data and AMR data, one can specify particle fields with multiple types if the particle field names are given as field tuples instead of strings (the default particle type is `\"io\"`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "posxr_arr = prng.uniform(low=-1.5, high=1.5, size=10000)\n", "posyr_arr = prng.uniform(low=-1.5, high=1.5, size=10000)\n", "poszr_arr = prng.uniform(low=-1.5, high=1.5, size=10000)\n", "posxb_arr = prng.uniform(low=-1.5, high=1.5, size=20000)\n", "posyb_arr = prng.uniform(low=-1.5, high=1.5, size=20000)\n", "poszb_arr = prng.uniform(low=-1.5, high=1.5, size=20000)\n", "data = {\n", " (\"gas\", \"density\"): (prng.random(size=(64, 64, 64)), \"Msun/kpc**3\"),\n", " (\"red\", \"particle_position_x\"): (posxr_arr, \"code_length\"),\n", " (\"red\", \"particle_position_y\"): (posyr_arr, \"code_length\"),\n", " (\"red\", \"particle_position_z\"): (poszr_arr, \"code_length\"),\n", " (\"blue\", \"particle_position_x\"): (posxb_arr, \"code_length\"),\n", " (\"blue\", \"particle_position_y\"): (posyb_arr, \"code_length\"),\n", " (\"blue\", \"particle_position_z\"): (poszb_arr, \"code_length\"),\n", "}\n", "bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])\n", "ds = yt.load_uniform_grid(\n", " data,\n", " data[\"gas\", \"density\"][0].shape,\n", " length_unit=(1.0, \"Mpc\"),\n", " mass_unit=(1.0, \"Msun\"),\n", " bbox=bbox,\n", " nprocs=4,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now see we have multiple particle types:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dd = ds.all_data()\n", "print(ds.particle_types)\n", "print(dd[\"red\", \"particle_position_x\"].size)\n", "print(dd[\"blue\", \"particle_position_x\"].size)\n", "print(dd[\"all\", \"particle_position_x\"].size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Caveats for Loading Generic Array Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Particles may be difficult to integrate.\n", "* Data must already reside in memory before loading it in to yt, whether it is generated at runtime or loaded from disk. \n", "* Some functions may behave oddly, and parallelism will be disappointing or non-existent in most cases.\n", "* No consistency checks are performed on the hierarchy\n", "* Consistency between particle positions and grids is not checked; `load_amr_grids` assumes that particle positions associated with one grid are not bounded within another grid at a higher level, so this must be ensured by the user prior to loading the grid data. " ] } ], "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/examining/Loading_Generic_Particle_Data.ipynb000066400000000000000000000172421510711153200275340ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading Generic Particle Data\n", "\n", "This example creates a fake in-memory particle dataset and then loads it as a yt dataset using the `load_particles` function.\n", "\n", "Our \"fake\" dataset will be numpy arrays filled with normally distributed randoml particle positions and uniform particle masses. Since real data is often scaled, I arbitrarily multiply by 1e6 to show how to deal with scaled data." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "\n", "rng = np.random.default_rng()\n", "n_particles = 5_000_000\n", "\n", "ppx, ppy, ppz = 1e6 * rng.normal(size=(3, n_particles))\n", "\n", "ppm = np.ones(n_particles)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `load_particles` function accepts a dictionary populated with particle data fields loaded in memory as numpy arrays or python lists:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "data = {\n", " (\"io\", \"particle_position_x\"): ppx,\n", " (\"io\", \"particle_position_y\"): ppy,\n", " (\"io\", \"particle_position_z\"): ppz,\n", " (\"io\", \"particle_mass\"): ppm,\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To hook up with yt's internal field system, the dictionary keys must be 'particle_position_x', 'particle_position_y', 'particle_position_z', and 'particle_mass', as well as any other particle field provided by one of the particle frontends." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `load_particles` function transforms the `data` dictionary into an in-memory yt `Dataset` object, providing an interface for further analysis with yt. The example below illustrates how to load the data dictionary we created above." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import yt\n", "from yt.units import Msun, parsec\n", "\n", "bbox = 1.1 * np.array(\n", " [[min(ppx), max(ppx)], [min(ppy), max(ppy)], [min(ppz), max(ppz)]]\n", ")\n", "\n", "ds = yt.load_particles(data, length_unit=1.0 * parsec, mass_unit=1e8 * Msun, bbox=bbox)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `length_unit` and `mass_unit` are the conversion from the units used in the `data` dictionary to CGS. I've arbitrarily chosen one parsec and 10^8 Msun for this example. \n", "\n", "The `n_ref` parameter controls how many particle it takes to accumulate in an oct-tree cell to trigger refinement. Larger `n_ref` will decrease poisson noise at the cost of resolution in the octree. \n", "\n", "Finally, the `bbox` parameter is a bounding box in the units of the dataset that contains all of the particles. This is used to set the size of the base octree block." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This new dataset acts like any other yt `Dataset` object, and can be used to create data objects and query for yt fields." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ad = ds.all_data()\n", "print(ad.mean((\"io\", \"particle_position_x\")))\n", "print(ad.sum((\"io\", \"particle_mass\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can project the particle mass field like so:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "prj = yt.ParticleProjectionPlot(ds, \"z\", (\"io\", \"particle_mass\"))\n", "prj.set_width((8, \"Mpc\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, one can specify multiple particle types in the `data` directory by setting the field names to be field tuples (the default field type for particles is `\"io\"`) if one is not specified:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n_gas_particles = 1_000_000\n", "n_star_particles = 1_000_000\n", "n_dm_particles = 2_000_000\n", "\n", "ppxg, ppyg, ppzg = 1e6 * rng.normal(size=(3, n_gas_particles))\n", "ppmg = np.ones(n_gas_particles)\n", "hsml = 10000 * np.ones(n_gas_particles)\n", "dens = 2.0e-4 * np.ones(n_gas_particles)\n", "\n", "ppxd, ppyd, ppzd = 1e6 * rng.normal(size=(3, n_dm_particles))\n", "ppmd = np.ones(n_dm_particles)\n", "\n", "ppxs, ppys, ppzs = 5e5 * rng.normal(size=(3, n_star_particles))\n", "ppms = 0.1 * np.ones(n_star_particles)\n", "\n", "bbox = 1.1 * np.array(\n", " [\n", " [\n", " min(ppxg.min(), ppxd.min(), ppxs.min()),\n", " max(ppxg.max(), ppxd.max(), ppxs.max()),\n", " ],\n", " [\n", " min(ppyg.min(), ppyd.min(), ppys.min()),\n", " max(ppyg.max(), ppyd.max(), ppys.max()),\n", " ],\n", " [\n", " min(ppzg.min(), ppzd.min(), ppzs.min()),\n", " max(ppzg.max(), ppzd.max(), ppzs.max()),\n", " ],\n", " ]\n", ")\n", "\n", "data2 = {\n", " (\"gas\", \"particle_position_x\"): ppxg,\n", " (\"gas\", \"particle_position_y\"): ppyg,\n", " (\"gas\", \"particle_position_z\"): ppzg,\n", " (\"gas\", \"particle_mass\"): ppmg,\n", " (\"gas\", \"smoothing_length\"): hsml,\n", " (\"gas\", \"density\"): dens,\n", " (\"dm\", \"particle_position_x\"): ppxd,\n", " (\"dm\", \"particle_position_y\"): ppyd,\n", " (\"dm\", \"particle_position_z\"): ppzd,\n", " (\"dm\", \"particle_mass\"): ppmd,\n", " (\"star\", \"particle_position_x\"): ppxs,\n", " (\"star\", \"particle_position_y\"): ppys,\n", " (\"star\", \"particle_position_z\"): ppzs,\n", " (\"star\", \"particle_mass\"): ppms,\n", "}\n", "\n", "ds2 = yt.load_particles(\n", " data2, length_unit=1.0 * parsec, mass_unit=1e8 * Msun, bbox=bbox\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now have separate `\"gas\"`, `\"dm\"`, and `\"star\"` particles. Since the `\"gas\"` particles have `\"density\"` and `\"smoothing_length\"` fields, they are recognized as SPH particles:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ad = ds2.all_data()\n", "c = np.array([ad.mean((\"gas\", ax)).to(\"code_length\") for ax in \"xyz\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "slc = yt.SlicePlot(ds2, \"z\", (\"gas\", \"density\"), center=c)\n", "slc.set_zlim((\"gas\", \"density\"), 1e-19, 2.0e-18)\n", "slc.set_width((4, \"Mpc\"))\n", "slc.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/examining/Loading_Spherical_Data.ipynb000066400000000000000000000164351510711153200262520ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Loading Spherical Data\n", "\n", "Support in yt for non-Cartesian geometries is partial and is still being extended, but here is an example of how to load spherical data from a regularly-spaced grid. For irregularly spaced grids, a similar setup can be used, either by using supplying the `cell_widths` parameter to `load_uniform_grid` (see [Stretched Grid Data](https://yt-project.org/docs/dev/examining/loading_data.html#stretched-grid-data) for more details), or by using the `load_hexahedral_mesh` method.\n", "\n", "Note that in yt, \"spherical\" means that it is ordered $r$, $\\theta$, $\\phi$, where $\\theta$ is the declination from the azimuth (running from $0$ to $\\pi$ and $\\phi$ is the angle around the zenith (running from $0$ to $2\\pi$).\n", "\n", "We first start out by loading yt." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "\n", "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we create a few derived fields. The first three are just straight translations of the Cartesian coordinates, so that we can see where we are located in the data, and understand what we're seeing. The final one is just a fun field that is some combination of the three coordinates, and will vary in all dimensions." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "@yt.derived_field(name=\"sphx\", units=\"cm\", take_log=False, sampling_type=\"cell\")\n", "def sphx(field, data):\n", " return np.cos(data[\"phi\"]) * np.sin(data[\"theta\"]) * data[\"r\"]\n", "\n", "\n", "@yt.derived_field(name=\"sphy\", units=\"cm\", take_log=False, sampling_type=\"cell\")\n", "def sphy(field, data):\n", " return np.sin(data[\"phi\"]) * np.sin(data[\"theta\"]) * data[\"r\"]\n", "\n", "\n", "@yt.derived_field(name=\"sphz\", units=\"cm\", take_log=False, sampling_type=\"cell\")\n", "def sphz(field, data):\n", " return np.cos(data[\"theta\"]) * data[\"r\"]\n", "\n", "\n", "@yt.derived_field(name=\"funfield\", units=\"cm\", take_log=False, sampling_type=\"cell\")\n", "def funfield(field, data):\n", " return (np.sin(data[\"phi\"]) ** 2 + np.cos(data[\"theta\"]) ** 2) * (\n", " 1.0 * data[\"r\"].uq + data[\"r\"]\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading Data\n", "\n", "Now we can actually load our data. We use the `load_uniform_grid` function here. Normally, the first argument would be a dictionary of field data, where the keys were the field names and the values the field data arrays. Here, we're just going to look at derived fields, so we supply an empty one.\n", "\n", "The next few arguments are the number of dimensions, the bounds, and we then specify the geometry as spherical." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ds = yt.load_uniform_grid(\n", " {},\n", " [128, 128, 128],\n", " bbox=np.array([[0.0, 1.0], [0.0, np.pi], [0.0, 2 * np.pi]]),\n", " geometry=\"spherical\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looking at Data\n", "\n", "Now we can take slices. The first thing we will try is making a slice of data along the \"phi\" axis, here $\\pi/2$, which will be along the y axis in the positive direction. We use the `.slice` attribute, which creates a slice, and then we convert this into a plot window. Note that here 2 is used to indicate the third axis (0-indexed) which for spherical data is $\\phi$.\n", "\n", "This is the manual way of creating a plot -- below, we'll use the standard, automatic ways. Note that the coordinates run from $-r$ to $r$ along the $z$ axis and from $0$ to $r$ along the $R$ axis. We use the capital $R$ to indicate that it's the $R$ along the $x-y$ plane." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s = ds.slice(2, np.pi / 2)\n", "p = s.to_pw(\"funfield\", origin=\"native\")\n", "p.set_zlim(\"all\", 0.0, 4.0)\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also slice along $r$. For now, this creates a regular grid with *incorrect* units for phi and theta. We are currently exploring two other options -- a simple aitoff projection, and fixing it to use the correct units as-is." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s = yt.SlicePlot(ds, \"r\", \"funfield\")\n", "s.set_zlim(\"all\", 0.0, 4.0)\n", "s.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also slice at constant $\\theta$. But, this is a weird thing! We're slicing at a constant declination from the azimuth. What this means is that when thought of in a Cartesian domain, this slice is actually a cone. The axes have been labeled appropriately, to indicate that these are not exactly the $x$ and $y$ axes, but instead differ by a factor of $\\sin(\\theta))$." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s = yt.SlicePlot(ds, \"theta\", \"funfield\")\n", "s.set_zlim(\"all\", 0.0, 4.0)\n", "s.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've seen lots of the `funfield` plots, but we can also look at the Cartesian axes. This next plot plots the Cartesian $x$, $y$ and $z$ values on a $\\theta$ slice. Because we're not supplying an argument to the `center` parameter, yt will place it at the center of the $\\theta$ axis, which will be at $\\pi/2$, where it will be aligned with the $x-y$ plane. The slight change in `sphz` results from the cells themselves migrating, and plotting the center of those cells." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s = yt.SlicePlot(ds, \"theta\", [\"sphx\", \"sphy\", \"sphz\"])\n", "s.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do the same with the $\\phi$ axis." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "s = yt.SlicePlot(ds, \"phi\", [\"sphx\", \"sphy\", \"sphz\"])\n", "s.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/examining/index.rst000066400000000000000000000013661510711153200225450ustar00rootroot00000000000000.. _examining-data: Loading and Examining Data ========================== Nominally, one should just be able to run ``yt.load()`` on a dataset and start computing; however, there may be additional notes associated with different data formats as described below. Furthermore, we provide methods for loading data from unsupported data formats in :doc:`Loading_Generic_Array_Data`, :doc:`Loading_Generic_Particle_Data`, and :doc:`Loading_Spherical_Data`. Lastly, if you want to examine the raw data for your particular dataset, visit :ref:`low-level-data-inspection`. .. toctree:: :maxdepth: 2 loading_data Loading_Generic_Array_Data Loading_Generic_Particle_Data Loading_Data_via_Functions Loading_Spherical_Data low_level_inspection yt-project-yt-f043ac8/doc/source/examining/loading_data.rst000066400000000000000000003666171510711153200240610ustar00rootroot00000000000000.. _loading-data: Loading Data ============ This section contains information on how to load data into yt, as well as some important caveats about different data formats. .. _loading-sample-data: Sample Data ----------- The yt community has provided a large number of sample datasets, which are accessible from https://yt-project.org/data/ . yt also provides a helper function, ``yt.load_sample``, that can load from a set of sample datasets. The quickstart notebooks in this documentation utilize this. The files are, in general, named identically to their listings on the data catalog page. For instance, you can load ``IsolatedGalaxy`` by executing: .. code-block:: python import yt ds = yt.load_sample("IsolatedGalaxy") To find a list of all available datasets, you can call ``load_sample`` without any arguments, and it will return a list of the names that can be supplied: .. code-block:: python import yt yt.load_sample() This will return a list of possible filenames; more information can be accessed on the data catalog. .. _loading-archived-data: Archived Data ------------- If your data is stored as a (compressed) tar file, you can access the contained dataset directly without extracting the tar file. This can be achieved using the ``load_archive`` function: .. code-block:: python import yt ds = yt.load_archive("IsolatedGalaxy.tar.gz", "IsolatedGalaxy/galaxy0030/galaxy0030") The first argument is the path to the archive file, the second one is the path to the file to load in the archive. Subsequent arguments are passed to ``yt.load``. The functionality requires the package `ratarmount `_ to be installed. Under the hood, yt will mount the archive as a (read-only) filesystem. Note that this requires the entire archive to be read once to compute the location of each file in the archive; subsequent accesses will be much faster. All archive formats supported by `ratarmount `__ should be loadable, provided the dependencies are installed; this includes ``tar``, ``tar.gz`` and tar.bz2`` formats. .. _loading-hdf5-data: Simple HDF5 Data ---------------- .. note:: This wrapper takes advantage of the functionality described in :doc:`Loading_Data_via_Functions` but the basics of setting up function handlers, guessing fields, etc, are handled by yt. Using the function :func:`yt.loaders.load_hdf5_file`, you can load a generic set of fields from an HDF5 file and have a fully-operational yt dataset. For instance, in the yt sample data repository, we have the `UniGrid Data `_ dataset (~1.6GB). This dataset includes the file ``turb_vels.h5`` with this structure: .. code-block:: bash $ h5ls -r h5ls -r ./UnigridData/turb_vels.h5 / Group /Bx Dataset {256, 256, 256} /By Dataset {256, 256, 256} /Bz Dataset {256, 256, 256} /Density Dataset {256, 256, 256} /MagneticEnergy Dataset {256, 256, 256} /Temperature Dataset {256, 256, 256} /turb_x-velocity Dataset {256, 256, 256} /turb_y-velocity Dataset {256, 256, 256} /turb_z-velocity Dataset {256, 256, 256} /x-velocity Dataset {256, 256, 256} /y-velocity Dataset {256, 256, 256} /z-velocity Dataset {256, 256, 256} In versions of yt prior to 4.1, these could be loaded into memory individually and then accessed *en masse* by the :func:`yt.loaders.load_uniform_grid` function. Introduced in version 4.1, however, was the ability to provide the filename and then allow yt to identify the available fields and even subset them into chunks to preserve memory. Only those requested fields will be loaded at the time of the request, and they will be subset into chunks to avoid over-allocating for reduction operations. To use the auto-loader, call :func:`~yt.loaders.load_hdf5_file` with the name of the file. Optionally, you can specify the root node of the file to probe for fields -- for instance, if all of the fields are stored under ``/grid`` (as they are in output from the ytdata frontend). You can also provide the expected bounding box, which will otherwise default to 0..1 in all dimensions, the names of fields to make available (by default yt will probe for them) and the number of chunks to subdivide the file into. If the number of chunks is not specified it defaults to trying to keep the size of each individual chunk no more than $64^3$ zones. To load the above file, we would use the function as follows: .. code-block:: python import yt ds = yt.load_hdf5_file("UnigridData/turb_vels.h5") At this point, we now have a dataset that we can do all of our normal operations on, and all of the known yt derived fields will be available. .. _loading-amrvac-data: AMRVAC Data ----------- To load data to yt, simply use .. code-block:: import yt ds = yt.load("output0010.dat") .. rubric:: Dataset geometry & periodicity Starting from AMRVAC 2.2, and datfile format 5, a geometry flag (e.g. "Cartesian_2.5D", "Polar_2D", "Cylindrical_1.5D"...) was added to the datfile header. yt will fall back to a cartesian mesh if the geometry flag is not found. For older datfiles however it is possible to provide it externally with the ``geometry_override`` parameter. .. code-block:: python # examples ds = yt.load("output0010.dat", geometry_override="polar") ds = yt.load("output0010.dat", geometry_override="cartesian") Note that ``geometry_override`` has priority over any ``geometry`` flag present in recent datfiles, which means it can be used to force ``r`` VS ``theta`` 2D plots in polar geometries (for example), but this may produce unpredictable behaviour and comes with no guarantee. A ``ndim``-long ``periodic`` boolean array was also added to improve compatibility with yt. See http://amrvac.org/md_doc_fileformat.html for details. .. rubric:: Auto-setup for derived fields Yt will attempt to mimic the way AMRVAC internally defines kinetic energy, pressure, and sound speed. To see a complete list of fields that are defined after loading, one can simply type .. code-block:: python print(ds.derived_field_list) Note that for adiabatic (magneto-)hydrodynamics, i.e. ``(m)hd_energy = False`` in AMRVAC, additional input data is required in order to setup some of these fields. This is done by passing the corresponding parfile(s) at load time .. code-block:: python # example using a single parfile ds = yt.load("output0010.dat", parfiles="amrvac.par") # ... or using multiple parfiles ds = yt.load("output0010.dat", parfiles=["amrvac.par", "modifier.par"]) In case more than one parfile is passed, yt will create a single namelist by replicating AMRVAC's rules (see "Using multiple par files" http://amrvac.org/md_doc_commandline.html). .. rubric:: Unit System AMRVAC only supports dimensionless fields and as such, no unit system is ever attached to any given dataset. yt however defines physical quantities and give them units. As is customary in yt, the default unit system is ``cgs``, e.g. lengths are read as "cm" unless specified otherwise. The user has two ways to control displayed units, through ``unit_system`` (``"cgs"``, ``"mks"`` or ``"code"``) and ``units_override``. Example: .. code-block:: python units_override = dict(length_unit=(100.0, "au"), mass_unit=yt.units.mass_sun) ds = yt.load("output0010.dat", units_override=units_override, unit_system="mks") To ensure consistency with normalisations as used in AMRVAC we only allow overriding a maximum of three units. Allowed unit combinations at the moment are .. code-block:: none {numberdensity_unit, temperature_unit, length_unit} {mass_unit, temperature_unit, length_unit} {mass_unit, time_unit, length_unit} {numberdensity_unit, velocity_unit, length_unit} {mass_unit, velocity_unit, length_unit} Appropriate errors are thrown for other combinations. .. rubric:: Partially supported and unsupported features * a maximum of 100 dust species can be read by yt at the moment. If your application needs this limit increased, please report an issue https://github.com/yt-project/yt/issues * particle data: currently not supported (but might come later) * staggered grids (AMRVAC 2.2 and later): yt logs a warning if you load staggered datasets, but the flag is currently ignored. * "stretched grids" are being implemented in yt, but are not yet fully-supported. (Previous versions of this file suggested they would "never" be supported, which we hope to prove incorrect once we finish implementing stretched grids in AMR. At present, stretched grids are only supported on a single level of refinement.) .. note:: Ghost cells exist in .dat files but never read by yt. .. _loading-art-data: ART Data -------- ART data has been supported in the past by Christopher Moody and is currently cared for by Kenza Arraki. Please contact the ``yt-dev`` mailing list if you are interested in using yt for ART data, or if you are interested in assisting with development of yt to work with ART data. To load an ART dataset you can use the ``yt.load`` command and provide it the gas mesh file. It will search for and attempt to find the complementary dark matter and stellar particle header and data files. However, your simulations may not follow the same naming convention. .. code-block:: python import yt ds = yt.load("D9p_500/10MpcBox_HartGal_csf_a0.500.d") It will search for and attempt to find the complementary dark matter and stellar particle header and data files. However, your simulations may not follow the same naming convention. For example, the single snapshot given in the sample data has a series of files that look like this: .. code-block:: none 10MpcBox_HartGal_csf_a0.500.d #Gas mesh PMcrda0.500.DAT #Particle header PMcrs0a0.500.DAT #Particle data (positions,velocities) stars_a0.500.dat #Stellar data (metallicities, ages, etc.) The ART frontend tries to find the associated files matching the above, but if that fails you can specify ``file_particle_header``, ``file_particle_data``, and ``file_particle_stars``, in addition to specifying the gas mesh. Note that the ``pta0.500.dat`` or ``pt.dat`` file containing particle time steps is not loaded by yt. You also have the option of gridding particles and assigning them onto the meshes. This process is in beta, and for the time being, it's probably best to leave ``do_grid_particles=False`` as the default. To speed up the loading of an ART file, you have a few options. You can turn off the particles entirely by setting ``discover_particles=False``. You can also only grid octs up to a certain level, ``limit_level=5``, which is useful when debugging by artificially creating a 'smaller' dataset to work with. Finally, when stellar ages are computed we 'spread' the ages evenly within a smoothing window. By default this is turned on and set to 10Myr. To turn this off you can set ``spread=False``, and you can tweak the age smoothing window by specifying the window in seconds, ``spread=1.0e7*365*24*3600``. There is currently preliminary support for dark matter only ART data. To load a dataset use the ``yt.load`` command and provide it the particle data file. It will search for the complementary particle header file. .. code-block:: python import yt ds = yt.load("PMcrs0a0.500.DAT") Important: This should not be used for loading just the dark matter data for a 'regular' hydrodynamical data set as the units and IO are different! .. _loading-artio-data: ARTIO Data ---------- ARTIO data has a well-specified internal parameter system and has few free parameters. However, for optimization purposes, the parameter that provides the most guidance to yt as to how to manage ARTIO data is ``max_range``. This governs the maximum number of space-filling curve cells that will be used in a single "chunk" of data read from disk. For small datasets, setting this number very large will enable more data to be loaded into memory at any given time; for very large datasets, this parameter can be left alone safely. By default it is set to 1024; it can in principle be set as high as the total number of SFC cells. To load ARTIO data, you can specify a command such as this: .. code-block:: python ds = load("./A11QR1/s11Qzm1h2_a1.0000.art") .. _loading-athena-data: Athena Data ----------- Athena 4.x VTK data is supported and cared for by John ZuHone. Both uniform grid and SMR datasets are supported. .. note:: yt also recognizes Fargo3D data written to VTK files as Athena data, but support for Fargo3D data is preliminary. Loading Athena datasets is slightly different depending on whether your dataset came from a serial or a parallel run. If the data came from a serial run or you have joined the VTK files together using the Athena tool ``join_vtk``, you can load the data like this: .. code-block:: python import yt ds = yt.load("kh.0010.vtk") The filename corresponds to the file on SMR level 0, whereas if there are multiple levels the corresponding files will be picked up automatically, assuming they are laid out in ``lev*`` subdirectories under the directory where the base file is located. For parallel datasets, yt assumes that they are laid out in directories named ``id*``, one for each processor number, each with ``lev*`` subdirectories for additional refinement levels. To load this data, call ``load`` with the base file in the ``id0`` directory: .. code-block:: python import yt ds = yt.load("id0/kh.0010.vtk") which will pick up all of the files in the different ``id*`` directories for the entire dataset. The default unit system in yt is cgs ("Gaussian") units, but Athena data is not normally stored in these units, so the code unit system is the default unit system for Athena data. This means that answers to field queries from data objects and plots of data will be expressed in code units. Note that the default conversions from these units will still be in terms of cgs units, e.g. 1 ``code_length`` equals 1 cm, and so on. If you would like to provided different conversions, you may supply conversions for length, time, and mass to ``load`` using the ``units_override`` functionality: .. code-block:: python import yt units_override = { "length_unit": (1.0, "Mpc"), "time_unit": (1.0, "Myr"), "mass_unit": (1.0e14, "Msun"), } ds = yt.load("id0/cluster_merger.0250.vtk", units_override=units_override) This means that the yt fields, e.g. ``("gas","density")``, ``("gas","velocity_x")``, ``("gas","magnetic_field_x")``, will be in cgs units (or whatever unit system was specified), but the Athena fields, e.g., ``("athena","density")``, ``("athena","velocity_x")``, ``("athena","cell_centered_B_x")``, will be in code units. The default normalization for various magnetic-related quantities such as magnetic pressure, Alfven speed, etc., as well as the conversion between magnetic code units and other units, is Gaussian/CGS, meaning that factors of :math:`4\pi` or :math:`\sqrt{4\pi}` will appear in these quantities, e.g. :math:`p_B = B^2/8\pi`. To use the Lorentz-Heaviside normalization instead, in which the factors of :math:`4\pi` are dropped (:math:`p_B = B^2/2), for example), set ``magnetic_normalization="lorentz_heaviside"`` in the call to ``yt.load``: .. code-block:: python ds = yt.load( "id0/cluster_merger.0250.vtk", units_override=units_override, magnetic_normalization="lorentz_heaviside", ) Some 3D Athena outputs may have large grids (especially parallel datasets subsequently joined with the ``join_vtk`` script), and may benefit from being subdivided into "virtual grids". For this purpose, one can pass in the ``nprocs`` parameter: .. code-block:: python import yt ds = yt.load("sloshing.0000.vtk", nprocs=8) which will subdivide each original grid into ``nprocs`` grids. Note that this parameter is independent of the number of MPI tasks assigned to analyze the data set in parallel (see :ref:`parallel-computation`), and ideally should be (much) larger than this. .. note:: Virtual grids are only supported (and really only necessary) for 3D data. Alternative values for the following simulation parameters may be specified using a ``parameters`` dict, accepting the following keys: * ``gamma``: ratio of specific heats, Type: Float. If not specified, :math:`\gamma = 5/3` is assumed. * ``geometry``: Geometry type, currently accepts ``"cartesian"`` or ``"cylindrical"``. Default is ``"cartesian"``. * ``periodicity``: Is the domain periodic? Type: Tuple of boolean values corresponding to each dimension. Defaults to ``True`` in all directions. * ``mu``: mean molecular weight, Type: Float. If not specified, :math:`\mu = 0.6` (for a fully ionized primordial plasma) is assumed. .. code-block:: python import yt parameters = { "gamma": 4.0 / 3.0, "geometry": "cylindrical", "periodicity": (False, False, False), } ds = yt.load("relativistic_jet_0000.vtk", parameters=parameters) .. rubric:: Caveats * yt primarily works with primitive variables. If the Athena dataset contains conservative variables, the yt primitive fields will be generated from the conserved variables on disk. * Special relativistic datasets may be loaded, but at this time not all of their fields are fully supported. In particular, the relationships between quantities such as pressure and thermal energy will be incorrect, as it is currently assumed that their relationship is that of an ideal a :math:`\gamma`-law equation of state. This will be rectified in a future release. * Domains may be visualized assuming periodicity. * Particle list data is currently unsupported. .. _loading-athena-pp-data: Athena++ Data ------------- Athena++ HDF5 data is supported and cared for by John ZuHone. Uniform-grid, SMR, and AMR datasets in cartesian coordinates are fully supported. Support for curvilinear coordinates and/or non-constant grid cell sizes exists, but is preliminary. The default unit system in yt is cgs ("Gaussian") units, but Athena++ data is not normally stored in these units, so the code unit system is the default unit system for Athena++ data. This means that answers to field queries from data objects and plots of data will be expressed in code units. Note that the default conversions from these units will still be in terms of cgs units, e.g. 1 ``code_length`` equals 1 cm, and so on. If you would like to provided different conversions, you may supply conversions for length, time, and mass to ``load`` using the ``units_override`` functionality: .. code-block:: python import yt units_override = { "length_unit": (1.0, "Mpc"), "time_unit": (1.0, "Myr"), "mass_unit": (1.0e14, "Msun"), } ds = yt.load("AM06/AM06.out1.00400.athdf", units_override=units_override) This means that the yt fields, e.g. ``("gas","density")``, ``("gas","velocity_x")``, ``("gas","magnetic_field_x")``, will be in cgs units (or whatever unit system was specified), but the Athena fields, e.g., ``("athena_pp","density")``, ``("athena_pp","vel1")``, ``("athena_pp","Bcc1")``, will be in code units. The default normalization for various magnetic-related quantities such as magnetic pressure, Alfven speed, etc., as well as the conversion between magnetic code units and other units, is Gaussian/CGS, meaning that factors of :math:`4\pi` or :math:`\sqrt{4\pi}` will appear in these quantities, e.g. :math:`p_B = B^2/8\pi`. To use the Lorentz-Heaviside normalization instead, in which the factors of :math:`4\pi` are dropped (:math:`p_B = B^2/2), for example), set ``magnetic_normalization="lorentz_heaviside"`` in the call to ``yt.load``: .. code-block:: python ds = yt.load( "AM06/AM06.out1.00400.athdf", units_override=units_override, magnetic_normalization="lorentz_heaviside", ) Alternative values for the following simulation parameters may be specified using a ``parameters`` dict, accepting the following keys: * ``gamma``: ratio of specific heats, Type: Float. If not specified, :math:`\gamma = 5/3` is assumed. * ``geometry``: Geometry type, currently accepts ``"cartesian"`` or ``"cylindrical"``. Default is ``"cartesian"``. * ``periodicity``: Is the domain periodic? Type: Tuple of boolean values corresponding to each dimension. Defaults to ``True`` in all directions. * ``mu``: mean molecular weight, Type: Float. If not specified, :math:`\mu = 0.6` (for a fully ionized primordial plasma) is assumed. .. rubric:: Caveats * yt primarily works with primitive variables. If the Athena++ dataset contains conservative variables, the yt primitive fields will be generated from the conserved variables on disk. * Special relativistic datasets may be loaded, but at this time not all of their fields are fully supported. In particular, the relationships between quantities such as pressure and thermal energy will be incorrect, as it is currently assumed that their relationship is that of an ideal :math:`\gamma`-law equation of state. This will be rectified in a future release. * Domains may be visualized assuming periodicity. .. _loading-parthenon-data: Parthenon Data -------------- Parthenon HDF5 data is supported and cared for by Forrest Glines and Philipp Grete. The Parthenon framework is the basis for various downstream codes, e.g., `AthenaPK `_, `Phoebus `_, `KHARMA `_, RIOT, and the `parthenon-hydro `_ miniapp. Support for these codes is handled through the common Parthenon frontend with specifics described in the following. Note that only AthenaPK data is currently automatically converted to the standard fields known by yt. For other codes, the raw data of the fields stored in the output file is accessible and a conversion between those fields and yt standard fields needs to be done manually. .. rubric:: Caveats * Reading particle data from Parthenon output is currently not supported. * Spherical and cylindrical coordinates only work for AthenaPK data. * Only periodic boundary conditions are properly handled. Calculating quantities requiring larger stencils (like derivatives) will be incorrect at mesh boundaries that are not periodic. AthenaPK ^^^^^^^^ Fluid data on uniform-grid, SMR, and AMR datasets in Cartesian coordinates are fully supported. AthenaPK data may contain information on units in the output (when specified via the ```` block in the input file when the simulation was originally run). If that information is present, it will be used by yt. Otherwise the default unit system will be the code unit system with conversion of 1 ``code_length`` equalling 1 cm, and so on (given yt's default cgs/"Gaussian" unit system). If you would like to provided different conversions, you may supply conversions for length, time, and mass to ``load`` using the ``units_override`` functionality: .. code-block:: python import yt units_override = { "length_unit": (1.0, "Mpc"), "time_unit": (1.0, "Myr"), "mass_unit": (1.0e14, "Msun"), } ds = yt.load("parthenon.restart.final.rhdf", units_override=units_override) This means that the yt fields, e.g. ``("gas","density")``, ``("gas","velocity_x")``, ``("gas","magnetic_field_x")``, will be in cgs units (or whatever unit system was specified), but the AthenaPK fields, e.g., ``("parthenon","prim_density")``, ``("parthenon","prim_velocity_1")``, ``("parthenon","prim_magnetic_field_1")``, will be in code units. The default normalization for various magnetic-related quantities such as magnetic pressure, Alfven speed, etc., as well as the conversion between magnetic code units and other units, is Gaussian/CGS, meaning that factors of :math:`4\pi` or :math:`\sqrt{4\pi}` will appear in these quantities, e.g. :math:`p_B = B^2/8\pi`. To use the Lorentz-Heaviside normalization instead, in which the factors of :math:`4\pi` are dropped (:math:`p_B = B^2/2), for example), set ``magnetic_normalization="lorentz_heaviside"`` in the call to ``yt.load``: .. code-block:: python ds = yt.load( "parthenon.restart.final.rhdf", units_override=units_override, magnetic_normalization="lorentz_heaviside", ) Alternative values (i.e., overriding the default ones stored in the simulation output) for the following simulation parameters may be specified using a ``parameters`` dict, accepting the following keys: * ``gamma``: ratio of specific heats, Type: Float. If not specified, :math:`\gamma = 5/3` is assumed. * ``mu``: mean molecular weight, Type: Float. If not specified, :math:`\mu = 0.6` (for a fully ionized primordial plasma) is assumed. Other Parthenon based codes ^^^^^^^^^^^^^^^^^^^^^^^^^^^ As mentioned above, a default conversion from code fields to yt fields (e.g., from a density field to ``("gas","density")``) is currently not available -- though more specialized frontends may be added in the future. All raw data of a Parthenon-based simulation output is available through the ``("parthenon","NAME")`` fields where ``NAME`` varies between codes and the respective code documentation should be consulted. One option to manually convert those raw fields to the standard yt fields is by adding derived fields, e.g., for the field named "``mass.density``" that is stored in cgs units on disk: .. code-block:: python from yt import derived_field @derived_field(name="density", units="g*cm**-3", sampling_type="cell") def _density(field, data): return data[("parthenon", "mass.density")] * yt.units.g / yt.units.cm**3 Moreover, an ideal equation of state is assumed with the following parameters, which may be specified using a ``parameters`` dict, accepting the following keys: * ``gamma``: ratio of specific heats, Type: Float. If not specified, :math:`\gamma = 5/3` is assumed. * ``mu``: mean molecular weight, Type: Float. If not specified, :math:`\mu = 0.6` (for a fully ionized primordial plasma) is assumed. .. _loading-orion-data: AMReX / BoxLib Data ------------------- AMReX and BoxLib share a frontend, since the file format is nearly identical. yt has been tested with AMReX/BoxLib data generated by Orion, Nyx, Maestro, Castro, IAMR, and WarpX. Currently it is cared for by a combination of Andrew Myers, Matthew Turk, and Mike Zingale. To load an AMReX/BoxLib dataset, you can use the ``yt.load`` command on the plotfile directory name. In general, you must also have the ``inputs`` file in the base directory, but Maestro, Castro, Nyx, and WarpX will get all the necessary parameter information from the ``job_info`` file in the plotfile directory. For instance, if you were in a directory with the following files: .. code-block:: none inputs pltgmlcs5600/ pltgmlcs5600/Header pltgmlcs5600/Level_0 pltgmlcs5600/Level_0/Cell_H pltgmlcs5600/Level_1 pltgmlcs5600/Level_1/Cell_H pltgmlcs5600/Level_2 pltgmlcs5600/Level_2/Cell_H pltgmlcs5600/Level_3 pltgmlcs5600/Level_3/Cell_H pltgmlcs5600/Level_4 pltgmlcs5600/Level_4/Cell_H You would feed it the filename ``pltgmlcs5600``: .. code-block:: python import yt ds = yt.load("pltgmlcs5600") For Maestro, Castro, Nyx, and WarpX, you would not need the ``inputs`` file, and you would have a ``job_info`` file in the plotfile directory. .. rubric:: Caveats * yt does not read the Maestro base state (although you can have Maestro map it to a full Cartesian state variable before writing the plotfile to get around this). E-mail the dev list if you need this support. * yt supports AMReX/BoxLib particle data stored in the standard format used by Nyx and WarpX, and optionally Castro. It currently does not support the ASCII particle data used by Maestro and Castro. * For Maestro, yt aliases either "tfromp" or "tfromh to" ``temperature`` depending on the value of the ``use_tfromp`` runtime parameter. * For Maestro, some velocity fields like ``velocity_magnitude`` or ``mach_number`` will always use the on-disk value, and not have yt derive it, due to the complex interplay of the base state velocity. Viewing raw fields in WarpX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Most AMReX/BoxLib codes output cell-centered data. If the underlying discretization is not cell-centered, then fields are typically averaged to cell centers before they are written to plot files for visualization. WarpX, however, has the option to output the raw (i.e., not averaged to cell centers) data as well. If you run your WarpX simulation with ``warpx.plot_raw_fields = 1`` in your inputs file, then you should get an additional ``raw_fields`` subdirectory inside your plot file. When you load this dataset, yt will have additional on-disk fields defined, with the "raw" field type: .. code-block:: python import yt ds = yt.load("Laser/plt00015/") print(ds.field_list) The raw fields in WarpX are nodal in at least one direction. We define a field to be "nodal" in a given direction if the field data is defined at the "low" and "high" sides of the cell in that direction, rather than at the cell center. Instead of returning one field value per cell selected, nodal fields return a number of values, depending on their centering. This centering is marked by a ``nodal_flag`` that describes whether the fields is nodal in each dimension. ``nodal_flag = [0, 0, 0]`` means that the field is cell-centered, while ``nodal_flag = [0, 0, 1]`` means that the field is nodal in the z direction and cell centered in the others, i.e. it is defined on the z faces of each cell. ``nodal_flag = [1, 1, 0]`` would mean that the field is centered in the z direction, but nodal in the other two, i.e. it lives on the four cell edges that are normal to the z direction. .. code-block:: python ds.index ad = ds.all_data() print(ds.field_info["raw", "Ex"].nodal_flag) print(ad["raw", "Ex"].shape) print(ds.field_info["raw", "Bx"].nodal_flag) print(ad["raw", "Bx"].shape) print(ds.field_info["raw", "Bx"].nodal_flag) print(ad["raw", "Bx"].shape) Here, the field ``('raw', 'Ex')`` is nodal in two directions, so four values per cell are returned, corresponding to the four edges in each cell on which the variable is defined. ``('raw', 'Bx')`` is nodal in one direction, so two values are returned per cell. The standard, averaged-to-cell-centers fields are still available. Currently, slices and data selection are implemented for nodal fields. Projections, volume rendering, and many of the analysis modules will not work. .. _loading-pluto-data: Pluto Data (AMR) ---------------- Support for Pluto AMR data is provided through the Chombo frontend, which is currently maintained by Andrew Myers. Pluto output files that don't use the Chombo HDF5 format are currently not supported. To load a Pluto dataset, you can use the ``yt.load`` command on the ``*.hdf5`` files. For example, the KelvinHelmholtz sample dataset is a directory that contains the following files: .. code-block:: none data.0004.hdf5 pluto.ini To load it, you can navigate into that directory and do: .. code-block:: python import yt ds = yt.load("data.0004.hdf5") The ``pluto.ini`` file must also be present alongside the HDF5 file. By default, all of the Pluto fields will be in code units. .. _loading-idefix-data: Idefix, Pluto VTK and Pluto XDMF Data ------------------------------------- Support for Idefix ``.dmp``, ``.vtk`` data is provided through the ``yt_idefix`` extension. It also supports monogrid ``.vtk`` and ``.h5`` data from Pluto. See `the PyPI page `_ for details. .. _loading-enzo-data: Enzo Data --------- Enzo data is fully supported and cared for by Matthew Turk. To load an Enzo dataset, you can use the ``yt.load`` command and provide it the dataset name. This would be the name of the output file, and it contains no extension. For instance, if you have the following files: .. code-block:: none DD0010/ DD0010/data0010 DD0010/data0010.index DD0010/data0010.cpu0000 DD0010/data0010.cpu0001 DD0010/data0010.cpu0002 DD0010/data0010.cpu0003 You would feed the ``load`` command the filename ``DD0010/data0010`` as mentioned. .. code-block:: python import yt ds = yt.load("DD0010/data0010") .. rubric:: Caveats * There are no major caveats for Enzo usage * Units should be correct, if you utilize standard unit-setting routines. yt will notify you if it cannot determine the units, although this notification will be passive. * 2D and 1D data are supported, but the extraneous dimensions are set to be of length 1.0 in "code length" which may produce strange results for volume quantities. Enzo MHDCT data ^^^^^^^^^^^^^^^ The electric and magnetic fields for Enzo MHDCT simulations are defined on cell faces, unlike other Enzo fields which are defined at cell centers. In yt, we call face-centered fields like this "nodal". We define a field to be nodal in a given direction if the field data is defined at the "low" and "high" sides of the cell in that direction, rather than at the cell center. Instead of returning one field value per cell selected, nodal fields return a number of values, depending on their centering. This centering is marked by a ``nodal_flag`` that describes whether the fields is nodal in each dimension. ``nodal_flag = [0, 0, 0]`` means that the field is cell-centered, while ``nodal_flag = [0, 0, 1]`` means that the field is nodal in the z direction and cell centered in the others, i.e. it is defined on the z faces of each cell. ``nodal_flag = [1, 1, 0]`` would mean that the field is centered in the z direction, but nodal in the other two, i.e. it lives on the four cell edges that are normal to the z direction. .. code-block:: python ds.index ad = ds.all_data() print(ds.field_info["enzo", "Ex"].nodal_flag) print(ad["enzo", "Ex"].shape) print(ds.field_info["enzo", "BxF"].nodal_flag) print(ad["enzo", "Bx"].shape) print(ds.field_info["enzo", "Bx"].nodal_flag) print(ad["enzo", "Bx"].shape) Here, the field ``('enzo', 'Ex')`` is nodal in two directions, so four values per cell are returned, corresponding to the four edges in each cell on which the variable is defined. ``('enzo', 'BxF')`` is nodal in one direction, so two values are returned per cell. The standard, non-nodal field ``('enzo', 'Bx')`` is also available. Currently, slices and data selection are implemented for nodal fields. Projections, volume rendering, and many of the analysis modules will not work. .. _loading-enzoe-data: Enzo-E Data ----------- Enzo-E outputs have three types of files. .. code-block:: none hello-0200/ hello-0200/hello-0200.block_list hello-0200/hello-0200.file_list hello-0200/hello-0200.hello-c0020-p0000.h5 To load Enzo-E data into yt, provide the block list file: .. code-block:: python import yt ds = yt.load("hello-0200/hello-0200.block_list") Mesh and particle fields are fully supported for 1, 2, and 3D datasets. Enzo-E supports arbitrary particle types defined by the user. The available particle types will be known as soon as the dataset index is created. .. code-block:: python ds = yt.load("ENZOP_DD0140/ENZOP_DD0140.block_list") ds.index print(ds.particle_types) print(ds.particle_type_counts) print(ds.r["dark", "particle_position"]) .. _loading-exodusii-data: Exodus II Data -------------- .. note:: To load Exodus II data, you need to have the `netcdf4 `_ python interface installed. Exodus II is a file format for Finite Element datasets that is used by the MOOSE framework for file IO. Support for this format (and for unstructured mesh data in general) is a new feature as of yt 3.3, so while we aim to fully support it, we also expect there to be some buggy features at present. Currently, yt can visualize quads, hexes, triangles, and tetrahedral element types at first order. Additionally, there is experimental support for the high-order visualization of 20-node hex elements. Development of more high-order visualization capability is a work in progress. To load an Exodus II dataset, you can use the ``yt.load`` command on the Exodus II file: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010", step=0) Because Exodus II datasets can have multiple steps (which can correspond to time steps, Picard iterations, non-linear solve iterations, etc...), you can also specify a step argument when you load an Exodus II data that defines the index at which to look when you read data from the file. Omitting this argument is the same as passing in 0, and setting ``step=-1`` selects the last time output in the file. You can access the connectivity information directly by doing: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010", step=-1) print(ds.index.meshes[0].connectivity_coords) print(ds.index.meshes[0].connectivity_indices) print(ds.index.meshes[1].connectivity_coords) print(ds.index.meshes[1].connectivity_indices) This particular dataset has two meshes in it, both of which are made of 8-node hexes. yt uses a field name convention to access these different meshes in plots and data objects. To see all the fields found in a particular dataset, you can do: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010") print(ds.field_list) This will give you a list of field names like ``('connect1', 'diffused')`` and ``('connect2', 'convected')``. Here, fields labelled with ``'connect1'`` correspond to the first mesh, and those with ``'connect2'`` to the second, and so on. To grab the value of the ``'convected'`` variable at all the nodes in the first mesh, for example, you would do: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010") ad = ds.all_data() # geometric selection, this just grabs everything print(ad["connect1", "convected"]) In this dataset, ``('connect1', 'convected')`` is nodal field, meaning that the field values are defined at the vertices of the elements. If we examine the shape of the returned array: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010") ad = ds.all_data() print(ad["connect1", "convected"].shape) we see that this mesh has 12480 8-node hexahedral elements, and that we get 8 field values for each element. To get the vertex positions at which these field values are defined, we can do, for instance: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010") ad = ds.all_data() print(ad["connect1", "vertex_x"]) If we instead look at an element-centered field, like ``('connect1', 'conv_indicator')``, we get: .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/out.e-s010") ad = ds.all_data() print(ad["connect1", "conv_indicator"].shape) we instead get only one field value per element. For information about visualizing unstructured mesh data, including Exodus II datasets, please see :ref:`unstructured-mesh-slices` and :ref:`unstructured_mesh_rendering`. Displacement Fields ^^^^^^^^^^^^^^^^^^^ Finite element codes often solve for the displacement of each vertex from its original position as a node variable, rather than updating the actual vertex positions with time. For analysis and visualization, it is often useful to turn these displacements on or off, and to be able to scale them arbitrarily to emphasize certain features of the solution. To allow this, if ``yt`` detects displacement fields in an Exodus II dataset (using the convention that they will be named ``disp_x``, ``disp_y``, etc...), it will optionally add these to the mesh vertex positions for the purposes of visualization. Displacement fields can be controlled when a dataset is loaded by passing in an optional dictionary to the ``yt.load`` command. This feature is turned off by default, meaning that a dataset loaded as .. code-block:: python import yt ds = yt.load("MOOSE_sample_data/mps_out.e") will not include the displacements in the vertex positions. The displacements can be turned on separately for each mesh in the file by passing in a tuple of (scale, offset) pairs for the meshes you want to enable displacements for. For example, the following code snippet turns displacements on for the second mesh, but not the first: .. code-block:: python import yt ds = yt.load( "MOOSE_sample_data/mps_out.e", step=10, displacements={"connect2": (1.0, [0.0, 0.0, 0.0])}, ) The displacements can also be scaled by an arbitrary factor before they are added in to the vertex positions. The following code turns on displacements for both ``connect1`` and ``connect2``, scaling the former by a factor of 5.0 and the later by a factor of 10.0: .. code-block:: python import yt ds = yt.load( "MOOSE_sample_data/mps_out.e", step=10, displacements={ "connect1": (5.0, [0.0, 0.0, 0.0]), "connect2": (10.0, [0.0, 0.0, 0.0]), }, ) Finally, we can also apply an arbitrary offset to the mesh vertices after the scale factor is applied. For example, the following code scales all displacements in the second mesh by a factor of 5.0, and then shifts each vertex in the mesh by 1.0 unit in the z-direction: .. code-block:: python import yt ds = yt.load( "MOOSE_sample_data/mps_out.e", step=10, displacements={"connect2": (5.0, [0.0, 0.0, 1.0])}, ) .. _loading-fits-data: FITS Data --------- FITS data is *mostly* supported and cared for by John ZuHone. In order to read FITS data, `AstroPy `_ must be installed. FITS data cubes can be loaded in the same way by yt as other datasets. yt can read FITS image files that have the following (case-insensitive) suffixes: * fits * fts * fits.gz * fts.gz yt can currently read two kinds of FITS files: FITS image files and FITS binary table files containing positions, times, and energies of X-ray events. These are described in more detail below. Types of FITS Datasets Supported by yt ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ yt FITS Data Standard """"""""""""""""""""" yt has facilities for creating 2 and 3-dimensional FITS images from derived, fixed-resolution data products from other datasets. These include images produced from slices, projections, and 3D covering grids. The resulting FITS images are fully-describing in that unit, parameter, and coordinate information is passed from the original dataset. These can be created via the :class:`~yt.visualization.fits_image.FITSImageData` class and its subclasses. For information about how to use these special classes, see :doc:`../visualizing/FITSImageData`. Once you have produced a FITS file in this fashion, you can load it using yt and it will be detected as a ``YTFITSDataset`` object, and it can be analyzed in the same way as any other dataset in yt. Astronomical Image Data """"""""""""""""""""""" These files are one of three types: * Generic two-dimensional FITS images in sky coordinates * Three or four-dimensional "spectral cubes" * *Chandra* event files These FITS images typically are in celestial or galactic coordinates, and for 3D spectral cubes the third axis is typically in velocity, wavelength, or frequency units. For these datasets, since yt does not yet recognize non-spatial axes, the coordinates are in units of the image pixels. The coordinates of these pixels in the WCS coordinate systems will be available in separate fields. Often, the aspect ratio of 3D spectral cubes can be far from unity. Because yt sets the pixel scale as the ``code_length``, certain visualizations (such as volume renderings) may look extended or distended in ways that are undesirable. To adjust the width in ``code_length`` of the spectral axis, set ``spectral_factor`` equal to a constant which gives the desired scaling, or set it to ``"auto"`` to make the width the same as the largest axis in the sky plane: .. code-block:: python ds = yt.load("m33_hi.fits.gz", spectral_factor=0.1) For 4D spectral cubes, the fourth axis is assumed to be composed of different fields altogether (e.g., Stokes parameters for radio data). *Chandra* X-ray event data, which is in tabular form, will be loaded as particle fields in yt, but a grid will be constructed from the WCS information in the FITS header. There is a helper function, ``setup_counts_fields``, which may be used to make deposited image fields from the event data for different energy bands (for an example see :doc:`../cookbook/fits_xray_images`). Generic FITS Images """"""""""""""""""" If the FITS file contains images but does not have adequate header information to fall into one of the above categories, yt will still load the data, but the resulting field and/or coordinate information will necessarily be incomplete. Field names may not be descriptive, and units may be incorrect. To get the full use out of yt for FITS files, make sure that the file is sufficiently self-descripting to fall into one of the above categories. Making the Most of yt for FITS Data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ yt will load data without WCS information and/or some missing header keywords, but the resulting field and/or coordinate information will necessarily be incomplete. For example, field names may not be descriptive, and units will not be correct. To get the full use out of yt for FITS files, make sure that for each image HDU the following standard header keywords have sensible values: * ``CDELTx``: The pixel width in along axis ``x`` * ``CRVALx``: The coordinate value at the reference position along axis ``x`` * ``CRPIXx``: The reference pixel along axis ``x`` * ``CTYPEx``: The projection type of axis ``x`` * ``CUNITx``: The units of the coordinate along axis ``x`` * ``BTYPE``: The type of the image, this will be used as the field name * ``BUNIT``: The units of the image FITS header keywords can easily be updated using AstroPy. For example, to set the ``BTYPE`` and ``BUNIT`` keywords: .. code-block:: python from astropy.io import fits f = fits.open("xray_flux_image.fits", mode="update") f[0].header["BUNIT"] = "cts/s/pixel" f[0].header["BTYPE"] = "flux" f.flush() f.close() FITS Data Decomposition ^^^^^^^^^^^^^^^^^^^^^^^ Though a FITS image is composed of a single array in the FITS file, upon being loaded into yt it is automatically decomposed into grids: .. code-block:: python import yt ds = yt.load("m33_hi.fits") ds.print_stats() .. parsed-literal:: level # grids # cells # cells^3 ---------------------------------------------- 0 512 981940800 994 ---------------------------------------------- 512 981940800 For 3D spectral-cube data, the decomposition into grids will be done along the spectral axis since this will speed up many common operations for this particular type of dataset. yt will generate its own domain decomposition, but the number of grids can be set manually by passing the ``nprocs`` parameter to the ``load`` call: .. code-block:: python ds = yt.load("m33_hi.fits", nprocs=64) Fields in FITS Datasets ^^^^^^^^^^^^^^^^^^^^^^^ Multiple fields can be included in a FITS dataset in several different ways. The first way, and the simplest, is if more than one image HDU is contained within the same file. The field names will be determined by the value of ``BTYPE`` in the header, and the field units will be determined by the value of ``BUNIT``. The second way is if a dataset has a fourth axis, with each slice along this axis corresponding to a different field. In this case, the field names will be determined by the value of the ``CTYPE4`` keyword and the index of the slice. So, for example, if ``BTYPE`` = ``"intensity"`` and ``CTYPE4`` = ``"stokes"``, then the fields will be named ``"intensity_stokes_1"``, ``"intensity_stokes_2"``, and so on. The third way is if auxiliary files are included along with the main file, like so: .. code-block:: python ds = yt.load("flux.fits", auxiliary_files=["temp.fits", "metal.fits"]) The image blocks in each of these files will be loaded as a separate field, provided they have the same dimensions as the image blocks in the main file. Additionally, fields corresponding to the WCS coordinates will be generated based on the corresponding ``CTYPEx`` keywords. When queried, these fields will be generated from the pixel coordinates in the file using the WCS transformations provided by AstroPy. .. note:: Each FITS image from a single dataset, whether from one file or from one of multiple files, must have the same dimensions and WCS information as the first image in the primary file. If this is not the case, yt will raise a warning and will not load this field. .. _additional_fits_options: Additional Options ^^^^^^^^^^^^^^^^^^ The following are additional options that may be passed to the ``load`` command when analyzing FITS data: ``nan_mask`` """""""""""" FITS image data may include ``NaNs``. If you wish to mask this data out, you may supply a ``nan_mask`` parameter, which may either be a single floating-point number (applies to all fields) or a Python dictionary containing different mask values for different fields: .. code-block:: python # passing a single float for all images ds = yt.load("m33_hi.fits", nan_mask=0.0) # passing a dict ds = yt.load("m33_hi.fits", nan_mask={"intensity": -1.0, "temperature": 0.0}) ``suppress_astropy_warnings`` """"""""""""""""""""""""""""" Generally, AstroPy may generate a lot of warnings about individual FITS files, many of which you may want to ignore. If you want to see these warnings, set ``suppress_astropy_warnings = False``. Miscellaneous Tools for Use with FITS Data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A number of tools have been prepared for use with FITS data that enhance yt's visualization and analysis capabilities for this particular type of data. These are included in the ``yt.frontends.fits.misc`` module, and can be imported like so: .. code-block:: python from yt.frontends.fits.misc import PlotWindowWCS, ds9_region, setup_counts_fields ``setup_counts_fields`` """"""""""""""""""""""" This function can be used to create image fields from X-ray counts data in different energy bands: .. code-block:: python ebounds = [(0.1, 2.0), (2.0, 5.0)] # Energies are in keV setup_counts_fields(ds, ebounds) which would make two fields, ``"counts_0.1-2.0"`` and ``"counts_2.0-5.0"``, and add them to the field registry for the dataset ``ds``. ``ds9_region`` """""""""""""" This function takes a `ds9 `_ region and creates a "cut region" data container from it, that can be used to select the cells in the FITS dataset that fall within the region. To use this functionality, the `regions `_ package must be installed. .. code-block:: python ds = yt.load("m33_hi.fits") circle_region = ds9_region(ds, "circle.reg") print(circle_region.quantities.extrema("flux")) ``PlotWindowWCS`` """"""""""""""""" This class takes a on-axis ``SlicePlot`` or ``ProjectionPlot`` of FITS data and adds celestial coordinates to the plot axes. To use it, a version of AstroPy >= 1.3 must be installed. .. code-block:: python wcs_slc = PlotWindowWCS(slc) wcs_slc.show() # for Jupyter notebooks wcs_slc.save() ``WCSAxes`` is still in an experimental state, but as its functionality improves it will be utilized more here. ``create_spectral_slabs`` """"""""""""""""""""""""" .. note:: The following functionality requires the `spectral-cube `_ library to be installed. If you have a spectral intensity dataset of some sort, and would like to extract emission in particular slabs along the spectral axis of a certain width, ``create_spectral_slabs`` can be used to generate a dataset with these slabs as different fields. In this example, we use it to extract individual lines from an intensity cube: .. code-block:: python slab_centers = { "13CN": (218.03117, "GHz"), "CH3CH2CHO": (218.284256, "GHz"), "CH3NH2": (218.40956, "GHz"), } slab_width = (0.05, "GHz") ds = create_spectral_slabs( "intensity_cube.fits", slab_centers, slab_width, nan_mask=0.0 ) All keyword arguments to ``create_spectral_slabs`` are passed on to ``load`` when creating the dataset (see :ref:`additional_fits_options` above). In the returned dataset, the different slabs will be different fields, with the field names taken from the keys in ``slab_centers``. The WCS coordinates on the spectral axis are reset so that the center of the domain along this axis is zero, and the left and right edges of the domain along this axis are :math:`\pm` ``0.5*slab_width``. Examples of Using FITS Data ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following Jupyter notebooks show examples of working with FITS data in yt, which we recommend you look at in the following order: * :doc:`../cookbook/fits_radio_cubes` * :doc:`../cookbook/fits_xray_images` * :doc:`../visualizing/FITSImageData` .. _loading-flash-data: FLASH Data ---------- FLASH HDF5 data is *mostly* supported and cared for by John ZuHone. To load a FLASH dataset, you can use the ``yt.load`` command and provide it the file name of a plot file, checkpoint file, or particle file. Particle files require special handling depending on the situation, the main issue being that they typically lack grid information. The first case is when you have a plotfile and a particle file that you would like to load together. In the simplest case, this occurs automatically. For instance, if you were in a directory with the following files: .. code-block:: none radio_halo_1kpc_hdf5_plt_cnt_0100 # plotfile radio_halo_1kpc_hdf5_part_0100 # particle file where the plotfile and the particle file were created at the same time (therefore having particle data consistent with the grid structure of the former). Notice also that the prefix ``"radio_halo_1kpc_"`` and the file number ``100`` are the same. In this special case, the particle file will be loaded automatically when ``yt.load`` is called on the plotfile. This also works when loading a number of files in a time series. If the two files do not have the same prefix and number, but they nevertheless have the same grid structure and are at the same simulation time, the particle data may be loaded with the ``particle_filename`` optional argument to ``yt.load``: .. code-block:: python import yt ds = yt.load( "radio_halo_1kpc_hdf5_plt_cnt_0100", particle_filename="radio_halo_1kpc_hdf5_part_0100", ) However, if you don't have a corresponding plotfile for a particle file, but would still like to load the particle data, you can still call ``yt.load`` on the file. However, the grid information will not be available, and the particle data will be loaded in a fashion similar to other particle-based datasets in yt. Mean Molecular Weight and Number Density Fields ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The way the mean molecular weight and number density fields are defined depends on what type of simulation you are running. If you are running a simulation without species and a :math:`\gamma`-law equation of state, then the mean molecular weight is defined using the ``eos_singleSpeciesA`` parameter in the FLASH dataset. If you have multiple species and your dataset contains the FLASH field ``"abar"``, then this is used as the mean molecular weight. In either case, the number density field is calculated using this weight. If you are running a FLASH simulation where the fields ``"sumy"`` and ``"ye"`` are present, Then the mean molecular weight is the inverse of ``"sumy"``, and the fields ``"El_number_density"``, ``"ion_number_density"``, and ``"number_density"`` are defined using the following mathematical definitions: * ``"El_number_density"`` :math:`n_e = N_AY_e\rho` * ``"ion_number_density"`` :math:`n_i = N_A\rho/\bar{A}` * ``"number_density"`` :math:`n = n_e + n_i` where :math:`n_e` and :math:`n_i` are the electron and ion number densities, :math:`\rho` is the mass density, :math:`Y_e` is the electron number per baryon, :math:`\bar{A}` is the mean molecular weight, and :math:`N_A` is Avogadro's number. .. rubric:: Caveats * Please be careful that the units are correctly utilized; yt assumes cgs by default, but conversion to other unit systems is also possible. .. _loading-gadget-data: Gadget Data ----------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. yt has support for reading Gadget data in both raw binary and HDF5 formats. It is able to access the particles as it would any other particle dataset, and it can apply smoothing kernels to the data to produce both quantitative analysis and visualization. See :ref:`loading-sph-data` for more details and :doc:`../cookbook/yt_gadget_analysis` for a detailed example of loading, analyzing, and visualizing a Gadget dataset. An example which makes use of a Gadget snapshot from the OWLS project can be found in :doc:`../cookbook/yt_gadget_owls_analysis`. .. note:: If you are loading a multi-file dataset with Gadget, you can either supply the *zeroth* file to the ``load`` command or the directory containing all of the files. For instance, to load the *zeroth* file: ``yt.load("snapshot_061.0.hdf5")`` . To give just the directory, if you have all of your ``snapshot_000.*`` files in a directory called ``snapshot_000``, do: ``yt.load("/path/to/snapshot_000")``. Gadget data in HDF5 format can be loaded with the ``load`` command: .. code-block:: python import yt ds = yt.load("snapshot_061.hdf5") Gadget data in raw binary format can also be loaded with the ``load`` command. This is supported for snapshots created with the ``SnapFormat`` parameter set to 1 or 2. .. code-block:: python import yt ds = yt.load("snapshot_061") .. _particle-bbox: Units and Bounding Boxes ^^^^^^^^^^^^^^^^^^^^^^^^ There are two additional pieces of information that may be needed. If your simulation is cosmological, yt can often guess the bounding box and the units of the simulation. However, for isolated simulations and for cosmological simulations with non-standard units, these must be supplied by the user. For example, if a length unit of 1.0 corresponds to a kiloparsec, you can supply this in the constructor. yt can accept units such as ``Mpc``, ``kpc``, ``cm``, ``Mpccm/h`` and so on. In particular, note that ``Mpc/h`` and ``Mpccm/h`` (``cm`` for comoving here) are usable unit definitions. yt will attempt to use units for ``mass``, ``length``, ``time``, and ``magnetic`` as supplied in the argument ``unit_base``. The ``bounding_box`` argument is a list of two-item tuples or lists that describe the left and right extents of the particles. In this example we load a dataset with a custom bounding box and units. .. code-block:: python bbox = [[-600.0, 600.0], [-600.0, 600.0], [-600.0, 600.0]] unit_base = { "length": (1.0, "kpc"), "velocity": (1.0, "km/s"), "mass": (1.0, "Msun"), } ds = yt.load("snap_004", unit_base=unit_base, bounding_box=bbox) .. warning:: If a ``bounding_box`` argument is supplied and the original dataset has periodic boundaries, it will no longer have periodic boundaries after the bounding box is applied. In addition, you can use ``UnitLength_in_cm``, ``UnitVelocity_in_cm_per_s``, ``UnitMass_in_g``, and ``UnitMagneticField_in_gauss`` as keys for the ``unit_base`` dictionary. These name come from the names used in the Gadget runtime parameter file. This example will initialize a dataset with the same units as the example above: .. code-block:: python unit_base = { "UnitLength_in_cm": 3.09e21, "UnitVelocity_in_cm_per_s": 1e5, "UnitMass_in_g": 1.989e33, } ds = yt.load("snap_004", unit_base=unit_base, bounding_box=bbox) .. _gadget-field-spec: Field Specifications ^^^^^^^^^^^^^^^^^^^^ Binary Gadget outputs often have additional fields or particle types that are non-standard from the default Gadget distribution format. These can be specified in the call to ``GadgetDataset`` by either supplying one of the sets of field specifications as a string or by supplying a field specification itself. As an example, yt has built-in definitions for ``default`` (the default), ``agora_unlv``, ``group0000``, and ``magneticum_box2_hr``. They can be used like this: .. code-block:: python ds = yt.load("snap_100", field_spec="group0000") Field specifications must be tuples, and must be of this format: .. code-block:: python default = ( "Coordinates", "Velocities", "ParticleIDs", "Mass", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("SmoothingLength", "Gas"), ) This is the default specification used by the Gadget frontend. It means that the fields are, in order, Coordinates, Velocities, ParticleIDs, Mass, and the fields InternalEnergy, Density and SmoothingLength *only* for Gas particles. So for example, if you have defined a Metallicity field for the particle type Halo, which comes right after ParticleIDs in the file, you could define it like this: .. code-block:: python import yt my_field_def = ( "Coordinates", "Velocities", "ParticleIDs", ("Metallicity", "Halo"), "Mass", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("SmoothingLength", "Gas"), ) ds = yt.load("snap_100", field_spec=my_field_def) To save time, you can utilize the plugins file for yt and use it to add items to the dictionary where these definitions are stored. You could do this like so: .. code-block:: python import yt from yt.frontends.gadget.definitions import gadget_field_specs gadget_field_specs["my_field_def"] = my_field_def ds = yt.load("snap_100", field_spec="my_field_def") Please also feel free to issue a pull request with any new field specifications, as we're happy to include them in the main distribution! Magneticum halos downloaded using the SIMCUT method from the `Cosmological Web Portal `_ can be loaded using the ``"magneticum_box2_hr"`` value for the ``field_spec`` argumemt. However, this is strictly only true for halos downloaded after May 14, 2021, since before then the halos had the following signature (with the ``"StellarAge"`` field for the ``"Bndry"`` particles missing): .. code-block:: python magneticum_box2_hr = ( "Coordinates", "Velocities", "ParticleIDs", "Mass", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("SmoothingLength", "Gas"), ("ColdFraction", "Gas"), ("Temperature", "Gas"), ("StellarAge", "Stars"), "Potential", ("InitialMass", "Stars"), ("ElevenMetalMasses", ("Gas", "Stars")), ("StarFormationRate", "Gas"), ("TrueMass", "Bndry"), ("AccretionRate", "Bndry"), ) and before November 20, 2020, the field specification had the ``"ParticleIDs"`` and ``"Mass"`` fields swapped: .. code-block:: python magneticum_box2_hr = ( "Coordinates", "Velocities", "Mass", "ParticleIDs", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("SmoothingLength", "Gas"), ("ColdFraction", "Gas"), ("Temperature", "Gas"), ("StellarAge", "Stars"), "Potential", ("InitialMass", "Stars"), ("ElevenMetalMasses", ("Gas", "Stars")), ("StarFormationRate", "Gas"), ("TrueMass", "Bndry"), ("AccretionRate", "Bndry"), ) In general, to determine what fields are in your Gadget binary file, it may be useful to inspect them with the `g3read `_ code first. .. _gadget-species-fields: Gadget Species Fields ^^^^^^^^^^^^^^^^^^^^^ Gas and star particles in Gadget binary and HDF5 files can have fields corresponding to different species fractions or masses. The following field definitions are supported, in the sense that they are automatically detected and will be used to construct species fractions, densities, and number densities after the manner specified in :ref:`species-fields`. For Gadget binary files, the following fields (as specified in the ``field_spec`` argument) are supported: * ``"ElevenMetalMasses"``: 11 mass fields: He, C, Ca, O, N, Ne, Mg, S, Si, Fe, Ej * ``"FourMetalFractions"``: 4 fraction fields: C, O, Si, Fe For Gadget HDF5 files, the fields ``"MetalMasses"`` or ``"Mass Of Metals"`` are supported, with the number of species determined by the size of the dataset's second dimension in the file. Four different numbers of species in these fields are supported, corresponding to the following species: * 7, corresponding to C, N, O, Mg, Si, Fe, Ej * 8, corresponding to He, C, O, Mg, S, Si, Fe, Ej * 11, corresponding to He, C, Ca, O, N, Ne, Mg, S, Si, Fe, Ej * 15, corresponding to He, C, Ca, O, N, Ne, Mg, S, Si, Fe, Na, Al, Ar, Ni, Ej Two points should be noted about the above: the "Ej" species corresponds to the remaining mass of elements heavier than hydrogen and not enumerated, and in the case of 8, 11, and 15 species, hydrogen is assumed to be the remaining mass fraction. Finally, for Gadget HDF5 files, element fields which are of the form ``"X_fraction"`` are also suppoted, and correspond to the mass fraction of element X. .. _gadget-long-ids: Long Particle IDs ^^^^^^^^^^^^^^^^^ Some Gadget binary files use 64-bit integers for particle IDs. To use these, simply set ``long_ids=True`` when loading the dataset: .. code-block:: python import yt ds = yt.load("snap_100", long_ids=True) This is needed, for example, for Magneticum halos downloaded using the SIMCUT method from the `Cosmological Web Portal `_ .. _gadget-ptype-spec: Particle Type Definitions ^^^^^^^^^^^^^^^^^^^^^^^^^ In some cases, research groups add new particle types or re-order them. You can supply alternate particle types by using the keyword ``ptype_spec`` to the ``GadgetDataset`` call. The default for Gadget binary data is: .. code-block:: python ("Gas", "Halo", "Disk", "Bulge", "Stars", "Bndry") You can specify alternate names, but note that this may cause problems with the field specification if none of the names match old names. .. _gadget-header-spec: Header Specification ^^^^^^^^^^^^^^^^^^^^ If you have modified the header in your Gadget binary file, you can specify an alternate header specification with the keyword ``header_spec``. This can either be a list of strings corresponding to individual header types known to yt, or it can be a combination of strings and header specifications. The default header specification (found in ``yt/frontends/sph/definitions.py``) is: .. code-block:: python default = ( ("Npart", 6, "i"), ("Massarr", 6, "d"), ("Time", 1, "d"), ("Redshift", 1, "d"), ("FlagSfr", 1, "i"), ("FlagFeedback", 1, "i"), ("Nall", 6, "i"), ("FlagCooling", 1, "i"), ("NumFiles", 1, "i"), ("BoxSize", 1, "d"), ("Omega0", 1, "d"), ("OmegaLambda", 1, "d"), ("HubbleParam", 1, "d"), ("FlagAge", 1, "i"), ("FlagMEtals", 1, "i"), ("NallHW", 6, "i"), ("unused", 16, "i"), ) These items will all be accessible inside the object ``ds.parameters``, which is a dictionary. You can add combinations of new items, specified in the same way, or alternately other types of headers. The other string keys defined are ``pad32``, ``pad64``, ``pad128``, and ``pad256`` each of which corresponds to an empty padding in bytes. For example, if you have an additional 256 bytes of padding at the end, you can specify this with: .. code-block:: python header_spec = "default+pad256" Note that a single string like this means a single header block. To specify multiple header blocks, use a list of strings instead: .. code-block:: python header_spec = ["default", "pad256"] This can then be supplied to the constructor. Note that you can also define header items manually, for instance with: .. code-block:: python from yt.frontends.gadget.definitions import gadget_header_specs gadget_header_specs["custom"] = (("some_value", 8, "d"), ("another_value", 1, "i")) header_spec = "default+custom" The letters correspond to data types from the Python struct module. Please feel free to submit alternate header types to the main yt repository. .. _specifying-gadget-units: Specifying Units ^^^^^^^^^^^^^^^^ If you are running a cosmology simulation, yt will be able to guess the units with some reliability. However, if you are not and you do not specify a dataset, yt will not be able to and will use the defaults of length being 1.0 Mpc/h (comoving), velocity being in cm/s, and mass being in 10^10 Msun/h. You can specify alternate units by supplying the ``unit_base`` keyword argument of this form: .. code-block:: python unit_base = {"length": (1.0, "cm"), "mass": (1.0, "g"), "time": (1.0, "s")} yt will utilize length, mass and time to set up all other units. .. _loading-swift-data: SWIFT Data ---------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. yt has support for reading in SWIFT data from the HDF5 file format. It is able to access all particles and fields which are stored on-disk and it is also able to generate derived fields, i.e, linear momentum from on-disk fields. It is also possible to smooth the data onto a grid or an octree. This interpolation can be done using an SPH kernel using either the scatter or gather approach. The SWIFT frontend is supported and cared for by Ashley Kelly. SWIFT data in HDF5 format can be loaded with the ``load`` command: .. code-block:: python import yt ds = yt.load("EAGLE_6/eagle_0005.hdf5") .. _arepo-data: Arepo Data ---------- .. note:: For more information about how yt indexes and reads discrete data, set the section :ref:`demeshening`. Arepo data is currently treated as SPH data. The gas cells have smoothing lengths assigned using the following prescription for a given gas cell :math:`i`: .. math:: h_{\rm sml} = \alpha\left(\frac{3}{4\pi}\frac{m_i}{\rho_i}\right)^{1/3} where :math:`\alpha` is a constant factor. By default, :math:`\alpha = 2`. In practice, smoothing lengths are only used for creating slices and projections, and this value of :math:`\alpha` works well for this purpose. However, this value can be changed when loading an Arepo dataset by setting the ``smoothing_factor`` parameter: .. code-block:: python import yt ds = yt.load("snapshot_100.hdf5", smoothing_factor=1.5) Currently, only Arepo HDF5 snapshots are supported. If the "GFM" metal fields are present in your dataset, they will be loaded in and aliased to the appropriate species fields in the ``"GFM_Metals"`` field on-disk. For more information, see the `Illustris TNG documentation `_. If passive scalar fields are present in your dataset, they will be loaded in and aliased to fields with the naming convention ``"PassiveScalars_XX"`` where ``XX`` is the number of the passive scalar array, e.g. ``"00"``, ``"01"``, etc. HDF5 snapshots will be detected as Arepo data if they have the ``"GFM_Metals"`` field present, or if they have a ``"Config"`` group in the header. If neither of these are the case, and your snapshot *is* Arepo data, you can fix this with the following: .. code-block:: python import h5py with h5py.File(saved_filename, "r+") as f: f.create_group("Config") f["/Config"].attrs["VORONOI"] = 1 .. _loading-gamer-data: GAMER Data ---------- GAMER HDF5 data is supported and cared for by Hsi-Yu Schive and John ZuHone. Datasets using hydrodynamics, particles, magnetohydrodynamics, wave dark matter, and special relativistic hydrodynamics are supported. You can load the data like this: .. code-block:: python import yt ds = yt.load("InteractingJets/jet_000002") For simulations without units (i.e., ``OPT__UNIT = 0``), you can supply conversions for length, time, and mass to ``load`` using the ``units_override`` functionality: .. code-block:: python import yt code_units = { "length_unit": (1.0, "kpc"), "time_unit": (3.08567758096e13, "s"), "mass_unit": (1.4690033e36, "g"), } ds = yt.load("InteractingJets/jet_000002", units_override=code_units) Particle data are supported and are always stored in the same file as the grid data. For special relativistic simulations, both the gamma-law and Taub-Mathews EOSes are supported, and the following fields are defined: * ``("gas", "density")``: Comoving rest-mass density :math:`\rho` * ``("gas", "frame_density")``: Coordinate-frame density :math:`D = \gamma\rho` * ``("gas", "gamma")``: Ratio of specific heats :math:`\Gamma` * ``("gas", "four_velocity_[txyz]")``: Four-velocity fields :math:`U_t, U_x, U_y, U_z` * ``("gas", "lorentz_factor")``: Lorentz factor :math:`\gamma = \sqrt{1+U_iU^i/c^2}` (where :math:`i` runs over the spatial indices) * ``("gas", "specific_reduced_enthalpy")``: Specific reduced enthalpy :math:`\tilde{h} = \epsilon + p/\rho` * ``("gas", "specific_enthalpy")``: Specific enthalpy :math:`h = c^2 + \epsilon + p/\rho` These, and other fields following them (3-velocity, energy densities, etc.) are computed in the same manner as in the `GAMER-SR paper `_ to avoid catastrophic cancellations. All of the special relativistic fields will only be available if the ``Temp`` and ``Enth`` fields are present in the dataset, which can be ensured if the runtime options ``OPT__OUTPUT_TEMP = 1`` and ``OPT__OUTPUT_ENTHALPY = 1`` are set in the ``Input__Parameter`` file when running the simulation. This greatly speeds up calculations of the above derived fields in yt. .. rubric:: Caveats * GAMER data in raw binary format (i.e., ``OPT__OUTPUT_TOTAL = "C-binary"``) is not supported. .. _loading-amr-data: Generic AMR Data ---------------- See :doc:`Loading_Generic_Array_Data` and :func:`~yt.frontends.stream.data_structures.load_amr_grids` for more detail. .. note:: It is now possible to load data using *only functions*, rather than using the fully-in-memory method presented here. For more information and examples, see :doc:`Loading_Data_via_Functions`. It is possible to create native yt dataset from Python's dictionary that describes set of rectangular patches of data of possibly varying resolution. .. code-block:: python import yt grid_data = [ dict( left_edge=[0.0, 0.0, 0.0], right_edge=[1.0, 1.0, 1.0], level=0, dimensions=[32, 32, 32], ), dict( left_edge=[0.25, 0.25, 0.25], right_edge=[0.75, 0.75, 0.75], level=1, dimensions=[32, 32, 32], ), ] for g in grid_data: g["density"] = np.random.random(g["dimensions"]) * 2 ** g["level"] ds = yt.load_amr_grids(grid_data, [32, 32, 32], 1.0) .. note:: yt only supports a block structure where the grid edges on the ``n``-th refinement level are aligned with the cell edges on the ``n-1``-th level. Particle fields are supported by adding 1-dimensional arrays to each ``grid``'s dict: .. code-block:: python for g in grid_data: g["particle_position_x"] = np.random.random(size=100000) .. rubric:: Caveats * Some functions may behave oddly, and parallelism will be disappointing or non-existent in most cases. * No consistency checks are performed on the index * Data must already reside in memory. * Consistency between particle positions and grids is not checked; ``load_amr_grids`` assumes that particle positions associated with one grid are not bounded within another grid at a higher level, so this must be ensured by the user prior to loading the grid data. Generic Array Data ------------------ See :doc:`Loading_Generic_Array_Data` and :func:`~yt.frontends.stream.data_structures.load_uniform_grid` for more detail. Even if your data is not strictly related to fields commonly used in astrophysical codes or your code is not supported yet, you can still feed it to yt to use its advanced visualization and analysis facilities. The only requirement is that your data can be represented as one or more uniform, three dimensional numpy arrays. Assuming that you have your data in ``arr``, the following code: .. code-block:: python import yt data = dict(Density=arr) bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [1.5, 1.5]]) ds = yt.load_uniform_grid(data, arr.shape, 3.08e24, bbox=bbox, nprocs=12) will create yt-native dataset ``ds`` that will treat your array as density field in cubic domain of 3 Mpc edge size (3 * 3.08e24 cm) and simultaneously divide the domain into 12 chunks, so that you can take advantage of the underlying parallelism. Particle fields are added as one-dimensional arrays in a similar manner as the three-dimensional grid fields: .. code-block:: python import yt data = dict( Density=dens, particle_position_x=posx_arr, particle_position_y=posy_arr, particle_position_z=posz_arr, ) bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [1.5, 1.5]]) ds = yt.load_uniform_grid(data, arr.shape, 3.08e24, bbox=bbox, nprocs=12) where in this example the particle position fields have been assigned. If no particle fields are supplied, then the number of particles is assumed to be zero. .. rubric:: Caveats * Particles may be difficult to integrate. * Data must already reside in memory. .. _loading-semi-structured-mesh-data: Semi-Structured Grid Data ------------------------- .. note:: With the release of yt-4.1, functionality has been added to allow loading "stretched" grids that are operated on in a more efficient way. This is done via the :func:`~yt.frontends.stream.data_structures.load_uniform_grid` operation, supplying the ``cell_widths`` argument. Using the hexahedral mesh is no longer suggested for situations where the mesh can be adequately described with three arrays of cell widths. See :ref:`loading-stretched-grids` for more information. See :doc:`Loading_Generic_Array_Data`, :func:`~yt.frontends.stream.data_structures.hexahedral_connectivity`, :func:`~yt.frontends.stream.data_structures.load_hexahedral_mesh` for more detail. In addition to uniform grids as described above, you can load in data with non-uniform spacing between datapoints. To load this type of data, you must first specify a hexahedral mesh, a mesh of six-sided cells, on which it will live. You define this by specifying the x,y, and z locations of the corners of the hexahedral cells. The following code: .. code-block:: python import numpy import yt xgrid = numpy.array([-1, -0.65, 0, 0.65, 1]) ygrid = numpy.array([-1, 0, 1]) zgrid = numpy.array([-1, -0.447, 0.447, 1]) coordinates, connectivity = yt.hexahedral_connectivity(xgrid, ygrid, zgrid) will define the (x,y,z) coordinates of the hexahedral cells and information about that cell's neighbors such that the cell corners will be a grid of points constructed as the Cartesian product of xgrid, ygrid, and zgrid. Then, to load your data, which should be defined on the interiors of the hexahedral cells, and thus should have the shape, ``(len(xgrid)-1, len(ygrid)-1, len(zgrid)-1)``, you can use the following code: .. code-block:: python bbox = numpy.array( [ [numpy.min(xgrid), numpy.max(xgrid)], [numpy.min(ygrid), numpy.max(ygrid)], [numpy.min(zgrid), numpy.max(zgrid)], ] ) data = {"density": arr} ds = yt.load_hexahedral_mesh(data, conn, coords, 1.0, bbox=bbox) to load your data into the dataset ``ds`` as described above, where we have assumed your data is stored in the three-dimensional array ``arr``. .. rubric:: Caveats * Integration is not implemented. * Some functions may behave oddly or not work at all. * Data must already reside in memory. .. _loading-stretched-grids: Stretched Grid Data ------------------- .. warning:: API consistency for loading stretched grids is not guaranteed until at least yt 4.2! There may be changes in between then and now, as this is a preliminary feature. With version 4.1, yt has the ability to specify cell widths for grids. This allows situations where a grid has a functional form for cell widths, or where widths are provided in advance. .. note:: At present, stretched grids are restricted to a single level of refinement. Future versions of yt will have more complete and flexible support! To load a stretched grid, you use the standard (and now rather-poorly named) ``load_uniform_grid`` function, but supplying a ``cell_widths`` argument. This argument should be a list of three arrays, corresponding to the first, second and third index-direction cell widths. (For instance, in a "standard" cartesian dataset, this would be x, y, z.) This script, demonstrates loading a simple "random" dataset with a random set of cell-widths. .. code:: python import yt import numpy as np N = 8 data = {"density": np.random.random((N, N, N))} cell_widths = [] for i in range(3): widths = np.random.random(N) widths /= widths.sum() # Normalize to span 0 .. 1. cell_widths.append(widths) ds = yt.load_uniform_grid( data, [N, N, N], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=cell_widths, ) This can be modified to load data from a file, as well as to use more (or fewer) cells. Like with a standard uniform grid, providing ``nprocs>1`` will decompose the domain into multiple grids (without refinement). Unstructured Grid Data ---------------------- See :doc:`Loading_Generic_Array_Data`, :func:`~yt.frontends.stream.data_structures.load_unstructured_mesh` for more detail. In addition to the above grid types, you can also load data stored on unstructured meshes. This type of mesh is used, for example, in many finite element calculations. Currently, hexahedral and tetrahedral mesh elements are supported. To load an unstructured mesh, you need to specify the following. First, you need to have a coordinates array, which should be an (L, 3) array that stores the (x, y, z) positions of all of the vertices in the mesh. Second, you need to specify a connectivity array, which describes how those vertices are connected into mesh elements. The connectivity array should be (N, M), where N is the number of elements and M is the connectivity length, i.e. the number of vertices per element. Finally, you must also specify a data dictionary, where the keys should be the names of the fields and the values should be numpy arrays that contain the field data. These arrays can either supply the cell-averaged data for each element, in which case they would be (N, 1), or they can have node-centered data, in which case they would also be (N, M). Here is an example of how to load an in-memory, unstructured mesh dataset: .. code-block:: python import numpy as np import yt coords = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], dtype=np.float64) connect = np.array([[0, 1, 3], [1, 2, 3]], dtype=np.int64) data = {} data["connect1", "test"] = np.array( [[0.0, 1.0, 3.0], [1.0, 2.0, 3.0]], dtype=np.float64 ) Here, we have made up a simple, 2D unstructured mesh dataset consisting of two triangles and one node-centered data field. This data can be loaded as an in-memory dataset as follows: .. code-block:: python ds = yt.load_unstructured_mesh(connect, coords, data) The in-memory dataset can then be visualized as usual, e.g.: .. code-block:: python sl = yt.SlicePlot(ds, "z", ("connect1", "test")) sl.annotate_mesh_lines() Note that load_unstructured_mesh can take either a single mesh or a list of meshes. To load multiple meshes, you can do: .. code-block:: python import numpy as np import yt coordsMulti = np.array( [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], dtype=np.float64 ) connect1 = np.array( [ [0, 1, 3], ], dtype=np.int64, ) connect2 = np.array( [ [1, 2, 3], ], dtype=np.int64, ) data1 = {} data2 = {} data1["connect1", "test"] = np.array( [ [0.0, 1.0, 3.0], ], dtype=np.float64, ) data2["connect2", "test"] = np.array( [ [1.0, 2.0, 3.0], ], dtype=np.float64, ) connectList = [connect1, connect2] dataList = [data1, data2] ds = yt.load_unstructured_mesh(connectList, coordsMulti, dataList) # only plot the first mesh sl = yt.SlicePlot(ds, "z", ("connect1", "test")) # only plot the second sl = yt.SlicePlot(ds, "z", ("connect2", "test")) # plot both sl = yt.SlicePlot(ds, "z", ("all", "test")) Note that you must respect the field naming convention that fields on the first mesh will have the type ``connect1``, fields on the second will have ``connect2``, etc... .. rubric:: Caveats * Integration is not implemented. * Some functions may behave oddly or not work at all. * Data must already reside in memory. Generic Particle Data --------------------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. See :doc:`Loading_Generic_Particle_Data` and :func:`~yt.frontends.stream.data_structures.load_particles` for more detail. You can also load generic particle data using the same ``stream`` functionality discussed above to load in-memory grid data. For example, if your particle positions and masses are stored in ``positions`` and ``masses``, a vertically-stacked array of particle x,y, and z positions, and a 1D array of particle masses respectively, you would load them like this: .. code-block:: python import yt data = dict(particle_position=positions, particle_mass=masses) ds = yt.load_particles(data) You can also load data using 1D x, y, and z position arrays: .. code-block:: python import yt data = dict( particle_position_x=posx, particle_position_y=posy, particle_position_z=posz, particle_mass=masses, ) ds = yt.load_particles(data) The ``load_particles`` function also accepts the following keyword parameters: ``length_unit`` The units used for particle positions. ``mass_unit`` The units of the particle masses. ``time_unit`` The units used to represent times. This is optional and is only used if your data contains a ``creation_time`` field or a ``particle_velocity`` field. ``velocity_unit`` The units used to represent velocities. This is optional and is only used if you supply a velocity field. If this is not supplied, it is inferred from the length and time units. ``bbox`` The bounding box for the particle positions. .. _smooth-non-sph: Adding Smoothing Lengths for Non-SPH Particles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A novel use of the ``load_particles`` function is to facilitate SPH visualization of non-SPH particles. See the example below: .. code-block:: python import yt # Load dataset and center on the dense region ds = yt.load("FIRE_M12i_ref11/snapshot_600.hdf5") _, center = ds.find_max(("PartType0", "density")) # Reload DM particles into a stream dataset ad = ds.all_data() pt = "PartType1" fields = ["particle_mass"] + [f"particle_position_{ax}" for ax in "xyz"] data = {field: ad[pt, field] for field in fields} ds_dm = yt.load_particles(data, data_source=ad) # Generate the missing SPH fields ds_dm.add_sph_fields() # Make the SPH projection plot p = yt.ProjectionPlot(ds_dm, "z", ("io", "density"), center=center, width=(1, "Mpc")) p.set_unit(("io", "density"), "Msun/kpc**2") p.show() Here we see two new things. First, ``load_particles`` accepts a ``data_source`` argument to infer parameters like code units, which could be tedious to provide otherwise. Second, the returned :class:`~yt.frontends.stream.data_structures.StreamParticleDataset` has an :meth:`~yt.frontends.stream.data_structures.StreamParticleDataset.add_sph_fields` method, to create the ``smoothing_length`` and ``density`` fields required for SPH visualization to work. .. _loading-gizmo-data: Gizmo Data ---------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. Gizmo datasets, including FIRE outputs, can be loaded into yt in the usual manner. Like other SPH data formats, yt loads Gizmo data as particle fields and then uses smoothing kernels to deposit those fields to an underlying grid structure as spatial fields as described in :ref:`loading-gadget-data`. To load Gizmo datasets using the standard HDF5 output format:: import yt ds = yt.load("snapshot_600.hdf5") Because the Gizmo output format is similar to the Gadget format, yt may load Gizmo datasets as Gadget depending on the circumstances, but this should not pose a problem in most situations. FIRE outputs will be loaded accordingly due to the number of metallicity fields found (11 or 17). If ``("PartType0", "MagneticField")`` is present in the output, it would be loaded and aliased to ``("PartType0", "particle_magnetic_field")``. The corresponding component field like ``("PartType0", "particle_magnetic_field_x")`` would be added automatically. Note that ``("PartType4", "StellarFormationTime")`` field has different meanings depending on whether it is a cosmological simulation. For cosmological runs this is the scale factor at the redshift when the star particle formed. For non-cosmological runs it is the time when the star particle formed. (See the `GIZMO User Guide `_) For this reason, ``("PartType4", "StellarFormationTime")`` is loaded as a dimensionless field. We defined two related fields ``("PartType4", "creation_time")``, and ``("PartType4", "age")`` with physical units for your convenience. For Gizmo outputs written as raw binary outputs, you may have to specify a bounding box, field specification, and units as are done for standard Gadget outputs. See :ref:`loading-gadget-data` for more information. .. _halo-catalog-data: Halo Catalog Data ----------------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. yt has support for reading halo catalogs produced by the AdaptaHOP, Amiga Halo Finder (AHF), Rockstar and the inline FOF/SUBFIND halo finders of Gadget and OWLS. The halo catalogs are treated as particle datasets where each particle represents a single halo. For example, this means that the ``"particle_mass"`` field refers to the mass of the halos. For Gadget FOF/SUBFIND catalogs, the member particles for a given halo can be accessed by creating ``halo`` data containers. See :ref:`halo_containers` for more information. If you have access to both the halo catalog and the simulation snapshot from the same redshift, additional analysis can be performed for each halo using :ref:`halo-analysis`. The resulting product can be reloaded in a similar manner to the other halo catalogs shown here. AdataHOP ^^^^^^^^ `AdaptaHOP `_ halo catalogs are loaded by providing the path to the ``tree_bricksXXX`` file. As the halo catalog does not contain all the information about the simulation (for example the cosmological parameters), you also need to pass the parent dataset for it to load correctly. Some fields of note available from AdaptaHOP are: +---------------------+---------------------------+ | Rockstar field | yt field name | +=====================+===========================+ | halo id | particle_identifier | +---------------------+---------------------------+ | halo mass | particle_mass | +---------------------+---------------------------+ | virial mass | virial_mass | +---------------------+---------------------------+ | virial radius | virial_radius | +---------------------+---------------------------+ | virial temperature | virial_temperature | +---------------------+---------------------------+ | halo position | particle_position_(x,y,z) | +---------------------+---------------------------+ | halo velocity | particle_velocity_(x,y,z) | +---------------------+---------------------------+ Numerous other AdataHOP fields exist. To see them, check the field list by typing ``ds.field_list`` for a dataset loaded as ``ds``. Like all other datasets, fields must be accessed through :ref:`Data-objects`. .. code-block:: python import yt parent_ds = yt.load("output_00080/info_00080.txt") ds = yt.load("output_00080_halos/tree_bricks080", parent_ds=parent_ds) ad = ds.all_data() # halo masses print(ad["halos", "particle_mass"]) # halo radii print(ad["halos", "virial_radius"]) Halo Data Containers """""""""""""""""""" Halo member particles are accessed by creating halo data containers with the the halo id and the type of the particles. Scalar values for halos can be accessed in the same way. Halos also have mass, position, velocity, and member ids attributes. .. code-block:: python halo = ds.halo(1, ptype="io") # member particles for this halo print(halo.member_ids) # masses of the halo particles print(halo["io", "particle_mass"]) # halo mass print(halo.mass) In addition, the halo container contains a sphere container. This is the smallest sphere that contains all the halos' particles .. code-block:: python halo = ds.halo(1, ptype="io") sp = halo.sphere # Density in halo sp["gas", "density"] # Entropy in halo sp["gas", "entropy"] .. _ahf: Amiga Halo Finder ^^^^^^^^^^^^^^^^^ Amiga Halo Finder (AHF) halo catalogs are loaded by providing the path to the .parameter files. The corresponding .log and .AHF_halos files must exist for data loading to succeed. The field type for all fields is "halos". Some fields of note available from AHF are: +----------------+---------------------------+ | AHF field | yt field name | +================+===========================+ | ID | particle_identifier | +----------------+---------------------------+ | Mvir | particle_mass | +----------------+---------------------------+ | Rvir | virial_radius | +----------------+---------------------------+ | (X,Y,Z)c | particle_position_(x,y,z) | +----------------+---------------------------+ | V(X,Y,Z)c | particle_velocity_(x,y,z) | +----------------+---------------------------+ Numerous other AHF fields exist. To see them, check the field list by typing ``ds.field_list`` for a dataset loaded as ``ds``. Like all other datasets, fields must be accessed through :ref:`Data-objects`. .. code-block:: python import yt ds = yt.load("ahf_halos/snap_N64L16_135.parameter", hubble_constant=0.7) ad = ds.all_data() # halo masses print(ad["halos", "particle_mass"]) # halo radii print(ad["halos", "virial_radius"]) .. note:: Currently the dimensionless Hubble parameter that yt needs is not provided in AHF outputs. So users need to provide the ``hubble_constant`` (default to 1.0) while loading datasets, as shown above. .. _rockstar: Rockstar ^^^^^^^^ Rockstar halo catalogs are loaded by providing the path to one of the .bin files. In the case where multiple files were produced, one need only provide the path to a single one of them. The field type for all fields is "halos". Some fields of note available from Rockstar are: +----------------+---------------------------+ | Rockstar field | yt field name | +================+===========================+ | halo id | particle_identifier | +----------------+---------------------------+ | virial mass | particle_mass | +----------------+---------------------------+ | virial radius | virial_radius | +----------------+---------------------------+ | halo position | particle_position_(x,y,z) | +----------------+---------------------------+ | halo velocity | particle_velocity_(x,y,z) | +----------------+---------------------------+ Numerous other Rockstar fields exist. To see them, check the field list by typing ``ds.field_list`` for a dataset loaded as ``ds``. Like all other datasets, fields must be accessed through :ref:`Data-objects`. .. code-block:: python import yt ds = yt.load("rockstar_halos/halos_0.0.bin") ad = ds.all_data() # halo masses print(ad["halos", "particle_mass"]) # halo radii print(ad["halos", "virial_radius"]) .. _gadget_fof: Gadget FOF/SUBFIND ^^^^^^^^^^^^^^^^^^ Gadget FOF/SUBFIND halo catalogs work in the same way as those created by :ref:`rockstar`, except there are two field types: ``FOF`` for friend-of-friends groups and ``Subhalo`` for halos found with the SUBFIND substructure finder. Also like Rockstar, there are a number of fields specific to these halo catalogs. +-------------------+---------------------------+ | FOF/SUBFIND field | yt field name | +===================+===========================+ | halo id | particle_identifier | +-------------------+---------------------------+ | halo mass | particle_mass | +-------------------+---------------------------+ | halo position | particle_position_(x,y,z) | +-------------------+---------------------------+ | halo velocity | particle_velocity_(x,y,z) | +-------------------+---------------------------+ | num. of particles | particle_number | +-------------------+---------------------------+ | num. of subhalos | subhalo_number (FOF only) | +-------------------+---------------------------+ Many other fields exist, especially for SUBFIND subhalos. Check the field list by typing ``ds.field_list`` for a dataset loaded as ``ds``. Like all other datasets, fields must be accessed through :ref:`Data-objects`. .. code-block:: python import yt ds = yt.load("gadget_fof_halos/groups_042/fof_subhalo_tab_042.0.hdf5") ad = ds.all_data() # The halo mass print(ad["Group", "particle_mass"]) print(ad["Subhalo", "particle_mass"]) # Halo ID print(ad["Group", "particle_identifier"]) print(ad["Subhalo", "particle_identifier"]) # positions print(ad["Group", "particle_position_x"]) # velocities print(ad["Group", "particle_velocity_x"]) Multidimensional fields can be accessed through the field name followed by an underscore and the index. .. code-block:: python # x component of the spin print(ad["Subhalo", "SubhaloSpin_0"]) .. _halo_containers: Halo Data Containers """""""""""""""""""" Halo member particles are accessed by creating halo data containers with the type of halo ("Group" or "Subhalo") and the halo id. Scalar values for halos can be accessed in the same way. Halos also have mass, position, and velocity attributes. .. code-block:: python halo = ds.halo("Group", 0) # member particles for this halo print(halo["member_ids"]) # halo virial radius print(halo["Group_R_Crit200"]) # halo mass print(halo.mass) Subhalos containers can be created using either their absolute ids or their subhalo ids. .. code-block:: python # first subhalo of the first halo subhalo = ds.halo("Subhalo", (0, 0)) # this subhalo's absolute id print(subhalo.group_identifier) # member particles print(subhalo["member_ids"]) OWLS FOF/SUBFIND ^^^^^^^^^^^^^^^^ OWLS halo catalogs have a very similar structure to regular Gadget halo catalogs. The two field types are ``FOF`` and ``SUBFIND``. See :ref:`gadget_fof` for more information. At this time, halo member particles cannot be loaded. .. code-block:: python import yt ds = yt.load("owls_fof_halos/groups_008/group_008.0.hdf5") ad = ds.all_data() # The halo mass print(ad["FOF", "particle_mass"]) .. _halocatalog: YTHaloCatalog ^^^^^^^^^^^^^ These are catalogs produced by the analysis discussed in :ref:`halo-analysis`. In the case where multiple files were produced, one need only provide the path to a single one of them. The field type for all fields is "halos". The fields available here are similar to other catalogs. Any addition :ref:`halo_catalog_quantities` will also be accessible as fields. +-------------------+---------------------------+ | HaloCatalog field | yt field name | +===================+===========================+ | halo id | particle_identifier | +-------------------+---------------------------+ | virial mass | particle_mass | +-------------------+---------------------------+ | virial radius | virial_radius | +-------------------+---------------------------+ | halo position | particle_position_(x,y,z) | +-------------------+---------------------------+ | halo velocity | particle_velocity_(x,y,z) | +-------------------+---------------------------+ .. code-block:: python import yt ds = yt.load("tiny_fof_halos/DD0046/DD0046.0.h5") ad = ds.all_data() # The halo mass print(ad["halos", "particle_mass"]) Halo Data Containers """""""""""""""""""" Halo particles can be accessed by creating halo data containers with the type of halo ("halos") and the halo id and then querying the "member_ids" field. Halo containers have mass, radius, position, and velocity attributes. Additional fields for which there will be one value per halo can be accessed in the same manner as conventional data containers. .. code-block:: python halo = ds.halo("halos", 0) # particles for this halo print(halo["member_ids"]) # halo properties print(halo.mass, halo.radius, halo.position, halo.velocity) .. _loading-openpmd-data: openPMD Data ------------ `openPMD `_ is an open source meta-standard and naming scheme for mesh based data and particle data. It does not actually define a file format. HDF5-containers respecting the minimal set of meta information from versions 1.0.0 and 1.0.1 of the standard are compatible. Support for the ED-PIC extension is not available. Mesh data in cartesian coordinates and particle data can be read by this frontend. To load the first in-file iteration of a openPMD datasets using the standard HDF5 output format: .. code-block:: python import yt ds = yt.load("example-3d/hdf5/data00000100.h5") If you operate on large files, you may want to modify the virtual chunking behaviour through ``open_pmd_virtual_gridsize``. The supplied value is an estimate of the size of a single read request for each particle attribute/mesh (in Byte). .. code-block:: python import yt ds = yt.load("example-3d/hdf5/data00000100.h5", open_pmd_virtual_gridsize=10e4) sp = yt.SlicePlot(ds, "x", ("openPMD", "rho")) sp.show() Particle data is fully supported: .. code-block:: python import yt ds = yt.load("example-3d/hdf5/data00000100.h5") ad = f.all_data() ppp = yt.ParticlePhasePlot( ad, ("all", "particle_position_y"), ("all", "particle_momentum_y"), ("all", "particle_weighting"), ) ppp.show() .. rubric:: Caveats * 1D, 2D and 3D data is compatible, but lower dimensional data might yield strange results since it gets padded and treated as 3D. Extraneous dimensions are set to be of length 1.0m and have a width of one cell. * The frontend has hardcoded logic for renaming the openPMD ``position`` of particles to ``positionCoarse`` .. _loading-pyne-data: PyNE Data --------- `PyNE `_ is an open source nuclear engineering toolkit maintained by the PyNE development team (pyne-dev@googlegroups.com). PyNE meshes utilize the Mesh-Oriented datABase `(MOAB) `_ and can be Cartesian or tetrahedral. In addition to field data, pyne meshes store pyne Material objects which provide a rich set of capabilities for nuclear engineering tasks. PyNE Cartesian (Hex8) meshes are supported by yt. To create a pyne mesh: .. code-block:: python from pyne.mesh import Mesh num_divisions = 50 coords = linspace(-1, 1, num_divisions) m = Mesh(structured=True, structured_coords=[coords, coords, coords]) Field data can then be added: .. code-block:: python from pyne.mesh import iMeshTag m.neutron_flux = IMeshTag() # neutron_flux_data is a list or numpy array of size num_divisions^3 m.neutron_flux[:] = neutron_flux_data Any field data or material data on the mesh can then be viewed just like any other yt dataset! .. code-block:: python import yt pf = yt.frontends.moab.data_structures.PyneMoabHex8Dataset(m) s = yt.SlicePlot(pf, "z", "neutron_flux") s.display() .. _loading-ramses-data: RAMSES Data ----------- In yt-4.x, RAMSES data is fully supported. If you are interested in taking a development or stewardship role, please contact the yt-dev mailing list. To load a RAMSES dataset, you can use the ``yt.load`` command and provide it the ``info*.txt`` filename. For instance, if you were in a directory with the following files: .. code-block:: none output_00007 output_00007/amr_00007.out00001 output_00007/grav_00007.out00001 output_00007/hydro_00007.out00001 output_00007/info_00007.txt output_00007/part_00007.out00001 You would feed it the filename ``output_00007/info_00007.txt``: .. code-block:: python import yt ds = yt.load("output_00007/info_00007.txt") yt will attempt to guess the fields in the file. For more control over the hydro fields or the particle fields, see :ref:`loading-ramses-data-args`. yt also support the new way particles are handled introduced after version ``stable_17_09`` (the version introduced after the 2017 Ramses User Meeting). In this case, the file ``part_file_descriptor.txt`` containing the different fields in the particle files will be read. If you use a custom version of RAMSES, make sure this file is up-to-date and reflects the true layout of the particles. yt supports outputs made by the mainline ``RAMSES`` code as well as the ``RAMSES-RT`` fork. Files produces by ``RAMSES-RT`` are recognized as such based on the presence of a ``info_rt_*.txt`` file in the output directory. .. note:: for backward compatibility, particles from the ``part_XXXXX.outYYYYY`` files have the particle type ``io`` by default (including dark matter, stars, tracer particles, ...). Sink particles have the particle type ``sink``. .. _loading-ramses-data-args: Arguments passed to the load function ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It is possible to provide extra arguments to the load function when loading RAMSES datasets. Here is a list of the ones specific to RAMSES: ``fields`` A list of fields to read from the hydro files. For example, in a pure hydro simulation with an extra custom field named ``my-awesome-field``, one would specify the fields argument following this example: .. code-block:: python import yt fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "my-awesome-field", ] ds = yt.load("output_00123/info_00123.txt", fields=fields) "my-awesome-field" in ds.field_list # is True ``extra_particle_fields`` A list of tuples describing extra particles fields to read in. By default, yt will try to detect as many fields as possible, assuming the extra ones to be double precision floats. This argument is useful if you have extra fields besides the particle mass, position, and velocity fields that yt cannot detect automatically. For example, for a dataset containing two extra particle integer fields named ``family`` and ``info``, one would do: .. code-block:: python import yt extra_fields = [("family", "I"), ("info", "I")] ds = yt.load("output_00001/info_00001.txt", extra_particle_fields=extra_fields) # ('all', 'family') and ('all', 'info') now in ds.field_list The format of the ``extra_particle_fields`` argument is as follows: ``[('field_name_1', 'type_1'), ..., ('field_name_n', 'type_n')]`` where the second element of the tuple follows the `python struct format convention `_. Note that if ``extra_particle_fields`` is defined, yt will not assume that the ``particle_birth_time`` and ``particle_metallicity`` fields are present in the dataset. If these fields are present, they must be explicitly enumerated in the ``extra_particle_fields`` argument. ``cosmological`` Force yt to consider a simulation to be cosmological or not. This may be useful for some specific simulations e.g. that run down to negative redshifts. ``bbox`` The subbox to load. yt will only read CPUs intersecting with the subbox. This is especially useful for large simulations or zoom-in simulations, where you don't want to have access to data outside of a small region of interest. This argument will prevent yt from loading AMR files outside the subbox and will hence spare memory and time. For example, one could use .. code-block:: python import yt # Only load a small cube of size (0.1)**3 bbox = [[0.0, 0.0, 0.0], [0.1, 0.1, 0.1]] ds = yt.load("output_00001/info_00001.txt", bbox=bbox) # See the note below for the following examples ds.right_edge == [1, 1, 1] # is True ad = ds.all_data() ad["all", "particle_position_x"].max() > 0.1 # _may_ be True bb = ds.box(left_edge=bbox[0], right_edge=bbox[1]) bb["all", "particle_position_x"].max() < 0.1 # is True .. note:: When using the bbox argument, yt will read all the CPUs intersecting with the subbox. However it may also read some data *outside* the selected region. This is due to the fact that domains have a complicated shape when using Hilbert ordering. Internally, yt will hence assume the loaded dataset covers the entire simulation. If you only want the data from the selected region, you may want to use ``ds.box(...)``. .. note:: The ``bbox`` feature is only available for datasets using Hilbert ordering. ``max_level, max_level_convention`` This will set the deepest level to be read from file. Both arguments have to be set, where the convention can be either "ramses" or "yt". In the "ramses" convention, levels go from 1 (the root grid) to levelmax, such that the finest cells have a size of ``boxsize/2**levelmax``. In the "yt" convention, levels are numbered from 0 (the coarsest uniform grid at RAMSES' ``levelmin``) to ``max_level``, such that the finest cells are ``2**max_level`` smaller than the coarsest. .. code-block:: python import yt # Assuming RAMSES' levelmin=6, i.e. the structure is full # down to levelmin=6 ds_all = yt.load("output_00080/info_00080.txt") ds_yt = yt.load("output_00080/info_00080.txt", max_level=2, max_level_convention="yt") ds_ramses = yt.load( "output_00080/info_00080.txt", max_level=8, max_level_convention="ramses", ) any(ds_all.r["index", "grid_level"] > 2) # True all(ds_yt.r["index", "grid_level"] <= 2) # True all(ds_ramses.r["index", "grid_level"] <= 2) # True Adding custom particle fields ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There are three way to make yt detect all the particle fields. For example, if you wish to make yt detect the birth time and metallicity of your particles, use one of these methods 1. ``yt.load`` method. Whenever loading a dataset, add the extra particle fields as a keyword argument to the ``yt.load`` call. .. code-block:: python import yt epf = [("particle_birth_time", "d"), ("particle_metallicity", "d")] ds = yt.load("dataset", extra_particle_fields=epf) ("io", "particle_birth_time") in ds.derived_field_list # is True ("io", "particle_metallicity") in ds.derived_field_list # is True 2. yt config method. If you don't want to pass the arguments for each call of ``yt.load``, you can add in your configuration .. code-block:: none [ramses-particles] fields = """ particle_position_x, d particle_position_y, d particle_position_z, d particle_velocity_x, d particle_velocity_y, d particle_velocity_z, d particle_mass, d particle_identifier, i particle_refinement_level, I particle_birth_time, d particle_metallicity, d """ Each line should contain the name of the field and its data type (``d`` for double precision, ``f`` for single precision, ``i`` for integer and ``l`` for long integer). You can also configure the auto detected fields for fluid types by adding a section ``ramses-hydro``, ``ramses-grav`` or ``ramses-rt`` in the config file. For example, if you customized your gravity files so that they contain the potential, the potential in the previous timestep and the x, y and z accelerations, you can use : .. code-block:: none [ramses-grav] fields = [ "Potential", "Potential-old", "x-acceleration", "y-acceleration", "z-acceleration" ] 3. New RAMSES way. Recent versions of RAMSES automatically write in their output an ``hydro_file_descriptor.txt`` file that gives information about which field is where. If you wish, you can simply create such a file in the folder containing the ``info_xxxxx.txt`` file .. code-block:: none # version: 1 # ivar, variable_name, variable_type 1, position_x, d 2, position_y, d 3, position_z, d 4, velocity_x, d 5, velocity_y, d 6, velocity_z, d 7, mass, d 8, identity, i 9, levelp, i 10, birth_time, d 11, metallicity, d It is important to note that this file should not end with an empty line (but in this case with ``11, metallicity, d``). .. note:: The kind (``i``, ``d``, ``I``, ...) of the field follow the `python convention `_. Customizing the particle type association ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In versions of RAMSES more recent than December 2017, particles carry along a ``family`` array. The value of this array gives the kind of the particle, e.g. 1 for dark matter. It is possible to customize the association between particle type and family by customizing the yt config (see :ref:`configuration-file`), adding .. code-block:: none [ramses-families] gas_tracer = 100 star_tracer = 101 dm = 0 star = 1 Particle ages and formation times ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For non-cosmological simulations, particle ages are stored in physical units on disk. To access the birth time for the particles, use the ``particle_birth_time`` field. The time recorded in this field is relative to the beginning of the simulation. Particles that were present in the initial conditions will have negative values for ``particle_birth_time``. For cosmological simulations that include star particles, RAMSES stores particle formation times as conformal times. To access the formation time field data in conformal units use the ``conformal_birth_time`` field. This will return the formation times of particles in the simulation in conformal units as a dimensionless array. To access the formation time in physical units, use the ``particle_birth_time`` field. Finally, to access the ages of star particles in your simulation, use the ``star_age`` field. Note that this field is defined for all particle types but will only make sense for star particles. For simulations conducted in Newtownian coordinates, with no cosmology or comoving expansion, the time is equal to zero at the beginning of the simulation. That means that particles present in the initial conditions may have negative birth times. This can happen, for example, in idealized isolated galaxy simulations, where star particles are included in the initial conditions. For simulations conducted in cosmological comoving units, the time is equal to zero at the big bang, and all particles should have positive values for the ``particle_birth_time`` field. To help clarify the above discussion, the following table describes the meaning of the various particle formation time and age fields: +------------------+--------------------------+--------------------------------+ | Simulation type | Field name | Description | +==================+==========================+================================+ | cosmological | ``conformal_birth_time`` | Formation time in conformal | | | | units (dimensionless) | +------------------+--------------------------+--------------------------------+ | any | ``particle_birth_time`` | The time relative to the | | | | beginning of the simulation | | | | when the particle was formed. | | | | For non-cosmological | | | | simulations, this field will | | | | have positive values for | | | | particles formed during the | | | | simulation and negative for | | | | particles of finite age in the | | | | initial conditions. For | | | | cosmological simulations this | | | | is the time the particle | | | | formed relative to the big | | | | bang, therefore the value of | | | | this field should be between | | | | 0 and 13.7 Gyr. | +------------------+--------------------------+--------------------------------+ | any | ``star_age`` | Age of the particle. | | | | Only physically meaningful for | | | | stars and particles that | | | | formed dynamically during the | | | | simulation. | +------------------+--------------------------+--------------------------------+ RAMSES datasets produced by a version of the code newer than November 2017 contain the metadata necessary for yt to automatically distinguish between star particles and other particle types. If you are working with a dataset produced by a version of RAMSES older than November 2017, yt will only automatically recognize a single particle ``io``. It may be convenient to define a particle filter in your scripts to distinguish between particles present in the initial conditions and particles that formed dynamically during the simulation by filtering particles with ``"conformal_birth_time"`` values equal to zero and not equal to zero. An example particle filter definition for dynamically formed stars might look like this: .. code-block:: python @yt.particle_filter(requires=["conformal_birth_time"], filtered_type="io") def stars(pfilter, data): filter = data[pfilter.filtered_type, "conformal_birth_time"] != 0 return filter For a cosmological simulation, this filter will distinguish between stars and dark matter particles. .. _loading-sph-data: SPH Particle Data ----------------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. For all of the SPH frontends, yt uses cython-based SPH smoothing onto an in-memory octree to create deposited mesh fields from individual SPH particle fields. This uses a standard M4 smoothing kernel and the ``smoothing_length`` field to calculate SPH sums, filling in the mesh fields. This gives you the ability to both track individual particles (useful for tasks like following contiguous clouds of gas that would be require a clump finder in grid data) as well as doing standard grid-based analysis (i.e. slices, projections, and profiles). The ``smoothing_length`` variable is also useful for determining which particles can interact with each other, since particles more distant than twice the smoothing length do not typically see each other in SPH simulations. By changing the value of the ``smoothing_length`` and then re-depositing particles onto the grid, you can also effectively mimic what your data would look like at lower resolution. .. _loading-tipsy-data: Tipsy Data ---------- .. note:: For more information about how yt indexes and reads particle data, set the section :ref:`demeshening`. See :doc:`../cookbook/tipsy_and_yt` and :ref:`loading-sph-data` for more details. yt also supports loading Tipsy data. Many of its characteristics are similar to how Gadget data is loaded. .. code-block:: python ds = load("./halo1e11_run1.00400") .. _specifying-cosmology-tipsy: Specifying Tipsy Cosmological Parameters and Setting Default Units ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cosmological parameters can be specified to Tipsy to enable computation of default units. For example do the following, to load a Tipsy dataset whose path is stored in the variable ``my_filename`` with specified cosmology parameters: .. code-block:: python cosmology_parameters = { "current_redshift": 0.0, "omega_lambda": 0.728, "omega_matter": 0.272, "hubble_constant": 0.702, } ds = yt.load(my_filename, cosmology_parameters=cosmology_parameters) If you wish to set the unit system directly, you can do so by using the ``unit_base`` keyword in the load statement. .. code-block:: python import yt ds = yt.load(filename, unit_base={"length", (1.0, "Mpc")}) See the documentation for the :class:`~yt.frontends.tipsy.data_structures.TipsyDataset` class for more information. Loading Cosmological Simulations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you are not using a parameter file (i.e. non-Gasoline users), then you must use keyword ``cosmology_parameters`` when loading your data set to indicate to yt that it is a cosmological data set. If you do not wish to set any non-default cosmological parameters, you may pass an empty dictionary. .. code-block:: python import yt ds = yt.load(filename, cosmology_parameters={}) .. _loading-cfradial-data: CfRadial Data ------------- Cf/Radial is a CF compliant netCDF convention for radial data from radar and lidar platforms that supports both airborne and ground-based sensors. Because of its CF-compliance, CfRadial will allow researchers familiar with CF to read the data into a wide variety of analysis tools, models etc. For more see: [CfRadialDoc.v1.4.20160801.pdf](https://github.com/NCAR/CfRadial/blob/d4562a995d0589cea41f4f6a4165728077c9fc9b/docs/CfRadialDoc.v1.4.20160801.pdf) yt provides support for loading cartesian-gridded CfRadial netcdf-4 files as well as polar coordinate Cfradial netcdf-4 files. When loading a standard CfRadial dataset in polar coordinates, yt will first build a sample on a cartesian grid (see :ref:`cfradial_gridding`). To load a CfRadial data file: .. code-block:: python import yt ds = yt.load("CfRadialGrid/grid1.nc") .. _cfradial_gridding: Gridding Behavior ^^^^^^^^^^^^^^^^^ When you load a CfRadial dataset in polar coordinates (elevation, azimuth and range), yt will first build a sample by mapping the data onto a cartesian grid using the Python-ARM Radar Toolkit (`pyart `_). Grid points are found by interpolation of all data points within a specified radius of influence. This data, now in x, y, z coordinate domain is then saved as a new dataset and subsequent loads of the original native CfRadial dataset will use the gridded file. Mapping the data from spherical to Cartesian coordinates is useful for 3D volume rendering the data using yt. See the documentation for the :class:`~yt.frontends.cf_radial.data_structures.CFRadialDataset` class for a description of how to adjust the gridding parameters and storage of the gridded file. yt-project-yt-f043ac8/doc/source/examining/low_level_inspection.rst000066400000000000000000000300121510711153200256470ustar00rootroot00000000000000.. _low-level-data-inspection: Low-Level Data Inspection: Accessing Raw Data ============================================= yt can not only provide high-level access to data, such as through slices, projections, object queries and the like, but it can also provide low-level access to the raw data. .. note:: This section is tuned for patch- or block-based simulations. Future versions of yt will enable more direct access to particle and oct based simulations. For now, these are represented as patches, with the attendant properties. For a more basic introduction, see :ref:`quickstart` and more specifically :doc:`../quickstart/2)_Data_Inspection`. .. _examining-grid-hierarchies: Examining Grid Hierarchies -------------------------- yt organizes grids in a hierarchical fashion; a coarser grid that contains (or overlaps with) a finer grid is referred to as its parent. yt organizes these only a single level of refinement at a time. To access grids, the ``grids`` attribute on a :class:`~yt.geometry.grid_geometry_handler.GridIndex` object. (For fast operations, a number of additional arrays prefixed with ``grid`` are also available, such as ``grid_left_edges`` and so on.) This returns an instance of :class:`~yt.data_objects.grid_patch.AMRGridPatch`, which can be queried for either data or index information. The :class:`~yt.data_objects.grid_patch.AMRGridPatch` object itself provides the following attributes: * ``Children``: a list of grids contained within this one, of one higher level of refinement * ``Parent``: a single object or a list of objects this grid is contained within, one level of refinement coarser * ``child_mask``: a mask of 0's and 1's, representing where no finer data is available in refined grids (1) or where this grid is covered by finer regions (0). Note that to get back the final data contained within a grid, one can multiple a field by this attribute. * ``child_indices``: a mask of booleans, where False indicates no finer data is available. This is essentially the inverse of ``child_mask``. * ``child_index_mask``: a mask of indices into the ``ds.index.grids`` array of the child grids. * ``LeftEdge``: the left edge, in native code coordinates, of this grid * ``RightEdge``: the right edge, in native code coordinates, of this grid * ``dds``: the width of a cell in this grid * ``id``: the id (not necessarily the index) of this grid. Defined such that subtracting the property ``_id_offset`` gives the index into ``ds.index.grids``. * ``NumberOfParticles``: the number of particles in this grid * ``OverlappingSiblings``: a list of sibling grids that this grid overlaps with. Likely only defined for Octree-based codes. In addition, the method :meth:`~yt.data_objects.grid_patch.AMRGridPatch.get_global_startindex` can be used to get the integer coordinates of the upper left edge. These integer coordinates are defined with respect to the current level; this means that they are the offset of the left edge, with respect to the left edge of the domain, divided by the local ``dds``. To traverse a series of grids, this type of construction can be used: .. code-block:: python g = ds.index.grids[1043] g2 = g.Children[1].Children[0] print(g2.LeftEdge) .. _examining-grid-data: Examining Grid Data ------------------- Once you have identified a grid you wish to inspect, there are two ways to examine data. You can either ask the grid to read the data and pass it to you as normal, or you can manually intercept the data from the IO handler and examine it before it has been unit converted. This allows for much more raw data inspection. To access data that has been read in the typical fashion and unit-converted as normal, you can access the grid as you would a normal object: .. code-block:: python g = ds.index.grids[1043] print(g["gas", "density"]) print(g["gas", "density"].min()) To access the raw data (as found in the file), use .. code-block:: python g = ds.index.grids[1043] rho = g["gas", "density"].in_base("code") .. _finding-data-at-fixed-points: Finding Data at Fixed Points ---------------------------- One of the most common questions asked of data is, what is the value *at this specific point*. While there are several ways to find out the answer to this question, a few helper routines are provided as well. To identify the finest-resolution (i.e., most canonical) data at a given point, use the point data object:: from yt.units import kpc point_obj = ds.point([30, 75, 80]*kpc) density_at_point = point_obj['gas', 'density'] The point data object works just like any other yt data object. It is special because it is the only zero-dimensional data object: it will only return data at the exact point specified when creating the point data object. For more information about yt data objects, see :ref:`Data-objects`. If you need to find field values at many points, the :meth:`~yt.data_objects.static_output.Dataset.find_field_values_at_points` function may be more efficient. This function returns a nested list of field values at multiple points in the simulation volume. For example, if one wanted to find the value of a mesh field at the location of the particles in a simulation, one could do:: ad = ds.all_data() ppos = ad["all", "particle_position"] ppos_den_vel = ds.find_field_values_at_points( [("gas", "density"), ("gas", "velocity_x")], ppos ) In this example, ``ppos_den_vel`` will be a list of arrays. The first array will contain the density values at the particle positions, the second will contain the x velocity values at the particle positions. .. _examining-grid-data-in-a-fixed-resolution-array: Examining Grid Data in a Fixed Resolution Array ----------------------------------------------- If you have a dataset, either AMR or single resolution, and you want to just stick it into a fixed resolution numpy array for later examination, then you want to use a :ref:`Covering Grid `. You must specify the maximum level at which to sample the data, a left edge of the data where you will start, and the resolution at which you want to sample. For example, let's use the :ref:`sample dataset ` ``Enzo_64``. This dataset is at a resolution of 64^3 with 5 levels of AMR, so if we want a 64^3 array covering the entire volume and sampling just the lowest level data, we run: .. code-block:: python import yt ds = yt.load("Enzo_64/DD0043/data0043") all_data_level_0 = ds.covering_grid(level=0, left_edge=[0, 0.0, 0.0], dims=[64, 64, 64]) Note that we can also get the same result and rely on the dataset to know its own underlying dimensions: .. code-block:: python all_data_level_0 = ds.covering_grid( level=0, left_edge=[0, 0.0, 0.0], dims=ds.domain_dimensions ) We can now access our underlying data at the lowest level by specifying what :ref:`field ` we want to examine: .. code-block:: python print(all_data_level_0["gas", "density"].shape) # (64, 64, 64) print(all_data_level_0["gas", "density"]) # array([[[ 1.92588925e-31, 1.74647692e-31, 2.54787518e-31, ..., print(all_data_level_0["gas", "temperature"].shape) # (64, 64, 64) If you create a covering grid that spans two child grids of a single parent grid, it will fill those zones covered by a zone of a child grid with the data from that child grid. Where it is covered only by the parent grid, the cells from the parent grid will be duplicated (appropriately) to fill the covering grid. Let's say we now want to look at that entire data volume and sample it at a higher resolution (i.e. level 2). As stated above, we'll be oversampling under-refined regions, but that's OK. We must also increase the resolution of our output array by a factor of 2^2 in each direction to hold this new larger dataset: .. code-block:: python all_data_level_2 = ds.covering_grid( level=2, left_edge=[0, 0.0, 0.0], dims=ds.domain_dimensions * 2**2 ) And let's see what's the density in the central location: .. code-block:: python print(all_data_level_2["gas", "density"].shape) (256, 256, 256) print(all_data_level_2["gas", "density"][128, 128, 128]) 1.7747457571203124e-31 There are two different types of covering grids: unsmoothed and smoothed. Smoothed grids will be filled through a cascading interpolation process; they will be filled at level 0, interpolated to level 1, filled at level 1, interpolated to level 2, filled at level 2, etc. This will help to reduce edge effects. Unsmoothed covering grids will not be interpolated, but rather values will be duplicated multiple times. To sample our dataset from above with a smoothed covering grid in order to reduce edge effects, it is a nearly identical process: .. code-block:: python all_data_level_2_s = ds.smoothed_covering_grid( 2, [0.0, 0.0, 0.0], ds.domain_dimensions * 2**2 ) print(all_data_level_2_s["gas", "density"].shape) (256, 256, 256) print(all_data_level_2_s["gas", "density"][128, 128, 128]) 1.763744852165591e-31 Covering grids can also accept a ``data_source`` argument, in which case only the cells of the covering grid that are contained by the ``data_source`` will be filled. This can be useful to create regularized arrays of more complex geometries. For example, if we provide a sphere, we see that the covering grid shape is the same, but the number of cells with data is less .. code-block:: python sp = ds.sphere(ds.domain_center, (0.25, "code_length")) cg_sp = ds.covering_grid( level=0, left_edge=[0, 0.0, 0.0], dims=ds.domain_dimensions, data_source=sp ) print(cg_sp["gas", "density"].shape) (64, 64, 64) print(cg_sp["gas", "density"].size) 262144 print(cg_sp["gas", "density"][cg_sp["gas", "density"] != 0].size) 17256 The ``data_source`` can be any :ref:`3D Data Container `. Also note that the ``data_source`` argument is only available for the ``covering_grid`` at present (not the ``smoothed_covering_grid``). .. _examining-image-data-in-a-fixed-resolution-array: Examining Image Data in a Fixed Resolution Array ------------------------------------------------ In the same way that one can sample a multi-resolution 3D dataset by placing it into a fixed resolution 3D array as a :ref:`Covering Grid `, one can also access the raw image data that is returned from various yt functions directly as a fixed resolution array. This provides a means for bypassing the yt method for generating plots, and allows the user the freedom to use whatever interface they wish for displaying and saving their image data. You can use the :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer` to accomplish this as described in :ref:`fixed-resolution-buffers`. High-level Information about Particles -------------------------------------- There are a number of high-level helpers attached to ``Dataset`` objects to find out information about the particles in an output file. First, one can check if there are any particles in a dataset at all by examining ``ds.particles_exist``. This will be ``True`` for datasets the include particles and ``False`` otherwise. One can also see which particle types are available in a dataset. Particle types that are available in the dataset's on-disk output are known as "raw" particle types, and they will appear in ``ds.particle_types_raw``. Particle types that are dynamically defined via a particle filter of a particle union will also appear in the ``ds.particle_types`` list. If the simulation only has one particle type on-disk, its name will by ``'io'``. If there is more than one particle type, the names of the particle types will be inferred from the output file. For example, Gadget HDF5 files have particle type names like ``PartType0`` and ``PartType1``, while Enzo data, which usually only has one particle type, will only have a particle named ``io``. Finally, one can see the number of each particle type by inspecting ``ds.particle_type_counts``. This will be a dictionary mapping the names of particle types in ``ds.particle_types_raw`` to the number of each particle type in a simulation output. yt-project-yt-f043ac8/doc/source/faq/000077500000000000000000000000001510711153200174665ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/faq/index.rst000066400000000000000000000377671510711153200213530ustar00rootroot00000000000000.. _faq: Frequently Asked Questions ========================== .. contents:: :depth: 2 :local: :backlinks: none Version & Installation ---------------------- .. _determining-version: How can I tell what version of yt I'm using? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you run into problems with yt and you're writing to the mailing list or contacting developers on Slack, they will likely want to know what version of yt you're using. Often times, you'll want to know both the yt version, as well as the last changeset that was committed to the branch you're using. To reveal this, go to a command line and type: .. code-block:: bash $ yt version The result will look something like this: .. code-block:: bash yt module located at: /Users/mitchell/src/yt-conda/src/yt-git The current version of yt is: --- Version = 4.0.dev0 Changeset = 9f947a930ab4 --- This installation CAN be automatically updated. For more information on this topic, see :ref:`updating`. .. _yt-3.0-problems: I upgraded to yt 4.0 but my code no longer works. What do I do? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We've tried to keep the number of backward-incompatible changes to a minimum with the release of yt-4.0, but because of the wide-reaching changes to how yt manages data, there may be updates you have to make. You can see many of the changes in :ref:`yt4differences`, and in :ref:`transitioning-to-4.0` there are helpful tips on how to modify your scripts to update them. Code Errors and Failures ------------------------ Python fails saying that it cannot import yt modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is commonly exhibited with an error about not being able to import code that is part of yt. This is likely because the code that is failing to import needs to be compiled or recompiled. This error tends to occur when there are changes in the underlying Cython files that need to be rebuilt, like after a major code update or when switching between distant branches. This is solved by running the install command again. See :ref:`install-from-source`. .. _faq-mpi4py: yt complains that it needs the mpi4py module ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For yt to be able to incorporate parallelism on any of its analysis (see :ref:`parallel-computation`), it needs to be able to use MPI libraries. This requires the ``mpi4py`` module to be installed in your version of python. Unfortunately, installation of ``mpi4py`` is *just* tricky enough to elude the yt batch installer. So if you get an error in yt complaining about mpi4py like: .. code-block:: bash ImportError: No module named mpi4py then you should install ``mpi4py``. The easiest way to install it is through the pip interface. At the command line, type: .. code-block:: bash $ python -m pip install mpi4py What this does is it finds your default installation of Python (presumably in the yt source directory), and it installs the mpi4py module. If this action is successful, you should never have to worry about your aforementioned problems again. If, on the other hand, this installation fails (as it does on such machines as NICS Kraken, NASA Pleaides and more), then you will have to take matters into your own hands. Usually when it fails, it is due to pip being unable to find your MPI C/C++ compilers (look at the error message). If this is the case, you can specify them explicitly as per: .. code-block:: bash $ env MPICC=/path/to/MPICC python -m pip install mpi4py So for example, on Kraken, I switch to the gnu C compilers (because yt doesn't work with the portland group C compilers), then I discover that cc is the mpi-enabled C compiler (and it is in my path), so I run: .. code-block:: bash $ module swap PrgEnv-pgi PrgEnv-gnu $ env MPICC=cc python -m pip install mpi4py And voila! It installs! If this *still* fails for you, then you can build and install from source and specify the mpi-enabled c and c++ compilers in the mpi.cfg file. See the `mpi4py installation page `_ for details. Units ----- .. _conversion-factors: How do I convert between code units and physical units for my dataset? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Starting with yt-3.0, and continuing to yt-4.0, yt uses an internal symbolic unit system. In yt-3.0 this was bundled with the main yt codebase, and with yt-4.0 it is now available as a separate package called `unyt `_. Conversion factors are tied up in the ``length_unit``, ``times_unit``, ``mass_unit``, and ``velocity_unit`` attributes, which can be converted to any arbitrary desired physical unit: .. code-block:: python print("Length unit: ", ds.length_unit) print("Time unit: ", ds.time_unit) print("Mass unit: ", ds.mass_unit) print("Velocity unit: ", ds.velocity_unit) print("Length unit: ", ds.length_unit.in_units("code_length")) print("Time unit: ", ds.time_unit.in_units("code_time")) print("Mass unit: ", ds.mass_unit.in_units("kg")) print("Velocity unit: ", ds.velocity_unit.in_units("Mpc/year")) So to accomplish the example task of converting a scalar variable ``x`` in code units to kpc in yt-4.0, you can do one of two things. If ``x`` is already a YTQuantity with units in ``code_length``, you can run: .. code-block:: python x.in_units("kpc") However, if ``x`` is just a numpy array or native python variable without units, you can convert it to a YTQuantity with units of ``kpc`` by running: .. code-block:: python x = x * ds.length_unit.in_units("kpc") For more information about unit conversion, see :ref:`units`. How do I make a YTQuantity tied to a specific dataset's units? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you want to create a variable or array that is tied to a particular dataset (and its specific conversion factor to code units), use the ``ds.quan`` (for individual variables) and ``ds.arr`` (for arrays): .. code-block:: python import yt ds = yt.load(filename) one_Mpc = ds.quan(1, "Mpc") x_vector = ds.arr([1, 0, 0], "code_length") You can then naturally exploit the units system: .. code-block:: python print("One Mpc in code_units:", one_Mpc.in_units("code_length")) print("One Mpc in AU:", one_Mpc.in_units("AU")) print("One Mpc in comoving kpc:", one_Mpc.in_units("kpccm")) For more information about unit conversion, see :ref:`units`. .. _accessing-unitless-data: How do I access the unitless data in a YTQuantity or YTArray? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ While there are numerous benefits to having units tied to individual quantities in yt, they can also produce issues when simply trying to combine YTQuantities with numpy arrays or native python floats that lack units. A simple example of this is:: # Create a YTQuantity that is 1 kpc in length and tied to the units of # dataset ds >>> x = ds.quan(1, 'kpc') # Try to add this to some non-dimensional quantity >>> print(x + 1) YTUnitOperationError: The addition operator for YTArrays with units (kpc) and (1) is not well defined. The solution to this means using the YTQuantity and YTArray objects for all of one's computations, but this isn't always feasible. A quick fix for this is to just grab the unitless data out of a YTQuantity or YTArray object with the ``value`` and ``v`` attributes, which return a copy, or with the ``d`` attribute, which returns the data itself: .. code-block:: python x = ds.quan(1, "kpc") x_val = x.v print(x_val) array(1.0) # Try to add this to some non-dimensional quantity print(x + 1) 2.0 For more information about this functionality with units, see :ref:`units`. Fields ------ .. _faq-handling-log-vs-linear-space: How do I modify whether or not yt takes the log of a particular field? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ yt sets up defaults for many fields for whether or not a field is presented in log or linear space. To override this behavior, you can modify the ``field_info`` dictionary. For example, if you prefer that ``density`` not be logged, you could type: .. code-block:: python ds = load("my_data") ds.index ds.field_info["gas", "density"].take_log = False From that point forward, data products such as slices, projections, etc., would be presented in linear space. Note that you have to instantiate ds.index before you can access ds.field info. For more information see the documentation on :ref:`fields` and :ref:`creating-derived-fields`. .. _faq-new-field: I added a new field to my simulation data, can yt see it? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Yes! yt identifies all the fields in the simulation's output file and will add them to its ``field_list`` even if they aren't listed in :ref:`field-list`. These can then be accessed in the usual manner. For example, if you have created a field for the potential called ``PotentialField``, you could type: .. code-block:: python ds = load("my_data") ad = ds.all_data() potential_field = ad["PotentialField"] The same applies to fields you might derive inside your yt script via :ref:`creating-derived-fields`. To check what fields are available, look at the properties ``field_list`` and ``derived_field_list``: .. code-block:: python print(ds.field_list) print(ds.derived_field_list) or for a more legible version, try: .. code-block:: python for field in ds.derived_field_list: print(field) .. _faq-add-field-diffs: What is the difference between ``yt.add_field()`` and ``ds.add_field()``? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The global ``yt.add_field()`` (:meth:`~yt.fields.field_info_container.FieldInfoContainer.add_field`) function is for adding a field for every subsequent dataset that is loaded in a particular python session, whereas ``ds.add_field()`` (:meth:`~yt.data_objects.static_output.Dataset.add_field`) will only add it to dataset ``ds``. Data Objects ------------ .. _ray-data-ordering: Why are the values in my Ray object out of order? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Using the Ray objects (:class:`~yt.data_objects.selection_data_containers.YTOrthoRay` and :class:`~yt.data_objects.selection_data_containers.YTRay`) with AMR data gives non-contiguous cell information in the Ray's data array. The higher-resolution cells are appended to the end of the array. Unfortunately, due to how data is loaded by chunks for data containers, there is really no easy way to fix this internally. However, there is an easy workaround. One can sort the ``Ray`` array data by the ``t`` field, which is the value of the parametric variable that goes from 0 at the start of the ray to 1 at the end. That way the data will always be ordered correctly. As an example you can: .. code-block:: python my_ray = ds.ray(...) ray_sort = np.argsort(my_ray["t"]) density = my_ray["gas", "density"][ray_sort] There is also a full example in the :ref:`manual-line-plots` section of the docs. Developing ---------- .. _making-a-PR: Someone asked me to make a Pull Request (PR) to yt. How do I do that? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A pull request is the action by which you contribute code to yt. You make modifications in your local copy of the source code, then *request* that other yt developers review and accept your changes to the main code base. For a full description of the steps necessary to successfully contribute code and issue a pull request (or manage multiple versions of the source code) please see :ref:`sharing-changes`. .. _making-an-issue: Someone asked me to file an issue or a bug report for a bug I found. How? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ See :ref:`reporting-a-bug` and :ref:`sharing-changes`. Miscellaneous ------------- .. _getting-sample-data: How can I get some sample data for yt? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Many different sample datasets can be found at https://yt-project.org/data/ . These can be downloaded, unarchived, and they will each create their own directory. It is generally straight forward to load these datasets, but if you have any questions about loading data from a code with which you are unfamiliar, please visit :ref:`loading-data`. To make things easier to load these sample datasets, you can add the parent directory to your downloaded sample data to your *yt path*. If you set the option ``test_data_dir``, in the section ``[yt]``, in ``~/.config/yt/yt.toml``, yt will search this path for them. This means you can download these datasets to ``/big_drive/data_for_yt`` , add the appropriate item to ``~/.config/yt/yt.toml``, and no matter which directory you are in when running yt, it will also check in *that* directory. In many cases, these are also available using the ``load_sample`` command, described in :ref:`loading-sample-data`. .. _faq-scroll-up: I can't scroll-up to previous commands inside python ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If the up-arrow key does not recall the most recent commands, there is probably an issue with the readline library. To ensure the yt python environment can use readline, run the following command: .. code-block:: bash $ python -m pip install gnureadline .. _faq-old-data: .. _faq-log-level: How can I change yt's log level? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ yt's default log level is ``INFO``. However, you may want less voluminous logging, especially if you are in an IPython notebook or running a long or parallel script. On the other hand, you may want it to output a lot more, since you can't figure out exactly what's going wrong, and you want to output some debugging information. The default yt log level can be changed using the :ref:`configuration-file`, either by setting it in the ``$HOME/.config/yt/yt.toml`` file: .. code-block:: bash $ yt config set yt log_level 10 # This sets the log level to "DEBUG" which would produce debug (as well as info, warning, and error) messages, or at runtime: .. code-block:: python yt.set_log_level("error") This is the same as doing: .. code-block:: python yt.set_log_level(40) which in this case would suppress everything below error messages. For reference, the numerical values corresponding to different log levels are: .. csv-table:: :header: Level, Numeric Value :widths: 10, 10 ``CRITICAL``,50 ``ERROR``,40 ``WARNING``,30 ``INFO``,20 ``DEBUG``,10 ``NOTSET``,0 Can I always load custom data objects, fields, quantities, and colormaps with every dataset? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The :ref:`plugin-file` provides a means for always running custom code whenever yt is loaded up. This custom code can be new data objects, or fields, or colormaps, which will then be accessible in any future session without having modified the source code directly. See the description in :ref:`plugin-file` for more details. How do I cite yt? ^^^^^^^^^^^^^^^^^ If you use yt in a publication, we'd very much appreciate a citation! You should feel free to cite the `ApJS paper `_ with the following BibTeX entry: :: @ARTICLE{2011ApJS..192....9T, author = {{Turk}, M.~J. and {Smith}, B.~D. and {Oishi}, J.~S. and {Skory}, S. and {Skillman}, S.~W. and {Abel}, T. and {Norman}, M.~L.}, title = "{yt: A Multi-code Analysis Toolkit for Astrophysical Simulation Data}", journal = {The Astrophysical Journal Supplement Series}, archivePrefix = "arXiv", eprint = {1011.3514}, primaryClass = "astro-ph.IM", keywords = {cosmology: theory, methods: data analysis, methods: numerical }, year = 2011, month = jan, volume = 192, eid = {9}, pages = {9}, doi = {10.1088/0067-0049/192/1/9}, adsurl = {https://ui.adsabs.harvard.edu/abs/2011ApJS..192....9T}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } yt-project-yt-f043ac8/doc/source/help/000077500000000000000000000000001510711153200176475ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/help/index.rst000066400000000000000000000170451510711153200215170ustar00rootroot00000000000000.. _asking-for-help: What to do if you run into problems =================================== If you run into problems with yt, there are a number of steps to follow to come to a solution. The first handful of options are things you can do on your own, but if those don't yield results, we have provided a number of ways to connect with our community of users and developers to solve the problem together. To summarize, here are the steps in order: .. contents:: :depth: 1 :local: :backlinks: none .. _dont-panic: Don't panic and don't give up ----------------------------- This may seem silly, but it's effective. While yt is a robust code with lots of functionality, like all actively-developed codes sometimes there are bugs. Chances are good that your problems have a quick fix, either because someone encountered it before and fixed it, the documentation is out of date, or some other simple solution. Don't give up! We want to help you succeed! .. _update-the-code: Update to the latest version ---------------------------- Sometimes the pace of development is pretty fast on yt, particularly in the development branch, so a fix to your problem may have already been developed by the time you encounter it. Many users' problems can simply be corrected by updating to the latest version of the code and/or its dependencies. If you have installed the latest stable release of yt then you should update yt using the package manager appropriate for your python installation. See :ref:updating. .. _search-the-documentation: Search the documentation, FAQ, and mailing lists ------------------------------------------------ The documentation has a lot of the answers to everyday problems. This doesn't mean you have to read all of the docs top-to-bottom, but you should at least run a search to see if relevant topics have been answered in the docs. Click on the search field to the right of this window and enter your text. Another good place to look for answers in the documentation is our :ref:`faq` page. OK, so there was no obvious solution to your problem in the documentation. It is possible that someone else experienced the problem before you did, and wrote to the mailing list about it. You can easily check the mailing list archive with the other search field to the right of this window (or you can use the search field below). .. raw:: html .. _look-at-the-source: Look at the source code ----------------------- We've done our best to make the source clean, and it is easily searchable from your computer. If you have not done so already (see :ref:`install-from-source`), clone a copy of the yt git repository and make it the 'active' installation by doing Once inside the yt git repository, you can then search for the class, function, or keyword which is giving you problems with ``grep -r *``, which will recursively search throughout the code base. (For a much faster and cleaner experience, we recommend ``grin`` instead of ``grep -r *``. To install ``grin`` with python, just type ``python -m pip install grin``.) So let's say that ``SlicePlot`` is giving you problems still, and you want to look at the source to figure out what is going on. .. code-block:: bash $ cd $YT_GIT/yt $ grep -r SlicePlot * (or $ grin SlicePlot) This will print a number of locations in the yt source tree where ``SlicePlot`` is mentioned. You can now follow-up on this and open up the files that have references to ``SlicePlot`` (particularly the one that defines SlicePlot) and inspect their contents for problems or clarification. .. _isolate_and_document: Isolate and document your problem --------------------------------- As you gear up to take your question to the rest of the community, try to distill your problem down to the fewest number of steps needed to produce it in a script. This can help you (and us) to identify the basic problem. Follow these steps: * Identify what it is that went wrong, and how you knew it went wrong. * Put your script, errors, inputs and outputs online: * ``$ yt pastebin script.py`` - pastes script.py online * ``$ yt upload_image image.png`` - pastes image online * ``$ yt upload my_input.tar`` - pastes my_input.tar online * Identify which version of the code you’re using. * ``$ yt version`` - provides version information, including changeset hash It may be that through the mere process of doing this, you end up solving the problem! .. _irc: Go on Slack to ask a question ----------------------------- If you want a fast, interactive experience, you could try jumping into our Slack to get your questions answered in a chatroom style environment. To join our slack channel you will need to request an invite by going to https://yt-project.org/development.html, click the "Join as @ Slack!" button, and fill out the form. You will get an invite as soon as an administrator approves your request. .. _mailing-list: Ask the mailing list -------------------- If you still haven't yet found a solution, feel free to write to the mailing list regarding your problems. There are two mailing lists, `yt-users `_ and `yt-dev `_. The first should be used for asking for help, suggesting features and so on, and the latter has more chatter about the way the code is developed and discussions of changes and feature improvements. If you email ``yt-users`` asking for help, remember to include the information about your problem you identified in :ref:`this step `. When you email the list, providing this information can help the developers understand what you did, how it went wrong, and any potential fixes or similar problems they have seen in the past. Without this context, it can be very difficult to help out! .. _reporting-a-bug: Submit a bug report ------------------- If you have gone through all of the above steps, and you're still encountering problems, then you have found a bug. To submit a bug report, you can either directly create one through the GitHub `web interface `_. Alternatively, email the ``yt-users`` mailing list and we will construct a new ticket in your stead. Remember to include the information about your problem you identified in :ref:`this step `. Special Issues -------------- Installation Issues ^^^^^^^^^^^^^^^^^^^ If you are having installation issues and nothing from the :ref:`installation instructions ` seems to work, you should *definitely* email the ``yt-users`` email list. You should provide information about the host, the version of the code you are using, and the output of ``yt_install.log`` from your installation. We are very interested in making sure that yt installs everywhere! Customization and Scripting Issues ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you have customized yt in some way, or created your own plugins file (as described in :ref:`plugin-file`) then it may be necessary to supply users willing to help you (or the mailing list) with both your patches to the source, the plugin file, and perhaps even the datafile on which you're running. yt-project-yt-f043ac8/doc/source/index.rst000066400000000000000000000121401510711153200205560ustar00rootroot00000000000000yt Overview =========== yt is a community-developed analysis and visualization toolkit for volumetric data. yt has been applied mostly to astrophysical simulation data, but it can be applied to many different types of data including seismology, radio telescope data, weather simulations, and nuclear engineering simulations. yt is developed in Python under the open-source model. yt supports :ref:`many different code formats `, and we provide :ref:`sample data for each format ` with :ref:`instructions on how to load and examine each data type `. Table of Contents ----------------- .. raw:: html

Introduction to yt

What does yt offer? How can I use it? How to think in yt?

yt 4.0

How yt-4.0 differs from past versions

yt 3.0

How yt-3.0 differs from past versions

Installation

Getting, installing, and updating yt

yt Quickstart

Demonstrations of what yt can do

Loading and Examining Data

How to load all dataset types in yt and examine raw data

The Cookbook

Example recipes for how to accomplish a variety of tasks

Visualizing Data

Make plots, projections, volume renderings, movies, and more

General Data Analysis

The nuts and bolts of manipulating yt datasets

Domain-Specific Analysis

Astrophysical analysis, clump finding, cosmology calculations, and more

Developer Guide

Catering yt to work for your exact use case

Reference Materials

Lists of fields, quantities, classes, functions, and more

Frequently Asked Questions

Solutions for common questions and problems

Getting help

What to do if you run into problems

About yt

What is yt?

.. toctree:: :hidden: intro/index installing yt Quickstart yt4differences yt3differences cookbook/index visualizing/index analyzing/index analyzing/domain_analysis/index examining/index developing/index reference/index faq/index Getting Help about/index yt-project-yt-f043ac8/doc/source/installing.rst000066400000000000000000000217451510711153200216260ustar00rootroot00000000000000.. _installing-yt: Getting and Installing yt ========================= .. contents:: :depth: 2 :local: :backlinks: none .. _getting-yt: Disclaimer ---------- The Python ecosystem offers many viable tools to setup isolated Python environments, including but not restricted to - `venv `_ (part of the Python standard library) - `Anaconda/conda `_ - `virtualenv `_ We strongly recommend you choose and learn one. However, it is beyond the scope of this page to cover every situation. We will show you how to install a stable release or from source, using conda or pip, and we will *assume* that you do so in an isolated environment. Also note that each yt release supports a limited range of Python versions. Here's a summary for most recent releases +------------+------------+----------------+-----------------+ | yt release | Python 2.7 | Python3 min | Python3 max | +============+============+================+=================+ | 4.4.x | no | 3.10.3 | 3.13 (expected) | +------------+------------+----------------|-----------------| | 4.3.x | no | 3.9.2 | 3.12 | +------------+------------+----------------+-----------------+ | 4.2.x | no | 3.8 | 3.11 | +------------+------------+----------------+-----------------+ | 4.1.x | no | 3.7 | 3.11 | +------------+------------+----------------+-----------------+ | 4.0.x | no | 3.6 | 3.10 | +------------+------------+----------------+-----------------+ | 3.6.x | no | 3.5 | 3.8 | +------------+------------+----------------+-----------------+ | 3.5.x | yes | 3.4 | 3.5 | +------------+------------+----------------+-----------------+ Minimum Python versions are strict requirements, while maximum indicates the newest version for which the yt development team provides pre-compiled binaries via PyPI and conda-forge. It may be possible to compile existing yt versions under more recent Python versions, though this is never guaranteed. yt also adheres to `SPEC 0 `_ as a soft guideline for our support policy of core dependencies (Python, numpy, matplotlib ...). Getting yt ---------- In this document we describe several methods for installing yt. The method that will work best for you depends on your precise situation: * If you need a stable build, see :ref:`install-stable` * If you want to build the development version of yt see :ref:`install-from-source`. .. _install-stable: Installing a stable release +++++++++++++++++++++++++++ The latest stable release can be obtained from PyPI with pip .. code-block:: bash $ python -m pip install --upgrade pip $ python -m pip install --user yt Or using the Anaconda/Miniconda Python distributions .. code-block:: bash $ conda install --channel conda-forge yt .. _install-additional: Additional requirements (pip only) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ yt knows about several data file formats. In many cases (e.g. HDF5), additional dependencies are needed to enable parsing, that are not installed with yt by default. In order to install all required packages for a specific format alongside yt, one can specify them as, for instance .. code-block:: bash $ python -m pip install --upgrade pip $ python -m pip install --user "yt[ramses]" Extra requirements can be combined, separated by commas (say ``yt[ramses,enzo_e]``). Note that all format names are normalized to lower case. .. _install-from-source: Building from source ++++++++++++++++++++ There are a couple ways to build yt from source with e.g., ``pip``, all of which require a C compiler (such as ``gcc`` or ``clang``). yt is primarily distributed on PyPI, in the form of pre-built binaries (wheels). Since version 4.5.0, these binaries are optimized for portability accross Python versions. If you need a stable release, but pre-built binaries are not available for your platform, ``pip install`` will automatically select a source distribution and compile the package for you. You may opt-into this behavior deliberately by specifying the ``--no-binary`` flag, in which case the resulting installation might be slightly more performant, because it will be compiled specifically for your Python version. If, on the other hand, you *specifically* want a portable binary (as the ones we provide on PyPI), this is achieved by setting ``YT_LIMITED_API=1`` in your build environment. You may also want to build yt directly from the github repository (which requires ``git``), for instance if you need the latest development version, or if you want to contribute to the project. Run .. code-block:: bash $ git clone https://github.com/yt-project/yt $ cd yt $ python -m pip install --upgrade pip $ python -m pip install --user -e . .. _optional-runtime-deps: Leveraging optional yt runtime dependencies +++++++++++++++++++++++++++++++++++++++++++ Some relatively heavy runtime dependencies are not included in your build by default as they may be irrelevant in your workflow. Common examples include h5py, mpi4py, astropy or scipy. yt implements a on-demand import mechanism that allows it to run even when they are not installed *until they're needed*, in which case it will raise an ``ImportError``, pointing to the missing requirement. If you wish to get everything from the start, you may specify it when building yt as by appending ``[full]`` to the target name when calling pip, i.e., .. code-block:: bash $ # stable release $ python -m pip install --user yt[full] $ # from source $ python -m pip install --user -e .[full] .. _testing-installation: Testing Your Installation +++++++++++++++++++++++++ To make sure everything is installed properly, try running yt at the command line: .. code-block:: bash $ python -c "import yt" If this runs without raising errors, you have successfully installed yt. Congratulations! Otherwise, read the error message carefully and follow any instructions it gives you to resolve the issue. Do not hesitate to :ref:`contact us ` so we can help you figure it out. .. _updating: Updating yt +++++++++++ For pip-based installations: .. code-block:: bash $ python -m pip install --upgrade yt For conda-based installations: .. code-block:: bash $ conda update yt For git-based installations (yt installed from source), we provide the following one-liner facility .. code-block:: bash $ yt update This will pull any changes from GitHub, and recompile yt if necessary. Uninstalling yt +++++++++++++++ If you've installed via pip (either from Pypi or from source) .. code-block:: bash $ python -m pip uninstall yt Or with conda .. code-block:: bash $ conda uninstall yt TroubleShooting --------------- If you are unable to locate the yt executable (i.e. executing ``yt version`` at the bash command line fails), then you likely need to add the ``$HOME/.local/bin`` (or the equivalent on your OS) to your PATH. Some Linux distributions do not include this directory in the default search path. Additional Resources -------------------- .. _distro-packages: yt Distribution Packages ++++++++++++++++++++++++ Some operating systems have yt pre-built packages that can be installed with the system package manager. Note that the packages in some of these distributions may be out of date. .. note:: Since the third-party packages listed below are not officially supported by yt developers, support should not be sought out on the project mailing lists or Slack channels. All support requests related to these packages should be directed to their official maintainers. While we recommended installing yt with either pip or conda, a number of third-party packages exist for the distributions listed below. .. image:: https://repology.org/badge/vertical-allrepos/python:yt.svg?header=yt%20packaging%20status :target: https://repology.org/project/python:yt/versions Intel distribution for Python +++++++++++++++++++++++++++++ A viable alternative to the installation based on Anaconda is the use of the `Intel Distribution for Python `_. For `Parallel Computation `_ on Intel architectures, especially on supercomputers, a large `performance and scalability improvement `_ over several common tasks has been demonstrated. See `Parallel Computation `_ for a discussion on using yt in parallel. Leveraing this specialized distribution for yt requires that you install some dependencies from the intel conda channel before installing yt itself, like so .. code-block:: bash $ conda install -c intel numpy scipy mpi4py cython git sympy ipython matplotlib netCDF4 $ python -m install --user yt yt-project-yt-f043ac8/doc/source/intro/000077500000000000000000000000001510711153200200525ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/intro/index.rst000066400000000000000000000177101510711153200217210ustar00rootroot00000000000000Introduction to yt ================== Herein, we present a brief introduction to yt's capabilities and infrastructure with numerous links to relevant portions of the documentation on each topic. It is our hope that readers will not only gain insight to what can be done with yt, but also they will learn how to *think in yt* to solve science questions, learn some of the yt jargon, and figure out where to go in the docs for help. .. contents:: :depth: 2 :local: :backlinks: none Fields ^^^^^^ yt is an analysis toolkit operating on multidimensional datasets for :ref:`a variety of data formats `. It represents quantities varying over a multidimensional space as :ref:`fields ` such as gas density, gas temperature, etc. Many fields are defined when yt :ref:`loads the external dataset ` into "native fields" as defined by individual frontends for each code format. However, yt additionally creates many "derived fields" by manipulating and combining native fields. yt comes with a large existing :ref:`set of derived fields `, but you can also :ref:`create your own `. Objects ^^^^^^^ Central to yt's infrastructure are :ref:`data objects `, which act as a means of :ref:`filtering data ` based on :ref:`spatial location ` (e.g. lines, spheres, boxes, cylinders), based on :ref:`field values ` (e.g. all gas > 10^6 K), or for :ref:`constructing new data products ` (e.g. projections, slices, isosurfaces). Furthermore, yt can calculate the :ref:`bulk quantities ` associated with these data objects (e.g. total mass, bulk velocity, angular momentum). General Analysis ^^^^^^^^^^^^^^^^ The documentation section on :ref:`analyzing data ` has a full description of :ref:`fields `, :ref:`data objects `, and :ref:`filters `. It also includes an explanation of how the :ref:`units system ` works to tag every individual field and quantity with a physical unit (e.g. cm, AU, kpc, Mpc, etc.), and it describes ways of analyzing multiple chronological data outputs from the same underlying dataset known as :ref:`time series `. Lastly, it includes information on how to enable yt to operate :ref:`in parallel over multiple processors simultaneously `. Datasets can be analyzed by simply :ref:`examining raw source data `, or they can be processed in a number of ways to extract relevant information and to explore the data including :ref:`visualizing data `. Visualization ^^^^^^^^^^^^^ yt provides many tools for :ref:`visualizing data `, and herein we highlight a few of them. yt can create :ref:`slice plots `, wherein a three-dimensional volume (or any of the :ref:`data objects `) is *sliced* by a plane to return the two-dimensional field data intersected by that plane. Similarly, yt can generate :ref:`line queries (i.e. rays) ` of a single line intersecting a three-dimensional dataset. :ref:`Projection plots ` are generated by projecting a three-dimensional volume into two dimensions either :ref:`by summing or integrating ` the field along each pixel's line of sight with or without a weighting field. Slices, projections, and rays can be made to align with the primary axes of the simulation (e.g. x,y,z) or at any arbitrary angle throughout the volume. For these operations, a number of :ref:`"callbacks" ` exist that will annotate your figures with field contours, velocity vectors, particle and halo positions, streamlines, simple shapes, and text. yt can examine correlations between two or three fields simultaneously with :ref:`profile plots ` and :ref:`phase plots `. By querying field data for two separate fields at each position in your dataset or :ref:`data object `, yt can show the relationship between those two fields in a :ref:`profile plot ` (e.g. average gas density as a function radius). Similarly, a :ref:`phase plot ` correlates two fields as described above, but it weights those fields by a third field. Phase plots commonly use mass as the weighting field and are oftentimes used to relate gas density and temperature. More advanced visualization functionality in yt includes generating :ref:`streamlines ` to track the velocity flow in your datasets, creating photorealistic isocontour images of your data called :ref:`volume renderings `, and :ref:`visualizing isosurfaces in an external interactive tool `. yt even has a special web-based tool for exploring your data with a :ref:`google-maps-like interface `. Executing and Scripting yt ^^^^^^^^^^^^^^^^^^^^^^^^^^ yt is written almost entirely in python and it functions as a library that you can import into your python scripts. There is full docstring documentation for all of the major classes and functions in the :ref:`API docs `. yt has support for running in IPython and for running IPython notebooks for fully interactive sessions both locally and on remote supercomputers. yt also has a number of ways it can be :ref:`executed at the command line ` for simple tasks like automatically loading a dataset, updating the yt sourcecode, starting an IPython notebook, or uploading scripts and images to public locations. There is an optional :ref:`yt configuration file ` you can modify for controlling local settings like color, logging, output settings. There is also an optional :ref:`yt plugin file ` you can create to automatically load certain datasets, custom derived fields, derived quantities, and more. Cookbook and Quickstart ^^^^^^^^^^^^^^^^^^^^^^^ yt contains a number of example recipes for demonstrating simple and complex tasks in :ref:`the cookbook ` including many of the topics discussed above. The cookbook also contains :ref:`more lengthy notebooks ` to demonstrate more sophisticated machinery on a variety of topics. If you're new to yt and you just want to see a broad demonstration of some of the things yt can do, check out the :ref:`yt quickstart `. Developing in yt ^^^^^^^^^^^^^^^^ yt is an open source development project, with only scientist-developers like you to support it, add code, add documentation, etc. As such, we welcome members of the public to join :ref:`our community ` by contributing code, bug reports, documentation, and helping to :ref:`support the code in a number of ways `. Sooner or later, you'll want to :ref:`add your own derived field `, :ref:`data object `, :ref:`code frontend ` or :ref:`make yt compatible with an external code `. We have detailed instructions on how to :ref:`contribute code ` :ref:`documentation `, and :ref:`tests `, and how to :ref:`debug this code `. Getting Help ^^^^^^^^^^^^ We have all been there, where something is going wrong and we cannot understand why. Check out our :ref:`frequently asked questions ` and the documentation section :ref:`asking-for-help` to get solutions for your problems. Getting Started ^^^^^^^^^^^^^^^ We have detailed :ref:`installation instructions ` and support for a number of platforms including Unix, Linux, MacOS, and Windows. If you are new to yt, check out the :ref:`yt Quickstart ` and the :ref:`cookbook ` for a demonstration of yt's capabilities. If you previously used yt version 2, check out our guide on :ref:`how to make your scripts work in yt 3 `. So what are you waiting for? Good luck and welcome to the yt community. yt-project-yt-f043ac8/doc/source/quickstart/000077500000000000000000000000001510711153200211115ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/quickstart/1)_Introduction.ipynb000066400000000000000000000060131510711153200251260ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Welcome to the yt quickstart!\n", "\n", "In this brief tutorial, we'll go over how to load up data, analyze things, inspect your data, and make some visualizations.\n", "\n", "Our documentation page can provide information on a variety of the commands that are used here, both in narrative documentation as well as recipes for specific functionality in our cookbook. The documentation exists at https://yt-project.org/doc/. If you encounter problems, look for help here: https://yt-project.org/doc/help/index.html." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Acquiring the datasets for this tutorial\n", "\n", "If you are executing these tutorials interactively, you need some sample datasets on which to run the code. You can download these datasets at https://yt-project.org/data/, or you can use the built-in yt sample data loader (using [pooch](https://www.fatiando.org/pooch/latest/api/index.html) under the hood) to automatically download the data for you.\n", "\n", "The datasets necessary for each lesson are noted next to the corresponding tutorial, and by default it will use the pooch-based dataset downloader. If you would like to supply your own paths, you can choose to do so.\n", "\n", "## Using the Automatic Downloader\n", "\n", "For the purposes of this tutorial, or whenever you want to use sample data, you can use the `load_sample` command to utilize the pooch auto-downloader. For instance:\n", "\n", "```python\n", "ds = yt.load_sample(\"IsolatedGalaxy\")\n", "```\n", "\n", "## Using manual loading\n", "\n", "The way you will *most frequently* interact with `yt` is using the standard `load` command. This accepts a path and optional arguments. For instance:\n", "\n", "```python\n", "ds = yt.load(\"IsolatedGalaxy/galaxy0030/galaxy0030\")\n", "```\n", "\n", "would load the `IsolatedGalaxy` dataset by supplying the full path to the parameter file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What's Next?\n", "\n", "The Notebooks are meant to be explored in this order:\n", "\n", "1. Introduction (this file!)\n", "2. Data Inspection (IsolatedGalaxy dataset)\n", "3. Simple Visualization (enzo_tiny_cosmology & Enzo_64 datasets)\n", "4. Data Objects and Time Series (IsolatedGalaxy dataset)\n", "5. Derived Fields and Profiles (IsolatedGalaxy dataset)\n", "6. Volume Rendering (IsolatedGalaxy dataset)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 0 } yt-project-yt-f043ac8/doc/source/quickstart/2)_Data_Inspection.ipynb000066400000000000000000000263071510711153200255220ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Starting Out and Loading Data\n", "\n", "We're going to get started by loading up yt. This next command brings all of the libraries into memory and sets up our environment." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we've loaded yt, we can load up some data. Let's load the `IsolatedGalaxy` dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_sample(\"IsolatedGalaxy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fields and Facts\n", "\n", "When you call the `load` function, yt tries to do very little -- this is designed to be a fast operation, just setting up some information about the simulation. Now, the first time you access the \"index\" it will read and load the mesh and then determine where data is placed in the physical domain and on disk. Once it knows that, yt can tell you some statistics about the simulation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds.print_stats()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt can also tell you the fields it found on disk:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds.field_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And, all of the fields it thinks it knows how to generate:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds.derived_field_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt can also transparently generate fields. However, we encourage you to examine exactly what yt is doing when it generates those fields. To see, you can ask for the source of a given field." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ds.field_info[\"gas\", \"vorticity_x\"].get_source())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt stores information about the domain of the simulation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds.domain_width" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt can also convert this into various units:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ds.domain_width.in_units(\"kpc\"))\n", "print(ds.domain_width.in_units(\"au\"))\n", "print(ds.domain_width.in_units(\"mile\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can get basic information about the particle types and number of particles in a simulation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ds.particle_types)\n", "print(ds.particle_types_raw)\n", "print(ds.particle_type_counts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this dataset, we see that there are two particle types defined, (`io` and `all`), but that only one of these particle types in `ds.particle_types_raw`. The `ds.particle_types` list contains *all* particle types in the simulation, including ones that are dynamically defined like particle unions. The `ds.particle_types_raw` list includes only particle types that are in the output file we loaded the dataset from.\n", "\n", "We can also see that there are a bit more than 1.1 million particles in this simulation. Only particle types in `ds.particle_types_raw` will appear in the `ds.particle_type_counts` dictionary." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Mesh Structure\n", "\n", "If you're using a simulation type that has grids (for instance, here we're using an Enzo simulation) you can examine the structure of the mesh. For the most part, you probably won't have to use this unless you're debugging a simulation or examining in detail what is going on." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ds.index.grid_left_edge)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But, you may have to access information about individual grid objects! Each grid object mediates accessing data from the disk and has a number of attributes that tell you about it. The index (`ds.index` here) has an attribute `grids` which is all of the grid objects." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds.index.grids[1]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g = ds.index.grids[1]\n", "print(g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Grids have dimensions, extents, level, and even a list of Child grids." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g.ActiveDimensions" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g.LeftEdge, g.RightEdge" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g.Level" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g.Children" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Advanced Grid Inspection\n", "\n", "If we want to examine grids only at a given level, we can! Not only that, but we can load data and take a look at various fields.\n", "\n", "*This section can be skipped!*" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "gs = ds.index.select_grids(ds.index.max_level)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g2 = gs[0]\n", "print(g2)\n", "print(g2.Parent)\n", "print(g2.get_global_startindex())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "g2[\"density\"][:, :, 0]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print((g2.Parent.child_mask == 0).sum() * 8)\n", "print(g2.ActiveDimensions.prod())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "for f in ds.field_list:\n", " fv = g[f]\n", " if fv.size == 0:\n", " continue\n", " print(f, fv.min(), fv.max())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Examining Data in Regions\n", "\n", "yt provides data object selectors. In subsequent notebooks we'll examine these in more detail, but we can select a sphere of data and perform a number of operations on it. yt makes it easy to operate on fluid fields in an object in *bulk*, but you can also examine individual field values.\n", "\n", "This creates a sphere selector positioned at the most dense point in the simulation that has a radius of 10 kpc." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sp = ds.sphere(\"max\", (10, \"kpc\"))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can calculate a bunch of bulk quantities. Here's that list, but there's a list in the docs, too!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "list(sp.quantities.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's look at the total mass. This is how you call a given quantity. yt calls these \"Derived Quantities\". We'll talk about a few in a later notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sp.quantities.total_mass()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/quickstart/3)_Simple_Visualization.ipynb000066400000000000000000000176041510711153200266310ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Simple Visualizations of Data\n", "\n", "Just like in our first notebook, we have to load yt and then some data." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this notebook, we'll load up a cosmology dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_sample(\"enzo_tiny_cosmology\")\n", "print(\"Redshift =\", ds.current_redshift)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the terms that yt uses, a projection is a line integral through the domain. This can either be unweighted (in which case a column density is returned) or weighted, in which case an average value is returned. Projections are, like all other data objects in yt, full-fledged data objects that churn through data and present that to you. However, we also provide a simple method of creating Projections and plotting them in a single step. This is called a Plot Window, here specifically known as a `ProjectionPlot`. One thing to note is that in yt, we project all the way through the entire domain at a single time. This means that the first call to projecting can be somewhat time consuming, but panning, zooming and plotting are all quite fast.\n", "\n", "yt is designed to make it easy to make nice plots and straightforward to modify those plots directly. The cookbook in the documentation includes detailed examples of this." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p = yt.ProjectionPlot(ds, \"y\", (\"gas\", \"density\"))\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `show` command simply sends the plot to the IPython notebook. You can also call `p.save()` which will save the plot to the file system. This function accepts an argument, which will be prepended to the filename and can be used to name it based on the width or to supply a location.\n", "\n", "Now we'll zoom and pan a bit." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p.zoom(2.0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p.pan_rel((0.1, 0.0))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p.zoom(10.0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p.pan_rel((-0.25, -0.5))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p.zoom(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we specify multiple fields, each time we call `show` we get multiple plots back. Same for `save`!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p = yt.ProjectionPlot(\n", " ds,\n", " \"z\",\n", " [(\"gas\", \"density\"), (\"gas\", \"temperature\")],\n", " weight_field=(\"gas\", \"density\"),\n", ")\n", "p.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can adjust the colormap on a field-by-field basis." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "p.set_cmap((\"gas\", \"temperature\"), \"hot\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And, we can re-center the plot on different locations. One possible use of this would be to make a single `ProjectionPlot` which you move around to look at different regions in your simulation, saving at each one." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "v, c = ds.find_max((\"gas\", \"density\"))\n", "p.set_center((c[0], c[1]))\n", "p.zoom(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay, let's load up a bigger simulation (from `Enzo_64` this time) and make a slice plot." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_sample(\"Enzo_64/DD0043/data0043\")\n", "s = yt.SlicePlot(\n", " ds, \"z\", [(\"gas\", \"density\"), (\"gas\", \"velocity_magnitude\")], center=\"max\"\n", ")\n", "s.set_cmap((\"gas\", \"velocity_magnitude\"), \"cmyt.pastel\")\n", "s.zoom(10.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can adjust the logging of various fields:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "s.set_log((\"gas\", \"velocity_magnitude\"), True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt provides many different annotations for your plots. You can see all of these in the documentation, or if you type `s.annotate_` and press tab, a list will show up here. We'll annotate with velocity arrows." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "s.annotate_velocity()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Contours can also be overlaid:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "s = yt.SlicePlot(ds, \"x\", (\"gas\", \"density\"), center=\"max\")\n", "s.annotate_contour((\"gas\", \"temperature\"))\n", "s.zoom(2.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can save out to the file system." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "s.save()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/quickstart/4)_Data_Objects_and_Time_Series.ipynb000066400000000000000000000300301510711153200301000ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Data Objects and Time Series Data\n", "\n", "Just like before, we will load up yt. Since we'll be using pyplot to plot some data in this notebook, we additionally tell matplotlib to place plots inline inside the notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import yt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Time Series Data\n", "\n", "Unlike before, instead of loading a single dataset, this time we'll load a bunch which we'll examine in sequence. This command creates a `DatasetSeries` object, which can be iterated over (including in parallel, which is outside the scope of this quickstart) and analyzed. There are some other helpful operations it can provide, but we'll stick to the basics here.\n", "\n", "Note that you can specify either a list of filenames, or a glob (i.e., asterisk) pattern in this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ts = yt.load(\"enzo_tiny_cosmology/DD????/DD????\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Simple Time Series\n", "\n", "As a simple example of how we can use this functionality, let's find the min and max of the density as a function of time in this simulation. To do this we use the construction `for ds in ts` where `ds` means \"Dataset\" and `ts` is the \"Time Series\" we just loaded up. For each dataset, we'll create an object (`ad`) that covers the entire domain. (`all_data` is a shorthand function for this.) We'll then call the `extrema` Derived Quantity, and append the min and max to our extrema outputs. Lastly, we're turn down yt's logging to only show \"error\"s so as to not produce too much logging text, as it loads each individual dataset below." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "yt.set_log_level(\"error\")\n", "rho_ex = []\n", "times = []\n", "for ds in ts:\n", " ad = ds.all_data()\n", " rho_ex.append(ad.quantities.extrema(\"density\"))\n", " times.append(ds.current_time.in_units(\"Gyr\"))\n", "rho_ex = np.array(rho_ex)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we plot the minimum and the maximum:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.set(\n", " xlabel=\"Time (Gyr)\",\n", " ylabel=\"Density ($g/cm^3$)\",\n", " yscale=\"log\",\n", " ylim=(1e-32, 1e-21),\n", ")\n", "ax.plot(times, rho_ex[:, 0], \"-xk\", label=\"Minimum\")\n", "ax.plot(times, rho_ex[:, 1], \"-xr\", label=\"Maximum\")\n", "ax.legend()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data Objects\n", "\n", "Time series data have many applications, but most of them rely on examining the underlying data in some way. Below, we'll see how to use and manipulate data objects.\n", "\n", "### Ray Queries\n", "\n", "yt provides the ability to examine rays, or lines, through the domain. Note that these are not periodic, unlike most other data objects. We create a ray object and can then examine quantities of it. Rays have the special fields `t` and `dts`, which correspond to the time the ray enters a given cell and the distance it travels through that cell.\n", "\n", "To create a ray, we specify the start and end points.\n", "\n", "Note that we need to convert these arrays to numpy arrays due to a bug in matplotlib 1.3.1." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ray = ds.ray([0.1, 0.2, 0.3], [0.9, 0.8, 0.7])\n", "ax.semilogy(np.array(ray[\"t\"]), np.array(ray[\"density\"]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ray[\"dts\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ray[\"t\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(ray[\"gas\", \"x\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Slice Queries\n", "\n", "While slices are often used for visualization, they can be useful for other operations as well. yt regards slices as multi-resolution objects. They are an array of cells that are not all the same size; it only returns the cells at the highest resolution that it intersects. (This is true for all yt data objects.) Slices and projections have the special fields `px`, `py`, `pdx` and `pdy`, which correspond to the coordinates and half-widths in the pixel plane." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_sample(\"IsolatedGalaxy\")\n", "v, c = ds.find_max((\"gas\", \"density\"))\n", "sl = ds.slice(2, c[0])\n", "print(sl[\"index\", \"x\"])\n", "print(sl[\"index\", \"z\"])\n", "print(sl[\"pdx\"])\n", "print(sl[\"gas\", \"density\"].shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to do something interesting with a `Slice`, we can turn it into a `FixedResolutionBuffer`. This object can be queried and will return a 2D array of values." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "frb = sl.to_frb((50.0, \"kpc\"), 1024)\n", "print(frb[\"gas\", \"density\"].shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt provides a few functions for writing arrays to disk, particularly in image form. Here we'll write out the log of `density`, and then use IPython to display it back here. Note that for the most part, you will probably want to use a `PlotWindow` for this, but in the case that it is useful you can directly manipulate the data." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "yt.write_image(np.log10(frb[\"gas\", \"density\"]), \"temp.png\")\n", "from IPython.display import Image\n", "\n", "Image(filename=\"temp.png\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Off-Axis Slices\n", "\n", "yt provides not only slices, but off-axis slices that are sometimes called \"cutting planes.\" These are specified by (in order) a normal vector and a center. Here we've set the normal vector to `[0.2, 0.3, 0.5]` and the center to be the point of maximum density.\n", "\n", "We can then turn these directly into plot windows using `to_pw`. Note that the `to_pw` and `to_frb` methods are available on slices, off-axis slices, and projections, and can be used on any of them." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "cp = ds.cutting([0.2, 0.3, 0.5], \"max\")\n", "pw = cp.to_pw(fields=[(\"gas\", \"density\")])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once we have our plot window from our cutting plane, we can show it here." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "pw.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "pw.zoom(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can, as noted above, do the same with our slice:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "pws = sl.to_pw(fields=[(\"gas\", \"density\")])\n", "pws.show()\n", "print(list(pws.plots.keys()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Covering Grids\n", "\n", "If we want to access a 3D array of data that spans multiple resolutions in our simulation, we can use a covering grid. This will return a 3D array of data, drawing from up to the resolution level specified when creating the data. For example, if you create a covering grid that spans two child grids of a single parent grid, it will fill those zones covered by a zone of a child grid with the data from that child grid. Where it is covered only by the parent grid, the cells from the parent grid will be duplicated (appropriately) to fill the covering grid.\n", "\n", "There are two different types of covering grids: unsmoothed and smoothed. Smoothed grids will be filled through a cascading interpolation process; they will be filled at level 0, interpolated to level 1, filled at level 1, interpolated to level 2, filled at level 2, etc. This will help to reduce edge effects. Unsmoothed covering grids will not be interpolated, but rather values will be duplicated multiple times.\n", "\n", "For SPH datasets, the covering grid gives the SPH-interpolated value of a field at each grid cell center. This is done for unsmoothed grids; smoothed grids are not available for SPH data.\n", "\n", "Here we create an unsmoothed covering grid at level 2, with the left edge at `[0.0, 0.0, 0.0]` and with dimensions equal to those that would cover the entire domain at level 2. We can then ask for the Density field, which will be a 3D array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "cg = ds.covering_grid(2, [0.0, 0.0, 0.0], ds.domain_dimensions * 2**2)\n", "print(cg[\"density\"].shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example, we do exactly the same thing: except we ask for a *smoothed* covering grid, which will reduce edge effects." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "scg = ds.smoothed_covering_grid(2, [0.0, 0.0, 0.0], ds.domain_dimensions * 2**2)\n", "print(scg[\"density\"].shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3.9.5 64-bit ('yt-dev': pyenv)", "metadata": { "interpreter": { "hash": "14363bd97bed451d1329fb3e06aa057a9e955a9421c5343dd7530f5497723a41" } }, "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.5" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/quickstart/5)_Derived_Fields_and_Profiles.ipynb000066400000000000000000000232001510711153200300030ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Derived Fields and Profiles\n", "\n", "One of the most powerful features in yt is the ability to create derived fields that act and look exactly like fields that exist on disk. This means that they will be generated on demand and can be used anywhere a field that exists on disk would be used. Additionally, you can create them by just writing python functions." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import yt\n", "from yt import derived_field" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Derived Fields\n", "\n", "This is an example of the simplest possible way to create a derived field. All derived fields are defined by a function and some metadata; that metadata can include units, LaTeX-friendly names, conversion factors, and so on. Fields can be defined in the way in the next cell. What this does is create a function which accepts two arguments and then provide the units for that field. In this case, our field is `dinosaurs` and our units are `K*cm/s`. The function itself can access any fields that are in the simulation, and it does so by requesting data from the object called `data`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "@derived_field(name=\"dinosaurs\", units=\"K * cm/s\", sampling_type=\"cell\")\n", "def _dinos(field, data):\n", " return data[\"gas\", \"temperature\"] * data[\"gas\", \"velocity_magnitude\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One important thing to note is that derived fields must be defined *before* any datasets are loaded. Let's load up our data and take a look at some quantities." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ds = yt.load_sample(\"IsolatedGalaxy\")\n", "dd = ds.all_data()\n", "print(list(dd.quantities.keys()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One interesting question is, what are the minimum and maximum values of dinosaur production rates in our isolated galaxy? We can do that by examining the `extrema` quantity -- the exact same way that we would for density, temperature, and so on." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(dd.quantities.extrema((\"gas\", \"dinosaurs\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do the same for the average quantities as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "print(\n", " dd.quantities.weighted_average_quantity(\n", " (\"gas\", \"dinosaurs\"), weight=(\"gas\", \"temperature\")\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A Few Other Quantities\n", "\n", "We can ask other quantities of our data, as well. For instance, this sequence of operations will find the most dense point, center a sphere on it, calculate the bulk velocity of that sphere, calculate the baryonic angular momentum vector, and then the density extrema. All of this is done in a memory conservative way: if you have an absolutely enormous dataset, yt will split that dataset into pieces, apply intermediate reductions and then a final reduction to calculate your quantity." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sp = ds.sphere(\"max\", (10.0, \"kpc\"))\n", "bv = sp.quantities.bulk_velocity()\n", "L = sp.quantities.angular_momentum_vector()\n", "rho_min, rho_max = sp.quantities.extrema((\"gas\", \"density\"))\n", "print(bv)\n", "print(L)\n", "print(rho_min, rho_max)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Profiles\n", "\n", "yt provides the ability to bin in 1, 2 and 3 dimensions. This means discretizing in one or more dimensions of phase space (density, temperature, etc) and then calculating either the total value of a field in each bin or the average value of a field in each bin.\n", "\n", "We do this using the objects `Profile1D`, `Profile2D`, and `Profile3D`. The first two are the most common since they are the easiest to visualize.\n", "\n", "This first set of commands manually creates a profile object the sphere we created earlier, binned in 32 bins according to density between `rho_min` and `rho_max`, and then takes the density-weighted average of the fields `temperature` and (previously-defined) `dinosaurs`. We then plot it in a loglog plot." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "prof = yt.Profile1D(\n", " sp, (\"gas\", \"density\"), 32, rho_min, rho_max, True, weight_field=(\"gas\", \"mass\")\n", ")\n", "prof.add_fields([(\"gas\", \"temperature\"), (\"gas\", \"dinosaurs\")])\n", "\n", "fig, ax = plt.subplots()\n", "ax.loglog(np.array(prof.x), np.array(prof[\"gas\", \"temperature\"]), \"-x\")\n", "ax.set(\n", " xlabel=\"Density $(g/cm^3)$\",\n", " ylabel=\"Temperature $(K)$\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we plot the `dinosaurs` field." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.loglog(np.array(prof.x), np.array(prof[\"gas\", \"dinosaurs\"]), \"-x\")\n", "ax.set(\n", " xlabel=\"Density $(g/cm^3)$\",\n", " ylabel=\"Dinosaurs $(K cm / s)$\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to see the total mass in every bin, we profile the `mass` field with no weight. Specifying `weight=None` will simply take the total value in every bin and add that up." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "prof = yt.Profile1D(\n", " sp, (\"gas\", \"density\"), 32, rho_min, rho_max, True, weight_field=None\n", ")\n", "prof.add_fields([(\"gas\", \"mass\")])\n", "\n", "fig, ax = plt.subplots()\n", "ax.loglog(np.array(prof.x), np.array(prof[\"gas\", \"mass\"].in_units(\"Msun\")), \"-x\")\n", "ax.set(\n", " xlabel=\"Density $(g/cm^3)$\",\n", " ylabel=r\"Cell mass $(M_\\odot)$\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to the low-level `ProfileND` interface, it's also quite straightforward to quickly create plots of profiles using the `ProfilePlot` class. Let's redo the last plot using `ProfilePlot`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "prof = yt.ProfilePlot(sp, (\"gas\", \"density\"), (\"gas\", \"mass\"), weight_field=None)\n", "prof.set_unit((\"gas\", \"mass\"), \"Msun\")\n", "prof.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Field Parameters\n", "\n", "Field parameters are a method of passing information to derived fields. For instance, you might pass in information about a vector you want to use as a basis for a coordinate transformation. yt often uses things like `bulk_velocity` to identify velocities that should be subtracted off. Here we show how that works:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sp_small = ds.sphere(\"max\", (50.0, \"kpc\"))\n", "bv = sp_small.quantities.bulk_velocity()\n", "\n", "sp = ds.sphere(\"max\", (0.1, \"Mpc\"))\n", "rv1 = sp.quantities.extrema((\"gas\", \"radial_velocity\"))\n", "\n", "sp.clear_data()\n", "sp.set_field_parameter(\"bulk_velocity\", bv)\n", "rv2 = sp.quantities.extrema((\"gas\", \"radial_velocity\"))\n", "\n", "print(bv)\n", "print(rv1)\n", "print(rv2)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/quickstart/6)_Volume_Rendering.ipynb000066400000000000000000000070041510711153200257170ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# A Brief Demo of Volume Rendering\n", "\n", "This shows a small amount of volume rendering. Really, just enough to get your feet wet!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import yt\n", "\n", "ds = yt.load_sample(\"IsolatedGalaxy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To create a volume rendering, we need a camera and a transfer function. We'll use the `ColorTransferFunction`, which accepts (in log space) the minimum and maximum bounds of our transfer function. This means behavior for data outside these values is undefined.\n", "\n", "We then add on \"layers\" like an onion. This function can accept a width (here specified) in data units, and also a color map. Here we add on four layers.\n", "\n", "Finally, we create a camera. The focal point is `[0.5, 0.5, 0.5]`, the width is 20 kpc (including front-to-back integration) and we specify a transfer function. Once we've done that, we call `show` to actually cast our rays and display them inline." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sc = yt.create_scene(ds)\n", "\n", "sc.camera.set_width(ds.quan(20, \"kpc\"))\n", "\n", "source = sc.sources[\"source_00\"]\n", "\n", "tf = yt.ColorTransferFunction((-28, -24))\n", "tf.add_layers(4, w=0.01)\n", "\n", "source.set_transfer_function(tf)\n", "\n", "sc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to apply a clipping, we can specify the `sigma_clip`. This will clip the upper bounds to this value times the standard deviation of the values in the image array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sc.show(sigma_clip=4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several other options we can specify. Note that here we have turned on the use of ghost zones, shortened the data interval for the transfer function, and widened our gaussian layers." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sc = yt.create_scene(ds)\n", "\n", "sc.camera.set_width(ds.quan(20, \"kpc\"))\n", "\n", "source = sc.sources[\"source_00\"]\n", "\n", "source.field = \"density\"\n", "\n", "tf = yt.ColorTransferFunction((-28, -25))\n", "tf.add_layers(4, w=0.03)\n", "\n", "source.transfer_function = tf\n", "\n", "sc.show(sigma_clip=4.0)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/quickstart/index.rst000066400000000000000000000054031510711153200227540ustar00rootroot00000000000000.. _quickstart: yt Quickstart ============= The quickstart is a series of worked examples of how to use much of the functionality of yt. These are simple, short introductions to give you a taste of what the code can do and are not meant to be detailed walkthroughs. There are two ways in which you can go through the quickstart: interactively and non-interactively. We recommend the interactive method, but if you're pressed on time, you can non-interactively go through the linked pages below and view the worked examples. To execute the quickstart interactively, you have a couple of options: 1) run the notebook from your own system or 2) run it from the url https://girder.hub.yt/#raft/5b5b4686323d12000122aa8a. Option 1 requires an existing installation of yt (see :ref:`installing-yt`), a copy of the yt source (which you may already have depending on your installation choice), and a download of the tutorial data-sets (total about 3 GB). If you know you are going to be a yt user and have the time to download the data-sets, option 1 is a good choice. However, if you're only interested in getting a feel for yt and its capabilities, or you already have yt but don't want to spend time downloading the data, go ahead to https://girder.hub.yt/#raft/5b5b4686323d12000122aa8a. If you're running the tutorial from your own system and you do not already have the yt repository, the easiest way to get the repository is to clone it using git: .. code-block:: bash git clone https://github.com/yt-project/yt Now start the IPython notebook from within the repository (we presume you have yt and [jupyterlab](https://jupyterlab.readthedocs.io/en/latest/) installed): .. code-block:: bash cd yt/doc/source/quickstart jupyter lab This command will give you information about the notebook server and how to access it. You will basically just pick a password (for security reasons) and then redirect your web browser to point to the notebook server. Once you have done so, choose "Introduction" from the list of notebooks, which includes an introduction and information about how to download the sample data. .. warning:: The pre-filled out notebooks are *far* less fun than running them yourselves! Check out the repo and give it a try. Here are the notebooks, which have been filled in for inspection: .. toctree:: :maxdepth: 1 1)_Introduction 2)_Data_Inspection 3)_Simple_Visualization 4)_Data_Objects_and_Time_Series 5)_Derived_Fields_and_Profiles 6)_Volume_Rendering .. note:: The notebooks use sample datasets that are available for download at https://yt-project.org/data. See :doc:`1)_Introduction` for more details. Let us know if you would like to contribute other example notebooks, or have any suggestions for how these can be improved. yt-project-yt-f043ac8/doc/source/reference/000077500000000000000000000000001510711153200206555ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/reference/_images/000077500000000000000000000000001510711153200222615ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/reference/_images/yt3_p0010_proj_density_None_x_z002.png000066400000000000000000005173011510711153200312070ustar00rootroot00000000000000‰PNG  IHDR=˜Ö‚ˆ«sBIT|dˆ pHYsaa¨?§i8tEXtSoftwarematplotlib version3.1.2, http://matplotlib.org/%‹¡J IDATxœì½{dWuîù­½3³ª«_Rë-!$Ë<†+@0 0W¾øàë/cËfÐÄŒ­üƒÇøqGøš‹Ahî0,ÆÆO..cÙðeÁà l„¡ÖIÝ]ÕU•™{¯ùãäãœÌ½N×ÎÎʬÊú~uòœ³wž³Ÿkïµ>QU!„B!„BÈ‚áæB!„B!„í€FB!„B!„,$4zB!„B!d!¡ÑƒB!„B! „B!„BYHhô „B!„BÈBB£!„B!„B=!„B!„²ÐèA!„B!„…„FB!„B!„,$4zB!„B!d!¡ÑƒB!„B! „B!„BYHhô „B!„BÈBB£!„B!„B=!„B!„²ÐèA!„B!„…„FB!„B!„,$4zB!„B!d!¡ÑƒB!„B! „B!„BYHhô „B!„BÈBB£!„B!„B=!„B!„²ÐèA!„B!„…„FB!„B!„,$4zB!„B!d!¡ÑƒB!„B! IcÞ ÛGŒGÅÁƒ!"óÎ!„B!dQU<úè£8ûì³áÜl××[­–——gš&ÙÐè±À=z_|ñ¼³A!„BÙ#œ^ =Üž}ºçŸoûÛ4|1hôX`<¸ùÿ–| ŸÆ¬| < [³üÞúí¿Æ ?ð¯+y,ãÜ2r½´DšÉã¡ýPåïÍØÁ{îû[üò¥¯Å’¿&ê&b\ÏJÛ¹%#Oéã¸ÿpÃ%¯Iß,¦ŸIÔͬ<©vÍçû¿?ð¼å¢+ÇŽ[ÏÐBÄA$¯¹4qëýwà†‹_±µ \@ºŒ[øæ‘Úïßwïíø¥|S)OάGK­g'‹;˜¾—a¿õ•ÿïüá`Ù¯ðÙ_ºAùZ#OÚJ÷í}DÐñ²«ÚA þ¾å¿‡ÿá™7:ï¤Ó0ˆq 1¬f]ÓíÅ8?â–oün|æÿ”ørü^ª„îcÉ[yf: ó™ü‡»oÅÛžqCéHŒç5Ý:YN÷w¡:>!PÄA{<Úþ„‘ú±%Ìò`?|¶[j]ÈlÛ‰P©ìåï/ðË—ý,û}Ég(âáÜþ±ã’϶8žîëTÃØ³ÖMç׸Wýø&ÕV§ ·~÷SøÅKÿuͽRI7í:h¼§Vë“Çoù—ßÃÿxù¯'¾q¥vÔÒ|YÆà7êTסºÐ=:ÌF©Nˆ,Ú]EÑ÷@Œ'qË¿üÞöŒŸ¯&­Áhû&¨›¡ßýæÌjÿ’·×ŽYæ,úõü¶û?‹Ÿ¿øš-\‘YÇ8è”çÜúÿŒ.yuï/Ÿ5†;Þ9‰ÿpß_â»ÿü2:8»©æñ]<õÙŸF»Ý¦ÑƒŒA£ÇÓwiYò-,û¥£Ç°ƒÔ9‘âZ ˆbL²%¯ÁÍ1zé· švÏq®…i=º>}|É5±”ø.ƈˆ¼ÎÌ%Œ'@1PIž/’L»¸(}8Ƽ÷¡ZlE4ÓwãÍD¶Cò:ÌâšfýïÅ5Ç&­§Âû´±ipË~ùïçi£‡5v+µF>Ë~ehôp¹F&’Fm‰:¥ÚAÄÉÁßN–}1pð±þY¥ƒ€<ã˜Õ2Ò/VÙiNÍèá|k¬üÔ¶?ø˜Î—YÏ¥‘|çÅ₩H?}¯h<ÛÉŒö{*úm4zõ*å¯JÊèá4]F}ò§6zŒ§Ÿžœ@4ÛÐ¥£G¢"úäÑö§óêS?¼ó‡yÝRû[7ɶ’ô³JÝgÉ5‹òg=ÒïÜ´Sc‰”Ñ£_ÿ­gh¥Q7¾IõQ —Lc~õFôñ–Q‹òŸúÎ'"-À¶qqW]EW‡õ¨0zøÁç¤ÑCb/ý‘÷«A¦¸{Àh—úä´ÉÛ«˜e΢Š÷Tô[©[“=N}ßrýø¬z¾бñ¡ƒ :Ä©&Ù°$îúj‘aí£ƒÆ~Ã?2 ÑY?¼ÝÙ«åª3Ó«ä{…«ÎxÚ\ÓÿW‡.škúW¾l®é¿èÈsH5”þ¡Ø­¡½•ÕKËÐQ®Û¡Ìî›:ÇæÅg¿tKçms/g>k®é¿ðŒgÌ5ýyóâ³_2×ô¯>û…sM>íÏy·¿ófÞõÞýÿKÎy¥ñM(|â`ýEá!®gÜÐPì&Ôö¸!¶¼°æöèM{;Cú\}öø.ӽĕ‡/™kúó.„L=öâ‡LKae…"ŒAz\}Öó·7o§àEGö¶Ñã…sît®ónçÍÜëÿœûÿkÎý‘šoûÏÅ—> ä­%÷Êòs¬î{(OµºæÅg_5YÆ„«_:×ô_xæÓOûQ;æ.¸í ¦\Q éA£!^®=ò,ø-ºâ2MÒÄ«/üq42·Ž2 ¼8¼âœ«ÐÈtç"d4ÄáÚ³ž‹ûß݃®#vîë¹AøìX;‰†x¶„,4zì†q4ú xXÞË~Íf'¥¨8sX±ŠÎ5åëÛ´c/lqõ»á<^~Öå[Íâ–¨‹MöY¶ýsFÀÒ˜Ȕ̋;¨] ^sá(ê¥H(ü£û”ÜÕ ÃãÕ"!YÞ˾ӕãÚ…ìàéºfÆ}¨mG2ã ¹%8·²å<õmuòkú@m†ðò³Ÿ `£a‚Äh[Vàdüw("¢ZWÓï#Æ“É@‘ªíš`”ÆñÌØKv@dÓÞ*ÎAñWÄ/ÃÉxNÕ¢AÃCfGP±vM„3±û,7ŽRmâ÷kú&^yοê¶úyoöóÖx%»赉VìŽ9îö˜„Ô?o–Q3P´?"ã1=´ç¶2¤ÿÚ½ú™ˆ¹1SjÇC;’+§¢­ýÌ%³}µPD3®VÃy¼òÜùîô"„L=ö…Á£YD‘GÑ«1*NH ˜m˜ùb–#è’†TüÆùSìU›Á£"”+`‹†Á^`U@ʘÒÀ²<©8í:žšôh«Û¢`ý¾BÅ(1©Ë ”ZwMiC‚« Jh¤1ÍÄæ$°†T Öº Ì ‘é2ª¨¡fQ­qK>GË 9 †Á­ä„â!ÑšœÖåiüw(:Ég_WÇ-#†"l9ù¤ÔP¬ú‘Mn`×A5d·K"­ªq½¡ÔÏŒ¾—­õZv‰é½ÇÚqé6 pi£vˆäÐéˆ"€î-ĆF=€¸}ðî øÆkèv¿7Áê!„B!„²{ ÑcÐh\ŒÆò3÷=‡ÎÀÊÃwAÂ#-¶4ÓøAÈ΢¿ V]ù®ˆ ÿîoíî°œN qG!„T©îŠ*ïÎ2ÝP´3Ø5¤q*CW—AŸµGwäB¶=öâö3ñ//<Œo?ó{xý‡Î6èAÈ® $'m»T¤ 4nBÙ^zñá4Žô?–Ñ}èÆR,ºÅÞõíê9d×Ñé($Ò½…Ô@£Ç †ïíûñÌ;ãòOo"l|¶G!„B!„,04zìb|¡{òäÃèê4®!7À!íí†S ÚgS40}A0ƒíMOF­æ™X˜Š21¹*_¨·Œ_£Zô-÷YÅleq™Aþ&‘}˽FcvZÍ5þIH(1Ô¦aÔ5é©´Œ_JZºV´¯bÐ?ï4ÖOê‚´ßåJ5v²ë‡sËÉã–êQñe:ú~m¾RÇ¥™ ô§€1©’1SAIà’¥Dà!n)y¥ö5"ÔRkòdºAYe×¼—Â>äÉô½â†¹SI¬rbäK$ý ­6f¨v6–)¨¥t‘R¬èåIRÁ …1Á4g'ïÕíM·PCɈ†ŒÝ¯ÏÖzVµ³Ç©þNdÉRk%QפʵF{wAæØ§.¬­º”[44@Ì~p ÊC¥ç¯qH©ÿhÖÒ¬›eU°ÒsS;бñÜíúoRÏQãR Q€]~rËádÌ`oDf߬ˆYcgK•‹yB£Ç „'ÐÁÉvz=FÃV×@N4ñß^÷­3HìDíøLùK˜ê-uƒ½ôùù“Ó‰˜…Þ}æïˆ!o"ïüÊryu’‘‰üª!'‹ €ªBˆjùs¢™’5­ÃžpÙX|[•ÃȯøUkÒ±®I«‘¸Ì|V€KåK<œ¦¾qØL;5‡qæï°ŒJÖ³²Œ±€áR¥Ñ4º„®•†%S^3¶®1&I¶²Š!…*€X†.1ŒÒJOŽÅÒ¯Õd¤qþðïRzÎL¦a/b¤'òªˆÑö• «Õ‹F~C¯ì[r²uXrëbŒ Dšö;ÌíïrÝ&4mÈìöʾ—­Nbø¬[YFEºR±²týàx<^ª[SiŒø@$ýþ&Q 3%v­4´“”Vé$àuc(»-1˜Õø*—ìz“·8caô3Vo¡[±¡ÑƒB!„=HÅ_6öÍb¡‚Bf{Áê‚Ç `TY½´Zš»K€B!ùTݦÊ+êåIè 3Dö&ãFÿsù{X^O˵’Bæ{€÷Þóa8¼èÈópõYÏli¤¡ƒBÈÂ2*ýœúl¿]Xî;#®5‰x ;‰Â=Šjp»›ž›UYz0]¢E¸@FÒÜyì>|áØwb1¿ { ÙIÐè±xë¾Ë>Ó÷”B!„,4E ›V/&F/¦ÔNŒ}Fv±cJïÎs[¦j7y\ÄgÞœO«:EÔ´T§ÓVRb¯¦'u¦"IÍbö¿-`ˆ\¾Œ©ø’®gÑT×L^S÷ü²ÛÓ°ˆÑfLbÔ5W¬I„¥NƒRJ1ž4Ô[&yV–|iNÆÛ>qËpîLã^E’žÁCúÒ¡fy›Ä”Y>1ToIÇTÀXÿæÝÉ{ÅxÂTiIµ Š8Ñ<Þ‡N`4ÙÙJ0F½µ¹½Ji§ ¨uÏrÃJêtñ§0(yˆì×-áøø¡oöËGÊØa«Ð¤dØGïS=ßjc"Ô¸—5Vj¸K“Çc<`¼páhû ˆ3\á »pá–Ì=!„²Àx8·âþW:÷A±±3w2C´ïÎWqéþ ˆÇ52V !d¡`‹F!„…¥ØUÑd6Ï|àÏ„H«p)°v Î$c§H»¯¼¦5+ë„LmCuÚý.bx@ J !d¡àN=AÄnŒÕA!„L…žqáK/ÛĹ>?ð÷BeÏOq³a;×f—-AIûEFu€t½«Gdß0^Œ´J’µ€­|DÈ8c÷–ȹ©;=!„²ØhÐu\zÏè¶úã9íðØUp±è¼¯þ£aƒ²8p§!„Bñ4®ãü¯}çk;­>CÈ^„ c„={€ÖQUi&£¾Ë­{ÛZ[n53z:¼ƒd®…îÃ<–6(Ű:¹éxûšäïã€r×£1¹ÁWHNh5˜2¬§Jg*dN Èq#ÀÕÈ[4«>y³¯°Œ!¦ñÆL£]2•Ïñ@i‚_½oÚõEKí¹Æš1¬g¥Ñ4Ò–'¨ƒôÅ% µIK©ÉtáØœ›¿#+KEùœRû®SȾ_̺ÞB™)?©éW¶p½ÆÁû醇’éÉ{{ ²¤M×Û›!4z1ú«5΄÷ç‡þHØžüö¼³F!„]Â`âZ2€ìEiVB!ó…FR‹"`ãŒ3áÛ]È“óÎMƒUÊOBz+€ª›sÌÔ|Ñ8üí–KË^Øñ²øÆ‘Â@u1+>Kâp ÏçK©EcÕe )¿‚‘òU$)@U-¹ÃÌjZyWÅ`çNoˆ´ŠÝ nýg!Ðè1ÖGìÐX.§r7™øž½ßãÉÁ½-—=B™4z$ÒßÞ«mt[êÍÚ)÷):Ì"jø¨ïÊ¿“ïé"² ç4:D)\‰Dg¢ IY$NѧŽN4+;$ÊÆ´“±fÒþKµep SHZw¤O£ „ïo¾v(•w²ÃÇTÛF¥ì²ï\dºt·ÑgzébA£I¢@<‰®n`ùŸoÞÙ!„BÈnCZYÆêÅ/F·åpÆ·>7ïBÙƒÐè±и •êŠBtHš×EšÅŽ í@ÅC´ïë+¾[^ãWFoS‹5Þ¦œÞØÊRïïràIÕbF/cIfo­Ô˜-³¦qkEûDœÌÎWŒÆÖaówÛÇ­`¦¹+ˆÎ€oκ&»œ”‚ùmùãÙ šÉÕ§O „ÇzõÀ@8VV6 é€æ31JM ÓäVùin‡×˜¿ã*wåÓ ÎYwÉ ‚0çÖÙ‰‚OZõVP –8L¤Ná'S)¡äVX¹•¡n$Ò„ó“·*^®¬ëæ ÏcŠ0‰ç«âÌgb*«Ô%Í-'!ZþŸFý3ÎmŠôè9N­àœ%Õ‹ÒEE0ò¼«e•¶jÆŒÃÒªª´TÚ¢~ûpüˆG{)âð=ëƒÝrý¼§ò$%u+‘e@öõ¾¶›"M8W.WÃëc0ÚvI·ífÛ7ÍÝ[é£N3=ÕNµNm»_I×A«&8ð”Ð…¡*5U7˜¼ç›ö$Ôù²Åùý¶sKYç«v³úóܱ.!³€F=€jg¼Õì´T6¡ýNN;ƒÁHâ‡Dz¡bØù€«5¬¤¤;Áò6& ¹´Ë÷sÍNâûœ;ù.Ë ŽÝËšDdväίd=r;~E'{Òn`Œˆý1œH*“±â;{0­2þhÐ?¿Ÿ¯ÔaK%AŒç¨³œLÍw[C%¾ÌNAÜÆ´ÜAhfýP™‚êF)íÔ»4k Ö½’‡GT(·qéI’s+ðþœtÒx|xßR<ЇÏ]*.ÆÄQÄ,Óe×S¿K‘¦iH´°Œ“´ÇæïLükòQ–¾•&0ø” £„E[â»/S[íK~0y½ðŸþ+ m¨¶!²¿tmY’w£pÕz;DzeIöA|¯ÿС+jÙ€R5"GI/zˆº´úœLo"][Ƭ¶Álc2Û’Ø1¦"‘4Órφ1¤xד«ºT*MÅ—–bO~ß•=ΘAœœIŒ:ÙýG¦ÑJãfV¾úçFÌVQe:Œ‘-B£!„B™:…¬Ø=:8V1zB!3€FB!„̇ʊqyMÐaZk„ è<úϽ²3hl…¹¬üâ!=×>·ïùè.YœÑ>´N¤à«ŸÃBˆ„B™ ®ä~µCÅn£&&LÕhÕ3Phn}@ß§0‚¿àR;»°bœuôV:j\ÓÜ—e‡Õ[ÈN‚FB!„2}zñêAJ !„ÙB£Ç^&1  ¨.PãŽešQ×ÉÞÂTæI‰¬  jjCH®ªúKàíHðWK§Í•Ò=Éå'UVTDýGIé¨q7ˆ~@O-+’ÔÚÍ d8YðX;ðâ4(Ôw,ÕŠt€cûw¤ƒTmu­hs:Ȩ+Êà‹Ô}=퀥bׇë†a`Y «‰ÀCû÷?\·F­(„×RÓ™*Sl¿§ŠYo¬€·æó5±ÆcS œM!§€FR¡z—;×Dœkçd¥‘ìL5äËeÒC 9Ù7Ì’hEu·e?ÒÇôöo ƤÖq#B»ˆƒ&Ô)¸»¸¨†ž2ÐuQü­27“Ƈˆ“ɉ¸ºáÑä½%U—Ba¦øì¤ í§!q˜1TDÄ×'8•êHêxê«]PHì&Éì7µƒ`(‰ˆ¤e&ÕªçXN§^óÎM%(ëüÑÊÏGú“f_UÃ)}.$oýðxOwßaùø2b£ß^ÜþÒý{eLÛÕ2­ë¥û*M¦ •ñžÊ†¸Ñ[Õ(s™ÌÛÀ‘”t*F‰dl#høX\ºpèÎðývgc¾œˆµµ5¼óïÄÇ>ö1;v W_}5þàþÏzÖ³æµ=[B!„2_dpË€ëB4ÀuÖ.`Bv9o}ë[qðàA¼ç=ïÁ[ßúVÜqÇxÕ«^…cÇŽÍ;k{îô „BÈÜ7 j*º¦ú·Ó IDAT9Ü%uº®Šåè]´Ò¾+‘´‹”\(¤|NyˆkA] pMhï|}÷âéºJÙU|ík_Ãóž÷<ÜtÓM€ÿñÇYg…›o¾Ÿüä'ñS?õSsÎáÞ€FB!„Ìç>ûæ¥èO”Cç^Äx¢øŸDŒãn—[EJ 13îÙ2t{J<q‡‡çù#¥Ïû»KgB}±Ñ€Äâ]¸Ð…ëÝ÷DCÅ£µìŽÃ˜D„ì, Âî-wÜqNœ8믿~rTpâÄ ¼ímo«»îºëpóÍ7ã‰'žØ¶tIº·ìn»ÿ³ø£ïþîø•ygå”|ñ‹_Äu×]‡W¾ò•øâ¿X{î½÷Þ‹ŸýÙŸÅóŸÿ|¼ä%/Á ^ðÜzë­[NëÅ/~1–——+Ç666/yÉKò3O&‚;=ö?ñ5XrFP/B!„€ºêZœf*òBæÇ•‡.•‡.ÂñînùÞÿ7ïì$yòÉ'që­·â‘GÁç>÷¹Sžÿå/×^{-®¿þz|þóŸG³ÙÄg?ûY\wÝu¸óÎ;ñ|`¢|ÜqÇxík_‹ç>÷¹]Oò¡ÑƒTiÂù•Ä7ié;pþ`ò¸従-ï×í<’u¾Æ\ã@Þ5¼Œ·¿ýí€ï}ï{¸ýöÛÍs;†ë¯¿Fï}ï{Ñluëšk®ÁÍ7ߌw½ë]¸æškðs?÷sYù\[[ÃG>òüùŸÿyÖuäô  B!‹‹´ ƒ‡?ºtÐ8âö'ewÉ|QGálBÈö!2lcF]NFyßûÞ‡ûï¿?ù“?‰ƒ« XoyË[ï|ç;Ñé-šþñÿ180ø÷œç<'yß›o¾ð€K/½ô4~ É…;=!„²Àx|ùµWáØ‘U\ð½#xÆg8n'2ºËƒBæÉm·ÝxùË_>öÝÅ_ŒË.» ßúÖ·ð©O} ¯{Ýëpýõ×ãE/zÑàœþÎ2¿û»¿‹ë®»×^{í¶å›¤¡ÑƒB! ‹Æ'pÅ9p_CìÜôÜZúnŠP¸gPÁ•E5\ëÊJ)j]œ¶Ÿ^Ú£Òµ%÷˜Ø®˜nØ?ØÝ±¹â{›o\$¾«¨ðÝžšK·ô^‚ƒóûŠÏ†.dô\°ºÁ¯FD«Lî¤"»™G}÷ÜsàòË/OžóÜç>ßúÖ·ðÉO~¯{ÝëpðàÁ±!e>øÁâ¼óÎÃýØ Ž­®®â±Çã®@£!„BÕ6pü@ìMÊÇããx`Ñ$kgßiÄžÑ#400z [ø_‡†À„¢; €J_"»æ÷.% h]ŸoVÈŽ#À¡»Ã%k-îºë®Áç‹.º(yÎ…^øÚ×¾vÊûýùŸÿ9þìÏþ ?÷s?‡?ýÓ?œølíÞ8t¨˜üÈ#õA?ó™Ïà§ú§±¹¹‰üã•ïÞúÖ·be%?P<ɇF’ a•Ÿܲ·Šx8ŒWpE¾R ™æöiCQ¹•dzÕE|š˜‘ÛkŽ[[v-eXÇ5&U1¼¸fÒ“»\•SÝ0Õ)f”ä)[‚oI~×m?<•4v#šh_E¼­tÓ8lß'ÓýÃR1 áûFÉ4¬zVôé<©¶Q‹øj?ti†åo‚~H`ôÉ|I]Y7~‡¥ R=ièæb*¨!Øõ¼&_ɶO|Umª—G‘&Ä•Ê+)¶,­ zØðØ8Ð@§´—'÷w|ñÛ% œ ZK›­V¾ 4ÚÍBÅ%úÆ@M5À…a›æd˜^å·»u@SŠVÉú1‰ŠÈnÃRRm'ßy q<.ŽÆÊ³ÊUï"$—ÿã÷ÄÿöÛn˽OžªµZé¶wi©P+[[«WÃzéK_ŠLEB2uhô ¬É¬À™ßYƒPKvzbÈß÷§ènÆ6"8 a4ØÆ±ycú#§~㓤r ‘„k (Ó¤fB´Û™ÀH»ðh¬‘¹µÚ]ëxM1Ú{be 5¦Ëb­œl}ÙU„’A±zn!ïºeß›’§æn“-˜”ßYa(­¾«Sý–º~;_yçCcNù¸ÜpÒšMĆCh¸Ács)`c¹ƒN«ðeHŤÂw\bCz1@Tu w;æÞbººxˆY~ÒF¶…hë0ëQ×4TêmlX*Cå<²·é¡»»êþí¯<?õ¶ ¯ø‰Ë¿<•{ïÛ·oð¹Óé$ }ÕîÔØÐèA!„²KY†H "˽•öÄÕ»{R Ûòè¶û":­ˆõ•6öµÑ^ê úáDÛ©haiÓÁwé”DÒZrh-mÏbéùçŸ?ø¼ººŠ#GÆw®®®Î;ï¼mÉ™.4zB!do !½#@;èO©ŠçÒ`íÂpîLÄCWA:«{]…J ¢ííRè=7qª·´JŠ-ZÚéÑmy„† 4€örÄÆr'÷o¢½ÔA·Ðmu N!Q°±¯ ßuè4=$:¸(i[ƒd~ôËX¹¬Ñµ…ìvžýìgCD ªxðÁ“F,\kžóœçÌ:{dØ[B!d ¥HÇ»ØÍÊÒÂ?þ¨â¯ohCÜ>Ìr?ĸ۫ú±S¤Ùûç{.ŽÅ¿ÂhQœÝil.wprÿV¯âñ‹ÃS®ÿÖ¯¢Ûì¢Óì¢Ó  …ö Nççhü˜1ŸtyVD<È€7óÓâÈ‘#¸âŠ+_ÿúדçô^^ñŠWL-]²}°— „BÙµ¼ô¯Ú¸þýŽ!ÆãóÎÐ)‘Ò¼XTࣃ¨ Ûêà×Wþ›6àƒ‡ïzøP U£SDWHÜö „²]¼ùÍo|úÓŸûîá‡ÆÝwß#GŽà5¯yͬ³F&€î-{'Kp£ÑùkNæ)sØâ âšk… ;(¡ìDï_ÍT<(†å]c5ÏÏ·}ºÆã^Íäûõþ0¼—üRm§#æ@÷dúx¹«µF¹ª æ—ÈoýÊ–ØOM­¤¢žPùÂx’Ï]Ñ®‰Ÿ×ì ˆue=çYi0$¡"T‡ÄNöÊ¥ÄÜöÊe«¡Xm¢UÿEéÒÒ„ségâÃí¼ý@ŸªâÙÊIyö:Ýà±é•dûÝš}Qéü¨'J*-åûWi¬çkª’¹åôqë½J3¼&¯¥€Ãcpk_‚ˆñ‰Áyu*-¹ÏΗÜUÊ;îj©¼éÈDKÕÙ‡¥õ|·8ÿUßú_pd} |ÿ_Wà¢ÀÅÞX¼ ýÀ§Õñ†jÛÈT:0i±C!­Þ‚˜ù &q5ÊíÏ'Á*oNöCdߨqÅ:¢&Ô*t‚òf;ˆ™Ên¹i|öø*÷ <`¨cÙ‰äæ)fÿçÆßkâ–L5¯M7Áøm¬¯ŠO1¦Ÿß 7Ü€ßÿýßÇí·ßŽßþíß®,ýà?ˆ#nºé¦JÐS²s¡Ñc/àšc®©ÒR+áiM 6FÎë·ü;#\b’VÛ™dNEšù†YTNu’@tL<2Ÿ¯5I·džïF¨8¥Î±$?¹åK&˜¤YÑì§Kæýj• ÒG$&ªù|;O.{°gÀ`Zˆm´³îå$=95óä|¶Œ¯%kÍ\¸vF]³·œ.ïâjêmQg ©×eD]ƒêFÅèQ1n×H:gc¾×˜|Åû6bJmL¥‰¦-ÚzVaÕÍÔä°øÂ8,Í´±²4E7~+y<†5„îÖe‹I’%ŸhŒ ´•4øŒ?§ÒóéµK*ÑËXßàQìÚ(îÚ7nàɃxê~Á‡ÄE‰#;D¢Jcïß– OMŽ‹2m©·XÊC™ù‰Æ$™õi#‰%…,²âÆ¥5¶Ä$±(WÆ31 G1®#†¼±A]ÿ‘D&x¹ïÐ5óûƒÜßQÓöYXFZ g,h™çÇ¢Lê-³s*ÈIëûßÿ>>ó™Ï>ûÙÏ¢Ûí¢Ñ¨N‹9‚øÃxã߈o¼ïÿûÑl6qçwâÝï~7^ÿú×ãïxÇTÙ>èÞB!„1T;p²¾q>~óËÐð@`ìP d‹„† :ÀE`i³F§(OÁGì[[ÂòÉe,\F«Ý¸¶?4¹HT¸n„«³„bBÀ•W^‰K.¹GüÝßý.¼ðB¼ño;ÿºë®Ãç>÷9¬­­áꫯÆË^ö2üâ/þ"~ã7~õWïÙî¸Óƒœ>¥•…ò`XËÇu¸B>µ•@B!ÛH€¢ Õu¼æO~ÀWæ!²#éíúQ@z;[T¤·b-¨ºùIJkKWÐt*M<ô”ÇpÁýçàÀ±Dá‚C«Ý@kÓ£ÙqhtQÈÖv#\7À…a@ÚQ÷BIá½Ç¾ð…¬k®¸â Ü~ûíÛ”#2+hô §MeÕ¯$Y'HTíz@!dŽhDŒ' q­>ŠØsš *ÙhIG ÃGñ± ôcH:žGt…ÑÃw=–Oz8~B#ÂwšBžÖwÍŽCkh¶vDc³×íBBwĽ…å’DÝî ìšQ—¡ÑƒB! ¡·KoÝîC½£œX’ÓÃw ƒ‡àÃ0è‰Àý—=޵ƒkxú]Ãw+k‹k\W{®-…±Ã K!„Œ@£9}z«8‚åí ‚ó‰biÑÈÉ΢p–ˆ¦¯äàš;™[¨Mw®Ò®¨ÓÅJc/Ç^0Õ[öð3™E(žYnÐÆmBà ‰ò.¥]ã_æ)†MRFìzž.Òʤl•é쀚S4\ϯw? Ãg'ÍJ[)¥@–‹¾[ \, !­Í&|÷Ô­M‡FpQáºÚsk‰ƒRRV ÎlªÊÑh7ŠõL 5¯D]¨íé¾LÙ™Ðè±G däÆÜðîŒaÔ~ñ…\$""0ðéU9uTõbEqg ¬ÉtÑxÝÔÀ¼FZtîl³¸ e³¥÷êIRiÔþ¶9N Œ•ÛÚè÷‰ß§ˆ€ù_ü¸ŠÐž@41ù¢¡ ÔÀ9ÃËËå¢<™…·'šðX£ÖHºJ¦Ž)¯ [.×ÄPÀpîÌäw"†¬¶¡ñXò^1ž°ÓOeÉ’Å®‹Q«4‘zÕªÌu?–—Ä E·9ÌÇòêYƒÏÍ _HÐö¸ü‹gíß.ÜcD‹Ý@!Që;«ƒß!ÝÞgDh\Ü'Zê4un0FýXô‰¼Õß„î#Ie-¹.\1‰]²{3Vo ^WÉéA£9mDš=i³e¾'ý¶Zh sU…B!„BÈœ Ñƒœ>Ò‚¸ýw ˆØ_l3-5-©·Çh !„ Ödz wqIú!¹˜r0BáÆÒóW’8ŒßQ¨´ô?‡á}©ÜB!¤=¶‘{ソþ뿎¯~õ«Ø·o666ðK¿ôK¸á†¶|n¹å|èCÂ7¾ñ 4›M¼à/ÀÍ7ߌýÑÝÆÜoçA!.Ÿ ›öÀx8´Û&UÛ…š Š­”Ê !„ìlÊn »-ö™•X^}%—¡‰j€”\O–V‡î;¡¹ uÅ–t×iŒ®{rDKëŠ@‡÷E*n!dnõ©¸0Û–Õ[ˆ Ÿ¶‰/ùËxÁ ^UÅç?ÿyüã?þ#n¹åüʯü ~á~aK÷PU¼éMoÂ/ÿò/ã®»îÂææ&Nœ8ø‡ÀÞðÜrË-Ûü+¶JãqôòK€æÙ=?PúvB!„B™4zlÇŽÃõ×_F£÷¾÷½h6‹À`×\s n¾ùfÜvÛmøÐ‡>tÊû|àÀ]wÝ…¿ÿû¿Çúú:VWWññ—]vàæ›oÆ<°?eKh)øàW¯ü6]¸sÌ!„B!„B÷–má}ï{î¿ÿ~Üpà 8x°ªð–·¼ïz×»ðÎw¾?ó3?30ˆ¤ø£?ú#|âŸÀ¥—^ h4xýë_K.¹Ïþó±¹¹‰O|âøùŸÿùÚüˆÛ窸­ÛâVzIÔ(Jh÷Èê£x埽'Îهм+îÂñâûð(bì}Ö hLDÆ— éí©ÙÒ~!52…)­³Ó÷²¤ïâ&¢¡¡– EfžŠ(ûy×8¿/ë|©QC°ÓX±¾Iqª å¶\^4¢ïˆ[†˜ùJã\úüÂ-+ñ[&ˆLÕPÑV2 ïσ¸3{YG çUõçÏæOÛ@É•¬^õ9×È9e·^Qfüš"í<—×HK‹`É)™Ò¦­6‘™¶…H³Vò4™t7­¾aáü>»Ý5h¶.Ja”Qç¹qå7Ón·¿™•§WÍ6ÑBÜ’q<ïyœyM¿ì ü@:{Ì]§Ü΋‡b-}/Ãͧé"-<ðükqÎwÖÐ<ù8¤ýHQ÷ƒþ$†'㉼¶Ã㵿c«(:…Í—T嘔þ3­(Þh€öúüÑ]Ÿnã¡áçöråšáçNmßÕ¥]Ql†ç[¿/âÉtߢ†¢xsyÐ7§¿0ÐØÉ‡™ç;,M¯DLº å*Ñæ#RÅÉDòÆ¢ä¸D a3ßÌg'è#¼ÑG™äŽùt;ßÏN#WfÛRì2“0úA‘&|B͹Bm©;cõ–Y¦Ev,ÛÀm·ÝxùË_>öÝÅ_ŒË.» <ð>õ©O™÷øÆ7¾×¼æ5ƒG™ç<ç9¸òÊ+ßÿ~fã¸-ôdGµÐlâ»?¸†ï>M ! -ˆ? iœqÉ3|²ÈF³&D–ŠÉð¶ôM…m§‹À›!ÏhK!„íƒF)óè£âž{î\~ùåÉsžûÜç>ùÉOš÷¹ì²Ëðk¿ökæ÷O}êS—\rɤYz+.kØ÷øwð”oïÇ‘Gó¬ú„Éwðg".¸Œ©CH‘åb7˜,íþhÀ¥_øš'îBz§!„BfÝ[¦Ì]wÝ5ø|ÑEéíÇ^x!àk_ûšyŸf³YëúòÐCaii ¯~õ«'Ìéô(o›×öý8çkÐÖY…kË`k$ÕZÙ W‡Ÿµ]ÙNoMÔ4ƒ¸ýP×g]”îS\ë)AJö$N–‡F!€«è†G¡±0N¨œn*¡P)‹uÇkéÛÞw*•­õÖ.šŠëJÉe"´‘~^%Å–ÊçÞßu= ¦[)!„B£Ç”yüñ¡/îh<>‡<òÈ#¥±¹¹‰/ùËøÙŸýYœuÖY§>?TýâÑôÛ´ ¬m ®B:Í‘A !dË”û ®jÀÐu ®ÁuV°^ì¼êÅóèŸ'ðƒÉ d/áý¹ƒÏEœzÿOe  HÅ€ÂWcŒÄOI¶sæøaô8Û2BæM7tKuv³Ÿ¯ î ÛÀn*ˆ!=hô˜2'OƒµZé@ZKKEà¶µµÉ¶¿~ìcC³ÙÄ»ßýî-ÿû÷~´ò÷ËÏþa¼òÜ+'JûÔ´¡ñXaøpûÀ!ÛOŒkx$<EHWuý”ײp­âjÑ7 ú$ß3N‰r°R û~+h8!„, ÿå±/âŽG¿0ïlR Sfß¾aÁN§“4|t:Å΋••¼Û@a(ùÕ_ýU|ä#ÙÒ.¸éioÆ’æ£1„en¯µ¶ã–¶ªTYÄq¸mUµSÚë ‰¨Ü¢vÄõY`©h8iF·¢‹›*- ²ýV$ý¬R*@ñþ“ÏPcvÄõib¦]³K);Jº¦FÝ€$”y¢óƒš9ªÒÒ¯ƒŠXÝF®m¨xh,oÒä«z¾•¯IWMQÝá÷ìš‹=Ñui·œlÞlw}ãœäñ¢L¿ÇÂ5jü¸"ôÊͼ°ÛÄ”;WñûRÊKñÅ÷bÝì+ àÜ~DiA´¯®dýÀ§ã‰DX^0ª¥EŒrÝrûQ5þ÷]3¢©šeµ¯’PÒ¨c¬ýè—a>Æ¿´”Òn(‚•þ5ÃÝd®q!Úç¼KOÜ„cÐøxÏý$ õ¶ñ´ËŸý ý*Ç+ñ€Û7¼¶äÒgÇl©i¿SuMãB¹"¥!­6U¨º¤ŸWr· ó¹;ÉScŠ.B´›HÃAÅê·³’ØÓ˜ªYÒ4TŒ\²_i¹xù¹/ÃKÏyÉàøñö üá=œZ~ ™4zL™óÏ?ðyuuGŽ;guµè˜Ï;ï¼ìûßxãxûÛߎW½êU[¾fÉ·°ì§aL°:¿ò´/%×ï3&ùâbp±'b'˜~Û5פ >:ê«=cf‘vÍ6íÔã’ÒäkìVuõ)å#H¿+ Ho)ß™F¹‰¤žçJ:¿æï°$fq/{¡É‚åm9â9¶W–$x]ˆÔ5µeD<Љ3€ØŸàM°C\¤sI.µ"ݺ\ÿc’éL’·T=Ÿ^=PDˆU®R‡eþög¾‹‹¾u)®ü…®?øàƒÉsúÇŸóœçdÝû·~ë·ðô§?7ÜpÃée’B!d›Ðx /ÿ«gài_?tâ -ü‚!„ wzL™#GŽàŠ+®ÀW¾ò|ýë_O6ú /¯xÅ+¶|ßÛn» ÇŽÃïüÎïL-¯S£²êVruF'ö¶õÒKÈÖÐê$¡º‚<îöbÝ£8‡« doÛ÷>—cÝl×¼ZgCu zÁê¡"”v€ÄÁoáº÷?ðP´«cÓ¥±æ}$Û1 \bÃ\Ýc !„ìl¸Ócxó›ß øô§?=öÝÃ?Œ»ï¾GŽÁk^óš-Ýï£ý(î¼óNÓàñ7ó7“gvÊ(âà_±}¿]øk{èÊ@UBl4êÉp¢Prc"$‹Žþi\üƒ†Ù»U.`ýíïàP„Aü&Õ6b<Ç¡q㮯}Œ6®–ž»PrkýG™7®çâ2›ÓZRKÇ6pà 7àœsÎÁí·ß^Qs€~ðƒˆ1⦛n= !à§ú§qÍ5×àèÑ£•ó?ñ‰Oà/þâ/ðÞ÷¾w,µµ5üæoþ&zè¡íû1„B!9h×µø74är§'!„ÙC÷–màÈ‘#øð‡?Œ7¾ñ¸ñÆñþ÷¿ÍfwÞy'Þýîwãõ¯=ÞñŽw ÎÿÒ—¾„?ù“?|ä#ÁM7Ý Ø)ò?ñh·ÛøèG?:–Nû÷ï? £ÇøàC`Ô«a,}?Ê:Z¥­¨Õ•MD¡WíL/ðš¸ìmÄv´÷tЧIoV´~û‚$>êÓÈV$ïÉH?—äo—& [‰äjÄ-Á¹¼ñ’y>4fo‰7ƒ’j’R¡&ÒÛ¼ËOëêÓðyŠv†õ@Ëç¯÷V_s˜ nf® ›eW|º~:CeCì@mÎç©f©†ä{ªCZç¦AâD–ŒßáÍ€¥ÎžVR·0Õ7t½˜|Ž}¡ñäøqÑPu°ÐØÉw‰iW‡X§š‘ê'\„Ætûª®”ÆH`FAïHÿ¾âÞ-`Ǥˆñ #¯í’ Fõ¸¥ d¹˜n!5Ají Ó™ïIÒ— E›„ÊŽ@{ÐîhIU*–³ZqÓ«*Lõq²âJc‰~{7öÛúî3uê[è·û¥g&.ÂÕ´Õ¥Ü ÇÍñ“ëp˜ Í»@Ì|EkÌ'Íaý¨$b)ü¤Õ‚F©ôÓÒ”ŸŠ2“Æô %v F›±#ÑX[SL4†Ë[ZÁ¶ówI¥ÆÙuƒ@£Ç6qÝu×ÌÍ Ž IDATásŸûþý¿ÿ÷¸ú꫱²²‚µµ5üÆoün¼ñFx?lX.¿ür\uÕU8zô(^ûÚ×î¾ûn¼á oÀúúº•àMoz80ÝÌgGÙ6üh¥5Œ¨_j5¶Ó–†¤1È— Utj9ˆXқƶèø̎I'%e!®™Ÿ¯-PîèE|¥ léúì s³FJ׸&5 ̉‚b#ßXP;I)¨Ô `Œ{Y²¸"ÍdHûÂÀ˜7p4U6jI¹õÖT=AÚˆY7Ø3»<£‡L°:íÊ¿£”oKPdÉ0ÀySê±lô(·ÁV\ƒžBªœD„x"yM·óXò¸…8ÃxSC4 .fp@¢½’Ì´+“Ö‘Iöð}xl%î”exÝGÓç»<¼†S´) C³Qo€ Œö†‘0Újí˜àˆô;7ë¹s€¶zç (âã3?q!®þ„Cãä÷Šdû®´å¬”ò]–¼U Û`A0û£]Ê],È\À(®É4N©aœª!†tý·”þé:a®l`*=gçöÆQ7{Òί1eLë@bz\¬,še+ƒe2‰!8ßèá³Çpvß\g I—jÃcïõÖÂÖ6æ®ÜFv24zl#W\qn¿ýöSž·²²‚Ïþó•cÏxÆ3pìXÞ*!„BÈ^á‡ÿëð݇Ôïð „²·¡Ñƒ’MyM£yñ`Õ Û}1®çˆç ”…Ê´VË;HJ»;Üô9ÆêèÞ`ØU„T´ï†Ñû»ÿµÍØ;–’{K\Å¿Y솈ë¨U)cõK©öÑsL}gf”Ú=çvYŠÛ??< éù>qì@™64zB²)o#o4Ÿ>˜,Äx¼´Õ1¶iN²í”RCyò%û†ûCP×›¼—&¢ë@w†ùÛA”·ÜKÉÈ¡À`Û½“å žPÃeŒÌ—¢/é½›H8Þ3‚TU_*F«ÊqÃM¯äÞTIOz ‘©áý¹ƒXEÎíÇÀ8ÛÅÀµ+È.ŠÛALúª*³L =!„BÈ.#@ã*¶¼ÃƒBÈž…F=ÃVՉŊÖ]{ÈÆ ¿C e¾ngÞ:åg¥ñø`…´ ·X[k§¹cECek}©IÇ®#V~­z_SצÕ=7Я}Ÿ R)H/’ÎTæ™"fÚ%¥‚êqûÙší’YF$–BT]z«ác÷Ÿó„1[EÄVt1·ÄWŽŸÚŨx&© Ã5N­ Ÿè@ŒÈ’ö5ÛR=ª¡6ˆ«ÙÎŒx¨µZoµoÒL>+Õ$¡ØR(+•vql¡møäïY‚sã‰â{+pó®$Ûï<&ƒêõ,¡ªRÅ€_z.:g½Nö×î^%„²;áNB!d;é­Ii7Û`E[Z(¶‘O+­’E­${œÁŠåˆ±iÁwè \1¾+a»¨zÒDh6á©Î2*»z;<Ê;†ш2_hô „äSÙ†jm‡æd‹Ñ-Á×s#jÿ»CÐð‚æIÊVÜ^Êê-eâj/Ðãp»ñž¤ôlœ;8|'%#‡jqàÖÆ¶kG¡vŒ ;žQ©_*ÕÁFã-h<ލkE÷¥m´WÿüšGW;BuR>fH$¹TÜ&JÞVK®‹¥¶Ò-sÜÐU®Îн…†kRM©„BÈ,¨ÈÌz¾wƒ{BfÁ7_þzüó¼r,~ˆ"ö&æ¥x4pBÈÂÀ„BÈ(·zŒ¹YB¶…GÎ?†ÐxvßÕLÜpçv†;Y2Û«=6ÛDKQJŒ~­NÕ%³VD(,#« (µÇ¥—pռЀQ½’þoü–~¥ oŒ‰BÈW‚—ÙGuóÒg´%5Xe:âdr ¢Ú­‘‡N¿s×8>!é!+ÒÔ©b7\ï7¼‹r;¿¼ÿ™é4,å¡:¬ß—êÿOy¯\…*C6Þ?:¿b¶?6™ý¼v 1ñÅC’“渚Á!ÌЩ ÒÔÀÒAN·ê€åõšë«@<~êk!„B!„m†;=Èéãœ{¾ƒsÿeÚ¹¯·êË­¡„B!„Bæ 䴉݇!áq(<&2vô%€+¹½hz;+!‹Bj»ªé~E!„ì1D–à|á§Ú|áš¡P:*MKõ›²˜ÐèA¦@€* Ò‹ß1©d”øÂð1€ÁýÈB¥B!¤žR_9Œ)ã1/Šñ"½ôw2]8tg8–ïrшÔ@£9}´pgQ)!„B!„ì hô §M ÿ°T@J»?D–^Dù±¨óƒÏÐõÄñ*VDyçV’ÌëTfóû’ÇÍhèÆ®™¹»@˜ b(sˆ7”kRÁÌï=H—9qÉHå…òPž2†¸ýU÷–~zÚ„$¢Íïܨ¦ÚLú¸Æ @Gõ N…Q·ÅìÐOçÉg+|L£N œ­h“IJ "ÖjÒO¸:YÊQ§Ø&r •„·ÒÂ÷“Ç Uˆ­¯+¢Yv­vošh4Þ¹4>2B2w^š*-ÚI«±ÔÕåÜ]mšV¨í$»©$Ûéâššv/YÂ\Ç ³À·jT„Rå'nL· 7ǽÝÃÒAŒ'ŠÏ#ãÉaË}Og¨5ᇓׄp"+I°Ú|{,±ïŽ/Ü u£/Ó„¤ Ñƒœ6e©NÑRç5Ú™ô:0‘å‘sz1=*Ÿ+|7·‚5سNŸãŽsª19hV 9èøù>LÆ0ÅoKLê4äËåNkÐ\;I ’°Ý±,ƒ7ʃ‡Jjà¨!Ñj—ioL¦;H14NV®ïPö²9ˆ÷È‘ œ{=Ï|‡‚hËCNêö8LùbßBª~ ¢Y׿ù;&j¯r1Þ¹ê¦aà¨{ÓiŠ:+;:Å~eÉÓ]„[Ö8iž m·Œi[BÓ’ÅE¹p¥ÓŒþÑjkƹ²¿3#Õ× %}GÆ©YÅ?éªCWg7Æ›eZd÷ÁÒA!„B!„…„;=ÈZ’+«’Më~yõ¹o…/ –­Ò;u'![GàzÛè›…KÂ<];!„ODì¹`Šv RìôPÄ¡ÛKÝÎ2BFrúXÛ}Å×úܦïÒŸ Ù¥ˆ[†“ýw!< ³u×-B!d¡‹f£Ò´UæçnFNM~Æê-{Ø­–œ=!diî{ºžŠgÂ÷}Ú=Šnû”®%„B!dÐ豸õ»ÿ/œ®<| ®:|Ií¹‚˜¨-d*G(¥ªZ¬ºÇ ·-Z¶ê¢~G+è£K+8¿bæ×$s7Šóã¶·J¬Xj¢é[+¹A5ÿgSSFDÓ“ÿ8Áû°™Y8—÷¬M3ø¨}Q=õ0¢Î‘œ;H+€TZ¤Ô?ÄRo°’vézã"ÒÓ5¥iáµ¢Ão$WçTƒ©t1WUñišÞwI¬ qÖ{ÒN:0©Dë9n~ÙJÒ4˃yMåúáµÎ,\¶h\³ŽñDRyè»/¦¾Èk{FÕNEH¾sE0Ú]g·¯ÙJ%Q×2¯1Ê›D£ìv’½Ä$A%-u#‘åì>ʪfÐ^Md®C\Ëþ.yÐúŒ`µu™õI5_¹ªÑ<;ëüiRW×\b¥Í>įh'†Ä¢£:UKÏS\fðQñf}¶úµFc‚÷‘Ù6˜ýÒÂq nSýÂVo ÝcyyÚb»ûù'ïÁOÞƒ¹‡ì†AÙ,Å)Yt&!„­f¬Þ8f"5po5!„l#E ^ý]‹§|ý1HìïMcIe§Z¯›áÒÊïfë±Bq¦—ÇÁÏ‹ÇáRjE ùÛ åK.³Æno* d}Èl„ÎcYçjy «~¸šz©€‘©šÝ0•¦µÆ¢ˆPíþÿì½i¬lÙuß÷_{WÕ­;¼÷ú½Õ­'6Û¤)¡H™ŒEµM±¦ØDâˆ$#€ 8ŒäƒA (ûCàOF‚¤@qâ8 ð K¢%›E‰c7‡îf“Í^OoºCÝSµ÷ʇ}†}NíunízuëNëÜîz§Î°ëœ}ö°öZëŸuŒÔNK #Ä¥1Åz•¶7·JZÚ€Ì8úwúw0&ûTï÷áf™u1·ÿG~ˆ“\M_[è?¼ßU⤱Ä`ðXýÙØæzDÛu›Èþ˜£¶«ÂU>g²bçÝ­äv²ÙÒôùjsFn+…:j¬ðÌmº_‘$R Û1ÏBÛ.”ÉÚ+0ÂõE„q¢ww…ò¦UÚBû™~ÿm®!JØ·ê‘«÷% ãU¢M€|4fˆÕÑäwЀh#ñ;¦Ùê²êbš0ËiÃþŽiÍê-'(¦œzÔè¡(ŠrÁØ~ófÃvnuÂDOQEQ”< WÇÊ:1ÌÓäó *ómœ¨zôÔ7!‚LÚlZM òø*QØç÷ebÛãÒצ!¬ð;œ ¦#·‰i•–>õ–v[rP÷}Œ¦Oì>Ïêýdš4Ï-R$# o÷7[*.Š¢¬5z(g‚ÜÁ7Á´Üñ›uc{[¹Èò–²Ä]?yi»(±+î¿:å†Kç¤håÞXôdqNøí}Z×eð–yý’z)Ø0œmbÙ‰¹}Lçxjå*©¾³B^ &éÎË놓È‘Ëcê1’œVCÛ1CâóD•¿S8a^¹Íè;(Ï€ÛGê¾·'À¶Vyè+‡úrÆê´$Y-ÕOIšþÞemT, qms Lê35ÆT9o‘K¿SR=„Y.¬Iˤ .ò"FÿyR÷X(+™õ´+ëëûä½Zý¹kúñ^ã+ 6½œ×¼1žÍZ%ký¯¥œ=Ôè¡(Š¢´¸þå Ø˜Ù4 OEQÎ3ŒD M€ 02=S%æ)f3UrQ”ãFŠ¢(ëBðúϵ©žD†ŒZD€ñL¦ìTr–BE‘![‡` Af@PðÑ»yQÃ´î•Øs‚ª•v„àÊ£ÌË{Mu^£ - ³ .¾ù7Ã9Ù—^gÊE…#/%öq‹¼» rë÷Ùƒ!‡WkcE9^Ôè¡(Šrˆ2Šë0zxPŒºÄ €Ø€ìôd˜W”uòy”ïÙ™«ˆ‡í¸þ v¹RæÚ.¹-#ö >ÿÃoÃC/oâÁ¯¿S<[~åaR.$m#d–:².[‹ÕÎØÀ¬1äd¦á-JZ;EQ’$*ŠrÏ ˆ†¸óÄÂ]þ@©PN€2§ÇèQL/=ŽO}øøäÆ¥“Ç*ʽÂðÉ?EQŽõôPî¢Ìj´LrÌÜIÙ&[{k»œõœ³m€ùÔÀÞŸy‰[p½™Õ1ù¨ä¢¹„J‹/BÊŒ/=?¢ôw}õMÊU!%Q…ó^ú;ÉíYºWd’«¸„Q¶j ‰á‘ôÔ \‰>G kɦI&J>úM|}ŽÕ%:õ¤ô ‰7EIÜÍSÙNn7ˆ²õ·ÎŸ§IÔÉ(:«åÍ}óþnò2®§¾§‘5®–¼öйÈ.Wî½b¿öy“Ä~åˆÄþKä—‘ÊÄdÄÊ`7ÚᾋIx¥¤‹dêwyŠÛ£ýmØÛEKyÔü^“‘¸í Š/Þå×]IU&ûöÆa'ÉœøÓÒJI‹d43.¯íñ~’ýÛ½ê®DFV!½ƒÞOÒÊ*vØc\•ä¡s±Í{ÇßþÉ>ÂÕ×ý^S#L4%5$ƒ)Øï'¿ñ™u½OnU wR×ßÑmæú?Á¨ Jµ¥ÊÆì%/Å—áÚ ©m— Òï³Â1^VV’ ÄRýäü~X|RßEÃdÅ=bcä¶'ý>ˆ-? í%C¹< Ø@ÅÓék“kµáõx’Y\È 5.³M”è“Å56oœH|˜mÄÈmC‰rû¨P ·Fešu^K9{¨ÑCQEQEQ”³;°ßìøˆž?éÒ(ŠrÊQ£‡¢(ÊGôœŠÝo»¡4ÕJÙèsâû‹¬ìr”Áž]·Ä|h ÐŽü‰‹EöP…Áø=°ß=º,Šr*i’ SéÄp•hEQ$¸~oÚÞ;±G “¢wªúNÕRåb¡FEQ”³NËÕ¸ç¢ÙÇ"i`ÁìÍþâD+o€ÈQ S‘Ìû1$éÛ„®ðèa¸ñ}aëäVã©ÎEdÑÁ¬rÚ‰CÄÆT̉TáÎÕ¿L.íóù•,¸5*ª¬óZÊÙCk‡¢(Š¢(Š¢(gŠ*ˆ˜Ì[Q¥D==”³A_vúœóàô!%¨êKÇ)¥›ú³“g¬œ¬:Ã÷±?[w*]b›gëZÏrþ™—÷»£¦RÃh…¡¸ÎwóÛCÓÔ=‰Ýô½¨Ø"%)ŽÃq˜§ òE'w3 kƒºL³ZÞþœø J’ÜÄÒgÉ{‰}bÈ<$&¢ÌO¶-3WVÁsëéUÔ:)Ø È½XÞÜ„Œ2Nð°‘%»{Ÿyª¤vØÞ=Á>Ù.Ó7g×]²ÉkSËkpÁS-ñü¸ϔϥ)G¬ÒJ´J¡Ìì'`LËÿ¯ò=È»‡Ì¨û4EQÖƒ=.¿ó¿ƒ!®¼¼ò–“.ÎÊIuòDöì_Mqîø}’®éew¢[áá%9ÉÃFòyH÷\¸­13¾8èŒÒï^bàÄ‚¬a¶! )‹ñ&(Šm&³ƒ#'Jqh…ˆ`Xc> IDATmãÆ¤½_ÒÀíâ'mé9Kñ¨¶àÒ}ÙÏ‚¦›seg.¥%¹eYnBz¶IÜëcèêIN'òówiism EYe’”®¤I3û(Á‚Õ܆¡ÇXpjI”—É)U 2=mrî$4­nžß"ùˆ½¶d@Yâ9¥®ÃN¨ï’QGV7å™aÓ”_E³) cA½){lè ³]—ŸÌŒ}> .^€w7àf/ƒ½#­n”ýÞ,&c2UŒÎŸ¾õ>}ûëp>ܘ5†œÌ4¼EéA€¿{ý{±qWEQõeçT–{d„ í´!ü")ÔA^)Igu7ýõ$Á`x=kö{`ŸÈÄN ³ÙävYg˜Çb6M_Cb ¿ÑHx¶Qû˜*Î9‰ÉߨûÒ»óTt7– 0ékSB—;~™÷q´Œ§ ¤íÞÝ…w»Gï1> |#µ­=jH½56Ý^‰Š f&~UÛÜi¯8RrÕ›ÄÂæ«1™ÁNæ5\)“œq ³•ÜNf#½†Ñïhdj‰F{©EÄu—†hêeG©zž’ltç˜ïï¤w—rÚÐPüí« û"Â}ñyï Áf÷ÎÝJng.’¡ Æ\‚1—“×–ž“•|Z!u’ªË´gì#´WÂ;è%©o&ïÁ‚9=6°Â؇̖¬bD)u<™Ñ†¤àÖc„Ôæ¤ßѺ·Qx¤so&û5æ)8·.Ju]­ ýc*߈{á=/¾•¾ “ã2[m¼5qñž~pp-]`¡]§ô»94wŽ ¯/”È©z‹ÒƒÖEQEQEY¥$fEQ”C==EQEQ%E¼º¾HÒfEQåÔ¡FåœÒ˜T«2Äh¹_Ç®›,¸—*вâÐ 2;¨ÜÊÉ{Ä.æÌ8C,âPiÐâ²2¢fZ®àJdê‚Ù&Œ)v÷•#½ԔÔCàôÒ =jˆÛ%&`0þÀf‡Ÿ_géEQ”{@Š¢(Ê)¤™x„IGø·ÎÏ—CÊõ¡,O+¾¾å °þ²(ëÁ˜ËøÔßúXgð¾ÿGŠÒGÈé±¾¾Gsz(}¨ÑCQEQEQú ¢m¼ã —áBæJEQåT¢FåÞÉÌÖ¿¢â‚I&#Œ;!-Õ7¶“q?ZMætvxƒtfs†OÆ÷2 ™ôQùC ­!.À)u2ùê¢|*xÖ{¬Åå^ÀÊ!C{úþ&I`’î‰ðœÌ–_ÎZ/ª¡œ Ì…°èœÎêTÒ÷½R@Jž+1þïõ,VÂ=ï5Ç{_Ÿ#.+ÁÖïsøœzo¨zàø¶\®•c:ÞQY»uOPí!á}‰s1<ˆ}P^€möa/늕¤Î%K¨Í!”¥Rh)®½»÷oãÚ³Ÿö8@h£,Èl6÷ÚÝnU}J\»O™FzæêX²8á=¸‡ãy‚+Ï|€Ã¬§Í§Ú3-zÏɤÕÍ +†9éý~Ád+Œõ©Í¤úZÎTß[JtÒ;+*WÑ0ùŽd«æ*‘øä;ûŒú®->óž²æª˜¬z *g5z(÷Œ<‘_"v9sÀÍð醘½(!ÖÎã!u(RÇ‘žPçB &-!H6].vùoïÿNtÄI MÖ-I‚q™‰œ(SHò$[–Ÿ¾rÝç)˜¢:Ë0O[Ï3ÿž[d{¯ävìA¹‰83û†¯ïu¸¾Ü^…A†û× àª-ðŠX€ zžì+U—²ßŒÙŠEêRn&u’ç6_ˆÉ«sÛöeÂ8Ü=J©ïˆFíð³ v=ïTOŸ“­à–W§“eía•@¢\KôQYç𶍠§>Ø”1¼CXý<£ÄÂó'êOnH¥GO·:ãrû·–že0a\I¶SÜöüî”òPH?ÿœ i¸¸rúP£ÇÀ.ÃZIn² ÑFv£î…8eѲÄÀX,“¨z`èFù=Ì•h{lÜ8ˆþmjù1†oMHA¨“+¢)@û‰ý{újá^ óVÙ‰çÊ@m,1HKwšµ´æÜ^Øî’rÿÅ5cê‰yHÙÂD,Ô;An˜§ÙƒÃ0éÌ Íf¯‹ß¥ ]’Šh ²¬¡“PR†±mÈ“sìK)IìJo$ØLEC¢LÔö)'Ñ“Î;8¬ Æ\‚5e2Ì8 À7϶%Ý+J ÷ 0ÊúXåaìáãíRr®ùÒU™xðªdÒ¥óD‰¸nPÞ»*D)>WU¯*£LÀþNú±ñ¯uÏ,ˆ6[ÿn®/M˜ÛûÅHÛ¹²ê"¢‚Rß!K,¬d+påª “m“ aL+Á¦ëû2Åñä¶}.¡ÏYJ±gÑqÔ×èW¦~?K²êdÒuºzo“pïØ·îU¬æ `yõÄäö=öÅ\£‡4¦*† UÙ’ §ra¦2 ?[”T6ãN»T•©y æÓK oQNjôPE¹g,Èî„ì`àà=p*¥€EQåœÑÇ-¨2ìn¼µ.’gWƼ¾Xô.š)ÊyFŠR¯ˆ÷ô´ªUFå Ò¨ˆ™ùUT…A–/Uºð°ŸDmKìAäêUF‚mö‰<†â)å’o «³UcAƒGšÝ‹g×X8E9[ØÑ;ƒg7àñRðzáØS÷S{t½‰,&×ÿs[ì|ù_¹å¡FEI@æâ>«xw5~\4Z“SŒ‚‘£‚`F¥£à­ /(ͪbþG´Q× CãÆå»%ééÞŽ[¿Ê`žS:©¨Â,h?~¬Ù¬FE¹õÖïF1&<øÜUÐî»u¢áó® S…ö¦¢§ž}Ãþö>>ðe"×x/<›µ†œx oQzP£‡¢(ÊJpØ}ô]8ܲ¸ÿÌxvû¤ ¥(Š¢(çžW®ÏðÆwñàsÊœZ¹¤ÞóÇ7à‡;ÍbŒ¢\PÔ衬)ÑVÞþ+'•ÌœrЛèËÕÿ™¦“Çõœg©dbyˆ -¹^eÆïó@RÝÄþðÉzä>«íð±§Çlá0˜ü–Nñ «ç<ægN°K¬4Ùl/‡Ó¸²WË"´ET+ Dõ*þ¼ìoH†àÙü„¾€dÐÓVJõª¯¾Yw{®gfM"k×[N_}?•P~[’Oß /¹sïyr’÷õçâ˜Á =Æ9¹ü Y9æí~ï˜îOÃû;eÝV¤ŠïAŸÒ•|ýÜÀœº¿ÇÙÏVI˜ ð{Ÿ@hÿ `‘º&Õ‡s2T..jôPîQ•£WêM`‰F5©zÂÓä• ^ȦO|U¸ÀAmèhubY啤öB´¹Âp™Ly_ Q•c=ˆƒ!5¨%îØA”¦•r·øýtZ—À&¬”ÃøàÉÁððî&ì7¾‚M‡ë.#9Ùë=8˜N\_RÙ`DÅ•qºlR³ŒbÏ ‰l­IuôN±oÊÍ~oðÑ £‹Îe ´»¢Z’ωéüB¤¶D’ñ–Úâ09M¯”²°h(·£õµ£‰8Y4ª.àÉW£ò .J‚d»è²sèdµÅ1‘¬N#Ž¡*éäi‡f.zLZñmi#M&â{èçíÀ½ñOê7ý~ÔÖN:Ïêè²H %H¨žÌ8­2æJ^Ï,±(rô=¿¹R—ò;¸R‹9’^•ÒF=ýö|wTÛãØ€T½E9%híPEQEQEQå\¢žŠr$¶Õ­ÿÊ…¡NÆLå ÎÜ žÖ%Àìä³qÑ„¥jÅT“Õ*Ê’´BW´}Š®m+J…=¥¢•ÃÁv>W2¥·Ü•‹Dˆ .ž>ú 8øØåVcJDe,mÉ]Çtòu8N˜ôF¡/±|is•ϯœ¸›KHÛEYˆ8|#Y&wÇ™‚]®ÒÊ_9|‘oY5~†—u^K9{híPEQEQEQå\¢FEQEQEQEQÎ%Þr0fƧ;„ÌÍynoÅNì#0öÐà1ðìp©VâÜëð¾R'Ýu¥lÖD‚RX&~Kòé,ûÓâ+µ…óÀõdùO»S6÷ ‹°}:#8 – RŸÅ<¼K«oˆ×0 ›~R[=<’Þ_KÙúz cÒ×0´Ü.½7ÞÝ”Û%IMC/±  "ʰ÷H*g¯†BxÝr䢊Ȱç·­Vf(zç…ý=wU]¢SIc;av·„ýá}Þ¸ÄØKéífœ,™±ü 3eŠEeAy(ŒµçŸaµMÕ[”Ó„=.¿ñÌÿ Cßsÿûð=÷÷ñ^Œ,hüN|ö‡Áûþh >üÒñ^O9Ř0¦á åxEQEQ”ÓÆ¿í“øäëŸÄÌÏNº(Š2‡=.ÿýÛþ+ŒmÞêM•JÝñÙ|ë¯0¾ëã›ÇwMåÔV‡aÅTX%PEQEQÎ>zð{ð¡¿·‹ÛøŸŸú‡']Ei¡Feu°‡w71| ?ñ¿< ïo×®€ì÷7xÒUÿ“@t×$ƒ–ßüŠ ÅàÚÏ€oýkL§Ï¯þÊù£Û6Ô.¸MH h³qC÷œ oQ”Ó Ñ8íb΄ùpC ãZÕ{&S+û¨T§¢(Š¢Ì£³Oe¥0‚ô"—†ÓAØi…ì1ÉîÃá;ðñ¿=ƒi:¾?J#±Y(/Á$=­Äµ^Ô¤T"d'+¾7L’ÚŒ¤rP+å„r”ê-~/©ÆÄ\ˆí‰¤ÊÁŒäï'8Ù'RTÍÉ„¬¨%ÕEc.§w§M¤î#ÑH\ƒ¸GB.:æÃ´â¦r»”»—ùΙ^—4>yL(jÊOV¹†÷Ýl彚{6åùhê™ßÞ€8êcØëòºúXYÕé„‘Ú+I ¦Ì•s$ €ÙO€Ìç!wâ’aLT#YB8Û˜Ö•b^©Nç\yâ( Ê V|Hütæÿê\D;0ö¼ñö¿†Éðè—_M¾…z`åwZJÓ¥å@¹€88øâ@xÞK|!K:#a$ ×ÞJ·K4”Ë›¾òé61|·Ìà1q}ŠÔ[hÄ÷š§õõ©”Fe.`H’×4`J<[ï@$Ü_¡¤,$¥>‹„s±—Û8I6Z¨ïDc¹ÏŒzR} ’炼'Ádá#^ÚÂKoe|å½ÏãÉ?|®î¾¸7KðÇu»ÆÆ>ÉÜÅÉÝ™0FI6ZR| ùOòTOÄkÃ@x• ÒuÁûDÛù}æ*ÄtŽžvצ¥©™ yâ* ‚z ÁÉý{Ž‘p2÷²dYl¡^õ-* x¿ŸU¦e¼]óå–ÓÆÿÞkï`{Ÿ+ sð¯÷ì•wUÆ, oQNZ;eÅ¸â«øàÇð7þÙ_Å`ïa #ÊË)ÊÙ€ÌvXù·WðçÓxê»^ÄáÎ5 Ó ©œ2ȃËðÂÌe€6OºT„frrx0^þö7qãm/`¼;ÍöJE Te½0¼{ôŽŠrFPOE¹'Â`4veõ<…¹ý‡ØÞ½ ï'§FÏ^ ¡´«º¢´)W¦ÝmüÍß};¯½ 3½Q»Þ+gv@í©aá[ðÃM0˜é&à÷4IìZp;/`ï`Ã{¼ýKá]ù¶Þxèé?d}nŠ¢Ü;Ìx*ç5z(ÊÒıš;=#Ķ“{# {Yß@´ß^cQ”ðþÈïáò³ÿÀdvÊÕ ªi]:ÕÄ.ÞÕ3£!üpû÷]ÂdÇàÁçî‚fÈÎQ¡dSŸ|bË>}p{e¨XååášýkÚÒÍm¥.©(в q»õÞßp'lCe85ÖÞÂÀ ô FE9&¾Œ7Ö¬ûÊ9€@å 3[ì&KÄ+§ o¸ýáÎ}‡¸öÍM ŠQ2‹²:BN„”dŒ@³2¹©Ÿ7$êóPEQ”åQ£‡r¬¤³‹ç&J;yRÞ‹dBgøVn½¾ä£RNøT‚*¦¼Ä`!Q¬›ß‡…¤„0`ä%Ç:I—x†ëI{r,£nNº¶Ä½]uvÙZ)¬þ ›¼­¤‚ëª ©÷ý òr`ßÿN¥é9]ú=—®îw¸èýâçSlñÔû_Ààpˆw͸ W¯m“ $C²ÛL• )É(¦H%Éö…éõ%‰]Õýi«©ÔpÊ$§£¹ïBrÂ# Õ÷Ò5(ýœB¿t‚F)d‡¤Ä¤& { C¬¸Ô§†²D[–Õ^;¹ÍÔÂf©·„}O_ÿ¸\¿¶ÄïȸN¸·h±jáû)_…±«¢œ2Ôè¡rFp‹Ó˜C7ßV»åïñÀĘt&vCÛɄ̻bæxI"q! ¸*y >-ïËSx2•gª‹,“á_¤g ›¾¾³0é¤á–‘ã“&câ5ü~ò˜¾k/£ä‘4ºP‚’@}oÙ˜–.õ€5ÛhTA:á)9Yi‚vÄõÓHÆ“-…äˆÆ?TÊë§0B["ªïÀˆ aû'1)d‰ÍV˜Dma|g?üÏ®€Ü Øûd˜ŒÓR;Í\¤•Rh˜®ï\ɳ&JkÓ¿Ïû=á]ÈWP•1̶¨b$ÚE-Ëôóðþóɉ65÷ºNë¢9mÕ¸Ío߃´,f8ïü{@p¥¢ÖI!È‘ûiòö³Uz—¥î{¦šVÿh<ÛRäå[aî“®ÔzúùU;âü<0¹†˜ÞïDÙ_ù]Nµ—“«æ'0lÚ˜w¢‹s²jÎñ*å¾®O5/¡€UnóL Q.iõø5^K9{¨ÑCQ. Z½Ê©nÓŠ¢(Š¢(Š¢œSÔè¡(näÐp IDATŒÚ*OAc½ÇµVQ”sJãõAn ?EÛ…YÓ!¯º¿Ý•Òv’REQEQî 5z(ʽpDÜâ2á+%òîîêD›õg`'† (-š|éí4S‡±qbäwaî¼ØìT‡Y@¥ˆ.êçÁdAQŽ&Â[ÚÇ·ÕZªÐ…Ó˜×HQ”‹‰gZ£zË:•b”³‡=å Æ>€Ù¥wcºµÍ›ß/À7OºpŠ¢(Š¢(Š¢(+GŠr 2€½‚ýkW°{ŸÁpÿ*³;'],EQEQEQ”cA;¸ k»²€Ë¸À¦]×­¹?½{ŸL`r¿¶JBœµÜ{abÞ#Q˜–¾ƒœñ<™Å•&€ÜûH‚¢™qò\D—Á´#œKPˆð{ÑNÑýäÞï‡_ÄÎsÏâRš3½ŒiñœPb)Éé2òšBöo)+¸ÍÏžž++ª:2šžØKRÁésyw7¹}4z+ÈlÏmŸÍ^Âtòõä1böv˜Þ_5¶8)à  jQY¹Ü¼«4Šî{\ßÀ|0 –Õ7f³o%·{5Y߉Æ ß’¸FÑ~„ßÑ>שw0üŽ&ük¡5Q‰E€‹Ö5šír¾Y‰!íµÅ~Ï 5&vðœ®?F’“í“ðM¾Vl¯Œ¹/´×4 Š&å3ðþ&˜àý>*u.Ã=’õ‡Xø¡}­®w©U®†H… Òm{Oø–pOØï÷$¨o £²7 /Ò{.õƒm…—æ3a˜T$é•a6B%ÈûŠÏœU0 ¿þ$Ë´•ì;©§^eC£l¥¤^¥´Ôþfƒ´‚[PÚ)UÕ¢÷ Õ^ñA-_,µc"ìB(kïÓ}”8ö¡Œ º”^Ôq¤ëä=ƒµWìÁäÚû• Æ¥¯! Êb÷„‹:÷Fæ5†â{+a­0n‡ VV怛ÛLs’ÛŒ ÓKf^cÈÉ:¯¥œ=Ôè¡(Š¢(ʉÑ’Jã‡Ù¸€@43‚ÅH“z*ç‘8÷Vm Ë3¨(Š¢(ý¨ÑCQEQ”“¡ë%D›€}ä`<À¥ç3@ð€ªM)ç²Ás„,@›µçYÊ[NQEÉGÊú¡Qé¾ ûxãzØì€Ù ³»'VÄs µ4£qý—][%M Bf' ç°Í$– $C-Èâ´ÌZãßaìÍï Qý;xö|Ýõ¨iÄç=†²ž[ØÕ.æDcÐàa¼úïÆþÌ.~âÿø1\{ú÷àÜ«`¿¿À½Oc[Ÿ[îÛÕsæƒHå»HÇÞ4-õ¯Q  ²ÆKA¶]·âÐ^Ú¬CÈ\Ì&@CkÚPv®W ÏcT}]ÄÉaEJ?R¸tÄ…n匣Fåd¡`š¸æª'*½·,b¬bauЪÜ+Õ*½ßvªéL~k£‡E•ƒ&7OŒ’ÃPÊèò—_à»þ¿÷b¼;ó£¬}¶™¤î—…ö!K¬{¥Ñ¸jºyÈFŽyà SNZã2€×%¥5z(Š¢(Šr"0ˆƒó󘽂ñkx￱`¿ ï^/“É–a.breEQEQ”4jô¸³3—ÙYZáV z2±‹YÄ{],]yÚÛ Ãh%/r“o©Eq­“d™ƒ ¢ð• ‰Ü íË\æóó‹¸ÙÇ÷ ¾'m c¶æÏö E `qežú\&©"Àì­sÒdÖÉevæ^ùÛsÛ½›ßvô5òV÷˜]ròØçºk"õ„–bB ‚XÀl7íàAYÅö(¤¶Kj4YAA…¢ö*ö"<?¼„Ùø~ïg¿‰·>õ|çg Æ7-Œ¯ÚŸ¢VOXÎ<­TÐ7‘Oªº O½E8ÏÆ¹ñɺ”„sIm5¿Ïû;õ3­_©ÊR©<0OåvI .ëB„’º {ñ>Jja,%`¥ªýënÎW$!˜t¹zÔi3â~Ášûër1O;õ/q.öðœ~Ï å)Ù–Õ[D©ÿÏ÷î!á=ç^µ—EÞϲ­¬ÏcCØŠ½ ?¼7ÚÆà`òà‹2/áé´¶§ê8£½¦Rý? ¿ÿŒi²¾3\ÒMæ}'AE¤‡X¹(Æ`$ôÛÅÀ–b‹ií¶Ù¹{H°Áp+¨‰m>¹#ÆÈóˆý³t¯D*€UÀÞë ÷PQÎ jô¸ÌKMQ{ÃB§EÕyR_ ËS€  6hÄîåëI<uNèä–$Srl9·éÔùŽ¿C!˜ä„„Xž¨ä_Ä&'ÿD6šDž–¸Ò ýHHÊŠ’¸½×È›ˆõ"ýƹíñ¿M³Om‰$9ë{Pí½›Ñó—'{}÷]Ô-«‡®°Ín8Ä{þìí¸öÚl ÀDFÕ’¾!±fž¤bo›‘4†È“ñ\b•¥!—!Ûˆ"½ç­>Ùž3Íõz ¥«DºWÒv¡¾‰÷Ü&“¿¡šɰö^š¤pó]uþd3C±ð;²åá 5?? ­âúÚ’Ä;ÈN>F—=i‹Žz³ñøæ»¯à…'^Ç÷ý‹wš`+×ÿê‚ÂX-LÎÓÈ ;é÷L2.„/sÛ¥#¾[#-Hw{”¥ÛîÏ*£óTÊSÝkñŠS[ ý¼0 Sc²¾ý“ϯÜ_%k•Ó„=.ÿëWþ |èïŇü¾“.Ž¢(†*ÞÿôÇý“;€°qwŠ·~nl ö#¯(Êš"Ó­mÜxì6¾ñžçð×ÿÍû`§#X2 ?ùih“|õÿ!É2m"íi£}DÅŸÞø×øÓ¿™ŸtQe5z\þ‡wü=Œí¼kè‰ÁEŸM¨t¡$2@ËåN3/ÚQ++ vÑe 6U2âXŠô >Ø7rÖOQ/fÎ…R”®ÎÔqoWãÈRÄ.ô,…Íð4ò,¹·‰ÇýJ´ÔÊûßœtq¥…=”µÓ0ñ¨ÜÅÉÂÄ^ïKË*GÓtšPY Á™e'¾žbÃJÉi}¯}ËðQÃ…N$Ž‘¸'¤CƒâœGËL Zmœ”§€§hÚņ—œa8š¤sm¸„¾÷@U»Fµ>î7M_Çø¶Ãho+xvÔ!ƒ™¹Ø”•@0ð£ûppe—n¦d‚‘.ß› þ®0˜×§¨²Îk)g5z(Š¢(Ç„®ƒÛpƒM v¿ ¸×NºPiØ0óº >hUe]”Þp~(va ör Ux5Ä®†Ýþ8.݃ìƒÍ¥¡ƒ—éëÙ‰IEY/jô¸s Ælιë5+·QçÉ®íþ‘ïy‘N–jº ÜZ«ÁUÇîZIÅÞŒ“ªÜÁUYP›Y"«{Òåz ƒr;\—D&ÿžìÛÙσ,P¯2mÖ÷Ô‡’¨JÞ+KbJÜJÙÉÍòÝÑ¥{˜«žN¶ŒúŽpŒÔJr“¾JÉÍh˜¬Ó!Y¡pí¸î¶>lj>È•‰‰í6hpÓ­áxk1Ø¿ô"±ÚP·½Š¨ÚQ¤'k½‰Cè¤kÄ÷Ùµ~_«ŒQ‡ºˆjRrg©Ýe/+(xAGH®È\$ÏÕ¯Ò$½r=ìSÈÙÞjcÈÖ¡(DerÀœrµB3d…²fŸ‰XV~íŽh‘¶Ë¥ Órm{¶:†tªÎ3¯Õt8RJ‹Û!ñ]³=ÉD¥k ýcG£Iªh;íRT#¨SD S-º92¸ Opu›ÅÑ ~øN&“î¸hŽOÖûоp¥ET}æ j£Hr<ÔÇ%‹Õz¶Ñï0ctÛÞ %Ý® Õ;iØÀûýùócˆUyò…>JJ„»`R}ªŽZÇÈm”´=ÏìC\Ålãjvä†c È€f{€»S¹¬•§Ü h+Ê:’Á¦Ä6\JôKCHÉkiN¹±Bz7%Ãͼ B¸ˆ°]U]”Sˆ=.Æl•’d±4¬dô(Ú t„4  bVõ!Ä·ë_o&§} kR&0ϰч(Øs.iB¹BÇ‘ºì@”îÛî”̸\U¬6œ0xëQÀ‘åâê“'ïIÏ$~)ÃC®t«4±Ùõa‘ÉT/*×$ë ûlC›œ¹]Å$+4ãý[ò|-£Ç´œ]R×XÑD¢OîX6šM’¿Ý`œ<†—PP'o4j:š¶§óžGŸyÉ_Gå`“6zŸ‘rVæÆ´(ÀÑ{G‚a…ùTO»íBuïeu ú>sûøVy3 Já è˜VÛµSþ߀wþCܼþîÿÚgá§Ï£mä)õ D[‚>‰ä¼6†0ì™°çÝIY¥e@êR³X¸Ñì_»‚¯½go<|ÿÑï]¹3„™TÆAWïOûÎ\”÷2ÎÃ÷}’D»ô>¥žÁpT)"vÇÔBýéóVïoÚàTá-ëToÑðEFŠ¢(ÊÊ ò¯#Œö§°Ó)†·A³>/EQå$yõ¯<‚o=~÷½xæÎ‹>'Å\À·±uk€G^º‚û_ÛÂÆÝWaŠ[¥Ñ?òæáÊK°2ΕFœd®Û/?«(ç5z\PZ+µpÚ±)ŠÒKkUSô¢ˆÂ؃¼‡`¦E³ŠÊ`±íé®5íÕñ Úâsú–g¢dѪŸùá”J ÉRQ‹ŸAì……r´‰Ž!öõkOìß$¨\ÊÃ"‰¬fuïí[JV5uýŠ:ÃìñÈS_ÂÃÏ\¾ Fã¼Ïñ8±w Üñžu°Å}êVðr{hyðˆ^‚@éMõ•Âï‘ †S>ÏÞ0kE9¿¨Ñã"ÑlFƒú"Ä­BUQå‚c®¤·“aS`Ü ˜zwXº‹W.щкîi[ÿîDcUDir±À¤UQD:ùʾvUá)NÞ€ê~vÃÍ¢÷–m:4‚É ’‰ó¶³Ô9“ÈÍšö‡=È•ç"ø£sg4å9Šã¨éðÖ£¶Ê|"³—€Ù2tÈ\JSÑ“KçtR‡ƒÔÀ|»Eòduž¹¿:Œr¬øƒÈpѽÏå}¬CV @Ô tŒ½†?ø»âÇ~ûùz[7WÍ*Qõå4¡FEQeõøi˜<°¹CÀOÚ®¿Š¢(Ê)Â|À‚1:OéÉÁ®\$¬BYì\n fW.(º2ô¥ú\œ˜w5ûüØ?Öçª\LÔèq!ˆWIR À,@q¦òUy{¬nõBr÷”W:n¯ñ¹rU>ú\I×Y¢³9©Ùž$œ«ÉÐ~I%í ƒ›T( /Éà‰"zU±ÊmÈ0³ýr¥ô ¬†Áõ{“µîm:Aáœû°ø®ÝKûÓQoÉFJj+­,ÊÛ¥÷_4Ë훎ñò{žHVX]xQïû!”we†U†˜Ä‰~£û+c@0 ³ö»ðþnòT¢JÃu:·=‘¯mª×\'l\ùwH »I_£ñ u´¼)‡·¦]Wݰ'<¥vDÔ¡/ÆÏ¢6¿ëq"œ§g ?þ}ÜòÄ’ZË OeÕ ×ùw•„5n{+µB Êö>¯¾QOÈÎj½ú<Cb]csïnÔFŠù2yy|%©JÕûW n«{7jGÛã„rßÚ£¢ÿ‘Ç=û'“‰š2Q}é…Bxû0ì·æÊjp#‹blQŒ7FŽhÎog\CˆÆ{#aÐ¶Ç ó˜½†ÑÝË Ù.ÜiõœTeí¨Ñã"SZmÉOÑÈhç¹â+Š¢Í’ÏÈíZQÎ.íðbì05­é*¡¶©77¦#F±áagã Œ2oÒëã¢dáv(á«u;U{{¯5,®·gÄxÀÀ» âÆÉåj%NM|§(ʉ F‹Š»Ùú'•MQ”"n#"•v Ò=š»á!µ oó™Q´Ã)¤A`ËÝ»Ær/ƒùhšc¢Ð5w|ì• À‘azeÃÙ+xÇ¿}ÞßÑ%†eèN&Ë3Ù–mÌtäáë¨4~l6¦•hJcGúRQŸ·Þ²pY-Èl†f¯nûúBsïWâœ!EoHEk¿úÑñQ8_nžªuÂ|v­1nÞ è»úv‰ß^_ 1é伩Ÿíäž[ ”ä8è*´¬Lzù¨ëD«ªë¹ž¢¨ÑCQEQE9Wߘ)¦CÀξœë’Gíý±R¢÷E:>3d™Õe¥®¬&*Å„X,}m"#*¥HÈn–sîŒÍ ,y¯È‚vô~_(” ÊaÆ0ØN~·”½ÿ_þèã¸|kŒGŸuîïJ÷f3µ JÚ0>—;H×E.D…I‰¢RŠ$Y›¨`âe—¹?Ù-Qy@8&Y½–~Ïyšý;Œ¹$”išTDð<ÛWY%AhcÜ«`·š‰­ÞJ©u tõOÜ+†ëé‹ÒÏ„Ðy¼``(ÝæÇå•*È>õSBY VñàZÉÅ42µØ£†„ŠWŒf¸uõÖg0ž ÃgB^†SƒAAؘ´“œ²!¸aTÆX±Cì‚í\Èð}_ÿ‹èëkáøÖï“Âü¤”ECü"‰Õ¹1`ÿ¹B(bJÍo(Öñw؇Ž,i÷<Ùï`R¶5–ž?KaB;ê+²EËÔG¦Ü:uû‰Ú°6L†«PWÒ9þNz~RüV_YSßUÛ˜Ö+;¥ê-JjôP:$0M¼¤(Ê9áêë›Ø¾k`§“zSOd´­S”sy†ñTÿ‡?o€Çd<…uÖl`0§æ¢(Š¢œ/Ôè¡TVë*ðUóÌ+Šrö¹þT'‰³°B_­ ë4HQÎ&äaBY†ÆbT p¸1ÅlèAÖÃ;ƒìÌÀÖ1ôWE9Ÿ¨Ñã"`6Ã_‹¢q76;uè ÛMÀ C¶rv wr¥+ûB« ‚‹v7ô#WHç -m/ç 0kY±·{ „™Ê•×7n‡@Ûe¿xv åê"É×il¹rÚè{·ËF`N½aØl”ZFdØ0î°Ü§ùžØ‡Ð–j÷òøÙø> &t(…«(Š’Mk<`ÛÛ«÷9JôÙ“5ªKd2ì†É€m3¤õƒ0È ŒnP7 înïc²}€Ë7/cc2„xø©À›P"ò¼Efäf+ýùk#N¦ P*ÁXÙPNcš°°8ćá@\}Ö­Š’‹=”@Ëf?ÞÃr9¯þV‚Å‘í¨³nŽéÛ­ò…DbË–/‡T¹Lg±è$%´Ú÷¹ŠoÕäiÊ™Bò4ëä aq¿Ø$E9×t,3¸óà-L¾û3ÀŸ|/|ù~0UO\æ© &kDœê½{6X`¼|Ša&ðól¬óZÊÙC••Ác:Þ„ÛÃÛ øÁØJI«EQÎ =†ÜÐÔÃÌ ÏL¬(Ê© òÜp.sylv:Àß{ðŸc÷þ;ð†á3ë뤧1´žÕEQå˜QO3ÊÇ?þqܽ{þð‡Ø»òšè 𙼽Ý@±µ…¯~×/¼í%<ñôu¼ý/gîƒf‘‹wß:Ï´³½úÎ,–¤ö8i¯ÚÖ!6]ZYÝ£2ù´È³)3¶dØö·Ws¹)·Tv߸”ʳ»ž-Õþéí€|úž´U`ÒÚç3î ŠBötIá#<Ä1d g$ϵ½ö¬úçf{WG Cö§QZ‘ˆ†¢*µÂðâçµfÔöè &ìÅÛPT2ðÃÈûº¹§Ça-0Íg35­üLÑ¿“m×dÒÊ<²”Ï󲺉U…wÖù´‚ÒrHu×$UWF›‘”|–Av]ïëCSê# ÑßÂÕäYœ¿™Ü.©´ô)G¤ä¿ÿ4jÙ¸@[ñMxn¾|oMûºlLr·ÊàÖ\ºy ÿàËÿÞùÕëL-F‡lZlîY Š ÞbfÆ9ï #è}?¥g•8†¬¨n$©o,äýïG®–Ï­Õox pÂ[ä¶ÊŠõAüÙž%¶ÓTp=å’Ë›.”­ÃEæ.#ôk²rŒôœ¤óô(«˜kàÍ'pp߃ØzåÏÁþ R0l<™Âgc.'û¢ ¸›U®pòÅUe¨£þØ|¡ ¥ÊéCkàÙgŸÅ¯ÿú¯ã _ø6771™Lðó?ÿóøÈG>’}®¿ø‹¿À/ÿò/ãcû~õWuA£Ç°CìôÆÀ†qéæ%ÜÿêF3io…‘H“ÈJõ¾Cl˜;W³­5™‰%òRƒ‹JZ²ú.î¼È‰‰UüZ•4\ÛÐ!æX( ¬íHñÆ×HuŒ®Ÿ#ŽÖ»Ê<ñgI6v‰IWRÒÕ& apŸ7’ 6{¢¢$È”äÍGìµãû;Ÿ“²x&zïLmÐ`;Àlcò ;†SN^â¸ÿ6CÀw¶‰rÕß༺+·=^œ@÷ØmIbWvR^Ѐ¾(I븼û*Ÿ‡|®Üv7R'N¸úΕj{çäÚã2 ïy´ÿœa¿®‘'Vw†ŽV[ò‘‹ñ¡ç§2iéöÝM¼ûïè”­µÎ„d§`Ê\ä}ý—M<.É9&EßyŽ|âvÓƒ«øü|ö.Mð=ÿêh¶‹¥C'„k‹õm©óKíî Á©ß±äù³ ;dAR_kvðúãâÅ·ìã}¯]qQ†kÇϳÉïÁð@(V潨‘­³Í|ŒÙq²Îk)g5z3Ÿýìgñýßÿýøð‡?Œ?û³?Ãp8ÄŸþéŸâGôGñéO¿ýÛ¿½Ðynݺ…ßú­ß«¯¾ŠO~ò“ÇV^ãgNñÎO[ Š1ìá Ëuà+†+HÇÃ$ö¢PE©`C Ïaõ×!xu,3QåÌÏ}†Sò„aa0y\áañ„ÑÄb850eó`Îi.&‹Ë·6ðê·Ý ‹J}&ÊÉÁxî¸öÂàn®Ô‹LQ.2jô8Fnß¾øÃ øßø ‡aâþä“Oâññk¿ökxòÉ'ñ³?û³GžëÊ•+ø¥_ú%À·¾õ-|ô£]¼ 4hØéÜú“#sX9-;Fòh>"·Ò8ßÇÜê\ÊûA !Ó:×lc«¹œ›Õã£Lê~Ø$&ôÓ¦ŒÞ÷xn¬._…üXTYç‰lsØ~r¼åèÒZ‹·ç¹_*ÊÚéó®H´%\)M¡ [‰ÃUfÍ*måÒçÄ\¢¶‰^MVe ¢¾™[a—IU±–Gè¨Ý·Wý?»0N(±3•ÞvF _BƒñÆ • /á˜Aáafe»aÌrž'Mí!ƒh à@Ó×ñøgîàñÏ”›üÁñ“”|øtølaÁ~·ê‡)Š’ƒú#¿ù›¿‰^x?ù“?‰K—.µ¾û¹Ÿû9À¯üʯ`:=:QQcN%D˜ IDAT3cå$×ðÝÆçÎÙùc3j}®'%ÕªÂ\臉þ,¼„¿Á³èo´«GÍ>fPŸ¿¾Î½üÖL˜‹0`¨þàÃO_4­}Ömþâçƒ`I‡Ñ(Ê)&•ãÜ^·OÆÔŸ«ÉJ•„°ŠõŸkÇb¸3ÉZ©b•¢(I¢wÙ¹(ÿÚŸSûƒÈOË;Ê?3cÏ0µAƒBË”0:4XlíŒ÷©ü.œ>KC»AÞÏå 9õtïÊ?v€ßÜíæÿ| ^§ºî×F©3Ü1­ÿOQÎXk~¶øßùÀüÀÌ}wýúu<ñÄxñÅñGôGk.ÙâôNÖÈáΦ›Áâ6Ƶä´”OQ”ÓClð8“+µŠ¢ÜÃÉ ƒÂcPp¶ÂxŸpé°uÇcëŽÃÖ.ccß—^!y©q•éùi?âÉôœ1I9=ðAóW­˜=δñCQNÞrL¼öÚkxæ™gïz×»’û¼ç=ïÁsÏ=‡}ìcøñÿñã/Ôœ’BÜ€6®ÜU'Oݬö±:I”d4VL Þhvg<2†#xŽíßPt@ãžîíl üÀàpËâîë;³Ø˜Xlì›Y}æzR+9höÊFOÂÐ8‘¡ï¸àÖû}=Q¥…Fr¹¹¬ü•Kk”ЪåÕшqâDiEÛÜœ+^2öþt™ü^Z¹BP­ Èùb8¥9™áäÄ€Òà.¡ÜÐS_"ՓÈ* é²&UU*¤{"ª õ“Æéäj•‚@ò XÕ¡)cð «’”ŽáMÙu•íEEÕ΄cÊßn-Œsõ¿ãIL¥îþ%Â%ÓiÿÒ‰”Ù§3ãKªGb]ç̇ÂwÂ1>½¿ó4; gn]gžf§M‘ê. õ0ÔÝL%(á\RbYnµ‹!µW2BbPÈmœœ(1 i_#åùH=jZb‚Êì²ÌRh§¤*VtÊåê}¸ngl;Œ¶Tv#²0.øM7®;¼ñà]¼å™«ÀhÂ03†<:†¾Qk1³Yx÷£qPK¶º%a-Tv_MVsžG¦zK7ÑgÜÏ‹*FékH*mòõ]+\é^ë¨ÜwIíq¨0Âû!쿪À,Ý÷ž$Ø-Õš*|«ëÀ@hŸ—çÕü‚‘KjG¥‹ç¶1˜ZêåQ£Ç1ñ¥/}©þüØc%÷yôÑG_üâ·0e_=Ÿ1=tðѤ ‘ƒÉ¶ro¸Qó9žX÷(¶7qãzØví5Â`rЊ»ïÂdà­… 0ì]f¼öÈ.Þ|è.íãÛŸy O|ùìÌ`8) -©™Qz&ñ¬ú D¢áH4ˆJÆYuD”nUŒdchóólÝn\3yd 3mdމ¯À›¶2>Áx`P»ÓÚ‹#ÆN ˜Y*Wˆ¹ÉÜöðYZ‘ÂFúTÚÒ§’p„çÄ@®ÁE|æ´™,/÷È “øÌ%úŒoéI¶¤*ç…w–ÂýMKï2¤ýû .ÙhD@{{tÿ¥Ðäʃgþ‹CW/‚q#Ùwäç .¨¶­;äDÃ[”ÔèqL¼ùæ›õçn>ŠË—/^}õÕc-Ëd¶×ú÷ÀŒVúàã¸×j‚2¦þÍG,þÝúiì¼yÿÁ§ÞŽWF¹}†ÚpŒ÷ ×^߯ õ¼ñ¶çñà a²\TGû<íxlÈêLø+éߨ#gÎsGQEQÎ!ä=…Ãý7¸ïí:Iéy WQ”ÓÂ̘E‹i‡.aQ”FÇÄþ~³ 7¥-õÁsoo/ùýªøûþc­ÿÈõÿ?zýçÚ;E“çjrLÑ*èÕ¨ÊðT_ÊVö¸ÿ¥þãñÁ2;zdV^«s™` a41¸ì-Þñ—oÃäé·àúó;±·ÎµÎã¤>V‚AÛˆÑÚ§•ݾ½JP­3"ÃGä¾ÏdÚî»ÒJckóíŃ¢,Im¼‹<–ªD¥á3ÕŸýÀ`6ŠÃ[Âÿ©¥ÉQViá&éÙ¶®ÄÈŠ¢ô1Ãì@qø†oúG3-0@xßýÀ€<ÃÌ\7$BxÉÍä~Ps_(J/ÿïKÅÇ^ü¿NºŠÒ‹=މÍÍ&&o:& •jËÖVžkz.ÿÓ_û=ŒÛõ¿¦[–¶Ñ¡vñŒ q§OìÚáÕ>µá£bãî<úåòü¦””ì®´Äá4ð0Æ€™å@Åà±çG &sˆ»u3wx¾$4¬C‰BèÏFûûH•¢ž–ÿ·ÓH°Ç,?«)ÄQ,{d0!öm×ÊÙ++øqŠÒ!ªcn…· êÜl¨iqƒbLÑ1ÍiÝ éêLdô03W·~Ħ­¦}¨Šr¨]ùm;—A”'ÁÎ`gLÚýfKò¾3ÆYÈè¡FPE™ã‡ý/ñýüíúß·‹7ð¿ð oQNjô8&yä‘úóîî.®]»6·ÏînÈ!ñðÃkYƃmŒØúU ªUÓN'cJ;Œ›})9é-̬‘Ÿ¬Œ- “ÁÞý;˜l.½9Ãtl1vÞ´°Ó8w‡Þ/EQåÀ.ä×Ò±‚¢;3j-¨NÜ9§å˜Q£Ç1ñÎw¾DfÆË/¿œ4z¼üòË€w¿ûÝë.^IåÿÝ“H*R6@œ .v ­>;ƒy¯‚Ö> bo‚êÔbS‡ÍÔ ½+5Õ Ÿ6î­±!ÇBöèe;%ÊÍD©äQV̾/“NjŃmL7¯„{P|šË j¯Žž8Äd³À;omãÖýŒ/~ð9¼ó/Ç·5ÊóQæYrôYS$N„À z–± &¨4(À¾/‹yâwKuc9âT]>Ïž@+&(4Š/+ÊÊß»RÚñ(«¼=jÃiµ›¡úÿÞØÜ€ZÞ³¨w³åkC¾î»º#Rriy<±¤øä²á*÷³›Y*œJÉDYNfº2¤ö‡†ù‰eÅä‘i,†•U–HȘ.’JÜ£”&ÞsAeƒ¸¨Õ[º¿‡#¯8IyðÚ˜‚ÉÊio¹é‚ë|"Ó€Oo“¢éïh9IdžšŽü‡<éSæHoçä¹ä¤áò¹2UAz’pJŠ$"bRTa÷(YîܵûÞA‰Ô»I€¤ (&ÛïIœ¾W«ìŸ¤ÄÇR~õˆRNjô8&®]»†÷¾÷½øÜç>‡§žz*iب^~ðpÝÅk'Á¬þÿ¿¤{ðÓù—ã•…;£ø\q>‘Ƚ´•„3ºFKmÄOËÚ5$}YÇåNQÊðö‰kô ¢­ õÊ‚|ýèG[j.ð»¿û»ðÞã~áꤧÎ9üôOÿ4ž|òI¼ôÒKâyÂj†d_N¬Ü¤þÝróNü-sÝ£ÊÑÊ,íÔ{m‡U†¯œµ¡#¬’›Ù £ ð]Ÿú ŠÿŸ½7¶¬ªîø¿kïsïúõ@35³ ¢üPÅ!"ÆB‰FM¢ÆrÄ’ƒ1£&BEÔ`Š*gˆÓϪôc¢ &‘6(* ŠŠÒ -sC7M¿éÞ³÷úýq¦}îÛëö;o¸oZŸª[ï¾3í}ϰ÷>k¯µ¾~F^”ÐÍ¿—,œeáŒL¦ü(ÊrMaüÈŒ©Í Ývfôð¦ø þR12ÖYÁ퉢¬:²™óÆPµqƒz=†X?èó¯(вÐð–EdóæÍøÒ—¾„sÏ=çŸ>>ýéO£Õjá†nÀe—]†¿øÅ¸è¢‹Êío¼ñFüã?þ#à«_ý*ÞùÎwÎ8æÃ?Œë®»°uëV¤iŠ$ÙËe¬uôfæ6È]Aky¾ ßÍCI"øÊµòäaá1£Ä¶a ´½kÞÝ„….¨=2º+-¦°p¤6`†Á­ H¦vaÿ_ߘv¦`a‡3·H› ;œÔŒÅ I«kk£‡ö3žAÂ4xh4)3 Q veJB1\ÂH-£3ìÐm98ëá¬G«kÑî$p6»‹ >i—žS¦[É_Ò\ ºŠ¢,<ìªþ½–_ÌB(\<ô„Ì*7ýÞñƒ$S(¡©Ó¤¢4`Àê-ÐðEF§l™³Î: ×_=ÆÇÇñŒg<gœqÞúÖ·â’K.Á7¾ñ ØàEò¸ãŽÃÉ'ŸŒC9/zÑ‹jÇqÎáéO:Ž8âˆÒ äÚk¯ÅÁŒsÏ=w ¿IYdÈ‚ívo9nhC0C•ÏLI¹4A½²)î{—x<ºi·Ÿp'¶p&×M£Ûrð”y~8›y|(Š¢(Š¢(k õô'žx"®¾úê½n7::Šþð‡ÑuÖZüèG?Zèª)ËÜ ñ衇ã?þàf<÷_žŠ~y7à; ªTa¤Yo5|(k&Æä† œò´¯ãŽ~+’®E«kÑê!¨ÍCQEQeí¡F5§wƒy´¾PȤÍcOÆä¦}C{Æ‘ì¾5Xº{ß§‰‹HȘ-bÔ+P܈ª'Ô꺫fÿ‹n¨‚¢„sÆ7¢>š}»Ÿt™²Ùg%TõÚøë{ðÊKJ~ Ø Yy=!AÝáª^¡±£3\¹÷Mø2c«K0yfkò€ ö‘’<_­KÙOà r›dzº °¦jn² ‰"53e›™; ˜ª €¦JL-A¥EöŽ„ªKó0+#f¡÷°›„5qI`¢¡ìùœ±Âˆ÷»\§øsÎì@µç6¿žf œÄ ÈÊɺҸçZÃ¥<³Ol-¬Ëä2´65X¿{ëvŽáÿv> 6ŒÃ[‡‰uS8ð¾}0ì vÚ 5*Dpþ{mÇWçÊMVíߎܣ̰ 9èýcÑåÞOÀKÊì§÷¾QmûnãûÝØÑ½o–æÏ“Iâò×2Ãå]º£YÔ‚±ë㫚:ÈJmO?ÙOa¹w;…c-œµ[Rˆ‘ú5æNô¾ÊÔ[„cIêM™”v³kžØýãÇbZâØ@úí^ê»ÈÆï“^å™b1”U(PˆcŒu;¦êüï‚ÜT¾¼^V>*e:0Z6»GÄ>5®<"÷Ù¢Ò('+«ìÕÕ{ªï5…˜^Õ”B =ãXáY“ž)æxl/ÑH~ŸV÷ûñü9oÖ.JcQP+zÿYc$•Á3Ølʘ…ônxÍÈÖÔØØ´óM³þ‰˜@ odYÊÊCÊ* LBh“ªUd £Fo’GòqcH4¤¢,E‚RΖtçØÎ©HR‹öt6µ¥lžzB)вf™M¾5eÎØÖãñè‘§aýÀLüi÷×z®e¡Fe•`{¾kGÓ—Ìt‹1ЗDeåàÆÔHÎz¶m €LÑÅxBâ G5ï&EQEY¨ÑC©a&îÀºÉ{òÿú)„F†%60Í\õÍ@m¤c‡#™x<}ÛÒÖk!à»·†N{¶k1²;s)dC˜ØP=ÊÒ‹]gÈ—‰‹0—X’Çp»V×ÀS¶MÒ©\8‡&*iÜ6“Vž6åÞê³Æ ^Øšôµp“öl_¨©˜´S†ºXc@>ÛÎ%…ºTöIR oÝ–Ãúdžó0.B«kО"´:²Ïä}š–§(в,ÛDÓ*ÛAo‡ÊÅl“2ü/kߪÐ, ÛÚ¼í3>…éØªMv˱oÂV U»â!Ô2 Í ÃXËÏæ.xüxêwŽün°ŸÌƦlçö·üús²@q È–!-Ùÿ¦þWQ–jôPêðdÏËiØY,w X Àdûj|yáz|ªI³ÿ³ñ]Âð”Ш!©XôÇå!Îzo‰ÛÕŽQÈã°•ÕÆLiȹÜÉ{¹i2OŽV‡ÀÆ¢;Ùù,wMÒÉîCã¤C°©&úUe…ô¡3˜›™1S7€øjygÃ0È3’é.ÚéDfäõËÑà!CËíeºè›ð<Lÿ v^ÏÜP°Ý=bc»ØµáKÖjN¥jôPV>ì*—²H&„„oÊÞñ†KH[·t;ž²õØ`«e6ðPÖ$IÇÁxFkŠ0ç^.©Î±³ÚˆÉÑn™{|ƒ-_ É'°i6“Á¸¬Y!ö€ ¼>"w2ï¢Ø:Ä3é3:ÂŒ‚pÞ¥PŠU„¬0„¥ó¶¡6Pd”³×‡Ùö{Ÿ¹š7SO¨KþÕt; _Üc à½`0<‘mYÜ“@f)ž“¤S©ž˜îdéEBnªšõä©*Û¿ŸŒfìgîöic”•¬TÔTAITíê;3W­Zðþ*-ñ>µÙö•c}öSð~¢O93‘úÁ…$®.RT Ö;¤2õ¦¬íŠœ{î÷•|8èë½*½;Òö|bóm¨¦hU@žaRòl 6î`ïÑšìda vl‡`œ¤BÓŽöÝ¢2Þî7©Ÿ¯«²´Ë¿‡çÖeãÉüZ‹Ïl¯’KPߘŠÃÉcÑ^åC*þôÞS‘P a/Âö™BËÌsl̺™õ*%(IJE Vÿ1CÎbZèŽl„q)l'S¯F/keÕ F5³ï1Ĥ,]ýe£FÓÆÌ £+„ä2b†ŽlEØ™Ò`Üéc¨hö;d#ÆΓÔ!Pü\ÕõëEùŒzK,ô„ × ž wab¸ÄÃÙzY›Ú—xPžã#µŒbŒÅ†à‚ñ ŸØª^dªY$±ãå†g‹&ª-Qê3¨Y8ùËЀêƒ7‡\Z1ö,ËjëÙ—»›ÒEÞ—ÆŒâ¯ñ™GGÒ©ö7é̸ölŸ`Ðé;¨|ÁÃgÞÅeÙ-¨T¨²¼è'=ÞŒ¹´oMs  =îwæ¸ò¿¹!?›ék™\¼Îý^âúä&Š?æäx_Ëéaªœ¹t7‚Ïÿ†}–ç¨h äÃõoCÕ»(ò5ŒNhÍõ>û£by;ðHÉó{P&•ž ÉrhÔêä±·çBºG v†Üo¹N2ø„Û×öíg@Œ®}®G|$û»—ò£…ÛþãƒÜàáív´7œq^ü…¡`_ûKl²>{@ ²,eå¡FEYåô7œõ5Og}&÷™B˜¸–ð´ðîðĀ͖»ÄƒsP—6ü܆tÊ¥TjòPEQ”fd“ …¤0|dë¼Éúdã3ãˆI6õ°«!Ÿ&…= uëñ«“R<õ;à{ÕË`€0ØØuð‡,uueV¨ÑCY!È¡'eˆËªU)~{<숼G+øéá*d%mgÖöл#4z”á,Ö#M\¹¬:6Á:b‚¡Ê¨Ñråú¡é6¼)XE³b» ¸˜I/fd”5„Êðàù% NFsµöá£'Yo‰ïÖØ•‰M½/ÕX¼«º³Ì­»Úßv«Ñ¾M«ÙNrAx w«$ÎÜAèé¡(в„ÉKg$2Efðè¶3ƒGšdžžÞ0lš%t¶)`Ëð—lFž<ø´<ÆòŸŸÙÛéGÐp÷ÁSþ{ÈíÈC§ lá Êf^'«e\šÿ>bfãSìóÛ‡ðz\ª(+5z(«ˆÞŽqå'èâ —A/ÅË[[Ë_æè“’ºÄ—^…a××cô O¥J†aFn+—õ—pöâ’J¾–ɨzËZ† ļ{“”a*µ“Þýo2¼ÅwË,¥õÐ7ܣƆÁ`€_3t„Ê=†Žb TE=}h¡Ìæñ`tÛ…''cjÔÁ¦„áI €`<àùBÑŠ£”…Û ;±ç|¢ ø¥O\†SeC.7cùŠ'Wû!d¹fˆ{”Ù—•<«¨¢Yc•>¨ÑCQÖ…Á£gabLtà­‡3ÞfF&qÖBž`óÒÔè’N íé’Ôæ†Žb&¥€S”Ì0:cc0©ƒq-ß­{{(Š¢(}!Ï Ï`CHÀ%ŒÇ6tð؆IìÜöì»OýÞq0>ñY¸ËêÀ>ÌM²'óòÕþcø,W‡s0Á¤D˜lWQ–3jôXì=¡S÷É`ÞBRvú¸ÊzÜÃzIYÒ;},éÍÜYpÑc)ñj¿c u"#dÌæŽ ªÊL ÌXJdK÷TòI§êt¬« !…‡Gâ’Rë2ƒ‡És{_3zø¼L&FÚîÂ8ƒ4q°ÎÀ0!Íó|Ø”j³I®Õ*­ýaUˆƒóé2cÕi~«±ûDO•÷»…û¡¹;kS…†…¦¸ßç-(%]ƒVD±g“`Ås"y<!ú|0:=eçÛ¸q =¶»œ%"—+¥ôSrAÊk„½¤Ã£ßœ©B$ÆúwTû†Þi¨T¸$û=Á³†ÍM ¿Ï‰ª9†š©S0M’Ç‹´ÏùIhñ‡ÍŸA#žw î·}äÙ!YqA:ŒT†ôl‹‰påë*õQb Ø½ÈRPƒG\‰Eîg}4¡g¿îƪGÔBÓq‰x®¸õr nIÚ§y÷èa¼þ ‚(þü‡m— êíZ­,‰iÐÊ’'Üÿ¸ûaO¹Mì‹G~qŒCÑÖÛ$÷ ñMfÞ#ÏÅ'ÔsÏèQš&9ÍÚ…|¼Díh=€ $ L¸]Õ…¨©º‘ø¬ÅÇ¢$$/% š9»ò¥s%)D€L\½ERuéë…»—$ã€Ï¼$ OÌØvj„R–!jôX0w2)ÚY@蓹¹ñ‹®3£>ýŒ0ÒÀ_2†t…ÎÔõɬ. öX’²í3xk¬#t˜ÜŠf §Ú F°¯ \öÉÀJ­Ž…7ÙÌñ˜*äÅçž>ÏãQzx—¸,&)Ü ä ˆãM™ÐÔ%®Tw1Îäò¶”»ÐO <šãv†SœufW†4YT²mT»O‰;AâÜ=¥’yfÁl^NÃmäRcšø‚&Câ+¬,!(")ð¸¨Ô+ùGa'¥òƒßÁSñÏ÷ðúy{ Æ7dwÀðac÷±jÛð%Ë?-šýθ1MT‚r}ŽÍ_ä»A7 » Õ”f½Ïž†ýDS•Ú‹º@QRx>¨=Q³g1ÕøFU„öR§èùmúÂÓg9³ nˆ}mÓß‘Všº¤3›´â†.I¾Túí¢(#úÛ=ò1UÙØlÒj\b€²î¶; 6J Lþc †÷ŒàÁé ð.Aš8Le¡.€ÉÿfÑÞ{âs`ÁQE›Üدq|±¨\'Ý‹íàE>WlЫþW“° &Pj’³µÐE öLÆÊgQÅP¼ÛñûJ2ØPø›zË&ù„ ÃBÍf0‚¾ßs^üŽ^CGíÿ¼&+Ÿ+ œ@ƒ,KYy¨ÑCQVÞdýr‘ЬTe ¼<²d¦•Á#m¥HÛiåñ‘Iˆ ÆgaR[z‘°áܸÂe|±7™P*›<ˆ1š RQEQò™*K«“åîðdqÈ¢=ùl¤­›v®Ãèxë¨Hƒ¤(вfQ£‡¢¬PMÞ†! IDATH˜%4¾˜QÊz+gvâšA$÷øh9xëòð–<œ† ÅlE¹WÞ#•q…òü”7`&5¥K±œ»aí&=„ÇÉÊ$0²EgŸ*ZS“Û•Ívµ§\=I©¢(ÊJ¤'QdáD¹ry›2¼!XGžL°ÿ›`SƒáÉÌàa|–Ì´0|Ï€_îV„fÿ˜áíPó’ì;»¢}l'WŒ B8gŒE–û}¢(³Gв¬ :x$) 0Æ#—­”W\OàGi¸ÈÃ]Øä!/íi»[†¯´:­ÒÛ…*L"ì[x{¤IQŸº’‹wIŽ“¹§çÊ3ä{^ø›†V¬&ò|5€dP rhôˆ@’=¿Åæñû‚å:XSe…„‘›¬ò(¸Æ ƒ,ÌÅ›&š¶°)FäÉL‹ü[ƯœÌ¦™Š]§ö|Ã>!4 –cB ÕvËߪŒv'ˆ«¡ã dYÊÊc…kV)ŠÃìå}‰áŒa.¹—‡·Y¨Ë;Oý0ºÃxërÃHmçò«7=ÆS¯)dõXVEQ¥ŧ3LêaS¡ á‰,§Q{ hu€$Í”[Vz‹¢(ÊÜPO5ü(#n)í—°³_"º¸âƒœL*GJ|Æ¢Œ%Z‚0aé\Ô^¤óXSø(]÷~L*ÜYM–Õ<ˆÊÄR&Wgé-–˜ð¡›þ 65@¡âj­'Ðx‚7 ë*HáF;5Jh‹¤“»ã–ºíÕõ¯…ºÌòœÚÑßÏÔñÌdpL³W/ZlfÆ"&îQ1i¯m”È0ËÆ"<7ñ1ù±ÿ^j£±½½×ë£7ÁZË„dp¢”@ã£ÊÚD|²Œ¥nÇVγ '¯Î.´‡]ì~î…L JJ„ï#‰}eøð™áƒŠI”{~fãKBòQ¢±”ï­¦÷œ‹z¹H j–£'<:¦ž Þ·öñ®‰yŠ4ìge¨Ñc àÝ£ð˜®/ì#¡%f$ Z'¬‘ågEyX1ku£GSƒ4ð^’æ0Ðå …1#®3ÏhƒLžK.íl— ( ã‚Á¬Ïò‡O@ CH}6²ÎÀ8&yS*³Ï–'V¾œ`S r3³oF“O * -6î>jÖaËö¬™1éDyÈåªìêã,Œ d†A˜™‘œÐ†eßç|$cþò¢ m!jÅæ}d&¥{טuñ{±ï¿á¹¢¶lÜ2ÊÏF!*Ø&T6¢Úvð15vÄ‹ðq•&IYI̘ϽÙì•eK#_ãC‰j, ]î¥ûj@ˆ ‹­"4 HP­ Ž^wF7>¹ÒW¦¡:;Ùð!•á÷ûÇM€÷0ù §·ù8Á\R•o<ƒ ÁxF’«¼™ÔÕe¼ûæÙеíò>’´¹¨T">áØÀ‚C;ÔØj,Ê6¸þdÁd@¾›ýnßAiúNq(ÄÔÎA²ØûÄÆÛìARXIW¨c±/êÄϯhÌ@5~²ñu,§GX^¿ö*v‹~™It/ªÞ¢ôAвÊ0ÁÌNqåía†M-ØøL™ÅøÒøaS 0Á8‚q¶4‚g@ÞTž#‘¦­9úÖuùŒ׌2J™ÑÒ¸Áù C½ EQ”¹’¶ ¦F³°òR} œØ|¦GŒ=<ŽÖä£ g7~ÏŽÜ00Ã{DQ–ï=n¹åÜtÓM¸óÎ;q×]wa÷î݇÷ëÖ­ÃØØ=ôP<îqÃñǧ?ýéZêª5z(Ê*¤2|`êá(…ë+ù"ÄÅdF—:ÈØpµ³™±#·Øgj^ÆW^…ÁÃx.åô²ãèË|F5ãchDC jgÞOððnלfŸ×e"SavTQeS„·TÿçIà 5“y|–ëƒð–•so’ó¹3¾)Á¿ìfœyÍS°ùî,;›k?k{¾¯PƒG?4M¦¾¢xàpÍ5×àšk®Áu×]‡‰‰‰r³üÌSžk/I<íiOÃ9眃sÏ=ÇsÌ¢×y©P£Çàc¿ú óiu‹LñTK jSd:{)5ig–î´«pàFÔ*ÆnF’·ñ tÇÆ0úÀO15þ­¥ªæò 7wGm]6H­‹Ívàºvî7EQVuIÖªŸ'7Uµ~”’ðIg65°)pçvã¨_m@ÒÉB[Z™w±¯÷ÇËžžö»¡ífì‘.^ôå'Ã' 74 ãSPw7椰’lÈëPä²°:+ÎP@Swÿ„ýn{^ã³ëî¿ßàëHsu "‡Ü dYKÍ 7Ü€¿ÿû¿Ç×¾ö5¤i fƺuëpòÉ'ãÈ#ÄÁŒ-[¶`ddÃÃà "Œcrr»víÂ]wÝ…»îº ¿þõ¯qýõ×ãúë¯Ç_ýÕ_áÌ3ÏÄŸýÙŸá%/yÉRÿÄGk€ó9Ã6ߪ¬^L>«c˜Ï0L`O°©'†ÉÃ\¼10Æä¡. Ï>óAž»ÃËÖ'©ÍþÏ Ö™š,^/LTÎ@)=ä^ {Ø„‰1ÂÈ=÷è,‹¢(Š2glêaSã³É (©­UÚH¦¸V vz*ÈgÑüåþ»ŽzÏü7‹djG<‰çæô-/Ãé[^†]ÓáÒ_µÔÕY•Üÿýøó?ÿs|õ«_E’$8묳pÖYgáùÏ>Ž9æ˜Òƒ£ ¿ùÍoðýïÿñÿk®¹/}éKqÆgàãÿ8Ž?þøEøKƒ=eB>è„OðDYR3&Àõ0ÞÀú,¼Å³¯Íg¹;²Ð–ÂàQ;Œ§òCAˆKo.%sÞíÄÆß|üN¤îLŇ¡†EQ¥1&õhu²‰†#5ŠÆn«Û‡!Ó‚qÓ€ïb®JDwû¯Û˜‰¬Ÿ& °ö×Ê`øÎw¾ƒW½êUÁßýÝßáu¯{öÛo¿y÷è£ÆÑG×½îuسgþéŸþ —_~9žö´§áŠ+®Àyç·µ_zÔè¡Ô²Ž÷Ë3àüŽf…°o,ÙíÞݬŒ9äEp݆¿Ã*s Ë4—µ¥R‰³ Üuªí“@œ§=Ze0ï¶Û¹ñ#3HØ„à»Ý4 uiwLwÑu‰õðÆ—¡-Ê\Æ´;I™Ïcýî*éÑð¤- 6­(^†àjìL³Äcì§Á¾a”†nÎd†AfTZ]l’ñåfT[6ÝÒ¥/RúÀÂ5”^!½ H&˳÷Sÿ‘îëfýšK£GíãÓ†Ûó”¬<"œ,²ñkÞ§,”\.óT´ZìûüI¹.Q†¯[I 5T ­ì0Ødmx&]›Õ½=îÑšÈïcàm®žÖí ™| :VM)-®"yBˆm>lÕwQ¯òKC¯ ³œ„¿=¸÷óóÀdàmÕÚÎîò{qn¢ßÉ‚“ÑìœEÚDê£z²qû¯°q{QÇvõa—íÔUS¸?¿ìÀÂy÷.>Íebmâd|9 ÞW’Š™<¦ U×ÂÝ6ŒqJò¾_Ã[ŽO|â¸è¢‹ðž÷¼\p†‡ǃll oxÃðú׿W^y%Þÿþ÷ã–[nÁÇ?þñE)ohÆüá/i½-e•A>xQô ÊslÏrodlx†M ¡ÓW!c&'5¹ê‹ñÈCcÂòû)+•Ò¥8´ÖÂÆ><…ƒr½±EQ¢6Uî8!“”3iÑèy^¸œ@1ð}TiŠóÆÊO-Œ3 •"F !&ƒï¼j†oKÿÀ”}Å*Îé¡Ì5z(ʃ|õ1†à}–€”¼Àç³™‡JOÄ‘8EQEY1ø$3Žø„`R ï˜T¥f¦Ê³gzÝÌPäÙÊ¢b­…sê%«FµU¡²[]?¥e~xžŽ.'ï£çÍú¨rˆî¢¶Seå&öÁ¬•.ž­¶K²ïEÈ xSIÝf긭`Œ?Bµo¨œÜ!o@ù,>w ê™çGòm:çÉËõÙ(½7úûb´‚Û»&—ìª4ÕQ5£šFt…ï».†˜_œi5`¡Ý¥å˜€O’TŽö5²*Š«Ðdç°™š‹Ê‹?Ø3’B ûø=ç§Dõe…ÐG½-v Gsúä@Ô^8+Ÿ/×jÃÛìù0©)Ûà,”pæ8# løÐ(Ú…Çé s¡V™ÛÁ·Öçª&4W ‘ñ=Joydký”C‚å¡Q#±>±@§ oá¼­"¹†}ú¸`|ŽÍzÏo¹Ϲ ÂÈj,.S®›Ë5 Ý µëê?ìâÊ1̾¼æYØKV"  ºG©ôÜlƒÛûdÛç¹S^òùC±kê>|gN¿CY Xp®-Ôè±&è•ë3•³XêÇUB¿—ap0sÔk@8 ×*I&õ¥ÑÃ.¡T„º¥ñ£¬UpÉ“N¼Ñä>ïdEy&£žAl5x´#™¤{ nʤÞùí,œ«%F’]Ø—YéE ØeëgUéÓ4)ŸM Ýáî—åq‰Ã†í)†MsùYÏrV}²³tÇnzÍÿ‘ÚWÙóVä\êü%s¸ä¾¥Ïunt|ù¹!i¤ß™¬¬«¬æ2A—a¶rP lÕÚKŽ„vûzn¤ß™[{{>¨µ£=y<ØŽ€íÒ¡Ñ2IÏy¿:ÅÖ…Ò°¬dxöݲ^Ä “æ/ïÞW!?d£`vV%ƒd̰«kd]¯ŒoMÒ} sh볨٠ŒÕÎèÏU²vy@qëåšCвÆ1žQø,Éœrë!t‘TV6éð(&6üúI÷¬78²»ÍcHQEY]¤#›ÑÆÔX £»,Ú-u•–f~hß̈㻠馞0ó%÷0QÖ$×_=¶nÝŠcŽ9/yÉKwÞy'®¿þzvØa8í´Ó–¸†‹ƒ=eµ*^øn9©ŸL'ðI’/gø¤ {é— ¬ ˜ŸQœ°¯I}Æàk3Ålò¹;,Ù<ÜÅÕ²¢++Ûí`xbÞ·O©þ³ù~Öd®U݇!ÞT]’aWÝ'Ì^ÎÚDQeõà’‘Rv•¼‡íf $Ô/éó Úʰm6mLn\‡Gn;þ·ØrϾxÒ‡aâÑ{9¦0 ü¦šW‹ B„|…·F2eÊñ‡ñiósRóè­ö1•“½P佨t¾ùÇwàÅ_< C»ÈëDÏЇF@Ôàÿh‚K@ymû*ß—Q…iΔ™xïñÁ~W^y%~øa<ëYÏÂ'?ùIuÔQ­Ç'?ùI|èCÂÉ'ŸŒ¯}ík¸ì²Ëðõ¯{ÜãÀÌ8úè£Wmþ5z(Ê*£¨fKWÐv©òÂÆÀù>êÇâè÷^¤}C%™Òƒ£·S½;òuõü^阴ƒÖ”áÛÚ°©G2ÕEk2›ÍbªäÃÐ'ê¹5Šª:f*в&©åÂJÊö’¼¯ú×0dc`ÄÃtØ´¶ &Öu056‰ñõSðÉðÌmçE¨H,®'ªú7]õ%=çI ¯\ÈÐÊšrYnœbt÷:´&ÇAén Ôàà±Ç‰íGwqüÿ&ÏWäï˜û„±/óªaÖÞRqÙe—aóæÍøÁ~€íÛ·ãþèðÚ×¾[·nh=¶nÝŠ_þò—Ξ˛nº ïxÇ;ð‘|£££«:ÿ‡=EÐߨ±x…ö„³pWgñW fêAŒ>’_Ï<Ö—Øg9[EQ”Õ;›Â>Û·cÓ=-<áÆ- —‚8ž`|-Ó¿/ú‚ÒíOÔàß~õ­Øyø=8æGÏ@òèÀïxÖ“““8öØcñò—¿pàâío;Þýîw¼.O}êSKƒGñÿç>÷9|øÃÆYgµªó¨Ñc-ÀÝšÕ¸wJµ\ÑÍMJç}ÃxMv“¢úˆ‚ÉÞËiVs³Lúä3·ûFe);§fõþ1"YÝ)s…ŒÖËmêXy}Øü娻ú¤ oãMAmÞûèw‰pF…\Kiñ}–`­Fÿc3\ôÚf™IfÎRdÛKÉDç2«ÑìšKª’™á,Ë”惒سFÜGgt<¤™›V“'+¯j‹Ê… “Ïõ±Ÿ‘ÐÒ[ âÌ+ˆƒû0¬µuÓbR»ØòLµfáÜk£ÊJ˜ù[*âçÐÐhôõ<æH×§­b$æ°‡Œ ¬"©ªP+®†#>kÒ½ÞO'~Nâíxß,^ÞØõ0vc£}Òôø ¡¾ì§â}ޤö’¯[l¼ €%ÂÌéâ–4þíÖ6k+³{·™G™uñåÒóaâê-} ÔB’ÉGÊ6'4ûª=ö] ªÌQ)qÈôÖÛEúgÈÇÉC4ÈO``Ò‰ªöMã[ú$ö Ûü°] ¿\=aº”t6Ú.±bí1vJ‡h¤¾.—©·Mûù¸ú‘Þ¿âï7 >ÉÔƒ`?‰ÊË#â‰;nx¯Éó)PZAÞßýîwñØcáœsÎY´2FFFJƒGÁ®]»pöÙg/Z™Gy$®ºê*üõ_ÿ5þíßþ 'œp†††ðÞ÷¾ŸùÌgÔ衬l˜;à/Ô¶Ùà²QÀ7ì˜åÁ©¸—ŒRÍ=šÜ2i¯foÞKòŒÂK¹FìЍ#\cã׃àQÊÁ†Ë“1˜¤z1¯e‡ópôä ‰–!Ìz“oGµ—V/fz×pP Ùš]?"Ó8ÖBHñeo(¾OŸ—!é™U]„Pßó‘ìWÕÑí¬®•Aü%¿2hPÅ[÷×¶0iŸØSéØoqÂoqQ©gù8}–“‰ÆZŒ‚%½°Z_ÇQ;"eª&eô¥¡‘V¼Go¬NÒËE¿}f¿œÌ?c…ïk—îŒ.—0vŒÙÐhïn‹.—ú;æ®lhîEßðÅ‘}·y[ÙPšÈö1þÅ1nñgþ›N¬ òÉâ>‚ÑC”Z¦vsÕ®@šž¦ƒïvŸz‹rûNüù^°gÔQTVé³ÿ}d\k†‡àm6¤ê)Û‚Hê§BïÐàkp®jˆc 5zp%%Ù9Œ\>†Büþ!›ç爗ÃOýfªéN½Ìòk9‰åª¹»Â·ŒÃ[~ò“Ÿàâ‹/Æ·¿ýmüõ_ÿu_£Çí·ßŽ÷¿ÿý¸å–[022‚©©)œwÞyxË[Þ2§znÛ¶ 7ß|3®ºêª9í?^þò—cÛ¶møØÇ>†c=¶¶îÍo~3Ž?þø×iP¨ÑCQE8ép a‹»ž0ŽŒà€{ ÚãsðèREQæAfÚÙt,ŒK‘ˆë¯¿~¯ÛßtÓMxÎsžƒsÎ9?üáÑjµ°uëVœuÖY¸á†ð™Ï|fÖeïØ±—_~9>ñ‰O`||Gy$.¿üòùüœ9qÔQG‰ TŸõ¬g‰ûÝvÛmx⟸XÕZt–D¡RQ”å’i‘ñiù©­cý„0™òÓyÌ7(ƒ‚»Õ§$n®µÜ,Á,YèÉ Eal;áNl?j:êأ(ÊJÅ–êù,{Êv¼t(w¥"Ô!ÿpð½Ø'*1oƒ²$Ô®Ë"]!´T¾/–7nÄ_üÅ_àòË/ßkxÉ£>ŠsÎ9I’àŸøZ­Ìñ´ÓNûÞõ.|ö³ŸÅ?üÃ?̺ì}÷ÝïyÏ{ðŸÿùŸ8ûì³ñ‘|ÿú¯ÿ:ŸŸ3P.¼ðÂ¥®Â¼P£‡¢¬uòäcÕg:øÄ——ÞÞBˆÂ0è‰]Oh‹²ìqVÊc“i¤k´«J|'ÿtóŒÖ”Çß<ÇÝx†&æã ­(Š2hCµËjŸÐø±/‚³y¦6@-À #m[¸V¦–…fu>y6òãU5€ ŠÞ1V0±Øìm,È4øÏ^sV„ =c|êSŸÂöíÛñŠW¼ëׯ¯­{ÃÞxÏ{Þƒn7 aúÊW¾‚±±±òÓ*BDÃ)§œ‚k®¹Gq®½öÚ½Öyüìg?Ë^ô"<á O(=BŽ:ê(y䑸÷ÿ÷¥®Þ¼ÐðEQe ÷°&5H:ò ³JuáEQV¾ƒõ÷oÏ ûqhh‹²Öùìg? 8óÌ3g¬;ì°ÃpÔQGaÛ¶m¥çÆ9眃SO=µÜ¦ð ‰‘$ N9唾Û|ó›ßÄ1ǃ£>:ºþî»ïá°Ã‹®oÂ\€ç<ç9xÕ«^ˆxïq饗ÎûøK‰=”ˆ ÊâQµÈ–ÇgV˜’|IÉüÈOÆ3Ç{T3d«¤T@ÿYüâ«‘L+žè±ŸwG­;+®PÈî¡P-ˆÙ»JPÓ¤¤èŸà0ÚÎ-°bF¼|+$8u¹GEúÞŸÂ:©ÍÔ[šÎK‰AÑ\l @qEYÂä‘Ùó™açÉy=ÃbÎSˆf?.¼3Jòºp§v¬ZÏ =§îŽêµv´¿‡GüÿžPÇ(RrÔ6$Y\BV%ŒBðBe':%ô3ÄÔ³Dø±Ä´5³Y_Ïþ0»ÈýÓó}xñ>ôÐCøÍo~8î¸ã¢Ûœp ضm¾ýíoãì³ÏÆúõëgx„ôãÞ{ïÅ›ßüæ½n÷Ñ~øÀ¢ë¾üå/ãOÿôOqàâ _øN9å”Y—ãùϾ(¥ûÛßþv^Ç^jÔè±`øƒAQRMP$ X0šeuWf"ËâVÊ*µ¥’´Y!C;àéÞø.f<úbEf ¥ŒZ4>7B8xªí ø¨ ²ËsÚRïÃß(ÄHr€¼GL•[&YAs‘NlŠø¸ ìÁˆ+D°T¨ƒâÞ 3‚J;4zô†¾äê<ÞÂv Ñ`›t¨.ïAꃨâ:÷ðQ.—•nú(`,ÐË´ôœ3uÐ+dí+EÔB˜;Õ4YfVR¡jEïw‚դ眅$Š ßPÞwˆ²ê’¢U…±ei¼Qf@TÝ»d7f“ ½÷³Û™…V „£ ÷‚¤“Œÿuª¶9Tù Çyµ‰ŽZ{ÚééS™ò¢ýï}Þ© ˜±ì{úPnl™ê£' ŠÍ€[p¤ ïwÇ%…z°`l†‹%¡¿‘aî‚%ùtÑ&)±L‚¢ëÖÕÇ`Åxg†1¢:R?H5IÝ^éÝò»«Æ¢1„|®Jœ}ÅB–õóŸÿ¼ü~È!‡D·9øàƒd!!ýعs'.½ôR¼úÕ¯.ßúÖ·pÄGàw~çwöZ—g<ãøÉO~‚K.¹/ùËñÚ×¾¶\w饗â+_ù Î8ã ¼óÑ#ôîèåyÏ{Þ¼Ž½Ô,ÃQ·¢(вâ‰%.:~hß%ª¢(ÊC63üÚ}®; vÌšmã˜Ò±cq÷ÿsÜÈÁK]e à;¸ññê3±pY<òHù]òÞØ°!›¬xðÁû«Óéà»ßý.Î8ã œ{î¹øà?ˆ;và‹_üâ¬êÒétðùƒQ IDATÏ###¸ôÒKñ­o} À̸ýöÛñÌg>ccc¥f>üîïþ..¿üò¨WÇ{ßûÞy)QOEQ*Øṏ„–ý½ìä(Å÷Ví¥wpJíÊbÎ|eîÅ’[²|¯ófHpIU+†abj}žç׉e çWºš&,gtÅkHˆ?›µsX+0þ略Ҋ2…Òõ:&Ø(Ü&paBÈÆåȹÏñäo$<ÏÆ¬ß‹ÁsSû=}U¡$•Žð4‘r.ÞVJø©_ KwÄW6T]ê××I}­,±-Ü#&^†1= JµðIj|–c…b{j5–Õ„k+Ÿ Œ Ìž÷JNvæ½[”iÌQ¹Bê]*È7 ýó$XPù‘®‡5›âe0¤çFÆ7ì£ÈËŠhR¢²Š0KOpTñɉǒ‘å²›ŽEß%¯Œ<ÏDíÆm†±UøgýÞ äákaÅÁyª¯àãã`Ûzb¼pA†y1)d†j:Ô«PÆc‚¨Í»,`Y›7oƉ'žˆ›o¾·ÞzkÔ°Q(¼<÷¹Ï]°rcpÀøö·¿-®¿ì²Ë°mÛ6¼âóíùƒ?øüÍßü ^ð‚àðÃ/—33>ô¡áÜsÏwK…=EYzì˜,Øe/»ì4ñ©¢(Š¢(в$¼úÕ¯ÆÍ7ߌï}ï{3¼(xàÜvÛmؼy3^øÂ.Q 3N?ýtœ~úé r¬ãŽ;¸ä’Kj^ÌÜ× d% )ðEYb ؃“Qx;o‡ÀÂÌ¥¢(Š¢(Š¢Ì—ÉÉÌÆû¸wÔ[Þòì¿ÿþ¸úê«kj.pÕUWÁ{w¾ó ¢š²\8úè£qÓM7áŽ;îÀ¶mÛÊÏí·ßާ?ýéK]½y¡žŠ¢ ˜ÀÕ”læåaZð&kŽÊ°†Ð•²×m”wÊ’¸Ë´²r0>-¿{“Fpç/îUûÐãþ?Ã%|pùSˆZAˆ€íYW]Ë0´Šåe¡Ê}¿:²rN„¡YY¸Yv?Z³OÙô†6ˆa›Š²Ü•R‚Å÷•ÅÃ?Œë®»°uëV¤iŠ$©¿oÞ¼_úÒ—pî¹çâüóÏǧ?ýi´Z-Üpà ¸ì²Ëðâ¿xÕI»¾ÿýïlj'ž]÷îw¿{ÀµYXÔè±øÔíWÃáÔÍ'âÔ}Ÿ²ÔÕQ” æ9óòØyèþøÅSƳ¾™‡¶èàS Š@4€„\‹C]:QÊ…P7€,v”µH=/Jo>ÛóФ›ÊŠEÈ!³\wÿ?ãû÷ÿ Ò|ƒ°¸’µ½Ð,£s8õÔSñË_þããY®²k¯½|0N;í4üË¿üKmû³Î: ×_=þöoÿÏxÆ30::Šññq\rÉ%8ÿüóaíò9ÿ ÁK_úRLLLÔdx·oߎM›6á¥/}éÖlþ¨Ñc pÞã_…aÛ,9™¢ ø.†÷¤˜ê€Òx-EQEQeùrú–—ãô-/Ç®éqéO~o©«ÅZ‹ýèGö9ñÄqõÕW/R–_øÂð¦7½ W\qÞþö·FFFðá¿÷{¿‡“N:i‰k8wÔè¡(« æia‰«|°‰M“sS ¶0[æ*×Ù`f‚R`Ýý»ñüÿ7«ãÞŽf*]¶©8öŒ\ü÷¥ºÈl!EÕ[:ð~g£c-$’ÂÇ`pÑÙú˜ª €=Œ‹ß£ÜãPüÏI êà ÇßZßù™±´Ä,\YÃBÊÑS• ˜% àÞ,ûÅ5¨yQt1S)i&’:F*(˜ä`Qé¢^F츮º&yu¦À9¿ÜÜ­[ºæâöÔQ.š¤2¤s"zδ¢*­¡Ã‘Ø-HÓ{àý\š=L&SAZ"$ÅŒÆíH×#TÉ·rõ–öã+•1¿ì‹ð* câêF,©…4ža·ò3¸ê=ž$E´VŸó{þçÒæ Q{«WƒåÌÝx\µ»nä`Üð;ÏøÆ=€ß qì%Õ)¦täá‘3÷!]LxLj¥úŸlÞ/¤ÍÔ•åïýk|þóŸÇ™gžY.Ûo¿ýp饗â½ï}¯=EY>H²™"J¤d¦È ›½ø(–ÍAʲæâÅ5¬‡8ÈA–'nB÷ùPöÑ‚"/Ïä9(Šk6HÉ^.fk5W›ž°k4Ø#v`^%‰ä0ÔÅ›‘Aã˜bñ¥Ã ×±+”Ñìw/4u‰ËÀøþvåvs1ÒÈž#•u_„óV;–t/ØÅ°_?SðH2¬Òö`–ò5”¸™è>Ƭ‡M8ÿ0¼Ûæ.¶ÞæÚx×ô÷Í› ¼…†ÁIö’FÝ)÷(‘öYZXB6¬öÛiu¹È¯b×v‰¯…Ø&–ìÙ3v¼ ÜÚšoÖç¾a4/Ën ¿ßÖÇmÁò0·çûä äÞ2À²V+Þ{¼öµ¯®KÓ4º|¥ FEQEQ”U†µ€’Ñ$‡Ã¦÷ Ûù-#z’(в¼aî€Ül¼ûgxɧø‡‘’Õh¦, wÝuWtùÔÔn½õÖ×faQ£‡¢(¢šu(Â!²YÐ ìEš™gKQz£0æ03Îì 3Êò ôöà|&Õ®éÙљπïW¾à2µ‡y4†Ú(à ÄgÖÕŸ;÷ÐâÖi ãÒû@Ôó$¼Û•µ·ì‘å\K‰‚]éÉé <\…ýžÒÍŸ1õâS”A#©9€FH –·1‹ü @á-eëß)ü^´ÿk©}Y]<ïyÏÃi§†·½ímxžøÙÏ~†}ìcxë[ߺĵ›jôPe0Ú˾ϒyºÀ›ä@ðð Émðé½s?²¨Ô áw2ƒë-{B9Òú€¹xÙLÞ˜½¼8\jqíâÜ#ïƒïÉõ†±ñ@~–€à~c¿Èe’Ü7´·ûVQ– ìPµùƒ ³ '¨PËËÖ·|Æ`ãWS¬ññÆ7¾>ø Þô¦7¡ÛÍ&Q’$ÁÅ_ŒóÎ;o‰k7?Ôè¡(ÊšåÁ㞉›NÝŽ§þàtì÷ÓÖ—1EQV ÞïÉ ìPJ“ñ0t(ʪFÇ*Êìá†Úð_|1ÞúÖ·âºë®ƒ÷ÏzÖ³pÀ,Rí‡=Öì¼Ì™ÆÉ+“ds³êÌáÅ’„$q"FVËX „ˆ}w2Ë;4wêÁD“œ2w–T$a{²ñDªaRTv¥+&s•ÉÕ¸wyž¼”¸ pDÉìãõ=àÖÿÃïüæ`Pz Øl~oÃ:¡OÒǘÚK_æð|а°¦AVü9–‘©$åøNÍõµHrVSŽÔE¼å/f–=¼MÐÂÃ[lÜ í™ÎÝiÃk[<ómùï7).ZJ lú¸"‹üzô&Ç,<<Fl³ ­—Þ»aV»OéÑÁ¦¸4[p¨H"µz 1쫌ÿ†¦Ê0¦ð0ÿìäòŒð|í«1£Ñ}úžCIED¸=&@.ÞVKɨ_'cG W!3œ—_\ŸVÙÞÖû‰êüz?-C"KîÜ0!´C3Ï#¡÷ù×ÅÛjæNOÇü\ó8àbýcž›%Šd¡]èW_)Ï s³ëÁÜYtƒ–!’Æ }C‡'•Bçš'Ž*sq?‡IíE(RbP9á­Œp¿qTªÍ–0!| "Þ¸drŒzw˜²_W–/Þ7o6oÞŒsÎ9gj³t¨ÑC‰Ð¬!–bÌ]pÓ•j´=<`šî3Ccõ¦u‚on$j8ctE£RT2ý\åû%Ô ]ð‹Ž´Æ†ƒÆVQ)äZS®“…µ/±a¾„Ùœ+¾ÜÑ ‡¹++ðHJYE O(º$µiPçªQÓÎFÅ3æ9»ðÞã@fÒ§•„­ó 2xà0ƒ{G¦ûb¨Tš©ê]«»tÿHÔv «éêqͽ»45ÒÎYöS0Èša|ö¾vW’ݧV—ê “@!™ÍÏä\ò.ˆçJŒ%3 C‘¶š¬hl’ž(CÃѾ…á?1s‡>Æú̸©–>ë¥(†¯¢ 'Þ£ÜPÊ–Hnû$Œ`ôÚi¢!ÁpeçоÆÇÙùˆHÓ²÷Í}­bÆ†ÔÆd¡I`/*sH4}¦˜»qÅ·¾eÄ¡R_DŒ>çPhw%5$‘øx…ÑÛ—ì½ì~Ò·rß9b}T¯!/¨“tÃk^»þ=û”:µáqãïÞ L ið¹ ²¬rß}÷á /ÄUW]…ááfmÞBñÐCáo|#¾ño,Iù fšQEQ 2Ñ™iÍÉ; È^œÉ¥8ä7Ó8iëFìûÛ)ØnW˜*KƒO¤CCH‡Úùß!¤í!¸–æKPEQ”¹pÐAáÙÏ~6Î8ã Ü~ûí/ÿÿþïÿðìg?çŸþÀË^HÔÓCQ”5LèÖ }‰^D¼ÂÖ—xœþÿµ@©ä.^Ÿõ ½Œ›†™˜FkÒf LÙga2zÍzØBøÒ´*OVÞfë|b‘¶-Ød³mäÆgS…®•”ËZƒƒ©¹V‡INvšŸNQV gõxUh®™AÑÒR|&'¸;ÓãÜ.2¡Š-‹ÊÛÞö68çpÒI'á/ÿò/qá….º×ÇŽ;pÉ%—à‹_ü"¾ô¥/á…/|ᢖ·Ø¨ÑCQ”µKM²v2èìõEzAa&ƒ©‘IP*»‡ï–á-¥ñƒ@>óg§FE'WîéÞÁçÞéPi»0zºm—c_BåŸmc<0äN°àÈánÔ\«=¨pßÖµ¢¬@Â# ü9f?„Ѻ,¬EY|fäñÈ1A¸ª‡œs+\Ö§M¦‡· 4”f 8ÿüóqì±ÇâÿðqÅWà‚ .Àë_ÿzrÈ! Zέ·ÞŠÏ}îsøÌg>ƒ-[¶àºë®Ã 'œ° e,:zPEQ<×F2½/üJä›Å›KÇS–.!tÛÀÔ(cbÇÄ:É1FgiBEQeUñ‚¼¿úկ𲗽 ï{ßûpÄGà¹Ï}.>øÁbëÖ­˜˜ˆäŠÚ <òþë¿þ _|1N:é$<ùÉOÆÇ?þq\pÁøéOº* €zz(ÊÚ¡OrµÆ‡’’õj¹—8 –ü´ï ¬´¼×å²çx³>NÆO_p ÚÇþÏyÂÄüPµc…‰M›Ôuy’)ù À¸HÊÈ”\ʶqšPɇÙÄsûû2YP®"R;»žälá:99^ôÒ’§ò'³´ÆIíAJÂ7—g¿7ô¤Àä³´Æ”¡,l.\˜r¥·yÀZòÆHÎ¥ˆYTo"²à|v8»_ /MÚÉY½¡/‘ 0sJ˜ƒ¥Ùkv}“–6+ĉÉRce/Û2ˆ&„¤xÒà~ ªÅߨð™e811ðBÍ)7M>¼2)ž] 3t,îyò±8äæƒ»w.a;:Ï61DT[he“f‰TÅåµö­¶Rª¬ù¸fì„:ªrË’°Ï>ûàÊ+¯Ä…^ˆ+®¸_þò—ñ?ÿó? ¼o=ôÐCñøÇ?x ößŒŽŽbxxÞ{LMMarr>ø î½÷^ÜqǸï¾ûÌŒ}öÙïz×»pþùçãÐC]ÊŸ¹à¨ÑCQV²ABxIc/«´®¦ŒÉèrQÒ‘ ”Á?„ ôdFâ/®ý:_q ÒÁ}‡?„dº…có—[.”"¢òµÒoðâº%GÊÞ.^Ûæ2ÅÑ;Ì`@u5• ó{XÀ $뺳—X.’ ö¾„“ÇŒi¾ ò%†>°GBB­ÆÚA¨·ø†R–¢Y:1³)_¬½MàZ-øÄ m[¸$3xL3¦F&G»è¶Ø0¬3 Oè ·0ÿqc“d,ìk jjõè×G ÆØUC1éBmì:âüàù?ÇËn;èn¯mS;ß e ª«ˆJ0Mp$£…¼®B_+õÁÈÇK1BÅ.¶óÃeX)Û¡Z^-žEha¹}a8Ñð–EåØcŧ>õ)|ä#Á7¿ùM|ýë_Ç÷¾÷=lß¾Û·WϚʙ¹;úè£qæ™gâe/{^ð‚ Ý^ÉÇÕè¡(ÊÚ…;8ë&²N¿áËŒ¢(›êÓm1:C“£t†²—úV'uíé>ßÎñ6 µËŒ.“¿_~EY¶0|i¸ÜtÇOð{W¬woilDTe&ëÖ­Ã+_ùJ¼ò•¯IÜÞrË-¸ãŽ;°}ûv<öØc˜œœatt6lÀG#<OyÊS°yóæ%þƒAŠ¢¬aàvfNœ È”0Ï„±3œ*‹ *ä=È{„ži¸Äg¹Q·•"Im6¹nªÙ¥"$¦?ÅóhÁvœŒ.X8¢(K;øÎí™—^—'ÃÏ T–Ñ»C“¾/k:è tÐAK]e‡=EYÛÄòxhG¾0ÔB ‚(²µ–y9J*×ápVH×*‹Dx Üd bÒN–Ï#Ír0x“åò`8ëÑm§xÂkþûÛGñݫ߇;Çà Ã%Α\F-§KàNÚÒÝ J÷€ÑY¾ù(E‘aæ™!hb8¥ާ, aHK-GG>ZuéŽx¹b6k ž…Þ 3ï€1ÃBÂïê—$]ø}M29¯øo”’íõ?'ñ:Iõjz,¹ŒØÏ<'2£ÑåS ®BÊóÐëÑž‡$ô›è³ŽÓˆ¬{!C& ÊR2&ƒmCÅøö'ª»_t9àÊóP?–„“¾Ùäàø.ÔFüap=‰[m} /˜ðÜI çlT£~ej×%luÉ J>=Çõ&èºrùTx_ºÙ˜ AÝ«gˆL¿Æ!&«l£ÊCµøó ÅÛ+QNVªÎœB9$¥™–Ðfô‘ýô{ªÝ¥rë`ØÁt'l„I´'€=›³ã·º£{†€6g°î±­N&a›tR˜43šÔŸÏNUvAÝ|ù`µ£í.¡k%¡ÿ蓵©:Žˆ¤žd†aE÷}¡ÿhú;Èö•umRÆ\$9›öƒÞO€" <Æl…jE•àÀ~<^xÃþœy ÞO4ÚÇ»]¶Y¹ßίÔHm ôk’0 ŸCéÞ¥9/VÓñ•ç®x, ÎIvÆŽ7¢,ˆâã© ¹®²ä±„µR>KYÚ0¬4|žÃS݇®5Vö£Þ&Yè"Øϯ÷0. }¬¶3i§VbY)n1dYÊÊCŠ¢(Ê ²p‚¥®…²’°)ОÎÏ&Ëñ‘-¯<<Œ¦äPEY–êÿê;y(+5z(Š¢(3½>e6$Æð0`¤–aò<Išyyd<+Š¢,G 6T=´ÍVVjôPe^„îÓ[sÁ¬\s]ànÜ㊺Ɇî›Òr±"•û}}Ÿ ”ƒ,ê!úRžk^Ÿ,¤¡w™EèÉ<á$paï#Ó*3Ç3°M‚u¹|-yøâ»3°EØ ;Àî:v›=aH“߃âÞ t”»J'S-Ï\¨É3ljaÓl»jÀ ´§È3È3Z“SêNèúÑû<êó9hzCRÊ6\•4eùëég<|w­±ò{wt]ÙV§m›;Ÿÿ-‹È››2lš‡({F2l”÷µµTEY&è]©(Êò¡fèXºj¬y̺뇾 ‹ÃïØ‡ýäú%©FT"Oˆ3Ñ—µy !¸ž›„¼/ÿš”th†[´I}nôÈ’ Ö Qʲ¥0‚¨T°¢¬NBŽÐàá”FòU¦!nÒ3,Y;¸¢”•‡=EQ”œlÀ=O\‡ßûSLŒMâ°ã \%„˜soêÙ¤•A$[ïA.]ª**Š¢(½4Éò0Yµy*«5z¬HÈÚËêÎäìÛR6r¢¡è,+QK(Û}G¨¸¡’GS²pÃf cÆn†5û€ì>péoÁ<™©ªp·R¡ê×OÅHR­ ÈÊ#±óëyì¥öx ߯öç}ïq,#”A­…—HH÷!»¨'f?Å7 ™zË\ÎAP]£–| ÅßÞgŒ9ïYßÑðÚŠô†¢Ë˃Öûr¶Õ³~wCÕ÷Îh_†·Þ.\ÂHmvEËÜLLš¼•XØ”aRF + µâ¯¢,'Ôè±FÉC±Ó"*MË®Oç 8J1‚“Ð1Îe`³@níýŒ4’<Ö‚ºK¿C°Ë’ƒãbà>ìy(‹A/Ñ0vñ,Ürò.<ó[´ç–½ìû]?ŠtòB6íš<| _*ÕÁuãÆ é9³FzÍ×E*%J:Ë÷ˆ`x€‡1ÀìÐÚsžþ­jyߺJ/o{•²íA]ac*£‡ ¥GA¸“)Ëa²õk.ž+i`ìÀÆèæ0ÂsÞ§s2h73æ/âï`í~0­Çãî§=‡ÿø'H§nQ·ÖßÕ w}ú±7½&ì{­7¡\Yô»G›àüj ]Ÿsôé‹äI† 5¶ëÍ6ÿ†Ç2Ñåa¸h8qæîHs—0º-_SáÊrzXp>Î IDATØ”r®J•+[1ú0Þ2Ȳ”•‡=EÃMb˽áZh™1°ëàÿgïÍ£,«Ê»ÿï³÷¹·ªz¤›n Í Š ¾˜0é—0E4Ä!N+* Š¢5Q–þ._] ˜d½Š3þBÐúsÈ«F–äqB0b3hT@»¡Çª{ëž³÷óûãLûÜ»ŸSuŠªº·ºög­Ó}êÜ3ì3í½Ï³Ÿçù†D…@ 0B°ìNrëÂÄ÷˜i?ì-˜ˆa4§F¶…Ñ©á8³z¶H¬Ù†ù=!Œ@`ApG¯ÙN¢ýÄm8òÖý±õ)OŽg< '~'Úõƒ¾ÑÓ7ö™¼B]òðêˆX%¹ñ‡S-[†¬f“´ÇŠyâêgV>2•z} Êèåù"Òù¾§Š§ìžèÇ}o&wÞ– ?d{UOïµ¶÷’6Áð„SË“àä ;ÿ^H@`©zyT¼;Õ3•õto< i€¸Å0‘Í<= ¦Çc$Qš˜{mDІ%T„ÅK`ô󊯓ÌèÁšGAÜÁäê#±gõz+÷ÅØN ¢YvªóŸ~V_NÒy6·áMt†s­FEÆ×íˆåJM(Œ}Û…û?_äòÅ='NÞ U‰«ëúBÎØ`Ö!-áã{q`îa|\“¿ ‡ Â½ F>ˆÊy¡ðæÈ –Óã1ö¬™BÜJ ŒB»×Bk´b%<<Œ+-„ð–ÀhŒË€ýúsP¤pʾÇã” ' »8ålwá}‡ü(ÿa¥s €œ|5Á¨,/zc º=ìZ¿ 62X±chº V}˜}†>ðÿ⇿û'$!‘i` FeÀ_=å<ŒëÔm¼ÈÒO“pú’`qÍú•‘3º®h¼Hpè&ÝJƒ¶=Û“h³š0ªi§Kξß4KºR«ý ²ØŠ#aR²+f8…”XVÊü]—ˆŽh•3ïîÓ¯NÁûU{ØŠj>Òy“”HŒt%Qä@¹¼ÛHJ,Õkå¹L ¬ZéhƒR°Ž[§2Iá @Ê)“ëŠOÚŸ — ¨·TϯZAqm°ïÞR»º/g„œ”üÌU§B~l•$Å•ýT3¾{!I¦å?&éjÙ"±¨ª,—FŸTbÁ*«3œ0ùÃ[RÚÙr çqLºÏÛ#{/e»fà†¥J^8K™Šò™M}©,€Væ!©2ã†6 Ú(m%QœN:!Dž´Tô£‚ËÅ0xûÛߎ+¯¼rØÅY‚Ñ#0¯¸.ŒŒ¸t‡vb*‰& oBÏq‡t>Rœ˜ñÅ‚Té‚ÈÖøÃ–b® ¢6¸½/(™ld÷ÔBÑ>‘†Rk´aÌËXè™É?B¥¦Ô¶Ú0­X’¶F<®` £ÙÔ“œ”ãÿ÷ÏÀÙ_Ú„¨ÛÎ ©h_HØbwzòã©v:ª…dlþèÿ¬Ù)˜±5E® å$ ³‘Ò”ôŠ @6î3ä4uå–ü¹;T« iQeèQ*9ë=œyåæîp ®)ÕDª’<­\ŠíÉÚÝÞ {qeûØu÷í;¿Jˆt örÊÐŒJ¿t.yy!— ‘*ÉúSJA' VioqÅd”y{0”%èDA'å>Æ»-D=…±i:©7nõ–Ååãÿ8.½ôRpÀÃ.ÊH2Ù¤À(Cj- Ö¦£Ù×ÔT©5èø'˜:øåD°# +‚lDˆÛÀ®u:‰_ó>¸ƒ©U Q1ôŸG@™%2ŠC¬â‰6º«³%Uõ @ ˜-©<|:ßêíi…ñŽÆxGcÅd„ñn«˜&&5Æ; ãS©Ác0™x`˜t:œ~úéøÒ—¾³Tú¶‹HðôXÎ4½n¼¾M“aY†G)Ú€ZØNšH“{pGž¼<¤°™9ŒÀˉåÊ,ÛŠ Ì0¬>C\×Ê™©M´'„ä ¹ÈtŠð–ryRB‹Ûîl†Úr¢6I½¥æÚÎŽÞ¼6Á}ÿ&¦Ç îzÖ]8ù¸oa뵉©Vî*«½ÕÛz°ZƒÕxê-Á™šÛ"¤†L@&âK Ì%é³…/²ƒÙ¤ËÝ{ǬZè¬Ç[Ný%ž÷ÙƒÒËᆰ©÷‡d)’‹Åþ23jÞ)!9Ÿ¤3KªIF*ÇSÃEÒRV¥wGÎN›ùe(ðì’¢ö…GyתÉüÏ,$}m ùÕ¦jëQ-¤ÆÖðþÈ*TvÀ3&¥æ=Ê;'/ªyêÌ‘‚_ ^—2†•]jÔÍä„©£B)ßó:…ß37ç&µÁ#7ùù\v$]Gí½Wò8÷\Þ½DÔò',fn¨š³8gvo‰F%)QxŽûhT¿. ÷ˆÒê&2­ª˜e^‘Æ@Y ‹ÌC2!ͺB–BIǺ©ÚKÔK :±ÅÀYGÈ£RXxÆÆÆð®w½ ÷ÝwÎ<óLœvÚixÝë^‡Ã;lØE ‚ÑcÀ°ƒ£”.ïÊŠeÚJÜ¥ `5¼¥è øÕæ§á©?~˜Ú¶“™a³f‚ é(tü ZÌT®ôþ ±ÃÀzªók°Ý “<–>|ù=„sÚH ×Kì8VÜbu(ñ\ÄLìÊQÙp:&œlMe1“‡²Ý‘Vkвóô/ÒMiBüv6’z‹xMÒ}2ˆß}®TT„(Ø(*rBô=Ȧñ©ÇÜüt<𫃱~j ­^‘ŽP5åõY‰ò\g™Î™4ÀÄe9 Zé£@TÙáž Y›´œ¤ªlŒ­Àc¦û:îæ£Àª[œsÔ)3ʳj•›4¬L°“Ùòž7Ë>‘_Ö ×ø—S)ûÙ¯Ö→HH±¿&"ǨAÎrYã}áK2·\'¤¥ç¨ºôÊ2ªþg¦ˆG¶@.û˜ÀàXþÀ°Û¼‹%ï)Qñ srÈôv¾uQÒ¤Ä )”¿õïG6ÒÊaŒs0’Hõ¾d¼¥ ïoÌ1ˆ=QDüØcãU>ãÂêùM¬KüíŠ('ÏqcÊ5»Å}y R¾k¸ð£ŒC7IÒÂjÜ;`@{?¾™å>ƒž·´ê¤I}Ôl«·(µÚ[gZé•aeî€íï¾é<Òóö)íAT»jjŸ’=cëÂcš>®Ï¦0>úžW¸Œ¨èK褔„Ï“Ài´4|Ô@%ÑtÚ6µÐq·8%©™T0„·,.×\s ^þò—.¹äüû¿ÿ;Þýîwcrr\p^ð‚@ë4¸8ÁèXt˜c˜›pÛ݉£¾Ÿv¬‡Ä(ڈκƒ°g}ïžÅ¤v¥ù=†Ý¹ ÌHj°P…«f«—ƧãhÅ+÷´1ÞQ…Á#uËô76jÃFvmœÀª'VclÏN¨Îƒ‹t&:5VÑ8 'Àz<óôPŇ|þ¿$Å@ X>䜳Î: gu¶nÝŠÏ~ö³8î¸ãð’—¼^x!9ä!•rx„`ðÀÂ12˜÷ š¼/þßG‚“G2ïˆÅ‚{ؽ¡[NÿMú¡I튢˒dÖ£muá2£ 1W¼ —K ´»ÀêöÝ:Õ;5Z=Õ7õ«ˆ˜V„Î*FoEš@tÑ! Ó^‹©õûÁ´V‰ÐžVÅDÖSî 6 ¶P6²IêåÁ6{·FÃ@â–{&£Mz¯žü1Y•“H–lUJü½ ¯'´³¼ZÊÛbª.g§MæbRÂ|:eë³­ô=ܾH`4˜œœÄW¿úU\ýõøÅ/~|à8í´Óð¾_ÿúׇ]¼E%xz 7×ÚÝOe?X`úN¨­wÂpœæôÈ>¸‡™<ÓöîÁÆ;Ĺw®Bjœ1Y¸ûÜE2&óÉP‘vÿ.sx°#ÃJKø#¬-Ü2•1 8NÕAb•ä’µ)Ç=3ß–ÑêVïk.sª³Æßcr‚‰VbýŽE6 ±ÁCO[ûžò8žúó Xÿà$þÙÎ Î}sÜ–UgÒYÞqîÿ“,»˜“Á‰¶ež b¦ïk¥ÀŽšY'ôÄVórx­U¥÷rÜð–±®“$’ß[Êv`ᄺ,á÷#3Óú#†¥úr™Œnh,@Ö QtÚpíÌ+3]„àXyååÉ$ézÈÚÆ›'Òÿó0'&xãûŠeÞrñÅã#ù`Ë–-øÄ'>ë®»{öìR /|á qÑEáœsÎÁÃ?ŒO}êSøÈG>‚Ë.» gœqÆp ¿£G`Ña˜"ž9 ‹pj@Tæo`ŒFèM`fŠQ‡<¯Cb2yÔ<¤jð0¤«ø-tcÍö6Ʀ´:ÓÞõ†|”$ÆÆßNÃèõXÿà$Z“Û²xí5¶@ †Âg>órÈ!¸þúëqË-·€™qÐAáío;.¼ðBlÚ´©XwÓ¦M¸ì²Ëð¶·½ oxÃ055…sÏ=wˆ¥_x‚Ñ#°øè#Òâ`¹ âŒÉ"!ÖRwg\vX›æ2´©<[¹‚J†×¾öµ8ñĸtÃgï7ë@ 0•P'Wx*$2òÑúRh©nTxq Z¥×zC*MèîjBí@ æÂ;ÞñŽFëŸqƸú꫱ï¾ûâ¥/}é•jtF@`/C”Ù¥|´5_oæÑ̪ «»ßf!‹›jˆŠû“°‰”ÙÜ-ÿæRõ&ÐÒ`]61¹ñ M.[>W¦U®Ó]UÎO;^Õ½1[¨´XÅ…ô¬U©Â˜È"n¥÷I1ÿr«Êý¶§Ëc·"•yùs’Ý’MCœŠp*6 ,d‡YL¶/RòC¶l1…ãžÓŸƒ»NºGÜu(Ž»ñ¿ÊœNCÀ I µ½õ'â¡§LÀDŒîD‚cÿï6ðÐPÊX:¸a2 TBZ*ëemm¨"F ‚vÂw ¸w]Àº‰ÜH·¶Øõ¡Ûßé Ë!¬U^ýêW7Z_)…7¼á S˜$=eHÓ|'{-µ±³ÁÐñdp=$˜ª†Žr9ÉÉdǸ1¸lp_Ž‘ƒ”åýP– Èo{ë MW”%ºaÄ¿Ò H¬˜ŒÐê¶11%äžXtòûÖÁŸ¨pÿQ÷âé?=㨒Ã'hD‹ Ì™E6€¸mVÿs;í1QFv1µ”¸õÖ[1==M›6áˆ#Žvq†F0z@`Ùà   ˆ³ lú¯¯cÓO'Ü—%Y #‘µ»pÜ·àé?þ}{ â¬yVHPÀ¨òÚ×¾ŸùÌg–ß|óÍxüñÇñàƒâÁÄUW]…£>z%.ÁèxòÌÁ*Ü4»¸è*[ƒ©YqpÇ¿Ün{·1òyå’ü(Œƒ}ÙÅY>¶W1pÔ ò¿gŠ[¯†ÃTÕ>¤kháO/—Wʲ¯­ÿ~P2éÏæo{B¶wR³P$ª¨Åôœ{hÊ2rÖ«¦!«:HYà%5BÛŸQž¨ 4oP]¢IAQBÕà>릕žÁF™‹·"˜Hó½ñòé¬*ÃUzã¥"n™B±Åh Îæ-q1ÏÄÅod ”MG™òm`ruzl²ÀôXÞ2ÞQÐIZ–Võòr•ÊWÄ6“ANç•«þã„J‘]å¿Vû]àÉTUEÜm„û!­ŸªPx¶©yßźDR*ÕZ¨„Á¹eÏe—ûŒ’BT]!"=»Î»™¾§éßÖw¡´ØÖ$Ãr·F½AR›,fR™¤ûTƒ¨#¶ƒV®—¥+š¥ÒÛ¨ãUhÉðÞ+G}¥¹¨\5‹¾£ªÒåW¢±¥«Q!U+ou†äÙÔ1ý^&U9Ûõ÷?ØÀò`ŸÈz”^ Ͻ÷Þë]þ×ý×ÅüŽ;ðŠW¼ßúÖ·«X#C0zž4Rgo^“Áͧû¸”÷AòŠãž·s*wÈëÊÚÐý•üqÂ,,O·‘òvè>CÇ“qÅ•>†”hÄh.Ï&,·=ÀgÄ$`AuǨ+GÞ)—3[¯ë;ÊIîu[ø¼×—Xá<íºæãÍ—ì“I3­é4G>O°Q:oÁ8­PܲÙrFâ7Œ.—»F&. œ@,1 Š @jÔ`Çè· È”"iŒ‰P@¬"ج¼JQæbKÛ[”*lÀÎ3) ”߀ʀ·S+‹ÆfF«¹3‹~ÁLò´~šÕ»ò¹W®yå=Á00ÜÉ:õy‚—fR™sA.«©—ñl³ëã9IÍ/%æR'z¯ÕÜÜ¿\™ÚRÊÖÂ/š8¢‰sÙ‚É÷¡kE£ ˆ`t%(oAý’×ù¡9ÁߦÖì}ûšÞïTÜyJŒ¾³Ôý]ŸaV2ìÎ.gZe¹¯\¤Ÿ·"\²ÙµM ›Òû<›º¬&´&7Ä çíˈ2ÞœÉÇ£¦¯º,÷࢙¯ÀÚµkñÈ#,BiF`ôÀHP0Tj¨ÈÿN“—æ¿U 67ˆdF"·‡Q bÕœV1R?­Ô(B°!Å` ,+tcç>{²7¹rÈÍ}¶lÙ‚-[¶T–=òÈ#¸öÚkÁ<اaflݺ_ýêW±~ýúÅ*æHŒÀÞN6z@N8EÅÝ“;`!”¤Š;°:ùHÁU•uéªZ±;„^÷J@‚•ò†´Ø¨ i± èN”#©YØbÄí¤Xîóæpì<,¥^V[X£`ŒB””Ï«6 F—ûU6-‹NTáÝa]ï*ÉßÊÂ[’ÒÃ…HäŒÞ;ÄÜ+Ü'qæ¾³ ¼ ó‚;Â]Q¢Ê62vFîUx¶FˆÖŽ-8ûóë³])Ù[©ôiLÑ7ôà1žõ£@’$¸ï¾ûðýï7ÝtS±¼NÁ…™±aÃ|ç;ßY„ŽÁèìu¸y8ªó…k&M”† ³hðë²} f7Žº㺪æî®&Ô…´$Q¾·«¹;rcFÒ2«máÍQ„¿([>€ô‘V™œ,Sê’’‡ÊåÌÜAMTºžç†ЉÞb 㓪ª·Œì;pÃEªá˜®r‰<& 6 M 1’BÊ.ÒÙ‡ ˜&K|åšØtéYBn`!‘™”ÈT¸¶ÖQ*PlÎ>ˆme"3S&¤–WM‡¸'(üÔߤ7¨‚á¿Æ›@Tu@¾„lDðgEd-ŽFº¡@.VE€Ê½*œu¢ÂÓÃD &Êç¤]ª¯ôÆÊ{–')µÄ°ª/aifÔÈ ìx|XeÓâb¤Gƒ•‚a‚² Ê((&LÇi"S&£ !nYpVü4¡©ÊÊK0íü¹Pà$W± “‡àDPfº¼¾JP$±møòŒ1µAžç$%–Þsa¹€/U ñ$œ…•;ÛÂA:JèyϹ‚¿•‡¤cK‰Z¹]£Ì%á„«9Ï}e´“TáíAÐÍ gá)2[µY ª™§@šÈTR|ñ—×˜æª ó¦B#íŸZµêC>”°:SËî‚JK]2XédÄÕ®xzp™ÁvpyJCï9%pÚ(ä‰=ûhÖ–5Ï4b¡¯6P®‚军¶¡”ëà¨f‰Ê*®Rštú™MLðªÊkÄTOdƒ‹¤t'©õDe ù~Ô(ÁxŽ/%2múŽž<'t^ô¢ »#M0z,”Z %uÂûH?¦›5š$ ˜Ms7ÏÚï#Û¯²AhJø6SþC×d —•¤—1hÂÛ ÈÆ-f¯u\™Zj§“^‰dÅ~ØvÈZÄmƆßmÀÄÙ=´]PE­¤é~•çG IDAT(ˆöÔñAQ ÅÿÁE$)GÈÏ4ë² dÛJ%Iê‡&WC]¼ŠÔ– 1BÂr×û#MrÕlŽá\›ôžõ>òÙ†Ç`÷Z‰lå”ňßMef»zºAã6njô°s5XQj¬±Ü;‹FAÀ¯–"‡¢’[¸”¼Í°°°> ŽåþGc#†5”¿ÛyÇ8;„g ý(¦–÷ºG´®j<Îס¾r·Ú¸çeß§¸Ž®´l¿áA2h¸8Æ“ªA:W~ÊÊ'Ã9éº×ö•¼}ƒ¬•úá’e~ «r=–©·dž—‹ÅbkÔÐZãoþæofµîO~òœx≠\¢ÑcDu²Àü“6~¶½=b-n>û—˜\@ÇÁ" r†ëÙa"ÓJ`Z âv‚¤À´ ’±â±I;.~·‘L±MÒNÒuZI¹^d`2ã g¸Æñ @ÀO6BÎä"I×#ÄE]4ì" …àéìu” ®o$ŠL:voÜñ)Öä6 }ªÈ¶¹ô/ÛKuëŒÆ±àæ:1³†Ÿr«Våë ðª`5˜»£¢Ä’çìÈ•¢{)=BX9£Lî()€)‹xaèDÃ…JJ™[¥,TšÛÃñôˆŒÙÔ¢#*TÃ/B=<Øîñ† „`‹!)ýý \¹®]íùüê:ƒ«v5íV8Zž„ð–…áE/z¶nÝŠŸþô§øîw¿ 0Æà}ï{ˆÒþOä‹o^£G °\(bL»hïzë:k@ñ®šdZÀâR<2V™gG–ÇÃj « l”þ_äópBS˜R¹Z²ig‹,CÇÙ1lf¨PH¥nt}«Ú,ÏŽ@ B ¦µ ÓkVÃD +žØ‰¨óh™×hÖ^)@3N;í4|ùË_Æi§V,[·nˆ—^z)Î9çhí7ÜíØ±çwÞbu¤FeÀ?þâƒP¤pêÆ3qê~g»8 €¯Q©ik˜taýÉÙˆþÅôb‚SY™Ã™ÝyH* f;Èl/ÿv7ɶáþ2ñà:»ž×´RqG‚ÑI FP‚qÝÐ+.îþûŽ%\Óùrcg˜æJóÅ,;iÄ¥g“*<˜ªžyX€BŠÖºŠ|™G¥¥Ìï°ÁÊ‚u™Ð45bpÅãô ZÝ6t¢¡¬`Á*—±M"¶0v0,3ÈI†ê+o~Ú™Ñ Â(%QÛ«\ŃͶF‡“cÎ>ëY°ùóŠÊU[ÞF‰å}Eú€r' 3ÛnŸªE®ê`½#ÙéºóS¦À<ÑЋ‹¥ö™c!飜,u4½ å÷\Lœ-U—lá“oIû]¾v¾®U÷˜òÿÙTݵ×Ý aÆÆñ½sÄã?†]ýûˆ¦·v²zÿç«ÝU¶æP¿ÕmÓ¨LÕ{~ó¶ÿÂÍÿISUÇ@#n¸á<ñÄX¿~}±L)…}öÙï}ï{±jÕªš­c=v¡‹8’£Ç2àoŽyƹÉ%CÃŽ)Hh׸'KÓÖTöb§RP\=ìDyFE„´÷4¾¾ZÅ®ú—;YÄg,K_lÅèᬯ„ •{UW6¦rqIö·öªJhl0âõÕõÈs­ þJßPAh–IÃÚ]Âo’Š@3eƒT†Ùw=@ –°Q›(¤!®L-²yr°V!óäH“ˆZU*µäá.…C§^¬laܰÚdNø‹.s~€ 62©‘ÄèÌëC0 rÃ[laüÒÜ"¹kl’)Ê(K0‘Bì¤ÆÑI®HC þœ0suñÓlˆôþ·áSQzm\ßN‚Ùÿ~pÃçP’/¬C5TòHŸ]É((”KP¡a’Ô*s DcPî7ùmq ?\Ÿã¢´v lýŠMÚ¡b}ŸâËH~HŸ1?5ä7TÀ1;½ï5S°f·çV|o·s¡éó 쀚v[xÏ-wAì©/Êv¸R¾býÊnÿÅýÁ5t¸åö%!Í–Kõ’D* ´¦vâøЍw(tÜ’]Tlñoj$keÕÿùÏ/D‘Fž`ôÀÐ(äfUÕàáÊÐIL£2iþ[ò’{{ärµœu`‰‘þÆàÒ‹Åzƒ^@ øÉ*Þ÷¤†²1ädðÀÂóÍo~I’Ê}ôÑ8ùä“ñè£âõ¯=nºé&lذ—]v.¸à‚!—t8£G °×S6Ðï…e<êçzt^øð‘½šÁûÊ7x'aûäËÕÑg QíÉÝÝÕÀî}`µ”¬N×a‚2ª²-¡4tPaüÈBi2cÊ\FŠ\À2ÅeŽëCäÀìiþ;þr˜{ÿ'ë/Ø.Èv>Ux×Ãã¶ÛnÃÕW_¿ú«¿Â‘G 8÷ÜsñóŸÿïÿûqøá‡ãÊ+¯ÄºuëðgögC.íâŒÀÞF%ž¼>,ÈÝ@—§áƒ°RnØ ’uË‘~Ñ XE…áÃD¥a#70Øl2ºLXjU–§c@ÆE(KêÑ‘þ6õà‘@+†mÅ ±î<ì³8öî7€­r¼8vn‰Ó| Iž¤4~ÇQ eÍËÈŽÜ®‹ÞXn$½»Šyc/晻ŻSqÖ@@†-ØÉÑáÚÅż!îæ£èרŽ7t•ÔDs%:7”Ô5nrGx§—ù{n)óx˘(Šðío›7o|ç;ßÁm·Ý†|à¸ôÒK¤IPÏ;ï¼eiô{@ :³ñ°Èåh;‡Ü:ìnpf$Y°‰ðŒû^“ýí÷) !œ%ÀÞÃ-·ÜR<àË_þ2ˆçŸ~±lÿý÷ŸQÒvo%xz@`ÑÉÃTL_ÚáZ1ZlB¬-HÙ4I©²@ÒJ÷’´@FCÎ&@ °waûBmo»í6ì·ß~8è ƒ*˧¦š%ÑÞ[Få÷•AYÃtýf²$¸2wüê&Ô–Ýã…c“G Q2rÆ~E+›íká’ÊIáÒ9plý*4Ön÷.W´Ò{ ¥ÖÔZO¡têúYl£Šùdåï¹,\ûuGfgöƒëš£NæÎ~å¢|Ïn­áZȆNŽ ;×Ô:Q–—¼YÝ•è&KJPVäý˜{àâšTÃ“Ä ê‚¤bš!ÞSZåSù%fާРDì,·@¦ÞB–A™”«NIÛ•¬Íÿ/U[Œ®Þ{bʊ˃6 &%@T’Ï"sWì¬HW‰[i¶x« Œ‚²j {<1 ƒHî9B–²íÉ]Ç„lù~~T+Y+…ºH1ß:U]é‡4” Ã(= éó6¸/¶{€ÞÎÁ ×ဠÞ"=o¶+ÖKÒ•R4^È"ƒ§½e’êpÛP%%}7ýªRû1ÕýÏF‡°fL2¨äQ‡‰…û!@ªj(·Ú˜9ì¿©2G«`R´Nø¥¹$°Ôôz÷7ÚOúìú­´_qAGž¶¹X>xíµÞJ{¶©‘“{¿ñ¸ÆÜMÊ> ’tjÞö1ˆ[Îßîo û jÜ{ ­7€ÔwIº~¥šž·Öà»/ÖЉÆÿüzdº@’õkØ`0$¶>gÁ8ÒÖÕûbÌcÞm$ØNÁ6­w›Ö‰ßƒºýõ–Å%Žcc µÆ}÷݇Ÿüä'øó?ÿóÊ:¿þõ¯qÀ{Ø» á-@`v䶪™*˜‰ÜèàýÍ1P”MʨBŽV e4tA ²*ݧUYôKn)“.[ÜY – Ù'R–äü°_oÄ¡÷î›Ê@‡œ<ä9Ïy.¹äÜqÇxë[ß fÆk^óšâ÷;ï¼/{ÙËðÁ~pˆ¥Áèdú½Ÿï7>@ Á‡Þñ¾ý· Û+ÕZ‚ñ#0B¼óïÄ<€g>ó™øÖ·¾…¿ýÛ¿ÅÙgŸ_ýêW8õÔSñÌg>·ß~;ÞñŽw »¨C!„·б#Z &V-t×”®¸­N¯P±ÐÝP„"¹‚ „274–röwrÅ*Tê l"†‰l¡B– `- ŒJ•[lÕ A À*µ RPÈÂO,”ªÊÛ!”y}T¢2ƒ‰‚NÒ|ùïÊ(h«Å:“¸ÕF•ó‰B”ÊÞC'€rÂ[*Š-¡ó{y]žÕï¶b˜à†‘TBAûû8ÙßbHˆq¶Ñüa¥@Úí6¾ô¥/a÷îÝPJaåÊ4äóˆ#ŽÀ¾ð…ÊzË‘`ôUÜF: ga=Ž©õû!šŽAÖÂ*adœœ¸Öð˜Üâƒ`Ns}g VAÙTEq&Aë<’n޲*ËÛ¡Š|©$ý]õåòe—•ûö”Ù¹Ú@ XÚÔ ÒÒ´fpéåJßr³,úJ!§ÇpX½zuåï(Šp衇ßpà xÙË^¶ØÅ:!¼%È“ÆÔúýð/ø-~wTÝÕ uíY↡äd©ô̰ d(ÍÍ‘&T‘Ó# wщ.&etþbt¶/U< Øú÷ÃP`eîÉ*£1¥–ùŸq½¦‘²Ã3Yw`]Ø—sÎc{&±áá}ÑŠâq‚‰RWÿ±)ƒ¨;²qêêŸlÛL!¢2"Òó«S€ò_[­÷ʵʫ擪 ºˆ2ÇbÖzQ©„;`_yëFdÄgDÎfïS1`r"5`‹íÉÉŒŸo'W¥XÞãר7Iû4þìðº×-›\l¤`¢´‰Ñ Ï P%¤…˜›*Ê(& +a›†­äzòV[(¤ûaÅÅ…bÕ§´’'*5¥1C‰L¢^«XÞŠ#´©†·´{Z½t~lZ!Ê“¨g¡’ô\•1PIùüqEl^>?©s‹T¿Ö)xö%)DA~†Ž¯.# ÀŸWȲ_Y…Ù¯ÄâWbª+Oþu$Õ,ñY”Gˆø®APò™O¤g4=Á~”±»ýíBÝy4ÿLU<õ¨†Ë;Fh‰õ•¨þÓÐc‚¹øú¯Ô©QêP§Î¯¨¿¨5é¼™DêÝÑì§öª¬saëÖ­¸ñÆqÿý÷#I’ßwíÚ…;î¸c%>Áè±,ð}àÔ|Ä7ýÀ'©á×ÂG¶®©ßC‡r¶ˆÂ5‚ƃÙYï’½gcöñ L­´ØzÀžù½vaø@ÅðP–Oü8§VIêMMxK%à<'2õr¶^¤eÍGt¤û!IbV·U•Žj±¯L&²|¿vAÊRËG©È¥dqey_ˆÊV1HÃGL]hK^’1÷İ*Ý‘²\Ì ÷¹v=7 o‘\é%ó±å::_žG•O'¹‘¦Ìã¡,W <Î5¡Êõiöœ)á­3nJ¿)ïvLm¡>žá8#ˆ¯ÎHß“¥u…CnW$Ùo[䡇B9ùÚsŽEYeÙè1OÌóàPciã¦ï?À# Î Ã592÷³¨óÿ¿·¬zb%Îù\ÿ Áò¨«BxËâòãÿçž{.vìØQ ù oå±÷Œ@ÀOæÚI¦²=lüB2¾ÉX ­UÖo[‰¸ma£¬Z ¤™N|`*=fHƒÑ„‘Ë¥ŠbJ»æL °æ,Gf± 9 zîéá~ ¸a2:Ö…ÜmnðÐVåÞQ ÀüóâœÜ޽8G`ø¼ë]ï‘G‰¿øÅØo¿ý õ ápçÎxç;ß9„Ò Ÿ`ô–=}‰K o‚6 ÚE^²1ôt*I°î‘qè8JDÝ ÓM=IlÇqžûÈw ƒ4TaähA©5Pj HoÛ=`³ ÆnnÈ2t’Z tBÐ&5,ØD•ID³AÌ"ÄÅ(°bh&p¢Ë0–`fXfPnàPƒFâØyrÒÌÓC 2éqÛ½Ô]\e!-* ië´eóãv7-o«¨ÄUlɼ>’¤ôpaÉ3(#‹¯Þ&@ ¡óy“®Ó+½Tâm™¢]ÞF8R·¡ÍÌ3[·nÅ–-[¼Æ—þç^¤Áè·T'î?—ªÍóH°…2Ó€M ãÔÈA6N=<8vtëG4?À$ÍÃ}¤Ó8t´ ݈ÇÇ?»à=þ˜äCY8¡# e L©G‡²«R…–mnÅêÇ×b¬Û)e†ÎŒ¶ Ñr¼3úý4Üd¥ ’Ô¸ALˆ’ôz)£%™¤­%´b¤F¨Ghe±N¸*S›…·['£¿ Ø@ Øë™Eø'•Æ‘2D§_õ¥š•…\lKÞ²¸~øá3<à£ýè"”fôê-@`väFÓ…ŠwC%S™‡ÇÞ^1²O<·žµ¼ &¾&ylI^ÿþ’Ü ²áÁ©Á# ?QLY.Ê”Y”UÙ”+tf¼hõ¢bŠâ:›¢^ ­8Bdtiä0*ËÛ‘…¸,óŽR æ.Ô^\ïþ±@àÉò”§<÷ßÿŒë…D¦½šh¢/ëtHƒ@^1¬#ì‹÷T¶eO’("Ý—ôÏÙDJÈØ0˾å. 3« Ãj“˜ ÛH *>Õ‘r›& 5MJ&& u½3œð–b¤°Ú,9PlËlÀv2͈އ¨ ÂbR]áZqEÃ=éx¸1z°ÖŸoD‘”áD>%9YâB(`0LqÏØv©ÿÆq·œ‰µ¿» wÓk*=ƒ9¢b‡ÏXbAžw6Ý@Pº1](ϳ«uyU°RhõrÂBdRƒFîY‘‡ª$Qu?L R Î<=¬²EL¡Ò’)³äëéhO¶BLˆâ¨X·Õ‹R•Qœy}0¡ÕSEN”%ìÒЖB±%1e"Sk‹÷œØT¯scù¹ë>±N”Uy|Ï(CöR±vJØ—„©¯ã<Èç'<×¶Ëžr±•9X:j¡ÙØ…•’D64>2±Pz¬Ñús¡±¢ )QÑb¾ ’•<ämÊçÐm{HxOSÅòÜ‹z,¨5½V¬Z †ÏÉB_Û”†ª.Ô‚"áY”Wjê+o¿¤æ¼ë£KÇhN³v°rœŠ×‡–÷åà&WjÍÌÅs°l4Í©Öܳ¤IBø`Ôï{ßûðæ7¿_|1Ž?þxq½¿ÿû¿Çk^óšE,ÙhŒËB+kÐÎh%wƒ+?Ö×v@+ Šò®ÂÖ‰S$ +û†êjÕÌ+¹¥³€:*iÚ ýíóŵòsM¡wšýǶ£G鎩ã6ðå=³ÆŸGÂò¤ðñ/wð}†1@6±ÝS^/î æ®×à¶¢”% r¹Ä=¿ŽÜᚃÑc6·ü\18þo¬øå辈YîÅN ¬N#%£G2 è‰åÊLÆ‚€u®uÔ+nnŽ^;•Wcâ4¡)•¿›ìùÉ[Êr•^#®!$2º˜oõʦn¼[¾ƒ­¸¬ÇÚÓ Q’—¯Tlщ…Ž“l_Õg¸0Þ\˺úÍw}eÃ(õ,!J*îeÇX64ëH“ICÒ6 7ŒÖìöübÅó°Æoôhlpaùl§½Ë%ꤴ%d×Ô»â1ÔŒM?*g2Ôö¯Þpýt#Ç€á~¬‘BÙ÷)¯ÑXŸÌm.nÀ¶4€TËÕìZµD jéÜ›Ý×ùTn!j‰’Àò6þõÓNÏ9Jý…šw¦V­L¬O<s,ÀI×]~FëÔãüç¢h¥´¦ÉÆ÷—mÃ>'ùUÁÄýsÖ˜ÚraQØ;£„fͧ?ýiyä‘xÞ󞇃:GuÚíjb÷îݸ뮻†TÂáŒÀRfž À(‡¹XÀ¦&8fN=>,m*Sk‰+ÉKscCÛ2QjnÉ÷ÂXeµRoÒs`%ÀW\mÛ¶™ñÈ#à'?ù‰w½ YØ{!'9eAŸ×†›È2Yè߯IpéŽVPâ|tÛ=J‹¡ ÒãÐíg€“ß"‰;Ç=e£Z¤ûÂJ\WàÅNPê>á™ZrX •d^8ŠÏ B”+¹X —÷¹ I)–ŠV[Ä:ó¸Èr~䏯ŒÜØ¡²š|¿ù¼6åvyx z‡¹#Rù¼›¼T„tHdŒD­lšAÆ‹Éâ]•½JGоOm¨k  ™ýöÛ'žx"Þö¶·!ŠüŸø;wîÄyç·È% ‚ÑcY…²¸F w>“%í‡IŽ«+;óV•NäÆÅš'À™;6¡'¸õ…ƒÙ2“û3©q(½?¦ö?+ks0zTÝ2ûÞ†¨ â_$ƒ i§#ªú¤£±uä]-¢^z¢ 9X«ª£ í±¨0v˜¨44pfôHÆb¨D§y:¦[…'Y')jöwž´Ú½¨ðöhõÊç½—Ûé$5|¤ó r[Šs²¶©¥~«E‰½³h¼øÆÄ3@½‡`“GÁÆ”ù¾Ø–¡¨5ï/9mbÓ¤@`9Ô[—7ââ‹/ÆÙgŸ]»Þ±Ç»H%-‚zËrÏ=÷àüóÏÇüÁàÔSOÅñÇO~ò“CÛO`ïÃÚI˜äw˜xè{0Ó·»8À“B' Q¬¡M*«ÊZZ½«v¬ÂÄÔ8Æ;cÐIªÆÅÙÿ}ç冷ØåÝ! –  ¥V‚ôFÜ{ÂAøõæ“@jMóü#@ °DøÔ§>…SN9eÆõþéŸþiJ3zOâg?ûN?ýt¼à/À-·Ü‚V«…üàxÎsžƒ[o½ŸúÔ§o?Ex‹ÒBºâőê•N¤¥`uTé,ÖsC]¨]$/en;J®9„Q•%À Ù(s&¹ì$õôQ÷¹7» ñî=G¥ü-M¿å%e¥Ú÷OJàj@vðy ¶PIRüm³p² d ˜¨,|+Và¾|iÄ “ÉÊj£`WTZ´Q…1ƒóy‹²©çGÞ’T¥r •'¼…,á8d-”)ÏÖñôT¨ÄPéù‘žÃ9$íëW’™‰¡çšK?Xfææ2©:’/a±|M«›†³qýª‡]'²„î„ì²éó|åà±Bˆ‹ü,úžw¢Ö¼&-mŽ[L¶-)ÄÌ!_™ðnŠŠRµÊ…’wiÃÄÒð÷¯¸N¹.˜#Gy$`zz?úÑÐétð'ò'€Ûo¿|0Ö­[‡£Ž:j˜ÅÁè±ìܹ/xÁ E>ö±¡ÕJ+âÍ›7ãÒK/ÅûÞ÷>lÞ¼¯~õ«e? 1 ÏœU¾¬'ŠÆ†õx®’4XG°:B2ÖF<³“¨ÇЉEÔAœÆºk×`¢Ö o¤Ó~‚ûqš¡NÑe˜qñsú l(£'(10ê2ù»ËuuYVfË»œÉ§²çŸÒâîG’²Ü.£&㺧\Lrœ´Mõ/çþ˜lW¶VȬ.8µIêÑü$¼“äÓ÷`y؊2ÌJT›%ü|“aP‰ ]ÄÝÂPYžLO+™¨È‹)…ÈQS/çÕ>é¼U =F°Ž² §•&2Ò| ¹qÃ]WeÞ!ù|»[^ŸñŽÞâœR«k C‡JÊ|$Qo*. p…Ñׯ€qŸ÷9t¤»Øv…÷\®û¤2ü‹ï¬\Ç Y6Z’¹ö+¨ÔIÀJukªÒàÙF’/§´^ëý-‰ó. k¶Ášmˆ¾ëÑÅS@”ÕqJ;ý]ù0§Š|­滨RÍU6˜Í sÒ󮽿µ*×d6Hê-é;ëkå#¥üu"ÁÂ-×¹F–)ûiÂàQž´r?^µ¹½ âÅ 9 >¾ëƒ IDATyw€Ï}îs¸ä’K°}ûvz衸÷Þ{Ýn\p^ô¢áU¯zÕK9BxËðñ<ð^úÒ—bõêÕ•ßr]ä÷¾÷½ˆãúÆl¾öÓ&ULVkØH«Vƒqø@ 0”^TüŸO.ŠKÏ&Ìe›@ Qçßø.¼ðBlÞ¼W]u6lØPüvòÉ'ã«_ý*¶lÙ‚›nºiˆ¥KÆèqÔQGáÆoXþ†7¼§Ÿ~:N9å<ÿùÏÇ…^8„ÒUùô§? 8ãŒ3~;øàƒqÄGàÁÄw¾óEÙOO…t¾’£²P–(‚4L¤ ƒ‡‰Rƒ++¦üÿr’'$Q å!&dÓ0²Î”åæè_–ãH¨2_5’([Néñ«ª-y™ÀÒ…Ñs¦Ô;h^äjC;F”øÃ¸öÚkñµ¯} oyË[°jÕªu>ô¡á£ýèJ7|–LxËÝwßsÏ=_|1.¿üòBŠçŸøࡇ›Þô&\sÍ5…±`lݺwß}7àéOºwg<ã¸÷Þ{qã7±V µ©:‹ês¯s ¬#˜Võ÷x¢Þ¸FÇy_LİŠ%hU Ëh9F n­)\Œ)AI¶ã¸Ç‡ÎÂÜqr[,ÄudS†ó8á-\ê²P0["70Ÿí6uVlÀyèœ[÷¨ª¡t|ªTu‰…\•62-$: mIë!. ®ìl޲€6N®^99UÔ+CZtÏ!8Ê”óðä1$ÔiÀ(Ç¿+æÝp­×—»ïu‘û¬Jæ¶»¡] fKPoY\&''ñŠW¼¢vv»Ngï­ò±d<=Ž9æœp ¸òÊ+ñ¬g=«0älÚ´ ×]wתµ˜Üu×]Åüè]gÓ¦M€;ï¼sÁ÷3˜&RHÚÀô˜Åô˜Åîµ1>pv­‹1=ÎHÚ Â\À <ž©Hæ¹áü_¿ŸÁИ@ ½}öÙgVë=þøã \’ÑdÉ=ößüà?À%—\‚Ÿþô§8þøã$wV¬XãŽ;nH%Lyâ‰'Šùþ<9kÖ¬<ö˜œøl¾öÝdÝdO1%¥…¦\H" i[˜È¢;£³²‹îxŒ¸ÅYÈKø˜ O¢âÈöDYC+¨‰Ò´Â{“ª…ìçݦ×kæágÍn³³á6~¥VxN´^ ¥ÓçJÑJP¦¦@Ô‚Rk@Ñ!€÷îKë»IòP£21÷  e§Ü¿œù²^HCøl9ïmË켩´µ ô ÿoB{µýÏ‚Û.¬õÔ»uåòÈ×§ej6FV'›*וÂr5Ö\‘Ä©CÝ\ Çþs!¡T¶€ì=h@ú4T1êðºk8_ç!ö¤åÐ5²±Âýr¼)´…þ ÛyYú»çïhÔ\éÆ4!—ú uDÑïy—[³]P®êÉ×WÊ=#)¾ ø•iJ¾÷øÏðÛnÜ.˱µX,æ±F‘7½éM8묳pÅWà…/|!(È~â‰'póÍ7㳟ý,¾þõ¯ãûßÿþK:–ŒÑÃý°?çœspÇwà/ÿò/ñ…/|?þññùÏ'Ÿ|rqƒ‡ÅÄDù!DZ×`‘«­¬X!7˜óµøNø:Æu)åõç÷ð¬êDŒÂ¯vбûDZ±.\Ì›°~k„¨’ÀòC4éõOÃôÊ1¬Ú¶/ÔÔÝH¦ïšyÓ@ Œ4´á÷qê¾ÿ£ø{W<‰ÿ}ï—‡X¢åÉ‹_üb|÷»ßÅ+_ùJ(¥ ”ÂÄÄz½Òˆwå•Wâä“Ob)‡Ç’ oùå/Y1|lܸÿöoÿ†+®¸<ðN;í4\~ùå^WžÅä€(æ÷ìñ’åË÷ßïïó¹×+1­*¦Ù=r…±iÃy Žýê³ñ”Ûž‚(Ö™‹9ÏNá K€¹ï/¿‹5÷ÎRe&{  R«ðøxô`‹]l’BÀ^@¤4Æu»˜Æ±ÀÂóÿðøÜç>‡#Ž8I’`zzÌŒãŽ;ÿú¯ÿŠ·¾õ­Ã.âÐX2ž»wïÆ{ÞóüÝßý]¡Ü—\r Î8ã ¼â¯À{Þó(5\;Î1Ç"3ãá‡ÆúõëÖyøá‡Ç{ì‚ïHU8ZU C!†2©Ò•Û$ˆº1Ú] ©´è„06zŠ´b…¨GP‰-ŽÛ¹nàÜJ».šC“y›…dî(:¢.®ÙlÝ‹«§ÑÞžsW«`Ú©»¸2Ó xW¶£lÝP™ PhHQÇ8õ ª .®2Š2Æ;O–‹ÄÉnx‹²P¶N5tÅõFs ´³IÀLl\½%HSÎ †e׎)mö¿çqبÖîûÀñoü!$ AE½c-Üz0¯W™§ —vk§ÄО@ F™óÏ?çŸ>î»ï><úè£8ðÀqÐA »XCgÉxz<ûÙÏÆÍ7ߌSO=Ïþóqûí·¿xâ‰øÙÏ~†óÎ;oèžëׯ/’©þüç?÷®“+³œyæ™ ¾^)6&ÆôõÇõY bFÔ³hõ­^jèhO«Âà%~u…rçAÃ~^¤ôæuÿ3îg`°¶œ(ó,óy—‘- ù|Óð»ÀBcÀÜî<‚Öä#àøAX»kÑŽNPÅèòï<7AÑê…¯o@`™@ B¶vQ&,ïœývØaøÃ?üÃ`ðÈX2žßþö·k_¹r%®½öZ¼ño\¤ɼüå/Ç–-[ð½ï}/{ÙË*¿=úè£øÕ¯~…õë×ãœsÎY”ý4AÙˆ{›¬ÖˆÚÊjXE`•&Ô gžáË"õ0Ç`³œ%xe»–ý s@ 4ã‰'žÀM7Ý„-[¶àñÇÇêÕ«ñÔ§>Ï~ö³qØa‡ »x#Á’1zÌ–g=ëYÃ.^ÿú×㪫®Â¿øE|èCª$½æšk`­ÅÛßþö"Y©1ñßþö·¸á†°iÓ¦9íGÄÆ¥ú@*a{åh–ë•¡Ç¡Ø@%=0)p7ÂØnVé”n›< ·t7¤Å8áìªÌÎûƒÄŒÒþ<$¤&š)¾p°þQ;®Ûqej#m0g9Q»ÜŽÚ¥Ñd¶Sßþˆ”wË[[¾¶0 ®›ËÒ•òtþ}ÚåDÊ¿éùXþóyÛðÝlCwí¾éG Dzq§É-t\Ì«îæÒÒ5”aØidÉ:”¾©=럤]IûÿºÏ/ƒõˆW~•ßxP“‡©4°º¹:ÉÙÄBgS]Œ‹UåÄ•©4’ð“Í 5‡g75  N–»Þi¨Ô<‹Å{ÇÌÝr²SÎÔs¯Fzrž‹ë¾4Ð@©¤.M€Ô*Z Òé¤hå@[NÚ;I÷\¬÷Hn?ÄveÈŒZýZ'£YwÝ÷ ÄgËß~H†Øá#•«ì§¹Ñ`ÿG”éÍÈë£þ‰…:ì_ÑŒbù€aÿ´YÔЖlZNÜpà 8î¸ãpÝuס×뙽Ó7¾ñ œtÒI¸æšk†]䡲×yzŒ ÏyÎspóÍ7ãƒü N9å¬X±“““xÿûß7¿ùÍк¬èŸþô§ã¤“NÂC=„ç>÷¹sÞO`öPn€P ¬A‘ÓÁŒÞèϼÂÀd'õÆqÌíOGÜbŒï|lØ% $3Xƒ™aaoù0À^ÉW¾ò¼ò•¯ÄÊ•+qñÅã¹Ï}.žö´§aß}÷-djwìØ{î¹7Þx#>ùÉOâu¯{&&&ðò—¿|ØÅ Áè±€wÜqøâ¿8ãz+V¬À-·Üò¤÷#b{éäºëI®{•_9ºË£*•–Ñ7µµÜÞ7-‰wfÀ{²ßóóLÃRcHv^ŽšÊâ8 ¨9®BáŽV=4üê-Ì„r¯’ƒïø L{-T25Ê~‘§odLPoqiš'¨êQá©*¹”ó:ñïkVÜYð .çÙñNq=t“ðÄù(—²°¢q04Èv¤íXêÒ©ÙC Ãã±ÇÃë^÷:œuÖY¸îºë°aÆuÆÇÇqÀà€ÀæÍ›qÉ%—àÕ¯~5ÞøÆ7âÌ3ÏÄþûï?„’—áûKNR#‡ä®7âÒK§<ÜÅÆ©»_žÄÆ Ó-¦tßÙTqQhfÅÁø·ó L4Öe^Âà¡7€Çô†Ò+$7,fv÷8uǬ¸Ð—¡1U×hiûLR8ÏQ`Ó°KÓ20J8¡n äÌŠ ƒ‡ÍälmŸÄ }Q”®4Ï'ÕP—¸˜ªmÆ"—‰;`Þæ=zYÙRCÛw†Ï{ diѧåÀ'>ñ qÄøæ7¿é5xøX½z5®¿þz}ôѸú꫸„£I0z–!ÄÇüì(3&u…)Ô·×!™Xn¯Ãœ’v-%Ødá=°Ù $‚¦ïÇ »d@ °`¤žp½ÔÐa'$ÁàÑä+_ù >ò‘4Nq µÆ‡?üa|ík_[ ’6!¼e9`v$øt÷Cm pCvÂ\wZÎZ¿+0sÇÙuGx%Å Y‘DLÂÅ=ˆF ŸgD6ª§¦îÆaÿ•å¬ànvÎùö¶#²1(©†¼¸y?|™ÅËò6¤H’ê”›Íà9H#åy‚V(çþÕ¬ ¸ÖÙ5ñî6 C`¶¨¨äîß$¨…Ú¢‰•¤$©Râ>6ð",ƒ#´µ‰;Åc «3ü×”ÒP(ﮨF]Éë½Ó‚ÂJÿñIzwü‰-eE"-Üs¹•F¿I:w׳ý—œ*?I!.fü=ÇU\åxxTBZœŒæ­n¹_)g}ë— é 'Õc㽇„L•À·/éh¦üAÔ‚Rͪ¬ð~ÔªºÌèÍSÝgÓ¤¬œ{-6ÙF—å5vG™ä—ºEBHæ8SgÒÇi’Ó>ˆc@ù­#éýoyCC­í‚íô`YóDоó°ÍØ’jþœ4‚MQçW…ö%ÏtU2TC¥´þk~ÀP·ÈÐ_'Y"Q,HíMŒfjEi˜€<¨« "T}PJ1ÁSsëå¡âß Ã†÷…¤Ò8â=ŒîahÄp=£oĆŽFcœ+ö)âØc}LÌ(Y ÷¯±(FöA]‡š!vi»šª·©2tEî‹øœåª^‘u5?Ó~ÍÜ/Ü{žœ;\ü-É{‚šÑs¦¦hB•> Mů 5£íŠY–ËôÉ1¡ô8'Sµö‘úK°i²ýÉ”œÓš 1µÛ{»g„áeô&š¥÷Da°‘®{Yê=³iõ >ÒG¶13édB `_³/‘Æc„¨.½N5å¬%à $ÃnßdLÉèÑû`[‚Kï¢þ•B¿´Ný•_Sf4É.=òû½–˜]Ôx{2 ÌU†YÖ(Ù½{÷)í¿gÏžuªÉæBвQä/Ó›`š •Gåmë¼PŠÙ4 &‹Î¶ýø×+ŽaiÛ"þ럟Zµe£É Ä &6®ÙL Ífá!z)Š2zȀؤï·@Ö½—†A³q6Èî5öÁ­ü;¼Ÿ‡÷ÇGVEQ”õ¢Ý®g<ígjªÞÈVAв¡°iai÷éh¬tÐê6k¥³~ù¬v®þ˜ï§Øž,È{\ú¯²åÛ ì†Î(ʆ#òrcF¿çŒoX,ìlbç£]ÑàSZYkÒðX¥ò4¡©¢œ‚Œ™©4 Å̤žìà}îuè‡î‚Ïp0v¾ñâ§âéÿÒ/ÞYê¨lN*­(ŠCвa˜Ô˜Ñ˜Á‰ÝMÌÌ´æû\Bó1Ûg.¿ÞôKã“T1'g&ãÌÎÂqB×áÐõ?”Û…ô®2aTx.…mÏ{Àf¹c ¡ÛnàÎ+¿…ÿúÁ³AΧ¡.ÞyXŠ÷àìob.…¤„a,lƒ×[ÒÂÏûAÞ«¡CQê’…±» ÆÌ Õú1µ‘$ßG·ûýtv X8ÝÂ~쎉Á¶z ˜öšrÂ(ãF/™$‹®ŒÄbÈÉ0Ë%Ω%Û>Õý7+jôP” þJ±ûác¥²~ò¼L0Þ6@ÞƒÙËIQ3ƒ‡¢l*Øõ¼<úÚ/yFk±‹—þ÷óaœ›FQ”±!OkÌ ÍàŸþÛ³pàá½xúßÿõHëż ×ý.~ìö'àÃÜBC6¾(Š¢¬7_þò—ñë¿þ똙©—VVVp÷Ýwo@­Æ5zLÌIĵÔ$§L7®PC‘2c‹¥k&›«JÆ$ª¸hVÍ4‹·ðILY×òÙ|W^Ç €Ü‹Ù±û¡{Ó€o¶ÀÆ€‰@&ËyÐîYE&3ðÑXËR„ºÄÎ%?Ÿª„·1ê%exyvf„³qUm1št=˜äDmñBäD˜µ“UÌpIIâÀm0뻆¡—QeJ.`øTe…¼¯4®šUxm”’²ü·b¶®Œás°UC+„$µÇ2CNj)– O–È.žó‚…íG ™hxH˜Ô–ÑÅÿúî¿QŽ—ƒ¶»ºó‘s€ÄË®>VúÎuÞI£OÖ¯ÇÎb´Ï`E[¨Û×BÝë.«MÅ#*óUQRléý-' —‘¢‡×°AyHJâ¾^IL•ñ#I¼ÿýï_Ó¾Ì ¢ÉðˆéG€KƒãrÒ¢v4Òý7+jôP”1€\‚ÆJí„)Cð†Ðè8˜Ä¸÷Vg2«ù®SEQEÙRPãt¬ìÚO|Iç?F]E \pÁH÷߬¨ÑCQÆã  ´ÂYh—òµ4Ÿ‡²µ¨P¯QEQ”~¸µ ‡Ooã´ù€=E©=&ïOÀS¿ÛöñøÆ Vøpñ¼T‘=&—+Ô{/ÔWb çQ%gذfW½}ZO Šó…›/±‹+j˜&Œ5_Dp!·<n@ÎRN-™‹¯3ñ$Hä„2:ȯ¯,KØ ¨ ºOÃûQaHF’L؉2pÒ>F¸ uÍ%YDPVÎ;&Ç™–ÑdûWJõ”„ð(Â8R™Y±lnE3ú·Éò߀‡…·aØŒp}L*ÓsµÝ¾Ð0Ÿ¾ dŠ•‘c¦2¶ÙqÉ‚ŠUòX´•ß&XñÞJ9,íŒ.•&ì,ŒÝYáz*ý»4ö uŠÃ~>Ò™dE IE@O•c`y¼o±v[t¹tmÉÄժʖ%]ËE{°(Z1â 8ls¯P©¸”®1í uéÝ,\wáÚ’ª-·,]ßT§®q?~½tŸ\|¹wG+Û\tá¹! àX­]šÍ3ã‡BKPo逹¼«z÷Àؙ豤wš¨äCí¨Ò È‚H3x¤B…/Ò~V¾=_Z޾êÞ?æøØ{{9Ï¢×±B¹J‘ÐF¼°| eÔ½VDUï¨Èö™ÂŸñã‡r2̲”͇:Ê+Š¢(Š¢(Ê&ǘY4›O†µ{2ÄzÓ)Š¢jôPEQEQ”Mm>G.ü˜í/1Û2å@Š¢L:Þ¢(ÃDrãt&žÑÞ´Ò_NIÑ"u5¤M¦.1Æþ?ȸ„@”¯MàzÊ¡"À²&rßä”ÔS„°.6ßÈ”‹|ÿ‡±øào–þ&‚qY–oÏ9Ó;j¿ §P÷Úí,ÀA˜NöL0ÊyO(xV˜—ê–¢(ÊV‡¦±íàqPçŒÝ ï÷BêÄ0¤Í5†Øjô‡ŽPø«î¥(Š„=eT„q£ä!Æ—gÛq¨MOfÓ;âèì“RÉh1hIñ†JÛõ+Ü/Ìý‘5WÀ*} IDAT!¼Æ8ð*6.ª(Šß@cé0àŽ!Ÿ ýxC¨dÐÞšŽ÷*Y«Œ[ó)SEQEQ” »'€å{áÝð~¾X¾U?ªE©ÇñãÇñÍo~sÔÕ ê顜2¢›µQž¤Ù}A5b+ÁþDt9q'îzÊÓ…ÛcÉ~M¶¼=ÇC>ÊÇê"š ;rظ\ºçBÙdÑóê°Âò2¦XîÒmÈŸƒº[Ó†ñÛ«Wü•W *-ÂÈBRùUiD# vî ¿îdÅ+CÈÜÎÝh2rø†|½ïLT³½Q ¬@ìÁ”‡±x óÂ`Cpüï^èŠéóôp0¼¥·Ü”N¯·Â$Èd¡ =¯¶bŽ”Ø÷Ε=ÈeÏAÿù‹^-Pt"ªÿzôÚ>å§E*&·‰¹WÚú㼜Dqµ±-ƒ‰4QæŽ8Ï,µi¢f\Âû¸ê5al\ýǘøyâeÈ*MÞKýU•LLecÆö? Ÿ©˜ b…óó~1ªÞ@ÔU¤™éZ±_^7o±NÔbêʭÛ_ )n¹¯yBì¥?ôú*Q©«W©øbjGÇkdÚ%uµÕÀû°_Õ[„g³vÅ>Þ—@ho‘±ÆÉ8µgÀ¨‰FólX» 9x÷8wÞ=yÙÈ/õTÊÜ}÷Ýxë[ߊOúÓ£®ÊÐQ£Çðîïü üøžKñܽÿËú |$©[v„ÒË=ˆÏG§op‘ÿ-C|ÅËLzùÆ?NS¹³zCRÒ‚êÁU¿±#[6p~Ù ÐL÷êÛÜ›þí—³æÜm7¨ v369÷‚ÙGÕŸ{aõÄ®oÅq¤gM«#é<ÐÊ–‘¯aÅqb튄vˆ^(WzÜf/¯†÷}²³é¿ièJöw߉††¾é•Z6áß”F™!5xï]kVQJÿ½(“düž¶@a¸êïÂ0¸0¿d°«`ÀÑç@’g…qM£6ÙøŒøÜVIlÊÛÇÚu7j˜cTiâ÷Vúpô@ú,ÄÖD—Wô¯´Ìûã9í@|ÎÉö pýëj~ðIe¤÷võmT~oÕ—äUÞë剑=óëöÇ—úcXá: íª²bÂddøX{K95 dÆ8o‰)žSc¶ƒÌn|ñç ?~ë`åßáýñSë·Å<.õ$nWË—݃/þ:’|Áhˆ2²Ã,kT,--áãÿøšöuÎáÃþ0<¸ÎµÚ¨Ñc¸î©¿Š¶u5E o0–Ô9Ÿmñ8zÖ“aÆÜ‡`–ðÂHjª(Š¢(вž¤^YmÀÌ¡Ùiþü&çìÀÜ…KƒÃcE[e¨WÉà>¾÷Û0 ¼7º¼I`×®]xßûÞ‡ÙÙÙÒò÷½ï}xík_‹7¿ùÍ⾿õ[¿…½èE]űDŠ¢Œ6ÍbŠÉÀ7§ámÇ÷ÍbîÐìÊ2à=ͤ£³ÝŠ¢(Š¢lÒ\,)¦ß žÿ_Ç>JÆûßÿþƒ|ä#Ág>ó™Ê}o¸á\sÍ5ø‰Ÿø‰ªÞØ¢F Àó2¼4[ÙGíds(' \ bÆl¬£¬{Tg|ìR3±#û…Ú©Z ;)i&ôØÌ1-4½Š#zïb2Ñ*Õ‘ºgR¡ä±šíósÍ<=ؤÉÒ¼‚k¶ðð-|í…ßÀewü|0IϾ”ˆ2<Q ̃çN”+(Ī[3;xŽÖî™Ùâ»äQx^1Ê *ÁàÄ#žp’ØDï9Áƒ#Éãˆó,í‘c‰¡NBbGX1û> I£Š @Å@ÌÅsö©ZQ­c¡ðôXž:Sí%×`8;XJbCÏ.Â[Œ'8p‘ìÌ6zá-®A…Lö’îCžalC“ï€mïÙ$œSðÌ–gö¤d°VtÜêÓ¤é-‚g“” èFï-órTy„á+Ž%$çõ>ú>`^ûȱŒ‘=…7q–Üý%¥vð¼2¸€•™ÂFŸC”’ó–Š‘TÄ„Å&Ú7šŠ¾˜œ˜È°ö{rRm©¾^PÍJ·ßX…qû5Œ=Ä2$¥-!9o5Þ¡bŤsÈ ^k’‚RÕ9Hj~Þ¶_–tG—9¹/ñÍâºKã æN‘8•¹+kÝ ås “ïRxŸ>1IppÍDõòöÕ·×}>T"ycùÙŸýYqÞ+9³³³8zôäª@[5zLì—Ä™m×ÐQIƒ½êr¤«u¤®äXÍlÖÞ|W–!|KÒpÔ(þa.<¤|q R‘‰]”’“Âæá€<,+3xpáåÑ‚k6°8ÛEc¹OŒdª‰Rø|ÿüº›Þy•r…”Ïܵ>þØÆ~݇{_ðlìÄbîÛ¹Ne»•Û•‰x˜vücÝX…A.!nX0˜M÷‘ÚB…A"20£Tê"B•‘-ŒAwÅîÈÕ>q` ‹³+8û»;Á& oéÏØž:z*/œ•Ì0>5hOð†a>J / BÚÖµ,Èg²ÑÌ…ª‹À~*[îKm€Lh¨Œ©•!/÷‰¢1DúpÌ%žˆË6‚«Üá¥~Zø`öËq£Ë*ìãF2Íèû¨¬N®èŠÒWmªÎC0T€šÂXŸñ*è& eJ7ÔŠ\MY¥¥®A2K6=xœŠkUa“Y¤s‰5©·Ô3ÈjA-¡O–ÔPê—]_O6¸ÈÆI-H’šwb¿$=$U9üUí*Wc"“¾0ƒç"¿^Œna˜õ¼"ÊC¯/±~ÉËåçCT¹¡)ybGÀ¬Á¶XK¥)^‡¬Þ‚ Po©baaÎ9X[e˜dÌÏϱVペâE-}ÛMpÞ7xÞ?]ˆÝg›õfEGèâÉ~ì—pÁ]ßÃŽûþÎ?•õ…Mê‰qÚCÓ…Á£ÛôX™rHZ®‘È\ãÛòXšébanÇ·-ãĶe,·»è6]áÙ‘35ˆ¸מùREQÆvEN2³0û_äì×LÜÃVQ¶Ïzֳ𶷽­r›w¿ûÝÑ$§“€zz(Š2\Ø!LàEìSwò¼ŸÁ¾›Íh¹¨+|­%NÌ6méÇÆâÜ ]‹™Å\Ãg!/ݦC·•€Mê BžÒð–,ñ)’,Á©M“Ÿ…F6€|,òd¦ä}áé‘.—î³&9U‘°ÿÖ„ÀÊ@°øÞÓgql×.y$ðd㤟Šrªüöoÿ6žõ¬gá«_ý*^ûÚ×â’K.ÁÎ;±°°€{ï½·Ür >úÑâÎ;ïuUG‚=EÙx¼!…¾p`bS|P’÷iS0äïf¿0™ép0i΂̅·ßð¢IÆNÀÍ:WñÉIU[Rå–ÜCƒ °°m íÅ)´V0†Ði%p åé’Vg<˜í¥©"wŒu€ñ©AăA €3×XãÛŸ;&¯b>hö¾oùjïø‘§mFQBꆱ(JÃga•Œžü™[`Ìv8?¯ïç¡ê-Ãå /Ä>ð¼æ5¯Á?üÃ? ¬7ÆàÝï~7žýìg v£GzE-ìAìSƒFþw8ãƒu¾;Úðîõæ=ŽÁìÂÜñ6Ø0ºM×ðHl‰uXž]®ãx䇰8»ŒÎT‚nÓ•dl×߬(Š¢Œ —…¢.À»#êÝ¡LW_}5>ÿùÏãÊ+¯„µÌ k-®¸â |îsŸÃë^÷ºQWqd¨§‡2tªWM®«« tQ©ê %–“’]U_7ƒ¬xÿ€U¹ösÏëƒØÙ¤ÉǼOUP þ– ÅhY5ÛU¥A£¨WUÈ@JP9Öfä©Hž£?Ém_2@òy¨ `@0ža‚'gÉJsc£3½‚üȘ:1ƒ÷Ÿ‘CˆÁ†‹d§&Ý)Kpš—ƒào.y u ï=¯±O£bªkPVT’EžótbLPY…rÀÀöë•‘M¼ÏÊÂáñbRBQõ¤²_ˆ]“ U01ѧxµ’úÉ„£›““2‹ÉÄ+zoˆ¡·tN¹òFæñE¶H\Y7Ñø8R©h1Œ±TFE2úZj3Uý`Å>r?S‚#÷ö`òwŠñf¡Ø‚ryR %Ô:¡¢œ*Ï~ö³ñ÷ÿ÷ðÞãàÁƒØ»woerÓIAÊ)#«¬áÛâÆ)³yÊà .½†5¥)%e߉ޫTY îõThÐŒ‹ .8üxoɦÞ¾ Ø©"l`ð£Òƒ)7e븃\a†ýRôÑ×b˜¨+çØgX ?.¥Œ˜‰]Tq¡QÕñgŠêKÊI2“T¥þEÞžM¯íæŠ>9Æ—¿Øs£D¿Ê¶ag.®Í•®¹à aƒO?ú¿£q¬üÊKsw0ȤFÒÒì0L’ÉÜ&Æe+|Ï ©dˆcŸ†j•ë?¿üÿ¹#“15-øæ6˜¸ƒ‘+âj« ÈꈪxL…ÊF=Õ#ù#e5FÄþ²WDÉJDT³ËðNPi©4< >”+@D+&+(ÈeÊ\’š†ðlzAzW,&R¯ª*©ŒÚ°dW)Sœ+¥ê&Öì(PØ/ÃûãpîX­ò‡‚hD¨’²•d«¥q”ð> iòǺԮŒÙ/ƒ»Âñ*”!„ñJ•‘­žÑCVøçJ-}êà¼WkPØxcˆ—Õîê:áqDñpÕ[hÂÃ[rVVVp×]waii W^y%àë_ÿ:Î:ë,ìÚµkĵÞ¢(Š¢lZȧ†*¼5²¿A#±øÛϾ·~ù5h®4ÑH,Œ3Å ,”µ~Å-¸1‹ï=sžô##ª„¢ŒCmX»æùh¶žÛØc¶ºjŠ¢(›‚~ðƒ8ãŒ3pùå—ãºë®+–///ã5¯y >ô¡°v£EŠ¢ ‡“¹¾ù<ŠYôâ×çõû{\Ç:mqŒ§’áÃ0¡‘X4º ì}xÎøÏ³0;?‹f·ë LßÌSsò‚ú^™b¨E?r˜ÊÊ”C·½5<Ùå” ÔŠh˜}Vö^–z:lOOEQ”ä¶Ûnõ×^‹ç?ÿù¸é¦›°wïÞbÝe—]†O|â¸çž{pÇwŒ°–£CÃ[EÁG£äŽª^˜î‰Þòd¾·.³ažîôÜM¹fÐZ‰5Ö’Ë@‰„0™ÂÈÀ†à3ÙÚ\½%Çx‚ ŒÄ›L'-Xgà: xëAž ƒ•ìê5z(Š¢([Š<®·Ñµ©á#3zN ä gÒÐ?ܘã—%Î Ã2Ø)Ê&€ÑÁyßÞ†£{fS™p}>EQNÊÂÂBaðhµZXZŠ$NŸÔè¡(Š¢l9Œ'oá Ã&Ä©gGáí1ƒ‡ü›ÝH•eì`‡íÿñlƒƒçeU³PEY;wî\Õv‡ÚàšŒ'jôPÊ­9ÚÚ}µ¶÷þ8< YöÅ6^®ŽL»Þìà×IYIPO eÄ2¥ŒçL¼€TêÖÂØ]0­ Ó’#ÅŒ²K‚wi§ÇÜG”cª°fO¼láüÈì(gâ|ûàzû„ŸŸœÄÔ,Cì#ÑÉjÂG¥±Bkvq—XîFgÓð–xÙÎ m]¨“/+×”ˆ/·'E—µ£j\‘cB¼VB†ÿT0R/jeÊ@ýu2«òl°ÉR¡ÞÒXiÁ$éyoáéòNÛ ÓN’:Ëp VÌ º¼ú 6\¯Å™@¦Öxej1ixKzL2”…Ü—‰)¤u];l04 ÎÕ/í'ÌM³o»ìày!z}êöÓdÚb8Ž¡Ùèr/´Ýµ(vIê?Ž„ç\ÊãÀÝÚý®(Y+¨´¤Ê Òõ?ƒIçÑøæ¦½.¶±ÆÌ¥å™véµS ö%‰÷º²­¾{‰t­¤÷G]å(@Vºb$…–Vë4Îú4Iò0À>mO¡p_¿eíîZu*®u„5)ÑÅŠ€ð^©(CTP¯y…BŒÐ~¼Ÿ.ûvéý/ÜC—Ž—!RßÐU·O44ª™$×¹£µ¶¯Ri‘¨=V™[ª‡mÄÇí†f£ €DÓ j‚OJÛ…?ö `^*Æ’@ïÝË£#*•¾Q ³¬qÄ{ûî»çŸ>€#ï½ï}/Î9çœaWm,ÐD¦Š²…!³ ‡Ï{:>ñ¿9pûÌQWGQ6Œ<‘iþ³ eñÄéºüßÂH‰MÞN!iMÁ5[ð KPNjÂØ]hN]‚æôsaí³=j@œšÍ§à«—'ÀÌ%lµÑVQ”Zèì¼ÉÜE Ö¹  ï—Æ‘ë®»/~ñ‹ñ±} KKK JÇ<‡Æí·ßŽ«®º o|ãqýõ׸¦£AGgв•á%Ì]ÆôñM´©lIÈcÀ|Ÿ:JÛ䬳„o´àš-$í&Lâ EY D-»nöøF Ãáqì'·ÿvþ üèÝ;AËÿ‘zÔé»LQ† ‘Q GO›E£3‹¹C-´ŽÎ|âä;+Cãå/9>ûÙÏâU¯zŒ10Æ`zzNÏãô]ïz.»ì²Ört¨ÑCQ¶0ÞÏ£õèßâ%Ö‚÷Ç WUæÉLbTEèËw7æ"¥²!ô¹èæÊ>¶ÛùôuŦgÌhv B#!x4L9\%ö·Mz*06g=–f»šhvMv\À&éN&‰» ç¡7¦›>K‡Î˜ÆÌ ƒFÇ÷zqµ¦s´÷‘F=×s.™©%fãCø”ñ"ï š06m3Ds8tá‹ð½ Žãô·áôc»aà&:Ùm·ó}´¿õÇXÉBP¨ðö€@e0{ÀŸÀžïÊ ýÍ, F==Æ›o¾—^z)n¸áÜwß}H’pñÅãÆoÄË^ö²×pt¨ÑCQ¶2Ü`^Èr äE9~YQÆ‚š1ÊýRuáÿc²´äÓüŸ{©.ÎþÞþÂèáÍê“›{À{4;@·E8t8÷ˆFŽ*Õ0»¾+=#뮇bÛÁ´æ¿-ç{™(|‘„` D©)вa8vå0Œ›ñ,à—p²03“IËa–5Î\sÍ5¸æškðÀàñÇÇgœ3ÏÔw5z(Ê–Ä¥ &T#‡²5!ŸznƒÆ~Œï%35Øyx+í.5*ØÁ&KØõ0Й›ÃÒœ¾V•SÁN|Íã'à³P†PrB`v©×Y0üÄ'+T”¡Ã ê>J,Ëì‚}<Ѷ2œ{î¹8÷Üsñ•¯|<ðN?ýtœwÞy£®ÖÈÐÑÙD`pÊ9k¥LúÃb=Ë¡;¬4pqÔ¬‘¼G_†õ`漤_{V!¸ZKçÁ'€HL:Ã! 넌ò$d­gö6ÆâàXAB®pó0Ì…© ޏs2œ˜eŸ¤A™h{¨¼ßâ¹ÇïG:+ºÁÏ.µ@ƒZ•âX__n’È%½ý3 “4 U6×èݹðÖ†Š-yrSht€3¿7 טî á"Œ&ý7;€÷€  l9Ë~kq¾×‚IuŠðúÓ4ò6JÔ»fì—â׋‹¸²ŠŒËž…Õo/DIhÓ%cjÿÊHe/ª‚3]žöK±>ÃDEíÜ —ƒ¨)ìW¿Ï%?I!Ƙ™Ò¹ç D-X„÷„*!ÉêXíh¿ÁfÞO îPåéWó]°®°ú8ß÷žêm#ªÍHçQSiFº)R›Z„ç&-Cz>âÏ¿1»¢*à%xá#%”®¡÷EÕÊ\T­Œ¹#öíʘ!=&®ºX°sx¤†È°íýéçå(xík_‹?ÿó?XþÅ/~‡Â#<‚Gy7Ýt.ºè¢Ôp´h«œd"ƒ½*)Ô-ÃV?¿~úY”8ØsQC ÁR~að3:Ç‹ N+?ûÚBüÃÓ¢Üf³›0 \$éMw ­~Ã|…ñ!¶|­í£N Ëss†q |öê2‰ƒ'Ø>0]…WÙ $=e˜¬©åa0a2R6$&'åþöæ=ìÊòà€2o¥¶¬¦Vų)µwéZu„y¡0d£ $}Y7ÿùފχôÑjA¢×CÍZIšv ïñ9#¿a[‚ȲÁcUe Æq†/×z;xQtl=J‚ö†·¬§3{½¾²êù¢÷̼T„²Í\ÔèÁÎ I:×ì§$éݱm;ʪIÛtdŒS¼ëNfÔ²¥s5µa1̲Ƒï~÷»ÑåoxÊ¿=Š«¯¾Ÿüä'‡U­±AEQEQEQe“’KÔV±cÇ<öØcC¨Íø¡žвÕfHÃ%#¼¥0„ÞßD³½51ŒEY„ûAì Çr L¯Ð*&žØ˜Â#ƒ ÉHÙ’V| `ÄJET|p›éžÑC¥~7˜°]†®ÿ®ˆ«§À¨gÂ<\IA#ÏÃÁp ÁE¼¤ðÂ¥¿{FÕ}T…õ*oL/ÞÙ7§ámúÚmuë£7ñäy•¼Nv¢åikÁ©¢DÁ¤…®*ÊPÉŸ¯Õ¿¸T½eãH’<ðî¼óNÜqÇÅòW¿úÕâ>ÌŒ½{÷âŸÿùŸ‡PÃñCŠ¢(Š¢(Š¢(в ¸ôÒKq饗¾õ­oá¯x–——ñ+¿ò+Ñí18çœsðÓ?ýÓêé¡l]Œƒ±‘ îUª qêOWÖMà(ªžHÙ쩉ڙÕ!d—êD¾v5QÞ‹¬pÝmíÙ«Ð5zÀ£#ú²–ä¦ÉÜ@L0èP,Û;YPDÕ%-CPŽ@ÑD[°àÚm7¤—œ-UKÈ’br'˜¹w jGöíÔ{Yˬ¤º$ÜtÖ8¢`Úb{ãXâCÔÏü/SÕ¦…~=Ègç؃½)¼2ÒP—žªK+¸5&r ½;LÉ»£ÒBWQ ’È´Ø!ôRéÇv`ò}Øõ<ˆÈ‚xf©Wá%ÀÜSl¡塵»Ÿ0\4Qcªno£bÛ­ Å[Åu·6±gÔ±$å˜*ä$ÎñdÊL‹ðA˜F±=5E ö+ñådÁ‘çýrüú²¯è3êAÔ¬­ˆ"Kì¿Wy¿KÞŒ¡úÍ*ž­5œC©ï ïa¸ Z½uœ½còû[ª¸j)'î^¯pU2;z‡ Úa¡$ÈÖO¬%T¶nÒ)ÞUmßUsÖ«Œ4™øÆ¦O\Ëy”WÊííäç¶Ûx²ôúßÊ©ð#?ò#ø‹¿ø ¼å-oÁþᎺ:c‹=&CÓ0ѵõ¡þàiýŒ$’œQ{8Rv5q⇼p0eÉÔU\;fa`,ª¡Ô‰?`UâNT:•Ø‚I¨—t­Hø`§&bJ´ZÃQ)ŸÐSÙŒHì£ÿµò£ÚðWÛUù Æ®{0Bȇp2âóȰ–©×SºIÕ2‚ç7Të!FcP´˜¤÷B¾÷ÚËeiÀ$Áq½ %LüJ2ÁÔ/K4×ÔÆ?nãÄ kPbÈêEÜûðbt2CbV$n?X/Äï-ÙxÛeYÒYj#•ýRmå±zF’*IÞÚÆÄeX½[DL˜ÈÂS|’ÂK×ÐEŒÊ¾[ÑWKÆÍºOq áõ„¨¾ñ?¤daïzùøÒ»žh:ø`À vñl§§-°; æå>µ±µ(«ÔK Û۞уIRö¶x¹• ìåçsCŒªd¨×¥l2µ õÛº¯=>¨m$YÃ3Žch@ºÞ„aôºgmÛ0ÁðÃ[†XÖ¸ñìg??ÿó??êjŒ5jô˜Þ}ïŸÁÁï½ ÏÝûœQWGQEQe¢àéóðŧc Ü*) IDATïw¿Zú÷QWGQÖ/üî:øy$>9ùÆÊºb­ÅoþæoÂ{ùùyìܹ³´þþûï3ãüóÏQ G=&€ë.øohÛóô_„ÙçML]¯EÙÊûÂóƒÉÉDÙ£œ€ÔÄCZBOþuÅ6DàlÿU{€Œ#dQND„p_ÜÃÚ^ü²óÇ‚kW =ÈQE-Ì:…â ¶%^hd …¤1¯¼ð{xታ1÷ÐYèdÌÓqÈ¡%ïŽ Ywè…¥Êo[ŠJo’˜çÆ*ïÿóö½ÏÛ÷BíÁÿõï°ÆÚ)k塇 _øB<üðÃøË¿üË’çÇôô4þàþÀ7Þ8ÂZŽŽ 4S”±# #É›°¾Á`‹Ÿ¢L,œ†Ñï?ãü’Þ/$Wiéý|aØÈÿfcàZ®eá¶X>P 2Åol ˜]šÛcäQ3‹ßÀ`Ÿ{aXŒ ·}e a#¿úp–ª˜€ÚRNÒ2 Ð4ˆ=^xûÙ˜{ô;€™ËB "uÉÂ`*óy¬#>y¼ø±?ÒûñRé™(«¢¬ò4ôß$óÎw¾çœsœs8|øpiÝé§ŸŽ|àØ·o>üᨆ£eŒGgŠ¢(Š¢(вù±Ë1ûÄã€_îGTfWQ”uâÛßþ6>õ©Oáî»ïÆk_ûÚè6o|ãññ|È54¼eR©zÑn67ÆXbÒÕ{‡<++'›ªJì(du7¡[vèš½(–¿~³˜u“+Æa8ÙÍRj‡ä„Ä`©Wª(Ÿ1U¸#”!%生™úIÉÖ1ÜZf0×T~>c-}»è#Í Ó{¦Vãe aI—÷BWL’ôBeŒg‰PMâÊ^¡ÂKP4Uœ‡œtVÚGPc©œŽ«Oɧ8nU²Ô Ä$§±gPîw†áYQJdiÚ@¦”Ã~9^¾øôU(ÄÙ6ºŽ)Þ7¤Ê:5“ƒ³+ÄTÍÌ‹õ­Wô0`v§”,µÜÖOXºªí…çÙõûeP'Þ„Æå}Þ²"Yý~ZJf.…͆ÉKËË¥çc-ª.ÊÈYõ;?’@šâc¸V¬Qâ8çÐjµðÌg>S܆ˆ°¸('leÔè1µ'¤öòË{ Ýe™@”¤7˃žþÁ ¯uà_C³µ¶/©Ð”î£ ¤SËrÞl(©$%ȃlQ…&Z¤¥ØÀ˜Ð’•Py)ºÜqGPˆ©RƆ¹Læn­¢¤ðœ$£|mÃ^ÍÁtÕ9Ô5 H’Î%qÚàðdÈ?æå™&¨´„—ÀvË”y¾Û ¤]RH䚤ƒÆrï¹a…ØÃ¸x…‹·«ôübê&ìcwà@¢ÑƒÌ\¼Œ*òþƒ—5×'SÊíJ íè·±çñ¹@q5–*•Yþ¶w~Öì)ÞŽû|ð×ëWâ÷"݆½ †âeA¦7†Òâé±âϲ÷¶âÙ ¾X½¼ýPU~ê½·+Õ¼DCûjÔ¦"ù<€ìÙÎÚ/¡Ü¿æåÙò­”$PˆYMxI¸&Nl£¾Òˆi£Ü­xn•Í@øü¯NÒY;É¸Òøô7,†YÖ8râĉ“n³¼¼ŒÇ|µ?Ô§LÅ 'øÅdHEQEÙBͤà{?EQ”­ÀSžò¼ãï¨Üæw~çwð´§=mH5/ÔÓC™<²AOþ7€¥ EQ”TÓcil`dnöÌÃñlÂYÍЫO“–N8¹÷¤?±mÁaíª+“ó,*ãA}cŸæžÙ ¼éMoÂsžóÜyçxõ«_g>󙨵kŽ=Š/}éKxÏ{Þƒ/ùËøÂ¾0ꪎ5z(Á¦ahež&y~øîwF]½5bA’ µ¢lu—ô<ä‚yõº°Þƒ÷¾÷½¸ôÒKGP»Ñ£~}ÊÄAhÌ,Èl5ž²; VlEQE™Z'N`Û±(ôþTEÙÄüò/ÿ2îºë.üÌÏü šÍ&˜Æ¼ä%/Áç>÷9\{íµ£®âÈPOe²(òxL§‰»€d^=%EQe«Ã®PÎ1G?÷ÌÂù#ê¤(Ê–á’K.ÁßüÍ߀™qðàAìÙ³Öêä®=&€Fë©hØ™Ò2öó'ÏTÞ¿^p³tî ¡d!Û:wëgO¯ –yšLÆìÊ_»¥´zÝ{‹å¢²J…R@Lލc¶Ÿ¼â¥c ²”¡\žáWõþƒÕ¼±“|Âýpu3·sWVV©§TB¦-¯sñ{HW* o ÌK²Jƒ@ÝíŠlê½ e[ˬN ¹´JT{ “ýÊr‹aÙ$>;’;µ z­¸”&µÀ6PÇÉ]q+kL·dŠ-Þ6à­Eg¦6›xL_“ÉBW˜LüxdJå…ÛÛkW(¼Páa^ ߯ 5%++ú+ç~8¸y…*T†¡vT2×r,õ}R_RSŠ”ý²¬V"ôcÝ•k•aì ÈÌD×IrùŒÃWÆ 4ÁuðUÝRŸ!lîºGêmâªYUáÚV©SÅT°ª”|ÂöV’,ÎTÏ>a>«Ó.øÞ–TŒ„èv7_“°Ý›Ø îC\!õê…~Á=öyä©ñË2S)DEV=ªˆ÷*þî4RUJL5Ç uÛ!¡½‡ '^Cil a¨Y[ ÙKeKŠVÔ†©Y¯ðžšŽ¾÷Ë¡-.xg¹L";Uþò¨®eý÷©Š1@C´'ÒVŒÏ<ˆû÷ïu5ÆõçSEQ&6+3+3„•[HØFŒ“ÄÔÛÓû)Š¢(Ê&ƒ¨2Ó »0;3'N^(ãËÊÊ þå_þŸüä'‹e_ÿú×qäH=£öVCGgŠ¢(ÊDBÞƒ áá'/àg-byðÖ‚mcU û=@D¯EQEwh0sàæ. ±°Ûƒä¦¾øà?ˆ3Î8—_~9®»îºbùòò2^óš×àCúÐk7Ztt6±ú"Ï… :·ìïp]HÙîÇ2}¿föK“µiÂ6eMH}ÁBìK?øàxbä*-{Î`çá6lsºzF ßh?¶Ò¯3» K»vb~ÿnºé&ìÝ»·XwÙe—áŸøî¹çÜqÇ#¬åèМÊyl×Í ¡Œ!úRR&ˆÜ[ヶ¶s<óV´ZAòc''#WFÊÂÂBaðhµZXZ’ÆoqÔè1 ðÀeë'QȲ˜ƒZ½DE¹û²ìîAÖæ’ÂHð·•²oûÑÌ߯ÞK¦p¬ŠYѪuuÊg^ÙÕsMeÎ2W¯z—5ÈåUe*OÂõÌÍâC.-/WŽðQõ†Çx&®Rš‰Ï4yÄ?D­ Þ8ñÜ×î¼H^YÒ¤; ª‘n¢‡ Î{µí¢öÀˆ¦…åÑ{[ñ\’¤Z‘SW`2 ä’RØ‹7 go¶ßhû”/j£ãpÉvüNØÎ2ˆ(y”êA¶Ï%ïw(Òÿpeß#õW÷‘T$õž5†Eô9díÓþHR‚‘”’:ñ~±Ê½_|hëÎ@›xÿ*œ_¾OˆoJ2°‚º‰sÇk•1¶ˆÊ²"‘¨Ì%,—ÔÈÌFûp2ÛAvG¼|iÌ ½ç¹S#…û“¨æ%´Ÿ\ñÅ4A¾“…çÊq™š[è™V5 ®oá•AV¼VìëgúŸÊ}œp^ê“¤çÆ€ÅuÒx©â}9VÞ3¨ TRÍëcÒÆâ5!W+,ÂÒcÇ’ÆÔ‚*YïÀv—KÊàN Ô Ô Ãëd{cÍm5vîܹªí:´Á5OÔè10ûÁåù:€^¬˜íàæv€}OFÍ-蔾ŸJpˆ:˜Z èÇ4 Ë-J'±~Veù%°>0\Å໦ToåöÒK΃¨Ý“VãI/ùÕ$5“Û¨ˆx?N-FiPÎaßÕ[ïû^‡y S2tpŸ: y†í8—ñ »‚r÷f Þ³íÉgFMà‹žÂ®–4 Ãɲr¹ñ2¬øQ ×·Ußõe4%CSޝ)½¹Ù¨VƒX}?JU.÷’áOø@,ƒV[')ôdày â»H6¦¯l?€ùýmì»ßºGÑ›ˆÊÛÐúˆ˜¤&É œ®~ñÃD6"ï•*cóþëJßV›ÁÕ¡*L&{N»Á¤gndë‚syù“¼ŸÈc¸’µã74*Þ{Üwß}8ÿüó̃ð{ßû^œsÎ9îÚX0I£tepcÝ™ÝH¦wceûièl;=Íàl„Xe,1vló)°ÓaìÚ/KEQEQF ;Ü{ ãkÏ}‹{N`z¸ë‚Ž ”‡•gâ?ž7ƒ¥ûßI β)¸îºëðâ¿ûØÇ°´´Ê&l>ŒÛo¿W]uÞøÆ7âúë¯qMGƒzzLy(CÅË-ëП{9g g~oÙÁyÿN`Ó¹>‹¸h%VÆö'Àî1Üö†§á§ßç÷(€Þ¬Mý E fþ 4“öhØÔ¦ÀÄ¥gû½¥([ž5x¹Ipç|§Vç4Ly"ó4Êû—xè }²í[x ÛÔí§ - ÂB‚°ÙMÛçOÁ}b^îyÃø%Ø•eL/nÃÔñc€›Ï<=–{ÞAøOÙëWÇ•£æå/9>ûÙÏâU¯zŒ10Æ`zzNï~¿ë]ïÂe—]6ÂZŽ5zL2E§å{oïxì€ØþØL-nàƒj9C³2>0$Y³‹/¾7Þx#^ö²—¸†£CJ‰Æñû°g!ÍÑXz<]¨³“›æexî‡ÿG–tPQEQ”MEn8“F®%¼%7xd [çϺ¾AØùà}ÀâãëXaeSÃK`äyo²‰Qöê¼É¸æškpÍ5×àÀã?Ž3Î8gžy樫5rÔè1¤I§(œ <7’Š­"Ñ™“‡ºIÂσ0]'u¬²…YÂö Ò 5YP à’'*ŽU³øSHºX¢ÿ^PÏ5µ*OGtÝ^\RÂR3ÐKx°¯)å)&8T9P¥ßÇû¸BªP3Û¼ÐF<¯uŠClê'Õ•Ô¸m‹LY¢âª ŸàÙ¶³Hföã[—MÁx“¿i0óÃ{³çKHÎ'&ŠŒeÒ`Zñó°í’RJÉC£ø»Q 1 ÿf ÂXb_,'ï{ë¼G˜ ‹¼Gg¦6„Ö¢y_NhZªlŸ nø7U©æÄWéE¹·DM³mpSxǯ»”á¿REHl-!&ôdíªú0éÙl6Ov¨P<ˆ=SìÁ¶¦ŠÍFåÝ18w8¶G¼®Œ ê"ãk'Â5¶^DÍJ%±Õu\g0#”aa„>£¤ &í4ÛíÛ”£ê*‡ô+/³¤zWá Áq%˜žbFH~ŒØyØ>%¨|œ—õgd»ÿyñ ¬´»xîã»aVvÅËvŒ¸´¥øJý‚Øv¥gЉªRÖÊbRÛø{ž¨ Ôf˜êz/V¼ ûé´b²²ZígPJÂK.únT  ³C•» äÜsÏŹçž;êjŒ jô˜ˆLÙà íˆë|˜›òQ8@`©#žFt6‚¬8œÙÒ ¨Š|ÀLýŽ+~(QÇø‡¼ôáo¨½®YöåÁ|o€Sú  bƒŸðå·jãDä¥Åðâ}½ùôx5g ÄÁ¡$£Ù­0”HHÒ‰6ÂÀC4ØÕ4­EEHðDÕ&˜ÛàÈÇBú¬¬â>…m¬1‹n{Í.ÁY#Ojbæ‰Vj Ž%õ=F¸¶¦½·lšðt&S E)…›¸²Lm¯¼Šy; ›[ª·]Ú.i$-€|Ž1—ÃÇúÛk©]‡ƒÍšƒÄÚnädæ–¦ÆBa /}ß‘  R ƒìC(C*»$«]Z7†²ÑÃ6öÅë$Öµ+<ϲףµ{¢Ëô‘-äRHó"Ä?öê=ÒãÔûp¬[™úFïkm ®GVEO½ è›°™CO*ºßÛ?1ÔGÕ3noÚ}}@xŸ…6Íqã¤å@d\WT ø»ÿ<,à—qÞn‡7€I™ÝñãûùŠñk!=ƒ’4uºS¤o箨 (VâÒ»•ý’4.9›W©hÓ®m(åK)Quk§4îf‹èI’EÆú !d+»§ÆŒž¼Ï0Ë%Ýnßüæ7qñÅ—–îsŸ«uœv»ýÑÅÜÜàØ`+¢FEÙŒP ÆìBª—îËI©Ô Q"ä–ÑžOýêÈwScAÕŒæ(Rî5Ò>Ñ…oäò¶„dª‰cöafÞaæÐÁtð7B™CEQFwÑ“—‡þ05®sØsß7°ÙlEQÆ—¾ô¥øìg?‹_ýÕ_ÅûßÿþbùOþäOŠ-«eff¿û»¿‹·¼å-ë]ͱCda®qe#¬Ø>÷>Æ`w)\¨Ñ3œ&›µeTXչ߅^bœ>H•MHY‘„’X×çÑBk [#‡ÏÜF˜9šÖmz6K;ÚhtøàÀº_üÅ_Ä…^¸ªã,..âÛßþ6þèþ;vìÀoüÆo¬wUÇ 5zL¹Ñ#Q\¯œãLaÜ(+•1™lwd«GþâÝFÉÌ‚Û瀺Gw¸ˆ‘&r€ ¼>Jq›Ã©¯²Uõ¾S~Ærµ§šñØk!ÏÉñƒ³1³ØÂ‹ gï™3’©&žæÐ^²˜Z¶˜ Ó¸Tx}äá- UfÉCiz .œª¸0£}ìPYªv ŸúŒ¼j„T” e ‡PR›"›†Ñéó<§”ç£" ¸nä8(¿_¹ô£0xä (Ã5©—GFcaÂñ Þ2r"ãÔ4‡L/¼º2lF9äÓß0Ë›>õ©Oá¶Ûnë^õªÒò‹.ºýèGkïÑGÅÏýÜÏ©ÑCQ”ñƒšgãÐ9ga×Ãm˜Å¥ÔØÁ®"þVQ†DǾñƒ}¶ ¸fûoÃ&@ÒJ 6ñ°Ý.¼MÍS+Æ’ xãå‘Ü»$ÿ?“)>E™Æ)¤%F9IèddAP”­Ã…^õæø¥_ú¥ÚǺï¾ûpàÀlÛ&$ÐÝB¨Ñcˆ©°‹«°C4ñY¥gHÕ‹=V“»'–2÷䥵x¦„ÞyRÓ’;»+OÎÔŒme8!i×Ú¼hbŠ+i‘åÉAÌÊœ™YðKiÆóã3ôÅù²¯}î£DÊYR¥f##%Eõ²JK: 1±£¨ŒÑ&–djõeþW†3¥'«PîN~2—òUR‘ –A;ˋ蛮aÐÂP‚É›{ÿ±„d¦bèKU5 H^XžVXHde-ýnÅ># k[K¹ãúÁXvâó}n«òáÔNÔ¼E¨PÙ“¨–‡ Keê©[±i1%¨ðšsÿ{!¤þ…`zïj ]«Õ^jÔm’Ã¥þŠ]Erge\ø½ßû½ZÛßu×]xÁ ^€sÎ9çŸþÕj|P£Ç@´ ¤ïã·ŽIueëWX JºeD%Õ˜øöd¦{i*(|÷çô8yg*I¥nœƒ3Î Wv»§zå• I¥€ñb$‰T_qàÆA8NPû#˜:üŸàÖøö~,íÜ“xLú.8ùAo;î pu 92 ®t›14†Hê-”©Ä>¤A¡þGïH G ÍCPŽa¿µ* Öìþ'äßá>u‹h{7âGL]ãfªØB˜žï"iY¬Ì˜ÌðA°Iï\f烆!9¼-¿{á+¾ç½Q¥öæI‚…ýnñ~¦âz™ü9*w áøÐÕ±Ö2Ȱ{ˆj!âG]=%(.…ô×Iº?Ò´±ãKö¢ûÔS|b¸x¿T$¹ìß^Vo©«’2¶¬—ñ†L)Ô¡¼*诂~…›» ã›&¼ žûR¾žìùg“„¡.ýJ(Öõþ–»âFæ¼—Bmë7û·ï…Ð…Ï,s¯â䉸¡Äw­U²ªç º=¬XÎÀvÅ,ˆæÒP»/kpç;ñE…¨qý`_ë$ccIý1–ÀVo¢*TYf<Š ‡a0̲¶ssshµZ8räÞúÖ·Žº:Ž=&€…Ó.Fûà‚Ý1Œ‹U6Ï)ÂŒžU™ºòz“ s삺éLÕ9|å§þ ?õÑçáÀ¿}mÔÕSEQ”ÍC…AS'BjÁOŸ•m»áÓ FE™0¼÷øë¿þküÕ_ýzè!ìÛ·W^y%®¹æ´Û½‰”§?ýéøáˆééiX»õs=ªÑcø³Ï¼ ×Åóöý<ÿÀOº:_ZDy²­c6­£x àeP²€³î{ºÍgbvžEoEY_ògg|fŸ¥Yž<Ùèø²1ÊUŠ2±”¼ÆÂ„¥ƒÞÒÿO~Ü>ŠñÄ8å󈄷n¥Död±2×ÄÎîâ¢A! eÈÜuðNÜõÄødÔU™X~øa\uÕUøÊW¾²ÿíßþ-n¾ùfÜzë­¥P–¹¹¹QTs$¨Ñcø?.ú0e’²‹ß¨_zÔ*ê@Üû¥<¦ `cê8¾.È.´D-õ,´ÌHÜalxÏ8¸I›aZõ¶Yþ*|îÆÊ¡{kk`Ó~”aÂA;!î ÷|vQ„´D6b_4lâFó¸FïƬÂ膧°é)¶”ÂV¼/…¸„Ò¸%…!Ž¢(C¢±«ø“ƒ\®9Wxu„!mÞZø†…I2%´01qñl7àíT±q½Ð,“,öžuÈáC Ö÷õI›jTÂy@ªTG'¾ßÃΦÇ1wâxÀs÷ýŽvŽâíÿñªzËYXXÀW\fÆ›Þô&œ}öÙh6›8xð î¿ÿ~üÝßý®¼òJÜ}÷Ý‘¸´5zL¼Œ±ºÕÔOíGwzZǸuJUÊjÉòq¸Ã0+@+Yu…EQeSÁÆÀ7Ò_.{J_+cB&É›š¤ˆO€]ðêáª(ú§Šg<ãøÈG>ЍUxïqýõ×ãOþäOðû¿ÿû#¨áhÑþ|`tÆk¶Ñ´qäÌÓñw×>€¤½7KÔ·…Ü-‡@9_ìO€“Ç€ÎC#«“¢(Š¢lFؼ!¸†AÒ²HZ65‚L@œûfƒáÒ¼fÜ…÷ ðþ¼{|ÔÕR”‘së­·â=ïyOÔàÆüñÿ1>ýéO¹fãÁMÿ+ûp_úx¢é"™hyÅt\uE’.«,8t˰Øùƒƒxùÿ»À pc€ü²œ‚N 2Pû`Zˆ¬qÑLÓ`ÌöxÑÔBì‚R©›pÖÀl¸ò€¯ð Ï/OB0XjïdãjnŽµÆ¾xµ$ ×)£{ÞŠ® ÛgÉ{Úl†²Ý ØÈ3E¾1/_º’úõÂPlÇÃ$™t¡!$­ø±\³þŒ`ˆ —Àtå*I:±JÆ7vßÙ.ÖÔúDŸ¹~,oÁH \ìçëmÏQ)ARì°6þ|HØÆ“¢ * />;¶±?ü_P¥9„I{¡lR(? IDATÙDA)4 }wx*]ôÊ[YþF­ó`î‚}=åWsû´Œzï¨P¶u5X4£ªOU’ßÍÖÞèò0|³T%šETŒˆæ`šgE÷ñÍžë¶oôžyo©W‡µp- ×0©˜•!tÚ@·Å° ¡‘㙩X“¤/]6„ÙÃó —¤a.¡ZœŸ/žƒÒ;‰;˜Á‰Ï÷ǣ˫¨ûna1¼ÞG"#(sÉ4ìiµ¶gt䶪ñ×SRò‘êJ@!w;ˆp~Òæ’¬²4¦ ¾ JÂå{㵄st¢±±ñgP”ýå%°¿‹ª0žW^º^ ³¬qdjj ;wî¬ÜÆZ‹Fc2?ÿ'ó¬•ÑÂä–A¾[Z¦Ä©<*Š¢(ë-Ä•øÊ¦† à„n 8¼·6Œé…Ôˆ³í˜‚õ oRs7¡/ƒÓqŒ¢(£cyyuFî••zô­‚~M)ÃÇ/ƒºó€[êý4U5£N<«(Š2)MgNÍtæ’þ$Ï0eó@ÞÞZ¹!Ã5€N›ql÷í›Ç¡ý'°4ÛE·•Eú@6£ì=ˆe#E9)dвFÎ:ë,|ò“Ÿ¬ÜæŽ;îÀ“žô¤!Õh¼POeHòŒ0p`IS³'\†µ‹ÿ3e!ìÁðñp@,l%t¿VÛ¸ÒîI§Ù•åôƒá-’JžˆH•[Vfzël ž×"R”­ ýnêé;›{{!ìÒ¶ç» ß¼¶£»cKèIj‚çß7ZÈÓ“ú¡ÓN½<||ßKîŽìÅy_»Ûî‡MÒ–<¬¥µØ)”]l÷ÀÄ®¯-„ala¨¤¶—‰£?¤±äAÖ̼ȪÚEØWiûQª¹þúëñÒ—¾ïxÇ;ðŠW¼{öì)Ö=ðÀøøÇ?Žo¼ÿøÿ8ÂZŽ5z(£!Œ—VD–žü ¸ëòÇpùŸ%•öÕ—ž²…a!ù–¢¬/ƒý(“-Œk·þúAÌÙ†—ü÷¦66+¥\&:Þèyz¤FŒ…¹%òð»ž@såi° HsxC0žA̽?Qƒ‡¢¬ßÚ ×šEcépj\Ý‚íˆxÈ’µ“Ò—]vÞö¶·áõ¯=^ÿú×cjj ­V ‹‹‹p.m_7ÜpžóœçŒ¸¦£AÃ[”Ñ‘tÐ â ¡½4 KN¦×IQeCYÞ¶„å¹%Àw³>7“ÉÔþwË‘4R/o€ÖJ»ÿçÓqÖç/Ãì‰6’£ÛJ #y‚Ä<<†|fðÈÑI¥¦…Å=ûðÅ—®`yçiÞ¢¬oxÃpÛm·áÏx–——1??$Ið”§<ùÈGð–·¼eÔUêé1xžÊIk wÀ1ųó&¨ìIë„ΛZÑlï`iŸŠ…Òu⹪bI<ã:Ë 1’§1Ë‚„ÛÌÞw žw Î ’»ü‡×°·˜}{-^"ëôcá)žQˆš¢zCî'@èÍì,@„fV^.Ä$%%†  ij¤KL¥‚K-*®¹Ò$µ·õDl»è¼úLþlšp­ÙèºÎL+Ø®wë•Æä‡ë˜lÊ(é߀MzÿO¦âê•n鸥òbaiÒr Þ¿'éǤN+^[A@XNÉ<¨®ê©·¬'”»~¯v{¸¦£ÉI©ó8l7íg_uÃ,À+€?š*nøcHÝ’ZÄ0ž»ÍJÚKmZPºÈúêåÔŒ*sP•"Xÿ³™õçÞ6àšM°!¸FêåáŒnËcöø4æ÷̃†§´O±tßþ¾§W›†@mµ½;+Þ[Ò;J3á$´ª¯}„˜úNЬ¬#‹4îÛQ«N@½÷|ZÆÜÉ7*íÐòsŒlš ÏXܶˆ»÷bêøL‘†*Äñò±T©Þyþ¡~ؤN_T´‰^ØÇT ဈª‹†_箸â \qÅxä‘GðÐCaÿþý8ï¼óF]µ‘£F ¨•; ¦&À‘—Y•äàz²ªÁl\f´|aW¶Qù[® «_~õ:ïôXª—ô²åAZ6ðóþxµ¡¤.ÂGš(¹žT>¢TÕ5:+/ë Êl6hnØíXxÊ/âÄNƒÓï¹náâEÇk44ÆsÀ°Nƒo²ð‚\šoPðw¹M1$üac⊠@iyn‰o1z°¤åâj¶u‰*cH•e7º}S”5)ëu­Nö'zÆY,XÞ“jô¶Sϵ CÈ@’¹¯]FpŸÂ^!7¦²¡"))z{&˜Ä€¼M È÷úâo†BîxÛÁƒ¨ò}.öÕUG¬˜ ª³}•Œ·t|±¾5åÈÙ¡vDeݶŽÈ–&§<Ë?¾®¹”®CÕ¤\ßaÉö d}OPE}¥vJw¯b9quWÉÚárÓM7áÍo~3®½öZ¼ç=ïÁgœ3Î8cÔÕÔè¡(Ê–‡`aÌv{ß¿ ƒ£»OàI÷]FEQeëÁD™Á#0°Æ÷Ÿú,m_À…_;Ó M´— Ú‹e/1EY7؃|¶ãa;™§„†Ï)§ÈÍ7ß çŽ?>ꪌ%jôP”1€áʳûy‹Î$®‰~O †ó2ØÁÓÿå! 9×½DµS$„gáGIúw:“EîäÏy.)¶”¼;&|&H `WšÍ.fN¹¸ü» vÚ'oJá»±ê¬hvΜöà~,Í®f'5x4:®^çzKXõ«pDÿÖ¶£ôH†Ò~†É ^ñ6ÃýíKs€+Î=÷\|ík_î]»*·{Ýë^‡÷½ï}CªÕø ‰LeÜéOøª³5I¯sÞσWî…ëÞ_¸¬+ãy.~ÞPñ;UØô~ŠR"Ò¿2/ƒùDö[JC>ykª+L LL¦¢B>•³¶Ž°ýh{߆™ƒF’.ïõGaòÒÞdz?m3ÏÉ”²ë©·lµ>æÿgïÝcm»ª3Ïo̹ö>çܧ}ñl Ç@aw Iñ¸Õ¼R…ÁQœ" ‚ˆnD¬È ]V¢ˆ7-R'j©¤B`è&¼*1Ý-ªB)tZØ´c7ØE;ØqÀöůëû8çž}ö^sŽþc®Ç\kϱÎ^çîó?éØûî½s=ç\cñ}E‰ØÖüÍ»*{·ñ‡ø‡øÐ‡>„<Ï;§ûÎw¾³E-ÚYh¦‡¢({ÆìÏ‚`àý™ð`³ß{GEQ”}H©ñ“=ƒÅs„AFð…ÞG™q¶ßõ”M¦àØke˹øâ‹ñüç?/ùËqüøq\sÍ58zô((¯yâ‰'ðàƒnc+· zìد€[ Îiñ(ŠÔégÌ‘ô÷°é”»®hv#Þ‘žZ εDWË嚥H¨5JAÐxóÒHŒE°b7‡ÔÛšà2­˜Ípb •ƒœ]L›–ÎÙ'Û´.)e|¤Ü&ºÞ¯³?×s½ƒ¤Ú;Á$gÀfW€yÏ+-‘×örÊýÐ2-Ž£ÜW® ‹¾ß¾¤bd7‚•{ õï ÐÜi"¼HŸ‡À¾ß5»%"‘°ßk™¶2<ÊÎ &/öƒ•—×âÇB¦RæH¨õO_Ÿ~‘Ë>¼=N!ê+ ûcjúxºH¬.žŸM}þtŠªÆË”„L%¬ž ÀD€;û›çÙ Ä\Gý}C‹’Òûyœ¼¦‚iÚZ¡ï¾ ã6 É•làþcúµËä²!ÎÑsú.lRÄ9¸…$Ÿh(ŠWrv úì³ÈªüS!°hs†ñ€ñÁÍ…ü´ãSc•ѵF.:Gâ±C†P\[ë‹ßöuVé¦ßØ@\7|R0ÉÉ×ZÏ{~èÿËyÚí–ÄOãñY¼ß;N)–¥H”Zg‹ö2ç[tîÇý]<Ö÷íøúe?=}rßÅßI¶Ù.}ík ¢¯’«‹àº¨aÁÅoüÆoàôéÓ`f<ðÀÕ÷qЃ™ÿÞOhÐcày¾5(&·–@±_é°Kw@™0Hba¦oÄ]IÒ`¿±ÂÖC,¦—Û®ƒœ!èA~U°&¦Ç ì!í+ц•‘Üöú=µ¬¾AéKRÒï¼ôµ«ƒD Éylö¼P‚âŸØWÛŸSá»î¶„ Gº&_ÞöAÿm–%= M]dÂàÞ,&íC,=Ðô½Ý^æùFIºnmò!_ €‹‚®Ñ;™*è‘S,ePƒ|m[K ÛÚú³ËÒëgcÀ’;CÊ¥ÒðóqaéÞGñùßûâÏ `tOkCÈ ç´E»î4G‰naBÿѵ(é¸|Bª—5¬i›¿Íça/,«ß­t¯”g0½]¾Èô[‡dG.¹lu.«pÚšnÓ"(eÉICHA—8Ðámz¨t= go@¾¶¼.Kì¨$Ç B°9”DMŸsÜœ”N»Î)+­¢÷}ß%j‰'IûÒØ>µ¬šØ±%¾÷µç^|‰Žö:âym}ŒõWÖË® 4A|~šØb½c¼°žc`[¦¯ÃàF\Å G—Ýzª]>Ý^*³¦XtUÛ ¶r];‘K/½/zÑ‹pÓM7!\òNŸ>[n¹e‹[¶3Р‡¢(;“ƒ/‡= ·ö,¿Cm\ç @4D6¸2Xd²êÌJéݧ;Å ’Í`¶¬EQ%Peu„À›a­¡¡({ˆK.¹øÀðæ7¿¹sºÏþó[Ô¢…=”]AüÆØgª·Å6_|…o«_ÇÙ!‘O:IÑu¿-ÙåC‡v’[ŸûeáÇâ-oäíÕÎ$¼4Þ·9£qµA»ò ,Kq™©!à~3ž‘}#Û£=ïùÒ(ÙÌØC* Øf€Ëg|áF ’(ûoãñC« 2Ê+Ÿâ “û†c‹^OóBÈÊH!•æ±ke­ÿb„í"òÅ ðý×2žûØa\ñýÖGʼ`ÛQ Ö(KL•Êurû³Ø@™7ûØÇðâ¿xÝéþìÏþl Z³óР‡²;ˆ:ÉÒÁêáƒV<¨J£›4;©£Š-¸žfÚ.¬ ”Ô¡Ô°7ì· J?ÜÚ}Eú·K§ñL—»ì0„@SÝvªRoãúæýG\†Â>¶¬­§ %0T}ï2`2‚Ë –– ç|#ðq¾Y I­4Kuæö0DQY»PÚeðƒ%<ýsGqødŽÅÓ§;æoêTm$«ke_â†Y²ì-N½o_ÚÔš</WÊß\Oý$@³´§¨˜Eû‰†€-4]Ø.zqÕ ­×6Ý÷²]ÄÊsá±+Äê5\ñ}iñ}4ý½Ë–€r,£ÙU÷Z¦t™WÛ¢¶± ¡I»-oÙZ^ýêWÏ4Ýk_ûÚMnÉÎDƒŠ¢ìH¼?>´&7"æ·Ãa8Àà'?Ýî¦ì*\¬-2VçxäEÃäÿü‡—c8ÚKÃÆPÖ²rì¾þö¿Å«þÏ_ 5Þ>+Š¢(.Dxf| GN8¼âÎãØÏ<€'·»qвa¼÷øÊW¾‚‡z/~ñ‹ñk¿ökûV¤t4è±H)8“M ^1ù¤p–èÄ@EtÉlb7»„Ni!Sb®JQ˜L­N?«h ‚“RDû,ÚövÄ?µ}d¶& Dz³,ÒŽ= Ÿál Ãn²¸k¿7ç,¸P0¼¸¯:mk%±ÝM!Æ1/°¢£ÍÎDRˆw‰ì)ìA>½ßà˜—§Ä—¯>_þÈ¥XXË0†`G9;l¨‘]R7ÊTímg|H¢¬’KKï€]´œ¥Óçðºÿô/pÉ#k0y‡h]kŸ—×QòXìpR™[$ ç"éz^%Õ¡á<0éþƒl‘lñ­¿$¢Øq?Žî1í,Žøsym“/Æ&AEry$zé›.-^Èe'ˆ–J}TǵÙûža:ÅL{;Œ´J]› ˆn>¢¥•CSH9µ¾z¿qvk\…å“ ñ³-ϦÓÉô¿øÌØáçî}FhPÙÞEª…¾,ΰÅ»‹±ëÔXGêÿ…ì•ùlœ§±H¹ê=tûxôÑGqýõ×ãþûﯾûå_þe|õ«_űcǶ±e; zì~êáNR¦'Á’É4JA¿ æd„ ¯¦ëái˜¼©€·µ¢|9·P×TrÝ$fñÁJ"‹6ƒ|ä3A½O¨î|©Ë>l#Ç¢2·ôÀ.ØLÎÓBP²²œÓ:Ntà¶\ùQšž'és®¿¥ë<ЦSs½?C!;íHÁ->•Êt؃(Ü‚!~3I?´Fõug'õ±Ì†Uzú¹#YQÎR@žÍ`ü ªÉw°p.ýÐ3>îö|f¦´@€¢¦?º·”šA;¤,­óÍm”üE‹Û蟚† ÈO&xþýg“ó4W§xGÓø1’7^vý•ÿ·Ùj1íˆ4™Cé%¹ÇÓ‹¢z—“m…Ýó6"¹±@pcéræ’ØMÊ~¶ü>ñÀGæ:¶ÇÂNj=¨lmX]«ñ8ÁN&UÆ”ñ9¨Ô+ÊÊ€"X˜Ÿ©]+>OŸWà±Ð?K}¼ü Û¶6¯föB9è!Rÿì¤em(ÙHkަ×!évE= pë­^p1~ü 9^ðÏÑÇ' ±Ð÷¥l¿ ^ŸdyL°‘ÜMê}kÜZµ&:êi*-¦Ô¢8ú>¯f¹°MQÙÌ4©s¨ã^%Þó‡éH A6ñ Í« ÿìôäÅ=ÁÖÐ[ÅV®k;qÎáúë¯ÇøÃÆ÷÷ÜsÞþö·ãë_ÿú6µlg³÷òÄEQEQEÙS,ž9…Ÿ{hOžYbEÙ£|îsŸÃ#<‚~ô£¸çž{ðàƒâŽ;îÀýÑáÛßþ6¾ùÍonww$šé¡ìjÈ{Qd°¤ë÷¾Ù Š2/noÛðn€T¦ÐÌÔ0>üe"n)PX¿ïQ‡ e¿ gÆ¥.Ñ$ľ!>™Ì¸Ø…ea{Žèؘµgpô±QQ&¹N†ÚºÇ΢÷»ß¶ëzDãÏ©±hìV¶©v`Ê^ãK_ú¾øÅ/â×ý׫﮺ê*¼æ5¯Áå—_ŽÛo¿o|ã·±…; z(»‚8•;ð“RÝ VkgCȇe:#Åìè!+[;­Ð×&YT½#Ò÷ÐÖͤ‘¾ËÑÁîB°QnN³‹·oŽ,«S¹?9(8Weï.²zðë}lFj+ª$ˆØ˜Èí¡¥M²Nðuý•esrLjúéçݨå¡(ç E%)ƒ³Ëõ½·5f(õwÈ… Ç¤Y °#KÁv3.ù9h¢:nò'‹_† <*u’ÊX¦Ê=lýÿê\Xg¥+Œ¯î•L­Ò¬èž—ZO•%V%Tõ¼n0¨J"ƒÅzÔ_å>ú\·7¤bv#Sýä¬o?ðØc51¿û»¿‹ë®»n‹[´;ÐòEQEQEQv8.úC¢Á]e—ñ;¿ó;¸õÖ[74ïáÇÅߎ9²Áí}4è¡(Š¢(Š¢(ÊŽ‡¹‘×@‡²;ùÊW¾‚Ï}îsžaa¡ó÷¥¥´)DÉ~ô£ ¯{7£å-ûJXÊ1O’êßÌ“ÞÎÓ~õzSØìsaz¦ügéï¿TnÕH–jìùâ.k®3ûÊÉpY™ú^¦³6§H# V¦^L|jâm–Ô²“qÒ†–ÉÀgé¥YÁµ€ü¤‘Ž]¥ï“M¿ HºÁ‚›–Œ®Ü•ˆ.^˜žä>ŠÝ‰ô÷º”‹R¸Ç¤mnœ;\ŽÏÓßK·©~mÞ­ïtÑF²“5æ@zúÄ=Œ9(Û§¦4d:ïcÂb ”˜à"ÀF÷Ÿ²ÿñc ºž¿Q†Òo_ùŽ{ %-Î=™ü>í.¶·Ž“1é·¶ÞŸ§F_ìE«g©]r{Ó ÈôëS›ýMÝ>2G!¹ü±¯ïquÿåƒË n\„ûU£Ïæq=à¡Åä½dàu M£Œ%rbÉêÿaS»ùÌTýŒÏ.+>›ºo! ÆuÙõð\½1ùÒ¥Õg;Yn: a)ÚŽr?ºŽAœì‚•„WŸvJ^œ¾ç—®CÄܰ‚Þl¶r]å™gžÁW¿úU\qÅÛÖ†o¼ßýîw·mýÛ…fz({6¡“ñ&<Ö\õ—[ÆdÀpÃe@®á?eRPÒAEQEQ6—2àáµÞ÷|èCÚpYKÉx¼qm!ï=zè¡óZÿnEõ”=Ë€Ü2Æ …P '°)DM…3;?2¬(â™EÒ•)£(Š¢(Êv ¹—)›Ë·¾õ-œ={7Üpæ¯ëK_ú^ûÚ×â’K.9¯åÜ}÷ÝxÏ{Þ#–±<øàƒx×»Þ•üíĉxúé§“¿íu4è±øø?C¯zÎ/âUÏyÅv7çüÔ™Éså¬à3‚7T•µüÍó ¼àï®Æ•%d bÂáÓK`J\l^;¼€[X¬T ‰}HB:¡ÛCÒÚʦÓL– ÚÌ'0Aæ0¸ §Ÿ=uvõq`òsYöº˜a]ÒB~¶=.=!ÏpƒºŠKRb›Z7¬§1¹%JZ6Büæm2l¦—غú¦YcHt‹‰ÕûIíce~­ËÉ‚|ääáN¦çQ”<E*U î”ÃOä•·Ä.ùÐVý‡Ë¨êW\Vþ•®_€ñ¥#X˜×x€|½\»P—·V÷!v)”tUŸwЋŒ;Ÿü|÷©o"÷ùúo3÷Þ{/>ðàk_û>ô¡u=zè!üñÿ1~ðƒ`ii £Ñ¿ÿû¿›nºiæõýìg?÷¿ým|üã?ï¶çyŽÛn»Mü™ñ…/|aê{"3Ïm µÛРÇ>à¿»ê¿Å¢í½Ù‹p™NHŒW|õ5-‘sœ~Îi¬aqy ƒµ!Ž=uÏyê@ㆉd ʲN\Qv¶x0à0pÚ®ÁQ¬Ãu¶Ûñf+ÖëRËL°®iEÙz8Ö“"õÇ;çÁOÙ%Äb¨’æ Žãþ+Чµ;B‰57§à èÍJÕ¨\^C§Šl-AR¶h?-vЍëñK~Ç/ùUœŸÄG~ðß/#·Ò²výÌíS§Ná¶ÛnÓO>‰»îºkÝé¿ÿýïãu¯{n¸áÜ}÷Ý ¸óÎ;qÝu×áž{îÁ§>õ©™ÚöÁ~ûØÇfšv®»î:\zé¥ëOØâĉøú׿>·vì&4è¡ìI¼¡JËÃà‚g—p† ËÖƒ ĸìÍ·ã‘ÿû­L2\ðl_Á>EÙüK§W`Ƨº„SEQEÙ<â—o`˜ªÀãTãµ´e+9zô(Þÿþ÷}ôQÜ~ûíâ´§OŸÆ 7Ü€,ËðñƒAÈ:~ü8Þ÷¾÷áÖ[oÅñãÇñîw¿ðÅ/~¿÷{¿WÍÿ‚¼÷ß?þüÏÿ×_=.ºè¢¹lÃ%—\‚¿þë¿ÞмÞ{\vÙesiÇnCƒûIÕ]D|“"(žKë­Æäu›ÉÙê3ÙÅ:Ýp Už˜aòrÁ%Áæ¡#YË<VpÖc|æ:„§ÿã»pØ–VA¾™ªN\G§½Éê­`Wí#4 apJHáÓå d–’ÛÏ~Lg¦gà1\Ra;íFP¬$ýý(•¹g_÷ ·B|—KKrë˜[‰ ’î-#ª¡“©]0¸´¾Æò3A)x<ƒÖ†¯Á؃"…uv§A<ÆàÌ Ø,Ôðƒ{Kj?ÍôÚÉJÿ]4• nxùbpˆ`"ø,¬ M•‰””%fq¦EùÙx†É‹cލü,Ñ`ï!ét»¨×+…Ž«mŒN}ŽKßX˜¾IÚ#Ì3Ý^&êß ÷6éó=Ü{®BüE8WÙŽÄô$¹IN,ÂÊõ&Üg„ë\roqî™ä=Ëó ¼?—XÐÎx›:+Dƒä}Ÿh2éìOÉ•ƒh!¹ß™''Št¿DøØ"åÇ~”‹vÄ÷.®Üt\}?…|ÌÃ9šv§H_†Ý[äóÁöîoÓmÚd@;P¦Lr¡“ú"æQp(Ià…ý.%çrµž&á\rõDåyìVa'õX‚Mô9r1‹ïå&çê—n-¶1Ác´äà W%.67ÈeÖ¡Ÿ‹¾¼Éª¾ˆÉF—Z<æ'·/ÚøÙÆçÌÒøÃ¥ïÇ=þ¶’¸´cq±Û…èŸø~úӟ⦛nÂáÇ¿Ýxã¸õÖ[ñÁ~ï|ç;1 pà 7à•¯|e5M$ùìg?‹{î¹§¡³qîÜ9üÉŸü þôOÿŸüä'ñÎw¾sæmxá _8ó´mŒ1ç5ÿnFƒû•®T¾®yz"Ú&)ÆR rÏTü›Ì ç’¯kýËFã!† –M,\‘ñ±0`q5CæºÊ K°},W÷9^þ IDATh;ö‰ðLÂà‰†É‡fâ1RV¯s Uô³HzÁ6¹¯}aÇjJ$ žÂyØs`J°b°PøÔÇ•íb5€2nõ€«9ÐHͳ}ßã>§«_m"ÅóÖBÞW‚‰6…%l³*M×g¶NÝm½ÑšNF¡µS:/«ñ„…ÖsèùÚÓµ-Sp4M9O‰tö´­³KÈs䉃¬Áz·çC’t­±KþF$ ØAÜ1¸ µÊuœ»ý®‚I÷$]ç]׆lÚö£Î‡æÝ„t_’óò}LºÇIA„Ä ´ð…q}ïb4KZª@GËY  /]X¶UÞPé@ÒÆw›¥ý¾Yâ9 ©â~ŸÓ5È. ØE¶¯„JÇÉŠ/Mȳ˜¡á£~…<à,c2ôX9´†< íŒ387ÆÁîÖeršî#«ñ§©Ïëx, @¼'nó9kímÕúæÉ§?ýiÀë_ÿú©ß®¸â \yå•xøá‡ño|oyË[pøðá©à|á _ÀêjSðõ¯=~ó7ðлL峟ýl¯éç=ÿnEƒÊž…<åá¡Ê ì™…"³LjŸôþ!qEQ66Àx1ØMO°¹­²·Î7à¡(Š¢(›ñ„ñ‚Çdà1æX9´ŠsGWpèÔ!‡±hX›c-qÙI<õÔSøñ ¸úê«“Ó¼ìe/ÃÃ?Œ¯}íkxË[Þ".ëòË/Ÿú.Ë2;v W]uUï¶½øÅ/î=Ï<çß­hÐCÙ42=¢T~?©ßX9S¥¼³1"¤†‡$[‰I Æe!`óúÁiñÌZõÙNÒLFµ”žD“3õ‹%¦z{ÉLù½™a?(rÚJ"ñ2“»ª¼…|¨e.i¾Õ2 A¸þK8ýÜU*o™çê%ã¢N¾ÉV½O*³1˜f{Ø·¹‡7Te[—׬³‡Ÿ>–é¹ÒÜa©ˆ»ƒ.7–và#…ÐÖWïm§CkðCQzÓÐ>â±:”ùÓ*§–ÆzÄMÕÇ™ÜDC‘ ‡Ÿ³q†g׎à1ÖÆp™Ã`%†iºùdïå?òÈ#j—²q4è¡ìIL^GÆÃI^wZ6÷U¡É‹h¾¦Ê+ÊŽ¡Îòˆƒ@3àèµS›ZÐ- ç¨XÖ%l*±Ô©íè¸éµX‚¾pj¾®L‘’3‘aO-{aDUIË¢7ƒ†fÊZ‰ËzD!Sé<&ŸÎRa+±¥2"i¶€_ú:¸É™É{œèþù­Ï7 ßûíio1O2¢Sм¡M”Š.-Ó÷¥ð}ºŸ0&ýF4H2Iíã®}ÈIáJ|v‹£AUÒB…MÕ`b1Y &¶Ñ?zPQÞB~”Y mí¿¯ÄéÉ ¹žçèe`½áçÿÿõ ¿ú÷(?‹}ûsYöÒR=æ™L&ÉÀÇdŽÃ=ÇÝʶ Aý牜ìÛ¿¾‹—Ê…gïÏiºs"X:-w2ý=ÙÊÉ%Æ CPõƒy;®;°8 áp9°K[@’M?`0:ß]V}‚2~ÇÃòkôH3FéÁiÇàIV¹—š)«Wî°a•h?°×ƒ[בmn›_”ÓµÅtDK Ä µk2]Ó ×”tÌ…@ ` š´[P׺c!?8\í«|ñ¼Ž‡)[L•¦—’‘gØI}þÄ×VÀ`czÉçu’ƒRУ¼V?9a0æFŠäÖ’úwjžåCõö-®Ö–ƒYnP޾ƒ o1oÞµÂ5Oß )÷/“IûÉŠÁÊÞï=Á‰!ý}²£Tò¶ã>Òû!'÷¸ù $»¶ÃÐb2°kÌb°šZ¹…‚dÒ§bP×£Æiàà9ý0-ÆÌxœìï˜& î÷&µoƒÑÿ˜{×/•Þ˜ð¦_`N_ ×GWŸ*‚8eÏÜ'×;`×=fèé%®Dê»"g–xrÑJÛ‰cµøÜµìëë.~yç–¢‹`CXQQÞbCÉffà‰1˜O®™Jo®ñ’Àå•“ ü¨0ñqŽ>Ïc\ŸzQ+ÚÙ§ÎÑò»òåâf1Àƒ(°KsL}îsŸ[}^^^ƱcǦ¦Y^^€Þî+Êö AEQeß`ó ,Ǹë_ÿ¿ÈÖxã|év7KQEÙc˜Ü!ÈQ}›²qVd ™~ž ­¹Ùt¥”­á¥/})ˆÌŒ'N$ƒ'Nœ\sÍ5[ÝÏ7­u”Ëk¾ÜØÇâµñþ![e„l‹éw·ÞP•ý@>yÑΠjhëxßx\fB”¥iBJqÏͰ•ÜaaeÙÚ°X~»ýý–·µ´Ân/Ë×ZËáÌ-3= %Ëtˆ¹‘Ò™õ±‰oÓe·Al@;UæD4f$vÉþÇø¼Ê©¡,‹2 ŠœÑ†K™ñuæŸÍv\kÒûf?Y‰—¢N;™Yd_6À±cÇpíµ×â¾ûîÃ< l”/oxöºyÊЧ³É ‚ËêRŽÆ¢âë.vJŠ?·èÜÔtqo©íªWÜñbØœà;´:¤R—vÄF®‚Vê®umÁƒM©ãÍ£²îõ’¦GÃEjcbËU¹º\(û‰¨'.µÕˆr~Änl㺟2ÃÚUÐOêʤ°¥'‚ÉMáhŠ{>MéK‘熓 ùIºte—Üϧ‚6[°¾yòŽw¼÷Ýw¾óïàmo{[ã·'žx>ø Ž;†7½éMs]¯²9Ìﵺ¢(Š¢ìJëêÅs„Á8!y¤(Š¢(ç‹÷0yã\øËìØ!;ØÜ#‡¿ÁÈa0Ê‘wG0c¯°º„½)yÓM7áâ‹/Æí·ßÞps€Ï|æ3ðÞã½ï}oCôTÙ¹h¦Ç~&)Žò¯[̳ô¤^h»ÔŠ–k\Zq½1}ãû´ãJ•:®þZJˆ’¹¿h§Ä†Þ÷ͮ؄lŒŠâ0õ?G.0ñŽRE»\OÄmܱodRí’eX*1*œ‘¦¦/²'ŒGTæR”¦lA)FªM`â2›œÖ xï)kµC;"wš¬™‡Ì¦Lk&ø,µSÀ¶¯ÓØ–›ØW™7LèVZ´(XšpøH­³^®tNo¤è/I‰d3a_kóA Ç/±ïi ˆv§žÎ\ Á …]ï,¹.Ç•þçIÏõ°ßÀùÓßh»ÌHÅ}»UÌP¢:ËôóEv=‘öâl‹!ªëŽ]Tm2¨Î%b¸ÄìMèf{š*ù n‚<[ÿHv†óµ=Ý,QI.×")~Îã´sÔ.È}æ™gpÇwî¼óNäyŽ,k>;v ŸÿüçñÖ·¾7ß|3>ùÉOb0àž{îÁG?úQ\ýõ¸å–[¶£ùÊРǾ¥gH¶[-¼/QIK­éÑ^~ùoïŸM.Æ8›t!wLÁz¸~È2>å‘ |«>³<‚.®°šMÌ"ZªIX aSJdÑß ! ±ð0DFtà‘\DºÎÑ F8ãóZÁ°T³ˆ;Ò$Õú¨M­ÃаÞöö At=I#Y*¦ÎÛÆz¦÷­ô0Äì“n$¨Ùƒ :Á4\AZ¥åà-¶™ua¼ˆBž*«h“ç0>G /Xòö… !Î6èªJoâÍ‹«o:J_â಴ûKt”Ó;˜AYòC00Lp#j¸GÅZ(¥Å- Fë?2ÅjùŒ:ðÁ@z0Y-Ný” B ذRŽ,ß3dç!Ù–6eyJ€|OÜ#±#8fs(y 6Ê<¿ ›^pIaá˜oˆÎ¾6í![ž§!DÛ[Üb’tJaÃKV³R ¨Óíezÿ»þ.-’kŽäHÆ€ìl7Û‘¨ Ù²v#v«Ò¸„'@bŸbpC †øhÌØøå`BÿEnT—ÇúIõ™IvMŒïÿ6Æ íë®ÒôpÑXDp›¡!`ëýÎfýþ•ܪð2o˜vºñ«àÔ=¿t¼c·¥¥•³¬Ë9‡W¾ò•øÑ~„••p¿ûæ7¿‰Ë.» ÇÇ—¿üåÆô×]wîºë.|ä#Á«^õ*8p+++øð‡?Œ›o¾ÖîüР‡²c ˜0Hk P6ð†GQ”Ãݯÿ,=€×þçc›j]§(Š¢(ŠÒ…µßûÞ÷zÍsíµ×âöÛoߤ)[…=”í‡âL(¯H›ÛH&IœŠÏdªÄÖâý=@œ²‰Ç³:‡lTb¤¸Y¡B‘>N0Y¨ßBíFñà2ó#Îø  Æ^xÀs¥âÔeA(Ÿk1X鯢(вÄ™ì¢qB³<*6V‰¿ËD;úE¡V”ù Ae[!˜#ÅçÈ«SÝS`že =oúdà‹Ú¼`©ž"ìdg°jg²{(](P§ˆ-Ve"Ì«ýÓw _¯ƒ†UZ1e—¦Hõ+`wzãÛ°›i¸!åUI„q¾™¼¾žìØaé ð¦/½vì`'«µ{R|ÝEƒÀY3AÎ'p;ÉXß’¦b¢Æ×Å€Ö·+{f)7iþ»*‰1 6@n=ž¹xŒ+9TüL†e¹0ZrÕr.ÇûŠ£ÏM×›ÊÞ×ûȽe÷›e£ˆ}^ÊyÐ(åhè'EV¥'ñx¦•Éi)Å= ¹x‚v\£d·c[•ÖX Önšg¹Z[¤áÕXŸ¢hÐCÙ19†üè/µkŸƒ–—Ã^вë(Äí8F”—1<1\æÁÄxð—Äå?yECKDQEQE™ôPvæÎ]p6÷°ã ËZJ (»âàÔ¼uŽ-»…2àá¬Çp-Ãñ¿~¹<EQEQ6 zì˜'`žÍ(ÍûQÿôOAù_˜E-Lÿ0ú{ þKPMf±t–]–\ó(©ë@îY WjáìÀèks+_dEù”Í,I+˜ÃKªì“~yê{æ1g’‹êkMGæ ,¤}%9(Hêðãäú)ž‰õ¤ÈÝÓÉï -DË:­Ã¦ÏEöðÂvs8Ù—Ÿ¨—•?ÚšK²ÞKŸ _”ü樰O„}ÅNÜï”]šþ¾Ë–.ñ“…·‰k€¤]6ìd “§®ÁKßó²qݦXKÃF†2q™‹7Tiw¸,ü…ïΦý‚bÇ–† È ln°°f‹å¡ñ8}ÁN<ÿé¢}—<öÜzøԥž‰Ç¢Þ®~†™ä¢Ïõ~³“ È…Fìk‡3¹Ä9G­•ˆÒŽ$¢{F€O;"I÷>k.¦—3{tzzžÀûséuˆ.iÈH;®tèI®`döÒ7bøàb’üQ²ˆÖ´]ëIa„þQ.Ié¿ö G‰.6`‘êû®ýûgk¥¿7¤ïá~É:=ö`ɱGXw§lê˜tìÃñäÁô¢h€TŸ*V¼g˜Aºôùã¢+Qò|'œÄ(rPb®Çf$,Ÿè ÈÔ}gÔãt÷Ï Ö™Ž†@Ù.²•‹ “môµq9_h}ÊEEý¶qkU åË¡Ôfj›×’ ÷è¸Ø ´ŒSéBUw!ßúÖ·ðWõWÛÝ EQeƒ&&§)ÝEQEQe~hÐc“y衇ð®w½ /ùËñš×¼¯xÅ+pÛm·mhY÷Þ{/®»î:¼ñoĽ÷Þ;ç–*Š¢([ñ›‡?EQEQesÑò–Mäûßÿ>^÷º×á†nÀÝwßÁ`€;ï¼×]wî¹ç|êSŸši9§NÂm·Ý†'Ÿ|wÝu×&·ZQeC¾ÎgSŽKOØÈe,ä£`FáÒã àô(5=Œ²âû‹¶ˆ£§.ŸjS>ëÿ.C*{1¹¦©S¬ƒ0m1|`.Ek©•’®"ÔŠ¢(ýhß7)ñ}£4Æ¡*aTe(D­’ߨ)-.‰JÏëõø‰Úå*{ zl§OŸÆ 7Ü€,ËðñƒA¨¥;~ü8Þ÷¾÷áÖ[oÅñãÇñîw¿{Ýe=zïÿû>ú(n¿ýöÍlº¢(Šáç˜YZÖÆ%-Æ7ƒåoÖÏÄ¿uk…l„¾:a3³½°¢(Šr¤±E ór´-ê7ÕôP:Ðò–MâŸø~úÓŸâ·~ë·pøpS0èÆo|ðƒÄd²¾W6Q=(]\”DßEQ”Lð(!OêÚ¢(Š¢(вÉh¦Ç&ñéOðú׿~ê·+®¸W^y%~øa|ãßÀ[Þò–-nÝvÓáä!"¹ˆ#¼ rÁ%ÇŸtc ˤȶ´îü©¤’7óH*ö;QåßK ÿ°¢+ˆèÒ"n_zA™~z@=_RÛ¶³Jµ¬žo ȈoD§Ñ‘¨ÃYAp’ÎâU€SNÃôq" Ø´ƒ Kד´dJîõr ئ»/|o\^9y4V!¨¶wª«¯¥ÝÈÁ&q Uß·3J—9¹˜œŽ-ñ<ù’’c‹Ê[ÚB¤ÖÇëóyb°á*ða|ÖZUËŠ!ñîr‘#Ëb÷–év‡upõÛ,¥1m¨£TÆÛ°~ã\•õaÜ åHRÎoJŸ» ~÷ð$\#©E Kbî‰Üï~œ#$—Á±K˜žÌb‡s€pœE§ $](cÑ…†Yp$îcŒŽ7¹¢c×ü"}’ÃÈØM9­mâÕä9'¶µ¾'&]iG)°K^SaL)mKô}\’Ò¸ïµËÇÉï«Í æø\½ÉOšŸ‹}E~\o/O¼'sTZ#mƒ¢ì4è± <õÔSøñ ¸úê«“Ó¼ìe/ÃÃ?Œ¯}íkû0è!ÓÇvVº3•íd7BzY®ڞؑ®‚SÀpHØÏ-µ‘…utϳ¯§¥A û}Ž©ž>ËëÚrÀ®×ÀŠì†‚ÉÅ›8È##7RA€K2hi­R ’¼O–S¸ð@ÙØ£Eƒs=Ÿ6ÈÆéÇ!iw´¿/!€1= ›tà¡ rxb¸¬°uMDÿdKײð€HhˆÊÉ'uÍv­B ›x ô؈=l_z®»˜û}‡!>üKç¼’v«ý]§Õü<Ù@€#{Á–ó;ç¤k@z©Ãä@‰—D̃·BE#Ã5ï—åµ:uŒª¤9m|ßî3þ›}­ãјG﮳_Ùomɉ–·(hyË&pÿý÷WŸ/¿üòä4—]và‡?üá–´IQe?óè?{Ï\:‘²ÄÁŠÉÀ!88ë“óE Ä(Š¢(Š¢(ÍôØNžZfÀ…lò^;®»5²uÙ,YÀMªÏd4)ƒÇ ÀÔóN0Õrr?AÎõ4kN(÷Q”mDƒ›À¹suÚèp˜N_[XX¬¬ÈéxóâÏ~ü—¿þ¢—ã—üÒ¦¯WQe+)ËZÊàGùo›{\úSðÈ‡ó ø¶…I«v˜ÔpÖã‰+ž9ƒÃ§Á:‹Rƒ¤ie$jݦÎHc}RõWì“7'óU œìÎõ¥k6Ê`Ò†i¤ho|1Š¢(û‡¶~FJc-‹ÛrzÛŠÔ¬gmm#‡˜uÊœþæñÿ _ü/§¾'v[j{«»JôØ––jÁ¶Éd’ |”®-Øôö¼÷ªw`ÁÖmÈöz ¬¢(JÄy= oÌdÎàäåOaéì>uÙÄ‚…@ÉùÐW«CQEQæÅ¯^ö6¼îyÿ¦ú÷éñ3øŸ~ðo·±EŠ2=|âŸÀ{ÞóžÞóÝrË-øØÇ>†ç>÷¹ÕwËËË8vìØÔ´ËËË€K/½tã ‘!ÊHœ%\º Zè9}sù a8! ãýYaiéé €h};àf»úÚo@È´S˜K2M¬£K|Tó¢!:^w´+EOw’^èví?IíÝÊ $ÚôCÜ: ¶B`1ºnD±ÃÉ¥¥k^&á­? µ,ï!u1±pZ¼NãÖÒÓ;$„TMž7N+±ÕÖ¡ˆÛžÓî-6·¢©ký»žgºt¤ýùêÿçÀå… fZjÏ5ÊNÕfXÀpYÞÒt_ñ-Mv‰¡v¦FÈ™Ú&?]“Z¦Ë¤i(œ]È3ÊÆ³ÍÀ¶¸²«]صŽI´0ÁÕEÔøe¢;¢S §ï‰ — $¤Um €´Žä·…ûF¡ÆŽ—Äéus$)°Êði§-žn ‰Ÿ¶@¤º¯óGø­§€¤°}A2¥FìÄ}?¿}Ò_ÜYÚæIò¤cøÞ¢âu#m†Ñiéxt¹1‰ÂäÒNr®œ‡:H^7HcùÜ•Îϱ @n“§a¦÷â˜7…žíú§ìÐØŸ‘c l=†÷& }­÷0Åý&¸»˜Hxµ%ŠJk1@=†^úrEÙN4è‘à /ÄK^ò’Þó]rÉ%€—¾ô¥ "03Nœ8‘ zœ8qpÍ5ל_cg€ÑeIÚÄ (ß5™S¶J‡ šs§Ó³HJú<êØŽô:¬}NzrÑ¢¬¿ëIÒ¢åó\Ÿ ‡0˜ÀÜÑÙ$øƒlé]n4γv§, ¤sÓÁª¯kÀ,«žÁ ¢(ù[Ƶçéz${@¿k0¶Ÿ·U²‡e›ÍäÒ’ (¤¦O­Æšæ4)' <ªŽö! N>„š´ã©13s_ؾ²!Øœ“Ϲ±MìÔj¢€‚‹28â2”Ëõ€r0iž åtdŠ0>¾P£YJ©-Î\T ¡²Ø5Lu&ˆl¼°‰‰O%›QUFCža Ý‘àþ¶—ØWVˆÌ¦™vM^C9æ)º!¤¯'æqòÁ1¸Vô nˆM¢%1 ÑÛêÕ÷J:˜Þv:ùÐÌ<Å`A—îýœ²G ¶ãáXº·KK“®+ü&[›ÏÍ)­#`'Ï#'á%ÍÒûIèk7„pßeß\hÒH¹râ:„ãG°àÄu7ý.LHŽ—NvcƉD¶c —rÍjò¢1c¥Õ€£ Û >Ë‚cZÙnöÅ˦a´œx¼Óqü$·½ÍBË[”4è‘àío;Þþö·oxþcÇŽáÚk¯Å}÷݇x Ø(^Þð†7lx=Š¢({ 6€¬™ïº – Šl fÊ`æE¬¢(Š¢(}Éà†än0y…å“€ŸQÕZQv0ôØ$ÞñŽwà¾ûîÃw¾ó¼ímoküöÄOàÁıcÇð¦7½i›Z¸ ˆ"ðñ[¦™RMÙ‰oê”9¿Í‰ýÞ㈿ ~à’o;”ùg5HŸc¼Íà‰7\Q1‹Ù3·K˜¿ j´Û÷èÕáyÿ° ;¸ƒ¼É IDATmª>EÙ.›‡SÙøðˆKIÖ_‹B¦íéJ¤ªBch&­YÚåÁ@±,öTe}x0]Rfð†`#ÑØ²LˆÛ6ÅgÔÕEQe§—§D¥.Æ9Œ³VÆ‹Œc?ó¬.Àø Èe‚±CŒ¢ì"æ“ó­LqÓM7áâ‹/Æí·ßÞps€Ï|æ3ðÞã½ï}o%zêœÃoÿöoãøñãxüñÇÅå®®†ôo/žw#d§ÓÉÀÐbõWMÓš.Ô¯Nÿ)›­ÿBn~(5 …êϘÃÕ_½¥L¦úóƒáºn0€ÚÄ_\·›3¨iV|µ>6§.áÜ…‡à³ŽZòó$Ä,œsXZvX\Îg ²xâÂu¥ðh#ò¼êU¯Â°²²‚øÃ¸ùæ›a£7©W_}5~åW~?þ8Þüæ77–ãœÃ+_ùJüèG?ÂÊJH/ûæ7¿‰Ë.» ÇÇ—¿üå-Ý®Me tv$‘*wûû¾™+²h­°ö² ìLéðí, HkÌsÀyþ°ü+¥»w׉«©· ¯úYé˜>±bÈ¢d]ôœGò XâL†Øµ¤üž Ágù°tß(–í0Ôxøçô!Ê,d{KW¶©«Ë[âߣUHˆ¢¬©óÊû†j»Må:¥@‡”õa˜àPkg¸,-^gr05§)57Úe/Ô˜®ú,Ø!Ä¢¦ñ:â’ögQ ›¿…¶¯_1Sö0åº ¶/5W8¹˜¼.o!ƒJ|–м>â¾]b~I‘Q ãS=]+`EQľH÷¤ ¢š¸g¢ˆ+!éä;Dµ)±íN}]D ÷½—%Š×Êë–ìë†âqiÝõÐ bæéRÒ ÚÛO˜|«ÆcÉs´ã¸Šý¿¸‚.ÚÔuÓ_°8Ì7½,Ñ͆äßÄûX—èsržôöM7&*uq#d«Ø‘gàMãóF L7å²4ãBÙyhÐc“¹öÚkqûí·¯;Ýp÷Ýw'³Öâ{ßûÞ¼›¶3ˆÂUÉDùÅÆ;\†ïCœ×Íx#:éé= K’÷\·¤¾/Zò±K¯š,‚ƒŠhqøÆ„üD˜ôö‘•â`Op¢¡`Ykûk”i¡Sî QL,ÖçpÃ8Rè9‚/J\†ê|©gìWÉ×ógØèZ 8#w#©y¨e}Û°ÂËö¨~ m ¸ZVyÉ Y8Á&,¯°RI"m  °eEØ/ƒÂæÖe€§ò3#·±»M½­.ú¾©ûÁpÅ!·.²ê#½,/,·½>NœäÄ層½¥Þ>Ið4nW\¶Ûóo‘geû(ú­Þçä=ì¤z°‡i8¶ÄC]ü~C`)h&$HpJJ=hw9˜9$®C¢ ÷+ÁÆ—s)€‘vc¾ºÊ@¥þfÆ øúˆÁ[±MÁiYÂ÷D&ù[„ôtuÙ „>Êû³âôÉãNi»õ®u̵T@<æòô}_øˆ×§ì F/{ÿ¥º’›Kc5–·ÝH¥¥é—.péó,é{ ;¥E17*– ÐÄÂ$µ˜:‚HSíØ€Ëáy¡šQŠŒ=E™;Ì€Ç09c8Z²‹¶»Iûšð6žà 0ã—1lN°Ž°0 ¿í¦÷œÊì°aÀ©«‹¢(Š¢(û z(;—8:>Ë›–Æ›DunÙVØÁ»§qä'‹õ…CàÉ?nw‹ö 乑±„7öÞ„L…|„)ׯcS¸qlv»Ò+ ©Va¹|”UÞ0Ú…‰«L Sâ¦åúR@³ì¥]BSb¢]ÂZÉwÚs¶»­Êˆœ¦:+Š¢ìXÊþ¶I2K†GrZEÙyhÐCÙ^¦J.ꛦç•z²ž©­ÊÖÑ, ŸÞ= ïž e Bšµ2âÀAC“Ä’–²¬e2dL£¥ˇG°Îàè³KLLx@öuIj<÷úŒ§o[áJã;Ó6áZoèŸ$J[ºhkxÄ¥'“ÁúmeS—›8[|ËݥܴBâu—.1ñ4e€%²”e,ä©Yj}Ž·q!.L¶Ê¡êœ1>èÅ€É8W¬¿Uz¶™~ÄŠ¢(J71YtoÞ^’¬±BìäÆM`+×¥ì>4›YQ”MáŠzÖ­®éTÚ”™Áj”1Zœ`t` ?yÉOªiJ;Reo!eŠ(Š¢(Š¢ì4ÓCQ”MEv“Q¶‚íQgpxÃ8yñiœ}Îi\ñàáí¼aØ éÕ+Š¢(Š¢(ÊÎFƒû›]kf›˜ìþ!ÍÒ{ú5x—væ ÓO£Ãççàz*{¿²þDÆ™½æéiÛÚ…5GûÍ`ÒN%u–Æ4¢eOd×Ä÷D‹f:‚Ä:Î1é%%ÿ´²9ÑbrÉL»4°á`zÝR›ÌÉveú?ÙÞV¶vR§ª2-TËu™ËšÁŒáÈbå‚e¬\x‹«CØœŠÀŠ!ž>*{J>L«ì¥YÖÂð™Ë2L ¼––†+õ±5.¦OßKByDÑ–Ö¾I¹·0ø,ݽù,½o]f¢ÀEß7§«t2rSYÖzf†°P†B‘ƒŠ'lý[>èÖè`âdéJûsž90…&ž^Ñb‚õ¦ú\–¾d“zCdëݬrlŒ òapþ eTa‡Ùœ‘òÈ3²µ¦³KùÿL8æÄ.jM68¥è[F×ZNuÍ“…é© %õƒb¿Âãæ<Åy\¨†U{È^ؘ'¹AʦKÝ/ °Çfž˜GI FǘAè?<¯õš¾›t?a³ôöÉn/kÉí\`ÀýÚ Ò#÷û”+OøAÚŽ‰x<¤vÙÞBã¶ÓÉ(…,ÄE:ÈÎn²L?¢ŽqqÏq­1G’cÏ+ð|fzݰ…Òù“þžÙƒ(qo`€%G+aì#c…óG¸—.7ì·¶ }žŽCÊžCƒŠ¢(ûòá/áÚ»^„•ÃcXIØïšzúyÀ…]îÊÆ~áQüâß^a¿øâ¾%x°©àcûÚò;bøÂ¾–‰jêƒÀ˜ Dã œ â¦ä¨Òý`à kÛ¹nOaÅh±¦Š¢(Š¢(Êf¡AEQ”}‚ñÁ£§*‘Lov“tëwVuCzR<<537˜êï43;¢àG Q‘Ñá ¨¥xi¼¬uÛePdwfs-"œ¾dˆÅsŒÅ3Ѐ‡¢(Š¢([„=EQÎ"+§A÷,UÙ-w´ÜQŒ²<+5mW pê#6o㽡ª|%v7áèy•[/ñ£,y†É=žqÁ“¿òôósѽ¥“-Ø^ØÆ’¦¥+Wÿ6 ¸lýmŠ3(Ú¥#í¬Ž’Ò™…‰1äU`Âe¥Å`³”%ü5¿“Qݦâ8[€I,•‰i[ìZg¢í1UFvíBC V„k2dŒ– Ï¡Š'Sk‡%Ž9÷?¨‘~]”‘ eÛ¨ö_êlÕ&€Š2ÀN¯”ͦ¾¦â²•P.¢š\Š¢ì 4è¡(Š2ÊãÎ$’g¸u,Xƒ¾Dýïyˆš–Úåÿ³±küh—×ß–%@©É<ðQVGõgš¥+]ÁŽr:à2èQd{0x²<¸e¿¶±©¶™àbƒFILç°‘ý1ù4Œ &#lý5R:°ô_Ü*xü€~uû[;D±%tEÙZÊûõVò•=ÂV»÷íœñ—²óР‡¢(ʧÔóˆÓ0Lôdå‰1zœ¼hÏ{ìH(Y˜C<¢¬Œ¾c}ŠëC#Øa¾80®,-êßã Šß™Âdt=(4dËŒŒ8“£«ÄÅ›LëSªDžqèÔtàk«(…ŽOþ³`áœÃÁŸ<^ˆê YQEQö:ôP”ýDâ ¤"N0Ió (’§±IÇ…àHJU—8$uxÑ9¦“ô<²ÌRx+<õÃR­TÞj;UÏ‹íuÍÐÞxß°àšCl׊x2_ë%Ï`Ž${òð“ñ—…’O o€ÑRŽºú<ï±k ²¹u&8sdy¡è-~}ÀMY²PnJJ·!°äÖ1¦—©”KKXiÚ½…)­â³zú¦KK½Mñüô`oŠð` "ד|P¿ñ0*I±éHRéÊR4b‡–R·Ã[_:nàÙµÖ‚¦Güh Ó’7!ÈQNï ¯\]r_;¹PQBCLs˜È}ƺò#„sœ˜V PQþã6Q }Õ†ÏjŒ2ó‡ÉÀ©Þ,SëšHY' ÷9”×á…>òPv1È/UŽ(¡<-¾î¤{†p=ûÓéïÙN­¯yQ¸e ÎdÓ«Ü:6p¯”æaXˆŽZ‰{µè¤±cú›®ÒÅ ,OÊ’ú»¾îx ÷}zÆŽ>×.dÞ¯4\L˜¢r¬ÞHóHe¡&í ÒÊ„jþ6ûù¹,¶kN×Òý9IîT°éq ,fgÌ€äh Û.¸Í ÷*EÙN4è¡(Šˆ8Ÿ.Ò_36¦½%)´Òƒ„I=Q+PÑÓfNd4âLÆÿ#ŽžZÀ«ÿ¯k ÛV“LÓš¶ „L¯g¶ƒ,¹vˆ ‰1Li¾UêÃë”þÌÔ OpÑ£kC»£í ’  xÄŽ,žšå,l|¡ýáÁ„*¨áMTÂ—ÃØVy •ú#E`MÐéˆmmMp…) U»}h£ÃÂTÁ d}øhûÊÏd¨:<¾øw‰tbâc^}6ì•…1#¾µHT9 '™Ñ“a~ÌÒ*vŒ™²>Ä’~´­^˳N….=“Ä}ƒi¨Ò5¸W´ÈnÊ=¶‰°ïø^ê'ú=z¶ h>dê‡[¢ItØ]³ïy¯–úÿîFbÛÉÎ5¸±3é—ÓÏs¼”´ í° Oî÷âÀJ/q6‹mÔhRv>ôPEÙg”A܆‡j—1+ÆO<€é‰2_ªLŽ8hér”Þ0¸pe‰³>Ø6ƒu¦G¼’òƒ…3ÙÃU¥/ ãM#Â>úÌAÈ´Kçã¼1Ln0À0cGjn(Š¢(вëР‡¢(ÊŒ„7É ÁÒ^o‹·2eúçœßL÷•–†Éã[¼©ãÅPÞòäó΀˜0gÈÆKXX«ƒÔúsÍ’–M'ÎÚØˆóKõ¶´3@ú-'.ëˆ?[gªS%¸:C"²”õÖ#‚¡9µ†G™Íá22> Ã[WˆˆúF€£Yòog)`êᕎx[x‚#_YÚ2S%€;¿Q‘ÍQ»ÊTÁÃUæŒËêüŒ8f|kß¶2rظa?Ê`}q­±—oŸ×;ëá¥Ò¼íe÷•ƒ({æºüŠìóêL¿œ.ó蛑§(вР‡¢(ʆ°Øb‘Z¿vôù.? r£ó|4\P¢Ïil„ÿG⥆±¶4†Í-¬3p™‡ŸP£4CÊðˆ›*JY¼ùolŸ1s|Ì›X´íŽRRfw€«ÜÔó¨¾.q)!™¯3A¬¯‚q‰KŒ é…‹‹[3DðÆÃÀ€|(›)^<ú•K ‡R—BÀTiK1&ÒHA*ôfýêóÿð?|Gv1~ë¾4ìÏHß#×YÀÑõÅ«õ稼¥¹\M‘Vö‘^ŽYBúšP” î-ÊÎAƒŠ¢(›JôˆG<¸ãCá?ÜòwxÛ¿{5=ñOõï춤æœ|ÈFxá?\Üøw䘇sËù’/.atdÙØcáì¹]ooÛ›i Ú*»cWå.Þø ÕQèø8ë#Ò‰!&€CFy0ÁÀ„Œ Ç .t7ˆªRbªkÈ àØÇŸM~ÉËÆà_þ¸ø1àéÍ]™¢(Š¢(û z숢KÅôÄÏ8m5O_¡Flzz$Qa§¾éÅžG Ae_bæãPa{‹sIÛA0¢+HúÝkǶmàøu¶+9Czý’ð5ZHŠùÒ9?v؃Òïä'¬.ã}9ìøØ …m§ŠÞø8ÓÃ×n¹¯Jln1£- Aðÿl\Ïo#Ç}ŽÅ?M.·5%&š/°z(l¯ñÀârÞhoɹ ñäå\|b€ÁjÙßúÆé—?•DLÛmË,ØÔN-±‹‹‹zÉÒí¦ü-%àÚ´þëQ)û£ x4t<Œ/‘ ©uÕoÞúº\¦XF•mÂãLñÆ68çxx€ Ûb xöá*dÈ€A¸*{áBØ´t=vÖWî-AÆÃxjìCãê}bn?Ímö¶ÞÁÆå0yŽ‹`ay¥tM¼Rnèú”•â€ä°¥âjá¾ä5ï_gH.Œ±p=w¼·7:¿Åþƒ}º¿EI»úºè^}6äÁÅú§¿Ëc8°?—\ƒ/\k¦éèWD¡OÁéFrC°¯àA˜Þ¿Ì‚”è¦#öid“Ç0ìOáØ’ì"ÒG¤Vt#ŽìO#¾>Œ9Rüàêëc¢™ì¥cÞ• зì)®JCa¿»¹‰¢ Jœ'SçBtÝ5®ÛH<49&qc¸6³îÏÔt¸gKçe>hÐc`h†d+Ȇ“;_ižÞêâéLv^ )ûéA™ëOah˜ñØÕÁ‚ŽéSƒ½ð€/Ì".¬§-ÝœlÖú ÊäÍìŒÆ4´ U’Ò²»Œ-Žd?†Y{? ÖªfÐÈb`6âÃÍ ‰—EÜ´-?gãÚn•|Ü ÏÈÖ"G :¢k0j†LÛqáa¶œ›©‚ ùÐàìÑBsćLµ¬±­Ö“=Ž=5ÀÂ9œGºÓë‹Û¯ozºh1&.÷iµßO>oÞ0Ž= D'ý>áXõo’ä ÂVŽÙ–5m¸]g[­$ý=ùIÚu‚@G¤‘à3›,ópat làˆE›ã³X_d㯒‚öÆ´Õl6v8x¦N oêjHa–è¡×{°M¤o·JZäeµç›þÎ7J`š¥+Ræ‡èˆõ=â @lO[ÿ?* oš‚¥¥~GøìBÐÃæAD£h”Aމ0•‡` ã%-bb}ö æpìKM25„£ícæ pjƒn ›â ­ò SìHc¨µOåã$걜”TŠ¥—³]Û…¢ °Í0:¼„Ÿ½Àãñ+žÀèÀ‡NÂÚÂ…€Ád€+žÞãõìò–Xs`©*í`LÐpÄQv±ë‘²-L›R÷ Is€YBUnkzðéàFYò‚zÚmEÝ[”ƒ=öÿþþW2xÕs~ ¯¾è—·»9в‹quÀ£Lh'«œ?e–‡/„IÙ )RZ~.2<|–‡ìë@6"†)”B=°7ð> €Ë@ˆ·–mX![PXŠ™é±GKWf€ÉÀÛ Æ3È'Ÿ÷4Î\r /ûεÛÝ4EQö+¦Î®*jÃxdÛ5wœøßñÝŸýÈ}¾þÄŠ²ÅhÐcðo_t#m_1Leï!uŒƒŽß6Ÿ ¹ºÌI1\íëÜS [Ì~ð« ACá-»ÊÉ‚ã7ª[0€ ⥉²Èê7õR!ëƒÉˆv²e©Bûí½ñÜxÛßnGl¦³Wؼ)² ¥,ñÚ}‡{ Ó­Û”zÚÓõ4A*»ÚÒ²6.kih}pdSþ`<`ŒÍ‘Ù y˜¨-7€ó.*q Á‚aS,7Ú—QöIûH°akí÷\j·2ƒ©±V²H vˆs6®ÙÆÂâL/W½7>œA6šà²Ì`ó—âäÅ+8úì-…v!Š—ÎL*{¥ëÍ|ß²úŠr{Ÿ$—ëP#u¿ì놶3‰sÆÓ7D"ã™ÁYýÊ=PŠb©¢ó˜I¶7dà¤úµ–+ØL}¨m9‰ÔßKÈÂöØÒñ]‰ú®£¿Ë_oW² Ž2ÊŠý@-Òô~Fê5Ã0F0ƒÐߺµpi·_¼ØƒEH)žì6…•ÎrÆ€xMÕÿåsߊùÜ·âÔÚ“øïýÍžëV”ÍEƒв×Í]…ÞVº}v§Â÷ÝÖ»åçx[;¬ý¦<å[qŸ´)dGƒéhÀD‹ð‹—ÀŒ~r'Á>X7Ú}ïêõ׊ë!øh½:ÔA†2P j÷…jšPîR, Fп°ã~onˆ}³¹¦ÔôpÈÆ.3`Œ‡Tòa}<â Dlsê2š PŒ„à‚³Œáš)\aŠÕz ¯ÿpî–µíÏ1lê`›à¦Rµ1úÌqI‹‰‚¥.†©ífËßòa²/bASC¬ å,6¯‹Ù2rLÌäå !ム˜ ¼/ÄJ ¾¨”Á¶\]–!ëƒjW„¢Ä¥]šSé{í‚M­Ë‚p*|§Ÿtƒ*PF¦š# †`aâÁ•ùalÜš®ü=¸Åê!ŸØÁŒ\1@ò¸ò„ÕÀc°;™^®p/#ÁUBpi"ÀKV¯.ítœ?À¤íV§úˆØyª^GH*ïqÑòýr]ꟼ3œxßMÙLú>€Z±_K;’Õm›žHÞ_i Ývç{0 Np’CŒYDò˜Óp: Ân*'bËÚc/L®#8æ:Uì%{fÙÙ’ÇCv+érLóA‡«KúŸÌ¡ô÷â*vÁskO²¬ ¿Y€†àÁ‘ ¦œ áüçŒ_ÿ_~vm›¯‚¢{Âê…χqÙèÈOBpdüD½Ð‚´¡Lwöû+‘’û¤|£Ã[[b¥ê®JôPE9Ožüç×àûÿâQ\õ_®Á ¿ÿpîþín€ì˜UÛb;ð†0ÖÙÃ÷Â.&C`åp1LÈÆb˜kÇP"= ¢¡ÆÙ†=mÈifÀxã`ÃÐN04 LŽ¡ÉaÈóÁÈ á9”¸00S)„M}xoô8l2Ò˜Êðh[òÎÃÕ…éÌ R¥²á¡,ˆ!ˆÏc8Õ^Ù]6Ùä'0E·¸¼„ÑáEŒ/^Â…YôpC ï ˜b°ºò}ŸeosÊ`Š¢(;™óó¯göàºF“Å1VNÀvqƒå7}¥Èdª¿ÎéŽ&Í ŠªE©H9ýfN&gC–†DªôÄ /œUrë18äUŦ5÷¼ˆ3$Úß&˜D¡$!_ —ã‡E;Æ‘ìŽdçp=‡ƒvTA&Èì´ ¡. IDATdBvH\6³Y²q¹Oåæbf+jºð¬Nϳ«þäì2_=dÔ)âQšx⯼GÌzŸØ0 w–è<ÃWÊÎ"„1mð-á™—ý›"3äÿgïÍ~dÙ²ó¾o­™UuÆ{îÜÃew“l‘4›-ºI‘”l™D€0 ëY‚4ÿê‚_ °>Y6ùÅ0@Ë mÈl¤H«9ˆCsèn^±»Ù}[}§sÏT••{-?ìvdî•‘5W­P8q"cŽ;V¬õ}ÎÅ:HYöH*g±³hÚ{ɪû «~þxôàK?ý—Xݹ;Ì‚k0Å ç–㙎ãÜ /H„9 œÔIß´ÓA:j¾àyª¡ðñ¯>ÉgðÆ_‚ï¦æ ©»ÛCz{×­É_™·êô´e,„˜= úaBÈÊMî/vÿjÔÙÐ@¢M© a5ÏŽ¯äû‘e0u‡¤ž÷/ÏË=Ár_+ ¡ŠŒ©Ìi‰ÆSÏz!'Iî# k©º…cK Ò¿ø7e,Ò”±tY¹ÕlZ¨ù[GI)§r–ŠjÜ ¼T½À>B€C™c)V\›i…*€QTNú)˜‚®Ä¥=v¤Ôež ¨Y?$Ô”âôèšåU@ŒiC=™Íeq£32ïÚ ÅºËúØ-N“—o¬]“íÿó¯àº\K•Ÿšš°l-ëœfY&PÙÕŹôzsÐì-ü«Ÿû þ“¯|?HhÄù×ì8@³O@ö^ðêpüÕæ—âƒþ~TÐShõݦ€ðÀKøü—^ð Vûн°È PÁÝ÷›R–FŸh½ (ï×\Ô½ËÝ[œ«ƒ=ÇqNIX¾À«ß:ÀìðC@'Öh\0W¥Ü…EjÅþQþÒ4­7DBµd„š0[2BÝ8’\¶í33ú?¢øh³<æ\ãµð?8ûÞ‰/ã[ú*I#nÚ 2ÛÙmȃ.­¾Ç6<}é!2fË wç[¿_µ8¯@Ç:–9Ž3…F›!>ÆÏüúOAãÿ}ÙääPʡƑEã{… j“aÕŠ4kÅæ‡F`2c¾Atǹnx”ã8Î)¡ÕS¼ÿ5Ðñw¹:uÓ¹“ÉUÒ÷h3=B-Ø?¤îo*)»#;fKÆl•DL¯zÐcPÎRÂ(ƒ)QqD AA Á?à'€Úªñ)”žô?Ÿƒm§_î­p¼¿Âj^£ÓNÆÓ×÷ñÑëþ=ÆqN…F¨® õ;xðÕ_ƒÄ÷/{‹œàƒ”-îÂv’iKËz}ÙâÕñ1Âj5Ô=ʯy&šãœ=Þ³¸ý¶sç°lÐÆØ?ø‘lý¼?‚èqaQº¶:ÈÓ:Î!<‡—&Í£†b>€bÙ„Ägˆñ‰µ°òvñòâ­ãA³Éµ¸¹=Úv3ŒÕ––©Â›ÓÖa`)ÿcjè#tí¯wláê ,^ûI„Õ ³§o7î K?,Ì‹aªi6¬¡]Pãèœ"T^”7ÉÚV]NKl 9`µ…ü‹xÈÊIBÚ§Õ~Èt:R)‡2ðìaÄGŽðì¥xù½Ø_ÌP-—æÇ}»«ò×¥êøÙ@A¾Û=žAùäsxçƒ~xuçaqš8«º€Í^v™*bÅhã÷Vf@=ï÷cU¸<”“6H+ÂÙ–x°ÐÀ¥¥ZÑ®ÛÒ~Ë]]=Ö‰¥· ]+siJ[Ú—¹¬šlQBTƱVxïáÇ:ÃRgˆÊXIØÑôËáÈÍò‡Œvx}I_vC…–4Í«ß}ˆ*ò -å%-y‰‘Tœ Ïqçiº6¹®Áõ²Y÷ZǾT‚1¦×a8ª, ÛÓX.óÐ8b“j¹·X÷ãæû…u¬ÌÏQ*å,2ÕUÑéB­ý0Çc°œn_ULm¦» 2îKƘ1ÈÒPÚÀÇ)Wœ@g¾ßØÂ¨.‘‚» 0½…<õ¾ýé%>ûoöSÐÃY£Õ –sê ÷vyŽ/þÓÈ·G‚©ŽãœôpçF£ò÷Þùs F6ÆM‡Ð*½ð·Ž'1(f+ÆÃæxéÃ9b•\Oê•›@Ûl†Ñ Úì…€À±Ÿö,iKVòcC.Æe.YP¢µ ­¥Âqœ# ‚q„ôïJ*,âµD© Â€„ô×e`4ÈÑ ™fë/•´ä–µÎ5§ xÍ«݋œêªG)Ü¡+[ðÔ¹‘ÌÞßCX>?yâ[HÊFšƒh€h9*ª*€<Ù0%â uœÛ…=œSC”¥féžÄû]m}^Æ `x šs.´ÚTSÕ°ú:°jÓ?ó”Ï<åÖ*ñ ¸Ní5wn‘Š+†2°¸¼û±ø«ïÿ6^z÷>ñÍ—1?ª:M;[&Ðö¶ \eHÕróÜôe/méÊðw!MŸJêT’QÏ¥ÏþX2*”³=ºÒ¤±óaŒ¿øç‹õÀG.N;(Ñ4TMÙ‹¤€‡(£–€%Í€`JºQ9ý¦Ë8C”‘ÄÇ®Xxm=Ã}hKZ•¶ Ùדô?Ö‰Uæh“ÏK¸6‚Lyš¾¿œ3)à‘þöA³Oãÿû?g_àoüÆÇ}ˆÇ¼¸¸´õ¬Ü@)v©ü¤š;m \z®¶ ô¶””@œÎ!ƒÂËýª ªVw œnžÕñ /¿S£Þ¿ƒzÿNï¬$Šé!B*ຩ?TÕYfå-ÐùÊßùÌŸù­ÿ35ºr¸¶ïmË[B 0êú>FÜ´¿ÎÏsV^GXô¥2´?(Qr§Œ=œó¥«ÃõtXç’ÐÐf§PUv´¿¼zÐ×—°"Á³Wžâ¥w çoÄ?Ó¿Í‹§2¿)G¨'ÅâNÄj±8X¢ªî>ßëJ\ø ZŶ"¤ÀÂmlaž¦äDY!U[§O]ƆDE³‚(wN-Ò=š`‡´õLÀ1 ‚%mŽn=…ÿçÁ|˜OAíµQB\³#n—ÇCñݼ]oÓÆ3B#€‘§òUý=u@µ´‚‹¤!5 C·ãÌéÃàÆP‹é&Ä6u"ΜÜÊ”BwN•x`‡žÆ5AñF/ŠP“:§2\ÖT}¸ëMÄü¿¿Ñ §R°“PPwû˦7Úñ ¨‡ØkR_éÇ´[Ö:Wz8ŽsãÙøzrË&ì-€7Þ¹‡×ÿ×[û­é¸ÖYÀã+Åóû |ý‡¿Žg¯<ÁýÖçpp8ˆ•87M´ð>Ûƒ„^ ”dÃ$„8«!U ®+P#vªÂPT5Ðé{´´™ * m‚Üdz¤?J™’es¬ ˜öãÓ¸^ø”º€çA‘ÀÇâ âßû]|â¯áÞóY³ü sޤÌÌ%b|ª |áŸ\½à/{qIbÓ|ÈqnILøyÓרV¼36Ú2ç8ÎùàAçÔ¨.Ó—š¡š}/ž}úïâÁ_ý¬Ž~µ¼×L“×ÿ^Ñž®¡Ä®º(*•'•í3ÚëI(®cLôª¤æPYÙÜZ·•~K¦É©ÄòÚŽÐ|•´¦Ë·)¿Å¬Æ6nÔàŽ/,·Ã8O;|Ë¿‚s]w%.\·)È€jÂjÞ—­Tuzñ 5êd#›>bÕoKœ•¯°2®2œ Û½³£r½8×ûƒ²nQÌ}¥4,yóx@¨ ÷žíãõ¯¿‰O~å-¼ôø.fËá²·±¼%¸YŸˆýò¿á€¢4Õµ+ û=ËÀ˜íAÂ,¹¾äå1¡$4šmCÏDNU è¼æÂ²¶}Ò‹™R£÷±>MiŸBÜ<7Šå~ßö…wž§TýN7& u¨¥Ëô Xw× iæò$KÃ:2lד£â}Fui—G˜_aˆ`,‡BÙýƒŒ²Ãª²;źãÃp› Û›ÏõÕg™Dû€Fˆ<êáoí¶ÜB6(ßËÈx~Œ2p"™·ƒe©áÒ“#ò À4=Ë¥År{±öù~rGÙÀh °QÞÒ¼PS3ŒäÒöc#:»‰üϯn {Ý};Î÷!! Î+¬öbE%É™I1?¬V5XšÜ3å¡£ˆU¦ƒ'å¶H¡èD§ˆE§¢ö·I¨@J×§€åÆ–gõíHie»ÿ Ь"•º´Ël»uþÖö»Kô%hâýÊqn#ôpNM’þknÜò¾ýU zÔÔ+žœ²wU°XŠS¾€f7¬‡å˜EâåaVŒö”’\bÇ}Û¢-µïeÉthÝ·óò 8U¹R穯·nHÓ¤ÓA]Ær¨û’–6àÑJÖ¥ Ð7Î&ÅÝ †P¬ºêAjÓ­í[‡•0_¼ùÎ#TK˦vÉI¥¥eŽ1ª¡„öêhÜj7æã˜ÄG•, iÆ Ô€6V¸]êÄú±jÄK‘S^òò–<Òÿ;]Ãc=s†•ðÊû†¥d»¿V¦‘Æ>økÙ0ªýuÕº6mËì±eM{a¶ÖOá^Dlàí{—”õhG‚òÛ<ÓZç–4Ãr Ÿ1Õæ|¸`ëZ æ3¤×2–O ƒÙ}Þ Ì³i¿ka¾ÌZjËžBq|Á,ˆ2kÒ¦í÷úsÓ–Iì$‚™ÏC¡;>Ê ©êyÀjNˆ°š÷âÏ@ "‡šÀu^¿´v.Í`þH;*Ì“úÓEtKí=2ëYoVŒÛà ìÊê!ëÓz°#/]1®Móž´ Sz¤wk“§XŸãXxÐÃ94BH| ]ü.R=ð³ËÞ*ǹոZ+ÛvšKÜÀK í¤ÏVé³a5“KnmÚ•’$¤,"ƒ!’^.Ò·NV‚2CY€6èÑîTÍ¡˜´¶”)»es?Â|ÑŸõV8“¸zM¶F·ïÃew/ú’ŽeKþ10Ï„ÈË=$r—õÀJ@û[TH›!è¶e%е, IÙªÒ¯KÂ’‚hgnJ_HAÂ)ˆÑ:¶ÄVÃÁ¦»Ü6ÈÁ2ðhËYZM\ÌTI¡œëf¿…0;ê¿,îçŽ-™X隈iW¶¥’¹¶Ü²(Ý%¢Xv7LÑØÖeŸÚoe5\y©å0÷ô¢7æ|w÷^Ƴ7^ÆÃw¾Z~Ä'€™usºëCª9$TÍ0ãè^À³‡‚X –{Ïï§~ A8=€9b+†TÍùqææwª­ºÞœ\öb•Ö¶ (;õË¿M‚±Ž³;ôpÎŒVü,ÿÿ™•…8ÎS 5"¶{_×(ȱÆÀÙBV]'‰c„,9ýQŒ ‘ Œ<ÐÑ–Æ@XõÇÄÒô¸lHÛýò‰MûÔÿ?Ô}–K~{Ê5:†ó«E†î%ýK:®&$}9)5Á8 ƒJ´͸\—Ð^4”ûla(§Ò%¬9À´kïIy-àÁMVGXôÁL÷#‡óˆ u<º@G6pîH“²kús€lØzx°üâ<³×µ¸4Ÿ®½×^À×ý,à²É6º×å9ÞûÌ+øý¿õ—øñßü ^ÿÚˆO@úl Ù¹9] ZBÕ=” _ù‘ðÝOÿ;üÐï|Gw–øèÕ'øößü=̾óq|ÏŸ~óãæË uÕÓ›€ ‘õ-{/•ãŒ^[ÞßÚ8ײâî-ÎÕÁƒÎÐÞd‚9ç*!’¤uÀXÌ+ÆÇÏAÌk/ì·Ç†!eV¤0ØE—³˜šÒ”&K#Ïä Ft¤U" ¥<Óƒæ +5IS–·CÒ>˜Á‘{×–, ¤ ˆdY¹/wÉ2>ǹhõoüÅ ?ûí×ñþ[ÀGo}/½ýÎ…¬[˜ðCø >ùõGxq%EXU¸3?Ä+í÷ öé­U}òòÇq.zÜDŽ [Fÿ‰ˆ¦5 ‰vÇZ”[åЬ;¥]D­& [}JÑz& 8ÚUÆùL]à;“&'ÅäÜSó‹ÃÈ—ÙR fZuy1ÏŸí`»Çd_“󯕹 „Õ&û´\s"ʧ±Ž‰åê2r X ºº  Ê`Ì_¤r®— Xƒ›/rÊŒ(†3˲?˽Z’Õ0Ó$ß&=Þœ^cÑ 4”¿Ô¦—ýÂñfûûá|¹YDZ!xÊBlfnÛ¡|š<3"Ï‚¨•@±É¸¨Û×& ‚ÁÉ]¥„¥ËüèÄF…A6G²ªE3Ÿ¤’Ò4'=R9 rÛYapÌ}P$ÏÉíkÛáû©]Ì—3Ì–ªUH%/Y†È,rw|’nJL™Ëþ?³v,½;ɲoãZvJÒ±kpä~U¼—éô,Åâ}Í}¯ðœP¬Šn¤lŠpZŽ–[Grò0ž-æ½:+Ôòø´-¹°¬%Jjl“Ê (mÞ÷‰ö»'ðFy¢¬ý?[Vqæ3xLŒÚp=¡ò³–ùAï0“OÏ@ᡱ~ÃIL— ãw1_>Æ+ü½Ç ´NEýq ïš"®¦CÌp< O|WÞÇ‘q÷ùøÿø»`%T« ³eVB]UE]F ‰ œ½Û´î¾Ôý–ï·Õ®ãô¾qn¤›ÇÄt=@Ø/¯ƒf€–Ú»!Î;’Éf·Ã±ûXa“F²L¬{‰-è?-óNÕ£`ÎÕ÷©ŸCx»SM<³ŒÖò-›À\q°Ž=àRbÍÎÞäYŽ+a$x3mYcîd퇱î {Ó6)˨ß~&«“m0bq«Ö14œ M;-­Ätã°<—ä+¨a-:õÅXGÛOyY—݈ˆî%»ßõñáe <(¬ ƒ ÍZD=ÂÞû_êÆŠ<í·+¿‡òÝrÀeL{…3])ôÙ$ÝÇf«Ð;bN燅+E='Äyóü†å-y½U÷ÂÔܼS¬% nZIã|L ð)­Êý]æõÁl÷ùJVºJ¹5-Ðöa†ýã¼_3U£Ci9-à˜öÎj®{ZÙHoõòçêàAÇqǹDÚ€BÊ6IYIš1RMÁ‘@Hi‹ŽdzPö…¶ t4ƒó Ù ”Œqï£{ X§ÌlŠǹބZ€p –Ž÷5ÂŒ!¬˜­f«€ù‚ýú?EDàGàê ÄÕ×!ZÖ9Žs~xÐÃqçÚ-3¾úŽ•\å_sÇ—¬ô€¤¢3¾hß$8ÛE€Ñ—côÙó·Áetú7DM›eäî-yBYA‘S9RóÕ“!Ó¶´EI“» 7Y íüͪ¨Élé²=t(dÚZÒæúè‚-YFG6d˜éÑîOˆŒÐd­Ì–UW"w‚¥¡¦AyKç’#kÇ7?žy–RþuÖM¬•'dÇÄÌÚð¯žW ;#pðE~Ðö3qøAöÃȲ¦²æþ¥Í3„g3(B­˜-Ó½A9`¶b(çöߌÙ2•­ånL6·­]F<{ «‡ŸCøà1_øõé8Œ=Çq®2]g8 Ò‘[BgŽõr•Þö3¦’“õÅË 3í‰:5£´Ûn½À^ÂËlþÂÍkýó’°© Ü[P}ô8iYD h2,bä^ƒƒuYQWÀ*)D“ŽHSL£ z¨TZ=Æa¡ ~ 3>Z§j4EÚ²Ò>¸ÑŠ–†UÕÍSeå-Õªï"ä%;U„OgË ³Uhl+Ù’‹%-$½Ž‹[ò’–õ¶áÁŽyÐcXâ³nyyó–×@Wša— f±Ê#A¯¼4b‡k&+ áú°Û®÷» P® Ê„Ù2@9ݯXÒ_¨j™®k®Û íú}r­MÞ–k[¥y¦Fèþ§ðîgîãOÞ@¬¿µ]욣*¦¶ßy­Ïq,<èá8Žã8—H+ ˆŠ° 3 RÀƒÛ ‡tãÛÎ]_î’‚3]mìKV’ )YyP¤´=æ¶®tÜÍÅqn&\7Ée²óN‚Ð)èQ1u®_\+ªe~Œö^ýì7ñ‰?x õòÏ/yƒçvâAçÔ˜QÜ]"®[9hœ·åËÃ`¥SÛjá)½ó,°…v·ûr¶ æ×‚Q‡ˆ©lqLòý¡ 8žJ/ò86Ïà˹ Ç·¿Q ˆæ_Ëšx=·ë(*×Osÿ!ã«ÊjÙ|Œ‘JáKcb=»ã¤ßd­ƒ3'˜Và”%e„tÓ䢦5w0!2b£‘·V¶*Ôep´ZÂÒ•³Ä&óƒɽèK_¶³Mm2;Òp–õ1(mÙÜßÜ…2Öܽ%¿.âÚ-gíXZ¶ÈÃ’£žÖ ý¬žQcæóƒQŒ \J–³Â]~yåç@ïÿ$>.nãVœ÷óNcy¿1òüPÇje¸­ìÌ´ó2‡-–Êú–Øw·{ÖÄ'sç™<»Ã Ÿ^±Åôk×#IºÓHsü«e„2A¸½þÔýõ÷Ý4¼v=†û2þy{±}°1w“m܆Ìl‘õæØÕñ;@üΉÛ5õ¼Z|Úþ6eY¦PÞ÷²k^IL×q.z8§g¤“Uºy—®ûß,»¬óNµ(„ÆÉi{4Õ~ÖêbúÃìL™èB“œ%&¾,Á ÓŽoÌjÍ@Ŷ5.ïˈK‹¹£ÓªóbG@9P@3Ôû/¥AT‡¹J³nbhó4/ãÍ$,ËÞ*VW°:0ùºi ‘“Øh§k£AgØEAI\Ãì²–ìmB]¶¤µ2–öR 5º¾ò°&b¹¼%g¾L¤®b7] )  A›i$â¬îKX2«% ë™ظY”‚ëî.C‹Z¸·Ì—}#wÁP÷ó¬ëx´ÃmÚ{{,ÛãI¢Ë,?ö×à üJWYÛ)å`\›ÆË©éðaº4ègì™»é­{uÑY€lºº@Ø{¼ÿyüÏÿÙ·ð÷þ›ŸÆòÙ¯÷ކ„µÞÉN#c¥eQóª\œÇtÓq¬» ßúº§BàÉÁqëÜÖ«oÇÇú½â:Bx›{8ïƒè^¿]Ýó.Bäi¶rÃéFf`‡JÉ*€eSšYHïö§@D)ðA"¸uÃ$2°šî¬ÍE×62—?Õ£n¿TýpøÙõܘ^²¹»Ñ6$§­òò­>Ñ´u(H ûHæ‡ã˜ÍŠm—FmJ¢’K#àî-ÎUƒŽã87³Ôâ8+46x¿ùz?ÑîøCB F¤Kmãœuð• —.¸ÑŒ_³u®!¡Ë¯âïý—o¢>þíËÞÇqǹ6xÐÃq窑•žätéÂ2’-Õi=0 ñ”.,g# Þįþ“/ãͯ½…/þÓÕ•|LMÈ¢Ìù%Ñ!X¨+oI–´Ù|Ų¤°RJSt-†§&[Nä,7ÏúXŸÇÈLÉ—Q*aq.ED¬ßâÉòÒK2¯/Aöc“E±õýú,Ïý0ó®ÓRé3ˆŽVÜ=ƒHSYKG7>›·°Çqœ‹ÆƒŽã8W•\C¨^dõÀë)¨M“âÔÍ“— ¬;<å;f:ìÄÝ®n;€ëCüýÿüÇ1;zpòšõ ~—6Vf´¡5ÀáaB J]úáY¦ûÁÂÃ’˜5«Z`¨ï¡¤]‰‰A¬b7XÝvË!=1ƒ#/cáA$QÅÐ¥%†´-@*oi—1[öå-³wº(¡îu[÷ß/÷2^ì®?*/R8B–fíÒ¹¼Zè*« B_¶©š•Dœÿfåå-ñE÷l Çýóg ïdꨬYNǣ짡NI'ĬKÜôë߬7 IDAT|—RÃFðëÖç8ôpǹi\¥— ¸Àìè ¨~ÑëŠ8'Òfg´¯º–’O·1N63=xMÏÃÊðp®&íËSŠ{]¡kÜqÇq®8ô¸üwßù#0~ìþ›øñû»ìÍqç<ÉDM¯ ²-—äjd®m¹K—ͱ凬õ@Hn/Û¹³xÉʵD AJÇqœËä·Þý—ø­÷þ%j©/{SgzÜþÑ'ÿöx»SM4±-S¯Þ7–UVI'PØ+̈¥Zë˜_î˜ïƒùAúO¦T¯ˆPyÑM—w¶[Ù1ÈÕÅÓ<íKeó¦„jD¬ V‚@qú]ªˆŸü&d¹ž€föoÆ jwl7–U.HmjÅül}Ùy2§ÙË‚ À x+vxa·j´‰Œå†¥$k¶µÝrCß–hù±ò£â:T—é·â†M-c±Ža0–e;àPxÙX–¡Wš\6\ÀA(ß ë½9”7ƒ±bHÕŒ—¾ËÚ´Cñüì·9b;Mo_ TˆUoYÛ–•)BèÛL̆sÁÒºÊPFô9J–³,dΓ—·ÌûcUå%0u¯ RÅÞ±e¶ªæt¤’–vùŠªql!ÑÛÕÙuž· y^toQ92ÜXbr…(ý"Ç›N%º2\dºc€rí”å1¾üä>2C>ÐD3H|?{Æå×C(ßÛ×J(rbü°¼býAea>·-¤~RþBñyN(cªËÆØsœÇê+18¢®€h´Ek#}†’[IŒ˜ý.Ëm–ÝK4¾ßÍOt7[VÙ^w@fE»nqZ׆UëØy*sâÎgë:×ÔúoƃÂÀjë£ö·†m»UöR…W·_ÚûUa?Ë:™ïÇ ¬kyXFgñ¾?ñèûðdùÿÅŸÿJês_`VÚE®Ë¹~\¡OŽs4œíŸã8Žã\g4‚ø.¸z8øˆËÇqÇIxÐù¡$rjÿ¥¼Ó×Çqǹ tºü:ÿ8/} ]òV9Žã8ÎÕÆË[œIàGàÙ[)½^Ž ò¼)S95©ÑŠ8H­Ü;ø[ݰÊs´©™uý­>¥QWÝø”žxþti“†)”ºí‰)š—‹Uö»äâ6±‘•ÔŸå츹Jù¥°^ÖÒ’»Âð`xXFÒ^ÁUìPr[[%§aå-¹›ŠðÐ"÷$aSάk×§Ï·wï8ç¯"eÎ,4Ø÷¶ÌG@ÒZõ*¸nÊ[Ö-.içëm:K»w-䥫ã?-ÿówQË‹b¹ŠùmíKÌÜ;nsÞçèK\òk1¿FåÊÛ‘;Žs{ñ ‡s3¡9À÷ðÍ/|ÿêS„¾Ô<̳N<Â0h1xƒáöÁ¯ÄWFˆ±íÐ*}9+²zÊ5*tÄ.õÖRÖÅqny0DÑR ZY³ª*úÀÇzÀc=ØÑ­OÊ2†ó@Žiû;éuºI]u"D€.@ò´ må÷ g7r BÄ0!|$ØQ\ÖÕè+9—‰¡Çu®ësœ2^ÞâÜ\Â]|ó3âÃO>Äð+…ã8Žã\gÒË„%†è8Žã8Ngz8§†¹ìÄÂÕÃÁ—ü¢½¢"yRØ.—iXJìÄû l*’Í€x„/üÆ[8xüW P¤a–ð¸ô¥É -cYõËü‹…uˆfݶªûdåJûN¼æû˜Ußƒßø?ƒÿè׎Q?ýõn›Tz7»l8fìûÍ>(ý§9@@x=€j-ßM‹ÔµœN\~ËYEùuXŽ+»@F Ž€PVVL¶îtQ@u‰â1±Æöþ™ç|ä|˜©ÎSS ­, ™œå–%ç„ráz– "ýø¶¼E™«<"ß<[¢[EB^ÙÑ~œ ¾,„uÕÑ–±ä¥-°še_Z³ßò,޼ì%DF¨ûý`£¤%'Ïî±_®UÊC‚¬ìEêô®aÕœsðÀÎÐ(uѸv¿l‡—¦KK~Ü–¢# [{7”žu»0¶œÁ³nàê4ëKYÙsËÉåæ³ ®µòw¸¼Ž]\Z.³ÌŒýÊç1µç‰vÃZn.gRP0ŽILŽz¥UדȰ¼v8SÁÑNm§5ÝØÊ˜Žr€éBCTvº!(•žƒÃçÁOgL\/Kn—O0Û®µnRØÏaãÜÊÇqj¿OÓLÛ3õ:vœ ÀƒÎé±)-u ZQÑM2ù…r¬£)¸óîŸw>¢Ýè@6lÛv‹VdçÓ £¦Ôá§ÿÇ÷ «·Ñm;Û±±]qz©ÄäŽüIÁüÐp€8¿ Š5Â2Û|ÕÆ²ÆSfKóLþ¢å м¨¹&tä\¨Ù^m{ØRJ²Õ)Þí× œÔ§*uZOœisƺ9ÖP)uNi`eÛ•¡¤¥¥q²Vz2r hƒ "ÃTKi^TÎÁŒÃÍš­½ì¥¦í,­ýVZîz`ÄÖ-ÙÜÜiJnDA±Î~³^üÊéóÛ1bh]_lvÓÙåšÚnžöI­KY‰ë¤ýCe±ñ±{þU,¡ \™mÈ´6·‚Ð;´+«-dã‡Ç³/&ÍÚØ à±V¾`¾È[×¼u¬bñƒÖÖ‹¼;£O¤Ñ^ÖCÒò­shµé $œõ}Dí>ȹpEÊÏ«‰=ç º‚b‘§ÐÅï^ß”f Í aq6{Ñ‘ãÜJÁÇqÇqœËƃÎDä1xða¡ùZ. óËÔïôú7)ƒ/êQN)ü"‡€þ»æ Ï5è \bvØ|ñíŽé™(7îÓ¿3‡ ZooþEãÂh3”©flfx””ˆÔ½KK…õR—>«"VMYˆØÙù= /‡É³3„´+}™­B}rZL¡b¯Ùö~x¸Où°vÙ d›L³`í«³3¤–Å>CÂï ÎD6úD]Zu%:ogv\ÅÌÇqÀƒÎ EäE§’ÊBšºF]šÂ?(/LW]°ã¢3,Tc—*«²€PìkDóÁµêèF ¥9nq7„Br BS<‘w*×4œ‹…D¥.yP ?}©¤¥)o" Õ¸9B ÔåÈCAú£Æ‡™ Ú–˜ ÝXÖ5Bº¥ã~¿ò2’>ø“—´Œ„¶l»Ý½ÏÛúÉø1r΂àz{ÑÇM-%sn'Š‘¶rNës z8ÎfPL iÁ²4/®4mЉ<Ðá8M´N+ðá8Žã8Žs“ð Ç-@êÞî™h6ªH^Âro±ÐÜ eðÃ*•pLY¤(8Ez8Pòn§QDže è#Ðî—WBÜ)`>v` -ºÍ08Ü1¶Øù2¿ŽäŸŒû4ÓäFÃÅé,•t¥P”Ú'Ú[Ê‹–Ú{Að1MoYÇd|¢H¿›bY¹@š™ì.2}[DulÃÇRU·Ž•,À«¬J~Œ¬1û}iˆ†Æ²ø©†}¼{űT½Ö«éËÓÞiL\ÕüJhÍ3æ3-ø78¶ƒuß)ž[RAÔ^_©ÍΰEåb• Ñ® ~¶N9ϺÈÖÇ@‹hç_/;©b¹¼E8Ëâþ5w¥/›y:——|ËY#ù4³ºß®P÷.-$:/mêwHզʅLµNŸÚ³áÄ`a]³:íË`öÄy-¡Æ±ëߺd¯©qשèˆ=îD‘C6\6@\v|3-U㎂ץ¦‘–hôW¦ ²Ž?»]¡Üd…RûQ0ˆ ¼m²-G2KOûu½ìn@Tvc²ÚÃÐ0_õî-ù¶Ó T:¾4,qüdʼn —À>>æxóœ &‹™šN7™«Ë–¢¸ZÜwÏìq®ô¸ÄÕÄs z„Ù£‰[$PÙ ¨¬ µa½Ée+[‘Ãbçbl?¬NkÉF7-kßèlFP©žìÒ‰åý³P£#¶[–÷ƒ)BØêŒë±b:7Õp¹Ù(”`ž–.è%c]†ú zò­öbÈËth¿ n0úÛùPSe¨ÉT´?¾ÈÏu&9—†=Çq®9´zŠ ’exð¿ã8Žã8Žãôp΂‰jöª«¢XÚ¨J¹)ˆ(F„œ7íšé'¯Ã*O!Dó[!©aÙKDŒgŸé±³"a•¤œ~>–jGëËIvÛÌ „K-ÿ±ŽãÅØÇ¢(Y²¹™(ZßOí{J·oS… ·˜þA¼³À;‘3L¦Òõ±Ó’Œå[æ\Îêm³6ÚùsJY#y¦Çú<íôA†¢¨£%4¹KKYÖ¿¿¯H/¨I*kÇyDœ×.º=àlOÖD,‘H[Àq,ýÞr{;¥¶;â¸bwë¾?rlÏû~©MAØ«ˆuÌMQÉ3¼_jék;Z§¢© ;ÃT­ÑóWê"Àj— çûöwÇçìÌ]k݃k Ëü¥VtMÜ>õ‰¦o_Ñ­Ðp1#ðÐ¥ÅÚ^c©/;Otp»Ú‹I§6ÕʳZ&ºÌ8ÎàAçÔH4èq¹s )«‹c,ða<€ Ë8â™ÝÁ0ˆ†õ®¥"Î|ÄwÓ0탹±¼¥9˜[ñ8Ù†7'9`LëxL¶NÔYoç6XP,ªÃvÖ:é²ø*ºÚÎ:-ßZº[^‰]d¸Ó4[`Œ·Ú­ÎH°-ë&buäUF25Ìã"ëЮÙþvn*kÇ ³û¦C‹»lxPÒ×jœm~lù'Yún¹nRÁÀ%Ó©(9•½³ÉƲ Zc$íŒl³K¸ZÚ÷…6pÁ™î­ép(S÷oè˜/ÊVºÂ}ÙÌКVºòŽõàXµARÅãf8l˜U G++ci—ĉNPºƒãqÙ¦œiÏ Ì•­Ûíg†yo7÷oe:ÝX/æ–×Ae«`¥Ù ú %¬éǬ[¯­”BÌ燜2­Iro)¸ã,Ë ÌY7Æ^Öy‚Ñ¿²>‚¥õûÂËý¼ën*y¹I¾¨’µ00lqGË•›ute!Äh­“K]ë¨vÜôí¦}ð±ú´D{†û`s¹•ß6–K„9ˆï‚øÀ÷@ºìïÕz‘§iR‚íx®rf¶Ù[p‘ër®ôpÇqNAnYº@Î…f{\bóÄeÏâpÇq®#)ë—0G˜} DsèÁgÃ?Ä÷!xœ>ú]‹çcèõÝïK zþ—½AŽs*<ÿÈqΕ¸öç87…&¥–Âp˜æýÿo)$ý_΋û‚ßþÙ¯áÉ#é »¯C‹@Êðà’»JÁu…šiå*Ýßp?ÚáÒ²7Öµöõ–4]~.ZàÎqœÛ‹J“Qr¾÷œ.k‚ˆï}ÿû?\ ~ôS :ÀU|.ÚYFéy~øòCß32Àçá™޳#)U5¥ h£é¡«dŠÝÊSg:›é¥í°]Ó?‘¼¤%<(üúYY€ä9ô&º¹´å¼Ý7ƒ<ð+ÅKdÙ*×Í ƒÛ¹©XÃ]9N4«­’ªWèÏÑA9N(Ê$m¬?w*h‡ÕhŸŽã8gÄ@oCW(;—ä:mSõhJŽ0i¸Ññ >à¯ò!Âó/ã‹ÿíCh|ªK¨.šÌÕºú *M‰ =ƒwÞê÷»’–<`}bÿ¢ 6]ÞßvFð ‡ã8޳32 õÞ=¼ƒƒ'‡¨ÏÀÇœ(vz¹ÿ$àûÿì5Tu/*L[eN8Žã8W@#è.ò4éÉTžBôEÒ\¹Y¿1é§IÕ3tǹ¾xÐÃqÇÙ™zï–w÷ñäU€äwb ^Þîò‹j9,qÇqnŠ”ñ¨´jœZ*/RÊËò(¢ªÂTâš`¹ã\O<èq P©·NqW¬&—<—U®cýÄšaÚ Ìæ¯Oš^å2ñÁÂá~yYÖ r89^£qL,vx8Ú‰eÒMµ÷1˜ïÇ[ÇŠh¢‚z;å"˜kÛ5ÑÉCt•Meõ±ôU®>V^ÅÈ1´\Èj×YYñÁÚÂJîG)v]Úéú¢F;*¥uó¸Sæ²AqÞ‡°\`?ÖøøS ,>Õí¶Øm×>¾–³Jù+“ê¦W,· óZ“8\¨tfžóji¸ipêÍ•¡3J?~]›ƒêþG®§GMâ¬ä+TøÓÿ]üÈïô÷×|}\¯»¿¤á°Œ]©LîÞB±×™óHëÞ"K ¾(o”ÙFcÙbÔ†µÜ:g­-Q¶ê#6º1>.ŽZ¡$ŸF4ëmγû#ñ]TÕ§Ú¥\^<ÿÅu0ß)[ææëXKËoݱˆf¨ªO¶«;îºl¾J·®™ v3 ó} ¼º±j…˜÷±Ò}ºgê3Ïr´1]Ó&ö?ÆžRpI]UEç±´žòvYûgîw|^ì{Z”Î*»`õOãb·ÑFêÕwŠãÉpPbÚ+>tÌuo¤_ÒÃ8oaÝÇÌû›ÙŽ-9D-PËGÅy$>3·kÒ6ÔÍòݽŹJ¸©ã8ŽãÜBb|ðñ÷.{3œ«ÝKV•´мy)ô¬-Çqçz㙎ã8Žs ¹÷QÄOý_?|Ù›á\!(ëhËY¶ÆEmÇ!îËÅøhïûÒxÀòë»/<(%ëJhæ;͸9ˆ{7¦Ã7>ÙѳÃ÷@ËwR  hPJ2#!¿æÇqœ«=Çqœ‰ô/9*KØÙÝ–<ØÑ³ ƒ$ç!~J¢5­‹«^ ”“ÎÉòÂF«ý ${¨Ž÷“NHI+ÀqgÔÖA:·õ9Žkz8Žã8Î-„Ü*×Yc¹Oˆó€X D˜Çqç:ã™·€öá·ÁDøÑƒøÂ'Ïà8'0UÉ;e”Ç ' àtÒŽ7cµ0]AÊ_+©q0Øüa^tCP Lµn[ûò‰èÖ·L·ÜMÎî놩~®ÏËãM®êbkÿV@±]—Ûáèªr.—‘Ä*ý¥iú@„2!Ü^ÚßJpm9Ç ·© x„Z€ÌLcà#ÃmiשÌ@[â²¶Üâ5½^¾•µWË…¦SJy|rV1Ü ŒûÕ轤0!ØîֽDzò6IŽ1b|áðwÓhg&˃hVÌ!še%- BîsÐ,“îH¯~ý@V x­ßšûBº{ÃlþÃ@•[tùu¬V_Ùܨ‘{»å”–3 NI¦›E,»Ö»¹´ìà,7³ÍYû1rËÓ×?âHû™ºMWókºý ] mH´ì¬Ôü:qí¡ØNi‡¸²u/»Ç”έÌ{âyñ¯?øCüëÿõÔHǹ<èq øû/{ëSǹ5Û®ÏèÔÍË/XB­eYGð2;”V°bhšoc?½Ùy]Vùs–˜–ƒÆh¶mµµ # 1Æ0ØÑŒƒ%ùÑÙem#Ÿ—Ölq-ÆÖ·Pé–­å@ÇVXÓ«exÆÖˆ¥¶<õ´]Ô Iµé…4M#ºêïtãOÜ&`h—¹6¾Ÿ' ²÷ÓSžø8·ý\®¥šíá{Ъ±¼­Ëöå¶-Ìa¼Ô‹ç0?ç—(âQ[×éLès]P “y}L]ÿUµµ®OãÖ¥;hiM}F)äÄëcÆôfÐê %íÿä+?ùÊ_Ç“å3üW_ýï(¦Nƒg/:6WçJqÇqÇqÇqÇ9C<ÓÃq粸Š_ËLÊÛºmvÇmb½,dXÒtyÆÇÙn“t¹xéè6e¥2Ë;U—u²' j¶—ëÒ¬$æZµéK¦=VY¶AÎí»hñK±F(úò–TÒ’yÑ×£neõ6(¦éb|ÿœ¶ÖqÇqÎz8Žãœ+WUÛâ´”öë¦îëùÂR)µó Tê’0fÄŠ:W™XñÀÎöD [„Œ“kœÐšÀi‚ŠMM‘¼\$_Ÿf"ÐeþC·±þ@M°ã‚5ǹ>èY—n±>DZðOtŽã8Žã8Žã8ŽãÜH<ÓùLjs†‰_U0U Êv°fÀô¯pS¿xZuc³ðÞ´é åÿQ&¹µDíV°Î“Š!¨ió3Ó`L°´øCYÈëAbêqÈ–ƒãØ ¤©nÚù 蘣Œ!îV ,»V¤í:Êþ—oG~ WÝovÙK~A¬cš3ñ>¦g—X™K ÷¥AEG’éEš’X!ËœV3Á{o>GU<øh¡¦æöÒgb䢦«ýL@6Ûe*ZÑ’¨iQËõÉûB‚î3‰2u5$¡+›áÕZt ™ýi\û­ÜNe±Ä”¡P8‡*æ=\³2Ღ1&¦i9G-ÝÃuU^ÿÚñËïÿæ3JWåuPèÛ.5"©HJý²e/”+í[T»aÒã­ž}–ðâ6¢¤ ÉÎùܪFû˜ˆ1~ îš/Lìç ±¯Ö³v²C ñô>‘Ñlò²FÝBŒcUü¢®R¼f»ß&2õ«½-2*¦x¶uícewÛ=jêõaÛc¨–c_É)i*ÛC•£“'rœ ƃ·UnÙ»»ˆú|B˜¬€==H·Þç–Éiq*ç.MàæÅnÂ<»tx¦*’OYv 0;IB–;£ô¢Ë´¢ò±²;¡ƒ×Ç)ªï/ø§, ™ØH?ÄbÇJ±²;n²(Ž[Ž/s”O4ƒ4SÛk°WæË)ÁÝ/·QÝí^Ù:êª_Äj&ˆ•âÉ£çØ[Ìqp8Ik?Íúå°ÜÏ\]ê|³x0mnMkC,ÖËVXÚum0F*îKeBU~IÊíY7ÚŠuþì¶Pl'#N "Sí–§Ÿ[»íN”Ø3”—%²(Þ÷Ig}ÌL¥ÛAdÚºE‹×ñþt‹MƒÁ=) h¨®Ê/i*X~y#¶bBù8ZÁØ/ÓvÐcªÊHÐcâ˺ò ¬åí2]³Œý`ÞGùZh!¨¤¡ÆùØ©5Ë¥EV“?PY}aë<1íÛÁ´b€È>fð後è˜g,<íY›,Á·?'ÒöÔ²?'¼”ÒÁƒŽã8Žs‰°D€O~ã5°ª%wÇqÇqçtxÐÃqg ùW¿î#ùújþ5Æß^2­S‹BYêô©RY! pì3CÀÃŒŽ1òìmÝcÊËéÛq¨µ˜u’—ÍË€gÁ×êì~J4ëo©ºÊJþ\,ÕqÇ9ôpÇÙ 7éøƒ´MFÜà~°ÊBÆÖqÎeSÎ)È^Òò €å`’WÁ÷ÿ×lX8@”û€‚²¢ª>h'¶äïLàBÉÊ.Á¥~žj™9Œ0uú"cZ!ŽSbX²™=²{%èu8Žs-PÈV>g¹>DZð ‡ã8Žã\”i¢^n¯æ8Žã8Žsz<èá8ÎÅP˜šêÓQru( ‚ý·D¯8 øÊIJÔÎ×9±œ¡xÖuâÒXÓxŠó{Åè0¸??ăìŽ<ë#Ï´ÂÁ‘µ¦¥M–G[îÒ5½ºÇ2\N¾Œ|q)hBÝ¥¬m¡L škd‹2"¶ØÝíºì1"ŒwËkTcQSuš0àÉë)KÂrÙ( ï¶âë^iíeâµ¹0¤b†6c.oµD¡( ©#b·£Åv§‡MÁÒ]áϲí–î»#×ßTáUÕh»·\U¬m3ÏÕ9ï •Ý©€·´ð¬MÓ_ácï8Wz8Ž3«e¼ä6 Åå”â€)Ä>Ò±ŠÁ5¶K˶jD¯KTÚé¹qº‰YÐc_6u» IDATPâ‚5§Š2·‰m]l{Ï‘ù'¾lZ.Û ;Ïœ:iW©.+PnŸœkÄFdƒy`š—{$]fQÜ–²d¤á:bÕ¼ð‘BX"#ÔŒ*Ð,‡$¹¨,æY@B¨+‹Ch¯$Fµ0”ü¹Ü®Âj™ £³‹–ª”¾tûªÒ+°U,’}qéz^mÔxeäåÔº†w¸”\3TW¦ u †ðÐXõ¦]%(…é¯Bæýº/Hùú×UѾfü &¾ÛÏ¢+ˆ&Ë\Òew|ˆf(5Q•¥iÖ›Ä{å—|bÓ¥Årº0Ý[&»¡EÓB” G2Ói³M«§´$XŽHSmîUW¶K‹Q’D8KÞ1¦_ÿö3Êtº –\ÙUŒ}·\ÏnS ._k#.-—Ž»·8Wz8ŽãlIœÝC½©ªã%æÏÞI?Œ|µqœœõ,eE ’´<ªˆå¼Æ|Yaï¸/Ä€0 ÔcÇqÇqœ“ñ’aÇqœmaÆâÁÞþ\D½·™ 2‰kÁ )q&“œ[R¶G ‚弯³‡øàãïãéÃXÍ"b¥]–ÈÅnœôh39<äâœm²r­XiÊJYyüeØqǹx¦‡ã8Ζp½Ä{‹ø‹ùK|ê«?å9H–ÃNºF æe3e{FP4ш¶Z@¯dIˆS¤É¹WâA,×¾(¹·¤‡vAŒg÷PV,–8Þ[âÉëñÁ§ßÁ§~ÿñìÁ„÷qçÅ ¡.­èÖs†1 –Þ#W•»ò®k³$ÆqN¢®¿Ù íwå&¢/º4ý+›®ï8ÎÖ¸{‹s•ð ‡ã8ΖP}ˆÏüa…O~íû°÷ìÝÍ€‡ãL¤ž –û+HR§íµ·ßB¨HiCÄÔqÇqÇÙz8Žãl ÉÕá;¨Ž2 /MqNÁW>ÿu¬ö–øì}/^Ü[`o1‡~ø”)ü¶‘VŸ°¨Sè8Žã8Žãñ Ç- š¿„Š·|1Ûånª=Ûvn*‹‰ëáÞ´Y&Ú¶wP#Ÿ˜ÀÕC0ß™¾ž $g•iû^²Ÿ!”Ý[vÁRIG9%š@TÖßÈÇTâc ÖÁw¡Õ½l;Ú™‡®®<ƒCfP¡:>¯æ}{Èæ¥øˆ¶ÖvÀüRqüYbÚè™çÕnS¹ee‡Åñ6óa™ÐXmal{7Ê—ÐÀêÝèY&¸¿xôélæþZ óЬ‰ÀAXñ£¿ý}]©ËþQ…ÕLp|°ÂÁá¡&ì·K£OîP·®-­Ó‹6à ©ÊûH†­íòÎýòô#5X%Çv:OÉÁeÓ]PÉY */6Æ=µ%TP-;¢XH|6mÉÏÑiÏÁŸ — sÆ~X÷|Õ¤àCÀ|P^Os$ZÖ»bºlðÈö­nÇÊ uÌ÷>Ýÿ‡B÷åð°»ÇEù1>I“Ьx¿T(Ûk8óXÛdº5ÛX"®Þ/OoAÁìÿX.-!÷Ú+nÑlÐæ}£ÄºôÜT‹iÌûiý¨Wï–0܈öÊí}äÚSï%ºšl<µG4ãº-À±Ùwoq®^”ë8Žsž´4 8¾w~â.Žï=Lö·Ž“ÁB¨"c~\!Ô„Po>¢Ù³<ÇqÇq&áAÇqœó„´ÉôxñR…ßú¹?Á7~ˆ¼,Æéঔ%Ô„jÉ8x1ÃlÉàpÇqÇ9¼¼ÅqœÛK–rOëÎ*íÿótÉ]b܉ŸýŸ~³E]ž€UÂâÜ Ú@ƪqpRÉJ¬Ü:Ú I „äó–!·JãƒJ5¤k¥5žæìœ7Y{cêËPCxD©@W D<¹ðM»î¸\v;RÞê8Ž3†=Çq.RAu|Œê¸©Ñö—²[  ù•ʽ @ðhçÉçw†:2nQè\º H@÷b¾ƒ†™ãÜgꣾÕú§Œ=ÇqÎIä”a™ búËšÓ+ÅÞñzÀíjÇqÇqN‹=ǹf©­™zþpülÄ-`Þ/xŽoþè÷CHñéßÿ*@³²=­¥žO¡üuO#òWXÍô˜˜2VvsF%¦KËØºÍß.1=Ùr1ÝEØÞc|µ8*Žƒš@†0ahbÃR°ÒVÁŽ|šm3@”Ë˵ÆÏŽÊ.$#+4Úþt ×%PZvÓp;F@û󡚃‰&·£M[5ªR¾Ò l8v„êQq¼Ès¨Ú…¢+ˆRYÎnSKÇ‚ÐFûéÛ•Òª»G®´ŽòhÜT0ÝBLwLsººØÏÒ³…ÃKá•ô}ûÓŦ«ðì­âøUýPl';8 ZÎ8ZÎ1 Ž`ÏFsœ+=ǹšì’ê ˆ•Lê8—¬:Ó—Üá)m×”ÖL¶€¾ ”ŽÕÄ€J™Z8F(§vI¢]€‚¥n("IÛƒs ™±ØÂ6++è1Ü(M¿S[k‹¥cœÙ;#t/‹Šj_†4z’ó…Q¶“M6Ååû±„#8eZÙ^r‰`ö¬è_z)fãuÕÙ¥Íìr,¶ì}›€&ÝuO*Ã{@ösYŠivÏEëäHÅ”Ü/ ÕX´ >Ïõ9Ž…=ǹ½äp夺ƒO~õ$å9HÊ_•ÇqÇqǹúxÐÃqœ[NÊðê$ìAfs(súÊMk¥ £_÷û/ jŒ|™Ìƒ)q±b_ÎeÀ¢PÉYÚ_h ã±M‡%pÊ’k‚ÜÔ|‡tRxªÞLú„ÖßJ.ò¯íŽs ¾à._þåy7ªê“ ù_Kÿ‰O!õ;iX—^tÓ)Œ¬Çqœ5<èá8Îí†R‰‹Ì°¼s½>ƒ2ðÚ7^4×S¾µ©Ís5~ÍžMÆ€"ö7ጱc>½LGìç¹¹’iß¼Ó½dûkD䦸97 zÜ~õݯ€‰ð…»¯âÇî½væËŸÅ.=y{¦­Cuzcª*÷NL|ÐLíŒì‚êªlxÂVÆ{úÄ›˜/"î¾{˜R÷KècÈ®îddíE¸êêkTzÝšÖÑåý€ýçøÔ×>Ökmm eáŽã8ŽsuQÙMÿî4ësz8ŽsËi…ѲRYA9‰—Þ}|®k[ÀÔqÎÅ÷ÿÉ÷`ÿ0tÇqÇqçôxÐÃqœ›Ï‰‹Æ]¢Ó˜Ü.(`ïéwz÷ ž÷¥.†ÜJrºÓûî4gŒ‹Ãrv)c‰¥ž%¶pf,o˜©ˆl»¬’Õ1Фt$¡BRé>D‘J–é! >ö*9‰‚Dâ£ëî+–ËEÊ)ïŒd¡’X)P1E“¹rfÁÁ±6m‰¥:§Åô¶œJd„BÒb°yضŒ ^[ë˜Èy Š+dq÷k†y}.M“ÀëúxelKœÛ·Lb¢^šê8·z8Žs#˜nó6ì –ýK/Í‘l ‘Óê.âÞƒ4I¬Áu£¯ËvyöËÛD;×!UÓ‰å"^MËÚ別£ âÆJÆ,O'buŒÑ>ÖÇk°ã–•mýtЙÖê¾ôóð±o?À'ÿ4Y/R\tǧZΡ‡©ƒO" ,ò¥™Än!»¼c8fpÙñ…EÍÓ"·.+3¸!+@,W i.;*Ëò ­.‹.»Ø³:Æ‚¥ñ$ RÀ—Øt©fŠã)Ü™f«b;Ym}Äýk2æýÍ¯Ñ Žßô˜‡Uöi9‚h\•Ý´ÀPÃy$æ–Õù²dU¼—B9Î}Ö‰ÖÆ7 …í:w^ës ¯vÇqœ³f-(ðâÞž=˜fì8Žã8ŽãœÏôpÇ0(WÐxv_ÛK#|KñÿÁ¿x}˜9AÁüØ–•䙎ã8Žã8ÎnxÐÃqœÛ‹õƒèKHaS`ðŞ⼚5Óôªä.rêœÄüÅc(Ï:»Ú¼…b Ž© %}>¢¡Oµ656Çqçªáî-ÎÂ?#9Žã8Îy¢1ix8Žã8Žã8Ž=Çqç¼Ð@’À§g9Žã8Žã\8^Þr àpæá©V]­iƒjþê.[5mêpgÒô* HÉ.OŶ¸‹GÅñ&Ä CEÜbê~ˆ,.¦tbây¯ª×垦”~0ÕúMû9BñåQðTš‡”Êç–´ì¸Ât·¬/OÀõ½ÂFEs{-%øäS:·Ó_Ž%~P¯‹ò:TlÇK¹>¼£¬Ël‡>ƒexX¼Ö’CËæV{cfŠÿÔ¶ââ1!ÃZ˜Fî‰ËÅ7§­€u­Ä{ Þ›4ÔO_¸ØVyNX.4õ“IÛÔlÙ¤©•§ÙI«¬L§±Nmͺ_Ÿ–s’ãžéá8Žã87’ßüù‹ÿç?ýòeo†ã8Žã8Υ♎ã8ŽsùèͰ¸ÿÊ4ÈàpÇqǹMxÐÃqÇq2áBSrO ­•§0ˆâsÿês8º{ŒP „i0Oû†CÇqÎÂZtçÞ²Ò1˱Ç9;<èá8Žãܬºý›È÷ÿÉ€“kÖ»ˆgƒ8Žã\0e”[Ö:Wz8Žã8Î "ÏühK[ØÈöpÇqǹ xÐã6PTÚf”º¿D³¤<¾±ˆ=„YYÍÞV³>;8”ÏGÝXJ_t¶…e9[b:fŒ;µlNÓÁÀ8¯gø¥Þvˆ™ZØ^=‚б~kY(;`¤é îF›&Œ8ÝŒ–b”湚mÈ8Væ Æ1¡Ûnb‹²ýrMõýXÛk9ãDIá·ppò6­AÒŸCåþš ËÚœ'ŸN×ýñ±\Z”ʛݦ0t§é¶uZsú6—ŸL†# £èH º*ºÍ¨ÆIŽ…uÿaÃM§œþ¯ˆ Öå`9Ý”úPíxëùh¹N°á.¢rXì³XÏ1Lwž¡äw@¼oÎsó¯òù3¹wiJn1É¡Œi!¼Ñ$~”œ¤ —Úžç¦ãAgˆeEF3Ó:MpþAÓ–2²í$mÆV/=Î(f`ãL—55¸qQe gÕ~,ËÚòòñV•fle“ltB§w*ÇŽ¹3ÖAåT_Òyùf‡”gRûîG±ß.•>è‘CÓuæR…y¨T. O 骔ÆG”Ž{ºXm¡œ"=FÙ×j'œª}ËPûº½ŠÏmëú4¯[.߯FîIVßç¤ù6§i·#Û[<†•­"^ÍótQ˜v,€Î|³ƒÿOßú<îþÅ?Ãññ—m+çëŒF¨õ¡ñœÖç87´ˆÌqÇqÇqçjÁü‹W~ð7?@8øÉ&ã•=¨ê8çˆgz8Žã8·žÿ`7¬õ;])Ê -›p­¾å¥.—1¿ã8Ž3Žê C@”°ÿÞïâoÿóO¡>úm@W· 4Èq.z8Žã8·÷ÚDßïJ(ñƤ·AŒõ²•||^£ä§Žã8çI^2'rˆ¸z²ü¨.º€ÇÍ+\¬†ØÍx†;çƒ=ÇqÇqÇq.‰Op,_†êÊ’ç‚ð Ç9ñöÛoã—ù—ñÇüÇ888Àb±À/þâ/â~á¶^FŒ¿ò+¿‚_ýÕ_ÅW¾òÌf3|á _À/ýÒ/á‹_üâÖËQYnDUÅLß.ªÙ‚v RVø· šaê‡E2DT5>+GÆ‰Ë êXªãÇÓ6 Àä¨òTaB(®ÞÃP&+¢™)|†©ûG¶`¡‰ñß:¶"e—@dsÃmÆt$±DÉGç1Ô÷Š×‡e‡Ë »©òOucQ=ùÕ:çÓÚ¯ráåÐmcrò‘n|ÑÞj°®De­’c<©-œùÿ·w÷Á’TõýøßçtÏ}Ú½»î…\„ŵP` _ÃW‚å¦ °”Ÿj1F“³ñM%º…š”%”ÑúEªdÌÏŠ)‚~¨(ЏK€Ue¿ (ì.°ìݽsfæœÏ÷ž‡ž;çÓ÷öìÜ;÷μ_UÃ^fzúôôt÷túœÏGKXj¼¯÷è˜]áÅGQÒËÃ{ýý©ç]ܨ*c½^&-”|U2ªV3žÂ_AR±#ð›#dB™—–X¶LÔh%J*8µÌÈALx?J¾ý#ùÔ*W…åÝE\îíýžçÛŒÉ{êia­²t¨ŠH;I¢µê-@Fòue¹´s,m‰¢Qˆ Tùò3°¢,—rœq.¼ß$ÕDÛ›„3Ê9€:,0ßvØÎ¶kc¥ÊŸ¢„ìj• øÙ½Ÿ„™k[7«zOÞÞ yS4æýìÉ9CŽåê‘^“Ô[ôX=ö.¼ðB\y啨±c …~üãã²Ë.ÃÎ;qÛm·Í9ÁæÍ›ñµ¯} qÃ9‡ééiüà?À~ðÜtÓMø«¿ú«y-«LÀÙæ`µlšv ’ ¸ÒËÁ—¢Âêy-Gã sd$°&\¶Íã˜~QøŒb2NF|Π‡øŒ*1!pùs ´óc“3#9OŒa³¶9å‚O9É¢\eqý¢'xñ”j§…8ÑÌb.õ ©e†ôÀ‡ôPÊâŠq0¡ Ÿ8ˆ¶ªUOr~ç™åˆÃ|u¼sË"™´à˜^rv•ê÷æRDZ¦Ò¶ŒI·9O"çuLËôÈb+á „Z)Å¥Ña|ðʽ‚xºœYé|!µ]•Só¶¶ÜhjO©#¢T­Ô²¿É.*é…Ÿ ¾nC‘lWárÄÁ‹ÍŒr ^½pÔÛÎôȸùœ^Êù‡låÞøÜûyVÉóàä™%ÒÃòþž·u‘­”“Ín(_PÀ(ç>0˜Ð¼ìPF…ªp®®À§m;Óáu•q‘w[ÏÚ×:ÖFµNÐÏ+ðø]¯ÀLÎíÊh7ù2ß”?È—çû¨O+`õZ"X½¥ÃÆÇÇqå•W"ŽcÜ|óÍ(’ѦM›pÍ5×`ûöíøÒ—¾4ç|n»í6ìÞ½÷ß?¦¦¦011ï|ç;ذaàšk®ÁÞ½{ò£-cŒ†N„]ñfD…×ÁÚUÝ^¨®Ú÷‡‡ßòs”†"µL-Q¯áYO‡ÝrË-xî¹çðîw¿£££M¯]}õÕ€O|â(—³ïØ|á _À=÷܃ /¼qcdd—_~9î¾ûn ÌÌÌàž{îY°ÏAD´lUïbÙh Ê'¼_¶°+:4ótïjs&ÌpãaW4&j<ß¿ÄZ¬{&ÂïÜ÷[°~~}ÔjCe’á0Ñr G‡mß¾pÑEµ¼vê©§bÆ Ø»w/î»ï>uO>ù$.½ôRœ~úé-¯mܸo|ã‡êÈ2õƒˆNÄôè0*øbþaY™ D0ÕѪúCÖÔÉðî:š×{ g0ö"—šF¤þhzÞU_®vUf÷a""š›À/úƒHàG8pO=õàì³ÏNsÎ9çî½÷^u>6lÀµ×^«¾~Úi§Ö¯_ßî¢õ<)?‡U¿~oú÷ŸÂ—Ÿ… ¼µØJƒÅ¦JˆÊ妤§DDDD½Š‰L;h÷îÝõ¿O9å”à4ëÖ­<þøãê| …B=HÈ /¼€ÁÁA¼õ­omsI3dÜÅëfU­¢&R"¾¼K¹ ´„sY™ÂµíJKP—•ÄT­³Ðß{æü;—¼R¥%%k§ mŸÊ[y¨ YÛIÞê É÷^„¸i@JIÀC\¸j À“Ñ5gëO%G׺‘šb0õе°‡ÉcÉgªM7ûûHÿè»j#ٜƈ‡ÛRÙeÞ¹=´$ÁÆÎ9äeq†ÄÌN\›¢U*SÓø;±ôäÃË,—qÜûX’š|¸þ÷ì|5«V%‰ô^z饶ژ™™Ác=†|à8á„æ÷WnúŒŒA¬ez^¢AJiø…Œ²»^ËÞΠã–]©"Ge•vºËkeU•R¶^­<’ÿB:”1_ —€Ö?_Þçõ×ô`A¸d­º\¼Èn‹4Ë÷|R°¬8ãÝÁ\‹Ô\ !‚1µ É@ý¹¤ƒx|aÿõGÿ£‡Vã»_ò0à«—tOq@S†ûPÖ«Á&ãòUoc(ô¨Mç›ZÏo§ blý=ÆûæJ.9{Žè¥=3Þ”·u¨z(Y]&Ø`¹åE  ö»œÇ%­b‡ ôùhm{_F(È0G±¤°¶¶·œæ òµUAeYÉP…¾«Œ*-½¤".u\.ÕKœëe×ÏïIÇ GMN6N¬Âeø“RTÅb¾Òz5_ÿú×Q(pà 7Ìû=·|¦éÿß¼b ¿»ê¤¶Ú'"¢YÄR†-ƒq38ï3±æ€AT:œºháÉõžüy¶Û‹A”‰A®ÿ].—ƒZÕ–‘‘‘Üó/‹øä'?‰;î¸cÞ½<àC'¾ƒ©;y‘ÑÑü¸Ô¿Qò¯+Âøiœ¶û ÆÍ4z3-“,eKDDy¼ùëqÁêSëÿ¬2[÷îìâµâÙMÊ-·ÜcLîÇÇ?þqÀÉ'Ÿ\Ÿ×ÄÄD°Úó'”¿§ÅÖ­[ñÑ~—\rI®÷ Z‹AÕñ"ŒÍ'"êE"¾:®ÛA¤Ôx¸qÀnvê7°Ó{aÊ_LRšénDŒ­KÑhejÅÚàƒˆˆúOl,m\ T‡CЏEiØÓ#eÍš5xýë_Ÿû}¯|å+guŒ1ìß¿ccc-ÓîßŸä¦Ø¸qc®6>ó™ÏàŒ3ÎÀ–-[r/-–9’“Ñ¢bÐ#eóæÍؼysÛïù瞋]»vá‰'ž6j^.¾øâyÏwûöíÇç>÷¹¶—ˆˆJ2Ä¥v—)”ì–ˆˆˆˆºƒA»êª«°k×.<ðÀxÏ{ÞÓôÚ‹/¾ˆ={ö`ll —^z鼿w×]waçθõÖ[ƒ¯ûÛ߯W\‘9W.Âͳë±1F«ì¢È›ùv0íá IDAT[Ü$¼S²é+üÔÞ\ÓËémTò-SfÅE<˜¯ì*)Ú{2¾¿ëĘÆ_«TŸïd–î¸pbðycƒÙüEÊÊ÷áá}¸zK\P†™I¡DûÆŽ(é(š¡=ÊY¥¥6t¢õ§ïkJÉ{ÂËdì DvE®åòn\™ÁíJd:¼N”Š00pFðyQË̶AÛg絯5Þ+âÔ²ÊÀ°úJx™Ê0•ðHíy>9ü|‡­¸Bn ü“o|¸¬„ñC©¿ßÙôè0|ÜúémEUZ¿[[ñIë^·m«yRfOpÙáäxÞN¬QªÙð~#Êþl1Øðvb}ø{løxå'á*áÏ®íç6 çSK…›8w9kµê<¾;¨ž3hU¥âW…ço¢à{Df‚ç Yç…xµúZ˜ ®Ç$©T RÖa¤¶Qº];VkÕi$\!Æ»IxŸï<ÃÚü9æòòNÛ?ÂŒ)ä®#Êï¼:½TÔsƒŒFr¶‘¿d­¶Ÿ«mø™6¾ó¡¹'J·!.WÙøú´"‹[½¥­IÔ/8·Ã¶lÙ‚µk×âÎ;ïlªæ·ß~;¼÷ضm[=é©sï}ï{±iÓ&ìÛ·¯iú{î¹ßüæ7qóÍ7·´S,ñéO/¼ðÂÂ}""""""¢eŒA׿üe;v [·n­WkÙ¹s'n¸á\~ùåøØÇ>VŸþÑGÅW¿úU<øàƒ¸ãŽ;êÏ?ðÀx×»Þ…»îº ƒƒƒˆã¸é±råJ|ö³ŸÅUW]µèŸ‘ˆˆˆˆˆˆh9àð–pÙe—ᡇÂõ×_7½éMA±XÄu×]‡­[·"Š]ÄÎ>ûlœþùØ·oÞö¶·öìÙƒ+®¸SSS™ílÞ¼+W®\ÐÏBDDÄê,DDD´\1è±@Î=÷\ÜyçsN722‚;v4=÷º×½ããã版ˆˆˆˆ–(ƒdd¿Zˆöˆ4¼uCDDDDDDD=‰==ˆzŒéÖ2¡gUˆÎ(¯1J¥csg 7&\ï!ŠÆ`ÕJ¼ÃÌÅÁås(Ù÷E¹1aÄZ%ˆœ™æ;K¯Hb”dæê-ÓzÎjH\¿™UÔjHÚóámW«Ó¾üU”òqJùïKø(Eñq±á [{J!ëâ[ß#Ö <Ôú9lÅ¢0®”bÕu›ÿŽZ]@©TdÌ@¸ZHÖ6ª/m¤TìЪ¤hǤ ú1\ÙNL¤—´y)û¿Ö†1娮µ­U IæªtSDx_𹫞dîS¡ß.Xµ ›³Ò1ƒz5형·üuÞã4QŸ`Ѓˆ2ä<ÓNŒ>ùÖJþ%oÒÊ0OØ” ˜eØÝ1ï‰nÞé¾@ìôºêhð!p1&F tÒBÏ?‹G;1á †W‚V)Y;ér·>6Á6ŒE°ŒîbÑ‚!jA _õ·õ­¯Qq}Œß‚´G¤á©õ$öô ²åŒÞЇšDMû|Z†öå&œ/]™ÅØÁj¦ý¤’Š1ƒ€‰`í¬A©ô ¼Ÿ„¯LTßásGÍÕÄ€vÖ® ¾æ%\"Bütðy‡CJõ–iå=^­B#‘R©@Ëëhƒ@":©IõíJ¯`"'µË[Q"+©—š OMF›Q}'´\ÆÃ(Ÿ]k;wE™6hëÖ`X}šÜ1=ßÔ2ªUDÔÂÉy“×òÍ˺ Œ¯/í_«Þâ£(5MýÝø×Ûä_“jÒ𝠆«1¢Uá…òÓî#RL±åi=y¥R9Æ®@þDµùöø’r ð•UÂóRSvÚ‘à{l4ªVJÑŽK®r8¼Hê±}V©´¥%2¶¦±Léc¹±+êÇÑô{½88¶›‚šk[O­Ln"uyµ g¹Ïü4|¨úWVµœÉ MFÅõ¸›÷sˆÏô:o%8Qª¤eɪDl×s/W[r&qn§·BÞßœ¶zDäù¬"DKƒÔ*w刅’ä®!’³ AÞ`1áò¬Ùäk#)"·,^:èÑøá±v$ >D£ˆ¢Õ0fQt¬]ÀÁûcpnâ&«ë¢é?µìÚªø`ÀÀØ!X;œ>tο~Þ)AqêúUOêr–¬UK@š( æÈʺ¨sêö.ʼ¬ —äÔ´S¥Å-èá‚ì™'“j&_Ä ™`µÄ%Â0ŒEøÂØÍ:fÌcªÇ XeûÑf¥K\%xÂ0ƒ¢,q $enëÓÍ ŒÔå`zØ£P6œ6ˆ*©eL-by(9ݘ])Fb¥œ¬‹‚û§ñSj 5 ¥Loìj5 ¢}çÚþ¬)ëÛœrl×¶kQ.ll´2x\Š¢1ØhM`F"á ²vL4v(xaeÌ`¸d­xx¥ôôÆ ×Æ®Dm½4J^‹-ÁH 9 ––/ŒÐêÌ™Wóc\ð{JáÀQ[•¹BÇc8=0ï•ÚÔŠdÈBþ@I¾éÛ8'Ëy®ÔVe‘Ÿ˜_Œm|n½ t`rSû!ðȈL.€%xÓ–– †âˆúEúǺúƒgí˜Á×Á ¼®zAÆ ¢Vº[ºviñÖÀ[ÀÅ@q´‚ý¯>‚'ÏÙ#'Tæ~3Ñ"cЃ¨_Ô"ûâªCA&Q©ü2ó Hy¼;ï§SwWf@Œm<ˆúˆ)œ‚ƒß>ZE‹Ñ£Ãð‘C¹À$r4QýaL! ¸›Æ ¥©ç¹ßÑqâð¢Ó”`vwzñ™¼‡Ã4¼/¢\zÞMÂUë¢~f‡ðóÿý.ÞÝíY\Z®´¡)‹Á™aœ¼÷Ô¦á,Æ7e™=¬…úW:ˆ‘hìÊúp#ñ¨o}y‡‘Ñ’Àê-´”0èAÔ'.'ë ÊõâgàýäÒL`K´HéY\üÿ¥s°ô÷‰•õñ¨µt I£»ËEDDD -{Zr. ¨½F\ã.™xˆI*“ˆ©¾&â§g%L­³t $Õƒ$o–tH‚@â³6-j$?#£|;‰;C:ßÍZI–˜·}uh @•jpÊ2e,W'ƒeZ…iã;Ì[é&s^³“WFh]󘯺L¾­„…ÁYißGV%¥GGèySí±a|u|ì¬Þ5éž&kѾ¼Ï£½ F­ÆzƒË<þŸîào±J²Ëê0Î4ÒÎþÑúû˜›^Ï9o -t-™éìþìYmhû”6T|pgȾá U$ W Ó5Gû$"ZŽô em®lÒµJ"ýüh*ilê¤ÍV+ƒ”gM_Fúq~ݵ“:¬|âÜ1u>6j­ê’E¤¬Ÿdwð\ ¦-tWk+àdmÃZ•µ ­šÂ32ÛÐ*àH9÷~gm¸|©÷G3ƒ›ó]¦ö…¿óôÅÓ¼zJi•?<ÂËœuïôïÕ¨"ÐÊþf,WFõ¨ÐWkl!w5(½Ä¶|/ûmHŸ|ÞÚ¥J‹Ï]…hÁ´S!çxÛ#R0èA=©QN¶œ€øü`DDýÌÛ¤4-€j)Z07-3 zPo2lµ+°÷3@hh©Ä&oØD xP‡ \jXRº'[€[DDÔ¯ô žV¡0x åÒþ¤ª›î»ÞM]î3Ö§&Ku=æ%ÑlJž”,ål<ð£ßÿ9â™.ºûõõç­øê°–¬a,Íy=x4¡ÄìáFõ-£’"” xˆÏ7$„ˆ–YÔ$ù"ü!ƒÔ“¢x5ÖÃû"\åe8íö"-+ÖåÁ2ÄÔ’—ò„’ˆˆˆ–=¨7‰ƒ—i ¾Ó“×ÁË^ DD9DÁ%w°•þê%GDDD½ƒA>`ãX;¿®×ítÑ.OíÏ÷†6ª_¸Ò‘\Ó—Š¿ÌÜ>ï÷Øh(W6„‰ò}W>ßΰ­š…-¬T¦Â¥ô²JYæÌ˜o£‘`âË7X¨¨ ÎËW&rµ-Rν¼ƒÃƒÏkU¼áÊm1cìy¡pz®eJªž(U>´÷äìþ-F¯Äâ%ð=µ!ïwÞ¿œ³ý;×*+Dæáy­*HTÍØa×ñc¬ûÙO îpjºt‰Í)eµ’ÃS:6$m·ÞF%VKfJêw ö·"¸8ý|cúÒP£~ÊÌHã…i öú˜]ÉÅÍ£²KiE¸zS<3ã*-ÏGÑl9PåG UPQÈÀI@õS™*Þ2ñ/çUx­ÒˆË(ª=¯T7Éø|ÁŠ=þ*î€úž`9÷[c ù*+¨”ŸSç\&„K:S€Çô÷X;,m,³rˆHSE³ê:‘FeW:lCeìœçZÞ2ðêð Jùd …ÏTùN} ~&ÿoŽržÑH:?ëy„ŸOf6rä©c`mD0¶ ù¼Ÿ„÷ß;ñð¡ó ½Ê89Ï«sïƒõÏ•ã=&ßå_òý…Ê-»àòúÚö!nqÇI3eè½AÈDDD AF¿„âh°9/,ˆˆ¨{Œ…±#ˆ¢Õ(NE¤̈¨7±§Gøò¡ßÀƒ7Œ¬Æy#Ê]N""RÕüšéçqƃûSU&ˆˆh)3ˆ`L„ÈŽÂÚQDñ«!p(ãùn/ZOyøèóøŸ£{á<‡CÒÒàGxÿ §apžÃ[ˆˆ¨UÒ•ÜÁÀA|òwzxBºûvxPÇÒ—”¨m,½Oýl¤A\l`«wžE¡ˆ–4c €©ŽórSP³Þ;ßDÍã¿h™IzzX; ­uᡵԾ7®z5Þ¸êÕ8Z™Æ?=÷“n/Q=ˆˆˆæ”8’ŠxŽc‡‰z•±¬×Þ£DÊð2 ë' ÂRÈ Mà!‹x üxÔ?ô ""š7<ˆˆ– ñ@ÊðîHìrî$¿D´¼1èA´˜”läFÂj5+¾Ñ³uÛh$ü%{ºˆW¯f\·¶µâA²\6˜_%ݽ€ÔîôdUI赊‘×ï2jß¡rwCÝ~ÄÛ_ºwI¢pÅSʬ>”»`e…@U! ~4ž7mT¥jmgV3f0áê-¯? AÕ*C#p…ä'?]e¥2af$õÿ©³76ÄB)ý 4ˆÇxÔ‡½Ôþ?i¯eÊÔßúiˆu׬… ûŒ/Ã$ Öʇó¯q€ TcpSáœ-â`lx½[ ·-2¾Ï)%ø~½Ó¬í+™¿­¿ U߀^ Æš‘d¿j™~¨þ/ÓðHªt‰”Ÿn{ #ƒë+\e#s6Zµ µºPÞeÒ‡ü«”ŽÑ*ždïÓçéJ<ưÉw)ÆàÍ8œ;–¿KÕY)ëªÊgyÃU*åƒáöÕê;Ê÷mxc€–=ˆ–ºœSú LÖÉ‚òšZ.üï–Å«ýnQ>_R66„Ê[r°“Ú: Ug¶|NJô@Zí?Wð åºMT?±k.y¼AŽ´&o`í„ÙØ`ÐCŒO•¦­åî«:ÒÁ ?s} 4}óyÿì2·éçÅ·¾æ£$ɳÈ9£Ü±àóÙÕ¢ržÿh‰É••+ƪûÚ„rÓžOªæ´~çŸqlOUÓ:Ž{ºY½Ú«Ò¥¬_åÜÇHþóŒðLsãá|÷’»-4=ú€1fAƒít'Ë{€Î‘݉’ g97kƒ%hm4Œxà¤ð¼lcúä„4Y×€H%õZò¼1Qø„ÒX½Ì\ÖÅ(°¢m#ÆÂ(ƒýs—’CþíÊ»ðÅ©vbkíPî“=µü­‰Â'ì©‹úVáå2P¾SPNðKÁZS«ä­Z“òÚ?y2¦ z4>ƒ˜Æ²3¬^@éÇ¥Œª¡ïV\¸|)´ç3.Œ¤>›a¡€HX%P¢m‡©€G: ÖÀÅ&õÿÉ¿.Nb“•êmcðé1*à¢Ú4¨Oo¼©kqQø*ɶtoNSlë{¬7ÕˆI=×XãªmK=è>ŸÒ¦òU§2FÛ’öÃ2ªÀÚñR„÷­´Yu"ùމ"åÜÇĶä<ˆ´c‰±áJ7RVƒ7êï„TÖ¡hALQ.‹ÄU^¿ øÌr¾d3Ê‹jU„”¦%üÛ•hh'èZïÉq=¼½Ù¼7%Äux8„Üó#u}å=wU·Ý Úv¥Ýœj«·dòÞcÏž=8óÌ3»½(}aiÞ'""¢¾ä""¢ål÷îݰÖVoFDQ„Gy¤Û‹Õ7ØÓƒ¨—µÜ9«Å9™ì‰–Ú¶kCO’.ðÕ×Õá"V»óÖ|)Ý£Cë!³´aêô¶9Ѩ(Amšæá-ñá>a¦ú¼oãV_º˜ñeñÖÌFc«ÓxˆIž7  --7Þx#n½õV †‡‡ñð]^ªþÁ Q¿[¢ùOˆj’.ÐÕ BKæîΚîjœq-½T @8"ÖÔ é²³«¬Ôòvx+MŽò€‡¯Ž++õÞQņ¯Ô†¨„W˜KyJÉX1õù͇¥ô@;±­¹CêSU‡ºˆ1 |u«·4ìß¿ãããø‹¿ø‹n/JßâÕ5±Jï…âg%™QRý-;7Ýtþýßÿ§vþò/ÿ¿øÅ/º½H}‡==ˆ‘šPVËžn á×L¤&¨3³ž¯ÝuÕï—ŠÕžàOÒ¦}F%ŸN®JYÚìÑÜN\ª,¬Öd®IrIcW&ÃZüD¦ô$£[Œ•‹AIjçmøç[Œ…̪Þ"ÖÂ[SïÕ1߀B:·†7ÍÉN%Ýë#°kûH’<¬J/Féçg}–Zo¤{œ¸8‚ñÀÀ´ÀxõÉ¿Q)ÿ]>“ëΠƒö½‹’ÔVMLœ‘°XOdÜÛ»½ZHî¡]Úþ,>œ„[ID»üxäíü¤&856u”¬ó‚žèmjõ„¥9“¸ÂOup¹™ÛtÞ N¡äÃËçöýïÇŽÕW^¹ íœ}öÙøÈG>‚ï~÷»øâ¿ˆý×ÅW¿úUüáþá‚¶K z-"(? è'6 ¾Ç˜‚šµ^R?¤Éªo´]û!J:Œè;òž|'Ÿ#е^˼/å¦r€­óZXz•ŒÏ=ryǯS'ÙJpC²Êþuñ4µÞ³."ÍL´Ó'lľ×`ýã/#*> q3Š?o–e¾v¡[û~#4¾ŸÔ:L¯O%×HrRÚN”j!f¢= ©Š6M[LKÐÃÇå!‹ÒP£,¬Øäo‹š8ÔÅRvÔ³‡½hïMOc¼Aäˤõ"‰*©`HúëK­‚rAðò “(T°bb« b`Úb`®¤Þ”šÑ*ˆ ïë¾Þ?D¿ÖÊϪAèIzã"[§}nkW„K4[wg‡1ù‚öÞ‡ËþŠŸ(¯-IÚM”óú´›.¦ü6ÆjÕ¬6n¢„>‹ø…¿!b¢Ü•‡´òÖ†vv0©ú˜vŽ£Vìé ŸU/´N²Î ”ï¯þï¢Vo™ÿ÷ùÈ#àoÿöoqï½÷âÚk¯Í z<ýôÓøû¿ÿ{üüç?Çðð0¦§§ñ¡}[¶l™w{ïÿûñþ÷¿ð­o} ú§Š÷½ï}xöÙg±víÚyχÚ× áU""ê Žœ<„½ë/~ÓfÆ®†‰VWKÌ.Á’Øb fFbL®Ì z¸X0µÂáÐÚ)Tæ>µbªei“ÀE¡Õ”†ÛKõIõéë“e?á(~ñ;Obbe.æp""šÛ‘#GðÿøøÊW¾‚‡zhÎé{ì1œwÞyìØ±>ø þéŸþ ùÈGÚÎÏñû¿ÿûøÚ×¾†ÉÉI|ë[ßjk”OˆzŽO=ˆ–¿¤«lr÷ûÄߌãÿp5¢©}€,B7à*¯â!ë`ìð"VŽiŸX[¼¼Öa÷y¿ÁøX²¾\,(\K,Æ›úCo/éR{¤ÿßE.òõÞ"Þê=L²DÎ`õáa¬<²ëwŸŽB™§1DD4?«W¯ÆG?úQ|þóŸÇÛßþöÌiÇÇÇqå•W"ŽcÜ|óÍ(’žh›6mÂ5×\ƒíÛ·ãK_úR}úû·ÃÊ•+ë7ªó¾è¢‹ðÚ×¾/¾øbG>ÍÃ[ˆzLS÷R¥«Ÿíïqã´$5W@I×K-b!î¢#‡1|ðî`2äEÝ1I¹ÜñÓÎÁ̈Áôˆà”_¬D4ù,àV§iÎϰ¸)@›Õ†¶ˆ5¨ Dk°â˜Áº_¿@2´d¸!. r¦Z %ù*ŒŽWK{Ô«·Q÷%Õ[·½¹Óø Óª¹å–[ðÜsÏaË–-mzíꫯƧ>õ)|âŸÀûÞ÷> \yå•¸à‚ êÓÔ‚$š5kÖओNšs™©3ô "¢%-I&Xn &Üâªã®W8Œá!”‡aËË*ïB¡œøâ0*ã“°±Kn½-Œ7ó¢"FP‰]=É)XXxNhÚ®ÁiƒÓŸ>+Ž% M­¬R—ˆˆ¨Û·oôʘíÔSOņ ð«_ý ÷ÝwÞþö·ctt´%8¢9zô(žyæ¼ímoëä"Sö %"¢%.I )~Þá}Y•4:/BT|ã¿ÂŠ¿©L,bÛù‰5Û2Ä .XDÕ¿M‡;FˆøÈ£'=>:™Ë#Íx`ÍQ% xŽ""ê_ÀSO= ©¼rÎ9çî½÷ÞÌyíØ±ïxÇ;ðŸÿùŸÁ¶mÛpÝu×aݺu\jÊž}À•‹pv~ñ-µœ[_)æ~O^#ù âËð.#;u˜h6É÷­Â‡’[”ª‚x7|ÍÆ«Âm¤»ý™¨^±%k™l´2ø’VqE%~I–H­”^Ê5½uƒ09¿ó8w]4f(X5GL Àw"^Âë]ÞN’ª¡y•ƒ%6^;GJ7Meû1°©J Qc¸ŠVÂO<âð‰…‰Æ”v´ãš‡ñáýÕö„ßb\ªÚ¾a!¾º®\ë¸[¥„–kD\ðs£ç™]‚ºþ¼2”Ãf<qIØz`ÀÅI^¨D‚™ÁF•„t/ 7Ú¨åãq‘Gqå|ä1µªˆÏþ¿ˆ&Vaí³¯ÂÐÄ0ŒxëaÄÀˆÁЉázE—tÛÞ6Öõ LºÚ‹‹ÿ_[;QÅ``R©B‘÷7Ê…¯äg->ÿï µùŽ%YÕ^Bí5©îÛõß™¨yøXj›ôîˆÞ~  ‘rFÕ%¥•B•BÁD‘²:Ó(ëÐWÆÃÓk ˆí`îÊUVûíl§˜vÍ9|ϘBRê>H;ÏŸ+™h(Ø~lW&‚øéä{‘rõÜÑ7³¤óÕ nÊoW²ó}™IÂïhëœ7Ä9½Bv§‰âÕ¹¦·Ñ`îÂN9Ü’J>óo£ïvXÈò-¸Ôø™™qܽ{wýïSN9%8M-`ñøãgÎk*Êc/IDATÕªUxî¹çðÎw¾¿÷{¿‡×¼æ5ø£?ú#\|ñÅ[^šƒDD´È¢äbÌ À˜ˆŸ€ÇÂOóÈs‚×ï\äqtíøØÃ*Øtò#€Ý‡þF âR ë,€éà0"Z|ÖŽÂØˆ_ ï_Fiæ—@V f¢òÓâ8œ 8×áÃ*qÚ•U«’›Œ/½”}íÌ3ÏÄÃ?ܹ…£¶0èADD‹ÎØ•Io»2¹‡ë´»Å´ÔEÎ"ªD˜|Ŧ_µO×ãå#'aUäQ)T`œMzy8<ˆ–;cW ŠNFÎETz ¦ô4~ï鵺`Åj¼q¤Ñ»yÆ{ÜzxoGæ=9ÙèY;0î‰988(—ÖM cЃˆˆ:N펎dhU}x âônç )5¤ÄDk`Ì€(žRþ³4“EÈ<†,¦“{Z/¨ØZÅ \êßI)ÙÆ|ï)®l¢š*«T{ko’a/V°âåÑjå–up¥õXS‰ªíHäá@ä`½E%€‘ä½q¥ñ½‹‘zO¨bë#³bg™³£ ÍCX,ö °fEõé!Dƒÿ«>Ùô±¯/æ"Ò2eVþþóO§qé¯C4½Æƒ÷“w¬Û‹F}$6±Y˜`úððpýïr¹ |”ËI撚‘|C©;ô "¢.p)ÁÈ¥®ç|1f°ÉIŽñ.˜ód92sT5ÑÊÕÍù=`oVš+ºÄ.Âð±¦ác±.v°.ª÷ô’á-éà†Oµ¥Ú¬•»¥…¥rï̓”aföãÜç!žÙÊÜï >µKÖÎ×É'Ÿ\ÿ{bbccc-ÓLL$¹QXvvy`ЃˆˆÔª®T“:_l<×MÑj Z‘$Ðë‘€ÇBª>j¢J„¡É!8ëQHí¯McëQ–…ªîBD G¤Wú^½ó¼?˜äò¿¬ÊxÍ嬳΂1"‚ýû÷ƒû÷ïlܸq±ÚÀ Ñ"R3…kÙ½mâ[ßc陿sGºm8Û»±°v¨õy.g6ò~¦ Û¨%òláëÿi"0¡ª.@{½$BUDDßFÕá*žWò¦ê{L#{2½k]fñ€Zõd*\Ñ@0X"N­ ’þFJõe—òo€Ê@½½äDÞéq¹3æ×Þ×Ê…Ÿ7Qî ã¥^½%ÝËÃ[ƒZ¼Á[©ÒÍ=/|jØ‹u5)D \ìê½3œõÉ{’5µPRÿ×Û¤¤mmF®9RïMb¥^µ¥µ×Gë¶— |¬|Úö©Êš¾‹v9÷Í$ÏBíoÌ@øw-£’‡öÙ»*ï~k¢öŽ%¹iÛIxY½2TE¢2¼LÂà9ˆ”àý$P«üÕÁ;íDÝ466†sÏ=»víÂO< lÔ*¼° ËòÀ Ñ ^´Š V‘ÐO@;,Lªlpš˜¨ÃÞá"-Ç ¾Àè'ÆÚ~P ˆ$Q‚Áy'Ó‰/¦\o¯±Oæ˜×\M¡K¹KÚ0»FzHJú91R”ˆ•ƘÙÓVçg|L©1ÒùDb#õÀŠS¿D3"ˆê¤Ì.ØÔf$Ö4•êm^N¥hß™n ߊàÜÁÆ j0=\nRÆ‚ÿN˜¨ÚÎ2’;жô¨efýt#|"®ô@Kiúår¬£…#â ‹8¾¥“Ã[ફ®Â®]»ðÀà=ïyOÓk/¾ø"öìÙƒ±±1\zé¥m—¯hˆˆˆú˜¯.¼Iz}Ôµ F=ÏFêï¼*±Cy°Ü˜÷¬&DDD‹ij*éê}8X²eˬ]»wÞygS5¸ýöÛá½Ç¶mÛš’žÒÒÅž}à߯Àƒ7 ­Ào­èöâQ¿Iu­¢®'-­IºÿÛêß.î‚6‡ -!s%/M¦I¾ëM=7GzhK-¸a…õ¶©§G:ða¤Ñ1¿é´1=/+ÉÿzÓ8qqc]W ¶¾\F Œi,£‹“¹ÇÎ6õð lµmÙ 7ãpÊKüL£ˆx½GÈ2?ŽR~;<ƒãÏÂ)A„¥äСCøÑ~øñŒJ¥‚8n¾,׿üe¼ãïÀÖ­[që­·¢P(`çθá†pùå—ãcûX7ŸÚÀ Gxß굜GyC"¢~SË `Î3ÒÛݲ“\á&Æ›¦Þ.òØ÷Ú½8aßZ1ˆœm ˆÀBïñ1+ØÑhÓÖ£"MATêCW"gë}Q“ü¿©³´‹V¢‘2d®¼^Ú0)êiç¿â58ÿ¯ÁÑÊn|ö¾j®®E\€yqs¸à‚ ðË_þÅbð½ï}ë֭æM›ðo|£iúË.» =ô®¿þz¼éMoÂÈÈŠÅ"®»î:lݺQÔÛç ½„A"""Œ ][-5[örl}£Š÷@¡\ÛÁ, å(©âR no°vÿ ˜LŽ=b.v0ÞÀˆiê©Ñ”ÈtVBÓPo¬œ >ò-ÉRƒÓ¥vôPŸJdj}zŽV5KÙ7¥„/[œ1, /;g“¦$ºMÃÍæÛ†òe"hUA:Z‘d® Qh"÷±OÿþB߇Ñ*Ý«ÿ>çíuÖÉʺ•ù$w>Îs0WÏ?Ô÷´S¹.gõ¤­óÕÆö)>ßù®H&ïoKîó1¦¤åŽAjµ3ÚûJ1ßÚÈœžÿGÃe\¤iVéí‡*y('É ¨²XRQ—Å3¦<áxu[È{"&ÆBr^¤é>ºGÄç®ø ^Œˆ ^x$e[ƒcÉð ¥lc ¬q½à2!ܶè•.òJ¶µ¼'”G•yeT=ÉY.3SÎ’ í]8¥  —¾lgÿ¨ÅIã Þ*Õ ‡7RÏaʕءRpØÿÚ½=¸Sƒˆ+QK‰Û¦D§©€‡·RÛ¨ê—&©Š™õ··‚È¥+¼XXßÜë$]Æ6)½›jcyM’u(-%6è‘ÜŒÈôÈ "Ì'ÀÐTYC Ì)þj½œl[Ô`ÓQ8H#®c=çÛ©4’u¬ ®+[·c,¬ ßxÐJÙ´mAû-ÊÙ†øìßµ®Yø G;AŒ¼ç–y êïk‡òµ‘Q6:<}²žû¦ë’,aMKƒDDD}ÌxÀ¶9¿GTÞrÒ3¯ª÷Â0b —šéŠ/R/G+ð¦1__MlZ›P z¤ò~Ôæå"±‚¸apº3ÑDDDÔ_ô ""ê3µa!â“^‹$ii­C€sFÓƒ˜Jî>šZ–Toúßé€Ç¬!.Æ4zrDÞ@$Së“qUH’šZc`­NƒQKfj Ôƒ„ˆˆˆ(…A""¢g|#ÐÑ4:»DSâm£+v¥à`ʶú|c‹|½·GS~z #ùÛGM):ªC]ê=<¼¯ö©?ç,ŒM‚&^’¤ªq)J*ÈØÖ„ªµüVás"¢¥C<µ|Ë2Ï_H ‹A""¢>g}@HçÞ0^óI4 #ðÖWƒÕ±ÝFšXo`Œ­'GM›ìÔx\"""¢ù`Ѓh© ­ÔÌôVOœ¥&ÔÊH*§Uc &qs¹³ˆÓü% ·ÂIjCÉÕ–ã µ‚–ìN½® ',4Z¢O!wõEW£e%T›y$ð´>•:ötð‚¨Rëñ!MëM½·GpQMÆp—(ÆRŸFL’¼µd2¯`ŠÍüHWrÉ\ÇK/™w×å®ö 'oT«åkaiSëçŸL|nóÿNò&¢¬¿ûõ=ˆ‘Z–Î/t½Ÿ gòÎ(e«•“µv$øZ²L¡‹ì2œ?œ?‘R¸2O¤Uo)©ÕŒ gbL/N6ý@ZP'ë$Z{Í(•n’éCåróf×+`?_F9Yåûйp‰T3(¢¬RÆ;ž‰áãÖŸv[¸¸±®ÒÃ[âR²rÅD®¼˜W‚2bšž6åîH/Ä|ìá •FOÚ0–êá+ª>7{þµ÷×—·ì°bUl½´n²ÜïŸ*õlÓa2­4m?dúW+)ûZÆÅ·V²6o°B«N!¦sômTPë ­ò‡ Ò‚Ö!ŠÇ”F´à‘ ¶ãe:¸~“õªlï9?‡z|]‚•ÿ¨»Ä{ˆY¼pæbVŠ¡å‡#`‰<8yD© *¾Œÿ~þ«¨(Á-¢…Tq3xôŸ…«´Wf?I?ßHdê!ÖÃG.vp… *… \ì’¡/Q:áië¼¼ZS5µŒ¥!‡c«fPå~ÒK*¾‚û^ø*^)ÁN´€*âpÿ¡'Qa ‡¨g0èA¤p"øÉÔƒÔ)ãÿßw'*Ê]Q¢N3©»ÎÍ`ןƒs^¶Zée®¡(³ƒ"b• þÏïü]5"jàd¾jCm\äQ¨`r‡âõ’Š8|ïÅò¢“ºÂ‰Ç_Þ—wX-Y zP¦Ç¦§ºÚþ®’Ö-¹?<<þ›®¶¿ãÈÓ]mÿácû»ÛþÑçºÚ~·=tèÑ®¶ÿ“?êjûøAWÛÿÑ ÿ±`ó6¾9ÈQS lÔ‡“øêsÎÖ+¬o`½…uQõßzõ¤*±TÿµÞÂV,¶ýôŸ$/µ®1¯+O=ø¯ÉûSAŸ*—[[þï<‚'Ïÿ%\ŒúC¬©?ÚõàK÷·ýÞNøé¡]]mÿ'wtµýnûéË¿ìjû?9¸³«íïxyOWÛßyä™®¶ßmÝÛåö;pþ#nñD =(Óc3]/ `W©¿ïrwû¢{çø¯ºÚþÿ{¡«í?|ôù®¶ßm?=ôXWÛÿÉÁ»Úþƒ~ØÝö_¼»«ík¬ÒÓ#]‰%]­ÅxƒB©ë"ÄåQ%®? Þ#³ O?ô¥9—e`&ÂIÏŸˆ?Ùx|*àÁ÷w|žyüôðϺÚþC}ôØÙå ÇCîjû;Žt9è1þlWÛï¶ÿ9¶¯«í÷ûùõ&2íaµ„>¥¼ÝóRÓ‹fæx¿_ÀáIû›»‹aþ»{³»l×Ö[I|0ÿ—5&gfsQÆ'Øp"SÌ„Þ#^—sá@‘E &ð]#ЙzÌæçr޳ö¾ïó­+‹Šþù;ÄšìyÏnßÀÂJ¾X±8%¥‚Us,¦`$Æ´›€ú¿¼÷¼òJ2?‘p"Ó$Éh`£/a^ÓÕ„™V”$¼Jì\m#ƒ•æÏáÅcÚMÃɨ¡W‰.—èÜÙŸ%i*˜Ð/+‘©Õ†n˜ð{¬$A€Ù¼xLWŠÁ÷øÊ±àóÎzxiýi/Ç”mëºr¾žS@R0 J¥dþ¥™äß2*õe_‹’j,.rpµÖÃÅW·IWðpØr#"¾Tœ…x S.ÁW{ƒÈLŒÈ[x˜™Ä;”§¥‘KÌ—-ÌLÔT]&.'UgfJõ+åTBÖÊDp]A*áýÃO»©Æ÷?NÙÏ5^fà税•Þÿ}_S™(÷{Œ4¶‘ÚþоÍç¼»š>ƤY‰`ªÝwfªŸ{ÆÍ$¿wD¿ï½AEM^›g9ÓÊü=!«žÔTo›Y±ˆLë:iZÿ­¯*‰LõäçÚñØ)ŸQûý‡/+x:{—=ùýÿ/‘¼ç áó.õßýÙçyÏ}ŽÇ|Ï¤Þ sÏ·©}c‘'ií}¥E¾ØíÑòb„©n{ÖóÏ?SO=µÛ‹ADDDDD´ N>ùd<óÌ3Ê[Ezƒ=Ì{}ûöattfKFQÿ8p'žx"¬]ÜL  xPƒDDDDDDDÔ“˜È”ˆˆˆˆˆˆˆzƒDDDDDDDÔ“ô "êqßÿþ÷q÷ÝK³ü)ÑñàñˆˆæÂ QÊÓO?|àøíßþm¼ùÍoÆyç‡/~ñ‹¹æáœÃ7Þˆ7¼á ƪU«pÑEá;ßùÎ-5õ’Nlƒ5<ò.»ì2¼å-oÁ#<Òá%¥å¦SÛV'·Qê/<¾Q·ðüŽ¨Ï ‰ˆÈ£>*«V­’?þã?–R©$""?úÑdÅŠòÁ~p^óðÞË»Þõ. q‹1FÔ7ÝtÓB~Zæ:± Šˆ¼üòËòÙÏ~V¶mÛ&«V­ríµ×.ÐRÓrЩm«Só¡þÃãu ÏA"9r䈜zê©266&GmzíSŸú”Ûo¿}ÎùÜzë­ræ™gÊý÷ß/årYŠÅ¢|ç;ß‘ 6”çŸ~>-gÚE’“³šÍ›7ó¢ ÏujÛêä6Jý…Ç7êžß‘ˆ‡·¸å–[ðÜsÏáÝï~7FGG›^»ú꫟øÄ'P.—3çó…/|÷Üs.¼ðBÄqŒ‘‘\~ùå¸ûî»Q(033ƒ{î¹gÁ>-_ÚÀSÿ›õê©SÛV'·Qê/<¾Q·ðüŽˆæô lß¾pÑEµ¼vê©§bÆ Ø»w/î»ï>uO>ù$.½ôRœ~úé-¯mܸo|ã‡êÈ2SoéÄ6HÒ©m‹Û(µ‹Ûu Ïïˆ`ЃÀSO=8û쳃ӜsÎ9€{ï½Wφ píµ×ª¯ŸvÚi€õë×·»¨Ô£:µ ÍÖ©m‹Û(µ‹Ûu Ï&îöuÛîÝ»ëŸrÊ)ÁiÖ­[xüñÇÕù  õõ^xƒƒƒxë[ßÚæ’R¯êÔ6H4[§¶-n£Ô.n;Ô-<¿#¢öô ¾wøðáúß³Ç{Ö¬Zµ ðÒK/µÕÆÌÌ {ì1|àÀ 'œÐÖ<¨w-Æ6Hý©SÛ·Qj·êžßQ ƒÔ÷&''ë §‹Å¶Úøú׿ŽB¡€n¸¡­÷So[ŒmúS§¶-n£Ô.n;Ô-<¿#¢=¨ï ×ÿÖ²wמÉ=ÿb±ˆO~ò“¸ãŽ;x€‚z¤þÕ©m‹Û(µ‹Ûu Ï†AZön¹åcr?>þñN>ùäú¼&&&‚mÔž?餓r/ßÖ­[ñÑ~—\rIŸŽ–ƒ¥¾ RÿêÔ¶Åm”ÚÅm‡º…çwDTÃD¦´ì­Y³¯ýës¿ï•¯|%ଳ΂1"‚ýû÷cll¬eÚýû÷HJ“åñ™Ï|gœq¶lÙ’{ùhùXÊÛ õ·Nm[ÜF©]Üv¨[x~GD5 zв·yóflÞ¼¹í÷áÜsÏÅ®]»ðÄOøjÀ/¾øâyÏwûöíÇç>÷¹¶—–‡¥º ujÛâ6Jíâ¶CÝÂó;"ªáð"W]uàhyíÅ_Äž={066†K/½t^ó»ë®»°sçNõñÛßþvû K=©ÓÛ QM§¶-n£Ô.n;Ô-<¿#"€A"À–-[°víZÜyçMÙ¾àöÛo‡÷Û¶m«'ÅrÎá½ï}/6mÚ„}ûö5MÏ=÷à›ßü&n¾ùæ–vŠÅ">ýéOã…^X¸CËR'·Á´©©)€÷~áž–´Nm[yçCTÃãu Ïïˆ D$""ÿõ_ÿ%ÃÃÃògögR*•DDdÇŽ²jÕ*¹üòË¥R©Ô§Ý¹s§òùϾþüøC–(Š‚²bÅ 9vìØ¢>Zú:± ¦Ýää$.ºè"ìÛ·ÿýßÿ7bÏž=8ÿüóqôèÑÌ6®¾újüó?ÿóBZ¦Žg¬qÎá‚ .À/ùK‹Åúók׮ŦM›ðo|cQ?- ضò̇h6ߨ[x~GÔßô """"""¢žÄœDDDDDDDÔ“ô """"""¢žÄ õ$=ˆˆˆˆˆˆˆ¨'1èADDDDDDD=‰A"""""""êI zQObЃˆˆˆˆˆˆˆzƒDDDDDDDÔ“ô """"""¢žÄ õ$=ˆˆˆˆˆˆˆ¨'1èADDDDDDD=‰A"""""""êI zQObЃˆˆˆˆˆˆˆzƒDDD}ìöÛoÇYgc Œ1Å_ÿõ_×_¿ãŽ;044c †‡‡ñḋKKDDD”éöBQw}øÃÆ7ÞˆÏþóضm[Ókÿò/ÿ‚n¸÷ß?N9å”.-!Q~ìéADDDø›¿ùXkq×]wµ¼vèÐ!\ýõ xѲàaýúõ¸ì²Ë°cÇ<úè£õçEwß}7ÞñŽwtq鈈ˆˆÚà>ô¡n½õÖúsßûÞ÷ð»¿û»( ÝZ,"""¢¶1§œs8ýôÓqäÈìÛ·£££¸êª«ðÿðذaC·ˆˆˆ(7öô """@Eøà?ˆ‰‰ |å+_ÁK/½„‰‰ <ˆˆˆhÙbO"""ªÛ»w/Ö¯_ßú­ßÂæÍ›qÆgà]ïzW·‹ˆˆˆ¨-q·€ˆˆˆ–ŽSN9W\qþã?þÓÓÓøÙÏ~ÖíE""""j‡·Q“ZBÓw¾óL`JDDDˇ·Qï=Ö¯_x§Ÿ~z·‡ˆˆˆ¨mìéADDDMöïß7¼á xѲǠQsÎá»ßý.ŠÅbý¹Ûn» þçÞÅ¥""""ê =ˆˆˆúØm·Ý†K.¹×\s àØ±cøþ÷¿+¯¼²ËKFDDDtüô ""êc'œp±iÓ&LOOcË–-¸öÚka-Oˆˆˆhùc"S""¢>&"ضm~øaT*üÝßý®¸âŠn/QG0èADDDDDDD=‰}W‰ˆˆˆˆˆˆ¨'1èADDDDDDD=‰A"""""""êI zQObЃˆˆˆˆˆˆˆzƒDDDDDDDÔ“ô """"""¢žÄ õ$=ˆˆˆˆˆˆˆ¨'1èADDDDDDD=‰A"""""""êI zQObЃˆˆˆˆˆˆˆzƒDDDDDDDÔ“ô """"""¢žÄ õ$=ˆˆˆˆˆˆˆ¨'1èADDDDDDD=‰A"""""""êI zQObЃˆˆˆˆˆˆˆzƒDDDDDDDÔ“ô """"""¢žÄ õ$=ˆˆˆˆˆˆˆ¨'1èADDDDDDD=‰A"""""""êI zQObЃˆˆˆˆˆˆˆzƒDDDDDDDÔ“ô """"""¢žÄ õ$=ˆˆˆˆˆˆˆ¨'ý?ËHŽÙ IEND®B`‚yt-project-yt-f043ac8/doc/source/reference/_images/yt4_p0010_proj_density_None_x_z002.png000066400000000000000000012121261510711153200312060ustar00rootroot00000000000000‰PNG  IHDR=˜Ö‚ˆ«sBIT|dˆ pHYsaa¨?§i8tEXtSoftwarematplotlib version3.1.2, http://matplotlib.org/%‹¡J IDATxœì½{¬nÇuö›ýÝïÞKŠ—”=hJ”)Å®#É/‘”,K‰%JX(&n…à(–#²[Ô‘Ò6j0 E›‡¡¨¤bËP›T.lnT†`ǵ™Èzд#ÙŽbUµ)êi)¢.yyïýÎÞÓ?öž™5kÖš™½¿ýsxÏü€ƒsÎ~ÌÌÞ{^ë7¿µÆXk-n0t']€††††††††††††††††C ‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$éÑÐÐÐÐÐÐÐÐÐÐÐÐÐÐpC¢‘ 7$Ît‡að¹Ï}—.]‚1椋ÓÐÐÐÐÐÐÐÐÐÐpÃZ‹/ùËxîsŸ‹®;ÞõõóçÏãâŋǚgÃ3ô¸ñ¹Ï}wÝu×I£¡¡¡¡¡¡¡¡¡¡áŒàŽœÇ¾xýøó½ã|æ3ŸiÄGC‚FzÜÀ¸téàG_ü=¸¸9?-3®ÆŒ×X;à==ŒºëuÓ™Á÷ [ôqBôºl†i™ 6€öøGð·_øjõZÃþ·…<ùõɽÓý=® Gø'ý¼ý…÷áB75r~ºIÍ_ÌkRÜlÓÁ˜é÷ôHh€EŸ{ücø[ßðþ)Ï9ßíÕs¿ðå|þ·Leß(‰öâ½ÆtþžÜ{Wa ~þóŸÀßú†oõõ!ýþ¬L òñi øgûE¯!åï¦|y>Ósºö5}W˜s0ØÀt[³ÐÁ˜sáÚеþ:þ‡Oý3¼ã[Þ† ›óqÚ«3´âý¬2S_wmðþä×ñ_ßýÆÐÿ‰`uß—aHÓ÷¬´!ÿ]<øÿ}o»û¯ŽÏCû=§\´°»P—“gdydówé<ø§ÂÛîúÞèyF”ß/Ø÷¡eÑÒ õ{üýžÇþ5þö‹^#Œ;B;beŸ­¾üüç?·|ÃwÈ7-éÓ+àÚâµáÿ䳿ƒ¹ë»q¡;×wCûa­ONß§<þÅÇè»ø¹Ç?6¶ÿÂ=%$ß©÷|ÿ㎳q0~féXxg|ü¿Ž÷]¦ûÿ—üµ¨L!Ÿÿ{ì;:³…é&CÓ°ö:{ vx:¼úîL¥íûØ©½?ø'ÿ7Þö™Œ5îÞø}džšùÈÕþ:þÑŸü:þ«oü>¡ÿËåÇÓ&ã´M‡Bvø¹Ç?ŽºëµáÚáÖî`‡]Ò'òw°hÎ'0kþÅëøåÝU<ø…Gñ§ø—pë¥ã35¿~ù/~ÙoáúõëôhHÐHÎ¥åâæ".tÛE`g .tÛé¿’ÁÂ\hö =€qðó?'^«*I+®÷÷øÉ¥ñ_èÎe°`ºirÿ³ÁFÈ#yïn Çdx™ÉNHžßpÍUœ Vú›t‚`Ýó§.PcþnÀÛ($Ó&"Nü5œðàï¡¢Ìîù¥ ¯ÿw!ñ‘3]:1¸¸¹àJCò”-bü9#Òl=éiR]Ç`Ãè|gp±;—¦OŸÁ•Ç}ŸFX#„ã„©óôßÏÂ×3ÛOÏ~ªsÒ>:`ØUǺzÀúÚ–ÜÿžP%u-ªç¢úÕ?~Ÿ„>ÐÕcB6:£Ÿƒ¶?ko—v8BdP:ùÏ‘iùåëÏMùw¸Ø]Îçå#µ3Ú’~h4|zlà^õyœÏô¼½(F?3ºCŽüÕ?÷I?éêâJ¤Ç”.0}ÿÍ6öyl« 6±QzSú ù<°þŸµ%À9u<ž‡s⸒Œ¿&=üø Éèç†8é?í@¾A†$¢÷¸±Ý¥*=?»§Å~‹Á=—”¿LþÐw&ŒS|̑ڠÒþn:wS”6ç4í‘ô¸ç/|®_û=Àî0 cáˆtþFÄ ®™ˆßþìØ.´?›#¸ÖÞûXÿ¶äL<Æ–AhœžþLÚôx­1:l )2ÖO Ûø9(x]±l¡)¾§3&Œ?Źw¨§Öö¸¶ÿ¿õÒ9Üzk35NZM<0¦›Œ®™°=^uÛÝñ¡ÊÚ哯÷Ýú"ñx2ðFâ•htÀÜ•˜h°Y=Œ`°Ý{éN³‰l9~EŸŸ„ +š2ƒ¿çYÏNÊïÏßcF⃴*ááŽÞß½—^0>Âdˆùç‰&·ìkiΙL×ÞwÛ7bxÏÖ¿›a4ÌÍF!"„öe¯Â0è`M¨~.ÝGVŽý$Q^ ×» i<1}ÕsþBººL Põ×i÷Þòüè­î† 6(£¶?D±c÷ÞúB9© ëxîþ{oý9ÝÚ4µëàúK k·ŠH -1);½K¦|ÑoèEÒNZM|Õs^ßêúIF|Tƒä5^§ïöªgST§ ˆáUS× pF¨Å@Þ‰{çî»íCÿo‡‘ŒrýýôÜÆlB›Sß’²l’qÜõ¿éÅõ}bT¾cBÜ6ëT1é“´ÿïv©Êlj˜áca<º2vabª÷ãâÛÖ¦c¯¾ý>­>ï7iÚ€1[|ä?y5^ý+Àµ«¿#;‰íþë½ÅƯ~ηD¬±PÇ1ªP–Á/×*©Ôk{ÜséØaGéx¬Ä o¦lÔèM&{,í"ÁU³BgâÉÝ«n{IV6£U”gXm D¤1Âþ¦K;ÖC>;BrHÿkyI„¼ú9ßý_r›à“»7‹$£kâÛžqé雳z¥‘®¼<ò‰ò¤Z_¹ñªÛ¾Ñ—5*º”X¡‹®wD½f&%ï|ÝÔI»3²œÊšX²’iˆê­5ðj(é}}×í/Ëè%ïR~榅¨¯T•E¦Ã«ŸýM!͵ wI HÕrÆÕůºí%pã˜H|¸cœø˜ŽÏ.[Á¾ï’@ú•Œ%QíÈÜ·D‚¤ýBô+à¾[_TumíâÊ\ܧ®®_5ˆû¬”ü}¥bâC@¤ø0|×í/GBx$c\OÆ›ìpßù/Þ…kèa‡«…vÓÅiZè}}õs¾E~ÿ™öX&?´óòBÚìS¿é½÷9ÒË«$<Ѝ <Æü¿!>.Õï=؆=\²çb°GÇ–WÃ3ô8H%n€V¯Èq îTSÙú>nk •´Êä ZYÂÃؘñ¶»°©YÕªäJŠ ï>£Üà A2I'®>­=ˆH†èÞ>¥"âÈ’cfLBáAc’„Ä{Ö&ò£ ¼~Òã#Ι ÞðÜ{pÎM`'…ˆ/‡'É:ˆŠÿr«ô="â#7Á¢ÿC³¤ i„<÷C"cO.(©NíDÄǘöfq[)ºLåî%’óTõ/ð¡Ï˜iãüEõÖ}_ƒˆøØÀà/=ûî±ÿÓÒõeüÊr·Çª.´”HŒ.B r…ž#;¼ºÈ×;ZNºÇäcšŸ@NÎÀ^jœ{Íôž2~L„Ò\ÃûöäÝíA~èåWŒå™ïSSÐt6¦ õûµ­œ ÐÒyŒTþC‘¹…!I9* ñn J'½ˆñ;Ì6r­tiŒª0Gš_CßïØSx—ìy¼âÉ!j6¤žW‹ä¸Šñž¾ç9ß„ÍÌW%ôr‹‹KQIxT]/õaÀ1Û õh¤GCá‘®ÏQwðAà8do逗!@$£Àßóly¥uN9Â?úàá')%5‚B€hþ¢’dyœÒ #™¤ï| P"drò^åØOþò×I„Çx”Üš%‚ŒØEHÄÇäƒqÒõ}Ï¿/¾ž«<œ› UvpÕF¡ R€9=øiE;WÚStÍŠPWö* Rk‡ˆøb{:¾8 {R/‰3âFâ8ùî ûºpòƒÓýŸ±ÑÊr²Ê<­0;âà÷AAòKþÙÐdm¯DÝ1ªMvà¤ãˆ¸QµI”ŸD2Öô·žXUÜ\ÄûBÞÉ¢ƒKÐÉ-ÓÁPÕͤóý¼#G@êÃô<*ùQ‚Ö¶—Zž‘Úˆ•“á\wß󜗌×fb:íã~8W¥ZÛÖh=^ͽG øø˜®áÄ»ëPlLt1;Ìy`jÛÖ^ìŽ=ßƉxÞqDÚkïjj×ŶGoIÁêçqç̯¿ý›«¯ó97®}UÌ&só©CŒ {¢‘gfã'ÔªO¾3œ¢sÂc¥I‚Xä=Vv€Ô( Ï5~t2¢FÐÎ`ÖN1ѪFÝswgÈùué³HäÂ"_Ó‚¯èÌT¢Ì!>f¥A ¶¢ƒ—5žT‚’P³‘NÎbc°OÜ[buGˆÏQB-™·y®ò(\Ÿ-@¡Œ3ê…H~äˆJäaz‡~bi£¢²dÙ·–ë`L¾®Í„Ì\â#ì \ã Ï!%>0Õ;òŒô»zòcбáâÖãh2Œ°W¬è5ƒ¥uÈ=©o±A%®h¯ …øççª=b%OL|„1ÊbƒmB|„˜#äãGvíÌŽÇ•uxÙAÏ'ŠÀüx¨åÅnx#Ê»–ðX:‡I÷µ ŒsI®9¥#[#—³1çÑmnÇöü+`‡¯âúõOd¤+GfœäJÂpž*·Æÿã]\yÎÊÛôL¾p 2óˆ9¨®' ÔÅù©Ÿûq7²ú¹š8æU©œóó®GkiTªÐ£¹·4èh¤ÇY@·E·¹]w0[Øá †þ ¬ …O·x;ÂCDÒ±¦+ÙÛ£kÜ ¸võc1‰#øLÚ¹û¸z&Cð†bý}•dDÎÀU&z³ßi¦Á½€M²‰ÑXåÊQ axÊGÇ4q#î-ñ}B@Òj ;(e‹óÒúˆã f˜¨¼*‰ÁmD“4꧸êZ€$Š”¨åp¾îœø˜UÖèš`Å+Á=#3—Þ/U+ ¦±†ä¥JiÝ›vfqm/‰£Iùׄ@|,rsaDx¢êq¿"ñö\ùçȪ¤ocGM»,ïk*û´ôDƒ)|}lšAx¬ÕW¬Ï«U}0âÃ¥;×M®-Néq~ÊcG掂ªƒçJ9ïRØ!ÌÃáQB±^†…‚ã >VEVUI•3ñ‚Ä×½Úy”Jä密††S„Fzœt›[±ÝÞ‰ns:ó,ìŽÃ0|>zw²b»᱊ÏþLc6K|8™i0«Ì0-2ì‚Aoãê‰%rÂ…¿¶/z©%ÉðxN[ÑÊOÈ`ћž>¡3ÕUéÙÌÎ'‚ÁePé~ …ðÈ…wEõ ) ¿—Ú÷ ùÁ'ª%Âãò\‡Ä*Ó5Tñ0K Ç¥PûOG–ØñøHtyŸtÓEÄÇhgúÝÒ9ÁL$>Šp; íbãËb½¡n.I€Ò©lj}Kpà‰vi pu¬àÚ≎ˆð袾Ût[¸m9sć'@hpkqw/òRÑ5H1¶ÔLHã`n›ZûKHš…˜¥)-$ÄG=äë{Øá)ì®ý.¬½:ÅÛ¡ BŸŸSÿÙa¬ßt®Å”\åQ‹ùX«íŒtªˆÜ|:¼tî\[¨§óçÚuiêóNéqÐu7’Ä‹÷áñï¸Ïý7ÿãxBYµN!á¡N*ÂDYÜÀ§9£ÛÂ4yµ;ÀôˆrMl¹zb©j¢f`Y,‡ÍMƳ+Ádu¾b€’w¨ÌóɈJò£¤öXô>û5ÆVWyÄÐý”Ý1WÇó«ÚÚ.,þï™þýÜ­%΋¬¼óïdY?¢¤_­WÅ ˜Ì.©·Ê5.ýä\I¤½+Œ6[ŒäÁN¬¿Þ8D²¶„ÙqwæNvTg Ð_RòÉ©lH cC™b?½íUm¡¸€‰bføƒS]”ÔÀl8&<œ€ÕŸ‘˜ïXpS‡|pk5°õt^.“TØå2ùYã&WFºÿ£ré„móÅùH‰ð8!Ãm߀¨ù-ÞK»ÚQ—œð`7cy¸8;©úJ/é]6’äú’¯ïHãÈñÝŠø¨ŠÝRšÏHã¶zq^½¨æ¿AW BL1àèX饡¹·4dÐH3k¯c.ãèÚïã¹ÿú×°Û}.òà 81Í؃ì¨ñÎ"5Eå‡Õ;kvßâª@|$Ø—èY[€ÊxH†dÏb%Èšï%—–‚šÑ Õöœª#† ÉË«<œ¡¤ÝWŒÉ!“³AëZA.¹µäVÝÓ©‡5³’ ‚âê]’I2±'þ³¤QyŒ'Q€D€la<!Çi¨I+ñãöÕÈÓÐkb/Q~¤ï*GZQã,Ž¡DÔ}Ñ uý5Üòãª?³‹oRî3ìü·¨ñF’pq?rî/sžeŸ˜ìüþAµÙ1Ë50OîEx` *É%oN¾FÁ†­p["–Æ´¦ñÏ^÷õst9Ûa1qàÇÖ§Jñ+æÌe”wåw€9iâcVÛ¢vqêëõ,â«¡á„ÐH3€¾¿Œ£Ýgq´ûìpM!H}"Ä„þ„r‰ÛŠÿCÞò¶\FrxÏ ãÕc˜ òàßš¯äW×ó\þ¹(žÏ?E••@Ž„øÐæœóH%;m=½ã^ãfËË™< -£¿NQ{evHòišB|åYªºÅᜬۗó:ð"ECÃ1¢‘gÃuôýåøX©#«ìèöîÜ÷VyäoI9j“QaI.ŸÝÜÕ¨9£é’U½¼}(/[¥™ƒ²ÌrZ¹¯p})¥ ¸B>ž<ÌjØ>Oú4µ`–³3¡*\º®ÓT|Sÿ{åIPäGmã€ÇR)¶™•I‡âÎF@jXeܹö%£ª\¹ÂCºÎ¹WÙ)-c6á\=Æ­¿HÜ^8és—H™Ü.#i3#¤ –=ú,Y?!QeVKÝ?7à·Ð;Ê#q }—/9?µ)øº+_Tv’Ïb*_ÿgBÇj¿£ß.F5ÄÇÁêÔÌò. _Lt€óÁëÑóî-MaÒ £‘g“urX‹U H§é$@ž…-)Á Ÿ¬UbhyjÛýF1•!>²úÉWŽjIdrÀ%÷ʪÉ>~¢5÷­Ô´É`¬|Ò­^up”#?|Ò5±¢Õ%g|oHPÈM"3Þû½­@xTM<£|f¨˜Ï»é¶a«biÂî•j„ÜÀfRÔKW4© H·®ßìIþEpVS|œ”kKEø)¶Î.éqF ®B±UÛg¢ž4 I[×Y;øA?`ƶžI¾±´SÜáD >y0Øvº{BdÂ*il í¡Öùþ\í17 iÍdåÐÈÿݲ“ŸPçD—*FxŒ[N÷íC4y,تVKÜXu]Šmׯý“Bh§ U)ůqåÊFÞç1EÜa8—n:F¢ö õ7'åö·î­Œ,âÖ’ÛvÖ] “!/s„‡Bv$± 2;‹d‰T‰(ØÓÍE2ê£ÆÌÆö°èÐu‰;Œ¡_,Ûý†[|l¦ØÐ×%ʨӢð`î?rÐà¸=¥„â:ýOöù×&<¤ë²í!somÙ¢ñº¯'Fx2‘[ QP(}èªb¤öb±†œÒ„É“‹X ô÷Íß‘¦€9&Ô3B½Hæ>Ú­¹o•´=¾lÔÐpòh¤ÇÀƒý:¯ºín¼êÙ/À'Àòn§‚©Uýl"lé¾ìºŒ:QIˆôòêN®d‰m5†žËLDÕmv9á![{m}&ŒšŠ!3Ó"ÿû[Á¯ºvC–”ótùQF,¹-M®D—˜}b¿0RoÌ#CJùœvƒ(,Tx9ýþÄGU{“TSÞ;_êæ’›TgwOqÿ×À©ÕÈä?'‹v;²¿íê;ŒA«ëNuÜØ½E$;2îFYÃK’É׸†Tƒ§Ñf fjú`?š0à 7 &]¢#|lŠ“¼ûÓBx¸¼½Wá±?ñ[!¾NT¤5Ë ª5ª¢2]ôWVpj..}_ùÝQª|Ü>ˆ²kÍï3»OÞéÇ/òKè'YZsoi8Mh¤ÇÀ½èµ¸Ð“'ÉŸú8 dàXêÒ!MΨo7›Ü—&ÚÚòÉ5vˆTb|ƒÈX ć?&å¡LL¢@dÓut×Jyì¿ãP3)[}Å-)Ü?_ ² ë¶ÀÕÔ±t¡¤žbKì…㜴2_NN‚îÛÏÈ“¶u±ÍeÈ$i+hKÏÙ‰<èà‰ñ¢¡Øgûò ̈‘ø‹U*±BHu½› ‘X’díŠÔ@äú³Ø°gu£ZíaÒmA©ZÅÆlGCÑ^Ÿ6óÆà¦Ý;ôQ`FIm"î^3ã½”ðP\j䀽‚’Æae»nH`¦]ÓéxT6ù©„ä>D´ë£T–*˜ñvµe"ÿ`XÚG¹x9BÜœ©ðþ3‹SKˆµ‚ЋãI¡,‹¯–™÷ÞsËópÏ-ÏÃ×®ã]_ø·Ëóhh8éq&PP;õñúƒUò‰ ï @v8ÕDDxøs¥•"™"òÃAÝõÁö^í!¾“ÄO}2ˆ”­+}¾|’xL„G²¾ï [‰Ù“@¦,Ңߗ£éË+0Ní1®Ò»ôr“K²jÍäý²Ú£‚\ŸT|"„Ø1Ô‹ô{%Š¥ú6%Y =ÑË74œ4Òã,€¯øk—­Mxп_Ô¹ñ=²Áó¢g$¯¤ÖPÊ» á¥-«;¤†±¤ðPVláA'×aÒ;Mà§ôK«Òdbo$J,ž4ÍÝÍ%‹>Ö'…ºØû—Y#JªÒfFéžUË®˜ªdÍÊÒå¹}mEÅ Uy,,OÏÿYÛw¤iB|ø]]de\ÍôxrLˆ!•öÉ ñQÆÞ0-!<Œ¹0!ns;¶^‰kOÿ¬½†a¸Š9Jˆqu‰ºcxÀm•.)ì©È\:ñ¡ô Ö²§–À\–G)xlÍ6µiÀrá™(*‰±Üä¾ÔÀâ3ò‰Íì/Ù\+·hb§¾Úßg6°v‡a¸L¦~~ÞÖêó\ õpÍ~ㆴè)¡¤E1Aý ÅøŠ‰ GöŠõaÁ¸x0ÅlDZû_CÃ!ÑHÂ`]ZÝRÒ`Ù¸‰_ˆ%{Lv…ò×þš=$˜š!Ãÿfƒ«‹ïÜ"V€MõRMª=7¿¬ê!Uº,Ábò`EƒˆÔ\lLÆ¡D|Ð ’êÒä `/™Êç—Œfn@Œø(MLk@+äo×|=×T4©Ä-MøÖZQÛw0EØXFgǤŒ]ƒ¡™ž©–ððy³v›•××ôË4Me숷u¥J §‚ØÀ˜ó¾Þùô¶ÏÅæúóÑ}~¡|Ä(ö®Û}üýyMw{²}è¯ÍÆõ“›‹D|”Ĉò§®4îP©>ï7’~?ݦ6"ùiWÓù>PtãcyW—U-Û|ÔŽMÉ÷Hš./[íBÂTçB@ä©ÅÔø>· ùDý—E²ƒ8ÔÞu…Ç­âç²!&›'½ê©@ÀÐ2NÄ¿ˆ1OõqrØg»ò*uX¦¯ŽÙ½ehî- 4Òã À¢‡%Îaä¥5‘Uêy6¢¨;ªäzÒŠ!<¸4xV›G‚ÚCðûU™!ø÷Ge’ Ò0Q>N6> ^ž3»µçBâ£jR™Q©é®Ð&µ‡ÛF’Ô}©Äÿ)|H¸¼²¨µ¯ÙñXýI¶½,øÇï…ðX[摉ÑÄãgÌ‹Q£MµÉ½€’´ýñÞé07À÷@ŽðØG‘¤Ö­6RçÔ­,5f®‚óéqÔ眻x®_ÿ÷€u«Ù¤ŒÜ8NÈ­Xá§5}l”Þ¿#1øœÑ»ScBxšàâB‰dwOZw±­.¬CÅGìý–tÇ6ÓµqÿGûÓñüô.¨ä‡D|È"X4( m†ï*i£@ŒVPЦ‰L䯎<À`ëÉã˜ÖZ7eþ•ÆG«$<4—B2‘gî^¬<”8$®Ïé¸"+ªÆÖQ}D²²+PœvÍüw?¶¡á™‚FzœAÌ%<ª¥ {tœåÝ0Jjy°ZBxøUOs…Nž¬$G²uï¬QC&»Þø™ÊÛmn1[ôGÿ ÃUlìâOÎ7œYPî©&>¹sÐ]|\~9ÔÄ(N3j¶%m]’òÖ˜neN&>ô‚¦­r{}è¥>bñÁ wE ]½ó‰‚¹._ ¢‘wYVyä”V‰ú„¤OUãµS±Hÿ1üž’2oF>ê¤^PÚU+S¬ŒTåÁ3&â\é½úÄ?÷ß`¬£îÂJ’º¶H¹š¨GL÷Y;X‰”x5Û ›òv}Ç4FL$wO‚XM|Ì@úͤ÷G zz˜Õ£€6~Þè]Òs™<ý#?j\“æ(žj$š #Á’ùÇ„je”x2oÜü×øcݤF$Ä\ $!gÄ—ÜY*êe¤ö°ÂÎV“Âcì”Ø6 áAQw¡ˆø<Éä¯ÛaMûÎk¢í¬ÒÐãti°'ë;-X±,û®&ç¢v‹qøó• ºßI¤ÿ[Ë»2c´Ï3áûÒo+X‚*Š>ߘ_xÆ(Ði¶`co¥~T‹ b¶Êç~ÿÇðôpe*ßP.Ï'£’Œ\(},)ú=ÈÜÃõ1N-AHvêŠ%?g$Bò>¤:å¾­âùš@vˆ¯ÀÆjÊpÚï‡4*$#EÍAê UùAÓH²¤sZ9*q¬„GIÙØÐpŠÐH³ mR·ÀgõàV¥`Ÿd¥Ï˜iyò FùqLøRâ#Šç±ƒ®¡?ú3X»ÃÐ_~-Oúyb¢ö²—Ô¦¯ÔÍâT’çŸtÝEƵƒòÎR£ îÚ¢»ºø¼¥²î£ò`÷å §jO†|ÌîB2§îFùIïjaZ¥¬jdÜœÔ\^бEÜ–…Ùú!‘*>?Áõ ÂhœY» ±s0Œ}¤Ï3~'ZL§7—â1±¬Aeíu=Ž{ÿ_Æ`ŸJÊ•Äù¡ê/Ûf; !°©µ}DxŒîpãxPK|DïebŠŸOŽdÞÉ(ŽÈgxÓ¸%|×ú¿òŒ.­Ù}‹ÁQ‹\;9ºþù¬êQ- ý_cä·{Xˆ“'ÂC$ZGÕ€˜ðȾ+œéÆ–ì4qn(s€h„Ó ¥µ„‡d—R¬=ûFœÐ¸! úÞͨmW LjFz4ˆÒä$ª»-øÀîÙÑŠÜ‚T>¤¤±ãjRE§+ 8‹TšQT;€Ï!>àî8pÖÐ_ÿ®N‡váÚRöd%]0ZXåάÓ|¤ôkƒBJć—uƒLˆ×ÝOŸÞ[ 2i¬·"øûOÔÊD‡¨°8±&²aòÿˆÌ„BS&“Òy±(•í0V ­HVÔ¦µ4¿œr%—&=ϯ-íRrKc騮sj")¹Èòn±ÁêÚà³9$ LªË÷ˆ»ºTÀ2}ÿ¥àz£Á»rÃßL*K›R/ÜD±?x܃hEÊ.£úTœðÇ #Å#B|½ îàŠ¯Hȩڦ¾‘ÉvÛS™rDp$”×Îç0Öm[Œo—Þ¢xHn|*í¦” ebÞ“$‘÷@L$¸¼)á¡~3n-Ýu7ÃÚkè펥ÊVBÒïG÷¥ÄFŽðƧDmJú1ùÈ;iIó…ý£„wgq8I7²††éq0úš*#^­öÐIÁ¾+è‰ô5/“NÀV\èßupîü Ðu·àê•OŒÈÚò.UyäÞ« q}³]ž5ÄÛ.ÍøÕ¼q•$ªž›–9Ãq–¼Ÿ>7M£’àÝY‚ú÷W®à•È‹ùF {—Ò²'’sZw+ˆ$/O.}OºšþÎíd îD!§RÉß4sÕRÈk< “ÏTÈÁhðLÌh‡û(vx•Vº+òˆ©èB{–”éêª?Ÿ îÍ œ*ߑۦڭPw“Á[ù½L`3Õ! QÂâü˜Áok ”7 ê[V}Ì"<„þµF±íÊCâ_P8Wd—ªðIˆ‰Ìò탑Y,igd¡ÁÚ7¸å¢3IÂí4FŸÝ2BÍ]+¨<ÂIRŸ¥3Æ¢¹ù¶7ãé¯ÿ ôýå´,sÈCñZEQ1ƒðˆò¨pcáH(_JŽ•ÔÚóÇîb§{,tQ»Ž3¿† ûõŽ Ï(d'mÑ@ÙE?c´-L7ý–& µmŽð0ݼÛr£¾º-yË‹óç_V=IÝKå±ÂêoÀ+å|yà¬àt"<†Q*j‡]X5RžQ-S†JŽÕ ·’]ø§òg ªC5A¡]”&ÂÚ}fúU„\ŸÿaFêè dÌÅEðù{ÄeWÝ’öYa<`¬{K ˆLÕc›ôº=P›_ñØŠÈù¿çoäï|ˆf¢nR/AêÒLWG#ã˯ôöþG#<’ ºìYÕXÅHÊód…9”wðý©Å0ÖÊ u”‡Û’Wîò%ô¬_4iàO#ÎÒt%’X$<&U ®Zúqó ¨Tr žóí8ÙæÖÏ•'rõ¿¦mÔŒµ4˜óÜÅ™BºêõɈÙ¾vJë=¯øˆÊ;ìÓ…‰°kÈ¿²—wP+êörÚ‘„y† 3%æҦö˜ãÞ²º¥o$juL6‹Aý2uV#Šrm«–ðH.ékâ¾lõÉáIHx3®.{©§xŸ•S79Ù“þfeÅ”½Ém¢¦=Ï9ÕK¯€£÷ pª‰ð¿P_yRƒÇSRAÚô[p‘ðë‘¶L(/%pBÏaxÍvJkËÒ­œÀ J®RŠß¡Ï CŸ@ïIâ}dPêORµ1güˆƒ p‹1 § ô8cˆŒ;qp L®1[ }.`®Ãµ+»i''¿ÎL ö <æÀùDÛÈ}W\=‰À& Ò 1×ࢲuÑÍE)«Å;ô0Ø)G´º8D×ÇÍib 9¡¸«Diì³ÛËxY]“Fu : ©mP2FTRÌ«C!’þfj— ÞWâ&öº?¾Ë'ÞÍÀùùiB¦¨< OÔ½@œ4U©<5G4I£ÆO塯•8%~ÌÔE®ÊðaDáâ|BJÛ‘Ú;+‘ž„ðð;LXxfê.]¹cô'™5Ô,èfègx[£ryx·‚œuëÞÁ´ý.¿'ŽÑŒ¿äYXH øè¢ëÇç  ]RÏÔº¢‘lüþÄ•lAlý„F¢×Ä3:”ë—ŒL&|'þ\”XÐ õÙã±0ýDÍÝF:Å ¨ð2ù1 ê;9ìC˜—ÊÝãxCšžªå½ï}/}ôQÜqÇxä‘Gð“?ù“ø¶oû¶“.Ö™G#=Î ª‚0NÒÔsçnÇÅ‹¯ÂÕkŽƒ’¦9Ð0=,p"7ÌE"`Â#Ý6lôã¶>x§ .'iÜg›ÚÅ«ø’ÂÂûÔ¦ñ=²i˜ ,ŽdOºâRóLŒøØ3J>º< a§‰b`Fiu°öÝ DÅœ I6Ø›Ñ]FVTr$Æ85@È5ˆßoXѤ÷h™9[TÂD·f2¹ÌØö“4¶n®BNÒŸ-ôÉMÁªÝ€€2Ñ åQZ½3×ÉÛ\[A˜!Ä6Â@zò#M `œ e§1=Ö@“ ®ì&2Cr…ê¢çv»°ŒcDHþÈ-fzG<ˆ+WýEù¸…˜üˆ‰á6AêÊÍŽn²nL‘êI¥¢dB±ÏŠúò0FJí>GŽïMBäêO%á±W_êÆ.#ÇF)í¶S•>ÝnVV`$>¯^føëï°z{Xr<¤“–Űñdƒô#Æu`÷†gâ}vOÔj¡¿1„z;Csˆ@•ô^òCc8XòÛc®_š®±×`‡«†+ãß„ðˆc-¸ñ°ßARï]÷¯òÍ}=2$>ˆ¿4®i,°®»Ýæºî˜îf˜îb8ïc…Œÿó˜b..‰S„ö ê³úy¢-=X®_â=R¹¼ÈùE‹E(ý`¦?ãÄ?ŸÏå·‘NÑyKÙø”Ÿ|Ž'ºDKí}‹ÜĪԶ*\ZrÊ`ᜃ¶ :êKPfôé} bÕ‚; ›Æ'¡4c@gcP Ç\™Ý®Q_6ÝáÙGuÈyœËíX!.pør»Õë üÐc/±@èÃ=Á8‘ÝEtÝ%ïfbíUØáê”_¬2qîIq_ÅÛùøìÑî-‚K^TÖB»+n9<³¯•ƒ™kO¢U¤Ô‰ÐE¿Û_ØA'£äJWÅ×ÈÈ"×æÒµ¨Ÿû‰ê×Ê{N-jŸ%ã&µ±#A s,Éë7~ã7pùòe<ðÀ{çÿÁ~—/_Æk^óšèøw÷wã}ï{~ögF ŽÓpp4Úî,!a´u‰©ÛîtèŸÄn÷9ôýñe†Mj‰‘*ý$(u°ã¬ÜgêÁ¯³É®ª®,šˆÔm5ÉšÒÃí¤"³;žþÌ~.EÁáO‹«l®M\ÌF4Èô]öóíÞneê*®ü¿?ì¿£›ô¯¨´bb´§² n÷™ürß|ß­Y' ¥SJÁ8ë2‘ÓXݵemµH.o¦ìXLxm.J/ ¾_Ýï¶ðn5åHTi_)=úúh‡ –^’¨Ä•Ž+¹~of5:ìTªëŠð|Ñ.M‚" ‹Ç'~ûRwDAKECyL«3Ñ™‹0æütß”†ÙÆéвPÂÄëÇëˆÚ4º)¯Š‘n{Ã@%ô÷A¿e²›—w‰‘T7ä=Vô‹ó\›‚з8Ž o÷éJûôéJD—ÐVÙ=%UÈiPtH(a{ x<yäÜÿýø¾ïû><òÈ#Ùk?ýéOãÍo~3^ùÊWⵯ}-î¹ç<øàƒÉu¿ÿû¿xñ‹_ñ‹_Œ¯~õ«øÌg>³Þ4ÌFSzœŒ<ùÔDõAUñŠÙøÛdJ_8õGn…„B”– ®1ôoǪµG¤*)ä™á17–ÇšHVˆâC¿IV«8h|ò¢ç,(8’•æ9ŠÉß8QœþAt&Ì´êéÞ›ÿ6™Õ0þŽÍ†¬zÆ«¦jöDþN¯ŠR­È¯šTÊNìŘ{o.&Jr_ìBS„à¶6hÐãjh®,¹w»¤ì‘R‹¸Ô$±#:€¼óbÐQ©x ñ= ÛmeßtqÇ&vL­O|\Éí˜Bƒ¸òŇz­—¯¶`ÀÏ[¥¾{S~@h L¡'Űö:®ü…¿ƒ§o1xÎG~fúF™]UxrÙ\vî N1g•±žÏKæÆó:ö,RyÔ̵ÄÌh› Š¡n|VlCTÁ¯¸uóÁ¨ ]>ñ‚[J8ñò(ŠBú,üñöu«ŒNHíš(kÒx†‚ösI§©¢ sÑÓ„¯}íkxðÁñ¥/} þð‡‹×?úè£xýë_xùÈG°ÝnñðÃãþûïÇG?úQ<ôÐCþÚ¯|å+€K—.Ei¸ÿ¿ô¥/á¥/}éŠOÓ0ô8#H$ÄdòɘQ°8i%ÞºU” ‘6öULÿlpâ#ãN“ô õl"€œknI‘´,B°9z}r.çs ÌÚ5©#øDYpM ¤ÿî³"åÏàêïŠàòYçÏ4î*¢1Ò´…IjfÐ4‰¸Ÿ¤}@Ì!<ËV£"ß¿ð]à5ëÚö7g'…VDx$q¶Sÿ9ª5BŸçž£’DçàD‡DÒÔHøšUÏØ¤ë’â6å>sâ†ü¥_Sfo„²ƒÉħ%}B„ˆ$ #¥ƒ'g{ Ó8ÿ‰ŸÄy³ñoÄ)<¤ö*!<&CzœoµÝy*JGˆa"¶ï¥$BÙ­ÐOÐ¥¥ÆÍ1"?"E‡F‚Âdî²Æ–áZã9ÑçÛ&[ôJ2,¤«§e+¹;R÷ºªyT†ðp¼;ªÔÔ´Ûn» ï|ç;Ÿýìgñþ÷¿_½ö‰'žÀ<€sçÎá]ïz¶Ûq»ð×½îuxÇ;ÞŸú©ŸÂë^÷:üàþ àÂ… ¸°tÝøŽÏŸ??ó‰ÖDso9 °6(´ Oc`µQ>‚Å ™Ž/¸·ì+‡sé¨Ç™Ìd18ù‰¤†‘HÆÅˆ]C`速È"™Ä2)·äž£I3 .+"Öàs¾ÃDZ+­¨ºëO®þDubµ ìÚ‚œ],‡–770£ €Pã…(û]«,9‰³CIæ-ý¯ºVå%Ð7pü£â]û46@ç‚WŽqLwÁ»ƒcžÃ\¹|(é¥$W4`Õ îÚñ\Y¢ö©­“sÁM1Œ¹j`Vž”öþÙ3Äcç6zî$À,êιºp%S{$å³=\V·³š6¦¦ÛÈwš1Oêî²‰Æ þmâïv2*wÇÈõéá¦ùsÉ"lWÏT|6Œ3²B”¬ôkïÅ[©uR]ž“U##BS½¨ÁTGEÌU¿î `z¯…3ÞÏìY6ñk ‡Ò±S<RBââÅ‹Ùkßýîwã±ÇÃ÷ÿ÷'ê·¼å-€Ÿø‰ŸÀn7ök/xÁ O>ùdt­ûÿ…/|á~…oØ 'oA4Ø„IÜ΋üo환±IŸ &“¨= Æ!6q‚Ã:fȑёÛWÒW1sßÐÒ󨾤ûf{N©!IUá!úe³UÄ%¾Ø5òØy *ÄÙ’¤¢ÉEJvXe2“•̓.d™•\Zªü¹•ø/' ÿ]=XöRðþ]YpÓÊåvo9Οµðå/üÇ xùË_.^ó­ßú­€_ûµ_¼ñoÄm·Ý†ßù߉®ûð‡?ŒøHÜ^Žô8CÉè“(?Y\¤øHxh{¶K(NHÄAØ¥ï$€A­Ú¢iÖ‘‹Áu¢»Ï¥Hè³jÁ¨œe¤/0.¤5DÃίúÆ+tÑåÏ1uˆË£Nn?¿ûKI‰ã€# ûèÿñ`·I¡Žå ëd…´¥QÊ;q¬0Y“¾ý,e'Ï ’~~]‹‰°5 úã&B#$ç©açúð°ë!EI»OwVC—¬Ø+ ˆè%C²ÒüéyŲá‡ÜÃÆ%qW. k¤j$F"G?] Ÿ$¥‡êþ#¾€ô;kÄG4ö:W<³E×ÝŒns nú^lžõ—°Ù<ßçÕ,ÖDªØV•óIÝZaªÌÉJ’£äfP•5sk¡[[+l_oÂ;õ‡jÈawÚçÇlbˆ.VÒbIî{.탽¿­ÌoI¹Ä6,»çg:>ùÉOú¿5·”;ï¼ð‰O|À³ãï|'zè! Ãøn}ôQ|âŸÀþè¸Ä %´@¦gQ°;;„À!°ÙÍeŒÎŽ×ºU¤ªƒ ˜è3šCUP8‚mE‰íC¾¦›þªŠ±+¹³.Én5˜0ùòƒºfmåyOÂC:,H ³÷G±2•gØËß”«<€drmQ˜`¬_³qÉeNK_V¹gëŽüžy?|¢AÚ&ʓͱ éø :½N™.¼×éo)¨ðA±·» ìø¾c {ùŒ7å¾ æšS‚\á0~¸ºÔÁ:2Ãvr1twu4%:b5“L°Ž…ÔëjŒù*…‰h hõÉï¢ëýû2›xŒ¡Ù@ÛQ ¡_MdrI4¸þÆ¥=]ƒÐÎmfq2;–Ó~"9קĸ#ÎÃ` Ûè¿€aø:É/ìúÂwª²î7Í»´_s‡ø|&—æŠýÆ5Ö_ùñfš'I»¹]tJH¶Fü1Î΂@h¸2/Ûga\š®…C i­å}ë×\¿"?yy½2|õ«_õóx·Þz+€qW‡ÿñÇùóçñÃ?üÃxéK_ŠüãøÐ‡>Ôvm9h¤Ç7zÕI”#BñQB2™-M„<åÉë&üž&ÑÆ¸2NF²»Kš®BxÌí¨Éd-äËÈ9 l­Ñu0ÃL”“/ ;r*(+2JⳈܚQ¯j'>ªÇ'‹Š±4q‚}øæüùÕŠûE}ÛZÞ¯Í">*±øÎUxP⼊É8áA¿™ÿ<_·%µlådò넾ff¹ù–+kwѼY[x:.òcJ;·PäÖj #t8: âçÝÿÓ—ðîðѤ}åÊÿ·¶ëŠÛ­å©§žŠŽÿÝ¿ûwR¦†ýÐH³ÛO#\Üó•€pœla åÁ˜šC|¸2ÊË­NØ>&\¤AdZ=Ô&1i~º+@5Ø@˜5ù×¢d)ƒÑl#m Â#’˜“ÉUyˆó™ÙpƒUÂJÄÐ~>Àò ;ã ß%ä ½fR\ ¤œªö‰ùÞ@<ÅÄoó+S«ø¾ƒ{%￟ð„ö\M|ð>\Päûè}ës=bµÇdŒšfØMÄ[Uwî Ô0›ˆ\åáÒP\¬Jñ]¸KˆøL‘a3øßëN1©|Ã%î; á“ cfÓ!F~ŒÇÆ1ݲºBMDÔ×— ÷íÙaÌ9gXs}z¿›°ÈAû(F|½ÜŸE¾*Ó»¨Å^õ#“OÖÖ+ÊÛX×öw†ñs‰]¼\¹{ÿ{oâCÁjË©ÚØð!£›;âÜ®-7ß|ó*y6ô8KȬÕ!LÊ(ª%áŠ1ä&tbÀ?`¼“»;ðýás“šU·•ÜFVˆó°ÄžRÕ&3¹ µzrÝ"pŸ[¦hÇ ¾È*’Á»ü­“‰WÅ$pÉ„Mý&|ÅJ$>8þ‰ ^­usQWÝc×–äow»FLí£b˜ó¾kV1kȳ= ’µï`MÅÇŒ¾ÊŠj}ÛÞ«¬ÁH|˜!†Æõ¹0;ÀîˆÁÍT9ôoÓe íØx¢NÝ!ë×6ÆVŒÙºÊ£‚ðpÿOÊÉñúø´SxˆÈ´ ÚXC³M ç`Tna°…1çEc̘-`;X³Ë’³¡O«ÿ6a+ãû¨#?ªëÆ‚±~nÌ0mk êÍ8Äоć–7U¸Ð€ýì»d jržZ÷i[·Ð·ËÏÙæº—p`%à3…ð€ó:œ¿p˜´ï¸ãÿ÷“O>‰Ûo¿=¹ÆmE붪m8Ýh¤ÇY€ÙÔ Ê H¦ÚÎZöÙ®[%ñ«ÇÖM´z2™‰ r¿òLÕ&öÕ¯ïЫ$m2>lKˆÒù}°"á‘Lˆ¤´³‘õëVnüÄ…Û¢?êÂC½Po<Ÿ4M™0LÒ£nfYWž6ÊÓ»LÝçª,x_ø°jªß+º¹hðFÃãeÌ#jBœ „‡J.å'ÖW$„ò”ê©rÍ¢‰uÅwRÛê²5LõLîün.^í°Ã0\í.y¶¬ëœBd”úѽMY1ö»bH[ÕFÐIÜ"rdìšîVQ§é Ä¨¨æ*’F ááhGZ9ÂcK‚–öÀ¤„‹œø jKú5€Ï1‘;^úBD|žü(Õ#µ?Ü£ÿ«2ªù±jRrˆÉ.ׂŒ=Ówwê2°×ïÔC¯Üùtg™´¬jl š~˜Ž“T5L ;T´ýt|¢„‡ ö;öQÀ8ÁÜÁ;]Ý·å á‚*·ÊýFËG;Eçï%l£G‡þÚöšÝ/{ÙË`ŒµŸÿüçEÒãóŸÿ<à¯xÅjù6ô8xïÿ1¸ç–çáÞ[ì]è³ym²Y)wéño²â$ùy;µÇHÏPbþå„òrô9(5÷sCbÙQ\ñ‰d¡±kËxbc,+ýoàŒ'¡–‘rçÈ’E«W{ú'ëä‡)Gf ÿD~tDí!cž•M¼RU…\ÿ£•¯ôM DHԦ缃•VB¥xÕÿJõ“¿’Ú‚+¼¢íP»ÑàuFÅ0£«BÈŠ‡˜€àÏÌÛ­è΂ŒQåÏ –=Èš®Ô')*ý1¢"¬¨üÞ;!Ôb€±0¹ 9×bZ—Æk¤÷¸‰Iµ\s½%Ï"-x˜.";|Ú‹Ý[:ÿchÿäÏÁrTYæyªE‹´–ȯ•"yê+xäÊ@?œ¢Ž…¸ýöÛñíßþíø½ßû=üÁüHl¸^¾÷{¿÷¸‹×°ÇG¿5œ~ð/Ã[ïxEBxð]4 q«¿JãÒ¯X²ÝZnËÐ?Ûº«jûWÛïeØÔ¸¡hA¶æÞWKxt:ááTE”ð.a~-™lÍœå®W·d¤Li Hžæ¡ê~z,>7¶ ~.¾×­pŽ?ñv„ÜoY"ƺl{Ö‚Jæ·ƒ#’ìVLÈh]ž£–“Ò9 Ô(³Ö*[Žð0éö—<>EÍV°UÅHTnkëíô÷–.ò¶×b{!êŽhûOä)¹ÿsíN‚@$d¦iú¸)•ɈŠ* ÚwŠ FÂLdGJxl&…ÇôCãyP‚ÄœŸî™®™\a¼*„^ïóMû¨ºmlé Âxq«!<øü¡z.%2c^Â\O*£ªTéaíÑVÃ>ºýôÊ}žèΣ¼¯©mFóÅJw´XAÆ0ÿlÓâ o×4Ýêåù~d/W˜µ‘©{÷<ëÏᇞ÷MøÏÿÜÝëç{ø›óo~ë·~+9÷Å/~ŸúÔ§pûí·ã¯ü•¿rÜEkX€Fz4”±På!&Å'™ZÚdàârâXª¸›¼Ï R5qÙ“øŒÅÜÊ@QåQ"J#Ot_QŒB)¯ñ¶Ú;sPæ«8Ñ9(‚+P'<ÔdªŒÌú•r 2‰!ØOŽ êRCa–êçtÒû&º¤?Iέ”‰<<Ô.0K@ †ƒsWcð´ý«7L‰Œ“ìpv¸k¯«ÇsVÊ5b">ºîfl6ÏF·¹ Ô•Fm»|üqÇÀë•®ðÓûãNø ÷héÿ³óVoß)Ù‰`4Ï4žµ<$”U'”ÔØŽ»´ðqD$KÑaœ:ðÞUBvB…–Q%óϧ që˜IløûeßkVß:ø´õñ•Œ%D­0Vàb^Â{¥~Nê¯é1ÜÈŽJ£º8þ;ò¹hy>œÔ™mÏõ%¥ŸšgXrn Ž`ŽåçŸ?ôgøë¯þCü—ßûïf•ïé§Ÿ Š2åmo{ž÷¼çáýï´› üüÏÿ<†aÀýØEAON/NÏì«áX¡+ð³óO'ÆÙÉ2YYãª7¸åŒáúrÍ@8ÔÉÍœÉJ꣜$É`™íªxÊ{K"Ÿs5ÁÜàhʤ§„š€¥Kz¼j]÷í¢ÕÄ=cYÂÊVtw…´MJ“XÔsÏÔI2(œÏµ>á'/ì/r}…Øwœ”⣀E²rI»1TI1½ì;ƒ—à ý ÃÕX2?Þq®Z“Ê£{ºÍmØtφé.ÊmZ ÛUÂÃz¿Îêôìñ¤¢¯’À_îXz¼öÑ»  ÕmGŠá1þ ÀÙø÷˜ÔÆÕ­"Ñ"”í$d4>ÍP„.%8f HPÓº/‘¢4-³Åfs;ÎmïÂfs»W묢¶­ ¨}¹ø"™Ö~iÚKŠ4µ;Åò°vúŽâoUøŽEâÃÿü§5}Í©Š¯µþ³·>¿ø‘—㽿ñªïùÊW¾‚ßþíß<üðÃ8:J]”n¿ýv¼ï}ïÃåË—ñö·¿ÝïÖòÑ~?ó3?ƒ7½éMø{ïï­ó G‹éq  ‹°Æ}¡£M|hm¼¦ˆÉ¯œÆú!9ø\nrTe(/ À¸Wtn’Ï?¶8;ø"K+Ê+wm†ðÈ«$b„#ˆ#–‘VNdf|eÂCJk¾¿mvliW¸ÃJzJø­]› »”cätHž}jÇÑ!¡M«mj!1Pµº%õ+ Úõœ6}Á[¥mkÇ©/·´-r|Ajdȱ9”ïä‰-¾‚.+ph¹©#)6Ô@¯îqkRÀùàá ?‰MoðÂ?ú3}å}QO¤|4ýèØœoÊÆšRˆÈ3ö]kU>Ȱ٠Þ*´êvLI hÓÉ}L¤¦ùÛ)8&rÃ磱,w¬Ëq fLkœ; ãu˜æFÐLëÈÚñ ªq2CƒèÚÂÚãø];ÀÄ1LŒïã;¡Nm&Òãù0æ&Àî`Ì×`qmŠ£ÔiàìÂ’Úb©ÍÎH?{9zL{lc|»)™!X¦€­Ùç ꘱üó··I°Ù|^êköž£>CÑ÷=¾ë»¾ ôG„§žz ðë¿þë¸óÎ;ñº×½¿ò+¿]ÿý÷ãÃþ0~ú§¯yÍkpóÍ7ã©§žÂßÿûoû۱ٜΆô8#();¢]aõVKGÖæ9 1Ñãć`@OƒJpDໜXZÇêxRxpÂ#‰wBûBF‡­„ AMçäÙ©ÿó¤pi1‡Œ³.¯ÙÐæÇ ÙN6› >ö±ÍºçÛ¿ýÛñþ÷¿ÿ@%j8.4Ò£AEÜñΑæÎ½.Ý OS|D«IÌÅÁ“îKœøð éù‰€s„I?#˜ÓÿÙ]®’X“ªÃ¹µ ó„Gpoqyt°ØòCïÙa§mW½áîÊœöÍtgª,jTµ* ã4ÕqwD¡´Xåæt¬›#{vxÃpCÿµ‘Hô.ÈòóŠmp )²úªÖ#!†!&ÿ ¸vÇ}[Tx¼kà6:æI¨BCæÛ,õcmŽ5vMÏ×£ÃÑ1FRXsËÚ†ôhV¿KþØÔÅ"—´2Á³6 ´~;Z `„W¸ªî ²+H2!%×>“½Zþ¥®„‡~3W+pePXÍQ^É©Æ^šŒû2¯Ñ%úqÝ—ˆAÝq ¨}öp=]©ÊOÜfC"2ñ¬DðúÆÞÁœèý«#qÇÒWÅ{kêÎt*[&XQ¨Ž ò=Òx¡ö'ñ!•'߯‡ÔH”€0\W¯kb>”VŒ³Ô…‚Ê#Ù©ÖÍ%‰e!õ“¶Bÿ)÷·QZŽÐâ’¤n-ç'ä"¼Òã‡ÁuŒä`ìyXs}Ry„>ÅLDÉèò2©=¦¹Å¨ö@âæâê‹´+}æä¹¹Ú@AMÀÊÚ>oi¿”ݵ%*H 8üÖµfã݈Gµ¯{¶Ÿ‚Ûkal®íË‹$¼ð•œÀÁµgŸ¶p'iûX'ñ‘Ä»aäœ/¯Ð_1„]b¶í³l‹ÝDÂH¢4´±–¾Ÿ\ÀûbÝ|Εéq–‘qm‘¢×†c!€N|w2 ‡¿q49ɹ¹P(çkb`$Òþgzg¾‡a]q½ô]ããq}Š'×tu‰L&K²r ”(#÷æW!uU 䉲sQªN‰PŽœŸþÚpï+ûì¶÷+|ð`[üIª©¨]h$ãDr”|·ùm++©¤-&gõ)I‚·',N"AJ®€Ø´k²ÉðU~²"ê]SÕÅ¥ÆÈŽVQ)áëž“¸½¤Æ›3bâ8"‡Ë«êBá:¤›KxT–߯f»<øb'ôÚH*rè0æ<à‚š‹þ7Ìy˜î¦@|Ø@Øë°öúdÈ]ìf²@]ù&’cúæŽ jÄć7b31‹ö$<â´$Ãý„û ‰Áââzx…sǰño®LŠÔ¡{%D”‡ûg>áQºWz¯kÅ­ˆ„üðuH]Œ‹¡x ¦\ù¨ÖLu>¸c Š8Ÿ¨®–žý.rnyÒ\Ùבgðºá†E#=Î*f¸ª„˜ d冻ˆ Ð™j2gÒ0Sš¬ÊÎS4¤*T&žè@ Zü[x–5ˆÓ j•r”ˆi¢+MŠ4‰*«;f‘^ùQäž4.A¸V› œ’ï.ý´ÎÓÖÞʵ¤öð—­D|D$[´Òó¡"¯‚àÐFÌ ¡„øägÚj¹|5.EÜ-bê÷é÷ï£h†`âä‹ó侤\™~§4&h߉ì¹ç¶]cþÐ:Çß½ÐV+4Jß‚§¯bœ+„ØdÑÄtH ›F£» ’~Ûö*Ìðô¿#¸¹tQ_b¦ ÌÖtÓGN‰w­h¨I³²E*Éî>3ŒòÕ±o_1‘œ0 õlðs4ØÃ4nøÝ–9ÀÛ‘B*•жÔ5¦:ý|{O”%ƒ§µï·qu×tP»KÉM|Á¥*ˆ“ óÏt„GK·´_€£æÞÒA#=F˜t…ÆhDEro½\WÔ«ÏéªÌ^+³d²/ª>HÙÖ &8K qÚQpY)ù¦R#Š+ˆŠséF+3ê‡f QE€Dp nTé”ÍN"MX²Ê)Ž %;¸ÊCïÊ»®È“,‰¤\›øÈ®”uÛ(OÛbŒó ç=?4#îÄI¯æJAò’w¥­Øs#Û]Îwá˜J…Ñ]yF9=M”3ìþ’§þD¬ø('$Õs¥½×’ÂŒüP¯ãßD"0YåüÿGµÖޏq„°˜7‰÷¿‡¦¤U1á˜n§ç›ŽI„ÇæY°›‹#qa60ýU˜3*>¼KŒÔÞ=@²h1ÖƒXíÁTâ^UBdÌ[l¯!õ~ap¼‹'DyIŠèB… ü¹´ÐñBSyÌ$âè=Ñ![fíþv᱆kß-Æt€íÀƒGcltcX<œ½ ¹šVJ|œhl» ~ù¡/à—ú"†¾‘ :éqôšTjÏ;ÁµELsêQPaÄEJVŸ·5Á]YíÁ&e‰¬‘IЕÝYf‘µ8”» 3 Ôí/'Ì}6îŸ:¦±)“™A•Þ_Üâ¸P¶ðôÌó&,«©”w:{"“¤)<qkq~ðzÀ¾=ë f<­¬” †XìVV.£~¥ ~·lÕÈd Hv˜9ŸL¤ÜÔ0MH‰ ÎˆéÓ{Éñ´üÙ}†~Gêâ“#½4â#N*-¸—’ªj)zþç/¼}ÿ ý×0 W`íµ8m›iZ"×8­ÍòC áâ’tƒ—:åuw¹)"<†íM6çÐõç±±Ìn·mÈp“ìÆRFHãŒêš œÜ¬Py›‘¸p®Á]YêÉ­%¹XFxJ[»ŽÔîX5ña{?÷ cñ.ôU¥:$cK•ŒÒœ™žŽ®äsÇ­tþëo½ý­w੯á?¾ëãÇšwÃ3ô8+Ⱥž”;§œ1¼H!Á'w.K#¯ËäF ñʯ*ÓN\5T×"j&!JÐ+­,kDÖŽ¥îÙ¥×B+'+Ø÷O’–&ØKÕÙÂ2#wîÄ1Cš%\™k—Ž %Þ ¹<’z¾¦ò˜wÚãPˆêÝïÞbBü¨`eÓÔV~®Ô&W"[«Œ­ *ð÷6RÈ9—ÃñÀ¤1‚¶ÕÞIYjQò“ÏœKvqá;ÅdÔ 9%’|SY=VK%!;ÜßþÛup}BDþš >ÿšÄS—Žðâü÷Pû ÁŨ6Cü±~]"äÜZ– fÌ+,ú±ªšnlZB]ç¸kƒÏ™™b (׳úcݽåxÉ–†géq†Q=˜QÐÉ3iPðUï C&—dâeÌ&6NñI‰²r·h+½â¾9ÄÀ˜(—ÌØ\ůTvwÑÞOP{œóu†º/Í ¸'Ò¢ßj­qÁ—\yf`v]àÍÈÑѴm 3Uy`ÏÉ“û69ƒdeUG=(ÙËÚ¥òfU@¬ŸÉ=o­<™Ÿ7 IDATx©k]õêlm›'D‘Ûe4^™AÃoÓÒ¯p{”\ç@  MH•øHnêpV¹¡«&E(ú¯¶MvBvðoœ¢ÖöxÁÃÿ3,v8®÷-‘»Z)ìô,µ»\…ç™4¿KK7©:6rZ¶‡±_xÑU¼èŸúÖWàUê`è³LõÑbrqaî;sÚP¤LpÇ2D°to8ÀT%ƒxqX) êÊæ”9±JŽ“ÇÚí±[ËLÂÃãý­@>ÕÆP‰’)-tù ç¹´Äß ýß&×õUÃ.¼ûµÕ™>)·X(‘b̳††S†FzœQ”|„¦ U€ßíA#>ÆtuòƒK޳“I¥ÌòþãyÕGtoÙ ys 5ªÐäÝÇm•CS{Hª).Eºm:€úøáá|¬©ûÒ^DOÁˆ.{ÔhI&u)Ü»Idú>®,iêÆ&ú½7ª'ElÏU7lÕ.›eŽŒÉ¨=ÖDˆ_1„~ªG¨:#W¾¨ “Œ`A8®íj¶•.v‚Ê#€®l¤ÝŽió²`…­ †)Œ4Sñ1^£ìr WÞ©¦„˜C¦id‡Ôo¥ÆùGG_ô¡SÕB9ÕGäò"rªRmQ›œŸŽÛÝî2Î_¾†—ÿ›[ðÙ—¼ /ùÔs`ú¯Œ¤‡Ý%õ€¿ßÄŽêgêbáŸðFz ÙÒ`§3 ¾«Ç,Ô(-÷#?“¾A˜G ¿Cá!akVé=Jßî‹W âxT¼ôTN· fDdzÃò÷Ãû0i±0YàjÄFÃ3 ô8ƒH:7Á›—`:1Õ&𒯵Ë7 ŒBg®­ÀÍ >ª±f|Nì,µ´ªPEÎP‚£d Gl|>á˜p¯"—õrç(À^¿®«C¥¡Çÿ.ŒÊf¡Fš¡ÊÇJ 9²"“©¯¹Éj¸·b¢4³MÔÉÏìââ&߀k$¾+¼pæŒÎœ2@su)€\ÇŠDNÅ4—LU0óD%¿¯»8•w ×àÔZÙ<*¬z*j½ÙºHïëüªiq2&œ1¯—//!WI ¢’VÞ?i¯j¥O†+>ïˆððeJ• â8Ka{Œ*LinÒ:l{PVÓ?íñ—ßÿç`Íe˜¨ôäÛo`q}üŸ¥Ò…_ÂB+õm” Žl\Žˆ”´LµPÛÑ î‚)pò.*}úŒŒðÈUÉ–¸ä¸»H¿Ã^ñRªµ×ð…¢ø½Ö¨€‡¨?Œˆ 7T(bÔ²jÊW»*šo×,*Œ×Œ»·€Wpœy5<óÐjÇYF¡ãÒWo•î NpƉˆŸŒL“°ºœND¸´Ï€Èc ƉÏZéüO4 õ¾2ÊC»ÂH$‡?×%ß#ù&D*/¥;¦q#pˆêû¿…‡ŠšoPP˜DÉùôÒ•G­=Ž®)‘9‹ðà’»fa¬ž} ¶IiR­&2N°½ñgûHÍâõ-ôò{Mî£Ç$w…¨*l¥Õ%?*¤¾×¤l•ðÚ(Ïåü…—à£ç§Ð™‹cL³é¶Q>Ñó•µûúö‘¯7nÅyÏ"*šØVdÖÏU¹@%‰tâûËx”èG)¢ðXÐG&+Ú‚r2ºÞõ%ä=㼈=‹½Ø«€í±»å…@ÿ4ºÝÓ莮ÃôWY›Ÿ\'ìõñoð~«y }Eº=±´0“öIí5 5Y´±Z¼V3ôv'ÖO‚ð.I€SR“Ø(B›sý4ý¡×Äé 黟û£@ú¦s öóìX ,'"ˆ½ÃÙîDIÙÒqHûªºŸÜ¾× 'ƒ¦ô8cPUs$«Òõ\î¦ DÒ™lU8ÏZ”m–b9œr,Uy'9#XÔp’V)ý½òJ1¤«q«Ú~¥;Ý¡‡è ‡ä|Ý)2ò(ý ÙéÊ$Sò®…Lí³ÖÄå‘Òšó.ãzØaï@«Õ³뜑Ý7øÝ´W¸J,%éä¾Îß'õIÒýd•Lô//¸rÄijd 7¾ë ÍuÁb@ß ß÷¿}Ou[Àvc°i qÚRÙ–¶™=WY¥k©Ÿ<P•Ç\…cÕõ9’Äéjú¤Š«‰•ÅWÓ¹Ñ~r\ñ1–¾ç0Îý¡‡Ávº.ØÖö0ÃÓ@÷,<þ= wì*ºk_ÌÞ­Å> ;žØ°»p̦$G >Ò¸ER€SNŠÊj†Ì¢Iiγ2}|‰ðÐ\škvëãurTìã{Û£›‘è‚1.‚æsj =ßí¬>DRß1u­FbHq<ªb>'˜þWÇl®†ȹYJ ÂئÅ%ÏÕçÓÐpLh¤ÇBy‹ÎŠsBzò¶“„»àΑ¸»ˆ~â,&€&G·rì€bЫŠsév¤ÔOZ‹QLCÊW]õŸïâ’_ÉõK¨ä^ê‚1F ¹ø‚¼˜Ù2Yîš”q&ºÕZÉ ÆLB§ˆÜ³ìKXœáQ{ õ…vÇ2 O¥x/Éñ‚"€—E¼»¹crÂs ž¼dYsõ’ptôgxêëïÇ0\&Þ›(.†^° îÎeüùÕ¤3}]Lš í !Û™‹Ëì~#C—î㤫Dx„‚Ç<_MƹÌûÞ×Å’c$µáÝÌäºh=i±ßqÅœG¨2TñÑG_ÄÝù"`ÎOå°×§çí§¿'¢ÃN®-b\º’/( èu{=·œÖ¾*¹ÐÝ`êÆfޱ}8¥áï®íOªä~>ÿ*I¸^ ;r[ªj׬…Rß[ExæbÅ-¤ÉuáEÄ‹ZšË1â#9¹>™n4/°ÁÑZ1Ë*p„™ûª7œ)4ÒãŒ@"<$·‘rB›°: ¤í'ÁÓÄ8">” }í–†ñ‘3Óü?ç¬(å¬!B@Å0‘¯y>…_c Þ?¨™B–E„Èô¬’BÃÁöUA&‹F?Ì—Æ„ˆÞ‰kËß“ºÚSS” ©Œ5BÕ·Û×h°‚/t Š2HŒé ¨=’k¿hÏCŒŒ_*¦:IUT0´ß¦c@’Žà¦2ìÐÛ'Âÿs¿K¦ïÏÆXBxU,²"§¶OQw©„LxÔ@!)íêu ÈîTÌn£å].¤ï-‘]òÞc7] <Ø.-þYªË¬5»y#ŒIî)ùa&5€µ;laÍu»p}"#{8%Ȉ÷ýÚ°›kÀæ6À> ;\… bÊãwŒ ±C¢÷}ÜÈ+]>é± á±§«ZÕ­— ?^ÇǸ²h€Ånb¯ÆmSÇv“+oÅ{T0›ì(-\‰™Ñ ¢ö¢)ò$ƒž® ÆÃÜ(ßâ$P ?fʹÅ8°a`"ºl¸—V'#u~ÀáÿÚ•¡"ÉÁ}_«!­`ÑtuUÎ>ðFRIå1ÓÓyb£D"ê†]M’VðÏ€-‚Ô m‡Ñ÷½âÎQ<ñ¾Ô\ª[sˆñFl¸2ûÓŸþœ\c«È¨áŠ[gõƒyB&!\Jª ¢IëŠÖ/¦m±h8 &‚º Ù1ç]ò1¹¨ÂcÏþ˜{>ŒåÆc{X3ºMX3¸>¹HŒŠcú v|úÐu·ÀOÃOÁZ§öh€Ôñ÷&5Ùá%»3•ØÎÜÖùºujÔr„‡¢òwL¡[µªŽ ®Pb¿%Ö!—N˜ÿöǼ{Kÿ T½6éq–p€X.Šÿ~%ßîÆ¹†âóÆuQVâ# arS+T‰6ÀÕ«b­\"´´^U¹¡¥ÍÜs¢}æm›ËròÍäøR…EnbrŒ+ÐYdžoÞ ³»®Ž|Ü UÊ%í{VºÌì×TâCLðúD$£ˆXòŸL‚iòfƒ®{6ݳљ›Ñ™ 0f ¦ƒ“ÂERj³IÒ\,—I}`2¦‹~¢òÐR®ª T|б}ø‘Ž×â*÷­k‹˜náz¯¸é£w®Ê}eNqæ¬rQCÏßß%çý¿ÐÙD?áÄc¥ ×ﯚHŽå¾åZʈ%8P¼ Ó¯®õ¼Š!/’Ò„Ôz\RwŒ}ö&JKì|k1ŽQr|ŽZ$k€¬×VC€Ì(áAÞ“CüX’{ŸšK‹Ýƒ#™/ýå¯)»ømöyQŒ!ô°ØM¿ÇÝWe½*êý’ûöQ„ìåòçòOâà ñ¿nî|2›7›Ÿí‰ÞnŽý§¡ACSz>ú(^ÿú×ãÀG>òl·[<üðøÿþûñÑ~=ôP1 k-~à~¿ôK¿„sçΡï{\½z¿ù›¿‰ßüÍßÄ?þÇÿ?ò#?²¬€E•‡2ˆk+Íf‹®»„Íù—áÚÓÿŒÝÆ9§Ùýhõ˜2þl²Çw{©U{¸ N|,£òˆž5^•wRÊ6Þ[¹å¤Xèõx¹<Örm)ËÂ¥þ+ì5„o³&ù ÊQsþÈ7ŸU·Ze@ä¿üÛ¸¶8Ýâ¤Á"2Á'—(%–é‡RAðgÈPµ’`/Ááæ²•õ€ü­öQv°ãQÐ9¶:¹³Ðë\¹éŽ\3Ǥ y)Î_ãŸ÷Rÿ¬¤C±¨Ÿ(IÄi;®q ©”ÀÓ1ÒǤpÆ&W̸FËÈòU+´ƒ*¢Ù>FÄè÷°A´u-w_‰Ô,Œ$§ï»¸¤.jQüõ{€µ[u®²Çû-ÅK/Ê„•¼óW'¥×xwUI‰ 7fÊïTŒ}¤ªµ*æ|³ˆÑ *³%Ší¾ä[æ~¥ùŬ'bãXÈÇyк-”ÕºÝÑ€h[ð“VE64(hJà‰'žÀ<€sçÎá]ïz¶Û1Šñë^÷:¼ãïÀ{Þó¼÷½ï-¦óÐCᓟü$þÕ¿úWxúé§ñä“Oâø^úÒ—ÞñŽwàñǯ*S²zœE~àã°¶ŸŽíððßø&œ;÷B˜îâèòb¶é„;¼q…ÉWv¥yZa1ݦ;Ç~¶á¼¦™¬V–•®ddå&‡Ì*ñò4çIeŘtµBÚrXKK“uf°Ša[»Ò¾7:¢öØ"!52÷ù•úCtëKÞaíÄ'R¢eVKƒ\ áAñ÷SPQ$õÍ­˜re=ð0¦ój®†‘Ç´<¹ÝÐp²h¤Çðîw¿=ö¾ÿû¿—.]ŠÎ½å-oüÄOüv»t»Ç?ý§ÿüàñú׿çÎÃÍ7ߌ7½éMøÕ_ýUl·[\»v üà‹å© B¤ŠÜ7YÂ`¯áèèq¼æÿ¿`ÌMØlž‡nóì„øi i-Ï%Ð ¸IÉ8Áﺋ躋þÓ ¾í’êE!>æ ÌÇ‚Ï45×–ªxÂJÿ‘à'ó@ü½”¿‹ïM’úkçŽ ³ ŠÉ 5[saú½D|HÛ\·„š ”ÁÈR#:×’šGQÈêß©ßËÅ›_›þöÞ6è’í*{ºÏ¼w†¹W¾ $!@øR‚’EÀRÁæ ‰Cp¤(ýHd燪 IE¡ÂG)†J*Æe\ØÂ2P±H(b ËE0e@JŒ®…ƒ­¯‹„W÷Îè9o÷Îî½÷Zk¯µ?ºû¼3£·WÕÔœ÷œî½wwïÞ{­g=k­›_3ÿÐ×᤼*e*„û!(í-Æ=ëlê{Ëó°<Ó a&Ç|ðã.Â&àAÀŽ„ýÑp]IâS&ËÞËøGúœ·?j¿¬œD9ÀCczåÍôy€3ÔÒw¤¨f¥­$f7ýÁ™DÚ¿lûÆïMëZüÈåIjí+Ù£­¿£úùb®Þr™ÿvÙÅ’}vœ@Þô¦7¾ê«¾*ùí³>ë³ðØcáø~é—~Élã=ïy^ýêWãs>çs’ß¾à ¾¯|å+ýèGË26±ã¢z³öò‘à½ï}/àe/{™zÌË_þrÀ/þâ/ší<öØcø¾ïû>ó÷ÏþìϼøÅ/^4Î%†aN¼gêbxŸÿÏþ>ã× ]÷Ðì±Ö #¹ŽR\'(øáÿ¦ž U‰^ll¢èä”ÚJ c›q,¡Šùd•žeR…j+fŒ9ÆÌ+RÁ5ÃC*½VR¸ÐÇRE~ ÊF‡]ïÿÚïAß?k™Û7âÜ“øjòÜ"…À¾ÎÒíuÐ'|·BùËJu8VC‚Rèl VU¤‹`G×_>´™ìõdÒBÁ zšL¯&±™`2G‰‚øn&FvëxÈó:ÿÇ÷Ö{­011¤-£~hÀG­QW36o;†õí¯m à‡>f†Gëõ×^‡¿?Úñ-!y5àG&LmÉžÜ:/O‘³Èl•\“`/èÆP¾<-_›šr œ˜è“‡2åó³ÑÍe짤­þÙ{¸ÂðH䊹 “/: $¦÷qÊK‘¿gá½öúÀüL­5+ì%lŽg@ ¨c˜Òò³ —I<Û×ÀìÜ#÷ÎAè~ʺIÏ™>Ó±Ìû˜_ð°¾óº ÈzÔÒwçç Il*žYhsEi×Lª¬Çõr8LvÙÅ’ôØX>ö±…Ï2Ÿ‡—g?ûÙ€øÃ‹ú¸sç~û·ßù߉OýÔO-?^°¿݈kA©l_¤Ì ³9Ž·}Opî|RàÄùÉæ§|—ŠMƒÖ*·Ô˨o¼R `£ø0ÛÝ@š•µ}f(¿ìÕH:àQ3žDˆ±D”‘-ŒýÄ8Ñî7×\G†#Bò2Â9ã9FôxÕO½#zâ9-Þ\é¼~ý‹€Oy9pû…~+¤F¹…zæÖ€Ìs]ª¨iU\ª¿ìzµP˜W3dPƒÛ¤Žûs<ë²?Îè1Ï%kÎã›}-’–ƶàa­5ëbÑØ™^ž Ì»!TF ÿ|Xzô}€ïT2ÚڪϑìwJ7€{ÄéZ8ÚûX…±ÇÀr¼f8ùõ.dš͘`íïV<@Œ¿P±ª' E¡o(krƒñI3iY‰¾2K (.Á €Z牣MÛ‹à„ŠMÅÁ´í/ñeÝ”àø}aŸ‘VÑÛ\¨N ÁÖó3ÀGÓjÀ›•ºÍ¶É±‰¸pÑõïŒÓX/pÀÅÆŽÖœ\À]Z_»rÃSž[ÉöÐÄ>Ò×oÙ¸†RK-xžIÎ \zFKåq+= ‹’ïÎ fðDލob¤Ï9uàîLï/]ê>Fœ»[Ïü3¸§ÿIü}A°äxø¨mj‰Bµ‚Õáû,29¬kRZãÝ"¿ÑdÐôÝÓ@ŽéxÎ2ò,ãKÆEX”m3>c…˜€‡4€&ó˜–~íxNéÍ—mM×"€ùþô‡Gp¸ö(>þÊÿÏ~ü àôƒ#€ô¡®ùá̹ã }jàÓ{%'?ËûÞ(5¬ ±õ.·Oùcó²B=è’}g@J=Oö)EÙOíköc N?¶VЄ² 冫°?ð£Àú8‰X *È|u0í)`¯ÌõV W4¢~•ÞßV¾Yc{¬>jΡ}bøqàãҧ%Õ9±Æ ùNäÈù¿þäð¯þäßÔy—]î‘ì ÇÆò)Ÿò)áóñxT_µåæÍ›Ííߺu oxÃð–·¼¥Šå㳿×Éât¨X°—†bD:ëWc&Ôì>Òƒ]^Ñ5•ÂL¸K‡h\výÙ¬,Çìñ˜”àó„\†²3àH6ФÊçR<ö„Ôålɹ ä”g›­ÃÛÚ{îE¹ol®/TFšé•¡ÀÇ<KÉ$9;Á{sNµÓž«‰¨>¡ ï¡FI‘ðQjïÞ£•ÈR™· IDAT`ˆõšjÙ*ØÁQÂäçÉд P“€Läð’²·RæVݳ­[‡äoÍÙှ¿‰ë×_ßÿsÁ—½ëEpîÃø§€è £ŒqQH¢gš˜NGðX+µ¹IdX_Õúe¬+êpx®_îŸÜ€Ñ݉!„g¬bPvÈô ¹ á+˜xú'gÌôÞè€G5ÁNLiÔ ˆ¦7 C‡ÍÎk>‚~ä*æ„^DeºïZˆ|L×"Žëæ1ÍzýœdLR,s)ø´X?ÞøØ<[’{ÅïCñÑ—à/ü™Ï…gv}üâø»ÿþ—7ß.»¬•ôØX^ð‚„ÏÏ<ó }ôÑä˜gžyðüç?¿¹ý×½îuøžïù|Ý×}]õ9×ûk ôhea¶©ßÞøò h²Ûá€nN,ç=#wÒ¡ˆpÿ¹V™fç¯:u:±<ÍFXøVUÇn vØá8½²éýÝ’ßàd‰K©R¬ÎWãú àTÒO­ÁPø>xP ½8ŒÛò ž—ªq °Ï­mP•ÚðÛÀÇIÀŽ’(†Ž É“Ê}8UC²é\™IvÈ´JIÚp œ…±¸>—%B.‰PIc×H€¯x}ìyÌ-³º +fcÆ#(+ï„g@ݽû¾ì§><«…Äñ*FvHb}`˜KDãq6ÀSFƒ¼¤Ø*‘`¶ŸžGÃ\ØXDX ux@''¼¿~zü¯¼/}×_Áõ÷üÏÀEdÁfÃ' †£`MïÜ‘@A÷˜ç1}c]׆ù·­ò1l* ð1ƱÎaG  Ê™{]>汄ûìÿ–ÀˆN%?>c,«îÿ–ÌŒËhW­”awhÇy¹Öp­(¼—wÇéLed/3¼å>{Çv¹¯d=6–—¾ô¥èºÎ9|èCRA}èC¦Ò³-òƒ?øƒxÉK^‚×¾öµík¥Ÿ[bxB9ÐÑ+ÊF¦oïÁ¬hºnöÀQÊtèŠrXym BïéÍÔ@îaÓµÚˆs% |„>¶ó&Ÿ4$£SXä·UbÜßåD£gÖ†Û¤ Mü`kžÎJO)9>4—C>|’Çé8jz`P²=€àÕ,A ißÏ`“{0ýwÄØ=ƒ?ÿ¶ŸE×?Œ»ãÓá~wl°ž™ê2ÿGààw˜×M0àƒ± ¤Ìc˜öÿÌè>®çKà €KîÎhN=õÔ!e±ŸÒ“¼N5˃˜B4ç @¬Ê$\ìý( b†µ|Ì}iÀ“~Ô0nôÁVê —-ÚøkC[jíí8kl»ìrŸÉzl,>ú(^ñŠWàw~çwð{¿÷{*°á+¼|õWuu»ozÓ›ðÔSOáG~äG–NlÊR$pQ-Ì[ OeáU–žÍœG0|^]æÑ[¥…xãà±Þ­ Š>Ìc²ã,xª´¯+3ükÇeÙŠ‘E=Ï“ŒáûfÎ)DG&ÙŽùTušY€$lc°:£ú6€G<ØmYú|kçÆ=9*éÄ;™>jûk;Ôê+JH‰5îÀN r`?p6(m3Ã<väÆÅdN¨šxÇEÞ IZåÁ‡§wDÈ;EXE1Þw P2–àƒ\›úY{¿–°>Ôð#í:uàÃlæ% @ié¸0·q÷î{Bï‚àô{£Á¢ðÝdt’ðžø q@|hç˜æÊèM*¢­ ìœõŒ5¼Âÿ(Pž™S¡ªÎ|¼?™KÙJw €09æ±%ß…±=Kæw© #*kË0ü3}È*Xð(%ʶŽá¢•Xß]îO¹OàÊO.ùöoÿvÀ¯üʯ$¿ýÑýžxâ <úè£xõ«_]ÕÞÛÞö6<þøã&àñö·¿½m€•‹vUpsÉ¿\ã¼z¥:s|£áÕŒÆ+¸úw©™B™Òð}ÍÆàÓþ)ý–þ˜lu‘î½w2_Fî]†¡aõßubƒm}6µ Lî~k1áP#ñˆÑʹ–áÚ öMžûôŸo# ca†‹ÕljØ—í9+ÖiÝÿë#ø óm°d¡¹vD]w†®?Ó~ èº3ôýMö¯ë¯£ïo„ßå¿$/ Ä*?GL•L2€Ç¼V; ì_XÃI(K3àÁØ%C0’»-å*ñ×9†G’PÕÊ{"Ǥ‘ ñ>ùÏó^÷´¸Ö°<Ò‹PÖ ¹–¨€GƒH°³ø·1`ßóô~ŒaþÉïãïä<5¼$2>üZmíƒ,°gKùßrÆžÿ^þ“í+!ù g+f¨‰˜k•º}§Ýx$óxœC_”ýË‘ßkE»—ä_²zÆ)Ýÿg=…V¦=HÑ—T#]{÷—>ŸŒŽ–w"‘c]x(óÎõäÙ“y9 ŸC\.çß°›µ»ddŸ'×¾öµxÞ󞇷¾õ­¬š ¼ùÍoÆ8Žxýë_’žÃ€ïøŽïÀ—ù—ãƒü ;þïx~þç?öc?–ôsëÖ-üÀüž|òÉöAX@ã,7Ù™¤!*FB©M*þTÉÏ'¡%–6·ŒMAˆÐÂÂ)^e¦häXMåâE­sf…†þ“ã«i?e øc %4Ty©˜çUe5C‚ýÞn¼˜F§oO ü¨|Õ…µrÀ‡PÞrÀ;-9®ÿ&PÁ¿'`PÐŽ¢à£ëg%ÿln÷Zø»ëÎàÑõ×Ñõ7Ðõ7fÀcú? `JèÇ3^¼ÁJÙ9¹‹làŒ èÈÎ;åþËcèxÜx ÿ|¨ ²°$—¨7á# á c:Äõ7,'×ãû§ãã¡?M^ZÓ¶}Ìv,:f°HpCé7a>Òç!ž‹ŸKô7ú\“@›€ÂÚýI÷'ÐP/Âï~Ð÷ZòXá•‘tà#ëTÐö:åKÌ|:Ús2þió˜‚)СVòÙÉ0bþï,‚…þA–Óˆ>S T†+ ãÝ×õz‡”:žJm>¢é÷ Àƒµ-÷B¾~,fFï²Ë%ÈÞryôÑGñÓ?ýÓø–où¼îu¯Ãÿøãìì ?þ8ÞøÆ7⛾é›ð½ßû½áøw½ë]ø™ŸùÀ[Þò¼þõ¯01E¾õ[¿wïÞÅÛÞö¶¤ŸaððÃ/=¶ƒNYKÊèÁ8ò¼Yjn… ¥¾¹6 ®O“göB^¢HÊÃ3`Ÿº°úÁÅ!Û_ÐÑe’—…ƒÄoä¹å_Èâ cË+¥¤~Ó—œž}^†–æ/à%:þ>×+ßµ‘(Ä ’Ôxâ7 ÊÍåÞå*¶²E3\¼áK P6Ú6áÅôó¡,Ÿô¹Fú”kál¦øOã c ,ƒyl)°,“•òÜM•X´÷7g¤9‘Ìó»dóiòSñ®º©J= w’І$™"–¯5G 'äÞ¡¹'æä¦“dÞSyµ>V ÛH–'Þ)ÿ\ü½¯n˜‡ññ<Óÿ{¦ê##€Ôq2݃ôþNáC8†%@{‘µ÷%e©ƒÎ`‡sðAèº]_J, ³šÈüBrvÉTRû=Ä9Iæ±®Ó‘w‰´•Óÿ$x,×Ѩs‘õŠ„¶ÄJ=cÔË€‚ãÆpOKá.¥P£-Â6Ìõ£²­–y /†µ4èQy~œßϨÏ\ZN°]viô8‘¼æ5¯Á¯ÿú¯ã‡~è‡ð¥_ú¥¸yó&nݺ…ïÿþïÇë^÷:qyÙË^†W½êUøà?ˆoø†o<ñÄøæoþf|âŸÈöómßömxä‘GÚ(iíÖâ×^¤íp¥›5-ã8 5¶èq2$W9¢(¹û@û(l”ùñ5l2rÃ^ØN¾“”ÒÙtð²°cJ}+J67‚Âb€"%R­‘ª»Ü°LšÖhìšòD~kÉËR5o¼¢ªäñÐÞµšP™w!aL­ó61Ì|ïl à%XÉw ^Êð÷2Îs£#÷ŠÆ¢[ãN*±ìÞÍPåJñLÃgá·iLgó˜F„ ¦ÉüTrgX`ÇF¥>‡íyÇŒ87 ã €&>ñ&y§’á…õ#ª|0:ø!ËOçÞ}Œ\!éµl¿È•HOÊo†Î)h1´ÝKpÀ+€H €‡æy>ý=~ÖÃþÁdlùóŒ ~u¤ê¼weóò¸´­8väHsÿ*» ã¼J´½£´WÈ& €düg½Í|@¶G¥§géÌ"|€€¬$§%à£(ì* 4Ó–zhÆY“8OŒ~jZ–•äÃ;4=“i|ƒ;`pí`Q«üó¿ÿoñË?ñÿaœ]®”ì Ç å¯xÞúÖ·»yó&ÞùÎw²ï>ïó>O=õÔ©†ÆÅ*æŸY¨Fn149‘ëAn` ðxŒ†â¢t©xS¬rh¦¸´‚ £cg¼0Y/Øl«â9E_Å6ÚÊSÜø+é¶ñ~Dåqú^€%àƒöIŸ…ìW1Z«Ân4e달ŽÀT9ë»Ðžn Ö„ˆå7«rG«Ø•”wÖ˜Õ!V5Ê\EI³ñ@C•p€€ p2}o€ó(à1ÉS½ç÷Š&–ôã×:¡›B,&ÃRƃ–¿# xÔ>÷VFVKûIž-€`6|iÕÒØˆ‘˜ µ@Œ`©Ä £¬‘Ò½hy—*ÀÈš=*t­0V—÷@S¶µXË$Sʯ‹´ºº3tèñÐý \`ºÏÎÝÍI#+嘒d!Ctb_’ã§×€<Û#ë@IØ3m@X|4ŠO +Yˆö ©£Ãb’t]?‡ä]÷l^s:¯yàœITªîRJpªŠ s©`èÖ¶U#&ƒ­x°vÖA{Ž— äk¿û1|íw?†O|üˆÿòÅ¿p©}ïòàÈz\UQ6áNÁt1ÌÑ7ª²•lJ³¶:U”|°6 "ñ¦ktøÙ@ÐB\jKЭI`µØk”qR"6ò óAB™íº3”߀‚ñ aƒö*ÆìÈTýÊVriÕé´gšsVøḊ›@wÌùvàŽ˜BÏŠÀÇt  SpM ðÆj„i‘sËÖºAe_ZiÚøcÛõ÷A¦gx¹\Ðc—]jd=®º´°<”sí’üïiÓlïG‘no1œÂ& í$àGF¨×P»VPåãTIšZÚn1ȬŸ2ôÉb³Ý×út|ô+/üµŸÀíÛ¿Œ£¸6ð1}ixÀr^AÔ+eÜ`œËîP¿éaKÅôìQàDñ”jÀG~ÜeÆGSmÜ%ÀÅ8Fo?~ßs+ß¿ÃBXS&ö}ùÒ`c(+z$‹0D¨×8)¨…PQ/·9·g¦G ðèÎ_ý ;CçFŒÝ;F…tf¤%'}xΰð(V²mú}ã»ïÜŸÇC!”€z¬É;Ø ÄHH€£ sèõcÉ»Í@œT3i ª0¯cÿòÎ…³°vXKø ã¬]wäÚç ]ÊöÀä€ÇÛèp>³<|^²ö ¶GR¢[ëÛ¤—’ ¢qO ý{YlPü^:Þ&¨F#zI¨TK2ûRÖ¥/YÎd-œ’-ßD×Á¹ã¤)ø)‚#:×G C>¦öã{ž”µ¥@F ¿ì¸L£ELG–²¯Ðï81ô7^@¨ªrYr™}íòàÉz\e1¿$ÖR385X{§mr*»jÔºÉCcz—3ÀíÏ?¬Í?nðÂkh„µ$µásU€£¥²Áæk&ó¤`V±>ýWÿGÜ>–Qà"ðÁÎ%Ϥ¦DÎIªcyLç÷ñú(è&í€ð|W³Õ\IPÄkçbRÄç” Ÿì*Ë\êÙÚ£%†”ïÚv]C¨~B¨Ô p ýÐ=ºR‚W|>ôk‰ ÆæÐÀ€FÏj™ Q¨L긩Tþ»’á$%›äWüò˜8¤T}øð,4-áëÔ¿Á”ȬU!6~üìï Ÿ“ÂÐö®%Îâ{dœKúÌÞ§@”,•Èö€›þva}Ò ñ”µÄ;ŒáÉI˜“¾ûÈæ§’÷× áÌMcQÏÈ…BäÚM$yækï¯5ÿ…ã œ ïëtÏ}Å>ÿÌ}+q@ñiß~{–”íá™s0Ù&ÛCHµ>–2²€j -[xTŒÉfoja‚JÆÒ.»Ü²ƒW@:LØÊ¥’M›„0+Y­p[ˆQw¤Û*,£*,íÔ>Ô 42ôÆ3FºÕ¸X¨ˆÒÍ.—¼®%Ãw87ã8Fk˦eLE»]çÃÓc?w˜’+¼CùNDy•†…|¦))ʵ=´§”¥\=†¯UPä Ò¼pû9ÌÃW>éÆIgs€›+Èök…)·âyÀC2TdE)¢>Ë>Ÿ+Û¨†Œñ½ Æ„˜+¥ùҪ̖ØŠ"Îòúøq­!Ç‘Ÿð¬”39&¬é­ëõˆJÐÅ‹ÂÄÐDe%T$»f€IS1+…\옆¶ìŸt|þ“² ´~ä®ëó;zíÚ£èûGpqñÇp㘂äü ã4ï[x§¬ªvá÷5Ïk‰¸4I0ûÜ[æùg 9¥0ÞÁ8‡$uãYºv¸1y7bIZD’8Ï, ƒ±=”ßYt¬=§úÜ¥ìŽcš$u2¨:[c›þ¿À'`šXr±çÙ%#;èqÅ$ñ8P¯©<$Ø$†"_@•²¦µã¡Tbšñ~ö²Müœ­ú!Y­Àùnúà=9Iƒ"lx·’–3Éëú[Í95ÿ{¥´\9¯V×Oc¢,BŸ4ÉjüÍ@jŸaƒw5Qø5Ú/P¾?™þVyd·Ï&ýœ;@‚³ÔÈOj¯Ð³Ä `ÀGð°*ÇÄp‹ í=t2ÎG?+÷³Ò‰øêHòÒî çÀx7yÎÎÓÊ >| ¡sŽŸXxÈ÷O{׊Œ<µZ~/¼ÑÓ—ÔPŽsfúÀ &àC†\æÞ«’Aë™A’ÕPh7mH„ÌUÎs‹)R½_Xc¬]Ûj˜ˆþXRy‡±=4ãPç¥ 3Ð @×áÚµâì9ß üÉ?Äñø!€<—À0ñàÇ€‘Ì7Vd5¶‚Ö¾ØÜ†`OùŸ´êNÆÚ •˜ø!ŽS~ XP°PÉñ‡|RÓÊ=ÛbÄ¥VOX²¯ZzÛ°c}Õ]¹Ë.÷·ì Ç¿è#ÂÓ¿µ0ò·<Ÿ‹Bƒá…)ð6¬©<„fnÛÿMc°5¡BiÈåB›&ófÔ(q†ByI!L*½µ^ÔÈ0X/JÈP±têJà£ÃAey„S´g­ô™xú–Êàƒý¸nÞhFO)Ë—3Í·[9®•'ç*ònP×Íõe(å9Kîm €‡Aެ´#àz~@øcX;ŒøÑÐã ‡þ¹èºpˆ•,bþùéû×|Áùç— g¡ß­ðV'н|ÿ3@¨öY3 Ì=²`ü¹“a5$—÷UÞ¿[Ì…ª~kÏñ]ï‘ü½g "+ÉLÇQ8Ø©çÏ<݈‹‹OýÄññšÙ½3 ¶yqŠ| A´÷";¾?vð‰cçïeƒ€ôo#“¾­èú3¸ñ8ïÏ4¸‘éžç’›æÂ\jK8/;Zo|—J€G ³£jfBõnèóŒ›=Ç.÷¹ì Ç.ŒÂÄ+¨1Tz²ÅÄb…ñHÅ,-½7K«º‹]ÌDªŠ°ÞÔ„øy¾ë¯£ïoÌãƒãñ½xÞã?†ÑE6‡¯já“úDfè†;½¿Z^(S©¶€Ô¯ó–ðXò˜ ~,"Í‹*V_Ë»BŒz•cÜ+y˜|GZ iº®+É:«E>ÖBô˜Z2›‚åÑ)e»+@õþð,Ü|ä?Áxñ>œŸÿœ{:^ƒûP“d´Éø¾ŸÅdDå™5á§c“ì€ÀöÐ23^ê+¹9J@Ž´ÂKàC: m/h96eKð£ÂQU,C[#%§öNjò ¿»\ÙA+"ªÇ½vÎ…`Zd»þ&®]{þä1ÿ”Q«'Ñ©j›ÔkEÀ6$ÝõÙøÎf¶‡o¿&”å^„§´HÉSQ³ÙUƃnxl’ÄT sQ®Añô—mÞÞ*`à˜­¤*0²NÚ¢ÎIó3¨‰KtÐJ8þ{ÙÜ)ÁškuÐ…° ëHrXл™Ý䎄þp{Êé1èRÀÃv'’ŠŠg]S™+|,M¬“¥ö©Ì: ô°dŒÖ”ÜkjÞ÷Ž05 •ËÊ>A†EöB‘õa¹x|ùùjlA-Ip€„ƒËk‘Ã7Þ†ŸÂ0ü1ÜxÎ߉Œ0Vˆ4ºÿ¦¡›¡ÑË3,ss/ 6)cŒ%oût çåÙ¼ZßA½´Lp k‰9ºÂc3¯L·úƒ¾Ž´Šn“^*Tæ87• çö.»\’ì Ç¯' ‘‹ÉñС”#|ÑL8ý­ïÁõ_‚ßù¦¯ÀþÓ_ÂpþQ¡Õ¬¶½?.—jiè¹5Ió±1]Ø!½í¹¸oÕkX›»ºa’£ªk¿e³x#¯fÁŽ“ƒ~3W÷yc‹z+7ìMÀÒŽ&IÛ^ªOVñJò$\yÔ’Eé;µ™X÷ÝÑH3BS¦Ã¼Á}Ìa–Á©"Âz i[†Ï…ÓôÕ5Ù¼ƺb0¬XÞ °–²€ªïY5Ó˜ü;Ë“2N_ÚÌŸÉ­c*«ÒÏ »Ùȸ2Ûûìbà#+'2~(¸]º'ä÷axÏ<õ³üg x3¼é!w0¾ÍDž–T‡÷"GB5àQ;èoøðçNàõ¶†y ø€Ë—ým:r¿·°H3ãac²æcVïÌ)éyÝÍbGè1\bu¸ñ^T¢Ûå‘ô¸ 2ÓµEo¢ŽS"©èp‡ã¼yd6²@KgTy›áÿà¨t‡¨p#5Èdî[ŠÀGN¶NhYÑGs^5€‡±IQ%EVžˆ2†ßkŸEñ8m¾®ð|iFNü±‘ÒMÎY H²Ókž"’ÛæÒsl4Üì3¹TÅ'‚ëƒ «LjBÅØªxûõK„&Tç6å9|•©Bµ„ÑPˈ£ÇO2ã •‚’…<èÞDíØø÷x„ NЯ§õ[Ž'<´*à¡O¾ÿÚüHƒ(xÁ>JïDs>*œQÔ"&ãi–º½¼àÈ kF©¿Z1÷ÆS…lÁ༷̀¸$9¶B\(pÎëѹé]Îö¿ä¾fÖ«¦Ÿ–¾, C§5¡„É`Ûçš:oW‚¯»ìr/d=®‚ÌL€/^IÍô®p á‚eà)ã$ù”s†á)ŒîN<Æbyh" ñΗHãÔÛ¹)zeºžæ!³€äo©ôma5dدnÒØàÖ0<,:¶MÓ¶•تçP!‹7V7€4^îê’ €x¨@"ñ¾jÀ½ÿr~V^‹9Öû=O}>rðCþ~l&1Iäx™Çùøj#J®MÚ÷PÞ M±Õæ½ñ¼Ã»)€áìP- ¥Aû/}o®@èÈû†x-@Ždylåé¤e•ãwܘр¦P<ËÐÌŒI]ks¾,cƒù15†Tˆùl[×·ùÞÉp¨øƦk?ð‘Ûë“~úØŒmŒ4Tä2%ý€ëŒ#à.šÎÏ&¸·úk[…$cÈ={ðìŽÀ¬%Ÿ^²'R<:šîÉò«oz¿ú¦'0µY[v¹Š²ƒW@¦øn©yÇ…fÑŽÔëü¡Dï˜ûõŸ#1•~ã ¬Ps1³4æº;„˜ôé‹ @‰™ÒÛ”ž+|pZ$÷; ©D&1•­úi*I:W”;]£ÈSðQ2êdŸZ9̤Mbˆ*¥(§ó„ñêϳÀªFàÃ<$(E+ä @‡ö{±Œm…RÔÊHúXéIµ¸’^ŸVW¹à#9<]ŽU)UP0½ýÒ _:¶ð˜ÒµVRØ­1kï~UB¾9\‘¶±¸<# ­Ôæ‚ x\²Ñg[™uLk#û®YûN뜱šWhü)`^ðRw1ÿRÓº¡†^/»ìW¾CôõlÁÒ­¼;ª!jk6kíÆsC5´²±-ÃÄ9¥|ÉX—Y]þMŽ:߯ýÄ!ã2KçZ 2`µ>jD}&5 “–pÙ†ž’@*®Í<¨\ ÇÅ% _ú×^Š/ýk/ÅùÇïâ ŸóÖ“÷·Ëƒ);èq„0=éú™YëJry=G€)ÿGø;V HúöiùC.Ñ3à#P‘}iÊøÏXuT^Ú¿–·Ã>¨Ãœ­ÀGF—žGö›¶7vä¼ùZ[Z¦ï©í¶‡“º²œ•‰(žâìsØ@¸'$÷ÌÐù*d•VýC»& ™#€ &Xù;<à‘Äïw½ |ÄãÒ÷ ¯‘+„Y¡3v¢Ù¡lŒ±R𭪌ê©EÌ:ø! æ^˸SÁŽ&GsùÕ’±b2æ¸k°•‰U«Ÿ7 ;²Øöûpo ®2'‘jÉ;#Ÿ‹n¸äÿøü±î…dqöÉçØŽÍŒñÝ1®¥V²ës®_ò]õºi ¦~éÜÊ87Ìç3¶eèåü®ùr°%¶G SÇëž-¹¨"Cù€.r}Q“ËÖl¾ê¬WK߬ºŽŠ£¹ð°î;«Öͽ*'Ç ¦Ö.»œJvÐã HÈéADzá!l6t$°?F ? ÀGTV å´Zzvø •~,†di£óuqž¬íž’Ьí­¯ZÀ£ÍVf{L±·ù6@‰´¥U„X|$ÔO"„®E¦,ià‡v í|úÌS‰&ŠdxHÆ>’‹ÑK9³>äxå|*Üï¢'×´üϵ pçëmòÞfXG¹J÷Lè=× Ró¼hÜ©¢¦qÚ8dË# eÝ4X]­ ­eÀ©É§qnd€=`¼S9FRª¶z°ˆ {žð^³cR‹­ à£$ö}àÌ[¢^Pª’ÿ{Š]µVªB5æ”ÚrŠeÀõùa”w¥äÒšñ{’ÈÚ«a|U²PØÐÙÏæ{ Ä X|ü= \YYÄíÞi¡$qŸJû©6ô)[Æx9‡î’y×hµ0 ~´{þŽ]dÙA+*‰ÂFãU ÈÐu‡VâFÄ„§(7ÀuÒã_a,Ñ?i¬½ÿ,ów¨íxå'õ©´LÚ®|H£ˆà#Ø}ªðH¼ àCkK¶¾«<*=Û#­œ¡t‘‰'ÉÉ}W˜7%©‰gåDÙ>ÉQ+T‘- à‘´Cî>H¤æõ°b}ýa’ž]0ò,É=§-âx«˜ ðÁË<Ózo­O…‘Ö3ë¹ÕcÜ¿)€G½aZ–êüµžÂ <Ù€¾–']¹%G À‡7žÃ޼™pJmoZb|;Œs~,*“2Ǭ/Éò@:“j_àÃ+dOa8V…'åº2ö8M’°1ÂúX |´$ Ïæ€ÆcUþ&R‘`]›çÚ^¯0+Áé‰q%à‘e—±k‰£­ë-‰²¯&q×ÏJ\OÞ?ž “£å!¿g÷Ýç>¼§ÓߤùZðªqßì¤îŒ_BáïYª/—d Ðqáz\¸ú÷l­\f_»‹Y”Ý à#ˆý·bZiIˆÕ\K €ÖªhÉõ›À6,ÒvV€Æ:ÍZ·…Ü øáÒr°ð躳ð¹6œ«…Ý‘{F ¤¤{ðxD¨(èC‹Ó ¨²<2×Ïs#¯ çjã©-'*+¤‚ég}æùð¢Ó0÷) øÈ=çÕ±Ë'›ì ÇçFÐD¦]i±íH¢0~xO­ô@°)’v¥‚Aë²O‹m?·! bŸô#ÑkŽ|ÇÜ uT> c†›´(Ÿs *í#éš±ÉÛ·;ÕÙ Í+ÊWè7Ãö¨Î¼^¿ÜÔ ×Àsq¤÷9Qbµ92ÿ&é³àá?kÀ‡TT5Š>£½jLg„yÝ\ì}Âä©gÊ{—æVh˜WµÀÇl$5å0)Íoj [ϼF¹]"L±OÛÖc;égþþiì²8ê}ÆcÛ¯¿dÈæ*ÆhÀ€”õ¨ÆBçý9$aâr°6¾Ǫa7a†›Ö"ç`Öjáz¥çQ>CI}' yµ”€Ø#mé¾Þà9Wsk£YeÏ´†@˜àˆïÛˆÏÎ]†¨?ñçìC¼Ê,ܵÏ| ïé4^?†cÂb“÷kUÞåÀ먱¤ûsEŸÙj* EÍã‘´Dß.&+¯g³%ãdƒðëÙÀ˜j»ìr?Éz\qÃŒZø×0ô•Å—*aø‡$£ú¢ïYK<[^aÉN Ä8Y;^ÖS;ºžî(®Úf¥²=|$÷¯‘ÊëÛK<˜^‘–IT í„ïZ2Ž7JXêÕËöů]Æ™•$¿£ÌåžÉüÐ úî ýáLU‰îLáaþXPÔ?³1¹?ï¯ T̯4¯JŒ©žòù”è±Ó8ùø FoAZŒÛäØFàƒý½V¤W•–!%ÏbIõ© 5繘c€_º7†¤I‰Õîl¾§#Üxl,PFK× PØ £0ÓX¨ž#y>h® ¾Ã ÀÇâ|ˆc!¦I™ã1ð íùcŠÀGINÌziÊ9“½¶Š÷ߺ– &WUe.ª?ø1åŽ7BW,=¤ªj–2EÝc…‘?ý݃:‹@ò-m*â¥zå4&¡[’û/ ‹A·ù¸™C &u ýÕTår£¾gj¬ê )°y¦cjõ>enR½_K”]ò<éù8\JõÚß.»X²ƒW@¦£¼áS¤“ #†øàINuµß´â9#hÕšœJË ¢•u³J ª|p%"k¬šJI›@‚jø…¶ÂöÈ‚‚¦(†Ð¤ÆrøÈ°qÌaÒ¹Ú 7ø&c PŒZMÙ0æR×ãpöixè¡—†á£.>‚q¼=ýœñ3 ÉJŒH…ù¾·>—€ ~è¬/6½wËÊ<¡Íj*û´þ°¼Õq×Û(O*½ºÆ­g–åAèßúA=ŠÆÒÒp æ¸#àŽs¸=>V‰ [Ш×ݿˎ„Œ0¡ ýÀ˜‡ñšÉ}aÀGºG¨Ò²À‡z\íw9àØlnOmÙFXË#]W’|<‹€ÁÀ“á|ô¸ÐÂzSØpqŸžœ7fo)„Áð½/»“ø¾v„ãöjº¯åöñ„åQðø[cÐÚeï+ ­$)ÕÖ¤Ñ XnLçš.H_YŸïÃl2Ùo Õ§ä35%eÑò @\s’Zs,2~cc"ã¥á K 8ì-Ý_u0‹¹2þ:™¯GŒãyÀð‘a{äæ{vdïI$€Gb‰õP}–Ô}ÔY±&'É:cµÉ¾¬œ{³DÈ аøwø0¬d{)ahé1Ü'Àoøh¡KX€D®2HìЮI®÷Êè’ëç5«ÖÉTpk¶>¦ßl`;YðsÕ8½~Bä4ƨºÎæ¿=ë$Ó-ÝS}79‡28 ï¬|×e5/Õ™X=^Òç¯[€x”m”+ê«°E:ƒÝ¤†Ø”Ë@ßNªc|D#*[e B¨B)ÁÍ£Kìû¢È8õ °ÃÎdZÙkvªÂ€èú³™6t8Âuý”ÐÚM XZñZðaSú=†°(ì,:wYžˆ…ó³8§ržÿ ;c‰È}| ðaKIÅÂúض‘þðÃý.w;6 >ÙpRñřƛ‰u/‰æ•Íc{˜!. `ÖV ÛBV ƒe\~­S•çù»¬!,æ‚§üá‘»#V3M=ü©Ê­!OØáÇÅ~"L4™|[9Ës;¡ˆ—¥Tg‹0«¢„õBî9võ“R[IN xÐ=ÏA÷Òk¡¦ˆÏW7šàCŸ’£B~VÏjŒ ß,ÍT>ZE³Ë€t=СöUb_IƒX;vcGŠ Xd¿uéoJ%ˬ¸Ò¾$ FZ;è»V“dméj˜,Å}‹_§V¡nú W`‡™¬Ù`M±¶vÙå>“ô¸ â ÖX·š Ê>d‚9æíõ‹%PBŸ»"*ä|VRP1,°ÙÐ ûU@‡ªtH–A›mV‰§©á,)–EÃUUY“á8ÖF˜œØxY¤Â‹¼d`J2Ûy8QAû_â"ÏÑžKø.5¼Ë×=CIÆ÷¥ãm ¬îÏ"s,Žu7ƺ%ÛCÏ™sZUu>3l%~-àÑdtSƒÒ¡l¹ë„Gwb«€KW­NtëOà :Ù÷•w³’m“œSsÿ4vg)„FÉåa'Ö>Äý PžºÇ;·6¤à‡X;ç9Ù ¤%~6ÙìðÇàC¿¦u 7mSAš°eáXœR¢vKÀCÙs™-Z®Ÿð{fM‘Ì˦u¡EPðƒ‡CulîÔí‘Ih—`ö•ô9ëùiá.Öºš“š³gn{ÀåVo.±¯]<ÙA«"[Ç ûfµÄTT:£Àê± ð#Äc7¥ŠÒÑ`è…ß+Ã+ŠÊ.Q¬:o<ñÍQZ´~ €G"5€‡åÑYì‘4<›ò^ å±d(ùcî•”â§óÊiÎ3G)é|^•´6aÜ”YÎqƇC¤77TVŠ,Ã[¢n³ïZí%ó¶ä)Ý`½,@‡êùT~£RÊãQumÞöe%)ËÎE™çdQ÷‹À‡ÅRËUl`¦Uj=©êC®3iZóˆiyÓDׯ¹*{-—S¢B¬p*%ü„†µ´€—( ãemæÖ9\…­Õœ8 êò=•…Q GH¨0ÆÎ·ØuÀ‡uÝþ}±ªs-I[Ëö8yÕ Ú·å03Ø{¦hkŠšgÛ¨ù.;Ž8Xþ'_Ú{ºoV‘=2RbhÐߪs¹÷݃PZXí*q—]N/;èq•¤–ʪÒíS/Èôƒ7‚øðçÓ’¢žò×ÍýÓøjé9ÒÆ«• ›_­Â¦QLi;ôPSQÍ,ø]ŸøÊgüI-½éêâJÀCHnÓ[+‹ÃC YR9§I$c);›ÑØÊó$ء͕~ðy—S$ƪcê«ëðqúŒþÓ#à"à¡ô½ï£0–€þÔ Ø¹j%¼¯… \É /~ÓòMßЍ ~_ŠàÇškÿÌÞ¢•NšZÀ#s\þ½N™WI øÄ*N­]ï,†G1€0¡åìJÂ\h[*à!ûó›pP„Nõ¶é˜xBõàƒþ ®CÓol,3θ׋ØmF»yÀƒî)é5ò1,`ž–ý.+µÌÐÜoðQÓ—|”úÕš×æ[îú´9®9xüG–¸Ä1YpÅâ{ t˜Ì.¨j©vÙåÉzì@ß8«$ã±e4(ØA”£¨Ð/¦q*¨Jж0g”›š,êæÏ„å~®|àuËnàqеžð5²€Ð²ÉIÅ´°±7UÎYaD·Ðëç)Ji–‚, =a èBõî—™i¹½|»ºRM*"IðC o™N>è@$°é|Þ’¶¼ŠÕAï£|¨ù%ÕòÂ!‡tp ËC*â²”jR™¦4ž%ÀG2¦ ÀC£Í;RÉ%°=üäÇг¿¹¬aZ÷¸xhï‡ ­¨ q±÷ª%.eŽê÷dZW¦ÄÌëÁŠ4d½Å)Ûc_ ø(U@~M¯cã5æ!ReØ`‰ÒbémKÁ—Å`òVkùƒ™åk!zf±úÛQ÷V9{‡µve!Ú† ÛÀT5Ð!~χœQ€’ ð1Ëà.¹zË%öµËƒ';è± —–GI4Ê"cvÀȈÓÁq㢠«¬r¨õð›>æÜ!™xÇÚ¤úý³–:™FÇyƒÖ©ž–X†ÞÉé©U’QN‹^npƒÈ¨œ“;§YhÈP×ëïÉÉ¢ÅóŽú±48»8ŸcåÀfgH@ºfnLäÅPÚ¨¸ØõâS°.ù,ÞÇmŒ‰ [ xX†çʶØwLÕÉ"Ð!Y[\{F4Ï#ó¨ Kãxz–c)¬Á+Z†Gzbnë”­ð×®)Ƹ€ÀÃÖÅ\˜‹ôôO¢U@IÚPr˜Ø¡Sóqó=îF~mpC6giŒÖ—ªÄà…ü¶È²uP˜€ ~ÕÕ¿^¶Wßp:$àG ðQà Òççžx‡­äõªHWðHÚ”€G4ϲ:rs’Åeࣶ&ï.»\žì ÇU’TÜ’bamª¼ ¨Ôm”³Ø’ØÍ¬êbmxÖø YÔå±MÂh—œÅ ypK ûò}4Ç%RÄ(1ØÓ÷†§Ñ2þ´îû\JÚj7¨1$ @ªxÀ£&Œ…µ£‚'ñ79·yÎÀ4ȹ±º„4Ù KI´±Í"T`¢À‡¯D¤ •1üú@ôgºiRÓS@4 Õ„c¢ VÅU× Š¡oæÕ‘ÍgŒÏbåƒy êÊsf¹Ü$Ì$^¹Þà]v¾>§Ïº±ƒÉ|ä½á>É®É‰×ØË€˜,}G½imÐBuBCö}L÷²†çgÎsè6`{,Ö/ý¿aŸov$п}@5ðJ6ü(Sj˜îE›1>Ôp…-W<´ïXRpcýÔX,ɬxlvÐcZ€K”ßú‰wã·þÁ»1;ز‹-;èq%ñ0g(’šTy‚b5Å\– ,²=´ð…]!7 YÍEz·”RbK)Î@7¢§\£Íðhe:”Æ*•É…÷£mCãE u’>–è=s_¬Tmø1nhz¬8Nk+Pû¾:Œ¥†y ²~U^.pÀáöé/ü«_ˆ/ü«_ˆ;OßÅ>ö÷NÞß.¦ì Ç.Q*<À-q¢øˆß§IMs›]ÈíACc@ JcÓ R]§žž“LÅ´BÜxŒ›ÃL!V,z–HÍy”OÕÃBÇ'»MÏÕӇܽ“ã&ÊSh:ÎÂŽK)¯¼Í<àAÇÌÆOªE†>ºî!àÓÿ3¼÷‹®ãsßñ£óu‚ñbÓç;ôSD¦s3ty~dÆV  s™þŽ °¢Ü§)’å’BR…3©ih³b¬–Œz—¾íü9…J õ´q¯·&Ùñ"êPµÍ¿aþ½PŠÆn©Ëk"Ï7XB%À£Ã]wŽ›ªî#rX<ò ¦˜^»ÍÑ£ÿð?Âçÿâ 裇U‹ëNò†ôèú‡Ñ÷Ï0ÀçîÅìx“°•Ô‹–ìø€•s¤¨ ææë‚¹YSÆ›—Åp9þÄ›¦+«UÞuú;cÉùq–ÙÖøsy",f‚~ð¡| KÄ gG`4ci[‘SÉS­„6ÀaPá-°0@‹²ÛÈR°ƒ'KOíŽEàƒ³=€<à£À‡L¨,™¥õ¹;*ö”¦¹­¦œÜÕt jàP×{S,‡C˵É÷ÙØ³Ã!à ¼¹7·ê€ ÀP§;V†ådÖbÓYçx™híݤÏLî—v%—å€G•=`°=vÙå~‘ô¸ ’1Ä—æìà)‹›Tn¨Ñéj’š‹l^x´–5R.G LÊO®_®ôU—slÞÀÅFiÜÓ\íÒr›Ò†•çh*q☜'‡*X àa.^DÔWA7÷Ÿ;b¸xãÌÖpîHòzØâ}w×®}&ÞùŸþE|ñÛþW`<'† 7žÓ6ÌÀpè9ÛófÖJøà”vÖ2·¶ÍJ­U“ò Ú3-œ—°<€b:vz^Kå¢×zâr€‡ÊˆÛâ/ºø€ÜXqºT©I^˜’ WÎz UL”9M߉*qFÜAçb^˜šŒë¬ wÄ—üã÷âÁuÇࡎõ¶2·Ý(ÀŒZ˜K ìZÀ¨rš[—R6T6)©ÈJ£,T5¶GuROå·ø‡âµK Fœ¶˜Ã&øÑ j¸UUxQ½³FÖxåzΞ'<ŠŒ—¢÷¿ÑKŸíláý’ÞnšØbÔäöˆÂþ±¨JŽß’Ü]‘Ù(ß7 غõ è;åÛœ×Ó kÄu~úw=Ì÷iÝ<ÚÀƒºÞlè41÷á“°’ʉ=s%`kE¶ß⢆†X )= ú«yιЖ“§ëÑug*X”;¼ã¤ïoÝãðÆaÔ“ïûvkÙêÉËc$,íû]v¹Ïe=®€tè‹I—T{=“s%8¶¹R…É9}Q]–!ºnÓ_"RyN<ò·€hÇl¨pgD*]L© Nxh›â¥^ x´Pi3Ê›yJ!¬…„gkHÖuÒøWªä+a ¹ñù`*¸}µpî.ÆñOàî<5½GÝ&†Ç€˜CÄlhî︻@&¿5Ô<søY¼>Ëà¡¢¼¿ÖPÉsË*fÆó©‘ìñF"ºxn ~$¿/sÞçJøHŒQfûp#1(c“¢â^kXß)¯ÃÉüZö=NzügVÃdH6¾‰ÚRç‹=É”ÍéʾX |¦“€ƒ+ôÞó<ïîlZs%"wçpã9Ðgßg9æÕa#§v@í²Ë},;èqÕ¤j´ ‹§šÔT†¼x·P˜œ£¼RI­Nb†O/ÙÒóXN;€œgß+þ=¤Â•ÈEJ\É02ÃRz~\æ˜"àAÃ[ä04%™]§ü½Oß•™ýAsc€›ç¾i‡n¼×ÍߤbyF<˜ªÅtìï˜ÉĦ@ îÐ0™é€ò{a*íŠ$€£¯PúäÚR8Fͼ_?ÝšøOyw›ÃÏVp­ãm)Í›°]¶XÇÙ\ÓêEýH h¿§".Þ¯¿ÜÀâì15 âF¥Î[ÇMÁš°]‹½œ²ßX €‡ü;°C=‹Ž­‚i×"’­ÀØ6ôXc¯¿—§>¦¶5ð#î×Vîµ:š%' ^Ò€ò¬rm&àeºŽv8̹¾<€x½§T,µCë7¾·^üâú{ÿnm ïs×£s=/ѧ°f¼XïUí·³ ÑÅÄd¾Kšk†Ò§Å1Öß&;ÃiÏ+5”í1-1ªhžò=a÷tÝ#躀;ð b6ü€9ùšWÐH˜K|8îõ4`üÈTH¡¹@¦kÏ_b×Ía}}Œ½v]n<¶ï¨fÀ©ÛÂõ*7G[)àMBò¤*]K%àѤ؇{:˹12 Õ\Ž5 ‰žï{"Ϲë€÷~ûãßñQü¥ÿåï¸3ͱŽIï‹ý‚5ôY‡œAˆ"rïI®míûD7à†ûÔ®O9ÆcL¡¡ ¤ …9ÀdƒpÍÀ5Ãg¬ý>D˜MðCMr®°?¦6<³­D®Ùó°tr’Íù" qƒ$ŒH9Ô9foã8þa<Ä} ÕÔBRV@²=.KÔ°=ë·]vyd=®€t]!¦ýd/ôpiá/Z˜K§Ä;û~ɪ¾ Ä¥„ÍÑêßÿg_píù¸ðï× xP#ir L„%sm ÃJä6ps¾ iLqŒæ9[É<ã$ l=?¼¢zó€«å*Bí¼‘k51~7òì«á<Ôp¥‰º;ÿî  '¬¯‹çÙJ±J¨&U²*ÂBj×U.#bµ³ØŸÍÌœXp,¡Ç>ò¬ÿÏ|üÿˆýÏÎ_â=©Ž¶$DM y2«`5èµjâ×q±Ë}(;è±Ëz©0ˆ[AK1¿)ðA•5 |0ѨʱßÖñ]–È™z!¤w9èu>\öµm vd=+'ÇRe×`{Øc¦×•²qd÷¶?< ïýŠ×à¿ðOøÚ&€ùäÝ®xOKà[•n.…°Êé>_F×_jÉÚñûÚåÁ“ô¸ "=ž™M›Qí,†ê!ŸózñJ­Çšô³'™Û8ŒøáÍ…ÅØj´Y¾!wݵh´Ã”ÔS3)Ptƒ,‚4‰—³…%a3ª·átžÓÒ˜s céþ,I ºDAæ ¤lEÅ"ó"€‡øÛxŽ—üË7ü1Üx‡»r{8+žã4£']ÌKß?€Pa!÷¾)ašÛC4p-¥œ†™-`xèb9¶CIª¼‘]eÓÄ‹Woä&ÆE’—çñP“lZ÷·ð(/ìÜb^ –J¶× ˆ\'¦Q:W”ùä¿w0·ù>àsJyÂ1KÑœh]Û½÷竉LSÖ[2¦J™0¥wÁfB@Ü ³‘êÁûºc˜=¦eÎØ™R;K@Íö7ÿÍB-u¦©òÝV¬!Õ¹áAíÙ¡„Éœ,UyÌ´0^ þ`•ò`GÕ»¦èFn| /ø…×ã;¨a.,¿Ì)s~äyèÎîØå>—ô¸²´œžEdí´ô-6ù]Y* >±ÁÉ0—IrÊØùÉw[…gÁÀÉóÐõ73‡OÆhðÔ]ê]Ï*UìzÝS†^Ë©iáÅç €@ªÔ–„<ó–¢Kï…´ãÓ¾ø{À™PóoÔˆ'a,1¬Ä³<Æøp#†ññOæ6‰BBÄß~¬É»=éøAi¾]é$ÀH?*ßE6¨Þr 0ä~õè û²Ã:\«P£åð(†²˜ç5®'”Õ!“cÏsˆ•N÷ùz–ô¤À™"Iéøèèº3tý¼ž;1Ç‘7&XÌhR6Z>¬ö£Í0Z—X-çØ9-t©É7¼óþ9«ÀwdvÝCèú›€0ŽOcoC<¬„¦¥½Œ½¿l³@ûµ,.V.5ÙtŸQ€€Í°cí7€%™ÞÕ³¹]ÁÔ¬a¬Ÿ„ž5}'YþùŽp8Âð#öMÝ&cg¡g‚ùªŒ2X´0—pþ©t­à¡éô»ìrËz\I.$þóžüðì+× Èç©æÎ‘rµVM4)’¡Æ⺡Ìô»lè›r ×É-=o%–Çê÷©”S€Žé{ƒÕB¿+Éû¡-÷Uà á²0—IT‡€²ö Ò®G×_GßßœÇwÄ8ÞVsíäÚÈå[©%fȦ%È«ÁfÊÒiqš ÈxˆêУëÆC½n¼…ÑÝ‚swÜF5ËOa–†Ÿ2¬^-¨]´õL®ÿv.4á,bcçàc}óèü‘LZU7Û@‚äß»pc¬ÒS“+ËÒTqœv\²×+÷8¬ð¨­0ÎÌÙPÙ‚êú2Ç_¸ý%†œ\ìá-»dd=®˜°LQèëËSâMÙ—Æâ(•÷ÆPt¾í´®|jä$a.À&h9eÇ$'¥¤¨D c±;LÕ(º³°ÙO¡ñ ͘KhžŠØFuþ¾X†LVyÎR„ÛÂp²‡ À#ñ´V5RÉ&È(/Å2aÎ/ÑVÌ"vÌ,ðAÏ™Æ$Àå‘›Çê}ŽkO\3<’a|t˜žð9´£ÃY¤Ú6€H&k¿F«ÏZÅÒ`‡Ì‹ñ`u_ h¬'ÉóFrݲ ïô]ì`ljï“sŒc´ñ4 }ŸÄgZVuÛ8ÝJ:Ó£?<Îî€Î§u=ô«n­$"C\ÀØ­cª6¤NÍæ[»þ1°œAUo¥ ¨ ð¸k‡çá·þòWà•o7†;ï‚sw«¾dÂ|˜…H ^+M‰%éeæœ^çñóyž+²Ò“7 ѱÁüðà©´%›ÆTp† aÚ'û®¿>Ÿ}bä¸X¦&OJQæ{Æ:˵Qê\’qéÛQÚÍIê°ZBlyc…½¬V¯Ï2ÀwÐa—ûWöÙy„–›H~ÛÎã®.Œµ€=­&AY­ò«ÑA½‚Óõ¯@‹)5F4Ïe…'¹ÔOÇË«pGÆ™çc»Ã2¸ÁCŒN þ•Ú5ʾrCM©À÷ôu‡øÏ·¹V´k”÷¢týÖyþ7H#Y*[I±"%Œ«ÊÀIÙëKÿ ‚òMŸEwº3ĹJŸKÏþùPü©Óï¼rOB Ðõl°q©ƒoy/S² fåØ8Wx„÷_Ì[µÝ 0ª²;ع ~ãú¥æq’e”ñÐ˹k «RÅrîˆ?øÆ¿'¿ò¿ûšC^”µ9·gêÕ:j×ÍÒ+<ÒkÚ׿Â& “5Õ ¨)Y;º[ø~îÃÝówb¸øFw‡ïwk$Y÷ø’Õö’ê{KY¤²¢‰ÿ8oòûܘÒ5yž§dõíÉß’>É8I×ãpíQüú_ÿo8û­£{IY* ÙO“s®+úþþ)ç²óØuñý‰_Wî)ü«Þs-”3³Ž•M¯ße—ûUv¦ÇUÚ°º Ÿ¦tÕ†³°Í\A²_Û‚ªÅ˜êù=°ší‘T˜!~MVñ8‘ˆNm&Jöˆq<ŸþïÀç*}ñ¶Òð‹Õ¥h[Ø ÚoÕW¥J–ýR¡¥²-­ß$‰`^£iøU(ô,¼¥ãùbîíýË?Gê¡b%Ÿý}œ»ðó#)¾¿Ih£ÿÝ’[ñÝaŒ›‡<:šË¨ŸÙ^>‰²ÏíÁûtñ£áð1ø$¦êÜËìqLϨ•D'Ѭ îü ì5šá]Z¡ @½â:«%Ííõ5á{Cq„oã‘ßœÂZ\¼ïÔÝÇs”-ÕÂu+ó@÷ãú¶)”=âÛ×@ ÏTN’äfijk4}¿ÒÙ¦¶IÿÌþù 8`¸Ä|—Ù×.žì Ç.«•ù­«öñM5æ2 §Ó3°d„— ê"Wø³ÔïxÄØA;ÌÍvµÔKTðòåÇ®Mô_C_Ö€‘%”0c6euxi0ÞªÀ -¼Åg•GÖ€)äåÀOdà€‡×&I¶¦ZÇz ƒ² ˆÇn~¦n…¡¬‘ø¬}IÀhøMãœ% ]5à€²FÜ_R—H—'¢fÔlíøÊç8µ-©Ý€G±Ñ…€‡P”ú2Â\LpQñº²&—Η™a2OM²ùß(  UUÙ8Ô!šs¦ð΃¾SÛ#öš8­Iv x†ýþÀÖÂØÂpwȹù籄b‚«ˆVÚ\ xYGæÞ$ôC•i47a”'§á.Óqüˆ eXªƒdúnÏñòŸ|CÐÛº9¯M0kWd’B€‹0ÞÜ9£MV„’€9ì½×Ÿ@žAj¦c“!j­ŽÎ5€G\7Øe—ûMvÐãªHiÁ«>6 ‡©‘f#]n’4ÆTÛÖ²^ÅÌñQ©ì„ãG®”ÕnÊ-R“àµìßqVLø°€2SòíäbŠ¥'˜ŽM½–ܵײ^ä1ü°Æ’\»/y´ |Ìù=¨‡Ng àÑà5–‡ŒÛX]ÁYgAq´ŠÉȉ^}øõCðQ¹Nž¬|ଳ\BGWlÞ0úšÂ!NÈð`í cÙb{Pa¡44‹5<”Jc<“Wx®! ÀÖÙ~Œ˜éб |ÔîÌ5vêFàm2ž .æ2Š ¯„í¡&ƒÔö²šuÜb¼Øï\8•…Ÿ\‚žåFƈᎊ¨@×µ¦µenZ€ÍŒõü mK–G&ÔÙ3_µÐÙ/O.Ë]©Hǘõ›XëG˜Ó|r-¬S’/†}ß8cY0¼ãxõën;ÄïEgæ.»ÜcÙA+(¦¼ ur9Ëã$%¯¤K æ)“%k’mªçØž /Ì® ÍŸPí±Ø@rÊ®Fw.yý-%•Jð0Ic³m}fvužQýl-`¢ÚBì´À#Ž3çUò™éãß4‡€üÐ;êÙž§…8úð–çèœ*Ù½¡ßŒ3&íË‹~OÌp×Ä BÛL’#¸f_0$a,“vê ¬<×c¸ÄŠ*—Ù×.žì ÇUç »Ër©Z<+XÍȬ2޵.©G¿ë9•½±ÐìE¿nQiv¤ªE±µFAÒžKÞp+1Náán)'—‹½÷ó«TUFeX(Çeß„þ_PŠ%}8Ú’¾öÜÊ*ÒÛc:‰SxdŒÝj D3Ä ¿‰Cÿ\Œî£ûœ»(³WÖUïÁ—Õ`±œ3Á*°>¶2ˆrßļ« mF²úŽËf-ÏvG¼Îò½ÔöØÒ|#ÞvÕIàFf0™FÝãîù­e{(cS¿®a± æÚ)¤6ßQzŽs"!½'Ü׆PrˆÅþ¨6?2Æ4Ù_Âߘ]›Çff"qî0C ýq¡ÿÐN&ÄÅÕ¥{'{ÿ -X#èÃ:“c¤VIýDótqð ˜SYÐ\ËÕÑ3¶‡ÔƒkÚõí¨×¡ü~©NÌ]vY);èqÅdËPŽê\ÙŠgF!Mâuå¸À7¹d#2h‚²®¼Õæ*Q¼„©ò<³M¨ñ±¹X÷Ϧ|¶†²”DM˜­ÜC7Ú²WVó´­¥d'U$àA™8…禖¸m”î!¼_IU¢B;3â?³÷FE0¡@ý ã̉ڧëõ øBoð ú%Àc«P?&y`¥jÞå¼zš’¹…A9ñIØ—ÓrÕÉZñ²Cmc9VÍ’"Ë}†!ÇÂø ì7e­‘¡8šÔ•˜!^›±pjÃö6>JÀFü݇/`6þKchôä·Hˆ)0ªš #znKNŸRž¬ëàF¹•?­ÄòH$ qé}"7Žæmñß³”öLÉæ0Âüè{ÌÆ(çè!ý\”>&ë&aZ!1ÿÜg²¾[Ø!ŽÉ{N]î?ÙA+ o~òwÑwþü#ŸŽ/~äy›´yªRù¸_²I„ãgp@óäQYÈöÈJéHã›( ‰qO³¨Ï ꤴšbœ*‰Û>»Ëñž{¤Ž1“ˆôAŒÎ³GØ$à›?7—CªöU®3–rJ—÷ýMtý À ¸{÷ߣs‘í¡Î½…ï ;7°8¬\Bã¼·õä^‘½83/ì0õÙiÖ‰j9à÷º&Y+àÑá€ßüøûð›?†qß…ëÑ_bÈÉÅÞ²KFvÐã Èw½ð希ˆõJkM²£œ¤å]ól\’2š Á2 IC,¥ôBdß ÀƒÕXGt#àú9Æ[ñügÄ̰ß"”ñ .‘…œy_»>OÌ1ZsP;¼ð¨¹wò˜Š„“@¼Úu×q8|*..>ˆÑÝÊߣ.Ѳ šnhL :6þ…x( “x2à€0Œãðx@pkIªŠ(ìŽ\~­syäD¬-ê{Öv‘;^=”®Iˆ†s`å™,K…Wת_s-)±Ãq$Õ”l¤!X7c IDATDge0“6^3>oà€›0sTKé9lÚÔ KC^WÉ|½ì™jÏX»/ ûÊ"–Ù©ÅÐ]éÀéƒ>&+ŽHí´{g׊èÌÆCø¿ë¦‡kÏÃ?ÿë_÷·ßŽ€†µH#÷Ö”Üf~®d¶_·ã2uDT€èb>ò’½ü^ç’º—Ø—à¯|΋ñÊç¼Owñ·þÝ¿(Žy—].SvHl—*éº>üÛBª £5íçÚ”åéš”pŠ®ÿ’óëXì³x„>{öÿ¢D©§R¯fS¿5÷Kéoxtæqòÿª¤ €àsЪ𓾭<¬qz†F½‹ìÜ€¾g×ÿÞùíÿ1‡çÙ÷Èß?£öï&…sS9d]á↰“ ƒ¿Gþϰ!Üß韣ãqSòR7žctwÂ÷|y[Ï´"âÅ™µ³¦_~©’í!¨þá·ù¸Ò?¹Ï„ïI¥ñ„~Ù÷¤Ú‘µÇ5È¥±rÌ(%ÒwØd×äÚû”<+ùO=)óL[òƒ ™Âü./4ÌKdSÜŸ§¹}HÖœ8ûHL?¤÷¬%Ÿ€ìºëÆÛøŠ_øLܹóÁœÏì÷Ú¿ê¾3{ ­ äÄä÷]¶ÿ†½Ù·™Elý ëм×þ4mM¢"žGv’kâ.»Üg²3=vÉÊ’D¥É¢iROcü¡<ÎFÅÓ—´a#,!x1=¾± áèyͰ<äùÆgß§sƦªÊ4æ’pïXÅ=ôÃyN¤§+9n…¤1¼ð€¼§uáv‡¤-Js¥Ï$W¥ Ñ S«ŒzÆb U\È8ûãèâU¿Ôc?j„.De&½&å:Ö°‹´°«2 c¢•U ¯Ñhù+Má\û¯¼CI[â·pÚMu–skr#’P¡%†•ÁäÐÂ\X^Fï·Éöh qÑ~¹·uÁˬ“Q«Vˆöë׊­B‘ÖÑ5Ì•Ë6ÒÝhìu{iÅkκa{š>²åµs6VãIú¾i€iø‚€] #™XyÇp+i‰ðPM¥ãecÉF. –´ÜMË0| øýïQ“‹¶ö0ÝvþÊç>É5•›3Æœ¤¡v´t{G®-ÿ§|FÙ}(‚¬#z —à_âÍã‰ø8Üpù ß.Žì Ç.¦lY™%§p2ƒ¼R1M6ušÿ —™¿+Å=óM8gÈçÂ/4ªªô¹)wGÜô‹€\Wòy´¶hQzkîYQºžm®ð0=Ô*5¶`$ó‚9ÉÇÖ) Ê­?¶Ô-VÊG\\|øÄ¯áâ™Ì•@(Šªq-V‚Ò(tnŸ¸“^S)(sŒ9< ì?æ€h'/<’ç¹àaæ¯PæoÖh6†X؆‚e¨Óf€GOÁÐk5æ,Г$R!±öµm*ÀG݉v¥¤I;Zˆ»Ÿ`¿Ô~«ÜCvO’3~- o¤ò’Àuó2y—5c @– 'X+z~¡2±(¯‡µ(¹3&ÀzL{Ä ¸î ãxnœÁ£\­ËåôІÕÙc™$N/6‡G¢_ñ5;»GÕÌã–ý]IlÊz5÷„÷g憚ûsO€2ç‡&Ø؀ǖ¬ðZù¼ïz>ï»^…ãÓwð³Ÿÿ7/µï]ÙA«"^ë6ÊŒ¿dA3b6eÀG8—Ætz¯øBàc‹?°Æ‘a#hUõ8%•…P 5ÂÖ<¿øˆ×`‰VÕ&‹_ËúÈW¡è9à¡xºycBÁ—À‡FAG£ºì°¥tnªHS„*NãðîOK|Ø·’“ðUOoìȹ1Lôåî:î|ÁÿúÂÅñýl|š¬Uð£·N¼ÊÜÍy[é/ðÐÆ§z}+ÚHÚJX)ã,ž”<¨ÈЪ*Àƒ®0¿xÛ±=Ìö·øk í¼V©^ggFcKBÓ­emþ”’Ô”!žàæýM‚OëzjJƒø¶¼7[¼cZÖ½‘¡ºò^wôÝ ÀØ=&ΈóÐWÈÃDXê°Ä½K«fžÓ§¦ðÇpßG&©$ãÖÞ SdæU#XkøÑ"Úb%2 y‡jÙ…@rý9Àc’{z%;è±K»là*‰LPV•lnöbÄ*.øH ø˜ŒÉQ?R©cwXÀ0ŽäzÆÄÛ®SÃËȪDp ÀGnLj©Ú®ç'&Ö ¼Š"n4Wû]óFZµÝùAð0ï¡Ëg!÷m…5ó3£x&eesÇ©Þi¤Ò”çFøˆ±ûœ˜€ºÝ¾ŸÍA1ôEØ‚bàò’ãø ŽÇ'Ø ÐŸ½(U„7p*–n¶„ö‘+X x$Ïe[ÀCJÖP2ŽMr Y›ø}ýú\ãÝPrj<¨í‘ž_`´åîMµñÑ‹gç1p`ïÿ½VâšœHõ3šÙ`lœ"@4s­ßt¤>ØÚÂÖPݘÓö½dü´o,\ûj¦N`ýÛœò]™îQà΄}Ð<À|í„-êY”õÚ^  ®‘ƒ u1Aç¥:iKYâüºðj*6 Åry8%ïP!¬§&÷ÉÎÝe—ûDvÐã* £oïEۂºˆ…ÀXiÀ‡DPàˆà‡¬/¯ e‘†y…$J}kYÖsm ’¢Æj ƒÁúH$Ì Éòø}UPõÔb% eB•9&õ¬œ˜ÜLg.$ÀGx¶Ãüœ"X×QÀCôAÇ¥%þ l)”‚kŠP L埓 5£BHÙÉùÞÈÉéwìœ ÀÃ*K»%àA¥dTæ×—íB9âýPèß ²Irc¹O%ÆÔŠ=§r-TËavÜ뜞d™NH/ná^f€ E kŒ?6®»[°C bø@ç<ÌÅÒ=Öâ†q2øô=)² ÍwK°PQ$è{V‹Ì÷w0%«nÇÔÈÀHÁITšwŹYXׂ£KC9F³²¯©ç%óU pÇݼùcÔb æÁÉÎÝe—ûYvÐã ‹|,ay¬< y‰ÂM•`|Ìmk¬‰t³úò yáçéF¹zMá¤ÔûUí9óÍUÑcKŠR+ðÁìœXå­ eÆ<ðЬû'A£Š ºA¬ÃEÑ+D™³¤G̼”HÃ\( G˜Ii‚O €Ø’°;Xî;ö8 WRhý5y!Û¥×iKÿ÷žBó"¸²÷Ëcèi—xT°¼û^ismM\<Î ÉRÝ8!£µ~¨ûTA¹Î݇¦½ª×>)ûF8 [ ðA~®0² ’ú¥¶±¡³ÃCüÇZ†£ÕÖQ H|’qªCÉãäéÞ7ƒ^YO|1tÒ¾Þ,øP»›Æ*˜¨áÍ€ûÕà#îWD78û½Rû à{?Âïœé‰JÛ˜N,¼zXÍš‘ÍÙ´T*Ü‚"‰)q\eEc1k ¡IÞe—ûIvÐãŠH‚²ûïk’BÛ-R•«cA»Sヾ‘Vö™!#Ýd»ÆÆÚa ƒî[º”¬ò¡b(ºRœzÚ¯|Èqi”Éð`yÌÇkžÕ’òoþÖ)¥Œ%³&'FhŽæÅâ  QKcµ¼ˆž}$€¨°B^ ½Ä«!dYe-ñî(‡Ã@†»°n³Ša”ñØÍsÎ¥¨ñž© mˆñÖ.—xÐcøaW<(Íê’Ü–,ö’C[LYÈLlís`Cb<#ÞW«€/Ó默7 ?ÆeNÎ)¬'ä}jñ#½n9hw^gD%l/|˜ {Êö`I¢ 9ºî€/|þðÏÅ ñ¿Æ8žWk•hLðàk€YõLŠ>EÿМ|g÷ÎlM©ˆ.5½_d}£ÏIiÓÜ_’½ `U­—åæ ²×Õ^w]þºò^®V¶ñÏ5¬¡ ð!ÆÏ-ù1M9=.Ùszì’“ô¸ŠÒâU9àáÿ?uF÷j‘4P¶‘§VWŸÏ­l)–ñÏ6øÝç*àÃÿ&ÏÍä&ðJ—42TЦ#F[N¼Â¦Í{aCXŒg†aöY÷þ1¶ÇÌv ŠSDY¥¢ ¹ò»¦2"˜•>§*ɬ5ržLk‚7æFnM{ÖûéDÞ™ pkÓ°¿VoWnmÞáaB*ЪŒée7µñHà°Ÿy–²Œ6&鹚Q×=„®»Ým`<Ïx£3`Å©eMN”Üú^:Ïl ©‘Œ!h'Š$‰%Û@´§1?ºî:ð§ïÀçüËwº3çꘋ÷(»6Æ…™cÐæR¶Ê‰v¯è^Eú3Sâ< À‡, 4®Ê~9²·ÉSs 1¹¾)OîÛƒß ‹q-]\ ’eÀWÊ‘À~XMfrˆX‰çwÙå~“ô¸ª²WåÆt¾L–®ä;¡×u$)€‚ºJ5¦[kgÉF¿HòFtš U§üjb|º²¾$n_†ËÜZ»é¾ãà ò²å¹oèkÅt®#χ·°Šo~Ћ‹L¬žq!à!…/"Ô„€U%Àƒ¶—æÊ(ƒ‚ ªˆ ð!¿_%d {¤²ý<8×Ï{jyÍê÷¾\ÒÛÕúòFë™>¦ï ¡ZÉõ×ì?÷‰Ss—]ˆì Ç’Zjjm[›žg ü@ºà®Ž[ïÒ$t²/™è)w®–²>“´Ó¨(h›”Õ†¦üÑou‰#ÓBá ”ãÉç%ϵpÏìð%tAù[;gê×[ «Ç Ñ[Þ(³{å™1e²‹÷Ý—Q¦ŽdßÇ/ÛVVÒÆÕb6ÁÁT7(c‚.Ú°ÆL>Ç Ü€8¹¨^n9æ19¦œï¡ pjÏŠä<ŠÏtÎEр맡¸á@[?Î<ðw£ä‘•íí­ ‚O!ÓøÞýš¯ÂŸýý3œ=ñ.ÒgrCÈ$@€ g¸Ê0 >/î[¤’íQ¼×òù8Åðj>2yE²§…Ü_qnÅÊm|í%¼pîˆáâc3èqG´YÇî°Â§cÈúeî%Ýl}¦ïY®Mzo´pÑ\ÛÖ12üÔ@/˜zbȾÆüìb¨œ2»æ^ªÌ¥yÜ«d Àƒ†01à ¬ 7§”½‡†R*ç èÑoXB¹$Ã%öµËƒ';èqŤ5œâ2CPxˆƒ¤È]r^Cr¶ŽV ÑŒ ¾R̃.QASÆXTࡤ0Ô$Ü„aR |ZrSKYÍeþæ÷SaV˜ãœ$$6óJPS®rˆcÿIî–4|¥J™¥þsόч}³¬4­9Ç}áíÚLŸØ¦õ ê¼ÔUÞa-üþ¹úyœ¿ÌÇjÛò¼dn ù¥€!|ž§å°SEÜŸ'e-ð;å¤8 ë®£ëÎàþöÞ>Ø–ìªûí>÷¼wçæC#d}€ (DôHŠGb ” ¸  &¥`VR±òGŠ’ˆ±B0EU¬²Ä¥’G²¡Œ1°ƒ‰…@H)K}1Œ0’…, X4_šÑ›÷æ¾{n÷ÎÝ{÷Úk¯µ?ºûÜyÃíUuÞ»§O÷îÝÝ»÷^ë·~k-»Cg.£E tlHî#WDÆRÁÒæKæŸëM ƒ°v¨Ðd:Àîð¼}Úîatö8œÌ9îßž¥åÇZüpç; ‘ôƒT#¶ÃÆ”mð1Exˆ YwŪX¶ Y,tƒ‡»m?/-ÅìÔ„Às$_¶[’¸/È õ©@ÇN„`ÿB¶¡õe×wáïÀž—?V¹WÁ<¾{%ón^˜þ¨åZVÜ$Ô›c.h—rG­¡-«\Ų‚gP®š\N*XjUm˜#‚ÒèãvãÉ_ê<´xâÔ1ã PC0&ÆëI…¶,$à1þÆ3ÍË^qʬé7”x„H Iks€‡š¸LËé!í«ì‡º8v4Qß5æGØ0Q£‹I=ï=g¥¡?s„z¼f—i,8YCO˜úAÏj0£`~L…¸˜æÇßøã¸þ ÅÑåßB×]°ÆÚ¼ð†êãHMbÀÒKE"„A`:ôÌ«ºî"¬=Fg/ûD¦Îëyþü Ð< ÇG¿Ó·avÊ|Ûgê³Øç8Ͻ9pÉÿ&ŠÔ°¤þYÛõ`1cyÈ¡0Âú=À^=OXoôÛ ý£`©l¬×ÆÂTøZT ÞkbÓk­ß®­1%zÙUÑ£úJúH þ¥DR¢¤½T¤~δûÎO!ÞÞ— »Ãÿ¸Æï*«\]²‚«Œ¢M² Åj§Î9•å1[Ì ‰­¼2QH‘u¸àõË}gó®Sªt¥Â»S% ¿HÙ¥ gxHõÝ‹MÛ±û=œ3™ø J¿yd}¸J!Ò5ˆý4a%€Þ ܉L$q(÷L>ü.àˆB_Ì&?ެ¿$Gm¦‡/é¼|­¸{õô˜ýÌØ¤ÉÝXì¼ó_T•¬Lª¸:¶“ÀG¶!Ò_#eéÄN2Äem•ðÅ%õJÞVŠA1—¹5ž#ÿðçà×û,ønWÁ ÔçW–Ç*W±¬ ÇY‘¬Z&“&é”ÄqbûÎL]0:øÌÒ“ ÍáAo‘e¡¬ ʱx¿f §*ÏbÑdhZW´öç° ï©ÄòÞF Þ Ð*ÏŒ3‚úû@ƒOɋǑïg?(×áw!×m'@În¡àB¶Ÿ9YØ( P&WDôÏtk¡Oúè#¿ˆ¯ù—ÿwp†@4}rÑ‘0²MT#–Î_´RˆXái0&|b×>Ôš~.ëìÐí–›{&Ⱦ¼¡yà ôÆ|¬L4ûMáBsµØ®‹Ç•CŠžW–ÐsÜŸ'>¾\˜Áœbñ¸]J߇†x=/c}ºÇTÌ͵¦ ÙÒ˜,ùjY–n;æp „°ðÂ0¶[°va)À-NØÎØ 5 ‰b12¡ì°~ÆÂ®Y §ʷϪà5]tE~,Çh"÷Æò±@&Ò¤ÞT¼!騛ì8UGä*«,#+èq%]V¦€dw/˜È£ª,´êayŒy¿eô^îPG”}€–ÄŒé~t^¡«I”.ªƒb´4Ñ]tƒT±PJ€G=ÀA•z"<ŸÂòЄ*ä~—{3eÀ£F‚Òņ'Ã*axEÑyÑaÞÀåHpm×’=9cwð8pò»”óˆ½7¾iÿ^7Ø-@ô¾•c¹g¢&›SXòIb (h[RÔÙø‰ª'Pàcxÿ–b{ôy;†<ÝåøwxôŸ¾J:„óBt¤Ðž¸Ì6?Î]c|`íNðôé‰,³Ï»@©ÏUÒêßÝ¢ùm*–8 2]‡ïŸµ;t°Êz´)z¡CVÜ(ÜÃOª¯Ùç“2î%Næžfó(À‡ë+ÀêB ú¥ô‘­ËÕkäB†5syÅ*  ³´üA¾ŒpŸp\ ›‚kõl\~‰L8jöy@U°ip<•»øimå}Ô$sosàÇ\àC]—Í^\_çY†»/äåÛ8 W[Ç¥÷{ oYåj’ô8#"OŠå±ü% .~µ ‰¦lpj]¾-Ž^k Ê8uYa“øvúcÆ0…BÏv2¾2.Ù'-¬é‚Åz’&}óØsÁ <’Ê˪ï6Ï¥n J:/ßë’Ác'02‡qJ UÏ"hÐ5ÐÁ´;ÙçSÝR’FýN"ë#'r,pô¶µ~%ÃRBÝW8®D¢û‘jÏ e#9à㟇îÏöɵÑ…Ê?9gw óAÓ\èpE~BÉqM™Ximàe:œo›‹r_RJ}y¢ê<R<·¥ÂÕ¢{:¬@¼ŽðµExGÆj!²Ä×½÷½±°Efz†ùZâŸO% 56° ‹Ô—>²í.@³§IDG™h°=¢Ü_ úu®Æè´>åYccé^ †wâþ‹)³¦ï3. E#€e4"Ôß²y0’',`¯2…œG¤„e\Ñ-4£~IÃ¥º^qaS´ü¶ØwßÖfÔÓø¥WÍyüƒ«¬¢É zœÑb˜kbWµ**ä6~ÎoLÔF‚íÁ`‚üËÅ ªÍ›2VŠi¹+ìÊFö22[\>1ó£TD~Ÿá+Yƒ]Ê®6Äã#‘iÒ“dj³jÄ=+|¤©õÈ1*R®©'n˜.\û¸÷Åß›>ò·Ðž<÷¡À£SÎ „ ïlfKøXJ&·%¼çÕùhˆP.™®;„ ²H÷}`ÿ4‡CC;X[ öûçžåæ%jRÍâœ`PFÀ0ÝP)y¶´ï^ª ¾`—2E}¥¤QyºSÀÖ¢õU Û*>Ø1©£Eï»í`š-6_cѵ£ë.ÃÚ+á9m+^³šÇb a@÷”DÙ¹q(9#œNBC&ûPF0à"à d€GrСS]¿WYe¢¬ ÇYFÿÓ½´EžtK”,Õ5žÔlË#8?I¤%°=ì@_OÆ©wý³Þ[1w’oØÿÃÂ5ij&cpµEPŒÇ¤ì™Ø“9Wr‰÷r5ÞÓ†#cˆÞ¯™¢±=&C ŸA™0æ¦=Ž÷³8hV¢P«÷7~KÔí…°d2RÄïyj_MJ€/1”‹Ç{'¼Èýþ!…|‰çñ§qøÞ°Â~Z»Ãñù¶;‚ív½_›$žºLÁ`‡Ñp¡ì)×)2öèÉ,•€‡>_Nù=OÏï0˜æ¹0¬¿GèÚ‹Ù÷%ù» ã / ø˜"KÍ-"³i!VØxŽüçŽ8¼%x†5÷G Ï„üœD¶œ2DU·üNŠŸî›4 ;ø`} jãïh¢‰ °g<èJ0l6OÆùó/†iž„ã+w'÷£µCøø½H·[TÕ¥vœ×:d”ó‡/‡^ÿøæwþTücª_àQ9?t¶9Õ“n oY%!ëè8#b°ñŸ~Ãè]MòÃcÎý§½•¾]r¾Ôgì`h˜WI¯Bj‘q¹1&PG)º=YALÐvý½@ BÑJÿ†2m °¶õŸ±»ÃxMàá½E59;ö!T9íLJKZ%C%¿7Íh¾âõhšë`»¾FM²ÜœH÷½ešmçíìK4e^û¤Dš+Äù#q|n[8-̆ hÔîZ}^· IDAT»àw‹¶8l‹Ç.ÿø ïÿhÛG†±“0Œµù”³¼è]} ¹œÒs˜LoäÏ©ÿ)ë½6GçöÇ.ôÝs:W6ñþt×% \´ð÷;ä6tl†ã”–”wë.Lƒfs#Ìæ&|ðžW…jt´ØM½—‰Ë)|îôžkÏê4ª:ÝÔÚNN¾€›í<‚\?B?KC»¯6à{•U°2=΄h•;¸7«n"÷ iÛN T$뵜ª,ÇŠŠÈö  è¦0HRÞž­3Ø·ƒcϸ˜ï0Y`;xº†ìÿ™î« k Ÿ]²ÛÅÆ²ÂîòÏ*'¦Y¶Ò cEŒ÷‹ÅóÛÑ`é“6=S :ìú„¥Í_üº'ã/õI–ûd§©qâ½!÷ÃÑŽ£j5Ã~5^LjU&xúTƇ;_щÏmAöH9Ó1ÑËò ±¾Ç …•f‚<!E½/|ŒÎõs P«ÊE;v‡c0‘gÏ5á+Y†_1‘9Wjle–æ­Þ™ æ*8-cÔIŒðD[Û…÷;`”¼ÿ”ý…XÎõ„ñ¡öE;}À²ÓŸKTma»Ë89þ ^þ»“ûѵú5x,Ã=è§êØÍ3@4=9¥¿È•Áö/ðÙ]ùNþä6fúQøÞ®á,«<e=΂ÅßPåøTó)aÄÚ¾•s8¿h‚M>ÖŲ§ôw¥2JÔG€›‡RaJ£>É“ê àmû0Úî"`whÛ‹£Ó1K¤²õúXN€.îýxÎS@ü\´ƒíV˜ àÈkQ¬÷vñvvm­mzÐö”d­Þ²JJVÐã ˆ1<>UðÂóœdÛø]Zôˆ'Û4è«8¦©Møi¤Üý_ |”2<øáX AÆr®€0Šje^ nˆZ8€(ô8yC|>ˆ¼è!C±ò®gŸQà¡Qˆ—1°)[Ç—eãb¼&ÎÆ)W6Âò“@rÖ ^WÒ¾£™wör x ®ËÓ²DX 7l¨Ñ€wl¡AToqm3™•è/ºýÍÇ/-ë(\S.Ád­LºÎĽ¦‚Èe"IܨNž–&¡"`t<ÇÄg§xɼ+§xÌ×÷¯æ™Ç•äÙ±-¬Á˜Œ±VDFN¼~ª`WDKžSÚ¼À{X•ÓG”Fdyh¢¾ï3À7µïsÖ…Iù20CX?´ˆ COIXòv’IA¥¾ÖôMhÆpŽÀáP°þN*ƒœë»o\{T_™ç€õŒ>§£ä“ k`GÔ.ýº2>VyÉ zœIx¯œ0ü=nÏPÍ]ˆñÒ°µÊ­û"#ùÅa´x“ž|+*çX¯(QŸ‡¤—c y | € qIФ„çÌH"û#N•IuÒRÄ÷^á‰Bã^Ñ™ ¨FÀ‡<óMßžB?Ks¼°8â þÞ.€hЉ|Ì–4#hv¨‹o¨‰^’`àéÚ¯:%QÀ#¶?x%£ bOJ8#,a´ô¾ÿ"¸•0þû’h< €¾¤¥Ãð"FŽbÌäÂC(ÊBóP z?Ë=“ÚS™<ñ~c¥‹Š¶E qa6RB¢¤¦IFhø¬Uç‘x€3kSÕWÂJ,Nw”Á(ì…°>Ä>ÚJ–¶EãZ’EØ©ãmËîMÌ@‘Οzç¥9¬Ÿ§&²£4=K×SX·«¬òxÊ zœ ˜@¼x4F59Þ°¿¯ZÁciä…Õ§bHyfH$¡ù=bâ•L!u–€D|Fdxï©!”ÎÊO”--ŽÙÑãs‹(îB(Á’B ‹±*J­7µ+;2m†á#½÷Õt;XÓ?¯¾Äðp”ÙÁóä~¯5‹ª h9IÎñË|`¬H‚ñLç²þ Àå¯ÈVD¨”ýч±cÃJ @øsTeR)¨ ÐÁÑM`´ÄqôW÷Sg¾JÏ"Ëœ¨ó©u(·¶D¹WPθê¯i¡´¥Dbh»î+¼¬DJžµÒÒ‹ÄRÕsÛDÀ Ž×¥À@®Ÿ¨U]ü@„~UK¼Ï¥91Tð#:Hè“g8<ŽÀo3`œ%¬“œdCÕùXÕÚ©<ŠŸÿп ºS¬™qšçZå‰'+èq$ªœ$n(ߌ†õ*tHI,éL–GØ=•‹î]ªè¾A‰^Uèoõ!W% “èY$€ÇØ_÷Û|²87q •†¥€ ?àß2g˜8F^‚í¡Ù2Ù§~IÍÉSŒð(` ä%ÍÒyù’à§,¡U%¢ÎeSEè—TNLÌéú”3ãÜ3;"@#ó^L•¨JÐAÆ«ñÞL˜?SÎ5wˆÒŸjT ˆŽ9EÄs%ô“Ü»%ŽMxh¡.¹s”èÈW3{o•U d=΢H BQŒ%7 ÃÅÅ'Ò4[¿ÍÒ…Ÿæ^(Œ×¬•ê°–WC󮹄f²ÒÏ@ƒÒóÒj2È'¾ã¹=´\­Ê“UÜ%£'î÷â¢ÜS|p­–S…*Ò"òlDJ-i3Hl:©¼‚LäÉI%U14)öð3`Jg|Œ,#G÷¡ç ÙHšh}«¡‚ëàÇøwã—Ì3§JÉ%÷\>„àg< Ï—K:pàäÝš |¸v]Ÿ¤Ÿç€µºàY©sêbl'A²¡3ål5~Lòšés-¹Ç¹ûW9×ׂü{öøŠç¥å0[ª$u°Nq/Žqö^ùv欧Òó+t.È \zŸâ{-—e¦ °šÖhH•^’€ŽzÎ~[øH1ôÜ.5™*„²Co¶³P蓨Çìk]e•=É zœ5™³è(Š9„iÎc³¹Öîжô‰¥|€ |Hý[tâ$¨¦€‚c?€'«Œ¿Ì¿·Yƒ)9Ï_.GÊãQF­?qNá@W^ضC8 Â{.$+ë·w¡’¦þD¸A ŒFF ‚#•lëe<â“0 0|L¥Cka.@|ý¥q© §z ©½&ÖÂC\\hš¿ÿLIS+ØœRì=•ø^§€(Ó¼­LÍT™Ûì`a‰>90aâqàÃ]‹(ŒÅ&õ«øù«ÞâòñãïyÁú•¢ßKò¸†pLi¾Ws ùcÒ÷z©{r žI‘\h7ßéXd¬@?¦Ì9™P¨² É´ˆ%Ž’¸;I¾_©Ð[‰õ!ÁB›á<¶/ÆGš«0?òq@®kR1‚…D¼¤ûÎÆùZ½e•«IVÐã È»¾ðA4Æà[o|6¾õÆgÏkŒMz‘BdlnBó•ÿš/þì•O£k»ôHI!h QÕÉ^Z )Mœ$ £'bd;HT9W8Û#‘àµNd†‡tÄØY¯ Œ†Žëox"¡Èðýãâ:(6Q‚Ù¡=-YhÔw-žX{òö^y¢’Z-¦@æ0>Š…]¶êñ`¡üÙI^ ©RDíe <å»(U°a¿‡ß3÷¾äùÌõ€iÛ%‚!*ÑÉÙüãÚ7hÂïh Aà,øáÎ;ø¨awh£´+M+~µÀG”†§ôש³œø}¨G2øj\ÕRÌÚù@ǸÏ8g¨IDÃ2n+Üž>€é õØÀfüŸ>³ˆYX.5Ž©TvÿG‚íAú7›íÁÙ#• ê`;Mà£ÿƒ>£<àQžŸIòRaË¥"­)’•‘}éßããm÷89ÔVY%!+èqä¯ýÅ¿„ó›ÃôNL™ŽÀ_‘…y0¸ÑÓ]A×^Ä]¯Üá¥ÿüzß¼Z…D36³ ãBà#Ú_‹Ùåô~°kÓ¼dÿ`°Œçx0@¢mªÂĉ`;‘™@Ò1¦A³¹®Oíådû¡bÄŒR ´PÃIfÚ½%©Ðº9@ÆŒ`æ’äÑã‹iäƒc­Ÿ>í]à’ñúOxˆçó½ 0V;’(ôUZNÖÞù’¸èÛC=¦Âk«sôxÇ´ëñÀ †»ô}YcÛãµÅª|Æ5ìŽT»d¬çÙLùgàe"£¯Ä@w}ÉUwqǪùl¨p˜þ¤±< ž—ø>‹*w/‹s˜%EÆç’>yþÞ ½¡ïŽ–*ÅÂ`áËÅí³H_Ý}ç`ÙÆƒ¸†0â<ð‘¾D04ëÄ)<èÿUk•äÔJÝÛó3¥ÿÊKŸü¼ôÉÏÁ—w—ñwïþWUÇ®²Ê¾e=Ί(žC±Lb =€‚Ñ3îmû^øOÞŽ+fƒ®;‚ELœ9ãº\¹/TRsá üÚø±ä¸t‡¦)4¢×½¨nœë‹ó¾C…$%´Ù\‡'=íoâòÿÚör¿Q ¡Â”oEaG@x”&”i£Ì@œ* ¼HeøÏyž’ŠæÕN{1À ? `°¯FSÑÿ[…G>ØÅíLÁËV5*•äû6*Èbpê*x¸ûæ—èlà7!ðaÏ(«Ê•b}øSjìÒ¹±”Ý1}Àñº¶G‚)'‚iEÀ"c,…ö?ë7ÀgL3Xþ4)À£´E'؉¥F)Û/•À4n·Q¶óy:>¨àGØ—e˜9"“"qIÇÍIAôÀÒþH¹7h?'K&¼'è“| ¿9\>å}š ÜS$Òñ<Ëc*Ã#§“í~ÿy¢…û­röäêÒØVÙ¯ØvüðŸ(%MÒha»x;ÙÛ—èlO¾„öä¡Þ@ªP~ömœ;q}¼ìì'ü_À\Z{x% ˆ†|2b6ñgqû±=÷•ø—ý2ž:ŒƒàÑAô6ºç`[òÙ ŸvÌ)‘þ¾ómïÀqw€fP“E7¥ $îCjµÅ©øeA®qÇ ×S|Ôž‡žBb2TÏT–ú¦¹€ Ïø <úOÁÁ§þºöÑž1$+¥mgú-ÈL ív°¸Ûùd^šp¡:daAEßÃi4ð˜öÜ·ÕŽ÷Í…¹>ÄñB™nˆKJb§žÜÐα7xå”^j˜#Húë*÷øîÎ uá@pÎA…ß§eÆ^U¼»¤pš†=ûž%†¡+v0Æ}œf Øðày>dJÛºsE%n©°{\ x”älI†zi¿i†…D O†Î8Q<òQ(ÆðΘv|OÜ::T×CÜ3”Àâ6ú[ÊŸ4 (ÞsˆWq.”B#°´rWî=Oå]¡‰hi(:Š€Raì–¥¥¸d,ézrs›¦¢åîÏ«éA›1q<ê×v)Éqò}Zðˆ6—¼oSßI×_MŸ-Ó`„ÆWqÒ4 ¾÷{¿×_=ÞûÞ÷>ÞÝ9“²‚«ÄB•+ð#Á¼bÏp!àÁ÷™ |ï›b€(÷ƒËÜØæ 'Ę,¬(+:£û7Í´Ý _‹vs#ºî2°¯Š1@¨´»$à(ÛR¾øæŒ%ÄJs­W"u¯Ó PìE ÃW†¼ ü lƒqÀ‘)¦H’¥áÇ/Sð¥>$”«þ^´=;€»¿.ä©*ÿN¸¦)™ã³Î)„Ú{ª+æÁW-„ı¶x°1 g\qOó˜6(ý D¥x’Sº-d}Ðk«>|;å€G˜+* |LGª¤ŽçªÃP–Çø?¿ê¿78snÁÉ•ßÃÉÉŸ"ÈM`K#;)tgÃ)0:Øú—Ëe©‰p–þ÷ðÙ’ò‘b“ÅŒø÷Ä|.l§%ϧøòúvîZÀ£`\ó1T•ô|)VÇÜvT]¬Ží¡ë]:à‘tH"²œÏÕðùƒH •ÚÐÀéÔ™5¼µæCNÚ'@xËf³2c/YA3,)£Ù8ÊzïÊ"<§êGðHPéF+'”¥bº™1¹H‚E'ÜÓ!÷iO±µWprògþïžåÑ{é3,ò~¤h—Á9];Wð‡mÒñA™DvxCBºHNéþ%CtÔð¡zV•üî䯀üÞŠÆ‹ÈFˆó;ôínt€€ …e>t8c-YáÅ]7»sA`"sh)Q«$B$)Ûƒ–yΞ˃W¡ánÌ_ÞöµxÉêßÇò ýò!F{ Gàý­:  øHÂØ•zN­¯ô7),ØÀçÍ@–ž³®ÃçÁþ]ba0J%JÂФ.å™&¤ÊŠZ%Ìiš~WüXZ ×­œ.6»¤.€Tˆî$°bMÎ\¥€‡Û^ñ,ÿ<…´|àÀÅ‹qÛm·=Þ]Yeϲ_^â*W­ä&zÿ{&4%J€*}IÆÝQø˜t ’ª»Tä(H&^ÔŽIP8¹X»C×^ÆÉî>ìv÷âd÷lwèH%ެg©|º8yÅ/â“ýÿ¾IÆað“—ŽIJ¼'Q7èýžšÞ%“Ïõ %Ù@RÆ¥þÍ<c`LxI¼Ï©&¤°ÁxH%ô Ú`ï{TY‰Ž‡n?Tµ³;ÔêŒÝèÏÍæ™’ ãû6õH ¥2ÏñþFÇ…óL¿?K€Ë÷åÇgDg”qæÓë&è~ÉöS}-Õ ÁÖ¸`,Hùt¦Ì£C‡'Uò€‡ø» ¹yT÷AÃáºþV¡ï ïlÍ|ûDEî¼óNÜzë­¸å–[pçw&÷ýüç?׿þõxñ‹_ŒW¼âxÉK^‚Ûo¿ý”zºÊR²2=Πȕ3¸4cü"a|È Ö¥ ]„Ôç<…5 Jx‘ØØ¹ž%ÄHßAßm;F²9*û–Q8ܘ9øíÿ/üí‘Å  ú9*C>*ñaìnÌ.ˆc{eq^XYa¡åó<ë#ÉÀÒ=—a{Éqþ· `G î ´BÙݱ4Hÿª”¢UXc ÕÌKlßTùEUJæžL|{d¼?Ê›\–æuš¨0LLÚ·afÈX.µCÄDrÉN1‚Léò¶L¤9|AHЦûSÞø>½pE¿É¿ D(ãÇEÆ3 Ì`¸šs Œ …µG°ö8{Þi¢±B™R*;¹¯ÚFö‘(ýÅkÂ>SsÍvžLÓ’Fñ°Nms`d±¹-9çdæÐ,û1èү#\epHy[4†‘¡e¸sïƒ0§¦Ø%ë°ò[QXKÌ`4­Õʻ檪œ–”œëá‡Æí·ßŽûî»ùÈG²ûßu×]xå+_‰Ûn» ûØÇ°Ýnñá·Þz+>þñã]ïz×]_ådezœ1)<ÜöÖöŸšïöËÕŒ¼|ɌĤ;Oy)6pçiÌ!67`³ý 4› ¡¼Âû‘=¯Ö‰êë„>/t‰¦óD S <o}J"÷àòcÉ}ökȧä:ÂßSßÇw« gôôTòYw]ª¤ŽX@ø.Õ²<\ÛÆlaš-Ls÷LçåÆgêE^= ðÆyjþʰіøŒ ì¾cuˆjÂö¥aËF< ¡ðçXÛLñL$Æö ¬`üÖG-ÛC-«D!­‹©õ2¾¶ºg*·QhŒÓùS`{„Ì4þ(«ƒJà¡m“ºœýR:ž$¥z§2L<öÜ-$7ÜpÞò–·àío;^ûÚ×&÷}ä‘GpÛm·áààïxÇ;°Ýö,¼›o¾o~ó›ñîw¿?÷s?ç÷ÿ…_ø<éIOòŸç?ÿùû¼”U*eezœ!Izx@€»cã¸mù<“ëÆ—÷ ÎöÔÔœ;­h5› 88x¶Ï†1[} í¯“ÊØÉÚ¾MºÏƒ(”ÞóQ–ˆpd4è"{R‚d„©¬ç$o —:vÇr‹qy<ðøQöT(#ÛÃÇx#Ál™ T,¡³Åæà&³ˆ¶žA`­ —¢yÀ¶¹†*+â!VËg+áxU•£²ý©wMŽò”0:ã$—)E´ä=‚䙆WÑi»#I2ûvƒê@ì”':¥¹>zasIÀ^Üò9‡Þ“$ÓOó–"¾w VL„ö%™ü/e°8ÀèÜ8ó}ìì¥ÜŽðp‰|åP­@ܵ,œÑ¨+F©ùn2À‰²]¬H•˜×UaÆûR2&žÕÞ£”“dyî– JrœñOÕa"9JÆ÷˜³LSè€ÂZ7 ð`ãdvÎ43VrI2Ó´îät•ª¾H íLÀcrèvn‘÷޹Š0#•‡‡‡É}ßùÎwâž{îÁßøF\wÝuÁooxÃð¶·½ o}ë[ñC?ôCØn·¸í¶Ûð²—½Ìïã@’U®YAU²yÈü0(ÀNøÄ>ÑØöWN¶{–êXSô÷Ì¢ƒ¹ö›ñÉW=ÏûÕÏ¡k/ÂØÝºP¾å2Ý{£BR¥¾æ’¡9åFXük+0ôÇ,‘$¬ä<õ€GyYZ9ô+/Òq𡈖o"Ü©ÜX g ¢÷3{ÏþöÜWãܹoÌ9œº7̺#t¸3a©{TS•F4|m¼ü ò %O²ñû')Þêñ¥ž¶„¡¡´%=/i{15ß3G6ƒ‡wÞO³… «°8BŸ•äû‰Ø ÉQqéBàƒ†p¾O‚±ÌîGò’'"ãI;_ ø÷‰L˞˛bšÐlžŒO}ÇËñ¼ß|ÚöÁ€²ÇðåÅ ØÑK  V2Ïñ³é·Õ¥ã0ù^Cy¥€r¤‚N¦ª:Âïi¡ÕYz‘I] JiÌ~½©ï•!‚béÆ>úmºžP"³…ýŸùp-cBà2ݧÙaÃÏ9Wä^À#£ëE?ñõ޶ÁæIÓœÇÁò·ÑÞý?£k/³c”5Ë„ïîÕÞR#ï~÷»¯zÕ«¢ßžõ¬gáÙÏ~6î¾ûn¼ï}ïÃk_ûZ\wÝu8²ÊÕ#W7·ÊbRÖ"„„I$;òáRéMÝ•q†¨”t È aÑ¢ë.cwüpå¡„ÿô=¿Š®}xô~øÜ~JýÊ–VÎ-ÒïA•MI!-è#YøŠ×å©Ä»¨(‰àáîEt½N´1N«J”Ιq IDATßdÅJPŽ™ÔO@þ½ñ÷£ fsÆlñ[õxèoA³¹Æ˜sÃ8Ò ñÄܠħ¢BXÅRº‚2ÎéçÒG=>ÑýM¥·«"ßku~0 ¶hš hš 0&íùê%¸0à@Mlšt¸1œ ™ö Ú¢í—f£÷o’P/5g"9öÆ1ºkŸ‡;o¹ ]û,R€G:g¨F1–ü|/Î} èó) 3š’#÷.h¹€BÑþ0'Õ–n“çãö#`¶ÿ›†G„ºŒ2ó‡sˆ8oÑvü~å&C1 2ep=Uƒ“ZçÞwHºÀ~¤$™î’€GÒ\Ð/c68|ð30æ|Ù1ÑÜúÄ–û￟ûÜçÏ{ÞóÄ}¾é›¾ ð¿ñÅí^¹rÐuûw ®ÊÊô8b#O£ä@ž¦gU˜=ÉUx¬£ÒnS¤Æ@ªðBX{WpÜ]FÛ>Ò+«Ç”¶++ã®­d—ˆç.2fržIBs¦í£w6¦@+´ó}IfÎM1}œ± HÄÄÕBŸ‹Ö…vªˆÀ—N:(“¦¨¯¶…í.¡3-.ý!þ›?Æ®}p;¼PÀ*ƒÀõ?šó‡sùùÞ'²~æ{é:+ßߺã øÆßâwG×uBÉxÿI„x¡.b6!ãC`dß.Ù!ÓtA¶G•2öÆýÇ51zÞîH%ƒCÝàksží‘éY¥@X <&¼3Éu1¡7vÝ.~éjŸû~I€Ç >‘åSŸú”ÿû+¿ò+Å}žùÌgþàþ ¨Í÷¼ç=xûÛ߸ýöÛqîÜ9|Ï÷|ÏÌž®R*+è±J¹HHùT©¥K kê·¥$åí ”MîEjÑÚ/•+ÎCävˆ‘’Ñ(ÔVÁð¥UÀG,§äIz—S} ÇB g¾ÈýJ*‹ $.I*ƒzX‹Þ¯”Øn‡““/¢mîYÍáЧ² yC"÷ àDQ݇¤<ü•“QÏg*ÂWÜ)¢±áÆrN60æ˜æZX»C‡‡}{’dÁO?×ÄFRßîØ¿ Ü ³¤—þôp à¡Ì¡nÛt{Ð6më+áô¡Ç°h±;þý,¿—„0LZ:Î’HÎ 'ç{×çüÈKiÌ…m–H®JV=à?‚%ÀGZç‘pÞ"ÀÃ}÷``:,‡þÆŸ¿t\ 䄇m7Êš¹°”:W¤ýx$Áº¿”‹(¾6A/Þ±à^Eº–².“B/’bëäÀvuÿð söÞÒï`Oü÷öÑåª]=ôÐCþo-dåúë¯Üwß}Em¾îu¯Ãë^÷ºù[e’¬ Ç*ª”,¤K‹:é2…8ô@$áY)\¸2ê¼Æ.©"M6&-~) ‚Çcki3¬‘À;”£ÄÚNIð¥4áiÆÔAª<‡e#›HÙé%¾Þ”¡—ƒ$Å=T¦bšº˜(Íðáö­#“T^ÅðïûµC×]†±»Ðˆòž}0¶Ç\ÀƒîýÙ!c)È]àO®(ÆI%f›OˆÏÔ·™§òÞåÚ÷Řshž†“^„æ¡÷ÁžÜSlà ”c{@x&„a&±>Ä<@‘öGا2<>Ú¬(àõØF0bzÐarÕ=3°C^R’Ë!õîï W÷Þ*ëXfž~‰0_iÌ÷[4®Š2¶lTŠ=ÈáAA+ÂÚäïe HÉ\”Iò àýyð‘Yrï€Z©.Ú1¼‡beAˆYœåû’×MKÃì‚k+u¾)â®+—Ë#Õoõ7Ñvšá-÷¾ó½øÿ={iûòå1ɹsçÄ}ΟïC.]º´—>¬²¬¬ Ç*³¥Ús\+¬MºU*=\Njé’™¾pc¹$î5Zø%[3"ä˜ü„§+áA®“ªÀĵ±>)CTñ ÊÄi-žTÙ/5Œ¸Ï±qÕïÈÇ_ãƒ*íÚ=Ê(ö‘”† y¥±¿'½qÖØfÇüÔ(5™e²¿B•…ÆÑ¬p÷8c¨Ó1‡ð1¿—áØèz‹š°/z†=ßúâWâÁ§>†ç¾ç‹°ÝëG(EÏ‚œ/HÓõA9‡ à×V(2,†ý9<3-|„žr8>ìc;ˆBŽl`¨!´2’Ëå!I‰+zþéýæÏˆ€©:)Y+&‚Rxb‚°yPšÙ¼š>ò€‡TºTªÚÒoï‚¿ýx´]ôžV‹bO@(H–æäÆP„¡ûòõ'¼Ã1<„û*]›;ØaÀGœÄº©ÆÌó¡žPÌbŠX%Üyãú0“m‘`‘ÎaΕgü÷¯ÅÓßðþ{ûè>ùŸÿØ"m_sÍ5þïÝn'»]¾~áÂ…EιÊ~e=V…)q%1ì%åµ²çäçÓ<Ì‚ò™¤g¼ñþBÞ£Úö’ñû&¦²§bå£ã‡ï)¥O[4Çãû{IKP––tÓœ©Þìм¯3,ß4Í“²eW|¥dPˆ)ËÃvi£’±9Dð#ËøÐ(Á8â†íú±9”3u^â²2zqßSÀ–û½ÌÈ& ¸ 'Mõ]3¨jÇœÄRK S2“÷!odö†À¨ÈÓw¼ë¾Œgþîÿ‹gØKØu1æÜ m*F·¨`S°`4&\?tÖ½^á<¥N:o—zRÙüy„Åkkä9Ȭv˜Çði>:X&¸ÿ™çß¶Q>(—Ï£¨c–ŽENù{=Ó}SÛŽßÙ\8’|,"à‘ÚæX ÆýX (Áö(¹– RËþHáŽe Mt/Ô!2NíDì|ô¿ëLZ ¤u×–>€40[À:ó}¦×X;Và#Úg®ýŠÐYS\[y‚lÏÁlG0bÉs=ýéO÷?ú裸馛¢}}ôQÀÓžö´ÅλÊþd=ΤŒJF¤H瀂œ2é¤Ê£¼]7´mšÁPµh×ÐÜ•þ‰ýÐÂNíÆ±û±Ò/t)<¦€.õä\Ú‚9/³91ðk¤†jY+,"•xÈžC3ÄÏŤ»ý«Ã±CkJXCÜ.ñ¤)ÑИs÷& \}V*ÙHÊ–·)A¨°iн\¥÷oʸ#,µ%ÊÆR`d°c|€íŽprrwŸÏ£»¬´Ó?ël‚Z06œ~AIçåѧ”S­<‚þ×HÁþ´üû8§´0`àÇpÂú¼™GÉø|e,êl©R|ì_óì˜c$˘PÒkÈ…ÂÙ<Ì%’sQ¾/2;Å7#…fô $-qŽG©) Âð6¾¿"H ˆ¥žÏCJ@ gÉèÈ}ÿ&ZJ=¼†Q$³<¸,*>NòÜç>ÆXkqï½÷Š Ç½÷Þ xþóŸÚÝ[e‚<ñGå*Y‘ˆ†àETâOûû7p´<^4*ýê~£çpV&OÒö›D-e”ôƒ\?ÿ] g¾óäOôþê4aùÊ"ÀÃm>¦Ùú߯Ÿ¿xõ÷¶ZY&cLü¾„pÆ…ðï—J÷–‹3V&»,{oÝo¢òl»p?n-+݅φ´_ª¼óü´/ҶИë2}+dƒw±ÈPÞÑ%¥°í¨BÇ”SEÏ 7¦]E)÷éºË躋hۇжõìñþŀǜJN¼D¸1[³×fKÖ.÷Q”ì)€‡¿¾FŸOÕ¾'@¾€±ÑŽïÐÀಞÁABÄè³ ÂYÆczX`Zˆÿ®µÉµÂï(]ßÔ¹¸f¨<ÜO™µp6KKéc²Œ6Øœ)>§ú~dó™Ñö‰>QZ‚¸VIJ÷5ó.¬ÖvÁ'Ø­0EÔ|阙’Ó.ԩù)õ÷ez3„q]xD¿)ŽÇ`›¢‡—Š<ð¹ùχiyÓM7á/xàÓŸþ´¸«ðòíßþí§Ö¯U¦ËŸ‘¹J‘ÄàÇ8Y÷¿§ßðw¦H¦€:‰3Cviâ(AjQ íÉ}”¼k 9ù]@bà#1¨²^ Msˆ¦9Ääi Ê°¬ 3ÚØA›§F9“)PÄm.Pæbã0¤5Ãlˆ1—i‹Q*ø[ß'ÜÎ( €) ŽœÄ}î@\G„÷ z÷$@R¿ »ÔœªÎ+ÁsÁ…€A£îÙúÏpÀG{1 §ðÇD9Dd€-èƒ3ïþçŸfë?n~r@Èø®Ià#qýQc3Ž–®¥fí"ïLà1Wà¹`Çø.D%Péé2*JÖ±½UCš(%´îÈ®‹Õïxùþå¹fRF,[Ÿ*$ r@xo¤ö39HÔssð£TØ1Ji˜Å<â¬Ã”0àCràÉà‡þqûKz3aé\4/§KBG›àxŒØ×ºPg›Sÿ,)?øƒ?øà?ýöÅ/~ŸýìgqÓM7áÕ¯~õ¢ç]e?²‚gPJÁyM Ôš¡ )ïË^¼¬)©4´£EBaÅDûÒc¼Ä¥ÅWSbz°¶4¥‡Mó$lž†Ãk^Žƒƒgô¥GÍ€À6ÎÏŒ–„ÄÊ‹.%Ï:ºæÉ=OM6a™¸b¶Gä• Ÿà-ðl×—%ð PÔŒg1¦š <<’­_ ­—ú<ÜÀ×É` ¶‰Ì'ÁÛÅ@Ĭ7¼RñŽ˜t¤ÿÚ¸žÃ¢…_ƒ;Ùð:Á'ŒyÀ˜FYî{p ÷Álašóý|ÔlÇwqð];{¯†qñü¢1'ˆD Fðà øÁ5Á;áŽ)5HSÌ8[2ü*~‡ãOþ[ÒÂÊ\­œC£ `×Ü"‘€Xõ€¿Ãþx#‚r‡ßw²Œ‹ˆÁůOhO;¸Ìä4@/z‡9[¢üÈ}$6t1Ø¡è—Õ’ÒO…vÅ5*²è¸z|ÌÊûáýøwßýVüá÷ÿTÕq=ö ëäññÆ7¾O}êSqÇwÕ\àgögÑu~ôG4HzºÊÕ++èqVD@{cÅ#T»ã\›ÒB­êä¹ gR)¶=² ’§.P>¶ ‚+už Ls-Î~+~éûZ|Õøì¼¾þJ&ÎQ˜PgÈiaÎ8)‹7Õ–Ó¬P@€~À®™G=Ø4m ¦Æ:Ï£¡VàßS‰õ4!†i©DÀ‡`¼iìžÓAbydÁiŽ2ÛøýJ^D¹>À˜ë}=¦ƒ`4´ÓŒ ì yIDà‰yæÍÈðhšC˜æ<sè™þ°IÀ?wh(Oò¦€øâWr=Œá.(•ï·ßV(QÈMð£v©R¢#Ä:•ÌžLÊáÁ(,PŸoʈ+SìúLüwÒpõ×Ã?4Ô‹‚lN“R%Ï"wâ[{‚ ÓŸ#ªðCÕ`Gª{¥àØ @ˆ:/º¾æÞ“SÖ«ŸúC·à¹¿ö·ð ¿ü¿óàƒâCúàÃþ0NNN¢}nºé&üüÏÿ<.^¼ˆ7½éM¾ZËÇ?þqüÌÏü ¾ë»¾ ?öcËT‹Yeÿ²&2=’ðÆ’¬ÔT¸1$%)U‘”(Hrv–”¬—\~ M¯dU³OÀ JáûáÆs럓úŽ«€GùûF\ó1 ­/ª.]W/t.‘«æés˜^îšï8ô®±¤x‘,dD“µPtޤÞêS“’dKk?ÿ’’:_JVú!‡b߃ûkØ…CNRRr®¶mñ²—½ ŸùÌgpéÒ%Àûßÿ~<ó™ÏÄÍ7ߌ_ù•_ ö¿õÖ[ñ‘|?ýÓ?—¿üå¸pá.]º„ŸüÉŸÄ›Þô&l6ûÑVY^VÐ㬠_(„ïŸ< '숚n•1ø"$,JUžF©dYcI_`úûâ~/1€è¾ð[»ëc÷»#´'÷ÃÚ+èÚËà!‚ô»¤\-¤MŸ7)5†¬ð}5—~“3ð<îàžq¥¡E¾šË`øÐýÄ 3Ú³Š’o¬Û·„uÅB±äfȉJCu™ŒÁ£AÿjbÀ§Hfn +ò( Ÿ³7psÝ&&å¡ .tÂÄF­.”dÔÒpûN½ÑC)H`\¢ÊR͸_bþѪŠÛf(äÀÑÞK|Œ•\²@(}–¥ù*ïQ®âFr aá~›xˆ}×ËøÈH àV"ã@­ °À!2Ÿ(#¯ùe!¼òQüÞì§šT¹HQBQX@‚daì‰×¿$ð9ö|>ñÜØ‘Yku‰«U6› >ñ‰OTó‚¼wÜqÇžz´ÊiÉ zœ!))íU2a¥‘qŸ‚‰€ÔÃ'%Ó£¹ïÓí¥c÷õ5òß(YH¸b_v¨dïaœ¹ß5]Òœ’R¹}ûòóåÏÚ•z•yÀÀ‰®ë¶ ,÷?^'ŠæÁ•D+EÌ÷‡¨ ËAA'(¬ v”0ܲcÀ‡Ó¥ÍÏÄÁÁWá±Ë„ív°Ø¥ÙÀ r®¡!@``ýIuwF"©aËäšÊ°¨²ÏÆlô5 7' lœêcÙoE€GòF°MzÏä5¢Üè !À°ËÀ-¿–>ôa ÷‚Ï÷âr¾°~rGˆáÏœ%9€°?vÏ@/uœ-Ï ˜”dUm‹=ß‚ýà Ë^_Éz›ewTëNú;×·{zìŽUV)•ô8#R2UMR à1ì0æ<3 ›Èøó´_@4ªrçMî/ô=¨}Ô)9ååy/²G3nƒžWVp¢ìÿ>Öœy}K¤h?¦ Ò>¥À IÀQZ/Ë ! õQ«ÄûÏ)îÎpïíð&Æ18»‚¥´²=ô¶töUº}²Ig<»\R5Ú˘,i¯,ñºAØËØÀh,y£)¸Ž‚m[=0h Ò¡6…"N@üH*4\"b}Èï<÷òÛáÇ_ ÙF`øHùZè3µl74æ<¶Û¯Ã?ÿ›ÏÂ_þ©c\9º ]w¶kaìTj<ébåZ±´…ð ,¥€Äq‡xŠ}Á÷ÁÇIîü@à‘’qý)òRÓÓ®ÑAh a{Ï,¥ñ¹Èå¶1‡(¸í§ÒþŸátH@úçâßhþP@šS)Ä%F¥1¶Ffa*4±&€R^˜²Ó¬‹I÷}fx=?Ý–Ö)…¦”°–ðXåj•ô8"Õ/–Œç70ä%$™OÎðh. i. 1¼òdítÝ¥agª´ ”Ç £9Ï«-hø1QäóOXxUO o‹%€¿wÌx) AÈQ•³BY©kدXÛõbCÞ …ú-ËO0ùm ÕrwØ] ôÓL6ªkr¡ô$Ø)q¿;E0Í@ÕLºáÞïC±Ž•5ëò@ 4º ndÀQÊÏùïÐÙËã«3ýߺ×xêØ.~/ rËX¶-bw(É+{£µ‰€¢°I‚À9Z ëè& \u)èº/ãûß~ Z` óÇ·㨄ò/KÑøœÂIˆÊ6d÷0ð.„Ô¤;)_Cd¦æz¢êî¾hla{2V·–íQ!q>„þÛç# Â÷£„õRK§ût€Sn;;ì– J°=ªDò(Šg{m€AÙ†ÙŽl bÔŒ¿I9^å\ó^E€GscúϦy2ऽíPYàó†MJáËE  ù^dÃK4°ÃµËs÷a"Xi®áýàœMÈ à}§÷ƒü>f#¨:ÓâøÊ§€+Ÿê7uŽ!x,GQN’Sé»°0ÈQ-dMZø®‡ž øÊ¨JÇ/z2:F ððMÔ:x²¢º®Å TÃN‘¹SÔ­¼A_áúuÊ­YÃ~!ð± Ú €"Ï…£1S|ÿ¤q¦„¡ˆŒ¼üé÷¶Pi §ÞmyçŠë¯y‡çÒ¶ðHÏIèdey¬òÄ‘ô8 Â<'þÏÍ7˜Áòo½§ …µÇã®Cko4* ˆ†„f4óó%”qó^•*tÉ{ªRI† »”OâÁñ=ó ŒgKÏ]î‰ ½7”"yšÇbŸ+v|X‹ÞÀï[bdB[„>×õk‡®L{-=“ò±WZÉ¥8ÄE¡MŒ¥!ÃýÈÿ¦ÿßX§˜wíÛõƒ¶›”EX1xÂä¿Ò1¤ÿfÓ÷×¢i®ÇßûZ|˯ßÍ¥Âv—ÑÙÖ4´%Åc>øÈj‰œ6RÒË ,Å<ôÍŒÇHŠ«šàØŒ!Yø¡ ?¯$mkv±ç3ÃHËUµPçQ-‡†ro'õB˜à̆o…FôiRâÇ’œ# GLšì’±BÑ&äÂÃf³€G̈e£Œž“'Myÿ…kÍ­Ùý=w ì5°Ý%tv7Ì©ñ¸–rÓ¸0ÃØ94”™"%ž|LA tŽI`=Nì‚ãØ{ÇßMa<ÇGc‘;ä°Ö仹Ô|¤ô½@ôM,$.'üÂûðà/¾¶Ûm•?²‚gER¬íâÌ×&Ð’ä|èŽÐ°öhP¨*§_`¥*Ûƒ9@ƒn.ô$kŠ^Q[¢wGêOÈF(¡ ‡É —GØ'•+”FKKMæY+Ið£R¸„¹x/ÚØOñï¹y6hœ»¾Tm.n¶41dꜵlNk ŸrÐiÓ:”íQò¾”P‡ƒ0çé÷é¯É€&búhñ-¿öYt»{|¸‡nëÚäytT‘ ßh\fµ $|åû%ÏޢLj†«æøôA‡Üc[XÓß_«Ö¾º”íPËò“<Êe&µ|%E€SdçÛd2Ù%5¢™W>Ø/ˆäXuÛøЯ­|1²0Æ‚ñ_tà‡j ¥X´Í=mþ¾QP”WLv-9¿Ù 1‡88x6yέ¸þyp u€ƨĮ æ› c{P€&d¦ÀÕLƇ“Ù9ª†k7çt ðÐÖѤ°$ð ßVϪI`­õ (×r ìÀCîøˆå´JÖ>导Où+¯FûècøôKÿ»½Ÿo•'¦¬ ÇÛ¿ð[hŒÁKoüz¼ô¦oôÛ«˜4>5;%Mjׇ¬ôFŸu‰¥÷ûjI0‡óÀÇ)K­w:Ue% æÀ‡ ¹šó¥l 4,|;îO8¯æØQ&åö˜¢òa.êqi–ÇTÀƒv‰‡·”í‘ýÍŸS`†™8gÀ¤t­ÙÂ0vçA@«aAM)ášD·v‡—Ð}À.kMCL=1jÂ1áî‰0¾ƒð©0ô(!4Åÿ”0ÀUf<Üf%tÍÃßmíÙùý;M5t"–Çx @¢ÃfJß§‰ÞaÛ 'Âû:©JK-0ìÎ?UþïHÆ1U:_.’öí„ï¾È>üØ–BoJ˜}c²!_xL5Œ]æç0r!)r¡pq'ZÀ¶hšë€kž‹çã»ï~L÷Œ_Ü{<áùFý/ |¸¾.%©¶DÆapϼêþÂo`4ØÀ4[¢KHú]õ×x'O €ÔêóßéPGï…5Ú=êä}˜Î÷‘?‰>ôIœ¬Œ‹U®BYA3 oüê×àðà<€ò+Hà±I”}êë=¿½j0V¶ÈKH% žf‹í,(¹E%Ž•Ô ¾Xê¹+¨ñ=JÆXœ\L‰é””Z[u¿¥g\à}¥÷ɱ=÷šòIÏk¥'·æ´©Ä§üž–‡3~\Sõþ™ïkPŠ7çžgAM•Œ·—1}ú<¬XŒåœå6àùkO IDATeo.¯QŠïzÇõhÛ/í7ažŽYëg[Žó­zEÙ;«–9FrT&É\ÀÓþîD{_ 688÷ ^x5{ôÿµW„£Gö †F Ã&'Òû’ qÌHÄÂVBÄÊ?—é|/Ê ñò§¼_Äßþì?¬êó*«ì[VÐã,ÈãׂÔžò†/&¶_¦„pï„hÄÆG ßÂ@‡,Ä{&L©ä›)cJf‹TJ†íQ’\LSH¨B” ™™dÔW?7íÁ¾ûþL ï)¡¿K‡I×-)SÚ0i– @„ñÝÚ¦ƒiºžÕ¥Ä4§h¶jõ¦¢kÓ=R’¡ä˜?ÖySínü›¶Ãú$³>ØoüYæ¨Ë)ÀƒßÇ’P8–Ç´vOKÇNOÒ€‡°F–ô/£ó9±ÖÀ&íeå4ϵÊOVÐãŒH‡XÀô sG€yz£Êé{g" \©¢.å9›è! EœÒ†Ü†Dœ’Ø4k@ç”MÉdÞóâöXßä¸2[É¡"E¾]úÈãn !%L ~ç€Øœkœ#Žåˆ,UáñÇœœ;x&:{ »ã?B×)×’{?ÒÀGö:ÜŸ™Ê¨Ì¶ÆC%ð„ãWóæ¥˜ÎhOÜ ÄÓ¤t>ç×vÈKWqâÇöÇ‹Œ‘è¸éï@q²ÕÄoS’4Šs >¢Ð–aÍ ™i㸠¹Gø¼Q]ºº¤ï> ÏçÄ5ÎQû<¤uHM<é÷e ŽÁLÈgì(Ðæ…üæÁÖî",.™äT˜sz¥2GdÞm9lIÎ6 øXÈÀOXñÎ2¸»c”ŒÃ®}/úù¿‡c—d–°ÚŒ9Žß… $É‘2æUqÀ°d®0Q裵Œ×Ä»¶hØÓ*«,,+èq$<ʘÜ;(_¢B›O&8¶+ÿ¶ˆK*†@ZÙä¹N&Ë\¯JÔU\†¿9ÛÃ7×”¦g<Ò0©ܤ<aÞgœðk7¨PPR KûHcâ¹±Lbå—.•©%~ôç@Y£g‹>¯ž—˜ØlÐ4×¢¹î|â5×ã…ÿôïõ ‡“jP’ …t\˜SûOû†Ã!|z( 9ÝTÀ#bÏlb CênÕ;ÍÙS3ØWú±•!,|†ç©a/æ’4æ·ŠÀ‡ØžcŒÀGÿC<&´÷_ÀÏŽYtýŒòKéàg/‚¡¦²=8Ø!³»\N¢‘Ù‚á=Æ»]ø(ÑÁ'Y&¿eŸdP“ü,$@Z#‹…¸ðg¯9T‚â÷+Î]ºŠ„Ρ9¸Éƒú¦9„íŽÐu_ì…]µ© KbïÅÔ*‚QuŸ¹€‡xÂð]\U®VYA³ "à¡MîÙ§-)׊^¬,—09É>ŒÀ±qåÁ•ÐòøìîšUŠÜýª ?…l’ûU¢À”‚ªÑP:†`ë¨÷Åy]î;>h¦û%™ÑóЀ9瘫ÜLxf02ì¥á›ùËØ¡r|H1ѺY¢aóßó}©Pò¼G]¹ï)ãG|^uP ðw,Üæ~)cKžgJÁ(MRÇ—½kÏchÑrzðC8UŠÕ%¤AßþÞmž†íöëpåè~žv NûãÆé|¾i¢5¡ËS$x¯è|С1ç±Ù<ÇO þìkÏáY¿}‡p\%šÆß\˜$ª¤fÐF¢,|”fÃ÷ç÷ñǃ¦å¼¨I|W)Úý¼ÁSi)Ï(QEÞX øÈ°=²ôÝ1CŒg|' MòpŒûuÝ—Ñu_–ÇŽâÇ Bc•3Å2sŽÒ* æ¨Ð¸É%8N-Uð¡ýáT{²m«BÿÜF™âí–d}8I=í~Ì{ß$à Ê~§ýµ± í¼Êh# €ø ŒøEe£¥Ãz¼n #±> ™&ùý€‡Bëࡱ:(Ø!²Ú6,LJ3TÁˆvãsŒXŠúzÁÃôâµ»7¨AY5®¼à×4i¬ýïã¹ó/®ùFlvwã¤}p  uIé 5À‡ÂÌ? FGôC£ï“rJ%Â/ûd×;Xû(Îß÷ÛøšûÏa×>ˆÎÁ‡·0°É /ûì›`Ð úGfmWòzõíÄ÷4_¬ I¯ßþ9ñ{ÀØ||NXe•'€¬ Çá5BviÑ{!+ñ’ZpH|0IjJ¡¬Xð>ÒŒãE¥òÔN—y}‰™é]-ÈôƒÓ¼k¨õ’r“Y\£P™šÅ˜/Þ”²›ð´åúôYÉ0J UÔ+dÊþ=`ºEcÎ+Ài¬Dòä‘À ü«×¥³=ܱºHteÙË›“r ²|2(9ž$6bs KÓ€GS¢ð Ûà‡þ\rì’uD>gîw |ôÛðc2Ù`c NýæTe™=ˆjÀÓy236DÑÀßÒP76Gaj¦Av´Æq ?I†yÕå3‘€ÞnðWún¼öU®]~ßL¨”X*ž„x kÃ\€²g#´[g¤ÑN”°;d0®Nb´µ ¥–PŠéï’h¹8’21±i±ˆññ…‡ÅAyã÷È8e{ð’šÞ¨·öÖ žÌRiâñÇò2¤D + L75ùp”c%áÁ”ú Û°BŽB°ߥªÜS çBϾú2_J¯3]þ[–Úp¶ä»BŒ \< Ãô†mÁÜ¢ž§pY<ÓAm=$°d͘’hãI<Èô3À\·<“ÃíÓG1>$ ²)JÆ‘^ÍÜA¢´£Íu6œCœ‘šî[×Ïӆܫ âHÞÀ~¤X¹kI£Pa},ƈê€!ìɱ>û°jvJ1l’êTR®¨Ð™L?ÆŽ°q9„(¹køª÷ÿ]\é."LÒ-¯%á>ŒÅf‡Áš[c•«OVÐãLHèé©êƘ-þÝ-߆ç}økÐ>òOîH>"*g>¥ÊnÂIHKÛo€Äúpç”c¾@Ì¥8Wä XTñ/VR§±;‚mÀ$¥¡šá¡u³Øë¯±J$dØDÀÀƒ¥f,ºg¦°=¢}Û£â¾ñvkÛ^¶»ÈÃ5z|:XìÐWrq÷o7Û¢ì9±9 NÚÍ/;ß—v˜DWŸ³.ìà‘z‡ª}„BÐ6ãWÏÏ2¼[6|ÿøØX"¯*ÊÚSšÄT/×kcõ´mjjm;¾Âû¬K…çÜÛÙ9öJ$²<Äó²ß½GÊèòzx ç{ìxwžü½Õu•âÒóÒ>ln³ ¤êÖgÊÆxÐ5ˆ}ûã6ˆÔ¼' à!±;RLž¦¹€¦¹€®»Œ®»Lú1Ýyà’Ø?”&ÌÃB¤_ýß–<{×6Õo‡¢~DŽ÷{ÁûfímûÛª¯%êx€ÒÊT«¬r5È zœ1>)ÙÏû7ÿè¾ cÓö»éÂX\û6Ü5ÉM+Ä-ä U·dòU54ÔÜ·U>9ëÉ/Ò^yüdQ½Z '¤• @Q^|_4ÏdÎ3]Ò÷ÀCÛg*ØuT†é”ˆ˜ ƒxƒSÆO&édÙLƒ$¾vk¯Àvq2½~Ÿ‘úëØ@¯d…Ÿ’Ñ,åò‰—À#*3Zp}%É £ë^(ÄKÂòHI¦QðHŒ½|Ŧ sV§Q= [XÔ¼QtŸLn„¬qæ7ȘgMsèsâ4hškmûº¶Ë>—œHÏ-S1¦'4µÄ{ò]ç ÈÆœ@ nmB•–‚¥s™oÐl¢N|NƬèEc~ɺ ÏŸ•îÍ€mÙw8zöµï_ZÅŒ‡·Dú yI^¹Ž»Ce| kÈÁÁÓ°9xÚö>ìŽÿÃÈ0œÉþá¼ë9CHTx†ˆ®ÃÑsˆÉnSù>Üï´MiN°-‚2ÞæVòêû^opRx =ß*«h²‚g@ÂrnÔ{`‹ÝåDûG¬ È U˜É\:û/W$C¦É˜`²_$âp›éçN‹re†œ)-òJarJ0[„}$Ie O*Ò•ÊChÜ„}šR…Cö§Ž‹÷“Á÷z.è·3F9@(ÿ;C¹’’‘J¥¤å‚øï‘²X2{Ús5o«Du Â6Üg‡R–< 6¡Ç’ö›{Sï‹xð±GYò\³l¹JäžR£Ú²ë߇ð±6enȆê)€‡?wfmÉI ägžá±EÓ<Éïþúïƒ=þc{-ãLñ¾—*Óˆ‹Æ²0·¹ÄËÆ<©wæ8€17…ÿ͉]=b0êU;F„ÎS!ÃbÔO¸ØJ§‘Z5#rƒ11«Ê°öBàŸ­{ ã£ßŸ1I‡mÅአà¡åóà÷­¿Ö~ÝÃ… ÂB‚ ”tˆFfëÑ{G}Ÿ¿=žßãs$«üäX®ëü¾E!*îÜóðÄ¥ å*«<>²‚gAˆçÔ‰7F̨P[{$z¶Ô’{`Þº¨G“º²À…ZR$5@D‹(ÙE£Z¤ó‡@C‰GщX~oïz”²¥O%XcLöš”€RyÃ<~ÈçÓŒE7œGðƒ£1VRž—=y²£óÐM à‘ÎA#„!¤¼EÁF¢p3e9`x(}X q@Cä/YÆÉ^Ï8Ç$)üÐQV•Acù1 ñ¸^ð( ‹šÅ ©ÌÙPÆø(yËd‚Û©ë<¼ñ±E³¹}ýá[~öÿÂñÅ…¾ìå•úS$t"…+.=¥ÞUiN ´TRJš‹¬ß—9o„Жè»i`šk3„§ta?6Àô }H_ÿî@‰åþû¸ùEÖ¸¾2œt^LHiˆ;9¯ÿI ­úPÅŠu•5šÊ–Éß주ëíÉý躋°ÝQr y.’cÇýNî?¼8²¤òìQßsyå”ù7|ÆËˆÆòXe•«MVÐ㌈¦àõ´¿Ýø·F?Ô_JE•Ñ®è³;]ˆ¡ÂË\Bå;Ü®Ëãx” _8Eæ‚ÿ± X~‹  Óº¶ %¢};°‹®¹DTÖGª’ëK±=ø¾…1×BåØäÙÛÃŒþý'HEð”lÞl D‡†Ô8Ÿl€„±/$ÌÐÊE»kC[Ф~|ü·’Øð”;KRs€{ÏeÃ@j'˜?4À#÷ަB=y_%º;‚í.ã/ý‹ð˜mqròEjàR H…Ãi¡oK)™´;O$í Ÿ2à‘ïÓÆÿmmG¼ýÃö $Ö'̼4«iƒÕ!DÕ]àa8àဖ„=•žJT5Ü '®ËÚ<Å%f‚HÛû œ(Œõ1ö½ÍŽ™’–”îeí¶uI³cæ˜ò¬äÙÊ—ån0!à–.e@oJ¤~óÜ{©v'…iŒ¡¦ÿœ–œæ¹VyÂÉ zœI* |²S&¸cˆA,éÔd’ÑÆ6VÖܹžjxOŒqWÏ#ŒÉhüÄA—Ÿy´Hürßþ¨äù|ø˜ðþ;%¾o\ðÆó~IÔj·‹xvJhÅÅëGTlÉ¡±;&]ÿ”¤{Kµ™\1â!E¥GëÄçÑoÆ{n:Ø®•J‡õ‘9„‚óàáþö¬/!ÔˆypÕð.*Cbß““ûÑ>ò1† tã}.xnEÉÉ6 @ö×CæîYl–:YI…3êÃÚd½%xoé¶¡½ð|ŽÕÀœƒA ‹c¸*ƸßÇëß»ž bHYn7PÆÙ*æˆÙÇÀfóTüé·ý5<ãßüØîúJî¤CE¦šâ±Oò!£C)¯È()F\Ì>’¢ÏXb½G5öó\)ð1¶_2ÓP“JIÍ÷Â{•lJ™Gçä¥ÛWxä*«Ì‘ô8ëB”Ÿ¢äR©¦H\kÿ£„cÜ.µ[Îð¾)ÀG/óÑò´4‰ÅE1îEZç E´BÄ )£†,¬ažƒ/ßÖ¼àĨ û "Ø¡?Tñ?—z¶µ]fÄÄ/S/VÛ™¡”ôRp¯#C‚ O ¶c©;UÁŒ®ÁŽŠr2wBF´0€Ms#Lsˆ¶}P5Üþ)à#)à±pxÁsY1[¨"$f)e×fëÇ 5 Œíz'j7我Œ 5œ…lË‚9ÚýMô„÷£ƒíŽúív‡¶ xhýç9„ï“Ö›JÀ#Ý–Îî*´(}¯_c‘l…­˜k`Ì5€=FŸ´}=ü}hˆ°Ž‘êØ «ýôÀÚâB½ 6¸p©AÓ\‡Îîú>DΡÔý<öpf¡.!ÛçaA4¶šþ»ëchˆ‹9Àô˜çaNsÁzZ˜u ÓJ™¥í…Ç–íŸm;ǢћEëë<aŸòÐ?ûu|éŽ_‡íã[åÏ•¬ ÇY‘’ )±O‰Ry–Øw12CBϬ |„U]B¹ŒÞ©Mò! „Rq©­6¡€{“EB£ÖqÂu19JîW’š™g$«ø†¦)\©p‹YRrÿ û\˨E‹ûæ¿B‡„Ï÷˜‡‰Æ¤Ð¶ÂT0ØâàÜs`¯yì#ÿÖ àÔ Š:cŽÌ)ãY3&…l¥ŒŽ )NÒœMc/ö»yXb}!ø­7~IÙWŸÃ)Ý·"À#u=tO\L®5Éöp×ï“ù’÷¢ìàLþK xˆôu·»PY#UUCdŠ}ðÈÍ'&^;Àc¨pS¥(ÌãA‘0×›¾jÚK€=î™¶{Œ(ÔÅlÆ{gÚ¨,=g"çÎ=„ϴ݃¸áwßÞϾ_] ˆyÑjô%|,'•Œµ³C@´föÂR*˜PàJ³;b–IÈ**)O¶Q vsGÜâiÞ•ôekM¶µ€Üô¯ÁM?ð´^Æg¿í‡÷~¾Už˜²‚gAJ mT,4‚!Ì=¢˃,î“ûÊw€ðoZÕÅUÏúH{DIiÍà vú\ŸTÌ]_úÅpjœg©aí7‡ÁPêÔ.ÎÅÀ‡¤€ ËñQëÑÖÂdRûi¿O© “¦‘Wµ‘ŒÄL؇Òv>‰iÅ<cÜ¿=¹æÑ2¡ZÔÐQ~'²±è9Ôd¸¹D©2M»êý§áJ~v® ï‡þn„ rÐÉ-¢ÄàF5SÈyÆ•ûjm_ÕIL"I€ƒ :è1QbÀ#6äè¸PA£€X xH}Ž ÃYàa¶Ñq©P5M§ÐAÑM~ØCøªRöÇpàc,ÁM [½ NŒê€Ôõå4î<>)vå[ZÌÛ^ÈìHö%?ù™….:J‚œš³…þð<½HsUYHK蜓™*yIôü¹võ¾çÿ[e•«@VÐã, ó$„tY7qUxÁ*xP1†Š'£6™b@å4›1‰">¤ä¦@ñ/ï3x¤%×][˜Ð,4ögyb˜Q´d‚«Ô~QHL¢OùFëÁ-*bM¥t’WY‘B=ÿL–Hžƒ!Ú’¢Ïç¡•çEcÐlâ¾3 bO×> ˜/§OIrH¸*4±!ÝodÈsLúDiàJNþ[|”JBß ŸôgQÁO4o…$Éý»k2€±]èY·mò]7Qžy¢ëÛÊU}çÀ‡?v`À‡kk.Û‹…™QÀ#6BFžXÅeJ˜à¡Í;»BZúOü®—³I6~=Ü8:Fš²º-М öñÅ+mÆ×"ÍÅâš\sL çåHö+Ö£hok×?ð'*dö¨`ï\#¹d §ú8Á’Hú¼tRÐ)úJú<Ò½Ðí}®;WYå*‘ô8#)¾‚÷>ðœxEÎ1)ؤ[b2C?F„à ,â±JÅÓ±E@6¹)Z šÌ~!טs£"i6½Åhֽ¼›HƧ/v¢Ç9+2ý³TæPÿ¥¶ªÎ#ÝGoœ ¡’ç³ÂØGâÆR‰ÞçŒbK #Ðȹq]c*v9eÀ—¼gìS•.é9)t[q\èl‹þÁø0ç°=÷ 8¾rg´_Lq×ÁU c$qv\Rf0–¦Š¢äôl‰íA·TîB¶Æ:’‘ߢó\Š¥dŽ¥€trníÇ¿>†c¤±*'HžP†ûK€‡û[-ÑI§a/’dú˜<Äœàå©Aòt¤*)Q]%™ßí0öZ¸p{p-NÎ_‡ƒÇêûawóvlà*Ú™?ìZý=ŽÂDÊ2œC?¦ˆT>ÔzÐvœ£‚n©¬¦§WqÇã¼"ÇÀç’ÇY*œ!åý溧hJ]Òׂ}JïûÞØ9«¬2]VÐã H‘aæO/=7(E-ÐéÊØ4úlb’LeŸ÷§A!0ÀCjh޾!ÄžFm‘|ÏðÌØ®4…·Í¶ã†nχ1ò²ïýoÓ©¼5¾”.Â1>Î1ÃWú.Lƒ/<÷›ðõ¿ãúH޵­ÿ?ÏüqÒV?ëpÝIÓnîw@MÓßÚá\ÖtC%!@³ XÙ|LSÁ)®@µ/YŒÑ@×/2O IsSýY`Àƒn×ΕºŸW ëf•U2²‚OPùÀ>€‹/â¶Ûn›t¼[PǺî£B`Рi®Ç½/þn<óSŸÁÉåf Ó¤~…RWr,3!àyF‚¸i©‚LD—)žia}÷TÜѸŠ<Ì‚2Y¬'i”¢$ð!RÛq›S,–ÊëqRâašäÕ¥ F­H¹tj?úi)о;òž\"”b̽¡sŽ¼Ò½sè%ywŠ'<T=ðá¯g_ïe#L´Ÿ²á6œ]W(ÒõŠl¹üZ4™Q&Ìq!@!0>RïDì4Ã‹Š°žˆrañ~aøÍmDŒ¤žÕxÀ“ï¹×ÿîKðõ¿óG@w Ø\¢Q²À#~?6ƒ.ä¶·â»ôxæ6ˆ7ží±îKÛ_7¹¾ªü &O,J8¡¤_,$q(ÉØb®šñüåBÚÖ²¦¹›æFœœü):\ú&˜g³`PNpðC9FrÆõÿ;ø*q|­² ‘uTž‚|þóŸÇë_ÿz¼øÅ/Æ+^ñ ¼ä%/Áí·ß>©­;ï¼·Þz+n¹åÜyçù¸h±›„ÊéÊ®œöøÏfÿG3~¼ÈÀÏ´žûŒá"Ûøã~Ãv<†õ%ôl0cÈýíéãDÛ„†™ë{xͼšK…?ƒâI´dž /ÿMðÅ0j3}OTÃ5Ê]ò8‰Kr˜Iv¸Ì¹2FæD|¾ÃøŸ¤`Ïy^”g“_M0õЖèpŸ0”eƒÆölµæÐ'ô3¶Gæé#qŽ!}ÁÅ»Y9NŠB[¦©¶è'ÕnÐÏ4«Ä¢õõœC;R¥íE,„Xt"Ý?–x>T–ÔýÕ~Ë03f ]'•OP9EZãÙ÷à]òml¼ñEÿ×mî±m8VØ|n©žÜ·ÔºêÚèÓíð_þƒóÃñ`a»Çû ÑáÇ¥Æ|´=ó^.‘+˘Clžþ?àè¹?Ц¹‘ÍeïPxäŒÜ„>}®œ+Ë&å;§ýúd¢Ö”sç_ˆÍ ß…¦¹No[|綨µ õ»Ú{\zL¤2±ÍéVYE‘•é±g¹ë®»ðÊW¾·Ýv>ö±a»ÝâÃþ0n½õV|üãÇ»Þõ®¢v~øaÜ~ûí¸ï¾ûð‘|dz‡¸‡2qÂêºð”Oþ´Ý`w°ö؃!ºl༧ËÆà2‰™Rã…ç30„^Þ®9–¬þóIÚó@ðÌÆÉ‡»pC§÷~º¿c¶Øžl÷(NvŸCçCYÚFš_„‹J€‰že¥½ Ä1¡p&îIOê2ÙÓvнF‚—î‰ §”ã#åMóÏFzÉv€˜ö¯\OHVá½Vë©…R Rj|™À«»éÃô<]~œ?Æû4Î[¹k‹D˜cœ)ÜÕ,#­¤ÁíÜÜÃ/îWÀèp}2|<Ññª7\Éÿ1Uô£ó Èð(5&öêÍN{ÒcÑÖ‹®_Ç|éÔVxÿ¨‘à ¿&ú»H Á˜š}HX¥ß¾ †4íÀþ sÂ5¨ßþ`7èÃ}ó@Hx”®Ñ!³CaópÏ HaçÔÆgp/2÷W*›lÌÍCïÃæúï< “ ß)Ì'SÂâÚ¤}ÛDûç@ˆú9¢‚í‘b&©ê€±Èý9¾òI4LjÎå’bíKUp Œ àR‰å©\5áË«¬R)+è±Gyä‘GpÛm·áààïxÇ;°Ýö“ÑÍ7ߌ7¿ùÍxÛÛÞ†›o¾?üÃ?œmë†nÀ[ÞòÀŸüÉŸàŽ;î(ïˆóÂ00 ôPP¶€9ìéá `Û£!EÂXÄŽ;Q²>„F£µ0xEzCöÖ+@üZy [µüéoÃp^ãé® Ð\Óß»ÝÐÀ+SÁÄYÒ=¼ÉAŸÝ>I*¢à…(X´xÞ¬ÙxŘ‚PVŠÃÎ *s$>o¾Ã)/ôs,ìøRC³øP…(ê–á>ŸOÒ}e€‡tl(ý$•ÐŽòvøýy?[  &€ÚéçÛéyDŽác~÷^ì0¯D8nÃ\¼x±T%†Üø+¤J<òœ! PÂ%6ºÒ ‚¬òÍsrÙ¢°0jôÖÎc©ö«BÒFãkü>4£¨Ã\ 4ÄE6~ËïQàø la èÃ32?°Íãû öÖ³Iݶ¡DzS‰¡4þý³ìÙup‚h€ž?Õàæ #ìv„ígÿw´.g›"šÁžML륋öÛÓÁýc;RÖ¼pà£r.T個è.¡Å%Rí:ŸØ=a¬è~m‰ž¶GýLŸL89WYåñ–uTîQÞùÎwâž{îÁ÷}ß÷áºëBŠÚÞðÀ[ßúVìvùÄ–†¸ë+{ÒOB>kàS/h¸-a†2†«¸‰ø\HŸu} ÇrÀõ×ô4õÆÂ`H:|F:áµ±Äé~ž"_+<æ×öIJûvGCg…Âvl_A‘–<ÞföŽôw¶¿DEéù }Áª&Vf½W½ñGÔhîC·œ(Õ€Ç>’J‘i³áX6CülPº2 åHKHñOù‚чRÉ€Oî½nÌ4æLsí0G Ÿ ß Àñ ùô £µ»8|A0ÜE´àbc¯©µáq£a”v<Þ‡l éõ>¯ßŒc­><†CM¨“ûŒ¹w ”†=‹þžŒ¡žÃx*ùºHw“ó«9ýÏ*«(²2=ö(ï~÷»¯zÕ«¢ßžõ¬gáÙÏ~6î¾ûn¼ï}ïÃk_ûÚ½õc\@{6Bä »cs£¯W¶¸³2q’xh70€ÿöÞ>Ú¶£ªýÕZçœ{o‰7‚@§ $4Ã'߆ùЀÐ(ŠÞc ÏV›?PP' ¡A´éˆOéB dê\uH —3ž÷ œñ¼g ;r ÿô¤îiÛk¹çÈôX‘|á _ÿŸ{î¹b™sÎ9ðùÏ~Oú”@Eüÿ¸$O*ÿmŒ%5æ šö0ÌÖƒÐÿ,úþ|ðš<%0jL ˜C°[ç`çÔÃø—‡ÂC>ùY`÷z~‚—BƒF¦í£'¬Š¶PÑÄb‰”caLÛB4Ÿ+y@‚„ âwÅøÅˆä7±=¹¦ôÞñáM0îbð£ÛÖ8λ’l!,$Ư•çYX™É{Ý÷bç•ÅE÷ù{D€€KM Nv ’¼Rƒ¸¸åããÿÑ3PpL;¢¶™çË÷Ç"b¸ýôïë0wT°=’pµÝQ í mÏ<4vG-ر—2øðcð!Í}µL> ìYQË!¬Eü Æ÷t@Tܱ æT9¼AO,©±<4Nº÷MÆðÛ‰ÀYϬŒþîDßá×§þ.lÞùUXPÀc›]¿7}˜‹.rÀC—jÀƒ\WIÍ‘ züsmª¡+¼àL`2{žÀt•ÊìñÚ]ûþ©!…dNŒæG6´1–[S“0jñÝ‹ËOyv¹1èuXšÜߨã“uxËZ2²=V$ßúÖ·Âÿ<Ÿ‡—{ßûÞ€›nºi¥}¹»»;úÞšÍÄV¦pµÊ9GÜy©'·iÎÀUÏ}4θå4\ðW_ú;±¤YtP±wÁÜý5\û„sð÷Oü,òé{Ãô·ÃvBŸ ÛÑ„.øÈ*ÁŒ® ñ@1%̱WšÈ0áš*ºªBãç×[UG"^‰#$3EÉÂæ»³>0ÚÎØKm)ôE¢„küHÆ…â1JO8m•Í*:¹Ý fJÎX´¶wú¼úžG3jåú–/LYOEŸ¨L³1±(3I¢yƸÝ\@ „1#‘QÅßiðHYEYð˜ÆrùèN.!aõbad%†_ŽÝá-üà!,UõgPZµxøÿ™Q4Ž¥&ü¶È¼Á@ÑîwÃv¦™|8P¿ì]°;×G€‡´KK”»#¹¤ '@N̤ºœ“˜ "ÈM9ÒµBwx­N$ÐOrZÕ€˜óÖ>‰í‘ 9Ív‚¹Áiäϯ{ŸÅªÛxhŒ×ùÀGvÎn¿‹ÝA¯6f+±;Ö²–“AÖ ÇŠäØ±q›©­­-±ÌG]i_þã?üfôý)÷}žv¿'#Ùª’›.˜¥4ÉÇA iÛ¡ïoÆãÿ¯hÑ÷·F VÑ8´,Z ?‚‡}ô³xØÇ¶`wþa8æ“ôÚø|Uòa F½Þ±LI(žB˜Jr, C"LŒPׂHZÏ Yï‰B<2?@ÀN?¬²%×ÉÀéùi¡J™B­*»ÂXÙÃ:°m<ðˆàž—µþ|Z³÷Ð*ʬ¦Uï¦áÛÏ—óHEb’cx¤y=Üü6î Ò…ß­q‘µ;°Lëx'|Wr­ü|}§–à‘cwœ°£Dä¼´?É–·b>š½ñxÛ„õ!xg«=òÒ\®ÜG úŒlŒ?"= +¦ù„«NÒyŽoÉ;†ßéÀÇÀ¾ÄúþhüŒlšä½ßðÈHìФ˜‹+)¯¯ÿ‹Hxh,½œ ƒ*‚o“WGó¹(Œ•¥‡¬¢Ô׉€‡ÿ?zǽ³ˆ­Q™vÕ0T:N àQßóRŸÿý›ŸÄ_Ýô7 µ±–µ¬ZÖ ÇŠäСCáÿøð»¶œrÊ)+íËÿ~áÿ†í0)¶¢b Z¼x¬@ôÀ?@Í=ÑGןaXlÆs:`wÖö°öHXø*ªV¿b8—éž5ñÈ€WÄè–­ùp ÔHÏ+Êm@Ï]aÎÄ â]qÆ„ªœÎò¸$ ˆ ˆ†óŒÉn»a,ð°äbOxªBRÜ‘™#„¼ ×™Q˜Çòhý¢‘%†­Æ‹Û'À‡•Ÿd[cãïÙÉ{“ø¶shH g½]œ¦Ÿ¿Év¥I‚Šþ“öÇ2Ïçé0wS*õ—{ÓTÀƒ+¼Òõùþ˜ xä {Áê˜|ÔпiÝÅ0‰îM~ŸœÏ)#SßÞ"! øø»˜{¿ë™!ۣطÂÊw1’o˜6ü·ðÞ$€FÙÌ´GØ–…œ Œ)¿¦ñk ÷À³À´›‡,šÓ`ÊZo¿Íkâç€Ý6µ®hK`°TÆ&y·RM!”ðd?Ÿ‘û0? ë^˜Oøv´f• O ­Ê”}ÒYÅŇ¾ß¹}ÿùËïÆ:¼e-'“¬AÉýîw¿ðÿ‘#Gpæ™g&eŽ9¸ï}ï»Ò¾hà`{0ZÐlj’z›è“_Lb”ìÎ(ãQ¹3œGÆž–êóypEEŽ¥€ñšj<ql$e5P½´“‰ÖÇœ"ì¶üuÛôªJiÑ4÷FÓF·{Œ9c¢ïn„¤¤ÅIK)ç㈌²›‡5^©oI˜yþëz§L“ûQ\—H¢Û È-˜ïcé,&«<¼ÈkÀaG! | EË#ÞÒ9:Ò]32¢}Ë”‘õDÁZxí‡0Ÿc2çÀƒ!„§xcÆGý$ÀC2D¢„‚+<&B€æ z˜)ùÙú—ÅäXP“ @MQ>V!<Ì'½O„ @"žäèÄá,uÀG dAèC)wR¸&€cÿ4Ö‡|ì kr —¤]ÜyÎŽôÙuÂÿ„%)$:ôüÅPÀ<ó'»–©¹ÄbÚšúV0|ªÚUëŒëNÿß+Yp=‹Þ_WŸ|yð#Éi§ßó¹ó©–)êzl4Ø &åv[¿–µì¡œ>ä>‡?üá0Êýõ¯],ã¿è¢‹ö¬_^ò˜ûð7·€Æñê]0<(‘Pºk IKê±;!76NºÈ ‰÷«— 1ÏÆ$%…—éáq‹ÊáãÁ»õ/ xLc¶`šSÑ4§Âoã•7žrÛg>дgàŸòT´¿n?{º{3Œþ|³—@v Î Ütö›«kk¨o3ý|\?ýnÄ`¶`ôÛÓ¿~^6ünÒû–$øâ¹6D/‡ÒFI¦æñX2ëÄIìáÉR§i[Ò÷ð¶‡i¥IC¦€*KL»àPð ŸáX’ØÔú9¤gsÕÀ,〛ߜL<$6ZÎ@PÎçšá7Á€›dôud˲¶jÊE‡hÿKÍ-êÅUôäù"é:zĽäeÕEË÷ 2+ýoä9ñû.ß§±ŸÚ|‘Jƒú-hi;úo"àÁ®ÇKŽ }^º‚WÐáwdqŸÐVp¦Øñ=*ûy`'îoøì„c¢Bt˜äSðàs„2ãùéZ*+ÓÂi²’\[-à1U¤q=EšÌgj?rmL• ˆSKDïÄD©ðì%ýTAs-k9 e=2W$gžy&ùÈG¾øÅ/Šeü/O~ò“WÛe±­>üù¼>ÿo|l§J‹m§é·ä㕺œí#ƒ$þ QùV^ô:tc‘+€y¯–WèÇÏ(2EñqâÀ†¶½?>ñ¿<]d5øgØ÷·¢½é:ôÝ­8ëë›@„l™±(ƒ À?>ª F¤7„é.;ì7ò·|h¯|hìƒp}й“dš›b`N©vBF¸güÿ(¬%Ú&³¤(VÞgÕúœü˜f€Gôü–€w‘9} ^àóMœ_‚W;**±72€o#|­;$ãmªÔ¶»‚q¾PÝ j¾, øò @îZÙØ‘%–õ‡H˜«Óíqóm¤õ©€¿i­&Çxßã>x'ø–Óœv',vB>¨ØA2P€Äƒ&‚nBA—qNÉÜ㉘ÓçÀ@ݨlEµB˜Šû§M߉Šð ÷EsãwÍ0Ÿ2ç se…Tï@3 üdˆÌCPêêHïgP® kÉÀCNoöü³–µhr’Xßžr饗>ö±%Çn¼ñF|éK_™gž‰øXi?D–0‰e,!TAc¨2K<¬ÊÇe\?îï¹ €BªÌGÀJâÉû/•³¨|±ë+a¬JZÔ4F#ßßkã¶³nc÷‘÷wT‚¬=‚û|þ÷Ñmi(@=ßòGbƒ$€È²í“‘IÀd¶GÂZ`솕Ë ¬r¤œ…{%†µ™_4Å:YÉs¯0HÉx4fÀnd¼ýÕ?•Æ‘7+¡c'xˆ×Å®Eú]b}D¿ù÷‹€1ø1è ’Ž!èÛÔëP´^ÿ;ùö/–<0&ÃqŽÖ Ò2ð‘c{$mS½€HŽ­‹ü¬ŠÂu’¢³år%öbY$Pk‘ùN˜ë½ˆ!lå÷0¥! yÉ:^ú¬e-'¡¬AÊÏüÌÏଳÎÂûÞ÷¾h7x÷»ß¾ïñ‹¿ø‹!éi×uxÁ ^€‹/¾7ÜpƒZï]wÝèûÚ ¶ÇÄžJΔ¨Ùn‹FMu¢(É1DŠG¤„ £·ÇeÃ{pdfˆ¤°p¦Er­ôz9 žñ$ ~güPƘ×J»¿t‘±.yë3ÿóç‡zÆsh^ƒˆáiýžö±$¸§¨!‹ÝR‚hqÍÙpzï àU®'jgˆZÑÆc”+bŠÂ}ç,ñ™Õ*–¥6b°Cð^bŠAꕬì ×…ú°±”1ËÃˉhfØr@¬èfC´¶’Ó—$:ýè'Èõ«àÇÉ.³Þwj|pC—®)LDà#TzÖÅû,{‡k µ\ÒÙÜot,k¬ ð1^H+ÉšêßUâlHB\ˆ³„‡¼$NÎÒôk:k« .îsi 2ƒr4VÉ:©8x’󨾨®‰"\Ÿxd ó B^b†]£’°K¡ÞÙkZ$1øÁY{@~nœ|Àï³À §gôY.¦„B%¡UkYËI$kÐc…ræ™gâ=ïyî¼óN¼ä%/ »µ\}õÕxÃÞ€üÁÄ+^ñŠPþïþîïðÇüÇøÄ'>÷¾÷½b·Ür >þñ®¼òJìîî–;ÂcQ)øÁ&aƒ¦9˜ä‘~®¸Kà‡ÀÀÐŽçŒt CKëû¡xe$å3ñVD^T ø(¼&Y–‰¤¬yaÆ”oÖl˜0ÍAÀl¢ïnC×ÝkïŠûF$<%2=‘ÿäÀÊú˜ |ŒBèF7¢ç²-´-X÷™h¬.þ}RÞ å±ÆxŠØhávZ4¸ƒ<`¶€æ`Í©ð1ô7€¾3Ê8a¬Š8·¥ìI|hàGÜy¸K~/é}È…Wó²5sG ~Tµ¥ÖźÄ[™Or.c=ëR7Gæ™4ZýH`g$ër#%=2©a—½ø`Þ²]Ö»·¬X.¹ä\uÕUøõ_ÿu<þñÇ)§œ‚£Gⵯ}-^ò’— mÇIâ /ÄcóÜpà xúÓŸÕÓu÷¸Çášk®ÁÑ£GW\qÎ9ç\|ñÅøÀ> ö! UqÙÌñI)±5LÌ;Ãù¾ìx~Ø–Ï×$•žPµ€d$t²²ÇËJ,ŽÊ¶¢ß€[lH ‹æÅJ³íÑÞì´ÝfH j¶Ð¶÷…µw¡ënúípðJ‹9„°}lÂ~6åøÃÂN3î|}~dž°þwÄóÙo°nœŒ;ò`¸~ ¯ÞÕ¡(<;¿i‘nÛªœŠ8ó=ÝÅ`©Ì™¾GʽHˆ …δ긞&ã=ô÷GbHÛv&]Š@´á]ác±g˜è IDATPbˆ„6:øq4\ÛÆa=ûÁ¸ýðîÍu0»G»Ñ(`s‡iÛºw#2²b@XÎã¡l™hSƒi¬“ÿž ¿MÉ¥±Œí›}êxóm:Ø./Ñ»9%>|`c˜{“ö¥¢ Þ'q7¥¤P¼fFÛÙòñäÇ¿_?Øýäc a.æS¿е;îÿ0a€Grã}åÀœê%Õ„kK®y²;Ü8oøkçyÞçfìÃp}nçžTo‰ªÐ#˜áY½v-0'Ï—í'/áÝ|Xz/º0G[ôã!I@SN+­aÒ½XT zÅ”rñ®ElÜ-ܧX¢÷8Ò éýƯº^,6W¥`F&ÄlìåæSN¸–µœd²=ö@ùÈGâ}ï{_±Ü)§œ‚O~ò“â±¶mñ©O}j^( &ªqÑ©ìnGŽÿñ?ÿÿdžn÷h¬ü%ÇÙYem 2ð(RÐ颒ës--UìW¬ü'ŠÿP§„­E‡ûhÜ9ÿí§‹ï¿ìt»7 F$AÚ›Óд‡´°ýí(m1 8 %+ (2.Îx6lƒZ|ùÝ+VÃuC?¬íx€a«Á’ÔoK9ôE¸‡µ`‡üŒëE£Ý9…k¢R±ÀÃÿÅ’Xôn,Ew¤dÖGæÙRfˆûN®Ûvp; rÿ~Þ_âßÿ’K$mvCÌ ß7Æ€ø='Ì ÉsWšïvǘw õ„º²ó½Äb¸I6D¥P·J«®?rÀ-LWŽç(ÓRøIîû^Hü eDðïÙ“sxù¡ "#^acpƒÖŸ 8VÖB?Ä¢™má%ðï‹# Ã+ƒ³u`0ð(ÈÔu$?ÃÜòšçësëh| =o꺹TïcfmȆ~ÔÌåëLžmnN]Š¡ïuÃܵåDb»(‡ò‹º…²z½žÑ»–µœ\²=öH;P…ÜM{*κéÞ‹Ù<òĹ]êyðd¨îÅ ?ŽtS'‚Y¤O¦Þ‚*Å&kdÈŠª£ãúgÒà 1ÆOù?þúÜÂa›‡av¶`»o">Úñ¯÷ê|žmÑÂø#|ÀÕmͶ|mô;à´áð#>Ò~T<‡œ×jE2ø(UÀ‹ ]y_$´e’ØX¹ã@¤Ó¶Ay×;“é‹ÝvoÅÙן³®}vœ‚¦Ù@Ó@süî8œA$Ž!ÓÄÀ©v=…—èç,àAM›CkÞð˜¢ä½á´þ€_ õT‡²R‚EA:0(¬U"¿?³ÂcX?ð#¬Yì>)^Öp¯À§cɳ<¸¹<`¡¿ów!ÚHØ¢HD¶‡Ó›ñ;qÎŒÀq© uKD<Ýc†¨Œ96Â܉ô¹Ó¾¸{E¢˜õQœ‘ô£™ïT¥£©F‹Yš”ÙSC i,^d®#Ëö N1±?Kй €‡ž4žþÎïYª‡k`¬âQ[ìe[k¹çÉôØb»Dñˆ<¯€STÌúî6ü«¿ü#ìö·¡·Çâ:zWªÂçª(ͱ·‹ŸÏtÛ¿’r€¢Ô(4Cušåÿ(ÇÔ‹o¨Q8´`lƒ]{}ìå”_‹¶?Û}Óå1ðÛøH¶·ò|¸>rÐH_°$`d ËÙ€Ž-XÓ%À‡A?~g@Ûp󸍡–1ìÉRe¦Ä*É6‹2>êeP( »Qì×.ã)ò`‡/NÁ° %o“*n¼ŸþµijÞ}´Û·»fLCê$ïýàýô¿ ¡-ü>F»¶°ë çdi´i¶, xðó „Êþ¨e}¬J2×\›˜6–\hÚ"!8å{\åEÎ1&´Ð'g¤†7(çòõŸü=Bwè;ÍQú°=²átÝÊEëБôƒçÙš1 ]„gB‹‡„ õãá.¡\H4çÁôkMAÌ^¸ïõ`G>g‡¬C¤!$Rݳ­p¾¸\l›hÛ3Ó ïîDgoÏê ðñ–x}+•ܽŸÀò ÿK!,¡½·ü¸ÀPôt™ò­ÿûƒøÖÿóAØê Ö²e zìIör¤¶CgoC×ߦ*îšr!·),–VQz sD¯0£DùI›ä’…Sòò,€Бsž¤øR0füßÚÀlÆ Öî ïn†ébܹBZœZ 9 _{Ôw㟺 ¶#öõè21Ñå™!D€p`ë/><–…¸ðp øp 6‘AÊE]¤Ía ÕQùl³†°…îª%Ëê¨ OQÅQR%' p ÷T/¨x_‡±þoNš-lÜùeÐÏvnÜÛððgJíŽñIS2ð»(íKàˆh¼Wd59-j€uKé%ˆF¥÷¢Ünè˜Å ¨¬#'SrÚº\8? 7¡a°$¤¬Ö§È67‹…Ëjà¿.Ήr€L”:Àƒ9\†>ùë¥ÌÖ鉜ù\èÿdØ@~î,ß"ÛC[Jù4ïÓXÞ3jêñuÉ2q™xmU¹”|OšÓ°yø§pëÎÂiÿfôý‘bž „GX¨+Kðàͯ à‘ÝYf“ž(ÀÎ|î3qæsŸ‰îè1üãÓž¿çí¯åž!kÐcˆ¸S‰_tYÂIµ<=‡(%Y&™¦h tRg ùBŠ×B@£r’lmœ‡»ïþb¦c\ò^›\LgU¨EA"À…‚‘èô¹.€ î>mÃØ­Øøã÷Ïvxàg¾ !K&x»+Œ˜ ña2€£Ê¶0vÛuÙ ]·[#ÄŸ“äü`À\}Ü;£±=4åœ'"Ô…º$ç/²“ÃBí4´J>¾"±ýBJKIyw÷¦‰WÄŠ—ú^Öˆ¶`Zô§ Ù½°Û Ö ÿo'¡-#‹Co/þ_P€µp©:)´…Ö•ÍÌŸÑ–‡HÛÎyTàb.ð¡†õaZ™ÊöxxˆÊ¸™É|©N¼Àñ¹;-²•ŒKc6kQBñÜx'€ U#jÙé8"&:eõÉ9@”6*YÒ1ž”z4ò›øHœ^”5šçD €… gÅõ„Ý^†µ{t0Ô°=–Áðú7áý+îı–™ã¤Z2:_Ø]w;Ì‘ÏàÀ±§ÀöGÇîiº =] “Eº'ê}ʱ<ÄÊ¥cšÞÕÄ ] pYËZN ¬Aý$\§‹.d¥Gò¾XêQPó‚È~øàŒŸäÓ‡‚|üÇŸ'¼ëìîÞ˜=_=üa GQ"ÀcÈØ?°+`–ôÔm-lˆ"”Ö· ìÞŒÈ#Ž6ü"I8ö®1LÆv0f[#ð1$- »aø|¨b•¢gIPZV•Ì”Ô[£œ¬Våÿ^ømAÉxÓ=0Ëêh£$Jžƒí¬…P/ FÖŽû×v0»-š~èÀ³9ìÀôøâ÷?^yì‘ÿ.Œ·‘’?Î+ ñu¡O’<†>d ³Z, xÐ2‹éy‹‡¹˳¾Õ'£¬»'*رd¯¦Œ•@hÊn>³¥.’3Á ÷òkHzbú|5=De{!02ӣɿe™ðáûh™s a²ñ/³Þèøñkü¸î7æTXt°ö8` ?>²1#ÀppT¬ŸÓC|8û¥ܯ`Lg–ûnXt@7Žù Ì?4ÜÍFÖ[2,Ò$ap1<¼ð¿K,à!%Y%l%»G²Îé±–Œ¬Aý,ø(è—œž™Ô]ª“')ð‘ïOÜF3„Z4xü»Þ®?¦ž WÌdÀCÜE’\FýŒ$ Q8—zÃ=¢.ÕÕf”¯ø°{ª–çuµøèóÎÅ÷¾ïÿƒÁ ΋ÆýۆÏçá ?³<†¾ a.’Ì#¸çŠåõ(1I?æ±}5º¯ mï ýŽC†‡Â–œŸ“¨~l†ä ¦­~—–Ѯڟ‚~àžq3}°“ ÓÕ€ Mwäá+b8 gS+,¼®¢K˜5R›÷°µf-ûBÖ Ç~mqª]´X j8] …)-B"s„íò¡ïÞARì @»ñ;9®Ñ³ ¬8àQ£D–¯hÜua5ÊxõÚl‘ï›xøßŸ˜ë‰h¸/¶B[Qà¶ßzÖßCwï:pŒ|÷Ýí–¤Rõ‡ÿ1Ô%0­ mIÆßúu%Ó¬†åQ+ %嬔E“剆 3eâuçÞ­ä>,ì¨:^ÉðXˆ•VyŸÔpWð{×'k§;Ÿy®éñˆÁ´þQâð©Ñ0µk×#$ÔN“)çX¾¢2›•Ö5îTæ¯Á—ƒÀòˆ½ñ4¡¹<4Ì`k¨¶ k®“7Ýï(ÎÛ< lÿÃPÖ9Chî­y é’Ë"þ:L¯îÇ4 %ôOÑ98øB4MKvq™¡«Tëˆu€‡ê ɬÅNP-üߘƒ°f›èÛkYËÉ-kÐc?ˆW»d¥–…œ‹SϰÈ'1þ"Ë")Æ…ÓÓÎ9Øè{u’Ҥì\žY¼4fÇdñ4ÿAd…¡Mÿ/²©Û=Æ{á}hK?¢d÷wRÞã!É2“úª„]\¨n¯8f&ÚmN ­ ‹ÐBV´s/rÙ­3è´¸zMªò „ü1ÔHÀú…µÐ|àËÄ»E¹¹ªÏ+ù¢R¼âsVÄÚØ Àc¹âÃÊIT¹TÕ€8_昳¥TY8¯†‰)€éýª`FFu*Ô}ü¤¹FLÀK+y<ÀÊh;žä@“p~Å3ˆõ˜á7~Ð\^IHkPë³ò28,ŒŸïŒ›ç€ßõ_.Ç®Ýç¿°3\?®Í4ÜTbe.A¦&õz ÇÄë”1ÑÛãK7¼kõ=4„2–Úºõ;Û¡4ŒEÛ¡ÅõKŘ~!´‘ï‡Þ6ÑnÜG¿óé8ð•wçAÿ>ÛÆîÝš²—m­åž'kÐc߈¬¬8i”2ôØ™ÀÀŽ$¹*«3^x©â/Þ©ï GƳÊi÷áRÔ|#ðQJ&‡„;ÀŒž^¡ÉK³]«ÏYÙE´|ÌÊÝ‘¡‰×ü.ÕQ%J¨¤*KÁò€“sÂ@Éz` 0³øV§—Da1ü˜C~·Ë¶û•¤t¿I=ø³>ˆ>îÉ0gÐfô»<Ë`s¬gЩø1̇ñ6Ý€Bú¢Ü[Ð@Û{a¦Î¥H²þ0vÂÖG¢;ý{€›~ý*äqíj!¬SÚίÝÁÍväÜ`5È×õ˜ñEAŽjÀCù]N®;êâ:ÝÿÝî×qÇ™-î{íAô‘ó`-k99e zì ))C¹ãƒq(ÄJ—=™f £¥rà#*¼³œÎ¨18FJ¨ƒ—Yª’˜@øB’,,Û#Äúê`ʸƒ‹3ÀÌàÑ=H€!þ>ÚÍ,Æ"(|xÀeÆt°h;zÛ][}ê1Šè´þ·°^½~áÿL^:e{4:cæDÌú„±¢åÛ!Š…bìØ`HÈ UDå%÷-a|$ÏÖµ?)ö©r ÿ­¬ø‰à¥ŸCL#ˆY&E€»Ã'Ì¿{‰™®_ñÀÅxXð)<®`Isd–µ£ü¾,À÷=‰1%¤cÀc‘Ð?2‡.âQͳYS)÷`îµf8ðXìàǹ3€€>– ï$/×5XòÓXn«ï©¸…oæ‹ð$¡ƒ¶#ì?ÿwÐçÛØ‰  ° —Sb{d[ù>æè.ô+ s‘ “df˜GNög×Ý„Ï_|¿¼q×ïk‹øÈõCkæ'=¤‚SxÇ¥õ¸ð¨;Êa-õbÑáì«ß90~WÈj\ËZ–$kÐcŸŸ´g1 xÁgLŒXjX ÆM½"ŸËžÿ†¨TI²›g1ž¡Ž@[ ëñ9Ä̪¥¬@9Ð!`f "«Ãÿ/-˜Íl³™(èÖ6€mø!yä­‹_Á祲¶ÛEŠÔ¸› ‰…íA”âÂÂ]&)ÜDRà(‚QÅ%º—±ÂUT¤xì-UDsíÒcBx‹‡LËñã•"Í%‘Ï¥ÅÆæC`íôÝm$¨R=}†öèŽFvHžëÆy7r€KZÊs}¸òÐJ½uµ9°ÿSã Qªrïo‹)o"hÁb’5C3Êë!Æ ÂúJßãð|kØAÓ†µ§¢ïãжªŽòÿðȶòÆtt3[i"@L<¤y¦š%¦xÐÅJÐR~„e¾ ‚à'2¤e%²àQs/If}ÏÌssªWÇ#[óìŽò®ä)ôS`üÔÌ»2ȸåDueÇ—…D áŽQv >Æ>¶‡mÖoÂuõøëÝÌ!`ã ;ü lÞu›·ý=|Þ-Çmà’Žo+óJëtÛà# AuóV•c`@›a¼ô~G¹ø ƒËMr,i¢vŽÎŠÀ ¥íÒ„ýj4Öh fŒ à±~äöÂoÉØ××cÎüI·˜ßã1±–µTÊôXKV¢„›!É( wI&¸>œËE5ž4†/Sa$D?i@Gé·©uh KO&§óÖ&5õ¹2‚hÈ›!µ“Òð0møô›÷Â]g܇n½ Ö4°ílÓÀô½=zî÷è¶âاàÞ߸èŽÃ ʆ*D1„%“€D‹ép:Uéx"^“ìŽÒxÉP»èŠN Ñ–:釼(HXÅ,õÊ{ O«-ßh,k´ß’_*ÂûÝáîãW¡1Éûë%C½4 ¡§ŠÛ¦½÷Àþð`Æv nÌÖ ˆÓÊz¨ë¡R¨Š6¶F#ÇÑ$—H¾ìä Íò<“2=É<©Ñ–¿Q¸—Üorþ½¤ÖÀìaxË^¶µ–{ž¬A} ¥mZu¡Š6™œ¹A LŒÜ‚W>²ýè¢ßT*m%àA“b-ùùE)€*ðÁ /x™¼³=hý•Ý$àš&ÝÖlcÐ7MïV³f·G³ÛÁ4 šÝ]¥ \!Û×F¹<’£‡îÞtÁ[ãµ-ø(Ê"ÔÖéBwÁPý,§H‰Sfyp!ïo‘æŽ.<ëé’²AFåk»G®ˆØ.æ˜>D&€iË`xÄ!Dñ±6>fʉN¡'ñ¿$ŸÂ˜Ø‘°>¨7Æ4îÓ4è7Zt ú z@ÓÛ0I´;»ñµÒp¯Œù¼ÖØ€! žÄt¨q˜ X¼«»/îž5á|tD= ½Š÷TôPƒE€) ¤¦.wžÖ'ÕHÍ)ý ªJ¤¶j±]:®g‹0øXõÆ2WÀŽr˜@¯Î'ª$‰‡j¸iÝâøY_¢yguÊc4–jžÃ û’Ÿd·Æ(š,Ò{:Óø¥l”Ò;Uº—ÂØ¬JðÍ™ BßÒÝĘ÷¼"ô夓%wsµìŽTüò˜øÄÌUa.v[d÷¶m¸Zw í±¯è€þ®(Ôod€ú-¹)ómdÎù>É*šu2f[LMt[8Ðoû.“Ú‰’¾RoBLhA a)R¢º: '¢Ò2 ïõEßžð\Ã8¦Ïpuˆµ¬e†¬Aý"Ją̂÷&Eôz1„ºÐã Ý)€09vJ.©à\À#í@ì½BZL°æåñÇØ®.N(ò>¶%iôÔÕ"%€-v¤/¡ncÃcÃ`û ÁÎ`zàÀqÓ7hv{V¾!Ͷƒ7†¸ŒqÃNIwfa’xˆb0P}íx¸P Ãõg,A™Ÿîi_ÔÈÓ@–ÕaLùgÍ 2Ð1ƒ¹y£$évy9…Mk#H†ëê8&°4("‚ŒåQaà«ì°¨PÌ>Z”ídA–ÇdÀƒ–[ø‘é“xp0—+e*øï W–xH’cGÖ€ôØTfŠ|!­Æµ¯³÷Z$|<î—g¨%yŒ¢ƒä™ppš~Žáž‡µÄ1°‰Àò s —LÜ uù;p@æ='ì{A%äjjXN”+%â©¿/шŸÀ ”Ç++ãsT°óF£j0$Åݺªù€ê ¹pÂE¥vãœè;`”ÌeHUØȵ&a.#³–îæBó{¤9½üÂJC+ IDAT¶õR»nþ”Á*‡w…55**ÀÜäÂiòº—Ñ~O¯Ct ø2ʸ¡àM"«ö™ÏqHõWÞÏÊu$Ñ3]ƒ6@2¯+fA×Kêd-k9yd zì'aQÉÏ€|—‘åy«……x*È!”«ŽŸé{ÌL~S…$ÒJ’¢‰^ =[}<8`± Å»àÅnzƒ×}é?à’#›0=†O ŸÛIÒÞæÞ°ýŒåQ¿°ê2RN-¹'ìH€*™¯¼—Œ|9MÒ-uk;”†º$Iæ2÷&·£P ëCþMïFØO>rÞîí }e±½fH^Js1ø0n ئ߱hº’¼›¾qú€f׎»·ôH½AÉ}uíþû÷ ‘uÊVðHb‹™—>ˆWªFª¬y ´T  *äTXµL‰É±W»i”€ì.KZ¥ÄlF1r$[áe¼|t' *†‡íPN´\:¾™ÃþI€ ?¿i¹ {½Ã½#[pκs•<ÆõwiÏNœƒ¤0–\h =×+UôYŽsE|ÌÿÞ¶çÀ4§¢ß½½=Øãp[÷€>:JÜxKZˆ <ø<¶àš2='€D= ÇQ˜n{ø.ÚùÚ=XFhÚ\ùŽç<ßñœg£;z_üÁ9!}8¤ï{|îsŸÃg>ó|õ«_Åu×]‡;î¸GEß÷8õÔSqÚi§áxô ᢋ.£ýh8pàDw}Od zì#Qãð®lMS¨Õm!s(óLæF¶˜âY“À9ëø¨`K4c1Æ7 rð\à”ݳçõ€ûë‚~àÓÂt»h|?¶Ýµý†ûÞì:ˆ±ÖÝ®»¦~G;H<ÛÄn;åËî$€G|‚xd%w~' qKÜŠz2xµq)*î.™X{ Zôý±á Êô”>M¡MWÔ3øP$×ÇD¥ˆ)4÷ s%À#ÿl+æ”eÌ™´o¶×Ûå!,‡†ö¤«ôJ/ãÏ1,Kaw$•æ€Ìi‹äzÈ…‰UÙ¹.Ë4ì…ÿÝu ¹ž„Gû6uîË1:V¬Õ){'2F%§ÏÖÔÜ0"žcÅ4³µQ¯ðkÇ!sÈ}ï ™Q°vÇíèÂC[–!Yð…ßÒzT°cø£Rˆ‰û¿i㊞'ÿAô7£ï‹ãÃ=hA¯?>x·6 ¢w jŸH‘è »¬<ïyE;ôØÎÝe}Î9£Ôö3¡IkYÜxã¸üòËqùå—ããÿ8Ž;ŽY)Þm3ĽmllàQzžýìgã9Ïy.¸à‚•÷ùDÉôØò{×ý9Óà±g<;ãÂ%×^I æù¨PÀƒOôScL3R4¢ ÌÍhŠw¹¡¿¿ùï–JTo\@JFˇÅy`tð\â„f]|ÜjBÓ~Æ40]ž¤íMÈááØÍî.L·˜&0<¼Â¤yÄàa±MQÙP‹ŸyšKš3ÃESŒ1ÁÿÌä| Ñ8À£9ˆ¶=íÆYhÛïÀñcŸBßÝ9x爔ñš¤y³v_A"öFšÁ^Tæ99,\¯Ç%_“-Y(àQò¢+óÐL籉oÅœ‚iÎ… •:cŒj^ºåyƒÆï5%–‡F2o¨À«+ùq¾ñ*%v­YÃËQwnÄöÂzõ-¢¸§ýœ,3r¹l®Èä$šViἄ-6ñ(À‡º³H67D3ï:m‡×#¬ùÀßþÐ÷âQÿ声vdYºë"ßyUËxßZK`‡ûN Á‡~÷<å»!ùú!GHî 2Vý¼Ê@Áq¾ÚLçãu ¶Þ‘9!›—#Ñ}ýˆ•›,h12Zúèþ™·‚Ð{ò77 WÝò·Øí‡9ؘ=o }ÙöN´\}õÕø­ßú-üÉŸü vwwa­Å©§žŠÇ<æ1xðƒŒsÎ9÷»ßýpèÐ!path,áJ×`ùÜ—Ý®]z\É/‘›ÿ'ƒḠ>”ÃnÀ¹Žèàã‚‚ôƒþtï›n‡õz Z,pmóõ$vheŪ=XÁ§Jï¹^‰_KÚüü”Ó½©Ù²y-Óå#ùžÿüçãСCøßø ¼èE/ÂáÇ®÷üóÏÇù矽èE8räÞÿþ÷ãÍo~3õ¨Gá-oy ^üâ/¡÷'^Ö Ç>”Il%òVMÈÁ Æ9+JX´[Êì>¥Jvä „ÉÀï!¯>âŠùo=üÂk‡ƒÆ…Ñ¥Î/\$¡é@e4 €‡ÿÛÇ?Å‹Þ;äÿ°½<úã àAY²Ç¹…Af—Ò–»§Ü`˜²÷²§²z„¢K2üh }”¼Öö@½íB>Û;À#(ÔË;jÃT–$<ÎÚ ½™®L•¯¨®AŠTßÌýVß/و˅µ,ê20PrF"õ4zP—‚Cyü¨ñHŸ©<¤ò® ñv²¢M™…DÅ“”ñ|ÌþßZ¦âRÞíÕ»N1n®/5`‡j$Kc»b,jFëüùŠ€>ѻÞ§ÿ eÀƒJi‡9¦ xè,z ÉL-`_›i¨ÂË#ayp¬<.³É©¥ïÂ9QW”ÝQcáÇŠbI2uµ&´ß’ßõgkíqà¦÷Äs®ÊIדd.(èš%Vú÷±>©v>çlœ?'¥ã¬áëð–¥ÉïüÎïà¯x~åW~/}éKqðàÁ•´sÚi§á'ò'ñ?ñø½ßû=¼æ5¯Áç>÷9¼ímo[I{{)kÐcŸÊœ0—ú…˜+ Ô©x6ÑF;<$åc2•[A©Ó ­MêaŒ\jlV…ºØ~8&ÓYñíáP‹ðÛÖuÃ}dl x0à¶šMÀ0ÏŸÏá¡0ƒðÿ;h^:-qœï?<ª¶;âŒÃ˜a&ŒÒ{ŸAhÿ¤ßÃùâ=ÏŒÃ$o{~Z¬¸W¢ØÆ¢R‹šxP°, ka9cx½R"J| µ’1 ôºr K5&0ý]×r”ä„®9¥r±èk#¿³@®y©-_oùá €G8§|È Q9©9¤ÅÐ94êPv-3B]ý¡ O ¹< s\¾ß .³ö|?sŒ,ZOx¬W&e¢$íç@ñПJðÄäëKÞKr‡”„…yCÓa(ðáí~/¥sjÔxù<;§Äð\Ë\y×»Þ…w¾óøÛ¿ý[<ô¡Ý“6›¦Á‹_üb<÷¹ÏÅóž÷<¼ìe/Ã[Þò–=i{U²=ö…t6Á½.Eà£8yÕ*_´Ü´ ±´U¬¾eî”6°£V¹ b¾ íh gr|:Ø‘´Ù¯#Ëó1x´£ík‡¸Ó1¹×ðñ9>zgt›HÁ'@ØízXtxê,¼›)UK-eœ˜z a‘z]Ó–âÉþ¦•ÉŒ ´ñõ.~øöìØ~•Ç5ç¹,x¢q˼r£rQî†L‰)ÉÜå.(Ã#ðFZðX†Ô€m‰ñ,ö ûƒ„½h¬´¡%+¥3<ù"àáËjžb ððßà#­HÇ9£ p(“¿£œ'#ým,Í@ÏïÃÞ“JæcÚHÛc‘™¤ŒíAŒA ÆdÀCwàL’j–‡pª˜×ƒ3Èœôù„­j…Ô–Áj,½k18ÛàÃý&€“†'$m„ßÚ!ÇÆ!WÏÀp O’¤IØÃÅkÄ÷‘3*êÂà!®5 ¨Qñ»ë|ÊÐþMqòÔ–å‰Óãg³éž ZÀ¯n{-e¹òÊ+ñ±} §žzêž·}øða|ä#Á/ýÒ/áúë¯Ç¹çž»ç}X–¬AµTËz¼îù)ÔA¼dÙ-ƒÌ7LjÀŽœK¼ÂÀ ݱ¿ ðÁ«Â¸5'Íë1ïIý#ó#ìäâÛfk@¶`ÀÃ` >ÿGøô#ã‘Â>”Ý!ü&¥ÌƒFW´Ì@ÑÒ³îÅœÓxc¥Ã_ÏIa<ä”õPEGQi3i—Jµw9÷ÎÇ} çP&GPÄ{Ayeçåvd‰ IZ¤ç£&>|ŸlxÀ¯š5§z"cð‚×ʾI9?Ä]^赜 \5àõ¯‘Çü2¯!‰^ÐZ¶Z†U–7¨dËóìùÓ„Î#Ë;Šyº2¢o»>½¾t>M×Û$˜Hi÷Yžï–!³òy°ÐùùpcÀƒƒ9ÑÿËžmÄêc¬¬àÁë1ÍA4Ía˜æ4X{Ð…Å‘ Å¡0ñÀÖ]úuñk#MNRf0ŸÀc ØQ Ê&9=¦‹˜k£4>͘èÕ˜-4Ín¾üZ&Ée—]vBÛoÛozÓ›Nh–!kÐcŸˆl€OPØ}ù)Šˆ¦PrÏõÌ#UKÀÃ\e‹ö‘5uÆ[ÕÒER0tAA#¦ˆaX¨¼q8lÇ)+øŒ‚;jÔu#˜b·aÍÖ€´ŽY`à"\à6­›Ô—‚Ã1 ð J-n¶íhl¤êÊ}V!/…´$ÏuyÞ|U4àˆÀð1«YÂö·ßËÃ5 x<ž‡çm6ÝÿÆy᜴áºy vÌnpF|˧Øx”æbâÒ™ Q]ÿ 2ëÒ5$l‘e÷s‚H»q”dÜ%n­±»eÂy|L„ÉŒ‰P¨Ö µl¤øQ*b‘ŸyU 0 ô«’ ÛcN¨m±¹h>€ñœå¬Iî™˃}ùº‡y3xԷ±ø9  ð˜:Ç–òu$á'L”{<;é¥qk zï|Žó˜!4Åâ8+Gû“2Sr"ÝË\8Kz¾ÀUç5Kç¥(¥ö—ÊñyÏð#ϼÙ­ü_±ùÙ?Î3À^æÙø6Îé±–Åe z¬E—9´Ô’Á`ØM{C•°`ÅÛ¶¬/^a—À÷7U#`êý(¯f3ÏŽ¥BÛ"ÏG«*QÁ]b8ƒX·„EE&0©&íL!„Ú²‡µ®ý }áN© yä£hTˆ‰700A€Hq­P6E¡àŒò³žêRe¸E×3†Û$sXÆX=¥ øÐB]&ÈTÃSjC¬£"¤%eÍaùArl±ªqS¦–2ðc’È z  ÓwLf¸-.ù5Oÿ:²ÖÈYIÓ˜©%Îj%Ú²9ü™›€¼€eï4‰’t2`caÀ£xä”B #­Ä° BÞC—óÌíRÓãF˜îÖáz¶£Ð–àHòÀÈàhqNŒëm¾‚™Bû嵉Ơ߆rÒ¼9®S2³#7&)sÉœ5P’Yhª ,ïÌøì£¿Žïþâ3Ú]˪¤m[tÝ’ßù{ ¬Aý \išÂö˜af=¦ª7¬G†Ð'n"4_ÁÜ>’ó‹íñþÒ>@>äü)ð¡…¹Œ’²æÊ²Ã ÆŠ‰±–uÀCÈårÌëo-C£:vÞ÷ ñ<#4)à–e/&ë?XQaK’ÎÇdÞˬÕE6 /»àû“c Í®“9Äâ'j~ǾÀ¾‹XÍ&X$¤%[¶®cø‹(·ƒµÛƒNÓMÓÏàÎñë 0êFÑq@À”0–|!w¾ãv‡BY–Œ4:&†IÂ<éý©<è1õÝç \800kE‡€¢©mŒŽ³¨]?¾×[ÖžbøV‡ûTÖ ÇZ‚ÌŽÝMÐp¦@I†htÂpœ£¢BF$·ÛC®oÉᜧK;GóJIù$o)O•²ð‘„åýEã¼µÑÇö|¸PÇÀhC½Nü5I÷,ÃÔ si«'3 ´S’³cuŽµâ°™6­1•JÅu–‚»JàCT²¨ÁXxäŒJ!´%Éí“0râCC2 °ía± ƒSaš{Ã|(nyðCðÍûïàaý?`í­ Š#õˆ' ŒÌÝnXõVš6ñ;GÛ¾6Þ®tôxx…$‰oîñb†éÄs—øåsiÌúDuñXã:ɰƌzO'¿;᳋mX{,=¥BïKØ2’.GõÜŠÏY¶±q?\õ¢KÑ4gŒ×=èv‘þ ^_aÏ-šKãAÓDcõ3ÕI³Œ“dÝ¥:åŠEkYXÞþö·ãÒK/ÅUW]…7¼á ¸øâ‹qóÍ7ãAz÷¸Çá{¿÷{OtW&k¦Ç>’ÉžMY‘ ®Ñb¬+‚ÂV˺¨eqTœ¯Å2‹åsqüÜkUÉúp1§ã£×Ã!ó Æík݉t·)ÌÀÒâóaÆ{Jkâ=uŠ‚QÙ’Š…¸²NIY¥ç*´§´!2‰2±¾åIÊljø¹ï:ØáúFfªä> JføNÆIMΙ¸C5Z­S~Ž£ïoÅãÞúîVìPúºà} ñ ÛcÇòõÍ÷ÜÎà.Ì3ÙQ8g3ù¹‘³N#ï¾gùg'?Ù‹ÈÀ½[H6¸ò€íW2Æme¨t_ÔµI{Ît^we"æ‡tÝ¥°-°u¥fÜ%kÓøîŽßSÀ£6§GÔÝ!^Æÿ´VÒu¯Ñ5Ì‘ͶI¹9 ˜]‡POà IDATØ!Õ±$à@XÔGàƒ¶5.äna÷åRààù<6…0™` Ƥ§d÷¡ÞP ˆp% ؽAŠQ¹˜ÎbzuENÚ2²X­F¥ R¿„úª-¾›“2΋}öR>€h,dÃ>ÌÊ Q°h3v]7&3óqÌtü^/É{±ˆLbx0ñÉv#ƒ¨~æZŒxx™NÅ©a>¼ïRòÓ( }§*šÜ.+s¤&oÿžx¶%ÀC m c|xN‘PÐ~¯Þó’*þQbÌ`¨ ÛÛÖ¶¥­RQö~Óu@ôŽRpC<¤œþú$Ip:•Íá·‰%sNø }Ðkš(ãwÒ®X!|Oú=xT„²”…]áItn×Û†õÒv%nœ×ˆt´u´0†­ÝÆîîõèû[a¶¯q ‹þ¸{vœa%´¦M߃h|øwTÅìµ$§O®ïQÌ¡xhÌ.>ï(b,`ûãèp NýÜ›ÑõwºûSx1¢b›ÂÒÜ÷ßãê§õxô{ßÛBjdÿÒ}]|_ËJ廾ë»àá¿_vÙexÓ›Þ„K.¹äÛ:ÿÇôØÏBó°#t¸²š’™¡ÇI}âíæŠÏKŠ N±áåѹÐÛßoa žòð·в„¨Cq˶•õ9@ªïAÙsKÆÛ*©‹z­ 2Å+>üèþµ€‡Æ"vª‘ÿ§ý¡0õFówŸ$¾ŠÁØv}Û Š¾5ƒòê•$æYïU/&žMÄkZ/$¬’\Â/u ž±QéO{×&I-8 °4ðÃ_Ÿ+K=›­|¨ý›#¢™ÿìüh§–àQ 62©<ëvHÐëvƽÎÞ¡ù<ÔPáøœc+ªÓ×å¾ÖúïR¿’µ>€ÞM>HªEbRF"{«ÓD¯[ÆŸ{ÐsŒ¤ôCÙoÊXsÏín×%Óë:Ä{$ç˜È±Ød½˜Oɽ²ÇÑ÷¬`w†ðX…ÁêâìZðÃKÚ•B9EÑXU”5ìÁ‹alÀÚ»a;𴆀˜åúÛ%:Lãexìû㨘'‹²t†ÕZ–-~ðƒñîw¿¿ú«¿Š?û³?Ã#ñ8p¯zÕ«ðŽw¼c z¬åž/j|wF±½?‚r#.¶Ç°£ö˜Zg)䥚µA”Y­­e¡í»ŒžqíÚìΔ GôFÔIÊÈåЈéÆW”}Ž—J‘ÉrUð#s-W<¦æ¯HË„±Àøl ?1”ƒ¤0ÄcÛfí1C ÔO¼lb@2üGcÇ´qÿ3Ñ5O"®ÈÉ4^zM#`T8™W!åbæV ø!&€—YFˆ *@ ñ¤Šw *#ìÔ¢W¨@tä10a=š!øã†?–1øskô3ÙÒ3¥ê“ëWØI¾MªÖôÁSoÓmb5à˜6ö´mâÕïἸOòŽcñq÷’`·Æ×YÚžœoÓ:¿w`r$É2(¢K‘5'a8Á‰Åú–²ZϘ ÇðÃÓ5S×}á}‹€Byš o¡ï”ÿlÕ DÀá¼ü€x(ŸAc°»ÿ<Ì}õxÈI¬ïó:¼å¤“ýÑŵ×^‹ßþí߯Ãö°èØOÿôO㢋.:A=[½¬A}"²ç#_Þý#+Aå¬÷‚‚Yé¹)¢ÒZÞTž³H¢³ð‘´/yèIÿ$¶âB&@ðá_tÀ×.ú–•s2YS·!x>#Ï¥²¼7¥8ú)P«kŒ`5Ìa àQ0$Ú9N7Þ[èHçŠA!2›ä:Ú„â·Ñƒwâ zƒMsªKx×ÝŽ¾»gs± :ïT\£ï;?´€!<üp'ÞYžACÐxˆV~$ÀÇX/ Wãý-öU›¯I:Ä×›š°&¹Ü2€- ’âÈÌõɵ F°Ø†l„% DIÔŒˆ/ †ŸIÙZvGá>jR±Jkžïgnj[éN»R?£ü#’Ã#“o&— 4m'x`X>8ƒS’øÙϺ ð0›ØÜx vv¾"‚´,`ArD!Ï|\F¬µØÑ“?ܹ•à[IHˆ¥t®¤Ë쾦ð¹Aõ:§Æ¢áVâÎn\·}`kÊùDòâž¿×f€äk9¡ò‡<yÈCÄcßó=ߣž÷¥/} }èCWÕ­•ËôXK,x,"$ݘ ´.ì(*qJ»ðQôL©•¶U~0þG0€$„´#ø1,VÞ §¨D-áùI”m˲¢ âúçŸÇ@eh¹ j@fDÊI)ÅE ±ìð}ÄÀC‹_¯5Æk€ŸP `z;Œ ×m 5£‡­”‡qÓÂ4§ã‹Oú7¸àê;ÑÜq¥£D/XÅs“Æìˆe“9*c*ñ¢«ýÀf:&8XòÄJ8ÝÖw>ü»?á]`}ŸxBWrçHa-¥º¥{h¹!gGOeŽå1V0uNÞYÚí„) / ð åÊ¡4eÀ_œÂ°ÃÕ™gw,¶ÛÉX‡Êöðý)«bèH¢ÎÍ,üª¼KN4gHð᜺ħà1^Ç&¶îó|üÕs7qñ;¾âúÑÓífSÀ"a„÷PK¥| )QÙ ³*ÉK¥„¬L;hõ÷|rˆÝDÀCš—ÃòÖ÷hž^!ߎò ¿ð øÓ?ýÓÝÙ²=ö‘Ô²=òJ¼²LPŒ&yIgxéª 1þó’ED q‰r{°¤¤øšpž>Â.fÜÆ]ÜØ&õQd’ÐÚ :÷œÈÌ y+IÕ“-2…™ŸWñÌ‹¬•¸Lm²Rð˜;3Æà¤-C},œòkÌVè›æiä}ã®}HU ïnÆÃÿò#C±ãá”|BSnD²‚l]ár,´9 •ý¡† a C!,æQÀ‚€YàdNð×6Ô3…á—^Ü&Gáq{Zc’ ‹¥{ëÏ"Ë£ÀI¯+Þ]&í§gøùEý³y–1õÀG¾vå Úö;ðÅ‹ÏÆ/û]XR¾6L´X®ö™fÃ[Òu,›sD:®Òý¬pøi€‡Èt6ÍBž ŽÒßDÖ›£ï%–‡ÐEa×dŒŽeŒrcW$ëð–…åóŸÿ<^þò—ã+_ù ºŽÚy_ûÚ×N`Ï—5è±ÏdR˜‹U&Iò[*:ËcY¹x½Uõç!s™iÑ£WRÚhˆK|@Mn õ€*ÆÜcdïMT‚%<)˜ÆÎH)ûcˆKT”ï°11¤¼ –Â^ñíi‡ÄÄu©ñ”Üë)1íԠΖ‹Á¦(ÇFÆ8wâ 9 oÐѱ£3 ´~8e}H„j;Xs¢-fN<ÿT*á•ìW=‹,Û¦eΆ à#üÐÄÀíƒ:ðáÄo¹;*ÕøÈ‰ÆòXfž ^7”íiµºJÀvà1'}O­r#(Ç$Ë€i“ó¯”Â{èX6éoá˜tO—x,ÅI0° §V0et@˜ŒOxÐP-ºËU’“',°î©|c‰q)U‘É¥ëîI×Ý‚ýg¿‹¾¿Óµawª*Ñõ‹cšf|¦}ßãu¯{ÝÂõŸHYƒk‰d01g+ÐºŠ—À™våmÙnËÏѪÀ’ç£ Fæ˜8‘* ÔÈ[³Kw¢0ð6jêPs ÀÏ“ q-BMŸb¾0vGE±E…„s“¹fР ¹>%ÿ€Ä¸}!a.Ûãmèa(à!vÆ jpÆ,®`Ï gYf(ž2$yúÂø%ì™ü ÀÂê ` ý ÛÚÒkcóW2¶k™“ÃbÒ÷@<ª(k€¼fÜ(,­±ƒAóÙøùE¡â±'æÇ¤|L:²çå®{€‡ÆòV¿†F mKîctölðãßl ßé €›ç€ÂÝç‰×ΰ==@Zxÿ4§•ñÛ+Óõ¹Go€´ö^)mÒC|Þ_¯Ø5ft§eÖבQ Ö§Rû±>DÏWY$B*»#óîNrõ©xå+_)û—ù—…ê>Ѳ×ÜZN©Kþ”Q" a-Kay$J›n˜È íú¤ÜX—*dÓ¬NYSûÉîsBãCÒXñÁ+ÏŒBÿõcG(âôžu“?;iˆŸOÂò’{;²b¥ÔIÜ0ÉÆ»ºzœá^眲€7ÉǘfÌ]`ü–®¤?þ£I¥‘F?á¼(´QýÔƒå?v{PŒû¸]Ö½R?n·C]t\&Å'—îÉLàb‰€G¨’¾_@ü®±6£1=Œñdœ“ñ :žEÚ=â:†ß“cJ®Ž¨¤ž¸Žtì‹ïÁp à‘´#ÜËèžeEX¾žåáúÚÄ÷BÊGR’bØí`¼~%ï?>¬iÚGí]›( sbQÀ£æ=T¤j‘Ö†ðiÆwÐïX5€^2àÑ8ÀÃl¢iï…¦½šæ^hÌ)hÌA2Îgƒä9Åk·Ÿ‹wÆ2‡¯ù‘áÙv›| €ùMŒs¦9Ms/wßü)¶K>Q•â±QIt˜á~ðßâq=œÏêçÇø»0¥Îñ7]÷åóMq}Œ ó¶¼ïIX6Ã'Ò—|±$^ÐòÊï£n™²I¹vï?ߎòøÇ?Ÿþô§ñÃ?üÃxÏ{Þ{Ýë^‡?ú£?ÂÕW_Ë.»lá¶(»ƒËSžò”…ë?‘²=ö©d'a1tD7èoK45>f€Y™v,‚tËÏ ö4Ä Œ|Ø~X܇D¦^Y ÊQ˜ˆn ?;AªXmË€Z€{ôŠI¹‘·Áä…ÆzÔܬíðSR6oعsšè“©¾O‰7‘õ "á9Þ½…Äï>A(ÏÃs”lÞ„EÑ¢iNó‚Ðr °RN´”ØødÃÖÑAf\¢ø 4þ¡†E Ã·Oês¢}ñ=ȼl¸ËÀ#ËìÑdUw x^ vAlË;ÔüDɱ%: `ê%¹d€x,ŠàV àáŽ5æ 6Úûacã;Ñ6÷qsÜš-…ÙÇæßh=·±ÃA2Ú¹C‚Ö“|DÀƒ€Ñ fà½ipËã^N@-_DŸù@(âþ$ Œ°åêAø-w“éo žÄ¿¹:ê}Üa‘sÐq,õß³v:è3AÚ17ß‚>Õ‰8 ®‚‰¼üºWàDÈÉMÿõøÂOÿ8þáç^¼§íî•looã]ïz:„×½îuøÐ‡>°Öâ+_ù žð„'à´ÓNÃ9眳p[Ï|æ3ñæ7¿Ydu¼êU¯Z¸þ)ëð–},*C"ü¯#ü5™êÕv–L!¯©{öv{šwn(|4–Ó‡¬„x^BÏŒè‹ wÙîK”Qžïƒ O) ÖÓÃû°µžû½êk¢¿Nyi1ætpôqƒvèSúò õѰ€è{t1õÜ-þ@n÷ ð Ç5FÎȆxnx:òÛpïµ6ùØ–ÛåÛ–ê$í…¹èhW¢±Xì8±š««1±±ñôýèû; exO€ÃêIKrÄä:ÂBÿ‡rü]Sóc(!+I¸ Æ÷>Ó€ú^Ož»ÄP–èˆËfhï¼ <´ùW›Ó§<ó%é>14OžZyN"ÏQkR »ZÆn*‹ˆ¼N*ìŽÚp).­¿òÚ¥d´ãA¶ÃEfj»³6ï¹°–hÚ3`6ÎužôwVõ5¿ÖÑïL„ÄãÅ|@Xe¸¾'±Â†pZs_~ø7qø“Çaûã)pÁûÆ™ƒ¾ã¤‰t žïC®uúãå<‚nK~×È‹æ¯+åMŠwìÆ2ÜhC?Â}¡nö~Ú!ì ×›Óh¨Ñ˜§Ëëp'Æ—~ö³Ÿƒ³ŸýtGâ3?òÌÒ‡UJ×uøÄ'>¸ûî»ñš×¼ÏxÆ3pë­·ÂZ‹ûÜç>€C‡-ÜÖ-·Ü‚·¿ýíxÅ+^±p]'›¬AµŒ¢ùximQ¨hcd¶’È=ÿ«:ˆ’—ä˜|Ðü£äÀC(Ú &c°¦F7@¥ûë”1ãRj@û&r‹#1v|. ËszL‘P›²¢ÑbŸ;’XÚ2ãdü.=¥÷G2ÀcYBóPü If½·(‰‡Ž€ghll>þâ‹ðŒwÞ{üSalÕ¼[îÙˆâ“{rlú¸à ½nZF“*Ð5÷nûïJh‰ª<3ðC>\=ËQBÅÝWjÞ ©Ûï ×’_/Ó-`—P¬ ðàì·¹uø°a/×Ì)¹’ígI{r>‰éxÖÛÈDÛA·{úæBÿ´gæ ïÅÞO×'_Pc,p°#ÖÛF½‚Ö)Ìw§õödžðZY²»›Lç¸o%‡[îÞr0%×Ǽž[L¢?a,×¹üÆöp[ËSPV5‰“¿ÓÝò<[C¹ƒNBwó,8·}]ËòäÚk¯Å«_ýjœwÞy¸òÊ+ñ˜Ç<ðÑ~ÖZ\ýõ8÷Üsqýõ×/ÜÖ“žô$üøÿ¸xl z¬åž%Ú"\;’°–½<¼LÝâï„3<¦9’G8¿XÏ>0ü¯±>B÷……KŒ¡GÃë6-c 8àÃIÆ›íiü£ÀzÀÃFípß²^q±nâ•Ï2³CŒ×$k¬ÍŸ~œT;±ÌöüàcT‘h"ðñ=Úݾf¨¯&EÏv) ÿÇ´]®´MÞTëJ6)kmãÇ gwpËp#B‹¦ôeøFmy·"ôóÂÀ¿Š÷` ±$)±<âÂS@*ǰƒŸs+Ú¾G§ÈÏ9Sžÿ”ù±vÜ?œ‰AûX¦vÓþИÏø Âï53hkd*àƒÃÊ¡L‚q […Õ1Ü ýžóh&9¾ú=T…{U ×8E$°ÿ6æ÷ˆ(G g§ ƒ5Û€ÍÅñwùqŒ 6Å=TCÛ¢ÒÆ€eÌ!5áI…|*Ñ{N~“þâñ±œèñáúìp½Iè׊l÷"»£"7‡4–’º)€s"$É < ™+b‘Á–½¼®RXŠ €Í`‚Ù5ëäÔµ´ß ejб˜¥P9¬€Ô`´¬Ù‰Ö¾p´ÆÍ‹FÀ>ÇxÈ%ô÷†ïÁæÑŸOzƒÍqÃõfˆNž{çÝ9g­³Lñ• ìŽÚëÈ€:à¡Ês±Þ™Ôç×7¾&eÌ%1çÆ¨ßѱ`yÓV-ߦá-gŸ}6>üá«Çßð†7àÚk¯ÅsŸûÜ…ÛzÁ ^€_ûµ_ÃÓžö4<ð ¿[kñÆ7¾ÏyÎsnãDÉôد2츲4ýfÇÆúÈ‚[C‹&Æ;õÞNöðOq÷‚½E€¿ààCNn:;œõá˺do žÝðÉÆx¼o^|6qŸ(0·¥[“ô¿ øà”nv£€ák 씚hn©}7„k”€•åQ~C@>ÄÓo¨o§A•hŒ¥Æ~ô›áI>= ÒÇ÷aIlø‚ ìŽÒéBn©þšñ“°>€qŒÚÔãé„düsƒ¸ÈrÊåC }ˆûª0[†w7êç2æõ%))µ_9…–{!Õ ¸ Se. 2×–zC–$œ¹1ŒXG°•ªØ×F;@~€ÊÇkdn“¡Ø÷$7.³»±ù‡‚<¡hÚgÿ^ :I<ÍZn§"³aÆ=“Ãd¤ç\ñŽÌyrkmðÞ?ÅIAŸwÍ}R½=5zÏ݉íž*O|âñÄ'>q)u]xá…8vì^ûÚ×F¬km–rO5è±/„ ¶Á`à‘V^`‡dDT‚WÍð(,F{&s€ ñ´SfE´{Fog}ðxͨ[Qµ˜6;Š¢DPïB´ð·hÌAspø¾@Œ¨n¯P ïÿxbƒ8 !Œ·êÄÀ@1£Î/5š ã½VqËÁ ð1U¤ð‹˜Hª~[b„kqž©â=®/ s¡lÏÞ‰Àž2EwpZ;Ô±$^;©“®.!Õ‘Ðú‡ñn騰]ì™ ï½þÓÀŽ c—×úAûL®õD°?¶“~é5NÞ²râ:6²Òð¡–U¡?UØù:cB9] %˜Ñ ¡ðˆ˜IÒÖ³TvGUаæä¸6j»—$×^3 Œ$ù ›O’9ŒŒWÑA¢* ø‘ô)Ó^)QóP?os)!‹¶cÀokb}¹ù:),¬*àQî_çc`+uVÝÎÄ[“SÀC> a^õN®µœÌrþùçãþà¢ä©€Kdz饗ž ^-GÖ Ç~ |(€€ xd›™¨¤j,i± J0 kXT$p2Mw¢äh…&ÀX¯‹”³ÃQqûAùC–õ‘öKbwä¯)ÞâŒ>Îz32Ö5 -ñ,Ñ$­ñµòû ×™yU¿’éá—,ð‘»Þ’á 0TVKÔ²=’²ýœ…dšÚ8òYˆ÷¦eÿ€gœ€-æ$Ù™ÄþÈQ„éWnÔyMâã HbÇ¢ö…P‚HÄq:†“…ÄÂÌ€‹Þ'Ex(‹vä<ã²!ÄË`©a‹“"vEÏ3£§/dxÙxÞT…ÝØQÊ£’À¹6+DcBFk€Â8 ‡?Œ0&9àÞ¿fSüµyÃÏŸGA@—ËåRCãöà Uz£Šg5ɇ0øà±½Téͱ=|q è X-ß Ïa¡]ëx}á‡`¼ûû»4FÀ”gS<òçê÷tJ†tÝÑØäçsƒ×‡wQ£.ªëÿgïÝ£m)ª»Ñ_õÚ{Ÿà#>@4É ‚ÁÇUA|EÄÄøi¼‰$: Ñ(¢aèPGbŸ_4^G¢^ xsIbD êwæ3FcÄO®øFñ‘ˆ(>y Î9ûìµW×ý£»ªfÍš³ªz­µ÷9¸×£ÇÞ«»ºªº»sþê7géìnd¼ÈJ¯´£ŒúpÜG†ÛM0Zrà`-Ëcˆä¾%¯[¦^ XX[|)(­6–X“øÁ¥†x÷ðà.-Æí$EÛír(/Ãú“Üù¼ 1økÚ…hÄÁ𥱖þ€Z|þœ IDAT¨ƒärGMxÜízn<ÓÛpÚ†ÔüjÇnÖ.Sw—ùK øA%<#»(2ËüDò§ Êè’T·þÿkný®¹ísXoÝ3oò–µ¸{»_ rî¹çbß¾}عs§?wÓM7áÈ#Ĺçž{k6»,à¸- /8á‰øŸ,6ö÷³¶Ø•«Rs’Ü zšøîÛ„­ ž¤ LºšÀ8¥«?,&}p5ö?ñ=vÿë´ÖP~60,¬?7!Ï0Fk÷âG÷;Ö®uÚ‚’D 2§ì†¿ÆjNÉÅÈ+¾±ò+î^)eè†Ó&Ô…­Š‡Ê+ C”¦ne¨(U«e›Ñ§Sú-Ö·íx+[ÿî#£;üŽVuU¬ÀòªœB)»ƒ!éÓü}Ì Þ²û£ÂþãÓ4Ñá¯;£‘ôÓH}†Ý÷IÓŒºë¾¯ÄÆFÎðû’7:çvdò¨‰S5OÀ]dQЃ§óãT7™f99èw”ÊÓµ¼.5€¦R÷<‘¼@<¢q¼ÙÓì ÿûsýÿ®ý»¹¥È´Éaí¸›[2Ï‚Ì{Òt\¤×ÄëI¥¦<´ôÙúòºO9èA<¹Ð±t†¾¦Ü[Uÿ –çTbbý%œgí´I7FÏĪ îXZÛ¢ùGsƒ«K󨣆—œt!^ðÓçM_¯…Tùë¿þkìÚµ ù—éÏíØ±¯ýëñ…/|á Ölv¹ûZŒ ™J4%¼vti*hä †pãp%Iœ#%lÍ{@•¡ÊB5ðaÃvy>‚<@8øAüÈ`ñõtƒ¸NAº:L&7c×çþ,Æp{ËG, Q>ã t幉•d¢øG€GFÙ×@Xñn@ ¸œ¨«Y’b3P²«½CóËø€gfßV¸èåKàG$žÂî½øŠß(N?Pj]œºJÎ $RÝfê j $¡ï³*N ¿3¯\Ó•ôeÒþБGÑáŒJ V¡}"$É3aXPšKÕ8„Ù0Ž¥å£ÑŒ@0R#*¿ÜÈþ™îÛº±Ø1Þr®þ}ŒÐ4;1íÆh´Ms˜o‹á]жstÛY¾(àæJ?gÒ9–ÔÀäG(+‚NË" XacJ®¾T±‘^™gSÖ鋺^1µÏíòߌ…½²è} AÓìÄòò‰øÆ/¿MsXvÞ“@ùܧ4¿%`ö7?› sóBî~òÿñ¸üòËqÎ9çøsG}4þðÿÿðÿpk6»,Ü[¶ª0À#aÔ&‚%¼æz­è‘"JmáùæP—ÍÁç¼äê "ú¶Í^ÕŸ§B‡‰«ÙÕÕCê×òŒþ~EøäÎãØ|½zý`e=]‰ÒãŒz—£à’•Æ£¨¨ËCH‰;Wé¾¹/#êKA!îfÒÆ©xÊ-úvÓ÷y¹5ôß$ëVQã6¤‰äN4ävB9Oòˆ —FR@¦ 'xÀ‹ŒeSêO¾m´~ p}ö062®+Z ‹©¤Ò•J×J€G³ ÓlÃh´ ËÛŽÇúø‡ø.ÚÉ]¨Îg26ážâj;Eº4pטíÝœi–»Ú¡Ï7W—6wsÁH9p0$q$ s/­S‘IæÝÞš?m,Â{Öê öåJ]P‹±¡¹µ Ž#ÇëË݃0ÝX4›e‹¦nñØíXZùÜvï;Ñ4G ¬fÊ·¯ObNUÄÿàõÊi L»‰î-›XÖOª´m‹ßøß¯­¯¯ormæ+ Ðc!L†³;üd¨ùa³ëѹœÐIxˆq04=¿½´ÂVaðnˆh ¹|øaš0©=Øiôˆ€]¤wÍ€>ZxRމß*öcÖwX‰c“àÃ?³Â<˜)p÷õö c·ð2$ƒZêó­LkÈL!ޱ33ý¶Pgƒ¦Û¡$'DºÏŒ¢<îJ Ú áþÛ´ÿW²oŠ¢¶ ‰NàáXt¥¼®<—Îam×]=ðAj“¤xï1‰“°˜Â=Q`]–ÛSzv«ÁÍÒ.,/=ÿÝgáŒÿþG°íÊûÙ„EÁ ®iŠàŒcytïxŒÖ®Âذã0¯ô ÑîqÇìÿFLäç®Ø0à]Î|$uRê'uŒbQðûœF÷T£aþžà!Ý“ÄëqõÜHÉæ¯\s®(hÛ=8°ÿáQ—]‹ÖŽC’Bðì$ËÒbø¦m`{È€ÇBîžò­o}K<¿ººŠë¯¿~“k3_Y€[Al bžÛe°aHò$À"½>TJ ~ÁHɱ=æâ3£ ZQ#Š…¤ ÅA¹Rð@ V8æ™L= Ñ+9VÈ«^8ð <˜R%I?éªÔJa§–(f–¸¬Ù‚¯‹ë«I¼’1Öå9ŠVE!mµ»•Àü¯yê5 -MŽv˜£²\äó]_ ZÖ†&ñN%Ó‚’%àƒä­­¤©Rr¤:»{l+ïB¤}çVÝÃ*ärÂðH_NÍ7wýyØ5À,:©HWÊKx_UBݵÑW£¦U€(9ñ[˜·«X]½þ³O mWCÞ›,¢>À®É7æL(ÍåZ´hÛ=¡Ïö€F÷;ôË0¶SÀc x”µìŠidÈXÌËT]ê„1‰é@}T·s*ûôæµQ1Pì”àTNø»4?& *c´ô›)ÀÄ\D;Èyð8øºõB¦“'<á 8ãŒ3pá…âg~æg_úÒ—ðñ8ÿüóríf“è±^f<¨PÀaŠU:m‚­ÉgàcZ™ËcÖˆáÚ*Pnzk[Ïüˆ\N\~lÛÑ4/:™ +]BÙ!Óƒ'ðh£V¢€tTIÞ‘¿N˜Qž@`~D•ëW˜©?j¿Xä}FÛÄ”¢Ÿ ø«¶Ë9‚ƒb_Óòš—Ë™—ü=Å(A鮉i`ͤ÷}ÛêZ Be{eà#ªË€ÒxB€_ ÿ-k”wvº¸Ur·†Åå˜Î‡Û¹£+eàî[ƒl¿«¿«©Çeí=°ëÙF*™[R_M¶Qž†áèë²Û®bâc5aÛq÷M…qnÈ0réùÿrb¥ ño}xÖ{–’;é‡ù¸¿úñI‰EÅ™ÙUî_É5¤&omñCÈCŠÍ¤«¾b²ñ?ŒmD²ãìØb?)±K¼§ª *àα¶VLÜ>UPÇHðQ8–‡k÷‚NWÌw€Z<|:ÊžAY·š§lfY?¡ò¼ç=7ß|3žÿüçc<îÚØÒÒ.½ôR\pÁ¹v³ÉôØ*"*Ò@€!J£4`óIm¨’äó’¾háÀG‘å±+bóÜ-|ÄeÅÌ¿H]]8u–lY—æ#ÔÅ·­´ì|„2#7'¬íˆ+1}:ÎîðFQÿx²BïójÐ4‡á[g_‚?òf¬One¯žð(æT‰ö´ÏHέ¥¦LÏCÌlWaë"9?Iût¹ÕßY©Ez ¯sw¡2·Ùb®>H=è3ð ‡E`ã%€;€~L)I žä}•æã •tþQwËÖ7’g…[Ë!+v`p¾K/½çŸ>®¾új´m‹ÓO?÷¾÷½7¨v›' Ðc ‰¾9Ý€®)'Ú¤Vš«•Ó¸Ð}¡¥s*Ð1„Æ8Ãc£ö‚ÏúýF€¿Q¡Úóø ðQY/vO¤x¥M°úgýéáY3JÝ\ˆ{Lê³=Á‰ÿô—°ö@ºZ/=+M"0¥‚¦.ºµ°ëƒ)àBº¬ß2“YV-§nÏ~µŠ>;탠gÒðw¾zâs°ý[ï±k°{zºcˆlŒh%=Ò"®ÄKFªâBswŒ\ϘÙoã)±› É—éZ´Ÿvîsý_°ã~\PÚ_·ÈÌa³>¼2Þ$ì÷›Ž¯tUº­êÈI@Ý)ؼ¼h'Î8ãm=©Øô.5ƒî“Ú coˆ·Iuf <Ú~åzh.iëXØ‘e,áp”Þ×Ež3’瑘\Ü؃ ò8¯ÁÐõWŽ4W|ÇÒ‰؇JnÎB:ñ²¶ˆBï-0[¦aF¥jÁ¾R{ ãOÌžå®M%÷›Š9+<Òk]=&õ^ÈfJÛÿ»wïŽvpùI»Ÿf¶áâüRûÿ`V–‡<@7ä@ØÊŽSLµ<縒]퇽I2Ó¶~>“º÷ ™øõÝw†Mè¹ö¡]ã[êÆ‰Ïô,þÏTÁåÌŒÞPsç¢y;Ædr;Úv¯x=ªªß²°û?g€iÛPúS™öinŸ;T†Ü/•W¼¯ºêà‹ t „¤FÆÄŸ7Í.üÇϯ¡iî¡+ã3‚‚¨)äÒ÷R~Ž´¯ ^Ní˜0ÀP ±<Ü{£`GãÓnëÚøík±Òf‡ÿý5ø-o—û2\~#Ðím÷kbžÖošç­ˆ{}sð0&v¥èÜ}èùºƒnËë«_. 7Dd€ŸmåÍç¥!sŒ»…¹¯ˆßÇå+ä×êM|4¥-iÛâí¸“xkÙqðßãè·{Ò¿rF½âÖáêOç9¸ß›ø.I=²i†ŠÀò(ë„©§n«ËÙÜ»;®O¬ñ¾%¬þUnÖÀlâ!½ºûË÷¿ÿ}<ûÙÏÆêª¾SÏFË-·Ür·A¾5¸ —<à1/4–7¥üèþ‘•*³ú‹–Vìä 2¬‚9HØÁ•Bé*s ¿K«[±â˜™„{Ñ)áry¹ƒ×%)‹”ŽOÎE†#WŠõkÃ=ýÁuÑÀ–”ýÍæfm[*-)ððŠ:;úsÑ··´íðóÿã=h'·ö~ö)c$*²Øæà#ÉHx~i¥k³þݦ#ÜíÊw5T)vj³;zðÃ4Û£¿ÙÞ_ëAb”zÀÄ}M|HAE’”¦Y‘%`~8Øn$EÜen¼›Æ,õ@O%ˆ3è[7ÁåƒÕÍ]O²—ÀR;„²Ò…€ìè~€£Ëé3òð‘Jë vÄçÇ>еc?ñy(ÇÈË"¨:Eß­ž'# ¹ j¸táÇ@†‹ ~©Yü!À‡¤WÌÂò$|áhV79Äß|ˆ^Ôƒ?É·Ó_'7æö‡Yö}¨–¥²—cŽ9yÌcpæ™gâßøÆ¦—ÿéOyÌcpÑEmzÙó”EËÝ*²I€G¼ê®%]någVjløPu‚Ÿ 1sÝè;j3Ô"%j R!S¯¥2˜à/L ¯þç‰õ¸KW0â<ôoBWɸY|ÞyZšÔ €Pʼn*p#€¾ÿ¾|îoÃ|CÆúñ`Ȥ¿¶k÷£µ{ÑÚ½°íÞ~%ÖµºÂ;œ€ È#Ž)CBûSï!u“ÓÌ0–ˆ«ãMô C:—60=:#ž0=¢ƒ!”Ò°#<ü_ ~äØ7S>³”¯ÈîÈßÑJ)¤yðÌw4ËÃØ €@¯«ù‰À‡;ôüf÷+y>Œë‘°h¤÷ëvPCMfé±ËJ<§Ùvìø•€²Yƒ7 ׿#-ŸAÆ6c|pÀC¾G6Œ§u%Ë2;%‘ÚG20 éÉ~·úN–ñ$|çNÒñf&©a¢*€G×o:¤ÍÊ:]Èüä /ÄsŸû\œvÚiøã?þãMa}Üzë­xÑ‹^„§<å)xýë_'=éI^æFÊ"¦Ç– ø¶¥áo'­pgº¥§tsM0Zlƒ(=ßcäøI^µc&xWIT°cƒ¤è Mª5+.}“˜!>ý™tß^vOï7ÅÛ‚\öuöu©Ý!ÁÕÃÆ[ß&ñ;r+)ƒ)¶]›Â{ÈÆžñõÈ\£iœ¯UTÄ « €……Íô»êXݱcvØWUŸâN9†x@Ó èXP=|/=Ã̘â®'•më@ ÒF³×ùiâï?­T±<¼êbެô×WzÅy¥Ÿˆt[Ô:À½Õ‚5€3íâHfд[…Ÿã¼¥­ÀG “`„' )7HaW4@³LÆÑ1`&ýDS¬",î›MR_¿“•yÙ‰K'ĸ™Và‘–%ÜB Ns‹’+‹$Ø¥ßH±#„9ï €5} ì~KïÐÅöpñ½è,ü® ¶ƒOÛ×kæPlÌ÷SÙNUåIï­b^šRJ;puSïDÕê¶'Gp;Ù$Ù̲†\tÑE8餓ðë¿þëxÓ›Þ„¿øÅ8ï¼óp¿ûÝo®å\ýõ¸ì²ËðŽw¼÷½ï}qõÕWã”SN™kC Ç‘%™)2¾fs–G.½!>@r+5î$`š¤¤ùzz€d×C‘¨ 1`Ã"í(ø;Ѷ{zªó4"õ©&ºž ð›€rñr2ˆ–Zj´Ü(æ3’9[¦µ«xÈ».ïßÔ$ÿœÂwNýØÓoÙäwÔñß?¤8ðáÏõõÓÆ¡š>‚éÖ®TÖõi‰6­Ç¥÷%ÙIL–hµ?fêP–G³ƒÛ£s‰%c&€]ëÎõî´ïDwv iÝ ¾ŠÎ )˜GÍO2‰UÑÐ$L.mÓƒë=›`Àª²x$ñO¢gl À X•dr¯§Ï_HÎØ0®¾ìÐbe¸ßòó§1"À#çÆ0O­RhùƒêRËÖ(õ6O”9#íú- Nêþ7fŒYFkí*,ÆÕ`E•^8§ ¼Å:TÌÉÉx-²$ y¹j£âGå¡AXs˜@<²ãâÇè…ÄrÖYgák_û^ñŠWàÕ¯~5^óš×àÌ3ÏÄÙgŸ3Ï<§vvîÜ9(ÏÛo¿_øÂðÑ~ÿøÿˆ/~ñ‹X^^Æ%—\‚W¾ò•ؾ}û=ÍæÊôØb£ÕÆÂê¸0xUûÚœíDF¬ÓWLVð#1.ûó.¿ÁtÑ à1U¼’i'jQñTV*k,OqK·)˜,‘RµNñe,·‹Ü{*K£ûb´t Æk_ä½Ó_OwgáíF‰#!®”\±áJÛ´,ŽšoBûÉ/‰\Ï ÿ•aØ­ð%|„T‘ÒE쳬¿kÐí„=Ò">ºtƒV¶RaW¡)ŒÇŠüç&dµ`´w)h`й³ <Úÿõ L³^1îÙÛ:†F05Ý7ŒØãP¯!ïÔ}ƒ"˜ÛÀ‹Pq»ÈÊúåÚ£ê~ÂÝhx½s`ŒRÎTlJÀCÛ…8¯¹4IíUÔ'œdÀ10hô›2ëæºÁ»½p—­.®>Ù²7¬©d{HÀÐ}+ÓìĶ{]„öŽÿëão£5ZËä*Í{ƒÇÜ‚O®ÙÝ`jÊ)J ÒË×]þ•J²ØÅÆŒDâ ûð22_9ꨣðö·¿/}éKñ¦7½ ó7ƒO|â0ÝD‰ãŽ;?õS?…ûÜç>¸×½î…;wbûöíhÛ«««Ø¿?n¾ùf|ï{ßÃ7¿ùM|ÿûßXkqÔQGá’K.ÁE]„ãŽ;î`>æÜezl™~äÑÏÝßÔ_¶I€êI¬à›LÄÕi ü()j ›¡(:®*‡ˆa”Ý”ó>üIñ×i»”ê[)KÛNÆx×)°?ø’w‰˜u•Bf4ÉL@ãætÔ¬ i!Í/]’ Ã$è¥oεÁI|ðº¸öÀ¾±/[yþè»™&aðÕîNØ7Èö™§«Ñ K¥Ô7r}½Ñq‹Äï n-^IîÏù¶ÒfGFlgРÝصžéàâ¶ô†ÐßRб«[W—ä©dlÓsM xäÀz¿¥õTÚ‰šÙÿ´cõøª¸VçØ­#!ýO݆Œo5ÆcÓ¯;¯¼G 8¥n+9·xp‰v)Ø 0Ë2ciÉU&:Ç]w›h÷©>Iùs:´g€Ÿz7W·Ž©µcçðÿ=q¿ð÷7û@²S»TÑrø|:EÛ-žçÉJº]¡|µW‰>ˆ 6&èŒÐøPÓø²ôÅÉ…{ËÆÊI'„·½ímxÃÞ€}èCøÀ>€O~ò“¸é¦›pÓM7ùt :`ƒËOÿôOãq{~ù—guVVV6¥þ›- ÐcKHŠÔN+t(|ÈR^íÖÜS²÷°g‹ Ë ˜þœt_Q +ïÒ*Üà@•C'ÍBži`ÍØ IÊØ!½‚ðò“UžŒÔÒh+Ó >HÝÖöXý û¾£tu41êÙ%£È+ôzzAG*¨å(ì(1¯Fô‚® ]m¬tM™EĘàcX \£;¹ô«…f¥€f..M ðH®Ul!©ÊÀ6_Õ.ëè·)íÂâò¬ïæ„ÊÆ¨0À³q,PEüfB,‹Ú¸EÓó]™Ï½ÑÃŒp`õœö÷ŸÃúäNØv_—Ž?7­rôÏÍÂð"…m>•õ£Þ…O”B=k2þ#ý.ƒ™Ô ™›vØaxæ3Ÿ‰g>ó™º-nÿýßÿßüæ7qÓM7aÏž=Ø¿?Œ1عs'îq{à„NÀð<øÁÆîÝ»òlŽ,@…Ì$餖¸A®ÍÉh”Èx”(Ãó šój ;Âï€GUûåÀe neóLúsLj3@–+ƒÇùà~Ó*Ûcà2HÜH7–\YC˵]ÎnýÈ1AV"@ÉŽ¶w GŸ¦{§#8Ãß—+¬m}_[Zº/>÷¬ÿ§^ñG}ãþ¾Ißþ€nñаò•沕¨Hjý*¹(%J"5Rðáê:xE``½$?ä…Pr%Tü€‰Fu#¨pçIvs)3Þ*LØZýÒ“º":m}x|ˆ û?>ÛP[ ª®Nònk²®¾±²˜‚ô>1P`5Ãc¸ðö™U| „äÚ’ž£cR ‚ÔÕY0\6@ŠSrƒfÊïTJ;lÇè°¦cæLºóè]Yе»t®û»ÿG/ýÇþסï!×¥J¤†cx¶­Ý‹ÓÞõaÝNŠß#Ç¢Š–wûÐÜ+FP<Ð1"»“Y©Né|šˆu µÇô|6>†’6–Ë#7®›¦>§£«§\7gïwü]%ó%™÷ä 2†¶ÖÛ¡ê›ÍW¦b ]ô°]ÜŽAñNŒ©ÞŠD·¸¨”ŸCªŽ*ÆE¾%éK¯gèEç’›´ ÑË@ÝÒj#d‹¹·,d˜,@…ÌMôÈÒ3NÚÜ ) âê,U€9ëfœ(åzæ&²²T@ꄊIKòW.ЙU%dÊØ¢¨ïv ò#ùgó|œ2(ø e~”N2ãcHðàü7¯hoÑóÒ|i¹ÂJ£½NO¡ßD1MѵE>ŒAô}\^³²=¦~ Åíi±%ä7„åÑõuǘ㠺€´i]~ø¡_ÇÍ­Á1Í2"÷3ŠÚž\^·ÊÝýwn-.6Höùò,*ú¬±ËVÊòHÜ\‘<Äçc˜Èß™_OŸ!Ô?-höö9`]x$—$W+¸qÐõ R؆5PŪЀ@q“¡s9c{äîÝHÀcê1¼Û¤˜☈y‘Ei±˜bh€~R xäâx…“ñ‚’´«M‚ ]ðÉ0¼h x¤[×r©ùžñœx(°~²I Ç–õ”9}’­ßÅe–ÀL©`NøIˆ`º}¤8ázMùI_kq÷U$ÖÀ´Šg¹Lq÷­,’õkVË'“º¦@V)ª¥Ôf(K’Ò‘ÆÌI̤HðªÏ—ß[Pœ ¡º[wo¸,º¸LÓ“vÓ±h}ŠF†*®>ußQrmY ’›‹˜aoTŒŸiüœçî/=ŒlQRv­ÿŽk€í¶°…]ì ÌÚ= IDATmxÒ÷Àdû.4ë?& Ç$î+B¯{¦Ä8öû'Cý=€gTˆÀ6‘*À#ºAêû}= ƲÄòȹ[Ö*–ƈéÁHyÞÙ©²;=¢‰~w IAJºÊ ¦k¯"ð‘»Çýæ ‰pŸÔ¦Äü¦‡‹m#—§ö<€<®°6ª2„s ‘¥ƒ§k › krÛT *Jû½Ê±ÐŠÛÂÖäþrl°B:Z¼ÿ~#pàc›ÎB²I²=Âd>ÊÏ´û‰ç¶šË‹BmÕÜ]Ü9‰MQ9)fa51Ü'ç€úæ8ÁÔ€™¸u(ÿ°IQS؆‚Õ+g‘o°^OyGê"!IÊðÐCDTƼ¢#+Nn›×€Pß×YÉ ¥+S`©.ÓÄÅÿK.¾€ÀîˆÊ€»¹t7 lwž>£ø|3™ø‰H+ÍÓ®ÊöŒ¹îû:ƒ:Ó±=Ö`íŒ]0‚ÿKë?îÓ¯v ¶Ý ? âÚ]±/½CcšÄ ‚Ï) à¡fƾkÿ’]5ÀÆ ‚ä\®4`¦&¦Ç ]+_ :>$?íÙ†Õ‰¯ŽUÔ½óÂFàÀÇL;äô ÅØ÷[¨gKÑ6ëä^ØÖ/À$ŒxÔ0†r2 3`ÂwûÊ~#8"ß×½/ò¾¥sÑí•»¡i2 àQÕˆS³HÈØ•Qpõ¨ÎyVUÌæ`,nTn6žãžŽgÆæûɼe3ËZÈÝO ÇB)¯¦ÑùèòÖÇ|÷[^š9%å ‰§1 à‘U°Éõzðcà$=o0†匦6T1âu‘DÚ>Úy!G ¯fal¶Ä ]©Ôïð;$ßùD•-BŒF?ð‘*M’âǵÐüp..`Ï!0<âò©› QÚ5àÃW¨`D ×g§É׃ùþ•ÍÂ. `›ð?í{v ƒýÝ'vM]›²ØžéaéJ¶Ô®µÕs_©ôZºÕt˜¿´x~<,±‡:_+}ÓåSu%”›êïh›]=FɵbùN ôLÝ ÝM¦Ä$ïh¹<ãü}ê€GN‰¡`À ÝFŘhWÄþIhÐ2£t©½aÞŸsuñy*®-©ñ#ÀC©ØCÆÓâÅÒ­®ƒ´èLzW’°k0X MíZïN8i&°Ö)ÐD ¶kÝ®+è·o­ókOá|üí‚›‹n”k€‡$Ü0­uAçT¥-UîZÀbulϽ_aî™F/H…é¶­>*Œè!ÁU»„½±œëw‘ëƒ |ð÷`\[t÷Ô w á×PÅËSõå:سÑ}zxø±€g}Ð"ý®,¯#ðï`¶ÇBr¨È¢Un1Ñc T_.Vâ¦4fZ1å+N#t'šp ›"ÃŒ]3KïõçQxFä¿¥{‰¾ËÇ]¢«€@æªÛó%îÝ9@›‚Ò+|#¿Õ!;º‹ ÛAÌ»’”ÔwˆáYSF 4iË>ÒgdíL|7&>ŒY†Ac–£ßƒ$ûý¤Õ4IâêÕ/ÕE¦7² ]c}Sxn@wŽˆþׯ¨2SL­.6ˆûQÞ+·è7ù®•uжø¤}ÒNº¸=£Ã¹šð®Áb ¶]…µ«€ÝݹΦK«¸«äXM™XÑwŒÆ½´‰€Ï_zW‚{6^åÁ‚¼NÑ#±>žöuˆ÷%uDôó—_—&9æ-bΨÑÐ?¤C±ImI-· ߨNô#óŒáb—¶ëmr¯þ lÞçzKÍ‘}Èáz[ü^§o+î]øwâêCßMÿ­QɹÚ6P?'•¶p°l ›¿jÞI´>Ëõÿ©.àÏû±pرҳõè1‚1+}Þ+Ñ}|­ ~ºõdïÞ½xÉK^‚N8Gy$žüä'ã«_ýêÁ®Ö–’Óc I³ äU©Ñçsfª8^™î-pwƒqéå» ðЄ­2ÉŒD×ižA bl†ÊË ÆGt¬J'þª)usÑù¶ÂƒÙÖøboŠkŒÄöËÍõEà Ep¿]úî¬iÝŒ`š€ µûLj˜Óû JRˆµ#Ÿ¼¾^R0+ÐlÑÇ#õƵÏ4sQ¡ î*¡5%mNdqm2ËöpéIÀ=`ì‡B™UBún×gßæ\ PÂúèØ4þ¡ºÆtÌþ\—©3L‚×½_Êš˜¡m)ÌuÎã—vi¡ï„š`:ÀC¶^•¶¯ªÊNÑYsY¿tE}¥†‰HeƱ^`7¹ ¦ ('Qâê´ ª[ä€q_d€D $–ä$)£ì×@íy‚Q¼=—\³ÀÀpRÁkÕ± H®/5Ìlš)ße)Ž×dñy€Ì3q0Ñqpi |Dn.=˜vKiü;Œ]é‘5Xß~!q+µX¨YéfÛ‘KÀÏK2s€‡ôŽ6L*€M"%»¼óR£¹—#˜f[Ÿí¾ŒQ®Õ7Œ{‰‹K…KWÿIÔÖ’À¥D õÛ•ÜÎb]ˆþŸ¾Û°{P=‹Bz·ƒ\S9ã´ úû©c¦Hí*š¢Åza8»K*¿+S? ÛÖVi™y|{Ù\:@Öutý‡.š)uQúMŽÊtCx °ÝàAÁ‚Ø Œqàt‹ úXM¹ â |éK_ƒü`\|ñÅ€_ù•_Á=ïyO\rÉ%øÈG>‚_ûµ_;È5Ü2Gxx!w+‘Œ¬Œ"2Ë´)  P¨¼¥üèµ”Ž[:h":1¡!šf9>Dê1}ŽÌïÚ÷Éè¹C$ÓÖ³ñBjE5â&˜LnÃãþîXŒ‹uKÞMVûÝü± úÍ¢oädö‡k×ÅÖ³ZÂ7Û¿ËÁàeàI …±¡ª ’ºóç¥Ï&=²JC6ß¥Ëø¥R'yQw›Ô€ñî-¶…żŒwn.í*Z» k÷wÖút}ç#0**V½ Ï“pj¿.’[Q¡Ïææù÷°2Dã•g|<4Ë-íÂÞG¿ M³h–ÁT9þw]¬ˆ’ož¸ÈŽaÛnÛbÛŽ#ÀCuoˆ2VUѺ;Ã,‹FüÞš¼j8ã” ¹“¸óÚ¸“T¢‰@(ï'¹´jàÇêKí«àXrk*–ÉŸe€;,±îKY›5A¼kåºô ›³é8 ½[‹qG³0;€ÞµÅ¥“™U“)ÞÅÁ“üãøà?¸¡eìÙ³¿ó;¿;ûì³?úÑ6´ì…Y€[QJ. JÒD´É‚ àòOý„Ëà‡xTùSe*Áb²ÜY°&;6 ðp#QÞàáêíÁHT³ç°@&¾‡ÿüøá¯Áh´»7l%##(_rå†®Ä †±?–ÁB)þ‡h@¹ŸC lÄrQAC!i¦>ɪ(‹!Å+8°ÿ_!m”Êd •¬”åú‚rÐï‘9ôÊÅ F ~дi|XQlȸ԰ߘ øPÓI|Ûé´ZŒ ÜùÞè à…µ«°á»Jz¬…¼¢ø …þÏ$,Xߤý!ú­E&L¥ Ý%ÉôñY@•d±ƒÏU L³ ËËÇã»ÇßÕ¢>ÿŠH±`æ!!hlnôqe|;"c–ʈT†KïÏIc^É¥g¸‘—0 JmlŠwKßKŽe¡ÆÎ˜¦|6†Ðo(¥£¢¶3¡N3më;m?*µ+þì^J }à¡ÌmY°#y.™åÑýî ËÃ,ècxÀ-þ…¸>·›«n™cÖX³éG­|þóŸÇÙgŸ'<á øüç?ŸMûo|¿ù›¿‰ÓN; §Ÿ~:ò‡àío{uYzÔ£°}ûöèÜêê*àôÓO¯Îg!³É½e!Õ2ÄýÄ¥ï$q‚QœÖOüîln•ðH ¯"ê^ȧ£ä.¥é¨ÔL´ÍðpÅ€(ñ”Þî'S¦p7Žˆbj[X“ñ7pï/O0nv¢m÷T)Ç©j隆Q7½P =‰Bâ}D1&Ø»©ó¤ýút ÍÕ)IV©µò¢„6–kwR°Lò;¼«‰¬¨Jù{PAªK};“âtD+ô5ýÉ*Ê=ûžÞåÅÄßœ»@y±»K×~¢ßÌÝ%qu뫤Gjü';éøø® çßµÿ¾•TðøæIœ¿„øf Ïü@ÚV)%>Ô  D(n›³ìi^i?®ró‘wiˆ‹M©Äßl*–ÇŒº”ƨÓtÌ„ÑÁ¯ Ï0„q“Äò˜hÕ™Uè*ˀǨ7ûÃèçÊn[rصþ^… zËwÜ·¿ýí¸ùæ›qÍ5×ÓÿÛ¿ýûØÇâœsÎÁg>ó,//ãSŸúÎ>ûl\{íµxÇ;Þ1U=>þñãÉO~2N9唩î_ÈpY€[\ÄI¸Wƒ2Ö)ö€|ð<ÙÊt4afVÉC ªþw! Q-£"¿úMžð“~@°•I¨76zûSI!NŸ›¬RºËD÷­¸_tºÚ2ÃÊ7ž2q>¸£ñMî…Ñq/Çú·ÿ Ÿ·ø=ù9­îÜW5^ñ…m…­Öúü Ûâ”}wó#ÄŒê· ~á½b,µÕ$ƈ"¹{k…U]™ÂŠŸÒ‡Ën,¬?©•ï!Œ Ò=œ1£ÎM"ª+5|»´þ{FÀGHï»:D@‰íQ>ª„‚”îwBϧàÉ(õk7ñw˕à $øpýÚº ¦îýµäÇ Àô¯Æ×!Ô’2ÚX`8à£õ,“ˆI ®ƒQÉ{£ïÁý_e¨l0U»ø˜+ƒå -&“¡+xPœëxl aP ´Yµ,z»¦Ý§å!ÿz,é%+#óýÕ­=è>0– $“s‚‹¼ ]|NêKâ–ÏZ½|>3èV¥XcN_RXÃ%]7ÿ…ƒÊJÈS1I5)±<Ð3„ûóžÍ1ò¿Ñlš•ådc'ýX¿ß¾×¡ëÞ²k×.¼üå/|ç;ßÁ»ßýn5íwÞ‰sÎ9KKKxË[Þ‚ååÎ=ûŒ3ÎÀ%—\‚×¾öµ8ãŒ3pÞyç ªÃÞ½{ñ®w½ W]uÕÔϱá²qK½ 9ä% FU×´-õ¤ø‰Oßò¿°ÞÞýÜ^$ù«¿ú+Àã÷¸äÚýï<ðÄ 7Ü€~ô£xÊSž‚sÎ9xÄ#|Ç ¡ògög8ûì³ñØÇ>vÃê½Y Ççßÿ lk–gWœàRxÝ2\º‰¼"JÛ‹Ò¬øD¡¬äzñTòÂÄAºJì¶Óóõóñ30;øQ¨S1ÈYyî~w“¸3Z,Æ=¤ã+"•VS …É^5¦\=HÙŠcu<ÆîÐVöµ=æÓüâ÷tø·=M`8õÌŠ¬"$¸¥ì˜2¥XÌׂÛG®bš4Y®ÑyYcRiûÑ· v ~Ðíõ:ÅÑuŸH”ï\]0½›‹«k…QùÛG/ uCãHʹ¢HÇJ»L‰Ñåß‹³ÀDéæ„x„´Ìpbõ®É­¥ð`Ï#çÆþÁ=½:ð‘¤/e«õÓR›¬0š¥ôî;TjCdš|´ïa9ÄìrËœA·8oé[Ðzdž[>jÊ,è$ªžhF±îQÛ‡0àá‹I—jäÿ#ÀCæÝ‰»6îë0žßÖ¬%ƒ \Tàƒô1¦™ €vL/ïÎØÏk;—±çͯÄWÞ÷;8ë]mçêbW¶qwÀI÷Žu¯Óñ¨£‰;Ü‚?ýÊroà—[n¹ÿùŸÿ ø¹Ÿû91Í)§œ‚n¸ùÈGð”§<GqD¡òÎw¾÷¹Ï}pî¹çúswÝun½õÖëcdzlQÉÓÌ5¶‡“ü$3<Ð@1p  ¯\Š…W®â1cV»G¤AÓðd5<¼·î~ý]ÕïPtxÝ’›ÝÄ”™à«ÚÂ`9¬¨2°)Ô3e>¤ÃVç rŒŠ2HÕI‘\K, ÀØ×7ün1h¾%äJàcp°Fyþè½ ÌÎlÉ+mŒÁ cÜI+W9À“ÓméCé€Iá’FïßEÚ³ÿ—²" ­G øç¢ìš¨žì{çÆ™JÑŒ17»ëûÃÕ FÏwÛÃÅöèKŸA÷‡Ç<¢÷£eQü¥XGÝûCß'woÒÄQð#*þG¢•y zn:o 0ògv«b,o<_´6šF“<æxL·$ïʯÅÀ‡Vbßu'ò†s•pã\p÷PûUr <â2øQ\äRê–º"êà¼wÃv×Z d•ñÝÐùv ð'½czºp>÷ÉÏZ/NOq™¸ZºsGüð‡¸ò›¿ˆ_¸ýÀÜÉÒt`GWnæâ°™x7AæXÖ—¿üeÿÿýîw?1ͱÇ øÒ—¾TÌ磌®ÂûÞ÷>œwÞyxï{ß Ø·o>ðàŠ+®˜CR’è±¼¨ÀÇ<êV S¢E†‡6YÀ5À–R§¬¡Æ€¬ÆÊ´ÔÍ!¬•ì$'P'»{–;¦WbéWVÿ6{ïqPÏÀGÍ„¯¹(U¸gÈyÖ€Y uDµ_#†,› I>”3p >øQ¶’ëHfl;üiXÛû?;{Ÿf9½— î_Á˜ëÝ*ïš,o1P,]]J¾¿—¹ÀIý&C‘Ñš°¢n?âX><(§ü¢Å³þKß%Að˜ÛÐpÉ­ à)È" ^Q‰Òn™»Kœaj”%.‘Z¼TjËQ¥É%Êñ|T{gšUæ0bð*¶`˜U‰ôÝ% W¯ ˜';6ˆ&Â7­Ý5eÞ a`¿@¡Ÿòü¼HÓ)"U±< ôÑš÷šÑŸjX’ § *š˜tx?.™‰„W‚@ïÒSÑ6lFdpsÂ2£$iÇkhÇkþ÷úþ½sËûöÛo÷ÿkì{Ü〛o¾9›×ÕW_g?ûÙ8pà>ô¡E×.¼ðBìܹsÆÚ.¤F Çmõωd»Iv–mÜr«"~ì¤;@?²…ÔYßîœÒìÎG+à |øüô <ÞABYyÈMª ϳ1]à&k×`1îÛб=È¡<ë4+­!È*[aÑêšÉ+–؈ö€G³{–b»Òý½ÎU)f{0)ÑÍàƒ_,ÜUÇtioxØÿ†ã?ùiØvo¤xåY Î…CWø½Ë–ÔßHÐEØ"8r±aõäté*eM`†HàÂsøòz—ï’ѧ‘X Áè iÔöá¿ZÃuŠþ“•ü ¢H8 Yryã{¤€8–1£ÌŸK~Ën-Z`À,[in4»Bl)}÷¶žï+ýÁ`AȧàœÔ9M;kcCt `·eFûŒ®óÚ©Žç™—!c@ÜÇÊ~ÞëÜ­¬zÁÅ_ß”ßA,ÒwR€š©DÔ‹4F›Ð7L7…Äî¬þâô¤â¶µIÐÁ{÷¿…¹|†ˆôseØ5 ÝßZÀØ1ŒÀ4Ë0“Ut‹ckÝ}v @Ø"¾k?#ØMÜÆö¦«þoÜôÞé¶Œ-ɾ}ûüÿ+++bšmÛ¶èvcÉÉ£ýh¬®®Î¯rŠ\|ñÅxÃîÞnE) Ðc!ªp7~(øÁÁø…Wt£•Ræ>@¡ºþú䣦%€…¸M÷€±» PœÀUvGÀ¢Ø÷ùmÛvöüô¹Øñ•?ïŸÏ€1•syåX[ýzWeÑÍ¡Âphx$†BÕ$3BRÀÃïVímoÔ»3: ßzäSpÜ՗Ú5¯8ã9]íW -Añj(åžÑQmÙ®þ»Þ_öJF\.m»ÐñíWXá 1^¶sŸqÍÀCò)®<\ 5ù½óþ@ô>)ø¦;¯¹ðhâp"ÛàãYz¾ÿÄ#ÉùhžRÉÊ!+ƒÀ‡­(·hŒ+€G”„¸µøº‘úÈùæ™e’qVõÉ·ÖDt/Ü¢ù€~KÚŽ$#~Îìˆ!,>×+Àú~S pýù{ªhX+À øæÏîOý³2j–±ó°'â®S^œ–O¾­_N¦¡yJ¿S e3"ŠÃ0;`š]ÀÎSaø•EÿÞ IDATßa–Ó¿ÿ<€®ùQÄó†E„‘¼.óïR5‚ÁŠ>šæ=°³£ÿ˜Qø•ªïm=šˆô®iVªÚ³¢o*õûxü!õïKÛKh+ñ㓈 ’±1Ì×oNí@*ClÈûm}X0*¥òK»õ¤™Pa{Ðy£xкΕAƒ¨ÿ%Miëó&m·Ó0PL܇n+V~„1‹C;‚HàH$îܵ£F óª®ðLÊ cIºB¶4Eô?Oo´6åß×ÍN’#©¿x-¸¥ß¨/Þµ=¾]½«[æ=ÄçS}¬¤ƒÅPÁò˜vœŒX:ÃÇaù]¶þZ‹Æhí´ö@Ø~[ªFf~ÔØjpv¾°Æî+É `Ïv‚ÀÔp {lX»¿@ì~xHÎï¶´žô÷LX~s‹i–W°´óðpì8lnySàà®»îÓ¸ó÷¹Ï}æVî,²ÿ~<ö±Å{ßû^L&›ëjtw豬”vað—ø0CÊ+¦Mt4Û1íF3:.Rvœ¡0Õ,šV,ǸýØ—Ñ4;ûc{¤$ÔÑ“€QÙ­Z]T&ð¡J3›\ìûœôÿ“õ[ÂDÎVÆk_Ãaÿþßà÷§€œÔ‘Ò‘:W®¾Y Æ´ø]ͨo+@s8.ûÓÿćþê°+÷„i./v Ûþ82l½ë„7š™Ñ+’ÔÒ­Yºh' ÷8ïÛ`ÍÒýñ¹§?±Wfý0Ë0Íöøð `g%2RÄ+UÀCí}9P‚£~¼ ÿ§àÿ–á\3¥ÐO]›7éó$€@Ê̈éþ™•9¸ðòS£½#xÜZPxT3%É1q8ØËÇ9ÝH”¿Ë ï,Ӏ酨ՌæX q<øàëݤ€}.¾( å]smV€Q>†™àDãquý8ðÈ\ŠRƒÞ›oÏîÿôº gñ9=×'¤g¨•ºA1I\Lߥ:u¿Ç@t€Ga m®£’~ ×™Ï$= A·D‹_°°ckÑÑðÃ:àý;ÅÉÏéú“™ÇæCAô Áô‘»¿ÿýï‹iÜù“O>yÓê•“mÛ¶á÷ÿ÷qã7âñ<^ùÊWâÆo<ØÕ:ddáÞ²¥§Áe£`¡ÞËëÝ[¸`šíøÖY/‰» “ÉíÈ)TTYL\\´UcBˆ´ÓÎH1ÍN,-ÝÀm{°~;ÚvŸ¼{Løˆê8k, 噢ª$ôs§¨C£Ÿ”Z»žNÎi½ŒÇß E²ý&Å-IbTÔU|,%ðZøn«9œ¾‰QGÉÄÏÙ1X?ì!0ãiV`ÌMZ`tvÕ+ TJ+ˆüzª¬6ñoå{ÄϹÛÞ‰‡^õOàÍ2 FÈóª«ÿÐ`hš1–äÁb;D;z˜Q_¿‘R>3° àŸ'ÅÙ–\Ï»%u}´Og„[€pžÆöð÷»X.@LA/Šê8ÒÈýJe¥BÿʰÄ)#Œ¹Œ¡^rùr,þì"à1u3B¿}ÿ}D{2¾×ΉÒv½ÕÕª˜§C9u†pБ©“:V9à#´»ÜÎÝùŒ=Úè')“ôgC]Qrý¸Ðïh_pcà]ຂ+t‚(†ILvs‰suéÆÖ°XCçéÈ(­”ˆ%"°èöܪkKmy¼ðwC¿¯Àò ÿGz£åÛn)¡Ì‡R›¤^ep,Ël°ð¾W9þp—–`ŒH[éõ'Œvgi¼;Kväw`yt±âú6ºÁî-\æYÖîÝ»qê©§âºë®Ãõ×_/n‡—Ç?þñs+wyç;߉g=ëY€—½ìeøØÇ>†K/½{÷îÅoýÖoáœsÎÁh4#@|7–Óc!²”VñÞ ›{ô|eåAøê/ÜØ µSYqȱ?ü9±NéŠ, ®Ù4G`ié,-ÿ F£{£ÑLjÖM$†Ç´«¹‘dcdV|"g¸ó玨¼|µáyS×þÌå÷(Ç…ˆóãl ÞÖÜä »vr;0ù1–îú°~k·RÀÁ»Èv˜æp4ÍahÌvï2Ò˜í5Ga4º7¨q­®¢ú%e/öëí>°<»ƒ+N}œ’¾yʺwi-ÕîGÛî…mWaíj¯˜¸•˜Ì7ŸÕ£Y‰®d…™Òí 5V,Çpゲ3³ƒ²O ý–ƒº L“9¸ý‘ñ$®ÿýߌ’gà4ûä ðÒÖŒIóXyd}µ~Ç‘Ô5²Z†Ä{Èøêç #´›~Q0Ö¡‹IåôïVH5<¸²=¢K }¹â¾8V@ kàÀ/â)oü´“=ªŸdšof…R¡Zjþ)í°0Yºþõ9§ 1‡ÁûÃj«S¥êŠ”yAI–DSøK”n(Í’¿± _Q!ÌžèÝ4Ñyêò¢R¿g5”²ï¦ôÞUÓÚ¶Ÿ°ï„]ÿl{'€5¢ðOÈ1êÁŽÒ?÷m?ÿL¬ÝïW±4ºW^©â”ñjÖNá)³¦ÃÒܦ9 ´í`'·¢µ{ ȱÖSLÇA1iWaÛ½hÛ£µ{»kêWë®Ã«&Å0ðï¢÷ †®Èçß—|$@†ãÜxágpüÿR>4ƇÈ6‰hâ1ðQrsIdnô_¶². cyLÂjù+퇻å]B,êÖ2àáëÅ\2l‘,ðlêÞ!1"÷Î)@ÅÚ‡$VGDÕ¯:¸hÆ~|H@G·`0šåxÎÔùb 2—…„$OÞ‡+€úDç€GT Úyν…ƒˆÛ®µÄ=Òã ðPã¼°1™–ëÙîû&Ç:R ðp»Þjm#;Å]OÈßA­Nv4äüÀ÷ÃÛFq1¥”ß$[ÁæãÐÜŽ,kñÑ/¦tñ>z°#Ò=ú…—žåa.êÆÊ÷þç•øìÅÏÀ^ù›ƒîÛ¿¿[k[ùû¾à/À½îu/¼ûÝïŽvs:VEÛ¶¸øâ‹£ §‡šìÝ»ïÿûqå•Wâ+_ù ^÷º×áÌ3ÏĹ瞋~ðƒ»z›* Ðc ˆh,ô2óî|Å]SÊÜŠCÛ Ž“õÛ±¶ö5´í¾zИޠï£`ÛUŒ÷_G½ó]¯»C»éÊ­fàgŒ~‘ùP |äD˜„ýª¢ŸØÇLé¡«BÛ#~'á\<I©#Ï)ûÖ¶ÊÿsïgÚ¯F´{»Ã¿·ŠáÞ‘æ"5½¾ö9ì¸åz£LhS‚bVœ… 1‡áÀÎÃÒÊÉ€c2¹ “ÉÍýó¬†Ã­ÀØq¯tŒaíþû•›üšçJŒ/hž:s#VÞSe.4¥:2 #~HÀ‡k"a|p@£|$ ®Ô§âwQmÜÎòÈ9À²j¬¥™Ð®L:6$̬iÚáy Y-MÝgf’DW=†S»'ÏFHòwÿdïÍK¼náú’Yî@ކÄÜð×VôçÁ*ªª´ ³( h1+¡ßE`xtÿd kfxËÀ‡ûŸ€LHÀ rÔÍ ½@ÀPàc©X¨ÊÞÎD”÷š?ü}ÅÂãE¡ ÊùÓÍ}­qyªéçé"Gc¯›c;Pc-m/ÂÑÚÕá`‡Ó=¸¾iͦÇ>é¿âaö>œöº+*ÞK'·Ýv®¾újÀ§>õ)¬¯¯'ivïÞ+®¸{öìÁE]äwk¹öÚkñ'ò'xêSŸŠW¼âÕen´¼ä%/ñÿ_wÝuxá _ˆc=\p¾øÅ/âÜsÏŇ?üaÜxãxë[ߊ/|á xüãù—9x•ÞDYÄôXH^Ì(\gÙ†³›¨t{£_}Ú\ œ>1·hÛ}híÿŒ†+ÖR']aI j≫ÿÄ÷PÙ2abÌÆXð¾ÌMHÛoµ-,âzÓg–©nËM€?gÈËíWï«ÌýÍRb©òmKÚúILŽÝA¯[µ¼OÆ“µ¯v “öŽÞP/·ý(ï?®Ísº¹ó5Ž¿E§œŽ1™Ü‚;—°}üu´ížþ{Þ?‡»I_ÊES(«b< (AK¾ÿE†Î ºvâ>m]4#ÐoÜ¥ë¯'ÛÕ’­géxgâØ]šþyè53Šb|„-‚!~óé¥ë—ño&n-!­’¦Ò‰üäm÷ÔváòŒV‡Æ-ò´™v „öÔ·G:–wÎbùr@”¬ fUˆÖ·¥¤bý´±?H ËArç(d”Çãa¢²v²sòü˜!Ú»IÞ ´'½ iã…ÑÚr4ÖvßÕÚZd ä÷»Ùyü‹Äm©/ÓýÐÔãÊ· .¬Ã¶»ú°$äÚ¦Él3ž+^<’s üPe#ËÏU ØcbÂÅÑŠ·.v÷¶°ÆéRýЖ²FÝ‚[vØ:]é`Éd2Á#ñ|õ«_ÅÞ½{ûØÇpì±ÇâŒ3ÎÀUW]¥?ûì³qÍ5×àþèðÈG>;wîÄÞ½{ñð¸è¢‹©—]vŽ?þx\y啸Ìg>k-Ž;î8\|ñÅxþóŸc=Ö§=öØcñš×¼/}éKqþùçcß¾}xêSŸzk¿ñ²=¶„è Œ:å8>œH[ɲ'rµÌH{ŽZqiaÛ±W–žØõùÔè‚&÷ ~°ð>càCVnã ^%åWZ™u;9_yRub(Y´é¾ò‘LàƒpEyĆ~—Ƶûôùºv;Žë¹a¹`¨«pn#aåBÿæâDOúÐâvÅ3™x%Òšv\÷¬ö«‘¯»t¯X—ZEn†¬Œ»ògÍÄþÆçã÷Ù¯äù£ø õ!@¨ éÁ‘P–B\_&À‡(°¨—4¨bz½ûG<ËcpÙê÷rÆÕ$ŒìýÕ–Ùõ‰ R©Q@ž~ôÿhŒ7£n.ðù+Å…9¨Fé²#K—i¡Kn:Qûq2ð à;'S¹žT´uËb^·i„é9j2‘©RaÄV0ŽrúD¼T>¬»F€]Òöà¡:£¸Ø‡=‹Ëõ1·ó¯xAGrÿm38;`¬“æß(x{îV°‡ý9ÞW@ú¾Œ‡„q^aÞ¢ :þÖ~þ¢ù˜há¦É‘NîØ³¨s:X2ðÙÏ~vÐ=§žz*ÞýîwoPæ'wÝu^ö²—Áƒ'?ùÉ¸à‚ ðK¿ôKh½_qÄxç;߉_üÅ_\€ ¹ûKµb•›¸…`°ÒÖ *.—7g–@d©Z}÷ï¦gôÆ“Søs[jÀDà‡tûI?£W„Ü VYÍœÖ!?Y‰+ëpÊ`îÞ&-Û² —ü¯î^}<6lшßeª< 8\.F ¤hà˜Q_é6>:amÀÓPǤí5Þx+.i¤¶Í%«úäû RàʯvŸx„|bÌÆÓw»¿°k]ßňìjЧ7ŽùÓà#6ò°Ñƒ%Qß,í¡®$f\¬rßq(à¡}“â·ÊŒQ9¶Ò¬qr÷Ú‹ñ̨s± : F±ð†Ü\]b0ýÜ™ÏT' òÓ~'éË€G¸È¥É¤×ÀÞAïXr³cå qgá¬"W7í;Dãg‰Š7쥨/:©\ CDbMu”‰]¹+2³a‹À‡èÖÂalÐfÒ2+@9ªC¡î¦~Ðt±NAË åz°C`•ú82^Oêßek`ÚMܽeË:TåÅ/~1~ï÷~Ç|1í¾}ûðò—¿gœq––~ò!Ÿü'\HdTÌD9‹ÓVŽkX‚kˆ¿$Pú£´4›YÐsŸ'‹º3€î ×ß+&dLÎ?ƒê¨}zƒL³ ÓìDc¶ÁÅA0ýóµ9À£"HÀä†bá2ø1÷b°¶ ßžžÖ¾µ6¯gÒ…sä–ÆtòÞ•¶¨å‘‰²Ì·êNÚ¸‹ë2p7­ê@Ê 9TO©g}2€»‡ðÞ¬7Ž'ÅþQ’hL­7‡äÏÁ ÿ¿| ðP·Ÿ(éõ)s¤;?bmHÓeøy7†]ˆ3>|=((Ò›íº¾NN:é$¼ño¬NÍ5×à-oy .¿ür¼ð…/ÜÀš²=¶‚˜Æ+„³ÄäˆóœŠ ”º‘ÒT xÐsRL lÑÝ/šxPƒ,YÅ’‰2)$¸²DŒ JÔLƳƒ+H¼î¢bà é]†òêâ$àÃÇ`«å2Ûƒ®\ LÆ€  OPµJÒßKW3’Èæ>Ï ×‘LÑÚ‰¸‹qˆ¨¨BÝ}¹ ½XÇèÿÙŒ³”jêÝ^2é¦àX Šä²€ St6I>®Çì"ÇÀpcÅ=ëæ2ØŸtˆ0*z‚¤ÈTHì„\¾1Øâ&Íþ<óƒ"bL>ûóuí]<ä4`×$ŽŸà+õ™v"œç±ôr™¢Îü}s25+г4€ðÐÀŽ*–øQ°ƒþßÄ€‡÷;ŸÀ` ]¬–i£.냻¹ ¹øÑ5IR¶]Ñ y6 °ÝÌ”ix¨Û®–d àᄎ­žÕ¿Çú}ìøê›a-ÙöÏ?S1‡TÅm02òµþÞ3<šf{zŒŽÄÒÒ &h– vÿ'ÐNî$»Hýý ö€äYK1…‚hsK;DWAÂw}c¦GÍû-‚ Ò5Ú™AHÓ…m^9à!K¼%s×OEÃSÍ€J¦@8Û¤#A‡‰€RÆ,"oÿÚöócã¿[ÿΙ1‹—£•_ËöHž?œ:p"?Ǭ]Ê€G‰Ý¡‹Žt^ d`bbו(vÇ,^¼$u `úö°=†œwÞyƒÒ7MƒóÏ?c*sÊôØ¢RE±ª««–J>¥AQEjSÜ3Ãö(çU¢ÆêÔu}«J’Fr[˜Aa)õ’w#OÜ‚£¯{¥hè)øÑø•¤x¸ïÆÞ㫽€n i«¥úªßÉGPf‰ðv!§£Fˆ»Vg|™HɉW9‹+ŸSJðVIkãÝVFB} ÄÿßçAÜ\`Vü¿Ý7 ñyúJ©ì÷\ Çâ{èïÞ#²=¸’.1v+zØ1ƒ;K– §JÇöp+ÂÀöëÿÝû`›~7(f”ª[ps‘oêúSk`Û®_ÁdÛ=0úÑÕ°“[ÐmMCìpÂûëÖ‡–¥ÈîëÆâhãÆúU×AcÏUÞãO }Ï6Ѫ· J¤eK¿0+Ù\35»¶¦ßJz½6EÞê½{fðZ†T0£HùªÛ~ð!VLÐë ®ÏHÑ?G:WŒ?œ ËØÁ Mï­]dPôÄ…³\“xÆY¼ÝmÌê€(£ÄÖ t޹IDåäef”¦ÈXîÓ +5àÎ\w 65Z'°Òèêä¹øÊü VIý%C?*W¸"× ”ŒKE¡.µÛþþdw ²›Œ"ðo´òÊîÀ€"ŽÝ±’‚þ> ø †¿ÀQµäè5¿³‹| \îjÕUZŽï!1CÜuO/¡`ŸÀðPþ—ú\ðÈŒásß…¤¢yàã©Ý a‡u}ÉÕ!ç&¨U ß%Ækß‚i¶ÃÚ1Úõ;aÛÕªñma?Åýr¦:p©¹då=ÓØq€j­^±;ŒØÎ2óçôtxT–òÀÊHat|ylíÞƒá÷RàJØ¥Sú{F7É1MN'cÑpÀÛÅÑêÚâzE±´Ò-xãÂÀ#ç†çÊg‹NƒE\}Bœ”úoû´3æ…¿Àk`‡5VRf½W:Ø_ëMún ”áႜnKÛ„Ý៫‰X ð!Æÿèê­ma›°=¢`Ÿ`‰ëAÉðY_P=7ÆS¯¼Ó2%ƧX]ØC·ø¶°íc`r—ãQËæ›5(x)¶–ºýª(Æ x°12aô‹™ÿ䘂Eà"{_+°0³3 }ñ”ý¡¼£äý•Ç'î†ÆE/’¾ÏÛ3cdðŠX3ífgÓívEÀCúÆ%À£äjÓ³·f³¸^cÐ`yùD4G<ëw~Óo¯¶Q‰/¥±<*޵겓äç*H&žE5ðу‚tŒÓÊ©<ÜÏ à¡ºášNŸðy–¬‹?†Ì­Ý‹r,îÿñ‰ ªt!ìà¿ëãuúÕ©÷–!.¯é8ËA¼Ò¢Taü˜'Cy!ÕrÝu×áºë®‹Îýà?ÀW\+¬¤YkqË-·àýï\brö IDAT?vïÞ½YÕ<¤dzlu©ueÑˈ‚1Âs`ý]| Eû¹BOƒc ÓÕ®<VŒ©€¡FBæ› ~çrº&þß°€¤F |Ç€(Q1Ïø|J€GòŽÅL\Qëýo}ÀHôÀ‹u1&Ýâ½ék×°=úo({­te½ÊŠ2 +·3HéÙHß·±ÌܯÖqØÑå£Qþ´àZ°‰‹¶Un˜Òùô¾üwÕÞO¤7úpÐÄDý®‰¾cnÉnq_0Dƒ ³Ä÷‘YwI3|2WcqàD›¼ÎEÇ˨]–À’g¯µí>Üÿcÿ'ÚvO=*ÞkìHž,´åDtiZàX7&”ÓDù‹î7¹žb.ÄÉúú:n¼ñF\}õÕøçþg>·ƒ‹µG}4>úÑnB =Y€[Iø ¢;]ão8‹ AClV*¶œ·7Kò¢nÉ*¼ßx…ˆwâªûŒk‘šH&zçËò§“«6ñ³çÕ·.ëþ÷éJ€‡;GŒt·¢Ö•ÑŽÕAóõ+Ò-Œu YØfRR„(€µ “! Š:¥DÀKß¶]Ýè ü@ ’qÉE®R&J¤ˆ†y´%°X‰ Œ²<¤YðèƒË5+àbM¼2èŽfÙ·…¸íÁ°~%Õö+hî0¸Xëë¹Òõsߨ•ž•Ùá+q=t©`{l¢$À‡« M“€¹€Æ$ê$ëØXçõ¥RšË`>Räçð¾ùø˜1ŽÓÊâÿ©Á\ñÞæ vðët¥æØ 5íZaÕÊ……ûE6¤Ó±ÙmïëǼh®®xÎöÐêX8'f=€A+‰8& ÓŽf3Ñ×Ù,+zJ@|S´‚ lÂòpçG¤_­Â"Ö ê+Y»£¢ÍÇï±ðпý1L:]M7K¯NÚÙ̲²<ô¡ÅCúPÀW¾òüê¯þ*VWWñÜç>WLß4 N8á<íiO[0=²ED;f66Bf]àÙQ ƒÇ÷¨ÄÕÕ.Í€÷7J;i°äsP¨H <ºc; Fhí*¬]íAG÷.@ì$À£ ìÈJÛ¿ì>fÊeÝ7ŒY;9Å5sÊi·ÚVâÛØÀXgqÄ+Ö¤þ=ëà³|P°‚ U>75tã´R˜+Qý=4í 9Çf¶Y‘û¾u cuØÑRzt,¶{:Ût¿mØQ ~8ö‡«Özø¹ÀGW6>’1#e‡Ô²=¢ÇÍQÞ+‚¦Ö€»Œ9¦”+i!0/Éo‚.ƒ%*å(3‘9¼'+òÜ-ª$À#®Ÿ¼BI’K“TìÀŽâ.4Ÿ6oG%#) `^ú–I9ö‘ÂÐÌÔ­ø ßлl|Ÿ˜U7ˆÙ}u*¢+eÓøÿ…t™…,¾ðDëjÐx]¥{Ž1\€áŽ•GÆÐ¨ßHî2Œ9â Êè̵ka,ˆƒ»gƇ,"Í8à1g}²vÌX09)yЃ„Ë/¿—^z)^óš×ìê²²=¶ˆ”¶ñ¢iæ>˜ ]µTÜ\ä´…|ù*empT‰ZZXíRW£IdNÂWJŠ S² –1jŽ„ivÁ´?Âdr,V~ÂÍQråóÅÛߨOš2­žÄö°cxE“(áνeHN_^x˜Þð¶f<{x_Æ´’S)W>ª´«Œ¾¢±6d ÃÅI‘¶ŽD<|Þò}àä@&Ïúp1˜iNtN;™HA—oQbCLuT˜zU±cêÆcÕmNt;™ôírZÖD(™nÙtÂ5‘‰S(X9¥Ò†Øhru‹þO€ í™+ø@G.]5æAÆ 4SáY„ô5;¸‰y‹m€ÈÂüÎßU¶q6%5¦Ó£:Vÿ^î·Õ¿_•d W÷ «“ÌQ¹zûôBç¤hŒáåôïÒ g{¦[˜±> tX)ø‘-ålWR†«»¤óøsѼǾ5 ¿Û–v°sâo/`ÇmfjFÇBº<üáÇÓŸþôƒ]CZ ÇVy¬àÚ2KüŒ*…}¨ÌBóÎ Ñ„MÞ©ÂÈ•¯šÕ.É­`Í Ô’ò’®nté¶£…féØÎè´khÍѱ<&DI•€ºíÒ¥rà*Aª¤uF©[™õ[x„›Zƒ„Äxðnýj¿ñõ§’É+ßêÎÙ‡¬>âo|q3íEPTe¶Òwgén.€¼ã x¤LÉ»ö¬,ðü<¸Aÿ·¦šî/9¬1 àaÚíÒLÛ˜Þ²î] "æG»ÖÊÂéÙp;Å7»£‹$š‘ @ܵ©äâR vY|^›².$8<†^Ñ5A@D×>W/t¤lx Ÿ'¢+K à¾>vâW}%6G”¼ìPŽÚ¹'¹”d…ñ²ª%†T.Ïuc¿›£DàØÕ£Ï#Õ‘ä¶Ý}ÓÆßCç‘´=¥ † x 4<“þYÕ›¼ë•$Ó,d±¹@ø€«“~7¶’ëIø–éùx ˆ¶”íA‹0¿OÄr´Þ¥]g¸Hn,áZ xH`YUpøY¹û}ó~qƘMp9ùÎGÿßù翇mg°îæ2𢽨*íg?ûY<ìaÛàz²=¶Š(¬Ž‚?m¹*à#·º6‡•5­nô¯~V7ÉU ÊcÈà_³‚Å€¨*åF™äãëéê†Aƒ¥•“pÕKŽÄÓß´7±ŒÃwÉ*Ñ÷K•H‰PO!Û£7<ý®-øpÿ×jº‰ÒnGh‰]Ä,5«i¼:Ã#W5ÿÀü»âT*rà!¦£ç °á][B;•bv8ÀÃŽ–<ØÑŽúq©1°MªP™ÖÀ´Æ`4‚±¶wu!m`Ú1[Z wk‰Ùêß&$¶G:ÖåǾ¢‹KI4pƒäd _:fÚc- WÃ. ésì‘‚dÚ«3zS¶Â”Û_–Æ˜Âø“¸³ø|SÐ%5zê ,[@+O"h¯ÉÐï'e!&ãÒXåJÄ̺€àËÃ!bÙ¾OÅà•¸í§M·ÌMÙù÷"º§%®#5cµP¦¶x¥ñ mž§¡Ar)âûë¸kòfÒÍ ¶ÛaEfÎ(@_ôa€GW?M_ÕÛQìKEn÷€ô]ÖDš¬¨Oþ «OY¥GÈCõ›/H²-Òï˜/õÆm·c¼} vý»kK»¯Ì¾&Þè{Pà(RˆˆÊLX ªÿ°g.¸•¥Î¥&ðäT..®­×´ñ^Éâ?$Šœû-(½ƒÚOthï4vuaÀRIËÃ+–‘‚HY€æ@8¢Rfä×o[¨Á`kÁŽÂ{¯ÛªY³IhRÅ¡.}Ä]"Ãhx`]Þ&ë¾·º»<è7«pMŠ3—A$–Ÿ “ì>èzTr.cÈ“ßø!±>"Wx—Ö°•, õƒ<+Œ¸¢˜åðèê—<¸.»ÆRF\‹À¤ÏŸÉø¯]+´O©ÏÍ=eˆë’ØVf>’—OúÓ¸òÊ+qÁàgögwÜqÞüæ7ïݳgO²ÕíV‘è±Õ$ëÎûÁT ‹È8Iüi-í,+ãCÒäÐz+–ß+Nðu«,bybÜ –Oî'õ–š$XezôÊÆdü üÚëŽî»*|#a%:ç#€®ŽˆÍM3A2Ês,!Û¹.~ƒcjPųß)&‘ZÅ[JS>$é“xë>%/Ñšq L”\ââ #P°#:ø!¹¶¸¿MíRÛL–Øøp5iz€Ãöl†€ Û6 øl‚+‘À4u MÇöè¦lðRIœ±5L¦e"PÀCF}:çÈÀG”úgo(n2ÙXH%àF*KswIØÄà™õ"Æ ÐÄNtC‘?¾¾ cƒ9·ˆ(]­ £§pGI²@“ s$ÞÞ¼fÜ«cªDõ‰ú²ß—º(†öÕ¼J€ +€ îy\¾•ß”ÆÿÜûázBé êê$»vjÏ];·ÅzPÀákí+3ïq5Ó³i˜ ðPE5%ÀcPŸg¹ºi,ž(Í&¹·Ðò¶‚<ýéOÇ-·Ü‚ÏþóøÄ'>˜L&xík_ c l~O⎻ezl1M2Ùåbî™ \IæeÕ›%:râï›]MQ€ÑÀÍÓÂÅ ]ôÞ. êwä€ýž˜`}ýhÌÝŽ¶ž/ Þ%9ñóŠF![™²BWåʾ™ÿ°f°k0X¬¬äb\(FCøØÞíƒ~¿à„+íìœ*–m_[E‰åÁÓ‡6둘FÎv´Ô³;ØÑ.´éAÀ’,ÚÆ`y 0-дø1ZoÑ.…DŽí¦ ’¹¹XZ·pˆl}úf¡Œ¡â÷¾u XÎÊr·¹]iªÜWêY\š+Ÿ–~°‹Â€UðÈ@*?bW ]ÉÆœÈP$1 jVêSFuc!yÍb(å]Io¸™“Â8TíRCX]òíeȶšx792*¤ÿŽn—¦qÀCbw¤ß³r^*=—äÚÌYà‘ %öT9sMíêAÆEÒ†ü¤ê;®œ\[J~º2GÅ6¥¯SÖ"ИÃ`ší0f&“[aÛ=e§L xøk èéÓUCú{%“°x²iræ™gâ}ï{Î<óLî¨£Ž‚1—\r žô¤'a4’ÛðwÜç<ç9›UÕCJ Ç·ó 1?êAxä=O!W´*¦Z'î/À†Ríyˆskð¿Ÿïø&®t ¬ˆèº²Ò8¥Ô¨Pî¬]ÅÄ®ö? •¢¼ˆÉ‰áäÜ\Šq/*™ ªâÚÙ ˜NâÞ°ï€ÁqczÃX둸p1àƒÕ'[Toˆ³ ·ŠÔµÕv:œIÖ°À(uÔ˜üH|Ÿ³@ˆçьέŖ‡si±MxL–àA |ØÖb'4-0Z70-4­·] ¶ |jlÓ5=3‚wsñlvl¡ †Á¨~û㨽„ÿ9¨¥gç‚A“‚¢a·ƒð›»»8©³séÇCr+¿9·§¡¾O$6 íò.°Ùè)÷^›nkَͦÎulÜËu+ùŒÉ¡¹¯ÔºYæb@D×…{k¥–]K—€FµuQ€±Ä4r‰ €9Ð7B8ðác"1ƒÑ¶ÎM#<$ÆG÷,yöA"Ô5Ѷ¢Ëa€\'Mät +@ò»£F—2q\.vÀ0+D«Óyª­ó|y±`çkFÛqí¯= ?<þ‡xÚ>Ûî…EÚvçåÒ¤ãß\ÜwÕêäÚѽ>sÇ7pí7`²…Šn†¼ç=ïÁí·ßŽÝ»wûsMÓàÈ#Ä«^õ*~øáÙûO>ùä®â!) Ðc È ð_°ci;;D iÉê`ø˜³ä Ú,íJS€èj:‚ÁÔÏ“s§a“vWÞ$܇G&ùÒ„Ÿ‹r”DªÐå”}ž¥3ýŠyïOTV¥ ¦%ŠhD£õ†U`S#¢VÔ]\¤X6êCµÑu±Íç”dV©¼pöŒÀ¦‰$(y" „±EDxD@¿®D@y‚ÐÄ~\\¢`’CÎÙg¯½ºÞÝU5kÖœUÕ½ÖÚ{öšc¬±Öê®®®î®®šó›ßœÕŠ×aitøÉ|!¹ÜLâ)Ú;!ô¬I€z¾r®‰1!¿£ÃÿhÕ ±­Ä°¬<’¶Æá,àqšÉIhÌA´víì0Ћè7;jÛNÚ¯Þ ’Œ>âJ*ûmÄ*ª?od¬ !.¢±Õ—ˆlÔë9¥¾nmÛÙð–ØPô vª³;2`Ç @‰mCsq[Ä­|[y\ÐÆÄ@EK®JçúH÷ºèžçâ¢{ž‹»¦Gðk7_³ oY¢PÀÃÉM7ÝT<àíoû2š´çezìGë\2]­–öOЦ$ñbd“¸[ *‘eý‰gå‰S<®¬ð¦ ŸðÃD%¥,04Qï7cQF£¸\Çö¨ñ2ÈÊ« ê(z¢<Éרµµ0ëÂ[ÌüÏŸ݆ÞûNCjrÙxÖJgŒv†hK’_C©ðè錙XyáËÔÒ±ˆ±ÇøÁYQ¹>¤%üfÛƒýýh a~¸§¦»ýýj/”íyf~,j–«Í9că*ý_Â4‹~K!Rþ ²Nê“–«ŽÃÜ?6¸DQ‚SÀŸsÀ#õïJv‹ÈbqÂÈ« !áL=rÔ$¬þwm7 À‡9° l» k¦€RÀCM\)^ˆ`eÀ޹ŽÂ!AÚqUìCÁy!…Îpƒ¸ £e™Fý»“&²ëO1à!çóŽ›C£ƒ…¼ÃZúr5Rbh×…y@BvŦõù°<Æ\C´=ç4t3×öPÂRŠú"`ÛMÌì³í/v€‡ mQY[óâ2Ǥ]c°ƒoÓÀ•ìž¼ë]ïÂöö6àÁ~0.¾øbÜvÛmxÞóž‡÷¾÷½8ýôÓñ‹¿ø‹xö³Ÿ½Ë-ÝYûRÒ8ÉÅ&›œ_†Z‚MÑà§ ñ(øÿËÍ@É(kªœ£Q]mäÝ Û“¦ÖüšœbO`Ü9² Yjr‰‰àê(PJI{;VJHXY+ÒÒµ^I1td XõZØöX_¯{G]Zî#óhÇCª$ñ.–ë¼¹}.¿½m±=˃†²|â¿ü;NýòÉ8å«'`{ÒÂ6“YƒÉ¶AÓ6~UW¾mº+¡lo˜])€Ëù~<’)V#Ês”Â\ÒBB~³ØiÇÞAëûp’Äz ðA¥Ö.0â€XÙ•ù6:Fo 3. €‡Wè)àaÂØ'ÐÝ’š[Ý·û8o²¥K–²péºxó…ëb„äŒqÕ£?¦- SÄó6ïuàF7^Ò}<0ëTÉqh’Ò áÒ6ãk¤Ò°ðÈÖ«]¾t7Ú?VªdwFå‘‚8¨+é%½gh>!‹c¾RâÒôd=ëdÀƒßã€G1Yª xÐßM´~ì¹þúëñú׿?ú£?ŠóÏ?pùå—㓟ü$^ñŠWàÜsÏÅ•W^‰SO=ßû½ß»Ë­ÝyY+ MþnkØvì…xÁ]Å£êô‡SÍõtÏY<ès£@Ø€1'éŸ ðÁŽîºyBÖªÞ¾ê~Æ·iï4giÖ9„xâs¿°?Œ_IÇ1ù˜¾ ITÏ)?—dì)Í!fØ›EÀƒŸLæ<ªÀ¾-€*Û«5ÝgÉrëûÞŽß;`÷y.‘µµ5¼ûÝïÆ%—\xÏ{ރ믿¿ôK¿„¿øÅº$¨ÏxÆ3V ÇJöŸèJ²>èa…A‘!†å»rà‡›Xk¦¯¼Ö6ZGÔ>ù|c¼¨ñ$XÜÃ@dz$B½W*à‘®Ó5¦7 Üò¦ÖåÉèXCÃ\¢¼'š¡ÎÂxäú„ûJ=udyÁˆf+P`³Kör%š­¾>nÄfúW§ µ]²MÓz0 Ê³¿4ay=ªB_rûIø•¦‰Àž±áäŸø¯˜:†GüÝÃ`×,lcqáû.ÆsNy>À:»[¡Ö4!Äeç¤Ì< ïqÙjú mIÂZ\;f‚½ âUDìeÎÎCAá/5/2,2íã!-€c1f½ß@¼ù-àâù­íÔßãÜr¥];òllø g·+êÆçÀŽõµíâ=ÖuÖIz\­.“®ô&1â³öÏy €‡3!àG ðÀ޾ý¢°«;Òc\Û¹ÓBËy&½ã4¬Ö @3€py•ÜØ(„Ùt2‰¿´oUG$:mR·cÛµ›cWð¨bê”ÞÝî~ºãÆ¿›óË9ßötœómOÇöѯáï_ø¨]kÇnËG>ò¼êU¯òÿÿäOþÆ\qÅ~Û™gž‰Ò’¶_¯²=ö¹ õü., FŒSÏZg×°ø¸yV{½« ElP x ‰ N&ùî¿<Ék“Fm&^u­{3ì–§ËwòòØ1EQYKÍ{BC(Б‰œ—& qq÷Ÿ…ìVïàï{(øÑå»àϤ?l‘9'I„¨çŸ!Y¹@3rÚ˜(á}±‹Þÿ ÌÖ,¶'݃:zÂNýò=pÂÛž‚­µ>¼eÀeôáñÉúœyuoðÔ½œuWZµ…—m†ùà–Ñ$«JŒ)1¨;—|€l,jãO\°‚ÒmlÞSí¤Vq€\Û¢Å._ˆvôåâðÌÈ„»T'»ûL0{°ÌAqφɸ1ÛW dpe€¥!íU—J,_Œ„´¸ã¤:EC¹8<»k½ ì t°öIa- I›k ?æûÿÌ“/ý¾îý¢N—&„Áø¤Üù~ö7h&§ÚÙWüy»2õÌPc6pàÄ'avìã˜NoÁç$w'è¹J€Ç˜„³5à{\vo+ZÆt¹þúëqÆgàì³Ï޶9rd'›µgdzì‘ÃVRƒy°äb¾Ç°$€QŠRtÜP¢ªÞ¼QªÖ½`£tLr¯t’Ÿ{¨üþNìrŒTŽ˜2[Ò)tÑx>ÜW@htenCæ]H ¹Yúß·1½îb¿l ŒÖ¦€ã<£¦é—ûLÛÝ´¶KJÚvHc€µ­§~ùÀú´AÓ˜Ö ±]¹¦ÿ k̒óì ißÌŸ/wO¥¥i%PØÑ¢=MbÆ.ÓŠµQM¸ìQ@‹Ö"µÅ´Þ£ïrwH`€<»£à©ÕBW––»cÑuÍÛ¿#F”òžë+‡8ÐEbØa-ù_Ç2ÊtsàÑ• û³ B´mc+Èc gzòùK>%p$^¥¥ß–0?@X‹i?sÏÁ˜îþúäîÿ—ßÛç+^¯¤3š s¨ß—;¸ŽõýמÛè¤ØF»Z½ege:b6›a2™àæ›oÆ?ýÓ?áð£2ŸùÌgpŸûÜg—Z¸»²=ö‰ŒŽå/Ie²;™=1&|$ç]~Ôœ&†0tÐ׌•šä„Ѝqþ^‰î·ð£Þ VÒÒ)Të5& mÉ'„+ô:bà‚¶eP]TpR\W=Gž %Éó}kN£ºXØ)àC•Ží‘.8nó™«%kW²ß¤4HÉñåt°+fù¯5j’Ñ6'0!V9ôZh[Fî/79/g”à‹Lò„‰*x:œ"XAïìcPsð Ç >–-ûÛÝ»ù€-¯GM|u'²1'€%;îu–êU(£à  Œ | òTç<Î#…¯ô3 %Ï݃]Õ2‚ÒZ4­…¥,À¶! JûÚ§èÙåÑ´Ͷ%À{~½¡â®-úÐ)·³DalTZ’²9„=“¬Z@óEʯÄ6©—ÚU¯4¶hȺ÷Kñ¾æ@õð‡…• IfsíŠÏçæµ&EvæÇ¶˜ÙŒbw(`‡ÊòX+gκ³l9YEÆÇ@]×WÊïY>ÂÄ Ç¢DŠiÒRÊðÈ M–æm'jN4úw®ˆŒ^ ëK™æêq?In3 |ô‰xXBó™Ÿ×º/ÇndØ5ÏlÞöð®.oœZ‰./yÉKðô§?ßôMß„õõuüò/ÿ2žð„'àÓŸþ4žõ¬gáÃþ0¬µø™Ÿùüõ_ÿõn7wÇezìÇòˆ 8­Nºb 3>ô¸G—«í«‘\=ŠŒ¥âÛàþñÐm‡v„ê©Q<c72bé5LzE«cxxJ¦?fKUœ:ea ˆ‰ì(SA`{]¹ÒIœæßˆê+)æ†GÖ’w†Õ¿YÛcæÁä4G»—~[¼ÏØl¿4‹±.n¿¯ma›‰gi8°èV¶í•Æ”Vvò逶«ÇöÀG3=Â3 ÏM6šë˜ A!Í”58«¨qÀ€¬ø8îÔóx ÍÛ8€åQ]§>Fg—hb $ý7,EÚƒ’ÁŸÏÝ‘<$°£žm&È2žŠÔÔ%½ ÐîLN›±szý’ßk„°:(¸•ž¤›£Œé“ÂuJ hYZ!Ç“/‰D\ò]§¯E«—AÓ1'ëE°8ÀÀ¤x%Àäô·2”>Bî3z/ÐùvÖ…æºq$zË—šNjŸÂö ãŠ2W‹;Vl½+øã?þc>|MÓàÄOœwÞyxÇ;Þ•Û²=öÌÖ’ŸøùRñÉãmÙåmeò/sÍöP+¬ i)8¹v€LüÎŒøw^&h̉0ÍÁP7Ø.ŸGÒŒ¼„™S~öéDK$ðϪÛUð„÷ÁýöÞ:¥íN @æM mÊ<ÿèøEÑMIÂF¼)<MQ´ˆpÀDÛzVF`zt¡*“m‹ÉvnL¶¥+Óbâ¤>DÀGKÚ€ÎÅJ$H™Ó ” 2>–)È«'¾ ïuø>y c9ê€äÕ­NNÉûì>Lß•¬Àã!LÖ%-ÝÍÛ`íT÷ú>5&‡˜…µ_û쨰vùÍchíµlu¿Ì|¨vøÐ¹ˆ¾wfâ?T¬Ù¥UƒéF†€dÎrÛý~ ÙŸ&+¥Ò÷ÂDó}’´·.oO-é«‹<ªÞ}ËÆ·Rß®tArãc¤ Y2·ÙnÉé.gÏVô¡}ߢ>´ýCö g٨טüÇêHwK<Üöd\ÜYP|%‹º¬í~’Óc_H0ÆJ2”B©ü~éNçÕà, 8³l ­}¹ j“¥ qâ;¶äç" *•Ûˆ @ÏËÂE<Å3s¬ÝÄCn8˜lvžv³Ñ{d¦2ä~b@R£±RÄç )°½Bè^a TÑîú‚ÐßÔÃÆBzOSQy¯º.Â)­p$$á$~»Š3$ñº/Xrn⹌Vpñù>¯€Ôy{=áI8O@Ú×l÷·Pbv·ðIÄò°Íö6ÌlM» Óv (u–Gøpƒ"ˆÄò#[(6[Ä£Ø[€UãË4vS² >E|eafù| B°mXžY2ZüùÖ y#CF€xL59%o>7ÚX»åc„ú€5{^ŠŒ‹JÖW (Oæ ^¶eûé°‰Þ@^”Ï®^Ì%ñã5<1…Ž%UáûÛ<ýއ U°W|Ø\1ˆõ¡Ì3bTàÂ}ú0Ïø¨l¯ÿlÀ`¦9¶½QŽ1 œgël“1ß_‡;'oƒû9ð9¶äÂ\Žûñê8–Ûo¿×\s n¹åloo'ûïºë.Üxã»Ð²Ý—è±d«·ð<ðÁZ Û€Ô7²*R:<’³‹’õæ3†}RrÐnÃLPf½g£ÁYþØæd˜æ`r2>ó-ÀÞ÷§˜µwwñîÞ+2í'ô:%+v±ë¶õ¯Œ˜‰š_D_”‡࣯sâû”Ê1ƒ1Éæ³©ŠÆT5¨×_{r|@(˜aá@âÔ/­%Ë·E¼ô1¥77ƒD}c4>ÔÅ4¶ÏåÑ’œ G(àÑÌf2àÑÃyüˆ'/°<(ƒ%ö¤Ê’7Šû–B7¼IŽ·‹Éžm¯Ôë¡[2Ø‘äòÚ–í³’ùu²ÅžWîÛ®mÝHˆ€ÇÐy@<І#ccñí"Ë×OÙ&;%ÅijÉu@æÜ¢„¶D˵º}ý! ¸n¤Wù |( y§øèˆXŸ/§ÜgÛ…_„<a, b­Oª²|À£:ÌЧ¦?ú{æÂ}êFXÒžëLQdl634ÍAüÃs¾—^õwØžÞÜ¿œó"à–_öÇÔ€é68™ð(ˬ®œm± oÙYùð‡?ŒË/¿_ýêWau¯G¯Oí?Yû@$z¦,¹É>¯ÐpÍK à@*‹ w,œƒ<•J‡Fç÷¹"î½ó®ÎüÊÑ0EÛÞØ-4“S{vëvœyëƒ:OÆöV xØÎ«T³BD6Š)£^™­ e©0LHß“AÒãEeg„RÈ™P(ÇYN±ïCŠLÓ'¥k—±40ªø)ËùùäpFˆ›¶3XL`LÊœH˜EŽ1ç¡$]¶ížˆµ–@¿¯ÿmУQðpŒGcÞ]µEôÐ%,Á3G„'­“ógPÀPtîþg ”¿ƒþæ+©IO9âòpvÒPqF´6†E`\´Ø!ÍsÀ£b|˜ÏAWA þ 2çÈ+n_1ÔDÒæEªC¡€‚xö©¤ùk,…ÌpæÀ‚¢å%f+b@ƒ˜þo…ýÁÃdBÜ‘Ñ}½!GÅzzÆ$Xœ(á,CÀŽEi%‡Œ“A‡u;û/² –öþЕžÀ¶›hq'þ7ÿOÌÚ»BsÐàsZÃÙ·Œ”Ôí9f^…B=øX±¾ïû¾gœq&“ô=ºóÎ;ñ’—¼dZ·û²=ö…Ô‡·èÇ—ö9E€þN :‘Ñ‘Sj÷ª,0_ˆ8©,[jÛöÚS‹M´˜ÁÎZ˜önÀnáÄO_…m{:àQ½”ŸÈìØÑý–’—vm…¸Z ¤“}²t-8ðA¥”ŠR½©pƒµX,=àaÖÑ4ÑLNì 3Ü ÛIódžF-˜(y¸ðA—ªÙœíA·»zäsÑÿ.2ÉëøÐ…ºÌzð¡ Ú¦‰–¢5½—Ãçéìˆë~o €Çnyfßf¸ñk Á °´>“0èŠì…ÓÞ³°mÞxíh™ÜåE©×§žO÷ÛÓû— •a+šø¤–jýôÊu¥•÷]郵l’âò±µí¨94ÁëÏ›$õô­ëÛ´|¡ü ctêÜýÆ ì-·½vlC ãD*ðašˆi’Ô£†écPÃòX¦ •æŒ\œ~\+Ÿû»·ásÿvØvïçÛo¿7ÜpƒvPùýßÿýjÑÞ’è±_dÙôÓ~2u“m²„§lì%dGózÌ+ŠÇwyXÖÎ`Íww›ìVx„¤_à1Dx,vDU¦9<œÆg™HJ[ê o’²Òþð_¯W¾0¢˜er5HŠš<&'a}ýl¬o<Ç6? ÌîìOºœw…aû,ò|Ve{¤lkû¼îc˜d\{ù)xäû &[wç'è˜1ð©ßnlëIHK»…ÀðØBšÇƒ3ibàÃÝ‹”æ\û<¤‹"×àˆøèëÎŽU•†ahI×UZÍšG m¡I)Ø1Ì`£’?¾;çÆßa@ÉáüyïD¨ÉœOÍÿPìßÊ*&Œ=WÍŒqbÉ2³ˆ¬ÉY&Šn$·+VúãUàƒäÉ©’1ïxˆ`‡–׊¬Ú'…i cyjK€sà£&ïWÛ‘‰BL‚®"Ê€¼JcCáæËöBxËý÷LÜïqÏÄöѯá=?ñˆ¥Ÿo¯Ê¹çž[<àu¯{Ý´fïÉÎÃq+Ùy‰ÿ&ÿ髪2´¬Yï',J/ F«áKF¶£+°ì Ô*;ž3Dñ ÊFJì°vŠÖn¢µwÀ#ÃðÈõÙvNÃY\_“C,¸§Šî¼ôÓï‹—ðL=\é~® ¶É>ù:2×YHÐkúÕr&ÍIøò#~ ø‡a¶};l{¬ê~Ut³ÞˆÚOú† ¡n›ñûøÇ]¹GýÏØ»«30zïc´¢‹mÑ̶{£ÿ4³íäcè‡0;L;íÀ ð`,ÿ±!Gxb@oY;u% ±0÷Œë,¼PÅ|Œ†A"…­‘þá7!^1ÁöLÛNAWY‰Þ}&ñ»íDyyh‹N(Œ/‹B÷§&äO–!†R¶=Iþ¡Ì¸6äºF Õ[²õ¹{G tþñ‰³}Ûš¼nCô—H¿¡Û%#_ûDZÕ#.Pɸ¬*ÀƒÝO¿ ˆ¯I»Æ‚~¨æt˹%¹ÙJ-Ú!ˆçiu—âœašÅ´‰’æÃ9¾uöã]ð€à–[n)–Û¯‰LW½sˆ:ñ³Ovô€F²_DúrÔ •2§Óåߌ4ùx<ÏÃ. c&žì¤¼Ä'¥L Š+gÜúœvÚ4D`–| G/x`ÊZlˆ4qßbÊVdðxÀ#€/xQ—óMÂabÇG1à!¿ñ«ô}¦mïÆ½þù·ðÈ·¼nÉÌEHÞ›-{¦Ò„‘$ìêÔ`%}„~¸í?2ðáÀ)¸A?¶õ K; C i!‡’Ç#õÈ!¾Ö±Âû²h„S¡ã7G„qe(àa­‚]gÉ@Î…U”>I£d°ÃïóãŽ[N3vdÀ1R62—ÇTŒ–5Ä{·P–Ú!lAØç7™xIV¾Lkr]ìØ¹ššÓàö;à£kOìØÁ€‚>4Á|mù0·Â{T?~ñóåñx xôãÕóJºÞÕL?NØ‚Ä ‘¤¥-LVeA”§ƒÝÏL‰@Ž™¯7byDŠ$õ΄ý²H!%Z?Œrnˆ¡PuF‰¬×YƒiQ!$ô#NëÞ9·BÛ¥å}}u6Í}R•¬”Þ7oñ¹lÁc.œ…Cs« :wî'†òÈ{PêG¶ ap}Nº¢”<Ã0&ùDÄýyãÚ¨Eïi¦-"NI|®¥¾="hgÓ|®}ºPÇVÌèBZ’P“L¨î2ÑÂçCíyIó¦Þ¦’êÍ.¤1Ö¯{!c#•\® …ô3ñ^2–ýÝ©.;'»cìyÓ›Þ„óÏ?ßõ]ß…³Ï>\p666¢2‡Æ'>ñ‰]jáîÊ ôØOR;M0¦‹×ïêšôFŒ[™HòzxqçÀW7‹sìø1x^¦w¡òÜ£ÏK 1oçá$ÒJ)ÀýçM¤ Cb¸Óp«,jƒog&qªÖO°%–^Ô( µ+f$ÇUxÜl;íWH™úÿ*]¹&žÙ·1\ŸšçåöH“šÆ(üê$Møm&¡"[0füFE0<&ðCI_¥{NzL{P°Ã4œ%<â¥i³+X(Ï”¯æR%…þ$÷· ÓrºQR£ì Œ&……móx¨‡Ç烳@Cð(¹Uй;l:Æ…ccðc!À1jjòOä˜ÑŠ:#dÐí›ÇØœ³)¤Âƒ°|Õ+V†Š”ŽÀ]ŽŒþ7qø„v/ÌÊ Q…÷I:ŸV‹`à:àÁ™iôwøÐ$W¶ðÐŒԓÎÿ,ó÷™}¢ù6@–,y†R x¬d÷ä5¯y ¾ô¥/ÁZ‹ÿüÏÿÄ?ýÓ?‰åVKÖ®äëWu˜OFÃ(sdBóÞÖLû}½1äVl`À†“˜Êh‰ÁÄ Q?v.¹g…H ¿Ï•Wîš&““`š°=½müDXô¨Q¶xè+µhíç C0šCÈ”1ë¾ é¹‘?¿`¬E_lÉ‘¬ý‚”õû ºqFÛëîW¼=«U€GHÀG†í!2@f±bçIœ„ål)Ûúës€ÇàÇçvìkZØÉÑÃ1=(Øáþg»ñ7gyDÿóÂY:•¹U~µL¡q€G|®…‚ô?o[mߤc»âÑì¤ððM’€­]Z}$¿Gwn)dF>ÔFÍ?_ÍÊ¢y‡KIRsLÑA+ÊÔÊб̈́°\Ïk§hÄÏ„‚:¦@WðÒ éùùÒÌõê¶]üI÷¼ `h`Îc·`1éŽía6\%áºí h63A;9;YÃdëîÀæ`÷Ç3;ßgr€ey¤×LØ Ð&ÝÁèƒ"©§`ЧÏe¸Q×'œ—öÉy€Žœ$Æd!Œ,¶WxS e‘BÁ”úŠl±¨p ò”–v»Ê‚ @P(øÛ£` -ì˜èeP‘ÐȵoÀdrZ—øÙN;òÙlJîEãõ)ô@Äöp ŸG,¼Ñ9ø˜ ð¨d’ZÛú (Ìd|²y–[=œ¥kGŽq¤õY™í‘8$ sÞ À£ÀòÇÚ,Ï£¢$ï¬kzGd¶‡/¿CRbÆ:ãN­ÞBÏ·ŸåÞ÷¾7^ô¢á OxB¶ÜCúÐjÑÞ’è±D¹é¦›ðò—¿7Þx#:„ÍÍM<ÿùÏÇóž÷¼]©G<„%@}yª¸ýn0 ¨¬qZW®%< l8‘ø(¤žfÊä>*”1:ÉÊÉ]ršÔÆË3ÙÜüg¬Mo…:ž?ýÏ&2)ah·£`Ì'S‰öí–Àí‹÷Ëâ¦á4ܨîõ-³= JfbàƒîçÊ\ð6Ѱc÷[ÊC2$ÌE.Ä–°Å¤ßÖçþéÏÕ1>&ù?(ÛÃÕ=év ÆÎ°vlÛ8æOÿ®S£?ô!o‡»þx5™Jóy@xÎq¹Ñûp,¯¢Z^1ä ¬ŽbÒR‰Ý¤åHP$r…Ô2“`\Ù¸õlÛ}{õ%Ê5 '1N%@ƆövÇäX4öφhãx…aT ¡)%*]:ØQÙëž)LÓÀÚäÞX?ô­øØ·?üóWÃΦýq„‰I€îÜ“(טÄöˆX¶î¹ ÑÆJÎ0% „hjA8Á³ŸcwT‡äßÕyDM(Íx`ó@ja¡GÑ;ÛÈÀ}~tlìÁLm|”€îbs àôŠå±·äo|#Î<óÌb¹ßû½ßÛÖì=YK’ù—Á¥—^Š'?ùÉøÈG>‚õõu|ðƒÄe—]†~ô£xã߸cõD+·pÀ£Ï—ä‘à1ÉãÙ•k;¯­c{p4º?¶“àͽáïˆ6IõÊ ±í~ ­’¹ø&¹f|Œ”ÙôK˜mßÑ…ö¥93žã%½-$vEÖ€*I_ß8Æ ÇÔPL˜’ð ^‹¹WØb•Ú3˱2”б]üXíB¬´ª¨HL›ˆõA=UýïÀ‘íážEα<:£eôèŸ#üˆCzø¶ƒ€»ƒ… t÷¢E`v¤,+€#~E£h#Ï9ÃL?&²0Eš×/ôC-±¡XWÆÀ)Ý™P?‘ÁGÉl¿ØÈMú÷<ÆT”§#.KçÑÀ }T>Ä•FÊP‚A¹=2!±@$‰6Í;oÍåÇ´uX»…vzxÃ…}¸$›ßX¿?bíÌâ6P{Âb•úC€\ª80»ï‡hµoÈZ ˆÍ£7Òã´±…>ÃâØJïÃâ û\ra™€?ϘfWr{Ho;É:Y‰(çŸ>àØ±cøÐ‡>„£Gâ;¿ó;ûØÇpÎ9çàÔSOÅ\°›ÍÜ5YAtK;ï¼O~ò“±¶¶†ßú­ßÂúz\rÉ%xñ‹_Œ7½éM¸êª«v¬ðtOÆðKb®÷Ÿî·ÁŒÙ€i¢1'˜{tŸæˆ–³¥§@ÓØ#p‰‘& ÜËÁ‘ä–fÎp7Íz¬¼Io! DM3xâ.y3Sg bG„2"Ä6äÎmR¶Ïàó»°¦…ŠU¦€vLÖB–¦˜¡V¸eÄä‡ôl{­TÕÖ)ÔUõ(†]í¶Ðb ! ÿ·úg:°·‚Š_6–²2hˆŠu9:ÜÇ­Ô÷øe”·®~R§+ƒxÅ–𨼟–3àá¶ë M%a¬§þøh Wfe…e¥ ä–ˆå-üãΛ»gýXêÇf7¶’ObÒy€[6ÔÝ £]#ÆíÜ{µßÏAn{>¼€~ºÌÃ>Ä`Ε3âF–O–pEgÔ'ž~úq›M~ Ú*aϯ¸ä«gå´@Û ­=‚éôßÐÜ|%Úök˜Mú]‘u„ øsq 9"‘n\¼‘Z;_ó~õG5€ iõ»1€{Æ|9ÛáIö™þEúÕ%Ã'3ãqZû„ã†]"™ÞÇdédzŒIYIÝ26îñ«YYø—ùìò…ï¹êª«pßûÞOxÂð‚¼ÀoßÜÜijŸýl¼å-oÙÅÖí®ì­·äëD~û··Þz+¾ÿû¿'tR´ï9Ïyà¥/})¦SÙP]t=ìJÊè0½wÕ8ˆ9Øy^ÍQðz:7”½RŸ„’È ð‘£Æ€Ÿ¬Œg¡4Ñ„À¯µHÑËÅšŽPÚ†‚óztÇ(Ä Œ¤¼Õ€Åm¹ædóN´Ä0f`3âÔ°–ªxü4ámI¡rý²÷<Óž9(ûNeµ'¨’:%x„'Bb·`±ÀppàÇ,úï?øˆ€^Da,øˆBYèï à.f†¼òàÆ,´¢cZrݰ‹ïmíª@C%É;bí9°£ ÜPYNz;£ðjtƒÒ$Ìm¤ IDATû*IcÂ\Pd:!6ªÒö§ ‘?ε¯Yï>®f]6ìsÏl¨ÄÀ » Ø’W^ ‡áå8ØQrÈILSp«øñ×ÉžWf^¦óÆlv³í;0Û¾ ÓéçѶ›°í±ÀÒ©<‚Äz ï?%£sGX§êuäæÔ€E<ÜoèЫ‘ Xɱu2ã‹täL!>÷§cñ}G†¤2ÑɨîKŠrÞ¿dIAû¶’”w¾óxîsŸ‹K.¹¿ú«¿ŠÓO?Ýï»øâ‹ñçþç¸á†ðÞ÷¾w[¹{rÜ€\p®¹æšdûüÈàÒK/Å£ýh|÷w7žûÜçîBëbyÓ›ÞxÜã—ì;çœspÞyçá?þã?ðž÷¼gGêI'›„p!, Òÿî™ìèÂaBé`I7à:Ð$GeKa}PåBʃÏ&®øR„_™4³€Ç@Y³d„pI9s’>&|ЬÁ““¡4sG}úeZ##Žn¼¾ €Ad{,`Ò®]Áfü 2õeχ±íIB*Ÿ­êÉgløpÌ’cÃ"0;:`‹³2ÛÃrfE£$£ÿ <Ôë,ÜÂ~¡Û:¡÷X¸ßÄãlƒ¤lAxÈW ³C: +µÕJ.óLòÜ ±ÇUVêS;ö„jÀGiLÌ'Œˆ¡â؆ÍzØ.yüEO?é_œõQ2 4ƒ&ºÂîðÁ ðàÿƒ#¤väÃ[ØÙ̦_ÂöôKhÛ#ýø“tã–•Ãܘ qvÌê–g*È"a£ðlLúW• NV·ácMrÏâ¾£®²&Å9öW$Ê=•Æ\™œP…—Îöû¶¶m|솼öµ¯Å[ßúVüÅ_ü~üÇ÷¸Ç=’2¯~õ«ñº×½nZ·ûrÜ€Ÿýìgqùå—ãÅ/~1¶··ýö7¼á xÿûß?ýÓ?ÅÚÚÞüæ7ïb+Ûo¿Ÿýìg^x¡Xæa{ˆ ΢ëñBÁŠl9dlô`GÇô蔢 ÿß‘²DB@˜ÅØÏ„É¡4M9¤}¢Ç¯@ŸL+Ì¿5 µVO­RU%µÊdVÙˆYz‚?aÒŸ‡ºÕ-°4¥#1Ö¥ínß FTœ¿`˜æê•÷é€Ov;¡¸˜Ì{¤ÜÏô¼mÚ?.ÎwAv‰¡/ÄÀva(t{î†l(á/‰Y<ñÂØàÁ®ËÿN¯=ayXáúÉý†ÝùþURÜ9;¥ì@C7šh¹T—~5â$O¨™¤À‡66 FYöt†µß4‘áaL`V2UƱªèÚ’&„Oø(ÕKêˆC[Ê,1Rv䯏Àö LÁvêYNU!àï+y‡ù³ò^óþWñdõ¢]p²h9{Â~A'ªÑÁ¤>T`ÿ¨Î¬  Z½$}F§Pû†/ ™IHß¡€„í‘îŒÃÿ¢]왌Ibš“ôúe mgC[vv¥˜½(wß}7žö´§eËlllàèÑ£;Ô¢½%Ç èñ‡<xÄ#på•Wâ1yŒœœuÖYxÛÛÞ&¢Z;)ŸøÄ'üïûÞ÷¾b™³Î: ðñ|éõÄ" ªD1NâwÍ!t9<6`Ì!˜É)ݧ9±c€d©†Ì“T’ˆ&«ƒ”s%yæëCo‡\fƒ"ªKk¿ö_¢ƒ.Pø’”ádJÀ [y²¬öæ$ b (÷F³¶WníùpᬌÒýXø% ŽÂ}•Ø.sÐw)ÀÑýטA¶{@"€i®¶B~Gá*[=°HòzD!-´Ñôž0ð"º:À®_cye*¡¹¼G‰ïÛÙ&¿s`G tÔx…3žMÏ‚X€ ©¨+yM:¯¤Æ;ý4cœýÁqɰ&€b®Du—ÇíðøþKFS‰‰áö倵^‰E2ØÐ¹O\AØÁÙl øðcM|$ó ôþ)lCú>©óé@É‚8âyVÄb W²< "ic_qž;'°–Ÿs P²ßÕ#çgŠ\Éoñ¼åq ¯—œ¾3F²¶vùã•,ZîyÏ{V•ûò—¿¼ä–ìM9nVo9óÌ3qÍ5×àç~îçpå•W⛿ù›ñ›¿ù›¸âŠ+|™N8øÃw±•ÀwÜáó<NN>ùdÀ¿øÅ¥×ÇfÇú ¢,×ÌÖšµM7àz‡„§4‡Û'ž‡/žw*κñŸaÌF?°o&Þ+ ›Àºr¿ -š\q’Ñ ‚â›"Õ¹%ÆÂR·]Ѳs¶õk-<tÈLÂéò§ô:óm(N:s{Õ(%Ô)2dY¾ˆ†­5¶bÅ‘™0 ŠYµþÇ…#3—Ø0MRÿ¼ ”xorŒ À÷ÝxŸ|Ýá]nûzõ{Å´“ëíVVÉô';óm¦«tX´ý±“Ð~t«²„%¬ýÊ-îò…íÉê.˜vÒ×Õ3Ìlx§RÖ>³ø?ù­†´Pïœÿ-%‘ÀÁºåiKBY<î9åë €‡ày´ô~8ɵ=–¬qÅçΰ;пïÎ Ç-1q)ìÌ éû¸–ÃÏ3ùSèä¼\ÍRºcÍ‘ ®Í®¾]fÝ£¤^©©NìY9#~М×vc ÈXC~wýf"÷-¶MLJ])Ñø/õ1 +t†Ã¼"<{Î÷>çÄ!ýSÜ_!tµ¾ÁÇ-È2×t~ÕM;Ý®ûïŒôlyECòŸ3÷öp˜ÕÉ·í Ûm`á#¿wBn~ÿ[qó?ü>l»wï×NHÛ¶øìg?‹<àkÓÌ®¯ýëq¿ûÝo§›¶'ä¸=¬µX[[Ãk^ó|û·;®¸â <ûÙÏÆ»ßýnüöoÿ¶N9å”]mç‘#Güï ±Ìt4¤e׿ü¿~#úÿmg^Šo¿Ïメ‚ ¬é@Ѐϰy¯‡âÿûǰyÒÝxîO £E¹ä„!ñŸî%?xâwfGÿq´]^¢‹’È ˆ;è ¦î?ú6‘2äÔÖâä;@áªõh&õGJ©32Ïž½dEÛ-/Š~ÉÑîü²’…º8ã!J¯a;?’ýC™üô¹0#R€É³^¢(¼¢Œ ÑaïM\Õç€õ:zca íë$ÀG´Ï-AÛ*ðY9ÐÂ~¸cÃïN(p!ÔØ£€Èqàáö7t:™C sÒÞÃç=}ä=KAÉÍßá °£6ÁªaFršàºgz41™tó³m7ÑÚcÞç`‡h\V¾ _U4™º%HeàÃ÷U^‰æFæ²y$·(_AE&‚—BMwʸ({ö—ËBŒÛ¡sy÷Û,ð‘?X?§OðÕ0ðãj¶p̪jÑú‹Ò?È÷#çL¢ï«(¹ûK)0Ì4!zBXú½ßEuPÛºÜvl¡z•w@&çá}l²P&E•sMwÝ¸ã³ø‡¯þï´îÖÀ´Ë99÷±WàÜÇ^éÑÃø›—ü—¥Ÿo¯Ê ^ð|Û·}^óš×à{¾ç{`úyÇwàÚk¯Åïþîïâ/ÿò/ñ|`—[º;rÜ€Ô°Ò“ž„o¼?ôC?„w¼ãøð‡?Œ·¿ýí¸øâ‹ýÞ-9tèÿ=NEÀ­¶r ',½xɃÿOX;Êô Ò)Žíá _bü6 îñåSðÀ~`?‹ˆ"ŠØ¶}lÀ¤É˜½¤Æ—cL¤“W ~f…ŸsûC”ŠÄb¼,¢¥ÉŠ¡Â AÜ'ã—ñáó <è2©r®‘ à!‚@qéÅZc„%4égc`ÒÜ ¦9ˆ¶½íô–4d€/J†ŠêEÏ€Ñ6®CÚÙÛˆÁa¾R™#¾sÎqIød«ïËÇwç kw*ø€ÕÁß,Àžq€Ä6êcl‡¢H@ÂBÂZ$’ÐϤ\©y.¥ýÒu¹û*ê)âÆeÈ«9 ¢1ÁÉŒ€M ~Hb&¾‰`çygA ë¹äÔóðè{Þßÿ¿kzoøkw´ +¾ïû¾ïÿûñô§?MÓ i:t[[!¯Ù•W^‰‹/¾x[¹{²øà°%É¿þë¿FÀǽï}oüÍßü ^óš×àÖ[oÅcûX¼êU¯©<;)÷¹Ï}üï¯}íkb·ýÌ3Ï\z=p`íNNÀÁÉAœÄZÓF4ŒfÞpp1ô‡n¿ßû›úë/´ì û§Ó›1ݺÉÇ‘ûLç…eÒâ¥Æä2é¶ð‘–*‹VµÈ}‘–WÌ·±^D ¯ÐŽêXèBs> ¥UÒ8ú’¢#ÒÿyŽ 10ÉÐZnÌp#LQL]Y1·ˆ ‘²º0ü;år ñëvHû¥ßË1ÔÆÅÜpá–¶ c“KNêžòk’û€={xñ¢Ý§‘Æë\–A®˜å1 ðh‘<2ý¾JŒKúÙ­Ö4'ãïŸý|õ›~°SØöXx–î\´û6ð>Û‚Ï%É©MºÂOöÙåi`ÌxTɨLÞu1DA÷Žk««è÷/iN-Î};*šãÃ%8¥}.ú°~™Œ£(ë0ZûÅ{“Ó&{“>ZSvÇ\ M8x–ÑŸôefcÝΗ ûHIäŰ:íx©Ÿð1Ácí´?_ŸK¦ÿ-&„VúÔüs‰ð>(zå<²fhÖºOÿ{%»#¿þ뿎«®º çw¶··qìØ1Xkñð‡?õW…Ÿø‰ŸØí&îš7½òðáÃøùŸÿyüʯü ÖÖB³ú§{Üãð´§= ?ÿó?¦Ù]ç!yŒ1°Öâ _øN;í´¤Ì¾ðÀCúÐ¥×Ó‰4ÀåvaE‚iï•L‹ÞØ@§,Ø-ÕcÅ æ|‚$É{Hj×n^.ïa '˜EeÛcÒÓ² ûCa/”E§HæËÊåŰžœgŒ?$ mqý ¥ïÆ?#î"³E*Å1( Al·Àš¨õrp'ÕålŽR[¹^ÒΚøîl{Õý]ûj=ä*Ûƒañʾ¾Þ3åõðŒ1Âö`’S®¼§+ŠGsûf&„12ëÃoºíÌØ‘=¤%®[Šhõû+”ðð¨ðrCÆh yx»­Áåt²ö(Õ‡1›}[–$•Ôê*ŒËJáÞj¿ÍÍ…î>uó‚c}äØeUy˜×5b¡hŒ)z¸j"…²¨‰¤éøÂØ:È!³<Ë¢@] ¼À  âëC¹ÿÙdÄ5y¹ÛÃo—æjùQ£x‚K™éT`Ê™[åg™—¤ç’ËÄÛó —ó©ëË­|eÛª²Æè+ÂÉC¥<£¨/ãÐz¾á¾»÷ÐçVé ËwR C–=_®¤J®¸â \qŸùæ›qÛm·á¾÷½/Î>ûìÝnÖ®ËqÃôx⟈k¯½ßò-ß‚ïþîïÆÇ>ö1¿ï‘|$þå_þÏxÆ3véqÚi§ùdªŸüä'Å2ne–Ç?þñK¯@D‡b® ÷8v‡µGaí&`ÂÚ£ÙVKp¢xJ=E¹Äê(IÆcÁ¼žâ>úál’N¼¹OM;5O‹¼½È ©`§d³¤‹µƒþ óÂÚip1/D'Ôxª¥æ–(U±Zºá™¡0cF0ó¾V³>’ûOëäïñJºcÝ.!‰e·]Sž2×*æÀNÓ–ÛC¼øŒÉáYÈñȆCm»4F‹~>.<™¨?NP¦bº(zeÿ šæD´vж=õGÉä«-¨íH. oñz.'º‘+yÖÇX‰™Tä9õçˆ<~CY+%H¸”áÚP„íáê7a…žÛ#/ÎeÞn*†Å9Û¢!‚çk2l¼!mu¬6 kwHX¹dOZ Úv+x&Ù^<|Ùð(‚CHI£g°¦cú.ãÛk&€3 ùØ1T8ÐàŒ³Óðcªµ3ÛvɼmÛµ­¡m@ÌøPîK”¯§¿.gLº•SèõA |™°}Õ&mÛŸ«¿Z-ôüB¸Šhì—„Ÿ;ö°[÷ŒÈ8Ö+>® àPu€6}†¿Pæ &|>瀇ÆT‰ú¡?6¼Í-„u8̘f`ŽxM@Èûô8©žN*XkÐï;-šcÅû<Ûýûàæò~P]´K4ìrÀ,Îá“_  /³ $Å;*»’Xî¸ã¼÷½ïÅ 7Ü€/ùË8餓ðÀ>O|âqÿûß·›·'ä¸=jå1yÌn7Ï{Þóð«¿ú«øÃ?üC¼ú՝޾ùÍoFÛ¶ø©Ÿú)Ÿ¬t6›á™Ï|&>÷¹ÏáꫯÆYg5ªžœtƒg zˆ+—øå"·zÅu cšH5(¤òÀ6#“H«(Xœ"+l×Îç©ø~U’xÂ+%%³NÆ*—L©â e€Pï@ ¿Îcl Ð4'Á˜ƒ°vÓíÏ÷÷)M`ÊE‹‹M’wRÃ8'ƒ¡¤@·= ¸({’à´B™O”Tž`$KœätàAÿÓP”x_X¥B_"Z>üò|‹Ò¾ø¹ÍÀqJm¥Ÿ¢0 ¦’Å™ ‹Rîh=¼ŸàC i¡€GT©Ü¶êqj.à£í眶íߦl€Dy;ÒöH€‡V9Äxs€GÓD<ö;Îm» ض=†ˆqî —ðŽF¡ R™þ>ÄÉSu°A¼®Š–…y"à1(sö<©ã ö:Òe›k¥!âóÆÀP rD•Ô½Oôújõ^'ÏŸ°Øüæ˜r8S²YbÖ¤ä(=k xHsIõÀG^ºç.æ,¨é•ÇóãhBv–(9ŒýÓCîiø(0¯ŽÉdžñ c¾¬dçä–[nÁK_úR\}õÕ~ .—_~9^ýêWW¤Cøú–Ýå/~Êi§†·¾õ­8|ø0^øÂúNøÑ~¯zÕ«pùå—ã%/y‰/ýõ×ãþàðÿøxÇ;Þ1ºMf³;ÐÚMP°B£wù:¦½¼ k¢µ›hÛ»`Û»»Â¶›0"²Í¦‰‘ ùþwïeUp-ƒCÚVV“e³Ý½¤÷›è þqca~„°ƪÀ›aÎ ¤!.‹d{¬dGåÏþìÏðô§?'žx"^ô¢á;¾ã;ð =÷º×½ü2µ_ýêWqÓM7ášk®ÁïüÎïà‡ø‡qèÐ!<õ©OÝíæïŠ»Û™?W²4¹ë®»pÊ)§àÿzØÏà`³^>ÀOFŒRè•DâØëƒÇ—óDÊoôÈMzLÁ6ÍA4æÄn›ÝêrX“VU’Fˆ(K=mT5²F°Ö†&¹gɽ41i½ ³öNØöˆÊô ñ®š' 7Ù*(Z(GFÁO))}Ô çËeêãÔ㚬ò4¦˜ÇS’Ô.©n+÷U1s^p°ïˆ$4;2vccøvÊ8ÙfÖ!æôࢾóxŠu‰¡#f£7xh8…Oâ ÇX˜!îRFg'ÄÿS¶WÂÇ® õ“4‰©òîºû"<ã"\ë~¼Ë5µÝIaŽÎíûMŸ¬´iN褟³þð³ñø·ÞˆÍ£@;»³c¤ôKšúþÐßGÍ€ªÛrãf¸ˆ !/ÃÇ`!Üpá´ó\XkÕ ‡6†š&o‚ðQ›C%œS._šƒsÀ‹Ö©çÂD–G¯ã%«ÂPöUFª6Òg¯ÍrpÉI×uЃ²ÉHãoéûZ“·#ûÀIjÁª÷LyÏ¥fÒ­ÊŠ.tÆÍ?f=úø2M·BŒ1“ð»Y‡4öeWðÊÈBãþÅ´ò;5ø…q_ûÐ㲸Ša;Qa>w»s–ÙþøíÏk&l¿ÀæˆêÚÊSË’yûÕ "Õ‰F@¢4Ü6Îã1$Qi²W‰®×t½$¼GZØ _ Æ_ÇÈ9u¡€GÍa­ÙñÏ~7¼á 8ï¼óð®w½« ð€“N: ôG„?øÁxýë_¿äîMY…·ì©Í4íévQL¿ÏÒcv‡ß© øT>*ïBRüäÎ)~¡J£KJ(„\ø¡»%cõL‡u4ÍÉ0d5gÈ•¼!C e6M}ýQâFñxwŸc/ -“d©ïWæiq¤S.Hi­Ôb>„ÃoÈx‹rõfX bÙJOcºÂ‰ëCmÔV‘AD媠ö *1ÔÅ— Ô*á1žÁ$]'ÛSù V;¨WzãĦt C¼hQI/<3ûµ¡õ¹Š]8ã™®®î¤>DÁ"6(w&[ƒ€og*‹L~É¥j®¹¥1(Ì¥sÓU© ZX4€bóè‡/é õF¨ û´Ý¹Ôä×ÊsMWLŠßm“•!ÆÇªÇrh IDATÚ»ð.Êcî€ù€‡cÌ63gÈM@±?`>Ä* ã[ôÉcu>¨«µP! €¥ÉÀ•èÐD×sÂô ðèt´žX(ÐÀuT7/¦ïÜ`=£Ü†¹ˆè_œÊÂ^¦o+cÍ*aèþ•?û³?ÃoüÆoT¥8 2™LðÚ×¾/|á ñ²—½l9Ûò=öt¨p5N‚ÎËÅöE…i<¹0)$žö²²¡ò6Ù âÄïŒ,zþ`ˆµ³;Ð~éͽØ)Áñ’q[€ŽÕ¤À‰+´%§tá%9ñF*‰È€aù³\¸GÈ@ž¨>¯€u Ù`a FbÐó¶ ÛªW7Y€¢—>€äþ'™ö¥ø]·}ðA·– à#¾ˆ6Y}Å­Ò‰ÛÞ²ÿ‹ž@~GØ²ØÆ-;KÀNºßW> Àf$ó¿,L¸CÞ!úÌ­Ä `ïN‰ÆÐýÝñÃDÇé{l1뉰²Œ|ò|›ƒaª•Afë‘Ø‘0KÇ…LøˆV¦H«e$¡ÅbÆÚBÀ'ý¸Éõ×OjW&©6¤ù¸4~VÔÇÏÏÁ øˆEI^ ý9Õ´5~Oâòâ<\ |(åŠ+÷¡:àA˜p< - UÉÜ1|­ðÊð#É÷áöKàÉû±pYåò8nåÈ‘#¸ä’KF{É%—àèÑ£ nÑñ!+ÐcHgÄÖ+Q’6+'$ã`‡Dß«Eäç^îÕΈ7ÙIŽÙÒýÊ3á“Nàm{ÀÀNÓ‰I¬"•ÇÔðNM•ð«ŽB}dùí¥pŸ,k‰‘m1xÄžƒ|ÐrÙüÃp‘kÙKõFJ ÷ÎQ[EðcðAÅ4„­¤ÅXuøˆ„][HFJ ÅÞgO8¸I÷‰"°Él Óƒ¶i¾¡XèÒÜQÌ£Š‰ë ! |tˆg;xÙâ¨bý˜ØP¯¯w4Ú_bõÅ«\ž1LÒUHËv´d¹ËÝÒÊü–Â\*] WHú¶k“Ò¶ÉXØËžiñ~ Þlz cþÒq·0usÑ$—rçôÅÀïKƬwy\0ñ ÆÐ Áy"mç"Ç5À¢x°~AëãÀG'îºdÀ#‘,êeÞ°&‡‰?J<¤–áùW€QàÁ€™ÃÅ{Û L¢{Ò)òýûÂÆñ}³q‘î~¥¬—„9Fú¦XW^H¡ÜSå>SÚºÿÍÉ àCì#E†³u„f‹×²À‹Ö‘¬ùò²¤^âøHç…`0[d€ $eE0tè X‹°b9ÏÐ÷~^`ª;iÆpvE8ðÁ«pìŽh¨©3©w°(Œ3™œ€µµÓ3Álû´íýÞHcšÆº‘Þõ¡Œª$GKßW,à£/&¢“\ç¢ó£UZå·tâzÀ°$¹¶Òø%2ÝAt¬¸2’Þ@D}wv‘áŒWWR/œëø,¨%Ç—¬@} ÖnÃÚ²DɳíË H´=xÌ+~Â`ËeòvˆàŒ™øp—šsd%ŠÕ ”^ƒu³fr/3loÿgÔÆde Z—•©¦Éùúäyë·½ {× ÛˆWêqËnúë”rd'ÉŒw¸Ò#-*f‘2‚î8=aèÀ>5·±.(}¼^v£Þôy¾’ àCPdCH<ø¡eÝ$¶õÀ€ Èc‚É=¾³Ãïl)àªi"÷ôúº6vƈxø}¦Á»_ðh\þÿÃìîw'Fd´”'õ™õþÞ+Lc1Ãlö%3³® Î{„ÞÀ¤×_`,É[P@„rIùÁ']Ì,*}mš(IlîPàC }+‘‘§ØŽa~¨õÙLs¼ï)k¸ôw3åT¡×SqœÈc€G­r8ð}áù=ŒËIÂßË! ­+Ë2JËòlÂ]æfwÔ_22F>eWEà‡3ŠXyZ¨1¢Ïs‰ô÷SóðŠÔöJQó2¨4û<Ø¡ ˜50Ѳ1Ç…âø0’îwÓ„1kXÛø³ka6½m{ ËHÜ›>,Z´v³í/áàá£Ø¶S˜f½+fõûS†£½c@ ΨĎWF<$ÇAÔæ¢¢ üHXãî¾ÎÃäýIê_5ï‚”¯®îüÀ;-ôErÄ%IiXTÊ”%;”0ÖÀì`ÈÉNžk7ekkkW?^ezìé–åsõÐÑR\ÉsÞ0®LÔNæÄˆX¤áœõôÖ‡¶O¯W0fÇNÆlB³žô›m&D“¡Ë!®Ã`Ã+7ÝÊ2[}»”¶xÐdâë³fôk0¸†qÖ€|Ô„ºD´|@4BTažC¿™¹"żfâuFÞâŽÅx\¿S`FqÖÇàƒ?›Z*xvå|”„¶-jç@ö8ƒC¬­íxܛߋÖn¥Ç*Æh´J‹—’÷¦àÙÔÜ=¯{&rŸ|ödÛ°(ࣆÁÝÏü{Ÿ›r†O‘yÂC‚È15yhü} Ù$¤39ˆ_ ½—=ð¤I‡L®ï`u •ÜýO¼ç†å]p€Gs¦9€Éyÿ76O>€~¶j|Ün$¦ìžnœmÛMlÝü³pËÎwÇ5:»Ó9üÖ€HÁSÃ2“ô:(Kˆ„ºÌ-‹ÐÁ*æÓ¡,Òø‘óíL|÷—!Ò{;<Ѳ|l€HÀ‡/ÌÀÍqµ‹¡-»%7}ð-¸éƒomwÎÙº›ò‘|Ïþóq ' >öرc¸îºë–Ъ½/+ÐcH¼ߌ(1Ý–²bXDè༎6 3š`Õ1´c|ˆŠ‘4ése òò»Õb¾ÂŠ)ýð0ÍA²¯÷BÛ-’pqì¸Ý’"+f¨Ô}ê±ö¿9 ?üïÚ(3Tº©â¡ªKØ+œéµ‘ëà‡Ö •¢å–‰ÎÏXøèŽUiÃPQZQA‹ýŽ)x%κP_~0ƒµ› ;ÏpQ›IŒš>ø1ñx9hE—Œw®øè¯eã#:ñð‹×”J ø®¦¨—ú*Û¯’y=¦›£{âXYc¯~7ðÎëÆôÀ¾.j×¹(°C OÊäáÐÊu€BãÇÀégþ;ÖÌ:fv Ûn"â:oÎ"5Ô®Ÿ\¢Z·Bl«ß[.b0R :×8 4/~R;–G+Ÿdù:XM˜‹-_]~W>IdÉ€´ÂÄŠR"4§$>¨ƒ`´ÔéÇ»œÉÃù—ü7L7ã/_úÐÝnÎÒe{{o|ãGk­…Q×\ÿú–豄Mô>²¢_VOJP¨/&¶Êi…zåú¥A7xì(S˜d³RåÀ®¬.%™’hžªah&˜LÎÀçõTœùÁÿá·E9=šƒ0æ‚wzÖŸo(ÊV²ò…$%p ª¯‰ ùØC씕þ’ɽ*ŸÙ>0]s@H´Tà"“¼%çà¬øˆ¶e/ ¾OcˆÚТá2°Žln=४`xŒ~Ÿ8˜ˆrÀÀ€èÎ.)+Gì+CŸ}¾Î!ÌÀ"ðW Øîý³ä óc…Ô7uà€“ø>3 @y¹ÕXÞ7sù”„}¢Ø¶ -iôàåv·Y¸Žå¼çä¾›¦¢mãò\Uþ;gH à{ÔæÅPYú#ë½+ øäX½ˆ9k"]WÚæðk–áÃQW!Õç_2ðaÚ¦] G:ß~‘Ë.» gžyæàã¾ð…/àoÿöo—Т½/+ÐcˆÅ,ž¼ƒ«¢lr/Ô¶Ä F5ñ½ä—ËTÂq¢DGÆ{!h|ºæìèï3&hÛ¯àìÛfƒMƳÑ1=̉€Ù€1øÌ·>üÀµ˜mßÀBÁ4i×(fÃCòGKçEÌ‘Xôûîhä)mº:i£›B1r æ"@?j€ ë#jãü, I›hL%}®¿ Œ{Âûb0ÁµÏü¯xÔïÿq¡½¹ç؇…” ©}8j;5Ô„zªKb*1;*@[©~¾œ;Œöj˜é;Û#ÃÞꄎAT…[Ú®†+äÍÝ“ìýÒê]7.Y&,ëÊß=b|ñ¶ `G‘åAœ`W~×ý³¤Ì R6;F$Ï<$-u€ÇBV–+={Ì´SX#÷±¥'[LŽÁ½ødLÉÒ©v,hŒ—Y²C„Ÿ«ŽE£\¿²-š¹ÈÇÀÚe¸ƒÐ¸Ž‹>ÌÇÆ«ÉmTôî¯#ÆÇ~‘3Î8ïz×»FÛ¶-Î:묷èøè±ÄÚX¹òK—¶Œõ!*{šÂq¹Ð˜&þMrU W(#joBq¦Ta¶ 9ês•½@V>ñ™”ù¶Ø›Àv95¶ïú«P?M‡0àÑÌ!œýW`íQl Ù¸?>÷È‹pÖ?^S^oyIù‰¯¥†UbÑçh¸òAÃzâóÆ H_„€&üÐXIòNÚV–ü­¸:BÍýX¨KÜj Gë#ËÒ–$™b«ï/‚ôv~ý òÄ¿%ࣻŽ-<üçvÊÚ5¡”Ö—WŸyB.…¼Ï6Â;¸`ïWR…~PõžŒM1ÖÂY†(èªÔ¥:†çs 9sÀG'= _k]hP¨8`|)å¸ZÉòåÜsÏ}lÓ4s<Ë ôØ2ƒ1\ú ˆ½Z€2 H9e‹Ðãb!ƒ¸Ï[¼žgÈM$ …Ú“‚yQ¼jBq×Ícn9ÂÊX3smPFˆ¿w3Ànuóš™Á|íFØö.Xla6ý4¾ñºCØŽ;pr‰Ø€ÏëAÚf%‰š>©ú¼“ˆÄr€0æOØŽhbb«âÄ·)4þú¸c¡í%=&˜X<ë#´W`HŠ_t¼]]%G*ŸU¸;‹Üûø÷,>ú•…}ú·ú”ºLŸ×±1¡+ú4j9n‚,«Äv—_e©IsÐÐí#¬è÷mìÆg=±eØ‘ ›Ñ¤øì3Jy­².å•(„aj÷"͉TÍ=ÆX8‡@U°F-Q›+—9–3AÃŽf¢ m´3q¾öz uIΟÊç•_ÉŽm§9L6ñ6±Ž pÌL”wyÝíUÇHÕp)à¡3P‚¿WªÐ{1'À6·(!:ÅPB/`uÇ}RåÔº2M-²­e)ÝÏ$)þjõ–¥È[Þò–]=þx•è±ÏD\A‚L¼Y°ƒeQ=Êàٱіckaœ‡(j$1#öGÅõ©ŠF-‚Mš* ø!°>’pÀ{š¬™Â')e`‡µS ý¬ÙòT}‹­Hé˜möÙ–Çxh¨PeÈo“Ý¥j™Ä™rÔ³@ â‰:”q?$ð#x™£K²y C wqÍ«i3ß-xZDÉ)´ì<µ¬ª%lÙuæâîkDRL©‡1¾8-Iß ñ÷L~6Ñ5Tþšrâ?B“¦R¢ûcÅ2 ø0MÊ#®¢œK·È󓚪mVž%c}P£È$ÌE©–ŸSz6ѵ0D¸¨é¿Úý¨ðRŽQðKËiÒû¡†EÈ,Jû“dÑsÙ\€G©niŒŽ6žÃnx‰yX‡Kã‡ú¬Ü<ýýL>"£V 7òmç»IºŸ‚dç¼ÊcDà•ü—Øå\o½¾åÞ+Ç6À§žöR\øŽW`6›’v×Ü÷ ¦ÄX N™cxð[ %”YÌr©žéå$´EVrî2dOäæú:– .¸`W?^ezìq´SâYœáÝÄÿéaµ“&™„f¸è&÷áÒ„P¯LˆýVÍœ¢èdKÞÙMÆý6…õ·i†.wÐ%%sX3ë닽åªdh²ÀL¼²wPñXC/oq{êÑ ¿'iBÔrĬ*òê0Ò9bƒN3 /LAAúÂÀÖ‡|DÛ¨pÑñ·\i%†e$̬ðáþQ–ióЉÿš¬bïë0¸°™T¬¼ì}Pû>àÞqnDø>+½ª±œ>ŠíNŸeÎ(ò˜®u©9jš!…°­ÿKõR€0ãÙŒr °ÃNœWZ§°»ýÜ ëM•húÑá0éÛ®ÕÃO1àYV—­doðNPâ3Ž _¤äX‰XeÀC3@ÅU:Èùãð*v¬{îþeUÀHNècji…3I ç~{À#ÏìÐÆ@÷>9}Ë]ó#þâÿÃÝmXá«ð ß;(˜ÏŒ”H³€H_ï •¯…‡gWǪÁsÐJV²‡dzì'aô÷„å!fµglF5ä@…ó´ÔxXsYö«ËçÚGE¸~ ñÜ'ֆߜ‚)y‹ÂÄ5é=Êp1ö “Öþ™ü[¸)&xô„•5 úí¤íœV«­#QéÃdL½BdŸÀòH€ ¾?šÒ^|$24†›œ_Zñ§û= H\”ÌŸÌͽOàÔ€P/€òù;0´3Ã6bàUÔ¹‚nÌ=ðïþ|ãuŸÄìØõª2*”ù¾2 ç7ì˜ú6¤Æƒx”͉mˉòÒµ“áï«Øè{_î{"•Þ?(G3M®ˆ]»ÐšÌ32Ü"c-½¡ Da/0>rmÍl¢,sÀdaœXà1áT.?»d{"c#sàGm=þ½®;ø~ÂÀC]Ò¼2|?BxÜÅÂ2MgäÅ@|r <¤UüÒ>5ƒs™>WÌ‘»ÿvùà…j éžFŒºôZ³«fØ¥©>­·yì;KeˆÙ´M»s!';y®•²=ö£ò ðÉDC ŠU”$ }fwÛD“c_ºý1x#•)IBÛ‰|ˆ¬-þ4iNêùî”ÙØ­Ý‡¾ÏؽUXô\IB ˆó!DFïCÒø}UÀÉ]Pb´T—£$W¶‡‡ŽÅ;sìÊ(È{½“2™÷¿Nø¡½q€“po„ëMóÞ8! %öüRêõÄcíQœý¡«1#ÇÆ!) –¥ìMO¥<ŒÙèïß´o·$'Â<°8 €\й?8kDaPue2Æ~tÒùÀŒZ‘B=r´þ"¶ÁkQîáAáqQà#a¹ `íh¹{Œ™ø)'÷Þ/›ÎžôUM'˜7¤i¤¨Œ¥ð£º%CóØÐÜ bŽù€ ï?®ÿº¾Û…Ì#ŒüÜ®nB6iã„ìðèÿf(N6ç<ëòÀ¡×)gá»F¤¹m@TWi8@‰Ø,y¾?'+¿S2ŸÞ¹’•쎬@ý v–€ "ðá¶»"RkÉÍŽ¥¡3õÁQìVVò¥ˆ(I>ù˜à‘da.„pìðF^÷[Ù÷†^“®baéþpë„+΂ÊŸ©ó @V24Å/}æyÀJ9ÒzãóÆý Šµ3Bܹúª8‹€ç.ðç,(äe"‘1À‡«Zc-(@bî}ˆ "¼Ïs+MÝ»ˆß!ö 'Hà+c{™°oüˆûX÷ÛoKí%lG~ŒfH)쬜Ç-›7¡L a?Äy1++j?=FÛFǦŠ=g=”ŒXéøx??5Qó™²Ñæï{>¤…¿¿<¼*Î2øÃ@Þ#÷Ÿ7èX0{,{~ï£ûÍC†¾o‚wÜ×ÅE¶Rðƒîãu á,º±™O;N>‚ž 0<ÀædÓÀ˜uL&§àÀ¡Çb{úlûÚÙyꢾ¢@ü\K€]åGšÏDÐFÒ[ª®…ŒE¦qýs¦+;I«ûÑúº:†Ï³I>©óHMÈ`©Vl_ÉJvSV Ç~’%Žý¸6þ]¥º"ð Äj“žÒ¶QFHɨ“Â\ˆ1‡o࣓œÞçò쎄 -»)Çk×ß ð÷[§NV)G¥Xg×ä\¾’tPÂ[2àMÂ]|”„@DÕCSI/ãlÎLšÇ»ÕÅêwÛKálZl0¥æ&T]ÑKØÁ˜SX.ªÖ;è~Êìà¡Y@§"E9»bF–_3«XWR‹õ1`eK‘øÿ¤ ”>ÀYìÞÈ«›„}’”©†º À‡Æjé÷Éi…Ù!mp/Nó£”ÁFóIl¢l^Zžê1•’äB€2(GcÒDaC–&alÓ>¿@Çæ¾Zxœ–»#5„kßÅÐ×KÀ‡OrZc&0fÆLИƒ0æ`6»óäØ%f ;úmù>S7ÝObzHáÂó_-[¶¨}0Û’g(9Á  ·ú¾õs'wxøž™Ä]ÉJŽ#YûM¸”y^Òò ²Ý+ø|2V“,QV IÊŠìƒlÍÀ¬1$v ‹}§ «>B¨KÆô€ójuFŸáìMï÷bÄ   ×j.„¤² GS¸A$y“©B­úBŽàä+êÐp¡(_Ô©ÏŽí€` “æ/Ð(É“à Z­È®ÈÔ#(¶ù˜äôATàgé³óÀ}®ÜȤà >Ĥzܘœ¤ÀG”‡&fƒÐóÅ@Xh‹iÿ_¸‡ªòL½Ü:€š53Þàj¡u(\/ÜXðîˆì 0F@‰P©`q1VÝÞ ‰@Ý(ÜÅ•(ƒÚ5E!-@dÇm¨caŠÇ$žúz)'©¤åºûnÌ:¹÷ `Úüjdõ¨šÜþXü£R<Ƽ{¬½0nååte@z Ç6? Ø.¤¸+ÓFù=B™{%Œ·t{ŽÝáɲ<܆|¨Jq^òÚ\fCVÝÑŸµ^Åè+«á½ÎËߦ…ÕQ*’Õ’µ+ÙK²=ödÉŒ±¯WH&i2–ê’Âi$†È¼€G©|ŽÑÀE†4°‡z·“ªÕŸzoÃz¤e°ý=ãHÔdî%÷ …xdIœ35F£r)Ø¡¯l!µ—@ÁŒ ÙÁ˜è¼E}1ÇšqǛ͟C‰ñ64„eޱyR؉èÍw‡Õ—;®dIFH²\^oð¹$¾ñv­¯•ó–€Þî>J¬žR¿ @‡Ãâï BÈC¢ìÊàG1©¤>HÏ¶šª€‡Û6`Q‚ðƒX(#ù“Æ`NbtKí‘ÆFÁ έ€‘ËóÑ]‡ ~DmFüÌ»ö†~VFCÕû@ƒUv¯oK)xÐs[e,$À€–~ÃØsÁâú ]ʈî'­¡µÇд€µÓT‰Æ±ú{$vG~žh}ð¨fJ dá†ÑÒsîÚ®okðDse1Q†Kh7` ‰$ ]A us½\¾1ۣι’åËáÇqë­·â /Üí¦ì¸¬@}"z~hû©˜IvÔæÖ(†Ó””u­=¼mI„_ªFJj&€=!\Á›-OzÚ]?0 €>Ä¢C<#$ ÑyÄø×JÀCˆ÷åñ©)Ó ì ¢'™tû'Þ ¦õFÆ®7˜;)^Q§ŸìAÀúf1¬Åmß-ê&SÞã?Dz‘öK×Á=çRÎAá%©$KØÒÜ7k£kGÚ×\˜,ú}Ô8w=Ô`äŒ )£«¸DÀFÏ"14Y®¼ d¶ßDuàCËÊ…ß±"3ÊR%´jLEj¨^]þFÃcH^èŽ7^Õ“Ê:Å{Df½ëkͺäÊ3V¼ñlÜ«ù@>h[I;3†¤!ÆQW¾e¾ ÉÑ +¡~Yt&Ò(!!óô™T­`NñëÌm¼L,‹Ÿ+âU¾Ú4ôˆt©(a| ´¦jŸŸhvûÆë+Y®\wÝuxùË_Ž÷½ï}»Ý”—è±ä·¾1¸è”sqÑ=Ï˃5â^Kð¼Ôz¬Œ§§¦ejŒó$Ôƒ\3eyÃLfyø:”¥ncŠÜWw^_tæ'¶q’!”‚¯ÑG»‚ ˜A蔸U E<ßѾƒb×µŸ*²aùã`L‚M'{NÉOÚY±òL¥pª¨´äêÒbýÁ®cå0wŽn‡L{O”)ÚDá.li£0–.)/ékÔpåÀŒ™øHsÛL˜Ò%°Œ\XM€ËËø>!QÝü›EïÏõТýØ sˆ§?Gïc;Íç/åp©’ °0û°°AJ3 Éa|£ûýðàÔxa©Ï"ð‘c® ÉæßHBÀ"ay/è)¼¡@°O6èµý5ûâ:40¡Ä aû)S´vüRi)ð!ŸïÀ×Aãè¼ù@F gæIÀ‡=+¼‹5IsÕq x »Í·ßvq¥–yÁPŸcy̦D%f}Ðm àÑ‹1ÀLÐ4'àÀ‡aû~æÖ+1íWàIÚœ¹Çi.¥aÀ‡dÔŠ1 >zç-øèÿ†YÛŸ·50;¸ŒìNžk·äèÑ£¸úê«G;›Íð{¿÷{¸ýöÛܪãCV Ç>>çR˜¬ûÿšñ–ÍãÀ¥[΋s£ªä±æe lš¶Š…Œ ®4wvòG8@ÄZž ¨÷´ÛÀØ –{è3 ”x¿+&w~$Ì’ZÀCbt¤¿8ÃqÂŽë¾Ö&÷B39ÛÛÿ޶ýjw=v+rõ8Àƒ€œú_½ñ ŒYr®(¢ñ'+oƒhß ­™±E¤°ÆnxdêŽó€½þðü}2?Á˜ìÚ9 ¡ Rï·“”ñU× ]NŽ X³çJ°3ø y@’Šcƃ¸¼©ßƒ’’JÝVÉâë íªí;ª¡Sq\þÁÀŒ€ÞdÚf%äÀ­~ 1½:fFt& à‘°XÈó¥ç­æ7žÅ ¶xîr×ë¶„{åèîúÒÁ¡¾œ±™õÜ ?©Œ_B?4É¡m&|ëÂ!<àøÈ7ž”©£ñ5Crùº•ù@Xô)Iãg0¡Ê¸èumb !¯Xûºû5é ÿð»»ÞˆÏ£xßýù韞mb'¾ 1hQ>• ’¼› 8LŒëØ;GëLûËœ2HÄ1nTärrPE=Hr€G99©Ì)®¾$$Gÿo¨‘ׇêQðƒ²;Œc÷Hã 1ªgx¨P@©å\™Kì,N®ÉžqZ^9gÍX#ʼ` =¾ö~ÈçälžyújÎy4ئýµðð§û"{omË€‰‘:·•ÂCUÉè›YtÅòR8-k§˜ÍcëØ§qlóS˜m¶*u(ó¹ |ô» líÝĺscÕ*¼eiròÉ'c2™à~àðÄ'>19¬µxå+_‰K/½çœsNrìu×]‡Ï}îsøþïÿþlòž‘è±’ 5(»+Åœ¹Už1é;pM:®ëoΈ)ãùc‡ÇÔpXL8UÍq‰D'€‡ ¸(c´VoQ„÷·´‹§»SТïQâRx$ì¸ÿŸ½wÖ¥¨îEÕßúÖ~°½·1 ÙˆœHD¹ÁŒCðä`TƘëö…f$˜\%ÆÄäŒ$¾•o˜&D¢1O!>ð8Tâ usÑAQòØصַ¾®ûGwUÏªš³ªº¿^íúæ=Ö·úQUÝ]]5çoþæ,3Ÿä²¹%F|¾.Hç°S&>hΟØ0l«’å0¾ºÕÁ®dÔª€ï-Æþ 6õ’Ð-¬ÔÒâ=z½ÇŠq@{ßá£ùD¡KµLrU8ÿÍØ:Çû°8Þ7¨È”i¶‡”×'ð@ùB+˸¢ÊrÖµ’²eË\qÅ8è ƒœýW\q^ûÚ×â·û·Åkó7gŸ}öR7qUÊôXË’Ëv Â…}Ä®á¨ûA™ùÆøÄáÂ=Çâ¡)ËÁÏïaHØ€ ™ËúpÃ_âíwË rxøËëÂ3(ûy)DáŒ5DQR×µÃÚ(õ\½ÏÜGý¼àÁÆÑ;É*[ãQ5Ã&‡óòdUÁ‡¹ aÂPÉ•˜q–\ú2Ë GAŒ†¶ï'«b^¹šÑAB[ØsR®'‘)É»,eK’,:à‡³¿ ûJ |øá-Õµ.‹(>%5ðŒ\ÀÖÝÔÍóŒfºdû`C"ÌÃOæ\/ß‹xÐ6Õ¹Wä© HMpÃÀ)ìûH²o|À¢{i˜‚d)dTåò[Zº6hWi ½g[7à¾Ï„'¹­ô‰Êêò6‹¤tyûÚŒËaÙ‚~%´… q â¸ïàa¼0Ó÷Rá͇kö-ÃퟮxÂo‘ëÈÌ-ð‘ÎíÑð0í£!kKþm¯m¹òÊ+À>ô¡á†nˆ^û¶·½ ;vìÀsžóœ¥jÞª•)è±&d €Ä¿O2°{oš¦Ÿ`ñÁq)'!ðJW«‰…TØÕ0‡Ðäú0õ¦«ã!Ðáƒ4mÁï%É_ÁÔQý¥Ô픚Å`óqÇYGã¸>r|_s$tb v=æÕ$Ÿì£ìð †Rë*ÐCW´Ór¼'ñœ"t}ÅäII±iº(A^ŒÐ F÷;ǨA\sY+„xÂÆyS–1þš[bß Æ"SÚeÅ×&25âÌAðÀ‡m¼kp»+O BàûÎñžg%ùîËðÞCàáìÀøJ ´¡(‘6¢x ˜<Ú7ÖmC+Ðó Ó9ÂQôc!.ªZ=†.«í°ÓÜwã§^þ‘ªî&d/™¨’ùî5í§¤,÷b©6FQà…ÀòXåT§•*¤ð+ÿ˜xûÙ>hŸT.°:î\?ïZÕxï^Xè ô­dÉaWvw>9ËeÃÇÅ 3ZZ½}*À‹^ô"ñXQÄûÏA„Ý»w÷ݤB¦ ÇZ‘ŒAHR\$æƒý7|pmè«Ú/Ë#¦¨%H)*~â1Õ—8ËÃ5<~T•Õ•xlx ‹s‰ñGâ;—øpë›…õÒŽÇa÷a˜” n·Ðvöžˆ3ú¢Êuµ¤œÒC|ü^Š_øó£0÷½ß.ç`VqÅ3X#ÀGuÿ-À˜Ä®‘òÐ#@AÀ£¡ù<ŠáQ(× µçë0 Y%´m5;ÄŒT‡c 3³ãÇøÈHÜ«½~æ/éê&®!™|ð°bY ²ÑË.×ÜE¤ºj#†ÿЮ¯˜q2Õß‚|Â}qì“„§˜Ž‘aèEå=µ4 erØÌÜ`Cšÿ¥þ] 1íó)é9Þgœ4€Ýdß±ÌúàöI}oò±$×@oRdëE1‘r%0áîÿí¶"ærbÛÛÒë/±ÆØ”þÊ9Ì!.4O*&É‚˜@"ý¥°”S'Ïöàëí§î@·\æÕ[°Vo‰ÉÞ½{11Äô8Ç|[µzduqÿ¦²b’RžœÖƱ§'² -N>Ÿ1¯¤ê²Rô82Ù–d«‹pÎu'ç]ÔÏY£t¶¬çÞÈòó•·Ï¸t¹p¶~ë_ õÞ ¤¡z–Ýd;É{^Ó¨âhßÑÁ¼ÛV’”§Þ·lú‚Ž|—‘¾âoÑúiÝ”ya¯kÚ¡õ¨,FöºÙ1МcÎóÆÈ&±&§ÍÿõÜHÁ3^°ì—pœw¾ÅzSjØlÁØ'Ì%ô¹ ß§ÿlYï»sž0O0}8¤úKæXx®ä}î2Æ(U8[—ký}ñ‹îfv£p¶à\{¢0^›cÜþŒ¶ßQÖ\ámL9õùî»rÁØÜìü'œ›{Îú 'afødƒ‘‹W›”Ð%é7šól̼bûô>[öq¶qÍx±œ³©4rê©§âÿð£ç\vÙel’Óµ S¦ÇTºQS' ÖåYFj\W:n”Õ"†[¦†ç þgîW(šsØw=Àa~´Ùˆéñý«èr¥ õ~èrÁ9¯2TFíî¥öâ7¡ æ7ñ´JžN¶Ðz„²œ³Ï|üáãæ›o^馮ˆLA5.mŒÿV¹.:5&¦ç·³ë2¢­%×õb´SÀGp9wIçq|Ž#b²E`OÊ©cè½ÓÐúÍÒ u½åÈQ¬5Ë#¥¢ HdÁç· :4r+ƒGí„>L=}žÂàz¯-€Ž¶J:½O'¿‹0÷@±1”£œÝŒbôPî'^Ó!PÌ¢œÙØ<ÓÁ: »«Ç¯<àƒ‚!é¾UÝ÷X|.é›dÕÓˆæ"ë+A¬?yŸY´ï ÿE.àá7Ì.bù'L=©äÐì5u8’¦ò­Ó²hßÑ©|)4!5 |0í‹Jím¯¤zçRn‹ä»œúšU†\ ©avôÐfôåv¹Ì{!y˜bÀ×$ãq¬=²ìÄv_$ÜAC#×´ oòÊó0ésÊãµEvÇÊÊ‚”¾¿˜Nª œzÕ;lj¾—xýKâ"Hz©åF©Îçõ†¨]ÊfQ0iB™®Þ²¼rÜqÇ᪫®Â«_ýj|êSŸ ŽEË.» §Ÿ~ú ´nåeµñº¦²Œ<Ú)>“„Aô!e”ìkvïñR‚7’DV7¹W{OY3I{Þ¾Øæ7ÏŽgK®­‰bä·Ï†ç¸Šy+±Ôa× PµQm m7¡*lÛí}—Õª-ìê-­…¾;ÞC;Q¨Kð0ôæ$@Ñ"Œ¥uøÐ|“j@ž?¿ôœ (öÝ Œ&€GÆ¢ëÿ×mÄ#?~þõ%€ B˰¡cö¸=Û»÷ >ÎãÞôåß­³ŸÐç³Vk¡ûȷʇ‚ííJ%I„‰dÂH´C—#ÛðÒ·óÉæœÃ…âðDî⇺PÃ/Òc¥¡¢‡ €IÝe„íжhw ÓzÔ”á‡>fÎÕ»HÀä·lPú´û‚½€ï;Sç¹pNrΑ.%%eBWü/”Å>”Ò#‚ò(àá†µÄ ò´.Õz¾Ì<¤ãÍøYB—óC³œo¥[…!;K/m–œ0?œªYÛý& ÿšÊê“ .¸Ÿÿüçqî¹çb0@kÁ`€Ÿû¹ŸÃM7݄׿þõ+ÝÄ“)ÓcÊÄ€Ç2N’Ñœ ´,iBζxâ=÷iä,­›^Êz$ÆTFü‰2ó>rž¥³+¥A+oyÆ€í*´ÆØM)N\»ÂU2¨g¿h”P”}©·uÙ¹,ª–ýËÝ1‰±ÙšÙIŒÇ3<šåFååiX„÷ŒU`qÝF|î¥ã°û4~úºuÐÅ"PÌBëª\ÆEpŽçÁ4†®m›Ùï4 «Y(¬/nŠ…Ë €¿ZǼˆRÎýFš`X6–}æ2‡bŒ1ÌÆ©À) “ÌÛ/–/±·Øœ!r äb|p¡E±¤¤î½šqÝ]ÇO™dØ5¡©e¸PGšx6¦8že kÛžmw,¤”ü|¶’3F$òôT¿'ƒs Ñ€U‘f&Ò÷`'t)d™…S­º8ñ<£€GB·Ñ͘®Q÷{º„*$áÛOŸÍr°?ÒŒ¨Fß /vu6¬J»Ë§[½ˆ–1©,3XdäŽ[>€oßò—Ðåò²tV«œ~úéøøÇ?޲,ñÐCaÛ¶mÑä¦kE¦ Ç“´â¡ôøû—å3dÛˆ´Ü^/KíM|øÿ÷|˜ý¹*âøõ3à‰¿"‰¸¤œ¦ïd`A€zÏæÜ0F€«üçKæÒâ:gyÊŒòãï@P`È3Ê^‰J.m:¥H§ ö–iãµm”jEléú€j] J¡AJd°0‡GÿÏ/âÞ=[pâ-ÏÅìÞÝPÔh«ûÂü~°DŠnFUÉ*¡M¬9ÈsçÀŽÜ "ï6tDEÊùÀ¶Ííû,øA€@|i›“D:¯¯\ Ä5ÀêùÆŒ6—Fä›jÂOêöÙ>É/ói™G‰|%6~ŸÎ'žá™“óÄœÃÒêƒ>ž{$àCC¨b]õ]€.mÂh]ŽxæZN>Šœ|¦m~û[„£±eFXUrA-Æýœœ7Q Çæª æ¾3BB{W7ãÀÂ9—öK»³™›]e9Üp0ÉY&Ãû¿þæ =‰Ò˳zË Ï¾'<ûB,Ì?¿ýã§-y}«]æççñ¥/} û÷ïÇ¹çž ¸í¶ÛpÔQGaË–-+ܺ•“)è±F$ϸ_"6DGé xÈuà£5Å/g Œ}>ŸGwàœ2MõÚ_.×i÷JŒÊPño€æ>Ç Ûò?ºÑa;¯ê³tã˜x£É¢´*®Ž” ¾sæø,iŹ€Æ0 Ê̹ÖüÌŠ7æX©}lÜÙÍßÚÈSåÿí/ÀGîÇ`4ª˜:4A.›ÇÃ’dŸŠ¬ôZI¬qLÞƒRCÌnz!ôÌAXØõak`4 8ec’U¼9˜SÖé»ÍA"y#d¡m13?ÖGÉ=?‡‰$kÌМ"@áæ÷¨GÛ¨K÷|/çFFe&DG,Ÿ$\­~„eÎõv©^ŸõaŠ•¾¿M¨èÎeJPÅ: C¡ÔºÜ½X‡)±m gŒhvøÇ~Îé±±MµoksXÈ%äôáÊ—Ææ!™Ã@Û‘-,ã(oNçú¥6l>¯|^òÙ*Ë€¤s}D¤Mÿã ¦˜\`V퇖OeYåꫯƛÞô&ìÚµ Û·oÇÝwß ˜››Ã«_ýj¼øÅ/Æ+_ùÊnåÊÈ´W®YJÀcÕl^\.‡€g-?ç_ßE|Or$Ÿ»!-J{±çVd£È=×U–Řv§Ü6BI{qãÌóqbím¼üÈct#€°<¶G–מÕv- `Â+“仂÷mGCñÿsž½Äð`ë“â ²ÁÆÌ›¼:l?r .ø­‹x×Ó¶Kp2ù¡c€‚ÍïQšü%]]—7„æ4ò ÿ³5cútœ®–êM\xóF¥S¹<¶H4 ¬ÔgkÀS©!ƒ­˜™9 Ëö:`}ÿýþ¹ /¼gu.½ôRlÛ¶Í;ãŒ3píµ×bçÎøìg?»‚­\9™2=Ö¼H“J;šhôØ2R"E!4ÜãCRšzÌZ°ÇøðöUõʬ[¤ã‰)Ÿ°ó½(:;”£i ]6¼…«Ó||Cpì(RMrIHi$ôhŠ^å\¨ï°©.!.‚q6‘²Í°=Ø>(…¾ ´óü|<þ}ÄîeÜ×c`¼·> Õ  k)œ™k4ù   wi¼ü¶O&iî~ûÆì3lŒÜy,.Þ›TÒ*çe`hù`GLØ1$¶GNÂ3‘ÀdwLqC•¤á&ìGƈë&®¡â·³tÎñY)"#†ŽÛ%`ØKvÜ&c¾cLpᎴØàA¯sî8c¦×f¿.áZvS 7Îe´nU8ÞìŠéU=‹ý'¾‡<ð0x»Íw£j£Ëõ€ðJ;’ã¦ã^Ÿ_,esù^:ÊäIµS„ÎÀý†XÀ£mû–?îå€ 9«‘äÍÑ![“¥Zñ%¼×ô¼ ¯rD{¹ê}Y+ ùsZäÞ‹(–!¼…Ö·–åÝï~7®¹æ\pÁ€ë®».8ç]ïz^ö²—áùÏþr7oÅe z¬Ii?*Dãö@ˆa°””‹,0ðæ$¸ÇŽ€G–bDé±È wa ÷éÂ.ðzêò&hi)G^¸6Àjˆޭ4šw_( A TìÙ{û¶Äˆñ%0rI]4.)Úló®²B\ýÛ Ÿè›áÑ'S¤“Œá÷keC[fÉs1}¤ú©¬a^z–’­¨l™¶¡?«Ìu §QSöà.<‚ïub*mw$©)zaØWÎxK@A»+€4B˜#ä{¬¾SNü=¦Œ¥šïÏVð#|N~  ‘oÛõsÓZåRŠIW@ÃîhVk¡åš¹[‡bó)ÛoÆXÎÚ˜ÐKî¶Ä<¾KÝÊËõÜùßìfÆv'tOr^pºšJä„ZÍ ˜aÙ»w¯<$™Åþýû—©E«K¦ Çš‘6ƒ~K–Ç$ƒ›ä%dr)ɉ7ì |°M”¼8L»c“¸ŸÜÓ–Aóøy>Ⱦ¦=X@ÎÕB‚85¨Œ2¼NlsZÂÄ©~^—õ‘#³Ãg€¤X²~|ï7àz?¨ÇÉ÷þõÍöh#LËR]bš»Ð€›oa ­ªß•±KXVƵ•IßÕ¸9‚}c÷:¯œô»u²¼êæ\FŠŸÜÒ9®Qá,¬ ïyJ|¶‡|ð ØþïÜO´R.€4]lTÍ’p“ÞFáx\›vøÿKàGl5§à£~'feç¸2ŰpšºÈø%…ŽÄª$áÊòwõx SVlùcௗ×#Ëì(˹†å(ªä¦Ê†êІ¶ù¿³´òÄkº†þM(Ù}V‰®a?‘V ‘K€‡Àòྷ‰ò\x’“CË ÒÀGuÍäàGÛ±+•Üô ÔNæaW×Û|EË©óL…•Í›7g÷È#,qKV§LA5!%â´ð¸$YÁA™ªËŸŸ¢ŒËâ+Û, @ÿÏ_"ûœÿ[z-èyü²®ëpР¹ž¯YE`€Á`+J=]ÎACèra˜ˆ@ëùæ!‹Ä?âÌ“˜ø€ÇìÃY¼6¹’?!‹Þnüà€ðàÄåöHŤ2¨"}ÆÔ`E½:FfÌB×ËÃId 8à†Ó)`b”í±óø.8Iw;àRűÉ(c†fxÀk óK¥2f ˆÀÚõÿêüöÆœ¿ZK*£`G0•Âîc¤9Þ¦½ž¡†jÑzl ÁÊ.’4K7£ª“aÉUÀ6Ðä€aú‡öÚË62£/1À{ÈGXH9Lºä/Ê4æ06ו#ûÌ Ÿl­GP¥;ç`GÎ;žÔH‹2\[}*ç<Þ#χ6PÁ™Ÿ øšZ"Û›Üd»ý¡äë¾äŽ]éUWÚο Àƒ­„$FöC¨Ââe™1©aUÀcŽ9‚Æ(LÖLCƒuD\Yœ[¸d IDATκV£”e‰;ï¼Ç{,@3Þ„÷¿ÿýؾ}ûr7mUÈJs•§r IŠåá%}sö-“‡Ã&Ê´;ÆÍFΉJà‘lȘߤ¶ÒãºQæÙ„¦äxSI|WŸ[<ë0»þÙøü.ÄpxfÛ¼ú1_S®_~p®–¥f=2çÝñ€‡ÿ~ûZ‚ðW4ɾÌ>?f¼×|/Kà!Q”ºl¶úPnvâvûkcÖ{Ó%`ûaõT¡)•—wŒÿü?ž_£ÇÐz¡BôþúïB}lÁÙ*©\µu½­DP˜…R³P˜múŠÿœ9fR°híÕôdžÈ÷ç'¥Fw”’oúÛ‘J–Ç2CÒÏ¢/p«(Q&†MHj×<–”¢(6¢lª·(ÔúÊØ Ïž7[a„Äm­nF2"å€G伿ùŒP–óÐã}Õ¦GÐåE±ó§^ŠáìSɘ<àûyޱŽF$ý„îÏÙ"Â…íh#”q×ö¾ çl’×›ÇÅDçÎ\ÍÌ“‚®ÑHOóRdüqǃÉÇ'Y²p\>–xð@d4Ù-­ÃKvZµ·—†³Û1»îx f¶¢(̸2MW>vmÊE]„ç=ïyøÈG>‚ýû÷CÕ1 >ú(>ùÉOâüóÏÇÅ_ŒK.¹d…[º22ezLÅ“4¢œ“(‰NÀŽÇà®¶r64Öé±)€ÐÐJ”Ýç7‡™ÈùͳpÛšÃú<JðæQD_ëyŒGßÅÆ=§ºõ¦ŒÙÕÙi‹ÏD½ËµçR1÷j½ïMÖœ‡ðs^rÛT–ŠîÒÕ9á{^oçžå~»Ü!.”¾êT¯u49Ç“=©q©ÇMnBÒ‡u (T`…š­óÁ,@éYh ð´/~À,ªÄô~\&‡Ûöêoxÿò5¢Ôž×™™íÆ(ËÇÑ„w%žae=×#ì×%¾¯ª?Pևɹ#öýÖ•ÓqŠgÞå.—H“zrÒí=û»Íy™!GÔ¸ 6£Š™áÁÂüN,Žîs»STK¸Žåü"} ¹X¸e,× »†Ðh Êsã–Y–û°éïÃâ⣵‡™éàAËöëe— Î ãôšÈµž®„Ó¹µÕßOJ!´,Ãiá¿WÖYÃÏßögìûáÊgõŒîŒ²Ž·ÄÌM?¦3CMÔ'KwŽÒ}Õ;‡:N”âÆ×ýN¸í~Ë_aÑ´Ý„¸øsÆT–]^ò’—àÆoÄË_þrE¢(°aÃ,,,ØsÞûÞ÷âŒ3ÎXÁV®œL¡¸©‰10€ñ<8§I^ˆ®"j2›‚eD¤³ç>w‚dØ'¬Ÿ`^ÄΪ i~þvœò?Âhô]Œ¾[%†#çHeçÖá7?£1ÁnM¹Ío xˆŒVÜÉš÷¨4¬%ôáæ—õÁz2úŽ%ŸD8…9x‘¾ÝÅ œ)2Àböû õŽ K›~iroŒíñf3¬ÿ÷Øž¯k&¨bT± •±Õòý©jx4úÿ¨YãƒãľÁ=s™Ã–›ö<Ë  Qh—¨ßŠÞt‰ÍÁ zL–|­Ç„Ò,¿š ÂÑñiò>î¬,Ô[Ž!”bfæ|ñUçC© „åQØãJ ¡ŠáÒ½3I,ÃlÀC8ne³”®Öcèrû÷ÞŠÑÂP–ûä9Àíã˜-u‰™‘blL´j–W‡__ÎV]ä~w¾ÓD_I]€c|¤†ÚúÍ??©Û¦®éYdæGðÆPÍsÖ¥ûm2ÌVç±mjæ­Gøé+þ?vëßC©õõ6l¾»6àÞT–LÞ÷¾÷áꫯÆ1ǃÅÅEÌÏÏCk“N: ûØÇð¿ñ+ÝÄ“)Óc*µä è1ÃN¤CëÒŸÎ6EÖµmY}x¶Aéý{ð€낲¡¦–Mâ7' &#îZª#ˆe=1®F£lVp1^eó,àþfs$•Ú—Vƒ½Þ{oNnƒúÄŒæÜ1ÃRH‚1Ý ­\ÎxJó”8ÏÿÂbòzø«39¬%{šü†DXBøàœûï®ã^€CØ…™}߇Ò€r!ó™4uŽöߌMÿq€çCÂââ÷Œ‚« ²º‡éGØ?±î‚~U¹u²M `{®aÓF© <Ô)‘÷Ètø²CßmÆ"v‰r²?[¬¢?™TFêX²|'é0ÌÏ}ºÜ û­PÖßBÚ¨Õã¬g 4ãßD€IÂkÜ‹xs Êy˜•|4yØÙKt‚è õ³Šé$AX#ì¹mi-‘r’ãx„e›Z'¼ ½¤²Ã0 úýaœ%)ö‡È*¥ãi{a—&fçöîóy/’øv=R ì3 t¹ \ÏÄ­H¦ç0.w¡šƒàÙ)ƒ¼¿¢T˼díòÕµšeÇŽرcî¹ç<øàƒ8òÈ#ñÔ§>u¥›µâ2=Ö€¤•8Î;"áÙÊË©ÚÙ²LÆ\Þ²k¼‚]Ë“˜tŒ£{ „Ƙ¬˳2Ø$x¾Dêö{Çœ*u³¼(<¸ÄSRèJv‘1F^Ô $Ÿ­’*ÛÚ>k¸E)?ÄÅ(™Êšß/ü÷‘ ~´€òÚüîœðËéLÿj@¿æ~`P+tM(ŠMdš¿h±å;Ÿƒltì¨Þ/=‡€iå^ 8 ’暪ý¶Ï°’|äìÅD|‡ÞØ/„7¬¢`wìûöŽS¯ðd@ÂÆÆfO¸ä¯U»ÇâýçŠ6@€ÅñC~ãw«§¦ÇáûÄZ•õøâó¶@ÞÀ¥¿'QË?Z†IóL%e}¿¦ì~ØI@O +H¼óÈsa¿ Ž•á‡;÷Ì1ägÝ6Çcjl—¿k†åÑàÁUÅöãÉÁIò…õ#Ë#wðhözo…@±ùj†ÝJì®óíÌÙs,XnûÄ”í±äè£ÆÑG[o½÷ÜsŽ8âsÌ1+ݬ“)è±$®Ìåî –Ûˆ%8&jÄÅV€¡ûÛ_¿Õr›qàÃ÷v‹Ì ®,Â5Òé~s¾¿• …T>x¶Gé`žqŒÙ!R›‰s8£’7(æ­‚h°¡lø¼Cm5%L¿ €‡™¤¥@mU9 …xøûÒ+h¶;ý€ ß`À„ò–°­Á0—‡ß>úŒ=fˆÞ½¸ßîK‚TjÀ‚ȸ‹£ÿ@F¹ž2"GÓO¹ûâß ÅßcÀöð@½°"ôî’w$ƒ$8Ì*ø>éT²Ê3¬-B—Ðz¥é–vIÖÒ1›ÄŸ™ßìrÌ»©w‘ê™ïÑÌ%ÐY É 4›Â¹€G—@ ðÊ`Çݘ7ÞäçàŸµ?ÇÉË¢–Î5ìÒ±ŒÃ„ËåAs€G§Õ©¼þÖç2¶ù²”lÉ#l¾8âsX”Lßž©£Ä<ÔxdCM§²ºäµ¯}->ðû¿üå/ã‘GÁ~ðüà?À¥—^Šã?~Z¸²2=Ö¬È _óØù´Ò¶²¬({Ûɲã@¿”1¡¢pÞ> ì`=½ €ÃÛ 8ÖЩœ2>̹,µ4Ö¶Þ$T^DÊ(€†]‡TLj¢bB²Â^LœrÑg(ÛÒJä¾4—»§€ò«RA-cÈÉrÖµåî»ïf÷ÿÚ¯ýšý½{÷n\pÁ¸þúë—«Y«F¦ Çš”»®hz’M¡0ômøæÞK›z}zþrHŒqAOKüEÄ• ßëÂ0NÖG½Û„$èF)‰+±þC&ô¨d”ÁHè3`\½—ムFé£K?Þœªa2Ã…i{´nŽùшìæIËW8ù0–"8FsN(½ ÜP)«ûÐvÓß4*Üþ“q¯ì u¹á¹!]š‚Hæ´%ìáŠ&¦\÷Úº»-ƒ ‡¡«¹TõL8¦åŽ9¶M“Sßùrù\C¹’ ꜻJ—ШV&¡Ë@Úè—jÖ%ÌR®XK“xÖF­ª“¾êeÌáº2aÞöV«P~8Zs^[I±Y³æÝ6uùÀÃŒ ˜—tâbŸ ™’Ë£‘•p u–¥d„äI ø˜Ê–¨ŒXßC=<ðÀ2´fõÉôXS’v²ÂŸE¹gR.¤ÂÛïœÞÕÓßK|o%˜‹ÿL†Zø…DX5­)¹ŸžO(­‰ŸTëƒR”dåcõ¸ñ2¹¢AL5xa¼iQ˜> ½ÊÍ?ôaø`Y!½¿1Œ¹÷9øÒî[áÊòzX1!(`úߨ>,KHÁ‚¨óÏ“„¶Ø†$þn¦aðÈà‡{®ªŸ·V^"_4}¥*§dÛM g¿ïÕUþxDØFl×?Ýel¥„¦¬Hã½S§9Þƒ1Œ-K+Ù yžWµúéXY6}µ7Lbßà›ô»\$ÍIQ ¡ÔL œ l˜Œ.9öQOÂ墠‡9¶EƒK`{¸ç4ㄘ³ Íýç´/vŽ— '›e’ i©5t“>;N„œ1¼¾Vx–vŽcÄg‹¦VlqWi™ðh% ·åx±ÜËÐ÷)Qö$#yyRLèÝùLtÙ¹s'vîÜéì{àpÍ5×@3t/­5zè!\{íµØºuër5sUÉôX31´œ­R$J€`2Ëah€‡tmýÈ» à”øˆ‡U2:Ìþà™¤òx2øa¤ú4ÂúH ×N?gH¿Ò,×6$Ê4|Ø ŠøHÖ!+`©þÈ18àƒzVàƒ£³6¬´pʤŒ‰ù=Âký}š>ư=ª›µÀ‡Õbö¸2ïËÔ+„qÙp î;%÷âålÉš©õ(0@Y>èy6/ —Ø—]Ù´— ¼¶yý¬j h=b ø,ø‘ ¬Fö‹€G›çJËwBéòÇÍÞ¹¼ô˜”S à€VEý-{à—.Ñ,»ÌUT±ªXW_WÀ„€+ãH‚ÄèKHrYØI˜0\qº¬Æcš×ÃmY 'zœ€Ñ„©b!3Ör€¹Ï4£‡aŠ0]ý@g<Ë(Ë£áÂ@—Qÿ;”øKÔVâ3Urï½z÷ÓÕ[–NqÏ=÷àæ›oÆg?ûY»ÿU¯z•xÖÛ¶mÃg>ó™eháê“)è±&D ÑÛü„Ñ9¬%ìâ€GKƒÈ›¸Ú"Þ]D\ú ðŒ‘ˆ¡Ûö™G6{W…0v 4üˆåúÈjB`D¯Q»[QÖ•’@R¯ÛÀzÕL¾m’=àH¬ÊaÊ(KfŸ|  ,å[¸W7Yfªøª0~ôéu¯ïÉõàª2’Ѐ#ä}åK&ØÊSÞ{6Ïþñgþ26óo¡ô4FàAŒª éAhíAõ ¶ˆ²lã=’†Öx  XGlÜ·; °´F3r@\v~+L˜_¯ù™Hí²=걫ÎMa:«r] FW›÷S%0-ë„Øððî»bzT›iKÛU;X‘rr¡±~XF}Ï:…L1ÀQ:7PîÉÕÙf æê‚¼1PZ =¯Œ¼:ª²sÂÅ„|6„9RX3ƒ€*4J» „—šå Ñmxd2›q¼Þ8oÊ9À˜NQ-—Í9BdiÞãó ~Ôä´ÓNÃi§øÖ·¾…—¾ô¥˜››Ã+_ùJöü¢(°}ûvüüÏÿü”é1•m‰åì`Ïï XrÀƒH6RßõÞZ ˆ€±v)A9ŠM²Ü±®,k4 Q*œ%uŽ—ë£­¸])¼E¾–²Ô zð^J€$Œ«þvˆ—rgôÈ%bvýegøS8¥z¡=mîÕ>˜PÀ ð'>ò$–ሠ_‘=Øfÿ¡ßø+”z? Ã!‚Ó|¯Ô«Ëp ˜ê‡Üe°¬êU}ÚawäŠÐ÷³<úRØM/â÷-?)l;‰kìJ>Hˆ¹˜²«1̲L~º¼je l8È\Ÿ£õ(:>µ Ç­p<ÃçÚ†LÙÆ ¡Lz̲=rËŽÚÞ½Ç"Ï.УÀr‡qŸ‚ˆ0I«û3sbUn0T:NðhÃòˆ|C©÷a›? à‘á,‹´Ì ü°÷­ ìhr¹â²=–#/ÐTÚÉ 'œ€¿üË¿Ä[ÞòüÏÿù?Wº9«V¦ ÇÜF9”%>ùHTnâÌ>GR \›ò»*Éž!’Z÷= ð ÿ·¤™JËAF¯åâÿ}:-“l/|˜süè"Yl‘ÉJ=_éÑàãØ­šâ¼ñg—ç1ÏSÚ‚‰ø°a0€`ûYÙO~ §Ÿ¶ˆñ0öMü~È÷VŽw¹Ç|æ7rX_îj!á˜C¯O†´£mŸ½¶ƒd ,Òu¥ÝÉa¿a$®’ðN§ÀަŽ1|ñÑ‚§º¬¢t•ÿ¥*À€‹uRÒ±SŸ-»NZªP ,ª6érÎ2E$‰­V)ñ ¨ðw˜+†Öa–ãÍg^åkm»}ý‰`@ë-›B—ûZäN ˦á€m„‹øUøUÿó¯%…eX\"Ë#‡´)—ùÒàAÇ€Z~àG3F9ãS×yÝ¿Ç C„B'‰|ß…V(ÚvÔ d9ëZmrúé§ãÅ/~ñJ7cUËôXrå÷>¢(pú¡OÃé›ÐÂÃÚ‡òÀyÌ¡LÀƒN&ˆÃƸJÒÇ=¥ N’ð‘ xpõgL²­æú ó·=gÄΉÅ7ç(AMC,‰a;HØHÒ2ž>e©Ã®xq•–OxpÏ>Á"rvue|8ÀG}Ø”éä©÷kfyÛàá·[yðÒ郪ì@±÷¯;çFf_ËΑà1Æšåu뱩^Fº ]™ez'僾±äHæØ§Tá7¦Üt_.oGìpË  Ç ¢ªD¥ª€ 5 €†xв«c#”% ,ãR¼0±}D¤¤¤Ñ¤œIö|в£ýIb{ô)=®j¡ÔPCÅFÌ ž‚ÁÌa´ðm,.>ù[‹ÍýüX+;Uè~²Šezé…f84ì :oL;&’»†4ÒW~—J|`„tìÏìàð±%œ[ÆÚ’Y¾Õ ÚÞ²ëÛ¸e×·1. öˈ üú¯ÿ:ʲÄã?ŽÍ›7;Çïºë.h­qì±Ç®P W^¦ Ç×ýøs±n0loPµP(¨’Â2ð>E@ mhÛ%škœMÈ5ü;„çqíl6ß¡À~㤯>#=Wc(‘¿­Ö1ÆOÊK&ÒÌY NôVPŠ$ ¶†u€‚n;$o­ïŒ?¿]ÂxeÎmXpÁz,«ºsÝGi¼¿^­ÇµáZ…Æh ¦Œ²ç9˜‘$ÊÕ#ÑŠõᇸ¬¡É°•Báç<?ñéYŒËÇêw“{¬3i%§´˜¾˜%ã«nâ:dÄv儵Ø2òX]$g5±THwÖ*yRÈ 7·Õu®†P—è½ £³øÏ™¼[UàÌ-ÏÀ[ŽÃc {ð¿î¾v‚z¦ÒEî½÷^üôOÿ4¾ÿýïãïÿþïæÇ† ðû¿ÿû8üðÃñö·¿}[¹r²zf‡©,™(!âËmþ1±@¹ÛP€‡9n±ãt7ÎæïÚ‘RP2¶Z$‚RÂm©ër7&Åиs6éz0Ê8"_á§›½¶6 yOZÚp¥ßžQÄz[1"C‚ð‚À¸‹Rgk¤EßöÆ”6Ì,óìƒwà04 ô±e4TáF™'¬Ž»䛣Ê.ݲ„<Œg;L 2#Øó:ˆIŠÉyˆ¥ð ¿Ý±²¹ßÍNÃNh7f¤r'ùŠÊ»>ŒÁÌ¡Pƒ᾿pü Û'™ã•¹&¸äÝVF9j6Ý0<ÜñÉ¿‡¦Ÿ7ý=ÂDaÚÍ&­Ì•Äóc¥p+á¹ss´t^"ßwYîÂ3®ÿGŒno’Íúýš+ë}V—c®I&¿ö浪Œëð§ùêo½Áôš?Æ”#ŒS+ÏòˆH ðˆé»€~n_ÚùÞô‹@×XÝ2ø%œ9ß>·‚€{Ìó(Õ²okYÞýîwcûöíÇxôÑGcGq®ºê*vØaøà?¸B-\Y™2=Ö’´1¼Ùãm(èüÄ¿$Yû’£Ð Ê2ç9eÆGòBxáNõ3àgÄ»icCŠq†¹Žw°]\v¤-1µ“¸ÒõN¦¼9ì '_HÍ qj¸ø¡:U8Œ÷>jÏ}ðl;ÆéfIÔckâáû«›MÜÛY\o·'Á§Q¸Åe[KØÐÀ y Îð¨÷“ë)È!÷Mê•Ïð–ruÆö«¼pûœì½šg9vÏ¡_O€ß›ÄƒÌïôü’•x¸fyj=ŠÁF¨b# µ¥žÃ£,÷¡Yz:óžéyà‘[†“c„ŒQÞØ)ÕŸ=nåžÀݰOh›rW@ñÛÐÇRâì’ÒC »‡)Ëow§9ª Hc˜:åÆP0m(„ñ«ûἦx?Qº·¤ºdq 7/&k¥•UÀh$æTh½BžÇ¸ ”FÏø0õöûܲrñ)Ú—ìïü ›>Ø,Ýnú[5f,i´©DåŽ;îÀ§?ýiÜ~ûí8ùä“Ùs.¾øbœwÞyرcÇ2·nåe z¬q˜¾Á•1·äÑS°ÙóóYm=ä±%⚊âYäÛ,ŸfÊ3×e “„î’Ö ¥÷³†vG‘’gµ>hÛûöêPeÜu©î†Þ¨#TàfŸ|ÄD êäš9^ò¥]NÙ]9ÂséOR‰{£ícÃIòÀY‰%Š¡Á Š™ßîê @Q¬t‰£šÁ#×ßhW©I³ÝØïnR°£Í/ü>ðdÍ_ÒÜFZ;ÀëâóŸDçñ¹%šQbJwcÃѰz_fNiæ—8 åüË„ª8e ýÃ™Û ƒÑñü‰þæ}ÃΜ˜™„Ù¿@}+ÀÃìëø¨Ú?€œåᣒ výѱ‰ ¹²eµk[QVÛrÉrÖµeÏž=Ésæææðàƒ.CkVŸ,m ÙTVø9(rc]—\’Êva·ð`fÛÉ o3U«ªBC8‰8€(–ó#U¯©+òüÙw!$)šMŽfbƒI&îž<÷š1`Á@¤´ê.y:èu챈‡Ð‹§û’J7CW'[“磎q¦çç~¾ Þ¿ãåßi ¾ô3]È qÍÓÀÍÅA.åâÉU ìð¾/ßóLs…xý˜æÛÚWH§]N}!Ó"Ê‚ã6¿ÝÒ³¨ÃJ´¡Ôs(õœ›ï!nËäÀÀ-"“ç´I|un‹re¹£…ï¢?I¨åmóBôÂè“Ýá—-%"%ß›$1†‡¡Mç*Ã].›æµàÊõæðÖ¹>Ä6P˜($¼ËÉ-¤›œ:âXk“ù?Ç(Í|ÖbŽ¡h£¼°6×ÿ¬—â»ñÛ?§ûXÄλBžQÐäË0›{m´^?Œ…éçÎñ"êKŽ;•‰åéO:þøÿ8zΛÞô&œxâ‰ËÔ¢Õ%S¦ÇšA8ØQE‡I†¡y~ÛˆGÝ«êæÀƒz¢VÃZ )¬áÒfrÈÊÒé'·¹™x”õ¢ïÑÈ¡¸¦ŸyÊËŠ(㣴ysñ IDATi+y^K¨¨Ì]º9=¬‡ñÁ36(€Ä}{KãÊm‹°W4ó>–Bqˆ•ÙJá‰)gùÒú›wú—å2UH¸J² <ØÁÖ alsÄ3êÌOÌVlúGö²ÝB8O,1´†1.Ç€r ¯ðh9&sžÚD¨‹ã®Ûçz‚Ѳ!kËŽSuø’YîfYc]B›¿õ’œÍµÂzT¸ìjÜ{šÿít;Z¶©ãÉP–9/Ò\"åXŽ$ŽíÑÉÈ6zD&³CZÒ›²ÒÄù3G²‘2‹«¨áÕ#æ­‚;æç°=Ly}IP•{fΰ9¸9ŽŒs±öµJæÌåsœ)†S@>SK§¹d*½Ë›ßüfœy晸ùæ›ñªW½ 'Ÿ|2¶lÙ‚Ý»wã+_ù .¿ürÜrË-øâ¿¸ÒM]™‚k@”šqÃ,€(’U¦DLaAá$’}mKæÄå±%,h •‚\g1Oå HNÐ2ï?æYe'¥a.&ç„-c«lQ xÎi…I>h {ªé»qŒ¶Â*‡^Œ©óÜHºŸç£¹/R¾ãqçX!eÛÖ¹&ŸÜBÄg7È!Óƒ;x¾¤ð'ä±)IO øˆæCaî5Þ&9œ¦)€<:ˆó|¼o4Æäˆ-£lÏ£Œ›<Ñ;RK³F§šÿ-Íž‚t,¨Ç+7™hÛ~Ƕܺ^3FjÓîÒ%Çt‰‹HÇo;ï™Ë¸~Ù#hœÝ/™–X¨ —€µm{Ú€ N›"íy–ç`ÃAÏÁ`öì{üoQ–ûø9þ{âïWš Mòß9=DÂ_ØPŽåÔ/èóYBXÄ9ÏÍçk½´UÈïT–L^ñŠWàK_úÎ;ï< ‡Ch­Q^ð‚ছnÂ…^¸ÒM\1™2=Ö€T¹¨^Ö’CLj¢™ÕO(ŠO<"°½8™æ|£”›qÇÏ_ˆ?õ)ÌÏ-¼®ãàÍ%«äÒ4‚zƒöWžÍj‚CÕÿÏ À캓±÷‰ëøö°tÓ¶"°>¨,ã³ ¬'·ñⱞ=¦í¶;xØÓÄd”2ãƒmÆsj&(7ñlíõ¿=}HÀj©ßqŠ•Nä:ÇŽ&I`Q³bïhlñÛe™ 6Ô,¼&KØþ*ß@úH8f‡?^î,¡á-äÞDCûì¶\¿/b¥ÑïÚ°Þ,­Þ)“<ï»åèhcÉ=Òn OH ðp¼á92 ØáTÌ·eѵ𲻅…Ï6ù]3€Gõƒ1…6Ilù¹oaaþNHó3k¨³RðíñÚå€>+‚mxOó…xÀG,l³wñ4¤­atUßrÀø&4–'›»RÉ[›ƒíÑy<“®DÒÉ>¹}mÁ}é›rY)ÆìT–WN9å|ô£…Ö=ôžô¤'a0XF'ô*•)è±D©ÙZQh&'ñj²á¼TÂ2©]³qg)åCŠm´ûçxüŽÿÄ_cTî®=ãV“qœªë?³ØD$(´.''…ñÆŽPë1‹œr66ßôÑÖÞ®#¡¢çÍy>%¯þN^/—A|ðC[|#,(’ófÆÚÆÜ_Òƒ¶”’<„ÿWäH|Ë}-ßëmÞ{2Vµ¡îS ¨˜T¤pChŸ}aûž|ÐŒÙqoÒ¼G±p¢#È1pƒƒ1O¦ ø€·òƒ>|*¼+<ðeoH®°ãµ+ |˜[¥÷XŸï€Òu¹tüŽa/y,3ÏA‘!Qàƒ{V±1/'$&y<Æ(hW—Ö#æyS°#c Rƒh[ìŠVj¶ÞQ kE)²†9 x´ ÙŠÖúž|€:\àÃ\;¡$“~2u-e2iþ V}dý· ¸Øæ¾‚gU4ýØaMû ¥ ";¹<’Ò€ZFòGrÆ5&J)<ùÉO^éf¬Y®½©,­¨A•ÃaÏêxÝb¡Ί(¦8Ö»,¢Î9©-çvƒ}ÔYýGuVïŽ03;BôŠdç}†–r^Ž`’ñÇO`ÿ¾›°åó— ,ë•ÐbØÅ{gÂ^‚ð—–×ålÍÅÕóVÝB£Æ = VäHQÛm^@—,÷}±abžba% ¸ÞÉlVU?Þ#_¤\¤ŒÓO$–N­ÔA‡@© âøäŽ+ðFË Ù”YMià–™«Ü'”à8Õ9¤)‹ž¸H¿—´ÌÈc‰ò¸9£:Pß—ø| DÇJ.œÂÛÌøØê»gîϹG?3_Ê«}´¨zÂrüq!;d³–¬Ð.Œ%¢i³ÄòpNê0g‰÷Rõ1'¬%¥‹x¿mX Ya­ÒÁfk}j†€2› {Á{G°<&>œÐÐÈ<'öæ5ﯕ:÷çØu|kâýôÈdW\‹ô³`ŽÆÚ6ß´-›Ì)¦ÏÅÆ*\ÍòÏ“a¹}dŠ>øR–%ÞñŽwàè£ÆÁŒsÎ9wß}÷’Ô5??ý×Åõ×_o÷ÝvÛmصk×’Ôw ÈôXâÄ’Z)¢J”{¬6üøIü><¸Qñèü®tZ õ|ušs¯Þ¤ÚÚ ÑthP˜Ä„Z°¸ø0….ç«x}1.=ÏÓ߇‚^IÙãV‰~àƒÍó¡KÞÀ‰(è9äéPÎR­bÄÆõ"TŒBaÚCX5ùõ·SäØ:#9bòÚЮïj”P3OAyð©É2òCa¶fÕÍÖç‘1ÑÍ{H{âZNÏ×zåøýV2Z}ƒÇ;Ì#™}®ÉûzÉ£ñí­’:fœ­BÔ"ÀG_9 "†0À|“%ÓÍ3” 9‘÷ÅKž×¡í“èø+D  —cC U3^PТ;j T ë“ÌïÝÁ¯ÂçPFf‹q±íûˆìÜ·]IvK…>‰Àå²?Üús—)îÐ×ccˆòû:œùC©õ(ÔA(ÔA5HçÎ+l²\*KÍn=@åï|'¶lÙ‚¯|å+¸á†pÏ=÷à¯xEïõ\}õÕ8òÈ#ñüç?]t‘Ý?77‡W¿úÕøë¿þëÞëø“|‘>Ç?âã3 ÔÐ{IáIñ ¤hRðÊV¾!ŒðYÇóÝDÚ×ÒØó@ßã­>È]žë‹s¦“Ì— g‰> ¦‰aža^:á1vÄdsF›Ä¦ðQíÏ?zgyP0 ¹Sû#àt2‘­)—´—cy8÷ö;æîÅ`ë/B©õyÏ“²hè >&óØLÚ…éäë+Òí€Þ€ŸüC>P;äY Ïa5Æ“ZIøÑíYw5.xå™>³2ü0y úb{ô=©;ZM"²¢ë÷½TŸ“ lV‚>ÝÓ=uTJcF{¹ø ôè.èroÅ–’Îe½…ëNB1< {žzT±žgò´}~þ1ÿ<ò]§<†±1, úqÀǤߙ#š}ò >øQÉäãQ_ÀÀ€ô¼Ü>+QÔÉ|ê82€¬P·ðÁ°ìþV!KèÈ‘êq à!­¢"}sá·oÀ ržahÐ*½ò¸¾ï‹³*Zf¿@[¦te:t>òÁw¿ùÂ~­¶Råµxv9ìÇ.åšK<‡%ŸÀ<­÷ãÇׯ²×—ffŽÀW_ýF f¶ÖçÆ¿é¢^²v9·¶rà 7à_þå_Z_×F6lØ€—¾ô¥Î¾Ý»wãÜsÏíµžw¿ûݸæškpÝu×áâ‹/ƦM›‚sÞõ®wá²Ë.ëµÞE¦ ÇZ`‚m¦p«cP-½sèÌQþo_‘•Ðç®(w«{.ùßäþ—R\CÒ¯ßg-É„2IéááAÏ3hh‹¾ç È?z<2bîcqá!øaŽÅÁ]'®e™0:Óˆë„òó}Ë&„–ÎKðA¿ß”"Æ8)`‡Sƒzr=®nùœq¯1F©÷¢ïBY>.ÿôžÉ¦QâÑcNÄ¿^ðTl¼çc(Ç»êû¢L+A÷÷Kß2UVsY!æ¸âÆ$ø˜H–‡ëç=¢¾HÇ«,v ­²£ñu?oLšs3 ¬€Õá{¡½gÆ1ƒÖÜ–$JŒùÓ1T°-“ȹ4™ï‡4²¿ ÃaÙkeÆi‹¦ãŒ7ÞèQÝöqpŒ­Çî$¿±³ €ë¶}¼Kˆv`^ øbàGv>›`Yh3—ó&«ì¬ú ~ó¯m^ûSzv 4ŸcݯƋ÷ã)_ºZÏIWÇ‹à9ÿxÆãÇäçp€ÈW¿úUœsÎ9xÞ󞇯~õ«Ñsïºë.ìØ±§œr ~ê§~ §žz*®¸âŠÎuß}÷ÝØ¹s'>ðt.ƒ“½{÷â‚ .ˆž3;;‹ýû÷÷Zï"ÓÕ[Ö€ðÊ3ðS£˜-¨6Ú”ù] YÍoÀ]®P{ýQí³õ$”–¶ÀGj ¼%]Ê_Ö¯£ÑP—•”ÀH ½¨6»]yÇ}îÊ é:©Â’L`:‰BìÔS߃ y¦…Ó¥ ¶OqKg*ÔçÖÏaÒ¾ÃgØî=GY'mËniÆø=•0Ë »}•gf9ÒÁxä=×Àä} €÷ݘç̼ó¿^À­<%´m›¿õ÷8ûÛëQ–5É„1@§ejë²9¶H2¬…^«¤êÕ©§ï+0HbÆ5¯uY¾ÔùNì 7ãäûµ+á(º f5èØ=©ÂùÓÆo³`!ÓüChÆ©-¹õ1KãÒ9Öm~½ù#fÙ%wé|͉7¶Ðûtîk’0¶}‹Þ3;Ž \°ôY´k§ómú@„ù‰’‡Í³!ó‚*`Vˆ’V«âúP+À‡ëoÜ»ÑdÉéH_fÃÓÌýÀûöb}½ŸŒáê9úB&ËCêóË¡#¦¾z¼Ç +þu\=%`òvØçeÆrZÝßö?ñQTÉÿÌP–Ý»wãŠ+®ÀøC|ùË_Nžÿõ¯gŸ}6Î;ï<ÜrË-‡øÂ¾€sÎ9·Þz+®¼òÊìº~øa¼ç=ïÁå—_޽{÷âiO{Þóž÷Lr;ŽlÞ¼9ë¼Gy¤·:$™2=Ö‚è0¼Å‘šáQ¨u(£(è¶…Z' ¹C¹l˜Î 1œGŠ w©ëoEóëUÂpßk’CÉ•&ÐÔµ“Ò}/)à<ëb° ƒ™Cë÷FßEãu‘&ëž#?·N95’Ì\ØK°ê Ã0pYñß2Àó´D¤«w’/Ì3n‚ã¯,W\”BÑãtxö¥ï«•Ò5ŽlþûÀ‰Á÷˜¾ÇWcŒ²ÜkÛã·7o)Qï[ôžµèuÎÇ€÷$‘ Hy¡h‘<3~®jŠÔ{¤ϩ‡ˆKÆÂÓ!Ú¶ˆÄs Èž\1ïG¤~—I9¥f@COy•¬¢ š*L;£¡2þ³“Â]"çØÝ‘PNù;iÆv·^nÜó&FÄä¥˃ÑOÄ| Ò¬¤æŽ5›Óa'’°äb°ÃáÓP¨õñ{GÞûfÞ³J~ þwêõw§nÀyvÍ1Úßã¬ä±DùN[˜{ÏóuÕ\I°ÀD&X‚‹2˃»ß°LÛU÷]­lâüfËÓaT¹ü[J=ôPüÖoýÞóž÷$ÃK{ì1œwÞy˜™™Áå—_Žá°Ê×uÖYgá’K.ÁUW]…«¯¾:ëYÀ“žô$¼õ­oÅg>óœ{î¹xï{ß‹üãÙ×§¤,KÜyçö­ÃUtÞÿþ÷cûöí½Õy ÉôXËb Šz’/ªŒÍƒb3ÅfÌÎþW Š'A©uàa'}þÒÍRm®2ë(¹; Óvõï˜G™¥³x×–) }æ}rO0;¡ª!ffÃ`fT±Î>ø(§€¯€xÕ´õ8¤@zjβˆt—˜ó£y§ÕybO ^4òÎûz!º4oÐ6ùÛGÉ=p†j%ñ{佉0´Ž &;北â; ßO3n¹€‡m›soc@/8íëŽÏâ!θŠHb–+ŸDr¿ðp扂.í){º]ÅŸwðaÎåÀv¾IÞém ŸÃãjPEõ|hX¹_k°5¨mž'AØî'àc©¤hìÏ}ÑUÚ‚‹™c*¸1²YNvìÎáâønøQ­ÔöõŸ~­gÁîC 0D¤6ª¯·L¾ ªÏ5ýÓeјkØÐf@Ïø•šü Îù~\¹pà´+%9ÀGt çõÄ(’:g¢±[¾¶ÒFxü™¿Š™áÓP%)- ØQ¯H–ª=ÐD©†ö»~ýúÈ™ÀŸÿùŸãÞ{ïÅù矃>Ø9öš×¼ðÖ·¾£Ñð¡}›6m²Û‰'žÔ½iÓ&›TtûöíøÜç>×Çm.ºè"<ïyÏÃG>òìß¿ßÞë£>ŠO~ò“8ÿüóqñÅã’K.é­ÎI¦á-kB8¯›;ÀÚäYÅA˜9èlè¹ïàŸû(¼ø#h,@—%Ê†Š¬èµƒjV…è•‚ y18#¥07!„I%0ðûe€ˆ´aNŒl ƒ½k;´ï™j9‰ˆ¡*p㯾g_ö7îkÞ¡1[Š)K‰Ï“dhKß {üwË{ô.mLE/š²¡ð#%4‘[áÀcyÄž›ªC®19$Úææ}6õœ¹¯¤øÞN±±qjyN=U;°‘íÀ)„“‡)·Qˆ…º¸{´à´ €©ëw”ï¿/ô )éJ¯Ž„àQª»y®Æè2ß©'è;ÉÍõà<§@IÊuïÍùž%¯v¦ñ/†ºãTœ¶áügÚ@ëP ï§¨Ã ªçÖÔWÝ{õOÕ6žfï×´͜Є@ya.æ&¶nöûiú¢’TXƒ˜KU$õŠhX‹ìrsŠóí{¡ØvŒôŸ‰?>G%mÂh’çÆØ8–GØ?¨”ÕL€% à{Oß‹-ßÜ[…~Ö ›&!-+ž”x™äª«®<÷¹Ï ŽuÔQ8æ˜cp÷Ýw[æÆyç‡3Ï<Óžc˜!œÌÌÌàŒ3ΈžÓV^ò’—àÆoÄË_þrE¢(°aÃ,,,ØsÞûÞ÷âŒ3Îè­ÎI¦ Çš‘Ja²Âå Ð%Êñ.è¹ï`4ú6Î{ÛwQªY×@ ¼#5àÑLz ÀG¥¹>Èÿ€§Ü¦Œ–’çÅrÑëرâùC"žÑÅÑýxþ7cnñ!'–_RB‚rÅ#®ŒÈ ·×óÛž%Bþd¾÷Úx0À`û®5 hÔ%5GºGM ‹€‡ùß}Ÿ9àPð#0"b…/’ñow„`…Y 6iÀ§ãˆñúÞÆ¤xqõ~yA=Ž @"ÎJ €¤ðdƒJѹÂó­$|4ÿDê ó‹-6ólÆªÐØ“À¶~pãES¦[ç ¶úíóA†z¿Ï«$¤Ô^3²s¨“ǃ´³/àη¾¡í·ÏŸ€Ð4û–J„>Dózø†®È€b Zlóóqa-’w¾Õ÷]ëZ\ådœ×觚 ãu—„ö3]ç]sìBóã¸ûÝgês@ŒŽï{3åpÏ,ŠÈ\æú€³ßžOÊò÷ý€ÛO¾{ÉA'œ$@–I%Š<ÒbBª*`ã„|@ç¡ß%²¢¬¶å’>ëz衇l¨È3žñ öœŸø‰ŸÀÝwßOúÓ8÷ÜsqðÁŒ˜Üwß}xÝë^×K{¼ï}ïÃi§†·½ím¸óÎ;±¸¸xÖ³ž…·¿ýíxá _Øk}’LA5%ðA¤ì*$paa'P³7´šsQ…Id wÒ1ʘ|Ô€‡ItJ¼PñD Êh6’=¡äOIÖ|,êLH„HømSŽx0€k ðè[XF!ü‘äã•· }ÎÀ;¥†(Š žŒ¢8£Ñ(ÇOÔýP›¸oÍs×fßHæ;'õ©½×s ÉôXs"@= kDab…S^Ô ¾Ö(‡&Ü–õðÊX¨À¹Ê¸ßƦN™é Ÿä©eyPÀßì¬×Ü«?õÁ{ %‰*±I• ñXÒ0âå^bÚcdJ0¸UJXà#% K*¬ŒhY€‡/!B½^Á*5Ê } €‘1ù>¹ºÚ)fFi,ÔA(‡ãþS~ß;f7Nÿ‡ë¡ê2#lNiµÇ2–ÝÁ}„‘Á)á’°l8f([ õe¹+¼Ï%‘}Â&è…‡„Y§8AqmezÏX×áþ>¶^<ŒÝ¿¢õFçHì;ˆßÔØ`XNC«12!í!ðy|a½äóeôyc RC!ðð}"eàe«™ ´,¯~„™À…î±€‡fç¶‹/ÒÂ%¼ÎK‚Í þ}Åfüçϼ¹g~ðûPjfE4‰ÉÉÏ·!«Rs¡dpæÍ ,øÁÝ«Àlާ™á½Î_Ê” €xž´t‰E'²ÒZ|sŽTúº>Wiê0N˜ö»¾eq<ñxÞþ¿°ðDoe?úè£ö·ÄÞ8äC?üá£e-,,à†nÀe—]†¾ð…8óÌ3qä‘Gâƒü`oíåäè£ÆÑG½¤uH2=¦âŠÉqPœÁÅiÛËkH€3¬k[r U„ÕÑ—¦ò¤­‡.ç u3hÀCRøpÃSÒú wI®ÒbR[š‰)œøƒåƒ&^ÜeûðcàðòÕÄËTÒJ'ä<¤÷‡äqdÞ-|S¹!.ëÁ}&Õþ²|?öõÏáˆÛÂØSlé2£¾ÍãAÊwÎõÏ÷(5"yûãï@“ûÅWô‹MxðYÿOùÿn„Ö{P½ŸA M—cÍ‘˜ÔœÒœ"yŠ©ÆaGaõÀ¦®Ì1& º2 3NˆÌº¶@Žmƒ€tó¶ò"!ƒ@5ýÓeZ¡ºW=²à‡¹^×`!P8¯Í$ŒŠ)½Ã À>è}¤ÊçêèULÙ ØaÿW4GšÝÁ•ãÖ鼂Q®ÔzÌl{5ÔcŸÇÂBÜ,¯Z6nÝç]át¨1 ­çpìMŸÄ±jô¨î§Ãª_Ö6-›Æ>ÂÞåÎñÃZ˜°©ê7éi£„ˆ IDATܸ'ô‡u—" —ãÅϱFÇoç~pίhÞÉ8¨tÿî}ˆ¼S;˜ùÈ_\ 6¥BQf#έ喯¾_üÚ;–¤ì}ûöÙß³³³ì9ëÖ­ìÝ»7ZÖá‡ޝ}ík½µm4áöÛodzžõ,gÿM7ÝÔªœõë×ãÏx6mÚÔ[ÛV³LA©È¢K@«¢±Á ð²>(•Ð^/xÙ£1ç- i3ÈÏ ÂÌÌaaþ›GrYþ>Å$‰¬÷Ç%ÇÀLÜ‹›lâ½}Š4¯¼ÏJÎ;!/ƒD}HewÆß‰|PöÁØŒ(¢Ô3×ðàŽÉÉY‹$øÁ¯,Àƒs~¸‹½>â±Ôzãñ#(ËÇ…ÛI£›gä©@ÄxgXlXxH H<6¶Lú›T-x³• ÷ Û®õ~<ùë×¢Ä ³€ŠÁ6Œ¿ ­÷ó÷ÁÚZXÀëÊWôð#*†nà$åG—ª¶çäx¹MeÌûï)žÞés¤n›‚.NOÁ°¾gÊDñBÊ´*¹ÑXÀ#‡Õƒô;rf¿Ô7–èhæ»Ây¾´îàQ­LâQÁoì ÷£ò›f•ƒ=ßÀh|Ÿ|.'è`Â"S좴˜yn„r¼óåmnIj´‡ ð‘ÊiVϹ‰PYÄt™J\ÿvWÏ WŒ‘¤K¨Ið=Ô Zc9n²õó"_ßöÛ3÷Ç€;c ž¯€üäÉoÂéÏü5ûÿüÂøóü×^ÊÞ°aƒý=XàìڲqãÆ^êÌ•sÎ97Þx#^óš×àÊ+¯´ûŸûÜçBåÓTmÿßù¼å-oé»™«N¦ Çš“¼WÌ £öQ©w|`“œüˆÄ\GCTžRÑЪ§|®ÎA`¨øÜEbb§{`vpF2~ºNtW]?ª•·­tB£Ì6V)oaKÀ£cN ÑïšP6Âf° M=ÙWûPïsÁª×ØÛ’6BÚ* ñÕiæ‡S@ýî IËnžAá^CøÛPµ¦6€ Ü3YpÏ“”š ËEã-¬–×^o=åÜpûŽ›ñ°y&M}ch½¿iÃ` °á¿ß-–—1T§˜·ÏÇãŽì*R@k@9•ÌS c!u¹ûZ*ÓBØ¥ó¯ðý²ÏOdbqý·av8˶×F·³D´nž¿!UZcW›%§ Ç‚&OÆHÃŒ¡Ç±‚è%‘ƒ¤tm¯pØßõ17q©9N€À‚n›=6ˆi&ø;(°x„ù¹/‡÷æ…#ŠÇ@úÃDy¶Œ˜ùÍ,5:ržƒÑ%”ÖõHç²0 Jгù`RaV>ûÃ?šëÈ7惱¾ÔŠFÆG†¥’“S-¶?ÖWâßQ;`$O‡£ºyWYªÕ9™¬ÃÌ`Ý’”ý”§<ÅþÞ³g¶nÝœ³gÏ“c9å®»î‚ÖßûÞ÷‚c¿ø‹¿ˆãŽ;.«œ}ûöáŽ;îÀ;Þñzè¡øÕ_ýÕ¾›ºªd z¬ ©Ñûe’f’«•Bo÷ ?| –û£Êb€­K,޾ÅÑ}0L m”ÍHhKSc¨36o ¶XÈý4uT‰ÈŠâ`èr_]ZÑNIJQt‘”ë'6ïyö*Ã]~x쇼z è²<&‘0ù¨@Ý÷A ÿ0ÉñAËtÀÁDqÀ†wàÃQi ièÕÿþù•ÑrÿO¾ GÞraVÔß¿ f1 ¶ œ¿£ A!a˜õR§cèÅû Ÿø~›:'Çsç^ÜoVÀ*AÝ1ö€9Ö8q<©©{ òƪœØì󜳣„Ëö0`…ÄüÆ~GÜ2*ࣨÆ@e5ŽÅå¾÷Ÿ ÄÜ0>x!µ§fx( Ð$ú5Çøç‘Ɉ`ÙO䞸¶™sý}éz»€"ùå´ÊB›LTi_˲HŸup PJAkû￟=î¿ÿ~À‰'žØ_ÅòéOŸøÄ'ðò—¿ÜÙüñÇãÃþpëòî¿ÿ~üÂ/üÂô˜Ê€˜8cÆ#‘eÈt`{øŠ+]­…@~Tâ*ã•p¦F±ær„°·Ã)5¢•äæDp„gcdUGØ J ñØÉãЯ¿…®ÞG©ç–…f˜¬£«ç®Óó$—ÇX7‰D~ë#yÉU>¨á´DÉccÀ]Š7¼°ôBÎ|àpÕÈûˆI9†®ªÒFˆrYU"±Ê[iûû»2lf±{ûq8ôÎ{Ò€È7¨c¢Ç–®íéÉy.1á2i´£¸Ï7j;­Bù"@o¦8áQöKj%‡êœˆ1Nóäz;=ª½]jV ÝP eÿ²®«´ ¢ÝqÃ5@‚0œ æ¹2ø^å*Ï»OCˆÀÃ?˜:TìàVwñó;$Ú~6¬žàaöåþwçðM®Ã>â®iîƒ:wdà#"´¥¾C»Óí‡ö;+šï¬9·'w2§nGÓƒ.L*¾*„óü¶pÒâÙg†£´]ÑíGY¶nÝŠ“N: ;wîÄí·ßÎf…—Ÿù™ŸYÖ¶wÜq,›ã—~é—Z—uçwâðÃoµÔî*ËçþŸÊЉ™¦ÿ‰NÖ­ã(] pC&ÔS5ðw3Ç›­¡;¿‹a ôôIó¯aw”#ø+¶¬*Ñ¥m—õJë¶ýç½õ=¯‡RëP¨õö9³4rŽÁ2A˜…ØÖ˜¤ú ¹×èñIÞS¤ ^ÁhæÁ‰ãà±Býʲ\Cø ¾óï`ëÖ­øÙŸýÙån+¿û»¿Ûêü/}éK8þøãqòÉ'c0øÑyo’L™k@”ñ8ÁUÊÛ­öÂs1ŽUÙ&8­éÄ”õahY\E¸’º\Y\C)¾‰¶/—´fe¥V—sXØõ7PjTqT1 ]î…Æåø±æ<ŽÁ²_rÚÈôÉÖ9V˜„§Q–Äøhù.']y'¶T]¼°Æ“æ°<˜ålCæ‡$cH ÿbÒV1нÛêØØÆ¦k =,‹l¡®se߸`‰P_Ô{ìåÖ°`ªY‰!ýlâ‰ï¼ç¬˜¸{Ž e{L&é¾ÞÁä\2wÈ…Å,É財‘jÐ|¿z…pø0†JÅÞ„F-"c¿æ“7â2ºøãèmÚJC Ã-¹òvùÏXè AþçÆóï”WÈÌ5çï9P² /bTíåûqlLª~a,ö˜."ÊD¤ E¦ ~?0ÿ o.¥€‡Ç´°}‰ ŒÕ툯—q§xc±Ýg€LÇÙµ¾Ö+Kz€²Ü»òP¦Ð{ð¿.\‡½¦,u¿{aK£3Âz öë1\L.¦ ›$”¢¬¶¥–ÿÖ_àßï¸e˹mÿþ*$¶,ùF¾þõ¯Ç¥—^Š¿û»¿Ã»Þõ.'aé_ýÕ_¡,K¼ñot’žH²iÓ&ÌÎÎb×®]øƒ?øƒ•nÎ’ËôX2œ}Ôø>6Ñ`*:¶„rœÃ±áü¾dI¨¦†¢Ì`Á2Fšç&PéÍ=Hïƒ}¦Þ¹Ð¤Ë Ð0Š\ðŒÌ¸ã]fsy¸ËArí Û¶ï!ÎÉZc¬‘AP‡â’WÎFâ}3¯î Æ?|®¢OëÏ;ÄPIUšUÿ®ÁDÆ~çZ®—õ\Fæ('l¡´9¤šœ2¹ïJ2B<ÀƒégNˆi{ë•uŽñø‡õØ—/ñÕŒª±1XÅ(ZF‘ÜÏæÉl’@Ffî¦`¸“«$k¬çÆœˆ^¼ºài'ü2N;á—1¿ð8.ýðe]óÈ#àæ›o|á _Àââ"ff\³xëÖ­¸æškðâ¿oxÃðñ‡¸õÖ[ñÎw¾/|á ñæ7¿¹÷û™TʲÄ?ÿó?ãŸþéŸpï½÷â°Ãù瞋;v`ýúõö¼g>ó™øáˆ 6¬ ¦‡ÒzÒ<½SY­òøããÐCŶuO†Âgn= ?ù$²¦³C1—c‘~¤ë=±^P=rãý_Á¸Ür¼»>FÐåÈ oaÂ5XEŒ ψNŽ1*u `õ8)&§V -ûÞŒRϱŒˆ^5ÿ—R íæAB€|7Ä+î…]¼¦õµV& qJIn5-] Q-; ¥L"Z7ÜÈ(}Õ7°à†üä°w2•T²É)Â1=" ‡ìAt^.„È©‡SνHôxÇåsꦉF)ïAL2 Ì!ŸB¦Ê»»ŸŽÅ®aì¶“Ò¹}v\Î<+î7ä”mC껢P‰†é¾§ xˆm–ÊJÜw LŠÄùa¤,2æøáDÔëŒS“C oØk$¤ú¶FÞ·LÅí£±ˆü:²u–(› y^¡x}Éï‰ñZÌ["è;ñÂÚ}WÁu&„¬Ö,×õ˜™933Ûñ¿ùüÜUb~ÿ «ÞW”1“&±þ{þ9@«Ãæ} gUZ7ÜV^·kÍF­ßÑWvÝŽ[v}ã²Ä®Å=ø^~?ÖÍ’]Ö¤b@Ç{ ‡Â×;qæ™gâŽ;îÀÞ½{íþÃ; gu>úÑ×ÜvÛmxûÛߎ;ï¼7nÄÞ½{ñÊW¾oxÃVXðýïçŸ>n½õVP_)…ã?×]wŽ=öØláÊÉôøzüþ‰oÂl¡CÀüL¢Ï=‚粉) ” !” Ú^&.Vò€mnc´ ÷•Jb%ÓšùºY…ÃSòª•B1ØŠB­‡F‰r¼ ‹ã‡¡Ë}Aª½fÊX’ï»Ôù¬H}¯H(ûK z´•6^úFÚ‚ÕþAð›5ÞbÀGLrôô–= cK z  ´ÒòægüíàfÉѱ7®¥ Ò\ÐÃi# ® øÝH ØHiƒ+RFÆwÁdmæž…2šÂé|ï·ß¸Hû£¬‡P±ãq&àa‹"IÀC`³ù}® ØŸ=V²s àöÀ¥€‡©€f&G Ç䈃N;9PÃŽr^¬(¨éϵàQ=ƒ §RF˜Q®qJËŒ‹’:&7¦#øaêRÍû.Š ždËj>1ÀàA ~‹ç¸˜ìȘS¨ˆz˜ÕÕ¼>m¯tâÉôvÜÑ%=ÿu÷ÇðÆ—-?èñÞÄAeÙ»w/Î<óLh­ñ¢½?þã?Žápˆ‡zwÝu>ö±áàƒÆW¿úÕ5‘¸Ô—ixËRÏš¼ê€G¯BøŠ° ãòa”å>¨hõš 왨¸ãýTᲸ¬.£&·Áñ\C™“I’rFïAÔ(QÖù‡<$1€"5„£ÈÏíaÂ\‚Ð4Ó†D¼s%™Ê^Œ.íÑÒº5IF4c…¥ûúÏÌj!ËË8¯o·»ÊGä›eC\œ¾é21ø]`ÏIUÝ¿EI‚д+3€þ͆G Œçß86Æ„ù–xƒÀ‚ÏàAŠbæ .\2 (Ñ} X5:#ÌI¢à0 Q2ç5+ˆx××ç»:JÈò¨®© ŸÝ¡†¶Æp¦€G3–hUåjžÉ;6”+(°Úä‚ÑUáÞ3ïð û¹Ð$ó,üK":H—|fþJ.RÛü6Ùv0ßun;šq¾ ËrŸ°«gšbx2PÁGW<áû+°#¦×ÆÚ¢›œFÑù+kŽ¢c›;Þ÷'«‹ý°VäÏþìÏpÒI'áCú” •¡²,qÉ%—àOÿôOñ{¿÷{+Е•þµ•©¬>&¼†‡Èòð«Èž8+¥d¸ál\ÿ†Ó¡Ôj½³Y8¿Ù‰ñÎÆQra²ŠåÈÝà=«6yÃ(`!cµò(—ã'0^|‹‹b<~´fÐŒÝwð<–Kø¤‘gEžïRÈr$¯U*ž„/_ „}8£Ü\/ ãYRµ’N 54ÞêF™Gž-eb$¾ïë¬>7¸žëÛ„}Á±7¢Àã1ãÄey¤…[@–8¥¹)dÀÿþ’nÉqÓç´ey’ú~2æoyRÇP6ùš•]œU^êo{V¦Ï;›°*Fðà©æN]Ú]}ƒ='§ê2½q×mj~ûƒÂÙªd¯ÇH;Vzï¤yOEøÍØ÷Këf… ðP¨b}Åš,R› ÔÀ0ÉÔ,f­ÎR¨õõñ¡üí lJ9Ô …ï)\Eøþ8À®M_ —FÂWý¡þª.öXËÜ* H4‚ÖóÐz~BÀCg|öi³39ÚÓQÃ%‘™oA¹«ÀÐ+ÓßÇ•Ï-ðÞ‡{ŸœžÑA–Po›J\®»î:\~ùå,àEQàOþäOð¹Ï}n™[¶:dÊôXb–EkãMÍóÖæÖï&–T`´ÿfœóÿVËM6ž÷¢ö¨0YúÙ6zº&+Ãlº†íћǿí3ê`¸J|éü¶qâþÿ–G¾4žIV`½:+8A¶Nzš<¶‡ÝÝâã친~À÷™P–‡ÃøÐMb–íaþ矓0ÊÏ´Ý·9®Øb¨ßr$9FúáK,Þ+}|sð<©¦<úL³ˆ°©|¶‡moÀø0â²:tpŽß'WRËMt¿ol‰àGP^ ¨a`°Rˆ‚"äF A?‰ !ì$-@ЉëQ…àíwYœ8L¥œy ¡Xq”™Aêð5ê¼@À  Ž…ªì±½¦b, bzŒšÂI¨™ÝÅ2<"¡mß[#'VfN}°‹IA)H’`~8ì>í®Úž}ÒFâyŒôç(»ÃÙÇèÛÞ3q0ºn+ZNnݰXhÍcäèE©Q”Ë—Ea9ëZ²nÝ:lÞ¼9zÎ`0¶®Y›w½ÖÄx•¨a’khH“\,&“õ¤6Ôs“äOS%ÀÖ]$— ì’ãQSáLðüDB$#‡‡ü ýz.6ÓI¯¡-¹á*H+6Ë)­€œøÈ ^ô¹ L–0á.<ðÀ,•ªøÑÙñçê²´ÚOžäˆ|0us}Ÿ*‡b‰_.a¸ÄÃZW[ɸï€2Îô-úíÑþçQ -ðáµ] i Ál‘Üâ¬ÑëïTÑ WÀ‡Ù¯ÇÐÕà‡1²Bº˜ÒŸëÌÿ-QÉÌ‘0ÉRÅlØDz}+òýÉ…ó^xXaXˆï·iwIX³õ˜; 5ØZ/÷Tà‡^LØ,ÆXxÊÇú‡þ åøa(½€Ð¼€ÑLÀ£mŠÌ0‘ç 5Ÿl‰0ªòÄÕaœ\gNgT*5 Bd_“ºÆé#^+À#ú=@( |0åW×7ÎÀ˜^‡ÍÉàÇ$cÉT–Næææ²Î›ŸŸ_â–¬N™‚kBþöÞ=責ªü{óû2³ ª¨ªÊ*PA(¦y?Ê¡•lqlà<ƒ g‚@ ÆBŒ™‰f°dÂØR6¶F‹ò E –B¬¶h´ JªŠG=2+óûòœ=œ³÷^kíµöãÜ{¿Ìä»+"#ïwÎ>{ïsÎ>{¯õ[¿µö€°…^ H0Çè—ab pÝ>Y”@¢Lä@AIªì¾ë…ëÖ*Š2RT<ˆG+*–ƒðnðX` x$Šø9´X­=¯‘Y€Êº¼Y,³÷^.§ö¡$Òs bÌ$ÀGôª%¬É©iŸÑÏ[„*̃`˜àÛÍLÊ¡}ÿ[ûæ E‹ñò¼¿>Æ2aá$1á€2NuÃäœPp×Å+yX±C#˜Lž|·‡4àiÞÎX¬X÷3®ÈÒT5´\„ís¢4Hìàeõ0£’$@l·Œ€ÇâþpÇŒay‹ý{Ñí®¿>¿‡sÀ±¯Þˆ¡¿ÎÝ¿”g«š„5¹û)‰šOë ÁôBh&ÍŸãfè'’0¹ôp-ÿM¾O%‘@ذŽSÀƒä¹¥} X¬è˜£™õ/ÿÓm{ÏeùÄßý:>qÓuÎ…µê,ÊCò¼ûÝïÆóž÷<³Ìûßÿ~\qÅØ«sG¶ Ç!×ÙMá4üÃ*M`ˆåU2Y cý\ái¢ÁûÙ-XRÄ®†¡VÝg:aó0ÐEþ¢_oIÃB0ðàÛ‡RåHWdj=9%c.Û-+ÄÅt5 ï:/%™ZPY‰aÒò½”ÊU?oü0 £]Âa݇A_§€›Êúè̺ŸÉkÑÏG©x6anØ\æ)ë ÀGðÌ’:ËP©IÊÀàà ‰øAvŸ‚õ(à {®¤ÇÓ¹àà”äÚ¼Sü"%@·@×C×›xcwòDO«Ûç×IVÁêëÚØQÁòX ¢Y¶Äœ£‰v:àÁ EbD²6'Гè:˜Õêâ¯ï×ã_v;.¾ý<ë/NÞAÞYaïæä»îÄß9‘óHv79zB¶í”‘ÄŽ×Ì[5ï%+<ìI׳–aM êŸÂîÒòpXù6Ri:Ô9Q©#Z’— 0KÖP…®­Öo”+Î Q碳caΛ'>ògðÄGþ NïÝóûWžíîœ5yÕ«^…k¯½¿ò+¿‚ù‘Á·|Ë·„s_üâñðxýë_?ÿó??‹½<{²=…ŒL7å·0€ƒãµ´=[XŒ½¬—÷N&"3Ë«æË@#ÍÀ*õ£äh›ömy{„Ò£)1ÊôØwã–gü4®xß«‹ ³ÛïVÅì*ôÙŽî@RiÈT…rVŸšEü8󆊑ùChøÀ*Rs=32+ží }J€Ž*àc…g@ Ò< c‚Ô½©—%@']Tz8L;; @À_«Õ§·Cû“UÊ\¶눘GEž"õ>żªnËØ)½Ù20Ö­«ê K‡%‹‹ð…ïý1<⣋ýû>4õÃ3=2†Èt~6³Csçȃ:O8¤ý-Ä­€‡ÿ]œs,àœdm–ÇpÏ¿Œ—Ü —“÷Ý#äìqô÷¾sKr¡+k¾ŠE-=AöÁÿ´¢ÉÐ óÜ1É它5Vd„iuJFœ(“ËA— iYt÷Ãbq!úá)XJ$Ël”/:f¦ë‚í ÐîÐà4Uå0ý&–'?ùÉxÝë^‡—¿üåxùË_Ž£Gbww'OžDßãç—ù—ñ”§<å,÷ôìÈô8â†}¸Å2$|Ò€€€øÐê4$C9”!ˆg{ þ?m÷Æ”ö™’KÚ@@Äõ™¶-Ï']´­çŠ`kdåâ’©®ýXg%àq.äË ’{ž&kd…:£x1ðÜò¥=zì‹À««”[ULÓÛÌœ%àc”ASu“±<€xt;ãnXî¾ñÒ)Q`r»Šv<¼LàjY9ó!k¸l£%“ø€ª­–IŒÞ ðÃÊë‘ ð#izCÀÇØm0¶"+)ÞS²Õ²œú¯âzú7ßwÒïLäw^™æ ð»ZPã76h³;šv³É±zr@:]sJï¤1¤¯tÒä’¡Vb{i3¬yøb‚2>H·º]¸nøí§âҼݙ¯‹ãÀ°7ø„ë à™ÜÀ‚-Íëp ­8ªwïì¹Í2ˆ³9•jeA@ È]·Ã™„þhk¾2®TcŸ?_# `‡z9ÍMÓÃòÈïI+û IDAT·âÔƒ_€Ý|†áîô]ä@©†v}_“P— – /øs~{h+“ŸýÙŸÅ#ñ¼öµ¯Åg>ó™çãá8^÷º×áE/zÑYîáÙ“-èqÄ+e0IhIC–¼.Vy†eQ>áŽ×/ƒGFzãkD£rÇßœáQï¡ÖƒöI½)®Ö pØÇÉËþü/à´ëëòç3 N•pÊ{‘Sóœ­2Výu[Ípb2ªƒÂæ†<”ø5òðƒ]»:XǪ£cmÝ€JN*!Ór=?ï…µ†‡ßAÁSÍ÷0&\MÙÓ…S_Uà#ëÙŸcà{à…ˆdkæwl‡røã}rNc·˜cJº ~de&ÃeŽ¥‚sÓ:@×½P~2jôøžßøÝø–Ü>œóa û!\”)ø†—v%ÀƒU´#‚TmÀG¶NƒíÑ|˜Bǵ0šS‰l™Ç%0ÂàÏÿ–§îÀ…wC¿{?Ùûútù¶4VGçd“þ’|ßK²›œ½ž±±A¾“,ƒSHQG0ìÖˆÜ/_Ø;áur0œ%åì–,o™ÆôÐ+`ŠÂ×(ÕáYVù„Ñ4¹Ã½øËy+®ýõËÆ9§ÈX…îd%-•:zi˜ P•ûaö€|låÜ“¾ïñÜç>Ï}îsñå/·Ür .»ì2<ìa;Û];ë²=ƒ8<Ž(%ÝĪpuwCDƒ‘‚±>i¬6ñECÈ/R€w¡KÞ¦@«-eÂôl®AÁSEgy´(.´œùl ž¿R{-¸-ÀG•WF•X¿-)ƒ#íÀ¤à.v&&ÁŽ<–GŒwÿ܃a¸4yšžï…2‡ à‡ìÊÌ1§]§±¨jÞiMÔPŸ*¶G¥„wºŒȤ¸!D¾Ü.Æ$‚vÊjBŒm9›ñÊ 8 :M?Ê8Za=€ªÉПäùÔ+¼M ë;jÌ; Vò üyäÞ"ÚMÛÖ†‡g{xÔñm‰µvgÊl¼&&˪¬¾JÀcÕÄÞÙ°× IHtœxé•oÅícÑŸÁâÌ0œ`—£€‡+•%á¡¶|­«Ùñ­ÜHÎçUfÞ'%¡lÔ c¡¥WI¸K=£BÛ²ÕK™}\!5 ‡R>É_åzôÃ7ð/þíŸaèŽÁç û˜:b@uú–d"ð"»HYä}±gFìø(ËvËÚƒ•7½éMxÍk^ƒ—¾ô¥xË[Þ‚«®º W]uÕÙîÖ9#[ÐãPˆgz DÞ‡_L<øá=`üðS)e*_]‘ñžXÝ3˜Ðž5eÝHŠšÝw†ŽTl+ÁŽ*EXÒžkî->è¹Ô¨©;4°À96¡Š<ŒñF ê®ÛÁ‘#Ä‘‡áo¾ÿ)øÎw½g2æ`8Âki±>|ŸsÀGµ’UŒ¯MC´6Ö¬ËLÄ+i1Ù(ò;I#ËÃu£>2¿J÷æ }w‡4WDôÎæ“›®knȽcÝÛËDù±åA_$ÀG¨N•´ÐŸ€µÿJ£â5¢æ ÐÏ/ ¬S`»bùßKL k©êTððç7¹»ÕAa^$ÆsµáI枪gÑ)Éi™æåž¾ñ°§âö«z\ýWŸ†ûà†{ìnü·.À#t·“9Æ„#~ÿµ!«LÏR"ç¬äÖ»,³bܱëgŒ?Kpå2u¹MŒ{0õxPPÉázwJm›²<ÌöºünSÉ\Ÿ»Ní§`|ˆÈªp­0Ï¢Óà˯ýÚ¯¡ï{ÜsÏ=g»+ç¤lAC"ÔScM=bKÁ©<?üõÒɲ"4·€ZÆ£ŠfX -àG¨ß‚F½—*Q(Ì¢¿¼½Êz À‘ÄõWÆu¯†45Ïvá>J€G‰ÁsoT”+Òœ£8 Øßû<®þãàŒ£['/bÖø\^… ðaö³’ŽÿÎx»sˆbÈ®Sl¶GDF‡ÿ;ô“zÛÅy¹ »®%(rbÞ8gD¡ÇS!€G­âMŸ_.Q´J× €uÀJFwKîs×!\ëÖÃQÌÝQŒg‹yÄõŠù^m»@eoaÚkV1?Z¬ŽF¶ÝÍ,cÆšr®4ovÀþáïpñ­Áõw@únÅíf½Xϳæ9Ç1ÇÙâ[ª]Ûs’yïZÂÝ ™p$k,–’ŠªßñŒ¹#瀣ïZ»ErMZ¦È”ã×øuŒ<YOºÒ'sú®„Ã ó »§¤½-ùdÌðÿ,iÈoåœïøŽïÀ§>õ)\rÉ%Ùr/{ÙËpÝu×P¯ÎÙ‚‡A<WÐÔh¸‹ÏõL ¢g~Èd¢ ëÃn7Uf¤Ç¦ìQ0ŒªØ(!0èH@ŽNÝ7wrȄϔ”ImÑi2´ æU†³¹BjC‘j”Îj,Là#• àQÁj[µ&€IVÁ‰ ‡›¨ËCöú{àÜ>†á$0ì§×5ITìKl¤_Éñ‰½bíNBÁ;• ©úçˆX”hõ9ôˆ»®@(p˜|hse1ï'óëR(f¿R¯šx±lÈñP® «‰¢ø!•€&¬ßðN¶E-Íà ð1мހ{ŽzÜ›X|®´Áé<ø@¬ŸFûss§´€• U²ë•å ñ`ªÂê0AŸló`òØN`Î݇îÌèÎ|eJžÜ#nEÛ³kŠâ V£½ ~T}k;€àáyàƒ¥I’jÔ®û´Ì´ffÀ®о٩€©ÿsæg«.³¼ÂÊcú´xP)…\ºèzÀ‘°t,štÌüvºÖ˜_¤}µdÀîÞrØÓŒüÜÏý~ñozÓ›°³³c–ûЇ>t€½:wd zq=€®Txä6„»ððÞõŽ„·0*.²—gË+ã•Æêk3J°sûzb´ ûH=x¨Væg‰Ö? vøÐI¼ÅØ ¹œ.5àdžãèC3µÆKV–ì+)â¾>ËJ¯9x°(·en*K¦ Ö&<ðÛàˆS}&ð1[l6ؼº¤Ì­;²»èß5y˜j’—Êï/Ëö(×ÂÿfŒÓšç“¯ ó æÑ^»$uç%ÙoebwŒ€„?+º 9ZD1xs“Ќ߹’«gv¾•àᥠ|¨RË4Ö.Í8ÆZ%Oæ¸N™äSÖ²`kddïÇЦë•[V`.oHnøÏ×ᆛ¯Ãp®9}Xô áÁ~0÷¸Çášk®Ácó\|ñÅèÈVH_ùÊWpÓM7Å^ž=Ù‚‡E¤¬°> ~P°ƒPÙ¤£†šÍàÛÇÍÏê.¤‹‘€ÊQ·w<%·0®!”à`B–òn”Úª*`A~·ˆ÷euXŒ¯Xh”å²q Ý©by°Ê(åuï»[0Ú3ÝVZŽgžÐwÊŒ °aÊÚ§c4LzUYÔç ¬ÖS}â»Ïí\ oÓ+Œ8ì…õo$æõXsG¬ïª TIñdH tp×Qhò«>ÏUrê¶Gîù°ç²6àbÞ|(””UçGÎà¿ ž9Ö•‚Ñ´qе™Éî+ãTÔY¥úøÈ&2]Ã÷™³ÕlEß5ÌÑÐ+ÅÈùdG–Úç"ÃLV ËPwÐ"§ýf­¡•h7Š1WàlšðU.Ï£°•'?âexò#^†Sûwãï:¼‰;è‡~wÝuœsøÜç>ŽSÐÃ9Çþ>L²=›xd=Ãú@vy鉫ÑöOg  ­`{HàÃ/lµyL6H滦a/êŽ5ø`JʨpkYØÇ²kXP[dÍÌ+:MhÈ=˜õŒ«ñà‹î–G.º%ú3_ÃÐß«„(ÕPJ' ¹Ë‚ô®'ž}ª«*çG<ºÅÑiÛV x ¡rœÑ”?V•†Wðd,0©.¥{gžYVž0³b¾1ïE,x¸-ÿÅh¾wK‘Ín(Ž™ Nb܉öÊwΟ‰uÿvS*¥öÖÉöX½þlÛࡲé6Ѿ±9(Ãúhù|JìŽÕ]ÛQHÖY4¢kÙo ð ` €Ý-#+#+¬ëz~Œ†©(ÂÏVFã–!ydƾžøÿ¦Å¯ƒøˆz›ýsÀGv¼ùã C¨îfäbKõOïlÛ‰sm7½·’¬’PÚ`Q˜¬¡VÝ1=:ìÆÃdjc0<Ÿ\¯tƒCw€;ªd[ç¢\~ùåxÄ#—½ìe8rD7ñïºë.¼úÕ¯>àž²=8 @ȱ>Z¼2ÒÙɰÆ2ܘҀ&Ú‚™ËÅ «ðd˜Ô»ž&qª>YØæHvuå(g¨KC(>æ6ƒnqË#B×ÉùUV•âóknVT„Rïï{±¸GŽ\çöp¦¿3 QŸ`5æb^Üøq® IÈ:~óÚ7* §qÎIh訓®¶Ð§ó‰î¡«‹©e³Y Ž vXBçVKnT®7Ê€Y…Ѱ `%xl@dÕ¤Ðk?LPW2Ï´"¦sAþo{ ×’Ò1^<4–G0⺀̭²m]h8œ7éû÷;·èàFQÔ²’2©êAƒb8²?ídélc¬eM;0¾Rb¦È¾§'©N)ÖÉh´èê²—žª³@X£+¥èÙqÙ pÙe—ᵯ}-žûÜçf˽ýío? [²=‘$DÊúR¶ƒ€LuE c!2ƒÚ(Œž¬,RÐU/§TüÃã$í•$1ÍxJ’vM &´^ |¥›¼$FÉF %9ñZ×îPP!ÎgÌNÂuûpô•Û øu7M Z“‹“b¿ƒã÷Þõ¿^ˆùÆ¢ëÓå;Ë0àË’ËÆ’+& «YNÏ`)X?Ä qÆv’’BÊ[’S2sô䵯ü'ç*”R:gh¡ëæ°(Aû»FàHÁ„¬Ð9ZË{rDÝ!©üh™“+škóåÀÃõØï à€òmtØ‘$+Ì –‡x¨;n8%i#àa±<ֺƯ ²Ý¼ü¨XW;ÎÎa`’<ˆ>*1ç@YelÉRù­=+%Œª$ š³Šü àƒéa4 ¶UhX¼nÌ×ÍPH*ïóÿéºî¬­†·²²¼ñoÄÕW_],÷«¿ú«ЛsO¶ Ç!,0*7?q=ó¶Jð# |„´x_I_hÌn·;.ݸû&€Ü½@5PE2\ÎIà#ñ*„Ň@‚ÅyÝŠô:¡lRPiì4?uâ†ÓØßûÆxáwWÊ…¥„®Ü4çòðýR®;}òýøÿã(wz:=*]·LЉúð†ªÝ*da@©PÏ•eøFc?vì‹å3œP=^øÜÃB92ߤ1Îã5õlSdH€òŽºî8–ËoÁ0œÀ0Ü­·©¼G ¹õK-Ë¢…E5JõzÂÚðs_—%t³ï‹(øéEÊzÜ@Ýó®ÙyFö }Ë…ómR’04ƒå‘Œmq\$n–yJ€GºS@ÁPÞž4bcN®ôÓ¹àá¿­ðÐ…9ÀcCR£+´¬gÌy¥€œõ˜ß±öå3¤ ¯Ü’µâ{ôïkÑ]€#—þ·øÛ§^„GþÙ¿Cßß.JF'œ-d|VQÆ# ƀ _v¸µBB»,j‘&b ~dæ×mxËÊÓžö´ªrÏxÆ36Ü“sS¶ Ç!‰«eŒÊü[Æ„§à QA_õ¿»n]wp{pņ!«Ñ’V0D2Y@iœ¹$§%àÃߣïKʸºrs®$Z-Õ Ò& í¨Nu+ èØ%ÖsÌìf£É,ð"©d `ÀàN¡SiÓÄh$ “—¨ü5„?Ùç:Gª'«®ÂÖÙ^ø¸KF×qt‹ûãÄ•OB78\pÛ_cè¿b¿søˆõ) ¥ñ^¹â-æ˜ZPÐb² Y,.Â{êñxÎoÝŠáô§Ä=YÎ^ü}–™c}ûZ€XŠyj¨ƒÏÙ sµ–(Ø`¿ñþ4<ÇLBÚ"ø‘«6yñœ¥áëß-¯Ë<ŠI«»4‘³–Ã#<„—z”iW¿†O†idie@þç*Pܲ–nÏdwŒu ü7@æpLgw R¤&7%¥mjk׿4}ŽÇqJóyp“²Žù–ì¨ô´cøR×íNïá4˜Ô°>Üø^û¯ÿ{\ýîÃpÊ¥>š~X,.Âry†án¸áöb]%F–XÇÓpêðG–…}oIûšœ˜álâÂØ) ¹Ú “að'ò'¸ùæ›qõÕWã~àm’ÒÙ‚‡BzÀåã%Uö‡;ß…¶ éÞ9:1ó—©Ÿêâb,>SÌíè•ëõ2¼"åд8……u‰ªKï0þ6€± X rI(s²!fE¨¾AªKr˜5Ï a™JUÒ I#`ƒÏŒwÄðhVÊôì ¥™çŒé< $ 0íÙdÇUKâˬÃÏÙ9:2âÁøoJêÁËcõÇc»èº] ;ŽûýÓßÃÝpî>4±/Øæ×Y fL„ç@qÊ<ô>e»¬Îüsú¯ãûÿ]~ÿ&½ŠRVùŒ\íb°=ªÂøÒõÄ4Üe"`…}ǯ§õÆP2IçVûkÍÉ1Í“óT¦L ø!/ͼs5lÚšŒ¤ýpH;Æãð0ÙƒÂó<|1ö;ñúè¨ð»ø>ùõ˜=£ø›1<Ü~Êî`a(œÝ¡ƒ ðàG ðHò7åCEx»Š(窜a&kËÉFçÍ%ÏÜåeÁÆB?A:Ïİ”%ºÅ1tÝqtîØÈvs§I)ëC†&)Ó-’o® \8Ûh]w'D!ùñ‰x÷»ßK/½ô,öìÜ•-èqØEL„ê‚#/ Ýý%*AR¡R'SŠà2ƒ«ÝvûðôÓH1ÏôVà0Åòz„2”FiÄVº_h$ÔHÕ€ÙçDñ)µ½æ:$(V9/DüX…™°ñœ'ô°¦<=/ä`ëF’3àß'æVÍZXŒV‡¤¯‹¸jÚ7^5<*ß>¢poܤøzš:¥«»û·Öv|iÜ.&úÑóEç €Ï›*¢´]:çÜ)œ¹ç?ò“9vÕ&Ù,…º6Äv™¢Q]jß_ moˆŽspþÛgëU˜·¶í¯>´¹˜ÖQ³sLñ¼~Ì ø¨‘,»ÈJ¢ÒTà¡„µ°þÐv$ðáCݘ1)ŽéºxŒ°;ÀCcwè¡“)»#ü;ÔðˆŒƒ¡€aE’Qo’Ï( †²R'\|—~ýÛÁGþûÄÓÞþGè‡oÀ¿mþ6·—ÑAd_¹3ŽŽµÁÎÜJôPK¸~êXüÐÃY|¢Ä“<«¥ ¦{¦ë¯RÞ' ÆrZgpÁƒ$Åà°8À“ƒlëlJß÷xþóŸÏ~ö³ìøÇ?þq¼ð…/Ä{ßûÞ³Ô³s[¶ Ç!“t{N®,ð“K¶øÐħ2ì#c{PÃ'|+±ñ\)Ì…/¨”V®Çô*U¸´ cvŒTCFÝÝE‹­ì–øeaþ¾i5öGÁ³X |l4ËûL1·çTÇÆ à£ÖK½&*( Ü‹V·6L† Í““zÇ#›@Þp4Ú"ȶF‰ ¦ r;Èv©¡Q‘òÙéEBO7츗WÛš×@c{Œ¥>_(<@7nªÆ²öN’\5ÂÈXQª¾ölÚU$aiP#ÛÆ!äòtÒ?€ý5@ø@jXÆ1H=ÿi¤dßeK²×Ü7 ó.4Ô«1dVÒ°̓ËâͤRì7¸GB_ j<‹¼¼ |œ‹`‡\tÕ8eÉúh5Ès‹ßAŽPå°£¥~æR•¨±ÂX® ‹IwL‚bœ4Ü— ' 2&(0…¨øò^!Ƥ.'„n2J<`‘xm•{Q„ÇüQ»³¨ÁË ¨úë™·L?šdÆxYk"ÄHøàcw-ÀˆxtËø.œÓ-ñàH4N0±1“<œ1“ëdh—ì¯Ã Ȩ̀9`9»%­5§Z€‡?]û]tFòRåy:^ŒlŸ]Œ;ƒÉÇÜÈ“IK»³„2$l%T§†¶ ðH„s„ŽÁ¹€‡¬Ò+2´¥ä¬ : ýöèÜ?É0ôØ?ý7ÓñùßÍg Ÿ[TQ¿%ìHJ‚”׸¨ëÅŠä€ùZaÌ(rïÎsÓ»•Ìf¬¨Óϧ>ì§ñÔ‡ý4Níß_þßv mŸ ùýßÿ}üîïþ.~ð0{øÃާ?ýé¸êª«pýõ×oAE¶ Ç!×°Pª ˆ?hÞ2ð€€8C„æ÷`ɱ´øBÎw¥©€dû{ô€G6^Ò{‚eb„º0°`‰Mé3 †À”…ÄC\|ŒåíØÌMʺ¶®5Ùáüja.딪0+ ¸Ïòˆ€áhŒé¹RϺ¯#ßáy@ˆ.3ÇXæytÝ1 ð!â,Ž$Ó¹$ˆò·ÿÞÓ÷AÚdg©”wKÖ§ àD2»Š±óíßÐZ#ÞVv,+é8-fö7Îë¬ ÿ=TŠóà×€ÈðEã®n5wb¤­[Jy¬òš°„“4ׂ56“ceÀCÛ¡%[ï\0¸"Œ›Ö¥Î‰>©õ‰mhI;5€G:ÆtÀ£å>'I¡üÉåó(³lf¹9LFðÎ'×]tƒú,lÐJiW3æåüEA¶ªgŸ;RÇo[ «6d¥¼jµNé»ôs§–÷ÈÿtÃÁ1?²­³)_þò—àAå¥/})®½öÚîÑù![ÐãP‹fHqñF,ÍÅ0ø|¨KÂò`!0Èt[ž¦,àaxä/ãZчEÁSË™æâ™„¶PvÂXº•­ÌÜ-•ÿ,È}$ù@ŒÅîœdv¬"$¬J&7MËÎ[ðÖöÌèX£ ¿¥v‹&e‘‰e#Ó¢ì—ò)d•Ö¥Š]N4Š/à•ÍË!ØTAúiǦ2Ke–ø¹…Pämàü4€&Õ¹×ßGM·3a4uþ©>V…Ñv ‹FÃd„ºq§†Ùlpçf[,µÂ¶YœWÌÄ¿{@ÈÍ¥ííNôùÌ\Êß‹xa&cr¸}È ÐÁê¯< ÆÇ ï? I¬ !ÈmÑn7ÆŸv\o+à  ÿ§·+Ù³QÆq0ËûÓ·u? 1àŠö!CÒa–ä°ë¦±ï³KÁõQ2N¹ð·íˆóÌ´øÐrÌ[Øš²Fˆ×Ûu\äö8$!&gS.¼ðBóÜE]t€=9¿d zJ±Kzœ/L, %ÀÃU¦¿t’ö!¦ßÞýÉYC¼®*Æ]<’k鹓ØÝŽHR7Ñ^»ñŒ}镸ÉèÝ¥™¶åâÃéˆTÁ3à`àÇøˆ$[Ü2àƒ×¯ziN’œ¥¸é´›>ìò{˜þ†¨7ûˆìåaË"¯¸ÀCÏãA<ÏVHÃŠï® Ø’mðC5P3 Fùw¯5á}-€ ˹‚AŸO5ð‰U"Á ¯;w”c Ⱥ“w>¨`G2vVyߢêëÒû\µúnÞ' Èڜ՟(ð1`Ìóá q…•¦±ù˜p"&µ§}ž)´Š±1‚ô¼®œ~ª:àü™Â%–ÇØ@ÆéæÏËò³Å÷stNÈÝ„ hßJ£GfÏ?~<{þóŸÿ<¾ë»¾k]:/d zlźèŒÂ¶5SÂUØn.IrÓˆÓpÎòP¶›3’ ævTàâ]Žè0£÷èúhL¹%L¶‡™‡C àd’ùX8‚¢ÔP‰;Ã…g:¶và£uqd÷©‡¸TÅ~ûÓ9e«%·G&h­b5LrÍ(Zÿ¼¡W§à¦q÷Psd¼‡ºåu´lz\hB|ø¨”$-tÌÒ±â\š&ùÕ˜æTçеn¥¶‚d[ç³¼ä%/ÁG>ò‘³Ý—-èq(¥Þ€’ʇ>(­8”Qw4€™ç#²<(ý–´ò}Ð…¦”·‚1»÷RçôÂ17À8yù6ö§û˜PíÛ#ˆ‚°sšæø#ПmbÊņ†ÀÈœ(àm®Å¸Yƒ!VKj•æÚE¹p­ö|J÷[¢Œ†ßº—T3"Ôªº%4"W>Ÿ£ ­—±Œ‚èˆÞÙ?ðð×*agã³ €‡4–­ûŠ ¯«³vB9xÄmqûé›T¹vï›ÍB«Zž0ÁˆíQµCT2•Ö™–uH‚pðÁŽð»êùðq­}[Î{7MvP<²îºÞåœ|0hõdÅþØ"~¿ÙJ+ÆwÅ<v³¢!.µ9ý ¾]¨™Ð›ê( "ÎÆXó÷´.QƒÞá¡2¬>ñÛ̼—jG’òÎêÂ7z;b-ÈX¡œ¦{Ê5̯¾,k»Ž5­/–¢KÖ4Â:sÊ☀â ¤à:g{`BÒíkM¶q…˜€G¸/» xlPߊ){{2)s½ À›o¾y½9d zZÙð1)øˆ^œðAêlŸ°?Ô…œ1Ó‚´¸?°¸FÚótˆûµOÃfGÚeí’§FîæâŸ%g}€—Öc…Ö·5[ƒ¢Uk„QV‡¤óö"8ìÈõËÊ9a$…ÍÖUÓ¾¼¯à‘€Ù¶iΔÈ‘Ún.´,­/¼+G•-ù®Ê句á!“¿…sÓsI‘jƆ1n*Çœ~\ÌäÈ‘&(ÕÄð¾åhÇÓ=Øs‰6¿e<Ú¢ÞxŽŽ‘©Ž†P9;±°0®X»Ö˜/’ ÀëK$FëNPÅpÑ.½ÆWay“•ç1›íÑÈ(QUñ F×õY†¸ð-M pË;™¬~' vÿ œ0Rljj4Šð•Ø1;±'-D™±Qóì×ápXƒ¤;÷åØ:ü93¿†¾× \aSIlÜé–°ùü±nW„°Èµ…œ#Æði½ItÎ>èz’2ø€GiœZkJ®ÇÍVV“n¸/ùËÍ0–›nº /~ñ‹Õs·Ývî¼óÎMv-èqäm·|‹®Ã/þv<éâï gÚÔ-lPö-C‡^Lû©ïb0]êÿ&9QX‚ºM„¹TˆÒ“)›Üà⨰fæ×e{²ð°ò^Ø,ŠRŒ¡V-ÞsoFŦëvÐu;XtÇà††“PYFIŒ i|Pï/»oú¯ÓÖwѹ=%6Gz^&5õ÷–¢.ö,c\g4b~T'¼Ú°hIBéñô‚³äX'BêË1Èi6vÑÅ„{Áà†c}:·?–¡a- à1+œålˆÁ`Er€‡ «.aØ ¡ò NõëýÚiÿTä˜Òt«ìÔ?•áìÈ­#VÒØLùì¹µœ¢ÖõXy§¹{ŒáÊDç ÏPèŸä™[»áhamò|¨«ÓÂZè¹i> 9œr¹äT¡À<èz³4ÖØQ§s¡€KœíCšäy´¤6·ÌÆ–Üò0´LÒS.} žré£ðÓwãM7ÿ>ºÁ𖵇'§Çµ×^‹Ë/¿¼ùºÛn» ï}ï{7Уs_¶ Ç!=ÙdÝWéá!ëÉMÇ+.‚H®§D &I l7p–ÀÞ­Àâ8à×u¸ãŠ=|úžð¿ýO¸äôeÀɯ‘gã“Nõ¸hÀð)”²è¤1ý­‰¦H]"l-=¼„hcq]Kœ÷$3ý­ô˜2©Ò’†™P €) Þ›9Ña;¢¼¥T-ãcªúöëqÑí=Î §Š×éRÎ2Ýð0¼Ä^aìvFƒâ]:’äTMrW/6àr¼/ü-+þ£:|HÉïôâ«Üä7£°EhÒ¡¼9÷‘úFid|˜ù8ð‘½Îø-)ñ–aM¤Æì|¬ ðPîÇšËg‡¸X¬  ,4©«Ì[,œhïQ&´ßÅУp‹kË$V$¡'¶”@^®ì0¿[Ï(*âžksAµÊÊ€‡—!b£Þ‡éò×Nà…¹ÞjÇÇëëwxò}(è¨y}S â iÝ2ß-_Wâ[†õbÔõöâ½zÆGö¹øöW`{X¢…¯d–£(}XóØÝ —Ë.» ú§:ëÚapå•W®¹Gç‡lA­ÌkRc ƒX„‹&+SÃ\@¶ “»»°Å†°@Јì¯|/ΜđSGñ]7œÁÕŸø ,†;Ðíß=&.6šÂ™ÄUJ¤Ý·ã˳P”¸€ªÛ¦&²~R–F½ÏÅ€¤Ï‰+aŠ‚]°6©Q¦VŒz²^LPYgFa/^Ù»äÜòÄ·ÙÛ!ÞËýéY¦.Ü ôì çöáÜúá«ãï5Z8Kk’&uë Ï'>Ù[ºsFØQ˜Ál’ãAÖx‰Êiì é¿2vC\v )àƒŒàŽÎ0B*=À:#+ÎyZ|xN’±D˜:%àcåÜ–ÑE Ó\øeàJƒ6<ø7&ÛUÛ; d³LrÉ&;k{lÀ΋[ñ%hždßbNs¥xø¿{þÛ׆¢_µÓK­Xã»ãã(fž`ß_øÐ¤Â³œ õKÁºÆ°—ÐÌ59cTÜC2Žè¹\ÂÒÖ>Èk eç„;™çåEóÑbJþöMU䟡6žÙ9ά€ „õo\Þ#s¨<À§š0–Ìx¢ ÕuZó{he×’'¦9ísøoþŸGãè½Kt{_EðMÏ'nW;®§ua¹¢GÏëÂV¶òÍ*[Ðc+ó¥v!%Ê cy$‰M ÕÃ…Å÷{*9>€a¢N]@ó}ÄutØS¨}¶ñ9¶/ †ÂÂ#<°âöH¿¸ç¾\Ÿ°ÌŽëmY¥2TuMážä"n)áJœ¥ÑÃ-ÀC L§ý¨ÀàšWÎÌ­áLE¥ €Ìõ€%õX 6àáÅ>ÆŠƒD´Gwæ®þlšxˤo3Å–ƒ)¾¬]Ö·’,ÅÿHPÇᆈÝg,àÂ’t>"× €ŽÛ$ç TˆyLa¯©ýuZ¬>/Ídwd¤¸›FðÈãùùFAñÚ51Ð’Sò[WæKxøbØ!¯ÆHA¦æ²Ê¾þq _D‡HzÀ£ÍÁû×vø¾—î ˆ÷E€¢˜aXdŒ×ŒÉmŽ0ùø’ûgy]|·sý–×—˜È‘ÍÍî…ìGÛ×õèÜ.Æ+ÏÎ$óºëÑu»„õ1:Û€ûáŠ/ÃM§ŽbÑ÷p‹¸£Âbog")Óºêׇ–ï0£féó‘àuU8Ù†bd[[9ßd zl¥MæM2Ì%ëÁŠ^•FHr|²Œ@áÝ|,&UÎãâГcQ‘¨õÆd.f•×¶öÊ^ˆþ4xu1Y¡Â»4 òª6sàG͸)σÅÈ©½\(ý:àùLKˆ¾WE£¬Ìðʇ¥ÎÍã‘VDÁ õ!µ?þ¾„&Dd Àé›péçoáÝ(݃>YœÝ—ºãð𿥂š#žªo%Ÿ« aÒ[ø0¯ÁøðuyuÖ*f2Sú’„cÉŽ¯–¦Hð¨ Ii`yä;3óÞŒ¹Ë:€¼M˦Œ-3ˆÙ/É£ÑæÓmD9ðÁÃ`÷á=÷r· ð˜tˆ2súw`)ðá%óeûw@€G•ó$|Œå ðHAî¢J‰Å´ ýf,âq~—Ž6×õðN6 2º©Œs{Sâr?z`ØÃÿË-øWo¾Ýð•±Íå1„ÜsÝrRb%kcˆºäºÙ¾¡ßb—Aå=¬´3ÌV¶r–e zqnHéïÊ,gá+á§Zðù‹‡­%á/9¶‘ç<Åh.åÿŠR;rà”Q®ìË¡.I–Iõ ÂdK«Ÿ…𬕒•æîǺ¤´Î¡,Õël%&ÕW|É:$@*ÍÑæs»9îÈcy@ÜêÒù|]à t{~W¶àöÑùúØwßXFvbÍd'×;oZ!Š[ÙÊ7ƒlAC*<)_AV4”-*k}%}T"&œÇèGCfÕ×dŒ-Œ GL“Ë %Z() cÃ_'Ø,ì~Eê<;“Ò¹ÏÇ eH–5³£W´§µÙT_…4{1!-ð,‹J›g U°YZØM£0ƒˆ0àÁßWoôsFü;àíÑòñüøÝÍT¾¬1Óí¢[‡ëïH¶È- SéXò‰W ³Œ&§MÞã2(lj§-ÔOª7ç&¼@EŽõ‘޳ÈTQë%u[;Ç´&iMØ3é˜Õ¸Þƒ - ½k¼ñsdnhd)„E©»ð°X%‚òžµ—J–Z" ðáÛós½ï¹y:’âë;Ä1¹ [šÜ ØamE? ø¨Yã“m7|ŒåÁCôµ¬‚õG(«Œüv>H8žé¦`QVçuS9|+[9¿e zbI”û™€‡?^ |„c}PT« EH"¢œÇ—%Qúpm¶)E’ ½7ߦ쯚‹#~ôíÊ"ÀŽÒ=}5Œ–Ò-}#÷WôB5„•ëÊyÊw UÖ,#Ùá‚Ô3Ö,Y* ¬•„¹áŸ:ÆÿÓÜð»åU@JI §çfK Þ¶_{ûñ‡à²Oûxl©Œ’™+ëÑ2;¼xFÇ’å÷çÀ©mò¼rÛÀ†nÔ‚ á‚üЀñ\üð’22Ø–·š°R‘VÁ‚JC7éK8¥Y úgÃsWC6 è³²TÒ˜LYi'uVfx7ø`¿óýó.€ õ¼Ïõ½eÀŽÜ˜c1æ˜dP¡xwöœW¡Cä+ôÞ˜ž¨Ì×ÙV‹Ù[HD> ¬Ë‰ãó«Æü°žµg*ð!B4“ˆ<ðÑÅœ ¹Î=uJ¬¯îÔCt¶*àµö›£ç«€Z]ïØÊVÎUÙ‚[Y›4)’Ù—E~RVÂ]乤<øâP§°* ™{B£)AU,ƒÔƒYÅòÞ±O3î¥öþCZLoS¡Ž•â„sã@H'•®Ãxɉ€%* ©hJg=ÈÉéï ¤I q8%Þ·`-$Æír¾²¯Þ—ðªa×ß…ïÛ]>5²0j¤^bàO^9À!Ïø @‡oC†|6…~È6Ë Qšàcj7Ö¯ôþQצ*Z8–d´eêÎ8¾HRC\2Œ“5ÊÚ0*Æci[êb—ãÛÊwȘÚ×XT/h6¾|wd~ u@_èûÏîðÓö—’ßF‰ã*à@4×Z'7Áð Õ70Vè±0nªô•TPN„OÕ²:Ú¾Îö¨:¨Æoâh׌˜Ї^KFˆëö·ϯM!Ì%8Ìb?hˆ—Ù_ß•™k>+kÄ1){øèÜ€îwo9ȶ¶rþÉô8Òœ´4#³ãGWhO vÄE†üícƒ«YEDîØÂΕRìxøÅdDåYј¡žÍØ©¤W{´Îèål|´~nÍè)vÍTêm%ít°F±Æ.{NR!À—y†cptž½±Ì<Îð躠»ÿø\Äm˜Oª¶½¡òÞ%УvÚ7ˆëÛ û!—D5¾ƒ¤Qfmkn KV¥ÌÞŸRÜý+²=¨(€G-P?(œ€œÃÌ‹¥ƒ€‚ @‡?œ=$ÀÇÈ„êáº%|NjuÇq½ì¹d9®/-þZeb= ¹é\½e~lå\”-èqè„z³ìEl®¬'p˜V Û’ù0h&í&煮’PJc‚×À¤ ‰€ ‹LadžØ7ò [-êxê 6˨W¹ëÀ5“½"³ÌàC‰–†>9Vʵaõ]Ji ä=îTæ9–VæòÐE‚RK»LIa©˜ÛX[cB{ŽªçŒ@ü¼Ÿûò GÃxJŒi¥èº]8ìàX¦‰~`Rpmà#e{ä„€4b."©ÛüdCð1SJÀPP¤3À‡ÎÉH:«®¸fHeUiɇ šÐÕ"p;T`}øÓj|e’b†*)âC ¦ç²÷„ÿ¸õœ¸õ_‹zÓ¾kIÍì°ÆcºDÖÍÙQ³¦É‚H á9¼Ï²œd`)u4¼Ë¸ë— Й׮ŹÀú»€þÌdˆ ßI>tסЏ%™‡†)Ä…ƒ‰žÝ½1ä‘2}3`xXËëžcUž)^”GÿßÊVÎ%Ù‚‡Vâ"€¢7ÍšóØÍ×å h¨û’LÆËü‚\Y¢»ñLÈòÈÕµ ÿ{j‚‚š°xû²_°µ¤;à¡î¢‘Q°²Éè„\ìS›Jõx¦ÏH¹—Yì)“í¡”Ó./€#Ü ,pÅiü¦Óú}¾™Ü7Ä(+Òòžl1”;±ÁÞ¥øßœXVË øèá·'d[ÙÊþ»iËmk‡—>´ä÷À‡&N·á4ÉÛnM›ï‹½u-•ôxq׎"Ó„³=ä·£…¹T‡¸¬xÌo‹€GfÚÌQ‘æšáܘk•ô»¹@Ž}êÕ8ÑßÅëÈÙP2àÁê­]OŒº²ëBŽíVÐ’ÃYÀÃÿ=ÓQÖÑmÎ%»£Ô›9fäûaL¤z¯ÊÐíÒ—q~\(yå83Ic„ø9Ýš àa‡VÇ9<›G©[²y"6dSŒ}By4,($âÎÏãëÑ ëã ÛÚÊù'[ÐãˆNÝm\Ș‡Ý0ŠÆ²fˆWÊô5ÔÝ#Òº‡kœ ãè&@A»Åú‘H¸1{K™¨Tf2! r×K9‹’h°2¤¥$Åp“L mHcðÌ IDATFÁÔÀ¾#LêL/”Ñvy`¢x‡Z¥0ÆŠŠ­TÞ¬[Èo¨¡ß-K½CE#jTæBò6,GOÖb‰[ ü‰ø:¹¬Qj€“6v­/*­ñY¤J¦ù7eÇP—Ýñz·Gr~ÈÎNÏÒõSL¸˜RJ”UYMDž“’3a¬œ3C$Åy•y%,@$Cj²SežKæ½5„¹©!.2œ†ÄàŽlqL.¢[Ã0œÜ)8?~ˆ÷FJ“1.=3ŠÈÄbV¥wWâ•iaDe…ÓïA<+#¼‚î¬BÛR·^˘³êXX ¢/Êõ¡H·„ ”t»9Oº%>ÿØ;ñ=Ÿ[F ™\ÛMeÇérÜò0wIò»´^¾:*å´„ÁKxàƒy<[“[2j4Ÿ³VÓx;ù0-WŠ |ø*+ò¨Ô*@ ÝIÊ7–äÂsßcßU¼—û®Aäúʘ§JŸ×“г¥ƒ:ÛC®e9ÝÉ!NG¿Å̘,Ú7Ö¤ p†«øÜE¼â†7CãçDZµÀGŽ<·=éÇñÀþJ¼7ç=< Ï„(j"V˜÷[3îÅs^S¾½°ðšLôÒ4 ew„öóFTPŠèö}ì<; ðà÷’3Ø|ß|÷Y*ßMF¼`pð#7GJÀCi¿£ çô ÷á{ÞùŸ2ýÚŽ\‚nÿ+‚ÇÌ×/Æ„f¼ÓªÈ·˜²²L› aÀ“%@(ßÕ ƒÕQ>r9¬0”"ðAú¤¤¦²X(­¹ùó“ÁK9€‡ |ä -v…#v«5»¡å Ìg?$c±ÄtJÀ¤q97€:ÌË%Ã#œñAMßž*ô[§KØ.áo«\l;žKÀ?.q¶“šFð£U¤Óƒ¾“Žé×U–ÌIP)x-ØÁ-z*»· ÀCw®ø9¡Ï!T8¹yCÇ;ËÀÇV¶R’-èq¨¤ ,T.Ú‚‘?&ÝÒ+„¯ À™ý/`Ñ߉~øœÛŸú4àŒW &ª¢d{`ªwªß¡ŸÚêÃD^¥¬ÖzHÙ*D1~Ã)Ji§Ê [TJ’øJ@¢Á]fLTyäEmׄ¤¯(*}†{àNÝ„¡ÿÆÈÈP„†¸PàÃ)Š…Ã>ºn‰ÁÆþ™Ä?úbè¼ÛGªÔ´år‘¾œ0+äùh„$à†(C“«?ê=ý@àa3Qâ}Ò¯”Ý‘i7mhÏvÂ÷)•¶%1*ü9Ý3+ÃN’f çÇJ§¶ÜfU9r ¾ø¸ïÄw|²ú;ÅIÚFÍxOs(‰q–.ÕÄD‘¯ßeD ‘`h2·Ís=áëú€3· {æóƒYIKsÒH /±+|™¤o3•òeU±:6 ð!·D/îšWÅÐkGôèø{iÆ›òlE%9U3RÖ§.“q?å„sn87¨mp}²î6´6YrT²u,?(¸AC_Ö¢¾Ÿ\áì2yŒç—æDË„ŸŽô‹9ÙjÊi7²®J²­­œ²=ÎCùà?ˆ{î¹/xÁ ªÊ;»·j ¥¶`¨àG±¢®[Œ „€Å€3gnC×} ÜpJÍçÁCs"ø2î†0c\‡z8iPš^cܰuÍ¢P‘ÕDSô’c¢JŒAÝ:(º'_BœnÄXI6u*9ÑC¶I Êä>úý;q²ÏxÞícp§„çDx1;±k M‡}'Ñ‹1˜qœº<ÒÍ¡ô½+^;ÝP&i(Š®`@tDù¢RV~b}Y°Cæù˜ ™ÏÆ< ý0 ¢Ôk¯+[5[ßr•U6²>jDõÊk°VØaÍÓñý¯àÊ¿¿连)«4]Ûò§^{-¥?Ï m±ÚUÂ]4àcÖZ&½ó™Δ1Ó:ð¨±ÅÞÒw¯(“Ò@7·¥m kYuª[6ôdFÛçØÑÌö²ÀÇX§æLˆ uuî0–ô°JÑ_ñN³¬ŸëiÈ?34¶K\^RàvÑE7åõ†“©î(¿y¸ŠE¢‰ÁZ¡á+Úï*è)]ÊmížÊò€\Ç5Í\#`C×I @)—ªl­låÜ’-ÿhÃróÍ7ãÅ/~1÷¸ÇáéO:ÿøÇãºë®›U×'?ùI\{íµxö³ŸO~ò“WÏ\äÝ`ÿ]0üßC¢Pøc윧ï û£cL@9œÂ0Ü;zÕ=’i›.XÎõ¡Þ±Î½éÿS°2µò{LesÿÜ~(kþ“Œ>ky¾[f"Ù½n¦”vn©Úé$UÏËráoix/Ôþ¹på/žèø‰9Z ÃIôý]èû»¦Üúxg¬1ž´²Ãp Cr—§ØX¥×»aŸŒÇ!ü_Züù3´Þq† b”óï•þ‹.ôXBìZVNô]|³Œ_'PI­s°PÆþÌo¬ë–¶rÌŒ£¥h£Çß|ßc#—ë×ù9j¸ ;w~Ù¯zâßÓ²ü¯E”ëL¬ø¡§ªê©TS]»Cé|EÏÅùIb^¬<ºŽäã(¼/_6\Cú»ËÃXÇbß3ÏDV5­›Mmk¿E³¥ ð ÿÖ$†Îäû¤éNãI?ÿ aÍ[\œÃÖ°j!ÌZϸ ¿“y¯É~]åzŒ±á/.Ä¢»’]§FI·•m§}@R.ôè—úïž? ¢›ŽÇöÊz§öÝÊwK×ãp_™5:;,êçÒNÑa·²•s@¶L ʧ?ýi<ó™ÏÄ ^ðÜpà ØÙÙÁ‡?üa\{íµøøÇ?Žßøß¨ªçßø®»î:Ü~ûíøØÇ>6£'šw¨ñ„„…lN[Ì\&éâÎo3 À'Rò§, Ÿ/Ó‘ð Ò»èŸèÇ:3 „ìë:.ÍhFO4…xŸZ?[> F=Ýñœµ=cU<;õÞ Û÷T1Õ=`1„ƒxÀ¬çáÛ +¯c¾üäqDy }cÒSx=ûÈ{~loÊÁÄ"¨®Ó“£¤¤øè9<òåã3ìu†ÛÊu l F¨Rž3ÓPã;5Úø¯Þû åíocD `â ‹}®Fzö-û4`¡þkJgZø2c}tü§åS–/ayÄ´^ b«‡è—É)×C†X§ç¦ãd=c L—gîÌIn«ÕAsÁ˜;ØÑy†®Ý3Àµµ°‘·²• ÊôØÜu×]xÁ ^€#GŽà-oy vvFäùšk®Á«^õ*üÒ/ý®¹æüäOþd±®‹/¾?ÿó?øÒ—¾„믿~s_aˆàG‰ y…$ˆ®‡ë¢"9VÄÃÆ6 Ô,6ÎM†³WJ'å<ö×@Ô{[#MϤíçÃZÂå>ŸD ÐîAz•d]|¡« `ו€Õ(0ˆÒ0 e<Ê$pÚ;4e,ÕJ Ý4<'×Ï‹VENEi3Zf–qÖx¤×Q c™(n1±› (²~Yß•zÜäd8ORVþ¦Ï w]Í3RÊ$F9ýþrï¾ nZâØ½ôITúsWÞI"¶/Bg q¯c3ßFøHB^P‹T™Ïng«UŸÒ® /a*4|E¿AIœC­§Ç¦ =Gú¬¬ºL¶CáºÊŬ‰*®V`ÐÀ;Þ# pJå¦ýMÂ\ ýwéxÙ„p)‡Z?ª¤T¾‚`„…Ð"€I¸‹¹âtûØ@¦ê³.±«ú0îé÷jSz[Ǥ)–Оc{„ÍA™Y´]ö\,`C{~eº¸á2¤B oÊýS……âÑr¤¬|ƒ:Qy)ç á"ˆãV†Ø©ÔzÑé3Mç&³/þúÄCYbXß"²z8YÚäånÁzß릟Kàä &i_¨•ª< êÚS^j#sE ŸÔ±W% ³4ÒR+ª¾·B\‡þ. 鬑Nû •1CnDèK®ï9©;ºt=+†N«/é9µºM/~Oÿ[&ÛÕÛ‰4‘­lå€eËôؼímo|ï÷~orî!yö°‡á _øÞ÷¾÷áyÏ{Þôi€‡Y7e`X(¾BM¤Œ@x´è^æ¥6àEñá.4¼ ÔWý Ä‚m\WfµÐº8êß%^€‰~èN ðHúÒFý.)f­À†šï4ÐRz<§Á·²&ªv ^7½“k\¸]Ÿ†¸øcrZ36 6Â%C†V0ÙŠñ×»/Ÿuî^#«ˆîªR³ÕjÚîxmÌêïû¬ƒV_$PÂÂZª _Ž2AI¼cÿ•öäüi0qfM,¤…oUK·Ò»a°)¦ûÒvF1›M<Ô!.J‚Àt®i{ÉJd|èçVzí'Æ cօ䔯; ×f@(ñ,V¹oõ»¯\W«r€äò3”¶æõZ9^d1’eyÿÍë±Tj6QZΘV]fÍ…™Z;”„.=3ªŒÄùF¯Í„œ©ï¬†u’eëxÝV껡QÞ~åXca¼kƒë€L8nêÉ3ó€Ð€d½ª?çÊw¾•­PÙŽÊ ÈwÜ¿ÿû¿<úÑVË|÷w7à=ïyÏô©ð¨ð–ånÉ(¹–Èkz­Å6ñžö°½hÈñ‘ckð¿u–JÞSd3]8ê/=Ø1ùê^Hd•„´hÏ.QBÅsÎäH)>Oå\þâ:Oƒ ¯ "•,‹]P!*›GŽ-Ié5A…‚W|FßâïèaÉ?»ølT¶ëð²üÜÛø#Ü#ÌÛ×5.ÕÞ< D£ã¥m|ÏmwúW+”ýa0ªÆ}Þòÿñ|’¤“m“w%¼±œ²ºÝñ½®}H˜-ò¸öd(€‘(7Ž aÔ[TÍ·­2l"âh¤®`V±=|ô¿‹€cuÄß&+#ó=³bÄ0VëÒXt„ ©ýK/Ȭ ʹ"Órâ7¾ÿ.x±WqÉy\[](#„ngúY9þSe³ä¾Õ€I×6-\U'r:Hr^©?yÎVyëïÑB5:dÊɶ“c&‡2ò¹õ†Ôøí%¬Ëg\{Ðÿ¶²C¶L È7Þ~_uÕUj™+¯¼ðÙÏ~vãý [„ÍÿÛÒ®†ä36Æ2ô/Ä•æâõô˜|ËÀšzæÛ?qûj©˜Oí8#q +Û(Ý.ЖÇã±a/#!ü®'ÀRð$a®H‘ÇÀªu­Òãa5=TƒÉëÅ„lPŠ}6LŠ…Ô¤‰ã³ò÷þx×3 .é§*äž“ßÏÂØÉ9:Òßêï øÁ.ËÍk4X]ßœ£×Ïi`WLår 5©î¦½Ì´ ãÚ÷!™nÜ)d‘$Îl>j%yàèmMºA…E q!`‰&P¾¥ZIRYdÊm˜!-ÆîuÙ­€t>„„yhæÃN\¨ÏŒ—3Ü!ïýô°I¶rîÉôØ€œt!ý’zš!%ÛÃKM-–ݪÂÖAŸ¼:Õ]èo™¼žøïBÈH”gýia ºÒ°/‘Ù‘'"üE>bÊÓûˆÀ‡Ê.%zòxÁŒÏõ™z2®yàÂßwïÝ‹7ÿýo÷´•­œYC\Á7Ÿ¼õ­oE×uÍÿ^óš×®¸âŠP×½÷Þ«¶á_~ù忟£ÝGG¿#M±¥J’¬*z¬o®ò¤&M„2GÊLIŽØo—&¡JŽÍLDÛֻʄ®åíóóŸxÈöeŸL¦Bá^K ‡bhe§—c·pÌ‹ x¸tW0>þåZe.ƒ¢FŠ ¨å9cýXËC›4d¿ÇDæ\OHeHA½5¬Eì]Ì]§aÏÓ±Þ¨Ÿ™‰Îµca,“gt²ùºË.» ð¨G= ]×Á9‡Ûn» —^ziRö¶Ûn<æ1Y­³3¥ÆëOt5~QiãIêlDfÞÏwrD×ó±Ç´ô¢Ý’J¹*«£&Ì!yFn÷ÀðОI¹9Àæµ e|ªß<&” õò–¼šW)§À¹yb)ý|VbÜÖÓ:DñÔľëßhñÛ­î·0<Ñà¹ö—eÙ”5!Ú“ç YM6Dz ÆG…¤4_Éìð2µá†‰‰%Zë\IµÏz ÃϷ|Ž¢õ=–ã‰J…WÜÑ{¯e}Œ×øíÀ!“˜†Î‰^eƒñ!E²;èßì\ÍX½ßþyT³=ü9 n¨øæjZö¤Y·Hƨ#ë‚4mÀúgk’©ü>k¾™Òºc‹—2à‘»˜³>Ú@ãÙúgäÆÞÆ÷<`Ü•.^›ìÖ&˜|tkéñX%Sqœ”•Öú‘k'ÔíYs5zÄ:˜ ¹•m«[ŸÓq(ôC+ÿí‡&I²Tªs9-¤Åg ›M~7 xáõrÎ [ÖbÔ=EXí8¿ï†±áp9¿€9/³û–Œ˜v€l%9ȶ¶rÞÉôPä…/|!^øÂξþÒK/ÅcûX|æ3ŸÁç>÷9Øð;¼<ëYÏšÝN‹äÐòfZ<.îZ`‡ÌnŸvrHÚJËÔOrr1ˆ!Ô(«$²¤ v)ÌF½>¡úIV(¶±¹>iN(Z+,Z\ŒÓÌó c½tÿšQTßZEän/mìÝÀÌ®’ñ´ 0Ç pØ›-…[Èõk-¡¹%hº¾W±v|ÈF‹.f#¨WÓ×ÄÑ«óS6¿«_ѧ¦~Ú·•{¶ô™FpÌÔ§„¾g•ãϧÇÇ&¶Ø.B2lìbÀ‰QO¥¡R~.ÞÊVÎ#Ù‚’½èEøÌg>ƒ}èCø±û1vî+_ù nºé&\zé¥xÎsž³ñ¾Œ“¯ýª‹1’-"“ª)`GU8‹@ÞÙq­xfÁ`±Ë£‚õÂÛª;r Òü"JUYj¯ðpÈþ%àˆ@êi_i¿äBM«P¼)TJJOñ]ѾÎ]T5o1;_Ë8Q¾‡ê¬î+|GrüSÖÁt.·„Pû/¨ªµÞY­*ø¨»n\ºcX,ÇP¿~ÿ pîTgi\ud&Øßb4æ¹òÊc¡»nOþ‹Kðø?!Þ¨‰â™¾­(KåXÍÍV€jØ‹(S#Z’dŠìœüFdñ™¶3<)Í#Àî˦X÷{þ<$b<‡ìŽ0¬ ÒEþƒÐ~îróýä¿ùY×I#ßp0PÀaûh ðHÁú÷Á妰E W±CXìû¡’¿/ÝICÛìºiûæ™ ‚µƒ‡Lª·]LØ";^inŒU4­ÂÙŠ€‡?Þ‰\€½&uKûy—Â?³ zуnØÜ9”¶›:?|yöÍfr~ÖxÇEówhéPA§ð2ί®›’Ž.v±èŽ¡[^Šaÿ‹Àônä«g „üÕ­ïÄGn}'† †Ýmåü—àoNyÙË^†=èA¸þúëÙn.ð›¿ù›†¯|å+CÒÓ¾ïñ?ñ¸æškpë­·šõÞwß}€aXǶJ3JÿùSÍ0î½ß Îì ÿrm„õ(Ç…’áÞÄâG(ôZî‹l®‹’ÔÄ{j ÏY_z†Bëg”P \ªx.ÝXì„£ç nªÉ*I­4sk|­Ô+çTP¤Â;Fôš4þžöi¼~ãp]·þÇvÇÅ<¨??Cºn‰?û™‡¡¿ò¿KÞû^Yžë ³¸ à <î?ü)Ü@vÑšCµîYó|—ê‘Ï59¶,ÿc×ð]·KÆê®rLkÇè{çsÂ,#ˆÆŽ•ó{¨´Év?AúÄk¬ú$3@åúå¿#Ñ'5Xÿh9ùO]3KsÊb')Ë;–›ÏÚõ„•’}nÊ^ŸŸc@ëîkº¬Tö»õ}¬A|Þ ·þÅPú™½ÄÇ5[ñ¬ópÐzUoR2ޏ! ë‘fí†ÓÀp*éÕWs\Ô<'+¿p]ŽñCë¡Zò~ÚÃ\Š;…÷¢==!iÚg)¤÷ëÙ‚%#=o²NÇó¨Ô3¼¶‡þÌ-xοýâ¨ÈO}“ ¦•iÒ›`TöÄí„pD¶GŽu$ži*ô¸¨'avXì¤FðD°‚ˆ1A”yÙb‡tñÚ4·ý­ÏIj˜Kh$e4Xá1Úß9Q×1-ŸGcØfmvÊ|wéœ)ðäïÈáä€&,{ÞÀ{‹˜[<Ï4Øæ{Ž5H¹Æ^iš«"ÛCêÐIØXÙjT ÏXÕ ®1G×ð ÙŽ@ÞH—÷ÖTaáÝ'¡,ÊöçÚ³:›÷Îb‰ä˜+<{¦wÆNÁ¹½(ë÷ðÔßýãñïîYÿ)Cn9û;ßÊVΖlA ʵ×^‹}ìcxýë_§>õ©¸à‚ pâÄ ¼îu¯Ã+^ñ ,—qÂzô£'=éI¸õÖ[ñÜç>—ÕÓ÷=žò”§àóŸÿj KÔj©?ô÷âÒ÷½ƒ;…¡?Y‘]ž÷#( øðY“öËÀÇZccWœ²Ì‹ê:2ɼ¤Â_;èï®[b±¸;G‚»ý?âø§n8 ×qÚiÓ‚Y¡¤”ÞQÍ3fïBÍQc{dó[&¿«¾‹9¢Èìð[rÀƒzó£·¾`[íðCJõûÖ<ñP¾•Â\%0¼3rLù{öçH ,2@ “Ù‘7ä‹õ&ç$Øåÿ^*}õÈþQ/«È"LÌ ä°ŽÕ0Ç—2ß”ØÉv”öÛ'—ÓCS9°£Äâ°$Ì+b^ ÿÜ{ðÝhtàƒ8cÄ4,W®Øú.ôûZ/ØáÛ žwÙnåš8«mc]ÖþöÇXûŠ#å<ñÌ×8¶rcÍÊý’>ZÁÍi2žX RÇfäã£e'b› 8ìΟÛg×ȶTGrzr~Œñ­œÙ‚[Å2ÀKE àáËš!5Dù'qˆn85þO&ä²D…E´þÅÏÜ煉Õ›èƒ |BÉRž{Æ pÃ>Üp ]wøò?¡?rΜ¹vƘQò®‹ÙÞÙIÊ(^´M²r,‘Fˆö]$×ÈïbåNÄœ £P&Éèv§oswú=õÙíM}öÊâ²ø°ú­|g´›3B—é}δv¸‰ý«“M5F° xÆcý}åÃMÖ´ÌNkÞw%‹§e>–ßá k×8oÔ€Hx>‡[ËJ*8X´|?ÕëSA—*…q²q’ég ˆc'gíìëçÙÈÎ|[ÙÊe z)yê…´zS›ú%ýcT:×'€G}¼î¨°Pàcܽ% saýZ×ý–D,j³@¯HF±±ógdþЏöa8‰½½¿E÷Õ›Ñ÷߈TõÜk¹þÔôk ¢)4U×Uô©3 ÔU-7;ÞíN‰KwÇä•Ý.ÐG‡î…î›î+“Q™«` °¶+Y ÅÓç- TDí¹–Âäum\ðmJà£T«SaYÿk`¯úùùÖ׋ôoiðXa.žùaåû¨>2b'=¶RTÆD&ψ<Ÿ~ó«×þ>†tà#mƒ¯^GºåøZXëYÌ©?rsy#ËÔµ¬áumV³ ×ѧ5²6šÙ ñõ >4ðÍê×LFEk8Veø¨Õ¹•b¦#«€ØÆiNªUeÆØÚ[9d z&)Åx~¨ @aÂ#r %—™·%¢_ëY‘±t±ca.dÁi£ÒÆV ÏMŽU 7ìM±YÇBe)sn8_û|Ž„ Ññ˜a¬Ò&™çåû´vÅÔ`sð].H{Vkû2…¯ŒmDã7†³LÿºãÀâÜò8º½= Û‹ã„xîÙû`i²Û¼k*Ù™÷Ï[*ýÈ=#M!4ÂFx8Œš˜Ê¥¢èc&1&4 Mºèl+TÄ÷×’›«èH È[á—LlfF¬/>ýSIæt©¿Ïà,M -Ä»K̾•­¬I¶ Ç!KsrU³NSðÃJ°”HAÑ1èt–ÂP¿KK…peÊúÀ¤PÐ=áe3—óã‘oÞjÞæ|Û͹Pr ÑªlÄ÷´ªçEï“ñ˜¡·Ç ZŸmü°è¨„!CžûÐ߉'ü‡ç½¥èqÛÃ/Âß=ñSxÄßü׸ÿ-·¨í'À8H•WØxØ‚O¤:^{|òÔäV¤bï„zÓõãÚ.7lÛZUZ•ì e‘Î,³ˆ`‡=¼Ló{w÷îÝ»{­§ŸµVè÷~˜S´?yyñÀ`¿0×G |ÔÖÀÐ(û§-ðc-Ãcø8à1:Gi€ÇZ€÷MËh]å2x( oå{R„õQÀáÀ¼Ï®ÈbÚ╲;ÂÜ](»ƒ¹ñt¥õ.­‰«כ˒g˜n~”g–A+.µ/>ˆR4€ Ê¢˜&–°=ßRb,àÑRlºôÒð€ÎþÀ=etç|'c/º”Rº¯ÂÓÄ:yW¹ƒ@•ÓÄ@*vÖ+†µá×»ghäX5þç®×:·Àà/úµ_Âß|÷àþM.¿paðõJ(Ý•[;þͦù^¸k/yê×aíR‘ÃЂ;,ð›|¨`ƒ‡¯f.ÁŽjU1†‡30±{°ÎL0Ι#½‹Ò}`øˆc¨Á>·Îpæ|5ð‘úƒì•>¾û_‹¾+¯•Ù2f9à!#çü&Ìó}Ø. Þó"ý˜ËÖŒô#7L#ã ’¶б èÛµýºðœgi´òßwè7qMk•ÃßO üã €~P¹|ÃVÔq8“Ž0XWš~„µãºx$àCöœÔx‚ ^þ¢o´å(O§A« fÒ >IÓkVKžlÕ4±4vF<0ÀãÀ ¹/ê¦"‚ FÊ…‚,r#ée×´«uìàÒÚ%îIcïBF]œ.@Æfä¨Ï–+!ZŒˆCÈ…”ˆÃE1 ®/qœÃÝP”©hpžEZ}½À¹·îþTÜöÔÙà5´®†ò¸Ó\ÕaSŒJt'"@S÷üJ–ûœ\WRìö™üÇ4åcø0&³øœµ˜l˜#ëñû\„kuV›ÀŽèæÿïþUøÚO`Ÿøy8·qgðQ}îC °’yÇwdf‚ÆÚ’Þ 3JàQèŽó㛼žn?ë¿þà¿ ×Zÿ_<„÷¤™-ÇWbr†éá]ZrñFîj€l„Õ¨ènQ0˜ô~Ó@Ýâ~ŒÅů`o©î.\h/ 6OŸw+ 3t£­W®d I ÇŠqÞ×$À#m2£<3åz\1f'È”QÚmWY•oa |hì‰vÇ!EŠÑ!½vïÃú¸Ô@W½†µ#-OQ*©øqç»Â ûxÐh–véèûÓ<ô€¥}|P™’ÊVúÃïêðß"ˆ‘?×sÂÊ\˜!~ƒ @Uí“ççÎû œ=ôÿÀº«¯\8s‰ªúm”屿B–b0Œ”ÕÜ]ž§|PÀƒ»®h@G;\˜ØŒs阱¶°8©YÁð ,|/`€G6úE¶qs1˜ñ܇ïÅr²ÅdnÏó«›±fÞç쎗—üˆï´t¦dTh倫餖U0]M7à÷¾}ìz"ÝuIcVméã‹>žºz«ù:Âæð6C°KëÒ§föTuŒŸZ´“÷a™Í.µ5ƒ¬9W÷6PÔ5·0[ÀGjFéÎÜꯣ¾ÒrîŽ ~Cl®Ÿ=gbÜ¢‘ÄC,•€¿•5™iéª-ÖFëQŽò4Êô¸2R#ãý—¸õBßS F€èê’/ZÒ9mÚïŽFJÃÅ¢?Œì@—õq©Âïk×Å¥Ãn DÚ3Ú›¯£¥ iíØGÀ@3~øBßiËNc˜ïžï[ž.ù=¸áSØÆ¬Ò³¡ ŠôÛRüîØA¯oìá•©uÀ%<¶_QTè§ú¸ô2àa§“äºÂnšàŒ!ß ŒuE<ceë?Ǻܒç½x>mkÁð˜Óg?Nrßäï {å?{Kîƒ0ÌpfI;£rjíHo°=о­<ߦ‰•a«5G5R#b]*×piE¯ç¿lüEÃQ×W»D `²`…µ Ô lã{Rž%¾ù;{»Î5æÎ6ØA+€G«mè’ Ð w-×[j†‘ç#Þ@ÆA·àÑ\Ç;.½|c¤šWÈ{§êlüÈí¿<EÏ…I<äñµà¹Æå’õñ£<«äz\ áWün+dšgÕX#CÀGq~fw´¨¥ýÔ ÒEÊÈv‰sÖ‹ø(vC8냺é\&¢-Ý×.íZ~Š€¥ë)ð1PÒš® „éE’²¡ï°(€Ç@{+%Bí³±Ýµ±÷4‚¥±žÇúœÊtgŸsëÃá \p™ÙçE#Æí Ÿ+ëÇJÕw+™ Û£ru€ ÉÝÖmHÜŽt x83‰ŒÌÀMª Ÿu&2CŸ º»˜ÓÃÞ½%¼çΰìÔ‡»·ðØ0œ-G~3X¼æ\ Ô €|•íE1ȵ8;“¹ ·^þ÷píw¾Ö>å]l ½Vü¬Žã•ÌÉÍþþ”ùb„ÅÖs%l•½‚U3.»—ã cx#p÷Ø/£r@ÀC”&l•V’QÌœVc¦`*Œ‚’hìÊìàãKs‰©â~ú;§ôýè¸]DÖ14GDZ˹¢[{—öb{hQx nå(OƒA+ Þ½¥œ€¨¿p?8ð!Ö1lÀG-mçU×€­½½ûâsà€èîÙ*I¿L𣷋'µc; o¿ïe×öÑ:TP*(û°;z ‰ø4vç4—–QÀc]*7Þ×qQíÌQ †+e%ûÀƒwÄ ,Æv¸vzþ¯ÿúIüõïo;FE=ÎÖ×·³kËAvÏ–’A8¦ÍŒííÏÅÃ/¹/|ïïÖ`‡¢”k€‡ç‚ÑÁÿÛ©€*“u0ÖaÚ.RÚâ+³èî•» ezHàƒœrרbÇ8°~*`<žŸ×%Ï Ì²tÀŽT£^àn >b}ÔÝEÌHSìÜîà·y²B mÒ)óç n<-°£%kûŽŽ]awˆôdÅ ¾Ì…~„ €7e÷[p °|ßð?úX}ã²Fò¹—îê`o–GñŒ£±ÎûuÀ0•Tü†ßOn<‚Oûýsð09VmÍÆbç“äÂbOf‘ÑaÓÃñ¢ln‘› ,æw¥ •ª‘O:7ª.<Z.ƒ½,Z{½ŸhªÝçˆðà¿ï |´2ýì²öõãÁÄÚÛèëà±FŒ9M©gkw+~è~YkTÚüÜ]¢Œ½c@Éö<ž–u[;>´ñ’]˜ôú¨Á6­ÎuËsÅr™uåY'GÐãŠHµ€Äݱ„”3ภ#]<ÖÆ‘DÍ@³v'J4’Yœ|¤:ínÄy&Iåó½kðÑ–ÿïš…iŸ>ž UP$Æ ˆßz†=KØõ‡d`Ð{“Êܳ»€Çån.d̸ÜÓæ‰ô[<ÜtZœ_*ØödJlÇnŠÀ»] LÀ0‘bMŠbÜ”ñÂXVüLÛˆ¹­ÞëÌø¢‘Fǽփó§ÙH2ç!ËKÀP8ðÚEY)=®dü¹Ö>Qö &=`µ$lì¶\>zAL}; à¡Í‹´¿Y\êT%Àö¨á^ezéߘŸ•ÚöâÝO˜¬\÷z ôÞ.-{€L½4¹øËëžwq õM€³K•‰D:´ï򾃿Û¨«bËìxp`;²>xÆ´QIskìì xìJzŽRžÔö}¦å()GÐã HM‹g»cá¸7êYœ‹”žqqÅ1žfdw‘ä2v] šîHŒF¥TƒoîÒ ø >èfÖ•ÕTPƒr±Êòe媢CK²&nÊ!ÇÆ`¬Y€Û4R¦p ‚ëâu Pˆ¯ažŸex‰W IDATù(œÛ ·XÜBv¡‚ÑÊ€3ù´¡_ú/nÖ—mÊî }Cªaô¥yåg€9ƒs7`ÜyjÙ*|ãë`ġʱବ”BFšÅMödÂr2`ÇVòvXáÕ™<[óXN æ-Hü °Ö3J씽ÂHCêÚ’ØQš;ё١³ƒÛÂù1ö¯¿å/ã«êËÿJ.oøVÍGÕµÊ9 üphaŠ¡¯±<€G®¼ÞU=˜Û¡XßTöqª³Ñǃk÷¸—/=àЙ4{± ³}8;Üy<³ÃœÁ˜ÓëãÆÎm‹bÌí˜æçÓ=°›f& PSº+´éFÈ´!3Äz” ¦<Øé0˃>{v­õîmšs™ 'ÿ€Žqw:ÿ0c‰¶gß÷Rši3w"_g$ס1BQLGß—ÖûMÖéð´ ¿U,E¤uE2ôs{Ëó5ã¿šß ›»œ`qóÆ»Ó pîk[=Öºà1p£ócGàc_·"]£®-ÊúCÆQÁ˜ªNl Eï<˜´n<±gÐy”«%GÐã H»œ¼SP¹øˆq>IIÑ‘>à±^éÉíì°=âyñР2¸v2÷’ƒeÑ…6-²Âî—¸€ ~¢jý£ãž‚(¹?¬ôãVÛ4@¦H“ÝØñŠò.‰9=[çnà¯þÀ;aÝuB¥[,Cq—hHÊ[²=êrÎálÞ…¡n,¥R lïûJœ>þ;X6ïÛ=—v|Ã.¹1§p·½ï}õóñŠ·|4Í)íÛiº¿ñú¯Â_ü—ïòù}DãºL]ÊßÝg4$³ú¹ xtž•[ü­ @GñŒW¼ãŽœ)ÀÙóð90"ìu|þ¿x+÷d½»)*ÝýcËCsú¾IÛ‡Äû±l µ.WîòÌ,ð°'¦pg‰ìŽåÄasꟅ\(‰åᬵÀ <ða&ƒ)”m>p2aÚZXÌ0Öp&ÌãaZJ<Íd•âz ÍC:8B×k¯«çTøn7(ÐQ~ÌûçUwt $†ÇÞÍXÕç Hw­êH"±=ÎÓiyg®8«¸ÏJ:EO ‚7½zÿwYË2 3|PW#aw¾ä•ÿ^±‹¤wjÏõµü ž?g¼[ <3àjðcøðíè³.&ØæÄ>׬´BÌTô™x¨Œ3tyô~bŸ ƒd_årNÊvn¹Ô@Ä—ôø(Ï*9‚WBäl³z2bø÷³² XWåç‚ÝÁ?GWXÁÝÌŸ(¨p¶Gj”¼—ø(E<â3Äû|gŠº¤„í½ƒ?jŒ¯Pd€Ün}Ñ?v>FeÀây¤c­â×½ûhìžÊ>ñ_¼Pw.È‹?gŸÂtë‘uFC*k)‹6ë"ÕGã²ß†,ñ³%€9½RéŽv¿žæÎ“[à°ANŸëÛŸÇTǘmEÚ®˜,ýsÜòx(«u.5"$–Çàx`Gª{ÊÌ xxÐÃˉ·F¿àí/ÇætILžáa &g0ÁÀM6L+Ü0ü€EY¢X̘˜àÚâé!ÑèˆÍù;qSÊþ€ø±Þð‹e’ú¼Ìˆ.X|üs'Ìt'è¸tnƒíöá–ó¢HÑ­ee[[ï}N«IÆ®²Ë^T ¬;ÌöiMOƒ!faÜìÁLuƒ)ƒ@ÁyŽ2C´#J©Û¼Ö½AsÁÙ%†Ùj׊ùª56Ö¦L—iö‚|K.@òï\JDêÏ–>¡@2ø‡€e­]Íúˆ í;3£.â\P³=Òç$mÀƒ®i&ÌOE¬* }9x–©QÝ¢>BD=ñ~åY"GÐã ˆ‹~ÃtÂa{DåàG¹Òcà!•ϤåïèHJMyކ¦û…I~ L±­Â¢íˆ¢Ÿ¥4¤+ðCZ„9ð±ìa(ìVߎ ßLøÆ99ÆÙþ;HÐÅß¹sÜ|â§«~í*£Õ³^Jà£!:x (µ ù 3Ýåï æ’ªÏë\ë~Eú­(Kèäv“)·]©Ú)íŒ÷îE.Ø»-•Á~~£^ÓÃÇò(]Z(౜x6Çrâÿ¶³ÅrbáŒ+@eæeJà0ÁXƒÉ!0<<ŽaáA .6Mm±&e”1KdzÆG¼·èâ!–ç"ëAYþº©›k¤»Ó]Ÿ` 8Ììù^Ø»_ {r†yã3M·Ád?áÝÔö;Ԙ§+Ö§{Esw6%þÞy¥ôW2ÊàB¿ù:òs,]cü}JFš*DÿàÀ‡—AEJ=gøØ¹-ð¦ƒÄâ¥`^ý¯Àc,.›Ptk~K÷àûtà¨Ç¯èºƒÞSkû¼ßÊ1“>W šüP«4gÈ.wKù^9õ²ÐF=woM­âÐÑ œÆT–K׆‹“·?ô³øå‡ÿ9ì³$SÔQž9‚W@Þøáw`2_tÏ‹ñE÷¾´íæÔ 0~ðB$aaºL¤“6“Aâšú¥ºj— Æ J_Þ‘ñ}¤§$¿|í‘ÊYšÉeGi-ÆÚoù™Ñgw+Éó!Š|Yîø"›ÙSºV PÆ¾Ž xÈ×yæ‚g[Í6åo+vds[ýNÛ/±Rb[ _]<ƒóI¼çu̱ÑÀ´Žê» xLàà‡ ;…ù{ ĵ…/¥.-Ñ}…Ëìÿ\=¶' 6g[ÜõÄ혷“5œÁrb1…”´|0ÔÙæ z7À¶È4Á,ÜÝ)ÎÌ ü(ø‘ž+Ã#ºNvD2pZ§¯<D–Gb{D£ÞœáñüÎþøáÅîÛ‡áÜFYRƒ„yt€(¹‹iÀAo -²H0àƒ²HrdùÝðkß5£e îjðA‚¦O‹[X÷d¦Áù¡W!enÖ ËH ³JÛ5>¿ÖÓ· 3<²hŒMøõ¥ôP Z øjŽ×€ ^ÃÌÊüEëÜÆô{$ªYC© vQðƒ­ÛɥŜñÄ6HñuÌpÉÒkï¢k¦M Æ0I̳Ð_ÿæã¿Š_ùø»°µ[©˜ “/Áøò<€›Ûëø»ÿö¯_jÝGyöÈô¸òŸ¾èKpÛt›ì^1êF!€’ÒzèˆÙ¾P®ä4æÁŵ]^ëvA‘WkwÖù éÞVK2ÎŒ™Öå %üiL-“‚UR°oäÊ"…£äº®-Ý—€:§½Ò\66Fv{EW·Æ§€-vç c„¤åϨ¹+Mi¯å®›HýÍuò4§ÙÅ¡/žö{–æ%Ó 4Û‰å±ö]$€Gu,K‘±¥HQkðÉûfÜñ¤ÏÈcpÄàag›˜ÆœÝ:Åöd5ó2a²“±œ¸”Í%5…ékK¶‡¡ˆ™`¨Qa¬<tça\ ’žbƒ, œUu÷ëŒâßå°Û‡ñÒ_ú9|Î;ÿ<`oöIØåOaípöfhmKþÜt‹(hû¹ÝÒ½qƒC\#V²@$‘ƒË¼} éö½ÆŒ/…{ËPCÆÀ- x¬1äÔ±êŽe÷Ýj„öðòèïkÝPzRªüÖr#­\Z$Àc°í«XÇ–€Öï,·˜µiÍ|@i(ú q *ÝZ„ DÆö(¥f‡ˆM2§˜Ì5˜éNL°î¦ïwªȬÖDjô×”szzê—<ï‹ñ%Ïûb|òü |ßïüƒîýå(—)GÐã ‰7ÂîKA__aÈR匱>ÖûÐÊß»Ÿhç•2Òþ>åUÏòÒ>ⱊÎܹ߿.uM­Ï£qŠeBWPFïC3E“Å…?§ |TïMªwÂ4ÝizûìòX³†Ò° .I7ÔÀ‡?¨¸©UBŒ9·©~+˜’m¡µ«#ÊŽ÷ïÇÿ\´M>D)ÊœaÌ5ÀœùŸpà.Þw³×øúJà¥øßk–€bçÎäó˃BÂ{6Ùœ–Öà#‚vòn,nrà?-\|Ù“3Xf c Œ°Nmjjãðl `¶£S¸ÏbŸÓ3¬e‡wÞLåã¹íù±9%‡³×}çØfûQ˜é¬ý$–åQx¸Ìv¸¬Ü޾¸N`€EàÑÏ+çÅx=e{T®vÅ‘Y,ðÁÝ4áFlÇx]åêA\åzà‡îVC%2౓ ?¯µ¢¸¬¤ïhe>Ó<çMh²,¸Î„ô}˜EÚÒ;F€†›¼ß„v¥:ýUº^«þ¾ó=kÒè_3ÃV>Mîcðµß‚Ï}ëÿën¹›Ä=pÆ<} ÀáÎm<#„2®Zã]‘‚!Bæ`SèE TçVo–í%íÎ>Ê—#èqE%5vCÃt¬ÒFj§ÝDŠòN—™$rý»9+¥át(v‡tÎ!Z™Øl8ðÑÚ‘ºž)x%p• 8QPÍÌt'NÎ>³}·ný&IË„25Òû7¡mf`d§ kR3*ÀMPÜh°´N€ÑŠ"_TVº%c˜€©lwpå¹–}ÆÜcn¦Û}¹îFH¯zž”®¬<¨Ê­ò$×7Üþ¤ÃrbŠ©Ât¸ÉŽÀ?\8ÏX“Ü]Œ306¸·8ƒÉ:,s¼c“@I'vÆŒu¡(‘ÁÃŒ÷UJpåʹŒÆ)²@´æ¹*I8Lc‹D×XÄL-eÛ7™ÑuHÀcd~V@÷Ãÿ˜]ërCCø]>daŒÞ~¶®À‡&ƒcC\§EÑmðPPƒ¼`•Aè!àCnC J ¬S{J:lícF{b"Lsˆádá,[›ÌÔ<8“F™Ëx ÕrCÜHÐZ>9¿hkéõ}ŽûÝ«bÁ ˆœA¦fˆHˆb,QÀ£ÇÐÔ~jâì·˜RؘÛÂ}lüßT>/5mjhs•6•ˆÈ¨ Lš -@2ðQØg‘Šºc{ͶtZ Ä):~CŠÇÁÏ›¦;pÛµ/Äæü±Ý> cÊß“ë¶ß(ØÀÚ§`Ì3N>ücxòsþ3VïüTR»ºUP#¹x´#h ÐQQüZ‡ÓÔ m-Ž5ÀI†\hz>ÍZ3ãnØj¦MT|ÙŸþo‡³ãÜ>U*,]A=§gz ÊÐ}xã]6 "(a¬Szzj™CP~n5M Äâ•#3æ:˜@”R x` Ó¢2Ú¤Î-ð˜«ãþÇ]XS&‹ÊtfÊLø˜lp¶°5üA79|èßû0–Ó-îÿ£Oó1<‚1Ù ÎNž ‘)³EüÙÅeo1sŸ¬š¥<.²–E^gÕv êÊJàGfmmqì8½´cƒ ©#¯atn¥Üýަ‹¥à½^<Èõ•›Ld&Ü. Ïàƒ»¸ 1K¿ äªô/7˜×®ù*à±3 /˜ôkD#}Â4 ~ïoÅçþì?IJ}1ØlÙ€7 ð‘„1eÒaR¶ÄúsaN À€2»ÉèF@Áðê­ç"D/\'Ìt'Ìü©˜æG€åãÊ“3Ùrý"À‚ÅM¿Aà6pæ“™p×þîþàs`²&¹ÀÞIJý“PæRÏwñž[ï,‹ù¹X¥î.<£Lz† .#{K–ˬë(Ï69‚WBæb¢« ei¦VjålXÆ5ëöÂ")?™F9rÍ0ØA/€ÜNªüÉq=¨´¹îõ Óð1º³ÄË“Êu)Y£ÜíË’ÎU•—bûZ D6"~7ø&6ç¿åƒ2:|ûZ!p`Ãïz5Ó Éú eü_Æ,b­½ü˜Ü4쨻 `Ÿ„qçpö©ƒ–IÜ{œ Jr6¸Œ‰n1þ¿‹Ÿ±°¹Ž3<Êã{KP Àgyq¦Úà‰â?awÀ ÿà…Øœm±9Ýb9Ýâo|ã‹æ¿ÇÙÍ3Þ@ºŒÉf¶GÑäÄø˜`ìJDää^`¹à†Ÿ7F3»‹º›4eÔ•€÷èàJþ“C¯suÐÆÊmpŒ¢OYZ%¨ÚßóMÒú¤1ÞÏk€ ¦§Jã•¶¡]0<×ÑHªñ¡Ç‰ÀÇ î _vp0¿ZÓZbaíxÙ?ÿ1l¶BζCÖFvø:ãqä ÕI@HÑÎÜwÎmÈ €»Ò)½˜*MÀ£ÅöÀúJ»<†[7Þáã ›S˜é 柃m(PF`ñ²ÛÀÙ'E=#õAEç#cº5/²¹°¸o·TLgÈå>ÊQžf9‚W@ ¦4é“X-y1bô¸‚¦)ÓS{PÜÖÒï2¸0â,îbˆ¾ªíÀµ 9ÕbðhøWŠõžR¹£ìSæèµš¤±;Š]¯SLóÝ~‘NgL•ÞÒRoXwpH±<š;L¢«ëSZ ÛeÕ¤JG”±‡_õãïþ§¤Þ <Äþ1Ó= ÆgVÀ–°+·Àa\¹ã>¢ØPZ{Ú‘N,€sÿhÍ P,Ê|“Ù) pçp6Añ: x0C¦åÒR½§CZ2e¶;ù›œ,póT."ø gþöô`‚Fj†,ÅМ5ÁKë«‘gXn»óS×Ã{Çž§¢Lxï×ýø‚Ÿ{¬½Ž22šzS V’önsw’±µrçx’a×a ´RJ¾<™åÑ,#^$àÐÁU²šqXƒîMFªxsk«þU}N×¾6“ï‚K‹ž"V—"Ålƒ}ÀŹnyçËìa3@reIŸ€€†ª>6Õ×ͳ’@X‹1(®-6V»Bu€, ˆ–¸UgñîqËöcb; fœž½ Ëò06›?.Û˰^ÂÂÚ'àÙ‘ÔpdÍÌó[ýÛˆôr¬.8»ªÎ£å²äz\Q&í®nBÞãwÂüåj´ÇTÆànU]÷>€‡~îhð3 ÄÙå~v?$Jì°ÛFµk8Ë)c"ˆ0XæÈNÖje¦õ³ xÌ0Ó5œ¾Ö]Dzý¬»…Š&])|„¥ØŒž’zg£øm…lWDx_ð‡Cd{•nþþÿèK_‡k7&|úûÞ ,ûbŒr&•…êÒf­í5ƒ…Μûÿ®< Ï´0Ä"[ ð ò’/œRjQ=Ü5¦‹بTM¤óæ¯ÿßÿ!NC àÃp»;4]mÅòhĸðo`¾þ~$€â™øÖŸþ9?'·à/¾õ÷±ØëÙ>HJü.ëJK¡ï¹«4×Ën6’6Xya»¢M¦;¯s]¾B1p!B_¬%Ø<âç*®ÈZFm³°ùÓbx :cqÿ‹\À ì˜Ì˜¦»±ØÇàpS.“ad¦€G—g"¬›¯ÝÇÍÐǾ@IÌ `²ìs,‹Ìçê#9øÝ¯y=þü[„1§5³ê ѽÊ^‡5›zD¡×9MCP½ïšÛ_‹í38…Švú XÛܶK@Ž`ËQt9‚WRBé΂’6‹4ð@Žk ìTï8Ñ­fF°…JŠS°Æe4;ƪ jÀ€Ë -|€»’aÑŽvÞhWñ¼k0eWmÏ0XÛ?’ÝžßüúoÄg¼ÿNÜõž¿ã6¥R¾XïÀަ>¡UÏ`ün¬˜îñ·Á˜³à?|–ŽÇó_òÎ_FÓm&±'$‰†¬Do§×”ϺP~äT‚Õ/›“ʶôæù@ ë³Q3J`ydÀ£|ŸcLxů}&6§Îs bg0Û Øf£p¶Œ5)m­±ì›ƒ”[ƒ¹lÒɶTœMƒ"*2~Äóæ`°0Àrë7ü©)¦F¬Ojà:ÖQÙI¡b_tÛ0Rgy:šÀG‡ ùEÜŸ]ÖḮ‡ ;Lö®kÌ3z<$Ò<ö3ÓAøÌâÝèZq/öi½Wã‹H®mäs3ð8ú€‡¤Gì#M<ʾ÷ÇO¯½ ?ÿ}^ýcÿËòˆÐ~›æåR4àa.ÇvñÌ.;‚®¼»0nÚuÒµ S3H0 ïg¿å±µDŠ×—ï°ï«M`uS[w:€Ç:°£lS 7^\—´x"G9ÊÓ,GÐã*ˆ[|ð\<º GRXÂ1bhähåÌ’øö¥ø•k.ûñXo#Sý›e­ÜAY—vqLjXuAŽïVÇž a“"¼Þ]¥×žVÌ‹þ½xêìË~ú`¦;½ï,‹8/íp¥qÝ™ÔûhŒéÂ…eeªÙ¤Fèn]qlÎA±ÀÙ'‹ßJP6‰aSMTš“Ç1Ò¹±l*ˆÊÊcjŽ:>Âý ÷É烤 U„³<6·ßcŒEÛî(“óyo"“ÃXc ¦…(ô6ºÆL)k‹OUkØcxH±<„Ì-®JYk:ïE¶Ç™ëøg¯ô/ýj>V+û®Ú¡¥Ï¹Áêв¬È%si}Р๠Ÿ]y¾ÓthkŸªRr˜j IDATkÀsK𱋮 >K-·jå4ê;´¬žßÆJ;&U£8âRŠÌ4h„/›â5?þ0wÆœUn™9ö ±´Ú2¡çJ¦ŠÛcÐTú¥µQ•Á ÀàÌt'à6° )ÇyË ¡`–MØä „…ÃäÊ.r›âý3iì…ᦻ÷ñïS9ÊQžYr=®€D…Åé¶<”‰©ˆÀÍ—å¨ÛõÎ âìÊ‹Þ>©^SÑ®NµéË–™ûž—Š^ï³±ë‚3Zç¾»#EyäÒ@kŸÜ­| 7îâxÑÒ9w\Y Ý͹ј3ùsú"¼÷«^Œ—¿ã1˜ë¿Y}waáׯÝúv,…8ŸÔì BU.ÚŠDå­ŒÁ° ïi-£¬ò»À‡9âŽYR6“#ß ¬ñ†›œóŸ3E¦™.ƒó2ÁXà·þÒð~õ%8±&enáALSlçRLìèôS¤¬—ãµ@ñ<å}®æ…2-²E5$*·Ø¾šñçÝ;FØâ|ÝÈnÂm›.gÃpÅ:{€y¤3f= ì),Ë£øÔÌ8=¿'ÇS¥#ä2÷hWt§©âVHàÃ\¦8•ϱ¬PÛ‰¹¶ÓF¥y<‰föîn¼vÕ‹²+ wq18K ;$74g2{OÝ,hÊáôm o±€ ð°88“Aš3ü²1áÖ#?êÛœ¥voÉeFàCÒKú1†¢>CAH´˜7Š õr9àÑZ{q>Š`è—gã2ë:ʳMŽ ÇgÏápÒ;ÒùU-+7žN¾›EÁ-A™ùÂLæ6øTi7W) ëv0}Oc«DàCZõF]½Ë¸ÖµeŸàb<‡ƒ_§6‹v£J¡®F»gøG@©©µT 8gˆÁYb`15 b:4úuºnŘØÍ%‰±Ü9Üù‡ñWþqxê5átâÆæ]Æ–j2Gk.ácW?X½(AÓ<ҤۗٵŘ˜žƒ_í‹ñG/ûcüµÿå.˜ÍÍ\¹¢4å1Dûuá8ÖNÇ£ºÆú¿|îÇðirNÎ'ë0MÎÌ[½5Æå,.)€)qk™·Súÿ9ïûLœn&Lb<ÌÔæ ¦ÖzÐÃ-ð“ø>ô^¼÷í@¤~ÉŸS¯K•á☯¸š¹28¿ózàÑ;”šj›é©Œý¤Ú”Êž]îºÅ¸ ìbáìg¿ñxÁ|¾Ü°n¿®AœÕ£:Ÿ-Cm×–*hù˜5ÍøLi¾= ÿ{k+ýÍÊëk!6 ðu/€挌½Ùǃ˜3„ÐuqÌk¢­]ª«ÿ ¸î¹z3Ir+î‘WHôîži"ØQº¸p6]Gcv8¿Ñ(Ï14¶G­—Œ€%ë#ë8á{,[íï“DxNà9ÊQž9r=®€ø ؉;YM1S9‰%?Ú˜îs •lÚ¹›¦kø×ßþ­øêø`sëCpØ «}JÆkëVzçI4G.àÑïGý~²1)ÖãÑ@–äüÔéº]vÈD7"®xJRúê®VfHü˜µÀ‡®`Ärçj\Ä~–cêëï:!e)!®.{±=ºÏl ´omœ,€;€eùKJïZ0VÂ}7„äŽþýµÆ(hÆ„!÷ú07i D*ŒI©ßì'ñŠ_¶øü_ºfóA™,ð²ßx>¶'Àö,ü¾ŠwîÄ‹5<.,ܸãúIŠëYüp‰ñQdoq6±GbâéÇÓGÇÅ«å]-Ó7çõ4]¦1%iÅ:òöËÅFÇi8t³¥ëvu+I}6‰õT›1"ê%õùE9€%í\ï˨Ñbrźy€Óp_Û¥7·ºå:;#0e"kŠ38(»æŒÃŽÀÂuqý¸á‹Mñ=âXŸ2ð!fnéË!2UYÁÒä zS‘QŒË®wÎz¦ ×m†W tp •’˦qTh ØžhàÊpàæ{í"cbå(ûÈô¸âì.L«2a0Ä»>¼ÛàæâÜ‚×ýØs§ŸmV8GØë€@ÒE ]Ú»î c’+Ì£€Ç~¹Ýcå¥R´ë¢¯2E”ûW$ÀC¹ðÛºF‰×H †úl0Q3;ÒWð¨•ñ‹–a†îN%ccAb`„Ý·s3ÁœÃÇîˆÆ’UZçxG'Í9Y™£..]ZŒ¡¢< ÏX9Çéc¿¿Ky†kŸ| °7Pfauðò$¶Nd/hï FB4†¬­ØÆz/íEpu1˜·8‰]Ê ®-´ÖÈö˜¬ÜäBl ~¨[×–¶(Æ#s/27ÿ„]—w‹#ïVd“Å{_¸µ–‡lȬz/ë‘xHóV5ÚeS°ƒ„ôå)qŠ:Ù¸‰Æä¸ zVŽ-Öyýäëvq`ÚðòÇzôú88@Óž6 Ð}ÚÙ’bþa€‡œ•eg1fFLG*¦À•Æ8‰Ö¬­„ϬµO'Ö›c~œÁ˜k   %bjêÎû90ÎŽ§÷rý/¹¦ñS´x2Ün .]QŽC[ÇY¨‘y¥tô@W“ãd×—Qê\«o…6ä²²~t™ Èp9JKŽ ÇŸ>Ê蓘bX‹© FÑ p\ô‡Ãe·J…t´½Enðz¦:¶Gœ…²Õªà!·7•Ómw¹0“ ¶"W öð—^ËÙE¤û>D°ZêØmúí*ÀG¾x)ï–ý®OI„]ÈÊŒÊîŒfAvynå8¢ÀÇ @@˜X,åN—cç8êŸû)Ä!Û±@ÝakÞ@fUà•?DèãøN9v¾–ø¸èîîÌ“ïUÜ}FÛËÀ „‘{ÙRÖÂÍ»c& Xäl+θ é‹›Há&c Üä`'—–žn æmø[L?|=´>—2É®-ô>[ß ™Ëñ͘ ¥Ïwù?¿sJù ÀcMÆË£®Wkz:B7™Ñ1GIŠ2¨_x4j7€Ë4Ý}3K£Í:›g$¶ÒÖâ<âîÔó[€4êØÈ<êØ%H€GcjJÙR æ›â½®@¥Û=–1ÎËAê3k&Æ€2 ÀöHñ’+èâc{çÎ}[Ý å9gܨ.ìÙ (<ÄË4bÄåV)G}TàƒœÒzíåÙGä i¹éµûº?ÊQ.HŽ ÇUçò„)Ê ,2@i¬P(™nìÆë+ÎÂábüäJ`Hô”™QcYˆ‘K”`²¨sh±Ôïa¯~ ø mÜe1b€ÖE—)­œ`§ ­•0• –Çàø¤À†Æ d—q€‚äWb’X˜´‹GËJ+ûˆ2ºì\µ”BÑxJ âš±¥d£Iï1wó‰;M^Á4&ƒz?×ÇË1Êë1;b£âzgýë5ÜF6*(¿À™ µðî°;N…ÛTØ„ÙpÙe¤h܉ÑÁZær3Ý“ù¹ØnìõîFR¹ÙÐrKc{øvÇø*À 3]ƒ™î0Ó]p§÷ÂX’ñÊž‡>ŒÁO'*ÓL~;l|¬Ú,‰½3*J¦¥f\5>ÄßYY=Ñtu ­“öØ$C@ÎQŽò,’#èqÄ9 Ƕ’8kAA¤Oü< c7p&+ˆûk¤7¹Kl €ƒS©È1†Êš8ëÛOØ(­6R5Šn,Íßq>ûœS™ƒJ€©¯n©‚eV»®Õ=ì7†âz4X œ Tƒ<±"µx³(Äë©pCVÙ=/ΙÚg#nlgX•‹¿èÁé||! f¤“ý!êó,f‡ÙE–€6¡üÊ6.€N‰î/Û"-ÃÙq%..“u‰íq²ìäoÆLÀ°#¸ÂP™,Ã#–§êìp˜¶Krk1ì(âyÄ~·ïF£ý q¾ ß)¸Ø¤‹—çê±;Æzù‡‘ñ+Ÿ³*Dì¿8ß÷— CÏÙ5Åzë(h¬ËgÆÖж‰0‚„˜†€‰ñ@²/ùó€&`»ðè¬"³ƒ±:2èqÚ˜«(ƒ ˜ççãáWý‡¸ÿ×~ÛíCpËcÍv4ÛHÝR…¸V1‹NŽÝq>øÅ_ÿÛ_¦kXΞƒŸù/þ>õÃÏÇWüŒ…±§0öfÕòO6 F]ÍãJìÄâᢹÝ@È^ €¤KÓŸ-eƒ&HºúØæL¿þUïtQ^œû/K.³®£<ÛäÈAº*,úGv6ý僶FÐMôV—XáÝL%vÊ‹±®‹™”ÚiˆB,)Éñ^W,˜b”ø¡{”GÅgÜÁ¦¿ª]gšÏ]Gû\¿c˜wQEêøˆ;%ÚßjqšrUt©ŸYŸÇE·¦0»ÉUöˆÙÀ+ú&å €¼{þø+¾·^üŸ÷Å]R–“PG|—ûc(CŒ)¶n¬Ò±Ïî)Ýy¿.Ó|¼`Þá‘óårãýÙú7·$à ‚ fÙæ”°Ö–DÃç)¸ºD†Æ¼Eá®rº18Yê¿ÓMü›Âùð˜ÓŸ«Y ð²¶é²ø©oÜyøã€ÇyŠ“ܦ –GîߊåÁAéÀc Ë£%­wWœ«{ç‘~¥ÍùO8§]aÇðçú@1êíÉç×ïªØfÈ}]íÐs6ψk©ÉéV»ªmЧ±Òpå›E‘žâ5d¢™NCFšð7]ƒ1·…ÿ§Õ_t)Éõ-°î&¶Û‡ûÿíã×ÿÚ×ãôô¥ÅzÕ Ô[´WˆÃ•SÑÎ%ØîÏÙOâ3å‹ã›kçxÿ«~n: }›8D¦‹®Ò7EjvW9Îò»3°Î¨•(ë” îÍ*Qʬ×q+ž+–GÛÕ;_i»6—™Gù³ G¦ÇUm×XØYP»ÀDºGz á)¥|’½x cTŒ @¥Ë,ZÖ¾÷ˬ?%VJ²ë“µ²X°ëvÕg;Y‰² þµÔ}xx+¨Ðz ÛÒ•Hg5pÀCëØäsÁæ¼”LަMÅ‚HÌàÞßþÀcIå-$(1Óz6ws%È4#¦Å=UŸ6ÓÐ’g奄ÿx-í°x)_% $—•šMFÏ[|GÏ:ßgAþŸcPSãBüŒÉÇNˆ™W0yf† ¡“"aŸzëÏá\(®,4qéœyk1m=Ø2-‹ xTY[¦³|ŸöÕ;îÎÉ…áA¾sæ†èÆÂ¥9è€Ç>,i±¹$°¦z)+¤^·¢ñW»ãu @eÍIuhz-‚¯§+2¹T®1”õÑ™ÿŠvÆö÷êl|mÝkkNFþT1: eV˜0ä2iŒ—xOé9Øëظ›øKo~76Û?i\¹X5@)(t¢a©ºTÆÚ¶oê]±@ L ¦\ ׈êŸÝ>è½ì¦x¬Qvo6ÖĆxð¨¥Д)Ô" Д¨PÊåårÃó Áí–mÈœ‘¨º3ñ¹¶¡½œn­õ‡4¾Ëßð @ÅÀ=@wwQ€ äø‚Ñ>h!‚…%ðÑ|§+` H >7Æ˜Äø& nž †G4‹gë<ø1™ð?`$Êë6‘ïìȬ¤À¥ð˜¶Û n0×*ËÙ=xòSïÃ]{Ór f³Ö3‹r_ #€ÅqÂì(˜ް>¤n.ʉǤnpWƒ—òzÈ9»°„Ö¤§®Þ‡žÛUl{åŠgŶŽdNKuSÐ}ÄÕ2]Hâ ð÷5¶£bnÅk§úD@V§í{7ÿŽÑŸ4ó ßñÖ©ZÝØíÝ'»‡ ð ÁI3ØAŸ­%eÇuw*Ûäܸþ¯ÂóÈmêàòB“‹qT‚/þ3eø…µ¡t°±ÆžÃPÐ3´Ý·5]% [r±ŒÏÏÅŒ.R{I,‰½ |Áõ5ïlÛMYŸZ:• cµbÜ´˜"kÁ‡Ñ"½2v®£)-=â"äÜE—#èqEDf+E@ÉpR²lNµxI@óפثtHö³#ÞbUЃ˃þ^¤ëÚ5õî:ð@4YÁ¨X>Ìg´?úÒe{ì8&úñ<8ðÈéõÝbYÈAË&àZ+. KµÒ?|Äk©P@À‘ôºP®» ‡s½ÿIŒ²²rw¾H1ZÔQ^/Ž\ѬâhÀÀúù÷0iå¸3(Cë£e·…âçpO.”EÁöðõZøM|ÏöpÓœb{À:ØÉ€›BÀÜŽu R$@eŽ`Gˆá‘èjC×bL·`ZnáôæçwÞÛžØ{È`xDÐ#õIžÛxìæÂÝX–b,V,EZnhõÉ#l„3 J$r ~Oï:n\%à†½÷ ³jH1¦¤2ÅlIô«fFêgmw›èT÷¨ãLðî|Q˜.d[À‡xoÅIôYË€ ×ÃTrWÖò<9ó#ÇI  â»D€‚جü¸.ñýà̸Mpsy’Üž–èößq„{Û€‚&”í¡¾m9­ ¢ûÆž`¿¦G €¸v`€¢OqÀ@ÿöaZðqØm×Aõí Ǭ-Gy†Ëô8 ÁÀTCŠN®Å†*jE§v‡Á…MŒ»dNQò›—E-,»¥Þ]~*"îL¤svCì%ú±?P»µì•Åæ"¤EŸ“¶S,of†¡.4øºÊÖ¥|H踮\º²2é’òÊLr¯øO*Ï:ÖH®ÓŠãXww¡À„êòéue[8óC»ØZ‚Á>Ǻ‰ñ¢º¹X 3¡Haãl¸)ƒ&n–ÖGÑÁÅ…3;h¹ÓvÉ.-Kfy$Àƒñ÷íuÜñ±OÂÍ·‡ø› nà#¹³ˆ€GF²áR»‰¤ÌGQÙ1¦·¸–åQõ7ŠÀG +óz,9ŸÜX¶"¦ƒj_SdCYr]µÞ“•m¦©ÅHä.<ÞP:ŸŸ}ƒ°hCÈD ‚1¢ ©aÓü °ÝäJæò9°£«º›f©Ç \e‚ÏU ܲÀLñ€gbrNH‡Þ¹¿5^$w–ј&ŘVORÊÐ\R5€¨ô ‡Å3~^öƒ˜~ÿïaÙ|<ßSÕŽ‘±±Ç×ð8Ê•#èqÕDšÔ›ñ<‚¡ ‰¾jÈŽ¹¢\\n¤õ“/_5:0Wšëê¯ÅdJ·«´ÑØaŒ£fø·vv-DZç De{Œ.̽¬¼XÆö¨çï¥X帜p&Iø*pC5”Ì„ó — 6ÕÅKÊu_iAjD€‡ò¾ˆã«@´¶nœ­ŸE¡À‡ÐŽ\xvHhWŽÖŒž¦¸E>& Xœx7ââ ÞµÅ./©Ø€ŠØ„D°#^Oƒ•Æc)h©#®,‰ÑÁŒ jÁ-0ÛO†ï à‘ú‰È G+އÌŠÂÀ)&j ðèÐæs[x% C«Á’»Ö[cN1Ï÷àììs`0á–ûmlÝÇWÆLš&}W€v?b“9ãp0FJcY.äo.²Q±s}äŠúÎÔþ½(³1¾Ä“ocÈhçÜΜ"3QfØjçÀÄuÄYhk_‡ÄÀ¥ªTßÎz–GdlD0›¿'3VrV&ëú^§†iúÒ Ð¡\×<ø˜>Ä‚ 3ÛY'„M!Z 6¸öÁÿ7¶îvÐsXºâÆãŽ¢n˜qÌÌ©Ÿ}‚„}€Ùu²Æ•ï(WOŽ ÇU•6XïäˆiT€"ø‘¤5IKÅöDùךõßv˜D$_ç:7Õ% æQ¨â%£-¦H2z­Àrü<í§=Á5}qÒnÛ®ÀG%f‚1׊óEcŠR¬]V›;‹ì/BŒlh€­¦¥Šˆ~ˆÀGÜAÍñ=šFZ¡—õ•í »šjqù\ÊîRØÆÛ%‡Òin–-fnšà¦ 8ƒ”òÒº~äðO=œº±ˆ`‡ecA Xêò®ªÑ Õ÷:`)wi)®á,ò;7šö¢~k€‡ÄfnŒ·¬K¢rau&†€9EÌ~áÜ ¸ðÙ˜S(íH®Œ“•¢½S |‰ÙÓžØÄØNd1 1*c8‚Âû»ÖpŽâßÙ¼¡Òæ"¤÷µó’-ç|7Á™Ù¿ëqÙ#E<Œ$ÌEGã¹²ø5HhƒÀMnF@¨›äRìö^Çw:½ƒåÿ¦PÆÎJ† èJ» ï^·)Ýw*—·à©ëoÊÝá½lÝÓ þY´m¤ÊàñŒcøå(LŽ ÇQ²ðÝfïþºóÉÎ#"íš’"'-t}^+Ë£Š—pPwE¹KíQ¶ÃXÄ|h<µ"–1|ÏLi“„ƒ tqüc/ŽMDž÷ÈÎÔÈC”`€MC}ÆÙÝ_{z7–OüŸ^ùTÇq(#Ò™‹AÙòîªÆ?k#Pî¶rE´£DüX|Äkb,ÄqBÁ *Òý/%:QÉ\º¹ô€"X!ÁBÌ”‡e룥ÔMœspÖ$ $²?æð ¤»- ãè9X'¤¦-ÀŠšù¡Iž«â®qi ùÿR:Ú ¼h’c#HR†MÀ£h´ŠL¯zGÀA´x[qóÀ™ÎÞÄfó'é¼ëî×ãÆSÿ¶ç«åýÆ\ |ð9˜ïÖ¸äô~o1]:å¦>Œß‘ÇœquÛ(c¨‘H[DöNó™Êm¯oŸ5üX6nöYNþ›¸O€9õçSvM5^`cqÞJq qÉ¡k΂JçˆsÂôåÂy¤?³kygDö{ç‹{¿QÀã``GQ¹°iÁô«ÜÎFûö W-ݨxŒêU5«èèîr”gžA£”ÒÚÉ©v%à@áî¢ý­̲U†ìB)ÁaÂUvR¤Å𴸵@@Û8^±°q¤ÇØEVîè²C‘ê-ãëîó2v#ô€©m K“xËS¿‚izN¨DSVfbðu*QaœW «²<_3$8µ;ÉØ®‘ªŒ‰@W#F_–4oPP¥;sP„€©msñÛ*àƒJÜ…¶Ä€~NœS´„Süˆ`†#±=¨[Œÿ EW~gK½qßðàÿ £ƒ‘˃KmèøàŒKý;ß1c© t°jªgÈ€‡ÀDéH”׌üŽ‹±²š×@ÂyÔ¹ ¬} Æmmü6üò7}9¾øÁa»i»¹dömÂu‡ÜO…v"ñ `Юã}Z‚ EŸmbyÉúÉÞSúÌâ”Á"Ñ´³üˆ®*ü õSl¬»M bµÒþ×BÚÖ'nÉ:\Ê”ãÁ–<ÇR×ÂXw-c`%B8Y± àq™n{Iøˆ¢¹ï*kÞKq3c°šƒn ióûEɳd åi‘#èq”Õ"¥²•"Eq>Øù\8ë£qܪƫ1™6Zí¢»=ôºµÀ‡¾iÌ 1 $鵋Ügbã ó>2ºà þR;‡Ù­c{U4ª IDATëú¤RÈzÊMø9±Ë#°Ë#DÉ• vÙ £V’%©RM*Yê:è®$Q&œØÁîšL)ZN~ŒÙ?¤±-‚’F°b ðA®/¨óq‡9ŠõhÀâç¹Èèé4 ˜dDMùÂØp6¸¸8ð ß«´45­dtH€GúI±Ži,€GA‹éÌÍwUzVcñ1~´¥4¶ý£Üx·%cáÌÆ»»˜_ð¿7í-êP âîR¥ìx ¹_\ëEfÀÔó6 Xq¾u!(gåz³ðP9tnJ ˆ~8xÀÃÍ€9…3>°¥ø:×[¾Ñä7aGðÂ! ÈšÃÊID}ö¥bŽ `Œ+Xny]0 sŸÀî A‰é;íܦGÊû½`ðcx[#ZÀmÿ}ÿ4ú×!¼«å`›aG9ÊÅËô¸JÒ ü–ÎÑõ¥|ðßµêYŽË!ÌSꀥ5 Ñ$1a’‰À‡Ï—MÅz0ÄÐÅѬ-qþ’ìÜ®´ö¸M jº€¤{žéˆÖ‰•Ó䱤Ø}0fLʵ&kw=z@TõÛB cY KF=ÍØ¶rw»€ªL R–_¥(?ÜÃK~áAlÝõvôvøû³ð‘Aº“™|Ó©; Z†í‡Y?–žçBvYø±(¼ÁMÓ=ùã&Pã#øÁÅ´ô}d CJGËÙFàô½íÚÒ’†!-2BÊöº¹ÈåÊ€‡Vf ðOa ö[§­Î’x ‡Mz/b ÓO"{¤ë.Piú½»FÈkæn¹èýÙ­übã%ë #™lº©‰1)ÄpðeÅ÷Ú¿ÏynF¿ s{féYönŸÎÃÎés}U²®•€Y:7†ñáçÔ)Ì=¹=ùóF^状†Güî6 .-…«ŽÔ¿Š¨îmÃ:ݘÐq¢fÏcqkÄÔÍk}ØV’Þ9 ³IÄË]¹œ¥¾‹Ì§Ë’ˬë(Ï:9‚WM´E³"øØ¥Ð*ÀJ€£ð-€Éå¥fkÐ{ ÊKq.<"Ü)“µÐ²RĘƒ¾š‡¥¸ƒ2–‘cœ…Ñ»V<¸â•r_Ì(+.Ý·Ãôùànâ^åï®Ø¤gÊw1 zq8LÝâyL*…Ÿg„g!Ô%Š[l°, Çp“ý(cÀG>Wʦ‘¥Åþ@ÊÜøˆßS,d€Â ïlåºB%²J*–m»ð[܉Mn,ô äðçL¥&±ñÔKíÚ`Øaa¨²xÐã-6ÀjÙq‰ïœ €‡9 .¤“7D#(¥Øt`ŒKˆm4&°cp¾m±0JB´äz[Y}*f ™{èηŸ"Óƒ´+šfëAP÷Áô¢nžñF¸>»¿ tДçGÌpfLÕ‰ó\üÎ×áxD—– ðØ„¶éàNõLMªØ,=©\O`ëš5ž•+ºªˆaNom !÷Û*=hpŒçÓßBjåVÊÞ£”ò-ßò-øÌÏüL¼á oxº›reäz\yÓG“1xåÏÅ+ï|^w‚-üVÜ .ðAw[¦„É,gwð„û&KçFÀƒæº»½Õ·w\‰F‘ È jê,.í «+€úì Dú‡¶»WÔÁ~Ü÷唕Òx?¸ Ïñž‡\[ €‹Ï×+µ]—aÌ—÷/€‚æMìæ@‰XGáŽ#ƒ€€Ä¶kDW—x]àÀÀÏm|ײh ðLh›9¸¡]»ŠáÁËXÕûÔØA—ÚC¾Ë H0´ÅÌÇö> ÎÂLq ç+ÙèÞ™Nj¸h ÷µ&[N-íë4°c絓ßqA‘Àž>S­½.¨s|²DÐ[>†XÚx+®³ýû%ÀHfÀX€ÌÑ]ŠÏiêØRÚšâw4n ´ê~ÉØeà‡4F[±e‘±/F×pHÛD®eà—‹ Pªmʵ¶ÒoñØÝûÙø¢{?ŸÜ<…üÀ›á¢{Ó%ÉeÖµ«<òÈ#xË[Þ‚½èEOwS®œA+&ƒAú­uݨH>öÉï4-ºŠ Ë á;ã3L fÉŽ®@ýÔßì’:péÇüØËí‚1[ôàdÙ¨,X ðX»¸6Ù@ð•Ãx~øêèg¦¬»R¹ïfÿÇ-†]GVïAe4ž²èi¬x€Èê·ÆÎv×YÛQ—bLÝ8#ã0þ×ðê-ÖÌì( ¬/f&eø´êÐ&RVŒ- • I(ÈQ0¡*s[_W5ºBT ØR~f¿¹¢¼-À#fu(XHá÷Ê +6K‹áQ´ŸÂn¤+ß ^Fß­eÀC9ÛÈÈ…f¾§§Ÿëž‚]‡ÅMõt™©ÇæOnHÝ÷S7W´nìi´t\P æCï:é4 ÖUdàcMD£¹ÚÀÉàhÓå‡è`|&gáRAoŠq·Ê£àmlÞÕ»Zjœ”á:wÝäP‡ží+žPƒé§Ò²W5pð>+¶¤•Ûb@íÔŽ+(ßó=߃7¼á ø¹Ÿû¹§»)WNŽ ÇUÆDªNÞt§U=g ÑèžÃm‘ΕÊò˜NC:¸È~>¤ˆ;é2'”lLÆTŽÞ€ÒÏ ˜¹©‹ì—à÷KÓŠŒº«!Ä‘‚¾òû)/XxtÙ@×¥¥Ø½/G ¾t.3jRŸ2@i ©ÛóÌ]‡\[ˆÈFS,Ón›°S^”%ÐrE*°Ø»J¥”•ÀGª—î¨R×»|$¨[ W@ÛàG)±-¹MÕg5£K4ôÒ –G«‡˜Q$ A;Šs–1ãŸåwaüŠÑzÀCŠ »AôòóñX׈&Ïÿ±W};ž÷ÁáæÃ?PÞ«æ:Ê‚ûñú‹æHNÀ¤zÿ+V×°1µGÇ] 1í™ÏН³v&°´W1ƒU0z?ø)ÁÆÌŽ(RàÓdäFfé$—«­)ä}TQ°©„Q×­âþGßSMŸk´GӽГ”zŠæÓqÖ¶b| 󊻪®ØóL—_ø…_ÀO<xàÂëú©Ÿú)|åW~%žÿüç_x]G©å°œê£j;Pºµ€ÏkŸŸÀîØ'káâ@cXŒ‚(­öï3>ƒʨY-R  Ü é'þ{º[N]Z$ÀÃ-ò_,нgÝ÷/Ô¯•U*¼Óšá9.å5Uš^—™4»@î›ÈBP˜B©ü=vŠSÝEßYÿ‚Â-€=ןÛ.R Ž}.ž+<Êç-Þ·Æ6긶ð1˜û ¹ýB¿ÒïêŽòª1vXt×9†ö]žÄ¿ü­¸ñÐÿ»<©†iM,Ö¼©<†1c½¼Ú'28DÿÊín€‡píðuZq¬Ò•¸þI€G_,ÊqkÕ?‚<§9T¹†¾w!Ö†óK ŸaÓñ”N–®!ä/ÆêpiÙä¿TÎ8à]!ø—bLÓþç ×¡ç±æ¸,â³+NèÍœ4Ø®zoš ‡…gÇÇ™4îž¡òîw¿¯}íkñÕ_ýÕx÷»ßÝ<÷ýï?¾ù›¿_ø…_ˆ/ýÒ/Å+_ùJüÈüȪúzè!üâ/þ"¾é›¾iŸfe92=®¢Œ àd7)_·›!9ªLU ‡R^ÐØiÌŠôwŠiº''/Ävû¿Æl7¥s£ïª1§€™0M×0Ï÷˜38wënÁ¸ b<&gl$6M`]ø6ê ‡ÌöDÆŠÁ+þÆû|í3캳DY[n}?¹o#Û£3F÷ÙñkÛaë\¤T,É­e àÒÈ,Ý[è˜-Ù©ØåOë+RV—ÝÁÏBãÎË'åEÎåÏßçâ¼*ÎGÕß úŒrŽXF<Úš>ÇßÈgí8>¯«c˜+ØŠa¤1=@ßÍ5ï 1–¸Êk3ñž(@\ãéF«Ï¼ø§Á­e'Æ‘µOùæñx`@7=NݤœõŒFC>ÇøÃFÔ@ÿ¨¬ƒ¶ÑËScf+ÛZ—=*2{Qaå ­M­6—:EÒ‹x›.jjžì-<à+ÏðR¹%ð´~Ó)ìðm«ß͵îL|\Kc6¥fv2+ö¢Et{ÔÖø3wý’ޏ[#„~mmV±õtïØÅq™óf¿®Ç{ ?ò#?‚~ô£xç;ßÙ=ÿ=ïy^ýêWãÀ¯þê¯âôôïxÇ;ðÚ×¾ïz×»ð£?ú£C-û®ïú.|ß÷}ßйG¹92=®¢(‹}waà”>޶ ÂÜ-êkôÔæ‚LÊ4íŸÌøƒ¯ù¸íޝªÎ-Úw½¦;0Mwc>yæù¹˜Ìmð‘ñ×í€Uw¦¤…Þe…¡TÊþ€óTd3Ý–™(ÉâA›AOç·º5ìŽR¡¬ë4˜rÊÅÊ•Âܸw²ûNÞz©Œ|Ãß©Y<’ÉY kø½k’ôÞÃ]¥QÞP Áj÷7+ZeDÜÿ®±a*ÆGuÏ®[W±Ö®gõí_Xý;Ñë‹r¬ü—~ÊÁŒÐwÅÎ7Qd…gG•}êÖÒÊ(„ôlÆE3´$ÿsž×ëR]u=kd—¹­9¶(ȱ•±y|¸= àAßÉÆŽ~q~UÇ`?=€aV:PÓ­²|tf‰Î) Þ_2¶»l€¼‡6?—ðÜ –F5ïlÒoŽ1;ҟ݈åúö­ pÙÒ5 =SÚPذ^´ñ˜õ~èyÑŸ;Œáþj­ßÝk×ÙÏF¹çž{ðßùøø¼îu¯kžûøããÀÉÉ ~è‡~§§§€/û²/Ãw|Çwào|#Þô¦7¥ó|ðAÜu×]éïå/9àMoz^ÿú×ãyÏ{Þ…Ý×Qúr=®’(Æ ]L+ #Ê| €HtÚdD ;RYd%½A$…•´#þæÜ9>çm?ÍÍ_ í‰;‚"Fè‘Ót/¦ù¹©M5ÀÀÀsbˆ÷›“ÓûñÎoû»¸óî¯Ã4ß!#mh(¤»É€Òsê]‰¦»1™;`ÌY¿Éz=ê2—€] u±Áð©Þƒ…¯6à!÷3 ZÑ. k‚²KËPvµP üàåÓ~*Á øŒå}¤49È¡Ï;Hq͸Qü×eAú+ïÕµÚ™¤Ý79“Cï`«µ3gÂÛgh 'ýBp­\»§¶ÛTê…;¿¯­ “‹×õÆ7¾ðš×¼¦úíE/z^ò’—àøÞö¶·áu¯{î¾ûî €ŸüÉŸÄ7Šc¯yÍkð ßð ø¶oû6Üÿým÷Qd9‚WH†,;e@S‚-L•V‡èÂÒº®1¡îíÈißhìâŸïX{½ød‡‹îöFå×ZŸòÏmBp.{3÷JºVˆ”R5Ý+£ÆZ* @¬} ÎmpcûNð4v/c´uS¦¯Sw÷vãèÌ@vdœ™áw©…fµú}t!^u_ôÜ`ó8{÷“|=ß·E<´Ïq­QêŠc5Fã\åX/ŽÇ‘Å^x Î;ÃsWg>i¦“ç€?¯fW#I‰•Q½@ðxšv‡/-þÊ.kAcœjsXßDëWº¡”Ï‘âKÔ)ÄC»ð#ÅûÚÚ|ïÈï]Ð/{¿ÅXmþ‹_âZZ€€ÿ•y˜øyý\µaH7¥g#•³¦½»°¦þŒ°;v‘}ìcøÃ?üCÀç}Þç‰ç¼â¯À>ð¼õ­omºÊ¼ð…/¬Žœœà¾ûîÃK_úÒÃ4ø(]9‚W@<õ‘ljt:")Ý#2ð‘?ëìOG=…™ncHº²Ë$íð¶Eß·@/%6 $(~Jã,XÀ: 3yã;Òîyo!  £¤< ÀGº” Qá±·à°ñýÚ‰R®)3Rªâòg»Ò>8{vBè_ Pš ¦^»¯00ê6@oGCÊÝ>@Èìê‹Ì¾W@GöîtØ’K€ü½d] 3>àç‰jþ¨Y^ÃÇlüTqï)ê?q Q>”y|HNüàÇÆÒÙø@À¹¯CÁN;ý½«1Èäs=€#|ŒVUïï=C,´A+·(»¸FÚ癘å…Ü7„]pmÁDðC¬Okî*EN1˜Ý‡óp[4ÈYl× ŒfoÙUšJoÇ5¥‘óF÷¤;ð¨Ü¯ˆ²ª}dÏJ> w²Iü\<ÇTÿ€(;E6—Qc>ÿðAS øåÕÌ)ÃK‡õQ°V⽬hJ$›Ç+ £7¶(06r¾RûœçÃèŠs‹ƒ%Ÿ@W°>zãEcµ ôthszðØWV-ÑET~×V·+%•¥˜¤« x´¯Šì“§Ñè  ¿^6Á rˆóR©ÚæÚëˆ,ÙrŽÐgóÜΞÁо§ï]À£¡>ý¥’Ð>îêR&¹¾(÷³>ÌžLО×’]tŒvMëþë=û/B¶v‹­Û¦ï7—›+ûÑGMŸ%—xÎsžøèG?ººü~ðƒ;µë(»Ëô¸â'{ ï£À‡¯#©ÄœƒkšSga'Ö–:ºóÆ”è#J›ÁÜ3Tà€qYÄ'ÏŠpsa'8,€ÝÔÆ!Óö¹ Îèüšp8 Fp ¢>4]²8VñÂñf} ¶Çê,Ö.`YÀŽ(A¢¢1à Î($úGD<ë„¶-ÕÙU<¹BQ~W;È ‚ä¼–è€Èq ø(EÊ6“>¬,©.y²%«ÒØLad»¥+zO’Ë‹”Ú6Ó™#°@†øtÖ‡&X`ee3€¥bjHõÌBõvn,#‰ÔQ€pèVX+XBêœðtP²56Ù+0~d±<àaœbÌ…qgé­U=¹(ÀƒþÞÐ.BÒ|(möT'ˬŒêw¡Ž(iHõ)†ÿt#|Ú| ÔÅ(ûG~½–Òœ26$¬(øÊñýê+À`%`_êùØaeø¿F?[œ®?Ö¶cù…‡þÞöп¼²Ÿzê©ôùìL¸Ûm·®_¿.þ~”g–A« Ó#ý$L`Ô˜öÆ€ |d³‘("~‚fe‚Å”€ƒÊ?˜ú±Æsˆq"1>Òæ2N7² ŸW ƒ=Ég?þ8g_×ÊýN\2€´ðìÞ}{57pºèëY‚r@ÌÑúH±Ewñéí iåra…Ïëd„";âdW—ñë%ƒÙˆ‰Ç˜!\¿®»㢰sˆ”;Ís>ff˜Ö>ën2€mpQØ@Ãà3Üë€Áñ8™ß*àH¬”í#ÀAÁ€(Œá±Þ€cà”„mŽ·Nsl âÔ€GipÓã5À\`˜¥ÁËj°<ªÝôÁþÝÛ5ü{€}Ç2_À°*$=fG-k³¦zº»Ôf ¿ÐƵn‡{ Ûì‘D'4,¾CAgù°Òõ´*ÎÞµ¸S Ø Jð¼h@ZàG5–ˆŽšÊøQŒÝ–ÎwR­a+×@M„¾ÙÊqRöE,_õ‚¿‚¯¸ÿ«Ò÷›ËMüý÷½á eß~ûíéóf³˜µåŽ;î8HG¹X9‚W@œs«¨yEà¥d ÔÀG‘Õ%¸—8·ñ±0Ì- ²%KMÙ¬œíþ >½tÇÕ¯SAmé¦DTwZê¬0¾"¢2àC.“©­ƒÀ +:ÄÍÉj#/Æô nBURQ¬´ì2j³‹ë¨\e‹íš‹’”ÈÊeYT¤{¦;ôiîJaJ’êÊ2 Â쨜F79?J€Qܲ›yw´4D2ðÑdõ0g2wàçÿË_ý?ÿS˜èúA™<Ãï¿|+?øð×–¬ø ¬àƒ¥5Û(ÜWh¯ÛͰp„ñ‘æ„ÊhXȹüÚ,‰i"šïv<ÆÏ$‘\[.E xìÊ43jŸ6w¯Ú@8°k[§nî’[üÖù$î»ï¾êœ'Ÿ|ŽÙNĤt IDATWž%r=®¢´&ñJi‡|Ù.£ß}¾ö[°Ìtš·ˆ1=Ä4ƒE[¹¡Ï(¦n Š ŒÞB MŠü~g}vR“]6 ÝQ>ð¤>)ÓØqº«-ÛF 6@?¸bÅÝ\â±–Tn0ð!ÑX+ ‰+РÐ-‡SÝüÊ%¢"Ù>´z: ÎÂïþC xs `ÌØ›MàO3|õ¦:$GÅßàkøW°a̪q…‘ÔÑ>|}ƒ./¢ÒÈÙL{¼¾à“…³'àÇÎAh_—a Y•v¶’ ðIïDr5µœ†–²6”w£2p:`jt<†ù¾÷ ; 0xˆ ïÊõ…_'g¿ÙÕødÒ ìÖk÷u™l!7Î(¨ iÍúö¢ØÏ¼¿ÄM“õ"ƒ¥;²1Vˆ*‚ q®æëØ(ö9²n ÐQ€ÀG˵ù ‡&Â:C›=fÇóƒê­tCL»þÏŠ¼ìe/ƒ1Î9|ä#A|ä#€—¿üå—ݼ£ì GÐã*ˆ[+‹p®|•"ú…º ¬*œ…S\ü¹ < „í|p%ŽÿQ)§;ß1ƒ @ÀZ è»eØ+#õÅ”ÚR2Llµ•e…ëT>4QØ.’_oœT¥ƒEmï¹¼p Cò)n‰ô¬Dà#·/÷Ä>¥ ­¨ÜŽ!MÀ£ †èÀÐóé­”QQ9%ïPoÇŸ+œ˜aÌ5Lÿ?{ïsKrÕ‹ýª÷ùÎ93ãðñcó.áa2`b]&‰íìàD–@6Ñ•#ñ‡%FŠdPB—‡ŒD‚ºƒ-™’ñÂqBc ¶Ç\lÀ¾Æ±ï ø9Æã™sæ;gŸîÊÝU½jÕZU«z÷þÎ7|½¤­½wwuUuuuU­ßú­UîxÜÂà‡É…h¬­wÍ 8×—?³) é3©Ÿ÷€ýí—æw'S.¬µB~ô=ÏÜ\øø)â±’ÔúÅô­.*Xt Ï¢1¿žåAþ6YÃ3wþ1×Ùʶ:’,>‰ßQcÙQ÷QkŸîÁ´GI–´d¥s/{5¶‡e¾×˜5“´®¶tKç Zb¡©Ñä <è¡…Õ©%Ô€$ b¼%až÷~î/Y€S †hÀG’_ |ìàR?Dà£ä.¦1c„ëy;gñR¤¹éríÚ5<ðÀøÀ>€}èC"°vxyÉK^’ÛäüÉzl’‹|HÖW$ëlC 0`Ná‚bQ ã†][|ÌZ eÿÕŒšÜg\—Rlqé‰ù…E™V&Dy¢aV}7À{„ ­UÿiÎ`` "‰¦Ÿ[ëƒÌ, üXðÁÊV«^d£HÛí1déB¬´Ð^iòÕ­‰Öüé"Cc‚2 €÷½8¢o”†w³rh=J ðU¨Òv¶Ç"I@¿é‰mF€°ñ‡³=Bv*ㄳ;èq@?jbL'j †k£›L²:&ਠàQ{7-¡¿,OÖ“H11[—³;j"ÁnqŸ’î+c7*`D‹hJ™ë’¹,ºòºžA XPÜ•ëð§y·ð×ú®1@쀱Z ¾ä²/=y>„E $/ºnŠÇ•g8U€ùÙ›g®ÌÝš±c‰ŒDb•°ƒƒ‹c¾iß‹ïÐô®§;š‘öð…ñÅs°üȲrY¯zÕ«ð|ï|ç;ñÝßýÝɹO}êSøÈG>‚k×®á¥/}éªånrÙ@ Þ§A/¹¨A0kÀIâaŒÅìáp?ì1Æõåððò¶VE× 6¡å·/D‹.ayĺf×ÉÖ¸™29M.Ã~ªë`_(Oy‰ ¢ð3ÛMB™à™ LÀLR.%†‡T(2ìV’@¬iG‘)ÀªBÂÁ¢’ja±P;×b!3±<$Q€þ?†#¸æoÁ§bªe1Ö­ôÞè..c]hÞÒ"g]ËüÁÁÜ8ˆ’0ªX c‹BÙbl.(ÂÓ" ãÆ?„çݺ V~zÐó)»ƒ+嬰d')¿CÁ2nÁ]Ir™0°Êª`‡*S@WBÚÛ´‘æ)Åõ09Ç‹€M;——(Y£Š*škäÂg.²lö†©~ D ÂÇ‘Â;²(³¾sªûU­mø:/¸ {ı9 t?ÕK ˆšWŽôaòÛÄFKòæûRRÎó±^YJÆnLÇ4`ѳ5ïxŽþG?d1>2àcåþ]“?úÌ»ðÇŸý# å>þøã€a¯{Ík^ƒŸþéŸÆ[ßúVüäOþd°ô-oy †aÀë^÷º$èé&çW6ÐcªW>|îû9Ðh°s¦<Ò‹S%CÝC™k”ÒŒË£Ž“j7+?Ib6Ak\`µÌuí&«Ã€1ˆç°=Ô:š-ºCVÿL2H¦Ó‹‡ºÕÜ[$0 éĘ/\R–G €ïº.ZÉV§[Êd¾¹VÚ´Lù=Æ-Ó¼ßÇëÇ6׳ìxæšTi~<(À¢X€œí!VÕÓ£$±ßSW ˜UÙœ»ÀGΨ¨ÞJ¶H–®•ÆÍÝf€sRýf# ^Ê딜#€ˆÐGÂññw`¸•X+hŒ®žž«(Á\áÝW€TyPÀrŒçU”0.*÷5%ŒÿgTÚ^’ë`$x±â{À¯e­k º¥lnå† ø(ÆLª ŠÉu)abUʯwxžUQÚ]]CÔòõ|¬˜Š¡àö ðc½ k ^>~>÷Ý2ÛÃ,Ò{}Ž—Æ¢,>[µL™Í‘)¨‘®ó4ÞïçõadýæŒøÖ§=ˆo}Úƒ8íOñ#öæk~øaüáþ!à]ïznß¾K—RµøÚµkøå_þe|çw~'^ûÚ×â~áprr‚‡zoxÃðò—¿?ð?°úýlrÙ@MÉèàC¼ˆn.œb˜mHAÀú˜OæÀ‡&œ…àٵ٤É)|@6QÇ{¡ôIäÊ)¦k#àáNÐuW§<ö†Sœï`e{hK•ÅÁÀšäën['¸/ià‡Vï±"XŽϤ;ÑÔg¾MáYœ”(ÿò©u=æÁÀŽjÀà˜0g5$4stHb°ºdõÏŠ)‹Ç"«»¸XËl`{ÐôÑÂèFW‘|PÑc}LÏ3IßÚÏCyŒ}# 3bšžXôrÀfñŽ~5ÀCïŽ"90Wt} ï ctÄì}bÇVõ…WÀ1VöŽQà#Ù²}:”%°ÔX7¥³Â%÷;º+½Gs“p™’úv ›È k-á`9V»& ïÙúI¾ÿÄ%8;ÙÖ§efmš¿eNLf—©(øAë¡3Ñd1³=JŒ-®»2–ç÷ã;âó÷°6¶êzZ‡±à~' FalC7»Îºüp ïo’¹aÙ*ˆ¾ùqÅRVß÷xÁ ^€øÃ¸~}4ýÞïýžõ¬gáÁÄoüÆo$é_ö²—áÝï~7~â'~/|á q÷ÝwãúõëøÑýQ¼öµ¯Ånw¶F²M–Ëzl’I ðQTHßÊDYÕr½e²'È’!1(ÀØÞÏàMl¹uw:Œºî*ºîIpî2úáQ8ßc*I7¸¹D 𦹦çP>ÆëØs­PRÅ´¬.Y}H ~‹·|=²Ô—ÕX"ýºËCZ)/Ì»tQ§Ücb¿E Y‰õAØg$££t޳=æÄ=r7—9?}Áªõ'\˜úVv¨Ï<#ÃØâ1!a…%¬@³;IšfìðHŽÓ:§ÿ)é½É}³® ¤€Ýv6žçý:û½•Á’,Ž8ðinôã»:[ÝsÖ‡-ªŒ±r´ iºÂ;‘=+Ö6c©, 2kÊX`ìÓŽ3…<Å<˜i1p¬¶–˜HÚóÖ3ðÄÔ|L#kŠdÝW†´õ™•íÑŒëÁÝ“ÐíîÅå“ûqóæ‡ èûGáýÍüþ–¬E¶X—B`` xÔòwî—.})\wnïÿ ý0§wñ‰(»Ýï{ßûš®yàðÖ·¾õH5Úä¬ä|j›¬+¢}å’Œ2+\¯åé…íiƒÂEˆÇsƒz]«$?ÿðó¤¬4°êX?ìÇO¨ßԞѢžÔ±ƒë®:îÜ®hÕ2ßg´tO¾O%r¯Ù91¿¹íÇŠÂûRZžø Jçù}b@:©vHé˜MÖØ_òZ[?Ó¶õ<àá1è }> à1æŒ*­È :‚SŸŸ9A8×EËfõ2KA¶(p¡ÝŠíg(ü]h­>”=Àu×ðîïþàº|ë»xI,g hüŒ,ÿÂ'µ²õ쓾‹¢ï¿'iÅû7>WÆèà÷0¥}ú¬²qjµÞ/Ó±&¹VyÏ%Àá›?Ó;‘|âÅÝj€ïójÿ—Ú’¸kæŸ]rOp;¸îdò:$ŠTí^xÞ‰€Qæ*dûÄ98<3¡Lóö¯+I‹å›·g 0‘æyË;Suç‘æ~þ_tËPòåÏ]Y!çMï“®Õ’ñ#ŒƒÃ>æÇ:¿óÎÙ?²kŠ&3r×]A·»'—žƒ?ü¯¾W®|ý¸.Œ`Û0Ï+À0õï ð mÍcxÌýCfó¸i=»»ô%Ø]zŽmŒZã^6ÙäH²1=.’P—Kr…ñ¡ne+^3¤Û?Ækè¤%Süæ­c Œ–vÊúòS‹G’Ç—èò{Œ÷3Zýp:µÓ~œT0MÄV…|‰_-cr ÜÔváþKŒë(÷! ¢2<Èy¾C¾MAø"Œ”q,Yº0.*\ØQ:ŸJh%àÇ ÍQá ‹ª‘Å.w€•5¶Õâঔy¶’¥IR&’q­Äöë7 D¥ñwß ïÇàiÒXRŠ1óÏêZjgíÜxzú’¶¥iÀø ‚Ðg(³;l€G± ‚åV²Ôƒ›ç°Ytw,ùýO¥`_q;HÅ Ñü.nkãYÁÎÜ'É6–œõÁƒ"«‘z}ÕpOZ;ûq^ë6,s‹\‰•™UM?Èoü‘˜T¡ë8/ÇnÒ`[ƒ·.Ž‘¥ÀÔ~5Dv.™áA玥±Ã´1„‹à‡¸ÝþË·a¿ÿÀïuðwiu”qfv¹–Ú^9Ò6àÑ€ýþ£ãUÃq̦ÆÆØË¤ÁµŠl Ë&Ù@‹(–;$åÉñÄý!ý=‚]>Ò àZgð!ÔQÀðÌÇC ÖxF§·êMÜxJ~íÞ ‹>ÉH[û&ùÐ{¥Ç4à•Ë üêËumzžcÝf1*Ë•EÒÙ,%Š`R†%Æv)à1,ô?ë3Èꔺ‰Ñ„ËpîdúN1àú¤øìŠþ¾ºðgž‚Kã{¬µ©¼,ñýgp÷ÇÿŸÕËE`…‹qK)«$ä Fí1ýžEê7¥®6®RW?QddЉö-ÿ’ÂžÓøÕ¤…@‹™˜Psåç=3¸Ò²t¥í*àЪòY†÷S¤õsæ_?Í;ëÒñ×Þ%h É™°)l(B¶ »D˜_²¾­P†ºÓ±5 ¼­o¨ß×”؆þ¼ï1L.-Ãp ?Ü\ŸåÌï/ýVÞ³×¾Ã)zn¸>ÕyŸ%YØÙd“µd=6©*°š"œ¤QØó$³9J‹´jЦ‚”&’¤N ˆW ððŸ¡þ1ˆë€á&àFe¦Qd¿<¹I>¦À#Ó %]lø-Æë¥±€Ðú3 ©R%l–_©l‹)å/Y‘ÔûÔúii¡ÊY1K|n箢sW±;ù `÷TøýÇÐßþ4àO‚àÌÆ`à›‰u´¦$`n¥Ï&ïc„L@pzèb7–/@óØR{æ elácgQ·–ЬâÖBç!ð‘–oe¦²Øª4´™ÄNÑÚ<Ä™ê¦)Ò&ã,ÙÊýh¬ÍU79WÊÏ2?ò ç­ì•õ\Aé˜\Xà BA¼æyÎÓÔeyWG„ µ&Ë£©ýfC‘SI¬§bÒIñøæ´³køxp<69Ÿ²›¤¢(ðQb{&HÀzqP\hÅ=XèÄ'0A8ðáý<©z¸aï†tâûÉúÝb”›JLuÁQ`€¤2:fÖG’§R¿‚Ò(6ñ\È(² [¨åxëâ7§»KmgJ`^/þn‘²ËFP'—ß!¸²Ä`À~[°»@´»¸dÀ[í^ïÈXÞ—øH¥‡EC¶b»VÆR*I[ìÐu÷î.øá:0¹Üº‚…Vôä÷ ã[/¾¯v–G›dVyøˆu íCêRv})HèŠKJ6.JVZ¨æ½®Àßw1ª›wh10b¶ÄªKÝYÌîk¤!ÀÇxmÈe3&Ü“òîŠì~¹ÁM´Ì2c!¾s¦l†8 ÖøMô|üÐÞcÉIÓY€Í(³T$£TxY`,fý%L‰´ŸRàc\sßñ}6ϯ€'ùQÐ2J¾þ¬Åð¦11‚8´-±-1l‘?þÌ{ðîϾÃæÞ²IA6ÐãÈlyi¹H@±+Œ¾“KF3Të6Ur£© ³ÄÔ&’*Û£5îÉ|ŒM‚ºæ´Sq" ‹ Kú•$½ç:ð1Ö§aÑ1FìͱÜPÊÁÒ>Ájm&ngTE·«(`ÐØît1;¤Œøa€Ã)üþÃp·ÿÞ?>ùýNü؇K}W>6ë{TŠýðN¨g"VV,þÞoÁÛ_ý(¾ã_ü©ü>&íÆÁ Y øx ‹}+ËÃ"5‹v øÓ¥}]r—Á’„²é|¨V|î›18p7m9ÕǹÉÚê;xìÛçÓ’+ˆ›·Ywî \w5ü~ü`šÖ80@Û6¸²‘mmÇ:é·ŸÕ5»¡hGËŠºš*J|¬—|¬%Ú\h™óè=ˆñ›Juý”•/‚’•¶Ò |.ge5¹âD6’TÖ2¥ù³©Ô‚ùú†¼‡bÿ«»Ô —Ö‘ÆØ¾àѺ®LŒ‚Š¡NçÏJ¾åi/À·<í8íOñÏÿü'îH69ÿ²Djà‚~!S6QÛ–—k"„rÇc â ’F¶ÇXïùÚ|Ql§–j](5à&©e¾ÐÆRäÀ‡%¨¡=HœÔ†Öv­ k=Ã#=¡ô5Ë{ÛŸ0š$î\ñÐß  á-bý·*¶ VséTµÏ­ |H «Š~ñý˜"‰b^ïÐT‡Gþ¾ã_¸t?3x•(ZHWW8sW‰åa”Œñ (¯Ü…‡¬|¢ñ­—KsYÂr¬K”·{ïFÂäïOÐù6êИ¼Px`7±;® ëî†ë®"(ö„ñ¥C°÷‘àêâ‡ø˜êw¨ËO"µ÷˜ŽyIJžŒVî ÚVÅgÛ# n-¶ ç9VŒÉÁËiÍ¿PžÆø¬–A®Q>™‘$g~$ ]‘±J.´}+Ï[édÚù¨äR·냬Wãw†6ÏÀ „äàÐÄÛd“ó&èq¥9V†|HŒø ×¥˜iƒÒÌ•løËH­ ¥· xê¸äšVÑØã¹:˧˜7ïžíê£À¼0Ó‚/[­É‚)Ö¿|̲ÃÊ,Ê;µ@Ù6mÇšÕcPŽ[ LÀyû ¦x`~ŽãE!^«ÿJ–ü9㜦&]9héÁ»-ð¸ ê¼# ð±v;s‰‹ÚË#þ¦iëUI·hךðq£ð®GE…,òkÀÇx]©|bïÆ-"»ÝOOá‡ÓüpPcTÊ åÄòÜn<îvp8A×Ý7RÒ‡ëpèáÝÌö;àB{q1Ößl¬ºÔµtàA%öÁhÜ’§Õ%D›ùû_byTº“ºX\‚Šç›Ö• cd‰Fl=ˆ¬ž‡殥ï›lgÀÁâ@¾±Ì!qA‹‡I¿3Ns@Y˜{ý3TÃ\qÆëýM6±Èz\Pɶ…­^  ÞÊñŒÚÛê.R9¶†ä[ï I$ ºØÈÜBb/ª,Š5S>¹¤ý*ÀФÐK`˜d½*=ƒ5¤îÖjTjÙï¿X¦AùÖ][Òã²ò'3Šš„·³[9cb{ á|B…Ïê}D©•AhÆMÀGlƒ‘Éa¾^]ÜîXž«Yæi&k»cÀÇTVî²±[¯Ý•|T…ÈPvܵÅÜ/+ êZ÷Zc=%m.¸ŸU¤¬pÊ}ƹtî*œ»ŠÁ÷ð8Õó72©¤>œÌ#åvW±»ôl·pûößÂõ7§û.e>¹áp†‡òÞXÁi+ûÄBOFò<"ÓÌ× AÞr£lsÎkk1/Òù|Ý9¶X.c•,¡òŒëÆ«Ìu`;ð… µÝ¤¬â4Ÿˆc‰À8E}w˜! é\=×ÃëN¨‡äw6ž.Éç,Ö´¼M6Qd=.€Œ»ˆxqBo?¨²ÊÆö¯¥bY\,qqYC›Ãk¶DM¬„¾_xÐcš{‚ÖGlŸ €jtHwßé®G>ÂÂ\ˆÙ!Æñ ÇŠ®;µ…Šì3.°J€‡œ¯¾´¸ÔX7Ñ͈£îÊuO-lg&W˜Ü}a‹Ã|íO*àÁÜL|H€€³>¢ ÌB¡;ƒ.)è[/\óÿ¶‹«ÃÕóS›' ‘ía¹ÿNTà½ëà&%}ð§#ËÂßœ”ö}žw¡Mx<5]ì{ÃÄŠ·œŽî[Nz²À¦€¦ø˜/HÁ#.Ý_)†Vƒ„Z™uvÇÒþ´:ã¨0¯5DzÜ\jÖö³>Jk)Zß6H3Ř+IÙQæ9‰ÒíÕ`,XÖH¢  U ×Ïù¤kTuÝÉ]Rx™ï] ›ƒÓc“ó'èqäûüÃpÎá›®ÞçÝu¯˜f ›Âìæ"^\PJjlcLΪ²—žÉD’ Rö댶wº(ìþ—B«KÊöà£$%†·NøXxÈÖQÙ@®9Hè"D`yTã˜ÔܨÈ9{Û#€}t½(›\ðH‹i îvúu)Ûƒæe…õQ=¦”1+×3ÈqXLùÞ’ ÎJ~ù¶š ˆÇ»Ùýé(bµñu‰…¼)NTKºNGxìá‡è1Ž5ˆ |d-,³8¶--ÂØ±Ç0<ŠþößÁµÆ+S]Øñe†›8o`Ï÷!@WáçP®0¼ÆìÈóf 2]s$÷ÑÒ/lÂ]h봎嵦˜YÆõ%›šK©“ Íï!¦w1 ;$éŽ-â; ÍêºZ•,¬~N|FôX§¤ðÐ#÷=ò×è‡ ôØäüÉz\ù/¿ø)¸Rü13‡Xö—ˆÊa ƒ… õž9Ûƒ–¡à‚bãùŠõX‘®]8¶,&ø( {†EK»Æª‰ Ÿ³XÇFÀC°¼Ô·¶½¾=¯˺1-‰› €È:pÅ€ Åâÿ\e»´¸³è®V‡ËäÚR¼'¬pq;, %g{àâb)f¥]"õi½Ÿ7°<4p¯¦j玦›Ü'(ÛÀÐ߀ö™Âø¨ä“¸]~|.NÑãóãî1~€N­¶SÙ!øéîÒµÑgxøaAßÓ¬ÚÙ3/[œÇÿ}ÖVñø¡m£ô©U‚™Zú]…²Š™A›ÇÙHk×”Œ7¼í‹îÉ"ƒ„b¥÷èÆ¢ÂU“+ntêµ:ùo:â¡*à!ýŸåù÷=Ï¿ïÙøÂíSüìßü¦QMM¿¾¬F]Üä¡l Ç&‰´5¶‡*K‚; –±Uå K-X¡®Ù¤O-SFe$;fiß9‡É1AÉ.²j¬BÁžXvðóP,ñtU°R6oé›ü^‘å±€áÁ™Oé®E<˜/—LJ é”q(u³€|ÓÛØKåxÙ Xu€Àöp;ª0*ÕZRÞfP%*»ø`¾ÜbY4¯hyŸ•Û„N€o_œöû%–)©™ÕS³z7¾'åÝÈêõÝ\B}¸ÈwìðƒÒî6‹t`yÐ|#“ Ø%>íÇ TÛ¹tîn8wÞÍ1G²x±‹R/õ‡p_S2Ë\é‡zÛKÌšL}Juq9ûc±£.ë¥QšÃSL+­„u]k´ïMy4\wpîU€Ž öÕ±˜}›l²®l ÇEßgTÖu\Š¡ì5‚—Z-`«J¥e€™šõÒ*  ¹q4°<Ê}Bžì—¶ºÃ ¨H<ݸS‹9†‡´Àê5Šm{É/·¦•Ü»8ðHãÉÂwIPÈmÁ#5ËTÊÆÉƒè2àÛ„ñQ`bU˜b¸êÙù…Ày\`€wÆ÷3ìÖSã”g—»Å,g©Ì…)c-<¤2,´îÿn`"”EOIœ x¦º\iIYô¼A)c€Þažt·zä.5ÞßÄ0< ¸†þQ¸B`+ IDATøa¯¶¿IT%]šA€³:Ž>Ðz„ïfCÅR+àÒš/(P 쌱_Êë„4hr¹¾‹Ør*›c  U2ŽK­·ÅkæÅÌÅQ®×âmÙÖÅX>[ð?ûÞýÙ‡0œàq“s)è±É2iõqo”³<Äk¥¹/uaYðH2<&ˆéY|”úLÉ­Ek]ÓöÜl€G5ð^<¨<ßzë–G9¿6À#œ·.g&+›,;Îâˆq@¦ŸG¦|Ëbíó›IæzrÐb:(ŽÄÝ`úvîd<…œ¿@—ëƒ «[ °¥2š"#«Dµ?()ÛNÆzˆ€‡'sE…ÖÏ»9²í,!âÑ'm9ô7F cû°¥mË£G†Öæ­%åî.-óxå)Õ“o_«º¹4Œ3kLç×iÎÖÚ«ê¶[‰Q[ëØ²‹˜n$©­#ª€#¸½™¯x("³RüEy7ì’±ãˆò§~3^øÔoÆi?úÿãÑËÛä‰)è±I"‹¬öš‹Ë9¨Ûy“Òzˆ5^b5¬x”e ð‘YßÙâ¨TQcׄsÒ¢Ž±>j€DÛƒÅô°«åuVÒ²ðh>ì–0ia”ƒs=£ÝŠ¢”q0¥8È¢÷i7]·KÚ1nW[¼ƒŠ€ÑÕ‡³:^ÛkàòÄö˜˜¾ŸÊ/(~´øÒV»ñwêâ¢I ðÈX%¦†Ûeý¬³'äµXOßÊ–bå‡ù3ø‘—*ý–÷o|ÙûòsÝ ^ì ß¡£»‹¾•néàóQì%÷ _ØEsécÇ–2 ~„þÎÜ\¬ N±xhsjƒûJ¾Ã ®)´X&ͲÄÅ’×ùz*ë÷F¢èzÀZS“Ø1#ë³uN’Øí»K ù®ÄLÞd“;-è±I”fPáÈl1ÿ¤n+ù–R))íK¨KŽš”Œãåþq0ð¤à •óF)2<w!¶`/ÄÌ¢ 50ª¥nšù¡R±ÊÚ«hý=Ï]MæÊôBú6Ñâxdçé9?ð˜žõ’ñŽ[Û“wd—üv'Ïú/`ŒqËX@øêP­/J ïPÞ|$(ÒI¬$FC8>°k†ìw½~F‹-QÒãýÝ[ĺ¨VSt)1F2à#uAÚ…€i¹LñOò @d%HwÖ^‚»cʨî=|Ü ýÍè^IÁ°¹/¥uIêEâ®<šÕ¿&…yÌa7»!…´I¿^¸&²-ùÑ~aŽæ€G…1ªÊ1•_üˆ§)ð”ŒÛ2sÔïJXO4Ïw¥´—á̺¶ÛQx°|íJ¡7ÙäÈzlr<‰TñÃvp‘üN“ÿ’%¢fݰˆÕbÐ2ëdš0Ž x,{^¢ÅRL(áx¼X°à$™Ï‹š5€,eü/çå¹RIëìûfëÎ,Rž^Kļ]§À¶)s}kn/ú¢+W5Wƒ9©šK¸B¸•2½dU·—ÀžëàÜe¼ç¿ø<ðÇ÷ã®OÿÉ”†[5¹Õ“1$à#œ+JÊðÈÓ}ú‘ú!‰/Rb}ÌJãÄ: ®1"JPð¹Õ_U’sÀC›8À Éš–Ó´Ïò¼¤9,o‹zasTéo“˜‚ùýO.aÌpíjµêñùõÙ1‰ý‘g Œg\J®Z,×8^ps)Õ£$°£É­D‹1B™ÀÃ:žR7Ÿ£JÐRÓ3Ы˜ŸY¤ùmª’K×€ý]hqŸ«²€ x¬í*;Ö¥’§Wæ‹cÉ3h7ybÉz\@‘&³5bh˜vm1ˆ4qHLmbÏò¾ÐµŠ†òŸ%à¡úÛÖK¦ÅÌq\‡Š QtDVD©m™;Œ¨ŒA+º(qèd°CZSÅ·iË;@• )½?¥úõ‘Õ)µ”iÀ‡ »¹( $¡î9b¿–Ÿ_äªulÆšÈò˜xn(^ð~hê÷½é9Ï\€º´¤ÀÇt.“‚¥_ßÃc¯=°2×yç^Áê/Š;Ky·}¬²mãÒÚÛEK«½IzŽoŠ·ã‘Ÿ%cit–I?KSUH}ÒüR‹;­·67ºÉ]Ö*ê3”Æ3^}בàÁlL^¸Žr„ceÄ”v“ƒ« ý¥GÄ,ðø&g&Ä<°zV]IJµDþ~ ðhOdt<·è3]¾196yÊzlrà¡( œF©í”`ª›ëòEŠÑ‡œæcZ<åc äY2ëD¢-RÐ xìЄ>ƒ,x_‰Z®‘úDEUh©ÖEQØÖ1¾ œ¾,Û³á¼ý£v¯ð{…eËBGzŠ´_|ËÄw'_8r$¿.·†Ë¢¼Ãëìè®z;å÷øß…˜¾&ìÔsãøþ€R™:à¡õÉñ™÷s9JšVÑ\[¸ëFµ 3àayçj@yÐÄ<4^¯Ì[tö汊NúŽ4i>åT€~`zžsYÊ;ÊXj@ÜÈP‘ªËe…-)ͳQA&n.K˜xÒ.k†bqÝC]Z¸;_ Tâˆd „8,ç9®/„º¶‚”E£!g‹.•ê»X“uÁó¸^qEÚd“;-è±Éq¥¤L”À·Ë+|1BÃäKýZµs±j"ÿ¸Ò¼h’@X43ÀƒK€(”ùäÎ ýKcxnEq;x¤å§Ly,%nRZ­‹Ž,¦ÅÈúhe{èJb½½i-[Ù–ë'¹H [/¤“ò⊠'ôç§ Ffwˆq=h»Ôò–cyÀ#9G¬ç1~F¼Ò`w'r–~P è9§%ÉuÅg¾0ΙôwæÞ°ðàç´gy»£nR¥ü…wQNKÚÉÀ‚IB „Od{Fò·!ø'ÂûؤQJðH˜Šë²Þ @ƒŽóQʬ)ê¼ۄέ¥µƒxÐö ÖøH¯$KËœd½·d©»ª¡ŒÃX“ìýZxX]ZZ˜fó5½I ÏÒ±¹æ&ÇèÁ|ŸkÈY–µÉO6Ðã"ÈQ-˜³¨»¸„ÿK¬¬a&“ó™Ðû×ãÄn–‚0Oÿ&í+¸j¨ì–5'’‚+0@aÑLÎ<ð¤…í1×¥Kó å%.K­6»ÄU&ä­».MÖî LJî2ø˜¥„ðg'Ä:h‘¶]hý€:KESÐj×ñktÀc-¡@!l±‹8ùp˜¶M=_7 ë@Îü˜ÓúǪˆ÷ÑÏ âÚ ö¥Ìmk™TYG±4[•‘e`Gu›ÖFE$ÙÑÊ l+aL¡Ï&…8•?<²€Ò~CŸÅÁm))×ΤÊõÄò¿'V wÇŠ6 ˆKÏKRb{Hl‹)]¬®2¢TŒ-U–kÅmˆ¦Év¤í@­¼ÜE€‡±žÐ&þ=`Ì_c;1`9µQuácé“¶alk»vœg&Ï&›4Êz\0áû*ÛÀ6PÃk[q•ê³hR&UîêRmƒ#³=Öò¬Æü ú¡jyØ1¹X'Dz‹B‘‰Sb™Ø •9©¸ƒ ­P\p'1hâb/Øw,?‹Û×ùÿ$à }®eÁD¶…Vî‹.PK€ÄÚ Þæ‹Û#Ö§‡Ã-|ŒÇe—•À!¬1ÖG*EÀ#i49ªXíœåqdYºã"à¦2N'ÖpÆnóžŒã˜SÙTeåJÛ–.¤ Vp˜2 Üøûñˆ—$Œ³ø¯Cêúbtc\X6‡Æ¹Oq‰”ئ€þ,[ža)RW ¸ˆsBu~"à!° Çg»à«¹µæ¹âš¬9–I5Ãuw ð8tûت{àTÞýÙw?ü§žà÷±Éqe=6YUŠl€Xä…ÅWe¢- µè튨ÊþBËË1¥™Ú)ÑvPp¡ìª/&™h´k)PèÃ)#£…í1—ÅÜ]|O¬Œ J|XÆÇ..ÎÒX }[ß ïÃâm;AÂþ8¢u¶ ŠTî V 'JëûÅ,•@…6@âpð¢&%Ë%wu L >öIyp@{üæAN£dÁË®Uq;ÅÝ £ùùfÂÚ¿ÈX©ãñTƒ¢QÛ‚³VFd8$˜ÇÌ´<ÊøÈË®‚«ôú0Ž˜A¸ ”wd|ຜ»2õË=ÉkÐcš2¾I¬Éµ„ÞÏÔ¬¥À¡…´€ñtYýK¬ˆ±E²Ø*Æu˜ç®š‡Î;Ög+´ïR–† f…Ÿ‡¸ÕÄLÒq7u‘m3©Œ rØQ¸\»¸À\Q^ø”oÄ Ÿò8íoâÇ>ô/Ž^Þ&OLÙ@MŽ'ø Ç ÆúÐóe‹çšoá+Ž" =´erÎc¥(×f>É+lÜ$º;Ì‘š n—úx_Û}8úð|He*þÈtÛÎñ@°Ä·«JÁÚeî_,Ù2ðÈÝx,é!<7Cßi¨[зý ±>HŒŽv™€·ÜeÐ]†ï.à ·¦~ÑÏl _D&TêÐi¿Ôû( ´“BÍ.]?÷[î⤈ À„Ø‹ùâq¨µ_d u¼vÐcM»QLngrÖEq²ò¹îølG¼KœŽýw pêN¦úŽn€Þ4)FcÞv—Z^jl<Ë/žˆ˜¡ÎÒÎCS¾Åíuƒ@q¯±’¦iÔñ=ÆÌ »÷Øú­Xg‹hñ>V:¬å×vo“DuqTÆÓ9vTðPp1kÕÌžçòù}“MîŒü#Ò7©ÉQ\[‚P¿kqA7ˆi‹é±`Q˜>}z¬píx¸²¸¹´ŠáÕ-;£Ë =Qgy¤é+VçcötäSÃ3Ž ËSÁ/v<ß ÁJ·°»ïeøøüß`·{ ºiáB-ôÔ›âxøBì¾HRãÞXd±´ð`ýàp-©”úgyÈR]L®¸XsÝ“°»ô8w—!ñÄJÊŸ~ïî‚¿t®?ýÙ.1ÐÝ…,ài‹@ú=ë¬6m ie–&Tá=΀êÜ¥ •+¤”ê•Km¼)K%kk|×éyéSʳUø8GÛÚÏ’÷Ãlu>™"[¨ck}(`VØÆqÖû=ü°üÞßÑšÛosùå,àm`¡”îIˆÙ‘ÄÕ"þIÒ°´1^ ÷ÙÌÔäó¸aL÷cŸñÃ>ö“×%í[Bý ù†v£õ:ö\Äkx».]£Êð(°èæŒwé'–—×¹|ymÀÃ{Û6é›lòD–é±Éq…2; Œî¢0_?$Ö«x,üä‹Nîa>1¯Akl)ÞRÞ €CÒ.«¢ó -óׯ ºèBãz„ÿc:¸Ó¿Æé]Ï€ë¾èGK·D“\ˆ;MdxæÇÑ”rÛxXÛ¿‰aeQBswk‘ªb 0:åYLØ¥ vH¶¦qMª'0º»Æón‡Ûw==õ>üÁË?Š—ÿÒSÑíî†Ç‹‰äceøMÝXÊ쎳x,‘,ŽAâV ³¥r–ZKß4Xa5‹½AÔĵ˜ÅLçy30J…€’â-1 -.9¬ü1Ÿ!Æù: þ¾O]á`Øë}d ¦¡Á…#Ö‡^F\ø–u)‘\PCDL[r-Z™¥Z•”Ç×U3¸Ñxuh9ž'ldŒ,p—i›óF¦h¶5´ÈöX,FFoëCÆ›bþGm÷–MΓl Ç&GàPŽçÌ;8~Ê䃨GlÏdítljÁÁ­EMq%ÊnÉÄÁc¨BóОQn…›Ÿgp¢Y—…6ÐvqÑ”‰qÑB¹dÛOàö­ãk~çcèýõd#)[|”€# øˆ÷u–ÀGvì0zo»,Ë÷ÜÑi}?<†~ÿWÓtHûÞ Ô{‡Ûw?ýï߇¯zèßâöå+:‡ýö?ðyøîdR‚@òN¯O[ÞÕ)ôn·.€3Ö†íz¾­lQj”~͵¾ODh}§”q©¢€,R$8kƒ+Jü=4(ÍøH.çÊ,qËvK%Ž«ŠÒ>¶ÿÆoÏâŽx¿ŸX:¿d &Þåc¿ÀØÉ®ÉÜMø\6ç]sǨU k¢[,Í‘ZyYš¨ÌÓü¼);£1*‰¯ÒÂB1Ìy¥à’5-ý­¸%'.Iâ|Ÿ³U×L2õ«d'&ÿÉ÷ðàÃ8ï [ðR@ÏR6t‹¿zŒŸ[°jeHã»6žŠ€3B˜Ë @B ;·Xšk€‡Ô>6veêÚ¤Ÿúë¸þâàGd{Äk ÛŸ%ËCZÿHLV%ý¼v÷Xœ~±TߤM.°l Ç’f·–c—±fٰ䈀‡EüàLuv-3Ÿ¤KÌÌø°ÄE¹£`° à¡KÛÇñ˜ÝQ¨˜0 }8y´(¦Íýh^”e  ƒô<3àÑL³¯=¿ã¼Ÿ¥~£±†Êm0²=¢(n.Ù1µ^žÁògbaÔ“¢RLàcλd‘Ï]QrECA€¹œ‘X–´‡JÁý@T¡e‰™RšÃ½Àü©IK9¦üX°J„~7º´xwÞ÷†¢õ‡\åÇhÿÏ”öp\SÕzë,5ï¥RY‡i€G;>›8þNïâîÂÓV]ô±Œ…±,dÌ <æƒO ·þÿNÒ÷Æ÷|îÏ1ø ôØD— ô¸È"¡ñk0BSJÌuYðéèF¯´(·HÑB"²;–<¯ø˜Ë®ïÝdV’’ïî"Àcq_Þ¡sW1øÓ4;©"å—=C?°ÎLg‡b,†uDs ƒ!™q«@­Ìårö ¬e€Gææb>D¶GŒ½”sÎÚDÛŠ@Ú®pNI×Êœ±ô]6´7eÄM»^6?ðq^D›“² Ë Ф0S’¸‚kKrÏ•y7Õmvûb ‘<ˆsfIÐÕ |„"ÆŒšÞ÷ˆ)}•W7å¶ÇÞíɽåL9VY'œs2}ol®<"OÊ,—¥íy¸sÿä.$25q4¸ñ@€©`ä9äþ[b`0­|¦éE7ÝZÎà1ÉyÛ9ð…Oy/|Ê8íoáÇÿÍ/Þ±zlr¾e=.ˆÔ‚AÅí¥T†48¶Æý°°Gbx´DønÏ|8TÀÃvObT~©¿|H«-(´ [Kh²Æ]w˜…âj)»yQ†Zö¬®L/`Ò0Úaì/´‰AuÁû}—¤]GeæØÁb@ÈžÎb'Ïà!‰Å{-NÕqä¼¶Ï&çC6ÐãÈA€GèÊbÍÓ€Í*%ù ëc)çŒó!ùä$§³jÌ Ë¼ü(J¶è+äßàb’1MË €‡sWw‚/ç/Ããòh±;`¥À‡gVüQɳº2P±/êkÀÇb(èL«n–FW#MÚ™. ÀGˆíµÂ)_·.Ýìo[ÕZ¥xˆÊ–Yù/€üz-¿¤¬œm²”iAûͨM[„úÞïáÜ€a8ÐUc|˜å-ñFŽ)I[Hþõ-å Š‘!B$N”ï#ð1öû]x¸îÊøÜÀ÷Ð~9Ч¹úKAì„ôÞtŒíÑ…>Ù_Ï]¶˜¡ßZÃrˆ „x@©›¥ì–u–„ÐXã\|‚n÷$tÝÝã.8à ÀïÁƒçÎsw00pгOÚp-6KÆbŒ™x¯Óý”îÝ\æJ ½6÷–w,j™_Ïž¹¹É&Ù@‹&&WÍ÷|™RaÚrl…2xdšêuZL»•”A…åaÙÎWÛW>>B¾:XÂóͯŸëV’"Û£ð0Hq× ªtN€‡sWѹa2 IDAT«S=8¿›NL¨Â”Ô“¶\"àp•üÅó›E#••€<_öNd}×|Ð`¦ÖÇÝÄö¸ì??<¾ rš2n°.›¬ÿºkL 003 ’÷!=óŠcŸ:À ëîÁJLñüÎ…|h¹z¼’Š´PQ‰íTò¯—Ê·Ðç‹@™{^ºã„0¶».~œ»4‚èá]‡†L™µH66‚L¼eZ:­^n­õ÷°D­[¥¾~z7ù‡_ï'¦GÜ…‹Œ tnÌçIh)€‡¡­Šãg6> çX’ÝçaeŠë†ÒÎ3L\w7vOþ.tÝSGÄ]…ë®æqa&VÈ¥“g£ûÊŽK—žßÿê\ê:ˆÎ1óïe™Q‚¸,ï~é93i™«›æuøØï³þl²‰,Ó㢈ð(_»üš[‹ÙeAc üãÆóÄ€°I ÒÙ*ºtgkz Äðšý¤K~ÃɶfágÙV&ÍÐ`+$XŽÇÕÀátã°›À¶½>5ÀÃæÖ2ªÒÿù3Öšõ·eÁ^SP¤úòþù 7.JSØ ˜1½È!ŸÎÅlJ@H\ô… ޼ ¦ÙÑe>’]Á‰6™ò>4¶…§tø\éj‰ç‘Ë@¾y;Î~ïc“‚·í)Î*½Ÿ(ÉÚ»Q¡é[emàC+cüQ £[†ˆcŽ £äuòÃû"‚€F‘$ÖHÑú.ˆTÇdLiÙ“[€û˜ãØs\hDiy^ãVÏŒAÙДÆñb„TÚ'º¡Héü€¡ ÞßœyÆŠëŒ=úþóãý §Äí(€¡ín*“h…÷ÙìI CrÌêúV3Öú[øà Öôÿ}ü Þ÷…£fPhÛ½e“ó"èqäŸ=ùKp¥ TS™Q±Èµä…S*û¼k–ÅuÑ=…»:¥àGªüfA¶JÛ‘ÿó)sŒ=àw€Ã¨ˆNÒ¢¸à#W¦ €‡˜-¯Å¢4³{J óÂͨH™û9G­A^LWÉšƒë@º%b€L×&uhïü| ºçBú8ì¦Ý ðò.=Zþñwõ¾_`xmñ9ŽeóóïÇ`˜}b¸3 _ÅjšÐ¿>3>˜ÆSÁ*-Ÿÿ—%°HtWåA+§w!s£A?ìóú¸-IærÙ^>ÿ À¦Ü‹”~:çÑ4 ÜiªUâà•‘±“3<òñ­Ä±¸ I÷^~މüЋïPsÇáuzááýöe–Ge=&¥k‰Ib“6CÐQE¿jqŽðñü/ú2<ÿ‹¾ _¸}ŠŸý›?8b…7Ù¤]6ÐãIð·ƒ-éY†u×ó.,5KÌy¥žÚn-«]^r«5?dX´s±·HLÁF%tŒú¾ƒw·€)¨cTT[*Š€G<'(\æÅRä1ü>paÖhÅ´%œßÙÔ J»@/êÓl FÄóSš˜O¢å¬ÿ.€ÝÄøèáÐãæ3þ3\þäÿóÁ”f¾ŸìǼ ŠÊœf Û}\¬Øl3hÆ€ÁèJÓðdu¡ c>W mÇÉó—~g™äVéDQæJrƒÁ³0÷,àe‹{…/ºyÙ2øQ,j…8 kJÊÆáÀ†xÀéØãChSƘIÒ‡òŒ ðP—@`V¦=éwøDîd °<Š19 }BdF.¿2 È|LcËÁ[îÖØË­€ŽÂøØd“ó(èqA¤ð(‚+Yí¥:5m;Û0°†‰¶ÙÒvÄ܈nu1*ÃX2Ê–žõ,-€ÜXv¿Çmñ$ UïoÁár´ÌÿÕò|åŸ_>ó¿6.6R« ª¦Çž+õqòßâzb)·tL"zäØ3M€•Rrq‘ÎèJn.S:qQèû˜¾Çp‰°=“aVØ M@«<ÇΤð"òøÚ>$"uë­yûÎx®Ârh‘%н\S 9™¨î<ƒ,T ìPÞ1~àÞG™Ÿ×X™7轆ßaw¥{Íï%Sè ë—Eî0f©ÌÊsXªèªÏU3‚¤ Fô–Ú'é¿HÇÙ!’çqëZ—M6Qd=.€´¢"©×´- ŠÑà ù¯,q1Å58 ©±;Zü´W—Š¥òØ>êYÄðnüÌôøÊõË“Ë F°$XðE%¼¢l-^¸Jìqñ¼Ø}ˆ[-.W­ÁÍø!-.†@5nöë #Z~Ã|^ax$9/Då FÁœQ!$÷ᇉñòÏ]ýÛߊù&ƱìòRßá©QXÇêfrm9(+¼åŸ÷¹ÃdnAAD}>’|Xy¡mÄÊeK€g Ô½Òw€*dÁµ‹±º´qAtá¡€Gø=v¼Þ!/ÜIÀ%À‡eÎ'ilëR^/ ŽCéˤ®IžÒqv®äº'‚FR™H€‡ÆòûòÄöȘÇÊL€~ðÈîOpSie^e ¥C$>ŒBÆk,¬æõA!·*øØd“s(èqAE\[J×*“W©¬j¾*ûÇt³É#±ØÚ'‡$ñ9œ%J\´(ÚwøPûéí,•ãbº‹î Þ~ø[ÌM¬¤ ü0(bɹµÛZåç×´ûDËB©ÒÿKåftþ…€¢’¤ìü­ÁYvàƒråûÑ¥ÊÓ4³{K\¤Gð„¶§œïc„)ž‡)m*4îG©ÉõK+t% âØåÀG-ÆWÐ$wŸCjàKXNHÝxýhLYÎer,ŽÍµç/¸µ´²>ªï¤¸JÀGeLËvšŽÍîHû’ÚçàÚ|£´Iµ ';”óYñ¡œÈ~¤€e7OÛ-e9eÛ47€U Ï'c±v¯!ý§&ɺKXwš ¬/J±wæ“ ÀžÇ&›œ#Ù@‹$¢;ŤÀUö‰oÞÙ…Y,šêÄ“ÜÔØˆ¬q2¨Fì>“{M]\Tà#ˆÄÔ)¸ •­=‡‚YÆë Ê^²Ð$íqÁ>À*)º ÙÁ¹’Ã.÷9Ž™ëýâ(Ûï 4ÙbPGÍå„*S ѰCº¦àC/-»åN¥| ëHÇ,€X€üFàãò=/Cú> ýçç$É8ÓbÌŽü˜Y;v~LS’ÃâzH ÊDÍeK+S8.?ç6k«¤µ°<8͘û×ÁŒ «CSkt¦ì•¡î-[ùÒx ðˆß†÷]:îº ðP‹÷̲/ýv;Ùbð!Y³5àCK“(ùB !1l}þ QÜ_DàCbâF@ÇÞœ™’–˺ .£ðXª„ëÌE¢ñœ|Hé•uW ”Ѐm.—Ö›âÚÎ:Ž1À/|Ÿ%².›”d=.ŠÜZ¢…r€w'dQG‚›ÆE~a„ü|´l%¯fíN3´Ê•øYË`…æKiéE);F°ƒž«Pc—û´KÌ€¶›vnL@”¦ð’ ZçNð'/{6¾æÏ¾W>úó¤w³Â(Å¡Ýa‚õXRØf€d¼7Û£ |ôv`¤õ‰e§ ì(ÜeAD`‚k{Jy…$K]]è|Wq{TE¢Ý{!h©ÝÑ*jó³{GÛÃÂjñã 詺”Ƙ^>bá·ÐràCf{Œã6g7$ÙjÀt¨*e€-p[Ýø’u@(Ô£ƒeüSèãQv¢gc2y~1ÞGÖ¯Àƒ·Â–¢ÇÒåç!ž«ô Ù\Qq7((Q\» k<`i>ÎRÞó¹ã¡ø0†ÖÀW›\(Ù@‹ Ù·ô|2[]fp< E¤Ë™mÁZiß’Bc_Ñë‡ì¾Tˆ%Ѫ£¥µ,®Ž3‘Fš=Ùm#‚n xê;€°µ¾H¬Õ’ é¢.uŸ Qóí¢‚ŠgáR``äïm ™¶¼-õSÉÍ%œÂ€~ã†sw ZlØy§¯î`C‘ øÊT«¥¶ÿÄöÄ>—SÁ'Ú8zh;Õ$QÔ*..óØÓÆìHÊ©€y¹pbmàCR+`¸¦Œe`GøÝÀî˜Ë°¿³ª{‹¦Ìk׉õ˜ç_¾ƒ‰÷ŠÙÐGJ@M ÓÔGd¦ `.gåé:Z:†ÏÇnlCí½^0dAš+â\‘‰ŽM¿óë´)K&\˜ƒ£íà²-‡€ïE¨²Š«qfÔTÆ*¥E·R?œÍ¼Oä×¾/¸öµ8íoá ù•3-{“'Žl Ç’²_怇Fã#iW‰ˆ_“¥eT£ð:‰/‘YQ˨·V‹Kó‘âA¨ H üPŸUAÎÐ P\¨¦ #µâÓ`jcS~ç*qÁÑö¼ÃâRe|Äc’ÕÐØóÿ´JÇ|€-Àð1V¬L½m<²‡HÍÒ¤ïr°ÔϱwÎèæ"ú‹û0˜ÛøÈ„»ÖÄ:0·–>¸ÔÔ…‡JÒ߯b£’&îÀ`Y°«€Ãp‘Œ{À£ô^$ŠŽxÀî¨=;:Ÿ›˜žìÚP—íöB·+%÷¶& ÷˜0ÕK1>ĊؘsúôÙŠ±X`Wm玘¥OÇJqçšÀpZcU“R{¨;Š."Ú5ñ—-Æþ®n9É-…þŸkšŸ„±½àJSά`ý®¸Tó·›Çøñq+›l}®º#J¤¾!(´V%_&kàÍÒ÷)Ö2ð¨÷iÙŽ4°=fF">¦çu DdyH¬h_j¬…4(.ÏÆV’òµe21Ò£f§°=’4…6hDØÜ?f°p·7i.Wúç™1¡7Ùdl ÇE·Ë E U'ÿ±ÖÉ·hé² –E¨å¼DA&«E@GÚ^RÔªLAzN2R‹®KË2Es lÑ"LßA‰¹•膭iþ¥.$÷ waCð(Æ¢,´äD‰YDú¦ô>ˆ`1Û¥ÀŽôrÁú.9‹]8¸¯­ƒd&ŽÐÛ2ð!ÉŒEš c{˜]4ÀüW/­ŒÁ9àAÓÏÀ‡˜bg„SÔÐsÂ2ØÑ, p¬i®l˜ßjÏY‡•òLñ»²±ÅÆÊ;kãß&›Xd=.ˆˆÚ1¥:$lc™8ÉSq¿×–‹0 8€tÁrÆ ™EÌm;êb¡^Ÿº¿ØÐüu UàóDwš”ùS‹+§ºqwà4¯Ûz“|Ë6žªHV4Åâ›s3ØÑ¼…l#à!^ïº\ ºƒïYNE'}¢Ô4•°•'NÂýÍ@F,[c¸˜>=Þ¹1­üN÷ó5ž“öÜÊn1iÿ-§Íe=Ëô1¶×怆V†øÐâxèèsZÂ~ €ÇB·#qg  l@)éá=12µ`¥@R”\[è±ê–§$¿5¶{.T2¤zaÊ/‰í±€íQ¯¬ósVך‘A;2sSD)`©EZ9ƒ#Ï_ û­Ä@ñ¾ ƒ7匕iuGâA,ß=g‰cw¼MþñÈz\qèªØA¼fa_±,+]n©…­jéi`}œ”Ù¥…ß*¯]rÊj~>­Kz ÒµÅ69¶,¨éØfònAÒE±|Œõ<@¡Òn-7»ÈV©¨% Ì à²>Ï | Ñ‚EÄÒFE7—p Xö®­ô~¦€Ùl 7õEsÊÀG^mÛðŒvÀ=Ï®ÿ)F¶žK€„|üP á@„©<î’re/œTi`«X€.*Ta˜""ÉyúWcªµÊJ®`¥ÝAĦJ: é3V«ëæ9h ÛÃ*”Á#¸F‡z·¸1Xâm4W“çŸ(ôw¶^K˜UROVkë ñ4«g‘Þ· øàLN ŒjŠeÒÌæÛd“³“óg¦ÞduqÝ œëâ';¿ý>ÂÀÖbí’>átm"®X‰Â1iÏøøs‰•â@9lñ<@ŽùÐÏŸXŽå~ºÂg+àáÐÅ?.Éh=¿*ÐîožÄég–aþLí’) ªU¨.Ây[KÏ$XéžµŸx„cs|;帔¦eq(.¶J×—Æ a\ˆ§Öò7hõ¹R¸¤¬ž|Êåå×ñó;¼ïÛŸ4Åÿ»î\w¹Ž»?²²k.+ÉÁÝ!”¡‰ÿÚÔè¢ÛÈ‘ÅÔßx¯)9f+³þ¾/:NK€]g˜ØsçêëŽûYñy ¾Hû÷ƒi¼[eܪå!¸B§uÇÔò¸6°ßƒ x$íÚÁú!׋yLâ¡ìò“Ýc(¬xÖ¦»ä“(­O…õ‹4†m‘¤ Ïe2Œd×yÛ’¼ø‡URnM6¹ƒ²1=.‚Љl¢;6_Ï50;V•š¢³ò"ÊLs52>² OƒÄ$e¶Gš.ýMç#qkᤶóOvŠßƒóbÈ>«4/ÙZteˆ.3]y±ÌÏ l ° i›¥Àòª§$¦ÉÌú(³=J À–§æw€¿gƱl™Â ÷Îö¥ÕЬ2YFæ‡}l]hÀù[ðèñüÆÿ;™®¿ôT¸þ €|,Z{ïÜ"K3u•ãï®2F¯JI–ú„™á1³Ø´€|‹buÁ)Íà¼FA?/ÕÄ•Š*¹ZŠ.^ ¢<‡R_ZÎäùHÖpx†¾NŸŸ·C l™ÇÇ ýaMPPSÄ“qÖ6–åî£jì SamÀ in©[›á‘-žÊ“Æ*9¸¯R?m¬à1ea¾fÆ@Ù°g™óWÝf“MV” ô¸h"#³¤Ç2FHžßº–¶lrˆ~­;“h‘ø(gàêRÛ*×~m˜ÄÖ‰î¾$® vðÿ¾Wm+rÛI÷N·ÿÏ™ô©± bÌR_éxr¬éÙÖ©¦MVal0ý?ÄE“TNò×&;Çß+ÉÍ¥AZcΤÂë:ƒ\>D‘ʉ‡¸ñD™’4Øßd»õ‘±¨âóúl‰áø˜®™ßÝJlËœT¶ƒ2Ác=3ðÁÝÔ¼~OE€ÃÂFâÕ3ºydó MFÊ ¥ÏC/§dåç ´˜ÕYGTA”c ŸãÀ#»ŒiɹâœÞ%¿›Æì’¹¤i}Scr(éÅ][¨%š§Êx‚ Ò¼PÍÓO1V€lw²E ~©NˆUhÃy—¸Ê\Yª›a='–²ŠåеOw–íyƒ79—²@(Õ4NΖXð è´ˆ(¯¤ð—üZ¥t¢/¢&±n›€ Ø%¶GVW%nq¦ Êô¾E‘ývE¾ü<‹‹W‰¤åçäE€© }(Š•M¬Nl“` ±<”²´ˆøÃBL«Y0³ãŠeÆ`±RÛSiÃ"ðp(àAÓ¬0†˜·JVE««ÀT’²“l‹ìÒ±KŽ4‚sžìzQå€ï BxkZt§—n-Ü%¢<ÎX@!˜âyM±12Àƒ*3ð!_»• ëœp¬…µè30ãïÐZê¿Òqj4iÞµùbŽ1¥¼ÿ « Þ©.WPï¡8·eq9W ØZZ#Dfí»F&¦€ábafÖv³IÆ9^-ÊîààBë{Ä£cÄæ¡L”•Êဇ뮠ë®Lýd?ŒÛ™kãœ4·/šg5ãB¬¨Ì*Ë]W k ’ÿ&›œ7Ù@‹*‰L/-æ‡ëàÜÉ| ]œ ÏL¤TkKA¡5ì¼XŒFn`H;‘ÌÏ’Êd(y]Z¶Æµ2;ªpxäJS»Ë—Ò‚LN?U'ay øø! qÑËá ‹@mí˜Â)属Ò%Y`UöB#¥X¼Fj7QÁj,ôm’pJ|~ǶÆ hÕ É‡‹hÉç‹xåÚx¾Äîà ‹¼PŽ`´+—•Ô•X½Çç3»›¨å).©KI9äÂE¹‘ê}¨HóŠÄòÐ38²Ò±øH¤à“§=|ÐÊÏÀ™p~•`ª\ITÖ´^£[js\;–Q`ÛÔܘK,”Çþ„‘”—Ÿ©Ú'4²Ië+m˜%ýMbyä€Ç ºî Üîn8w?ÜÄ€ðÃM¡Âr}BºšÑŽ&o_wŒçM6Y*èqD ØÉÿ»d9Îí’g\@’Añ,|ù² tâ}-Xˆ$>Œ2øq4à£q"â¬ÑV¸>{VEQÜ¢Ó"VÀèàr×%³µ˜¶»b9-Œó!Íd!ö¤-Â9ìËØ =¼WC´°I1Y ‹süÃ}dL¯Æ÷*ôWñ1Äó·kÑ]¡Ÿž'g{„>0e!°>$àc‘›vXíû¤ÿÁŽ,Ïvl®NéÝìy©} UÊ´ç\]š”ÖÚýC9E+Öñĵ¥˜AÝeÚÖ•Ï“\jØæu X]WÓ-Y•SSð éDf‡AÔ÷W7s¥AË}ÀX\nÃsõúú#uo©¿R5ŽÜÛ³m„“²×eyÈ :àÜ v—®Á¡Ã­ÓÁ£ƒ÷û´½¬õisؼëTZ#-×|ïÏ9 7·Mž°²@ÆH¦UnPº$K_¼ø>PÈÀ=R0•Iö€Å¯šq­ÏºÐl û7¼¾I^2ðQ¯/_øêçõ€–ÂdGÐ¥xTýƳ5‹ IDAT•gËÛ®q+ņÈDÌ_øÐX°¨¹ª¨ç’cŒ­ÃŽ7‰òŽ}äyÕ”Eûš€‡YØ»Â]2‡„~ B€IYES à­.gEðƒ±>$à€êâ"–E˜{ZÝ$PŽ)³ósµ<7é7>ïLyU@”ªÜúLXZ•òSð^éÖ9æˆÀG¬ ‹å1ŸèW Hò3§™\:€‚¥¾ä6QarÊüsÂXÐê¢9VÄ65Å. s”·ÅÔÏÃÜ3 SÚb‰+ ®I¤à—E v´Ì4v’ÆH>€YÅgîaíí€ÇWö身¸|ùkñð7~îy÷ëSðƒÿ„ÔG­G#(ÿ[Ý…*Ó;¶±Á&›4Êz\ñ}\èi™lñ"îɤZp&¿|±Ø%iÞébVÝC\dšQñZ—Ò¢Q’8’õO*_dñšñ¸Þ/–KÅ@Rj¡Í²š øÀô;—ÔU!?’rø³ÐŸ¬à嬅f±ÆIáõAo[“Uù ™•œ´O¦n<˜” ´·¤ôòçhíwuG=–62HRàC ôK¥dWelMê^xn®+ µËüLì ¸È€‚ ,®‡È,i>X¹­×ç~Øhç_k/íÑ"5Ö‡6°t›‰Ž;-`­&G<¾Ž soLű°žµ€¥ñ°v˜ÝZDW=¡->´ûX(-,<ïocèo`ë#xÒ{ÿôýÑ8™°vŒ€G¥spimi· à±É&O$Ù@ ÓÉÔÏQŒçëÒvYìØÙÃtÏ%fÂTÇ HѪ@4bCüæí£¹"•Ü\Tà0É÷ì¦ç‡S ¸9v))Cø-ÇA¡`˜Ìà°±=¤¶i[|Öúc{°ÑpßÒB{Û£ÐÏSà¡Ü×ià¿Öà„JY ø0[ `«”¦¤@‰;¶Äs(#ÆqK]7ð<`| à‘&bÏ€¹»H®RœåѺ­:ÛÄóW,éR¿ óN²³H|ÌϾEJí­Š¢kñ±T÷БäSd¤kŒ5ì“5Ø´í¼Ðßþ ‡†þQ à HìŽpmMÔ]c¢».?$ã ‘¡Ý>Æ 8~¬,^Þ&›È²A4Êk!ž@X€º@;: '¨s˜à¦Éhœ—‰¯$t´Z]Ü(€‡s'p»»qéÒSẫã„qûx/"åÅlù/Ki²¥‰—-Š÷ÉÓSº¨Æ¬i`oXæt—Ü DÜË€ |ÄzVýÂýYãnŒõ+0J8hay¼~ˆ Ï´ f€#ßnpj…õaU*—XŸ¬pQ)8ÄX{2e“/˜yPÏü}Ó‘þÚȘpèàÜe¸înœœüxŠ¡ÿôxnŸ™÷{„-!S¥Wgy,´4`mN ˆH(¥Y[èBzú6Œ›‡+ u0/J6ŽŽ×ÏïÂÓéôð] pZd{Ëz}éô’¸X%–OÊçFETº^Ì“­Lq>, d6HÓ{¹ØAòÑ$KÅxQ>ÆD¬“ÊçJñ8´66õ]f‘Ι’kTd|åþ©Ö³Ðî-ì)éÚR[iAèg—¥´í†þq`¸‰`<ä€Vu½'½?Úó÷C}œ¢àÇÆäØä©¬`–ßD’}ìcxõ«_ç=ïyøÖoýV|ó73~ñ±)¾ïñ3?ó3ø¦oú&Üu×]¸ï¾ûðâ¿o{ÛÛšòƒ#õù'K8ŸóÆÏ´ÖÐß„ïobnbèoŒ¿û›ðÃ>>0?üÈ ñÃ~БÅãÿ©H.-®;Aç®âäòý¸rù¹üpÓ¤2ƒ05E¹àSlnP¦ÄI¨yø;Ý'ý¤ Àk¯äÆláŸ,­ôIî¡ÏòÎ#­÷¹"Aï_b‰ý¬|ÏI;eàU—|E¸_«r’µ½Úå¶©1«4iswZ8Œ¢4+ýhñŒ&-Y›4· Ñ:?/vp—¿ Ýå]wâú2¹Î¥ñ@Ȳq9ËMWR×ô%Ù.¶´»äea=/¸w‡1­×DZgHÞÑùÝä11)3wÃ煀Ϟ½ëlJ{£XóÉŒPL ª´g×iëS½ ã>r@`î[Z†•1&”>HŽ§Ì‡•ØSŸ±Åâx<Âè©Îa´O†ßaÌs'ãïî®;ÉÏ×§Ósäã‡ÛÓºo˜×ŽÓÿd] ­;}LìŸÚ¹;,: Ôçmç÷ú†Ói½—×?ÖyÖ¥ãý4õÇ¢1M`Á¬á2µÉ&g$Óãòþ÷¿/zÑ‹ðŠW¼ï}ï{qrr‚w½ë]xÙË^†‡zozÓ›ªyxïñÊW¾¿ök¿†K—.¡ï{œžžâïxÞñŽwàgögñ}ß÷}¦úxïÛ'b5à>‹–IÓw#@ ˜\^bœ!÷œU`aøü”×>É“×ý)ZÂb¢ ã(2€ÜZ-Qô‹–Á§¶IhzÇX „õ¡»»PÆ ~Ë(Ò’u¦f¡O€$NΕ”ô¿b©0²¤k‚uÈ_ÛáF þ*Þ?½'ÙªÙ4JÉRcŒå‘Ç~–Y€¬ [ÞWü@¶Þ‰ïTÆ Ò\\Ò'{ìo¼»ŠÁ_Gt¿`Aùªrl–EÁ¢9¿“ lÀÆsw¡»ô, ·ÿÞ?^¾†€9±8sæ©O)¯…ãò̶  ôw¡žüX_®C`{$ïâünŠï#y'–²[¬ì3ªgçàYÌG¹‘‘Ñ2îœ ë¨P¶ïçõŠÔú˜#°;Òüµñ9ßiŒŽ…Yr·CØE˜ú‰#;…0æì|]7÷§R5kטּ5¶EšvÒŠ;ƒ|^Ǥäxa<ƒ°n¦×7¶IuŒ²¸á™ bìœ3ؽ彟ÿ·x葇¡¨¨lrÑeƒèV–Gy¯xÅ+péÒ%üÜÏýNNN>ø ^ÿú×ãÍo~3~é—~©šÏ›Þô&|ðƒÄïÿþïãñÇÇc=†·½ím¸ÿþû¯ýëñw÷wí”Ê'°8$æEÚÖáXjñë X{JõæÇjÏ4‰«À,]’PA£ÈAÚ$æK™ñÑ*¢¥Ù|qeˆÏ£ŠÕ5ùo`IEÖb0¥Vguh Æ!Q’;A×Ý ç®N4âSôÃçá‡Óù:ß'õÊvhIØ•{Ò,Í’Äzël5ÅÝó|øÅ/Üå¬NIÙŒ½ÒÂQËn^nÔAQuL“æúÞò1@e{„z¤}Ãñq'ü4(V9ƒ†Õƒ~4‘æ#zzA?ç¸Cä€1OÌŽ¯8€Aˆ„u!¥/ÔVï&À£fµOæ>yM“vþ}2¯K–0g“>G¯ÊW]SèQbtXäP¬õसÆâ몸~•?ñòÞO3à!ý?çòO¿ø~üw_þŸâ5ÏyÑ®Ê&çXžX½ú o|ãññßõ]ß…{ï½79÷½ßû½€ú¡Â~¿—.òó?ÿóxûÛߎ½èE¸téî¾ûn¼üå/ÇoýÖoáää7oÞÄÛßþv[¥•’dÐ-€ð˜ÝQ¡ØÏ` Mè ®€‰_¤ú~¸‰¡ ýíG0ôÁ“{MA)ïº.4N"*øQ:OëÆ=5”?»9(AóRÝh´,•‰9¡„+ ÝÕ#U^ƒ«Ô\†ÀP‘ÀŽ&KcÛPfi+ä8øPï«>©X€š¦´ÐË”ôׄҳáç4‹kãÔc>ò¥:œœ|vOùg“;KïO§ÏMx+öMO˜K‹$Ðܱƒ£ Â üUëñ=î᯿_ûû4Ÿ nŒ%9ìr“KxÇsöÇ(ÄU‘üO„»¨‰@H ð`’‚_´®w'ð¡ºrh › w‡˜f]`Hçÿµò“~¯!ÀÁÆ2 Ñ+»ÝÂÜuÄgÝpïš›DÈ3)ƒ€Á8qk˜•qÅâò´˜ÝQ`áhÏÊüü¤kÏð࢜Ø1&."C~¥õ¥tØòlž`Ç&›,•­§¯,o~ó›/~ñ‹³sÏyÎspÿý÷ãïþîïð»¿û»jù—‰—¾ô¥øŠ¯øŠìÜsŸû\<ÿùÏ<üðæ:ir Y®²A¦¼Scb}dúˆÖò?­/YÜEðd?7Ƙ"ÃM”byT٠„ݴƒ…ÖVJ›çÔh£ ð§J ³.ÔX!µò¤X R< õQf~´s4VC<¤D0ù³º§ç V¸ÀÇ,Kú9ø¡+qaS$¬Ï†B½Måx)9²<ægÒû}ÿi à øá~8ÅàOãØ”*ý¶çÅë?ûÎçNFðe¨Q’š(ïÒ௣®-†gßY_cbÚ àÊ9Çã©Ô:Η¬¨ÙqéšJ¹â<Ëü^Z€*£ã@9f\ƒâúà ŒW)¹! €˜ëÔx8ò Û¶,mlÓ„Õ’²h̉)D™Y^¦ÊæíÒÔP>/‰•S¬â­%ÇWbcT3KF>„÷HŒïFÁÒ¦g·p|JÆÜáÌ?›l¢Éz¬(ŸùÌgðÑ~ðõ_ÿõbšoø†oüÎïüŽšÏý÷ßù‘QÏÙ—}àË¿üË—VU–²,NÒ´~'@ð#0>$«V~‘®ÇúRQYW,P¤=ÉÊ`ámXª¯„”ž#×þ¥ÉMIWte‘ÚW?Ö?2$ÖaÙdÆ­`ò}%ÒÒñâ1{À*tq˜,êÚ€é¼ÔèPuÓ R|.•`‚´<úi’Âó˜=nÝúÿ°ÿäÿ„þöç&°ã&Sezvƀ˻:¹Ô\’ÖûÛž[޹5ØzÊtQ\Ç>±N)ö9ø0º HǬÀ°‡¾äJ˜þ¾y˜üâÂ&²<ÖŽ)ÇŒó!)øTÑ·€æ>®T¡Å­I[kŽeå–T¤’¹¯°®Ò· @DZå耇fPªš4°£j„ éI>±˜Ç é³ð0Êb,Úd“I¶@¦+Ê?øÁøûK¿ôKÅ4ÏzÖ³ñ¡æsrrcHòÉO~W®\Á·û·/¬)Î’0 bÞ§¥â?ms;ÉÜs*Ã[xf.§ë²mÚ|?ëÆÉÝ÷i0¦° õ0ººÐIÇLÓäçb`»!½×BÚ,½æEðô[ hZš‡cLiÂÿ©ü$k¸Ö§NC\©è4–³«Y5ùv¸ÜÊFÛ2ü/,B=Z% VVxn³ P¸Jðͱ¾(Aßž–¤sév†I¾><¿e‹OuÛY‹ò‹iL!ýiÐÝTyH Wså(Ö=GÊVÀ®»ŠÎÝçö§ÑŸ6Rßìý°hKÚem3ç1´i·( ”‰äÝßaŒw¡>Ó6Ê!(¥²½³u[æ&©Ÿô9„>äºiß·X/i¼Iò²˜Ç‘¹ÞkfJY-ׄ©OgkC?Ô0J×yò®%÷›Œ§÷’¸n¡þüW_ÈM÷«nkK…)ÈÙ1bùxõ£B?^à`@äü§„jÍÕ¨9eR{ùûBç†yÝ®ËòLrÐMt#šÚ­•Õ’Õq<Ø”Ç&›œ…l ÇŠò¹Ï}.þæñ<‚Üwß}€OúӋʸyó&Þÿþ÷ãÕ¯~5žò”§˜®¹5ìA£'ïœÃ%—‡—-ÂB#pÙb†|€ i ðáÃy|ÄÅù4 ÇµŒ;¢ÙÓÿâýINëbˆÜ'ÏK„¯Ñ¶¡ïD!¹îÌä “?˦…™@KÀ­‹Œåð#_l+éu)XT¥ \#pfx¦ ˆ¥÷ÈïO;ÎwoIø˜±Z5fKøp®¦¼WD³Œ®%VÄì‹/€q~€ÇÈä Ê%]‹¥Ç×¹?×= ¿öC_ÿ¯ðô÷½ÎLe¦ÓÒrFPDS[ò-´ËŸM265× W× è J–ÛS–»Š„+ª¤>A⻘(øÓ¹©^U@¦ðàóY©ú@ƪÀ‡xhL/*ð·eTdc?›ž«¨ /yשK \ùnlß̽ƒÄêˆå„ÿ°4ƒxÒ:hìEs…1¡ÐÔõ†ÌVÆ Kb¹|=V àEd×f­aÈÇ|4ÔE*&Y²Õm? '“ë­!\?4·Ûa²1O6Ñe=V”7nÄß—/_Ó\¹rpýúõEeüú¯ÿ:NNNð†7¼Á|Í/üÃ'“ÿßr×½xðž/Jމq5Irøä™§åsà˜­6Î#‚é…Œù Úiz‰˜ß«¦pŠÖlÐPAil1 ¸Q»WšÔ8é8V ð S6Ç?Òö¶Aì o0ãéRÌä àC—2Ó€ýè>ã¾€ïþÉS · ½¿5Ý›lIåï×xlîßk’ä1& J*·ò6³CôºÎ ê¤P†-c´}lK™i]µ:Çô ð1ç“7˜w] È,´ö‹}Ñjg’¼ŸŠ¥>/¿žw«Õ~±•ßÚ†Rþ~°1½uV< Rlc^<À:JÀn¡ 3†lk?åïE­¾KÇóÊuY¹%–‡TG¥YÁŽÅ a ðh™ÙúúNË=ò÷x×£Ÿ¸ÓÕØd“¢l ÇŠr×]wÅßûý^>®-wß}wsþׯ_ÇÿðãW~åWÌ,ø¯ŸüL\&ÿÎ9u<ÈÚÙ|€,î¦HTRBZ~$E.;’4‹¢fàl….-jÞ¹4ž¬4A™[y²KŸ>²Äd‘Ïï^Ÿ°>‚Øë¹ÇJõjtÖÄåJ7ø%àcía–ÔÅE’ÙÚ<§uØÍ.cÁÕER,R‹n-Ö§¨t7Ò•K–ãì¹i[6>“v íûð7Ñ÷Ÿ†?½>Æ‘ð{PÀ£V(..ñÞÈùbT8›ˆÒñ íZK£rÑZ¸›ëS`VH©Uq!Ø¡•?ÿQ,šÓØBäùd‰‰11S&à#^²Àý&KßÀò0‹ÝÐ PÂò(æÛ¥ß{.5E›õ+ øh¨ß–6Û …³2•:ë~6±< ¢±;|Á%xº×⺢Ä<w­à‘'¶·—·ƒþ]ìXšFv!Xª–< ŒãRù%ù–ûž‰zï3b½} oú”îÆ¿É&wBVv¶{bËßøF8çš??øƒ?xæ3Ÿózì±ÇÄ2Âñg<ãÍõ{ík_‹ïÿþïÇ·}Û·5]wಛ?;ȾvkúJ¬Œ÷“ätœ¬÷1(݈o¦Î©;ª0t})à!V³h“•Êç÷'g<$÷6–u\À#«£T'È‹ôä9(»äð¨âñ÷Bö,ò8Šª‰ëš­n‹E° ©`!´p´¢9N€cÊoc}—XβvÒpò8×ò.åï9ÝáÄû=úáó†G†–iŠï ô÷~´±Z:.»©p¥uqPÙÐwŒýg̻Ą4n¿å|ôò8à‘wt'sÇiÌ0Yíéø2Ýo¬§ëÆz‘ÉßdŒÒ,±Æ—âkÐçt,åT‹·@“,QRCÛ çµ °Íe+éEÀ#ôVv±ôøgÊ78Ö¹¾ãš´œT†ôÑ/ûTS¯gÿ×3~°µ_[éú²öáÙkàÛ!說ÜcÊ%×áJ·‹ŸËÝ žõg“M4Ù˜Džüä'ãk¾ækš¯{úÓŸøº¯û:8çà½Ç'>ñ \»v-Kû‰OŒô¯ç>÷¹Meüøÿ8¾ú«¿¯yÍkšëW’5ˆ’oà˜@°Î+iÒ Ÿ©+Èœ¶`µ y u,¥©FÄ\¤ü[¬ „U1^«¸j”ê"±#j®7\)K”¿°<„!ø’$rÆŽ‰^Z`º$¬ƒõ꾺¬ÝOZ-ºââYS[ò£ ×[]ŽŠ×P¶Gø-áÉŠ›ˆÞV9ÛCîWÖä4Á¤Ð/gʼnItÅŠ q¨ûqÛÊÝI$j;4âó›1îë’ßEfe­%ÜÖÜÉvvð ç‹ÊL–rpL1×€¥ïyÌk—ý·0-xv±nIÆ9»Cܪ8柲}Jï€ÄFšûhÎò0ƒœYÆx3óŒÕw£Â’4ò‰õ Zœ´ÚºqQY Ç%m¯2Œ3vu%©f~vìžM6YK6ЃÈ+_ùJ¼ò•¯\|ýµk×ðÀàø>ô¡‰ÀFØáå%/y‰9ß7¿ùÍxä‘GðS?õS‹ê5ZIùÛ‰à=Ô)ùA#ౄ’H]Z¢{?˜6ñ”¨–¦‰Ø¬`-“¢ÒH”‘æ(à2“Ò3å­–KóÊŽÉÏZÝ}ÄââÂÀš®E¹®3VR²jTëâµuš¼Y,€‡á9ß™¬4‡Ð¶Wæþg> ì ð(Ä8ñ!Dæ¹& ü™Äéwå ùOn/Mý±Ã¢÷@°žðD xlQB_ÜÆƒ®6Ü—â†pp âDñ®€UÒõ1>—´¸µh;mhÇÖÚÙ&>€¤½- µæÊ"‚a,ÓÀ8©–¯Õ­e^(n+ àq\#DZË?D4ÀCy¥{ÑúÔYÄÝÐÝÖ+†I5CÃÚ˜‰—µÉ&g [Ï\Y^õªWÞùÎwfç>õ©Oá#ù®]»†—¾ô¥¦ü~õW=ô xüöoÿöòÊJrÆô¸dÁ¥¸ƒˆû–hƒÅ=Î×<´ëX9Ù=é}$ÿ‡|¢a.ðÑ€µ“è~Ä«%<§&á÷+.ZSVL¨P¯Ó´Ú¾ñcKMj,ªBeïóéÐï†ä÷á1)H¾I9Ë3àþs€@ˆ¿ÿû¿OÒ¿ýíoÇoþæoâç~îç²r®_¿Žû±Ã'?ùÉìœYú®e1׋ŸV&•¢»K‹òX@õk-Ÿ¢Ä xþ‡(ÃG´TðCPvM~˜Åç:ïj@c¼Dàc<æôËú|«ˆ0«Û€£mÕ©<]vLP”5)<ÿZ™-‹êxÚ½‘õû>Z$Uö €‡)S x€üÎ\^1èð1¥ÒOylÛ#Qz©Â¯'Yø[“ÿŸ½w¶í(ëEcÌ5÷+Ù;²1  %’sÃ"z \!+â¹AQ¼r5z«R%ü åpõ`…§PÑxW!X*A”KWƼQ ‘‡š°“w²÷ÎÞk®1úþ1F÷èÇ÷õcÌ1×Z{¯ïWµöžsÌÝ=ÆèÑýõ¯ß×t܃YþŸ¹¾tl—¨[‹Ex ?2d‡ELa[ÄAdPäUðU7&¯h=ü:D‡Cvô縱B¬ûê­ªŸ}²Ÿ < ûßh’D\]¿ä„|±8xWXÛ!ãaÝSñj*Ò¦›ªšÈ§s”ÅÁ6…¸·LŒƒâÃþ0.½ôR\qŸꪫ0ŸÏqã7â=ïy.¹ä¼ímo3éo½õV|ô£|ä#Á•W^  Sмöµ¯Åúú:>ö±å4MƒSN9%Ÿô˜¨S %Â4¹æ\ŸìˆžÏ´E}¼¤.¹àÞ‰Y½Üšó1÷Ÿ³“†’ꯀü´ë@Õäpég³äùÄ{$úµ˜k˜ãºt¡§‡¾£s¥°Û¥ïâNʇã±þz'jÔ•I(Ôukt÷Ÿh7gKZŽh_)øí9+‚I_N}"é‚™ˆí¬A>§Ñþÿ„[KR•CÎïÄ8â+"¬cEˆÜ{'™v7òΤ±ºEû&Û­…tùq•Üoê€H2Ÿ-R

 IDATàÎ;ïÄOþäOâ‰'žˆ–sÙe—áÔSOMWˆðsè¼chy„ññ1JeçE7PtEN&=˜—=OÖÈŒåcú;ະ†±mHpT–’®˜ì Cd™¸$„1Ÿœv¨FÆ÷«VyÄV¢ýw;;Ïä³èHÜ>2Ix8uK©rˆë¶5 ­í’59±¢þL·Qâ¹ûñu’¤lîDWOà½ØPUÎ)ùжˆÔœ®«&‹3ï«ÿÜTøŒÅõðò ?*OÑ}ðT0ú”b= cäØã=†>Ÿ%DGp®O|˜‚ÂösËÊR»«ímá1ì ë,᱌›pŠøÐ Þu*þV”„¡Uº£™ÄÍlI‘ŠÁ±ê`UÒcE8ÿüóqíµ×&ÓíÛ·7Üpƒsì9Ïy}ôÑUUm4âÛ±1Pü€€›Âw3ö;{¾ž<§ OÝÁme‹óU%+‚c@Ø«&IUŠ·Â ÇÈ‹F¯§ÜˆTãxsÑ·±UNRíáM¶Â¨ûô³Ÿ7µÒD· î¾€È9ùÁ=÷eÝ­|Åщà‘A@Œ"sɶæª9Ø º òûxéó´ÎéÛõÐnÐ3ƒ*£Â¬'AÚQå¥^"ûˆîžF·ˆ…7Ë©G„øò£Ckd¦Õ¹*vŒ±­àý D]ü|ÂÃVvDH6,uI~øu³\»üûI!ÙD 3'w]£ò¤”.ÆëK©±ÎûÝ%Z–$;œr"ö”_ ¿^ ŸctG¼e-BéŽ8£%mçY™Ãh è·m%XʵXpÒCHÀía„[‹÷[Îä|KÈ`yÂÃO¨I «ÌÒèßÜyYÛñe‚•ŒŽkìåv¬j…Ó\õñüÈRÛ¤Pz„ÇJö›·I‹T}½UTÖ RÏÔû\B°A< ÝV“È»£K5LÞØUIŽä˹=MìöØÙ˜yÿÚõ7Ö!½›Ó º­ëa캌5Z“ç.á¦ÆÆ” —Á§;ÏJjÖu}¡êû®«Q8ÑÉPÝ:ýjÖy¾‹KElLÂ@|rïõ‰æCK¶Û¨» ¼~Ë#BF«Äd1•6µ#KΤ¼5±<¨ÝÊFÓ©Å åUáxk$•˜Ìd:J€ä]x&Y3f,^Æ–˜£"Eå!8É ¤‡ 1Âcå’¼±òÝ‚¼R„Gl àj{‹Z€'>RùçÂÏcÔ6bÔŠNI p¦w(‡îUÕ4Ðä¡t2Kxl¨C‹ñA¹+䬰S)X+#™m<¾nËçSÚ,òƒ$>ˆ{ŸK^NíÖVª€Ø,Œ¾ÎàŒçÃ-eÚÉ4÷ÊU¡tÐõ€»£o+Un›í>UÃ!¬ó–!MlÕû;slpßi ñ¡¶.×ýrW*ò¾Æªì»¤dz®ÃþÙ®–ÿ£]Gnìׄ‡§îˆîHºÒÔÞuyõ¢›è\ç\²É!cÝL<±CÈ|‰X wL?¨f´¥`ËŒÎñºå,ÌDÏab}Ø Ý_¸ga+Â#7hüf¹eç@_ÿû‘\œ;”.A.„ôØ! :B>ð¯ò(Šã1Ë €ñ,û˜¢"ƒ]Î`2Z5°¤¨'“;æªtrÒéËxsåñ2ƒUÃa¢ãL>¸::dÁ@|¸‰âîSŽ»K6b„u±U³¸|?î™ØDGYàHðïb4>†ÛŽÆnÕš¦ ,sÞ6Xåã Mˆ„õ[Š4°ó18úÙþà&F*#”»Ôغě†9mÓ~¿‘Ê—çÇfP³T3‹ø` WžGÜ‹¢#îºaõ§~õbåe«;<»/³+À% ”v 2„€.;N0¸„…?6†×"Õpê¦ÿtÆœáúá=õÕA)eg ò +”K$ƱÌBŸ®trκÿz÷š$ýeŒ%ÆÞ‹Í0$°&&HQ,á†à ·ŸdJø*àó$…¤ó£úš!–‡åâ¢É’âcn!Ñöàì¬b)<|â"+€¥ÿ^$h+ó b¿;iRÅsáH*VE î€U=Ôzqb ŸIOntýpÿ“eÒ8Ê †ìp'Úy} u­ƒ“‡= §]ylÕ¢ Ü¿œŠÇÏ{HTàê”An¦‚¸'ƒ¼³PáäšsÿõÓ*,Wá%Ub”:·¨€LbžTÐÆï¯@°]!¤‡ÀE®Ê#3ŽGp^Îù #qå~†%» ꚬ`¥¤•³JœFí ³ —$j¢ê´BòlŸÎ¬Ú鑟ÇêFÔs >\7—®\^ýÑÕ£a‰4|É0MxðÆX÷µ¢¶9‰åI)>(÷DoLpÐFábíÔµ+À; $;¸ï@¢Ï÷ú'ŠˆàÉ ;¿¡!Ý4­²ì˜0±½tJ"Sõ ‘cód¹”ªÝrâiT3Š;]®«pÌÎ+èóŠ·%?‘!<ôg!>'„ô,âÜ`óÜD¢ƒ-35Rq<é€aå J~Dñ`£ªªæ ù¤¤ÖËÒ÷£­®÷pU ÝÎ`8ú“ÿˆ ²ÿ[9ÜI¯‡…rsa‰çðÊ}|(ß?¦‹MVF¯‚“™¹™`§ ŠøHL·ju(_á'>&UŠäæµ´B‹šxtJ‹ìø'&‡NK»Ëá³eû Ìz¢aèûô./fï®mE‰’ð ÈUëZÁ¿¯>ü˜"õiw¬fÔÊp¨¤Êg²â[0n†n`OýÁêìrˆüÈ­i„ð³oŸjø PmÁWÍ1„‡ÿ,B9ã#ú6RÑ”E€°‰ ãòEõ©jHnëê2–X’ð í"Ø Ç””Úd C`3\Z6Eí¡á|\}ldmN ¨ÀïouXIxÁI!=,rTÅJƒí@x”*I6©Í"?Æ¢š²£ªæŽ¿µYiì]7ô±¬:ç ʽa[U3TõnTÕ|˜@«J-X¹ò€¶Kƒa" p¥Phûç5\SXiJíÑ•ÑÁW8$V¬|ƒé 2¥î`ãqL`ô,«Á}½Æh7/H*írW´s²Ê•û®P¹ùgÅ ðÏYÁuãÕuáê×'«ÎRà hš­ö°'u¶òÃNSŒÑ$€R~ú\ˆøˆ^GDåÁæ=bûÜîq ɺçYã ×–hü&/Xgð^xŠ o‹ðpvı¶æÜM’ªTûX†KÆ”PªuDZŠ"Î-‣+EjäTû*Ši¶"»È' Yò#ǥĮ̼žÍŽß±ij ! ;Bz,;à§ÈBÂ#IvLÜI¹²¬“ƲeÇÕu½§ó«ÖŠ‹jÕ.Ð6‡Å“ãlâ£Zë—zw¿Ê´êª¹²FG| ¢½JÜ?;3¹HUÚ%>xäG\WÖs‰ÜŽÌ ~ Œ1ޤÉ@áê^jõ¿ªf$ñá—•íâ«Ã²3®\`&ɱü¢÷z2Šþ!$<Æô ü.Q…j¯.]…ff‰Ø<’j¿ªf=ëæ]ªròc?”O*ô{k4 KdÅ\WÿÙøç±¤AÄ:ù#Ò¶m׸õ¡òqóò”•×vm•‡•Wx~;Ô#ò\©€Ý+™ –¹YcPIt}µs‘)•Ûa'`ü¸³o‘;õõŒU¹0çm7’MyÏ‚CH‰b•G†p r¶F[›M¨ùGîÉdćåÒ2¨-æFq1[{ðÜßÇìÿŽù šöÑþ¼‘RØT]t=êy7Ǫjµè¹ÓPR­£›xWµ™艅9/ùŒÝÕµ¡à?'F ¯$õüJF]Ç`ª9ºÌ"’þØi©k˜à:ãÈ'>b±–B†jhÀH"Úvµó‰S‡þ½ÉÎTO`C÷–ª>µç{¿ùmÂU{tÿ7´JB¯vÄöåO¨=,øÏ1x?c“ýÞäªÎË">Ös@Dyæ\>@(ŸÀdÙá ¹ ï¾²÷(îNâô™IÀ®ï¬ï£@ôÇä^U–îS¥SNüêçÑc¢§^P0î“ñA!Pυć! ý~™rMZvlŸÚ6²Ýgï2üÝ»w~ÜUÛ“V¤ÚjâCßÇø.QÔµvÙæ`3ËœhÒccUè²;¹LÅÄ(…Ϫ •l…VÕj“o=Ûo?§Ås¾ýTÿ²|þö¤UµÝD ò&\°Œ0KJ=È•ù‰Ì`¼5½ÑG$%Ͻԅ„hËã« ¹õv}ŸV„ò!ÓˆÞú•¡üv±2Â#Z(íÎ2º°&{Dœ ?b±:†ºhÕ˜VyÌQÕ§Aí==õi8ðÍ¿…2fxn¸Um§8qYkXµG…ºŸünSoXJ‰KÞï&2¸jâÃ&j¯¯óåú|lN¬®N¶’Ú–»®ä½â'‹)%'Kv°×1Ôy(£'·ûû9(ËÜm€³mƒ%&¸ãÈŽØ9-M|á5‘õÖãMŸp‰ ?(â£xûñ\l‚›P.VNc]óªPNxlnzìn~üZßWQ ° ¤‡`ûcU«/FÐØ¼§šàñòôœJ *7~ÇJu…Å}xÖ'Þ‰'6BÛ¥ w¥adóíªŒÛίݚ!>ˆç{6©ëà$ý¤Ú#øîáEx¤V´ìÉž¿Š5Ô1œä1ñA¬¼’[ZÏ.ËfFe±ÛEOÒ”ž—ç.Ô=ÏÝ{~ÇM@ R˜È…A~˜bí‰K~xpÜZºóªõq÷ÙOÆyÿvhîßM½*o\Z|òÐ. Ùa©=¨Õî jì{1Àæ:¸fîý¦”']}ü˜<ï`bœÈ ÿG‘z[X€'<<×=2k}nF\˜0fMv„ñ`¬~ÓzõmO yà°Â±ÜEyT[Ô»†ćƒ‡z&áX«Wßâ£;7$?‚wƒ :¢ª“¨€hÚΘò–™ðœ»ÕjÛ€ð¹àÀ¸àÀ8Þnà½ÿñ[]Á6…‚“»¶Ä02XÖÔ*ŽÒ¼³c–L=زõ’PÙ>Ò ´Z¥ÝG³xm{ª]¸é'«³ögßè¾·àï‹C|¤ë2Ö]Ãì  ð„²ÁÞu€üÝ[¥ŽÅ'`|y 9¡'÷Zú åDå dÜ;«,ò7û«oŒÄ´”Ô‹æeß§È :üI0­ ™­Žõ3ÿ7à›ãHòà¦ñô¹£QÏU+†žëKƽç‰Âu`ã^œûÿüGÁû躹AM3vráÜ\F°Í8Ïu¹I¿dÄsâv1âïªMxTÕZׇöÁ¢©6Æ÷N%Øn-:w©j”vsR pʹ Xrt›G\¸}wÊÅ’ú± ýyäµM~ ej€¯TÛÝ"[ÑÕéùAîÆ¥û[íáÅMqŽDû¦Û4¿•oxm¬êˆ#À‚ñ—k[Þ5ès—¶ˆàÙŠmBn %Òc'@5è¬ÇmÖI­ÌLCxLAp$ý™)£57Î ##޹¹øA»’ècmèÏ]þeñ€«ÕX˜CoÙDŒûô@:žñ3HÚÂ7Ôã*ûœ1„G,;1ZÁª5¬{ã“Ö1ç¼R2¥0°ïyî3÷‰*¯mŽh<Ÿþž¹m7ç¾Ï\òƒ¼÷ÖÄ?«-»Ä›Û¿¦êäª=4AZ¡ •”›K5›”øpkw$Ñ‘îG7"iâõ¢~§ ÛõÎÉÂß ñ:9ð'½;f±„G Õ ó]Ï0Ãñã·öǸ”Øçä«àx„9Ÿr»Ì èlõMÆÃÌ#?:ø.T:íܺ¿C>Z%iT}cä¹ 9A|tåÖ!iºÖ úÝe ? y #ãÅʱ±Ýײ°ÝæA!„ôØI˜R>¾jä"©Uk°^é€Sêö“"<ìÏ6¹ÁîÂ`f¯PQÍs¶u‚ƒŽ—B«=:ƒ»÷eîë_°•©ï7Íœ›ò¥×“fÇ~w„\0“³H@9Öma…HÉ÷}d|9Cà&kÙÁö¬¶gò£êiå[÷’|"ŽB¬mÄR¥cC„¤§“ŸM|˜Y“WNÐÞÃ|ã×Ó_“³B:feÚäÙÿîÖ…RvôõIíæÂ¹¹˜{Ò½K.ñ„ï×D¤d2J uä#C"[³ê¶Ç¸´øeêI®ù=ÓÕ†tðïEî;£Ûºåv9ij˜¡Ú÷Të_…ÂÑNfP–Eb^„+÷ Â#Ò'ø}@ŒðÈrsrb¨Øä£K~è Ý,L_ÓÔ¨TÝÇÁ+ÖÆüàˆÀ(NTÿŒlâÃ`,Ù‘e P Ì\—kJ¹âŸ“"@ ûØT¶èÔj6¯eç ª]Ú>(.O ` ¤ÇNg˜¬’ !åÜù*¥öˆgä‹9’wÊð³3ÍRñOðÈp}ɾmàê­ûºg~‰eVíüºÅŒ¤Ê#¯!âÃQ{äNîá>»e Ò&‡€ñ¤ü£âyÁ}¦bVP÷& 7ì:Yä™&ë¹¶;aN©5€ð}4I8y½‡#›ÈŒ=ÉÑN6á1·>¯{íYÊŽ×J`X}†Cl@ÍÀº¹°ñ=|âÈ~fÑ ?±ÕÏ'úŽW±]©ŽÊÈFÙá)RÄ]ÞmpŒCV°Töä|²ƒUZX×ÁºÝºï°õIü¾íÕ™öêRÏT ØDé!`Qœ';Ǣ 28Žð¨ªÕlOÊ1¨æx±oéjïdÁ]ÉÊ„ä‡côú¿[¤„ùltÉüŒ,k¥G RugeÚ_U©Â%âegLÎ=㵫Sê¹XRq+¨@“ ÉÕÀ$ò‰œW–AŠðˆþ>eð°Ê݇ª U'’ܰÈfRuÌ2ï9E|¤È }ªM|àâÄ-s­fw3˜X¸ÏºšucÉí«xäKOÊÝ]œ}—Šøð¯Iß­*ŠLP|ÂÃ&õLþÄìIA„èðÕBN¸ëˆ9 ¯=EêVÚ—÷= ©h·uÇ­©”:¥Žíá>bGz вf©J¶ Š\¦ê›3Ö¤¯.Î91 ¤Y/ÕÕ|xG¼€ìw¢kwº\«~šÐp\Zâ6‰’]ÞK(˜…© u˜z1=4÷Ÿˆ‚¥°/½S«?JÊMºEºPdŽ`ÇBHÁä(•Ý)fò”"<‚N»ŸDTUz¶³]OT‹Bµ‹I:XµÊƒ‚3QòTÞïöwŠøpóÀ¥ÅÏÒkÊ‘•Zjýì¼ò¹«ÎsÌtÓS.ÿHƒ3Ü•ýÛx5@ ¢»sL n…;õNfLH‚UA«ÍÚ—Ùý&¶;Oœø˜@P‹¬ ‰Ä©9înK“< °÷ à# A ÛÖ·Ôæ4Ùé‘.áf‘‚1â`\a¬Iz‰;H >‘’£™ó—¯ ÅIÿ?E~l aª1EÙÔýó] c÷(¢˜)…RjSï§R”NJ è ¤‡`%È%>8Â#L!Cˆòêù©˜ï9ª9ж9Úwâ‹xÄ@QìÛ?î>Î$àÉ€èð‰W¢) Wô¬]´ÖJ”mldÔ#¥D€7éàlcjM.í-g)ƒu‹PƒÏtEÎj,w BêÈð¥ ÕFLb¹UÚìÝ*2W%„‡þuÃÊqAK¨_è˜Kbýïª T{ø×9£ßýÜ:Va̧Jý$h±ï æÕ  H»Ì© 'm~S` B<÷–øè>d œíQ¹82B£M¸¶®~Ÿìˆ¸w± ¨Š~BeV†‹užÞwa+ioÌäÞXVŸjêAÒh¸]>¶r ì[sÈTjG.g›kŽðÈ?-ž,Ó#ªx÷·†V„¨¡OM|x¿VûõÉ ýü‰ 7ik¤ÜW"é}Â#9¶ùA[­v³•îK—sÓÍé«'$<‚í!=FOð‰Õ˜Ÿg‰Š#ExØ¿UÕ Ç>ð=oÀ—~ö‡ðœ÷½&ëŠ ¸‚»Fr… É‚ø&çÏÃü@ ’9+»„›In7íÚ[gŒE ÈjZjÒ >ñ5ÄG? ŠV}:"g Oè"e ˜4 R’ô˜›Ix”DžŸ^UµE޹“ ±˜Ô¥¥š¡®÷uuU‹¥VžX®*ƒŠ(öüR„#>³PÍ@Çóèòš?ð…D=VKía»¹¤ˆÀ#?,êýÎ÷®‹ìX¢-%ïÀ¥+ð:Ô)!ÿ§ß~7Õ>48VœMìþÙïm7±°Ü/L"ÿ¹¹Ä‡SeB9¯z‰¤Ë•J—ž_ÍÒÊ ïtp¼??Y¶þ軋±êüë$&Ž1×§úz¸{ž;©¢&ßÑþ1¥ãÉû{àFIŸ±1Íê§³(uì¼óU¦lÜ­àšk‹d"™}Pt{`ê>ýtGn}xöäx<ñ¡ëì«>|°öXß‘RrpÈh7•™ÔÏQÏö¢ª÷ªAÓî] ÛråÉ&a,ù%¢R®D9÷\‚ Bzì|äñÇQUþÓ®]xþž}æx¬#OuòQüëØ„?Jx8ÆG¥´^ýT»@«‰HÙÁõX†[ŽÚƒ:žŒmÂ8±ô1Õ‘ÆWxî1ÈlYã‘0H×?£â eÄæ·L2KŸÃüVUó÷iõ*¯<ñÕÎwC|Zí¢Ðp`Œ j¢CN~l(Çh.!<–%Î| ?É·ˆ§ßò ÇöÖþ¿¡ ¬˜Kš2j[ZÀº–H΂=aô\[RãBƒ§Yñ1CÌ&ÜÍ!¬ö IDATÄ †î-…Ÿ¹Ç,‚i('¬‹Áßu)Øý"IÄ0««Áû8Ä@2ŠåüÌuK ®+á¦ö±ƒëˆ3Þzm7Y¶C–Œ›D9 À‹ñ⥠îµåòdWËr•J­ög©T2fÿýŽ’ææþæ«s8,E|Ø×ÆŒÃ$ñ­O ùU@èV5êÙ^ÌÖ¢ž ¥½m´€ÙyÙ~m…X–|áÈ“’°)Üüø}¸ùðwÑ´Bˆ¶„ôØø¹ýû±›Ž²Ø!eœ/³RZIÊTwtß#„‡¾]‡j×ù2"uIV,">’×ʘ ËcË4“%ϵòŽ_kù€•sívQ{râ )ÂÃÙª4‡Ì²$Ô¡ÚC¯ΜcÆ^µñ»6‚ü(Ýá%ÛýÅ#>¢„GðŽy÷‚›@ûï_fìŽ\$ÛJA,ŽeÔTÐB¾RMGv¤âyø®fútûî¾EÞÝl¤ã²Š#›”è?3+ãÙè'þóÓ“V[íaÇ®« C0ø÷ÁS~` >º2û÷_§1$‡»Z î  1fb¯Â€É>á‘";ŠÜÑ¢*Lÿ³§6ôÛn¤üñ1hìïî¤7îÚæCš!>HlLõ­2ˆœ>ÏÄ.#ô(ìqiâþbR]ì"²uE…Y¿¨±»WzÌÌBǦbÙ~sÙâ™{í·½1cêï ^xêéxlc¸÷_F×Q X„ôØ ¨fƒ¥K¶Iîäà—puÉ&‡l!È€gÿƒs}ä¬ÖDU.ö¤W°„‡jƒ{—[7¾ÒƒËK{RÁH_½I–­bñW¦3^C ®QªëU蔺?zu»ßDAòÚ 5mør+­«@Ì¿; BŸ˜LDwC‰A,Ž"âÃS,˜Ï„|Ù¹1•ÇÌûß"$ÌÆSfL²B:3D’JÀºT›p&u5ô} ãNèÉÒƒ¡¯ƒN`‘æ]¿LÂÑ>ÆníjŸ—";F«üòǦ¬Ýœüm—Ǹ\êÕy ~›ô NÎ=&V¶GÄ/oˆP:FUx–û¥†K‘ª4ùáêR BèÛôÖ³Äû¥Uª=æ*<6 iç¹Ô­¬\ò2„"wìó¯ªÚQ~)>Ka%±Æ' „ôØ¡XƵ¥%“éR²£4ÿ\òƒÍŽS{xõÉ" ˆïéÝi8·ŒñÉ¢[¨ß ï%;iÏXMË"<ô±lõMÛçQC©*ÌжÇ:°ÕFQÃÖOÇ^poÚ-ÅG~ŒÚûL+^LùTÌ¢­%‚à5+NÊxã¬$p]’°H­Ĺê§Ï™Ž˜ Œ0fBšµ«O¨Þ W¬)•Gߪ6$<ôçÆü?L`ôï^_À’4þ1-Ñ÷ÔAl¾l‚ø0¨AN»ƒÁk‹•ˆÔɳ6îjCð¿9 ¶ßÆ0y´áO$2Vú£[È‚xg9¢rÄ{[BÀq£R†¥†(CœS©É|Eô[@Ø/hâ#BxTÕÜádoJÇ‹lQØ-ü}ãvÉa 2ºÃ@°SŠŽ€Äˆ©=È!;¸¾{£•ý5µíq`ãa´õQ@µh›ÃÝOV6®¨)FlÙ,õAvø6IÖÂÊfEÁDÒc‚½@&ž`òL&N““þo̵•¨=¢»¹d¨?’„ƒ= Ax8“›ÜgIÝgKÌ1XbôÚ˜:¦hwkõL¡E¥Z´8†JÍ t¹=ñ rÖ¯¼Ö¤K»ø1È<ìzú÷¸Œð`/5P¸n.Y“§©1ÊŸ~™sΖ….|’É­_^™õën@‚'<Ü•;ë˜9Sy̆|#Ü&>€,òƒ¨+yÜl;;ë߯\WŠøÄù`Ë ~/$³ÌzŽ õ ôè·­z‚RﬢÁÆü‚ÉdléàðÈwvün;|wóÍŽåqé— ü;&öO@xTst»y͡ڠUµü˜Å=Œû ¤ê»ç6=¡f5cBa+#®ì잯­(´Ûj‘ÒP{iôÇŒwq´òDJªsUk7ÕÑÞ–XôÏÜ~ ìâìûQÐ׌Q<Ùi3Êrj¢l¶ðèç'd‡à†;•e}ñç2ã/{v™ÆWÙQTîĘlGŽ ßÌëሎìð~‹W">)+Z¢F\±ñâË»uLŽÒÆ~~Ú?_ô•^ŠþX+}dµôê®þnËŽ-I©¼5ÜF¥ë'oþÊî„àvkÙTƒRyF±³J¾dP9ï#;¶-Eú¥ ¬÷˹'áû%<|•Ex˜ëöˆ‡ø°·=Ç‚<@?æ<Ÿø°WÛµòÃ&>ôõâ²Scùs`z+Zþ÷.Q¸cÓàfÝËj—s½ªÿGOLc1?Hd¶gRÙñÎN3fÆÛgndL,«ý'•*¶ XŒø°¡ÚcÖÇy˜ qj@µMØPtv÷¿EqÀ¨óFªDyÎ'¶ì|–#Þ»‚#ªŽоl¨>.Žj€ªíˆ­~\]ê™æ¸§,k/ŽtáÚ–MxTÕÜ|VªqwÈ2-ñ ÿ.­«vœÐÒc‡€–F}† ¤x€Kv0çrçaBÒ"Ûh™¢L#§¦&;KJò3W¡c3'Š¿1Êâc2ÉŽð«Iý®!1äï)-üÜTøq!Œ…^¿xSa=ø½{ß0Äõ[=Ë-œ|%ÆÏ²@¤ú÷œ÷`Yb¡Ì  Ç+[a;×óã ”­zpûÛQ“aªìlµ¥Û§+Õ»Øcˆ5^ñ(y–!r\Y²]±2Õq²u:,õ¾å`»Ø™Hjf—: ÿßw™wÁÉ!=v*F¨<–!<¶TÙ±„ëÎR°Ë¨fìhæÑ­j«MxØDÇØÀ[©gÁù‰Æî­E|˜ú|Žäî<9Fm¿UÇÜpƒŒzÊŽ”½"”jµ‡³úh•ebÌÂü`e —–è–³}ÝØÝ ¦jçNžK{žÊÞÄg»ÄòôŽå’½Å.]LËbp[ Œ(áôC9*Œ{CÆüˆú}8¯# ,âbu¤†²ãÑÌL¶ÊùAA5î3³][BÁ €Xüœívì|fú눯¿);ƒðàUk3/-ÕîËÛ²]ž;±ÍS8¤\7€üàÒ8îˆÓ® 1šžI™-MHÇT)# û˜íI=O÷Yæõ_9[ËFëJfš&<¨¾¸$~ÝŸ÷Í¡üØlpí?Z§œ… ÛmÌSݺåØDµ«l›‚ðPª…ÊÙRk"lå³lé±PÍ`™Žîê`æà;%ᑳƒ:wÕ˜zçš Û_Ù¸˜AÌ'9ôg{…\“z‹X?Hž²'½É•›iÚ§;fç“A¦´+ƒ¿ò%4¶#J[)DþÎŒµó<Ù¾’<Ÿw‘s&Î7¨¬ÝM– <ôo®M0–¤.)c€iP¿§'ë9b·Ê®²NÇs²][WˆéU!1ãÅÁòÝ#}e`ßjb‘D|¦–Kx, ÎÆ°Tvý—"{F"wÌVûŒ%¢hâøª@kb˜é6®T#®-‚“Bzì4ä 0׊RL¾#Ë ± ÂÃV_ØÛ¸óÕ½fÈ ›ð¨çèöDŸ£ªçy€."9Z@å$V*Yä¤q&£î¹äG(ÿuX÷7¡î óôHƒÙ1z…N³œB#Ǩôó¢®ÞÊv ,yi3Æ©B£˜ÚBÖuÒrqR(RÀÏ7gU4‡Œ¦IÄ%ÐçSn(Ê£€ðpá«üЪGâßïTý’lôN#a+dž‰µïN÷¡dûKÎ;! Ý ÊiLEèv'>rŽsêkl…î[C&™‰`¥<ƒÁ¤d|¹Žã1ôåN\( „ë¥&†bãî¦Ù^áAŒ¿9j¢ºü1n™˜%#1ÆîYNÃ[—¶¬Ø5¢˜œ„Òc§ aÀŒ€qb…'–nÛ’Lì¨[½#¥öð]¨mi«zŽª^3„‡Þz¯C …Úö€ vjeׯ§þÉ"nJ¶ÿÝL«`” ÖˆIQ(£ïQ–á©W¼áóŽMŽó‘M|f¶k €.ÀšåÞ‚Þ†½ê*ZrüÆCŒl«©´ÅEÑ„G9ÙAäÅö{TÞƒËÉ@LØn ¾Ê£qÒº°îI~Øuì” ªÛÖrwÑîa5烂qÙâƒVY“gÕ" Êlë+>lÂÄwëGpLF|ãZ§$0Ÿí‰² ãA¤]Tú>—ugÈpgÉq×cS“L‚½ÉüØíZɬ™¸!öo>1ÒÝ3û=⟗}ܹžÚ¤*Ð&4b±žtZ;^ƒ­v žoH–m 90•Â6Alû¤Ý2..üÖÛ™çD\|»ÌVpß£Šˆ½÷ÚVí.¬Bz\¬`Ew« ÑôÈ{ÁïâM4¼‰¥3èúJ ð@ÕÉÝ…GOxÙ{?°a1”Ç] ­úp@)Vìߘ[Ù Ô 56‡—™ŒS«h¬I9®:'§|jÅn;ùÒfŨØ4ÔZC|ý3CL¹p¬/àŸI™w*%­·OËïÞ2ôÃwWµà‚Vk„äïêb—á&Oš]=ôêyÄÍÅ]qöÎçÜe¬|)â#ˆñꓼ±û6AßêOƒ:ßQE1®IÂßÄ}LÐ%ˆ^÷ä1÷Ñu%#û–’Õl»:‘8!è _ î`K\û„6•CÞ0ä”®Ÿ3¦xn.ݱ‘m?×E$Gåá¥aÇ>F™w]ÌÄDý@ìì8í•Xì`ê´tìÎö*É"“‘€¢‚í!=Ó1·‘*2ùŠ:Å¥¯§pМäþi„\ðvkÐJÞÏ¿ªú‰HO„® ½”£ö`ëæò'aÌõ°È|9÷©¹šE‡c ú˜ |Ì3·Õ;þŠgDõQ^ÎÈúÙ`ž7)_=âúw뜭†½3¨x áo qè»ËXmŽ$<ìïÔ}¢ÒòÏÉwebùhÂÃÿÜ6­•ÄÅpl˜„0„¦ÒŸ)Ò£'<<’"Wâ·ŽUCžÝ³®k“ã£+¾¼-ŽV{0„‡VyPÊ*0qiù±“QxØç&®“싸>e“&6Ãd¯#>'‡¯€aÈó;[Ñ,jÇe…!üóüÝÉb}TŽª'IRXéìcáä9á©cR.Z9¤B®½Ç¹²8v[Cf)L{ý<Û‹¬«s+B—“Ô®'ªEÕ®íúЙXŠŒ¼‹œeœ× q\ 1² Uµ Pë€:Ü0j¥t]òÕ`œvhãžÚGQßcn%šä(ª#·J?í$…ì×6Ñ—Ÿt««tì+ ¸Þ%ÈŽ.‚ð ~Oä3´(Ó·ä}—MRQÄ™>YIkθ›8ÉGË"PÀ$Ú…³­ûÏ5A´TÕ¬wCîÆ²- êžÄ%ìTÝ&">@•-œdÒCà‚`€³‚6¥:Ý”Êcä$j[‘‰à)5â19–…«.ሴØ­jíß¼6gú +¶…>7ø\í²õ߯}aºÞ­å®z:žö¯‡1?r¯>† ð!šážÛî4†øˆÉÎg†Ðªª]Àü)Ý=^<Õ>:xÖxÄG† /AB¨Ô.ÂÃΟRy8•ÈW´ÙåP(Þâ3Ü8™¥b/˜tƽ‘èõûKlOL4;ùù‡(À3c„ïþ2ƒ±ò§¸†à8û~z×»¿c   â# §üwÕ7¬#h6îÆü®ÿ‰¦}­:6”gõ•M|ôäGvÿ`ö_UïÁlv³ú4ÔÕî>ÿAmåO~Ããyˆ»¶¸uŒæ¡I t÷ás_ª>µK°ïñ¡ãS$Û•WŸ%¯3»¿Í¨OÖžIS»e)Xã3¾fQ¥$Ì2 ~™c?Gð ùÁãöÛoG]רª UUa6›á–[nÙêjíˆ{‹ D‘üž&4V©òp÷;§â)d¯Ë"ØbVï)/%&;²"ŒÌ®ó$ÀÝöµVù¾4¹—Hú®9JŠœ(à±óº/«}fþö¸@™å¸Žè c€tS»6›í–SF÷eA;'ðSÓ’Ï:§? äµÑ¾æ3/wMV>ªEÓ< j9e:ýK¼vͶÒûu®ª=ø‡Ëÿ þ—¿9 Õ¡ÿ4ÇéëlÇi×3§o­¨íeÃ:RAñíû^iuf8ý_ï0×Uû“)Q¦®¯ïšÀºç”mûÕ¼ÿ>Ô:“Ÿ]'Ÿœ[Çcg<þýËPê M&¡ž£ò°ÅûWáå¹IPçNBúëç;>¯° êIЧ‹¸L8î%7“׈ªÜ`½ÚÍPÇöˆ——rÉíkƒ¸׺) \\rÈ(.6ÇžÒ$v½Ðî-YýÙ¸ p‘ç¹îäÆêr0ѳ¥bk a‰Z(,`÷µª] „HÌ¥Ž¨_ÌmšÎƒó(0c‰]Ó-Äe'_Û=.ÉûÞ÷>\uÕUØ»w/`ïÞ½øéŸþé-®ÕÎ'h9œŽs*-ã&;åÍ!?(rÂ&9ÂC§UM¶ß(7À(Õ8“n•aÄÄÊpö_§&¬T{ðÚÉ”Ï8ðáΦe­æGîU4hXíŽþ3`‰Uúξ«1I=Kv$>‚ß(Ñᬾ9«‰³~•}PëÎÈ9Ý!>¬v™çº7½JE-pÑÕªÚƒ^12‘Ì…!à̶®}ã(²lÅXµþo}âª}bHル›sÜ&5lw„T½ zeÞöWwúH½’Ø®÷yÎî/Á³Òç6æCxôn7,ÆŒEd,áçlj(qïȞĄ$ÅG8þQD‡_¦;9ÉœÐRågìÀa¿¶ý¡Ðš¾ÔÍ´&ž€4ñ1%“cÇ®ÚÞ2Îöªúv'Rg1%Òßû§ÅTVDÚx¬˜‚wnÌŽ1±µzâ£CÝàž¹.²ÉBˆ¶½ì­gKˆ‚2qÜsÏ=xôÑGñ«¿ú«[]• !=v(² o»SôWû‚üF¨ •óÎúÄExxþßú>„á®â(­v¹ç[eMÕ¹Ó´íÑn›Üö”ZdÀµe\˜bÏÂU_uí–SDøímæþ™gXH²µS€zŽõ}ûñùŸyßúO§ ™ïÁ'þ£Põíl7ÚÙn(c°Sý¡Š‹ÉÔóSïMf¿“¥ò âú¤T¦ïhû-_— IDATÂ2öZ$d»ÎmëÃ3óÇL'ȯ3®æ¹?t_BòÒîËüUoÀí';B“KV?#JMY’ÀÐKÈûH<‹¬s'DN½cî±<üzÖ¨ëݨëݨ깹nj,ËIJzîbÉóœVGÖѶlž2Åü©…û—cŒÕþ¸?}ÊíŽŽÑÆ•©Ùùf þÊð¹Ï}×]w]ñy¥8÷Üsñ–·¼ć>ô!¼à/ÀÇ?þñ•—+ ¤ÇŒ±dw–6‹ð˜ QWn¢eÒ-?‘dó(È{Œû‡÷¢uÈ ›à0“oïw;]¶„ ÑQr}ÄŸLO`˜,C’°;Æpõ6+ ¶qNÜWV%bMÜ«0¸¬O˜ã@Ù}/Dή9Å÷™™ÀÙ;U˜¡®÷¡®÷9×_ÙìÊ FZ¡Ûîo˜¬eÜŠà‚;M“½ªÃá¤ÍÉŸ<ÞFËqÔlf"PùAuÛŒÀäûf«=ÒsxäÔ»ðÀ³ÎÀу߇cvã¾gÿò’¯ãè“NÅ+®}:Úµ]P³5¨ÙÓ–íöb“.vÝ|L±2œ¯ò áíÜT ÏgÁÝcýs1ñá’ö˜éÖ1·Ìå&òy“aý>y÷ÚŒi9ÏÚWÅ,¡òÐ(pÍX6ÿUç͹j¤‚.{ ž£šíÃlþ½˜­=©#?ªyG€$àÛÎdõœ¢ÄÇ’6×g²ÄÇT¾S€$,–lÓ'¸«Ë-·Ü‚‹/¾/ùË“ÁD¿ùÍoâ ox^ð‚à¥/})^øÂâCúPQy¿ø‹¿ˆ?øƒ?Àm·Ý†ë®»§œr ^ÿú×ãþûï_æ28±[¬`iðF{Ùjx4`ØŠT¾ñG)V¥ö°sP 4ÎgVS'X¤mhÃÅ@ Î@=ã¬ØÓÆ¸]ÏlÅ„uÁd/W9’‹uCª›øXÂvH ŠèðÓ,‹Lã†%@KT3Tõ³µƒX[;u½gˆßÚÄñ¶ª£Vƒ{d‘zòŸ¼êë§ˆàÆ›Ä X^ìïjÀŸdNü¨çlAr‡hÐç{jŽìðú÷?ûϬñ/<ˆÓî;ˆgþÏÀúž ÇOÝf¾ íl íl­SzPdµ.ß9f‘Áµ7ØRÃ|áUô®-ÄyT9jÉw'ëý¥”8Ä ¯OvÔ=©ØO^QÏ­w®öò&ª<ÃÌþŠP{8?{ýMàÚ”S¨hMQáE0Ö¤¤Þ9;NUÕkkß‹Sü4Öv= õlªj-›øÈÀyN|›à¯jw¥‹ƒ©¿x&yeÙŠÇ@ý ¶F¶û·ÀèüvâNyäüþïÿ>þüÏÿ×_}2ým·Ý†¾ð…PJá†nÀ—¿üeüáþ!Þò–·ŒŽÏñS?õSøË¿üK=zó73*A9NÜV+˜ )Â"Õ1‡2ßÄjþ”*ßÅe³Á”Ϻd¨ab;šÏžØéï°ž‰MŽxjêÙ8ÄáÚS~¤H²Ø„:<‘!–m; õÉè:'Œ’a5Õ¾w®k‡ÿY¾Üäªk¾rdÒÀÃÌûùÐKÞŽ]{.À0 J{Úu‰é·?ÍŠçæ8¯òˆ+ÂâJ ²¬â#ö½?ר†VeÌ{$¦ÿ½ÞÌöÆ•Ö÷ùÑG±ÿÑ•ªðÒO?ßç~Ô-Ьõ{ÌfØØ½Ë¨=”­$1åj»®ýça‹U‚4O­aO´ õ½=µs@’Ñ>–uã+ïèÝÀ:â°®ö žŠÙì4Ô³SQW{vòÊ«Ë2c²uÿõ_Ñé1›†{Ÿ8—”bbÈí{'P°°Y$¯[V=ûÜý¢K°k×Ù¨g§öîˆV½JêÆ…×W¼cRîÂL$M¸˜© eòbËMÚ|»Œ¶Ù1}&Ó~¶šÌÅi§†·¾õ­xï{ß‹W¿úÕÑ´>ú(^óš×`mm øÀ0Ÿwíü /ÄoþæoâꫯƟþ韚ôög†SO=ÕüwÞylÞ]tžýìgã¾ûî›äºiH S€n )u‰‘Ýï+&< @Aþ±ÌëÏ68UÓE:¯ê4¤ëîԆuôƒjÅ¢™³F uœl§eë·¾Üd þuQ«¨Ü¹TÞö}@[Ð4È2ÓÀñc²˜[Ðí0g´àËÓ=îƒwÍöÂíð»j»¼¨ûí¶êótnIœuÍý=7÷šk«lFm`()Õà´/¾OTü IS \8X.E®T:T¨ð„G7ñ®ë}€jжGáº.ŒŸ¤Ò}k‹¥ÖÌý W¦l®O\Iô±¾Nõ®ž”¨Ìƒ»òß?ÝW¨§z/¹w ªzÍ|Žvm†µã먛 ¨ªF¥жÝg[íahUÍP'4n›'Ûÿ yÄ—.¿Z’ðpÓ'ŽT;äÊÛ"ØlUÍ;Âcí €T{ EÕ,Ô}ßæ÷5b»Æ8÷D¿ó¥“óXßbµô84!qkWaªÝ>Ru'l îvú™˜[N5䩉þ5U-T{gÝr=ÖcAfíñ‹:N”ǥɽßÜÎ)±ëJI¶ˆMÇžDÛu:?ÒÎL\G ¦Íö6Aîî¹yûŸ‡î¶ß–µU5l;´gÏžhÚ~ðƒ¸ë®»pùå—cÿþýÎoo|ãñÎw¾oûÛñú׿óù¯yÍkðâ¿Ø¤Ñ$ ‡'=éIxÊSž’¬³`é!0ð 8ßPJ·ŠðГ1v-Œ"ƒ‰ ¹ãÖ‚aB–=i÷ê8LJ[×À¬jÒ çbNPƒlåÀ²MPÆ0QÇØ1ýSi LnYT”7Yy¢Ézž¹î+Íl5ùÌ 2Öø™àØñ1²¡Ë ˆ³&8žGxôÙVsÔLêÆ’9}.C¤¶keQèzÚ €9”Z`±8„æñ¿ÂŒ“îëçÇLJ“Â' VDˆuyÓJN['ÆR–øXJmŠ„«¯¾@§ÊðqÖYgáì³ÏÆ·¾õ-|æ3ŸÁ«_ýjìß¿? G8<öØcøö·¿W½êUSVY¸·XؾìÛðèê”Ty)EÊvDÖ$Tå¶443š¹0.(‹P&°îzµvÙ²§†)òyåʈa« <†V<±áÜ RåëUyÏÍ%f\qDÝ×*//KÃn„LØž°šzOï¦lÂÃíßPíQœþ/×™wH2výrüÝ·¥uË|w•ýžS¾ÝÕÌIS©U³Ñý^mK§¯çPõ.¯û®”rJ»S̲ÚpΑŠýâ«=Tl\±óhry컓3ÆzÈš0©MóðÀ_acýk} ^­ªÑäöß#̳ ևΓt}I3A>Ë…SÊðGåU:>ÄÜE¦kœ¾¶µúé…ù_©E·“UsmÜYµ +MºŸdmKò½)ïsYWï^.«òpóoÈw•sý ÎÍ*ƒ±i¦vƒÌU>Ÿ .-cpÿý÷ãßø€nç Ï{ÞóŸþô§£yÝpà ¸ôÒKñ·û·¥®¼òJ¼ë]ïÂgœ1a­1ˆÒC0œÁ75áa1à¾Ô>86e]rV±©zôÁõÇ'”–"5(Ú×à«<ºƒÞj§ pÊ´dñœ[E¦A¬|d* &“p¦&ÚýÄ”ÛXíu~9ã~õ!ó)wsáË©öP­q ²•IU…NB]€^}êeöªŸ|Û®áÁh á¡?Ú_àè‘Ï£ªæPê8L@`Jb¾ ÄÇàµLœŠ‘çªP³A•Á w’{”çF¾§)5GÔÍ(R%w–Z‘Ú£[7Ï¿eJûúöÛo7ŸÏ<óL2&,¾úÕ¯Fó:pàîºë.üÌÏü ~â'~ßÿýߟÿùŸÇË^ö²Éê+HCHAbn ›åÒbÊkzÄïäV½nìÁÒ«çF夑ö`L¸¸˜Ï&£6®ò`¯Á‘käÙëá±UdPNl—”A® Ct¿·Ó_Kñ|žn.}{sâ¨äšÜ° 3ó™"TÓÅ5AkdöYÓšsM:bgz·wòêü:²ÇÉzûHMè–B ¶êITÖ ñ<˜²ÜŒ‰¼F¶¿v¶†¯ýç ÏûR?Qªê>Ž~žÖ$Ø&>tŠ„ïªþÍ6)«ÛUhÌ1sô¥ÙK$Ùp³¢ì¢v§¢>GÛG¦ Zýk…T U-¬ êáCO8:¹¾‚Ž÷wá ƒg—€ª‹CXLÙgFb”d¹äÎ*úú°[ɪJG£®‹&<˜ y¼°%Õ ‘2—"œÄAY²ø7Qb„¸îl×¾X ÞÕoØP Eª¬Oxïzè!ó™sY9pàà»ßýn4¯ç>÷¹¸é¦›&«›`„ôØøóGDUUxþž}xþž}é øQAœŠ àÕÚ°±ª¬ ¥ËTÇ„¨Iý<(VIËI#ƒ šþ Ъ &Ê«fƒß·wmz23ú—5FOú2ÚE¸U[BNï(>º•±¡^¡z&Ùvƒ EV~rá«=–]壸ðIÕö_ j…>ÔC7™êêëÑa× @8Y%n[WŽ”Y a<ƒ¡¼D9Ùê%]TÓ)6&RÇUm‹so¨ÚC«H ùŠ:Jvtm¥! £.³ú/¿=…hƒÏ~»±nÃÉä=â][ )l¿ƒ&¦J¤ýe¥‰:XÂ#W˜…¡äþ2ùMàÔ.=–MF)—ë8å&¬°Ò±ß ‘¼œ K?£¤ÛÎ×Íô¡:Ï$ù”)‡ü²'ÀM߃›¿M»‚þŠÀ GÇ?>qx%y=zÔ|Þµk™f÷îÝ€#Gެ¤‚i!¤ÇÀÏÏ“±;fÈŒììVNvøyr.-¾ uEu±ƒ&‰0T„ ËŒ©XBw6mŽ*cä¤.×e‡3b'[½,ÁØv‘Á fÚ=§AÓ¹uØ“^Ë-ªê'ùÅ$†¯’+é^Âýª€øðIâìÊÓ*š BÂcH'<¶/´²a<ºÝ†˜@¡6ªîxéYxΠjŽŸÀxñ<*ËÝ%psºÀ¦N% bØ$ŸY^ôq{aWm—o›¼4…`&ŸÐ£žµjê¤qâ# ®O7ýc± Þ@¸î)Sƒ!$ÆNªm‚"8ŸRšy°©[Ry,…-ªŒ";¸4Œò£8¿ÌEU«U£ÁPM¢xV¼½kì.Øÿ4<¶qÿýÐÍ]}VèÝòŸ÷ž‚¶s×U‹=òÀ$yïÝ»×|^,$ñ±XtÄß¾} Ê‚-‡;S“yÚÆHöîñQPf6r'n¤Ú¢{cÜÂ4³¬¨»K„r!p~ó‹×õäÏ”»Œ·âeŒ`G¦N]\Þ3MtÄ&àí…Ûý ˆÕBM@½û`bX˜“jtÒn½¥ðÌr'2Ù–J¨Wfœ[¯PñÀÈíÃ<¨{b­\z¿'wÈhwÓM¨†xãâ£x÷Ün§1âœÓÎ žñu jŽ¡jf[Z©{ÔÊK×m]Ûºß!¾0Î=NÅé~§&ž¹“Q·–´Êƒ-#¦,Ø*7ËÈ­>=L¥ ‹í*ã÷)“#ÜE²ˆ"ß|÷Oú^§ú{–ðÈÈ›ÛVóŽÇ®5·YãZéX6ÆÕ†ÃŠb^(Y<˜blsM“+Çc­ª°fmA;%¿üÔ§>Õ|>|ø0<¤9|¸S™È¶³'„ôØ©˜Z^›Å‚§WÒ’ˆG|tç,Ùù–ø/zî4ÚB\ ‡±¾Û„´4w‹3Ò˜ðVëœãl¥FŒ& CkSÑ:qíÈŸ„û÷hƒ…#”ãÃsq鶇ÔÄÐìÞÄÍ'–ˆv5—”H#Óhsˆ9‚¬ ›öé’«ÈžA¾M'™>ò·­¦ N’„Š¢';”õY°ï´óý¨ÚMddK·+ËÌTjUÕ¦Y;ÁRµòÀùVÙGÄŽã#¥ZºíL4xBjÀ¼ƒ̃¢]ârÔaì <ìãæþQ1Z‚:Ä%«ý¾MB¶‰Bâ#èÃ#+ö,iQxLBxè¾ò•¯àk_ûIlè^d–ÛCŸ$Ø–àö4“Eç%ÔÉÈÔ]d`/]®vðëR< –ºˆd¬ºÅþÈ,áNR†>ü3·Ë©f¨´ë‡~^úX$U¯¦–¨<ØÒøþOi½Z€Ün¸ÅÄü¶í?®úaJ²'~}½À€Á½@ÿ墪fæÏ>¶2èç;ê\«ß0÷€»ÖÆ9úì¿§aßÓßKµŽÁÍ¥úvÕà‡?}.¼îh7ÙU­›¿Gx•‡þ«*ç¯Í†ßêÚQ…8[ÝÃ{_ê=p\prŸaßo8D9¨q)16ÜÏ,¨ œ§€K#ËŒ½N¿Oä–Y œ ¿_éf”Û÷ÏAßDçþì~^ä8±MÜFa‹HzÚÝ·L±jÁ{á-ÚÙcáçûËËPÆ ä“¿Ï|Ýë^øÂ¾üvß}÷áÎ;ïÄÁƒñÊW¾r³«&¸7ŒÆ™dDz+Ë,&žìèz:÷Kf’ÛL2¨†9UŽ?!6ÖÜüQ“æ¡ì†œ,p3y®úI”Ù¢Ó7¤KÉ“)0*ÎBKCÃö½M¿²èÿô;3H²».yÁµÞó |¾ƒø.#”7Ëbd>dRðH¬ïðÇN Ÿì(|×sßmŸ v³1ðïCã}Ö¤µ&>zòC­w$œZtÇÚuTÍ1ÔGÙ ¦áaÔ•!7Úµìhg3ç7}€¼Iq5CÄuæþ_éw…R6Ø$ÇÐGóÈ|Žþ»6ñds’÷0póˆ¸uXiêšLÌ=/‹¬Î%;Љ§hž›7!#‰ õÉh¿·É j,öÇlûÙDÆ‚ Ö‚Àêe…Ü÷-‡ð(É·„¼{mÙçæÛ·{úÈCøóG.:ï‰'ž´Ìn3—_~9N?ýt\{íµÎn.pÍ5× m[\yå•NÐSÁöÅ Ú3 ¶+PwŒÎ«¤<ÖƒXE Oٱ٫‡.ùÂø˜Z†•o\ÙÆT¼ ë§–‚? ³M|°UÛN+¶QƒÂS{xõ.YÕ£&(ŽAlæIÎÙ[; 7|PϧĶaú~úˆÛwÔÖ]¯l5 ÷<—}Ø85ƒÚÃßž·C¦ÚÃ|÷•g6ñ±îÍ @j×»?Õtn'Šª‹U¼QsTh׆ÿõg£±ˆÛ5ÆuçjPmÚclyYÈPÃÐAL P#›– j7Á½cêɉK¸ú“ür‚ƒCœˆ¡ÞáeÆëR’…'XÛÁ'¤½>#/ìÅÿo;ÅñCb‚jÿS¿IE±ù¼IÏÊRè‘Üi…ćû»·AÅËIÕk‚¿çïÞ…ÿý´Óðó§–ºK>ø ¾øÅ/¾ô¥/acc#HsðàA|øÃÆã?Ž+®¸ÂìÖrã7â=ïy.¹ä¼ímoË.S°µÒc'"5iÙž(5bÇäǸ:ÅåÁÅ ßÏÚZ±«ªªzîþõÇ´1–“ç(Ø$qܬ0Rêb†³BëüeW9“ÐAH+ÕºIíºÖT]¡Y«ÑôÄG÷7(>8*îŽ1Ë*Í{NäS¢¤­¶¿É&lj”ÚƒJsE,˜qùn*‰ÄMX È ¢ÃŒ¿æÏ';b¿ù¿/i¿¬›¨ˆ½+£Þ£`Lç¶0™J2wò_’ÿSLõA\ÿ‰Œ¦ipÁà™Ï|&:øìg?‹3Î8—^ziþâ‹/Æõ×_#GŽà%/y ~ôG¿ök¿†w½ë]¸îºë0›m³wMÀB™ òq±êî÷¼NjÊà]ºN¹Q¾í_,½½ƒ ·íqmVš%Ú>ú`‚}^Ì»ìáËx¢ÇùÎíÀR˜ÿ¶R|dÃ];˜ÞÅ„ tGî2PÕÖîéT'P¬¾ßþ»“»Ú:Öà[1I¢•5K½×+2úØXT TÖ;ižQw}@Í. ¬ÞY÷ ýy†ôjç3àº4ú÷³n‡Ä³âqèãu…¶®Ð®uÿ@Ý*T-°¶Þ ]›¡Þh êUcÝÓjËç$ú~²ë7—í³k jû³]^mÚÐtT€Ó€ß޼ Ÿ#u¬TÛ v®ážÍˆòù€ª¶qÉçÓé4Ê® çöSÃ_|è@L– ˆ¿aœpëPrý±6uÌiïÖû²ÕHíD’:w|ÁŒý´ö3ó¾Eû Àœã¤ólÌíÐ÷Å0›ÍpÓM7sþùçãÚk¯]Q›!=KcU„G1A®¨”Án%¾:ÂtòáuêßN[ÒìùuF#ÝLd>¨K>ááFŸª†Âb˜çØuµ —¥ÙþØ5©~çMæX×µ%âƒüˆ9Ž¹Âº°}߇]L¨3}„y7õ3¤ÞCr…¿šˆQ»•]Ò0åÚ ÞùeI³µpëGÌŽ-4:"ÃÙò·h ÛܶžÒÄ!E,c™":L²^áÑ®UX쪠t×V˜¯+4k5fÝn.J)¨¶¹ÓÕ¬ŸÜ­ƒÛ¹¥›k²ÇïÏ7]Éö¸©‰œ÷{@dôë+[U-$8â;J‚ö×¶Ô–½\§È{$ØI~¬=ø®tááØ~¼‹äæ®Õìê­€eˆmÏßUj«°J˜Ø•×&fb{†oi"ÃÖdn‚0‘Gº ¨¶…¢ –•`3wŠœxØ^ºcÁöÓ1o©Âc„‡>VU56”—>†¾åä£áaˆï3åÖÁEùvòb®WƒZ1HÐÆèrÝ)ªjF$1ä®ÄÂ| ÷Ï@ >o³‰ì4Fžko´ ŸýîMØhœgC>³yÐ.)D]aì6kIÈ)¹×Ž9—2âdhAÅ@¡Òر>ü`±¼ )]ÏM3à áp;_ïCåKŸ·Ñ<¿¿ëO±Ñ¦Ý\œzõñ:t*ºB³,vÇö),vÍZeH‘¥PÍ€j0; U}ªõC¢Í9®Zùí3ëÝJ”—‹)_ÊG“Ú:ØrÆn]vß ¿ÄFÛàóÞ–)ÛO½ÝyHŒ¹—ÄÂ3)xî\ߊ£šµãv1£úfûú»¼¶Ö´_M¿?-RïÄ¢ÝÀ=„v¦¸–ø¶X4Ô:>5rÜzé9ûžé;¨ôÛDÅ#Ä ¤‡`["©òX’ðˆn«×£E…|â0šs#<Šâo8ä†e$¥ˆn°õVEï&Â×Çs}É!>¨l˜{œÚ~@`4”ú~çb4õn‚{ 2ë¶÷î™o@ás÷߈ (ë¹äM‚À£Ü˜bP‡ y3ú y*Ÿ 3 à/0¤Brp ’(¡ÈÚ•‘>†ëãRí1+ÞGfZù_©íŸþÎÿìŒþ%ß»¶îzÖ#8ÿw~ªî¾+îÖM”{Ò£ÞÓýïM¸É86æ=`ÈW»*¤ë1É%¾sýej\˜Šì ò ö},܉ùÞÙñkH§üòÈîœtÕ *ü¿}­k•9}‡J}RhîRJI.iÆ$ŸÞÕ‡!<ì>;‹é¾9$~"m6΂CCO$„V„±»˜pÇ9‘û ÔIDAT¥ð¥ÇïA£Tž/I4ä÷U¸µäïþUF|ñ{›<_ Ø.ÒCÅmÇùˆù›áÖrÛñãD‚å ²ú„‰³…*GxØ*ìàl—SÕ¸ùñûˆ4ćuÜ””eØ+ßnzìnÖÈϽnP{ t×ã–Ãß WR6Qåqóáû³ÓròoäL6¼I©´ðƒ:ÄDäžzJ sÌú³íù†{.§ä H€PÉ!E<üÓÃwt·-é@„ÊÃ!D2 ´þz2 ‹ üë¼Å;²=|¥+íZiä®mgÜ}nüÝÿ¨[ jªíUt-õ‹‡>šxg î¸ðYøÄÿ¹Uµüv¶q8“Æþ=ú§¿J§-™Df(õ8Üøè¿ŸC©âㆇïtÉ ݇QïtŠø æý‰¨)¨¼J _ozìî¾^5‡}8²«–o°u¶o¯ô}ê ÿ«KxD¯Å³;”é{ÓcßI—›ªVÆûÇmÃzÓã÷²¿mÙʨ2tÙ±ïA>©rü<¼rKÞÁ[Ž<˜¶8È=ïþÜrä÷ç11Ü(U̪ÿBzìTd _‰›¯¬{ÒêUÜŠ6QŽC€ØrÒ\ÂCY[Ff ‚7þn¼îV¾æ£mØeØÊýÙ!>B—›{£'ÛÍ…A@|ä•ÿ–£îžë+'<¼çuË‘xR È^Ù°'åúhe¿ ͺ™ˆ°­+‰¡ëï7>òÍh:–¡¶U´ ‘Ì­‘oìI‰û¾³Ëš#2•,”b`€?q¾_ÿàmÑúEÕ)«6þ,õUl÷ #8ö­°ÿÑ5ì:¬­+Ì6ZT­BÕúïÔPï/ú(‘›nóý=Uëxî?ÜŽŸüà1ó{>±MŒ'Öýÿ§‡¿Ê>;7’§î©öðû—›ýw¶æcöaŒÚÀ6ééýæ fOĈÊEż?!0Æm"­uìï7?öO-D³䇫ÞÌ_€¹áá;‚š‚‰ÓïPć•4¥R2etåÝüø¡Ñ*)pKÁ¢‹ IžŒUe0n-\þ1øöO ¹ÄGÒŽêÛEº$·Þ"µÇmëëø‡à#‡lIù‚Èô$†è³ÎuB¬\M9Ÿ+.«TU3§,S>ÐDÖo•ÙÊ¢~·¾£C¯êðš«s*¬«.ÿuÕº\€UNgXT¨ú­ *¥€ ÝwÕ¶Ë:AxMg (e%ÃI­R8ÞRî*Ô€Ù¸õCÓׯ/£RÝìfõˆßÅ¥»¾VÇ5ab»lôÏaŒL“3º)˜ëÇêÎPÐ÷Î:^©¡|'ô@ã”õÌ6¬{¹½½`w¯[TÕ€¶K^Õ@¥:Yuq;ºgÔ‘úº7ñ—qŸ;—†!¦…ù_ÑdK刱þúí]5Ð:íÓ5ì‡Ø7t=í{¯“I¥ŽmsÚ&XØ–b{»h úª­R8Ößÿ±Rí®¾:ïš=fÚŒYíÞ@«ZkŽ{ï§çÃÈæ}K§Õªœ«¬sÌÿ}ÚãMçË~lãñáxµf&ªÊLPõÖ³s¨¦†š­¡Y›£U56fkØØ¨°¨]G»T£°ØhÑô¤GÝ´¨› TÍêv£#QÚ¾ÿiplãpßžt”ÝNÝáþÿº§ôV» ”êòêŽu[ù*µè[˜c€îßÖ;~‡ôówÝ¥DÛ « *%¡nOZÜ}÷Ý8묳¶º@ @°R<õ©OÅ·¿ýmìÙ³g««"ØfÒã$FÛ¶8tèöïß?¨"@ `PJáþûïÇ÷~ï÷¢®77’®]»„ðÒC @ ÁI d*@ ऄ@ @ NJé!'9>÷¹Ïáºë®ÛêjÁäþM )é!Xøæ7¿‰7¼á xÁ ^€—¾ô¥xá _ˆ}èCEy4Mƒ÷½ï}xþóŸ½{÷âÀ¸è¢‹ðÉO~rEµœL˜¢ jÜrË-¸øâ‹ñò—¿·ÜrËÄ5œh˜ªmMÙF; Ò¿ ¶ bß ;J (¥”ºõÖ[ÕÔ/üÂ/¨õõu¥”R_üâÕ)§œ¢~åW~%+¶mÕk_ûZ@­­­©ªÛ¼Üü½ÿýï_å%NpLÑ•Rêá‡V¿÷{¿§®¼òJuàÀ@½ãïXQ­'¦j[Så#ØyþM°UûN é!(¥yäuÖYg©ƒªÇ{Ìùíï|§ ®¹æšd>W]u•zîsŸ«>ÿùÏ«Åb¡Ž9¢>ùÉOª³Ï>[P»wïVwß}÷Š®Bp"cª6¨Tgœi\vÙe2)Øá˜ªmMÙF; Ò¿ ¶ bß ¥”÷À?øAÜu×]øÙŸýYìß¿ßùío|#àío;‹E4Ÿ?ú£?§>õ)üØýÖÖÖ°oß>\rÉ%¸îºë0ŸÏqüøq|êSŸZÙuN\LÕ ª*óYö«LÕ¶¦l£‚éß[±ï 1=ÀÕW_ ¸è¢‹‚ßÎ:ë,œ}öÙøÎw¾ƒÏ|æ3l_ÿú×ñÊW¾ÏzÖ³‚ßÎ;ï<\pÁ€|p’: N.LÑ Sµ-i£‚±¶#Ø*ˆ}'!=ÜÿýøÆ7¾8÷ÜsÉ4Ï{ÞóŸþô§Ù|Î>ûl¼ãï`Æ3žxæ3Ÿ9¶ª‚“SµAÀÇTmKÚ¨`,¤í¶ bß µ­®€@°Õ¸ýöÛÍç3Ï<“LsÆg¾úÕ¯²ùÌçsÌçsö÷{ï½»wïÆ+^ñŠ‘5œ¬˜ª >¦j[ÒFc!mG°UûN hˆÒC°ãñÐC™Ï¾¿§ÆßýîwG•qüøqÜvÛmxÃÞ€'?ùÉ£òœ¼ØŒ6(Ø™˜ªmIŒ…´ÁVAì;@ !¤‡`ÇãèÑ£æó®]»È4»wï9rdTõW…ù|Ž÷¼ç=£ÎœÜØŒ6(Ø™˜ªmIŒ…´ÁVAì;@ !¤‡`ÇcïÞ½æ3½[ß·o_qþGŽÁoÿöoã#ùˆ¬H¬º v.¦j[ÒFc!mG°UûN hé!8áñÁ~UUÿýÖoýà©O}ªÉëðáÃdúøSžò”âú]qÅxë[ߊÿñqu‚Û½ v.¦j[ÒFc!mG°UûN hH SÁ '=éIøÁüÁâó¾ïû¾pÎ9ç ª*(¥pÏ=÷ààÁƒAÚ{î¹@·5Y ~÷w?ð?€Ë/¿¼¸~‚Û¹ v6¦j[ÒFc!mG°UûN hé!8áqÙe—á²Ë.}þÁƒqþùçã+_ù ¾öµ¯‘ŸŽþ²—½,;ß«¯¾>ú(þÛûo£ë&81°]Û @0UÛ’6* i;‚­‚Øw@CÜ[¯{Ýë_øÂ‚ßî»ï>Üyç8xð ^ùÊWfå÷±} 7Þx#; ~âŸ_YÁI‰©Û @ 1UÛ’6* i;‚­‚ØwÒC \~ùå8ýôÓqíµ×:Ѿàšk®AÛ¶¸òÊ+MP¬¦iðs?÷s¸ð qèÐ!'ý§>õ)|üãÇ>ð œ#GŽàw~çwpï½÷®îb'$¦lƒ6žxâ @Û¶««¼`[cª¶Uš@ !ý›`« ö@ (@ ”RêïþîïÔÞ½{Õ/ÿò/«õõu¥”R7Üpƒ:pà€ºä’KÔÆÆ†I{ã7* €zï{ßkŽÿÃ?üƒÚ»w¯šÍfäuÊ)§¨Ç|Ó¯O°ý1E´ñÀ¨3Î8CP/ùËÕb±Ø”ël?LÕ¶JòlHÿ&Ø*ˆ}'*¥”Ú ²E ØŽøçþg¼ûÝïÆ7¾ñ ìÛ·GŽÁ/ýÒ/áŠ+®Àl63éŽ=Š‹.º‡Âßÿýßã¼óÎÃwÞ‰½èExì±Ç¢e¼ñoÄÿñ¯úR'(–iƒMÓàÅ/~1î¸ã9rÄ?ýôÓqá…â¯ÿú¯7õšÛS´­’|Ò¿ ¶ bß ;Bz@ @ 8)!1=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ @ œ”ÒC @ ÁI !=@ ØÁ¸æškpÎ9ç ª*TU…ýû÷ã7~ã7ÌïùÈG°gÏTU…½{÷âÍo~óÖV   •RJmu%@ l-Þüæ7ã}ï{ÞûÞ÷âÊ+¯t~û“?ù¼ç=ïÁç?ÿyœyæ™[TC@ ‚rˆÒC Þô¦7¡®k|ìc ~{ðÁñîw¿[@ '„ô@€g>󙏸â‹qà 7àÖ[o5Ç•R¸îºëp饗naí@ ÆAH@ €_ÿõ_\uÕUæØg?ûYüÈüæóùVUK `4$¦‡@ @Ó4xÖ³ž…Gy‡Âþýûñº×½ÿõ¿þÿíÜ!nbQ†á?¥ ¨D@H$xl%A€`4l€`jêfx< µ(ö@H°@‚ 0jšTÓä?Ï#¯úäÍ›sί¨×ëEÏøkNzQ*•âåå%öû}Ìf³Øn·±ßïàf9é|Y¯×Q­V£ÙlÆ`0ˆF£ý~¿èYWy,zðÿ¨T*Ñétb>ŸÇñxŒÕjUô$€«¹Þ|óçAÓ^¯çSহÞ|s>Ÿ£Z­ÆÇÇGÔjµ¢ç\ÍIà›Íf­VKðnžèwìóó3‹E‡¯oÓé4†Ãa«~†èwl:ÆóósL&“ˆˆØívñþþÝn·àeÿNô€;öôôår9ÚívÇFñöö~€Ûç!S¸c—Ë%Æãq,—Ë8NñúúN§èY?BôRrvHIôR=€”D %ÑHIôR=€”D %ÑHIôR=€”D %ÑHIôR=€”D %ÑHIôR=€”D %ÑHIôR=€”D %ÑHIôR=€”D %ÑHIôR=€”D %ÑHé7êØJäùd¿oIEND®B`‚yt-project-yt-f043ac8/doc/source/reference/api/000077500000000000000000000000001510711153200214265ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/reference/api/api.rst000066400000000000000000000730751510711153200227450ustar00rootroot00000000000000.. _api-reference: API Reference ============= Plots and the Plotting Interface -------------------------------- SlicePlot and ProjectionPlot ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autosummary:: ~yt.visualization.plot_window.SlicePlot ~yt.visualization.plot_window.AxisAlignedSlicePlot ~yt.visualization.plot_window.OffAxisSlicePlot ~yt.visualization.plot_window.ProjectionPlot ~yt.visualization.plot_window.AxisAlignedProjectionPlot ~yt.visualization.plot_window.OffAxisProjectionPlot ~yt.visualization.plot_window.WindowPlotMPL ~yt.visualization.plot_window.PlotWindow ~yt.visualization.plot_window.plot_2d ProfilePlot and PhasePlot ^^^^^^^^^^^^^^^^^^^^^^^^^ .. autosummary:: ~yt.visualization.profile_plotter.ProfilePlot ~yt.visualization.profile_plotter.PhasePlot ~yt.visualization.profile_plotter.PhasePlotMPL Particle Plots ^^^^^^^^^^^^^^ .. autosummary:: ~yt.visualization.particle_plots.ParticleProjectionPlot ~yt.visualization.particle_plots.ParticlePhasePlot ~yt.visualization.particle_plots.ParticlePlot Fixed Resolution Pixelization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autosummary:: ~yt.visualization.fixed_resolution.FixedResolutionBuffer ~yt.visualization.fixed_resolution.ParticleImageBuffer ~yt.visualization.fixed_resolution.CylindricalFixedResolutionBuffer ~yt.visualization.fixed_resolution.OffAxisProjectionFixedResolutionBuffer Writing FITS images ^^^^^^^^^^^^^^^^^^^ .. autosummary:: ~yt.visualization.fits_image.FITSImageData ~yt.visualization.fits_image.FITSSlice ~yt.visualization.fits_image.FITSProjection ~yt.visualization.fits_image.FITSOffAxisSlice ~yt.visualization.fits_image.FITSOffAxisProjection ~yt.visualization.fits_image.FITSParticleProjection Data Sources ------------ .. _physical-object-api: Physical Objects ^^^^^^^^^^^^^^^^ These are the objects that act as physical selections of data, describing a region in space. These are not typically addressed directly; see :ref:`available-objects` for more information. Base Classes ++++++++++++ These will almost never need to be instantiated on their own. .. autosummary:: ~yt.data_objects.data_containers.YTDataContainer ~yt.data_objects.selection_objects.data_selection_objects.YTSelectionContainer ~yt.data_objects.selection_objects.data_selection_objects.YTSelectionContainer0D ~yt.data_objects.selection_objects.data_selection_objects.YTSelectionContainer1D ~yt.data_objects.selection_objects.data_selection_objects.YTSelectionContainer2D ~yt.data_objects.selection_objects.data_selection_objects.YTSelectionContainer3D Selection Objects +++++++++++++++++ These objects are defined by some selection method or mechanism. Most are geometric. .. autosummary:: ~yt.data_objects.selection_objects.point.YTPoint ~yt.data_objects.selection_objects.ray.YTOrthoRay ~yt.data_objects.selection_objects.ray.YTRay ~yt.data_objects.selection_objects.slices.YTSlice ~yt.data_objects.selection_objects.slices.YTCuttingPlane ~yt.data_objects.selection_objects.disk.YTDisk ~yt.data_objects.selection_objects.region.YTRegion ~yt.data_objects.selection_objects.object_collection.YTDataCollection ~yt.data_objects.selection_objects.spheroids.YTSphere ~yt.data_objects.selection_objects.spheroids.YTEllipsoid ~yt.data_objects.selection_objects.cut_region.YTCutRegion ~yt.data_objects.index_subobjects.grid_patch.AMRGridPatch ~yt.data_objects.index_subobjects.octree_subset.OctreeSubset ~yt.data_objects.index_subobjects.particle_container.ParticleContainer ~yt.data_objects.index_subobjects.unstructured_mesh.UnstructuredMesh ~yt.data_objects.index_subobjects.unstructured_mesh.SemiStructuredMesh Construction Objects ++++++++++++++++++++ These objects typically require some effort to build. Often this means integrating through the simulation in some way, or creating some large or expensive set of intermediate data. .. autosummary:: ~yt.data_objects.construction_data_containers.YTStreamline ~yt.data_objects.construction_data_containers.YTQuadTreeProj ~yt.data_objects.construction_data_containers.YTCoveringGrid ~yt.data_objects.construction_data_containers.YTArbitraryGrid ~yt.data_objects.construction_data_containers.YTSmoothedCoveringGrid ~yt.data_objects.construction_data_containers.YTSurface Time Series Objects ^^^^^^^^^^^^^^^^^^^ These are objects that either contain and represent or operate on series of datasets. .. autosummary:: ~yt.data_objects.time_series.DatasetSeries ~yt.data_objects.time_series.DatasetSeriesObject ~yt.data_objects.time_series.SimulationTimeSeries ~yt.data_objects.time_series.TimeSeriesQuantitiesContainer ~yt.data_objects.time_series.AnalysisTaskProxy ~yt.data_objects.particle_trajectories.ParticleTrajectories Geometry Handlers ----------------- These objects generate an "index" into multiresolution data. .. autosummary:: ~yt.geometry.geometry_handler.Index ~yt.geometry.grid_geometry_handler.GridIndex ~yt.geometry.oct_geometry_handler.OctreeIndex ~yt.geometry.particle_geometry_handler.ParticleIndex ~yt.geometry.unstructured_mesh_handler.UnstructuredIndex Units ----- yt's symbolic unit handling system is now based on the external library unyt. In complement, Dataset objects support the following methods to build arrays and scalars with physical dimensions. .. autosummary:: yt.data_objects.static_output.Dataset.arr yt.data_objects.static_output.Dataset.quan Frontends --------- .. autosummary:: AMRVAC ^^^^^^ .. autosummary:: ~yt.frontends.amrvac.data_structures.AMRVACGrid ~yt.frontends.amrvac.data_structures.AMRVACHierarchy ~yt.frontends.amrvac.data_structures.AMRVACDataset ~yt.frontends.amrvac.fields.AMRVACFieldInfo ~yt.frontends.amrvac.io.AMRVACIOHandler ~yt.frontends.amrvac.io.read_amrvac_namelist ARTIO ^^^^^ .. autosummary:: ~yt.frontends.artio.data_structures.ARTIOIndex ~yt.frontends.artio.data_structures.ARTIOOctreeSubset ~yt.frontends.artio.data_structures.ARTIORootMeshSubset ~yt.frontends.artio.data_structures.ARTIODataset ~yt.frontends.artio.definitions.ARTIOconstants ~yt.frontends.artio.fields.ARTIOFieldInfo ~yt.frontends.artio.io.IOHandlerARTIO Athena ^^^^^^ .. autosummary:: ~yt.frontends.athena.data_structures.AthenaGrid ~yt.frontends.athena.data_structures.AthenaHierarchy ~yt.frontends.athena.data_structures.AthenaDataset ~yt.frontends.athena.fields.AthenaFieldInfo ~yt.frontends.athena.io.IOHandlerAthena AMReX/Boxlib ^^^^^^^^^^^^ .. autosummary:: ~yt.frontends.amrex.data_structures.BoxlibGrid ~yt.frontends.amrex.data_structures.BoxlibHierarchy ~yt.frontends.amrex.data_structures.BoxlibDataset ~yt.frontends.amrex.data_structures.CastroDataset ~yt.frontends.amrex.data_structures.MaestroDataset ~yt.frontends.amrex.data_structures.NyxHierarchy ~yt.frontends.amrex.data_structures.NyxDataset ~yt.frontends.amrex.data_structures.OrionHierarchy ~yt.frontends.amrex.data_structures.OrionDataset ~yt.frontends.amrex.fields.BoxlibFieldInfo ~yt.frontends.amrex.io.IOHandlerBoxlib ~yt.frontends.amrex.io.IOHandlerOrion CfRadial ^^^^^^^^ .. autosummary:: ~yt.frontends.cf_radial.data_structures.CFRadialGrid ~yt.frontends.cf_radial.data_structures.CFRadialHierarchy ~yt.frontends.cf_radial.data_structures.CFRadialDataset ~yt.frontends.cf_radial.fields.CFRadialFieldInfo ~yt.frontends.cf_radial.io.CFRadialIOHandler Chombo ^^^^^^ .. autosummary:: ~yt.frontends.chombo.data_structures.ChomboGrid ~yt.frontends.chombo.data_structures.ChomboHierarchy ~yt.frontends.chombo.data_structures.ChomboDataset ~yt.frontends.chombo.data_structures.Orion2Hierarchy ~yt.frontends.chombo.data_structures.Orion2Dataset ~yt.frontends.chombo.io.IOHandlerChomboHDF5 ~yt.frontends.chombo.io.IOHandlerOrion2HDF5 Enzo ^^^^ .. autosummary:: ~yt.frontends.enzo.answer_testing_support.ShockTubeTest ~yt.frontends.enzo.data_structures.EnzoGrid ~yt.frontends.enzo.data_structures.EnzoGridGZ ~yt.frontends.enzo.data_structures.EnzoGridInMemory ~yt.frontends.enzo.data_structures.EnzoHierarchy1D ~yt.frontends.enzo.data_structures.EnzoHierarchy2D ~yt.frontends.enzo.data_structures.EnzoHierarchy ~yt.frontends.enzo.data_structures.EnzoHierarchyInMemory ~yt.frontends.enzo.data_structures.EnzoDatasetInMemory ~yt.frontends.enzo.data_structures.EnzoDataset ~yt.frontends.enzo.fields.EnzoFieldInfo ~yt.frontends.enzo.io.IOHandlerInMemory ~yt.frontends.enzo.io.IOHandlerPacked1D ~yt.frontends.enzo.io.IOHandlerPacked2D ~yt.frontends.enzo.io.IOHandlerPackedHDF5 ~yt.frontends.enzo.io.IOHandlerPackedHDF5GhostZones ~yt.frontends.enzo.simulation_handling.EnzoCosmology ~yt.frontends.enzo.simulation_handling.EnzoSimulation FITS ^^^^ .. autosummary:: ~yt.frontends.fits.data_structures.FITSGrid ~yt.frontends.fits.data_structures.FITSHierarchy ~yt.frontends.fits.data_structures.FITSDataset ~yt.frontends.fits.fields.FITSFieldInfo ~yt.frontends.fits.io.IOHandlerFITS FLASH ^^^^^ .. autosummary:: ~yt.frontends.flash.data_structures.FLASHGrid ~yt.frontends.flash.data_structures.FLASHHierarchy ~yt.frontends.flash.data_structures.FLASHDataset ~yt.frontends.flash.fields.FLASHFieldInfo ~yt.frontends.flash.io.IOHandlerFLASH GDF ^^^ .. autosummary:: ~yt.frontends.gdf.data_structures.GDFGrid ~yt.frontends.gdf.data_structures.GDFHierarchy ~yt.frontends.gdf.data_structures.GDFDataset ~yt.frontends.gdf.io.IOHandlerGDFHDF5 Halo Catalogs ^^^^^^^^^^^^^ .. autosummary:: ~yt.frontends.ahf.data_structures.AHFHalosDataset ~yt.frontends.ahf.fields.AHFHalosFieldInfo ~yt.frontends.ahf.io.IOHandlerAHFHalos ~yt.frontends.gadget_fof.data_structures.GadgetFOFDataset ~yt.frontends.gadget_fof.data_structures.GadgetFOFHDF5File ~yt.frontends.gadget_fof.data_structures.GadgetFOFHaloDataset ~yt.frontends.gadget_fof.io.IOHandlerGadgetFOFHDF5 ~yt.frontends.gadget_fof.io.IOHandlerGadgetFOFHaloHDF5 ~yt.frontends.gadget_fof.fields.GadgetFOFFieldInfo ~yt.frontends.gadget_fof.fields.GadgetFOFHaloFieldInfo ~yt.frontends.halo_catalog.data_structures.YTHaloCatalogFile ~yt.frontends.halo_catalog.data_structures.YTHaloCatalogDataset ~yt.frontends.halo_catalog.fields.YTHaloCatalogFieldInfo ~yt.frontends.halo_catalog.io.IOHandlerYTHaloCatalog ~yt.frontends.owls_subfind.data_structures.OWLSSubfindParticleIndex ~yt.frontends.owls_subfind.data_structures.OWLSSubfindHDF5File ~yt.frontends.owls_subfind.data_structures.OWLSSubfindDataset ~yt.frontends.owls_subfind.fields.OWLSSubfindFieldInfo ~yt.frontends.owls_subfind.io.IOHandlerOWLSSubfindHDF5 ~yt.frontends.rockstar.data_structures.RockstarBinaryFile ~yt.frontends.rockstar.data_structures.RockstarDataset ~yt.frontends.rockstar.fields.RockstarFieldInfo ~yt.frontends.rockstar.io.IOHandlerRockstarBinary MOAB ^^^^ .. autosummary:: ~yt.frontends.moab.data_structures.MoabHex8Hierarchy ~yt.frontends.moab.data_structures.MoabHex8Mesh ~yt.frontends.moab.data_structures.MoabHex8Dataset ~yt.frontends.moab.data_structures.PyneHex8Mesh ~yt.frontends.moab.data_structures.PyneMeshHex8Hierarchy ~yt.frontends.moab.data_structures.PyneMoabHex8Dataset ~yt.frontends.moab.io.IOHandlerMoabH5MHex8 ~yt.frontends.moab.io.IOHandlerMoabPyneHex8 OpenPMD ^^^^^^^ .. autosummary:: ~yt.frontends.open_pmd.data_structures.OpenPMDGrid ~yt.frontends.open_pmd.data_structures.OpenPMDHierarchy ~yt.frontends.open_pmd.data_structures.OpenPMDDataset ~yt.frontends.open_pmd.fields.OpenPMDFieldInfo ~yt.frontends.open_pmd.io.IOHandlerOpenPMDHDF5 ~yt.frontends.open_pmd.misc.parse_unit_dimension ~yt.frontends.open_pmd.misc.is_const_component ~yt.frontends.open_pmd.misc.get_component RAMSES ^^^^^^ .. autosummary:: ~yt.frontends.ramses.data_structures.RAMSESDomainFile ~yt.frontends.ramses.data_structures.RAMSESDomainSubset ~yt.frontends.ramses.data_structures.RAMSESIndex ~yt.frontends.ramses.data_structures.RAMSESDataset ~yt.frontends.ramses.fields.RAMSESFieldInfo ~yt.frontends.ramses.io.IOHandlerRAMSES SPH and Particle Codes ^^^^^^^^^^^^^^^^^^^^^^ .. autosummary:: ~yt.frontends.gadget.data_structures.GadgetBinaryFile ~yt.frontends.gadget.data_structures.GadgetHDF5Dataset ~yt.frontends.gadget.data_structures.GadgetDataset ~yt.frontends.http_stream.data_structures.HTTPParticleFile ~yt.frontends.http_stream.data_structures.HTTPStreamDataset ~yt.frontends.owls.data_structures.OWLSDataset ~yt.frontends.sph.data_structures.ParticleDataset ~yt.frontends.tipsy.data_structures.TipsyFile ~yt.frontends.tipsy.data_structures.TipsyDataset ~yt.frontends.sph.fields.SPHFieldInfo ~yt.frontends.gadget.io.IOHandlerGadgetBinary ~yt.frontends.gadget.io.IOHandlerGadgetHDF5 ~yt.frontends.http_stream.io.IOHandlerHTTPStream ~yt.frontends.owls.io.IOHandlerOWLS ~yt.frontends.tipsy.io.IOHandlerTipsyBinary Stream ^^^^^^ .. autosummary:: ~yt.frontends.stream.data_structures.StreamDictFieldHandler ~yt.frontends.stream.data_structures.StreamGrid ~yt.frontends.stream.data_structures.StreamHandler ~yt.frontends.stream.data_structures.StreamHexahedralHierarchy ~yt.frontends.stream.data_structures.StreamHexahedralMesh ~yt.frontends.stream.data_structures.StreamHexahedralDataset ~yt.frontends.stream.data_structures.StreamHierarchy ~yt.frontends.stream.data_structures.StreamOctreeHandler ~yt.frontends.stream.data_structures.StreamOctreeDataset ~yt.frontends.stream.data_structures.StreamOctreeSubset ~yt.frontends.stream.data_structures.StreamParticleFile ~yt.frontends.stream.data_structures.StreamParticleIndex ~yt.frontends.stream.data_structures.StreamParticlesDataset ~yt.frontends.stream.data_structures.StreamDataset ~yt.frontends.stream.fields.StreamFieldInfo ~yt.frontends.stream.io.IOHandlerStream ~yt.frontends.stream.io.IOHandlerStreamHexahedral ~yt.frontends.stream.io.IOHandlerStreamOctree ~yt.frontends.stream.io.StreamParticleIOHandler ytdata ^^^^^^ .. autosummary:: ~yt.frontends.ytdata.data_structures.YTDataContainerDataset ~yt.frontends.ytdata.data_structures.YTSpatialPlotDataset ~yt.frontends.ytdata.data_structures.YTGridDataset ~yt.frontends.ytdata.data_structures.YTGridHierarchy ~yt.frontends.ytdata.data_structures.YTGrid ~yt.frontends.ytdata.data_structures.YTNonspatialDataset ~yt.frontends.ytdata.data_structures.YTNonspatialHierarchy ~yt.frontends.ytdata.data_structures.YTNonspatialGrid ~yt.frontends.ytdata.data_structures.YTProfileDataset ~yt.frontends.ytdata.data_structures.YTClumpTreeDataset ~yt.frontends.ytdata.data_structures.YTClumpContainer ~yt.frontends.ytdata.fields.YTDataContainerFieldInfo ~yt.frontends.ytdata.fields.YTGridFieldInfo ~yt.frontends.ytdata.io.IOHandlerYTDataContainerHDF5 ~yt.frontends.ytdata.io.IOHandlerYTGridHDF5 ~yt.frontends.ytdata.io.IOHandlerYTSpatialPlotHDF5 ~yt.frontends.ytdata.io.IOHandlerYTNonspatialhdf5 Loading Data ------------ .. autosummary:: ~yt.loaders.load ~yt.loaders.load_uniform_grid ~yt.loaders.load_amr_grids ~yt.loaders.load_particles ~yt.loaders.load_octree ~yt.loaders.load_hexahedral_mesh ~yt.loaders.load_unstructured_mesh ~yt.loaders.load_sample Derived Datatypes ----------------- Profiles and Histograms ^^^^^^^^^^^^^^^^^^^^^^^ These types are used to sum data up and either return that sum or return an average. Typically they are more easily used through the ``ProfilePlot`` ``PhasePlot`` interface. We also provide the ``create_profile`` function to create these objects in a uniform manner. .. autosummary:: ~yt.data_objects.profiles.ProfileND ~yt.data_objects.profiles.Profile1D ~yt.data_objects.profiles.Profile2D ~yt.data_objects.profiles.Profile3D ~yt.data_objects.profiles.ParticleProfile ~yt.data_objects.profiles.create_profile .. _clump_finding_ref: Clump Finding ^^^^^^^^^^^^^ The ``Clump`` object and associated functions can be used for identification of topologically disconnected structures, i.e., clump finding. .. autosummary:: ~yt.data_objects.level_sets.clump_handling.Clump ~yt.data_objects.level_sets.clump_handling.Clump.add_info_item ~yt.data_objects.level_sets.clump_handling.Clump.add_validator ~yt.data_objects.level_sets.clump_handling.Clump.save_as_dataset ~yt.data_objects.level_sets.clump_handling.find_clumps ~yt.data_objects.level_sets.clump_info_items.add_clump_info ~yt.data_objects.level_sets.clump_validators.add_validator X-ray Emission Fields ^^^^^^^^^^^^^^^^^^^^^ This can be used to create derived fields of X-ray emission in different energy bands. .. autosummary:: ~yt.fields.xray_emission_fields.XrayEmissivityIntegrator ~yt.fields.xray_emission_fields.add_xray_emissivity_field Field Types ----------- .. autosummary:: ~yt.fields.field_info_container.FieldInfoContainer ~yt.fields.derived_field.DerivedField ~yt.fields.derived_field.ValidateDataField ~yt.fields.derived_field.ValidateGridType ~yt.fields.derived_field.ValidateParameter ~yt.fields.derived_field.ValidateProperty ~yt.fields.derived_field.ValidateSpatial Field Functions --------------- .. autosummary:: ~yt.fields.field_info_container.FieldInfoContainer.add_field ~yt.data_objects.static_output.Dataset.add_field ~yt.data_objects.static_output.Dataset.add_deposited_particle_field ~yt.data_objects.static_output.Dataset.add_mesh_sampling_particle_field ~yt.data_objects.static_output.Dataset.add_gradient_fields ~yt.frontends.stream.data_structures.StreamParticlesDataset.add_sph_fields Particle Filters ---------------- .. autosummary:: ~yt.data_objects.particle_filters.add_particle_filter ~yt.data_objects.particle_filters.particle_filter Image Handling -------------- For volume renderings and fixed resolution buffers the image object returned is an ``ImageArray`` object, which has useful functions for image saving and writing to bitmaps. .. autosummary:: ~yt.data_objects.image_array.ImageArray Volume Rendering ^^^^^^^^^^^^^^^^ See also :ref:`volume_rendering`. Here are the primary entry points and the main classes involved in the Scene infrastructure: .. autosummary:: ~yt.visualization.volume_rendering.volume_rendering.volume_render ~yt.visualization.volume_rendering.volume_rendering.create_scene ~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection ~yt.visualization.volume_rendering.scene.Scene ~yt.visualization.volume_rendering.camera.Camera ~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree The different kinds of sources: .. autosummary:: ~yt.visualization.volume_rendering.render_source.RenderSource ~yt.visualization.volume_rendering.render_source.VolumeSource ~yt.visualization.volume_rendering.render_source.PointSource ~yt.visualization.volume_rendering.render_source.LineSource ~yt.visualization.volume_rendering.render_source.BoxSource ~yt.visualization.volume_rendering.render_source.GridSource ~yt.visualization.volume_rendering.render_source.CoordinateVectorSource ~yt.visualization.volume_rendering.render_source.MeshSource The different kinds of transfer functions: .. autosummary:: ~yt.visualization.volume_rendering.transfer_functions.TransferFunction ~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction ~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction ~yt.visualization.volume_rendering.transfer_functions.PlanckTransferFunction ~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction ~yt.visualization.volume_rendering.transfer_function_helper.TransferFunctionHelper The different kinds of lenses: .. autosummary:: ~yt.visualization.volume_rendering.lens.Lens ~yt.visualization.volume_rendering.lens.PlaneParallelLens ~yt.visualization.volume_rendering.lens.PerspectiveLens ~yt.visualization.volume_rendering.lens.StereoPerspectiveLens ~yt.visualization.volume_rendering.lens.FisheyeLens ~yt.visualization.volume_rendering.lens.SphericalLens ~yt.visualization.volume_rendering.lens.StereoSphericalLens Streamlining ^^^^^^^^^^^^ See also :ref:`streamlines`. .. autosummary:: ~yt.visualization.streamlines.Streamlines Image Writing ^^^^^^^^^^^^^ These functions are all used for fast writing of images directly to disk, without calling matplotlib. This can be very useful for high-cadence outputs where colorbars are unnecessary or for volume rendering. .. autosummary:: ~yt.visualization.image_writer.multi_image_composite ~yt.visualization.image_writer.write_bitmap ~yt.visualization.image_writer.write_projection ~yt.visualization.image_writer.write_image ~yt.visualization.image_writer.map_to_colors ~yt.visualization.image_writer.strip_colormap_data ~yt.visualization.image_writer.splat_points ~yt.visualization.image_writer.scale_image We also provide a module that is very good for generating EPS figures, particularly with complicated layouts. .. autosummary:: ~yt.visualization.eps_writer.DualEPS ~yt.visualization.eps_writer.single_plot ~yt.visualization.eps_writer.multiplot ~yt.visualization.eps_writer.multiplot_yt ~yt.visualization.eps_writer.return_colormap .. _derived-quantities-api: Derived Quantities ------------------ See :ref:`derived-quantities`. .. autosummary:: ~yt.data_objects.derived_quantities.DerivedQuantity ~yt.data_objects.derived_quantities.DerivedQuantityCollection ~yt.data_objects.derived_quantities.WeightedAverageQuantity ~yt.data_objects.derived_quantities.AngularMomentumVector ~yt.data_objects.derived_quantities.BulkVelocity ~yt.data_objects.derived_quantities.CenterOfMass ~yt.data_objects.derived_quantities.Extrema ~yt.data_objects.derived_quantities.MaxLocation ~yt.data_objects.derived_quantities.MinLocation ~yt.data_objects.derived_quantities.SpinParameter ~yt.data_objects.derived_quantities.TotalMass ~yt.data_objects.derived_quantities.TotalQuantity ~yt.data_objects.derived_quantities.WeightedAverageQuantity .. _callback-api: Callback List ------------- See also :ref:`callbacks`. .. autosummary:: ~yt.visualization.plot_window.PWViewerMPL.clear_annotations ~yt.visualization.plot_modifications.ArrowCallback ~yt.visualization.plot_modifications.CellEdgesCallback ~yt.visualization.plot_modifications.ClumpContourCallback ~yt.visualization.plot_modifications.ContourCallback ~yt.visualization.plot_modifications.CuttingQuiverCallback ~yt.visualization.plot_modifications.GridBoundaryCallback ~yt.visualization.plot_modifications.ImageLineCallback ~yt.visualization.plot_modifications.LinePlotCallback ~yt.visualization.plot_modifications.MagFieldCallback ~yt.visualization.plot_modifications.MarkerAnnotateCallback ~yt.visualization.plot_modifications.ParticleCallback ~yt.visualization.plot_modifications.PointAnnotateCallback ~yt.visualization.plot_modifications.QuiverCallback ~yt.visualization.plot_modifications.RayCallback ~yt.visualization.plot_modifications.ScaleCallback ~yt.visualization.plot_modifications.SphereCallback ~yt.visualization.plot_modifications.StreamlineCallback ~yt.visualization.plot_modifications.TextLabelCallback ~yt.visualization.plot_modifications.TimestampCallback ~yt.visualization.plot_modifications.TitleCallback ~yt.visualization.plot_modifications.TriangleFacetsCallback ~yt.visualization.plot_modifications.VelocityCallback Colormap Functions ------------------ See also :ref:`colormaps`. .. autosummary:: ~yt.visualization.color_maps.add_colormap ~yt.visualization.color_maps.make_colormap ~yt.visualization.color_maps.show_colormaps Function List ------------- .. autosummary:: ~yt.frontends.ytdata.utilities.save_as_dataset ~yt.data_objects.data_containers.YTDataContainer.save_as_dataset ~yt.data_objects.static_output.Dataset.all_data ~yt.data_objects.static_output.Dataset.box ~yt.funcs.enable_plugins ~yt.funcs.get_pbar ~yt.funcs.humanize_time ~yt.funcs.insert_ipython ~yt.funcs.is_root ~yt.funcs.is_sequence ~yt.funcs.iter_fields ~yt.funcs.just_one ~yt.funcs.only_on_root ~yt.funcs.paste_traceback ~yt.funcs.pdb_run ~yt.funcs.print_tb ~yt.funcs.rootonly ~yt.funcs.time_execution ~yt.data_objects.level_sets.contour_finder.identify_contours ~yt.utilities.parallel_tools.parallel_analysis_interface.enable_parallelism ~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_blocking_call ~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_objects ~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_passthrough ~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_root_only ~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_simple_proxy ~yt.data_objects.data_containers.YTDataContainer.get_field_parameter ~yt.data_objects.data_containers.YTDataContainer.set_field_parameter Math Utilities -------------- .. autosummary:: ~yt.utilities.math_utils.periodic_position ~yt.utilities.math_utils.periodic_dist ~yt.utilities.math_utils.euclidean_dist ~yt.utilities.math_utils.rotate_vector_3D ~yt.utilities.math_utils.modify_reference_frame ~yt.utilities.math_utils.compute_rotational_velocity ~yt.utilities.math_utils.compute_parallel_velocity ~yt.utilities.math_utils.compute_radial_velocity ~yt.utilities.math_utils.compute_cylindrical_radius ~yt.utilities.math_utils.ortho_find ~yt.utilities.math_utils.quartiles ~yt.utilities.math_utils.get_rotation_matrix ~yt.utilities.math_utils.get_sph_r ~yt.utilities.math_utils.resize_vector ~yt.utilities.math_utils.get_sph_theta ~yt.utilities.math_utils.get_sph_phi ~yt.utilities.math_utils.get_cyl_r ~yt.utilities.math_utils.get_cyl_z ~yt.utilities.math_utils.get_cyl_theta ~yt.utilities.math_utils.get_cyl_r_component ~yt.utilities.math_utils.get_cyl_theta_component ~yt.utilities.math_utils.get_cyl_z_component ~yt.utilities.math_utils.get_sph_r_component ~yt.utilities.math_utils.get_sph_phi_component ~yt.utilities.math_utils.get_sph_theta_component Miscellaneous Types ------------------- .. autosummary:: ~yt.config.YTConfig ~yt.utilities.parameter_file_storage.ParameterFileStore ~yt.utilities.parallel_tools.parallel_analysis_interface.ObjectIterator ~yt.utilities.parallel_tools.parallel_analysis_interface.ParallelAnalysisInterface ~yt.utilities.parallel_tools.parallel_analysis_interface.ParallelObjectIterator .. _cosmology-calculator-ref: Cosmology Calculator -------------------- .. autosummary:: ~yt.utilities.cosmology.Cosmology ~yt.utilities.cosmology.Cosmology.hubble_distance ~yt.utilities.cosmology.Cosmology.comoving_radial_distance ~yt.utilities.cosmology.Cosmology.comoving_transverse_distance ~yt.utilities.cosmology.Cosmology.comoving_volume ~yt.utilities.cosmology.Cosmology.angular_diameter_distance ~yt.utilities.cosmology.Cosmology.angular_scale ~yt.utilities.cosmology.Cosmology.luminosity_distance ~yt.utilities.cosmology.Cosmology.lookback_time ~yt.utilities.cosmology.Cosmology.critical_density ~yt.utilities.cosmology.Cosmology.hubble_parameter ~yt.utilities.cosmology.Cosmology.expansion_factor ~yt.utilities.cosmology.Cosmology.z_from_t ~yt.utilities.cosmology.Cosmology.t_from_z ~yt.utilities.cosmology.Cosmology.get_dark_factor Testing Infrastructure ---------------------- The core set of testing functions are re-exported from NumPy, and are deprecated (prefer using `numpy.testing `_ directly). .. autosummary:: ~yt.testing.assert_array_equal ~yt.testing.assert_almost_equal ~yt.testing.assert_approx_equal ~yt.testing.assert_array_almost_equal ~yt.testing.assert_equal ~yt.testing.assert_array_less ~yt.testing.assert_string_equal ~yt.testing.assert_array_almost_equal_nulp ~yt.testing.assert_allclose ~yt.testing.assert_raises `unyt.testing `_ also provides some specialized functions for comparing arrays in a units-aware fashion. Finally, yt provides the following functions: .. autosummary:: ~yt.testing.assert_rel_equal ~yt.testing.amrspace ~yt.testing.expand_keywords ~yt.testing.fake_random_ds ~yt.testing.fake_amr_ds ~yt.testing.fake_particle_ds ~yt.testing.fake_tetrahedral_ds ~yt.testing.fake_hexahedral_ds ~yt.testing.small_fake_hexahedral_ds ~yt.testing.fake_stretched_ds ~yt.testing.fake_vr_orientation_test_ds ~yt.testing.fake_sph_orientation_ds ~yt.testing.fake_sph_grid_ds ~yt.testing.fake_octree_ds These are for the pytest infrastructure: .. autosummary:: ~conftest.hashing ~yt.utilities.answer_testing.answer_tests.grid_hierarchy ~yt.utilities.answer_testing.answer_tests.parentage_relationships ~yt.utilities.answer_testing.answer_tests.grid_values ~yt.utilities.answer_testing.answer_tests.projection_values ~yt.utilities.answer_testing.answer_tests.field_values ~yt.utilities.answer_testing.answer_tests.pixelized_projection_values ~yt.utilities.answer_testing.answer_tests.small_patch_amr ~yt.utilities.answer_testing.answer_tests.big_patch_amr ~yt.utilities.answer_testing.answer_tests.generic_array ~yt.utilities.answer_testing.answer_tests.sph_answer ~yt.utilities.answer_testing.answer_tests.get_field_size_and_mean ~yt.utilities.answer_testing.answer_tests.plot_window_attribute ~yt.utilities.answer_testing.answer_tests.phase_plot_attribute ~yt.utilities.answer_testing.answer_tests.generic_image ~yt.utilities.answer_testing.answer_tests.axial_pixelization ~yt.utilities.answer_testing.answer_tests.extract_connected_sets ~yt.utilities.answer_testing.answer_tests.VR_image_comparison yt-project-yt-f043ac8/doc/source/reference/changelog.rst000066400000000000000000004050151510711153200233430ustar00rootroot00000000000000.. _changelog: ChangeLog ========= This is a non-comprehensive log of changes to yt over its many releases. Contributors ------------ The `CREDITS file `_ contains the most up-to-date list of everyone who has contributed to the yt source code. yt 4.0 ------ Welcome to yt 4.0! This release is the result of several years worth of developer effort and has been in progress since the mid 3.x series. Please keep in mind that this release **will** have breaking changes. Please see the yt 4.0 differences page for how you can expect behavior to differ from the 3.x series. This is a manually curated list of pull requests that went in to yt 4.0, representing a subset of `the full list `__. New Functions ^^^^^^^^^^^^^ - ``yt.load_sample`` (PR #\ `2417 `__, PR #\ `2496 `__, PR #\ `2875 `__, PR #\ `2877 `__, PR #\ `2894 `__, PR #\ `3262 `__, PR #\ `3263 `__, PR #\ `3277 `__, PR #\ `3309 `__, and PR #\ `3336 `__) - ``yt.set_log_level`` (PR #\ `2869 `__ and PR #\ `3094 `__) - ``list_annotations`` method for plots (PR #\ `2562 `__) API improvements ^^^^^^^^^^^^^^^^ - ``yt.load`` with support for ``os.PathLike`` objects, improved UX and moved a new ``yt.loaders`` module, along with sibling functions (PR #\ `2405 `__, PR #\ `2722 `__, PR #\ `2695 `__, PR #\ `2818 `__, and PR #\ `2831 `__, PR #\ `2832 `__) - ``Dataset`` now has a more useful repr (PR #\ `3217 `__) - Explicit JPEG export support (PR #\ `2549 `__) - ``annotate_clear`` is now ``clear_annotations`` (PR #\ `2569 `__) - Throw an error if field access is ambiguous (PR #\ `2967 `__) Newly supported data formats ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Arepo ~~~~~ - PR #\ `1807 `__ - PR #\ `2236 `__ - PR #\ `2244 `__ - PR #\ `2344 `__ - PR #\ `2434 `__ - PR #\ `3258 `__ - PR #\ `3265 `__ - PR #\ `3291 `__ Swift ~~~~~ - PR #\ `1962 `__ Improved support and frontend specific bugfixes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ adaptahop ~~~~~~~~~ - PR #\ `2678 `__ AMRVAC ~~~~~~ - PR #\ `2541 `__ - PR #\ `2745 `__ - PR #\ `2746 `__ - PR #\ `3215 `__ ART ~~~ - PR #\ `2688 `__ ARTIO ~~~~~ - PR #\ `2613 `__ Athena++ ~~~~~~~~ - PR #\ `2985 `__ Boxlib ~~~~~~ - PR #\ `2807 `__ - PR #\ `2814 `__ - PR #\ `2938 `__ (AMReX) Enzo-E (formerly Enzo-P) ~~~~~~~~~~~~~~~~~~~~~~~~ - PR #\ `3273 `__ - PR #\ `3274 `__ - PR #\ `3290 `__ - PR #\ `3372 `__ fits ~~~~ - PR #\ `2246 `__ - PR #\ `2345 `__ Gadget ~~~~~~ - PR #\ `2145 `__ - PR #\ `3233 `__ - PR #\ `3258 `__ Gadget FOF Halo ~~~~~~~~~~~~~~~ - PR #\ `2296 `__ GAMER ~~~~~ - PR #\ `3033 `__ Gizmo ~~~~~ - PR #\ `3234 `__ MOAB ~~~~ - PR #\ `2856 `__ Owls ~~~~ - PR #\ `3325 `__ Ramses ~~~~~~ - PR #\ `2679 `__ - PR #\ `2714 `__ - PR #\ `2960 `__ - PR #\ `3017 `__ - PR #\ `3018 `__ Tipsy ~~~~~ - PR #\ `2193 `__ Octree Frontends ~~~~~~~~~~~~~~~~ - Ghost zone access (PR #\ `2425 `__ and PR #\ `2958 `__) - Volume Rendering (PR #\ `2610 `__) Configuration file ^^^^^^^^^^^^^^^^^^ - Config files are now in `TOML `__ (PR #\ `2981 `__) - Allow a local plugin file (PR #\ `2534 `__) - Allow per-field local config (PR #\ `1931 `__) yt CLI ^^^^^^ - Fix broken command-line options (PR #\ `3361 `__) - Drop yt hub command (PR #\ `3363 `__) Deprecations ^^^^^^^^^^^^ - Smoothed fields are no longer necessary (PR #\ `2194 `__) - Energy and momentum field names are more accurate (PR #\ `3059 `__) - Incorrectly-named ``WeightedVariance`` is now ``WeightedStandardDeviation`` and the old name has been deprecated (PR #\ `3132 `__) - Colormap auto-registration has been changed and yt 4.1 will not register ``cmocean`` (PR #\ `3175 `__ and PR #\ `3214 `__) Removals ~~~~~~~~ - ``analysis_modules`` has been `extracted `__ (PR #\ `2081 `__) - Interactive volume rendering has been `extracted `__ (PR #\ `2896 `__) - The bundled version of ``poster`` has been removed (PR #\ `2783 `__) - The deprecated ``particle_position_relative`` field has been removed (PR #\ `2901 `__) - Deprecated functions have been removed (PR #\ `3007 `__) - Vendored packages have been removed (PR #\ `3008 `__) - ``yt.pmods`` has been removed (PR #\ `3061 `__) - yt now utilizes unyt as an external package (PR #\ `2219 `__, PR #\ `2300 `__, and PR #\ `2303 `__) Version 3.6.1 ------------- Version 3.6.1 is a bugfix release. It includes the following backport: - hotfix: support matplotlib 3.3.0. See `PR 2754 `__. Version 3.6.0 ------------- Version 3.6.0 our next major release since 3.5.1, which was in February 2019. It includes roughly 180 pull requests contributed from 39 contributors, 22 of which committed for their first time to the project. We have also updated our project governance and contribution guidelines, which you can `view here `_ . We'd like to thank all of the individuals who contributed to this release. There are lots of new features and we're excited to share them with the community. Breaking Changes ^^^^^^^^^^^^^^^^ The following breaking change was introduced. Please be aware that this could impact your code if you use this feature. - The angular momentum has been reversed compared to previous versions of yt. See `PR 2043 `__. Major Changes and New Features ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - New frontend support for the code AMRVAC. Many thanks to Clément Robert and Niels Claes who were major contributors to this initiative. Relevant PRs include - Initial PR to support AMRVAC native data files `PR 2321 `__. - added support for dust fields and derived fields `PR 2387 `__. - added support for derived fields for hydro runs `PR 2381 `__. - API documentation and docstrings for AMRVAC frontend `PR 2384 `__, `PR 2380 `__, `PR 2382 `__. - testing-related PRs for AMRVAC: `PR 2379 `__, `PR 2360 `__. - add verbosity to logging of geometry or ``geometry_override`` `PR 2421 `__. - add attribute to ``_code_unit_attributes`` specific to AMRVAC to ensure consistent renormalisation of AMRVAC datasets. See `PR 2357 `__. - parse AMRVAC's parfiles if user-provided `PR 2369 `__. - ensure that min_level reflects dataset that has refinement `PR 2475 `__. - fix derived unit parsing `PR 2362 `__. - update energy field to be ``energy_density`` and have units of code pressure `PR 2376 `__. - Support for the AdaptaHOP halo finder code `PR 2385 `__. - yt now supports geographic transforms and projections of data with cartopy with support from `PR 1966 `__. - annotations used to work for only a single point, they now work for multiple points on a plot, see `PR 2122 `__. - cosmology calculations now have support for the relativistic energy density of the universe, see `PR 1714 `__. This feature is accessible to cosmology datasets and was added to the Enzo frontend. - the eps writer now allows for arrow rotation. this is accessible with the ``rotate`` kwarg in the ``arrow`` function. See `PR 2151 `__. - allow for dynamic load balancing with parallel loading of timeseries data using the ``dynamic`` kwarg. `PR 2149 `__. - show/hide colorbar and show/hide axes are now available for ``ProfilePlot`` s. These functions were also moved from the PlotWindow to the PlotContainer class. `PR 2169 `__. - add support for ipywidgets with an ``__ipython_display__`` method on the FieldTypeContainer. Field variables, source, and the field array can be viewed with this widget. See PRs `PR 1844 `__ and `PR 1848 `__, or try ``display(ds.fields)`` in a Jupyter notebook. - cut regions can now be made with ``exclude_`` and ``include_`` on a number of objects, including above and below values, inside or outside regions, equal values, or nans. See `PR 1964 `__ and supporting documentation fix at `PR 2262 `__. - previously aliased fluid vector fields in curvilinear geometries were not converted to curvilinear coordinates, this was addressed in `PR 2105 `__. - 2d polar and 3d cylindrical geometries now support annotate_quivers, streamlines, line integral convolutions, see `PR 2105 `__. - add support for exporting data to firefly `PR 2190 `__. - gradient fields are now supported in curvilinear geometries. See `PR 2483 `__. - plotwindow colorbars now utilize mathtext in their labels, from `PR 2516 `__. - raise deprecation warning when using ``mylog.warn``. Instead use ``mylog.warning``. See `PR 2285 `__. - extend support of the ``marker``, ``text``, ``line`` and ``sphere`` annotation callbacks to polar geometries `PR 2466 `__. - Support MHD in the GAMER frontend `PR 2306 `__. - Export data container and profile fields to AstroPy QTables and pandas DataFrames `PR 2418 `__. - Add turbo colormap, a colorblind safe version of jet. See `PR 2339 `__. - Enable exporting regular grids (i.e., covering grids, arbitrary grids and smoothed grids) to ``xarray`` `PR 2294 `__. - add automatic loading of ``namelist.txt``, which contains the parameter file RAMSES uses to produce output `PR 2347 `__. - adds support for a nearest neighbor value field, accessible with the ``add_nearest_neighbor_value_field`` function for particle fields. See `PR 2301 `__. - speed up mesh deposition (uses caching) `PR 2136 `__. - speed up ghost zone generation. `PR 2403 `__. - ensure that a series dataset has kwargs passed down to data objects `PR 2366 `__. Documentation Changes ^^^^^^^^^^^^^^^^^^^^^ Our documentation has received some attention in the following PRs: - include donation/funding links in README `PR 2520 `__. - Included instructions on how to install yt on the Intel Distribution `PR 2355 `__. - include documentation on package vendors `PR 2494 `__. - update links to yt hub cookbooks `PR 2477 `__. - include relevant API docs in .gitignore `PR 2467 `__. - added docstrings for volume renderer cython code. see `PR 2456 `__ and for `PR 2449 `__. - update documentation install recommendations to include newer python versions `PR 2452 `__. - update custom CSS on docs to sphinx >=1.6.1. See `PR 2199 `__. - enhancing the contribution documentation on git, see `PR 2420 `__. - update documentation to correctly reference issues suitable for new contributors `PR 2346 `__. - fix URLs and spelling errors in a number of the cookbook notebooks `PR 2341 `__. - update release docs to include information about building binaries, tagging, and various upload locations. See `PR 2156 `__ and `PR 2160 `__. - ensuring the ``load_octree`` API docs are rendered `PR 2088 `__. - fixing doc build errors, see: `PR 2077 `__. - add an instruction to the doc about continuous mesh colormap `PR 2358 `__. - Fix minor typo `PR 2327 `__. - Fix some docs examples `PR 2316 `__. - fix sphinx formatting `PR 2409 `__. - Improve doc and fix docstring in deposition `PR 2453 `__. - Update documentation to reflect usage of rcfile (no brackets allowed), including strings. See `PR 2440 `__. Minor Enhancements and Bugfixes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - update pressure units in artio frontend (they were unitless previously) `PR 2521 `__. - ensure that modules supported by ``on_demand_imports`` are imported with that functionality `PR 2436 `__. - fix issues with groups in python3 in Ramses frontend `PR 2092 `__. - add tests to ytdata frontend api `PR 2075 `__. - update internal field usage from ``particle_{}_relative`` to ``relative_particle_{}`` so particle-based fields don't see deprecation warnings see `PR 2073 `__. - update save of ``field_data`` in clump finder, see `PR 2079 `__. - ensure map.js is included in the sdist for mapserver. See `PR 2158 `__. - add wrapping around ``yt_astro_analysis`` where it is used, in case it isn't installed `PR 2159 `__. - the contour finder now uses a maximum data value supplied by the user, rather than assuming the maximum value in the data container. Previously this caused issues in the clump finder. See `PR 2170 `__. - previously ramses data with non-hilbert ordering crashed. fixed by `PR 2200 `__. - fix an issue related to creating a ds9 region with FITS `PR 2335 `__. - add a check to see if pluginfilename is specified in ytrc `PR 2319 `__. - sort .so input file list so that the yt package builds in a reproducible way `PR 2206 `__. - update ``stack`` ufunc usage to include ``axis`` kwarg. See `PR 2204 `__. - extend support for field names in RAMSES descriptor file to include all names that don't include a comma. See `PR 2202 `__. - ``set_buff_size`` now works for ``OffAxisProjectionPlot``, see `PR 2239 `__. - fix chunking for chained cut regions. previously chunking commands would only look at the most recent cut region conditionals, and not any of the previous cut regions. See `PR 2234 `__. - update git command in Castro frontend to include ``git describe`` `PR 2235 `__. - in datasets with a single oct correctly guess the shape of the array `PR 2241 `__. - update ``get_yt_version`` function to support python 3. See `PR 2226 `__. - the ``"stream"`` frontend now correctly returns ``min_level`` for the mesh refinement. `PR 2519 `__. - region expressions (``ds.r[]``) can now be used on 2D datasets `PR 2482 `__. - background colors in cylindrical coordinate plots are now set correctly `PR 2517 `__. - Utilize current matplotlib interface for the ``_png`` module to write images to disk `PR 2514 `__. - fix issue with fortran utils where empty records were not supported `PR 2259 `__. - add support for python 3.7 in iterator used by dynamic parallel loading `PR 2265 `__. - add support to handle boxlib data where ``raw_fields`` contain ghost zones `PR 2255 `__. - update quiver fields to use native units, not assuming cgs `PR 2292 `__. - fix annotations on semi-structured mesh data with exodus II `PR 2274 `__. - extend support for loading exodus II data `PR 2274 `__. - add support for yt to load data generated by WarpX code that includes ``rigid_injected`` species `PR 2289 `__. - fix issue in GAMER frontend where periodic boundary conditions were not identified `PR 2287 `__. - fix issue in ytdata frontend where data size was calculated to have size ``(nparticles, dimensions)``. Now updated to use ``(nparticles, nparticles, dimensions)``. see `PR 2280 `__. - extend support for OpenPMD frontend to load data containing no particles see `PR 2270 `__. - raise a meaningful error on negative and zero zooming factors, see `PR 2443 `__. - ensure Datasets are consistent in their ``min_level`` attribute. See `PR 2478 `__. - adding matplotlib to trove classifiers `PR 2473 `__. - Add support for saving additional formats supported by matplotlib `PR 2318 `__. - add support for numpy 1.18.1 and help ensure consistency with unyt `PR 2448 `__. - add support for spherical geometries in ``plot_2d``. See `PR 2371 `__. - add support for sympy 1.5 `PR 2407 `__. - backporting unyt PR 102 for clip `PR 2329 `__. - allow code units in fields ``jeans_mass`` and ``dynamical_time``. See`PR 2454 `__. - fix for the case where boxlib nghost is different in different directions `PR 2343 `__. - bugfix for numpy 1.18 `PR 2419 `__. - Invoke ``_setup_dx`` in the enzo inline analysis. See `PR 2460 `__. - Update annotate_timestamp to work with ``"code"`` unit system. See `PR 2435 `__. - use ``dict.get`` to pull attributes that may not exist in ytdata frontend `PR 2471 `__. - solved bug related to slicing out ghost cells in chombo `PR 2388 `__. - correctly register reversed versions of cmocean cmaps `PR 2390 `__. - correctly set plot axes units to ``"code length"`` for datasets loaded with ``unit_system="code"`` `PR 2354 `__. - deprecate ``ImagePlotContainer.set_cbar_minorticks``. See `PR 2444 `__. - enzo-e frontend bugfix for single block datasets. See `PR 2424 `__. - explicitly default to solid lines in contour callback. See `PR 2330 `__. - replace all bare ``Except`` statements `PR 2474 `__. - fix an inconsistency between ``argmax`` and ``argmin`` methods in YTDataContainer class `PR 2457 `__. - fixed extra extension added by ``ImageArray.save()``. See `PR 2364 `__. - fix incorrect usage of ``is`` comparison with ``==`` comparison throughout the codebase `PR 2351 `__. - fix streamlines ``_con_args`` attribute `PR 2470 `__. - fix python 3.8 warnings `PR 2386 `__. - fix some invalid escape sequences. `PR 2488 `__. - fix typo in ``_vorticity_z`` field definition. See `PR 2398 `__. - fix an inconsistency in annotate_sphere callback. See `PR 2464 `__. - initialize unstructured mesh visualization background to ``nan`` `PR 2308 `__. - raise a meaningful error on negative and zero zooming factors `PR 2443 `__. - set ``symlog`` scaling to ``log`` if ``vmin > 0``. See `PR 2485 `__. - skip blank lines when reading parameters. See `PR 2406 `__. - Update magnetic field handling for RAMSES. See `PR 2377 `__. - Update ARTIO frontend to support compressed files. See `PR 2314 `__. - Use mirror copy of SDF data `PR 2334 `__. - Use sorted glob in athena to ensure reproducible ordering of grids `PR 2363 `__. - fix cartopy failures by ensuring data is in lat/lon when passed to cartopy `PR 2378 `__. - enforce unit consistency in plot callbacks, which fixes some unexpected behaviour in the plot annotations callbacks that use the plot window width or the data width `PR 2524 `__. Separate from our list of minor enhancements and bugfixes, we've grouped PRs related to infrastructure and testing in the next three sub-sub-sub sections. Testing and Infrastructure ~~~~~~~~~~~~~~~~~~~~~~~~~~ - infrastructure to change our testing from nose to pytest, see `PR 2401 `__. - Adding test_requirements and test_minimum requirements files to have bounds on installed testing versioning `PR 2083 `__. - Update the test failure report to include all failed tests related to a single test specification `PR 2084 `__. - add required dependencies for docs testing on Jenkins. See `PR 2090 `__. - suppress pyyaml warning that pops up when running tests `PR 2182 `__. - add tests for pre-existing ytdata datasets. See `PR 2229 `__. - add a test to check if cosmology calculator and cosmology dataset share the same unit registry `PR 2230 `__. - fix kh2d test name `PR 2342 `__. - disable OSNI projection answer test to remove cartopy errors `PR 2350 `__. CI related support ~~~~~~~~~~~~~~~~~~ - disable coverage on OSX to speed up travis testing and avoid timeouts `PR 2076 `__. - update travis base images on Linux and MacOSX `PR 2093 `__. - add ``W504`` and ``W605`` to ignored flake8 errors, see `PR 2078 `__., - update pyyaml version in ``test_requirements.txt`` file to address github warning `PR 2148 `__., - fix travis build errors resulting from numpy and cython being unavailable `PR 2171 `__. - fix appveyor build failures `PR 2231 `__. - Add Python 3.7 and Python 3.8 to CI test jobs. See `PR 2450 `__. - fix build failure on Windows `PR 2333 `__. - fix warnings due to travis configuration file. See `PR 2451 `__. - install pyyaml on appveyor `PR 2367 `__. - install sympy 1.4 on appveyor to work around regression in 1.5 `PR 2395 `__. - update CI recipes to fix recent failures `PR 2489 `__. Other Infrastructure ~~~~~~~~~~~~~~~~~~~~ - Added a welcomebot to our github page for new contributors, see `PR 2181 `__. - Added a pep8 bot to pre-run before tests, see `PR 2179 `__, `PR 2184 `__ and `PR 2185 `__. Version 3.5.0 ------------- Version 3.5.0 is the first major release of yt since August 2017. It includes 328 pull requests from 41 contributors, including 22 new contributors. Major Changes ^^^^^^^^^^^^^ - ``yt.analysis_modules`` has been deprecated in favor of the new ``yt_astro_analysis`` package. New features and new astronomy-specific analysis modules will go into ``yt_astro_analysis`` and importing from ``yt.analysis_modules`` will raise a noisy warning. We will remove ``yt.analysis_modules`` in a future release. See `PR 1938 `__. - Vector fields and derived fields depending on vector fields have been systematically updated to account for a bulk correction field parameter. For example, for the velocity field, all derived fields that depend on velocity will now account for the ``"bulk_velocity"`` field parameter. In addition, we have defined ``"relative_velocity"`` and ``"relative_magnetic_field"`` fields that include the bulk correction. Both of these are vector fields, to access the components, use e.g. ``"relative_velocity_x"``. The ``"particle_position_relative"`` and ``"particle_velocity_relative"`` fields have been deprecated. See `PR 1693 `__ and `PR 2022 `__. - Aliases to spatial fields with the ``"gas"`` field type will now be returned in the default unit system for the dataset. As an example the ``"x"`` field might resolve to the field tuples ``("index", "x")`` or ``("gas", "x")``. Accessing the former will return data in code units while the latter will return data in whatever unit system the dataset is configured to use (CGS, by default). This means that to ensure the units of a spatial field will always be consistent, one must access the field as a tuple, explicitly specifying the field type. Accessing a spatial field using a string field name may return data in either code units or the dataset's default unit system depending on the history of field accesses prior to accessing that field. In the future accessing fields using an ambiguous field name will raise an error. See `PR 1799 `__ and `PR 1850 `__. - The ``max_level`` and ``min_level`` attributes of yt data objects now correctly update the state of the underlying data objects when set. In addition we have added an example to the cookbook that shows how to downsample AMR data using this functionality. See `PR 1737 `__. - It is now possible to customize the formatting of labels for ion species fields. Rather than using the default spectroscopic notation, one can call ``ds.set_field_label_format("ionization_label", "plus_minus")`` to use the more traditional notation where ionization state is indicated with ``+`` and ``-`` symbols. See `PR 1867 `__. Improvements to the RAMSES frontend ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We would particularly like to recognize Corentin Cadiou for his tireless work over the past year on improving support for RAMSES and octree AMR data in yt. - Added support for reading RAMSES sink particles. See `PR 1548 `__. - Add support for the new self-describing Ramses particle output format. See `PR 1616 `__. - It is now possible to restrict the domain of a loaded Ramses dataset by passing a ``bbox`` keyword argument to ``yt.load()``. If passed this corresponds to the coordinates of the top-left and bottom-right hand corner of the subvolume to load. Data outside the bounding box will be ignored. This is useful for loading very large Ramses datasets where yt currently has poor scaling. See `PR 1637 `__. - The Ramses ``"particle_birth_time"`` field now contains the time when star particles form in a simulation in CGS units, formerly these times were only accessible via the incorrectly named ``"particle_age"`` field in conformal units. Correspondingly the ``"particle_age"`` field has been deprecated. The conformal birth time is not available via the ``"conformal_birth_time``" field. See `PR 1649 `__. - Substantial performance improvement for reading RAMSES AMR data. See `PR 1671 `__. - The RAMSES frontend will now produce less voluminous logging feedback when loading the dataset or reading data. This is particularly noticeable for very large datasets with many CPU files. See `PR 1738 `__. - Avoid repeated parsing of RAMSES particle and RT descriptors. See `PR 1739 `__. - Added support for reading the RAMSES gravitational potential field. See `PR 1751 `__. - Add support for RAMSES datasets that use the ``groupsize`` feature. See `PR 1769 `__. - Dramatically improve the overall performance of the RAMSES frontend. See `PR 1771 `__. Additional Improvements ^^^^^^^^^^^^^^^^^^^^^^^ - Added support for particle data in the Enzo-E frontend. See `PR 1490 `__. - Added an ``equivalence`` keyword argument to ``YTArray.in_units()`` and ``YTArray.to()``. This makes it possible to specify an equivalence when converting data to a new unit. Also added ``YTArray.to_value()`` which allows converting to a new unit, then stripping off the units to return a plain numpy array. See `PR 1563 `__. - Rather than crashing, yt will now assume default values for cosmology parameters in Gadget HDF5 data if it cannot find the relevant header information. See `PR 1578 `__. - Improve detection for OpenMP support at compile-time, including adding support for detecting OpenMP on Windows. See `PR 1591 `__, `PR 1695 `__ and `PR 1696 `__. - Add support for 2D cylindrical data for most plot callbacks. See `PR 1598 `__. - Particles outside the domain are now ignored by ``load_uniform_grid()`` and ``load_amr_grids()``. See `PR 1602 `__. - Fix incorrect units for the Gadget internal energy field in cosmology simulations. See `PR 1611 `__. - Add support for calculating covering grids in parallel. See `PR 1612 `__. - The number of particles in a dataset loaded by the stream frontend (e.g. via ``load_uniform_grid``) no longer needs to be explicitly provided via the ``number_of_particles`` keyword argument, using the ``number_of_particles`` keyword will now generate a deprecation warning. See `PR 1620 `__. - Add support for non-cartesian GAMER data. See `PR 1622 `__. - If a particle filter depends on another particle filter, both particle filters will be registered for a dataset if the dependent particle filter is registered with a dataset. See `PR 1624 `__. - The ``save()`` method of the various yt plot objects now optionally can accept a tuple of strings instead of a string. If a tuple is supplied, the elements are joined with ``os.sep`` to form a path. See `PR 1630 `__. - The quiver callback now accepts a ``plot_args`` keyword argument that allows passing keyword arguments to matplotlib to allow for customization of the quiver plot. See `PR 1636 `__. - Updates and improvements for the OpenPMD frontend. See `PR 1645 `__. - The mapserver now works correctly under Python3 and has new features like a colormap selector and plotting multiple fields via layers. See `PR 1654 `__ and `PR 1668 `__. - Substantial performance improvement for calculating the gravitational potential in the clump finder. See `PR 1684 `__. - Added new methods to ``ProfilePlot``: ``set_xlabel()``, ``set_ylabel()``, ``annotate_title()``, and ``annotate_text()``. See `PR 1700 `__ and `PR 1705 `__. - Speedup for parallel halo finding operation for the FOF and HOP halo finders. See `PR 1724 `__. - Add support for halo finding using the rockstar halo finder on Python3. See `PR 1740 `__. - The ``ValidateParameter`` field validator has gained the ability for users to explicitly specify the values of field parameters during field detection. This makes it possible to write fields that access different sets of fields depending on the value of the field parameter. For example, a field might define an ``'axis'`` field parameter that can be either ``'x'``, ``'y'`` or ``'z'``. One can now explicitly tell the field detection system to access the field using all three values of ``'axis'``. This improvement avoids errors one would see now where only one value or an invalid value of the field parameter will be tested by yt. See `PR 1741 `__. - It is now legal to pass a dataset instance as the first argument to ``ProfilePlot`` and ``PhasePlot``. This is equivalent to passing ``ds.all_data()``. - Functions that accept a ``(length, unit)`` tuple (e.g. ``(3, 'km')`` for 3 kilometers) will not raise an error if ``length`` is a ``YTQuantity`` instance with units attached. See `PR 1749 `__. - The ``annotate_timestamp`` plot annotation now optionally accepts a ``time_offset`` keyword argument that sets the zero point of the time scale. Additionally, the ``annotate_scale`` plot annotation now accepts a ``format`` keyword argument, allowing custom formatting of the scale annotation. See `PR 1755 `__. - Add support for magnetic field variables and creation time fields in the GIZMO frontend. See `PR 1756 `__ and `PR 1914 `__. - ``ParticleProjectionPlot`` now supports the ``annotate_particles`` plot callback. See `PR 1765 `__. - Optimized the performance of off-axis projections for octree AMR data. See `PR 1766 `__. - Added support for several radiative transfer fields in the ARTIO frontend. See `PR 1804 `__. - Performance improvement for Boxlib datasets that don't use AMR. See `PR 1834 `__. - It is now possible to set custom profile bin edges. See `PR 1837 `__. - Dropped support for Python3.4. See `PR 1840 `__. - Add support for reading RAMSES cooling fields. See `PR 1853 `__. - Add support for NumPy 1.15. See `PR 1854 `__. - Ensure that functions defined in the plugins file are available in the yt namespace. See `PR 1855 `__. - Creating a profiles with log-scaled bins but where the bin edges are negative or zero now raises an error instead of silently generating a corrupt, incorrect answer. See `PR 1856 `__. - Systematically added validation for inputs to data object initializers. See `PR 1871 `__. - It is now possible to select only a specific particle type in the particle trajectories analysis module. See `PR 1887 `__. - Substantially improve the performance of selecting particle fields with a ``cut_region`` data object. See `PR 1892 `__. - The ``iyt`` command-line entry-point into IPython now installs yt-specific tab-completions. See `PR 1900 `__. - Derived quantities have been systematically updated to accept a ``particle_type`` keyword argument, allowing easier analysis of only a single particle type. See `PR 1902 `__ and `PR 1922 `__. - The ``annotate_streamlines()`` function now accepts a ``display_threshold`` keyword argument. This suppresses drawing streamlines over any region of a dataset where the field being displayed is less than the threshold. See `PR 1922 `__. - Add support for 2D nodal data. See `PR 1923 `__. - Add support for GAMER outputs that use patch groups. This substantially reduces the memory requirements for loading large GAMER datasets. See `PR 1935 `__. - Add a ``data_source`` keyword argument to the ``annotate_particles`` plot callback. See `PR 1937 `__. - Define species fields in the NMSU Art frontend. See `PR 1981 `__. - Added a ``__format__`` implementation for ``YTArray``. See `PR 1985 `__. - Derived fields that use a particle filter now only need to be derived for the particle filter type, not for the particle types used to define the particle filter. See `PR 1993 `__. - Added support for periodic visualizations using ``ParticleProjectionPlot``. See `PR 1996 `__. - Added ``YTArray.argsort()``. See `PR 2002 `__. - Calculate the header size from the header specification in the Gadget frontend to allow reading from Gadget binary datasets with nonstandard headers. See `PR 2005 `__ and `PR 2036 `__. - Save the standard deviation in ``profile.save_as_dataset()``. See `PR 2008 `__. - Allow the ``color`` keyword argument to be passed to matplotlib in the ``annotate_clumps`` callback to control the color of the clump annotation. See `PR 2019 `__. - Raise an exception when profiling fields of unequal shape. See `PR 2025 `__. - The clump info dictionary is now populated as clumps get created instead of during ``clump.save_as_dataset()``. See `PR 2053 `__. - Avoid segmentation fault in slice selector by clipping slice integer coordinates. See `PR 2055 `__. Minor Enhancements and Bugfixes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fix incorrect use of floating point division in the parallel analysis framework. See `PR 1538 `__. - Fix integration with that matplotlib QT backend for interactive plotting. See `PR 1540 `__. - Add support for the particle creation time field in the GAMER frontend. See `PR 1546 `__. - Various minor improvements to the docs. See `PR 1542 `__. and `PR 1547 `__. - Add better error handling for invalid tipsy aux files. See `PR 1549 `__. - Fix typo in default Gadget header specification. See `PR 1550 `__. - Use the git version in the get_yt_version function. See `PR 1551 `__. - Assume dimensionless units for fields from FITS datasets when we can't infer the units. See `PR 1553 `__. - Autodetect ramses extra particle fields. See `PR 1555 `__. - Fix issue with handling unitless halo quantities in HaloCatalog. See `PR 1558 `__. - Track the halo catalog creation process using a parallel-safe progress bar. See `PR 1559 `__. - The PPV Cube functionality no longer crashes if there is no temperature field in the dataset. See `PR 1562 `__. - Fix crash caused by saving the ``'x'``, ``'y'``, or ``'z'`` fields in clump.save_as_dataset(). See `PR 1567 `__. - Accept both string and tuple field names in ``ProfilePlot.set_unit()`` and ``PhasePlot.set_unit()``. See `PR 1568 `__. - Fix issues with some arbitrary grid attributes not being reloaded properly after being saved with ``save_as_dataset()``. See `PR 1569 `__. - Fix units issue in the light cone projection operation. See `PR 1574 `__. - Use ``astropy.wcsaxes`` instead of the independent ``wcsaxes`` project. See `PR 1577 `__. - Correct typo in WarpX field definitions. See `PR 1583 `__. - Avoid crashing when loading an Enzo dataset with a parameter file that has commented out parameters. See `PR 1586 `__. - Fix a corner case in the clump finding machinery where the reference to the parent clump is invalid after pruning a child clump that has no siblings. See `PR 1587 `__. - Fix issues with setting up yt fields for the magnetic and velocity field components and associated derived fields in curvilinear coordinate systems. See `PR 1588 `__ and `PR 1687 `__. - Fix incorrect profile values when the profile weight field has values equal to zero. See `PR 1590 `__. - Fix issues with making matplotlib animations of a ``ParticleProjectionPlot``. See `PR 1594 `__. - The ``Scene.annotate_axes()`` function will now use the correct colors for drawing the axes annotation. See `PR 1596 `__. - Fix incorrect default plot bounds for a zoomed-in slice plot of a 2D cylindrical dataset. See `PR 1597 `__. - Fix issue where field accesses on 2D grids would return data with incorrect shapes. See `PR 1603 `__. - Added a cookbook example for a multipanel phase plot. See `PR 1605 `__. - Boolean simulation parameters in the Boxlib frontend will now be interpreted correctly. See `PR 1619 `__. - The ``ds.particle_type_counts`` attribute will now be populated correctly for AMReX data. - The ``"rad"`` unit (added for compatibility with astropy) now has the correct dimensions of angle instead of solid angle. See `PR 1628 `__. - Fix units issues in several plot callbacks. See `PR 1633 `__ and `PR 1674 `__. - Various fixes for how WarpX fields are interpreted. See `PR 1634 `__. - Fix incorrect units in the automatically deposited particle fields. See `PR 1638 `__. - It is now possible to set the axes background color after calling ``plot.hide_axes()``. See `PR 1662 `__. - Fix a typo in the name of the ``colors`` keyword argument passed to matplotlib for the contour callback. See `PR 1664 `__. - Add support for Enzo Active Particle fields that arrays. See `PR 1665 `__. - Avoid crash when generating halo catalogs from the rockstar halo finder for small simulation domains. See `PR 1679 `__. - The clump callback now functions correctly for a reloaded clump dataset. See `PR 1683 `__. - Fix incorrect calculation for tangential components of vector fields. See `PR 1688 `__. - Allow halo finders to run in parallel on Python3. See `PR 1690 `__. - Fix issues with Gadget particle IDs for simulations with large numbers of particles being incorrectly rounded. See `PR 1692 `__. - ``ParticlePlot`` no longer needs to be passed spatial fields in a particular order to ensure that a ``ParticleProjectionPlot`` is returned. See `PR 1697 `__. - Accessing data from a FLASH grid directly now returns float64 data. See `PR 1708 `__. - Fix periodicity check in ``YTPoint`` data object. See `PR 1712 `__. - Avoid crash on matplotlib 2.2.0 when generating yt plots with symlog colorbars. See `PR 1720 `__. - Avoid crash when FLASH ``"unitsystem"`` parameter is quoted in the HDF5 file. See `PR 1722 `__. - Avoid issues with creating custom particle filters for OWLS/EAGLE datasets. See `PR 1723 `__. - Adapt to behavior change in matplotlib that caused plot inset boxes for annotated text to be drawn when none was requested. See `PR 1731 `__ and `PR 1827 `__. - Fix clump finder ignoring field parameters. See `PR 1732 `__. - Avoid generating NaNs in x-ray emission fields. See `PR 1742 `__. - Fix compatibility with Sphinx 1.7 when building the docs. See `PR 1743 `__. - Eliminate usage of deprecated ``"clobber"`` keyword argument for various usages of astropy in yt. See `PR 1744 `__. - Fix incorrect definition of the ``"d"`` unit (an alias of ``"day"``). See `PR 1746 `__. - ``PhasePlot.set_log()`` now correctly handles tuple field names as well as string field names. See `PR 1787 `__. - Fix incorrect axis order in aitoff pixelizer. See `PR 1791 `__. - Fix crash in when exporting a surface as a ply model. See `PR 1792 `__ and `PR 1817 `__. - Fix crash in scene.save_annotated() in newer numpy versions. See `PR 1793 `__. - Many tests no longer depend on real datasets. See `PR 1801 `__, `PR 1805 `__, `PR 1809 `__, `PR 1883 `__, and `PR 1941 `__ - New tests were added to improve test coverage or the performance of the tests. See `PR 1820 `__, `PR 1831 `__, `PR 1833 `__, `PR 1841 `__, `PR 1842 `__, `PR 1885 `__, `PR 1886 `__, `PR 1952 `__, `PR 1953 `__, `PR 1955 `__, and `PR 1957 `__. - The particle trajectories machinery will raise an error if it is asked to analyze a set of particles with duplicated particle IDs. See `PR 1818 `__. - Fix incorrect velocity unit int he ``gadget_fof`` frontend. See `PR 1829 `__. - Making an off-axis projection of a cut_region data object with an octree AMR dataset now works correctly. See `PR 1858 `__. - Replace hard-coded constants in Enzo frontend with calculations to improve agreement with Enzo's internal constants and improve clarity. See `PR 1873 `__. - Correct issues with Enzo magnetic units in cosmology simulations. See `PR 1876 `__. - Use the species names from the dataset rather than hardcoding species names in the WarpX frontend. See `PR 1884 `__. - Fix issue with masked I/O for unstructured mesh data. See `PR 1918 `__. - Fix crash when reading DM-only Enzo datasets where some grids have no particles. See `PR 1919 `__. - Fix crash when loading pure-hydro Nyx dataset. See `PR 1950 `__. - Avoid crashes when plotting fields that contain NaN. See `PR 1951 `__. - Avoid crashes when loading NMSU ART data. See `PR 1960 `__. - Avoid crash when loading WarpX dataset with no particles. See `PR 1979 `__. - Adapt to API change in glue to fix the ``to_glue()`` method on yt data objects. See `PR 1991 `__. - Fix incorrect width calculation in the ``annotate_halos()`` plot callback. See `PR 1995 `__. - Don't try to read from files containing zero halos in the ``gadget_fof`` frontend. See `PR 2001 `__. - Fix incorrect calculation in ``get_ortho_base()``. See `PR 2013 `__. - Avoid issues with the axes background color being inconsistently set. See `PR 2018 `__. - Fix issue with reading multiple fields at once for octree AMR data sometimes returning data for another field for one of the requested fields. See `PR 2020 `__. - Fix incorrect domain annotation for ``Scene.annotate_domain()`` when using the plane-parallel camera. See `PR 2024 `__. - Avoid crash when particles are on the domain edges for ``gadget_fof`` data. See `PR 2034 `__. - Avoid stripping code units when processing units through a dataset's unit system. See `PR 2035 `__. - Avoid incorrectly rescaling units of metalicity fields. See `PR 2038 `__. - Fix incorrect units for FLASH ``"divb"`` field. See `PR 2062 `__. Version 3.4 ----------- Version 3.4 is the first major release of yt since July 2016. It includes 450 pull requests from 44 contributors including 18 new contributors. - yt now supports displaying plots using the interactive matplotlib backends. To enable this functionality call ``yt.toggle_interactivity()``. This is currently supported at an experimental level, please let us know if you come across issues using it. See `Bitbucket PR 2294 `__. - The yt configuration file should now be located in a location following the XDG\_CONFIG convention (usually ``~/.config/yt/ytrc``) rather than the old default location (usually ``~/.yt/config``). You can use ``yt config migrate`` at the bash command line to migrate your configuration file to the new location. See `Bitbucket PR 2343 `__. - Added ``yt.LinePlot``, a new plotting class for creating 1D plots along lines through a dataset. See `Github PR 1509 `__ and `Github PR 1440 `__. - Added ``yt.define_unit`` to easily define new units in yt's unit system. See `Bitbucket PR 2485 `__. - Added ``yt.plot_2d``, a wrapper around SlicePlot for plotting 2D datasets. See `Github PR 1476 `__. - We have restored support for boolean data objects. Boolean objects are data objects that are defined in terms of boolean operations on other data objects. See `Bitbucket PR 2257 `__. - Datasets now have a ``fields`` attribute that allows access to fields via a python object. For example, instead of using a tuple field name like ``('gas', 'density')``, one can now use ``ds.fields.gas.density``. See `Bitbucket PR 2459 `__. - It is now possible to create a wider variety of data objects via ``ds.r``, including rays, fixed resolution rays, points, and images. See `Github PR 1518 `__ and `Github PR 1393 `__. - ``add_field`` and ``ds.add_field`` must now be called with a ``sampling_type`` keyword argument. Possible values are currently ``cell`` and ``particle``. We have also deprecated the ``particle_type`` keyword argument in favor of ``sampling_type='cell'``. For now a ``'cell'`` ``sampling_type`` is assumed if ``sampling_type`` is not specified but in the future ``sampling_type`` will always need to be specified. - Added support for the ``Athena++`` code. See `Bitbucket PR 2149 `__. - Added support for the ``Enzo-E`` code. See `Github PR 1447 `__, `Github PR 1443 `__ and `Github PR 1439 `__. - Added support for the ``AMReX`` code. See `Bitbucket PR 2530 `__. - Added support for the ``openPMD`` output format. See `Bitbucket PR 2376 `__. - Added support for reading face-centered and vertex-centered fields for block AMR codes. See `Bitbucket PR 2575 `__. - Added support for loading outputs from the Amiga Halo Finder. See `Github PR 1477 `__. - Added support for particle fields for Boxlib data. See `Bitbucket PR 2510 `__ and `Bitbucket PR 2497 `__. - Added support for custom RAMSES particle fields. See `Github PR 1470 `__. - Added support for RAMSES-RT data. See `Github PR 1456 `__ and `Github PR 1449 `__. - Added support for Enzo MHDCT fields. See `Github PR 1438 `__. - Added support for units and particle fields to the GAMER frontend. See `Bitbucket PR 2366 `__ and `Bitbucket PR 2408 `__. - Added support for type 2 Gadget binary outputs. See `Bitbucket PR 2355 `__. - Added the ability to detect and read double precision Gadget data. See `Bitbucket PR 2537 `__. - Added the ability to detect and read in big endian Gadget data. See `Github PR 1353 `__. - Added support for Nyx datasets that do not contain particles. See `Bitbucket PR 2571 `__ - A number of untested and unmaintained modules have been deprecated and moved to the `yt attic repository `__. This includes the functionality for calculating two point functions, the Sunrise exporter, the star analysis module, and the functionality for calculating halo mass functions. If you are interested in working on restoring the functionality in these modules, we welcome contributions. Please contact us on the mailing list or by opening an issue on GitHub if you have questions. - The particle trajectories functionality has been removed from the analysis modules API and added as a method of the ``DatasetSeries`` object. You can now create a ``ParticleTrajectories`` object using ``ts.particle_trajectories()`` where ``ts`` is a time series of datasets. - The ``spectral_integrator`` analysis module is now available via ``yt.fields.xray_emission_fields``. See `Bitbucket PR 2465 `__. - The ``photon_simulator`` analysis module has been deprecated in favor of the ``pyXSIM`` package, available separately from ``yt``. See `Bitbucket PR 2441 `__. - ``yt.utilities.fits_image`` is now available as ``yt.visualization.fits_image``. In addition classes that were in the ``yt.utilities.fits_image`` namespace are now available in the main ``yt`` namespace. - The ``profile.variance`` attribute has been deprecated in favor of ``profile.standard_deviation``. - The ``number_of_particles`` key no longer needs to be defined when loading data via the stream frontend. See `Github PR 1428 `__. - The install script now only supports installing via miniconda. We have removed support for compiling python and yt's dependencies from source. See `Github PR 1459 `__. - Added ``plot.set_background_color`` for ``PlotWindow`` and ``PhasePlot`` plots. This lets users specify a color to fill in the background of a plot instead of the default color, white. See `Bitbucket PR 2513 `__. - ``PlotWindow`` plots can now optionally use a right-handed coordinate system. See `Bitbucket PR 2318 `__. - The isocontour API has been overhauled to make use of units. See `Bitbucket PR 2453 `__. - ``Dataset`` instances now have a ``checksum`` property, which can be accessed via ``ds.checksum``. This provides a unique identifier that is guaranteed to be the same from session to session. See `Bitbucket PR 2503 `__. - Added a ``data_source`` keyword argument to ``OffAxisProjectionPlot``. See `Bitbucket PR 2490 `__. - Added a ``yt download`` command-line helper to download test data from https://yt-project.org/data. For more information see ``yt download --help`` at the bash command line. See `Bitbucket PR 2495 `__ and `Bitbucket PR 2471 `__. - Added a ``yt upload`` command-line helper to upload files to the `yt curldrop `__ at the bash command line. See `Github PR 1471 `__. - If it's installed, colormaps from the `cmocean package `__ will be made available as yt colormaps. See `Bitbucket PR 2439 `__. - It is now possible to visualize unstructured mesh fields defined on multiple mesh blocks. See `Bitbucket PR 2487 `__. - Add support for second-order interpolation when slicing tetrahedral unstructured meshes. See `Bitbucket PR 2550 `__. - Add support for volume rendering second-order tetrahedral meshes. See `Bitbucket PR 2401 `__. - Add support for QUAD9 mesh elements. See `Bitbucket PR 2549 `__. - Add support for second-order triangle mesh elements. See `Bitbucket PR 2378 `__. - Added support for dynamical dark energy parameterizations to the ``Cosmology`` object. See `Bitbucket PR 2572 `__. - ``ParticleProfile`` can now handle log-scaled bins and data with negative values. See `Bitbucket PR 2564 `__ and `Github PR 1510 `__. - Cut region data objects can now be saved as reloadable datasets using ``save_as_dataset``. See `Bitbucket PR 2541 `__. - Clump objects can now be saved as reloadable datasets using ``save_as_dataset``. See `Bitbucket PR 2326 `__. - It is now possible to specify the field to use for the size of the circles in the ``annotate_halos`` plot modifying function. See `Bitbucket PR 2493 `__. - The ``ds.max_level`` attribute is now a property that is computed on demand. The more verbose ``ds.index.max_level`` will continue to work. See `Bitbucket PR 2461 `__. - The ``PointSource`` volume rendering source now optionally accepts a ``radius`` keyword argument to draw spatially extended points. See `Bitbucket PR 2404 `__. - It is now possible to save volume rendering images in eps, ps, and pdf format. See `Github PR 1504 `__. Minor Enhancements and Bugfixes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed issue selecting and visualizing data at very high AMR levels. See `Github PR 1521 `__ and `Github PR 1433 `__. - Print a more descriptive error message when defining a particle filter fails with missing fields See `Github PR 1517 `__. - Removed grid edge rounding from the FLASH frontend. This fixes a number of pernicious visualization artifacts for FLASH data. See `Github PR 1493 `__. - Parallel projections no longer error if there are less io chunks than MPI tasks. See `Github PR 1488 `__. - A memory leak in the volume renderer has been fixed. See `Github PR 1485 `__ and `Github PR 1435 `__. - The ``force_override`` keyword argument now raises an error when used with on-disk fields. See `Github PR 1516 `__. - Restore support for making plots from reloaded plots. See `Github PR 1514 `__ - Don't ever try to read inputs or probin files for Castro and Maestro. See `Github PR 1445 `__. - Fixed issue that caused visualization artifacts when creating an off-axis projection for particle or octree AMR data. See `Github PR 1434 `__. - Fix i/o for the Enzo ``'Dark_Matter_Density'`` field. See `Github PR 1360 `__. - Create the ``'particle_ones'`` field even if we don't have a particle mass field. See `Github PR 1424 `__. - Fixed issues with minor colorbar ticks with symlog colorbar scaling. See `Github PR 1423 `__. - Using the rockstar halo finder is now supported under Python3. See `Github PR 1414 `__. - Fixed issues with orientations of volume renderings when compositing multiple sources. See `Github PR 1411 `__. - Added a check for valid AMR structure in ``load_amr_grids``. See `Github PR 1408 `__. - Fix bug in handling of periodic boundary conditions in the ``annotate_halos`` plot modifying function. See `Github PR 1351 `__. - Add support for plots with non-unit aspect ratios to the ``annotate_scale`` plot modifying function. See `Bitbucket PR 2551 `__. - Fixed issue with saving light ray datasets. See `Bitbucket PR 2589 `__. - Added support for 2D WarpX data. ee `Bitbucket PR 2583 `__. - Ensure the ``particle_radius`` field is always accessed with the correct field type. See `Bitbucket PR 2562 `__. - It is now possible to use a covering grid to access particle filter fields. See `Bitbucket PR 2569 `__. - The x limits of a ``ProfilePlot`` will now snap exactly to the limits specified in calls to ``ProfilePlot.set_xlim``. See `Bitbucket PR 2546 `__. - Added a cookbook example showing how to make movies using matplotlib's animation framework. See `Bitbucket PR 2544 `__. - Use a parallel-safe wrapper around mkdir when creating new directories. See `Bitbucket PR 2570 `__. - Removed ``yt.utilities.spatial``. This was a forked version of ``scipy.spatial`` with support for a periodic KD-tree. Scipy now has a periodic KD-tree, so we have removed the forked version from yt. Please use ``scipy.spatial`` if you were relying on ``yt.utilities.spatial``. See `Bitbucket PR 2576 `__. - Improvements for the ``HaloCatalog``. See `Bitbucket PR 2536 `__ and `Bitbucket PR 2535 `__. - Removed ``'log'`` in colorbar label in annotated volume rendering. See `Bitbucket PR 2548 `__ - Fixed a crash triggered by depositing particle data onto a covering grid. See `Bitbucket PR 2545 `__. - Ensure field type guessing is deterministic on Python3. See `Bitbucket PR 2559 `__. - Removed unused yt.utilities.exodusII\_reader module. See `Bitbucket PR 2533 `__. - The ``cell_volume`` field in curvilinear coordinates now uses an exact rather than an approximate definition. See `Bitbucket PR 2466 `__. Version 3.3 ----------- Version 3.3 is the first major release of yt since July 2015. It includes more than 3000 commits from 41 contributors, including 12 new contributors. Major enhancements ^^^^^^^^^^^^^^^^^^ * Raw and processed data from selections, projections, profiles and so forth can now be saved in a ytdata format and loaded back in by yt. See :ref:`saving_data`. * Totally re-worked volume rendering API. The old API is still available for users who prefer it, however. See :ref:`volume_rendering`. * Support for unstructured mesh visualization. See :ref:`unstructured-mesh-slices` and :ref:`unstructured_mesh_rendering`. * Interactive Data Visualization for AMR and unstructured mesh datasets. See :ref:`interactive_data_visualization`. * Several new colormaps, including a new default, 'arbre'. The other new colormaps are named 'octarine', 'kelp', and 'dusk'. All these new colormaps were generated using the `viscm package `_ and should do a better job of representing the data for colorblind viewers and when printed out in grayscale. See :ref:`colormaps` for more detail. * New frontends for the :ref:`ExodusII `, :ref:`GAMER `, and :ref:`Gizmo ` data formats. * The unit system associated with a dataset is now customizable, defaulting to CGS. * Enhancements and usability improvements for analysis modules, especially the ``absorption_spectrum``, ``photon_simulator``, and ``light_ray`` modules. See :ref:`synthetic-observations`. * Data objects can now be created via an alternative Numpy-like API. See :ref:`quickly-selecting-data`. * A line integral convolution plot modification. See :ref:`annotate-line-integral-convolution`. * Many speed optimizations, including to the volume rendering, units, tests, covering grids, the absorption spectrum and photon simulator analysis modules, and ghost zone generation. * Packaging and release-related improvements: better install and setup scripts, automated PR backporting. * Readability improvements to the codebase, including linting, removing dead code, and refactoring much of the Cython. * Improvements to the CI infrastructure, including more extensible answer tests and automated testing for Python 3 and Windows. * Numerous documentation improvements, including formatting tweaks, bugfixes, and many new cookbook recipes. * Support for geographic (lat/lon) coordinates. * Several improvements for SPH codes, including alternative smoothing kernels, an ``add_smoothed_particle_field`` function, and particle type-aware octree construction for Gadget data. * Roundtrip conversions between Pint and yt units. * Added halo data containers for gadget_fof frontend. * Enabled support for spherical datasets in the BoxLib frontend. * Many new tests have been added. * Better hashing for Selector objects. Minor enhancements and bugfixes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Fixed many bugs related to Python 3 compatibility * Fixed bugs related to compatibility issues with newer versions of numpy * Added the ability to export data objects to a Pandas dataframe * Added support for the fabs ufunc to YTArray * Fixed two licensing issues * Fixed a number of bugs related to Windows compatibility. * We now avoid hard-to-decipher tracebacks when loading empty files or directories * Fixed a bug related to ART star particle creation time field * Fixed a bug caused by using the wrong int type for indexing in particle deposit * Fixed a NameError bug in comparing temperature units with offsets * Fixed an API bug in YTArray casting during coercion from YTQuantity * Added loadtxt and savetxt convenience functions for ``YTArray`` * Fixed an issue caused by not sort species names with Enzo * Fixed a units bug for RAMSES when ``boxlen > 1``. * Fixed ``process_chunk`` function for non-cartesian geometry. * Added ``scale_factor`` attribute to cosmological simulation datasets * Fixed a bug where "center" vectors are used instead of "normal" vectors in get_sph_phi(), etc. * Fixed issues involving invalid FRBs when uses called _setup_plots in their scripts * Added a ``text_args`` keyword to ``annotate_scale()`` callback * Added a print_stats function for RAMSES * Fixed a number of bugs in the Photon Simulator * Added support for particle fields to the [Min,Max]Location derived quantities * Fixed some units bugs for Gadget cosmology simulations * Fixed a bug with Gadget/GIZMO StarFormationRate units * Fixed an issue in TimeSeriesData where all the filenames were getting passed to ``load`` on each processor. * Fixed a units bug in the Tipsy frontend * Ensured that ARTIOIndex.get_smallest_dx() returns a quantity with units * Ensured that plots are valid after invalidating the figure * Fixed a bug regarding code unit labels * Fixed a bug with reading Tipsy Aux files * Added an effective redshift field to the Light Ray analysis module for use in AbsorptionSpectrum * Fixed a bug with the redshift calculation in LightRay analysis module * Fixed a bug in the Orion frontend when you had more than 10 on-disk particle fields in the file * Detect more types of ART files * Update derived_field_list in add_volume_weighted_smoothed_field * Fixed casting issues for 1D and 2D Enzo simulations * Avoid type indirection when setting up data object entry points * Fixed issues with SIMPUT files * Fixed loading athena data in python3 with provided parameters * Tipsy cosmology unit fixes * Fixed bad unit labels for compound units * Making the xlim and ylim of the PhasePlot plot axes controllable * Adding grid_arrays to grid_container * An Athena and a GDF bugfix * A small bugfix and some small enhancements for sunyaev_zeldovich * Defer to coordinate handlers for width * Make array_like_field return same units as get_data * Fixing bug in ray "dts" and "t" fields * Check against string_types not str * Closed a loophole that allowed improper LightRay use * Enabling AbsorptionSpectrum to deposit unresolved spectral lines * Fixed an ART byte/string/array issue * Changing AbsorptionSpectrum attribute lambda_bins to be lambda_field for consistency * No longer require user to save to disk when generating an AbsorptionSpectrum * ParticlePlot FRBs can now use save_as_dataset and save attributes properly * Added checks to assure ARTIO creates a metal_density field from existing metal fields. * Added mask to LightRay to assure output elements have non-zero density (a problem in some SPH datasets) * Added a "fields" attribute to datasets * Updated the TransferFunctionHelper to work with new profiles * Fixed a bug where the field_units kwarg to load_amr_grids didn't do anything * Changed photon_simulator's output file structure * Fixed a bug related to setting output_units. * Implemented ptp operation. * Added effects of transverse doppler redshift to LightRay * Fixed a casting error for float and int64 multiplication in sdf class * Added ability to read and write YTArrays to and from groups within HDF5 files * Made ftype of "on-disk" stream fields "stream" * Fixed a strings decoding issue in the photon simulator * Fixed an incorrect docstring in load_uniform_grid * Made PlotWindow show/hide helpers for axes and colorbar return self * Made Profile objects store field metadata. * Ensured GDF unit names are strings * Taught off_axis_projection about its resolution keyword. * Reintroduced sanitize_width for polar/cyl coordinates. * We now fail early when load_uniform_grid is passed data with an incorrect shape * Replaced progress bar with tqdm * Fixed redshift scaling of "Overdensity" field in yt-2.x * Fixed several bugs in the eps_writer * Fixed bug affecting 2D BoxLib simulations. * Implemented to_json and from_json for the UnitRegistry object * Fixed a number of issues with ds.find_field_values_at_point[s] * Fixed a bug where sunrise_exporter was using wrong imports * Import HUGE from utilities.physical_ratios * Fixed bug in ARTIO table look ups * Adding support for longitude and latitude * Adding halo data containers for gadget_fof frontend. * Can now compare YTArrays without copying them * Fixed several bugs related to active particle datasets * Angular_momentum_vector now only includes space for particle fields if they exist. * Image comparison tests now print a meaningful error message if they fail. * Fixed numpy 1.11 compatibility issues. * Changed _skip_cache to be True by default. * Enable support for spherical datasets in the BoxLib frontend. * Fixed a bug in add_deposited_particle_field. * Fixed issues with input sanitization in the point data object. * Fixed a copy/paste error introduced by refactoring WeightedMenParticleField * Fixed many formatting issues in the docs build * Now avoid creating particle unions for particle types that have no common fields * Patched ParticlePlot to work with filtered particle fields. * Fixed a couple corner cases in gadget_fof frontend * We now properly normalise all normal vectors in functions that take a normal vector (for e.g. get_sph_theta) * Fixed a bug where the transfer function features were not always getting cleared properly. * Made the Chombo frontend is_valid method smarter. * Added a get_hash() function to yt/funcs.py which returns a hash for a file * Added Sievert to the default unit symbol table * Corrected an issue with periodic "wiggle" in AbsorptionSpectrum instances * Made ``ds.field_list`` sorted by default * Bug fixes for the Nyx frontend * Fixed a bug where the index needed to be created before calling derived quantities * Made latex_repr a property, computed on-demand * Fixed a bug in off-axis slice deposition * Fixed a bug with some types of octree block traversal * Ensured that mpi operations retain ImageArray type instead of downgrading to YTArray parent class * Added a call to _setup_plots in the custom colorbar tickmark example * Fixed two minor bugs in save_annotated * Added ability to specify that DatasetSeries is not a mixed data type * Fixed a memory leak in ARTIO * Fixed copy/paste error in to_frb method. * Ensured that particle dataset max_level is consistent with the index max_level * Fixed an issue where fields were getting added multiple times to field_info.field_list * Enhanced annotate_ray and annotate_arrow callbacks * Added GDF answer tests * Made the YTFieldTypeNotFound exception more informative * Added a new function, fake_vr_orientation_test_ds(), for use in testing * Ensured that instances of subclasses of YTArray have the correct type * Re-enabled max_level for projections, ProjectionPlot, and OffAxisProjectionPlot * Fixed a bug in the Orion 2 field definitions * Fixed a bug caused by matplotlib not being added to install_requires * Edited PhasePlot class to have an annotate_title method * Implemented annotate_cell_edges * Handled KeyboardInterrupt in volume rendering Cython loop * Made old halo finders now accept ptype * Updated the latex commands in yt cheatsheet * Fixed a circular dependency loop bug in abar field definition for FLASH datasets * Added neutral species aliases as described in YTEP 0003 * Fixed a logging issue: don't create a StreamHandler unless we will use it * Correcting how theta and phi are calculated in ``_particle_velocity_spherical_radius``, ``_particle_velocity_spherical_theta``, ``_particle_velocity_cylindrical_radius``, and ``_particle_velocity_cylindrical_theta`` * Fixed a bug related to the field dictionary in ``load_particles`` * Allowed for the special case of supplying width as a tuple of tuples * Made yt compile with MSVC on Windows * Fixed a bug involving mask for dt in octree * Merged the get_yt.sh and install_script.sh into one * Added tests for the install script * Allowed use axis names instead of dimensions for spherical pixelization * Fixed a bug where close() wasn't being called in HDF5FileHandler * Enhanced commandline image upload/delete * Added get_brewer_cmap to get brewer colormaps without importing palettable at the top level * Fixed a bug where a parallel_root_only function was getting called inside another parallel_root_only function * Exit the install script early if python can't import '_ssl' module * Make PlotWindow's annotate_clear method invalidate the plot * Adding int wrapper to avoid deprecation warning from numpy * Automatically create vector fields for magnetic_field * Allow users to completely specify the filename of a 1D profile * Force nose to produce meaningful traceback for cookbook recipes' tests * Fixed x-ray display_name and documentation * Try to guess and load particle file for FLASH dataset * Sped up top-level yt import * Set the field type correctly for fields added as particle fields * Added a position location method for octrees * Fixed a copy/paste error in uhstack function * Made trig functions give correct results when supplied data with dimensions of angle but units that aren't radian * Print out some useful diagnostic information if check_for_openmp() fails * Give user-added derived fields a default field type * Added support for periodicity in annotate_particles. * Added a check for whether returned field has units in volume-weighted smoothed fields * Casting array indices as ints in colormaps infrastructure * Fixed a bug where the standard particle fields weren't getting set up correctly for the Orion frontends * Enabled LightRay to accept loaded datasets instead of just filenames * Allowed for adding or subtracting arrays filled with zeros without checking units. * Fixed a bug in selection for semistructured meshes. * Removed 'io' from enzo particle types for active particle datasets * Added support for FLASH particle datasets. * Silenced a deprecation warning from IPython * Eliminated segfaults in KDTree construction * Fixed add_field handling when passed a tuple * Ensure field parameters are correct for fields that need ghost zones * Made it possible to use DerivedField instances to access data * Added ds.particle_type_counts * Bug fix and improvement for generating Google Cardboard VR in StereoSphericalLens * Made DarkMatterARTDataset more robust in its _is_valid * Added Earth radius to units * Deposit hydrogen fields to grid in gizmo frontend * Switch to index values being int64 * ValidateParameter ensures parameter values are used during field detection * Switched to using cythonize to manage dependencies in the setup script * ProfilePlot style changes and refactoring * Cancel terms with identical LaTeX representations in a LaTeX representation of a unit * Only return early from comparison validation if base values are equal * Enabled particle fields for clump objects * Added validation checks for data types in callbacks * Enabled modification of image axis names in coordinate handlers * Only add OWLS/EAGLE ion fields if they are present * Ensured that PlotWindow plots continue to look the same under matplotlib 2.0 * Fixed bug in quiver callbacks for off-axis slice plots * Only visit octree children if going to next level * Check that CIC always gets at least two cells * Fixed compatibility with matplotlib 1.4.3 and earlier * Fixed two EnzoSimulation bugs * Moved extraction code from YTSearchCmd to its own utility module * Changed amr_kdtree functions to be Node class methods * Sort block indices in order of ascending levels to match order of grid patches * MKS code unit system fixes * Disabled bounds checking on pixelize_element_mesh * Updated light_ray.py for domain width != 1 * Implemented a DOAP file generator * Fixed bugs for 2D and 1D enzo IO * Converted mutable Dataset attributes to be properties that return copies * Allowing LightRay segments to extend further than one box length * Fixed a divide-by-zero error that occasionally happens in triangle_plane_intersect * Make sure we have an index in subclassed derived quantities * Added an initial draft of an extensions document * Made it possible to pass field tuples to command-line plotting * Ensured the positions of coordinate vector lines are in code units * Added a minus sign to definition of sz_kinetic field * Added grid_levels and grid_indices fields to octrees * Added a morton_index derived field * Added Exception to AMRKDTree in the case of particle of oct-based data Version 3.2 ----------- Major enhancements ^^^^^^^^^^^^^^^^^^ * Particle-Only Plots - a series of new plotting functions for visualizing particle data. See here for more information. * Late-stage beta support for Python 3 - unit tests and answer tests pass for all the major frontends under python 3.4, and yt should now be mostly if not fully usable. Because many of the yt developers are still on Python 2 at this point, this should be considered a "late stage beta" as there may be remaining issues yet to be identified or worked out. * Now supporting Gadget Friend-of-Friends/Subfind catalogs - see here to learn how to load halo catalogs as regular yt datasets. * Custom colormaps can now be easily defined and added - see here to learn how! * Now supporting Fargo3D data * Performance improvements throughout the code base for memory and speed Minor enhancements ^^^^^^^^^^^^^^^^^^ * Various updates to the following frontends: ART, Athena, Castro, Chombo, Gadget, GDF, Maestro, Pluto, RAMSES, Rockstar, SDF, Tipsy * Numerous documentation updates * Generic hexahedral mesh pixelizer * Adding annotate_ray() callback for plots * AbsorptionSpectrum returned to full functionality and now using faster SciPy Voigt profile * Add a color_field argument to annotate_streamline * Smoothing lengths auto-calculated for Tipsy Datasets * Adding SimulationTimeSeries support for Gadget and OWLS. * Generalizing derived quantity outputs to all be YTArrays or lists of YTArrays as appropriate * Star analysis returned to full functionality * FITS image writing refactor * Adding gradient fields on the fly * Adding support for Gadget Nx4 metallicity fields * Updating value of solar metal mass fraction to be consistent with Cloudy. * Gadget raw binary snapshot handling & non-cosmological simulation units * Adding support for LightRay class to work with Gadget+Tipsy * Add support for subclasses of frontends * Dependencies updated * Serialization for projections using minimal representation * Adding Grid visitors in Cython * Improved semantics for derived field units * Add a yaw() method for the PerspectiveCamera + switch back to LHS * Adding annotate_clear() function to remove previous callbacks from a plot * Added documentation for hexahedral mesh on website * Speed up nearest neighbor evaluation * Add a convenience method to create deposited particle fields * UI and docs updates for 3D streamlines * Ensure particle fields are tested in the field unit tests * Allow a suffix to be specified to save() * Add profiling using airspeed velocity * Various plotting enhancements and bugfixes * Use hglib to update * Various minor updates to halo_analysis toolkit * Docker-based tests for install_script.sh * Adding support for single and non-cosmological datasets to LightRay * Adding the Pascal unit * Add weight_field to PPVCube * FITS reader: allow HDU in auxiliary * Fixing electromagnetic units * Specific Angular Momentum [xyz] computed relative to a normal vector Bugfixes ^^^^^^^^ * Adding ability to create union fields from alias fields * Small fix to allow enzo AP datasets to load in parallel when no APs present * Use proper cell dimension in gradient function. * Minor memory optimization for smoothed particle fields * Fix thermal_energy for Enzo HydroMethod==6 * Make sure annotate_particles handles unitful widths properly * Improvements for add_particle_filter and particle_filter * Specify registry in off_axis_projection's image finalization * Apply fix for particle momentum units to the boxlib frontend * Avoid traceback in "yt version" when python-hglib is not installed * Expose no_ghost from export_sketchfab down to _extract_isocontours_from_grid * Fix broken magnetic_unit attribute * Fixing an off-by-one error in the set x/y lim methods for profile plots * Providing better error messages to PlotWindow callbacks * Updating annotate_timestamp to avoid auto-override * Updating callbacks to consistently define coordinate system * Fixing species fields for OWLS and tipsy * Fix extrapolation for vertex-centered data * Fix periodicity check in FRBs * Rewrote project_to_plane() in PerspectiveCamera for draw_domain() * Fix intermittent failure in test_add_deposited_particle_field * Improve minorticks for a symlog plot with one-sided data * Fix smoothed covering grid cell computation * Absorption spectrum generator now 3.0 compliant * Fix off-by-one-or-more in particle smallest dx * Fix dimensionality mismatch error in covering grid * Fix curvature term in cosmology calculator * Fix geographic axes and pixelization * Ensure axes aspect ratios respect the user-selected plot aspect ratio * Avoid clobbering field_map when calling profile.add_fields * Fixing the arbitrary grid deposit code * Fix spherical plotting centering * Make the behavior of to_frb consistent with the docstring * Ensure projected units are initialized when there are no chunks. * Removing "field already exists" warnings from the Owls and Gadget frontends * Various photon simulator bugs * Fixed use of LaTeX math mode * Fix upload_image * Enforce plot width in CSS when displayed in a notebook * Fix cStringIO.StringIO -> cStringIO in png_writer * Add some input sanitizing and error checking to covering_grid initializer * Fix for geographic plotting * Use the correct filename template for single-file OWLS datasets. * Fix Enzo IO performance for 32 bit datasets * Adding a number density field for Enzo MultiSpecies=0 datasets. * Fix RAMSES block ordering * Updating ragged array tests for NumPy 1.9.1 * Force returning lists for HDF5FileHandler Version 3.1 ----------- This is a scheduled feature release. Below are the itemized, aggregate changes since version 3.0. Major changes: ^^^^^^^^^^^^^^ * The RADMC-3D export analysis module has been updated. `PR 1358 `_, `PR 1332 `_. * Performance improvements for grid frontends. `PR 1350 `_. `PR 1382 `_, `PR 1322 `_. * Added a frontend for Dark Matter-only NMSU Art simulations. `PR 1258 `_. * The absorption spectrum generator has been updated. `PR 1356 `_. * The PerspectiveCamera has been updated and a new SphericalCamera has been added. `PR 1346 `_, `PR 1299 `_. * The unit system now supports unit equivalencies and has improved support for MKS units. See :ref:`unit_equivalencies`. `PR 1291 `_, `PR 1286 `_. * Data object selection can now be chained, allowing selecting based on multiple constraints. `PR 1264 `_. * Added the ability to manually override the simulation unit system. `PR 1236 `_. * The documentation has been reorganized and has seen substantial improvements. `PR 1383 `_, `PR 1373 `_, `PR 1364 `_, `PR 1351 `_, `PR 1345 `_. `PR 1333 `_, `PR 1342 `_, `PR 1338 `_, `PR 1330 `_, `PR 1326 `_, `PR 1323 `_, `PR 1315 `_, `PR 1305 `_, `PR 1289 `_, `PR 1276 `_. Minor or bugfix changes: ^^^^^^^^^^^^^^^^^^^^^^^^ * The Ampere unit now accepts SI prefixes. `PR 1393 `_. * The Gadget InternalEnergy and StarFormationRate fields are now read in with the correct units. `PR 1392 `_, `PR 1379 `_. * Substantial improvements for the PPVCube analysis module and support for FITS dataset. `PR 1390 `_, `PR 1367 `_, `PR 1347 `_, `PR 1326 `_, `PR 1280 `_, `PR 1336 `_. * The center of a PlotWindow plot can now be set to the maximum or minimum of any field. `PR 1280 `_. * Fixes for yt testing infrastructure. `PR 1388 `_, `PR 1348 `_. * Projections are now performed using an explicit path length field for all coordinate systems. `PR 1307 `_. * An example notebook for simulations using the OWLS data format has been added to the documentation. `PR 1386 `_. * Fix for the camera.draw_line function. `PR 1380 `_. * Minor fixes and improvements for yt plots. `PR 1376 `_, `PR 1374 `_, `PR 1288 `_, `PR 1290 `_. * Significant documentation reorganization and improvement. `PR 1375 `_, `PR 1359 `_. * Fixed a conflict in the CFITSIO library used by the x-ray analysis module. `PR 1365 `_. * Miscellaneous code cleanup. `PR 1371 `_, `PR 1361 `_. * yt now hooks up to the python logging infrastructure in a more standard fashion, avoiding issues with yt logging showing up with using other libraries. `PR 1355 `_, `PR 1362 `_, `PR 1360 `_. * The docstring for the projection data object has been corrected. `PR 1366 `_ * A bug in the calculation of the plot bounds for off-axis slice plots has been fixed. `PR 1357 `_. * Improvements for the yt-rockstar interface. `PR 1352 `_, `PR 1317 `_. * Fix issues with plot positioning with saving to postscript or encapsulated postscript. `PR 1353 `_. * It is now possible to supply a default value for get_field_parameter. `PR 1343 `_. * A bug in the interpretation of the units of RAMSES simulations has been fixed. `PR 1335 `_. * Plot callbacks are now only executed once before the plot is saved. `PR 1328 `_. * Performance improvements for smoothed covering grid alias fields. `PR 1331 `_. * Improvements and bugfixes for the halo analysis framework. `PR 1349 `_, `PR 1325 `_. * Fix issues with the default setting for the ``center`` field parameter. `PR 1327 `_. * Avoid triggering warnings in numpy and matplotlib. `PR 1334 `_, `PR 1300 `_. * Updates for the field list reference. `PR 1344 `_, `PR 1321 `_, `PR 1318 `_. * yt can now be run in parallel on a subset of available processors using an MPI subcommunicator. `PR 1340 `_ * Fix for incorrect units when loading an Athena simulation as a time series. `PR 1341 `_. * Improved support for Enzo 3.0 simulations that have not produced any active particles. `PR 1329 `_. * Fix for parsing OWLS outputs with periods in the file path. `PR 1320 `_. * Fix for periodic radius vector calculation. `PR 1311 `_. * Improvements for the Maestro and Castro frontends. `PR 1319 `_. * Clump finding is now supported for more generic types of data. `PR 1314 `_ * Fix unit consistency issue when mixing dimensionless unit symbols. `PR 1300 `_. * Improved memory footprint in the photon_simulator. `PR 1304 `_. * Large grids in Athena datasets produced by the join_vtk script can now be optionally split, improving parallel performance. `PR 1304 `_. * Slice plots now accept a ``data_source`` keyword argument. `PR 1310 `_. * Corrected inconsistent octrees in the RAMSES frontend. `PR 1302 `_ * Nearest neighbor distance field added. `PR 1138 `_. * Improvements for the ORION2 frontend. `PR 1303 `_ * Enzo 3.0 frontend can now read active particle attributes that are arrays of any shape. `PR 1248 `_. * Answer tests added for halo finders. `PR 1253 `_ * A ``setup_function`` has been added to the LightRay initializer. `PR 1295 `_. * The SPH code frontends have been reorganized into separate frontend directories. `PR 1281 `_. * Fixes for accessing deposit fields for FLASH data. `PR 1294 `_ * Added tests for ORION datasets containing sink and star particles. `PR 1252 `_ * Fix for field names in the particle generator. `PR 1278 `_. * Added wrapper functions for numpy array manipulation functions. `PR 1287 `_. * Added support for packed HDF5 Enzo datasets. `PR 1282 `_. Version 3.0 ----------- This release of yt features an entirely rewritten infrastructure for data ingestion, indexing, and representation. While past versions of yt were focused on analysis and visualization of data structured as regular grids, this release features full support for particle (discrete point) data such as N-body and SPH data, irregular hexahedral mesh data, and data organized via octrees. This infrastructure will be extended in future versions for high-fidelity representation of unstructured mesh datasets. Highlighted changes in yt 3.0: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Units now permeate the code base, enabling self-consistent unit transformations of all arrays and quantities returned by yt. * Particle data is now supported using a lightweight octree. SPH data can be smoothed onto an adaptively-defined mesh using standard SPH smoothing * Support for octree AMR codes * Preliminary Support for non-Cartesian data, such as cylindrical, spherical, and geographical * Revamped analysis framework for halos and halo catalogs, including direct ingestion and analysis of halo catalogs of several different formats * Support for multi-fluid datasets and datasets containing multiple particle types * Flexible support for dynamically defining new particle types using filters on existing particle types or by combining different particle types. * Vastly improved support for loading generic grid, AMR, hexahedral mesh, and particle without hand-coding a frontend for a particular data format. * New frontends for ART, ARTIO, Boxlib, Chombo, FITS, GDF, Subfind, Rockstar, Pluto, RAMSES, SDF, Gadget, OWLS, PyNE, Tipsy, as well as rewritten frontends for Enzo, FLASH, Athena, and generic data. * First release to support installation of yt on Windows * Extended capabilities for construction of simulated observations, and new facilities for analyzing and visualizing FITS images and cube data * Many performance improvements This release is the first of several; while most functionality from the previous generation of yt has been updated to work with yt 3.0, it does not yet have feature parity in all respects. While the core of yt is stable, we suggest the support for analysis modules and volume rendering be viewed as a late-stage beta, with a series of additional releases (3.1, 3.2, etc) appearing over the course of the next year to improve support in these areas. For a description of how to bring your 2.x scripts up to date to 3.0, and a summary of common gotchas in this transition, please see :ref:`yt3differences`. Version 2.6 ----------- This is a scheduled release, bringing to a close the development in the 2.x series. Below are the itemized, aggregate changes since version 2.5. Major changes: ^^^^^^^^^^^^^^ * yt is now licensed under the 3-clause BSD license. * HEALPix has been removed for the time being, as a result of licensing incompatibility. * The addition of a frontend for the Pluto code * The addition of an OBJ exporter to enable transparent and multi-surface exports of surfaces to Blender and Sketchfab * New absorption spectrum analysis module with documentation * Adding ability to draw lines with Grey Opacity in volume rendering * Updated physical constants to reflect 2010 CODATA data * Dependency updates (including IPython 1.0) * Better notebook support for yt plots * Considerably (10x+) faster kD-tree building for volume rendering * yt can now export to RADMC3D * Athena frontend now supports Static Mesh Refinement and units ( http://hub.yt-project.org/nb/7l1zua ) * Fix long-standing bug for plotting arrays with range of zero * Adding option to have interpolation based on non-uniform bins in interpolator code * Upgrades to most of the dependencies in the install script * ProjectionPlot now accepts a data_source keyword argument Minor or bugfix changes: ^^^^^^^^^^^^^^^^^^^^^^^^ * Fix for volume rendering on the command line * map_to_colormap will no longer return out-of-bounds errors * Fixes for dds in covering grid calculations * Library searching for build process is now more reliable * Unit fix for "VorticityGrowthTimescale" field * Pyflakes stylistic fixes * Number density added to FLASH * Many fixes for Athena frontend * Radius and ParticleRadius now work for reduced-dimensionality datasets * Source distributions now work again! * Athena data now 64 bits everywhere * Grids displays on plots are now shaded to reflect the level of refinement * show_colormaps() is a new function for displaying all known colormaps * PhasePlotter by default now adds a colormap. * System build fix for POSIX systems * Fixing domain offsets for halo centers-of-mass * Removing some Enzo-specific terminology in the Halo Mass Function * Addition of coordinate vectors on volume render * Pickling fix for extracted regions * Addition of some tracer particle annotation functions * Better error message for "yt" command * Fix for radial vs poloidal fields * Piernik 2D data handling fix * Fixes for FLASH current redshift * PlotWindows now have a set_font function and a new default font setting * Colorbars less likely to extend off the edge of a PlotWindow * Clumps overplotted on PlotWindows are now correctly contoured * Many fixes to light ray and profiles for integrated cosmological analysis * Improvements to OpenMP compilation * Typo in value for km_per_pc (not used elsewhere in the code base) has been fixed * Enable parallel IPython notebook sessions ( http://hub.yt-project.org/nb/qgn19h ) * Change (~1e-6) to particle_density deposition, enabling it to be used by FLASH and other frontends * Addition of is_root function for convenience in parallel analysis sessions * Additions to Orion particle reader * Fixing TotalMass for case when particles not present * Fixing the density threshold or HOP and pHOP to match the merger tree * Reason can now plot with latest plot window * Issues with VelocityMagnitude and aliases with velo have been corrected in the FLASH frontend * Halo radii are calculated correctly for domains that do not start at 0,0,0. * Halo mass function now works for non-Enzo frontends. * Bug fixes for directory creation, typos in docstrings * Speed improvements to ellipsoidal particle detection * Updates to FLASH fields * CASTRO frontend bug fixes * Fisheye camera bug fixes * Answer testing now includes plot window answer testing * Athena data serialization * load_uniform_grid can now decompose dims >= 1024. (#537) * Axis unit setting works correctly for unit names (#534) * ThermalEnergy is now calculated correctly for Enzo MHD simulations (#535) * Radius fields had an asymmetry in periodicity calculation (#531) * Boolean regions can now be pickled (#517) Version 2.5 ----------- Many below-the-surface changes happened in yt 2.5 to improve reliability, fidelity of the answers, and streamlined user interface. The major change in this release has been the immense expansion in testing of yt. We now have over 2000 unit tests (run on every commit, thanks to both Kacper Kowalik and Shining Panda) as well as answer testing for FLASH, Enzo, Chombo and Orion data. The Stream frontend, which can construct datasets in memory, has been improved considerably. It's now easier than ever to load data from disk. If you know how to get volumetric data into Python, you can use either the ``load_uniform_grid`` function or the ``load_amr_grid`` function to create an in-memory dataset that yt can analyze. yt now supports the Athena code. yt is now focusing on providing first class support for the IPython notebook. In this release, plots can be displayed inline. The Reason HTML5 GUI will be merged with the IPython notebook in a future release. Install Script Changes: ^^^^^^^^^^^^^^^^^^^^^^^ * SciPy can now be installed * Rockstar can now be installed * Dependencies can be updated with "yt update --all" * Cython has been upgraded to 0.17.1 * Python has been upgraded to 2.7.3 * h5py has been upgraded to 2.1.0 * hdf5 has been upgraded to 1.8.9 * matplotlib has been upgraded to 1.2.0 * IPython has been upgraded to 0.13.1 * Forthon has been upgraded to 0.8.10 * nose has been added * sympy has been added * python-hglib has been added We've also improved support for installing on OSX, Ubuntu and OpenSUSE. Most Visible Improvements ^^^^^^^^^^^^^^^^^^^^^^^^^ * Nearly 200 pull requests and over 1000 changesets have been merged since yt 2.4 was release on August 2nd, 2012. * numpy is now imported as np, not na. na will continue to work for the foreseeable future. * You can now get a `yt cheat sheet `_! * yt can now load simulation data created by Athena. * The Rockstar halo finder can now be installed by the install script * SciPy can now be installed by the install script * Data can now be written out in two ways: * Sidecar files containing expensive derived fields can be written and implicitly loaded from. * GDF files, which are portable yt-specific representations of full simulations, can be created from any dataset. Work is underway on a pure C library that can be linked against to load these files into simulations. * The "Stream" frontend, for loading raw data in memory, has been greatly expanded and now includes initial conditions generation functionality, particle fields, and simple loading of AMR grids with ``load_amr_grids``. * Spherical and Cylindrical fields have been sped up and made to have a uniform interface. These fields can be the building blocks of more advanced fields. * Coordinate transformations have been sped up and streamlined. It is now possible to convert any scalar or vector field to a new cartesian, spherical, or cylindrical coordinate system with an arbitrary orientation. This makes it possible to do novel analyses like profiling the toroidal and poloidal velocity as a function of radius in an inclined disk. * Many improvements to the EnzoSimulation class, which can now find many different types of data. * Image data is now encapsulated in an ImageArray class, which carries with it provenance information about its trajectory through yt. * Streamlines now query at every step along the streamline, not just at every cell. * Surfaces can now be extracted and examined, as well as uploaded to Sketchfab.com for interactive visualization in a web browser. * allsky_projection can now accept a datasource, making it easier to cut out regions to examine. * Many, many improvements to PlotWindow. If you're still using PlotCollection, check out ``ProjectionPlot``, ``SlicePlot``, ``OffAxisProjectionPlot`` and ``OffAxisSlicePlot``. * PlotWindow can now accept a timeseries instead of a dataset. * Many fixes for 1D and 2D data, especially in FLASH datasets. * Vast improvements to the particle file handling for FLASH datasets. * Particles can now be created ex nihilo with CICSample_3. * Rockstar halo finding is now a targeted goal. Support for using Rockstar has improved dramatically. * Increased support for tracking halos across time using the FOF halo finder. * The command ``yt notebook`` has been added to spawn an IPython notebook server, and the ``yt.imods`` module can replace ``yt.mods`` in the IPython Notebook to enable better integration. * Metallicity-dependent X-ray fields have now been added. * Grid lines can now be added to volume renderings. * Volume rendering backend has been updated to use an alpha channel, fixing parallel opaque volume renderings. This also enables easier blending of multiple images and annotations to the rendering. Users are encouraged to look at the capabilities of the ``ImageArray`` for writing out renders, as updated in the cookbook examples. Volume renders can now be saved with an arbitrary background color. * Periodicity, or alternately non-periodicity, is now a part of radius calculations. * The AMRKDTree has been rewritten. This allows parallelism with other than power-of-2 MPI processes, arbitrary sets of grids, and splitting of unigrids. * Fixed Resolution Buffers and volume rendering images now utilize a new ImageArray class that stores information such as data source, field names, and other information in a .info dictionary. See the ``ImageArray`` docstrings for more information on how they can be used to save to a bitmap or hdf5 file. Version 2.4 ----------- The 2.4 release was particularly large, encompassing nearly a thousand changesets and a number of new features. To help you get up to speed, we've made an IPython notebook file demonstrating a few of the changes to the scripting API. You can `download it here `_. Most Visible Improvements ^^^^^^^^^^^^^^^^^^^^^^^^^ * Threaded volume renderer, completely refactored from the ground up for speed and parallelism. * The Plot Window (see :ref:`simple-inspection`) is now fully functional! No more PlotCollections, and full, easy access to Matplotlib axes objects. * Many improvements to Time Series analysis: * EnzoSimulation now integrates with TimeSeries analysis! * Auto-parallelization of analysis and parallel iteration * Memory usage when iterating over datasets reduced substantially * Many improvements to Reason, the yt GUI * Addition of "yt reason" as a startup command * Keyboard shortcuts in projection & slice mode: z, Z, x, X for zooms, hjkl, HJKL for motion * Drag to move in projection & slice mode * Contours and vector fields in projection & slice mode * Color map selection in projection & slice mode * 3D Scene * Integration with the all new yt Hub ( http://hub.yt-project.org/ ): upload variable resolution projections, slices, project information, vertices and plot collections right from the yt command line! Other Changes ^^^^^^^^^^^^^ * :class:`~yt.visualization.plot_window.ProjectionPlot` and :class:`~yt.visualization.plot_window.SlicePlot` supplant the functionality of PlotCollection. * Camera path creation from keyframes and splines * Ellipsoidal data containers and ellipsoidal parameter calculation for halos * PyX and ZeroMQ now available in the install script * Consolidation of unit handling * HDF5 updated to 1.8.7, Mercurial updated to 2.2, IPython updated to 0.12 * Preview of integration with Rockstar halo finder * Improvements to merger tree speed and memory usage * Sunrise exporter now compatible with Sunrise 4.0 * Particle trajectory calculator now available! * Speed and parallel scalability improvements in projections, profiles and HOP * New Vorticity-related fields * Vast improvements to the ART frontend * Many improvements to the FLASH frontend, including full parameter reads, speedups, and support for more corner cases of FLASH 2, 2.5 and 3 data. * Integration of the Grid Data Format frontend, and a converter for Athena data to this format. * Improvements to command line parsing * Parallel import improvements on parallel filesystems (``from yt.pmods import *``) * proj_style keyword for projections, for Maximum Intensity Projections (``proj_style = "mip"``) * Fisheye rendering for planetarium rendering * Profiles now provide \*_std fields for standard deviation of values * Generalized Orientation class, providing 6DOF motion control * parallel_objects iteration now more robust, provides optional barrier. (Also now being used as underlying iteration mechanism in many internal routines.) * Dynamic load balancing in parallel_objects iteration. * Parallel-aware objects can now be pickled. * Many new colormaps included * Numerous improvements to the PyX-based eps_writer module * FixedResolutionBuffer to FITS export. * Generic image to FITS export. * Multi-level parallelism for extremely large cameras in volume rendering * Light cone and light ray updates to fit with current best practices for parallelism Version 2.3 ----------- `(yt 2.3 docs) `_ * Multi-level parallelism * Real, extensive answer tests * Boolean data regions (see :ref:`boolean_data_objects`) * Isocontours / flux calculations (see :ref:`extracting-isocontour-information`) * Field reorganization * PHOP memory improvements * Bug fixes for tests * Parallel data loading for RAMSES, along with other speedups and improvements there * WebGL interface for isocontours and a pannable map widget added to Reason * Performance improvements for volume rendering * Adaptive HEALPix support * Column density calculations * Massive speedup for 1D profiles * Lots more, bug fixes etc. * Substantial improvements to the documentation, including :ref:`manual-plotting` and a revamped orientation. Version 2.2 ----------- `(yt 2.2 docs) `_ * Command-line submission to the yt Hub (http://hub.yt-project.org/) * Initial release of the web-based GUI Reason, designed for efficient remote usage over SSH tunnels * Absorption line spectrum generator for cosmological simulations (see :ref:`absorption_spectrum`) * Interoperability with ParaView for volume rendering, slicing, and so forth * Support for the Nyx code * An order of magnitude speed improvement in the RAMSES support * Quad-tree projections, speeding up the process of projecting by up to an order of magnitude and providing better load balancing * "mapserver" for in-browser, Google Maps-style slice and projection visualization (see :ref:`mapserver`) * Many bug fixes and performance improvements * Halo loader Version 2.1 ----------- `(yt 2.1 docs) `_ * HEALPix-based volume rendering for 4pi, allsky volume rendering * libconfig is now included * SQLite3 and Forthon now included by default in the install script * Development guide has been lengthened substantially and a development bootstrap script is now included. * Installation script now installs Python 2.7 and HDF5 1.8.6 * iyt now tab-completes field names * Halos can now be stored on-disk much more easily between HaloFinding runs. * Halos found inline in Enzo can be loaded and merger trees calculated * Support for CASTRO particles has been added * Chombo support updated and fixed * New code contributions * Contour finder has been sped up by a factor of a few * Constrained two-point functions are now possible, for LOS power spectra * Time series analysis (:ref:`time-series-analysis`) now much easier * Stream Lines now a supported 1D data type * Stream Lines now able to be calculated and plotted (:ref:`streamlines`) * In situ Enzo visualization now much faster * "gui" source directory reorganized and cleaned up * Cython now a compile-time dependency, reducing the size of source tree updates substantially * ``yt-supplemental`` repository now checked out by default, containing cookbook, documentation, handy mercurial extensions, and advanced plotting examples and helper scripts. * Pasteboards now supported and available * Parallel yt efficiency improved by removal of barriers and improvement of collective operations Version 2.0 ----------- * Major reorganization of the codebase for speed, ease of modification, and maintainability * Re-organization of documentation and addition of Orientation Session * Support for FLASH code * Preliminary support for MAESTRO, CASTRO, ART, and RAMSES (contributions welcome!) * Perspective projection for volume rendering * Exporting to Sunrise * Preliminary particle rendering in volume rendering visualization * Drastically improved parallel volume rendering, via kD-tree decomposition * Simple merger tree calculation for FOF catalogs * New and greatly expanded documentation, with a "source" button Version 1.7 ----------- * Direct writing of PNGs * Multi-band image writing * Parallel halo merger tree (see :ref:`merger_tree`) * Parallel structure function generator (see :ref:`two_point_functions`) * Image pan and zoom object and display widget. * Parallel volume rendering (see :ref:`volume_rendering`) * Multivariate volume rendering, allowing for multiple forms of emission and absorption, including approximate scattering and Planck emissions. (see :ref:`volume_rendering`) * Added Camera interface to volume rendering (See :ref:`volume_rendering`) * Off-axis projection (See :ref:`volume_rendering`) * Stereo (toe-in) volume rendering (See :ref:`volume_rendering`) * DualEPS extension for better EPS construction * yt now uses Distribute instead of SetupTools * Better ``iyt`` initialization for GUI support * Rewritten, memory conservative and speed-improved contour finding algorithm * Speed improvements to volume rendering * Preliminary support for the Tiger code * Default colormap is now ``algae`` * Lightweight projection loading with ``projload`` * Improvements to ``yt.data_objects.time_series`` * Improvements to :class:`yt.extensions.EnzoSimulation` (See :ref:`analyzing-an-entire-simulation`) * Removed ``direct_ray_cast`` * Fixed bug causing double data-read in projections * Added Cylinder support to ParticleIO * Fixes for 1- and 2-D Enzo datasets * Preliminary, largely non-functional Gadget support * Speed improvements to basic HOP * Added physical constants module * Beginning to standardize and enforce docstring requirements, changing to ``autosummary``-based API documentation. Version 1.6.1 ------------- * Critical fixes to ParticleIO * Halo mass function fixes for comoving coordinates * Fixes to halo finding * Fixes to the installation script * "yt instinfo" command to report current installation information as well as auto-update some types of installations * Optimizations to the volume renderer (2x-26x reported speedups) Version 1.6 ----------- Version 1.6 is a point release, primarily notable for the new parallel halo finder (see :ref:`halo-analysis`) * (New) Parallel HOP ( https://arxiv.org/abs/1001.3411 , :ref:`halo-analysis` ) * (Beta) Software ray casting and volume rendering (see :ref:`volume_rendering`) * Rewritten, faster and better contouring engine for clump identification * Spectral Energy Distribution calculation for stellar populations (see :ref:`synthetic_spectrum`) * Optimized data structures such as the index * Star particle analysis routines (see :ref:`star_analysis`) * Halo mass function routines * Completely rewritten, massively faster and more memory efficient Particle IO * Fixes for plots, including normalized phase plots * Better collective communication in parallel routines * Consolidation of optimized C routines into ``amr_utils`` * Many bug fixes and minor optimizations Version 1.5 ----------- Version 1.5 features many new improvements, most prominently that of the addition of parallel computing abilities (see :ref:`parallel-computation`) and generalization for multiple AMR data formats, specifically both Enzo and Orion. * Rewritten documentation * Fully parallel slices, projections, cutting planes, profiles, quantities * Parallel HOP * Friends-of-friends halo finder * Object storage and serialization * Major performance improvements to the clump finder (factor of five) * Generalized domain sizes * Generalized field info containers * Dark Matter-only simulations * 1D and 2D simulations * Better IO for HDF5 sets * Support for the Orion AMR code * Spherical re-gridding * Halo profiler * Disk image stacker * Light cone generator * Callback interface improved * Several new callbacks * New data objects -- ortho and non-ortho rays, limited ray-tracing * Fixed resolution buffers * Spectral integrator for CLOUDY data * Substantially better interactive interface * Performance improvements *everywhere* * Command-line interface to *many* common tasks * Isolated plot handling, independent of PlotCollections Version 1.0 ----------- * Initial release! yt-project-yt-f043ac8/doc/source/reference/code_support.rst000066400000000000000000000231341510711153200241200ustar00rootroot00000000000000 .. _code-support: Code Support ============ Levels of Support for Various Codes ----------------------------------- yt provides frontends to support several different simulation code formats as inputs. Below is a list showing what level of support is provided for each code. See :ref:`loading-data` for examples of loading a dataset from each supported output format using yt. +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Capability â–º | Fluid | Particles | Parameters | Units | Read on | Load Raw | Part of | Level of | | Code/Format â–¼ | Quantities | | | | Demand | Data | test suite | Support | +=======================+============+===========+============+=======+==========+==========+============+=============+ | AMRVAC | Y | N | Y | Y | Y | Y | Y | Partial | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | AREPO | Y | Y | Y | Y | Y | Y | Y | Full [#f4]_ | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | ART | Y | Y | Y | Y | Y [#f2]_ | Y | N | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | ARTIO | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Athena | Y | N | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Athena++ | Y | N | Y | Y | Y | Y | Y | Partial | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Castro | Y | Y [#f3]_ | Partial | Y | Y | Y | N | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | CfRadial | Y | N/A | Y | Y | Y | Y | Y | [#f5]_ | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | CHOLLA | Y | N/A | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Chombo | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Enzo | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Enzo-E | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Exodus II | ? | ? | ? | ? | ? | ? | ? | ? | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | FITS | Y | N/A | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | FLASH | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Gadget | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | GAMER | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Gasoline | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Gizmo | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Grid Data Format (GDF)| Y | N/A | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | IAMR | ? | ? | ? | ? | ? | ? | ? | ? | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Maestro | Y [#f1]_ | N | Y | Y | Y | Y | N | Partial | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | MOAB | Y | N/A | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Nyx | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | openPMD | Y | Y | N | Y | Y | Y | N | Partial | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Orion | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | OWLS/EAGLE | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Parthenon | Y | N | Y | Y | Y | Y | Y | Partial | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Piernik | Y | N/A | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Pluto | Y | N | Y | Y | Y | Y | Y | Partial | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | RAMSES | Y | Y | Y | Y | Y [#f2]_ | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | Tipsy | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ | WarpX | Y | Y | Y | Y | Y | Y | Y | Full | +-----------------------+------------+-----------+------------+-------+----------+----------+------------+-------------+ .. [#f1] one-dimensional base-state not read in currently. .. [#f2] These handle mesh fields using an in-memory octree that has not been parallelized. Datasets larger than approximately 1024^3 will not scale well. .. [#f3] Newer versions of Castro that use BoxLib's standard particle format are supported. The older ASCII format is not. .. [#f4] The Voronoi cells are currently treated as SPH-like particles, with a smoothing length proportional to the cube root of the cell volume. .. [#f5] yt provides support for cartesian-gridded CfRadial datasets. Data in native CFRadial coordinates will be gridded on load, see :ref:`loading-cfradial-data`. If you have a dataset that uses an output format not yet supported by yt, you can either input your data following :doc:`../examining/Loading_Generic_Array_Data` or :doc:`../examining/Loading_Generic_Particle_Data`, or help us by :ref:`creating_frontend` for this new format. yt-project-yt-f043ac8/doc/source/reference/command-line.rst000066400000000000000000000222571510711153200237620ustar00rootroot00000000000000.. _command-line: Command-Line Usage ------------------ Command-line Functions ~~~~~~~~~~~~~~~~~~~~~~ The :code:`yt` command-line tool allows you to access some of yt's basic functionality without opening a python interpreter. The tools is a collection of subcommands. These can quickly making plots of slices and projections through a dataset, updating yt's codebase, print basic statistics about a dataset, launch an IPython notebook session, and more. To get a quick list of what is available, just type: .. code-block:: bash yt -h This will print the list of available subcommands, .. config_help:: yt To execute any such function, simply run: .. code-block:: bash yt Finally, to identify the options associated with any of these subcommand, run: .. code-block:: bash yt -h Plotting from the command line ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First, we'll discuss plotting from the command line, then we will give a brief summary of the functionality provided by each command line subcommand. This example uses the :code:`DD0010/moving7_0010` dataset distributed in the yt git repository. First let's see what our options are for plotting: .. code-block:: bash $ yt plot --help There are many! We can choose whether we want a slice (default) or a projection (``-p``), the field, the colormap, the center of the image, the width and unit of width of the image, the limits, the weighting field for projections, and on and on. By default the plotting command will execute the same thing along all three axes, so keep that in mind if it takes three times as long as you'd like! The center of a slice defaults to the center of the domain, so let's just give that a shot and see what it looks like: .. code-block:: bash $ yt plot DD0010/moving7_0010 Well, that looks pretty bad! What has happened here is that the center of the domain only has some minor shifts in density, so the plot is essentially incomprehensible. Let's try it again, but instead of slicing, let's project. This is a line integral through the domain, and for the density field this becomes a column density: .. code-block:: bash $ yt plot -p DD0010/moving7_0010 Now that looks much better! Note that all three axes' projections appear nearly indistinguishable, because of how the two spheres are located in the domain. We could center our domain on one of the spheres and take a slice, as well. Now let's see what the domain looks like with grids overlaid, using the ``--show-grids`` option: .. code-block:: bash $ yt plot --show-grids -p DD0010/moving7_0010 We can now see all the grids in the field of view. If you want to annotate your plot with a scale bar, you can use the ``--show-scale-bar`` option: .. code-block:: bash $ yt plot --show-scale-bar -p DD0010/moving7_0010 Command-line subcommand summary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ help ++++ Help lists all of the various command-line options in yt. instinfo and version ++++++++++++++++++++ This gives information about where your yt installation is, what version and changeset you're using and more. mapserver +++++++++ Ever wanted to interact with your data using a `google maps `_-style interface? Now you can by using the yt mapserver. See :ref:`mapserver` for more details. pastebin and pastebin_grab ++++++++++++++++++++++++++ The `pastebin `_ is an online location where you can anonymously post code snippets and error messages to share with other users in a quick, informal way. It is often useful for debugging code or co-developing. By running the ``pastebin`` subcommand with a text file, you send the contents of that file to an anonymous pastebin; .. code-block:: bash yt pastebin my_script.py By running the ``pastebin_grab`` subcommand with a pastebin number (e.g. 1768), it will grab the contents of that pastebin (e.g. the website http://paste.yt-project.org/show/1768 ) and send it to STDOUT for local use. See :ref:`pastebin` for more information. .. code-block:: bash yt pastebin_grab 1768 upload ++++++ Upload a file to a public curldrop instance. Curldrop is a simple web application that allows you to upload and download files straight from your Terminal with an http client like e.g. curl. It was initially developed by `Kevin Kennell `_ and later forked and adjusted for yt’s needs. After a successful upload you will receive a url that can be used to share the data with other people. .. code-block:: bash yt upload my_file.tar.gz plot ++++ This command generates one or many simple plots for a single dataset. By specifying the axis, center, width, etc. (run ``yt help plot`` for details), you can create slices and projections easily at the command-line. rpdb ++++ Connect to a currently running (on localhost) rpdb session. See :ref:`remote-debugging` for more info. notebook ++++++++ Launches a Jupyter notebook server and prints out instructions on how to open an ssh tunnel to connect to the notebook server with a web browser. This is most useful when you want to run a Jupyter notebook using CPUs on a remote host. stats +++++ This subcommand provides you with some basic statistics on a given dataset. It provides you with the number of grids and cells in each level, the time of the dataset, and the resolution. It is tantamount to calling the ``Dataset.print_stats`` method. Additionally, there is the option to print the minimum, maximum, or both for a given field. The field is assumed to be density by default: .. code-block:: bash yt stats GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150 --max --min or a different field can be specified using the ``-f`` flag: .. code-block:: bash yt stats GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150 --max --min -f gas,temperature The field-related stats output from this command can be directed to a file using the ``-o`` flag: .. code-block:: bash yt stats GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150 --max -o out_stats.dat update ++++++ This subcommand updates the yt installation to the most recent version for your repository (e.g. stable, 2.0, development, etc.). Adding the ``--all`` flag will update the dependencies as well. .. _upload-image: upload_image ++++++++++++ Images are often worth a thousand words, so when you're trying to share a piece of code that generates an image, or you're trying to debug image-generation scripts, it can be useful to send your co-authors a link to the image. This subcommand makes such sharing a breeze. By specifying the image to share, ``upload_image`` automatically uploads it anonymously to the website `imgur.com `_ and provides you with a link to share with your collaborators. Note that the image *must* be in the PNG format in order to use this function. delete_image ++++++++++++ The image uploaded using ``upload_image`` is assigned with a unique hash that can be used to remove it. This subcommand provides an easy way to send a delete request directly to the `imgur.com `_. download ~~~~~~~~ This subcommand downloads a file from https://yt-project.org/data. Using ``yt download``, one can download a file to: * ``"test_data_dir"``: Save the file to the location specified in the ``"test_data_dir"`` configuration entry for test data. * ``"supp_data_dir"``: Save the file to the location specified in the ``"supp_data_dir"`` configuration entry for supplemental data. * Any valid path to a location on disk, e.g. ``/home/jzuhone/data``. Examples: .. code-block:: bash $ yt download apec_emissivity_v2.h5 supp_data_dir .. code-block:: bash $ yt download GasSloshing.tar.gz test_data_dir .. code-block:: bash $ yt download ZeldovichPancake.tar.gz /Users/jzuhone/workspace If the configuration values ``"test_data_dir"`` or ``"supp_data_dir"`` have not been set by the user, an error will be thrown. Config helper ~~~~~~~~~~~~~ The :code:`yt config` command-line tool allows you to modify and access yt's configuration without manually locating and opening the config file in an editor. To get a quick list of available commands, just type: .. code-block:: bash yt config -h This will print the list of available subcommands: .. config_help:: yt config Since yt version 4, the configuration file is located in ``$XDG_CONFIG_HOME/yt/yt.toml`` adhering to the `XDG Base Directory Specification `_. Unless customized, this defaults to ``$HOME/.config/`` on Unix-like systems (macOS, Linux, ...). The old configuration file (``$XDG_CONFIG_HOME/yt/ytrc``) is deprecated. In order to perform an automatic migration of the old config, you are encouraged to run: .. code-block:: bash yt config migrate This will convert your old config file to the toml format. The original file will be moved to ``$XDG_CONFIG_HOME/yt/ytrc.bak``. Examples ++++++++ Listing current content of the config file: .. code-block:: bash $ yt config list [yt] log_level = 50 Obtaining a single config value by name: .. code-block:: bash $ yt config get yt log_level 50 Changing a single config value: .. code-block:: bash $ yt config set yt log_level 10 Removing a single config entry: .. code-block:: bash $ yt config rm yt log_level yt-project-yt-f043ac8/doc/source/reference/configuration.rst000066400000000000000000000265131510711153200242650ustar00rootroot00000000000000Customizing yt: The Configuration and Plugin Files ================================================== yt features ways to customize it to your personal preferences in terms of how much output it displays, loading custom fields, loading custom colormaps, accessing test datasets regardless of where you are in the file system, etc. This customization is done through :ref:`configuration-file` and :ref:`plugin-file` both of which exist in your ``$HOME/.config/yt`` directory. .. _configuration-file: The Configuration ----------------- The configuration is stored in simple text files (in the `toml `_ format). The files allow to set internal yt variables to custom default values to be used in future sessions. The configuration can either be stored :ref:`globally ` or :ref:`locally `. .. _global-conf: Global Configuration ^^^^^^^^^^^^^^^^^^^^ If no local configuration file exists, yt will look for and recognize the file ``$HOME/.config/yt/yt.toml`` as a configuration file, containing several options that can be modified and adjusted to control runtime behavior. For example, a sample ``$HOME/.config/yt/yt.toml`` file could look like: .. code-block:: none [yt] log_level = 1 maximum_stored_datasets = 10000 This configuration file would set the logging threshold much lower, enabling much more voluminous output from yt. Additionally, it increases the number of datasets tracked between instantiations of yt. The configuration file can be managed using the ``yt config --global`` helper. It can list, add, modify and remove options from the configuration file, e.g.: .. code-block:: none $ yt config -h $ yt config list $ yt config set yt log_level 1 $ yt config rm yt maximum_stored_datasets .. _local-conf: Local Configuration ^^^^^^^^^^^^^^^^^^^ yt will look for a file named ``yt.toml`` in the current directory, and upwards in the file tree until a match is found. If so, its options are loaded and any global configuration is ignored. Local configuration files can contain the same options as the global one. Local configuration files can either be edited manually, or alternatively they can be managed using ``yt config --local``. It can list, add, modify and remove options, and display the path to the local configuration file, e.g.: .. code-block:: none $ yt config -h $ yt config list --local $ yt config set --local yt log_level 1 $ yt config rm --local yt maximum_stored_datasets $ yt config print-path --local If no local configuration file is present, these commands will create an (empty) one in the current working directory. Configuration Options At Runtime ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In addition to setting parameters in the configuration file itself, you can set them at runtime. .. warning:: Several parameters are only accessed when yt starts up: therefore, if you want to modify any configuration parameters at runtime, you should execute the appropriate commands at the *very top* of your script! This involves importing the configuration object and then setting a given parameter to be equal to a specific string. Note that even for items that accept integers, floating points and other non-string types, you *must* set them to be a string or else the configuration object will consider them broken. Here is an example script, where we adjust the logging at startup: .. code-block:: python import yt yt.set_log_level(1) ds = yt.load("my_data0001") ds.print_stats() This has the same effect as setting ``log_level = 1`` in the configuration file. Note that a log level of 1 means that all log messages are printed to stdout. To disable logging, set the log level to 50. .. _config-options: Available Configuration Options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following external parameters are available. A number of parameters are used internally. * ``colored_logs`` (default: ``False``): Should logs be colored? * ``default_colormap`` (default: ``cmyt.arbre``): What colormap should be used by default for yt-produced images? * ``plugin_filename`` (default ``my_plugins.py``) The name of our plugin file. * ``log_level`` (default: ``20``): What is the threshold (0 to 50) for outputting log files? * ``test_data_dir`` (default: ``/does/not/exist``): The default path the ``load()`` function searches for datasets when it cannot find a dataset in the current directory. * ``reconstruct_index`` (default: ``True``): If true, grid edges for patch AMR datasets will be adjusted such that they fall as close as possible to an integer multiple of the local cell width. If you are working with a dataset with a large number of grids, setting this to False can speed up loading your dataset possibly at the cost of grid-aligned artifacts showing up in slice visualizations. * ``requires_ds_strict`` (default: ``True``): If true, answer tests wrapped with :func:`~yt.utilities.answer_testing.framework.requires_ds` will raise :class:`~yt.utilities.exceptions.YTUnidentifiedDataType` rather than consuming it if required dataset is not present. * ``serialize`` (default: ``False``): If true, perform automatic :ref:`object serialization ` * ``sketchfab_api_key`` (default: empty): API key for https://sketchfab.com/ for uploading AMRSurface objects. * ``suppress_stream_logging`` (default: ``False``): If true, execution mode will be quiet. * ``stdout_stream_logging`` (default: ``False``): If true, logging is directed to stdout rather than stderr * ``skip_dataset_cache`` (default: ``False``): If true, automatic caching of datasets is turned off. * ``supp_data_dir`` (default: ``/does/not/exist``): The default path certain submodules of yt look in for supplemental data files. .. _per-field-plotconfig: Available per-field Plot Options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It is possible to customize the default behaviour of plots using per-field configuration. The default options for plotting a given field can be specified in the configuration file in ``[plot.field_type.field_name]`` blocks. The available keys are * ``cmap`` (default: ``yt.default_colormap``, see :ref:`config-options`): the colormap to use for the field. * ``log`` (default: ``True``): use a log scale (or symlog if ``linthresh`` is also set). * ``linthresh`` (default: ``None``): if set to a float different than ``None`` and ``log`` is ``True``, use a symlog normalization with the given linear threshold. * ``units`` (defaults to the units of the field): the units to use to represent the field. * ``path_length_units`` (default: ``cm``): the unit of the integration length when doing e.g. projections. This always has the dimensions of a length. Note that this will only be used if ``units`` is also set for the field. The final units will then be ``units*path_length_units``. You can also set defaults for all fields of a given field type by omitting the field name, as illustrated below in the deposit block. .. code-block:: toml [plot.gas.density] cmap = "plasma" log = true units = "mp/cm**3" [plot.gas.velocity_divergence] cmap = "bwr" # use a diverging colormap log = false # and a linear scale [plot.deposit] path_length_units = "kpc" # use kpc for deposition projections .. _per-field-config: Available per-Field Configuration Options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It is possible to set attributes for fields that would typically be set by the frontend source code, such as the aliases for field, the units that field should be expected in, and the display name. This allows individuals to customize what yt expects of a given dataset without modifying the yt source code. For instance, if your dataset has an on-disk field called "particle_extra_field_1" you could specify its units, display name, and what yt should think of it as with: .. code-block:: toml [fields.nbody.particle_extra_field_1] aliases = ["particle_other_fancy_name", "particle_alternative_fancy_name"] units = "code_time" display_name = "Dinosaurs Density" .. _plugin-file: Plugin Files ------------ Plugin files are a means of creating custom fields, quantities, data objects, colormaps, and other code executable functions or classes to be used in future yt sessions without modifying the source code directly. To enable a plugin file, call the function :func:`~yt.funcs.enable_plugins` at the top of your script. Global system plugin file ^^^^^^^^^^^^^^^^^^^^^^^^^ yt will look for and recognize the file ``$HOME/.config/yt/my_plugins.py`` as a plugin file. It is possible to rename this file to ``$HOME/.config/yt/.py`` by defining ``plugin_filename`` in your ``yt.toml`` file, as mentioned above. .. note:: You can tell that your system plugin file is being parsed by watching for a logging message when you import yt. Note that the ``yt load`` command line entry point parses the plugin file. Local project plugin file ^^^^^^^^^^^^^^^^^^^^^^^^^ Optionally, :func:`~yt.funcs.enable_plugins` can be passed an argument to specify a custom location for a plugin file. This can be useful to define project wise customizations. In that use case, any system-level plugin file will be ignored. Plugin File Format ^^^^^^^^^^^^^^^^^^ Plugin files should contain pure Python code. If accessing yt functions and classes they will not require the ``yt.`` prefix, because of how they are loaded. For example, if one created a plugin file containing: .. code-block:: python def _myfunc(field, data): return np.random.random(data["density"].shape) add_field( "random", function=_myfunc, sampling_type="cell", dimensions="dimensionless", units="auto", ) then all of my data objects would have access to the field ``random``. You can also define other convenience functions in your plugin file. For instance, you could define some variables or functions, and even import common modules: .. code-block:: python import os HOMEDIR = "/home/username/" RUNDIR = "/scratch/runs/" def load_run(fn): if not os.path.exists(RUNDIR + fn): return None return load(RUNDIR + fn) In this case, we've written ``load_run`` to look in a specific directory to see if it can find an output with the given name. So now we can write scripts that use this function: .. code-block:: python import yt yt.enable_plugins() my_run = yt.load_run("hotgasflow/DD0040/DD0040") And because we have used ``yt.enable_plugins`` we have access to the ``load_run`` function defined in our plugin file. .. note:: if your convenience function's name colliding with an existing object within yt's namespace, it will be ignored. Note that using the plugins file implies that your script is no longer fully reproducible. If you share your script with someone else and use some of the functionality if your plugins file, you will also need to share your plugins file for someone else to re-run your script properly. Adding Custom Colormaps ^^^^^^^^^^^^^^^^^^^^^^^ To add custom :ref:`colormaps` to your plugin file, you must use the :func:`~yt.visualization.color_maps.make_colormap` function to generate a colormap of your choice and then add it to the plugin file. You can see an example of this in :ref:`custom-colormaps`. Remember that you don't need to prefix commands in your plugin file with ``yt.``, but you'll only be able to access the colormaps when you load the ``yt.mods`` module, not simply ``yt``. yt-project-yt-f043ac8/doc/source/reference/demeshening.rst000066400000000000000000000255351510711153200237070ustar00rootroot00000000000000.. _demeshening: How Particles are Indexed ========================= With yt-4.0, the method by which particles are indexed changed considerably. Whereas in previous versions, particles were indexed based on their position in an octree (the structure of which was determined by particle number density), in yt-4.0 this system was overhauled to utilize a `bitmap index `_ based on a space-filling curve, using a `enhanced word-aligned hybrid `_ boolean array as their backend. .. note:: You may see scattered references to "the demeshening" (including in the filename for this document!). This was a humorous name used in the yt development process to refer to removing a global (octree) mesh for particle codes. By avoiding the use of octrees as a base mesh, yt is able to create *much* more accurate SPH visualizations. We have a `gallery demonstrating this `_ but even in this side-by-side comparison the differences can be seen quite easily, with the left image being from the old, octree-based approach and the right image the new, meshless approach. .. image:: _images/yt3_p0010_proj_density_None_x_z002.png :width: 45 % .. image:: _images/yt4_p0010_proj_density_None_x_z002.png :width: 45 % Effectively, what "the demeshening" does is allow yt to treat the particles as discrete objects (or with an area of influence) and use their positions in a multi-level index to optimize and minimize the disk operations necessary to load only those particles it needs. .. note:: The theory and implementation of yt's bitmap indexing system is described in some detail in the `yt 4.0 paper `_ in the section entitled `Indexing Discrete-Point Datasets `_. In brief, however, what this relies on is two numbers, ``index_order1`` and ``index_order2``. These control the "coarse" and "refined" sets of indices, and they are supplied to any particle dataset ``load()`` in the form of a tuple as the argument ``index_order``. By default these are set to 5 and 7, respectively, but it is entirely likely that a different set of values will work better for your purposes. For example, if you were to use the sample Gadget-3 dataset, you could override the default values and use values of 5 and 5 by specifying this argument to the ``load_sample`` function; this works with ``load`` as well. .. code-block:: python ds = yt.load_sample("Gadget3-snap-format2", index_order=(5, 5)) So this is how you *change* the index order, but it doesn't explain precisely what this "index order" actually is. Indexing and Why yt Does it --------------------------- yt is based on the idea that data should be selected and read only when it is needed. So for instance, if you only want particles or grid cells from a small region in the center of your dataset, yt wants to avoid any reading of the data *outside* of that region. Now, in practice, this isn't entirely possible -- particularly with particles, you can't actually tell when something is inside or outside of a region *until* you read it, because the particle locations are *stored in the dataset*. One way to avoid this is to have an index of the data, so that yt can know that some of the data that is located *here* in space is located *there* in the file or files on disk. So if you're able to say, I only care about data in "region A", you can look for those files that contain data within "region A," read those, and discard the parts of them that are *not* within "region A." The finer grained the index, the longer it takes to build that index -- and the larger than index is, and the longer it takes to query. The cost of having too *coarse* an index, on the other hand, is that the IO conducted to read a given region is likely to be *too much*, and more particles will be discarded after being read, before being "selected" by the data selector (sphere, region, etc). An important note about all of this is that the index system is not meant to *replace* the positions stored on disk, but instead to speed up queries of those positions -- the index is meant to be lossy in representation, and only provides means of generating IO information. Additionally, the atomic unit that yt considers when conducting IO or selection queries is called a "chunk" internally. For situations where the individual *files* are very, very large, yt will "sub-chunk" these into smaller bits, which are by-default set to $64^3$ particles. Whenever indexing is done, it is done at this granular level, with offsets to individual particle collections stored. For instance, if you had a (single) file with $1024^3$ particles in it, yt would instead regard this as a series of $64^3$ particle files, and index each one individually. Index Order ----------- The bitmap index system is based on a two-level scheme for assigning positions in three-dimensional space to integer values. What this means is that each particle is assigned a "coarse" index, which is global to the full domain of the collection of particles, and *if necessary* an additional "refined" index is assigned to the particle, within that coarse index. The index "order" values refer to the number of entries on a side that each index system is allowed. For instance, if we allow the particles to be subdivided into 8 "bins" in each direction, this would correspond to an index order of 3 (as $2^3 = 8$); correspondingly, an index order of 5 would be 32 bins in each direction, and an index order of 7 would be 128 bins in each direction. Each particle is then assigned a set of i, j, k values for the bin value in each dimension, and these i, j, k values are combined into a single (64-bit) integer according to a space-filling curve. The process by which this is done by yt is as follows: 1. For each "chunk" of data -- which may be a file, or a subset of a file in which particles are contained -- assign each particle to an integer value according to the space-filling curve and the coarse index order. Set the "bit" in an array of boolean values that each of these integers correspond to. Note that this is almost certainly *reductive* -- there will be fewer bits set than there are particles, which is *by design*. 2. Once all chunks or files have been assigned an array of bits that correspond to the places where, according to the coarse indexing scheme, they have particles, identify all those "bits" that have been set by more than one chunk. All of these bits correspond to locations where more than one file contains particles -- so if you want to select something from this region, you'd need to read more than one file. 3. For each "collision" location, apply a *second-order* index, to identify which sub-regions are touched by more than one file. At the end of this process, each file will be associated with a single "coarse" index (which covers the entire domain of the data), as well as a set of "collision" locations, and in each "collision" location a set of bitarrays that correspond to that subregion. When reading data, yt will identify which "coarse" index regions are necessary to read. If any of those coarse index regions are covered by more than one file, it will examine the "refined" index for those regions and see if it is able to subset more efficiently. Because all of these operations can be done with logical operations, this considerably reduces the amount of data that needs to be read from disk before expensive selection operations are conducted. For those situations that involve particles with regions of influence -- such as smoothed particle hydrodynamics, where particles have associated smoothing lengths -- these are taken into account when conducting the indexing system. Efficiency of Index Orders -------------------------- What this can lead to, however, is situations where (particularly at the edges of regions populated by SPH particles) the indexing system identifies collisions, but the relatively small number of particles and correspondingly large "smoothing lengths" result in a large number of "refined" index values that need to be set. Counterintuitively, this actually means that occasionally the "refined" indexing process can take an inordinately long amount of time for *small* datasets, rather than large datasets. In these situations, it is typically sufficient to set the "refined" index order to be much lower than its default value. For instance, setting the ``index_order`` to (5, 3) means that the full domain will be subdivided into 32 bins in each dimension, and any "collision" zones will be further subdivided into 8 bins in each dimension (corresponding to an effective 256 bins across the full domain). If you are experiencing very long index times, this may be a productive parameter to modify. For instance, if you are seeing very rapid "coarse" indexing followed by very, very slow "refined" indexing, this likely plays a part; often this will be most obvious in small-ish (i.e., $256^3$ or smaller) datasets. Index Caching ------------- The index values are cached between instantiation, in a sidecar file named with the name of the dataset file and the suffix ``.indexII_JJ.ewah``, where ``II`` and ``JJ`` are ``index_order1`` and ``index_order2``. So for instance, if ``index_order`` is set to (5, 7), and you are loading a dataset file named "snapshot_200.hdf5", after indexing, you will have an index sidecar file named ``snapshot_200.hdf5.index5_7.ewah``. On subsequent loads, this index file will be reused, rather than re-generated. By *default* these sidecars are stored next to the dataset itself, in the same directory. However, the filename scheme (and thus location) can be changed by supplying an alternate filename to the ``load`` command with the argument ``index_filename``. For instance, if you are accessing data in a read-only location, you can specify that the index will be cached in a location that is write-accessible to you. These files contain the *compressed* bitmap index values, along with some metadata that describes the version of the indexing system they use and so forth. If the version of the index that yt uses has changed, they will be regenerated; in general this will not vary very often (and should be much less frequent than, for instance, yt releases) and yt will provide a message to let you know it is doing it. The file size of these cached index files can be difficult to estimate; because it is based on a compressed bitmap arrays, it will depend on the spatial organization of the particles it is indexing, and how co-located they are according to the space filling curve. For very small datasets it will be small, but we do not expect these index files to grow beyond a few hundred megabytes even in the extreme case of large datasets that have little to no coherence in their clustering. yt-project-yt-f043ac8/doc/source/reference/field_list.rst000066400000000000000000000000521510711153200235220ustar00rootroot00000000000000.. _available-fields: .. yt_showfields:: yt-project-yt-f043ac8/doc/source/reference/index.rst000066400000000000000000000010111510711153200225070ustar00rootroot00000000000000Reference Materials =================== Here we include reference materials for yt with a list of all the code formats supported, a description of how to use yt at the command line, a detailed listing of individual classes and functions, a description of the useful config file, and finally a list of changes between each major release of the code. .. toctree:: :maxdepth: 2 code_support command-line api/api api/modules configuration python_introduction field_list demeshening changelog yt-project-yt-f043ac8/doc/source/reference/python_introduction.rst000066400000000000000000000743371510711153200255470ustar00rootroot00000000000000A Brief Introduction to Python ------------------------------ All scripts that use yt are really Python scripts that use yt as a library. The great thing about Python is that the standard set of libraries that come with it are very extensive -- Python comes with everything you need to write and run mail servers and web servers, create Logo-style turtle graphics, do arbitrary precision math, interact with the operating system, and many other things. In addition to that, efforts by the scientific community to improve Python for computational science have created libraries for fast array computation, GPGPU operations, distributed computing, and visualization. So when you use yt through the scripting interface, you get for free the ability to interlink it with any of the other libraries available for Python. In the past, this has been used to create new types of visualization using OpenGL, data management using Google Docs, and even a simulation that sends an SMS when it has new data to report on. But, this also means learning a little bit of Python! This next section presents a short tutorial of how to start up and think about Python, and then moves on to how to use Python with yt. Starting Python +++++++++++++++ Python has two different modes of execution: interactive execution, and scripted execution. We'll start with interactive execution and then move on to how to write and use scripts. Before we get started, we should briefly touch upon the commands ``help`` and ``dir``. These two commands provide a level of introspection: ``help(something)`` will return the internal documentation on ``something``, including how it can be used and all of the possible "methods" that can be called on it. ``dir()`` will return the available commands and objects that can be directly called, and ``dir(something)`` will return information about all the commands that ``something`` provides. This probably sounds a bit opaque, but it will become clearer with time -- it's also probably helpful to call ``help`` on any or all of the objects we create during this orientation. To start up Python, at your prompt simply type: .. code-block:: bash $ python This will open up Python and give to you a simple prompt of three greater-than signs. Let's inaugurate the occasion appropriately -- type this:: >>> print("Hello, world.") As you can see, this printed out the string "Hello, world." just as we expected. Now let's try a more advanced string, one with a number in it. For this we'll use an "f-string", which is the preferred way to format strings in modern Python. We'll print pi, but only with three digits of accuracy.:: >>> print(f"Pi is precisely {3.1415926:0.2f}") This took the number we fed it (3.1415926) and printed it out as a floating point number with two decimal places. Now let's try something a bit different -- let's print out both the name of the number and its value.:: >>> print(f"{'pi'} is precisely {3.1415926:0.2f}") And there you have it -- the very basics of starting up Python, and some very simple mechanisms for printing values out. Now let's explore a few types of data that Python can store and manipulate. Data Types ++++++++++ Python provides a number of datatypes, but the main ones that we'll concern ourselves with at first are lists, tuples, strings, numbers, and dictionaries. Most of these can be instantiated in a couple different ways, and we'll look at a few of them. Some of these objects can be modified in place, which is called being mutable, and some are immutable and cannot be modified in place. We'll talk below about what that means. Perhaps most importantly, though, is an idea about how Python works in terms of names and bindings -- this is called the "object model." When you create an object, that is independent from binding it to a name -- think of this like pointers in C. This also operates a bit differently for mutable and immutable types. We'll talk a bit more about this later, but it's handy to initially think of things in terms of references. (This is also, not coincidentally, how Python thinks of things internally as well!) When you create an object, initially it has no references to it -- there's nothing that points to it. When you bind it to a name, then you are making a reference, and so its "reference count" is now 1. When something else makes a reference to it, the reference count goes up to 2. When the reference count returns to 0, the object is deleted -- but not before. This concept of reference counting comes up from time to time, but it's not going to be a focus of this orientation. The two easiest datatypes are simply strings and numbers. We can make a string very easily:: >>> my_string = "Hello there" >>> print(my_string) We can also take a look at each individual part of a string. We'll use the 'slicing' notation for this. As a brief note, slicing is 0-indexed, so that element 0 corresponds to the first element. If we wanted to see the third element of our string:: >>> print(my_string[2]) We can also take the third through the 5 elements:: >>> print(my_string[2:5]) But note that if you try to change an element directly, Python objects and it won't let you -- that's because strings are immutable. (But, note that because of how the += operator works, we can do "my_string += '1'" without issue.) To create a number, we do something similar:: >>> a = 10 >>> print(a) This works for floating points as well. Now we can do math on these numbers:: >>> print(a**2) >>> print(a + 5) >>> print(a + 5.1) >>> print(a / 2.0) Now that we have a couple primitive datatypes, we can move on to sequences -- lists and tuples. These two objects are very similar, in that they are collections of arbitrary data types. We'll only look at collections of strings and numbers for now, but these can be filled with arbitrary datatypes (including objects that yt provides, like spheres, datasets, grids, and so on.) The easiest way to create a list is to simply construct one:: >>> my_list = [] At this point, you can find out how long it is, you can append elements, and you can access them at will:: >>> my_list.append(1) >>> my_list.append(my_string) >>> print(my_list[0]) >>> print(my_list[-1]) >>> print(len(my_list)) You can also create a list already containing an initial set of elements:: >>> my_list = [1, 2, 3, "four"] >>> my_list[2] = "three!!" Lists are very powerful objects, which we'll talk about a bit below when discussing how iteration works in Python. A tuple is like a list, in that it's a sequence of objects, and it can be sliced and examined piece by piece. But unlike a list, it's immutable: whatever a tuple contains at instantiation is what it contains for the rest of its existence. Creating a tuple is just like creating a list, except that you use parentheses instead of brackets:: >>> my_tuple = (1, "a", 62.6) Tuples show up very commonly when handling arguments to Python functions and when dealing with multiple return values from a function. They can also be unpacked:: >>> v1, v2, v3 = my_tuple will assign 1, "a", and 62.6 to v1, v2, and v3, respectively. Mutables vs Immutables and Is Versus Equals +++++++++++++++++++++++++++++++++++++++++++ This section is not a "must read" -- it's more of an exploration of how Python's objects work. At some point this is something you may want to be familiar with, but it's not strictly necessary on your first pass. Python provides the operator ``is`` as well as the comparison operator ``==``. The operator ``is`` determines whether two objects are in fact the same object, whereas the operator ``==`` determines if they are equal, according to some arbitrarily defined equality operation. Think of this like comparing the serial numbers on two pictures of a dollar bill (the ``is`` operator) versus comparing the values of two pieces of currency (the ``==`` operator). This digs in to the idea of how the Python object model works, so let's test some things out. For instance, let's take a look at comparing two floating point numbers:: >>> a = 10.1 >>> b = 10.1 >>> print(a == b) >>> print(a is b) The first one returned True, but the second one returned False. Even though both numbers are equal, they point to different points in memory. Now let's try assigning things a bit differently:: >>> b = a >>> print(a is b) This time it's true -- they point to the same part of memory. Try incrementing one and seeing what happens. Now let's try this with a string:: >>> a = "Hi there" >>> b = a >>> print(a is b) Okay, so our intuition here works the same way, and it returns True. But what happens if we modify the string?:: >>> a += "!" >>> print(a) >>> print(b) >>> print(a is b) As you can see, now not only does a contain the value "Hi there!", but it also is a different value than what b contains, and it also points to a different region in memory. That's because strings are immutable -- the act of adding on "!" actually creates an entirely new string and assigns that entirely new string to the variable a, leaving the string pointed to by b untouched. With lists, which are mutable, we have a bit more liberty with how we modify the items and how that modifies the object and its pointers. A list is really just a pointer to a collection; the list object itself does not have any special knowledge of what constitutes that list. So when we initialize a and b:: >>> a = [1, 5, 1094.154] >>> b = a We end up with two pointers to the same set of objects. (We can also have a list inside a list, which adds another fun layer.) Now when we modify a, it shows up in b:: >>> a.append("hat wobble") >>> print(b[-1]) This also works with the concatenation operator:: >>> a += ["beta sequences"] >>> print(a[-1], b[-1]) But we can force a break in this by slicing the list when we initialize:: >>> a = [1, 2, 3, 4] >>> b = a[:] >>> a.append(5) >>> print(b[-1], a[-1]) Here they are different, because we have sliced the list when initializing b. The coolest datatype available in Python, however, is the dictionary. This is a mapping object of key:value pairs, where one value is used to look up another value. We can instantiate a dictionary in a variety of ways, but for now we'll only look at one of the simplest mechanisms for doing so:: >>> my_dict = {} >>> my_dict["A"] = 1.0 >>> my_dict["B"] = 154.014 >>> my_dict[14001] = "This number is great" >>> print(my_dict["A"]) As you can see, one value can be used to look up another. Almost all datatypes (with a few notable exceptions, but for the most part these are quite uncommon) can be used as a key, and you can use any object as a value. We won't spend too much time discussing dictionaries explicitly, but I will leave you with a word on their efficiency: the Python lookup algorithm is known for its hand-tuned optimization and speed, and it's very common to use dictionaries to look up hundreds of thousands or even millions of elements and to expect it to be responsive. Looping +++++++ Looping in Python is both different and more powerful than in lower-level languages. Rather than looping based exclusively on conditionals (which is possible in Python) the fundamental mode of looping in Python is iterating over objects. In C, one might construct a loop where some counter variable is initialized, and at each iteration of the loop it is incremented and compared against a reference value; when the counter variable reaches the reference variable, the loop is terminated. In Python, on the other hand, to accomplish iteration through a set of sequential integers, one actually constructs a sequence of those integers, and iterates over that sequence. For more discussion of this, and some very, very powerful ways of accomplishing this iteration process, look through the Python documentation for the words 'iterable' and 'generator.' To see this in action, let's first take a look at the built-in function ``range``. :: >>> print(range(10)) As you can see, what the function ``range`` returns is a list of integers, starting at zero, that is as long as the argument to the ``range`` function. In practice, this means that calling ``range(N)`` returns ``0, 1, 2, ... N-1`` in a list. So now we can execute a for loop, but first, an important interlude: Control blocks in Python are delimited by white space. This means that, unlike in C with its brackets, you indicate an isolated control block for conditionals, function declarations, loops and other things with an indentation. When that control block ends, you dedent the text. In yt, we use four spaces -- I recommend you do the same -- which can be inserted by a text editor in place of tab characters. Let's try this out with a for loop. First type ``for i in range(10):`` and press enter. This will change the prompt to be three periods, instead of three greater-than signs, and you will be expected to hit the tab key to indent. Then type "print(i)", press enter, and then instead of indenting again, press enter again. The entire entry should look like this:: >>> for i in range(10): ... print(i) ... As you can see, it prints out each integer in turn. So far this feels a lot like C. (It won't, if you start using iterables in place of sequences -- for instance, ``xrange`` operates just like range, except instead of returning an already-created list, it returns the promise of a sequence, whose elements aren't created until they are requested.) Let's try it with our earlier list:: >>> my_sequence = ["a", "b", 4, 110.4] >>> for i in my_sequence: ... print(i) ... This time it prints out every item in the sequence. A common idiom that gets used a lot is to figure out which index the loop is at. The first time this is written, it usually goes something like this:: >>> index = 0 >>> my_sequence = ["a", "b", 4, 110.4] >>> for i in my_sequence: ... print("%s = %s" % (index, i)) ... index += 1 ... This does what you would expect: it prints out the index we're at, then the value of that index in the list. But there's an easier way to do this, less prone to error -- and a bit cleaner! You can use the ``enumerate`` function to accomplish this:: >>> my_sequence = ["a", "b", 4, 110.4] >>> for index, val in enumerate(my_sequence): ... print("%s = %s" % (index, val)) ... This does the exact same thing, but we didn't have to keep track of the counter variable ourselves. You can use the function ``reversed`` to reverse a sequence in a similar fashion. Try this out:: >>> my_sequence = range(10) >>> for val in reversed(my_sequence): ... print(val) ... We can even combine the two!:: >>> my_sequence = range(10) >>> for index, val in enumerate(reversed(my_sequence)): ... print("%s = %s" % (index, val)) ... The most fun of all the built-in functions that operate on iterables, however, is the ``zip`` function. This function will combine two sequences (but only up to the shorter of the two -- so if one has 16 elements and the other 1000, the zipped sequence will only have 16) and produce iterators over both. As an example, let's say you have two sequences of values, and you want to produce a single combined sequence from them.:: >>> seq1 = ["Hello", "What's up", "I'm fine"] >>> seq2 = ["!", "?", "."] >>> seq3 = [] >>> for v1, v2 in zip(seq1, seq2): ... seq3.append(v1 + v2) ... >>> print(seq3) As you can see, this is much easier than constructing index values by hand and then drawing from the two sequences using those index values. I should note that while this is great in some instances, for numeric operations, NumPy arrays (discussed below) will invariably be faster. Conditionals ++++++++++++ Conditionals, like loops, are delimited by indentation. They follow a relatively simple structure, with an "if" statement, followed by the conditional itself, and then a block of indented text to be executed in the event of the success of that conditional. For subsequent conditionals, the word "elif" is used, and for the default, the word "else" is used. As a brief aside, the case/switch statement in Python is typically executed using an if/elif/else block; this can be done using more complicated dictionary-type statements with functions, but that typically only adds unnecessary complexity. For a simple example of how to do an if/else statement, we'll return to the idea of iterating over a loop of numbers. We'll use the ``%`` operator, which is a binary modulus operation: it divides the first number by the second and then returns the remainder. Our first pass will examine the remainders from dividing by 2, and print out all the even numbers. (There are of course easier ways of determining which numbers are multiples of 2 -- particularly using NumPy, as we'll do below.):: >>> for val in range(100): ... if val % 2 == 0: ... print("%s is a multiple of 2" % (val)) ... Now we'll add on an ``else`` statement, so that we print out all the odd numbers as well, with the caveat that they are not multiples of 2.:: >>> for val in range(100): ... if val % 2 == 0: ... print("%s is a multiple of 2" % (val)) ... else: ... print("%s is not a multiple of 2" % (val)) ... Let's extend this to check the remainders of division with both 2 and 3, and determine which numbers are multiples of 2, 3, or neither. We'll do this for all numbers between 0 and 99.:: >>> for val in range(100): ... if val % 2 == 0: ... print("%s is a multiple of 2" % (val)) ... elif val % 3 == 0: ... print("%s is a multiple of 3" % (val)) ... else: ... print("%s is not a multiple of 2 or 3" % (val)) ... This should print out which numbers are multiples of 2 or 3 -- but note that we're not catching all the multiples of 6, which are multiples of both 2 and 3. To do that, we have a couple options, but we can start with just changing the first if statement to encompass both, using the ``and`` operator:: >>> for val in range(100): ... if val % 2 == 3 and val % 3 == 0: ... print("%s is a multiple of 6" % (val)) ... elif val % 2 == 0: ... print("%s is a multiple of 2" % (val)) ... elif val % 3 == 0: ... print("%s is a multiple of 3" % (val)) ... else: ... print("%s is not a multiple of 2 or 3" % (val)) ... In addition to the ``and`` statement, the ``or`` and ``not`` statements work in the expected manner. There are also several built-in operators, including ``any`` and ``all`` that operate on sequences of conditionals, but those are perhaps better saved for later. Array Operations ++++++++++++++++ In general, iteration over sequences carries with it some substantial overhead: each value is selected, bound to a local name, and then its type is determined when it is acted upon. This is, regrettably, the price of the generality that Python brings with it. While this overhead is minimal for operations acting on a handful of values, if you have a million floating point elements in a sequence and you want to simply add 1.2 to all of them, or multiply them by 2.5, or exponentiate them, this carries with it a substantial performance hit. To accommodate this, the NumPy library has been created to provide very fast operations on arrays of numerical elements. When you create a NumPy array, you are creating a shaped array of (potentially) sequential locations in memory which can be operated on at the C-level, rather than at the interpreted Python level. For this reason, which NumPy arrays can act like Python sequences can, and can thus be iterated over, modified in place, and sliced, they can also be addressed as a monolithic block. All of the fluid and particle quantities used in yt will be expressed as NumPy arrays, allowing for both efficient computation and a minimal memory footprint. For instance, the following operation will not work in standard Python:: >>> vals = range(10) >>> vals *= 2.0 (Note that multiplying vals by the integer 2 will not do what you think: rather than multiplying each value by 2.0, it will simply double the length of the sequence!) To get started with array operations, let's first import the NumPy library. This is the first time we've seen an import in this orientation, so we'll dwell for a moment on what this means. When a library is imported, it is read from disk, the functions are loaded into memory, and they are made available to the user. So when we execute:: >>> import numpy The ``numpy`` module is loaded, and then can be accessed:: >>> numpy.arange(10) This calls the ``arange`` function that belongs to the ``numpy`` module's "namespace." We'll use the term namespace to refer to the variables, functions, and submodules that belong to a given conceptual region. We can also extend our current namespace with the contents of the ``numpy`` module, so that we don't have to prefix all of our calling of ``numpy`` functions with ``numpy.`` but we will not do so here, so as to preserve the distinction between the built-in Python functions and the NumPy-provided functions. To get started, let's perform the NumPy version of getting a sequence of numbers from 0 to 99:: >>> my_array = numpy.arange(100) >>> print(my_array) >>> print(my_array * 2.0) >>> print( my_array * 2) As you can see, each of these operations does exactly what we think it ought to. And, in fact, so does this one:: >>> my_array *= 2.0 So far we've only examined what happens if we have operate on a single array of a given shape -- specifically, if we have an array that is N elements long, but only one dimensional. NumPy arrays are, for the most part, defined by their data, their shape, and their data type. We can examine both the shape (which includes dimensionality) and the size (strictly the total number of elements) in an array by looking at a couple properties of the array:: >>> print(my_array.size) >>> print(my_array.shape) Note that size must be the product of the components of the shape. In this case, both are 100. We can obtain a new array of a different shape by calling the ``reshape`` method on an array:: >>> print(my_array.reshape((10, 10))) In this case, we have not modified ``my_array`` but instead created a new array containing the same elements, but with a different dimensionality and shape. You can modify an array's shape in place, as well, but that should be done with care and the explanation of how that works and its caveats can come a bit later. There are a few other important characteristics of arrays, and ways to create them. We can see what kind of datatype an array is by examining its ``dtype`` attribute:: >>> print(my_array.dtype) This can be changed by calling ``astype`` with another datatype. Datatypes include, but are not limited to, ``int32``, ``int64``, ``float32``, ``float64``.:: >>> float_array = my_array.astype("float64") Arrays can also be operated on together, in lieu of something like an iteration using the ``zip`` function. To show this, we'll use the ``numpy.random.random`` function to generate a random set of values of length 100, and then we'll multiply our original array against those random values.:: >>> rand_array = numpy.random.random(100) >>> print(rand_array * my_array) There are a number of functions you can call on arrays, as well. For instance:: >>> print(rand_array.sum()) >>> print(rand_array.mean()) >>> print(rand_array.min()) >>> print(rand_array.max()) Indexing in NumPy is very fun, and also provides some advanced functionality for selecting values. You can slice and dice arrays:: >>> print(my_array[50:60]) >>> print(my_array[::2]) >>> print(my_array[:-10]) But Numpy also provides the ability to construct boolean arrays, which are the result of conditionals. For example, let's say that you wanted to generate a random set of values, and select only those less than 0.2:: >>> rand_array = numpy.random.random(100) >>> print(rand_array < 0.2) What is returned is a long list of booleans. Boolean arrays can be used as indices -- what this means is that you can construct an index array and then use that toe select only those values where that index array is true. In this example we also use the ``numpy.all`` and ``numpy.any`` functions, which do exactly what you might think -- they evaluate a statement and see if all elements satisfy it, and if any individual element satisfies it, respectively.:: >>> ind_array = rand_array < 0.2 >>> print(rand_array[ind_array]) >>> print(numpy.all(rand_array[ind_array] < 0.2)) You can even skip the creation of the variable ``ind_array`` completely, and instead just coalesce the statements into a single statement:: >>> print(numpy.all(rand_array[rand_array < 0.2] < 0.2)) >>> print(numpy.any(rand_array[rand_array < 0.2] > 0.2)) You might look at these and wonder why this is useful -- we've already selected those elements that are less than 0.2, so why do we want to re-evaluate it? But the interesting component to this is that a conditional applied to one array can be used to index another array. For instance:: >>> print(my_array[rand_array < 0.2]) Here we've identified those elements in our random number array that are less than 0.2, and printed the corresponding elements from our original sequential array of integers. This is actually a great way of selecting a random sample of a dataset -- in this case we get back approximately 20% of the dataset ``my_array``, selected at random. To create arrays from nothing, several options are available. The command ``numpy.array`` will create an array from any arbitrary sequence:: >>> my_sequence = [1.0, 510.42, 1789532.01482] >>> my_array = numpy.array(my_sequence) Additionally, arrays full of ones and zeros can be created:: >>> my_integer_ones = numpy.ones(100) >>> my_float_ones = numpy.ones(100, dtype="float64") >>> my_integer_zeros = numpy.zeros(100) >>> my_float_zeros = numpy.zeros(100, dtype="float64") The function ``numpy.concatenate`` is also useful, but outside the scope of this orientation. The NumPy documentation has a number of more advanced mechanisms for combining arrays; the documentation for "broadcasting" in particular is very useful, and covers mechanisms for combining arrays of different shapes and sizes, which can be tricky but also extremely powerful. We won't discuss the idea of broadcasting here, simply because I don't know that I could do it justice! The NumPy Docs have a great `section on broadcasting `_. Scripted Usage ++++++++++++++ We've now explored Python interactively. However, for long-running analysis tasks or analysis tasks meant to be run on a compute cluster non-interactively, we will want to utilize its scripting interface. Let's start by quitting out of the interpreter. If you have not already done so, you can quit by pressing "Ctrl-D", which will free all memory used by Python and return you to your shell's command prompt. At this point, open up a text editor and edit a file called ``my_first_script.py``. Python scripts typically end in the extension ``.py``. We'll start our scripting tests by doing some timing of array operations versus sequence operations. Into this file, type this text:: import numpy import time my_array = numpy.arange(1000000, dtype="float64") t1 = time.time() my_array_squared = my_array**2.0 t2 = time.time() print("It took me %0.3e seconds to square the array using NumPy" % (t2-t1)) t1 = time.time() my_sequence_squared = [] for i in range(1000000): my_sequence_squared.append(i**2.0) t2 = time.time() print("It took me %0.3e seconds to square the sequence without NumPy" % (t2-t1)) Now save this file, and return to the command prompt. We can execute it by supplying it to Python: .. code-block:: bash $ python my_first_script.py It should run, display two pieces of information, and terminate, leaving you back at the command prompt. On my laptop, the array operation is approximately 42 times faster than the sequence operation! Of course, depending on the operation conducted, this number can go up quite substantially. If you want to run a Python script and then be given a Python interpreter prompt, you can call the ``python`` command with the option ``-i``: .. code-block:: bash $ python -i my_first_script.py Python will execute the script and when it has reached the end it will give you a command prompt. At this point, all of the variables you have set up and created will be available to you -- so you can, for instance, print out the contents of ``my_array_squared``:: >>> print(my_array_squared) The scripting interface for Python is quite powerful, and by combining it with interactive execution, you can, for instance, set up variables and functions for interactive exploration of data. Functions and Objects +++++++++++++++++++++ Functions and Objects in Python are the easiest way to perform very complex, powerful actions in Python. For the most part we will not discuss them; in fact, the standard Python tutorial that comes with the Python documentation is a very good explanation of how to create and use objects and functions, and attempting to replicate it here would simply be futile. yt provides both many objects and functions for your usage, and it is through the usage and combination of functions and objects that you will be able to create plots, manipulate data, and visualize your data. And with that, we conclude our brief introduction to Python. I recommend checking out the standard Python tutorial or browsing some of the NumPy documentation. If you're looking for a book to buy, the only book I've personally ever been completely satisfied with has been David Beazley's book on Python Essentials and the Python standard library, but I've also heard good things about many of the others, including those by Alex Martelli and Wesley Chun. We'll now move on to talking more about how to use yt, both from a scripting perspective and interactively. Python and Related References +++++++++++++++++++++++++++++ * `Python quickstart `_ * `Learn Python the Hard Way `_ * `Byte of Python `_ * `Dive Into Python `_ * `Numpy docs `_ * `Matplotlib docs `_ yt-project-yt-f043ac8/doc/source/visualizing/000077500000000000000000000000001510711153200212635ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/visualizing/FITSImageData.ipynb000066400000000000000000000516721510711153200246430ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Writing FITS Images" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "yt has capabilities for writing 2D and 3D uniformly gridded data generated from datasets to FITS files. This is via the `FITSImageData` class. We'll test these capabilities out on an Athena dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import yt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "units_override = {\n", " \"length_unit\": (1.0, \"Mpc\"),\n", " \"mass_unit\": (1.0e14, \"Msun\"),\n", " \"time_unit\": (1.0, \"Myr\"),\n", "}\n", "ds = yt.load(\"MHDSloshing/virgo_low_res.0054.vtk\", units_override=units_override)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating FITS images from Slices and Projections" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several ways to make a `FITSImageData` instance. The most intuitive ways are to use the `FITSSlice`, `FITSProjection`, `FITSOffAxisSlice`, and `FITSOffAxisProjection` classes to write slices and projections directly to FITS. To demonstrate a useful example of creating a FITS file, let's first make a `ProjectionPlot`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj = yt.ProjectionPlot(\n", " ds,\n", " \"z\",\n", " (\"gas\", \"temperature\"),\n", " weight_field=(\"gas\", \"density\"),\n", " width=(500.0, \"kpc\"),\n", ")\n", "prj.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose that we wanted to write this projection to a FITS file for analysis and visualization in other programs, such as ds9. We can do that using `FITSProjection`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits = yt.FITSProjection(\n", " ds, \"z\", (\"gas\", \"temperature\"), weight_field=(\"gas\", \"density\")\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which took the same parameters as `ProjectionPlot` except the width, because `FITSProjection` and `FITSSlice` always make slices and projections of the width of the domain size, at the finest resolution available in the simulation, in a unit determined to be appropriate for the physical size of the dataset." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also set width manually in `FITSProjection`. For example, set the width to 500 kiloparsec to get FITS file of the same projection plot as discussed above." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits = yt.FITSProjection(\n", " ds,\n", " \"z\",\n", " (\"gas\", \"temperature\"),\n", " weight_field=(\"gas\", \"density\"),\n", " width=(500.0, \"kpc\"),\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want the center coordinates of the image in either a slice or a projection to be (0,0) instead of the domain coordinates, set `origin=\"image\"`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits_img = yt.FITSProjection(\n", " ds,\n", " \"z\",\n", " (\"gas\", \"temperature\"),\n", " weight_field=(\"gas\", \"density\"),\n", " width=(500.0, \"kpc\"),\n", " origin=\"image\",\n", ")" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Making FITS images from Particle Projections" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "To create a FITS image from a particle field which is smeared onto the image, we can use\n", "`FITSParticleProjection`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "dsp = yt.load(\"gizmo_64/output/snap_N64L16_135.hdf5\")\n", "prjp_fits = yt.FITSParticleProjection(\n", " dsp, \"x\", (\"PartType1\", \"particle_mass\"), deposition=\"cic\"\n", ")\n", "prjp_fits.writeto(\"prjp.fits\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "Note that we used the \"Cloud-In-Cell\" interpolation method (`\"cic\"`) instead of the default\n", "\"Nearest-Grid-Point\" (`\"ngp\"`) method. \n", "\n", "If you want the projection to be divided by the pixel area (to make a projection of mass density, \n", "for example), supply the ``density`` keyword argument:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "prjpd_fits = yt.FITSParticleProjection(\n", " dsp, \"x\", (\"PartType1\", \"particle_mass\"), density=True, deposition=\"cic\"\n", ")\n", "prjpd_fits.writeto(\"prjpd.fits\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "`FITSParticleOffAxisProjection` can be used to make a projection along any arbitrary sight line:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "L = [1, -1, 1] # normal or \"line of sight\" vector\n", "N = [0, 0, 1] # north or \"up\" vector\n", "poff_fits = yt.FITSParticleOffAxisProjection(\n", " dsp, L, (\"PartType1\", \"particle_mass\"), deposition=\"cic\", north_vector=N\n", ")\n", "poff_fits.writeto(\"poff.fits\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "## Using `HDUList` Methods" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can call a number of the [AstroPy `HDUList`](https://astropy.readthedocs.io/en/latest/io/fits/api/hdulists.html) class's methods from a `FITSImageData` object. For example, `info` shows us the contents of the virtual FITS file:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also look at the header for a particular field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits[\"temperature\"].header" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where we can see that the units of the temperature field are Kelvin and the cell widths are in kiloparsecs. Note that the length, time, mass, velocity, and magnetic field units of the dataset have been copied into the header " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " If we want the raw image data with units, we can use the `data` attribute of this field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits[\"temperature\"].data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Changing Aspects of the Images" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the `set_unit` method to change the units of a particular field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits.set_unit(\"temperature\", \"R\")\n", "prj_fits[\"temperature\"].data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The length units of the image (and its coordinate system), as well as the resolution of the image, can be adjusted when creating it using the `length_unit` and `image_res` keyword arguments, respectively:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# length_unit defaults to that from the dataset\n", "# image_res defaults to 512\n", "slc_fits = yt.FITSSlice(\n", " ds, \"z\", (\"gas\", \"density\"), width=(500, \"kpc\"), length_unit=\"ly\", image_res=256\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now check that this worked by looking at the header, notice in particular the `NAXIS[12]` and `CUNIT[12]` keywords (the `CDELT[12]` and `CRPIX[12]` values also change):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc_fits[\"density\"].header" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Saving and Loading Images" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The image can be written to disk using the `writeto` method:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits.writeto(\"sloshing.fits\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since yt can read FITS image files, it can be loaded up just like any other dataset. Since we created this FITS file with `FITSImageData`, the image will contain information about the units and the current time of the dataset:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ds2 = yt.load(\"sloshing.fits\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and we can make a `SlicePlot` of the 2D image, which shows the same data as the previous image:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc2 = yt.SlicePlot(ds2, \"z\", (\"gas\", \"temperature\"), width=(500.0, \"kpc\"))\n", "slc2.set_log(\"temperature\", True)\n", "slc2.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating `FITSImageData` Instances Directly from FRBs, PlotWindow instances, and 3D Grids" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want more fine-grained control over what goes into the FITS file, you can call `FITSImageData` directly, with various kinds of inputs. For example, you could use a `FixedResolutionBuffer`, and specify you want the units in parsecs instead:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc3 = ds.slice(0, 0.0)\n", "frb = slc3.to_frb((500.0, \"kpc\"), 800)\n", "fid_frb = frb.to_fits_data(\n", " fields=[(\"gas\", \"density\"), (\"gas\", \"temperature\")], length_unit=\"pc\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If one creates a `PlotWindow` instance, e.g. `SlicePlot`, `ProjectionPlot`, etc., you can also call this same method there:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fid_pw = prj.to_fits_data(\n", " fields=[(\"gas\", \"density\"), (\"gas\", \"temperature\")], length_unit=\"pc\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A 3D FITS cube can also be created from regularly gridded 3D data. In yt, there are covering grids and \"arbitrary grids\". The easiest way to make an arbitrary grid object is using `ds.r`, where we can index the dataset like a NumPy array, creating a grid of 1.0 Mpc on a side, centered on the origin, with 64 cells on a side:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "grid = ds.r[\n", " (-0.5, \"Mpc\"):(0.5, \"Mpc\"):64j,\n", " (-0.5, \"Mpc\"):(0.5, \"Mpc\"):64j,\n", " (-0.5, \"Mpc\"):(0.5, \"Mpc\"):64j,\n", "]\n", "fid_grid = grid.to_fits_data(\n", " fields=[(\"gas\", \"density\"), (\"gas\", \"temperature\")], length_unit=\"Mpc\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Other `FITSImageData` Methods" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating Images from Others" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `FITSImageData` instance can be generated from one previously written to disk using the `from_file` classmethod:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fid = yt.FITSImageData.from_file(\"sloshing.fits\")\n", "fid.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Multiple `FITSImageData` can be combined to create a new one, provided that the coordinate information is the same:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits2 = yt.FITSProjection(ds, \"z\", (\"gas\", \"density\"), width=(500.0, \"kpc\"))\n", "prj_fits3 = yt.FITSImageData.from_images([prj_fits, prj_fits2])\n", "prj_fits3.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, individual fields can be popped as well to produce new instances of `FITSImageData`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dens_fits = prj_fits3.pop(\"density\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So this new instance would only have the `\"density\"` field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dens_fits.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and the old one has the `\"density\"` field removed:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits3.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding Sky Coordinates to Images" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, the FITS images we have shown have linear spatial coordinates. We can see this by looking at the header for one of the fields, and examining the `CTYPE1` and `CTYPE2` keywords:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits[\"temperature\"].header" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `WCSNAME` keyword is set to `\"yt\"` by default. \n", "\n", "However, one may want to take a projection of an object and make a crude mock observation out of it, with celestial coordinates. For this, we can use the `create_sky_wcs` method. Specify a center (RA, Dec) coordinate in degrees, as well as a linear scale in terms of angle per distance:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sky_center = [30.0, 45.0] # in degrees\n", "sky_scale = (2.5, \"arcsec/kpc\") # could also use a YTQuantity\n", "prj_fits.create_sky_wcs(sky_center, sky_scale, ctype=[\"RA---TAN\", \"DEC--TAN\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, a tangent RA/Dec projection is used, but one could also use another projection using the `ctype` keyword. We can now look at the header and see it has the appropriate WCS now. The old `\"yt\"` WCS has been added to a second WCS in the header, where the parameters have an `\"A\"` appended to them:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits[\"temperature\"].header" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and now the `WCSNAME` has been set to `\"celestial\"`. If you want the original WCS to remain in the original place, then you can make the call to `create_sky_wcs` and set `replace_old_wcs=False`, which will put the new, celestial WCS in the second one:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "prj_fits3.create_sky_wcs(\n", " sky_center, sky_scale, ctype=[\"RA---TAN\", \"DEC--TAN\"], replace_old_wcs=False\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prj_fits3[\"temperature\"].header" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Updating Header Parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also add header keywords to a single field or for all fields in the FITS image using `update_header`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fid_frb.update_header(\"all\", \"time\", 0.1) # Update all the fields\n", "fid_frb.update_header(\"temperature\", \"scale\", \"Rankine\") # Update just one field" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(fid_frb[\"density\"].header[\"time\"])\n", "print(fid_frb[\"temperature\"].header[\"scale\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Changing Image Names" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use the `change_image_name` method to change the name of an image in a `FITSImageData` instance:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fid_frb.change_image_name(\"density\", \"mass_per_volume\")\n", "fid_frb.info() # now \"density\" should be gone and \"mass_per_volume\" should be in its place" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Convolving FITS Images" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, you can convolve an image inside a `FITSImageData` instance with a kernel, either a Gaussian with a specific standard deviation, or any kernel provided by AstroPy. See AstroPy's [Convolution and filtering](http://docs.astropy.org/en/stable/convolution/index.html) for more details." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dens_fits.writeto(\"not_convolved.fits\", overwrite=True)\n", "# Gaussian kernel with standard deviation of 3.0 kpc\n", "dens_fits.convolve(\"density\", 3.0)\n", "dens_fits.writeto(\"convolved.fits\", overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's load these up as datasets and see the difference:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ds0 = yt.load(\"not_convolved.fits\")\n", "dsc = yt.load(\"convolved.fits\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc3 = yt.SlicePlot(ds0, \"z\", (\"gas\", \"density\"), width=(500.0, \"kpc\"))\n", "slc3.set_log(\"density\", True)\n", "slc3.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slc4 = yt.SlicePlot(dsc, \"z\", (\"gas\", \"density\"), width=(500.0, \"kpc\"))\n", "slc4.set_log(\"density\", True)\n", "slc4.show()" ] } ], "metadata": { "anaconda-cloud": {}, "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb000066400000000000000000000163051510711153200306500ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Transfer Function Helper Tutorial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we explain how to use TransferFunctionHelper to visualize and interpret yt volume rendering transfer functions. Creating a custom transfer function is a process that usually involves some trial-and-error. TransferFunctionHelper is a utility class designed to help you visualize the probability density functions of yt fields that you might want to volume render. This makes it easier to choose a nice transfer function that highlights interesting physical regimes.\n", "\n", "First, we set up our namespace and define a convenience function to display volume renderings inline in the notebook. Using `%matplotlib inline` makes it so matplotlib plots display inline in the notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "import numpy as np\n", "from IPython.core.display import Image\n", "\n", "import yt\n", "from yt.visualization.volume_rendering.transfer_function_helper import (\n", " TransferFunctionHelper,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we load up a low resolution Enzo cosmological simulation." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "ds = yt.load(\"Enzo_64/DD0043/data0043\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have the dataset loaded, let's create a `TransferFunctionHelper` to visualize the dataset and transfer function we'd like to use." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "tfh = TransferFunctionHelper(ds)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`TransferFunctionHelpler` will intelligently choose transfer function bounds based on the data values. Use the `plot()` method to take a look at the transfer function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "# Build a transfer function that is a multivariate gaussian in temperature\n", "tfh = TransferFunctionHelper(ds)\n", "tfh.set_field((\"gas\", \"temperature\"))\n", "tfh.set_log(True)\n", "tfh.set_bounds()\n", "tfh.build_transfer_function()\n", "tfh.tf.add_layers(5)\n", "tfh.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's also look at the probability density function of the `mass` field as a function of `temperature`. This might give us an idea where there is a lot of structure. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "tfh.plot(profile_field=(\"gas\", \"mass\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It looks like most of the gas is hot but there is still a lot of low-density cool gas. Let's construct a transfer function that highlights both the rarefied hot gas and the dense cool gas simultaneously." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "tfh = TransferFunctionHelper(ds)\n", "tfh.set_field((\"gas\", \"temperature\"))\n", "tfh.set_bounds()\n", "tfh.set_log(True)\n", "tfh.build_transfer_function()\n", "tfh.tf.map_to_colormap(6.0, 8.0, colormap=\"Reds\")\n", "tfh.tf.map_to_colormap(-1.0, 6.0, colormap=\"Blues_r\")\n", "\n", "tfh.plot(profile_field=(\"gas\", \"mass\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's take a look at the volume rendering. First use the helper function to create a default rendering, then we override this with the transfer function we just created." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "im, sc = yt.volume_render(ds, [(\"gas\", \"temperature\")])\n", "\n", "source = sc.get_source()\n", "source.set_transfer_function(tfh.tf)\n", "sc.render()\n", "sc.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That looks okay, but the red gas (associated with temperatures between 1e6 and 1e8 K) is a bit hard to see in the image. To fix this, we can make that gas contribute a larger alpha value to the image by using the ``scale`` keyword argument in ``map_to_colormap``." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [] }, "outputs": [], "source": [ "tfh2 = TransferFunctionHelper(ds)\n", "tfh2.set_field((\"gas\", \"temperature\"))\n", "tfh2.set_bounds()\n", "tfh2.set_log(True)\n", "tfh2.build_transfer_function()\n", "tfh2.tf.map_to_colormap(6.0, 8.0, colormap=\"Reds\", scale=5.0)\n", "tfh2.tf.map_to_colormap(-1.0, 6.0, colormap=\"Blues_r\", scale=1.0)\n", "\n", "tfh2.plot(profile_field=(\"gas\", \"mass\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the height of the red portion of the transfer function has increased by a factor of 5.0. If we use this transfer function to make the final image:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "source.set_transfer_function(tfh2.tf)\n", "sc.render()\n", "sc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The red gas is now much more prominent in the image. We can clearly see that the hot gas is mostly associated with bound structures while the cool gas is associated with low-density voids." ] } ], "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.10.11" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/visualizing/Volume_Rendering_Tutorial.ipynb000066400000000000000000000252151510711153200274620ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Volume Rendering Tutorial \n", "\n", "This notebook shows how to use the new (in version 3.3) Scene interface to create custom volume renderings. The tutorial proceeds in the following steps: \n", "\n", "1. [Creating the Scene](#1.-Creating-the-Scene)\n", "2. [Displaying the Scene](#2.-Displaying-the-Scene)\n", "3. [Adjusting Transfer Functions](#3.-Adjusting-Transfer-Functions)\n", "4. [Saving an Image](#4.-Saving-an-Image)\n", "5. [Adding Annotations](#5.-Adding-Annotations)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Creating the Scene \n", "\n", "To begin, we load up a dataset and use the `yt.create_scene` method to set up a basic Scene. We store the Scene in a variable called `sc` and render the default `('gas', 'density')` field." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "import yt\n", "from yt.visualization.volume_rendering.transfer_function_helper import (\n", " TransferFunctionHelper,\n", ")\n", "\n", "ds = yt.load(\"IsolatedGalaxy/galaxy0030/galaxy0030\")\n", "sc = yt.create_scene(ds)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that to render a different field, we would use pass the field name to `yt.create_scene` using the `field` argument. \n", "\n", "Now we can look at some information about the Scene we just created using the python print keyword:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This prints out information about the Sources, Camera, and Lens associated with this Scene. Each of these can also be printed individually. For example, to print only the information about the first (and currently, only) Source, we can do:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sc.get_source())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Displaying the Scene \n", "\n", "We can see that the `yt.create_source` method has created a `VolumeSource` with default values for the center, bounds, and transfer function. Now, let's see what this Scene looks like. In the notebook, we can do this by calling `sc.show()`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That looks okay, but it's a little too zoomed-out. To fix this, let's modify the Camera associated with our Scene. This next bit of code will zoom in the camera (i.e. decrease the width of the view) by a factor of 3." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.camera.zoom(3.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now when we print the Scene, we see that the Camera width has decreased by a factor of 3:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To see what this looks like, we re-render the image and display the scene again. Note that we don't actually have to call `sc.show()` here - we can just have Ipython evaluate the Scene and that will display it automatically." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.render()\n", "sc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's better! The image looks a little washed-out though, so we use the `sigma_clip` argument to `sc.show()` to improve the contrast:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.show(sigma_clip=4.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Applying different values of `sigma_clip` with `sc.show()` is a relatively fast process because `sc.show()` will pull the most recently rendered image and apply the contrast adjustment without rendering the scene again. While this is useful for quickly testing the affect of different values of `sigma_clip`, it can lead to confusion if we don't remember to render after making changes to the camera. For example, if we zoom in again and simply call `sc.show()`, then we get the same image as before:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.camera.zoom(3.0)\n", "sc.show(sigma_clip=4.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the change to the camera to take affect, we have to explicitly render again: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.render()\n", "sc.show(sigma_clip=4.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a general rule, any changes to the scene itself such as adjusting the camera or changing transfer functions requires rendering again. Before moving on, let's undo the last zoom:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.camera.zoom(1.0 / 3.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Adjusting Transfer Functions\n", "\n", "Next, we demonstrate how to change the mapping between the field values and the colors in the image. We use the TransferFunctionHelper to create a new transfer function using the `gist_rainbow` colormap, and then re-create the image as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Set up a custom transfer function using the TransferFunctionHelper.\n", "# We use 10 Gaussians evenly spaced logarithmically between the min and max\n", "# field values.\n", "tfh = TransferFunctionHelper(ds)\n", "tfh.set_field(\"density\")\n", "tfh.set_log(True)\n", "tfh.set_bounds()\n", "tfh.build_transfer_function()\n", "tfh.tf.add_layers(10, colormap=\"gist_rainbow\")\n", "\n", "# Grab the first render source and set it to use the new transfer function\n", "render_source = sc.get_source()\n", "render_source.transfer_function = tfh.tf\n", "\n", "sc.render()\n", "sc.show(sigma_clip=4.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's try using a different lens type. We can give a sense of depth to the image by using the perspective lens. To do, we create a new Camera below. We also demonstrate how to switch the camera to a new position and orientation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cam = sc.add_camera(ds, lens_type=\"perspective\")\n", "\n", "# Standing at (x=0.05, y=0.5, z=0.5), we look at the area of x>0.05 (with some open angle\n", "# specified by camera width) along the positive x direction.\n", "cam.position = ds.arr([0.05, 0.5, 0.5], \"code_length\")\n", "\n", "normal_vector = [1.0, 0.0, 0.0]\n", "north_vector = [0.0, 0.0, 1.0]\n", "cam.switch_orientation(normal_vector=normal_vector, north_vector=north_vector)\n", "\n", "# The width determines the opening angle\n", "cam.set_width(ds.domain_width * 0.5)\n", "\n", "print(sc.camera)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The resulting image looks like:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.render()\n", "sc.show(sigma_clip=4.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Saving an Image\n", "\n", "To save a volume rendering to an image file at any point, we can use `sc.save` as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.save(\"volume_render.png\", render=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Including the keyword argument `render=False` indicates that the most recently rendered image will be saved (otherwise, `sc.save()` will trigger a call to `sc.render()`). This behavior differs from `sc.show()`, which always uses the most recently rendered image. \n", "\n", "An additional caveat is that if we used `sigma_clip` in our call to `sc.show()`, then we must **also** pass it to `sc.save()` as sigma clipping is applied on top of a rendered image array. In that case, we would do the following: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.save(\"volume_render_clip4.png\", sigma_clip=4.0, render=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Adding Annotations\n", "\n", "Finally, the next cell restores the lens and the transfer function to the defaults, moves the camera, and adds an opaque source that shows the axes of the simulation coordinate system." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# set the lens type back to plane-parallel\n", "sc.camera.set_lens(\"plane-parallel\")\n", "\n", "# move the camera to the left edge of the domain\n", "sc.camera.set_position(ds.domain_left_edge)\n", "sc.camera.switch_orientation()\n", "\n", "# add an opaque source to the scene\n", "sc.annotate_axes()\n", "\n", "sc.render()\n", "sc.show(sigma_clip=4.0)" ] }, { "cell_type": "code", "execution_count": null, "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 } yt-project-yt-f043ac8/doc/source/visualizing/_images/000077500000000000000000000000001510711153200226675ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/visualizing/_images/all_colormaps.png000066400000000000000000006261071510711153200262400ustar00rootroot00000000000000‰PNG  IHDRX ì=:EbKGDÿÿÿ ½§“ pHYsaa¨?§itIMEà  5Gƒ[ñ IDATxÚì½ypǹ÷ÿí™s$í: !t™}1Á,– f‰d+^ˆ¯œØÆ©ûÞ87T½©—ò/UÑMÕKáªø›Ê½©äâËs†Ä"³c! c_K¶¶­$e¦œsFg“ Ðöý¨ºz¦—§Ÿéî9óL÷¨[IB!„~CaB!„ÐÀ"„B¡E!„B‹B!„ÐÀ"„B¡E!„B‹B!„ÐÀ"„B¡E!„B‹B!„ÐÀ"„B¡E!„B‹B!„!„B¡E!„B‹B!„!„B¡E!„B‹B!„!„B¡E!„B‹B!„!„B¡E!„rÏ0 TÁEEEžƒ£Œòº¨~6GùÉ·öóEÄ{eû\d Wƒ”Oìgåüdîç®`0Ưbîmg: ÀQ¯ßİÈòúýÝ ™èD:aC'2ÑÕÏÒ:üœ»eKínxÝÍ{ü‹aÐêu-´~”íö“ÝÚï×â˜4 sæ sî\tΙÕ󜸅¬àx©(@d¤GfT`6÷«î‘‘‘°X,†BÜÓVþÍo~ç26 ü–`#‘ÝŸ+›B†¦×@²ÈÈîO’•MÈ-©««ÃÉ“'qåÊܸqÏ>û,¦L™’®±±‡Bmm-t]ÇèÑ£±víZ$$„I?zô(ªªªðòË/‡ßºu+êêê_-„ÀüùóñÈ#àw¿ûž|òI̘1#$ïîÝ»ÑÐЀŸþô§8räŽ;!¤ì¾wRRRð³Ÿý ðæ›o"55«V­bƒÓÀâK0dˆ¡÷Üç!·Æét"55óæÍûï¾6Mss3þë¿þ óçÏGNN"##qíÚ5˜Lwþ¨óS999áf³‘‘‘xàP^^b`9N|öÙgX±b…!gôèÑxá… ,Eá§Ð4°øL†Á¢ædX’ììlOÏ“áû^ii)xà,_¾ÜKJJºë²Íf3bccÃÆÍ;ï¾û.ÚÚÚFÉ>ûì3H)1sæÌc*&&†I‹B"æ¾”¸xñ"~øa¼õÖ[hhh@bb"–,Yv*±? ¿˜˜TTT`Ù²eFxEE¦NЍ¨(6 ,B!dhróæM8Nœ{ö,Î;–ŸŸ™3gBQÌž=;ÀÀjnnÆ¥K—ðüóÏä¹zõ*6mÚ6kÖ,äåå±i`B!ƒß´á”)S°páB@jj*._¾ŒO>ùä® ¬Y³faÉ’%aþS†sçÎʼn'ðõ×_Ãf³¡¼¼‰‰‰°ÙlyRRRPXX0ÅÉÆ£E!„ N¢££¡( RRRBŒšË—/ß•ìÈÈHX­=/¤˜œœŒÌÌLTTT ++ çÏŸÇüùóCÒ©ªÚ/ß„‘¡ÿ}BÈGUU¤§§£©©) ¼©©©Ç%ú“¹sçâóÏ?Ç矎7n`Μ9l”—i à .Ó@ÍɰÄét¢¹¹Ù˜^kiiACC,‹a@-Z´ûÛß‘‘›Í†/¿ü/^ĺuëz•ír¹ÐÐÐaŒZ¹\.ØíöƒÎb±çÓ§OǾ}û°wï^Lœ8ñññ!åèº"œnìèèÑ%66¶Çÿb$4°z†ÿéMFxâ2 „Üšúúz¼ùæ›B@fÏž‚‚ÀÔ©S‘——‡?üû÷ïGrr2~ðƒ`üøñ½ÊnnnÆþç„Ùl6ã#õsçÎ…|ä>qâDüð‡?4ÎÍf3f̘sçÎaîܹaËillÄ–-[ºï )a2™ð«_ýÊûôÓOñé§ŸäËÉÉÁÒ¥KÙ h`ñ%˜ pâ5'ÃŽ¬¬¬î=l{aîܹ=8áxä‘GðÈ#ôÿâ‹/öYV~~>òóó靖Û-‹ ~þ,¾“ÞŸ8‚E!4°!„B ,B!„X„B!4°!„BH7¢¨¨ˆ_ªB!„ô#Á"„Bégl,¹Ú;p&H!(^_@ÂÏPŒpÅH/ ¥'^ñžûò(RºSûÉV HYJ@¼¤‹ÒƒnÝýÂH￳+Ð=çÂã+^ß?L (NèPâ½i„ÇWÎ%`È“žò„îõ½ç†Ýe!¨¼îønç‰÷8Eèá]Ix.PD /¼&¼ÝK& ï¹ä‡“^BøÉ(/Ø÷;V¤Uü/Âã$4 èR@—€&4Ðu@Ó·.àvnMÀ­šæ‰7œ7ÜåõÝAçšÞ^×=ñ.wwZÝ_–*˥ݺl·Ÿ<ÿ²t]@jt@j€Ð%T)¡èªT]¤I˜t¯ó;V}Nz_¸/â•¡J@ #ÃHïuŠ_Zÿp“Ö]†!Ó/ÞT®I—2°¿BhŠ]Ñ! RH¯è'• G°F*\Z‘°72p\¾|ÿöoÿ†mÛ¶ :ÝÞ|óMìß¿ŸDh`BZ”——㡇B]]nܸÁ !ƒ«àöà¸ao"d p:¸pá~úÓŸÂn·£¢¢K–,1â¯]»†C‡¡®®RJ¤¥¥¡  III<{ ž>}ÍÍÍˆŽŽÆÔ©S±fÍ@WWJJJðÅ_@Ó4Œ;+W®Djj*àèÑ£¨ªªÂw¿û]9rÈÎÎÆã?ŽˆˆìÚµ µµµ¨««ÃG}!þõ_ÿ‰‰‰l8XÀ!´BÏÕ¡¤ëÐRvÜOÃäø¯ÒCòg2„ .`Ô¨QHNNƬY³°ÿ~ÃÀjooÇÖ­[a³Ùðâ‹/"22—/_†®{¾={ö,JJJ°bÅ dggÃápàÒ¥K†ì;v ""?úщO>ùýë_ñꫯÂb±ðl ýÅ_à¹çžCgg'vìØ'N 77«V­BSSFÜÜ\H)ÃGk  ‘gŽ”CÇJº-eň¿ŸîÇ%и>ÍZ^^ŽY³f&Mš‡ÃÚÚZdeeáã?FTTž~úi(Šçë«Õjä=~ü8~øa<ôÐCFXZZàÒ¥K¨¯¯Ç/ùK¨ª xì±ÇPUU…Ï?ÿóçÏ7ò ""0{öl|õÕWÈÍÍETTTU…Ùl¦aE†¡uŸ~œ8FBØ›¹¿\¿~ß~û-ž}öY€¢(˜>}:ÊËË‘••…«W¯"33Ó0®ü¹yó&nܸ›ÍVvCC^ýõ€p·Û––ã<11Ñ0® 667oÞdã``qŠpÄëÊ)Â~¾Ÿ†É%pkHþLpîÜ9H)±eË–€pUU±zõj˜L=?Îz‹<ßvÅÅÅaݺu2°v¢¢¢Œã`ãM’žái`qŠðÞT+§G¦Ã)ÂaÉP¬]×qþüy¬\¹&LˆÛ¾};.\¸€1cÆ ²²º®‡B‘‘‘HLLÄW_}…¬¬¬ùiii°ÛíBÄß ªªÒà"Ý9«€BÈ`æ‹/¾@WWæÎ‹Ñ£G¸©S§K78üÏÿüêëëÑÔÔ„ÊÊJ455yäœ>}gΜASSêëëqæÌÀĉ1~üxlß¾555hmmÅ¥K—pøðaÔ××÷YÏÄÄD|óÍ7hmmEGG­§ïàí¯?Æ8EHeý7Xœ"– Å)ÂòòrL˜0‘‘‘!qÓ¦MéS§ÐÞÞŽ^xÄ›o¾ !RSS‘‘‘˜3g4MÃéÓ§qàÀDGGcÚ´i†œçž{‡ÆîÝ»qóæMÄÆÆ"33±±±}ÖsÑ¢Eصkþð‡?Àívs™†‘~¯ ÔfÏCu«Å(›[åp«œ»Ý*ÇÇ­r¸U·ÊáV9dØÁ)BB!„X½Ç݇‘·þR•S„Tv°OÔ “)Bþ_é°kVBh`ñyÕ‹ªÔ•Êò~º/—À1ìš•X|û#„½‰Bh`B!„»fÀ–iû‚ßÞ¥÷O‡Îv!$à-(ÂëúœA`¾¿zê^ç˜_Žr°û]û*«ŸbB!„Ò Øk^QQQø7!! ç;ö{Šï-ͽ’ÙW9w#£¯ºõE÷;©ƒ»‘q7º•zí/ÝïW½ÞîýÕok½ÞÍ}/úÞ`ýÍê¯ß’àðßüæ7|*Xdä ¸2!÷ô>á=vkvíÚ…ŠŠ ãÜb± ==+V¬À˜1czÌ÷Ûßþ .ÄâÅ‹°ƒâäÉ“xñÅö&ܺu+ñýï¿ÇgŸ}S¦L ‰«­­Å›o¾‰7"**Ê8=z4^yå•€6Þ¼y3V­Z…9sæ~ÿûߣ­­-¤O<úè£zÀ[o½…¯¾ú ?ùÉOBfõÕ‘Ï€ÇôéÓ‘““sË ¯ ,2H)ù äÞ'¼ÇúFvv6 ¥„ÝnGii)¶mÛ† 6ô˜'++ µµµ†Jmm-P[[kXn·ß~û-æÎÛ¯†rKK *++ cª§|¹¹¹˜7o^@xðÖ@mmm¸|ù2zè!œ;w.ìÊ÷¾:Ò4 õõõعs'„X¾|9;Ð}†ß`¾]2À÷ ï±¾¡ª*bbb‹ÔÔT,^¼íííèèèè1ÍfÃåË—¡ëžŸr8hhhÀâÅ‹Q[[k¤»|ù24M Ñê,X€#GŽ@Ó´^ÓEDD 666À™Íÿ©R^^ŽÉ“'ã;ßù.\¸·ÛÝcÅÇÇcÊ”)˜8q"jjjØyh`‘ÁúfNh{wŸð»}*++aµZÝcº¬¬,8Ô××.]º„äädL:ß|óa¤ÔÖÖ"11±_7gB`áÂ…ÐugΜ¹ky˜5kRRR`µZñùçŸ÷šþêÕ«¸téT•ÿã9pŠBÈàâŋشiÀét"..………½æINNF||úhØ4‹- ùN+>>Þ8.//ÇŒ3Œè3fàÀhiiARRRH9Nœ>}Š¢`êÔ©ì<4°!„ð˜Íæc"??›7oFYYrssñÄOÀåò,uë?-–••…ýû÷£££W®\Aff& 33eeeÈÈÈ€®ë°Ùl÷DoEQ››‹Ý»wcÁ‚aÓDGGÃjµ†ëììDUUt]ÇÙ³gp)%ÊËË‘››¶Žžxâ üñDyyù]}¼Oh`Bø¾£Š‹‹ o³ÙŒäädÄÄÄÖûï¿êêjX­Öó÷Ó§OÇ©S§pôèÑÛÎ{þüyÄÇÇãŸþ韾׫©©ÁéÓ§‘““öÛJ!–,Y‚’’Ìœ9“K5Ügø‘;!# ~PM†*š¦Án·Ãn·£±±ûöíƒËåÂäÉ“{Í—””„„„|üñÇßY%$$ ..eee}½jmmECCC€s:aï­àóåË—£¼¼ÜeóÇét׿s‡€gzpÚ´i5jFm¸¹sçâæÍ›¨®®îÕ°Sü1;Ð}†æ,¹%ü¯3Bîí}Â{¬oTWWcË–-<ˤ¤¤`íڵƔ_oØl6TVV†|Èž™™‰ÊÊÊ>XB”””„„¯[·.l;ŸÛl6Øl6|õÕW!2Ž9‚#GŽ„ÍŸ?óæÍÃÕ«Wñøã‡ä‰ŠŠÂ„ pîÜ9dgg‡EQ,X°'OžÄƒ>²ô¹‡¿ EEEò:Ë­r¸UNÔÁ`¨×þÒ[åp«n•írÈðS„„oׄp‹B‹ÜoøÍ roïÞc„ÐÀ"„B!4°!„Bh`B!„ÐÀ"„B!Ý Ø2 „B!ÃŽ`B!„ô3¶’{àB£ÂÏW‚ü`§ô’Né£Nf89}”%¼¾¢xœê;öÆù|ÕæóƒÓøÂTß/}°ªªßyoNíáX ºüžâ|e©~éD˜üJP×t¯“4¿s=(Îw¬ùÁDzۗÞpÃ÷Kã;–Þüƹæw.ƒÒkù H(0IÀ¬^g–€I÷„«Áq~Ç:á—Ö—>BÌnÀ¬yœ¢ªŸoru;Õwìî>÷O«º»ãL.Ϲ!O÷6Cp·õ?÷¶¡/\õÿô"8OÐ-#‚úGp:t+Š ~ÔgÝüËéÃê&ÂäíA÷`?@7ÿºPÂèx«òz¨ tOõTO⺣‡ú «›èC½]cºõ¥OôÒ~ó›">• G° Ë+±i!„ ì!„ÒvíÚ…ŠŠ ãÜb± ==+V¬À˜1cxfEž}öYL™2%$EEöïß7†ÄùçkmmÅo¼ú‚"~üãcܸql B‹ üw‰AÝ4Å"C™ììl@J »ÝŽÒÒRlÛ¶ 6lè×r„xþùç1jÔ¨€ðèèh6¹-8EHÈý@P}ê:*ìV¾ªªˆ‰‰All,RSS±xñb´··££££_F¤„ÅbAlll€S>. ,Â'ÛàCí¦‘¬jöÍATù‡•••°Z­Y"ƒN>ÙFHÓÐþ%C™‹/bÓ¦M§Ó‰¸¸8Þ“²þò—¿„lŠþÚk¯± ,Bbh œ"æ}sT¾ÍfC^^¤”èêêÂÙ³gQ\\Œõë×#!!¡_Ëzæ™g’’¾Aî N>Ùîœ")U=²ûæ=¬|³ÙŒ¤¤$X­VŒ;ùùùp¹\(++»eÞÈÈH¸\®ð®®.@TTT@x||<¬Vk€#„!„ƒÛí¾ešääd躎+W®„×××Cäääî—Á7EÒ?pŠpèà~0ÀS„wû §‡yß•¯iìv; ³³ü1\.&Ožl¤immECCCˆq5zôhL˜0»wïÆc=†¤¤$\¿~%%%˜1câââºï)ÑÑÑa”å#** &™„á“Æg?6ÍPúHžô`…ÝÃʯ®®Æ–-[HIIÁÚµk‘™™ééãB ¤¤$$ߺuë‘‘gžyGÅÞ½{qãÆ ÄÇÇcêÔ©Xºtià½"Þzë­ƒK§žz 3fÌ`!4°!„  PPPÐkš_ÿú×½ÆGEEaÕªUXµjUio)‡¾Âo°Hÿ¾ “ðˆ¡Ý4œ"dßdåB‹Ÿ#X}ÚùXa¬|Bh`‘Aþ&LØ4„B‹B!„/¶EEEÔ%„BéG8‚E!„ÒÏ Ø2 EEE=˜| D ïïT0™<¾ÏùÇ…sþé}é‚ó˜L½Çõ$Û?Opú`ÝÃÉ ¾¶àò‚uw­áÊë­L¦À:Р¡ÛéAÎÞãµ[¸piÝ·çîƒ,½‡xíº¹o¡[Oqz¯Më£î: ¤ç;)%È©·p·Jۓܸ½ÅëAuü¦§ç!Á"„Pø‘=!„!„ô3üГ2ÒàJî„{G°Èݰk×.TTTç‹éééX±bÆŒÀóÙɳÏ>‹)S¦„䯨¨Àþýû±qãÆ8ÿ|­­­xã7Bû¯øñŒqãÆ…ÕïèÑ£8zô¨±Qt\\²³³±|ùrX,#Ýïÿ{´µµ…È~ôÑG±xñâò#""€¬¬,,\¸0`SjB‹B8‚Eîšììl@J »ÝŽÒÒRlÛ¶ 6lèß—!ðüóÏcÔ¨QáÑÑѽæ=z4^xáèºŽÆÆFìÞ½‡O?ýt€ìÜÜ\Ì›7/ odddØò].®]»†>úÿñÿÂÂBØl6v†!§ !„ zTUELL bcc‘ššŠÅ‹£½½ýû2 %, bccœ¢ôþ¸T111ˆ‹‹Ã„ 0}útÔÔÔ„¤‹ˆˆ‘m6›Ã–Ÿ””„É“'ã…^@zz:vïÞ )ùºB‹B¹8TVVÂjµÞrdi hiiAuu5TUí7™ .D[[êëëÙ†œ"$„2è¹xñ"6mÚp:ˆ‹‹Caaá=)ë/ù‹ñ=•×^{­×&JJJBò­[·xæ™gpôèQìÝ»7nÜ@||<¦NŠ¥K—öU!ðÖ[o\B<õÔS˜1cÆméüÝï~»víÂâŋ﬎9‚#Gޤ›?>òòòBÊ7›ÍHHH€ÍfC~~þM-’üÝ+**—KnöÌÍž¹Ù3¸Ù3¸Ù37{æfÏdxÂo°!„Bh`B!„ÐÀ"„B¡E!„BîœûÈB!d¸Â,B!„~fÀÖÁ*z­Èsà?óù¾ÿÜõÿÞpÇÁÿᧇIÛSž¾Ê 'ûVio'þNuœªÇA„âuÞbWT@˜ºÓñ^§øÅ)Þò÷å÷ùŠêç›Ã|åy‚âDOq¦î¥$„ tE º“Ëã«n@Õà‰÷:Õ ¨.ÀäîNã¬hÝyT¿ôªP|òônYF^We¹ýtòÊ4zµCÑÚ h­P´6´AG‹ÐÐ*t´ ŠâqªÏW5˜LTÕëüUÍHçKëIï6Ž}qŠªCQ$L&ª*¡ªzÀ*Bxª\UÕÔ}ìï+Þ•@„Ò}®z»ƒêmB#^éAŽßJ"þë&ÿæê.ðï&ÁÎÔÝ _éî¦Bõ®ÁuÇpB ë¡âL8•½. aêá6oÞŒ²²28NŒ;O=õ¤ ”íûnkçÎèêêš5kUUñç?ÿš¦y†¦[?%pe#!øÃH†’E!„ô·Û´´4|öÙgˆŽŽFdddØt—/_F^^&MšhkkCGG‡?fÌ´··£©© ÉÉɬXr× ¾…FGÒ  žlZêÌë W$üš4MƒÝn‡ÝnGcc#öíÛ—Ë…É“'cÖ¬YˆŽŽÆöíÛQWW‡––|ýõר·oÚÛÛÉÉɨ¬¬Dcc#¾ùæ¼÷Þ{0›Í†ü¬¬,ddd`ÇŽ¨©©AKK ¾üòKTWWóÜœ"äµöMOYþsŠWtÿŒ¼ÛïmÕÕÕØ²e "")))X»v-233ë֭áC‡°cÇ8ÄÇÇÃf³#ZO<ñöìÙƒ?ýéOˆÇ£>Š”ñƒüÀßÿþw¸\.c™B†‡E!„øQPP€‚‚‚^ÓÄÆÆöš&55?ùÉO¦M›pn±XðÄO„Í?gÎÌ™3' lÊ”)øõ¯Í"aá!¯•mÂ*VÍÌ)Â{d+2ä ,NRO6-uæu ó?*„!„Gœ¡E!„2Я%EEE«%„BéG8‚E!„ÒÏ Ø2 o} ôSG @ôà{âeÐ9„õäºótç…Ÿ Ý®ûÉÑò9@x}E:c¯/¼aJ/z‰ ð˜>¸,Ÿáâýå(~ò”0º‹0ñªÒ·¼jÇŠO§0õ¤ø‡#0oˆP=Ò„‘ï¯ze¨~ç¾<"LýT_ÝÊð¾cÝ/\ïö&½ld³Ç×[ét ÐÝ€î†p; 4'„Û ¡9¡¸]PÜN(šŠæòK«š BsA¸½¾æ•¡k€®AÑ5¨n ЦAÕÜP5@ÕU ˜tÀ¤ ?¨š€âóu@è>ßs š4@h¾XRBxý€sÝçtï'úÒÆ±ðÊþa½œÃ¯Z)=]NJ(2è×Dv7£âËÓK³†uÞ¯€î€Ð®øê˯;èAaẠí6áÓ‡Õ]¿MÝý»%zëWw!õÖƒï÷ëî×3$þhQÑ]=WZ[[ñÆoàå—_Fjj*Ÿða¨­­Å›o¾‰7"**jPê¸k×.tuuáÙgŸ¥EHÀ/%é{}I¶»éëC·¢¢Â8·X,HOOÇŠ+0f̘î>3Höô2BH)ƒŒŒ <öØc{*„¡Ôuäk!„ˆÇôéÓ‘““Ó§}9‚E!„ ²³³QPP)%ìv;JKK±mÛ6lذÁH¼Ùó€¾ W_}hjjž={ðÎ;ïà•W^¹k#GJ9àÆ¤¯=4MC}}=vîÜ !W¿§Eúþ»Äà¨/9¼ªˆ£Xćªªˆ‰‰àY¹}ñâÅØºu+:::’¾¼¼%%%ظq£VUU…íÛ·£Èo:²ªª ÇŽCcc#âââ0{öl,]ºŠr÷Ÿ)GGG#** ±±±X¶lÞ{ï=477#99§OŸFyy9ZZZ`±X0yòd¬X±6oÞŒ²²2äææ†ÖM˜)Bßôœ§Ó‰œœœÃÀ¿¼`ÒÓÓñòË/ç±±±=·x饗‰˜˜ãÛ*Àó}Õ;#|>ú(, .]º„÷ßš¦Ál6cÞ¼y˜4i.^¼hc+W®Ä‚ –çt:Ï=÷âââö}üϾœœ,Z´è¶Ûã‰'žÀÿøG”——cîܹ†>cÇŽÅSO=ò]ULL̰ùÖŠÖ ºàáˆóà!‡‘Õãïn·;lxtt4GÀHTCCC@š´´4455Ájµöí¡i2õ9-à™ú ·ŒB}}=¤”X¹r¥váÂ…tñññøÎw¾ƒï|ç;8tèÊÊʰ`ÁcÌßH5jL&ÚÚÚîx:0&&Æøpý¶îU!°dÉ”””`æÌ™0™LHKKÃgŸ}†èèhDFF†Í‡o¿ýÖÐW×u\¹r¥Ï#nƒ®äN!dH iìv;ìv;±oß>¸\.Lž<9lúqãÆÁl6ãСChnnÆùóçÖÒ€eË–¡²²Gŵk×ÐØØˆ . ´´ôîß^Fh¬V+t]ÇG}„––TVV¢¬¬, ÍþýûQ]]––Ô××£¶¶£FcJô‹/¾ÀÍ›7át:‰E‹aÿþý¨¨¨@ss3®\¹‚3gΠ²²²OzÝ Ó§O‡¢(øøã³fÍBtt4¶oߎºº:´´´à믿ƾ}ûÐÞÞx衇pâÄ TUUáúõëøÇ?þ®®®aÑ_9‚5`ïåÃpÚM!]ËÐÃP’ËÑ™¡|w ª««±eË@DDRRR°víÚ€ÑÿiA‹Å‚'Ÿ|Ĺsç0aÂäää`Ïž=FšI“&¡°°ÇŽÃÉ“'¡ª*RRR0oÞ¼»o›^Ö©JMMÅÊ•+qòäI>|™™™X¾|9vîÜi¤Ñu|ðÚÛÛ‰ììlcÄ+>>9998tèvïÞm,Ó››‹˜˜œ8q---ˆŠŠBZZ–,YÒ'½îjÄFQ°`Áœø Ìf3Ö­[‡C‡aÇŽp8ˆ‡Íf3F´-Z»ÝŽ]»vA¹sçbêÔ©ÃÂȰ͞¹UÎ0Ü*G=o›Ã­rº×‰ônÓÏ[åÛå ý­rB¸U·Ê!dˆÀ)BB!„XÃN¸®ƒNޤKa'„¹? 9Äô¥®„°ÇBh`¾JÔúâ2 „B‹B!d¸1`Ë4¼X4T7rjÓ{CÝëH*€h¯¤¯g 3[Š¿„p?‘„B!„!„BÈàeÀ¦+‹žHèРCƒ„ ]ÐÐ 7:àF'tá€'tá„'4ÑéI#:¡¡ÓîòÆ9=é4ÑMq@‡žx8!á†nè )4è ]hBƒ: T* ¡BBñúB XˆS‹~z®IúVßóúRè^ФðøB†\!( ED¾Š("ªˆô„à E˜!`‚*"¡" ªˆ‚Š((" " Â śτh¨°@…&X "&X`B4 ¾<P¥&˜¡J3T˜¡J8¡J'8a’Pe'TÙ “ì„'é‚"P¤*º Ê`ç€*ž4pCH—ŸïòÊvAH7´n'ÝP AÀ?\÷.ÿªCøV0ôNºˆ • ¥oÑV! …€ ¤¢vûŠ ºbòú*¤0A**táS# +^ßìçLЕhj45ºÍ—V1ûÅEAS-†s{¦Z È2Á­v­vBS;àV½ýYq@S<¾[鄦tzýãÜÓ¿»ŒûB.hÂÛï½ÎçöÞ.Hé‚$œÐá€.Ýн÷ž7¤”ºg )è „®zœT!tÂsì[pTÝÓ¼R: u hRÓ=¾®CjnH]ƒÔ4c±Qáõ]¢›¡jf(ºÇ ÝH„n‚¢G@•‘PôH¨2ªŒ‚ªGÇŠŒðô{é¹OLÞ~ïëû " §ŠH˜„ªˆ†I±@Ïý&"¡(P… ª*aR$TU‡IuävBU;`2uBUP'ÕéñM¨¦N¨¦.¯ï„¢ººÉÕìqŠÙ Åì€bvyPLnUƒP5ϱIƒbvC˜ÝPÌn@•ªn8(BñúB4xnÕàuÒ𤮺©)Òã H©B—*¤4C—&HxœÕ{¬BÐDt ДH¯‹‚¦DBf£ïëŠÙÓÇM¾þå½'<÷ˆ&"¡¹-pkÑÐ4 4-RtÏ lû㦻z®´¶¶â7ÞÀË/¿ŒÔÔT>áÉÈ4°!„¾²k×®€}- ÒÓÓ±bÅ Œ3Æ¿WÛÀø¨®®Fqq1þÏÿù?ˆ5ÂûÛßÂd2á¿øEˆÁ÷ /Àf³±i`B!ƒììl@J »ÝŽÒÒRlÛ¶ 6l0ÒÜ«Œ}ddd@UUÔÖÖbÆŒ€ÆÆF¸Ýn¸Ýn´¶¶"11ðõ×_Ãd2aüøñl<X„BÈàDUUÄÄÄbcc±xñblݺˆŽýoÛòòr”””`ãÆFXUU¶oߎ"¿}«ªªpìØ1466"..³gÏÆÒ¥K¡(¡Ÿ)GDD`ìØ±Vmm-222 ¥Dmm-æÌ™c„7&“çQ[YY‰3gÎàúõ눈ˆ€ÍfêU«)%~ÿûßcéÒ¥øÎw¾c”wåÊüéOÂ/~ñ $$$ «« %%%øâ‹/ iÆŽ‹•+WS¢ Ø¿?êëë!„@rr2òòò0vìXv X„ bHŠ&l‡»ª¡º|„Ãá@ee%¬VkXã èyºÐ?¼®®;wîÄš5k™™‰ææfìÙ³B,[¶,lþ¬¬,üïÿþ¯q^[[‹¬¬,èºb`Í›7ÏH§ë:rss‘œœŒ›7o¢¤¤»víÂsÏ=!f̘O?ý4ÀÀúôÓO‘‘‘„„ÀŽ;ýèGˆŒŒÄ'Ÿ|‚¿þõ¯xõÕWa±XðÞ{ï!-- ùùùB ¡¡ªª²³ÓÀ"d‘÷î Ì5O“¡[/^ĦMžáN'âââPXXxW2;†%K–`öìÙ€ÄÄDäääààÁƒ=X6› 'Nœ€ÝnGll,jkkñðÃCÓ4|òÉ'€ææf´µµ!++ËÈ7wî\ã8)) «V­ÂŸÿüg8NDDD`Ö¬Y8}ú4ÚÚÚ)%.\¸`èQWW‡úúzüò—¿4Œ¦Ç{ UUUøüóÏ1þ|´µµáá‡Frr2Àjµ²£ÓÀ"„BzÆf³!//RJtuuáìÙ³(..ÆúõëžÛ¥¡¡—/_ÆñãÇ0]סi\.ÌæÐ•tÇEQP[[‹1cÆÀív#-- º®£££­­­¨­­…ÙlƸqãŒ|õõõ8zô(®^½ŠÎÎNã{±¶¶6Œ5 ©©©HIIÁ§Ÿ~ŠÅ‹£¶¶7oÞÄ´iÓW¯^…ÃáÀ믿 ÛíFKK à»ßý.vïÞÊÊJL˜0Ó¦M£‘E‹†S„lâXCiËl6#))É8ÏÏÏÇæÍ›QVV†ÜÜÜÐë 3E¨iZÀ¹ÓéDNN¦N¶¼žôHOOÇ×_ŽŽddd@UU1~üx|ýõׯwY¾‘&§Ó‰ââbLš4 O=õ¢££ÑÖÖ†âââfÍšeXŸ~ú)&Mš‹ÅbȈ‹‹ÃºuëB>把<òÈ#˜9s&¾üòK|ùå—8zô(ž~úiL™2…ž!§GD“áWn·;lxtt4GÀHTCCC@š´´4455Ýö(ÍfÃ… ÐÙÙ0 ˜‘‘ÚÚZÔÕÕ|Kuýúutvvbùòåˆ|ûí·!rgΜ‰ÒÒRÔ××ãóÏ?G~~~€®v»BˆC3˜ääd$''cáÂ…øÛßþ†òòrXWr'„2$Ð4 v»v»Ø·o\.&Ož6ý¸qã`6›qèÐ!477ãüùókiÀ²eËPYY‰£GâÚµkhllÄ… PZZÚ«.YYYhjjBMMM€•••…ªª*´··¬}•UUqæÌ´´´ ªª*`ZÒGbb"Æ÷ßRÊ€k›8q"ÆíÛ·£¦¦­­­¸té>Œúúz¸\.|ðÁ¨­­5âêëë1jÔ(vž€#X„øà!›xÖÇPŪ®®Æ–-[x–KHIIÁÚµk‘™™Ù}M~Ó‚‹O>ù$<ˆsçÎa„ ÈÉÉÁž={Œ4“&MBaa!Ž;†“'OBUU¤¤¤ü÷_8Æo,¿––f„§§§CÓ4c9111(((ÀáÇqæÌ¤¥¥aåÊ•xçwBdÏœ9|ðfÏžm”áã¹çžÃáDZ{÷nܼy±±±ÈÌÌDll,EAGGvî܉›7o"::S§NÅ#<ÂÎN‹„S„#¢‰ÉЬ‚‚ôš&11¿þõ¯¦L™2=lúè#|ýõ×Ö­d755a÷îݨ¯¯GRRV­ZÅÎC‹B¸w*‡ÃÊÊJX­VDGGÃéty=Lú‡×ÕÕaçÎX³f 233ÑÜÜŒ={ö@eË–õI]×QVVf€}•-¥Ä»ï¾‹ØØXüä'?AWWöïßߦ9 ¬aðÎEXµ„þàâŋشiÀét"..………w,ïØ±cX²d fÏž HLLDNN6oÞŒ²²2Ì›7/ÐÈ 3Õ¦iZÀ¹ÓéDNN¦N¶¬œœ,Z´(¬.111°Z­°Z­xâ‰'ðöÛoã_þå_ŒoÄz“m2ñ±K‹ï\„UK¹Mî÷ µÛí ‹ŽŽ†Ãá€Ëå‚Ùl€5²ÒÒÒÐÔÔ«ÕÚ£å3˜z#==iii8~ü8V¯^Ý'Ù£FB{{;ìv»1ŠõÍ7ßð¬aŸV-!„ïT·ƒ¦i°Ûí°Ûíhllľ}ûàr¹0yòä´ãƃÙlÆ¡C‡ÐÜÜŒóçϬ£Ë–-Cee%Ž=Šk×®¡±±.\@iiémë¶páB”••áÆ}’=aÂX­Vìܹ ¨««»£rÉà†#X„B=ÕÕÕØ²e "")))X»v-233ÑÚÚ0úc±Xðä“OâàÁƒ8wî&L˜€œœìÙ³ÇH3iÒ$âØ±c8yò$TUEJJJÈtcˆf”);;III8~ü8¾÷½ïÝR¶Ï>û,Þÿ}üùÏFbb"V¯^Í܇ÛËFQQÑ€Œ1 ®ÍžM¸óŒ¹Ùs¯º›î ¸Ù37{æfÏÜì™!§p‹UK!„ÐÀ"„2(ágÚ„ÐÀ"„B¹' ØGîEEz1.¶Ê@âûX‚ àû¿çc3_SðŽ|ßûBÈÂ,B!„X„B!ƒ››"ü¿ÿ߯RJ¯Às 0¸eúÞÒJ¿s„‘>>¸,?Ù=¤—AéÃÊCyýXw–^v×C˜x]Êz”a®[È’·•Þ¿^eŸäûÅËðñy¥÷:à;ê?þ²à‹÷–$[Ñ©[ÿ|zOº‡K¦¿†÷CÓ£—ôºûé¶]dP~‰°íš¦‡þå§«ô*Ó£î²wy>Y[noòn­[`¸ôÖ™×Ñ} yAéÃÊ ’œ®·4ÑÝücÓ}Qºß±§ö®ç`„ûûAòà—Ö—Ï(;(oŸâƒt°±`ŸÊ„#X¤¿T‘°‹SWr´¶¶¢¨¨(d;BFä!„ÒvíÚ°ÕÅbAzz:V¬X1cÆxlbîãGh`‘ðHªHØÅ©+éììl@J »ÝŽÒÒRlÛ¶ 6lð4™d£X÷ !‚¿{ƒþ—rðkFÏЊñ5óÿª"鯢äP<îàŠ¬‹%]Á{«8TVVÂjµ"::N§3èe[ôðÞ^WW‡;wbÍš5ÈÌÌDss3öìÙ!–-[†Ï>û }ôžyæŒ5 v»W¯^e³“‘i`ñÇŠ*ööõáÉÅ‹±iÓ&€ÓéD\\ ïXÞ±cǰdÉÌž=˜˜ˆœœúo¼ñ&Mš„ììlLž<Ù˜>$dDXœ"¼ÇzrŠwsÅœ"d¸ Ìf3’’’Œóüü|lÞ¼eee˜7o^ÐËv衦içN§999˜:ÃIBˆ IDATujزðꫯ⫯¾BMM >øàœ:u ëÖ­£‘EFžE!ddáv»C¢££áp8àr¹`6› d¬´´4455Ájµöü€4™ðÀàÀƒ>ˆÿ÷ÇÕ«W‘––ÆŠ'#ËÀÚS„C@OÙcE‰ú¥ ñz•ÔuÄ÷»@Ó4Øív@gg'>þøc¸\.Lž<9$í¸qã`6›qèÐ!<ôÐCøæ›oÖÑ€eË–áwÞA||<¦M›!®^½Šk×®!77ÐuÝuþüy˜Íf$&&²éÉÈ3°8Exõä!ŸÇwsÅœ"d¸ ª««±eË@DDRRR°víZdff¢µµ5`ZÐb±àÉ'ŸÄÁƒqîÜ9L˜0999سg‘fÒ¤I(,,ıcÇpòäI¨ªŠ””cº1** 'NœÀ ë:ÆŒƒÂÂBX,6=éÛÏHQQÑ€üŒp/Âà½=1ƒz/Bô²‡¸¡OîEØÃ^„Þ€!±!÷ä^„Ü‹ÛeX}©ú]ãàß:A ‘1ᯢà¦rC¯§ ‚+–ÔuÄ÷Bh` M†öáÒ“S„|sQ+Âz%däX|¤Š„ý‡}B‹B!d2`ÿEøúÿû7Ö>á› é;YT Á»€>'!„Bî#6‚ua×ÿ |Þw"á9öùÇ!ºÃœN¤÷÷_¼_Yþqþù4Þ0EÞ8ϱ¿ßí¼ùýÒ†Äå —.$M€ÀôŠ ( „¢@ƒ"<ǾpEõºîsøÒ{ÓÂ+ËHïÍѯ¨A2Œ8¿²„bÈPºõð¦„7Ü—×[–·²ºËôäS 9¡¾ð®{#ºuö‹7dú•käõûû>'|oæ^_ˆðñ¢§x¿¼áÊ J/…€„\Ê2d¹ þKDx—€€4©õ¥×ƒüàå/ô0ò‚—âðÉÕ¥4\÷’Ýaºÿ±šâ»îïÖ©[¦ #XC®.¡IºîW¾Ÿ¯éÞ2tš®,{`äÑuh^_ÉÐtŸlï//¸¼€p=Pé/¿?8]€.Aiz’Ù[œ.%¤W¿°K8èzK;„‰3ÎuÏ1&¯Þ¶§x¹¾4Š–ð©L8‚EH(œ¿¹WÕÅše_&„ÐÀ"„BÑp³gÒp]œÛ®.Áše_&·b×®]{ Z,¤§§cÅŠ3fLùjkkñæ›oB)%L&’’’°páBÌŸ?? ­ÝnLJ~ˆ/¿üíííˆŠŠ‚ÕjÅÌ™31gÎcÓhBh`‘~ãNnCH×!Ý64†OK°1{%;;RÂn·£´´Û¶mÆ ¦×4Íû› ðꫯ"""n·_|ñöîÝ «Õ ›ÍhiiÁ_þòX,,_¾£G†ªª¸víÊÊÊvSiBh`‘»{——}5˜7þ¥ëP1²nó¬pÏ^>‹ÁÔ-ÁÆìUUˆÅâÅ‹±uëVtttÀétâ7ÞÀÓO?³gÏâÛo¿E^^ÑÑÑˆŠŠ<ôÐC8sæ ®\¹bX{÷î…ªªX¿~}ÀHURR +B‹ SŒUp»ÕÅ)BöerÛ8TVVÂjµ"::N§pøða<öØcÿ?{gE•æÿï­êî$÷Î !2@‚1 ›e0D c˜Ž¢#ê,,ÎꌳsvfÎþüídç·‡ãzêã×FœÕÁ£¢È 0È›DäE# J •¤yIhÈKw×ýýÑÝ•ª~É $¤;ýýäÔ©®ºÏ}îS÷ÞT=uï­{1tèPX,8θuuuhnnưaÃW®\A}}=***Ø Hè`‘ëü.Ï.Âè-ºƒ§$X˜]rôèQ,_¾ÐÑÑÔÔT,Z´È$SVV†›nºI?v:RâÙgŸx<ÀwÞ‰áÇΟ?)%²²²Lºžyæ]~òäɨ¨¨`!:X¤ßåÙEØÏäk|öòy-»û›‚‚Ì™3RJ´µµáã?Æ«¯¾Š¥K—ê2ùùùa^º}ôQØl6x½^œ:u 6l@RRn»í¶ˆé-Y²RJ¬Y³Fw´é œ¦BHÔcµZ‘™™ ‡Ãüü|Ì;n·µµµ&™pdddÀáp ''%%%˜8q"vîÜ p8B„t'Òb·!¡ƒEúï]¾Ç--ÒôþݶFòÚE9fN_• 󪸚Ö%!„Ïn·cÔ¨QØ·oÜn73”ÐÁ"×ñQÓãg¸êÇÓõ·5<×ke¯RN_• ³K¼^/\.\.ššš°qãF¸Ýîn¿ò“RâòåËp¹\¸xñ"¾øâ |öÙg;v¬.3{ölhš†—^z ‡BSSœN'<§Ó EᣒôŽÁ"„õ;v +V¬Øl6dggcáÂ…1b.^¼è_›4Œ«,þð‡?øZiii¸í¶Û0}út]Æáp`Ù²e¨©©ÁÖ­[ÑÒÒ‹Å‚œœL:µË±Z„ÐÁ"Wÿ.Ï.Âþ¡¦ià‡gýY8½ªyàW„ýGUUªª"/‘‘ßýîw!çGŽö|8RRR0kÖ,Ìš5‹Nú¶{’î5WqÓgatº$ŠK‚…I,B"¿‚“þÈ.æ,ë2!„!„BH\3`c°&TýŸ¨È À‹%0<ª×ü!}‹À`@*ÐÙZ¦†©,kBb¶`B!„ÐÁ"„B‰n¬•ý7Oý_¾Ià€Îh‚¿ þ°&DÞ† °nt‡¤Õ3[d-FÛ¤IO7á†øÆ8Òt²Ë´‘ÒBˆ¾à|ôí5Ù)'ÃØªIé?'¡ÉP[{ 2T‡AW籄”þsaÃ{ ×d{Px¸s~Ûç:Á¶Î4 22×˜Ž¹œŒñ´v˜ä¥¦ËÀdƒ_³I‡áO‡IAò&Û¡™Âd‹ —^°lCð^væW`ó…iúom7ê”×°:ÌzÌ{CÙhÆ´Be;u"(.:ë¨ Õkºßþ×ÂÔ[“¾`ÛBÒEh“VhñÿcË€tæmà8$<èØ¸¾ †‹/% …ItE²%è&^}û>• [°H"h+aý!„¨nÁ"„BzÂÚµkqàÀý8)) 7Üpîºë. 2¤Wºjkk±ÿ~455AJ‰ŒŒ àþáàp8˜Ù„$m%¬?d`3f ªªª ¥„Ëå¶mÛð׿þ¿üå/{¬ã­·ÞÂW_}…;••HMMÅ¥K—ðå—_bçÎ]ÎOH\;XB›âÚý“ÑÌÖ>͒ЊKXW™¹A¨ªŠääd¾emn¿ýv¼üò˸rå Μ9ƒW^y¿ýío‘˜˜8}ú4^|ñE<ùä“ÈÈÈÀ矎C‡aÑ¢E¸ñÆu½ééé6l˜)­µk×¢­­ ÇÇG}¯×‹ &`Ö¬Y\ø™Ä§ƒúŒâµ_î§2†l…xq®ÂÙ‘1dkìÝq,åööv¶Ëôp k%„™¸q°!„Äv»RJ\ºtI?úôi“Ü„ pîÜ9|õÕWÌ4r]`!éý k,dëu¶]„ÑW&×T0¼môw­ï5^¯.— ÐÚÚŠ}ûöÁív£¨¨‡éé騱cÊËËqîÜ9ìÞ½Û¿¸¸GŽÁ[o½…Ûo¿………HIIÁÅ‹qèÐ!~Hè`uyOdáõ¹Ÿò+¾}ó+Âè+¾›EzìØ1¬X±`³Ù… bĈ€ùóçcýúõxñÅ‘ŸŸòòr¼ùæ›& .Ô'ݵk4MCZZ PYYÉ"%}{Ë«®®Û×"ŒÁµkøÅÂZ„ƵæÀµ£b-B}]ÁX‹æ5¹!¸!!½dPµ‰†ŽsäHÕ~ya1dëulœéÊË.¶^õw­'„VLÝ¿ÙE8x”ƒ9Køeë*3—:X„/¬´•°þB,B!„ëÊ€}Eø_ÿùÿ˜û$êˆçÆŒø¾ú«Ê.0Ë!>Ø‚E!„ÒÇ X VÈ4 ÁŸ!æó@—òáãËFÐ…nÒ–=•|ŽÝ]¸á³ì[ÃËDÒ¥ËDJ ¡ŸGÊGÙM^E”‰dkòæ/ÉeЗãÁñBó$¤‚>ƒ‡þe¾ •1|2.ƒ”Ëõ<<8„•“!Ÿ°Køæ|›Q‘?¥ïé'÷= ïMz@Ø|ŒüI¾ì^ÝÛ.{šAåMv›– ºþÀ¦øi ô)$Lؤ3¼­2R“!qÂÇ•>ÙHù£Oõ`˜æ©6ŒSOÝ¡uJ‡L#¢M bÔ£I­SO˜iD"†KiLQ—€[«'ñ©LØ‚EâÁ¾7•=ŽR%„ÐÁ"„B‰,ÌÒc8_‰ŸÊG©F?k×®Åô㤤$Üpà ¸ë®»0dÈéhhhÀªU« „€”ÉÉÉ>|8fΜ‰ÌÌLf2¡ƒÕÙ¨¿eqÉ3‹• ¶LæÙ``̘1¨ªª‚”.— Û¶mÃ_ÿúWüò—¿ìÕ3â‰'ž€Ífùsçð·¿ý «W¯Æc=Á!¤d3¹Ë˜{Œ©Û>o@¬1iòÕXË1XцªªHNNFJJ òòòpûí·£¥¥W®\Á‰'P]]¶¶6]þôéÓ¨®®ÆÅ‹Mzìv;RRR0bÄüà?@SSΟ?‹/¢ºº§OŸÖeÛÚÚP]]††‰o‹ô»Ë< ôzé¸8ííí8xð ìv;„a[ ºk•²X|8^¯·Gò„ôv´Í¼©vÒZÒ-GÅòåËHMMÅ¢E‹®ZߥK—ðÑG!-- YYY¸téR˜^Bè`ùÞþØEØß,[±â¾Ä¢ÉWÛExý¯’®`d 0gÎH)ÑÖÖ†?þ¯¾ú*–.]Ú«gijÏ> )%<òòò°páB¨ªÊ &t°!„ÄV«ÕôµßܹsñôÓO£¶¶………!òn?ó;¢À£>Š„„$''Ãf³™Âz¢ƒž2¨Æ`…þƒDzLõø³õŠ• &M¾k9+Vðx<°ÛíRâÒ¥Kúyã`u#ÈÌÌ49W€oð;€—E®v´Í±åÁÒÉâS5Mfá`ÀëõÂårZ[[±oß>¸ÝnÁáp ==;vì@yy9Î;‡Ý»w÷àщÕjŰaÃðá‡"##—/_ƶmÛ˜ñäªáW„„B¢žcÇŽaÅŠX±bþô§?¡±± .Ĉ# ª*æÏŸ§Ó‰_|»víByyy˜wÄ®[£î½÷^hš†—^z ÿûß1cÆ f<¹ú¦êêêyaêÅžÇæ¸2ª{„s±gs^ÄÕbÏþŸcg±gê_L,öl¨l{æbÏ„°«·°‹Äfťɴ–B‹Ä/ìIâ§²ÇQª„:X„B!1À€}Eø_ÿùÿ˜û„\/Ø<2h !…|–Óf2ð°‹B!„!„BHt3`]„ÕÕÕþ_€!ÿ^ Ú”‡‡†©‚²0‹áœY_°Ž´ýý/Bø~ öþóú>8\1ɇ!ì±YÒ‹¤Ã(ÛP ý› EHtnŠÐ @óÉ-(ÌWÂͺüû@!;uAƒð§¥ôùt™õuê7Û¦ê¶ù7=}£ÝTxõcUhPý²Âh+:Ï«º®Î0]W@¯è<)¾Ö6 Í¿I@hþ®< @  ©HHÿ!û€œï—Õ÷½ «?(€-¦8þW5߇ƽbØ+þóJ9ÑM¸n/:uyÒ –Aþp@(þ0ðß+ôߦ½â‘eú`ÐgŒoú­ôoªA—>>:÷Æû•ñ¾eþ­è[è½Ré&<°·˜î§û`ÙpáSZÆýüÇð©Lè`ïpH‰Ëz?€ßåraçΨ««CKK RRR0dÈ”••aÔ¨Qxî¹çÐÜÜŒùóçc„ ¦¸/¼ðšššPUU…’’ÐåεËÊÊPZZÊ‚&t°!„ ~.^¼ˆ•+W")) 3gÎDnn.4MñcǰaÃ<þøãB ==09Xß~û-\.WÈúƒB”——£´´n·‡ƺuë––†Ñ£G3Ó ,B ¶b‘8¬÷r`Z±Ö¯_!–,Y«ÕªŸÏÉÉÁ-·Ü¢Oœ8»wïFKK ÒÒÒû÷ïÇĉqðàÁ½6› )))€©S§b×®]8~ü¸î`8p›6mÂ~ô#üýïGKK ÆŒƒyóæá‹/¾ÀŽ;ÐÖÖ†›o¾•••úr<ÄÞ½{át:a³ÙPPP€ÊÊJ$''°jÕ*,Z´[·nŹsç——‡þð‡ÈÍÍeEp;é¶ŠkR#X%H?e(s»µµÇÇäÉ“MÎU€ÄÄDýwrr2FÜn7:drÂÂ;އFkk+TÕ<Ñ…ÛíÆÞ½{±`Á<øàƒhhhÀ믿ŽcÇŽáÀ~ô#|òÉ'8|ø°GÓ4”——ã±ÇÃ}÷݇‹/bíÚµ!é¾ÿþû¸ûî»±téRØív¬^½š¦ñbÀ,ÒƒWÖºÇ_ƒ­Æ¨=Y\E„ÄŽ¯*A®W½®Àh½:þ<¤”ÈÎÎî‘|II 6oÞŒiÓ¦á‹/¾€Ãá@^^^XÙ-[¶`Û¶mðx<Ð4 v»=d –¦i˜3g233ãÆÃgŸ}†ý×…ÕjENN pâÄ Œ?L]ff&*++ñ§?ý ¦®ÊéÓ§cÔ¨Q€yóæáÙgŸÅ‘#Gt=„!|&õ~ºC×™íšo¼ëׯGCC8Ðå õ)S¦ ¤¤.— ›7oƤI“àp8L2ð’““‘‘‘ajMKNNÆåË—õãÆÆFìØ±gΜAkk«~ ÍÍÍÈÉÉñ;«Æ Óã$%%!++ N§“mÀ.BÒƒWÖø°•]„¼ÚèÌPævVV„=v<EÁĉ±cÇœ:u ÅÅÅeív;†Ž `ãÆhjj Ñg*!ž 8QxõÕW‘˜˜ˆÿøÇXºt)î»ï>€×ëe}§ƒEHàõ1>l•½TÃ.Br}24ºr{ º“’’PXXˆ}ûöÁív‡„·µµ…œ»å–[pòäIŒ;Ö4F«+ÒÓÓ1~üxlÙ²åšìu:hmmEEE†Žììl¸\®Ð’•ß~û­~ÜÚÚŠsçÎõ¸+”ÐÁ"„B®‰Ù³gCJ‰ÿùŸÿÁáÇqîÜ9455aÏž=X¹reˆ|NN~ýë_ãÞ{ïíU:eee8zô(¯ÚÖôôt¨ªŠ½{÷âÂ… øòË/±sçΰ²|ðêëëqæÌ¬]»ÉÉÉ;v, |À1X¤¯¬ñako»Íc°â©]'¾®v ëmtýÔ4 ™™™ø§ú'ìܹ›7o†Ëå‚ÝnÇ!Cp÷Ýw‡“””ÔëtrrrPXXˆíÛ·ã¸*[“““QUU…­[·bïÞ½:t(î¾ûn¬^½Ú\²B ¢¢›6mÂùóç‘——‡ûï¿?ä+F£ÿÖÕÕÕrŸäR9\*'Ö—ÊQý¶s©p©œ8Z*Ç÷›Kå\+ xå•Wð›ßü¦Ç]˜$¶`!!„2 ­l¦ƒEâ¼3>l½š.ÂØÌ¤øª±Ÿ¡Ñ×EHú¨Øÿ“3ƒEzæIp¢Ñ^¹[ñP%Èõª·œqm02räHüîw¿cF bØ‚EÈÕ¾}2 H<Ö{V|Bè`B!„ ÈËÈ@}EH!„2Xa !„BH3`ƒÜÿgx+@H Š”P¥¦oÍ ‹Ô`‘^ÿ9 Å¿7†YüòÆøÆ0]VÓ J¯oN}=Ž/Ì"5:›ÕßhO¯ªÉnb{`âéŸ$HƒÅ· ‹o¶$¡vÎ%ÔÎp¨~Ã&TÿÌNþ8B…פˢÏÂÔ•>¯ÿXêiäƒÒõ§Ðçºd ÚHÿ¼[¾é‘ hÒ—·½”P5ßÞ7¥’o¯è2¾8Ãa&½¾paØåÙ¹dƒL8F]¾ù¨$4ßPBƒ¦hÐéßkÐT ^Õ÷Û«HÈÀyá“ñª¼ª×¿×àU¼¾8ŠY—R—óÅóB i’ñúõiªWÿmŒHWúmÑtyC¸!=ö¾9¼:k¨ ßÌE¾}gó…+~yyÓ:ã¨0ëR„YW@.kÒeÖØ Ãù€>5DŸ?]Ù9ÍàûÒMJ©šðKó^ G ×:µàpC:æ°ÀoŸ-šÑ]“^hZšþë1Èê3 h2ëþMšgu“Ò/+;ãHý¾$ ¥_V*†ø›&õÙéBŽ;‹ ¸Á²AáR ²EÑmÊÏÏçS™°‹`­aAƒ¬]»¯¿þ:3‚ Ž,B!¤§Ð „€III2dŠ‹‹QRRÒïóI9rü1NŸ> ¯×‹œœLŸ>£Gfá:X¤ÿ‘´&v Š­X$Æ3f ªªª i\.Ž;†7âðáøÿþû¡(½ïÑ4­GÎÙÉ“'QXXˆŠŠ $&&bÿþýX½z5–,Y‚¼¼<‰V+N—´up]f”ÔcÁ:Лbé«R‹Ë»Øu¾hUU‘œœ HMMÅСC1lØ0¼òÊ+8pàJKK±{÷nìß¿.\@RRŠŠŠp×]wÁf³8€M›6aÞ¼yزe Î;‡Ÿÿüç!i:u ¯½ö¦NŠ©S§¢²²Ò>cÆ |ùå—øê«¯è`‘hv°â´¥aÎŽ}c°äu,=eÕ«­^òú”Z\ÞŢࢠ——‡#GŽ ´´BÜsÏ=ÈÈÈÀ… ðÞ{ïáý÷ßÇìÙ³õ8n·»víÂøCØívÝi P__7Þx3gÎDiiiøK—HJJ¢A"ÂAîdºÊì"dÖx ;;/^”••aäÈ‘ÈÈÈ@AAÊËËñÅ_˜ä5MÃìÙ³ñ½ï}YYY°Z­zØ‘#Gðúë¯cîܹ+صk:::0~üx‰»ò²ië ºLvFm¡²‹pPߺ¥”ú8ªãÇãÃ?„ÓéD{{;4Mƒ×ë…ÛíÖ)UU1dÈ=ß~û-Ž=Š… bìØ±Óûì³Ï°sçNÜÿý!­_„D™ƒÅ.ÂÁb+»ÿNì"ìóRcáÀát:‘‘‘‹/bõêÕ˜4if̘¤¤$|ýõ×X·n¼^¯î`Y,á{‡v»û÷ïǘ1c ªjˆÌ矎¿ýíoX¸p! èA.a!!„˜¤¾¾gΜÁ¸qãÐØØ)%î¾ûn 6 YYYhiié±.»ÝŽÅ‹ãüùóxóÍ7¡iZˆsµnÝ:ÌŸ?cÆŒaæ“Xp°âô{ñAØEëc°Ä`(PÑO%çÿ™ˆÃçX½‡y½^¸\.´´´à»ï¾ÃÎ;ñú믣¨¨7ß|34MÞ={páÂvìV¬XEQ˜˜ˆ¼¼<ÜsÏ=())äååáî»ïÆ®]»°uëVŒ1xçwz•NJJ /^ŒW^yo¿ý6~ô£áÓO?…”6lÀ† tÙ›o¾UUUô$H´:X„BHdªªªzäÈ”••¡¬¬Ìtnâĉúï’’Ý! Öo$55?þ¸~üðóH¯a!/»ÏleaŒ”»ûü²ØEH‰B+Ž»ië ºL9¸ËJÎBe!/šAì`¾ÐòõšYC!t°!„BH Ø ÷%__ç5œ€ yã<þ­=n‹_ús:ºÌ:ËUj|ï#QðNâõo†òýhYPr¤¯ïK"¶þ!ü!„B¬«ºº€oy‚‘#Gê[Ò•!ð68àmpÀÓà€Öf…¡onhè€nxÐ/4@{O IDATóB„  úæ…€ ¼Pü¿ÝðÂíßKhH€†H$Bƒ^hPüú|:=þ½šß 74E, –À’$+@*:7;€DI­Hêðí;€Tš`Øâ&k€µ°´–V@¹´·í—€Ž@s m€­ Hðˡհ]Ðà’os»€ŽK@» h¿EõšX‹ °)€ ¾-Á_1¬þ½€µ°´ÖÀÒ$$6›oŸ`3ǵj€Ú(í€Ú¨žN= œàÛì6 ÑbNOqèD;€v QñÉ%[»Õß©6/|Mfþ¦–ö6ßÖÑt´ûÒ³©þ½*oÔåõøâtH`ˆk16éãû÷n7ÐÞttøö6$ø7øšø›!m©í ÃëÛ{¼qå£ËKÃÞ¿yýñÛ=@‡Çw.AtêÐó-L\H_º´Û½¾òHðo6Â`³”@»æ—÷n¯Y—þxþ½Ušm÷j¾øíÐá?M€lcÝþxí~;¬è¬ƒ¶0Y|ì¯ZhóW+:u¨²)ðÛ㫞è@gë·±þŠÐ¬5é Ä ì-A[p<w6¯ßÞ@\%(=¯_Îm7êWüñ›1-øã¹ :C%H>Ü5»ÑÙSà5ÄW wƱòÒÿ\ „-XQGî’Ø¯~\œvÇÊõðŽKH”¶`õ=’ÿòd`«_ô¨‰¥K¦Ý1z=×;ݵk×âÀB@J_êBâÁì×´kjj°mÛ6Üu×]˜2e ïw$Þ,:W$ö«[°hw¬\OvÆŒƒªª*ÝÁ‹¥ÿcû÷ïÇí·ßŽýû÷ÓÁ"ñè`EnÁ¢ëEbåuž-XÑa·ˆ¡ë“q”®ªªHNNŽÞÜÜŒ 6àĉB`ôèј5kRRR§OŸÆ¦M›ÐØØ!²²²0gÎäççGÔÙÐÐǃ;ï¼À7ß|ƒï}ï{¼ç‘xr°!„Äí;Ž”X½z5ðÈ#@Ó4¼÷Þ{xë­·ôµß~ûm :sçÎ…§OŸ†ªª]êÝ¿?Š‹‹¡( Š‹‹ñé§ŸÒÁ"t°!„ Ž=ŠåË—›ÎÝqǸãŽ;P__³gÏâÉ'ŸDZZ`Þ¼yxá…ÐØØˆüü|477cêÔ©ÈÊÊàûн+ÚÛÛqøðaüã?þ#ßÂÑ/¿ü2fÍš›ÍÆ!t°!„Ä>˜3gŽi VR’oÒj§Ó‰ôôtݹ€œœ$&&¢©© ùùùøþ÷¿wß}ĨQ£0nܸ.¬Ï?ÿ‡C† äåå!==_|ñn¹å¡ƒE!$ö±Z­ÈÌ̼êøÓ§OGqq1êêêPWW‡;v`þüù;vlXùýû÷ãìÙ³øýﯟ“Rbÿþýt°,€‹¾BxÏìdgg£¹¹---z+ÖÙ³gÑÖÖ†œœ].++ YYY(++Ã[o½…ýû÷‡u°Îœ9ƒÆÆF<òÈ#HLLÔÏ·¶¶bÕªUp:ÈÎÎfÆ“xp°ø­ ‰ýêÇihw¬\Ï@|iéõzár¹LçEÝnGaa!rss±fÍTVVÂëõbÆ (((@~~>Ün7Þÿ}Œ7hiiAcc#Æ6­O?ý7Üp†–ŸŸO?ý3gÎä½ÄƒƒÅ‰FÉW¿èQK—L»côz"ÝcÇŽaÅŠ¦sYYYxüñÇ÷ß?6n܈—_~BŒ3³fÍÒ±+W®àwÞÁåË—a·ÛqÓM7aúôéa¹Ï?ÿ·ß~{X;Ƈݻw£¢¢ŠÂ%}É w°è\‘د~lÁ¢Ý±r=×»«ªª UUU]ʤ§§ã¾ûî ¦ª*æÏŸß£´TUůýëˆáS§NÅÔ©Syß#]2ˆ\oI׋Ä|3[°¢ÃnÁr´åO,B!„:X„B!„!„B,B!„èGTWWs¬"!„BHÂ,B!„>fÀæÁú÷ÿwÀ¹sçÐÐЀ'Nø·V44¨þÍ‚¶6Åï*P¢ó#a+€6ÿ^ ³Yü{€×°@"€$ÿÞfSÂÄ!¾(6@±ÂX½€­£s³º‹Ç°]¬W‹ °\ñ%gþ æ¸ íAº:´û·ÀÒؽ€ÝãÛ¼¾4¬Àâö¥mŒ¬S—õų s.ZýWØ'mVC˜-¨l`iÔv@í¬dü›Å¬Ë”žâD Ú}[¢$Û€d+`·úKB3l^s±´·ù¶Ž6 £HP›êß+¡òF]^/^@‡ô †¸U!¯Á p»öv £Ã··ø‹8Á¿ cõ•æ´¥´{€¯oïñvƵ)¾|Òå¥aA_H˜ìa\ÙM: %ЮùmõnÍ\ž¶pétz5_üv èðŸ³I¿ ¨ÁéÙÒá×î·ÃH׿…»$ã¹ÀR›¿*X ›æÒ{ï?P× ÿÑM–wU²‡2ÆptæõÛêöïeÐQtc‡;h†;£“v¸¼òøãj†øÆ7{cJJu5ŸÊ„!ç#ƒ·^sìDt±víZ8pBHé+! ñàƒöKšÏ=÷š››õ´RRR0zôhÌœ9III,B‹BHì3fÌTUUéX,ý÷B ¼¼¥¥¥RâܹsX·n6mÚ„yóæ±@,Òð-Ÿ°^“ë…ªªHNNŽÞÜÜŒ 6àĉB`ôèј5kRRR§OŸÆ¦M›ÐØØ!²²²0gÎäççGÔi³Ùôø©©©())Á¡C‡X„!×®EH»ã°ÊG“#*¥ÄêÕ«‘€Gyš¦á½÷ÞÃ[o½…‡~ðöÛocèС˜;w.„8}ú4TUíq---øê«¯0lØ0VB‹ðau}îîQ¥&–.9*íæ¬è,ÿ£Gbùòå¦swÜqî¸ãÔ××ãìÙ³xòÉ'‘––˜7o^xá466"??ÍÍ͘:u*²²²‡£Û4·lÙ‚mÛ¶AÓ4x< 6 3gÎd t°²„°^ 0gÎÓ¬À`s§Ó‰ôôtݹ€œœ$&&¢©© ùùùøþ÷¿wß}ĨQ£0nܸn¬)S¦ ¤¤€¯ rëÖ­xíµ×ðè£B¾b:X„ô/ì"¤ÝqX寷#jµZ‘™™yÕñ§OŸŽââbÔÕÕ¡®®;vìÀüùó1vìØˆqìv»î„9TVVâOúNœ8Q£F±"ˆp¢Q‡U_À.ÂAe7ëuì•vv6š››ÑÒÒ¢Ÿ;{ö,ÚÚÚ““£ŸËÊÊBYY~ò“Ÿ`ìØ±Ø¿ïîyþV+ÇÃJ@º„-X„Bb¯× —Ëen%PØív"77kÖ¬Aee%¼^/6lØ€‚‚äççÃívãý÷ßǸqã‘‘––466bܸq]¦ÙÑÑ—Ë)%š››±eË$''ã{ßû „ÐÁ"ñó;`°‹pPÙÍzݳ|¼ÞùtìØ1¬X±Ât.++ ?þ8àþûïÇÆñòË/C1cÆ`Ö¬Yº#våʼóÎ;¸|ù2ìv;nºé&LŸ>½Ë4·oߎíÛ·ðuÞpà øÉO~‰F ,‡l,yšì"Œ»ùaô•UUªªªº”IOOÇ}÷Ý6LUUÌŸ?¿Wi>ùä“,hrÕp !„B,Ïo±Q »•ݬ׃·ü ¡ƒEHzšì"¤ÝÌGBè`·XÂzM!t°!„B®Ã‹Zuu5[z !„Bú¶`B!„ô16VuuuøaØa/º8I¦¿tâÓé©LOlë‰íW“×¢ãZl¿†|Âʸ€b8VüçFN 3\tõÉõ(át„±AáJ8[Âè >îÎ6ǻۻÈ×nm¿Š|Eó@á_ÆD@*„S„ @@Ša¯úåŠ_‡?\¨P z(èü- yE¨¦paÐkУëìLÃd—Á¶PÛ{5Œíp£­JøcÀf§mÁ¶ Óõ ¨&Û»Ê×€Þ| ²ÝOà÷¿ÿ=ŸÊ„-X$Žàˆ_3éß ,7BØ‚EâÉ»8‹™ôk^CÜx*7—Ë…ššÔÕÕ¡¥¥‰‰‰p8(..FII ¬V+ž{î9477¬V+233QVV†ÒÒR]OCCV­Z!¤ì†,„À¯~õ+¤¤¤`ÇŽرcGˆLvv¶¾4ÏË/¿Œ“'Obþüù˜0a‚.³gÏìÙ³‡3ÁÓÁ"„¯È„Å< È¬n¹páV®\‰¤¤$TTT 77ªªâìÙ³¨­­EZZŠŠŠ „@yy9JKKáv»qøða¬[·iii=z´É™zâ‰'`³ÙL餤¤è¿sss±xñb“ƒ¥(ŠI‡ÕjŶmÛ0nÜ8S!t°_‘éDm1‹xù?‰ú¬/‰õë×CUU,]ºV«U?Ÿ™™‰¢¢"“¬ÍfÓ¥©S§b×®]8~ü¸ÉÁ|‹7'&&FLSQ$''wiׄ ðÕW_¡¶¶“&Mâý…tÖf!„hæÊ•+¨¯¯ÇäÉ“MÎU·Î§”8|ø0Z[[¡ªj¿Ø–€iÓ¦áƒ>€Ûífa¶`B‰jΟ?)%²²²LçŸyæx<ÀäÉ“QQQزe ¶mÛÇMÓ`·ÛMc°Î׳Ï>k:—‘‘Ÿýìgúñ™3g°|ùr“Ìĉ1gÎÓ¹Ûn» {öìÁîÝ»1mÚ4¡ƒE!$vY²d ¤”X³fîhÀ”)SPRR—Ë…Í›7cÒ¤Ip8¦¸B<ú裦1XÁ­\ÙÙÙX´h‘i VBBBèƒÔbÁwÞ‰7â¶ÛncÁ:X„B¢‡Ã!œN§é|ff&„tÚív88,X°ü㑟Ÿœœ“\FFF—c°TUÕÓ莉'â£>ÂÎ;‘‘‘ÁB#ƒEHÜ!i*‰­’°Ûí5jöíÛ×ëqNééé?~<¶lÙÒ¯6 !0cÆ |üñǸxñ"« ¡ƒEzrç`°˜I¿f §iè–Ù³gCÓ4¼ôÒK8tèšššàt:qðàA8Î.§H(++ÃÑ£GÑØØØé2J‰Ë—/Ãår™6¯×«Ëhšîr¹"¦sã7bذaøä“Oøÿ@ØEHzøòʧ/‹™ô_r¢Ñnq8X¶ljjj°uëV´´´Àb± ''S¦LérŠ„œœbûöíxà|Ž©øÃþ`r¸„øéOŠaÆššš°bÅ “ŒÅbÁSO=1­ŠŠ üùÏö/ÛDâú«ººz@Ú~¹!bg-Âk½>®EkF\3köl-°ëùõp-°ëùõl-BŸ-\‹hƒ]„¤g¯Èd½VÅN1‹xù?‘ÑþïÉÖBè`B!„ÐÁ"„B¡ƒE!„Bè`B!„\?ì+BB!„Á [°!„Bú˜›h4âɰ·úãXý›-Bü˜Õ'°®§‚öRñÿV 2Jy%(N¤ð cÒœÓ º´:ƒm ²U*½°-(O4Åo“È·.¯¿‹üè2ÃØé”W¦< J»Ç¶…ËWô0ß•ðåèß+Ò÷VØ[¤„E“°HÀ"}çU}8ïÛ‡;V qT)¡j>=ªÖ©G1ÈX4ãÞýþ è´K×å—׌zT) H‰ê{Åÿ/î·ö&“¬QNDïJŸ1}³\¨m>¨þMñöáσŒñØdt¤pÿ>’ŽÀ±.Ð$l[·áA¿Õ ;º²M º¶H¶[®Ò¶€NE@ø'O LÈÉy°[°!ä:ÁÙ—!lÁ"„Bú—Ë…ššÔÕÕ¡¥¥‰‰‰p8(..FII ¬V+ž{î9477¬V+233QVV†ÒÒR]OCCV­Z!¤ì†,„À¯~õ+¤¤¤`ÇŽرcGˆLvv6üqÀË/¿Œ“'Obþüù˜0a‚.³gÏìÙ³O>ùdÄk1êB -- cÇŽEyy9l6.^¼ˆçŸË–-C^^ž~œœœŒ_üâ°Ùlº®_|cÇŽÅôéÓ«V­B^^*++M6½ÿþû˜7ožÉVB‹‡p}DàÂ… X¹r%’’’PQQÜÜ\¨ªŠ³gÏ¢¶¶iii(**‚ååå(--…ÛíÆáDZnÝ:¤¥¥aôèÑ&gê‰'ž09*’’¢ÿÎÍÍÅâÅ‹M–qQi!¬V+¶mÛ†qãÆu¹àt8ú½^/¾ùæ¬]»sæÌÑõÓÑÑ>úHw¦zÂöíÛ±{÷n,Z´………¬Lt°Èà€Çžå‘dI±®“.X¿~=TUÅÒ¥KaµZõó™™™(**2ÉÚl6ÝQš:u*víÚ…ãÇ›,°ÛíHLLŒ˜¦¢(HNNîÒ® &૯¾Bmmm— Nw§üøñ¨¯¯ÇW_}¥;XFÇ.ÀäÉ“±{÷nLš4©[Û`Æ øüóÏñÐCé‹X“ëÇ`‘ëÐAb!¸!ëz´råÊÔ××còäÉ&çªÛ‘‡Fkk+TUíÛ0mÚ4|ðÁp»ÝפËb±ÀëõFþÅÅÅp8øàƒºÔ¥iÖ¬Yƒ#GŽà‘G¡s5°‹Õ. Û…Èùóç!¥DVV–éü3Ï<ÇÀײSQQزe ¶mÛÇMÓ`·ÛMc°Î׳Ï>k:—‘‘Ÿýìgúñ™3g°|ùr“Ìĉõ¦·ÝvöìكݻwcÚ´iWuøüóÏQPPÐ¥Ã3fÌÀêÕ«ñýï™™™aekkk!„À²eËÍJD‹ >øxìY±‹uô–%K–@J‰5kÖèŽL™2%%%p¹\ؼy3&Mš‡Ãa.-!ð裚Æ`·reggcÑ¢E¦®º„„„Щł;ï¼7nÄm·Ýf knnÆ /¼ ßqǸãŽ;Lœ¦iÐ4 7Þx#î¹çžn¯{ôèÑ>|8¶mÛ†ÿøÇaeFŒÓ§OcÛ¶m˜?~¯Ç‡:X„mƒ$hEì»,¬ëý…Ãá€N§Ót>ÐzÜmh·Ûáp8àp8°`ÁüñD~~>rrrLr]ŽÁRU5b Q0'NÄG}„;w"##C?ŸššŠeË–éÇIII!œ©©©½êƬ¨¨ÀÊ•+1eÊ”°á¹¹¹˜9s&þò—¿àÍ7ßÄ‚ èd]g˜Û„B¢»ÝŽQ£Faß¾}½甞žŽñãÇcË–-ýû2 f̘?þ/^ì|È*Šîì9“ƒpà222zä\¿*¼á†pÓM7uy]yyyx衇pòäI¼ùæ›Ð4•‰<ð>Vò(KJ²‰ŸÙ³gCÓ4¼ôÒK8tèšššàt:qðàA8Î.[gÊÊÊpôèQ466vÖ-)qùòe¸\.Ófd®iZH¸Ë劘Î7ÞˆaÆá“O>éŸÿ‡ ¯ ËËËqâÄ œ;w®K'kñâÅøúë¯ñÆot9ˆžô-ì"$×áÉO,¸ì"d]f–-[†ššlݺ---°X,ÈÉÉÁ”)Sºœ"!''………ؾ};xà_ÝøÃL΋?ýéOõ/îššš°bÅ “ŒÅbÁSO=1­ŠŠ üùÏ;‡U¯ëŽà㬬,ÜrË-øôÓO»Ô3dÈ,^¼ØÔ]Ø__UCyUWWÈ}•kr-B®Eȵ»[‹°7ú¸!¸!!Q» Û ˜GQ[Rì"$„ÐÁ"d<"ã9XRÌAB,BHÀ6!B,B!„â{A¨Aî„B!ƒ¶`B!„ô16VÄiø_O%¢ëáB?6þîR¡qÇGN+üqoÓë-¢—¶ å#–‰èã´zk{äk€ׯ KøÏ‹NÙàãÎúÞ‡›l Õ/üÇ¡›/Lñ+Â7B8YÅ/¯‹ÎKì”U ú áJPzÁ¿M¶úåEñ§ë» !¿œ!(Bªo×åTƒLèÞ,k<¯šÓ0§cHWuùí!é©¶›Ò3Øg8H×hbŠ#"Ø®†^—Áö»” |"èz"‡+›D §i lÁ"$Já hwõV°ÖÂ,BúÎ¥Mâ®ÞJ7N–ËåBMM êêêÐÒÒ‚ÄÄD8£¤¤V«Ï=÷š››øÎÌÌDYYJKKu= XµjrssñØc™Z¢Ÿ~úiTVV¢¤¤„“ÐÁ"¤OZ‰Åz'ÎÕ… °råJ$%%¡¢¢¹¹¹PUgÏžEmm-ÒÒÒPTT!ÊËËQZZ ·ÛÇcݺuHKKÃèÑ£Ct|8,X€7¢©©)üCPQP^^޽{÷âÒ¥KÌlB‹BH|`·Û1jÔ(ìÛ·n·»WqÓÓÓ1~üxlÙ²%¢Ìøñã‘““ƒ;v0³ ,BÂÁ/IÜÕÛ8™¦aöìÙÐ4 /½ô:„¦¦&8Nöüƒ>¨ÿ9r$~÷»ß…Èüä'?ae$}»É k $®ê­d­'„!„B,B!„B‹B!„!„BÈàBTWWs„$!„BHÂ,B!„>fÀæÁª®®öý°p¶Dªßõ ì[wa6 6«³ø÷áÂa6Ãù@Q‹X5ÀâTÍw¬~{|›ÕíÛT¯y³º;òÆpcÜàßFÙÀï`}átï»K;°W½€bØT7 xÅf¤³ Tø Åÿ[AçL?"èœb7Ç“þ‚•~ÙÎ}¨¼qß©ÛfÖ8gÖiÞ”óÁv…·½óº¥a Õ]Ww×.Ýðò¡yj»²1¼íáâ#$M%l>u›e®¥NÛ¬D(·ÐrŽT_BÃCëIx[Íõ]þdÐÿ… s^"üÔ 2±ñ¼°ø6Åbþ­XaT ø7Õ¨ †ßÖNYÅb–Sl:õ½ÕÇæÿKðý6¦­XüiøÓT­:‚å„PTÿõˆÎýïÿ{>• [°!±Š´D ]ƒˆƒ’!„ Ò,B!¤7¸\.ÔÔÔ ®®---HLL„Ãá@qq1JJJ`µZñÜsÏ¡¹¹`µZ‘™™‰²²2”––êz°jÕ*äææâ±Ç3-›óôÓO£²²%%%íÐ{`$$$ ++ Ó¦MÃØ±cõóÀÚµk!„0­{h±XðÔSOÖ®]‹@!’’’0dÈýzD7³ôíP©©©7nf̘‹Å¢Û±iÓ&üö·¿ «cÕªUÈËËCee¥é|wñ>ùälÞ¼¿ýíoõu ;::ðôÓOcøðáxøá‡uÙ'Nà•W^Á/~ñ dffâùçŸGYYÊÊÊL:wìØ/¿üË–-ÃóÏ?‹/†7ß|3ªªªè`B¢‰ë¹bcgZ2†®A`Éð\¸p+W®DRR***›› UUqöìYÔÖÖ"-- EEEB ¼¼¥¥¥p»Ý8|ø0Ö­[‡´´4Œ=:DçÁƒ»t¦"1oÞ<¢½½ü1Þxã ,[¶ ¹¹¹ºLbb"žxâ “ƒì43UUUÐ4 .— ÇŽÃÆqøðaÜÿý].bm´CÓ4œ>}k×®…ÍfÃwÞÙ¯å1räH¸Ýn466bذa€“'O"%%§N‚ÇãѼ††ddd 33³Çú—.] MÓß|ó Þxã <ñİÙlºóíÐÁê "z’aôÿ„¥†õë×CUU,]ºÔôpÍÌÌDQQ‘IÖf³!%%0uêTìÚµ ÇÆo$: IDATq°&OžŒíÛ·£¸¸ªªöÊž„„¤¤¤ %%åå娳gNœ8ar° 99¹K=ªªê2©©©:t(† †W^y0µ¼ue¤¥¥aìØ±øî»ïú½<²³³‘’’‚††ÝÁjhhÀرcqâÄ |ûí·9r¤~>ð»§ØívýwRR’~.111fê,Ç`EÑk¥ŒSÉ}Tû8«¿Ó‡{•+WP__É“'÷ªåBJ‰Ã‡£µµ5ÄB ¬¬ š¦aïÞ½Wm›¦i¨­­Õ¥¾   yyy8räH¯â9Nœ8qBwxú›‘#G¢¡¡A?8RÆón·§NBAAAÊk0Á,Bâò]„Ñè´ð*<çÏŸ‡”YYY¦óÏ<ó <_kTEE`Ë–-ضm<4MƒÝnÛdµZñƒü[·nÅ­·ÞŠ„„„Û´fÍ!àv»!¥Dff&Æo’ikkÃòåËMçFŒx [ýÙÙÙ8sæLíÐ4 EEE¸ýöÛ¯K¹`Ó¦MÐ4 n·§OŸÆÈ‘#áõzu§ó›o¾×ë iÁ ”‘¯×‹œœ:X|á˜dØICý?a©ô%K–@J‰5kÖèŽL™2%%%p¹\ؼy3&Mš‡ÃVGii)vïÞ?ü3fÌ0…­_¿Ÿ}ö™~üoÿöoúïÊÊJàÂ… øûßÿŽY³fé]Y°lÙ2SËLO[औúx­žØ!¥Äùóç±iÓ&¼ýöÛ˜?~ŸåóÉ“'ñÚk¯éÇsçÎEqq±iVkk+²²²`·Û1räH¼ûî»ðx¹Þúúzœ9sãÆë•§€©E¯+²²²ÂŠollÔ»d­V«)ýÀ—|€¯›°¡¡!d ûˆ#PWW‡S§Nõz€;,¾ðH2ì¤!׎¼ŽÿòšSíÛkˆí}›n¼ÜfÏž MÓðÒK/áСChjj‚ÓéÄÁƒát:»œÎ ¬¬ GEcccg^ ¨®¨¨Àþýû{íÀÓøä“OpéÒ%Óy—˲ñz½p¹\hiiÁwß}‡;wâõ×_GQQn¾ùænÓmkkƒËåÂ¥K—ÐÐЀ>øYYYÈÎÎÖeS8·¦¦&À¤I“pîÜ9lܸgΜÓéÄG}„/¾ø"b«™‘‘#Gâ믿ƙ3gB¬ÚÚZhšÖ£îÝþoÄàxvFÑÝ–]„$ºßØEx=J†]„áq8X¶ljjj°uëV´´´Àb± ''S¦LÁ¤I“"ÆÍÉÉAaa!¶oß®0žª  ¨¯¯ï¾œÂL:fÌdffbçΘ={6 ½½+V¬09 BüêW¿Ò»ÙŽ;†+V@Q$&&"//÷ÜsOææBàÝwßÕSRR0räH”——›N·Ûÿþïÿ6ÅÍÌÌÄÏþsdffâ‘GÁÖ­[ñ—¿ü^¯ÙÙÙX¸p! »µ¡  ÙÙÙ¦îÑ#F ££CŸÎášÿ7DìÕrQ]]= Ïl®Eȵ¹á@­Eˆ=Ö"T"äÙµ®EØU¹]ûZ„ÝÛj®ï}µaðK×"$$vaáÀ¿,÷*v’k’ea4– ï „ÐÁŠ¿çQ?$Ã.B2èÿK…:X„ØC HZœÉ=zÓ%„ÐÁ"„B‰þWÙäN!„2Xa !„BH3`ó`ýæ©'tN&ýC5CŽõ=º”ï.ÙšÂ#Û®…± †t5i– ”pð¾o’±Ð´AW$[Œi¨¾} ŸÊ„-X¤´‘°ŽÓVB[°!„ëˆËåBMM êêêÐÒÒ‚ÄÄD8£¤¤V«•™Dè`‘ $m$¬ã´•DâÂ… X¹r%’’’PQQÜÜ\¨ªŠ³gÏ¢¶¶iii(** ‰çõz¡ªüœ„ÐÁº&„¡ËåD»Í!ãÌbÀh>€ú2K˜Ÿ$þj}¯Y¿~=TUÅÒ¥KM-U™™™&Ǫºº³gÏÆ±cÇP__©S§búôé8sæ Þÿ}|ýõ×°Z­(,,Dee%ìv»Ï§–~ø!jkkár¹iÓ¦aܸq€††¬Zµ =ô¶lÙ‚¦¦&äååáÞ{ïEvv6àôéÓØ´i!„@VVæÌ™ƒüü|V:X1þ‚,cï)¥;W±ð ØHg o<â%?cÉ‘¤ÓU÷Ê•+¨¯¯GEEEº?øàTTT ²²Š¢ ­­ ùË_pë­·bÖ¬Yp»Ýxÿý÷ñæ›obñâÅ€šš|þùç˜;w.Nž<‰·ß~ÉÉÉ1b„®{Û¶m¸ûî»a·Û±~ýz¬[·>ú(àí·ßÆÐ¡C1wî\!púôi¶žÑÁ"qt¯¢#@XX×cŠóçÏCJ‰¬¬,Óùgžy0yòdTTT€>&+ÀÎ;1tèP”——ëçþ?{o\ÅqîýælÚw„ (–b'fX„Ç“…’ëØ7õÚåT%)ê}oݨòþÊoroªœrÕ­\ç&רåk¼` ÉX– !#l@ˆ##„,K-GZÏ93óûCÒèl‰UËó¡†>ÓýtOOwkæ;Ý3Ý[·nå…^ ¥¥…ˆˆŠ‹‹Ù¹s'Ó§Oú{Æjkk9yò¤!°EaÆ ÆþC=Ä믿ŽËåÂb±ÐÞÞη¿ým#ŸÑÑÑRy"°&Ȳ ÊÓýx,)OaòµúÛ“O>‰®ë¼óÎ;†Ð|†ä©®®æùçŸ÷¹gØívTUÅétòꫯz„«ªJBB‚‡_\\œñ;44€®®."""X±byyy”——“’’Bjjªˆ,XäY†ïÎõTÄÀííð!BÉëäkõ£"::EQhnnöðŠŠð6ôÞw8Ì;—7úÜ'¸rå ?úÑ ó¼IZÔ,üŒ’ ú9N ¨©©¡­­ÚÚZêë뉕¦2I‘!Â{~—!ÂI¯dˆPò*Šù†DGGóôÓOS\\ÌáÇéèèÀb±ËÊ•+Y²dɰÚaaaüüç?çàÁƒ¼öÚk¸\."##™={¶a¿~ýzBBB8vìv»ÀÀ@XµjÕuâýL&ÝÝÝäææÒÕÕEpp0óçÏgíÚµÒT&ë#HNNÎ=¹ŒÈZ„¾ë¢ñµñZMÖ"”µG³á`^ÇÃZ„^á²!²¡ Œ"¼çÊ2D8Q:gîMdéð˜ôyŸ­^D`«ë÷x"”åäÕRž‚(fA%Lâ‡Ay`&Kû‘¶.‚,AA„±Å=ûŠðÿß¿Ké eà€¬B6©*ÞÓaR#=X‚ ‚ ·™{Öƒõ»ßýð}1Ýß'ÓþÂýíßJÜÛ™—Ѹ£s+ǺUwøÏËoçfu³ö×ÛFçF6#‰s+Ç.­•Ïhó>ÚrÉqFÒnFs®#=Ÿ›i¯£i7£­owMÓFþͶ«ÛÕ~o´¯iÚ¨Ëõzu““#weAz°AFÃxœFEaÜõ` ‚ Âhèì줸¸˜ÊÊJ::: $::š´´4ÒÓÓG½F¡ ˆÀaÜ3ç©Æv»¿ÿýï‘™™ÉÔ©S1›Í\½z•S§NÎܹs}⩪ŠÙ,Ÿ›"°n EQ<.âÞûã!ÏÂÄh{‚ Ü^8€Ùlæ¿ø…GOUTT”‡°ÊÉÉ!;;›ªª*.]ºÄ·¿ýmÖ®]Ë•+W8xð µµµX­Vf͚ŦM›¦¼¼œÂÂB~ýë_{ˆ±½{÷ȶmÛhll¤°°úúzE!&&†-[¶˜˜(•#øeBÏä>nxrSž˜mOð/BáfèîîæÒ¥K,]ºtDÀŸ|ò óçÏç—¿ü% .¤··—W_}•ÄÄDžzê)vìØAWWo¿ý6©©©èºÎW_}e¤ÑÕÕEee% .àwÞ!""‚§žzЧžzЇzHzÆ„ë"C„‚ ˆÆ4­­­èºNLLŒ‡ÿ¿ýÛ¿ár¹Xºt)™™™Æ;Yƒ=z”„„Ö¯_oømݺ•^x––bbbHKK£¬¬ŒÔÔTÊËˉŒŒ$99€ŽŽzè!#ÑÑÑR1ÂäX2D(H= ÂäáÉ'ŸD×uÞyçCh>ÃvTWWóüóÏûüÝÚívbbbÈÈÈà¿þ뿸víaaa”——{ˆ´+V——Gyy9)))¤¦¦ŠÈ&À’!BAêQD¨0ñˆŽŽFQš››=ü£¢¢|† ½÷sçÎeãÆ>m0,, €„„âââ ÕÔÔä!°Ö®]KZZ•••TVVRTTÄã?μy󤂄‰/°A„‰Gpp0)))|öÙg,[¶lÔÓ1$$$pþüy"""0™†õ8##ƒÒÒR:::HII!<<Ü#<&&†˜˜–/_ξ}û(++% Ë„zÉÝû%ÚñðR­¼ø;1Ûžà‹ô^ ·Bvv6š¦ñ׿þ•/¾ø‚¦¦&š››)//§¹¹ùºÂiéÒ¥ôôô°oß>¾ùæZ[[©ªªbÿþýí2--ŽŽNŸ>m¼Üàt:)(( ¦¦†¶¶6jkk©¯¯'66V*F"”›Ž õx×D¨”“p³DGGóôÓOS\\ÌáÇéèèÀb±ËÊ•+Y²dɰ;aaaüüç?çàÁƒ¼öÚk¸\."##™={¶‡}`` ©©©TVVzôL™L&º»»ÉÍÍ¥««‹àà`æÏŸÏÚµk¥b„É!°A„‰Khh(›7ofóæÍÃÚ ®sëO =ñÄ7ø Ç f³™Ç\*@2D8Æò,LŒ¶'ø"½WÂX¦§§‡óçÏsùòe£7Ln"”›Ž õ(“ž—^z‰ÞÞ^6nÜè3ß– Lz%ÂØEÞÁÆ2Ï=÷œ‚p[1I‚ ‚ Üæ‡Êœœy¤AA¸H– ‚ ‚,AA„±Í={Éýÿæ_ëÿ¡h ¨`êßLе“­ÅÚ‡bq ˜('˜(VG¿Ÿµ¬‹ æÍâ‹Ú¿Y]è,:º±fÐM º3šÙŒf¶ ›M覡 ³ÅdE1Û ×d 06E±¡(VÅ ŠE ôÚl€ +`CW¡MÓ­è lº š€ªÛÐuè6Ь [Q4 fÝ„Y7cÖL˜uVÕ„US°j&,š‚Y‹f ¬šŽÕ¥aS5¬ª†Í¥bS]X].l.Í…YuaÑÔ~×åÀêêÃêìwͪ“êp]˜=˜½Æ¦¨Õ‰âr ¨pô‚shÓ½h޾~×é@S]h.'ºêBu:q:¸\N'.— —¦áÒ4TUÅaÖpXû·>«FŸÍi§YÁiQpšú¬z­fú¬ú,f³“Ù<°Y°ZlX-X 7‹%°3`2Y1 Ô±ÉdsŠ9Ì` @7ÙÐMVt“ ÍdÃe Äe D5àRl¨Š—bEU,8+“ ‡béX±)V‚0¬XÂB b†B&PÕB Ó!]'@]'DW ÕTBt•ÍE°æ$Xs¢; ÔœØ4Wÿ¦»0©\ZªÚ‡KíEU¨šUsöojªÚƒæêEU{Au¸mNÌ®>Ì®^c³9û°¹nšË…¦ºPUªËE—ÓE·sÈíUuú\½ªNªÑ¡Â5Mçš ×€k6…kV…«B—U¡Çb¢×l¢ÇbÂa±à²YpY­¸¬VÌ 6‹…sÿhµh $Ð@ 5«5«5ËÀoÅlC±Øú]³Ýˆf B³¢YQMVT³—Ù†Ël¥×@¯9€K½f½&+}f }& ªÉB€Å‚Íl%Àl!Äl!Òd"Òl&Òd"Üd"DP“Bˆ&ˆRt¢MmÒ Q4‚@E#nÅE;.Ú'm¸èPtààýnŸî¤½8~÷Ò«÷Ò§÷ѧ÷¢éT݉¦;Ðth½ ÷€Ö‹¢õaÖœ˜5f݉Uu¨õ ö¨õ¬:Sûu9U„¨.‚U•`M%XU UUB]ý[˜K¥OÕ¹¦A§Ú_o­.hqB³Z\Ðâ4Óâ4 ¸fºT3ª™.—™ÕL¸ÓF„ÃJ¸ÓF˜ÓJ ËJj%Ðe!Ðe#Ð@ 3@g 6— “fBÑ̘43 &L¿Œ—»² =XÂ8F¦lš…'Õ8KL‘F1VÈÉÉ¡¢¢âžæ¡­­œœ¥BD` ‚ Âíeÿþý¼ñÆ>þ555äääÐÛÛ;q dcXÂ8B¾…'Õ8KL—Fq7ˆªªc¿IÈÜqã–16Ѩ(uAZT„¨ärôtwwSPPÀåË—éíí%**ŠU«V‘––fØìÙ³‡©S§b2™8{ö,qqqìܹ“––òòò¨¯¯'**ŠM›6ù¤ßÑÑÁG}ÄÅ‹q¹\ÄÆÆ’Í´iÓhmmåÃ?¤®®§ÓÉ”)SÈÌÌ$%%ňÿç?ÿ™E‹ÑÚÚʹsç bõêÕ,Z´È°©««ãÀ4773uêTV­Z呇_|‘Å‹³råJï¡¡—^z‰gŸ}–èèhù3%_r¯­`½ƒ¥KÿëªÜb¹+“«ÚÝ{w\.‰‰‰¬Zµ ›ÍFee%¹¹¹DGG3mÚ4î¼¼œÅ‹óóŸÿÜHãÍ7ß$44”'Ÿ|’ÞÞ^ =zÆ/¿ü2áááüð‡?$44”ÆÆFãø‡ƒû￟ÌÌLÌf3åååìÝ»—ÿõ¿þF:Çgݺu¬ZµŠ/¿ü’œœLLL ‡ƒ½{÷2kÖ,¾÷½ïa·Ûùàƒ<ò±páBΜ9ã!°Îœ9Crr²ˆ+X‚()<©F)±;–Å Ü(.\¸ÀóÏ?ïá§išñ;<<ÜCx,]º”ªª*Î;ç!°¢££Ù¸q£±_UUEKK ?ùÉO `Æ ¼öÚk†ÍÙ³géîîæ©§ž"00€¨¨(#<>>žøø¡/ ×­[ÇùóçùꫯXºt©á?gÎc!é‡zˆãÇS]]MLL gÏžE×u¶nÝŠÅb!66–ŽŽÞÿ}#~zz:GŽá›o¾aÚ´i¨ªÊ?þñ²²²äÏ[Ö8ïZ¤HE¼@Çj.gΜɖ-[âââ°X,Ãæ#,,Œ9sæPVVÆ´iÓøê«¯PU•ÔÔTùó%Ïärï­@†¥áßÁ•!Âa±Z­½FÐÿ^Ô %%%œ8q‚Í›73uêT¬V+………>/²{ ®Ý(-׿U~øá‡TWWóðÃÅbá­·Þò9¶Éäù]ÙÍ,€ž‘‘Ann.›6mâÌ™3<ðÀ7uNÂG¾"AÆ=_ý5óæÍ#--¸¸8¢¢¢hii¹a¼Á¡¸ÎÎNï®®Îãݧ¸¸8éééöØéééÌ›7©S§B[[Û¨òË•+Wp¹\ùðfΜ9Øl6>ÿüsªªªX¸p¡T¾¬ñÕ;0¡ã=ŠÞØÈ‰" ÿ®èxk¼V{LL /^ä믿¦©©‰÷Þ{ÏC4 GJJ ÑÑÑäææÒØØÈåË—ùøã=lÒÒÒ á7Þ ¶¶»ÝΗ_~i ˜˜Ο?Occ#¼ûî™JKKCQòóóijjâÂ… |úé§¾7m“‰ pèÐ!bbb|†XãóÎ?Q! Qx2D8ÞêN¿óYT&oµ¯^½š„„^{í5^yå˜?þ‹LQؾ};.—±êPÝ IDAT‹¿ýío¼÷Þ{lذÁÃÆl6ó“Ÿü„^ýuþò—¿PRRbôreeeÈßÿþwöîÝËìÙ³IHHUþm6?øÁ¸zõ*/½ôGŽñxߌŒ TU•Þ«1Ž|E(‚ Œi}ôQ¿þÉÉÉüîw¿3ö·oß~Ýt~úÓŸúõ‰‰a×®]~îéDDDðýïßoüÈÈHvîÜéá7øµà Ï=÷œO¼§Ÿ~Úcúôé>~Þù€þwÏÌf3 ,Æ!küõLhdˆpBž Ž·º“!BáÖp¹\tuuQTTÄ·¾õ-BBB¤PD`M;¿ ­@*B˜à*Õ>vùâ‹/ÈËË#!!Ç{L D–0æGÔ QxR°ÄÆÀ4 ÂØ%==ôôt)ˆq‚LÓ ‚ ‚p»Ÿ—rrr¤GXAá6"=X‚ ‚ ·™{öÖýö 4\'.¦&B\Ø‚t4]CÓu4]G×ut½ÅsÜüÜü\mà÷ ß}ð‰«é:š¦Çpw5]G׆OÓ_Þ4?yó®ù9ÑäÝo:¸§§£¹ÿÖFž7ŸpMQ9^¯Üóv£¼kÞ6ÞyÇ3oÞy÷ΣæV7n7ÎÛHÛÀÊUwk{ LL¨è:Š : `RtEp}÷\?67 ýáqÓ€«ø³LKÁoøÎ۠ˀ½É4ò¼¹ëzÇqÞ•Qæϸ£)WïðæÝï¹ú–ãuónº¹r LðpŸÿ8GîÊ‚ô` ÂØFÞö•:A%‚ ‚ KÆ6òí†Ô‰0QØ¿?o¼ñ†MM 999ôööJ! "°î$‹üºý/Œ÷Æ'E ÜʵKÚÞd¹î “‡ 5ÑèhW/ÏóòTç~·+SKu2®®]· ²tYÝÝÝpùòez{{‰ŠŠbÕªU¤¥¥ÐÖÖÆŸÿügEñ¸W$''k^¾|™Ã‡S__OHHóæÍcÆ Øl6)`ar ,AÅ#u"L&a:Ô¦\.‰‰‰¬Zµ ›ÍFee%¹¹¹DGG3mÚ4"""øõ¯mØwvvòꫯ’œœ @kk+ÿó?ÿÆ xôÑGéêꢠ €>ø€ï~÷»RØÂäXÞO&ããy^žê'Fã“jnåÚu‹mopážþy?MÓŒßááá¬\¹ÒØ_ºt)UUUœ;wŽiÓ¦¡( ¡¡¡†Û»w/III¬]»€cÇŽñàƒ²lÙ2¢££Ù´i{öì!;;‹Eú#„I,°dˆP¸gŠGªQTè-]»dˆðFÌœ9“-[¶x\çëêêÈÍÍ5ÄVqq1çÎãÚµk¨ªŠªªX­VŸ´öïßÓéä{ßûžáרØÈÕ«W9{ö¬Ï=¥­­)S¦HC&¯ÀA&&V«•¨¨(¿ŽŽãwII 'Nœ`óæÍL:«ÕJaa!ªªzÄùä“O¸téO>ù¤Ç»U‡ƒE‹±|ùrŸ‡õˆˆ©ar ,"<ëïze c©NÆÕµëÛžÀ×_ͼyóŒ—Úu]§¥¥…ØØXÃæË/¿äèÑ£üøÇ?ök 455ùø ÂÍ2¡¦i!BážÝu¤E ÜÒµKÚÞ­ÃÅ‹ùúë¯ijjâ½÷Þ£³³Ó¿rå ¹¹¹<ôÐCÄÆÆÒÙÙIgg'===<ôÐC|ýõ×ÐØØHKK Há 7…  ‚ ãžÕ«Wc·Ûyíµ×°Z­,Z´ˆùóç“644àr¹8zô(G5â͘1ƒŸþô§ÄÅűk×.>ÌË/¿Œ®ëDGGó­o}K W%C„Â={¬—j{u2®®]·Øö&8>ú¨_ÿääd~÷»ßûÛ·o6ôôtÒÓÓ¯{œÄÄDvìØ! R¸-Èá=¿ÝÈMG´ƒ0é›  ‚,A'}RR'‚ "°AA& ÷ì¬ Q¿Ú¹2°•I…Üüv&(€YJç.VH¯ÎØCÔÛ•»+¤Bz°AAD` ‚ ‚ ŒmîÙáÿÎù½—ÎS\ `5\E±fpuÅ Š(VtÅÜ޹ÿ·bS¸n²ØXú]Ó`Zý[¿ÿà6hg2lŒôlúÃÍnûæ~{Å ˜ö‡lôÁ0Åäî~üÁø&tÅ„ª€¦€ê¶¹Ü~kô‡k€ªè¶ÚÀ~ØÐþP¸›K¸>°¯ì†GwsUÓ@únqtÃíÓp·´‡±w׆,u›ŽGQºãPqö» (fÝÃÆ{ÃÃÕ‡ÂÌni 4?Eñ_ño×ïêýq”!;EñŒ§xÇW†>Ë7•þ¼õÁ€«èôOä1´ùîãázÛ é>°¯¤ï®ø¤‡G:ži ù ¦…Žg¸·¿âßnè·â¹¯( ýÃ×U†ö1ü‡Âð Ã+®ÿðÁ¼*>ézœ«wüëÏ=ûáÃü” W8Ç`Çò›¾âÿÜ|â+ÃÇõMÛk_.ïðüÿûrW{öì!>>žM›68NNNÛ·ogÞ¼yR€]` ÂdG‘ׯn\FRÒnØ¿?½½½>s]ÕÔÔ°gÏvïÞM``à;~WWGŽ¡²²’ÎÎN‚‚‚ˆgÍš5$%%(3gÎPXXÈîÝ»oÉî‰'žÀl¾½oÌÞ­r¼UΟ?ÏÉ“'©¯¯§§§‡§Ÿ~šøøxX‚ ‚pûEßW}o¾ù&š¦±mÛ6¢¢¢èì줺ºšîîî§q»æj ºíç§ëú])Ç[Åáppß}÷ñ­o}‹÷Þ{oLçU– Ü#t]z±nXFH/–´ÛÑQTTDEEO?ý´áWZZJii)Ï=÷œáwêÔ)Ž?N[[‘‘‘,[¶Œ%K–øM³··—ÚÚZvíÚÅŒ3ˆˆˆ`Ú´ivǧ¬¬ »ÝNPPsçÎeãÆØl6jjjÈËËCQrrrP…5kÖ°víÚQŸ£÷áµk×ÈÏϧººš°°06lØÀ¡C‡X¾|9Ë—/7âuwwóÆopñâEÂÂÂÈÊÊbîܹ´µµñÊ+¯ ( øÃP… ð裲gÏâââ°X,œ>}³ÙÌâÅ‹=òÝÛÛˇ~ÈW_}…ªª$&&’••eô,566RXXH}}=Š¢Ö-[HLL¤­­‚‚jkkQU•¨¨(6nÜÈœ9süžû‚ hkkó“‹O¬¥r/¢ïXÁ ¸ùkSäOìNäqŒfòfn²gÏž¥¨¨ˆììlâããihhà½÷ÞÃf³7pwl66›ŠŠ ¦M›†Åb¦Œyä"##±Ûí¼ÿþûš¦ñ³Ÿý «ÕJSSÓm+X·ùiWn„qsSUä¯íÎñ×`ÇCŽ{®îe»½páÏ?ÿ¼‡Ÿ¦i7ÕË•••e¼ðISS'Ožô+°L&Û¶m#??ŸÏ?ÿœ„„’““yàˆ‹‹3ìÜ{‹"##Y¿~= ;;³Ùl¼ÛrÛʤ©©‰K—.ñÔSO‘ÀÖ­[yñÅ}lÓÓÓyàذa'Nœà›o¾aöìÙÆ°cpp°Ï;Xqqq¬Y³€èèh>ûì3.]ºDJJ —/_¦¾¾žßüæ7†hzøá‡©¨¨àË/¿dÑ¢E´··óío›˜˜#A:::HMMeêÔ©DEEM˜k¼  Â=¾a ¢í¥ÝŽŒ™3g²eË^«ºº:rssGœ†Ãá µµ•¼¼<òóó=„Úõ^îž?>sæÌ¡¶¶–ºº:*++)))aëÖ­Æ"Ò/^䨱c477Ó×ׇ¦i¨ªŠÓéô躴´´`6› q5(`ü½§å.m6~{º® 44ÔˆwåÊúúúøãÿèaãr¹°Ûí¬X±‚¼¼<ÊËËIII!55ÕYË–-ãÀTUUaÞÇ5z^¬1Ô LøF!C„“)ÇcáaÀjµúôptttxåÓ7£ªªz,èïåñ~‡ÊdºþÔ‹…””RRRX½z5ùùù‘žžŽÝngïÞ½,Y²„ 6Dmm-ùùù¨ªzÇÖhð>?EQF4Äz½x‡ƒ°°0víÚå“Ö `]»v-iiiTVVRYYIQQ?þ8óæÍ###ƒÙ³gsáÂC fee±téRXcíiWn„qsS•!Â;$ÊÊÊŒ!Úððp/^ÌâÅ‹9tè§N‘Àë_=Ê¡ ‚0¾ŸÝDArr2;vŒÔÔTªªª¨ªª" À°Y·n|ðÌž=—ËE}}=½½½¬X±Â'ýîînÞ~ûm.\H\\6›úúz>ýôSC$DGG£i¥¥¥Ì;—ÚÚZN:å‘Ndd$‡ƒK—.Õj¶gKÓ4a8(‚bcc}ÖÌ™3ÉÏÏgË–-˜L&>úè#¬Vë¨HDD_}õsæÌÁjµŽèeóY³f‘””Äo¼ÁƉ‰‰¡££ƒÊÊJæÏŸOll,$55•ÈÈH:::¨¯¯'55€ÂÂBfÏžMLL ===ÔÔÔøœ£;===´··ÓÑÑ®ë477Â944TÖ|Ú•^¬1Ó Œ°G@þÚnwÏŽ Þ‘<*c·Ýº ‰ØØX²³³)..æèÑ£¤¦¦²råJ±“‘‘Õj¥¤¤„ƒbµZ‰‹‹óxIÝ›ÍÆôéÓ)--¥µµMÓgÑ¢E¬Zµ €øøx²²²())áðáÃ̘1ƒÌÌL÷Ã’’’X¼x1ûöí£§§çºÓ48N^zé%¿¨¨(ž}öYÛÇ{Œ¼¼<^~ùeBCCÙ°aW¯^õøÚñFb+<<œuëÖqèÐ!òòòŒiFÂ~ô#>L^^]]]„††2cÆ BCC1™Ltww“››KWWÁÁÁÌŸ?ß8oMÓ((( ££ƒ€€æÌ™ãÑ èÍW_}ÅþýûûWXPÞy瀛žò⎶˜œœ{¢Id©Y*g²/•ãý[–Êqÿ­xúÉR9~Ý{±TÎЊ7²TÎX¥½½^x;w2sæL)éÁAa´TWWãp8˜:u*×®]ãàÁƒDEE“¢ "°n½;"¼c+Üvdˆpe„ Ž™<*¡ÝNLTUåðáÃØívHJJâñÇ¿áW‘‚¬Q]Œ…1s—&|£;™r,Âjì2{ölfÏž-1Æy+÷¸G@¸NIH»X‚ ‚ ÜïAA&*Òƒ%‚ ‚p›¹g/¹ÿËÿù?ÀÐ ¼>î€÷´ýƾ—ýõmosøˆí¹nøÀ´D^çýý¡´úãêé Wnç‡O¸îÏß-=]ÇØ|ÂÀ#ÜØw ÇÇßÓ*¯sõ,*¿û®ÛœLºâGò×½ÂÝãºÛá'Šoz†«xººâßßHÏØÏ~Èú§y„ëhvžþÚ0áš›;®y„{lÊÐo ×*ºâ¦¨CᆫzØãaßo(š1;Þà\\ š1¿—IÑ1 þf`>2cþ/0¡c¦ßƤèni Å1lÜÂM® óÀ±f»3ŽmrÛ7„ÆS²1ᛢcñÚ7¡ùQt“¦£hýnÑèÅ¢ƒKµSÔpUï·ôwõÛ)ꀽû¾[Ÿp—{:ôï;6—ŽâÔúm¿Uò3d;äjàÂ3ONƒáM€ÿû‹¹+€={öϦM›F'''‡íÛ·3Ï X` ÞÈ$“²Ê¥}ˆؿ?½½½lß¾Ýÿ¦¦†={ö°{÷ncqá;AWWGŽ¡²²’ÎÎN‚‚‚ˆgÍš5$%%(3gÎPXXÈîÝ»oÉî‰'ž0Ö¼]Ü­r¼TUåã?¦²²»ÝN`` )))dff&KAnë³Ê]ø´ñÍ7ßDÓ4¶mÛFTTTWWÓÝÝ=â4n×BÏAAA·ýüt]ó‹';NY»v-qqqôôôðÁ°wï^~ñ‹_ˆÀ†mÞR“±Êi‚p;)**¢¢¢‚§Ÿ~Úð+--¥´´”çž{Îð;uêǧ­­ÈÈH–-[Æ’%Kü¦ÙÛÛKmm-»ví2fGˆˆ`Ú´ivǧ¬¬ »ÝNPPsçÎeãÆØl6jjjÈËËCQrrrPå¦×Ïó"¼víùùùTWWƆ 8tèË—/÷X_±»»›7Þxƒ‹/FVVsçÎ¥­­W^yEQøÃþ€¢(ÆZ„{öì!..‹ÅÂéÓ§1›Í,^¼Ø#ß½½½|øá‡|õÕW¨ªJbb"YYYÄÇÇÐØØHaa!õõõ(ŠBLL [¶l!11‘¶¶6 ¨­­EUU¢¢¢Ø¸q#sæÌñ9ïÀÀ@vìØáá÷È#ð·¿ýöövcÁjXwè)Æý AAñyŸgìezÞ;ÇržeŽa¢7CEþ´¼{^FËÙ³g)**";;›øøxxï½÷°Ùl,X°ÀÇÞf³a³Ù¨¨¨`Ú´i‹({߃yä"##±Ûí¼ÿþûš¦ñ³Ÿý «ÕJSSӨʥ··×_ÒƒuÿÐôñ \ôq¨´dï{pÇ’w°ÆÕµèN6C}òþi]¸pçŸÞÃOÓ´›êåÊÊÊ2^øŽŒŒ¤©©‰“'OúX&“‰mÛ¶‘ŸŸÏçŸNBBÉÉÉ<ðÀÄÅÅvî½E‘‘‘¬_¿žÙl6D@HHÈm+“¦¦&.]ºÄSO=EBB[·nåÅ_ô±MOOç`Æ œ8q‚o¾ù†Ù³gÃŽÁÁÁ>b%..Ž5kÖÍgŸ}Æ¥K—HIIáòåËÔ××ó›ßüÆM?ü0|ùå—,Z´ˆööv¾ýíoc¤1HGG©©©L:€¨¨¨Ÿ»ËåâСC¤¥¥ KªE¸÷w,i‚0sæL¶lÙâñ0]WWGnnîˆÓp8´¶¶’——G~~¾‡P»^/Èüùó™3gµµµÔÕÕQYYIII [·n%==€‹/rìØ1š››éëëCÓ4TUÅétzôÝNZZZ0›Í†¸0þÞÓrƒ6›€€¿=]׋jÄ»rå }}}üñô?v»€+V——Gyy9)))¤¦¦"kÙ²e8p€ªª*#ÌûxþPU•·Þz EQÈÎΓíU†ÇB/† Þ£^!A‡ÍpZ­VŸŽŽŽŸû€¿›±»Àìåñ~‡êF‹#[,RRRHIIaõêÕäççSTTDzz:v»½{÷²dÉ6lØ@PPµµµäç磪êX£Áûü¼ï™7ÏápÆ®]»|Ò¬k×®%--ÊÊJ*++)**âñÇgÞ¼yddd0{öl.\¸`Ô¬¬,–.]z]qõöÛoÓÑÑÁÎ;ÇdïÕ„X2Dx·ó<Þ{…ÆÓ]X†ÇÕµ"¼WÓÙÙéáרØèÑû†Ýn'--í–Ž5eÊ***hhh@×u²²²Œð/¾øÂÃÞl6ß¶/ Ýó i F/VKK ===£Jgpxo´ùKHH ³³EQ®;¼CLL Ë—/gß¾}”••C´ááá,^¼˜Å‹sèÐ!N:5¬ÀWv»;wÞ‘/*o› •K¡ ‚0Q®“““éêêâØ±c´¶¶òÙgŸQUUåa¿nÝ:Š‹‹9qâ---\¹r…²²2Ž?î7ýîîn^yåΞ=Ë•+W°Ûíœ;wŽO?ýÔ ÑÑÑhšFii)v»òòrN:å‘Ndd$‡ƒK—.ÑÝÝÓéöœ4M£±±Ñckjjò+°fΜI~~>ß|ó 8p«Õ:ªi¿Àûꫯèêê2zúnĬY³HJJ2¾Nlkk£¶¶–ÇS__Ó餠 €šš#¬¾¾žØØX ©ªªÂn·S__OMMæO\½õÖ[444ðØc¡itvvzôRŽdˆp,ôbèc½[hu¦Œ§!BykÂ"C„wÿÚ?Hll,ÙÙÙsôèQRSSY¹r¥‡ØÉÈÈÀjµRRRÂÁƒ±Z­ÄÅÅy¼¤îŽÍfcúôé”––ÒÚÚŠ¦i„‡‡³hÑ"V­Z@||û¬íc=F^^/¿ü2¡¡¡lذ«W¯z|íx#±κuë8tèyyyÆ4 #áG?ú‡&//®®.BCC™1c¡¡¡˜L&º»»ÉÍÍ¥««‹àà`æÏŸoœ·¦iÐÑÑA@@sæÌñètçÚµk\¸p€ÿüÏÿ4ĵ¢(ìܹ“äää±Õ.ïÕbϲT^Kåô{ÈR9“h©·4n¼TŽ>°¼Œ,•#KåÈR9Âõiooç…^`çÎÌœ9S Dz°AA-ÕÕÕ8¦Nʵk×8xð QQQƤ¨‚¬[!»œç1š·ñ‚ NXdˆP¸›¨ªÊáDZÛí””Äã?~ï"X#¿_ë¯Ç™0˜¢EÆa3”¯/fÏžÍìÙ³¥ Æ"o'Ç3¯0î«\Ú‡ ‚,AA„Éü }¯¾"AA˜¨H– ‚ ‚,AA„±Í=ûŠð_s~?ðKÐyæ¡M±‚béÏžášÜ0Å Ú™ûà [ëІÛow[üÙ[úób„äÅ㸖¡ãºÛ ¦§xçÙ ŠÉOø`úÓ*fT@U\ŠÒïª.¥ßÕðtU¥?÷pÍ+\õJc0ŽæÏÞÍu×ÝÓós,Í-Žªøß×èŸLÓˆç~ï 9Í^Ådq+n‹[qšªËâVýƒÅ:fòlU;TüCöf/“W¸É³Zsö÷NÃã8Š›;nÅÜ?á§¢ ºý“o*n›ï~ÿ¤–“pzÚ …MÔ9®û Šç½¯û‰£»Ùpp_¿!{Œ B‡\&ýT~+˜P0£ Lð©¸mýûŠûÁÍ=Lˆ£Œ(|Ðßó··ýp¿ñðó¶ñc70c­20­r½ok¯£xÎ`ë>£­a3Œ½·¿Oœ}Eñõós‹£xù+^iøÌ,®ðû矗»òmäôéÓ:tˆßþö·R“I`äË-aâ5iÕc¼rä­X¿ìß¿ŸÞÞ^¶oßîá_SSÞ={ؽ{7wìø]]]9r„ÊÊJ:;; ">>ž5kÖ””4¢4Μ9Caa!»wïÖæÓO?åèÑ£üú׿öXêú—ÏùÓŸþÄúõëY¶lÙ˜©›/¿ü’S§NQ__OOO¿üå/™:uª,ù³AƵ.UîücÛo¾‰¦ilÛ¶¨¨(:;;©®®¦»»{ÄixÏÕè pøðaΟ?OZZšGعsçPU•|pL•¿Ãá`ÆŒ¤¦¦ràÀi"°Fôç E L¸&#FJû™ÈQQQÁÓO?mø•––RZZÊsÏ=gø:uŠãÇÓÖÖFdd$Ë–-cÉ’%~Óìíí¥¶¶–]»vËÏDDD0mÚ4»ãÇSVV†Ýn'((ˆ¹sç²qãFl6555äåå¡( 999(Šâw±çî¿ÿ~ÊÊÊ|Ö™3g˜7oAAA”””P^^noÞ¼ydffb³ÙüžÇ»ï¾‹Ëåâûßÿ¾áWPP@ss3?ùÉO X\\ÌéÓ§éììdÊ”)¬Y³†ùóç[æééé´¶¶ŽHDŠÀÆ×Ü=N\nØ‚ ÜBL[¾iMz7ô³gÏRTTDvv6ñññ444ðÞ{ïa³ÙX°`½ÍfÃf³QQQÁ´iÓ|†îŒ"Ryä"##±Ûí¼ÿþû¬ÊÈÈàõ×_§½½ˆˆC¼\¾|™;vvf³™ììl"""°Ûí8pEQؼyó“ì±ò IDATM—ç'Ÿ|—_~ÉÖ­[‰ŽŽ¦ººšwÞy‡;wŽx(T5~¯(wûÁw]òÌ2~šŒˆá{ø‡2lL½_dåw°Æ@×ç… xÞëExMÓFNQQYYYÌ›7€ÈÈHššš8yò¤_e2™Ø¶mùùù|þùç$$$œœÌ<@\\œa·|ùrãwdd$ëׯçÀdggc6›wÄBBB®›¿Y³fFYY™ÑÃuæÌ"""HIIöxëÖ­£°°ð¦–Ó餤¤„]»v‘˜˜ÀÂ… ¹|ù2'Ož%kÜÈAšÌd½O rɹ)fΜɖ-[½là ‹ "°Æïe,<øÊá„j2"†ïáʰ1eˆðvLgg§‡_cc£ñ;44”°°0ìv»ÏKä£eÊ”)TTTÐÐЀ®ëdeeá_|ñ…‡½Ùlñ;cÑÑÑ̘1ƒÓ§O’’b¼P__¢(<üðÆßÙ³goX6uuu>eÀÔ©S1›Í´··ßôpàÝø¢S– ‚ Ü íç&Z’““)((àØ±c¤¦¦RUUEUU•!"Ö­[Ç|@@@³gÏÆårQ__Ooo/+V¬ðI¿»»›·ß~›… ‡Íf£¾¾žO?ýÔx+::MÓ(--eîܹÔÖÖrêÔ)t"##q8\ºt‰øøx¬Vëu{¶222ÈÏÏGQ}ôQ¦ª*Ÿ}ösæÌáò刎™3gRZZÊÙ³g™6mååå477=y¬X±‚>øUUIJJ¢¯¯ÚÚZ‚‚‚†¢§§‡öövÚÛÛÑuææf4M#44”ÐÐPXÂøî—‘!Bi2¬#br†õ yzw>kn½&±±±dggS\\ÌÑ£GIMMeåÊ•b'##«ÕJII ÄjµçñÒ¸;6›éÓ§SZZJkk+š¦΢E‹XµjñññdeeQRRÂáÇ™1c™™™ï‡%%%±xñböíÛGOOßiÜ™?>˜L&CÈ ’˜˜ÈÆ)..æàÁƒ$''“™™Éþýû‡MïþûïgÕªU|ôÑG¨ªJFF>ø ---†Mff&¡¡¡c·Û $11Ñ8Oœ?Þ‚Š¢ðöÛoBvõêÕ“÷Š““sOþ¤ÇÇR9/Y*G–ʹÁR9f/›1¸TÎ,•3æ–ÊXæF–Ê„ñ,ö,‚ ‚ ën"C„#M\†œÆO“‘ÆÞ_¡  ‚,a2Þ4õ{||A˜à…º(sA%O¤‚0¾›Œ´j©AD` ‚ ‚ Œ¿ç¥{õ¡ ‚ ÂDEz°AAn3÷l¢Ñý—éÿá½l€®mîáº{8þÃ=~ûÛÇ+-nîñ[ñÝ÷°W¼üý¸Þá¿uÃÜãüÇ€Ÿæn3}˜pM÷,>wWs·aÈu÷¶ñs³qß×Ýöuo;·¸º÷¦ø“÷Ô?†kò ÷°uŸgk`þ)#ŽÉ—ßp“[¸»ihÃäß#=³›ÍÀh3䇑Ÿ¡| nýsp }`ž+:f·}³×þ Û?•šî±y§e¾A–Ït𿱽ià÷ ;Ïâ–†{œA;³®cÒÀ¤éÆÖ_¤n® pêàÒû÷Õ?$u Ü9`ã°qßWÝìTÜÂÝmü„;ÝŽë¾¹Ûª:8ÝlÓôHË+¼¿I10¿ÿ§ßÉ]ù6rúôi:Äoû[)ŒÉ$°áN2–—t“:„ѳÿ~z{{Ù¾}»‡MM {öìa÷îÝÞ±ãwuuqäÈ*++éìì$((ˆøøxÖ¬Y3âuûΜ9Caa!»wïÖæÓO?åèÑ£üú׿öY`Ùétò§?ý‰õë׳lÙ²1Q/.—‹?þ˜ªª*cæ÷Y³f3‹ÀA„ñ*ÞïÂÃo¾ù&š¦±mÛ6¢¢¢èì줺ºšîîî§1’…ž,XÀáÇ9þ¼ÏbÔçÎCUÕa×¼8®^½ÊÚµk‰‹‹£§§‡‚‚Þxã þéŸþI– L4¤§DêD˜œQQQÁÓO?mø•––RZZÊsÏ=gø:uŠãÇÓÖÖFdd$Ë–-cÉ’%~Óìíí¥¶¶–]»v1cÆ """ŒE’9~ü8eeeØív‚‚‚˜;w.7nÄf³QSSC^^Š¢““ƒ¢(~×" áþû溺¬ÌG`9s†yóæ@II åååÆñæÍ›Gff&6›Íïy¼û\.¾ÿýï~477ó“ŸüÄÅÅÅœ>}šÎÎN¦L™Âš5k˜?¾ß4ƒƒƒùñìá·yófþû¿ÿ›k×®&k‚<Æx½Ó5%¼×ﺵ,ŽÇaeÜ&.˜îú诀gÏž¥¨¨ˆììlâããihhà½÷ÞÃf³±`Á{›Í†Íf£¢¢‚iÓ¦ù Ý Ý‚yä"##±Ûí¼ÿþû¬ÊÈÈàõ×_§½½ˆˆZ[[¹|ù2;vì0ìÌf3ÙÙÙDDD`·Û9pàŠ¢°yóæ›.ÏO>ù„/¿ü’­[·Muu5ï¼ó;wîñPhoo/Š¢ÜÑ![Xwÿ/m<3ën²H¹å,ŽÇ^ý.¯¼ï36…µÔ‰p+\¸p罄Ö4mÔé‘••ży󈌌¤©©‰“'OúX&“‰mÛ¶‘ŸŸÏçŸNBBÉÉÉ<ðÀÄÅÅvË—/7~GFF²~ýz8@vv6f³Ù!!!×Í߬Y³ £¬¬Ìèá:sæ ¤¤¤ {¼uëÖQXXxÓËétRRR®]»HLL`áÂ…\¾|™“'OŽH`9N>̃>ˆÕj%î©VŠ@êD˜pÌœ9“-[¶xôZÕÕÕ‘››;â4­­­äå呟Ÿï!Ô®×ã2þ|æÌ™Cmm-uuuTVVRRRÂÖ­[IOOàâÅ‹;vŒææfúúúÐ4 UUq:£&“‰ pæÌÖ®]‹®ë”——“‘‘áaWUUEII‰ÏñTUÅl6º|[ZZp:ìÙ³ÇÃ_UUŸáP¨ªÊ[o½…Ùlæ‘G™ôíU†ïÉs¼›+C„ã(qAî%V«•¨¨(¿ŽŽ¯Û€â÷Æï.°¶nÝê#L¦ëO i±XHII!%%…Õ«W“ŸŸOQQéééØívöîÝË’%KذaAAAÔÖÖ’ŸŸªª£îÍY¸p!ÇŽ£ººMÓèèè0„ôîÝ»—eË–‘™™I`` 5558p`X5Ò²Ù±c‡O/Ûpâîé¼ùæ›tvv²sçÎa‡?E`ÛGd"œô=2D8¦…µÔ‰p§ ¦³³Óï±±ÑøJXXv»Ýç%òÑ2eÊ***hhh@×u²²²Œð/¾øÂÃÞl6ø±èèhf̘ÁéÓ§HII1ÞǨ¯¯GQ~øaÃïìÙ³7,›ºº:Ÿ² `êÔ©˜ÍfÚÛÛGü¾•»¸êèè`çΓþÝ+C°K‚ ãûÙzH´$''ÓÕÕűcÇhmmå³Ï>£ªªÊÃ~ݺusâÄ ZZZ¸rå eee?~ÜoúÝÝݼòÊ+œ={–+W®`·Û9wîŸ~ú©ñWtt4š¦QZZŠÝn§¼¼œS§Ny¤‰ÃáàÒ¥Ktwwãt:¯{^œ?žŠŠ ŸáÁèèhTUå³Ï>Ãn·sæÌCŒ ÇÌ™3©««ãìÙ³´´´ðñÇÓÜÜl„²bÅ >øàÊËËimm¥¡¡'N +ÞTUå7ÞàêÕ«<öØc¨ªJgg'½c“"¼'Ïñn® Þ‘Ä¥§d Þ¥„;véºªÄÆÆ’Mqq1G%55••+WzˆŒŒ ¬V+%%%zÇòPSSÃ'Ÿ|Bcc#.—‹°°0î»ï>¾óï`6›9sæ ………ìÞ½[*L˜`k ‚ óTQˆˆˆà‹/¾`Ó¦MX,ý·/—ËÅ?þñ"##ïèñ›ššxíµ×X¾|9›7oÆjµÒÒÒÂùóçŦu]’…q&°&G“•?LA„ë‘€Ýnçüù󤥥pþüy"##}VUUGåêÕ«(ŠBRR›6m"::Ú°éèèà£>ââÅ‹¸\.c¡èiÓ¦ùûâÅ‹„……‘™™iøEEE1{öl ¿w+//EQÈÉÉAQÖ¬Yƒ¢(œ;wŽ_þò—éýå/aÞ¼y¬[·Î÷n ë;vŒS§NÑÙÙÉ”)SX½z5©©©ÒD`™gž1,\”1^n“à4ïj™)ëôÆéˆ¶Lv:þ¯¶ .¤¬¬ÌXeee¤§§SSSãaçp8X±bñññôõõqäÈÞ|óMþùŸÿÙùå— ç‡?ü!¡¡¡466Û ʵk׸|ù23fÌð pEEE<óÌ3躎Íf£··—O>ù„úúzhhhàêÕ«üà?ð{¬ââbþñðï|‡èèh._¾Ì»ï¾KHHˆßc "°îÁEq,ßt¯3Kw,ýöÝŠ&MÇœ>± »Iè³–„±_¶iii:tˆöövt]ç믿æñÇ÷XÞ½=[·nåßÿýß¹zõ*S§NåìÙ³twwóÔSOô÷H Gjj*/^dÏž=„„„0}útRRRX°`˜Íf##žÍfcÖ¬Y”••«¬¬Œääd¿Ãš.—‹ââbvîÜÉôéÓ|ÕÖÖròäIX"°ä¢(g)‚pû áþû溺¬ €9sæìc×ÒÒ‘#Gøæ›oèîîF×uE¡½½©S§råÊ Qt#L&ßýîwY¿~=ÕÕÕÔÕÕQ\\̱cÇøÅ/~Ahhè°q322ÈÏÏ'++ EQŒ÷ÈüÑÚÚŠÓéäÕW_õðWU•„„i"°Æ 2DxÏó&C„£ dˆPCWÛôôt P…ììl¿6¯¿þ:QQQlݺ•°°0t]ç?þã?PUµÿæg¹¹Û_XX>ø >ø ëׯçÅ_ääÉ“¬]»vØ8sçÎÅl6SQQÉdBÓ´aß§r8üèG?",,Ìó†m‘ïÒD`™‹¢ Þž¼ÝbR“"œµ$Œ²={6ªªb2™˜5k–Oxww7---|÷»ßå¾ûîàòÿÏÞ»GWU䉿ŸÚûœ“7ÉÉ“„‡H›(y-@Ñ”IÇÑ^ÚÓ­ŽØj;Ó¯~s{­{ûv~½ÖeÍL¯åØ3WW/WÏ]L˲ii¦%Šˆ¢´±%@ˆˆäeHÈãdï]÷s²sÎÉ‹@Br’ï‡UÔÙ»¾UõÝUµ÷þîªJU}}„LNNŸ|ò ]]]$$$\‘ñññ¤¤¤¸F‘išƒÎá2 ƒÅ‹óÉ'Ÿ`š& .ÒXÊÊÊÂãñÐÖÖ&Ãb` ‚ µÃ0 ~ðƒ?MÕÀÁ„„ùøãINN櫯¾â­·ÞŠ-..¦²²’íÛ·³nÝ:RRRøâ‹/˜1c†;÷)œ?ÿùÏœ?ž¢¢"ü~?–eñé§ŸÒØØÈ=÷Ü@ZZ@€Ï?ÿœ™3gâõzñz½@p˜ð¹çžàñÇòÚâââXµj{öìÁqæÎKOO§OŸ&>>žÅ‹Kk2|ÅÒ¡žÄºMޤ&72D8jI¸º²½–M"..nh]”âàõ×_çùçŸ'33“7²uëVWÆ4MyäÞxã ^zé%Ç!++Ë5–¢™5k ìÞ½›ööv|>ÙÙÙ<ôÐCnOÓœ9sX¾|9øÃèêêbÍš5îÐaFFsæÌ¡««kÐe ¹óÎ;IJJâàÁƒ´¶¶Onn.%%%ÒÐbéž(//ŸÇäh¶Ê‰Ümaªn•ÃàûÈV9ÈV9²UŽl•ƒl•3ø÷ÿwV¬XÁ׿þu±>¤KA„«áÒ¥K=z”ŽŽn¾ùf)1°&2D8ѺMޤ&72D8jI¸º²•?"œ_þò—$%%QZZzÙËBb`MYÓìÚ覦¾nòW„S£ ä¯)Û+¦¼¼\ abÈW§\¥ ‚ ÓÐÀAAˆ%&lˆÐ[þ )ýk¤ÆxCN¦*Íu ?Ÿ iª‚0‘·  ‚ ‚0†LXÖùÿõôÅS*€i0Œ† à±»0í.L« ՅǾ„Çêw¦Ó…ÇéÂãtb:Ý(, m¡èÅÀ “ Õ‹RÁÕ°”rP†2í~ßãKÌ«ûþAR€Â±LtÀƒð {=hËD[Ú6Ѷ݇ˆs}ÇòâØ´íű‡M¶Š{pðâ(/¶ŠÇR‰Ø*K%b©l•€­â±U–J¢W%c©”ob£°” ù]ت ‹N,Õ…E;½tÐK;—pèÁ!€MG÷ÐË%,ÝÅ%,} KwaëN,݉­»qtMŽ„\/¶Ó‹£{q´ÇZxÎ1QމÒ!ßQÁ./Ýç÷9,;ÇF;6ŽcZ@J}¥ íÅÔÞ`[Ñ^”öaè8”öaê8<:S'âq]’ðèd<:>L|˜:/Éxt ^’1ˆÃÀD¹NæWôðM|H#‡iäC’gvrÇw¸nþüùüâ2²!H– Äò~AÄÀAAK&7²@ Ä4;w¼œƒFœ¯©©ÕS[·neÏž=R ‚XWŒ ‚ Äü“\)¼^/ï½÷ÝÝÝ®5¶mK¥#"+¹ Óàé,E ±Î¼yóhii¡²²’»ï¾{@xgg'¯½öõõõtwwã÷û)))¡¸¸€]»vqêÔ)êëë9tèJ)~üãsòäIÞxã ~úÓŸºiÕÔÔ°}ûv·wìwÞ¡¦¦†+VpàÀÚÚÚøùÏŽÖšƒòñÇÓÑÑAff&«W¯æÆo” d³ga v¶ Äþw’R¬[·Ž?üá¬\¹’3fD„[–E^^%%%ø|>jkkÙ¹s'éééÌš5‹ 6ÐÜÜLvv6wÜqIIICö€EŸoii¡ººš‡zÈ «¬¬ä³Ï>ãoþæoHOO§¾¾ž?þñ$%%qÝu×I¥‰%]‚ Âä§°°™3gòÎ;ïPZZ6cÆ V­Zå¯X±‚'NPUUŬY³ˆÇ4M¼^/ÉÉÉ£ÎÛ¶mî»ï>]ƒ®²²’G}”Ù³gà÷û9}ú4þóŸÅÀdˆP˜Ÿ¾R‚0U¸ûî»ù¯ÿú¯c Àq*++©ªª¢½½Û¶±m¯wl–²OKKs+öhõööòÛßþv€!–››+%È¡ ‚;\wÝu,X°€}ûöqóÍ7»çß{ï=>ÌÆÉÎÎÆëõ²gÏž'¤6D8XœhC-nêôï|‡”””È«G^­‚  ÓéÈ„)źuëøõ¯MFF†{®¡¡ÂÂBwR»Öšææf²²²\Ó4Ñ:ò˜˜HOO½½½®uþüùuÈÊÊÂãñÐÖÖ&ÃÂt4°äÍ*ˆ-Sœœ-ZÄáÇÝsüõ¯¥¡¡øøx>øà:::" ¬´´4Μ9ÃW_}…Ïç#11‘Ù³gãõzÙ·o+W®äÌ™39rdDâââXµj{öìÁqæÎKOO§OŸ&>>žÅ‹KE‰%‚ ±ÅwÜÁÑ£GÝãÕ«WÓÚÚʶmÛðz½,[¶Œ¢¢¢ˆu³V­ZÅ®]»xî¹ç°,‹ÿøÇ¤¥¥ñ·û·¼ùæ›üÏÿüóæÍãŽ;îàÕW_Q‡;3¤¤$ûì3.]ºDjj*%%%,Y²€/¿ü’7ß|“Ó§Oãõz™?>6lp{ÀNœ8Á¸páJ)æÌ™Ã† HOO૯¾âÙgŸåÁäðáÜ={–ôôt6mÚÄœ9s¤¢™ä>A¨)7î )S¾ÍJDWWuuuÜrË-®qÕGrr2‹-r÷%ܹs'Gåž{îá?ø¥¥¥ø|>º»»ùíoK^^O=õ?ü0—.]bÇŽnz@€[o½•§žzŠG}¥¿ÿýïèôöÛosÛm·ñýŒ ^yåÇ‘ÊÄÀšðõ)÷Y<òÉ zª×ðØ[p2KhnnFkMffæ á™™™twwsöìYªªª(++£°°¿ßÏõ×_ÏM7ÝÀ‡~Hnn.wÞy'Ìœ9“ÒÒRNžú˲"ÂÚÛÛùì³ÏX¸p!999h­9uêÔ éäææráÂRSSIOOp^¯—ÎÎNš››Y½z5ùùùdffÒÙÙ9Ȳ´ A ,Aa pÏ=÷`Û6/¾ø"õõõ´µµQ[[Ë‹/¾Hjj*wÞy'iii,^¼˜ÿþïÿ¦¦¦†ÖÖVN:EUU+V¬ ««‹?üáœ={–––Nœ8Á®]»ÐZ“@bb"ü1---|þùçìÝ»w€A%=UÂå C„õU< ‡å‘4Õkø*‘9XÂ0dddðä“O²ÿ~vìØAWWÉÉɱfÍw ¬M›6ñÖ[oQQQAWW—»L‡üqÞ|óM¶mÛ†eY¤¥¥±`Á׈zàxýõ×yþùçÉÌÌdãÆlݺ5êù­y¦K¯– Ö¤@ë©fd]Þ¡¼¬b»†ÇµÉÊ¡0©©©”•• ÿRóxX¿~=ëׯ4<==|pÈøóæÍãÿñ#Îýüç?w§¥¥EÄÇÇ8'2D(‚ ‚ ÖÔ@†…©WÃW‰žœmFÚ­ b`ÅÓñ¯©aA1°aŒ‘¹,ÂhˆÌÁA ,AA!øqV^^.=ÿ‚ ‚ cˆô` ‚ ‚ Œ1¶ÖÏÿ}+âöu¢éÐìoÝ? \‡…¹ÇaNGÇ%*!d¤.­ ƒ¦§‡ÒmÐü‡ &L¦û0áúrt)Þþhdè^´ƒ•ë`á——îÓm€cˆóQŽ!äœËÑ£” ׫O÷P>:¼tßuáÆÑy„…-O”âå—_æûßÿ>ÙÙÙRÀ‚XâT¤ ›ß ÕöF7öÚi,©<¬®òŒˆ™6PQQÇãáá‡Æã ¾ºRSS™9s&¿úÕ¯xûí·¹÷Þ{yöÙgY²d ---ÔÔÔPTTäî_Grr2ÉÉÉÜyç:tˆ“'OFX•••:t˲¸ñÆIJJ’Ê®ˆ©5+ú'α7"„Ëk{£Šƒ—;Ut•gDL´®®.êêê¸å–[\ãªääd-ZÄÑ£GÝs|ð3gÎäûßÿ>kÖ¬žã8|üñÇnïÀÑ£Gy÷Ýw¹ë®»xòÉ'IIIá£>’Ê®"®!‚p…477£µ&33sÐðÌÌLº»»¹téùùùÜzë­ä^yå”Rôöö¢µÆï÷sÓM7¹á‡féÒ¥,Y²€;3Ï?ÿ˲¤„in`É¡0YÚÞ¨â"C„¥«<#¦dÈËËôü† ÈÏϧµµ•7Þxƒ7FÌÛjlldùòåqfϞͩS§¤Â…Q#C„ÂèŒáòÚÞ¨âÆàåN]åm ==¥ƒ†766ïΗòz½ƒÊ%%%‘žžÎüùóùæ7¿ÉŽ;Ü^/AKA˜V$&&2oÞ<>úè£Ãuííí|öÙg,\¸pTiΚ5‹ÜÜ\8àžËÊÊâìÙ³rgΜ‘ ÄÀÐÃ".cüÉ*Ÿû—ÝöF7/wªè*ψ˜i÷Üs¶móâ‹/R__O[[µµµ¼øâ‹¤¦¦²nݺQ§ùõ¯?þ˜öövV®\É'Ÿ|Â'Ÿ|Bss3û÷ï²×LFbjÍÁ’!Âñ7"ÄÈ{ãS†'NWiÎ1Ó222xòÉ'Ù¿?;vì ««‹äädŠŠŠX³f ñññ#<¾š„øý~8À½÷ÞËÂ… immeß¾}X–EQQ·Ür 'Nœ FÿÊ,//ŸGŒìEƒ{öù²¡ìE({"{2hÞ²¡ ‘!BáÚôÒL‡Þ½+Žƒ—;Ut•gÄtj¶‚ Ö˜b±`|ÊáÄé*ψéÔlA ,a²~²Ê7« ‚ ˆ%‚ ‚0LØ_þïù¥ô…qB1mfˆ¨(_b¸ ÂTBz°AAƘ ëÁúùÿúG€–:`„eˆœ\<Ôò—µLÃ0y X¦!x|ÙË4\ÎÒ —»LÃHr0ŠôÂÓu–Ì ×Î@]ÂÃÄq–£ý±öPyF¤ÿeè>hšCåÁÐqkÑúD·½º]î:L^Gé½ü„ŽJS°\…ŽÒ}ðð!â ?´.DÜ×áËDã@y­ûÒ:|°%"ž#ƒÈ­V [y"¸RGر¹2Eœ(¹ðÕ24¹l<゚€žžvïÞMMM ÜvÛmTWW3sæL6lØ€eY¼õÖ[=z”îînrrr¸ë®»¸þúëøê«¯xíµ×8}ú4¶mã÷û¹ûî»)((‹X‚ ‚píèêꢮ®ŽuëÖ¹ÆUÉÉÉ,Z´ˆ£Grï½÷ðÁ°fÍÖ®]ëÊÅÅÅqß}÷‘œœÌ… øÓŸþD\\·Ýv›+ÓÒÒ±cÇøÎw¾CWW/¿ü2äÎ;ïà7Þ ¡¡¿û»¿#))‰ýû÷óÅ_0sæL7×^{¦¦&¾õ­o‘’’Buu5Û¶mãþáHOO§¢¢Çqؼy3^¯—ÆÆF×€¦²Lƒ ‚0©innFkMffæ á™™™twwséÒ%òóó¹õÖ[ñûýøý~V¯^ÍìÙ³IKKãk_û«V­¢ªªj@Zeeedee1wî\/^ÌçŸ{¯>ýôSÖ¯_O~~>ÙÙÙ|ó›ßÄq7îW_}Å‘#GøÖ·¾Åܹsñûý¬ZµŠ¹sçòÉ'ŸpñâEæÎKvv6~¿Ÿ¯}ík\wÝuRÉSéÁA¦yyyÎ=z”ÇÓÚÚJ Àqâââ"dÒÒÒ"z“’““]£­µµÇq"ÒŽ0ú.\¸€ã8üÇüGDº¶m»“ðW®\ÉîÝ»9qâóæÍãÆo$''G*M ,AA¸¶¤§§£”¢±±‘ÂÂÂáÄÇÇ“””€×ëohhàü#wÜqóçÏ'>>žÏ>ûŒ>ø B®o>VJ©°õG&`O=õ*j‹>ÃméÒ¥,X°€ãÇSWWÇÁƒY¿~=+V¬ŠžbÈ¡05‘ îR'”!11‘yóæñÑGaYVDX{{;Ÿ}ö .2~CCiii”””——Gzz:_}õÕ¨tðûý†Á¹sçÜsÝÝÝ477»Ç¹¹¹h­¹téééé.99Ù•›1cË—/çÁäÖ[oåã?–Jk’# µ¹ÕD×iÿ¨¹Ö ÞsÏ=ض͋/¾H}}=mmmÔÖÖòâ‹/’ššÊºu놌›‘‘A[[G¥¥¥…C‡QSS3ªüãââX¼x1{÷îåäÉ“îDy¥”Û[•‘‘Aqq1;wºšÖÖVΜ9Cee%µµµìÙ³‡'NÐÚÚʹsç8uêYYYòP™‚È2 ‚ ŒþV]§ý£æZ/Ó‘‘Á“O>ÉþýûÙ±c]]]$''STTÄš5kˆ2î 7ÜÀ׿þu^{í5lÛ¦  €5kÖðÎ;ïŒJ‡ 6°{÷n~÷»ß¹ØÖÖñ—eee8p€½{÷rñâE™={67ÜpŽãðÚk¯qñâEâââ(((`ýúõòP™Š!åååòl—½ûŽ#öøÙ‹Pö"”½Çv/B´FÇÈ^„A]×½ Ÿê{(‹i¸a à™gžaýúõ,Y²D, Az°„éðé õ?ézld¡QéÁŠq¾øâ ššš˜5kÝÝݼûî»n™ LmKAÆ‘÷ߟææfLÓ$//Í›7Ë>ˆ‚X‚ ‚p¥äææòÔSOIA—…,Ó ‚ ‚ – ‚ Âäf†ÿ÷/Ÿ“Òû^Š`,QÈÚoBL6[A7œ ‚ ‚0"ÖƒõNùGý_0J¡®†k(e„üÐ ¹ªï7 Ã00Txx¿\ß9×7ú eQaéãÃ"ÓrJÇPaÎ@™ýáJ)Pô‡…ˆd‚áÑéDÊ È#êj8á¾R(E„îá¾aDɆén šVx¸‘áù„—•›Ç}òáñ ƒ½ ×ß»ž݈¸ÎÝ£åäÉ º„÷þDœóûd §Uk¥Tð³e€Qòáù†Öeêóƒ+L¹ëXÿ9ÁµÂÿ¹á„É ®Ý”ü#”_ÿ±ÆÑŽ›N¸ïh'ò÷H²!?Bvk4|ˆ|í`;Ž›n_'$ï†9A¹`œþ|‚a;$ÓîàèÐùPXD^N0<˜Oßï`Zz8!ÝC¿ûetŽBVGÈö­çÕw}ýñœÐoú¯=*?÷Øé?&|]²¾¼°õ̆×ÑÇN¸¬"MOs]ÊoBÞÊ‚ô`M_¤C[£–4ª½FÔ4»^AéÁA„qc×®]9rÄ™HMMeñâÅ”””púôi¶nÝŠR ­5‰‰‰Ìš5‹»îº‹œœœˆt:::Ü}/^¼H||<ééésóÍ7ãõz¥°1°&ôb cÒ’´ESÒSãzá )(( ¬¬ ˲¨­­¥¢¢Ó4™={6J)~øÃâóùhoogïÞ½¼ôÒKüèG?Â4MZ[[ùÏÿüO¸ë®»ÈÎÎÆ4M.\¸ÀÇÌŒ3dEvA ¬X'ÖþØëª†vT é:¡-BO@Ü RyªÞÔZôOLÓ$)) €åË—S]]ͱcǘ={6‰‰‰ÄÇÇ“œœÌ­·ÞÊï~÷;šššÜ^¬Ý»wcš&O>ùdDO•ßï`X•——SZZÊñãÇ©««#%%…õë׋&Œ ™ƒ5ƒŽ±gñUõ<èÒu W}ín‚®W_ÍõNA´è}Í{<lÛp¾»»›¿üå/®QÐÙÙÉçŸΊ+.{ðÝwßeáÂ…<ýôÓðÊ+¯ÐÕÕ%¯@áòÛ¨Á•<‘dˆP#cP†aÔÔÕÕQWWÇÊ•+ݶõÌ3Ï(,,$33€––´ÖdddD¤ó¯ÿú¯X–ÀŠ+¸ë®»Ü°›o¾™… °nÝ:>ÌÙ³gY°`T€ Ö¤í¹@†'¥®Ú"dˆ0æoj"WŽ?Ζ-[Ü^«ââbÖ®]ËÙ³gQJ±yóf¼^/gΜ¡²²’M›6˜æO<ÖšW^yÅ5´úŸ ïóùˆ‹‹ãÒ¥KÒÖ1°Æ÷‰t•_òc{\UÏÃ.׸ÃïªtÃU_ö€ñPoÂØ"œ²½X2D8îäçç³iÓ& à %%Ȝᒖ–F||<ttt°cÇ{ì1ÒÓÓQJÑÔÔÇï÷ :lþ”n¿Â¸ s°A„I×ëÅï÷“šš:Àø‰fÅŠ\¸pšš 8~Þ¼y|øá‡ôööJa b`MÕO>"œ¤º^ë–¤GÓ—©'G_E¶Súë_‰Þ“ã^ê7Æ–.]ÊþýûÝs÷Þ{/Žãð /pôèQijjâÓO?¥©©iD£MF‹ NÀI†'©®`¸Ê¡|w‰ÞãóaµbÅ :DUU7Ýtééé|ÿûß§²²’·Þz‹‹/âñxÈÊÊâ¶Ûncùòå1ù¡&LâvY^^>!·XìîEú-{Ê^„W¹¡k`]Ö^„Á}òby/ˆ= e/BÙ‹Pö"¦8Ò':Ÿ|2D8Iu€žÁË/$"œÜ7‰è-‚X“ÂD“…F'¡®1i´ëØSY¾»DoAKO>aÜZÒ¨zÛÔ4»^A1°AA„0&ì¯×–ßÅÓ7˜çŒ:ƤÄÕ¥´ 8'Ra ¦Î7§L©[A˜H¤KAaŒ™°¬òòò+¯ïÛÑŒrÆe†C„›Q燓*Ì&Íáò6¢ô¿݇Ëk°ë2G¡DzFÈ™A_à„9˶ÙïúÎÛ}¾é,spùpYk¨ð>ÏÀtúdíAÒsÃÍÁÃ-ó2ò./3L·!dlspg ’WÐØÊÄQšAÜD†H|€¬1txDaxÂΩᕿÜJR÷^áhØJ4†iæÈå0X¥§›ÝÕ£ °‡pN˜ï„GË f#oÖXæ-o]F^£•,Ü"LG¼Az°A˜D(ÑKA ,AÆ-z ‚ Ld«A˜RH–0õصkGŽqwèHMMeñâÅ”””púôi¶nÝênÅ”˜˜È¬Y³¸ë®»ÈÉɉH£»»›‡z€K—.±ÿ~jkkéèè !!™3g²fÍæÌ™À¿ýÛ¿ÑÖÖÆ<ÀÂ… #tzî¹çhll¤¬¬Œ›o¾Y*IK¦6Òƒ%LM (++ò,jkk©¨¨À4MfÏžRŠþð‡ø|>ÚÛÛÙ»w//½ô?úÑ0ÍÁÿœò÷¿ÿ=Žãpß}÷á÷ûéèèàäÉ“tvvö(Ejj*Gމ0°Îœ9CGG>ŸO*F"A&=¦i’””Djj*Ë—/gÞ¼y;vÌ OLL$99™ÜÜ\n½õV.^¼HSSÓ iuwwsúôiî¾ûn®¿þzRSS™5k·ß~;7ÜpC„ì¢E‹8uê/^tÏ}òÉ',Z´ÃW¨ – ‚0…ðx<ض=¨ñô—¿üÅ5ÊÃçóáóù¨©©Á²¬aóIJJbÁ‚9r€ÞÞ^Ž=Ê’%K¤„áÛ¨ ‚KÔÕÕQWWÇÊ•+à¤Ï<ó @€ÂÂB233ïY0 î»ï>þô§?ñÑG‘››Ëõ×_ÏÂ… #æmõqóÍ7³wï^V¯^MUUéééÌœ9S*BKAˆmŽ?Ζ-[Ü^«ââbÖ®]ËÙ³gQJ±yóf¼^/gΜ¡²²’M›6 ›^QQœ>}š3gÎP[[Ë{ï½Giié€Ië_ûÚר½{7§NâÈ‘#,]ºT*DKAˆ}òóóÙ´i†a’’2`þSZZñññdddÐÑÑÁŽ;xì±Ç†z<Ì›7yóæ±zõjþô§?ñÎ;ï 0° Ã`Ñ¢E¼óÎ;œ={ÖýKDA™ƒ%S Y¦A˜šx½^ü~?©©©#N._±b.\ ¦¦fTydffºCŒÑ,Y²„úúz ‰— FDz°aJ!Ë4Ó°Õk=À[ºt)û÷ï§°°p€|gg';vì`É’%äääàóù8wîï¿ÿþ òYYYüÓ?ý^¯W \K¦Òƒ%LÃV¯¶¯+VpèÐ!ªªª¸é¦›"Â|>³gÏæÐ¡C´´´à83fÌ`Ù²e””” ™OBB‚¶ – LÓoyÑK˜r”•• výõ×óóŸÿ|ÀùÔÔT~ö³Ÿ š†Çãaݺu¬[·nØ|ò“Ÿ þÓŸþT*G™ƒ%‚ ‚ – ‚ ‚X‚ ‚ b` ‚ ‚ WŽ*//—Ù§‚ ‚ cˆô` ‚ ‚ Œ1¶LÃ×JÀq4¶£±ÛÑô:–­éµƒ¾åè ŒÊY¶CoX¸í„;ËésýPš–íDÈ[ŽvÏ÷ÚŽÖ˜FІÆcj¼¦Æãqð†~{Ýß3$oj<xL'B¦/,˜&ýq=Á¸nü°¼¼¦ƒÇ£ñ¶¶±[;ý¾v‚e¥m,'ÒÙŽƒ ï;×!cõÿÖŽã¸iZŽe÷ËÛaaŽvðh¯6ðbàÕ&‚¿=˜x1ðá!.ä|Úìÿ‰O›®\0^_¸‰/$ßwÞ’÷…Ëh¾P¾>L”í€m£oYè^ mY`YhÛFÛv0̶ƒç½QÇQòn:áán^‘á(ÀT(¦ò(Ÿ‰ò™à úî9¯Ñ/×çû"e"Â<Êg ¼fÐùŒþ4CqúåÊ 娠à9í¸çû}´ ŽòíåX¡ãpy'(%‹cõ‡i­A+Žá ùqA§âІ/è+Ú𢕷_FÅáñhåÅ1¼Aå ¥cÄ¡Uœ›žû[õ¥’W>åÅV^¼8ŽÆ±uÈw°­0g;8¶Æ¶ƒaŽ­±Bç#ä,Ër‚ñCé8ŽÆ±lK÷Ë÷¥ç8¡<7]ÇqðxÁãox}àñ€éÓüíñÏ÷…›ðxûýð¸}áf(ÜëŒ.ç‰Wcš¡8&hÇÆîµpVз+ØÎË žëµ±á¾…:må>?\¦×¥ ³ì๾¼zm»/Ü­ùäÙ‚F,aQ¢Ø´PU–Þ„kÃÖ­[Ù³gϸ¤½k×.¶oß.…,Lž,AA¸\#æÈ‘#(¥0 ƒÔÔT/^LIIɈû<øàƒ˜¦))ˆ5ÝѢشPU#½X‚p¹PVV†eYÔÖÖRQQišÜ~ûí#Æi‹Û¶Å¦ƒ¥.ëõ¨®"® Â0Ñkiš$%%°|ùrª««9vìË–-£¢¢‚úúzº»»ñûý”””P\\ìÆÝºu+3gÎdÆ <ûì³,Y²„––jjj(**¢¬¬Œ¶¶6öîÝK]]J)æÎËÆIKKÀqöîÝË‘#G0 ƒ%K– ØhZ&±¥¯Bjj4ôI=KÇNj©gal7ÓX¯É{y<ººº°,‹¼¼›6mÂ0 RRRÜ¿¬¬¬äðáÃlܸ‘ììl¼^/{öìq ±¡ˆ6ÀyyyÜÿýæU%%%É\+a*X‚ ‚0Ð òûýÎ744PXXèNj×ZÓÜÜLVVÖ¨ÒÏÍÍ¥ªªŠÄÄÄ!‡ýRRR8{ö,×]wœôþÅ_››+$ `ÎÁRW!55údÖôPU¾‡cæq3õšüÏÔŒŒ êêêhhh ±±‘W_}•ŽŽŽQ§³hÑ"Ù¾};õõõ´¶¶ròäI^ýu.^¼ÀÊ•+9xð 555455QQQAww·ÜG È¡<[G§˜ N£W×4@†cþ3`õêÕ´¶¶²mÛ6¼^/Ë–-£¨¨hÔ†×ëå±Çcß¾}¼üòËôôô0cÆ òóóÝ­U«VÑÑÑÁ®]»PJ±dÉ’+ÊKKA&œ²²²!Ãxè¡á÷0üû¿ÿûˆãŸüä'ƒÊ%''›—alذÁ]OK†C†åãu*}ÌÆ”ª2D3›i¬—ô³ Â2°dˆP„inåÊ¡ ˆ%L£oF%e(}‚ ‚X‚ ‚ ñ]^^.}À‚ ‚ cˆô` ‚ ‚ Œ1¶LÃÿ¹|œB©Q8}¾Rh ¥pPh¥Ð®Ž2p\¹~_«çe¸ÇA<æ×Ÿ¶ÖwÒE‡tè˳/'¤ŸV¡Y4}“i”FJ;û|ÂŽûœ%;”Lÿ1aò:t¬‡Ì+âwtºáªht€ ùáÇ*ÞF„ltø@ù¨ó¡sC…3\ºáòªUí–EX-†Î9Aå¶´¨ãð¸ý­Ò-çAÓ,<*ݾã¾8ƒ¥çÖ)ý?ú\ôq´‹lc>œaqûºÁuð²ÜmEƒ¿ƒ¾»ëˆî‹®Ãí×aòè(ŸAÂÁu ?=©Ðÿ 0¢ŽÃ}†8î305´üPi0¨¬΀g’V‘7¹NÄ-&3Hüˆc–ÖÐa#ÅÜW<óÌ3òV¤k2K“†ûŸs*ÊUÅ`¹ BðöR1qÅÞVÅðóWÄÀ%±4™¬ßPåªc°\!x{阸Çbï «cøù;ìÚµ‹íÛ·Ë=& É”ZÉ=†vr‰êÁÒ“¼\¥KˆÝ‡‚ÒjÒY±Úƒ¥¯Ñów×®]9r¥†aššÊâÅ‹)))Á0Œ«N»»»{ÄÕàG¢¾¾ž÷Þ{/¾ø‚öövzè! åk XV×ÌL›G¡Ö¸ó§&ÿw«Ž#«¿\Å” &I–º5cà i9Äóv¼K¹  €²²2,Ë¢¶¶–ŠŠ LÓäöÛoŸ¥˜9s&K—.å÷¿ÿ½X‚ìE(‚ L~LÓ$)) €åË—S]]ͱcÇX¶lÔ××ÓÝÝß理¤„ââb7nUUï¾û.---x½^rssùö·¿Í{ï½çöŒ•——£”âÑGåú믧­­½{÷RWW‡Rйsç²qãFÒÒÒ†4 Bz2WAKAˆÅ——ÇCWW–e‘——GII >ŸÚÚZvîÜIzz:³fÍ¢½½W^y…o|㨯¯GkͪU«hll$PVV†Öš„„lÛfÛ¶mÌ™3‡Í›7c`Û¶m<ýôÓ˜¦) Œˆ¬ƒ%‚ ÄuuuÔÕÕ‘ŸŸOJJ «V­"''¿ßÏŠ+X°`UUUttt µ¦¨¨ˆ´´4²³³¹å–[ðù|ø|>¼^¯Û;–œœŒišTUU¡µ¦´´”ììl233)--¥­­S§NI—÷ E ‚ LvŽ?Ζ-[°m€ââbÖ®]‹ã8TVVRUUE{{;¶mcÛ6^¯€œœòóóyþùç™?>óçÏçÆo$!!aȼΟ?OKK [¶l‰8oY­­­RÂ42°®é ÑØ˜Ž*Ä@³ÕÓk¢ûô•2Åžt9ùùùlÚ´ Ã0HIIqÿz°²²’dzqãF²³³ñz½ìÙ³Ç5Ä Ãà‘G¡¡¡ºº:>üðCÞ~ûmžxâ‰!çSòòò¸ÿþû̧ê›&#! N”®²Ðè8—« Ž£–×z¡Q¯×‹ßï'555bi††† )..v‡ ›››ÄŸ3gk׮婧žÂ4Mª««àäùh#*77—ææfIOOpqqqr_ ÓÏÀ’…FÇ«\e¡Q!v ²Ðèøh9YÍÈÈ ®®Ž††yõÕWéèèpÃÏœ9Cee%çΣ­­êêj:;;ÉÊÊ --/¿ü’¦¦&:;;±m›E‹‘˜˜ÈöíÛ©¯¯§µµ•“'Oòúë¯sñâÅAõœ?ž/¾ø€ÖÖVΟ?O[[›Ü‡ÓYht¢t•…Fǹ\Yhtµ¼– ÇêÕ«immeÛ¶mx½^–-[FQQÝÝÝÄÅÅQ__Ï¡C‡èéé!--õë׳`Á–.]Ê©S§xá…èííu—ixì±ÇØ·o/¿ü2===̘1ƒüüü!{°Î;ÇÖ­[Q*¸ßäÞ½{X¼x1eeer/NÇÇOyyù„<\…–pPQò q¦éPéEÇ×*âùW—Wðú4aËR„]·ÐáÇDÉëHùˆe6FwÓ$ï‘t.`÷îÝòV¤K„«DE¢›„´‰˜a×®]lß¾] B˜|=X‚ ‚p¹ÆÌ‘#GPJa©©©,^¼˜’’’ˆ} ¯4íîînzè¡«J§²²’êêjšššðz½Ì™3‡»îº‹ÌÌL©@1°A¸¦h¤Kˆl²—æPVV†eYÔÖÖRQQišÜ~ûí“B¿úúzV®\I^^Žã°oß>^|ñE~ðƒàõz¥ÅÀŠ]û×cå 5Fc×dÿ/¥bfŸG5X9Oú6 ûQÆœÊÐÔÅêåÄ4M’’’X¾|9ÕÕÕ;vŒeË–QQQA}}=ÝÝÝøý~JJJ(..vãVUUñî»ïÒÒÒ‚×ë%77—oûÛ¼÷Þ{nÏXyy9J)w/¶¶6öîÝK]]J)æÎËÆIKKT¿ï~÷»Çeeeüò—¿äܹs\wÝubmˆã±ôù§õ˜YúéªcdrÈ€N¡ÉÜ&Ô5­Å1.רºÕ&¨”F7–¾'ZOÇCWW–e‘——GII >ŸÚÚZvîÜIzz:³fÍ¢½½W^y…o|㨯¯GkͪU«hll$PVV†Öš„„lÛfÛ¶mÌ™3‡Í›7c`Û¶m<ýôÓ˜¦9¢~ÝÝÝ(¥HHHKcš"“Üa"­Aˆ‘ïÉD]]uuuäçç“’’ªU«ÈÉÉÁï÷³bÅ ,X@UUh­)**"--ììln¹å|>>Ÿ¯×ëöŽ%''cš&UUUh­)--%;;›ÌÌLJKKikkãÔ©S—Qš={ö0wî\²³³¥Â¦)2D8a Ëḵƒèr–!Âq(WdˆpäRºFq§z¹9~ü8[¶lÁ¶mŠ‹‹Y»v-ŽãPYYIUUíííØ¶mÛî¼§œœòóóyþùç™?>óçÏçÆo¶géüùó´´´°eË–ˆó–eÑÚÚ:¢®466òøã‹•!ÖÔè !ÂñÓU†Çó},C„ã}«MP):® M~~>›6mÂ0 RRRÜ¿¬¬¬äðáÃlܸ‘ììl¼^/{öìq 1Ã0xä‘Ghhh ®®Ž?ü·ß~›'žxbÈùT@€¼¼<î¿ÿ~tÔ…öÍθª­­å±Ç#%%E¬ŒiŒ  ‚ “¯×‹ßï'555bi††† )..v‡ ›››ÄŸ3gk׮婧žÂ4Mª««àäùh#*77—ææfIOOpqqqÃWÇŽãÑGÒxÄÀŠ9Ü!˜Qx솯…®*–ÚÁ8”ó¸ux\»Zãrv·Ú8ßý+´K¹ŽŽŒŒ êêêhhh ±±‘W_}•ŽŽ7üÌ™3TVVrîÜ9ÚÚÚ¨®®¦³³“¬¬,ÒÒÒøòË/ijj¢³³Û¶Y´h‰‰‰lß¾úúzZ[[9yò$¯¿þ:/^TÝ»wóÙgŸqÿý÷ãóùèèè ££ƒÞÞ^±4¦)2D8a ËḵƒèržôV‹ Ž÷­6A¥4ê¸2D8zV¯^Mkk+Û¶mÃëõ²lÙ2ŠŠŠèîî ..Žúúz:DOOiii¬_¿ž °téRN:Å /¼@oo¯»LÃc=ƾ}ûxùå—éééaÆŒäççÙƒõç?ÿ¥[·n8ÿÍo~“›o¾Y¬iˆ*//ŸÛDö"”½ašïEèÆ—½e/Âp}½a C„¦° Žk§Ð—ó¸ux\»Zãrv·Ú8ß2D(b`Mæw•ž~ã×lˆ0–ÚÁ8”ó5Ö:ÊuÚÝjã\±·–¬Ù%ÓÄÀ„˜Cz„è&!mBÄÀAAgÂþŠðãÿïU)}AA¦$Òƒ%‚ ‚0ÆLXÖÿ]þAÔfó©0?Ú>˜Ìå¤iŒAZÃ…q…i\i~£¹þÑ–Ñx—UôùHÙÈ%)-UýRþ;üx(8Y=ŒŒ¾Š|úÎ÷…Y|ZG ‚~Íõ¡;Ôå‡ÿ6£\¸Lx¸1HÜèø† Êî÷•ôVÈÙ!g…ùá2}áNÈï *½ð´údì!Â{‡ðí¨ü{/#½¡tq0BNá`¢¯ªX=ÃTŸ'JÎ¢ŠªØ3DxôosÝ¢Ó ÏËtÀ£Á 9u5ÅÞ7üæ~Q].oeAz°„éÄôÚÄVªrJd*U*‚XÂdgzýù¹Tå”ÈTªTWvíÚÅöíÛ¥ „!ñHãû,ß×S¦*¥ H•N 1säÈ”R†Ajj*‹/¦¤¤$bãç+M»»»›‡zH“ –0ßÈjâ ãR•Ó#S©Ò)fPVV†eYÔÖÖRQQišÜ~ûíÒ1°AáJ0M“¤¤$–/_Nuu5ÇŽcÙ²eTTTP__Oww7~¿Ÿ’’Š‹‹Ý¸UUU¼ûî»´´´àõzÉÍÍåÛßþ6ï½÷žÛ3V^^ŽRÊÝì¹­­½{÷RWW‡Rйsç²qãFÒÒÒÕo¨<¼^¯TžX‚ ‚#//‡®®.,Ë"//’’|>µµµìܹ“ôôtfÍšE{{;¯¼ò ßøÆ7(,,$P__ÖšU«VÑØØH  ¬¬ ­5 ضͶmÛ˜3g›7oÆ0 8À¶mÛxúé§1M3B—áòÄÀA„˜ ®®Žºº:V®\IJJ «V­rÃV¬XÁ‰'¨ªªbÖ¬Yttt µ¦¨¨ˆÔÔT²³³]y¯×‹mÛnïÀ_þò´Ö”––ºçJKKù—ùN:Åüùó#ô)A ,AA˜”?~œ-[¶`Û6ÅÅŬ]»Çq¨¬¬¤ªªŠöövlÛÆ¶mwh.''‡üü|žþyæÏŸÏüùó¹ñÆIHH2¯óçÏÓÒÒ–-["Î[–Ekkëù+ÉCKA®Ž1)ËÏÏgÓ¦M†AJJŠû׃•••>|˜7’×ëeÏž=®!f<ò ÔÕÕñá‡òöÛoóÄO 9Ÿ*——Çý÷ß?`˜/¼§«+ÉC˜úÈ:XÂe Ë4HU"m@ªtBñz½øý~RSS#–fhhh °°ââbrrrðûý477ˆ?gÎÖ®]ËSO=…išTWWÁÉóÑFTnn.ÍÍÍ$&&’žžáâââ†Ôq¨<1°a>?e’çTëI6 U:™ÈÈÈ ®®Ž††yõÕWéèèpÃÏœ9Cee%çΣ­­êêj:;;ÉÊÊ --/¿ü’¦¦&:;;±m›E‹‘˜˜ÈöíÛ©¯¯§µµ•“'Oòúë¯sñâÅ:Œ”‡0=‘!Baœ¿‘¥÷Bº;¤ H鎫W¯¦µµ•mÛ¶áõzY¶lEEEtwwG}}=‡¢§§‡´´4Ö¯_Ï‚ Xºt)§Nâ…^ ··×]¦á±Çcß¾}¼üòËôôô0cÆ òóóíÁ)A ,AæY•ÊTªt YeeeC†%$$ » {VVßýîw‡ OJJâá‡p>99yØ|ÃÃFÊC˜žÈ¡ ‚ ‚X‚ ‚ b` ‚ ‚ ˆ%‚ ‚ \9ª¼¼\f  ‚ ‚ Œ!Òƒ%‚ ‚0ÆLØ2 å埄~%™@I 2i"“F2iÂG.P7’F'Y¡° Œ,ªÉ¤†,j€`5p;=ÜB¥§‰JO#•fñœwe3©&•öl PB3É⯡´þJ&õdÑäê“H®›6”PcXôÔpÀóW*=58ƹ0½šð07¤wPD:mnX&M¨°0Å‚ˆëòÒÄAO#ð´F\—ÉuQuþ»‘!OjD~F=<ýu|ÞÈŒ¸¶:Ã’Éùˆø&Ë\Y(!ƒÚPûú+~Žq ¤w¥§‰?{:"Ú„‡YdÒQÏáå‚'¢ÜIŒ(¯èr­24CyUzQ*²,Lò#ž´„•W£Û²¨!³¡ö¼¶‹Ü@¥·Ú­çf]D§Ö†V“HR¨¼úžgܼ²h"ެˆöþ©èyšˆWé„ÉßQ´G¤åavD¸¥Îºõ[驦ÖĽ‡a5 dG”Sð™ÖßÞã˜qm©|å†eRÍóKWïJO–ZöŒ½-âÞÉäXDûéŽh^r"î‹zÝç~ÐOâW¿ª—·² =X‚ ‚ ŒGŽáŸÿùŸ¥ ¤KAƇ]»vqäÈ”R†Ajj*‹/¦¤¤$b_©ÄÂ… )((Uœ­[·2sæL6lØ F ,AA™‚‚ÊÊʰ,‹ÚÚZ***0M“Ûo¿}ÒêlÛ6¦i^ÙËÙãÁã‘W´XÂ$A¶¤¦fk”–-˜¦IRRË—/§ººšcÇŽqûí·ÿÿì½{tוðû;UÝj½šÖ“‡ÂCD I€xZ€±ƒ‚'Û1vâ$d’Yö¬Ì·>ß|÷^kòÝÑufÝ̰2++'™<Œ3‰“„ml ¶‘1/ƒAÂæ!2 ‰—@tw©_’,!µ´¬â¨êìsή}NWí:§êN:ÅÛo¿ÍÙ³gIIIáŽ;îàî»ï&!!€µk×2sæL.\¸ÀÑ£GILL¤¬¬Œ¹sçZùWVV²bÅ Ž=Jcc#N§“{iÓ¦Y2W®\aË–-444 ”büøñÜwß}¤¥¥¾ž¶öövrrrØ»w/6›gžy†µk×2kÖ,š››9räIIIÜÿýŒ;–W_}•“'O’žžÎ<@NNà"Ü´iÏ=÷555Ô×׳`Á¶nÝJ[[ùùù¬\¹’„„ª««ill´ÖDTJñÌ3ÏXº ·ykH!3nC³5JË"zl6¼^/---¼üòËð·û·¬ZµŠO>ù„7ß|3D~çÎŒ3†5kÖPZZʦM›øøãCd¶nÝJAAßýîw™1cùË_¸xñ"àëZ·n‡ƒo|ã|ó›ßÄáp°nÝ:¼^¯•ÇÇLss3O<ñ>ú¨u|÷îÝŒ?ž5kÖ0eÊþû¿ÿ›êêjŠŠŠøÎw¾Czz:ëׯïñœ[ZZ8zô(=ö=ölß¾€åË—3nÜ8fÍšÅÿøÿƒüà¸\.i(â` ‚ Bl444ÐÐÐ@^^Û·o§°°yóæ‘‘‘Á¸qãX¾|9uuux<+Íøñã¹óÎ;ÉÌÌdÞ¼yL›6]»v…ä[PPÀÌ™3ÉÌÌdéÒ¥äääðþûïpðàALÓdåÊ•Œ9’¬¬,V®\É•+Whll´òHHH`åÊ•dgg“mÏÏÏgöìÙdddp×]wÑÑÑÁ¾ð¦M›Fff&¥¥¥\¼x‘ÖÖÖϽ¢¢‚ììlÆOQQ‘å$&&&¢ë:v»””RSSQJú~ô!@L0”“04[£´láØ±cTUUY½E3fÌ`ñâÅüæ7¿á³Ï>ãÃ?´dMÓ×çyùòe²²²;vlH~cÇŽµœ§àcáûçÏŸàüùó´´´PUU"ãñx¸té’µ?jÔ¨¨ï]5Êú;55€‘#GZÇRRR0M“ëׯ[ñᤥ¥YÞ|®_¿.C,¡ÿ‘ah¶FiÙB^^åååhš†Óé´¾ìììdöìÙÌŸ?ßr¬ôåYgg'999<ôÐCåÞ °ÛíQÓGûÚ1øX ·)<ïžòPJõ(/ˆƒ%‚ =b·ÛIOO8>fÌ.\¸5.˜Ó§OGìz·‚…ì3Æ*çСC$''ãp8¥t]‡k!ï` )d Eš­QZ¶Ð¥¥¥455ñÆopîÜ9š››©¯¯ç7Þ‘kjjbÇŽ477³gÏ>ÌüùóCd>Lmm-ÍÍÍlݺ•³gÏZ_’œœÌÿøGN:Å¥K—8yò$o¾ù&W¯^¶HKKãôéÓ\¾|™7nˆ³5ÀHÖB~LÂÐlÒ²…î5jO=õo¿ý6¿ùÍo0M“ŒŒ Bä,XÀÙ³g©©©!11‘åË—3iÒ¤™Å‹sðàA6n܈ÓédÕªUÖ‹êv»§žzŠ·Þz‹W^y…ŽŽFŒA^^Þ-õhE{ýó¾”¾páBª««ùÙÏ~†Çã‘iÄÁA„¨è1>''‡¯ýë=Ê8~øáeœNgù¤¦¦ö¨KwqÏ>ûlıçŸ>d?---äXqq1ÅÅÅ!ÎßâÅ‹CÒÌŸ??¤.33“o~ó›Ò` 2D8¤ah¶FiÙ‚ ˆƒ% 2" ÍÖ(-[è÷™3JècdˆPAÒD¢ '|ÈN>/Òƒ%‚ ‚ÐǨÊÊJé}AAèC¤KA¡°w°Füc׺Q …R¾PS ¥4_ˆB)…æ )Íw,(Þ’S𕇶ßOXžA²áyjþM×ü¡Biè]ÇðËøB-T&<>(Ÿàýè2¡å(] +««L4ŸÎ(ås™ýçÐ/Î?]°œo_uå£ÂÒj(‚â‚óôËù Z@ο’.HWe5º®ã*輺Òû÷Á*O…å!”Æ÷7˜Áû€©ˆÔ%3š®!é••·•ÁyF)Ïÿ ·©ü!¦ÿÿžBÃÚ'Ê Ä!òV|ù‘¯i˜þдBÃ4Ââ¼¾cA2Áñ†zCöM 0ÍnäÌ x™¦×úeCÂH #X7ß’Æ0Bt7LÓŠ – /Ï4M¿¾f7z6|y¦?oÃèEÎ 3 møeBâyBH™¾¸°ü 3(_³ägXÇ#d 3(>øò7ü-+P&¦¿…ú›éߌýŽÜ•éÁás2̾Z’o´AKA›º:^xá1„0èia 0ÍaÕ­#_Ó·Juu5uuu¾×;4 —ËEQQeeeLŸ>üü|Þ}÷]öîÝË÷¾÷=’’’¬ôçÎã—¿ü%_ûÚט2e 'Ožd×®]œ>}šÎÎNFŒANNsæÌa„ btAhïîV IDAT¬Jôí?]ñP–Š#]{> sÒʃIÏx9¡Öloüü|***ðx<?~œ7¢ë:¥¥¥Øl¾[YYYÇŽcãÆ¬Zµ ¯×Kuu5EEE–sµgÏÞ|óMŠŠŠxøá‡ÉÈÈ ½½“'O²yóf¾ýíoKý â`?+Ñ·t5ÍA븄Ûq0ëÅsõŸù9-0@†ŸÌœæ2ÝÛˆ®ë¤¤¤PRR‘#G8zô(©©©lÚ´‰çž{MÓøÊW¾Â¿ÿû¿søða¦M›Æ¶mÛhooçK_úW®\aóæÍ,X°€{ï½×Êßår1jÔ¨µýA,AˆË”  Â-ß¼l6ÚÚÚ"Žgeeq÷Ýw³qãFؾ};_ÿú×q8>|Ã0X¸p¡Q+Ö§cÑ·Ÿt•!ÂÛp2D8 zÊa\¶††˜7o^ÔøùóçS__ÏË/¿ÌüùóÉÍ͵⚛›q8¤¦¦ZÇ>LuuµµÿôÓO3räH©cA¬ÀÓ± ö“®2DØ_ž+2D8ÀzŠs7MïØ±cTUUáõz˜1c‹/æÐ¡CQå-ZÄK/½DYYY¯yOž<™5kÖpõêU~÷»ßa&ùq°A„¡L^^åååhš†ÓéDÓzže(.—™™IGG­­­V/VBBhšæŸ°W>?Cf,"ìG]eˆ°ŸzÌ>h *n°9õ”ÙOã¦éÙívÒÓÓq¹\½:W=1mÚ44McÇŽQ~’â\ }‡ о1ø2DØOž+2D8ÀzÊý4îšÞÍ^»Âq¹\Ü{ï½lÚ´‰7nP\\Lzz:mmm8pÀškKÄÁA„¨Ï0ѶæÍ›Gvv6»víâÏþ3$%%1nÜ8üqyÁ]+üéXô½½©ÁhǸ"T³ú_Ø¡|E8hÚÀ­RQQÑm\qq1ÅÅÅÇsssyþùç»M7qâD&Nœ(õ(ôC¦Ô}ûÑ0ãÆŽñùÅð"”zŠs5|.º‚ – —€áõ†µ¼O.‚8X‚ ‚ Â-3`ï`]}¾0ÍeÞÁ¯b ëÞ+ \(@4š(1³ H– ‚ ‚8X‚ ‚ ƒ"üÇŸVí)kSš†Òt”®ƒ¦£4 tÍjÊ®£ì6”]»Ž²iA› :*AG9‚ÂÀß :Ø4”MÝ—†Ý—g‚޲ûËÒ5ŸœÿoeÓ}a@åÿ[)@C¡ùt740µÐÐP]¡[Cy4”[ÿo^å ;5T§:ª£+Tºïx@Þ­¡Ü:Ê­ûd:ý{4”7 §£¼6ðØ¬¿•aó‡:Ê´ùu×…ŽRþ ¥þÍ Ê@³¹Q6µ¡(›e3Pº•à Ú¼(‡Kðo6ì†OÞîE%àð…Êf l&Ø ”núeL”Ýð³™ ùâÐM”2AùC@ùÇE­Ð0}S ˜øþöš(¯ å5Àm¢:Mp(ïx`Sn:½¨N: ”ÛðËà1|Ç;½¨¯/t{ÁãõÅ{}ûÊíA¹Ýàö <”×ò¸}¡áE™”éÓ‹fv¢pû·NÀ‹R”ò¢TJ¿¥—¡i¥(Ût”Íf÷øC7J÷‚Íë«»¯´Êáñíìn÷¢ì†¿Nü¡­ËÖØLËæÊnB‚éÇg”fúÉ|?Pþ÷ô¨ÀÀ˜xZ™€o˜Úå<þ}~»^è1¡Ó„N:ýõàñÕÓ_^T§·+ìô :=ÐáAy¼>ûBw'šÇr»QîN0¼(ÃÓµ™(£Óšû{¬PÓ:PªÓjYhz)J_ˆf+EécQºÿ· {Pv¯.Üh nÿï·a÷µ-Áë«“/šÃð×?´ùíïß°ƒ Ú|öVþ:P )”î5¿ÝU×5SSù¯A ¼ ¡Wùê z¸N|¡;ø÷€¯>: húííoûÛ÷v…n7ª³Óow7xÝàõ€×ƒitbz;0½ÞLà x1M#ÁäŸÿnšÜ•éÁ„Á|³&ÄcK”v{³ÔÕÕñ /ˆ!éÁA„›¡ººšºº:k—ËEQQeeebA,A¸½È,ˆÂài‰JÚíç&??ŸŠŠ <ÇgãÆèºNjjªGKúÕç‚q{†Â°©U5,Zž®ë¤¤¤PRR‘#G8zô(³gÏ‘kiiaóæÍœ>}·ÛMVVË–- YgÏž=ìÞ½›«W¯âp8˜0a_ýêWøíoËÈ‘#Ñ4ºº:t]çî»ïfúôé¼ñÆ>|˜ÔÔTî»ï>òóó0 ƒ×^{“'OÒÚÚŠËåbΜ9ÌŸ?_·8X‚0Ôz zY(ÍŒç3†šós»í%õíùm ’›—ÍF[[[ÄñÎÎN¦L™Â²eËÐuðŸÿùŸ|ÿûßÇårqöìY6mÚă>ȸqãhkkãÔ©S!y8p€;3oûÛW™~ø!'Nœ ==pæÌ™˜BA,Aˆ³›™  ñãËaßñ¥/}‰ 6ðë_ÿšäädJKKéèè°â9rä555x<233YµjÙÙÙ7W¯ªË³gÏæÜ¹süå/A)ÅôéÓ™;w®å¼ â` ‚  £¢¢¢Û¸ââbŠ‹‹­ý´´4V¯^"3gÎëïñãÇóä“Ov›_´¸gŸ}6âØóÏ?ßu#µÙxàxàBdî¾ûn©¼aŒLÓ ÑÞ"f_¶ÛlÒòA,Anf}(·g( ›Z5¥å ‚8X‚p»çm![¢´[AKAAˆþ¸TYY)=¿‚ ‚ }ˆô` ‚ ‚ ô16MÃóÿó]K æ ž.ä8añaòfÔ´áy=ž^Ê6c•7ýzÐí¹™Ýœk°¬ÙÃ>ay.ÏÍ•-ÞŒbwÃ4#u ²»¦k´}Âäé%¾ûüºÊ¦'ù(qFоÑ˹·¤[PÙaå™Qê!Ø®FXÚ€l@ưö»ÏÛˆ¢{p~¾´þ2£éjFæo˜áúŸ‡ÙëyZeùËNÒ‚ë7Z~aåf÷v5ƒmƒn‘?x3JHÐ~È1J¼ÙM~ÝíÓM™fw#À º†É›&˜Fð Aûáºd£”ošáé‚ö £{{òí6ÞˆÔø¿—ß!weAz°„¾F‰Š‚4qAq°AA„hÈLîƒST¤‰ Bª««©««C)…¦i¸\.ŠŠŠ(++CÓzï'¨©©¡¦¦¥¦iâp85jK—.%77W ,ˆƒÕOйª÷•mƒ-©Tä mñ¡¹¨8,•–ë@?]poëu ??ŸŠŠ <ÇgãÆèºNiiiLéGŽÉêÕ«1M“¶¶6vîÜÉþð~ðƒàp8¤>…>gH š?öxº¨îTÌ`Íx²«ô ¥•xHq›¯º®“’’‚Ë墤¤„‰'rôèQjjjøÅ/~"»{÷nÖ®]z³Ó4RRRHMM%;;›%K–ÐÙÙIss3—/_¦²²’sçÎYiÚÛÛ©¬¬¤±±Qê[ÞVœ_­DEAš¸ ĈÍfÃëõÞRZÇCmm-IIIdffv=(ùCèÃ6:”NF†ûѶÈápV1¾•–ë@?]pì:ÐÐÐ@CCóæÍ‹9Íùó穪ªÀívãp8XµjUÈð iJ;ÄÁŠþ€÷C„ƒW_3XE"v*ö‰ÒÖÄå¦ÙOÜÛZܱcǨªª²z­f̘ÁâŋٱcGLé³²²xôÑG1M“ÎÎN<È+¯¼Â“O>INNŽÔ§ – ‚0üÈËË£¼¼MÓp:Ö׃цõ¢ êºNzzºµ?zôhêëëÙ½{7>ø`ÌùB¬ ©w°" ñ4ž>¸Ÿ²U°Šqõž‚Là:h”6ÅÀCŠÛ|°Ûí¤§§ãr¹B¦fHNN¦µµ5D6øEõÞîÇÊàÚµk!ùÈ{Y‚8XÈW„ýîþÉW„ÃVÅ>QZ¾"Z ’ë@nn.ׯ_gûöí´´´°gÏNœ8!g­­­´¶¶ÒÜÜÌ»ï¾ËÅ‹¹ãŽ;,nìØ±lß¾ .ÐØØÈ;ï¼#õ,Ü22D(‚ Ä-ÙÙÙ¬X±‚÷Þ{mÛ¶1mÚ4.\Ⱦ}ûBä.\¸ÀO~òË™JOO§¼¼œÂÂBKæàÕW_åÅ_$++‹{—^zIŒ,ܪ²²r@Cúc±çðô¦QYìùó/öܵPp`mV3N{ZܘAºØs ¼¸Zì9O<,öÚd±ç>Zì9$YìY‘!B!6ÛFó<ãOsQqX*-×~ºàŠ a¸8XqÞ™(* ÒÄAÄÁAA¢1`/¹ÿãÿI¬/ Y”“'˜>6¨ÐGÆ u1‰ ôrýAAKAap3`C„½NÓÐÛÔQ⻋ò5teÓs|Oòf”ò"¦ 2}ïÓ4µ¬ðs¿éi¢èmšÃ4#ucpLÓÐít„M-@SpSD‹ÏŸž§:‘ú;º]͈© Œìhôdצiè*‡¦^Ò 3Hw3húF×¾éß÷ÿ³Ò˜~IŽNg훑å‰avÉþézÒ­ëÇ>í@÷SX–íN.d‡ò –ˆ3bÐ-ì¢1]BÈüÑåC¦\è.žîe{ݧçøÀ}aáýrW¤Kèkä+BAA,AA¸ TWWSWW‡R MÓp¹\QVV².awÔÔÔPSSƒR Ó4q8Œ5Š¥K—’››{Óú>|˜½{÷òé§ŸâñxHKKcܸqÌ;—1cÆH… â` .dBLA„îÈÏϧ¢¢ÇÃñãÇÙ¸q#º®SZZSú‘#G²zõjLÓ¤­­;wò‡?üüà8Ž˜õøë_ÿÊ®]»˜?>K–,ÁårqãÆ Ž?ÎÛo¿Íã?.•% =+ðttD¼‚¾²m°%•Š£Yœã  H3@ûö•ñ¥û]×III ¤¤„#GŽpôèQ<õõõ¬Y³Æ’ݽ{7»wïæÙgŸµŽišf¥OMMeÉ’%ÔÖÖÒÜÜLNN—/_fíÚµ¬Y³†Ñ£GÐÞÞÎ /¼À“O>Inn.MMMìØ±ƒû￟¹sçZy»\®ˆž«ššêëëY°`[·n¥­­üü|V®\IBB‚Tè0@–ÊTwAlÛ`e©œa§b|_n‡ñ¥o{ï€Í†×ë½¥´‡ÚÚZ’’’ÈÌÌ yHâp8())‰©œ––Ž=Êc=Æc=Fcc#Û·o—Ê.mTL wY¹‡‚O444ÐÐÐÀ¼yóbNsþüyªªªp»Ý8V­Z2ù$999·¬×Ì™3ùâ¿ÈéÓ§Y¿~}È=(---d8055•ëׯKeŠƒÄÿáàÕ× VQ†‡Šñ}a¸Æ—Jìoòòò(//GÓ4œN§Õ‹mX/ÚС®ë¤§§[û£G¦¾¾žÝ»wóàƒÆ”Off&MMM†a•Ÿ˜˜Hbb"W¯^Hþ…cd'€0”‘y°A„AÝn'==—Ëâ¸$''ÓÚÚ"{îܹØk•ÂãñXù\»v-$Ÿ`Çkúôétvv²wïÞð…ᎠJ7Fl¶%¨ãJ†‡Šñ}a@†‡0¹¹¹¼ñÆlß¾iÓ¦qâÄ Nœ81õ‚a–#ÖÑÑÁÁƒ¹xñ"eee–7vìX¶oßNZZׯ_çwÞ Écܸq,X°€Í›7sùòe¦Nʈ#hmm¥¶¶Öº Âs°dˆ°ŸÝ?"¶*Æ÷…áv_*q ÈÎÎfÅŠ¼÷Þ{lÛ¶iÓ¦±páBöíÛ"wáÂ~ò“ŸXÎTzz:åååZ2<ð¯¾ú*/¾ø"YYYÜsÏ=¼ôÒK!ùÜ{ï½|á _àƒ> ¶¶·ÛMjj*&Làé§Ÿ¾©9µ„!þlWYY9 WY‹0|-BŸä ^‹®uè¬r‘µe-BY‹PÖ"”µ!œ!õVd×l!å^žÂT*ð ˆòU»éÿÚ>rškº ¿lˆ¡S)„Lãà ÏB§i0 ¹àÐ0ÃŽ™F]ƒeƒÒA–iýoø¦‚°t Ùß|“Jš†O/Ã0BËð§ ¤7 3"Þ02]ùtÉF?ë4.øX@à¡Ê9rW¤k¸"“ôöh1˜kHœ°´dAÄÁAAdØÄ7‡iJ/VÖܬ¹”´ÑTèêêjêêêü¯ah¸\.ŠŠŠ(++ Yø¹'ÚÚÚ¨©©¡¾¾žÖÖV’““™`òäÉ$%%‰‘q°òÒ-_òÛŒ|E(A © ¹¹¹\¿~íÛ·ÓÒÒž={8qâDˆÌÝwßMjj*/½ôÇçÊ•+466²nÝ: ÃàþûïC }Ž  ‚ qKvv6+V¬à½÷ÞcÛ¶mL›6… ²oß>K&99™§Ÿ~šwß}—×_ÖÖV’’’ÈÏÏçÁ”‰Fq°óq_Íä.C„ñÐnáNí!BéÅê?***zŒ/))¡¤¤$äXYYYÈ~rr2÷Ýw÷Ýw_y=ùä“bp¡O!BÑ7&§rxÖü*Ë]]~Ï‚ ˆƒ5u{{ŽÄ\ñÂÒ’AKAa1`ï`©{ž Ù7ý›!uÒ;CÖX†´að¢=6±øêÓäY[úé—%‚ ‚ ˆƒ%‚ ‚0x°!Âÿùÿà[éܲÀÚ‹7­x¬øîÒÆž7±¥ïiß ƒ2ˆßSÚ¨yÑk^=åU‚ムE¾§óŠ߃n¾îÏnt§']MP¦‰ ú»ÛÐð‡téî‹7QþüT˜î*gøBf‹|{(ßšÅÝ0»Ê4‚ÏÃì:/#8Þ¿Aº‡F÷ñ*8>8ÿ@|PÚˆò‚õ Ò9°™Fàwé;9_úCÓÝ'h?ÊŠ7Âö‰ž.ê¾Ôž»òñýÝnV¾xÓßþŒ€9ýû¾jèŠjAò¡ñ#,½•‘òáyÁqañFˆ Ê3òŸGоt48$J~FØß„¥1{—#¨4Sù”Ü•éÁ„Á ©ásSYYI}}½BˆŸ,AAˆ…êêjêêêPJ¡i.—‹¢¢"ÊÊÊBÖ%쎚šjjj¬…¡N'ùùù,[¶LÖ ÄÁ„›ÃŒ ¥§DZùùùTTTàñx8~ü87nD×uJKKcJ?räHV¯^a\¸p 6ÐÑÑÁªU«Ä¸‚8X½¡” {Ï*°ˆÃ56³Êâ°7×^û¦5Ç¥rn÷9ëºNJJ à[çÈ‘#=zÇC}}=kÖ¬±dwïÞÍîÝ»yöÙg­cš¦YéN'ÔÖÖ†”ÑÜÜ̆ 8{ö,ééé,_¾\~@‚8Xù{\\òâðªlrÝTœÜòÄ íÕìçÁ»Þþ›—ÍF[[Û-¥½té'Nœ@×õûÇŸþô'RSSùÖ·¾E{{;›6m²†aX;X‚O·<"”Ö#Ü 4440oÞ¼˜Óœ?žªª* ÃÀãñ ”âK_úRHžÍÍÍ<ñĤ¦¦p÷Ýw³nÝ:1¸ – ŠÊâ°7×^eˆ0^ÎùرcTUUáõz˜1c‹/fÇŽ1¥ÏÊÊâÑGÅívóá‡rîÜ9æÎkÅ_¼x‘#FXÎÀرcå$ˆƒ2D(*Ë¡p³íU†ãå:——Gyy9š¦át:­¯£ ᜰ`t]'==€eË–ñòË/SSSÃÒ¥KåG"ô 2– ‚0è±Ûí¤§§ãr¹B¦fHNN¦µµ5Döܹs½æ·hÑ"vîÜɵk×ÈÎÎæêÕ«!y>}ZÞÁÄÁŠþ$?Œ8üíª¸Ð->ÞÁº½ª>©%v0rss¹~ý:Û·o§¥¥…={öpâĉ^Ó7ŽQ£FñÞ{ï0qâD222X¿~=çÎãÔ©S¼óÎ;òÄÁ"•ƒu“™Ü…XÚ«Ù'u#C„Gvv6+V¬`ïÞ½üâ¿àìÙ³,\¸0¦´ ,`ÿþý\½z¥<ò‡_ýêW¼öÚkÜ}÷Ýòný_YY9 ¿Y‹PÖ"ìßµ YKp0®E¨ d]?Y‹pð¬E0»¬E(k ‚ô`!C„¢²  7Û^eˆp]ºA¬[¾aÉá°WYœáæÚ„)íNÎYÄÁ„¡ô|-=R7‚ ˆƒ%‚ ‚ ÄÈ€M4úãÿïÿë ƒ…taô«Qu1GœÖž<‘ BìÈïEA¡°¬ÊÊJ222ÈÍ͵¶ì#q5= IDAT¦“Ö˜Ž«1 ½Ýîÿ´Wamtp\§ëÜÀARH$)¾O›Qþ0xß‹I7¬Í‡RH )è8,ù@™FPz7nÚ¹A;×iç`ÃîOo'°ù?‡-? {‡_{ßv)ØHÅN zˆîÊ*Ó´t÷âæ:\ÇÍu¼xÑIEÉF*ʯ{צBìn 3(Œf3Ó ‰¢W´¸à2½´â¥ƒV <€§?L +Ou=+@oýšo³]{$8Áîô…6Ý× Øìþ–¹Æ50¯­ä„¤¾01%R>8/åïµ®M7 ÅéÛ’èð¥ lt|<7 óZ×–’.§oKs‚M -ÏføÊl×®ÀµË¾ðÆup:`„œ‰l<€;(ômž ã™ðxwXØS|¸l¼ékíp­ÃÞèÄ_½0H6zίÓ× ¸æ…k^4LlN°ù«YO5­-¬Ê´ ]uÍ·%$ûš†ÃßDì*z5é€ft5ã¨vqBòHLŠl"Áy™¾ªíðo Œpú6—ö°*Ž’OðßÊxx5ð(ßæÖº6¯ÿ˜WGómáñÁéƒâ:Ú×ZáZ«ÉµVмN› §®ã´Ù°›€ÛáßLðà5Ákr½Ýõv/WÛ<\kó’jvU³30¥‡7°]y¹ ¼ƒk“kƒkn“N¯Ócâôš8½IÞÐé]~ä¿/ F*++yä‘G¸ãŽ;¥~ÕÕÕ´··óÈ#Ä…óQWWǦM›xî¹çÄÁá6#Ô‚â@ÔÕÕ¡”BÓ4\.EEE”••…,/ç ”bĈ°dÉl¶Á;Þ·o{öìáÒ¥KhšFZZÓ§O§´´ô¦¼éÓ§“ŸŸ/=X‚ ‚0ÐäççSQQÇãáøñãlܸ]×­üÍâõzÑu}@ÎÁëõröìYÖ¯_RŠeË–ÝV=êêꨫ«ãÉ'ŸŒI~ÿþýlÚ´‰û￟ &àõz9þ<Ÿ}öÙ-ÙÝf³Å…S)– Ed¢!AA×uRRR())áÈ‘#=z”ÒÒR¶nÝÊÑ£GY³f%¿{÷nvïÞͳÏ> tõ°äää°wï^l6Ï<ó k×®eæÌ™\¸p£G’˜˜HYYsçÎíV—+W®°eËPJ1~üxî»ï>ÒÒÒb>‡#F0iÒ$B¬Þò6 ƒ-[¶PWW‡¦iÌœ93Ê<}˱cǘ>}:3gδŽegg[×ÔÔX½s•••(¥X½z5iii¬]»–U«V±wï^Μ9Cyy9@ÈaMM õõõ”””°mÛ6ÚÚÚ˜2e +W®ÄápXåìÛ·]»vqùòeÒÒÒ˜7osæÌ±·M›6qäÈÚÛÛIMM¥¤¤ä–pq°„ۀݥޅanÙÁ¨³Íf£­­Í§ŸŠMÃ?þ‡ÃÁO<r|çÎ,Z´ˆ%K–pâÄ 6mÚDVV'NŒÚ³nÝ:ÆÇ7¾ñ 4McÛ¶m¬[·Žï~÷»1÷Š?žO>ù$Ä)‹%ï;wràÀ***ÈÊÊbçÎÔ×ד——×o¶NMMåÔ©S–cÎÂ… ¹páTTT`š&III\»v €·ß~›{ï½—1cÆ`³Ù¢.ÈÝÒÒÂáÇyì±ÇhoogÆ lܸ‘|€?üššV¬XÁèÑ£ùôÓOyíµ×HHH ¨¨ˆÝ»wsìØ1¾úÕ¯âr¹¸rå W¯^•,a°w¥¨8Ö]îÂÃÎvÒ*‡¼Î 4440oÞ¼›J—ÀÊ•+#œ ñãÇsçw™™ISS»víŠê`È¥NäŽ&ñ@À9ñz½Ì˜1ƒÅ‹ßT£FŠÚÃ4vìØˆý÷ß?jçÏŸ§¥¥…ªªªáÒ¥Kœ:uŠ—_~Ù:þå/™3f——Gyy9ìÚµ MÓBsçÎõ˜w{{;×®]ã _ø‚§i999=ž÷•+WøÙÏ~fí†×ë )§¬¬Œ²²²¨éN'ßüæ7ùì³Ï8uêMMM¬_¿žýû÷óõ¯½W»÷¦_À! 8W:0M“ææfhiiaÆ ¼úê«!瑘˜@qq1¿ÿýïù·û7&OžÌ”)SztvÅÁ¤+eØê.¶ËŠÎÁœMÓp:!_F" 8bÁØíöÏ­Ggg'999<ôÐC=A)))èºò.XjjjHùééé<ðÀüüç?§¶¶Öz·©·¼oõ]+§Ó¢Ó‘#G8räHH9III½æ3räHFŽÉœ9s())á?þã?hll$77·ÇtŸ×7/ع 8˜cÆŒáÙgŸåĉ|üñÇüùÏfâĉ|õ«_KÌ])2D8üîhÒ…&–\:;'á$''ÓÚÚrìܹs1ç}úôéˆý¬¬¬¨²cÆŒáСC$''‡¼€LFFFï?q¥(++cóæÍ̘1›ÍSÞN§“3gÎ0aÂÀ׋óé§Ÿ2f̘nËÒ4-D§””l6[·öŒ…€}ήëQÀXß»rå ×®]³z±NŸ>¦idee‘’’‚ÓéäÒ¥KVo`40uêT^~ùeÚÚÚbrû™É]Aˆ{rss¹~ý:Û·o§¥¥…={öÜÔ{IMMMìØ±ƒææföìÙÃáÇ™?~TÙÂÂB’““ùãÿÈ©S§¸té'OžäÍ7߼闪 Ð4={öÄœ÷¼yóؾ};õõõ\¼x‘7ÒÞÞÞ¯ö}ýõ×y÷Ýwùä“O¸|ù²5D˜’’¸qãß;QçÏŸçâŋܸqÃêAŒµ×Íf³Q]]͹sç8uêo¾ù&Ö;]K–,á½÷Þãý÷ß§¹¹™óçÏS[[Ë®]»Øµk}ô/^äâÅ‹:tˆÔÔÔq®@z°„øèJžº›b;ùE‰Î±’ÍŠ+xï½÷ضmÓ¦McáÂ…ìÛ·/¦ô ,àìÙ³ÔÔÔ˜˜ÈòåËCÞß î…±Ûí<õÔS¼õÖ[¼òÊ+ttt0bÄòòòºíuê¶—CÓ˜;w.;vì`Μ91å½páBZ[[©®®F)ÅÌ™3™:uj¿:Y“&M¢¶¶–>ø€¶¶6’““;v,«W¯¶˜Y³fÑØØÈ‹/¾ˆÛí¶¦iˆµ+##ÃêujoogÊ”)!Ìš5 »ÝÎŽ;øë_ÿŠÝngÔ¨Q–#œÀŽ;hii±ÞK{ì±Ç¬MŠƒ%Äx§—!ÂáwG“!B±ìàѹ¢¢¢W™’’JJJBŽ¿´ÝS‡ƒ‡~¸ÛøçŸ>d?555&b9‡ÒÒÒ¹šzË[Ó4–/_ÎòåËoÙžÅÅÅÇ,?uêÔ^¿LII‰úÂ{¸íz*?Z3cÆŒn‡gÏžÍìÙ³ÍoF†AAÄÁn?2D8,» dˆP,+­An"b¼ÓËáð¬wA,;ô[C`)aàX¼xñMÏi6Ø‘,A¬H— ‚8X‚ ‚ ‚ÿ¹²²RÆAAúéÁAAKAap3`_þŸÿëÿ"§Ðì[‡Ã0­ø®„ÿi†ÉÒí~h&ÝÈ›EŤ{wútÅ›=¤ž1äÕc~=Ä›Ñò Ž7ñoÝéjFÄ›ÝéâÃìI3¢ìnu7#åCÊ »Ñ54mP|˜®ñaºbv£[7ò½Û•{УîÝçgGv¤çrz¨s_d¸ÝÍèõÞ¢„¡nÓ÷P¿˜`f/ºuc×€z'ZÇ”owùõ¤½¦ µkçÒ›îÐóï'ð;0"¯ÝêFOºt]·–TêrW¤Kèk䓱>µ¦˜Së€ÐTVVR__/†â§KAb¡ººšºº:”Rhš†Ë墨¨ˆ²²24Mú q°„‘9ûÔšbNA®CŠüü|***ðx<?~œ7¢ëzÈ~±âõzÑuŽÄÁŠ¥Bo¬J©ˆw“„>±´Ü„8j­*âýJ!þìªë:)))€oAà#GŽpôèQ<õõõ¬Y³Æ’ݽ{7»wï¶fh¯®®¦½½œœöîÝ‹Ífã™gžaíڵ̜9“ .pôèQ)++cîܹÝêqåʶlÙBCCJ)ÆÏ}÷ÝGZZ'Ožä­·Þâ³Ï>C×uFŽÉC=„Ëå’F#ÖÐ鵈/ç*žœ–Á¯g¸³- _âË¹ŠŸëÀ@ÛÕf³ÑÖÖ³üÇŒÃáà‰'ž9¾sçN-ZÄ’%K8qâ›6m"++‹‰'FäáõzY·nãÆãßøš¦±mÛ6Ö­[Çw¿û]”RüéOböìÙ¬Zµ ¯×Ë™3gäG(– NËÐu¶A®C‡††˜7o^ÌiX¹reÄÐàøñã¹óÎ;ÈÌ̤©©‰]»vEu°<ˆiš¬\¹Ò:¶råJ~üãÓØØHNNL™2…ôôt²²²¤ÂÄÁŠdˆPž²!²µÊáP°ë±cǨªªÂëõ0cÆ /^ÌŽ;bJ?jÔ¨¨ï];6bÿý÷ßšÇùóçiii¡ªª*ä¸ÇãáÒ¥KLš4‰¢¢"^zé%&NœÈĉ)((ÀétJƒkhõZÈáð}Ê–!B¡«µÊu`(Ø5//òòr4MÃétZ_ª(s²œ°`ìvûçÖ¡³³“œœz衈ûKàý°ŠŠ æÏŸÏ‰'8tè[·nåë_ÿz„#'ˆƒ%‚ ŽÝn·†Ý‚INN¦µµ5䨹sçbÎ÷ôéÓûÝ ë3†C‡‘œœŒÃáè6ÏÑ£G3zôhJKKùÕ¯~ÅG}$Ö0dHM þ £âj¶Éx{ÊäÖ”Þ+Áj­rÊvÍÍÍåúõëlß¾––öìÙÉ'bNßÔÔÄŽ;hnnfÏž=>|˜ùóçG•-,,$99™?þñœ:uŠK—.qòäIÞ|óM®^½Ê¥K—xë­·hjjâòåËœ8q‚––²³³å‡8 ‘!ÂAå´Èa_:Ûâd ¾Ö*סl×ììlV¬XÁ{ï½Ç¶mÛ˜6m .dß¾}1¥_°`gÏž¥¦¦†ÄÄD–/_ΤI“¢>¨Ûívžzê)Þzë-^yå:::1byyy8Ün7/^äÀ´µµ‘ššÊܹs)))‘âp|¸«¬¬_‰¬E¾¡_Y‹°OÖ"´Ê—µe-¸Z‹H{!kök×®eþüùÝöX ÂçA†¥W蟲ã«7SÆO‘2D(vq°úö¦*S4ÈM@dб« òá zÇ¥­)ï` rz!°”Ž ô² ¹ ‚ B3`=XÿÏ?ýo±¾ Ä*úß äm¡—ƒ´aØ!=X‚ ‚ }Ì€õ`UVVZÏ4 =èŸ Ý:¦ùÿéØüÿºãûÛw\Ãæu4+ÔQèVI … §ƒ_ßß¶ˆÐ·éþM³BÝ¿ùâͰx&:†?Î@Ç@ÃD³Ž™Ø0±a ãņà 5+ôÅë!ñ^븆 à Þ”#HýàÍÖþ­9°™ÆCÚpy-h‹ßS^¶0y=,¿hñÝž º4£k#°,é<þÐoø{‚âÃÿ6Â6wX~F”üÜÝä^¶7,?O”øð|¼Aǽa›»Ý}å*ËÕü­¸»¦^Eö^äbiráù韣éF“‰Ödzj~áçÜü”4/(ïoåíÁôž›Œ'¬Š½aMÀs“M&¼9„7çîš`OÍ5šnÑÎ-P¦ ?òßAz°a Ñ!NšŸ4UA,AAtTWWSWW‡R ¥#FŒ   €%K–`³…ÞÆ^{í5öïßÏÃ?Ì´iÓBâjjj¨¯¯gÍš5Ö~MM %%%”——[rçÎã¿øÏ>û,iii\¾|™µk×Zñº®ãr¹(..fÑ¢E!e´µµYå´¶¶’œœÌäÉ“Y¼x1.— €>ø€-[¶ðÜsÏY‹Vwvvò /0~üxž|òI+¿“'Oò»ßýŽgžy&êZŒ‚8X‚зȗìBœ4?éÅê òó󩨨ÀëõröìYÖ¯_RŠeË–Y2n·›ƒRZZÊþýû#¬¨7A›ÚÚZ.\HFF†u<|¢j¥O<ñÙÙÙx½^>ùä6lØ€ÓédæÌ™–sõË_þ›ÍÆ—¿üe²³³¹|ù2ï¼ó/¾ø"O?ý4éééäææâv»9{ö¬µô©S§HMMåÌ™3x<Ëqlll$--Mœ«8D†…è(ÑI„Áƒ®ë¤¤¤0bÄî¸ã&MšDCCCˆÌ¡C‡9r$¥¥¥œ:uŠ«W¯öšoVV¹¹¹¼ýöÛ=;ʦIRR©©©¸\.f̘ÁøñãùôÓO-™·ß~›ÖÖVV¯^ÍäÉ“q¹\L˜0Ç]×yã7¬2SSSill´Ò666rÇw––ÆéÓ§CŽçææJK–è¡“8[BœøúÒTûžóçÏóÉ'Ÿ ë¡kÖÖÖRXXˆÃá ??Ÿººº˜ò[¶l‡æìÙ³1ëpæÌ>ýôS«Ê4Mûì36lØ@{{;‰‰‰½æ{×]wñÓŸþ”ƒFýŠ0š†aàõz±ÙlðÑG±dÉRSS-9·ÛÍ|Òþ^¹ÆÆFÚÚÚ¸óÎ;­ã&Làøñãœ9s†9sæH…Ç)ò’»?ÝÁ:É;XÂbö“¬;hšÆž={¨­­%??ŸQ£F1räHk+((ÀápðÑGÅ”gjj* ,àý÷߬GÓ䯴¶¶rõêUŽ?Îûï¿O^^‡€»ï¾›ÔÔT^zé%Ž?Ε+Whlldݺu†Áý÷ß’gnn.Ÿ|ò çÏŸyÏj„ ìÛ·Ã0¤+Ž‘,!~î 2D(Äáó‡4Õ~êÐ4æÎË_ÿúW4M㡇Š´½RL:•ýû÷ÇÜ´páBöîÝk}íœ×K/½dýít:™2e K—.µd’““yúé§y÷Ýwyýõ×imm%))‰üü||ðÁˆw©òòòðx§úZ÷›m·’G,í%jPþM úÛ¿¡@Ó‚âü† —Sh*T.8$Z9Zh9=Å[yôRNÔ-Xwƒî7©›unüÃÂnpZ/•®ð+æ T„Ól tÐôè¡ —ï!."0yMï&MX~ª']º)GÓ¢ëwSç–wOºõ¤»fë&N YæF¤Kò(ÑUêYã ‚=XBü`ÆÑõ?žtÛ büØ©®®¦®®¥J)FŒAAAK–,Áf뺕EQJñÐC1}útùíok­-˜@zz:“&Mbþüù8Ψå×ÔÔPSSƒR*êBÐJ)žþyÖ¯_ÏBä”RLš4‰Ç€ý×åÊ•+¬ZµŠéÓ§‡äó³ŸýŒ .PQQAqqqˆ<ø½ÎÊÊ¢´´”‚‚‚uËÊÊâûßÿ>¿ùÍo8uê”ïÆo³ár¹(..¦¬¬,êù¾ôÒK|üñÇ|ë[ß"''Ã0øÑ~Ô£ –,YÂØ±cùýïÏøCBdþå_þ…ÒÒRæÎkåÀáp••Å¢E‹øâ¿hß¿?¯¾újD¹ üð‡?K‡kÑUl'ˆñ?ùùùTTTàõz9{ö,ëׯG)ŲeËBä¾ò•¯0iÒ¤cÁ‹=+¥ø»¿û;èèèàÓO?eÇŽìß¿Ÿ§žzŠ‘#GF”½páBJJJ¬ý_|‘’’fÍšádô v‚@¥.—‹ºººëôéÓ´¶¶F8%J)–.]ʬY³èèè`çÎüå/aĈŒ7€‘#G²zõê25M ÉcöìÙ,]ºÇÃÉ“'yõÕWIJJ 9/€+W®ÐÔÔļyóØ¿¿å`išÆ?üÃ?Xr}ôï½÷ßûÞ÷¬rMMMQÇzˆ¼¼<:::xÿý÷yå•WX³f ÙÙÙ–LrrrH9sÌÈ¡Óõè*õ< ~‡ã ]èºNJJ #FŒàŽ;î`Ò¤I444DÈ9RSSC¶`'pÃNMM%33“éÓ§óo|ƒ””^ýõ¨e'$$„ä§iZıp=ƒã‚<€ÂÂB¹zõªu¬¶¶–ÂÂÂÇ(¼üÌÌLV¬XÍfãØ±c!ÎTx™ÉÉÉ!yØívRRR¬Þ«Ñ£GGµ_mm-_üâ)))áàÁƒx<+.8ÿÀ"×ÁåÚíö›®×@}eff²téR¼^/ráç¼~£8X‚ ‚М?žO>ù]×û$?»ÝNII MMM\¿~½ßõOIIaòäÉÔÕÕàv»9xð 3gÎìýÆ­i躎×ë½åòO:Å… ¢Ú¯®®ŽÂÂB²²²ÈÈÈàðá÷¥N½^/û÷ïG)Õgõ:È¡ ‚;vŒªª* ÃÀãñ i+V¬ˆû¯ÿú¯ˆá£ï}ï{¸\®óÏÊÊàòåËŸ«w$ g0eeeï;³eË-ZÄ¡C‡ÈÈÈ`ôèÑ=æíñxصkäåå…8œáeR^^níïÝ»—ýû÷ãõzñz½ØívæÏŸ’¦¡¡·ÛÍäÉ“­<öïßOaaa¿ÕëŸÿüg”R¸ÝnLÓ$##ƒiÓ¦…Èܸq#âüòòòø›¿ùq°Aáó——Gyy9ìÚµ MÓ˜:uj„ÜòåËCœ Û—׃ ~)½/ô ~_()))BnÊ”)¼þúë466RWWñ>W0o½õï¼ó‡„„–-[F~~~ˆsøè£†” v¸-ZD[[[·neܸqŒ;6D¦¶¶–éÓ§[6˜>}:[¶láÒ¥K¤§§÷K½Þwß}äææréÒ%6mÚÄŠ+"†T“’’øö·¿r~·2)– ‚ „a·Û­›ü<ÀÏþsjkk#†ÕRRRÈÈȸéü/\¸@ZZZŸéÙš¦QXXHMM gΜá‘GéVváÂ…[ïb…£ëz¯e:ÒÓÓIOOçá‡æ§?ý)cÇŽeâĉ´µµQ__aìÝ»7Äñ¬­­eéÒ¥1À±ëèèˆxa¿½½=ÂyJMM%##ƒŒŒ V®\ÉÿøG¾÷½ï…¼C¦”ê7O,A†¦9Œ^tn ¥eeelÞ¼™3fD¼Ä~³¸ÝnöïßÏ„ "^ïOfΜɮ]»˜>}z„ãLrrò-9Ý‘À¼yóزe kÖ¬àÃ?äÿgï탢ºÒî߽ͧ4ÍW7[üBgÔ$DLˆÆˆºŠ›dkâî$f&™q*5»ÙLíîT½[ïòîNYkmM­»53135•ÌL2ÎL4¢¥æƒQ™@ÉN Ñdm4‚(I 6Ý÷¾Ð\º›n¡áùQ_aÇÔ IDAT·nß{ÎyÎsÏ9—ûÜsÎ}ŽÅbáñÇ÷ë)jll¤ººš¢¢¢õî èét:ýÜ-tttàr¹HII ™ö®»î"55•wß}—õëׇuK¸ù?2ÑUêYŸ„äææR^^NMM ÆùÞÞ^¾øâ‹!Å@oŠ®ë|ùå—¸ÝnnܸÓ餪ªŠëׯÛ‹4R<ÏüUU j¸Ùl6þùŸÿù¶‡»4M’'´·k€•+WòÎ;ïðñdzxñbjkkY¼x±Ÿ{‹ÅÂÿøGΟ?ï7,ŠØØX–-[Æ[o½…¢(Øl6º»»)//'33“Y³f ›>??Ÿýû÷SPPà§ÿh¯O ,aò÷" ŽF¥ž)üɇªªäååQYYÉ=÷ÜCdd$Š¢pèС!q×®]ËêÕ«ûíYEá'?ù‰ax%%%1oÞ<òóóÇä}þüy~üãûKII1œ~3Hn—+W®øå©ë:üë¿þë°†Ð׿þu***HLL¤­­-[¶ ‰Ãܹsyÿý÷Gd`lÚ´‰S§NñöÛoÓÝÝÙlfÞ¼yC†ƒõˆ-X°‹Å©S§(..ú‡/¯OQþéŸþéŽö8Žê½©´´tB<°ÈZ„á³ápå8ÙÖ"¼™n²aèµÕPkÊZ„ã¿¡±V ¬E(Sæ@Š@É˵è*õñññ#ÖAUUøú׿Naa!ªªRWWÇ›o¾ÉøÃ!iKKKùÆ7¾ÁÂ… éêêbÏž=FXll,©©©<øàƒdffç+**¨¨¨ð»æ… òàƒeÈÙ¹s'3gÎ’g]]eee(Šâ·póÍ–ÏÄÀ„°g¢–ŸÓÑ¥K»–›““CII §ÓÉÁƒQ…uëÖq¶mÛ†Ýn÷Kãgp}ÿûß'**Š7nÐÚÚJee%ï¿ÿ>O=õ©©©#ÒÁívóé§ŸrôèQL&“±ÆáHQ…'žx›ÍÆõë×yçwøíoËßÿýßgÄKMMåÉ'ŸÄãñÐÜÜLYYn·›Í›7r†#&&†ïÿû~–¢È½§!Báæÿ DWA˜¼-wš<0M&qqqFOŽÝn§±±Ñ/Ntt4f³Ùoèá`ÆŒ˜ÍfRRRX²d ßúÖ·ˆ‹‹ãÈ‘##Ö!!!•+W2wî\>ùä“Ñ›¨ºNll,f³™ÔÔTÖ¬YÃ7hiiñ@«ªq͹¹¹|ík_óËÏ×p E\\œ_yøp‚XÂ$x_]§Ž1(½WS¬åêú4i¹ƒ´µµÑÔÔ„ÉdºmY‘‘‘¬\¹’ææf¾üòËQ¥ˆˆÀãñÜVþ}}}ÔÖÖ¢(ÊM¯g,òî2D(øH•!BAZîÈhhh`×®]hš†ÛíFUU6mÚäçÀC†Àž}öY†•mµZèêêqOcc#¬Zµê–®ç—¿ü%Š¢Ð×ׇ®ëÌš5‹¹s熌ït:9{ö,ÙÙÙ#Σ··—]»vùËÌÌäoÿöo¥‹%LdˆP&qË&C„ÙÙÙlÞ¼—ËEuu5ªª²hÑ"¿86lb€Ülò: µ)ŠÂ¥K—xõÕW°¿ú«¿béÒ¥~FÞ@/ÒÒ¥Kyànéz{ì1¬V+ííí”——³uëVTÕP©­­Í0*5McþüùlܸqÄyDGG³sçN¿¡ÄÈÈH¹ÝÄÀ¦÷ûêÔ×U†…1i¹º~‡¬‰i?‘‘‘$%%°uëV^xáÞÿ}V¬Xaĉ‹‹#99yÔ²¯\¹@bb"QQQìܹÓ3›ÍCŒùäbbb(,,$//ÏïîîæØ±c\¼xEQ˜7oÅÅŘÍfz{{Ù½{7Ï<ó èºÎîÝ»±Z­<ýôÓ|ðÁ?~œçŸ^*Y ,AA˜8ÚÚÚhjj"11Ñ8ͶmÛ0›Í´··søða¢££¹ï¾ûBʹxñ"ñññìØ±ƒk×®ñÚk¯‘žžÎŠ+Œ8UUU¬Y³†¢¢"Ο?Ï›o¾‰ÕjeîܹèºÎ¾}ûˆŽŽæ©§žBÓ4Ž=ÊþýûÙ±c111¤§§ãp8ÈÈÈ ­­ EQ¸|ù2.—‹¨¨(.]ºDVV–TªX‚ ‚pçihh`×®]hš†ÛíFUU6mÚd„¯Y³Æø˜˜HAAçÎÖÀŠeãÆ(Š‚Õjeþüù\¸pÁÏÀš3gŽ!#%%…ææfª««™;w..\ ½½çž{‹ÅÀ¶mÛøéOŠÓé$##ƒÌÌL8ìv;4551oÞ<«W¯– KAî<ÙÙÙlÞ¼—ËEuu5ªª²hÑ"#üܹsœ9s†ÎÎN\.š¦=¬ÌÔÔTep–ï@ï—/³gÏr|æÌ:::HHH0Œ+›ÍFLL W®\!##ƒ¬¬,jkkÑuK—.a·Û1›Í8ÒÒÒ¸víšô`MQÂÂMƒLA˜ÞDFF’””DZZ[·n¥¥¥…ÚÚZš››yýõ×™?>Û·ogçÎâñx†ªþ@EQÐõ±}âdffâr¹p:Æp`VV‡‡ÃÅb!99Y*X ,A‚#n¦B-ÉE„I( ………œ8q·ÛMss3‰‰‰’‘‘Arr2]]]c’WKKËc«Õ €Õj¥»»›žž#¼½½ÞÞ^l6111¤¦¦RSSƒÉdÂjµ’™™Ikk+ dffJ…Š%BhÄMÃT¨%¹ˆð!77EQ¨©©!%%…îînÎ;ǵk×8}ú4õõõc’Oss3•••\½z•šš>þøcòóó°Ûí¤¦¦ràÀZ[[iii¡¬¬Œììl222 YYYœ={Ö0¦bcc±Ùl|ôÑG2<(– ·×­ ÝáPKraôðRUòòò¨¬¬dîܹäççsìØ1^|ñEZZZ¸ÿþûÇ$Ÿ{ï½§ÓÉÞ½{9uê6lÀn·á?þ8±±±¼ôÒKüæ7¿!99™G}ÔOFVVº®“=äœXSøÿMii鄼f±çPë¦ÊbÏÈbϲØ3²Ø³,ö,‹= ‚ô` ‚ ‚ ˆ%‚ ‚ ˆ%‚ ‚ – ‚ ÂTbÂ<¹G–þ»”þ``2©KŠblP€Hï&HSä¯Ïª4UA˜È[PAAC&¬«´´ÔèPQQQPQ1ùüE`2ΩÞ?Þ?“ñÛäÝ"ˆ4$¨ÞŠ÷·B*&Tï9Õû;»™Œ=~¿ƒ}>ø¸Žêý^E't"½›jÄ4LÞ8&4#}¿ 7ÌCL¸‰ônýç4T#7 }Þ½ÅÿËíw º_ÉïÀ-Ê»Ez÷jÀéS4¿¿Éð‘ÉP÷Á~ûÆ ÌgØkò~W¯j`òx¿±w}>[°oêûðwãìø¾¿ãÊ üÎ>0Mßd{Bä5ÖE°oüÃ…ƒgHkd@µ‡j"¡Ü1DT³é&2|Óóå k ›†ášã&£Ú¥ÁÀï¾T{°&£ ¨—Oó tÃà?0¼/„ÌPMÄ7nàõõlwÐiO+pÒg»àó\O***¨¯¯gçÎ#N³gÏòóó £áÈË/¿ÌÌ™3Ù°aûΑä)– ‚ L eeeÔÕÕ¡( Š¢`±XÈÍÍ¥¨¨ˆˆˆ‘?Ê Xµj•¨ – ‚ äääPRR‚ÇãÁétrðàAEaݺu#–5%Êâå—_fÙ²e,[¶L†X‚ ‚pë˜L&âââ°X,Øívý ¬òòrêëëéééÁl6³téRxàTµÊqàaYY½½½Ì™3‡ªª*<K–,¡¸¸ØHpãÆ öïßÏ'Ÿ|BLL ………äååáÝÝÝ;vŒ‹/¢( óæÍ£¸¸³ÙLoo/»wïæ™gž!##]×Ù½{7V«•§Ÿ~€>ø€ãÇóüóÏ[ùišÆ±cÇøàƒ0™L¬\¹’|0düêêjjkkéìì$66– ðÐCù©MMMœ8q‚Ï>û “ÉÄìÙ³yôÑG‰‰‰"¯¡¡°yóf–.]*– ´E™¤²ÚÚÚhjj"11Ñï|tt4Û¶mÃl6ÓÞÞÎáÇ‰ŽŽæ¾ûî )ëâÅ‹ÄÇdzcÇ®]»Æk¯½Fzz:+V¬0âTUU±fÍŠŠŠ8þIeMSصkš¦áv»QUÕxذfÍãwbb"œ;wnX+66–7¢( V«•ùóçsáÂ?ÃcΜ9†Œ””š››©®®fîܹ\¸pöövž{î9, Û¶mã§?ý)N§“ŒŒ 233q8àp8°ÛítttÐÔÔļyóp8¬^½z\Ë/!!Á˜pž’’B[[ÕÕÕ! ,ßÉ‰<øàƒ9rÄ(óªª*222ظq£Ïjµ‘SSSÉ'ؾ};™™™Ó¦½Š%‚ „ÙÙÙlÞ¼—ËEuu5ªª²hÑ"¿8çÎãÌ™3tvvâr¹Ð4èèèa妦¦¢(ƒ]Œ½_¾Ìž={Èñ™3gèèè !!Á0®l6111\¹r…ŒŒ ²²²¨­­E×u.]º„ÝnÇl6ãp8HKKãÚµkÃö`:uŠS§NÇ}}}´´´pìØ1ãܳÏ>KBBBHÁ®¡ºº]×ý®€ÆÆFÞ}÷]:::¸q㚦áñxèëë#22’Ë—/“››;lÙ~üñÇ|ùå—|ûÛß&##cZµW1°AB!C„“ŠÈÈH’’’غu+/¼ðµµµ,_¾€ææf^ýuŠŠŠ°ÛíÄÄÄpöìYª««‡•ë;× ú‡Æt}l»333q¹\8N.]ºÄÚµk1›Í¼û¥¥a±XHNN™~åÊ•~ÆÌX¼x±Ÿ?fúvuu±oß>î¹çÖ®]Kll,MMM>|ÇCdd䈾ÞLOO§µµ•÷ßÚXâhT!2D8ym_E¡°°ãÇãv» +11‘ÂÂB222HNN¦««kLòkiir<0fµZéî§Çooo§··›Í@LL ©©©ÔÔÔ`2™°Z­dffÒÚÚJCCÃM‡ÎbccINN6¶ÈÈHâââüΊ|öÙgC®!999hï•ÓéD×u~øafÏžMJJŠßõ¤¥¥qáÂ…aóLJJâÉ'Ÿä“O>ñëmKA&)¹¹¹¨ªJMM Ð?¯¨»»›sçÎqíÚ5NŸ>M}}ý˜äÕÜÜLee%W¯^¥¦¦†?þؘ£d·ÛIMMåÀ´¶¶ÒÒÒBYYÙÙÙ~½6YYYœ={Ö0¦bcc±Ùl|ôÑGã>Áú¿t|ë­·èèèàìÙ³ÔÔÔ„t*šœœŒ¦iœ>}šÎÎN>øàþüç?ûÅ),,ÄétrôèQÚÚÚ¸rå ï½÷ׯ_÷‹—’’“O>É_þòÞ|óÍiÓ>eˆP!d7É$•%ô÷¨*yyyTVVrÏ=÷°`Áòóó9v쇜œî¿ÿ~***n;¯{ï½§ÓIEE111lذ»Ýn„?þøã¼ñƼôÒK(ŠBNNÅÅÅ~2²²²8sæ ÙÙÙ~çÚÚÚîˆõõ¯·ÛÍ/~ñ TU%??Ÿ»ï¾;hÜ™3gòðÃSYYÉñãÇÉÌÌdݺu¢(üÃ?ü‰‰‰´µµQ^^NSS‘‘‘Øív6lØÀŒ3ÄÀA˜¶HÖ¤¥­­¦¦&‡ô¬lÛ¶ ³ÙL{{;‡&::šûî»/¤¬‹/ÏŽ;¸ví¯½öééé¬X±ÂˆSUUÅš5k(**2 «ÕÊܹsÑu}ûöÍSO=…¦i=z”ýû÷³cÇbbbHOOÇáp‘‘A[[›aè¸\.¢¢¢¸téÒ¸-øÊËËyíµ×xòÉ'§|•¯A„° ¡¡]»vñ£ýˆ^xëׯ1œÖ¬YÃìÙ³ILLdþüùðÑG +766–7bµZ™?>óçÏçÂ… CŒ”ûî»””V­ZÅâÅ‹©®®àÂ… ´··óÈ#žžÎ¬Y³Ø¶m‡§Ó @ff¦aÐ8ìv;V«•¦¦&ãÜxX111¤¥¥ù域Ÿox===\»vÌÌL#¦ilÞ¼™ôôtÒÓÓÉËËR&¾òM&‘‘‘ÄÅÅa6›Q…ššÒÓÓyðÁIIIaæÌ™lÙ²…‹/rõêÕ)ß^¥KA ²³³Ù¼y3.—‹êêjTUeÑ¢E~qÎ;Ç™3gèììÄår¡iÚMç ¥¦¦¢(ƒc¸½_¾Ìž={Èñ™3gèèè !!‹Åb„Ûl6bbb¸rå deeQ[[‹®ë\ºt »ÝŽÙlÆáp––Ƶk׆5°N:Å©S§Œã¾¾>ZZZ8vì˜qîÙgŸ%!!!hú¬¬,455±nÝ:>úè#šššøê«¯°X,$''ñ###IJJò+“/¿ürTõuùòe.^¼È®]»üÎ+ŠBgg')))b` ‚ ÂDãûÐߺu+/¼ðµµµ,_¾€ææf^ýuŠŠŠ°ÛíÄÄÄpöìY£§)¾s­ ß/䯂ÌÌL\.N§“K—.±víZÌf3ï¾û.iiiC œ@V®\Inn®q|àÀ/^ìg`ÆÇLJL?`à]¾|“É„Õj5Œ®¯¾úʯ÷*T™Œ—ËÅ‚ x衇†”çpºŠ%‚ „¢(òÖ[o±téR"""hnn&11Ño.QWWטä×ÒÒ2äØjµ`µZéî§ÇèÅjoo§··›Íô£¥¦¦RSSc8qqq¼öÚk444 1p‰%66ÖÏØŒ‹‹Ö(óeΜ9ܸqƒêêj#¯¬¬,Þ}÷]z{{¹÷Þ{o«|L&Ó#*==¿üå/$$$ 1ئ2KAKrssQU•ššRRRèîîæÜ¹s\»vÓ§OS__?&y577SYYÉÕ«W©©©áã?6ÚívRSS9pà­­­´´´PVVFvv6†Œ¬¬,Ξ=k8±±±Øl6>úè£q›åk ¥¥¥qöìY#¯ÌÌLZ[[¹zõêm矘˜HKK ]]]\¿~€¼¼<¾úê+öïßÏgŸ}Ƶk×8þ,X@~~>ÇŽãÅ_¤¥¥…ûï¿Lòº÷Þ{q:ìÝ»—S§N±aÃìv»þøãËK/½Äo~ó’““yôÑGýddee¡ë:ÙÙÙCη,¯Ïl6ßö|¨‚‚TUå§?ý)ÿõ_ÿEWWñññ|ûÛßF×u^yå^xáÞzë-bccoiÈ1ìþ}LÔbϲTŽ,•#KåÈR9²T²TÎ^*G¤KAz°AKìœ¯Æ IDATaŠ!ŽFAKAA ,AA1°AA1°AAî8æ¦AAaª"=X‚ ‚ cÌ„­E8àhÔ†ù>,c–‡‚B<ñ˜1OÛŽô¹¢é¹šNOG=WÓéý2qLUwyÿnp®1•mM¶òkXW~ ÛʯwWº8¦Òƒ%‚ Ly***Ø»wï¨ÒìÙ³‡Ó§OKá ·D„ ‚0™)++£®®EQP‹ÅBnn.EEEDDŒì1VPPÀªU«&ýµ¾óÎ;|úé§\¾|“ÉÄøÃ!q>ûì3þøÇ?ÒÚÚ À¬Y³x衇˜9s¦41°AaääääPRR‚ÇãÁétrðàAEaݺu#Jשi¹¹¹Ìž=›ÚÚÚ!á.—‹W^y…… ²yóf4MãäÉ“¼òÊ+<ÿüó¨ª L‰%‚ #Äd2×?Öb±`·Ûill4 ¬òòrêëëéééÁl6³téRxàÃਨ¨ ¾¾ž;wý½b½½½Ì™3‡ªª*<K–,¡¸¸ØÏH¹qãû÷ïç“O>!&&†ÂÂBòòòŒðîînŽ;ÆÅ‹Q…yóæQ\\ŒÙl¦··—Ý»wóÌ3Ï‘‘®ëìÞ½«ÕÊÓO? À|ÀñãÇyþùçxਫ« ZôööRTT„ÅÒ?gùþûïgïÞ½tuu‘œœ,e’ ¦® ‚V´µµÑÔÔ„É4øqNtt4Û¶mãÙgŸ¥¸¸˜÷ߟêêêaå\¼x‘ÎÎNvìØÁ¶mÛ¨««bØTUU‘žžÎÎ;Y½z5o¾ù&.\@×uöíÛGoo/O=õO<ñìß¿€˜˜ÒÓÓq8†ÞŠ¢pùòe\®þ.]ºDVVÖˆ¯=%%…ØØXÞÿ}<}}}¼ÿþûØl6¥qL"¤KA˜ô444°k×.4MÃív£ª*›6m2Â׬YcüNLL¤  €sçÎqß}÷…”ËÆQ«ÕÊüùó¹pá+V¬0âÌ™3Ç‘’’Bss3ÕÕÕÌ;— .ÐÞÞÎsÏ=gô&mÛ¶Ÿþô§8N222ÈÌÌÄápPPP€ÃáÀn·ÓÑÑASSóæÍÃáp°zõê—Ctt4;vìàw¿ûúÓŸ ½¾ùÍoÊð X‚ ‚0:²³³Ù¼y3.—‹êêjTUeÑ¢EFø¹sç8sæ ¸\.4M#::zX™©©©(>~]Ìf3ííí~qfÏž=äøÌ™3@ÿp]BB‚a\Øl6bbb¸rå deeQ[[‹®ë\ºt »ÝŽÙlÆáp––Ƶk×FÕƒÕ××Ç¡C‡˜3g>ú(š¦QUUÅ«¯¾Êw¾óOúÆ1wA„IOdd$III¤¥¥±uëVZZZŒIàÍÍͼþúëÌŸ?ŸíÛ·³sçN ñx†w˜Øã£( º>¶‹›dffâr¹p:Æp`VV‡‡ÃÅbÕ¼©³gÏÒÝÝMII Ìž=›Gy„ÎÎNêë륡ˆ%‚ ·†¢(râÄ Ün7ÍÍÍ$&&RXXHFFÉÉÉtuuI^---CŽ­V+V«•îînzzzŒðöövz{{±Ùl@ÿ<¬ÔÔTjjj0™LX­V233imm¥¡¡ÌÌÌQéÓ××ç×ë6žÆ¡ – ‚0ÍÈÍÍEQjjjHII¡»»›sçÎqíÚ5NŸ>=f½9ÍÍÍTVVrõêUjjjøøãÉÏÏÀn·“ššÊhmm¥¥¥…²²2²³³ÉÈÈ0ddeeqöìYØŠÅf³ñÑG ìîîæòåËtuu¡ë:—/_ö›o·Ûùꫯ8zô(W®\¡½½C‡¡ª*ÙÙÙÒ0&2X+‚ „_’——Gee%Ï=÷ùùù;v ÇCNN÷ß?·Ï½÷Þ‹Ó餢¢‚˜˜6lØ€Ýn7ÂüqÞxã ^zé%E!''‡ââb?YYYœ9sÆÏÊÊÊ¢­­mˆuòäI>øàãøÅ_àÉ'Ÿ$++ «ÕÊöíÛ©¨¨à—¿ü%Š¢žžÎ7¿ùMÌf³4ŒI„RZZ:!}вáMÿ} kŽ Y‹08²á‘µC"k Âí=ÅAA1°AAÄÀAAKAA¸5&l’» ‚ ÂTEz°AAƘ óƒÕZú’ñ[ñZz  *ý›IQPUQŒóŠ÷Øä®ú¤ñ—AHY* Êó+ÞøŠ¢ ˜TUA ¶)ƒ¿Q‡áÃä Ù|ò÷“¡((ª jÿ¾?\ñÛœ÷ߤQPßtƒqðÍCñ†›Tÿ8Š¿î桨àÝÄGñÙº¨(Jðpÿô¡å…WeûÆa0­® +€‚® þÔG1âèùú†I¯ú^ÛÐø: : ëýþ%ôM×½û¡ÇšÏ1F8Aãã#O Ï' ?M×ûóòž88§yÏi>qtïqàÞˆ‡O|ŸüûÃ}ÃüuÕ‚Èóhi÷a­Ó}å¤Ñt<ºŽ¦ù§”í/g M0]´øÞºMÇ,}šå,1ýô.++£®®Î˜¶`±XÈÍÍ¥¨¨ˆˆˆÐ1ßE‘»ººØ³gql2™HHH`Ù²e¬Y³FÚ°05 ¬éÖK­‡Ùuë·£«>¾(^×ÇÊÈÒ¹ãÖaXu@è·v½úí×»^E]w¨>–BŸÔÕ8QäääPRR‚ÇãÁétrðàAEaݺu¡// Eá‰'žÀf³áñxhjjâСCÄÇdz|ùr±„©g` ‚ Âp˜L&ââúדµX,Øív «¥¥…#GŽÐÑÑAjj*………~=XWll¬±(òÒ¥K©­­¥µµÕ0°^~ùefΜɆ Œt¿ûÝ‰¡¤¤D*BKA˜š´µµÑÔÔDbbÿâÖ.—‹}ûöa·Ûyä‘Gèììä7Þ¸©œÏ>ûŒÖÖV–-[&…*ˆ%‚ L?صkš¦áv»QU•M›6ðᇢë:[¶l!""›ÍFOOG"ç—¿ü%Š¢àñxÐ4»ï¾›¯}íkRÀ‚X‚ Âô#;;›Í›7ãr¹¨®®FUU-Z@GGiii~ÞgÏžTÎc=†ÕjEÓ4ÚÛÛ9vì111ÃÎå„[aR¸io‘„i‰.×;i‹K×¥QL2"##IJJ"--­[·ÒÒÒBmmí¨åX,’““±Z­,^¼˜üü|ª««q»ÝCæmx<©!< ¬é†¸i?ÝÄÑèÔÏY*BôV…ÂÂBŽ?ŽÛíÆf³ÑÖÖfIÐ?é=Xº`ç4M3Œ¨3fðùçŸá=]‚ V˜¼È‹£ÑñÑMNýœ¥"Do€ÜÜ\TU¥¦¦†¥K—pøða®\¹BCCUUUC/W×¹~ý:_|ñ===|úé§œ9s†ììl¢££þ¡ÈO?ý”††:::8zô(½½½ÒÆ…Q#s°&è¥Qz°ÆG7q4:õs–нTU%//ÊÊJî¹ç¶oßΑ#GxñűÙl<ôÐCüáøÿ ð›ßüÆøÏüùóyðÁ8Ë—/§­­²²2TU%??ŸììliãÂèo¯ÒÒÒ ¹½|{V}ŒYìY{ž6‹= =O“Åžãr±g]÷O?®‹=„ßñÅž}–Åž!l‘!BAA1°AAÄÀAAKAA¸u&ì+ÂôÒ§n+½x¼›p›©Ó?kzÌJÍâýŽŠ|Äj '¥Mᤰ Œ;Òƒ%‚ ‚0ÆLØË¯›…AW â¦AÜ4Œ½›†›èb¸MPCì½-TOQÝ5¤Ð-˜›†· ºgßÏôõ!ÇÛ@øpñF"s´ùnš¦8¦i£¾¾ÀtÁŽƒ…iš2žoœ›… ÷fñ|uøL¯`iBÅ •O°´£¹Îєí”ëpi¾óïÈSY¬±Dݦ%Òð'oÕ(R9áNWW¥¥¥\¾|Y C˜^=X‚ ‚0ÊÊʨ««3zÕ- ¹¹¹1*Cùã?æ½÷Þ£µµ·ÛMbb"wÝuyyy¤§§Ka SËÀÒåe^˜ŽHß¼U£Ë2@“œœJJJðx<8N<ˆ¢(¬[·nÄõX^^Nuu5ùùù‘Àõë×ùôÓO9~ü8÷w'-L-kº!kŽŸn2²#£¸%lŒI“ÉD\\‹»ÝNcc£a`µ´´päÈ:::HMM¥°°Ð¯«¹¹™ÊÊJ6nÜH^^žq>!!Á¯çª««‹ÿùŸÿá™gž!##Ã8_]]ÍéÓ§ùÇüGi8BøXÓ홨‡ÙußVGË8÷ÒŠ×õ02²ÄÃc¢ïí0½Æ¶¶6šššHLLÀår±oß>ìv;<ò¼ñÆ~iÎ;Gtt4+W®Vvbb"sçÎ¥®®ÎÏÀª««cùòårsáe`ÉH‰0-‘†/†‡0bصkš¦áv»QU•M›6ðᇢë:[¶l!""›ÍFOOG5Ò_½z•¤¤$TuðÛ®êêjNž“·j¦É¬pî©ËÍÍEUUjjjXºt)‡æÊ•+444PUUåÿ®»îâÞ{ïå­·Þâ­·Þ¢©©‰®®.¿ž0ßò°ÙlÌž=›òòr–.]:¬¿-A†|E8ÏUùŠpìu“¯…±2<ä+ÂIÞ; ªäååQYYÉ=÷ÜÃöíÛ9rä/¾ø"6›‡zˆ?üá~iÖ¯_ϬY³ø¿ÿû?jkkéëëÃl6“™™ÉÓO?Mtt´_ü+VÐÒÒ"ÃBøX‚ ‚Š’’’ çW¯^ÍêÕ«þIí;wîô ÿ·û·!irssÉÍÍQ¾===¤¦¦ú¹k„¿LŠ·¨éövŒ Ž—n2D(ŒIÕÈá´ÆårÑÖÖFMM «V­’n éÁšÀçª ޽na5D(bHNJŽ;ƹsçX¸p¡  ám`ÉóP˜–Hß¼U3Mæ` Á))) 9,)#EÜ4‚ ‚ Œ1Öƒ•^úÔm¥×wn³ u@KR3‚à÷&ë]žÅd2IaÂt¹ï¥AAÆ– ëÁú·ùà3Éò¦{†GrN¿UY#Üë¾yœ×o–.@Ï‘ä2ž7??™„÷‘´l|×ðÑ 8ö ÷Í+ȱî7D¹6|ÝtMˆ§IëÝûÆÕBèênÃÐó~é}ÊQó‘R·þ½®éƒU¥/G]] h>>yVñ€Ì€cßð@9~{ÝÿØ«Ûp-H\ãx Íà^ <¯ ƒ|Mó?ö ×BËñ݃¿Mlóz0]¼<Ø t¿ŽÝ¡¿õ!MQ ‘>ئ…ׂ¤Õ†è_­»ïqhÝõºBßtÁóÖý:ÅC…ÞÒ«JSå©,H– ‚0ÖÈ—‚ – ‚ LRººº(--åòåËR„ ~°A& â"eeeÔÕÕ¡( Š¢`±XÈÍÍ¥¨¨hØ5}©vuu±gÏã866–ŒŒ Ö­[Gzzº² Ö°(ŠÏœ¬þÎv=Ìt“‚–ÁI8–¦‚â3›G‹:”»jxrrr())Áãñàt:9x𠊢°nÝºÐæjÀÿVEQxâ‰'°ÙlôôôðÆoðꫯòýïÈ:„‚p»L­!€›ICå-{úu<„g 60Öu¨f– “ÉD\\‹…… b·Ûill4Â[ZZØ»w/?úÑøùÏNkk륀t]'66³ÙLFFëׯç‹/¾ ¥¥€ÒÒRêëëýÒüçþ'uuuR¨‘!BAyy +ÚÚÚhjj"11è_;pß¾}Øívyä:;;yã7nþô/z<â·OkxdˆðN)-‚1,"œÖÕ/wÕihh`×®]hš†ÛíFUU6mÚÀ‡~ˆ®ëlÙ²…ˆˆcðèÑ£!å}õÕWüéO"::šY³fI b` ÿò'C„ò–~E"C„Óºúdˆ™Šììl6oÞŒË墺ºUUY´h¤¥¥ùMxŸ={vP9¿üå/Q—ËErr2=öqqqRÀ‚X‚ Âô#22’¤¤$¶nÝÊ /¼@mm-Ë—/•œÇ{ ›ÍFll,111~as¶@†…[gjMr¸9”0Ô9L Zîœ1,’ðlÒƺûeHïÕÈþm*rüøqÜn76›¶¶6Ün·g`âz`:‹ÅBRRÒã `ÆŒ|þùçÆñÕ«Wéëë“ÄÀ’!Â;¦´Ü9cX$2D8­«?@†®#%77UU©©©aéÒ¥>|˜+W®ÐÐÐ@UUU·Ã×Tvv6555´¶¶òÙgŸqäÈY [KA˜F/U%//ÊÊJEaûöí´··óâ‹/ròäIzè¡!i”›Œ<üðÃ$$$ðÒK/ñúë¯sß}÷)…-ÜJiié„¼ŠŽËbÏÇ>aú­Èº“‹=Ñ9hؤZì™AÙ²Øs@yŒ~±çÁÃg±gß…e±çàá£]ì9X˜,ö,Òƒ59!Â;¥´Ü9cX$2D8­«_î*AKA_d– ˆ%‚ ‚ eÂü`ý»,¥/„÷k‰|\¦(ûðÒ\‘æ'aõ¨AAÄÀAA˜œLØaNéoß®ITÕ¸áÃ…©Áã'3T˜¢*àÝÙo˜24jÿy%døPŠBÆÀ±!Ë›NíOƒO8¾{ŸMñ;ÆHnF¸qÝ>á¦Y Fø€®(¾á7©LUE ^‘F¥(·Ö(BV¦‚¢ªx/p ‚½Ç›_%Hœ ar•›È@Aï¯ÀþßÆÞ»é  ¢ëýaº®öïQ½áƒññÆ H§ ÊÔûÓ¡û†«>é½r½zhºÒï:!˜§ ¯ mÀ½‚÷xÐ ÅÐpÍ×ÃEøýùè~òóôÏGóº_ê‚~7 ýyê>.ðw ¡ëèÞx.%4?·rôðÆÇ×D€K Mp=1¨‡¦yÃ8þñ\vèšà¶B7\KºkCÝch~i~cäç[¡ºOÃõ‡¦û¹Ø ·"ç|˜7ß|“mÛ¶I¥Ã2e†ÑwÜt»-]%L V†jƱULž»M™DÍE‘{d8L&qqqX,.\ˆÝn§±±ÑoiiaïÞ½üèG?âç?ÿ9­­­~k®]»“ÉDyy9/^¤¶¶–mÛ¶ » sTTf³™øøx²²²X¶l­­­FxEE{÷îõKsúôiöìÙ#ÿ6ÄÀ’—cÑwxÝnK×;½F5¤„s‹ÕÃèzõQzÖ˜­•£È=2ÚÚÚhjj2 #—Ëž}ûHMMå»ßý.<ðo¿ý¶_šˆˆ¶mÛÆŸÿügêëë9tè………¤§§8ßžž>ùäfÏž-ÖƒpSdˆP&ò¹/C„“TU"œl444°k×.4MÃív£ª*›6màÃ?D×u¶lÙBDD6›žžŽ=ê'###ƒÂÂB~ÿûß¿oÆÿøGNœ8aä;{öì óµaÊX2D8~ºÉ¡Ð_ú0Çá¤û8\ÆXȹSE:fùÜÙ6ÍæÍ›q¹\TWW£ª*‹- ££ƒ´´4¿ ï¡z™Ö¬YCEE«W¯FUo>ˆSPPÀ²eËèîîæøñã¼úê«|ë[ßò‚„@dˆPô½©n2D(o2Dxš‹ Kdd$III¤¥¥±uëVZZZ¨­­ýCÏkTÔ8š1cÉÉÉ$''“͆ hnnæâÅ‹!åx<±.ñƒ%‚ „Š¢PXXÈñãÇq»ÝØl6ÚÚÚp»ÝFœ–––qË0òš1c_|ñ…_œÑøÞÄÀšü7œè;nºÉádïI“V¡O»M ¥ª~盋r‡ iŠ £çææ¢ª*555,]º€Ã‡såʨªª“|\._|ñŸþ9---”——Ç]wÝô»€øòË/y÷Ýw¹ví555œ?^¬ aêÌÁ ·ù“Yß@ÝnKW]¿³F֘쨡é8D¨OÝo¤ ŒÎÈÓ!B}Š#œÞTU%//ÊÊJî¹ç¶oßΑ#GxñűÙl<ôÐCüáñÞ7ò›ïäÉ“œûŒ_ýêWÆñ£>ÊüùóûKCQX»v-™™™¬^½šßþö·¸Ýîaç‰ b`…ÅÛ± Žn2D8¾"çö ÆÕ85 ;[°ÙÙÙlÞ¼—ËEuu5ªª²hÑ":::HKKó3dfÏžTΚ5k¨¨¨`õêÕ¨êèqfΜÉÎ;øßÿý_4͉´´4ã·ÙlàË/¿$!!AÚÚ4D†A„IOdd$III¤¥¥±uëVZZZ¨­­ýCÏkTùÎÏ Æ@ÏUGG‡qÎd2‘œœLrrò°²}åL´ÄÀ [dˆpüt“!Âñí=P¦×åÞùö NÇ©M\Á*ŠBaa!ÇÇívc³ÙhkkÃívqZZZn+»ÝNLL •••ÒN„ém`éÓ@_}‚tÓébÂmÌŒé5D8!7št ŒSšØ‚ÍÍÍEUUjjjXºt)‡æÊ•+444PUUu[ò£¢¢Ø²e ¼úꫜ?žÎÎNÚÚÚx÷ÝwQůÇ*XO•ô^‰%‚ áõðRUòòò¨¬¬DQ¶oßN{{;/¾ø"'Ožä¡‡ mì+#3÷-ZÄ·¿ým¢¢¢(++ã'?ù ¿úÕ¯p8~ÜCÉi>ÂÔD)--{¬{V}ÖÕ ‡ÅžŸG±Ø³â] y¼{V 9ÞÅœàV{Yèã´ØsÐ8“{±çÁŽ–Ñ/ö<4¾,öì·Ø³Ï¢Í²Øsˆ…™µ[]칿Ðd±gA˜â=X2D8~ºÉáäUY FP@RHãÔ€¤`aZX‚v(r¹‚ b` ‚ ‚ #bÂ~Zº]JÿN¢y·)‹ŒM”€}ø¾yÊÛçMªÙ$Å 7û?"‚ ‚ ˆ%‚ ‚0y™°!Âû§çGl~{=àŒáŸáÂo&cؽ>L^¾òƒê®kþòt_=4Ÿc=øoï¦û×òðM«É'”ìátÓƒÈö ×49¡Ò sMCÓ–ÁÓÆ ©äZ†Õ•›”#“=¤ìo_×ýøµ'ŸÏÚýÚ„?Àög”CÀçóe«ûú*” ËÐüýƒêæWLzÀåêÞ0=D±zÏáz@“ÐñWß'„Ì_Ó‚‡ùWnÜõ¾®#ŒsºOî4¤ Wøgèpt¿m?ùºøêAp] È[óþ6\Nàï‚ÂW¶áj"àÚò5Ü@Æ÷qCXnq‡„ûèð—ôÒÈSY,A„Ñ ˆ®Â$£¬¬ŒßýîwRÂÔêÁA„‘Buuu^çÍ ‹…ÜÜ\ŠŠŠˆˆÇ˜ – Ó]tnƒœœJJJðx<8N<ˆ¢(¬[·N GkÜQŸyTá¢s˜ü7=¥íIsÐS ¦©q[›L&âââ°X,Øív «»»›·ß~›ÆÆFEaΜ9“˜˜€¦i¼ýöÛÔÕÕ¡ª*Ë—/²óG}ÄŸþô'®]»Fdd$ééé<þøãDFFJc¦¹¥O³uRDOi{aÔ&âa®‡‘®r[œ¶¶6ššš ãÉãñðÊ+¯p×]wñ­o} UUyçwxå•WøÞ÷¾‡Éd¢ªªŠ>ø€’’¬V+UUUÔ×ד ÀçŸÎX¿~= .ÄårqéÒ¥!F˜ LOKy˜ËûÄ”¤¡¡]»v¡in·UUÙ´içÎC×u¶lÙbÄß²e »wïÆáp`·Û9sæ ………,\¸€Í›7sþüy#þ_|®ë,Z´ˆ„„RSS¥à1°ú_;eˆpÚë)mOšC€žb0MÛ:;;›Í›7ãr¹¨®®FUU-Zô÷h]»v]»vù¥q»ÝtvvÒÛÛËçŸάY³Œ0UUÉÈÈ0ŽÓÒÒÈÎÎæg?ûv»»ÝÎâÅ‹‰•†"ˆ%C„¢§´½ÉÛdˆPnëÛ!22’¤¤$¶nÝÊ /¼@mm-Ë—/Çår‘‘‘Á#<2dH/..nDÃ|ªªòÄOÐÜÜLcc#555œ8q‚gžyÆŠ„Ñ ~°A„°BQ 9~ü8ÿ?{ï\Õq&úþº×~èýB ’õÙÈÆ`¯@`çbO2>q†É0™rœI\5÷ÔT™“ºET*Už¹ó¼sNÊc{|'1VâClc‹0æá`ØËXH€ ØH6’¶´×êûÇZ{koi‹—õÖ÷£šÞ«ûë¯{u÷êõ­î¥^‘H„iÓ¦qùòeÒÒÒÈËËKpÁ`””2339þ|L‡ã8|úé§ýt±bÅ þüÏÿ˲8yò¤T¸ j n¨¤œÒ÷&F·5#TαRV¹¬o9sæ µæ÷¿ÿ=UUU¤¥¥±}ûvšššhkk£±±‘Ý»wsõêUxàÞyçêëëùüóÏyíµ×èêêŠé;wîÿñÿAKK W®\áäÉ“ttt0yòdß„;B–G¼ÌRNé{£ÛÊ¡\Öƒ:; 5‹/fÿþý,Z´ˆM›6±gÏvîÜI8&++‹ÒÒR‚Á =ô¡Pˆ]»v¡”bÁ‚TTTÄŒ¬`0HSS‡"“““Ú5k˜5k–Œo‚X‚ ÂøcÆ I×,YÂ’%K÷­ä¢ÙÚµkY»vmÒøÉ“'ó­o}K*[¼‡€qu6²D(唾7j»ƒ,Êe-b`Ud‰PÊ)}OºCŸrJוËZÄÀaÜ¢¤¬‚ ˆ%‚ ‚ Ü)#ö’ûÿé³RûBª? Š#Ó‚0àåaI5 ‘,AA„AfÄf°¶nÝ÷,¤=gyÎç¢a:Iœ¯O\²øx¹¾ú|IÒÄ뺑¾Û-›¾íô:æüh46 Ûs|ôà#‚Eƒöd¢aÑøÞc×i"Xž¼…" ÎŽs†ž$ñNœL|\ŠˆçzP±òÚ±ü|±²ô$„[8 åŒÿÝ{®Qy·ü>||,LBëݬ}IdƒqΗìÑ} ÄñJü·Y¬K¨^§5XV¯ïóõ÷£Î²e“ÉÇëÓ ´Ê€r@Ù}ÚÙm»ä~¤\ßöwªÉº=×÷;>¬o‹·é}Ûàßgi|–Âg),ŸÆç·ðù,|~ei,KaYm)7ÎïÆùü:.βt,Üç·°|…BÓë÷Wââ®ïØï«>~2=ÖºúÊ$Ó•,/}ƒòÜiÙ­èKV¶Û-{b™~üãË]YK+È:— Ý@û\¿~ÚÚZ…B¤¦¦2uêT–/_ŽmÛ<÷Üs(¥’~wP)ÅÆioom4 îæ¢“&Mâî»ïæ %%å¦å8räuuu|öÙgcÈÉÉ¡´´”x€¼¼¼Ù«W¯ò·û·Lš4‰ïÿûN4|÷»ß¥°°0‰DøÙÏ~Fgg'ú§JIIÉÉGùõ¯ÍÑ£Gùú׿ν÷Þ{Ósܵk]]]<þøãI1°A„qÂŽ;p‡êêjrss …B466ÒÑÑÁ¬Y³øÑ~“ݽ{7ÝÝÝlذ!fp¥¦¦ÒÞÞNJJ O=õƺºº8{ö,ÿñÿA]]›7o&33sÀ2üâ¿àÔ©S,]º”µk×’™™Éµkר¯¯gß¾}ý6:=vìsæÌ¡©©‰óçÏ3cÆŒ~:³³³9vìX‚ÁT__O Hø”ÏÊ÷ôôpâÄ –,YÂÑ£GoÉÀykÜ#;ÝÒ „±MWWÍÍͬ^½š’’²³³™1cK–,áž{îÁ²,222bÎï÷cYééé±0Ëê}-?žŸŸÏ‚ ؼy3ÝÝݼùæ›–áøñãœ8q‚¯ýë,[¶ŒÂÂB²³³),,äË_þrÒ]äëêê˜7osçÎåèÑ£IõΟ?Ÿ'N‰DÒÍŸ?Pä?øà¦L™Â’%KhjjŠ}›QK2dÍHäZõõõ †Å`‘žžNUU§NJºÄpâÄ òóó¹ûî»oIç'Ÿ|B$¡¬¬Œªª*Nœ8AOOO?¹iÓ¦‘““Ç~@{{;MMMÌ›7/iYnW¾®®Žªª*‚Á ååå;vL.™aB–'ìà+SÒ „ñ÷ ¬µ¦ººššš>Ì´iÓ())¡²²’‚‚‚AÉ#??Ÿîîn:::HOOïùòeòóóÂ~ûÛ߯f¦RRRø«¿ú«æ²²¥S¦L!77—>ø éLÓüùóc†Ð±cÇ(//'--mÀ²ÞªüåË—9wî\ìÝ©ªª*^ýu–-[&—Ípô[©‚‰0ø Ò ¤ „±MEE?üáùæ7¿Iyy9gΜá_þå_mF&:û£n㻢˖-cË–-,_¾œîîîXxWW'Ož¤ªª*VUU5à2aUUçΣ­­÷ߟûî»ï†ùÞª|]]³fÍ"55€Y³fÑÕÕEcc#MMMlÛ¶-æŽ?.m‘,™ÒA®å±qÃòù(++£¬¬ŒeË–QSSÃÞ½{|ÿèvøì³ÏƒÎMš4‰Ï?ÿ¡–z~ÇGÔâ~—,úNw¼¯nrõûnµ8ÐïäÚvQ“|Çm˘l‹J‹äÛ4ު̭Ä%sÉÎåF[^Z·Q.wƒÏÄ:R7©×dõkGÕ?Œ$mÔ7IÂoV8uƒBédúéQÞf°½Niݦ¢ÑjÐÞæ±Zƒò|­¼p ,Ÿç,ðùÝß±Ígã\4Üç÷âû8 ‰ BÀó-_œ~?‚½ÎðôÆ9ü~ðy~4Ÿ´% 2ƒ%‚ ‚ ŒÂ,AA¸U®_¿Nmm- „B!RSS)(( ¸¸˜·ß~¥Æôÿ›-¥7n¤½½]»võ“óù|üÍßü »víâØ±c(¥ÐZ“ͼyóXºtiìãÌGŽá÷¿ÿ=mmmh­ÉÉÉ¡²²’%K–H# b` ‚ c‹;và8ÕÕÕäææ …hlldòäÉüèG?ŠÉíÞ½›îîn6lØ3¤RSSioo'%%…§žz*ÁÀêûåòòr6lØ@$¡¡¡×^{ ˲X²d Gå·¿ý-?ü0ÅÅÅØ¶ÍÅ‹¹té’4 –0–‘Ý2¥†„‰Øè]]]477³iÓ&Š‹‹ÈÎÎfÆŒýdý~?¶m“žžžT×@áQ,ËŠÉÜÿýœúè#*++Y°`AL~òäÉÒ…¤È;XÂB¶l“&b£õõõD"‘aÍÛçóaÛ6œ;wŽöövéwÂÍûŽT ‚0ªg´¦ººššš>Ì´iÓ())¡²²’‚‚‚[ÖÓÕÕŶmÛŠ‹‹yâ‰'’ÊŸ>}šÓ§OóÀ°|ùrvîÜɳÏ>ˤI“(**¢¼¼œ{ï½·ßR£ ˆ%Œ!d“&j£WTTP^^Nss3çΣ¡¡ýû÷³~ýzæÏŸK:‚Á [¶lIxËï÷'È|ôÑGlÛ¶-6k5wî\V¬X@ff&›7oæÒ¥K455qöìY^yåŽ=Ê“O>)}QK«È˜Ô´Ñ'ˆ‘åóù(++£¬¬ŒeË–QSSÃÞ½{oÙÀRJ‘››{C™ÒÒR¾úÕ¯¢µ&333ö׃ñL™2…)S¦°hÑ"î¿ÿ~~þóŸsæÌJJJ¤? 1ä,AaL’ŸŸOww÷ êôûýäææ’Ô¸JV`ÐË!Œƒ©aì `RCÂDlôŽŽ^~ùe,X@AA@€––8ÀìÙ³oKW(ê–‘‘qKió›ß™™Iii)YYY\»v}ûö‘žžNQQ‘ôEA ,a¬" `RCBÒFçFV  °°C‡ÑÚÚŠã8dee±páB–.]zËzÂá0?ûÙÏz«Î”Rüð‡?¼%#kæÌ™ÔÕÕñÞ{ïÑÙÙIZZ………lܸ‘ÔÔTé‹‚X‚ ºQù|¬ZµŠU«VÝTvÆ IÃçÏŸÓwµJ¥¢¢‚ŠŠ iá–w°„1„,€I Òè‚ – 2²&5$H£ ‚X‚ ‚ b` ‚ ‚ _µuëV™`AADdKAa±m¶nÝ €HRQ¤™J“©YJ“…"M)ÒP¤z~¦Òdyñ™^|j4^iRP¤(MŠR•&‚"¢4¶ò|¶R8("Ja{ñ®sÃm/<š&“I<ŽÊ91yMOœŒ§Ïé£#*ŸWŸ²F’¤qnP6§Ï¹%-»§'U)²Qd{¾¥4J)”Ò€ò>\ïkÏÝL&>~`ù¾2ÉâoEÏòʲ F>½úðœ”râ|¥"(e£” ôþvãåûË$êq¼°¨ÎÞtñ镊x2ñùDǧé•ï_§OY#ä}+ewú”%’¤¬}õõ•‰3€F)ËsÚ;îõ•²«Lïñåûê³Ð=Ž×£“Ȩ>:ûÆ÷Ï+¹ÌÀeOV–þu¯ÓweS}â}Öûüc¹+ b` ‚0Z¹ñßí+ù³~aûÔppýúujkkihh  ‘ššÊÔ©SY¾|9¶móÜsÏ¡”JøsoŸWlܸ‘öövvíÚÕOÎçóñ7ó7ìÚµ‹cÇŽy?Ь¬,æÌ™Ã—¾ô%|>_Â$Ïçã©§ž";;;¾}ûvRRRn¸§Ö‘#GøýïO[[Zkrrr¨¬¬dÉ’%ìÝ»—½{÷&”aöìÙ¬\¹’@ @{{;Ï>ûlÒóܼy3………ضÍÁƒ9~ü8­­­øý~òóóY°`óæÍCkÍ®]»èêêâñÇOÐuöìY~þóŸS^^Î7¿ùÍ„¸hþ[¶laêÔ©ryˆ%‚ ŒUvìØã8TWW“››K(¢±±‘ŽŽfÍšÅ~ô£˜ìîÝ»éîîfÆ 1C*55•öövRRRxê©§ ,Õç©£¼¼œ 6`Û6---¼òÊ+(¥øò—¿ÜÏ ©­­½é¥ñ=z”ßþö·<üðÃcÛ6/^äÒ¥K rS¦LaãÆØ¶ÍÙ³gÙµk‘H„¯~õ«±¼¿ýío3yòä„tiii1ãê…^àâÅ‹¬\¹’¢¢"‚Á çÎãÀL›6í†ÆQ]]<ðuuu\»vÌÌÌ~ç.ˆ%ŒÿÝŠ12‹% nŸjºººhnnfÓ¦MÍŒ3b2ñŸºñûýضMzzzR}…G±,+&“••ÅÌ™39}út?kñâÅm(U ÜŒ/ö¤"wra¢\'C•V®¡@ @  ¾¾žH$2¬y_¼x‘ææf,ËêWTTÄÝwßÍž={nY_FFçΣ½½ýöfC|¾E}9~ü8eeeIg©´ÖøýþÓž8q‚É“'3iÒ$ªªª¨««“ ü,áæÏ®ÞQïüÉWnÃ|«2C@¸Ñurçu{ã´£áÙüµÖTWWSSSÃáÇ™6m%%%TVVRPPp[ººººØ¶m[BXqq1O<ñDìø£>bÛ¶m8ŽC$AkÍ#<’TߪU«ø§ú'š››¹ë®»nšÿòåËÙ¹s'Ï>û,“&M¢¨¨ˆòòrî½÷ÞÇÙ––Ž?NiiiBøÿú_ÿ«_šÿößþ­­­ýäo•ºº:ªªª˜5káp˜3gÎPRR"ºX‚0¡oõCfÒ§FŠŠŠ ÊËËinnæÜ¹s444°ÿ~Ö¯_Ó8Ç Ù²eKÂWßÙœÒÒR¾úÕ¯ÒÝÝÍÁƒÑZø‘çÉ“'3oÞ<öìÙÃw¾ó›æŸ™™ÉæÍ›¹téMMMœ={–W^y…£Gòä“OÆä.^¼3òÇáî»ïæá‡NÐõõ¯üüüŒï„Ï?ÿœóçÏÇ^z×Z3gÎêêêÄÀKôgWY"„[¸N†*­\C±–ÏGYYeee,[¶ŒššöîÝ{[–RŠÜÜÜÊøýþ˜Ì£>Ê?ýÓ?QWW—ðÞT<+V¬àïÿþ﩯¯¿årL™2…)S¦°hÑ"î¿ÿ~~þóŸ'ÌåççóÍo~¥™™™I—(³²²ÈËËKªÒ¤I|þùç·]ÇGÅÃÏ~ö³„p˲xøá‡ ƒÒÅÀíÙU–ÇÚ­~È áF׉,7ùùù·eÔÜéæÒ¥Kyýõ×™;wnÂV Q²³³Y¼x1o½õÖM·ÎÜ—Ëã šéºÙ˜|ðAø·û7¾ô¥/q×]w 9þ<û÷ïçÑGígx:uŠ®®.,XÐo¦ª¢¢‚£GÆ ,cLÒ²)S¦ µüýœXÂ-?Á·'ßq>—2d3-®“¡J«F}Ÿj………:tˆÖÖVÇ!++‹… öÛ>àf„Ãá„å¯è ýøÃ,­5‹/fÿþý,Z´¿ßßo\LMMåþèxûí·o8fΜ9“ºº:Þ{ï=:;;IKK£°°7’ššz[ãò /¼Ðï<{ì1*++ñù||ûÛßæàÁƒ9r„7ß|¿ßϤI“¸ï¾û¶•ˆ–·®®Ž²²²¤Ë€÷Þ{/àâÅ‹ƒA”Rüò—¿ì'÷_ÿë%++KFðcÏò©ùTŽ|*g¨>•“ìó5½ŸÊéÕ#ŸÊ‘OåÜê§r¬>Çò©A¸2'‚ ‚ –0ìÓœ²D8Ƹù¡0×ÉP¥•%BAKŸ·ë/tG–Y˜(×ÉP¥•kHÄÀa Û4ÃÛ§AKAaèKFê¯AAÆ+2ƒ%‚ ‚ – ‚ ÂèfÄvrÿîÿ÷?ÜÍ1µv7ÝÔ¶¶èÑ>"–ˆöyqŽçG,‹ˆöÑcùˆxòñi#ÚŠ¥ùÚK•u”öd­8Y G[ØÊróÕ^^}tôXîoGiŒ"æPhoÆè¦Œ*ºÁ¡AiÛ;¶{Ããeâ¢:ì^˜D" @ IDATÊñtöæÉôyñ:*g÷–ÉóµQhÛB;åh×7 å(T4Îvõm%ÆEåÝ+•‹Å)”ñ|/ÌŠK߫ˊӕ¨ßÍßõ-46ZÙh×W6ZEGÐÊÆR´Ž UËsÚÛR{u“‹ùWQ}^˜ŽÄä¢éceнºûúñåR8hËAiƒÒm÷‘G«^gEŽûw¬Uošd²ññ:.ΧóÑ$ɧOžº×7ZÂóF)ŒŽ ÆÇ18=àD¢Î`Gä#&gGÀØÆs¼xÛÄâ\½&N—o<]Æ;ßãész㢺zó…îuÊB+ ­|q~ïïè&™ÚÛ˜3QÎÝ`SG7åD÷ÑÑ_—ŽmD•MÌ[Åm Ú¿\îfŸx×6GƒÁVG™Øo£ îïúñqqñÊ`¼xW_¢œ£<}žŒ‰É$¦I¯£7Þ=O?ýTîÊ‚Ì` ‚ ÜòwhÒ‚ 3X‚ ‚0Џ~ý:µµµ444 …HMMeêÔ©,_¾ü–>’¼wï^êëëÙ²eKBø‘#G8~ü8Ÿ~ú)áp˜¿þë¿&%%E*\K„±ü¹ò8jȘÅÚ±cŽãP]]Mnn.¡PˆÆÆF:::¾Þžžf͚ŬY³xë­·¤}…ñi`É̳0’HMø¾¨nKR &¹Ø†«tuuÑÜÜ̦M›(.. ;;›3f$ȼþúëœ:u Û¶™>}:kÖ¬aêÔ©;vŒ½{÷¢”bëÖ­(¥xôÑG™?>>ø gΜ‘&Ư%¶0’O×ñITÞ _«ò5Þìðá»J@€úúzf̘Ï×ÿÖµsçNO>ù$Á`÷Þ{矞§žzŠ9sæpéÒ%>þøc6n܈1F–…‰e` ‚0¾Í7ab?ÄÜ)Zkª««©©©áðáÃL›6’’*++)(( ©©‰––ž~úi,Ëà+_ù õõõ|øá‡,\¸@ €ÖšôôtiCaâXò„+Œd'’%BY"n·¿ _/¨¨¨ ¼¼œææfÎ;GCCû÷ïgýúõtww‡ùÉO~’&‰ÐÖÖ&M%ˆ%¶0’OײD(K„Âí4äð_%>Ÿ²²2ÊÊÊX¶l555ÔÖÖ²hÑ"233Ù´iÆ$–K–1°Aá6ÈÏϧ¾¾žéÓ§ …PJ‘›››TÖ²¬~Æ— LKžp…‘ìD²D8´K„rk'Ä&:‡o‰°££ƒ—_~™ PPP@  ¥¥…0{ölÊÊÊ(,,dûöí¬^½šI“&qõêU¨¨¨`úôéäääÐÖÖÆ… ÈÊÊ"àóù…B„B!._¾Œ1†‹/ ÉÎÎ&55UÚ[– ÀÂà þ_,©,þµ*PãÍÞ¿",,,äСC´¶¶â8YYY,\¸¥K—ð­o}‹·Þz‹W_}•ëׯ“‘‘Aqq1€û×É“'yî¹ç‡Ã±mÞ{ï½ØJ)ž{î9€X¼ Œ KAúݨ|>V­ZŪU«nh„­[·ŽuëÖ ¨ãßøF¿ð+V°bÅ ©daÐUß"”'\a$;‘,ª!­f™¡'±aÌX2 #ى̄ï‹F®UAFlA– ã™ï†1°AA„;fÄ^rÿŸßûîˆY”Q«2¸ÓÜ=ž C0s®Ç®-|âÀö\oÕ9CÞåGáßv8C~â ËsÁÑZ@è|µ¦/ˆÞþAA„A~œZV– ŒAƒÆõ-ã`9ž‹É€e|&y|4¬×¹Ç¾h¸ã¸* iŒ§ÓIÈ'1.^÷åóÊŸŸû ©0(@acá q”Žó•ë¢ahWÎ;¶UoƒŠÉ¥°û銓P_bÞË?V¾„üÜòÛÊŠËËòZÑ{Ã(¯Îè­_ïXyÇʳæ <9Ëó•‰ÖPTž¤úT\x¢¼é#Ÿ§nQ—[>Êq§”ƒQ£ð|£Œvp´ƒÑv,ÞDe£qÊNøíúqržNGÛ8:*kÇåÓ«7A‡—¯ã•#QÖ¸q:>®·ìx¿‰û=g÷ü׃¼úôŽu\¼òäuŸ4‰q$Õ¡â’ëÔ±°[/›îí‰q[4iÀÂ1:±dÆÓàÇõÓ›C4Þ Áó£ñÑ4é»q~ÉóX—Šé2±qŒQqãŒ{ûòŽéçCŸð$Ç$èë¯ÇIšwï˜×[6’”Aõæ×zGŽ‘»² 3XÂÈ!ï˜ Â8½¶ÇÄÅ-# Œê,AA¸U®_¿Nmm- „B!RSS™:u*Ë—/§¨¨è¦é÷îÝK}}=[¶l‰…uvvR[[ËéÓ§¹rå éééÌž=›/}éKò‘hA ¬‰ˆ¼¿*ãôÚ÷øÕ`ÇŽ8ŽCuu5¹¹¹„B!éèè¸c×®]# ±fÍ&OžL{{;¿ùÍo¸víZÒ]ßaŒX_à¢w˜TÔiŽÔX^®ÒÞy>Ã÷É܉AßúTj¬.c¬ž‡±^»ººhnnfÓ¦MÍŒ3d^ýuN:…mÛLŸ>5kÖ0uêTŽ;ûÞàÖ­[QJž5oHåææ²råJ^yåÇAky‹FW–ö¤f¤Nóí–a¿!3†_»0£>¹÷mKŒ%ãjlƒjØë5¨¯¯gÆŒø|ýo];wî$ðä“O yï½÷xþùçyê©§˜3g—.]âã?fãÆc\ìêê" Šq%ŒGK² LÐk[–“¢µ¦ººššš>Ì´iÓ())¡²²’‚‚šššhiiá駟ƲܿrþÊW¾B}}=~ø! .$ µ&==}À|®_¿Î¾}ûX¸p¡tFa<X²D8*«Ç,²D8Ñ%Âᆳ^+**(//§¹¹™sçÎÑÐÐÀþýûY¿~=ÝÝÝ„Ãa~ò“Ÿ$¤‰D"´µµÝ’þp8ÌK/½Ä”)SX±b…4°0 ,Y"u7dY"Ò|äÞ?´-!K„Cót8åôù|”••QVVƲe˨©©¡¶¶–E‹‘™™É¦M›0} v+ ‡yá…HIIáOþäOdyP¯– ‚ Üœüü|êëë™>}:¡P¥¹¹¹Ie-Ëêg|ÅW~¿ŸÿüŸÿsÒ÷»aœX²Dx»OèÃòX=f‘%‰ÆX^"Kï` g½vttðòË/³`Á ´´´pàÀfÏžMYY………lß¾Õ«W3iÒ$®^½JCCLŸ>œœÚÚÚ¸páYYYlÛæùçŸ'‰ðØcÑÕÕË3--Mf²„ñf`Éᨻ!Ëáæ#ÆÕж„,ÍÓápÿaaa!‡¢µµÇqÈÊÊbáÂ…,]º€o}ë[¼õÖ[¼úê«\¿~ŒŒ Š‹‹ÉÈÈÜw¸Nž<ÉsÏ=G8æÑG%''‡––þîïþÎ;/ƒRŠÿò_þ 999rA ãÉÀA„¸•ÏǪU«XµjÕ °uëÖ±nݺu$Û<ô™gž‘ †„Q8ÿ©†=©,ÞÂcõ˜Eú|äËnCÛc©ûŽ%Â1>,ÂÄ4°&СŒú㨢e‰p´¶¸lÑ Ã‚ ˆ%ÜѺ ãäÚ·Œ@‚ – ‚ Â0b/¹O»á¶Ó â¹Á{S£ÜÎt<'L<`yÎ/Õ1n‰^ß¶T… Œ#dKAA ,AA„Ñ͈-f>¶…A+Ð,–6ø”Á§ >Z¹áQ?n©^_{¾¥{eâeÝcW§E¯.+^§v­McŽãúÆQ8ŽÂv4Ž£pl÷Øq4Ž­ÎwÐ黎Óg 0މ9l0¶‰9wƒÉ˜LÄ`úÊD;ãžo“ gìÞüˆ÷Ž£«¾Êçù–r ªoœÏSÚ;Ö |žœ/.­M¯zÓGã“ÈGõiå °ÑžS8 NÇâzååM¯¬ê/ãÆ/}ò¼bq þûÿ›+weAf°&.JÎAZL·ƒ´¾Ô’ ãxKAn•ëׯS[[KCC¡PˆÔÔT¦NÊòåË)**ºiú½{÷R__Ï–-[Âýë_óÉ'ŸpíÚ5EEE¬^½šüü|©tA ¬áå |DpTƒ0ò572}IZ_ji,²cÇÇ¡ººšÜÜ\B¡ttt|!½Ó§O§ªªŠììl:;;Ù»w//¼ð?øÁP²]½0 ,uKƒóäáùàªåúKóÄ †ýÜr·T¶ Ñ.ê‹]rcá íêꢹ¹™M›6Q\\ @vv63fÌHyýõ×9uê¶m3}útÖ¬YÃÔ©S9vì{÷îE)ÅÖ­[QJñè£2þ|.\Ó‘““ÃÊ•+ùçþgÚÛÛÉÍ•w„ ``™[Pƒ3”ÞØ¸RƒxFj8kèŽÏÁ [¹Æ®¡oUÛ0íÍØ,̪¥1ÒwÍk›±0'Ô××3cÆ |¾þ·®;wxòÉ' ƒ¼÷Þ{<ÿüó<õÔSÌ™3‡K—.ññdzqãFŒ1¤¤¤ôÓÑÝÝM]]¹¹¹dee‰… L ktf²D8‘[¬÷%i}©¥±†Öšêêjjjj8|ø0Ó¦M£¤¤„ÊÊJ hjj¢¥¥…§Ÿ~˲øÊW¾B}}=~ø! .$ µ&==½ŸþÇóæ›oÒÝÝM~~>O>ùdL Œ{K–Gn6D–ÇÕ|Çø?aY"—WhEEååå477sîÜ9Ø¿?ëׯ§»»›p8ÌO~ò“„4‘H„¶¶¶›ê®ªªbæÌ™\»v°sçN6oÞœt¦LÆ%K„ƒùÄ-K„ƒm.Êáè,d‰pøG¢!½aù|”••QVVƲe˨©©¡¶¶–E‹‘™™É¦M›Ü}ÏâH¶Ø—`0H0$//ÂÂBþÇÿøÔ××SYY)V‚0þ ,AAˆ'??Ÿúúz¦OŸN(B)5à‹é–eõ3¾’?`»2‘HD*X˜ÖèY"¬ç½‘\"4ÃXÒñµDhnp–掴Ñw°ÆýášyK„¼üòË,X°€‚‚---8p€Ù³gSVVFaa!Û·ogõêÕLš4‰«W¯ÒÐÐ@EEÓ§O'''‡¶¶6.\¸@VV@€k×®qâÄ fΜIzz:W®\áwÞÁï÷S^^.‚01 ,Y"Ì‚,¶¹(K„£s°%ÂÑjæß@€ÂÂB:Dkk+Žã••ÅÂ… Yºt)ßúÖ·xë­·xõÕW¹~ý:“‘‘¸ïpÊÌ™3innæÝwߥ³³3–fóæÍI_†„qi` ‚ ôFåó±jÕ*V­ZuC#lݺu¬[·n@ßøÆ7ú…?ñÄRÁÂ0f¾ExëK„ƒ—ŽGwY"‹˜A=K3ªÎaÜ4«ŒbÊ¡ ÄÀº¥%B†c‰pTÝæ†å† K„r–cê„Ík—AX"aX£÷qQÎAZlµƒ´¾Ô’ b` ‚ ‚ Œ9Fì%÷k¿|]jÔby.Ž×ÏE€ð°”ÎöÜТ¿çÆèñœ0¢D†õrýc‹ ŒSdKAa±¬»þû¾Þ¹Õë´VžJƒRÊý¯{åTÔï“Vë>²ñihåêVºW>^WÌiOVƒR¥øcÕûW‡J›XTŸx¯LŸã¸°X¾¦Ob¼Â-˜v}¥”;é¢ãü8§ŽÝs÷*Á+K\|ôXÅÅ[}t©Þ†SÑr¨øxÕ[qºo¥G|C÷ÆÝH‚XIOÒJ¡tB&¯XÕWFõ‘щ> œ\&i*LÜŸ¬š˜oÜÿçGÿ%;qÇÄäÝ#§|ßøþú/Þ‰É8Æûeܲ9žo 8Fya 'îØÄdNL6N•˜ž8yã͇÷81oå…Ge¢ùöÇ—1g›Är›„4½.YÚ¾2ñÇ7’7Fá8qù9 ã(·ŒNܱ£<Õ+ÿ;‰ŒqâeIŒw¢yÇå“§[ÙÊ€2Æõƒ6å”îU¼vÜ8í”1±ãh&1MTgoš>ñNŸ<ã㲟È]Y,A¾ JÎwÔV—¼ã. 1ííílݺ• .HeÈ – ‚ Œ ׯ_§¶¶–††B¡©©©L:•åË—STT4,eغu+?þ8³gÏ;^‰%/– ƒÏXøFÉ`ŸïÈ%Å™ ·ÂŽ;p‡êêjrss …B466ÒÑÑ1ªÊiÛ6–uk/ò#M ¬1€šåU#T65–fÂ=Ž¡oæ 7nÇ1×”ÃWà®®.š››Ù´iÅÅÅdgg3cÆŒ˜ÌÖ­[yä‘G8uêgΜ!33“Õ«Wsï½÷Æd®\¹Âo¼ÁéÓ§QJq×]w±nÝ:rrrb2GåàÁƒ´¶¶’––FEE?ü0Ï>û,J)¶oß@NN?øÁ¨­­åÔ©S,^¼˜}ûöqåÊžyæ>þøcöíÛÇ¥K—PJQTTÄÚµkÉËË“î.–LŒ¶ò×9öÍç å;Ü 3–:¸ÿêòàÞ‡ÕþvcÇ€kOÃWA@€@ @}}=3fÌÀçK~몭­eõêÕ¬[·Ž÷ߟ_üâ|ÿûß'??Û¶yñÅ)**â;ßùZköíÛÇ‹/¾È_üÅ_`Y‡æõ×_gõêÕ”——‡innàÏþìÏøéOJuu53gÎDk÷f¥­­­œ|˜iÓ¦QRRBee%1¹9sæ°`ÁV®\É'Ÿ|»ï¾Ë#<‰'0ư~ýú˜üúõëùÉO~™3g˜9s&ûöíãþèxàb2Ó¦M ==€`0HFFFBùlÛ¦ººš´´´XXüÌY4¯Ÿþô§\ºt‰)S¦H£Š5¶ŽÇ{ye‰pv„±7Õ" U;Êá ©¨¨ ¼¼œææfÎ;GCCû÷ïgýúõÌŸ?€Â„4………\¼x€‹/ÒÚÚʶmÛd"‘mmm\¿~k×®QZZzÛeËÉÉI0®._¾Lmm-çÏŸ§££c J)®\¹"–X20ÚÊ+K„ã¬#Èáè½Ëá¨ì{>Ÿ²²2ÊÊÊX¶l555ìÝ»7f`݈îîn¦OŸÎc=ÖïåòèìÔâ÷÷ÿâÃK/½Dnn.ëׯ'33c ÿðÿ€mÛÙKA“äççÓÝÝ;>wî\Bü¹sçÈÏÏÜ¥¾Ë—/“––F^^^‚ ƒƒArrrøä“7;µ,ë–þò¯££ƒË—/³lÙ2JKKÉÏÏOú׎²MƒXg2`”—W–ÇYG0ì„嬷㘻×_;::ø·û7þð‡?pñâEÚÚÚøàƒ8pà@žT~ø!uuu±å¹––/^ @UUiiilß¾¦¦&ÚÚÚhlld÷îÝ\½z€+VpðàAÞ}÷]._¾LKK ï¾ûnLÔ …BtvvXÞÔÔTÒÒÒ8rä­­­|òÉ'¼ñÆý *Ù¦a|#K„c¨¼²D8Î:‚,gòQœ™×޲D8 @€ÂÂB:Dkk+Žã••ÅÂ… YºtiLnÅŠœ8q‚×^{ÌÌLþøÿ˜É“'î2Þ¦M›Ø³g;wî$“••Eii)Á`€ùóçcÛ6ä7Þ ---áeõ¯|å+¼ñÆ9r„¬¬,~ðƒ$ï>JñÇüÇìÞ½›üÇ$??ŸuëÖñÜsÏõ“Æ/jëÖ­#2BË·å[„þ[„Ja˜@ß"ôäïô[„¦Ï·‡ô[„}âå[„£ÿ[„ƒ½Ëº |Qd‰p •W–ÇYG%ÂÑ;W'K„ãpÔ1°ÆÆx8å5#T63–fÂ½Ò ïpŒ›v4Ò÷¾¹'ËmÂ(C6™¾ó•w°„!â™gž‘JF²Mƒ ‚  3b3XÍÏ,“ÚNÏ[ 2å öÓç„xU€å9Au AA‘›Áª}þ€ûgáÊÆh£l«ÇêÂöuáøÂ8ºcõàèŒîqã}Ý8VØý­½´ÚƱ"ËÆ±lŒñ|ƒcÏwMJ£À訳0–…Ñ>Œ·_ƒQQßíÇè€ë«è FAA@ù]‡TJn7Œž¯ü(ܴʸ2Š ŠÊ<ßï9 2Êè˜ÓŽò~+”ý3lÇ 'ÎÙh'‚¶#®ïDPNml”Á²»ÑN8æk§'æ”ÁŠtbÙ]h»Ëõn7ÞîF;ÝXvVijì.|‘.¬HØ;îÆ²#ž¾–݃/Òƒew{~$VN˶±lŸíà‹8îo> >Ëu–K+´VXZá³|ø|>ˇeYX–K[žóá³ø­ >+€Ï â×A|VŠët­üh@)?è+•BD§ÑAlÀÖ~l ¢n¸J¡'§|D”×xr=ÊODùéQå£¶ÒØ(l\¿Eˆ=ÆÝ`ÁŽ:cˆ`Ócl"ƦÇDˆ˜"¦‡Óƒmz°M›ˆë›n"NØs]DL7¶éÆvzpLÞ‰ít±»°7Þqº]]v˜ˆÝ妵»0vcwcì0Øa·í¼>d9,Ûu¾HÄkÃhŸs}Ë€e ––Ÿ¥°| ËRhK¡-Öž³|hŸËò£}~÷Øò¡´Ïû@[)XVí ¢­×y¿• ¬€ëk¿—“CùQÚ‹WV¥ƒh+ˆ²^¼¥Ü<•åí…iŸ»½‡¶PZ£”ŽÛR$ºeŠ·Ï€{ñõú8 ¼V=@£ºn ž §cz€. ]ÂÓåʘ ÝÓ1]Ó‰cº0&ìÆ™î˜ï8aÓ…ã„Ýc;Œã¸ílLDZ1ÆÆ86ŽcãØ6¶ç;Žñ¶˜p}ÛÇÛñüˆ…mklÛÂŽXljs¶ ñcÛlÛcûq?ŽíÃqD"Al;ÅsŒÑ8Ž…1îàüoš*weAf°†† ôÖ¯üÁ˸jiNAzÉèçç?ÿ9o¼ñ†T„0 ¬ ô¼.4®ÚDšS^2t¼ôÒK¼øâ‹IãšššØºu+/^”ŠÄÀ’'>ù䓨7ã©««cÆŒHE £ŠQ¶Ö›Á’ñv ¶‰šè=W œ¡߯Ãþ[CPÍwß}7iii;vŒeËzÿ½»»›?ü5kÖÐØØÈ›o¾ÉÅ‹IKKcþüù¬\¹rÀMH#‘o¿ý6'Nœ ££ƒììl–.]û&áo~ó …Bdgg³xñbxàXú_ýêWD"¦OŸÎ¡C‡p‡¹sç²fÍ´vç/:;;ù?ÿçÿÐÐЀmÛ”””°nÝ:òòòä’KAF­5óæÍëg`}ðÁc¨¬¬¤½½ÿ÷çþûïçk_ûŸ}ö555øýþ„4ñüò—¿äÓO?å‘G¡  €ööv:::\sز³³ù“?ùRSSinnæ×¿þ5YYYTTTÄtœ>}šŒŒ þôOÿ”Ë—/óòË/3mÚ4æÏŸ3®\¹ÂO<ßïçÍ7ß䥗^âûßÿ~ÌÆi¿•*AF; , µµ•3gÎÄÂŽ;ƽ÷ÞK0äðáÃäåå±víZòó󩨨`ÅŠ8p ©¾K—.qòäIª««¹çž{ÈÉÉ¡¤¤„{ï½×}ðùX±bÓ¦M#''‡ªª*æÍ›Ç| '==‡~˜üü|î¹çÊËËùä÷£ÕŸ}ö lذ¢¢"¦NÊ×¾ö5ÚÛÛù裤QÇ92ƒ%‚ Œzòóó)**¢®®Ž’’._¾LSS+W®àóÏ?§¨¨(!MQQáp˜k×®‘™™™wñâE,Ëâ®»î0Ïwß}—cÇŽqåÊ"‘¶m3}úô™)S¦$gddÐÚÚ+“ÏçKH“žžN^^Ÿ}ö³gÏ–†KAF–ûî»Ý»wóÈ#pìØ1òòò(..¾³›ŸïÆ·¿÷ߟ={ö°víZf̘A àwÞáÂ… r}—ù”R#oe ²D(·‰ œÒvÒ½FêæÌ™ƒRŠ?üá¼ÿþûÜwß}±¸üü|Ξ=› ßÜÜLJJJ¿ ‘v IDATÙ+€‚‚Ç¡©©)i^gÏž¥¸¸˜… 2uêTòòòb3S·J~~>¶msþüùXX(¢µµ•É“'Ë%)Öp"Û4c³M¤9é%CO `Μ9¼õÖ[„B!æÍ›‹[¼x1mmmìÞ½›Ï?ÿœ“'Oò»ßýއz(©®¼¼<æÎË®]»¨¯¯§­­ÆÆÆØ;V“&Mâܹsœ>}šË—/óÖ[oñé§ŸÞVy'OžLyy9555œ={– .ð«_ýŠÜÜ\î¾ûniÐqŽlÓ0’§*ãí¸i™×¤—  , ®®Žòòò„™©ììlžxâ Þxã Ž9Bjj*‹-bÉ’%êZ¿~={öìáµ×^£³³“ìììØ_.Z´ˆ .ðòË/£”bîܹÜÿý466ÞVy«««Ù½{7ÿþïÿŽã8”””ðÍo~Sþ‚P ,y“S• iNA.úÑCQQÏ<óLÒ¸’’¾÷½ï ˜ö;ßùNâ ÐçcíÚµ¬]»¶ÿÍÑ磺ºú†eùÚ×¾Ö/ìá‡N8NMMM*'ŒäS9rªÂmµ‰l4*Îv0%Õ,b` ‚ ‚ b` ‚ ‚ ˆ%‚ ‚ – ‚ ‚0bEø¥ogŽþÚ1€í¹/¤¤Ûs¡‘5¥õ`·¸R<—uG5Óã¹ÑI´Ý†å5ÑÐ_˜Ñv¯DÛîÊWåxnÛ8Þ¶¶Sž“ÇjA¶Û® ‚ ‚0ˆŒØ VO—kÛ Ư àúŽOãXÛg¹¿»5¦Sáty~»ÆnµpÚ4N«Q8='¢qR5Nšëì4 'Oãäiì\ '[cüÊÍǯ1Játè^w]ã\Ó˜rýë^|§o”ru{y˜ åºL×wÒ½ð4WÎØ Q˜×9mÓ¦0­Êõ;ã\Xa,…ñy. 0¹ “ç¹l©`R<ß§0¶‚nÝÊ »î:sUÁ0W\[7=’ ¤Ç¹ì>®¯é}Å ®¸êªéÍ+ªÃ@”mPƒR• :Û ² :Ë@º \?Å |ey¾mP:Au¹ùè6ÕjPmÝå "m{~ÀA¥š˜Ó9*Ï s=ßï üå7hŸƒê0èÇu×t«çÚôOgçã Ótªç§;è •iЙÞqšë¬4¥Ü´ºÇAõôUW¯Õj»y\w°:l7ïNm9(ŸWFŸÁʳ±rmtžƒ•k»ç“➟:舃¶¬ˆ÷Ñ}ÕÕ©:{ëÈòÙhËAû7}†ƒJ7è Ç-¶ë¬lÛ­…A‡ÜúÐWt»§ÿJ¯S4M“m\}9:ËÍGetº×>>Ó{¾ŽƒºbPíÆÕuÍ ®»ýH]7¨°Ihcå3¨©¸º² *× ò ä‚ öö!| Ânÿ¡ ·µT+àõ!"¸S¥P¶×ÿÓâ\&nÿŒúqqF{}½èr¯5sÍó¯+7¼CA'ø¼qÍ&»÷Z&LºÂ¤Å9oŒpz´;VDLJVwÌ ‹Þq¢ÛK»~@»ú¼ñÇd*œls& ÜqÑvÇGÓ¥pBîØçœ+½ÎØ ÇkeƒÂÉÐØÙVL_ßßnoðz…Ó«Çn·p®zcèuoLítËaG,·Ç§¢¢¿ßmÛ¼øâ‹ƒA¾óï°yóf‚Á /¾ø"¶mã8;vì ¤¤„ïÿû|÷»ßeáÂ…rïÄÀ’‘lŸœö(¯;©z!Jii)ÍÍÍ8ŽC8æÂ… ”””p×]wÅ ¬³gÏbÛvlëÁ¤¤¤„œœJKKY¹r%|ðALgUUõõõôô¸»ñ…Ãa>úè#ªªª8qâÆÖ¯_Ï”)SÈÏÏgýúõ\¹r…3g· ‡ÃÜ}÷Ýäææ’ŸŸÏ¼yódöJd‰p‘%ÂÑVý²D8L=V–G´ÕžÿVgg'“&M"--’’^}õU"‘gΜ!777fàœ>}šwÞy‡Ï?ÿœp8Œã8ضMOO~¿Ÿòòr´Öœ:uŠÊÊJ>üðCRRRb3`/^¤µµ•mÛ¶%”%‰ÐÖÖÆÌ™3™7o/¼ðeee”••1gÎ2331°FlP5cÐÈŠÍãw‰PÝñ{b,ªÁ¸†a‰Pþ6R=h¬öÔ¼¼<233ill¤³³“ââb233ÉÎÎæìÙ³œ9s&fµ··ó¿ÿ÷ÿfÑ¢E¬ZµŠÔÔTš››©©©Á¶mü~?–eqï½÷rüøq*++9~ü8sæÌAkwq§»»›éÓ§óØcaLbÍ¥§§°aÃ|ðA>þøc>øàjkkyòÉ'cïŠ Y"AÆ¥¥¥œ9s&áEv€ââb8þ|,¼¥¥c kÖ¬¡°°I“&qõêÕ~:çÎËÇÌ¥K—hllŒ-L›6Ë—/“––F^^^‚ ƒ1¹©S§²dÉ6oÞÌäÉ“û½×%ˆ% 룢Ã'4¶¼%Bs‡ Çj°®5H—ÐXj 5zÊ=–{jII ÍÍÍ\¼x±ŸuäÈljÍ`åååá8‡¢­­÷ߟ#GŽ$Õ™‘‘Á¯~õ+rss™1cF,®ªªŠ´´4¶oßNSSmmm466²{÷n®^½J[[{öìáìÙ³´··óñÇÓÚÚÊäÉ“åþ'ÈáÈd²D8ÚŒ_Y"¼ùÊáPddd°xñbî¿ÿ~¹ÿ b` ‚ cƒœœžyæ™[ðÁyðÁÂâ—£¬^½šÕ«W'Í3##ƒ 6$ ƒ<þøãÒ0BRd‰pÄe‰pD‘%ÂÁ¨²;»d‰pDË-ßw1°&²õ%eS0½N†ë Íì²D(b` ò¨(ϾrÚ£¥î¤êAKAAp·nÝ*3Æ‚ ‚ ƒˆÌ` ‚ ‚ 2#¶MÃwŸév˜è¾JžóŽ•w¬âã ¯úèêìx¹ßLž>ÇNÂq,oú–Í•¨ì7*_ò²öOVvç&e—¿YÙâUô™Ø4æÆqÉÒÞîñPè3fóæNåš­\²ãÛ•gô£²š[Õm†¦¬Æ A½™;ÈûvË>Ømü?j«Ü•…qÃøšÁR79ÅEÓõ,L¼úTRVé·£¿ˆ[·n¥¾¾þŽÓïÝ»—þç–¶ÄÀº•'¤ÑZÔ1]ÏÂÄ«O#e•~;|Y½÷Þ{lûÿÙ{Ó¨¨®tûª(†,†’yÁ1FiÀ‰Ä #‚QÌ`;¶1étlÓÝéN¿+\÷KȽkÝ•»òÞ÷ºî혾7Ý+¶Ý™cDÓ!œA ŠQ#NŒ2ÈŒ2Õ9ïàP§(©ªþ¿µj:g?{ï§žýœsž³÷©½ÿýß!Š¢|¬»»ÿú¯ÿŠ}ûö)dKKK‘žžŽææf¼õÖ[˜5kÖ¨ƒ±ÄÄDìØ±ƒmM`±‹ÊÓžÔ™þêxv1czzzP]]-+//‡——ªªªÐÛÛ+/++ƒ|}}áååµZ=êz]]]áááÁ¶&£Â±–Ê™Â=X‚ÕS©þ¤í@K:kO€SêlOº v¤ïê9°Æ`YYÂÂÂä@jîܹ(--Å;wäÅŸËÊÊäŸÓÓÓ±uëVÌ;ƒGŵk×ÐÙÙ ///$$$àñÇÇž={ >ûì3}Kï¼ùæ›ÈÍÍÅõë×±k×.@UUŽ;†ÚÚZ !%%ÁÁÁ¼Ž°!„8$3fÌ@YYüq9zì±Ç IÊÊÊä^®ªª*ÄÅÅ™åÏÏÏÇ7ðüóÏC§Ó¡µµ÷îݼöÚkxï½÷ðôÓO#::*UßàŽ (»»»‹ÐÐPH’„³gÏâã?Æïÿ{¸ºº²‘ˆNÓ@!dÊ…ŠŠ ˆ¢ˆ®®.ÔÖÖbÆŒˆˆˆ@YY ²²ƒAîÁ2æÞ½{ð÷÷GDDt:"""°`Á€§§'€¾Å›½¼¼ Õj­ê°páBøûûC¯×cãÆèéé‘ë'Äö`B™ò¿‡õàÁøûûC«ÕbÆŒ8tèz{{QVV___L›6Í,ll,öïßÿùŸÿALL fÏžèèèéÐÖÖ†œœ”••¡½½’$¡§§­­­l ‹Bˆýáççooo”––âÁƒˆŒŒx{{C§Ó¡²²Rñþ•)ÁÁÁxóÍ7qëÖ-”””àË/¿ÄÌ™3ñüóÏÛ¬ÃÁƒÑÙÙ‰ 6@§ÓA­Vã/ù ˆ˜Á! Bzh{ø„žcãGTTÊÊÊäw®ˆŒŒÄÍ›7QUU¥8nŠ››yälÚ´ [¶lÁO?ý„Ôj5$i訬¬ÄÒ¥KƒéÓ§C­V£££ƒ C,âX=X¦ÿÀ™ÂÿÈÉÎÄùìiO:Ó_Æ®3fÌ@ff&DQ4 °Ž[ëÁÊË˃———ü¿«W¯ÂÛÛ[ž†ÁÇÇ%%%‡Z­¶8=ƒ¿¿?.]º„àà`tuuá»ï¾ƒF£¡/'°ìl¢QÁQìLœÏžœ¦L‚]£¢¢ÐÛÛ ½^/¿˜>`uwwËÓ9È1 Ñ¿]]]qæÌ455A¥R!$$Û¶m“Ó“““‘ÂÂBL›6 o¾ù¦Yý›7oÆ7ß|ƒÿû¿ÿôiÓ°víZdggÓˆågôôôI¹üŒËZ„Fù-îO¡µÍu³£µ!™¬IƵGœßô&%Á¾Ö÷3Ý·c]¹áÙ™k¢€KåLT$;œjöÐ%°G€=,ìÁšÐ íJ,B!„‹B!„!„B,B!„'dÒ¦iøË;\“Bœ& Ž {°!„BƘIëÁzûí× i‚Á‰S$˜Ì‹e2×”iº$Y——,î¦sn™ËÛ¢‹$/«0P®-û’…}SYiˆ}˜å5•‡ÕtÉFÝòýŠ’d®[ÿtXÖó*·°PöÐéÃlM~«ñV4Õ­+ËX±sŸ^ýeH&^c¡Óß")§lQ’ä:Å!ìh¬Kß¾dÑn¢EÝMòɿӒî’Rw…œÑaRŽ(ù•dì#ýöì—ƒñw#‡µ˜>0ÝšÑ1KåšÖ§Hë²®`øà">>ÐÜÜŒï¿ÿ7n„(Š ùÞÞ^üýïG]]Ö¬YƒððphµZ´´´àòåË(((ÀÚµkiXÂËᑨ¤³ªJ'§ªdxt:RRR™™‰èèhøøøàСCˆ‰‰ÁÂ… QTT¤ÏËËCee%vî܉   E9‘‘‘ Ù}ûö!00...¸páÔj5°zõjžŒ{É]bÏ4¦žÎ ­KÓÒ¸¶‹™3g"##çÎC}}=6nÜhQöÊ•+˜9s¦"¸ŠK—.ÁÕÕ¯½öÖ­[‡'N ¤¤„ÍLFƒõ`Iv÷*_«¤©®'ß¡™¿uì²d¦å;XSÖo7mÚ„÷ßxá… Õj-Ê566"**Jqì³Ï>“ƒ¦ÀÀ@¼úê«rZ`` V­ZðóóCAAJJJ0sæL65a€Åk•tº˜…NNÿq2<==‘€ââbÌ™3gDy7n܈îînœ;wåå労ÀÀ@ž——ÚÛÛipÂËÒc§=<„Ú˃2èi]š–LãªT*¨TC¿áâï³  <<<,–©øu‚`> !#ñSG~B¶»!Â)¯§½Ð"دß:ö ÙNLËw·ìÞo,X€’’ÔÖÖ²©È¤À!BB!ÇòåËqóæMüíoêU« www466âæÍ›Ãö€ÂËì±ÓN‡%{ГïÐL„ß:.’ý˜–#Cvï·...رcòóóQTT„cÇŽA’$øúú"&&Ë—/gX£½*ò_„c­'ÿEÈhÐÎ…ÿ"tH¿]½zµÙU±±±ˆUS«Õxì±ÇðØc YÞË/¿lvlëÖ­lbòP°”B!„ÖpÖö¦¸Æ’=èÉ!‰ð[ÇE²Ó²÷Š~K,ëWEÉî4¦žÎ ­KÓÒ¸„0À"Nü0Èiý‡B`B!„L“ö/ÂwÞùÖ'€0Ì~ßvˆØqó²ñ!£€=X„B!c̤õ`½ýöÏ ­§),%yà38¹¥d"¯Üæ…Yšd’kû°EÓôÁW=%iPzÈ}É<]’#M”²Š2,”ešß¬ìaêQ %Ëõ‹’Q‚ü1M“ŒòŠË’ÌÓ¥þº¤~9É(Ÿ\ž¤,G)/˜Ôóò¾›•m)Q;(ʨËÈŽCë2t}Ëë·“qÛ@2i©ß÷%ã}#yc9˜ÈÊ|°–_„&Øvû÷ï‡ ؾ}»âxAArrr˜˜h–Ç`0 ??W®\ACCÔj5|||0{öl,^¼ÞÞÞ6×ÿ_ÿõ_hmm•ï5^^^ˆ‰‰Arr²ÅÅ£ q¨‹ÁÕ¸YÖŽ{ÒÕÞ}Ât•ØŽöâ›7oÆ|€ÂÂBÄÇÇš››ñý÷ßcãÆEQ!ßÛÛ‹¿ÿý飼«Ãš5k­V‹––\¾|X»víˆà“’’I’ÐØØˆÃ‡ãèÑ£xúé§yI Ž`±k<Ÿr©+™Äî‹ñÒ•=X°—,N‡””dff"::>>>8tèbbb°páB)äóòòPYY‰;w"((HQNdd¤Bvß¾} „‹‹ .\¸µZ„„³åx\]]áååðööFll,®\¹"§?~ÅÅÅØµk—|,??ùùùxóÍ7yÙp2ê%w»®¦t\ ÙÑO”gÙ‘OLº®’³_$»rØØXÌœ98wîêëë±qãF‹²W®\ÁÌ™3ÁÕP\ºt ®®®xíµ×°nÝ:œ8q%%%VåïÝ»‡ëׯ#,,Œ—bš€Bˆ½°iÓ&¼ÿþû¨¨¨À /¼­VkQ®±±QQQŠcŸ}ö™4âÕW_•Ó±jÕ*€ŸŸ PRR‚™3gÊ2ßÿ=rrr Š"z{{†ääd6 a€E!ľñôôDBBŠ‹‹1gΜåݸq#º»»qîÜ9”——+Òû^^^hooWKLLDll, µµÇŽÃÇŒ_þò—v›%œ‹Bˆ}ݸT*¨TCß¾üýýÑÐÐ`4ùùùYüןiy–ÞéÕjµðó󃟟¢¢¢’’‚ÊÊJ”––ÊyL1 l0X„Bˆc°`Á””” ¶¶vÜê¨z{{嬭­M!3žõXÄ$þDÚ“:îX±|ùr„……áoûòóóQSSƒææfܺu 7oÞ¶ÌÝÝÝhkkÃýû÷qçÎ|÷ÝwðôôDxx8`ÆŒhooÇéÓ§ÑÔÔ„‚‚ܺu‹á¤pšb‹e©+±`gNÓàÔí8Åmçââ‚;v ??EEE8vì$I‚¯¯/bbb°|ùò—™››‹ÜÜ\}½U¡¡¡Ø¾}»<ä8}út<ùä“8uêNž<‰ùóç#11………¼d8ãÙ•žž>)§—ÊáR9\*‡Kå :=—ÊáR9·T!S ‡"ä¿8Ø+Ä,ÚyDº lGžj„0ÀN4:n–µ£ŸÈ‰FíÉ'&]WN4ê.@,B!„X„B!„!„B,B!„»bÒæÁzçOi}2¦O jšÂ~ú?|äã³6!<«!„Bˆ%&­ëí·_ëûb:ÙœÉäžÊLÒ1´¼di0™ÁЂüpºô¥KÖt‘,Õ?LºdË×%aèr`¥\I쟼P´’_T~,–eZÆešÊÀDθžau³ò±<[§9 #‡AÛZK7›ís(9I11äÀ,¤’‰.’-[…KX–tgkù%E~ØPŸµ4É‚.’ÑvÐDæòRÝC¥[*W®Ï’¼(YÖ Ã¸¬O„kÉ%D =¨²Áú¼£f“øš\©$óyW•éýÛb˜¹M1º}cÝ­êt‡¥ ”„lû—QßS.^¼ˆ¬¬,ìÞ½›wvâÜ!ãŠÓ/2›D0zN d„ìß¿‚ `ûöíŠãÈÉÉAbb¢Yžžžœ>}W®\AKK ÜÜÜ0cÆ ¬^½ÃÖùÑG¡¼¼\Þ÷ôôDdd$’““áããÃF!CÂ!BB!SžÍ›7£ªªJ±®_ss3¾ÿþ{lذÓ¦MSÈ÷ööbÿþý(**ÂÚµkñûßÿÛ¶mƒ(ŠøË_þ‚;wîX­Ë`0ô?ˆÇ[o½…·Þz ?ÿùÏqïÞ=ffNá2‡Çé7Oñ½`Á”””àîÝ»&zKÈÏÏÇôéÓÍÞϲÍû¹··—÷2$"$„âp,_¾ׯ_Ç'Ÿ|‚ääd„……¡­­ §NBCCvìØaS9===hkk´µµáĉÐh4ˆŽŽVm„8x€ÅN†™§æãµÙÄIþE8Y÷ þ‹pœ~óŸ8ÖÅÅ;vìÀ©S§““£˜hôW¿ú•MÀ… pျ»;±mÛ6øûûÙ‚ïM çHzzú¤œ"\*‡KåŒëR9°°D —ʙԥr”æáR9\*gl—Ê!dªÁ‰F !„B` Ù!7ä.3OI$;²‰“ü‹p2‡yJŒÃoæõ•g °ø/B‡ ^Ý&]ÀÞuå¿ !`2ðxML¹&a›B`B!„Ñ2iÓ4¼ó·´>ãr2Y`/±»ðÌ'„wJB!„IcÒz°Þ~{cßrEzv IDATkó7a¼ç’jn¨atéߎݵ°]Ûü÷ mk—*ÓuÇÇ6m×ýû÷Clß¾]q¼  999HLL4ËÓÓÓƒÓ§OãÊ•+Š™ÜW¯^mÓLî}ôÊËËûn–..ÐétˆÅŠ+l¸q ž–#ÁàjmKжö©2]w|l3Ñvݼy3>øà">>ÐÜÜŒï¿ÿ7n„(Š ùÞÞ^ìß¿÷îÝÃúõë*¯Eø—¿ü/½ôÂÂÂ,Öe0 V«!âãã‘””„ÞÞ^”––âðáÃððð@BBˆ8Q€Å,vжT™®;!¶™h»êt:¤¤¤ 33ÑÑÑðññÁ¡C‡ƒ… ¢¨¨H!ŸŸŸ;wî`×®] ”Ëxá…ðá‡âðáÃøÍo~ÈÈÈ@gg'BBBpþüy¸¸¸àø@£ÑÀÓÓ‹‚‚ܾ}[`ݸqYYYhmmExx8-ZDç"ìÁ"ì m[åÑè:YAÙD×koóãÆÆÆ¢¸¸˜7oêëëñÛßþÖ¢ìåË—-WƒÏá–/_ޝ¿þµµµ ”””ÀÍÍ /½ô’ÕúËËËQ__???ùXkk+¾øâ ,Y²ñññ¨®®FVV¯kÄÁ,B!ͦM›ðþû¢/¼ð´Z­E¹ÆÆFDEEYLÓëõ$ r€åêꊴ´4¨Õj…ìùóçqá  h4,[¶LNÿá‡àçç‡ääd€¿¿?îÞ½‹3gΰ±`B!ö§§'P\\Œ9sæŒY¹fÁ,\¸+W®Äƒ››‹ððpÅ»[õõõ Uä±önq.8!„ûºq©TP©†¾}ùûû£¾¾ÞbZ}}=A€^¯—i4‹²nnnðõõEHHž{î9 ¤¤„@`Bq>,X€’’ܽ{Wq\’$äççcúôéfïg ‡««+–.]ŠììlùØôéÓQUU¥»sç€0À"„S$'«×Y¾|9BCCñÉ'ŸàêÕ«hmmEUU>ÿüs444`óæÍ£*7!!øé§Ÿäý¦¦&dgg£¡¡?þø£Ù?‰sÂiˆ¶åÕŸ¶µO•éºãc›©nWìØ±§NBNNŽb¢Ñ_ýêW6M4j ,Z´ÇÇüùó¡ÓéðüóÏ#++  ÅOêÚÖU¦ëŽmhWBœ)Àâðà8Ú–& míSåÑN4ê Á ÄK! °!„B`B!„0À"„B! °!„B¦ “6Ö;ïü“ÖWð>Ú¤|,"„)/Õ„B!c̤õ`¥§§›=˜<œo…¤«lÜZÊgk}ÖÊ‚I9Cégi¨òU6êd‹îVe…¾iÄTª¾­éG¥LSõ¢HAÕ·5–3Þ´¼aêTJÙò-Ê Jy…^tŒÊVÖJ}–Ò­ývke *@P ýß­È?^enlk†STˆ!”WYnÄ!fÁØ*•²Kº[4†Ñ}(Ý,4¢©ƒY4¾ÝÏÁÚY¤ã+“­W‚¡®’Ãé3ÝÇãÊ„!®¶æú¼óÎ;¼+X„;EpØÊˆÓÐЀŒŒ ÔÖÖB¯×c×®]£*'##غu+J`BqnrssáêêŠ7Þx®®®“ªKQQŽ=ŠÝ»w³a`Bœ ر4¡•'¦¹¹³gφN§›üSŒ«ŠXÄìi½2ÞÊÇÑz4.™Dnݺ…“'O¢®®‚ <<©©©ðõõEzz:A@uu5Nœ8U«V!66{öìÁsÏ=‡sçΡººxöÙgÑÙÙ‰#GŽ ¡¡xæ™g Õjõ={gÏž…Á`À‚ šš •ªï½²àÛo¿Å7`0‰ÔÔTøûû£¬¬ ‡‚ ²^«V­ÂêÕ«Ùˆ °»>ˆ‰î¿`ÉÃÄQÃXoTÆek±¡»»Ë—/GPPººº››‹Ï>û ¯¿þ:Þzë-ìß¿111HLL„««+:::ÇGJJ t:222pàÀ¸¹¹!55_|ñrssñä“OÊu•––ÂÛÛ/¿ü2šššðå—_"88qqqúÞÓjjj‹/¾777|÷Ýwøøãñ»ßýáááHIIÁñãÇñÆo@’¤I²$“§i Ä#fǬŒ80óçÏǼyóàëë‹   ¤¥¥áîÝ»¨««ƒ——T*\]]áåå¥h ½^eË–¡¦¦«V­Bxx8‚‚‚‡ÒÒRE]ذaôz=fÏžÙ³g£¤¤ÐØØˆëׯcóæÍˆˆˆ@`` žyæÜ¿ÅÅÅP«ÕpwwxzzšéCœö`‘qíƒà¡µ4K&‘ÆÆFäææ¢ªª $ ‚  µµVóÊß===@!ïéé‰öövEž€€ Ã{yy¡®®@ß¿Õj5BCCåt­V Ô×׳¡,2²>:CÅ!B2uùä“Oàëë‹´´4x{{C’$¼ÿþû0 Cæxo €4™3})Ý8Ýš !ÃÁ!BB!SšŽŽ466båÊ•ˆŠŠ‚^¯—ß±2¼Æ>À×ëõEwîÜ1Óo gL­V3 # °ÈøöApˆp "Òz˜VFˆÍxxx@«Õ¢°°MMM())Avvö°”¥ çaÌ™3ß|ó ***P[[‹¯¿þÓ¦MÜ9s>>>èîîFII :::ÐÓÓÃFd€EÈØÞ"%;Ò•³4ÆeÈKÆÀe[¶lAuu5öîÝ‹ììl$''ËiCå³åØHyê©§ŒO>ùýë_!¶mÛ&-†‡‡#!!_}õÞ{ï=œ9s†èŒ~›žž>)÷$®Eȵ¹á$­E¨øá㽡ʲq-Bp-B®EH؃EØá1)yÙ_2ÑÝ|"$„XdòïÇSðöÊÛù8ZÆ%„X„+[!„0À"„BqØgÙÉzÉB!ÄQa!„BÈ3iKå\L¿ ï½Y©ÿïºT ‚Ø¿íûòvPFm”> #˜ä3ÿˆŠz³´ïJõåšë&šé'Èé×£²b•™ Pú?½€`P‰ýÛþã¦2òqѺü€œ ~Ô¦e¥©¬Ô§(Ë0X×À1µQš¬‹Á¨ “2Ýk¿Õ H‚ªï£RIpQjú·Ôú>*@-ƒéࢠêOS ƒéµ ¥õ•gœî¢äº\úÓdUýå˧™–¥ØuW™èn¬›±îu©‡Ó]èKWAèó\©ÿOô¢|¢ˆP~ä4’4xLBÞ4Ÿh!¿išqy’•òLë“,è. £»hEwɆº†³ÕP²Ò2#Õ]’Ãt“¬Øªÿ#*>z% W„¼í’€nqà#¡GLë‘€®´~9ãü"€z£é{a!dÊ#Œ›ð8ä'd„´´´ ==µµµ4a€E!„ŒÙC„ÀÈž0À"„L0Ò¸ C~BFãv\\™L1\h‡y~£ œÕ¤=ˆ8>·nÝÂÉ“'QWWAŽ””øùùY”/..Fvv6îÝ»‡ˆˆ,Z´ÄîÝ»áîŽdff¢¼¼ðõõÅŠ+ðè£*‚¶Ó§O£°°mmmÐëõX¹r%æÏŸÏ! °œèù&“ Ž¡§0‹w°èŽdŒéîîÆòåË„®®.äææâóÏ?Ç믿n&ÛÜÜŒ/¿üË–-C\\jjj­FìííEHHV¬XWWWܼy„ŸŸBCC§NÂåË—±iÓ&øùù¡¼¼_ý5<==ÉF! °a¬8%ƒ+2˜ö¥¥¥á½÷ÞC]]\]]i………ÐëõX·nÀßßuuu8uê”,3mÚ4$&&ÊûK–,Á­[·põêU„††¢··§NÂŽ;ðõõEEE~øáX„–óÀ§5)‡‰ÐØØˆÜÜ\TUU¡££’$A´¶¶búôé Ù††„„„(Ž ôJ Š"N:…«W¯âþýû0 0 Ðh4€¦¦&ôôô`ÿþýŠ|ƒÁÁÁl‹}ä¡LÊ!ÂÑEjtG2Æ|òÉ'ðõõEZZ¼½½!Š"öîÝ ƒÁ0ªòΜ9ƒsçÎ!55Ðh48zô¨\^ww7`Û¶mðööVÞ8]xë$ °!„Ø9hllÄæÍ›(//·*¯×ëqóæMűªª*Å~ee%æÎ+¿Ô.IåÞ°éÓ§ÃÅÅ­­­$ °œð8­ImÐSI1|‹L1<<< ÕjQXX///´´´àرcV羊G^^¾ûî;ù%÷¢¢¢>?ïÏãïïŸ~ú •••pwwG^^ÚÚÚäËÍÍ ‰‰‰8zô(DQDDDºººPQQwww,Z´ˆ C`9¼«‹I9D8º€îHÆòB°eË|ûí·Ø»w/ôz=RSS±oß>9`2¶|}}ñüóÏ#;;çÎCxx8V®\‰#GŽ@­VV®\‰ææfüãÿ€F£A||<æÍ›‡ÎÎN¹œ¤¤$xzzâôéÓhnn†»»;‚ƒƒ±bÅ 6 a€E!Äþ™9s&~ûÛß*޽ýöÛ¿Àœ9s0gÎyÿäÉ“˜6mšüþ”‡‡¶nÝ:l½K—.ÅÒ¥KÙ„–?ãÑÎjRbÆùóç­V‹ŠŠ œ={–a€ExW›2&œGOzq$qòäI.ªú¶eäûdDÕàw@R¤äÌß÷V·$HýuH&å î›Ê‹F2Ç`*k’n,?ðRº©>ƒé&åB©†¬æzÁŠ®¦e™¤¯›ÔïÖÓa|\Þ7Ýö¥ ‚bßÖ¨,A  /]èOLŽ«ú¿«úÓU¡êß rÚàÖ8]e–.B jA„JÞŠò¾j`kôÝXFH“Ëü(òB„º_YF¡†aðØ€ŽÆy –eºº_÷A9ã(_!TT’|µ1Ú ú·}5TPõm¨û?}©ÿö_ñ®L؃E±S§«˜8iËáÝwßeX„B! °!ö‡ät'l9ƒÁ0¡ù1…N• åF9ë%G³ÆÇ®îÃé9õgEìÅÆÙ”·nÝÂÉ“'QWWAŽ””øùù¡¥¥{öìÁ–-[pþüyTUUaãÆrÞââbdggãÞ½{ˆŒŒDZZt:àøñã(..Æ’%KpòäI´¶¶âí·ß†$I8}ú4 ÑÖÖ½^•+Wbþüù<ý ¬©þD(PÙ PNr‚šŒ;¶4ÊŠ¥ v i~‡ó~‚-¶gSvwwcùòå BWWrssñùçŸãõ×_—eŽ;†äädÃÅÅ·nÝBOON:…gžyjµÿüç?qàÀüò—¿”ó555áÚµkغukÿŸ—€S§NáòåËØ´iüüüP^^ޝ¿þžžžˆŒŒäMŒ ‡ qÆèÞ¹*&ÐróçÏǼyóàëë‹   ¤¥¥áîÝ»¨««“e–-[†yóæÁÇÇ^^^Q±aÄ……!88O?ý4***PUU%ç3 xúé§„ÀÀ@ôööâÔ©Sؼy3¢££áëë‹ØØX,\¸?üð‚Ø{°ì¹Ê‚C„“iw:÷é7±C„ÈÍÍEUU::: IA@kk+¦OŸ 1ïEP©*ïëõz¸»»£¡¡A>îãã­V+Ë455¡§§û÷ïW”e0ÌÓŸ0ÀšêO„"œå8D8>wl:Óé7†?ùäøúú"-- ÞÞÞE{÷îU¼”®ÑhFU¶i¾îînÀ¶mÛàíí­¼iºð¶I`Bq:::ÐØØˆÍ›7#""P^^nS^QQUU%÷V544 ³³Sîõ²ÄôéÓáââ‚ÖÖV¾oE`9e'•‡GÁ˜t‰ŒÆ®Ò$¸‡ÇÊe†µÝ8šÒÃÃZ­………ðòòBKK Ž;&¿>*• ß~û-RRR R©™™‰ððp‹Ã‰¸¹¹!11G…(Šˆˆˆ@WW***àîîŽE‹ñ&F`9ô=ŽÊ‚C„“uÇæ¡3~“=D(¶lÙ‚o¿ý{÷î…^¯Gjj*öíÛ'YÖ‚-FƒÇ{ Àýû÷åi†#)) žžž8}ú4š››áîîŽàà`¬X±‚70b›ß¦§§Oʆkr-B®E8Ikš¬8qkBþ\‹Ð¾Ö"HãZ„„اi°çN* Û†½& óiL 4YC„öÝÐS{ˆÐIÎB`9Ù=ŽÊb܆€8e“ýHšÿ ¥1Ñÿ"$„!dj#8]Å„-G,B!„{gÒþEèóÿ¼åpÆ”†Ù9bÿ‡ !+Œ2-ºÆÖʰåw‡¦š]1F¾7’¶yÝF÷±ð›©l×ÑúßxÙh¼®YSÏX^KLŽ¿óN:ïÊ„=X„;…o,B,B!äa)**»ï¾KCX„q‚/ægt{‰ŽO&®EH†G ®„Ð÷&—[·náäÉ“¨««ƒ GJJ üüüPVV†}ûöa÷îÝpwwÔÖÖâÏþ3Þ|óM´´´àСCééé«V­ÂêÕ«‘žžŽ­[·bîܹr]ï¾û.RRR‹––ìÙ³Ï=÷Î;‡êêjàÙgŸEgg'Ž9‚††DDDà™gžV«ddd ³³8{ö, ,X€ÔÔT¨TìÛ`€E`_ ;ò"Í è{NLww7–/_Ž   tuu!77Ÿþ9^ýõ>·¶°ØóÀ±`ìøñãxã7 I\]]GTÿñãÇ‘’’N‡ŒŒ 8pnnnHMM…F£Á_|ÜÜ\<ùä“ržÒÒRx{{ãå—_FSS¾üòK#..Ž ê0Œ&ÄBìŒùóçcÞ¼yðõõEPPÒÒÒp÷î]ÔÕÕ ›W­VË=[žžžðòòq€•˜˜ˆèèhèõz,[¶ 555XµjÂÃĸ¸8”––*òxxx`Æ Ðëõ˜={6fÏž’’6¦“À,âX=ìA ô=‡¤±±¹¹¹¨ªªBGG$I‚ hmm…F£÷úåïžžž€€€Å±öövEž€€EÏš———M!a€Eœ2 ô½Iæ“O>¯¯/ÒÒÒàíí Q±wï^ ‹½QƒÁ¶ÓÁÂТ¥¼ÆïM ä1=fú"½é»V–dˆãÂ!BB!SšŽŽ466båÊ•ˆŠŠ‚^¯Çƒät­V I’pÿþ}ùXmm­¢ µZm1¸ÑjµŠ|èéé6#d8؃ElxÄ£®{Zè{v†‡‡´Z- áåå…––;vL|üüü Óépüøq$%%¡±±yyyŠ2|||ÐÝÝ’’A£Ñ@£Ñ **  ƒ(Šøþûï¡V+×´˜±'Š {°ˆm7dêÊ@€Ð÷&Ëe[¶lAuu5öîÝ‹ììl$''Ëéjµ[¶lACCþüç?ãÌ™3HJJR”Ž„„|õÕWxï½÷pæÌÀúõë¡ÓéðÑGá믿Æc=föN×PÿP$Īߦ§§OÊek‚kr-ÂÉY‹ðaêáZ„\‹kÂ,â„=|¨dO }‹ð†ÌàðjA0Jë+Ï8ÝE%Èu¹ô§ȪúË7–5N3-K±/ ê®2ÑÝX7cÝêR§»Ð—®‚Ðç¹Rÿ´E¢|¢ˆP~ä4’4xLBÞ4Ÿh!¿išqy’•òLë“,è. £»hEwɆº†³ÕP²Ò2#Õ]’Ãt“¬Øªÿ#*>z% W„¼í’€nqà#¡GLë‘€®´~9ãü"€zÓù a!d*#Œ›ð8ä'Ä„}ûöáèÑ£#ʳgÏäçç?tÝÕÓÒÒ‚ôôt³µ‡¢¨¨ï¾û.–!„B,¡ÓéðÖ[o! À)W{¨‹!dÂÆMxòbG‚///¨TŽ{;E‘ = \h‡9Íig5©@"Ž(ŠÈÌÌÄ¥K— V«‘ /èÜÞÞŽC‡¡¤¤ÞÞÞX³fYþÜÜ\¡­­ Z­óçÏGjjªMu÷ôôàСC¸zõ*<<<°råJÄÇÇè"ܳgvíÚ…   @qq1²³³qïÞ=DDD`Ñ¢E8xð vïÞ www¹Ü[·náèÑ£²ÜSO=///9ýÂ… ÈËËCSS´Z-æÍ›‡ 6Z[[‘™™‰ÒÒR‚€˜˜¤¦¦Êù?Žââb,_¾¹¹¹xðàfÍš…´´4¸ºº"##eee(//G~~>AÀþð´´´`ß¾}ضmrrrPWW‡íÛ·cÚ´iÈÊÊÂ;wÐÓÓ½^'žx3gΔõ-((@~~>îÝ»777DFFâù矷Z—,âp}ÄV“ Ž¡§0‹w°èŽdŒ)**B\\vî܉êêj>|>>>ˆ‹‹ÃÁƒÑÖÖ†W^y*• ™™™hoo—ó^½zùùùxî¹ç0}út´µµáîÝ»6×——‡5kÖ`ÅŠøé§ŸðÏþ3fÌ€¿¿ŸË ƒ'Mss3¾üòK,[¶ qqq¨©©Avv¶Bf hËËËóÏ> øú믑gžypþüydeeaݺu˜5kºººPQQÑw®J>ýôS¸¹¹á•W^(Š8rä¾úê+¼üòËrMMM¸~ý:¶mÛ†à‹/¾ÀéÓ§‘””„””466" III$ žžžhii;v ÉÉÉðõõ…»»;Z[[1{öl<ñÄP«Õ¸té>ýôSüîw¿ƒN§Cuu5Ž=ŠgžyáááxðàÊËËÀj]ìÁ"„8Wøý°%ƒ+2èt:¤¤¤üýýq÷î]äåå!22·nÝÂÎ;ؼy3þô§?ÉyïÝ»oooÌœ9*• :¡¡¡6×=kÖ,,^¼ðøã#//¥¥¥r€%IƒN_XX½^uëÖɺÖÕÕáÔ©Sf=r7n„¯¯/`É’%8qℜ~òäI<öØcXºt©|,88PRR‚ºº:¼ù曘6màé§ŸÆûï¿êêjÙðÔSOÁÕÕ°hÑ"””” )) îîîP«ÕÐh4ƒ5kÖ(z§<<<äºôk×®áúõëX²d Z[[áêêŠÙ³gÃÕÕ:N–®.XdM²Î IDATŠÃ§5)‡‰f¶Ÿ——‡úúz¨ÕjEP¡×ëCqóçÏG~~>öìÙƒ˜˜Ìš5 sæÌ±ù½©ÀÀ@ž———¢‡Ì˜††….,sF®LËlooÇýû÷eµN'W0}út¸»»£¾¾^®ßÇÇG®†Ó[q-³ßÐÝÝÜÜ\ܼymmmE½½½hmmÌœ9:N¶qLL æÍ›Fã´>ËË9û(ÈDôäL!=9DHœN‡7Þx%%%¸}û6233qöìYyHq8LeAPôZKeÊ7f—±¹5?ŒÞ¦QVVJKK‘œœ ???¸¸¸à‹/¾€Á`¸¹¹á׿þ5ÊÊÊpûöm?~ÇÇÎ;Á®3ÁB™òTUU)öïܹ???èõz TWWËi èììTö&¸¸`öìÙHMMÅŽ;PYY9¢÷°lE¯×+t±¤ûp¸¹¹ÁÇÇ%%%VëhmmŽ{÷äcuuuèììÑtjµÚ怫²²±±±˜;w.ïkt3gÎĺuë°k×.´´´ ´´tÄu9 ìÁr8Àã´&µAOi$Åð,2immEVVâããQSSƒ‚‚¬_¿z½111øæ›oðä“OB¥RáèÑ£Š˜¢¢"ˆ¢ˆ°°0h4üøãÐh4ãò/¶øøxäååá»ï¾“_r/**ê;ÇÛO¬Õ«WãÈ‘#ðôôDLL ºººPYY‰¥K—"::8pàRRR`0™™‰¨¨(ù=-[ðññÁ;wÐÒÒWWWxxxôÂ!\»v ³gÏÐ÷¯Lc¹7n ¹¹‘‘‘pwwÇÍ›7å`ÐZ]#±,2‰ð®6.&åáè>º#c-Z„ÞÞ^|øá‡P©TX¶l™øsçÎÅš5kØPDÆ¡ÞÁbpEŒƒmBˆãPTT•J…;w"55yyy¸pá‚Uù'N`Á‚xýõ×1kÖ,8p<ï:/¼ð~÷»ßaõêÕÈÉÉÁÕ«We””” ±±/½ô^|ñEüìg?CCCª««e™ššÔÕÕág?û‰(à!qHlâXèt:yvtܽ{yyyˆ‹‹³(‹ Ö®]‹sçΡªª 111P«ÕX½zµ,ëããƒÊÊJ\½z<òˆ|ÜÕÕiiiŠ¡Áèèh\¼x!!!€‹/bÆŒðñña#Ç °8DH!ŽIXX˜Ù~^^žÕk~`` "PrssC{{»|¬  /^Dkk+z{{a0dV†é{Wqqq8|ø0Ö¯_ApåÊ.‹C?ÀbpElB@¥RY½&\¾|ÙÙÙHIIAXX\]]qæÌTUU)òXúgâœ9s V«Q\\ •JQ1þ|œ8v€E!Ä11 ~îܹ??¿Q½oYYY‰ˆˆ$$$ÈÇš››mÜ-Z„‹/B­VcÁ‚pqá­”XðGú1|±™ ÀÞ+B‹ÖÖVdee¡¡¡—/_FAAâ_#ÁßßÕÕÕ¸uë‘““cÀ E\\JKKqëÖ-¾ÜN¬Â!BâpˆÇbÑ¢EèííŇ~•J…eË–!>>~TÛñññ¨­­ÅW_}A°`Á,Y²7oÞ´9@ ǃÊÆ!–ý0==}RîB\‹kŽÄŽé\‹kr-B®E8ÿýßÿ%K–Œº°‹½„½™„ÒO{{;®\¹‚¶¶6ÄÆÆÒ Ä9,ÞT !„Œ'ï½÷<==‘––www„8G€EÈìÍ$„Œö4ŒI&M@!„2Æú“õ’;!„Bˆ£Â,B!„X„B!S›I{ÉÝüEAÁh+ÈûƒÆÙ’®ünž¦,˶të2æ“Ù ŠeÝ-•?œî°’ní·¥ûhì +¿Ë–ß=vª1¤.¶ù€¥6¶lkÛì(ŒÂ†óχÑmtº›ûÔxû+†±£0 ©î¶úßHuËëÀpm<Ò6·Õ'Fs=:}Ó¦wxW&ìÁ"„Ø+\RŠØûöíÃÑ£G­¦ïÙ³ùùùVßPdddà³Ï>·ò¦ÞÑÈ;ìÁ"„BÈØššÊ©i`B& ìÅ"ıpss£`ûC ®„Ð÷&Q‘™™‰K—.A­V#!!IIIeóòòpñâE477ÃÃÃsæÌÁºuëàêê*ËTTT ''UUUP«Õ Ö-[,ÎÎ~ãÆ 8p7nÄ£>j“¾ÇGAA }ôQ¤¦¦B­V[”MOOÇÖ­[1wî\ùػヒ””y9žÖÖVdggãöíÛHMM…€¾a¿ÎÎNlݺpõêUœ8qMMMÐh4ÆÏþsh4¹Ž³gÏâìÙ³0 X°`RSS¡RñÍ!X„=v¯+ž'ÄVŠŠŠ‡;w¢ºº‡†âââÌ=\°aÃøøø ¹¹GŽÁwß}‡'Ÿ|PSSƒýû÷#..N|JKK!Š¢YY?þø#Ž9‚-[¶`Ö¬Y6éZRR¼òÊ+hiiAFF´Z­Õ€p8 þñ <<¿üå/¡R©pòäIüãÿÀ믿n¸Ý¿@rr2æÎ‹îîn”——+†KKKáíí—_~MMMøòË/lÑždt0T%Ä)Bì N‡””øûûãÑGÅÒ¥K‘——gQvÙ²e˜1c|||…¤¤$\½zUN?{ö,BBB°aÃB¯×cñâÅÐjµŠr ™™‰_|Ñæà \\\ðÔSOaúôé˜5kÖ¬YƒsçÎú·_¹r’$!-- ÐëõHKKCkk+ÊÊÊÌäÛÚÚ IæÍ›`ñâÅŠ<lذz½³gÏÆìÙ³QRRBGC؃E¬Çƒ=„¾çˆ„……™íçååY|±ûöíÛ8}ú4ÐÕÕQa0ÐÓÓFƒÚÚZ<òÈ#CÖ÷ÓO?¡½½¯¾ú*BBBF¤k`` \\\ºvww£µµ:nÄ¿ýîÝ»hjj¿ÿû¿+Ž÷öö¢¹¹ÙbýQQQØ»w/¢££ùóçÃÃÃC– PL¡ãåå…ºº::,2ñ="d @è{SŸ––|úé§X¼x1Ö®] TTTàðáÃ0 Ðh4ŠàÇÁÁÁ¨©©Á… F`øŒÌýÆ`0Èß»»»‚gŸ}Ö, ôôô4Ë«R©ðÒK/¡²²·oßFAArrrðÚk¯Éïl™¾k%ÿ…8ÆpˆBÈ”§ªªJ±çÎøùù™'ÕÕÕ$ ëׯGXXüýýqïÞ=…L``à°Ãa¾¾¾Ø±c®_¿ŽÌÌÌéz÷î]ôöö*tuuuµÚ{¥Õjqÿþ}y¿±±===Š`¯±±Z­~~~ŠÏPÿ ÇêÕ«ñë_ÿjµ×®]£#1À"ìñp]' >¥Ò÷ìÖÖVdee¡¡¡—/_FAA–-[f&çççQ‘ŸŸææf\ºt ……… ™+V ººGŽÁÝ»wQ__óçÏ£££C!çïï;vàÚµk#šÔ`0àСC¨¯¯Ç7püøq,Y²Äª|TT PSSƒªª*üóŸÿT¼¸¾páBhµZ|öÙg(//Gss3JKKñí·ßšÝ©S§P]]ÖÖV\»v ˜>}:iá!±ñ†Ì!B„¾7y,Z´½½½øðáR©°lÙ2ÄÇÇ›Éaýúõ8sæ Ž;†ÈÈH<ñÄ8xð "pÚ¾};Ž;†?ü¡¡¡§`Ðëõx饗ð·¿ý *• ÉÉÉÃê|ôÑGò4 «W¯¶*¿~ýz:t}ô¼½½‘ššŠšš9]£Ñà•W^Á÷ß/¾ø]]]˜6m¢¢¢,ö`¹¹¹¡¼¼ùùùèêê‚Ö¯_˜˜:ÒD^iÓÓÓ'åq–kr-B®E8YkŽfÝ9®Eȵ¹!!#C„ÄÁz<؃`[O ¡ïBÆoÈ"$„¾GL§J0æ¿ø"""h$‹ç„A!£e×®]VÓ¦M›FX„BÈHñóó£ˆm²“õ’;!„Bˆ£Â—Ü !„BƘI"4›¦aèðÚ–>ô¿ÚǧLÓß ØX†F™ÇÝGckeØò»ÇÃNSÍ®#ßIÛ<Œn£ûXøÍT¶ëhýo¼l4^׬‡©g,¯%&ÇÓßIç]™°‹b§ðwBa€E!ĹٷoßKÕìÙ³ùùùV! °!æðo-„‹L8‘;!ô=2n Áá>>˜9s&Ö¬Yƒ~øAQvll,,X???¬]»ÝÝݨªª¢M0ìÁ"ŽÕãÁÞô=‡$,,Ìl?//’dþ¯Û·oãôéÓhhh@WWDQ„Á`@OO4 jkkñÈ# YßO?ý„ööv¼úê« ±IÇÎÎN´µµ)äU*BBBÌô4-³¶¶]]]øÿøÅñÞÞ^477Ë2•••8yò¤œnúÛ 00PNwuu…››ÚÛÛéD °!„ÑÑÒÒ‚O?ý‹/ÆÚµkááኊ >|ƒ..Ãßú‚ƒƒQSSƒ .Ø`„`h€îînx{{ã•W^1 ÆzÀº»»±fÍÌ›7oÈòÞÇ’Ÿ©Áb J`öx8¦®“{Zè{vˆé×;wàççAP¾ºº’$aýúõò±+W®(dQRR‚Õ«W[­Ï××ÉÉÉØ·oT*6lØ0¬ŽîîîðòòBuu5"##ôõ0ÕÔÔ(^T·еµµAøúþÿìÝ{TTwžïý÷®â~‘K€Ü$â]ƒ(Q¢1Q“&¨ÉÉLF;'É\LOOϬ>^ϳž'äyÖÓ«ÿêãšžYÝçxÖjgŽÓvŸžDcgoMÔ$b:’xA”ûEäVPUÏ›*(„‚Ï‹µ×®½¿ýÛ¿úí½«¾õÛ›½cFÍÓÜܬNû ]ƒ%ãûBV]ˆö½)ÔÞÞÎñãÇijjâÒ¥K\¼x‘ÜÜÜùbccq:œ?žÖÖVÊËËùâ‹/¼òlܸ‘[·nñÿñÔ××ÓØØÈgŸ}FWW—W>›ÍÆ›o¾ÉåË—Ç}ãѵk×òÉ'ŸPQQASS………ôôôŒ‡[°`©©©üö·¿åÚµk´µµQ]]ÍéÓ§¹uëÏ<ó åååÓÐÐ@cc#_ý5ü±viH=X""2íeeeÑßßϾ}û°X,äææ²fÍšùÉËËãܹsœ>}šôôtž{î9:ä8½ñÆœ>}š}ûöHrr²Ï[0ÄÅÅñŸÿóæ_þå_°X,lݺõžõ|ê©§èììäðáÆÁš5kX°`ÁˆÓv¾ìÞ½›Ó§Oóá‡ÒÙÙIDDéééDDD™™É®]»8sæ çÎÃjµçõŸ”cròËLÉï.=ìÿyØóÃ<¼ùQ?ìy¢¬‡=ëaÏã}ØóD>H]{ž1\.ÿôOÿÄŠ+ؼy³¢õ`‰x~B ÿ"Ѿ'ãÐÖÖÆµkט?>ýýý\¼x‘¶¶¶1oP* °DÄß)y`?ýéOGMûþ÷¿OTTeeeœÞçýÕÝg9îú††{Ú0FoWº3J}¼Þ›ÇÁȺŒ,Óœ7l÷È7°ž¡ù‰eûZϰr<óøLgdy÷Êã«ìáëö]·áíÄÈ÷ΰ62|·‘ïºá½G©ûÐz}Ÿ³î>ÚÕk}Cù ‹1êûô¹O¸†¡}¯ºû„çÑã3ÿðv>-ß°¼_ÛÈ×gÍàà³î#ëÆðü>êx¯º[†ï£ŒÇóþñ˜ÿÞ{ïé[YÔƒ%"˘eëy0û÷ï÷ÔE`‰ˆˆˆ(À‘‰åšeë™=t,™Q þ’©Úâpóü¸Ù²Ç;NŽ=Jyy9V«•œœ¶lÙ@ww7ÇŽã»ï¾ÃápžžÎ¶mÛ°Ùl”••QXXÈúOÿ‰ÂÂBîܹCZZ;vì0ŸóðÅ_PRRB[[ÑÑѬ[·Ž'žxB‡”<õ`ÉŒâò«Þ]ƒ5Y{ÁÔ¬Öõ‹ºüíÝ>reeeX,öìÙömÛ())¡´´€Ã‡sûömvíÚÅ_ÿõ_ðoÿöo8Nsù¾¾>JJJxõÕWyûí·iooçĉfúW_}Eqq1Ï=÷?úÑxöÙg)**¢¼¼\¬¢KD€¾’efŠŠŠ"??›ÍÆÊ•+Y·n%%%477óí·ß²}ûvÒÒÒHHHà•W^¡££ƒŠŠ sy§ÓÉ÷¾÷=’’’HJJbíÚµ\¿~ÝL/..&//%K–ÍÒ¥KÉÍÍåóÏ?WãËÑ)B™QtŠPtŠpfJII1]RRBcc#V«•ääd3-,, ›ÍFcc£9/00˜˜s:""‚ÎÎNìv;---|øá‡9rÄ+( Ñ!% °D\¸ü(ÈÒ)ÂÉÚ ¦ä=º\d¹\®²\èÆãa±XF jív;/¿ü²W æk9X""2cÔÖÖzMß¼y“ØØXâããq8ܼy“ÔÔTºººhnnfîܹã*;""‚ÈÈHZ[[Y¹r¥[`‰ ç_§§ªïa¦÷yèáLÔÞÞÎñãÇY³f ·oßæâÅ‹äååa³ÙX²d øÃøÞ÷¾GPP§NbΜ9,^¼xÜåoÞ¼™cÇŽLff&ýýýܺu‹žžž|òI}¸Š,™ÝtŠP_É:E83eeeÑßßϾ}û°X,äææ²fÍvìØÁ±cÇøÍo~ƒÃá`þüùìÞ½û¾Nï­^½šÀÀ@Î;ÇÉ“' $!!ÜÜ\}°Š,™yÞzë-óõ‹/¾8"=$$„;wŽºüªU«Xµj•×¼%K–ðî»ïzÍ[¹r¥NÊ„ÑÕ{2£øß)ÂÙ´ÞG·LÍjuŠPD`É åÒ=žD7X"³™®ÁQ€%""""ãû)[PP f‘ ¤,‘ 6e·i((ü÷X†Ç`Áé56î#}xÚhc_Ëw}£•…YŽ Ãû.ËðQ†eÔ÷7TæØyÇ®ûƒ´ëðe-8}l·¡ºŽÖVŒ(o¬º;½òŽ¿]‡¿*Ï»Í>Þƒg^ÏvçmåôÙcå5÷øFœ–{ï•w̲<Ó-îiË=ò_î^uñ(Ói¹wÞÁùÎaéÜ£ <Ë÷,õ9ï£-FÔ}xË·«1rýæëûhWƱ¿Œ·]ïgß»ŸvÑ>–Å€‚÷ ô­,êÁyöïßOaa¡B`‰ˆˆˆ(À™!‡A¦Ü4|TŽÁƒß:ïa–õ·÷úhk9ËúÃúDäÑp:=z”òòr¬V+999lÙ²€½{÷’MKK ,]º”;vP]]ÍÑ£Gijj"!!§Ÿ~šƒòƒü€ÄÄD5ªÌ¶Ë5EËúÛ{}´µ4¦`YXŸˆ<eee¬^½š={öpëÖ-Ž9Btt4«W¯ ¤¤„gžy†M›6ÐÛÛËÁƒY´h¯¾ú*ííí>Ôc‰Dü<ÀñE~~>6›úúzJJJÌ+##ƒ'Ÿ|ÒÌÿÙgŸa/½ôÄÇdz~ýzþð‡?¨1员†×`ùÓ ©©|¯Ú"3a‘ñIII1ÝÒÒb>ÇqÞ¼y^éÍÍÍ$$$0Ôœœ¬†”Ù`é¡¶ÈìÙDdbªD–ˆˆÈý¨­­õš¾yó&±±±£^SGCCƒ×/Cd–X:E¨-2{öŸöövŽ?NSS—.]ââÅ‹äææŽšåÊ•8NŽ9Bcc#W¯^åÓO?øœÐ…î2;,"Ô™={€ˆŒOVVýýýìÛ·£G’››Ëš5kFÍÌ®]»¨¯¯ç¿ÿ÷ÿÎÇlþ‡¡çuY"“E{™ˆˆLko½õ–ùúÅ_‘þãÿØçr©©©üà?0§¿úê+, QQQjT™–n4:k9ËúÃúDdú*//'&&†ÈÈHêêê8uê+V¬P–ÌÖK§§c-u£Qñ7wïÞ¥¨¨ˆ»wïÉòåËÍ»¿‹ÌÂKDDäámذ 6¨!dJè6 """"Ì(((Ð?^‰ˆˆˆL õ`‰ˆˆˆL°)»« à]óµa ‹÷xøp¯ôái£}-7ÞõVx—s¯úùš¾Wù£-w¿mu?ïó^md±Ü{»V?ÏeóUŸÑÊœNíú ûËÐàÂÀ=¸\àkp:ï=¾WÞñ–5<}¬¼÷ZÎ×ò÷ZÖsþð¼0z]F{0úºÇÛV÷ÓæSÝ®ƒmt¯òÇÛ®÷Ó÷Ó®ãy_À{úVõ`Éìñ07=~Ô7LÖ šEDD–ø—kj–õ‡õ‰ÈäÛ¿?………jQ€%3‹z°DDD`ÉS–ˆÌT‡C “B7‘iÏétrôèQÊË˱ZÖN$Û IDAT­äää°eË.^¼ÈçŸÎøC._¾Ìï~÷;¾÷½ï‘““À¿þë¿’’’–-[(..¦¢¢‚µk×röìYÚÛÛy÷ÝwÕÀ2áÔƒ%""Ó^YY‹…={ö°mÛ6JJJ(--%==ÆÆFººº¸qãáááTUU=T555ddd˜eµ´´pùòe^ýu¯‡A‹(À‘Y%**Šüü|l6+W®dݺu”””@hh¨PUUUñä“OrãÆ jkkq:¤¤¤˜e9vîÜIbb" j\Q€%""³“g€48ÝÒÒ‚Ëå"==ªª*zzzhjjâ‰'ž ¿¿Ÿ¦¦&nܸArr2æ²ÑÑÑ„……©QE–ˆˆÈhæÏŸOUU7nÜ 11‘àà`3說ª"==Ý+¿g°%¢K¦ŒnÓ "S­¶¶ÖkúæÍ›ÄÆÆb†yÖ7ß|ÃüùóÍ ëúõëÔÔÔ˜óD`É´¢Û4ˆÈTkooçøñã455qéÒ%.^¼Hnn.‰‰‰„„„péÒ%¯«¢¢‡ÃAZZšP9ݦAƤ,™jYYYô÷÷³oß>, ¹¹¹¬Y³ÆLOOOçÊ•+f0•@pp0qqq:%( °dzr¹ìÇ–Áý¯ÿuD¾M›6±iÓ&5ªL:"Q€%"""¢KDDDD–ˆˆˆˆ<8£  @ÿØ."""2Ôƒ%"""2Á¦ì6 ï¾;ðÂÝfxŒ‡áóÇJ¿WžÉ*sø{0‡QÖÃ(ezÍkF¯Ëˆyã©Ó}¶‘Ï÷=ŽvÑN÷¨û¨ít?í:Öû{˜v#ýaÚõaʼﺱï=È2ÃÛ×s{pü×÷2cµÁùdzÍ}ÖßÇϸë~?Çôx>FiW_ï{´6O»ŽwŸxàv­.À{ïé[YÔƒ%2­é^Xj‘qjkk£  €ºº:5†(À™°ºC±(Àý놚Fä~Ž =ÈT&ØÌzTŽ¡oÉjVÕUDûÞT@çΣ´´”ööv"""ÈÉÉaãÆÔ××SXXHMM ,[¶Œ¼¼<‚‚‚ÌeÏœ9Cii)ÄÇÇóÜsÏ‘™™©†Xúi>µÍjø[]l+Ðq2£œ:uŠÒÒRòóóIKK£³³“ÆÆFìv; 55•wÞy‡»wïräÈŽ=ÊŽ;8þ<çϟ祗^"11‘ÒÒR<ÈßýÝ߫ƕI¡S„2s¿íDM#3Boo/.\`ëÖ­deeCJJ ÙÙÙ\ºt‰þþ~vîÜI||<¼ð ”——ÓÙÙ À§Ÿ~ÊSO=Åòå˱Ùl<ÿüó$&&rþüy5®L"”q5«ê*¢}oª455áp8ÈÈÈð™–˜˜H`` 9/55—ËESStttššêµ\jj*õõõj\™43«KÁÕ¬oV—¾íè8™y=jQ€%""2‘l6\¿~}DZ\\uuuôõõ™óª««±X,ÄÅÅLdd$ÕÕÕ^ËÕÔÔ?ôÃC·i‰þa0ã~šëgä¤4«ßÕUûÁ¨t1¶Ž¿û¢ `Æ œó6 [·n5—]·n½½½œ8q¼Mî]»¼þƒP=X¢KDDf¥7²qãÆóçÎË›o¾9ú Ãà™gžá™gžñ™Í»ƒÏÇ™ 3ë,ý™õͪS„cSÓè8XúæP³jí{"¢KdZPw‚šFDD–ˆˆˆÈ ú1[PP žm‘ ¤,‘ 6e·i8ñù¦¡ Ãpß»ÈÀ0 ‹{ì0Ç`X,^iæ|ÏyƒËÏk1Ü«} 0–nž×szèþ)Þu÷^àNÃ+Ýûõ½Çî&ºg¾¡<†GÞQ¦Ýy½Ëõn±x§›Ë ¾¦]潩¼æ3”ŽW]\Ãò¸†­Ç5¬î®aó]æuF£¥ MãžöÌψ²<§‡–±<.a(ÿ@ºü.Î<.Ï´Áiò\Có=§¬ŸkpžÓãµGý].\Æå]¾gúÐzœù]Þõñ̃¹ŒÓ] ï²ð,Ó\Ƴ\¼ò ¦ •Çz†–s ›ö—Ó#ÍéôZkXþéQÒNsÚ5"¡iùó ¤{,ï~írŽÌ?2s¨ s<¼ ¼Öm–ë¼×4æksìdØ{À=0¸CÓNÇPÝ\žyCcœƒË¸Óû¦w>àNÇ_ü_»ô­,êÁ’e Oç:âøøEëþ¼O<êe§ÛZüù°ž}-4“Þq[[ÔÕÕi_XþË5l<ëèÑ)á/­ëòç}âQ/;ÝÖâχõìk¡™öŽu'wQ€5c~÷©K Ñ ,íxþ·×NÏùцŒÎÁS×2cèPÔ«|bÙt~r™wÝ\.ÿùþð§ºÒàš‚=â!ÞðLíè™ /gj"×#obçΣ´´”ööv"""ÈÉÉaãÆÔ××SXXHMMù,¼¼<‚‚‚ÌeÏœ9Cii©ù,Âçž{ŽÌÌLŸëúÿã°bÅ Ö¯_ÀÁƒ¹rå ?ùÉO âÎ;üüç?çþሥ¼¼œ .ÐÔÔDPPäçç››®ªªbÿþýìÞ½›?þ˜††Þxã ªªª¨¨¨`ݺuÓÝÝMVVÛ¶mãÓO?åüùó¸\.Ö­[ÇÓO?­ˆE–ˆˆÈÄ:uꥥ¥äçç“––Fgg'Øív8@jj*ï¼ówïÞåÈ‘#=z”;vpþüyΟ?ÏK/½Dbb"¥¥¥­­­üîw¿£µµ›ÍÆÛo¿Muu5~ø! , 99Y;‚Ñ)B™Öz{{¹pá[·n%++‹˜˜RRRÈÎÎæÒ¥Kô÷÷³sçNâããÉÈÈà…^ ¼¼œÎÎN>ýôSžzê)–/_ŽÍfãùçŸ'11‘óçÏû\ßüùó©®®ÆårQWW‡ÕjeåÊ•TUU=RóçÏ7óggg“™™iÖ+??Ÿ«W¯b·Û½Êݼy3=ö111„††šó·oßN||<‹-"##ƒææf¶mÛ†Íf#;;›¸¸8*++µ#øõ`‰ˆÈ´ÖÔÔ„Ãá ##ÃgZbb"æ¼ÔÔT\.MMMÐÑÑAjjª×r©©©Ô××û\_zz:v»Û·oSSSÃüùó™?>üã¸qã6l0óߺu‹ââbêëëéîî6¯çjoo'>>¸ˆ~Þ¼y#ÖmžÊÇbñîû7ƒEñêÁ‘éÝðhûBBBHHH ªªÊì­JOO§®®Žææfš››IOO0OQ†„„ðꫯ²gÏ^ýu‡W¹žA ù%<,˜2 Ãç<—KÿË«kJ¸fäªdfsé ‹šh\l6\¿~}DZ\\uuuôõõ™óª««±X,ÄÅÅLdd$ÕÕÕ^ËÕÔÔ˜½KƒAŒ§ôôt*++©®®fþüù„††ÇÙ³g‰ŒŒÄf³=hÝÝÝ<÷Üs¤¥¥ÇÝ»wµÑD=XžnÓ0©5×m¦] gÇa­Û4L¦€€6lØÀÉ“')//§¥¥…›7oRZZÊã?N@@‡¢¡¡ÊÊJŽ;FVV–ù_|6làܹs|ýõ×455qòäIêêêÈÍÍ n‡õÍŸ?Ÿk×®™Úà¼K—.y]…ÕjåÂ… ´¶¶RQQÁÙ³gGÏêšut Ö”üFÕm&­æ.ü®›%·i˜Ñ‡µn4:Ù6mÚ„Õj¥¨¨ˆŽŽ"##ÉÉÉ!007ÞxƒcÇŽ±oß>ó6 [·n5—]·n½½½œ8q¼Mî]»¼þƒÐW–Ëåò ¦æÏŸÏ… ¼æ…‡‡³cÇNŸ>Í… HJJ"//ƒ‹Áuͺ!SòÉ0{ŸE8þgNݳ ßÏôƒglb{¡ûy„ô,ÂÁ¯ºÉ}¡ïeõ,BÏç:gÕ³]§»Úz¡ˆ/3ä¡¿Ýht*~ÿ=øoS=*g²w¥Yò¨œ™ü~o4ª,X""""¢KDDDD–ˆˆˆˆ,‘ÙnÊnÓ°5§xf´ ûŸLŽéW%dðÈ/%7«Z^d¶Q–ˆˆˆÈ›²¬‚‚‚?òÀó^G†9œÞ÷ožî¹Œ1<¯ç½¤^Öø×e¸ï“5jy†ºH¶ÞáïcxÝîµ®aï¼ë ƈû^ _7¾Ö3jºw]îY×aëbÄ´w; ¯ûèûÂð{xùÞNcmã1Ûý>÷¿ÑÖw¯ü–Áû©wÿkŸxÀºÚ®>ö×1CŸÛjäþê{6î¹?ù^ƹ¿2J;÷:¶†·Ã°ûÀqºxÎ0jIf%=ìYÆæO)ÕUÅðÃÕj·;®s¹8w¥´··ANN7n¤¾¾žÂÂBjjj̇=çååÀáÇééé!11‘‹/âp8X¹r%Û¶mÃjøÏýû÷3wî\ÊË˱Z­äää°e˳ýýýœ>}š¯¿þšžžxî¹ç̇?—••QXXÈÎ;9uêÍÍÍüÃ?üÑÑÑÚ€ °D|~²ùOàâGuÕwê$µ‘kj÷¡Vëò£bŠêyêÔ)JKKÉÏÏ'--ÎÎN±Ûí8p€ÔÔTÞyçî޽ˑ#G8zô(;vì0—¿~ý:¼ýöÛ´µµqøða¼¨òòr²³³Ù³g·nÝâÈ‘#DGG³zõjŽ=JSS¯½ö‘‘‘\¾|™ðÃþØØXúúú8wî/¿ü2aaa„‡‡ë@ž¥tŠPdªbA5ÚHe\z{{¹pá[·n%++‹˜˜RRRÈÎÎæÒ¥Kô÷÷³sçNâããÉÈÈà…^ ¼¼œÎÎΡބ€vìØA||< .dóæÍ\¸pÁk=sæÌ!??›ÍÆÊ•+Y·n%%%´µµQVVÆk¯½FZZ111¬_¿ž´´4¾üòK³ §ÓÉ‹/¾Hjj*6›ÀÀ@í3³”z°d¿XuŠPüiõÃÕj·½§¦¦&>Ó½™ÔÔT\.MMMfRBBC_y)))ØívÚÛÛ‰ŠŠ2çyJII¡¤¤—ËECCN§“_üâ^yaaaæ´Õj%!!AM`Éx~±êá Šf~¬¤S„3nÇõ Œ¦ŠÝnÇb±ðÎ;ï ÝËÌmðZ¯éRW™tŠPDD¦5›ÍF@@ׯ_‘G]]}}}æ¼êêj, qqqæ¼úúzúûûÍé›7odö^ÔÖÖz•}óæMbcc1 ƒ¤¤$\.ÄÆÆz ÚH¢Kä«NN]_4Im¤S„3nÇ `Æ œÐHCƒÅsl ]a€Ëpyç1\¯ÁL7Ër 垆14 ÌÊkŒÜùÞ’Ëý¶<òã=¶àÂâž¶.÷[Î7«>l¾ÅðlwÚà` ½¶ÃÑ”VÀ‚áÎgx¥ Ly††fLœoñxmxîÙX]ÿ¬ÃRç{¾š74Ì=pìX‡¥¹÷},†gm¬CÇÇ Ö¡wg¸óƒÓÇÉбdc(ïÀÎà‘n¸ÓÍ׃ËXp 'XÜÇŠÅ+ŸË*ËL·ær#§½©ãg —Åóø:.ÇÁcÍ5üXô<ö<Æ#µQŽE<žÇÞбæýqãë¸ôühñL÷šö\åˆ/׈4Ã]'·èÇcùÄ«œ3gΰbÅ þöoÿ–… òþûïÓÝÝ @OOÿú¯ÿʼyóxçwxã7èììä÷¿ÿý¸ÞãüùóÉÍÍåƒ> ··—Û·oSTTÄöíÛ\)À™X½½½\¸p­[·’••ELL )))dgg» §ÓÉ /¼@JJ IIIìܹ“êêjjkkÍÀiãÆdeeÍc=ÆæÍ›ùüóϽÊYµj+V¬ 66–gŸ}»Ýn–qñâE’’’زe 6›ÄÄD^~ùe*++innW=·lÙBhh(GŽáƒ> ;;›E‹i'ðC:E(""ÓZSS‡ƒŒŒŒïM°XHNN6§ãââ ¡©©‰äädêêꨩ©áìÙ³^A™Ãá ¯¯Ï¼=!!ÁL "88˜ÎÎN`àÔdee%?ýéO½Öm­­­Øl¶1ëiµZyå•Wøå/Itt4yyyÚ`‰ˆˆLÂUÀè_U¾N:Žû^‡ÝngóæÍ,]ºtDšçùy^5¸~—û!óv»Å‹óüóÏ›óEFFŽ».ÕÕÕtwwÓÝÝ­ÿ2ôS:E("Ó—k¶½?xîG_G›ÍF@@ׯ_‘†Ë墣£ÃœWWW7"ŸÓé4OåÁ@¯XOOñññ$%%ÑÜÜLllìˆa¼’’’hhh **jDã ’ZZZ8~ü8/¿ü2ÉÉÉ:tHŸ °D¦ݦµÀìÚ®³á6 lذ“'OR^^NKK 7oÞ¤´´”ØØX¢¢¢(..¦¹¹™ï¾ûŽ’’’‘_v ÇŽãæÍ›Üºu‹Ã‡“ššÊ¼yóxæ™g(//§¸¸˜††ùúë¯ùøãÇ]ϵk×ÒÝÝÍ¿ÿû¿S[[KKK W¯^åðáÃ#z´|q:|ðÁ,\¸U«V±}ûvêëë9wîœ>ÛýNÊÌëñÐm¢ñô®fÑ.ïWGȦM›°Z­ÑÑÑAdd$999X­V^}õUþã?þƒ_ýêWÌ›7-[¶ŒøÏ½ÀÀ@6lØÀûï¿OGG‡y›†A™™™ìÚµ‹3gÎpîÜ9¬V+qqq¬^½z(Äã‘‘‘üÕ_ý'OžäÀô÷÷Mffæ=—Lûä“Ohoog÷îÝfy/½ôï¿ÿ>™™™^׉üø)((˜’Ï=‹PÏ"œ”gzzÝC`X!sæ2g¡QQôöõÑÓÛkžíâtö“µ2†¬•Ñd­Œaqf]­tµÜ¥³õ.Íwi¿ÝA[Ý]ÚoßÅé'iÙSÌ[¶‘¤eO“­­CK 45A]Ô׌››¡­m ½­¾ÎNzzzèuÝ.Ý`½€Ýcèrî±gZ¯ÇrÝa‘<–³…ŒÕ›y,g 1)+ih€ÆFÌq}ýÀëúzhoï§··×=ôàtv-@³{ÜÜ5‡ì•±l~*•-SÙ¼!…0Gܹ=0´×Aw+t·AW+®î6úz:ÝÃ]úzºèëw™ƒ½ÏEÝA·ÝAÝa±ÄfmÆ–µ…جÍD¤¦7÷p¨uoÒq³•úÒnÊz¨/í¦£ÑAO/ôô@O/´´ZƒiµÑb ¦µk­w‡—Wëy·lP@?[–†³yI8[–F“ ]ÙÐÝÙô´Í£îÆmnWÝ¢îÆmÚ»±$ßÅ’Ò10޲ó­û±5"êÁ™"û÷ï§°°P ! °DDDD`‰ˆˆˆÈ}ÓÞ’¡÷"“½]´aD°Ûí|ôÑGTTTÌ“O>é•ÞÝÝͱcÇøî»ïp8¤§§³mÛ6l6›™ç›o¾¡¨¨ˆ––"##Y»v-ëׯ7Ó÷îÝËêÕ«innæòåË„††ò /’’‘#G¨¬¬$&&†íÛ·3oÞàðáÃdeeñÎ;ïáC‡´AD–ˆˆø7»ÝΗ_~ÉÖ­[ÉÈÈ`îܹìÜ¹Ó žš››ùöÛoÙ¾};iii$$$ðÊ+¯pçÎ***())á±Çãé§ŸÆf³±jÕ*Ö®]˧Ÿ~êµ®… ²fÍbccyæ™gèíí%99™eË–a³Ùxê©§hjjâîÝ»Ú0¢k2é¡Lúvц‘Y®¥¥§ÓIrr²9/44Ô<ý×ÔÔ„ÕjõJ Ãf³ÑØØhæIKKó*755•ææf\ÝÄ æëˆˆæÎkÎ ÇårÑÙÙ© # °&“Nʤom‘G÷¥h±ÜsžáþÅãÒ) °DDÄŸÅÆÆb±X¨­­5çuwwÓÜÜ @\\‡ƒ›7ošé]]]477›½OqqqTWW{•[]]Íf3ƒ¦ñ2Ô­, °&ŸNʤom™å‚‚‚ÈÎÎæÄ‰TçPÅ IDATVVR__ÏáÇ͞%›ÍÆ’%KøÃþ@uu5uuu|ðÁÌ™3‡Å‹°~ýz*++9sæ ÍÍÍ”••ñÙgŸ±aÆû®z¯d‹†ÕgDÝ=Ë6†µ“1Æ2óÌvô1 ¶‹ÏIsÝ–áéÞë1<Ö3øùû³ÿ¶WßÊ¢,™Eüé>Æ´*ffíªçô¬´1ZìѶþþýû),,œ² ¨¨¨Ð. °äò§û¸¦U13k7P=§g¥]Ó©Åtäˆ(À’ûøQª,QÖ´­ô,îÁ™Îô¨ÇR—ÿY®‰ùŒŸ bfÔwù”´‰1K¶k*7ÌD¶Øäµ¾Ýnç£>¢¢¢‚àà`ž|òI¯ô‚‚^ýu–,YbÎûÙÏ~F~~>«V­ÂápPXXÈåË—ééé!""‚œœžzê)Ÿë+**¢´´”ïÿû$$$è{@`‰ˆÈÌsâÄ ª««ù‹¿ø ÂÃÃ9uê·oß&11q\ËŸ?žï¾ûŽ?û³?#**Šöövîܹã3ïÑ£G¹rå ù—ILLŒ_ˆNŠˆÈ´f·ÛùòË/Ùºu+Ì;—;wât:Ç]Æ;w°Ùl¤¥¥EZZ+V¬ðÊãt:yÿý÷©¬¬Tp% °DDdfkiiÁét’œœlÎ Åf³»ŒU«Vqûöm~ñ‹_pìØ1®]»6"Oaa!µµµ¼ýöÛDFFªáE–ˆˆÌn†ëD‡ù:))‰ÿøÇlÙ²…þþ~~ÿûßó¿ÿ÷ÿöÊ¿`Á:::¸zõªTš®Á™eüê"ð™üß2n±±±X,jkk‰ŠŠ »»›ææfæÏŸ@XXæ2ÍÍÍôõõy•ÌòåËY¾|9K—.åÀtww ÀâÅ‹Y¼x1ÿþïÿŽÅbq QD–LôOC?ªë´*ffíªçô¬ô,¸MCPPÙÙÙœ8q‚ÐÐPÂÂÂøøã±X†NÂdddpñâERRRp:œ:u «Õj¦—””ARRúÓŸˆŒŒ4ƒ«AK–,á•W^áСCX,–-[¦ƒ_`Édõ"è6 â?m¢Û4Le‹M^ëoݺ•¾¾>‹Ðc;èY„2Ëé"w_–?õLŸb¦ç¯*jc–l;=*GD–ˆˆˆˆ(ÀQ€%"""¢KDDDDLSv›†ÿãÿþ?Õúâ·\èrÞiÍétj;‰Ì`êÁ™`SÖƒõnAÁÀ‹¤XX™gÀãét'eЕAGôctDePÝÊw×áêu¸ršoÞ¢­¶šö[Õ´×ÞÀå¼\s×Yæ°2ÀJ̲b—¥³,•9‹R!!æ.€„tÏ£¼Òà«ëʯ[¸Zå ùF÷ÀPÕMw[ pѹÍJœ¬ÀÁ ¤…L|T0ñQ!-Y ÙO@öZÈ^K#‘ÔuP×H]g_öqù¢ËŸÙ©ø¬¸\®bá*si`.uÌ¥žyF ËÃÜC óm°,–-„e èIÉ ÑšD£5‰¦€$n¶ÍáJ\ý®| µô´~KOëwô´}K¨ý6j±q‹Xj™O/ Áæ,I%lõB²3 Í^ˆaKÅŠ+*¢R¸ø'ƒÏþ¿†/¿qÒÙÐFgc­t·6_e@)‘\ã ` , $rÞœ!i!óÓañX²–¬¦%$‰ª¨j*¯vSùU•åT~ÕAwÇeàssH ‹t Í=,ˆ eÁÜpÄ ÆÂ…àer³;†šînöÄPÝÉ•Ò.®~ÙÍ•Ò.j¯´ß¹·ÃB¸A2Í$ÓÂ<šI§‹L`»bS°æ®Àš»˺8’Óè Ž§'8žÞà8*®sñs¸ø|ö9´7]£¿óú;¿¦¿ë\· ¢ ê ¤'€'€µÀ‹AЂ¹-˜Kà‚¹.˜GêrúR—ÓŸ²œÖð >û.~ Ÿ} ß~×Ko{ãÀÐÖ„£§ _cåk,\"ŽF³î™@Zl8)óc™—n#9ÝFhz:¤/4‡šî(®4†q¥)Œ+M¡Ô|ÛÁÍoZ©ù¦…›ß´âr]uï³W0¸Ê:Íá1ºI³@ºÒ¬ž€õ9ðd™K¨läz[$•í‘TÖ†pãT~7¾‚¶†[ÀæL ‘Ü!’v"¸ÃRúÉqïO9@䊅ðT6¬Ï† Ùt…ÇÓiæ®5šNK _\4¸XŸ•ÀçœÐW_ ô×`ôWÊU¸J(Wˆâ&‹Üm´HÁ¶d¶%ÉØ–&’‘NÏÜÅô$,¢'a1 Ž8®TÕ¸Z µUí4VÞ¦©ò6•·±wVšÇ5\e>wYN?ËégýÌK·173„Ìæf&`IÉ„”LH^ˆsÞ.ß ârm —oñíÍ@ê®ôsû»>ê¯ôÓ\Ó|f~&QA½ÄÓK½¤Y¬ ±°"ÄÊÊ ‰iɰn-¬}Ö­£3.…æ¾Pší!4ÛC¹ò­…ò_•8ùêSµîÏÒ«À5æPC45Äp“n’eu²Ê «V.€ `åœK£-<öðtÚ"Òh2’¸r®^ƒï®BMUu•tÔ_çn]%=w*Àต‚Áï…Q9r„Ë—/ÓÝÝMHH«V­"??_ßä¢kVÓmÁEt¼©‘Ø•+W(//çí·ß&::zăšE`‰ˆˆÜ§––"##IIIðz¡ˆ¬ÙLWD‹èx›vä½X‡¦¬¬ Ã0((( ::šèèh¯<ååå\¸p¦¦&‚‚‚ÈÈÈ ??Ÿððp3OEE'NœàÎ;¤¥¥‘••Å¡C‡øÉO~BHHˆv Q€5)tJA´‹O“Š:TéÆ˜þÑè¶mÛˆ‰‰¡´´”={ö`ƈS„N§“-[¶`³Ùèììäøñã>|Ø| skk+¿ÿýïÉÍÍeõêÕܾ}›'Nx=äYD–?þâÕ1,3|Ÿ¸Š¸þ}èx›1Á@pp0ÁÁÁ†áÕ#å);;Û|C~~>ÿóþOìv;AAA|ñÅÄÅÅñüóÏ`³Ùhhhà“O>Ñ® °ôí&":Þ&²‘fN$zëÖ-Š‹‹©¯¯§»»—û!õíííÄÇÇÓÔÔļyó¼–INNÖn °É9íâÓ ":Eøh7†ÿG£v»™™É«¯¾JXXííí8p‡Ã¡Í, °fô/^}+È ßÅ'®":E¨àêþ455ÑÝÝÍsÏ=Çœ9s¨­­õÊÇ•+W¼æ Ï#2‘ô®""â×¢¢¢°Z­\¸pÖÖV***8{ö¬Wž5kÖÐÔÔÄÉ“'innæë¯¿¦¬¬l ÔÔ…î¢k’ÌÍŠîÑ.>Ý+2§u¼³‘fF`ÎŽ;øæ›oøçþgÎ;G^^žWž˜˜þìÏþŒŠŠ ~ùË_òÅ_ðôÓO`µZµ;È„Ó)ÂGõ¬H2Ãwñ‰«ˆN>ºH×"ÑÜÜ\rssÍé·ÞzË+}ÅŠ¬X±ÂkÞ»ï¾ë5½xñb/^lNŸ={–9sæ ¯BQ€%""ò@>ûì3æÍ›GXXÕÕÕ|ú駬[·N # °&ýÇܬè>íâÓ½":Eøh¸¸Jdö4Vss3gÏž¥»»›¨¨(Ö¯_ÏSO=¥]A`)™»ø£8E(úàó!??Ÿüü|mvy$t‘û¬ë>Ññ&j$X""""þö¦  @=î""""H=X""""lÊ.r/((p¿ 昃A8‚Aˆ{ A@†aÃÀ° ŽK 0,c fÚд;}0¿axÏw±øH·¸—ó­{Äú õšî±¬1VÞaëžopyî‘nñ|ïCeºúHJswzºÇÓ. Üc÷4†ç4 ^PkÞùFKǸÏò<×?¬nàk#Ëâê6Ð,éýû¸Çƒ›Íܵ c ¿{l1Œ‘i K3ËrÏ÷H7cp9‹ud^ËPÚÐg´ÇØbÁ°Z‡ò°fÝþߟþTßÊ¢,yHºÆXdÂìÝ»—óçÏϘ÷SPP@EE…6¬,‘©³gÏÖ¬YãwÁXqq1¿úÕ¯´g ÝKdªÌœGÁ‰L¹°°0¿«³ÓéÔ†S€%³™_=i~,Æ£(ÇŸwGôÈ=cªž%û|oo/}ô„††²aÃ._¾Lbb"ùùùìÝ»×ëy…EEE”••q÷î]ÂÂÂX¶lÛ¶mcÿþý´µµqüøq 1 cÄ3 }¹qã§OŸæÖ­[„‡‡³dÉž}öY‚‚‚(//çÂ… 455DFFùùù„‡‡PUUÅþýûÙ½{7ü1 ¼ôÒKc†ÁöíÛYµj]]]üö·¿åÚµkDFF’——çõ,EQ€%~ÎårùOåO½BÆôzË÷,ÇÚuDU]~°ë=̳¤'ôøñãÔÔÔ°k×.ÂÃÃ)**âöíÛ$&&ŽÈû§?ý‰óçÏóÚk¯ÏÝ»w©¯¯àÏÿüÏùå/INN«W¯׺[ZZø·û7ž}öYvìØAgg'G娱clß¾èÚ²e 6›ÎÎNŽ?ÎáÇٽ{·WY§OŸfëÖ­ÄÄÄÀúõë¹zõ*o¾ù&.—‹3ï™3gxþùçÙºu+.\àý÷ßç¿ü—ÿBhh¨¾˜ü€®Á™ÊH@DÆÔÛÛKyy9yyyddd0wî\¶oß>ê)¶;wîÉc=FTTÉÉÉf0ŠÅb!((ˆˆˆ"""Æ\ÿÿøGüqÖ­[Gll,©©©äççSVVF?ÙÙÙdffCJJ ùùù\½z»ÝîUÖæÍ›yì±Çˆ‰‰!22’   , áááDDD0Ôï±jÕ*V¬XAll,Ï>û,v»ÚÚZí~B=X2vG‹NΊ·¬S„S¸tŠðžZ[[q:Ì›7ÏœB\\œÏüË–-ãüùóìÝ»—ÌÌL.\ÈâÅ‹±X¬O¡®®Ž††¾úê«¡ßG®ª­­¸¸8nݺEqq1õõõtww›éíííÄÇÇ›Ÿ¥žïa, æë   ‚ƒƒéììÔ—’,™1-:E8­##"£ª:E8ëDEEñ÷ÿ÷\¿~k×®qôèQ>ýôSÞ~ûí ²ìv;kÖ¬!77× œ<×e·Û9pà™™™¼úê«„……ÑÞÞÎp8^ùǽÞáu5 cÄúE–ˆˆÈ‰‰‰Áb±pëÖ-¢¢¢èé项¹™ôôtß_n,Z´ˆE‹ñÄOðOÿôOÔ×ד””„Õj½¯@%))‰ÆÆFbbb|¦öZ=÷ÜsÌ™3`ܧòî·.â?t –ŒI§'±Ëc½å}Špºïz:ExOÁÁÁdeeqâÄ *++ihhàÈ‘#î;×l€²²2JKKihh µµ•¯¾úŠÀÀ@¢££ˆŽŽæÆܹs‡®®®1×ÿÔSOQSSÃÑ£G©««£¹¹™ŠŠ Ž= ôbY­V.\¸@kk+œ={vä!ï#ŠŽŽ¦µµ•ºº:ºººÌkºÄÿ©KÆŽtŠpZƒ:E8FUuŠpFÈÏÏç£>âàÁƒ³aÃÚÛÛ½. ÂÿøGNœ8Óé$!!]»v™ÿ}·yóf>úè#þñÿ‡Ã1æmxûí·9}ú4¿þõ¯q¹\ÄÆÆ²|ùrÂÃÃÙ±c§OŸæÂ… $%%‘——ÇÁƒÇü±ºtéR._¾Ìþýûéíí5oÓàW?lÅ÷G|AAÁ”ôMêY„zᬡ1øô:=‹PÏ"Ô³ï÷Y„v»Ÿÿüçäåå‘­osQ–øa®S„Óº·M§Ǩªþ‹pF¸}û6MMM$''ÓÓÓÙ3gtãMQ€%þK§gÇ[Ö)Â)Ü:E8.Ÿ~ú)ÍÍÍX­VæÍ›Ç_þå_NÈ#r8@uuµÏ´7²qãF}ˆ,¿¡K,DÆ-))‰wÞygRÊÞ¾};}}}>Ót×tQ€%""ò"##Õ2ñ¿¡§ê"w‘™J÷ÁQ€%"""2½MÙ5XC÷Á0ø_jƒwæõœoú½òLV™ÃßÃða´õùÊ¿u­ž÷ªûƒ´ÁheŒç}OF;M·v«d½Zæd×}"ö›éÜ®ºÿMVMÖgÖìg"?K†Ïï½÷ô­,êÁÿ¤;DËL´wï^Ο?¯†X"""eÏž=¬Y³FÁ˜LºMƒÈ,ãW7ާ‰¸áèDr8X­VmX"£ó§/c¢}ofêííå£>¢¢¢‚ÐÐP6lØÀåË—ILL$??Ÿ½{÷’››Knn.EEE”••q÷î]ÂÂÂX¶lÛ¶mcÿþý´µµqüøq 1 ċ=|ñÅœ9s†îîn.\Hjj*gΜá'?ù ÅÅÅTTT°víZΞ=K{{;ï¾û.W¯^åìÙ³444`©©©äçç À¿üË¿Ï /¼`®«³³“Ÿÿüç|ÿûß'##C_–¨ÇCuU  ãD&ÇñãÇ©©©a×®]„‡‡STTÄíÛ·ILL‘÷OúçÏŸçµ×^#>>ž»wïR__ÀŸÿùŸóË_þ’œœV¯^=®uWWWóÑG±uëV-ZÄõë×)**‘¯¥¥…Ë—/óú믛û‚ÝnçÉ'Ÿ$11‘ÞÞ^ŠŠŠøÝï~Çßþíß°zõjŽ=J^^žÙãõÕW_1gÎW~N×`‰ÌÂ@@ÄŸôööR^^N^^Ì;—íÛ·ãt:}æ¿sç‘‘‘<öØcDEE‘œœlS¡¡¡X,‚‚‚ˆˆˆ ""bÌõ_¼x‘… òä“Ob³Ùxâ‰'ÈÌÌ‘Ïáp°sçNIHH`Ù²e,]º”˜˜yùå—©¯¯§¡¡€¥K—PQQa–SVVƪU«´áýœz°dFõx¨A´ïÍ<­­­8NæÍ›gÎ !..ÎgþeË–qþüyöîÝKff& .dñâÅX,Ö§ÐÔÔdBƒ’““ùî»ï¼æEGG¸¬¹¹™¢¢"jkkéêê2{:ÛÛÛ™;w.<þøã|ùå—,_¾œ[·nÑØØ¨kP–̨õÎ(о'QQQüýßÿ=ßûÞ÷ äèÑ£üú׿µÇk¢Ž˜÷›ßü†žž^~ùeþæoþ†¿ù›¿Áåráp8Ì<«W¯æúõëܹs‡²²2222ˆŠŠÒ†T€%""2ybbb°X,ܺuËœ×ÓÓCssó¨Ë°hÑ"¶mÛÆ›o¾IMMy–Õj½¯€8..Îkݵµµc.×ÕÕEss3O?ý4ÄÅÅÑÕÕ5"_BBóæÍã‹/¾àÒ¥Kdggk£Ï:E(cÒ)™Eckßó7ÁÁÁdeeqâÄ BBB§¸¸Øç“"`à&§ÓIJJ |õÕW œÊ»qãË—/' `Ì[<¬]»–ýû÷SRR¢E‹¨¬¬äêÕ«cnóÐÐPÂÂÂøâ‹/ˆˆˆ ­­Ó§ÿöî=8ªóÎóÿûœ¾ê†$Л0B  027'Éa¸ø¼J2ؤlÈLÙë­ÍVMÕnUäýýj+¿ÝJJqM­Ù­ 3ÃSž5q<²,„‰ y;âVR<6’K HtëÓ¿?ºÕênµ„º}^Ôá¨Ïóôsžóœ§û|Ï¥Ïy?âûyäÊËËq:¬X±B+}Ð,ÕYuU ê{“©°°… rìØ1þáþE‹‘””„Ý>ô8Ûíæ“O>áÿïÿÍ믿Î_|Aqq1QQQlÛ¶›7oòÚk¯ñßþÛ»ë¼-ZÄÎ;9sæ ‡æóÏ?gÆ çþY{úé§¹rå ÿý¿ÿw*++ùö·¿1ïêÕ«1M“Õ«Wßµ\™´EDdÊs:<ùä“×½½½TWW“ŸŸÀ+¯¼H[±bňG,XÀ¡C‡Æ4ÿ¼¼¼Û:¼ýöÛ{Ylݺ•­[·yßÒ¥Kù«¿ú«i‘î»uçÎúûûG}ëQ€%:⡺N1:E¨¾7µ¶¶ÒÖÖFFFÝÝÝœ:u €ììì2ÿÚÚZ–.]ŠÓéä_ÿõ_9{ö,;wî¼ïr=üãùÍo~ÃÂ… #Þ×K`‰6Ȫ«ès"ä´··c³ÙHOOçÀãòˆœ£GÒÜÜ1mÓ¦MlÚ´‰¯¾úŠ>ø€ÞÞ^yâ‰'Æåbô––Ž9BRRßýîwµ’`‰Èt¥ @¦£´´4<8!eïÞ½›¾¾¾ˆi×m=óÌ32ï%K–PRR¢¬KDDdf‰‹‹S#ÈøïÌ–””è§/""""ãH·ig“vŠ0øœ³4iˆ{Ì1Ì'ü}£}9Ìô±ÖÝE½FšßhËMÛ‡×Ýa½™Ã¼Ž”¼ênÞG0ƱO0†z‡ÔÝðOó'Áƒé›æàßÁÓ‡¼Çš"¼/lL„rÍaòFš6ðˆ·Hu7Ì !¸Ü°ºa wëÁùÌQä”2¯° ˜¶Á±iÓ¶@þÁ Ë’f ç ~=B^F˜—6/#ìýFP½#Õ-jE‹L¶žžÊÊÊhll$**Š‚‚HMM¥°°ÒÒR6lØÀ† ¨ªª¢¾¾žÛ·oMNNEEE9r„›7orâÄ ***0 #â£k"ùì³Ï¨¨¨ ««‹E‹±gÏbccéü1gΜáæÍ›$$$°~ýzÖ­[H?yò$tuuËêÕ«Ùºu+¦iR__x€uII †a°{÷nÖ¬Y£•¯KtB´¢´ÉÄ8qâ---CUU­­­-sñâEêêêxæ™gHNNæöíÛ\»v €}ûöñú믓ŸŸ?¦çþõõõqæÌžzê)Ž?NeeeàùˆçΣººšï|ç;¤¦¦ÒÚÚÊ?ÿó?ãt:ÉÍÍÀår±wï^bcc¹~ý:o¿ý6.—‹‚‚V­ZÅõë×ùì³ÏØ¿?^¯·Û­¯KDDѹLŒžžΞ=ËÓO?Mff&à»ûúÏ~ö³ˆù»ººˆ‹‹céÒ¥˜¦I||<€ïÎì¦iât:CŽ>ÝeYìܹ“ÄÄD¾ñož‡¾ë§vìØxÈtBB_ý5¿ûÝïÖæÍ›ùظq#.\   ‡ÃÓéÄ4Mbbb´Ò`Él í£V´ÈdêèèÀ²,ÒÓÓÓÜn7IIIóçääPWWGii)Ë–-#++‹ììlLóÞ×åp8Á@ll,wîÜ ··—7nðë_ÿš·ß~;$( > uáÂ>üðC:::èííŲ,\.—V°,ÑAÑŠVÐ&S_||’4 ¾¾˲X°`‡ƒsçÎáp8HHH|×?]¾|™U«Va·ÛÇåÛ¶mãÝwßÅår±lÙ2úûû¹rå ÝÝÝ<úè£Ì›7ÎÎN.\¸@zz:Ÿ~ú)!e$$$ÐÑÑÁÕ«W™3gN§»]›iX2ci©­è\&[aa!eee;v,ðË»ÎÎΈˆÛí¦¦¦†ÊÊJ,Ë"%%…ââb¢¢¢ÁPYY¯½ögÔ·iI^^‡ƒ>ø€“'Oâp8HII Ü6";;› 6P^^ŽÇã!++‹-[¶P]](cåÊ•444päÈzzzt›†é¾ÏZRR2)Ç#õ,B=‹PÏ"œÏ"4‡þ­gêY„ãñ,ÂÞÞ^~þóŸ³cÇyämÍEG°dFáj­hþ”IÖÚÚJ[[twwn‘­ÆX2=i©-2ÔÖÖÒÞÞŽÍf#==ŒËõSG¥¹¹9bÚ¦M›B.LQ€%"“O›Œ“´´4<8!eïÞ½›¾¾¾ˆi×m‰(Àƒ¸¸85‚Œÿþåd]ä."""2S™jX""""SÚ¤]ƒ¸ –é[Øû|cG/8»ÁÑãÛýiö~ߨÑãOëGwhš­¼°œàu‚傾8è‹÷GÇ ýnèöOÈëO‹ò Þh npp:!†ÁÁ ¸üƒˆö±þ±;,O”z”°ù3èo›­DúÛ [`óø–ÙÞïkGÃF¿oL?Ð78xÃÇýÀ@>OhÞÀÐôw÷à`t‡¦½CÒ º1ñmô`óöa£»·»ea÷€Ã?Ø-ÿâø‡5˜i°…5—~ ¡÷_2mÃÜfÈÃ01LÃ00M6ÆÝðm†‰ Óÿφ ;6ìØÿ|)ƒ9þùòXØñ`ǃ 6,Ì Á—ÖÍŸg`º Ókù¦{=ØË´,l^/¦åõýòÏ XAãàÁ;Ì8|š° ßà5üyŒ Á´¼Þ°<W²üݪèõú†ÿ¸ßß}^_ž~ÿ8ÏÿwxzPW  Vм­y‚Ó=XsÀŠÏðÆ}(£üî8`NÐ8øï˜ ¼Ñxpá1]ôN<¦“~ӆǀ~Ó7ôÙ Ï?îµ…¾îóçñ˜ù½X6°ì^,»¯Ý‹Í6ÛàØáð v»oˆ”n·ûÇ/6;¾|ŽÁ[Sa`ÃæÛ}ß5ÆÀtÓðÝÂÊ”n ~møþºwÙ`Y†ÿ6bFÈwVpzȼæeüçÿï?k«,:‚%""ã͸ÔUéùÓÐÒÒRêêêÔ…D–ˆˆÈxyñÅY»v­‚1™2t›‘)Ã{©ª†Þ)ykùäΜ9Ã7ˆŽŽfåÊ•<ñÄœ9s†ßÿþ÷tttEvv6Û·oÇét†ÌcïÞ½¼÷Þ{´··óòË/“ ®§kfï9Ê,éÆ4èMêª3<ˆñNÑÝ@ßdÄ£WØ7Oœ8AKK ÅÅÅÄÄÄPUUEkk+©©©Cò^¼x‘ºº:žyæ’““¹}û6×®]`ß¾}¼þúëäçç“——7êù÷õõqæÌžzê)Ž?Nee%O>ù$}ô'Nœ`ûöídeeÑÓÓòøÃ0xâ‰'HHH ££ƒwÞy‡“'Oòï|'d|ð»ví"::š˜˜}$`‰ˆÈDGϳõaOOgÏžåé§Ÿ&33ð=Þæg?ûYÄü]]]ÄÅűtéRLÓ$>>žŒŒ À÷èÓ4q:!GŸîƲ,vîÜIbb"ßøÆ7œøíoKAAëׯLKKK ü=pd !!Çœ²²²Ë²,¾óï’’¢‚¬™¶ç(êÓ 7é¡LÕÈuüŽŽ,Ë"===0Íív“””1NNuuu”––²lÙ2²²²ÈÎÎÆ4ïýw]‡#\ÄÆÆrçÎîܹí[·Á_$Ÿþ9555´µµÑÓÓƒeYx<úúúp8Øl6W3ˆ~E8¥ö e¦t"”‰bŒ)ð™¬S„cÏK/½ÄÎ;q8”——óË_þ˲î}cœ_|n·|¬âæÍ›;vŒÔÔTöíÛÇÁƒG®<ϨËX"""ã&11Ó4¹råJ`Zww7íííþÇn·³|ùrŠŠŠØ¿?---ë°l6^ïøEƒ.—‹„„.]º1ýÊ•+x½^vìØÁ‚ ˜7o]]]Z±3œÂå)²o(3« è¡Ü›©| –á¯Ã×`MPÇw¹\äææRYY‰Ûí&&&†êêêaocP__eY,X°‡ÃÁ¹sçp8_ä%$$pùòeV­Z…Ýn—[>>þÑGQ[[KWW‰‰‰lÚ´‰ÜÜ\*++ikk£¸¸€3gÎpâÄ ¾ÿýï³lÙ2^{í5{ì1òòòÔ¹`é‹\Ç3´çïy u^™Ö_¦'Nœ ¥¥…ââbbbb¨ªª¢µµ•ÔÔÔ!y/^¼H]]Ï<ó ÉÉÉܾ}›k×®°oß>^ýuòóóÇ\ôõõñÁ°k×.¢££‰‰‰¡££ƒ5kÖ‘‘×륶¶–ÿóþ/¿ü2N§sز~ó›ßðío›¹sçòþûïóæ›oòòË/cšf`^§OŸæÉ'ŸÄf³QVVÆ›o¾Éhhh ¢¢‚¢¢"233ùôÓOùõ¯M||:;;G,+¸œØØX¼^/wîÜ ,išdddò$%%áv»»nkk#???¤Ì… òá‡Ú&55•¦¦&l6v»µk×R]]Moo/—/_fÉ’%êX °ôE®ãÚó¿ÿ<:E(³çË4>>ž—^z‰K—.ñùçŸS^^Nmm-Ï?ÿü=YvûÐÍå¯~õ+º»»yâ‰'ˆÇf³ñ¿þ×ÿÂãñŒXVp.”÷zÇ·c/Y²$`-^¼˜¨¨(’’’hnn¦©©‰7ª[N0"‘)-11Ó4¹råJ`Zww7ííí#DË—/§¨¨ˆýû÷ÓÒÒ¸Ëf³K@ÓÒÒÂúõëY¶lÉÉÉØl6þøÇ?ŽjŽâ—‡–eN´µµÑÝÝMrr2@ P ¯Ë@:øN677óÅ_ŽV-Y²„óçÏsãÆ ÁR€¥=å™±?)»¦ŒÙÕ™ÕygÝ—©Ëå"77—ÊÊJ¾øâ ®_¿ÎÛo¿=ì­êëëùä“O¸~ý:œ;w‡ÃABB \¾|™®®®»D#™7ogÏžå믿æË/¿äøñãw½×h;Ó4y÷ÝwùòË/¹rå o½õ . œ"-(( ¾¾ž>úˆöövjkkihh    $ÀêééáÓO?`ÅÆÆ2oÞþ<†ïïHóé7 ú š—1ø¾àºô™Wh™uh—a‡Aœa‹Í0ýwE6»#Mÿ0|®iƒc3è=ƒe¼'8ß@ž¡ó4‡w¤º ­«¶<‘–/´¬¡y"×mätsó ƒaX†ÃðßWȰ‚Æ £Ãð`¾û üí{œhžÐr,ÿ´2ßü~Ãè÷çœ÷àûËš'¼îý!eFž÷hë¼Ìžu ®—a¾íëÅwOG›ÝØ0 ›¿/˜Aãé6žÁ¼ó6 l¾±ab„¤¾?¼Ü÷–üž‘ëš?t^¡yl!e†—78}¸ò"—9PÁe²]#×ÝäÕW_ÕVYtKD¦ª‘±eè…2Î}j*(--¥®®N«J`‰ˆˆŒ—_|‘µk×>ð`¬©©‰’’º»»µ$„nÓ 2ãŒüÃ`¯WG±d|ûÔT07½§–ñz#>_ð^X–…i긇,™ÖÆòup_vKnLËÈÁ@·'Ÿe*³žžÊÊÊhll$**Š‚‚HMM¥°°ÒÒR6lØÀ† ¨ªª¢¾¾žÛ·oMNNEEE9r„›7orâÄ ***0 ã®{¾yó&ååå477ãñxHLLdûöí$''ów÷w†ÁOúS à 77—={öàõz©©©áã?æöíÛ$%%±yófrrrß‘¯#GŽð½ï}ßüæ7\¿~üà455ÑØØÈ£>JUUúÓŸÈÊÊb×®]8NuX2“öGïoÍû@·<ã¹79}è¬I[3¶m'ÁNœ8AKK ÅÅÅÄÄÄPUUEkk+©©©Cò^¼x‘ºº:žyæ’““¹}û6×®]`ß¾}¼þúëäçç“——7ªy¿óÎ;X–Åp8|ýõ×8NâããÙ·oo¼ñ/¿ü2N§‡ÃÀéÓ§9þ<þçÎܹs¹|ù2Ç'&&†Å‹Ê~ÿý÷ùö·¿Mbb"n·›¦¦&nܸÁþð¾÷½ïñ§?ý‰7Þxƒššüq}Î`‰ÈT ØtŠP&'`lzzz8{ö,O?ý4™™™€ïñ6?ûÙÏ"æïêê"..Ž¥K—bš&ñññddd¾Gߘ¦‰Óé$66vTóïêê"''‡ùó瘘Hx”Ntt4n·€þþ~NŸ>ÍþýûY°`Aà=ÍÍÍüîw¿ °¶mÛÆÒ¥K‡ÌsÏž=#V¹¹¹\ºtI–,™iû£:Eø Ö†NΪ•ŒIGG–e‘žž˜æv»IJJŠ˜?''‡ºº:JKKY¶lYYYdggßóõMëׯ§¬¬ŒÏ>ûŒ¥K—’““CJJʰùoܸA__ÿ÷2Ýãñ––ò}¼LBNÆÆÆrçÎuX2ÓöGuŠpºÐ)ÂI[:E8%ÄÇÇóÒK/qéÒ%>ÿüsÊËË©­­åù矿§ +//eË–ñé§ŸòùçŸSSSÃŽ;øÆ7¾1oo/ßûÞ÷†<çÐnÝìœR ^GÃ0ðzµ6Ýèç ""2¥%&&bš&W®\ Lëî½}ø£v;Ë—/§¨¨ˆýû÷ÓÒÒ¸Ëf³9`™3gùùùìÛ·G}”?þ8PÖÀÎÝ€äädìv;Ì;7d˜3gŽVè,¡#X³”NNµµ1ž{§ºkʨ¦É=zâr¹ÈÍÍ¥²²·ÛMLL ÕÕÕ'6„«¯¯Ç²,,X€Ãáàܹs8ß)¸Ë—/³jÕ*ìvû]oñPQQÁ²e˘7oúÓŸhjj"99ð-øÃþ@VV‡—ËÅÆ©¨¨À²,-ZDOOÍÍ͸Ýnrss‡e¢Kfá×¥NN·—NNÚjÔ) SXXHYYÇŽÃårQPP@ggçSnà»>«¦¦†ÊÊJ,Ë"%%…âââÀéÛ¶m£¬¬Œ×^{ Çs×Û4X–Eyy9]]]¸\.²²²Ø±cà;²µmÛ6Þ{ï=~ýë_nÓðøãCMM ¸ÝnÒÒÒØ´iÓ4ß!”QjJJJ&%„Ö³õ,B=‹p¢žEéù€ƒÏ",GÏ"Ô³Gû,BÛדý,ÂÞÞ^~þóŸ³cÇyämÍEG°dúíêáƒX:E¨ƒ<³é(ëØµ¶¶ÒÖÖFFFÝÝÝœ:u €ììlõ=Q€%ÓóëR§gþÆKÆi5*pPµµµ´··c³ÙHOOçÀãòˆœ£GÒÜÜ1mÓ¦M!§õD`‰ÌZºKlŸzÒÒÒ8xðà„”½{÷núúú"¦ \·%¢KDDd ÂïU%2.»%“u‘»ˆˆˆÈL¥ŠˆˆˆŒ³I;E8pß‘­†±¦”g¢Ê¼ßºg݆{ßÝÊkÝïeùîµî÷Ó®Y·‰¨ûƒj×ñ˜ïXúÓýäp·ºŒ”,ëâ~?÷÷úYïv.ïXÚu,ýu<ꃷïÑ,‘)J7ð™]JKK©««SCˆ,‘ñòâ‹/²víZc2eèW„2#é_"³ËxÜëAòx<E‹¬)Ï0 mXEDf žžÊÊÊhll$**Š‚‚HMM¥°°ÒÒR6lØÀ† ¨ªª¢¾¾žÛ·oMNNEEE9r„›7orâÄ ***0 cÄgöööò³ŸýŒÝ»w“““˜ÞÐÐÀñãÇùÿá?àt:9yò$tuuËêÕ«Ùºu+¦é;QT]]Mcc#ßøÆ7øíoKgg'?ùÉOîº\¢KG-DÁ¶ˆL˜'NÐÒÒBqq1111TUUÑÚÚJjjê¼/^¤®®Žgžy†äädn߾͵kר·o¯¿þ:ùùùäååÝu¾N§“?û³?£¾¾>$Àª¯¯gÕªU8N\.{÷î%66–ëׯóöÛoJ=àÆ444ðì³Ï®ËrÉô¢k°dFRp%2sôôôpöìYvìØAff&óçÏg÷îÝX–1WWqqq,]º”øøx222ÁTTT¦iât:‰%66ö®óÏËËã³Ï>ãöíÛܹs‡ý× yÈôæÍ›Y°` ,_¾œ7rñâÅr<{÷î%55•”””1/—L/:E(""SZGG–e‘žž˜æv»IJJŠ˜?''‡ºº:JKKY¶lYYYdggN×UFFÉÉÉÔ××óØcqöìYX¼xq Ï… øðÃéèè ··˲p¹\!å$$$„\+6Öå’éeFÁRp%ÁÁ¶ˆÌNñññ¼ôÒKìܹ‡ÃAyy9¿üå/ïëÈP^^õõõ€ïô`ðÑ«––Ž?ÎòåË)..æÐ¡ClÚ´ ÇR†ÃáÐÊQ€%""25$&&bš&W®\ Lëî½}Ø÷Øív–/_NQQû÷ï§¥¥%p–ÍfóùÃ?Lgg'~ø!mmmäææ†X lÚ´‰ôôtæÎËÍ›7'd¹dúÐ)B™‘ÔDf—ËEnn.•••¸Ýnbbb¨®®Æ0ŒˆG«ëëë±,‹ àp88w„„ÀwªîòåˬZµ »Ý>ª[äô @vv66l ¼¼ÇCVV[¶l¡ººz\—K`‰ˆˆŒ+§ÓÉ“O>xÝÛÛKuu5ùùù¼òÊ+´+V°bÅŠaËZ°`‡sºººˆŽŽ&;;{HÚöíÛÙ¾}{È´{rlݺ•­[·Žy¹D–ŽZÈ”¢~ 2³´¶¶ÒÖÖFFFÝÝÝœ:u b°3Þúúú¸uë555äççëØ's¹D–6ª""Bmm-íííØl6ÒÓÓ9pàÀ¸<"çèÑ£477GLø5àoû[–,YÂc=6m–K`‰Œ;Í™YÒÒÒ8xðà„”½{÷núúú"¦EEEñôÞT_.Q€%""2iâââÔ2þ;ú%%%ÚÍGºÑ¨ˆˆˆˆ,‘©mÒ®Á*))À†Nÿ`Ç 6ìØq`bÇĆ f`šÃ?øÒ|év Zä±=×ÀÄðçL³c`úk50§Ù‚Þ\Ž üãÐÁ ƒéAc#8mèÀׯà?Ã7öåøÛ÷p¾À+c i¿6K÷†Ž /ƒÃ`šÃðñb`ù_[Xþéacc Íòç÷ý þ÷ <€Ã𯭠ò- £ßŸ¿?$¿/Í ¦F¿ïuÈØ Ã_ŽÑ”ß㯟Çÿº/hùTi` IDAT}A¯û",‹yŒv4p#gÿbB^7ö¯Çàü!åEJ4#ò|ÃçI¤ºÃ!l:cÌ<6Ãæ^3¬¬aʈ¸,áéáãðºEJ(g žfèk#¼~Aï5Lÿk3B݃Ê.=ð~°…½6‡©ÛpéæÐq Ìðå2#Ô=,Ýÿu9¤N†¯¾Z2å7šÕÕÕ466ÞÓ}´DG°D&ˆý ÖP#ÉìróæMJJJ¸zõªC–ˆˆˆŒ¯×«ç ÎRºMƒ<ȯ5AXkèkW$£ÓÓÓCYYDEEQPP@CC©©©RZZÊÚµk¹qã/^$**ŠÍ›7³víZššš8räý×ÛíàêÕ«>|˜W^y…„„êë멨¨`Ïž=TVVÒÕÕÅâŋٵkñññÃP§Nâ“O>áÎ;$''ó­o}‹eË–ð‹_üÃ08|ø0K–,á¹çžÓ tKîʘ%N|Éêªë ­ô4i°'NÐÒÒBqq1ßÿþ÷ijj¢µµ5$Ï™3gHOOçСC¬[·Ž²²2ÚÛÛ5‘¤ði}}}œ>}š'Ÿ|’þð‡twwóæ›o[¯ºº:êêêØ±cù—ÉC=ıcǸqã/¼ð^¯—ýû÷óãÿ˜}ûöé E–ÈàA„ñý&÷Nƒºj»6%ÚÕ˜Ù}`JTz4XOOgÏžeÇŽdff2þ|vïÞe…þ0$++‹uëÖ1wî\{ì1¢££ùâ‹/Æ4/˲xâ‰'X°`iiiìÝ»—ææf¾ú꫈ùkkkyì±ÇXµjóæÍcûöí¤¦¦RWW@LL à»#|ll,QQQÚ¨(À™ÍáZC$SEGG–e‘žž˜æv»IJJ É—’’ò:66–;wîŒm£hšddd^'%%áv»ikk‹øÝºu‹… †L_¸p!_ýµVœ,‘q?ˆðÀKœø’ÕT×Zéô¡1ÍÐMZð3I#ôx<úp‹,™ tŠpºn×tŠp¦6ðôh°ÄÄDLÓäÊ•+iÝÝÝ!×WÝMtt4^¯—[·n¦Eºu‚eY!§ÛÚÚèîî&99yH^—ËE\\ÍÍÍ!Ó[ZZùm6›¯™õðùYG¿"‘)Íår‘››Kee%n·›˜˜ª««}7På-æÎK||<ÕÕÕ<þøã´··sæÌ™¡GL“wß}—ÂÂBLÓ¤¼¼œ… †œž VPP@uu5‰‰‰¤¦¦òûßÿž«W¯òÔSO¾k°Ÿ}öqqqØíöÀ¯E–Ìrãw¤Å;î%N\]ÌcÖõ‰o$"œ™šÂÂBÊÊÊ8vì.—‹‚‚:;;±ÛG·³Ùl<ýôÓ”••qøðaÒÓÓyüñÇù§ú§|‡ƒ‚‚Þ|óMnݺ¸MÃpÖ¯_OOO•••Û43wîÜ@ÀVTTÄ©S§¨ªªbÑ¢EºMƒ,‘ñ iÞiPWmצD»3»L‰JO“s:<ùä“×½½½TWW“ŸŸÀ+¯¼2ä=á³Y¸p!?úÑB¦ýä'?ò¾•+W²råʈõغu+[·n좆Á–-[زe˰uÏËË#//OX"""SKkk+mmmdddÐÝÝÍ©S§ÈÎÎVãˆ,™žtŠpV8˜ÜvÕ)™=ï1ª­­¥½½›ÍFzz: ::Z_Ò¢K¦§ñtŠP}@u2•ž& –––ÆÁƒ'tkÖ¬aÍš5úÀ˸ÐmD»Êj 5’ˆˆ,‘)¾XRR¢»Ÿ‰ˆˆˆŒ#Ág“v‘{II‰ÿ/Ãç ¶°ÁCºmÃpåÙG1¯Hƒ1LYw«»–çnuŽ4Ž´\#•ißWcÈ» qx©F„¥3G¨‰9LYf„ù™#´ÐHu³PÞp-câõçó /6ÓÂ4¼˜cËáL·…¥òø§Ù†—e LÊ2Ÿaæ7RýFJ¨Ÿax1üc‚ÓylxÁô‚a ÿž‘ÊKÞ{©[xþº>¨ùÝoYæ=¶ëýÔÝô†~˜ ;àœþq”ˆöQaã|®ù£‚ÒÆÁå„§ÛyõÕWµUÁ™ítýµZLfŸêêj>¬†©{KDDd¦»yó&¥¥¥:tˆÔÔÔÀô?þ˜³gÏrýúuÒÓÓùæ7¿IFF†M–Èì¦{nÝK‹‰Ì²^ïõF| õåË—Y½z5 .Än·SSSÃ?üÃ?ðWõWÄÅÅ©á`‰ÌƤÎYÁ‡:ŸŒ¤§§‡²²2‰ŠŠ¢  €††RSS),,¤´´”µk×rãÆ .^¼HTT›7ofíÚµ455qäÈþú¯ÿ·Û ÀÕ«W9|ø0¯¼ò Ô××SQQÁž={¨¬¬¤««+ð°çøøøa¨S§NñÉ'Ÿöü­o}‹eË–ð‹_üÃ0§—,YÂsÏ=ò\E€]»vÑÐÐÀ¥K—ÈÍÍÕ Ÿt –ÈÀå¤mgKp¥(bÖwûpâÄ ZZZ(..æûßÿ>MMM´¶¶†ä9sæ ééé:tˆuëÖQVVF{{û`Œp$)|Z__§OŸæÉ'Ÿä‡?ü!ÝÝݼùæ›ÃÖ«®®Žºº:vìØÁ_þå_òÐCqìØ1nܸÀ /¼€×ëeÿþýüøÇ?fß¾}ËéëëÃãñ¥•­KDÛDQ‹ÉÄëééáìÙ³ìØ±ƒÌÌLæÏŸÏîÝ»±,+$_VVëÖ­cîܹ<öØcDGGóÅ_Œi^–eñÄO°`ÁÒÒÒØ»w/ÍÍÍ|õÕWó×ÖÖòØc±jÕ*æÍ›ÇöíÛIMM¥®®€˜˜¢¢¢ˆ6€:yò$sæÌaéÒ¥Zá °DfcÎYf}ç›:::°,‹ôôôÀ4·ÛMRRRH¾”””×±±±Ü¹sglEÓ ¹Ð<)) ·ÛM[[[ÄÀïÖ­[,\¸0dúÂ… ùúë¯G=ÏÓ§OsñâEž}öYìv]¹£Kd†Ñ)BEÓ¦óɰÁQH3 ¼^oàïpgÒëüÁðÁðñÌŸ?_+Q–ˆˆÈƒ‘˜˜ˆiš\¹r%0­»»;äúª»‰ŽŽÆëõrëÖ­À´«W¯ÉgYVÈéÀ¶¶6º»»INN’×årGsssÈô–––@~›Íæ‹¡½C£èššNŸ>Í~ðÒÒÒ´¢g‹ØÛ½‡ƒƸÍy6ÂÐašqë|³ŒËå"77—ÊÊJÜn7111TWWcFÄ#S‘Ì;—øøxª««yüñÇiooçÌ™3C:˜&ï¾û.………˜¦Iyy9 . 9=¬  €êêjIMMå÷¿ÿ=W¯^å©§ž|×`9>ûì3âââ°Ûí¸Ýnjjj¨ªªâé§Ÿ&>>žÛ·oàt:q:Zé °DfÖæ,Û9"T 9io*,,¤¬¬ŒcÇŽár¹((( ³³sÔ×,Ùl6ž~úiÊÊÊ8|ø0ééé<þøãüÓ?ýSH>‡ÃAAAo¾ù&·nÝ Ü¦a8ëׯ§§§‡ÊÊÊÀmŠ‹‹™;wn `+**âÔ©STUU±hÑ"ž{î9~÷»ßaYo¼ñFHy[¶laëÖ­Zá °DDD&žÓé ¹wToo/ÕÕÕäççðÊ+¯ yÏ¡C‡B^/\¸ýèG!Ó~ò“Ÿ yßÊ•+Y¹reÄzlݺ5$2 ƒ-[¶°eË–aëž——G^^^È´HõX"3’NN4½·Î7 µ¶¶ÒÖÖFFFÝÝÝœ:u €ììl5Ž(À™ê›cÒæ,ê|j†»©­­¥½½›ÍFzz: ::Z # °Dfmï¥ÅLʽIKKãàÁƒ:5kÖ°fÍ5¶Œ ݦADDDd¼w)KJJ´K)"""2ŽtKDDDD–ˆˆˆÈÔ6i¹—””„¼¸ïÀyƒ_6}¤<Ufø2„ÃÍ/Rþ±Öu¸zŽT÷{iƒáÊÍrOD;Mµv½[;ÞË|ïµÌ‰®ûxô›©Ü®÷Úÿ&ª&ê;ë~æ3žß%áÓ_}õUm•EG°Ddzí£EDDD–ˆˆH€ÇãQ#ȤÒ}°Df¯×«£X2íôôôPVVFcc#QQQÐÐÐ@jj*………”––òÈ#pãÆ Y¹r%{öìáäÉ“466ÒÕÕEll,«W¯fëÖ­˜¦ÉÍ›7ùÅ/~Á /¼ò0ç3gÎPWWÇ¿ûwÿN / °dâL§±Qß›™Nœ8AKK ÅÅÅÄÄÄPUUEkk+©©©!QøÃ’].{÷î%66–ëׯóöÛoÀÒ¥K©¯¯ °êëëyä‘GÔèr_tŠPîÊëõª® D}oÒôôôpöìYvìØAff&óçÏg÷îÝX–’/33“G}”ÄÄDؼy3 , !!åË—³qãF.^¼xO^^çÏŸœR¼rå ׯ_×Ýå¾é–È, dÉtÒÑÑeY!G™Ün7III!ù‚Ó\¸p?üŽŽz{{±, —ËH_±bï¼ó üÙŸýõõõdff’ †X2±tŠPD}o:p8!¯[ZZ8~ü8Û¶m㡇ÂívsþüyΜ9Èc³ÙÈÍÍ¥¾¾ž•+WráÂŠŠŠÔ˜rßtŠPîJ§ˆúÞdJLLÄ4M®\¹˜ÖÝÝM{{ûˆïkii!!!M›6‘žžÎܹs¹yóæ|yyy|þùç|ôÑGX–ÅÊ•+ÕèrßtKDD¦4—ËEnn.•••¸Ýnbbb¨®®Žx#ã`óæÍ£³³“ .žžÎ§Ÿ~Jccã|ÉÉÉ,X°€“'O’——‡Ý®M£(À’@§g]ƒ¥¾7RVVƱcÇ¿ììì1ÊÎÎfÆ ”——ãñxÈÊÊbË–-TWWÉ›——Ç—_~©_Š,ÑYÁƒõ½ÙÃétòä“O^÷ööR]]M~~>¯¼òJÄ÷mß¾íÛ·‡LÛ°aÃ|]]]ÌŸ??â…ò" °DDdFjmm¥­­ŒŒ º»»9uêà;Ju?z{{éèèà_þå_øæ7¿©†XòàèáÌ¢#-ê{ÓUmm-íííØl6ÒÓÓ9pàÑÑÑ÷Ufyy9.\`ÅŠ:=( °Dd¢¾7»¤¥¥qðàÁq/wÏž=ìÙ³G ,ãN·i™eˆˆ(À™~;³%%%º;žˆˆˆÈ8Ò,‘q6i¹ÿ×’’À߆?ÒÁi‘ÒÃß7\žHåÝ­ °PöhækŒ¢îw«s¤¼Æ–ͼv5"´Ãh–k4ó³r¹†”e„VÀ¸—Î`Œ¡áî·ƒNtÃFªŸmŒæ^æ7Öú ÛÕæÃ26ÇC ³û nÏ?Rºm˜<áym£(k¤tã.u®n£™Wx^û=´ƒy—÷š¼úê«Ú*‹Ž`‰ˆÈl1¹?Œ8räZ ¢KDDdª¨®®æðáÃjQ€%""S‰~ %2VºÑ¨Ü•¡ºi‰LvˆçõròäI>ùäl6ùùùlݺ€ÎÎNÊËËùâ‹/0 ƒeË–QTTDll,õõõTWWc%%%†ÁîÝ»Y³fU`‰ö]Gª›¡¤FÅŸ={–G}”^x––Þzë--ZDff&ÇŽÃårñüóÏcYï¼óÿ÷ÿþ_ž{î9V­ZÅõë×ùì³ÏØ¿?^¯·Û­U*N§EDdÊïf¥¤¤°eËæÎKnn.ééé\ºt‰K—.qýúužzê)ÒÒÒÈÈÈ`ïÞ½455qåÊN§Ó4‰‰‰!66»]ÇD–hßuÚÖM 2¾V°ØØXîܹC[[ñññÌ™3'–œœŒÛíæë¯¿VÉ,ѾëL«›AÅãÆÊ Ý\†×«Î/ °DDDÆ]RRtuu¦]¿~îînæÏŸ€ÍfS0& °Dû®3¥nj™9¦npòÐC1þ|Þ|óMZ[[ùòË/yë­·ÈÌÌ$-- €„„:::¸zõ*üãéïï×*• §+ýdµêtjQÿæßüÞ}÷]~ùË_bYYYÒW®\ICCGŽ¡§§G·iX"""Ï=÷ÜiÏ>ûlàïøøø×C6tv;ßýîwÕò@é¡Lë}W¸Q#ȃ ë—D`ɬújÕ×¾ADD–ˆˆLC:L*¢KDDDd²wKJJJt‚ADDDdé–ˆˆˆÈ8›´Û4ü¤¤Ä÷‡Ø‚»pýžîJ· “žÏ1LYöQ¤Wžm„üÒ½þ4¯?Ÿ×^spðà ÷áúýƒÇ?ô#¥‡§GÊœ×6ô…•)½„ùÞm^^LÀÄ‹‰›°ãÅaŒ´ÒGÓiFê¶Qtš±t‚»åª·×–á<þq¤•7ÜJôŒb%÷°¢GZÃuÀ±”Õ7r'3<`zÀ°À´Àôcù(𣸍޵8îò•4š¯¬áÊ · O`l`ùßß#¯dÏé“Þ¡†ë4Ã}›Ü­¼á:E¤NàåÕ’i«,:‚%"3œ®kQ€%"ãLWgÊqäÈ***Ô¢KDfÁQ€%"ãLG°DDE(""S?Þ÷z9yò$Ÿ|ò 6›üü|¶nÝ @UUõõõܾ}›èèhrrr(**âý÷ßçÒ¥K¼ð !e½þúëäää°eË5¬(À‘ÙëìÙ³<ú裼ð ´´´ðÖ[o±hÑ"º»»©««ã™gž!99™Û·osíÚ5V¯^MMM $&&pýúu®_¿>âáE`‰ˆÈ¬’’8â4wî\þå_þ…K—.C\\K—.Å4MâããÉÈÈ`þüù¤¤¤pþüy6oÞ À¹sçÈÈÈ\"E×`‰ˆÈ´°‚ÅÆÆrçÎV­ZE__¥¥¥¼ýöÛ444`YV ßêÕ«9þ|àõ… xøá‡Õ ¢KDDÄ4C7W†aàõz™3g/½ô;wîÄápP^^Î/ùË@µzõjÚÚÚhmm¥¹¹™®®.V­Z¥• §S„"™nÓ ÓeCf·³|ùr–/_κuëø›¿ù®]»FZZsæÌaÉ’%œ;wŽþþ~zè!bbbÔh¢KD&‰nÓ Ó@}}=–e±`ÁçÎÃápȳzõjªªªðx<ªÑD–ˆL"Á’iÀívSSSCee%–e‘’’Bqq1QQQ<999”——cš&+V¬P£‰,™D:‚%SÄsÏ=7dZðmî4¹ÝnþÓúOjHy t‘»ˆˆˆˆ,X"""" °DDDDäÞ%%%º”UDDDdé–ˆˆˆÈ8›´Û4””ü?A¯Œ ±áû|Føkß߆Úàë ¿ 3ðzpl†äÃð•ãóxœîŸ†‰öÚ¨›?%æ5þg†½Ç K7—$3øïà2BjŠ¿®Aéþצ1rúàëðqhºi„æa˜÷š_{C×°TfÄtoX9Þ 4}`/Áÿ~#¬¼¿ýc3ìµaxÖÅV‡¡u‹XŽÿžáïó½Cê1І¦á ä1Á2¯‹á¶L‚§^_;øÿ¯^ÞÁt‚ÓÃæ7Ì@p™æZVè{0½ÞëO'(ŸáÅ0óà/3ðÚ -cÈk׌@šå/s -¨ì2‚;AP~Ó¿®ÌÁ¯&ÃÿÊ2;sàƒb„uts0Íôb˜¡yM¯œÇ Ê뼆T–‰7$Ýÿþ ÷z ›l‚ió—oå÷MÇð òÒi¦oxm÷ÿm ¾4#xøÞõ ÆaØýcß÷(ïgƒÿ÷?¦­²(À‘iÊP]§òâzµ"†8rä©©©£¾ {[[o½õW¯^%))‰C‡és/ °DDD‚íÛ·›Í6êüUUU8N^zé%œN§P`‰ÈàeúšNu§ÅU͆ ~ìÍhttt°|ùrâããïyžgLAˆ,‘QÐcø´îTé©#øaii)k×®åÆ\¼x‘¨¨(6oÞÌÚµk())Á0 ®\¹Â©S§Ø²e [·n¥³³“ÊÊJ>ÿüs Ã`Ñ¢E ýÖ[oÑÝÝMzz:}ôv»ûoÿí]çpíÚ5***hiiÁáp““ÃŽ;tôl–Ó¯E¦Õ‘„Y¶17fɺóNõU3µjvæÌÒÓÓ9tèëÖ­£¬¬Œööv~ü㓜œÌÆù÷ÿþß³qãF<GÅårqàÀ~øÃâr¹8zô('Pî¥K—hooç/þâ/(..Õüz{{9zô(QQQ1–»®{½Þûºû»ˆ,Ñ.ð”Øåžº9™òM'ê£|ŠÐëõRSSÃÇÌíÛ·IJJbóæÍäääpóæMþîïþÃ0øéOŠaäææŽúè—ˆ,mR´ i~:‚5K—@¦BŸxP¿Ó§OsþüyþüÏÿœ¹sçrùòeŽ?NLL ‹-bß¾}¼ñƼüòË8N‡V¨(ÀN?§OŸfÿþý,X°€ÄÄDš››ùÝï~ÇâÅ‹‰ŠŠ ::·Û­FX"""#¹qã}}}üýßÿ}ÈtÇCZZšH`‰ˆˆŒUoo/ßûÞ÷ˆ‹‹ ݨٵYX"""c–œœŒÝn§³³“Å‹GÌc³Ù߯ E`=púà‰ˆL·oU—ËÅÆ©¨¨À²,-ZDOOÍÍ͸Ýnrss‰àøYYY8œN§V(À’B·iЪõ‰ ðøãCMM ¸ÝnÒÒÒØ´isæÌaÛ¶m¼÷Þ{üú׿ÖmD–hwö¾ç§Û4ÌÒ%™Þ'úûûCŽB­_¿žõë×›óæÍlÞ¼Y+R&…•#3kXÊ™ÅK 3µOX–Åõë×ùòË/™?¾VŒ(ÀÒׇLÊ,íëÏÒ%©Ð'&â[õúõëüíßþ-óçÏ'??_+F¦"‘)-55•ÿøÿ£B¦"Q€%"""¢KDDDdV™´k°JJ‚/…´üC¿ÖÈdX}jŠËæ|¼€G2uwGµK:aô“#™‰_""""2Ž&íÖÿxý¿úöZ ;†Ý…iscÚ\˜¦ › ¦a`3|cÓÃðEƒ6ÛcÀ äÌkó§Ù‚¦ÑÐô÷–g ¦—Èo•å{¿-lÁu7‚—34ß@;ö J¤ ¶›i¥ÿmþ÷ÁùÌiCÓ ÿ`ú+3˜nÒ† fhÃFPCTØj¨¡UVC IDAT+"4 16,_pzXÞÀx`ðåõšà5|¯Ò¬×Áé†eFû+xº–éƒã|ÿ?{oÕu/~~Îmµv¤–ZH-! ÉBf‰ FØ&–XB€@y{Ä~qœ¼‰+©Ÿk*35UVf~ó~É«©&oË‹ó&²Ç‰ò0‹1;F¶!„MX`À,$Ђhë¾óGw_Ýnµ@Bjõ÷CŽî9ß³}Ϲ÷~ï9·ïѧ¦Œ8§'Ìeò]ß©ÜrNï1ª'<žp4\(“sǹt…ÓçDÃ¥+tŒŽÂ¥k8uw¸ —nržcݓΥk躆K·¸ã]t4t]¡{âtÝâ×-¸\º+ÌæVroAwYÀåöuÝ‚[‘ÞÎÐÀeñüm1»ÿVžNPžŽRž0eÄÒUs)4¯ïR(”šGNs¹ÿvû:ão÷±¦ƒ†n[<2]GÓu÷ÐÓu÷°Õu,ºËí£÷øgȹ5èç3cÝsméñÍCÞ}íð½.ø^#|Ïk¥)÷uLSƹlœ£Æ¹Ûãt¯oÑ<çŽòœ#_S¸4…nQ¸4·ŒKõ„;- §¦á´hîsÃ;þ=ylýÿþQîÊ‚Ì` ‚ð€(iï¨Õ—¬W*ÊÊÊØ¹s§(BKAA ,A>º´wÔêK>k/Â"_r”´gl4T–‰d ýöèHÙ¡çÎcãÆ,[¶ŒÄÄDöíÛG}}=N§‡ÃAII ©©©†|ii)Ë–-ãܹs\ºt‰øøx¾óïÃÖ­[¹v퇃U«V‘@SS»víâêÕ«tuu‘””Ä¢E‹xä‘GŒ|»»»Ù·oUUU´··“’’¢E‹ÈÊÊ’"È –<üÊÃ|Ÿ ;+ä,¹. 'OždãÆ¬^½š¼¼<:;;)((àÕW_å?øv»?üátvvú¤ûôÓO)((àõ×_güøñlܸ‘mÛ¶1wî\~øÃ¢ë:Û·o7ä;;;yôÑGY·n¯¿þ:¹¹¹¼ÿþû´¶¶2Û·oçÚµk¬Y³†ÿøÇL›6÷Þ{¦¦& ‚X‚ –k´W–CžÃ‡³}ûv^|ñErssÈÎÎæ›ßü&v»¤¤$–-[FWWÕÕÕ>i§OŸÎ´iÓ°Ûí<ùä“´´´ðÍo~“œœ’’’(**òIãp8˜1cãÇ'11‘ ÀÙ³ghmmåĉ¬Y³†ÌÌLxâ‰'ÈÌÌäøñãÒY‚,Êï<Ì÷ÙP™ÅQ$ôÛ£Ó=}ú4mmm¼úꫤ¥¥á·oßæã?¦ººš¶¶6t]§««Ëg¦ %%Åø;&&€äädŸ°îîn:::ˆˆˆ ³³“ýû÷óÕW_qûöm\.ÝÝÝF¾×¯_ÇårñÏÿüÏ>å8N¢££e€b`ÉÃïàÚ£B¥ãÔClx(.ꨯ‡ur)dk”]çRSS©««ãرc>Ö¦M›hoogÉ’%ÄÇÇc±XøÝï~‡Óé»'‚¦õ,Ø(¥ú ÓuwËvíÚÅ¥K—xöÙgILL$,,Œ 6ùvvv¢i?üá´^ÂÃÃe€b` ‚ £Ÿ„„ž}öYÊÊÊÐ4%K–PSSòe˘4ià^º»sçÎÀ6´êÿ §¦¦†‚‚¦L™@GG--->Ÿ®ë´µµ‘™™)$ôBÞÁ’‰‰kÏ :Ü ×Cì¬w°äºð€ØívÖ®]ËéÓ§Úív*++ihhàêÕ«|ðÁX­Ö»X×û ³Ûí|ùå—Ô××S__Ï|Ð+>//M›6ñå—_ÒÜÜÌÕ«Wùì³Ïøê«¯d€2ƒ%׿ÁµG–År}`åÊ¡\熀¤¤$Ö®]Ë;#¦i|ç;ßaëÖ­üö·¿%..Žgžy†Ý»wûvg€ÙªÂŠ‹‹Ù²e ÿùŸÿItt4O=õ>ò+V¬àÓO?e÷îÝܺu‹èèhÒÓÓ™UàÞ#‹¿¼é ¿>ŽûÏÏÞw¼Y¦¯´}ûÖ¿ºø¥Qzuòúśޫ,_½'\ª›nÔ¸.½ÂUÿuïH¶Ÿ< ¶¿øžº«ÀéM}¨îþñ½òò/;`¼ œÅ«€ñþùšëÖ;ß°,üâÖÍo`›ã÷W7ÿA©õÓÎôê?ú=Ö{¾ºÐýÎÇÇÎSpl÷Äk~çšò‰7ù¦Ï·xÃ{®™º‘ pÐ<Ç>¾ò¦Ó}¾–Ò«L#¾w|ʤ§ÌÿñË<  3XBLïû*y9y˜Æ€’;œcOãù)'› ˆ%<Áôsl]~;>Lc@—;œcOÆóóá•\VVfls¯´´´PZZJ}}½œÈ‚X‚ÌŒÈ,‚ÐϬa{2ƒ5Ì}#A ,AæFn!TŒu?c@šº÷ – ¢óS¢ë‚Lm ÙìYA*Î;ÇÆY¶lyyy=z”ŠŠ ZZZ°ÙlÌž=›Y³fL[]]MYY/¾ø"ûöíãæÍ›8–/_Nrr²(WKA=Nž<ÉG}ÄêÕ«ÉÍÍåäÉ“”——³téRuuu|øá‡„‡‡“ŸŸßg>{öìañâÅÄÆÆ²wï^Þÿ}~ò“Ÿ i²°# 2’A„ àðáÃlß¾_|‘ÜÜ\ÊËË)..fÊ”)Øl6¦NJQQGŽé7¯ùóçóÈ#œœÌÊ•+¹}û6_~ù¥(Y2dKAõœ>}š¶¶6^}õUÒÒÒèì줩©‰-[¶°uëVCÖårÙg^J)ÒÓÓ㨨(ìv;¢hA ,Avtô‘yÑ]'x~Lu² HMM¥®®ŽcÇŽùXË—/g„ >ò²Ô'Œ42…‘Ï4ò™†a{ò™†IHH`íÚµœ={–íÛ·˸qãhnn&11ÑÇÙl¶¾ÍX]çêÕ«ÆñÝ»w¹yó&IIIr² C†Ì` cjA×ÅÈž1 ‘‘52#öÆÞTyäÎχ?Ûf·ÛY»v-eeehšFII ,`ÇŽDDD0iÒ$º»»©­­¥½½9sæô™×'Ÿ|BTT111|üñÇÄÄÄ0eÊ9Ù1°„±>0³B?c@f°†uìí ÖÃ7²’’’X»v-ï¼óš¦ñì³ÏbµZ9xð {öìÁjµ’’’BQQ‘©o”__)-ZÄÎ;ijjÂápð /`±XädÄÀÆú|ÀÌ"„ŠÑrŸ‘úªà±C3ƒuïFK(Ì`­[·Îçxüøñ¼ùæ›Æq^^yyyÓÚl6Þzë­^á™™™üøÇ?– ¼0lÈ;X‚ Bh=4Ê—Ý1°Aah‘½ …‡, ‚ !CVVVÀ%CAjdKAaˆ±¬ÿé…êÐ8F'ªÏ^ÓGݘzHãK—á+‚0’È – ‚ Â3b3X¥¥ÿ‡éH™|å±ûÜ»_FT¦cÍÏ÷„ûÅõ>ÖzŽ}òÖ|ä8O¼BSz…Éyâ{ÒhƱ2çë‰×ÌiQ(½'¾çŸ;^3ÅiFž”V¦Rð“3Z¬Lƒ§î=š×”9)^™â•§w”oo™e5¿c³o¤1åcNoŽ×üÒkò3Ò(óñäkÈê>qòôm‹î; ûH¯Èϱæ#çŸ^÷Ó»n„iJ\#Oݯîº'ÝG^S¾Ç¨ÞùùûFÝ•_¼©~î¿{Ú©”¯Œ·/Pþñž2¼¾f’ÃWÞç•÷*Äsêöø¾— ÷¹lR´Òzâ•߀U~¨4Õ÷àмò=aJ’W~ƒO™O“Œò”§Ìƒ¨·”ŒæwRôŽÓ<4ߺa®·Ö»Þ†Ž¼exò3ŽÍå÷UwSgyëâ 7§5ÉüŸÿý¹+ 2ƒ%‚0ìȽAKAAK„Ѽ¨/B"ßÁAð¬Ó Õ÷ÕCׯ.½,í÷AYYÉÉÉhšÆ‰'°X,<óÌ3<öØclß¾Ó§OËâÅ‹ÉÍÍÅårñá‡réÒ%nß¾M||<³fÍòÙŸÐår±sçNNž<‰¦i̘1ƒ[·nÑÑÑÁóÏ?_åzó¨lA ,A0M#ÇE|¨ö"|¸-Ö¥—‡Ðv¦½3Cíü|*++yòÉ'yíµ×¨ªªbÛ¶m|ùå—L:•yóæQQQÁ¦M›øÙÏ~†¦iÄÇÇóÜsÏEMM ~ø!ãÆãßø ªªŠ+V””Ä¡C‡8sæ ÙÙÙ÷]®ÕjE×õËBY"atÛ‚8æÍ›Gbb"O=õaaaDGGSXXHbb"O?ý4wîÜáúõëX,æÏŸOjj*6›¼¼< 8uꔑßáÇ™;w.S¦L!))‰%K–ù@åƒ*[ dKži„‘ª©,†@/‡F;¤Å¾¤¤¤ôÌ hQQQ>a±±±´µµÔñãÇimm¥»»§Ó‰Ãá ½½Û·o“––æ“gZZZ¯ ïµÜÊÄÀ¿iY"^ýJ/•í K„cMÓü¦T¯0÷5@§ªªŠÝ»wSRRBzz:áááûì3HJJâðáô··{vøÞ²1°áþïr#USY"[½,K„Òâ{zÀroe4sæLêëëù¯ÿú/”R<öØc<þøã|õÕW†ìSO=E[[›7oF)ÅŒ3ÈÉÉ ¸ü7˜r½Ì˜1cÀ²…¥¥¥#ru—½e/BÙ‹Pö"p/Boy²¡ìE8ìg:ÿò/ÿÂc=Æ‚ Ä:dK¥Ó#öŒ-–%¡œœ‘%Ba0´´´pá²²²èîîæðáô´´——'ÊÄÀFñ]nÄžBe‰pì÷rh´CZ<ÜcŠ'N°gÏt]'99™µk×’””$ÊÄÀa¬ßEÂðÏ«¯¾*І ù’» ‚ ÂP?ŽÔKî‚ ‚ c™ÁAAbFì¬ÒÒR…gÁŠ•0¬X 'œ"ˆ$’H"ˆÄJ¸ÇõÄGE„oõ‰×а`ñüoéõY ô–q¢èDÑarí&×á‰ïBÑ t ¸kŠwÇaø:Àbøn§™œeg’Õ4ˆÀí"pOoZ=~¸'<"€‹4ÉyÓhœù§ð–qÆ÷ tO˜Ç×tPN?ç\n¿Wœ¼ISœÓýü@ñ:è. ônßz»¯£ÓN'èžð»¿Ã×x]·Û÷¶Ùüm‡œYw–~t~/yù§± "/E¿uW¡ Ry†òB1@¬Çñ^þñQ:XM.Ì4T4À¢÷všI¦Wœg˜hzÏA÷õ5—ihéî®R&y³3%å28ûp.“ï2{]§Ÿ»ÀÝ1ýÝÑ#«wí=Nï0Fr¿#Ý5ÀÙàwV¾ùêâÊhºÅ‚ DFö¸¨(ˆu»qã &¦'Ü?nœÛ÷Æ{ÕʶmÛä®,È – £y\Ô!‚X‚ 1ò¥¨#Ä)--åÌ™3¢ÑŸX‚ !2e#êqÞ|óMrssC¦½ëׯçСCÒñ£ù– ;òÉ1£ŽÑñMÿà#66V”à‡Ëå2öfÄÀA„^”••‘’’BXXÇŽÃb±0sæLæÏŸ¸—¸žþy¦L™BKK ëׯgõêÕ|þùçÔÕÕ‘˜˜È’%KÈÊÊà׿þ53gÎä‰'ž0ʨ««ã?þã?xã7HLLì·>û÷ïçĉܾ}›èèh¦M›ÆâÅ‹÷ìÒôéÓihhàìÙ³DFF2wî\üq#}{{;»víâìÙ³8NÒÒÒ(..Æáp2gÏžå“O>áÆ„‡‡3qâDž{î9ÊÊÊhiia×®]ìܹ¥o½õÇg×®]¬\¹’½{÷róæMÞxã ÚÚÚØ·oõõõ8N%%%¤¦¦ÊÀKAu*++™3g?øÁ¨©©aóæÍdffòÈ#”ß³g%%%Œ?ž¿þõ¯¼ÿþûüô§?%**ŠéÓ§sâÄ ëĉdee h\:uŠC‡±fÍÆÏíÛ·¹~ýºÌ_ÿúWæÍ›Ç‚ 8þ<;wî$))É¨ë† çå—_&""‚#GŽðî»ïò“Ÿü„¨¨(Î;ÇŸÿügæÍ›ÇªU«p¹\|õÕW<÷Üsüû¿ÿ;3gΤ°°Ð(S)EWWdùòåDGGCss3L˜0]×ùë_ÿÊþðÞxã ÂÃÃe` #ò– ‚0êIIIáé§Ÿ&11‘üü|ÒÒÒ¸xñbŸò?þ8S§N%))‰eË–Á±cÇ((( ±±‘k×®àt:ùâ‹/˜>}ú€õ¸uëãÆã‘G!>>ž &ø:™™™<ù䓨ívfϞʹiÓ¨¨¨àòåËÔÖÖ²fÍRSSILLäÙgŸ%22’Ó§OðÙgŸ‘——ÇüùóIJJ"99™'Ÿ|€¨¨(4M#<<œØØXŸåQ—ËÅÒ¥KÉÈÈÀn·cµZÉÎÎæ›ßü&v»ÝÐEWWÕÕÕ2¨†™ÁA‚ÂÀ2K[[[Ÿòééé=3 šFZZŒ7ŽÜÜ\Ž?΄ Œ¥ºiÓ¦ XiÓ¦qèÐ!Ö¯_ϤI“ÈÍÍeòäÉhš°lïñçŸÀõë×éèèàW¿ú•Lww7ÍÍÍÔ××3cÆŒ{Ö‘Åb饧۷oóñÇS]]M[[º®ÓÕÕEkk« *1°AByÁ½oÌ ¸—Ätýþ5VXXȦM›())áĉ<öØcX­ÖÓÅÇÇó“Ÿü„‹/rá¶oßÎ_ÿúW¾ÿýï÷ªc :;;7nßÿþ÷{Õ?22Ò}c»¿[s t›6m¢½½%K–Åbáw¿ûN§SÕpYQ 9ò#!Q‡Ð‹«W¯»\.êêêHJJ2Ârss çoûçÏŸÔò ÙyôÑGY¼x1k×®¥¦¦Æç=,sÙÞcoÙ©©©Ü¾}¥‰‰‰>.::pÏÖõ·üi±Xm\ÖÔÔ0{öl&MšÄøñã±X,ܹsGˆX‚ È´‡¨C¸wþö·¿ñå—_ÒØØÈG}D{{»¥iùùùìÝ»»ÝÞkY¯/Nœ8Á±cǸqãÍÍ͜ڣ©¾ño·ßßÅÔ[ÇZ?i¬{ßuSJ¡)…¦@SÊs Šž0MSXL2Í?Ç·œ'Þâ 3Ëúçá®–§L¥°˜ä,žºõ” n~²æºª«Å¯Êh³ožæº+¿´æºk¦ºû–ã­;}×ݤW¥”gh{òÕzò0·Ï®T`½zÝ@u7×E)Å/~ñ ¹+ 2ƒ%B°¢¤ª‚ b` ‚ ‚ ˆ%¨F—ª ‚ 3ò™a(©« ŒÒ±*ÛÈuww³{÷nN:EGGiii3aÂnܸÁÞ½{¹|ù2º®“ššÊŠ+HHHàèÑ£TTTÐÒÒ‚ÍfcöìÙÌš5ËÈÏž=œ9s†[·nK^^óçÏGÓÜóåååœ9s†9sæ°ÿ~îÞ½Knn.Ë—/'<<\†¿ –p¿ÓJê*†€TuŽ=] #Ëk­\¹’øøx<È{ï½Ço¼AWW¿ÿýïÉÎÎfݺuDDDPSSƒËåÜ_g///géÒ¥8êêêøðà 7ö!Œˆˆ`åÊ•ÄÆÆrãÆ ¶nÝJDDO>ù¤Q‡¦¦&Ξ=ËK/½ÄÝ»wÙ°a`áÂ…rYÄÀ!ˆŒP±— ³³“#GްråJ&MšÀ·¿ým.\¸Àñãǹ{÷.‘‘‘¬^½Ú˜q2,´¼¼œââbc“f›ÍFCCGŽ1 ¬yóæò6›'žx‚ªª* `ÅŠÆŒU~~>/^KK…i¹ ¡5öBaöª¹¹—ËEFF†f±X˜0a ܾ}›‰'Æ•¿qÖÔÔÄ–-[غu«îr¹ˆŒŒ4Ž«ªªøüóÏinn¦³³—ËEDD„O^6›Íg9066–¶¶6ú‚XB(L#Ȕǘ2Be‰pàRõÐy«ÏYXß·²ÎÎN–/_n¼¯åÅkÕÔÔðÁ°`ÁrrrˆŒŒä‹/¾ ¢¢" |q«Ðuù%† – ‚„$$$ iW®\!//§ÓImm-EEEtvvRYY‰ËåêeÅÆÆ2nÜ8š››´þÔÔÔ`³Ù˜;w®ÖÒÒ"ŠÄÀd!8ë:RÈ;Xciì…ÂìUxx8³fÍbÏž=DEEÇÁƒéêꢰ°—ËÅáÇùË_þÂܹs‰ˆˆàêÕ«¤§§c·ÛY°`;vì ""‚I“&ÑÝÝMmm-íííÌ™3»ÝNkk+UUU¤¥¥qîÜ9Μ9#— A ,AîrAzG#tLÙ˲D8œ,Z´]×Ù´i“ñ™†—_~ÙxjíÚµìÙ³‡²²2”R8233(,,ÄjµrðàAöìÙƒÕj%%%…¢¢"&OžLQQÛ·oÇét’››ËÓO?Myy¹\.„û¿|•––ŽÈ²ìEˆìE({ŽÐ^„ƒÑ£c/BÏÞx²¡ìE(Á†|É]cÓ2{5¸™©êX{J†¼ ˆ%È Yê*Èy2Ä¥Ê1°A È;X‚ b` ‚ ‚ #ö+ÂRU*O¨‚àƒî燨 ôÐmºÓãA~dKAaˆ±¬ÿþ‡Jß§uåö•ò<Âúcú%¿îùY·o¼Â/ÌGÆ7Þ‹9Õû×8>ñé]·@Çô?pZÕŸ¬‡¾âºÇ,¯ð:zä<ý£)ÝÑLqþ²šéXóä§X^땟|¼ñþ¾YŸx|å8MùËö¨Õ§ ^ݤ#s¸É÷­‹ŸŒ®ú¬‹|¯ºxâ´>d~yûÉ+½w¼fï+Þ“¯»<ÕS®Y^ï-ƒ®||­×±fc”§pKz»%=¾{D(eò•|Ž•çXÖ¯4O˜YÆät#<°Œîý[ÓLñM+åŽ×”IÞ”æ¼Üëšæ¦›ätŸzxd‹“ò‹7µW©Þe8ö½Ø)SÛýë¢ütÖ»\]i~²þíõWÊÜ— ¥4¿óHñëSÿ(weAf°A„¡EÞ˜ÊÊÊØ¹s§(B3X‚ ‚ð0ؼy3ííí<ÿüóC’ßsÏ=‡ÅbÅ b` ‚ òI«ÑÓéÄb±%ÊBÍÀRÁs‰ ‘£Ë’GHt³ðÀʾ“,ðÕvø‚ÑŸ IDAT*ÓÝÝÍîÝ»9uê”±aqq1&LàÆìÝ»—Ë—/£ë:©©©¬X±‚ÊÊJNœ8RŠÒÒR”R¬]»–¬¬,öìÙÙ3g¸uë±±±äåå1þ|4ÍýöLyy9gΜáñÇçÓO?¥µµ•·Þz‹ßÿþ÷¤¦¦RRRÀúõë™1cMMMœ:uЍ¨(æÍ›ÇŒ3Œú·¶¶²{÷n.\¸€RŠÌÌL/^ŒÍf“ñ/–<ÿ yUC`ÿdÙzY†¤0å»wl¾¯GÄê8ý!WÆk ­\¹’øøx<È{ï½Ço¼AWW¿ÿýïÉÎÎfݺuDDDPSSƒËåâ‰'ž ¡¡ÎÎNV¬X®ëÆ TDD+W®$66–7n°uëV"""xòÉ'r›ššøòË/yþùç=/êcøf***X°`sçÎåôéÓlÛ¶¬¬,ìv;N§“÷Þ{ŒŒ ^yå4MãÓO?å½÷ÞãG?ú‘,7Š%‚ È#âç³³“#GްråJ&MšÀ·¿ým.\¸Àñãǹ{÷.‘‘‘¬^½Ú˜}JLL4Ò[­VœN'111>ùΛ7ÏøÛf³ñÄOPUUåc`9NV®\Ittt¿uÌÍÍeÖ¬Y<õÔSTTTpéÒ%ìv;UUUèºÎòåË ùåË—ó«_ýŠêêjrrr¤“ÅÀíÈáh««Ìˆ„D7 ¬|Y"ìææf\.F˜Åba„ 444pûöm&NœhWƒ¥ªªŠÏ?ÿœææf:;;q¹\DDDøÈØl¶+€””ŸãØØXÚÚÚ¸~ý:MMMüã?ú~†¢»»›ææfÿb`ÉóßWU–’ÈáƒÞÈÂîýVVSSÃ|À‚ ÈÉÉ!22’/¾ø‚ŠŠ 9«Õ:¨üü;¥ºg'îÎÎNÒÒÒøîw¿k„yñŸUÄÀA„‡BBBš¦qåÊòòò÷Ò]mm-EEEtvvRYY‰Ëå 8‹e±Xz6555Øl6æÎk„µ´´ KýSSS9uêÑÑѽfÈ„±ËûШ’ªŽ²ºÊŒˆ Ia0Êwk_¢ŽS±2áááÌš5‹={öpþüyã…ô®®. yüñÇéèèà/ù µµµÜ¼y“ÊÊJnÞ¼ ¸—ù®_¿Ncc#wîÜÁétb·Ûimm¥ªªŠ¦¦&:Ä™3g†¥þßüæ7‰ŽŽæOú—/_¦¹¹™K—.±cÇnݺ%ãŒ"K„#YUY"dH K„ƒ`Ñ¢EèºÎ¦M›ŒÏ4¼üòËDFF°víZöìÙCYYJ)™™™R]]Íoû[ºººX»v-“'O¦¨¨ˆíÛ·ãt:ÉÍÍåé§Ÿ¦¼¼|ÈënµZùþ÷¿ÏÞ½{Ù°aÄÅÅ‘-3Zcù¹©´´tD¬Ù‹Pö"”½e/B½u¿}ÿtÙ‹PƲD(Uֺʌˆ Ia0Ê—%BAkT£KUGY]å»>2$…Á(?øN2]F‘ „’%‚¼Èü ˆ%‚ ‚ ôÁˆýŠð{)_´/ŒjÖLÇÊï'k:öñ½…÷%oª\_ùŒgùiªÿ²üëFï6ôÈx œß`ãýÛÒKï˜ÛGßyÑ[ yåW·òë§nÚBzõK‹ù{MjÇôßSààò# Œ^þ_eë»îÜãq ]øÖÉ¿-ÛÉ=êÁ7þ¿ø…Ü•™Á UäW>Â%¥d °~ýz:4hùêêjJKKiooå coKA†‚×^{ «Õ8‚XB`d¿6aÈÆ’®ÌE^±0ÆˆŽŽ%b` ÁG0=é)±†I¯A5`CLÃjÌ«µ¬¬Œääd*++±X,Ìœ9“… î%¢¢"ŠŠŠ(--eùòåœ;wŽ .0nÜ8Š‹‹™á±ÇãG?ú¹¹¹lܸ‘»wïö’»{÷.ï¾û.ßûÞ÷ĸÄÀ ÆË  Œ ÃU±„ÄÅÅQRR‚Ýn'//Ù³gSQQѧ|AA=ö‰‰‰<óÌ3tvvríÚ5™Û·oSVVF\\/¼ðaa²¨#ˆ%|ØøÕàƒœÏ>û,yyy¼óÎ;466ÊMN2ä¾±|£BC XŒ,=hŒ¬`["TÁ3`ƒÐÈz ?œÞiµæççÓÝÝÍÛo¿¦i1cÆŒû>·Ì2%%%èºÎ;ï¼Ãºuë°Ûír³üR[ZZ:"ÑÁºa{ Ê^„²á½ìE8¨²üëÆìE8Ðþ}²!²áèÚ‹°¬¬ ‡ÃAII‰Üµ… A–ïãY1ä¬pY"¶™Á`IJD8 §rïÈÎ2‚ –0V û²ä#O!ÿ`!¿"µ Â(@ÞÁËOòÂèKAô– Œ$ëÖ­%A‡Ì` ‚ ‚ 1#6ƒõŠç%÷PEGv,>eê}ü-‚ ™ÁAAbFlëÿúá(ÝýË/ ÐP~”®¼?êu.Áó—†o¸B¡éfüdÌi<Δ·fÊ CÆ/=P¾ó ßG¾]Oœ·nÓüäé+?e–¥t|~°­üåuó×ДÞ#£tß4Þ8ÃéF¸¦|åñ‘×}ä1¥óÏÇëð+¿x³¼75¸<Íyõ¤é#>€¬Û諜>âéÉ\ß<×ÍÈÛ¯ŒþÚ†Oyz඘|ðk÷u7â=ùÑoÙþñý´Í_ÿÊO^ë?/üô„_ޘ뢭·ú•7N.Ýäzò!PÝýê†YOþáZïxÝ(ÃO^ó‹Ç{ìsq›ê®k¾]ë‘Ó5åW¶òÉC×|/BÞ²½eêÞöÒÓîÿç—kå®,È ÖèBÁ’„±ÞS*ä4#g b` ‚ BP°~ýz:$ŠÄÀzô1X’0Ö{J9ÍÈÙ#‚X°£‚¸¦J´*Èñ˹&ý3F>4Œï`Ïö¹½kªA]£}Æ$4ßÁÒG`¼ŽqF¨Áóì*++#99€ÊÊJ, 3gÎdáÂ…å+**8~ü8ÍÍÍDEE1yòd¾õ­oîþqUKK Û·oçÊ•+8NøÖ·¾Enn.ÕÕÕ”••ñw÷wìÝ»—ÆÆF222X½z5µµµìÚµ‹¯¿þšG}”åË—cµZ8þ<Ÿ~ú)7nÜ@)EFF%%%$&&Š¥!–\aäâ=Öî:2jG×–%Báþ©¬¬dúôé¼öÚkÔÖÖ²uëVl6………½My¥X²d 6›ææf>úè#öìÙÃÒ¥Køè£p¹\¼òÊ+X­V ãËË'Ÿ|ÂÒ¥K±Z­lذ¿üå/„……±zõj:;;ùÓŸþÄáÇyòÉ'èììdΜ98:::Ø¿?þóŸùÑ~$¢ÈaÈÍ_ EMe‰P3+Ôü°‹‹‹£¤¤»ÝN^^³gϦ¢¢" lQQYYYØl6²³³Y¸p!§N2âoݺEff&ÉÉÉ$$$ðè£2qâDmáÂ…dddàp8(,,äòåË,[¶ ‡ÃAff&Ó¦MãÒ¥KFšiÓ¦1uêTp8,_¾œëׯsãÆ 9)CY"±’d‰pxë*K„£Ï‘%±Ôà‡}v¥§§÷:®¨¨¸ý… 8pàtttàr¹p:tuuaµZ™={6Û¶mãüùó<òÈ#L›6””Ÿ<ÌÇ111X­Vl6›Oصk׌ã›7o²ÿ~®]»Æ;wÐu¥­­­Æò¦ – ‚ %---¼ÿþûÌš5‹gžy†¨¨(®\¹ÂÖ­[q:X­V ™4içÎ3Œ±ââbüq#MëYàQJù{ÃÌÆÝÿøGX¾|9ãÆC×uþõ_ÿ§Ó)¢ÈgF¬$Y"ÞºÊgF[¹«dY"›Åšg‹®^½Jbb"JùÖ¤¶¶]×)..&==»Ýέ[·zåÇÌ™3yî¹ç˜3gG½ïºÝ¹s‡›7o2oÞ<²³³IJJâÎ;ba„8²D8b%ÉáðÖU–GŸ"K„c©Áûìjmme×®]̘1ƒºº:>Lqqq/¹ÄÄD\.‡bòäÉ\¹r¥—ñ´sçN&Mš„ÝnçîÝ»TWW3~üøž¶é÷Öº¨¨(¢££9zô(±±±´´´°oß¾^ÆŸ – ‚ Œ*òóóéîîæí·ßFÓ4ŠŠŠ˜1cF/9‡ÃAqq1dß¾}Lœ8‘E‹±iÓ&CÆår±}ûvnݺEDD¹¹¹>ÆÚ½FJ)V¯^ÍŽ;ø·û7’’’X¼x1eeeÒq!Œ*--‘Ç|ÙìYá·ê¨ÝìÙgf¼úê£r³gÍ›lö²›={ÇRÈlöì³yóÃÛìM¹g±ÂfÏeee8JJJä®- ò™†Câšê¢UAÆ@ˆ7XÎ5A KÁ’„±ÞS¡ù– Bh ï` ‚ £šuëÖ‰1°Ë›ÿK§h”ây+B¸/”Ÿ/È!‘w°AAÄÀAAÝŒØá[¥¥=5ˆ"=þ`ëñS€D Áí_³Â% Tknw½n´Ã¸Þw.åÓv±€¶KùÜ­~ŒˆúDÂëÜ.êfã$`>ž7ß|“èèhQ†0"È – !ˆü q,ãt:QJ‹¦ÉmN‚kKfõƒRwÊÿ@B)œ÷>ÈD'rášRÊÊÊHNN ²²‹ÅÂÌ™3Y¸p!ëׯgúôé455qæÌ¦NÊüùóY¿~=¯¿þ:‡ƒ»wï²}ûv.\¸@gg'ñññÌ;—‚‚ZZZX¿~=kÖ¬áóÏ?§¶¶–ääd¾ûÝïÒÞÞÎG}Dcc#™™™¬ZµJfÅ„1h`ÉCgPêNÖ>Ôï¥Þ28{ë`4ëDIŸ¢ Ç`J©¬¬dúôé¼öÚkÔÖÖ²uëVl6………TTTðôÓO3þüž^6mÚüñÇÓØØÈË/¿LTTMMMtwwû”Q^^NII ñññlÞ¼™7ÁâÅ‹±Z­lذýû÷³téRÂ3°Aäi-$‰‹‹36{¶Ûí\¿~ŠŠ ÃÀÊÎÎfΜ9†|KK ºÞÓÏ·nÝÂápšš €ÍfëUÆOïYõ2£ý⢣£ÉÏÏgÕªU”””pôèÑ>eáA‘_† ò+«¸,ÞÇ Í:‘YˆÑtáL)­­­ìÚµ‹3fPWWÇáÇ)..î¿—M³Mû÷ï'55•äädº»»9wîãÇ(Û_˜ ŒMKÆzPêN–CuɯåÂ1t¥äççÓÝÝÍÛo¿¦i1cÆŒþ{Ù4+e±XØ·o---X­V233Y½zu@ÙþÂalX‚ BH¢i%%%?‘ðÓŸþ´W˜Ífã­·Þ2Žç͛Ǽyóæí/ ••Õ+¬  €‚‚é apc6¨j+A©;ùa¨2Y"” ‡\Ú1°‚¹&¥îd‰0T™èD.riBY"!‘w°‚‰uëÖ‰„ C>Ó ‚ ‚0Ôq¥¥¥ò'‚ ‚0„È – ‚ Â3bï`•––ŽAu&©&Žû]¯ÓLN™|åÖ—ëOVõ‘gyiƒ¬›ê§.=qÊä‡yœ…Åãkhh(,(#>̈sÇ÷È{ãܾ9Λ×yå-F¸9^ÃæñûÓè@=Õ—Ò˜º™¾zc°² 2²€fåqšTxϱ²€ óøzÿ6Ž=…(r;ò(^yÃ5_߈÷þ­ÈYLòþå)ϱ2Å›â¼ÎHã—6à@Pƒ8 T쨿<´òÓü:x0ƒ€aÂé'{Mà òPð‹_üBîÊ‚Ì` ‚ ‚ b` ‚ Â=Q^^Îo~óQ„ – ‚ ‚XB B¢®*ˆ[j½<æÚ¡¤ã6N§S” +ò¡QaèAt¿ÿºú§ ¦V‡Z/¹vësôèQ***hiiÁf³1{ölfÍš5`;¼õX³f Ÿþ9µµµ$''óÝï~—ööv>úè#ÉÌÌdÕªUDGG°yófÚÛÛ™0a‡Âét2gÎæÎËÞ½{9~ü8V«• 0}út¹e‰%‚ ƒ£²²’éÓ§óÚk¯Q[[ËÖ­[±Ùl:}ûöñì³Ï’ššJXXçÏŸ§««‹Ï>ûŒU«Va±Xضm7nä•W^ ˜ÇÉ“')//géÒ¥8êêêøðà '??Põ(//§¤¤„øøx6oÞÌÆ‰ˆˆ`ñâÅX­V6lØÀþýû}6¶¾téqqq¼òÊ+\¹r…-[¶PSSÃĉùÁ~@UUÛ¶m#''‡¸¸80£Y",J/K;Fm¥Cd ÆÅÅQRR‚Ýn'//Ù³gSQQqOy1uêTl6±±±¸\.–,YBzz:©©©¬\¹’+W®píÚµ>£ââb¦L™‚ÍfcêÔ©qäÈ‘A×ã‰'ž ''‡¤¤$ŠŠŠ¨««ãé§Ÿ&##‡ÃAaa!—.]òIÅ’%K°ÛíLŸ>¤¤$ººº˜;w.‰‰‰<õÔSX,®\¹"·¬Q‚Ì` ƒ@–¥—¥£¶Ò!2PÓÓÓ{WTTàr¹GZZZïYMc„ ÆqRR‘‘‘466ú„tvvÒÔÔÄ–-[غu«îr¹ˆŒŒt=RRRŒ¿cbbŒ%PoX[[›Os¼WÆœ¦iDEEõJ'ˆ%‚ ÷…R½-Ì@/±[­Ö*§³³€åË—÷2¾4mð BfYoÝýÃt]ï7¥TÀ0ÿt‚XÂè¾|…D]e‰PÚ”•‘ê¿dwõêUÑ4èèh¾þúk#îæÍ›tuu h„{öéÚµk†ÁÔØØH{{;ãÇï%˸qãhnn&//oÈŒAA ,!d‘%BéeiǨ­tˆ ÔÖÖVvíÚÅŒ3¨««ãðáÃÍáÇIOOÇår±wï^,‹¯šú˜ÙÑ4;vPRR‚¦ilß¾ŒŒŒ€Ë‰ ,`ÇŽDDD0iÒ$º»»©­­¥½½9sæ Ü]ê!³Nb` ‚ ˆŸŸOww7o¿ý6š¦QTTÄŒ3(..fË–-üþ÷¿gܸq,^¼˜ºº:Ÿô}ÍY­Vž|òI6nÜÈ×_m|¦¡/ ±Z­ûì3>ûì³€q'N䥗^%‰%‚ £ƒuëÖ K¾ Y~3gÎäßøFÀ¸ýD„|ŒØ;X‚ ‚ cyKAA ,AA„Ñ͈½ƒ¥—¾åöÑÑ5Ðè¸4oqûæ8]õ„ŠwizÀ<||eN£áî4¾uñÉKÝ¢ûæ­LòÊT~_u7êgª§ò«K ï±2ç§ûÖÍoÈõŽw™ê «žüÜ?‡×Qhšîù©»nüä])w¸7®·Œo¸á+Ož^90ä}ãÄkº‘ÎßWJ÷Ⱥ}o½äú’ p¬èÉSÓzOYƒŒwçç‘ pì–Ó{ä8™Y#^ïIc>6äu¬É8ÞTݧ:öKgŽ3¥Qfß/ÞøÛeÊÃ_ÖÕ#gŽ÷óæá“—á@óʺ\|eüâÑÝí+]—Ç鮞¿0syÒ¸tp¹|ã 9z§wõ—gçÍÃï4å£ã[¦9Îé—F÷ËÓ[§ þ€Ó/§©îNW?uD¼û€_ŒÉÏ÷2ƒ%"Ó^„ààĉüò—¿¼§4eeeìܹS”'÷ – ‚ =ö¹¹¹¢A ,!œŽ•^”Y,!˜q:½öìus #,Lnq‚Xênã–ZèG­RWc¶UrÍ픕•‘œœŒ¦iœ‚©…Ôª‚¨®ÂPJúØÜÂ(Çkð¼úê«,[¶ ¥K–,áþáX¹r%—.]bÏž=ýæÑÔÔÄÙ³gy饗x饗¨®®æÀ>2'Nœ@Ó4^{í5/^LEEÇŽ3â7oÞL]]/¾ø"ÿ÷Àþð\.àþb{uuµaŒ566ÒÕÕEcc£a`M˜0AŒ+1°AnFÒ‹‚0ò$&&ò­o} »ÝŽÝn§¨¨ˆ¬¬,l6ÙÙÙ,\¸S§N ˜ÏŠ+?~<™™™äççsñâEŸøøøxJJJ°Ûíäåå1{öl***¸yó&gÏžå;u”!Y IDATßù™™™¤¤¤°jÕ*¾þúkΜ9@VV–a`]¾|™ÔÔTŸ°êêj²²²¤CCY"†oÊCê*à (Z%׌` --ÍçøÂ… 8p€ÆÆF:::p¹\8Nºººúœ²Ùl>˱±±´µµùȤ§§÷:®¨¨@×u°X,L˜0ÁˆŽŽÆn·ÓÐÐ`X;wîäÎ;\¾|™¬¬,bcc©®®fúôéÔÔÔðÔSOI‡ŠlO×r¡¶NQAr3’7¸‡ÍP›ª•kF°`6šZZZxÿý÷™5kÏ<ó QQQ\¹r…­[·ât:û4°4ÍwÑF)…®~ (5ð’’BTTÕÕÕTWWóÌ3Ï˨­­Åår‘‘‘!*– ‚ Œ.jkkÑuââb#¬ªªjHò¾víšÏñÕ«WILLD)ERR.—‹«W¯FÒ;w¸yó&ãÇ7ÒdffræÌÈÌÌÄjµât:9räiiiòþUˆ0êÞÁR#˜ZH­zÕUè ]T+&ÈILLÄårqèÐ!š››©¬¬äèÑ£C’wkk+»ví¢±±‘/¾ø‚ÇSTT€ÝngòäÉ|øá‡\¹r…úúz>øàâââ˜2eŠ‘GVVUUU8ÂÃÃQJ1qâD¾øâ yÿ*„%Bapj•%Â7)d‰P-8Š‹‹9xð ûöícâĉ,Z´ˆM›6=pÞùùùtwwóöÛo£iEEE̘1È_±b;vìàü#N§“¬¬,^zé%ŸåǬ¬,t]';;Û'ììÙ³b`…Òµ¶´´tD®0²a0îEسO ìE¼{š÷”½e/BÙ‹P†Y"îA­²DÊs6²D(‚Ä–,Ž¡;²ÔU†´J1°Ažö¥A1°AAF!#ö+BUú yšöÇåqBÈvh¢BûJ ¹‚ §¶ ‚ ‚00#6ƒUZzÄÏÎS?SÈô—ö^óò—±ô!cñ“S~qš_\_y©u± ¢÷« K€6*,…Å‚á4Í×……áïþC³XzËÊO©ž0³¼y}åÖ¿¬÷x |üeÍ~_z¬Ì@eú8åBÃåùì‚Ëóóz“s:ûwÉö•_w÷ý—åý{°e™Ë¬ì`ÚfÎs üÅ{Òê.—ûK&çÀHþ~òr 2¿¡*ï~Ê1û÷S·þÒ(ùLƒ 3X‚ cyuaìÐÒÒBii)õõõÃVÆæÍ›ùÓŸþ$ÊFÇ – ‚ 7ñññ¼ùæ›DGG[‹/¾§M£1°AB¹UŽ”RÄÆÆk¢hA ,!˜/”c¶erKFd¡©© «ÕJjj*/¼ðV«•£GRQQAKK 6›Ù³g3kÖ,À½D¸~ýz^ýuwïÞeûöí\¸pÎÎNâãã™;w.†ìš5køüóÏ©­­%99™ï~÷»´··óÑGÑØØHff&«V­2fÅ6oÞL{{;Ï?ÿ¼Û@×u<ȱcÇhmm%66–™3g2wî\èb` Â(œU³6ˆWbìŒÌ(Ѓ¤¾_ý57näÙgŸeÊ”)tvvrùòet]çäÉ“”——³téRuuu|øá‡„‡‡“ŸŸïy8ëy:ûøãilläå—_&**Ц¦&º»»}Ê+//§¤¤„øøx6oÞÌÆ‰ˆˆ`ñâÅX­V6lØÀþýûYºtiÀúîÝ»—cÇŽQRRBff&mmm444ȉ%– Bˆó¢‚QÅíÛ·Ñu©S§@rr²a 3eÊl6 9rÄ0°ÌïGݺu ‡ÃAjjª!ïÏOÿüs–.]j”Ÿ@zzºt¤X‚0Jgd‰PËãY"ì‹””²³³ù·û7rrrÈÉÉaÚ´iX,šššØ²e [·n5ä].‘‘‘óš9s&6l ®®Žœœ¦L™bNæò¼ÄÄÄøtÞ°¶¶¶€ù766ât:ÉÎΖA-– ɬ‚, cØØ‘%¾Ñ4ï}ï{ÔÔÔpáÂ>ÌÇÌ /¼ÀòåË™0aB¯4ÈÍÍåg?û_}õ.\àwÞáñÇçÙgŸ ˜Ö»¼èÖׯÃÂä¶*xÆ‘¨@A222˜?>?üá±X,ÔÔÔGss3‰‰‰>μô§ü¦¿£££ÉÏÏgÕªU”””pôèÑ>eï»ÝNXX/^” qÄÔ‚gVA–…‡„"£ X–¯^½Ê¥K—ÈÉÉ!&&†«W¯rçÎÆÏüùóÙ±cLš4‰îînjkkioogΜ9îþ4Í6íß¿ŸÔÔÿ¿½{jêÌÿþ>ç$@,!(ðÖÚ›H‘V‹TZ×ÒŽNí´•vl·Ý™:;Û™íMç7ÓÙ™ýc™Ùi·¿Ý©Óqv{³Rm»V*bÕi]Eeu¹cñ.‚á|ÿ IHB‚"¹¼_Ó#çyrΓç<'ùœçœœ'ÉÉÉGSS’’’¦ö½‡ž©@žq¥P(ð裢ºº’$!==ÃÃøví~øa\ °ˆ‚ðK—)Œƒ^"ô.::ííí¨««Ãèè(4 6n܈œœ€R©ÄñãÇQ]] ¥R ½^‚‚§“³©³3I’pèÐ!ô÷÷C©T"##[¶lñ˜××2_ŠŠŠ I>ŒÁÁAÄÇÇ#//V¤}ŽÆyùtçX„‹cr,Â`‹Ð}Ì<ŽEȱ‰Å{°(tÎÂú!EzŸ¢bÛd«%b€Eáò¥ÇK„Ä .h¶ÉVKÄ‹ˆ( ì!"XDDDDAfÞ~Eh4Îç/*œo·¤p`¿w™æ Lþú€h¶MˆU@aˆ=XDDDD °ˆˆˆˆ‚Û<^"4Ú"< J(¡€JD! шF bƒhÄ@‰(Û4•¢éJ—t"$H¶ÿKÜþ`› ^ÿ³ç±B€F&³Ó4jKƒ €QqJŸLƒc.C 9æÓŸw%Í09åE “S €(ÛÞTÚæQ¶åѦ§|ö×ú80çÇ{‰²m™m.Ê€`u›lO¿&<¤¹§;åSšËäé©<îé2 OäqÛ| Í®,“Ëad‹mùˆm>ê”6À>OÎíïÙÓãÌ|=fMðQ¯â,Öåþ þ?>ÎKÙ£ Zb[³\›@œm®vk^îé*P:M §¦"äé“è”gZš­™ˆòT“ì:'œš–<¹«§üΓsSlMV/Ó„Ó|Âéoûdq›F>ŽÔÔT¬Y³QQQ¸téöîÝ ­V‹{;“…T·£Š°¨ìÅòdhh²,cÙ²eHHH$''O}‘)(//‡B¡@RRÖ­[‡êêjc||GEEEÒÒÒ‰‰‰èèèÀO?ý„ÌÌLÔ××#&&[¶l(NÞ9£ÕjëW*•°Z­P«ÕÓÊVPP€¥K—º,+,,tü;??ÍÍÍhlld€Å‹ˆˆæ+8¤éôz=²²²ðÁ ;;ÙÙÙX¾|9T*•#]áô°Û´´4X, `ttcccøøã]ÖiµZ‘’’¸zõ*233ÁU RSS]þž˜˜ÀÑ£GÑØØˆÁÁAX­VX­V(•JîHXDrÝÄêc¢(bûöíèììÄåË—Q__šš¼úê«3¾Öb±^xáÄÇÇ»~Ú‚2ÅmŒDà8?~'OžDYY’““¡T*qàÀÞÏ‹ˆB/†Muð¡oéééHOOÇÚµkQYY‰‹/˜ìwJ]]]ˆŠŠBBBbbb P(000€ÌÌLëÕëõhhhÀÄÄ„Ç^,I’÷{¹ì/azKëììÄÒ¥K7µË² “É„¤¤$îÀH;1`Q0ëêêÂÑ£GqåÊ àÂ… v-V«_}õ®_¿Ž¦¦&ÔÖÖ"??ÂÂB8pgΜAoo/~þùgœËúòË/s§F^"$"""b€ED.Ø{6ÕÁÞ+¢ðÁK„D¡Ž—Y¬¼¼œ•@A‰=XDDA‚!,""""òvÂd4yÙŸˆˆˆèbÑ6o7¹¿c´??ÄÖ&8Íí“ûò™Ò}噫uº¿Çäe;ð²N—e3½ÞË2m™?e °Ž<¾o?ê}Z=ù(»×z ¤^gz·S¯3¤ßN½ÞÎ:.û mo6¯q¯_çýùÝ_LÝ”ä³fÈïÏ>÷Xvx>~ü.{ Ç´?Ÿ^êÕÓûöVGþÔ«¿mbÖõê­,À»ïù­LìÁ" n¼]˜UCä›ÑhtŒgH6=XDDDóé­·Þ‚J¥bEМ`…)þvƒUCä™}°æ¸¸8H’Ä ¡9f=XÉkΪ•e%bÛ›G8räz{{¡T*‘’’‚çŸß|ó Ìf3 êëëƒ=—••9‚§]»v!99¢(âìÙ³Ðëõ¨¨¨€ÑhĶmÛ°téRô÷÷£²²Ï=÷Nž<‰îînhµZlÚ´ éééŽrœ:u GŽÁÈÈrss‘žžŽ#GŽàí·ßæN¢p°\ÍYµ ¡VVÛ xœ„‹ÁÁAìÙ³%%%Xºt), ÚÛÛ!Ë“ÇxKK  ^yåô÷÷£ªª ±±±(..v¬£¡¡yyyرc‡ÏmÕÔÔ ¤¤Z­‡ž={ðæ›oBEtttà믿FII /^Œ––>|˜;ˆ<â%B ão;bÕP8‚,ËX¶l4 ’““±jÕ*DEEMö((//GRRrss±nÝ:œÃ©S§022â’®PL]IKKƒÅbÁÀÀ€cYjjªßÛ²‹‹‹ƒ,˸uëàÆ¸çž{\ò»ÿMdÇK„ä_µò! Ÿã$ÔzDÛ·oGgg'._¾ŒúúzÔÔÔàÕW_õ{J¥Òïm9ar‡Ú/EÔnYDD ÒÓÓQTT„×_’$9žauõêUŒ;òuuu!** {¾#ä àÊ•+.˺»»¹c(,ž>F|µ ÎÝ ä«†ÇIˆéêêÂÑ£GqåÊ àÂ… FRR€ÉÇ.|õÕW¸~ý:šššP[[‹üüüÀzªòóóqéÒ%œ8q&“ ?ýôš››g Ì(2ñ!ùW­¼DÈ@€Âç8 1ÑÑÑhooG]]FGG¡Ñh°qãFäääàüùóÈÊÊ‚N§ÃG}äxLCQQÑ̇‚[`ä)Pr^–‘‘M›6¡¶¶555ÈÉÉAAA~üñGî$šÞvŒFã¼| q,Bp,Â9‹Ðý©c:±Ç±9aˆEXUU³ÙŒmÛ¶ÍË—è¾}û`2™ðÊ+¯0¢ ¼DHáU­¼Dè_O ñã‡fåßÿþ7zzzÐÛÛ‹“'O¢¡¡>ø +†¦á%Bò¯Z–•ˆmº»»qüøqX,$&&âÉ'ŸÄC=ÄŠ¡p°ˆœ»p{­¢0Q^^~W··uëVV:ù…i """ºÓç²óu“;Q¸bÑ6o÷`e×§á:~©+Ø~á+Lýí1ÓÓ_#¸§yY—·üÎË×oÄ’s~x(›Ïí9矩¬êAðR6e\óû,‹Ÿõîõ}Ïð^üÉ?SÙÊïç>öX¯´‰™êQ˜EÀ í×ß}>›ô™Þ fylÚ^-ÛŒm €ã> ö?Ë.ÌM{õ縟±½ÂËçzŸMÙfzoï¾›ÊoebݦºÑX`Y‰~‡Uñ 1À _!tç›Ì²’̲†Sñ 1ÀâÙ`0•e%ö`…Uñ ‰‰‰Ç@¤ÐÄç`ÍçÙ`ˆ|`Érè.¡TÖ ø°Ù°ª¢Ûzƒóô&qäÈôööB©T"%%Û¶mÃ?þñ ”––:ò~òÉ'ˆ‰‰q<#«²²?ü0L&.\¸•J…'Ÿ|iiiØ·oZ[[‘˜˜ˆ§Ÿ~©©“÷9sÀ³Ï>‹ï¾û7oÞDnn.žyæ466¢¶¶f³<ðJKKcŽãСC8þ<Ìf3ôz=Ö¯_… º¬÷™gžÁ÷ߓɄ7ß|†ß‹ °ˆˆˆîžÁÁAìÙ³%%%Xºt), ÚÛÛZG]]žxâ <þøã8qâ¾üòKdddࡇBII ª««±wï^üæ7¿q¼fll 'OžÄÖ­[1::ŠO?ýŸ|ò T*^xáôõõáÓO?EFFî½÷^À·ß~‹7n`ëÖ­ˆÇ… °{÷nüú׿†V«u¬÷øñãØ¼y3bcc¡V«¹“Ã/QP‚,ËX¶l4 ’““±jÕ*DEEù½ŽÜÜ\¬\¹Z­?þ8FGGqÏ=÷`ùòåÐétxì±ÇpãÆ 9^311M›6Á`0 33Ë—/Ggg'ž~úi$%%añâÅÈÊÊBkk+ ¿¿gΜÁÖ­[‘‘‘ÄÄD"##§OŸvYïSO=…ôôtèt:(•Jîä0Ä,"" jz½YYYøàƒììl,_¾*•* uØÅÅÅ’““ËÔj5dYÆ­[·éJ¥‰‰‰.y4K@¤V«qëÖ-Àµk×011?ÿùÏ.Û¶Z­ˆuü-I’KyˆÑ]'Š"¶oߎÎÎN\¾|õõõ¨©©Á«¯¾ê¸÷É= ñ´_Ëìëq¾áÜý5‚ x\fÅb(Šxýõ×§•˹·M¡àWoD´[VÑ<áãˆí6 ééé(**Â믿I’pñâEÄÆÆbppБgbb×®]›—ò¥¤¤8zÁ´Z­Ëdï£ÈÁ0z¾ðÑ_V¶×ð*+Ó0wºººÐÚÚŠììl¨ÕjtuuaxxIIIP*•8xð ššš ÕjqâÄ ˜Íæy)§N§ÃŠ+°wï^”””À`0àÖ­[hmm…Á`@nn.?K`Ñ]9 ä£"º¬l¯áUÖˆyLÃ<ˆŽŽF{{;êêê0:: Fƒ7"''V«W¯^EUUDQDAA²²²ü8üZ¨òòrüðÃ8xð nÞ¼‰ØØX¤¥¥aÉ’%ü‰´óR£Ñ8/¾?¡Ÿã÷ÃX„·3¶àÝ‹ÐW=r,ÂÛïÏ×q‡ ‹ÐßãðE8ãgV¨EèVNŽEHäïÁšÏ³ÁP)*Ë:G§7!´Y•VUt[o=ÄD °ˆˆˆˆ`1À"""""XDDDDslÞÓ`føµˆì6' slòDDáƒ=XDDDDwؼõ`íߟâ8[—EÈ‚Y]'û2ÛƒW¦¥‹¢×哯ó”W°-÷¼=GY²$ùX·=¯[$·òÛ#ZaÇæV§iÇaÌ–6fûÛ=¯íߎ´±©eÓÖå6Ÿ–w¬@ $¢ @tš+Á‘& ·<’=Ó:$ûkìù0ù oû2…Ó¿íéŽõ nëÁÔkÜËæœÏþ·§4É[^·¹·zœæîëñ¹=§ºÝÞ«‚lûå»,C–'ÇB“e@ž!OȘp™Ãõo{þ‰É¹{Çzló Ùmîé²c}·+;oGv-ã´²LåÊï£lN“}=ö:ñT{¾‰ ×õÀ%ÿ„kÙõêZçI!ˆ"DÁ6wšQ„ ˆDÁ6ŸZ.ŠâÔëmërIs^‡m=ί—9¯Ï½\žÒmÿž¶=IòR¶éÛó·ì¢$9m[ôZ6x(›¯m¾ûî»Aý…YUU³ÙŒmÛ¶1z à °\…êƒUø@ºæ#DØõ@¼J³µk×. ”––Î[ÊÊÊ\ƒ& ‹ˆˆhnY­VH’ä÷rwÑÑѬD µ+TGñ ûÑÇhN›O¤µžùÓìTUU¡­­Í1¡ عs'FGGQ]]ŽŽ(•Jdgg£´´±±±&{½’““!Š"Ξ= ½^ŠŠ F<õÔShnnFKK }ôQ¬]»û÷ïGkk+†††€U«V¡  À¥ΗwíÚ½^…Bÿüç?$ yyy(**âN#ö`‘‚dôd!„ÊðcìAA}`ͯÒÒR˜L&$''cݺuQñ·¿ý +W®DYYÆÆÆP]]Ï?ÿŽ×644 //;vìpYç‘#G°~ýz”––BEȲŒ„„<÷ÜsP©TèììÄþýû{ï½×kÙ°zõj¼öÚkèììDUU222°hÑ"¶-X<Â#¯Üv<ÈA¸È!TV¿W9’‚+F’Áy|ÿGYLL $I‚R©D\\à‡~@JJ Š‹‹ù6oÞŒ?ýéO0™LÐét­V‹ 6L[çŠ+ðàƒº,sîyÒh4èììDcc£ÏK¯×ãñÇwl«¾¾--- °ˆ—#³Ü4+/ÍJOOZ[[ñÞ{ï¹·èëësX©©žŸ¹èiy}}=NŸ>ŒÃjµÂ`0ø,‡^¯wù;..·nÝâ"^"$:x‰pÎßc ê+øX,,Y²6l˜ö˾øøxÇ¿•J¥Ç×»/?wî<ˆÒÒR¤¥¥!** ÇGww·Ïrˆ¢8-Àã/ )ˆ,^" îŽ^"œ³À•—)¬8d$Ir \RRRpáÂ$$$L rf£³³ÈËËs,ëëëcû Yã“܉ˆ(èi4tuu¡¿¿ÃÃÃÈÏÏÇÈȾøâ tww£··ÍÍͨªªšU’N§Ã•+WÐÜÜ “É„ššš{¯ˆ|á=XYî@;x‰pnš­ýDLÇ{¯‚óøb¢ªª ï¿ÿ>ÆÇDZsçNìØ±ÕÕÕØ½{7ÆÇÇ¡Ñh““a†ÏOé+W®DOO¾øâ ‚€ûî»ùùù¸téÛÍîÐ2óò©ç:TŽˆB•c/7‡ÊáP9³*G° “1CåÀeÛ*‡Cå„ÚP9Dà%B"""¢ð °BõÒA„\òà%Â9j>ro,ðap߬¢0°($PøzÅŸG3î ¶?"X<…â©Ý©æií‡Ç 1À""""¢Yš·Ç4üò—?‡n­Mئ"ˆ²MáÇu—DÔÓ;ç Á]<É6ÝasÖj\gH~xÑ,°‹ˆˆˆè›·¬cïžw=-ìsmsÛ3Tìÿàú·{ºÇ ®y¯épN¦ž_%x~ì–ß5¯ýõS²sºÛú§Ö·×zH‡Ûº}¥Ãžî>÷±>Øò¹¥»¬^ò:­Ûg~÷²yJ÷»l˜žîµl>ÊîWÙä™Ëî£ld¯é²[¦•]vùÛ±.8­Ûí5®ëq8•ÅV2DaÂ6w-‡ó:“0áHsIwZ§8ò Ž×OmCrúÛµlÓ·7™·²Û'ûß°=ÓÍå#ÈéYvSφ›Llõ*ØÒ&—L¦ÛÒ0ùùüÿþÿ‹üV&ö`Ñí ¡~CéflÞGMßn¤Õkmm->üðÃóUVV¢®®Îg£Ñˆ‹/úûûa4ÑÓÓÃãŠB§‹Bh˜P@9BF/"¶»þvƒ¸^ ñÈ#̘ïW¿ú”J¥ßëMHHÀ[o½…ØØXWÄ‹çsQTö`­ˆ»A\¯QQ¾¼cµZ!IRÀ’ ˆ‹‹ã1E °xÞ9WEeVX}a <´øvC«^O:…ÚÚZüîw¿sYþÏþ±±±HHHÀÅ‹ñÆoªªª`6›‘ššŠü …;wîDee% PPP0™Løê«¯påÊ$&&¢´´Ôeýýýý¨¬¬Äo¼ƒÁ€‘‘|ûí·¸|ù2, °fÍ<øàƒüJ#XDDZ–/_Žýë_hmmEVV`ddÍÍÍxñÅÑÞÞ>í5---ˆŽŽÆöíÛ½œ7ÊøôÓO‡×^{ f³˜ú1”ý|Äéïššܸq/½ôT*z{{1>>ÎDñ&w"" j*• 9998wîœcYcc#Ôjµ#àr…Í›7#)) IIIÓÒ/_¾ “É„gŸ}z½™™™xâ‰' » ·åü÷Í›7a0’’FƒE‹añâÅÜAÄ‹ˆˆBÓŠ+ðßÿþV«pîÜ9Üwß}^óëõzH’÷'ÒÞ¸q¿øÅ/\î±JKKóY†¼¼<œ?~ø!ª««ÑÙÙÉC °ˆˆ(t-Y²²,£©© èèèÀý÷ßï5 ¿ôWnn.~ûÛßbõêÕÄÇŒƒrç,¢ "GØv‰nƒB¡À²eËpöìYœ? ,€Á`˜õú’’’póæM 9–uuuù¼ bccñÀàÙgŸÅÆqêÔ)îb€\ø˜†H¯Vb©·õzÿý÷ãÒ¥K8}ú4V¬Xq[ëZ´h´Z-öîÝ‹žž´··£¦¦fúùˆÓ=X‡ÆÅ‹ÑÛÛ‹k×®¡©©Éãý]DE8ø˜†H¯Vb©·õš•••J“ÉtÛ– ضmöíÛ‡¿ÿýïÐh4(++ÃîÝ»§å³“$ ‡B?”J%222°eËs乯y¹`À±ícû…ÀX„‚±Û0ŽE¹c pë/üÇ"t”c®Ç"´×+8!‘?x‰p^Ï;C¥¨2«uNNo"l»l¡ývyï,""""XDDDD °ˆˆˆˆˆÑ]2oixìûXû“¿Íc5„Ê®w×tî?U$oÍÇj›ˆ(2°‹ˆˆˆè›·,£ñ×.÷gõ8žÑã-ÓÓ_#¸§yY—·üÎË×ïô!¯Ï!òg{Óò{+«‡z¼”ÍcÙe×ü^žÇP½{}ß3Ô»?ùg*[@ùýÜÇë5€6ámŸÂßz l¶×™ÚÄLÇ–§íb–ÇÖ\—mÆ6Àqhûóç½ÍXV9€z ð¸¿í²ÍPV܉Ï[Àø.{B‰=XtÇñƒ…ØÔˆ;çv9søÃX¹=XDDDwÚ}÷݇ÜÜÜ€^³k×. ”––²‰VøáÝÓt›;Jø9®_j Šùùj³Z­$‰;Â1ÀÜ> ~`Ý©jõYÏÁ\ÖPl·,sH–UˆÄ†»Óšššðå—_â÷¿ÿ=A@OO>üðC<öØcX¿~=`ß¾}Ç¢E‹pàÀ¼ýöÛ€ÚÚZ\¼x«W¯ÆáÇ122‚ÜÜ\lÞ¼QQQ¨ªªB[[ÚÛÛQWWA°sçNh4\½zÕÕÕèèè€R©Dvv6JKK `²ç+99¢(âìÙ³Ðëõ¨¨¨àw…c€%‡ðÙ`aMëñƒ» ¡ÞnC!Y¿sÒöB)p½{åÌÈÈ€ÅbÁÏ?ÿŒÔÔT´µµA­V£­­Í‘§­­ =ö˜Ç×÷ööâÿû^xጌŒà³Ï>ñcÇP\\ŒÒÒR˜L&$''£¸¸²,C­VÃl6ãã?ÆÊ•+QVV†±±1TWWãóÏ?w ¢——‡;v0¢ ¼É=R¿d‰M¸sBDLL ôz½# jkkCAAzzz`±XpóæMôõõaáÂ…^×Q^^ޤ¤$dddà@KK‹cÝ’$A©TB­V#..‚  ¾¾)))(..†N§ƒÁ`ÀæÍ›ÑÚÚ “ÉäX¯V«Å†  Óé Ó鸳ȗi=¼D8·í–eɲòáœZ¸p!ÚÚÚPXXˆŽŽ¬_¿èèèÀÈÈâãã¡ÕjÑÑÑ1íµQQQŽ¿ãââpëÖ-ŸÛëééAkk+Þ{ï=×w-èëësS©©©üŽ H°x‰pΪ•—çׂ—ƒ¸íñ¡¯ëôéÓèéé$IX°`#èAff¦×׊¢8-H’eßå·X,X²d 6lØ0-o||¼ãßJ¥’‘EB€EDDá(##£££8qâ„#˜Z¸p!Ž;³ÙŒÕ«WÏzÝ’$M ¢RRRpáÂ$$$L Јüf­F˜‹Sõ°<¼­j æz¡݆BSX¿s²™õê…J¥‚^¯Ç¹sç÷Zeffâ矆ÉdòyÿÕL4 ºººÐßßááa@~~>FFFðÅ_ »»½½½hnnFUUÕŒ½_Da`…ú%ÂP‰ýä*+ƒkrm†'…¾,\¸²,;‚)•J…¤¤$ÄÅÅÝÖ æ………Eï¿ÿ>þøÇ?¢¿¿ñññرcdYÆîÝ»ñ—¿üß}÷T*’#?Žf£Ñ8/Ÿ”‹ÐC~È‹p¦²q,»0&žü‹pŽÇ"ôö9@½s,B"ö`Íý©9Ïç¦Çƒ—CºÐ¼DÈžÁÈ=؈`Ý™'^'Ÿ›/d^"d¡y‰0<Úß0¬°Ç³AbS#î"XDDDDäùti¾nr'""" WìÁ""""b€EDDDÄ‹ˆˆˆˆ1À""""b€EDDDÄ‹ˆˆˆˆ`1À""""b€EDDDD °ˆˆˆˆ`1À""""b€EDDDD °ˆˆˆˆ`1À"""""XDDDD °ˆˆˆˆ`,""""XDDDD °ˆˆˆˆˆ,""""XDDDD °ˆˆˆˆˆ,""""XDDDDÄ‹ˆˆˆˆ,""""b€EDDDÄ‹ˆˆˆˆ1À""""b€EDDDÄ‹ˆˆˆˆ1À""""b€EDDDÄ‹ˆˆˆˆðØvÃücu€IEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/allsky.png000066400000000000000000004077001510711153200247040ustar00rootroot00000000000000‰PNG  IHDRèô®(-sBIT|dˆ pHYsaa¨?§i IDATxœì½y¼-WUïû›Uµö>}NN:“Є9 M$4‰ƒåù|á^i‚O½ ú» ˆ> ýózlˆ4ñ7ø½"B]€é!ä&á@ZrÎ>ÍÞ{UÕ¼Ì5Ƙ5«V­}ö9ÙIÆ÷óÙg¯½jÖ¬YU£êÌßcŽé¼÷†a†a†a†aÜ©dwv Ã0 Ã0 Ã0 Ã0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†aè†a†a†a†±0n†a†a†a€âÎn€a†aŒ÷+++XZZÂÒÒöïß?øyÿþý(Ëu]'ªªêÝ–eYò'ÏóÞmEQ`Û¶mؾ}{û{èóââ"œswöe5 Ã0Œ»&Ð Ã0 cðÞcß¾}سgn½õÖQ{ßÞ}Ø»w/––Â÷ìǃQUåొ¬À$[D‘MPø øð‰þ…o„qóÛµ·­H;;¾vü}óMøíèsÊ•˜ÖSLë”õp[ó¼ÀÖ-[°u+‰öm8æ˜c°ã˜£þñÇSN9;vì0¡o†aÜíqÞ{?»˜a†aÜ3‘Âû»ßý.öìÙ£>çÛßÁ·¿ýÜtÓ÷°¼²œ¬c’M0ÉQ¸ r_ «›Ÿ£À9&ÍïbÆßá»lf¨9T‡]GJ”˜¢B‰ S”Íü^ÿž¢r|^¡r´O‰iµ‚i=MgÓâ&œtÒà^÷:§ÞëTœrÊ)8ùä“qÊ)§¨Ï&ä Ã0Œ»2&Ð Ã0Œ{$‡#¼óMØ”mŤZĤބEli~6c‚m˜`³ÙnÍ‚úÈ Íi1|gâ#Á¿Še¬àVp°ù9„i¶‚i¾Œåú V*}oLȆaweL †awKêºÆ·¾õ-|ýë_Çu×]‡ë¯¿{öìÁ·¿ý|§Gx/´Â{3Šz3°‹ØýÞ‚“G÷IiíP÷”\›PôM­k%Çêš÷ G^{âpö•”˜b‡°‚C­€'1?Í–û…ü¦Í8éÄ“”?ýôÓ±{÷nœqƸÏ}îƒ,³\º†aÆÑźa†q—fïÞ½¸îºëÔÏW¾|-®¿þaeu»[‹(ª-ðÞŠElÁ¶ˆÏ[‘cŸÒwGW MÆ úT9‡ Ó¤ˆùeLóC8PîEåCØÿâÂ"îúýñà|0vïÞ­~Ž9æ˜u=7Ã0 à L †ažétŠn¸A‰ð¯^û5\wÝu¸õ¶[Úr[&;°©:‹õNlÆNlÁNlƱ؄í‡fp¨#ñç{Dãl¶l‹¬÷Èþ ÎAï;¿þòwuÄ^À^,gK8”ïÃÁéR[îøãŽÇ™gž‰3t¦î÷»ßý0™ÌŠ®0 Ã0Œ~L †aï=n¹å%¿þõ¯ã+_¾7~óÆ6³y‘-`k¾ “élƱ،c±ÇbŽE…™ÇÉPãpĤG– `ÏÁÙÌ×+|›ë;ü6§RIâŽlÁáÖíz7Ž|ÔC‰)bo+Üb/VŠýØ_ß²Il—çN»ïixˆðºŸqÆؽ{7N8á›ón†aÌĺa†qÔYZZÂg?ûY\sÍ5øÒ—¾„k¿òU|ýë×aßÒ¾¦„ÃÖÉN,–;±èwb3v5b|&ØD"íp’uÅëች¸¾ ¥ïóPGç™­³],îC¬u0¤;âëÜëèž8T3¢†p¨DU,áîhü8äöa¹XÂþé^ÐùïØ¾gœ±~ȃpÖYgáœsÎÁÙgŸíÛ·¯ù¼ Ã0Œ»&Ð Ã0Œ#ÊÊÊ ¾ð…/àšk®Á5×\ƒO~âjüû7®ƒ÷>xó1)ƒß„]ÍïQ"6µ{µX^[RµøØkó„’ðÏÖ(عžyÃÏûà:Üš¯±Sbzüà†ûxôcÝzÚO?ýt›Ón†q7ĺa†1Š={ö´žñO_ýi|úÓ×`ï¾;[&Çaqz2¶àTlÁ©ØŠ@¡BÔÇIH)nÂìÞùÙX¡JÞÏy¼×ìñÆè}Ö2Ÿ÷|ÿ=¯÷üôõÄ«kǵÇðò4ûHÀ‡}ë}Ö>Xà›¨Žçm¯ÛšK¬`?nÁ~Ü„ýøMnÁé÷ÇìØ‰G=ê<êÑjEû)§œ2¢~Ã0 c#cÝ0 Ãè°¼¼ŒO|âøä'?Ù†ªßtó÷›ŠX¬NÅfj#Èï… 6w„Œœ‹›"C¥Äk²EoùØ‹=â¼_Z\9Ôs…½sùñëmÏ7—|^±žNŒvWñ˜Ý$wëKÖó„˜Ï;PЗý>½k[Cå00¤†Û“º×«8„¥F°t7áP~•a ¸“Nü64þ±},Î=÷\lÚ´ià Ã0Œ† tÃ0 u]ãóŸÿ<>øÁâ_ÿõøøÇ>†•Õ,䛱ş‚ÅúlÂiØŒ{a‚càQ‘À}Â<]6Kx/ýÜYÏ»u÷‹á´§9]>-ðûÊŽ¯7ÅÐ<øìOq$…yÈ >Nd‡¤oºlß’uÞwMŸÇs Ìæp—Î#(‹ü~|/xÙ³›pÀÝ„ÕêñÃ{.¼ðIx⟈‡?üáȲ#9hb†a.&Ð Ã0î¡Üpà øà?Dùû?€;ö~E¶€m8 Ûêûa;NÇ"N†ÇÂH¯cWš¤ÄJÝ$…Óô{…u{=(xuâ·1eûËÉïxŽx:ô¼ÛƱ!ê!eÙ˜²ý)±‹°ñ^wJÞv8Ì3ß|L]^å.>fÜþÔ`QêS6 Ë‘µŒ¥oÀ%uo8~¸lŸ]¥ê<„›°ßľì[؇o£¬W±ó˜cqá±þÄ'>÷»ßýFŸa†qt0n†qá¶ÛnÕW^‰~ðƒø—÷½ßüÖppØ^Ü[Ë È·â^Èï`õa46Ö¬÷¸Æð;-°ãíi­Åúp9:‹ª‘d}žs¯>§E¯.×GœE¾¿ 3Oþú¢ïï|™Ë5cÂÌIÇ¿ÓåfxäÍUNú¶VA<¾Üp$„LŠ8̘åßx@©F…ý؃;ðMì/þûÊïÀÃã¾÷¹~üÉâ‰O|"žð„'`×®]£Žn†a9L †aÜMY^^ÆUW]Õ ò/|ñóðÞcÛäDlžž†8Ûqrt稖X!ÂÆ ­Ãt\“ÇP’6'Ê gD§z†¼ö´^ùêLñ,™>·¨ž¾¿Óm[ŸÿºãùÐk¯%Þ7]×,Ï:‹÷aígíP.Lþ&Û9dŸ¼›‡GÖ±0ßrq³…: (™.Z‰eì÷°7âÀä?°z œsxøÃûñ ØÏ;ï<›¿n†q'`Ý0 ãnBß<òMÅvl-OÃ/ù;ë)±Il§˜%¸Ç­Ù°Ï³OSÎÑæìë}eÈ·_õÎ9§cõÏ1fO}6C@~’·¡,ïò»¾kÏe†®Û<CÌJœÐÓé|7¢Ìp{ÆzäƒX.3.oÀ±>ÿJó²‚}Ø‹±ßÄþâ[X.—lþºaÆ„ tÃ0Œ»0}óÈ·ã¾ØV/ùfœéÇíz·Ílcù˜õ¨¾äl1ÙÙC†øYeÊ^±¡îÝ_.ƒÖ‚_7u®_»x#/ó<‰ËR-8\‘>,ÐÊÌý´ÿ,Ѳ¹÷—™=Hàš„ré½É{Þ¿÷ð Ìl±>|¯çÎG²?„›±7bv#–ð-›¿n†q1n†qÂ{û·Ãå—_Žÿñw—ㆯ‡ƒÃŽâÔÆK~5\í;*$=µßp8ñ˜0â1ó~Ytw—kC#4‡„yŽig;sX{µmM÷ø¾÷øÔ¶¡Äo}íë-_kØúázW‡²‘‡?5a–0Ne{g{ëÃÅ@ûªöÜâdpaþ6»?J$¶ÛY×{Ì}¶‰Yõ¯7}Ì"Ì_ÿ6öáF(¾…}åÀÃã~§ŽŸyúE¸è¢‹ðÈG>ÎÉuî Ã0î9˜@7 ÃØàÔu«¯¾—_~9ÞõοÃwö|‹Å6SÞ;qìÀ}P`ÓL/¢dx.p¼-N6”![ç3ï«›¾çðs¯¾—¢CÆõ¬ï ߦ½ÞI×xÁSbˆµÝ<€Áë%Ÿš _‰¸]èÙwˆnVùÃgöàJ?Z|ë,öñúäq{Ãv§¶Ñù…ä„©íáw…Io»}3Ó'>ütn·<6yйݜPgsgÛÍ›¤}< €¦œWJ> àéCÓ.ú$®Ç ŒNRÐ3F){«‘cŠ)–p#îÀ¿c©øw¬”ûq¯Sï§?ãgpÑEáÑ~´…†a&Ð Ã06 UU᪫®j=å߻黨\lÇŽòþØ…aî ‰céÓÂy¼gk–pçïR¢½_j7ì•v¢Ý´æƒëcò~}§_”sâ·¡ í³÷<‚\ŠøÃû¯–D™¾®u¯xNþ–nøŒÜ)Ru’Çv8‰ÝÑ Â$ynqx¹Þ§÷Ä#C‰Iö¾‘Ç\Ghz8”XLÐ0‡< ts„d†Ý6„ óIòš‡6èvQ»kI/7Ùqÿ`KzÙ?Êiž^ÑïQö˜§Û˜#Ã4y(êd=SúÃ1C„‡Ç|»ë×á`ù}»s.ú™ŸÆE]„ .¸“ɤ·Ã0Œ{2&Ð Ã0Ž2Óéÿú¯ÿŠË/¿ÿ÷ÿ?öíÛ‹­Å.ì,ïãq¶â$ Í\=Ÿàî˺¡A}˜Yƒ‡>M¤Eñ°ˆv Qäé˜bÈ9¯p] iAFÞëÔñÉ}øÇ÷ÈQb23#~LÊŽ<òŽçœË¯iêšõÍ%ç°£©Ev÷uÇ; Á˜y3ÔSä@Ê–|3øŸÿÐ R˜ª‘¶åS¤ì‚öK‘²¥ùç«—Ÿ'Ï•¯Pà ¾ƒ;p-–Нâ`y+vìØ‰Ÿú©ŸÄE]„ /¼Ðĺa†ÀºaÆQÀ{Ï}îsxë[ߊ·¿íøþ·c[qúÐ´Š˜àý.ѽoñ’~¼-Ç´9FœI¾Ï“îοà{ž)ÙÞqv<¿@/Úšp4Cø¾k±¿¸Ê›±ëØãðìç< _|1þð‡[6xÃ0îñ˜@7 Ã8‚|÷»ßÅe—]†·üõ¥øê׮Ŧb;v•»q‚Í Q®áy¯®Ç1•..¿ö¶§pZP %rK{‡æõvçr»!ÓW^¶xö÷á›øzõ•§mó–¿×ëÞ÷ tCÕå÷±—sλÓ}S^Îaç%ݸ¼þ.Y…IB€á—g[ì–ïË8?æÚº6Ä=Ò$¼czüLš8Ußñ÷Us²¹§ †awoL †a¬3‡Â{ÞóüÍ¥ƒ|àp.ñþ8Þ?;qâìëL:{*û¶ìä˹¦G*aT_8pº”8—uÄIÍâóJ…ê¦=“ýeSB=U·¼n.*ŸЉԺu¬?±êk·[ÛQ_y ×â3S÷)krÈðrÛA*/6{jƒðå9ßq6ÙVR ĉÙô9óè,ä}[.¶Ñîñ¸ŽT=µ¶Ûni£qâ8}.qy“PΉ﹬¬‡Êê xÎz|L VTY*ŸJŠ˜cš´£þ§®±àÖÏcêÝž¦ QÙûð Üá>‡%w-¼¯pá…ââç^Œ§=íiؼysgÃ0Œ»+&Ð Ã0Öï=®ºê*¼õ­oÅ;ÿ¿waÿ%ìÌï]Õƒpv£À¦Ä^iA¾ã}–‚‚æ,{P7Z´nôyðÉœüŠÃZu§\fÈ¦Ž¾î”sy-îêFTIïµ9íý–áþ2ãµ¼Nâ,Ø4hQ5’BŠž2Qv|½ýžüZÞÉeÐ\Ô† ñýî+KµwÛP#¶#Õ;TvÚØ%…_;xÍ…xCSvQ´›î7…0ǶAÂ_ÛQ¨·êÌQg;¢²¾­Mgg¹¼'tãÁ#ª-5p”þŽï ÝyûeÒƒI!dÒÊIÏy¼œ P´Â›Ë†,ðe§,‰t>²C†y3•Cžs.¦w°ÝWb`%¶ÏøÌÆ'¢ì³ ²9´ÐƒBâ|Kùç°T]mÛvà?ÿçgà9ÏyÎ;ï< 7 ãn tÃ0ŒÃàŽ;îÀÛßþvü÷?ý3\÷õ¯aKq,v•gâx<›p<í‰MÏ+wHeDç²r¾kÚ“«=|¼Ì”¬[ +j3Íñ•"•q'ž–¼’uJÏ«ìð§Ž/½¥…H’Õ_Ö5É´ôõ’D)²2%Nò¦Meçøáßi¢¬®—ö˥﷾'k)+aO,ßnïËB—ç-ËûGÞÌÙíÙÖã²áÞfíuð¢\Þι–ÞT핦vd ;’ǧLëé²:ä:íÍåuÐåyÖâÞrÙô3·)Ô‘G6Ķªm#50Dek%œ©MV;õf¨ÚÐv]vEÕràsÈ<_?/®=>Ÿ¿,»*Ê’0—t}ô;cÒ ‰£0ä÷马àìŧ±¿ø –ËÛ°ûŒ3ñË¿ò<ç9ÏÁ1ÇÓ©Ë0 ãî€ tÃ0Œ5ð™Ï|þçŽË.û[¬®¬âX<'øG`NSÞëºí˜KaÐ.®…Aʃ§ë¬;et8x<§Tvø©DÝx"cG{èÚ U®Â52Èy·´¶uh« ñ`eàÖ^»PB{°ãvS)Ëð9ëˆo9¸ÑW7{:øñâ;òj.€í%^¶Í·×-åÑŒ£âëäA‰ÚØ«Ü /÷‰ßb¡+d Î/¦žò¢kÒ^ôn])aÞ}n»ËÜ¥ˆËÉ©Üê´ q<'’ÄQ‹ ¬v®»ô¶Ó}Ÿ`¹}IÈË6ÐZìñ3(m-Žb¡vË芬}èé6!ó¾ÎÞ_«éÁÆ»¶Ð½W©(‹e܆}ø(äŸÄjµ„ .x^ô¢ßÄ“ŸüdK*gÆ]è†a‚••\vÙexõ%¯Áu_ÿ¶ç÷ÆqÕ£±A.„-À"Rzµä<Ê€\^I oDuÕÐóŒY↿ÈÓÖïµÔ⃼޺ó¶è¬×:YVÙ´zAÕEa¯².š¿L×"„Äs¸+‰[ÊÒMâ‚@Ô¸³OUêLJo‹^^*L{9¥ð¨Ô>1ézÙS–òø¥DAìÝ õIQW5›¯;M’¦„‡ÃambÊÖÍIÃx0<´ä± s× cOž:ïy3¼ò´Ì (P[èœùš¼ör.•‘sX0Õ[4’cU´—ëÕâ£FÞ\‚ךæë§£(ºƒ1®µ =Уï]%„ß[™Ä/ë±»8“»oS¿ò@…¼÷±gV¶–Ûž¡Æ,qžö€kÆ–™%žÃyÎçó´¹ïy~èç•Ö7ª—Ä6{Ñ«ÖÆh )ˆo~Nh?:BÝWÔzóñsAÉçäû G -A¢xÜóJaùy;ÕžW^-ÂCÛ(GÅŒ¼éÃÃcÿ޽ÙXª?Ž? /üõ_ÅóŸÿ|ìÚµkÍõ†a L †qæúë¯ÇßøFüå_þ¦«%Ž©qqb#TØÃCé¡äíx*C‰¤(ÉÀÂŒÞTé’^ZªêN´B?Î/7Ú3Qõ—Mx;µ™’ÅQ×ɲ²|O…æ’׌E ='ê¥PZöª…Ð] Ÿ§LÖt-\“Ð*tåÀ)\©}h½h)(âzóÖË®c&DLø»„Sõr¦ìZ•¡{'ůè™¶mõí¿Â.CÄu¦sªSGc°oQÚÁváÛ£ÕíQH(•¢%2`*ü;e÷©,ó]åöq óxpaŒÇ<Õ–ø¹ê†Îëè—xŠÕ+M|ë9—HhçšËû¿ º6ÇBÞ7Ï! :Ñ´ƒ¬¯Û²k£^hð-ÎÊ®Ÿ9Áù,d„ =.œÊ §ÜhûL½G‡Þµ©U`ßÃø ögŸÂd¡À/þâ/à…/|!N?ýt†alDL †qäꫯÆk_ûZüý»ÿE¶;«Gãxœ‡ÛZÁøq¨}bu,NÐ#¨R‚›‘Œ)NYµÓuÇk•‡®þD•‘ »äüqU$èi ®xA³@(ZO»Èýa¾)¯cBˆã0€P(Ñ<ŽRÌHAMbÃÜYp¸oÞyíÅä¹ït ^œ®­C×#ݽwr ±>±ÛÕ Ä!б×VÛO×.´'SMÛ§¬WÞë´ý÷ Þ~AßEOñˆ'Îù¤½þÃsÌ»íì¶)¾—ú~ö½ ¸¶øo*#3ª“ݳH—S6è™bZÚ~ÖÚ~ˆzw€Š—„ëóúë+ÁçÍÏ¡„KEtßIdSa+]ƒ¾k8mÏ¡%–°Wâ@þ”õüÔOÿ^üâãQzÔèz Ã0Ž&РøGñ‘|¯xÅàÊ+?„ÍʼnØYžx$r‘è(T™œHŠíð·>éfݽÔó†»õSM$dMÕ@ùxî§nÀ!æRLÑä¼½+èuù dGºnfçæž©ã{‘ù=¤ô ´õ¥×MOE8Èõ§éJ° áÐ`Oûûv;Ÿ<)O'uö¥“ʇ>$‘^ò!‘®ï¥¶˜nÆk -н(¥çÓßÝ)]!¤íeƉñùöŸ%ÎÓƒhÝlä}õæ²ÎÔ6ªIšiÑûè5Þé8òoŠ"áò•Øî Ÿ*'ß©òz‰·éi lÇôLêç@Ûö®-‡2zy¹îûlˆØF»×n¨®+Ø«°¿¸‡Ê›pÁOÀË_þ2œþù#o†qd1nÆÝï=®¼òJ¼ìe/ÇÇ?þ1l-î…cË'a;Š ZØÅ;ÙIîzuRó®hÖÛØß—Oy&ãc¤2@C”“eš‹­:gNNyÅÂüs>rp¡Nš‹.[6ŦNy™-ÚCg‡×í‰ç^ëk¡DœôÌCYƒ¨çd[ô²Sì¡ç¹¦4ei×È×zùÚËûSz>ピP‰}ØÛZÒ ÇÖõÅâ|hIzé3µ­¿Mt?Ð~—öž÷ï?fy³!ºb»»}¨¼ODt…v_èýa.Åi×+/í¥û¼ÅKþë–ï×Ú\¹4Z€ëŒƒýO!mƒËýÉpüß o—ïÌZ}'íƒì?¶™¾ ²Ï~{‹÷‡¡R¤œhKøöÿ‡Êoáq;/ùËpÁXæwÃ0îTL †q·Å{+®¸¿ÿû/ç>õ l-NÃÎòBlǢÙ«¬Èº tE^×[{Àb†’‘ ëÏê­;ü¾“”,®-Rখ:‹NÕÌ-§9á2Õ#I¦xž* —W"ñ"—LòØæÌÍutÎtvt=ãLîÚ{ç[±/χ:â¹Hè&÷¡ºp¨ « 姈3äSÈ.Õ§£x)+9o—Z"…9‹Næ¦E•¶7týÉ»YïSÛõU‘BÜîÛtbº÷Dî;\÷Z˜GœÏòšwç¢ÇÏŠ~N‡’Êë+Ä6Άž"ƒÎZžºçñÀÒÐ=bAMß±×[GØÐÝd{Ë›Ülûa޹<žö²×êYŠ,Êš„rñ³JûeÑ`Bü®›ï:Ä혅~O˺=jÂç±/ÿ'¬nÄcs.^ùÊWàGôGM¨†q§`Ý0Œ»%ùÈGð{¿ûûøøUÖütì¬þlŃAÙã^Þ„‹¦u.©“Ý]JH†-‡ZªÈóˡ֩¹¨ÜYìvZÙS7«“Û q÷ªÍqw O§6éeÓj”@§¤o2‰{Ûƒ@ë¦s&j´™› ®‡l''–’ $Ž|³„%©Ó^ÇpÆÓvP¡h–QãºK„Œâ”™}Ò£„w¨%Ç9ʶ!ýÜ¡6â€=é²n^«=dÈž´Û Q·-9'Zʶnº§z@Az¸kù I c).g‰n]Wìѽó8zâ¼;€&˧殧m5žº/ÜBnK—¼ _§>gš+NÏÄÐÊ4C¿3ª¶­¡¬|ÿÛ,{Þu\G¤’¦¨U›åàjQ½±ÊÈšîÀ%·95¥EØÅuëv÷ {Cø"öåÿˆƒÕõøáó‡?ü£Wâñ|²¼aƑºaw+®ºê*üÞïý>>üá+±%¿vTOÅfœ×¥X ç­8Š…Rð÷‰çXHSÀ1Í楴HBx°ˆ×BRf6v­˜ëvÞý`Ù8ÙRÖ ;4ØLŠ“å¼fªÜu›ÉÏ-ÉãBw:*yÚI„²œ®j²±h;ÿžN]w.;i¯‘ qç5—3d@ñà„e , ûðówI È,ú¹á…=Q• ûœm[ 6¶%žV0< Ôò+íŒ÷Ó ¾Ž³Ä¹,׿kž™(.=ðEÌædoqÙ¸¼|v3x°š„gJxëðrèòÑ}¤{ž)¡=ÉÏnþ~–B˜{©êåü]ïr‘!¯>‹h-BÇä©8Ò~äô•Ô\|žÆ"Ÿ£>Ûã:} sÄ6Ìmæv8ñLñ±=‚PÿöåïÅÁêF\pÁð‡øJœ{î¹=í0 ÃX_L †q·àºë®ÃoýÖKðÞ÷¾›‹ûb{ù4lÆM±X\Ëy—ÔÑ¥%¿xި켲w8̳ÎGóÁ‰¸s+E×ËGêTóçºízÊN=m׃ ,Ð=8á[Ü9e:½´´•“áé,Щ,ge÷jáYô¼vZ×€j3'ˆ|´õrg^‡sY)\tâ,Á¼–:eS§'HQÚS`EuìÙF8›5s‚Cª ,€¨wYÔÛ=·LxFuÔ†{l)tŸb1Ï6Á‘Ú–¤@—ÿÍk‘(¿ƒ¾r_:ßÞÛµûù欳N_ǼµÁá²±]†³gBÄé)r;ÝóðƒÄ5æÌsm­T¯^>yè>s$Fð¥¨×ƒW𠤆ônànµÐñšé1rJnß7ñ^\_w^|×>ô0R"ïÄùÑ»‘Î&¶Ëtø|<íDg“§Ï]äpŸÁRñ*¿…§=í'qÉ%ŒÝ»w'ÎÕ0 cý0nÆ]š›o¾¯xÅ+ð¦7½“ì8l//Â<ÈJ[­E,éMÙÙ£mÔÙN ïnh*{Ñ@†=Ç^¶:,²-R,WXž'·—‹´µŽBÐå\R™$*lã²U›ýÞ‹m®ý[.ÙVa¡-Ëâ\Š;-4eœ®_ƒt‡œ;ò|þRÈð2m¼¬ûLõRVT÷’¼çºl8~•öº­W^±pªÚk•‰¹îÔˆ>ÚÛ¦{¿yJ )^Rbl¼XK輌ž»üµ²?™[ìýÖÓºe½Ú&=êz^¹,«½óRðÇï í©çmˆþ®Û:yÀnr5íù¥\ }aâò¾£sÌš(Þß)R¼Çƒñ3O`±.ÿ¥mqnÞÎûIO~7]ÿ³Ûü,È3¢¤ÔõÒQáßp5–ŠwcZߎç?ÿyxÙË^†O<†aGè†aÜ%9xð Þð†7àUÿÏcuØRý$¶áÇàZpŽª|åq­ÂD°.éìé±ç-k¿Mwû‰“;Å"kÌ6-(8œÊô…û²'–…4×)…I¸²R¬ÇIâäñô±µ(ÒÞLyŽ]‘2´3º“TâëÃ"Pf~'±Ï¥:yt4ŸK!ÑX¤×È1Ár»Ÿ^ÂJ{éô½–^‹Ny®]Ûí#%RtĈ,'(4ë#Ô)d9Õöyyj_m¿ÚFé{9Фv÷z÷‰ó~a®í:.«½¼ýçÊ |Ïœ²‘îÀÿÍË™Iû§Á¯¾-–e9ý¾äFŽ*Z ±—½ÿøÝ=©]´_|ûóÄ¢Ãïüîoã…/|!¶lÙ2û†aÌ tÃ0îRTU…·¿ýíøí—þ.n¾ùflñb;~ 9¶÷îÓ ó%(Y×D”=r‰²Òa—‡7·7öê£eQ9ÚÂ!îÔ ¹]^=·VzÏu÷!q—댽t³Ï->S)®ËöÛ1eS!´áj”¢Ü´-'—U#¯:æ¶ë~©úiÛáGxÌ*ÛgÓòz —;ü9è²þÃç-ÔsH¯¶ÎÊÎ ‡½æñ>úêtêÿC¼ïþÉ t6³|†¼èh#)b\U2ŽùßYý!ð]¡N‘7}eç}7¯Õ¦‡ ¦8€}øtÀ‰'žˆWýñáÙÏ~6ò<½ÂaƼ˜@7 ã.Ã>ðüú¯ÿ&¾ò•/a‹;ÛüÏb‚“ÀºÐ…–ÉÁ) ¨ Ý/úÃ…ÓŸ‡4Gb€:ú©px"öžk#ïWGuH!Oq_föªIüFíéó²÷‰-”<ÅB{„׬.’ÛÃõfa†”·Ð+Ï: ÞVÚ:9ƒ{¸.2;|шtš7üš!·AXâJœ£jãúÚE¿ç4(H ³ÙÞ×ùÛ3æ¾Òáç®êiVèzÛö´8÷‡½w#CdÝôw,æ»aÕdg "³­»æI¬šè€ VÚzä²ir:Ú£ÅÑE2ä|Ìû‹§ ½CÇÖ×7hÐöjÑgs©Á2²ùÿ…þÓû+o2çSäÎ7a){'ÔŸÄþàCñú׿OzÒ“æ<Ã0Œ.&Ð ÃØðÜxãøÕ_ý5üã?¾›òaKu1pÆ g†aI éT†O²3&“´É¹£¡Û'àJeÓÖaÝéãÏ: g3®›íW…2Þ“å Ä«"ð}ÔyØÏÕ¡\Ý|ö.”kïIÕÚP&2¬“÷œ½â5HàH1¯÷j{Ý‚xàÄl¼Æ5ß˦ѽ÷ޝ¿ò±@Ÿª}Xpð>åäQ´ ïdcÇv­çkòlf‰s‚æŸ{—žì2ÈV9¨Eå}–u” Ýͼ8éÖÍåÂõ׉åsKáB÷K¾Ÿ¸V´gÀ6š5¹xÀ‰ ‹ÆFƒ-¯¶ç-— $¯<ݼñÞwß]!eÜà(ߥ42šI¾Ó!ŽK+ ðÀR_9úžÅ}Ýü¸æúé*}NrXD¶pè½ïD{Jħp¨¸.?ˆ—¿ü¿â7~ã7°°°Ã0Œy1nƆäÊ+¯ÄóþË/áÿëØì[ñ³È›uºç'!eADËs¥:£aÏÔ™O•ã:Ñ–—Nþžºˆ:“7 +ê4Wm-ÔÙŒEŒ¡Nç%mtÌt¬®×›í’ËqØ|7<íZça`»eômT¦9Òdº^)âbÛg<àšsðȦpu°Uï<\3ÄBºÛÛj{ )¸yðGæMpàeþè{ˆò$Êõ€2 =öˆ§"/Öƒþ÷g,®5d$LŽ”hÏA¾ûé¨ñ}ÑQü¹û¥Ï°ïIJûG<ðÄ›ßüçø‘ù‘u¹&†aÜs0nƆâ{ßû^ô¢ã²ËÞÑ„³ÿ&8­Ý>&d’;^qÇ}6±8¢Ïã!aCBžÛAY†YŽo‡Ê³×p˜m·înx9f/ã¤m/‰Ù¥òiî@ó3»žÌ¬=~µ£½7Yä«ídÍùI¡žUì!w>w*ß´ÿf±öGÿÓMa{^1O^x ¡÷."qV,¦e(|ÝzÌö²÷%é"aüô9&Xi>­W¾n<…³v¥„…oæ¶· *Ägò¬×á÷tSh—(¼âò-…m“ åäÒv}¢·Ï+Ù7xBª§ˆEÏP¨qJ Çäjö~Îç6ÎN×2OŽš¶aÙ¤ E—vÇûÖ{–Qñ@’S«è¬ï®Ýg¾è=$DuôØs6ÇóŸ´g¸2DKxjuߪqòÌxÐУ;õËϲ‘Q§0RxÇËØ¥J åJàA ù<¤kâ÷—_;5Vð¬ä—bq³Ç%—¼ Ï{Þó,Û»a31nÆÎ—¾ô%<ç9ÏÅç?ÿYl“±ÏE†í=aí¦>q®…m_2—ü~ø³|Ÿã屸<·ŸÏ)%ŽCÙ1Kiqæmi›)!6F¨Ëó:ͦ·¥’ J[Ób^æH`[î&=äò…å\F'C¤ß}^ó æ)B¤¹Î™°_4Ó::vË^\ýYüíÅ4jÒ– áðYASr¬¶ç5<(Cþ«âxŒ\fÑïýþJÝÇÔ;“Ÿ™Yƒ¬iô5äŠfrªŒŒ@ékO÷Ì,N•¯±‡ð¬àŸñˆGüÞúÖKqÖYgÍ{‚†a܃0nÆFY–xõ«_—½ìåÈq/,–/Ä»„®wu‡‡¼A<ïO‡úÒ¼Iš×Kß¡-ÅB8å¡á9…á/9·›Ž)Ûˆ¶¼öBÑñú¼ÊÙ‡L8Î4Øç[ ,ô</Õ&ßmÚ3Ðò³äé÷Xl?ZLKÑv$¤t"7:~j7¶ÛõŽñXÅ2Þ†÷.œ}öáïxÎ<óÌu=†aw}L †qT©ª o|ãñÛ¿ý»pþ$LÊ—`‚©2Ü)ªÛNuœm—æ‰ëáºs˜i‘.®idG޼"ã<@tT™”ˆÛ¡Åo‘ËQÖe9X€Î~Ò;šòdÇÂ&ž+.ëŒE•çkÙ=÷øºIO§)\ – *tžæ’·âD o/ÖC^G™é]y'ó{ÕÒl;u¶N"&«Ù{N‚¾\çM¿3J(¼“A,5UVE{¾!Œ8GÕöú±—œc¤0g±«³ûKÛ a-3½³Ñ•g{æfíä%Ýø{bÚƒ.¸”Mc½ê|LéYì>sòy–pÚsNm«¶‹vÞ—§Øæ)äœ<¢5xK&‰ëÖ©CÝÉK·©›ù½±áL¬B /ºŒÐ ¢.yáATðjÂàR3ÐDÏI5\2÷6²ÒÚ ¿_|k#YãEw꾄 ª¤=Ì›ÌMVâÝ¥m+ý.¦ëÍ«!äžÉ>³öýÏuWðjP²?ïHül¤Æy¹_‰k±Z¼ÈnÆ«^õGøµ_û5››nF‹ tÃ0ŽßøÆ7ðìg?ŸúÔ'°€‹°ˆÿ‚ <²KèlÇ ×jBøHq’wʦ;^ìUŒ™%Èk!úyv™*+;‚ô·ƒ8ód NÑ×ö°¶9u`y~mßyJAß=FjP#M9êV¬ð1B®èØY¼ÚÍž*ÌDw¼Ì ›TY¢Oì䥘.¼ç4§—>ÓrmT&«‚Бað·CN·@ŠUÉ.œòtëDr< CÞó>ÛÐ^JTš¬-ÛõnÆ¢Z' ëÎGîÖa]¡.³^ówlûñ ¡—›ÓóËõ|b²§TY ™ýµˆÎ‘ö§‡ºsÛu8¼Ðêf{—Þø0˜Ô¼‹dCèô¹õ¦'á*6 IDATl5«x@H]ÈH¤·¯ƒH×I¶\óœxŠôm¯[Ñ­óèèŠ „+ñŽõw! í޾é{ßʺÇ„öøè6ăCãðÊÎÆî—Ž6ѵÆÂÞcËx Vq9ûØóð¶·]Š<à#ÛiÆÝè†aqêºÆŸýÙŸáÅ/~ êòXåKQàa‡]oÚcã‘ t”æaLÈå,ÒI»ÒžÇX,ž@wB@Há¡=Ý1Câœ÷O tGRIÏ$£Kr‰©>á•PaÁs—Ê%†K‰ôh~o%¿‹ÊJ1´"H‹cò:êNp¬¸—õ¦ÓÿŸ”ø¦ÅkßÇk^s ^ð‚ ËÖ’(Ï0Œ» &Ð Ã8¢ÜvÛmxö³/ÆûÞ÷?1ÁO£À/#Ãfå qCDóïŽEƒÕ¥yŒUûª>±2Kk![«KqÛúÚâUd¨2ŸwÕÓ]¿üNw\y»\>Šî<*ÛwNºÕ©0S=]OfèâR˜±HÚÕ [÷:ì·õ ÷‰ó±eûNÈ…ã“ÐöÞGe>ŒtY˜næ²$¢<Íu'¯8G|4W ²*Ç¡èr¬…‡Äó3@ÞLׄÆi±.á%äøžÊèŽÔsÒ—,ŽêH.ðv=È#¿OüÄsÐ(Y?ÿT×!ç”§²¸§<š]zw¾9M¡eؤ½·u:û†o’Ã5-”K)ï¹ ‡OA6«Äzóì¶öØÌY—!d§”ñ½µM¶ ŠÞ sÈ›µÓå=ÊZ>ë~3cqÚÓMOðí“Ñ÷~âû¡ÛâeåâgCï?fƒõÇãVðf¬à=xò“oû[qÜqÇ•c†±ñ0nÆãŸø~ú§ŸŽ[o9€¼ú¯ÈðÃH‹k=w<îÈÇáàa.$Ï‘Öa™SÔÑÜ®W‡ÅÕÕ]’LŠQ-îëÜ.>göxjï'‰)âÆvj»í¡ö‡ÎkÊC©ú|¤=XÒ¦°˜É:ß#4‚d(ñyÆå\u™ŽBäevì,>3OŠO#n” øÎi/¥Ï›Ûï€ÕF¤ÇÙá}su<…ÍVͽ¥D‚r > s甡›…[f9±ìO§àyÄ®IH×—$.ƒ4'ÖÚ(Ù"gY—Ï“’S3äùøh›Î} í¬­QÎç®›v²ç¸~´ßò€Q*‹{|†Ú!ŒÝAŠ}éIï&Jt¼ò+z ¨ƒÞlw¾¿ì,ÚLïñ Pæîj!|¦9ë@3ÕCÛ»œ“NsÐã•8A§Gßý–ßË++ßœ¸ÿ…ú”8eaCƒ‰céLꞃC*¢`½™âSXÍÿ'œ° —¿ûïpî¹çñc†±ñ°Ã0Öº®ñêW¿{Üù¸í–ã‘Wï€ÃùMç•æ@稰ˆ '›ïóVÄ…ðÒÐ !ÑịðÈQbP#G…´,Ò!›q… hMî²ýLõ‡ìåÔÑ&s;4ÕánöaÉ¢—+“ƒ òwwd'Žƒö¼´g(oë”e׆¬÷pë¢SÝ9ý‰ZöûFèŽiOs}Û¬éÑ~r µÖù%…4yéi-ê)Ðrn*c<ýÔÀäPøœOƒ—¾ÍÊMâª\ù(VÃöŃMùi{,Ÿù¦95aƒ¾±Áª±sߊ¼µá°-oö¡„{¼­=9ÄkÝwm€Bµ¥xÕƒ'ô¬P«ö¹%ÛÏ…mÓsBÎu89ïÇm–:hÛÌö>Í{ ož{-²ëö]‘#~ni»0¤c×–Ix†äS,è9ŒY×ã€F4S®ƒ8CÚ1ÒqÙ±¨0yy>Ø]¾•©ƒMº:Ø/ ~‰¤‹>£ùüyç>ÓO-ìBßï>;pÂnÑþÛ@_=#:¯E<Gíȼ²}’k’­ò˜žúLðl®þ·Ýr,÷¸óñš×¼u}t¼ø†al̃nƺ"CÚs\Œ/°éõÂÜÍ uû[{a(”5,¯¦çf§2¡×êw9¦mÝÒ HG‰3kW(£l½ïYÛ!§œê!A’nãìE¬!;u52ä"±’ôœäÅ“ÊÑ9k/g –뇎tœ„+NfEŸ‡ÊŒîr 6Ê_‹$sT‡6¬nmʓӳXó å\“àÐaLszƒr28º¦´ÌZ†iÇþƒ\¢Œðì9ËýѺÕrýs×ÚŒGÈ<ßõ|ÆwÕ »’Ï’¶ùL±Ç½­•Ërt‹ M3Wœ2p÷ÓgŸ}sà»eå {ÓõÀdQ{ið/üÕÚ¦Z]NÃsÓÙaî)šH dµö {Þ–u5/%HxW‡bÕàJ䵎v¢ˆ ý.“ÑiÛ‰é›÷²  {È( eÞ÷X_n9x˜.³vzúÙâD¢Ýs,±‚K±‚¿µwøbÝ0ŒuC†´»ê•€CPÙ“V4zafg-î(Élä¡6™|‹;V2‹4w¾¼êÔõÍ/ Ûè3:++\.ýeQÃm!¤@¼~µN.4o¢¸1D çµô”*Þ.xî9 ò¤ &xœô òÕôÚèô]›¡ž¼d3Fœ,ÐIø“¯Šž²ÍO żl–dËDÖ÷ŒE?­Y•Àê6Àó´žÎA¢Gf|‡èX¶R躂&oç–wŸ mû”$Žæýê}˜¾Á£æ"'Ÿ'z¶»ó„õ`S… RË^i¯ê°ÝiæèòõßR¤wCÛy©µ¢=Y®=ïL<ã™hj¦äu²éþDl3¡Á!"¶;™QÚ2Mi’º:aà‡BÜuÔ@7á¡Ü3¶§Ø6øÿˆî1c»Òôù™%ÐÃß}"}¾w²hëéDÊf§¸º yßj!ï†qºa‡÷¯{Ýëð’—¼™; ®º'‰®¾…ÔY¼:’Ø{Ù="ÈÓ’ç¼ö¹ƒRÔŒgXÌs‹¤WGî­z·f9oªýºöÙmë!ìz'‡7G3ôu¢ã–ö{ÑSY¯eb­*„‰ƒ‹íÈÛ®ï tjÉ«™4Cï_ð߇'«–b¨)Ks{]󹚠ͦí]ð\Âó|`OuÑ €~Û(¡…D-ÄŽk½›qr¸>qЗÅ}xÐÉí€S?ýG9üÅB—1,ÊS9RÏ×pvø¾ºµ‡\‡JçÐï4n;EÚÈ}•˜—sГöyÑAQ ±Ëèæ=õA âÚÕš:h{k§M¾&㻄“ò`clr›fX ¦X)9)†DúzyÐg ò!ä3PáV¬æ¯@é¯Å%—ü1~ó7έOؽaè†aËËËø…_øE\vÙ;ãbdø¸vηö€Ô¡¡“™N®@^vLÛ¥‚¸S*ËiÁ¼„”|‹÷๥äyçP_™h*8 ãu^éÐvî’kd'ëŠßIž9–ƒ}žÿùz|oúÐÞö!ÙR½nË9ÒMÝ­}(¡…¶S˜{#$iî7ÊѶ6aœX{zÚ$\ÂþH  ÝŽªÇ¢ Û¯9M.,×FYãéRºr*ÁØfÃU âd‚.j†™ß9ñ!y4YðéúXüKÛï³¹ø·¬+F>öÌ6‘~®ÒÕ¾a…Ò¶‡HÕ/Xd±/“ÅI‘ÞïíWåb‘î„ Ëè5½C^cÏû©F“݉{Ô·~ºó,ÔáÄ-BŸަ›ÑV!ì)¹ªFJ¨Ó简Ÿ-Òå}z×èòlcã_S‘Y\SŸmÏ;¸»^"𨰌¿Â*Þ‰g=ëÙø«¿úK,..®©^Ã06>&Ð ÃX37ÝtžúÔÿÿöoŸ«_?ÞvVe³î´6¢œB*ó’;޵kúcäaBøN&suãáiæy·s,Ax„PSÎz]«œgǦ¹å}âŸÂßÅ‚E )êŒ9%’¤0ïzG»ËÇÍòz¦…]°.Cô1bfl8¼ûº^m§úRt17—B|[Q-KE‰Ü(éUëA÷Q¹Z×›‚¼ñJ`G^ɺuˆkNóÐÉ6+1?Ä6Í?‡ãzÊ&«v¹ˆv{=a1D¢¿ó4§¼À*б‰6‘<èd{ìQïîÈ Rö¬Ãîy©Ãøo-Æ)g:éz¡ž#‡ª™ªB«èA«- {‹©t*\¸™x.ßó Swþyz*%ÒÛ6»æœHŒÇö,£F´sÂÉ.å³0â¼”ø¦ü ÚdŠñ (Ù|Ýôå­áÙÈJ¶çºê¼#Î×&Ôùs÷ÝÚE qú›¬…ÏÚCÛ-Û¾:rK ïYBl˜ûáˆs@ tbW`%{5Î9ç‡ðÞ÷þN<ñÄ5×oÆÆÅºakâ‹_ü"žüä§àæ›åᬶs[a‚ ”´¬éØ; xa„XÊF† RÇ1«8Áyˆhé+9àptzé)0 :|î+Ñ!t1IÔ÷‹f*Ï"ˆ‹ðZÅ!í±@ï?fíQꊕØÓ.½|éúÆÎW×JíeŒÃƒ]tÜhžðø‡<Ž%g£8^zÚÛŸÄ[%Ù/[•UÁóÕ<¨ˆ%Ù¢ëæªd h„9‰ôÆ{^.†s¤Ðø¬õ,Ð}Öwß xçið†—U“W=Ç´äÓV ‡mµ1””-kÄ1'‰£È–x`ˆ½ÒhÊu°€ø"û®›ùå)øä:ê6„ž½¨G*Ì™*s4ôÛh×kÞ]Æ-¾.”(¯Â‚ØÏ5þEÒ¸¦ËÍÇš…µLz8F Ó{5dj“VlË2¡ÙtìIϪÆF]°Ñ2¬BZ¬CÚ»ƒ‹Ò#íÛA9Ø#ï„l¬Á‰9õéüt=ã>:öì$qz¿á÷ñüâ<=È‹ô0w-¦ÅïâÄ·àŸß÷OxèC:Çq ø+`Ý0Œ¹yï{ß‹ÿôŸ~ÓÕ{ÃW‡“A~©# ˆÎ¨ O–s-ë‚“9é{:d$¢Dç²PûÆszÙ{çU0ž³Ûŵçèõ©»âdx~cìEå¥Ú ½F}KôÈÌòˆ®½è±@—mK'«íÈÏïPR-çbE”G$Ò6DÕO̳þ4ÀDíÀÜFÂ\ˆŸÖ®k ÜZ¹)ì_Nt¹:"¿ÉÎW­XÏ| ߈-Z?3Áëd]:û:?dC”LŽÖOb´–{9éläšäáùHÛh<'ÙµõËÌìó$áÒÞüÙ¢©KÚî»óÐÙNµ˜—S7¢h¨®°T¤Žðj°©±óbêw2¾Ïéä™oö{¤C#2¶åÖƒ%FÌ*žŠQÁÛ¹êMÓ¢„œi¡>ûù’ðõ¦ë)ןÇô±ö9ëM×C&3 ߦÞÍZ @›±šÿŠ…=xç»þOyÊSf¶É0Œ»&Ð Ã÷¯yÍkðÒ—¾~ð‡-ݶóÙç$€o¶w–{îùþ赕õt‚™"@ëAW"]ñNøptßbÌ/Ò‡h×»¦LÙdŸ‘‡’ÊÂEK³¹f0X0Eurøp8ã;ÙMá‚I]†Äà‰^àš‡®¸™•!~ž:¥ éæ\'Ãâ\¶]Û³ÌâÞÝ¿;¥G¯` ËxçÊ>ÅçÎ`Ó€@ϢĈªQ®+º;eB]mol¯špdHk·¡îîªá|RÉä¨Ü¼ƒ1ó¤jêŠô1YÜÇ&‰+·Zª¤l¨Æ2VÝ+Q⣸ä’Kð¢½È’ÇÆÝè†aŒb:âyÏ{.½ôR8<¿ÒÈPÙÑ ÞšŽ8C Ñœkê0Jh®¦ì<Æ:ZNK´7þî8Ò6/¶§Â|Il×Ñ~}K¾yUvž îqÙYœËÅ¢£ß˶V®Û¤;—}™²Ñ¶_¯Ò^ˆ“È›îj ˜ #!#ÄLJ¤»„¨Y7‘ÞØ“\gºž„6Ð|u™¾ZË^‘j¢AÊ&YWG¨“-{Мò´X×tÝÊ”-kÏ'o‹Ÿ—ýÏÉAƹ!RËR9~·-cËÏ3DD"Y&Ûò¶Œ²ùQÑ Â6×"Òe"Ã6ïGcsM{Ã+¬àzª(ã{+ÐcAÏÑKÝήÝͱኻé÷¸„m¯oJÑX‘>lÇ+δ@çw{lS5Vð¬âmø¹Ÿû9üÅ_üŠbÆ`Œaè†aÌdyyOú3ðOÿôÏ€ÿ#8P8ÍK•¬BmÂ-ßKë… Nlk3pgBÌ»®°§ÐvÞ'x¿DÇvÅaÀy$C¹v4w\»¡î¼T±Ç(5÷X¶cðR5÷FzGg…¶‡ýÖÞÎ-L‡hò÷r+-ä¹Ù‘W\Št9Ÿ7/µêð¡å>^ùæ{)äI<­Eíj ó][’m®¶WÚÞªI¸d´ÄU‹¬ï“Pfe0YV·4¡ñ ‰„_ å¦à$‰,<‚x׫ÐêiaCöÂäõo¶M)äåÔ@OÑâD?W³DËÏ;gÌ4˜Yâ\¶„ÃÖyþ9’ÏO‹‘k¦Y•C”Ð y‰ÎÊÉ)`[_ r S¾K•}’ GìõDÛ\5áA(ñNN½ïäýÖÓ4k¹‡³U<G–Msù¾!éc Ùi*‚ª¿µñ»9ö¢ü~žâ_°ê^…§<å'ð®w½›6mq Ã06*&Ð Ãdii O}êOâ£ýêúOàp¾Ø*Åu2§ÍŸ¢#'îZOZ]ƒ=ã”  `1Ö&èj„X•7§@¯Dá!Šp¨UgV Ðб£ Ú$”Â’o™è ²×†½†Øñg)–h¾îøÁŽîZÓã3³w…÷pùX¤KñÓ行ôIÃÕÔ=?¾ÒN‚]–¡:%.@Ñ5íéT/‰ýuƧP7×E tÏ6ÖÎ?/„'=ãü Õ & IÚ•‰:}‰w\·½kwžK t9m¢³MoÔl“®ÍóQÙÈ£LûÈ%ØgŒ|ì`‰ƒB…HÀåEè0x©«•-÷|¯!DKfQ¨{Ýì?Ý„ÝÊ6„5«ÉNjÑÍï.)‡‘w½Ù$š)™[79[8°þ;>Ä+Èöt#Ud 2‚$ý¬ÈgF‹ôÔÔì¨mÏ];ÖÏ•¶g§ŽÉb¿­Ry‘ž(»Z9U úxYÁx^zçÙ<Ï´Òhïü«̦kCÝw°,Ë^÷´XJ*$­mà‹éäì ôPþTůá”S·áþ§vÚšmÆÑǺaŠë®»çŸÿÜ~û¤ç§®­¢”·±MÀ%CËq­mˆ2"Ç¿[ÔV)Ö!>‹ß>å)ä³n¼—5 Q–,µó9iy¬rºæ¡#—£TÇL/YÔœR;8‘Šð]&<3¾s~ñYTo?³EÑz‰ô”w²¹ö®¹f¹¾vAŒQ.“RÙ6©\c‰~tËŠ‡&ˆúÚäsÑ€A+úgýëD‘Ɔ<}ï"û"Ñž>u.BE¹VÈÓ\wJ Ö4­ZHˆ–8Š#-–tx{w-ˆ(Äb]o¿–Çào€v/kæç§+Æè¹#–‚}ËzgÑ7m#5ÕCÚ;‹« Î…}ç«PóÏó²áÍà´Ã¾©ö™OES=Ú ï™˜¶#/ åäÈørÊ% ¢Ðw)Е͊ãH/{+Òiÿf°"~½Ÿ{ÊÊ,îÚ>øoÚ:õ••áúqB9fÏÏ[Å,b»ÑÛ¢åúïâÐ’=¨‹ÿÇîªðÑ~»wïžy\Ã06&Ð Ãh¹ñÆñ˜Çü0n»m3Jÿ—€?‰çÊæ‹„1ô†÷”õ‚`Z'ÏI>å9¾½^¥šç¬«öÔܧM&”+¸Í$Ò[¯gÑdÚ^ ¿§›Â¾e#Öiž1j¸&ÉW†)jä˜`U4ב:s””ˆæ:† Ý$äéûF_&¼A]¡@W¨zÛK0ÂãØïA—GN ”x»é\>^Æ/ªS†§· ÷ú{ém³ºSø{œ$nhjFBß~O¢IüîtÚ×GrÅ„'’ÊÖ¹s—˳©¤s‘˜—ÞʺHG†ˆLÜÝH<ýd(ì½»Ð"Ý}͹Oœ°nk‰0‘¶'ÿîŠôª–`|ë1oì-›²8'ÛíËÇ0­”—h3´žLS¦³†ç­H!î¾XrïKJdHEô^¬&á|ª…ð= "ÑûÖ5e³iȱ•Á¾ÛùÛàЪ YÉeåt¬æÿ9OÜ#GŽU¤¿I›Í¡Ý-;_B9@?r.|*éaw '^ @{ÐɪØ7¬p |ñË8îø|ò“3OºaÜE0n`Ïž=xìc‡={J”¸p'¤ ÊŒè)/‹d‘N¬5ž¼I³–Ç’eStBó›oìõçãËŒÛ$è§‹¡l¹ÎlìDYáž•puÖt§M'±ŠBÝë¨ &Ã)¥€bÞú÷ú„©§Ot‹ œé\–¿Ÿ!ÒÛMuB¤WÝ{N÷•Ê’Ç=Îú.E¶ÜNë­«ã&ºÜ?ë±mio‘Þܖ˵'ÑNöÕè9O´ËºÑ±2ö¼7×>NÐ&í/Ú.¹=ö ÏzÆ¥¤ìpŒ€^»@_)/z¿H—aÊZpeð­}6B½õž{¨éjyÀÄÊT’CꞸMY)ΛÁS²CÊSšpÕb@¨Ù&ß‹2£õÜı)g•7§Ÿ›ï¯È!ËzJäIe+•'D®ž¡Hp³W<üæÈ=Ø”N('ßÕú] 8¹@üËsË9™"ÀïR=0ê•+xÜ?SNà“Ÿü˜ÍI7Œ»&Ð ÃÀ­·ÞŠóÎ{<®¿þö γS"¯ÄÐÜkáÁH‘ôFÏ¡žW¤SGvlÙNÝMorpÞøÐS7ôôŽªçm¦î"š‡žUÍÜtiw}«¤ì-Z3½/ª#EgÅŠT™hHÙ(½çšˆxµvQ¶SœPnñ´iÏm™¾wnjЉjz?§±Ôïî¸D׆ºy<ÔÔˆèJžùî~=òübœõÐûáþ;vì<¾aw&Ð ãÌÁƒñ¤'ý8®¾ú ¨Ü_Ù:kgÂG)¹=îô A‘¾pô™aèf8|œ°â¤GSì#›<¶òÔ$ŠÖK$Ã>Û©ôœ7Þ" élË6Ývaê´:¸fþ¹Î’͡ŌÍQï]ï$ŸÅlOã:©O¤§:¢}"=%|»ëË´ž²¥Þ¤Zà:òpùžËH Y¯è¡6ô‰tY¶×‹/鱳μr!èkao^Ø[]èpv¸°fº¤šh»@jCyè14ß\F„ôÍÇ_¬÷ Õ7û8ë³´×8—S¹p.,Ø[q•…(™Ö†³2 .ñ +Ûo층k™\RÐ zz×7Ñ&òoˆªdB¹89œô²÷fuol¨þ a¯<â¢~¸ô¶8Ùg<¨'³Sïãð³–A'ý}z`tH¬÷½CÇ ôèVuº,ëñUäùÅxìcŽ÷¿ÿ}زeKò¸†aܹ˜@7Œ{(«««xÊSž†+®ø(*üŸ…nR«>Ä<ÄØ›Ñ·YŠ¡9¹}miÅùPó*î,ÎOyÐ…ðêÌåŒ%C¢"ªãê„8÷Bp“7SìP-„óê-+½ì“pìvù6@áwØ».ËÐÜÇnG±/ôRv©Œž×>†nØ2}?F¤ëm²ÓÚY7Ø‘P‘žÄè>öÙPj0‡Ú{*oDX»,ÛŠôh '•yžÂ—Û6ˆÁ)‚äœàöù"ºjæñª,ï.ˆõV 7IåÈ–\¶—‹ü,Ë2±° –m‰ét˜¯Î’Ý Èöɵ=Å`óÙÜö¿ (ŠÖqtÖYgÿò¬èuö hÎ9¼ç=ïÁïüÎïÁâ7óíËà„º:kVDOå‚ìÜpEt Y¤gUÞàRndèp5 ZY}X²;©Púm9æFQ0Ù%V]|~»¢†sh¶d« úœeÇÒ‘´ý­ ^v<¹ \SeŸ^9‡å |¿ä$²,SnÉÇ혞WÓVƒsæüYêp*Ñ6²fËŸ1à´É?okÌ@&²TÎÞÜ&V×V¾XVÈY_ÐWU‚—,ºóWXÜï”ÈZ ß{Þ~„œ¶Üê`TÛo¨ÆØng”‹¬@OŸ´ÿB°jÊÛ›{¡£èÑuéÊ’ôÛ(Û”½‡àT’Va*ºŽflÓy/Á¹àœ×" ˜¹(gé>pÈÛPÊ‚‡¬Ô¼ÖÕþ¯²u6¯‹uNchô+ž+rÝ­ýXBq8 ïsAº4¡€iL ëgH{šF Ú¤ùèñ}ˆÀ”ø2Ô|RaËbqqN-Ï!n£}º‚ƒS€Ž×å~?öc?‚ßú­ß‚Rwhí¬³Î¾>ÖôÎ:û´}èCxßûÞ还@C ¹R =0pËòׄA·:ú¬¡ œ^f®ÙZ«dð‚¥¸›¢rüݶÁÊô¦´ļ³ëT V0«¤=ðvÌ™ØVy]Õ~[!ƒ! Pä¿“¶]ôÁÀ•úÔ@ûw*ÐA%¨Rq•8Rp[°ím€%•8–iÛÓÏe -L eÐ#Èo²”þÿ§V±N1_“0^ài³U ‚–*k§ ¼ö`G¶©¶Ó÷§Ýòï­²´º;°œÛ›t+ØÉº;‚q!qUÞÕr¿«¤Ëâz*8Þ#(‘€}•5Û·v¸þ«ÚHòôµ {ñ˜š€[Ž HÕj@ŸÞw¬Äú+ç“®âô!üÌÏüÌÊñtÖYg_ëzg}ƒÙ'>ñ ¼ûÝß§þ6`~>Ò‘õïÉmŘgçB:è̶Ͷw²ozºúi‚ÐvÛ ,ïÝÎÎ$€À¸œVøM Nè,§npA}P5s™á±†ŽžegV¼*šßá¶Ì Ãú6uܾ­ê!8¥ °<ãÄÛ·i¿}[ûë †˜u·° ßrŠ£s7fƒãz:8§¶ìh6hSìçM hB·b>è¹]€çNúM+¹K ”U¬Uþo¶@ß.Ï%9ï–̧ڊj5òÄy1ƒ.·US~þ(šWÀ;“T„OÁ¹Ší!€Ïª18á–æ\<zobN·——×þrŠÄÉßyUU`Ë“`o·&Á:@`ZÎ9SÑ¿4­ÂÏ óÚênµ~­Ìæ~íë-ßk|”Š‘Ul[åñw8¨æ§iÞÍû²¿Ã}Ê=ÕM ,F4–Å >‡8 ÖEˆà%«©Ò QÍ̼oÛ(♲éŠ[²Åkçsºv.Ït^Gå’¬úçPÛZÈ-Ùâg͵×W§-`s ›¡‘¦ÀŠ Ð%0ÿo êÿø‡ÿßó=߃Î:ëìÿÖôÎ:û²'Ÿ|ßú­ÿ*&Óo‚U¿zÎ p€gV‚µ‰Ž³,p¹„ðŒ‹ªªON€ιtêà'É2#>é^½i[®rÍ2Fv9PðjØä·ô¶ÊÉ@dziË2Nl¦öû×Ñ ÒOsŒ+Ÿ¯^õé|×yÓ eÉ2’¶º‚²äÄ!ÇRDz†…‚A¹'o´§­àÀCû½„£'¯7µUàýWœìWÎ¥êéK×°œß±Œ«%îbT1ÿü4Î :ƒóyœ/ÊyfTû:ÍdÚǨX’Ûð²ßl[öÅœòó¯ò9ª«ªo7@MúÛmmÛåéiÑû±—èïÈß™ù[§;do À2@g@Π\æž§ò÷Î…Â#Í9—ó3Ü_IP´uOñphhl±ÉAQùœJ«:‹à<ŸûµÎ+‹8˜ÉkX]Ð1V=º·Ë¾ô6Êà9(ÄM]GÐ/çjcÃä\Õ6äΫD&eïËA§;étýã3Bάö8ÜqP§'³@Ü-%y¾†4³d“ÿ÷]Ìñé?ýììì,«³Î:ûúZÐ;ëìÀʲÄ÷~ï»ñÿ?ŸF­ÿ[@_ôà÷v¯=3¸T×;_ Ñ¡g«³è›­Ü’í“…ÚQo£“Äcä ê~˜ Y|&è–¶w1鼦¹Âº&vç4€žî9;Ž &\”€¶sY Œe@?@U)#Ô|Šßö©RuèƒØðàD±­¨‘d‘N³¨ÈÞÛóÔOç©tHØÁD/‰K€üiv[¶ýëÀ § ýNdùaxl¬b= ol³¦ãü¹mÛUà¼Mj/Õ5-ýŠóºŠµ>}ÛÀ—ÎïاAùÉÝoxŠ"$¬9I 'ÈÝ- 9ç=õ ¦ƒ¼]®ý ¸ÅÚæ/Æ· ¶Þ®-³ãr­ Ï `I¼©¨Î†®›s¨Q‘= Εڢ]Z±×ʰkFzŸ öÖSš·´t¸pµãœkοf:G3ˆÉß‹N‚ÔJ®yí)AR}ÑL“ë ¦–žaà±/Âÿ¾óoÇ'>ñ‡Ès¹cguöõ¶ wÖÙ7€½ÿýïÇßÿû¿W|ÐßI“‰“&™tS®èŽ•p>HG¨Át»Ûÿ•cH[¾ÏmÃ1$ ú’$Ñ¡“DZÊ H‚0!7 ŽNЙ }¹%Ÿ, ãËt{¢4ðÀçARè·…q¯ó˜œ>¢pJ}º‚R‘³¤s)ó/ùïËa+Oå)ãØÜ–ª]º+®áP[ô”m¿Åc¯S`Åcp@>£6/‡A%ãnËWg=Ýß\ˆ«Íémr`ÞÌvŠ Kb αÜoøm÷Ë“¡ËïßyzÌíæ|Ûü^5¯ïqáÊTØóº Ò÷CYDŽçKà÷ :ùàmsNIÞhÛrϦÏ)…m°Ü†kjÈàÿFPe™¸Ã…”ª§ª +òÛó- 6ɹÇë¢å2ˆˆuS|·Îït ^¶³å«@zS‰„ä[Mp×ݸ&Ïž[“TY¶ñ¬ªš ¤Ùç¡ÿñÏÿ>øÁ._çÎ:ëìëf@דּ׹}ò“ŸÄw÷wÅOÅßBVÆË%X Rf%ØåïgJäR·UÅm˜w&¶<þoÈ£ ²RVG‚y£“ýÕâ·eM€ŠZ¥AÃm:;?±L‚µUlmº Q%–|HrF“€D£š¶j: yqœpv뮇„„S3=owŽ–ö0–æ«ß8—i2¦69ú2Ñ©–+þj0èmòùV ÿ]Ž¿l’œq ZR½!sO «3|›;Îïý²,?-lvy¼á“eÇË)ú¶Ú^ø¿=8i¼I!èi€ôµa°$î‚% @MÔÌ€dÇé÷Ãý¼|JÖY¹6ÜI½„;:AI¿òo] ±Í%†5€J‚H¡>‡I˜qÁ´sJ¹¦dI➨@¸-hÞñÚèBÞy¤7S„ä…VRaÁ@]yþeÝ|Vr°Æøy¡“µP—M€ÞHs«ãÿ8ú=|ò“ŸÄ»Þõ®W~];묳—e@דּױݸqO¼ù­¸yp¶÷cÉÙÉæ‘¡pZH±]ÄKFä+ËÑ•oÏÀ½Ê#0Öud=ØÙ—Ñ\PNlEL:mûáJ¦¿ÍÒ¶@»7­R§t 2S§t“¬ÓŒA˰Ä8&9»±m’FÐ&cÇš€õðS˜LÙïŠâ^lw¶çtÓÚXÄøY2þÀ2»&xbŽeÊÞ Ø}9ÖÆÌ/W°i:‡Y ¶‚Ao-¨¸bþ¯’å·1è5«^dmƒ„A_5ï o™oé|®3þ_nÁ®{lûØÝÌçåA›Ì}EÐI¼³¼ñ *ÈÙë¸ç9Ï%^¿3™‡žÈÜWÍ»; PÝMAů—qà¨Îé˜Ë>ã|->ܳ¢«*ètWþ;óQl˜q1×VH¬ ðvo·+v¸\ÀÕIrK¶æ¶|íÛð±YðRU–®1éÎr‘ë¨rTí½6@6ƒ¾þAì­ßÄ_~ásØÛÛ{u®WguvWÖôÎ:{šs?ðÿ6>þ‡ÿõèÃ@¾ENWÃÍg~».¿ý ;ìr^aQÛ´˜5Fé51²ò¯Í©i¨æëßã~M)*ÿšþyNÑxÓ­|œÆ2 gf>iËA¨¦³ÏmÃ>»òäé8ÞpN$ ”ÌDCÊ «PIgø¶`I€ó6ÛÊq§9”|VVö–c`Æ*qFeÿ¼qè§™7|w¶¤tsU“$w)‡’¯’×üZtŸ‚iò¯ä1˜|·޳œ3>˜-’`‚˜K Y»[VKàܵ¼æüR­e>±œáT,2æitˆ€»M:Ì$—Å{_¶µ™_d@À·]ÚNþKòÖÙBEo‘ÛÚ¼ »“y|{†~u÷Ø$Y z’®ÁÁÀH÷mó–mÖtE¯³µ¹cpÎ÷É2³ÛÚÖø¶õŠó¾ĺ“~o×Va9 ˆç’ó(' Ïó©ì7ŸUóƒ²×7^ÿyÞW¾`§wÈà”ÉÂqm»Üí.màœw̨‘ [î !ÕHŠQÊ€¡O‰€^ÞÔ×a.ÿ¾ÿÝïÄ?ù'ÿ3”:e}ÎþJ¬èuö:µ|ä#xï{ß lÿ"0|{ãuôNˆi(&~ÿí2:%Ì&›*¶]ô€|á÷¸ óMEŸå `æ÷­­‹È|³óPçÑ9*&~ '@9ð@<‹£ÕôÞª ‚l+ƒ fAýç³X ~žw6xÛ2™^û“ØP[ àÇr=y¡r€™7Û.9âáƒS˜îU äxʶl’oäzŠ×ÌHSž›#_‚´Õ4›´mä†&u[ý‚ N?zÞ¢/œw¯ðh¤H0îš×C2E²ï´6€¾=kÓmDGYšBÓ)6‚¼XJ‘—¢¯ô˜xÜáØ\rœh‚¯`]lÓv 6×Ð+8lîÿй1Ûæè’D8• Ë÷°Ó*1>IWñ}ùÚ¶]£»1žû«Á‰Ìy_hjä/Õ@ðk¯/N‰}Í=ðæ Ÿös=€ó„g†T‚s äO­Õ î6@Ìßmœg,oVý@Å [›É(í‹®¯Bnåí²-«¯‹t  Y¡Ø`y{5Ûòk›ûà”Šm«‚žMÚÒ¶p@|¦Ø´˜¢‚r¤2 k(Yq†ÜáÔ·ª´¿Ü¦ Îc g¹Ê¿ÞèšÆÏÛïå~/{™þÀk…)£ÚÂj„Ï©­SÀäÓÀKÆG>òüÄOüDûõ묳Îþʬèuö:´/~ñ‹ø–oyæ½ Øû÷"€] €þ1ãÞ °Òƒ™A4@my_Ù|Ú»Ù4ov„µòÙ XŒè;ó‘ï×Ë p¾ê‚>[ c  Ÿú}lEÛþK0ûì¦l»/Tö¯Ãþãš…ø=›´ @-þ¢j‚/ei¼š‹7µ­PèI©U¶” œ8—<þj$cÉcUttŠùòue7³<†ô|dÍÏ™yå~µŸ'2°‘æÎËkÓøë–OŸ#y&˜àãÔBUbÎÍ`wÍkÁÆù·’mæöÔù2ÈçW€Ä6S¶£ñÀj€ñ–y³$Aµ¢än¹­Gøö¡¶ZØqnЮÅûÙ)Ì90ÚXp#Ìg!§ç|aþœ·ÿÓ®Kúù˱FˆÇטËI @3åÛf‹æwt¼yvÚç#æ—§…½Ú$ëK9èIaBfÛFÄ+`rþ´Ý¯á¤Á4ƒRœ¦p';$¤é&l!h!Ö[S­^3€$-BÅ×¼~ÉyȬx-dí!•"(eÄg̤ûtüñ›YcÞ*Á¢ßé.à ]Çlçf JkWðüàkž•q~(?~/›{¥ÜÜÆ}aØ«¿Þø3øó?ÿ3<þøã·{guöêYÐ;ëìuf‹Åo{Û·âÉg¯£zàçô=Põ‘ôrH¬õb È'^êW ¬w,³±…`Ûë<ÊßèÌiÏh›2æÖ=,jb„˜m_¤’x#œ\ï°VE”¡7$ñ̦Håí¥ß;—ÇË2ìàp»d?\!±oìÍ`VÅñ±l^;êYQS£!!”ÒfÉò6Ì÷¿T1X°8wÍ «x>ØVÖj0«²Ê»ÌÛoÈèY}àÕ Ü–12åA^“pýÄ5i¤4ȶe)Ü–Pþ åYGãEÌ2£Šb1xPñ·Øùdyg ~9èÄl&oõÄ“»B¼Í‘€ZJòSÐ ì´õæ·±‚-M‚l«À“`<×Â6TÜF'l·d¬ýµlÈדºðK>€•ÈìS ®›¿Ý¦ácçZrÎñ±ñ¹á¢aKÁ#²yÞ9M׊׺°¾˜*ùü¦m!Uõ*Ÿ hðA^§8 À¹ÈAçyÒØ^ïâŽ²Ø ƒû¶¼d¨¸f! *$럷 *ëb˜ê¼.Þ[Ù¦(Rb¾kKœ´Æ‚J¥÷býâç9è<Ã\òà\×1½4A¶Íâgæ¼ìÓ_.Èà½Öý£¾:½òçlUewYôMVqEa8í¯IæA4ÏÜ!5MŹvŠ˜ÐQúÎl;¶½ÎšmíÙ³¿ŽÇ>‡Ï|æO»­×:ëìëh@דּי}ðƒÄ/þâ ÷¦ŸÖî¡7ÜV=bÐ'[@ï˜o)gvÖÌ+>zÓÙÂdÁ¶K½Ř¾£ëȶ@o£ƒZÈ`¿m|ó~½UŸ‚ ñÖ@ÈïÀª*°/¥Ú¦ÙVYLXó!·Ú·mTŸ—k’ÏÀ(ì3ÌŽç 'úv¬Rk‘,ɘ«¦<ˆ€1émÖ‰DÀ‰ß\Êßw‚A œ\î·ß‘m`…Ç F ÙV‰º¾¿4wV‚èÌK`³9ô¡Êu%®OçóN¨UÔ†18¹"PÆ`âÜe0cùu2Ù¢©(y¼<÷¹_í¥¤ÆIÅë—îUÌŽ7ϳ6)ýR[aé¼[Fâ³FÁAYD9m%ðòrÐ25Øóý´®Å¯  Ò1_}¾FcZ éoésѹ@"³ì<Ÿ• …ç”SpΠYpÐ!„“òv‹°m`¼æp>Ý‘ç  ÿU cqºÉ +'怘Fzx™»n¥œ¼õäoáøüÂ/üÂ]\àÎ:ëì•XÐ;ëìudÏ?ÿ<{ìM˜í>¼ñ¸)Ge1!Ǥ7&›$-/‡‘Ù ²p‘Ë]LÀSêƒK`ÅMtXó)èlYl–D3 ,ÇœXübL¯¥ÍÎ~h»ˆq9N´wX°³óÌ{/Wïu@pzدz^ÒßP{@ϲHÁgƒ – =a=MÆêv²Ï%Î,úm:P%þ§Æà)€ ç€`IrÞšÎYÈp`EGà+g©–’U>‡ÖxàaÄõ[D@®l³íªüI.¤ÅÎmøŽnæe6dÊ‚yæú Ùœ´L2ÿ s,T6A"Ïü7˜> ÀøŠÉ™H¨ã¹ã¶iåùU{¬ß 8OÁ¼´tuY\Ùe®=Qgô·ôA ¼YýR 0ÍA;N™XteéÜš’Ö ]ÅüÞªO¿ku¬"/ëWÑ–Á;‘ä{UA×›×pߦª ®¡á¯wØÝ‚ƒ‹r­ój¥¹Q¯h›8/ýù)BÀØ3–ðAž²ç§.$Rš q½Wm××ÈW/éÞËçñ»éúOª¥ˆ§Pôœf¼pÊK›‚h®K©-½ œ/µñÀ[¶Ô` IDAT*tBíŸE%3èkñYGª/€Ú•}ú9òÓ-º~ó únÙªšºðmûqþšŠž›Ê!KõЛ–@­Ñ› (ìrq¶F®xÛJÀ]L€Ùz,&ÇÎhÈLÜ"Jã%ˆf'žÛ*ïàSïPÕÑçj-mþË‚œ>8Ĉ¬3p¦¤|{™ÏÀ;HÊ]T”Cÿ·‡ÀÀ0“ÈùXR™;ƒÖàäÖM3èÁaõ€MVœE–˜9L‹±£)ï”é rãWËT<Ï,k Š‚à2_ Úø²Y#ÁKlmdÒ¶{ÈçÓf>¸d(e‚tÅW^ åF¦ ΡAûC   Q<” .†˜-Ðʨ†¶:Êå\‚˜?â´#äÊ‹ÇôJö\9×Ò¶Kýµ0•lt~Ò-þø³¶ªëm•ÿS©;äR.*^棸îh vYãÁ8ƒsèƒ.€±ÿ• ì%>þºÂ¬8¯_ýâó'Ó\ò9­/¼.J•_<^ëx}•cÀ”¯QHï*fЃj‚ÿú4«bqœ3`“{¦»8× KE KÓùœó1I UËy‚Š`¯cRÍæÛ)Ö˜¿~nÞ /Íc×|R¥ÄzÏÒ|¾_YåÅ–²;–¸›ÎçktýfëjèÒÛnigþù£,P â9å±ò¹âyÁ©ô qŒÌtóœ4b;=À¯‹‚hÞ1;Îó=¡¬`@ß(&Çý: ž"û‹ßÁ¿õîïÆüÁœ~=;묳WÅ:€ÞYg¯ûøÇ?Žïÿþïþú7犎'Ë)k_ôm¾ “]*Òr Ëô鶨ø¾ˆEsZÙö‘ò6Ñ,?Í#SÄrô°º·GÄ,[`ñûf¤m“1ƒpŽû 4¶{Sˆ€ öL³øÁ E3^æ©)¢Ï‰^ ÎK €_ÉJ1H—U‚½åÿ27ȇ_“3—9Ä’5 ca‡Ø6~*鈲s>ϼ  ,Ç€[{†’eîR,sÒ¹ß Ÿçñ+b„%;Ïci¼Çt7&¯ì7µ•Ê ×ül œËÀ9’‰dÖUÖBH›YZ¾fœÛîМŸ|­ÃðRÙz2÷$X Ò÷¤ýªÊï Ðï÷RÑÄähÌuúõ A`á¼ò4¸Èç)(¯Ys,R±!çEÈÉ÷ëW>‹  ¦ý癯m sÐy}T¡¨/Ô¿@œ¬ÆÊ#]מ¾ô |üãÇ»ßýîÓ¯iguöŠ­èuö:°étŠ7=ñ8^,`¿ó›Ü3QÁ1årϧ[¾øÛºÏ£’R:å0²ì½1µåÜtÃì|V¾2û|ôÇô7°aÌZ©&Àfù©¬â΀ð@»ˆ9§\ñ=«c[–µ6ƲæÙ˃/X¾P(NäžKG”ó YÍ@ÓÁ¿Ï§W!Ä  íj…³ÚÒë¼ >Vù>ÿ.çî¾R»I~ZXÛË<æ ·ÍóÔ8h §‘?KåÜ„&Lði€µM–¿wZžvÛyHAÊR›¶`É €ÞøÎ ð³ÄFºåãÚzPv´øøqâ{Àò1´„ZÀyPrˆ¹n—ÖöždåsÞÄ Zë}aÚ%m”*§œë»µF°¬E½p§Ö¨ï!eóâjq4ªµû aä~~0£nD[©ü‘L´ç²6 @êÐ@/ƒ‹É:všq¿K÷lÝ 4¾#×Y¡ë€°5]j ®âsƒ´üL•Ù}y>¤gÕt“^/Ö¨ÎW·™/8W³ zþL·èYµX`›E&9XY|H?Qõ´1èÚ§Hä"]l•t¯ÂàÚ! Â3érÏô4†ÓÒ@ˆ¿.úsÃÅ5ƒ'¿ôE ƒ;»ÎuÖÙ˲ wÖÙëÀ~é—~ ï¿þ¯à~à `O5+Ÿ‘qΧ´{'T(®˜Š‚n¤êÚç@–ÔVVqgj+ƒîeíìl”ý˜÷ØCÚæ>蜗Éy ì˜1ÓT "۞ϣ³ÄùÚ€è׫k1` åÆìÄkó_Yæ¿à\Ù^täØáä\èªO¬§JY6Y<ŽeÿB‘/6eKnë|¨H‚H»ì§µmšZ Û g•mI>ËãNŽ·Dܲ”wé<ÜæZÜNý°tîØÉN )sœh „óÐLüN`Í[Æ•²ßòXïE€Ln›ÆçEVRçßK· *žûÐQŸaÅùk›£+ƒ!wàµ}×ùûéܼçŠM'sS^)UN¥ïJܲ`"3©+ƒo.ÎõF:ƒ¸ÆK)e sÕ.Ìdû¿)¨–÷âi’ø%™;÷áš÷h ÐÜ뭮Шž—þXë €ãft–¹så÷:£Üuç¦$ ŸÏèµ®<“®¨.Šj5ôšõRÒô~¦¤µ(²9õÒd ¦^nÛ¬ àÜyp.ÎqzM”&7 þäðwÿ‹ÿ¿üË¿¼ú:uÖYg¯Ø:€ÞYg¯q{á…ðð#£|Ë:ðŽõ˜ÿÍÑvL¸¨Q1%¢ð¬xÈí¶Ô”q9 ² ÷ÉÙ`àÍL2ï™ÊùÞÓM`x@eA$f¢¹ –ÜYÅ qW\e}=ÙŽ »eo•ÅR>÷t‹ÚÎׄãã»ff~>úGÀx ÒË~ÌÌ/†´ül3$¸Wo–~Þ[6u¾øÙ¡çcâ€F] 8‹ÌˆËœ|®nÏ`Åø@ïÕ ø~t,ðu7ÖZý{E¾òo?&Øšð; ¾ä`¦@S0Ç 0ÓÖÖ6û c¶…9o8ÿÉø©aË{«,¹†Kßçnî€}L e…îè’yèÒÑm[ ¼ÕRø€Y'ùÈaÜ&7Ž@A'¡ uƒõf¦Ü÷«+ˆò\I^ž³ZªøpÁºUÌx:Gí)çûvê^'_- çÁ_¯¶iÆ2@På„üóE¼–Ì–žUU¾h˜ØJ+ÝIA!®_\t3€I³ÃÏAJ^_@ÓÅ ²XJ'H+Õ§ëbCSeRð(ÛÍ hC}àïuNÏ©MŒE¡Qþ¯³ߌÏÉ ;SÅ&Ûþ¹¹ëÕ_žA¯z±ïðüY£k2[kÖºày)ïé:óU}‘W§D^9§éˆ¹À{›s÷6…‘\#Ò2d`Gž¿§ÿù¥'ñì3ÏàâÅ‹w5Í;묳;· wÖÙkÜ~ê§~ ¿õ»¿úÇ׌šÛ1›ËÿÏg”w×;&G"9²Âátä@Sb ì2 çmƒøaÏ…•fkÀàˆªÃŽšÀ›É“º@Øb­7ŽýÏÖ¨ÈW-‚ Ì@ÏG4ÎÉ6m·U܃ÓÏ…íŠ8ÞÞ±* ¢“ʹç‹>aºá»ž}çâAEtçž‘ŸlREæñ‰‹TY~í¾9ø| ”#ö¡°wp9§U¦ÈÊÔ|M[.Šj)ÿ;ÚÆ­€dð o¥©¡ÂrSëbàÅ$ Z2ÒAšÊmk ¤•ÙeÑ7nËÁ Y%»Á!V+f‡•¯ÛʶڧiøB‚|ž%8wˆé©#ޱ䦯ÌdH+HÀn›-oµìÚìÞµ‚sRZ¢m>]Ýï²ÀmÁ…U! ΙçkÔ6I¿’AgkSºÅIÒ6Ýîív6<ˆj!¾gø~êŸãm`´OëÀ€’>(Ê÷Q•GÅÒà€êxôcJßãpô½Ì§åô|ÿÿcÅLä¤;„üãňú+d䢛 dew›Ño:¯Èb…  9 */³±Ã¼&1("UB ?NÎéæÚ¡_Zúµ¸S[¾_9HÖÅ"ÞÛ\ׄ ›Í{»ÎýšÌðU|€×>¡_YtTÞß,C_øç唞Æ??8 Š·Vä93Ý¢þÇ;ôÝÅÎuÇßšEðyBÏÀbûâyÊ °°c…¯¡a|¾9Ï ¹°ž‹mrÅw ÐÓô€ÛtT ˜ñø‰ÿàGñ¿ñ·¿—:묳—e@דּװ½ð x衇P½CßÎL±(B䊠M ë ã0¯Œlm µ…­s8ï ¥¡u²Ê¢<+3p•Æ Ñø\öÞ8‚hYá˜ÝràÁ¿gBEæ4_Ý;u,/Ì'Ä^/O~nËj€bLm³Y¿dÐØYd²Mmgëú¸èS6ÊñÙQ›¯Åße“Õáe` ÝW@£*´®ý9¬bá< Ì r@ ôÕ¬+¿P¨®è<Öò;9 ]”TJ0C‰ç[Z(d%—°ñ8Wí¯ÎrÖPØÄ~SÇ*ॶ²ª°5œ—iü~âat>ÏBšÉóJÖ>`¢|¿<îàÔ‹=©y¼éõ[bLÉíÊâ98M„sO…÷B…gyžÛòßSpί`ÎVɇ‹ñ²Óž¦˜ºý»’‰æX–írº 3ƒá^ñì#ƒºþݯy²•#Ÿky¿ ”b=`PcMµœ–‚‹k±-çî2d&sºáפA¬Í¡+uQ>£÷ò10Ù¡@$³Ÿ¼ÖÁƒR#Ô<“MôÓMQ˜³Šk +‹Š õ;Ü÷)Hc¯"šG°«l tV=ߟWFq@‘ï[¾ Æó–f«ýÝ?!0æ×~^g×ìÖÂ>âa[LÁì.µõýpð–|˜o*¶á玜|ø¾ µ>JqLž™Ok_]Œ¬rNv‘)Lœ®5óuXÒt.§ëȶO¶è\òêå ©2еß!e ̶ë»p°ÜÑ¿°¾âúÍýµâú&â~åúKµ7üúœJÝ90"zƒgp^Æï>ó9d_ýž}æ™nÛµÎ:û+² wÖÙkØÞûÞ÷âýÞo£þi‡|äPU&³°µ‚1NYhdpègÀ¢r­0)ú9°ð¤ImÜ(ô PZ¥Æ ‡Q¦q2×( PVÆ{ ­”±¨*­ªÌ`Lz1€.fpe•UpVÃ9DöZn—4{¦³$/à;ïH1@©”$Úf[éÔ±CÏl»B”¿²l3÷mLéíE”³l–¥Ž¡’¯wèšÛ²zÙÚ 0([@¾Œ8Fþ°íƒ<ϲYí8ßVJð(U?)¸ç–Ù¯«ë"³ÂŒoW'­­zø ·±/vž`q5k®ü.ø4Ç6ó¹,÷—Ž£dÐY.ï´gˆ˜D²2;ú2‚i'(ƒ ìÀ‡êÛž­ qýÒs§D›PÍMvNâkë€Öhwb©t•¯‡®…³^¯>ßÅ8¾ßV”o©ßèsÐàvãUt¬²¢=€á>90˜-bÑÊéF”rýKfá•4¬šúùäA«òÇÏ,ê|Ý+‹¶Ä¶’>Ï—ƒxÄ }¶Nãæ ]å·äÀãœS&‘‘Á °!•)ó•½MdËÃ5ƒ¿÷Tdøsq΀|à@ß7¬^a…N9¨æ½Ó°í Ñ5Ûòùæô¨Ú4ï+¬³~-­9ð î/žKpÿ0 é.u‚l±±Ý“ÄìÄ™B=€F¥sØäøäÖp vY «ÎóoÃð}t,‹Aì·±œ” ë蘇[ ÇV·8€ –Šàµµe&½löÛÈ=÷N¢”]Ë"h)›ŸJ¶ާŸü¹,"úNUþŽÿMi‰sHŸ"—fÆ“¾ŒÕ¹Ñ•a¾á%ñ#úœ+¾Ãú´­90Þõuv<ë>B"³¢ˆmLO¢Š¤ìÓû<^`º.‚7r×%ÖFjd¾l\¡çÚèIøe!:N+NèXF7“>Ìÿþâ=?ò£øð‡?¼bîuÖYg/×:€ÞYg¯Acö|ûûkœû^÷…uý™ÅzO¡¬~¦Ð3Ž7z ã’üµ±ÅÙîÆ¬úP A¿9qØj<Xã†ÁÕ“gG‡s‡Üø/k‡\Û¾Ù'æüâºÆóG5vGsGÒø¨,úƒ¹ÅN_ãÅcÃKÇ5¶ú3‡¾*ÔÖA+`ìÁÿÍ)®žØ€è…Ê/_™R˜×”8š;ì T<ÌF9Iûh ‹¾­ÅZAmG…ÂñT˺Ê('¿ÎQ/<°fÙ)WÀŸn‘ã7E`¬ÆbD’Ú“=*5ÙòùƒÌ†ñ_ âÖp,…e&e˜RÖšÏb®ìñú–˜:M ¿Ð{=vÔ|0ÁiÏô°("§E%y¡P9œt ±-okÄì].ªä¶Fm :ÜÜ?qZü­ýil;ÅÿÄ÷¥…>RVX°ùiÁ¼ÐäžV ?µmKa4¾.<ÞU×xok¶¥\t—l­ÕÂ|Àúµæ¹‘¹§, _u>–uÙüŒ¢ûÏ:R߈o`à1u/£õ*œGé7ÊÛÌZêsQS`¯Õ©I·k+*gÛ€=âjô·¢íÝXÛ¼PŽÖŒñŽ/¤¹ è­¡ªzïéÁa’Û^ÇÔ À×ð€µÇua1B­A†®èúr½²O÷6ï°ÁE<•CÈóf©½Ƽ &¯¡àZŸ~»ìØ+E}4]In™YœÄÚAæ®cÛº Bœç«÷[äþ.‚–Ö7X|¡Bâï„-Dy+4ß/ß¼nr0… æå¢Â|„¾ÄÎíŸnEŸ-º¢óÃkllk×ãsÀæe`¼‹òñÕY|Î €ýû€Ýç€ã=`ãºMbzt“æ×Ö‹ÀáàÌÓÀáyú.?kx](ÆÄðéù³} øã)²Ï^ÅÓO?xà•ßuÖY° wÖÙkÐÞÿþ÷ãCþ5¼ùWÞxÎàhæÃÍ2ôA(¥0Ê€µžÂ´6{ÄtŸi\[ì 4Æ%SkÉ. 0­Í¶û ×&»bÃû¹Â¬rЊ˜™I Œ<ƒ¾7и6±Øê)ìϜϼä0¯ ÜÏ-ú™Â  æ¹&f^ÁÁXÔ™¢ Â W8˜9Œ àxî0ȵwؽ³Ïã^Ëç[}ã¹E‘)”5©jïÈçFaZ:ô p8w‡s‹ž¡c* ”5ÉŒç bà«©¯<<_#§(-ÄÜÍ9·]÷ùðì<{ù>˳òþ±çó¦ãÊNcɹ²k1@À¯¹Ò3@N$os7_#ðx—Îtü¾ã®£“Yúªð\¹.åóÒÉeL°âÌbhT—2SγuFätWhn‡XÐJ2\\7@ùPxNûse¢ÃòŒ-ÂösF€›‘£ÊŽCPóÔ½SÞ?òùÆ¢Útå·üb‡X9Áô ›C:Š,äÿK·þj *°±ü´!óOÀsÿ¸ù^ÜlŽª…KHÛÈ=-¡y¬¯PÜk¥àBÐŒwÏ(dRî€ÊâvôAfhz¥°;PtZ ¼Îίi\:¦@ä•‹­>à™Â¢rXÔÔÇÑÜa£ µÐÙw‡‡3‡~¦PY‡ÚÁF¹ÂÍ ­¡—ŽjŸ*DëWÅÁEë×Èeg@iEÛ~­ëg¤RRþ£ç8íóÙçPXѯï Éý÷·^.;Ïð^¿æƒÆ'1èjJºk×)tæYúÎö Àá9*€XȲª˜À͇(ÖŽ0¹yÙÞ×PÅpx‹_Ÿâç~úçð| }q謳Î^–u½³Î^c6ŸÏqï…óØü¶C|ßßÉQ[àìHãúÄáìHáxNí¼vX/4Ö Ò=CÌóö@ãúØbg@ŽæfœÅA® @Žea€£1Ñ×N,ö†·f…´¢¼s ´ä¼Ÿ,äßœ8l nN,z%ÓÄ ³C|¼ Öý`FÎèþ”á“…ƒÑ$µ¯¼‚vV;Zá¸tæ¦ûF2è€sääW–@À¤"xZºÚ £c§1ÿZ©ðÙxA~^Q£yM,~eéÿÎ9LKÀ(éBS¡¼² XS÷bŽxØ ­GÎr°Y_QÝ;!Þï ¼ðòHvŒë ¡€ËC™É™nøížvcA+®b<‘ãÅ[qñ¡ã³"WRH(­¦-áBŽ{ŸÊ ”,¸&óÕ‡üWeuàFÛydêÁœ#C("µHÚr.4ƒQéÐsÞdk[ÄkÑ?òVßÈ›õ@VLæªÙRžê#ø"€¡P–(ôÇyä|Îäp%y®QÀs€¯s>CP˜+sâuEýHé²P$ä™…6$-×ÊAi%Hd`˜)%åZ)Àv³iÅïE°öïkhŸšbý”Õš€öZ®PJ]™ø”_£h=83¤uêÞ5ƒ[3RÖL|À°¬©ÑäÛè‘âew ñÒ‰Åî@áæÔa­ˆAÀÚ9L@/£õe£§p}ì°= Î §@%`ÃÂ:äšÖ©µ‚ÖõBáh×ãµ´\œU4¾£™ÃfŸ¾ÛÏh-1š‚Î Ëšš“…ÃFŸÖº¡pMÁB¯I AÑCàOÔ爐0Ê+jj=_(ÏDמ IDATTeŽ¢(±XäЦ¢5)ìåíçƒ<\q±ê¡‘£ˆ9lc±>fÎ¥¢†Yqž»Ü¯ÍbECZ.ò¨­IÀ9³â~V@ïA:o÷Æòô0^qê\܃ÉN ²Þ@ȃOŠ?†œl©ap.a»3ƒŽŽk”dñeêLs[ÐÇÏë‹WKŒn¾ó"±áÃýXtÇ58„š¯!߸‰Åþ=ì\Ãühk'˜/ z€«*Á`Ž£q³;\:rxp×âҸę¡ÂÑT£—™_Ö ƒ™ÅÎPã«û5Ù6¸:¶ÐÔŸÛÂåKWPÉ/uÖÙ˶ wÖÙkÌ>úÑâ‡ø‡ñŸü÷l\TxëYƒgök<¾—áË7*<°epõ„¤p@a6z ‡3Ê#éÄâ ›Oݪpÿ¦ÁÍ©Ãv,3è' `g påÄâþM§oÖ¸°npmb±ÙcœõùÃz\[œ[3xî°ÆÞ@ãò˜òÕÇ Ç¥‡0Y8d¸6&¦ÿ¥#ÊA¿6¶Þ%gµ²‡sú™ŒKÓÓÒ† Gß9£xÏÂE¸2çùª÷ÛªssS'¿“ôÃí} ù½ÌÍæÂZ¡r¹‰ÒU.Ärü…Œú4“ãM‹æ™JœSÛøŽÒ5TVÂä ôŒj|¦eÖƒc¶J†ÒÏès6ÚõA`Z;:^ì:âsb×}[¥<Þ°~N€Â¨ÆogšT7ƒL¡È(°Æ¿g4ýSÕ®h9]µØ p‰§uƒî]z]; ²±¤Þú•¾Ñg.(køŸ”ßÃ÷_;Rö°Yñ‚¿×P"$ƧغæwJKÁ@ãwÖààe?£õÌø'D9ˆ8. MÄ€ˆÚ§òx6×ÖJYÊË÷,°6lõ›û ¶p~]YÄ-ᤔ:HâµXë(óNi[.—Ï*ê§Ìü©?Ŷ„¡ƒeAoZñûå|{þMY0¯ql¾¹CCV\ñºÁrùµëÀÁä{Ï¡šnàÞÝ ÆÖ¥ŸT£~…ùÃ#;Oݬñír|öJ…Gw nM.¬k ªûûïæÔâ±]ƒÏ_«ñ½äøç_+ñŽûr|þZ6µW†[}J¡{˃ö\‰ï{¸À?}zï}¸ÀÿùÿÓXâ£ý(~è‡~håüדּÎîÎ:€ÞYg¯1{ç»þnü~ùw <¸ep4wxâŒÁŸùñó6H¢9ÌÉùuv /9¼qKãs7j|ÓžÁSG^&ª”ÖaÝ(Ü(. 4ž×xdÃà™£Ö4nÎFž‘.Ã0SxilñðŽÆŸ]®ñM÷dø³ËØ$9ê0'ÇsV‘ÓÉyêq­ÂÛÎåø£K%ß5øÊ­:äÉ×–¤±—ŽjÜ·ið…«¾å|†?y©Â›v) ±ç¥ü•¥\ûý™Åù5¯Ü¬ñ×îÉâù8¤ Â¤ŠKÝáŒÔÏÖ¸¸aðô­7)¸°Ù#VÌèc/ŸX\X7øÒ ÷m5ê[Ü8ʰ»±À•k¸pÏ.ßàÜöû'9ÝŠê« ‹Ç›k Ü<ìãž ®8³Qá`B)eE÷UaN¦; ¼´Ÿa{c[G6G%Ƴ £^²Ò¾Ž‚ìÔXï×88îãüî—4v×jŽsô j›iÀ˜ÎiœY³¸tì𖳟¿Zá­ç2œÌ-î]3¡ ln€iépnÎç7ŸÍðù«þú}>wµÆgÈW¸gMSÀ{ápÿ¦Á³û5Þ~oŽ?¿Râ»,ðÙ«Þ¸cpùØbk `¥ˆ_×xþÐâM{ÿïóþ7äø‹«5ÞrÖàoÿ›œÙü|ê“ÿlõdדּÎîÊ:€ÞYg¯!{ê©§ðè£â7ÿáëoö°_µÂÉØ8p¸:væ ÓÐãtô `ß{¸ló¸auŒø´D”ް««¸ k–^àÓ¨QûÎgØÔÀ¾ÃlS¡7u8Ψ›±EsΜÃz®puêpÿ†Æ“·j<´mðµªü~ml‘ût¾“°ÕSxf¿ÆÃ;Ÿ½\á-g3wu§¯ðÂõû¥5.nÐÖv{C¿¥ÝÉÜàË×Öú5Æ{5.]ÀÙ 8¸H¹‹ã„üóé&Ù£óÄäì_6®ÆbA̺sûbJŒüú5àøÊwï‘ür1 bM¼½®Z»Nûí)¿qýš¯ˆ?ŽLt>¥<ÌÑMàè°u‰¾³vÝçÒiÒi¥ûýúí+¤`à.¹©x‹ÜkÙœªŠ{æÌh cl” h9û4Ü«+0«èážišÒ*¥Ôj¸ô3%dæ ¶ €oö2­Âö‡¨^ô”žkRœÔ–ÞcÍÇÇ2vë(O[Á|Ûž2ð ®3_8’kYlõ©Ï~eó<ÞaFÇ6ô¥2­ȶ0"˜á 6ñTä¹ã×¼#ƒÑYEÀmS .Á9³æUíZƒlÎÑ9úWÎeøôå o9kðì¾Å™!ÕÒàùssêp~¤ñ•[0üôK¾éƒ§oYÜ»NraœÌ,Î uPB}öJ…GvIåtÏÈàæ” ìÕ–R„¶ú Ïî×xl7ÿXá­ç žºQãâ&­¡ƒŒé¡O xî°Æ#;8}û½¾p­Âý›TÔs˜S°uQ;lõHaõð–Æg¯ÖøæsŸ»Zᑃ—Ž)矜ãÿ†-Ï]«ñöó4î'Îd¸|b±ÝW˜ûx)¬ÚÖøò ‹¿vÎà³Wj<º«ñ⑨̘݋_¹iñă?¿Ráñ=ƒç)5k¼pК‚Ïû3‡½ÂS·j<¼mð—שßç-Î)ˆ›išÃ7§›…Âs‡o|éfG· ^:©qψê±EêIIu\^:¶¸oÃàé}ꟶU>A×o^Ó3eF5O®œP°øú„Æ6õê/ RKðÚ¿Ù'Ú¹5¿õéHãæ”®¯óê¥e\Ü0¸|Rã;ÏY¼i×àdápv¤QY‡L+_/Æâ­g2|õ ÆwÜŸã ×*<¶gpý„@6©ä”¯ÕBçù¹C‹ï¸/Ç—®WxâŒÁ×è6­t Œ2J3ÛË.• žžÖxÀhì+‡ýþïù©1žzê)<òÈ#謳Î^¹u½³Î^Cöþ÷¿¿ý~ /þÑ[ îÐ îR uAÃ]±Pg4Ü‘ƒ*`áoïLGØTÀM Üã÷®¦÷{ `î@@}`¤€CK€þ¦v4p‑o«Ažjå¨-ú}ÿ±ÖT¾ú‘Žýoï;T[ fê°ÈŒu¨Óç è[à†s8ŸkºKÎèf8^PþÚ—Ž-^:¶Äæ÷h»»¾Ñxöò(ƨg#b¼#Ϩ`²C údí0æj½¼Ç1¼Ä³*bA»õk”¿~¶ê“t5+ p[ ©˜Ñæêwý*õ;8ŽEòtó1Ç{T¥xÿ>éã˜Á\U Ïk(S¢®2E‰éx„Þú!¦;XßÞÇd<Äp4âú¹ƒ2¬#°xäáçéš_[ìÈlô"ê$<°Eio›tÎ3}hˆ½dTaw ðü‘ÅC[tÍÏ HÚË(ÇÚ:PºGIûK'l<TãÜHãÖÌa³ ð“„­ ÏŽ=´Mʘ·5ö§4ojGŽ~ßÿƃ[‡3‹ýØÔ8.éýYEK@?'ðP'h÷ìH‡ éoÜ1(-ɪG9¥®<ºmðå[5¾y7×j<¸®qeâ°•+ÔžÞΕÂAép¡Pt¿Ö =`cÌ{@>u( EQ‹hÍ9r ¼f30ÔÀÄœÎê|Û1€mEmÎÒÚuLÐ÷ýrÛ9€-ø~o9Tk zêPå ÊKákd0΀ØÏ€í¸ ‡ (Láƒl‡Žà°k4^˜[Ü?Ðxif±W(ì—ÒjŸF34 ×gG ÏY<¶mðÌa 7&”ë>¯‰éd”æs~@o|ñF…‹WN¨í8¨…(EèÞu/߬ñæ3þôr‰‡¶DoöÆ¥OûÑÀõ1"¿zP㉽ _¸N é×jŸCOËsaüŸ_£@Ác{þâJ‰G= <3Ò8ž;ÊKöyû÷Œ4¾vHlø_\¥ês”Ju8£@AfìîHQõð¶Æ_^¯ñæ=ƒg¬gЩ-@¹øÛ} Ü¿¡ñÅë5Ý¥ñž_׸>±ù-D‰=&p{f¨ð”Z<³_ãÂ:ñø¢€óš‚B7&÷¬)K*¯7Ÿ¥ÀÆ›ÓÚJuQëpT;첊}³£àƪ¯à.”ÜÄA챃ÞÕ°×,ô¦‚;t˜Z‡‹o?Á{~¼+×Yg¯–u½³Î^#V–%.Ü{ÿî;OðkïD'÷†¶<à>ïò†`ÅrXû ½çÛÞã÷†¢Ï ÈÉ­}¿cGýJÀ=R@逜N>0òÎò¦ÿ;RÎeÝn;TÀäD½£=÷N9ïE›Ú 6pèûŸ; ¸š5ªûã` 5u˜Š™Ã4ò;_€®*ÏfÞœZlö4^8ªqÏšÆåc Ý)ó7¯‰Q¹5¥Ï®œãpé˜òë÷§äÔÍ}¼L9/€õ×»“Øñ ë×Eþ¾ÑäPÄj\9!öŒWÜšRPÅÌ CcÙÒoO+àxa}ž=9‹—ž?²däp¯äž,nN,r­1[d>w>ÇbÚ§üöéfܺ©S…zÞÚMHµ‘Íc…óù°vÓçÅ}áµY”“ç躇|íóÉ£õcL§=¬˜– ý¢V¤AîeT(w 8¿¦puL€áhNÕ³I^K`b¼pXï+\=±¸oÓàê±Åù Ú®o«§°Õ÷yÓ:æO³¼wXó5ÈÈ Î45+4ÞLC°èʳÈôÛœë,Ÿœ53´žA^ÔTÍœßßo¹¯¹_£hœ™¦kίµŠ•Ц¸XnhÜ=C¬yÏ(Œ b©¹âzaèXö† ýœîíD€˜yŒ8ÁZÁß×â@÷²üËÆÀØòÿ]ó=ø5Åú$kÝ9ߟKÞs!¯@K‰èm¯9‡€ÿÏßKÛªäóô¤p?éw EëÓ1ȸ¥iMå`¨/Ð ºsÿÙØÑ:{â€5ÿ¾_C-§DW‹L¡(ö°…k14 S®øî€¹¥-énM‰qýê>.Ylú@T¦éTÏ*‡žÿg†/[<°E·û:0ó0¤¼äñ‚‚—ÏQ*Ò ‡´½æÁŒÖžçƒŒØß3C…KÇ_8¢1íÏhkKë¢2äxNÛv¾t\ã-J':¿FŒñzAùöÍÛcÏ.¿ä+öí Æ½ë7&´Í權éÇ hq`ó9Ÿâu}ì|z•¥©éh}a¦ûÌÖÝ;ò[|’jÊ—*ÁÔµæT/…‹rAÑI‹“.jZ/ö§ÂÖèí¾Â¤ô¿’ÆÈéÌÀŸ[#%ØÃ[¬{x›Žqo¨C½‡{ü¹d‡To½'à ‡5ÝÍp8'6ß9Àùëw4Î÷ŽÃE(Àp¸‚ž8h~Î/@·GA±«6Îë5ïhïÎ fæŸë‡~>X ŽÞ÷¡~ÿ“#\ºtyþ*lAØYgßàÖôÎ:{ا>õ)|×w}>ó!¾å!CÎâ‰#Çñ–g¤oY`Û3݈é|O@@øÈnöc tƒè„ž8`G[µåÐ%tƒÍà|Ë3é[š´íkŠúÊnfÐ'þ·÷°ç¿³&À?|Ûh|ÛÄ‚aÇ·ÝN.;Ä}ó¶¡#s&û-ÏYºéÙ*ìO-^:±È4µ-ŒÂ ‡•E¨Œ?­¨ÿ±gÕË8™+™£âty‰ù¬Ëû*à £²t݃îM¡ªýþum0,,¬¢cîyfz˜‘ž —ã-J€R%@¹ò߃]>÷¹Þ½ ^"NÀ67Ô¦ÑVîèäÌø"]5B±.6ÎÁ¶¢-ç8+1öø­(Ü¥€L‘$˜º ÅÚH5çz^®ÚÓ'é1êïc=ÔÌE–º  ³ÃÎsþ@jïTþžr.¾–ƪNü–ïó´ ìºø\Ü3 €ÎmVéV@X^>Àઌvpœö‚mÕò¶×|>.®7b ˆëR Zψ`JŽ—•I¼ÖÍý3 B¬%¿öýqÐuìhM]ø>T,¡9X2ñm9 ;÷ÏJW zoj3vM%ÏnË©S™Ø±‹ÇÁsÀѸܰ=Jâ°Í©»ŠûPkJ¯˜8‡µ< Њ˜n˜M+R‘Œæ-òxëOZè~%uRí€EE ÿßVïd×êÜø †#ÕÔáœQWO,ÝÉpsJ©“’Xó¾!5Îî@á`êp®§q«tØuÀ‰rÌzN묋×íºÂ_â´ KçrêèÜ9ÄÔ´›ØUTkæ¬Æo*º® MýØûW,¥ì çåÜûSï/ûkrDàS§‹gŽ¿ù#?_ýÕ_Eguöʬèuö0çÞøðƒx×¹Kø‡ÿÎÿÇÞ›ÆZ–]çaßÚçÜñͯ†®ê¹Ùl6›Í¡IZ©™¢É–,#€ ÉŸ–ì? D’8¢ d;ÄI Aì°äHP›q,GE‹&$qjNM6Ùdw³»ªºÆWoºó9{åÇÚßÙëœzM‘t—€»€W·î½ûî³ÏžÎúÖúÖÚ‰3Î4Ë1)E|˜’NÅ0†ç¥Ã`^ì±t”:d°Ûƒ»NÁ "+T(G!ÓÛù›¨Y ®%n5Z©ŸŒ#­5+£IQkÊN“ iK2P±ëIއ' 'E©.¶£'8Æ!ƒóèî­/@‘ðBR¨çéÌäÃäU9Yò¸L®Ò­Î+&²,÷ô²D5ðgTimyTyl\TÖ«:;1íˆ7m<û…˜·æÊq´nhè–Ç Åñ2b‘¼õ‡‹ˆR¯Z¾Ó¥í<­ìÿ³•6çÖß™™"yc’™…1¦ Þ…Óí¾Ýÿ…±Ñ7/nítwhÔÌa!%ŠëîÐ’½ußâ¾Û)p²4ï•eë70ºª{{–â³j<¾[àæ$ÓE¥éUmtÓÛSˬ娍°·¦çÆ–´o˜@·&à Þ8‰xr¿Àá<âÒ†µa«”Ô•Má…˜–À¸f0ˆöy©É±T¤Ô•âüÐò"\Ú4úðùqÀÁÌŽœ­Ì°2H@`d^ÁÇví˜Á –0l£gó$0îå÷ü8àÎDqidñÌ{A0‰Šaü¨€>ŸàÇ xhR´P:H“¨”¶—›5³g¸–VjõÍÒ>@jöõ˜©Ú\g¢9¹d|ÂÖëiL?·þbÚsVi}ÎÒÚ«Ó~ÆõÁ\·¦©,뢔é÷}Ék{NêöŒÖ^'™Ž¾p×a2L LD®—àˆ¾RÚnhöù*]›@¶reC§l•Ú½ÐÌTJá í¾sõN5Óæ¹{ƒG/}Æþm@ºd RÙù;éë;>ch`àXLRfÈœe‘ꟻ±Ø ù¾/¹l™6ÒQºöV°\(›©½×Ï4vð^Ò³ª¤ämQQ”‚UTÌ+Û'')ñçQ¢¬ßÐ6ðnëŽK„±ó¤ßšÆDËØ˜‘± Àfß¹]Þ2ºÿ;/”xõ0âÑM˱NÌìs¦{¼yvÇݳ¤õ´J cp€öô,œhZG©¯ ä²S5fÜ‘ë÷ždÃ[lèæú8Ñl¼é¹±Vä9εiºö,ÏŸ¿ñ |ìÆ#øÚ×_ÆZÖ²–=Yôµ¬åϼøâ‹xæ™gð‘o€Ÿy{r½Hú["jV× ÌI IDATNYá©‘àš½ Ìz¾@[ùBª‹tWNIèš¶Œ'jóIŠg[¦ïyN•P*·;Áø`mè§²âÊ%g‰§2ê=èÙ…Lu+d…›ŸS¡+’‚ÇÄR4&l&°>=É}2hršiÒ)0©Lâ9´ôØ”A0]šG§Š–ÎÊdo/㽤Ï+óà@.P‹sœVŠIÊJ\FS?MÇÀ­j+;Y)nM-þò8ÅO~ý F–0j{œ,R}+£¿À­iÄþ0àê©%¿»•²A'@O‚Å“îLŒ’y8W\Ú°3v™9¼Æ‹xæ¼íg/–xãÄâøO—–•b^%KäñèŽÑYŸÜ-påÄâøoM"¶!e7Éݹ&ªª%}+‹„¹¬J£¼Kö¦“b=†~¬¸l<Æšc£½ôÈÀªH`¯ß)Cظ?ïáåµÏz©¹¦ù`k¶Û߃ÿ iëÓÀYö,™j6ÐI*GZ;¯Ýû4ƒVqu°,ªï*ù”n¿xñ¿•Î_yFÙn™.­½‰ƒx“ßœym¹×»ŽÎo d°ÞO‹Ô_Àúp€ä­te¹W³S¸o“ù4–ìm¦Q{E!œ3ljæ<½Tû–ûíB͸2Mm"@Œšè&’=åÌ?ró~>”2•@# ýÇšC›˜k„ì)Þ÷*íß©½§š¢~®Ð¨0’l Šhf¹î<[7dM Ó@”VnY[¢¿²Î `ßL çÓ1o§+`ìö˜<òhaG—½|`ÉênL".nX"É>hñÀf!˜GÅf¬Æ‚ñ$æøí†.žÆš Zªv’Æo®9—ƒ7’Ä÷P²á‹k:¦±æï7ÄèçÓƒô½HKéuÆùøå³²B6È4à?•¥ñ,>ò ?û÷—xñÅñôÓOc-kYËw/k€¾–µü9_ýÕ_Åñ·þÜù[Œ£Ž®m@ªW–k˜‚HÅZaÊ;%EÔ(è<(™^šMÉž¢%Ú ½øùTÎz2Q³wj,É«”ÚCÏAj[Qó”ø±dƒz.•ÆÐí%o>iùCɘäTl'п/9YïGR»yôâï9¥±R,ƒ@ ×… œÖ"—摞.-Óï**B:Æ'¦LÒU“•ë<÷X‘ް Òœ{¬pÑÂÎÁ®².Uq4WTQqš>?IÞõã¹QäW©ÞUTÌ´9žŠmàðørš{æ-ßœY‡™QxÔvƒØ„lö$%=³XñQϨÚ>ÆšÑî¶þÃ)žæls šk_°céªÓ×U:G|¡ÙxE÷JÛàÜø7#ƒƒBrb°.Ho5:ý‡ÞR®×Ø)ËöVHJº+KÐÎv³ì ™1ÓÔŸ^'šòóé âš­åwO2§õÛ{Úzé!ä³eÒ¸‰êè²%R">K1õ¥ûhSI»Š¨WRµó¾)C¤¦™ZZ!S>Ë3ê#Ur”îy$ÙÓƒôæ<£Â7–DåGÛóÒC]¤Ò2‹=@~_º6ô¬¬`GÔ¤¸ûXJã¼ hê¢*¦pN5%q…f½·@Ê ,%3fû°Xy¡ƒ)’ýt–û¸'øÜMKØÆóx_?®qg¦˜WèUs£Üß5ûxnqš‹J›é$b@Z`õbÙÃ{Á¼ÓýÂhè=Áfϼí›ýœÄlÔHTuA“©I¯Õtšôjš#ØßÌëIÝR5•MsB"ÚÆ*Æ]2®³æ+òú¨Ám¬ "¯HëžØh‡Opîž%œïTê÷ÒË ¢—0/*=X,[ë½¶Û׿Ô\Ë®ô^æ |@O•««élmâî=±l¸ú÷<ÒÈ!Ïd‘6€óeäM~Ëë4€^Ú󿬽«;\ÞÞ-ÓÝ»¸rü®î|Î?ßgݽvÕ)Ï(ÓV©ò®!ƒeÊäð%_—ÿ¿§83oH×°@6–¯×—õàß÷[peØþ晆LŸ§Œ`\¤Í^¡Q¡pk’eùºåÓ¸î:>/@wl¸©V¸ä™•Æ=$¸WqJc8®±,w€­óÞsïiŒjš×¤ÀÞõ^CÈYÆ•îöŸûòœGp÷íçæªS©MlGÚó~æ7V8}àøØ¿ü£3.º–µ¬åÛ•5@_ËZîs9::¹sûøŸ~¢Àß|®ÌÄB²Ë¶®’•”³Q®v>ì#\‚¸d5§"R Çc2Þ“vfY¦²B…§×yOJ*•ÙŸ©¶ *T樘tÀÂÕÇ³Ó2c1¯8½9¶Tʾ+KE•qžL¶Ó¢Úk2RH;«ýŽ$‰d‚1Œá/ó¦x†U‰| †dÑ@ëŸYÑÕÉö±½Ü“f®8\» 18UëIªc™Ã'Yäö€¼Z§'xfÊY€\Ð.ë? î=Û´çhteY˜ ÏUî¬}OC'ž×÷kŸ\áßÿ5îÜ9ÀÎÎÖ²–µ|w²èkYË}.ýèGñá/üÍÞ±²gO`ŠðR³RĘÀ¡ÜôS±¡BCÅ‹‡”rŸ€è$v!+2@V<@¯ã =_Ètz* Mvim+ ^9¡ÒHÀÊØtXK…ƒY©|1™ÛE¥‚J&A“àmˆ£E"{qêš“êÑ3ÏóàYÎ+úLÏ4·|lÒ"ÙÎ&öTï5†ôDm'È"ñJy9éÇe?¤ñ°A;ÅÐb'—Àµ‰âÍ€oWxr¯À×jì§óÖû‰Â¸¨»C;éѯÖxb¯ÀiÄ›ó•šG@½°Ô+† ÜKÕöGø¼H|3º­¢ ^8·ÉKŽGM‘é£3Í^!‚Ö³hÝ@›"Èà´·yÎ0!!×E€{<ϼNÓnÍŠ0“:2ÁÛij7cÐÙwM†u´½à€[[È÷JƒO· œ¿\\-ð–þ3îÜY)~L(#W†×åû¾ûÎ×Áµ«ËÒ!“Nùn¾÷å(rFYÿAgI×xáT3Gµ}\ÓÑ•ëλJóØú°oÌd›8VÝ9E`Lê4W±…ß@%ý_³14nÆÇ{OwS/¬Õ¤>Ã}Ï=ˆÆ5Îý¸øÄ€@~Öð™äãëyÿ|öqïdØÒfª¨‡¼—óy@/»~uiàl¬8ÑD[Ò\Nhj_ó†“ÖNºx÷™Æ{ðF9u¯`ym{¼±šm´çšÿŒ}ÍùçÃ5mï·¤žÐièäg¬›û©¢1h½ðFwþÚýèGñ¡}kYËZ¾;)ÿì"kYËZþMÊg>ól <ýpª¤ D*¬’=NH BCý¦2˜>#Àã¼—”£ÍôÚŽB˜ÞfaIn)L9ÙOJrzÈg¬ïRA ³,cÖ½’Ae¯£h{è=àl¾‘Â{:MÊÕISäl·4}÷]L×Q÷^\ÙÚyâÊzC|` Øo}&uö €ÍÍŸ‘ûýS%€IÄ÷öp¨87(RÖdwOz}§À¥a¸©y ¨.9c‘ÚF//=wTHŠA¥• ¶èqëz 9wiÌ&©ìDóçžz äPVhÚžJ¸kxÒ ÐLGí!‡‹ôÒ¼`"'&°òk‰s¥ Œéýf\ùÒú ‹t/5,QÒ¸{PG:©ü^Ví·Í9åÜC¿§·™åJÉ1ÛÌ51p`00Dàì鯾ùï 'ãúêwÚ²¾^‚û.øöe¼§½“Þ¥|WÆ×Û-Ë.õM<Ë[Ý¥p{ Æ9$e¼Oßó˜L–mú?•! d³ÇœÿòžÇd¡uÞx£)öc’nÊÇ9sŸiî3}ÇõÑݯ|ÿp°ýýÎýs ø~,ÜoUó|ô¬uei”ðû#÷|/ C:ª­=Ç©Þ&$í8ö®‡|'¤ÄvÈ÷ßKƶ%Àh§Ûé0Gî‹Ú=wz’®¡}‚ï»rãÕO{ÈÆÊjžC’4÷D&t£1—l!š9Y¶›m½†íaÿ ®R=ÞKߎŸè 1`Ÿ*ðöKãAÏ|æ3k€¾–µükÈ ¯e-÷¹|ö³ŸÅs(ž.3ŒaÔè”! ˜ÇÖ+Ã| ÒÐõøÐ X¸ßy¥t;ØN±Ÿ>d²5>à+ÍôÆÝäá÷Tñè”)E;°L_šßUØî{øy®¬ºkð·]oT·^*I|­‘ ñ{*IöK´ACì(=lâ·òþvïÑ'Lâõ¹Ÿ©xäyzyÎò:y**4+*Ãmïþ¡ü==ÆœŸ¥¸.»¸¶=Ñ ¼'à^Ïn—víûÃFż[ÿ·òŠ×øÖíîÊYã×07 [ôp0ÐÀ1¤ÆöýZê\œõúv×H‰‘=þ+dP>cßi6Pø~ð´+²‘ªëïzÎémdŸ’N5@ž/ 8Gži#ëÝÎÜt¿í&pkæÚóÎ{Ðyÿ]Ï|põyº;ÐÞÛšº}ã\YÖÛõpŸÕÏÝ÷µûº2cdƒ €VÇxCR„cÉ ç E¿’|êÆm#ã²3?€{v÷F®íN“Îß|Ïú{ÒNFêëïöwËXòm.Nîï‰æ: œ¤z4³1z’¨ìz/ó†cýF¼×ÈâÈ@ž£K´÷wF#ìøR¸Îö¦FŸ¹¥:e¢Þ[¶yÎt_Ï’ôݤSÆSÚ‹ÎOºû>Ã(¸^»ý°‘²‡f=…à¹G |ö³Ÿýí[ËZÖògÉ ¯e-÷¹|æÓ‚¿øtR6è%`¦wëPsÉÊ¿ÒÙ§0%Èç«öÅÎRÝ ùÌSž-Ûx˜Ñ¦"òˆRÁýñf `%RVȱÀŒ¹îKŠGtà‰žÔÝ܉Àù`ÇÁlJ¦™zϳÒïª#Ž5+¥@6æ…à1A¤s²ÌYúŒÂ`d¯˜×¢¢ÉÖK9½ºì‡®bË:ƒ«çÍ®ï¯Ko(•ìF9× ˜Nµíâïy/!ULÃ)  ?Iö² këÄcvú’ÛÝÐ-%S_™µx®h¨±œ?ìs–el7­›ì+‚<ØnE6p.1¾\R[í6p,ü|fŒøÀ]·Qò¥ý›€ä%wãGO¸Ïä.šÀ9rþï=ìÎ.y‘>ãý1‹{DV²»^q¶ RÐö|YÙæýŸ0ÇÉK ?ǰHe9)âP²Ü׫n"Ýß ùúf”u¶—ûQp¿c=Œ9ï»úZLw/ÒŽo˜*h{Ú9üZâzòûC—©@é20ØÎ‚99ï#lDädad-\Ü|6 Ç{3ã·7JEÍ`ŠÞUö©¢}Ÿ]ñý@iúÁÝ+Ãt¢æ¹À6xã!ÛCv–Ÿ·þúgí•Lžæ×Ï©REô3³8=Üò±e ?bž’n~®›&çdÃ-Ÿi€ÍGïÅ^rì4{—=ðïî ~ÿg9ÿÊÿwóø8~ö_zUiÎÃãéÈ8ã¾åìΠ×R×.Ÿ›†'´íð–ÄÔ{ÿcŠñ©?ÁZÖ²–ï^Ö}-k¹åèè/}ýUüçihÊAÕS2æ0€;GYOŸ)ìaº‘”¼D]ÞKà—Jì iô|3ŽïX DÏœ— Ĭ|Îa4À#‚È ’ÎZ²"2W3À°H^»`×ì«‹;D;{ú 9ã:•zM»Je¤b‚¶bé#*)dl¦.Hò¾£éãÊée¦ÇÜgKöŠ}‹R G©ÖÿÜwöt U×Á3N¸öRýMÞiL/]ÞMäÓ}ÓƒO áïÍÏãØ•6 ¸z½—™Â0†¦l*7@ŽóîRÖœPÌ{ÐK佉eÉ( -¶ƒ.í6üw“Ëù²dìtûã,Ï`pe=µœâçõ›yÃc…ë7Öý5ýäk\ þšLdXÁÆz¥6™™éæ;ËrïðûÐr,C[?íƒ<Úúí½‹p…L©æÉï›øqäyë“_FMc)Ùèç?OᜡaŽ]Kf_+W–¡3ÞÈÁþãÜnr\Ä ˆ»ë… gÈ!Z]ö;C6ööО?Üٹ݇œT®l“XÙØË5é/ßûvù¹6Jsi,¹Ÿ äyê ì3²Xȃ8£²ä±ä½0Ñi3îúMÿM¢X>/ÙÞ÷`ÀßûýWp||Œíím¬e-kùÎe Ðײ–ûXžþyÀûÏ% G³ ÇÂÁšãaÃÛš í̾'/¼쌮1%d„œ“;íŠ){é ½“•±$ *Yñ)lÀ>ßS¢géFV@7xŠìõßàf´ëES&ÈIàî¦ï®$û˜•LïbV];«¬}þ+${ð¬äQÁÚ”÷.h²¢V {°éÉ_$ÍD5+Zsµû#Z_]‹ìRk@hxÓto·b;9ë•Ôî^úM|ÎnLYÉóñø¼GôʧXôTS±£rFO]7žu¦Ny㟶KàÙ(à®­¾ßÕýQù^¦ö,Òµ¦ ­€e¢€ŸTÉ©€Az%2½µ §9Ÿ gPo@„´½yô°]l¤Wæ9ÌNï ãÖ Š¹NÈs•Y° 8x´½UŒyí§~hŒ®mœ_\€Í{ü=mÿ¾Je ÍÆ ~ϘØZ³Œ€^EÞOÄÙçîó>ò~ä8óþ˜,öÁ™ ÌàÞš³ 5M·q·³†páÜ÷ûÇ™¯Šl¤ðs”ó1 »˜°‘ñËdB°ï’ǯ ØÇ qkÂ'Od}ê>ë¶—óÉ0iÀPÎ £`mÜL{ú–´éøÜCjdãã¦dv …ûEÐ^ >1©_WÞ“/’~½)ïUò5bš—+÷žûJ­9Ö>"{¶—î»&ûÖ“ŸO\ç~î+òó³‡lpæ÷¼Ÿmc‘g7uÃHX†×ë¾²ßXŽ{’é@;©¿´È~â¹éÌ¿ábä<â~Î>$›ƒó@ÃŒi~“úl–žCsdC€÷_´xþùçñ#?ò#XËZÖòË ¯e-÷±|þóŸÇ°ðöÀ•:YÅ“VCàB0àúH0 x!˜gsŒ¬`sGŒV¾)ÀKµ=ìojûÈ‚PCµ%ÀíÓ+³J+Ú竸¡Vïµ4È *{Óh×ù¦f¯:é›r“’’Bœ¡Ä ƒÃæwß“‚{ QIë*½¼¯îÿE3Eÿ®fϺ¯È üÝbŽ4–2åê§GçX3hç—TÂè­’džï©à¥[mú…¯LPEï˜Ï„Ì2šÀËòzÞhÁ¿Fyî=Ž <¯}ŸEîö1Çëßs=×wµB¨Vv~ûáJQ%€#DPF@Zÿ%¡Iù‡SÌ÷P¥=`ò@Šßy€1C¦ó o– Ú{¬Ëßcãtë h"ßÃŒ…°9d`â× ÇÉ{Íjä#%õ‰¯ŸåÑu}Éš³áKi{È ºK±=ƒ€ +O:òœ&Øa"¹.½cãâÊý94QÑÕØtf*Àˆ®º¢Èá ó´Wy£!“âù¸}E»ì*¤¶xÓãßí7Õœ{„TvÞ¿g\xí }¡óÊ{õ†ž!\H˜æõŹ^i> dK›Nr¦xŽÃ,ak!føöcè÷\¶k®¦#«ÍO5{Î;²ôîD;ºóZ´0µÓ˜Û›ögVƒ2àóŸÿü ¯e-ߥ¬úZÖrË•+WðÈnÏr©EPTј;¼QÙóO*àR^M@þXM1!+·Õµ›éšÀú Ȱ÷æ‘ß À5p1¯×ö@¦Ç›ÊäVÏv¾™Ê\öÀ?uàŽ1ÃÞt–4€öà÷Ÿ°ŠŠ¨VôöÑËÈÌÆ+Í^™J²§€tHžm;IF­9Ûûqê³»1ÇãûLùâþè™!½õ¬ã ¼’Ͼ˜## z2|B&²˜¡—,„ ÚÇ­`L… byq.&ãÍ–dÀ1JŠØ~°{»\d€Ñë(úTp=]µ³è,E†ÚÙú m`#ÈŠû¼£7tàô})¥¢—ò\(«Ú6ÉøÛ@„›‡K3°`{|Â:ÞG“9[Û†Š{èâšËœEÑf¿uéíg¼G$º´kO;+€g²ûü¼[>ü8pnò>=(æ5èý'³†mHˆýô[R×¹V7ÅÖëPÚÆÒÕÇ…\×C¹W;iæœk4cŒÇ–1£Ñ¶ÉR]UôÎÕe·Í†H4í1ñÇžu3©Çμðâæ£µ_Ú ÝK3ßܼào½7Õ oŽL^“ãM0;KkàN´±!°b?ðº4¶2H!yô'fÄtlŒ›#6S]cIûCÚ[yüåB-cú¤HÙÏ‚ë}ªVu¢ÀVHÀy¿§ÿÁ€èX²y¦Pw×ÛdŠoÌïÇ1}7OϹ¤0 t/þ ðijÛ™v.ؽ|rsllˆoùZ ŸÒ‡(v^!fð ?»o¦gU3HgÜ9Ÿë7¢µå®æp+Ÿènˆü y%»îÕÔ'15÷³Cäç¥æ²Il¥2 ÙëáÊ•+XËZÖòÝÉ ¯e-÷±\»v —w’†écúÒg0%â$iG1'jk(…I1«Óƒý »ƒhí[ pŸh®DÍ®MAz¾2p÷B•cÞ©ÎÕ ߨ€Gð™ x¬0°¾ÚÇ–1æýn´¸öÛI‰¡`–Ài”áQº7–½\¤ß$ecC²2½ SZ*€×jàѸ-™Þ]ͱãŒÇ>P£â_OŠÉµÚ>?Š&©æb×&ÃÀ<Í€• ƒF C“†c8µŸúžâ={dÝp'Ut”Ûiê;MŸ¾áØcÉÆ›C*u0Ån$ÀÍdèù|•÷)°›ú–YÊWšû¸Y¤1Ù–›de‘æÛ‰Z[xTÐ*µ—Gæä3»ƒäXsf;o¾ƒÕ½™”ýs½£˜Â0Õ6S¿]H÷µ'y^²o)Tæi¤ ’Â&I¦{Ó“E!0ö! 0J¯wÎ)ÿ þ{ÒÄ{é¯ Îý+מ ­lSšO~ðÉÙ¸¾ ü¶RýݳÍ7Ä>ã1Vü0£ÀÆ K`ÞúQšiüh(äÞTH6Öô@7Ái; àz¨‘çÅD!}A §_öì=áŠÜ×>–¸1r¤NãxûðˆÒýÆ;®+Žc!ùÈ­“˜sRpL–°õs¬ÀÉÐùPa{ÓÉ`–y%v‚Õ³l­3ôˆà8"çm`,´7$¯êqÚë Ü•°y¼-ÀÕ¸€[µÑãOcfcÁÝ3çÆ"µƒí i>n\(p¾°k<77“™ ý´žJûþƒ…íËû¡½ÐpÀ(Kd3 –Üo¹®™ Âð–çXÑÃT¦Û7È"ˆ±À>P«õËi_üÓ•Mú¬zä IDATÇ+»Ïo¨=ËÆc_Œ=öP|­²²7Ýýõa×ÖTï"­›éYøZmkë ¦ûÔLÙ¿æ×+iO~=ùk1V8nÇiŽÞHÖÑ;é¹p³AF$³õFÈÏæºÙKc=Hë¡gûÁåÍ ×®]ÃZÖ²–ïNDÕÄÖ²–µÜOòcúQcÆW®Íž–O •ìÒÚg`šÁÀMR?3L€ á¨ì1 öi/µ±¬l ðñÄìCe½Úö´,rb6¶•Â1ôŸûXJ^Ç{Åúƒû­ºïÉ(ðwMïát’f¥±fÜ.)ŸUœ‡l'“©˜5Åw–@û~WrûºIå<ó¢ézɆ ×Ôß›ÿ/CÊ-Å÷_—ê;¯Úù ÇÀK¬87ã#YQ¦—PЦÛtº‚«ƒ{Fè”Ùm#)ëŒ/ïKöä³>zVù¹¿'ƒÎ=Åß·ïþyƉ¢Í6àoüYÎ,ã?'@÷ ÇÎbH •á)‰µÑxfi ϸ^+A6¬Üžã zl§ ÙþÚ?\àÖîá>ú‡oÒȵ¬e-ßJÖôµ¬å>–kW_Çs—${Ѩ$ÒëJ*ߥ`¦ýã@ ëTž`ï8æ8õ¾ÇHòôDç±+ôzî¦÷¤µ3iÀœ{ˆSá&(h%½ÊôYßS¼r]ÁJ& £;¿!°>J÷{ªöÙÒ)Ù¬w®özS²½Dù<Œ¹M$DäDtÍTǾSN ØQocÉžñ©Þ«(jú‡Ì*ÊLÄÄrì'*àA0—6x¢Âéˆù~f œø}÷szt(>Ñœ/ï¯Ã÷g%$b¸C7¾›7Ázp U”ñµ;bsn6çùZº2üﶤÙWîúgäè€&•Sz©Øÿ4ðwþ$j|?¶ú8hvû´K•÷}ÍõVºÏɶQ´GÒùS^Ú´÷Ò}Ödƒ°]dÖCŽ9§ac ùض´Y@®ÖÉùäõöQ<`s_ãwÜ9‚e8ß|Üys| 2èñëŽÞó@cû;‚EŸ“ƒ×c¼öÑæýUØžórNŽÆ™ê|5y9¯¥n&& }ؾӇѭû0?°g…0ð^Wjl Ûó[‰ç\øùÙÌ¿½«Œ5¯` ¹?ÏÔBlØVŽéiš'¢ÙÀQ¹:TÑ„¢Ù ¹ÔfÀÿsá¸sLøy×øæ Nýt]zÝ›xydã'çP“[%˜¾m:±=¿^:Qœ–Àë7"žÜüo_¬ñáó‚ß}-ⱑà´Rì÷úÀF©¸LäXÌÃ=cK0€F^†Ì4{ÒÉ`®È›uÍÑvÆÇÔÃäµå÷DŠÂÚ°Të· 2ShŒl8Hóïò¶àóW_ÿ [ËZÖò­d Ðײ–ûXÞ¸~¾ULÉŠ\– ˜0)=&ôD3«6ã"Ë[À¼'–½/_X&øÆ»™ÚGI9¸’ˆiÌ`Ô À•SêéuìIVz½¾T4Ǥ‘¢8‚‹GDÎDË£¦–šÏs%}ÑŸÁ*h+ØŒ[÷gÇÕ¡‚Ì>‹®¯è¹gß6ÊhÑru&ÒؘУÝÍRÌ2^é‘ôOÔ¬œ2‘éªLÖæ•£‚Çú½RÎö+2@¤rÛsõyª£ÍÑX¤…îzòH…]i¦êŽ$ŸÀúYÏ©Ï$'øóIœ<óŒƒ(ÙÃìÔ¥ h{ì›ÏðKFp>JÀ„ì Ïôðñß•û?c¨=ÃăsÆÌR©õŸ!݇OÒæÁºŸ§qg‹'НÇÓí‘ÚÃë6ྌ[¬¯À»¿.o]+}^¥9¾Jsb‹‰×4>…fo¼¸>(:×¢hç½ ÞÓ dö…?#Ü'Ùã˜p¼èI®]½4¬qÍq tAnÓžÔ8öC—õ‘O˜©í¡ £G¶g´ò)Àg4ƒSÒ¯Çb¡Lô\&ú0Tâ¼R3T)r–kâý%{¯çjá' ŒCfa­ƒ•»ñfÏã~$hŽz\¥}x¡@?½›§{p)5ï#1ï“ÿq?`rÏUN®Ã4ÆKÉÞä f|f?MÔ€âQªóyyÎ3Æ~9>œŒî;dÜŠÖ§_«ìþo[ù*uÌjàöR1¯ß½1*€?¸­P/*z!ÙYÄN˜X¦Çx¨Ñ^‹4’×âú%Í ¶±a±¹}‰¯ü?Ÿ[4|©Îàû6Í Ió¥'ö ÞJc0–Ì\"Û`[ÌÍçó4Vœ?}Áƒ¥é/kYËZ¾;Yôµ¬å>•Éd‚ã“ .zöl[›@ Á _«ã™½„:§Ù#Û_“^=PjŽJÐrÏržM‡àœÖn.`ÿ…Z⟥æ$:=É}…)”›IùÚæÚJ´O&%bR5žù=IŠã ù•Š9ë]MœyvÜ7= ˜2ô2ùÐX²¡€ Ò8ýv')€û‰­°-Ù«NÐ0£»î$ÏÌå`Êà8˜‡©ñ4§º'š }dúý=4NA“Òš 6â¢S^•î ùœr±ÖMšF…¬‡Æ‹„ãÔ.†,ø~dB§ã¤•NÓ«7l9¶;šÄkA&½gB2¸±Í€ÊSÕ)¼z¯77’ɹd.ó8x0Hª­?æ¡­¶ M¼ø¹®®L3ŽpkLr3 r½—ߟÇîÁˆ"WÖCfS°.R¥9×û’A5¯Sƒ¨Ü\’±BƒŒOè㡽øz»RuúˆkýH:.CYèM$ ¡·ÔecºÂùéç>÷,‚%†°pÌ=k¤FK¤¨s¤G’Þݲ–û7ûœãçï•s h'¥òþïô"`í¥ùKеJ¿ jmØó?òi·¢µc‰"9Oçqsdš´Á`ctÒÌ\©ÓZ£¼›34ñ~Yûì£=‡I#W䤟·£çݘ 5‡šs‰œO{-iâ@ûù#p ÙàCÐ;C>ÊqªÖ' Àñíˆðú¡b¯Ü]ió¨=ZãªØ)÷íž?Rüй€¯Nïˆ-Õpa$FÐÙÛG/¤¹Ì|-n¯ã^½l<öÄöÚ=ɆQ2wX–FÈ-Iy?R½4¢úþ`h>2ˆzŸ—Ô¸7lÁ¬€æ(¶´¶.ïŸb:b<c-kYËw&k€¾–µÜ§róæMÀ;’cùð0ÏäFA#æl­’è…È@"À”œ‘X¬3=Ÿ}É Î+׊”= ¡×0˜‚ÙK s>Xâ*ž@”›IÑÛMÊÏP2eJ)©£VînžNIèÞR˜r¸r|àv¢–õþý=à5ð¶Â2è?Yd°ÌdmO–¼íÇ{–Ìí¥)¢—‚ç"õóD€‹0Eôñ`õ>TØýmKî#z( SÔØÇHßÓ;ÂdqìÎFÉ’ö«÷ôJç•cF…šýìÙ |U8–-R3x¤Ò>HuúøZRÏÝ‘[À›•åxJ±œ·L²¤ltºçºS/oªu ¶¿#œJ¬§w*AÌí~÷Þbíü&¸W=z¥›“ R2šXo´Ã¼0© ¤Sש}Þ’APÎìßžfÌñ= ”»*îñœ»â÷ ƒFþ˜×žu“zßKó‚GÂ1Ξçi0èö ¥µ.´ šÙK 9õa¦¶OSòˆH¾gÎz¯kW ŠÍqf’çfDfªíw»ên2|‰žmHz=uÞÕÊê{­nÓëùÿÙh3‡Õ·•öÆôœ Ʊ?Q3Lž"% “¼Ö¡¶_^(,!çã…1¸ ¶ßmH.¿#À7Ó>÷|ø@@`?•6ïÂ0ÓNšwéÜpl÷ÜAÞCÛãk˜·V»O2}4íqý4JϪ÷”–ìï­E>•e¡9§Ä‰Oà+5ð=; æ}¥=‡Î tªÄ¢Ð;r>@¿VC Ы5ä\HYó‘ó)¤}ÿRº§7nà‰'ž8{]­e-kySY'‰[ËZîSyñÅñÌ3Ïàãÿí?¸Œ¾GÊ!ÏFeÆvEö|köú1K6½Œe#@¥G–±ÁôBõ%[ÛoëÓ6˜ ’²€ š¨ù8Zïaõâé¢] ÒÐYÓ+ãÏ=-™‘™fåÈôuÒ9YÈ “>ù ã>yäÓ0Ý“$õÅÑHÑŽÝ%8êöC¨ðÙ[Æöžhþ?ÛÌ# Ø‡>ûøY"ÈÞ' {F8Æücfêʽg?ñ•¬zSHù§!ˆ}J–@8£Þ†v©¹ÇFÄ݇Ÿ? EÚS¸Ï[^r¹÷÷À½óÐ'c?3Qéã¤Ö¶ÎÅNeJׇÙÏ~N¯©Ÿûï³<îg\O_g(¤óÞ}nÖ1N¿çúaL;ç¦_/@ž«>#:ûÕ¯U ¯Íµåχg x¿Þ›Oêå™þ³BÚ×níCgÌ‹îÚôýïǩ녥±‚É;È€o’úpêæ׆ϰîÁ4ÙAdc©§¯.Œyí°êêg{i Q̯WÖÍùP#…Á€/ ÜWši×éÌ¢¬mº?ç ÷ æIð9HÈÜà¼îö;Köqêß&æ¾ÒÔw~¾$p ¸vÐ+L#É2ÕÏ4v&p¬P+êÚðµ¦:$$;w¨#Pn ´d3µ…G ²ïR¨ìôÆÃGd#4Ÿ'{öÔ°çûYh1‹ ¦w"ðDaFާ cN<2K‚ì£ób!Oàë5ðîx%bqYPÜŒ8é²dª8ÙÎ_Q\}2à-«põ/öðÀT¨ DïK5ŠÝ¤OÌx¸ÀÇkþ•^|ñE<ýôÓXËZÖòÉÚƒ¾–µÜ§RU¦é–…œÔŒÊ •¢­`Gîl¦/øàç0Šü#w´“”MI`I²§ °2ƒôʳ½Õ¥‹úøæ€L9mÚá¾ë G!øˆIéóG5I}’âÃl³52 –ý&¦Z²"GE†÷AÐKeU]èyQöòûn[y4ˆPqŽš•- OjlÙÓ]ÃÆ‘ññì?O9§ÇÎÂnöÅ”;&±bÂ)Oà€·ßä@ÛÃüèü† ßß[p¿÷õÎëýf.±˜û̃ºÂõ7Ë ÚóSÓ?ôB ÚŠypŸñw¤Á/€#À{F0PÁhžQóIÌ~+á|3Ôˆ~ù5ذ.ejß@²!„ó:"‘ȾãYÌmŒ'gyÅx_ñy~dã… ÒƒÇnb«¢S&¤‚*÷ÞgCFÞðGÖ*ãÊöÎ(ëëõsÊ‹ŸC­qJk‡†²U¼ÑsˆëšùšìßšÛ[K¦Æ7¹=<_¾”{Y{2N¼7œß×Úž÷” ù9Á¾—Tžlž9Î}b(yí²_Wšcê¶×øÜp}EÖN?nÌ;ÑÕJí¾d_0ħIˆ"5JŸuO8à<ð{4±·4ÏŸ(?ä= ‚*P îÑn¯ ©oË4Bïø ÝY)<ŠŒƒ Æ  ñ—áÞpʽÈYüûrl{)ÀVaÿkz},X_ïHód—ì¥k¾«´×gP§£€ÙJ±¬U-¨¢âp[Ð ÀË?ÝC¯îþLãž@~<@fŠ0Gc°*ßf“ŠzÌZÖ²–ïLŠ_þå_þåÓXËZÖr¯\»v ¿þ뿎÷§zø˜Ö؈À§ç5nÕŠ»x~Yc$‚zZái ø‡±ÂSð¥MÅ œì V=  ¦;‚a³ªDà…CöDa@d/dð¶ì!Ë3Y/†#—”ŸÍ½&T"·Ä”¥~£òGÅ¡¡§$…–Ùáy¾*ÖbÀ¡V;:n¢Fï[ÀÞGØ÷ôVï&Ê3Ô_9³}Lm¤’t>ؽ<•úá{K{}oiŸ?YX}»<] ÎÅ_(Q\8ü`ƒÛwž.PÔÀñ…€Õ˜ã=TÀ•Ë‚­)ðÅËÀùð•`ß(æp€k=ëš/õç+ÁÇ6"Yüñ†b³¼´¡XDÅÝðƦ)€_½ìŸz›àÁcàëÏŒfŠkb!8Ùí 6†«·wËï/Q¾íÞN5+kbã$°¾š«Q#x8˜‘ãBš cIñ¨bó`â€úY€´ >»”g ½rþ† +ÒLQɲò\º²è”õžRt^YžÊz„Cff,°ˆÕC–C-.†UózÐN½o&,ÓÍàà î1V½ÉÛ×4F®ÛFÃÿ¶$ƒ&Mß÷ÄÚ@ÝÚ±¯[Ýw\R<`|³1ñ†¾÷^v?þ¼¾âÞqôt÷7+ÛPØÝɽh}_Còý(rN‚UÎ!Æ«ÓsLïråêhÆ^ÚÆ*qu³ÝÍÜwŸù²@îo¿æ8·{Èç„Óc[HÞ{bÏRlýŸ(ðxam~°°ûÙKû@/í3ØÞ3¶/PB0y8`u^° î>PÌ/¿=`ÿ.ðÙ· ö§À/¥¾9R ÇðÒªà†Å€ßéÕx¿¯5†…à‹uÄ€[µâ UÄ0>rZámÃÿó­žÝ ø­Û+l÷>q·Âݸ:W<T£ ‚ßþæ o?Wàï|r‰ïy¨Àÿò|…ÝQÀÇ^‰8˜¯ ^¸a[Ìï½<¶üýO Þû ðŸ‹xp[ðÑ—ó)ðÈ/DDÿçç—ø£ß«ñ‹¿ø‹¸téÖ²–µ|g²ö ¯e-÷¹üþQ…ÿkYáÕ/¬"΂ݞà•JñÃCÅo®pq»ÿµ®ðp/à…à8½K‚ý0.zC°1zo+  …¼»´£vÞž°M1…Œ^Î;j1…+X¬Û<µ<õS ñ‚MÔ%DS¦HÅ›!{ñ½§Þ žÁ>JõŒ%'£RyNIÚõ£É0ðhÈÀ€TQÒ ŒóhºË¡ ØŽ™vøS}{ýÙ¾)‡Û°¶mšº`Í“pú“=¨¯}_Ze­¨#°ŠŠU´¬¾üìã*T?9¯vòÐ*¢ª-ƒoí³:Ú­üö¦•ýg£hTÉ ¦•H“3­W ~÷9ûì+ßS`PŸþ¶ûï±€‹¶µÏ`ŽªÁ»Jë‡íÙÔ”IzÅ#2%÷Ѥù?•^—©=ðyGÑÏݤ_4ʼ)ÈqâA¿×NÙ®‡4 H‹º÷A²'Ê—%Ðáã½â>³6)ÉŒžiŽÉfhEå~{–A¢Fö¼S<ˆò¿©;õøûï²è9# –ó™À°µSÀæ±?«ü ¹µ!™æ•ÅØ2Ô?*pš<Å›}iòåõ؆`\Ã@éÜþ/¶ß÷wéÀXc ÐÜÚéczç·ïû¬Ó½-éÎ9y“¿®ñæž:äÏþMë¥ý¾ÉiÑ™‡Hí_ÁæÙ\í¤€ëµÍµÃh};Ó­íþðýÐ2t±M’=à©ÏwżãôÚ6^^Øÿ÷Ä+cöGE^{^ç™ÂÚôTaïß·?k¦¡¯xgºß'‹¼¦|Î…ˆ|]ÆÎÁÚûòÙÛ™AÕðÃ¥ÝÛJv$n ï 웥¬?Ù¹§³æÔϬmÿÎ Ï ÿ<ñÇ þâ ê¿>Äl¡ÿl‰Ã=AŒÀònćöÇÀ—ÞЛG|òiAˆÀ'.+ž:T¼Úúªè­€×ŠwNÿ÷0b¬‚ß(*lÖÀ?Š5®ÖÀW5v“Wÿµâ«ˆß<^a\ þ»ë \þÇ+K\[)>~Pã¡ {¿2‹øþ %þÁËKô{ÿå'æxt¯Àßþø ×N€?üðð¶`Y Þ8ž{Pðÿ¼QüŸÝSü÷ŸˆXT‚þµˆwï§u…ƒyÄsþѕ帖µ¬åÛ•u úZÖrŸÊ—¾ô%¼ë]ïBñ ‚xq› ,kEEY lËJñ®‹%îÌ"Þu±Ä孀;ీ'v löãxp;`#æÞÐ ˜b¸pJã(LáÞKV¶+¦,’ZMeª†¼LZ¨)S>Ã8UE¢ÞjÎ6»-íxKµäsÄéý¸kLQ‘g» $0.h{ð ä˜Îͤxï%¯Ì^Rf· b¨‚e Ì <Ÿ.QEe {UÎíuº²²ó•5u²Š­ï ä) íPÕÉ‘YJ§ Ž6{–ˆho$-lüøn§ö ôSÿ…Ø8V;·Rž ¿@;™\ óÑ ÂÏ<åÚ‚¯.u߃—®|«Ï9æ±S–žEGLÀNE|Þˆ0!“§’’›l’±²Ú›¦yº¥™šcvj†ŽfŽ5ç·k¦€§®óqåQM [Õê ³»à½r pÑË>e?4€%}Gª*×K¿:K¸¾'Ðt¹ö-‘Y/É8tµEZ˜®óJÑ ‚Ai÷°¬­ýeH =Û¶z‚a!йB§Ö/JÃF—Y!éÞ @‚@z€Œe€Bço«3À¨§K{@z–tç[×PpÖ\í¾àÌdtgý&¸ï}ÆY uqüj‰¼*XÒÏv²ÃBÍx:K}áãÝiÈøVýÑ2Œ mÌâ:‰…Âô™24ôlKû~¹ž¼!Éï‚¶áªÙCÒß©f¶"ƒt ëeû€l„ã:çÚf?ÈL*ßÏÚûA´g$ð¹äÅ9ø]w>ûç :Çvhm™‡”å}®˜­ÓJ›õu8·çÆáÜŒ³‡óˆJmý­¢=_uÛè{º´£ÛæUzÕö ZÕù´¨ò³dªÖfš¬Ò:ŽjF媪(@L›o,íÿ*€€ºW±÷¡êèÍjKû]±² N\QàŸâK_úž}öY¬e-kùÎdíA_ËZîS)K[žõ|ħ³ÓˆŠ*BŠˆi¥Àg¯¯ðèN~s‰¿üT|¥Æ>ÖÃóoTõoÝ+ðòa_|ïç3üÚ‡7ð??Åôì¿óÍ%>8*pu©(jàROð…yŸ•øõ ÿúø¯d…ÿ=üãAîx­RŒ°ß øÆ*âÇþpUãçFþ_©ñ£¡Àõ àü›‚"£ó’ì«yåïDóÖO€Y„–¢UÀMQ·Šx.|¹Žx¬\Š­ L*Å…™à¥‘âýW€O>-øà§">õlÀÛ¿qåAÁî°,€ù8wWñõ‡ï½ür[ñO6#~¨.ð¹iÄ#eÀѱbÞÜ øäõ |¸‡üâåé>þÉ×xߥ¾|«ÂùqÀÑ"bQ[Á«‡5Þº_àO¯VøžKüñ• Oí¼~1.Ó•Ú‘äp²v‚ÛÓˆ ‚ƒ™b£'8œ+¶œJÉ˽¡àtQHÀ¬Vl Ì¢bPXlે5TïLôX§»‚íÎC5Sî«„™•™G×1¶˜Š, TH=@s …ßw=èß8ï–óÇI±.„z’âIþšô:…ÿ´V(“•6 på˜ÅØè d[°= †i’ çJ:AvLàwºÒ¦îUmw£oG.íóÞŽ;IFÎê;ÅÆè€ÏÊ{ܲzEÂE‘ʼ)ö˜Ý‹fG¥4Þò`PXù: m¿0p°ŠæAÓJq²T¬j´Ø#:È¡+ý`F§~¡õì:oÙÍõ˜Ü±1ª¥:bEÚô€6;æ¬Kv㮻ﻟ5G5µÅSÐ/îãAw—Ïxþ¾››ÞCð:ç[bÙ·&®nöCT«“ÇÆEäÄ~g ç \h°¢±RSÁ 9»xl¤åœK_l‡;‚‹·_» xöuÅ‹ ¿«¸¾%Ø9UL‚ ÖÀæ\qcKðÖŠWÞ]àퟪñé÷xæ ·Ÿ+°s%âp,¨è+n\¼åÅgž øÑOGüÎ;€Ÿx]ðåG€ÇŽƒ‘a튀“=àÒp牀Ë/’'gÊ IDATGÌÞ]bør y,äSMØÇ€NõV@y¤–$í@ÓùÞiOabÄi_ ÀQÌϪ`}Â8÷d ¨—ŠÕXЯ€«ˆ6®EÜœF¬j`ºN—f¼½9‚듈­¾à`®(p03À>¯ È1?(wæVöîjyN’¡ç$šá‡9¼wÈY“n™þôÊ ï°Ä«‡5ΖµÍé #Á«wjŒxb/à«× <¼WãúÝ!Æ£%f³>¢˜¥r¾(Qô–Xœì ·}‹;!n_ŽB=¾ Ìv !m¢«1´œÓ=`ã8¾lÞ&ç€Á)°ÙƒVÔ,½åÂ<æÃc`¾ €åL€ºoeC²˜J ¼º~ë/¿üòú˜µµ¬å»5@_ËZîS™L&ØÜÜ~âbŠé+íÁI7foYPŽbÃáu°=RTQ1î`$`PóÀ.£½ÖØ–ÑMN¢2VQ1LÞÞ­¾àx¡ØìÕ{˜(„ý JónöÃÒ€øþX°=°¿q)Ø ¶F…%BínZ)æÌ›¨ãó•b™èáµfÀ4¯ìó@w;YPAìÿL¼ 8ÆbrRV‰jN>g\ygu4ETSk5@§j g³kí™®2HíŽv?ôÐ$’gû(«ÆH'6`%Ml0ÿ†¥`³oý½JÊoLÞÜK›‚·ì÷Ç SšKüÀ#%nL"Ý)0›*v+ÃÉÊŒ/7æŠÇ‚+Cà¡;Šãs‚SŲ/(OÕtþ¾ 8VT½×#ŽŸì|=âøÑ€ñMÅl[ЛKê0œ)·€ ‡À+瀷/íÌ·úÀf ¬D±êµàz©xR¾\D¼gð•AÄp{"æÜW ¥½]Cð…ñ}Ã;­ðþí_œ˜áé0eÊÞê ^™FðPi¶™ƒhÆ«C=ãè>ͱÚÌÞä@“Ÿ öUAš´&¯­Ñ³ç5)׊y•÷§éÊ 3§K3pÌ"T³J›çȨvÁw”…ÝËñBqnPˆõå²F#ÁÉ2âtiûð²ŽÆxYÔhÀv‘öx5fR¥Ø†äN—Š­ÝCÙ«háAG‹ˆó£€Û35£ÍÂ@p9¯lÌÇ=3”nô·gö›ƒ™¢_X›f”[VŠ2wçŠ~)¸~±Ù³×€»3[+§É¨PÜ™š÷ú$Zý§‚a/b¶(QiDUT5¡ˆ«b9f;@j9TæéFÌ^lÀ@´¨Yb‹¥m~Íå@Õ³zê~àÅÒ¼á!YC4ʺ P.“2V ?³6ôæöy±ŠÊ~ÿò àw1™L0±–µ¬å;“5@_ËZîcÙÜÚÄä]#à½ûöð؃S’6*{8í<:VC”Ãb]`Ø@¨P@°5Œ8\Ïœ7ïÒ÷=ÔÃK5ß ¸;‹Øêˆ˜B´?2ÀÛÎxáV…w_4Ôc;fÛSÜEÌSt0‹xߥŸ¾Vá¯>;À¾ºÂÏ>ÝÇgߨñ¾Ë޶Í<¹Wà÷_^⯿gˆÿð÷&ø?þí-üåß>Æßýñ ü½OÎñƒ–xí(b²T¼e¯À?ÿú¿ðÞ!þ›5ÅßþðþÓ?˜ào¼oˆùÍž>—ë=?¸zRãíç |ã°Æ;/”xõ0âñÝ' S€N‚**Â+Ű4p”Þ9¾¼Š¦L« ¦«ˆ"HsM*¦\šò8«ì³yeôôeeŠát•âcW —û, QÔcŠ î%pNå2ˆ†2\0¼lõçF‚Q¯ýEûCÁFß@L!Xz…)¤ ͈ÒOà×í‚E«Ë€¡)ÊOKÇ6X%*õ*9ÎØ²ªs´*}£Ÿ)ÕÎ(Øoòç’«ý‡*ªõ7ǰvIùÊ Fņµ/‚Fžt Ïð}TEH×f(A‘@aHfóÛ=ÍkËIò,Ÿ®‹¥`YXT€ÆÂÀnP««·Â°§  ÛØŽKó0·rHn×*z¯¶4÷  ‡à}ˆ›'š¾'Õ\ÄÆ¶Ný`뺎 »Wþ®îxÄs’Cmb_kÕÆEÏ‘Ç\k©ÙM3Îe3†ˆ#Ä·_’$µ?ð”¨÷ {?,sþгÁ+" wíÚÆ2¿æh ±õ™«Ú€%‹d "—už‹ÊÀí<}?O }Y#ý?¿ï%%0.{#ÁÅ€‹‚'v <°ðÖÝ—6Š©…•PD—ã@r7ùzhë„ë´ª f8Hsù`- ½3$,jmÀ´¤}àx¡¸;ÏßÓe6TÍÓšؘT ‘à{,ñ…>ðpwf½`sïd©Ø^?®ñèv—jŒ{æUÞì Þ8Ñ5ªyºO—н¡àÖTñÐVÀYÄÎ `^)†½ÔŸÑúñt©ØÜžF<¼]àÚIK'K`gh£]ˆÊl¼vTã±Ýß<ªqapgf†‡ymýSp¼´gɵ“ˆýQÀK5¶û‚«'E0È4Ÿæ£¢Àã!Fã)f@GwÉy œ圃¶Ø4úùé9`ópø°u˜ì@^¦ôgÀ| ž§çÑ!°Ø2½!¦;Ä`zCH`º\±„U¦²‹8o6Ždñ!p/RòPgj»Äü÷…Ø|aŠ“ã¬e-kùÎe Ðײ–ûXž|ê­xyãðóƒÐ?%þì½{ÌuÙYö[kï}Î{û.3ã{.žÏÄfˆ‰À $ qB ›RU!P©Mmjp¡´ ¤*Tµ"RJ%Î=¥$$¥´¤ AJ¸¢¢D€lãñÏø2ö\¾ëûžËÞ{­þñ<¿µžµÎ>ï÷~ã1šqö#}ßyßó®³ÏÚk¯½öó{~¿çYâ]µkù@£Y°N¥iÍej~LÒÖNó—Êœw*›mœÙ™È9„(ùlC¤ƒ%,ãbbôb:a}8òxö4àK®4øÌí LFÀ²gø¦Ê¶Ÿ½pÿ‘dz·Cú¬÷Á·éƒ8J›!âŠ~æµÇŸ= xøRƒçWW—1xô#ðºK{1àÏ|‰Ã/>Õ㯼y‰ñÔßô¥þàs#½â‹ñ†{üëψ\öç>¸Áw¼y‰_üð_ÿH‡¦ÜÿÆ_öøäÍïxãÿèßnðß½íÿø÷7ø+o^âƒ/ŒxÃEãðÜYÀW½®Åû?Ñã;þäëwÖxïWà§ÿÍ_ûP‡]ñšCæ×V_öøÄ€7ÜÓàwŸðï=Üâ÷?7âáKÏ Ð<ë%0q²pøÜ©¤Y}àù_•Ô›…wøèµcÔ@Ñnmd=_m\: ¸µ•ì%8¤Y ¢ ¸¹xTAôC—×.Éá^ÆðÞC‡Vž4xaðÀ‘Çí¬UdÛ½—ëv²¯9røìíˆûØßs(lùa+kÈéVÒÄ>q=àM¯ñø½gGü©×5øàs!–zªÒì7¹ìñ{Ÿð%W|ø…——7n/c@—XmZx0ÞºWq×ޝ7_+@|}YAt›™ìÍ‘0çg÷‡7åuy ØÊ3>t¾ý ,øâL@ùâ6°=º0,Ô?ˆš[sÑ· ¬øæXþØ [Nt`Ayôè“¿§}“ÿ÷Sx|}/>ò᧦nøÙf›í6ôÙf{ÛŸ{ÛŸÃÿýÉ |Ó# ¶•=çÃ9éöÕE‰€íåŸs^àÄÁtNœ¦c!ý¤Ä¯q"á;lÖcÄ H·LI2Ð"!Gh¥Õ …5•Ÿ×ƒB»©ÎÖµÛí˜e‘½²T›!bé=ζ‹€õé!šÅãú±ÙØ¡³1ˆÃ³º \~¸özàÊg€ÓûÐÞBìÐø× Û#,On`uý5¸òÀgpý“ᾇ?‰Ï=€ã+×±Y¡iF¸fD¿>ÄÑåë¸ù⽸÷µŸÅsÏ<†+=ƒÛϽW®¡?½„ÐôH¶‡hO®aûâëÐÜÿq Ÿ}¸ÿ£ÀµG€ã„Ùh¹c'ŽÔê pòvM‚£ÏŸÉ×Ì'¿ïÐãß|nÀýÇÏÜñðe_“+V«%B €G 4Žë#‹ÛcU}-Ñ,¶ˆÝI¨ ;hË{ð°Íj¨ê˜”¢®Ø ²¶½p&,6Ÿ-¼·»Fîû‡N<žzaÄ—ßßâ÷Ÿ$ÈxC”a›^¹Gà ·=^w9â>{„7>|O=s=pŠç_<Æ¥£Û¾Icwãæ!®\½…çž½Wü®?ýš{ŸÆøü— .o ð­œãé½ÂlßxXîÿë‰úmuU‹´±`X·[Íû¾©¬ø •»ŸJ¸‹òoì`¯.I^yº—•%÷9ˆh ­È¾È¿·¹Ÿ-Çï{³b+ß=vòY@‰€ÈüÊÇñçûJüê¯üê®c3Ûl³ÝÑf€>Ûl¯`ûÎïüNüÓ÷ÿ"Æ¿ôzy¸·#?$šÝ­$áwq[óÁVâw+€¥›A˜öŰ=DwxŠ~»ÄÁr‹~ðhš€€¨’O‡! ˆ^õÂB0oÕg]X9‡~”ŠÌ·7Âz^;mpr0âlã±ì"†ÑÁ;i?Œ‹.bµõ89qs#ÌÃÙÆã ‹èGqÌ<¶ƒSGþ@"÷«+â l €|©§ç³ãCçb} Xʹ¦Ü:¯ûWE/cØ ØM¬ÄUù½?Ê[Ç-èÅѹ}¯°gWoô:4Ò?*+¡ÒÁå©ÙЦ“ï…ËA“Ðæœ¿v#Žk·Òk½U K¥ÄöX®ã°Pp¾”J¼lÛnäz7½|ww&¯Ç×àB‹E7 ¸€ÃÖa¡Ê‰K 8öcL9¸‡m–/L1^h G~QXØ$˲ß;%ÿ’&óuÏú˜ÒJ†ƒÖ$ØóHj˜‹oeÊF®L)·­?@x›€&òÀ×C­Á|g㤘aãòvvÜû[O-íg¿"®¯D qûæ bô"A2¯ú¹¶¡ÉZbô>Ž@w 4£–Ú-Üân±†ï6ÀÁ-xÑv²Ÿðñ²ìƒ<7$Fmx­ç˜r¾#t—.3äLÙÈv`T°~‹[c Î;¹ï†¥Ü£î‰E°>,Ql×ôÜ)õž³:s`ô LšõÏ™ ÇvzÒVÙmÍ1@àõ\Ün›¢-‘ޝj$û!gD! ¦’ û!) ú@‚:žg!…‡e*0<‡ØS‚ è8píp‹nDÛH0´mT1¡Û3´Rd£õ$Øš¨‹FÖxg{ýåyqÄkŽ„un=p¶u‚Ãñ2à¹Óˆ+ËŸ{ñ¯¹ç&^¸~ ÍòÃÙ%UWxUrõ²N¹˜×èÐÈÚ'kSйǢþ°•~ž,¤jç4 ,#eë°êŽ×ÖÇY4ÂøK*QƒÓ(*NoãèòMÜ~ñ5X^yÛë÷cdàŒkøæXž ×®< <÷8pïÓr]“û»ÝhÛ¹‡o<Üó4ð™/—ÀìsOHÛÓû$Ø:.äYÒ­h_þ,ð—÷þðÂcÀ%)ä&@¹Ëxu8º!Ÿ9y¸©AÜíQî/õ°ðÜJ`uE>»Õ\q¨œ¦ä,Oå9yxCÙö3}•×DÖ«u~&Ž­Ûl¯`ûøüOÿ}èßùå9:ÝôÙµ’³±ÍC«p[ÆÝ¹ÐK¯ló¸‡,A%\zлa‰f¹Â°:FwxŠau Íò ¡_ÂéÃ}9ÞöPÀâíû£€Ûˆc²º’ìçæpô"pëµÀ}Ÿ^|½0ȧ÷füØf é‚ò–.lËWržßÙÓU¥}üǶщ£ìÔk' ‚;¶mÈlÓ÷à@ûÅW:¡iÛì¾±tfÓï5rˆeÛm–(FŸÛôêHéq‚V%zö@’&:?µ=€ˆv±Å”IW•CTÞîråí~çx=wÖK€f£ ó¤­´| ™‘ôý~ÔW#q׳ÜÉ­'ð&˜²…ó ´Ð—K¹®Þ£Èga³…Ï9ѶØZᘋ¦¯‡€ÖKÞlë5_=j>qß×=°í=ÆÍ†¾eÃöP˜°þPè¡“y4Âa^?Êõâ¿¶Ú•\¯v´8?ÝZ Æukùl³…oF4>Â;ÝÍA ØQ➊Ùi°‚¹ùÎI`†`? ïs.ã"¹Ã}°9×cßeЂÆVÖ’á@îÕ±CÚr):[0%à­à=´zoqÄQ‚s ˆ ª›Y)d>GöÕ`ß^cøi–ñ{by,¶­ÊÚ¥¶iÔìûæ˜EÐÀ~ÏD¿ìVVö`Áçqô•?0Œ ç‘ðÍ€è8?¢iFøfHÅ ­Cã4 %漞B€Ìƒ›‘ÖsNÜ^Éõ‹ëK’ªaÇg}9ƒÏ†9Ì!ÏwOl…o æ\0j/«øŠp> " ñ£S„­%àÀÚtk=°Þ6@îH—òìˆ^櫃€Þv+*$¯[ª@’¤“¾pû1V6 Ruó¨j‘Q·¤ ì¼?qÚ–Ï Ðd&{s,y}¢äCæ ªAÜþ@‚Îl;v98œæ°y–´kiC ^äŒ3ФÏ|o±Ö¾éó­Ùš9eÞgr¯wÊÈw+”µqÄGéþÅïá¿z÷wãÇ~ìÇvçýl³ÍvG›7(œm¶W°=òÈ#o®m´êÐü!fG!:ãÄÈ©wGe¡Æ…q€ˆ(:‚16nËqû³{Ða¸³óȾÐ!»ö¨8<×’ûå{‡…ž:õM|úÍò æ+…™~ú->ÒÖ.ú¹fl.+ @€üÙU)€3.Ä1$('_Þ–yyKäMÉà%àªlÁØ!±xtv’#²ãä• xÇ„Ö öƒ0ò D‡4¾AÉýgÁ?¶%Hßi«ÔæX´ƒÜ–Ÿ•­­Œá鑲*ËÜo?"º€èG`q†íöh×øÌªÃâ`ƒ·=ºnÄ ·#m1‡p¶Pþç$‡ÓnKÕz—ª]/\ƒ³8îÜ\IýjÛ k"†Aæ®GLh{Œ¡o·ÁÃN@y IDAT{)\å}DtAë(dÆx À¢6þ°•|Ú\ª´Îb`)Ë6ƒSªl5@¤ë÷ºß°°‰%ÉvÈíu‹a»€Û\BX]’kxz¯€ÕÕeqî)ÿm8j6òÞâLî?o?͉\_ ¦D•’öG×Ä_Þ–žv+ íá\€k7ˆpè[lGHet'r÷FÕ+QÎEô&\ŠÛ¬³ÎšchƒG.ÂØÊ½GE‰ Òw²»”¹˜ôK™”à°Qñ(øÐû–òZO ar²Åí6¿r "ÈÈëbÔõ º|_ ¤! ž hŒˆz\ۆę5ÄÌØòAͽ=f%ïqî'í¸^2g(q&ëLÆ@Ï fu+Ðh~@ô27¶ðè\£ÜƒCIJ裀_2÷óØô8=ë°ìFloÝ#kĨsx{œó™C#ý9{@‚JúݲæGÄ Ü9O®ñg>ÇÜX´€õu-¼1D§ó:‡ÆG ý¾Ý¢_íqs’×ß±•1–2N«+ùÕn…wAÛ9[>úéïÙ=zMÌ5AÝ.ýB‚Eg÷fP͹Ïg%üÐ)€×çÃö’ÞSÇy%â3}s"c³Õ5Ÿ÷籦NçóЙïF>Û@HðÒß^çñö Ïg¯{jÚ9ÎgŸÓû»õùfÒéYPÆ[gxä‘G0Ûl³½4›ôÙf{Ûûßÿ~¼ímo¾åk€{—¥Ój‹²¤-Q†ü欮mŸ½L$°Hy´4”#ì"Kçܘ 2 öòЖBÎ¥#k}ÎÇ¥<|{"N9Yòq‘ïa!ŽÎFs­)-ïxô4ÊàtgY޾9ÎÔ:Ì `´[qb¸-LbÆìp[VðÐÙ¡s*ëó3£8qd\¼Üg˘7æ3+ÎãÙÏ5½\#oú Èï”ÇûAÚ°ðÏò,³o©VÊÛµŒáâ¶Ê$M± H)rÔÈg6'&¹­O4 …yý‘´]_Ò-z³²€Ž|ô2b£Ûöt™ÙiT’¶ŠœÉ¸´í yª-·ðûy7^¤ê€5té/Yç¼õV¹÷7ó…_< ØŽÀs·n®8á7”±ºùZ×Óû‚^¼?lpÈ2QÌm7yûQÆ…ïQ ¿¼-m—§ò~·V§XSœ^sM'Ÿ¤>8džíRÀWÊ+÷ù¶ºUÒp m4З‚aΫôØä÷íxLYZÃBþ—*Hë˜1ï5}¦ZëlÛÔ¦*duÞ5I3ª÷™•ž§Ïi9w ¸y¿Ž Û¸òº¶1-‡k¥Éά_c“×Án•<_Ö°èVr-Ž®IÀóàF®øM5’s „ùΫ+2&ëËyNÛué:Bp›úÔA$þFéØrèü¬îßâzWמëBbæ5 © †e‚°¾ ÁgŸÓ0B#Œ¶‹ÊŠ+‹íBù\£xž$2Ñ :1`’®½®ù,LÏO3?Æ.Ži |vpÍäóÔZR]M¼¦À’ôé^r\;ñ „êÚXbPUÛhMÇ÷¢\“†¬1·màÆuà—~ïÿûñßøçß?³Í6Û¤Í}¶Ù^ÁvãÆ \½zøº'Ç_—. 6+u„Ë Ç:¨In­ìF·Élê`XI¾JÙ_»•*²-óÕI"k?v™1kzqæ˜+ž¶]ˆ³0d‡Ï‚õ"_— ]û2Šl¶u¶¹L·ÊLú°P“€¦Žs¶»µœÛòTdÍ&3” Ûñ´PådÒÁLïyìÓ«Žq*¨'ÉJÜ#&Žc^y :åÎ|&õ7æ>{ãœY§¸(B8–çÄW~'sîÇEv¤“ZÃôÈj…Úɬ·ò±ý±ç´o<,€Krè±:çç…)t~Dƒ¾Ñya =œ躽[Ú&¢=mÀíµ‡ó×WëÞ ›vvÌßëËùÝxHúÖ+€%(­ÁÞ»€ÌY‚m:ÇdÕ ¸—·å³Ý™ù!3WͶlËãÑx_%€ä€³+æ‚é½™Xñ. ¨LóÊÙŽÌ`ôÊž*“dÐoûP0ÍqOU…®2 쌯¶çü$ð>O’R#«]Ø–iBºVºnkHð¸6øfeéç1í\;£¯n‹ˆ$…ÞY3Š“ÌdzkYÎ4ܱj»-ûËgDJù1ÁBÈ>ó¸)]C¯Ið9àЮ‘åþú,b0ÐAø,J…ÈÌ:Åq oS—Ò˜é¿Ð*³o^ûC-z‰:—·Êþn.Éu>»*÷ÙúR~r ©Êâ6g›K9ÙnsN=ÇÔ›^˜îVŸÍ`Š™"¿òË4®±Íãk2V5ÇçOðùº¹žBÌcÌ9=µî“EtíˆeÛF× Øý€B áû=÷é|âàÿûnܸ˗/ïÞ£³Í6Ûmè³Íö ·/}üqüQ3_ý@™#€$i¶V³F±ÄnÇÇ2^€‘¤ÒI2ùt6ßÔ¶í—¥3•Š!™ã¬…«œ°ªÔtm–µ²“aÇÎ_‘÷h¨µ}[Æä2ÀÇ :Ï´>îÚ&ûÊñ ‘™l[±ˆ5¸£cg¯õd_ÂôßîdéšD_±‰s,Ї™ãÚñèÖ˜w¦`W²ÀÜReÅ7'6'"iR¼ NòCœïšúP_“˜YàVÁu³Íê‡v#çÑ­0É “mO zȨ}ÌñÔ¹ÑVWËû~`ή¹×7º7óæ‰AO ½-eî”óþÞ¬˜²‚Y­ôÏãÞÚ™çÜ'þNkÁÊÌÚX×ç)«;vÎØÅê¸vÝçåµÞQ*Û·&¦9jÖÜü@ù™óÆyêÁàóÇ9ï6Ç:7Êc°*Ÿ÷'Ó)C7ÁŠ;=ƒj‹×ÇM¿Ÿs^Ä&UwxÖ@}ç9hÖ#¶åzÄ€!ŸV¥cïÞ¯~~÷ð¥¡ÃÇ>ò‘—~ž³Íöï¸Í9è³Íö ·¯ûÚ¯Å3¿òËmt@z0’Q”·²g2d£Ç å–Eol~¨©¾œØ€M.~ÜòÈb:1K G•e2ïtPò>µÐ[¯mµ/©ðۘƩÜÌ"GÕ:‚t´Õùhõ\ÈyÃNÇßKLJÁÉÑjp`¥ðÌWß²IçH-Û–X²Ša³¹…‰Ó÷süÐå÷­ƒkÙ”hÎ1©ö€óÞ²[:Ö¶ w8—Ñ7s´`Éä ¹‰ ×ÉõMçTjvÒC´Ã®ÃŸÒ(ÆPòûu@í" :‹¾YP´©‰moM îÁ·ó¬Ë  ^¼¯ÉþÚû‰kWšv^¤ß®š{V5B³÷€Íi'+IÆ:—Ówç\}j‰÷ù<ΛËÀî¼gšÙÔÜñ<>ì7sŒ“Åt$1¯†õOênûdÞ‹| Ê© å¹ÛmƤuÅüÝ‘½§J)–çâÆr<Æ®\©ä  =4¥bÃEÙbÌAž+|°sTóèù|rQÚ²x\š£n—åàìÚeÚ¦gqJñu\ ʔ݀ ê#2â\“ ÃŽ]4ÏK±‚ØÖ¤5ÆÆ'èrš ÷’ÏUõ¹V±ÑËßY?%x“>5–¯ ˜4×Ïðußôïc¶Ùf{é6ôÙf{…Û[Þòü¯?ÿó@߉ÓHs1Ë‹GÍ££¬œ²u: .à R¡8 eç— qì–a[åôÕ¬x*•Ete«9§êL¥-Ó k!'—Ïtô™ÏŠÒ‰uRñ*¨ÓK–%ÉCél«£”侂=mꤔ±Ö(wHeÁéÜL)€ì[O¾pœ‡|lË–ñXt‚\‚‚7›/Ÿœ:‚S}µxµ`PÏß«ä¿Ñ>t¤¼Å”ßnúTXe³¬(Ì‚DùÚØ ´ÞôÓתƒƒý úy,bm5ÛG øFÏ‹²V€b>7:7ÀF'íC+R×þPAù²¬» Ôéœtœ”·ù\œÉwvÛ|Ýü ã˜ò‹×9]£ýz^±0æ{Žf/&º3ãX3èS¬¸ÍA¯t‚¥Ò ž÷ä ÛàPÞ[)õÁ˾TxŸ§¦™bÛmµy¶)~ÿ<Ô#û¬˜O ölñ”=d0É¿y-²…0í¶ŽQïW®¥””ósMŸç2S)€(á|L T׉´êºG€9Ɖ±#X§é¢=.êž8d€^Ï9Ñ¢“ù9´¦.‹¢6¦ž5ølJkKÔ¯o̳fZé>÷91íA)®»õº•THÌgç³ÀŒWÜãÜfaÄôHáúL0m”qÐWë?€Ÿhsá3!Ïž/ƒÔý#ƒ |®ˆ×oà-oy f›m¶—n3@Ÿm¶W¸½õ­oEè{àÆ ¸|IÞ<‰JΪËm­ƒ`óÓv.†Ñ% æ8ÅÃÞ™‰‰å°m› ä#˜ÀÈaõwgœ:Ëêxi›#²ÅüÝe‡¨`ˆœ‚x2QêpDd€fö!Vmè¨`\õßEuBÇ Äè`Z‡Ë2¿~4Ûó–À{DÕ6ä¶uQ;:v|…“Z‚B2„†ô#­#0°B±Ð>hˆÁ  ;©NÝhŠ,çh®£ÍS'sæèÆÜw:‰6X1¥+Â~¤D”“Fω…Y«]gö.Í“ Ûõ"aïÖ"a'H'è—H,»CÅöÑ)o {œ¹<7—UdÐÕ #à|>ça)2÷±S ß  !ßÃ)H¥FT°× ¦ ´y¾gWó}ìÏA‡SVò{× ¶·§–9ÕÜ`qHè5 Ž?ç'f¦-Ç8 &± ÎÙ{°ftâÜj7¹-U œG¼µbƒŸ!ÓžÒ ª{{GERñtCÜàg­¤;M@v{,÷k*Ê7ܪjêJ.\gÇßjí '9ÍE $QñÐäü}®¬©®õ"wÛ#ít1¸b Êx42þ¶P[h€À¹irχ¥¼?˜ùÊ}Äm~ºUÇ=>×-;¶Sפ šüózœm[`÷9!ƒ‘ŸgiM ²®Ù¹™-@Þü®ôCþÙÙþ›gbúŒ7Ç7ljˆægÌù9eÛƒù|­€Û·úo}ë[1Ûl³½t›sÐg›ín7nÜÀ½÷݇ðe_ <öDÏ. Ééú#©¾½=–ƒlÒÞË*Wlz)ŠÖiõíÆ‰³mJ¹t¤ ëêÌŒtˆ}ÉF¸ t#’Ô–| 8gÈŸÀͰáV*J`\Wû­‹´±`V’]†ì’!µRg¼fmöæW²m¨𪿦ù´<[&™J;S2ðI§Î:ÿ渶­-VË#-3  ¨âžÒ8G..I{]þÙöwjìÒ9Öý­Ð| È(‹äG¿¶­ì¤zP‡¸hëÊ>ÔìgúN;L Ë‚Sƒí‘JÝͶi–}Û9¶ aÕõF« ·kù¹Ýjw}mzÍEsÕöV?ËâpE±? ø*0gωÿhãBÖ„¨÷2A|nȤûœ›n :Ò™·A;˜1 ˜šÖìý—äºfNLY$´€Å2¿ÓÖïäxY…¢³k ƒ=Å=hÀyꀙßš¤txr­ƒ¹·›|nl[+–Ò6_m‹ûÊ—`Лçiîw^‡@âyleÎÖk¢¶u¹­]‹©ÚáwÛâoé35ØŒD »* ÇùZÌïeÊ[yM~~2xLPϵ©PcL¬_EÊ’µ»t¥kõˆ=N±fØõÚÜçõ|ó1_7Û6=3‡ü³Ì߆<ïmm‹ôýƒyöò»MJTh$8ľ<ý‡ðø›xñ…påÊ•»“Ùf›-ÙÌ Ï6Û+Ü®\¹‚oøúoÀ¯øi„ÿ FÅ­š¾‘}»5°¹GÁæÙéZ_'‹mÏîÍl^rÕÙ˜­u:S‘–{Ü’…HÀ}ÔJëCUíÙ‘·9™i¡Š!-œ u”È€¥ê´1ûG‰Ш¿ý<óÞÉ $‡ÆeH£Ó–ªæ";”Ìy$c9ÅÈñ¸d!XÙ¾Ûd&šcÒ³sÍeÿÏ:Jõg]„0«ÖA79Í."í= P×nc€”gÉSrA[A{ê6?Wte4ÝOË<êô»»àØÝÉ*†)1•>K¶Yµ¼?È œ÷Jч‰€CÔ4ÊF5k9~¡l`@…Ì%ÙÈmžûA+>“éB„䢒3RXÎužÏöE*ÉÙ=Hl%œ0­86?ÉÚ9~gÎߕ㱯à>‘æÇCO5>/b Òs \f$§Œó:¡!Kò2(±×În3éPÞ'dÒ]5ø·ô™ÑúþpUÿQ^?œ7æTmÏSVÙó¦² Ý¯zž£ù½ÙäJü@f‡Ç¡*T¦ëQXS¡²àÊóOßÍþ y½ øvQóÃCÞ½cüñ‡Ðê|äüô¹a’ÂSÞ®5Rš…ËÇyß\`üÎ[¯lJÏÎs-”óÐþµåXÖõLŠ1ÞSp'•'æyYü<¯Èß>cŠI‘sέ3h-àŸÿ4þô7üéœÏ6Ûçi3@Ÿm¶W½óïÀ¯ÿà8•–ú‹]æíôÞìÌÔQþq!Ñõ½Úö>ध¶£ä“Û¶[Ê™Û bYà¦?Ñߤ=uò™þŒú@§„Ò:ùÀÝ;/§±/ìnb0<зHû¼'‰­u´äùØê¸¦º*%Û˜$ЃŒw»V§qc¤Úd¯bV3D'ãlÛ6š·žÀ?$ß…t·‘ï±…‰^‚ c#ùÐTiùêŽ>;l¶m¡(Pö§(´¤^óZŠË|õq©ã±4Eù`H!Gh¹«r›ºÁùKB^ïv` í—þLöÜ…\m½]K›vÈܪb“ Å-n#1°¡É,fÚnÉ<Êëý—“‚¥ÍëÅ`€×â8»/³äAü¾ôМ7uNüyÆ” À€Ý}ÌyWý^1é/)}DÖU¯a¾×@XšàÖÄE Äš>ÏO Øx¿¥€“#¸F3¿—ª4æô¡áÉ'Ÿžü6àž7 µ*ú˜¢Te]+‡äžâ6ïŽ@3±2úPfÁŸaY‚ÒÞ˜¢qT‡ŠÅˆjÇÒÚ뛪[F+ì:)aÜ ýJíÇìdÚmµ’ãS3ç8(Eÿôø ¸SªZIJ-@aêÁ°ñØ U/¶m9ötÆ[e­š­P:`tà»ÌÈ·›ìÐ'çy ¬¾µð àæ?ŽEh$?;6Ò‡Z²j÷Áµ{G7&ÇÝæ9¦jÅ]Þi€vJyé”Ò)êô ‘t3—ÅŽ8÷ jû\(k ¤PZ[_ÎÑ`úÀëG Ûç¾0e„€~ßLû¶èò#ÒžÃܦh±’÷Û|O·Î[¬qŸóF娔ÄÃÜ”ª¦ô;æ"§WmfƇ.¯€´Y_A’ó'æÜ°î@–¸×kE³Ë®>6`HÀËyÄëL9ï[ 0›^ØÚf¨‚1ƒ;Ï5)L×Jè™c]T§ö%ûX´å¹µüò¦"Ý)W‚ ÄªÜq<Œq åú2vyMå9RÆÏùIõ ïm{&Õ+<§‚醙£½°Þí¶š×L­ ÇåW¶åºÀsoöÌÏÐhD̼2 ½7ÅdþŽ‹é¼rÊÕíüg*Jª«R©KhwS€òN–¤ßµšiâùcŸ]¶wpÕ1êT¯úØÅ3´jkÿ¥´-xe0ØþÍrˆ€ÓçƒÏk¨ À þàÿć>ô!¼éMozyÆr¶Ùþµ Ï6Û«ÀbŒxÃãOànß<ú­Ùy–â l«¼òÑDòÓC´…î… b¾ºeÐÕ ƒ“˜ne0(E²ÓÅÀ@cØŸäÈÓDéÔ9‘j5p´¹ÓÍVA :‹°Ðñ$ïK'É™6–mžô‰~'ù.Ù‚ó&H»¡ubBƒ,½7æv›bË$u`yM N¸-• XæjlËmv"²#–Ø—ÁrÚçÖì©M4ãÃôõ¹Mº¾¨ÆÎ\k2“;9Â.ƒÆŠ)ÝÉ/¯Œ×€Ç;ïéV3®é3>ÑóØáÚ,ã›®ƒ–("àfnyÓ °Jùç}0±=ÛÖ9Úɹ†é?2˜±ÜÒVq‹\Üç»9ÑjôÚÞæú2ÿ¼+£" ìÚ^Ÿ<^ûþ$رœs7q-QöãVäíî.0÷^D)‡7×a2ož¿ä˜ùŸî•Ô)½ß]¹¤uÖȲ9övIÅëãæÃ#BU£»:¸¼F”ÙÿfsУ‚²ô9äþmC~&ke>é 2˜WÔðù9Ãyªº;¹éM™“NåHºšrž$Š— Ä¹5 *Kχs;ŸRÛ0=ïìg¨6ÏÃà3+ÞÔl{ÍŠ›à³•ÐïÝ&ÈdÛò„|­PÛ¼]$ï«þ¾ôø6>öÑ\|g›m¶I›%î³Íö*0ç¾õïÀO¼ïbè—(׋š$—”ÛG±²µ~Ð\±QÚ6°º¬ÝCyÞ˜8(KÕKžz·V'{òÕõóM/ßÝ­Eb_H“y"† £Ïßkϲp6ȨÑùU–’y޶Â--í>9¢9x`姖ѱŽ\á˜(hì)±>G~º9É€“l6ê㪣½º’s"“6”à›N}®Ù¹ãñ²‰>÷¥ØH’ø¦t¦Óv\•Cç"ýç ÛÙ!ÐkÃ?W m`™[vq¼sœè‹‚À¢ÈVìj  ¨n;sžs ?P±@°.ôf2uÊž_ún¨Hýd_5À–rå›Ì:Úê뀜O˜ßK‰)€^YQn‚‰´ ‚¡¢ØçÉV²XZ=‡l€f sLµÑüÎyFÃ^ê=䔉vNÞK€Šm`À޽FçžçÔ¼çØY ZHx-‚ÃGì ƒŠ½a±¹ï8s¤9V‰a&Ÿ#µòh²­Qõ¤H›¡É뢛6°3vH©^Ÿg±bÅÇNÆÇn±·mS!½j¼¢Æ—0‹Ô˜ `ã²=Gþy'@m˜s ð› p§gb,ÛZÐ}7Æ`«=GÖS‘7$ü½îš±=ÊÁ×£½ùq|ëw¼ëî¿{¶ÙfÛ± Ï6Û«ÄÞþö·ãÇüÇ›/_ fÖúLñêjfÅ­Ô΀h•‡®OòÖ.1 >·Çò >»"Nk˜AebWݳ–9èã! y¶ ƒMv8Ò›Ñü\™e| üeYæ˜Ûî³è‘ ÞHãD Kº|¬¤óüe Âù¬Ž3 SmU~ºÑñ¥äÖÊ´ÉŠ Úv}9×ðc.ÚÄ`„•”RŽK§99¶t õÜÝK›Ì<§àP/¹¡#€h¤»3£¹dÓj¹?œq òõ¼é°§=Ó›<¼nIº«~›¯žÆŽãàsÀáNÅËÉ”¯9P‚¤‚‰µN8°¤¬L@ ‘ýÒ¯J³WyºU ¨;dö’ †wh¤à4¯ãIæ/‡\$õ>åÃ"Wg/¶Zóy?w2–u»|ÜI n@ÇóNòáú3²‰ÀËÎqÝùÇ%¸NLº:à8VmCâ¾Ï@…@6’Ý4`Ð@©Ü Ÿ¡t=­Í à©IÅ6—ú»ægÛ (ƒ+Ü5cXf•LÁŠë˜9Î;²×QRš@ƒ"0„Û¢ÜF×:ž£ªeF㜫©-ïu |¦1 ÀèòÚIÙ?`Ösϱ¶Ó+6'yí°s:ÉÛ½É+×\ô`Ž•®1 …ï‚)¯m§Ð¥®ûÅZðŒŠ)p._dòʇܨ‚|{À¹­â¾omä²éW€YoG Ls;ÐFk­ø8}Ãê&Þþö·¿ô1˜m¶Ù’Í÷Ùf{•Xß÷xíëµñ+û¿C¶˜ó{ãÔ¥ýiôÚ=e X9€tz\̺6ÇÜú£|Ü‚¥T‡‡ûÛvk©,¾³OÊLT™3p©%—©¼³e Q-q·Ne3ô=ËŠÛsÛFÉ=·ë¼d«> SËq&ƒÎÀ ŽcuMšAÇ.à­,!Ó¸§yÇ"j&W<êXÚ-ò‚×|ÏJÞJBÀͱëÖY‡¢ðY ð¹XPË|Ä`æ†+Ç·Ýæ9ÊÔ @J*½ªU ¼yìT-Â!µ¹íE[ãtÚq.˜5½_l°eŸ¥9§Æ±Jó8"íÍë₌›ä¾jÀõšsndð–©LÕÍ+ç9ÍMÕZʽ±¾‚P‚Î-¨¸—»µÕU> ¤âs)àd€ wh`mÎcŽEJ£Yf¬MU(Ú6ªaí… hÚÀ ÷zOm²¼6IÁ5xǹÌ9Wä•÷ù˜Ðp--¢ØL·µëThs¡?›«›æ†y/Õ™X’™´ö IDAT"Ãó8'¤×Ð÷HÛ_Ö9èzoÇ|è¨.Ò©¡áôÜxMŠ §—ᬓ6Xa>gÛÚ`Z­©eÚõ½](\^‹ÉŠF¤v¶4mGÿ4e#©µ*ýnŠÒŠ`a­ž0VTºG9ûdè©~EͶ›¶¾’¢ï¤Y˜~ÔÇ­Ûùûì}rÞzË`k¡ óÀ§÷„á³Ï~]W­/³Í6Û]ÛÌ Ï6۫ĺ®Ã»ßõWñ7ü§0n¾ðmf6GòàNUÖÉðN8¹Ûy`oŽKÎ-i ]AùêŠÈè7‡•ól€$™¡Íq΃GœC¡òÜËlN˜‹(¶2ñô+û2Ô(@‘£’wH•¯é³€Y4Îb× …¶ü˜Çy0Ç`ûQe›­j¿"+~„¬(@në•h7yìR°…ŽŽ:ð¬7ÐnóõÛª2‚•8˜ŸÛ|MÚmÙ–òùT]]<ûÝÛãš±sQŽÓnE…Á>5=Ùy¥#ËÒú8·uQÇNÁ0sîéðm€¥ã6åØY¹er,l»x…´ ^TiüÛœ*RªÏÇ4@ÍÎE¾Òn·Ù w¡Û¬‰Ð®¼[‡œ§çh}n)—–FYÇÄ"*k¾Õ}Í-žÕ/³ÄÕ®y^ëˆ7Œ£U–"r®4 L¶MQˆ(ƒ_lK¶³· ì‹®¬ª__k@¯}d ¥H!(Õµ’Aö!©B8ï °jTÁ`]p´;ä{!bÞ¯¼GR`Á©1ß+)5æP¾o§Š;òzË0¸KD4A@ö{Ôõ¤?”ç„Wi̓ u=ï4XÇþêg’H PUÛrÛ­X­ù1æ¶v7zLkU,:ºÈ×:: ðµ•kjt¶MÊœZSs ‘îÆR@Î|ïðL«Áy‘ûm‚Îç‚ó*L€í +ÕÛt3»¾yœR@ ²^º(s²]çùÜ8Csãwð]ÿõ1ƒóÙf{™lfÐg›íUdO=õÞøÆ7÷ü—Àò/ÈC~84ìäÙ+[-=êjòC•mé,Zi+`˜³À÷pÁ#ÂÃ!"ªsΟ"÷g¶,bW»—9åsE{0S{ºNåé¹`ØŸµ¦*jkÏ£“œ\ì8£T ÐôcAtž#äšØ=ä9Δ_'É02«4jQ¹Zf˜äíêÄÚ õ;¦¶T•wêš„ÝßQ9…uq­ zAÛ‘ŒßAÍïKíÌ{wۇ⻛ê»T~ú3õ÷ãhI‡¶0\bÅ Ò‡\ôEáÒgÌñ€Ýs¤šc'_œ[Pé^íýIÚΪ×dÑmmˆÐé¶Œæ¸…ÜØçkb{ñ¾M2¡¡Góþ…r¦÷f±zÎì°ºæ ;à©IõgyßTšZ¥Sô}Oz@QØù(1.²ÐÜ7ÕZ3Ùò%†õ7÷·mãÒ®iL‡‰{±jë²Âi¬Ö¢=ë×E®·é\ý.+^ç 'åW—1ÅúüûÒRu}—‡ÕÖ¹¨ñœíþæõÚ`¯EÍŠOÍÙºpmßü¶Vˆ¦×˺Ïi,L šƒRo™Ékrý·€gÿžzê)<ñįÙf›m¯Í}¶Ù^eö¶·ýyüÚo<ñäÇ„pGYd‚´0GÖJU}(«Œ' ÞnPPÞ Á Õï´§±¶ít_b:—„Iî‹Ýñ°mkG‘ŽÆæ«W¬ÛrìŠWëàIosÆmÛ"¯Û°‰Öh0€µ6ð²ÏªãNå-Ûë}î¡"0U‰¹±©:Ÿ Ð{Îõ½šaO€f걬,§•µ'&Ñä™ Í3_d@Î÷Æ6+ehÃ"àÚ "9nÈÛkg~·Loñw#a~¹ÌÕ÷öDðÆÎõ°Z[€M‚\¾O€Ϲn•%àã°"©|¢r‰Ê‘TÍÝçk@u׌4v4ýÚY‹M[Þ«uN3ë#ÔëK=i»K}õõ˜M<Š”«Ü0êª$¸¦ÖuÒ|òØ©«0yO;³.ا®ÙNŸ'¬~ÆAÎ)pn€÷Nev»ÆÔòùª-۟Ф-ïÒÏ•–Ï«5€ª“V1th>ócø³_û~åW~yz\f›m¶»¶Yâ>Ûl¯2ûîï~ÞÿþoÚÏíc9“UtÉøTJvõ–·ÅÎ"JY`ððp1"" Á $'ÙÕÒ]bÓÙ& `ŸòÚLûî,زL€bPFAYf‚idp 2IVê¬kÚ†Ì.â±9‰µ¶ùäËR:¤Êìýö‰•‚ÙÉ9ž”h2G–…ç,isp%åž¶°ißYi—ÒÚ$)móß )ÑíÅ™ªLa¨à²óGç;m•·ÈcèGm«A»ÍNªy@¦ÉÎ;ýÙE`ôeÛ±‘f‰1¯€¼5:Å…ÌÜ—L\Q¬ŽóɀƩãr$žsïà‘M" Uº 0<ЀM oƒ,”–B_½u‚9tÎ×cPæ`ö,'@ïYîÀüMstmGÃõyÞï|U$†‘,Ú ç"RmDÝD¸èŠ×ÈJƒŒ|¯¤ëÌ´ ‚‚ R €Iâ9rètœC|}ë8ÛçÓ«lpŽÁRˆKà|™×Û´ãÛëz›æŸ™r¢ù'¬®×ŠëÆy`Ýn€] nÇÛù  Øn®2[p^³ãf§$v>»Ý>Øg ÿ6¶Õ±jU]DqoèˆHð ¹öô5¶ÀöŒ§Æw÷î½Ùf›í%ØÌ Ï6Û«Ì6› |ð\»ýçå÷g ˜×`àÊÀ—Ž`tš˜A¤Ó°@îQ~GÐß)e—åÂ2è±éòh„]· ™D+“LŒ/&Ë2ðµb ,‹IG¯ÑÂP­²Ëup `ëô¤d*]wG˜9Ò>(ÃÕ‹ãm+3'’²ùv£òö˜%:ªÙ9*rB 4cép¥"W^+³·¹P–-䔪ƒSY¡Ž"ÇÃkeöB}0˜ã6ZTÎç*îioW^‹;ª@ž#·£²j{¾õxLJ•9/&ŒLѹ IȺ«VÈσ2çÕ|® ½‘=oT%Ò®åóuÑÃÉ>2¨¶Ì¿G'sû=ˆo¤íêªÞãKŒãØå]ü Ç[«¼½ƒ)`NãR|F4®.¤3 iý¸ˆE4f]qe: 8gtajÍàš€¬&€’1;?y,ž'K&uj'^ÃT?€Á.3—SÑ·6ƒ/~'óÉùÙüà|ˆìr­K{z»|n\¢ËA»‹°ºnÐÛnÊõ–i<®^¬þ`ÀÕ¬ùSê–©Üo;çôLA ûœÏ“©T@Uǵ …ó,©Îl€Q×5¦B¥œ}7ÑÖ¬Õ–aæx&ù?ÿ™µËôÉõî z>3÷]þ½ès4ÏŸXž×Eä´{Äs·¦m½ðpÏâ7ñìg>…Åb1Õ¡Ùf›í%ØÌ Ï6Û«Ì–Ë%Þý¿ù?þ-Œá=Àxißs»Ý²Ìëƒ2­t•j;x³±¶Wi›Ç€ ¯¾zu²-P—÷¼ÈÛa£ôü¹vÒÎW©‰ËN-sÛ‡¥Mu†= PG­:¿=Ñb6Z/9èÈŽÉ ÅÙVWra42½)Ï—é@œòÕäÂycfz¢ÏŒP(ýZݣœH¬»Ž_r—bƒècÊõO[,:Œê4Y‡¹í܇v0[ÃùFƒÊÒõ*…·[7ù‰Y#àðcîC_·%'ƒÞ›ív–9‡ÞÊ?m‚‚ž2TÍ3=Ïq¦Ü²é)'Ò€ž¹s‡¼Ñ—R½9™aòÓÅzÿt+yµ`.ɉõ^dpÅ*`ìt.;Ì©0š²ß©*»~ ­‹^æ%Pó:g·Ÿ$q7éSû™p«ßƒ¾7îm“ß{AzØÙ/\àøtÛÔ `ß NslK èØì™v× ß#ÖX”Œà¿®ŒŸÚ¸\ ¯8¾g{8¹_›èµåöIå’R ´ÍÖìj7È…9©0ª»ea›Åpk­7ê#4y½O íÚLÛW3aÊì üq­-æ¨ËkFç6éàünlJy‘æPõZ(¾`ÖQ¦&!¿Þ œ¿ì檀 »Š˜ª HJ´´ök LGªŒ†Ê­üÜ84›_Ãw½÷{fp>Ûl/³Í úl³½ íŸøžxâO`ˆïÚÿ4?ˆë½ Ì¾•Õm|XôÍcT'[Xv¢3æ19è<6™¬¨ž^€ä¬é‰EOl¢ùWìéŠ]6YxG_ º½aˆÆXl·½²MCi»¨AœÛn¥’Ò>3ÞÌ7w1³ŽÛ#Æi¯wîMæÕa7¿G@«Á 8Ä)uÌó3 …üêƒÈ“,~ßI@fp­Y0eü½–\VJ…v+}ä×%åBÅL~^æòÜLÆãÛßGéK’Ç܎Â=µÕÅÏ€ÌÈÛåŠ}M×`”yæ‚Ëâœ×¿‘aä6kõx×8´ÈÃåR PRñ·¥É3WiûúXÖ‚þ(Œ¬˜(Ÿ¦¹¨LXY6G–Zû ¹ÅZÁ¿ÌMÏi¸­§ À"F™ëÕœG^sè3›^3ã!ÏÕœOåëÓà2I†÷Ìùš¶³fŽŸ›“oÿpšRmì¨Xæ6¿/km}_ïÎÃôÏœkÓ£X7xÿ9ù´Û,‘çgÚ=óº®Ó`­Ó)¯Ü(’üd¸T¼³)ÇoJåQ OƒbG[·e4ÏL+“Ou3Æ© xê c5ƒnŒç±çéóõxQñbÛ˜>OÍåóŒëgÈ´iKœgõ<åÖ~ý°ú9´«ŸÅG>ò{ì±»ëÃl³Ív®Í úl³½ í±ÇûÞõŸáïþ½ŸÆ¸ý¶1Û혽I‹x€‹Mˆl0¯\œP_€rðР7“ô=ÊNQ|ö®,940à@Ù0:F)ªßb×!®¶:å–h‡Fâ§F ¶Ö%p}IŽ·19èÑ8~Ìû$fž‹N1VPh=ÂÅ`€Ó¼ý*'ž’Eê«3¯SgÚ’O…BD¤“êbvà)wç~ç»nm‚*u.ÿ£DwôÙùJû@“•Ô1£WŠFûjOq_Ó¶ B`ôùÚ¦|N¦4O]n3ÅæÝÉ(¶Æœñ} ½.YÀ^ω9åtS :Oƒç ¼¥| D‚ÞÛIÊnŠÁ Ë̦°ÌC_ ¨ˆM½>¿ÐÀÇÉÓŽzvÁü\³Þ9íŪiÌàîeÉëV¥©T=xúgë\äOsí‰òm1·—ùåÌ<3 Ûv'w*„àwO±¶ó’Þ£UÒØï4Œ¥nk&kˆƒ æí è¥qàœqNÖ›àk ËéxfØÜ£ <ʇnSó˜×V\¡}ð}—¨âïç–ùž‚T ¤6à<©&¬ÞÂJóy¶Ú:Êæ©¿©-s÷7»1µÒÚUí:r7ƭмYoR!Q£,tÝAþ™’ü´3HU™÷CJ=2Am@Õuº¦Rþ¼Ì#LúC œ¡Yÿ3¼ûÝïšÁùl³}lfÐg›íUjO?ý4ü Ã÷xÎËA·Œxýsþ]Jí|ÛÜs± LØ2@À+sÏæk¾žgŽn0,☺ä8Ùéøâô¹Þ$ã¡}"8§“Û®3p#(!èN9èQÃaLJh[H›f 7Ê8û´|rú¿{¶ÙfÛg3ƒ>Ûl¯R{ôÑGñîw¿ çïü=ŒãwÀÅ#qo …VJÌ‹€Ä⎈ú ¥¼=;â°,ù”÷FÏÀÉ™¯˜D²[Œî'ð,,HÖÉU¢?”ß·Ë òRñ$Ÿm/…²Ú °¥,ÑÊЕ¹«ó¼¹„œÿ²l’ΉÜ-äàãÔØqË9é[ƒ-o§ãO“´ßÝW¦Øqú™e]€PÀô@VÙ—³ì™  ¹FM.ÜÔ­‘ªB)HdØ2’”?´J»»cÛ 0ºQ¤¬ ƒ*d´µ­³í'fâTQ¹)c¡¾òÍü=ÓÒ?Y'ß²²Hâçù¡‘~ÛBa– ´`bT)olr ˆ…ß¶'„ö‡ÀúŠÌóþ@>òͱ ¥Jz3¸ UP.[ƒ¾O´«A¸ß“_Îñã¼ hÓk^ØÒé]’A”K÷•9l[ˆ êºì€óô×X.Ì™©ÄkF•Õ8œ{HýLµúC­õpP2š5¬~î#°>Ò*î—T½6‘kô ÷¿(–X¦;_Ÿ˜€¼+ÖhÀ!FŸN?ŸŸ•Csm €'Ýn¯£/4‚}3„é>.€«Ë÷-ï™´VKàÜæ•sÖ{„9èi«¹ª-Á}ÚIÂ\ÇÚ8 LY»æœê׋ؔt@Ê›·ck·1 ézØt¢TYŸkF›Sê4!ªl`!©/°Û6x9^ Á¬Q^æ|ô¹mèÑô?‡ïú®wÏà|¶Ù¾@63è³Íö*¶gžyoxÃã†ï…Ã{ÒûV ™rºÃ”âlx0k 9ÅåÜhËl5ê8çôüóÉ|gLäÿU1óÿ†…H·É@Ù"6:ÌÅßËS-¢d*ÓÁƒ²ìÝZ Þ´+¸±+à™KàÐÁ) n±Åˆ-·“ׯ)¤Ïí‚g 28Ž|m ðfZAL !`AŠ8n9ÿvWÚÑ p«.J×\Åæ¡&€c®I»FÞʪ °XÇ/(˜ÕAžR5Ü©í¾\K:¸Í¸>;Ô‰YŸ`Ð9Ÿ.¢ï0èì‚çú˜œ³i<ì˜Ucwn®ÿ„ åÊž§JÖpc{"}[_’v¬âÎmÕ¢OÅ[¬aU®3± ¸§~·Ú˜òo£ïâ@e7ÕÅ¥5$VãœOëGі᧦h{!= Äžjù)…žÇàUÕÆÎå~™×¤¢ÚzÈáÊèh÷èà4Ý…;ed… sJpN^Ù6ê¹ÖÁ ŽEVÖØ1ƒ‹ˆT|ÔóÓ©Êã¼zlKk7üDÜg Hí®ÌqOÁ:G0 ¸ë*îc›S&Áù BNZÜ ï†a?ÏìZ7¹Å2Ý ÌÓµ»H1cg}ÅúévÛÚí0wÖfó¼Ú_[ûÀ\ªz¨Ùü4ÚáÁÇ>ö¼þõ¯ÿüÇj¶ÙfÛ± Ï6Û«ÜÞûÞ÷âoÿíŸEÿ".À¡KàQ@¸8‹ X½Ý:ÜÖAgz _@¯%ÊVJ]¶£Ã•˜× NA·Î•Ô{Ý+œRàäôpC‡è¸»Ñe'8;Írþ#:´XaDW°×9XQ2ÛÜëÕíkõ0ZåØÙk‘CVÎÞ&vÐæóۜ۩‚|lcJ¹¶˜o:uƹ³ï!šâ}”Òi»àõnm‡•³Îè°Û¾oeåûÀ¹ýÌElH?·ÿv.ëØ´[ù¥Ôí¶œóçÙØ)ƒ¥RtÊÔC«¹žX]à”‡F‚Jã"åœ36ßóqççâTŒ‚ÆòÛ\?r@/›ÍO¿¨YÉz^;Jžwùwû3™óýà¹|Æ»ã½[TÏJkówg[(/õr¬g©’¸k¡ÎZÖž§ClŠ^Tw ЧlàÞ[Å}s>qýÒΆYÞwÚ|úÔ÷sîÙ:Ÿû¢k‡sÛzk¿}V3ù©¬ÉAgA?«”¸H-›ƒž”]V‘¹X1è]\†34«oÇ{þóïÄOþäO^ì\f›m¶»¶ Ï6Û«Ü>ùÉOâ oxCÿŸ Á÷&ÀV:„¹ú¯‡8%´×ÌÚ4snb:Î¥CXt†½± Œ@pȹœv [ùØÊNåÝnh!¼ …ÒôÛ£ž[D‹ôX¢Å6謰2Ü 6 Ò7 ¤#éØ~)Àn·&˜`Úqü¦é»éoÈ ëÂ65^^ûܘþN©5›ëÉÛl_`›ôÙfû"°Õj…/û²7ãÓŸ| \xŸqqwYq»·9·à½÷6Ï™•ÅY©}Jân?“Ú8ù¦²hÓ(ÞE¸H‡’}bìvo‰9G–åÛ¾—mÉtçâI<#ùÜ)à& 7?Cg*-rUõ¼ü¨ɬyéÆjì Xoú²_Ì¿µÃQKt+³·YÞÎêìÜ:­?Rý.4p£ƒ/ÀyùZƒî2§<§CìË5·ž¤ç–Ý-¶ßœ×Î÷朿åß¹í¨,Ò¼ºS&àÇ€SZêT€¼e.‚'÷ñˆiv<¶+_¹ebê ˆ»XÛi%‚ô 1¿SÕ¤l-çg½Ó@º‡|¿5Ê 7¦þÃήöç ¨W’t™µ–˜{Ìí°,ÛZÙuQ Þ·¸Äfݯ×®m¶­-ʆ ®% ÐGì¬a¿r*Il^yêï9m÷íÝÎëGµDðŸbÐyl[S0;˜TUÜ5ˆå‡ïÃëzüà¿Åáááùã2Ûl³}^63è³ÍöE`‡‡‡xßû~ßüÍߌ¿‡¿ëÆñ_VûäfǤv¾Åab ,t” ÌìJŽ»#ã³S.ÕŠø(Nh‡$Ë2R¯ßÝaƒ€-zx´Ú–`AØcî¶ ¼×@bÅ-@ŽÉ oµŸVÈÕÑ}¸y\i›+³—ã&m™ãߤ¾ôhv©÷ ¸ýZcª7ç¼õ¬TÈ[*I `T`¤¾i<¤Bvÿ"Ï—|~ã¨Ñô©ÈÞÐeÅ*ïtêbvæ\åX²ÊÙ#ÓƒûñÚ}˜ÇìRâÎÏìT6 @MX IDAT±çlË¼Ó €Ç²‹D³òºH•¡“)8gÀ‚s Þì{ÿ(LJUÂ{ur)gçöiãB †NذÐý‘žªƒ‹"cßçȪNÉÖ)¹¶WéιÎ5ðÜ]#¦,+I|šÁùNÉà2ƒöúÈîܶù3FÅÀÔD°…f+fÇ€ !µ9ä^ܤ5…ëP.')0yØj[F¸äBn²H: ïùú¼© ñºÆ±/v4ìÏ ìå€jrtBñIô «æ›X[9ÔiÛ5äyo‹ÁM™°ûpnR›X!1çV~í2xLÇßÎùÝÉtÝãy³fúÝ«<‹Hu}L Úc;ÏÒ® ̃oóqì«eè-.˜v·èòqkζý¡´&v™*¦AÑõ%¹†‰™×€•;dÊ}È}HUô«œÿá_!ô¿…Ÿú©9ƒóÙfûc°™AŸm¶/"û¶oûñÏÿù¯¡þ)€cã|çük\1(Áùy6Åì–¬˜ÍSÏà\Q ,C$³Ñ¼ï6hËLûäÜ Ho±Á€NóÊdÈ.<äÙÞLò¿í6DY*ŸË úˆE .¸Ĉ(+°ç¾Ô…ãj–‘ν­ÈœAÑifÃ=D–nƒ!ûsu3ƒn¥ðY>?Í ‹ìÝ8¶¶ê5e¡SLw-…i;Y¸b¥´‡çŸóƒTÜmL?ضÄ€ò ƾ𼦊ÕÙ¢SN€€ BX™’R@ ¾!Š´}ì„=ßÊÏ›ùìêŠ>Œz…ûIp.wÞ  0§kðþ/k1èij™iP»´+w&` (§*‹çœèrn—ÕÛíœ÷;ïåvfŽ× zQ8Òäá’!Là+K×¹ua£™Ô=ÆJ•À@¤Þ}z-•µŽëL›€xÉ2˜6¥Šá5,¯­Â}Ëž»4N.ßcé^2ƒN麢`‰^$ÒÍ`¶Y„tîÿ^Ï}S$ŽsŠÓç¦ë*îÅžÛ‹²mÍì¦Áñô§ïfЛ5‹_Ï £/×#Û6IøÝùk Ï‘m](™þ}ë¢Mq)òÊ«tnõ™æ±pëÖ{*³ëùE§¹`FjŸv2±Û¢<>PÝ'©k¼vüðŽwüYüüÏÿoûÇd¶Ùf{Ùlè³ÍöEdÏ<ó ÞøÆ/ðþK8Ä÷™¿Xžm:¿´dg0å}#-]V¯lM é§­´œÙ¯ÀØ+»LÀÍ=}mÁµ’j1½ï/]×¼½YHLtk¥LW^Gäýš3›MÂϜ۽íH¯º­8år?haÊ9v² S€Â"`²Îvë¹¢ufÐÙNŽ[Êe ê‹ÜÝÄ 9»-—˜ó»p\‹ f@“攸]¥´¶Ò4ðÊç<¯ ÛÝ ¬¥œYÃþQ¡À­ÓúC©É0,eË´í¡²èÇ@FW€ùV¯ä§À‘÷ËtõvkûõYúnçAéݘÒ»ëç°-`6 ÐsêŒ= @yÄ”bæò‹K°¤ùå6¸Sclpƒk‘/r^?× ‘³Pdòõš!);±Òɽ×:…%Ÿcqnæïe¤ÁÝt½·XÅÝОrÌcþ¹5s?1è;÷i½3B4ç0¶¢N–™ùÞçŸ*™s è“à¼A9Q­Õü¨·ZÛ·ÆÔyåuIž£ÖTôü§~ÅÜO¦ÎÉØ›Z@5}h6HÕÜs'r¢æ¼–Ï3?59P0,¤/S…ø†¿ƒå/àÃøÁy[µÙfûc²Yâ>Ûl_Döú׿?üÃí¯ý Bü&´øJÀM ë&kœ|¥ƒÙ…âÈÜR¢-Ìü+Œì"ÉÑ£i+[•-q[AúsÐKÙe¡YRÚ«ó܃ކu´Eº.ÕëÄbïžëhÚz((AŸ|—0ð¹í¦*B(ÔÕ¶,<·N =* ç5iRP!‚)Ò–Uâ-sžu €ÍSiÄx¥]—Ñœƒ ¢h(˜À¨2Éš ŒaX–Ô#oßkFþÉß]Ô˜Ãhy_n‚s:Ð;ù¢ç€óäˆZv+–Žî-æ¶)P Á,§Oÿø‘Jj[äš{èhäß°TÙúRí”k~ }\è–j"uvˆh±…ØØs Î3H/SU.®-8Ÿb€ïÎv%èez8qÞBanf 9‹åo¼]úü¨÷‹•{DŽu’\pQœÊ2u3è}ÓØ±ðdÎ7çXçà‡×Ý!XTrw»J‚ðLá¡Jh£´¬@âg¢®¯ym.SXDÁR§µ0À^¹<Ö¼‡‘né¾,>Û8T÷ EÓÞÜg§58·÷AôÀÀ.‘о1Ý(úÛ²ý‘¼?.*e÷¨EX×¹¨\h‘óšxŽ<ß隯EåÆEƒð!¸ø³ø‘þfp>ÛlŒ63è³ÍöEfÛío}ë×àúÃá§àpþFÐʯKËrñ}²î’õq ô3œÒòÁì+žÁ9”A/·7‹š·nt‰­Þ-´´\ CÌüÎ F³ü=ƒºû‹2ÕÇ­Ûúüºêç¬X°UiáM«ÌlG̰-Œ•Ùs2cV.œ{ê1¢5À½¬vmÙHg>S8ótb‹½Ò¤§&5›<–wØsdƇLœï³´E¦X9šà¼Þ7½.ÜÆ÷XþNóŽÝš}ßgÛM‘Ú©Ô®À<6¦«´ˆ“>t™AïàÆ=$×|P`Òï [€ž$€ôÞÎÕ‚â)€³Oö¾ÿˆ»€¼dÁí¼®çw6ã&߯[Þ[eõõl¡8ÚôïÁŒ)åïÌG8£T¨ÓrØ è°ëAVµL.¬2i_[¤ãæqËç?¢Ìz’Øs{Ûtoó¦×{Ð’s!KÜ‹*G*}g"L€ó:hÅ ú±™hSÝ? ”Æã\¨´ÅÓŠµœYò~ŸH÷²);…Öìùš>³­ÍõN{²}Š™'ÛnÇÁÊòÇV^­,¿¨ß¡m ÊÛ5R¾½ `'Ëgßp†Öýe<ùäeüöoÿ&º®Ãl³ÍöÇc3@Ÿm¶/BûÀ>€·¼å«á6Gøþ`Öf%Ÿ5¥Â|uTïÖRxbçÛBÖ]JJY]½Ç€:ÍAÏÌqΑµ9èÁäg×ù¯¥$>"À¥âo¹ª{yntÈQ‡ñ~Àm??Ý6·f1)Ë€ç¶%h±7ø~#mÄwe°¡GÕ §~ߎCj³oÿqLÓTð§gy‚t‚sV‰VNÝR]Ñ䇨 ìüþ”¹ïeêc>·©ÂxÉÉõ”‡6Kx‹}ÍMq8énXÀ£7ly¯³BŠ•åq¼’VŠŠþÖ<$çA Ðw`òýóA{ Èk`^ÊÚ§äï»à<ò]Ð>Ýß²Š=° óŽ©+àËÑGÓ¶n“Ǽ©>9Þ<çòœ˜îbAy]‹é³H½k«ñhµG%H‡uïó(°´ãÀ˜óÊ ¸-HﶈGìTj¯%ávÄMõï¼¥—•O»RY2Ì‹rVâvÛÚ¿…¶ìËÎuQåŽÜ”è×—òBõ6tÕ'£=v9g±j«æùœ·z-áŸ2L7È ‡rôv›óË-@·¹íõžéÑñG°\ü3üÎïü6ž|òÉ;÷e¶Ùf{Ùlè³ÍöEjï{ßûð=ßó=¸„¿Ž¾¾pì,Ø¥CZƒRëxR¢™‹¨eç>³Heq¶P€é ÈŠ³8[‹5ÊmÎlÈ~G-ÇBK”Ú—m((™îÁüÚ‡1õ+«vÁ¿Ô̧­%ðÙ¦œuþe —Lá¨Îõ.i`'ð¦ó>¢+œwaÖÛâ»òß2;?õw~¾hS€ôðí×:¯œæB®òÌ-8Ëôz/æÇÙÈ9¿ àœÇÇù2z2tµ„v¤LWAÁù¨Å¯b#9æÑ›#ù}sœ÷@[4Ð`«Pk«³ ôµÌ?Ï@‘Rëîî°éœÏ”v[Ðxg {çœå|«A©ž»(§@º¯ÚNv-ëSÊüü¨7š‡žeìYÖž™õ±ûu$Vã¼;VöíùÕ`½»]Õ_s- ›ÇïÓºDÎ×v€ ÝÉzæó^Õ¹Ô®«`œ ¾í¹?¦¶é²àÜÞ'E0«fÙm;¨U…ñd.ïU^÷…ò|Øš½æšc™î”#ÞVÛ›™œn¶bÅÙ×¢-ÒôÌ'/¶Z“ ë°íïT[(ØÛvàóš¤îIÎmï$PZiÀ÷â}ï{Þóž÷L_çÙf›í f3@Ÿm¶/R‹1â[¿õ?À¿üÅ_Å¥ñ'Ðà5 „ëêâµó@ÍEÚ˜]/¹­ø.U+n±U0eƒYòÎ"jÌá.Ùv@\QË*•9×6‡ÕÂè\]Û¦Ól;Lpâò9ÖÎؘ®Þ>ŨM)²ÓmAŒu²» F+nò‹Éë.8ß $Å=siÎîª1vYê©x”ݶçƒô²£» ÝÉrÿö›r¯˜wžÁ9̸ٱ«·ÌãÌjùùþ®Ç·^ vûYÞ[Ó ÝŽÃA:ïs¾– VÁW‰5SGºµô³Ý"1éVê£vY¬å5í¦Pß&@ú$à¶à»÷I ¸- ÚÝãÛBqwlïuŠÄ ŒÉЧô¦ÜÁžŸé,68©œ™`Å öß>­d‹?>³Êg ÕYüŸð—£­UÌÔÏùœÆö,šæø–oùFüÂ/üpîb»Ùf›íå³ Ï6Û±=ÿüóø“_þ¸ùÂëp~H쪕èÕ`—NªÍÝΟqéwÛ–Nk úy\ºžd¬b 4Ã]± ô³ÎA·Ç•Od;ÿ²Û·Ó¦~ÍߟÁFDé ѲÕÛKPg¥ªù\âÿÏÞ»‡Û–Tõ¡¿šs®½÷9šG7ݼ®B‚´ ¢Ò ÝM‹ðÑ ‰áa4Þ/`ccb@Ñ$((`üBÐ ÂE1à Q¸ÈCà6ñ"`¤Ññ^?h.ÝtŸ÷ÞkÍY÷ªQã7ªj®µö¡›î¦ë÷}笵æ¬Y³æœUsׯÆ¿Q”IW×u%*6­š•šåQ-v᜜úŽQZ ·!élᎠ/U7ôI'üìÞ¾ˆ~q³5q±BΜçes%éu¨¹¶n ¶š|ͤ|ê"I¢[{´œ eÎÜðÜ*¤3ê)¾\ÄáúDÎçF± [«ïáÉcº”5‹791.-è›É¹žÇ™¾mËæý9÷4áúë õñ¨ï)%æ>ÝGÐ1]"ôV_âÿ¹>Á6¹äí˜*¯±æ!3™û´nÁÎ%w÷Ò‚Îîí<þ¼Ž)Vpç0çƒbx>„»¬µ ³ë:»¡Gñ³”ß<."ä\,ç\_oËÙ1™u>Ýt¶°GWs‹ÕòƒÎô¸›bºéÓç.뱈nî\o²òkœ¸’hé7ü7¥\r*ÉtI¸o®²Àˆ®ûaœÁßàÏÿüÏpÁTžqCCÃ-FоÌñû¿ÿû¸üòËq ?ˆ£¸²JK­ó«u—Él ëv"Ê“aþœs-w4I±“¹éOükS—²Ýb#ÔņکjÝ›mJÿy"#+;éÑEýmëω9O¼-™Q×õ’¤;LÑZV —¶çô‚pÓÝȉÏ90Âq…è[´°¥ý’kyR,'çlI'Æjlk,çÜï{8·‚÷âÒÝѵ8Z|˜û‹Y± #´¿¨HýFÊj-ºø¬S¤¾²'ÍŸÿæ);á×÷2¼óïÄcó˜ Ç544ÜRhiÖ¾ÌñØÇ>Ï{Þóð‹/ý%,ü°‹Ð?ÙåTÜZ t":ï&Z+)Ó”€ËþJrk.ô5ê^‡3ç–új$…'G>ûÔ6ú¬¼œƒ'éú›¯¬Mô©ç˜ mV‚Où ª/Û4·ºz+hËUÅš[Åõ3bwG¹Bým)Ò¿”Vi¢O"ç×Ê~‰A_œ‰ â²+"Lþ3—Ós!ç€.Ü0Ö¾Ìj'Äbµ¶¯¢}µºXЧ8{ ŸÂ½T—v± —n×’þK¡&òšžÐºhWn‘\SaZÛ/´i]¡?æ.ë ÛGõHîÛBpûè’¶ó"§G[g9/Uë­{{I¼ù»ÞïÑÔW¾ëJ }¾pé³û!îÆª¡äL¯QRY†ñ­®Éö] OÆÇðÑâÐÅ z›‹+·x²È"™„qÈ8tB¾…p{%¹¼HÆù¾áâæNÉ­,\âÝé8ÉUÎ9oºÅÑ]«.¬UÕá3Ò>uñuåÒyœŸb{çC°–‡OÝ.OPÒÍùdi—¹¸óëÑy$Wö¨ï¤ÞT(\÷ÕP…hxú´„^þjŸ¨-±¯[LØ\vÂÜ+pÕUW5rÞÐp+£YÐîX.—¸âŠoÇÞ÷'¸ËøópO̯®óÄTÓ•åWܲ`4elU–ÉdãÐ×Y'ÂQâ¢gÓ ­³úëg˜ÒòU²…ÝÖ[Zåˆy÷_¾¶í „¦fEÔØQueí©mÖ…5“[-ŠI•kìM¹u–ôÒ‚ît➢˜Pgñ©â®.ñå}æ^Û‹•ü ³Ø 9—:©lºÙ[t!Î7ª¢SLÈmwy$|®b¼y² /¬À0…k"9WÑ7λúViÙ·ÏYÐk®ßs(ÇL]_¶NÖÛÏæÊιuo½/ ^Ø8ì½ËßuŒy±Èsa&<¶óû« íâ= Õ}=O©Ö¥ýI½½ƒ‹z‚“8sh˜‰,¤IÚ5^$“ˆœ†lÓø+È9¹º'Wuro—̹;Çsw£›[ÛSYg…çhÁ _d±®Ýy‹ðÌ¢u¼øcKòßù.¶OI½\¿Ô7SÑÞ¹k²ŸRÙuSÿ\zéÃñŽwü†¡ÙïnM4‚ÞÐpÁ 7Ü€oxØ7â3ÿïÎ[½v‰ü*åe²+ŠèANÝÖ9§8ǽŽQPN\ÜE|-'ßb)IƒÆmÈ'í6þ¼ž«\&Þ\²ÜYË™µX«“žG)|IPêmÐWhÙµšÙ ;Ûjü­L´å|5®±è:áç¶ŽXTDâô½Îm z7O¢ó\å]–¦É¸ÖJ~óXv8ˆ®íDêsyЃBåùØJÌ*n[í…²ûÇ"9?Ž[íFkz° ;xô‹C}ËIºNÞÙ*.ä±t¹.Ëæä|>'zî¢÷   Áà²y?ÞŒyWø¼ ‡«WkwÙ=›³ Û²5’žíò¾®k…½Æ\ôݺtB®ùžûŒˆ×ú„Žíuñ,Ï{Þ çôj‘ñŒ ³}A—ñâXwö4ÉÃAvë会bÒ´uå8ù—w´þ­ræù¯P“¿²mÌÄUõï„}æV€•û×ïᨿq½õ…(ý›âM½%<]Ky5 z8çMðÃá¾ÿ[‡}èp·»Ým¦þ†††/Aoh¸áŸøþðoÂtú¸Ëtut’´M±–VhI\Êur+¤4”Í­×夕S¦Y‹™N r²[›l0IѲ¼@P—epìû¶$a•Ò’ôy‚.çæXSk9·ù¼c² {ª›ã|Õ*öéýέx^‰ãI?Pº­WÉ9+¹GR€8é7\I4GÐç”ÖsK·à0$½F æÈ9[þV;‘¤‹åœÉyŒAwá¼Ý)’ò’œËw&ééÒgÉyº€›…¤óÛ÷™¬kŸÙÖÂÍõ„:j‹ELL[¯8[wtAlI•ò‡îä.ðµ–oã…ð¥ è}ŠWŸ°ÀHû‚Çw«B¼{΃.‹i¤QÓ˜èÇjB÷BÄ! EôHÀ r-èUrÓ‚ž¾ÚU«x•œëg=”AHºxyådz„þݡ椾znŠè¹‡“í:‘¯Õ[K Ê›ÙK­Oä_Ž‘v„>w€U÷<ìý8þäOþ_|1n}4‚ÞÐpÃ;Þñ|Çã¿Çüwà.xJA¢u¢êÐa‰fíª¸&´<ùak;[™K8š(”iKj.ñ:Ù@ÖêPû¼e!oG­mJþíbÂ&KOæçÚÀ„C¯Õº¹[+¹nŸÒ$ÝѶ¡Ú&M³¦dJ—Ö¹—m°ä\\ܽNäçÜÖ“ÕNT¡QæUæXõœˆ ²sVTž³àÉD]bgÙÍ}I¯–íC—5êÂUwÐãI­Ý÷Ñ­=’ó¨àîÆ!\rTh—8óKté÷˜ž,P’IKkäÜNÐ7Y2!¾ì¨œ<Û± ­ÛD¦ù]aË–õëïÍõÚÖÖ®Y—Âì⣽æ6<'÷,â«Ý.»–Zj5;Æu{ÏëØÃÆZÌYѽ‡Ç€IbÊsë9»º‹çŠx½¡+Ÿ¾&{BŽZøÇä âif¡kÜ)-áñŠ«\~;o…cç;xßCB²j:<–Ê¿Uuï-y&LÊ7õÉuVq×áÜšMÚÆKÚõ¿›ªkט‡j„ãöñ+Ýoáíoÿ=<îq[ÿ|¾dhA& w0\qÅø¥—ýžýìgcûâ.¤ · ‡?äÃÉq°õ /¢+|Ÿ\âó D˜Ëç˜&R RjÍs˜„÷üÈ?û<ëYϪ<熆†[ÍŽ¡áŠƒƒ|÷w?ï~×à‚ñyØÃÒ>µŽ39ß”~H'¬Û ŒŸÎ÷;3 ¹%±ù<ì.~xl+Êi»X%]&ýªÒnï¡ÄY¢ ®³ùv­OÈ@Š{-Ò0‰•›bY“Uü,oó+'AªÜ‚Iz®ü^¨Â#—+¸³¥M |ÚtR…ŽY$uZrÏ]¨‹ûòHØ¶Ü í"q÷árH½>ñF°±¤•ãÊ.’ÍÇŸn›‹?/Ë&oúœHÜú#¶t‹ÅñrBð¹aâ£^î—õRRd½æËnÆ6ï#Ž+ÏÀò<ïìò®ã;ìÓ\èÁò\ªµ÷iœO)zÊÊTÏní¯Þ.&d%ód‘°»ò òp‘\·AÜ×ÙÍ='èpôIc®š =« ±ëA·:ó€05ÏõÍüùÑ©‹wùö(Û°~a­^n›«WËÖâÜWøÎvÏÃc{Þö¶·`ggçmhhh¸¥ÑzCçOŸÆã.¿ô‡Á=ÆŸÀ.¾2‹A­eÁbåh"¤Ä‚±9ýÐ<ÙÍ£ÖáÜD¤Ö×· s#˜,Õ IDAT9˜s Oày²ÎeK’.1©léÞ‰eÃo©qÄ+Ø —úÜ‚.ç袕HùÇM ª/ 7Ç ‹UÜÄ çe%óœe~"2*{HTIz–«|Vmšþ­vmY€÷Ü®©Ó‚åè&±u.A—ÔiœûHέ[ni´‹cÛà0ä\Ðgqèëˆë\^׆sA­Þ9Ô¬”Ý̽›÷ 8\¶K7çLYö”ÉÇ ’r›fͦNëRY÷À »¦¬jŒ gâõ’/¨äœ !)"”XÜ  y–qRŒ)òN©.’ÕÈ9•M¿Y¬1x  —…¯’œo^ÄZ‡¹¿ å3ßfAª$å‡ß› ï‰YÛÔª>â/q¶¾ù_w¼ã÷pôèÑ›íÜ 7Aoh¸ƒãøñã¸ì²GãÏ?úW¸pü ìàži›OrÙª^³ôY÷9×V´æ±¤l¥b·^ÓaÜ$]®bKJ^&ŸðÖ‚>%Õfviå)èÍ•lÛ&ô:9÷•Ó¥Ë9©=‹Õ»¤<Ÿè÷,'nîbu_GΣåN0g9Ò_#éÆò„_â_)޼©b7[ˆûÔi<¹!çCŒ=€i¡nî«]À;ôQÝæ5çüæÁþžâÊ”=9ßl}›'çáÉ#ícr>WV~• iy+rÛñ¶„˜ÏˆW—Ÿo*®£~m뺾­æ¼^ø· »Í•eËyž¾PöKö^|[˜²,§–óÁ”cÚÌr‘,’s^PëBÿ³‚q±ìi¬:¦X MîÙÓº1<¦HG¸n‘,KÆõÎZÎ¥lîiâãbÓá,çrìÜß[Ë·Ë7 Ô;©ÿ Ô·0“èRøë=‘¸\|ÎcÄßàlÿ|݃ÿÞûÞwã¼óÎ+®µ¡¡á¶FÐpýõ×ã[¿åQøÔÿ¼­ž‡œ_„9rž»ÃÛ¸Ð<Ž\Èí`ÊJLŸµ¬s,¦¢$Û¹ÂËÄKe}vþr¶Ûô’@2I¯tµ”MIW+™NÚÕÝWÎ9D‹Ì6¶Ú‰˜œØy=£¤ž“ón¥ÄœS7%5÷ƒð]r,›¼Ë’Æ)9îµB˜8DzÔ+Heåwüž8ßà…ëzž³<‰Te],ãSÈ·¨³‹\Ro'‚>pQ0Îá7GÖsµ¤K*µUÚ'®×ÖÍÅëî«50IÈÇ)£uª8¢l¯••òºÀ3’(k›7Ñn^@:j÷ª\lئÖÜÂmv¦LÙf^+=erïJØ9æÜŽ÷šÑ¡Kž2¤wðIøsž/íbÙp]$‹ã2¹ºsì:U!Ϋ…~O¢sñþÄüãfÌ(´GßMÀr7´Ï(»K9`Bh¤4n–œÛ¿?cÅz^ZTpßßfÁ×êÈ6ù»b3”h;ôo ƒg+zÈcuZÓ¬åE€ð]ËÚkœ0â³83<÷÷À>ð>œþùk¯³¡¡áÖE#è €Ï|æ3¸äߊÏþí)Ü{õ ¸+DYÔ`;,ˆ›»Æúñ¤@VùûXÙT†Ý¹ët&Òj…â\åÛ’t%Þç2á/1—3ÛÁ|OÓÖ¹¸³e[ µº¢óÄ]!vÄ!MÜ}j‹ºÉ†g y–c:5ç‘TÓóüãb‘Kv‰t‰j"qK%ú&î•,«¹5=WŽNû+®*)—4U[ð„„š¿\ÜnS^sNŸFßóߣy \AÂõ»Uj×ß²_ÉxnE¯“åyÌ‘ëifŸ%5óuèv%˜óm±ÛóƒM8,A×¶ºô›'k½gï’y’^'èâk`ÃÊñ.äÜ.”Mèè¼]V¦ž=-Þ9rŸ‹KÇÿh¦PJ¯y+:aµ¨{­ðx›x¡k°‹cBÆF0nf4 I)]×ÙãdŽ ×ûBn>W¬Ü*>÷7ÛΞasyÓsl[Öæyÿ<Î ÏÁ½ï³‡k>øÜë^÷:§klhhøÒ¡ô†††„¿þë¿Æ#¾ù[püzàÞ«Žw! º‰c‹•Lܕȗ±o€Ln=¦¨ØÌîzJT¥,»‚01f’^#èë­áµ£úÅ]vH}¾Ö;tÏ|²ÈUó ñNî²l=' `cZ9÷2Ƕæ)ÙržâÏ}éê.è*Ä]0RL+ȵ-ä²/Ɖ§ßâê>FîûàÚî]‡ô˜ÌÕÝy ƒ¤#Twu±Œçä\¬ç\¶´˜Ï‘ vM×m ¶(ê¶y’"nö9É­‘!ÜÖ‘„s'ÉÖ“¦t¡Ws ïên¬mÛžtå‹€s±å<†§4†sònÇ5[ÏÙR.åì‚—)­ç²/,ƹ ì˜ÄF%él 7¼–M—FãZÐñúØ÷K²%„„ãÎÇE6ö*ñèb}7 itŸ¹LFîkž!Öû„­êë ú6PË4[¨k®åëB½xÁ+l³$ºž7±åRf]m[&\3ÃOàü –¸æƒÀýîw¿sº _Z4‚ÞÐÐ`píµ×âÒG}Žß0â>«gFww‰“³–8¶`ÔÔbë÷:™æ‰ ÕܺÌÇXËT¾o¾~Æ\ŒïaâÛו'çý-ÄÛNÐUÁ=l“˜S+ü&P‘8&ûò¹ ó»õä\ò×òš'×öÓºÈâЙ¤ç)ÙÒe“+½!’0GÎ9=“—tŽoá7)Ë„[¬âb]—²ûǽÊÉ9¹²KÊ´@Ò•ô² ®– BVœnÍ–µäÂ܆bL•d|N­F܇‰“vKšçå;á\PŽ—mê= +­¦ó$]Iø”½—æÈ¹–¥õ|1®$硬,ÂÕ­çâE£eA¡&¬àNcXD%w#É‹e•ñ'1•nsÅ2ž,âY̺ٖÕBØ1·´ëy¶³ž—.ãçJÒ×›mÊæãÃCs$½VïˆÏâìð¸ûùïýƒwãâ‹/>çó644|iÑzCCCO}êSxô·=ŸýÛ/à>«gâ(î­ˆ(Q“!YáGúݯ™¸[Ì ¿­#éóÖ­yBKá0ä\·ÛÉ9_¢û*»ºwÅ„<¿šfmHÜ9§‰¹D±ˆ”I³F±¬’Ž)'¦ÞÌ}¾cž·!ÇÃæqç¬]ˆSÅÏUX´(ȹ¸íŽ;Ñr.t:&–uã¹³ëb•*HÏ‘ó œû<”­»ãÖHºÕ‚¨¹°Óí,¬‡óesr®ØLLÖù›‡œçÖïÃÔ{˜Å·Íï˜uä¼ËêÍÉù¼ð›][¬-['ç1ö[R§qHŠXÏÅë%_™#é ߂ծŠ'0ä|ŒjïB¸%,$ÝdgÇj 5ñG‰oOg<÷t‚­ ò:r¾Í"ÖüBÒæ±•/¼ ØâžmƈOãÌp5îsŸ;ã=ï}W³œ74ÜÎ0ó†lhh¸#ãþ÷¿?®ùàð˜Ç\ŽÿùÉ_ÁWŽ?„#¸w…@°‹+GX3y™G˜dŒf2"ò¤Û²ëÝÏ&Ëâ[*ãù‰[µŽ°o²Ê—¹ ×•ÕôMê['%91ÝâRžrŸG–DŽ3¡wWvw÷èâýð‚ГËHt5ö5Nî]åYLp}Æ%’õªrt¬oÔú §D\,‚â¶ëÉÍ}¹Gâp}Ô³Ëû™O}PÅ¡¤eBn™5ðR .ÒCìË.ùãÂo!`6܃d_® Íc\Kåzuµi ¸ ³Kp¾¨¦¥l½u V7öÒýxîÞÙqâL{XLÊÊ‘6äE꨻µ³U\Äe{ë²îìȼitq.wo—ñ¶¢Xò ¶é~Ò¯kuLq¬Ö\ÎáC{ótiìÒÀ¸²OñLÜ7âg:lɉ¹<KÎ×Cz•-K†œË8éI…DKYuqç¶çaV*¡×Ê­éÌ5®#ÿ#>3ý¿Â¾ê¾x×»ÞÙbÎn‡Ø<«mhh¸Câ^÷º>ð÷áAÿ«ñ×ý¯âþW6)ç <è7Oî׃Ëç{¶!¨emÖ²•ÇpÛøÎ²¬ÜÍÿ¬uí\Û<-Û@bSmœ+ÇæwT&¦Bbñ¨~8þMîìbÏÓ¬!Ö‘âX¥YíL^óølÀ›Ëæã´/u§üÏ“ }¾ß¢úÌ*ÐɖȹˆVåjî9©Õvvq_…ÓÎ µZ¢jí=–‘B…ûÎÆ»¾[΃Å=,|”Ö5ž¸ËX¨ Þ¦t‘c۵ɎGµæ—c.ÿÌÉ9R]ê¦ÏçÌÿïM)'Êöªpߥû”ì‡`ã÷ùöÉ{g¤O½¦P§¦­«•µðÕ_vq«|“9°uÝQ™|Ìê6ûÉoQ±ˆ÷ïµ”[ë¹zÒ¤¥‰%ö‘²,ôËð))ÖXÿ²GWåiŒH*ê,ø–þ-Ô:>JINbÊýÎ÷ÉÃÍŒmó„|ñDêC¸ï–Ï&÷Ìš³V3:·¹å>÷Qo}êf¯m´Ÿ:FËóu„2K|§ú«ñ‡^Œ|à}œ74ÜNÑ\ÜÖâĉxžˆ÷¿ï¸ßôp<0MNåŠ~Ýj–O„Øb\ém]ÜK÷Ó2v•‘OƬ%c;³=OÎU¥¾V®\TX‡n]`=–Ø3×>b'ŠÁ©E0Ý+“>MH󤱪 ÷rN·Ö-Ul*¹¹/ƒÕ;å@g**Çbpy9§Y“ï¾SwxA7–ÝÆC „(°'+]¯V¹˜þ̸«‹àÛòHø\í†ò«]u{_¼ƒ[-àÑ£ÇYŽH£¨´«§ˆNÄÙ=]öžMîíB4'8šðç^+vÁÌ’~ΉÃÇÖß1å»…¿[ýuE¯Å”Qs‰=t˜R†!Þ¡¶¨‡1ÏníÒ.&íilz§qåý2~Ž´ÀFâ‹i,Òx÷Ù;‰Çbn9wãhµ«N,â)œ„½X²4†©®,L¥ºðÆ=…ÓæalU÷Ey¤§v8Ý&á6|KÕÓíb9ëe6ÿ7¥òÉ}NÇ·VSlJ{ðG8Ýý,.½ô[ðÖ·¾wºÓ6\sCCÃm 744lÄÙ³gñ¤'= ÿõmÿ _ៈóñà´¯f-Ï'HÈöæÄ¹®F.eÀsžlÛ‰t=}‰.±À6uÏÇÂ3 —¼ð® A•]Ò¤qÜ©Ç »fâ¶Ân‘]IŠWB-È…ß?ï&YÕÉÍ]âÏ1 %‘m©;].-¤m9iKüÜd6^‡o™Ð²µ.å8'k¹XÅ—{¡îý;…})}7–qd% 1çbë0h%å#.Ÿlu®w»­FÐk1ëóÇHÝBæâÖÓ}„Keô.—½$7J¬ëÖ>qJßìr¬ÖËrñN˸DHÖ¹çט×]ÿ`b- ]CVžŸ¦ŽO þ^R¯œ2-¢7yÍm\¹êOìð‹>dm„Æ–ó’8ô4Vãxäû"c’½[è.Îʸmµ Œ»Hi ù˜šõ\Èy"ä±ü´ÐŸ\$Î<&¹ÜçõºJÒòï3¿k‰w™cÝ–•`_§d=,`õÅeI“Y'ûø}œv¿„ïþîïÄßø[ØÛÛ;‡ó644ÜVÐzCCÃVX­VxúÓŸŽ×¼æ5¸'‰ûâ2¸ÊDa=9%xò‘“svײªe8O¸£ú,9׺­ÂòáQõmâÝkm°Ç[+ÞÝZubÝ&Hê4O“}!á9‘tT¢ûÑѤ<Ï9ÎBQ‰tÃZàzv_ßQ]Ò¬u™¥®¦_C‘÷Ü—Šïâ>knd´˜Ãv)gIú]lŽ„}bA?8¦VÂq¡ÿâ~ E®ÌΩÑr [¸ë› z®6]wAWòŸôZä¸ü˜ÒâWZ¸çP+?¯ÕP+;¥=R—.Éò¹ ;Ö òÖÕö6yé(‘¶‹f\¶^^Ó!ŠÛz›W®!*R—ƒOã[I}lsÊuNãm3)äÂoÙÕ«d|ä aëò»@$ÛË#:†r¬ö*ã”-ë›dæû[ˆëæE­º׿¿?v¼ xÁ¬\ôª‹:†(Ñ©›EâJÔÕÙëD]Ë;xL8ƒÿ„³ø-üÐý^õªWaš¼TCÃím744l…aðêW¿_ó5_ƒ«¯¾ø<î矈 W»îÐä<‡uq–B+Ë—–sOxBû·ÃIÄá¬ê¡ ó„£Öæòø!#%¡a Òýˆ¢K"ø&n&ÌLÒcm¡lü¹ô”¶Ir+“Õ¼P…Žä@RsÎI÷,œæ5çm5!Öïn©ä€S9Iüy² AáFp)¿ój7Üï+­tͬrNÁ"7¯ÊÎzûÉzœŸ+¬{û¶Çl&ær]Ò‡Ù‚ïá`-æÇMžßyžl»ì\œ }ó¸ *"¶>íâ‰4O&øRW¾Ý&²”Hu2TÓBË©û¼7 ¹1»èÉâ]ky6ƒ*²²qˆ¹Í3ÁÅä•Rq¹îâx’, ãBëB}BÚ§>”›†TŸ>ÿyU}^D’gË}®Ký8÷Öšwm·e¶òw,·%9\|/h»]òzÑsçmvéyʹ| ƒô­ôËö5¾öqÚýðA¼ä^‚ÿñ‡sç²ÝÐÐp[C³ 744o{ÛÛðžôÑœ‡¿3^‰]œwˆ£ç-èÁÝ“­U9eËÁ<9¯¹²ê¹eÚyËOdJ‚λyëXÐG,h›ZòFìÂaÂG $)ZÐÝ Öp+©wâÒž[Ò'›Ê· AO±­Dôe¿ÔŸô¼ 5ˆ®ïB}SÎã;¤øWv—ËŸ¹ ¡_í„}"b%*í¤Æ ¸¸»)ö©O.îÎMp^„ÐT=Ä€¯³\ë²RxÊ6®\¿3‘剾µ ×âÙÙ•¾æR;gA/]г[‰OÝ…Ü'«e~ oãkÏËp¬lí쀵°Ïåyfr¼ !ÏQó\±–q~ijC~ÇL©œs“ãDi]]ÖÕÚ>šØsÛ¬êîL½!ìÅcrao4qådg—õ\ï@á©’ê‰÷DÄÜܤD}\ iKHZ´å^ð>ýŠ)ÆÃˆ¹5Zz¬¨ösŸ“wrm±e.m§ö9_=Ζ› ‹Ðŧª1aǶu«_õÛñ/ðfŸl+I·ýÛ %uÑUïňŸÇ©ágÑ/®Ãû7ñ]ßõ]Õklhh¸}¢ô†††sÂG?úQ|Çã¿×_wþÎøý¸î³ÅQJ¬žú”âJù$¨»¬¯#èóÄ@ÊI=óñ¯ÜrµÂo.Ïíåvå‹ |-cœ”‹+¬ôðo'µÀ {±ünº‡£ë1::'OÔSŽdvA'.Bp©=wžTßÅŠ¾ Û“à””÷Z*n¶[[Ð3$’µòM= ^B"V»H1çFiºW’î] ç@ ð¼ç΋õ+<‘KÎ"XóVЉº‚Ínë–Ä3AÏãʹܹ´Y\¹,ÉÂ[el™ô…YøŠõë­sº€åãÙrOß"·ÜK"Š*÷û!õÝÜ ¬O†—Hçž³[KqË:rnË­ËU^’ú0Æv[×·ua‘a^|°í´cN•Êv¸Ø×ÅrÎBƒ kQXâ85ü[\tÑð»¿÷_ñ =¨z ·_4‚ÞÐÐpθîºëð=Oüüñ÷›¾çãï­-¿.þš]=yRÌq˜ZGYïÜD^÷É«N­u5KÅjV˜ùc4ntî’ »DH˜LFHJ zØ×¥{bUɾã:LÎY ¨åMD¥Äêí¨\7Æýd™3å'%¬êÎ1æb}7·b†(lƒäê.V>Gd›\ÞW;á<‰tw*ö6Rùýc¡½Q$Î;`Ü —€U"¾BÔûd%³±á:1·äÜ’âÒÒ®“ôœØçÖs!¸u‚n­ó¶}]r»EÚ—[êë :Ÿƒ•´·'æ:Îj‹ZLHÊý5rR[t«µ§v¬aFüxÁ,Œ/µ˜O‰lsüºŽC¯–œÛr² 6Qç÷€´aÄ./ã¦&úv‚nÈtöIw1ŒX«x7BاJ²ÜK» çÐgÉ Yü¬Ž]¿€šg%(ɽZ§•¸oó®Ññ(×À©ýtì颚.ÞñøÄž=Pøõ^0¯õu$,Ç„´fïÅÉî—ñÞòÖÿ‚ /¼p‹ëmhh¸½¡ô†††/ gÏžÅüÈàõ¯=î…Gà>¸Ýa4æ"_TȹZ¸¦,­‘­»´:l‹uÄÞ‚ ~yþñÈ…íòcæ§ä>¸Ìâ¦nï‚%Ž˜öO}%,Õy`!·8ùìF‡.ßE]&üœ®©[jü¹ðd’N÷è‹!æb TøM\oE0Nº¸¹/w•x¬v`r5O½ZýV!,ÞÁÅÎ"äfc¿5ö\­ç5²M—œYÅöò-WK¥d§ËrK»¸Liå^·@P>A¯y&èµ”Šµºó¸õMã­L‹¦‹iŒš|í™ÔÊå‹d<Ã1bAÏÕÖ4äFßI,gmjDõzY¤ëb%wöšAüVô<3B<Û¬£„²lŠUw€Žrš‡,É­ÐÜ4÷ óþVîUb½Í{·Ö¯¬&È:B^³’ òøvÀ†£„ý.î·äœßå{Àã£Þ]Ñ?ëãCúé 'ñzœÆïà)Oy*~í×~»»»hhhøòD‰khhø¢°··‡ßøßÀCò\}õóqÚý-î7>ÑÄ¥‡‰%ÒL`&²¤s¹`å ÂM·È—ß!ÿøœ(Q8JÈIÝ]-î.NÖæˆL8•†­³òËÔµ>q”4NZ»Zѹnž˜ê[0‰4³…-å7gá7šôKÙ…¤p•°;»‚®b=,9¥gç5(UÙÅ•vŒ‚[²ÿ€&ªË=$Ñ7±¢Ë¶ýc¡L¿"r¾Jd¸Ç*=#!ÇÒG6¹m×S;­'ô\nÝ~[_¼e±o¸H{Ôò/û;ˆíN¬ýjeÔXïuô[s÷¤+îƒ+¾×ÀnÄæL'ñ·ncÕÃ$½šG¹&Nµ &G2ßèHZò”i@´¤KìùN´ŒïÂ7\q'w4}Z$9’BMȹ|ï“¥,ô™žÜÅ··ž¦|ø®uæ®íRÖ–aK¤ºùjœ»Ïêž²ýÖ¢žŸ›±ˆ1ö¶Î›Ï‚. RËæ¼lÖa!¯Õ“s¶Œe*49†I7 $^ޱï/ŽQW‚ž—ÓºKÔ%û@ž.Þz¼dWˆøÞ ãOÉ]´¿‹ î’^pŠž(² ã§?^'YÝ=–è½Æ˜‡~¡â}Òwd‘“û¹\¯Ã„ H\7âRí)½g9ÖIKÒ19x1——Tä½ë²1’‡ŠX zت‚p¥ðb µ¿aµlr/öñ!œì÷¸ð<üç7½—\rÉÆs444ÜþÑzCCÃ͊믿O}ÊSñ»¿÷»ÉåÝe)†Xœ ²®ÿ¬8œç+ NÒ@õ†-ù$L&v\Ò§I¥Ë¬ólß–I| %¥¥Ý.ää»–·]ËtIŽÅ¨ÔevÛ%ñé é|ú-×8Là ¾Ï&ï[ÞMQà”×ð[$ýÅ”OÁI˜();'bžOVÉuPá7)+ñäÞ¾Ú¶ÚU àòˆºµ‹zûj'¹ë*9×TJ]J©6ÑÓ©YÁ¨ìB2B,$%'ôú)ççòòÝtKô;C˜öNq”ð3¨»´pŠå³tË×ëɯ³žßYµœ9¾Ç¡ÿn—ÿœ]…çÕÞócæÈ¹ŽQ“*æÆ‹ƒLºÃØ´ñâ‹TWØ.©…œ«zû”Õi$}ˆW 1#êxÈÔ{¢Ý`t)&ëÒ>î$m · ï–@v»HP]ܶ€ýôžï+úÏC‰¹¾wõ^¼ÍOsï)&Üü^W7m·X¤mß ‹qS,çÒ1+jû˜,겿Ç-è¢W~í6Ö<Çü‚Rîyå±Â ü&Nâ-xü㿯{ÝkqþùçWmhhøòC#è 7;¦iÂ/þâ/âùÏÿIÜÙÝ÷ŸˆÜ2QV‹8t¬†ý¬ \w/á‘¶n‡ùkŽ,L©M}šd ò©,ÛlJ«zé¾®V7]``—Z™Š+íöŽä>Ë)Ö„¬â„_bZåþ8Ó°„²8q XГ›:ųʿ”†mú}%I™}„I¿¶ÉR.ûæÜrUUO­xˆñçâ^;‘®ä3Ÿú@(¸l"ïä/=æmr®2On+U¦+›³œ—®ïš¦)'Þú;·¤ÏYÅkä¿f­ =kUÝž“~nwNЕôÔŽÑ{&Ë|wª= IDATŒöÒñ‘Ÿ²®rŒm«¡Gè™õj±]¼TTŒ2IˆINÎe,†ý.Õ3ÅÅ3;~­û<[îÙ2o•Û9m!õMnír/|‡äM3F!Å´ *"!!Pëp ǘóBgeJ;öÉ"‹o^âO—Ê…>2Ñ»Ÿ°.–êí¼|*ç·ïy©KÛâ‹raaŠ-íríÜËýà :Vùúµ½sñäñÙT 'F|Çû—cßÿ%~þç_Œç>÷¹èºsÓYihh¸}¢ô†††[ ìòþãwã®x ™>YõvžüŠz¹Le"ÆS¸|’SŸ¨Õ T^F-xJ"æâÈù»/Ê:s-öZÙµ• Àñ÷ZVÉû˜Dò$þÕg=¶eqXìÛës°s:6Ï«2{¿$è–aû°ŽA9øhE_ÍsçÄäbë¼µ„×ÀBoüo%¤ÁQ,ìŽu¶¢¯b:ªô]\Ý£êô¸“\à×þ âk"Çd:ÈŠ`Þzž‡P¨«9×k-й»8“è:‘fX·uµZv•úçº`ÀÙb[î¬ÛE8ËZk Û‚-áÒ~9ó<˜ø#µ§æ&¯ãSß%–hëþv ò¬ï"»èȹÛYYG¿¿¶^Í›žÚ+sNK˜§Edö\•]âÊùÞðXd!ÅÂÄ”S¤uQ“ß½ Jm-ÊL³¹I±)0í¢héU[äÉ÷×Lj.¾iy{mñÍÑxìÀ®îvAÂ^÷¸ÏÙ–å>pƉþßãÞ¥¹´74ÜÑzCCÃ- vy¿ǽq9z²<uec8u"27™š‹ÿ«õ9Ò®V0VbWXd×–OʵìNÚXS‚ e–DÐS[vN;g² ÷ÀîIú-1æûQµ=nt» ¥[#2Ϩm3 r‰$«xOÄ!Ƙ{§ÊìL¸Ç…ªJ‹eœUÜSY§„>–ucŸ ¢I¼y—Yº¥ŸtUuó9’®ým] ú|½Ü7çˆzÿ:W6OɯזcA—sÔ\Úç¾nIîPŽ1^$ÓúxÁ‰ú4#þ¶"Ñ·Ñ,¨•*îœua,ê`ÞžCF„ Ëx=,æÈÄRNrº ÄTûcÀÁZaÁÍdy¦¹e¶¯Ï.¬Õ4T¡V6_ì•sm+VÔÿ®¬#çÎà^SxGsiohhh½¡¡á–Ç4Mxå+_‰«®zºÕQÜgõ=¸3îöe.¦€µzmžËdÊE‹Œµ"Jžñuºu$ݶ§nE—ï¹Ò: ]cÊ'€Ê²è›o‡)º¿÷Ñ‚>@,85‚žÚºs*ü Jë>¸¸ç"pýRcÓ“å,!-®2>&tèI“ÂA-õ¹úüa`ED–¨ïã/p|øpÃq¼ô¥¿€ýÑm.í wp4‚ÞÐÐð%Ã_ýÕ_áiOýA|ðƒ×àøfÜ—ØK“lž˜Û¸n¬ðÄK+ñP·Ë9wäõ.†ZšEêl»¸ »«êYÔ".nœÁ*Çq°B æÈ¸ö)“Xà<p4]gºo;§‚µ< º!éÅi½0Vrï‚K¼Äµ ‰ïW(bΤxØ5‹zs¢ ;ßk& >æ4Ÿ2Wwx“Xsù>Êv‰+kùB-…{—,îK*ýAc®W¦ïÈä]Ó-Yk _O÷Ü’„Í îžêc"b‰ü:BÇÉòñy;¬‚¶-£NØ–„éù¯‘+¦Ôç•4±§‰$~HÇ‚Ê Y’í «ÜÖ:)B]u-Û|—ˆ„]ÚdÛx¡L=a”p³Hœ 3ºD¾¹,ëFpû,)—óh–CÎS–…¹ûA¡+nФ›ÃL ž(’ªm\nŒ B¾÷qåxðé>ôÉÅ]C†èâ.¿¥oðBhùŽö¥Ä碱îùÂŽ•³Ûõ¯+4^^Þ—¹:<÷ù”t¹~¹=§¦<³ öºÊÅ$Å&+ú„}Çoá$~—<â¼ö7þð€TŽihh¸£¡ô†††/)ÆqÄ+^ñ üäó WÜ{õ}8‚ûf­S=äK¯¹OæäC÷±2»Ý_›XÙEý^[@`ëˆUcV÷Yq…`S¦Y2ßÃ'÷>ç –@µÞí¥{°êHÍ\âO÷N»Ç-A‚+º¸°‹’»Ä¡6š›`r›×„ª¶AÍÅV„ßò}°9˜¥|ŠAˆ$¿Ð2òéeyEÿuÙïœ ³hÜ9¯-PX©\,Ën×}ö~ÖcËëdTNmËÛPñXàLê·yû¸]9–6Ùq¤„I¬ìÒKšô*:ÓNÉÕ®W'#ßJÒåñ‰OàÉO~*>üááþ[q!¾Έ)mãâg'Œuî”ztÍe¶$çR£X͸}5¶ñ(@󕇉d'çBúTFÀ©›äßDÝÁcìˆ09æ£{'Ý‘œÇS±Æ ûQ¥Ò¨õ‡ãÏÝ„”ÿ<sO¿·±t:² _Þ!¹¼‹U\¬ìâº\áeŸ±¨géÓÌqˆ„}$ g„áÄÅÕ§ ¹öR<ŠUÞ× ¶•e„àp¬»÷Z"m ´]8¨å%Ï­ôlõÈS§Õ¬Ôu :«Q« —$H-§li—þZWy—ë)·«Jv9Žu ·!œSÓ$ÚŵÜXÀEk;§>WÓª!áa…ߦnž/=¹­«†ŸKÔàC}רÕýˆœºXæi?i‡ªºB^•|÷õ`=G|ž+ˆøŸ’YÉ|gžKðœ8«¸ó³±ÈßÍõ1Pƒ%ƶæqy¯r’¶ôäž®‹nv¼’®éÚ¤víßù"îœ:{=ô‰ïF(·ÄMøÏ8åÞ†‡=ìðº×½_ýÕ_½Õ}ihh¸ã ô†††[ «Õ /yÉKð‚¼»8÷\}Žâþ¶#é5â"Û×#ŸÔ+¬%\m’l}ËÅàÔâÖWË‚ÎùÝe"¯ÂTcTgåwÒþ!߉¢6Ôʽw"ôìŠ÷w7µv±Ìu«`E—:rWø\œj#Awu‰3§ør8]‘[ºw¸ð}ŒByË#±bÑSÚ5²¬§tkÐt9—³úÔO8ž”]ÜkBqaùÄNðsrž[ÁçâÕëÄ~]:Ìv-Ÿ»¬O•6Ìô:‰=§'ÛÀÁ‘ÅrnP+¾%óæÞwêe°üÌèÞf]3¬Ò"‹¼• VT/ŸñغH[Œ»jŸ'èê/5g¥Þl™Ï xlKicážÔ… ¹ísƒæB°¸&Î䬿ŸÄ‰á×0â³xÑÏþ ®ºê* ÃP9¶¡¡áŽŽFÐnu|ìcÃÓžöOñ§ú!Üß„‹ðx 86KÒçÈŒì; ¸~v[W Wg&Ÿö;Oøë=ˆ¹Õ&ðMøÕ^Ü`Ãwv›íÊ;Q­'õ»™H“éÝHе¢w£ô\(î°]b¿b%ÖÅ‹#‰¹…c#!ÔTÜǰ”2Xª÷|È…ˆ‡ª™w†lsš³JàY(*'ºrìéÍS§•åÛs=žë--é]±`˯«"w€½žú¿½6«ëˆXMØnæÒd\ qü=Q.ªÙ±-ï^lËÕÙ™ØËbÙÊdZÐïë߆TFÛ7' bRƒw¡Ÿçp«P£wYß´ý_-àÜÿm?šÃAç~‘ï©¥)ºÎ‡öm§k±MΫԢ.ã{~Ak›Iz¨‰SµŒÃˆ“8á~'ý»ðЇ> ¯}íkð =hãyî¸h½¡¡á6qñªW½ Ï¿ú'±fÂ=ÆÇãnø ›|ÏOζ™ðçG09¯Ožy_W„¶jÌ:‰G2AÂ-®ðbâœæ¹ Ô$ îÎctäÆº{*µ6aWDâ"¹N1¬Î*áNiÓ¢Õ®_…}©G×:Ý!k͈F‚;+Mç©ÓVqqÂ;`Œq‰M—¸õq¡ä^ÜÜá”ÈCHGž»¢—ļ$¼Ûs©O·¯·œK¹ÞÍÖóyÑ0¶¤ß\]¬Xkl}\n^.o‡¸(³ðW=ädAÊI·£‹b¥V„”×ñì1EAG@CLÂøwõ,4'a+\¯,ŒÒuN‰œ—‹áÂEÛ!SùÅŠeÜŽ9Û'åþ†èw ÔS"ˆã>²I¨/„6,·"èó S›¯äéáÞöQq¾ln³‹W*@'×¥nþ5Å:òм…5 ºýáq€“ýob÷ð ¿ðb<ýéOo±æ ÑzCCÃm ×]w®ºêyxýë_‡;÷÷ÇEã÷âã\È9Oâ7“ó` Y$"$¹’ÃD-Od§òïB¼%>t‰#éFìÆ)¶'‚¹ª‹ø~¥qä)nÕrž«¸ A§8t¶¢ÃkŠ')ë]–b­qS·û‚XG2Âp!êSÈ»”“}RN,ó,"—DàHhn ñí¡”1åÖ½UH‰7ûĦä&w¿­‘ó9kûv–äy¢õÝÖhbim÷E½k óÞf'..Ãú]Ë"—§œC$Lªš­×´ß¶#­–ôàV/Öl/So ¶hŽ‘\;LÙ—è@èB ©ÉÇ’Ë9ÃÝVÏöx©YÑ¥¯ŒqÑNÚ>˜œ¯¹­ëóvé~ TÉÜ¥þ¬ÛÃ=¢J;€D|Ãóš¢Ë7×+ßõ9÷X¡#ÂÌ÷›½j}oÓ•–±®èêvÎnêLÆAíÌ+¤•žÎÃãOÉ»,npH‚] âíuëyhQ}ü Nô¿Ž3ãµxò“Ÿ‚—¾ô%¸è¢‹Šknhhh¨¡ô†††Û$ÞûÞ÷âÏøQüå_~çûoÁ…xz²4èté0`ržÇ¢Ö'Û#`'åeœ¸‹“Æ¥q™¡8C"ûÁ·“êbåèäŠÛyø~&ˆóÎi%§ÝJÉôÞ‰@Ð’.­_…ÏÎn¥„»FÐEÌÍQÝI˜ªÓãòæ’x+²³¢û(Jëƒ_#ÜÉ ODÞ(¾Su±Ö{IßÄ“x&âòÛÆ›ËÄ>WxXKt>ñ¯»Âë~Óʱµ¼ø±Ñ¶ÜZoë×ãsëäüqëöëÕ©{p=ßõ:löta²V’ìpn±Ì:ˆ…Ý’¢¼\¨IØu‘­CPú–ú|$”²¸0&oDk¹fW‹¸ 5†3ŠK| à ˆÇAî^ŸÚ&„< ¿UÒŠÛE æÞù¸@ÑA,ÛL:-QÏÝÛõ»÷m,èá˜Õ¬+¼ÞOÛWÖ‘s—]“-Ëý$_³}(<Ÿ>]‡vö"û2¥}s÷>'éµ #sîígðüœroÇW}ÕWáWõ?âÒK/]S24‚ÞÐÐp›ÅÁÁ^ö²—á…/üi¸q÷X}î‚C4wkäg,9gƺûz°Céd›‰ú”Å©²ÊûˆÝ4£H\8^‰€¸ÃËdqIU\7úü° dÖ¡tEÅtCнU…^p|ùAçJp«È œµ¨»xßY¥=ÿî®%mwJØW»¶¼!âYªµDÜ;"å™užÊY‚]h&ãùäßZÔóýù>yÒ9‰È‰ö¼[|Iˆêž ÛXÏk¿¯wqgËsjµ--ç@©Î®íg·÷¦f.-•¹ËºtN³&! îªê¸õ~±*îfñ+º¤ßc…]C²ÕjÎîî!'ºdxôLR\\ýéOãYÏzÞö¶·áÎýýqáøxÃWªŽ9b®ûçâË­˜‹?qL꘩?fs!.öˆKªÔ©/j5—:ƔڱÃ-â>µÃ¥ãëc–ë²Êîë_ä½´îðœ$¸í§¦pžo¡’S"Îa9B½e4¤CHñæövÆ‚ž?Ö ©óuc€µ Êqe3$Ì-z1Ö¹¹Ï¡ìsÖCËé¢ÏYüŽ÷oÀ™ñ“xžˆ_þåWà+¿òpŸ 744ÜnðÎw¾Ï}îããÿîꌋüØÅ[7ÑÏÅrñ7Vy¶–ôT.“ârseëô¥ZÍs17ƶ]ê”ýÎkL:`Û,þ6§â.Xª [‘}E§ÕžÖ¹Ü£|ç”û&ýî"ј#}!§n²Jr±Jõåq®¹»ïv"XëDâæ‰DÝâ¾™ ¯ÃPɃ~sbÛvÌ ÁYëåµx¿%ç¥z:`UÙsëúH¡(åxÕ, ¡î‰êÔ¾:OÎ=ÐÏe> Å¬bŸ’s`^ø°nëÜ—r·uþÞ〈ü¸v¡†¯­[ãâ>§ð¿Îsd)ÏË®sy/ÃKÊc6aþï„íKâU±Äu¸É½'ýâë¾îÁxÙË~}ìc·:WCCCÃ:4‚ÞÐÐp»Â8Žxýë_ç_ý/ñ¹Ï}w÷ß„ ñ 8V-¿nÒÅû€\A½Ï&ñ;©ÆÜµ=ϯ¼"A(k=ß1~×ÎÖóÞLvÏ 6ýÙ°o]Û]ŒÏÌS¬¥ ­ƒÜº™}b¯¸º§}BÂ]´À/ì6±Ðû.XÒ“õ|GÉ>«¾K‰¨okA·$@¬Œµ‰¿Æï–¾Ò6'óD}ÛkëÈú\,|Í¢¸Ž¤°5Tê²ßeŸ}ÖÛYÄ7ÃÆòê¹ÕÅÝÆþʹy!ͺ«'“lÓê¦,ô]–ZMÆ'DZ‹ÕfWHçfK²ØÅ¡!ko/hEëyng2mÏlßΧ–iǬo"èŠ`¡·*îÚ7øÞÚØòpþŽ~ÏsqÝ·â–^¯›¸½5W|U÷W7÷p}¡Œ8‰ñVœrïÂE]„÷âƒ'?ùÉM½¡¡áfC#è ·Kœ>}/ùËñoÿÍ¿ÃÁþ„óÇËp.AGD×ZÊyÂ_’ó\øÍŠLY!8®—I÷dH7çUöT[–-4‘ t+«º¯‚oR<8ô ú%€(RÕ ‘ˆ¤bç´ºœç1æ›TÜs±8!æïõ>'ïYè-> ù.éÔäüI¥*esò="·À9C,á¶dÂZÔ-V ýQ®sΪ¸Ùz^m/Ë–¤&·:Jmòn åØïZL=_—œQ£p%FÏ£ûò£C¼´ ql—öwí·ÎîÇáªY;BØF,q*âÖeYzÃJâe!€½cLÎu ñcÆ‘Ÿ y¢.¿'í³|?XŸ0!–œÛ¾'¤[îmî..ýWb´¹oÏ îÃe˜÷AOiÖø¹:x¬°€p½Úÿ½Ù'×P–µqãåÓã¸l­¿Íõ_‘µN¼]8’…žÇïãdÿ_±³ëð¯~ê_âÙÏ~6Ž9R=GCCCù¢ô†††Û5>÷¹ÏáE/z^ùÊÿˆÝ`u9À%ë—+&[ì²i-&¹+¬ƒÏâO¹lHµ¦ûŸ®û¼^ÍoŽT~– ÷¥u;wWò¼{’ò£W )|Sµ±‹ûR­}¾·‚Vç”àkõË-æ@ ‹Š»(¶{%X„Z/“p£ÜÞÛzsËz$ù¤?œE&ô?ž“X=¦$Á¾²¯F"´¬œÓ~–@7óÛÂ’“ueµmöºÃU±‚Ww1—º]$Óc*ËI%üÛ–µwÊ¿MéªæÓtÊùDðÅÜ~“Å7^lË˺Hþ…drŒ1à£ã‡ôç..œEK²ó0éÒ’Z{ì£2^¹¿§±Ñφƒäál=Wc]ÛùÞuEÙ‘Ö©Û(ÓÅ&gýbÀAtOBÄÖ|R»·D›û‘.š©âºÜ·ZYm—<=¹ÇºèÀm˜R?}n®_ÇG€yuvûÝã$þLJßÁÊÏ|æ3ð‚¼^xálÝ _ Aohhø²Àµ×^‹«¯¾oyË[pl¸7.X=çáïÑâ¥.°5¡Ÿv‡UKЏËZ5÷)³ç“}ùÍJ¿¹¼ü¶q³y»\ ÃV\Ï!*îb‘ž:µ¼O½€ë$—z´z”ÞÅš7ìa'‹ ”ŒP0–uruçøZ)“;Ų!&ÝaÒ.×Ç ÉžÓF©Ø±ž×Üfس’%±ØŽ¨‡óæö¹X]q Ž—Ž<5Tî  ßKpÙÊ­nÏb]f$mÒc8ö[Ëo_V“òîµÀ³%[êÉÇRY2ÎGìÀÅ”[š-´Wô ÄJnÃMÔó¼óH^%"ª(¡#.þ–ÐuÞ&Þ¡Ôj‚Þeä|>¼"ì·ú9á¶ýÕ£Lµf½EÖi°%}À>$ ;AÕÐÕÂzoÔèJ—ö¸öW´…ãËK·yy«O±Éóä3é;_è¶ý`—út½NáÏp|x+άþßó=ß‹Ÿÿùã|àì½jhhh¸9ÐzCC×®¹æüÔOýk¼ç=ïÆúûâüñrÜ_%ê–T &CäóTk.ZµÃïðɺ¦.²l™c0™Ï]䕼«›¼A7Â÷äÞÞ­¬}Ôb·³ÁÅÝ(´wD."Ñ5–z*/$»‹å\Å=G!~å¢Ò»\§ŽBKŽ1#ý#Å«Oƒˆ“m©ìŽuÎH¯Ÿe"Q’QTn;âS#!–Po§î*åëõJÝú›ÝúÙÂYC?#'Ôg® ›ê•TháûŽ’+ÈÙ¹Íyœ8[á=\ÌEʳ6Dø¿OÛBY1åUøM¼\ØÂÏBè£x õÈS®Y,«ºq]§~ï³1⻹@ª&½‰œ—}Úº•—}z¾nµD×ã©A߇X÷'ZØè±L 욯õzª‡ ¤Ïrœùhúˆzz¸Jÿ/! As°Y;ìâé‡Sø8ŽoÃéÕ_ãÑ~ ~ög_„K.¹dí9n.4‚ÞÐÐðe‰?øƒ?ÀOý«üßïÇú¯ÄÝÇËq§HÔs ´•ÖnZƒUzžhÂ/å9uÚ„ÆŒÌÏ-„m½\‚J˜xtž„îvOÄÑ…v.Õ“‚ÎyÏI,nêÕµ}f¡ ³|ÈIzo J®ÌžÊ³{Ÿ•'Å÷iÈŽ¥tlÙõ†x’§›…¸ruöí-”›Éöz«z¾¨Àûsb\#ò›¬¡¼hÀä.·¢ç±(È Ÿµf9—ç—·;´ºO¿-Q–qbãÙ}!ÔÆnï,êÖeÞ0ŽÂ!&<¦mº4AÅ‘úkfâhá«r_ì‚¢'HæîNeÜÔ™ «• ž!‚9w ì›,~ ‘–k/û?ÇNc¼èR[ØÅ\ëÒ~ªdq`…r¡)s÷U+æöçC9X£@ÏÜáþ7õÿ §ÆOá[¿õ‘ø¹ŸûY\zé¥3çhhhh¸eÐzCC×-¼÷x÷»ßýS/Àÿû58Öî6^cx0\´Ž+ºþ2"à°$‹\. —»« Þ+>ÄçãÅp )»ŒóºI­ØÉý\'ÖØ;þy MN·e“[§XœÕýb\§Dmê߀\ KXùÓ¬¥òŽ~ºM,õìŠÏârù9½ƒËÚj­V!»FÔÙͶ+Ê[2¤®¾s ëöw«ž“Œ°Ý}¹=¥‹¼…¬†’ ×I´žE]œ7Yçs½¾µÊ{j}®á²sÚ✘ Ùž ³´É¤N³¤€dì¢N÷ñVì8‘}(íz+úDš q<8¯Ï'_ê³ìåÂSži`4ϧÇYð=í°,úpô®#èáÜzÎRxQÇŒm ñ±èŠãꨑtgÊK_’ï¾úŽ÷8‰á¦þí85~ßüÍ—àç~îExô£ ç¶|·5444ÜŒh½¡¡áËÞ{¼÷½ïÅ _øÓxÿû߇cÃ}q—Õ¸ Œû¸) IDATRKQžêlBg\gÅ£dBÝÜB¾eÉÊÑJØó˜ZÅä\6×gë9ˆœÆ×¸± oug4fÖljí"KáÖ­³žŠ”R9IwÆÕ@…ÔÄç’Üõë?€ÄÆR/mˆç„ JÉ?%œ.‘mÙDZÖZ>€U³Õ"éS}yù™Î `-_sk·¤^‰tn}´d† “î-ص}rÜt¨òz ΔakføÝÓ±LÜ•°Ûøó<ÄDÊBL²Uf—sÈy9Ü%YÄ;ZŒéÆHÎiAŒ½Qºœ¨Çç- V¹8b¾eDãbÓ4€­ôœ«\î=÷±|Á§ÏÜÊó'p6=%ðõ¤üÀNc§¨/Š[ºíÃL‰kc%Éš¤ý²'ïgRø?_Êk¶ªìª 2á>Š/ ¿S«ÿz䥸éŸy!.»ì²FÌnU4‚ÞÐÐp‡ÂûÞ÷>üÌϼï~÷»pd¸ç­¾ GñHtä¶î ñK»íŽ&ÿ.Mb•œë6׸ ý:i”ýÇŸË´6Ʀ'r@¯ënR¢+Vî½ÀÞq{ÁœªŒË ‰èÆkKXÈ$>–ïÖY±ÎyŒºˆgñ${\À¤|’-Vý,f·,Åèòò"–d¢s“ty&¯kթ埵ªøœØ yj¬Ž\Š­·©LÈËuÏg¬\|.'ÉR§ônmY[oNÐóòá¼â¢äJú¼7Ûô®ÔHú¼¨#+±¶[!¹`1Õê»xf!ÕýJû¿ïc§Œ¨x‘°'K‹ËãËký=•í‹LÚï¼y¾¼0Ã*íás¤~9eñå>’sÅú¼éÙeÅ罃SØÅI*ÎÄýÈŽ ;VôIp?^ï ²‰€×Ë0…×w2L8ÀøcÜ8¼gVŸÃ£ý¼ð…/À£õ¨µçhhhhøR¡ô†††;$þðÿ/}éKñ;oþ Ý1/Åyx4:ÜÝLô8VQ&œ¶I,{š’§£}*V¤ªí¶Œæ[ÏEë¬ |aÁ«áÈM‘ Wâ^ç°‰ ›ó Ÿ+ÄÊ(ägÜÑÓv«@¨7q7#¤[\Ü“HWL kÍ\í")nÇóJü{¼ÇÎXùm>j@h‰µR‹;²<ûõ©Oáå/9~õW ˃ŽNß‚óp¸7r!!@ÝÖ•x³å\,x™«:¥Tc‹»Âeõ2A×2É]Xˆ…¤+sc ’fí0¸%ºX9þ[DµDøâ×)¾1ÕZ¯íH¤†\þW{‘tǘa±Hʆ£ñzb[ò€B¨n¢žÇ÷È…¸rWã =†L=É|n‰æzغlëu©ï“~©ÇiR’¶ŒmIS¤¿C™œˆ))bWù øÆ×;¤Ei—y°K즕+³äz # ¿I½ë‰9éæ0&æ‚<¬#å5OÐ̬¸á¼]úIp’Å‘²Ívá(,”Šóê±â¨^%àìå1&X’.ý,>‡ëñ>ÜÔ};; üÈÓÿžýìgãþ÷¿ÿÚkmhhh¸µÐzCCC€n¸¯zÕ«ð²_zþ¿Ï_‡cÝCqlúNìàká ®ïMƒà”ueŸŒu<•г{¼Öe 9‹_”“ÑGnönšßïûÈÓ*.»9Iak)³ÉzXW·¢Måo”$½›€åŽýÍqäÎÞ[÷‚I©Ïȼï€åµ` ™OÄÞð†7àç_ü\ûÉ¿ÀÑþïâÈø8Á#r­±•³±éˆÛúl’éˆthÔeiq_˜º¹~˜És–ÖhŽ O¶þ‚xlCÐáC,ú:²m¼z¥#’ ŸYÒQOÝV™ãë ;“yëf!¹\ñ]Ò¤îÖcŽyqANçgѹœ„ ™´qúo½›<™ÔT¸äz¬„‰­bíΟæŒf·àÜížãÀ5–Ý™ò9qæmnõÔÊ+å,‘v˜Š,Úõ:±Z R¿±ž;À;n¥W¯ CÌWq!‰ÄßÄ•=•‰å™t×úh7Îd%`¢û9÷ÙLû l˼7|ôt  .-ÜèU…܈® ÄcQ.Xϵ®š \m1mDZ‡ãT›¾Ç‚sò¾×D7O?K’.¿ùªž–¸ÇÃqrõi\üÀ¯ÆÕÏÿ üÀüvwwóên“h½¡¡¡¡ï=Þþö·ã%/y)ÞóžwcÑŸ‡½ñQ8†Ëáp_K`Ó­ñ6ýí+Å‹ÑK¹¨½Û‰In:XBæà/Eâ€-ºÖDãúëƒë»Q±Î,ïR×6q¹nBCIˆÖ)‡ãÌ3?—Iý˜å­ö=0eLßå—ÖúiQ.xgãÒ¥>CèÅòë+pKtæÏjIέڻìË]‚…TwÈI½uŸ—c,IRk´ù ŠÛ¢¦Íℹ›½´¬Ç¬Å`ÕßC¹Ò’íRŒ3÷ùšØ[X(ÐúõŽt©þöðMØõß…3q¸ªÍ˜Ð'Á+Æhò±k¼eîêË“á¯ûÅt ”š |¿ÌRRÍôMä¼Ïr¹¯ƒ!?½º;"Û¾·Ç$BOå¦Dâ¤NW·Œ§²Ðr@t9f×eDa9"û¾öiÛ]´¬®vÂ?¾gÞÁ­ÂyØ*(.çÖz­Ê×yY€Ó–qJ6½/¢:Ï ˜àiª,í£Jþ­+:[Ìõ¼„j;B{Ù‹DÏ4mš£¯yœÀyøBtл§íóí"Qû¥\­r_Rí"—cS¦îoQû ïû¹ÇFNÔõ°ÈQË`à¡e¨ Ê©þô ë²>¡OäÜZ³wpšîQ¼%+û„ oR|猌ÃtMAÙÃc | ×»?ÁøØÝÝÅSžòd<ó™ÏÄ×ý×o]gCCCÃí 7444|¸é¦›ðº×½¿òïÿ®ýä_`w¸'úÕeØÅcÑã>)6=wÛµîîá3äw®YÏ·Ëi>9 ¿W!Øë:[ €Å™²¬‰CJK{œÏÅ­WQq+ÞLÔ]ˆ]°Ñòã*B\ÈÜÚZéY%Þ”í¶+;DÀ6•%õy7¦±ºKNr!µl=טòUV6XM%–=¸Ì‡ê -yÐE ]\ߥ~‰ w”’M•Ó­]Y6XÃó²)›Òhõ«Ðz¾ ýnˆä‘‰q×E¼MöóÂNJñwP–ëËŽ‘*ÖîÄý½&hXõLË€·çBr5݃)'éÀ€‘cʤía[¸Ö˜Ã<]n$ùaQÅ™zj8†p7pæŒy¸ƒ„@„gk…2uÁI¦þV)›Ò¦QÝìõ XëýAÏt®Œ(‡:×õüSR­ñö©Ó{‘¶ÅP ñ$¡ºûɪ§AnÀ2Y±»(ض ë9 ²°\.ìÊê¶u.î9r¡8u$l,º%ï°Â|ÇMýŸáøøiÜùNçáÿÀ?ÂSŸúT\rÉ%Mô­¡¡áË 7444ÜÌ8sæ ÞúÖ·â5¯y-Þù½pþXøÇÁá›áaÝ]ƒìS™H-í_$AŸCUµ!ÅšÉÑ!WtŽNs.'Â,–|>ÚW”Ç‚ÄÈêcTµI|K~W‰úPßç| CSvß§>Z0+‰1võòœë:T®DËWËB‰`*/ñȱ.n‹ÔÏu‚È"•,ê–Å`Ë51ÌýÎî“óð™ÛuêS)^D"ÞÞ>¯D¼©<“ô®R¼Ufçz Í!ïl)ç…!W^|ָ߹òU³8í/©œSë8דÒfuû.†Xô¶=Þ¡[IÈ‚^×€³O®»Ç8S”WEw-ÊØ ‚£¸GñmžY ±ÛZë‹íó–ƒ&Ç_áF÷§8î>W<î <ퟆ'<á -¶¼¡¡á…FÐnA|æ3ŸÁÞðüú«ÿþÇ_|‹áîÀêr x<:< :þvXUâÑC>ôíÈ9p®=w+ßwN£ Ìݤ®Åyù~Y'ûLØòº¶"䱎ZÌüÚC2RÌu±åR ÛÜ”•GpI¯‘o‰?.TÞwÈÒÉl¡ßM]3 \Ö‘ø6åבQ¶ê¯+—H÷Ì¢KArcù!m&¢[ ‰ð]&LH‹=.¸KçÎÓ¥Éù»1»?´T½ÔËǯE¿>± Y—ðŠ|ñHêI¡´ïàhÙ'"Áw+y_èuHJµìÛX_®«êÖ>CÒ¥¾9âÍ–ù[ 5ò]³†§òYŠ5ÆA©! ">Çû*iáÆE±«Ý³Õ /‚Mm®A„ËÆ!>眰ÎxWÌA8j¾¯¤A‘î raB>Ïb¿Ü„ò}ÅÃcêgHù6é ~Í=®©³Ë158`C_:V'þ˽j}#œ-”Ô{,±‹SÕS2Ÿßãyñ7‡ Gq#öpS½Í3Ç¥‰+÷8‹¿Åq|§‡áôê:Üýnà©O{2žö´§á!yHsaohh¸Ã£ô††††/1–Ë%ÞùÎwâMozÞüæÿ‚ãÇ¿€Åp_L«Ç Ãcà𵘰³µk»`:r2(¹w¥õ ãɰ·Ûב¤a?æÎ‰$½†n"×ãÊŸ—nŒ|´bÁtd5ý’Â…SrÚ4ÞÄ8fW9¦³¢o€«q( XPWeHCÝeëÛÀÏÚ0 F%>a\dq×ï»Ú û‡¨9.×kLñXFRÕ¹É>Ki»,ø×ø>ZµWÙówºHP[xèFÕ9àëžzM±–÷'çi ÐvIå7Û7Wå1¾âm!m€1ïÒ>ñ˜¨ì«õ yöG+D?î[îQ¿áP†¡šÛѵ=(·[„à™â Ï_n÷M8а‡ã´pÈnëË’“{ ŒØÇ§p>Ž3ÃGqfõ9œwÞ]ñýßÿ½¸òÊ+qùå—c±˜YÀjhhh¸¢ô††††[ËåïyÏ{ðæ7¿¿ýÛ¿ƒoü<ý°Z]‡Çx\EP®†éèMðG¾BŽóœÐ@=º@¬ïâ&ÏD>3æzשµ‹Šù¹ h‡Ø é…S2^µz;uw®¹ÉÀ´@á–,®Êcf1ef‰3î&jG„íD†)^ȯ\·‰mTÝÜ܇x}ã‘ðI]Í“ìˈx-¦\¬Ì\§<Îc1·êq…ÅÛù(RHä[ÚájíWÿ^ûSº—]_Í#!‡³Ï‰ÁqäIe¶É1siÕdÁaÜ¡òtüj§²/ôÞp4³ñèwÎãMÒì)WöæOìþ`AßÅqpÚGv[×ÔhÎà¯qгÃGpvõyÜýnàÊð}¸òÊ+qÙe—5RÞÐÐÐ0ƒFÐn#X­Vxÿûß7½éMøíßþ|þóŸÅ0\”ȺÃ߇3ù¬íÄ|:zãf‚ž«¦§8ÞJœ·qCgá7)×­”8ÕÎ%äHHÕ¶qäÛõÃÔë;µ8,Wuc®Ä}‘(w1eXF꘨ ¡6äXâ›ãùå~H€>ÎÍ=ÍÔ d‹±Y„H–mè}13£š‰7ç¦gku7j»ò:%¾_â³9μ[)Á¨ÏUÜÐ9AÑï¼fÈs˜JâEaŸ½ÄŸ«éWŸy¯uçeózÙ oÊÇc8{@^n‰¹é¡/vc7i¼Æ{Kšµàåâh¼‹B¿Æ³t›õ,°ï—pOŽà&ìâ8š¼zœÁ>þ'ðœþû«p .Â?ø‡ß+¯¼|ä#1 3.ý  7444Ü1Ž#®¹æ¼éMoÂßøf\wÝßb.Àju9€+|ìd×½8œŠÜÎà<0œß߯êiÖµxu©W\‘·A"_•6§Åëüa Vòmò\‡“j¼x®âž#)tÇãà4¯7[Þk®ìF .#wêB´,?Vi$ÆSo ^~n¶X‹7…Xu’¤‹Ò}nG|Þ¼˜ÁusZ°·_ѺÁÏQ¤Ï€½>Û{R®NàóV‘êõà­ /Èì÷ƒÛ¦¸oQ(÷¾ù’O¬Ü_~ã*}¯ýnÛéÚö÷¦IÇ3ÊJÃÏ1IG#ð€@èLq/kØV£ô©‘Ú–¤R’öè˜Öë˜Ö¨Øó©J¼?©uë¶úEY(ïÓ§\®8g©‚èP‡c´råÊà4ø-[6É’KnOy]ݤŒŽRrždÅ{§±MW¯€å‹ðè¬r*k§¢ n¥QûPþ‹‚š`• Ý„<ÿ;‚°{Ž#Õ‰°Š{°ýÁ:ž>®žG]èLá÷ÄVD¥ýЏ|¡WËCöá}Œ÷ñzÕmEu©ò÷¸KÇ2Ú‚r%)'žsqÿ@0Ò‡ã l]‹ÜŽÉ£ã‹³÷ĶһýU#«,àÛº¦bù´N^}*—çS÷~%#¿rsÏЕeÓ×{õêÅêë t¨Ã¶lÙ¢E‹iáÂ…š¿à_:ðc‘\îT)ù,ù=ݤ”n’§­dÓŠpÏny…ë@¨rExœU@p%÷Š‚UÙ¢_®J®šWæd„uû´ã¨SâËÊ+ yQÞ}út”°¼ð©Žíç€GZw@µÐ žðûýúì³Ï´páB-XðO-[ö*.>&Or#y=gKÉ]¥”.’'»òÆ<%¡S–#Þßë?q_p´0² W.ÙýÇ1\)µ/òu2Är\ªø*u¥ûÚ¦ZG ÔåêH ¹_9XÇ>Ýܧ¨!3Z?+ÿ¶Ë/ÚV.ØGý¼È6->J@û°©Npó*LQ&âtùrÇ .þåýñ{Ê~¢|fì óyS+þ²¡$­,ä§TØ–ËgÉe"AçVÙãÖ"4Üòk|*”Ñ ¹<ŸªÄ»_ÉÉ©êß¿¿† ) äݺucê:œt¨§Ž;¦>úH .Ô¼ùÿÔgkVÉ£¤ÔÖ*qŸ-¥t•R:K®‘p—œ¸Gº".¯íÙç°ü¡ ³Ê0[ ·?ËúduIÁUàK;=ÐÛتÜçlç/ˆ¢^-wŒˆãâxÑž¶¨\”zåiÍ€ò_BTg]à{ùÅÚžTnAÀ Ú ÔóGù‚&p5Þ›\ú§/)´^ùGßySJ§³‡õ+ôKËç‰2låóG¼]úQFŸÊ¯OäN*TqÉVY–¥îÝ{iذ uÁèÜsÏUjjjôsÔ:œ"ŠŠŠ´dÉ’ÒÀ>ïŸÚ¶m³d¹äI=£ô {Zg)åÌÐû×ãY˜-Ö î.‰=`ÛŸq-¸‚î4ö«¡Mƒtÿ°eûÒ"Zݨ_D™ _~Ú´z‰µ¡ V°È[ «luýŠö“"¿7!Ï4·_E¡®/I*‰pí_àø]'VØ/N¯²Š»%Ë›º `™òS×S |®ŒŠ%­•_ËãùD^ï2ò«]»>¼44HMš4 ïà¤" À)*êýë)ùò'Ÿ-¥ž-%µ)»=JPt/zÔºîmX7ÊBq5¹Š{M±?j«&ÚŽ#½'ÑêFZ$.l5þ*Ô=Ùb½A҉ǸU&°êº§âµuJWh÷&‡¿2veu½©‘|ر,©8­´~Ø+‘î;÷KòIÚ$£år¹>.½Zî?ªF²5tè`î## ¢Þ¿îNj “Ü^~O®”Ò^Jm'yšH±¬Øìò¸G=.Ÿä®9l~Ù IDATd…øòNÊŠî5,ìñw'óØñ®öï0QÇ.†ðË ûª8Ì—¤œØ^Y¯$ü§…>b-°ú{4ÞÔ²iñåÅæ³ `$í’´VÒZ¹]ŸKÖ:ù|¹ê : LàþõåË—«°°P¼B{öì”$yRÉ—ÔN&©½”’+¥æJîÌŠtùã ßVÙô\wIìÓ¥íõ*[H.ÒJÝN™>ËãÕ¥.tû{¼½’€i%þʾñyʦ•îI1àúÝRIÙ£Ñb}?%Ï, ñÂr}6E’Y'™/dYëäv­“·d¿$)'§µúõë­Þ½{«oß¾ÜGu“]»v©°°P………Z±b…–R¨Ÿü IJJm¦O;)9WJm_zµÝ•¹¡ÀBqñN<;¸Ø\¥;(äÑ^`eÅxÅ=Òjå•}Y²\<ûU²JxM©K=¦iìVøÕæXo3(o·ßûì†Àâo~ÛtøX¼ß}⊺ýªx ЛC’Ù ù×KZ§$÷z•—~QÖ°aõíÛ[}ú”òÞ½{«eË–•÷àht@•c´eË[`_¡Õ«VéèÑ#’,%¥·R‰«]Y`Ï•’ÛJ®0¸«z;peÞ¸ã_œÌ:±VY#ñ]±·üRðÙÙqr•»Âç«×°xz´>Æ:^‘öùý*7Æ/BL„™Qï:šú+x–yØþG©•õÛª¸¿Pñ&‡ùÀ•I2Ç%ÿ¦Ò0î_¯$÷—*)Þ*É(-­zö쥾}O„ñÜÜ\Y±Üj¨S耄ñù|Ú°aCðJûÇËWè‹Ï×Êë-‘e¹åNk#¯§]i`Oi/%·–¬(Ó=¶)ñÑëUKåBÍñŠ{Uñ)PÅÛN´PV…þø“Oìóéf㘧çÛµfcŸ'öiäRè#Èìe¾‹µUØŽKòÙVM÷&G\1=¦vìWÑKÒlý ÔñJþ-’ÿKÉ·A×—ò•|#c¼òx’töÙ]Õ¯_õîÝ[}úôQ~~¾Üî“tÛ VÐ5êøñãZ»ví‰Ðþñ'Ú¸ñKcär§È•zš¼VŽ”ÜBJÊ‘’ÊþtE X‘Ÿ«t•í ¸½¡«ÆW÷pÏñÐÀmõJEXÍÞÄqÅ·<ËþeCI¹Ûb^á|Ç;uÛåÜvH»U˜­Ý@ÛÕ™bíöhÏéŽåK€Šúõ<<¶Ðn{¿ÛUýrÀçŽþ%MEÏ¥/ß·ˆm'…?:ÍÞOã—̾ÒîÛ!ù¿•evÈcíPÉñÝ œ[fÃFÊËËSç³òÕ¥KõîÝ[={öTFFF¥§8uÐŽ`ŒÑ¾}ûôÕW_6nܨ/Ömж­[äõ–^5w¹SåNi©«ìj»»UipOj¾0]¤p-e…yWÃÒûá­Ô²'>"Ë¡Ýø$- Þæˆä?¼ÝV‘\1ïV­JÃw«V¡Û™™™Üp:°y{€üùí·ßé»ïvF ò.wŠÜî4Yît+EF©ò™T¥JVYú´Ò+ôÁm[Ðw¥•ÕODàOP@7¾Ò ­#e¡º,XÛ·U¾üˆ,ë¨Ü®£²‚¯•Ïw$¸àZyoÀ©€€@؃ü¾}ûtðàAäòÙ¶½Q¶}1ÔP~èi©á¯ch'Þ>D)wÛ¶-{¹­¾å\¿¢×¢µ•ˆ1ªJ;+7I½O¯¸NÈ9G£hço;ÑÆúD¹Çö‘óXŠRnÅPßöK¯Åt ÛdßüOycÜaå¡Ûñõ/Q}‹m;Ú¾±´™¨:áÛL9ªÉ÷§G¬SýcG.—?Ú¶‰¡ŽÃöõÙöµmNšqTýGZX¹ì¥U«<òqcoËD)?™íD9‡ Ú™ôn±™ýýˆÖŽ>+±ì[ÑkÑÆ+î~„nOÚäÓcgºkþ¸Õh§Ä6vÇmåöíâ(åÇmÇ-6‘ëDÝ×^Çöy:fkg¤‰ÙµB@?Ee*SÔ(b@LR’²Ô$X-ì…–»R?Z`­ù6S¿¢:.¥+E¹Õj³â>Å7.Vœc­(mZQ‚{H@·¢„WW‰m;J`³×‰\] ¤¤3bhÇ^^I›åëÄÛf´úRåçsÿâ£Xûgo'Ù#5ͨ¸~¼m†”Û>[ö@l D¡-ÛÿM„Ô?±iÙ¿²µã¶T[y’­<ÉÖNR¹€íµÐr{ýÈu<éRÃ\+¼N´}cè_¢ú÷¾ ësõû–ÑÐÒY]=aå‰é_äòÐÿi·—G {QëÛË£„Ÿ˜ÚIüq³2-õìä9éǽ­ê;QíÄÿže¥YêÙÚ}ßhíDë³Óη¢×â=çå‘zfZ1Ž]¼ÇML;!áØV~4Jù1[ùQ+J{¹í°GmÛÇl_@Ùë-ÿ¥…Eâp:@@ÀèÓZmk» õRõ«í.ÔO)k»õSn³ÚîA½Ó¢ÿäÖ„á—&×vꥫ‡2®5áên,ÿ”hWçðw+ê>ÑC@¯ tnmw¡~JTÛ=¨Ÿè ×ò\þÉ­ ýœ Y®ƸÖzâÐQßð‰Àè8  àt€€€Ðp:@@Àè8  àt€€€Ðp:@@Àè8  àt€€€Ðp:@@Àèó¶×vê¥Ãú¨¶»P?_RÛ=¨Ÿ¶|_Û=¨wv}ä¯í.ÔKïÿoqmw¡^ze>ãZ^ùÌ[Û]¨w^ÙÃß­¨_èC@¯‡õqmw¡~:¾´¶{P?Ðn÷ÇüOdM˜÷&A²&¼²€q­ ôÄ# £¾! àt€€€xj»8¹Ž9"IúQ?J’ü²‚¯¹%I%*ÑËýeå¥uÂë—n»"Ö—­¾?JýÐ}#—›(}ˆÞ¦ÛV§ò>Ç_?¾>ûeɯ#:®-RHy|çXÑ1b—ÄŒµb¨o?OŸmÛvÿß~/ž/ò¶½¾½WY¹ÿ°Tòµ­ÜVÇç‹\îòFÙŽVÇVî¶m[ör[}˹~E¯Ek+jÿbèkLç¥~±WÚ¨â:!çmŒ¢o¼íØî1t[ù‰McûÈù­u|¶r+ÚGÔVîýON>Ûk^Ûk%¶r­Üc/·} î="ý´Å”•›ˆu<®;GmÓmÅYnoÇV'丱lGÛ7–6U'|ûÐOFë×z#Ö©þ±#—ËmÛÄPÇaûúlûÚ64ZµÁVòWuµÊ#7ö¶L”ò“ÙN”s¨ GV}ç‹þ~DkÇ Ÿ•Xö­èµhãw?B·x¥UMÍ·í”ØÆî¸­Ü¾]¥ü¸í¸Å&r¨ûÚêÛ÷=fëæÁi,cLù¿Q=÷ÜsúÍo~SÛÝà7nÔ™gžYÛÝ€¸‚~Ê=z´$)??_éééµÜµ)33“pî \AÀX$  àôSØš5k4bĵk×NéééjÚ´©Î=÷\½üòË1·±`Áýìg?Szzº5j¤‹/¾Xëׯ¯Á^;_"ÆU’æÎ«(++K:ûì³õì³ÏÖP¯¯ºãºpáB]xá…jݺµRSS•““£ÁƒkÞ¼y5ÜsçJÄguïÞ½;v¬š5k¦ èÜsÏÕâÅ‹k°×ηhÑ"]ýõêØ±£4h ÓN;M£GÖªU«bÚàÀr¹\QöîÝ[ÃgàLÕ×òîºë.¹\.uéÒ%Á=­[ª;®;vìÐĉ5`À5jÔH.—K/¼ðB ÷ÚÙ:¤;î¸CC† Q³fÍär¹4uêÔ¸ÚX¸p¡¬æÍ›+33SݺuÓO=ûì³*))Ñ¥—^*)¾1™?¾† "Izþùç5wî\ 8P·Ýv›~ÿûß×HŸë‚êŽk@AAAØß±Mš4Itwª3@9}ûö5mÛ¶­´^^^žéÑ£GHÙ¶mÛLJJйöÚkkª{uV¬ãúé§Ÿ·Ûmy䑓Ыº/Öq¤¤¤ÄœvÚi¦ÿþ îUÝë˜Îœ9ÓX–e–/_,óz½¦sçÎæœsΩÉ.:Úž={ÂÊ:dZ´ha.¸à‚*µYPP`,Ë2³gÏ®n÷ê¬DkII‰éÞ½»™8q¢8p éÒ¥K"»YçTw\ý~pûÓO?5–e™^x!¡}¬ËöíÛg,Ë2S§NyŸk®¹Æ¤¥¥™#GŽ„”:Ôdee%º‹uRUÆuΜ9Ʋ,³råÊìP}\AG˜¦M›Êã©ø |û÷ïׯ5lذò¶mÛªsçÎzë­·dx@@ˆXÆU’þö·¿)55U¿ûÝïNB¯ê¾XÇ5Ç£¬¬¬*ï__Å:¦o¾ù¦òóóuÎ9çËÜn·ÆŒ£+Vh×®]5ÙMÇŠt…»AƒêÔ©“vìØQ¥6Ÿþyeffꪫ®ªn÷ê¬DëC=¤üQÓ§Oçß)U\íW0ÏpU“´´4%%%)555¤<++Kiii‰êZVÏŸS82ÆÈëõêûï¿×¬Y³´`ÁÝ~ûíîS\\,IJII {-%%EGŽÑ7ß|S#ý­+ª2®’ôᇪS§Nzýõו——'Ç£6mÚèÎ;ï<¥§¸Tu\ü~¿¼^¯vîÜ©{î¹G7nÔ¤I“j°ÇÎWÕ1ýâ‹/Ôµk×°òÀ=½ëÖ­Kx_ëªhÕªUêܹsÜûnܸQÿ÷ÿ§_üâJOO¯ÞÕ]ñŽëúõëuÿý÷ëÉ'ŸTƒ j¸wuWu>¯¨¾›o¾Y~¿_·Þz«víÚ¥üQ/¾ø¢Þzë-ýñ¬íîÕy#GŽ”ÇãQÓ¦MuÙe—ñoœ§¶.ÝÃ9n¼ñFcY–±,Ëx<óøãWºÏç3M›6 ›þöÃ?˜ÌÌLãr¹B¦½žŠª2®Æ“’’b6lhš4ibfÍše–.]jîºë.ãñx¸uÀT}\†Ü¿Aƒæµ×^«¡žÖUÓääd3~üø°ò>úÈX–e^}õÕDwµÎºöÚkMrr²YµjUÜûþñ4–e™O>ù¤zV·Å3®^¯×œsÎ9!0à”ŸâIU?¯………Lq/çûï¿{*¶1Æ,[¶Ìäää„üÝü_ÿõ_5Ô˺§*ã:þ|3eÊóÞ{ï™eË–™™3gš6mÚ˜ŒŒ ³víÚì-z=±dÉ’à_â•ý|öÙg!ûn߾ݬ\¹ÒÌ›7ÏŒ?Þ¸\.óÐCUzÌ»ï¾ÛX–e¦OŸnöìÙc6mÚdFŒa<±,ˬX±¢¦N÷¤©qMJJ2–e™¿ÿýï!å“&M2–e™¯¿þ:¡çXjc\6mÚd>ýôSóÎ;ï˜+¯¼Ò$%%™—^z)ѧxÒÕÆ˜ž ½:ãp×]w˲ÌÌ™3ã>~II‰iÑ¢…éÚµkuOÅQjc\yä“m¾ÿþû`Y} èµýy­½ºcZ• ¹lÙ2Ó°aCsÉ%—˜÷Þ{Ï,]ºÔL™2Ť¤¤˜iÓ¦%òôjMmŒk$[·n5™™™fôèÑÕjH$n¼¬'òóóõÜsÏÅT·M›6a¿Ê÷”O™2E7Üpƒš5kµ»ï¾[‡Ò´iÓ4eÊI¥Ó†~õ«_é¹çžSëÖ­«r*ŽRãÚ´iSíÝ»WC‡ )6l˜üq­Y³F:tˆç4§6Æ5àŒ3În9R]t‘~÷»ßéÚk¯µûŽT[ŸÕ¢¢¢°ò@YÓ¦Mcê“Ug\%iêÔ©ºÿþûõÀh„ qÿý÷ßמ={tçwƽ¯“ìqݾ}»î¾ûn͘1CG?þø£$ÉëõÊçóéÀJII »ç·®©íÏk}TÝ1­ŠÛn»M¹¹¹zóÍ7ƒ÷ø0@.—K÷Þ{¯®½öZåææ&äXµ¥6Æ5’víÚég?û™–/_^cÇâVÛßÀyfÏž×tÊÇ›/¾øÂìÞ½ÛcÌ!CL‡j²‹uR¬ã˜‚ýã?†”/X°ÀX–eþñÔd7ëœx?¯åf‚DZÉøTë˜2ÄtêÔ)¬üÁ4–e™]»vÕTë„{ï½×X–eî»ï¾*·qñÅ›ÔÔTSTT”ÀžÕmU×X®ÖMš4©{í|‰ø¼ÖÇ+èÕU•+½)))æ†n+çwŒeYæý÷ßOdë¤D]A7ƘaÆ™–-[& W@b°HÂ,Y²Dn·;æ«´éééêܹ³rrr´jÕ*-^¼X·Ýv[ ÷²î‰u\/¿ürI¥WÎìÞ{ï=¹ÝnõîÝ»ÆúXÅûyµ3Æèƒ>PãÆ•]½«›bÓK/½T_~ù¥V¬X,óz½z饗Էo_µhÑ¢¦»êXÓ¦MÓÔ©S5eÊ”à £xíÞ½[ï¿ÿ¾F­Æ'¸‡uSUǵGZºtiÈÏ’%KÔ­[7åææjéÒ¥ºùæ›k°çΖˆÏ+§M›6*,,”ßï)ÿøã%I§vZmt«^Ú¼y³–-[¦~ýúÕvW€ ¦¸ŸÂ~ûÛß*++K½{÷VNNŽöíÛ§×_]¯½öšî¸ãŽé©¿þõ¯õâ‹/jóæÍÁ©F|ð>ùäuëÖMÆ­X±B3fÌÐðáÃuË-·ÔÖiÕºêŽëرcõÔSOi„ Ú·oŸ:u꤅ jÖ¬Y?~|Nõr²êŽë%—\¢îÝ»«[·njÚ´©vîÜ©‚‚}øá‡š5k–\®SïûÊêŽé 7Ü ™3gêŠ+®ÐC=¤fÍšiÖ¬YÚ´i“.\X[§Uë}ôQÝsÏ=6l˜.ºè¢°©“}ûö nG×€^xA>ŸOãÆ;)ývºêŒkVV–ú÷ïÖfVV–¼^oÄ×N‰ø¼¾ñÆ’JÃŽ$Ÿ8øÒùT3oÞ<>|X”TúT‹À81"ø¸´Hcúÿñš0a‚F¥o¼QiiiZ´h‘þüç?ë / >)ãTTq½ð uþùç«sçÎÊÈÈÐ矼íeÚ´iµsB@$µ} µgΜ9¦ÿþ¦Y³f&))É4nÜØ 4ȼüòËauÇŽk\.—Ù¶m[°ì£>2ýúõ3YYY&55ÕtíÚÕüùÏ6^¯÷dž†ãTw\1¦¨¨ÈÜtÓM¦E‹&99Ùäçç›G}ôd‚#Uw\g̘aúôécš4ib<ÉÎÎ6Ç?¥§ &⳺gÏsýõ×›¦M›š´´4sî¹çšE‹¬Sp¤—ËqµËå ©m\1&//Ïœ~úé'«ÛŽ—¨q-ßf}Z$®*1®å÷‰¶ÿ©¤}ûöQÇÄ>~ÑÆtîܹ¦ÿþ¦yóæ&##ÃtéÒÅÜÿýæÈ‘#'ûT¥:ã:iÒ$Ó¹sgÓ°aC“””dZ·nm~ùË_šM›6ÕÆ©QYÆSÛ_pª;õætà@t€€€Ðp:@@À耄[»v­~õ«_éôÓOWZZš233Õ«W/ÝsÏ=Ú»woÜí;V¹¹¹5ÐÓšWPP —ËüIKKSË–-uþùç롇Ò÷ß_Û] r¹\ºï¾û‚¿¯_¿^÷Þ{¯¶mÛ–Ðã 808_|qµÚ***RRR’Þzë­õ.~ýë_år¹Ô¸qc­\¹2j½üãºòÊ+•››«ôôtåææj̘1úúë¯ÃêvïÞ=8F£FªÉî„€H¨gŸ}V½zõÒÊ•+uÇwhÁ‚zë­·tÅW襗^ÒM7ÝT¥v-ËJpOO®‚‚-_¾\ .Ô¬Y³Ô½{w=üðÃêÔ©“-ZTÛÝ“$-_¾\ãÆ þ¾~ýzÝwß} è–e©gÏžZ¾|¹}ôÑjµ5wî\¥¥¥iøðá ê]|fÍš¥‰=q)2¢IDAT'êÚk¯U›6m4dÈ­Y³&bÝGyDÇŽÓÝwß­ húôéZ½zµzöì©õëׇÔ}ùå—õñÇ«E‹uþ³ˆ§¶;¨?>þøc?^C‡ÕܹsåñœøgfðàÁºýöÛµ`Á‚*µmŒIT7kÅÙgŸ­ž={¿ôÒK5iÒ$wÞyúùÏ®M›6©y󿵨C©OŸ>Ë=öÆ5lØ0êñâñÆohøðáJIII@ÏâóÌ3Ïèw¿û&Ož¬éÓ§ë§Ÿ~ÒèÑ£uÁhñâÅêÚµkHýwÞyGÍš5 );ÿüóÕ¾}{=öØczöÙgƒå;w–$%''×ü‰ƒ+耄yàäv»õÌ3Ï„„óÇ£#F÷ûýš1c†òóó•ššªœœ]ýõúî»ï*<ÎÖ­[år¹ô /„½ær¹4uêÔàï÷Þ{¯\.—>ÿüs]qÅÊÊÊRvv¶~ÿûßËçóiÆ :t¨6l¨ÜÜܰ+ºK—.•ËåÒ«¯¾ª?ýéOjݺµ²²²tá…jãÆñQˆ6mÚèÑGÕÁƒõôÓO‡¼öé§Ÿêâ‹/VÓ¦M•––¦ž={êõ×_©˜>¿téR?^Íš5Svv¶.»ì2íÚµ+¤îâÅ‹5pà@egg+==]íÚµÓå—_®£GF»‚‚]yå•’¤Aƒ§[¿ð š6mš’’’´cÇŽ°sºá†”­ãÇWiLvìØ¡Ë/¿\ 6TãÆ5fÌF|¿8 E‹é²Ë. –ùý~=ñÄêÞ½»ÒÓÓÕ¸qcõë×Oï¼óN°Nûöí5jÔ(½ûî»êÞ½»ÒÒÒtÖYgéÝwß•$Íž=[ùùùÊÈÈP¿~ý´zõê°~Ξ=[·Ür‹fΜ©éÓ§K’6l¨ùóçë /Ô\ uëÖ…ìS>œKRË–-Õºuëˆc 8õÐ áóù´xñbõêÕK­[·ŽiŸñãÇë?ÿó?5tèP½óÎ;š6mšæÏŸ¯sÏ=Wû÷ï¯tÿhS#•_yå•êÑ£‡Þ|óM7N?þ¸&L˜ K.¹D_|±Þzë-þùúÃþ ¹sç†í?yòd}ûí·zþùçõÌ3ÏhÓ¦M5j”ü~LçÍðáÃåv»õá‡Ë–,Y¢Ÿýìgúé§ŸôôÓOëí·ßV÷îÝuÕUWéÅ_ kcܸqJIIÑ+¯¼¢3fhéÒ¥3fLðõ­[·jĈJMMÕœ9s´`Á=ôÐCÊÈÈPqqqH[±9r¤xàI¥Ó¸—/_®åË—käÈ‘ºé¦›äñx¾T(**Ò«¯¾ìO¼>¬Aƒéƒ>ÐŒ3ôú믫yóæºêª«Búðî»ïʲ¬/}ÆŽ«‰'êœsÎÑk¯½¦¿ÿýïºøâ‹C¦é[–¥5kÖhòäÉš©ð<çÌ™c,Ë2+W®ŒZ'''ÇtîÜ9ø{~~¾ù·û7ãóùBê5Ê´jÕ*¬íòcøÈ#˲̞={Œ1ƼñÆƲ,³víÚ ûZ~ìçøÁ„Õ;v¬ÉÉÉ1ÅÅÅÁ²‡~ظÝn³mÛ¶ 3`À3hР°ò™3g˲̂ BÊoºé¦ˆï÷èÑ£Í%—\üýÃ?4–e™)S¦TxüvíÚ™ ˜;wË>ûì3cY–iݺµ9zôh°|îܹƲ,óî»ïVØfU”””˜˜F™;vDíë¨Q£~l€3qP+–,Y"©ôЧ]ïÞ½kdá´‘#G†üžŸŸ/—˲¸˜ÛíV‡´}ûö°ý˯6Þ¥KIJÈjÆv÷×_­¯¾úJW_}µü~¿¼^oðgøðáÚµk—¾ú꫸úÖ£G%''ë7¿ù^|ñEmÞ¼¹Ú}¾õÖ[µwïÞà´{¿ß¯'Ÿ|R#GŽTÛ¶m«Ôæ| † jÈ!!åW_}uXÝÇëŸÿügÈôöyóæI’n¾ùæJÕ½{wµlÙ2ø{~~¾¤ÒæSSSÃÊ#}&ªÃï÷ë׿þµ>úè#½øâ‹1Ï:Ôot@BîmÞ²eKLõSØí!) eË–***Jhÿš4iò{rr²ÒÓÓÃáJNN¹/; iÓ¦!¿¦pGªÇkÿþýjÕª•$iÏž=’¤Ûo¿]ÉÉÉ!?7ß|³,ËÒ¾}ûâêÛé§Ÿ®… ªyóæºùæ›uÆgèŒ3ÎÐ_ÿú×*÷»G:ï¼ó4sæLI¥ÓÍ·mÛ¦[n¹¥Êmîß¿_999aå‘Ï{ï½÷äõzC¾œøþûïåñx"¶Q^¤ÏCEåÕ}ŸíŒ1úÍo~£—_~YúÛßþ&IW(\‰?räHÄ×/½ôRµiÓF“&MÒ¢E‹4a„*÷K*^~ðàAÍŸ??¤üÕW_ ùýرcš7o^ÈôvIºè¢‹$IO>ùdµúQSÿmè™gžÑõ×__Û]8 WÐ Ó·o_=ù䓚0a‚zõê¥ñãÇ묳ÎRII‰V¯^­gžyF]»vÕ¨Q£Ô±cGýö·¿ÕO\Æ ÓØ±cÕªU+iÆ Z½zµ^{íµ¸ŽýÔSOiÉ’%ºè¢‹Ô¶m[;vL³gÏ–eY^ñÜËþÌ3Ï(##C©©©:ýôÓƒÓÀÝn·&L˜ ;ï¼Saë Äëúë¯×c=¦1cÆhúôéêСƒæÍ›§þóŸ’J'IóçÏב#G4zôèýÏ;ï<]wÝuš>}ºöìÙ£#F(%%E«W¯Vƒ ª5ý>n½õVÍž=[7ÜpƒÎ>ûl-_¾<øZJJŠzôèQ‹½8PãÆSŸ>}ôØcéá‡ÖîÝ»•””¤¼¼<3&$$=ùä“êСƒžþyÍœ9SYYY>|¸|ðA5nÜ8Xϲ¬°«Ýç•Ϙ1C‡ÒàÁƒõî»ïª}ûö!õ"íoyu®´öýÕ¯~%©ôžæF鬳ÎÒwÞ©qãÆ…ÝC>pà@­X±B÷ß¿&Nœ¨~øAM›6Uç΃Ï&¯¬oöò=zè_ÿú—î½÷^íÞ½[êÒ¥‹Þ~ûí zûöíõøãë/ù‹ $¿ß¯9sæè—¿üe°Î/~ñ Ýyçºîºë”™™ßà”“žž®Å‹kâĉºãŽ;dY–†ªY³f颋.R£F$•Noïß¿ÄÛ Ô³gO=ÿüó*((PZZš:wî¬É“'G›“)ðX¸Ù³gköìÙ!¯µoß>ââ}µ=ËprYÆ 7à€:é‰'žÐm·Ý¦uëÖ©S§N1í¸ß~Ñ¢E²,+xe<šx@S¦LÑ·ß~«ììlåäähúôé1­Ö^Wùý~ùý~qÆêÚµ«Þ~ûíÚîà$à :ˆÛêÕ«µeËÝwß}=ztÌá\*½*üÁ())I#GŽ Ÿ{ãóóóURR¢Å‹ë‰'žÐu×]\Lð‡~HìÉ8PÏž=µvíZIR×®]k¹7€“…+è n¹¹¹Ú½{·ú÷ï¯ÿþïÿŽø(´h6nܨC‡I’5j¤ÓO?=øÚœ9sôØciëÖ­:~ü¸Úµk§k®¹FwÝu—<žSçºÂ† ‚v+?F€ú‹€€ð˜5€€€Ðp:@@Àè8  àt€€€Ðp:@@Àè8  àt€€€Ðp:@@Àè8  àt€€€Ðp:@@Àè8ø¾ UÅ!ÖIEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/firefly_example.png000066400000000000000000040712001510711153200265540ustar00rootroot00000000000000‰PNG  IHDR~'‰JhBiCCPICC Profile(‘c``H,(Èaa``ÈÍ+) rwRˆˆŒR`ÊÀÎÀË È Ì œ˜\\ààT£QÁ·k Œ ú².ȬùÛ?}œ7³«víy…|_®-Lõ(€+%µ8Hÿâ”ä‚¢Æ [¹¼¤Än²EЀ޲g€Øéö; Â>Vä d_²’3S€ì'@¶N’x:j/Ø  aF&‰K*(I­(ÑÎù•E™é% ŽÀJUðÌKÖÓQ0202d`…7Dõçàpdã@ˆ¥ 1406 807 Ûãb©@IDATxì]¼$EÑï÷9$ƒpÇ G’ QpHI’³HRQ ø)DEÉ HŽGP¢äœ‘#Iò‘ïíWÿÙ©ÝšÚîžžÙÙ}áº~¿÷f¦»ºº»¦§gç_ÕÕÆS‹Qq Ä1Ç@q Ä1ÇÀàš)†ôï·©âïÓ!}Û™k~Ü3UGu3¯éé¨ü2}µwd£Mo¦ç_ËyF†çäëvÌ@üK™ÞF=:?äzu3¬vDÏ´¥eL[°Íܦ {fòÖ9‹’»6µ“ËÚŽs+’Îz”|›Ì´ªäȺnîýš·¯’·Ýóo ½™Ã§¨HºýÄs³ݧ™;T×»™²kºæ:}ÇÅýðñ•Í3H‡Ì£ôrAÏŒýsßz6ËÔ»F:/íóŽÃ8þ^€îåsu|Ïô™ºÊÞó£{¦ËÈÑs«”{eÏÌ ï×IßKˆwK;¿_¹>¼käûb~ªs±¬ßu޹`X:Üš3?®*ÞW¥ýÑrgóʲ9mø6åïnÂ~Ãü¹g†–þäéîTKÑÞÁó‘#Ý¢„˜ïcq Ä1ÇÀ@‹«”¡M± ñÙ°©ÅU[þ`H[>çGö`èÃPnc'€–2271_ úvÀG¥ü˜,soªúÀΫ{ñAªyçp¼‡ÎjàºYºÏÞŽvëþ „ëi¬è9ô¥ßzG»*û³_ÏW;RgX.û0W‡îã‘dT˜¦‚÷VPZö«êó5=Ïp™º~O ÜW*ÐO™º3e†ÿ;3¿Ñ¡ñ©³@¿%YV†¯¾Bç2 ,B&L“,Æ Ù ôçþQTf1’Å $ÏUwÐ9=åmøNäv¸Žß-8†Ö3uÍf”Ì3ÞÉúѼÖs€ß¿šh—ÉYRží\ßÉS6Gãþ ý¬žù2×’Wžã÷ª~PÏ|–ç㤒@ýHj׉ßJr,Qz33žG]Ä1Ç@q Ä10´ÆÀE9žPñ~­û]ä~Âó ¿äµýp—ù|®~t–®åÅãà¯?¥;Ü«2 íP¼Çß¡eú·Lú¬†z4•©ÃW†?Vó¼|2b^wžÝþ#>#D•÷žƒUÊ‹²º3&ÛÕ3V#À ŸAF]BdÂ+4„¯[<û“Qæ7–K¦ó»Í¨±õ›ß£‡öLãìÏÖÊÛœ^Ù·PÊrÁÃ:ý–F?)^åYÏ#£×uØ“S/Ô+Ä÷V1&É:ËœŸ®ÍÙ]ê:\×XJ ’ÀÀgó«º}»™á•P£¤«ÿ¡é+´‚*•õ!¯ 9˹;ÚŽ7 Î ¾¾ÀóZƒSðFõ•A^^Ͷß$yõr~ѲÒžC Í.úÉÀ/ËÇiW¹ òµ‘†ól^Æ,h¾ÖGªO=u€Ö1’u¾^ˆú·¹0XÿNÅf>} Ësû?@|…?È3À³á (x‹¶êvø®1®´¾%?Úqpºò‰Ó¨ækÄÈ– µkÞ-{ÿá~wÓ»9~̰òã1Ž…8âˆc Ž8âˆc Ž8ÿÐK9Cïé¢ôØI`9ò,z²éSèc3´ÝàûCàG|'ÚòÛÔ#ë ‡/KŠ;Q»2a”¹Æ±,:O6ƒ9à“ç¾r2Þ¥¯yyÀ× PÄç!È|¶c'6[9õ†´ÕWUZ¨q.°¯ªMQN÷ß}6` bH è‹i®cpãÉæ=,ïé/)óäù®ä…¯M<£[‘Qˆ JàÇÜrCo}7¬Ñ!İR FÞ0ó!1gÿI…_õóùØ‚ó$‡Eâòƒé(ãBã^¾”z%¦ß ¾~`… VÜ0ˆÌ÷ão­Úîn_Ãc^¼q Ä1ÇÀàÞïãà¿ñÆ{8PÆ@œSÎXœœAˆ/†Šï6ú ´wÖ­¢ YˆIéþ´§THý †„ðv› bÙ:e¬M—¾Êʶ•ƒîÙ‹Nç˶è¼*®mqJ«+eÀÐ#=,eŸ‡n½OÍB\8£Nâˆc ê1ÀV¨ªåFyq¬Æ1Ç@C ð¦.ñ^ûïuU ¬ü¨Ð:ÇÎÔ:-^Ûï âÃjplaúXd}Á[‹Ï«>ò‡£OnYpt&úèÌ|õ†æqxéaÆ¿'šLpGí­Z§æC¼J|Hs˜“ªäêzª¾.–sdŠÒ¸—œÇÇ‹%ØœVõ‘u¹ú9bðGÖióÔGZ;¡ ŸÇœ¬«sÝ›¬"`®­|;i—ª¥ýíÈ*[¶lÜçcX¶îÐr6ƒ@B[¼ó P1WyÔк$_Ù±±‡™ªå}ƒ¹\Ê–Ï•œæï-Éslú)å0 à ÇÞ½‡€sÛJ x«"”A‘;Æ€Ãdèöø®uØŸ1’óÊÄ”æxÁ¾¶è¼½Äær˜¿a”Òó´´ûYÀ› è¦-¤ ¦Ì?ͤBUa|MO}yj€õ¥P'sm­7˜ôjKK)D9°öaKº-?ó…-ƒÒÈèdž±ÜÃÚ°QTï„L)ß\^¤=,´ìÜFž¡fs3¥9ß|΢h,3¯é¥§½fž¤?9‡\ß3³Y·öNÂKÀ™ëÓ9 OÑ»l!Ç»ŒÂ3˜1i9ðž›ÎËxÿØ÷޹™žŠËn^§?”ÍóJ¾g<†ÍØ´ä5ž&E D D D D D t@ «R£È¨þ×ÅmëÿFÄk€>‰ƒyCë!ßÑH¹}!¨› /ylå¶½(CU /êeЗ<Ȭ /xf¦1Í /®ŸV`ÒBH‚¾äUœ}Q¾jÐ÷ãa€@êÄ /-æ$ï7ƒ¾`¬Jç>З6xò¶©êLò LDú@_Š‘ZuµAò$èKÞûÎ2_@êÿH|.Ðw§¦IXÛúG±AÛ*? Û@_ôëáZ¸AÃúBŽ ôEú×RІ(&žËɘ“ÇŸ€Ðo·ÁŸA‹ÂVdxåÅu†~Nó¢œï? †‡è ÂB›Œ%@"@ß«Äô¥˜±É9þÍÖÓœôÛ_‚¾àåyy³¾· ȬJŽ‚$èû—žºÖæ¤gžâ¸×è?ä >š@û[ø<ÚL¡HÌtO ÿ@Ì›ýú¢ÁoÓ_èK+Y ß%Ú(/y§,oykЗâ%Ú,Ò|Oðô1è‹sŒý»(ýaúèË¿Ïr=~ÇŒƒò‘¢¢¢¼ÆŽ›´1züø[5Ù66jH,Ý“­bÇ£†€hÙ0yfÔº Ì tµásíýÞHGûV¡yù_½öl¢hSsúf˧eë‰÷>´»IøÀÔ>Æß¤ÍÙ¨½íôŸýÅ|í½öywÉøh]‰îÕ¿U­k1.À‹~=›²lÑsŸ§_QY.þõéãý1V¤öÿÚì’lå‹x³Á;pÕq“Ò›M.Ò` ¾-×%×z¿}2ú3ïGŠ-<&CÚÒiæµaOòˆü£òˆÌ+S&Ÿ–Þ› e v± À³‹i&湫Sï.ŠÙj~SËB€~¥¡FvySÑß}žwˆä—ç<¾îíÅ,Gàª$íù ãYsý‚älž³'-§À,X_oÁ)î# a€“ðæ¥L͇4·í×3½Ùµ–ÿ á –wLWl †?¼•ƒ7ë=¤— ¸^Ÿ¼x7 {ø*]¿A˜ï¹ÈÿâY©€{½2Í»×÷­(FNÜf²ÞÇÅ$d¹ºïÝߘ¾3‰ùÝlºÂØ[€t´µÐ%˜¢Ço‹ªbBÔ@Ô@Ô@Ô@Ô@g4€åM‘¢º¡øh„ùŒu£5C«ŽGècƒ=xú«g´Jáª)v_p,,BùŸtE¤•ãÅÇfjü”õù@_ðaÑé¤W~XÚ-Ò /ê]§g¸Ù‘¼§Ú¡Ð·¼›|äóäD9… ø£1èËc™â7@_ð ø¤8mÐsÊû@kqy|eóÆôÁ3«è‹2ðf€Bð }!¯“ /äÛî÷"bŒ¬#¼Ù꾆(UœÚ)ë«Mƒ¾©ñf+[•Nâ—¡n€¾h×@}ñÖ»BÜ£ è‹6­¤NQ„06Ò /x4è /UûÈÓ oè{þ¡ôÛBƒ¾¨“=Ÿùƒ ôÕ+¶´Gzè\ƒúÞ£¿èóðS4·H¿Ul ïï,ï úB@_Ðã$çjò<ÞƒÂD ýMñÛä]ÀF>î#ƒ¾®ß¾Vèëóî¯ôEß)64­Ôw¥µ‚¾`ÄØÓ /Òóß|àŠ555555550h4€Xa¾eŠƒ¦#±¡V ìà=£ Ž+`xâåƒZF»×[»>bÚLåáùY–Îq|¼³< Ô–ƒ^ê’ðaÀOúÑÆ…\MeGÚ=Ü+ ñwPÞ@Þmdþ7l‹ À½dàÎÞ—Žå»IŸš6«e?†çï{3àÕFrü„±ebokëÀ–·GÏWmÉ•¤!f¦kY“\v,ó·’6¡’IÎó'ĹUxã@ʣ͔‰“ƒ,Vð¹*û\o¡Æ[^{ÛÉ÷-ñ·ÉͰq ý4¨6öÜ#ŽÎËá‹ja °Ú£õÉÈ—„0rìëxÏßE^¼š`LCŒagx…èòžçaIG¬^ÄÙžC¨a09aJÿÑæz 8‹yåJß3‡‹Ð ¿¢öÈ,±Aˆ- /^&ôo= ;1Cšo¾ü)gYíüöå9sÝL$†Zx@Kcø÷ã @>÷E6¬6 ‘½ëÄòù:ïx.­. Mñ2lˆ¡\†Âfã2’c™¨¨¨¨¨k Ï“¨ÃÕGñQQQQ4 7•‘ÅÎË^%¯ëܺxuúv9 ¨\<یȪ¥„_#Ž"b‚ŠxcÖKäÿ?«àRô|‰Õrœ•Þo|PkÂæ=ã œ…ñÊF«€<Žýjãñ¥í¦âe2/â\2Ù>Þ‘§cïÚ@i–!lXÀf`’¶Íè]:€ŒckŸ˜­¨|Y/PÙ}ÖÄkâXË2ÝåA‡XÈÚƒQ–sˬ!Ë˯U&N}n†®ë€’z„SW«º“ŽX¦’ÊÄS–ër~Ës&ëéôùå ¸êt}eä¦Â2¸dÀ ¡ «=äXÝ  l 6Pcð“ë8±¯Ö…1 Æ)i@Ú˜ÊbÞÒ+ çtê›Ë𻲚“¸^± Þ.4_"ä‚&„rhm€ÞìÆyK¦òû¨½LßJç`€Ä US}=GñÎ1ŸMÕÓc¦£8µ¥zoãÞíÍ ´rcb*{¸T€áßT–xNÇ\÷. ¡Ð>c8Ú oÓIϤfäø§¼"á-òos4ëÊZÖ}cx"ð«5¯£¢¢¢Êx šÎõSCwv|÷SsbµÖâNGêœÊxu®5Wò69Àë@jyU˙ً ñ û‹øÃ»Óõ<8žâ<Âó ´}z¿õ’bäaÇvàyk è}5`N±€ðúdUCöÄ€Dý é,àÒ/R #ÒBˆ ¼6Á›K’í¯>Áð"*Ï^ ‹ïÞ\r\éX?Èfí’ÞóHX‚&¼]$=†W |²§%df}ñ\µ4ÓËõAÇ}šØdKÎnàµÊJ.§·$b“¨¥¨ï2Ást³ JÊ»>!›(àÊÇ;Ðóì¾ðÆÈÕ,Wg``c6µQÐÏÉk_:°`µê™ÇsˆyAzÌÚt´PÏ” èÈ+ØØ^€‘0˜ÉvœLó&æ—†ÉÑÜ*ùšc8ä‚ÎÅ,vw 2º6¯æ1ÌÀú“4ö7JŸG‰×[Ц¢ñ`~[BÙÜDrw&gl~fúÎ2óÒÊ žë^¥ñÌ-·éÔô=°}ÏÔn¢nÄCŸ}„ü‘š¡ç;”¢gbÍ”½F;ƒéó‚Y%£í]€çÜF˪8ÏÌcçæÜxŒˆˆˆˆˆèŠæR?öºR©¥’¿|[Š ú¤çUÈAß!Kl?eÉù°´ˆ‰I4 ½€ ›lX÷ïàruŸÒhìÆm[ÎëkUyüá­åaÉjAÚKQ—ÃÇõUô^Ù¯öm0¥¡-Í ï/ÿÇ:—@¼_,¿Ý[}tßC€‚;0Èš×>–'o[€²<ï\Y^Ÿk/>l.e£QJï3ŠkÌß|ƒºŒ³ü(µy´X²-%[]2 ±’™àMW–à­gó–ò&žÐ{‚çÂfò\çy1·ïL«KŽ^Â.¹HKd²W­Hòž"–ëRâ>y™)S¸š÷ƒ4Az¿Ãpò Í)ˆµ ²Í±{9By€÷ÇFeˆQbd~ÈØÃFuU¢ŠÈs…q­fìëÅÃõÒô¼a“Ø?×þq´úàã‹éoÐRÐ|L0[ÜN¼>:¦–ÞËÆ&”Á¬ oÙ˨0FÃ(òcš7¦¹öò¾¦éÄö\ÂsŸI¯ÌغB 2Ö!Û:çqdäsÛ…-cŠÝËôzZË}2½¶¿³iËÔÞ½’¢{¥ï/ˆs!èñÌ ™$eþÁˆøF&….jÿ¤Íöèìþº¶Ý}^£â2$¡­2>ss–o£ãÆ+Tú®»î*Ä™£¢¢¢¢ÚÕvûÈôrúCe ·q(·m>r(öÕöSV~ÀÅ>Ç>U«h)¦ðQ˜x¡t\ÎÇl¨œ_§ž¤¡üEÁš<¹!†±Œz[M0+*/$* 0Vúf…nüÃõ9޼‡Ðc”+?®Cæ›;èc|ðp…mùíšJ—ÍE¶n üœ¼Ä$5¡fªöÎmædÏøI Ø¶$ù䓿òË/oɳ%ô½÷Þ{Íõ×_oËŽiQQQQQÑïþÚá©P¹9L'뉲»£Ä‚Ô?ž;Ys‘u´ÃUѾ²ñÑH´*Úe„k`úpÖÒœÓNBd¿VÂeðÈÁG=bâu›Žð$uµ‰aX>†€5Ë©J¹a–ïúháÝäâkñB"FÄ”Ài™ þ\õétx†u›Ž"nACàÅz‹ÅSËgt`1*mð)h-=ͳ€—²¿#o¹&ô sŠŸó=Ñã‚Qê2¿b~Cjã%Óy¼¡Ëû7 ÿ ä©ÇÄú» G!cj~Óc(M–f³.áýÍô§tÉ7®YC4íâý†—` ìu^¦¼,ã3,ýˆÆö•ι¯·ÙoÙn>gïj¾váÅ:¾ñ{ qm%­E!|„÷ƒe’@#Â^D!)ØKXй‰éš‡™Î%oÝoÒ»àÑÝA@(ÏCX‘€ñ’žœ˜;"Ëú&ú2–OCˆpÌYÈyJ„Ñ2àØä™Çì7K²BDȨŸXá[ ÂF"€. w²Í¸yšÖ÷íË|¹?àÇ“Œy ;xyc8›÷4ò/Ή9íó Eya,qÜõª6nµK>[µÑ í¥¿´Ãÿïò¶€_€¸}AãÇÏôÿÓO?Á_("RÔ@Ô@ԀЀ^(²âé ÐÀvâÇá hnlbŽV!‡½2rX+É. —ÙÀ¡*ò/ñ]D¼ZªXZ¤Î¡Î‹Ø¨¡Ôü4µ—Ø¡€,»w*6(kÇ‹½€Qí'@õÇq=7û_zbfsŒa# h¾v®) ¶Sg™²ÏùÀ–2&îiªÈ<œ»ŒÈ„2'D¤2a2rŽŠ¥ £{×qœÄo(0?fùpZÓl:®Ïžg–lã%l¼L“ á¾ŽƒIzF"MÇÔt…'y‰ô/CG°¼nwW¡?òê…7]‘.e; C~~¤aIòàül1¶ñ 0ø²Ì¦náoœ¤š >ïê &ü^º¢MÀ^¤o÷ŽL€Æ›iAd¹J¡071I'’m)V9¼åÝþÓ\Š6£$póNÂý€<1[kOVÀʲ>–bŠ{®6)1€L·h1ä,$B+@F‡œÁ gþ˜Ì €ÝAˆˆ{H¯‚ÕV a€Çάj«³Jƒç}•F„J’€‡µÒ‚‰û’aAr¬ÐË¡mâàùìßä=·))ˆ+—£ŒÖ;4€3²>ð}9l”™‚6ªŠàÕÈ¢@ƒ§¼¾`)J{S,ÓS8s‡ÃkúÄ»qcA3Ñß»ÉYóßz¦3‡ØÄ°1æc´ÉÙ½3ÛsϵÿŒB{üŸŠ‰ÊyÝ8@cÿXe€‘Bnåz&lúç6ûò˜'ݸžaÞëR2Z}BÏÍ…žêU,¡zo óÂ-€±v:ú«nbÉîã®43ýE™i§!ö‰¢Àax"ûŒ¹XÙ‚Å!„÷Ñ4ôÇ!&PuÂ[¿iJëØ&s*V{·-N¥µ‰ NÃj¼ŸÞÏ+1ìúañë<.o>ú§lÞ"È…è}£̢ߜnB_Р®u~YÐ÷¡‡2—]v™ùðCk7šm¶ß~{3zôèfBÏ®ºê*óî»õWáÊ+¯læ›o¾ŽÖ~æ™gšGy$©ã;ßùŽÙpà ;Z_50Ð5ß~‡bû¢¢Êj?6Ò,5é £¬Ìþ.Ç€LU퀇UÉŒr쀛6† Ý É.¥ž YXâZ†Ö£Æër¼¾°ôlE=êCÛ:î°gKœ¾ÐJ:ȇMÀþ› V]=€“Y©^mCZ N!ÆÈ+ÍGË p_ T,€Õ”TŸa.#`gÓÏ/_¾<Ø:3xÇSè ¢¿¢€hÙ…¹ë£Þ#z²Ëš…z\÷_´w}3•9‡ü4%ØÅœ6` ych®ÁÆiíÒ·ilbCDz„¹$€&åÌN¯É„ Îo%#Ëêb)~"+‘ªæUÂ`ã)¸ë "‘÷/º×6 ÏÆŸWç» hðÌÖ¡Y¾Gm(kÈ~ŒÂ;,¦¼e¹ ˜ mýB>΢ tÍIx¯ã7ˆ ó‚>t‚FPxPûŒ“[Ó†7ø;œV"¼Ä‹´é2¢¹6]µÉ¹–âRo[{Çûn¹žáÕèn+ÔW¾âŠ+šå–[Ž/“°hAU¾8Ï>ûl/è‹úÎ:ë,sË-·à´ë„ð§ñ÷Î;¾W{×›+ŒˆˆˆˆˆÄ€Àü_•’AÜ•FÓå†BÄ6N"èÛ†ò P[è‹j¿F½E‰ãx澋¥—®ÁÇ·mi~Ñ60¿wˆåh£ú¢U€¾Ø\ z %¸Éȹ̵’ž‘’ÔIÐ ˜$Ž¡‰Í“˜àµ†˜šôE^è ƒ[è²[jÅæa›÷e¨A_´‘ÁkœƒNµl XÏéÜÿßåVùuãOóžËþƒ@w»„x¢]ŠÒTb,ø×tZ úlפ½!9ËßmËÑ9?ôè}CCø@_Ô_5è Ïíú¢¯úyFZ°{!4è ð>u£6~®WÆ´­  Ÿ¥'Óå÷g1žŸÒqŠ X% I‚¾È“±¸Ág‹|fæ /jgùˆÍËäêò·U›\rß±jCÔíŽ÷§nÞë>Ð:éùFFæŠÞ>вôŹômqP¢IE@_Œ¡ï倾 Д}[&Iåþið÷å—_N@XläÆTÖÓ÷óÏ?7𦕴Ê*«˜½÷ÞÛì¿ÿþfƒ 6Yæê«¯6o¿í³óeØãEÔ@Ô@Ô@Ô@Ô@ÔÀ×@Æ (ª)ê‰ÑÉvÏA?œ‹þxîd{Be‡n2*/òµ¯bË6ëõMËÊËÒ¶=Ó˜uÓx·~dºê²A¼ˆåØ.éÕvåµ[Ëæ%q\\NÃxÑPüaÎ2äž® ½^òàü¦Þ¬¦8–/oAà¶‚ÁM |²fo³.ŸlÎÓ1/‘#†ŽŠôª6Yû›XðýÅCe‚ñçIá)8I. `¾¢d}Ž º% .á;l`»«.ô'/6¨«¬NßÄrÇŠh¾>q]Ø  òz#JÎ9öw¸Ž62B¹ä=írc2 [Vìñß47QÈ™I­0<‡–Yš6UÃó³ ²0˜ .¶6bq¿pD á»h&‚ ñ{±Š„Ÿ%xò„¸ækÔÎÆµñÂÆååñ¬0v‡?©ƒ¨²L»ç¿¦÷°¾oyƒùþȵ•±¥AÇ =ÀÈh3ÌÙd„¤­!îI?¿›ÞžµéМSóÊŸ$6ß/Æèu1/×SìÿC˜öÒ*à/ˆÁÞ÷ßoÚÊ‚¾wÓM7™ÿý¯ù# @ïk¬¬„ {æ™gN<‚9íî»ï6믿>_šÏ>ûÌÜxãæ•W^I@áI“&™‘#G&1‰¿ûÝïš)§œ²Á oÝ[o½5¹>|¸A…|Ð<þøã}ši¦™ç%–X"áÈýÄOd¼‘{ì±Äëõ§Ÿ~jn¾ùæ„w†f0«®ºª¹ÿþûÍ£>š„ƒXk­µ’<ü»ãŽ;Ì3ÏûìI¾ö5ÛÏËFÑÌ â£Î &$mžnºéÌ,³Ìb–Yf³øâdêÉ›3ââEÔ@Ô@Ô@ÔÀÔâ®lù‘4›Úv“ªöm»AƒT€o™_‘.½J*rûŒ"eû“—cpv¢ [ЇöEbó–NÔÑ ™€ô$HÓîRäN´QËômv¤yõµôÆYƒæÏpZ_ãë¤Ý%Ç, Çý¼8žâ&êUäáó°S:B¾tŒdŽ÷ê+o$þ0µññ‡¹-O{ºjžßÒRÚ_ð1S_æ3ºÁv7+Ї¿Ž•øZÖ½kÁÍ¿BÓ“g l~²ö¥Ù<­6§gþ’ô™gEó‡\_EËu!ÏEú±€7¶ðC)¼^L|IðU8ý¹[©ž£¯ýø 2º¼˜Àéî¸Ç;“—ñ_à\¯Ýÿ˜MâôÄf™SÐõ²?y§œp!\€u9ÊÖ" çQªI¦1¯ëx6rã¼ËÛ|Oè±€åÞûˆ‰ÍÿLc|7ã—¥uø6¢tµ·?Óy,ÚŽî+7µ/3'/dþåM½%Pó„ÚDò6íIžZ04AsØh[žÆ6–ãÛ®zðNxSp¬"y9Mk¢nƬ£bÚç­êØ>÷Y°;Tr(šËÉsz“ýT—{‰¹÷Ò²3Ì׉å¿Î°(Ö xã:K_'ž|Ÿkwf“+¼*êE› µq…Z²Ø|Sœ-p^Æ<Ÿ©Ÿ…‰ñËeøxÉ%—$›¯ñõTSMevÛm7¾,|<å”SÌóÏ?Ÿ”€yÈ!‡$›ºIAµZ-ñ0æø¿ ,°€YwÝu€Ÿgœq†Ó xĈf‡v0³ÍV_øõÒK/™N8!);õÔS'à+6“Ó„ï 'l²vñÅëìäÉŸ|ò‰9í´Ó’k€¸ÓL3yöÙg“kÃØäîã?6çœsŽA¸¡?øÁ2ñ‹]1~ja1\´ÒJ+™Í7ßܕӣ­bŒßA{ëbã¢&s `Iâûô“4û£2WJÝÐqzAK£?« îÁ*b+¦äîä²UÇéÝ“¨?V°—lc7ÎC6ÃAþH¼Í–.LÏ¿ômæ´žÁÛ8xn-™Mi'†fVR5W:æ°‹Ôò2 \GãÊë…ݳ¡Û¯¼¶y©çѶÁš­ƒ®Mâà O –Ãû²ª8¶¶¸Ò® àmãàÍU¶Óé«Nþ¥ËÐ:_$#Ë7úlf°¬x5¿LwcB6¹kWqÆtÅ\^ÌS æv­!+B(7¨­eß÷Uwx=óŠ€ó° =+ÐuwÁ^c?é= ]¬tõÜlxgN™°ÆöÔ©sëFL_ ’Âã•cþæ °0¼ùf³éð²bŠVGex°¼ÝgŸ}’?}û&õ%€° ýð@*äŸ{î¹à±&²ÜY|™àIŒtxÓJâ4ÝVxò2è+ùÊBƒ¾3ΈŸLuB;Î:ë¬Ææqœ®‹!Ah´k‘EÉô^Å ¤ëòñ:j j j j`pi n²\mŽ­Ð€·Ü`}9ƬîÇ@»Ö /¼âÎK—uºÚÚ%뮺»™¾â6r¾à•qz«X;'A_|I –ð)§(°ú–Ø¡ /ô ú‚7ô…v6Jµ’Œ¡ÙškOM`ŽoI¶½TXªŽ9ìó(]”úè£ÉkÜ ú¢°ôÅʦšˆƒkb˜³ý㊤_hcòyt•s¥s‘Ó¸«.>öv‡'&6BdEçX/,czß‘pºâ%³ÌãÛé²nDˆ« ÐóGôE8€é• „Aè5¸¥—}à`ºß! /j†Ws€¾Øðq}úິÀkP:M¬¨Á_+ze’2”å¤Ó¥ýXM$ÃÖÈÎÂ=ÙP¨M¾9§SÑþBè|Ëïi䨒ÚX–0¯2-Kza\/T»‡“Û<Ú´_9@_´ˆc€Ãk¿,5‘Ó¬„¦–³é¥®ôFnkÀ„peÀ_€ÆìÅ YR&Ëöï¾çn#cxÍ~øá樣Ž2m´Q£(YQÑHLO¾þõ¯'ü(³Þzëe²!{É%—4|°7/ÓÆoœ¤5Š“2Ç¥—^:©ôèÑaî¹§9°¿õ­o™cÿp¬9ôÐC“8Ʋ {4Ê4yþ /$ÞÜö“ŸüÄì²Ë.‰,Ù> 23ZOæMýݶ}ÈÓ.RÔ@Y ìÞÆò²uVQK¡·ÉYÖ©—ÇWQï@‘!Õ?—ðsBø¦VwÎ)w\‘ {?W¿“~ »$·ÇÓ%3/]‚IG×>ZyÅ*ɧ·Õ䉘¶ß'ðñ]?¯¤–V!ˆÉéÛ\«µD~ ¼5m¤ãÚËk^R.ËýÊ3“yÖЖ]Û`¼±;=@M¦žIMHÍåˆ/þñ*Þ2Êg#C³D÷‘C¸H ˆ¹Êc¶þpY×ÏÍÔ´i*SÓ-¬ƒaò¿4¶˜¥óŸôLgdLo[¨ æÇQÆK–é8˜Ï›qéùdsÍ5×$¾;í´“9ì°Ã’?Ä-Ž55555ÐM ¼@?2 ¤<ÖJ»ªnGóg{9É <•+KE ô¿$,¡Õ¢­{«hüW'qO³xc±z< ±K¹/ŽgùÏñl[ôUHLM]¦ªë™-±}ÚýÀþkð+Éhóß?rb³.:¨ ô‚ k‰¾ŽkÿDÚ7Ȇ\^kÊž€G:bf‚t½_ˆµY‡êy¼yß§t‰Ýé7.è! ðhnË’|¡^Só¿•š©õ³‡E_užëZöGo‚ä2:ëçFŽèÂEkß%#@¾ª`>lj¾=gÓIxšúè õ[ë?}„”óã=7ÐZ³ï­BÞú¾6ë<€©¼ Ò£t=K<ì?¤@«–SÕµ­N–{§ B?WñX—ùhjã»Tˆïï`U7Æb¶‚EfŽƒÀï®c”þ±×>§XÞ˜ãü…I Ư•>àŠ=Λ>§¯:Ʊ4h4ÝßPÒí ->Äôî(Õ.®D<Œ-£¥ç%‚»$¤à×ú"n-@ërË-×èNQðwú鳋ä†q ¡žxÓ2!î¯$„‡øæ7¿ÙH’žÁD:áØ¿HÓí‘|¡çÈEY/6pÓa#d»áýŒ˜Á.à.={!›¼ýå/1Gq„ùÇ?þ‘lööÕ¯~Õ%"¦G D D D D D  „ÿl·w˜'{îÐN}ÎâÁVEÏ1CˆwìoÑðÝ÷‰ éQ÷yª07m”´t¿WͱK¹6O¤û8êy æsv—Cƒ\ÛPn`™»$„‹¸%È?  €6²y(BR^ø›,NC¬LÐâ^\LÐ*â–Ûh57¼÷@§W.ÓÉtŽ>1a¯·Ó‹Ó<Õ.Áá X~; ËÐGÙŸÝ ‚Zº¨YE !½ ÒÑ´LýâÔ³r\úgàU·×wLpK»)kUͳ?òWM7íuÍ;·ˆ¿ñ3ü‹xÕ¡4"eDˆ€r„®C"¡*V‡ ¶µ¦ò¦há/̘*ZK¸61<×¢ µtõ)›ˆûÉÒ«4°Lx™/IÏŽûøËþëap=yF¯™Î5(_3X·,'ä(Ÿ6’`“i¦|Gƒùˆu4³7޼‘£ôZFHê]+=ÿ(q‚øÂÓ”(§‹`5‡œ‡êÁ24—fÉÖ¼  úÎ=÷ÜÉfe²p;à/bä e’ )§ñm¹ñÆ“¿'ž¨[L$HjmeâãÚhøð"¯›„lšv'NœØ`íáD†ð>ÚrË-Í<óÌce^Ž?þxóÌ3ÏXócbÔ@Ô@Ô@Ô@Ô@ÔÀàÓÀ%ާÁדjZ¼:}ì¸~ü¢†yó[<تh¼wx#˜¿ÁÜ–-(î÷é ‚@ÿØI'.¥ØèŽç¨ªyöl‹|Û¼ƒ¾¡ç?f¯š½¶‘ï£Óû3­å>‰Ëí«£HžŽmZvýÀ»·mNˆ‚ÐúŠò…Ì»x@é„©ã¼ ­KžÑoc”MÆÍ¸µåË4ù ðJ’•ÉHobйÐâóÄõpÂkû·Ÿð’†uH¨‡[<íÇ» Œ¶µÄf ùáæöçœo¢€n^<Û¬oVsÈyÈõl´åñk}7Ùd[{¬ž¿×_½•W'J ôá‡6r£6æ}ýõ×oÖë®»ÎàoÂØ5Œ1‚íZƺ¡™Üälæ™õ+ƒ¥W{„§±$ÙÆ_|ÑhZ¶åä¦oRŸÃëwß}÷5p@“Xz53O¨î™?£¢¢¢¢¢®6›» ÜVv¯e·Ò‡½/Þ¢Ž‘×½– îšF¤ñ%w/²­¿Ñ;RȋӖ1Px™ÅíÈî¼Rh˜m½û }ÀÍ•ÍÑ@X>' bù2ˆcdf[\épE a¹?Œ%ß ¸­MY]¢Ø5ƒk¾Rõ¯N‡=ïdðc/‘Ÿ O9ÄŠåeäXû¶Ã£“AuHÏ~¥6ëƒ\€¥ *0°µ½€ß¤üç 8 ¼tRu( ¤IØ%crÛQÓÇ”Àž øâ†ê¶<"žu€œè+â¥â%ŠÖPŒUj¯m—öjæxÊà [R—x¦³g¨kW½:}Ù\0[À×ßD]ˆ£[%ÁX\…«â±7Ñ' ·ò€ÒgŠô¿ ÷‰ï­Auê¹›kæ|­k¦Þå:Ýw ƒÊ^âÇOd¬^_ù*óÖð´ï‚vW½é¶ºB~œ`1Lé²®kלËüx¶e(Nw]£¶€_xò.¼ðÂIðôu¾Ü(éù»à‚ šu×]—³¼GÄ•töÙg›÷ÞkÚ¤øÒK/•,›¦dØ€ª}?‹:½ûî»›º1¹6bãü"Ǿ>ÿ²+)K×+ãøöMê32ðsÌa4p,e]uÕUæÈ#Lþn»í6³ÖZk™=÷ÜÓ}ôÑf‰%–h°ÚÀóFf<‰ˆˆˆˆˆỖº#±ñ…4€ÍGŠRYo¢¢õ Vþ¥`™¯?³Ð‡xÍê„Øê%»·¦Crá¶ ØUÀͯ¤zÍýþáõ@dÀO… jÛB:|?GyìËlE–Ës×sB.’+{ްðÏÛ0)[*ì žr î…•0™0‘l¹øúþL»gYHºDuج))dE¸×›äƒ›òpvƒÒ£:› }6ï« #³­ºDø¾à˜_UÏ#ŒïªJÖúDV ¥ø}—z4Á^÷ÔGÆ+¶ÐC_9Ë'60¯Ü7‰HbC§DíÇUbÎà1Ï!^À»J °r9ÌL‹#@j€ÌûØË:Þ2ýÍÃeB ê‚eäÄ¸ÆøÅ£zßOÎ(`ƒú}+ ˆ)‹ÙVm0ÇéeŽ6ZËç/ð<¹÷ ½ûy1›Î—aùA¿C§¡*!ÄÀµË8ˆÿ®¤æËƒñúRg Àß"ð·(}ÿûßÏxïÂËž°ˆY‹s¦9çœÓl¶Ùf|™À²}ðtE˜„ƒ@è˜1cÌL3éi¹!&è!˜C÷ /4rs9ÎÓÇÞa½-À9@ßk®¹Æü÷¿ÿm°ü^j©¥×¶“VX! âꫯ6'œpB²¹ÛøñãEVYe•Æy<‰ˆˆèÀ.Ó‘üØDýàôsWŸ+cU/}pJ\¼àæ5ƒ³—Õ´z ú¸¨’ž¦]®AUWO*ÛÛŸ²¾KãTnJÕŸméϺç*Ó¹Û/€c.@ئ;½š‡ÓäÒoNó±qP§Þ„Lûx²IÐóa2~¼L¾°$ϸ¥½?¹>}”mÑyy× °ASˆ9›ç5&åe¡ÎfŽí½ò0š,³ç ¨ åï,n7 ré žÕ8%‚=./@É+ÏgY{ÿ<]N>Å$[ïê¥_€$6¹ƒ·0èÒ&dÐàY‘g'’þcP—-¡—Ó('“õ’¡N†á1 ú´’´w+çq|kW>óáˆeí6jx:ƒ$p胙æí#Mõ,Ÿ”‘›káþøˆû&yZ(™Yòü74&ŽªÕ±Ÿ9E衆×äÛ·HÎáW-Á{<;¨ªzÖ£„æÚŠ5Å98m›&Ê玥=@÷rU`/=N!ÓãjŸ4@> Ú2¯ïˆçœA]êt=3£Õï[uLøíÚß§™ÐBI·qz±:ÃæQûµïRõ,éòò:t.4Élú¼,jî¡ç€|YzHåeäÜ—×¼‰iQÕþÊ.Z{~lð¶ß~ûŸWî:ë¬cöÙgó•¯d_«u·Þzk3õÔuë¬iÛm·]â¹Ìé½½åÔ²òÊ+Ï>²µüðjÞÿý3ž¿R<}wÝeW™”9ç6Cþ®»îêÜÜ zÜj«­¡02BâEÔ@Ô@Ô@Ô@ÔÀÖ@ö×A±Žâ‡j¤Îià‘žª¾Ö\Ôð òqÏ»:Øc¤¸l.ÁÞL|Ýé£RiÖ\d˜f©¡6¶B£$V>­I€ÓÇÇyr4N«êˆƒŠP‘òKì7lÔ„ÚŒ9Ry}åµl¾ ÿ•l[[\i °¡=ˆÉ‹.~™>SK°™›=_H0œ[4äÃØÌŸÎS7Þ… D2?ׇã/ȳTÅ;óP¨cs×Â&w¬7*] u‘МÓÀËÂý'Šÿà1É„ösx íÝÊ<â‚óCÃZpyJïÇ“¢n™séõi°MTíž„¥ÈxÝO…š(ÃW·Éuísz¼&]$×ñH`•Óy%Ýùj€1Ájª]gNž·eWLIòmèÜ.8zï—_~iFŽif›m63|øpoSkµZ6¿!N.bëÎ:+EØR›­y…d“ø³Ï>Kèg˜ÑÀ£·!ö06«›8qbÒ7ôoÊ)1#Èyùå—Í'Ÿ|b›ÂšmTáö«5rG ôŸÆŽ›T>nܸŽ4/z×ÈŽT…F  `‰O§À,QÍ€9…§Ú‡ô…?œ«h>DôÍ*äðEh~V”Vu§˜Ÿ%o ÛÇ[wZP¯e"Åj“Ëööì™Êü±öipð¡Sà]„÷q y/XÆÌ›%7z1îC÷ä$Ë=W`bþ‘ )<ß|;ŒïD ÞÿÖâûXbŠŽ£êjîœ$ý› ¡|^ºº%øêÓÞ¦‡èrXm¢f×ð¶«âý†°ðr.Jp‡‚ßdh;°”›Ã—×uå@IDAT‚­«,?<5] L±E€É6 æïê"\Bà¼(6WÃÆ_’$èÏ¿§hžé„Ù òã${!:NMðÏÖ.a;ÿD §ŽGîÔÜfêÐTô‡¸Ì …©½xóY6C“qqe^È9ÂwÀ“±juØY¬ìÅ.ÓmçwÒæhõ½KïŠf,\惷¶-®7VzlG àï(x2Ý›Ó=½ûF2v¾hÎïS^kÌçßcÑf)âù€ô£=B ]:Á»h}alÀ*21ÐÛi.ÎåèÞvÒ[¦}0"URë\­÷!КCȸڣÁæb®¨.©1=j j j j`²Õ@}'Û[ß/—^xýÒ€\iƒ¾íÄ?ë´Z}›Þ„Ö ¯Ë"/›þ$€ Ý ÿz»n´ ³uÀ«Zƒ¾×¥K⫨¹ÐõãCûl0F8Æ›}·WÔÜãØÅÝÕ/xp…½èÛ‰'@ƒ¾_A7ˆ8«ÜþM,­Ý™€œ­Òر̇ã·éÃ\“ '¡óp-uËù[vh—^›Ì—w„Ç&±i1·kÐy`g}«ˆ“¾¾ð}dÐ÷(‹žP¯¦*AßÐ÷ÈÐmƒ¡¦ñ’ùÐ 7¼¥g±Œî×8)Û A_äIïû;ú¢žÿlxwxú7%€¾ •žþJ`£ Hïi¬`œK‚$€¾€Û¥uˆ·)åŸdˆx’tªA_p´ú¢<‡ïð¾à } ƒV£ÍÑàÕjóž‹Æ èÏ)orAÿ¦t€¾ú×@_îÝC4ÖaÔÛˆôƒyjÚô…A ô ñ8Aßžeˆcî„/䟌ëÂ/yU´U€¾ÍˆÉ²&÷ù£=Ëè;¿›¡Ÿr$èû{z–¬¡BÛv—<ТNWÆ„Ö7\`…‘-j j j j óøY#E D D  ´ÿ¬Óýs~¨¸HȈï TÛ`ÅgãšéÇc#Ñs"A[ÛYXÀµ¶…pë ¯¦v›ºš¸§ú㿈l›7™.¯7ÏZ^íâ®ùõ5Æ  Ò¼¸ÎúèÙ8ÚOcðu>DÒÍÒå–ÖîKàįic$M÷* ù3 pï'žØ‹ð”ešÚk”ylÇ+ÈkWÒ‘ÔN¿qC%ŸÃK4'ý+§sVðñ–çø`KŒÝ͉¯ÙÛVñXBÝUñ‘#á|t?ŠÄBÈ 1?5­–.çÒÙÉõbÔx%úiÿ—Û²s 6J#€7xºB4<˜nZ•Ý®Ž%;Þ˜9še¾C†ˆ<ã‹4xüÖk¨øzSpz6MKJ6AOñ<"ìš&öšÆÜƒ„$„X]Ÿ¦KsâØ2½–Î;‹ ‹,9P2jBê1€ûIäe^3¬ëÑiÎ…bî8Vd¨v?]ŽÏ$é‹7RãÒgh´ ¾!¥æÅ5¼—1Ž™à­Ž¶ÂxQ„°šFµ¶¥xhlxÁëñ©o‘Óí„y„~tÝxîôjÍ㻆¡7„48ß­Ež¨¨¨~ÒÀYÊZ×O͈ÕF D D D ô³°Høfúðht‰\ëd§Â¥7Ð!9à˜(tª?úƒ •`ºMÜÓO –×»¨,^˜Ý浨…0˜‚U–ȇ–÷‡>¸mËé³pM]Ò Ü;Ql2w™ZÀ OÙvicªžÉÓ¥B³Ä-Ö›§a™uƒ¾Ø¥qê:ø4¿'þøãy§r\bKÅÍt « cú´­Ç§€ƓДYÑcüÐ:Fs4@/›˜Ýh*»?Ïì)#â¬Ú¬¦i5Áƒ½øÝ~•Ò¹¬>.Ó(¡súçú%qŸð›EåýÓ"ø†ì¯ÖÅz£¢¢&s `ÇÒHQQƒ_ìÒÄ‹5ЮB³vëévyövB½¬;Ü޵Ó6|p4’žbh–m3À’ç٘חG”ÇëËg0ÅÂbL‰sØ,Á;´§hLW,e‰Ã. gÄù¿H´y_røx˜2qh•‹Ô‚8ÏEàÓ›)8—˜Ü#®ª&yï°ÑaU}×õ„^ F‡P†=+o:D:–ʃöK½¼1ž¤Gp’IÿêCøª~dKmH€^zJoSÄ^6´ø^ÒkòÂrŽ…ý¶=Î2lT£DÈsêÉ¿¶L[ÿ¼™ŽíËžè\öá¤2m:v^Hõïfßo„a’l솮p¼q|îyî¼k¥!$­ãås¡Ë49³ìãŠIÊã4Ș•/rŽü–†…oö½™)1 ®²ýðÅø|Ÿú>kftgŠ5.d¸ <ω¬Ú X‹ØÀ6:TÍû[W2¼˜…w»öµÉ´¥ÉgóG*Äæ—«^‡ få»]óÛ®µ·¸§¿Ò03K ¼¿ÚaýÕšXoÔ@Ô@Ô@Ô@Ô@Ô@ÔÀÔ¼bBIÆ -3”ø°C÷`'Úv¦_»€Ð˜õkÃpåg©%ê·+i 4=Åd[8xš>ü™Ø“Óf1>.Ó´ 2Öó9/EY߯žÜðÆÓÁ°ä²o)ƒÏØàÉØ¡c¢—Y~Ïq8Y~Ùãè¤Y'õ~Ü»–cùï²°ÇÖ.@Ö ¦ü«”K °ØÅ†(‡M´@úÞ¹ú®=5¨“süãºÙ™ä%i\Jp@!hM‹÷µxDŒ ßædÍ' ÜM’ÞŸ¿€Ç^fN€Â. Žy\G„”8¾¯é{ú®xÎÑn6`´,ñw´y|2ëm4çÉ1t¬€8ÈØD ä[™€x³ öä„w3ÿ¦Á{ ñößq°o'p‘‰ *|ÍGl"í ‚˜´úç"<Þ÷,áx0^¦œÔêR³=Í‹Т9ódà׿+m|ByI ¤úBškŸüÞHz…áç`ë]/kÈan”¦'Ü;Ìc(Ï„Ñóï8±’ÁÃV8‹ŸÍ•¨MŠ1k$W½Ôó)&qÏ*6ÖFt#ÕP „ðâ縓ýÁ·Îek¯½vRï˜1c:Y”555P™ÆŽ›È7n\e2£ ¨¤Ä]»3ýQ=ÚÛ5PDïÑG߬³ÌÒÀ"õTÉ /»ÏªH²ðÁ÷ŠýgxÅ5Eq¬xªIÐ`ɾ–x¯ÌßÎq[òr:70_^=®¶¹h Á;ï{\›Áò$v&à B)`‰<{K¶[ÀZ›ÙLßKW=ˆ!É ”ä@ùSä$`'óaØ^mV$óËœ¨°ªéUZò=‡XnÍùçS¶NÛV79_ÏÃàôͯ=)¸µ"ýfÐÞš²L™süÁòô›ؽ H²¬õi,.Ú3¥ù]êú5Ê(à¶\*Ͳåq Kn ¡ÌÃ9tçÞÉš[Ë̸/Ccõz±Eóæ]ÿžóKè9o÷}pú j«- „« +P™Gé7¡mLÉ2¸ ï–ß;šá1†€<›./h ˆszÞñ<ëÛ´ñ¼ÁÓÛús½ÛÑ}:‡îÂiLEã⚊çK„ò€'9曬_/· äqدiþ¢\}K«ôë2ê×tÿVà>Z… ¢ÄÐù¿ê.aUÆÊãRÕõ8å ;Ú˜I¿pfû2¢Ç¯O;1/j j j j j`i ‚¾èfĦ”ÖÀŒ¾o‰ IJ7&°`øgq @bÓ /<+·¢8ñÞtÉé2¦#§Ëe·œæ;rÜJO'ó´—n^]ÚCô}á‰ZÆÏå¡Êm ïr–hÊóB”ü±lÄK²å–:¶eú¶²®´?PlÙ²´bâ»fÚ}G©ÊߢkŠ€³áév½XÎ=g¤GxØ!î³ ô ¼âô˜¥Iƒ¾ Û%@·”ôE] úâÜú"~“ØP /‡è /@ö DŒQ¦ (Ò$cFØ`#l"WI0o#n%jFj}9¦%@ßÖšÀí¦·,`­æ~Œz<^ôGçC×.ô=L}èYô…Œ¿§ ïiiثĘD¾d,ß]SíüšÊkÐW{°?¤<Áï¦ö»ÆêÝ!• ÏÓ7,:ý1Å‘}E Â\úˆA_W(!è;QxøB66Ô“ôõ  ïF4æê~·27ìÜúB@_Æ.ƒ¾ }žNúˆê"„cñyøHFDó<†ñ.@Šß§ó,Œ50Úœz+£ÿøïêä}<é'èK¬I¿J ô­Ò«÷ Ï»ô8µñ„¤á}ýÝáwXY¥×Ê@‰G·ñ.sɬô•›ºêq¦—}YÌ`-äñ[ß-/¦µê+ê$ê$ŽþqÞêÿ{Ÿƒx&—1@¼ÄßHôû‘>†¢*ÒÁïz¦«a\ÑG`¿é”¼hÚ®›€G¯ ¡àÍèsXµ_õL“é5Z I“ý¸®gæšÎ|Éë;Ÿ•d ™‘C@MæÚWÞ—‡g™ÀÓJdùê±å‘çb¼„k´!\Í5>hÓ¨Bm#o×Dž­>C ɲɨ2ÍõÌÙ3mK;;1?H[#˜7SÅÉ5… IŽŠeò ê­ð“I»·w–䚀­yTgòªÔ˺;­¯å‘ 3•Õÿ—žjR ùã:hã³Êä³L>b|b>~ñÌÓRð€µ-s'ÆÆ:Ê^\ðw …7(Ý´‹ÛKF‘Nãã2žgŒL‰ ]kí´…ëÊ;’Wq£Íà¥xÉ™k¤“¶‰e]hÑ)ä5Œæ±‘oû}¤õtFÚ&ò”ÏÈÃ<:ÆBŸ5´‰V¹$õøž©ÏÖ[ëGÎC¾{œ÷›Â6¦mõºæHo3md¦x†šyþû×}>ÜÿÙ=íûª'/¸/vÆ ØõÒýAÛuÇ@èˆóV+¡c%òűRõ Á*~” (ÿ´|dj½‘ß΀j³nŸïZ>Þªóð¡N^†Ýᣬ ð¦åØÚjûµñÅ´îŒç­ˆVDï"$3n4øVDV;¼´ae¦ä9\»@UZvŸá—uTãk~.5HÃù®#y 6d¸xÈ6áÁ|Ý``ÓU‡/]ƒ;>^WyfꦕµE…þ¸y÷%| ,Á8ÁyúHËó3yS”ð}å2. ç~Äíàò|ü+}Ú€Ây8†‚^² Î)$F¦/:ßuñL±dK•…Lò®ùtìªW¦_#ÆÀ^y¯]@#yÞ®—<¥k'èº7²M|Žg†¼SKë‡å„·TãQ—q‡4_è5 NàÅqBïÈDŸ?Èiƒ+ûôLeÕÍñ)ÐÊm¡UV>ÎDZ§gÍ ­ø¨Áø yä9Æž¼ö·;F5pºæöŒ‹„ø¿‚Ûçk{#oø­aòz– âëäï÷p°€üžöâ“5¾5ôA÷€Ïa˜¦X×kNOMF™»^¤ŽâyÔQk Äyk`Ýø|ÄûÇÀÐE>ãXð¤:¥' Þtªž"rm²<ÅlÌ|¼\nñ¼’ü8?°§œW …¬¨Õ†ÊÔ§eç]ç.à&On7òiyq[}—mHÁ×.¹ððežÐcý¹ÀöV£%÷Aí¸@y ºÚ ¯}WÒч<#ÌnfxC< µ<6H€ÕKæ×4æùžš0ØIj ¥ÑRÏæàK‚ÌéáÍ™D¢E†n{Õ×lxÄý¤é*«ÿ\q¿«ôÎë?-moé…#ȤåÍ›yuè|鹫óôµ+Æxãvb¼¼Ó;[£ì1ªeó5î)m×àçôNQm*بíÞ†6ià€1†™&UU·mž:H›·%†¼ŸØ ÆuQè§mœØh7§ç w¤|†]ü¸‡®¼þL׿ ªx>;y/ÛÕÆÜ’–÷€O®Ç¨Ù:Á@PPìzñ)9æEÅ1пc Î[ý«ÿ8þ£þãÀ‡ƒƒ±ý±Íöç®ì2Æu xÎT¥{,“eY.¯!ÎŒG—7 ÔèýãeïÝnçe)¨E1X“%ô˜³¤7 ­=E¼Ç5h·o  ÛA[]¡i6 ¸]´Ðº%˜”¾²L´º©Úp2Vxvr]o§á†4 Nöâg°;Ä«›âì¶x~®—Î-ÒËP{*rý> ÇæE Ïs€d@жlžewòÏXŧ6“õåñÌ;ƒ ŽaØàûÂe‹Vƒù)ÎkãœÓòŽò}‘Ç›—}Á±a ¯ 燄•¸U︬<ÚÆ–ÌÏ;G˜¾ÿy¼®|~NðìQìû–ß„r~„ž0g¹ŒJ²6Ò rqļMq ÷ò=›rp¯ÁƒúŠÿ¾%ÏåÞ32dŸø¼ÝûÆrÚ=†ê!¿ž¯çö9_Fvîi—ãl]šÃ+Ôµ½@±ë¥ÝËG½–›8¬îed å2qÞŠÏ×Pß±oß{˜©jÛÒ‡Ï@×/möá]N7ÐÛ?Ù¶¯wû~òW…Þù£¸¬,3eËv£\» ¯%tL^¿+¯\|Ãæýßæ9Èöyì¹êÖé!ÞGõe,ÉßXâßê¶ÊköFDšË!ùå¹-LmœÕÕq ¯Þo+Ï/„Õð=çž«Üù<â½Âé8ê0"2Ïw.AuÍçËï‹©§ò¡9KàK—Ìë¸Ç l¸sÅÚæåüº}|ýÙ”‡dt¡½"uèéÌ2øh3pp^óËÒGx€Â;T§û®á!Þ®ñ%×0Ò³Z£M´!gãí*âmÉ1¤}ýAž6XÑFš5ßó ¯u)“6ßj\Ããò Z:/cýü€ ½ÌQÏf|׿?l6ç»EÎWya3d¿‹œC‹È¯\‘P´,øaD+Vn‰‚üÅž³bméŒlš—ÁtÈ(I¸qã²tuÞy絤ń¨¨¨nj`›m¶i©Î7oµ0Ç„¨¨¨‚ ¯+ƒÊûƒÈâoþK?ÙðËt(} ™q¯° ù•bWô*ï yÓ™÷«8™Ê"o-ó¦£ï%ôȧxæ?¦O¤ÔO `¢ýàóézÚ±}ÝÚ;ùŒ8ús,7 y#m+…$1¿¬}”Ûò¢í%ðÒe£žÜ ÚdxŒv¤_²ï­Æ“»“nþf>3´Iý7æcú#PÇ„ÞEòâ4ÎcäYk*›šÑ Ý&’¼KÍ >!pÊkž¦vÑÆyæâ”‡sshzÉ›Ì\ßЗ ;âô¢ê3ô· ɼª„LZ}“è=¬öV.vÍeJdè0oµ²6Rt4O©>42'À¼-òÈaîWó…î‹mî ¯o3†æ‰½z¦2§Ö>ÃNi34ó-êó¿©ÏÏSÈKܼNGœkBýxÆ0® §ÖÖÌ£‚ïíÞ‘f–>~Êué*®;ûëéèžéÌ/jæ6”ŒfÿÚ†VP˜Mkï6ø]s>håéÕÌiÐ#žÛyH÷/‘þ¦§óÃIæ¾$³ÛDÞÚ™{Èõ£MóÒ=~HIÎ/s$ãÁÕÏ{Y(CžÐæ|óyÙâÁåœ6ãÅ8.X£í¹¯Ht)1½¥JÅBQƒXäÙA5l‘¢¢†Š§™þ"ZúÛ_UÇz»¬e’ŸýîJtŠðƒ»õs®Sµu^.…TH*ym÷ †hƒ¦FrÐ’’\õ¼/™Ú<È0¹’ ô…>ð¹®?Ùm /xC@_ðU úB¦ OhI^\HNè‡ô]„Èû­{Œ }!\¶WWF‰e’È“.[‹ÂNbÊ̸i /ÐH{YšçÉ;ؼ8¬ù¤¢Åä –¢f~Êkr·°dÆ9@_òdÍðAy¯fÒ4X À÷-À3‘Jô¥Í¿ /yû6@_€ø}É#7SÀR&òöçÓä# Óûx' ›ÝŒëmÎ-€ÒÊ€¾ PRZFWéäÌõÇtŸpo®U /˜úÒ¦s~yñf÷m†–• />±ÈÐ}™]éåú‚Ê€¾(÷Õ{6g<*ï q`})N¬ÀÍmz˜ø$è Yy /ô CB(ý€Ø,µšÌŽÃ Q”`8Ñ”}{~@ÙÙ1Íü}AôpïšóÁ Є9Ÿ?€¾ H+ úbT¾Ð;b’ûCªÉ9ÿ#ÏpïØ×÷Ë¡Me@_\ô$õ·*Ðuü£ƒ /m‚×è _VêY’³úÖ|;X'%×­cÛÊ–$þˆÞPÎ6¸‹9s˜Ëþ9î;ì(“£»¾¤|`yIö~ÅöG LÎ8$À©Súy.Î'R퀓‹v1xàã É+þ)"ÕÏ@ ›tCêöi7+uÔUÿôj͈a£kJx³Éÿü!ɲ]õp~G\²àu¦éŽ^xi r|âUÒszàZ'ûç çVw”œ”º0– ëï–h9ÞdYœ? Â)Nj°øÀËPÀ€èJ‡±á"Ñö?ÑGú–Âó®^²ù¿uT6óh9s)à^‘+9wYCƒð›½æùW‰gÓIM_Q¤LÜ ÁE ðí5WМ Ÿëƒå„B04RPþ•úyÏP;hI¹ù+éÉ¥´×Fè#èŸ"ÿf:§êÌŸIƒøXI‚'0h *ÃÂÉijO v&ôýàvÿ²gÒkfí>†Ã˜«º#Zv­e¾Å¸ašzÒëÉ=«C÷õT9Ž—ékBµÒhÎáSüÉÀs/NÁ+Úz€Í£—‰G¾gN‘í§Â¸ÇL²oœ†£~¦¹M#.¢˜ñæˆÚDó8ñ`Äi®r:c †„:–ÆÌï[èóçiÔÿéx5Í.`ŸâàZ«Ëûfjÿ rnh¡¸ë˜KhÓ³FÅ&nœóÉÝm ÇÙ…• ÷½™”ƒ×¹6˜Œ¤6ɱ1B›5êA›)üHã:ødÊë¬xÆ™š3§tîØº>Á]f19_º9ë9»ÖêfGôíÎÆŒUÏÛ›ž‡ä)¯¥ BnºË‘ïÛRÒ?Óût_)0¥ Û9¼¤¹¶ü¼´æ,“Çó£¢¢¢¢¢†”°T,ÒÀÓ€ö²êF ¸è#áùæâ©*ýòþë6Õ?½:[«üñ£F6¯³•úZ½´:Ýîñ©T™zÂ?õýÒm^hÛÕÞk’þµ;—‚Ý h£:aØ1d¼Ð†]²h±Í/÷Iʰ]˜@? PI¡-€±Ìçt@„»Ø=ýH¯—¨ÿ_#,qò¸æj„Ÿ@hx·…Ò©)ÈÀåß4ßÜeB@,ifÐPω*÷åOIËꥨ8üú ÃÂM²÷úéBwT$ñ$.Þ L•ÄíwÉüªúmƒ~ÔF» _îžxΚžµ)|CsLûX°D…<’ư„žx,ry¼k·í‘öÏ·7™Ç æðÁããüqZ ¡iÒ9?:×È»tïkS]a.]ÝòîÛÜ’Ö3éU›Ø–4Ü'›§²4VÈB˜ß>‘ tþò¬_S´a9‚зóˆbw_~¾n#ïÇbÞ#Æ`ƒ¡K''¦cÜV~™ÊùÒÆcKã¾aE¿C?®ñʹæ³n+‹´æßÅáNÿޏOn®îçd%t¿þXcÔ@Ô@Ô@ÔÀ Ò– F:p-:=œ=)>ÀÕSÚHÅ•œ>-yTåƒ*y|yù›{¼ÿòÊög>€Úì'¨ LhÓÖ ^fê# ƒl܆УïC[ʨÃ2Ř¹S¨ljw¯àsÕºœxþ5 àâåtéᇴP”Ëêø¡Ó¿„8FcèG»oÔ!Þ¡g €nzƒÍLõ1áƒýmº`0š?¨ma!>æZÖî{ òU@œ1ä†ppÐÀ1<™ï2L n$¼ÏLŸ÷ÙÅx`¾áăûoþE{êZ×}Ìû j‡íYà|>2 Î×|” ÿ;”ø ÝCüRC­(³-Í©›ÑŸüõF›ºQN=´€lÛý•ï¡]”îø~$BÔ?YÏó¤/ x#ÌHë^‰º|•âÒºè Húˆ% ©ËcîÍx´®!€€sx& '­Ac†½†á½>síÊäÞk^ö¼J¨›ÃÈXÄ÷)æ,é_­Gš¢ê÷t^ÇKÆùòì^Q=ÓðὕžI0,•ò=Ÿ‚Û€Êú \ò®4·”%ܧ{©¸oy“æ·Ó¶al!žööbLBÇõ†¡a!@ðÔG˜šnÑžg°H~"ŒEÊ…ðbE¿Cá;ðé›ô€ŽéH3³o¼ŽT…5 Ô¾ µ^ÅþD D „h€¸†ðFÓ°2G]D ô—ðA“GÚ“(ß–Ï  >CÀó"Nä@#°akëDJÜ+Õu¨ ï®ËÅrw,½v{“õ‡‘†A6WÛdºÈô‡¶ä“çE¼5e¹þ>—ýcO¢¼6I?ïŽ9žÃÒsaäS=Á&БÆÏœ-ûý4ÏæÿõñÅH¥ñ ϼnÑ%âÙø}o½ÅÞòè^ÒËáä¥*ï¯ b¢JÂRî“jŸ$›6L ÏLŽiÀsM ³±- ‡—âô¼_DžÒ3qƒª‚71ß›7½¬íÝI,3—yò\/5ç8éô_„@*^ûõLmP†ï„¿VQõÞÂß´€F6 ÷ƒCIÐi†þ˜z#q> %àÍà½e×2B<Ð9hÏfd¼šçO×\ØÎ)õ#<úm}ÏrÙ¯V$ÀñQ±*à;ß{[ øÏ¨€Ø»’®µ„6¸‡îß+\Û^éK‹ð#R=0ð*w)_žH”ô{W/êZ—¥šçÅ3qvÓ¬P˜ ›«Á+– ³â"Ÿ®Þ6€p‹ÔXŒ%ðÞ•d ƒªÁ! á%l›M ¹G¨P,²æg„|´µOÞ7ïâÄЀ.ŒÉMƒç…i]†JŽí|óJWV ¿©)ÀGÁ ïr¦Eéz9q?8]GzžAÍ;”®%¸Ý…þÑÁæYš Ææ°8\‚šOX‰Â±Èä©þ\Ú3yj<ö:j j`°jà@ú’žYU÷#ßw ê‡¾¼í n”Ô Dð$xCUEðp !|(2/âD4 št›È” ®æ±]c鵋ö|lºÊÈtö’iúÜí7gÌZôÊÔ<ã”æ‘2€G6Ú%óYiã¨.ÍåñY] MIìIÔL)w–ç‰4VyÎÙ~‹K0g5ºo,æV…ŒëYéCVÆ æ´,£ÓÇ;…Wœô¼Ì«ó â^†êAߨ#K¹—%°`Õa Û]ý¾ÆPÅúMA'®ójuŸŽ y\ôrÚäxœóhö—ã¤ër©hûÓµÏ ÊØ–˜Ëp\€å\Ômô‚ÇœJÂF»¥ÞÁ2²æóÅ¥Õ¼Ú[ž>æc¯Clz8'Ò÷’çOö¨ÆØ–pïó2Þ¹æ/YÆv>lRêœû¬'d Œxð¸Eì]Iß#^ L/O÷oN®áM1 õa»tLá9gðŠØ¹ö»byªÊ¸©ÿ.¹,3ïøžÇ5/6Wã ÉnOÃlÌDmÜÝón”2Ž pã±gbÐXZfµœß+æ)82{yìAžÝLк¼W˜Ÿm€1óãxºØ×wÍ»‡”:ÁC Ým©ÿx.õo4åc…ôžçß ÃÖÝô·‰Å €|I0ÈH1¡ï¥²zsIY¦Ûç¡ñŒùyîdû\GÌlèÎ÷;@ŸòÚuƒ3^€ð­T®—ømÕdL‰ˆˆˆˆ¨D?¤B\",ˆ4¿õ‡Ê@lik›Î²l”ÔÊU,»%ËÒb¥9F ®²íÈwÉìTz·7’ëT?º)Wƒ1¡u³'ËJ]t£€øÃ'˜ðÔ²Ñé™ÏJGkšÏÃÌî†z|¶ÖhÌæ) T…¼M¾/-ô»€zìºÎK£ÿGí,Þ¢oÙ[ cËͪ~@ånc@–Æà¥û2ýº Aòó9ÕßV'燵æElD¦ ±ÄQÏ]Ô·§éžqˆÃý,éÇ6¿»mËË!F]6^±|‡€G:6äqÑ\äû°«q¤ˆë*“—Žû´ÃRoé{÷O¶“u²lh×h-ã çµÙ—ÿsfBó&^‡Ã¡²ÞÊl°'½?Ó›ñ\c# o˜¸e¼è:}×€swVËòñΓ@¡,¿0 ¬Þx‚ë^ÁàÛT=»è×¹é˜zTÝ[ÄνÆñü0Æo@Á_ PõºÎ#ÖaŸ+FOÈ(¹éܪ¤;Ì+Ó$é’‹È¿N ¤'&ö-¬x¾a-§æFÉëg©';6FœÚ7Þd·4€0>%´õf¥Nd9œ#” "ÏÒœ¥W.hÞókè¤àë"š`¡%Cããy¾Ïî—¬ÞZLîÝ`e D6>qþoiŽ÷Í12Ž3Ê|…ÆI+=Ñš2Ù¿}}}æµ×^3O<ñ„yóÍ7 ®#E D t^3Î8£9rdÐßtÓåo(ÐùÇ¢¢Šj@\-?Ôø¦˜×;Ü©Pùøðãøvn’S¼Þ4ÉÉ8À2\»Œw£™ Æt£®¼:`pâ%´¼/Óep8OFH¾\N©ùÛwµ,yÍa´‡•äqc vi Ë„n¥çêyŸò©©-ðMàˆ&Ž%ùðŒáðØNèŸmy9òã¼y;]Š¥ÒðÎúS áœÃ2à\Óhý*°6 æÇ5–a3X+óÙû^hK‘\h6c b!3à‰ò¬{ Díi¹·\¼EÛYÞ¼4µ_ÖÅrmǬ_¬ƒÒ&îÈ0æwcл3_fàþ¹–Ôƒ/ì 7fåˉwží>Ü{‚¼EñŒÁÐõ‰«‡÷²ôNq\f´#ì12 ½šÖWÏ™gÓ3‚ß"L ˆ6º=—"öùÓ"ó–çâ@ §%¼Áô”+çf–› "¬ƒŽÅ«4;ØÅÒ²GŽœM­_ý= ‚ç[ËF2=;ØÃcMg欭’…G;<>gðÙõÚíÿ1Oü˜¼‡a`ó«ÇÓ3æó¸=ÚCÓžÂY×(Ã7èûÌuÔQí6+–è7 Œ1ÂÌ<3^UùãÌ|`eÜf›mZÒ}óV sLˆˆˆˆpjÞ^Yÿ'kÇ2æ¥Ëì?S“:±¬[{øi ¼F; iG'xÛsMúø M, ÀBB€ˆò#›Ì°7(>ðñvÆÇó¶ÆáÌß,:`i5¼ì°$qo«Šá• €ÒF—l^ÏëQ˯Kzh+5°ÒXw«UÆ`S±7i„èÀâ‡ð 6^„yƒä þ5¾ø ³ègŸyXfÑ#Œ=¼üíùX @\Ö èC^½ˆí]4Ì‹—\bÞ»Žô´„‘ñ}wSø„iÀfšWyxð\¡yÏ<¼Í`x@è`ù’t o„pÀuž!Æž7HÆâeІ•N6Ú!`ž°•«2 1S±|„Øò:Ì-M×ÿCòнŒî7w~WÊ÷•`ÑÂÔ5bíþ4`35P¼ÝŸÇ' xFm ³¬ZÞo9ÏKžvÎÇQ˜ƒµûl0uq©0x\7Œ‚vˆðÅ¥ƒ%øÿ ÕT ·ÊéÝÓ\Qû;Þ3FêVþ$€óÝÇ< N #Ô(ä` vTÀб4Ù*a FÂEÓ·)ýn±ë\ed:€ò§“çúU™l=“tÅñ±­ C01k¾­¨ƒµZý†®³Î:k®¹¦™cŽ9̃>h~÷»ß™›o¾¹PMÏ=÷œèùØc*§™Ñ®c=6}çž{n³í¶Ûš=öØÃ¬¸âŠí 'œ`üq]¬­ëªÚÎ`Ýòuž–ÎF•Ÿg‹ö¢t&6ɾٳ”ùǧ À)–$c§ôvˆ½ï\ /d3è‹å¹’\ ¯/„„,sx½†l€#ËAþ B2Ïåóˆ´N‡Ø‡<ÆX—܆#6cïVÁòÊ1èËa-x ¤ˆ}! «}¿6*O|’PÓE{ä¬rsOiô@OÙC—A_¤cy·¦¿HÐÀ™ ô½UÅlÔ2ô5Œ]ôÏ’sl^}Ù£Ï@mvEZ—mC¶ TÁÛ  /žs¦é©o³ˆÈq{%èË÷ ¼Ëž¾+¾fQ:¹“|VÙãS·acò F”TÛû¡¢Äíàr6ïÈ-In32+©RÐ!5ôÓü0`ðbY?â_ËñŽÍô@¸oË€ø»˜;yÆ‘Ïo^”k ¤Ã]ÂFaºo‰MÞPàuè „èËÏÒ$Á³ïཅw÷¼mÎËÿ©xVö×Ço Y{±s^Ð,y /xáYì#,ÁŸÓÂ3Ó¦ï4³13ò@_ÔÇ÷ÜW/žC¹ÀÇËyðà— ï•–p˜ .©}ÊE’£ ôÅÃA· F-Js\YÐráAÍžò¾z úbüÊg8O®ÎÇ|حкn}í"4wëi¦™&V®î°Ãæ—¿ü¥9ì°ÃÏß3Ï<Ó¼û.¦«îÒþóóðÛEY$i @é•VZÉìµ×^f÷ÝwOsÁt·Q±¶¨ÉL}ß?ÏöLžfGŠ(«üVVr,7”4ðçÌç[¶gò¼Ý—0Ëß©+ŽÉWµ|ù1ÿ±ø12¢ªöÈ-“ABNÇç5C…ú£˜yBâJú–n…XêÐ>êÐûÑ®çLð4z†6*XÄ`çñ1oSðé_ýcF÷LÙØüÞrE ŸßiŒé¼8¡\Z8Ö+çé#ÂA`á[äï+ô+óø\/{ÎwP÷I’ C¾ŒëÊ@ÊIø?Ç¥äüs7 90ñXC¼Qx‡êšáñX#ÓFSPŽÛnã{IÜ“ïÒøšy•¼¼·H^”Ý$Å Æúä÷Vœm§aã³ËÓ¥ã( âeøJë°j’œüãp Õ =Œ%Pè0ÂùJø”tÀï+»’W%@ìÿª; ï\„~$Ÿsôí†Ô(i(Y\lóXžGÿlŒˆ,'–ÅKª/î–)ùç:Ök~‰VŽï±VãÛíÞžIâlÜ'¹i¢Ádˆ à“—Ø4(ööÄ•tÕ˜»Æ&„X"ij]¤ÇÇ–<8Ÿ Ú.óà»ù’È»‰ÆG³—Æü‘€I¹ÙZÓÄAà &ßžT lÛ ë’´© c°‰‰Ÿ'Hñ,p%Í%ÿ¤¹!M$I¯rŽKŒ|èeq…N&në|Ú, À±8ç$ «%èšk€ö_Óçù7|18t.µãy2à¸èvÒ“‚Ç” ñi ð‹H‚ú8Ï>eõÇr†~Ø«˜åã *”°ùH‚Έ[/«©¹ q5a,Ô#a ²¹èäôY˜˜2 f€˜ >ËrSx–/Ç0¿³´÷%i6¦±—?V±°=dkiˆÇŒwØãêÙA^kO¾bàaŠÐ'óôÔG&o„þ½ßEe›£¥štˆðo¦ÖÏ0{#ÔëáHØÃ~zÊ )Yî‰Ö÷TÊÌ oQöÌ…M>û,G¬0À;=ël‡Ôü_”Ç0õH€â}'Ò™ôƒg÷cq¢.£ÂÖ+ÔåáÑs£¬ ÷ói™x¼Þã!‹°ÉVÛοJƒö—¶Œ$ žê£~ƒþüÄñ s,mìb„vVÖïdéݶíV\¿ÈâÔ‚Xôͧ¥íj¬Daf¤óå—_N L˜0!B’.±Äfžyæ1wÞy§9昣Ð1}„#6†sÎ9I¬^,‡×.@¢óÏ?ß\}õÕI¾íß믿nfŸ}vƒ06€ à 0Wx(ßqÇILbÔ° !'üEìѧžzÊqÄæùçŸ7£GNÊ]|ñÅæî»ïNâÛÚ žÅà›4iRRß-·ÜbN=õÔäzƒ 60£F2ª~ÂF¨a3ÆoV[mµ°¾þúë°ØÆÓ¢’ì°â¢èíëÒÌÐN—?Ú‡vOïääUmeƒ_í÷€?(]’à›ÃàN•›<Èú¾£v ÇGS^û˜/MPÌX[xŠþÖÝf Ìòµ‡=ÖÞ%&€Ee h+S8 –Lø…/ß2n°Þٞ˸ŽXß)ºâUºh{Ø=)úç*ãÛížË¸j|™ô#7M¼ÃaÌqie¢h§*¹ òxŠZR,óä9b{JtyEÚ6¨;ƒ€ÂØcÙµ)€(”4Î’Vš¬3ô@*ƒ-?"ïÔIb¹:dF’+ þMAýAH@jˆ €MúKê¹r¤Wùy"L ¯p¼pžöLÆÓ󆿦7cC>·™yysªC ì8 <Ÿ0àÔSýÿï" “é÷iœÐ'Óñ†tü¶ñÄu2€xxwfGB]2z( õTc¤!‚ÓlGÌبîAj Ï%ð´–:ær0¾F h6¢âž1½#šæI7/ƒLƒ“‡ˆâçŠÁç$‘þCžê‹‘ŽÏ&ðƒÚS&Œ=xãÄ—„0,6º‘ÆÞÉ¨Ëæð†‡i“¦kxzGÀXñÄ=À³z}Ùôx µÏ%‘¬™þÖø ÆuºÙ¢+”7=rp [Ðé¶ØÇS4q—±+)lù‡·úòo±¬Ð÷E8ä`Lò<‡÷å„,{%W[¾»å{ #Ì·!\hÃÞíÍʪç#+S#‘Æä¤ß6®ô‰áu^ó˜â+ÍË‚gE[_€üŠû®/ÀïfýÀ’5 ­ÈÎ.¦ª“f˜i&óMnQA÷ßràºóÎ;€¯ˆ 8Éfdæo¾ùÌÑGmvÚi'óÓŸþ4IÆæv®¶ƒmÙ{ï½`w¶Ùf3ðf9r¤9ôÐCÍ[la<ðÀ¤¯<òˆg¯¦‡z(IB ôà3ÀégŸ}V³Æë¨©blP¸?BÁØÚÓŠk e6ü£½x­±D·5PôR¶OÇý“yñ¼UXb&½~[9ªOÑ`o;`JÕ­“KÕ«’mûP×Kº«ª«j9Òã°jÙ>yðd„·œ6'ÒGì5¨?¸ß£B®÷/©—rû+,Ç:žMŠÎ@žl«›‰Ö“зE½8½çšJk_n}ü–J¥û¿3Íw oüÝèÜsÏæ˜cŽèÕW_yäÇ¢å€Òä¼óÎsvyßÿý-ŒYmHŒ_Zz …±n¤‰¬¸âŠeg€ÙÅ_Ü1~í2u€j5£˜û4ÎÒ9R,ôtt8Ö.Àô2Ë,SN‡¸#FŒpI¼üòË5IÍÎúÁfƒ¥hÛØ^ñX€pñ¨ªCAÑŠK|¶¨W_|½NÛlKY¨´{ ¿ÆÍ:2ȶ °j¬œ“,­/qT`“yØÓš@’J`gc:ÀÓORê0qÉm*}¬Ã´ ‡ÐºP–í/¥®Ñ/Ú6°e€xT€¹‡ågï/ß—¥L€^ +®,À«  —Ê`*Ú¶níÔNª”€tÛ' úI[xQØÌÔ)t€ÎXÚ¯2TʓƉ„}2‰¡qÛyÄn®Úé¾IêЮ¦V¦´æ§Ï hËuf1õ¿¢%³§^,ü²¶èsµÄÈÅlÂRfB@cä±Q-›WÃpDçi›ýÒ†÷ÁwJ õÛ\?§c²‰U–J¿bíg¥|yÂ×÷ÜÛR‡iÏ ÆÛú»KbJ„ô|ÐÿN޳Į¡|¬`ñÓeµåjÇÄoˆánÍÌøy·ûÚNUZxûr ÙÃÎJýéóÐíyIŸ6ß…ÞdV(ÎÆ¦ ù÷/·…ÄìEL2í­rõ9ð ¨Ûuá…v÷ôÙgŸ:;µ°eaÂdœ%@IDATÔbÚ`ܸquï€û¸û3‘eâÄÔdD¨ôÄO:€°,5èU€U „òé§•&ä‡0`@¦ýRâ6Œƒ]ÖlEó ±7Üp#gâ°ü¬³ÎrÔÁš~ì±ÇlÅy¡~­Ÿõ œÕFûõÍ…+4ðÔÀ9™_Á[¯º¥jžY•W¿½ðÙq½QP–cM¥òe»ør —v¬·T“AèÔ’=ÊܹÆJ36$0!³–Ɇâ„Ü|Û¸´°_VX9­0m4ýy$ó<À|¨Œg$좕³Vlÿ†–`jÊÖD€ºY–¢mcv¹¹ª²VÞ¼tjžÓÚ®Ú{¦Ý[ª[ïü=cBa&yFVNõ–ŽëDš| ¦m\cÓYGêPgv †ž¾iÊbãë9›?-›°ƒÕÍѺPüdYNoÍ0sû–8p.ÏÑ‚‘B*©z½I¤€øs±ña§È½9û¤r®¶joNÚât xya7œÊŠ`ïs*€ívzýÛ:yŽg €ÕÝ;+dìwŠéƒmhúcl—»ÚÙõkëUu>²MÌ]ýµLàWj^U6î‚/ôïVx6¯%“"g àO£D‘ÝlÑNظ˳bÒ)¸oÈÛÇö)k dâèÆ&úDaqï)õ±Qæ3“{Y² »|=ИÜA¸ÇyªJE›‰­oü/Ø!Víô:¯t ŸudÂn|V€€ŸÖí€W¿qº¾”ݵ³ ?Ë 8«)ªvæÝŽ´¨¥?õúëFÓm ±l07Ì'°až}ë[ßr±|ðAØî°ÃÑb‹-昹õìÔª]`6A[E6‹Ð0úàƒJã Mí³ Ü”)ÕóA˜K>|¸”Ù\ @ê•W^)'ņo€«°•1YÑ.!/DÍKhºº‰Þùà‹]a˜ÌØ:Þ}÷Ý£Í7ßÜSûÉ~œâºÐ@Ô€²~1ŸòÞ{•emý±¬E™Ú§Ð»ö¥^¤Ô. è’Év¥—'eæ ë‡ڠͱF~~\‡6¦ …ë+7X5Í »þf @”/0Ü|`ѣ׫Êle3Sw{ôÁ ë×Ûçþ °·ók6ýÃͲØP>˜C]^Ë¹Ú†Ì ¤‡6T(9ϰ °#M”)©þÕÅ®|h~®ÞÑ2; pÔˆ`—5Ô’šýúG׺aTZ9lûÂî)4 ŒbÏ4T&eÒÒÅÝÚNó¨iG_8—ô¿¾fìI"–  ±gÊ |„”eöî µ‡„ûc€5ÏsÈî­bѺçÙa”x–BóQ’2,a•9 Hô‘<›‹ìÓ¶¥aôHæ^¦˜;P³KÊÙ´ ±à¾í/t}0f¦~9“zV_ŒméFû¸¤åi?±G:¨ùWXùƒÁ £øa¬Öc”ÛcV¬Â€šZ|ÄNmy¤>ðâèAÓzâ‰'¢k®¹ÆÙüEœ:ÔùùñØ8î¿ÿý¯+;@9 1Ìi@p5£áÇ)® ôG Lž,Ëä>ÿÜMÞXÛÙý±¬E™¦} dr…ïÏ_Õ^Wã—ö¦^¤¦h”A¡ñì1´1õŸ–ÎÙõ7K&dy&~jÏ/ô>=”18".;a«ô“XóS€G¯[=nfÆ­¦E|ÕÇè&À}ßÖeÞòÀ¢ËŸr¡ô¬8ÖÏhWoL‡œÍFHÎ×5|À]f8|fg=ÐFmÉjV0CrEãKÇʃß1fÃ( {£€i¦'ÊÀ—N9:ö`Š_–³¶çVT†V²b4æg'q€þ™Cw<'¿Ý,Ö]ËéTÊî­¢H'˜µ.°A˜/3úr €øeÀ]Ù¾˜ÀN«°w½]N6vD‘ßÈñçj© •¿4à”퉣¦W8grBmÉb¢¡ÞÔ¶¥óôqLf*töKtU´ž]èõ?< ÅÖ qЙT@´ ?b6bHÏS'>ï> jc¸[éÚˆÚ/öÛ0@Üw’ü±—|l ýQ¶´çÂÄWÞ VW—ü0ð£éAnHéGH÷¿§êä×ȶ,²yäòS=ö„™+ú,­¯—e“e9‘ýe•=S ìV?{¤Âô²º1¯°Cb²6·í“®ˆyË.<ïÓd!ŒÝäÝ­Úq¤{†€¾Mn.¡ä0ÐùsO\ ×3}>ušI~l(·v@wØŽÀÿ›ÔÝÏåœú=TžS£‚7&¡(W>yC‚i+ ÇX8Ð×…CNƒ®_,USèÁ5.…CYG–O[=a¢~¯¿»»ün¸á†ˆ ÊÖZk­è°Ã‹-U6Ýt3nbºá˜cŽqà'¦ `ÈÂ^È\pÁ£xÀ¥Çj³O=õTtôÑGGúÓŸ¢å–[.ÚtÓM0|É%—hò5G@g˜¿0waþb"‚á“Gí6™#Ò¢‹.awÓÇwœ+ös1CËQYì_¿ì.bào×]wuzùÇ?þýêW¿Š`ôrÏp@»Xóbc¸VXÁ•}Ô¨QøôÞvÛmËå dS8è—À„ŠÚºî—, õ•Ñ@ö'Wø6+Ü£°o¸ÂV)¤w5¶ôn®_ýÔ[±[ëÇUöL£¼b™­yâ¦ê†GyÂç ó¹.cП'?ŒêÃwïÍk@•< . ¶E}9Ú€YyÛÿg˜Q PÚ4o3}䟘Îö4ŸzÌPÒU[²6Ðù2_^Ë£×YÇï è±fr¯¡ûÌŠ‹ŸÚ ´@¸Æ©·)’†ƒÅçðê×èQ>âù å- ù¦ Bé·»Ý`ó ÑÂ.ômn/ì …f"À™/¤1PX¡€¿0–Ùð áÅ·ñôÙàf˜@QÒ[[H]À̰6Ì„=ynÆ29qXrމ†KÀú¥âVe~¢4Ü¥»«giáLfÊ"”É ŒT >ãw¿·¡¢†GClJw”´}­Ûoš>ñxa çiOÓ{v¿ÓØâ‹ž…e¨*ûWË‚Ù{SÀ<ží()chr@Óàø†Y)CÝE Fg}É%@¼NNØô²Î÷¶ç–¬ ¿g»s&(z¤O^Ñè'¦ÔU‚rö¦ü0@? ZäôÄ.4¦sì&…Î3ïß”}‚!Ù*)WleèL×c'» ßNn(ˆ;¶ãio´/êÓRúR7˜h¢UmfYZYÀuùÄâï:NáÉÄ:‘óîüUfx&ruRGþ0é aÛµñš¦­Ç´wðzLdô7)è%êÓr9h¬¶ÁDæßø†ã­F”ñ:vìØ¿‹.º¨Æ­UVlëÎ5×\.)Ì*°É VVlíbÎa¦™fr¦K;¸Ã† +»›¹°w³dâĉ..éÎ?ÿüÎdB(<á`¸ÄÜøÜD¨ì¡¼`;t€Í%÷=ç2·&ùf Kã_ýuw¿Y÷‘•FáWh`ZÔÀöÛo_Sì¬~«&páPh Ð@¡:€í=5€ÿ:ÅÊíÝŸÊpR)d|k|¬ú¶(sßt‹lüä2õ5À UA(-ÍV2PýDžÏÍDR?=²©—oßUýücZ;ÁF¥¿Ë¼ß¿†ñ‡ÝÌ0Šø2äÑÏÜö–ߟÀÇ/,EF'jbÀ÷çú'®µ¬¿P@†<ý`¤`šê™•'ÍíäÒLÑ_¤ã¹ ô똃¬^@ØDÓ·§¸­€® $ /“cs÷äáhµ‰#ÜÄñy2‘°³1’–Ø’’6ˆÕÌ å|%ðLa #z?ñ•þ3õø¤:ÔÙXqeÉG'jôض‹Ë3'ÏȾÃ3‹6à7R1qúFf°¶z¾]fEשNrЕQ4ikq«L]UhÿírUég³&.í» T‚¬><¾¯ÜzÕÔC_ÝD‘O¡B… (40ík`ù€Þ;±'çß Ë.ú»0àhVB /˜õ–BfågÙbYáòøhÀÜÈüyËû±$txwö9Eû_7™@_ž‰?`Ì} ¯OµîÀ·ZB /!`›YзEÅ"@ u¬Ð÷c—}9·ô+­Hèh%=Û èËæ]Yr‰€¾ú\4œ‚¾~Ϫ&ô¼Q©7WЗðj¿Ó‚¾¸S/(‹_ü¬Ðïï°ÍkÃè9À—}q· /×–‰ikc–ú¦ /a}f¶‚¾øú !ØÙ$Kéaí"ʦû‘€îËýáÆJ@U@ß™åü4aЪýsÀ-+‘ØóEУ…Ýfo%€‘ZùÙäªèK{ºS ;„7v~)߉Â*U ¾øñ¼——ð˜Ñ€í 4žôÅ”BHpoîïRË(ué”Vu@¾`Û,më˜ß…QЗ ÁÐ?§±¡­>wmO€ƒos'%:­kAß*“.Ùl/ôÅ[Ù´U /Sö’?û”qLê¼úÞ#Ï"¯üÍ«oiñ¶—örœ}aõ’À¼ÃÐÃZ%¦Jªe ©ÔV@tu_à<™@  »•²™‘‰+I?ÑãêU=ÐW¿=”gª /i¾0?„û²R¾0è‹ïxù=é6¤å;,$´˜7xV¡4Úåæ÷1š.õünÑžÿWÿàqÊ!â܇ /…ðA_Ü&m)õÞ8¬•õSž™íkcÅí2 ô%Ž}øi°™èã¦ïõýÓ®Cu¬™tÒÒǽòÏ Uø(4Ph Ð@¡B…zYË–Eˆ5FF_’¸‘Žgåû´~ ª]ò¬ P•,°˜Ì<àY£yÖ Ò €Ætòƒ‘&ØÌk¤¼g˜åäÛd0ÓòóÝó²ÄÎʱ,¿I,IË‚ YñBíAÃÃGê¢îFÊr½€"a ~*Ô1µuúS·h½:ŽA`•ÃгÂré›´‡€d›öÔ8?)ÀÆ´å˜]î_Á.ëιÏ^…!†° ±`®shâæY£ „mÞ² ¾µ|jO€XCýR —Ú•ó1’n³•ÕgÀB@´ÏKŸ?¨k| ÷ÒDÛ°sŸ”_÷±¤±ŸÙŒë'†Ñx™L†,¬åû¥_U0ž´œ”¶8Xî°ç²øºü¿ œ--“Á†4±ËKšœ´§ß%›sñÓMKS&MÊY%'‹ÖÖª5qq†ôÅõú×»$OVºøH¥ÏîXÎáÌ^(èê‡/_÷ÜQ>Å܃ê¬ìØ36Úý†KëˆÄî8fп²|¼ï’>c[Ñ;08ÙøôÎDOÎÓüé’O' Àÿ¥m%ÄÌ&Tßœ6b‹öǦ¾e•îÛ%àƒR¿¼fÊv¬lºÇ +°LUŽ/Åúè”~ ó=iâƒøºäX1ßaeDRkê¨LžðEPê‚q­¼èàäZwëò¦”ëa ÷QM;(9Fz£ì{›¶ž÷›€xc<Àþs£?Ú”‰YßcqÛòKÑÚ5úfÅÊÔ–GSžmV¿Ør™œ$xS2ûÞ´<–I& ­ÿ·3úp.ïùÔ*yKZ„+4Ph Ð@¡B…¾r¸Rðç7²$÷'²$RSöfÙAzÇœ/Ïy=6Tž4Z Ó XÜJ~yâ¦é„eâ:Øóy~ºï–”ï׎ëÐæU€yYr•|”+˜3 &’‚ ~¼ZþRéF(lÌSOØ8(K,˜.Ô4¼]¶¯n½qLcÏh^¡Áe=PzSiëõµ¨9ÔwKžõ5eŽ^% Ì=A`èa“UEë^ç=bsVÍ“äC¸·M{”6­ ½A½õ Sײk~ÔÝÛ?$€ŠoûæoR©.Éøäþ`}²ÌÞ  /ì:X²åú1È.5TàøåzÇxɾöK6@s\±$…,uN—ÕD{&à`(:xÏfv:)7VÒö™°š ÒÄ?M°¼ˆ¬.°¢e ±™yÖÊ^:kÜÆrÝ ý¾Àr´y5ºÙ©_JÒ;°Ätoí]XÛÖ´mìjû­e™ž(õŒÉXŒgI}V§¬˜K@BÐ,›-†DŸÁ†Ý8Vµ†Qðݲ¨y6iÂäM=yCî‹g;‹i.ÎÀ êEmØ?í›€Õ ˜íRÔý­÷­cíšSs³X©šÎ9¾c4lÞ#ï¡fV¬äMßß 3-^è›$-lÛܧ$ýJOÍ$sžôϪ™~Í«±0ðÛ˜¾ŠÐ… (4Р|–UƒÑ‹àSI¡Â{£( h-ø3U>ÖÚ|cY ÞveeAÉz¬ª¬uf˜dY­È}O¤ jº´ÝLÿõÃŒÛCÀDL ¨X )(öÉj5÷¡ï€íò;OLC<Ó±t„9-c¨ß<¢›ÄØÃôé°²–,|ÄŽn½>–çBÂDDZbެ´&VxÚÖta²%ß‘ßs=“ÝFwYa×3\ZǘäѶ2\â«|,©|³´~t©Ô¡=¥¾1¡IYßK„LN¬h6góÍÑhºe“âê³B€²Æmé8yG0»ÖPK)×D¦?°f»u-»¾&B›N•¾Õ˜Üém¡Í#¬,°ž°!úöœþ®™Mó–Ò~ûä£áòiXCÇB… (4Рzó˜·(ʀȾEíXvÿuÕcƒ·],Xe¡cXUÊÉÀ˜°Ù\+M”,X–®ž» ³Â±‹|=è°Ëø Ÿ÷þ?ϸÏP¾>ø2³ ô€€¡A¦QB¯[=ÎÐjø·5PoJTlÝS·Fç% Ãß{˦u¹µ¦§P/uãJÃnT^ÃÕ;ò¼BÜ õý\LœŸ 2ƒbÓÈsÃBñÎU–,‘sÀJC—bŸW6 KÞ/À+/kð} KíV³ÜØ&‚-Ù‚ê'àÇhïµ@ˆM `㉹m2ÄQíîÎ"úOë ‘ú³]…Íå²äïR÷¬mR K›;-lˆY •vUÕOûH¼™|IÚ=öRµ}ùo€Ï£€D´¿äFʦ&ä|¤s0o1òã®ê);ÑA&JHê,Ìq}çmÐs«ÜÛ»P~±MgK“ÈÙnu§™ivn‰„Ýz}ì7›hóô9ZάÂa"ƒgŠ VþÜ%-Ô¶Y6ØØUS÷J›ÀEã„c’‡¶‚¼"ñ+ýÀ¸è‘»D#X[Ám5Ù£&'°‹¬¢“\_/ùØv¦ñ4,ýi}V7{¬·džmä›Ý­Èyžk—–‘$– &ã·Ï` Ž€ÔÚÎvL/ÐF¬ì/}®"fØì²W¦^òÅü¨ÜRkÃë„M­O o8§ÚT˜™„rþªÃ¶ÆÆJ=M¿V¤ÆTR„.4Ph Ð@¡¯š”ñU»¯â~¦= ÔcÁúß8;>Îõ®]®ñ8Þb@¿´ÍµløÐyÚF|„e“7+èúVŸÛ¥ŠÕ>•+–îÚeüê³Uæ/5½¿TS#ç kor{–íÞ %C%Îë{(^›S3Ø’Ufû®¢ûó®! M«—w#þ€<@‹Ô¥~ôÀ¬íÏr‘´•8´ 6 Ô;dc+6¢S9Gêöh1ÈJÜ\îÎgòB”guƒyVÀϺô]N#l*Ï%uòq7NñÚ)°Ùº'²~è¤ „C9þ.àç$>À<€-ŒVô>ÐBÞ+HzÊØ–˲Àò\´Й;Ó¶÷ËýÂÄû¶Ä±lür$9TÉb¯®,÷×ÊĬÏÅdƒÖ[ò¦”,ïGï rÉi¦`kyBOwô}£GÀöSü¦/¢®2 ƒƒ]Ä}Syæ«x õx¹ŸÓ;fs`(v˜Õ$‡xå“ç‰Áê]ó…m1Ô¼æp{¬S„)=T˜æ?–û&u¹/ÚP'’tïrØöÅÌûÀú~[€b[„É&v©3èškk#˜0“|gË3×IÁصö¦zh’˜Àµä¡ªÛK?øš”7ïê!Òè/r“ôÕÀÖ–ëlÑ7ýP–`Ö-´ÂMQÕ>~Vü|~3Ë÷Ç'M¿[óåÑîPLøS¯åñ¼ô›˜Mj› yPŠ4"=¹Ž}£ÎîÛdeÅSŽyïœ&¿þM×… (4Ph 750-€¾°ëúZ\Nm©æœMíÒôÏü-è²O85K}Êæ-¡2éò{õËÃÚѰ½uÜÆÁUÙ©Ã<Ú: ²c˜;ì«ö(þµ®ëÈ`y´ìtn7ØÒPyA_Â×}-ËJÓðµ /îØ×TЗëÅ(@X*ô%ìj)l:üòÊ>¢+€¾ºÙºç9÷A_â(è[‘ °Ô.É ú’ß2@†­ÝˈnSò`~Q·a·ö– “vP70ÐßA_ʺ½€¾ Ðzš°pU.* /›ŠéL»' /a}Ðw€Y ú⦠/&а© ëÐÖmn:ðB‚9¡À:öA_<´æ‘ßêÉmâóè à烾Ôùe¤=…@_˜Ò°èûwµ–À|¥ |C®+ýÜüîÌ9¥þQú›¤¾”³èK˜=Íd×*í}IñcyŽ­ÃŽ¼§êõ ZþÆßò¢hMôœ¸l+èK¾Y /þÝ¿—–÷Tê7TëO€L )4Ph Ð@¡B…¦ªØõµ0píKýäKÚRJ?\½k»|¯^ØFü±Ú{ÐI¾’¨MDB7b×5_ê­…‚5õ´0£òˆÏÄ¬ÇøÉ“&9ÜS±vYò\OB€Ž‡e³—%ü$µcè‡És +IÅg&U–^WÖ¸3T½AË{wO›éYÆ­ÊÀ¼” ŽíKõÒµlIÂ2©dÁR ˜ë„Ó2ÄiUN½øKzH– çM;Þ+|Áž"€`Ä|rDèãìRiçØ ˜¸ÀÆ#}8À$ÀëÉ}_/u†º]WÛ>ÚY”¬Í!¦}ñADߟktت=Ï=3ƒ$f1ö3ŒG}÷%`à©vé$O¨lj7?XøVNà &$‚ ;¼~‘gMÞ'G ó4Mv‘ò¿%žÿPúIi'ècÑ$¿7¨„Ý[‘AÒÖær`nvÓ7úP+c2DMm[ªMQ¬)€ê¦É¤—‹ ‡yfEÔ=tœ9ÑGÈϺýVž ¤•_÷¼± |NIã&©ÓoK­&„ö6ì¼æ6ÑÕ$q„YN©û S —FJ;9F&Ðî”MûF z˜Y_Xþ³ŠnŸ Ø‘Ws$tJIx¨¥]šîo²¿œ¼â¥g¹ÌB ºIƒ×Ù”°ì6)³Ê/E_Øæ½Ò3g€må ¨¿&÷¾¤ºá“W‹^Ù´We‡ŽéÜéOe’D…I$&vvHê uÒѬFñÂ÷ ‹ž‰À šˆi×H¿‰½þÓ¤½©À*ÿeR¯ü ;5Ì_å醸¯Ôï]¤¬¤|õdñv²7ëeÖGþ<%“UÌ¥°R¬â³¤¥=ÞLPøaxO}ì;¶íú¿mKiÑ#N-¡/ ÉÔ+Q¨4…[¡B… (4Ph E°Ÿ|¹58,õCÕ¿nÕNXZì>°ÒˆÀPJc54h\üòNæÈ#y7{JKË<–6;²§Å ¹‡‰P¸,7@›Êð1ЬÝÀ°ÈRï¾Ë`Óü.r°„^ÅËõŠAx,ñÂj…¬qgPþ˜´Ø€åiÆy$nŠÐ 'ƒcÝ`‰41¡ ¢[Øç!†p’&õö£ÌñK“Õ%M6 xÈo«2hÇúµBéønyõæÇƒUˤ`„Ú‚¥».œ÷ÓÈsýHæÉéDÙÓ=S¢s†¯¦±ÇîÕ °h­ØàØfI#¦B>ve5§/œ":ôÍ™„C¦»žU榇QÛŸÁ¾ÕIõ稬Eµ›{{çNÂîí‘Ú*LÞdÂ@¹ÓÔÝ…¤aÃÖŸ˜Ðt±- ÛaÃ2;yù²Ô¡#œ=\6:Mü^ÛÀÊÒ…eIo¡eÍ#í~¨Ü/ö}Ñ1›¾iyywéÆˆ06Tc2D{w!s%Œ`½æødÒ˺å=Ïû1µÉ½§ÔoB„Ba»¹ô,{V&¯´Ÿ ´Tvê¦fßÞ9"Ú¬´dôÃîˆê‰6”wö—ïkŇ:0eÁÔF¬m®æ3!ÂCíIi U#¥Íkœj&0¹Æ36‘¬íŸw'Ìdú¾cÍD× ÝEÖþìÞ¨æþa5óõ¥ûet‹é‡9ùà´^Wc2ãñR¯‰Îæ”MøTB½@0¬w+ô“›K¿‰½þíeµìu&Z–4áØ°óQÀ¶iØsµŒ›òž‚¥^O¸¯©!´÷Є˜–å¾ä¾ýMÚÔ?íø¤™ ßÕ¬ó'Òâ·ÓýmWÓÚ™bß§u»èoO™ÈS†yß— œc¨…C®… (4Ph`×6ȾNÒß>:¾Nºoå^_‘ÁM«!k œÆ°ô—õ†Ê¦¬¿ŸïöÏœyÀd€8_ò./õãM­ëý„ùòÏ„ÅRïÐ[~ÙX¢ÚŠü¬Ž]>ÒÖAt(Ÿ(³8$ äßË`ÛhÍ—¥Îiâ÷=–­­ÜWØçš–ŸKæ)+û1KìnÀQý8Ài~~Ø<×0›•¨P3hW ÐÃNnT”ºÎæh%êbCÓ£þe ÌáY½=ý𬠽Gýpz=RÂ[ [ÝCǼ@wÔùk‰NKšÊ2äñ>/²2 ¯„¶Õ°Èä_oJüö¦v‹´ (4Ph _i dg±ÕòqVHßh`¶¾É¦ÏrÑe—½•aeˆÔ[9Äéf NB›}Õ+V·M–AÛ8l VOP¶SÝLÍæg°XÔÂ2]_` Z6'þjûw«”ÝÁÙ0 ¦¡•´v{© öð„Ú&ØÞ 6iƒ2ðÒ¢Š}ëMÓÈé˜Æ”¶ÑíÒaë®çìTë2 s+b8WBþ7íÞ%|e¹æP}´Ã\êa5_3r;Þ™<. -l@œmªÊµní>· Õrëjs£¬*õ&R눰B³pWk!úÄý0ÉŽLu7Lžá¤`Ðu´mýÙ®#œ¥ šæ¥iêõÞf’ ·‘”©0Š©_@bº œÆaæ¢Î9¢ë¥mŽŠáèg3ßeôãdI¿–Q0k±;ŒŽÕä`/KñùY¦>yQê“Yk Ü=—œk›Å?MˆcAµ²‰‹ž»ÄG§*±Yr¿øäÅþu$à+SRû³„γ! º”»@ž•êú¹K”i-éÌ ù]àõ[ŽÞ[&_S+ŠIXçk H£\æ…sºMþ¨»˜KøaÂúfB†?bÔ!ËLÕøé«IÆF›g xYo*taóÑ58lÔÉi®!èŸaÊ/¢CPž/õyIÌ<&åÑõv#&´Û˜MôƒœíéÑ9þ(óòTµOÂήB·ãªzõÈÕEêýCâÎ} CîCËH>— ¸Ý-Í®í0ùž`²'†£k“Öö®>Ü›µK;+xæù– }--yÒÇ´"Ú6§8¦w×o%¹ðä®ÍGëˆukǹš—‰&~»É5”F[ü %Ø+µôJâR )4Ph Ð@¡B…šÖ€.kk:"bn Ì-Ã_%ñ73i÷½é†QíH×+~zYƒl˜¶"7ƒóFÒ©g OZº¼ž°y7&É“n(ÌÿµÀbβùZ´Ê†z¾¼”<§o%Cî]Èz›ïՃظÓå¨6oŸÍ©mÂ×÷²iv ²Â¤Á–mýIv²‚lžö<DÐ AÏÿçW¸«}V6¯‰ù…¸Æò­´{׿¨àŽqr§¶.«¶º˜XÁpò^pÖ> lš@Ê8ú ±ÏØL:àÌT/<öx”M—mgFϹJaX㙆H—e;rB À”*óÀæAy†Ÿ$íi+Ñ1|Êsöiq‡uyƒ° ‘åseÝ¥û ×U €– >"ìÑ—åzó´u}YY~?RÚ6mþcùÍ^êˆØ\MÅ·]ýwa­²Y×™ÔY“ ,ÍŸ"àž €oú6Üo%@â-~ýZ@o«ÍP=säo¬€¡*ô YõÀ¹<éÐu¬F‹rmt¦¡Kß•³êxLêòíò,Þí<&ºLî{©÷K=²ÙØ ³W£cîBå|™l<}àßܳÀm¸˜‚°œ¹¾TÒäùèÛînÙ˜l-Ñ=w 膽x&Äx.iÌTzåßôho)"ÚoÄWáÿ=˜e*”º´êO6/á¿IYà?ôùB HŸD_´¦èk¼+KýB`Ç^$éñŒ¾/`6ur©£Ï‹Ýpä]÷_ÿzãjÖÀ‹]`ê<9“_˜ M‰z ú±ü£C4…έ\•Ün°¼}±« ÔïådCRÝçfíÒŸ"m|I)eõ3v‚„{:N&¬]z͇£‰u3GëÏ9}€®êðý²®ýö÷ݤŸdœ3½ëÛñ_Tîqó`-ÊÊ9ŸŸel׋ÑìJ©´É×+›ü–¬Wζú÷<ØÖäüÄ à××Hq]h Ð@¡B… ô+ (°ÅòÉY@¾£%¢äù!°Q÷–Éب—¯t©*ó§_éÛ,n®Ð@¡BNöC·PIsmf‘•Råƒ=+T­_ž%ùµ±*.ÏÈ@ñÏ2ÐXP°¦¦–0o—(ø‡ËfÅü5›NoÅKÛÅûQhdå}A<Ë ¯~oɉeÁêÆ,êŸ÷¨ÀKs! ŒV´*˜ì`À¯fs~žÕ\6ß·öZ¬ZŸŠË…Þ@e(VBÕ?›9 ’’3à·Â2ø4à8œ€¡pGvC­P'gB ‰FcV%æûÅWnÿ_Â`=6©ûLŒÖ>" Úu XI[—tc2ÜcÞbi½pÇ-„†iàG_¢ýöÑï§f zîsa4aç?!å°mÃÖ³ªÄ3.˜ô‚%è„çl še*h\ÂŽe#L}O‰7*ŒÓ„¹kûÀÁ3Ëp{$ ç<ÑV†§ ˜É ý¦D[eaRÄMfuìÜù>ïy5Ú4™Ø‚:·ý1u(Žê˜5ÚRÀ4¾1v«ö¬€ÐÕ"µ½û\UwôŒ0™>(m^Uçéýý8ÑÔ„ƒ­c׈}€€õ!Ù<é‚ycÐ_ô™ý§<#XÑkŠÉŒÅKœY Àöû“~媧Ê&kS\˜Y¥Ý² ˆ¸KK™/a‘‰Ž˜äÄæòºâŸ5ICô*I—MUUâ÷ÝdY‘08%e}Ï{O¿-:ø éè2ÉàO.Ï÷”îܽr½˜|Ã!è}ÏÇ»óÞøÓÉ&ÀO6K&z솞„C'þ;•É•Ýå®ðóË<Ñu=ñûÀMò±s”ü-? Cç•( Õ¸Tü8ã ø¡GR9‚Ø»6Á;Œ6Õ§sÊå·¥l·$Ð À*&Òê`›jÒï,#ms V°í™¶ÌÛ†óÏY9p­L")؇íàƒå™ð±u¾²€V§;;µ%±uZÉÓ¶Çß@;Vظ·ÙY›žùùû×07uÓ+€ºzS”þ¢–ßOï÷r?'IùÑ1£U[£y{ 0z¬y½!~vÛ¦–Œ–àþI—-íáóDÇw2yÏrQ˜(õ@Œ0;‹nÙ Me ©K{Éý¼íãžSüFZ­´ltOÇÑ7Ä$zÁ6îYò|¸wú鳤mç^·†IÃÎðuâ»–º [y©c:©G~ôûw˜¢D @IDATwÎ-ò— êºÑÐöa3Å£Ðî)R¦QR‡®•ô–;1K’&¼~'uw«@_óFÇÜÑAÝ¢»e2Ão#<ËoJ™”¼ŽÐ{ýÒàh¼€ò—KX6iû…¸ÿ^Üß‘çþ?©·ú®N¡ØîjÑÁ7$Í;’ö™VæŠû|ÑçÜv‰=çñ²‰Ú h·Ž™¢Ue"åôîÏ¢KD/ö0ó™cÓ@¢Ÿ×®½~Ol3¢2AêÃ7»ßr&[Ô#æZ”¹oÝóžÛwƬiy~G% o&ÖBÌä¼i ŸÜMGW ½äèœÇôàíH©“ÖDמÃ_'_ ~½±9¥ôâ6HûÏ;OwÄY’î«M§Y Lw„È&·Ê3ÜH&”*5©élrG¤Îï­.±‘ÓÚ\&€R¿V…}{»´#GŽŒ¶Ùf›hå•WV§âØ $iÖ·Ó ÷Q’ÝÝÝ?^À_@`Ü>8žáV·Þ>þö·¿Bào¨-Àoo?"ýBÙ`Ù™Ï@ÈŽQß7ïgý”ÚÖÈçíM²WS[Y>P-X ™ÁHËËÀÓ8õŽ!„8YO›îŠR^PÁŸ¥Ÿ, |˜§ò »Þb67VÒd9*̤z²«  Îkl Ї<¶};Ë $+©¸Tç x±« À›ôÊGÓR©™g i´zd¡õ¼¢a0¤>”ãj9ŸK«yçÏ`p½»0O|À?; Ö8!ÀöÅËKýxBÛ̠룕&Ÿ& Ð-=óȲo)xfã¹¾ô-OÈÀ«Ù÷ÂÌ7ª2™õòéôb€¾´ìÞ–÷|2ðnä~aæ#}Àè¾`NM „&r0C0²cÛ¼Ì/m6LóîZ<ÐgØøá;I¹°™mJlHW̉,!¡èó^tQD§“v·oÇ÷$¼Ò¹¥õãuÎÍ"ìQ$­O?ª´D4Šíãzîváòþ}(i/)i—«¿2…#.öu4ŸEô²GnjќÔ^ÚB[ÅlÆiÒמÐ1[´›´÷[„øÊöD7 {vŠôMƒdªaÖÒ°èꎗ£»&Dg‹®aÁîÔ1C4Zا¬®°íØÒûã¤ÒN÷àp¨Ô½ùäz¼üüwÏÑõþÇIƒüµcä®$ )ÏÚšK]ç0÷Ëèi‰ñ;Ñãï¤^²aàÁ’ÇáR®M%ÖõÞûL—ò°.&¥IZ‚®~BÓÇKZ»ÕyÙwÌNV¯ÀjOò×aFêª Ò]®´¹0àï6“ŒÓë}Œ¢£iU¨˜8I[Íæ}ïÉó9½GÞŠö–fzˆ»U¾÷>=†&Œ³t±ªèŸÍé¾*ÂóÿLÐô&;ó8¯×´ÂŠ1Íe't*>m8"¶‚'Žˆ`Ókû#ÕÂÔCtÛ_’xà¢K.¹$š0aBT*•Š_/é ³—Òí/Ϭ³³30`@4à 3D믿~©ÞE9 ˜4ÐÈ`7ïíäaäM«á¦%ЗûVÐ÷ ±-Ȳ_H³ ¯.#S©þ‡ÇàÍ—¼ /ñB /îi /~õ{„±2\0€ôÈ6LÞs@_©"ºö¥] /éXp¾-ã ƒ8w–÷">è›¶±`à•kdc&\¶þ÷ÉfFÈœRž©%lŽ¤Ì›çå©çäã{»¼Ï X%ú$àÂ`>$jÂÁ}ÇH„„˜a¶/.ƒ¾ž´i*èK-„³2.ٰɺå=Ç&&ÏÀ–%o\ ×@_Êrµ€uy„%þ«yO’‰0@·4¹5¡“úž$à#h®SQMƒ¨›1C°¦Øãeæ¬PâP?Ê»‹¶¼´ÄQóÄcò ÎôE _Xèl¬U–A'Êi úâ»VßqÝ7sé„С{WЗ@i}Æi=ÿ«}ßApaÀ–Üýº ù£õ`WÁÎî¬ú)ûý PóW¹ì¡bZ ô%´U6ÖkD°UúŒ´÷å¹Àtg³=ú¤y¤^'lÞÄ-zÄmFycÝÚs4“”Ð9CêÀzrýGWÿRæ€`˜¥þûVËuqbª…É™çÝS‹ÜF€¼Ó˜ÃÜ‚ŠNù-ÚuLÄêîs©3;Iƒ©p·Ø[~_ÊöˆÕ}aš3Ñ‚`Bât×g½Q}qç{¯QЗxÓ:èq'±ú_Ÿ|³¨[Ý£Lš6"Y o#é4V@_d¸°¯cËãq*Õo÷Ø­øŸ†5ø{ýõUÌiøv§JÑó}úM•¢µ-S¡§Ÿ~úhÝu×k[ºEB… È£Mä£^"yÂO0ï³kZ”é„áƒíڹ̀¬™ûØÅØÜóãÃüÚ'Xù~¡ëíG§Ö‡~^ɲ™–àÀX’6»]:Ê&Iè:M`ñ!ö£<-l–;æ6)|û€' Ó –²¹€¾0úk'Oª˜Ù\žÃm‘: ¾¨ûS–¿Ð®Ù:±`Ù%åæ–’/ØñkF([³q›É¯Ñ8°»£Ò–Ñ’Þ&i~:'%à†HbÚÄ8¨PiÅaÔbåüé_²A¦êùhÕÚ%0$}0 +m6wÊ+w ¸2¯ ÌDX5$k<åTÓ>A¸ƒ|DÍúdù@ €Ã0/÷°ë9éÉf--âÂÖýëØS‚Ì$lΞèh3I¢ktR´ˆ•razDËçò˜´eô\bWÓ]—b3‘Öâî£ò&ÿ¨´•óNûSÖæZ¨Ôx³¹vôé?^’r³6o™Ä¤¯Â®.Ðj…•.ªC–ò“ÃHqÓçqg$Än¬§:éØ<•–ñMð+ܶÀx˜äAO‡ÝelÒãªy62C¨Y¼¿0uxzªL²q[uÆ=íîÈäþO0^ž÷r¥¾»Ñψ¬í­$ YÜüÅÄ„Ê 2Ñ€I%R¿TÌ1ì~–x¢EJeí¤s]®q õ…ý>|àÉÑ&ÍØ'Š6HÊÀ3øÂ^´Êo’~îaÎÏnÒÃ_ý4,ybú„>‰çÅsD˜”äy³i"ìs+”}{)Û^ÒŽ–Llô:ÿŽ_8 é3ÇMW1À/+ý³‚ÍØ³†YÏ{ê°Dç3KY+½®K±­º h«‰ô†&z4]Lhd‹¾í£è‡ýäÍJëa3Ä,Á†tÜs¤‡ZAZ¼­Ó„|EžsC"“¦­Ë0IbI—Œö{~š|÷Æqia¹PÚºíëtwáDê¹ÞtÓM˰íïŠ+®ˆî¸ãŽèÍ7߬½aÿGyÄå5eо®N"W„Ï>û,z÷ÝwÝ2x"¼ñÆ._ŽýIÐs!…ZÕàoG‡°Âæž«Õ¤Šø… |5à³ }U°s¹/l¢߯¯¯Y𒹄Á3-‹ðp/!@¯™{„ù¥+?>ƒ9_.N1 ¤€ˆ>tmí §lºåË}e¨£âã*>ñYhÓ? ›$e ,>Ä~”g…ù±q$¶%‘}¼%â«béŒpð„õÀ˜öï÷¢äy0ˆÔ±"ér^—qƒ{É`še»V&› ù0æ•Á^ gOٸ,qΖ¹ö¥Ü)zœ§çʆ²ÔI¿J\˜uÈ"ÄPFsC‰¶øó c°^2Ƕ˜ åÛ°!À'MèŸh`<æfcÿ¶Jpð–§¶PV–WH¾¨ÏO˜ÚÖŸsì÷*c ƒÞjœ´ÁÇ¥=Ø•““•¯ˆß i'I¼Ib¥öºžçH& @\öè>O²qùBæ#Ǥ“S´Gb+Ü“³ø/.uXæ\@¸ž±6Xr.@vÏå÷Z§·“>ðÐNphÈÝhü…ô”Î=ÒÇÚ 8CàÏåÒGj_ÌwHf þ’<ƒ Ì„–æãõIëDËiÉ ît¹ý&xLÒW–ýOä^ç‘¿ ð9ß km´òùÑ¢B³+I¿¶×”kŬƇÔ¶¬hîö0÷°sYÂ.'fN0}ó7±q{Bºªïõ„!ŠI_è÷an+¬  jmE;S“öŠñÞ96Õä¾Ï¿›8b'WÚËäî˜$Mëa“§nšÊóbb-òNàýEÛ¨˜‘ lkÏ)ßeÙ1:ru4êþƒ{Ÿøß¢=I;ŠÛ\ô$­äÞ4üœR=·›®âö¡4?ýéÄHÈ?Í Ó!*Gv¤„.LÇöTÚU«˜šä5àåôjN:®q 9Ì"õs®@µaÙÈñï óý31óìegÙÅQ·Ú¢>Í_“H/§FœS|6v}C2`TîàÕ_^¹£eìé‰_K/½tÄo©¥–Šf™e–èµ×^s6CŸ{.íÅ”nš/¬ÄÙgodÞ6-¥l÷çŸ>ºñÆ£I“&¹€,‡'ßkÙ)õ®ïË/§W¨Þ͹Hý«¦À_L>Rh Ð@¡f5`Y¡4ع¼?Khiv»ÊÛ KÕæMü0,] #*MØÆ~bæôü%žié‡Üu0ò ¹…?ããl8 Zœ¾„jßçÐAeèržeòÿ¼_…–ÙÁÀ›ÊÎUOF]«ì® †ú°lÕ×b­kØ€<±K×Þ_e¨X À€\&:¯Wç4ÖÅfЉl†ž²§½©a1È_J€}:x/®sRZbÁ³:IµÅs°Ê­në%l'AêmŠhëP½´[õ¯7Ò:¢Q`¶ÕÕ‹/€OP]t®çˆÃøXn!æ,«WÁÍÄ2¹{ÀïP£`Þ<ÐVß¿ŸšÕ ®‘÷^œ+c4`92ùõm1®6Ë Ʀ©ZOˆ8vDÂì+?¯Òúž=Ö)&R’X”1ª×ñqY·íqFã±CÂÂDeò  ›Q¿ÚmUf¦‰R÷ôÑ„kAÜ¡¦/Ô0“%˜¨’¿‹4“&v*NAÜLSR¥•\r_ÈÜçÅ«|öéy¢œ ¦„\KÊO/Ë„¤ÚÂ… üÔ¤ï¹ðþïŒy“~ñÚXf#5¦:豯«‚éyì5sïC‡:÷—I¸’²yœ°‡?çdåpÐa±vwì+ÓµïPÌ@àz„<[d¬¤ïÞ9€;ZÒ|ÏÕq4àÌh6ÙÍ×ѽÒ(å/½IÛﳩ  õ ™÷Wí›<*o¨ƒ}2Qó˜@!.ï+¥dâ`7p´{?a.÷°Ú&üô¢T8§N$|=9Už•Éõªÿ좿çD_7˳@'vdÎ7\Z»û¹éke¬@rù˜þ²Þw»–+ý(½ä”Ÿ;Ýcg¶FºþZãåú?ú z²u²!á b&¥žX~½°øWך<1²Âp/¶†WÂB¼ ~ŸaVaT|3ΦìŸáYíUé5ªÝ[¾šnºé¢W\ÑýVZi%g+t“M6q æ>ÿ<»ón¤€Ë›m&ÃbûRæ™g—ïÜs÷¯åž_|Á+ªBíÑ@Þv5nܸh¯½ö*OŒ„rÿòË/£«¯¾ÚmwòÉ'G¯÷3¶|¨Ì…[¡BÓŽlMKbYªÍ”›ø!àÒ¦eÓÖóÊð)ü ꇬ¾¶;^3Àb z¨ óˆJÚ8e Â8f}ÆoØ}ÜDÍ<Íx}ˆ© ]õ²€¹ºé14(U?{´ŒfZ0ØTi£òpS]«€>˲ÕU ô|£€?–ÁŸÖ5 ÚóÄh10¯ØmXè„]Ç0&çu%¬zuްÈvfÐÉ5 `ˆ¬ãÒÉÞ~Sv ósŒ×Ÿµ\/|–?v:Û)l2—G”í 땞@¢­CžwÓ—,‰Õ•ÚY4£¯§¥®ô[tsuÑ&m^}ÝÈUϽ.ô5%7ñ.ô&9&LÞCžM—[–n‚UŸNÿ^´¥€S*çÔƒ÷€-Kè¿}à:VØ ýXÌĽQlŸV "6?‹e1w87ôróÁ½Ÿ6’’Þ´%’÷/ï(tN Ú!Ç»HóH;î/úkX¥¯ß\ž:‰[xœ:TÓ]þ{PÃQö¾EÉⲡû°ù§WâæŸ ö¾yÅô‡ô¯sš<8]±‰:å%ÑôeÖw§›€h:å|{ ø e?Çs8ö/~–õ;qâÄèþû﮹æšèÚk¯zè¡7+ï½÷žc _vÙeŽuûä“O–üð æ 0õÀï_ÿú—KÿñÇwéaz¶î§Ÿ~êÂavâæ›ov¦lšþ¤A|-ù)“öÎ;ïtq)ùrTyå•W"5uÁý<öØc®LêèMº¤EÈëöÛo>þøc R Ls?~|´ß~û¹¶›erå´ÓN‹N=õÔèßøFD;úé.»¸Í§¹. \h Ð@•B AU€>ºÐSe7MfÓnpœƒ¢zl,UVëA5No›xÙrüÌc)©_3Ë1‰û¶g3 öfÇSÒ”k€°‘|±€ ‚@û°é;Òæí&€Áÿ Ãl"Cñ“ë ˆl'ns¡£è~ÃÂ"è*’N#’6ÛVØ‹·ÜZF´ ùF|¿F¯Ó@FÓÑð ;ÈL¯*wÙeÖ°.Ÿghi­J@sv³€5¡A#l*S/­©Ó!’æà@½´ `ãÒ+5nª¹Mڨײ~=±q»C2Ép‹iØÞifÊ…ù|N•¯å„ÉÀ6–úaÇôn‰:à(éêômaó:0Kë‘ÝœëÀ´Q ŒÛÚÂVŽ¥2~å3 äbâpòí!`v}Y>O_±’°W•þcñÒºr58bËY¥®hŸƒàê'´ÌONèk°7[# 軫ý_ß½ÞõѶYð»^øØÿi1qðw¹Í;âËž¢WzîŽ*Ÿ³‹Nb0Õ1p;.'»•éw™8öìäÝ¢KÜ‚¥ýï`d5R±%<\®æsà2ÏùÀ”÷ q|ÉÚ<õ¯=“àÕOæRÇä­¤â&o:Îc%M×áäì©C€– [Ôy„œÎ´ÊÎS½:yWøt?µ<›ég°ó{¼Êãœê&=™0³+hW æCÑrü¤â6“çóFòÎ¥,óv ¼ß±ƒäÿa´´€ÅȶBWøÆQt€Õoôt9@SDí6Õ´“¬iiÛ÷±†‚W[Ê{§LZúÆÀ¯LËäEž‰?pœÜ>·ê‰ÑEdÖZÛÛ0¹§-‚·$ëô‹6èY×Q'ÈêÌ`©3dL@ÝTç{/W&u5ö¥V'±<ÞÆ sÁ>ü®^Þ+ðb>`–%å³Í6[ôì³ÏFcÇŽ-ƒ®|ðAtà 7D}ôQ´À 8»£€§€¨iaFÝÝÝÎŽ0~€µ3Í4SôÎ;ï8`™|°Ñ;çœs: õ¶Ûn+ç{ß}÷¹´Zh!Ç&>n æAƒ¹ógœÑ™w ìä«à0÷p×]w¹r,·ÜrÑÌ3Ï=õÔSÑÝwßíâñ7aÂW.ܦŸ~W–×_ÝÅ+*N LC ­n»í¶®meûÕW_˜¸=zttÈ!‡D—_~¹ ~å•WfE+ü HÑ@>~eJä6;ûlÈ6'_$× ²€¸× Û²Ádûupµ£ØW…¼$˜¸Ñûx\Í#s7`3úS ô~ÓvŽÖ!òY󌲌‘ÁÂNé˜n€Å{ž@¼€>ŸÊ`¼WˆûÊ2TÀ²²—0ž°l ×ß´ Ê‚sÙr¾„ ‚üa•o5;ŸlŒ…[F4;Ý[itçõxùr%X®M‹là&¨AjtÀ×»,€ò3:}\ÏÁt. ø“¼`SÁªjDèÖóÀh“(V×ê7³œ¨ buëócçñmÎ’z?»ySR†é®í‰ º¼ŸóM½¶°¹¥ë¦‚àƒ#6•«'Ôx6++ÉÆ\,OgÄ |³OmÆq–̦,ÚÉÿa“•\us'¬ä^¸ç¸¬•T€v+Bxú&ôÔÖ4î”ã )ÁžrÆ’å5ºÞŽ^7L>ØoÀ6µáª¦ˆŒ(-Ÿxÿ—V7.O˜óÊ©†êB?•&–Õ‰ü>OúD€I+i¢û `}¹¶ jÎRc0õÑUÔõÛ²ßiI¿Ë¤8“èÙBÀ­;e²€þvY–ÎjŒjyT.ߌ®•çËsþ÷ž¡Ü5&-ª^í'ùð.²&"°Ã ¼Ä…ŸQþ/‘gu-“hä `›N¸…@G u+§º¸]!ý3u sDVÔ+nÔlT[aÒãƒ)ûÉæ†S¢ó¥¾Û ¾ÉÏ?gÙ<“bJ:kËÓæ}cÝÊb¤bRi’€¿c¥nŽž‘gIý¡¼µô¤·¶ø1)ö™øÓ.mŸ é^"õqV½hàÈž º*§h.(&-dZÁ‰Ö³ä²|øHÎbJ¥<óT[àåàÑ‚rŸYm«²}gÔµÁ^rgËdƒ®ZµãpqM{{Eîéj/f|)wÛ¼”¾%qÓû—Fæ[M7¡L;ð‚T¯vx¤¥´#õ@ØãE`ß"ÿùÏ¢O>ù$Zk­µ¢õÖ[/Z}õÕ£5×\Ӱʰ€EÖ^{íhäÈ‘ÑFm-¸à‚teéxHj·Þzk‡x*[n¹¥Kÿ[ßâaÆ@,G˜ºÏ›o¾¹3OAL9¼ÿ~\e[l±hèСVXaÙðªÚ¼,GÊ9묳Fl°AôíoÛå½ÄKD»þ¦v¤¿Æñ½.¼ðÂàöäÉ•ŽÖeTü˜4ð¿ÿý/Úyç£Q£Fe–öÅ¥“Ye•UÜqðàÁ®­iûvŽÅßWRØ +¤ýðíÏ¡ù{ƒ̲¿Bòiàþò§rmx<ª Qí’2´ ¦À Õg‡ª_#Ç}s°žÔŽbžtW•F+²¥ $ߪ“€îÔÍF?iB; Ó«ûŒÔ»ßé’~wOÏž¯¦¡G@_e)-lØl nô.']ä$Øj‰ü)`Ž/q „A¦-Þq®•?–ÞÖÓaÖFf›F©ÖeWV²vgñòåŠ#@KÓâ6p ?çw “û¨ØwT±}¸hiŽi@àJ3Bÿ¶£¸¦Hâ/,²6ˆ5lŸ»~UŠû€bs帋Ÿ£°hÓýÝ$qúÃ?ö¬¿I摲Æ0ü"UʼnG_Ölzy½y†?’~Å‚§6žûDë'`.àûéI=òxl”ÆRýݠƆ`‘@DxŒl4æsë’hÉae¹O]>þ±´U m^9î!y㲋À4·wÎí´‡mc[Ÿañn+ýÆ(é~(åfbFëÓó=·\¼&}N•”6¬º¬¾XÞ1à/©¹o ûoEaY:vv§lê¶«ôU:âÖwÁÓî©IÛì<¦%ï f.|4‚7Ì,ò›Aî{R²‰XÔy˜Krþ“|lpÊ ³`¾^æ]tÙÌG{ò6î„Ú ²Ž1õ ?4 D¤m¥¦ÉÿÊ®±­yÆØ\O³—OoLê/'ŒzMå1O/ÿ Õ×ß[Ë “b[J{`³RÞ5Ú†I«§s¨›Hã]Û/F.N >à"Ñ`/vÔñ¿Aò]^Þ»÷È}ý yÇýIòö{àmäþÐu_ÊK¢GÀê²t(§K–/kOü)ÖÚLJò ÐÝ~L¢³¬ôÞ—<ýv4AÜNJ&QN|{d¥—å÷géâi´ŒP=·Šg{t‘Kµ×䪯Û|ÕÚ—o“…”2$~1ˆr `й~œ#o¿WLXµ°tY®‚ý`ÀS6Ÿ É|óÍçÒÅæ-2ÿüó—íëfpjkx5Öˆ¶Øb gŸÓ €TŸ}öY”×^.¬cÂÏ;ï¼å<Èö0°«By®U´, †«{q,40-h`ï½÷ŽvÚi§º›ÒÖiÇ•n‡ºoÛF_ÝïBò¢/¤ï4\Æ×wÙ9M ô˜%¤½)ÕËÈz'§ÊWL蘭•*›~5#!€È²+³À9©>;´™2üÞc=5“†s_ôX7=·6 ÕÍ?²¡R=ɳS7íÄFý4ÿ`ëæ‡4ÔË&­VYÊØÈÜÑgE‘[ê]/ õÿu £>µÇv-—õ™\µ9Õº¤mzÈŽòíÀ€v™W¹VˆÓ柺Y Øg@ë®óiK¸}3šf³Gè@à%ƒîÞ%¥Ž5×U—f¸Ç&¬ömü v_h ne™ Y=Ðþ0“ òwæÆdu«Ô‚ÈÇ~7ߢ›pòRéW,xªq9–&Ÿ.`/Pb ¾ÿ=ig1ÿM °ó«€#1«ß9Vý}ý|ò‘ ò,aÃ8µuÓϼ™EÊ øé+î”ú<»˜~øI`bŽš³³°L’r2ÙÈÄÌIÛ:)1oàçÿ´@u*n¢·çÕø²´Š:WŽ¥.ÝÚ ;Tï‘J8¬–úvÿű” Ñ KØÏHÜËÔFבîºúoÑïˆj's…™ ˜©€‰ýÑL¢'¦Âî=.'€!«4¢®ßàíÀÂcE—¬žˆŸhaßÖ \:»4Ò:—Ïó€459â 8Û¬é€.›·V0áO©çD»²Â~…zCí¨{L²h]×2Ò~ÓXû¦7ޝ&z½‘v‘æÔ×@ëOýg0-–fKoHe)!l²ækg«@ ›~Ý%K'uîÕtå½ÿ¼CMA®¸ü…D™7!¿FÝvÊ`#e¥õÓÈ “æ—eƒPYµiq}wjŠÚ}<Ñ€J„c³ü×NÑ#a|É»¹‘nÄÒh}³ Cè^òìS>—u¿Ìy®±¹FzÐ4Óì ?ØP?°´&WsÜ_ÌÔó8î¾!åµý Ða`¥Y’€Ö-ë¼Õüì2@GÔ^«n' RV¾!¿‹SÀ=Â>+ß`:á9lf `•¶r°†ûå|Ü(Ô_J|žúSâ7Û ›\ýû{ ?TV’'Ü·ÈýºIéA׺à˜6ÙVtËg²DÓáÈJ—K¤žaãw= EÏ•V{÷ʦY¾#:Ú¡ëÝèØhý^XïôÙX.§×¹sù´7Oô96““o¼³™ ½  ÝCj­ñiùðmé D ])„¹˜¦iÝ×KÒMÇ.+~çÎ]ÑFÖp9ñiè¤úMÚWó °w“hu7Ûl³šKÂ;;ãH…™›¶‘”eæ¹­ÿûßÎFéˆ#œ¹‡í¶Û.‚5œWÐòÂʈ{Û¢å-W.¬z¯øp¬Âµ 0)‚Y+Øí†%_H¡vj { ­9i¨¯¿˜Á‰µWW?fuÿÓ:}*»:ž^~sYr« î€Pí”4¶ìha{•\ä§6N9·ÔVÚ%ØH¤Í€9 O§§’eƒPYµé±«}l?v¨Çžb8Ž?l²¼ªÑú*$ضúoûz¯pµ”Õ»²°3­³šïÄ·˜ºs¤M5¢7ïyÌ• …®fÏÙ°¥Ù¡Þ ÀL¡'!Ð]¤q6k&voŸ (¨° C`ùoCuD@¤fd»d;ž{:ˆÄ¤2àÌòæ@ÔN5›]…äçÞć x‘÷+Ò2 ËiL\©|ª'lÈÒi_‡wK{Û»ûƒhIaMÞ&u›_xcÁ„iº&éŽg)€é–a +f3®õ<"ðüíûõRIƒ•˜pqÀ¼¸ ª3AÀæbVÈþãØ„A¬àa´µÏ.çî» ðs•¶®³7:>e=$e ï{ìg»¡m—‡IÓ´%Z]y˜H‰„sédÊAî£4(ΉuÇø¾–ކ—Öq€¨[Á0àç»ðd©{€KÖ<é8™ø¸RòŸIBÚ²“š/ Ø:°C©G 8Èäõfʼn»¹TîÖèi²©Ú¤?D`¬V¤–)꿟è©À­•xñÙ’—²?™Àƒ¥ú f&ºN3ñø‹¾–96'KVäñœâÒTRÄöÕ—>Œº-Z+“¸‡3m”‰-_4éû.Iò¦çy0¡<¯”™²ðì¨w*øM˜´±^ºcˆCºŒ!Ý»kÒ÷\ØEÏwô¼Ýc˜ç‡¼)yÄmþYyÁÞÝ ÷c2©A91±ªØ´ÆŒÒr#×`rÓ=tî G¿?é<Êi¾õÖ[Ñ3Ï<ãléb?ôż0˾ùaûóž{è6h{ã7ÜÆmšØÃ?☹iÀ¯†Ës„­‹Y líb“SÇjß7OÊHf#9+€Óˆš¯°~ÅùÔÓ€PM½R|½rf"…¶F? Â†ˆL°h¼âXh Ð@E3VN‹³&4ÀGì½-¾-Fò ±¼ò–ÊòĉïÕ!Õ0l˜º§ÕQƒWiL.@­ƒ1ª-¨µdg5%}œóVl eóê6fúù9 Ð’¢Ö'T3óò_,ÐÒ­ö–ÙeãPšÊö•²ÁÐbc8«ŸÐ²I ËêM«³õá¿É€Û`•’ôÍY:ãéêÀ ³÷¦ZAü€cäY?t¦«¸ê·*¶_C 3MØÈiÿ2í P`Ta´VIÂ1”9òíxaFŽé|m`ÊÏ*Aß_>g³«ì& gš°!Òø4Oë>àŒHèDÖÅ«y‹q˜Ož‰ïØû[W&Ñ^_]f_ÅšìÕ $£„ýíV¤ˆ^æwOÿi ¤-±uN9åyŸ#À¬}µ­¼ˆ0oêÇf²˜÷à22i¨iWÍ`7úÿ\ÌÅåO7ÖZ¡4¨ìƉM“k&qÒ„wX‡›èN& frA©7wÊSU3M;VÇè7ѯ¤ûÀË€wD5 ¿öíŽy„Ñf"‰6z,Înó’R°¼Tõra9©fËûqjȬ{¿\úFû.£„Ûp ßøÔÒd·Êà Å·ýéÊŒ}<ìÕ­ß H¯‚iŽÙÅÿ×ÒnÞîéŠ>‰*6¤W9N&§žéœÏ¥ƒ|„„õ'„n2ö¼5M{ÜÞ•/vAWú|l˜´s&5˜¬,¶“/iáq¯­Õ•ÐLˆ ú–`3ÕË„-«î±osÿkÉ}êæ´6…«úÑ4Ï—ç©ß+6Nžsú±å9Ï.ÌÿËt‚­—yâ§…¹OÞ¥ÿ•´³ägRW½¯‰õÅRæ¢ §¥á’ÈòÁ„z ø…ùúøã»ß£>Ý}÷=ÑØ±cY6pSìRKÅ7{Çw8  è¶Ûns& Ò0÷ÝwŸH÷å—_r6{5àÝåtdù™fšÉ1ˆÇ7.ºóÎ;]Hb„ .%@hä…^pö|ÝEòð«αaq°ÌÆU€ÙØîMieùho–«Hûë­—^z):çœsà»òÊ+;vïqÇçÚú¹çž=ýôÓŽéÿõÖRq÷h 2Äh$ÖW;l ´ð•ºévÙÝ )…2ïˆ.osmþ³›"± ût!–W›³Í•Ü÷€¨ûq*)ÁÚñE‡Eñt¾ïÛ?®Ó+}mï}÷€¥› ¥i*kp–wËõâelP>äœ8a˜Q<ñ™ë«$Œ)õO;Þ›,SöýàòÝ›½ö´,æËìëI3ÊÆ«w/°À¾ñåȶ:œ ¶_CRͬãêÔ„µå|¦cu¨ø*kG}¤,QÆnZ˜’Á1lÚ]Í¿\¥:Ú໪¯s_Í& ÒŠé¡§ìÁÚóåß)º%÷•}ïIQÄ2{¤ ¢ë:ιÕûÔx´êêÇóùɤÜwÎéÌ?l.ý퇉MYòP°ÐòíœÅÜÍ7Ä;®!ù.-½ë祶S©ƒlÈ ;ÙdÀ‰®Oøž0±W=T~ÿ5öâ×€óö¤]c7ssYï‹‚ˆê>"a»êu£G&B¢7}º©(æ†:;¹sF_”–0®+Èvwé%ìïç$ý¹E/Ë;˜ ¸¶V¸Ë˜ÚÀÛÊâѱOm„Äey>w&ú¢œ˜0ØBžÏ ÒK v£Y7­ƒŸõne'¿ÄÀïaRZabÍ—R7|”u])ÿ9ðkfˆ1?¾^Ï*'˜})÷Á=¯‰Ë³2)r¡©:NìüB@åÿ¹÷—zøqªûõ¶SÇ丕|ÓüGžµ²y™œá,!í ý_môǦ>X;NÞ¸•GÎ4Ǖҵë˜%zJ&Bž•þüÉë¼îøkœ‰!e»b3Xè…iMžiß·õ|),üúð&¦9¢AWJJµò‚Lá¿,¼ÖliþÛRÊ>&̆ò9[&˜PcUPH0kò~  ÿ\ždt¤ÓGG3Y ¦€­ŒuxWú.á”?Vªü¤ºG¯ IIëËb0‡{š¤ÚëÐó¨L,?)hn?¢…s@O~¡ï¿ÿ^´øâ‹Gl°A4÷Üú8#–>Ü™Y¸ûûï¿ßÙÃ]uÕU#µ—K¼¥—^:D<^d‘E¢‘#GV• !3 !7vïrË-uuu9;¿·Þ*Vˆäœ2 €ÒÈСC[ pØ€-Àjü¯½öÚ;Á˜´Xk­µü m¿f N!…¦¦´Y3+»¿˜xÀ}Ô¨QÑsÏ=m½õÖÑe—]í¿ÿþ®mOÍryO[H÷Ç»ÐøþX¶Þ*S£KÛò–£1»›yS­ —f«°6dk.IçÖáNè`¡µœÚ{| 9ý˜~+à×N'ްÛ)jï}ãòЬ:u–ñé ¯Ú'ý*Íί?ÓÁBÖFsé¹Tû„vW÷˾`RÀÆd6h´ïRæxu©j¯V¨/¤ J$™e1§XöÞ—rºhÁÚÚNËŸt+‚½Ï•µÍL9 œ”Nà¨iµ½Z`N³ Ðäi¨>º¤¾\³œ"åTf!`€ÊyA½%6Q[úaîm¼!6aÀÕx$«Ð_Áv_uÝ`!tS£Õ¥}PWWïŠd·iRiÕ2`H àx@±ô· P„#` uþ{ÒÍ,e˜[ÀYdŒz¿—rJLkÜY™hÆŽ ”âo7ä\´k´‹òŠ´&`Iaw¯¯c®?0iºˆò·¼”Ëg•~БgjÌar2Ÿ^ä>î)Ïé/ݱiÌŒëy@â¾ úñ¹ÀnóÈ}ø“C•ÄqvOÑàõu§ÎâX»›»)3P»O/'ç wÏUžËàë…åÉí#Ìàs…Y«*6rí¦uC’çft ¾»Ä˜!qù—ýÞtgj:è̤ù= ëÝ*€&&À-ò„€ýoÉ5Œw~eÉþ,ûmÁ„2f_ô]³Ý]’Á¿¹…¡ü¸Ô[ÿý lûõ9%À6Æ^7$‘ÑR÷aræ§)Ò°ÁMy`Câ"زýµ˜æðek ïh_°óÿ܃ÇßèÜ*Ú¸{6±oý©0ÿ»£ƒLŸ1Ðþw”{·°ôc¶/³ù=-e:¢gö 6tšÍœ¼Z>ÿ•¶ÈG7OËó£>8Ëá»—Ãç=a‚ØöYñÎúŒ‰ˆ´ âêd MÐý …·–~x¬™\²aú꜕* Lˆø}(åY?°²¦¯Ê™–c Ímj@1Üpà ]8€V_vÜqGß©åk6rƒ% xCVmæÚ„'OžìÌB`6AmÿZÿVÏ»»»yØ¿Ê$Æ–0 ð 3Ä]'¦%°7Œi pÙ¼¹XÒN:º¹›õoöÝ\pÁu£S6XÓ…h‡¨ÿ°sÙ|öpðÁ·”,é¼ûî»nb'­ Ù ~ûÿìœ$EõÇkfw/|H”Œ ‚d Q²((ÿHR@r” HŽ%Šä$ ð'*$#ŽŽtÇvwþ¿ou¿žêžî™Ù½Ý»æíg§«+wåú½W¯þüç¤?†ö—]vYøêÍõÆ­Ï-‹i¢ØÖÂ5Rô™$if)8N:¥ò0%ÓYP ½aùÓ|’ $"lŸX¶ Sµ–R½± èkb£·{ŽÔi_§39ñ@ð€þNŽr÷±‹ “ú) ݨ¯C$‘g~‘ë™ –Féå¹6†:žC?c½T@p} Ç0Ddæt!ö–Ѕɱè,”GðWÖ¥úŽäœ(UÛ¯® Éé"7Ž«Y¤>b*~¶ ŠœìŸlÌî.ÐñlT>Ì®²’yý“n5VôL?*0 ˜(ìÇUÍ™¸|*ÔC…ZD×ãfus—¦ñÿMÞ,r¼$¸ÚÄuÕ½8‹Ö—?.¶Ê£N…—Ðá°[;ZéY$µùáðð#ù`Ôï?¶(5Óõñz*ÿI7—Ú$MÝu„œ¨,‘ËbvÅâ¥UÜ+‚ ׂ¥M…ŒÜšÔ¿SyŒ²‹Kßr;UfðÿŠ×ë‚Z§LÜ×Kšç‰Èm޹y…Ƶïºj¹”uM‘èÍ5 ÒgH?\¢Ìgö0zп ¡>ÝëyyÆ’ºKq‡À=ö!½%IÙ%š¢¢åPõ¿Å?€@7­sIžŽŸ¸¥‡ ‡„+`gïÆJ¬º6Õo×zM8p]+õHþB0mnÕüƸλƒ1¢ï¢²  Xƒ½(Õ ³‹ÙÂìÄ÷.¢¾õ úmt•Ò@÷†ô-ŸØý¹û‘úÂÖJæôs ­§~a 5ö9§)ófV3;¨OÞ­Z}ßÇTýa=¿§˜<û:ÿÖûÙš˜·OUqªpòW²§¯ï¬µË_µÊá2º¿èÓm€Ø\¸vμ[M-6•÷U7;Å¿¼«“óÆL¥± ±ˆíTäcjØKÈývjO'Mvâè9¾Jí(»b ÛÞd'Òô­èÄddtÀ€^xÎ9çÌ}‰U HÐöèKü€PsÌ1G d6Ð?Ҽ׬|¹°ª/A_Ò0k†Zh¡f¼µü´J ©€!ÓlÛk&Bôóz}¨™xZ~¾%0%A_JlZ}‘´èKjú’V ôíËúô%gÓ*èê*5Зüö'èKü@_¤bŸ\З´²`Q¸ùÄ}JPú^K{YºHÿæ¾€&õe~Ãç䀾Äc oöbšôMK­US7Ð×$ת.“gbÓß2éífÂÀÌD0 õ.X0þo/XâP^ÂC5²¹g(%†ÿ׫ß0Lh%v÷È@Í\šÃ $K^÷o`‰ÞàÇ]°£<è‹3:6CÐU)vœ;oIc]- †K޲ý8›f½÷³ÕçjA_…( ‚}âFvŸã–JtÜ~é%PÑÍŒ$hHœ.(•Vvç àB]Ì}‘þͶâ,èK\Œïƒn)åcU¬r ðð(G󫼑||@ãE=Ð÷ ]F¦?ØÞë=­LÌm©0]‡é5ôÅ—@ž‚~„+ /®£þ(6`ÛQ è¶Çïµý^àølüö3?;UÞ“Í˾]¦e-1»ÝŒ^AGÀ :I |Ø^‹™gèpô õÿÿ(è ÜbŠ~QéÚkw•8£>¦ôÅP4 o¾}ÍfÊ m@ñ#=ù>äb78 }€¾¼÷ôUÛ­’â+ï^}í:£jnÒôIàžb /jPÖ×8è™w.0}Ë–sÀ)­ëàîO㯌@n@_ˆ5ÊáÝŸi,*»«T:Ǩl $×à{q÷X·‹úJE 0qí§ñq§ô¥^Oöý(Û;}þçF•òuq;©Ú:Çz>}qƒÈ\ÉÜmª¤øë뀾Ð>’Â5¶Ïz ¯ÇK?‘¯ùÕÍn!¸§F /ž~*`ÔÔÎD¡ò~—å·ózlwµÆ¾ð¤aÞ¥¥N#wèKX.Æ Éphytvè‹¿+›Tƒ‘ç”´«ŽS2ÕVZýZk¯½v¿ÆßŠü›UÇÿf}pëkû¬ÝvÚg }"ºi2$çzR õt:ö$¶v!ˆÑ“°ü¢K®E½/"=r= ¦¿©ž®ÒfÒ6“ø½ ’©y”m¿H ±H^$ØH›}Þ3ÒòÜÍ.»1 7-Ü^´faúãYtaÖ«’ð àà³Ð¢ŸÌ\LSD68öÙ× ž2^B ?ns/;Ëêp†™¤4HÿÆi€@W¶å·Þ+ý!ð—üe`„k–~¦¼väÑѱîDÜÕS²ãâ„CW9©èË|'ÓŽ,~ þ‘ )«ûýº+ ` ‰ ¹é—~#ŽÿMA4 ó²íøÐ»Oç”:z@kGFƒZ["æJÁígcÝ#Ú#ˆˆ ÄÝô=Ðu¸ÝˆK”‹h#Œ)ÀCYˆˆ‹ÀÖÖü¸«@»Jå1÷kµ!¤ €ºuaÀ —9^Œe§ l‚ÐûŽÈ84N°•«<âýÔþ uŒMÇ<âR°vIAVsćf ý‹¶Ce‹§s¦?Ø¿?«H&5ºì«jÉEiŒVµë™i˜¼×?Ài #òþ Ï@Öu”Y§tQ¯Û}–ܾ‰'®l?:|­1sÁ㘒4ií+0Ú_:·“ï,mëîW}Ò.Õ©ïS*óºÛËßÅk"Më_üä]ÆÙ ˆÛ%n—Þ_éÇ`wõÆñjK€™¬Åöóe$õ"êo£åƒ70»^:€ 5”Œê]íÇ&’¥‘¿&g·ÕDkû(ÏHá¬-á9zE*>l”ä.€S.j»0Î uçià£þñ²C$Èô²¤`Õ…†'j ¥‡Ý¯S…Hö^*éZ@â:BmºXcÞew÷SGöŒ¿Ô¸";|›Z$Né0OäRâ¶~à’¹<2†j‡é³ÍÐ匙•kåõýÛI„tHúx^lèºnÌ{S‘½•ް—o{kì +•©›'Âþ%˜ y‘ÆCc 5ú›!ôbG¬fC6ç¶l}»Hôï õ.ToRj…™"%€ÞãM6ÙdФÕJäë_¨dxì±Ç¾þÚúÂ~)ˆ-švJ ”À±#h“›;–ælPûc¡ƒ.¹õ¾"(ÆåÞž\ëïud1KÑr6kÛ¿ï¨è …GUÓPQ5–"ÉTÚ¯\túñí÷7ÙC ÍÂç=ÿ‰/Ü´ Õ÷FìÞ,ã ù¬Œôn^ºf·S 6Ù»=­æÓ±Þãˡ̭/Ÿ¡V»%ƒ¢”‹€£Þæ‹M<àWQYô6^Â]'9ºð²³P/ïx?èÍäH6 P©kDÖKC¾]¤Ö,® ޼ZG§*ˆï‘‘üxBÀùã ϲ‘بތd ÀB˜/6ä„GÚxá8þ°ÿº®C‚är€¦ÀãmGglˆ- ÒNHHêxoçèáµ+ý@õ¸lü7Mj-½^‰o¬þ¹hê|}´ŠÀ Ú$ýb_?câ’dL¡Lù· Ûdtר^ÿ«’x¢ý æ4åÁ`ŸƒuôÜÂÿ4Ë–T!ô>&€£·ÑÏÀ"ÀWÀmûÉò0È16Ù\@<¬ô?Òø~uS!½G-uþ^Vñ±ýÐ10·þøË¾d9àúÀ¥ÛcàûJêÒ:Ìrrw¬K9/WÛw™²~/oÝK“Öc =MÚµ*A\¹FV}»@3`àÇmcÜæÝÏ9Nü3õÔÒj&@–ªŒÐ¨MfÝby±@IDATóßi%¢Ê?À^àƒ¤—^Ä7Ý,@¯h¼^·žÊ€ò/}\yc2²MT{9 6×>òÆÈײ[>¦6€lÐ ~ºêàb^GbDüî-ãT8üaÀ ¡•Æâ÷Üõ’º¥ÎP-sƒ>˜°Šï÷k|[Q/Ðü:éýÝ_jOèa„»MõµºÖ0ƒoH?–zˆ{ƒq½×ª_2@áÉcœ=¥~7>r®¹|øs)oÜQpR Þ£†Åh]¥ !Ý]o.òžâ¥C»¬™ £’u\Ϻ½/¯zYÃâºÈgÕ>î1U‹ÀôA`ÆÈeˆ!!Í^Ü~"Ÿ«ªü²ayfò÷˜”ólJmº†)¦‚øËÓ6Õ7êsˆ^iËömÜE‚¤o¨üö¦Ô¦Ñ0€¾Ûm·×‘Ñ\Ò…kŒÊ&ÅšçÇ`Èåtr‰t} É™Æ –XÛè8$Q8¥ €xÍÒCD®Œ%v¦.JÛä-üîÐwióð-e¦ýldɤ¹¼.;à—Ûäÿ^JÙ8x£ëÈ<ëÈ®¼“ÒN,½¬çï*}ŽPÏTù·6çˆË…X#ÔÆïІé[º¦ãŠ$h è*W´Iú0æ,Ù…mfÿ!†Î=}˜ß*'ÆõqÆð2Œ›á1èÏOYú¶dÔÜ„U«Ö¥µªfÀ…NÀô·»Èh ífGHÍ2ËÐ{R9è'nD!VrJ @àÔK#ãGº);þnQ5jûhf%|VzßÂûg,%Qyë2ÀThQÅwL,…Í{8‹n+éÁÄ¢TQ'°Q,Mø¹¤³#Ém™²'®ˆ:»$w·çÕeºH‹×PÇÞjTÜvêí—êÛ´êï«—½®2b‘êe‘™²—÷%ÎÝW&F3XÛ±÷FOêÍÀï´ßgT?êË2mC¢vÜ ïÙxú]sè· ­ÔþVpkŠ!Sn »èô…¸àð_êGô9À7ªœ ùw3À&Õ¼‰êk´Ü¢²›5ŽyvÍ«j·µÅ±åY¬·9ÀPh®ø‰Æ_0,W»_ ˆoßEû8£B@±1æa(Û)£‡%U¼Z Þæ 1Ô7f0߇ÎrFÎ[ô½õ†¨tAºBgïÉb] æÐý(â+ø¡NWÑšéúš$ ÀØxŒÆ£"&›EKÙþ1VÁãíìú¿%g-Æ\»¹Y‘$®çÓwmGúpÞ8OÚ»HèäÂÌÊ4º€qnM`˨¡ß‰·¦h;·k½0Z¾mfl*`=µ€ßØ´â ²ø· ²xšå‘÷µJ ¨B@3ÿ¿O³+ ?µí[ÀïÔ­E4"ТV „%€$G´Ô m¿Ùæì¦kJ”†m°zš‹Ot>æºKY´÷E¯¿D§Ð]hÏkSÖ8†IÁÆ›ÍY#BîIÎÉ!D Y­\}’Þ§ºµ¶9=&¦z¦û(€¶€w§¶çÍEÎÝ;r6¬õBo¦0lr‡ÊÓûõ<öƒ[hÝL2{ ˜A÷cO}¾7ª×.¬þ›•~¬c®aCO¿9_€Ê’ÊCUÉŸ˜ ‰ë´í%ÇÈ)'nsk³|ç~³…A³§æÁã}Yô[2­ˆû¯ ”3€ŽgWW—ëììôÿ¡ÙìZϨlZåPÛFh/a[²öÕ-¸óW½¦Ð×ÝÕËòëÿ¿ÅÇL§UÐ7¼!{J—w(i¦{Ü2ôPÇlG‹¼°ùè ¾ÄÅqÞ"З >›œ<úiÎA<}9¦×Sêi¹q³™ò@2½Зüöp[”û‰€E4*Ç!¯s¼ÕµjVopÝHäȱÄBi4¤!!@(@¬¾¢=´¡šéU£ï èË·dAßPÝIÑ·šdÓ2Ú!ýÖ'TZ; àBHUø!´M›í"1\j7ÂxKâì‰0\ŽÒ%굤‚¾ý«<«ï§Hê2¶š47Ú_( ‹¾¾¯€è´’@ƒ>}‘äk–L7 úìPÿžÆÒ¬J­âö н­¾³Á0@78e ï㦋4tˆÍ!è‹•—ÛŽó®€ZY2Ð0Šüs‰à@™%@2}qCEB]è … ïeíǦ‚ìW^,zÏ‚¾Ò« 4—Ðà·£k;¨j6Sçn’ÂãKÒȈ¾XfLKƒ¾CÒ2 /Ž%©pô…˜NhÍ)î=Mo* /R›è”Äx•€?ê˜GßV­¬ç廥Óä©¶Ý}\úf/FôŽ™ãô-m!×yR>Ž® p€mÌ5ÀTÙöˆçhN¯=¹L“µígû.ÅÐô]ؽÒqˆÅ1žÍUú–÷"Ù\*ÒQNþ5^&ËÑI…ä½À€¤p>-ì/J|!]"_€¾¬?fR=íQŽfp.õœ½RãÒaꓤxâ°¸îm^¤¯v*Š3ågU1¦TXN¡Ü*}Ï¡Š F™/Ô.`V/©tCЗ\Ю(÷ÃUû¨=[ùÒ9£{ŒcÉ®eÈm£ù˜‹@W 0%BbÜ-&Fð|²¶ÊzàÖF€¦Ö«P{²¼$?'×}!@õTå²µž‚¾Ä³¦ê¬è 3 ²¼ø—ŽËýƒŸ"з0¼3ÕÎu¸i>šâ /ik\è ¥WnÞªõóU)çìZÿ­2è«6`íê«ÒZùüæ–€)ºÏ+ÐSŠB[yi¹ÿºÓÏ)¢zßÊ1µ©AÜ:?­Qo±ö )Ï™\}ÇlƒR `K°Á“£ç¶ÉÉz½ÚoS²¶Ñ;›¡žÒä”[³iq wr©FIÇøiúµá[½rl8ö‚hõÂpûx=z:–ÂÉóó¡,‘„j(Ì‹#Ï.»¡€ FãÔ¤|ݹi¥Ùü=“S®ÑÏ£×ä×Ú©y6› 4€Î슞©vZ¹?öµžFmÁâåB3r (y€$HE§éeM/;»‰ž0\0’º¡ ý¨»¶U ¤ý0r øÜ©6è¼LcKÏÍh‰|YÀ\=ú¤Îåy„ÛGeit¦$ÂiB²cïôQ¾Ó.ˆ Áwc`Â0@Ú9Ž^I 5$äòtU ­Á1d#öSÜj*;€¦<‚¡ 8 ð¿N {9µíòÕîÏ•”ÞÆ2?RŽ£ôÿé<,|u‡vß‘zO^¤WÙ銻„¾\@Æ…lüZ ÇŸ딡òdêÕ^8Ê]tñÕŒ/åmÇî#تìk?Ò‚VŸí'WÍ2Q¿ÔÏ}9í†Ë÷¶l;ÍÕ?_@'mæ*µI¶ìh¨ €¶R]zIùîùwûñ#¸Å3AkRÛšò’q:<ØfaOÇü÷ûoÝ ©mó•}v7–wNYæ3èÞp‹O:^_]¥Üuq÷íBЕWýsñæcA›aœ½Ic!*xB¢mîœ]v?{Q;Ÿî=¯&ÆëÚ–m¨+ø‘Jþé&WZLÀkɃ¯aZ˜Yl$iðe¤‚æU£c¥¿pö:Ið¾A|åMýˆ.Z¾q¤TB³ÝË0CþÜõº?uÇë£ÿè[·Ö…YÍe´‰FŒÓŒqI§/é1ºžŒÚ¿«Ýàÿ õËUsêvBß¾¥½å 1Jƒ|,«Ñ0y㮹¹Jº}bT7³È±ñ¸Áz†̪sÅDú¾¤Ñ­E­÷Z–L{SÊÆ`ef_ÚŠ½E@›xRë¬ Î Ûդ݊‚¤ìi#Yº®‰t³aòÞO@ò<÷žØ13–‡ý0‰§ž®Ì–Žß¤˜¦9CK½Ã4W%_‰ ö~Õ¨¥êá«Qcô(6ûL°/åOWÍFñµ÷Çѽ´øÌêüÚøWô‘š¬ Ùgq9È?´9@*'úK×,Ó<o@Ãt#fÑÆ4´ŸVÌ—k‹;.©á2•©Elö&¤FB§´â? É ×UG€@ßârª¥$%!}AÛlnÇ®Ý ñæyj—A^߸Bí±Gk¸SRAÔý¤Ä.8Ø[bã?LÀBYÛ퇼blL3Ê" ÄâÃŽÅg}¾¸Ñ-6q«éy(|lo‘¤æfí*­$Täñ䕲\]yMt¹·ŸªÏúòÞT5)^@µ ÒLÛ&Ì€fŒË0 ŠÔnì#ø  ßPoÅGóIô=ÍY?Ø{CÛÝ6]/¸;§»Á ·*NÒ)í¼ªéÇÆ–õl2‡.­¯ÊÓ‘^”.{½K¥ªù¡ì>зúò(il«|$½³/«ÒÈDNy†æ'®Ýy?ZcB£¹3/®Ðá‘Aúß_ãŽéÅEO-ù‚éëþÌŸØ ®)3 i>:_éÞ¬~wÔ` ©Þ²Pª8æÒ–Î(x³€Ù»Ôkïл‚êš~Žô]ÆP˜Ì´¯ÔV¹4Ž‹úÖR¨×Õ¸`Žr^9ïäp5—B=ÂÄ7—þmeÄ:e‚Úª1¼zßîÂk@Kh Ú^L•#¼nÛ+ªhMJ Tƒ=­<é!žÜõQ1HzˆÊ¢V6=ô5ùæe5ì¨ò·yè,Õ›]¬k±vgÇdsëÉŒ`$¦=£§'ëøÚd-§¯V ˜´fëé¬m•CsåðÕjå­Ü~•J€c6}AÓ*èËF?$/Z4a¶ma^ëzôµÍo]ýà8w?Äùu‹ý|!±ék†¸h‚6 yĦ´H‚#ϳvy /aM2êgZØO‹d›´<Ð7¯öUÿË–Å䀾ÄÕÌÆ•Ín苎JI¢Þ’3}MènÍ;n6 }ñ‡úÅæ]ùg³á'Ï[÷Zbãmô~NŸîèKD @_¼\€¾'èÈR#•·)<ý‡JkeƒÆïctOun3É»P÷#SÆ$j³&nÙ4èËæ‰VÀ–<²Í¶Äbôk%l§il9¶ä¶uúz:²©<¤ç"¤à¹°}‘®mvT»Yãn®D,krx¿w45ú@ ÄQ~¤³*%mü“ÀÉôÅ/R¿ (Ï!éP¯Žï/€¾· 0Ê ï£ã‡Ê×Û±!,–ô%žƒTGùT­—z /aŸõð¨ \ V‡Å¶Ø’þÂüÓuŒÁ¿aùd»cæÄQ ôÍ EY2¿ºr©”ë<Èûbl7Ћm<Ôþž»£mg½ vO¨ è‹>X+OüÞ¨¶zº€AÔbú¾!ý¾{© @ߣr?õƒ®Êsî¼r§ûÁ¸Õã¹}>÷Çòf°Ð8$$ûav BHUÖ¶*ó•}ª¯—#)Çÿê{ïÑZR«ó /j>š%@_h´€ðÅÛOôæçdgåÁ…W³Hc1ߥ¢6‰.Ü"⤀¾ø9]Àjݹ«M§˜XíHs!寤Bc:8°<³—d_]’«§ª>Ÿ®Lt÷œna1VžÚ¤úõí}¡G ¸`?©ïïT;YBí# ú†RþÔ7€& /3ЉªØ /)=h Êæòó¸F/©]!‚pÀô‘Á7àÇ@_Ì´eæ‚Õ†‡bÁŠ"—)ô²Æ #¼.4‹Æ'ç~›iÞƒ~P Vúâ‡t³ÄØKžʼnÚ<þÕ£? ïqû/òŸ§Ò¦ÈoÖþÙò®}çK¬³ /µ­:ñÞ#ƒþ9ºC GÆž)É\VeO%~·z¯÷œçÞd¼¦U­øæ”À ßB~!M-‰ßtyL+o…“Ê´’ÁV>Z%)¶4ŸgìzúÊe‡3ëˆ=!ÛzzuÅeÒ¾lm!Þ(LÖýmVæÏ‘t3ɬÿiåé6b½%tÖq|‘x8~Y•i«Ý¢¿©žR误ÍÀ8lÈCbÃÔ솃$ÀC3„´ê‰ÚT1,Ž'"!½Ô å÷7µ`ŽŸWžh&Š\?Ó˶^æ*°DÊ~˜6¦iÕKË÷ !ò­`"%ß½§¶/ <ø±Ê9«céü¹ÔŽ]nü´;âËï héöcª0²ð¹•0-€qÐ@ãÇ`Ëä%E”÷’JÐÅæ}ÀàwôžmŸ©€Á c @ïã›kê6®ô#µ“»ì-÷‰š„[父µÝ<]÷{?á7‡¨£Á‚Ž-v¨¶0Bòq¦RÙUxQù ží§Ü7°HŽ¥Mõm·¦I*{uÕÖ+RŽ1Ê[Z’¶$Xò;›¾t3ºKs¸÷*O¹‘mkxz0z4_ ÆË¬4í/ô½·(ÀNÈ.²ŠÞ"éj€öPsUe6/.§WBçZ3—@¡P¢ros£ýï¢^KtGÀº\`—éNvî{²¦&îám[ºùºþ#ûwjܰ¨1ì¿$M9Qó8uþ…‡¥Sƒ5M&I ãÅÆb¦ÆžUß]6–ÀFåÕ)ŒÁv3OXÍ{Åv0?PMÅEhÿ$ëC&Ifæ¨;@þƱ”òóŠs ÅÉÜÁ€#á_’&8D[[Ò¨“sÈÒÛ¿/ÏèfHÞ#ê¸ZÌv Òäú¦´Žêø]¸Øíþ¤ù£ÚFjSíõ¸Ý¶Ÿ€Èó!'†èŸ’pn7Á¡W«DÒ­Õ;þ\Rþ‘Û±»þ¸†éõi¥ËÍ*z·_S«É޵æ I¤?.°÷Z]hªºŽV]!™ËÿB0–1Ù ÜçЮöÌö Ü {ûËþˆ<÷«_\Ò6‡[UóÄ<*·¹Tï×hN^CëºíÔ®æQÞÑÿxµ¡<µlŒu·ªï7·ù†¬ít\¤6ñ;·¬Fí¢õÁjÊÇbT{.¦%t@`77§ÆÍmõ ÙñÆë¼¢Q"Œu,—)­`îœVyHc9’çyîæïkõ,­¡~˜žªìܯ՗¶>¦U­øº–‹¨—Gª¦&õ†™š¹žºi›„›ï¯#qt·¿h½>hïË+Žf¥¼Š¾ ¦§ /qeAß¼ sŠÒÄi$ޝFÛz>‹Ý}”²d’;Yûiå=ôE¯[³d:ëšÙTdA_Ò¨·y¨-ÍfsÕØ_¸9ã¨!Ô,è‹ßfA_ü"­Z´©ÃÝ( úNXQKKy«ü͸Zðd€¾DÜÌÆÐ z¾6[96HÙpSu~¡jlÒÔ— /I.)g¬Æš,QGÖ_7ùr9?Ù˜ð­œñ¨Âê€Ð2 ÖUœì“‘ò»D@ƒ@/åm}ÀrØÄÑøf‰GÀâ jÇ=}™yP1B™­YZE}ÍéÁÖq ‚—Wßõ’ónu7*}¿9i+7\`Ú¡jc»é»  vvÅq¿ÀÚÑq¿|H}䲸 ^€hË(Núñﮡoš[ùþ$‡*)@_ˆvôvs /_¬H'ýTïM‚¾òé`´íë¸pqHÜÞÐy›Ì›˜ÍŸÌ­î³ë9[Ÿ¥…! ½r~зHBuÇæ·ŒŸòv‰Ñ U–q ¤–‘ÀôlGÍ9 ÀôGêSo³5€¨®Vw»«þç’gÔ#@ Æ<@bô>»I»& /~˜Œ}¥@Þ? @À=PàçD¹-¦ï¡U/§œ¨±mI*ú.¥úõ¿S+÷(O¶®ÇÒ )}±œô+ýÔg ?¬o«ú:,yÕ3Ë>TlYЗd¸t7;·Ã Ì#.W,c‘¿M%r£Êª¿F&#ëä­‡>˜Oõê(¢vÒ»±tÊ­·V ´J U}REÇû$ò¯A$÷jÑ¢¯V À¥‡FOcÙ7““µ<}“_öî>hï÷)Žžgaú}mÞ?>4ÿÇžØà„Gæ ¼y}mJFá±Q³›ÖŸ€=èÜÜB›#»ÉÞë=wð D==sûn¼Ée0GÏ‚Öõm[ñyä x-ï¨aÝäHYõ]lN³ñuH éÏ4½5OHbòß,!ýV8ríÆKªxZ§ÒFusø~+MF0`òeõsn-ÈÉŽ–›žè„PWôhiµ´îSÙï¦ ¢Ÿ±ˆšÑÝZÖì—Óxd— åŽæ/﹈êy®é§1!ºL\’wCüÎÀqÿ,­é)-ïf/­lÎþyœÀ2SYÅW‡,%&nâò.PKEУ—·¼oŽÆC ‚Öéf†Pªüa³§${!ÔEp™ãòöÇk‰Ÿ«=„Èý_ç’qÝåµö‘&?ƒÍãëÝ$LÍie•1'%B bÜæ6™ç½oŒÍ84×'õ2¾2l¸Ú·=Ü+P‡²¤íñ`2Õ€©Ãƒµ…ñ²åƒNÌ<0?ô¢;Y-ð&]zÆ1üͤŽ&R§@Ýu81qSdáÁ¶.Ö#Ý‘åYÜ/ ¢ ö§z¾/°øS…Y@uµq×îÛ*ï•* ¹³JË&º½¯ ï{lÇåîRÕ;@t‹Ì&-ê- ”–~RàÊØ¿MÝãzíØžæ)áè‡{KcˆG-bõmç:öIØ‘öÄ¢ÆK5Öni‡4,’ëH†ûËëJK;Ʀÿõ#!ä'EgÆ@xÊÒ¿T¥È÷×Ú)Åêé~$åqõO¥7$a›^}ÜÿZc/j¦hóÐ~Š‹@ìŽräemÆiŒvÍ•GiŒAâw´ê ¥r9”eï3÷_)i_ÚÌ·U÷¨•XwðC’*ï^”ª‡§ÔNQaÜ(„ÅXEŸ ÁÏ34±¥:‹è¥Ÿ‰¯úƒÚ>tºëlÏD7:EîœZ€f«ñi¾jŸHUÓ&(_êbí`­kë©ÚPU›mÆS_NmiÖ¶ÃÜ~åUu¿YL1ƒh­üK9.Rõc¢Œ‘è/&õÍÖAä›ußÞuñêôG–ª%šui½O• Ó©O•´[‰N˜½Uß=*ø/4iµ¨U­èß@çÍ’0 %ú7ůVìžIôÅ×lKÔ‹«0vš68&íS/ôµ…´P°Øí§e3utn†ÄQïfi«`Éó®S.zé Ùyà›z°ß±±;àDÑ×µ›H ¹¶æ)ÚTçûß©®:T»¡ÿ¤ŽoçvÍlt`‡ÕÊ«Gô[HUùÔȶùï.ÂØÓæ"f Ο&¨ü3y$E”²J½ K:ƒÕNƒ,éIrpò:{=ã®×ñO$ȸ :?ð-`fßÛ¬±*÷y·ê¬†ˆ€ã`–M)Bå€E¨s¸^ÚMHÞýEÀšéáåâ¶Ýô}ÇdÚ,`]B•ûÝñÚ(ÏPy<±ðó ƒØ†²üAP£¿aRÒI >0|&9=iWTñ|Ï˼I&Zùc د´½wŸKS­dºsè>2ó­>€~ö–d`3t±$O#@­Ö÷c*c‹BWcíÐâ¸d³WÔvl¯‚hµ&Õͤ Çð­.)K~“‚Çþ£ÃìÜâÕŸy ®½¼GøÜwTO÷º-ºÏôÀ;à-—·k„o9€•› ºC Ô!Ýј8£À»òs›@¤÷Î!•)#õÏ©˜7Ï”†‰Ñ÷l’ìöô>•r7é(÷«ÿ óšøNŽW»]K õ²•ëÓÉl„Ï’7 {ÄíéÁ`Œíè‚î =”äëMß›$AQËPoãÚIü¦ jŸHðìÝ*ÿÐ¥†;ºs5c{jÛYÜÃwSÁš¹8“‚tË+ŽêxŽ$ðK]¯%ñ¢æS.¨\8^R§!¬¸ìÔ ª^®£fyù¿½2«»¼íG’öžÎTY2Ol—ô÷˜[ìtñ¢ „‘›±úäî(-˜u€ÎC'làõ¿ÓÖ~ô…ïHŠžÓ˜¿²ÚãÒLšôPê cËùE§ä†Þàú´`sxâà5ŒÖoßËv÷6Ÿ†øÈ)šÓŒÑTmKÕä`öåÇkI…„àØôú7º(Ã4ûðù¼ú½Á¹aî“®c½º•Ð}s©]±•Íy¬¥»Ï’óë¡—”ùŸêãbṿ(_PȰõþ‡¾÷~ôÚv¸žËFfý²îCß½?A‘Ø6oh¿Í—Õñ9…ÙÔ){¼Pœ"¹ûæ&2TõÒ_4*êú+…V¼­˜2%À‚„…M‹¦­¨»¨.Èê+Z^\'É‘¯Ãø´ŽæÔ¾&ƒ¼úcÓoy½¡2€¿ÌH«r”¯²Å>~°m±ý@/@p1ßLÚÍøÙ%6¦?/ (ãHdï­Xb)žOjÃÊE/KåIàäûϳͻ¼)ÏŸÙ=^ ¹Æ‘àFTÔn>ŒFÛF±Dî/$›õæüOޝK2 ˆ¼¸”Ù°íHïHRÍl¢móM˜¿Õ¥†Ö‹6×mž m„HUÚi‚9ò°&ê/›ÀƒqDuDµu°K@:ömÕ›ÁÏÕN,Ø" ™ÎIÀº-ǯè:*wx{.Ãb»X è°y %D½§äG¬†Aû7Ûœ'NJk§^ûò•Ïë»è‡ •÷Z¼Æàô*uúÛÞyŸOqŒÙ}/¢¨tÓÒdn¬’.{³k’•ÎT•ÕoQ¼½³G>-"Tñ<·êþDÚu÷i‰L£÷UZ!ö=¸HËÚû–<ÅjNÖWy&Túabd/ÜÉKx§Ë 2Àæº<ÃÞˆî_»d3I IÃ6]L|ÒgÂx‡º†¥êºÖ:¾,+ƒ¬#W¤sÑÃk”º\xâÍ:~ XÔ‚Çhýë&¬›¸+Ð:ORº/I‡4m˜6xÀÊ«ÚfW{žà¾/ðò3¥õ©@ÃK¿óÝê‘CÕo7TK ´ `a8 ‰uɲš¡ñÁÿá‘>/ï+3R£ê—ÈTDîòŽyÞÚH‘ϪýòUcù'Þ<§˜žFÍ­:Ìwõ‰Z†|£½¨sÏèYó·ë¬}÷E²yVÿ `j0ß$Á ‚jút÷9²Õ“•0…`¡æS.ŒQH[[?1ÿö¤NŸW¹æ¯_úX—“>â¥ô–NfÒ¼,>ñŸLÿÚR}÷‰¶4À>Bai­Ã%Õ{©êKÝ~·Õõp ¯¡Y`ˆ˜‹ÖŽp›€éóÉ5WÌUjwoÇÌ¿£b°æÁ½¨•( ‹âùÎÊuÛ65N©m3nAç zEôþÚ8‹Ý*jWÔ®Iòl/£ï¬G¿P*VR̳¬×ìâÕ£Õyß^öë©ÏÁ(…~’9…?}¿Ë yb»z' ð‚;Å@±Iåß\·L°ëýОÌÇõ…ÙNP ‰ÿ^˜0ų'±¶hŠ—\Å7Ô©k)ž“V‚a äÝÔº·Ì­˜–JàÞ:ÛþÌ'RéWêÏt{÷1¸·á¿*áòÕõs-Ç%œìú¾§m×)!n€š- ¤~ [¬6Îü¡ɇK3Gfÿ«…j3„>4£÷†ÿÞ—æ,…]è¶E.°½£ëh36Põ$±¹í½ˆÂ£´µ~þ[kÕÀ† B¥È8›;!žÙ„$[ƒ¿E†Ò†r‰˜6~¯vÔÉõ™]‚é@ /'3‰^K‚£Åÿ‡Á³c÷Ü2Ï«¾ÛÔø5~%‹ªöYy®Ö.k#õyÔH¢9 óV÷é^Ú0´ Íÿ#°ÒÚ5 íñDfõ‘¾Ã\<ü!:HKë†QûtTCâqšHiˆ·Šô¦/–vnú­:6{¥.mûùÔýO Á¿žø®™P¤Ô~ž·¿ÐKôgÆ/ãß‘œÖÕoÞì*oGOýþB È¥åôÝ+'vÕ#ó‘Õl Ëi ÕÔ~¶‹eÊ ]žaÉ,fñ'19P¯…Æÿ0 }†D#òRíWDÞ¬ëlS½f€ò,h3ƒÀWùšb4È–‡h ´·èùè€s¼ÿP*}iÍ÷–pkVþ#Oïøãöjmþ¡€^$Ÿ’ÔåýÙöT¿þ®˜0•¶Üiê÷—êXÿßÛæô’‰ÈýA[è›~ªøhÓôŒ{ç @~>>&ï=u_é^ï¤ýQÿØ"óÂÐuXÕN*)k¨¼SÕ &ÛÀGïÝgøçRDñ˜ NˆÖÄQýžÒÚÓa.¬íÐÄ>êQ<#ÇÔYÕ‰çÄ ÒëÜ'y Ä2¾Í QOè)µû¬*˜'ÿ(›%ÀF¤Mߪž—~àÑúƒ¶‹H[«b{ˆöSÌ÷³¨m„tN ÌþPö0N—ôñ‹Žaôm `÷0õ{$¡¹àÍ«Œ)o᥊÷œ¸¡.O›ÁK&¢ºÂÿÌqÜÖÇΕì%ñÃFH[™§ƒíç†Ù’Y¹î:IÅÕµ «…!Jpü8¥ÝhõÂ<S æšÍ{$Â)ÀòzÄzÑVqûjLœEù¦Ü÷Vÿaý¶ÖUôk²=¢òòÞ{x2+P˜^DŒO¨nA€fÁQk`È6KgèbÀ,!±½BÁúÕa˜jí„¶-óT-è;U‹¿•x«üD1-C³ Ýo§Åo˜–òtvÎvæ Eý[?þM¥;=%.5‚l±š oàÌ”¸°ñs%¾E VBëúí\dnô[Äðá¦øf©ç%ØlÌSÞß`®?rÀ&$‚vjc·ô™3 P%o\|.ÒÃM.©D·³¿(“m±EÂ(’¢×ÂßÊzäÉ£› ú[t6RSŠnÍ•0Ωʿ,-é/Ô±¼¥6ÌfYïé¥r'Öó‘붺6•´‹‹ì#Ñ^.øAø1>ÐÑÊÅ9€Œ_çÆ@!aØ,÷ìôNº=„é&š_+¯¦­ã7»”®êµXÀžúz«!Ì„ôénZÿ0Ã"°M¸xà‚#è¼Ø@Ìâç2¬Ù6¡Ê›nPyu]TÙå_V¨üy‰: µ¸ @ ŸziµHozuƒo¾šz $m´ÄzùÒ®““`ÿ^OÖ Çyã»>+¿›¸wWŽòæ]cIn^8n  Á¤xPÚ2#íãox;~`ÜZù[®òDd7àF7w)ê×赤V`¬ÿÛÔ~. üb WûÓ(”ÿýVy3µ§Žˆ?åÁ–y»"Ã[ Œ\iËáI »ü4«»ÞË~NúE(G¯e˜Ö]÷HB÷ÉÐÊEjkË.šý ^ádÇÿ °:Ȥ¦åÎ͇ ÛU&þFµÞåî`kô‚æ€jŒ5`ð8]öör¥Óßi¿Y~Q]œ¥4*<¯ë%7G÷¹nS©ØPÞ0Ù¡.à1)‚üvÛqþ'€®ÒêmFz`ÔXË0-N{~¯û«íÇì]¨×¶r·¤™8a¸Ipܧñ‘vò²JS3©€Á˲_.$¨+6±ëàäåJÆÞöSÕ>G{‰Z¿6©<îÝ­]'ž›0 m¹o ®‰(R* Bÿo©N ì4nèÇžþàz›Nô¼ óye1YÔ×aè@Œ_Ÿ£á͘9ˉ!»:òYU·³†êù1ߊŒ>¦÷às'Ú­T<*&ê¬øæ=º>Q;œà‚Ëä¸ÒÚÔ)\¨þ}½òºI§š?ÿìÜ+õÊHÇlø¨$|# Þù :Á´Ü=45V¦‚&/Oé;`b¢¾(KŒ^¡Ú' ù":¤û3z3×òžïõÂy˜ËÃ>\—·ïþ»Ì‘ËeÚ»¬É‹7Ñ´Uô£ÓB̈́چìJ"¯]Ö·ä„{ßÇRû“ÇÈf¹Ú-›V ´J UßÄè17p ’IõL¡ä¦Édò¤u¦ÉŒÖÉT¸ø(ò–7éùí­}sPMoc/—2m£R¢ç.\²2µˆÅr(õÐÛ|Î@#´a˜ ›òM-˜}¬é€kc°’Ídzj1G¦žQÞ>giñ\OÊ#×óô,™”o@ž:JY÷ËK£ ÅH´ž”+’4§HJ4Ö«w@;ûzÄœ1%õ'´! ©îjV=Œkj˜‘ÛÌÒ¦’0®UñQP\@ÕvT6Šà}™ÀŠj–Ø®s QxrPvñ+L¿6¦ÿjCM¸gõ'!¡›’ Ö@OzÉe`Úëý66/x1zT`W5NÔØ³°dà6ÔÅL^¢J@ã _k³Ì½YBút´¾‹þÿC6Õ¥5M-¥çühæÕâÈ8ˆâÈû“ô”öÓb××Ýø®?øXpŒ;l¬ô yPw[& ¯§ó‚5¶«\çÓþ« !»,ÏÆ´R÷¢‚éô.ÒxÐïc]±¨¹¦Ûråä«äýúÖo¥Êñe…²’ðQÄ?˺ђúý«+LÜZ:âÇú¹ckF¦fF k˜·%±ø× ˜C¿ùíàÿº—ºoÒ¥sÓˆ9jí%ªUÇY¢þMºÐ»«Â1´åð¤Å‘q}ý[ÀÕÆÁ(äc ~³Æ*ßbÁØúK=‡êñø=Ò?ŠúE.à¥íÀL°y½º¨7 i†˜Ù‚ &.£rîK© ~¶o;Ø]¤üïZžNGü?rK«M½&`o°ô±rÔoÔ·—wp£ÛõzD=ø5à=úÏÇj%ðJסn¥Ž¯á˜´Ÿúð>„i-æe ý/ˆ!—l¬«J! ‚j?[~i#ˆ2‹({JåN!™¹VŸ€‚֣ѥë<ð\uÏ3q<ÞuîáMÜÇõ}±Ú¼Á„”‹¤G”ÔbëÆóâtF^Kk[äI™]¦ñ:–Ò×çÁX…yÆüˆÆc¯;=°”N}Knçv?!&DTÇ8IspÒªO”4÷_Õo6‘ '†~©>h®T{@ÖtÍÒ@÷€TÀ„õã¨ì.Rx˜D¯i|3 Tk ‹v¯îOݵb>\#FãHÈ`!Xy[ ÉK.éº$-rë`ûSD”Á_P¿ã/MB„ü‘×LR”ïãZø×£ØÈ«ì¶úê‚MO^X˜)µR~‘+[À#Ìs'e“GHXõØy`,‘—†Ù¥%®Ì6zî¥ã…жÁ†-r©þ²¸2ž˜+@cQ½›Ÿ¾xþx2Û(y0‰%ÌHX²œBOuSê&'‘:aóúÞ³*>ŠGWyî:Š žì6r{Ï“óßJãF=©(ú\x¹ÛîçtÄ7<¹R4¦£–qbMm¨é7ëêùn /~‡~`¤f‘>HWyÞg÷{¸Hª'4¸Tïê…Cªu€°7$çuGåZ³¡g¼±6ÉG ÛêÆ4Xù§lÐyì¾–œ{[å5©'èðG£-‹ßÞý˜Óy ½&Ok HȦt]DÝ’¡ë×IZÖ¨¯ú-z…¿§µ½Éþ›Þrè*d.€v•$'QòSµ#£a*ÉÇ.À`jîTʳî }ÇΪoÀ‹n;Kèû–Súv …ø/R/?Y¹ ‰±û´/—ò 5Ò!ȇ —¹Qÿ‹@%¨7)’““neµçÛåuPW\s¶_‡ü/¿-6—VH$ÑÀÊ9‚nxÆ—.[2> Ðÿ‰ÁØ €d*#Ð%=ŸÚ êŒûÎURQ3”õ½¯+FŽâ/*`lc}ã@Ù_-Fß‘•[¿æ“÷à—ŽçCŠ ü'4*(Tà0·ž]ÚMˆ[”g@'Ú `áø˜)¸Ãä3ŸLMI!2²öŽªe¾AÌŒÈÆÃJ$Ý2²>ªïÖ÷ª6‘ WÇLª4fLH¹p’Šrù}<ïž×ƒµJ*q’Þ–”ñ ÷Yˆ—v¾µXRyômÕ»õ@NBqÊÆ×]iE]˜¼qä9¹YÙ›åÝ*WˆvrŠÚÞ»ò-€¡©].Cg©=\ ¶ñ„˜!´ÔÇôÃä9•ÃÌjW¿S_ù¹„FŒé/,¦CIÿsj ,æ·™ç&Zç0¾€•˜®á$\Ç%õ;®û²R» ˆÅou®‘ùk½®ªôü°-(‚¤uš„ ˜÷`ÚŒƒi#ß2¢$´ÑCnåM9äÑ«9«É<˜‹è/¯8kégãCŸ7'˜K »˜pÙÍ©°!…ªØöÒluè§7f›¹z¶¦U߈øŒÅJ‹¦j dƒ©š™Vâ½.fUUô:¯I@¤iêm´m#hŸ›å›=ϼ›·[õ–Pÿš¹U>KÃn–XÖ“ôÿ®Œ‚7 ›ÝÎñHQ‘äM¨Ž!º@¦êÁ6¶!¸PuÍ7Õ–@¾?lÙØæ¾ÑB×Íű5vy&Þåù›¸‰LÑ–éßžÒF ŽÛ#ÅKÛÏ‚¯yéTû9¹¯%N°±7ú7ù`Ò?µÝ (Εw ­½™rü0¹§š#SñÆ ’JU›¼QÒ…lÙ‡Ç`Ì`î Ù|ÊG´¥÷AÜ ¸®1¿í È²Î﯆³Ã3J€B/9ĉh#^д,êﯕ^}ÿ™ü!5ŒÞ":?8¾úA kôýEÒª§·Í*0ÉHÐMå1{ñ@§9p‡CÂÜèuÅUD¡Z óƒ®Ô5bæÀR:Gªtß`×qæÝëÏfþÓ5gîìI¿u7kþ /«KWljÎMgPt$Cþ:5UyÂmÞ}{æ§£ù#± ßófV œFÈJä>,FÏú¾¥Erûû¨­†‘Ä9’±Ho#!|²æçSÔo‘%ow¡ºi¨^Ì)z”Íc ' Šô韪¼l¯ñáSÅu!Ü!M:4~‹æ’Ð 3£E½‹o¿X ݦüÃ4ñ’Ìåõ)Þ1ççå—q÷Ì{ëHúŸ: q”¤¨‘оŢ Û†ŸOÌ!~æÄ[¨¼Y«×S?EŸ„aµBi@*ÆÔêê|¸@6[7õ¹õ;ð{Ä[Oº=_y0÷ÿ†Þ*ÊWÊþò‘¯:âý¡ëÓIјòØzi•@«¾–%ÐààkY_“ª`ù£;_“ïìψ–•Q̶ìm:õê¡·qnÙxa99qL­°ÆÒ#Eé‡7|ùiÖ¾èv³áCvì4´3óÆ…’7!(aþê=¹$$$Ûp‡výež5Þ$÷Gü& ÿ½`T/ðhe=͸™=6]Up³™ý|+¾äãä 1 ÛŒÌ$å7kgÉ.¥ í¹m#)No!”úÌ7/ )¸4UÁ8ìMïª÷ㆴïþ}žD€1Rÿÿ.OïíÈ›LŽÂ#qõÏÊÃÞž²Èë7€Ÿ!`ë=ë‡xØ´n¬ÍôÏr}¤SÁ©­ÜînÀe Úë´Iþ•¥ÐÀYæ\j]ŽAçÆyR; Tár·« !¸«ûŽe!yŽJÑĪØÀ…h1Ý–™ÏZÑöótƒÿ_Aû³ðW&@OdÃ隣þl®Èø%ɳ =Xdq$¬,ɺµu@B©I€F“F[ö‹!îÍ€½ëK¢ÜÚ÷bê.ÿè arô¿´†B¨èG°§  Þ£Ð§©è¸^åô”¤wAƒ»*éòŲ¿øoéRÔ²ø. àØšxw‹ÁFs@·h–nS{»SõÅeP)àNmmHeúE}øÎ›Ufai¤c}¿ãÈ´À|@0ÄöŠó¶‡¾ñÝ ®ûã6ÂÈBú"Çà ³@j=¦éËübÐSz hÂz¾MÑgòæLÔ0¡#¦Ã¨ _ÄÀ~å°”âãz#ý-—TEü°<$UsñþÒ÷å#MK’?¢·xû±Éí³TvA!n!“7ö\ÿ!Uv‰Ûûå¹ OÆŠÓ æšÊ¿“Õ’õK2 Fï:AÏa½ŒƒžNåß;N oˆ&¬ªŸøÝe«´«¡öc¼­‹ã÷èÂv¥åco‹¹ Jµ ž0êcõ £éŸ9«bµ£#'þ¶Ö‰`£ŽtßP*bÜ8ZWDï뱿€»,ýEcÇáb”ÐG‘h i™òJá«ëÓ8 Â~OŒ%ðÐÆ£ƒÞ¦ôpªýU^f.•áÌ„0òmƒþŽTþ&jó!Ãä/Ñ×PZ¨¡€BÕU”Û bîüIí˜<ø±Ëû誖ÿ}] q!!ûƒ{â~{‘.ß9ƒZ0[Íòøºb¬:æãGúæU×ý¯ªÇÀæyMÓÌÅF·Kz1V³€!Ö–?Q™TkÐb)x¶Ÿâ.ö ~õúö ëÒ‡ï³t tÏ>Íÿ’ÓG¸QRaQ„æFáZî­h•@«Z%ðÕ(“féMnÙ$Ý)½,ìÝ×r‘Co¨ÙÛš‘LèKb‘hº­ú2Þ¼¸VÑ‚°eõyÕóÛ[·õµ°³eš]úÆÕàcñ6¤É‰Ö’Öò³~vÃè OH3ÊŠñ¡)ê<Ö{ãÂEtz»ÎúmÏÜrò¿ff­à#ÊþT“ÍË^×ûÕjG龜ò`Ýæ's¯e¢‹§hŽýùœ³cÛx6¿™ÿ%§£™¶¨U­h•@«¦T „GX¦TšýN>ÄQM•ÍÛWš^„ÔùМu|WöijPõßW¦]´hå¨éÔ$6‹Õålãœxɓؗ5Ñ@9pñGHÜ|üalq\Î"}Dè96#¡6{`?¹mýÁd‹é’¥ƒè›2ÖHß5ªM (¬oÊBÂÄ뉓9¯ ¼§^þp´ð õe+\5SïÒnäÎ&¨YºCÒ¡v´2¼u5y7Éj,¶Ša “5ýê§ ˜Dë¹Ý©rÔ™Gà—ÑEº>$.“:\Àñ…ê/+Iâ°ë Œ°òÉcVcsî×XPÿñm2Œ¹j®§f¦ê‹cÿ38SÛ“§÷º=î§G DÚ]’šHÙf%®¯•„iØ3þXûeg¥$è•ð98c`˜©,Úqµo/–?@s‚¥ë(/Ùš1òp‡ õM»Ï2¯þ‰ÿjü¯¹=ºÿYu¯Ü]5ËÄ\ðO¥†´+—¹…'WBsèÅÆªÐ3';ì»gŠËÉüÐOîU¼YZ!c¼ÓxM›Î’]„hsî¬E/m¿ÀmRäþ¦q“ õêïvÒwöwcd´>R…”×!ãæÀµ¢BÎíÖûæå=ie°Ú\’Z–¬¶²öÑûœjÊ%È»ýÏùNb¤ü¤ó8!I›¸&î*U7¹ï ú›WMQ ’Ø,r¬±ˆ\Þ§Æ6´`,Pçèýþ±”2c?UÓ~º¼Z퇡jÍûj‹¼1§ª³z¬'8ÖbE'«X×Sçû]*'4~j<¢M>·/˜Y~¤ï<,•8—lB0ŽF€’ÿУAû仫7غšÚ(L<sÿ«Üc÷¤Üa^íƒÏ3*èÜ7"¿ÓÛ‹ž¼Wõ•Wðs§ÀÛ¼Ó?Ǩ¬Y¡ÆêÏÆŒh?ØG€*ºÛº'xwSõòý˜±iãg5%]Ô¦¸ =4~…ë¿Ðf»8ñQ}+iŸ(ÿûk^h¢ÆŠô¼;¸m¾‡uº™Êo~•ß,éÛ*œŒÈ#T­ä‚¨úevÉVñ¡]ÚÌ ̱è£nLŸËKÔ¸ 5.FCe0é|€õ¼òÅ/'L€'ÿ+-Æ)ô<{ø‹î¶ßI¥vÆðçÝíŸ OÙå½¼ñåh÷ç·Ÿõêxý1wÑû¯¸P ÄȉãÜù#^rû¾ö°;ú­ÿ¸›G½íÆwGhù9ï¾èþ1êwÌ[O9Â>óÅ'®[­éîOßõª%PQq¼Üž“Þr¿0öSwÂÛÏø4{ó Gw…fèÜÇ}š„!,q¿7¿¬óÏûž–]«Z%Ð*þ,-ãú8J:-Q¸!›–òÕÊKq QÕ_*)ŠR Äæ3‚f̦þ}ŠFß*"|™äV‘ŸfìYä <Ökëy?ÀgŽá±Ä4B#KvÌ/›³~úêÝJrþx3Ü(ÞÙyÈq· VŽS]«Þ¤e¢;³!¡Ò„Ss,±ª³4Ó†´¸!âÆíZZÒ[Š|ì·WÒY´cS%Q®ÖIËÞ‘zbrl·1Tgk5épûKÿéFÚð5KHéfa”ÞJý_È&ºMÖg‚Í}(¡xž@ܬ”ö¹²«Gœa •g ÆMJþ6ˆú$‚N8Þ*3¨÷.çeƒ›ÉAÂáÚÀºj,ýÔ›i'ªHVUeÞªÞê™Ðk„ú×¹Ÿ]&·šÏê“ÛÞ ð6ÛÿPÖû ö†ÚØóýèz„óî0)<½°s„Ú­ä†×ù£þ§©ŒèþÔƒå&y˘yÀÊ,¤kÉÉ•Eäí C?˜m Ö¬šމ›Úžg<´>£3ýK>Ú·´Èš¶Èõ2/”Ö–[êzOúš{ÆÏc°…º3à*óÔBĘÜWßijqð4xÒϤN¢ZWI@Ý'ê›­—1Ö¼·wZ²-a˜ºæ¶ýüýÒ{ çJ“¢´°Épf„´ºåa%<öÝvRÉèqµè'‘”ž…ŒžÆ §Ž!FëpŽõ–úAâ:¯‡¾>i;]–5›èEÞLusƒÀ¥§Õ×fÜö—ó9‡¤¥ºÞï¯<®ãç#݆JÓK“wßè²s ãQh·¯Æ•EÜ‚qv4Ætü-6ç?¾ÏÉ„wä:š ¢?%`š¼–V–Ú†[rÃ$–¥o‰Ù_/ $Í^Þ›øë±áyþéI¨™d Û"¡0ˆ Á¢7Å?ûÈ`µ:™—q€«ŸfœOÕ˜Ym]iÇÁðïQ&©…ìÔqYâx£lh“÷«¥/OòcÌ–§+“|& 16|eÆ€9é—Wõ°†“QÕ³RÛÖº0nfG¾Pó±¦@w,í©[§§^Q¼úÑÓSëHuŠÁzjåߤrñ¿·ÆIÀôUôsbO}YúLÆÜ_MùÙÀfÊμWTÑ٫Ǫ´ÖÒ¸AÜ‘ +¾3Fÿêc«ý±}ý9‘L÷§*"¿¨7°{P+~¦ÿð+RŸ¼TsÌz± B¡Ã$š³)Òž‡ûƒ˜“f.ˆ˜y}ý!eÛáûrvWŽ|ÍG |Æð|º¨šX}–¹Üÿ}<ÜÝ¡hä¤/Ýœghgfú™Ý’L¾õã·ÝMsß4“Û~îEÝ`¹]2â÷Ô˜hØØ=W€qG¹ìÝ<³ãÚßôqŽëêt#6“‡%fâ¶sa7râXwî{ÿ¨œ­2¤õÓ*V ´J`Š•€ó쯑8¼W/‹´o qŠ¿Îߟ¿õ+þâ¹ä”^ÂûÅ%”¨2 µú!&ÏÉÙÛØ1íìcòbB7ûk†z®ù_&R6Ôy›_$$üQ댻ô*Zeë¼n¦ xo©H’ä£,Þž€MÜŸG|w32B=I+/zv=])²©Éþ›³'GôS™‰%6W|£ý)/™—í’æGX{G‚"+O¥‚²á5²ôw‹$ýÌoõ9)‘5»z¼üMV´u6ß½rs8Ä® ¬ðX©é¡µK¼Çø§ÈsÌH4Z}Qò^g`å®Äçª$Û"ˆ& "ý¨ûØ„@²DBÖ“×Eº˜YGÏò>ª««Óv½x›.?¸ãIÚ²^Tÿ‹ë¿1½£°b<ÆêrLÙƒj“vò푈â""ríÊb–õïöóásNúµ—CaN*ñ:¹!Q§ àúUùIÀ qKÝpg?`í76ÉQìhçÌ•ê9²4R­¨M9üÛ¸Uü85±=’Z{J Ï¬RIÄo¥üŠ»¥Ä¨àü:ÌæÝŠ¡,h `þÜÄm½_ ¤FÒXÝv€{°ã`¯ƒ6T‹Cèãiÿ-¢¦`(@@”ßuñ÷32.뀤BƆBæî/œë:IÀßf•K—šýÌú.ï\§4ÀÏ f#†Qf„ãyK ¨Ó¬í.Ÿñ ×VŠÆÁ¹øþî[Ô‚?v× ¼Dÿp–VèúÀqz,¸€í41¬P Y{ô/¥-ÝÓm}o :î¨øef®çÒÈ«ƒ™’u+B¨åB :^'*‰ûäõ*µ{b©UfÆbédï1þA÷ïòõÚqìÏVqa£Þ ’ v^ÝEx™Áøò?ê’7ûZRß︦ÖÚÛ(µö“ Üzg®á¨&jó6?LuLƒYeÌTÂ…—.‹MÚ#ª®¤z¬gž‡ÿÂôγ©ÿ+>x­g‘äøþ¢k’›ØÝí8ƒ[n†ÙÜÚCæq; ]­8K4ü>õÅGntçD·ßü˺õ†Ìëÿ7›cA÷Iç„„©}€ÛgÞ¥Ý. 7›tß%PxÉ醸‡.æV›y.÷»ù–qs ˜ÎÝ!É`èV=ôÝSapÇßšJ÷ߟ½ïÆ ô5ÚF€ïVs,$·¡^·ñ8åõóN†íµJ U­è} ‘2-ÓHen¨@–®ß*â¿?Ú7ÿ…ÈkÞe´¸ˆ…húÔFjD¼Q 7»}‘6 UÞwÔ´e”à(X#bã@¾¹ì]ˆ}IÀá¢rãnîH[5•±M´‹ü…»s;hs^ŠeG¾«±»äÆïÐ3ßn®³î¦÷.kß—ï®÷$NÛþý5,Ê‹© fÈòRO=Åßµ …P›Ñ_ıV£‡â&ãn(égîižo4#ûÔ×[ÐK5h/LlÈ¿BÚP6³Öôlç¥ ÿŒgGK¢Ë%9õv?]|Üv1E¿l’Äo{€€6\\SD€OÍû þa~1¦@€’µŒ*€ÈW½{òHä¡»5Å?± ÒŠèF®³MÚQ-©äÆ·1c¼¢ÍöéÞé¨icmÇ:¤â­Ó'ø®‰ÛŒ,t¯¶dKFãÁ‚Šk¯;ñÙ$Kan+0‚ËÌÞl›Ç#ЂxÐ[ mZÞØ>…—ñà)–êÜMðóÌ·Á&!“Rã‚Èëê]içÌ•Gi÷'µ,ÝYžIŒƒ’Ô%ŒñãT©óNïýÛÔý/zrì}³š Wþ¡’ÔxÙD¿¨Ö€qšÚ`ÄÐêÔQí Ý IÇÅ@pè;0_Ú¿K s’ˆÛÄ 8::/éŽ.2ò>àü„Œ›Ät çASבxŽ HÙB!ˆÍ7„ô³T·9wQAÆ$EÒ)¾lÛ°ðÛÇegcê¿‚ãöø™%¹oéÇÎôÏ\J;Ú\í”còû)_63Û¥|ı®@´PÕº^‡©¬–ê<\:<£ñ ü¶LŸI¯¡h?‘ÐÙŒ“þ Ë«^òz_uv™$Š.xi‡é×¶´Às`¤ìà?±[ÉkbS^Y@婉M¡¡ëúB's°~mï5ÏòÎê×Q­n€‰Ë¸R~Û ÚÜ‘¶§ê>7åßã±ÕÆÍıíõï'õJOÕQ{Æw–×Wÿ©øS¡:$T5lªö´°ú$'*Oóc×|jíKgõ$™kc&p)zîQžA}¹Í­Õ­±©r¿ÛaÌ@·Z÷%)âÃf>ÅÅL[ÄXE‚Õü‰|B0¿8ið(LÒæÞ°&Ìú•[ÜJ•'¼ÝÝÁå-ôs¢!—Æm;NÅ0&‡{ãÅ$[C¹G2}„þÑ¥ q¢â„xŒ€¹h¬›¡2'ŒE<Æ´¢úk¨Ò ’½7×è‰ú¤9é±LPÇ´€°¦CI Zê5Šî1Ø‘]ʤˆfáÖQ]ìæK|lrÅÜòž0½jçÎ<Ÿ®ð¿»ÚŸåy¾èoŒë©>ó~,ÿÙK—óÂÙMàwþA3ºƒX.õ¿ÓÐÅ‹òÔ´= íwgœÍƒ®¿}õa‡ê†Ïô~o†Ù}ïŒëf0Ⱥéf×í¬´Ë¥hp˜Ð ‰ùÉLYÎ!Œª û#Ð)^Ô@Œœ Š!õkî/Iõôü- o6š¹.ÊMŸéÁØÜ[ÏV ´J ¿¢^šïöMµE"¥ES®l³0åRüf¥4“Ž[Ùe‹Ç?.¿YªÔá§ÊÓfJ?Û#Àäé†åhñüî;n³¬nÛé?u«rÔ¼ |DJzÚè_¿lïCZ<¡6ö~åLÁ ¥ø¾xŠ·rÇ™ð/¬½ëºC¨-JFã€à“Ê©};®/¶æƤ§Ï)ðíæ¶9<°ŽITR\Ö¶¡\ Žä„osë?L»•†gÛöˆ£ÇÑŽ´ê³"šãàä=oì&LÈÌPÉ9¤ãë‰ÁèV^ö“¿ŸtViuIŸ¨oÛ]í“ Ì]xU⹚20B5Á?%Á;†rè¸Ü[£Oú%žø}Q°ì•ç¼^ùWÕfQ9 %Ü“õïmZgÑŽŒ(Oô óMÖ÷¢Ã2µ#c¬‹Kïûaœ_мÆŽ™0FwðÀ«¹:¯–õ7è27Õ0.a>³v·mæÒp$†¥Øb4†þ;¤Ó~¦{·\…ÌïS¹œ¯úºOŒœ,eÕÎà~¨Æ E´Z‡úDLœ@¡ÎÏÔÜÉ !±o÷0h Š}Š,?0î­k’2 ¶ÅN™}5F³é‡ç IÈÎ;húÔ?jBê ö㻳]5ô]5ï2tI·Û·–’ôíÜî]²W}ðº;a˜–QR«PVs«øU…4][uúœß‚:´c:·ˆ.ž³$‰7ž}Ü"a knC[uÊE*ب^0?­gT­²jµ„°ê,KBo-óT,ê¨73Ñ‹¤ó&ì¼h² CóG‹óbkÙå•@‘tùÜy‰†êLo±öͳèXå´ö0=нI){{7G>ŸÌHÿø£Ì™ÈÙ7CHä­²Ø ›Ô_5гH‚¢Q8s¿%gQmn}õD5R*Óšr¾ož&?`-K÷Ü9* q/dt\˜ ^óŽô áÙ\`se éC¤¯Lš'Œàð èºõ—™¼å3Jãåܤ€kè¯lؾ{ Ç,<+%yqÔÆ)Û§ª:rßñ ¢¿H¨Ä6÷ª=sl¿æX4þ$±:[ÇåAˆ´q¶8läÙ)¡ËP–¾ÉAUnkw•Z ¹QÞ=ýózÐ^rë¦òOˆF‚2ýMQJç´ösý%\0–ªT;;ôûvþ¯µý;‚nñú–_*d_u ˜2Ml½÷¦M‹`@c*p¢h%’6¦Ó#®š´§ÿÂü¢z&F1½+§ê(ˆükNêÌà@ÚÕ\|-^¤|»C'3`Þ‹®”' …ú3±Û%蓼7"@™“Ýí½…’† ,YWG¸!òøJP¯ØÙº$¼\íHIÞ^’é·ø ‰4æTš´i€¥‹c )þNHêq‰Þb’À3=Ã>¼$vãÛOõº:¯T>p‹iŒ«tê;íÔÁØ*`kþm¦G^°J&-î¨^R±ÝQ3+ïÖîð „‘©LúXÒßÃUf+v]çL}uLÆ?ÛVò ô< ð2ƒ»±ã9‘×Áje¯s•ô-Ï®û Áhè­}$éL·„›W:«|æfÏ40õš[¯r³wzAù}@åê¿¶0öôpy·ä0ËKþô q+þ쎋Á4ó3Qå¼DFeíe“Œ óïŸ^½Ã³)«ôËâÑkçiëš7•…IP–V©º¶m«¼ŒÞß|U¿&;úŽóhýÛ¶{Uç«ÏçÏ é/Ñ” '$.­¯›gö4ÿ¯G–,}×÷õGÔüã%Öi_K™÷ø©Ñ³íçÞ óç…·ñ ßè?GwïÒjqOMÿš»UÌÚ˵žcÞ>Aã9⊦KÿJÁwÈ%T«ð'ùù½¤üÑŸk´i羑Q’Î+•–Ð<u¿ãƒK\on¬˜EÏvàý­ õj˜×%†-ùmIñrfL§u”¿8+@ hhj!"÷êŽÏÆD ÷CÍMû(mˆ ßÇÖã<£®€¬ñâ~ÿåÎ"Æ´»ìþ[íIÞî@18!£Ù»LôÚú&Æ-hÍ øC×±ÍÉŸ+ÎO2ñ¢&!{Ñëbr²O¸WP¹ë<ÔÍ«9 š9ÈÆÔ#uòÃô~ûõ3*Æ í'í*ï4Úôr£\WéÜ;ñnR÷Ìí{ ð‡8ib÷0¸{;~LZTb•2 QNÙé€Ñr4àÖn$/£t)_QûiÑ3þEÚ*’H“TRiíÄ-¶F·ŸãþUÃ`©Îg€¨¸Áæé‰fr¨F²¿~_?¨ö8ÀÑBú6•DÛ>‰»\êƒAÅð@vš:<"Qy©€ ˆòŽø©ÿõVjQÿÿ~ÝÍrþØ=wÖÆ4t‹¼Bä{@ºs¯`ÀyªH·f(A†_ à"$T\Ü2~ƒÐÊ›R8ê‹nrfÕYZ›õKª> ËËÆa …ÄÑðÏÔoïVÓof·¡•™.)C¢” ŸjéËØj¹èÙù;„|{å5ÉW ¸=ý¹ÇwîNo‘jd½`}ë´±é‘ÎK‹ˆÑŒFu÷?}B]Á§ÈzÓÃhQ²¬O'¡cX’ÛH>!°+Ï"_YI¾ÜkË~£ï¸ ]«ª“ÊÝ–“”wØ+zÉÈ{Ô:ï–{" *vÊ‚öT¼Ð¾·ƒÁ¸°¤5†À¤M.‡üv”lü»„Ê›5Ï¡1ÐeÇìùBúÿ*±d,ÀþÚ4:'¹Oê`%@¼ÆP@$9Ÿèñ/ïî]ƒÎòj”siÍèè›úpƒÛZm,µ6jÛ:NaxR}ãµa»ë:Už‡åà<ÁôÈ.ë3·NÚÕŒ1cìê{`:¦€9y§€³!òG›Y_ã7ôRyS¢ŸWËOú˜=•¿'PunßSîñcªnI³×ÙTk›µ>]=öP<€®oŽ[É«X9–<]O½eÛ 1ß<'éϽ¥Öňqbù1UNDU›&¿ u_잨¼¬‹J½Ípø¬âèivô·§á¦¶Ÿ½¡ð–áfÃ#a­lþ› ú««·1ôØæ¬:ƒí0”k3ôB° Ìú”{<è‹,\ߊ“™ßlŸ(ý=%6Õí¨ŽHë 9@„IÏXœèÌ[¤›;ÏÙµa¶ @h߬9<‚Ølü¡/®ÙÀfºYÀ6‰³íÈÄØÃûP ðhFiAÖë"á·µŸuîñ;õ½š6^izS¯Ï¤­úë­íIÌ&õÃ¥1èäƒÐ« ‰7oø·ô.æ’i_Ñ[vC ¸U<Æú¸f)lc帚6C$ µ“$ÙBI úZ-•b¾UtÂ>®ïâÁ\’’€¶k›KÇ!²úØ-&iÛ´aì³ó7nM&³EÞ¸øŠvÏÍðFY°Û•#`öw:j Ã#—$õ™%T ‰nVkÀg#¦ÁŒ2Ëî©Ê9d$ü‘Ê<_9zæü"qžH`;n— !'vŠtMB†ïª0Š·ò?y_bàm$Ÿ&-ªåÿqÇ«Añ·B;2•'vA–÷耤BH dY‚!y¿‡‹«.Hô¿±Ã ÁC¿äu“3«F²pQ¸«J[»³Å‚ªÇfÝ)Ðö3 ”“ î¢Ðö«~«‹ßVÚw«­ÑΑ8¼\@Çå@h¾“g¬61y—•]^…ˆ^&n:¹Íµ&ø›ÜWÈö<]Ö€áxý¤}ÏK ¯Þþ'I ΓÒìAž¶]’4Ÿp½ëð‰ä÷1° xæm~3DÏŽÓ:Q>Œ©òŒ¨sÀ>3t!°/jEÜWðFŸ_ºó6÷sÕ;~Ôl—OùùåË-e[%æÂ—UïÇ Ì"ÀXÚõµ]—yÏH ~TZÞݧ«_³ jþ¥:¢µ­VÞÃýgú<Ù9Š‘âºÿáã^3IF,èƒH2{B?5ÿíGõ7q+w½úQjmÔu¼÷úŸ€©®ýEç3ô“îûG í€È[š«´’{¶¼P¯ïdì5ºvDå–q“c)¹] k ô*mÐq[J‡w‰¿3Û½}÷•Žõð!ê P/€š˜?WYR=•ÖòõÌ·ÌIß)E³1M—´é9•…UÞ'ø~Nßþ¶Ú߆š7ÐSþÿì]¸EÖ­™yï‘ €Š˜5ƒYî¿9‡]1ç¸fÌYQApŜ׌˜]ÔUWEY1‹kTE‘ b ½03ÿ9U}»«kªgæ%Dû}3]]©»+×¹·î¥Q5Jä߈6'ªœ®É n@<²*òü}¢nê÷æ¼2 àï\/DÈQHÆ~¹ç¨äcóTg?. ª­b^b´nî+ÓÕ˹÷žÐþtÊ5ÂôSŠC\S\‚±dF\íLü»s‰ÍìòE¤14a°=§kÌËŒ[ 9 Ïç{œGÝî(Ožz¹šsmHØ$§GBÚx~†VýÀvÁõ¢ 8o¨4cI»§¹(•lq‹çÝô»tÓ“¶\Ê=—é®%i‡OŸ¤®ÿòC  vk·¤’ùÄž˜m7%pªkhÐ鮆¹>iåuµtn{€¿»®´Žš PöæiÔ€µë-ÑIí )Þ$:FÙ6êyûûïãúµú¿Ž+¨aŽ´åÒ+¨Ý—[U½ÿÓluÍ—¨á3>Uëtè¤íº¶—E÷Ó÷æÏv[Þg¥šPëa0¨P¥ZºÌhÉ|cG[2ㄼJ-Z«çäjM*4¦Ñ²O‹I{´lÖ‹,7Ù˜ÑÆ¢"}Ü×y˜H„9Þ%o¹!-OìÝÆ}[_7 öó)ѱi %,}ñ`GjØ–†àÆ’€†í'¯"GøäÞwä°ñÅb[Œ÷ˆôŒ/M’ EÍÀBYŽå&ÅkŒ¿{¼Ï—Ö÷íŒ'ÇC}iå—½¤¬èÅ@œä âR,n¼ÏPž!5œ:µ#ØHЧ7kž–[ï•›[jÌ©„'¥¿›LÙ‹tRʉonbEw%%A#¸&z KÅKÊÄôìEi¹0‘/u–"ŽÆÆýüXÍ‘øulÒ@$¦ö¶@&†Sâ—RR¯âø2Õpð.ÌÛIUGl ‡G·9º›HF¦Ü&¥bûÐ/ßÀ#A.»Ýs“,Ýð!7‚•¡ô`80š¾Ô·Mˆ‡ïkÏÏ,Çyð»'õ%trêxH+Ýð`! nŸK ¤^¡„‚X®”¸&qÏ1‘àA8›(‘)œlq÷ àÁŸR£qˆÞ”·0%yÌœjØŽDå‰ T,ƒo§Ú‚"*Õ[gëÇïC}j ¤c ìºDQj~´w=91u¼<À¼ü´!17 ‚=<òûLv \97о7R‹<†ý³þ&ÖÙ“xÿZ|÷O §Úø`þ= NÅ£tS#S°‡Ü “Ã#Ý—¡þŽÈœK檓‰âf0 Ϩ¾ºŸM e®SðV$ŬõXg‡h?þµ­ë£Ý' þ«ÏÄ„Ts…,‘u*b<«™A‹ÀaÖùZAÛ8®ýLõé´{eg«³š¢mнßû%Ž„³-öE}(EpÌØ¸Tûžm^#&Égÿ7ßû)@åiùwÔI9ŒË¢‘혀A¼å^5¿‡‚ŒÌeuä–Ïlña‘è š –àÒ`ã½68©çB g®8Ð%Á¼nb¯'Ìj²O#¤v€ˆž×þ­oÂݤ v}Ã{êl&8ÀôΉ:ãã¡6iXx˜‡y«G÷1a°Nc­I¶Ðá]ð/je¨ZGUߥzè…´Çd9¾æaµ3Ö,1Èã½n‹Ôšê)ô%¸¥îÙW  „ãG¯€iz®®‡ P1p¶–žo²Ô-ì´) •^;í‘úh¿l®o÷âœn˜G)Hƒ·ÑÌ­U$ŸiáóѦ>L¯ŠŽ·a£Š1ÄÓV}$~r•“r¯¬:éXÀhœ&Çn9§ñ„‡RkÿªSÃð>Žz ®¹†¤~÷bĵ‘äãé‚‚wt“‘' ¶;¬6ïDÓ·dvíˆzá{ìHs]}Ê–ÌTÎa/¢.mz>`ŒÑFHœ8R¼_ @ð_t©»Rëvz‚Ä¥Œ¸uEatÛi]7ËVʉã°Û[tüvÚI_Gå¦W‡nw&¼÷×ñ‚(HT†Çœú:Õ.“i’ʦ…††D©[†w¨ªÒ€p¯¢ò9Š«W!UÌBr‰†Þæ4ÔBß/ôL€®P¥*%Ðz%0r¥š‚̇^àWlÜ*ˆ\ñ¨”@ •²Xk¡,ël¸‘\tðjT\äH¢EÁE]\lÉê¢ÈE?ý’m¡ /Á¥æ^‰y¬Ï&ê>]ÊQf~dSá…ã-µë ã‘}‘DS溇ÑWhô‡àW7Ô×ã(眫èüâjŽÔ/‡ãñ³pWšŸ€nK¹Fzý èkrö<2ænòROøJýÐJs*µ,Ò¿™.Þ„ÌçÉM³¯œ3®h'ú2™!Ð @°Ò"@C ÂŽˆÁ:Ká.Ç¿ü7ò_˜Y /$©ã¥Õ†`!ð4Ç{h?G§úª²ƒtɱýÏÄ<|ú‘äH@‹í]Ú/œ‰D]Þ½I?Brwi0R¶Ó9+èýÌ*ö”„&c´bmF«JŠû×LU™ TCöA„N)Ì–R©õ§ÌûYu†S}ŒK‘îÊ S µ.T±xiªùÏÔ2ùÿªU F°4)µo8«¨3ªO³ÃL»Ï\‚¿8þHM«º}Ð7Ň\÷žñððnC¸ÆãgÕ2$”˜M!¶µ^h v9ÆóéÛW;cPB9%ÒÊxï ðÿNŒÁ‚z6³ŒãÁ?C. ƒA|T»OÿϾS{‡‘lPº£z .O4Õª^€Z?«—[3ç«gs×hÙdtöEø’(ÿ;0Ö÷}+®›¥ŽC¹_¥Ö¨H½Ò¶YŽñlÃT Ãu7uÞ’øÎì ¬O–OHQµ‰sÓ=˜›vÀ›|…PY£RB›’ÄÑ·)E]ºÔ3,$k:Þ0´™‚‡sÕ@„Ôö#€÷ë…·â äúÖx^}ÕMXœž,ÞÞ+ÕýµªšÉåa<Ù—Ey‹\ž\Ù û0Hÿ£áX']‚9R˜x¾¸´0ý|&9šR¢úy|u_µ/âßá» _ÛöUuç­´ê0Ž•ÔSìÕ«i¦JÀSp'ä;áîKü û²žvŠ®™«ªãQÂÔ²Ó‹›íg6Þw \É(X¬ËNÕ5M}ùqL[LÕÃ)!\.Ì]&ôeÔüÀð è[n‰VâUJ R-Yœâäó{¦hÉ´ø”‚pwK½—è%.>Ë¥–l\Œ5•Zô¥5sYPË{5·-ØÒr4Zò¶¯"½ÊcÄBõº /Ãô¥Û}iaYޝ3\ò¡›Ä ëÑ}FЗ$FŒ\з˜DçÑöÑÉÿ| /#û@_ª³ Àß’ÄãÈ>ЗϠå{rBM%}™¾З}[ëEE|n‚%èÛ³ú úò] xò=:óÆ"Ÿ/ÜYIJ:{Jí ®ðJýéz~á¨3¡Z¢–7E(ôe ôå-W€¾ @_“Cÿfw†å~B«ÁQûB£Š}IÜHËF/¦–C‡} ¢ x6µ[úÒŸ ¯–Æ„[V l'd*°n¶­+ ‘6Ê  -ƒ02yÈd³A3öt Ü3êœ,F2~q­r¥þ@á>cÖ–tQ~ßuGIqôýúrñ©Å@ß=Ä 1nØm"¸RЦæzÿš¸t£s¼•"OH]ôí[óDBô‰ð÷€¾©?ñ'¸*úr"Ìé'‘‰”>Þt€ ôe$}EµŠ›õ²G¥L<”cã}©Ý´ i&¶«ó=Ô)©­!hù"@_ŽåÖ7eNÓ@cÏ€™¤qP0}ÿ]ÌXW¡}eðl“£ÉsÛ%æ‹S} /Ç ¾bDŠºümÐ÷!©W+ã ÚŒÖ}‹@”ô# >3Õ Î5ä6¼6¸З㓀Í:‚ÖYk@_ÞÛ ¯p#sfƲçaü ûÄHØý1³-NÌ´AÛyÒ~uê!€{ÕuD8Ç®PàðBöʈÙႾ̠/)ÿ6Ü{·÷|àËZ¨(è»>Àž Ķ}©\Ǧ/ã oæL赩z(îÌÊQŒÖÚÁâÞÅË 0вÓdÅØÚ¢á5<û~JõYÿ@ëè’›©(ÅÊw>u¹®ŸªÝeº+$öYödƒ+ÛæA`𞆶É9çÙü£ð­Uµým<þÕ@Ò›tÚ)A_À§T?˜þ“œÞ_3å¥n¹^» uÍçPÒ˜€ô]©å™…Ú ÏµÚ/`cžB{H4´”ú_ W[Âx¥Ñ³G†ÆÈú2œjŽô~¡èËÓf|èËS_WXñÌO@_>ƒ'Wn/ú2ÞyE@_J%SˆcüdŸÁ±ãp”Å‹ów†aÍKQŽoàDÊÌÔ{> /çGŽeQéñIý)8…Ãù—t-Æ©×2½àZQl®M‚·P®ÉIû8'ŒoüŸ«ª·1Ï$Qg¬ñx®râ+é]“ò¨øWJ R•¨”ÀbPäÂFO˽PÌ€C™Ùr‚lMrõ’¶æ³~+y‹TÀ/ý=\˜´ɶ¥òc>ÔÍÖTÒÖÌ=‰m+ÈUðƒ^þ*,ÎÓZÎgG;y Œ›;}ŒØzÛÒµxó¸¯Ð=z Ùzʨ“NޝK<^PEâƒõ¸Œ¾3Ô9iÓ lJ|Dé—$º+¶•NŠUÚ_¶õTgáø z´†:J˜‘”ÿ¤]‹æ}[ôc~›ðHn–h@­»~ö%D/Û»Mý¡±¸|‚w6]á€i‰:aíDtW]íú„÷k[«ÐDë£ÐpWõHŒRmˆfR@*‰’n› ˆB¢NZûYl³¯}£?×Z8Cìù“@&½×›Ø\/ nÙÇ¥·HÛeÚiXC-€lÒ诪þˆ¸¥ä¬MW«1ö­vSç3IôkÀðD΀íöNF`£k7·$aòÈQt¶'2µ¸ÅœŽôÌý¦|;­W4b2b„Hm…C½õ×Õ13ø­Ü¤Ó:=¥j…i%’Ê’Ž×¿Pµ‡Þ¢“D!›”Ÿk(RâñJ@P$æc3Äè³,€­Š!«¦2jQzïy*6ù{uŶ6µn_5¶z`rºÌÙN˜aª9žÚXP!ƒ£«Üøû£Ä8]<¬‹¹ÍÐsh"¤-/ ÆtQ‡ApHÀ|Ö¡Øæ_!Ô•6^ã¥mÞÒà­­öãÈ`ÿŒí å 8¯j÷Döi®½Óö]H _ÿ8§¥¿¡"ã&ô]¾ç^Z ú Fx˜˜×ÙS’—¿¥¡ãƒÜ-ÚcŽŒ)Ô·½Êü;|÷B”Œ&¨3ÐÆ¨Ì]‘ ã oDPÀoLZgú ä Úü … ¾¦?N›~ðÿ*Æù*ÿ®öó1‹%2õ+Ÿâ€¿*} ‚9{Ú3hd$¬U < ÐþÏ|s{¦Æ™ŸÔCh·ïá´‡›%(ËqãèšUƒ ÖÀ€º»æ8®(uÔÂÞ!ð¨=ðwƪbc¢e̹ýro¨9ù§ÕAˆ³&`âôBøYXóqÞÝîKs oæ=‚šT9a%A)L¢J¯§Cé_¬jêþF¥kÇÈžå›BìÇ$ê«?!h¯¼§QQQׯ{›hŒñl§oÛát›ÙÎø²¯“™ãÚ–àØdã™/‚jÇâà|m××`.Œƒ!õ{dö;è)gÝ£áG´›MÑßM Ú©•6GƒŒ¢6ŠýrËìHDúJn3‘¯½fØ@¹ÔN|–çkß‘1Ãö ÜßãêªÐá{ÆK"ˆ\ìÈtk©z]¹TJ R¿Ãhƒo޹Q!TT=DeQq-^%@c4P¡æ•@âb¤yÙþêRSÖ–ææ.Ø –õ-\VûÀ@n_¸‘µ·žÜÐn` ³¯£ÁKÅÁ ˆò ~¦ awù¿/žò¶ dJ«0_WÁ~ˆIň֘‹æHJKðîcÿ27)ÉbáOƒQ¢;Ô}!‚¹²9 L÷ƒá7uÏ•[·0~"ð8 íñIŠƒ'ÀÑ ”´KFÿílìiµ››sûØ/ÓP2çɰԓr‰ûûT£Äc@ªm+l¶û$,Eü–¿¤Û… ¬ŸG¼g¡­sm ÉfŸ’UaÓý ¾…àáW(7BRID‰nnN»"¨÷¹n-uvº?§A±¤¬)B^<"û6ù¡ªígÔ¶ÔVݦ6n8 1thžœo0†ÚjCÞûÆò“JP~‰÷ˆÔ0HHó¯áç:ÙpCÿ# §9!¾[Žt+á75¸ ŽÆ/€”$Çsê‹÷7´Œa™Ò±dÎ5\/_; Ç8ÙsÃ$%Õ@MÀþ:Zè^ºn÷éÝØ‘|+CJ¬¼l)¢ô¹0"ÊÏ“ &a™©aJòoÀIŽžSW5e¿ÿå!mYH=Á49åzôZOÕ SQLöõ'rûŒïMB¾{ãyGD:@«swD‰é¢^\K§°hï]¥®Pç1ªz‘“2_®rÎIwUr­þf/‹šåðš9J—ÀD5¢¾‡Rø©-X_£×^‰q›iìõ+ÛåH¨‡š½Îñ¾”õ¡ßWý]íÐp:4<féŒû4NfÓçô8ky4ìƒp—„´‰Â<íBµ T÷C©t!‚û¯cývŒ^àÒ^÷I¹ºjÈø\ ù‰½Œ[Їä«0ö~'I ®ñ6s§­ÂH"•3JÜEq%ão”=%FÝ%Õyå— Ö¿üÒ(ϳQžß9jÏÞÆŒZÂqËeFÛ*цbì`Ûƒ¬,bÎåp·3úkhD0ZúưµKǫĨ”@¥*%Ъ%ú¶êC+™WJ ™%P}›Y€ArÙÀ´Ln¿Î\¸ÿ‹D›úÚ»v˜ÏMЗ*\âFÏ]ü èkR¢ ‘x´A_æ÷MégØlðm÷½Ò/6ô%q‹òGl|$ê $e)Зy4ôe:ÿ0¤ùDt!‘v)' ú4²%Ö¢í‚ /ã3Ÿ =ê9Î,aØ^C#(mc?p*/e°ƒ;yÁ°(= ‚k>½¶}I¯&0¸aã±é±ú’\З~6èKÚå¥f]Uñtë­‡ôoZkÅ”0ï‘–uV eFöjl8{YŒÆ'ä(}ð}ä䂾Œ# /Ýuz£kXIŸúR— 7•¢F„~”èæf^ƒÕw¤¹UGíÈv—®Ýöû=¡ Jô}c‘h$l ×y ÇëcëìÓ<æ¼BÊôÒêP³ÒËÛÙ…î×ð}Ë–S–vœÐ""ކՠΉ¼‹¸–Öm+’4›îØ“7˜³@IDATë«!‡öFæ0E½µà(@ÏaŽ#h8¯áRÜû@_FƒtY#@_J èËÔ³êvXˆ2n7ýï&äg5w.&3ïÄȧ<µÔÕHã›-ÐWA&‘à‰€¾Ìãi”;ï]Ð×Ô cLD¹Àu²ÚG®£:PF’`ú>©Õ!59[ùÔƒ¬…ç±íéf” @_2 ¨¾ENÎø@_>ù ô%Uˆm^@_¢«‚zꢮU‚¾§˜J"J1w²@_ößkóÓ´dßÍZñ+µ9Àh‘$dŸЗyR:r,¤ì£ø[@_½fnÑk”΃®Ö&ô™z—±â}ù&ô¥£¹ oú$ôƒ­˜h:ò»U»¦°o¤vÒî‚¿ºWþ¿ôåÍÐô¥›ýd2³.˜ÒèÙš}©ÛúyÅÿž zÃ@Ú9u&™< }çCŒ˜ @_Æ£Zô¥?A_ÎTd@_‚Øfþ~aœØ÷ìuÇwŽGbùÿ :iôe8u×~™8Že¨pþ» tôíÄE‘Zv!xb ê a”¢ŽövhºîjB|±â7#ž±Gxªg¸ýŒ}" ôe~×åæ…ó ¿?*ä(MP_N]¼5û´šÇµÑT2Í\êé‘¿:8yǸç@EÌÜÌ9z âø"´&òZJn¬ël¸÷Çš…à~S¨i©šò¤JšJ TJ R•¨”@¥*%à)±l q¡ÄcÛ$J¢RÇ`K–rĵ)yS'šõ+–ž øU¬E¢èé<#ᘛû­4A¢Ê›ä kû‹û@Ò‡÷£±1Ëõ¼ÿ›÷䀨§®Â>âFèMJ!IÙq|€ë~¨H¦NP|3â&ÜrVmeé`+Þê¸Ñ[†jËÇ)‚Üâþö‹¤ Ф´Óœ¥ô°­ØÇWÅ?¼BW¤Ð~g°~\j]!ýTJ¦îPZëο/\"£Á´Zñ‰_¹i•ö)~wP°9”T€ð1Ú³%Í/ør>¤ùDw©­²„’8Ûé$ ¯ÏZ’U¼/¦FÃU%ÀøMPç`ÓMHJ„ªZhØgZ‰Ñ‚EÔйÞ™mínl9b’ÍöAè@-F¬GnQ§(¦Ö+œ¤ÁÙjÕPH#pÕ¥ÖÈ?£oŒz†hnÇ%d'DÝ–À29ŽÑÂ0àiÖÆöA?š€o¤žL‚ IDÁ¦ª+“ð$D T©¬ãN×ÿk@{¢Rj2‰LïŠØ1—æÆ¸ÿP}“Ÿ¤6ʦó@©)B†t¿hAe1»;ã?Ÿ¢¡¶òïX£Að¼hÃ]öA“Ú¼®¯Mùë htzõÖxµql j¥îEê㡜&ÑÏo°!YËV±9t  £ÜE×zŸÃiÉ6|¨£8/?Yhqõ 놙䯫kU÷ˆ;’cY™1P1›Õ<¦¢”Cî­S8ô wXRƒ’ˆOç öéI˜ç|Ký H{³:[\b;åxNú*s&þk°^ú cà }R`÷´u ëÈIý¯:jõ=VŠ^–ÛçŒú/´É~™!ÉIs7£¼b…’¬uó£,ÛI†™©¸d¹Íòjó’ŽD0‘'‹öÀXƺ¸ úÔ¿àÿ<$z#éù™è)uVZf%ôË%ka„­J­æÃ©ÍÑ·jM+%‚mFëÖž+_FýR:<¢éêþÜUÑ­Ç5j2FØ09Eë˜OŸê‰yíš½:Ýg«3R=ÕJÎJT•–ÙA¢Ä®¡®Ì©€íµI,¢u3ÊjÇ*÷B̸Ì({Ašz¤oE•‚•,tRmмðÎh /¦C^¢@ÿÜùÛeNÆ*û%¹/Ô"íÊ5{Ý^P3V±+çqqƒÃ ÒÂÌ’³ÑõÞ-ü?ŒE7BïýwZêWÔò¬Ž|¨‹ÛGûá›î7…*ÀoSJ­’¦R•¨”@¥*%Pmü+ÞÉ%ð†0’ÓBæ"@Žÿþ !ûˆ$q6†^²˜VÂÙpS'¡HÂZA1'%|Dh´¾L¢^3ê µônÙ ÙÇc¹ø¤„7Bö·Ò/I²ŠèA²‰p↑uaòHé:ØÜÐ  Xê®g•?Aêør8|<6Pí4à#>hs±nƒHî.¢Å_® , +ñKºÜsIžïú»§a¢dúAÓ¼ÁÁ–ÓFOÑ1Û˜w"PÁ²¦„1™ ¶ô0­Pz®\b9Ø @Ò7ØJ Ë¿­ö-"™¤(z¡®jJ }° ?A¶8­‚Íðfq/Ï貕 ƒÍ!ÇÒ`Šp¼:ú„žÁ˜Ã>f6Ü’ºgÎSç€á2&[™” ƒcŸ’õz–K‰)ÑK)nˆŽ©Ë@êõ`œhSv¨ÛQˆã¥¿–ƒÇOÀWv ¶ÐNè‡ãÛ %n…NOE€ýØFÂv•½å¼ |L;¥¤‘oS‡z?C¬Ò4eÎþ±9žÈo`›$ý w€i¢/\¤• ‚ P*’ÜŒžQt³›>$Ѝ]ÁHÚpfäãÖ÷Yer Ÿbƒ8Šm¤þVÕiØ&ŸB帘Dn¦ úéïÌ¿ýÎ5åOI9Ûþ|“ul²Ü”BÉ´U.S:üŽÔj'ª†™|З€}Mµ*š7Áò$†OãB¾þÈ(}‡Ñ‘®?àÈúϘ+‘.E?HUP›Ox†ÔìvŸ«·n f&H…›ˆJù€oÎß³­tGà¨%fLM2Hïd\hOwz¡ŽQCµêÕºýt[Hž¿»H.á•ê «ʵîÐÐ_;Ú?¿î~äø•½wfs‰J²Y 3Œýõ4wuùš“}êû†þ_YÒÌ¡§íHï‚;Œqs†ÀôáÓÎŽfϯ”æ‘'æÎÞ¦Zû„Y,fÙ7«Ô˜¹o´› Ð4`Z»«N/vH8W².xú¤3¤²OB<Žc‹ùmdÜõ†¡0¡^ M~6·ù·€Å_¢ëm}»‘ÙySê µljµbîÆ0æ7aß½ð m×ò œ ½½Ì—dâý/pŸQdgÊLŠeJ ÷vÄŒ½õˆ…ûnXGI:äÝø×Xg²Ë’§òX_€9ÃÓ ç©¡eñ=œ'þŒ5„MÿE?ãš:™˜JV*k©Á OÈÞÆNËþ1'Ó ^ÝÔèÏáÎHécñ;ÅŽ^ànÜ®¨ yÅ£R•¨”@¥*%Ðz%àYº´ÞÃZ(çb›ÄzD‹g#R³-žñ"Ì[D»½”’†´_Û«m±¶IØâ'†×¼øHëÌóX~·ʤ2WêîkƒÔ H—¸¸¤„PS$XݼŠÝ·G Af<²Ý-ß³¥)©¾Á6´FÚQâsXLj…f/"˜ÚlcÍSX*\ô™&ûÉÆXˆøìŸ%ToˆºÔŠÁ=»ì×.cJÕ”Cϵ}_GËá>#{åäãÆ™”…]&ŒÃöÊ žÐ¬ï$PA&-xûéKí-JœÈ×ßwª?. €Ë§3¡Ì›öq;Lý‡q™÷òÜÑ7ÖB»li]Z ''€0%ð¥ÌF‘;%´t?Î^ެ·¤¶,Ã+|ö…$Dý7‚>"H|Àޱx[»òn\‚RvDVƒ`A$Èéͦ¤±…ó¨8³¥ÑÚáú˜,Ÿ—^R’hcq{â9QXVAåÀ}*H?áçJ6s“»jl„ÕQcíðñT'íIÉr~ÇãáØøNÀwè”É-8Jû(¶Ê¬ê¶$³IˆiØîØÎŸò”0¹Ô@îÁ hmsMm QÃëØÜßaP‰p™¡[øè댑–úûB{QRœ A­¦â]ŠKwuРþÃÁû, >É“Ʊì4xЦ8Àâæݳw|ݖ颢¨ Êk0ÞiÕ1Y—ïŒXNk@Oyc£nRûê¸sðÿŒÕ®ì (Ýhûj^Àx S!s©º#%é’ˆe%P3>kg'êqÍUí ñ£+€—êG¢Ûêðî[©åòA»@ÈÑÑŽŽ¶æÝùâ–ö{1Yî…´†nÙ®?fMôm—àh£QùÇ}ŸžÇæ±p°p]AyeÝÇ­Ÿ¡$|öz}ïû›ÏÜq©áÕL$Y‡á»I©Ükš1(ã•ÊÞ¡ýí?m?m.½iÜ+¸Ó'Ò{àåÐÊÙ‹ÓÍ] ¬»´ª£"ê#L6×Z DÛæQ,ÓÔŸ­Û‰Ðu<¿9fÅHv¢NÒG!xãX”.bŽá÷æe|7³£x;4øvÎEû¢¼ã€ÐÿP²Ý±fɈØsÀŸÓ»HD$ '½ ƒpâ$ x}P”O<37Gý1P÷Ãü]z ú|¤16^°æÒð…GeÌnl3!KÉïsGå˜öÏÝ­ÎÉÖÌ;}#ƒ¶ I«êúÀ€Ù¡g¡c]ŒîÒææ£5_aÌâ>¦‚8™C‹GnD(ç/QÕDÇbtuHøvÂàLŒU=¹Z®ºFÄ{)(³!–45WlRÀäHõ€AÊ`ÌLxWÎ§í³˜YS]ÕÁh_±µ!%ænHHi¼+ÀoÑâY|ÅâââóF•7©”@¥*%Ðú%à­ÿÄÒO°¹¿¥cÿ:bø8Ë¿Ž7Þò8›Ú^¸(wÉÖMÆ0±D.°"7R-E±9»*]¼›ëA>¾V°v&}»ň› .Æ)¥Ìck.Žà¥htI¨À;—JIæPâÓ&ªÅð½!û߆‹Þoí›Çà…j-7ÕQ”"–}ÑÒ4²’ª:®¯³Ëá‘Ù¤œËó·uZÚ)XjÃÕôÇÛ@_Nþ*“ÓV3îrÿ©† 5ˆ}—äJ0ßòþ»[-“`×ý®X6éJɆ¶íb9lIgÏQ²q.&Ý€*ØG9Pj+ à+bïÑx¶ÎšDýÇù©=õ=h[ß)=Ê=ƒ­ÝÏê&l¥Þ\ Ê·Û¬²ÈÁÄvIº*`œPúGèbMÃ7Ž·  -I³Î©´zUÖ'Ç›1è_ã2§j`ˆ °Œü]0˜Ï»Þj‡} „ 7%ËùmážÐæa½9¥d¢¶´Þæ?ú5‡„$ˆH¢aêv‰í|¯”1ÉþôßSXU?)-‘™NgׯÊTKAZ÷tеzÇ[ß² ‘!¨Õ1¤V+ˆòyøLµCy¦Õ|_H½½€úáp ÄÔn®ïåïJHžÚ ñ÷] ‚Gd1¤´Ôn¶Eq ]ÒÞŒnÓ§ "l€Q_†.žÆwä z•ùí*ñ—êe"äßÅä|òx¥D‚ä઺=ÑF¤Ž“ãiEi²ª?Â<×ól{ާ”=é³ô"”êfýÔÖ±SQ H鿆G·›‹ÑÖ2þV Õà`áÓ&€Ü[g±T tÕì pKOguIôÆj¶`õƒ6ñ âîb²yôú}Ю9^™ñiš¶ŽÑàiõA›{Â11þŒ¹`(ŽH–¸E˜ÖU> ×£¤U”^¶XU7á>¯B °ú>0ÿL¼c)%œ„{ÕfL茫DN¸0z;TX^F/C[ Pfy®2Þ—`}==dXtSãÓÛê1ò&Œ}d•€w&ÇóÊ(ç¹8uDÐöR¬éhXt æ•¡\ߥÆ(±„šÕæ uRî{ø¿£®ÊRÇe~kª×ÿ¢„LhJ’Ýà1ÁïP%DúHE#¹ƒTrÙªþÍè‰ú¹ é]dm&ºí%^W£ú‚R”}Öãÿ0”¦uÔ#™ÓBÐ’-BÙó[$fÂ2UM÷`ÝÙõ*-–½êGüÞ`ÿj¸P¹±mÊ0P™¶\êÐÝêhwO"iá:ÚÎ:šùvЧ4š@\5˜YÉI¼ÓN;iŸQ£F9!¸8üð¿ŠG¥*%P)EYÇ[ ¬àÁÅÆ­Eùn¾gñÈnL©â÷«(n¹d™ö«xá~IJ+ð`’Oâ %E)ä_&øá. íï#@bÀ%Û·én.Ͳߟ ÐĽùãý’¾Ãm)á§‘§RDc9”Dã±µ˜ƒ“ò¡”ˆÄ£”Ì‹?ð?‹cy] ¡Qê-(½'Ç™óäÖ“m€í}Q%Mô¦º-Ï…$¦+ERü]¸=áÖ¤éDã/Ô}Êœ®Ä»ØjGÊʵæœ$Þ½¬¨åFâFšaP9€âuÜ.ÖwBkðx%± "0¯^h#±¡³ë–àñØàm )«#5 ÚA½žNArv>€Öïqhÿ8m¯ìpê4<`Ò$¢~b-™L½«ØÜ]ˆº¸uá¶_ê(þÀäGøŽÅ”J%@Éùé&€wÅ=AøvJ݈úì –D°˜RC.½ ‰ÝM,‰q2|âã=á­·Ë'%–Eãæò8€ðÜ ðMbhÞŠúÈ“TÓ0¼"ؾ03€â(‰vèé Âd½ðLÑUNõ:!°9ñ‡…ñ\ ÒèeÔÿ¡hc¡ ÜÓLCimû{Ü|ìû>x»ïPîÏf–ÓÒ—‹lÉV;®që/+ð&cðl€O†U¶>¾êø„ºòµÚ¤6¯ÿü¿‚<ÄC[•‡¤š6~V©Ë†Ó$H³Iæâÿ¦TÆóžKt#pWšÚA&î¶ÔtuÞÛ€wë!ÑG¥–ˆÁöxÚîQùp}[KìCö=ÿŸä8©ä?B‚oÉüËê?èWBdÞPUÌè3Fÿí²"0H³h©Ø±yíþ­ ׄð®i‚<¦çÏ…Dò¾Ùo›dèôu¬þ ×&Ükà“qU‡QEþ ;Z3ÝîÈÜ%ÈÀb$ †˜÷%,…q⟋ºÿ'êÃóÝx±ûê‡ñ¬,¯ÕòÕW«ëW#0£ 0?‚9À}Þ é5UŸÜdÌI]Ô»Í!ÕÍñðgHðv†·'PîdS²ú½7¦×Sïçÿ§.¨¥®«?O­™ÿ@Ûd XújÐæ¨gþI<«¾ŸëöÕkð½gbœ&ðËþƒI3©{r×cl`x!åÛÞ©ö_xNWD«KBàûcus›VNP˜¦ž†â·Ùľò_€Þ6CÅÿÅÜTUÆÉ¡Æ¼ûÍ®˜'h(ŽDclóP?ÏcÍKuE$Î=}ÒíÕ¨;ꇶ¾w‘y_'(ãÏ?;&¤ª¶Ñ–ŒqÊ(<9+®J TJà÷TœŽ+´èJ µ@ßo-½e‹îk~ßOŠ/uZ¶,¸¥X\ˆ›`aÉXƯPgg$çQÊ Û/ úòíWĉäûú·$èËübººpoKðñtc@_Jâ¸&èÔÚc½]ìÜøÙ /%%mŠäyTpüبyØïÌí®OnÄ€v.fÁínÈc$6:ópå&Ä,ÃãéxGƒ8$ʇ-Ô·öÀóäÆs—„LÒ -é›sIZ¿)ØX”Êum¨ȳÓ^„Ml)ƒWÌé^9¢ŽPY%©蛜z -Û ß™MSQ¤ePOãÖ¿WÁo[Œ÷W€¬± ›‚­2¥%y ÷p'b3FéÒu¡‹RÏ¢–Œ¦ ç=Ø ›ãðµØÌ-Pýp¼öv€ÀI:0çïÅg u¶üdœ¸€/õ-kÐ Ó;h{Ü,ºôxB˜0êîê‰÷€Dªõà¶“}°'ô§RêŒÛû1x_É›3ãô%, /[‹}ÿWœún”)u”ö¶ÊZÇLï& ä"Á^™-ØßÌ8P úmydÙIÉéÏa&pIdÓ?扣Á€¾§aó=,oUú2núòÆ} _óÝÉä!½ŒúæC)ê±y/}ÓÇ"´$x‹EÌ‹4€ ™OpV$J“@ßK±I×T5L_s¾Ÿ‡Ñø÷! #™oæ8z{Àè ‰á}Ïoôe¯n暆:jh®Áÿ>7s¾ôe„ŸË}ûêÔ×êpRt[ê©îms½C’r= ëÙ±=S穪êg|¡FÂK”ØO} S:Shtjªú/â è+ÆRyz€Lú2ö·’ãF08ÎÒ„B¯">:ÏeÅ(š¹–@ûYÏjç¶!ÊXû&Õ[ ³A_öûËÒ;ªcÐî£ê(Ž¹Ô‰kƒ¾îøkg[ÔmK:§vA›ëmEŸ7 †SÄž³§†Áj<£ÿ$Зú`‡ î}s¾­fBµ}7ÊŒ®ôe¯'MW3Q¯Û¦VÁZ”Ú‰ÿ^„1XSú$ÄÛR;©ª`“ª#4Óë®0ò¦¾sPî'Hã¦40Êþy@_2OÊ}¤.Àx¸}íÞjr~J`ˆwÉôeÆ §.ô-PdîqJÀpJúHµBjGµãUŒ²€Iû#Æè* 4ÆÂxSØÑÈA/Î « -¨-IÕ#0Fv+ˆv$¾É%öªAXì(½uÂ+­œà_Ú› ú26OØLÇõ]K§;çž)ÙñÿÐF€¾“ÃS…ïý ƒCš=Ìq—ʨq7Iå¾R•ø-”@Äûû-|Mã¾ák±×¸”‹_ìeõñ¯æ¿WKëxõmþ[þösˆ¶¿ü·î׈…JÒÛÒ0ŽKäX“|ÝNquÔ™¨¿È¿/ô}C±cEÅâøÂVáGã“-‰ç;mçAƒQ6}87\pr¬'èÔZd×#k;‚ ãOüïdàã–Çwævw”ƒèU“ÁöKK}ŠÜWl m€‹ô ~®Ú úÓÀÕc(AŸ¾C['´>ÂÎ6U?Þ½ÌD/ú–vp®2r•ÉqÅê¼H Ù1»à&®‡Tic>ví®ºªÀK<^C}õøô¸nQâÓø«m¨‡©yÖ)‹g=»™´+øÚzZgàJš†M’ ôiOÕ€ÛÚªGªFõF³ (ÕEÐø(XUç;œMÑfî 6õ ÐŽ†g–X“25`ºs²™¼ãÿ|W…ŠÝÎ%öD¨‡`/5jÞÕïŶKb» 7v–„’@ùvغ¬jØ©ÿñ"l/·aÃ9*£ØÏ¤„ŸÚásX½c‚yÏœ"³…R÷„ÚùŽlïb¤†:Jµ$|Õm!­ð£³qâŸÆÌöÎQÝu‹Q82îl)3I@†ŒÛ÷iÊf1î•pXò°¯W¤¥þsa32søvIênOˆ©øî‘(ecLÕ‡Ä2×SÐVu(~׊·;ÚRô$HIúQôxšÛ‚ÿ¾ü6œ@óVµo‰ù—ã‰eylδbÌR Rè65%¾ÕÉñgÊNîöª¾C­cjTue—x9À2wéÄ ɉêèÚ>±(‘ŒvÌÛº Æ=„sZÃÙÆ8 è[$:Y//XþöU*c ŒÌ€pò$Z» ¶» ›A…éuUÏ\Ô“hë°vãÉA sÚ2Çýmn$ÜÛªh ‘I ýŽ?%Þ5¡Jû“¸,ÑÁwd{&Õ¯U‘¸GþûIÿ«º‘ÖÔQËú£í¶cц‡¢ ŸÂï+Gv™Åé=O–Ë¥—0޲.|íMµyV½Í1.s:Äb{û³¬ù7œ¨ŽKÀΖ(Šz,ûgîfxdÕ¦éãa¸­ƒZ¯îEuV'é²Ä(‚vó}º«º ½´ú,è﫦6ÂûVi€{+ ä*5³×äܵIèq\‹º’½ŒÜŸ )‡Ø^Ý~(Q~ÝââëšÄw©h¹‘ƒûh‹"”©¢¸W¥*%P)ßD Üo-ö~ÔQlÃДìeQД´•4¿îèRâõg ¿ÁÚP‰¨à¦sq"+Â$¤Žp ¸û·g|’ i˜™å ¤°«{uÙø!ÁgË”åQt7.Qò™ßD”b*‘üc<.Ä%'5ô,B´F-ÄãîÓ±f.xy¸³q8-~$-¯ÝQ.6uµo7¿küX?¶šè4°N²ð–V¼ ¨þàÐk l´I”ä ¶Ûú^Ê[ßxþó´;eYÈ <Ë>!´56€¯T]›ÖbÔ€rh]_(=è½d;´Û)A@( ¹(¬»q‘g.‘;ÒWöj* Ï á&ùlÖ©‡q[8¨‹YhL‰‚~l£Ýñ¾¢ïù.lêîÈ&Ûg¸ñ–@"y&âÑh’ÝnLk`â£K(§{Q—”LÞ!E°8/îÇ»K‚z›†w$U£ŒE·'TU»öç&ÔÍMâ?óÝÁ¬ GRZçã}/À·ïwÃQy+>"ÝA«Qa;ÚeD£‹Ô#Iª ¡އ [ÝÔÔzÙaÚ?ö—ÚãCÊ«˜ñ¤|DG¹ÑUhrØ®æÅXV¼iÌ)©åŸcíFŽˆ…t®]A%ÆÙž©„5ylÕa"i?ø¼i„a#…£ßšRÛ‡¡wmˆs23þˆ²>!ŒC){MUWã‚ñ9ͰøØö\Ь²¢º}Ôj¾¾Ûp¦~õ°ÉT´…€È`™—"£$ž´k¹/¼ND£hvø´_’™o²ƒ"wÍsp³ö\ZW{5‚b½ªiöWÚ@BR0 Jyâ~SÔóX  ‘¾¯3©?JíwÚ´ŸóGµ gõ蛇ö @~Æ™Œ±É'©Gc`¶áQûCÒÐï–ÑsïGù:‹öQ*c„VÔK}ôÆ «a íô‹°®ú!ÙdIZòúQ#âBH 7œS2.#‚–¾“ÊKPv-kÛ*¥‘¦†3ô&_Ò”}…BA?9öï¦5õ…Òƒ^L¶C»~甿›¶Eî¡»ï À¢€ÒW´`Þó%ö>Ͱè4¤ô9·Q÷Hl£6Lp46uNI# ܺ$ÒŒâïn ô:^Óx‰u%R¥‡ÊÆž.¾ zŒçšcù%›©ƒV(i8y? &Ú€Tõ!´ç‘ؘ#¿ïaü? òOeº¨nL¿¾—À€ç­,É?U}9 ê_ y•Z½dõßÐæ%ýj7Œ-dô²u/ÜØŽÞ(wÄæ1gÖñ–4œ°bÞäŸvãˆ6™÷€<Ö>×z* Ç=‰¾Éö¼-‰eµuýea Ÿ Ñ˜ÛŽ«Ææ=J&Qþ %à÷ß°™Iì—䔲ÿµk=¼˜?4Xa·V0?ñZ©%%9®ŸÙbŒÄt_+/cøS믾+æßœ›Öæ5+›õµÊ—= ƒÕGÏg–RuV· bIO¶ré𮹩y á{(ÛqŸ<»Qúõ¯Ùhfvëí ´Ú‘:º7Èœ£ŸC]ºÔ“~ÚÙ˜K…U_ ÖPÅôͯ¬ÎÉ%ÁFz5¸[Î9ÝÇy^ ¾›% 8S‚qon0–(¶é–àd[fï´ ²>ˆ¶Rjo8}€y#îJÖÉ•Üמúoýÿ”A)£¯Q¥PûÞç[ ÆÓ‚6ŸÂÎ‹ÊØ_-èáõVé}½þô¨#D\ű|œ{2Ësµz†m„1­€ÛwUzߺÍÞ­%”ßA;ä 2©BæNÌîT!Âyc)”ß\ôY2ìçB_°J³=*5ã ‰úÛ¹Ö½#w-þ?¢WH½2§nŒ²P[á×€é˜%óÔ/˜?Þ N„\ú0 #$À8_‚ɱ¿5ÏP*•cx"Aº1$sAcÒ´nÜÙêËÜè²N3 s†'dÇåg¿žd~¦±ì¯lF™]·s«.l)dv4Çú4™{·£yÖ>x;ú2çttÌÀ3¸6;ë˜9±“c|ö"3’ä3î¶÷×uNÖ•ÛJ TJ R-S#WŠ6H’ã¯Í¸›¼wK\+†âZ¢‹çÁ3K­E<Âý[—<çbù—üæ•[=ž‹š¸©¢>NW2‡’a‘Åmÿ[ñ8%Yl"pÕ‚²\vÖ‹›òcÜ‘¸y†‰ ιe©#8\`wµ$»Ü¶CnÔÛ¸ÊW6RÜ€ï]( Øí†[î™|µûDí·`}lÒ—P{›+JpPjS‰Ò”MÝL}i´U=G )‘¹)6²ؤ©¯K'õó n(}v…ÐQ"ªX;¸›~׈ wä¼õL=¼”¼èGPζz’Âç„|¿Ð» >Ò¦©‡v;ÈÕR:‘~”†~Óéë¾ìyä»$q;XRA¡ °Jf87•<¢: >¾ñ–r~ó¨g3ÿŽyDÕuºú‡ë 7‘—Ô< þSwôÜú™DÔ7IÕ ¯TQß5ü=4P÷ÊXÚq˜iààwS%œ£>©’å|ÀN«ç_2õY}7ëèjìœAéçƒÑ‡XçˆD^ðìëð,QA¯%7uÀv˜Rt4=7@%;mš€6¿® HëÀuðÿ‰M»kp”½®€[­*Õ> ‹õ¡êqh¼øh€åÇð.r°Þ…ùÒEø?b(@Y_›_R=›ªW»nAÃFåë$ÓE€Ðø»ºwëÂcBèI½ºç£<4P^ÙùóÂøå9z"ÚÄ0*íb”«"í,”¥Qfæq C[<mqSèI’¿ ækãAÂîxÊÐT÷À§0˜ó·êväÍú¡j„‘?gaÜ"ƒË¦=Sª§òãm¯D÷&˜w(1í#®¡îc°3⸪ìµ ѵE/„óV[í!A=ŸïÜzDØúFŒ÷"ßzOJΙbÅz³’§×X–¾ÓBÏ7ß‘‰8NkþJ©6obzÙB¢7úJ¬à`¬-É<ŠØLâS¹VJ R•¨”Àb]9V³XH+¾·ÜÐ6•Zôå;-î o(µÒÄì‚t\¤¶q;RŒ~D`K€w|7GË%.Ê}U)Зù» /ý¨“´9D “[Q—Ü¢ š"8B@” ¸ìþJÆË²ˆï+K_ZЗ᳃HTq ÆzôeÐQ°´L"pdë7o©v³ÞŸä-ùëh`Û2õ3—}¬ŽÎRÿ¹ºèlЗ›»ÆA_nÙvIm„ÿ5ä4n¥AßÌЯ|{Nœ(cË}–°e*ôå}R;IA‚¾ÜŒÚ†çæâþ+lNûaóL”´¡‘ôå&v«˜ò > ›Ø” ¤§ŠEc “ÞEi:‚¾<ÚOæF1Ðw €x!þ+tùÚDðÐ, ‘$z€˜ úNÏl&1’jÕZÊ\KÖãÈ~G„£G70(‚où¸î€ qP€›ÐµQo¤wpq«úsÕi$ºÀ„¯Í=޾õX º‡ /êmªQ ò3Âúܦþd­_Xò:@›Kö¸ a”Z&8á,ゾwà¿Ö‚§¦€À¡Cô¥Ñ¹]!Ao7潩ûè‘ìžn¯å²’Ú§/ñ ÆÀü+¸‹ÆùËG~ÐW©•ýÜ×j†ÉLú2#}„Ý—#%»S*¦¶ ˆF©yEÒœ(ÿôI¾(Ú¯{‘1È»;óÏ !èËl\æËðóÏú¡Iá; ö¾ÞY¶ÃB56_(tÂ|¨‡W«"Cª;¸Þ"èKz;;8úf΃ïÚ:,ú+d(ØRÌߢÏDzˆ zÇûçýhç$êè&㘤¥ÏÛ«(Y«ª‡k?þ1uŸªaøg9’]ÑNu·Ž ‡=/õW„â9‰}äaTmžC{«8èËŒúÖü[g•××ômûvá£R›ÃoIãŸwË £ê'‰Ä8©6,§ËÝŠ©õWO„j¿¨áb´M‚už+ë;9¼7g$X®¡i@wyôŸ¨oôåG³ÙQêÁü§jTCü7õFÆõ/æ€H†NÅv`þ¨«ÔÆ8ß±^;˜q誰£þïý1Ö,ïôu¾O9 ïqë÷ÅøöÆzž$ò×P›aüÐזʶ×NTI¾Ì óS³A_ôÀš'Ã×òKü¡#@ú0¡vDk˜¸©;2;’hCE€þd«¿$Ť?At2g]ЗóÉŽh7Böœ-~æ@_@ß$}ß&Bñb÷YeF•Of”(ž®Z)J TJ R‹¨ìIa=òWù.¼&%ÑîX°ÄF‹ª2(ÙIzíûk ‰šçò8}úÏ©h£eB¢ÿ¯ÐÆv¨yhW2 À`»]Ö‘u2зì{³‚©?÷ˆ¸Žº\»ÂcKúšºƒmÀj¨7’ŒB ÆÎ$p¯˜5ÀoiÀ‡z)·äáy)ÞK/¥µß ñùÌZj£@šøütG¨ôˆ]óQ~ŸâGâ&õîh:õ"€ìÈ_¤Èèfü—qS²V¨]*¥6ÑÀêDñRcÀ¢ÛŒ Cñ\~Ï@ˆß}NJm|>ŸK¿“¹e_¥ž Ò2®‘þE¿ñ´ç—©cŽ´I¶‰ã7³ü¨ bU§ORÒ˜ïa`¾boã„QïlõÍŽgÓn þwnP2±=±ø½®ÊF[¿@êÙMŒòÏÝáz†÷Ô7D Ø?m¡/X4èº Y•¤ê«íšRÒPÛQa®Ïl]5}lèÇÓ¶qÅ0`ÞŠê¾t¼?H™ŒZý¥èó£¡CÝ? J|}Í^Õ ?…êebaÖÍò¶J‡üzl¦~nM gY1•:$`À°¼…q¬M6ˆë„qB¹j2´t½Ì‰mÔŽ¹¨ž. ÆQ•Ï kIçF†»ªr¨Ý6öNEoêvÃI4üb£òÂM=ɸN zƯ8au9o)OË$ƒÀ«£¾OjÝÅ¥Ò»!O² èÖ}[¤K ä‘d EÆîÉ(¯ è?"ˆÃÓ 4ìÆ7¾šà*X©¾ŒºÕ`Э#^^Q?,‰cY?ܽ2g™¿Culûˆ:&…÷hLÀÜsÂÅŸ'4\/Aá•zþKµ¾ZÎÔºªïôð8øÂ‚df\Àœ“9a]P†7Ä)×c/H1s]“U½À >µH2€ÏE zk~±úÑñ˜Tç6³Ê?ŒÚKIZà1%ûýk½ƒ mKÚ0$¹¿xÏ¥¤µÐãhÇ£óµzÓ³tqsL¤$ööõ0}Žº¯Å\žÑ *Õš\ ðx$òßÃéjèÉ?®vwÌgœPÄí>‡Z"Sßd uÔú˿DžWêù—9˜°µ»?a+=$½„ž7t¢ô6ú"yŠŒÇ$·OEí1«/‘èê*¼so>äøÄù¢ÉDà;µa™É#@Ý› ; Þþ>¶MX_˜2ûYÉßà #4œ:mõürN†¨„qàßšä>)_Æ]¿Ç°'¡}›|ë9;¼±î ðÛØ«Ä¯”@#J@†HR‰Z)J ,F%à;&Oëì¿gòJÇXÒîb`¦Õ뤑E˜cðÞHMð<ÜYŒ×½=€ bK‘ ØIžrL¸¯³àãÑxª;*\Jšc®a!‰Ï+Sè¸ß`«°ãÐM0É%‘vÿrÔ“¸Rq’–W‘Œ±ý\w‰%},úÝA½ˆÁ®ÆÀ| ¼(ñZ.0”ªþ‘˜”åM  UÝRÏÖí·°ûÌî;ÇÜ `mchTµ–€öµ?æó9Ø _@!BcÁpyg³UY9­ãy-À‡©HšÇ¡,/¡!øËMu÷š‘êOØhBN(¤Ñ„çh”Ÿh¿g@Ršh’”¹$^W­¤ó><ÆÜÀ¦jÊ¥WFÒñùø®t>U¨éeu_¼Ð‘tþ6²Ñ¦50CU;4̲«³Ÿü³Ì@½‰¤”°ÔS§Ü 51{†¬>Ä‘ß s?(ªô ]@ç_h\æX€FK Ï!Ø¨Ž¶6Ñ=±q~÷|€Òd<ÚJ•¤O,Ø7‰ÉF5'›¤þ¬7ÏLã‡]¢ô÷Ûe`|‹ÿSÿ0Ák¡/Ðb¦i™Cñ1WêÐdµ ˜¢­Ý‰eêg]܉r‘#üñ”Ñ0ҵ/å.~ñ+-©­ã^Þ»åÕBÀ¡¤gwÔ•ªÛ³ ^á¨_%ô  Äùä>”©;Gˆò•¥²JrpBˆÛ.X§¹Å‚Ô¯c¼Ê¿ `ëy„Tk ØøYwšÊe‡¤S©´*µ"Åæ%ª €$® á6¥¬¿ËþdǬºNQÒŸcU"´Þ2½½'¸»Ç¯ï§µ>–wùNß¡úR©0_Hyú¢Š¡:_X䇕$ð›K› ïñF†$éL0’˜tÓÎy5‰¸®yíìºt4¾&Å-åÏš´æn¯›Â¼*ÆÝ¢øÕ;8aš¡lñú”‡–[¼Þ§±os Y+{“ý–¿ÍûÁ­ìY1îÖÊìÉžD² ñÿæ¼Úã‹°Í÷Oˆž¯]ÆUêá+}$Óóò¿€dDÂcÏå<žÆ/hdˆ›“Ç&È•Òô•“O9qZ#ÏržkÇY7"µaû»n‚X?¢•™~bIdí C’ñ¥Xä&ÜpßÏxÐa†ðhêÇNo"HDШ5ˆàÓd€×¥Ê 8u½’¸í­¯º^ ËžŸê`‰œÚ ƒÂã:~kÿQÿâ…TóO€5û„·¶Aºk %´Â(%“qttlVä>ÛÝê(»xöGèo²-!óf#ôÁ1xçË\·ÛÌEj½ìe¡ÞV_¼â~ë#ïRDÝ“ù'« 6¥ß©÷н[ @$Z)=;ÞÃÔGH|ù]#CòJaQw8§F·‰.JˆÎM µÈ ˆ¶' Ù§ÐŠÉøúV"´}Wµ]¸ à’€ÚOƒNÅâàuIž¶Blí½bÿ ó Œ/ÍصÔ?Pÿw5Ú…œÞ`›¸%pWƒÿçA[e¸§)x2à§Ë`s»5m~eÈ›¥ûe B/µѰ {“­ŸR'Ë÷V=òÏi©C‘ô¤ÜKhaêj›ü{êÏbÖ1ÄÒ Áºª›¤œ„Ä/„îÜwï?BFÆ`fe:~÷Œ ÞV©$ j¥f©¡hËãêæ/ÁcH #^Í=5c <-ƒç¡¿æÿSìqÞ0³^Y#›Ë«mSß(&z£xR‡4µ9K›+ˆÝñob~ñ•’=¶r=  ›ÌÔsÜÌæ˜„#F…Fˆ@`¤ÑãUM”%E*©£ëžxÇï0EŒVÖ|]’öý0bÛÑ€=®T) D`Iƒ™Áêðì%êÞÌrjµìôDIB¦ãHt ¾Á6äæ3,¥ªïB{;2¹&¾O ™8—ƒadƒž*s$(7ǧ`¾ ó'æ…w4„2M­„²{*ŒQÜAFâWÅ£”åi…1hR×%¢{ƒ_Â÷nœèŒsKßSgpö—[Ì!, ì$ƒf"žÇ½Ç”l›1꡺£Õßò_£üÛªk0&Sß<é\€„s û÷VŒÀzÕ¾JÝ€zº ñþ޶0*“úå?×!d´Ùí ˆ^òÒ #Ûúxæ3ÍX{мàÿt¶Êôþìï.ùÜÄIãU»)˜gW÷$ÃY}=Ú塞°&x¥‘OnÚ&ê0ËõD´öy¸1¹Rõ“HørŽüÁILÝöŸa6‘“fLDoŒü_ ^þmÕ=VIܦ\9vLµÆ ɽÒã Ï ð+ET¹¶F Ý{ï½[#ëE–çÈ‘#•üý-Û"+\çAà×)"·-5iøAC¢SÌ^ñ+¯hDFŽ;–—â×K/‚óϸ›?‰²é9»8Ç…íðæºÃM\2ònÜšOk$ñ¿ò2Y®Ææ†/­MÔÇ£%É?cy,úM™þ5lwÀ ÎÇe€Ì¥žùä·ÞPLñ€r¥Ò»á”téQDz‹`Ý«~W²uH™¬Ž°µ±UŠ ìOðš’û"‘î>¯åî×BV“Jg—¹ ›N&H'X¶$ùÉØ\ß$ ™ŽUäšo”ÆèÇþ#ÊŽºšM­¢ÞÌ¥% ¼ON…MçC"›±|²­±†ï9é%ÕݹùºC×ýTuQ*¥K탼ï 3Ý  Çá†2¥.HŒ’G«¿*ȶmÜþ5f~wÜÒêDPŽƒ¡†a!äÓæb',D)Øq¬ùÞ̲jŸ» ú'…ú•X#ˆÑ"Æ%ˆ¼EvfIu6SCžá»ºã¥o£í¦kçÑ×F_›în`e|°Uç†øEÄÕæuÅ‚^:&£]ñ­‰cUª7vÑï¤Ù`âÛNîÑ-Û%ó˜÷¿Â:‰ÂmW£çÊÆ‘©-ðÎoÚ,pS·öX¼ï\Œ¢: б%X8-­nŒŠÌZ®Œ²ñ¸¶@¶ R§ª+x$ý‡yÔ†¬€ü¾ñäI¯ý±æzà ǶæGž˜/#³¤ØK!íPЗ<á±<î "öÀ»R*–ß³€¡…¨ƒ‡6Dgø}‡£ù®aBª Ä}Ù¤Þˆ.%Bh˜L*ö–ZE†ñp †g±/²D/vAíaò£ó ¬¥öÒõŒ}mÇ£7Œ’¥OÄøÅv9.òk¦‹å’F¹D°yó2¤Ê¿XëA¶ÿprb Ô!OPþ@Žýº|׋)!R@IDATçòܪÊò €ù\íì ÷16#[A;ugªVãœH¦‘¼1u»G ñUZbœF@ÏCz¯°NõØ3ÏB‚iøå0FÇ×¹š)¤™~dtÄ™ƒ6ƒ/zbO8g£?Ÿ¨>®Z]õ®=œjŠ"wéz¾é1Ÿ7Ur©[› ê$ôÂûôuáË5Øy)Œí`îùÈà}á-éw1ÆØKò]QXŒ_þ?a]*˜{yªhOÌclKìY£pïª)òåaûYrܶwÅ])J TJ õK€ í µl Ȇ½es5¹ýV@_ê;ý%é÷ú²¬³¿d'<û ,ÂmrA_†µè+G9)…fÓÌ"[‚=Å(IW˜Æ6n$þöÑgñké«+íkç?7‰@бˆ›ºv…ÖÆ”`2y¹Ô7P‰Ð?Õ. ¢ ›¸9t‰GmÐw$6t[bÁÍ.ÆÈ:µ+B¸‰3Dé8¡¤ÑÆ€¾Œ5G¢6ùš`ƒ¾ÜÌ¹Ä ú¶ :+;£L(uBi$™Cþ‚<¨G !Ë·õ(úR5—¸I @_†ÓH”-ÍI? ú¶û„ÎQ/!¿Ç!P³-‘›lX{b o³TwaQÝ›柀ž‘” €;°„›À%ÏHçŒÍh0}h¼š”é hŽ ï¦Ð“¸#ÁHÐŒºQÈ«AH'4 ¶áÓ±ùü1sR ôeœÁ,!HÌöN#E›¼ý:½¼º >?IÃ¥±!=~5h?|°A_Æ[?}}zu.D• åÐVV¿güСìÝõ­þËá7èËÀŽV~Ì‹êúb;Nm°ŠzØÝ±êH¿E„òhûŽ:+l[Qˆíb»$(S ôešFÏ•–>µßß~ËÈý$ºz¼¯€¾4¬GæQ1ÒXkßGÉŠ¸Ð÷Š‚¾Kè2? ”M!è[ótèý|æšÐͱ‚¤oBßBÇé¥Cˆ³0´‘>Õ÷„ ؿȀՔ öçZô2 UÍ“Úä¥Úû ‰Ön„qŒíí\ŒQcPûo€+¿‡jÅôå3¾¯Ó†C‰|¡F¾L¤ÇQ¾(3Dõj÷ž–˜–óÁ¥@_&ãÚe&¾ÞKmÿx׆Á{iÀÔûlЗ±0Å@_éOœnûž*Gu˜ÃÇiG* ³féSâžÞ;ÔOdƒ¾ŒþcúpÕ>›…Z5;gÆ1ÖÅ¿ÐßSù}0V¯¬vI·ƒ^ÜNjZÍÅê/ÁÜÆ5‡ù¦ÕMéô¥ÊmÑÏ„[õK?è«€öÃ3hd2} ¨ªž’ êú”íÆ¸ï¦™ Ç¥YΆØwæqÍOWS¹P ×K§¦6Pý«KTë:îÙèÏ·¨×§ºKû·b:ÁøÊÝb\ÙGÄÇ{MX˜¸©•õ• ’Ò /¢¦VÅ_0¶$€¾Ì°Ô,t›\o–CæWRÑ¢ýWò•×\¬J@ü})ê;]ìˆÇ˜S -J·ðûíƒ Ï¢¤NÎÃ\õNp³n]c»bÓÃs?g1ï.4)=%dëy¿Æ^Ÿ!/n.I”òsÉÕ7k‡ÛŒn(ÁÔXZ`ÒGI:g%.[È6ÖfHüm]»çbcM0™Æ¼\’2¼Î’såJnšq¤–+—DWàn}òÏ"Øl¤)-LI9Uó¸Nò—’íœÐT㈠œ¯´‰ÇºydÔ¦Otóô† ŸÙ–ŸÅÿeäÁ‘r~,ß&Qj·F'£³rˆ€Hl ‘ÚI'ÛrÁºÉyŒ;ÝjîÃ} sDåóvnNɽ >~è.¦31LÇ:*ž7%ÃIrÜXßàOúñVÝ®¦âøð{jÚA{‚±BѬ·¼ÌF\[)Ï•(áõì ÐÈRß”ç—É%FD9Æü @±%¦$ Oµ>G¿ W—0•önü¤pI2w B?{jåJÝX}“•w Ý‹¶~ý=VŒ—m†>-åõ9¤ä¹Ö âJ­ï4ÈRgjaoÜp„íaåìwÚã»?FëøNÃ7V€|8%åJÑû`4¼hµã3]´;Õ.¹äާn¸¾Ooãx;³qæT­êâ”$@Ý:=ËïRm„icÜo¬8V£Êy€hŽ €ýWóÝÜàè>s:Ü=1^?øalMí…»®†ë´%ö:á}VΜûUÑ!OÓþ=ág¼dðÔI?p› ?žŠ{2æ¨ÎŠ4àî5EuÏ8ô™ÄÇÝå¸ÿ©Óî”q¼ýL—Êœ¥¯œ›ôú¸Ã|‹i¢ügïQï-X+ì¿á>6¯i¬¶ª¿/}âþ\±—$ÑQ¹—¡i]FÄÊ¿fֵȫŒ¤¢/— Î|g¢ƒI‘{¢DJWÁa:Àb”†.“6Ã;ÏÏT©µ jƒ$ë³qrl,`å3?«ó _wÛ5€Î¿†Ûz ¾9Ió‰º yhÏC!ï½l‡9jrf E5^ʇ÷ñ -E;1î—gLÕÌ‘uÁŒ#qÝ2s f’M8kœÿê–—V\/]“ÿ@õo8OÇõÿ͆z”¯ÃöèøVŒvéCãÑæM7@ۥعغ)woAš¢ù#¸p}X4'ðH0X? ™ž–×ÿAâû ÌS® öú‰X‹ ýcs4K‹oë]·Ào.yõ Uô«ühþïá#+ßX)J ü®J iÁÿ«,­»rñ|óbR©M}cB4$$ ¤Üì3Ðø4îjo¼$åœÀñŒG2Tâ|låÞ½Ðp5ZõuIŒ=ˆÿ]ØôLxrppœÇ°}¤¥/œª› ñرM>©N;Üv$2rBÆ÷&k“D ·˜‘ž¬zúq»z¾×äjþ÷óÔ\»œÚÑYâ3Röˆ»<žE)¨bÄÅx«t'A®ƒP‰ÒÂZ»®Nö¨U~Iùøü‡9Òov[]Ãzê€(¨*ñD¡îA¶Ñž–ûô†>s%w°Um>å_ÒD$cdHÇQ§eSè1_:½ Ʀ íåûtW-±¹‹Ógô³êv Yé,—IÚŽÌ úîe‚°šI §‘ä׌QñöI†>’˜ö1ûØþºê<ÚX}uL wü¿0ƒ¡êF|˞ȴp+H©bUeƒ¨æÙ±x Ü?ä­ÈðàÌžj¡JŠŽ=z "q9’QªlKg<ð‚k펡­Ûæ@C §ñðú“ÃxZ& ÀÇÜó‚øOc,-­ƒ$TÚNß¼X}.Y\Õ÷AÊj6ZTjÒ:­w4ÒR·[Ïö;Â7—"–É ¥"•ç¸dµ«’þXã~±,x‚À¦ŒßzúÜŸQ&åHcè­¾ Ù[}:ÂË!žZèCM½o~³Ç—–'6î ¤gOo÷la”6žå»( ßÍTÝÁÈ;-¨«•p4<¶žÀ»QÇ8§¿ª'ß ÃŠ†»Ðò žG½Í”Nœ‡g‘'m:"îcmžÓnþ‘iu <޾l×éÃÀ™[Þ„fÿ¥¯k¾"3dþþÓ™ˆWcÒdÌüÖ32w™3¯™ÈÁí–ÆÑÐ?æ]ì†k"®C(›ï2Çäß uÏë|Ò'†ÙQniÀ=bDË|çÆë¨U¸¾Î}Ã(xp–&T?À tnÓ}C±]‚ª›UPç.éÌ%P—CÀ(z uýmÐnÒ¹ÇVßh—: ûRK××<ƒP³Î›‚ßÎë¤ÖHo¡žPH5(…`»qŠBF çÉí06“¸Ö¦:•ú?¸Ö¡—‡‚6憤17¦6w}ã÷õGÆïí»ZŒ»ÑC¸?  è^ךªÛ¼âÄôÜêv±º' …¼Èèk7©dfs/^ À8¶)‹ØëwG¹“¡HaG0ŽYD¢j‡‚q¶`ͤc4ÿÏ׊šŸkÃõÓ>T}?}-ñwÚä7Š>뽟¿Õi жÍË6¨›¿š úMz]]òù;ê´Io¨Kþ÷®š²ÀHj´Ôsš›ÏÃ3§¨ó¦¼Ý¬l¾®¯^ÿqF³òø%ûí·êÖ[oUõõ(À÷øè£Ôˆ#Ôƒ>¨>ü0à/øÃ?¨1cƨ{î¹G½ñÆjî\3ËûO˜0A§{衇ÔgŸ}&Þ‹ì:oÞ<õôÓO«;î¸C½øâ‹Šïk¿ùñÇWwÝu—zóÍ7ÕÂ…Q›ÿæ›oÔ£>ª¿íõ×_WµµµvÒŠ»R%Kà‘ (³¡5K :˜×šO1yóXsl³ÒŒG6ŸO^øðï/{¦âbˆ`_|p”¸-ZÑ8‘M¶Dëª Gá%>3õÆS|hÂ,Ü#ŸB ÙéÞ´}•:Û[^<.&ê&xì˜Dp…ukëá¼14›ÏÚ"!Zrö‘­C•³ÏTë{ Ió}l²N†sS!ô¥µ¥•$йr#”ÄÜéjÅ=:A¨pj%o²“›k‚\4ìÁãv9$1Ûm¦AaiÇ7+g8ú¦í¥NÔr7B’,ðj8'ôV€dH”Þ+§®WÂñ}Xƒ%Ø$ê ]¶÷§;«³\¡á8ä€|Âf2)ÃT²o -ªS.ÐÇ„g*½R:çfh‰´çB¹Ø(C¶9®É1ÆeÒD1-W «Vj&ÝV ßICzM¥î }ÑÍïk«?1ÌŒ4Ì´”°àCô!¡mDœ0«;ÚŠ–Ömè«Î‡ŒÙ ˜ê›Ç×£ÝÏr tÕ> 0ídŒ4ÖHâ¼ÆM*gþ„΃bÒ²m߯ðÊ÷¤T™ý~a åXÎr‹óxÔµh¿I©mÄé½ò5I{`ì$ØX%ˆLæ -i½·Ñ²Ù:—…ÓcÐj(³÷ðmìu”ŸÔĺ½Â–ö¾)t‰£Î¾aXúxu£ÝÏáŹáø%ÕLFâ¸ä¢ÿµ×’H†Û'Ö ”0ÈUãA¿×Q&iw Þ–Oõ§#nMùñ혙KqW­Ožü#5ÇIv 0jǨÝÞ¾‹¹§BÃûÕhŸ¤¯¡¿Ù¬'8VLEé’Œ.âÚmÿýõ—‚ŽÇÑÒ~éíÂ$dм°p³à~þ@µú™MC-& O`Ý6Àùãúç^´¢~º—ÆqüÁX%Øk«00{œuæ6ËòTêØ'ô·N¶Ü¥¢›(Eÿ; Ÿ¨5âQRøžô!Æ/JÍü6¾Ó­5„$,P]•ïó%^xÍŽÔΓ1¦…­/Ýßf'èð!N ÓÛŽü£¸›ø”ÀƒòïÚ)Õå™uT¯ª!îl§ìµ$ç]‚ü$®¯. ×UêÏmßV£ê/„:©œ’Ó :¢.™7¦ðÜR¥ri0mg™ö–î‡hËFêEt¢øß¡èW4FX@U7@',ÚW@ÐvVÔ­Ãü῞ÛÀ) 0‚ÒøjÜ­H÷Úíú–€f!Ù«@ ·qñ-¼ÏÁ»íhðòv( v}²WÃgŠëÛ²÷/J0È:$Ý)QíÓ`ÔÏRUXå¡ç÷g½>ã),®¤ úà Ìᢦè³Èg´mÞqyµÛ²ÝôïOKwÕÏYw‰Î¡ß.Ët+ëÙyHç¶]?í5qÞµéÒ]ÔÁ]×TÛtZQ ¾æËÔ— ¥‹¶ÔÓüù˜¥’?L|sžAQÂʽÞ6}âbh—z÷9sæhptÒ¤I*—‹cÇŽÕ (Óêêju÷Ýw«—_~Yg7þ5tèPõþûï«.]º¨×^{M 4(Wǯî¼óNÕÐРfÍš¥n¾ùf5yòäR¯Òbá°¯»î:ý^Ë,³ŒúàƒÔ7Þ‚Ó}‡ ¦¾þúkýþÏ<óŒzì±ÇôógΜ©®¼òJõé§Ÿ*¦5j”ºÿþûUKö‰ûÐJF‹m ˆ”aÒ ú¦äcÀ”¤;%• =HÛß"¨&,Ü8Ôi‡Ó]lNdø§Üi(Þ³V¾Ì×›'\JCJWëûÎ'¯é)ü&Rˆ¾Ú-Rp|~ì3ÿÄ÷S*¯]0Ï9V>£zk5£n‹='–^C‚E&„oy"üì±:OéÜTo„¬KÞØÜt‡Zí…a”Ôæ|µ~†¹Æ]­gË1gEü i¼–Këx:QG^>{ À½z-z\î¿ÜÔUA@™¶„À–¦PÑPcð~wwxý]Ç–ñAev7ÉùŸ>Íœ¼IõƒOTw|ß\jLm¢ƒ©Šç1 „¥þü¿¡ p¶¾X3”gÃÒÓ[Іð„¨êaô-¹/žÁÕœxúÒñŘ¼¸ú_‡pzfÔ.ð_F¢AàøN•©zêµY~,éûMÑ©š9\ß“ÅÚ/Iù׊F9)5MQ}ÎUÓær¾jó’Z’÷3`iQr #ÎΣ†5œ¯î¬­UÑ™d«°âÚfE¼çý; ÚŸŠ±y˜?a&¢A¸sü'û-¨òx eE,ƒì°h •{Ÿò­ºù¤úùçŸÕ[o½©£žrÊ)j·ÝvS'žx¢ª««SÌ4räHµÝvÛ©Ã;Lõë×OõèÑC ÎÑ""½w=öXÕ§OÕ¿-ÑK—DIåÎ;«ã?^í±ÇêÐCU_}õ•†F:óÌ3Õž{î©¿’Ï̳B¿Íh)¥R8M*ÇòlSrm~šru=6ÿI¿|è’…Û/ÿ6Åß Ú^ך¡7`]¹Ç¬ÊIS*ŽlWEG_©øI់¼ÿ@BÂýš®?…®1@nœd±(y»éÅ_®¢ÿ–GÃ[‚Œ~:N²h.fœŽ€ ÖYšÚJm½+Ш)o× Gá|$‹èÐ "Q/àçe§á1Ê¥O o‹•C’!*J`—ú3ƒƒO’ï-wËÀM_œ xµŽ¥’áÏCbÖÞ¼rC@êªÿÍ7…ìÜúº›m”¦D™ÙâW£$;‡ÝíHaþåùÐ@Q_n¾ü ‹ì@ˆ§‡ûM&Q©»Äÿi0ÔD:‘f ‡)ÄoÜíæÎ`l˜ƒòuÇ*©ë29ô-¢@‚…®²Mª%Y-Œß(ÔQôG=×b[Ð+„ãÓ Û‡è¡fxO´R~·Ý–‚ÚP·¢Ì p´¬ÖÙ¸5Àóݲ³B£uL_j-®ŠyýcÈËRÏ*Ç)W8CbÙËéWs¡´Äm‡0Eä 1E%·å(4ïó¨™ÛñÇ(Ù€@ ŒÑ÷ý•Þ`Ëó—DÒÉKÂM€žÌ€Íò¬“¥CUx{› CÀÝ%žºÃK§XR‘n¼íQÆÏ 3Ç•ïÕx†@‰:ôΔÞR•í¡v#’¨vX¹nއ~ÔJ à.ÈŒ’Ú¬sŽBd®­‡~xA*k¢gvS[dN¾aÄDþ ®´ý¡'£PßW óŒöQª¾ îc®Ì è6èdÁ,ÌC$ž‚…´A?~‚üìOšñ%ƯÐc*Ƶ³ôÝm3a‰ì°0TåFFn‹'(…ykn„Ú2?Nƒæ/åk½LE&7ó P…ÚÍUhþdŒî‘ä'›­&åë­ÌÍ¡yì'`sj‰^Bý¡­‘u¼†ÛÃÛWò8W3È„e/Ƶ;&ËÁaÜæ8nƘõžÙÉ$Æ3«6TÓòï+J\7…Ô¯ƒr+¤ž…^ÇØ>˜sÈÞžSû'í{1ú¶­ÂŠk›ÿgï:à¤(²wÍÌ&@”,ન**bVÌ sF ¨œ9gÌñŒxæp(f1þͳ˜0"Â$ lžù_u¿îêšî ïöío§+wuåúêÕ{œXbÝÐÏ:à„€ž9áB»ïáâylF*‹¹í›Æ6Ô½iSp‹’îÅxXß[ϯ¼ÕÃöú ¦¶«~¢!  °­…>ÄýDõQã Oô×™…#‡‚ä,}Îs™®f˜ Ø›øI´úsð²›´ø+·éváØ ~{”|Åsºgu 3±`›â¬ÝÔ;<(¿SñãôA çç\iôýú‘ز¶Žm„ñÅißVäÈò†Ìú)áBX¯…jB€ükÜêk‰×7úħœÝ·ÍT×OûÖA1 «¹´H§IåKtØçæMÕžI¬™ßýówuÅ´¯u|¦óõ_\†SuÊ («É¤žÍÖTƒÛ¯§JKœÂ°âOý¦%y»yú÷jj¹¿°þ«¶Z=9wŠ÷nŠ­ x‘ü¾ë‘9“Ô³È+ý.›ú•zaÞ4,Rüw1 ’w>_š_¦j ™Ã5ûÊ‚ZäÓx`ö/š;Yò^†üÜáŠÓ¸` 8bñ¾¥5΀˰‹ª*Ôw—qóŒï%Êjý$Ø9hÐ |š­…ÆÌE‹©^½zyÎl°6OŸ>]mºé¦jøð᪠À%P “¹ ÷ìéÜ _VV&ÁýIз¨¨H×|Y"‘PÝ»wWS§:í˜b(¶Øb ôÔmÛ¶­ºè¢‹Ôk¬¡.\¨6ÞxcU\ì,œ:v쨚7o®¦M›ÖèùnÌÔwHm̼­ê´µvîU‰•øþ±j"h?Ø“Ëul»¬F¹'ÿ¶{cÚ£gÙÜÞú°u!*AÈFa@ˆˆ4È7_ÊèuaWí–Í«€$Êû%q“ÇUGW'Ýl±,\È“›õ ȦÌDŸ†pÜIø ?*ò˜a•£€,>Ÿ§ÇÕdD.D“ ™‡Üž¥‘+—U܃³¸¸úÏ0®Zö›s’qfÊ¡|ÛÞ&oW§h^5å*4ñÓ5Mä” #Úû+8(®2Ê‘«KùÞm1í{Å&ÙÒxpi±U0Š¢Ý“á[–} nŒŠp÷À Oy‘ã=ÑÈÛEpœh56‹•ÎVεçÀ«Ò-ñÓÝÂ\*ú¡pß ó1Üœþ³ @M5zi3-ss9 Âóêðfº\ý–Çú ¯]?%rA‰x¯ì|À¢-v`˜ t¹eF’~HÀµP[¸Ð´«‰Ž„èØ“2šIÓ`J´qý$®ùì‡ÿëàG8¸*³ã;ÕÛ6AóMûÒõîäR”nú†ó›'þL„¯ó™¦]œóJˆS‘—ŠbÙ±¾l S¦È0-Ý6KðWú–Œä}’¹¨vÁ¦ûUä{8™f‰þÂÝï8&8¦ìX¡Ûõÿôí1/q|™+Cž”áj*h ‰+-#ޝòûþ`¼Ül{`>ñ(>ÆÎÚʾ fž¿a`ïà×.2ÊÀðöŒ< “ÍÊC@rÜ§Ó õzíÝJõÒý-—ä—–C¸µœ‹ÎÑW¸¿”«éK]sºÖ^†6ð»é…qçCü¤Ýx FžK´ €s "(ÿ”c=7321µ/7eôŠ$–ãgnŸWp—á¤GFr=ÌUÅ|ŒÐuÂÞ¼®•ðünîøIG{,üÌíßkÃïÍy:À¾’q îÒ1¸OxGŠ÷ú¥ã€ßʱñ=_[7ëKÉ[=/ì1-à™Á¢×u5#ÂÍÏr”©½G%I®ó@Ù çÊ@öì®}¹=zNK”"GQÁOâÁAÉ×Fpà·è•ÇÎoZ"Ž¥–€mLߺ8uC} ràJ°—4m¢=Ö‰?%öÔ‡oÜ/87!f«ëjïÓa86‹¹ã°x3õ³QÙ&ÛÀ(ŒoO ÇªðÖg1©£û2ò|³{àA÷ xøE±7IúøÇ×ÅÏT©ø$z« â-¼ƒ+í`ýÌLtV»bîuôP°~ÂÆ+R}¬‰uì´u×–"¸Ób%Z–®¼î(r‹[ô€8½›ÂŒ‘ÞƒòãøInðï«DêÙËäKԻȧOËNÑKp²W”i¡<nr/‘>³yAßðáâ9êÕùÓU÷­Õ 7Q»­ÝU‹axnÞoi/'è{÷ŒUË‚"µ»RíÿÚB óÊÔú%`›†Ø†f…ê±Ù“e‡Q‡¢fª{ó5ÕGÎV7MÿNýßÂêW¤[°ugpo1¤ý0»j€ÝɪÇmÔ¡í7Ps«–«ûfMð€Û'ÿ˜¬Ünߺ£Òycµ)8™?Æ÷|‚Òb€×ß.¯×Ú­§úÀÿƒ?g)­g”/ÓÀq÷f­ÀqÜSû¿»èw5vñlŸ?+.ý×<µÊ…Ó?üµÐ‹¿éßõûj^U¹: }©Ú¢+~Çò­eAðx òÕxƒSí«ôÕmˆë“f$ #AüµB0”^×Ŷ vBÐ6'Ó<òtг·æTNÈÃ.$˜çM‚±þðˆ À<šŽèó5ˆÏ|­°ö)Îÿ#ã—¨ÅÖÚƒ`¯CÐ=u‰± tSÑhˆŒð!(O¾/DØšê®ÚC¡f€ GÅñœØÏÏÔë’jÖ#õ*Q)}+£èMø®á)!Ör¿—÷‚f…ä*V3VχÑ7اx@ bûà+š<â<ó.¾ÉÔCáyFXß¿„ŒkÁƒÎµ#´½"‡šÿô¦ŒiηIÝPRÆ1Ï&_Æø¹H‡ƒ|“;¿Îï®:Q—FFgÿ6å¶sžaß“-2bcz°ì»‡BÜÂ-×Vû·]Wm`– «I—/Ö ïf-qÝ­KO\Sˆ«Šd­z{áLµIó6jH'GlÃÙ]7SŠš«7Á%EÿìÒK ºœ±o Ü]“/˜ò…zfr›t0ßÁm·oÓIË%&»¤¦Zƒ¿­ÅêðÝÕ®U·lÙ¢#6ÔQçV¦…·hK‘[¶j¯Áaæ}A®@ïÞœù¦×­¤¥™5 0ãÓ¿´y+5³b™ö qdàﲩÎýùrù~ À¹'Ê¥€iÛÂ]¶Dÿ†–vÚIM˜0AÝ{j ÚÞxã ý K AÔW^yE+w£¸„6mÚxà.å g°­Xr¹ÄVi Þ‹6ˆ1‰@7ÇÉ\I+×¢ÈRülõæ(9Øã¥sÔàx›¿§$âgL÷|ÌÌç]¨¿\è,£¿š²ëÙçM‘7T<73€nÉSÑÞG‰sáæìßž dÉEÇ@Ø«œÃ¦M˜¾â›•Û{î<$'ç|uâÃàÎã-—RÏ´í+¶À“}aš„!Ž*2׈[ÖgÕÞŽÜbL¡ýÄ{ÂÐAGë‚>çÐD÷™þháF™>ìmto†¨Cˆ!ˆ¤Ô»Ôv¼ãg¹Ü©þí›Èx9zPá]€´h çÛÄâù¨ø1rýkýp¯.ž‰ó7ei"¾Ò<fbµ÷`í±>MqrÄÐLS_'?WjÙVÚïþÚ‰ê¤Øa^¸­p˜?‡…%¶ÆM kUB™ÄZyœP×Õ)´¡‚[1w©çÃ?CÿÐ++”yl_WÑZ x …ë_êøæ^¨ã+¸çJMÂÿL8‘ÍÁÍ· h~86îŸeíœ-öùtêo½Ís&‚9ÒðÐ+šJ£½rô!àêÌée:ÆË.cˆ= ¬×ôÈÓlÞ¦dÿ“Û¾JßúökÙV½Î[Š) X…_W,ñ¸jå{Ÿu9€û`-èKš€“4§z…º âäŸ"Û ®ŒÆßóõôW—¯×Wƒ­‹Z¨·‡§ÿ˜"Áôs]„]³À™òjШâ!p@Ö5ÀQûâüišxÄ4g²¬5]Q<®67²PÏ5œdVårµ1€ÙµŠJÔ¨Ù¿ª !¦á±9¿ª5Àͼ!ÄN1>l¡nÅk(~‰ßØ á×)öý7má@%s Çø¿vÜqGuØaØ’,%À{üñÇëO¤Â39büqÍ)K¿¾}‰N8}É%+D3Srþ® *))QgŸ}¶êÓ§V.Ç'¿§]»vŠ~¤-·ÜRuéÒE‹t ‰ÊÞJKKµ\ãÎ;+*¾;è ƒT·nÝ´¢7hþüšÇ&{f³éÕT7gQ†Rß׆-×Ã4´›ïáõÉú(©O¹ÄÍ´ÌÈŸÀ˜¹h‘ðí]ƒ}ÕUüíg6Ï(®;±ót¹1¨gÈ·!ßC.í|êÝÑä]ÿ À×Þ¨gJ•³»p'É–ÿ—S#S<Ó/I%@õ ¸“c:ä8`>M ÐŒJ€ì1´i!µ§öW,1¸‡ŽË±ù£°«£¦ OÆ‘²Ã‘¿ÎXº?cn‚è`KÐYi9-ñàªUÒuƵÞg’ÎA½ãü¥â¯Ò¢1AGÓðŒ´y®Ûîî7^®ÿ~IæO1XÏH™¢V¸(ëÕÃ4ç–øS9¢þÒ6XY(x%< p!T\‹“ Y·¨g­ëBÚÝž5Ú—ÉY¦|Í”‰»£i 70V„)•$·ýD÷|Ôœ’’'rú«{qužÜ]ŒHŽ :º6Îh>ð6+¼D¾Î_Dë…>õzÄúŒaÉÙÅÍ.¹²d,ñ^^ò½ÚÔs<©R× ¥ÊëÞÕ'fÜ&ëÀúÚüdEîà Ü5ÉÃk™Á¢8u͸öœyBr¼î©O¡‰3Ý~Ç7pÎø”[9ßû@ÅÅxRä ÿj­zá[ÉÕKîP¹©ázê‡ö{Ø‘º!Ö G¹¾w£é3¹û5€zË;«õ ã vÖÀõ @‘+¤äj€nÎCŽa‚‚¤ìñwžø¾0ðÜ šý·ø½ìaÌä^t‰×º/7¹œÅ#ðü1`3-¥hŸ¤V÷Çd”5}Æ|¼1HŠûc,È@<Ù†( -Ú£‚ˆçcæ´ð?F 9ÜUº·ë>’dy.øg³\ /j~fÜÐǤÇdKKû'?AÞÖÆ°+ófÛa :äúW~–à ' ŽÓÖ—w „}@ˆ•–+©ÔÿÉB”µ_\s%¬Â…ëø\€0ÆÈ&Áýgõó0·So` ’ùƒ#ù­æ¡°L—NœƒœIZÍ… Ç!ˆ™Â!²jvÊù m? ·ú€ãw¿ÔÓ°;;ÖéÑäý´Rÿ(8Ï ·\Ï]âŹÈaêú ÃÝto…ÉCÞBáú—r½‡ù©øRõ¢þ†Éˆå1î…¶}Û<âšò{í5ÇM±–ª¼ð<̉Á5Ã)p¦¶Ú+0âŒJ\EûLHtLëg gz»ƒ"6‡“9œ#Ý ¹Ú¼¨Ô£Æ>Óâ¨Ëï¤Ø§lĹ‹²œIr¨)F–UGä²½mÆàP@6¦¶…x‚ ÀñkSˆ1è ‘OAô‚Ÿ"¯·Sasˆohíýï0y/»IüÙô%Ä&Œšû« T¥H„óºõÛZ‹U0c¯BrfE;ÃÜõûOê¡Ù¿¨É+–ªµÀU»_»uÛ¿ –P |—Pk<¦½E¢@]²îZDEwpòþ°lz¢$ž…œ`¡’8τ©â Œü1TÂÅm÷ðþ^®ßÿ½–}{â‰'ª#Ž8B ·.•¢ô}ôÑG5G0eýn¶ÙfÞÇQTiÖ,gP¢yΜ9Z™Í+ƒ/^¬•±pÀê”SNQ»îº«[AY¾$yJ^ÊË˵‘ÀtYY™|=öX5tèPÈûí¡f̘‘Gâ6Ö3Ïi¿±²Ñ”n#”@Ô5ªú¾*zôJO¹$Ý)ÖÀ+Ñ)ÓÙä>Y‰YÒ¯Êwë’ÃÓ䪱¿Å^¸ÓŸ§Ëu%gÉ’›Š.›œÑÖy‹€šëÓ}1–°J_¡øÞ¸Y˃L‘ÑÙר%9nŽ.u•Z‰¹Gó!^]­/í빦CŽæS8líxâøÝB×¹Ü=”9G" ²8’¸q 7 á¬iHÙZ½Æ?‚ÇZ öÄü]3娙ô²4SI—þœ±Y‚Œ#DàŒ9åF•d^ë% *ÄkÓ&‘ƒ¦¬j°çDy}Òþ´c*X¼åyQ /Û¸)Qa[`L×W7]ÏÂ§ÔøæO!ê x =lp:9€¦Ã¸ þaÏuSŸ†9Gº¥ ºEú™Àg¶„hb]øÛuìù¢ƒ¦ùÌuÇ1rÜ Ñ”*Ó  9"N¥÷9ì*´<áy.$JH9@¸Æ~È]¸Ä¹‚m­äºÔ‚´¼„;”â󟰼Ð2Éí:ýMú‡yíÝŠàY)^ÅeSÕ{Cæ^E™Ã­Yn^B‡ T TÕ8<Ú8ÏqN^vf¬<Ø6áÁÒü·1uPl…¯hPbúOSœ]Éùú0D茣CPì£_Éé%ÿ:lüpý W/9ÅÂ4¨ï @%@†¡ósó…˜,ÏÓJXÙEoÉÊ#C˜¥ú0cFí¿ÌWÌ"Ï~­Ò +¡øŽx’Šœ‡õ»…]5ïä[רu+À™G­X k¥¬ýJe®ô³®ª’÷#à }£¿Á+k†¼rÛʾ¼+ã.Ñv*;¾ÿ1CÌÃP”ó q§Ïú‘hZª¶©2__;Fûè MÎM­NÀ®P˜øÿA½ë¶*˜7ãÚ&ç¨XåÞ˜çFW/£Ý:\÷“àT!Îêúâ10ûóý…¯;VߎµÓD/ ‘Ì;ñSð­oè°¸c ¶¤>@[»A³þ$NÊİF”Cê+<}üÈ÷oX“îŠ<ÿMñn20ðp:›¼jÎ]Ž<ïÜò”>Òå¯Þ¡ž~ÅlÕpÕz[BTÂF©ÐY•»¢Ìì±VWu ü)Ú`4À_R;€­¤‚DLíÑ ò_©ZœDg°äþ ºúrÉ<€¬Ì BU ±j œÖ4΀¸…)àL&Ø{Áº½µ¨Š^Í¡Y/À«=¢hˆÙ÷í´r6$(rˆ=aùŸZÙÛ–­Ú©“!ßøæî[«õ[š~âã%h:6ÓbLExTJGêZµ6ø› Ö>ñÄŠâøO‘ýû÷W-Z´P}ô‘úå—_4 J@xâĉúþüùšƒ¶_¿~ê­·ÞÔ`ëwß}§>ùäµÕVÆàÙÈeQTT¬^ýuõÁ¨ŠŠ õñÇ«™3gj‘|5Å9|öÙgúSF1¸‘³— öèÑ£Õ¤I¿jåocƼ ¨à­Gœë`ò ±@j¢ÿÎ+× ýuùl¶ýåACç"<=laëL}í¨‘RÞW䌸'£¹éË%ßÜ4 üŽkÔX”8IÜÆxš¸€Úð}ˬ—^œaóc^!¶¢i+¯Ä“°ÍÑ$:תÁ£(™häJ‹Rò%ñ3=m¥w™¡³è”^ua4rê¶ ¶#Ë!d¿ß ¢¯»øŠ¿pcSö(A"j¼¢Ø!³½âæQüø4eêqKCYpQc¯¿šD%q|ov™·f,l‘"kUÀ*’öç¤â¬ç36h±~ÁÄa3EolåŽK¼ÞÓquÓSV}„z¥Ø.97˜e'/!—•ž#Þ® c?#× …ÚAµ=V±ÞM8›Y‰ÔCsŠm1 l ™É ¡¯Ã–@±×nJ‹‰Šˆ~²SEm_ŽÒ›XúŸ›¯D|K˜Lz™¿^ ¹s9Ä—x$ WÁž“¼üŠƒû´ÎޱRµkõqF(M4Ž—j7ÚD&ñÍ.*›zÀý.MŠW‘á0å hlŽú?æroíÇ^S­ÔþkÍ„ÒÉkgé^ÚåN´¶)áâGŠ ? îò ™€§¸¡÷à0å)5W°Í›!© +wêSP €¸–WB"‡«Ïmî$;yá!ia¦óŠ9æ@>»I§¡ÿR¼Š©Ìþ<Ô'ÐË–Dù¾æøP¸€qí­Œ¢é™4UñqžÜ%¶ÂøAñ5Ÿ©<4hå:öBï÷²RÖøeP&ê‰k(¸ã:i€{@—¸¦©nQD¥Y<ø\-cLyÊ»×…y‡ºQ¼Â~²ïÐ %æÂ Îo£m\–tFÒËÑx€-·+´X•ø:!Þ>Û3ÖUm[ø°âú­\ù}´ŸÜžàø¶nVä·wBúèW¤+/†ùZ˜ÖÓ‡øO—DÌ# ŸF«+þ¡©ü.g2Ë«yȇª8>NÍ'†ÖG)@˼04œuC®zÎÓû¢ŒIÏÆ5+aû¯pÈÇÑ\‰}ŠëmŽgþÈçÇ^e=„`+ÁÛ? ëvfÅr(O[ŽÞ)Z„AE2} í ‘{¶íf‹Ôঘƒ-šþø×"õÊ‚éjÒx@2•½ü £Í K·ua±æÔ}vÞTõÕ_󹀆˜ *N£µð˜ÁÔÖGšZþ—š_]¡¦áyïì Ú­2\==oŠš¸b±z2yß‚LbÊù-gî‰"õ9”Ã=÷ÇT÷‰’@|Aá\h‡Öu°GçNR?Dþ€ö«(*t£Â8R ‹éP"÷ÊìïD±ô}»í¶Ó€'ŸñŸ\²è,Zš’®>ðÀÞÿ¸qã´ûÞ{ï­Ô{î¹G5JQéÛÀµßÊøiÞ¼™Î+óyÉ%—hà—¼:8Ëq¿üðÁÕÕW_­æÎ«† ¦ÅZtïÞ]ñÛ}ôuÅWhÐû˜cŽÑ~+#ïMï¨{ ¤]Mª{RM1ó(sCf*Q2“Ø:IÔŒGóÓ9€vœ†°sTM"íÆNƒ›F!Â6I¿æp5Ê~Xi‡£2ÚÈ­¶ ’ðÎì,6ÿ) ùÎÈÔ$ÏSýúéšÀ's$@йÌ…Ž4€V`óC"XÌSg]È^ šJŠ^4®ð›mLÞÃ+p*þH[™ʤ$FKBDHœ¶Ëq q¬çÓLy¼iÚ¦Ýç¶êÄæ#õMZVM.­(18ë|„=¸JUjrÈ÷òeÏ£|)·Üá|ÍÞs ˆ“Ñ}ª>I ìkÈ!=ŽùÚëÅ©ÞÏù±±H£€ãlÕ+†µ;e^â›ÌmÔô°1ÃÕzÎ <à‚w\Ì8zíh¶MÆ‹¤š3"½l蜛”ÙM…?$ÍGQ#­”Èv–íqç@?@oO\¤†€Ó‘›ÉXá£%G:9Á”ÊÒ–#Ékô£’@ò ‰#ôk(&BÒÕÆ© ŽÎ' H‡<€ë ‰aÚL‘‚k&Ñvþ85¤×B†ï8îVª ßýdp.O\¥¿—â0lºÏà2ã$G)¯H±åØð[Ñ_™öU`ÆöÏxÉxøø3ºÉ©ç¥aj¯†Ëo®ë €eãaî‹6¹2Ý^ò•¾Y1Üà`6oFì€cd˜7ÇíΙtÌA˜Æ!rXVàt9ÓjNÕ!xsLar³.xP•NéËñ?à-¹ C©ú0µ°£\à%4L„#ל÷ÄMìpkTD(qîp#´åT÷V‹ø˜OÞ 2Çwúq¾•SžZ!—D¬ñû®I5Wë&Îß´'×!Txw:@Yr|“>‚â«( Žoõ³†Ñ~è­èª<4Xê:’‹™ï û7€ƒ¿6q“š !zkVpeÜ>ñÒµ ‡0¹<ø"wÂû„± ‡Í¤Ð ¢ªð9\3Sp~ñ sRÜ ç»!(,¶ˆÓ?'s‚½+—(Fï@ã°‰oï„ÿc U»š“™™…¶ÿKªJ½‹1ŽÇà<4¤¸0Þtê1¦Â`q×OCiŸ¸ÿ„zæ6dzâh‰ù|À19‹I|×qàŒîPäœ0/Λ¦ýn@œ–(ŸÕ…žn‡“ûÁƒóÎΊå*‘ˆkNÞ|#/[ybl*„Ë7 3ü˜1cÔá!;ßLß¶dÉ%â'Ì´h&·òòåËUëÖ­m/ÍùKná•%—8êÛÒ2¶’ƬS”ö&‚ø6e·ì°içw=+á‹ÑÆ|sSÚQ%@eZ<ÙΕ8òsá\ ä{Õ‡ã3 „3sG s±,~äz Û‹?¯ü ÷§¸Õ÷É«ŒúŠc}ú›Æÿm”Šß¢ˆ@f¸òЍŽ;åÞ @ÈTb–9FÃøÊ_¼%g7iù¹OaÉêÅ$÷¦òœ”¸Âá‚–W‚Àô§ñŠuA „W$3•u½“—fŠõ(¼ÃÝ™cù¾äŒƒxõ/¸ ÇÆ)¶9>~¬Ÿ€a‚¯_f†{}Œb~;` ÁnŠGXš51®KËU)—éZ²"ÄvTk¤faS8Y{Ü…özÀy“¨,Œrc)7:Li‰6hî+[³W ßµØ\½Ž(§?ñ¨f6s lÀ6ù99r@.ïîh¼RkR{X64ƇïÁùš«æpжé+Ðb,.ƒ8^çFu·ÀÕêw`>Ý|­6s£jŠ8¡#DzÊTÖ;9æ¹[‘ëù,õã±A~õg0©H"¤q+À†“\íŸèË#ÓúòFõ«Ÿå6Vù€…Ç(€¨ yåR—Ènr·@¹È\ͱ˼á%] ¨Ú•4 ’Ìr"—ê˨÷|ÅMeG(®ãq|[h]HîâÃñ­÷ˆ-äÙ n>úüýè´ ›k;ßx¯ï†)|íàÈt'rš¯@z·àpgƒªý#Êy,¼]Ý‚ƒ£œí„ö8ÞOÓS€ÓÑ ã[ | SBäôŒí:ŸŽ ?Eg?_¢Ûÿù0e)Z*Uô„VΞ`o8³‘ø æz qµ2Uú9D%Ö¹é3)B„*‰x>9àr´ïƒÑN¿°þ±–èÛN=˜è­ö­Iï3i]l0Êê%¤í`Wú%‰+a}GÍŒMQ{áЋ¢l„Fã½GÆ›«£j¨'¼1ݯ1—qž7©”Býé9Í˵•dÅ:m °Ô){úà¢g!b7Ø@Å_ÀŒ>ï»bn²×YÜ_2ŸK<Õl–Â:És¢êûbL¿Ã=x¥Û˜×Â[Ò¯>Äû ¿DŠš¨OâˆËo¹õ18ÞL¯ë^@Yš–¼ HÆŠa1Åì}Š:ØõD±;až¦bàÅÏ@ýßpÊfIkW!€RàWÒ {þY]¥šhNØ0ÿln5w»""Ö`ÊÊ•ø^HŠÐÀo®qÌpì"‹ª+UkǦ,_†y Šâ~§-Û%‡›A¦o€ê0¢q<¡Jð_âw4ƒ¨Š°ø,›æa~uyWCÅÉŽ6Ô;;(pô¿ùÛ»L£Òÿ»¿aßaôaaL·ƒ0È¿Á‘f†û;›×Bæ+ñÿwWW§²çu°± \æfŠ ˜s$¸:å2{^¢€8^C¦EÂ7QÄô?X\mqÿ”À=3´•bã¸ß†Å]Ø“uF@ù§¹ þù½Ã°øÝ›,.À ¶Õ…]´Ëãš?Èñ2íj‰ûR^aãڇܯóî´K’O!¨%_Ñ#íI:u?½™àÆì 9gBaíšùä8’ uD l[Ç\ÒáZá>Ë%üqàÚùÁÉJÙ‘a׈ïDÛ \H½Ù5&×÷nÝ=‰z<2Ð6EÈéøw˜ mꦽW7,!ljc}±q|›ÞQþ¥p&óh€#¡šãdò:º¦Äµ`¾Ì1ã—ýò¤aƒ´^1@C¸J½ªm/?«Ÿ‹ólQ¡ÖYÞ¼^NKãf±=Ò67²Á hÕðcßj¥`¼êí(ÓA™ÜäqÒf{Ûk±µÔ>–ˆlq2ùSž"Û:7ÒÃâ­‡–f)´+e´+æ*¸h Bäys¾= -Å;´†y>8b pôgK™ƒÒíh‹äŒ"‡îzÈÉJ?njmÐíR&ãÁÉvÍŸcˆÔ+`'ØL5@L17b?¸ €ŠŒäJ=ßwŠ K*Á¾d4ñ3±q¾S‚†>9–½ðÜßš KhhÓ±;@±Ý‘>ÁÁBð6Vg=  ôLä¿ÖLF›[àw¹âÈ•¨“?ÓümÔ²ýsµsÄäì¹XÙ‡ÉKZ¡ör;¬íá¾pbÚ‹ë2?sn¼e¥¹f‹?Ã0 -ÝlÁváܨÊkv%‡§Vdý¦žª3€óåV›D»ðäÓãòfˆ©€2èKÀÖâ×𽻤{¹.<´ÙíͼÑrzök˜%¥o#æ«Ñcù`ÜŠx z9\d-© î‡÷0' 8ÇåÊýó& Þ¹ÀŒÐN8ùŸ£I9\!€ÅuŒ?ª8)ö‚ý6½ÝKBuyöÀšg¦æ¢ôb›óWühäísxýæxa~ªÂ<A”™þÊfL–•5÷ƒss~D:âÌž5 aͪäG”ïf:–6„Ì^Þº1É<àÁë!”E¹§ŽfîP<ˆ”7çó1¨·£1W݃5Åa¬;*¬L½‡go<Ñ—@d2˜Šxwè0ÎhSжY?eöƒÁ ÜëÂþâ}뺅?d=Ây«óP¼SÁHõaòjµ‘±Î OEi`ô!P„Ý¾ŠŠ³ú¸wAV~ÇjLÏ¿aÊ’ ž§Ýq?À<ìªÏ7…#‘õI±qÛÕôåëÈAK.Y.ò!¾—ܾu%¾ïµA_;½5ñŽ(Зaé_`–ߟeågç³ÉÞTM%Pÿ0e„™©™§{¦{”ù¿ôåwÔª èkË=Œ*Ãÿ%wjÛõêøð0ÐW4¿®ÎåÅ}ÉkÈl/™ˆ›ôexðN¦E£Ë­XÈ7&-cjŠç¢ŽÛwô¥›P ’ÖáNº±Lxå; ô¥r lÇÆ\dæ ú¯áÜÊh¶+Bq„¸)%`C®[’ ú !QÄŠ ú’S7Ç k‚¾t'Pd9ÏwÿP”xñoÐ z«â’v¦§ ú2Ü|”+›D†?[”kel # /ÍAЗ.?áŸÛ)Ÿ|Зn„ÅÁÙ”úÒàšô¥• /·$<|âÍ}éh€¾´²_f}×@¸þ©ÿcpP‡tЗÎU( /­î|ôÕ³‡ W²Dúíó3=C9.œëì™DŽI‡øâ?¶ž@|ªð¾©à.õÄa(rdžešvÛrFз… 1Z¢¯æG§×Sî¬IíLK˜9 ô-ç…ž€›v›Œ})ôƒrÛÉM¬ú?×h×Óq ¬…y¼ ²teÆé ÌîL/Î*ÃË)û•k‰@;¯¹Î [Z±•¾-AŠ„ygèË4Wâ`¦K¼‚. ¤¸ñI®}®ß’î)82Q^&™óWò øüæøÆOŒ}ONœ‡0ëá6Á2Ür [Y;Iˆ8§¸5}KîºÊµèèèK€¾cB൮øO‰Ÿ GT¤HžinŸ#wh{„3Aß7цË»¢~OÑ"|桼[bŒÅ|ì {`Ü\ùàzWx¿jŸš†›;+ÔÐÚ…K¥Èç®y£ƒsOk(é·mX°¸!ú>"Ižíc§kû`ôµqœÌ9\hNÍ?Õ®ÑÛVœBžè£ñ#57¬únàùê°+Ñ©ø]¼Ìîmï/º_Ëñ½ë«0З±Â@ß_\¹õo¯dÙ$Ž"ǢɽN%à%n¢N 6Ej*¦ø[”€Ã-ô·ÈjΙ¤ÆùÜd]åœdÎýƒÅ–{èû4Œ‰ isÃÜ0©®úTäÚíªÎÉ#°”)/ƒ±`múÓYâ’âR:J| `‹(ʈ ÒŠND)ÃpQÇm’IæµJN…/Ùˆöv3¬m&—wærÓô¨Ë?»§·iw.Dʦ´o SÖF€„ ¡\G넼ãŸ>a„AÂB8nHDYÆB+Řgòn+˜¶¾nmìÃÂØnG´Õíc;ÛAöµ¶t ip7ÝËwYÞ,Àµå{d6±ÌùþÃÓúàHløÅÎg Á¨3ÜFŠp®CQ‡Ú7ö} å+®óû4ë”7[ü- du¢Åd\ÎøÕCÃ’ƒüŸl¿Ü\ÛäÉÕ,¸ÍöÊhŸmø¶†ù„îxõ½èÿ ß(_¿4|?}]> úƒrjn´AÇ O¯ìÜD_„,ņÞ?ú×F Ì™›B¢¬ÉËjƒVúõ n³¶OfFÓdüþ mîk«55°ç:P+¸C,Þs'äŸeÏC¯g¤¬qRˆm€‘CÝP†~?ã!˜©xSBÉóóP†q(¦‚¤Ó-n`íòCÎû0¢8¢à–: Æ ‰Y5Xhܶ 'žS›Á° ¯–ÏqE¶n7‹Žõ( ëy¼¢Ì Yæ˜HWÅ~«ªxÛ–Û‘žFm˜þ}ܾ³Èpä\G`Ϥû†>äÀ! !F›¤ü C›!{BjŠêP(Œ– ô"å^šVäØÂ)·ÿL=£¶)¼Omž¸âsä5ù•  ^ƶö°OÃíɧõpÔi#lÉœåxlº–½FæñøF¿kçÈ–óŽÉ'®}FÚÿÎ=꺋Ÿ\©4oWüŽZc*Á²á^ÛŸÌîδô*“Ø9SpîsZÑ :k*C¹ß*€;\dM`Ž)nPȆ-Àõö}ÄŠŽ¸“>l—6í{4Ži8ëïÂz™üO2^ŠZM•ö qýä£p˜†ƒIGÁlÐ×·uÁX»7mÒD4\ Ð+NŠeB€Š? XiáÙ§%_87“’ïÁÅY5r½WPÛàf)ûר{S!ßãßL~…ú½-ŽJ+ ÔïÅ7ªáÈ—Ok¨· wÀŽ£Á!r¢z¼`OõGÁ­êËäÃ^Ñè …B¢™{ÂYJ¼hðFµ,CÈ¿ëk…#q7‹Y.-ø­Ymˆ·aœµÍožïËÁò.ùÙrÈd%nôøÄhÃ*w…=t‹²Uí­´íý+çàôµšŸH¾¢-.DÙf‰ÓW=þ»šLõ(C!gøZÈn¢¦h*¦ø»—5Îçwűá¾87ùZ ÷>¦Ä…4¿MÔ8%04pM=úÙ®ÔEÇô},ÉvÎwâQ1Cd7Û×â³ÇH¾BM˜ŠlþÈÊ%^Ø’sn`‘ŸþÎ\]޵ê‰W;IexëÍ÷š¤IÐ2qI/ø}àå%r®åBÇ£¸?hÙO…›#'—Jñj¦I{\™&ç Æ6S 7ù¤ìƒ¯7RïÛAöæÈG$Ň…zQÙ© ÈvèÑsì«M,ó ügSDI€Ÿ2F)_NSlWÂpŽÙøÕù ×™A•±Í=›-zN¾r{D)øô»g\οhç€üÃÈS ½ÄúyáMLj7@Œð°•ʘ!ÃÍB2ÊË®9'$"ê´àî€;¹¡ìï'hò"e§¾ÙÞðb!xMb6¹þµcêkÕ/xÑÿv›‹MG þô¯‡2E®L¡ó áý€9æ¦`†Ãìp’ÛJi$®ýd£ŒW!ŠvHâ}}ÀÙM`‡" R ?„pM×à?!´ÍÝD0t$Xë§,ýLBòiʤ¿«Ù·hg†3ûóÆÉóÜú¡LI“^Bî(‹Ý#Öq€JÁ™F.½ ±¤xE[hÊ L¶¾øËSÆwÊ×5‰ ?»rÿô=†@™Q¡’XÝq¶åàö9žRÛ®Ÿè´ê7C!Óºyà°ËNãÁKîüJà’ëÑ0º)PïJípñ´i%`ˆp0ÊâcÈc½!†~˜ñNê%ŒXL"Û lœGZkÿ‡ã›I„Æî6Ö<4!8Çqq©VHèȇíëŠÃöx€Ã¿ÇK3Nîоž5ÌÀZΖ…ÅñÜÈ©|гªbØSïúv×Dñ=¤‹R WR¤‘?Þ¹AÞÎhë‰W2¼ &ãY€Ô^GÌ &UÔCe¿Š­]Ó$÷ÙJMÄ  q¸&픇X¬ŸOE!_É×Úþ”ÛŽyâ´Ø+ÏSÛÔNÕécxßX75¨òq5.ɹl™ZTû ZJõ|NZªí#‡oªÅa@ §'ŽDœutZá?“çÄ hû[A~sÍxVµ Š‘§Ej¿eÁ¾ëDÀ_ÊXG(ÿ«5Ö6Vyo¤Ñ©Añ¢—ga»}‡¡¶R^/@| Œ<«b[N¥õ~€º™ªQOá?Šœ‘#Ê7Ý=[xŽß¡o[™2~Ó³Ýäò¿P”…ûw¦0Ånò=ÿÍß&߸2ŸùÊø-yû}õб}h̼î‡ùÊzWc~G]Ҧܰ5´S—Øu‹³+–“ïf[8fIšçع‚NY’Z-½2 WË6L¦¸ζÈi˜7E§BàŒ2Ô–„áv‹ ôµ.pñR3¹¹Ê§ Þ¤DEÎà•Eù*›âæ-l›F1Â1MùÒWÇÛ¨«“‹õ¢Üü,æ¡7•f€úšcÛ \?7Ri‹-î “8ÜhÙZÉZØâ/Ò›T“{ T´ëµ®Šé$-[y‡‘ñHã€OŠúgPüçDæñÁÒÈt¸…™‡ÿZUŠ_n‰¾Å¿RÇ`³ÿ46­ÕºÔBz¸åN‡2¢»’¨æ3! ¯+ذ‰5dËê„òþÙ1~Ík=øO à 9ty3`¾ŠbIò¦â/Ð e“Œm+³ ú:¶ºÔ§™Å ì„òï+T×cÃΓ5/%øc]nüÖàþK Ä8#âh¢ÓÞ ^?zÞ”‹<üð¼r2lÛu¹æ³QŸë×E$ÍõïÛ¤^‹à8ëöQø@P€‰à“·ûÛÿiÇ‚eÁ™aÜ(|é…0A0”õ°'ÆÜí\ŸÛÂ*TƒjÞŽTX˱ü(ÔÑqý7@«+ÔU¨ËÃSè±±uñ}ïÃ'Šð5ñ½QÏ_#À¤¨@hÎcî݇©c!?áü:u"aî/¿óðÎCàT÷¾L¯h úÆ™pú$„ìîåh&uÁ‘àïñÁÈߺm?‡µO†R5£6Š™ ÿz˜ÿ/—ƒ§Fy‹Ÿ(Çþ&à×/&SS 4•ÀjX6ð[„<>4Š“t2XC®þ6N¾r=qUå–ºéáSDƒf‰›_[Aƒ¾ÀJŒ'½XB4 ÍÑ:a§þ9¾mu¨÷†*Êã5Sc.žÊ%…† Cî¡àÂ;·t3•7ºdÈEOþ‘‹êøŽ°œû×òò!rD¿…ޫܦ¼PI—›–5ño^£äwÉž×Sç`óÈ-ÅÄð[wÆ‚2K©°¤=®^c‹›‘rÑ8ž@ œ Ÿ9ÔJÅæ¹JÅÄÍ|Œ«äÏgÜĘ¡!#å«-Ð&ªÏ>k«†¦èx…Þä¨ ¦êpUm‡ÍEW[2›Ü\;~]íܲ>Éy*ÄöKÚ|û-ãS6,Å„’Þ˜ê•æH.a-“؆ÞK± ì ¿—Ó¢˜Qб%G: ôÌðaf³M‡ùgsãUé\@3¶õ5bôì†ïzÛI61@Ä-0×Kz€‘‘¨^Ž8?£gr^çêFäïXˆ7°oR®dØ1T¦² âH[f¡·ÎÇ(EnölWóÉ))¢²(«òZlÛ´¾ íé=làïH.AÞ’ê+üG‘Œ¯?£ö­<,4Å |—ª†¼ÈÅújih¨ìŽ¿+ñv0–)vXyIxrzRÜCCQ˜â=/m^[N±¶§h'Ÿê?þôDm‹´?ò^e`=ÏF}^€ºpFÊä¼I-`a»%xy5Ú1åÍ’¢¾÷9”ë1(×gð¾ý›ŒCnÎá×/ÁÜ5–3œGEŸªŽ†58k‡^Ei¶^“?ïû¿Â›Õîq¯ÞáF ø¾ó’Ëj ¸˜ªÃì¯Ð k{`­‘òCO0VƈŸ¡&ǾP7Ö~›•Þ<š˜8ÆÁu›úN¥â¿D<™°7Tßɸ¿#¤¬¢ ³O¦ÏêÎü3޾˜‡ÿÏå úÉ>¤g‹¢çñk7¶‰¡¨£ |iDKÜ€N°Ð¥àŒ/®ž#Õ3µ×€£Ûyä’8Y]”¼J+ »:zŒñSÞÀä& < ¡,â» 8žýäÁԬϨ,“ù½’jsô“>{O ,AZ*Ð$øËÞy&@Ø=º|sŃn±„:bµüƒ… Õy±ß'´ÈŽãýÁ½Íqò,Ä}ß³ÆÑ®h#/̇6F§àÕ‡»–'Û5¹™£Ð7EŒŸp(ŒÜ®`®»¡,OA=óx#µ¸J4›ªjËK 7¥ÌIÇ£3ÊuÊõdǪ9±Nn4F˜pPþ»ÇwWW&§@¾ð(§N¸{0 aÂ˼!Œ“}k=Míßî%›àþ ý;Yu=_åGŸ€¢{سÇ=S“¡©šJಸñû»Q¶¡úïö=aù]Õ /ó´2@_¾ge‚¾|Ÿ,WiÎ…Èå—+Õôå;¢ÞÔ¼~ŽEœï’Ùd—ÏwÖu×̱}_Ê« x$rõü”ÂM‰pç¼]ûaQFu}™Á‡(âö”@(‰žº¼ƒò1¹Üµ)_Зñ©DÒÜ¾Š˜ú…¾Ü$qÃAЗ‹XQhâl»Ki?ᛑo%èK¢¼M.|×ÖW`K *ìKT€{‰\"S’id¦Š€w&Зó}Þ}é–®JÇ@‡¦XB•Àª¸‰2¬÷jq;CЗú’ë7¬½GÉ"Õ åðCàjløxø<½°~KДr$ÃèE÷ªo$èËHÕ9‚¾ «¯ªÛ /=Êà—ôe([ñ ÝHäý¹Ø½Æ¯2ü˜mšZç󣾚ÖDá¸ÆM=Å‘˜DN½±©‰ø®·}çÚµrFå½ë¡ólíû[¦¥†‚´¶®Ç!+ Ð÷Ú —ô%q^ŸŒÿu |âŽíTî¶ 4Èïî#n’G`Ð?G?̲p<ü_ô¥]s+ŒöOQ Òn?EÛ¢Â.¡=ü(&èKâ–Ÿ‡ˆ™@_Up•8åµo%9Ÿb‰ëÁåµ±)Nà(€c¥·íU~¼L¦NEoªR«.3…¿o¦Ç6Y‡g}Ñò Ñ—mâÁŽ&}mÙ}iHÅWêÆ%®!Ü!NžЖ? ø—ã‘ò»9" 8»Äz~€«´Î¶ˆô¥l㘇HlúÒþ›ÑÎh–3éxŸ- ‰âNnsLgB†*¹H*¸XÑp´2sÖtBÿ‚ñ›³·axGóÝ«G /ÃaJ~¬9gÉù”3ÞU è…8< PÕ°\ çƒy`4è„6Ìëâ=ÐŽÈÌñ…`!‰âzáJ%xQŒNz?–ÑB×í°ûDÉ•Xœ`€¾N«9±zÊ€YÁ]ˆàŽM…w¨Áþæ¤ÖkÔyÁ™øæûP0Ÿ;ÎÆ¯Ó»áà‚¾ô:8èËèK+ë5/ª9×}Ïáxƪ¤ö:u#Æ›Ü@_ÆüÍ}醕úÒ•b]ú’˜_ýÎÔ§ª ï k ?U‡#èKZ”ªU=*¨/1n>îöíú /Ä÷Q·`LùÉùÀ5ÖÏxÛãrOA±dÛÄŠ 9Ö:éÀO•†šáDÑ ¾5LЗÄöœFÍ~Åûwuœ5èKã ”å%xŽwÜÝß…O¨ó+v¸¡uà†F«€›æX®¹ÜuÛÀ}²åÊ嬱ψ±§:ki7Päƒuze’ù™¡(f©zÖMÈáAp>öi¶‹"ÞPâa«M¿àr}Ç¡N2í>D;àA~‚ëÎ{M´ŠK`cc±³Š³ÒôúÿÁp¶ãÿƒÞÈŸ¬…ù7ò;VFòœZ¶€Ä•ñÞÕéƒpºnU–Ø~«Úþ¢mOÍzª9Ç}°Q^©¡Æ®o°(Ì•È-–ìë×vx^›&Øl‚¬vÚ)ë/LޝHs¹+2wÃâfs‹Rœ&KWd’Sð-lŒ:º‰gŽ(w±ß—í¨d!6=Ü™‡>ÜdHékÂ]¸‹Eîžý¾lv^ɯ qóc9`„"eÊþtÐ_$˜<€”üçdÆß¹ôźUƒUýѽönÈ™=Ó1ÉeŸ©ì¥|ýÔ”õ”{NÜ$ÊFÏ+±âÏ´¢‚¦oÛ?sü àÄ”ÌÄs4òk¨93¬ÎÌlx`ƒéèšEû¹í¥ÁËñv«M~íCeß"ôï^ Ö Åu“‹âL²å’ŠßkÞG£Ø-î"Mž¬„„OC\Ì|O$ŽküÖ‘ÆÓ!íxw\ï™\ªUOø"×÷˜+š¡m>ì^Ÿ¿35 ¶ú“)îek¼·»»ï¹\¼BÏ󯺠H ÒýÈ'¹}3RÍ)j¬'Ä)x|ùïäMjÛä¯`‡SÓÜ4ïÀ> ›ò|dEÿVµG ½Œy3<àûM¢|GÀðK)SÓ> •1r×å@qrË­§Á>;ô:ÙŽ®}x¡Û~TlcˆÍi­xèñ r(°‹-ï<"©€s3p xÔàŸ%'óGŒlR[œ.†¬Ò¿°Î„ÿù†x9| ¢¥ÙdÏiyÁ¿`^_s‘v ¹‘Š錅$êþŠ–ØG·EXÍ¥xÌÂLkűœÇB¢k[|«ßªÁÃŽò ôø’§ X«5.¶×ZâNfªÏªOö;x4j°|cy÷ä|ÐßB]ä4þmgGŒÂ¿àyÆ&rpRÆ+ñ†Aòv@±l—ǪÅ÷÷Ò£m?jæ½ê!˜ÖUÅo©šSUy¨!Sº]òþ ?mgã¥Ys‘󤛦ŒÐº7WJ膲·5QdN&pN)öáÚE'U¶aÎ-—ÄwG¹ödŽb˜nÂøLQO¦Ìvr±sŽî ‘-k×royØGÅ¡¡Ñ­î[Ô¿ŒßÅ0E—üÚê~B!ZÜ€yÑŽ,5Ÿå[Ê7Ð}‡oÏ`º ŠoLM „`Mœ„ÊtâèÜ œÃCÑFÏ@qmÂõàs³ó£¹îÔý€ÔOj‡ÚöQø?>"™åh»xwE(¥q㇤¦tW‚KU¡N¢ÁîìCF´9ú#–¤Úô\)%Ð o‘Î8ÉXH¯”—7âKZ{_Õˆ/iJºAK ô¯Aßð¿™X¾2ÜV—RâæÇ$N­Ï{ËrÓç¿ÇŒeuNdB@Ü$ו;׎rzq–@¼Öœ‰®ÃÆÿïD™®Äçóû6¡*¹Åò¡0îßǰ¹$(s­Ë%••eRÐer:E¥aº›`o>Šó˜nd:㟛M.; dzÿÖ({¹·£”gðÚ"Iü¹ü—ÒqX±¸êBgYi¦4$_ 3)•>CÐÓ¾Çõq!^¯·2Ñïp‚‘Cò —«ëel^Ø;<°³ìHƒÈŶ OlƒP¨ÆâB)z>|°Kˆ2|…Þ±e~.ïÊZd=7˜ãçÿë%bîÏÑǻ BÃN³Ä…Üá(Ê. 9*¦RaƒF”‡ûº BÒŸõ•Õ¦¨t"P„ÛŽáðÏÁøÌÍV…Öê®NÖ(ŠezÂ"î›aÇ~*²´—\½Æ~ˆZKËç…âXvåÊ¡-”êþJNÊr˜ŸH<Þ=f8¢J„Û™ßÌ«ôBZkqç“ÊÞ2ÑÞn?~ÍØÿp4h…r9sŒá}8s;âÉöųIÌ Áa›X²,ws¾>àBØ &r>“.…?•YNuí¥ª·vÏû²( ü˜D»ÏLGÃ,\ªì·Ýà›‡a Æ1F—&|º¤>ÓâTŒèáÆäpŸê7íjN¨sj—š ?x)à^3Ï ËQFÛ $‚íö§\,Ræ*þOĨv,ï©ö2çîÊ]‚ÉTôÓc¡sÐT¤6,¸>è/¶ò u?Õ +¸<j—$c«¶à‡ Í(îá|ã9P€(ôÁeÈÓ½,6Y]‚oæø˜[·ᱜ‡~ÂÝÉ‹s-ÝÛ%•>7Ý×íM®[Eu$â‹XíÚÜ?$’˜ªà× JC ržwŽ©f@Þ09=Ç¡Ÿü‚yè40+"_]T õü:¤&#º9¶m¬>ˆƒK×½þ-ú<[-ç&“q€y„TXˆgú\ú²@0_ˆœ²¢8†šûN޽Ss [}7~¼û„;3 ä &"%Í^ž… C€.qTÞÐ÷Ÿè›ó01'ž¢Kˆ%¹+ÕÙ«ÛñaŒ±îˆ™åKí¬W~¹~œ|ðýv¶Sbr^eÍñ=ëÇOÀo_ü·AªÖcä(ä|‡:&WþW“ïD» ÎuŽ­½Ð¨…cê6reFŽu¡„Î!Œ¼5x ~„n¨Ÿ“q(°Žï”ÍÛqÞ%y~ß’h\Ó|æJâ ‹ž/<Ѿj/EýÚÿ8ÃÙs|n9[°^z#ˆ^c&Gë>WG„xõqk8§®_c{„&ÈÛ`a0yh`Ñ;Ä+ݱ‹ófp&uf’•yì±ÇÉ5›J ©šJ`å—À¨Q£Ò^šiÜJ ÜäÐTy–¹!ìM“¨‹‚?:6@“gv-8/ßbØDy”@ØbŠ‹íùX¢^ìä‘ü* JPÔ"èË ÈöØH¬…¾@ ÷¢êlp6‰Åm<#®)J€ÍºØ,—a-íîkl¶48㢎Ãû[%APóñØ š“• ùB²Ñn(ƒwŒí<崵Ŀp}1>¿N!Üe’6ËñK¤ÁÃ5p%×$ÉeMÀÜW¹_=õRiŽ8ÁT¹‰åBy¼å~ô…ÞP`BnO›xÕõm£,mÚ)ãuVQDp‡íSˆW¨måaôãUÈ óϘ¶ &@ÇC=röó‡0ÃрśÜRº‚}´K¾û¼à½È²†“§°u5ˆ°¦§›ãÅ؜یt4˜?¨h°1’ 1Š|HÛ‹²©)/w÷Û+–JåÎÁÆ’ £ ãHš< ¹ õH€”D¹ÕÜÊóª¼Mþ—:>Ïàë0lüY^¿¢¬➈r}ÈåÀb(–= ¶@z¬÷—1~x4û%AjÊj>õh œ»£­=1kžóú´ßlJ.÷Œõ¼ò“Êÿд€sY´¼Z#'±+°'ù%|'!`$ ± ä.%Ð($²ÓÅží™KS€þöF}¬¾ó<ÆT³ 0}ƒ£‘G±ñ‡êƒÊÒÔI²Â!Å&±XWÍB—¯‡ô À=\«ÞFÿꡜ¶gÅ1šÈŒ"bNÀ|`Ê& Ë3_!ÁuÎ;mñ ‘îlŒ%Ô¤‡]ÐåJ÷ð‡`Þ»hw¦Ò4L1¬îÅàŠ¤â©ÁHÿKüËA˜(H; ·kà{oN±·³e‡“=škCö¹_c[  îI‰Aîš´DxËc7¼‹m•óÅúxg%¾woôùª¢GÔý•ëƒfFÜ£Õ"Ì+úfVÔ¹Æô½ÐöF¦¥k;hùÉÃ(¶p$ÛÑèÃuQðÌùm_”oVñTžÜX¼,~œR…ÀŸxPôÄCìcg1/û¨Xu,î^y¢ƒbÛ¨µ Žâ”%ò¡b®ÄæÔ3Ï¢W÷¼ÝÃ+ÿqÔƒÿ…êŒWä ޤíñÍ»£¼G†äM¡Ž¸5‚y¢² 0‚ÔŒR»ƒþÿÐþ8æ–¢\@ýjbû¬}ß÷ªcwDßÛs›~W|Ò á•8©1Ö4­°Î[ŽÃÀ„í®¯Ú¡/õ‰m®øŸée¶pŌ҇Ëñ=i£…ó]\g´…ïͨ“¡©Có%‰óñ]·˜.dvj5˜øÚè‡CðþÔÖõ¾‡Wï ‘4ö3qÐ{xÜÑDM%ÐTM%ÐTM%±¸ý_§0Зeò L™ˆg›¢—…vÈ•k Òhxeåæ%ìm"2Ì/ÊÜ_õ¡69D–{G7,7EÔ4½ª@_r05Ý…‹EnxI¼nø©Ûæ…Ñ}í«·mÜ¥&—Ñ$r™Ô›‹*¸miô“vÖòô-¼Ïx‚ßú:2ó°áˆím¾&'3Á%¾Aò*‘Ö„ÁìÅÂ)ײ)§Í}Ï}_68FéÏrLÛ^jÙxJƒ¾ SÐ7“ئEl³"Öc'ÔÉtp ‘hæ–MS@_Ɔ¾úÒïk”C6Êú2î®Ú¡ªwók%Ì«§âêørË]¬$ gÿ=:¿r7¾zœIZi®ªx×>UIæLðäÉ›7*(lš…ÌúR3üƒ ɹé“Ͼ®Ú Üe6Q”„9´[»ʦŽ}9?±mOÆ5R^KŸŒ Êš©ë§·ZÛ B¥8ƒk¹ Ònˆo;Ìs'(«1ÏE¹ ‡S^¹û[®Œeo‚Èäúüyz²+MЗág",sbƒ¾ôã|Ì1+ ôe˜ó]ðšf!¯®àð f}‹Þ–hÖ3ô ãüã5ì^SN*|ÌÝäO²Ò‚5¶žv3A_:ä9¾¥ëÐÕtÒþOá¿ÔÖB|Ouô%XË1…}æ`|~9úvrˆõ¸@_³. ú’& ý?à/ /ËÜáÇx¸ #Ò²P3¾ß¸zy”¾Êt£ˆ7fú4{ý÷/|mÃ$™×è¶/@?!r±ô刡™ Ï3K¾rÆIŠ0(¥¹òз‹Ž*bM(Wõ~€ÈÄ2o1βfxЯàvˆÚèéf¡™:Asÿ töúÂ}*‹Ô—óÿòØú˜¨>ﳑzyåáÀ§àøìK(ŠM™„þuL¢½:‚×Ù]šˆò€ZU¿×ih{5­‹övˆcl,#:jÎôä;0Uë0i0×…x¨™ôežÜX˜)]¸¿kž…Ãæ Qgz85Ç}™ @_¶9‹›/éQ±àxD›…®î1òþ'4: /j½Ù[³\]Žù1½$1‚áÆÅ¸ÄQªõJpô Í«_¥¶/¾ÕpŠªJÕ /_Ä1×}éP3}j ŠÖÅáÍô¥Ë1ïGMšÙ*>D}„ù… ¯èWhŽqµ£æ—”üÛZ¾‹mb[Á¡Årö+{VpÂÊá2„3ƒ¾1ð݈<Û³“Ž÷˹Ç®œÁx¨RwÒµjEÇî( ôå\ÌCñ’98Ä+Âi~VЗ¥é¯‚ÉPt”ýNÖ2A_’ß´µé翵êÓôWE™4_/mzgS 4•@d ¬®@ed†ëàAÚAæf¼1ÒoÈ4yqu¦p$=Ǜ˛úÎ>Ÿ•óÉìW½ ]dIJE+™¨®2g£ÒìnÐ…(ƒPˆ wY¼‹[Øaýì𸛎]Ýu ”Jr'åþŠ‚n˜O!·ˆ\,;{9=áÉDŽY¡u#¸Á2_ݤT˘Ë=Š GÄõX¿dämÁ'7`vw@CÍÜ(ŸùV·\¢ƒ˜¢ÿ5NúÇç!¦¢­¥N¿7  ´ó!;Ö|€:!øB8§5¾[PM§¸RS.(ì:çýàÆ…Üg¤†˜[ÞjöƒNK~œà4 ¨HLåj%(“þÐ@I‘vÚ*„[™{sí5W±Ž)ƒ‘¤÷möJ}§Ó!ˆ>Ðývíúƒ &ÀGU .?ƒŽöÆçéj\êkÇF€gZ–©ãLÎÜàfñ½äº5i9úAœ{q 1:¹\s;‘Î(çˆÞù^ÍÃ7P´Ck«ý½ö<y}ÆKr´!ä(@äü&±…ŠˆíòC…pT<¸Kü øv·B«Üa¹9Ö1î<α€ãÇ4OYCêÅ”Ù~›1f†&l:V 2mYÍ[lq¼Vÿ3@´«‡ˆWú“לI‰‹Phý3~]ã§x~Ž¡ì½éñ»hŒXõ“rT}œæÄ–1@üÈMH°Šcy¤ÈŠ‚kÕ/qÎB˜_!> ɵ¸ŽN‡ãŒëáxݦ1^׎ƸêçYÂÈ“åv*ÆŸbª¢èeí<…"$@†'Á¥ÉõCÁHÈE.|å¹ ÊíX€·/y1ûÆGÀÜ]]ðDÒýH•ªkyÃ¥kj'á`n ÜÀ¸Pu²ëÚLm’8^›¿0-t Êøeé3‰«µ˜ ®]ƒ÷|€˜óM Þ8©ælUX;7FáýZ<¹qY–_âdÆ£5]yÞ£Yn•Ça&ž¨^¨ÍÃM¡ q-ôqr„?‰÷6[1·ÊäuT=æIÁV¨ot\üOqücëãéÂàË›9ni¿=æùe}x³çÖÞ¢ ¬[ÌëþàRe/¨}ˆ²´ ­Pù¶{Nöš `‹ÕôÚóÓÄŠ0Æ9®(ÁòMt»ÏIB)L‘O¦Rê±ä Åò qÖ<½rGüVáß!Žè¿¢/PìÈ Æ>&™ºC=œh nO§_‰"¹‘ ×€R¯èÙà<Üêb™ÿ3õ¸ŸÇé7ááCü45=v w»ŠŠ)ëû ´oâ5¦è"I'Ÿ§-'<.V×A¡9 9Þ!?áÄþ×1õ@йÃÓò]­ÑR½ƒÃQÎ-šRß#_¯j#o±Œ.‚v”xæÂ¸Âƒ¦lT#-QSçQòbdˆ$vŽ ÒäñßPé'I«÷W «wV›r×Tº²Õ[Pöf®qß¶z¥N!—É2[®å c¶pᥕ¾:×i¯¬4yU8qQé/‹²…ÎÍ?Œ›ÊÜ×GfpØqÃ.ÛB֛榨"8á¯S®fX\ #2gÛ‹C=Ÿ<Ñç]è@lkÀ%B¢" QŽ&þQ‡ @xR–¡Iä"}®·eÚLÙŸæb—ŽŽ¦l¥DÖy1ÜžÁ‚”egÏüµ/¸œDÜsc"4¡.àWbùFQW´¯pò–èØL:i“Ã5"ØK¹‡,C¬Û!V¹0¶“ì\ÛÛòEàÂù©b—ç1„= þæê¹yu]μE¹w}¬T; e†˜£ß…<:"ó‘ ï}.r CZkk®óÐn¨ÃÎFÓJ·±8vDŽó jSj+gp×W)÷lšMðrpì-Æ‘aR@ÑìArÂcOrÛ²lý¼îöÏypÿ ËŸ2°Ž[hY¦NŠkÁº3A¾×”«ËµÈ;¿ýÏxGõhªúv'XoçéþRì‚PWƒ{YÜä)b;Øì1œß¤$ú¸i'ï‡\Z<8 Â €í%Ÿ‡ß]R”Wé@“êã½8¦áH—+õ €ÆOaÌá˜vTHeáì¦Â3Š”hà9*­ÁÈbªø?Záƒ0»e^{£TÒr]) 7@úú=å8nà:”ôÊÊqZ58åöà Ȱp,·çRŠ\è 0UÕ\Q?œ…šjŒÙÚ¥úv•*¹N¼#žÓ4·oiÞmÒì';ƒÛu ÆŸ¡µ8©:J§AÎag».¸''i7ÎQmcÛ‡¼ù«9M÷UUM}:ù9Ò:@‡G] P@Ŷö¾•ÊÞÞÄ-ócŒw¦Ÿ^õÑ0תýp{`À_Ž|÷ÆŠÔYÉ»trT²^y¥þøxs€°n-×>§Ûðfzìì¨Ãò=‡"E&v­Áÿî<(¨(í•‹—’.p@Ãf¿¢]/RKz&ŽßTb3õS«JÔzø¥psË)™Íµ"UI)Ã358ðGN}ä—ÌF Ž³-¶ÕAŸŽïœ¶4¨ï ŸÛ(¼EãÍe’ˆæ;Þ}Á ýžúwü ñINi›ê¢ÀÐNƒ‡×K(ÂÅ"ŒStP6úcÒ˜UÞ*<^ËÙ “k£eX×}¬¥±;)pDœ†þö)Zå±mÏ0¬€,F§üÆbãE2û*ÖÒÞÞ&µ—»Üß Wå“–™Ä¶`·&QælsÀˆÔlp)\£µøB™ ÊÍÎÁ ~‹ ª]K®œ²Ø1(¡KRe:¤´a3A“Èå,$ /à2qÒOÍNù‹ OP7é-A{y?<¨yá¢Úƒ0~¶g$Xϱ#tœ/|J=ÛÔ KÚø?Ïm¿ËS±eZQ¹æ®8)œQNðxp–qmi~Ø;Æ€›Ê©÷¸>y—¹ôi–¿–µ) ZÏïªäH”'-a…b-L’+â¬tÙ. íŒ~ö.ˆ¿_¦XïSŠOÅ>ûÿ´Üb¦cŠÛØ$õ¥—´3J8V“#tÞÌ\‹³­‚ê>U~8ž 1+Ô‚@K^¿Nœ§ÝÒf©[ð–'ʵ(›óc¥ª=ä±ìr.ê–eCðkpµ›õ5âN6Š V—\€ ìµ8BK}bT—Â[5ÌB7›AM!æ`98›Ÿ«žƒŸÏã;#D©æzO‡¹ºªšøøû4â%^ÀUò±˜C)z¦gùÆtº&¹TEšë`>¼(ù›ê»´Dûý·@D1…ýñ=<êè'çšÖñ\œ#b~Ér-{;{fé‰k¼0a†Ðo.Hl /Ž‘JßGŽi›F/Ã(F¬ù¼Hßž˜å[-â4-–""ˆál‹QY3ØõXgd']vO”;Ûh]è@”íOµÏ¨_j†èºãø»Gíj þïBÛâj¦+úXrM´ú×Ì'‚Ãÿ#i:}IÖ"¯/Ö¡ ÀÁZ-ø.<@í€ùòÅ„_ýà× ïâJìw´¹kªÑ+ØÇj1.׺ó¬ `ÆÊ}A¡O¤QÍÃpZ”æüˆÈ»9î+Bâ±gÕ< ÿ‰n\ÿûU @ü /Íñµã™»ÂC±)õÜëkàŽ³rÌœ’0=x¡’wÀ8ųšŠûq…ÇöÁjrŒ$ÍN…7”}H" ð"6´aqÛ4>Ýå4¡¼YŠ!£àfý=¦áF@IDAT,õr˜WÎnÜ®vÇ÷d$ˆE9’aŠ‘X&ýÿ{kk‘¹¾€ÀÔ<¤M`Ü;™dY2Mæ[˜F%o# ˆyÛ%¹XÓÁÉ®6f€s‰“ýÙAf§›‰Ãn^ƒÄÍ÷büo†ÿƒ^×»a[(C•£ÎN.S'¢mRKö<ȃ¤ˆ‡ã\\éœ]ˆMô§8À˜‚¶:W»8?”¿w¸£¨Éœ›î µ…ÕvóC< ðp *â( ùwÙÕ÷„‰WˆÉMö ¸÷óÄ0‚„Z¸Ucé±P<)ò^‰­v†®5O´|CÕþ’ûpÈô Ä{|‚>.}/â\IÅ•Å[éõedS\ × cìqäGg‰íˆÀ¯©¡Wâ\õsjžZ{G½ž¬T'&®PË’ß ýþŽÿ±:±çô¸VªIìªöN>¨¹}£ßÒ^ß¼É|2(Ù åÖi~ðs¸· ŽFÍãh_1¬"ט˜Óæ…p›‰óˆV—ØF{ÇŠÔ(KM… ¿¡}zà²+A€¶ ÿâ¼þ>Ò‹."è˃›7)¦ÃS†é&BQ0ä¦vÆŒÜR&cFëX‰>PŒïË¢/E~˜.ÆÌŽuÅ:ßï»2Êá`Ä¢m@ÝÐOqŒßVÈçËZ~bmóàÝ&}éžš¥}—ðûX„©7 D/°ªØŽ°w º5„-q]æT nƒ3äÿyuvõP¯gŽäû–øFE`O©RÏ…2`y‹n[Œug` ÄCÅá"BsF¢OñvÕºè‡CŒûÝ=îZ‰-\@_ÊãfÛbæ¡Û±Î˜™;b6Ž«Ãà÷U c[â2=Nq­öŽ—Âo ùÑä¨?¸æRØÇ#¶?']ŠvVƒÖ²}r”:f!®e̱Ž}Ö}v‘ÓY_åú²Ùó8€^î¨øIð›l€¾Ñ†ÁÍ$Ž–6M„C8èË¿¡ïpîòu™úÒw²=–†Hâan¾ /céôeù².yã[> /Ó|å8ÆèÏt3© ø5K#ÂŒîÝDM%ÐTM%ÐTM%Ð %@. ›û¤AÎ3.ÏêBæuÞºÄ_Yqê«pÂÌ'•ÈD‘ˆö 7w™ÚÐ% 7xa„em£Ò^7 ¡—ÅØP7ËQ/_ÇÜÅù`¡ ÒÁ)iQ®÷Œr ævöÉø`±Ð…Måg£Þzóà‡"·Tµ%õ}ÓýؾD-jE.°ÍÕʘ"oÙO›I¿dë@¹£Ï¸\ܤê+»~¤€)¬í‘»‹|rd ‰«!w*äÛ ¹¶ÅgËb‘k{ä–¢\Ä|‰œ8Âo) Ù!¨£ØF‰¢¸1~†ªÐ€F©ñºéàÞžåÙߌ­ÖÅÍcöt²QûÕó§!(R!à¥-Žr‚þTJ–/ñê?Á¢0ê®ÁSÁüQ$z[¦1ù 'À}7·Ï3Er4šÄ06Er´Õ\‰ þf:LŒ€}@dÂ×”ŸIÎA›ZX¡?nK ,þOËG@Z 7¥.&G–¦¥š û |ãí.8%Š©>D¯¾ßKQ$»€óŸâ^X‰åj»ª½.UÈ—?Ò‘«‰ið)ô“%zBÜù$(!Tº»«öN½Y·àóO}ý ×ÑoÊö•¶Í°”1~™¢0à+~Øðyš+x,eoÈ)îKˆãÿI[ÎA‘XgQÿ?{g'G‘¶ñš™Ýxˆ@IH,Hv¸»î~èáz¸»ûánw¸[ ¸BBDIDWf¾ÿSÝÕSÝÓ3;9 ·ïþvº¼«Ëë©·Þ—±q}89)ög¹cÈà fÃüæè0 çÿ6åáHm·„ ÿÁ郸sÃñØ¿»É–ÛÅŠjØ%“—óËR¾£Éƒä‚ÿÂÿC´ '>fcÆ9‰kp¢6|@;H5˜s»ÖÕ^b¾Ííl&Þ ¼r'ÚçÙŽÁaFv/Êld—þfû⊙6Œ*‚L:rÕǼæÚOýž¸Å^%ô%þ{e×Ðê,sRýf5ܺæG›_Mki3NÇÃFS72Ÿò.C]Ü•=Ä–ËQô‰è ·€^‰‘hÿ¥’ÈŠ‚+|\¶§aa&ffHm¯À·iÂâkÁ5(7Õ{²œG}k2¼ãûŸ`.¹;× Ðoºy—C©3µfþLÎ@õÆŒƒÍ`¸oäðî6ÚTœ†“Ÿ×§ëð Þt²¤r§ÄƒÆl#¿±.sR$zBÊûNäñ¯ˆ)¨H…—¬÷3ÔåY…fRîäŠÁMÃ…qÿyòÆÌ¸gŠMu–=8Å#á”m.ú––Oùs—€?Ýþ¹¿¤%÷-%ðç,£b‹Þ?ç7¸\kÓ53ä8!g&î3Î)%üøÛ“òOã¾q›”È”#æTá±nÈTZiܧr‡è¿JâV¥•MfÄ^ƒ-^Pî’-Ø ¸2 ´W©@ÝËã©”ö lÅMæh]ú¯¤Ê¹ûσH»ÜBÙÉ]ö×7k£^Z Ú$8ŽšåRêÓOïq’…›|qâŠ$/Rt§Às­É÷½Íw D¶nÖ7ýÇ‹WnZH]l.íg9q‚X®oû ÎRÓ“²) , yGàˆ\ ƒ¿V` ˆEœHƒC>À¤Ÿ³—«OùKæ^’|04éçÛ»bÙ(µÇûÁ*šµµzÀk°‡¯Œ'#lxÐÕkÇIÿ@D„\Ei £€Êmˆ ~@¹‰nÔéijÿƒ-'WÉ¡•y?}«úvž?ó/ {ª “´Z¸™>6ÛþË"-›ièõ+àD3/™H‡9—Â}&%—Ç $ƒN ŸÖÂk÷ƒ<€ÒA•’÷éh™”zt~µÎPáùškó…w£PÏ®ô «ŽTî=­ÒÈU› (Hž«£Å½9BGU{ò¿8íì²ð:±àå~ü¤‘¸è®ÅàDþÏX£d_î®$‡žÁ¡]ÙŸUj<@±ƒE ¤Äm[ ‰ËU€ŽÚ•Oº-°³·V: àV`O‘3PB!ÕïFÒx‚ì†ãV°²T†xŒâ±ÇÕÌWÃj.Do&ÑæŠ·ŠmÊ%­çF¤ãƒ*Ÿ4\…‚´¯h³¤=°õ Ú²nKè€ðyòþP®;@óCf@ãå`9sš×¶û®;ö•ëŸ4¾|jÛò¯ÛǯíMÖ 5—šå9"¨o©àÍ+­_ÃZ™ÿ-?ŽCš¢îäy>î<0'°.à¨.˜Uïu!J¼žlÞ…r«ZŸ€¾_Ú|›ÝŒ×Ôš ¢½•žÙxøú>xßšyd kbÆÛ8p¤]Jךþpo‚G{˜þÿp1WHD ?ÔœÎûoå½ÊCZ9ô%Ìi‰xßaÆž„GºUóQ¨±4œÙmÎ…÷‚gþŸ„¿¬4è,º¼—i+³¬éÐjç·ÆK¨ËÏmª:„i>*äzaôŽCK ´”À·Z!Ê×Mñúrù0³ÃÇWv3;ÒkI£ù% NžÿùwŠo‹äA*š¤%÷‘®×b(Ai`†_ƹDø9m•œ@qc¥q×¹w§))q~î™¶Ìt\À.Ì+À\…CÞ#Ë^¶3O™µf>nJ̦€òSê4™Ì‹º.ë¨íÕ.¬{ ÐfE×tÄÆßÉÎmv>_d‘.¨£Ÿ|×^Þþ÷U’Ú³yíÊ•H‘Ë·ìÄ)Rû›PsU$RÂ:–ùÊËQÀ9ƒ­ö.ç=/ÈO².(Ö•ÏJ”Ö/Å)é¨G¦ Sé¹zÝŽ€_ñ<ìÉÖW×ÿ'TŠhýë­B¨Ê%;Y´sCïÄ»¾aœìgzïó^ŸÔE’tíÚ’€pó%@JÛ0&{›Ç\~@ÖûÕU÷§¦SÏS=P× ‚‘‚:Àaí¨7¹8øV7êçÓ'ô­ÛÑr•'GgÒ:¸íMòGá88üÎMòÃ-è%Î*m\5Š#_$¥>„&Î,)6\ˆü-K9kˬÖѳ4‹;_”D‰Qù ¸öé’ë’8ÃX…Pƒs.û| ˜£)Z»°PIÅ}/Hp`—ŠÆ}èÙ`ôÈ9aÐM†d_r ôÔÆ$ÊÆ‘®1ëF)ÛÀ³w)ûMh÷dž}kuÂë B¢'"×%>{ÀqWŽæ£ÎD=2nLq¢ü8˜ðRÌrYx¨v=Êk—±à­ŸÇ €’ä¼»¾çÜôìL[¹Ø¬9IñÔ§´ÇXXΘíŒvtë<8TýUÔ¥ÚðOóÏüH3ˆ–8ˆr•è!Gâ×¼í¸Œ“Üoê?k×^ÀÍ“_ìMm‡z r¡“¹îG“ÝÚ<[XÜlxµ‡Û–¿F8'ÓÝLË®a¨?ج÷Ôœl_mûLfPÝòÅ› WXpïL\ýÑŒµƒÃ±Âs–sþÝšñ z²êÓrxgÄmi«pn}ú›À¸3kþ·ç¼Äô4s³&e­ƒÉû}•qæ4D@€à™o‹^3älÖ›‡" Íù #ð>À9’ÔUä³Xùï´PëQ€CúAᆶs0S[u›ó\’qàzžK€¿ÅÓ'à?‰Ð¡ú]x~ã{†æá¤ ĘY-ů §Ö/5@ÞÓÉCñ«ˆPUgÕž¤µÌXzÃ…û"ÝúÑ!yÚ8 [:ä¼-ä¤]‹¾!°õ;@Ý7Ø—²yÀ¼«zBøI´ãqÔÿ%à s厦ëyÙn^o ¥+ö¾”÷fæ,ÀJó{Ò_n¯¯åxžf×K÷â.êÀÿ›Ùžæ¾ÜÁÖ.Ñ#u¼k^Ú¬½Ý‘Ý—´¶0b€pÊ%%»ÜÝY .fŸ$˜Ö"VDÕpçjÜ46–BkfÆ›Ûë6µ}y©äa£=pH‰”pJŠÒRŸ·ómþUê@ýªHRŽQŽC µÝ8¬±É­5Og¾ÞÀ;4ŒâÈPw?¿Æœbd-û´)#¥;ÀõÝ}³f ~üZ”»EEÑbh)–øK E¹Ûï\-¯o)ÙPÚ¸¼TnCIú°yÛ¾ 13“Z,§Q\!QZˆt7-²tM±¹¤…x’Cª'nZB¡ŸÊ©ÞÀâ¾È¥ÍâÂü\9R_mj7e3‹õ Àùk$"ðHX„/ ó(H»!ðâ&³ ˜.\«­†Ö¦¼V¦¬ÂéÝp³Q.­@‰È„rÞ%î÷¦í‚1%ž qü?‚hò˜ë€8?[@nZÉð;€¬žå÷µlƆhƒøW”ÛlH› \ iK)åOINs‰Óð9«Oc3tuô3 À<¸ÈN„R·Õ¼> #°É]évŽéeÚ‡W®È+£†}ÈÓÅjú™|—¾`~þ¥…=•» ?r"¦¦ÓצɿBŸŒ!@F ‹H‡zgg»°)ÿÙ<ÚæY³>WÍx¥‘€«êjµ4¶‘zQbM5ŽTС‰€<)GÎ(K:>•2Ð@¯[t]ÿpÚŠ¸#æ½Q¦¦%xˆáú9oÐ)Àr™¥l®[öÀˆ M󇀋’ºÌ¬C›zÎW€¦ÆS-àú\uÓ {ß"g”Ïqý9ÙNýì4Õ&{XsHZ=*®Æ&µqwžÊx" ="€_<“»Äœˆ›úÒ'oƒ€þ1ÔCŒ“Þ3÷A&÷Ø¥>‚D'ÚpËòû©C‡8Mmb~ÚB–f¢üo´q ]L»:Ђ˜ øl€º}­_òÇÎíF"‹º/^¥­P®âÞ›yg êæ@{XŽER*Øðw”¶µ…«qa³\ÍÞˆ`¹ÜlØØÙ¼×þ#sûÔ.~½²5¦ãÅ’"\tðò6eû*â2n/üÓÂüã€çZŽ=êÃÙÖöi«tì8›¦®Ì?A»Au–ÙŠþ qE©#%Üü’S;6;Íto|3ÊOECî Úè¿0š ‰û¹ÓòÚ«iLë`î$î† ®¾} óÆ÷”__•#}x àüpf®•_ ÷1æ‡Â;ÐÔîe°×…‰k.©&ŸªÇA´ø ý4œq q%p#ûŠÃ’±º0BÿíñŠà½d°&ì}éï;R–W§ÃqĈ˜†Ã*‡‹|iå­©ò³‰§Ž†PS¸ú\†´JÊt¨~!}@‚>&…ïý˜1¨Ç°&Ö‘Ò§Ô³9$…˜…²=sÏiÄ•²E§‹!ŒÜή1¶æùS!àÞ]4»®÷¦ÖÏ/¬º0…½dú’WÚN6äQ&*s1/\ÏáÃâÙc(Ûûðý7kÚƒ»XëÄ©f)d[9cž¤7 @êôÇü;çü¡ôµKÉû:´ýE9¬ëFºÆ5§Ç¡`«ég¥¹V¥ÒMfu¾Ë;€Èp8‘Yˆ±îzïór†±n—Gn³k¢1Yãl²oG/ªÆÓ€iPûÿ! ­õËo|_¹ƒcT[j¡–h)–h)–øÓ•Às0Ç#Wªy•Žˆ´ Ô¢¦…â%à@ß»ØÌ¦‘}µ`YJrºt´a/Gf†šbÊ¥¹°·ô’fa‘¶z>dääªYÏ”Ÿ"è+Oq”‚¾eÓ–¤ˆ‹ÃöªMþÛ)\ >è«kº¢}ØŒû´%À°@_É`ë“€‚ G˜Ð×¹ûáeV i ¯ã o ôU ŠÄøí!™?ˆŸôÍü[¹œ¡ÅÅÀ~½úÊ.À7ú –$-ʵ’,½ZqBA›gÁ#å@_ÿ;l~tÍ> úÊÏ}e—b®zžqÐW>ª¡Y§}(’•©çñÉb…Ç­âšFϽ£8¢ô–‘±Ògï†ÛèšRA_)f9 Pܺ}½qY›Wýßïq ÚM¡•þã@_ù.—iŠ›È›í¦oeAß IOW7i,“ç4°Ð…)>i+\ ?ž8~+”"-¬Ir¹øà*ÙÌjOÉ+´.¬žÖzúŠˆ!éÑ<P&ŽI¾N.¶ PáGÀºäü>Å!ˆ”ºmБ>Þâ‹~l¼›9aTúj^\XéÂLGte@§À²ÞY¸v$€ÚK€`#?uÄ&H¾¹ÔŽ«Ú¥oòOQ¦«™!”QŸÌª8ÇÛSÎ74þ ¯€'JŒHެ¸ŸÜ~Xm‹1ÿGû0¤yÌ ¸Pãµò01Pr]'’§í+:ÃA¯~qB~ ópæCó@æÛ IDgd§¨Ž2â.ÿGÊ8ä· 'mOƳö”KyZ ¯‘÷´ÌRfBöo‘=ͰuÒ_!ˆœÐXS*€4K¨M¸ÆpZ‹|ç&IòŒ«}•Ú$@ßC›L6@½´Wûî]!|_sSíÙŒYû¦ÿZ=”Î~;¿£y»%ÿ‹grpÙN°²äµfp ¯<ÉškæŸK³$"&}-€¹žm‰Õn"³6†I`¢Mwhá³Rзöë§CK%üRö7ÓÎ}ªVm–5ínñÜæ”ƳèKŸÐîºÐÊûè+Ú™¾û×Ú“¬2Þ@ÌÖsJͦ[vO»"ÒAá²ôC)¦Ówëf˜d¯Tèj–«}ÀìMú>I±o¹yÏ's¹1Z~n~??·¾ûåf ®ûŽù;[øœøScnÚ£è@©¥­Õþ{Æÿ"ÕFµ ¯aõÎ}§bÔpÖbNšƒÊ¾ºApõ¬äcÑZ,-%ÐR-%ÐüÈ5?Ê\£¸Ôüs}RÚµÀ?ÓŒž™ÕC´`âêQ5¯zÔn›‚eÿTCZý/‘´º2V‰ÜÝHvc¸e n£¯(R‚â“®êÎIrW¬ö³ÖòoòÁ£—ÃM€@I ÷m|é[>½4¥ç‹#pa¤\, ®æ_´WmÍvHñL%:1\ìú@ŵÞøÒ`«+ÏŽ´‰FÚþ$IÀœ¿(½˜ùíá!ÁX/p¹…µÄûí! PŽÅ)<µÞT’o{Wç›âDQy:º¡ ðñ÷°|EØÖnJ|M1E—rQ„DÑ¥Ælž;<²*Ýò4¯x?lJÑZzZm«VT¹<×+µÙëké.Ué$dPë—Ù6gÒEC4EÔ…Û–½žØj3¹?|Ž^¤½êï–A ôrË<ÏPIÁ™Éî`ÍÒ.y¸ŽnBÜÀ7awnz:1ñC0@”7]Lœd¿Ñ\í”{µöڗɬa’rç𨠫z%æ±€-×NÏvŠ6õâ”n†“˰ùîI]%Ç`åÝ•¯»^/`]Ê+÷bl¹šoW?^R0}í Ž9c¯1[K?„ h¹`ó’GÓx&ÞCcA´©î’©B®`ÎÍ 0»d6q}cqLvÛ˜}ú¤ðwÃv£vÔp,X‰Å•‡õh¸82KÉq ÄFû0à*ˆ¦­»S"6…pWycè›Ùñ]$þúrŸåÿ?(°t(ZÆwÌÙ-ð!²Z~ºÂ ´+­›òÛž=S{·cªoe@Ü/i›±–u¹›¶wãd !7v½ÜqÖYJÉDãÏPŸœ¾uGúp‹ãVb,|q360rˆ#Že8m­~u ]\Ó’Ù/EV"‰bѵùáv^iÝô#‘pïÓ‡´ýÒ‘|å2úVŒ ­OÕ÷kˆ³²GmüÉÆY§q Ê?ûÀA[oÄ9©9¯_ØÿžÌÌ‚ˆ3М:ÂÞŒ)r3.Ê÷«k¯!eùWé&‰ÑYõ•ÛèðýV<‚ßb˜ÈóKíï!Ü4{ÛàD5¯½Å|T¨5¯òÝkÂyÜnÉ‹…° òÚw‹æxÊ7Ìo©œÖÊ¢+, ŒÎ¬Ó¡®?&JnkL޼æç°Ïª¼'6?ãš ˜¢1ÜLš_žåûÿiëÿ±’¶ò¥ý8Ö<=l…×°ÒfÎ- 2ëŒ}9µõjˆUÌ”~tà«©„œWC \·þêUmWm–ºµëŒÊékü—rÕ:žn®P ­-$f*æñpyßC»ÔžE}ÎÀîBpßkí3ÌS”ÙÊŒkZ‹þƒQàÆ¡OHkPg¦^bHŒ™&»«µìN;Ò{l¿¯¹ÖÞdè5c+bDmÐÔŸnÎáÆ )pÈ_KÜH?T¿·í~.†;uWäfOCDÉÚãÙCÍÙ#(o\ÆAÏ(Jíæ¢Ù§d;e™1™´œÜpN 82Œ¿8{ƒÃ2ã¢ÔŽÈ˜fôŸJ{‰s<³5äPkkr?K;ƒ}îÂ8ôº˜³Yq)£b(¾[‰œí!¬±ºë0-F£c¶¦,Ñ¢¾§új¡–h)–˜å¨ Ìròè‚¥æ:‹©™+å€I ö?åøzÊ&½R°Ô›%º¾âR`–’þCFN~&ÅùáŸj¯ím`.M¹W%¹³“¹Þæ“ÀIGs‚#Ûq ñ'-±L³€ÇåÀqûJ¸À’ŽÄ-¥L#IÎ/éž´7 r²Ñ¢^KGq:’˜„4rJÍ|¿CYŒ&i1É*ËPIÏ©KX¨„$»¶XCÆríÅ;$aoóÜ-Üx»Üa³Ïê³phf‚¶×6%òÁ†JÖ/XŠÃ]ûr,ö㔽ÄK, æßW[×.üJ½®§ÐIqLYª½=ÅK‘N)îE'mfÔ\½Êw>–øÖÏ­¬Ürù´[e¸°Ö‰ÞS/5IiŒk•ñ­¨áºøtËUT«ÔE¿räx(Lê3Ÿy‘gÆ*ºêâµO•²øàÒ.Ò!XŒ=³®ñM¯Âi®v²5“ ït#™Éë’Ñ Ø/€ÝˆÍ¬8¥}’ü^—Ÿ$ø+aý  7õ;³áœaܼ-iõ ¿k7Æ«[À¤øÇ'q‡»úñWðý®X¿Í“8›Š²¾éù5—øÑbf;δs@JÑëSÛF4^cz-^a"®dÚT­=Þ=eg>6?äo±Ê‹Š©tE—W¶ùk¯šë£ ê—Åí¿AtB±à°T¿%˜O9)̯Ý™˜wÚ@õ²õ"®FG›SŽŽÃ±!w²9´0Ÿó2kä?Æü]d—Á—û,»”1Íå¨9ÊîÃ8½1àYP ¸¿§{'õ"S¨ð ò¡\…$®Ëì!Î{îA›Vß³ ’ç³HýæÉŠmõ/\;F>ö»G#ùË_ Ðúkª’ÎË„] `ÁbECÛpcïISÉ~bÚ|ÀíŽ 9mIsV͹€Xíùî:S¥CË Û$óW\ˆ’E-Ñ"ÿªÝÜ“ÔóÉÛ·¸‰§ýOÍê厷ZsÛPŽ{¯?Œ2îƒVÃÁ•(’,wÀçè ¿C¢ Rè˜Âx³þdµ“ œÖ!(úÚ.ÿ½UD·mt!€êQÙ±fÎ Š³9\…€ƒ®ÍÚ¤9( ¸'sDôÉì¾D¦}^Šœ“É«G½¥Ì†„¥·(|î¸4;Ø+þÉ8Óë0ß_sŸúmóhaÞ9”(Z½4¿3ÌêSxy@é²ÔŸñëb{@aì±ùÈ0¤JYó¡/h­_ ý*?´ÊÇlMÂq,íÚÖi}Ô‹÷È} Æ¼6oíYöˆ@ýˆúø)ҳйñ{þKº‡w¸kfI²ý·¢¿Uðö*éÚÓ‘´ß•ñû˜qâP³Üï?rXtw>\½dº1>ýhNeÌÿµ˜‚5I†·æ$™î戸’¯Ê]H¿êfîGwþþ¯¤<ÅVÿèÙƒÚêq¬“5—Š& 6,hÏ¿iQdé† õQ†´z‚¾­ú è&Ö þºØ†˜Í™SÄLr‡+Åï¥ötÁ#«]7–2_ûý1%òEUˬO‰ì9g#ÏqN'Ô×™)š^þX¤©oŠó©)Ï>èáÄd¸ðMƒò.düé¸/%ûÏÑ1€MIÚ.6Arâx¶×‡½'PþÑ2[æà®+Oë¸vŸ*ËSëùr#`ætj î@mù6ïZ¹Ðµ·ý]Ú‚ l.CrÓå8ÅQZŽ´A.7Óø‡ ¥ñµý.ÒU%\hE?qqú$ñYÕ½B0Pjæ‡Ì‰Ð>á,Q?SOšÖUY[ÐŇ½­ž²–.ž“)¼imVuâÛõý*kqáã9_ÉXÑ«¿Å ®Ÿ”3_EŸÐÜ¥~'÷°Àúá¡ý`®Â«^“W^U§m3źQ˜¬t´°jáÌu”z7µ·ó¶O)T“r#‘»«wmKÛ×5â7sÇX?ý’Ø pïXÚù›Þ˜ø öŲ•ü\Ñ@@ ŸÔ¾Äu*z¹À¢alÊ:¨[r´Y¥˜€Ù—sµúƒü]æð[9/»ÕÙkØï·ÿ޺逧–ô-5žËC–£´ý1asçØþH¥«û"ʾ»uô~\o÷œ¬±yÞ,‡¢±«2åû¯m^Ä•|ÐY\—pvª­”#)Ks Ö$òf<€Ô†ùwÀ/U>¿EÑÄ­vr?÷Šê†Ž?×¢>¿¯0Ö Xòg#º5ÑŒcâwflm>œ_Äö‡Œé×싈xp¤>ù)€°iŸ\Á‚X‡pØàÆŸåè¯RN¥2p·}Tר¶A/­}þü4.´¹ëÙdà`Õ®ëàv5Ó–²nú±°ò©%ËÞ¹kîY#ÓÊŠƒÑ †·+r^®·U *@Oë‰×™>lu³"N|×f­ƒd¬CZ‘ôÓ!mŒÔ¦¼ÐÙý™#ÖŽ…Œöè0¤ð~!l Xz4ëœ_â¡­í¦X­È)lÿaXÍkq—УìcX ,T° G–eSIõÈ?L½Ÿê8òÍ ÇÓöU¯^ßÉ\þÆe|Èš+?Tfª§$¹¾»3@»;¬Óø'å¹:iM„·§/Dk«º)»Ÿ3Y€Xçé4?7ô=$¼Áæ_Nþi£…÷,¼-ñ7£ßDѽì†öFÄ̇?æþj]'Jç^ê|wæÃGóÓÓ`´žµ«½†#y2×s³Åp§í\æ„5ÕmY1ŒôtGj«„kh¹u uEHiœD i ù ÚãkŒ³£HvSò¶V1Xš©ŽC0ÎÕü§1ŒãÃÌ!Œ'¾BS{kªñ”xЙ±Õi/›õUúM¥uh¹´Ts”ò¬7ÌóÍ[æôïÞ5'~ûŽ9áÛA浉á`1Gß^9ñ·&6ÿ74ÈÓiäMy¼wô·¦‘´9ôÑä æëi“¢(¾ý·F´…~ý†yk’¶-ÔR-%àJ`Ž>îEséói&¶?2‰k§-ìU8ßå÷3kÓ8§i*/¨F©ò±¾ã 3¥E‡–XH ›|”<šŠèd6Îq©¹pn³ãì¿ÇÓçRбì"·˜;)$k.u7ÚÍ×TxŸ|Ò@ÜJR*ãÓ•lRuåKJqýêmT[¥çNl¦+‘6¸•(uÜf#¯ˆH_¢ké§³AIn¦Oçõ n*T"xÌ, Hñ¯yoVf¼Hyå&Wòx><\I®<%g·)Z%{F,ˆú¦8ŸÒHš¹ECÂ< X[&¬ X{·}4ù“;.â¾èW—Ñ&’t[²)°ŸÁÃlÈz´”oÂñwZíùòFTRƒm‡sâÀ _böG¨Ôúl8ÿçtVµ®KBqw`-ä¹®¸›F |Ëé´Éâ>#ÛÛ¬žÙÝ_®UÆèÂ_D¹¯Z§C…8I¦ÿÖùëÌ謹 !NßÌŽ}·œ½]ŒǼà_¡õ³¸ó¹É®o>A¼@@š½ÛD…ÁÁ3öËhbûyÌ‘âä}ÙÝÇÜIf=ÆŒB«‡°Ç× ‰X ëw1{¥9,Ke† ÕzÙVæ%5Ò3c´²›ýÕþpeGTs%Æ"й#Ú$Iº¦2àkµæÏg:ÓØ!Ý)*´ñ§-î%o;R æ“SKµ—àü>Fê–5Â"™åC‘”fãÙ¸ÿbtÐ"ñçPD :8v¢NÖj<ÏþÙ1R‡Xp;º“ö¼q^ÉîeÆeV·ÊWsÙñîa¬Ì.Í œÖйõl¹®æ”ÜitÓè¸ÕÚ¾¡žö`ž#¯Ù=pðûœõÓ3"ãLáCÚ8óg‚$ƒß?|TÛ¸Þ͉°ã°‡ÔqâazܽœMrvKÆ;O\U¹xI÷uè7n~JúU²§®á+Eh®ßã㇙—&ühmÓÉìÐ}³E·…LçZˆÇ5¯NÔ ñûÐG¿ýlAÞYd¢‘§º/jó(0øÖQÅÓ‡¦r÷kC¹åÇ/àΓö¨OÛŽ¦ßü¿Ds¼aý/æ\ú­É…Ï\ú™sì³J§ÆÙó*ÉÊû£’Û˜ÍjþܲuVÓù£Å×Ñ-Êš“7's0-N·4ÇÐm”¿¸ªÎy¯^9—ÙÿÔ¦½ ”ŒDOÍ¥äAF¹øŽÃ©R¹ùqµ JrÊs6ÅÕ»®]n±šLã ï@æí'«6NÖ¯@¶4Ìç”Ö†Ú‘6"¢Iü PpòO¥x$Xè:žS8#…diâ$’qT4¶7¦ ¥w8%{±÷åÂÍsÃá‘s¤<0ìÎ÷¸+íÚ$ˆ,Ûi¸O£rW«“aíUÜb>’Þ΀’l†|j8Ê·Eæ‹ïTçÇ9p(ò)Ԟˎ»µ÷Û€ŠÁ«6 H‹ën8µ™u€“ã2Û¬­ *EvòwÊäl¾<îÑxzí1m"gl¨q¯qV)4X2à hÍ%Ii½"¢¢ï¦Š›¯™¤Mš¾Is3=N[,’}ª›"nSFšÝÉæL}LäèÖxÛ½Q\yûî!—™8ŽE® < ¨4¼®¿ZÚ2ˆ³Hœf8 ·‡“k²1 ?–ãë·¢2ÃÚ{SÓ$~KGi;©:j®M¦Ÿy?w† fÍIîé"´3ˆR”t Ü–¡QY™„²š©Õ²$d7êÍ‘äœÆá”þΫä ôé²Úî…öæÜp¼4€j…n*83ܦÙPR×¾EÐoõ$¦Ê»-w0ôjæ—ð»9jlóy4²)6ý£rëÎQîð@;Þ—‹©ƒ›³ÄWø&àæm¼±GÃlp­Áô-g®AJã&ÕGîƒXJ ˜ù.)ýÕ-´ CˆÛ4“_Ù¼Óîk€ŸyãñêµJ%[‡¬âp—Q_ª" „›¿åü Üßëy¿ú{¢tº3‚ž›—@(weûÓ¾þ€È?iX4CΠ½®Q{Cä­5˜ÏÍ,™ÅIR|*s€Y˜ü~#NdàôÑ…;̓ÜNø=è5æšÚGùêáÉè€Öí8jÉZY¡ŽkÙÊßGÙ=lt{$àÌŒGýkÆà°·4^ÏñPi6F“†ÓK=ò·1‡eXa»LÝŽ„K_+”&¸hîš™CüXz™•èB‡†N‰ö 8‹q û¢VÂä.dŒr¤¾uEf€9ά`om,Éï¤ÄÁ¾D>íÏagYí–2a\[•’ðHâ Õñ´ùC Å^££uc­¦úÝàì]3kÖß>2Ý2ññD‡¬¿µ‰cÀöázá‚lg€[­ø VÐöß ÌüJ Ú…Ì'¯7xÞb|ΘÆüåø0–»¹@6Pƒi—5Ò3+OÍ ½Q’\ìüÝV§Ä[¥“zh0vÊ¿ ÿÏ2<@?+®i;˜‘5W!_ØÍ Ù4=ïß²œÏ+WŒ´å·PÙ|VŒ:[<Ë•ÐlI\‰|ðÛxÓ«u;shï¥Íº]zšÍºö6Ç,4À´ÊfÍ{“µÄø}èÓ)?ÛÞgY³9yZ»K›ÇEÛu2Ÿ ×UIãçØöžCÒÞ.WcNè3À,×af¶*^Â2c±{ýÉ2Þ’ÝÿZ ˆkjN“4Í·PóJ@²òþ¨äswþ7óè8‰ôΙ9\^›ºbëÂÍÊÓÉŸK¦1³J䀕LOöáÁv'Í+Õmb3Ãû‰èª­£{\¤Î½©§ÉæÊH”æûæN+•[Zz¿„ ü\èYŽk0-®Ü†ø×d±Ÿ 8“ƒÅQä®ÎJÉÓvlbý¶íÞ­ô²M•¡I®`%ŠÄ½Ô\cƒiÓÑ×Û¢H¬GšÂ™nˆÄKšÛ'Àê‹b¯S²'€G&«0å ŸÒû²;•FìªrÍEr2‘¨„© Z»ûׇÈì–ÂÕèÂꩱ£«6Ì!¹«À~yÊK@I’Êmctˆkfu®a;J’jÏw§¹¦ ¨gý.VT„dÉÎ1*°¡ a³'CN}Ç _‘“šXâ³rújç@åÈ!4ðøÚm Šyk3ـlj9 ð¶†Ë@ÈAÔSáx÷+— À0‰‘ˆ®¡:ðYIf¹TâRN„[Ñí9¾Ñ¦’ˆ”Lþ2kV­¨¿KÓ·èU€·#…Dh>PW­îš ·$>æºq‘y‚¹®ð8àXý6Ø.nŠâ«[ÙÐ ˜S»*7뀤2gÞäQ™ ¿¿‘,Sç6y±Í$8¾º6ža4^ëÄ*€&ÕÞiƒWfOµcÿÑÔË©©ŸÆK”‹ÄZ8 Ü÷Ók)$›ŽÓ’su…¡²YƒD¸ñK2ˆ5Îh.î(RíÅVràðxÇñ|8° (9'#s&y ê2Kê¶Æ»Ìn+âÑÖˆX˜$™¾ºò<}iϧùÆNÉüήÙi’[ª#‚3¢pŽSZÀ¯nµ¨ ús®ÊÔµ2ÐÇv¡~“c^Åݵæ‰C™ P†â«81eÖ°Á:„4Ù}ì±hQjZ|ÆGÂÕ»8~%ócÍa€b“;"! õ™³–Q` NìÁ™p"¿Y0.űׅóTÉ uV¨ž˜ixÀ\G;XYÄÌ_'Ì ÛÞe¤¼ûІMÝæ.©ØSsÃ=Ì;™Ü­¶ýIÖ´ລp >­T.2>ist,ኖ0.†¹T’¬óìxu ¹†û”S™´Pl.ÅnÒ‰SYÀ·7—~@ÚÜôÓÃ×¥:?Ýx}ä®×ª?êÂQ;깄êO³¢…äÞ‘°#ǹ£ZLÇhLÉn9ŽÁt‹]+ãF° ¼5ÇïW»OR~z÷4e†sü•ÈÁ½°Õ36ÈŒ“h;Z×mXw‚9eÆšÖ]ý_ù¹•1wËpýlo5¶ŸN^6aݰ“ g<ñ/øàià˯Ÿ»È1fС ɉ¤ŒSÊI}O².ùò¿£¸1é[-£HvÏÐE–S»+moQä„Á¿g/#÷!âV19è¦ÕïEsø­Ë+ï‰Oh“Í™=X¬Ôa¾è»ÇÖM37þô…!Ñÿö¾y9ä~‰çyÃ>0S‘ÅëHÀì…ßd^œø£uS7Õ\ûÃçV\ƒDIÜ>jˆ‘˜…rTæ§A²(<Úv¾…Í6Ýûšù RäýÂÄ‘æôaƒ­ÈåcðoÁ–n:a®ø!˜ž7ÜÜ7æÛ˜ý¡±ßY™ÆŠ#ñ¢à~vÂpåõaÂ5x@óÄFè[TgWïWãêé4¸’]~ô|œwûñm –Ÿ¹²ª¹ÆRòtñÔ¶uTäägÊÜžÿJ[*FîšÙÉ«£JЏ¤³vm:†§lQV¥Í9Nuq5­à•¿€Xþ,`S)þD j褸ö} ÕÅa\‚[Z(ScvhgFdÖ5âØ×ŽG`±¸²¦žÚždßêˆAcñQáæ>ˆüê€$XÔÓi,ãÆ}Mô1NF¥Øuv'_\C€šìþöpKöA§Ù+ÑR$õL¾£™‘;ËÜ:}#Nèó)‹‰áëŒl§ˆ72-Wú^”;Ÿ òâüo¦[öHçU|JI×ÚW¥ û›’㌽QÈ­ztë<âÔë`%³0²‰»Ç¥Î ëX/Z tyÝÃhÿmñ½ÎTs½3ÏÆ‡bvÕ•ÉüüÈóK]5d÷%|ä¸ÀJCà”i8ð`9·†óŠ=7@t‚Æ"É-Ìó?© Ža<Ë@®CøˆZ=•ŸuË÷ãb‘‚T)ÌscžètkeWʼ,YNBõ6ó’KÜúEÌmƒà­ßAÌè‹@Õ!¤É”šŒãFþ­ípëBÍ)¼™âaÞìÆ8ÿk«wQN/,Éô—³k»Aƺš«ÍÒH6Ùõ§Ú@U`³¿a¬u4±Ý{Ö8q¬oÀì´mô^¾[2¶OÀü ô“Ó0×0v|D{—‚VÝ’bÁˆÚÌn«ßÕ:ÿŒÌV ËÖî›]Õÿ9j»™•âIæþ·;[í}˜s6êêQêã.ìÔm~0ÏE¿Ð¤C=‰‰(K¹ÓñÒJ¦)š@€/a ™9Ò<6säËûW{¾$;Ѭ"ˆ Ò˜­½m޶¾jƒod†™ Â<¥@IDATï^§æß¡´ƒxY ›Ù¡æóz‡ñpãÒÏáÖoljX`I¾­ã&H°ZT=zƺ6„]—å_EÉf'³YfйÖ2óÛu¥¨TŸó×ÏS–ëT¾Ð4ì!ù{¬À©?ù;Ÿ¦Û°2Ö7‡¦¨ÍÜP1F|£oÍhe¹ãÙ¡Ä{›vp*öÒöV1ÁÈ3Àédí_{/¿‹F>’É;»‰ÆüÆáH3Hëƒ9JëÂIû㌩¨ú ¨”© ì8¯åÖËZ^2ânÄ.ì¶Àb¦-"!üˆéSL¿¶ÌO»zÂaþaúd+žA¯@à1 ÓÌ–ó-dÖì¼€ùÿ«G|j¸nô­«w –¤glî$ôëX«à­oÛfÃ. šŽ¡h†§~þÞ<>v¸Y¤Í*§¤\L…`»‘ú¦d­ÍêÓx,úw´e¥MH\T޾æú§ä÷ ¼×E‘0àªÒ&ô'ÂL£Žˆ ä˜ó@¢ÆunüãÇâ.G÷é4¬Na£®Ê–#'[×ùû2zµ‘ÑÆ_òÝU`ò`ï€D@¬¸86¥å'Û¥U|‹~áœ5 w$ S‹å¦¨â q€MÎ< 2î–f«ÄŹ£÷Ž$·­KK²«E’+qàwþî)¢Øv¾Å6ÜÊ8q¤àšÌæ&Ij/ƒ){G*om–üͪósOµ½¿Ã‰*²­ðPm ¨#¢(~Íž¸U·Ám@ÌhÚx5tãÄ «ÏW+BÅ¥/Ž+)L²Šnœ#O'ÃZN—†ï‘üêÃRÝ÷é†O’Ïês7ŠÛz®Ù‰۞²–rËŸp³¹v¥å"`@ýá=6ââJý>ìªsqmëðD¾¤L‹ì qŸ#Ëñ#@lÉ0§ôtÓÛ\ÀÕt'¢EauÅ^ô n0Nµv7öIö¢“»¬~zìgZ›%§ÎgÓ‘ïÖô~ÑükfR·¾¬)mþ«Aõÿ¯õY5–=$xÖnÎaS{Õ>ß‚ÑßÄœ‚zbF·@e«kãS„´å¿ÅÕ0E”M · ^Aù– ãyhž¿2e\TçkòðUQ_`Œ‡÷z®?â¦6˜Ô»¡±÷1Ó´>ÝØëoJ£„¯V™²ƒŸÍ›>áXT‡Ñü?ÏÜ!E°§òV ¦öNäý2z"^E‡|Dzc݃¹Þ0vnëéq='ê‡Ì¥¸˜³J&»€ã€¾ qŽ1_†nÕ°¤¨bŒ»„çrü{‡ :„ÈhǞ˘›¢[V;?`¾šïys@%²°qî•Ù†a™›¦cþλ¾‹îM¿lŠÊ­í\<­%ýõÚd<eNªDÛq@åæp…So˜£´ù¼},`Ú.“³2}o‚ãU ÞÄñ*X4t™]ÌÞ ,nVï´ÿó#xaë7¦~ªéø)qg úe¬™¿U;# VœÁâ,Þ.âõ»ô2[þî…Y`égSÒ zÉvÍÑ}–3}Ût4ƒI÷.À_)x;øæ'€j‘8zŸûù³T».fŸÊÛüæèÞËÚ÷>3~„ÉrÅ`¶h‘v¿ öí‹ΦQçÚÖæÈ—±õ>äSb/~à½þË(û-‡ã/ñûõX‚²Ñ0ÐxÀm‘ã¸o×ma³Ëüý(£æ!þAj-¿-%”€&ª®‚£žêÂÎJ(§,¢RŽ®R˜¿ò% pL¢®3Å4V—Ò¤î;Ú<Ü`:ûìx³æìH)žÆ²)ËÒ([yÉG9[¸­v^\S.ÙÀ½,8*«@`xÞÁª ¢¾© ÖLS«GÍ…UœÒkw]h7ý&MÝÙÌφPÛ:qóúp¬ìgà&àCÜMne”¦Hc¨£R^éâ>e«iªQؘ”­+%yíD~Åy,òåEûp™êžáÅže£D?îúpäàùJP#¯n2ý>IÊ“û¾¤Ÿì“«¨oGit#‘S¦•F›ªüo“€`®ð6ˆâØ~™rˆZ^íæÛ”Û’ÕèH2N“äsçq¾8žo$ Â'õÓáûEb=pŒž…àJ|ÔžÚO´iòyOô“¨h~2âg WC*/åËWêóÕžk©4ö9Ö&w&}©s”HãyäR4HùZOÊ0 àw·Aecÿª¾ujŸ(¢&%ä;q…Bh¿oómgƒòS»×¶T§9¹¾QdàJÜ{új¶ï‹ 7¨ŸàV<-ìÅPÖÕŒÈ- â­È©AwmJœcE|„|ž(®Ã!”­£Ñ¼Ct-ˆ§d~pÎÑSâÒÄ`8mí (èo@Øœ ^¹kÜ‘¬vjv—›#åo¹L+³/ÀÒÙÈŸ43Ö¶^ó™þ€]iˆz  rô"©ÿ5ÿœ¹­Íåöû±²P"½]¨’!Hƒà0ÒÈ?šÒÞp>®n§ÌعýK" ý¨á2TCÒx×>¼ "q{@sã§.ˆ÷üΚÅíY™[f¬ öÙ›²NIÉ¥ ó¦@EÑö˜8¬ÍþÍ,B”âè¡1ùVÊ[°¶”U}…"+ùî[QdN¦¥)TŠÞ?-ø†Èžfhõ®¥ ‘êÿ'…¯½—Ÿbv\ª[RßGµÿʼ…ÈŸëè_3åÅyù¸(QÝýÑœX”ixÝ f¼ü @ëÚ\…73n3›e×dÎX0UQ•úªzòÎô«—‰·6G›ÝÕ¦¡‡P”ª>'Z w›l;€¦Œ9<×Ñô˜¾¯Ù·á€p¢z¸4Æ<ekçÑF?«ÛŽó`¤_Ç©ü/ÊÔÞæÉŠŠ•L_  q#ÿÊáQŽ/6ñÄϽM¶«D¬ƒ´ÊÈ_z„³Lýs€Ò1þXkvº|$–= ´kÝÀ\æWãÍL“dÌzG®M¥#`÷&&HZ/¥¿ º.mïq%B¦}èy‡Yš7æ.ÆŒÊÄÕîs|ãž}ì]Ú3Fêvä¼küÛÅu±uW»‡MÙ HÖ641»€›c-ÞûÓnˆ(HµÃÚK1uób8#n͵ÎRþYsiä6\H˜OˆsÏ`LüJsQþ1»<‘ï«9«MØFlŠnU$ÿqÄj°l$÷5KeÚù‡ ºá䱊¿72¾œÇ\êh8õPú:QóÓMÜ€9žµdØ“\²és~ä+¹ì¹ØÚ6i¼sÂ(Àôÿ^Áœ·è*f¿žK˜åç™Ï|øë8¸|?¶ÅЭ¶Ùåj×ð \¾W!>áš‘ŸÙ¬8„¿›ú«ù%jëëÌ×S1kÀÙ+úqF°õ|rüpWñ_øåÇÀ¯.ð³–Ä€Ù#r/[|uó÷P±¸“Ïü•X…±uZ.°!|Všî_Æ•Ëq'^SbíÓ¦ƒå‘e Ñ¡¦•©¯!˜1ÙtkÕÖtväË^ºk«6æÎŸ† ²ÜÊŠ¿ óÜLÁT67aË·ýYJÀ.ÿ ™M$ ͦl\æ³)ÉÔdĽëz©~GÇæj‹•¬¦]›ÖI”•wT7 ^Øk°UF¶ŠéŽŠs+y•7=t࿪Y)d3üê¶3/E@–‹0‘ )€8< ^ÖêsÏJ{ý8\u-YÜ—³è‰;Yò~Å çs—ZÏ2?Û²I—P’û ­¯hã®ÿ$%åY»C*É7s@è™^?Ÿè%àkd®Fֱįf©Š¦¯ ´QIPåé± ›Ûø– ˆ®²þåä6bׇœTòÕrßåIå/ª¤DQ›*}k€9*¬Ç¥øÆn¤¡­6ˆ–êw6ýR€]§Ð)4“¿€ÊIR¿In:@l°×£MR2Ö§–‡'ê—SºØ4ª‘×.QøÉÉWT°³ÚfÛWÜÖWÖé‘Áe§¶ß›ò~1¬ ÙÓÈŠh</m†­€Ž(H›\?¦dÞN  ËmìçiÆ…«h‡âÂíO :˜”hK3зãÊH^¢[$jW'‡‡I>wžD˜šËÍ?S@¤Ê££åž€³1Ãß”›¸Ç Hl…h9€ØÃíYbD®Î°Ëî✣ÒX¿ö:³<é麖®| L_óumºSH· Dò}”6åÊÁ:†?›ÿšé, & tYÒU®tIä”Ô­`:½õu¸†å8§«þºЖ/“x Ÿž¡¼'´~Ír);mU³D¦Öüƾ,O ÖîLцYk''ÝïÌE ñ,§žó³O®ú‹Ã; yx° IœsnœÑ…Ïd ¹X2†“s£‹ëžâöŒ C«Œ™?îÁtC\ÂQª°Ä„sP͘—@¹îõÞa,Ôš|ð}­"Ô¢”ý¿y޳²B íßÃìê´Ë–Ìn«È s ã ©ÉînGÇm-×8ÈäŽÇ ,@ò‹“Ä7uU_¬×÷Äó/±Xêã⎠Jy}8³¦Mét@¶ù¦]‹,ü±vŽÓø>€>ó,}i,!÷j!c(§s…Ú®®éÃéwUþq3¡æ4£µìò ÐY·sFK7 ~eÒt\â73?^fû7ŸÄÁèñ…5ÌäÌ*f¡úËÍÙþfÁܾÅÑ,»6"¢2ænòsyÕÞy(šÓèp¨ûÖÏSê]mì@yœÁÁš o꺄 ¯¼Ã Ù*<„µŽáƬCéoI¹Ç~˜¦Ìâœ>«ðd¬ñóžd-Œ—¥ÌÊe½šïAi¨,’â Ud´©Õ8ëz#m¹öÁDìÀÚœµb4‡§¤änL8/­%¤¯f}tÕζõv]&ÚCãÚS̹-áÒ Ö}qwŸ.¬ä“ì8þ3 tAhžËâ¼éšK5Ï´³Š_5m̓#2\œíb´¦«uŽS(çÂ̬؃9‰$I’Úôµ…â|økŒ¡\Å5mÅ2gYÁ8–ݯ½í³˜õMôùJ$E¼V†ðA(q ÛQµ7#ÚÖ˜ÇY÷±Ìâ¢6ÓWÂ¥üÇÒOéÓ'3–]j^È?bžÓ¸’>÷~Û¡ÄL[˹¿‡ÇýL+„4&†}ÁÚƒÛPïêÆiv-ӳЯaœ¦{q¸£#Ñ Œ[”hDsøŽXÉÚ•ü^Ñ<”+tìföﱤÙ‘ãp§«€Ü3¾lEŒx퇂µmàd9||¥yX¼@âú}ï7 ¹\ÃÝêCy¼ýàâuÿýH7‡ówáNXqòÞ9zH$wW"~‡î‹˜cà}2ùgÀXÔ£¶]”®Ò—’:¥, lfý´âÄÀ§\X9r“ÌáN”S9j²¸ÿ[h å¢î—ñ'ˆ´¸.êǨñͽİÔBÿ#%ïÿ#=Ÿ)®w¢?ÑÿÐQŽ ÁŽ?t&g2sn›×µŠø%d/º(lsþt”¾4*ý 'oÖ÷©MÙøþ¾y´oI1‹¶9Täzda6I\{q·bK»\îofsxýÄ-Ç·Í·iðżCÈ"q’÷{3ÿH%³T^wZ/•"&Ÿ$³õa ŸR€ÐÏáÌr¤ÍÆd,úO’6(>Y¥¾f×ÏÞˆûÞ‘®; t†›ç>Å%Û³8;ÙÂXò9C§Øc=m› ÏÅÜœEœÎIÒµ{¶Ïl·ç'~­²ךdy&I[—'•’Tâ’ÃlоO%ÊÍù|É7¶óÊÈpÝ‘¸ÐJiIò¼v©³ç¢«Äe PÙQ|‹__¾ ¤ßµa³ÒžnãQ"™›ÉCO¢6ªþ]x÷ŒÆV®ßNýrWI¦î:‰M³ßÖÿ0ÚÀs)âìt-øN ºÔ‹O÷ºè¨+£ÌÌG¡M®€S‘DŒì €âd¾Jy`{Ü“5§ñfq€¦ÕEÊ‹8~E]1•ç+áCÇÆ‘ÖÝý‡¬ÏËšNæßѵöK G;+Ïe#ó«‰Ó,Ä8`Ï#¹¬äAd¹kžô0㑌9ÇR†ât×óó8Qj¦¤\Ç.°Y~Pkã·À&Tïh ‘ŽT÷N<Ò]ô‡­)ÿÎ@eý3Ú ;À*àHÀ±ñ¡[ ¨q/ •¬ÏÓè§ðˆãU¥%íóÎ8SЧ­ WlâR{=Kk|UxܨMŠëu<2ÌOù]°ñORQý}ÎwPvùÞˆz,GkZnJùj‹^ |èª?hƒ@“z°á€ëþüD-\Ô̹Ê/³ýÙwÕmk½–¢d¢µCvÀ°A¸‡£ æ *æ,qÜÀïoêà9œÿÞü/ e÷ı8v+DÔÿTu;2l ØŒ¹÷S–í­¨Š³}àXPÙý£„j$¹Éó’öäûòÌŠfi N1rÖÞ†gŒ±×´'1¦‹Òˆ ·"’e"áõmñ}õèW?ÐFܵtÍ#‘¼éüQn1ŸÐ—‡ò/0åÚ×Ä%½bÛŠC(àp$ÆîÌÁEêNþ? üj>ËlÎÏjþhµ+k˜C¼ÜiˆDX 1A›ÒØ¡ô‘êï‚Ìë¦uá]kß6ÿ–ÉÔ_H.ÂòַלŽ_O{_tÐ¥Ø;[À©†rÇÈnföEîuÉ,¦k9±³ë)z ©=Ḿñ• 4Òa1o³$ïšß麿@ô€~àÁŒÐxžó.}æv)u«ä’)Þ™LÞuZ{±ÔTÖ§}˜bán5o8Ï¡!° aeMûXNÜ·h[ÖƒoÛ0wfÈ^° …§H‹éJ‹G(üY“í•V)ÒÐ-Ú<,W«¾ØÂ"'’BO¤’Yª ¯/þ@œf>Iv¯b[MÌ¡‡ƒ3È»€1Gâ\øšF¥Ph<”Y9xsR>늠•©zþÍnFØlEÐaX؈Ž.‘ð)àÔq:$¼¬õemŽ"R–Šli†“ ­<èÂ#x;(0-dÑM›Þr2¤](•¸ä°WwΧô©ö3/Îý²ž±~ph«û"sÑð›þ×"«SÒfZ=e«§l £žá¨“³ÀÔ",C“M3}Ùw9|E]ûôó ¯\%b@ßZŽT¶ò×&¶oØT‹rwt3gqH›B|¤—U„§œ£#Ùü·%žK|Eº/d˜Ì–.Xô, EN‘!)Žâ=®Q_êqù¿Ï7«\ÊÆùc6Ð6E{ëh<èÆÆZdåÙZSpÀÑ•ëÝN[1çÆ(ï­¸Ž«ëë’{+U›ýþü™ì^lxO'•~|Gë05·ãsb8m|‡ÓGô/Úq’Ó­ôEÏöb»éüZ|jDvNn,³A͇äE\îîkEBœà“ˆ‹­BñHmønµ¬ë2ÃÍÒ…WÍ‹¤ÿç½ûe*‘E`¡+é¶7;ñ.qi (¹ €®”« ?‹†ò%Ö©@}¨%êà&âö›ÖßÊ’Õ»$GyA8޹€ûÔÌw‘¨ÿ@VVÝ‚äío g!kÖ˜é“@ðîôQJùw¬U°€£cÜ­ çæžš£-·¹sH>sgY—ͨOŸ^¡N£‘¿¯OlûóÝüð‘YÀLëÛ#«ÂŸ™éØóŸót«š@1jò#qÝ匜ì¿ßhç¬Âw‘Ÿ|\¼“Ú¿äM]xß|Þx=€ÍÕfoäöÆ¿_Ìe䱄¾2GÆØˆOt¹ š-ÕjììiT|yÓ.œ@6É¢Ek¹#7¯ÙªqŒ9Ž6&ðö78ÙíXô?¸ùäkêõE ý7ä¯6'Ã)h©íÁAáŒM{ãY}bj„ût)2ra¾Þ޾&®y·Žùg¡‡ér vi8„(´!KÝ8˜éh•\ÿŒu¼‡º¾£ ¶VŽ˜,€.ÿyÍÀìÎÑ-¹üuô%Üïê;ŽœùáÜ|Ñ̺9ý9¾¢p¡«|6[eÀ0Xvóòá3´'Gµÿp¦ð©¶v¬å¶ÖMY¡Õh'i‡ÛØô‰Ôõ~ÓwÂ¥/ƒæµžOѸU¶­9ˆ)q‡ÖÖÞmùKuc㥚ãqëÜŠñÍíçt˜_Žˆ`ó6¶$NàéØÕžEj³wéÛ ïÙR1\Ï\tó‡Öeš~®=ߎ;2ž¨-´ û·ÄŠé ÐQwp3­%÷l7Þ]¿g¸5Ðòôãˆh—Ve‘ÛŠùi—ÐG늣PÑÚ«nKê~ôÜÓŒK?˜ðPÿg¬’â6x0†ôe=ÕQ+°Eaw¦ÜŸ¡úígóø¸áVq[_8VÚv‡£VôåÔ‰–û÷SÀÝ;æ[ëVç í q³ ÑŽÖê BwþÖrñ™6ÉŠ@xÿ·ñ¤,V\X÷\®]¬— ÿȼ4ñG«tîEžW ÒA2w%^AÊX?ým‚yrü÷VÑÜ GZekEµ!øûÙÔ æ»i¿•ØÝûª}JF±ä_ôýGVéÛpLB™9ê þ6åøÐ˜@ñÝWØãI± â#Zhö•ÀR‰ãìKyîMiv &Å%ÆÜ[V¿ç—9 è÷ÌÃÜòn)„K^Û¯‰KÊ„ú€¸¿ÒÈ¿:žæ?'ܤܭi3!ÅPmʨà.n1ŸJp5ø~M™Ÿ˜t”Üx;wÿ¹U™ò¨—F7„ LÜ/mc‘fÓ½Rzž ˆ Ú,?ÎrªÉìo¬d9.8~úW Ä*`SmL›=Å».>þu¾jäµ,¥ÉîMhÉÚü,ÚÞÑ%¢@d9i§èW[—$‡çÃa½K‘•[ʼnóU@έ<;‡L.]}‹oò>Ɉ}ˆoãzO)ý©†&å´0þ²É ~ý5˜Úô6W†ôx¸eº…‰ÿ…º V˜ÃO´!]©œºwÔ)4Hsú°:m$R;‰ƒK»¯¤Í°y*Gº†£ìA\8yשտÊÉÀ÷¥Oþð</Ìî†)p8>äÉ bT’å¬*[­ˆ‚‹txXÏÊ4ˆÛM‡‚0“¤k´“rqîo½|`õJ6|?PÖ:ÌHæh]“…ÊíDbQ’mý&oYÝ»µ +Á¶fÖ5GÂ¥{$@Ò(nÒNF²\Ýx °Gÿ¾H§Sبa—C5‘w„=™ ¸ä ®›ÆFûN6¼g™å±fáCžY+vaW 5’HO›P~7x'¾ûþ·)íV¸à£:X§ü^Áû~ p­1øMæB‰'Ø'ÓΊöø{²ß¨n5ŽX²ü «mküp¢h´i•˜‡cQ(E§þÁÔP€`‘/û:€2=ê»ãUË.Ï鋊t@v9;× È–L]q7;êË7(7_‹ ùnÊNô%íAœÏ—3ö ÄÎz9ÑCwF¢€Gó«´r{[ëú˜}q9R´w?ãÞÁ̠Иnj¦ž—0oðnÂ^M¸¸¦ûŒå´VÓýQíM¼#}copÙ©ð Z˺ԧÀŸãö•'K 1fÊüV.°ælº|ÎÌsfv l–¾”Y/0óû6µ0„ü‰4[¿ÇÞvS»âˆ­›V¡`á%ë÷>m¦§d† äÞ”_ü »~/+¶$)šJ ºSòéÞ--Á¼¤¢×Þn½tÃŶ¶~…ôvÇí'ëûiõ4Vê’¼ïÁsyæë,mFmØeaàKIK‡P"­KެÙ'<Ìgv®ù[Ê{p¸ß[y²p¥¦C ×i /´ºÃü\I{½$ÿ«ùãªV>û:›Q OØd&0>½ˆCö?˜OkwGì { *Enîê}øÒ*ØL77­Uã·;PòŸ¨¾ãÈ™s¸iÄY(°ÕØÒ×µ6ÿc9@™%Te®ÍÁ{ÓÖVõcfµ)L£šmq ç£úÝ!ÔÖîH¸U²ÒhgÍ"8KÓi8Xæé©^«Q–÷sìÔ•qâ €J{ˆIÈýîç·x˜£qrZ˜‚­¹±Ã®#²â³´õuëŠyÈûd\¦ðïƒÔþÍÍÏw°.þó‡è`ÍåVtBÀš0&·¶Ù$³¦õ‹~j•¯ùìa™ÜŽ™~žŸÙ òö ñ[ o\™3¹³ì~ŒRóž‰õEiˆ¯8ðØ&á<þxIÌí_ôe >I¤’Ö躭£²¨DÇóFZ¸ÌiºI»­’"ÝU„—Ü3#Wá5w¤‰ÚP°p”OĘփàJ]¿k/@ÓÉæ>€Ù[~úÒ¼öËOf•NÝÍ(0-Ó¾+¬ÝÍ‹FZk·úÊl:]®Ö|8ëHÊÓ¤M€¬D38—ì½ú›_fX±Wx›3뵤§pmÀº' Û¦S8ÂA+¥sñìAZ'÷]Á¾CñvEöðŠäMJÞ.úþCž?ÍuYÐ&ÛŽôÅ}üѯãÍ=£¿!Ïq{ñÝÅÁ¢h*ú:“¾åˆ>Ëš¶p?Œ¼ãŸ(7••¨@ZŠî¶éÞ×| ¸­üÜ0òsÓ¹¦5Šñ–pI´ 8Q99EzŽÓàߨî‡wæäBÙ¹Ç9i×<ÄMׯâéƒ-Rt%ç«èo<µàNŠûøŒ4’ô^̹t2De»¾¿0s‚*h‹(ÚGàA¤ƒ†ùàžqe;ˆïu›hEý[R¶çJŒ2’KzªÇE¢ƒm£Í,IvŒòw­…!‘YÀ`9ùß’±êS±g®»QÏ'æïÅâ}ef3ì€!ùrj›ÿô[}·O‚Çß8ñÉ)Á“¸Œm?aCö…ñoTèpaJO€lÄ9•;ß&¡¼,jç6r~ÊYq’ut´7ŽˆkU$ŽæëCÕ'Î¥ ŸÌOCnn[3OýavïÚ€¶u¤/¬E}“q_ £®$KŒRB¹“˜zZ`X£ã¨ÜÂÆ)ØÓ­‚÷Ykhœ¼,{8ƒrç­$n«ü$3Ò4œÈ†øtó "`ÆòÞ\7«üR€³H£–JäÆ7Ä»¦õÛ€„ìwÜŸ‚‚7ƒ¾Vh½1·¾9Ç,oc€K¼¸)f[ÉUÕ!–ÆócÍ+æ6C¹Ãmý"àño)30w´õ³2´kïÍÁzKÀW lŠ‘ÙÎ*îϡʺjã–H¹~_üV£M Wh{6aˆØC"RT—ä]צµo0®9[¢¯‘ÿŸù=† ²[À©Ù—ëÚCbo¨X‘« Éovë@qœ?Kÿ9©>uº4N§L;£`.¸êo”«Ü!Í—j·†Þ’CÎêJõÙoëJ^¯bvÜö6H…Ÿ¢²EÚ²Z%äúàK”ÁvŒ‹nb²ûÛT4ÇæÝÚ‰'ÝŠöñNqp;ƳYQ„± Ž“=¤Q½]a‚2òªçŽ¡µn ñù(0hÑq·2¶ìöx,çyÆçÏ£h„³´<¥¿[}|;ÆÊŸÛÞm6‡ùQbžDù}Ò\íæAÝûa^’çý7抮µ{4˜¯O¢oEš?\oisó¡Jóê9:ôÁ®õ‡ëwW&æòùÇš #cTV·Ř¿™6ô$ÆýïÁ öþìJö€?Š›Ý•qá€Ðú+sÑI˜Ë´™Ú;ñëhÃ:Q(V.°7„ ¹ã‹fq7çÙK:PÕAzµÇ±µõP‰-bMûd6˜7¬Câç@d;W×ü>K&|gÝÚÁ2ž–ÎM¥¬Ñ)hQ‰o¼±uyî¹ç>Æìµ×^%nÛýXWâ–t˜ÚØ`EHq™^œ¤Ä.Lj¨·ŠÍÒü“áÓì¿"ÞA"ÀVK’ù; Ðx¾Ú¶&—IsSy›AÞ5‰9ñI{µyù®ç_)ƒUCùÅŠ÷ÔÏ#Ì3ãG˜K[ÍŠ©piMâ[[pKtE µ”ÀÜ\öb!‘ ;ïÔD§JãVueãÂFÏÜ?Ìa™¼ŽùWiÐêϽKÛbùúí¶š¤ÄÝç0¬&–1’?+åc¢×SÖ,ª‹j]Ò8é‹#I¸JK¸8PEâ֜¶¦À‚Ú_ÉŒíÅ;žbôqãD–ˆ›MÊä$/ˆŸ±sDnGs‘æ4Ô"­溛þ´¤9Žï—ìZ‰èªqçeÀ³-ò¯Z`M×|OÉvf>ž é<P9~Ù?ØÔJÔ;‘Ö79×POŒÄ¹zrþ3¸ÐÝ,Ÿk–%¥{•WÊón]•ü%çû]8€/lèÇ{%ªc'âlÃ8·žWÿ£Èzp ‘Ëh u}ù}R·æ”8›õ}«Áuú àÏìç¶"¯Oñþòó˜ HïDÒ}`ìeÊG‡5?¶:×lVwšå„Öz@ŠŠÞ!þËÔ¾K$%q;jª<4ï¿CžO¨½ÓLœ±…Uî)àJï¼(e.…’‹à&¹•I…‰®ž~ä{zñ=Š“É÷5Oeû™-Ÿ'uµ½âx€%"ÉÇî̾r|/Pº…-¨yz)+"¿[d]u(ñeõzf}âõÐ9¥0MÝÿ!^LŒ™H÷ƒ¸›0¢ìa˜¯ Ìî·æjßEؘ]l¾žq>ÞsæmŸZïTÚ®~JÒmí2ÇMéÙÄÓ‰hD©øX¤)X¯ H#˜è*  ß™C¥i(b]¡þ¤¶xcf!sPðÆÊIäfþB½ ¥ Ο;ÊŒi¼„M8ßÙø2åñˆ‘û_ñ×;ÆRwÝ ‹ò¹ÏÂòHö@¤æÈ(=‚yDXRV®Ÿ¦¿ápë%@W“Ç3µæ«Bsû$n~åí{ºàד÷ª-hõ¸y±þ:òò®€ÜüWZŸÚ@ÞÏÒŒ{ Ðíä‚ĉtæPåysýS¢hb a‰OzŒ~bÁ&/¤QLÛs8äÏ™»Ä äíMùk»eAÔ·0=/0[ÑoН+ö’ZxQcm+£C–ˆj® ,ÂJkÎìL¸û#¯¢¦¹ÜÁÔÑ©¡vzbµ$ñ5gQöõÿMf-N<§ßŸdÆW›Ô,†ü«9œï=ºÊtúóÝ[ñÝáI•±Ê«ÅC‡ùkxãmw³‰¤É”«ôÕâH÷ª›´Ù²Yþ‡Ñ•"Íf¿²‹†™xϺl@^ A£rà`s’·XÚ†H\Pg‚»3 Ä6“XŸÃÕÿ¥5àÅòÓ•QÉ–,G>È— #%hØí1àtß+.¶)$’ì8Ð*ùgמ[2¯˜ô®þ÷%Ìð¨åº”‚§®“w–ääýa¨›õÄ&¨\ÞtÖqV­†ùm@€jIâb —M Ùzh,ª€ã¥H3ð¼ä¶QXß­1ÏÈÀä¢À3ùkA¡/q–ô‰ÙU‡~[hƒïlzªá~OËn$îA"i$nNÇ,`,¶‰O‹pë€ýÿŽÜ!‰ÀÕ›‘üü'¢DÖRõ©ÐN£€ÔG÷à=>@RôŸcðãË!jë)>{ R|†GS§ ]Y{ºÙ¬þF\¾5â¨?pؽáÆ-Éû=– Çm[ ˆ)`X›|Õ'eW1O° ÿ€~þíeò°» =]2¹·(t1åÖ4;6þ“ÐqRzË0®ˆžÄØ€i³滹ùÍYp¯^w¡O' eB£y‡8d"_‹"‚Å úò(ó¿¸.C:nþ‘èOȱ  Ÿ½%ü )¤Õû”§Ûàʵ\þ„•x“·ÃñPà™®°ï Àþ 6ÚÙMØÀ_aÄA,7ת4ÿ)ç:xü7éUæðG¢”†ón•¹âl]bb©Î$Ù•¿sït8"3Ÿœjv`W°Ú´À@—¶ìà¶Íòp^‡uìY:-€n‚úÐŽ(¼c&G>*ÓÏÉû+¹L¿Æ/y÷¼[{ÁÏ¢0Ø’/<{`”èqѤQC0x‘>bÌ^•úŸPqÛÌ${SÀŸ“]È'i_û´M¨¹ççÜŒçÂÔá6ÔáåaJ°öf³0ò½OÉô2ûgw3½¹® rï“ 6Åmú¥§[I°ûîc­Çbü~“ bíâ88Ñ“yH¾V'_צÆkÊQc×ÚZˆ+Rýô+¤0ónÆ¢1¬GÞÿY{( Ù.7RxµCf9ÆÖìbú øPß+™ÔºÍ³ õ¬ù["7Ä¡ùƨ¡4µÂ+<eåxúÏ ¼øÖ‘4÷¾PÅñ¬¯)Ô®f¯¿¿"Ó„D›ˆ%O@¹ú¤è°š hWR7õfd-óK%îUÂÚø„¹¶ðv”×ò†¾x ¼Å:¥7æê!qÿ“»ð­þ$ Ãdû€`ÍAô‘ÃÊäe~ÜÇ$ü‚ÑD ã:´—{| =2i]½oïD;JŠK†÷í¯u6cY|ÎLéCYùä9Ey§Ÿ^ÌÌ– °œ»æ¸ ha‘Ø(<Ô–Ÿ-,Àœñ †®AàÊ`·5\tL€É8S{uϺ'º# O牢<ßê9Μ18 lMäjÁîòïqõU.įÌÍ]ا4– ÐLwJÀŽKÉhÙ¤C‹ý÷-É1^ùÃoMcE9ô]¶cWs@¯¥~ߌµ¼½¥ZJà®ÊiðQ¼Útj’ßö{‘–e³“Ä(j.è«8Òˆ-Ž6GÚôºM·s«öé‹Fh*Ž_Ú·4EîÊqSáújó¦YGhSñ“b Ò@_¥Q-è+NŸ,I+¯è@\Çåw  G5TôU\¾ë±Ès2 Åоè«8i B'×о ׋t›º*/ÅTþ¢_u  KTi‰+¹f}¥¸Â‘Òè\L/¾Éúrñ¥,¬Ð7)"Ez"-Ëu(’FW²‰ŽQîkÕLßœF¾Ö¬Û,Í+Õm à1GM¾I‘'pM8*ÉÌ’.™è)¨fPÍu‘]¹¹ú¾@ß×xKÌ?fiû4Öi1§4ËbaÝ ,© ,À¿XÍåÖ®ö™Fu´c,Óõ-ù¢r ¯Òp ¯Ì>蕃<*¶„ôU0qÔúmYn§°©éWç]] ôÕÌÓ.ÔßÓA_mZ56XÐWqµ‘ @üx?÷‡+|ÐÔfõÇcû¸Æz¢Xš1Eà·£¯9ð˜qL¶ƒU‰3nÚ}%VÊl¿A9€°e©p€Ñ™ÅL—Bo®é¶E㺎p†F ¯“‰ÝGlh;íIkù°¼OÚ’£­þÏGó•H"¶[¢¸oL¸®ÙS¯uõ|õìVÔ„ÂG~#ìꔥȨ<ÕË ÀPwü²ÆÊÄMcê…äo+À_Ñ(¦’œø•‘cyx0©$u½Yœ·"§ ¡ؾdÊÕÚ[hxbº²u³áoFCý„e>ÖËjg×z´kÎv<Ã_§to!òå·‹½2Ì®kžŠ#øtÊA$ЈÚ|Ó 'Ã,P„( …pô]VײÔ¥Gíݘ”ý»¹5»6€l{32w–Y­õ­æ®JÿL;0¹]1¿€ R-(NEз`Èž‘§æ\E’[;Z °³MX?i÷q`¼Ÿ ØM€¾M&‹Î&?cF^nþPjrç™'i‹úªÏî_Éøu‘éK›9‚±Ð'ÉÈ–¬ç8è;€o9ÚÓ€ô•U /À[ u#í¶ô—8©ÿ1Ê9зöN콊A2ÂÙ}‹vϤyLmpël[@×ÑV&è“ôû èQk¢£Gòë/$ïÏ1 ôWûÔÛ¾(Œê­ñºáÕ:›ýÂïUÛz¤Ão¼¡£¹ÑÔ¡¦; Ö0ëJ¼Nö Û{¯"îÅm¿1›ÂÕüŠßN«}Ðìû^Cãÿ³wpRÙ¯™Ù]P Ї¨ˆ(bVŒ˜sΧ"æ|êßÃ3çœÅ¬˜s<=sÆtb¨QPôHfæÿýUwõÔôôÌÎT¼}ûÙéîJ]]õêUÕ¯^½úÖ¶ÅÉæNÚU¥rÚº~ ¼-ó[ôUWäF²=þ˜PôUàæ;0°aP7ö I9–£1¬™­ƒ'uÙ p®zÐW)F <4Ó o­Þ¿ˆ’õˆçš ¼çjoißeA_¥€êÑúÔñÙsÁÁé}°ßÜÉ.~{Þoµp/ RÑ®!èû4òZ kÕÌkÅûLí§tgKhw†äÄ:LŒ°TF‹ÛE¶ÓOo‘Ì©jï56÷A_9êÒj5SÏÒôm¸‰jØ:ÊD3É#ƒ&µ,2ãmüÊ(Iän©ôM _»´{&rЮ+'R+#¿‘’ 3Hô&ižYúvâ ®G"‹K{Ø­b.Ú<ó©‰ìeÎ쵊9£×ªfhïµÌÝû¶™søÍkbÎ~áœlwñsváÏ!¹wC¥ìe;»¨Ü@ÔM ý÷êP”YAnbÛš´Vc28+)8t¨|ŠÒÞÒ ®ÉÖ⬠ٟi%¾%Òt·äЧX¤`º8úÚT±`%š¼éðœ!ü•DT[ðÑ€¸J2÷ÜéÀ(GhräàäÜ*]7IÓW[“OfÀ/z…IÚÔÚ-xZ®þ¥‰Ïc`×°ðF¯Új:½•eª:pÀ¶Ûb¯¼JãÁ'w`Ä«:È5Øò.6™\}ɬÌd†{—òµRT‰“µ(’D:uÚçMÃ{‘¦`úfùÅ–Bù‡³©Ü+‘›Ð(-åOZ¿ó{^ð€"ß>µ‚ŒTÐö5õoòá»B 7Š×|—Zpël6ÀäOhP.dµÅä{À\DSŠn“oxæÝpY¡¶´?·²Ê³d…”væ|abg¡1蓾EαÕ/Яµ” ëL@Ÿ@Ü€ÔñŽê>€ðZNUZø4Vh7E}ÈʃIFàŠ4Ö];´‡UN£w¨5H P¦7l˄ĕx2D€§¦º»g'˜{‘ÿÒàS/DnÍÅœàj ¤»%©?  |4Í—åß48] ä›jR›6•§3N³Íö#òè4ɾDùÄp’ zó âo8Èü‹z“ÙÝÓðUz©æhfyC´g1A‡éPFIoA´Ãñ_’¼«ýèµòáÅC)û{ø¥mÒ€‘–2Oµ»Þjë$"`"<¤¼-ŸêIø}í¡Ö”ÞŠÒl['½©é~i†Öåà ›Û2— #Ÿn¶ 8.¤£-á3n ŒxÙ-¸ðZ¨óùBœ’Ų¯ªðëXÛ`ws¢‹&Ð4 µíW”>Œ¯[<¸Øz9+`)$^Ò ÌzÜusômù§NM»¨ÛqfßÜgØ#žfúdï5?4¬mv˽i®‚Œ¶Éæ¯$Oˆim ÷D–\Ë퀓§«2»›5Óÿ‚„¿Úu J²Û]°©ΜÞ_T{“E'QßÌ…,JjݬY›Üp³5ýv|ü7‰:/>x¸Þ\ÂRƒ©{Øšà° ØŸ÷Ñb¾¬ðXs÷|¨Ïli Á°Bc^†/ÿ“{‚r-:ÐÍè`:GÍ÷rôõvq>ÿoÊì¦À·öv®‹D‹—’ŠÅ£¿¦wÇ/…Dä»Ê¹ô`óï‘8}LOÛ¯þ…6Þ=û¢ùÖjb·| ’] ëê`º„¶h'ÙÆ›á¹Ìš©±ÔíCVvÙÖ7uas}óð$r·qÌ2DK6–ÿsu085Ýœ–{É”ï7+Ô¹>ßorwØG<*Ù%’–sò¢òHÌ«,E™=(wz8áw cKÒhé¦9à„ U:IÒ–£%©”ËyîVktl, ÏÍCcn^Ÿó)ûè-¶$…yIxü€ÄÔ"îS¬-õ¤pInZ¸Û!“ê€IÑ͹ɘú˜—Y §àW´œêžŠ¯JefK¥Œ ñcÏ#_o€ÿLf0²dˆY9WE9dO"&”Ù¾»äÐÓÀ3ümB.k&¢RxôqP>²=ŒB6þ“ÿÑ9ZP¬žèM2;&o”ÌŒQþ4ÓáoÉ…ª©kÕ!‹ÒÔ\\ìTå“ú¯­Ãþiid±ÆVCZv°E˜ø‘¬j£?h t®­3éJ2m6ä[ƒÔ6šóK@ÛŸæTš0ç}N*sØò{’ßÕûùp=ùn³ê^[GÁTÙ=•¿>NvˇHöÑ@¤%ÒD6NÿÇ`NƒºßŠ´µOµD•lf*n0Ýk)•Öù Ìšêj;¥HZ •¨Åoà0‡wC&—Ρ˜{®tU)ÐIJÇÓ‰/P]‡ud+¥ÀÓrõ¯­Ú²JþÅפ:˜Âº°õö4¿{ˆ®²ƒ§‚†@{ò:µƒ£ç`røôõ†’µLfJß4/“›í©çŠáz¤Pýï×vО|u-Ú®­ìÂN朒Ä«¼%ÎÖAuÞÒÂR-|sg­6$'e.C‹«'í^ô.©zËmšŠøvO]TÙ ¹÷ ìë›êoœjX' m/wt’~~ Þ˜ doõ³ú­f–hRÙT‘Pß.-w•™ Ã;E>ïÿ;³.Ý­»ûYÙUP@IDATÑ¢€´vþÍU|,ÐOtšÖìóåðšt‘–\\ÒÄ2Læ jæ ®šzr ` ZLZØ‘†i#x æKÛÔ‘ÎucÏø½Âk¤ë•mŸH“0}¸‘9·H 6v7ux eýh¸ˆ&Í$wXÕ÷á[È˳, ÊNøê´sÕëuÄééµ™‹àÑehòþlù›ù%¦ÉN¤¼°½ŒI†>©ŒéÛîŸf2îÿBK÷CÀcâ²±\‡VžH@Û6¸©žÚËè—€¾²íëHv´¢:ÊWråZIÛ#Ó1³0À~Gà‚äÓ±½%ZVß±;e«÷Nv z×+ø® é}Ñ‚^Ìš?±‹¹ÛyßÇf¾†}½hó~Ñšh)ë}ÇPç—æ¿4[¢½5‚üÊ”Ê/µ·˜]Ѳ½•»•߇×u1ýh;:Sý˜¶Ìk¦#ÿš U϶ßïSŸÙçnhTî_¼M&S µuÕÝHŽç7÷¥w²Á #9Þ=¶®¼h˾áûŽM¹vÛ`ÖœN¢!Ù›q.€óóÍ:4U6¦{7llaIh_`èSsabâÚ÷Þ0Öü >_³¾Y~ ôY(³¯ËecºX¢¡AhÃù?›À}{äÒ^Û‰úÑî+-‚ÅIîîÐÏ"¿,À‹€îÔ*wéËW’A¡ÜÓwGJ®&(ÃxøC´(P÷ü²°]´ÐË(¹5î`¾dñŵ;“>ØÕìŸ9,H¢ù®¼šN~·çk’é?h—näy}I¾îôžUÍvaç0l ›Ú›y©inÆšàI-,BiáqDºçÕÝ€_µ[ýlþq²}¬m»sÓ?u©¹Ð «Ùàz‰è.-$hiBÛº}ZŠòßÍòQÁõzà]iÏ;äÕ”ƒ&»€tu~¢}ŸÝÉQ{%2Íí})ÄÕݯ€x'þJÜÜ d/Ú’IÑf³ü°‚Kû¯ ÷á•\ ®èî@1yÛE1 âò}’–€R«qíÍ¿)wD.æf ز‰ú?,´˜Ô޾‹wÏ((¸\D+ð$éàȵU÷ì®ßº›àZÿ\ñs“ø½hL•j ø£¬òo‡@y“‰@ñ„´«qÒa’þâ‰ÐNŠOwöÆIiJÞKx+dI&=ÈkµÀ³gÒ<BæÓn*Úi´³«iOóPfÆWZô©xqÎ÷Iº×IaÜ«ŒMò÷Ý 9{<õŽ4Ú/¾²€áüxª¯øÚÞ–Ìí2Çã^9áù­”Ÿâ·F~o(ãæk+†¨äùK‰'½Bªoàš½Ó ”ls˜SJ@l% €YA¤¶Q[ ´•ÀŸ£â“ì?ÇWÍÜWÈžŸ(€ im‹ .3wçVá+¥Ò;¡ ön•âVãÇ4´EÒtçÉŠpMq—j26 I©r¤m†3Bî°©xÜkC€&îîžµÅß'7l—‰KLìV5šüpÕÞ«ä|­M×4Èv ºlOú§+WJ·>µ™õvµQY«¹§gë±jÜž¢|VƇˆÃ•C!fñ&ŽzNpј˜C=œ¶kmä^ùFÚEq ´•·hâÂØ…,€DH£B^ßrdµ»ð\€ÿõÓñ[Êï)üµéˆÄ$¤áé4¥å%@PÛÊuJ»ÆSk‘Úƒnb±¶Žïü1“Ôqh„)žØñ¹ÿ `í ÞÊ$O´ [}{OßÞd!Pq±‡W/³ø¦dTÜ•Æ;lE ¨hìü””¡5˜PÅ·‰*¨l?—P}aâ--9¾MEgêD[Ã-eϯ'q ¾ÑiÅ¿z£vÞ±µœ9Ʀé€Uiª¬~"Ü+¦k¦'í*)NµðÄh<|žq9ج]”Ñiö~d«·;ýÆ‹ð™žó&žŸf²,\鵓_f.FXL$ d™z‘Ý^i`íÊd[´2æ¤)ê…†à¶æ¥Å¡ÑÒU™?Õ°µy‡o¸ @÷ Ü>¸ìŸª³ÛÎGf7­9üO~¶do€Êã"€Â΀ÆÎn°ÂlJ8Ы+í2¨$­îcsš6æÍpûÝJÕXÛ¼·‡u1~w¤vå4‰[p¥Íœdµ/Ê^ŠÓÇ‘·´eK×8¾&`Óµ”‘8`žÚ³­<¦(Ê•ðä&L:`U`Ôê¹Ñfà•N“¹3µ`üQfÕ†‹±/ó.‹€ ðsμž½¨ì Û¯+ÿKó% À©ÚÖmÒûÿ¤ð)àé•©Y€¡<œrø§¾y}7ÒXàK&I´€ôNÔß.Iñ<Šfþh«…+3L>½¶Y¹I{_¼2¸f#“Ž&ÔkÎB–D4m±èV7gI.ת¬{{å>iúÉô!6ܾɤpß…úz‰ëhþå6·õ·;Hj¯·÷8†×Yx‘)‚üý^‚ý¹A.«¿ÿ©çÇ-‡åaü«Ø­š§é«¡êßã*ž/%õÛEcªì…¥*¸D xæ6ê3ùMAõù:øÒ'_®ËýjE Ù_ÆŸð¯ÿõè?õCqÚYòa™±K<¬ÿ¬Ý"ng—Ü/mÚ»t½ühÞ=yaq§"Õ9~r¡ú›«RÛ™íB¹JcyªY>àÍ)«Á»Ùx¬Pôe®oÑcH5ì û5jcΕüÖ\MÏ"3Oo;“ÿ–Û„¥Ïé«à>ÅÂgÇ9¡«¦߯âU;OP´³RŒ$sCƒù¥µK¼íñ_àˆ©Û¨­ÚJ`Î*×WêÜgæ‹ÜpN§­·QPŸ`ã0‰.‚$‚Z%žœž<{Þ ž*wÞY­=^£Ü:οÒUvã$0ÈÝgÃÔ þJ38Ô$ô=˜¶Ø-ξ›Ó˜'ÒÉ×­% ®Iù‡^_í¶6»4ïeé4íœ[¹ë?GD„n;·ï,ˆÂ‚5¾c+ï(!ˆôs™¸«§Ö§ eÿU[äg”´Õ­%r¦WòùÜ5üVn5ñÅix®b§¥&#&¿&XJ'@{$S[Òµ à€OÏÛÞ: .îîž»…7N;| `Þü éL?ÇóªÃ  ”K)°Ç©é£@ÑJ$ Bßô DÁæž1JWô%“%E4]ñd’íØ®äYí_[Õ³×ìüƒk0ùÒäXäl¸šìñ”~/œ¸I#ÖQ-å-;Öz§3¡fù&2$ûï$í[BRa܇|ð#çèô<€°Y4uóf €[•¹Ì(iâ™æ¯6\¼ÒâÎCF]SiËaKX¾A✂ÞÇx¼þ6«LBˆú§j°Ûý݆ÀìÛ”±@á™Ä ½,Å{Tîµ\×€ìß¿@†£Ux%yÑ6s‘ÊbY'ÙVÌY—À¶ï‰¤ÐL®Ö!½óð[pu4¶…E±Ú4]á÷r$³ üT5ž‰·  3¤-¨ìLÈòÏ‚Ú.fNŒÿ{MÅ“þà‹j–°¯ìE.õÍ"W¸ p¡^Zœ+3H'â.›á¢Í°·y[to˜~µ=Lf1"¨hí¦6@ÛôiîÅáPãvÁÕ,c¯Gª~ì$>tæ"»­gŽè½Z8•éÝÐFÞßÓ¨“ÝÝ›þjþ–š\ ý¦wÄÐp¶M1sB”°øJs:‘>ÅǪƒ¯d 0ÿ}ÀæwÒ†0·tåz jíƒ|òû!¥-R¿6¼»­ûO "ýxñzÖ_ùèF™ôŒvv±¬æpÆýÈ\ˆw׋hßZ-êÊÖ>•þ¸Åõ‘nÜ*ó7j¯¢c¨ç!¤'“¢Æù†À‡Øçªê$¨xhiŠ@$ömEé°ߤvˆœ¦r7-ZmÝm÷MïcNMògxJºÍSÌë©7LjêùfŸÌyæ›ìm,Ä<îj”Ðÿ±84 fi$Ä6¹avQ¦‹@t€îÆ|ÞÌŸÝ<Ž9‹}hO? }¦¨%´B¾=(_õcÎ “ê°\׸ˆ²¾NíºØó¥ý…ô òÒ-°¹EPç§ëíZplú;w·BR?™»Ê>XmPkö'svJµGx7È¿߇}9vŽíÁ¥µ·† ̆ <–hFÿ%´@àbÍW|Wâ[µÃtÀİ̫ŽÓŠ€Ò˜Õ.‘Ìè¨ve²!‰>طLj¦¶Ž¼º2]}ü %»õXìÄuƲê÷£M½Á²e)Q^¶¬$ᪧé‡nûäÄX,Ž˜FåW ¢öá®à‰—Â?ñwúõ‰´oܯ_Sç½=`·ûÚs³@:‚äÄ{ïÛ¶àFvÌB®ËäKŒ$J4ÉoóÛ³h‘®ˆr·=Æ2®² å™GãÊ<ëHé$fmê¹SÌ­Üãp¯ß;Ð:™›ÊÅnso+¶h+™,މØL&5ÇG×°S Ý‚ûÙñ»N úÙñÎMsí™à¤•ëx>‹¦Ÿ4PpÓ…©–Ê¥‘ÿµ*¶a'ÅkÉ͸¡MKqZc·¥´Zòw6#ýpLÌÃŽÖL¨mÑv¤!`Ü4…^«–Îf|¿§ ãâiÊ2*¶ýò”ØV[é’èZ&¥åH\>Ň´¾ŸîÇ3ðMquÞ¸¿{¶öyö è®2y³ž%?‹áҷĵ’ƒk É}5öÁãiýwˆž5 hDþnŠ¥¡4œJIù/OÒ¾éDø–Èš‡h)þ.¥ÏCàÁ²m¨±¹HlHý#T;ÝýÀÜ ä\™,àÅל•é‘@i¹åâ *~\µ|ûØOZ£OdN´ñõ#~–­Õn\§…é„°–¼‹Hù¨Ñ-Ç»´Èàƒ îC}‹:]Zkñ-•d‘^ŒÉdÙk޶‹’ ÙMøæ¢6Q« ]`KÓ¤v2§ˆ²gX÷Ö“Ÿ€t³®b–£•äÍ,(,Mj›²R“¦£ÊT&2DÒ¦©Ç¯€k™£6>ù @þ9À,Å•Y•~€ª[¥ë-X§–®2_†´=Ž@;בdT_Êâl"ËüÂ.€Gާ¤,’-UŒ'þ½ÀÅá›ö"¼He3†<÷à½ò×L*C•»xæ5&yŸ“¾ÀÝe #Ð÷h¾o-üµbã8}Û¾ú–7yß»L’/ç›^ó+z ­ â½Ê€9É^ñæžÖ¯ þ\»/CC7°ÛéJ®° 0Ê[t=95¿Yœek)CiçW‡¡<¶ÒöA±—Y éz#3£)õ×Ò‡X;Óç‡uõ7À§é¨]§‡òBmNå{]þm36{ª ÏDö—ÒH³Nþ_f¥ÆMñҔߣ̮ö!éàQ-4¨Ïwmê–Ì|ði†÷Oæ»R–çOä;pEÛíƒÒ Ò>Ín­l’ìÏ–!•¹êo1´Ùÿ6s›T7ÒgœÚÇ.šõ¡í.DÉ”‹€øUéS‘vبÞ¸Ò˜‡¨óð·;Q¤¹ÜœZ4Hˆ_-ôʈ³+ÛÑïªþ›H;œâ}]´“9Ëœ=¬ùŽBŒâ»«œœO­yœC[‘‰•óÞ˜ ™Z{;‡ZÆêBÕ\‘"ŸÂMãöÜÓ.S‹¾/˽ƒƒÜBÙü…°Í|cþáÂ3wŸÁ#‡ <ÁtÈ ·ïÃÓªï¯r÷˜Ÿs×™ š_4s}9wžéš‡vw-±‚Å’káÙ§h#KÒ&up¡Ì“ˆ:Ón%¯e@Ig/ˆ3¤5©öÔ¿n­Ý€€¤qèÓy€ZËæñ¢ûG½+××wNíiö™¶‹TÒa—ŽŽ÷žœÛ(›Ç wy¸Èy×ô‘†æ?½wJÖ¸3B¾L닾„gö,Ž;+žR«§’:Ó8Â'À½ÚË|‡ÒûÚ[KÝ~™Ò‚·“+Ê‚¿ ègIf—n yríTogV2L\8=tÔÙ0·d'´¦­¡_eÃÔ]Šý’æ,õá¶ uÄ]çÈt@`ipŸûä;E¸.S™q”êÏÆÿPîçaÍæE®= aíݯäóóÐMa^F`x,Lá1µyxRö‹«¦ Ÿ‚/ O «:¦ ˜`v§R²­oG÷éÁ%ÁÎ¥­•S&”ýr½—:O{·=µ•@[ ´•Àì-/=!ÔÚ7SŸÖÆúc„oÍÖŒ?FŽŸ\è Ž%iPÍé[ µZŸŠŸë–êz€¶XÇHCÉÔ)Ù9ÁÕLŠù˶\9íB]ßN¯b‘Jp›y' üòÕrnx š‚ÄMSø¯¹8IW}ãñ €ýá›Â ˆí…MMŸv =ßM÷ùïb»t¬l´Ï¥?Êoùº ÂûÚ¯ÃÃË»ƒÖJS \4ÖÿŒÑWDûx†¢Êæõ¬°®C³ò&Ú^ŽâmèñÔž¯ò_JïT7ÒšéDxÑe ZëÁo÷[nzK eA gŽ•“¥ÍH[Zmâé‹ÕÎE¿\‘¦†ÀDG“‰€Jä¶ü+Ö™DùÄ´ÛÚ:}ðA6AôBdOµm\à¡JU@©§öa: Þ/5ÐF’VkRŽ$q|MkgrCÚ©ÐÁc6-÷£­£~J“˜Å’N™N¢¸¬R»¨@i:UÔ&ØöQþyÜgtÈ•/E¨}–²”9…€8ëó¯b93LP×b z7ÜïGºS–Ÿ¹~§æ\óù|÷7< ò@@Ù¹øvÙëÕaŸ„<h\§ìíïB@T61‚_ÚaV>%£^äZ ¹ ë9@¼W¦sdjÁѲäëÒyÏÎ\ë*W&®l _Í7G‡’È`Þ£ÖDe†°Í³“Ù ¾s=£ÀUÑVÙŠ©©ôN,½idG@þNJXíI¶÷%ËÄ››¡Q|m’&â2ž–ýá©þf=k«ÍA€cM@]ovi¾ƒù2µ°9›r•&µ3Ç" [ b •õ ùƒN–@ö&ÀÿÏyÿ]VKz¬ K@¿I5—šýÉ›4­ÉMŠ–¿Ô渗‰ Ñh|¶Bƒ2‰tG¼æ×†s‡%EÂíG¤…Ú‚ä÷_9˜o9½¦ºÇYœ©Œîl¤Õ½ïü§-+Là„üqLzîš  iqáuÀZw¸§øBeî&úKz3_îL÷ô³ Žv*­J}Ÿ^'€ñ/¦_æ4ê+kN©a®dÈ3´¯)»áiÉÜÈO`¢msõ¯[^¿^ošü£€¡k¡%7TÙ)¢}þPÚÔ]ì¢ÀX»@"s-Æô í€ í¬ü-Ѝ‡ìiÔߘÚ@û FGx1zï‘Þ4Š>VßF½K¯g6Êop¸~?#—OŽÂ˜”F$‹ÏÍ'‚#íSðóï¬Ù†~ÖE胬‰›‰¦oú «ù8>›iq%½w=˜“m ;h9¾‚½íEhßë iÔ'lIÛÚ™2}šr~«Ý,Ât²æŽÈ1Í5W™'{µ1(µZù'sçÐÔÅóKã—$ÿ Ø:^“w­iư.q¤s¶×sßXÓ3EŽáƒ¿0gj \­yŒ÷í}ÒØA2®2ѯ6ß(Øœ9› ‹Ã+·óµ­'½“%ˆ´©ÚÑNˆÙC´g4ß-åÞ*ÿŠÚóà33åB5W·w¬Éÿú|7íÃQÍÅîn–_5zÑ·SÂê^$Éëh¢$5¦O´øå—½Æ(IÜFÀ®YÁE®ìŒÐÁ†ÒοR‡¹²Xfw­Ñú[SVéÕ¸ïfŸ“~¦à¸¦ü±lR8“{¯Ôyúê¸}dtŽA`Ë[c¯3¸¼ŽûhxîpòD»ï5¬Û·±4hM‘½î_ OXÍì0˜€l®s#’4é¦÷ô|µ´eµÉ–i•äAZŽP|¡úz!ý{Ï9¼}™<á/Ä•†j‹ú9}]0BŽÅÝxã­ËÓO?óaxР·6‡¶(WsÃfsòac徫Íý÷-[o½µ$•äVIàߨAì`ÊX˜ª4Øû³6G¾N ¦¯-7»?BÛ‘“ÌAÌÌ{}ž(ŸN ^Íå½c :‚ͱ¤4¬-5mgE;DCYif:àþ3@àÞn¢‚æÀóMÛZíÌYýÁ$¢ø¤I´¶¿‹¤e· Ûñžgð½ag5ïùïÕ½À1mW†É{ë'je‡’ñרçõøžÏ€ÊÌGi¢=Š vÇü†ÁÄR¤ÙÅd]€èc I«pÓv×Ρ6£\ îí€.•l8 Ð,]²±–â˜6%¦û’®lhvÂÿd@>iNëûürІ߬ ‚bko6Ïe5ZðЂÔ#´ÿã%† ØTß!Û‹Ò$T¾Ÿ¤ÎzÀ³§2q° Û²Ò¼)GsóýèÒ'½ÉÚÕ…P:î8òè@º‚O I˜ä…©{Šyä&Ñ£#Ù:œo 8ß÷‰ß÷Áá#óÚ'7Lû,¡E@=ÿ2qšMš”J+V‡<ÊÞ·x^`¶´•6¦d¥=š¨g¤2Ö¤&ÏI|&ÍßmN¥/àw^ÂÉ,Œ´°—¢ÍJs··eH_ùpïÅ©ˆ¤}™TfVâgŽG(Ý@øñ6ŽlÞÉÄÜ}µ¾Mù“ì’ ¤ÝZÂ0íŸ û‹MC C¢ÔÎ;òî¸Ê¦¼&ögÁ[j—Ïóm«Â;ÿ@¢<†ÁOÒð–é°L³~Ò\¶&a2ÿ WЦýI«ö©ú‘æåÐV¨øóCG•»HfAœ†x#ö[÷éÕ¸ßTo`ÓbÝss}lR“—²ÚªQyuœb–œ2(øKê8ËÜ|Ô}Ö°Á®žž5Àsš¯N³‹ Õ]ah:Öt¯9ÖìŒÆªÀ¯b ¬¼ôã$šºíÍ«€ï$ÅSÚ½fþè ²ˆž#³' ðŽ"*î¯ý>þäË ùFs}¾+ Ôx¯û¯Õ¨V›õIv”eRÃñ³ -sÔ}Í)f£v{›Ÿ§t%_p]»Á RôL¤-x±7³´™ÆU &>-«ì8Ë$†£c©ûë©û‰ÎÁ^µ*‰f̲µw™-9î¡|g³6#®Gß[~dŸYíž7‹6 2ߤ2, ê$]—ÃñƒÈ£ w?EO˜#I­Â‚Èhs7Z²™ü0eãç×íð3µG!Vzw4©‹Æ}p“˜\ðÓüh¦«^ZÖCÆÜ_ÿ åç‡;¯’«v=‘Á¨K(O÷£Ô§ài@ìCÉß«ŒêïË÷¤ïhÆeÏàz:}Ö¿Ìb€gǧV@æ/eÆå1½SËš_2»˜ÃXL8¡fgómöEóD~‚]|r rþËuh§¿ˆâûßó]51D<´Ø¹èi^òÕÑìËâ„ì‹W>§H±0!ˆ×¥tQ̽E‰Dk#9nÅÄÅb¹ ¾k¯RÏÔZÄCx$í}™`+Ë%,ì™FÚ#¸Ö“íIÂhH¾ÌQäý¤Ö'S1ÆŒOá{‘‡!QêEòªl£¶˜½%ÐúÎÞòý_O]Öœ@ôU^ÝämNÈ÷5¿%è«2HÞ¤963äóDùt* ÙÊÇšYi³øä44}Ð×Ùå¨53¤É_©eÇWßö¬&Ô3BÑ€¾,/üðÍKD ¯oX{–ƒ¾² )z—I–;Ì:ðã@_=KËî&iS¸&ñž‹£«`×™¥w8Ô¦ZúòæPk«š<’&ƒ@ß3q(!&ÎEUOÇæ,x(ˆ#¥Å›ÆV¾K}CÛ™+¾ ã@ßx®Í•›(´ k§+Wg.c‚ZšŠÒÂíÝ }¤BÓ®ôÕ;ß §lÛ1‰<¤¾C¶/cr-R¾úŠ€äƒ¾²á­väŸû.ÐWýæ1¤éèxiQ%{ð:.Þ\ÄðÚ>üNg;7æÍdÉ}5©+øûrÀÜ)½Š ’úÊ£[˜‡…Ã1¦œ×¹¯tà£@ßÝ™˜»C’$‡{öŠ_þ ³c[ŽÏ:ÛÚÊìì+«NæåÝÏž¬Æd» óB€S·EïèêLI›]ZÙ eÑ-ÕïI¾¬Ä·‡Ü·ššjßWy ¯¾G¼p2[þµì ÐWi–Ú ÿÜ,3­§¦ hAß;è³&úœNþŽª9•Iâíô•ÿøð ´DGrݺÞðóf€Ñ-”Y*5ĉ&ûßøKÙó¸(':‘ň5¦/cóixµSúªŸÈòK‡Ï(­Ì@4謆Xý»6¼ýiÜ–Œ”9à@ô*1º½x/|÷”åÌ éåpŸhÕP" ? ¬7ˆÆ½ÆUPI°ƒ¢Ï2]°sÓívA¥9³<²k 3 Õj§ÏMD—e.@ƒó{¿wí-fÙ†5ÌMaYXGÁŸèë‚þÚV(Þr´ ;}NÅt-Z­WˆlJŒsÚªº Óõ©u"þ{ ~TÛ‹©É*çæ LÏ)‹X9ø„˜¢à"—¾H Œèn®!¤ÞHn©»Ð\ßa”ïHË\´‚làj)ªùed6Ðq´Ýçc$>Ýú™îhõÕ¤¨ÛÌ(Üs^ÆìH'³7b×7=Èêc@1KSWŒ_ ³Hbu2ù¯ÈÃ}xDÖF»AðôÍ ‹¶È[3^Ó7$îð0PòE»‹úcó:hÆšbx~{™ò>…²ºžè€<Írˆ›í²§æ[¶/=,ÿ ï‡$<Ù|–aŽÊžŒ6x¹­ö$ ­næ ÀD¾ B‹7Ñ.‹ô~f™|Àó6WÖ.±—?iŸ ´²4Ò¬&ÈJÔî óQ»G-è«`¿’Óý¢ÝMý¨ß©ô•ß-Èኒ¸f¨Y/µtè«xάÃéÈ[]ª'‡Ô³'‘@á•øˆçÂI n-€¾ X5è«ÀùøÿRw\_äç=þè+Ç÷õSž¬€žåýe ÿ…$ûú÷,E_îË ço¯©í­ÒEô•Ÿ›IFê@Sí¢x‘þ茉T”RÑÃpdæ©yÚr.‰nËüÝì™[°ÈKã·µCÐWRð«aØ!uŒÙ9§4‹A_…BF8Ð9gŒäwHÍz ¯Ü>v>°‡€â…gÿNZð©Å‘!,à”#ôpëd. 1(»$ýGÐWY”|),ž!òãÑ¿ÍRÐwé0G_¾r¼•…·‹DÏjqmÔVm%ÐV¿{ ô.ÛùWΚ:˜6j+Ö”€ìÔµ†Â©fIퟕt$íJÄÐl–P×*RÑ ãŽ¤)ä´Cœ›®Úþ+úÅþÎøC DRËl̼MÛc˜Î,i°<›—Úê+òwJJ[öV„Çà Pb*l¸ŸžÐÀ’ɳÊLZv>i’¿ñ=EùýÀ¿Gô@â͹cîcÏÕ<úS5èWφA»)NÎTC|Q@À£5˜ ”“cGšú¤ïš‹ñoKmXÀ𣤠5‘/n¸ðîªíéþEç„æîc’î¶aº°îz¸¬ÈM€:zsE€©ÿU² ›D#j@xÛ%”52Ïp`ŠÉßåã©"PʦüFÐŽÅÝÒb à’ò$žeV¤öí¶…:{ðåøÛßö,À‘;øL@k9À{t²½IIÓ©˜!Kâ¼âBè=ÒÎ5Þ!*I2ùSWÁZNF<Á„gUø â†4ˆö%·5µâ%™Z8Y"àBüèƒÊ´¨ã$­ª8©žçÃ}}&Ãk jî/@@ikŸ à( xÓӬϻ¥^|Û{J¯±æxs@ÞR¤ãL,Kzª9-8º¾T^Dš/¥ÓÖÔƒì ;äÃÒ›PÆkÛ‰ü$ú:à"Mjefè‘\&o&™s£ÌYÙp²ßþ œGš~ö¦$uHÜz!ßÙÞ ù:óõ›¤¡¨ô$j»®ÌCˆ$×FsõWûô2y±×ÔM>ýW3¼qKsW~i8˜üî†bW)y[<-`À˜Gàë‰QÝíö¶_³&>DZ8éM™/fÛ(d«h.jÃÈf-`à÷2±Ðþ+7ÑÿØjÑÛ£Ùò£Í@À–—h–/ó/Ùø&û2ùZ—YòßÌÍÃ,È/»Ü¥´ñÖ/qv 3ÏÑÜœÉÃÝü}f7´–Ÿë ûãÓÑ‘½ÛLwïÇEu¥zÏoSí`³cz4´;X™r ü5^ލþdn>#ßö´Oâ%Hv3¼uR¾[BçmôÉŽ4 ¡Ñ«>+ €ƒÆ£Ýƒ¹ ^³ LãÆæ—†•M}6e¶ÛüWDëþa¸0L`'@¨à§ˆ¦.ÝÝØ-Ü£q‰Vï%æÑ€æ<¥ž¾q´ÝÍp%¼è÷}# é?fJÍÁ¶| ‡÷n‡Í]Ssu¡^âÛ¢WFÙ‡ì­4~ƒ±–kQˆ¢Ù•–õgœŸå±èÀ½GX‚ŸÓ „ïd«ýâTƒ0}p-í©>èÏA¶lÃfAâh—Á€ý›p`ôå͠ܫѻ–¢´pvvŒ­´ÉÝÏ7=ù›Æ# ÷ºCSX`=Ç¢ŠÈÚ'ì—ŠÕÞ=d`٩ȃ~$»!%žkG™íáœíUf‚Q]‘³µ-¿3‹$&YiÀvøŽ=‹ÍÐÓÒ|¶LLdƒmû‡šÚœûžeÂÏ*gÊÅò²ßžZ™vÓ "Œ±²\Ú±¥ô <;Ì:Ë&»£fn´(QúÀèÖdVå>>Ú ¼K{©è\‰ £¤¯ ÿJPR’F=å"{ ´Úv'뮽¯ ßåú¥[‘-?°Ð1­ùL§tŠìñŸB öVJôŒV¶8¿¥;·ðÚ|)7eÚ´äÌ<ØEÈX¼¤ÇFñÏØÀ§hÁ¤»9,= )FÌíG³Ub? 6U–²„ÄëÎÁ1©œ=ÓÀ-R+fr¨„4ÛœÚJ ­ÚJ`v–€Mø_$;þ ?üÿZg&+Úæ5;éÒ­¥]܀ыèNýöœŠn55r“{M#Üĵ(Ðlx¸[{åH~…¡Qb´gÊh$O ]ÞQjHÚ¬¦~ ’fIk¨0À b „’V–À/mç܈a®;ÜI!¢A¯÷’÷™€_:› äèo«Ó€¥"`g[ÒuéÈþ¨HZó†ÛF­ƒ÷£I>çz›í} 7i ¾ç´¼ðºoê_‡~9úÙÝxWÿ€<7X÷¼‹n _äØŠJ-€[ôÊxVÑ$t2@%?‚ròÉ¥ÎMåï(~€¡Ú´¦CnqC[@“è:&êã<Þw,Ò╆ŽO¬×!Y×…uíüókãÁÉç^éºzŠëTn‡Òü–­ÈF®×†½æËåCá| [‡€I+UöR}ú7mïW›v>º—›O+h$€ _ä囨‡«Z»ÏO}àxÿ9ŠÈòÜ ÷x}ñ­ì꿤÷÷ƒG÷zÓΕ£@ê5i[qR;‘¶”|öoʤWÛÄ·§ü~ Ì~›rÑ–x™cPÞ;ñ~™‚P;Õ!x.Õ»~ÁíæØâNæÜtPä>OBÒaMâ _£[<« Ùø³aÍ^8æÉ®ôž„èÉ?Ä©á¢íé£Ýb”žÅÛõ|³Ì&ˆ¤.ƒ^ç:Ø68ëvÈ}¿öóKû@ÓI%"¹â¨'i g–7'åî6'äž ¼¦õ±Wië©æ¯IÐ;{óN‹Ù¥§+«%Ù=>Ãÿ¶Ð=ú¾èy “õËìÓéàÀ?@ ò2a"zZ6©mÍiÃÒ‚Ž¨qk{«<ž™ºûÍù5'c3x%»@)I{iŠö¯ƒ¹°ÙÚ%ƒé‰ÚFÁ9î sml?ZÖÄLb²'ȦHȬÜËÞ­¶{—¥Oñ¿`yL"}¨½Ž†×µPh–}º)µ šás1ŸÿŒ2MíeŽ”lGCðW´ “èÐTóXófýåæۆФ¦VäF4ua«Ùþnº{èÔ…ò[`ðAËKŒïY{<þjoÒYÌZíÌi#ê³Lq+”Ã^ÔÎNû;9t²22—YÔ|˜†i*pIîYÇÿY ·Ö,Ó¢„ß2ðÊžH]ôâ^ZÖa´,V¿¯ÂëZ< ¬T’™mÁ.¢ÈÞí—8‡²Kög¸_:| Ž!iüFc­ÌÉ¡_ée2é.Î7ߟlåP»P²)®¶>¼ÝP4üû›uÐôp×øÑ°—m2Ó#UÃ8£ÆFòÒÙÆ¶öÏf›v·Y-JKß¾<=šöd w-¨}ú¯RÐëüG™„7îÊÔ¹êÎs›OSò–¶²ãò‚«ÉÒö¨ã.HcÓ|·9­æ´¨sq W꛾²ßû$ ZöËæB¥|a‚Œ)³w´Ýúwbn•W#¯2õc)µ2í®ï`ƒ:íÇ‚àc„ô߇®ñË"nºHóqÜ%/V<ŽõÕJ‚Ö­X•Iòö4+oáŸhá .ÅŒŠÌO]êcX¨3Õ¤DÑN÷Ö¢Cz_çl¯Z¸s6Óû¦´ÏX„:}€ÿ—Šâ‘‰âçŽSÃç±Åî±§rã Ìõ›îÙ^S»ò=-ˆzÀã—š)ÐX ýRM ÙævÆZdJ¤ÆmqΙá57†I ‡ca¦P.D›{[ ´•@[ ´•Àl+M@«!M"¤  ËÌÐÅÀÅ™IWq[ Ƶô¾@Û¤ê(ÒÕ4ØéðžÖ’&•Á¶ÒßÞžíˆèç»å¡®:¸ß(N•†˜µ.9’{rÊ»Ú\ÔWPájÎ+ ß½àŠ;…ZÛ97f€î“K`+?M‹‰4ñvÔ7%)МôÁ½‡H×…ÚŸv.íÑù ç·x«ÉhcWþY>ÒvJ§‰Ù exG1üòÊi™ú)kRP-iAÆ×Œ°ä¨:L¯5$ •È™K˜'–¿Õ¥9@à@ƒJiüóôµ<}/#nêá»'i*û`ýÏÔ…#iÝ>• àWM3œ&´ü5é×ù@q7y@G÷g§;Û©î9LÄtûrÃ¥qn¨ñõxH±‚_QB.²íxR5,~L"7q ¶´€IR¸#i7ŽŸ”wÎèž]xÖZhÅíJY ¶@æH 7… äÉRé.Ð"­fiÃËn·OO1 ~#NÔoŒÚPÙ[v$>p ªÊ¤? ƒ[ÜÁ޽ɷ"ܘZÔtJobµ[uê¶lf:ú ¬wD”§7¾NÊ21q€Œ."B‹¹E@õ–©~0:'6k`xÍåf— «ë¦Ú1yýg,Z“q›‡Ci£šÞEÙØ–^Ù†ÕxÂlªHI»/FeAj0mL €#•Í'5Ûñ;Ÿs*½Ö†€jý‡‘ŸÞ+:7;‚ß¾0ü?5”#rÑÎÃNeÆ N.„ÄWZ=zœº™ÆÍyÍw™|íÖå4÷Ký J6&sY÷÷ÓŠ ÐÂÔxE¶¬J& ¥q'ûMç…íHšoó6ÿÆ-þ!lgN¤i=­óòù‘¶í> €ùƒ´}‘…†-Ú&µšõï”Á7ý׬…dY3û¹éO!дiwüÕð8˜^Ð^¥çeËáJßM?ëHZë˜Ëê_6ø ”³²jk" ÎßònÒm”©¹ç>6èß²€Úè—ŠòKjºƒÕÌT¸Õ‚àmCS:kºP/Äû°q¢Ÿü3hR“K½øÞ. eOåM£pG^ÞÙ@¶>¢àE7Òà–}¿à;žˆü$‡ŽÍ=»>Ó9Ü–÷¶Þ5§4Ÿu.Láú <´•}lLõ7›d‘ù»‚wÑ}ýtdzE ïê0ÛÈ”Uþ­@{Ü -[Û¦þÍ‚‹ Y|ˆ“ð—‰\£-û‹r‘Gâ |œ9ºà3}ÙÂýLÞiNt³íåË'D©E$[ÒÑ¿?CЋöý×üÍFàø Èm¤YY’ý]-þé›,ÓO¨X)µ iô¶éìE8ÀÖþµnîGý¿ozÌändÁæ`çMkÏÛþ,8°0;Ñáh:àÕ‘ÆE’ºþ¤±QºÞU¶¸­ô ݦ,J½çH¸Í[0=óÖØêQ”ŽöòÉß• ‘#+3K˜›$;þÀ´wÃ÷´·6蓲z/rÄa’üå¦þ?&e‚ •IJ:Ümûï ¤ÐöÛVØã[¸´³œ¼¢aôÿWW©›Øhÿ¤ßŸÄCúÔ9íp·jª§ ˜&”PW\.ÔYí£s?›áÞÀØ,Ímý­é}»Ú:;h ÀI¹ï­ö}Œ\8ïjóá‡ËðL|×¶ûr% -ÄS‡@ª\ÈÀýI|Òø«D¤H.8fã?Ö”ô/ÒºðŸ=ñéSòGIÝpÈØyù™ÔÞd–oL]Ë€(Ëûá»s9ëuxI‡Ü©=oh/΢è`ÆŽ¼§áFÞ5ÇS,ð¸áþ. ›º1.BÒâGÚîMµ·ò=Cpúôf-ìjótÓNfc™‡i÷ipÑÔÞ¨[9Ð|ÓbG:èï¤Ø5h¯›úŒôÕ[õBôÔý¹4•ò;˜òS~¡ƒÇä›Íã5g’÷§Èë ^gø>ҸׂXÙƒÙM°ëC;*&_‹ì{Sõ$sáŽíè0¹»( dIîŠâ,±PtVÓŽÌý&‡’‰1b{xž¾aöQÔºfß+S^×/}âŽp䟟fÔv蟿dþ<_X:˜s¾MÃÜj)GÀ”±ÚXÅáæGð¶–:Í@œÖ¾cV†ÛYœ•iÏδš'’†3#Ðoùßï*L®~ÐW®ô­Iü²–‡GÓזÖ 1³ o²®ro«ÎéåIš¨ÎNЖd2Dà‰0¶”‡$Ð7®ý¨í׎ìDˆ‡uqó¿qdŒÿt(Ò«!´¥¼¾Þ;1(›$qöh'p°¶µ „ƒ$×!+“\ª¸ÛòJjªZú*H+A_±3B_ÄäQ9ÐWi@š'|I¼~†3˜ÿ*}¸õÕKÿ­#’'íJç–XqiJCY dœz§6ÂI“£€ÔNÕïŠÖ&ŽH@šhÔªØØ]&n:•MÝ5¨gGŠ}·ÔØ—Å‘€–ÜÚj/ó #1O (¾"ÀåHæʾŠ# ÕJT´ºæ"ãl=_ž°Óâ@SW~ݼDçõîu» ùƒ¾Gâv—_(ÎC€mqX^} í¨Ö‚¾®K3'4ÏÏ4—À\…ܽçÅ /†Ã ‹p] ÐW‡ÊI¶lÎdô$ÒØ`/ú*/»R£zvÆ?—9Ôh¡BÀÍ4Êœ)x)Q~"úê¾íT¢ÇÉ¡}•¿"²‡½µ ú*Ήô­¹Ú&¡…+ô•]ã³ÒÍa™} ßù÷¬,zÀµ!€Å}R=Ìž€ŒõL×»²ƒbi›'À?¾íGÙ´ƒÝ­ä¹úç-Ÿ{ž ·}_Â]àüŸ“«`”~LàäZŽ–4¹±@Ó‹@_ñÎ|üc—í¸ 'öL_£ôÕŽÙ¸ ØÔµi_rÐdÏ=†ð&¦¿€'QÓ?ì€ÚðNÔ·v]T{$Ÿx;y-úmüwÈ­f“l|¯cÌ ÔÑaŒç µ$òoUxf0Úy[­î>æs´qïƒÇ¡]«Ý>ïÁc¢®,\—= ìržÚYÞ· ¯éIîV Ø™ZÞœ“ûÅöBóR/‹FË–´Œ  `Ãè«ð>è«g¾µ÷€¯  > Íõ'Þ±öÎz¨ÏQ7 ¡…Ø”ÉbKð'½§bšEáóȯ¥œøìçàž_™Ã-&µˆ9?åK-¤Ì‰MÛQ–€a,¸hYHt!‹¤Ní8Þ.’Ž£‹htp×1(ß‘Cë8ÐvÛ~X´ø«ÃÕ’ë ¯b T_mtã”õ‘b!¥Í è«~^Ê,Ö}ö ž&ò_- ^»áh£«e+åhxrE<‰W{/מ¥­ÖtarAIF%–}mj|+ý‡O·%ôQ¾¿»P{ ²•ý sâº0yÞ'z~—~ØÞ, OiÁx 7µŽüÛÁ-î_Î$Ïñ°Â:è43 "Ú+;‡³Š´ÏmŽk¨Tû %¡³’2h;s9MÐ$¿·©÷•¢Éu!„lÉ-Íä'ÉF¸O’M® 1«¿$ èGÓjMÏ´èÔÈ7h‚\‰:â¹"|ñ2 ƒîVéÀ/‘P™`h‰d›¹pL{‚O³Qü6‘”†l~핳êò'žH ºI>ÿ›våÚ‚@g·•±B4륅”Þh6=Ey÷cB¾­OùVš÷dæ7ód¿·á¦3i®´’Žì2K›Ü‘Ú|gþ÷¢Þì!wí¿äs·ÞÒÛ~óëçˆä#@Â-ᇵ©{-®ºTÔŽºðïìI ¢l±>åý¼ýƒò³‰ê'µ3®Ï¹LÌ›v±^ZÒ¡J¢Hk•{-|I:»Êj÷OgDûrœ «ŸBÄäÐ’ÒwD¯"ͺ)Ëá0Æ9šž”Åt¾B–§¥É[N–hò¾òÍigI~b· 9)A[ÒµÃh_Ï÷õå¿—ÙÀr·ôªæŽÜÔoά  ñOd¤;0ÊH™} 1@IDATçZ¤ÒÔ¶”èzi,´jPœ>Éh—ÁæÒÒÆø‡ÉÞ±›Ù-²aé¯M_L)¨ü¦P§à×µ¥Íá—‡2 Øòœ?}¨™û'¾A«oˆ/Xà -EÝF#å]1áç{MîV›£­Rk›»;»SÎç¯'ßM¾r7•¼Fµ³0_Þ—’\‹„hÈt4ûd'R®Íæß5À3ÇhœÏõH ÞvËáãšÿ3+4ŸØúƒ˘kÔÂÙà¥Ûé[×¥- SMÉtŽ“s%™HrT6éMÈÿ)öЩ5àÍJŠ*ÿ QýàÜOhúo–>ļ×á¦ÿTê¼îj³Õô8€ð“°5í(÷éý oÍœcæã=‘Úír€ã îY˜ ý,-½ënž­ÙÁØ|E$Õ.v¡ŽmUZgíµðÁn¥Q[å"ð>=…hÄ45™õ›·fuœ ÂЖ2ð_–:l‘€à:"ïŠ4Ê‘5ƒ¨÷c[ŒmG”lÌßßrز!àYK ü–öJZ´ž^Wýí̓ʳã·f³šÛG¤3'çG“¶zvd±%Úoj ßtOø\z‘ü‘­ø\_¢ÿ9•q¨ìD/®Ïë·3NßNãôÔ:¤£E4(½?mñ5n>ä_Ðçþ[ ÔÖ¤A}ç® ¦÷NlÏÚUÐYì6ZÞ;ÞÛÃÆÑ˜ggæVÂ×*]•À´Óà0LF6éë×¾“:É]¸!/íÒ]þ¡à¹¥ßÌß ±(6Ec”€Ú¥÷4 áá¯n‡³f,RS>Ÿès}Ê€În<ïâZÖŒá1Ö_´îÎŽ%V Ž¤Ô˜Ä¸Ú}5ˆ2 óƒÄÝè¢à¬^G!£÷ ÇŸ„»4í5¬ãt‚w›S[ üiJ  ôùªÔî™ÚÍÂïI'þ{з֖×ì}³&TÃÊѬ}NT$0³[5 =3 ¯^惾z.ú–³Ó«8ŽâÀ œÿ¬¸ ¨D‡4™p¤á~%ÐWáJ‡Í.vù«Uˇ˜=>•@_ÙþRÿЗï? iõhh5ÙËŠ¶SºAâöLè« »ì¬z™ ᚤá†ôòsu×¢hâ‡SX¾"MIÞ¨H'­´%Ø®.`LÄÎu8•³£;¼Ï?äË&î~2'Ù»™}Kµß—,Ú…@î…Õ_“@ßÝÑ*Sÿsޝ –¤KŸÐ HO}/£ ô•¯l×&¾ò;#ÝÉ‚aKW”‚ YJ¾,lÁW‹/¥y¾!÷?0éòI“0‘zñ†À¿3‰è«Ý.‚Ë¢ƒÁâ ¯ß iÛ«£è+—Â$Èo.¬õAß#­´µ$'$Ÿ·µàh’@ß'Ú¿ã'k¾ôú-õÃúîÛPúÖè =µ¦§|ˤ‚Òè»0›´ó±‡%aç2(LƒW4q‘ÆÿÝL¢µ[ ¯&¬¢É¾’é?×GflÍÚh–v5ÿ‡ŒüŠC×^¢]Ø2Š'ê?§óžÆE÷²Që@_9þ  2Øs\¾ƒåQñƒµœ½ÆÞ3ÆìÇÁ[’·&srt0Ÿ<ÕO¬È®hh³MÔš iÚþ§ùhB5ZÐW¯…ò¤¾7+á7Z¤ö6€. hÚ÷+ȃ!h‡Š#ÿÓ~ŒœÍš)Fž™ãyX5”ÿ7jüŒ/hW™AD–4_ ªm iB=ôS˯`¹ø-Øj²Wè´uz#÷Wú*Vnråþ >Z²ç¤‹ÆAc ¿u‘ĪÓÔ2‰_¡Ž~Jô™Çö#5Çsàf)ÂDÛ4HÛù!Ë€¾RN9ÐòSú›‰ô';ÑŸ/OZê{dRì?—êß-è«ìaû;¢Ü“Ü~>RîÕPziÒèOHxygdÏ;æÅ”LÁÄÔ7È =¢Páîðô-ŒÃÞAÆG‡ ¯‚t&ýe°n?²!ÿ*õú¨<Ê¿Ly¨žJImr®¸sV²µ¸¿j°oPÀ”UBló˜šò ]\uãyëýŒâ®ÀãAYDž­¿± È’–cÊÆ•É­s$««¦‰%!ßá›÷l÷L‰{¢ƒöñDµQ[ ´•@[ ”/Mþ—I€Ì¬¢1Þ$nV¥YM:=<›‡Õ„Ÿ‘0šP1ÄŸiêVe Ýb•Ѫ ¦©rI«d’ð¯U¬Àf½xL]ª&M²‚ JËQÊÓ.æ{ >4™pôw3W—sm X¤í…:€M+æ:d¥th|I@V¶×|’æŸÀ*MæT†N\=Ó2ìšñŽâá©luB$&ƒß1¨_>UkÑTA³8—)w œŽÒpNþ¢‡s³×ìcEz°iÙC5J¼扻2Ø×äØ“ï·¤WŽ2g”óIt¿Ûô¶¥.ϧûë4y,”¬€?Ä"ä#±Wn¡éˆp›§â1ƒß"Ç÷há^€Ü÷„ùÂIÁÔ€bÞ[9èÐ6Ÿ4ÁX{àÜ^ØØLå‚ k—0LìÅD)œ€¼§…Ú‘Bi¹‹qRœ€E{Ìq­s ¯ÒªI :ÏŸ¿Ð§TŽå}ñþ©^¼]¢šÁÔþ"í*ÝBrÓLi8½2}­¢ƒa§ßÒa5qÕwá»uÜ«rsu®´®áûu¸¡Þ°$“ïO˜8¿BúO1]‘2öæÃá=÷SÎß æA{ZK[yA¦®'§¦™ Sí¬ûÉb ñnwšuLøQ½H8Ð} a> BcQ$0ÿ™ÕÌ“ØûôËÎz†?:,ÎÖOzu›çIžg`j!thÜ„ÔüɪzßÒ8Ÿ™w?ì!ƒ¶ÇÜÀ&¹w¨ÿŒ˜•Œqüå½2ÔäîË\ø>˜íK¼~²‡fjo6“iï“2ËáÇä:ÿÿo†Q»šùrcױ𕒗cr€"ùáa.c"iš:À¡òB›ðliþ ðÆée6Vçy$i=nwLÈ>¨ÀRw Ÿ±š_äzŒú<$µ$EstdFCîjq? ¥ûÌ;x@ßÜ|ãy©©£µ^{ýB­µ‹mÁMàkkÁ‚IJ¥@+¡ »=‚Õò•Œo¾ Ï1€ƒÛØøÏKóZÀ5¤y°CøMÖ¥øG&YD³»àçŒ|Á}3 a§°Ðèh‡)« VÄÔƒlÿRÖ©ƒ2õ&ù.lÑÕ;h­ÈÝ{P¨­¯ÍòæñØ©õi_øéäš“ç™a5gSV€FÙGÌ#ÞbÚ˜ºGÂ>s!ÂíÚ°ÿƒI†ÑK»×¸R'õïÉfv1ßæo¶÷gRïoÓÇe1§šÜÛ¼‚&•5·aHô@ZÜõµ])OÚ…Ÿ…øFýy-Ò Ç7wmD&X|3©µŠ£$<©#‘˜gõ¥ß ÕŠG¤Rˆ6±p;ƒw“Ùa°e+ãj+é’@m¦ åìÔ“!à+´ÿX™LÚ¦ÿ[šzøc•F[n*•À‚ ¤~HèÒ“xHé´ÖÔCwâÌ `«Ò7´ùµ•ÀŒ”€´ËU:Á5éP“yÏÌđكè@‘™Ihã ¨qæf0‰VGÛ~õ=úÕ ­ˆ©|jKž€Ùw¼‰Éª¶°ÎKÛ0AØåÈ À¢éyß ØNõmŒj‹«¸œ‹;#×{ÜvÑöÀ™$lû†‹ƒù¦eÈÔøt-ßœøì»V/j €#_£2Š9•ÉÿPÜ4‰´Ÿ ào¹µ!«û¸'­ÖLŸtHXG«9Ø“ N=§a3ÑD‹Íäß±/°%mºÖ’lÓúß¿? „³•§´ºñïOZÅoöÐÜ.ib!`ÿ ¾ÇÕúg€i½£…@MHm¬—‚Ãoä7Á.f(n³ó ¯…j:ãîòÀ{1 `ÇÛrœ2ž- ÉO¤²¹0aCÊÍi–IûL 1sYÿy°ª©o¶@KíóÆ]­éWOÚ¾ߥ3=ž£ö&Õ=n~ÉîcÞÆfj°å´¿Ò]93Š3š²Û€í»•´5Ë¥-~e²ujó}~dÍ£r±a'¼¯{Ò|Ѹ© ôy¹Ëô7Ç£O*­8™3Òˆ+—’_þÎÍo¿ÎMWi—º…{€Žo[3}$ï¹Ô ^ýj_S÷_±»Ù ûC`k<}aïÂÝçNÓâþ ° p#Áˆ,.ÛeRéFÊJ >q3"Z øŒ…µS}Ì;¹›ín · fÚ½(ËŽ =>Œ_úž‹íÙƒeÏ;Ül=¼Ÿƒê6×Ú:’möÁõ¤‘îiæÒ)þ‹™ZØÐ|ArpÝX˯“,|Ž´¯¡ šwÑ W¨¥Zš¾Jä¦>þBì×r¿î ¡_Êj{4NO³}ßQ„“p-ØøÛ4nÈ1Éœ2mWs7àìQl)?ÈÚ&z»“̺Ü8Ž@e6£Üɯ€‘æ7L/üuÐR-e*SíÌrà=g7ÌÕ`&/Dn~6íÒ»šQ2iÐü*i’/Ûcå͆™]M]îlÄÍ'ñßQs>ï\·zº¸ j‡ûé¡;qâp<Ót‘Y1•6RýÌ=o4š¿™íU²ý縭›¯Õ\IúâW”!ÉËaù®fÿÔx@ë)f=Ú(šÃ¾L-nastÝ%¦±iO»³À%£ÅÁTVhs~˜z•þ¯ÎÌ…,\ ÷ h‡ha q¨™?ÿ¢ù «KNí€z@=ïÉÆÝÌ×,LõÃÆø¦,ô¦ø›ˆýÿ…Ð\?ËÏÝûJ®™S£þO%úKà‰Ìf³x®á\>][‘ýõϑЖÏ] #ù·Ë'T{'õu.þ”†©Á=GTyð\i³ÚeidƇÙ7°ˆ,ÖÐwÏÑömÏܺtnG¾<ÇÂ=´™©öý+ð;•ÅÊ͹Œµdöa ÿÄ 2¨~úBúÆjM$±Êü¦ÿŠGGêéúâªûÌN”ͱÖÝ7÷cRàÌ .-:„®8vÂSÓ{»Oj Ô8+Ÿ­ ܆YLI »?À¸L À³æC~ßìŒÝùÝYÔÕQ=ÁÓ3AÁ8ÎIctøûwõ/"¥ÚèOQc¢aôŸâsÚ>â°’@ßYY ~‡9+ÓmKkö—@°F9ûßó[¿aA^(MÑr ¯òóG}•ßô}°Ù‘úúÛÓe®B@F9ÒðjH¡œ&mîè–j@_™p4†I¹@_‘@¸ýS”ß¾¤w3ö$u°›#…ÒôØ ï†ó>MþÏò4à¾Õ$¿$žt «¶„ª|%¾:C`J%ÚXCLA=Úî@_Ź™ Tô•û|{œN÷¾+î§ç@»/ð9š°>èY>{ª=ý\nûÅ€l«í<ƒ ¯Ò{„©¹iÀk¿(ÒQ9ëTùݲŽKÆ ¯ A_Sÿ¦}e¤¿W Ò=*Ä#ìÒhU]ˆ'8’ Iin:šÞ<Ë÷üì¹:ÐWüL»¦iåjš!,Ø ¾IS&Áhâ'Ÿz°<€×ŒÕÜq~ï£a+›¹Ø•]SG}EÎO÷W¸èð¾?ø‰7 *w ¯L<˜ÚÛ¬ÿchqIƒ×Õ“ò¾0 ‰4Ñ´%ü|x´cãæ¦;`üä|Ð1ÐÚ´IØåL§…ûä›ùP«~ Í[G½–[}ÉM¬Þÿ|ÊDS¾›óïZÐWÀ¹+s—~ѵær—4:Dï@x¬OôU8mÉ?ÀM ¯êL挴°#àÚ'Ç™¾Ûüeø°ú:½?húB¶îÓKq§åsGaíלeVÄZþ´dOšÿÞÚ\v¡÷k"Sl—w ¯ÜlWþEw/¤a雑Ÿê}Yì÷®•¿—ÒI›'ÐŒ•y #Í݆µäk%܃› mo˜‚¾’b¿Ð&N¢ì~² ©–%2Gë–®i:›ç!îÑjšió¡i6Fò¼¶ù@ü¿¡¾Ï°o¤øõOã6š¼÷ˆâÝÈ@úŠlK»žšôÎhŠ.o~NwÃŽç×´Ìi”?ß]B¹ü‘ CØ‹˜«Oé`NË=dö®òùÙ° ÜÎh1Î4¬AŒ&´å½Ã÷¾ÎCÓiê†ææÆ­íŽ¥)ÞTßcÒÚCÖޥܤùþþäEͨL{s.moTê%Êð+{ø”©;…t2fE$ÐÙKÌ'ùÝK¼+(s Õqn®‡£­¡Ùj%€ƒ½àö6hÿ&û&qw6Ûw|Ó|ÃyS§¬i抃¾ ?mI~¾µ1ƒŸ¹Â{xµùTî?Ÿý uÒ€ûotüÀZDU¿} Zpèž1;PÓ[›;wŠ@_µYíØ¹ðÖ`Îc~´H—Í£‡_û°™‚ ®yø>\ˆE“GÒ ú¤¬Îམþ:6oÛý†È¦ë_¸Ãn+<)Mÿª@_åþO´|± âh3•[Ãa0“z‰'í8‰@_%S ô•Ó`~@_ù5ßßñ?ÓÔ»B EÑ®ª¡ôš„òåX©ù\n>©&…Ò05W{n PþËxÏÞmýû)÷¸Ãm|·?Ã=à$œ•ßÓm&æNÏŽ'©ák¥:‹‡Ÿ‘ç­r‹¢éÐ$QL^š ø@dàZø=ƒ‰˜Óxt-„îÆ2Ñv6Ð80".þ,»‘¾ˆø¶´ÛÐZ“ePÙÔäþΘ¦É¯$89–hdŸ w{Ú5Wµ7iB |­Dâ©þu ö^j%k¶\i# L©DküBA*>¸X)Ó…:¿­×#˜T ˆùeà'äzûмÅËο÷Íù±-P“K)&+ÚÛÇ&¦,Áfo7áà+¦WéÏÉ•Ê/y4A‹n%-Ÿ8Õ£ÍbÌûÙÿ+ò>`b*.ª‹8iâè&R:8ò¸d &‘ÏßÚ>êêË@ â㬱©-¤pkG|ïÖ‹KB‹'•HSu…¯eÛuÐ$BÇ„1–ªGó.¢Ž…šWÞ»D‹N .OS@0ÞauÆlë"—0™VÎö 'G²«­ÃÚ©Ô¬œ–ïld×Ò‚‹Î3l…Î^tälo¦›Ú¦½‹ÜSÍ5îÎÊWæ‘£cµE?7ˆn Eóüâ6û禼t@Üšh ¸Ð¤Ý åè/LÂ)µkä¼Aî³:@ˆFLë \²Ûëÿš«¢pV«5µ¢Yi®@,xHã\&ä›’Áj‘=Voâ«…)Ùˆ¾9a§ü=øég/ÚÞü <}`å9|ó£¹À4Æ™¹í†-Á÷/ ?ýg2’3ü--WÓ“¤Øô ÉÓé܇@£ãvÿP»·—Ë^¥Å%µŸë\ûµõÝ0`É¿A˜èPJ2‡bê´µ³Qæ(!ZÓ%HlÀhi’î»×“ÏÌKížîh–ÏžØÉÍ|ég>Omcmmf‡fºX/èïyøx4ú€Ï!d[9 å̹À片–Äî±i‡&|þ{3ئs—ì9ælú««s¿Z¹úxªÉ¬€ ÕK3ßÛ'û3m¤ñhbÌc¶ ®F y>‰6,ÍýµíW-b–KïBÝ=eos)‡×o¨ I°…cîzœ°÷ðÀ=aîàÄÆÇx΢eß`.CK|êOšƒÖ„HýGø9xKÑÓ¿@78?È8×#«iMÞk-86 ÈïabŒð±ôµÑ¢îÌ÷©¯­Ìîš»š…¡mø¾©MYlYš…›ÉÖdÈ#톲À4/eú2uþ"àçÉ„ƒóê°áo2¯›s㬹Œï(;™‹™P³²Hi½J™éÀCÓðÂ.éiÙÛ¨ÁOZ²_9‰z&Ï3¸Õq” DùÚ…nÊ2}(÷Û¼ŠîREOU?´ÿ¬LЀßN€·5F)¦Ï‹Ë>1²©¹¢¬¯Éè[‚þ¦4£»Ž#p.íOKÂJ3=½N‰³)ZMð–Sú@ÊtÇRÏæË=7¾7ð€çÜNïÏ>­•œéWä­ZÜ€>è@[/ådþ¾ôæ»ôö2wŸäí8m< †’…dz°).*G2æêzîOÔ™>-˜^Çù­ÓTÒBÛ¾EºNÜk|‹@°ý™ Q#F»RD“ø‹Fû5Œ§z™¾°Þ3õ# .Ã2çT ¾”ñ<"ïf —‰É¿A{ßÏKy_©*!+K\QöVY'ŒË3'â¾hB„R§hœØî<ça¬šµcP¤`µ•À¬)i³&™V§21©q´:•?_„QVüÏYߥƒ8f'É®çœN:Éó@”£¨Ã) Á½œÍÍ„ ‘“†Ä2“Àñw¡—¤ùR5¶A«–ºäfêú=±¥¥™D¥‡s%…*¸]AB7Ý T™‘:+N¥ò“@!ŸtÐÈŒÐLÆñöhB\HE[¤EívÌ‚{µwËti Ýi¥ÇËDƒ-_“N€ÒÖeêFïÔ`Ú'…©½iû»À×ÖQ+O=o]â­ ]€êÂh1[™ÒîˆJÛJ¤-ó¥$ ˜d8?ÙÀ“oÏùY-c‘‰ÎA‡ÿ »%Z£h2aÐïË7ü^MÛ ûv_jØî¦Àj‰I¹Þƒ ]œæàd§Jl¨Ž¨js"m«¯DY0q‡ö¥Q.Þë1;Wo£øNmÁYÀ3ÿ(ã£ïQs$“þ¿§ Ä~_ôä¤ï®{Õ«&¦²íê觰ݯN»’æ¡KÇfaó`¦Ôx¤öíÓš±E#ùiH¤Ã–d/Ô§u±tšæðв‚´íׯxìZÊvïÞ!³„Èo×upÍå˜Ãí²®MÛDÜörMú÷]µhurâËQš¬ã<Èû_ò-pÀ>*™Ü¥Yêhù"žÇé=aXÕD@Ï‘†3Ôˆén7¡g+¹¥æóƒkø»:@evr ø’»v›x#OQfy½+©ô— Á¤›9.z–æ·=ü+½ážÜMz}{¯»b@$-çmjo„í€VýdG³Õ\ ù*ó&ªC;NÉ ¥ÃZ…<¬‹ Z¢5ÇóÜžûñü‹È§Õ<º_Ò6¦§{"Þ¥ÜÏe´+C‹-›>œò>œ¾Žœ%™‚&pš$'X‰qqöb4Ýß¶H³@´ €ŠL¹¨/9-7‰ÃЛÑ6¦51’Û€ÓÕjQ`5¾u€Œ­ïºG͎ا,À/š·k÷¥íôDël%óy-ßݰ:å=°ò0›¬~Ä»7ÕÝmŸUß×¥wÂpE_³9åë4?/l÷²ù4½WxPäW¤±íi Ûf~jÿ±ÝÕ Ã% GÖ¶¿–…§=ÈÛ76Í¢«y](Ÿ‚í1+P#$Â] ¾lþqæ1ûfväýMæ.4¼GòE‹N_–€ÓÉÇÉðØ:Ü“'»8Ámãyü|Ê¿ž®ÇœÒ0`m^³<& ®/Òòïb¾¦NšeIµq'ÓŸ6 ÉrwíU,{ô2ê®åiAséÏŠê÷lØÆ,8o¼²Õ‰w”Ææ\µï¯ñK€smÓŸ·®ÓV5K¶»Ét®9ÀÜŸ#ɃÿÔÖð&uBùvÄÅšÎ&µ<7ĹTwUÿÁ!’F:-w»èÑ·¿=e5”™¶©UÿpÙ˜ÒXÖeƈ‘M3m£e/ÀçÛ2¾Ôµµùê·G?¨×òôô*ÛJ”9 ßì, –{ŒúØ6iE=?¬XüXwÕ¾'njËŤ›^H°¬+öÛ Ó9ùÔ£Öq 䃤­ë]H=«ý7 ck°C»S$ –¦}—£×ÐèÕ|At}‡9S¢Ðצ˜»3â›1ò67¢«ˆ³)‡žK;R_“D’R‹²+E$ŽrD=rö[—ZKZP¤]‡"‡/£?/&ê£iŸb§2OH‡–©ÝÓÂô'þ^Á¿/´¥[&réq˽Að \[ކh؈d“ˆ…ÆòÜùCþNlÂØus9LæØžñcÓtÓ˜Ïý¯o{çïPªkÕ¹ê¾fO Œ.Ӊ̞·Í™©j;Ùÿ"Å9*52“ËZS>§ÆVØ«‰»@h\Ð)­-Ó”YBæ´ôÍÒÒL¢cXáŸjÍ¡mÒ”k)6µ·›g*€B-åÓ7c °Ý½q~‘í×$xà4|…Nñ¯†^ðòè´Fe{WÚG"Fq­t_ãNZjJK‡JaÉ1Ò,¶‘c?SbÏóÛ˜Ç$­e_S±2¼³@b‰ë,sx-0¬2ùF&Q ”ˆº­Ðç%jH¨eOú|[Êþ+tÀ׉^›ïa‡ÁŸùAJîeÞB${ÂI[ý“º4Er#µÄ©J Ò6}U¢r 7ò«L˜W7!¶iŸlr'RÍEÖY¯"Ù'––Öj% 7 oJ“¾9`ñz‘ÊXßµ ÿe¯cÒÿuŒÈßѺ¡œ”6·“ò“FugÀ­ãædÁi  xt ûrtÏ7ÿ*¬ºr ZfòªÎ-ÀúKCÙµWµmµui ´8×ã/M^ïåÙ¦'¤;QRÍÀؖ؅ç4¨×'5ÙÚM¦€Gu˜ßjñé—ÀÐÜuDc"é‘ÀÓ“MÿÈý-@ÿãr?G!4ñ.7ù–Ì Lš7’#‹–“ïÿgï,¥*ö?>[7hÄîÀnì.lôÙÝÝÝ……Ý>ŸñôÙ]ˆb€x¥”{¹÷îîÿósæìÙsw÷îŒ÷þþ`ï™>s&~3óßü¦ó‡Šë<Õ2º>s¿ ïþl¿õ"®ôËšôÓ¾W÷L\ʳµˆïAbîv¾ë<õ-¤KEvlØ´ºø#6!©…ðÁ€/,¼žF(éÉSæ  J·Ï€êwÁ8ÛZU§ç’‡—ûÔOgèt$ð<¹¶‘f‹F¹»ÞéDž±5É÷Oô5ø­O™0%@h¶Ý8»ŠïjB_ìðy?ü—Ð[_ñ}º½á ŠŒpÈ©Ei2—{Šáz?æåPù‘„r˜Ÿ#)@¥KZ°"DY\9—ú (cž·#rÀ5¸Ðööö}ò9–ÚîÁõ籌TˆTxmÄJYÆÖ5gÎíMyÜnÕLè8ù°¤Úé0³\êI@å0ŸÈš æ®‰Ê›gi3^mæ@›n¹wˆkh³Á‘€þøIÎfŸƒXsM2Ë›-+™~õ»rIâxóÒÑíÚŒ´:rÛN›ôE” }ǧFmŠe^¤¾.ó]º›‹3£Íûlô¼üúeb€D_Ï aãqÖÚn¶œcªl ǒù`ësóu¢Ÿ95U€ÚRã’fj›4û2åë¨:±â‚>Ùú? ÷³QE²)=£ µÍqHpwi¸Ú$ëo1'ó7¶›Œß™K[?a6Èc¼sjF:fݘ¹ÜFŸ $¸jýñælÝ’qF[ižº‹‹âl¸¾Zº`M|kk—¾e•Iߘc7)´¹u:ßu}kåÚÞ6ÜH¤yÛåAýa]Œ9^­ÀíøÍBÊnfò&÷ÝØO*%N·§Ltl> äÍÅ%rt€¤pµ¹?ØtH×]ʦM¸ó¥ƒTҘɫâòyª‹­ÓCe?åûBÌíaúi¦ŸÇë·ÀÛpYO”€è;¬‰ ýu(Ã2ÃÌ‹às¡éW’Nln5žß:7ÛÓÜ$éè0‰ ä S-à 4·µ€(µa×ê1ëâ3Z‚ÛàÀ’#À’|Ò·¸µ-¦úCÊj€¤ yêvæÂêÏm[’„ë9Ùvfƒú!fq@¸çN´áÂ󳍨@½Æ~>¿|Üô" e[1„ âºNìpÌ»6®ý“¡¬3ƒsv™*ž0«Ó»V©;0h¿;1‹úŽXì|s`ÕfÒÆ9êh’ÉǰN£¾ð2çÖïn^³}†2‰uÇäõ;Ø/#þQå£6ì(6ÁÆ}«Îòµu.1b‹›©©S¹®•9}§GI§{ö3.Ö˼! P©ñz„y8¾²éXo¹›<\jO¿nº·™‰ZŒƒÍ[HJvÉh8ÍÎÃÜØs:Œß³—Y³t†r’út6s@žçù<=J:CÈ®K-›£•Ô¬˜¢Ä……\iŸ¡¶ aÇ×ÖržRKõ˜m[ôÉ ß@­¥Úñšgy•.Ù*‹$8nÒJ»”>—ÀÑ6Ÿ‹¿-cLeŸÆš57«Ï5Cmà·…TyѦ›žóŽ(¬DBK²¡âH›÷“¢T‰ƒÆ…Έ,æçgð¿Ø ú0˜—{>AyÀï ð©QÖÝ ¦'7GqBZ¯,o–0RÑææœ6b?Ú ÖfŸNü'ö&ó†3­Šô@úÀ`ÀX˜Ä8SˆtšGê?Ú4w~:Ï6‰ûÍí\{ßy8,-Ÿç=Àœ½].’ݤ‰êy—4ÎÄ·›èÅGSà ‡s§/š¶ö’Iÿž£<Íì÷4ë·C-cÁ“,šB׊Vf»Î=ͶüÖoßÍ´KT˜W§Ž5·Œw{E£æy|öÛd3 nÇ.Kš{®Bs\PÊ þ‚WžŸ\c>˜É \‚nû•1gºY»ý¢fßnËšþ»[ûº1_™1uåwL‘Gßݹ¢Êô¬jS"'ÿ^nÁðWÉ}®äLá¼I²|ð˜afFºÁÖµê|Ív‹šæÎ4Wþü¥ilÄw´Î£öÔ÷¢Ôû_…$Åöÿ•¼EÙŸÿõáã¯ó››Y¾^¦ù¿0âIÒ+|aÕÂHsAÒXqéE\PÊéÒ[ДšßÌj>J“ÍÈ}HH*#¼ï.œÑK …‘}ùHÙj‚ÕRš[F„„æk“¢ðŒB*JMÛµ\Ú „í…æ—”GÅÖQp‘.Ss“PI+:r’sήçäÕ„X“s¥âô >š €(¬b¤€÷Bq’è´"*´Èw½GÀk˜ì¤@?ŽñBÔuñÖ!^ø;œžæ×˜bï¯2iSÒá™/~“1ºÐL‹gMÊÃ*Iò¤X¥ñTþx³IŽ |R1ÀN ‘hèR … ÝÔ]ÖX¨‚0åêw,ªñ”<¿R-ÙöÄùös^$,Qj¤¯›O ó«€$ìeFŠ„þÑ'(sÕH*úëØ¸¤Þ|81Æÿ  „63DÑžÞ¤Ÿêb*ñ”v¯˜ÿdkHó+ýºiåÕH‹öDŸl5–fäÝX"¨Dê"\Û1 {:odøŒy‰Í›€|‰æÀî¯þ:»åt à…Êk#ZÏ! š%üã©Z¿Ë;gF}±  u ¬T\•Q7ÑDR9¦n®ì–bœìeÛj.®[´ç\J›¾¤ÏJg²¨ß àƒÔ\<¹xOzÊ{¯Çg{™Q’ £>ÛH9u}Ž ®n·i?r×¶ÔD³;u9^§ãì’‡>˜…þñ’&çxý/±LE|7ÀScY]ŒÔ½Aúh“X_ë(Ô÷þlœù–Â>ÖºIJ<¯ßeÞ°p{Þ‰'«® Úz‰Ž”¦ÉÜÇo°C”·§ÜfOã]<ñrÃߨÆñD_ >p05€´€3U€M†&~HÎKjj|Éiµ© Ù™²Ø<¶4¬€YÒ¡ð•!!¹Q\*B4ïÎEÆoûn_™aYÊ Õ̧<OÑû.´ hÎJÙ< 3`iQà-s~Kx¡+ÿÍ«Ï Ú‡míµKY¿¡ôõUÙ(˜›ØÒdëöa<èfÝ5² Fë’yÓ§:ƒ®À;Òm=Ù<ÛÎT¡ÑT›õÛ˜5™{5&ŽÁÞò;XQyG_Ó‘zÐø£Ì be&­åÀ#é­×æÐ²€r·Ômon—´ql/í(ñøyÛqÿ.Æ+H’¼öxV¶Úü˜ØÁ|U?Ð]‘M0©Ò&Æ+ñ~fùØVÇþ?61$u7Nb®Š×¡Ÿ·ÊôϾÄ'}fVk¤,c”K’o¡Ž÷c+hTÃAæò'àLµtñœ®f2ª úÓ¦ç¥ö³ï ÿy>Vã[ù–Ä@¤«í…޾cðx&Žþääýr€Âu}~eRâÞ›ŸšiG´(¿« g¨ç´u|Iô4{@xž÷œ¶yÖ<‹THT}”çä,S4¯‡s*ùŸ°%CÉ“1ÁSN$N¦Ì¯Ä]#€OqÊ6¾³Ùg/óýsñò7-«ñïeÃ8ýï~„‚Í;îô7@ý2Ü7 Ä‚;šíYú# "¾«1I¤S’”>5ÖúKêñ ?ä´¼š³kL^X$>VŠ´QýÏð @mì¿„Ô&¤3? ñ,6˜âÃïhè^ÕÚl·HO³=¿}»õ6§ôìgÁ³1HR¾7ãײß,©XÑ–{˜eªÕuÕ—‘”$„%¥¹iÇÅÍÝ–Äîjv_tisØâ£ýr6y!Ñæ¼cÐâ ÓŸZßÎaß¿?€¯êZu>p±åì&†@áQ-”ô.õ1“®úÉ‚’^e-LôEÇÿ¯(vác‘RŽÕ4ñoi<´´É‹ÙÒÅp^dßRêøS¡ða7M¦}´ÌnN/g8ýBæ&`V¡@¿ƒ›n›väíÆ{¶°°“€sá kίÔS“ZQ/Ñ"s© ä¶òŠ'؉©Bç“À}7.tl¾Ð„p°ypmÏ€4ë–.Ò€Ò€…hîÖÕñäAàcfçÊ‚™6±!ïð& -w\=&lÒ¦“t>—¢.Ÿ©‡0c%s`—¤_qé¬Ëàöè+à’èbÚ}›øqæSñûÒ¾7¬ç˜jÛê[ ãm„±ð«¼Ÿx ¢å(¾‹o¦v*O4Û%¢£¯Žž6GÐΊ™ômlîÁbëÙ‹ç´É¥/Hp›`G固}¹pì3E`ñ¼»‚„ÕÓ²éƒ-(ÛÖ  vJ–!]¤ÒkÊ¥T^˰ÞÞŸì'9Kl Ì]Lýµ…)¿·«¿ætzïÌÅ[,0ŽÄr”:Ó¶M¢™CžïÍyÇ{Sg’×õ­Z‰«n¾+¶Žy¦‚oEâÕ£Y<ÍŒìXôCטËêlª¾è«ð 9…n° ¥Ïû+Ðj³øÞ¼ƒò4_ð£|RØ÷ ¤v—;–ßÍ^$÷—cÚ%)u‘ç-ý ›¶|÷…¿Z^×x=·'9¬+¸‰9Ùü£áTÞ7û&N·å*þp; öðËküqáÕ ´C¦ÂlÝÚoEû×J ’Ĭ¤sO7gð í|GÒ–$ ªñQ›šÚÖæ@ûúm4ýñÀÙ¤Yi`FÖ•æZ6”îÍ»|O³c&i¾j쬨¡™CÉù\ѪñEû8ÅúoGÞÕ è¥f‡¬à« \FØÞÁ6€ÿgõÌ—æ.ó+Fõ”ÛøBRÅukQÄÙ~¦M‹ì?SégÝz¥ý_PšŸGY©ßk)ó{ðÕàSæ)úè3ÎfçŽ:éÑ”–¥¿Ðw ÒR€Žô±Žº0ÏŸs t>ýR¿R¤¶0€ •œêüÐ:Ás[š6~kˆ[žþ:~;qÅ\‚ªí­KKÎYŠ ß¶‘w·ìÃ8àf?î^µÝeØ”$ð‰Ù‘æªúýM_6_ÎôAæÕ‰£vU!&æIÉ{|]/ Ü'2/P_–T¯.uKÒÒ’Vq‚ïÖïÜÄ[ï>;u]È}Šw""¢v(àw0ª—z8^³‰Ç–!Èòüo6èŸ@*2†ÀK¢¤¯ýÃ)8 Mô~HÂvbý\sËØoŒÔCœ6ê#sÇÞCÒRôä¤ÑæÍéã­ùŠš/̇3'Y?©8ï§Oíq}œЧwwøìiöˆ¾KC‘¿òÝ¢j&^6Î|ŠÊIëXÿ¯ä%J èW¥£õ,WÝÞìºèR¦WHB·Ô·Ü9á[3­¾Î|‰z«&-ôîMkÕ è#æN·yú‰gåMª ®úy˜‘ GÓ¥¥&@e 5JSaG$µRQ!ý#¹ŽïÒù+?%]û”1V݇Ê@eñÑ, k9RY]÷¹ïTüP››Ì©œîýå{ë¯vV·‘K%gr½éÐN—|7éÐÝJž·Š§‚À_# .)`åM*I”W§8ZçQ»‘°T ˆTÿüúö1¶>õ=·‘§^¢Xè×¹Ôèû•õ§{ønÅù›þ.ß«é”È ¤§°¥*´^lú½¸Æ½npÁIžó]xOqÒŸUŒ¢ÓMéål ;zÞ’4Âaí‚=ìP¦YrZ -l*¤s׺5þ;PL`‚&³…òð–¿ +Ö´(R»ÛIqôR»ù™üè Iäj¡£¼ê8]5f7iV ëø{˜´$_û;ómõ»9£9ü†I'J©:qa7u‹QßA*7æ—>°ð.ëiš‚êÂà?—ñ•‰¿üKu¼tï(ÜÓ.³þâ<¬ÃT¡rp±'üwvö=ku£¼‹Ãaä`dñ¥­áXõöAp-Ú¦6Ï ¬b¤²äU1HîH:ž?‡sHu€M’\11ØyÛ Šð":ð(`ÐÌÎå3 DoæîÃ{ݱâ>€ ‚WJ‘ÒS¿s$ýåÊR?ÒF@Åkâô€GÍ‚Vd1) ·ïýãêá±@ùä|”‰JPMŽ Ø7Ӧ׾¹}1I©…È›}‡ŠCRT…‚x¯æ¼GʬlUvD¥tÝ­ô’æ}´•6iŽËjÁ%êݪ ˆz°»ú²z"mùê½-’‚à ¾7ä-܆süi« åyÚýl.-;ŽM¯)´©¢¨ Úã·ûüx›E|Ǽͭ٩S±Yó¥wm+ª[]ŽGYg .'.õÍzt·l‡PëQ¿ã´àmý³YW`À¯ÊótïŽìŠC ©©áç­ß®Ê9½¾Ìy\`Ö‹Öæøw©Ù‰æù$:2µÅ?–ôšÒ–yå„?§‰//â÷sQÑ1Ò¥²mt;$8ûeZÓÒjŒ6×*l‹˜4ËÖ²ÓÿíÅÑfÊ ;fOZµ~œg¸äx.nNO‡:ˆÍ•ÿ1÷µ˜âoœt¤LaÒSBÓÌ›­>1±ìJ\,ÙÁ©¿š÷©?æ¸Éù`ëÍA{c~š³•y iÉõi'_™Í×i£Dth| É¦N´ÀÕ ¦Y5Ñך{À!­|xì{sRæ¢îüh™ÔÕ&Hf²•Ç\.๺ð˧·;0Î0æ&.Ãe%{™¤w$©<?;¶W¾ŽWsKòlôœ/ƒëG6wRê8únS"ß·à\c½Ôïtá¤%6j8Ì»w;¿{i{“|‹NeTb¾<}½yÄçÙ.œ{ ”Oñ[’ ¼¦”ë»»Óv¼Rmª ‹úYlußK|6ÎT8ÎÍ8Gú ù!Ty¢¶|É|ú:‰öÒ”à•‘Ó2¹0#hOøVµÅ­|s/ö^ÃüªAÎø™ïYäц¼ßÄOc§6D¼ ïe-ø*»t˜ À}†¹¦69¤JAÜRcÇàʸo¢·¹#ÑÙü'ÑÕ4&/$OØö©ïìÅ&L›ÈH¤>ý2óè(½Gßh» ãå-œ†X‡: SŽ£‡]©et[[Šíjû„N•脤{ªšpŒÁæUú€* Ü ÝUÚP±Psߢok•Hí—ôr¡wÏ ‡t±h.’ÎÊóÐ_GšÚt4{.ºŒ-7tß>þk@í)f‹N=ÌÚ¨"x™¼©ûÍÌiô&^·¦n¶Ù ©Õ½‘ºžÓØhn7¼Eª lfþÄ?Le^Ục¾!™H˜»ÝæÁ8TmÜÊÆA%Œr6¤^dFãß&íÍU‰]mð„ß',ˆ™8ÅO‚ õ9Ÿ¶µõéãñíœsɧ§~ª7ýë!³6*"¼Qט«Ù¸<¸@›ÑÆq__] o÷ÿ=„t5<Šï¢ÓIGÇ/™ ºf?÷ÂT}A~Ä3vÄÞÚGE©ñî¨K;¼+ó~¿–:w$_+ù‘j¨·]¨ƒ7Ì:õC~Ö$ÅøQðÌ3}g]<ö¼G¥W˜öäß²ò.ôjcÊOàí Ì+/awó]‚|b㉨ym¦ÚK c&Uÿ‚9¹ö3žÍI©ù¸<Û6wÈêÓË9u ¡,HOÊÛæà~¬D+ˆøð;ŒÙ‹ñÌÉà"~ÁF/z¬k¹ôðæËgìŒËº+¼€ëðºÖCàÃ&±ƒg­ ÕýýIÖ½¥vÃmºñ”£ª[«z@X0À}g.l Mñ5PÍs €!ý"óay‘3¯Âƒz–6õ0áÄŸ¦q—ÈãöBÛ¼ˆñýH WUÞN(¯ÏŽ¿6+ÃÔp:¶o—æRþ†•mL}&c~CU€šÌ‡t[Þ‚¨Ä,Àóë9Ó¬j‡î•­-0»VÛ.¦KªÚtHTš½»ö6[Š­‰›“"€ÖR€·(i äSú­ö Ñ1=V6[¢I‚ö_HcÞ0}Ú¨}éN/Fsß² s eçT€ö"<,çÝ»øîÚe)³qÇÅ,¨9{&ÀîwsfX{RR°s—^<çrC}_¿6ÍÚm5‡îÚei[æápU³ÀôBW¡¼Žè¾¢Ù]ѧ¢2¤]²Â¼èKÉÎJ×›ÕÛuáÛV°u¨v±e¥v5‡øÒË;ðûpTh•ê´¥H韹äjf Àôëfš§r/«ùÜœŽí¿yÅ‘¶ Mƒczô ÔAlŒþgmDHºÁ(_Ú`ïÝѯÎ`ŸëˆÇlu`zGX —òŽ'Øñ¥÷‚HVZ²ÀÚ9æãÄEÞ¥_\žvqö.Ó¡vi¾õ† ¨ŽJ{@Ý +ñ~ùTho!Ý-øž®<÷ î¼`ç ®¤Ü/¶’ã(Sú­I\ø{ùʵ-ÏãKÂ.™ #“tèòŽmÝ\q¡ooŸÚÀÝ϶õ” æ=NSÙ¯·nù£Ìd¿Áð³µtÊ~jvе©&©d£©ƒ9+îµÄQH—V7@€hÉ9ê}\Tf[µîIß³#o|èm– /õ€¸§d~2ÁÅ“.OݛЫâ L¹o•·6]%M|zl©Ü¥˜uý¨ßÏå Áü”g ÿÍ Á6®öâ•‹$8Ó™'©ÓǼñèK¡c=ÏîÿUßÒ\ôÚÿj˜cÙ~V"q ÷8¶”DO4 j{(ýG Ç9œK<§£OµÊ¼V·’YwêHþ•Ô‹¾÷®ÙkšÓ- ¸m°: Îæ‘Ø&ØÔ_<º(óõu¼³Úç\x¦‰­iŽïéCÓio›â7ÆöŸbÃ̱éw-_ÏÍ)'ÐÿþɆF áØm8Ǧ¥?gg;S,÷xv]zVµRYŽ"ým©º(ç³ér_;Wïéæºøps¿‚ñø¦ « Ú¤½UšqxN|PžŸ³hL ˆ #5ñ•p÷¦¶V> ô-JÊ$ï ¢6¨V¿+ìÕb×Ñ´5ú]ÅsĬâ?Ißv1Ò8‘¾<ðývî6¾ÇáV6\¶©ÛŒKÄ(3hkm„ëÍs€½N›áêâÄÍæF60ÖÈ]R§&Ølz)û¦Y ó‡ÉÍ©‹ÕmXZ†¹ptÉ¥6ctY¯tȇ©1dÑ¥¦vßy ›&Ç1:Ÿ‰Ñ¿¤âB¼9¶"±ªÙLÙÝœ›]ã5¨Ü9Éô‚‹ëÒϽ“×PsqÔˆ×F‰ºiØ×sœG‡èøÄÛv¼ 9Fưº5­M­â.+ áyê;ˆÄg2’|¾‰y±”ÕU®ePÃ>fÎeÜ\ŸÍò<ÒF'bÊ"©å¡•&üëÖŽÑ >Gù­ çþ‡˜N‰Ú¶ŽŸ7ÇšŸŸRcUÜ8v¸ù÷ŒñÖmœ5Ñ<ˆš©|øçäŸí×52SŸ0Ï+§eCú¡W„oŽ:¦*M—3×ô^Ïê§Öå‚IÚŸ¤€?ñÕ*üê·_Iýªíê÷-ªDRû1?$ ¹s*7™è@7æŠ\y«sÖÉ<ŸðÃûV͇6+V¥¼þ¿Ž±E§ÿ_¾}a}g¡ þÂJû1RzxÃe9Òaù%Vç[˾9?zY¶è¥máHË"û—2 —ƒH—O$xnÆßmBªõôÿˆKK‚ÛÝŽZ’ ”tN”4ev:®×eÂ5ÉP èÛ5¦ðr¬iª‹¢–Ò4"hÑYŒ42¸‰~±0-u¡Äû”–›ÔçÒe¾¢cÕ;E$ÉV_-³uoàÏMM&ìJß#I:éô±uoù3żoùx•s(¨–©Uš¶ßÒ¼ŒŠ•÷Bcµ.×xž¹O†ÊUVßn˜Rïµ½lÚ7ÄæW™›lÚ‡N‹ $ é²7É)õÓ4·›åNQþ[Î U »t¹øŠ§ã”ÿ)å÷iy$]Ø™[ùùõ’çIM2&åÈï¯lÛ Å_æÂê[A`xK#ï,›Ò °þTÞX—{kꜜ¹“ÔLˆÑÜ]€º# 'hÎêhG梇Ò>´É»2clÏúÍðÉ»ésnüÙÿÅèOë6Þ/û—zýüŒÌt¿'fpË¢GºÂüT¥6L;,@_ E¼?òk¬ŸJk|šbéø<ÖǬErøaÓYzÍ1¯_Û÷o¼Ù<Òx’©‹kðšô‡¨‚ªSK´ãƒ^E.@â‹9Òœ©PçBst¬Šñ)aÂk¨ F8|Ë̽Z¼Xèøô‰òÛG±d~Ow€ Iû‚¤/;UT¸ w¢Þ¨sp?IEnȺTðt™UüÇ}mî|9w–é„ôì9exåQX{O^€2-sì^zW戮kBmƒ.«[ž|ëX¾üÜ;ÜwèYê[Ê|½ý6V eGNmEë¾Yççž’ø<¥çªfK$]Ói ˆ^\óé|ƒ’.Ý?ê™ ˆ¤J!L¿ùc²mU–ýÌñ ÌuÙ/Mïyƒ8õñ¥O©ã=súréæd®ç˵5mT¯BZ=²¯2Í ²±'ü`gN Œ±ÎªxŠ‹Ç2憹˜7êÜ!)N[ï,Xµ#m @C©_±j.Z åéF`Œ±uømåÝ£ÀÅiyê3Ø€xùÀ”Ùj1”±#;DzšcóŸ§v5³ ôeaj—¶oÞccÁ‘Õ)žºëÒ6O)“êô,6´¸T´òe6¡ó‚Æ©7K@r•ošu¹`m6ÛÌe¢4-p²ê.Ês˜†aie~©Aüå ‘ľƒ²¾u,cžâ’Àlëw̽±EÃ¥aÃßEžvÏr{ïš™¸”þâµ/mt¾­¹Š$‘€©dÝÆAW‘8Þ)yò…ëH=)ú+d°ä©±q¶ügÿÆË(©‘æ†ß,€9ýh Å(Éÿ'ÂóðØ.„Ù5?fm5äÿ±¡¾+áñJžÅûwŠ8Îõ‡P¤•èÓ®;gÚÿ|оŒ¥Úàt´nÅ«´ °x­6\§1òv¬Œ.ìFó†]3Ì£­&ÌÆÌ Dš[ŸÃ˜>)õmþbÀÚàô.¼CM Á$ioÇÍòIÒîš—«K,j7‰¡hÃöÒÌgf }gòµŽZel]³:ýsÿÌ¿ÌjóN³s¥¦Þ»2j¤ŽJªU–¡]k|qBùol¹ÍŽe©!¼/úb±1¡Ìt'‡.É8|qȾÆÌޘ쒈äLe<{ÃçéÓ¿3å8ûïü¢hòÒÍ;¹¾Ö¬ÛÞ²##UÔ=¹:†¯ßH,J D…vc#4ÐxG÷öž¶d?ôÞ.mVnÕÉow¸–¤2Ea ïWÞYŠJb¶”ÞկЕ¦8¯©DuC’|ê•-ý—V©w»0…žÝ«¼²…*GßøÒ¦²KñS“~2ÓП¼@äYK­nŽFe…T„ù¸-yV´$ð„•j‘“¢uIIr¹ º—+€ysÚ Æ^¼ÌZæ0Ôl€¾Ì‰Ðµ¢¨Æp4½¹¥hFcÉ£—¢é}ªïÊx@˜kò •Û cٵߨ&‘:Ž*”Cõ‘@³#ö°µ¹:ŸH;lÒ(«Þcßn½­„ºô?KúØéާ÷ÿÙìm9ýÿ(ÝýÝ…ñµZ|‡IÓÑ?“öbââHË&GÃæüqqÃÏb`a8ŒÌ:ò8¿4—ˆîh]©4æÿ ¥R]0¿b¬MMlõ†¾þ‚R²'’¬½×_Hæ–“ô%H _òàåS’sjsZú‰‹7zÎö¯¤ˆEër¬€³Zjãèk»4H`åÏ&RéìâÂoÑ¥÷Y¼ºm/u\6ÉJžîá9týN:ö´ìLž6й²SO#YŒJúèúÒC¶^XHÆ5«¡R¡š¥!¾ ‘âyƒ%<°"°‡ ÒïðŒ^Ö'·É±\8d‹Í½€¢Ç]?•ý0ä7ÓŸEµ¤ŠJ_FÁ?X­!~¢9..à¤Oàþ€ý6Ö2H"æS˜+!qNw.^Ë¡ôXÄ )óŽdÒ†â‡\~hUÚȪ'eÕIUlJ}@¸9Ô£N9¤Å%çúIy­M'$NLÝHüAVMŽyôÚcºJš[ÒuÜ/ɽ®ô³'Cu˜¤=앞D{ñIj' ¥m›8ÈmNX•¹2 ‚™^VT'´ÎŽ-λÞÈye|7çÒÔTùù¼¾©»Áê—ÈÓÖ¯go¼Çº 0y7}1—TÖWTá§Ý¶O0çêÅ÷Èdßæç»É[™¢}ÀA¿‘j°ós£Úeî"Ð2^<+UË|¦âAì˾L¸Æ‚(^€èß¾¤¿õ|šùõ^ïäy4mÍhÚË´çɨËמg¶CR:6ç|³<ë—cý…²ÔŸˆ$}èµMê’Yñ,ÑÇð¹±”ÕXúÿú¿6«¤/<ŽŸÆÊ³¤º~ &ÉåÆí¸l-î’¬ÌMÖª?«O¡½I5̳é H§#.Üá9™ŸOšÏe§XpË9 êÊVÞ„®ÌK}«‘UR–º²êHj±ûN¥ØTó8§Ô»Xê¹Äè{`k¶¤ŒG'WÅÜ‘ä§n-Ì ^H»ƒ1¶¹éÛp/ÀöÏ0{Q¸‰]ݵCœ|w=&Q_ÿ Ù9qš:‹ïùz;swlRÐ_;Ò$AiæmÏ&Û/¨•¨3ÕÝn~n=–“ëuJ¬pöŽÄò $GxÀ³y›n)»¹eÕqQþâ´âÇUþ‹>ð&¶Ž\X·i6Ï6žf/ÄÑêe¿ŽzÔ‰€¾Hnº=W…ÔØ(œ.`¥ø¿½u¶U¯r|bc¾ç}~êI]ÈÔ[ЛhC¦â$ç½ðŸ‰3J§© éÞ^˜”ç÷S›.ßXõLÑ7D7ýžr±ê &Ø;!.Gòû 6×úÔ]aõ¿kóRR¾šé´Q,0×< Þh7œ2}„¶ù/æÚÿêTõVh«vQÜ:¶GØ®O;”÷`Ú6lÝJò:5Õ“>ÓçënV±·Ã}› ÛÃ}¶d¡y¦¤¥O¸EW›éÚ$ŠæBo¨Ïjc`ÞMü‡}1yc`•A-X÷sdõ$;ÛÏ>/pö…øŒ…ÛJát-±^?Â7†´]Å·wš„´á[èð}“ß HPJ-ƒ¤nÚ¡»}ÿF¨"=üë(ó%`œ.MÂÅ]Ÿý6…pt¬-RÁd]û›‘þÚŸxÞ6a„u›—MÛg·”Ç~ž›\cÃéý:ú^Œ$-,µ:¾//J}¥;pô^RÆ£ëUá$|Ï/ß›¯Ð÷º!ßk4å| rÎæçÚÙ€ÈáͼÛ*òG’­Ê—Ô ¼È…bÏ£óö…)5Ah©§˜èûèÄÑæKò9žËÉàÛ‹Ò„ò— ’’wj=ÔV¿g1¹äû¤ãVÀ»ÚÐèÚY¶­L«¯3kµãb•v"0û{€Ýi\ަúÖE|¢úLÚ,ËÅ|’0qêÏAݽ6 K ’.a‘¤£_¢lÕ6ßcÓâjÔeÔ]×÷¦€Ì²ß‡DøäMå<ä—ïP/1Çê!VÑ:Ú¦\j®Î;R^ÃÉëPÀ_õ%õ]ö'êVÙ´?•û^…[”¶û7ýw–À“n2Ê~T"3äÕ"#KØß´›]…–å/+ŒŽäý7ÑvBÿçç8ZÒ'rz_ åp” _ò  tXêº+vÍ M)ŸñÛùLÞ¥ãÃZIo¹ôzx‚‰Ô\ ܸ¥“\?ý]ùf§g1üJ×ê6õÓ=_V_Šå¥޳ æóiç_P~ZdH7l˜ÂºùÜ…NF·Cw^„é8_§dØm‚_÷[új&èך}‘Ò4pÊ&j>~  =#o‘àܤüûn¥¢);@½h€ˆ‡`w¡£@V‘wœßs ýEZñT€Á÷7 düÌ‘€§pGÁü0o>>T×KŠ rïw)N¶ &æÑ> |:R>î†øGCêJíà Z˜û$­Ÿßá‚0Ñ­~ÝnËå{ZtJ_è=,­î:@/Òg[=Ášÿ~QxÞDA*µj—8È…ýÁBÇÄHìQ4š.ÿ Ûøñ„ëÕ$l må5—–ï;Ë>³V×õ›øIç¢V#gûEy€µ“™WÍòHA¿(Š>½Xs6úhW 6álˆh³é¸”N£ˆwIÌ€P™+ú·”Wnáç%(½âK#¬M ígÀË÷¢]ߊŽÕèŸ]²H½ d÷¶ÖBnl êλ )‰[[¾d&@¡`YZ^ú~ÛFXjTê)‡ëŽã[èsÉÛ-P¤EñVÊIæfÓ¡gÅÔbYˆú´gcæ Ôý€Ÿu¸WØRÔiˆ‰J´Å·¥M´3÷«-V}Lž$¿[`¢"‰ÁÄ~À‘•\4ÖÅœßû®üÔ>’ZÇ…ïõ·ÑÂV· VòÙ„úY [çZ¥ߤ†àµ2|çòµæ¦ô=åtsm>—¼k‹fŽZXÑ/ÎO y}¾=sŸsnnm üaŒ²í°â Ú‹¾›|‰êðGõàцHóŠÿx@†ÔcžGŠö®zНÆX—2fîA[åúꤹÐq}x¶‰©l³?›3€ GFN]˜Û ì`ί~ÑìnGXï.é¼I}À~ ƒ¹ðï'»‘%ŽFù%N$ÀVæÔÔƒÀb±@—Áü¿’(U«<µCÀ¼ïÜŽ¶u8Ò®’ð]šzÝËï‡^ ?˜ûëϵ`s~Šž­5—´™ìäÀKzR½ÒÎÇ‚2Tm£Œ·m|Ë8³mŸ¼P¤ûœ -‰`zš½ãëX0þ’ºCÍÒlúúÀm€¯ƒ´¼IÃåô­‡q«1)k7ÿÙ‹2–¾ûÅþRr¿ð÷|TW\8»ÒŒË~h>ìƒlŸ7Þh–H’¤|«Ô-„ÚÈZ´©e’ ¤s“MýmÜ_t¹ÂlÄ&\þH'ºNôAÞCb³`Û´ºPêpè.èÒ¬‡³qqO›Ì·Ùèë\%B@âý•Ø Ï Ö¼ ÿ…nмá&+}æ7Ô[Ý:s!Cá× Lü°&a4¶xÈ—óÒܸ+77™3cËúÙªñüÅÙÝËDYÇŸ'»ØzªŠC;º©ÝƒpÑÆ‡Ç¹&š£á™¾æ8cj|;xÜãDékG‚ÿÚÎV¤Ýä‘ßÉ8&û–YŸPQR{:–oæƒíÝí£ʵgÇ2:»)¹'y!ß“‹½K3sÎ3¤îŸÒ7ú$ݺ¿'i¢yºT • ¾ûÝ#õ»oJ„^^H᫆¼Io«­¶².¯¾újÄǘ<°‰Û®ã½ bÔ㤑ï[0.ì. Í~\8µ%ˆ­C—¨  ,ðU´ @Ýv\jµ|µ×|h¾ P7xY& Ðë˜_Ÿ6>¯#í3癤¯ì½Žý8Åyuª‡,8r/pöU<éè•Þ×óFl쾜½ðL€Ý-ã¥<Ôa«·u•a£Û?³„žøƒN$}ªR¥°ß㨹oQÞŸA Wt9y„„føÝ#‘f†d±òù9ó½¾7,½V óõsñ{'|gÎ_zM.º«2’H}dâH3 Oß¹*å+àS——­TµÀñG~i/8Ó;4oƒTõ–$7õûÑÓ‹W4I¼X[9æûw›„•ƒÊWå  Þ¡}dé;uÑÝ\h'ØûÀ«.q äÕÆ‚.dÓ%€RË¡rÖ†‚A×DçËÅê[錩›cËVªI© ïÑui«ÂCnRññÊô±Na•ï Õ»wYÆ, ûÝ,ZçóÞp¸ž‹—ˆ¸Ør汉?õïÞuk®Î¿FêûyÚ˜.H©÷§=@ÿ#©Œí×è‚¿(Ô†ýh’J)¾Õ$ðìЕ÷yïüâ¿_·@% @ëfíHÅ.Î(µ?0rê!Öû|¡8€¤ uìø"I®\†44Ód àt7aBù–…¼\ë"ª÷Xˆh)Ö>ö+ö]Š#½’-%MÒ%iy! Y¡Øš`K’iAIàjX^'œf]‰¿šúem¥þ´Â3‰ç…–j‚Y /F“4Ü™z’®ÚæH‹-Téˆâ5>(éÜÜSÀ=ד؛äÛÂz X KüJWó[›4†›†–>]é.ÕejQÒâþðO¼ ê[Ü.Àæ ¼“#ÒTÅcx>¯°nÍbL$}¹:¢|‹ÉsXV ™“Àýn@ÏI÷óD‹óÌb€†.¼ôT üÐ¥…îÂ6}ƒÀ&ͬ¯"î‘, FyÌÁLX"$Õ,NÿµÉN,2!Oz‡t÷jÁÖ9DOœÉèòÀªK‹éóµÐ iy]D#„Ũ肧@„EY¬OBúfGScœþìâ+¢¯ö[:è—©»(´Ã­´¾d³§„Òº–ú^¢òi³§¯ó6ä•3ê(hæ~‰,ª}Õy^Øÿ^ýs®Œw0k¼§øÎ7k™³ÓÃÌm@cÙ„7ß]¯†÷«ggÛ՛׆™§ç] Xü%.ß™m§sÛúht£.I]\ã^Ás1êpŽ™j%%GùsÜÔ2ºŠ2†Ì[׆];9¾¾™!p»â,Ü7±î…ÿtÀ¹)(#þ0oêãÆ]˜6‡ï¶ #(¹Ô£”å^”Å–äã €è,@Ç€s—`½Š;ÀkýNÁ+5V왼ÆÜ×8Ó”ž³6fxd‚<"±½í2|ùbÙªÑÚPßê•aÎ/lêÛ= áØ°c¾Y¶¤óhYl#ó\‹†B £¤½ Ix|ÆbfùX̸wFŸ7†š`GýÃv«žFþ ;bþ=§•ŧEÞ'ë,í¥+!?]ž×°è<ûžÚ<¾—™šyšÆ 8€EÀ'@ƒáï{)ureæŽJœÓåZ赤gÑqyHúZÒ’²º„EÇÕäWK…[žvÖ]W(½…å¦|ׯ˜bØ‹C>¥m­YfÛ’dýE€E—Õè¢kDZÄÈÒ=/ë_²)²ªÃB>º%[:kuñáæ€fÿTor\9¾”Qê$Q¬Åüþé)T°nó–´y5e0€º)´y±,~?pl\„Ž´¨,´!yË¿¨P—5vaÑ7Áèâš°CÅÍÚtÂ-I®ù4™Í‡¥¯Õz/p9³ ÐG)Ð/’óSõ.‡‘¾ÄäNP¢÷ntUÔFœªjÓp ú­Q·˜XŽœýˆý”kØ|Ú?ñ#)Ã÷1R~ÒXÚ·6æ‹‘TR\I}Ž¥ÿH­7¢å‡Öi §‡ÛÄ6fzÕ~8”ÆüË{› Óv\l-Ó>û±ùÞ¹\ì úÉ? J›Ï£ÞØFY©1ù$y»Y¥q—:îÃ÷|û0êðNêðˆÔò;«âŧïôáÛài‡ç}ßê„ü’_w~ÓùÍá—#éë~9Û–qbíï*ÿìÓ¹¾é©ø†æH@ò)eHv ΨŠH³t<ÒÍc”v *º ž¼•o'ïÒƒºèzÊéÍsRCé{û©Ò*žÛªø[b„Q†Vbºx°ò|úðþ“yÿÁ‘ୱϱzrÇ'Îe3fŒ¹€ÓŽJ•ÙÆôi6ÖŠüý6ˆ¯>ú!'nŽeÓ·àåpÚ¤É~„wý\ホ ·m1kný öãyjÞ­¶öæ8«Gæ8â¤pc;fmO¿b¦ÞÅpÓˆ-Ðù$Úc±KW b©#×'¾»LW§ªÜ‰5?H‰G/êe52òL‰0Ϋ/¼¾Õy±fRƒ©úçïH’êžI©yuXàE©GÈÙxÔðtN«`øÊY쩜ëߤ&t‰%#’ypÍ<ÆËóƒ¨Í§Ùôšø‡`Wè«l1´Y³è«0‘Ë:•ŽÀS¥ÛuLU”¿Ô·(_a±%ïçM`ã“Hµ>±~é—´ñc¿þh%b—©nj¿MjÊù¾¼ˆ!‹ôë–ªOÕ_)ÐWŸÒiápÙ—óyª¯n¨')Oú~•7…-DÑ:Ú Å)äÖ’:/§zÇßnÿ%PJoè_õ µûŒlÏB'íBk1S9‰å„ÕåÒïújBX‚4Q.D\[Zȹ¤[¹mKàdŠ”ºø#OôÕ è+³@_IF) úʯè[n‡ßQô WˆzÓ>5ŽÒ¬¨C { #·D˜°—n3ΣØÚÖ*Ð×Kwþº¤£èÛDÇ®‹zêèÙë™Ç˜¦6Ø ¨¼žâ« …‘ñ(˜’²u ¯ôaî#‰¼4¿0è« îh­.)™éÇU¸‡X,4G°ðï ô, œ0Izבôjq¥KYÌœ‰<·»-ú’Ì À $¼‹­K"£ú„•Ž$LÐW …é%gKÒŸx.ŠÍµ‹(èÛËï'}Eo°8›òq ¯¤Ï¼¾'p¡)åZm/´PÆì…f/ø `À[§ B1—@ç÷ï(ú*ŒŽ|†A_¹ ô]Ï•·ÚW5#%ë}ñA€ùQêP@ò)¦‹úÊ}°úzº(Ã!;™ÃYššäàÀq¤îâ'²0Ѓ_[†§þRãð)ßéø."Ú0~ºõ?€EøxxÏ«V’UË_Á+ A{=°Ë‚B¸‡/?z>–ÔÓ§E,¬úùØ¡ò53°éöŠÇœ'À^Ü\O~þåø¦äí" úêB—¥2KÆÖÌ­^goøp¶¹ž ­vÖ@géS©ÈÓsæ±ÔèGõ€뾟s›4Áªß3W%âæÒʳè66Ç$/HJ˜jÀ´©±­ÍçHŠk“kuÚ+y›Yºúwà f£ìª,¶G›SQe1¬ñhs]æZâ $´híÌ-€Ñ³É·àŒ¾íH•¿°óžìCfO HöA³Y¶ïôªímz#Mé‡$p[³Œá²¸øfY³zòrÓ¸Ì5? Gõ&¤'Mê(Âô­e¾=€M ·b™f¤·ÚÜ8æZ¢2"IO7 ú®Â÷]@›ùß° ß?ó<_ %$#ߟº× K-‰'œ§:”µÒÉôUP½×‚»Óxÿ‡rFR&=yRrVRnôÝäÍfÙÚ­ãÌ'­Æ™ ¥_ã×&”ý'Æ['Ö0Ï4Gºuf©†ýü‹³f·Äš¦Gü`%äyŶêm4CëvÅBÿ“ŽcôÝ@¥;GµcÙÝÉÇ~Fªúæ ß$ÔFQ S=wWê#ŽÄuÊnˆOßP›} )ÿóª>Zòæ Ú,ѱp·1³hú8 TÈÉQlCÞ¿} EóæåòA_ù²ÁQô•ì$ur„•bVÀÂt<üy^‰" ¯1]s ¯’Žf«Ò#?=©f82ýˆ90qŠY+¾¹ùIÊ•à&ý GxÕF {ÑmêRÏ¿»À;Úðþ¬r³me˜çWy Oú´?cç.Uï®u4­8°?¼_-û)·´ˆGt+›ÄÎØámñM̺±e1CÔŸG]¬þ^ úª,“Ôs Ç¡¨ê˜ÂøVÍ ÐÔhìR7ÐŒ©¸[¯¦Ñçà&Îàép¶Ù%niòÙ‰ï¿37—”ªéçûzì‰ìE±+xæðߪoÌ ‰³p¡\ÐW˜­ÔÙÔœ}h4uŸ/—¯yÿ©–÷{ÑÛ›E%•m–¶V»ž¾Äœ‚ÞÞE^X‹à9÷é[g°O·Ã¸èo³s*m–wIþƒryÈ<ïK›ÔûÔV´}B[enö6üómbA§û ¯Ì«ÀÁ^cŒŠÎ²¥¢£Q}œy•@Û§¸äM'Ý&ÀëuÂçNü/ô7!4¯øG8oñcI¹RÉݰZhvœbÉz†ÿÀïLòê° æÚøz<½¶d¥Æµ‘Õd¦‡s6C™¦©Ô ›o-¢å[Zubê@øcQj_­)ê-$ oxÞú3õ>Ùo:y¦ãMhÞF8ù<¿‰g;W`0}œýoZà(Qå œv9 t¯lÅåv˘ÉèîŒîÙ;Ñ¡,U',Ñ·l½œ÷üæ¯Sÿmu^øk”ëçì€þ7ÑÚe²ïð}Ÿ74ÿu¾´P©k§³n!e1Üö´.f9V9lå.¸#_NÄ2ÃDPIçÓ)Ú™ D¤AÒe…è"õØÖRж­püp½EâL É©…&0áH¾Ù'zIç¬ô*mIKïx€QZ¢Ì¾µ‡¹›K=À^ÎÅ{¶¤Í:‰)SùF~"ElÒM- “eÑéÓ¥! Zç}ªÍ4=¶ ¥sÞRD‹=]æ!ÒáP&·Òùüe)ª²@ú0£j+ƒÛìÈ´W-U¿¢vö/Z. HÀFõÇ^À¢øW?¼{ì9F¼|ꕾQ-¦£t*‹¢KCR˜¯J_.) ñ ‘nwS¶.B “Z ˜-HQxIâ-vá8åô朚˜j"ýäùøº„ñ¾ZâýJþÝeñ¿u“¸r"‰#8žeù¸ýŤYÀ ¬‘.WõG‚¿B=¿€”j!’ÎçRä$£%½¤cüZ¢ÏÈäÅ&™Ø½»ãÍfèW=¶áj³d¬Þ\“á‚,âUã.JP³gT^cÓ?P˜Xë#tlãù\Ì7ž6“1³¸¨ídÀªc¸h'@ŸŸá¡Ÿ#Yû ÍcsÈw-RΪÁ$’¿»˜%+_Àö’é‘}ɾãDÆ–ãÌÊí¯4;¤'›;õÝÌ]´uI)‹t‚CÀ@·Äl–$Í ÉUìå:Ö3u+:DG›™/íFÏçV·ªñTÀd¿â"±_m¶ò@}Àóm³¿P ¹¥ªTµPë0%‚%ÐÛº%‚Hʪ/ú¦oÓP’0è×x1Pb«S Wú±§"œ‹Ymq²å ûˆÏèâ»,å =Uýùì@uÒºâ­[¾ªøµÔEªFQi¶æÙr˜3wEÊ"RØÝÍ¿ãK™Ï+‚'þb&¤ï5'ÁŸ~L®O_îLé4šA´ÏæA ñ±=ìØhº[»›©¬LÑ/¦÷²_˜ ´“pÉ<‹œ+~á>5Ëjê{Å#_Ln‰d걨œ¬ä!0 I§úJŒ¢'èÛ¡Õ(éÀl{=ãDy²nó$3Ô—øYZ½×íàõfÚ½.þíŠÎÐeò ïI?"ó”çö^ð„êÃ}eY¿uû®çùû”½¨õ Ï5¶iÄ7ߪ0GšHš»ãYch¬– KÙðè9è/öºU}ë”ÉÞÖüˆkÒOaç,°ç ?’Gy¶†³x~öñÌóο?ÓÔݺüTÄÝ9+¯ôëfHãeIJœ‚·Ê| |¯< ‡LšK_Á­†Ÿ£Œå–=²=Í»wZ©ÜÛ‰fà-õcŽ4¿u$uL¨ä5Ö„úÚšl¶n ®ŽGÏôAfxlO%S#_ÖnðjSP¤y€#Í56K Dxt—J!3÷úó>é ÖøÖ‰¹Ã,⬋¿›ó×’Þ4NAç¨c¡ßïU@IDAT þ3’¼ÖW½‰¾rï» ª¦PDÆ{2"¶#ÍØë;6=©=HœÀw©ÚUØ@PyFIßD»Ê£Þ¤u åu®ßä|’7cî—³2U\Škˆ{!¥obë ¸Im»s!pÌ3ŒÉ³²¨äG8’Îq·þÔZH‚-"Ûר¸ù»nG¡è#ÞÖúgªz(”Ÿ¿²[ÑBü2-Ä}%ûW BÇôTÕÃ_á»þJyXØuÞ ¶ cÌ ‹tïíOÁò¹e©2¥1ЭT¤ê!ª+°ßjÙ›ÿý¿RºTG!ü™”Ó×¶à¹Ð±ë&—.,x²6…¨šõ?MJDó£ B—mÇÄÑ‘¤¨Ý„F f8p!Œ½æº'ÇÚ`Oηå&IcË„TÓüù¡ðwR¡Û³¿'ýæ—óóöòâèxç3Œ˜ŒÆÚ…Eçtò¨ •(I’ñj¢öûAÇX‰Áñ¬XN7­Ê`*­â)âšÐ*£pºa³ÒÞúF,‚‡2ñè¼ö{@)Z@Òj,¨ÖCÊÄL9_Ï$‰¨Èëa¡£±Zˆ¹ÅJ4|Ô.©^I¥ë8³#ép\œ1loŽEŽ$³ðÐQJq”…Õ•GAZØ‹xZ`ÉÚZöG@í…°Y}àÓêïY@-Ë÷nÞF§%aö¡¬T(‹$]ˆŽÌ¦Ò^lmhy¢ËÊ$I¨ïÑ¢6L…¹²Ô–Ξ5+ßZ‚9¾ ©ï½¸á©Ôµ8“Žèÿg8­Æ´¦Ìç„–Ö‰“*1Kßéä‡Òr scz™â³ó/ì‘ÞÝVO!칂٠зÀÑ…ÉL¦ê¯=Äü3ý±Ù<Žê1Ú+T~Kð›eF¢#w9«_u:Úbãæ#—ÏÙ,öŸÉÔ¡BD€Ó<~ôð€«âG™C3Ï™³?ù’倂ôHÑIœ.œýØšY›lrW36óo³üÁ a¬ô—À+}Ó–”Ácð‡L²…Taö3?.:ÀG™—Ó™³5ðyÓ zSZGù¦¿ D~‹¼~O¼©Ey|êíU"¸7·á«ó£¼3x %¯§YüKÒVduž%OÀ}禿±uHЬ  ¡€”g. o˜,pX€F| OÖYÚh%y6ÞJšùõÀýAϽÀß\ÿÖÒt@ÁR¡ÕF’â‚RħÕg%ÞË>³ð¾„ц㠴žÌh~ÕÅAY•C­÷†ø HKßgÕŒK Åm.e{¨çþ‹Tµ‰-š+«ÖÓMrN7ó0m`/$m¯Uy²‘ uíå‚´ß¡¨‡¹—Í û>ÊYm¦š¸×Ïæn«r/Ÿä÷uVlÞõŸøZfÓÔ•4¿Í\”г3à÷j\ øZà&‰om,šàméÜØòfM$í?ÏÖ jhN>,’8Ç,Št¥´•潿êSÞwñ?Ò]°öúÀ¾™M·Ø Ú¸ÊrY@§ôÓ³1C©Ç)ǽ1x —z™Ç¥­l³eå?ÍÐyWñ 2ß0µÃ¢’ÄÀ™ôyÇ©ñN©SPxˆççþjÓ`íY'ªéoõ“ 0ÿD±/eC Ô^ŒßÐrù¶K;úœ3™{q]¶{"m÷H¢gÂ/ é¤&µk\\š¼½\”{Fc8»¸.¼ ,ØóUh4 ^SwÊyM*éÉ7î•/Qÿ[„Üd\‰ßˆˆ[¾U§Šn¢M ;Û±´. ÞFOv Ý­öb£F8²aôIÁÿ‰ X–ô¸6fú^ ¿ÓE«öDßiÕHýIý­Ô×]6N¡?Z o¯6eÚ›ŠÖ_˜ÛfW±èZª1?3Ÿ[~Ý|û `0Ò¬ ?:TüÙ'8/óÀ¶æ~æinÞ§3—0×r§¾ôdæ‡sîd#P›¶Ñ kýä"µù®ü~̹'o¢8/Â>9ç–gZÛü¨ÃŠË½M¥‘"^Þy!=‹ÒwcF￘“Ûp×aÙY„šô¥ÏÕ¿‹+É⸳üýœÿhZ´óŸÖ‚Ælƒšƒ¿ è» ßòwüòJ`a×ùÂ}õQÐ7QÞgÙPÍ R û‚˜díï b ,VâÝQI×¼ÉxšÈÿÞ´ —¨éKÉ€l’d¢ô‡¢¨n]é#v’Åßù ŸBñй ô•~RGô•½žHŽ9º›I£vÐÔ9 ?ç§KzDÒ—&”ÛÂti»t%ýo'•4æ; HK ¸aÚ¿ÀÔÒkV8;’ôƈHýØc¡pò ‹ …èΠ¯Â-—Ô¢P*Zç]H&•’´ð%Ò”=LNeÆ™,þ›£7¨·iäû0òÿ²¾j…@_¥#‰•YŒ}%-¬òôUôÕmé…@ߨ½Ë§0aÐ÷BÒí¤•T@HÂEGî'"a®%›ë‡úþ¦j\Šå=/§Ý¿L;é;® ×å¤îc7S7ޤ[OT£¶è+Ò2J ¯è£øaÕWXÏèŸÌ}¬#^‹ºvok,è+Ç~¡~/»–öú…IRÇávö+dŽÆ†XVŒl\.O’Ú“<…okƒö'Ÿ:=àhSø‘òUŒÚã'Þt‹bIGYBRÒ‘.Ì‘Š+íÖð:Ï*óAÕžwúZžÌh$œÑ–ß`‘®Xñ=Õ’»¥Ý;B nõ¼Ž´ÓøÖ?™É Ì…UôÉÌæèÙ»›ö/#%;Uûš½Ó.¿cÍÇœ¸<ýœÙ5qÈŽHê·5k*8jƒ¤¢U!ÒI*”(ˆ¦gÉÛ]ñ¹:-’¯V\¾wpl=N·÷°`ñ(s.êPNg“¥›õ1æ–ä˜ZÛÍ'µ¯“ð³Gôc›u¼PÙ>)¿1Ú_ý.,Ð ƒ¾ ,ÐWïÞ7y«Ý |S‹úÄ|ÿ;øjï¦âIêòm~“Í£Æ{â6u6Þµ+æï| ¡:çÞŠz©<4ú.¿¤dA_$Ϋ_!wð»-¨ñ(ÌÃÉLJ€¤IIŽí›Ê·ì˜eG»Ä¸-cŽ­xž2éOý~eÕnA9L\‰¢Œ´9>¾ž35n©Ö¶…Žá[ŸC÷´êë¸Ôéf€€g[¾ÞŠy¹Þê¨×†©i؇ŸòÀ±k+=›c¥ö!È`NGzÄ<ÿrÌráG}Çj%Ú4°|™’ ï¯ðÀGiç‡rÁØšؤÿİQõG§YJÍ»6…ïx—§QB†)9Щ½J€¼ïÜ|%¶!á:Á¿¾7;Åöcƒ«^Ü+7q9€Ç%V¹÷~qgÕ'T·¶ý®hGU~œ ²÷˜€iC)égØ´ðh$iÜè›yX)cíÚŒþñmÛkDZýžæ¡F¾Eü53„fó, -ñU¾ìL¼ûQÂܯV p;ÛLFÝà±þèxíóóౚø«;•|<îůíkÖCŠO“ÿüƒ¾Ò§;_ÓØ@ûIM¿ê‡¹µåØjQ9Зö?¡ìä%KFø~ó‘µQ">¤ŸG%O3V€º¾ï"Ø'm°úË<—œ¥Þ,-Ð?ØKŸçRÈ¢¹ÎTë¡93<'u—Y±vW¤Y}ž;Þ=W}q¥IêUàq˜úêô’ÀTÍ+¶áô„èGÌè»ï¹ØºYÝÓu«Ñvî²vm"­…»ÑO»#â0£îZÞÍ ¶$s˜á¼3¾¦ï¡]DëLoùí è»/üð"ƨàER;:†¹zô•Ú›k³•€Ážš’úH ' JÝÛ­Ö½dIÞ¬I‹ßMq¯jú¬¸ 7•àôUÆ…‚ ¯ï§Ç iþÔ·Å}Éÿ8x ÇZtE˜vbsÀo¹aç¿Í—Àß%ðw ü~%þý’.™r—’¾{þ7•À/%2ûz€«X”üéQ±Pž»v×£ÓIxFI7S‡Iz(u¡S”Þ¦|¢ú:£ad¿ÐKQ7ysË"ùéò"‘T-D)úFé'u¤‹¤ Ü¡cMŽÂ ssOw3».R0±Ýq®0’n”Tj¿ˆZ€vL˜Z…¦7ëù—XÜb—bîZç}Ó—,êï°RGMB´ÜAVE©¥›œµ!àéw 䊬‰.Üq7„õ'Gß)»ö©~#‰íèMÞÚX‘T__ŽªÜލúÈè/KKïIšû+=å;y¨Þ^° &-¬s §^LV&(RSr'uz-R(*ý vþ€4·ñû@œ^=Qo¢Í‚¦+z,5)w¹›MBáM ׯ$)®^¢:jŽÄõ ]éØÓ7 5mˆŒsßÝj¼y2Ô'Ý…*µEÒy«¨”x‘(e;¿ê÷O"e2ƒ˜Àyô•_gTIb¸­˜8±˜—u/¥rGp_'hãë‰ôY¬‹´°”\U2ïÒHµÏÉ—À-™B qôŠßNMÝ äØ=ɱk×O&ÞsV¯­T‰jÖ¯ÛÌEçY 0<ÛŒB½Â±l*è‚° ”Õó©K5Œ™£‰_UàßÂ’‡g~6‹£su`ã£fÓÚ‰?™K¿>6Oü{ç'E‘ýñ7=³ "&TÐÌ ˆw愞9œáÌO1cÀ,Ô3`΢0Ç3ÙSñôôΜ0 (*ŠbIfæÿýUwõôÌÎì.AÏ»ÿ¾ýìtå®®ðªêW¯^чpÐÜvW:ŪÎä’Âõ9*~°uÉýÕ®§/N²Ò«„’i|«Ó‹L*¯Ïì8û¹ér¾"þéÇ…ðÒ¬”Ûd‘zž·€šDk䵉P©l§à|ð¯?nÁ-þÞÓŽéøÒë€Fйù‘¶Zjug%';¡¿Tcý@øðÔJà ÿnÐP×8„²Ø¯Â.²û ©8ƚѸ-CoFÒEM1Àæ®ÇFï“ô´7ãÅš)ÊAžfh³”·…Ï`3t›¯àºØÃßu±uÕ™bss½­^þù§  3úT6ªÐ]R†NðÆh@}v¬‰þî©¶Vv”}dí{ý%%-êø}¤«~åt:fíÐìëvÀw'QLú´§$I"Ï“$07wcM²{_=5g  y £NþèmÑ3áO­Œ>êœÄúï{Ã2F‘<õÓVrÒ¥ ­‡&Íz€¬õÙ¬p”îËã‡Ð¬>¬™Qzž½B7úB1áž9Î]2µũÛN©U]0©&_Òæè‰Í/¨ÓbÒmú;£]×¾ËfD —pN¦æPÁ°®]7I9>ñ¬†¨Ó5 Y¨_'2Êÿxá–T{Î:ÜBÂ[—n‰¶>:;Ä‚ZË¿‡«ŸéN³^lxôTŸJïáÂûôqu†´ðÊl6ÜPFè÷T (´ìaV'ýÑõkažn ûênƒb1»:µ°uÞkH'yÄ+ŠˆM™äñy]òéNq¥éWQÿ÷áŸò꼃VßBÿÞŒo(ôÃ=™{ê]Ò/ÛŒP“œ+ü©É™+a@[¼¯à™þüÙn 2؞ſ§´€Ö®ÞF~¶˵öMܨ¿ô ì›ç<ÚÓÖÍõ«øÑ[ m˜ Ë\‹ûjE1íÌœôK6þ¾ÅEcŠæ+:Õõ®ÍHî"ÚÒI ¢ö"7 »Ûø²IMßA“=Ô4Ô&¥ÖÀ¥Om•7•™KÒBRÌ¥´sÑd¸Ô·pجmpaóø¥.þ2œR÷Ùµ³ìÝù¸:¦ w¤Þbn)©¦Ci¹EHúdÀÂj§Ra(Àûêõ[cåÃ%*ºúÖaA³’LîB“(IÀ¿rž~޽&ž¯?ñ¯Ët[ì]d8‘vþcÂE’ÆRY2’…¼õ‡Dq“àlóUïê$ÊÂĤ~!ÙRþHçi0€ÛA‘´V)G8ŒüHßÚ”‘z¤Ô*xr zo)óÜ4ñïL_‹?ƒIÒ†ˆërœ±DÙÛ³W¯Ø'Ç$“j£yñ6†+ ¥˜ öRÓøD[•šˆ$i鯋èå”ß^íá±ìÈá]“Ïãzb)m ¢ãôÏ,ØŸMlJ F‡fռଣeÔNâ/‚ኂ k:š ©Îíet½ú›ÇôÐÛ,Â-wŸKç''µØ¬íG{p~ÿ'¦2º^ûÇÆ[ù5Áñvyã}lº=áN2ü <ÕôˆÍHËÁ_útÕÙH¿/a›ÑέÓ›…ì'ZXgN²Ô¬ó­K‡–nºÀn mÓü“v=Ϲq¶ôtM¶#íQeú$ùÔÑv5EÔá»þé%å%ißÀ@Ç÷{sàÞÒ±(ÇE á¸%ÉÓ a™ƒØ»Æ.š±>ÁLà¸Gþ§Û”è̪‹ÉóHJ†Ë1”Ú¿ò_sá^7À°Œ“zv2©W‘tãËäqx `él]`)5â §sÑÔBõëY¾æ—®ûñªNCÊñv8dv¤³Þ ÿo§a|è]u'~çó=CC`982Švíã=A 2W2¦ê"{åGiëTH9Ûz)+'1Lû–~[¾$Ç:¹éðúJ,k«dGØŸ÷f  Àf,è6o #ãˆ{]BJž¶[ýWbH•Ú‰/Üž…©À[åSjs¾}ÛØ*‰TË?\°Ûª”O¡ŒÅy§ ?Èl€©O¢Œè)ùGñ*Å_Ó§Vð U^¦úbéCšÏðœ¶Q@pkzŠø'ÇWÒå_±áûâÝ!ÑNk´iÆ[]ðMþyûž“*–ˆÎx/åQÚÀlÀ¿*[e¯gN±‰ lÄPCê/v|Æͽerwيٻ©j¨õ”:†Ô:ü“ïRª‹ZvxÉ–6Óïà»4VïéÉ|E*bþÈøö+ê:1²ÝÎ¥Hö´é(—Žÿq›ª³ì6&s¸ ÍQÙG·ÆŽåWmœg®òIÄO è²XË}€Û6%µjOæ¤É›ñ .”öûb¢íÕž€à]‚vˆæŒ §Ðذù=‹º¦n#Ò…|z×Î 5a–>Æ{Ïѳ?ã œ$$.ѳÔÒÞ>tõO7ÞùÏÀÆ·À €]à–óx§üBz¡”cÒEÔ¸—õ;e/‹Ç¥2޲îÄÐý2ÓÙ`Ø<M¿Œ›æªOÀ o§M÷NeئI9]ê>í1Á¶k°Ÿ}m–>Ƴ+R¿*mDò™¥¸˜m t² ¸^ÙNÉýh·¹VíSÐÝ %yÅëPxí#ié‚eS©Ã«qà³ÙFOetã‡wÔÅÍEs¬ Øl«‚ë,ì=õtr“.-›ú‘[SÉW¼ÞÕܦA¹‘[‘/¹¤r¶(s i L/&©J»¸t~šÚµ8Ðж}HíAÔ‰ÿî´£ÀÙÚÚK ½Z,欱ÅàsìY‰MÍq‚¿bÄpZó+¾p6_õC+áÃã–­jƒwðhCð²A®aA8¯(žìÌ«ÿ‡Ó‘„LKäv°KvéK<"«TÌMŸÀ‰°W°h˜—¤cFË€.*’&ú¾¤$H³ÅAÅØ¦-‰½$eêi’­Ñ L–Ki‹âl©#öäÑÉRoIi”¦TN’º4^E{Õݽ¼‡Ôk”|ò“ê…| 2OIÞŠ$5YÊA”æ™ÄŸ’¾_¤¾Áò}À×p¼{€Mƒ ³¾è¢Àxš¹‚~¹Qð3A¥€ÂË5!°SŽÄ¿Ž zÚ…¥E²ö=_‘ÒóüëB:©,Ù)Z‘&¾åHÒÌI’~ÒŸ¢Å¹ÜµÀ÷:w“ád|¿»ìËì<€ÄfÄâE·¨’Ómé¡:Þî.x©ºÑ%{s¢KU*&.+]hDP—»Œm4­Þ«lQP¯"Š6{€Æ$µÊ'õdž+¦,¸#ÒœÈÍ-‚£=JAÞY øñ4?Ÿètÿ{—+#¾¨ÚVÙ@»X˜Å®À4©)=-`õ¸¨!€¶´=l¼ÛÛ'?‡!ýäuj_I‚2¹²ŒßÒÑ·RV^zðœÌ-ÔàPãcÖ+{¯=J¾gÌâä–ò™Uíz¤ÿš»ýˆÛc´¯GIw!Â\QuKð[Ì^h8†·|a—æ¦Ú¿¹àíÖÆK¬wö [£þ4[¤ê,ÛzZúêO,п´[ª®·?ÒV^à]ìq4üZ=â6Ù-øÕ×}“@ZG3W´on,d™p˜aãƒÁˆ&nEÿç€Ú0 ‰Ô ûR?Á*ìt»Ic·ßRÀ{æ(‚ÖÚ©™åmdýpÛ>÷öŒ]„´ßkÊM°¥=Ìq’‚¶²mEÿ’>íW}v ›mCè;Û¡Äë'Æ‚Y£^é–P°qìpq ð$½gl/:EÐx&ïÝ ÀI qî²8Ük™‹0‡¼Ì¶5^G¾ÂMe´Z®`èi=‚ÝÈrCjY>m8^˰Ҟe'¿s`LÈÂ÷G›Á?pˆ` ã«‡ÂGÖÈ~ƒTi£­‡ªŠ²£\àg‘P–zHâß+á’ŽìSí2ôÏÆc…. vç½â—KEaZxT6÷d"À´‚98 ó[!(¹ÂêíÀü'V×$Œ¾×Vª~, ™LzÌ5cª¯?Ëe°'øž î.mäBÃ$Iÿo’Ngìñ›Î}úâŒK«Ð.7#¾€dH`w°ChæW'JiÚ Òi@TìrœÈð´ÑŒM€Ò'1Æ€öŸÚvGÙõ´™w’.à65äI ôQ ;cUOyÐ-|KgS%Íž¹À^ý’ ìZFhÒ,䪚‡½Å8±EÔ,Ùt’*1Î+ Àuxú<Ìa„Žn×WNEF]vH/».w·v—&}[Z¥ÒæÓG»“ ¦rKМÌkëᢎ$A\{6Æ>”/|¦•Îvš(v˜µ2íúÔb7¶ÐujnáØõ Loó_¨÷Ø Ãü”QïÏPÔ†•h;Ò)¦®Vßám빉9A]ìåÆ5l[ö÷Ì~ÅV_}ìw~nŠM`³pýT 9k Ç§ßd.ÑwPÿ°rö"[ž9:p}¨?ÌD8¢†ÕŒË2Þ-‡îèm˜ci¨Ò’$û øãŠé-akÛÅï•áSN£œH|x*oCg%ù]ξIíV‡×F6NDúnÝ089Ït**ÆÇáYƒê*ìgNN‰”%ÂÂ)¨Ø±é, Mãy1é~Œâ‹Žç£®T¨¢­}ÿòоóÚCzÏ5 W ó:õöôþgK G<áøŸýIJV<µ(dž86Ì“Tþ3‰Dìö?óòyðÖ–Ž™ÏƒäÛœDwBRœ´9r+7ï´ðÄÛëy-÷9*çRŠ%çJ=ÊØ¥JA$ph6§eRûí9ý«,DZ>Ÿ{¥7•iÌ<£RžûfbqV›ÐY©Œ$œ’/O^L!wéÒ¼¤hQEФ]zÌÊ‘»úMD¿Ÿè…ápÄýXjFÉã…Í]-wÔGÎdñ#²§Tÿ½(Ié /ôvΟÚMè:],sHQ¸bK¿‚µú!gÅmç8íûÏÅBN…GÃþÎí>òp)À­W@~ø€¦ mG÷ûQç^Wö?áYƒ³‹s_ZG«å8­6²^çomÍÆ{ÃeÜ«¨·e|L9½°zÉ©õ[ÙÕpDö3ßp0ÀÕÙ>1ˆß,KÍdƒšN·Pgñ@ͽv|Ã]zŸrØyYImVm‹}Œ °½Ó‡ºcЖ Ë´£ŽûsùœERßjg[“N3BÂÙÓÑ\ºôYöAou§;= =ùî7Iï6ª›oª)næúÞ‡,à¹2±üßHc,ÿµØùÎà@ÊçØ8]“Ÿˆär/À›ýÑqi9µÚqÖkz{??†ËÑ·¼ôGcÌ‘QŸî–}Ùjs7ØklÔõg ô[7U\©Ùïà³fýmF§±Êà”EwT ô¥2ñ‰x}ã–'¨o±ûÏ5ÃëCÀ÷Û(vÚJÄczþÔš›ëÄ’Ô ñÿ³dp$^øXÐ;²%Ö@yybc¡òñø• ‰¡Ù½owUßHØ¢U§S7ð©Ùq™“¬s@ÿ öpjpZu'áà±8|¯ÔZvgÕv”Ó•±› \}Ud/̺Pí¯êª¼˜^`”QéÜœ^ØÕYZ>JîR¥š›ã°\«hOgÎ'97q˜Cµ"R[Õ¦CõÞŒëê?IZ–6*î@šŽ~²Ûë÷r&éE?ZÀ5À±Ó%Zß¹K‚øX6¤bª:ÛÞÌ?ŠÚ¤&ç¤þ褡)«¢QÐŽqÃiqäP¯¾r Âðtý­ã¸DŒ*{]\çˆÐµgDæ9},^ˆX}}óÒ§¹Ë?ÅÎÁ 0IXãÙ‚EÎÍ,™ëZìÒMî{ƒ‹›ú}E`niê`ÝÄœ%K„½"©æ!ÑøÜÃmÞÂk'«Ý7lðÚÅNa^¨Kú¾­½H¾¯“ð–ÀЧâ²GÂ×¶ˆ6Â7f3H§3¦0¦üH˜wØpIÏôþl œm¼û©œ¤ø4!ñ#á´.?prlú$“ʳ ™R‹èÑþŠ^Á‰¦ë©ÃßÙ¾ð&¶¢•—«i·&YšoôîÒû½(ªfÊþäÚƒŸ|«:"ÆÂ”ÉÀÔ~žé+ûC™ÔÀ§~ ÊÐSë´!åD?ñ¡ƒý½©Ì“ Ê$–oÉi‘a´M¿>½I7{z2d fÒÌÞÒ‚±—Tü%zG±g‰MwQxÒÌZ*ÈÆºSGöâ¿ °ä…N­D+ëÝW-õÚBºdá„ôH¹8ZìËÄO»É¿   tܺhÑ”x±¯_éõºvÞ±Qàïýñ25vŽ ôúÀF[¨+´è˜×$U,ZŒµ¤´ôŸŒ,™›\ê<Çvé % ç8‰f“íXzo˜Õ1´rmHÒˆ­Ý¬6q= ¾eJ¾[ “é,|uI”(Ôe›âr¶¬)ÎG<÷á½?¡ÇÓéûk–Ó¶;H‚Y’p-mÊi©'(€å9·‘S»H¸”ÒRÏ#AùºÀPŽ[?Xû¼m?kC·þ‚…RWAZ6Ö—F,c—~f©êÐåyÔgF£ ’e‹9•£é]('Mº ˆ2é9e®b‚tX‘Ss‹¾´'ú—'™¯SÌÕ¸ P¤Å»ãZüä_rnþGú™Ku  (€>$zk髥ǟ ¾sjZEúŽ,vÎq:#¥¦cz¥¤:¼Ïby¥J¾mv׆O9u5J  àå•Õ#lŸ\4OV‹¦2Û=M§ašÖ,@åÜŸh{¥¾sÒæ¡TÚ*: »‹Ú"žßNœ†äâTxí,¿PKúS`¯»¤¬a§(ý¥|Ò¶í^ý7{%;Êæ@Ü@L¤·_ëô4}côsAǹSéëh;ؤ`¦-†>àúÃ;¶¥¤ìcºWÿÙ:4ÜgŸ²Ö‰—uq”ô,ÿÈc~Jb ©ë ”C3E›ÑÅlŽTËòÿ9ÿÓ,“†´ó½€5cìXÀ”»ò ñö ø•œŽ/:ð(¬°ïò¯‘Mi îÜÚF蛽’Àâå ¨Ç&ÃA§â=Œ‰·toÛL¯cÕ½ÿËSæ#Ý¥o¯Òòî³Q³³ Ä<õ6‰g ýweLþŽH-Ëèû¹SÏÃì‘€1THD5ưŸ½ó—¡]@1ê<ñv«¯E&¿*¿Ç]ùyÿÒçÁö¥¤§}]©½TNÎXÈÕɈb:NÀÏ.8U‚Ê›ŽqºÔå-µ=ç2z)XÊÖéø‘6}Q» žQDü¦/‰“êP£xa’àÎ5÷ó­Ð?)EéS°ÿ%4óÛÿÃ2§Ù©M7bšèÜ¥÷W:¬‹ÆiÔ9Ì7ëh¤t_µ;sÓÑc?  :k§vwׇÜÛÚÅ×jórÏûQÿcœ©ÅmS~ÿFÛÑØy\æXÛ3GÛË]á6Í4vN¡MÐ*ЩZ¹}…תÎGÜ^1¼bÝH]€ò!é{I¼Kªt’¤ÑƜ꿚ÿ;©{ÇGšnÇQ°'anwñlS«Vó*½þéóžRëòñ»ã}¸w©üTÛÊ®q y"ê8,;4^u«þ^B:NYµûc~³$·Âa:}B[Ü„8/:GÍuqí)Ô¡Û4`¾â$Ê£(º'A*³ÂƒÄœ53‚ï.#ñ.€t>Òu:¸­º×ØþÙPã°)ßtq<ÓÙfÙQÌ>#…4›³Ü;ñû#s‘¸4ófNz¨4먧 l in÷4ü|æÚtIo«Þµükò·Ó§¯èɸ¦™dóoÐ6²·kÿe÷5oƒGY—T`—1¯{˜6WE:çÒŸ”¦Nék¥ÚG5«·$ѵ¯~ /7ÿYŽá|i\>u<Þ‚Ñå• Œiúb#Nñ– \2ƒìÛ¦‹±MÓo‡Ò'’ß ÉO”*sÎçã7.Êo•íj Ùz©ñ&íl}ˆúVЇ4ygéøÆ´—¶¬‹ MßjÕÖ'ú.—FOš‚­¸nã×· ´¿q1µÚK ½æ°tû«&MsJÿëÀ‹ÀéÜœéÕ9}—§‰åœ×¤Oeîž’Ò¤¥~¡È\d+4닪ô–r€‘–~ßUŠÐ‚»tgy鉂5óÒòO“›ä;ÿÈ$õárºf±y-uwDêGêf‹2×0QŒ¯-Dìˆß þÝâ]á8òmÈäHIçe)Iꔓ€ŠRÒeCµHj…4á<‘Eþ)€£ÍS)Yâ!±xÖ¤¶> &ɦ狪K$¼}*nó&ß‹Yl°ð~EÏÜêóø"¯¶ZÃѸ\iÙèölIðyÒb¿-5-^š—¥ª;¶9&ÉÓÒ,ˆ³gûd‹žÖá½û!õ^˜Wäí,:Î/ I’þßÁ‚›ó óŠ´qù- 8I±tzN¿oõ5Rýñ à¢ïoK=½BZÕÅ L³¨‘C5€WC¢ýHÂñÊcÀ*ŽW;†J\+½J¢õë;¯þh²šZÄjIceû›D½Ù,ùÀw‰üòÑ÷†©œŸZÎ. Z¿ìi”t5 5èíŽØÀß&bÿlÒ÷(=Ù³ÒgX7ÓX¸ØŸB“‚å¬; ëSÙÛÑñË%plrE9õ` ?8¯eüwÖ™ãþKtú«}4­†›çËok!x}t:-ìªËì5I~V o¹‡ü>í.gÔ¦Í~¶W¦/oýà›?Ã3Äám—V`}¸Tí]K÷³¡3ºÙÙù€÷×h¼—€€úÛ'ÁT[37žCÛ‹9N"¨eÅH’ØÕWàPpWZe¼Óéé­li<œê[¹áò†ý”òþt÷ÌåíCT«·%i€¥AѽÇôã§Ó!Í“Hë1Üïܺ5Œ À ýÁ1x™Lf6ÌÚLù‚…ö&LŠ¾èø–Ÿ±°Oß>+b¹Üß¹ÐëÛ6¿‘M^äàT·\ ¿üu¯qkåï ó”ÁÖä•üÜXnDë9 ö޾‰²Óx*ßÕ„‡ |K\¼t4íëÀ°;ø¼ï²4êtñÑ/Ëä’9ˆ ƒc?é'•Úž€z_HZZ@ôõÖ_…™r—*Žì0ëO;˜JÐ…²å(M?í—5¡ö¢\¨2n™áäç&<\* á”>“r{вzÑ9†Tb°u°n¥³ç»ÓAoSÿø¥Ò ·0¾B—ëBNºÒ9•ûéð1û Ë–óAv>åÊ¡ïz xÏ2û ù[,OÛ%¼Ë“Ô>½€½­B óÓ§Äc>mŠ36>í®aÌ+ñyðý×a^ÜÖÈœÌÑý6´ñXìžÅðMhéÈø& gÒœ»¹wµKÃmÝvšLÛb‘¦½ºËûÂW•ýMO˜ñú®¬wsGÊÒoJ`ªá[øæ}n–Ò›ÚÝDþ¡àh{0õ‰–}˜V.w}§oïu˜'ðßœÔïõ5nŒMÁ»:>À÷,È ˜™¡ Û¤Xï»~® ótiš§7¿WEâÿU¾íÒÆËìÑÌ™v ø›ðÿÞäucÆ‹%rQù Îètï˜lR% à4{₳|ºÁ2MO°Mnì~Äxô'øÊÏ|O_Òúšñç¹ óŸŸäSyõ§Pä>†¼nBz“\ %ùTíç‡Ë„mI½"w‹ ¡Ÿ0¿‹Ûl†¹|Æ>‘¡í›Só”Gc ÜšÀwv'y_—­¿I`«ÀôSk^füß–ZÔ¦úN6W"S2>^©67…M€^ýIa 0ô¡Žu"¨E’ä2'I<ÍáêïÀšÑ|Æp–vj/9,†Œvú(1š_ù;Ôvæôý•³û_õ:éƒû5A_S㲤E̼$M>*ÑÿÐWGòæ1Ån‘¼.BIL¸4’tZzIôÖ1áÔ2AÔÖ©yºðÛз Á%}™$-ÝKßù=ýbΨ|yû -”¦€¿¶P_¦\Ú˜Ñd¹­ ï9¤­ ³£ôMêï–„ïú: å×¼ž7–!%@_ݮݫêZç.‰`-4/2&Åâå@_ó|E 5[Eþb>¯JÀ9òó#ÿº|°1rP8– k%R;‹ÅÏ^´›Ÿ ç³.®‚®¹$iq$0^V¾^ãïRɽ%1>z–HGp’¤Ëù½è}R• º(´ôBÈR@܃¾*›¥×f]`©@˜­¼‰ô«Åš'úÊ.Xk'^IÊÞ“´9³¤·E‹îDÊ« ÿÓ9ý”‚¾òŒUOjñ’®LÝ*{E>wS“E /î æ°CÊÉøj¡U¹ßðçn¹CÙµH)-æ»—’¼+@ð£ôE7X‹Ô±õtÁÞG¿¡I_g)ÍôM±hv²ë7v`‡‚dl—¨½E^kì>}EZ Ÿ¬èÌúQ}ý¨“²ÃìÃÔê¨2Œßìõ`]wÁ“@ßÃSÝlÛÌMn“`EŠDÚ9HÏ P›YÌÖ³TÓ—Ö|ŸŽß÷‚Å8Ž» ®}‡ß9)°Î€VÎì£WÚ H‚9J܇…f~¿¬}ÍŽBšq(—½Öp2.ôºìhÛ'ÿOxvKê÷žN_ë0Ú¦¢Ö26U9à(Eï <­?†…䀾páÌ…¤ó=Ëÿ.Nߤ{YzK».}’-˜ZÕz¥ ÕϤ÷Å«ÐÏv Ÿ”Ž->õ;ž V3’~K{G‡eG °Žîöy8{Ãiäv’ÝF{½‰ÿ(o©Eøáqp÷æð'Ì­÷ߨ³·a9ÒÓš­úê¢8é+ŽA_C|ª:Œ«¶—N‚^‘³ÚT¨};á/…$A/PþêyìÌxã’vLÕld\a](céŸAIvȪf¥|ªÛ´^ eG¾%I+SvKPÖ»âœ\²wŠ‚õâéÍ‘“¿QÒÅ Gƒ¾ôNzm@<´£¢Ž^EîûYëâš’ê³éuØ+'ï¾JÉëZß„ÿÁ¦àÁ€}IÐWágð¿oö[j(œHEƒ#tħ}át\¼¶=%pq%«ñþxá쀾J3 új¾}7ÒÅ’NwÄe”ªÓÁ§Qä@ß`_ü¶q^ÊoO6ñË‚¾âm ÐWs¥ÖUF…íÙ%.P=î‡Î%üÉO(XP}Ôœw‰V¦û¯CˆÍÚœ@øïÎ\ù¹ÊÚì3““ŠBßIýë$žZ‹®`kt­¤D<ù˜@_ImkŽZªE_'¥ÜKÕZÔþšQ»Äo³"iwh/öø”€¦ç×ÞÂd¬„Zâ[%AÛ­ÿ…%°yþ®•|ÄVËGäZI o-Y(RV¢þ¢ÞÒ ö{À‡½[˜2°ƱLå[?ò_ˆ3g&é=ÍuvT+´åM›ޤ%„¶¦[˦†­’¦þß&C!=qÓäမÅd2Ð/gШ‰[kô5ÝÒ ­…Oúkú®I¹Hà§tåÎ i(ù‘¤&1Íl­ì¥cîæh!ÆlùW°¤6-‘S=°(Ñmõ³A’ž­%ߣyÃað“R^º›ïd1p- ^éN’¤6¶Äö0êOÎS¶Ð´%Szåý‘LÑ%cƒ=ÊéòU¬Á€Ò=XŽž–µgƒ}ìü¦SwΩ­˜ÄÔ¾ez €ò÷%SË1Š}oœ¯ÞŽÿ¹'¼xr±G9ÀŸEêä­Ë˜>j¶ì¸0nÀâzw¼;”†ÑBöEk•t»¸»‘>2V³€îÝ{rK|ÔƒS¿'T5ô¼õ­EmWŽ&‘û·½$¯­Ú´$q<¦zº½ö¶õ¬ ì@xðX|F# Ü'³Ÿ Bçð‰ˆ:N´Ýf.ç.l{iУйõ¾«äWµ•<¾^i°-áå¯ôHBö5êtw$ºåBÛšC7qO¤u?µÃsoÙT/;­öQ;{ÖêN_zÊŸ¡Å%&@K€U ÂXòúk††Þ—»ÛX'ѹ2Nãù‡v\K¹ÌZ7-ßä?”àr|ƒÓ NRV@·ÊØmZز˜·>üe1º*~å˶ úfqŠShÄP|Á3€|W†2O‡{’þeÁ6ˆ²Ü¥êRûKã‘.|üüº7ÚÒ™Ëí$“­~ÃØK†ê'»RþwWäìÁ>ã ʧú}˜?«‚¿Ò,d7è/T#QíM‚ò|›ñn¯[’à¯PŸy_}¦Š9oã9”åü-@  ¸Ómê³Ã$›-½/:†p|/m*ÔÉëß>W¡^t¶Ý)»–ÕþÔñ®µÙ€ø›=ð žèÔU8ÉýBš‹aœT°º ÎqüSSk“‡§ð¡^ƒ½8 òˆ=GЦ^9â˨×rã!4¡¾¥±ûkþUV窎u£•|Ô’?âv¨Ü,2š°™q<¡.ÿ‚€&{Ë#á ʼn0åg±$T ÁÆÊ•ùÑ[8=AßÞI½býC¼Oí®á\lo§#éiéVÇÿþË“¤ö{"qxF4Ôñé‚õïבÿfò™k¨ÿa$rm¨…ÛÅïІìÁ©öUîm¾õîbÏÛnð‹1´^]@7{¥6#ð—2ò’çR'ó ›/])ÇÒùÍà`?Ç›ÿ’oCÝVÝ+ß1Yg/gÅ¡ÕoÅóJ‰ús—¼•ºÏžýÂÌi¶Aþ;+ûHáDŽøsÕ®´Ëay·ºvN/q¤ñlÀê£Ô»Ô«}$ K¿}pKKçïgÓa zVµ#kÙ¹7à?Ÿ9êIŸÊ¥„ÚÈÔ³¶5ó”iQÚO°°'€²8˜tÁ®À\àÇš'­kýnÌR~Ƶ> YxèÄœN©¬Æs6;>ŒîŸªv½X:ˆK7y 1—Á¨Qâ+ëÁ¶ùe© Œº78c§o­Çôn\rØÅÍå¢Ý´m]’[žˆ£Í·†óÃù›g\GP}Õl@ÜúZŽ>‘w¼IÛ}2 ·ϱ-Ç™ß`Þw/)´6“+~‰ú¹ÆR©j‰4*·S{ ´—ÀY ,CüÿL3ÿ?üoðÛ_,‘”››,zÉE/Q˜L«5ÐWa=è{‹„_‚4­H‚¾§ÌT¢ø¶íJæ¡ûe,Ê*¾ZÖSØ´¼à+ŽÑV›$ýMÛ;0y§J‚¾‹·5¡’pZ¨'¿Ãƒ¾ Öè«0•¦òK’Ÿ<ÅnHOœ(Ð7ÅBðW¢d‹:ÜKUðnIýåYô•£µ‘|Û¬E)Íæ±¤ÿO¨Òyì©%зa+ÑÖ,¼$«À O’Äß‹6ÑœŠÓz9Zš1µ‰XJ:¹ÁËôóEð{pó *ߡհy>SÎO’1àÀóöI+…¿iI΃¾€DÉèkVç`ÞŸØQhRûm ž@_ÿºd¬ôUK×b)„Š+¾òß"÷q úªOä&ð¶,æôÕ{Ïþ¹¶m ¯“Ç$5}å;ݹ“~6‰[Õ郩jBà"¿’¹ôU¸X·nà HµïCýlÄÿp¯Ùû©CÀA FÞswöR{¶ã Š‘oŸÝ°Óç:N ¥Õ°©M'<©Nø²~[O ïÚÝ¥Hš¦šNA`5úu³Õf,Í’|i{·ÃýH“ÍDmg[¯ú\[%8šüÜc› Õ÷mf]›Ù—…u{t¶èWV° üþ”úÄ2ùgí²Ü;H[Õ"1<|t$IÃmIh•Xöq—§-R›Ø ÎõH…wE:lY·4V>“§¬æ1–²QNJMH?[+µziÜ,@ýޥeÁ:î¹ ßƒ¾óã·JèŸZŸüb–%ÁðƒÜea»ÂèN×ô°ftEî&¤àú]Èõ³E›…‰¤ƒZîDC áâ• ã+x¼]â¾ïŠøAÕmN•‚.Í+% {FíX~Ç"Q¿cöo€rM&PWjz1·x¦ª¯í|ƒ­ÃürD¢._#\ÒØ*êÅüNí*¤.V,c/0ÇÉP†¢½„wíÆX#¾,}¼ŽÐC¾`þ¯´î| úÊýsÒì@8©Ù¹¾Ý·|ÓQ\BxÝu{U?Sæý\®›Juq¯&ßO?éß¾•ÿ»èÏãX‚ýø:ǯøzM¬¾›Ÿp.úù_8#*>$1}s=R5TôU<âÌúù활՞Î3äÝVj ôU:¹—( $¯[>õª»pïWÞ¯ ®ªÄÿ¼¹ £ êç¯f.©A­tãMŠg¡-%‚·$9WNÇïŽ_6$b·ÛK ½ÚK`Þ•ÀýK„ÁdŠÿŸtüjÿmòã1?€tÔd(ñhÁªaQÓ°J48Uënr­äÿŸt߇‰Í-NŽâ?™‹ßλÿŤo-vê[£LÝ>\úO¨Â('‰«Éc}k™þ¯÷¿jp*ŽsÙÙû rR¦ÍR¨y–‚ÜØ9kÒ}2 ÌLÂ5]Æâ%¦û·2©×Â^R¥Z6|’òé ¤_…Eº—þóõ#µ:Κ¤Ò‹²J/ +ÕmÆíÉäœb†pãŸ-¾P‹§"}•€/p§MÛz„õõ’üù¼ ø-,{½kñóz@]'ø 3Ó‹½ 6…ùçbû‚˜TF_Å.9=¡a½»#¾ù‡’!œYãé6mçÉûïL)½lM’¨7päv8f)Üœ&øgq¼TmOmCÀ».Óâ}îieù[j\ÔrRHYð }6ÏÄh9BÁשsøí…TB“Ž:¿ž>ÓŽDÀypÐh:ž¥wíTÙ ” `Z)U?EHìÎVÍ"}`±ŽÇ[ÃMÔõUNkŽæ]ŒG›Îg°Û¯±åX(Ÿ—_Æ.IM·õì`àõ@IDATÎ>K÷à‚¶-X¬g¬VàÝs¶dæ2û¼©90§-¸ú"U5™£Úƒí–¦Hû~˜–² ”oꣷ±èvªEªîäƒvǧúAò¹ æÒ¦FñÅ[¤–°:½kŸ¢ÿWT¥e:Žë¤âÕWf#í}¤ïg!§Å¢:½ õwn˜PútÌ#0‡ðòëŒA7‡ØåM×àö]¦Üo-ÀWÃeø·Ü“€¬ç°naGÐó^B‚3¹1XÝ/QÅÕàE©5(·WŠƒ8‰bfGÒ `äHù¬¥\ëoá=G•„¬äÛ’ª^©]P.Õ—“×30ø¤Oã{Õ17£%p©±ÛS?„ê ªxWãÑèUžf7ÖþPâ÷aŒêûIoÇf±ÿB9ŸHð8Ë[rAÅõ§ êÁÙ,fÒa%Êd>¾A`ÊþýÈŠ›“ÜãQŽb~…§ï§ ‡„´ÀrG©ÕH÷Œ, ê†ùyÀƒ>5|c * QF‹M&xò›Ð?öIê!Ø|\á” ˆ/¿,o« hm:[[ˆ •H› wR_ûñýç }–]ª HÇË+·Gd®%ç‘Ç­Éã•.ŽûImÆ£ÖVE¢Y­öÍÄØ4€Þ<:ð|„íé ÚÜ©HÁ@ÒEùÜL>‡Ó×?l6û¸ÒKÞ‘|ŸmmAڣƚ÷¢âù³‰ü9’šRU¡cáº8õm¸~EŸ¶âêËÔ}ÍÕTÏZ®|¬f/„6—Ã=Ù&WAuÃ8Ñ¿òú¬Txö£Üö¡ÜØ šMº–“ƒà3½¯Œæ°ŒÀÁÎäsD˜’6îrã0Ã1Q¹“ÔãÛ¶WPfN&oCÁ»’Ü úþ¡›6ÒÊ?œæж5¿ó=GcâHN+íÌÆÏEäi2mõ(æB3|ѳ OZGë䯅3 áüæþ†½ü˜ yÐÎl^Tm¢:“ã¥b¤½UÝMû?s9¬ÐËRóS_aÖÿ/é´ØðòÖ7ãúÐÿDûÖücS;RŒ9 Òs³ñÔ„è?O˵9 «0É–®­9}õ’RÕ¥/>Š€}å§ ûÐ`~$ sö ¼akŽñt‰'’*UðÇC“é=†.BúJzmu9™[d èMR‹&í-õüšt“äuÛjÁàõ]ºcÆÒwè+’n÷>J×/Üœ«~4iŸÚ&ÐW¡újñ*ÒeIšŒEïX/ú–¤Ÿ7 ôéFëéÞ±Ìsk@0©gðt$õÚly‘{=òŽ Ê€¾  ò÷`î Þ/ÐWy—´tLè9ÝÝ«>\ìÞFéœ@¸ƒþ¢¶Ç’ÖŽÇþ íqÞ€¾ÊÄ%rSÇBüè„=2r™£ô•4_ë¤EÜΡnEfa\ЭØ<öA÷æŽ)D MÌÛ¨¯°€¿,ß“þ eìxÇCà“ßšóÿn ›±è<ÛºWÅÑï÷Â÷ÌZ‹׺Kýµ—ú¾©ŠùtÆ–çè©$¸7bS÷'å¾r—#¬È…+r¡Õù7ù¶=íóê=\ºý¨ó ºAq6 =ì•>ÙÔ<`¹<’ÙgìéÏ“{€%Ú´¸!Є­€y5ÀgGMW†OIô]@ã|ç6^ Sæl{½!è+N¾ý~…¥ÿgR¾"¨‹Â’ñË¡õw±b—Ø+6tÃôLæô÷›ù¿0o¢ÝîCY:#í 7f Hú(ŽgºLO—ý¤ÖçCÜ—¤Ì¶-øÇ¦w1}M>þˆ"@‘cÃîò°Ð±ð›¹Žôv /×ǃ¾òV9RŸ€´/ÒvPöL~Þ–)$×¶ÌK“Îò!蛢M¸òÿ>KùÏZßîèð—€Ío{4qá¯-iÿ5ê'XäÞ0½Æ¡®î×XWóDèÖÚ/mEí*”|Mެ?·3ªw…Êý#v¾‚™6é@.~ÕËü¬B×ÏõAú°9è[G¨ ”¹62—RfkBt³kž 'ôÏ7ZO«X”{ÑéÍ÷|ù)6ȆqÔúÏ)ʬmRÀµHS°ohNþjS4Iz?›,!ÑSëG2Z‘xíº0m«*ò¸(«c´é ü?%¾¿ñ]¶ÿä›6$éõôUŸ¦ïJº®=“}¥6(&ížr£B­=‘˜€W‰®¤… ïò¼ï\{‚¾®±F`dào'ÌRCq6À°@ßSÛÚé½á‰j'qàù™u£Oœ@´4ÿC«ú‡¯“Dð,õƒdû’_$ežtóö/NÕãHm$飼‰)ia9§TR-–NJ¦¥#joҙוg'ô –£äÅpº iWþ‚0ýå¹; aüÕ1}-(Zú…e¹whÁp, Žr$ÀÖÕ‘~fsJG幉@ÂT:ñпÞq, £JéK=M[æ8²QI­ÑÕ÷¹ËÌ\~€TÜ‘ý[ü¡§³’êÏñÁ0B`@äîdÓ”û©ÿRzYC ÊÞåŒCè—ÚIÒi)ÚfGœæO:c¦lÒ,è¡õƒ]=èOŠŽ°‹§E—è™6´Q ¨(…—&-m Õomï“N.ÿ¥s?)m­ïÊ^Åf|³×B]™ŸßΗ:ú‘›-Œù7xïsQXÚ¸SŸ‘ˆZɘ¸Qþygp|RÇñ=åFb çÀE×8éeê¢àÈðé~Y¸6:iŒQ»‹©i8Æñ´ÇÃÉï§vdzû,¡oYŽ>“*\ʇ,cHýžv²*yR]¼\@ÒÓôcm€O-ö¡]â\¾JÎý‚p[& ))\@ùèÝèÝC×2¿”[zWÞ7 ¿B›ÿ‰^ª9®$þzðM¢«ScìJxkNå(û7l?8ü)mâÞiâ%åÔŸÂ>ïÂçÇ<([Gu´ ʸb ûA¸øQ{öÆ\ÀhÔys‚ç–«smÇV¼Ð³;É”ô÷e•Ö{!Iþm$÷ ìž$Êûo¤nªŸÝ¢_mÔmÁFàØÌpÛ„¹”5j“‘°]Jý¥êêomrÑÜC6#d/¤rª–wT ;>»9›w_J*/ àÇÓ³ÁÁEï‘…\%hQw9Ü?•BÄ{¼§Æû¦öGÌÛ:ŽåSwSæõî»õöK“ húd\zò_ I_y°UíàvÞTœƧI '¢=çzä_)Ü î=Oq‡Ú¥èJÆ ûWT’7Ð^F»å¤ó.úù–~¼í¤9é¶ÜÓå¼ìJñÈJÀ­>Ö)ŽWuöd+o«/$[g;¡æqÇGêhZ µ“))I÷Ùjê×P]ØúÛö]þwŒºßdˆ–Ð\ Îé´uº\C.ÔÏ"ž¡’a†S#Ô+ØxI,±»–Q£´Ÿ€À` ‹ûkìï´¹ qKùúÜžOw³|Õ‘¶z§¿ÛÚ©Œç @Ä:^¼iªÚ¾oÜͶÈ"oÅ1ìkkŸµ)¤+5,j§Ý³·LŸh?‡Zvå¬Xú>Å…nó…ö2¼åt7†zCÕîæs2Ú¬ÓÙ›ú`&w5|5¡¤¯Zº­z/@ÀõND3hƒº´mÆ‚€E +p£+å¾,ÏjÝ/No‡} ì€C¹xþ`fYh;˜ÂPkPcÕF;èWâà ƒ/8kIY}³3Oâ×mhJŸ¤À"OÐ †ÿàú¹m˜"ŸAÌÑFRæ*̽} ÷䋊hѨÝ<—¯w’9OIR~Ÿ7‚Â;‘¦.–2¤§f‡¹ Öt"OÊL`^öÂЭúaìOa¦Œ2×ñæ•B¿`}’^Á™û«eŽBÚ{ ÿïcI{ñGÕJ¯g¤Gviêb ÿÇÚæ´¦UÇÌ n’äô /štqf®Ñ…Kª§>×r`Þ-´GÁž<ŠKg+â4߀¥öá Í(s>N=œä¡ß€Þ2{oEI؈P™F$ SíÎÓˆT|GœS4ݶaãD`¤§:Êl9¾çë¦ã³&8çç²[·` ›¯ã—Ø @¦å¨ƒüknŽ\üe>5=ÕÂ Ü ÑóR^[ÌBįTÒ4DÚÿtŽ:B_zb©oæü–LDÂ(à©Ì ä¨MmLê¸UíŒkO§/5öÖÆFÍ-X— MìÈ“:Q››Þƒçka°à¨8x±¡–¶úH¯æ€ØëiæRÉrüÛ«¥éN_ÚPs@Ë$ Ê~Ï›Ãq¥èĉ¾#½GTm¾´ä©Ûš[¹mª-;}µd’‘Yí4ê+Þ·æEL5¡Mß× hç£U|VݯZ£Œ7}¡Óü6‹üæOhÞ™¥W$fR³Ö1ÆÛéqRÀq°…my.N(@ò&ûn:s­ìëѼc —ni‡ižÌ&a5cq¯ü÷ˆ L³1©Glåì}ßmödÇOi!"/c#nꎗJfpv$[|ÕNêy/÷žf?™cù¦ßáümƒ4á°¦zÆk”­S»Û™HüΰÕè_¢z Oëäãä.ÁÿòÄüIivv¾«¸ßÍÙP[%:%#F '­û$jˆŒ³9ÛþæÓùÎ&˜ Ú/µ—w<ÊGR»êOùÜJ{€ŸÔð­?Ñe«æ(ºŸ»0Òó±²AÚè¸Vþïv@x£çiÍ#þ>é瀥¾´ Û€ÍÎo’yÆ#7¾$á5A¡/'}ŠÌÁæ„Û¡È©ØRˉkd>ô#|9(öl·µ—@{ ü/•@ñ°^4}šëÏ,úç:ÁÿÑt¤ø3&‘³Úø}º¤F·ô2íøÓv €í?‘™V¦êmΒ׳Ֆ£ü¢©$ðÛ ˜îfí„»—Ô“Ó@—ð.2ÄEN‚¨ÈµeKòˆy2d9¨:¦_Z_?ÐŽJ%~‡$$’i¶d^ }d£#0)^Tá%Ô$I%¥Çî“þMnâ‘t™{s%=¯¥)M¨À(õkÉ. ®¢EP ¥›T ìweÂh’—”ˆNÑ7\󴲟¤ãp Ôû”³_(kAêÛø»$Ú0ê땆¤§–`ñ°†¤OJh&v½åФ´‚$K)}|ì¢v_J Z¢F<§¶ %?$jÖ¨¾61þ”êX[Mžœ o‰ž×ÒŸV¨/Iý†§“‡ÞM·žd ¨gŠã²:"û-f½íØ$š=—/ø/&éäÈ"RYÄÀip4€ÄÍÎ]?’ÙÚi©­­-àú>ƇÈÇÅb.Kæ-~KhX˜~,Þ!¾Z:~×»/_£ Vù—Š6î‚=†ºŠýŠlÓlxý–®|$ùü)ßõ\•Ú#ù`qÕ½]Â{&Q;ïHlD¯âÉ­ßRIr#%¤º;_e#½ž,c)ô6€ë$T¸Y5Òî§L¨¹!u°O&|f. ý+S½Ö]îŸî”ˆºg%Ê{dj{¾j -ífç6Ýc§4Üb§eÎviÌLomîöLUqiÎR¶=ývßìwv[ý`»¿jú¬Õ–mºÒÖâõŸfìö¦Þ¶"o!¯ëfS=ˆ‘êÍù0ÛP—Me‡# Y0Á"P”úCøÔ¯@J2Ó{ᾋÄU(ßã˜*à5;L¡0oŠû{¶ê|«­?Ø>PÝvÆ`áh¿!Oø;}â°ýB犿?:Ÿq€¸ƒöÅ ØSuOÀ¼üÓüåZ;|Š­–|åÃþ$^Ú®êÏvÉØ‘ÙÓÑ>!´ð+@ñúõʼnÍÝ / R¹—šGÙ{ìèT +rúÑËAK㹈 ÿtø˜r=n1Êh ‹ù¿ï¯Â R}‚t¢8Ójô¬CPe¡9Ǩ`„(µeø¨S# ~ÕW:gwR@eŸ}ÈÖ„ë§ž\ "*|°U¾ðø˜ïÒ%‚1©ÒÊgð{MÜ&ŧ'Ô^`nö"ðLÈ#ƒ!qôEicÍ¨é œ&6s–ÃÞÙ;ËÍ6ÙK/Cæþ§ÔºŽ—JáØvÔJ’tÚß"ØHDZµ¹© œgr÷Û‹3Õ§'‚ÿD¾wr ¥»X/áÓÌè6®:¶^‰r´õ„!Ø‚¼ïp'÷vþ6t—/P-ò’Æ©åRKÒÜÕÖë(ÿ‘OôàøuY°3÷9x§Û|ðuò]"n8ö_²9Ô½p‚#ógê¹mé\Ы֕ `GÍ6.™ ¬ˆ„©Æ ¿azc$=¶ÑÈFm´”¸àvms!z›Óùm¼h¾÷ˆÞ)iÃúõm]týÆvÇá·ÅÜ“þ~c/ôr¿õ»ó ½;šJÿQÿŸS"_MWÓw^)“텋Ü:ç;>¹2*$`ÓÚ5•V©‹<ó‰t5»©f£¾«IHGs±™òg ‹ Ey ÂÅÓäàsû@O/wÍfëð‚ï4ÜE]j»Î\3 ª1‰`JÂÑ«Ùë­©ç‡ò¡~+¬óÈËžf^Ò¯éƈ7 ›8ÁÆHЮRX£pRc{úÕVÌ ÿÎÜ÷9æ þÒå0:Þ7Äm‹‹tæËT?[¼úvÇǬúL¬;ï^(ñþýi×ëñý?²Ù*•÷S‡G0GíœBÿ^É4ÿËÁE‚=ŒÁ´‡S©óKø§mˆ‚ýp;*]Q^±—ÇýžËò?”>Àu‰ðºÌ „}îŒ9¢7Vi|]xŽÒ¿uÒxð}«ÁjÝÉ/ÍJÞÅM±ÉQ“Òú2×Ü”ú+nM­&ßà·TÒÓNÿÛ%°d4UšÓ¯ÔB?Im“q*™5üµÓÜ—@%cîSþïO¡÷ Q/%$‹!¢¶•Ç@&hsB»—‘+Nçgt;¾Uzà¬86M%tìXô¸&ˆ ÒD³TÂÌ{/Pf<(7¹¸8±»¯¸ÒÜÄD!IûP>þèú*‘~I¯§N%p’”lß™¹PQrùÐÃÓfðÆ«‚k+-Çû/M_òí”ÓwI…ôÎn†?ÐJQ9—¦©òx‚ú”Nß/#€Cê¾* ÙYò8R𥤣¹±ŠŽJK ƒ'I@zÒehŸz0Å;¶ðÔ"Et@W9Ê”:6HƦüܦy,ÝÄI¶ÎÇ8ÆWÚ^÷úóXÊT7Ó¿Ø”oЩ µ|_Îʆ——„ŒC»q$QÏ$uÇr@“â‘ËëÜEzr×ú1×UŽ…²4Ž|¶H©h!H éRöKd)MŽfPÇRµQJ’09>sH©s3ûò|_²ÜÀ«ßhxvÒábÑ´èr–…vÓ¶dšì¨¦—Xì lð*VSÐ/€XJ™ÃFÙPxÅ×U«ÛºÙ1– .AZ6üÚoœ$ݱ®æ†#…JØŒåùŠ­Òx”mÊç‰@èk 1›”À7nd·¿xÍÛ¶X8Ê[Ê>c•E¨ ±Ô áÙ8Ö¾ÈÊÏS“½ /øž¶VD)ú)#»eà÷بÒ{”÷Ôü¯Ÿ`“Q—2ð^'DΈæJŸ 5ß“¾lÙa.œ„H>ËgÑžv›ÙS#,µŒí °¹QÓ±nþ±Ë|_Û>j×Ri“{Žxðœª[¹œ7ìÃæð§|¢—Û¦ÌK´!/©ú¡Ô£Üu¤ñ¸âž?5ÚžìN‘,ÇæõТù2õÈ&Ÿ§ŸÙ },øÎF6m+gŒ=jWi¾otb$ú õ|óTÝ£ð-aÔ7tY犌‹{Ñ&>¦$‡D]¦–÷–ð©¼êâ×$ ¼t›è¨·çÕw:½…(7cMF.1;}ï?'É¡ÔÌT¤ˆ_Wô/öx…r¿Ù]:¹ØÃÙ–à{Ëf‹(#Õ{3"ßùRx~3éÓê÷^Á€hiÖ©œ Pórõ¢9žNlÎÞ׺äÚ~+%ðs)Óú­d¬=ó¬>O0Üy‘hé"v^¤ùK¤‘þ%ý¦)æ\‰4ˆ´Sq øËvJ'<ÉPêÊÑ: é†h Z.دâ& Ð$©®K'áç2('I©¿z©¤¤fIƒ'U-$½¥?rN¨µKŸâ‰|"ñkJä×µhM’$A¼tA[Ú÷ý­HK+íá,(’Ó?©pÇé’/þ•Ì)òR»c|É·¸å]Ò (ˆßRÀ½(`,ZD|œbÀ±–O°Ðð´n½·´á©EŠèjÂDzxO’T\4%BPFׇ•©ž/ãž¼ÕZ ÐƒÑÆFWÒUÿ“ª‘—|?&÷£=M:Úayãn´–¿€i›ûø¨/}-H¼F€œj]7rKF³M•ìùøNÎ û¼$ªBR¬d{ù$r/ò|Ñ]81ôK‡ ƒ,K7¾p²ÝKاþ Û³q/» éP–ðU6Ðm¦}I¾F¹™³6°¤_üòÚ§Yý 1ÝnÌìTè‚HÄÿJàg. ÁºN¯’z¡Õ®~{Ø–@ÒsïÔ§vV°—¥f‡*†lÓ Æzfö³WrgYo¤L'æó¶~çìN<¼ ?»q—›½f_ A¸ú´…ìÛü™¶6-ê`’û—ú¦ŽMšb­CÓvizu\&±ˆå;)/Ù_ÍÏ+hLŸwˆð¼ìµ¼¯"¿ùæ…âpll«ç}+—óûüÿh?I*:û ÷.’ãS[;³¥6¢NN¹"´c~%õ¡Õ"Á¾šé÷ þÌ;Ö$ÌÎÖ—RUÞ_€ëæ®·:¹Ï?‚?áÒ ÓÑ/Ò×uÒÓÛtkì6¢dœqé »›­šëd3«FÅa߯´yªoh§¬bIÕ†í1S×Ðhúý6)âoÖ°{³z–.AìhÑÜB[’ÎÕ‘ÇÝða\EÂ[ª~vÍ^žWð Är›EÿèHäéÝm÷ìe–i çiC3‡XPÍ·kYž>’°ÔgíØ 0‰$æÞEBJêä-¸ÒÇlUÇÌ¥&.ÕKê1]ò‹wé5øÖá‘Ð"Ø»‚/¯ëTªòÿ*ø%Mn£§‡õ©º×F•¬¹ ´ Ö1„ªG/ý·Í¬dto~„á@êÖÑ,µw³ý¨©º5G-ç[¦÷Ãu‚¼Ô“òÜ,²3†5Ü™ð‹Œ:MŒ™µeè ä{…jÕÇ‹çba~%é-Ü6‹æ˜+§6´îÁa¼÷Oô—s¨Ó!´Ø•lßÄx·C'aïS)<Ï”^ýNiü¥×#?»¸:5âhV_Q–ñ¬sNÒá]I’¤a&¾N•q¾P4ÿ–/‰íÃ{6¢¿jï«þ9ñ ¾qwn 1¿WE4bå;m)òx©s×&ʼnœš™ÎÅ^+I}J’jþN{Ú —÷)øBÓU”ψBˆÆs 沦&\g”õ‘#½Ôžð|²á¾ûaòu{Åð6ëwøQ~ ’PÅ·Þî€hÚTjEþ—$¿Ê¼²ˆª°é?$•P/„ ÆÁvŠ„>º#5A§ ¶JÔŸÏb°*›öŸÀCˆNfœè Ô)%á7#©ÃG§´þäLJƑêv!uâ…+¢×…é%NžäÀ5þ†¢€¡¥©Í¤ŠÇyq8x"R žjÙHÖfòwp¸5™§‰_ ôÝÄÍÙúÛMð2‘f2´4w:ÅokƒzYÄÞã:I¥Ój!Ñc8¥1;¤²Ÿ‹ON_žz½œ¨Éö8?ö®-'—»£$NËÁþŸÜh%|x òQ†2ƒ´ÅãÃ9åѪ¯àÐ2“NKGý%éÖ6ó‚U5}ÀÈ6•ñ…öKý‰ŽãÔËîô”ðÙ‹±íCûçÙP“ª4Õ_;µ—Ào¶¾Ûi–@:}K¤õ·@Ñtç·•y’‡JzçI⿱DtŸÞUGÒË}m îÓ"öà;ǯß°WB³K©Cjm.zzÔ6,Q olº ¾÷],c½«v´áU«a§<Ýw¼J{î€}ZF²”\J xJˆ*3wŸÙË…Q{Ø%÷i­W‡:OmºÕ¾û­š»@'äÎ1Ÿ$FEºà.Öi*Þ‹Ôº6˜ü&“\›QöVœê¥³mÔøý~ €=Ø‘ÿAÅQ‚ƒli6mž ¢vùNN÷±wœ´y!øMÔ‹ôp;õ1ºÜ­Ãc¼£ÐïÃÔg¼©ñ%ß3¤@dZ,zªFnõ\>7Šï½}®ð•`Ÿ(š—ùY¼`צ‡T0¤OCÊ3„NÖg|^5•Á½1 çÊ`l ’È…äÖ$ †ÒÜggÊkø³&—o€+¥ºvJïO´3.štíž_×õd{s!mRhƒ¢~-çÒügéæN6(O8]õר~œŠŠÂ«¥|Ü$Ù¯|©@º.ê7ø>ÑøW¾ãì0HÇ)×ßcþÞGáéÇ…ÐI^5 _"“7òÕŒº;©v)œ‚ß,Ôì90ßAçºË“@FÔ2ÄeœL(ÿ ~ÏÆ.?¡:aï€ñ(!©I®m×Yë²IÜè6÷O-c‹¤¸Í"Ý=DÅmŸäû´É| ýlÍ8¤¸žt1n©pEè÷9y˜dWÀÓJÕ?ø¸ñ³úI4Ã7°á25v*gÐ)Bm ÂñbÒåžâ×§Cá³™öc³HRÝÏ&F¹é²ßbï=åÜŒÄ?¼@H3Oç° ýîyLýh3K°‘H@¼¡ˆøS úš‡Tfc ÅÔëÅ{¦”"-ºl’Ôÿ%ÚXÍOpöÊ?”åW•Ãû4ц´a»åëN`Ó]5¡¬@3+¸ÿ·:·$¹úK“n`-%- [£æ±ZŽ!ÉÑ€"i‡òZ¼îKŸâŽä1í-óàÙâe¤ßÖ$šI¶5"á–ø}$YR)š¿ÌÍO‚ß(Š¿"b…éIÙ$½´iYÏ×›Ú‹¥XRëÆ)¶¶U iÔÝ~QÇj›Aº—[kËɶ§ üB©WÒoB´í>”%Ï\ëœtäªImÀ‡x?” öML;å_w7& Д{c&oI‰ ]]´ät÷“r~Aõê f:IïóåÛ óðÊq&àÓ¹)ŒU¼$íÉ"E¤²8U|±pJBW­ ÈŽA®N>5Ù‰G$—ùºP ¦ÆEô±Ð­%@·Ô”ëÒ§Ú=ÑbÄ»'ŸŸ§½4§wH»†üu\\‹9%é`-OŒsÕ÷è„ßT>Œ\˵Òo¬¯@H—~•£K#0]å()¾”·ž•ôƒû4öÓÿšÑÞ Àn^à¨SU ôÒ[·Æ}°‡`M( M›ˆ$úéœ&Û*qDqT_ŸOIlåÇu)çÿÔ‹}²?±ù1ľɜiý´ílg©üz¶pöR'å ÊAØ´» íYWn,¤ë·p Cðóù÷ìÆ{XŒm+±à@ü›£ºï s´‚û¿ØKÛ9öÏœC²™šlôöX~>;•›ÒOKM!v8Çù¾ñ!$p²½pæ¶îŠ~ÆUíäŸØUUG aµ‹Ù­ìƒÆí¬GÕ=V ²;¼bÀìC¯GRß~l6&'žâAEŒm0¾ˆt÷B}Áà IW!ýí¢òü°cg/VBL÷Å=™.#ˆ$q£c¸a",ÊE’¬ ¥rÝlºyÕ¿¥†ARš·° |Fí?lBõ¦.ŒtgA:<ß‘¸oØ»)¼‰ïIé,ïàŸÕ£BSýz<§Pûa™:þS¨íx:‰íE€ÓÑ€ï¹Ó½ñX’¸©[è‡ mÑw9‹øêûݲ‹òÝJ7+‘ŸÉ¦Í3‡Ú™l4Láúd¤à½« ßï_¿“7}ñðUÑïsõ ~“e.vmqpã0Û€ÑmT8Pcæj yÍ0V$‘»=»¦ú¸Ó0÷4>±i¬æñÁYpN f~Su>€„Ÿewv|6¾|­éò0ªÿ•Šêy}Z³þ+Óû/€,CÇh ˆ X•^óŸÂÐ~;¡êÀKG{žYÊ;{‘·–<'ósõèÔDÞµð‹Ô¢¸ÿÞ9ˆ÷­ó 9õp¥ð\¼A¾’ ÷oæKè!1ïMêÓt:há9ž(gCÛ©±Ù¡K8E#rÙHt;‰SçB¿ öãÿ`gûå¥s÷*8໫Û䔎öžü÷à_ôuq7}Z>ž´ö=,³½­‘_ÆêýŠŸ_ÚýHˆz˜| kE‘Kp4ý ÞíÌõ€ë ²÷£ª üžØ?aX ³@Ù嘴LKÚYþ$@îÊ`+êdõ«XŸaŠRIQÛrâ¡/RäcL'cO/¡ÔN¶Tçz6µ–+ñHXkžÃRG¹G>wˆ=v‡ûžè$é e"^Sz2o"ã—âtÒ!ª»êìD—£Ë™» ·³ñJ××¥ö0”¾gå(FÉÃ]Øù¾ÓU_¼¦\ÂÍÇü¥½.VÃáp–þì˜R«‘Ím¨›=l%TÐèÎ£Ì Ïƒ'‹n+™oÉURÝÚŽ™û›‚´Nâ[è®__’¶¹LÔæåÓ%s¹¥ýitãñî˜Þ†GÓßÄ\2¯BbÚr#\èð§ n û\_胕SR»k+?øq‰¹K•Úp û”8Ì™5O4ý{?Æ÷‘éüZ«>@û³½ÚKà—)¶°‘9ysñ霤ðëÄéXá5ÉKy*AÖ£EU*ï®E¾Jt_ B|[0V4u­èSÞCÒZåh‘hB”ô u_\ž! ¬smòº:ç:¡6$ F¹²ÒÑq©<ù©©Žº³thi!¶1»—„NêJí›ò)úÖjmµV€â¢¤ªFYçÆâº¶¤q;RŽúÈž‰d:2–¤°¨|5­Þµt¡ Ø‚Yº—þ¥xy%¦Æîr 7—ô.Òo¢ãÜ$ºyÊ‹ð+p$P¬"FÓy~çlåt¹Osš; pðê 6ŒìJó„à¨8L©¡’‡÷SZ^åüÕ‚/!_Ã#ÐZº]$7UOåú€ŽA&/"ص K¼Ýé;"c•ÀÿNO³ èˆKæÓô?Á}>7ν©T½Da9çót:ÙVH8$úº¹¡¼ý®a¤[YÚ²ú,X6zQØdÙÑI†Î•$û£H@TóÙhê]G0C•¼³j”÷.zn•±ÕoTpŸµfÁ<[¦¾-†î•½Áªº)³Ibñ;fÖ‰À:†©€z‘$œ:!·Þ`ŽÒ‚NŸ]zsy;z/ص ŸÔaµÙÉè¡üŒe|=GY ;Òó;[”Åé&gDn ´ç”M­¾û’0ƒ[ù?“÷vDåN£­Å&Gßìxüh9èÇÇÍ’—¿¤пÕ^l:ÇÞÈæÀö#ûöïüDw¼ò.€œUy»¨§¤ŸBÞà¸eÿ,Àõó/»Å^ŸFOʾhOs ~»¦çì‘üvpîU|gÙàÌ•ð¼o¸Zœ žg=|çÔAÄz¬S¸ô5Nø3)ØÐmÉCúÜ߈ôg±ü„ k:.\J™ëø–'q­¶•Nb1¾If¤¶+°(¨UuzÁ=Jw…4j5yÀGÇ›?ƒ‡®ÅxtBý¡öRý “®AÍW%¯—e¯Ä4Æú®鲩˜üeu‘ƒôŠJb|œ¸vý&Ö¿æÀÏNè!i“NŽ£™Õ9³«åônôñemÁ`ãÊR«¿æ»8Fàf°/eðVœžÆrén­H ”KæxêóBÚß vrõ†¶NúL\mx¿|Š:Ô9„pN ?ÄìàŽ‚GËuô+¿÷æ^²Oào4rÊ›6"j:„fm̤Ã>û> ãݨ!‰b»`½ª²ËQWßëNýÙ©½YŠm¶Í”–§ÚçŸ-\* P! Žâõ§Fæ#ñÿD‹míE¿«Ðlî/r)o™neß²½Õ'ÜS·!x[ @¯@Ñß·Ýòiû?öÎPªjûÿkꨠ€‚ADÅBŸØÝ± ;°ÁnýÙ­Øí3yæã”Jw×í™ù¾ûœ3÷ÌÜ™{/Æû»àÎ9g×Ùg÷úîµ×ŠØKíñöó©€U´W6JïL^îÀSãצ¢'±Kþ¨—yiý[ª!©gZ?RdZ—k®ýLºkÒîtÅbÔ{ŒvLù_O;J‹Ô¼£Ø\£BtTìÛ†E ]²QûœÍÛÁ¼1X{:ZN‘òp}ŸøH¢»ò+yÚ>,{Ù«„R̾í;ÝæÆ:SB…y±W;"Ýýu­ÆdHã‡3 9×{Î÷›x×nÎGêÄT?õTà]Õ½I{KTÞ\å­X•+u’E´}N)t¬8ÕÚùë‡,ïà¡ZóÆXúëÅ„ÿWàêÊîñJÆËþ0Æš·Èc˜dßâ?ô0Àf‹Zâ¥^¸¼«ÑiÅgðîÞÉm>ÒõùH'¨dŒµ!Ñ]›ô|Úr‘QZIå‚@‚¾PòŠ}@9<ä±ø#¢>$H<~å¶O{x'9ß%/²ÃÙ¬® æj£G².cc‡vé6AL/|è©ÉÛäí¾Äëw ‚JUWØÆ™lšž+\ÝÛkÿŠí$±ý¶ß 5ß¡è)v&^æÓtï±ÙèÙ_ÆÀBó¸G7±‘1>O”ÅsÇ7]eª¤W½›6ˆÔÿ› bü=u=AÀÕ݆öë´=ÙCiý¿ë‘ìüHù˜:P+Ï.€~vÞygwÿöÛogÜ‚›>}ú·™ëþ“j2÷á›~?~l[¬°Š¾Š×ÁÃ~Ëê~Vmµ øõ ;¦Cëµ\ÃJ|Îd{qú¯¶ËJm¯6Ùƒè× fÚÓ° Ë{YÇb¯ð–U>'WWØ5c¿ÎJ¾(ŠÅx‰mÓºåÆQ5&‘ÞØªù¶%åý7ý±% áœiöoZÂX¡i^hhz©£Ç@…“{ì±ÇÂî¾±q«Aàÿ1™.„ü…¾U†Íò3õ¡é#[ÔAŸæZÉü¸ P À¥óR¿Ýî+&Ü…K=奓àm{gø»þ‹›¢$qeõíÃùöì…ýâ¾Å ¯EpH…ûMTô"Ò,Q2PSÅØ¤d/Àó''Fµ„?I»‰$ '`$—iŸUë1­¹!êŸel-¬w7ðQ¿8Š~$²ÔHÌd5ß+©]I”HBP‹Èï±KŠù~Ýਟ¤‡$=›Kê×:*V:›…Ê ™ý´¯]`€çP†ÍÑíI›Ô3ÿGû>ÓoßRq Ò©ª¹†³ßì?éHqú3¼tÞ0aÇÈ&<¸#£aŸ—äTÝi.Bcc$ª×£\Áâ·ï¬F_$ðX' 4¦¯Tí¼ÙzÑpt tΛe0Pº£Õ†tlôPÊ^ÇÏ ìïðF^‘™¨ÑÃcšyÝtà~Ä £+ ªIBF·æo]À£=h`¬ûÕÇ©ç˹žæ*§äã-êæÈèövƒÎËZéÝtt R'^ò~À¹±6ºø,Yý†ÿ¤\º 7ÜÓ-¾2OÓ„½LóKÈ¢¾Wt¶mŸ¼Š’=æ¼$@îþU+M~f39*Ýßvµ‡|Ôªþ46¤6d>G†NUobß¶|޼omO¦&ÙùôãÉú®²|FZo¥Ý¦ÿ6Û¸ª¿íž8ÄÞCz9ÿØDTìTò¨X¼¼J/ÅN\&à·‡U–^fUöE"t6Ïå”áç€]wØJu7 9D‘$ùÊ>òóE[—Á6À¥ ¾¥Ì—üWÝö¦µäk׬ioÄZÙíÉïìIƆ¡‘ÝmZ¬Ì.ª{ÅæÖK’T•a7À;éâÜz¦ñ½£†!$I)‰aõ@¯ñ6ˆqîÚÏŒLµŒ(oÝf k¯²þH¥yú^@ŸöŽt·Añ)Ÿ¶WÝ¿mK¤çd@IRo®m"¼›ó*úq<ú9Žð'¬wº yúÒúDV·¹l(¼š2Q¿ÀJ76xå²yé7öiå¶¼ v’WÞð³íN_d¯ë˜K?·Ã«°#ãíèT-:Dç{Áco´ù·mI—¹6€Â$iƧ€·kdt8˜Ÿà.ææñ[2'Íß2P)žŽ¢ÇQªóaÞs³½¾— žx†t¾EeÊÕÈ ÒnÔo\»½‰~ûÁ†g‚.Þ |GÑë´Á[ˆVc=PuRøW aª $/ 1øFfÀÄ£¶ Ö÷䵩ݑ>ͧ_¢Kuÿä3løøe¿Ñ–«hïFK1úº‰›úl¡S —ÝíM)±ký ÙÇÐFülRl”+ àWR£ÅƒÛv#v5åüå¯Eeóè [SulTÔë;¦a-ý’Û”š=›¦zc·ÓµÌ,¯~‡tÜÿGÀI«î‡onÝ¢Ò¬²ß®5æ€^š$*N}WÑ¿¨ÓóHúçÉwÆ}¼óSþÕ Rdk¼¨Í\º`„æ{(¿Wph—ó­£Öüªô§y~µ¦:5²¼o‹c+çäÐûÉ à-¦Û~Ä”3Ϋ ÒÊY¸Ù©@ªaç ©» ÏwþoÍo«r›hà\ó$°”ÖnÑÚv€½@uGž™ö³½Fþ‡î›üýRé/'âßa—z x,þROö:AM·…AßÀíïkã%Í4¶9¾jþj ôUžúŽ‚±9ƒ,\µ´¨“E€¾ ·4@ß|j´ÿ&ít/ ;äÜÃÂç¥7XÈ-)©t åR ‹4쾤 ¯RŸ\fœêt± Ŀ٠¯˜¬FHìq³AßhBJä¡kkûæq•¶¶ð(”7ˆþcA ûÖ€2–¦C>Z¨‡ýù@__ö@;ôù›P‚Üæ}BýBðH@’*É•¢üš{UÞU ÃÓÕ0¼p!Š–sú*Í\ÐWàßÖ0¶ê×ÒÍ ;Ø,šàë6¤¯Ð)‘À€[}Âß¹Û@åKúÊQª3´ÈÏ}ÑAê-¶"‡d8&ô•ÔsAJeéù¶2ÒÆb„ÄMÁXÙ€¾ÒM**´#@Ê÷@IDATÁ%fE<úfÔù4ú*½µS Xù÷AZ$†”YÃñAabümÿ9ÆžÔÞ·PŸ‘ÔTˆ*kä ÔI̦]«îÞó¥ûnãá¨mÏ_µåCq-=ºþIRµôÊ€&s3öøzyÃ'È#IËÚ£àlöp’QëÖh;ÐÏŸôý˜¶ô>åô 5?†ê†Ä1€•ó)‡Úƒ‰ ì¸b€ÄÑ|Ç ¸íæ@˜U“_º×¾#é5¾Núµƒ}U7ÝãðoãúÛ±l~t¤÷î?ˆ/?Îahhš lB‹s­´èa—ξ€0·F¦š¤¼Äô÷aƒD@ÜÏ|ËÑ–öm,ÍØRd‰ä·€ãóìy6wÚéxù¢õàØ¦¸4‚냊®…‰~ŽõNû"^n—'¬ƒj‘úË&˜J'AŠ+Ìë -&ן°‰óí€éŽÐ!\Z±)ùÜŠGXÄïÑŽ,òîõ›þ®®èl:Iùäq œãµsÀª€šb@÷äïUæ‚6ÉåìNÆ µƒ;Ø¢ŽÒO^¥>^–Ô+ùŽÁ–î?¬|ƆÀéÞ»j¯t×C(+§â#N=ÜöeœÛ–̓ñnŽ©´=j²ù8ÞÆ’¶GùÞ ÝíQ‘µìB¤¹_Mÿd«ÖÞc{˼ÿ…]p$û îóH´Ó­Ä†ÂCÔÇr¶Ò ›Åöçíl§¢[Ã}EåÌ(¢6SLH @}›ªM¸ÀRKr4Í•ËYäÛ,ãc»a±^#´Â<Â{ ‚¾¤§Z9¸x0¿\Ò‰ÞÈT¨îÏ«Q ³M ÜÉ éƒ76,u‹ýa”Ê0²9qëB7}»ØC‰3­^Š î~º äN¬whæÝíäQãG>Òüí¾*¯-É«æûoòmÄm Êýÿ²ýd`Ú}å;Öó¯;×VXôzEv”FŸbñÖ_ÃÆî4óIúGNä´vÅ®¬³ ¦Šÿ“墩xpsP‚v!úUí@;œú Ög£bgØÊ~©í˜|:³¶pê*êî°ů±Q5ϾK½G* ­/éŠNœÃÆDÒ&ÆŒOè»GÑs¤'€J?ËÏxš.ãŸÚ¯Èéæëo»DOpí̹?ô‡µúê9~š~|Ÿ"âj”÷Õ‰ ÐK¶‰´é)Ð7v9ë:§àG¥º{¤#¯§ïø oyVßZÈÐSîìMdK4)èš²}韖4Œìæ3gÀ±²'möFòUNÛ® Ò)”Úûøã*ŠžLžÏæ†tDÑ#ùiØzd{#£Å䇱xeÔáhsúÂ4}±x›‰‡ÙÊuë… Ô¼à«géðÞŠÍÅBt'›ƒ‘K­%o§m÷bNÒ ×o“—PÖŸ»ègfNÅM¾òòÆÈíXÿEÑ8M¶Ý8i°=€¡6ve<” ­C÷MR§è«È©'x×töoy¨ÿ"µ¤>Ô‡NŒ³„6H½ ºnÿÒV=è‘K”ö'ÉxT05 øM8)ì’‘}µf?~~.œˆ›KzرŒ²÷å]ω+cœrÄ&#I.©õ/Ÿë¸˜ÏdîÝzÒý´úÇw_1¾ßÂZÖ£áÖ‰ Òr€wñZaÐWÒâ½[ÔYñØ!ÌÇQ/Òÿ¿¿OýÑRXÜý#©gKvi|éã>«t· VÛÐ:•Ù;³&˜¤‚ÿ¦æ—@ýPÔü8 ©V>êø;vj•6'Ð_0Lê/˜ç?c–#K)SÍ`›ûªæjÍMK“˜(8&dÑÒF Î²"}ƒ[aÒRà»,¨Íóm“µ ÇÈ?%ÇYïÑ¢bI]…HRÑšC°x0”K…u‘æ†lúY3§ Aä#Y8.D*·€éÉ S³§“ZÈr[Ò'Y07oì|õÐI™å‰Á2ÞI†…½êÆÖ»nZ ×»6q‡Ç4F©ºH’b)“ôòÊðFcè÷Óqͦè–a)ÑŽ´ó=šøfI|~D#°J¨ÿ¨LÏòØ|ï•á€Þ“àQ7òбgùhCò¦÷Š´t—t…(ëh’ŒÊPú{ƒrsé5Z㑌ىÑõa¤‘ ÀHž{Èóóo¿Nl¢ÞÄlaœ:Ÿ¸˜¨NyRÌuª‚é†)­º‚Me—ÉHCe§%Øi&ŽŠ?ÉxVà¼lkˆˆ$y¤Ú3)PRsTuÝ‘ÔÉd›ýÑæQíê(—!¹À¡¢ie\tï͸x  —£ø­ÞÕIå°ý,•7'0öMä}WÀÔ]„Îݶ¼53šFW'9ĈÕMµýìªÍÖëI:0]©»ÝFÍĺîÐç©€šâÑY+€Š±7z&›ëÛOu· ÃqÖŽ½lÈ¡(¡H[‹Š¶0³WpzbÇsgذ…k@¬íò׃¶¸œµa|ÿØ´Ü<>kk `â#IÒMÿ`½*Öä8~+›Ù×¶ Ó¥ÏPmU7º4‚©Pù¥öï)Ç#\ïCzë=`)1zëøR·ù6¢ÄTŸUy™Í(õvÁòûH¾¥àv¾uO4m<‡~ºOÎ\t1À°š3)’s‘˜^IÅöö@leÛªèÀ‚ólçÚ'ªù9žœe³¢ÛÙÄÈ.èFîˆötÛ29½·+Z*¶ÇámokÁ¬Ë@UJmY4Þ±Û§¹¾0ŽQ 0²…óøÛ>3ÇLvnù~Mo×Öô!ÞF¹§YÛª›ì1Êý ò­¿ÓÑötrCFi¤Mi\ßCmtO€œ5 wnõ>ö’$ÀúŒ_ÇuMÚAoÀ…ÝÉï6[BT!±…Óµ‹'4×µCÝ};ß-ìíTˆ\çñÔ[$Ô_K¾$ôrŠâjEÔÇÏO ¼(rmw/À“5(«Î‰£žŸawam ùÕ1ï'Bk7ó:ЬðËu‘îÔÓ5N݆ŸºcÇÕžšQ§Rï·«âWXù0!Ñ­ñ£éÆ| ˧òW[ÿ²Äãø÷àÏ'ú¡Â[ía6Ðß›1½þå@o¬ˆöwB©¸–&=ÎssÒ¨š (“¢7ýp«e»›”€-ê0Cã¹›dýÖîü½Ò{4f]¢9)B›­}’§lNr"›: ©Ÿ@Ÿv&9VÒê¤î£Ç[e=ãEÏàÝÉzóUÏ,ʸ¯$ú"¨=ÌŽ.ûš3Õö6uþ` èn®—½kR•À»;Bk²Ík/ M ¼ýëX®ŒUÒM[`-iûäõ¶qú ƼHf,v‘d=”òÛ±øß|ÇÑ81ŸFéO’þwã“ ÅÏLþ+Ã$௠ƒ¿*Õ[±–ö-€zstò°›{~ž¢wüBõxru«»“îèÔm¾à-(ù>?…ן.Œ~¤Ó9~8×<'é&N¾J_ÙŸüÀyîj§Ù´)cwîúO« IYo€z×®«&_Ô)Ò±š¯üœñÛ‘äw™Wæn¿¢«3ÏMݜΘ÷5émÁ{dÜ7L¹y ûeî#ô—HgW~ÇV÷Í8+nfK­—Nuy­,„­jø’ºOhêë)÷Ýß±å1OÛߌ;˜FyÜëEˆ¨çht›^Ÿ@p—ú»†o¼—ÚÕÚ?8£gšKæÔ&9RûàˆJ;ÌË^Fÿ<«Àš4HOåè¥ÜÂWF‡†RÜáY÷ôYŸ:RGw… 1kIæös³O#~e$ê’xÌçr±~‚¿ç¥.²×fŽ·K~ý¤âš1_Ûçó³Ãwsì¦ #íìÑŸ¸¿ưŸ*ë™™9µ5öð”ßec¾´ÿΛڬOزÕ*&õoΙX0|ŠZy¥äïË3\ø©5¨j ¿ß,b±èÓs¨[ ß%ÒõÚ±_Ûg9ßäÏ{i‹Û¡«ts~Cýx§ßš=Á¥­|èïî‰ßšÔZˆîŸü½Í®©²á¨ª¸aüç6½¦Ò$|þÏŸ»ð—ùʤæâÏNñßÁÌຘix¥Ø0Ò$¿Ã4ôYú.ÍÕ“Y¹ô_½ÌR,Yf)7/áŒÑŒæÿŸ¥‰dYP^ ®™/jö‘áf¤'5 ùH ¯eIú†lƒ Û’xy …6V‹ýÅ ÜÐ_įɀ²@¤5Á7‡$%C¹áobÑð{ÑêH+¹c^¡j†îÏ¢I )1aº,ß",v^8È2»Ï­‡àE‹¸Ù3øŸ§G¯MÌžSº¸xIN¶›}iÈpô@·­$â–ºbNšCÒ5ÇrÞÛØ¥ùº±…m¦ŽÇ‡­A«LÈóÍ“%…¦&©ã]2u<~-ÞÜç[ð*ˆ@BïX¾ÇÒ ïŽgÒ™”µÉÂáÿŒÏo½É­¹~ùÚwè%e‘ ^;`hO…|š¸­;“0ã"'ÕãÝ6üUŽF5tÎu‘ú¶g’z†\è¾’¯¬kµÇÈ+ŒZ’zÂW¨É`F‚‹\«$,¢´æi„kÚÐúÜÊG[Ñ*BLxf­WwŸ•«¾\¹Öf žÆêi™ûÖÜOOý®+§øÍ”Íöà]mùŠŽ0¯Øõ%oó:o<¬7ŠDzèï‰Ï#ÉGø˜'0¸ø zÔ;²Ñõo+Cj¶=ÒDÀÄK p! ¢ûy-¦!¸£ }_ð¤†bmì„ôF¤ØÒº±É#¦=e°cò¿ö&Ò´ãÏ´õÀopä3û,\N99BŠSsËéÍà—Z§+Pwñ< Ô:ôŒ'ã{ÚIè½*q}$`ÂÏ 5ÜÀ`Lµ€ÝÊ5¼´&-~ŽwŸóû zËÔFµ™ÒÎ 1`ã&KÖ«OE’ãíãjÊ7²ý‡ûnÝ›ÚÀrÞ€£)ÛfXytcjíyè0®°Úºí®ª‹ »»Sb¶º­™xÖN§üzO’Ò÷RVi:EÏ Æ]®´÷ö±‹m{Sô3ÏàR(»¨ëØ)5Ôf !×JyŸlï#É]½ÒÞ•ö5Å. ¤ýV–}bÓÈ6¯9 ÐÞ´Ž6åKo罺§ÞEr¦åâÇ;`Ùb›d2‘ í O\GüÃ=7Ê$s*¥jcîdÂZòZï¾n ×±®þ}?À¼~Pòy;”üjÞDÒüh©5Lø$µÊ~f¤7å»=D¬—ògÅTüqf¥¢Q¡^çhœü—-N0ÿ}ü^Ü»füš:y¶ÀñÆ¿Pc2q†ä“ ”oí¹üü ']e!j æZ¥¤P#Û0]Ž+ãUÑkÎ?ø‘^ùgõ•l28à>RÎU«ÅŸ`Ðw¥.Žb/d0Ïëò8âÆ’çÝ}î6°Nd< H%>Fý6ª|ñŽè{¼)ð¶ç"ë£Ä£Gæ9÷fµÄÓD›`UñmìnK@;>Î6LÍpÆ}gL#UhšíY±®SA:P'ƒ4O~£SoÕe%û%n=Cë…KÒ¿Ö«ÖÈ 9‹1à"¤õ]Qa!ºYsúG^iÍ´‘.¨D©³‘Õš_DZ¹»ðϽ¬ JŽSÏ꓉þá Ü·â£›Æ)¤wEÎi=©…šç6Šsâ6ã1ýb3‰ð{ë.²¬M`µ 6ïœÞÕª;4·AZá®~5ã“è)Ú×EÔÍE´7„c¥é9ñUÖÕxA<}Nõ¾Þsè· ?“~#¹?ÑÒ&]Â=Ž¿“ˆÙ1»‘ÛÔýdëc`3ÚI`»AO°ù%zŸykjxÌO<‡ëjÎÏ鯖aPÔ ¦U0Z:oÚXê3r* Õ#›•*›0Éà÷Ú¼Å`¢£¸À|æ»ßBœ&ò¤¶— ½l ‡IºƒOƒo H'ɲ£z>êôEÕÌ%áöâ¹.Á¯6A7d"Nâ϶!ሌ‡#¡ŠU¸-z0§VÎt®ûÔ]lŸ¬«ÿ¡ô4* ¤r¡s Çž;㱘=>å'ûdžºƒ9U wOø–&Å §an]µÝ è™ °z?àæ7 gÙŽ+v²õZ¬è$e›óQ»®ØÙéñ~zD áïë=ü;I¢Â2Ù3î·Þ;/ãÈñ½V‘>ÙB$ÉÕ6…<Ó]R5Í¡ÑHÃyÒ/Í ½øaU$ÿÊ)£ÅII†ÄÔÿVúÏœ@½iÅ# 2Ô3”þ˜ÑÙ˜ç%ùß]âB,R8Ún¤ù¨“ » ªô…a”…¶ÕÕÛ¥ëXtLcc5ö_?œ€²æÐjÍëÂéHßZ!òÄê}ëaƒz·ðÝ©Ó>‰H§¶ ¶OØ«É{6ñ¨«¿‹ÂÛ’vë1Y‰:·¶N2úÔÔ•xýœñþ6âîLß–º ψŠÞþ‚ʇ†5ý>,OL⸴ÄnDÊ®Wª»•Sƒûq~¤nƒÍwé½ Èk%ÁWqÆ?Æow/`èÜX?îGñ7ß$pÒ_#©:Û1õÏ&Îv:h%½•.:ÈÊa”Ïl ôZ½­íSµå±@ÞÈÖc×nH)*ïÙëZrô:˜…‹qk ˆ(åûÊN–H³K^Lw@}Ãl [q\:õ¢]•š„ì—Þë^µl£‹¢ÆÂCR‘æ«@=Å\Û©²;ÑýÐþ´ñ.©o+®@QB©]™šï¼œ~õ¸Ÿe{°dØéÄÓvzÙD›ŽÁ¾1H ¥Ïô‹Lz4`Z‹Š­¡q<ßÈ·Æ 7Ö”þh—Ô]g#êh—5ûK¶ÒÖ¦üof{c-(¥{ñ˜J?r†ð“f¦t7ày¢ó‰š“Dï¤KlJñ[ÜQ _²±q@þ“‘µؾ ù›Eºw[ý<õ*'¦Ûfé}CRþå”mß§Uè)Ûª=äFÿ‰ #Uäy1ÙpÚÆÆR^ûÒÑá±Ô%}.=ôô¸üË]µõ`³";Їd»å<9£wÿ¬{Æ«šÝlptsÞJ( í— C­‹×—r£‡3–%º PÅß {M›E6%쾑ѽK™ÏÇß«•6tÜÿr¤Ä­du<–6±å–—â×Û“%ûSÎoR´1å4îIÞ—7x®£Ú-ˆ3›}.@Ûðñà` Ìijԡi#ªD}… %X¯Ÿ‰uK¼àÇÄ=y•Spvd2­Jcâ‘Ä{Ý÷ú°!-¿@Úë¾§ad¯%Ž(²?·ÓîïÀRIPž™hO¤^ÂÍ#©™s,j Êr Hj0´.°è ^Ÿr}·aB'¥§ælûa$5ž¼¬A­ ½±t%J£úæ½5©fÜr7Ð~œ$9uO_¿#¼æðºêdt3õåûû'nÆgMç«õÅCÌ·]JñIÚÛ‘$iã;372"mâÆ®à窱÷Óûëä‹O›hÜ„öˆîáNE¼¬'œÊq^(æÎmÈ×yëZòùRóĘV£º†œª,dÙƒ¨çõœSÖý@1Ý#àWÑ©¯C=o †uë´–6ý&ñþñ ¢hœnéÄL °F3OŠl„‘ι~TÆê¤yý¿Þµž Ÿ’+…ï> ¯ö½û3×Ü\Ç<ßL’]‘8–úÀõW nÝMYOÊ™™ö£€¦ŸÏÃbîr+Ù €™2Jv^çõmùx‘½>Ókdó“5Ökù¶ÎPÛ?–kk;jnݺ½Õ¤R¶ˆø?U̳ UHt\u ìÀv«;³9_gwü¨ö=\ÐǧþÔ@åCU*io£na­²ÖÖ·}wglí¬NëÚʨax)e¢Ûº-[Û·‹f»4Æ’åK$ÀW4jál[±¨d± ÅÅ#ìðIíŠ4Ln‹Á·ƒù¾ )¯}Ú¬fÝËVp ³ü×&%€æm%ÎN]õ$oG¡:BÆõd(îÀv]Ô¦ÕÖ/&Ã_äg±€“¿È7M\:{A‘¯ý;›¿¥ Ì~"’þü=),­0ü¹ïo‡F˜„Âát¿BÈAŒR@Í=˜’ŸqR©¿ÊØÌžnVï–{·“Z†ÝÎ)ÿ°_S÷²Ÿn*fU ÅŸ¸HNÿ[ÓÑ—zˆ] ”މ…ëð¿þØÖ¯º¸™iIAiF•>ÙB$ÉUoÙZ(DóÝwöì¦bH§™Ç<5rÉüŸ -ö—,€ÚÃÌlž„6)°cPŸsɱŸa›qŒt’lËð„{7´Vy›{©»Ðf ¤¥æÔžà ÷…“ 6~Ž8jGôWž7R{£Ð+©MÙ×KþëÜ2 ¤“,BòŒ8Ú4|æIjPdTеÄÚ—$Íc5uX€;*·~‰“ü{ϨŽ÷0Àð ô`.g‡Ô= cÕpô*Û)™°½ÏΩ<ÐÓŽòzÓî«ÚÂÞ”<$µE·'Ï#ÉÛÆ™.®¤Öi—ïF¬™b£Ó_Ú>u÷ØÆÉú/0ãEj´]Qu™Ýÿ¦Ö5Éæ•|胇KežI\7ÉA¶®ííÍñky7éˆØd¹±öCòD„dR$®îF@žh]\]Å1ø­RãÚ°ka‹å›¿0ñ1@Õ)öxú›Í÷H7°UßlS'g¤kl;ß>oñ9iÈ&„ÇwÞ‚º†‹ª.@:<#Çáp„Ê•뉾äe’ß_Óç üðáœÄ©6ж/_“–>Uä×Bú÷ˆØñÖj:£á® Êm#¤6ÛS3W’ÞFa±äèz¶e¼¾½Lˆt±ÛJ‡(E;•ã»Úñ¥›CÞþm•µc ø³:ÞI¤ŒÚÑW;¹ÍÁ†qp`KûÈ&zy€.;\𴦣q’1MÈufÑ}´¯[xè䜼Ÿe¬ÿ¼ªMFÇx¯”ÆJêž636I?uD_·t¡U ¢k{ALïêNÎTíK×Ú‚vú4ñß PÝúD»ÚjA±Û@{;u½ë;k¸íf6½?~[2sÕë.ÚR±ïBîÓÐó]ŃŒ¨ÿÚø/e heó'#UÎÔ¡-ÇU•ü!I8Ëàl=M¥ý’÷z‘vÿkŒñ e™g•.çÃJGpßÊ…Ô¼¼(~ïÜ<'fèѵs•ù³ü}bW¥[Ó®éc÷d€ÙÌ80õ?°о8·F¯s*0h’lFmM³Iå–EÞ7Êé€u7GGûØ„è~Ö• =ÕÇ›”É×Qæ”à\êGÛŸ/+ºÝŽMÙK>(Ëâg2¾;ª;ß»æüb]uQÝ[¸Nv>Ã#[ ãõ¡”3£™#Í«R¹ã¤áƒ h?wvR´3õ²àpߌ›nV‰d¿ÒO5i­]N[X…ëϱ}md:f±7å‘6U}bÜÛ=²Àžµ³“}U0:A&]Ä·J¯{.»ÍÅŸrÝX.ÅÿÇü÷zrùüu›ª¯ê†áÇ%Íxëúwv¤G0ä*aœ¦è8æ·£µy¨9ŒMÙá^Í[‰K¼û<¿³6¸ÝõAÊ!²ižùœ4'=»¡ÿCŒ‹·5Âÿ8Õ5±™ˆRÿâΓpm£YGQvHr&-ð¤q¥¢žHé¤ÅÎú-W²ù—^eølï6å¨J˜fg¥òá $_E²t8¹ÚKcÒ`âäP) hs©Cq™í…D¬Àãw稀êi:ªDSJoŸ0*ó'ÀZR½R±~Ë6.¯ÒÅ+•m‹Jm“ÚÙHãâmÑDÞïYÒ{‚‹¶ôÝÃo¯#-µR'!л~â¶ÀµëjÓȧÔO(ÿwNüÆ¥U(ŽóÌù‰å<ÿ‘‹þÈ—ÿýîeVó€lËìeÍL¸ áêÄ53R`JKˆógw.lÙxÉs¾™(j!Û‚H@¹ê÷0(¸5ç*Ý–aªBáß …ÂüÞîbþù‹òA9`^>°º9y ¤ëga/–Ô¤40–‡×›X.;uïóBN’`kŒXeŸKqïÜŒšv浨o5ü“9 Ó±íB¤£žÍ¥Æúƒ$ªªóÛ‘ú"OZÞßS–ÛÂz=%‰._ªó] ÿœ0¡YÁF=7ÏøÙ4Ö£og|½÷èñÎx¸™Â±ÄŠ?ÖÑÕYÑSº /¨s¢-Ñãç9}q±‘äUzh&Ú=c˜qÉsÀ”ôDîƒaÕ …Á+ð½ê£—§~°r·â­¨²ú`ìâFa0*íCZÅí>¨‡cR[¬ê©‘*ØÐúÖ!ãgN·%~gêÆséw–ê‚a [ˆ¨ÍµªÚļ£`J¸­[ƨpj žCÚè0€zgŠzœÏÇèR†­Žoï—¸yI½s,{ëp…âwðð]l:é=ÇöEu…Ôø-`ý±“z¼"õ±b0h¬ w?:7õ¨À,º17Ãì^èñž«Ø†þ\çí~ªŽÂªüÉÜvq„ fñâ!I 4‹ï,­:@%a©a€½9–}¤ôtúd#ÅÃì¥Ø¥g™ïà:TFü›oÜlÁªö_Õ1¦$þAóìÍŸ’ïðdv¥ÝIG…}éÄRÔXtWøã[i?‘6 õ‚¸ˆl³hoÛz~±­æ = ¨½‘˜ý(‘n”|MºëÂܦíãšKœq%œ:†SÈÃd:£’'W=ë.³Ï‹>\¹ p@àÑH@«Ü1ÿ%ü'Õ>Dœ9ä¦?ù _s„í|Æ®©ú€Ï¡z…£¡>hîdÄjÑ&~Ÿ¨vmå“¢—©«=mÛØ!vÀýX¡ß'Ÿ‘D”YÖU ÿŽR-~èé‡['êì—œ;âÝÞÎIk½‹^²[‹öB"ûSüzQßSN€Y¥×¸°/éÔÀ¾¶¢ß¦ƒ9º„úúXý£Å7:ÇP3+a€¯§u¬=ÛŽø’zÙ®…Á®Ôø @0¥x Ý˸Ð0l(Æ éÿÐ_Ì.gSŵY~Ï¡þ¯ARY×zZè6PûúÞÛòeÛsѪôæ¨GUÉŠ¶zX¿µ±µ9)Æ·ˆJQ.Úñh ]í'˜¶´Zy=a2†¦ž#þtÆÇ4÷/ñšŽÞ«8ÆÜ-|B)MùæÃͨФ³ôõY䮂Ó.Á"ãÁÆ»À`~Æ¢ P2)âÒ¬xzص^®8úOò¾¹­¨Aôz'5 ¨&Ê·`í3ÔCÑ-´«H÷LâÖÇHNct°úÀZ8º)eC|§ønÕš1‡Ä=)TßÇ©ŠÈu3ÜŒáo2‹!åA”¸›ŸîîVóîFüg{m6Uu<üÆGœ,°´â{Ï’ŠÕ&°ÊXsòslÈJ'¿ kö¤vw¦oOQD¤÷/I~„ÎФKFúέú! Û ïY TâAÆfÏfÖR_±™“ö‚úýB’¥2˜¨#ð‘îvz´¿=íô—ׯQŽgã¦'y¿±e5aþÑ0)ôñ–ÙXÖtüÖÀ(¥ê…²ª9&V›®³y¨eÊx¸›ø]=ÛIOèw |Ñ`®ŒaÒfMF²YŒ ù¨ø]\Õ–xG|ŸìÅÿâ›87éWw$}À;SùUœÞÅ|U"ãvÉKÙŒ¹ÃvO=ÂwíC½p P\Fn-u—S·´ÔJÝ|²Aè=ªë¶õÏ®Ý-pÏzã71¾1Ø`S-˜¼ã½Kæ—¶ƒ $‹zåÚ… O±ŠU3!T^Sc;QÖ3mä4ë»Ò®ŸÇ<µ²ÝV{´­©³#£ÁvN(Zú»‘¶yHrºÝÃ&uy'óñËiÃû r%Ÿ}•Û]ÌÙÄŠ3w­ƒê³¬~=0¸öðÀo‚)ÿÒïI»£íÝß$ŒÓisõ6ÆHõ½ëW2¼’†1µB$½ëCÔ1–hÜ,*´ÞݵѱõÏYw+ØL6êZ¹~“åzÀij¸½Åš0µf³Gk)}%Ñ –‰K,¢ÎOÙDë³ç9pâ/0u úßš5ѪQ_Ð  u@PÒVË"ET‘dWǧ:·<5}Ý©u'T+,g¯Î  [™‰Pëïv´G¥C·²V™¿Þ¼w$nSüëêÑ÷äó»…sl€j¹Iwð÷€¿€·‡—¤·XqÛ%¼ISzoAuƒtþÆ)é'îŠÄo!Rü¿~iOMýÙ¦‘—n„Ý·­7 úÃV¡¨Yî4Ý¿­ÔhÇøs|ŠÚøŸ™4D±vüÝh|°HùÝÞØô‹fDÇO—…%¥–FzK’Fp oIâþ^q f-Ææ”ý‰,ns©YÇgˆÔ”ñ©Üt—å³À‘’Ð 4×J’JL…¨±£è%þñ`Ÿ5|F>x^R°ºÚ/ë\Ãd’È»0bYÑ,Ò×I¤%o>Ù˜”oGÇ/Å:ÞœV‰«¬3÷þ]G»Ð˹Õâ0£Ú"vm¶§@ Žt7—’~YJ¿})²Mp—¹ÇÌ„SYÇœp!Xwv&TÃ-º× Œ¤CDggqéÐ"ÿÓ2~P>éÞÀ/¸®Üä\ÓứORˆrz ù†§ Ï÷_—­BÀúÒHÿd}Í À^²N€\@šÁŽ—ïË,üQ!ßá˜p=&ïÇ5uúq397N*nÚ˜»1{{_2_ÁnÈ ÚõÚU=1V´¢mã4õGæ­FDlrôDZx#ZÓìøh™ŒÓæã' LrÆÏIr@…¤¥Vd×ä¶RݹÎäWþ0ÎIGäÑëÙ®æâŽ•²‚MÞec7ÙÉlTt´¼#ábg• €à=“¯0´³>N²WéÐéäMkàVkéÈÜ!eÎx‘ŽÍD ¹Ö¶Ö—p<{@L«îÍý Ḇhâ5ôêµ²ÓØ)ôÙFOÀ”ÄÞÑ Éë âc|#ô ÀAì𥄾\ýÎõëH“µA×f­Ž¾5/`˜m«ÔýÖQ’ÉäôP¤Ñ2äŽçþ‚>Û[_Õô×Õî¨:ÆŽE"m×Ú×ÝFú³Ы({@Õí#i{3ù±…´t°ÙèqWì2ÊH`ÓöHŽ®X¤ñ«g¬½Û™[µ›®™×ëfš'éÛLÜÌS9ãù3{/~ÆùÊì½å¬Úˆ Ÿ4ÒÅÚôØÈ1ø#]X÷#`'Ö“´^·ïÁ‡º´ð)zžoÔ’ÞÓãP$a‡î-K´mm“/ùiŒ¦xž·£SoØ‘€ß÷KQÍà J*³ö&€fofþX`]jœm˜¸ƒ»v^|À2Ký°±1› —Ö8ÆX¾U!OEÓ¾½h«Ùô;Mó=eïX‡Eÿ´™lHL?d×G[Û3©Q6c?•}oGF¶uéºÙ/²ßÏŸ#òxÂæNGÒë[Oû×q#èY¶ê»lJ‹Ïìžm%WìžÌü‰³ÎØ‘´ÙA¶Ib[[æÞ©˜: ñ_ÑÿN¦ß½¬¼µà;ܼ²®=Z|’ ¼ /ù~‹¨ngd¼$µX†¤£Ä‚Øy*@üjôÝtÉÃYÁcðìäô}Ú ÄOQŸée…ͨŠÉrõ\ŸEÓþ´Þ—>Qƒû¡RM£Mt:[â`ü;[»H[%~!n£(óÕêã„ïă¶G?bÜ|,ñ!{„C7r¿eM™ 0dãÉQr0oÍÓ†2lM=”r,e<*³Ÿ}u:¯ê:Ãcþ&»ìKœÉ&°6Xú1Î üý™Ý (Û“79¯Ì©ÚmõlvÎÕÔd땼œ±Ï_u ®=ÎeåtÚÃlR¨îñL¶$êm Þð.hÖ[© øb\Í¥µã% {^å5xùïäN'‡ÖpëYÕF0†$¥;Æ_wšŸÄ̬íý龫wéÌÅÇ|&ÇÌòuÊZ’ǪïB€žâ½Àür)u²kNkPv'á¦ñ‰¹·¶o¶¿ÖÁénsÜSOD~J>r«vN¹7µÐnN•ØÇ%j‡’`¾ÑŽc×zgF†S1>û)eí!^^«*bÎAiQý{ØÔr›Úx@j]€j@S"£‘Ò~‡ÇâÀ©ÁU'=É—Ú8e—xu!޾Hý«;s3NpóôHÆñsØØy4~¨ ô× ‡äµjªzœ=G9·cžÖfÄXþæ”ͤÿÒw2»–Kl ?MPtCÔkƒÐ¨©±ô{ œß1¬rg¢M"ç6;ºÖÉSè_WùåâEôÛvüvꌱ7D*£k‚u®z7¯05›ò­ri“Œkw3æågÙglÞ–çdž^ͬE\ ŠºûœŸÚœçeõØÖ5TÇÍHT'Hr¶uCfO¶"€á+»þÃŽC-ƒ$_¢­sqK÷ø¯ZAß¡^aqH*ŽjOLJ¤Â! I͊ⱈS#!Uú+"‚ô Kƒò¸îr+Ú—ógØøª¶&qR ~I[éÝ-T^Ò´ð¯™c\”Í–dñû˜“$óå]6¶>¨oø]Y‡®;øÒØÄ¯²Ã p|6ª3NëÔ“|w¶åcE.Xnœ îÿÂU`ÂŸÔÆ—”BÃw&‰–þ ©)ALïo% QÉßšˆ_ù]yZJÙùÿ6é[V”%1¶Œ^"ËàùH‹*Ña~oø‹gOº0;•æêrÍŽµäOÒSÚƒZ@šked*Ð[ÜØQôŸX0‡©·ÿía·%¹×<*’$t.y‹…\׆Ϛ)³ Ãäºhq¯¤è¤ýRæ[úþ]ÃÛXßïÀÑÆv³(ÿ߃YÚz¯‡A˦¯y¬ÌvjäiŠï')AÝ·áo9ô×]öBñOpÌ’$„—Þ^©„îV[­ÜøW-ò»ÁˆVA¢vqIz:FV “ßÊ—:’®Ïþ0Á¹Ô‡8¶#×GS=©OÕëa“ûwžgDLájÞýRþͬ™)'¤^H-åwf'÷SèQµ?¿þ9~'÷ëŸsîΤ ã7ÚVô=a=ç²l. ^Iêjäè Ôš6åæéha´/R˜m­7\ÀDþ’®EýÆr®Î/s”úÜôþ/Iá˜ä,;¿îY»JReÐB÷;Õ‡RÈØÜ4f쎎ÓñEoÿuß…KÅV,6çÆ_s éè@ÁH9FÖæÚ³‰gìü‡ó’œ‘Õx­™vTØ&¶ Ó³‹Væ›:ðWk&ïÀÛ,ûüoŸ~ÜÖFM€ŒT~ÃêHÆÁ.ÇÐØvbf§ÇNå7X}s‹ôi»TK›oÃÑür÷[ÇVHµ;cýía$¥NŽÙ/ÅOØ™‰ÛìXŽ_nÎFžô”;’±5€g«õŠÝ°¸Qð(¶Ý$\æ>^òe½ pûŒ¿ê¢GÛ›Ha}3NoŽÀðÇöy=Ž"ùÔ×.,h*º3u£=I¹øðCzœ•ÄŸ³Kè›NZ ÿ5>'ì¥tµ“Ú”ª‘ùlœ“|•1þ"qùèçÒÊú¤ïT4ß_ ÖÝ@]N·í¥A D$f+,,³õ)mzŒg[G¨O¤!EQI@V«w²éñS-p|™$7kŽÃ­¾Œ&Q³í‘Â| ëçêödt¦¢ÛZéý[þ0\ SaU…!Nçûo”|Çö%û¹°èoSÓyï¸m‘òGÕÄ>„Ù’…ñÇ€ÉZ¯šsíàÒg]{Û6½ ¥·“ÓŽÖØ&çUk‘^G;•;'çØk¥ß¡†OùÇ0üe´ÝâOÀG€Üœ4-÷:"=‰+saÍ´™§íFT- úby[¯îÊà>Ú9õW}7þO¹ü ˆ¿8ù÷ßÚvl†+7y­hÒÅÌë5¨Ä™Ï[#´ô¹vF ·ÂÍI VÝh»p²6¨°9ÉGů§¾iújˆ´QØU¢zåÓô3pRâxrOÔrxTÂ…­Iú u D è¥hö•nNoàžOy§’‚°ÙWÚ›&aêÏØÒ®x0uvžÍHô³gjöÀ{¼ÝÔù5É)»!”û—~”.´Àˆ ecE¤«Se¥mÅhË’Ö¥ÍÆoR @ŽÚ[dkê~É€R9qsIz‘WA™Íò‹–³5âÇò¾½ ‘.ÄÝÐÝ˶Á»Àf–¼¡ã+Û…è?Èxe© Gû€¤¯V#Ím¬[.åÛ¾eLÒfÂ^HŽî‘œT¹®í¿ØŽH\G»zßÅéK”~ÿ1Œw¿ºú.wîÞOWŽøn·¤Ø?™ksÕ†}UüúV½|ªÏ´Ŭõ7~Ë2«Œzϳ‚¹Dk™ôGžGôt³®nß²áp=†×Hwq'6êc™mh=íEIÕf¨SæÎ¢ÛRfô=Rþ®¼¼§ú_§¢h Ï ¨—¹öª÷ îÊÈF:vf«ß`]Ý‚wtB6¼ºöSm×ò}Ϲõ;kx6ûêI‹ucÖ¹é_è——Ò_i_5ÞXrcÙ»lr íº)=…öÔŸö?…:M^oç½ûcؠѬ{êVê9,y±SQ;›ðleÖZ ØÌÊ•ºg”/·Ù¾°bÆ=9o>—Çï2NæÚ0íÎ'Íá¯Ðú¦ÞµÃ1Ìy÷'E6ÃàèŠèZ¯s› AØ}˜ ·Òÿ›%hÇØCÉïéô—½(N-H;ûzms%õ*6¦4¯¦Éx5Ήèt¢TV4Mš’Bc£NdÔfÕÐB󽂓/’ú“ìq¡þ›bQ>ççóñÝH³ä‚þ2p[ÏË{íU§€´6©Ÿ–æäwOd%»‚í±Ñö®Ëm™ÒÄšEöÁœÉYŸ!¹Ú&QìT9 Eϯ$Z­œoO"¡: ÿðµ–Þ܉»³k«mé|ÈŸ¨† k $Y¥CWFؾX0Æ.˜nïä¨lp›ø‘>Ýý|¸AP©YØý£̶WÑ9,£hïÀÊØ›À߀$Ñ+U"åGñ:Þ ¤‡¥ž!4ˆ’uÍw«|Þçïåcí†qÃík€ä—og]Qa¡ø¡§ÕUòžE6õ OSNR7Q•ÒÐãQ ÕùCåB ð-Ãs"I"KúWº†ŸÂžHåö7ý5K _Í¡åË}L5¿ôOòyÊïŸ-O’¢ùŸÉ†Ówõ}Í{þÅÓnaÛx&ÂÒ…AÈæêr Â/«€Q «9iŽaQÓ=GÂwH#Û3ai¦Ò5 þbO¤£«¹¤Í‚ðÎuX€ÆŒ|)‰™ZZ$ ) ÀhXðܬkü¾f jÍC+ß¡áfßY;|i¨‡Ñ¬°ÆòÂzÁ05•y_p££û:â/úæòtÊ<›),Iµ$Üš"1Ñ2–L–QÄy~€aôë³$Í=€fþ©hQ™®Èß:¾N¯`XÂÆ_cdSÖ|òÃJW] Aº1ÿh’)_ҲѬÔÝ„Ù" Œøå†•do gþSÀA8ÒqÝ+âµfY¢¯Àíé²o@»²6R_:­8>‰¬këÅŽ@º¼r”Që‰z¸{4ÚJªæ.µKIµYdcç§Ÿ“x›@F+~Ïw[™…ÅÅ0_»gµ±ÅHbD£§Û´]QWzÜU0<×!};¦foâéÜ'Ðæˆþ(ºç8ÇWGÑîÛº¶# ¸Àßd×&{ð]Òþ}Ý9öxd j:zm5ºí¶Ô¥wL¦<3juƒ¡˜£Ð¤¾ßI?Â8¾«¿ÁóAzyëª#Å5§™ôÇ‹•|“v¯M«=æ'Lµ0Ayü‡¤ä_Á8Õ¸ê«ùö0ȳ»Ø^D`UÑÔÿ#€×OÛ¦è’Ä"yø…tŸ'½í9€ÝG0`·†[Ÿ´ù34v!ÌçùÖ³ô*»·örŽñ.²ÏAðû-]-¿ÞcÄj0+ŸtãÎm¾%ù•Ò\Y°<„h\´Øâî(,Œ;íùG¾çžø5¶zKà°ÈŽ´ XCÄêáUò¼Zíõ¶s)ßË÷ `”¹~a‰'ùzeÉSvºPE‡ ÜMÂl Hæ+®" (£­yöG¥ òZ{”­ºÃ.Šíg_#¥ýuåz€±7eOÛøô´…oxþ7zcˬ,`ƒîàrôõ^@›Þ»âd»²ˆ¶Wú½ÒI[½•WlÃF6þÒ?àO'?"Ÿ)º4D*Ï´áóFÊø—Œû¤s=_­øi3± ìêØqî^ãfM”òEH;ÒÛN_d¯ÇO·ƒ[V1̵SÙÌ𤳠“ºŸÿi[Žh#Õ~|ßE—T š9oZ¬»s•4ccÔ9ÕÖöN}CËHÙ®±í[Þ¯ §rƒòØe±khèqŒ“´Ng“næ„§Bë9©;º„>¬Ó&Òûý2àg6¡ŒÖÍrêCœéHcKª¾[í¿ízm ÄïòÂH¿9uåQOÜOD"¿†­nIÍtk…QƉ7—PùáY™DwæoÚÃE6™öð_~wÎÆÈTúÉÌH‡!©—ŠÑ'D©øÑj)Ló)ó.ï⯥_}ÉF…T¨dúÒ~Wñ!réþ•qêS$ƒgñîÃÒ{ÐÿÔnGÙîµ÷ÚÚþäËËë ÊPýY#EœYÛâšWEŒÃVr´Æ‘Žx´O©«Àˆì–¨Ž™ˆT$=Ðâ¸ÿ*JÅ—Jß–>ÿ6ãÿ¢øA¤s²R«§(éú¤z:P=ÔÝtÓ‹hË‹V#'b%§»ò°{}k?χR¯ç³)× U<«*ñ;\¸Ë÷¯XÔݺ2ßEC)-³Ûñ•(‚Fú5ü'µ ¢¶ï½…îZ©2NÜ]Ût¶ÝWô*gûÖH„¦Þ:é;»ì×/ì?s'Û8 (.‰Æ¬_Çuœ4룓´Ç&ÿdÝJWpþ…*°Pµnת£•—Q Ž¼P‡¡'w#Xy»aÜ0®“lëVím'ŒÌ´®¯£Xq•Q «‰d¸®)È«²y‘¿ù>‘ŒÔI²7 }¥ì*WùÚ¦×VÚö+vt ¸ôøŠ6mµ²Óé{?eU^ÒÒÇïÍžh—þ:Ô™òƒíºÒªNùW$“ÿ¦ÿ¨–ÿw¾é¯þ%KÃ(Ó_½ þJùÏ_X™~¶£â½$Q‚Wjç]Ä2¢uñA_ºÁb¹A 9” wÎÓò‘¹´DÀgn"þ³¤ƒ´@ç¬Í‚úë†!a…ŒÌ4Âv‚ç|×@ª7Ÿ_^·h“7ï莘6 ¡ßlœ´L 3gÒY™!¾­ìï)²‡{ó×_&xøùH‹êƒD¹`¨ÚÑÏ0ÑÚ^Å„Ûf' CâWЄâ!Au,¡ïÄȘ(DR­¿®È­ŽNf‘Ô&H!N'Ÿ*ão§ìšHWÇì¥ç­Ùä@>?4`¥ÿ]0ª6s ‘ Ü4 ðqæžÍwd~pR";Ö4÷èIafûOj•:z-ÑU‘-„Ø0#:¬¢§»®ë¬oÜýôD_$ÿÚ.é7¬}j*õÔªïo¯d¼jŽ ðygñP+e#D:9%!u¯u°Ák[½³'éFÙþâÒÖÏäÊQ5WÛàô“ChW"Iúpj’½P3 V7uNGŸ¢Žv«»0l€sÚ&à¾ÀSiÃ5Ñý{}HºñŸ´ãŒÞSšÍôÒM\JÿgÀ—cì¥ÔsöiR[)isªŠ€6L C ÆÊ¤1¬qðŽ›ÏOƒ;/w<®`½cÙ¨PšºÓx3lÍÆ”É%ÎO:°]o”åôäCÖ5o«Ì¤²£&z‚ ¹¡¤;—oë ài‘5y¯ …v€ë¼/ §Ê%ú«õ¬ØÑ¾YØÍNB ¬œú~ÕòG›½#B7Øjɧm…ªõhÚ°ZïìjÅÚݨžØªö)tͪ7×Ó³ÀKCåÉ%Å­uò7.iüÈPñ;Ö*ñ¸¥Š´âô¿,U;iëÃØ51M½%$_oØXÜDû’Dkû¦önkY¹m&¼¤;Ǧ‡rR¦ÄT’ß㨻i ‡q09å1Òz†ç œ‹Þf®i›ã´N~KøSþ X´" ¿V€CNgï‹veòÛ3º¥]_qœÛdÃÓ^X RsG¾i¢2÷ëýH‡{Æè' ·ÕÝf+cÌÐ#Ê7ñ(¥ÁpèJ¡XÞítdW`Ý£ ÞÉ×ì™äµH­þ“RE‰¼ŽØÙÿ!ÝØ—~ŽBÞ÷fü.A¦>a³â|?sšÖ_¡oT€Ç~”áhb ;ÏŸK²_ “—Vµy‰6Luâô‡È{òÆ:w±º¼Ï[{)‰ÿÒÒ.æ—Ðn,~“œ=#E`zŠ8òÅFŠÚô ß?|Ñ1x«ÚÀ¾â,Š65X@ϳ©¯ŠÐ[*‰dG´Ï©”ͧ:Z_÷(Nmùî­©£ˆµf »,½*n佯ÞAŒ¥Ÿpzâ•EtÀð¶líeM5…ü=â¥éÿ^@ù~;œtϥζB%¯°§|°üMÚ¡ÆGQÚQìròµ{¬Ž]`»Ø^žDxm0~ÍY$ÝÞ•©{Žt®táÎfNqFxC›×g‘·èå©·0¾ù„Ãå^§uO+¤¯ç»>ãÈúY@ÞÁµ9k®’=ˆÞ\›RI’•Lƒ‡‘¸¬ÙÀUÒäêÖ¶X“Ô†Õyøné)¦–mæ «\Ë;–2¹ ¬f-ÍX©ÓórRÕ8ö ÛŒ§—<Á·îä¶pÚ#Õ«V\G—>nK“núçœØ<–0Þ1´/y*´þøçéSB{£ÈŠ´)QW|Wd{a$sô¯ÝFfÚ.¨þ'àa*§¼;XW¤É•\Œˆ1îäž(ÒÆí•¹ë8BѪ3”߀®æ¥Òæ_¬~|ϳ2¢Í±¨Qj’"»Ó†/.¬NeKÙ7Iô¡ 1ßkÞH¾˜ñq7Ì5Æœ“u +;D#O«Pß#ÈR’0jmQúÕ[¹ vüžæ”+éW‹ðé̆C¿ÔÆØ»]¸©üÖ13V¸ÞTà+wÞ™ÌBo¿Í“C}úôÉq1ÛRM·Åq¨AïT¬ˆ0}°-B‡o’0±³p1´D$š_ó…YR·:Þ=¯‘ü-iº‹OÒ»eñ¸óùHù¬CýC@ÿYò/¯7•²Ú£ºÝßä•ÀšL4?†–á¿G¹¼Ô±¨Ák{Œq56nåýŸ|Ôñ›í؉ý_#-³ƒ¥zcß&f显°Äˆ,fgOi˜‚Ž·’Ž }—…K)Ë:̾_6H\R-bp–iñµe³ú”fŠÅÿv÷’ðOds’ù4ì’¹s|»˜Ãf’;GžŽ‡…ÜæL)ñkKÄÅ$–½YKÒÆ¢ hú„@7’ÈÃð4–p3ý´1!³»-ÍŒ×X0éa Éo,¬ó“ôdíÑ“Åéæi4‘Åô5Î5z2 Ö=N’ğѳx¾5T’ª7rˆ9ÿ6~«„äåëu·ò;ÝY2é¼— ‰“°ènôH“Q1ôËzs×r0fçÀ¡PF->± •!+»"ág¢G"ùøõŽÌ±KrÁøŒÒpVt i­›É“tb†%–2@jO£_¤­’Çod †Q߀Qº‰¿.d½ªo¤ïO­ïû-fÛÍå9ó0óž,¿×·.‡±ü4ù†} #ŒŽÊÇVÔé{HáMŽK¹}BV¾Ïd'¸Y“pR6Ò}Þ"²³.§¹„Ä™@ïÖŒ„G0,dÍß*käÄÅê¤ð+ÌóÞû&q?wãÑ“lhb–ò¤h«Š®ôÛÏ€Kólr‚:¯9}³#]( ‡bÔg(ë¿_ï!5½ríP°_ÐQßÖÎ5ÜÚ.MÏÍ»{îjëQ+‚'¿b³óT¥3 •®åo[L°Ûð÷º3çÛ¹(QbµXЂä+€’¡3ÔÐÑLÅóךã¬UÌ‹Ú貟n;âVÏ—>\üš]@ücô {>ý¨‚$ÖŒ$u 1O`XÅ®¤5„8>Å丹ä= -ºÒ®uÕg³ñ“l—þß‘­xkJ³*:ya%ͪvXÝÇŽ+~ÐüX]ò¶í„ô÷¸ÚÉs+ÞÙî~®×R/¼_RØï±Ø>¸oÍ÷‘†æ­³S·s%NÑ“|ÎÁ 7¯‘öé–^þk{xáfvlü2âwhu¤*«)£IÌÂn\Öýበí©ÚÛ1Êne=4 ‰TI™g¨è Òîkc8^l2gü Þl€ÏpAM…I—-xÑ#¨êã7k@oOk4•‹à_ßhø¶È7vnj¶íŠÛÙ\ݦ=ÒŽ%2çïmÝ¢ÇØÏ¶“WûïYƒkʶˆíeŸ$ÿÏ~¦¿mÀ¸G¯rmãžÄn®•~¼\Ëø›á=Ç”’/a,êkÆò9ô‚ìR•Îàœ´H°æü5ÝÎNLËä\9õØözª­P³G];UŠŒG.}'Ùo®íô§íœÎ·þÈÝž1î{®ÿÉZ¿*¤‡¬j=uàÖo‘B$ª6°Z·¦bMQü}gÛú þݪ¤11Ö—bNþbö™½ã¤`³%Z³£iS$˜g{1LÝekÖ]gûFfaЬÂqé’¯˜›6Ê„\—wîT6Änq*¤*b¡‹»±×L䜛B<ÿØ|Ùºi¶I]v¤²á­ø)¶6Àõ-”êuÌk£é—?£œ?fŒÀœªTÚDkÉØ¢¶’!mh,–š õL$ùWJÞ ø=Å&©ŸhãÚ0¾›ïÛžA§wdÀûùÌÌã§Vü õs ÆbÇÚ ¬ss{FKF›ù±´ÛH ^™¬ºÑæíô/ßY©qrb¡`KÕ]Âwñ½;eñZ›ŒZÌ÷0Çùy^»y¡ØZÏžÀ·…U¨ÉnI7Ö4­çÚöF曣£g2n1c¨v­H9ã1”Næ¶1eY¿™ÿ}ów ü]‰X›Áû»ß þ+¿S‘ZX}˜Ë²¬L⹓ê²|ßoI[;Ö2ÞÕIÏQ>ý®’0Ô²õºá Sii§ÿgß½ñó“jì­ûIfw&ý¬ER(’>k®±ºI,uçÛa ±|בޖ¼¥9í&³˜/«ÜL`‹å¬«ôÏ­ÅNwc ö¦U»ØŽãSùTƒ476Fˆ¿¥@÷Á0ŸÈBMt2Kè{òÒ…_S¿PÓx5Œ‡HVÃu0LRcp Ú¬°cν‰’\¹2$˜d©= <¼""¦P$‡Jx~ƒ>µCÖBÚ $ƒPÍ5ˆ–“¬{,§ÿIp˜ij3h€¯Ô¢b<.¦|‰az)ã=GÄ®€€¯ÇŠÿ[Üe­}G@Áëüº—q}Fó¢ÊiµÉðd½ô¿z“Ü%;âQ:¾9F¦zP7ØÂØ&Ö29Ôyô`lÍØlŽË¸Én´;ém»Ì6M=<Å=«É ä”ÍK´©0 ¼ ÁûJ)v}éF¯Ï= Xò>˜éóí~ThŒ$ö0…@0h2öF’é̱íRÊó$M€Õ¢Óa²×w̦TëH„b—òY'X¢¢9^„¶v€`’±ŠÙžèc=bQW6W´ñ‰M-Rû Ëoæ'Ú´ËÙˆä`€ÂÏ3’›fí ¢QQëlŒ$ÍusN?†ÄïEûú„õR¡‚¹HÌwOÞýJ¬-úP§`\©#å1Á­ĺýÄ_Cz¦þÀèiä©ÄFÔ]C€rþ¾É Ø–r»ˆövV–{gô—Æx÷êEô ÇF»V_GY…4òÅvf¬¿Ýš|J9ºÀâln¼ÄñÞ ¨ÀP+Jûå¿ýæ–ËUꋺDÖðN±AUŸ—•ðû‘:z‘z;±l𥫨@P§°*‹—´gñi”m/bÌ¢ÞP—@N3ÖÔqu§Zb´ÃÚwXé´>GÙ€uÖÃX¦¿ÐnŠíb£â›…êH;¹6äXÞ(uUC»*zštžÃq1Ú™vv ÷|‰Òš°è¨ X`-b›Ù^è%Þ!õ+ÓÔâð»tÅZW{,~ Çý4$l5ŽŒm1ø@Û7ºý«Å•H›¦®)ãEûÚ¸ô÷Ä¡v©;§ÚA¥ˆº ñVÆ(\­q[‹üÈ=„¡·•É«Z˜¡ÀRcÀãþOOvaE›°srt5[/²ië…NzÔí­Äµ¶Iš;õ *îg‰ÚEÑåP¥ ý¢’ÑÓºJ@a"ÿ ¯s&õ½½)“+ÃŽÈ7¾1Ž1ÿÄPëQss–dÀ‘äµÊ(FÍîK¹½€!¨1€MŸ4I’ÕIÔâßÝôËÑî³NoD¶!3i‹Sí4ôR?Ã\°/sÖ†©ÆÓ¾VGjÒ±ºSVgS”'áÃj—±ÃS‘õ)‹ù V¬™rE¾ïÊå*ÿ!€~ o0y£Òb&¯öYüR}†Þ9ßAûZE ØÓèP+ÚæCV^sù8ú׎\É ¢{±Áô¸ç®:,ã‹Ö¶éÑEØX˜nD»Úñ© êq2a"üéíÍ¥5¨‹ý¨‹¼lÄXÙ»ôé×ÈêûèçIœÑ?M™›89IÉÚwæ«ÃÓä¹eW±áßi2ƒ™ScNU®%ð(}ïþìÑ̽éRÖ43‘ؼ‡±Œñ7Þ¹323çÆ| ›6J]y|@9ÅÐŽb`LÈPw5™§Åº‰ÍðQj¸ìµS/=œ€ûªÌÙÛ_â¾ !ՇŜné»qošÊ)»QD¤ïGi‡©e¡þâÎbù\ ¿´ïã)“UcdmàNcd¢•0îý`+T®ÉøåͲÛÒø:Ýë‰*ë*6êØɼb)ßhÎ-ÜgŠ^¡Oä-SûMÌF¬»Ú;Ðû¾»½ûFÚŸÛh§µ ðM;;ŒqA6orÕêÔßWŒµû <ºØoÿ;Âß%ðw ,³è¢Eá_„~/Ð÷/R³ùg}•QÇDÌñÒ÷¾Ðݘs ¶§I‚%ÓÒ½É8€_QôÕ$Ð…,ÊeYŒN.}r_Ú ¯Þ¥#ÐÁÒ'üî|ÇÝsA_íä.IBpiSú*]bZ æk7ZPˆa“–·QÏœð…m®ŸXI"þÐWïR»h|1Y GÉ<ß9}óÁÅ}CÌ“Gè+É«\ÐW!gq½yž~éGw±aè«ü†y[Ú{0>KƒžP™ôUÚb?¤VL~.냌¹îÍ}ËÂ9 ú*Þ¶jQc ¯â‰5 @_=K*ã4I\×ÎÓ9QUÛy×%ø•ú€ŒÊŽç†IR«è+Ðþ8 6d§‘ε¢×C ¯RZ¼mN’£½ŸÂŒŽÁ­›=akqÕx¾–­]:ÊÚpS*:fˆIOä#’Ð,zÿåíÓ¢M}gZoÖ6’@Þ$õ­#Š_ÀúŠ$£Í%ÈŽ®:S? ±kí-@ʓӫ;ÐW^óHkË"Phàc±~¸ÙÚé±v^õ^¶;µvöëð 6±¬t´±ºWt¶šX+ZrÔ:<ìó´Àf ¤%'ÁM $Z炾z!Ò2#8²nÅ÷Àànä˜èÕµ^kA™´ Ý \Ši2€3¼d˜ÝËŸ®8‹ë¼Ä£üò@Zíé‹úŠ>nù+òtilhc#]숖£lE_·¦èœÜ–BÛ»HcœϱAô=_Â<Ý÷¨`©Í ½zönÇSFcÒÊŸ¨›½‹®á^HZZêSÞ¾ åò¬ûžölíh/Ø.ɱèŽÝЗúвpäWÀ€ÅsRM¾ö™€öCì{¿ðqäYE¯ÑF޲³lW»½¶[é{×ÓGVaI·"t÷RÞNÂ3†¹ÈNvwÑuÎK?Ïò Ítr„1®ö~\T¢åu=§ §r”÷¨ºÁ6ªæ2Ïôü¬œk;þ&þi£ÿ})ÕRPaeÔG À¨ä ÜÒüé¢Ù„Y,y½õç¨ìüÚSlëT5Ũ`û¶­쟴ócô÷;ü“}'°i•¡HoªpZ§ß~I-~PTëdBù7€¥ñ+i£ogÆØä@þ0ç3»A”ÁÌ…C_•§lªsÆ(¥Sß}Ãþ̕弲„4Fuh_`zÄmŒH5Š¿ìª¬ ‚¾N½ÚdóHkþaÒ ¦6aÿ> ô•[êpªïë]TæÍ!mÒǬ™òñôjv- ²îƒÍ±£á770צ.è«“‚ ·RÀoQöÑæ¼ðï0—Àß%°ôK “v³Ó“‘›Évÿ3<­ë-þ YùÃò  i‘ã?‚vÊÓîš›VÍ ˜NúBÿÞùôýÝÂ’û›` ‘CÓòxJ/ãä<ýE“|@{²8+DšÀ›¢]hóAÃaŸƒ—CtµÐ0é$+Dsùž@O¯ò #q¹ËÙ€±[[ˆt,|^ÿŒž¾B‘sܵÔë½mD Šz‹·9‰xü/ Æ¥f÷¢ `}µd]z¤]ûÆê³¹oº6§oÛH>7£Mh+Ê×ò½óEyO—ï„ÞψƒT¯A»›chRË1‹ºh{üõ':'"@ LêKÃLÒ£¹”O w €ŒFH B.ºÊ²Ü9òtšzËþÀÅ»× ¯Ùþ…Ÿ ;bØ$ɳ4©áh¤¥û’Ñ}”Ä~€­ŽRC`Ïà¶³÷ú}˜ï˜*…‚Tw9 Éž9Þ_ÃÎqã1=ÄÞ,:Çæ§¾âAàØ÷ö^eŽø¶£¨ï5Ñß+’®Zå°[+Tîa KžÑ[ÞmÌLÇ8/EV³Ë[µòâPDF·f­/Ù¾6N—Ž~:‰«=À‡£Â±½`lèñ‰‡\Zc#å]+¾…]Ã1ÆÒÙ±ÓíDÛÿ³=œ§“¯yÎÝ«“$ƒrjÉݸJç¢øÉvˆ_ž@Y´E¯éföÉfǶt~ÙnÞ÷XÑ«¶å½¼Ÿý”zÒ¦eaجsüZ$»{áõÙ´þ>|—~×!L«ªXßFH÷h|8êÁÌOó¬ €T À6‰§¨÷WøÞ]PÇ1Ñ:«½Tfnü"Ì&éí,øVHŒ®À 'UV4ȹ©ì¬ö~¢VAŠ—¦‡R×´§îdªmþ­<‰:}š0~ð%o²Ö€SÔÞÉómñ;ÉÀÉèõ=«bc‚¾æ‚¹Œ}’dÇ tqÒr’̽”ÿeþ&m$*""" fs=ã™0GsFPFT‚1ÀéaÎÎQ½ÓQDLˆ""‚€Àæ™ï÷¯îží™…EÄóîãÁlwWêêªWáýëÕ+Ê.tvg£º_`{S;(²5÷Õü~¦Úާì'ZÛ&UvY¬»;ðÉb[:~ŒõÐãàÊ 07 Ë°o,MÊ ÌIž =|Äõ€Ë.5ÝcûÚtêh.‹|êÃ<{¹%€”q;m´]"úFf^‘Í2s¥ÎØýtXTœƒÇß¿vs¿µ¼WFvÅTä§Áëýë÷Þµ\a|@1Ú‹{ÊÞ‘×Zú¥ ¼´3É<ÉwîG¾¼4N®¹Ô¾t6y›Û±«ÜA ŸþŽù’|ã”ÞáÑá·Á‡,|Qu8{”OÆ]vXÕžüà‡ä3¼÷ §3VØžÈPdm;1õöö§ùN\9‘ †ÏFyn±+}þþŠ4´¾É§oY°Ë¥Ö,¤Ð«Ÿö—KRrƒP&ƒáõg>„óÚö©¾Ã¼ë îùÆÂñ„ïÇý\~Õ6¢úF¯P˜~·eÓþœâxãäÈ:9(qCÜ7g|~°°³¯ÃA$ø{¾xM€\Õ~nH]Éx±´x*S³3ƒQÌ¥Az2ËVÄ¢KÓdœ…‡4=Ö8Óîi®Š.`\ÙQu#í~k/§}KqÐï†)½(üDyGÁ¶ê÷&½–Ù!¨Âè<êu3¾glÌçóÚ„”f|Ò¾aA« 'g-éŠ[¡]ɼp ÚókTŸæ´š£& áví$…çâ‰z…Ä)ûdæ®~b§…ÆŸå!,Ýc>Å«—µ‰¨Ÿ(Ãã»â7{ŽyþVâvan9á¦]K—: –­§6´,r¶Þ§× –õ/f\Q_+ú†ô=ÍuQ†’]ó€Ðu!òßé˜ 5–’z&CæÒg×Víš­¾x®)?õ1áùõ-ÌceJ$‰£Y1W=¬*J`)]äªòùK`¦›¸üŽ ®ä¤>ÍÜ5ö•ùÀ³ÆÆý3…[¶}ÊÆçvylu6>Õe‡|uøNƒíïInbI‚u6üÌ«=(€Êm nd†‚ƒ®‚àyA¦ÀÓ¿ju´6ÇMCµ-9Lfõ=²ûõ9“ƒ†Hƒo0¹QH |!“Ã*Eº‰²çYÊjm@²‡™K:)M é|ž!·ƒ™,jòUÅoq‚‰X(H£n¤8áv¡»¾¤Il_S"pËwÍ`egXÂ(ïIw8ÞÇLÌ4e­OùÀúà@ªú¡ë\¾$½¥Õg]È¥ßÝ胯A¨·ȧüß§üë¶Ý1ükÑ¿ÜV9•s˜G (vÝ; …çñ¼3àû DâTM䕞@w°nQªЖºì8KÓ%L -`ÝŒÀï¨hJ8xæþCW_™G×îPºÎ•»ôœeòF'¸eÅjÜC|èRÃ5ζóR“Xºg¶œëGB ‹ö¨ç¬SÛ¥b¦vàha@ h’S‹ Á1éÙ:¹j³hWøãôÂçý¶±+9”ë"4%ßLÚÊÁå©…Ö­[‰ô¢+L¥Í³¿rž›TÃßòrÔÿ°ôwvÅ’B›Qµ›õßãùÖð-u Εl¶@Ÿ»±GK˜®0]lý êözªÏ±Ý(€î~Vµcì%;[uÑÄÝÌ áLxoÐØ«¶ ð· ß!-™·R7ØÙ˜!ð´t ÜRŸÈ&Ÿ#섾KŠw€|4qšÍ+?Ð^MœYÌ<.Rf5Åÿ$îÏÎt…â"‹¨3ò[Gðb©F6vwÒÓKìä9«gsË{Ù{Ø›ÒÍNîÍ)ð­©]Ñ<°HÚxÁ+î)ó§úlë=Óü|ìFY\Ï7¼7œGæQÝ\ø¡ý$` -‹¢'Â1íìÍô÷üýÕ¬Ú ðc»€¡cAo»9v†iÊh%@ z‘z~Š:p€e-½èûM¼;À›bÜ@-Fì@gKÏäÓÞuÀ¦K-]8°µ9@P[´‹Eíc}m›ÚAŒ-Kxú̹¹?cªŸˆ½^Â7EÏ#ͨ3€dèøá4×ï7·Å1Äm%ÎgwJÜAþ´¼À8Ýû¯¨‡Ç©²Û@öAñ£dãÊä|ÂLåÇ;Ëvàú iPÖÑýyÇ9öõ|~Í ž÷²^ñMlBÍ»6V‡M•~Ä6à¬íÓ*¯šEóMÜ–cʹ褿¨"¯±ËIÓ'· ÷‹]níYÈéä9F¨7lU;ÙØîžšá¹n— Q<,cƒš6ç4äÝÙ5bPÚóaôtÛ$ýW§©;†ÙÅÐäßì‡êîØ¾œëõ/€ÌæÃ®í ð–¦ÚÞÍ’—bÚa ÷N-.»þ@ý]Øá +ƒã`ÉXk]}šÃ\àÜ?Ê‹…t¤ÞEEÏñGíЧè©~]ò,à;~gàÃõGÀ¹žß²ÈcéñÎïôôTΚ`£ÈB[ vxŒ¥Îý·¸pÁŸ"nncÁÕñ˜s¤~ ¨sèiÉæ»;úˆŒ}êI¸ðý‘ºùžÌmÆ–÷ÙwJ=ŒFðæ¥å|ƒÚeã($y%o%ß”«¨ø9; `4%ÇÇ–)x3‡vÍÏ\¢¾1~WV¨SÙñ\|g§Mµ4cà;-tØXg\ÕK| õ¤ü®á¾Æ™¯™Á½Où^ÿÑÁ‹Y»ã#ùþ]À_á"ÆzÙ鿇…£œ£ÌØXã?S—ôûVó˜Ý"†çUïoñݤ¿-å¦öpí_RÛYô4å:woÃ{6Ç>·dÚ¨Š]JgD¾ca(ÁH±á…ÿà‹K3üÐ~xO ª­Œ íM\íIÖQ 需Ì!l:´¾jˆTÇø´ÐTGÜGw'Þ›Ö‚þñ¾»bCx×8Æ8–—Ðÿ0z"&sÖ³ ß¶o¢°°u eÅÔFš]4Ÿ³ðWe±Â½I&Y 1¢ö&xÿpÜ4S4ÆÅ„½LßïùæOM)ú/YŸ§\þÉÒ*Ñ9ÌôóhK.ð¶Ú Û™á;/ÇE瀨^®£­dSÛ×99¥7òÕ¹q׊߽!ë]ÊæÃØ¹¸®áÆI™ÄHܽ[b &—J§ºkôŠá¤ºçœ;•Á²vŽeGὕGáT™qÖœ¼'í¸/sAí.ÖŽª[écn¶0)_h~§›¢YzÍd’^uóÿ©êXêë«CCàÿÖ‡ý|M>ðì¿ Ûª,j;Éš–Õ7ÈÞЊR>íÁ ÍRn$ð‹RU­íž–ýgkèj(ä-’I rAgmuÎG€%Ÿg·£;Ef;Ÿ¯ÑzÊGËs€ß’| ønu+÷fGÙ èôrà\Ž‹¦‡ÞÑ‹$ÍŸeÑ| •в úK„ aˆt¸œNÐ~¡?æo—n(ß'8-€PäFÜ>Œí¡¯‘M±ßJ r"æ; ]Adw—iuÃT¹ö–k³(“=fM@sjñž&±aú;ÂÊê84ó ¤eô mé-먴Vd/LÀ¼êE tG°ŸœKãÓý9ô(å&XÃQåæî"­å'ùÜa^÷w/Þ'Pz2BÐþ¤ÛãÏÉ,¦Ø,…d÷0?1Å-þî’üÞÚš›ßo\¥ |ç $CÔŽ~ôyT¾Ðî»Hƒx+Dª-Ôõõµw† âõíÜGáÓ‚Î8ÐcÄÎNŽð"åü?ßNu5ÅöøÁ6 I‡Q¾GŸÔ-¢;.zû7 ~WDÏr[ËeBçIG˜šiwƒÌ=<2Ô[ÜF:' 8½J½w‹÷æ[_ò€;rzJêmêïÛ"ÒÙ™“pÂbtlSWXŸê!ö}Œ:¬G<'ƒ³v±¶Ñ-ìF,ðq&´„¡}hÓC—ψ$ü)œj}Mì^æ‹@lµ:@ª òñ”%ýY$¨C°ÒQed:(,‹e=IËÍ*Gzn±~€Uï¸{Ž{³ép/GëYaÅŽ€8Ú¾¢}pÝÂó@;¼èMÛ¯É×v=BÚâ5Áâ !dèil£ºÃÎd’¹mr8 ”äPì{€ê+´A´ß÷L¬«HôsŽBËW¤1lad"ÀÂ<뀶¨LÄ„)ÙÁ6@›s ‹ j¿NüM¿Kë[§ê} Ú–ºDùìÊo[›”z3]IÿJžqIµSjœ©L6óï% @£„þ·h·£¹ŸKÿÓ ; -¬ ìŒÂ¿ÙÀøåV“üÚ>J·«olgp S¡Ÿ¨‰æ£%àé²u)‚¶cí?lAdÒ|“)Ÿ9èÞ¥Ñfó˪`°6ñ$îäA}ÍT׉ÇDåÉçíÕøµuþ|[@Ø îÌ)îÀ_Û+^ü¯øFÞ¿!DX*ný‹FÚáÞ·,^ÏZÑ~†FÖ³­ ö±#+Τ¬Nç>¡ ú¨*Þ_u$÷¾IÚLô¯ÖIït¦DŽóÂ¥§ñÓ´þ¢–*ùÌæ—ô³ã+b7n5ÔeùvNr¢=š|ݶpšxÒ@mk¼ê@oK?FüÉ"å@¥e"K>mФé耽î”úI”ÝGž;õR‘(gW.ûá׉ߧV´øp[¿dˆDv"­·°%ý¤ÛÃôZ4»„ŸGÍí(Æ5™{iɈkU§òí›Û¢‚'8\±¥iѶ…2A{ø©ð>ÆÈ¹ðõÁ¸2Û(ÛŒE™Ùn+»‚I#Ti†"ÐÞà9÷]ð±Ó¦õ{FN}¦3¥ÿM»¿Ø»Ïüůòhž~ȸ¸-ð‘]åû3 ¸¹ƒ‚óí詌ßn{¤»R䀨|«ÛT_}€Kïš¼óÇV´EZæ/RþYõÊç¬À)ûZt)|1Îs£^:G¥—ýâ¹ Œª¾ÛŽ©:Œ’«æ;á-Î4DzOl[¾6˜Lp ú¦ôÉ…6°ú‰¦Q£¼Ö¬Mì6ö 9ŠuçÒÎ-Ž2F—mäœufAø@*SýÈì ðêêä¶SDecN¹âL™éP½À!w¯ÜêÊwç².ä–sט–E‘ö<6aR­¦|¦oɘÎñc¨ÿÓ,ãš§ó!¾¯»Ìä/V½æe Þ#p%`Û®´¿5lcû MßéÌ£t°t*Ú,¶UiŒzô-eOÿÀL­Š¿‹8˜só¢c˜ûxï]×nˆ^i$EižÖ÷µgºÇümÃØp9š¼2#n?”ôŸŒmMëœnkUA;¸ƒß+çý3ü²™ à[Áá—² A2–æcè-.,¤OéPQÚÍkðã¥þ'uv¢óÒ®¸ÀÄ…³]ñ܃¶­žÐL;Ì"nÇ‚‹’õç2ÆVý%Î'Âu¼¯u(Ìúðèó¡çü·úµˆ«Eï÷ÉçñÌù¥ýš‘Í¢ûR/âI¯×›Å¹±içÐÏóo)ê<ÝÞv`üÔÁÅ’¡¥´0 ï2lÌ'ë‚»;õaîÉx¯:Ü-S«nþGJ@ XíÊ$ÁÞTqe¾åÿWÚ2^§ ^çuß}÷e„´ÒJ÷Ù˹½òÊ+Y~«þ¼% [eÁI³ år <æ…<¬üÂÐÕ–:×á=K#™kÐêø#ž˜š *-¶ >WÉ6eK›r&ÀRn8óÄYêé¿y Ó ¬ \C$›ºZÙßP€ßà> (Xÿ Ñ¿(±«˜€_¾Âé‰gDa¾ñ\÷7—ç–§Î÷†ß Ñ>£YNAÓЋ­#¨ýÙÖ7>  ¦‰¬è%4»¶cdyø]ÂNîäq:í¶Cx»©K}ÙtÈ™lòJ IÓåW8~ü'»¯aRÛY„ß´ÈV4Š»ÃFf0™ð}è]ë;-Ó×ÚÓ±>dVcƒ„ëe´õ°“jˆ?+‹š‘ð¢ß˜ø=±óí”ä߉í E¡tT6Ò|’¹ŽÆÊ™cµ8ìæv«ˆë“ú› €€Ø–÷C.[Á+é0,4/›êàDQ’vo˜t€Í _OcÏóÀ»c²ÐÞO F@<Ÿú9Û¦#X¯†ùvÉ(Û²l=´‘Ò>xšÅ‚þÆýÌSÝ¡¥·/öµëB$PH¦}‚EçèþlÆúRCêœÔ?€ŸÛB¿áë{À¸¡ö|Egâ¹@]¿:ð@Ö$ݹѳm§ôû61ý¯º4ÐKsÈVuto+L—èŠ&€À‹‘ G0(ý™{f—rA|¸=^Û××ÛA“_âL&Ÿ{ãÐb[òûœx¤)ÐÁ§½^+xÊYÍqû•­øÖœÅÖÍJOË>ñåxZeíX~äð.r–DÑ íeãØR9O ˜ôk'Q&oðþÎ689Á´Ì{š°-ÊO²KhE¡É”kPõ6*½»—Ï÷¯Æa`SÈÈÚÄ.·Y‘±áêÅ.AìêJH¿5=ŽÃ@é%KîÊ7õ®ÕO’Ëð_—²úи "õL-p{Ôaÿ4Šñ&ÐgãD³}ÝâûóÛÑÙ°¢¯U°0‹E® ÒíѶ؅o»T ÈÃDSÝ×ÞÆõ<®×â ^‡<ŽÇm{ÊŸ÷J¯úHÜE€ ¥¿dՋ箿ø}ɯ¿^U=I®µ¨¹5v¸\ô~Ø`,oÍVú,Ýž Pч­é»£û5|¤üðÎäK”Ñ8>ÃÑ“lXÉhLÀ5¯[ËÚ'ì*öç4ɦD,Ö“wÈFW³¿E"6ðû§˜>gÚ×´Í ‹>¡ü×òÒ•]ë ¾Ñ§.pÎv%ߨhßð±øYí¥à Û”69ÕõV«SßRÔ#pW\X-ÀÓñ| ®Ò¬®}ÇšUío“JÞ· 8‹ÌÙEOÙCñ-l „§ÁêEÒ‚•ú¡ŸžX—=Ͻ _´¾ÕÇ»1æ.øB ¹5¥e-¦ß/f+zS7ÞœZð´m’ØÁú•µvA¤5¹ïpãS1yÆ|Es`ûµyOƒöþ1[âÌ5¸]þ«Ø—<Ä7¯‡ýO=Ú‚oýÿðùÎóêÀâ©Þ³=â{P¯£™~‡mÆb…@½ú}t #@¤O½ùÆ›ý…†ÀÍ 'Ø ©)6¥öìzÎÊ8™{ʲ’­;þáÄâ“b@Ღ¸¦ùÕÚM]·@ÕÏÞË$Þ¿žýÌO×ﯢç‘ü7ü^Ì“9Н‡¯î¶AµwÑZ>±uégsÇwEÎ{PŸªviŽ/%‘ÌÔi>ðOÎ/E“lÔØm©i<|jý‘ïåЫ¹‘=ɯ꩜ßE;ô¡T™Dé-E_?ùÔšÏYÞ/y»ues `â«|©¾ ¼Ð¦~O=óON‡¨Ê¬ÌÍðÊ·¾Ž‹$†[ÇŠööe³*k¿hm¶ÄØ 'mùüz¡ÀBÖf‹y x|aFXy ˜§Y܆8š5ËȲä'ü.çy}ÛaR[hR'ʵçSã\li;‡ ·Í>±’g­ñö‚o7x2Ò öRÀÚÕÞɃŸ³ÔXqd[ÛÐùViÆQ'—>m‘%Íì© DÏR ø·¦'ïÄ ^<Íþã”Qâ ò×÷Õl^l3›m7ê 2@{“Öoé?­íñËŠÓ‰·#nKì/É‘ô2•SÔ©má•5/‘·î<+¥µl|“IÖ%!ŸÿÂ]uñÝJz‡¹nÆóg OÜeÅ© Ö©dŒ]\9—ô…çP÷âû0Á±EoÀFÔméÇÖ³¬™FÚÚ]%SÉSùíb;IpÒÛº í_ˆ2ÛÚ¥·™ÎÎñÏN[]‡L-µVh’Ï¥_íþÎÿžCû¶°“£´Ñb»72ÓNà¸*ZIQr²Ë„ÆŠŒY$·82wÞ½ÒÁ˜“Ê"¾Ñj¯"䬜Ðíyž‘ãÖÈÇèñÔ+õD®4vʬÈ$7ŽåĨÎÞËqlÄ£›' Óø½˜0‚9ƒBìI«Î5VRæ¼ÕÐŒµêcá úšdîøèón]ƉS{ñs˧\rù ýû¤måå±vcí\¼9‰Zô`ê½9}œÌtèùzê¹/m7 í™+@nàVwU¿YÃo‰s’Íí…”åë|;_h…Ÿ#;PžïãJœø5ÜÓ‹&Çò<ƒ_gÊá`žé‹¤ï7ÆìEæ\Šç´ óÌ(;´àY™_È"®K[ß‹qîÌË”8mLRé›úD›abâ—À)ëªù¾4ã/|¶Ú¡øÍðý7áú¹»×"°vàhq_òR4rƒ9¢ÓÆ'¿íR#9`»Àã{?zlz¡Y]샚JS¼UÃxÚΦÄj,ÊN:ññ>TXåÇÍjwøSà¾w¹>åaƳÙY®ÙÒŠïÄøÚ™<è¸$êÆ;·{Â-Æäö9¤£.“£¹™™hOâ¤ê÷ëÍà×Áméü.^kG$ÓãÙô!îоÂwèºOÆå+~§yŒ]Pœ˜G¹©åæ#‰³ -óU"ÙPÞ©e‹Õ pP ÷ÓhÛvŽ"çP4çùw\é/øÝsüß•àú4œ?3þ™3·òVÑÈ4ËîÏLšU´ü% mhaÊœÌv\û瘘¬lÊ7WÖûŽaRTëO(ôU²Û”Kä4(æ’gQ¾UÏ0èØËwÈš—²ÿjÀÕ{ò•Ke΄¨{žoжdb¡íUí ×Š-¹Sž0èÛX{ÈAšËºþ^ ¯Þ³\ ¯"hëX.˜!÷F´rëO¢Ç.´µ¼vcCi6dâ#7ÿˆYSFMÌV”;>þÊ‹úŠ2zî󂾑]]8¾"¾âª÷1ßð<ªø?û<© ý²HþS$Ôüa¹ ªüøÁ!…. ¶¦ŸÊô]:?ñŽ€Þã½ ‘´vwD@Ø=Ô'Þ€f×þ¸y /1ÙÖŸ‚rxm¨¬ÎónÈqž Ëá„„Fj}ÐWItBø×¾ˆ] è;Žç†…•§&íYÄÖÁ äQÓËKç D©®Õ&D— x¿ÝߎƒIGt÷Ê€¾Þ&DõS€„BT0ÒÎÅB¯´DezäµÄ…˜ èHššÑ¯mKyµ±þø4-†kØNŒs8@íÉð›NiÿMuÐX´Šõ€#bvVr>à/6 ¹B46 “^_w't/?2ÝÊ^n†”@×ðç B_ŠD3Ì*¸lºZ€ TwöG÷Q§êo¤ÿ,€Ò~Lÿ›{Ú½-d?³hÀȽ.xìârìúÊ !°æ4+lf7§ˆŸj›b—U¦h<ÐWa$œªGÁßâ|í·ïݦc›ÔQr¨u–n/€Ê ôßÀwâœö†×E}Eï Ä ¨àÁàŽvp–m=ÀÕ‰•ò¾øÍ´›˜ÝÐ~åß®æ Â~ÊÞ£¾5&†ÖÛ€¾ê¬ýÂDZ¿ 0÷…7oƜċ€˜Ï[<ý‹}xA$à^¯i{1y‹-d{y¤ö÷æÔDÔ*1«´^šô+.$Œ'ŒZåöÇ þY ¿¢”­¨èuwÙ$Ö“C<÷p÷Nãз=¹¹±É<ö`1A£79‚ןænoJÉýzäñ9;ÿéÇI!‹(ÀÃ, Ø^À©æFJ3ÈëMÔËKêc÷àÆwpøgc²«K/󧦷 Ï•ïÎ÷¿  ,Zl Ð[”úÈ+\ÌÙ}`:¼O}Qå0û²`Ú{”Aâ ò6Ë^åP¨uUçÉù–®6µt‡¶A3y&IpYXÈÁP-+o²{é=Ítüp£÷ÇÄ/ðit¶ϵw’Ô>V„_é×v`ñ+˜[ÁMZåqñÍdî;s°þ*CL•XÑ…¶í£x>JßÝ“2ä{«»»÷›måt㺔๖¼ïë€Z¬=ÛãhÞ'.óÂ÷Öø•H¤‰ŒH ¢×»ÇßügƒÑ¹ÒŽúè[:ùõXü^ñ‡'#0T[ö¢ù1.ý-â“ F¶Éæ©€‹` û@IDATû{8ÚVLhf0é ¦©‚ã‡þ£Ô Až³ëä­0.OF)—2¯Ÿ’&hõ4:ëk¤ ‚uàicäò[Í©ir)l—Оd×kY¤-ƒ»#¤]ª u#©pšüŠ$NJ ߢ‚ ó§ |8_à-Í#w²yàà_%¬|)§û¶.ä»–ÕçH;äªôêEßú)±©‘‰å†låÓD/ÜÓè{n¡¼´ýPv’—Fš8jú~ÿM¼+£ƒ¸?EÛPG8A#pkìUBÖ½h˜ÊFÛÚe¶xH*ñÑ4Ð3²ú^—»£ít[Ž0™ûN ¬á‰{Æð,¬}™qÿoT×*[Äõ¥’t”ä`>ýÏYZBNxé@;{Ä~¦Î×¥Î~‚6Áªå%p" ?p/AþDxK¦†2=…VÉöŒ!’þê”›@þX¯…OEO·sà°uû•NÅ&í(kYÝžES±sDR†ŽCm!ZΈ™#ˆ¯Oýн@/7ÛEϰ¡ØÜpÛbW" \᥋hmèÆuA9&²AÐ&Ÿç¯ÕÊÚ¸¼lì >~„œKs{*R`‡ªMGv£^Îñw° ÛK³w{ hòEß¾C¼ ™xíyën€‚²ÛX |í¶G:Ûêhx¯OÙ¤pù( ¸ÀAö]++²rk—Vv{¦5i‹8|k±C}°ÊãÏÆ¸@~&/ðŒ-õ3ÏNFJ Êf@=ᜱôFë&.±_~½*»-¿¯ƒ`¼kÀ¬ÈÏñ¸!¤F·­ÓOÑ7ô`kó8X<ÝFUlˆã´›gv´q‹ö§/é°<­Ì"Ç'Ýi3/^gµO@iÏ>nw[ [ £¼¾ÍRÿà:_À‰¼š§mG?(i[WíVRD£`u!Zðû”£rÑÇä›1Äî,\¬Ÿ~¾-­ ì^$üåø(,eÇLÁÚÅSâûÛÎ5wÐçéý‚7#Ø]Þi}ÊøÒY›ø×,mI>ßt«´YcnáîÔ}À´Ù•ôm°zéîe§F»ØÝ:¤Ê°'»ºJz×H³\¼PNz…|w|+\ÞŠ… Œª¹˜=Tð˜ý“é#2¦z¥Ò6mr«¥-`Ó÷Ø’±6qI‘k¡ßS÷Þ÷OæýHžùþ²íyžÌÏ£ÛØ÷Ü'r*ù=Þ£P}D;yákÔ¾JñjÇcã ÌžÌæj1žrÊAšþ%3¸´Š "¬êòà (àG9”.¸ƒ|Žãþ‰ñÝZü(àZÑ“çnøáì̹&4Àu  £ø$Û6¸pýwšY¯hSLóüÈ—¥3㊔ˆYî5DLÇw·ÛÓ_£ïÑy-TjRcć¹íÎ{“ûKnmNôx²ÏwÆ÷ã½{ø¾m"6—wªÄÍÕm/Üi¯"·Í|nªÝcÃ4úSŽxSÑËðg÷¤ïzþgH‘Ý þ’êŒö9u¨aåv^ÔÌß„ÝUü/{«b{Lc §|ÆàóIÆ×- Æûã>ÀwCúˆlBºo;ðn8¦tæÃ¥‘vvµð¨—|„½dÜF¦-¾Á-å `5Ñ¿›HóÉ<1y—¯Y*Ïu8ˆm–[Tó‚ÊÜÂûÈ»EÛ絃qœãyÔûK»píTüTG:ج;šª_пXÁ´úÍäuu¸ƒ#áÀúßðTü<Ê¢7.µYaŒ5¾¦·æöÿvà÷p;,ÖÏž¢Ÿi<µ¢,Ë0QîúõD‡‘ƒ¡”óÛ˜Æé»£®cn+ó*‡¯é7¥ý%Îì𠥬E£ä°:×È¡”÷SuÏ9wjõ³Õƨ{´ß-u{&„@[Ž»‰lù’cOµ˜Ò é‹L̺›§èKú1î/¦= óçò}÷,ŒH\KÚïàŽ¥Mx\Ÿ ïje‡$ç„̱(Vs~¿ê&‹tøë´óõÉç Þ#ýøä\7ž[qÃhWd>b7æèZHn©ÔSȉ%Ì ›Ø~iø-ým…ñTcjȼUnzš7`~¤ùÐïI§3/*fçíÎ, iGÏ$‹£y÷컕±üWveìg½|cž¦e¹*Ç]§e'ýÓD?»4ù¥•¦^¶Ñ¡q6æ ÍùÆ1ÿQI­T &©¿õ%[G$ꮢU%ðßW¬k¯”L—¬„Tc¤ÉÔ°é«¡â‰ü'‹«‰h@sƒ›¼.諃7–FZáü£@_Ùélˆòiå6¶1î¶!h5ôUº:´J” ú °I`Ÿïî¼?·ùߤ‰Ão% xMYö4äƒÃ&ël†€ØPé†y0wY÷ MÅŠ÷^Τº¡pr×Ä­ƒ¶]7HS˜ç¼ß ¯<ͤ¹õèâ|F}ç}óµÙ½ @_qƒ&DK§v6þÒÒûꃾ]mëÈÎy“Éú*`>ÐWî'Á aêÁä7Ì«a?ÝË,…úðe}å·FôrܼiyXû¶ ·Wn¦äÔõù­uÚ²³Í™*æ¾ÿj´]tøHqD­SÄ: i©´.“ÞõBH¤W ô•¯<ŸŠkû[{Â}¯ÚàkY ¯¢ç}å!->ŸºÆú·îê´ò|—R@<Ù{Ë%mÓø,½”>1ˆ§CS$Ð…IåúSØ¡ûóÑè ϧ³@_ÅA›U ¯Ò’·£;¹1]}ɶ’nDy}…`º©Ÿß»üºè+’@+û¹W§Û8pe;¿ R‹lÓTÒnˆáÓ‡@0ïqX“4¹ +ŸB˜}Ъj/bÛâD‹D¯´‘©%vgrm¡Æ5]ˆÙš è«$º¦îõ@_=8mhÝÄmó&ï³H·}\tüW[+ò±Qe}ÝA%ô ´è&2 x¸$ŒÛ%üÜnøè)-Ó!25!J¡ü&غ®ï€Ãz60RÔÕÊ?²5iϱnÑíuÀЦ¼¯P¡ ôù ¯n³ÛŽF zd4Us‘¼I€,6˜ô?&ÃqÐØ…©p0ã€Oì0n;.OwNcɃÅN³™µ÷³ÍZyˆB  @_ÙY”¸ñ*÷¶Õ9X¬T­ Ji…}Ça`ïKÓ¸¼£[€>=ù‹méfפb¶nÉϘØžûÎF²°r¢Ó ¥V"qÒðÚ½·ÆëCÍšñ›fq5ó±|¼'/ î)Õ€ôe íºyÕ®V§5Ö•oºð‰rˆîGylê§ä+»ù÷Œ¬¥³-Yz-ïXŸ°OöþôÝ)Ò)²¢èž6œ±Ýñ76F7¯¹(bq)è3ZÆmƒ@’Îd:”û¼w ¾ô8ˆ&ú˜2šïÊŸ‡ê³œ›øÓ}+@ÂæòmÑâÞ XÈÙ>°­Q¨rø°|nhOÕÔkÅEö"e[ ÷Â,ñØÍüGÕQpt­ˆ±HÅÔ+íŠE+8žo^Çfcx ïÎ0UðõÙ ¿ƒI›–]~éOÖÛ\¹bÙܦW‰–†v¹}å§>Mn+8–‡ xTsÕÄ7±šh;>÷/È”!º‰o4ÞÈlGÑe¤C½Å(Gª-Q5}åã¤ûq—|‘ûYÄcƒSK¾GÝ|F8ñ*ß#M|¾Òä%ïã}ð! o5€ÕÉ,0¨’æäÝŒ]»0Æœ_~A٬惾DæßQh¦/ÎØE‡p[Ú§ÌBä£Ö8jÝÒ܉öõ}ú'zA‹¦påàÜÊÞ”ËËäeÏßx~ú[uøÎ¼äó#æ: MnG›·PŒyÑ^¸Ž&z;–[RöEpà©v„„û¢Jµß\ª±3±K¬Å%«Ußö“ z6WÊÒií>í¹E¶g²¦ÍÁw"µá£}Å…®—É}W'”½È-TQ·×xÏU{Ê‘úü•¼7ç§w…©)nÀú}pô¿ÙÖ°YQúœÉÆnkö>/<×EN+V6ßëQü\Þ}%eÕ“t»ñÛÉÎ ×/¦ºt‹'‘ê&ôU:‡1Î_æêœúJQ, x¤|µ·w³6f| Hsû[9ìðRÚæSUgò^øºQT?ObþÕ %‚fn1toxz-¼Œ4ߪ›™Ð‡U:sYMàoÜhu&&• ¼ÃD£çÚ°èÑh½{mQ‹E®¦üŒúÊ´‘æžÚ‘$“?RVÑR°a޲€ Øh'AôÕ"îf¤s5e.ÙH» V§-‰òÙ;¾4æ|½?ôþŽ¥/TøŸy¸Ûƒ|‹ÎÁý¾­ yGŸßGR¾y1Fm{³PÕ5¤â¢°³Øá=ÑŠXôè^ÇNèÕâŽs"Ÿâs™±ëà«ÞŸ´»,}5¶Ì(ìæÀéq÷ã@H‹ýÕóegÑÒ@_úžEG#Gf9Ñš\]e9.åáKêæKÙ‘'mWî Í4_µ ûÛpL]Ï<Ê£MÈó@ÿ~ ôþ}ýK@ß/áǘ“Ë\螺| ¯b†yBÀvOú‹º–R?í?…Ë¿™4®¢U%ðßXõ:’ßé#êD°ß)A’Iò+Ë“œÝYZy]‘üü7Ç Á†¾AüQ”u"oÎKÇäx5øø©›T4èý›§öÇ<"RfÛw&7ê£"™¥êTD±í1 Ù•(jÁï ´7F0YO`Ó{‘@×—¾íže X€`îû¬§€HqêaÒ'Íõk|&3Ä…ƒ©wSªùËÕKlm„Ê-Cß&[³.5F ïêqE¶Û:ŒzÊŸàŠm°¦]üº–[‹Êm0wáµ - 1-wô‚vˆÒHÓvit m¼¡~iiñäæÙ ¬LÜhÁÈi…ñwCž•~4W·ˆÕ˜®¾D ‹¶S™üÙ>üÕ7‡Í„<‹ ¶_ôT; QsdÉ{øÆ]ºÛE6´S"ÛxÀ®¢mh£hÇMô ­½‘hR6'&Tsœg+´è#M±lU¾§ÍÁ, ‰/¢¤ƒÅ‰ìwEf(it±)W³H÷ ¨}ÅNçÀ°¾‰k]»ü$5Ê £¿Ò|ÅVaEwçaSÚ=ySú£¬ Þƒ8Š^`;Õ~h•üÍ+e>å¹{žöH“€XSyú)û]éÇH»}™|Âúò5o€M Lòñh=Ší6n½vä@›ÄXÏËýÝ”¿]H[Búš¾û wmpztòf/ï‘­#ÿ†{­}•žL9¾é‡ý–v3 áp¾A¥·%?¾[À€õjø•(Ÿ’º›x‚´µ \ÛuaŒÙ.²¾µ.¥å piž=ާѺªðA÷ÜPç/¥/Ûe%_ÙÌŠž˜/øÚ¹»?ÅŸ¹Ëèšó€¢ðÝ_¥Ó=ÿøÖ<ï`¥h!^‘þѾžEÿÂ'œOKõIŽC¼ðü-$²7í´"ã§ñí­1SJ;z7Z«o ÃâWZK4­tžm‰X p­Wá8Òöøb.î{m•ÔÅ?i_{©=Ô^罇<÷oò½µ”N7›Á;¡ê“yÜ[¶áÄL‰@Näw§1wSáa¶N´· P%¹PËëñƒ[ Ì7éÚÆ–É®6]_¦òˆžD}ІàÛÂ÷ÇŽµHz}{³M Ÿµ4 n_õ—57x²‚2Â.®êv|ÅfÎD‡Ev—Î$¯Gá>”ôžqùhY4‚÷ù½OÍå˜Yáýà6W‹zÑn;ฅ;º´2õzð(‡VeýXY›WÉ÷'nây6qSЃ{J&Ö™ðE”3y/8šò?÷€H_ "KœV<‹?VÆì©Œ²“¦hu®ž¢ô;{:Þ×gb;»(–žè]G×H®_ê¨P|ÝÚNìx‡Ñåoôå²ýªþJÔ™/žšl×£iù½?–_ܯð ;!îhS€4’¦– )zÁN7³w™»­‹âØů“ßUI™:àn߯s±øg}~Ѿ `~¸ºkÁÕ\üŠñ‡g㣱˺9‡&>JÀvý—È/ÙØæc&ãiÚn=´h¸L³C¤É¯w+¼uù-äçãÁ¼"v*9˜Š½V…ñHó/… ²}gÿò ž×û÷êÓ ™¥ ‹…V¹õ{{Ø•{ƲØ^Îh8σ/Ë ‡“ÊÕ·cþ¹¹_ %Ä7h>j¬(ºÈ.–iÛ“þ8êüdÆSÍ¿­bKÜf±¼«úBHPz2_ªEê#p¥ S×Ýð›Þ¤ øNÙ_:ò™Ù8à=õ•’ɦ‚qäc7fsE¶,à옚͎“_íæ7Ú %à2Jݼ @P0ŽÏn±-ùË«ê_Bµ¤oä wÀ˜ÛeQ¶A[ÕçîeÊéú¹_8ج5ox AiZ k™ MûõI $›Â‡’O)´‹Îònhn­-ÿÉ ®ŸÒ–‚ñAÎÚ%z8ÃÒhnÍó|~â¥éÄ“‚D56Õ0wÑn›ø-öZ´‡]é/*¾H‹õ§¹¹`)íÿ3šÊß)Ûøµs §ú:Úâ4æ -(Û….}ç܈?Òü•Æwƒ¤q·lMxõ|?È4î/l0¸çÑ<ã¯ÝH:9K$ùmåLݤùeZvô8žü9…5Õ9¶Ÿ®=Áñf'|•¦¯z‡nôo³ [á‹~J‡»2–@/œ«¤¥s–OR ¿y9îcËvUÐU%°"%ÐbiÊ$ü?—©ÃaÌÙéùú¬•þ)Òó§Ü+ý]¿Ç $dÿ·ÐšTä¶~ç#Ï&2“ï¥x¼! ¯€*̆ÔkKú §2 ÉU@qVVZ‚P@Œ|v…ÿ†®š @hbà½k1Áõ«O:ÀF¤Uþe‘„Ñ\j™ëzþž¼üÞhIv“ƒô¥5ª)Ò?˜0.eú_¡«Nømè›<ñl‡­P’N°½ƒºŸ°«¨„)°?v î›7ºj«¦Og2 "i^O‡ò’öÇ7K©×¶±Q™‰+¢¼‡#|lï¶HNsœ?)X„i`{¤ëU"Û{c¦ËõIf´ÅO£ÌŽ‘>õ„\ÎðÁÔSö-@R@DêN{÷\—ž¸žA(ñéÜÈ6NØÔ)Ø$8I#°„ÈmÕõŸG†êèÀ¥‰þ$¾?íRB«êå"î[a^A‡í4†´ÅµA*þ®A/yPWÿ^νŒ,vàÓÚ¶¯¯‰#ÍyÙ=íI­:­#îŸàKë›E[8ÁØJæÛK},Êì/åÔ-Râ—Û¿Ò3Кp9 ÆãΣ©7i÷Fcð åqŠe%uBûê•[Y‡øÕ€<Ÿó¾‘¶~õ¡î3ϤüžxÌ~Œ_ïÙ~,LH $¯q¯y½övû­àGxG.Z”™åìnàüÆ%†!,=ÈQKœ¾©ç§ž†$4Å…j7ÌÀ¿ÑÎ<ž]ÓÏn\RhGÕΰ¯Di üD)Ûy £¼°è)ô»Û9;º²qÛÛ}ó@¦[ûÈ«¼GO&.u–„_‹Ÿå^Âõ’ú’«O±cÈÇd¶Ïò®„J ª€R,J§ÌQzž÷þÀÓ¿ž†íH@ òg3x÷§žwˆlo›ÐË>Ù–mÿ…öox àø¹ŒVß6ùó=5öM®U÷‘çãÐtZ˶A#닪ã\yuO µ»ô·(?€´_peŸyAŞȒˆðQ_¸V¶…ç] _ü€=Zô¦Çÿ€™Áp 8cÖÞ‹i‘F6ÿÚùºÓâæ/D;„;]{ûÑ…³ÚöhõhÞµµÃÁfëW?nÌÁ»¢·UEO·£lÆú:>Œí³øEçw:Êé6´¼´¥¶Õ’Ó诨[ÊÉ#Æ Þ3wÉævzê=êî:úÎMìåŠÝ;nóÃt2™éù•z;­ö.çö! í¼Ä=ä{kòÀ/žËß1«uš€8n}ªàãÊk­#e¶Så'mê’ûÁjÄË>90ç@òÜ ÷±8Îô<Š^$/;~Wï¹|ëG;pImÏM£|3Z‹l} îË®„{µðÛ˜²±Úg@ðÙäÒ_9œh.fγíâ÷cG¶­Í¥½õÆT…Õ\ê…,xÛ3í 4Š5>,Û®òL#-ôSJÛ˜P°ÒŸÉ÷yî0ÔµYp¶]"9TD[ñpBÕð“F‰H{ €¼{±7%'bdoæŒóí,Úùdßô‡PǧXa(²«¬ÔMþ|è– ê‚0×\‹~¦}ÕA8R¶ÚU.Ò2ˆ…”ÙÜÌQ^¼ó_á³0Ik{©¥¯Ox m‹] /ŸèÙyc Dtó½èY„éc³h/ ¨‹ÀÞ¨Õ\…=çŘyÖŠËšÚšÉÞ·ÉÔ )Þ· ø+ÐÕ£¦þ•Kb0ŠØù°;å°W;}ÆÕ‘"ªä1ÒºŸø“MNž™ñ×n¬8ãÜóñþÄÛ™zyÄ÷kž ãŒnÅW¦ÙQø4<º¾KÚôB€•³ÀÓè—º»|zgŠÈÔR6ÕÒËLË8u§OٜӗY|Ê+™'Ýh±Cš©û£mý(‹ð¦*œîŒØ~–nò¥ « -¹ÓuëHv‚—d€ÜÑäamüæN U{m^à¥v€ ×0çVÉN±v5êVŒù¸´õg/ž÷aÇ@kæ.‹àÀ0iq=K“Tí956dMÿ®'²š`Òß”QÆ5Cwåþr&¬ê÷óŠ}l<ýdp@¬ø&s6Güø‡>]ó–ÄX®uÔÀýs@c‹Âcô±¹”ú‹^,nÆs}28‡®nø2¶[ÜïC|·YôX«q_×v¿áéx·ÀÍl>?ïEn"øSÇ«ZlS¹wc¾uI´9õÁ_´Ûáü6‡ÇĶÇýæ[7³SÿlF®ÃX/³×\œîÔÕ(½f¹œcÎz#ÛŒº“æv˜úß]yNeì¨H:™+ð×çêýÑû’ðÂ|´Å1LªÛí—Ò¶fÀ…IæF$}…8¿+“É|¤É½;z ÂK¬rÂáÕžz»‹ï˜ü„U¿ áëZšÑβ¿%£«|Ë¥‰[YTèMšå6ÕªÙmQ•4 qÀ ÒxCl\“k Oj7í"Þhy1BkÐoè^Ôsîµ¥±:‹6 ʹ0Ö7;¢å‰É }á¶dó<ˆ ;¶ðâ¡Ý'qn”4 £'¢uÛÞiøJ ègêc¸ÀáR}žW8 Wõ På NØíþ@è ~7±ýºÆ¾”°ÙçOÔÑ•-Ðö(éJ‹YíÛÊ×u[c%xVP¦?# >E×r©Ó¨—è(£C˜jï”B¼]½4ý¿»Ç/DÛ¸‰ÍB³V­hêKøû»?˜P½u¶{jŸ'oÝÐ[(8{Ês¢‡Se¸%ÇN°ýb®¬>к%_´»J±´ƒfv¨`ÆiÒkÄGxLpqÝ´¦/Ã|Ɠ𙀉q*[GlO瀺:¢¦¿ã"­àŠM¹l@™KƒG’4‰:ÞyyI 2Ùvøÿ€û9\Û!ÆiUðküVÛḼ»1·çó;Ïš$äžðÀÓøH€Öy†d›”º|)z€}=ÔD÷`äl Hp:¢Ÿ×¦¾ÂØÉ'×8¡Ýmé­½€ûgÐÞÚƒíâìÁÂ'íˆÈÀ¸¬iÕlÁnå/Pþ~‘ßÊ«m"[T¥QgU»ãú?ú5ü¬<ÈŠ²¬ìêbȦÃgžŒühÑM³Ýuxj!ž´öV\ú‚ ë´Fc;Ù¼ÈÖk£Ðj#mêV ÝÞÕýó™]™…o¥½Š©+8ú¢â‘öaÑM@(ííLiBÊmÁ©¸ßŒ´öÞ˜k“D£j…O¥>àn¶ÍJà=Õj:É®K'ì_·ÒOìÔÔºnÏ·NoSÌ–ð}Æ´(Up÷]\¼^pgLÜöñ⤘ÿÄÆý&¢_„[{Ò'_± ¸Ò%ßÂÿÀ­1öuÉÊF ß_¨7+¾ž´Ö'þXÂïBÚ£ìú¾ƒÊ:Ø‘͉KCßµ6±Ü a™îS“m8Ú{7ÅzÛøÈVô²óN=Šª¡1·é‰ëí ˜ò$Zl¯S†¥É´u.þÖnж¦)2^¤¯O¿=[Ú«µC½ YÕnËO±»Roçï±ý¼míp«”¹ F«½`§©uS»ôé[úÂ/Ô³¾ŠË†söU€=‡öY䘌;1€}’NC’ÑÁw‡¼RÈ„«»¡¤dž#L²ã=#ìòîS´¡%Ö„™Áx-pJ×¾ÝRÔmÒÁ[H:P`»#ódõ~ÛFŠ\èµÙ³?ýçÚ”ýÎM–з…{„|öັFÛEý¶4pÕOê¬=?¨ú`Û€–}_þ ÍÚu(Ù¥Q!”;³z7ÃDH†Ø1’Ô˜UÓ›ôúÎä§„2HvTÃû“Œ<Ïñ\Hk{;46C%›¸~Kæ›Ô ¡½kÑß;lkS›B^,ÕYÉÙEÍ7Á¡8²§?7k¶Ë[*áó,Ú˜²þM÷™Ö‡ôΉ6q¾#X|Û%UêîÏ€¿´h=”¾Nc­L´#óDm0g2›1IvúÿÆïRúŽðüþsòv5v½Ï%ý)h–ëÖ€EzRûµh¸­E2‚ÂÈ}!`¾Gð†Ošs¿â/^%OÒr®£/½[·hAßàÓt–àÞt5ŽfDÚô®M:”—·XÈ;jÿáÇ.U´á½iS}ᑺ¼¾F»T*ÒÊš»Z¬¢± º‡yH†CR÷ðÞ"º*ÙµàÓ€ÖàÎÎ?£NÆç{~i—ÜE€°X[¶þÔe ï7!g‹¶mBŠF¿ñU&`^Ĩ’‘ŽNc‘a]1¢_VF» öu|d¨Ïñ\½¿:„¶Wzº{É °²D/ÊBu«RÒûêQC‡»)íN<¾^øU| ´‚4©_E| ¬IÙÏý”}1Ÿ^ñÇþïþÆ8)Ö.gªôánË™½?<ø®ôÍüAUCš€d‹óØœ•ÁÀOW ¿—©Ù½ÔIµùH`½³ç•ÏóOì¦ÉÏ‚ß+^ ŽcÒð\¯m;¢[”{á.f-`Õ;4è÷zÿr¦£ÉÓxV܃Uú匾ÜÁ÷cšõÒröÛÓÿ³ý±¦Òç2 Û,]î ú‡&Ô¥± ·Ÿ×=.åNõÖ–†Ff².]Âeч E9¤ÓÃ'±½y|0‰–ÍÒŠNn*¸&aßA` ì&çDÍz0ب“¶Ä3 ”»-‘4d——šA‚`@šCJL~5È»ïqQâf»Q‚ZˆWBŠ&ÒN³•giØÖãEiph2ïˆö%í¶hg•s}·ú•¡kÉ:;~#q®!p¸÷¬‹+à;¼på7’6;%Ö!2˜P7s¿™w\Íã/§|7Ú¢˜Ô‘ˆ+!Á³ ¾Ä݇‘eßXšMb8Á»!êNK D=ŽxmS¯©aÍLÜÅ?s£”j 2 ­´=«H{¾¢µøÍq`£w¤+ü ß 4r H{|gñ|0š‘_!¼ÏEp=m°íÆ%ðCâqóðuN3ȵH"­d¢V _£™§-­G ü^ !’è.®2góûÎúÇ#¦ì[”z·Õù!˜Eæ[àž|–Ÿô‚ò¼5Òž-Á'SwW A]Ä¢_ep½…Þ'l 4 %xÝ£v2¶#-JY£}hØÁµŠ“ø–5xÇ[¼âÊá4—ØŽ/`]e#û—Wg!6'äTÀ³ËIïvîÛòû(»!½(I¾£{r¸žgWÙólÂ;w$½ñÎAø“Nõ™äçc@šŽà!ÃùíÍ=éEO%Ü¢ýÄu" U,Óo¸:ŒlK˜näÿm—?iõ^oFÊ7rvœM‡–•P¨šáŽçMÞt:ŠR7€Vk²´5—&Ë—°{Iá äå~+I±öh¸žO?6i:h øyøà/€Fä.°‡Šï¦­Œ¤N³·›¼c»-VI{ü­AË%Þ×ö)¤îÅ‘hÚöá½9é$v0[YK@Ùù˜\¸µ¢‡}ÀÁP›%ï°—c'Ú›µ*¶vÜï,im•|ÇœD‹HÚ•|J»vÛéeW´ Þ”y¹UgáÎlÆñCS²~å½I_Ö¬ê@ƹ&ö ÏÂ9ÛÍuIçÜEÉë^4Ú“¿X¦¯tŸ‘*w5û,¼~ ¼õ¯œù­LìÂâñšäñ)'u@^æF¹JÁ o)þÄ>ªÜ ÐÙä»n ’‰šSœÍíé7#öÂf ¦ù«â™ˆiM\¾vÂô•Ÿ@_bÛŸôU~nFЮ´Éãw"h!àV¤ÓŠÿ•>}DC¯cºX”jÈ;˽Œ§ß úÞÃ9hW2g°9_.u"'íùžÆPîL*k‚€-ÃÕš{¸dWgBzSâ>Ëš‹úÂ-·üÈ5}%ÔŠT®a¿ªÜ. MîeÏ- °Æ‡Ü*~$hŠžÉ§Åê|òÿ9XBˆ”ïô Ú‰¡Ê}%.j»œN@_%3 žÜ’Ÿ ×ÔíKàƒúêê¹t|}eŽÄ5…ÌdÇ @ßy¾-_i}¥rI>â@©Oª™É.dôøº¬q¹€¾r_˜d5±m¹«eމÈD¹Þ4ìÁºä•¶4üò@ßñôGCÚ+=ÎÕ ¡ úàu€ÐZP¨l{ôLöû³9$J¿·"U¶³I/‰P‰”‡û4®è¾`ww$åÜ›-ü³ROà=Ü\Ëfkú~¸9³ ^nëlÓIûñ¿·n²} í}…0*PØâw9ñ;Á{EÔŠ¾saªã€§a“Pýhòz3W„/@_ÚMå:!³9w!øZd'[Pú¹mV¬r”Ù’Z'Ýxíw+âHœ œ°Wl‹*޳ÉMÆ;{ºî¹œzK¿Kž2ë=…ÀfñÌØ$°Ü6#^G±’o¯]Ò.Š`ÔîÿPhuº¸ÄÏ‚oÛ÷½«\[†ÜÁQå¼{&‚ÇònòX®p>Ÿ}2@ •€"à‰Óx±ÊÃ,è7´põ¸v Ž%-f’%ÏØ¢¼´b8eÛÝ…x¼9Ó.xË)È[êÀf˜¸Š0› À_g_§†p¼)šD^ö³Éì ¸Au¡€§…J aüf­¢µ¹T>ññhÇ{);L"hZäæ ØŒvÀ–ÀF¦Õ¾rø‰¯˜ÃB÷³ˆ–°ù›áro¬ˆ°›òMO¢ ÷ %ÒŠ÷Œàù#EòIµc¸RWWPïﳸ‚yA©­[ü¶]Œ «¿Ç ¡=€næÍ _Ìï{hÉŽîëÓ´åˆîŠSDZþä`<5™ç¹Ò£Ô#šÚVEÝiÁ¢„¶ï…và·¶Võ¡^^8ñ~úÊ?ß&Î%K×P‡zÚeTØ“Ëc¤ e8“º‚§ Ž ÍQøÅÏ'i#kd寧µ'X਑—3°Žã-WÞîç{X¸˜b÷FgZ/•!Ë.»±"ð6 §Eîï·‚-Ò‹Bã–æ›ŸoœÛ“àjÝjéil™¯mcÉÏwN¿ŽoÃŽsÐú‘世֚ÂílÛ>²ºuŠüd k/vqâoßò ùô[Ý3™§=]Àýš½ÊŽºŠõñýÌÙñ¼8úöÌŒwTTIŸÕžÌ&ò0J?¥‘[m&L€¡¨­Peªv ]ÅŸµXPi‚]êÕáõr¥®v!OÔeMO÷˜õèz$à?~C=gϼRØL]6èÛžwÓÿdÑ·TÀ-u.1¾%r@|…]’üØwÿ•ò½þÛ‡¹I™U`—Üõ£Î—vë´¥yЂõ“M‰Ðã4îé7ÑFeb@„É K¿DÜüTÆÚ×C¨?ÔÁŽ9S$êsÌÞ <·síÙë{*qè+’¦úòŸ¬îzާ˜Ÿ½Ž‰ÙÖO]]}µ³XQÐ:¯€£î¾) ƒ'µ€¾ù L1=¨ñ‚þÉ*÷qá‚?2½0#Ö‹ü^À¡› è¡ÒÎFì8¾G&´ Û0°'m¤‰¿!éJSY;Ÿws´v°¥çsãæûÛØ½ŒÏóy®[xÄ>/ßÐ 3#ÒR¾€{sÀäó1&µ‰€öÄ=³øixñŸÖƒRÐÂKémæÒÒî(˜F·¤èßsI}EÙzjU(*„ëx x3h{~¤Zê•þd© od{XýMËå}‹gC¹ÓÚì8)åà<êßç?ÕÝÛYùôÂË”Ö^>ê Õe‰ËOéSáÛÒ.É‘“à…Ë2>?gîtC_i÷Ÿ³•mÀãoQ+:B.L ‡ ;v²ÝÃa¸O$•Ü@ UÎY% û‘«èÿO äfWÚÇkZ¶,’=ß?+¬¤Œ1Ü[ØðýJzÍŸ6Ù_–‘³sÁÈe„ÿ-ÞèÔ˜¸Úr¦\±a¿ð}?¬0Íã!<©‘_Ɔ”ð4lÀ HS•{Y™_ÊÍÇ¢Pbî4zÿYàá²(05±UÃsîeYéý'ý¥)±,ÒáX¶î„¬§s&WÚæïN¦o !®ËKÒö‘yƒ|Tîoiû,Oû8áÎ ±DT*íØ ¯øW‡€'‡Ó¨˜kûu¼ª½P³8l+ììJóÀi‹ùºü¢å¾ê#]Ä–¡—\åÀ-ÏA§ewãAÿº'RÙw HüZƃ„$ 4JÄ¡¯æ|¿ÂI‚}²0Éžo°µ.ìÜ è hy¨Ë…„1olú'š ¢0@Ýþס"á­j Ó¿æÁPÉÉ…Mÿ©)ü•€×L.Õ໎Dp¨n{jý¹õÑA* )¹p^÷àºFjŽ»•€æå|& ÀTÞ¡Ì­Ïò‚Åo÷®‰;‚hÞ•­ç'"ô„ûà ;" £§Õ µ¶Éâ—€Ô…IÞé¨8¥v(‡™¶ŸK³±jOÛ‚üˆêJ:GD:Ùý¶›!Áª³YMU}ˆs‹pðÒç”Ë MtÛ’-®¿ØÍ:=ÞÁ­ÈNHÿ‚†o¥4¿§­Ú6e⢅6%*DL^O¹€àÛ k‘«èŽû~´„£ûRi탪Ûm:[í7O`RÍ£@\ü–¶&À0‚¨¹]òo„ïá4þ®E¸9á3õ¢•F¶Fø§þ0ÜùéÏÓ´ø­›‡½…íÐoíá4ùp¾bI{û [¿Z:za~f—"¤¦»Q¦˜aAà’½½ÛÝöÒÖÄyÅ:/nÊ—ÁA±A„ϨìÎãÚœw^LÙŸë ‹¥s/f'ÇL[#.èì„å»ÃNG˜MÍÀŠ!ØEÚÓÈHSÛÒ«îv -_ùœ®º“–­#ìšzšÁz¤q¦*hÃÚÒ °ÈK¸ö"mñÂk<ó%5·nEà]Wð®]¨±´í–ÇóVšÛ_-=˜C‚¾±›±»z‚xIé@%@Ï4£¬¥‘Wv;O—p^s"ž/Á°‹;¿p{po8ðÇ3@ñëÑ”½ÍÞ®=E%ç€ø³(ƒ-ôê5›Áomøä>¾c÷kQ´sõ©d"7Š™rïÇz8¯ü-µØüYlg‡‰‡ Õý9@¶w€_ö&“ãÉÇ(¾sÏÑ-z¨ Eý@\"ÛqÐÑîöPeg¸ïIÏpÀÚ”•4¬yÛ®Æ0Ž[¸qíåk/n¤å ¿q›ì‰ÎG#ùjmÕFûø{Ëhªo=Ï Ý‚ÔåÄQ[Á;ÒxðÎw¸¾$ÚÓfFÛüVò¹“ìÊâ1ðaàQ«8“æ´ÀFbT ÈaÔ̹Å×pƒ1¾-5çoá¡ãxÿ§¶Nüh[Ç-Èð ©Ó†å} m(`ÖŽÅCùžs±åÙ‰oü^>˜:ÁS)êkÞŽ*ºâßú¹ˆÇùá_x.ù!Ÿ:0ÐðwDŸãϧt(—£rò[qù;’Ç͉J^U¿:”ŽCâ:$¿¡tÿ¿ þ~ÑD»­GÕíuj+!’íþòгwË÷§fpû«mHn‡&c”X’ybš9¶/÷ÅüøNµOìì:-hñI¶Þìï±5ÐÖ3A?e[ÚÉ,Þ ïJéFB#!y©FY>B˜…¤»KTö•,ëUbwCGzÚ‘s˜¯¶²)iŽycÞà5ÓÏ“žê¥ŽþIÿù¢À$|™K:øÊž¹PðGåvxdÏ­ƒ=âWZú GŒ'Vx·ÁØYh¾Âb™Ú•O¼b5Vºƒ“á5‹]ì<çðW|èÆLúhU?V‘?Qâ(âN᦭{tÚÏ€Z¢¿·Ä-™Gs åuó s€ò;ÎbáXk•þþ'Ï9tªÚŒkÁ(ð–m«—†µZ £šÓiu“˜7%œ-íR•”TÚmíïÃØ¶³òD݆óí±Õ±Ï½µ-N\~ìj,¤>cG³xrLt°3u¥¾ÝÑ\ÿê]4GíÄ¢÷Òw»÷jþ2Š9Û­¤¯Ã¹´ çXj½–v(ê Þwùþw#]ìÈèé”ãh»®`Îu[j vmÖ;ôp2ï™K[Ðâµ4“O 0ìÍÀfwTíöRW ¨ÍÓ?XÿŠmᑘ?ÖÒOFÕ}*|†› ø¶{ÛݜL»Â=•þNË{>Úê`G#„7£Ä¯J7G:Í d%·[£íˆ›ÚÅcÒÚ0%A?RÓÏìÇÁ0=l“Åj­[WÀ¾Õœæõ^\㸻ˆïž@Þ'ñ<Ï޶uzcKËžW2Íú $ó·¥Þ€fÎåðjªæ«‘WôoÒ$ýŠˆ«ÖØ‘÷_fƒ8tf4&êvˆåð„€ZEçùÒ#•ÛâªúiËï꺳-ˆŸ€S/tüN@Âc ÕQô|¯ÞJ§n¨e~íf5Äóœõ;=Â3ñ“ì:¾}@´ÈŽH.´qÕoq°Ù§Å·7­²S±+;?¶®Mfûû¶‹Ûã:›_>¢_ÓRR)<ön+¶oÿ\ò¸µ*ÛÜF7ý̺/9 MØG°CÜÊ®J-¤\ɃÕô´‹ßµ.1õ·Uc)˦vpÑ™6oIßVb˜WÛS]ÓX7𠘌Þ¾>i–N±Ïkn²¿TßFßH^cxÇM”Å.Ö3 Dv°þ‘ y×ýnQ¸v­¡Î¥-Èâ„¶÷¯Ð÷]|0<±£í—ØÞZ,ijó=k UDd‰Ý @Q P&ž-ÖV{&oÓÆ àFííÔѽ´ í8÷áØa¶ídOû›6C_aÔgÕ¼ûDÒzŒD*3 é««b[ZÌÙ×Þ‚o¹„ü½n;¥§¢áõU'®³Óª'Øý©¿‘Öü:£ÌÎHœd£«ø–ÒE,2D¬¼ò6{<õß³%aàõÄAóùK½L<?Cƒ?‚9އÉûäó5Ò#¾‰/k¡\‰ÝשÑõ#÷!îXxìÊ42¦>J]>øÃwõ ¯y÷ÉøÃï>݃VïE‰lž[4ÁŸ>nàßõhÙŸ@F`ql8ÀØ€âÛšB}UFöu#ìñO·ž±Cl¿tS‹¥žYbçñ½7šÍØXГ6º?ÑfÔEÕB`– ÕÆÕw,•h;‰óø^úº#ÛŽ9Ö´é$xó–ºG­§TþÀfîL1lììN=¿%œJUÍ/t]E_Øg„Û€öîh¢œñgŽÛT•…Ü&VBœ´Øš œÚºS¬eš˜8™p62q<ü£µLáÚ@mžòŽ– ÷ë?´¥ƒÏ1›tvVs9.]|•Y5}¸®ØüCuŸíªO¨vÓMÛñÿ͈²íðý–yоõ?9SHî;'”ùè˜sâ+æIçLx0NeMpD/ÓçÐ~ŒÃE]XK)Qt¤Ü×zaöNð*r*v‡k/àIóD[¸ë@IDAT¯ØõáFcrئ±¯»ú»Áï«‘çáyÞR¸ ]¶£çÅé$àBDˆ¿u2/qç6Cµ„lO«M †¦±Ä×ôݹ:d?3 ®áŠøæ›oŠø.ðZ@’"oEþ5˜´ôýóm0ƒ$ Aß›4è&œ†"ÙLºßÚ¥nbª“5o·a“®Ø‡Ôо9й¾,>öb¢—ú*äûÑÏlH3 ¯À–ºßrSµ¢Qо øW¾Z 4åÂ%uSo›ö×Ç%šr:2»"ÒP+‹iHzœ{Õ¾ô•gl#Ž«kx¤)ôè+·/<¯³HrÇÁÃW-úß3Ú­¿„IuÒÅ ¯;ÒÌ‹K# †#£]#›À+âñ €Á±[iUüAÀØ/ÿÊ$t. ¯ÀØÃRå’}(™Žò‘F•R «/D'A_õG)Ô—†ûú&ÛTÏ¢¡x œûéÃ# ´î_"n1'-ºÎbQ§šh±Uè$‹z±®­&@_Ñß-*Û¥NŽ©ÜŸFkæY‹„>,x††aÊÂEÒX:Îɦ£4wd{€«±‹å¾Ú`9~ð€@_9¥ À´¶2³Vµq%£I£½½áÝgë§¶ô yêròú€xcoŽ“gVä‡Ù¾‡ÍÄsk·3¯öK»ÓÙ[Ûå[´bǯ۩Ð#`©‚oh¸œ8Û7hiÞ,n™µ"Ï¿ñÃÕ~JóŒcòãfvà9ÃG±ô±¢û±é¤W¢¬?ã € ۀі9ûŠð[ú[¹z áº§_iëֶî57+Uçî«9Òdg´{ðPèQµ2dÆ}Ì3²jíÖú§B¨Aà£sO”<Àejxœ¨9Ñ©`{*ØO¥·­Ê ƒIh¤neKg6%õ•TÂQ*ñ™\Ù¤qt€‡§ì׺‹Içåð]æ(»àã9ÀJçÊáåÔÈð>ú_g=1a0*zšB›_kûú®^ù4%¯´•ªÇÙ3h5~‡ƒ#J†SÇ+!ÈÑÖµ†:4[í$´C»#ÕŸ´ð¼7ÛÂö«‡—Rûa¾ƒ>\v2‚çEÑà“ íh}@εË⤱lq><´‡K¯ŸßÃNl8Ïm ®Ù0-ÌO¼(Ó’Òþµ^”!æiEkÊ{&aÜh§Ü~?Ó~jP?¡Ï”ÜË»+MÒ[ØMhzÊ<™æ:ÛUH˜:{'µ.夿 ,âú!¶äZ庩ú‡}“>ËÞÄFç ïµÅ_cÄ4=å\=m+ÍPç"Ù‘}+°°Kö)¼AÕ\à—{üSú€a¡¦ípd^Ó ¯â#»R´AäÖEÂueœ’)“sBÛbjDgêHͶ¬þ§}Ñp­ƒLCÂT0~÷¡É}Íý6^¥³ ú‹óiá¿Ô®¤½“ oêÊtL^·°íx` ¾ÇÁ=ô’ñ¶uÅCöh›œª86x‹M‘ïl  §N­ \Ô&¯¸ β}š}y©ÙNǺÁ¥¡¼gídÓùuHÃ{¡·ƒWá3>†ØÈ90º†^ãÙ£šc¦Ïƒ½èûÎý^¼Á|7`95¸g)¤ˆ^9Ò4øl²´ï#7›«Àá·Ô?Kž÷j¶vŒýL÷­ÿÙ¾úØ¥LoäÜ4§´Ñ‰p1èk(5Ìá‡Ü%£¬p·½¿õäCq·§£b›¸#6¯_­›xÞÚnæôË?Ùø;ŽÍ õ®­‘óSÙ$e´Ê:ŠçÌEÁá”ñè}à¿ñöeýYM‚¾ ªoiä¾È©Ø©î¡SŸ˜Î–šÿµ²{1t?™ÝچТí°Çïìߣ÷ûkÅç6Sæb»EÙD›èæ)òìC›\B_áþ-JmœuM‚¾ ¦+Ìtm'ȕҥá6cÂùŒ{Ö?2@fç9ÿ üOƒ×?ú2.¹S&Q Õ9ýõEÄyñš}XŒ¹®_ô…t»iÌQÒ—E©¨ï¶ÜíË}ýš£C7ÒÙÙÁc5.›ÜlBÚ ©,îýÊIf#{ùå—_ÚW_1¡ú/s™ÿ²ú,¨Î ü])Ð:9TB²G2èÕÍÍ B<Œj’5?.>-mÂvÍ$°¶Þ¤µê¾ðM¸Yüî —jÍÄúk_ „eªšuú Ê=7×mˆbîSÊ/{¦Ÿ¡•4?î¾&À²bi…_*.öæßãwAö(Vãü4¹j‰{”㘱À¨cõM¹Û#­½ïik†7c^§Å¬æ·K{çÛøŸløº1L¿Ç³Ü}ªðmãg-Zå÷(m. NËŽ:V­)¼@|ÙN=žbê–åìxž0¿½ü…øo¶J¤!¢{}Í·ØŽ?ÒM)@.¹¡ïS‘ùƒÂàšDkq7'z!;Ñ­I¹R¦b§þx?mütW«Lç§úÊéKÃZ„Šd|0šv% Ú žB$CdWNv±c¹òDž6Ç,0¢E‹ÂrÌ5þèŽ4aøÒ„û¤HN°‹»9ó¥1Të|>u ©à¯n€vÆôW —æ­ìBû÷%åVÙ)x9•ºGÚª“l£9WµR"Tw{²±£½:¤Ý¬…Ý$èqOTn…_ÓßÌ&7!bS"²©ž]ç:ÀbqôÚÏñ<6zõq}5\nm-[Ý}ižËíšÐ˜v-ø7%Ü)Þ4=ãLà‚Uõu±’öÆYU°¨zÂ…q÷.DG»Ògão 8€#§ñ[‘{«w8 ö}À®­h·)¶KíU Übp÷4í&ÁS·ñ~çˆ÷¤w`ÖohóVj'›?VC »¥cÜÑð0ùUh÷º=HœÝ­ý<>š:¦îDû£áV~Êo·Ä c…ÿuôŸÊ° ^×y\¤>–«Ì=,„†oÏä@¨Õžj;I›¿î2ÂÕ$øfŽu¨»ÓÅÕ¿K.°ÇªÈnd hiЋú9';«µGRšÏyì8¸Uèÿ¯SØ:“\8çí„V«qû!¿ZÒþÊv™ÕÕ~ß¿†&a˜ýìH/•¶TZ_G¶Ý—Ó{A—¹@Þ©u ÑÎ,GñühWÿKÎÁÿHxLv 3v¨¿Šõ˜ÓíÍŒí„æp¼9w8_°ôrèH[óÅtt¹Ýß…÷©Ó]:6zJÅÕX)8ÈmÊÜÅi§½,mc´–¯÷·²OÊic‹©v¨= ÛE íÌhü:S–{\;)lâR†C0K¡w<`‡ÏFà‹ÔïëB3l†ïWÈèßìV¿Üz£y»:l£Ž£†§Ã<ÇlÜP½‘Ý?›xÚD”y”†©yó˜Ïe®@<‹ Ý&ÈÒ”ik|áý†hžãHãÙ/¤Á`´zÍÛ#}<¼™íG¿ÅF%Ñ"<íœÌø]ìªòÃf©«Ì^”C3¹Ì1¤CÛÈ¥×FËú}»âܬV”÷¤ðÞßÇVH‚më©vyýĻ֢9©éøô0›€y’eêg±MÒS{™åƒRÛf.§?o )Så×”ýfêZ;q­»›úÝņÛl»:Õ¼®·-‘1ÎNïì]í7om»³ƒöéžÔªôÇ_­=@êG’³ŒoW¯}ôië ìwº×0:[ݹÐíKÒØ4j3h“™Àý=ÐxÒ_Ž4f’/}´òS€ðeˆs6~SÃt¼d.ƒVï l‡Óî–ǼÅ"Øî¦~ZàdŸTòë4äÈݱ<—©…z@5x.L–—«×*ˆ™|¤Ý¯œS?Ê\G»Rv?æ7ŸWôeo]~k‡áÜfayxý.þÞéJÒ¿ 7„ôà·¢@Ù}L.|¹¾WhZj¯§CƒÓ€Ó=w"ãkɸšÃ¡¾o•é‘.ðë³»ÚòѦÌæVN£65 yyOÕ9¶o휀ùØùÃ}6†³×™“<œjc»²y†Tu'|¦»! ¦W–ºgèZñ#w´Ä0Df¢íÎÆñÖ^Hǯ½  ûêÐb45§¯¹±& šýÏi:ÐÜ£_°,íw° ™³‘3×°¶Æ:¹à3lq¿éÖ*’Û÷Gü"‘‘ðé°x” ÚXÞŽž7–±ntÅÅx÷°œ¶;8oiõ>Ë:ÍÐÁx%½Ìmâné…µÓsO¿£=ƒ}j¹q5;Úô•oSÿ`£ê];íÜíÉç«’!œˆ(EÞbëAׯ‘ó{W 1Ù‘•lîIÍïÆ¤Ã5hÏðÖ„ð4Nv7¤Ý¿×Lµô dz1káú± ÿëß\Õ7;"“ö+ðÏ=v"oZÇV­Þ ÛÂ_Û­Ñ/"{÷bÜ>@X›îÖnשĮþM»¿j›3B? ¢Y(óÄÓ·Äý5Ž3—«L§þ¤±\h< ?4ržÆÑHÆ×ŸB¸‡óƒpBb‰úóéãWãß:J¾! #§¹ïwñC ®Òæ?,},tß½ ôÂȺϠëÖø/ΞÑ0™k²{»Í‘©|¤ŸìUrWÜIë;§ó¶-Ww¼;=sT0y¹F"R—Ä}x;9gd$Ù<»?þøÃÞÿ}Óqëÿ&WDLÿ7UoA]ZH\÷oa„A0>×ð/Hõï“ä Í‹8ÉÉÉ ùuÚ…Ö/þRýü¦óÿ¯©¯¶Æå‰)W{Ìã5>j-m €F±d´«‡É 9»˜Š/ºWè&átìj~&DËОI—µ+—ô,¸—½±bN»ß·1½‰?Ž%3óÒWŒÍbiú­!€¡.i›´ÁÿTg/Žöׂ°n£7„×®ÿŒûœ ¦G±kWж±áÂØ_W`¹Y„š…ì•ûx^¦ Ô2ë2Ñ–]4ÕHáôu]¹á‰Ç£ÉêMµaêhB2‰+{ÏÅÑ¿*dZ½”ÞÎÆeLë8€;f?p]'±h]ÛEï4éíƒvJlRAÞg ¹—BËæ¢hÑ¢£‹úÃÿh&è#™PÙpÄYôðÃ#º6°ð0ª…Ò(4Lä¤1½+‹NC’!Û¶.ÛÐÚõ[ÙÆŽi?‚0±S›½ØÙŠc±s,k¸0~Ì»ÆuIzJKZy5åÆE ö›,$¥õ:„öh/Ÿ£M¹¥´eò·DÕOšZŠHVöCÕ« ÑkÔ R‹À³% ‚)|¥&+µ”ûÖ”;þðÇóhÌê({È-¼L¸Š}ÞÙkF]Ü’ˆcÚ—àÅÁ‹Ãª…dû6é¤-<7¹«V%Šw\Éq€?â~YÄ+VJ^T³¨t ª¾,föva­ôN»¨á-îIÍiöB{()»š–¹#²+»£ÙÛ£õc™ÛM—·©ƒ‹Ñ›®€?BÇJsì׸³’M |éüáS _Bw8c×”žéúV=¾ hiö`i©öZ”ç»øR÷ƒéã횺“xy#uNql¿=š]mÄ™;™ºðö%ö^“î±8•gúb>æÛO%‡3½`Â!Ò ü¯ígòs& RgÙ}ôs;¬ô%{ªÕŽ‘“­Ùë—á=ÿÕF{C©÷-ÐfÀ¶¸¿ˆ –¾˜0Ø É%³aÃqßôP;×DGœL>”²-p²þ¨‘ž¤ 3¶a‚½vêLâ¥1'¦VìµOÁ›”Iˆ«=…°#¸¶"µžaŠ,e|qd¼M'ÔJodc#úÓGX½¤ò6¯ì3[­z[@Ÿ‘a\´Ð SVBýʶ dU{Ò{Õ–­¹ÉVª½ÞÁÛÛÖ>IØŒ&%À œpÇ“j{‹ÿ“<ßÖªŸD[³ÞZ0 £ï¹Ns« k¡·ùƒý^7:´ÕŸ>Ì•‡`Èã[¯þ*6çÞÕ#åx¡ºv7¦fØU›SïQh…ÿŽIòF{Îè‹ c_t”ÚŒÉÊi©,iî±EÃÅ„»šðä_ à6–Ô?vÙ¸N ²rñÊӰ嘉ð<æØ—¦«ÉËß& _?º¯cûÿÑÚÞ¨˜h'ù¡³– ߀ëƒoNS›¹O½\76|—ø¿7Z½oÛÕôõÍË<âÚS_#Ðѳ†w\¯9Àå^?,ãúŒ[×¼`×=Θs?)1 òq.õçô½àvêLYp̦¼Hô£ƒÅ_‡s{³-Rö÷Ù–éÑ|ˆjY+ ¿$s†}[ñ˜õÀ¦¬ú©¶¯ôzÙª­¦[›Ô^Ö>r6ËN¤þ;ѦwAhïܼ;•4ïÛÍõ¤ï¯G˜ñ$%^G(ɹÜÿnu‡RôU¸ÿ: €¯w'þÁ¼ßƒû~¢®j¯E]^ Ÿùcì@¾#çïå.ú¸ Kyž§Mî ýæò›·ÐŽmR‡Ø€à#»P^ÆLB÷ fÉi“?˜ÿz·ú; q—pôqa0 ä\Ýé\$· œvç  ò;ß1 UQ_ëœ>9ÿ•hŠ-ëRr³’‹9 UEÆYûòmˆâàT¦cáÍ þ°Ý±q,ºEÉ‹¾†›É¯3s–’”•qŒõ*tµ;Øéþá>%˜ù\cXÃóŸc²ƒÛÈ÷àõkäMʳ÷FZ|m _vÔÜGm«Í6L2ÉÂ&ú¹:Ù¹‡0Ó6Þêƒi«zeÖþó…>~ˆÓG}·&­ZjpÕìC¨Ïè0võÞÄý– Ž6î¹’ÿݨ>Rxó¯ï÷n@f¥Ï£ƒíJi'ëã•ðôû”ûRܹz_èùG6¾Ne½|M \ù_g-r@*õ‡ÕÎb^Úsý¾}Žvën;!v ìyŽþlMúö¥ô_r™HŸKŸ|Þ?CÞ2…F©mNçlDúi ¡í9ø;WMÕéŠØ5\âîâ5¥ÛÄÓ¸TÁØ‡ÓØ6µÅŽö;Ø_ËFj“Ñ9Ú®aJãèú€eæ²Fþ:9³.í·oÆ{);–!§ì6\¾È†/¦”Ê‹ÎÙ0N‹×GÆàÎb̹Z‡ä¤A¾£|œ\ÑGmãІ¦)¦Ñ¿†P÷éN@qŽ£M5o©ÓF…æ¶¡£âRêeÿä.lwI8ÍŸfÀ?qHz\5Ÿnúôé6yòd{ã7ª³a|ôoiN0©~=è¥v…ž/¸lÜ¿ ýÚÂq{“YìßN†Ÿï\›&;¥…½z®öP0¾g¸¶uÏó±ÂjÒBî;@‰ñ  Sûð;:/gÇz³lÀà £ÝãxÚu•“¬S½hPo—VKž½Ïx±3ÀÒ3Ò°—2ÁÞ¯\Wøµ¬þ~Ì!võ0|H ’è¶±lT@ÚuZÈú©ŽØá6o ½!ޥܯk÷AÓËŸµêòÏl}4 mÖâ´õË\A½^³*Þ´W«®ƒ§»Úö oÓ–Ÿã#qkk‹TGðK'ó<•Duô½ªÕñ;:œDø¡ËáQ9WtY›¿—M¨ù$y„½†z«ýeƒ}Y²·.›­ß‡±²ÿuʤVOâ½HrKƒR§w,C±öŸÂÈùðfê˜ð>u<×%(ç}Ðöt®´•Óä¾ÿÜìï1ÿ[…SVñþñìj:}^O÷Óâÿä×äÔ†á3´ti§†p]”ú+®¤ÞÂv cŽ~üª> G¿"÷‚úƒm ʶJ´…’nm>`ùY\FÚc ›r«Æã0ZæÁ’e¥Op…÷’ 2tÂ³Ü Ìs_B£ í4¦5Š]ò04}õ>±û°7¾¬›6eæ¢6X#8»ø>€ÌÖ¸žs¿R…§ˆÑÙ¡¢d‘ûÂñfª‡^šOurð®†#ÿ¨€&¹ Íçél‹Ð¯šsÁ„½yP_»†qáiÁË´s=ÝÿQþ>v€9„rϧÌêo}Ë=2§ŒÇ9I †ùÉ<Âpæä#‘éÊ6¶3)dÈËŸÜ©ªXÉýÈïÆæçéÿ˜ïüF[þª¶ª»–r|nc Lå)Ž>ìƒÒî=0üžô6~Ý–µXH]…ÄaZJ’T_Èûà®ìëÓ.ç1î4:Õ‡Ì 7C\ Ñ¿w©Ãžîþæ–*}›äë"÷2“q§Ý¶€×ÚB»ß³a¡Yײ®æF¡|%«á÷Õ#2wC?lÎy½™Ëöå»3¢P\¡A#çN9Ð/ ÜW´ÝtÊÞÅK£$1­ôWˆŒk‰ó·¤D2CáÕ¿Ø‘‡V¹»bÿâ²¾›–'Çfñ:¿w†G}¯c'd[1…©±^©ÝåN5„¥QΫ°¹"3vâðGP‰>H²5úñ‘¤@¿bïø5¦×šü÷Ò„¯µþÇõƒQ^e€È¿´\m‹È¿_ÿÝå‹Ã¿´M“í·–ñ5Ú˜XeóØÓJ²÷z·|3ù£‘—VáÙÙÏúñÅÑì}2ßÂ{l[ñɆ-›¾c)’_˜¾ž1‡Qð¾sÁsóñ‹¥)?€ß¿$¦Ò—ÿ»þ’ÿò<šËÿ¯z‡]àI]–ç׳h8"•çÏîtPYжâ;´;óÂ5WF ‹æÃVþáÞ³° Ä{¹bÒ!@³³Q<Ž_5òk.o½+ägMEÓØrê”Ìs,4;Ú«(H³›¬§|Äõï…#¨?d˜° |¿`júðlœ“„‡ïmÙà}¿]toÚב,Oòô¼ççˆËÕQåìXÄ¿MÎ/}9÷ËÛY» «ñ‘7 °ÔpÚ†«·y6ì¦þÁ<¯XÉÝ‘´ö÷Ͼ_—±è>¿_g»Hº„Ák”;Õ„Á$øCrá_õ·àb¯s°P3›.˽¤,é€ÿBüBz°À®(78&}´/}š0Jcé`e¾KÖ;J'5*|ÎLzUΈÞW÷™ë©ÇÀ€Í¹ Mê˜ o|öVåíUY•£~w•ñœÎHŸI^â±~Á"鋳aÔ,uq·¢>{:ɼ„l噸¢ŸÂyÛñÛ’ç½yîÆKŸ¾ã=Gyñ;•çEB¿Ô±\éÓ%÷7fÎ ¾²aÕÇã9€£%ô¼¶ò›`|ê  ³¿SÀæG°¾·iðiŠžGÞwnÑ0]ñpz ùìvþ’ýCЂe¤k[§ú…´XŒ:t§Ý6-¹'8ùw¼¦6kíD{Oté|Chïý¡+*Á@ÞSYb:{ð<߯Qûw žöWú"g—„öË^ >ðÅoa¹bþäh6õÿ ÿä; ¶„þÛûàß›:I. ¯+gqí왆JÇ?*ÌËß#|®üü7t÷g‡QçÛKãþFøÔ鵪 {[‡á+¾ãJ?ñwwéœÔjNоU5~ÐF¼(>æw>ew÷NÞÆý¿gö=f‚ÝË^ .TÜ’{¡ÙuÄ_túƒÕŽ®ÎËã‡L(y z¦<.}ʬºA{SÛˆ·Ñ'6ñ­ëG uD· Kv­øÞ=ci:àÔBßDôU^™›Hga—€eLµlämOœP~ÄùfÇÒY”ßÁÈ%>*<‰léuº¨ßEe åO$§³~*×2aºþ>•>…×8Ñ‹gÉ£¨ì®iÆÙeáãŽÁvþ~\ÛÙñY|˜ß%ÉG™[¡ç¶Ð«ÂxCømh,«MuØÈÌ‹›LÇÝ#‹óü\S¿ÉÅS_x.µkÔV݃雇ù;¸0Eç9âwÇϹ4Lu({5èQò0ñº$Ò_ŒûÅݳæfêÇ€[‰÷‰4(“òc„:ÒöÞÚä³_£°{ýƒ[+>ÄŸ±1u"aŽä^m§Õ{ú¢ú¤ëòç}ùû\[€eA¹1†Fc[qŠ×WwãW.] øØ®«Ã7~7®ÝÈ3ûÞ?4‘:,÷•GõѼ+ž¿…áË…‹Ó‘œâÛ M¾ÃÍËUó³9)Æø<éeÁ¯~Û@ùbî*¸“>Ödþj÷Ô ® Õ–’Ùq!ªo~¹à‹ì|!—_~˜yñ¯$¿HVÍo^ÒJ„UsãIÂï¯L.iiþ ùãÖVþÎÐu´“w1ž§ý4OלB÷l4gù#+GâÀ…׿;€RXŸÏÿ?Lº€îº/ׄ ÿ;µá¹5oü–ls>°ãˆä¤£ˆæ€šf§â“£–•K“lvú³UcwîéHP´.äÝ[˜¨úÍíù\T ƒ]Õ&ã&Øqz¹Evñ2£…Ödzqÿßט'š*g оkT4l*^쯅·î5‘T›µtó Ž_ìʱºFåQ8‚_w+ð×D ³¨Ñ†Ú[|O3¡jŸÝ(è_Œ'ÐoJXô^åÌNò'@¤©Í>—ÉyÏ‹Qtkœ_Ë{—Ì#¾g7?/ŒÀ54Šòüâ°É«€äFŒÞi" H÷|¼,ì“L0Í:Ï5=ÅÁFö\ÃiÊŸ|$Y¶y½¿‹þ­¿[RŽbñ³ n憢ï8šŒëèÂñ! ,(›¬#à‹èæÀf2d¤¯dÿ~,ªóŠ®ílõ:ÿ;=A—æzîYcƦ0´{Ôa\T‰4´Å ½˜ÓÓCƒÒG¦›:îËãå>A9Êm`h™ÇÓxOù=Éc↲$̇ú’_ÀV×tæê`¨×ÞŠ@?K_áüEë3½•ݽÂMð–ÈÞ‡éäx yÀÄÉO¼½|ff®o2¼$H³Ñxè•¶,ØNy-ØÅî¡™KéP@ͲhByÅéK¢0ËqÍ>mX¸‰·ë0oÀòŽÁuŽþ=Ý"1Hu'ϳùXðN«Ÿƒ”Ü0Ñ-Ø‘4ò;)k îU•]4ׯĉÈU}{ÄsbÈ-è?‡#J®NMÚgÐ>¤ß .~ë=kü].hM{`²$Q–©ßÅÁee“ƒ dþdú… wƒðóäÌù4_g㬄‡‘¹œVÀÿKüÓ„ß=ة䮠?ÀŠË?E¹\Y4§@† K_N¸=ƒ…¬’qgñàúÔ>x±x|ÌŒç=ò¯â‡`uú†x½ƒÛ¸P™á{ÀЬ°x„ç‹‚o‡ h55ØÇß&Ø=}V€Öi”D/}<ß/¦Uæü“c©Ê¬x…q®Gfí¡þëKèf½ÉûRúÈ‘ÐzeÞurõØ—¶zŽöTܾNòè éË(ûÄÝ"L³ôùàÌÔ^ÁZn맬b€€XßàÆ²'ƒA€÷¸ñ6ãzlD'úXÅ„9ˆôbÈ €ÙÉ£¸ÎÐJ›(~ ·£•„é·ªWú<®Ëò£ÜÚÜÈDrKï´ñ¡ô¹mΡîKÁ{Vþó éRÎ=m«´‡ŸÆ\>2$ 7=ñ/{…wí…wñÄ+þ!a%wfÓÒX‡>³ÏÛx½ó€–8ÏF×Ô(âÀÃÚ0I—Ÿ çxžM–Atðs~oÒv‹3æªÿ],™%ù®ÍÂ/É=L ¾-½>•<° B2Þ¥•¢´%“^çç%Z­ó+Ľø;¤áiÞªnìŸó®Ì~‘°Ð¥r6×Rz°”?§±Ôñðû3Ù45'ÇfnôL\ú™úÓЦŸB§îædØmÎÆsyk³À• ÂF…d›6 MÚÌpã\a—.à•°nq]´a¤9áâðÙÖ©còò»ñPòmŒ·PÖ_s¢£6ôöŒËGÜ¡€Æ{ñ+mÜSrSp$›#˜»ÊÎUÞ¦æ‚*ÆJÍÂT¿à˜,Z@Wÿ\T޼ñ0ñ^Àó¬ÂÒBˆã'µRcp.~×üµK6æÃ5_žæâÞjW«ÍÅ™ÿwLŠ 8éKUyÇ`ýü—ÂY©¶N MmÕT:¥,â 'm¿Ïi$D4’g¿; Ìz|d«èFB.¹«ûý¹kWÀ–¾Ùr4•Ö´íŠÔ#ŽŸÔXWln"*@óN£5Ž?÷k¸{8‹Ê­…ÛöMÖS sÓiUò.^DÆš–Äm2_k&Þõ÷"¬€§|ñ@ì—“_ùaâ÷…WÑRô‚_€N€ÍQJþ‰_[~¤ ø É—O‰‰Ni´¥ÂÆMfBÀ‡‚º  š¶yáâQ¯mHo0šÃ«RË#X îáÀ@Ë[ô/)€ÏZQËÞ¨=M]•EmÌcX3·„yyBQÇñÞâÑXT楃½•w=i ¤®,re G^é ƒå<ê€V9ÓisJãL \;@Ülø÷*ÑŠç D+ «ü‘kW@ÇÐë õ~ôµi–öð£ž8h€ºÛR·´¿áà’ÇòãºbË=òSœEÜœ§tw `pvSFÀ¯?,—ŸÀ&ñ¼+C¿œTžP‹’D5^x;æüµ9…–5æ%ðƒ2·s 劔0„'IBèTö²“Óš ì`77NÀÇ€ÞÒþû2ù•Ï)Èm® d•¬Õ¼^›`æîdea–Î„Õ½Ö“ÅÆ§0\« ´d½• –ßRgò EñÃ+å‹ê£gÉ[·ñ–bú ϨC~Üø]t­øŠ÷ã›™s§ß«msq ]Ù[‰ç‚øyåʽөõ³BEŒ¢§›¢44^8Ð9zi\’VºÊ³'òð¼ôÙ8v2?)Q¨såÍ•A~¹y;<ì´ýI‹M¹C½U‚MiÃÂxâ iÇÊ;’ñ'ãp˜´ÉiŠ6öoöª±)¹yç­Íœ 8äÒý#‹§¯±2–í…eÐæA£6×ø8./-ñy1å’\þQÝ$Ëâ1'Êk76‹ÊšÂ²üégÉİí•+J[kFõÿø½6”tŠ,~nêª1Y'¿kFù£Ø»©ô"ÿ\A’ç@IÆ_p_œ®ÿº¸ã_ ûwäÙÒ<æ}êÿŽ-­Óÿb8M(°4šHüE|—s£¯4Qâ0ÿ rK@Lxijå€Û¼ q}uŦh¶þIÿæK1PŒÓܽ&5ÉÉNsaÿÊwÍí’KãMh¬K.ðË!úbÉ=ÇÀˆ&à±æ\®¥×ä&˼ɴpAÒÒ|þá´xž[>ÒüÒ®rNšIð;öoî*°¦¹÷ö]NS2×îqš|pÃå-!TÄÿúéý 1¨Â½Àœ$8ÇáæòPØ–òSs´ø-‘8ßøeŽŸã«ÌiÄ÷ºJö„š=BÒÀã9þIÛ}^LfÄñæéšá?1à6OqemO Ü\]ܽiïøÇ²7é/í¨¸½“þÉ{”ZLbo˜4;6J7«Í¸ü ¹¶ÐZ‹ø‚ò5ñ¬…šÊ£ðúÍOüG¥O~I,è‹å!pIfJ$5÷‹Odè9ï$‚Ó¬£Œ,$¯ð·ê³ðOèŒq ø°üIÙ6¯}Ü•¯ü£F´æSVóŽr)œúáýô7ç MVô¢=¯tåJjØ Ô¸ ­Ã‘©#\8mbIžë´è¨: ì—6V|ÜXïUv…ɧY›À/}íÕÒb<«5MxV¦,Jµ¡§#•ò“¶NvŒ0ªÀÿíòÉA œÓf4øFñNÆé -iÒâ„Þ¡_ lžÆâk‹9@-Ê;Žë@[€P7­ç ß.ø%Õ)x¹ìxíT{‚uS‡óî‰\~QÜ¹ÓΌے Aù#ÁÞ:ÂËæJ8èå6ÏÞLÝâ2”+.Ÿ®lŸÓK¾œîpØÛë…ë\™Ú“þ%~÷–®½MÎA”¶(@ÎÛ„ïìäõöuüKŸwô幯òÃîôt º+ÃâðѹNKY›z¢5‚r×°ÌÈL€Pwï­ú£-¾ŒÀBµàᵘAÈ™&éˆ_ÜvjãmÑ<)Œ—™¦£òÒ8`”üÄÓAz`×ô™î}çèò÷£ýŽ zŒÿ35„<$Î a\YæF§ÁÜŸòÄâQ®hc ~^Õ:8méü®Oµ“ihô,é©?L€®¯¦v ‚ÒËr÷”iQ@Œøµ|*é­¼Zþl°Mfqµ D?7•=ʽ9ÀÈXhåmâ!{U?§µò®•>EÙrqLà5ÀÏ´AÒG‚ÒÙ2Qü*À{W§ ¬töñ^@¸ÓØH#ú®J»¿Ì}”—õ&ÐYò‘û‹âW·©òtñÖ£<߇y¥ÏÇ:áyÉÍÐ…ñPaô°DÙ–™ýC?%™[saâ°E®Ø+Íç6cT¶=òý‹ÄUþ±ŒÊ–Gã´H°&NÊÄ•2 > Wž=•#P7—q'’W¡ætXÇÎÄ“œƒ†’4® °e3#Lƒ¾àLiD´W™S'çÒo¢ŠûlfTÐÆmQ÷(œS0ÿÒ>š›„u彨»†¼­°%¾*Lß™:Z±±¿ ×#ë¯zåÇ åüö´³;Ÿ—n2x…¶ž'{œ:¼ÅK¨±ßÜ®E7àýÝ©ûÙtç–Füþ}ø@ætÌlž>«I“%É<5¾†›!„iÊ2ÞÅéÎïUùä­ýtb@§"ziƒ ÄÈ/òsWitg&À£æû'ÃÞ¬á4Èž$ý’‡šIkÑüw’çóà¼òæ=¿ÏÎH8oÏò•Lq²¤0?(ŒÛI}>ù^§ìvd³nIwú#lG`Ô&NKp½ìF{õ­hN˜d’Üý€’$È‚û\Û, ÅZÄ< Å]|¿àúŸqRA“Ù˜ûWµÇ_©½…GØ/˜C>k èÏjS¹ ÷üñ¤@©b@C1 ®¶?%vCµÐÕݘ¶‚lGÛÉ—½ª$à¡ôµ¨,ämN9Ï`±—!;Ð7ÓÄáU¾–ýŽ GyÄ€DœfK®¹EÄüµOKòh*LKŽþ7÷¯öב(ñ·ŽÈ½ˆ×è¸4ïuDT ,-"´pŠì6üŠß’ ‹4ã²'ÛQг$¤I,$imH+3)â8ÉkN$l;ÙùÍ׌›{›º "w\¹Gd‡´qÉ®ÐÎhãwÉò´ô>yL²¹8¢sVS;%­0è©#®nñÈHÏÒZ*&#šL;ZÌ«Ï Ôo2ïõN l§%Ò¸þZøH‹¦hþ,n_' i¼DiÏÛÔÅküÞXG=KãËiÞ¹vÌ•Qü,;Õaœß5•gŠzæÞµ ºb‹Q@ˆûÕfƒÌ€äÂÅù¥ßr€‘Y 35<±¸k¬î LÄëæ…²©´ÌŸ“Ú µwp `Dh¯ò8À ”¹YÀ€íª# ×MÁÖ€D—ùç–O‰4ü àÃë­3ìõ»;ž¹„¶žâwÍžtÈÉöÁôÒëƒ>%Ò ŒëÓÝ݇fäà$íµì{s6c¾û–£¸í°ÙZ€Yf"á"àIáÝqv¥A}×ršÕ¡¥@Vñ¹¾Ñà6œ’@q"?bjÑH_x,Þð Ç=ƒ³Sû|4®(Ð`r„·¼¾G¶ôÖ)š&Ëy4Àû§]ÞËß‘ôbÞ û‰;Ž™Ñ@Zj˜µNñS½´òk€ñ¯¡Õé4>1_¡x²CîÌî8MH€v´ƒMÝÆ@ëðÞ?:mÞ'ëï­ú¹.QžN{Î?¼Qøq©ýötdª•¾‚¤—”»êŸ7§ wøh`¥/’q=óûíè°pÐGÚ¥q»êD‹ø€ï6ø*ÔTôDýˆz½Ü–‰x·{]ܽãk@úî³"{ÕO§6¶—¹ÀÍým²åttà9€Í˜)Ó0úžÌu Ü–¾"L?¢‘L¡ô"í3Èë!×…á)mNþ¦ÍU@o™9R½1…±ˆóWB‹o[úÉ!©cƒD ”ö$Xmɘ8Ü1ÄæŠ+‹Ë§Œrn\Qr ¼D{UþÊ;ú†0÷¸ømU^è.W?ºâ²é  îÒÓ±niåÊTG¬á,Ù¨8²Eœä#åíäé–!pâŽøÓG½õ¢ð''x=‚}dî!ub˜¾“7”Í"€ßß-ôwõX6qƒ”}ƒ#JræK²õ½‰°êÇ-ùµia¸\Z²ÕÞÈ”yé”Úq `úm^w_Xi¦æÌ:(MÆ$<實æKä–µqý`˜›—F}1®«Tãú¨´á$P>]%ƒbó ƒvõh+ÞíO[l&zƼáÊ9— %u²­mŸlÃí4w–Íõ\9‡Êy¬ÍÄ$¤Äåt¦u"Œè>“22Ñål´+/Ë–ZSËdÔÜxA²¸³“I¹²eµŸã¼Wµk¼)›-[ô^öícma|õY%‰ kÅ\]4†É Kò»*ÉtŠ­UÂ÷/’el|Ÿ[Ã$6Hq4Ò\@íìNO©¿yƒ³éÊdÛ 4ÆvóÜòÊ/^ Ó|ÿÆe̽W?oîýÿÃ;æ)fáœMs§¬¤ ß"€·Ö¹y›7‰ºè„—döý©£¹^®.¹9Lίâ±x£§ðžJTÅYü§K1b.ð[@«<ðŸÁ äÖ¼µC¸#_<Ž>f‘äëX!é7¿÷¹Evñ¼ãt¾&5õšÚèèÝhÖ|ºJ/Uq>-¹ÆZz…a›šÌ†ké³èO(ãoÆm žŒ£…»l™ ¸Ojî&û5ñæNËbq›ú_KAïü4bá1*HüùïseÔ‡¢ ßÉ.jnr†MÚ’. ?7Þeï›—¶´h‹Çqüø*[]±¦¹À-üŠñ“@á$X¡üµ (4 te&#îG1(ç_ÕwÜÄ4Ffÿó>ûw¸jRÛ¸Mš«C4QOm ˆ¶q’6Æ:rÏi¬_í ‰ustj´Ùä47»5ÇŒ4KŸK„àòsL[´OÓ½*ëhiÌpn>#Þ‚U½5Ö«3‘Ç eÖF_Î Û\ùÕ'ºªá‘äÖÄmí4FµPøvCÙS.½«Ò'‹¸…úv8“½Ægw[äî!L¤m›¾$ʹ`µÔ‰ÎLÞ€ë…vŒÁ~sÀ‹&µõÊÊ+€hÂé\;H‹ÿ™ Kf‚;þ.v(]z/qY„zEù†Ú‰=ê‡a`Ñ)m½ô•„KHk¼+ÞVÃÒ£²§x,s=ùwŠlÑ—góé*pÚ븬6+ 4r\÷wÏ‹ór9Û&™ë0C±q÷à·‹«—´>Íú; 8ި͆ò~ðÚ|+ä \V{„uM%ŠrŠë€WµsGê~@0pßr€JxŒ” Ú"ÎK4<Àëå4[_êhÞ 4[zžŸ çÞenÀ$à Ì!Ø’ü¢"¹r ´Zû¶üúðëÍo…¼ë•·Éé@l·z]›«ÿ¶hE¹wÎnh%ôÛYÚ­Î=ƒõ¡kûô¥Nî_2‘xý\v›è³bP[`.ý-›®Ó½.zF·ì¦à.ŸðÞÚäõKèïž Oy:ËÜÀb¥£²ÇÀmúDjëàÞêчŸ:SÇGq)on'ú¶;™!›§Æ¸§: ŽÓ+ TÏhÞ. Þ @V`‚6ƒôŽnóN²JÏ*¿ ðá µôß xŒŸ£MýhæÂàƒô®Áh0yÐç¤a]úlpeåwaújÏ*ƒ¿³»^ã!O §ÙÒÁ©™‹ù@à÷ÁA™+‚Ji‘ªLê÷Òr,Ä}WÇ¥+:îý2¤ ¯Èìˆû@Ý:.Œ´²sã˜úïÖäY„T*§è«‡©ÝE+ù VYMuŸü9 ßw~KÑ×—`;O_–»OÆ)¼={b¤‰—_®™ç,ˆãJhÛ‚°« ;ÚÓVúV‰^Wg&Gæ²Gº˜š¸Ï̦?=ÕêW6vi MjƒºôÕ/œÙѤ{p rúE?q}¿mXñ"4Îå_ÐŽôvc@^¸E]ùTwŽ´Á]ÿŒí]ç…lÍÜšŸ¦Þkó‚vQ^:e"À6/_wÂcûèƒZô]§É–]á¥ôáx6»A¾[“4³›¤›oPÚUéÜHc²NÎ45‘â@#mtâäÊGý‹´ƒÆü—üU‚Û3cƒòN¨$ãæî· úöó¤ö#¥_¸ŽÔÆTKæ×N&ýU¹N»¶€ò꟫CŽ&ÿ^¿b'ÐÃ9 @IDAT÷4ÿŒO¤èƒÏÅææês2õЙ¶J–½Ð®¯xCiiÓ%^¯kN›Œ£{䈫8—|€â<{ì±ü<ÝtÓMüx, À , À¿“»í¶[£ìš“[ÿx°P´/‹‹øFµç¸°-ÛðCÖŸ…²‹;#òa2b×ù‹Ùš ?gÃÞ Ùi+‘Æo…/þ¢g€{È격±µz~*ÛÇ-¬§"cÕz5ü¨[Cͦ÷ëyˆï"FÿЮ±›‚?ì#k°_“/÷L‚¬›¥ì{¼ÎïÏ84ÆH'°¯æ±¼Llö,è¯Ø^¶»‘I¿•>K›­DÛIªÈ•R—ulà Û±ìE;¼j`èüï“wùx>»»|½ÉØ‘e/ÙO5WÚ WQ®*+›Õ—ùr¼k{V­dKz‹Û£Ó˜—é½ÍÞ‹:O€&¯ñƒÖõWÙðô±v~ÝQ´Ã¡ÐòUøŠ¼ê'ù½ ¯Ýb×}gÖÞ~&í;íȪm¬·%õ؉ðËAÃav<úJå‡öDÕvVR÷€82ë–â.žñìl•”f){ ¾C³Ú„–;1 QœlŒð¦~«q·êŒv)ª{[S[ aWâ_Äo}ê¶ŒmB{Ñj– œµŽõñûÚð’c¬ÍœA6=}u:È¥T ¯\“>Þv©;Ùµ›®ö^êXêþ|x't åŸÍOk•'>ðÕì ðÿe(÷+¼¾<ž³ã‚Ûí* _*ߤ¾È†jèYÿˆ'ª×$|~ÓHc;ÒëLÞ¹1É”>‰ç›yÝ¿ÞöäqïC§±÷[–2ÜM~üêé[È-ÀnÂ=òVçþÅ(oÏ“sÏÙ».¼ë‚æ$ÛÓ+·±ÁõzÙ~?Û¢á7&mZr—õ­ÝÏÎ ~áMÎ[•<Ô®&,mϤ·±Më®±YÞŠ¶tzO»µþ8úO`š§ÖÛYŒ»n¾êm` ï7úÖ#´WßD:…·´W,c¹Û˜"aÝüÎ@9ÝIçHð—ðy+áÛžûo‘÷ËÚÅÊ3ëJmã²W¬´juÛÑËØ%”or"/kËlå{oùÜšåè¯YT@ý+^³ƒêß±Þ5Éÿ“]ú‡ ©äo.FþúºGûïòc¼B®X^£ß²¹ŸÐ¾ Œ'¥O ÇâWnÛ÷ٽЬN­Vú4 ·þðh‹i2+MºvÞ z]”¾Ú~©£|Á+VÁÛÞðëk1ýÜx¨4~ågÆI ;7})Eægo8?ý[˜=8tj»ÉÀëY× 9µ7¼bÖ'¾YºÔÒózçN[”»2ï»4¨Šƒä®þnÔkè¹iÖOë©u™cL|´vœR0ÎdçÝôD†œKZ›ãÛ‡ûÓ¹ß&/D‹RШ9)‘u¢5cC3n2 :}”½Åœm®N2Bs‹ G÷¹ÆI@›× fÙ­ ~;:/Æ(sþÅ\ž¿¤ÚZEý÷^³17V3 Û‰pDƒq*Àîf-ts€Åi‹UYoÍ$öCÌo‡7üJ¨nþ†`ªµÀ¡€¿Ò-㦽>Ÿ\=4I^àþ%± åÒ^,Ê¡¥ ¯‚'A_=kàî—h«Í¾Š³L A_Ž€*xQ‡-Å¢þòL‚¾zæ+¸º°` ÜUƒ¤|Ð<0l^9?Ž(¹kò_ úÊO@켂¾1?+þ(7k‹De‘ŸœžØi±ý SÍ–€¾hûÄÑŠ^_%¯y}•PÃ<»âS‘¹'SG÷‚šFg°XÝp. ¯"mÍdH€ë´¨] â˜t¡—Ë*Éï1è«€'±éBûpÒ†*ŸkåüÐÚåèÐÄes#—vè« €¾hfD¡çú¢}œ {.“°KR‹/„nfAÝ°ï• ߤ¸ÙÌ/nêoƒ«öuÞ¿O ¯œ&sr1èË‘;·0_7Ê9 ^‘Ó”Q SýZê”áðv1ÐWi|@j=ò¡¥éjY3Ï.u\6ÊÀý‹ßÖô•=ëʆÓz¾6¿²MfÁ,ЭX×öVVý1ã xVÏG¬ßó€Î®-}]Zâ.ƒÏ±_¼NÜ%•ÞÚ¤_ïÞë_jee¤¿6tŒùB`ô@Z¹©a„io?—>ꋲÀ²òi6;øÀÖÈ#Ð7¨ê š¶AÕÃl ë{ƒŸí©šú*­á ¿é:¯/ò>±¼Æ¢é|d2ªÊ>õV#\g~’â÷Ų‹:BçïƒÜM“ÇOäé‡Ü\€0«#ï?ƒæó»¼yæP—çk†Rö©ä–·US{ØóQ}WÀ ñ¿",ÚY,=;™…Ìm©nöª·POƒ=Mµ ÖæÑmÜñ«Š¿° è¿–>î’qÿê§@ä‡rÏô Éâô])áÏmù?);}°ÆÊž`‘ Pò@&Ý^ÿP££]Tu*-8ËæxS³ñWfñ¸BêÈpAÜpþpž×Òý#&¼6>©»×Öõö²}¼oМaf&X÷V_úÞƒê ÐðC{¤t¨Mž¹ ï©ä³tÚ‹8 Ûá ålbÔCŸÍ oìþÞhKhÞ¥nŒ=«|kÄOcxo~ð‚?”k_›ÌÃØò Ð)#|-`kz ýˆß´Ì ® ËRÝá(FÖ´È\MúYÃ…ðôR¼_™ð‡PÉh&>¯e\0=›,úX¸¶k ½Ù[C^ú`ê[$ƒ¬“¯HVAãÐ7QO·ÙRrTR³»VÜœˆ‘´°Zâf6q¦f3ðãm Úd®'>¼–u‹Øâ*gÖµ±ŽþÁ<)Ïi„¤^jcÓWÁ?†‰žºÙÀÔ Ñ\rYúÝ{‡·˜.à.u:ùŒïãÿÚÐè+Ú¥/áw1åŸjëXw6§Êm§Ì0Û±î*x#°ãáŸ-èßÁoËÒn˜5 îBö 2q4ƒ¾¤eeð®dV Cö_É&[¾ƒ/p1è«ñ0½jÛõÞÔ"vŠx‘²cF"u[æ]{ôÍT'8ÁÀ;h™>Œ¶Ã=对ۮ¨>ÚN ¦0ÿ«Ähì5øKÊuqÿýK­Ü†f’W¥—Ó./ÑÆ{FÁ$Õ¢òûËÂS´¼3úÑcqðtõ>\céê<<@ƒÕÐZ =òþ¿=-Í^s÷•„íÀØ+§Ív¹}éçj6Œ5ì};¿»Ó‡Ú¶é³¸ÿ™ßûl|Ö!ë ë,U»3eû úŒ¦m»ÛpÚ)vk×îo_ø™ø1T¦¨=ç7²~œn±ßÓçÙŠÞšš@C >νwwŸBŽ üŒMnß&úlj¿G{ˆ‡|$ gƒéÃ?bÇi1§`s;£È?¹†ÓzŠ^˜u-}Þw ¯îÉ¿æÃL"¼Ò‘Ñ®Oñ.¤¡B5éêÏãÕ´Äkä–Æñ¹¸²àMkU;¢ÙPšCbÂÚv'¼4?.}%`ÿúŒeŒy_‹ÏÞ jm¿¦\/ƒ¾+Q–µíúZ újý°–ÒéjÖBO÷÷JliÖ[j›YüF C>c<÷›Êlÿ¿ g™üß—çß=§Ppÿýjñ¯îp³ÿb’|œ2ݸ\ECîŸLî¿.º&P¹¡~þª'Ç€ &¥óê°IÚ(ʳQûk æ¨R£÷óëqq‘ÝÍu£i†@Ô¹¹ßÔäÞCºÇ,K'æ;üe2w{¤“3 Ðož]É=ÍF‰ùY49pÓ6’ãš²œ°]i¸iÂI“³ÐaïÕþ9Ÿ}„×1=Ž'L¹”¤K«ù¯pZ6ç´H–ÃümPDJ×lì64áiÊqL-S-jšvkj$(É©¤WÞ=KI¦„!H¿¯ˆvF›6vÛsß¶ˆvkü^×ÐÆ°Ç•ô.z¿JÃOÙÅ×a«×ÿ`1AÓd[ïäÔ÷ßæP¡Ó²E ó)éÁóîõsôImf4æÜ3í>ÒÁü„A|ˆ;M7ÿJ§ çÃ鋚I’¼9€«ì¯þRè8ÆŸïU?2ûkh/Öð½Ý™X´=›9Ãn $údOñ~É ÀÑ»{¡–¸ߌdÍ^)⟣ö›6´ÕÊQû$Ó+v\…îT‚Ÿ“¼ë­˜e ÚIÙÔ7èJX×O¹–²XìÄòCí]^oùG1a¯aÁOi«»t¶©=ÔºVnX=§…kÕF€#ÛÀWßѯV¶° …‡OE¾®p«ØÕúÙ-hOeq±ú‹©!Éw\!°!-\Í:­!÷5V ©µ¸c¡¼Hµ9÷ º²yƒ­N+oxÅÆû•y3}Œ¢¾Š?9z¼ádŇp÷ÙžéÕöAæJ».µ$àFƦ´WY°]Õ ºÒH¯7šÛÃX@—“æ­høÎ˜µåù PªÊå‡×øŠ\{Å?Z­í€+¹ú¬ÉÛ\¿ú t¼p~ðWj=è @×~V‹áìy‡ÀaÉn„‰y³Û@Å:{’r|$®âs+/½ŽŒ÷"fß¿i‹Õæ^R:Ú=i›ÀOíËæƒFwÆÏ̹,nY\§Ö`K-ñãñXå¤Nqaâ‹Öϰ½¾³MY¤w,»7öÎ^¬f[BéÄ”²kMÀOàù5§æ+û­árúÝ î£:©ž·ÙÄô)ö;Àµ´qµ•ô|³djî©YæLêx°Š–×",4Ç—ÞmýJ3yÛZ^̵Zg)¸]á}g}k¶‚äû2 _è´ÛØNôÎÉ”«‚'­ÝZUÛ.¥ØÏä!=·Ò}¡ë-výÉüîvJýM6|ÎÁ¼û ÌÏ %iùƒlÿ̱¶eåmð:í_ùZm›‡›±€¢k²hþH pÚô¨ôâr”íåíaýRÛ ­ž¶ÏëG¦Ø{3—° þW€§´©·£­’Ù˺³È¶šã\½ŸFÛûVUU?~‡Ì ì¥Ú'ìÀVoÛêuo‘®æ8Ÿ8 ÔÐè_ÖßÓ6¸­å¥ƒ =Ø åWÚ»h¼>çw±;½ßm @ù=f/G¯Oóc–Ó0޼·ª ÁÊçlk×Öž‰–m/~nŸ¦÷¶²97Û%ðíRlÏ,Ï6É©H;«œñäs»dÖörÅ»¶`ãHS=%źeFÙht¬Z‡u)=ÜÚVÅ3.5˜,ž¤Ü—Ù´¿1éú»wȢЖ³‡pߊ´à†/ˆOþ‘›Éuò¨äÇé…åªÖæ4ÆøÂ[Áû„Œðç¹Ð9¥ƒßKGes[ vOû–Œ%\wÖý«»Ó^‡w¶’&+ò²†|ò‡Ø¦¥×ØW3»Ú#®á¹`?õ?ñt¾ëª¾(Íк³¡u`ÓìcÂÞR?ŒöûÀî@>®áuµMRÇØ…ÈöãÛK(]ç |ŸÞÄ~¯„—c¡$ñÞ¥/@m͆Ívf¾ô÷Ç’¦N›èÅcÙù ¼ˆìKè‹Â×>²äA;Fbžc^P¾Ú¡Û`û:šç@6{6Ô¦²aq5e¼J€:}öM¿‡Ýʼn¤^Gÿ¨½œ>Æø’9û¡êí)í¯®ÎÒ¦…®dÃéÅY»°±‹¿úXÙ?¹B£B§Ó$â4ý­zƒè­xY޲7LpwV3>¼Ê—¾¿HöéÃðεŸtN=´$²åd{4tÈÆo)UëÓ’> íÂNÉ¡ qïNÌïƒÎ»Ôÿhgù‹8}ê.µ»êŽ':<\þ±½h?‰¹œ´j”|zÑŸe?•œgë °S8œÖ`eÜë¤_ì´É™çÐLRïÚ”¹ƒ6~TœÖJ)¥'3ؽÆŠ:-ÑàäÀ¶fQâú›@à'.`¿$±†»›9;f,²¡Cåˆn<Ó7#‡½rÛ—yKóîK{ züñ-LA»F”¨MSgrß3—Dú|îsyä^莾]µN¾W‘§g¨åóN–¾„ßÜ&r†DOPñ'Zæ ÊU‡ìkþzü6u}ðAÚëiò¿!Z«¶,]Bù+ð[5/øMÈìÛ%·qoц‹2lÏ/éÔ~?3×?„’!ù–ûæŽL/0õP@¬ (°€ÿv ,0õÐ4É7bq àMÚ£-±Ät"7šUh:·?ÿ;G6"o¢õçÓT Ø0ã¨þ:³¡K·ÈdšËŽüÜ–? ¦cù[õå8× IE’ùôZZ}’à—–’é —J;=1qliÜ8ÜU,|öw‹¨Ðg4 ÿHi µÐ15víš45s ‹’6²\-“ÚÚ½v0‹àyjÇýŸA Óm&ˆ‘.®¦X_¾ôÄË‚BZ ÙÚ0!;žÿ–ÅÚ‘ÕÑ/™NxšÜïâç•Æ=`OÐîa1ñ}ãWy>:æuH‚6ñKlÔÚ+ä#€iîŽEtVŸ¸¹Ð‹òR‹³¦œLù“O…Ôûtè1z|jc=ê§g7­òRJÍ¢°¬IÇäÚß… ùX(?™Åþ‹“ª(¦% §¾~‹6i5ÇN›GÉ6ý›»jCšÏ¹vÀ.ºRÜ]H{Œ _Fs{_àI>Ò2èo1Û"q˜G¢í¾š½ZñP¸¨õ‡R¿kìP@äK¥5¶TíÅ6=…®QÙSöSÕæ¶€Ng€›ƒˆŽ+£ÒÈê·r ¥´ËËFÙsúÛðv¿­!Ø8¶i,Nƒ|( ­xÑë~fîØjêd{½at†/„ïî]òŸäÁ¡ðôHÀ˯žg¡¨zm… üž´_òw¥ô«¯ËtïG{@U²Dhϰ©×‹þâøÔØhxœ‹­ê¸ÞðÀÂo¶zpä”®×ÞñI/ÀÉÅØ y—üf@‘P'Ÿö ѨjÔØ’cmŸš=âØ\»òk€~´×(‡êwɽó>¼Ÿäݧö€ÛÊŸ¶£çÚ`rŪH².‹yƒOJ¯j¶æ9á¤R–m"@q¹m™¹ò¯E€¸?,oËxiûÔŸnéz¥_ÜGÒ„A~(|ÃWÔ @@`SͶø]mÝ11Ìû•þ­^>?+÷•S§Ùf cìXÀÏnõo:/ÞuÜý.šmSg#Û +u]/.Ê?Å/êËÞšÔíÛÖ«µ{Ðæ~3½¦-WÇòmD¤cV$é°mëÃÛŒ†£½¹ƒ³Fú† ÒÞñ¶´5<-)a¥Z{aURërþÖ1½†}]{+ô¾œ,÷§ý>µÛ*îµЂ^ºì<{[›€Ÿ•\b§×Ž¢…>¶©ÞÆô$df„õ©ÙX¹5ãòRðÍxò ­Šó‘·ŸÛú³ú¡‘É3¼?¥á$[¾üK2õl94R?Nj£ÐZ=|0™`» ÛËS]mü¬ÖˆÐ¨îm»¶d|ô²õªÞÍ&¥ècõUl nN:¥Öytàö±4à8«?þÚú^ÌN¼n3göåy¿ï¨ëî\—´%.°9.ï´RÑìæ#‹v4`üZ˜„ø¦ìA«®Z˶gƒåB´Ø×^±ƒšjõ5OÙ Õ#쟲6TX•뵆MyÀ·Z»Ïom/ÀÓ2Z8}®Ý•ÙÔúÎéÅiòkõµuª¦|h³;sêSé«`ƒ\„²Ç[Ɇg†S·KÓ_wÚõ|ÔŽ6‹Öq}V}€<²€ -«»‹t¦.ÿäHÇLXõh]Bÿu§˜)©ù]èÇëç#hD›É‰¾äIJó2üªø-LèÛp3÷ ðj˜F°ä@Õ)°ýÚ‘0¯FáL´¨Ý¬f{ü(cÉmÜä¿ÈÝš9Çvª½‰§wb¯‚k'Êþü¨>#G9ÓcÉóòzçö¶Iætû‰S:™•sK‡2¬î@Ì“BÚæ^ºò‹›[ \q}['vc¼~ˆ>. f6ö:Ó?§²q÷®6²Ø,“»ÐëlÏ¥ö³»¨jcmÎElÂ.î¥ìex@¥yÞõ1Å,æ‘BùûQÿ -¨b^æZèw)A$ $;~¦ ¹Âï2ŠäÑ&Ác¶<|>E}ÐérÑ oi6Y¯³É”k=Æé/àÕüZŒwð2àÚ>ôŸ·½Õíõ²´vO"6vê#ÁÛO»9M’ÞÃ6듹Ž<Ž#Èô0˜LCTÉ=<ƒ ±ò—ióžá»è†ëÆË¶È¼óh;±Oº7écûE㽚g}h¤“\´çîÛDðŽÜ}–b€ÙÐõ°þôÁQuÇÚ6€±Úøº¹Óº¤Ý§9®NIM¢<]‘wp>¼¨úÈÍ'sIbYúÉå´ó†Í†s½U¼ÁI#™—ɬ]¼á6]'!˜»ÈiüÖ¿Ùäš~éïOzÿä}D;7÷£ï1¯Ísò¬ŸDXõå?ã–'²x`.NÚüœØ±†KrK(W-2F'äd(¸›ã;Ì:2hÆ—böîÇâs_iþÿÝϼryíΆLÒUðà'=Ü/ À , À ü}(À‡›Š6Ö¶l ô•F ÜÇ‘–—î×d JºB³ Éw…÷Ò`“]§ùu1è;=Òôã˱M&ÅôŸÉÖÜ(Óè»9€DÒiGý5豂›Ä†Óד¢ûŠ ›,eŠ:™wØI×ü€¾|4Ã!EžOÑAG¦ ]ò(žÞñ±±Â îY‹úb®]:(œÊ]è˜Î³HQË…S´ô]ZoˆfJxdÖ½nÑ¿ƒ¡iÒô†&›¼°'èa4`œ6Eb[±"D ¯üek- ÖÉïk4 ³X€La±¦…?VR0ú`ѦÅÁA³âCUö `$SD;€NÀ¤@_ið¢/ä&Æ.b‘° ÖòXK,¾ž]$DèutLoµ¼0ZXµ ôÅ,@f´‹{…ŽgjQTàr- ÒŒûÀ¯qo7ˆúTTôÜŠ…‹6štŒ->©¿Ï^ëŒn»Ù7 ‡iÅïB­j€,rZ\‰ŽÒ”‘öêK{E“<Ýä.AÃ+" ËXý6§ÕŪKN –ä4$CÓ2BåfÁº:íªEúýh4å\n¡Pç¹äQímbýÚ&øh9fß{;ˆ# ú*ëd_•fHdSsZWk¾\ùR˜ Ý•ecÐ~Ë6œUi»×?ÿÒöEå§!è[öºÝéo†åÿSä²#´–)”šà» {¬gøÃx³˜}ðmlÅÎðÖr¶#Osü#•ëdçØ p’Kmí†jž;òûC/¹OÐð:"hãà,icŸÄ‚C2o²Ð=šþf ¢K]PTn-¿}eLDSi7G?ù°pC€³1IWäyUú××4ôoaü?„6p@P´9°4ǵµ¸œR:‘ðáÝK°^â}ÄBè=ÛZð!œÐ_ÿ½e\سɞ ÿ»µˆ¬ÙŒ­h£5]¸_¨óޤ:æê‹é„»‘Þ3öqªÆÖªÙÎ…Iþ["xr·Ðái :ÐW!¾ ŒÒ“?Kíi /àXÉEÔ¥ º®‹¼­·Ýj §•´_žcœ¯‡V•¯òþªèM‰õi¸ÈNF0´Í ߯»¶Øqαlý{ohÕÔÿÿÎtǨŒ%Ò(„LÃcʘ¥Tf!yS!„2Ï”Dæ)‘1CER¢2• ¥;Ÿá÷z¯}ö½ûž{n7äù?ßçïSçî½×<|Öô^ŸõY¿Ùš¢ílÆ_„tÇ®i`»&ÝÛ¸¯FúõT;­c¯p¶mUq7ò“¤}Ô"ll;kŒŠR²8Þ›…·•?kÍŠwGýÄ0;/1ЗòÏ}7©»=ìQÚÄ=ñ/ûÜ>Yw{§ô«_ÖÍ.ÝÍÀ…ŸÛsÅóG#tz­}8º¸piî9œ|Q—ÐHСï[ÈfàkùS0Çòï& l¹àÒÞä•ôPv+5–”ì °7À¾)=Ù~̸%D¿‘šúÅÖó÷]m4`vyÎP¶£Ê‘ý\·Ù‹¤¥ô¥Ý©ß2É"án‰k*ûõÊ yI"w%„3èΜcq*âìtd2’¤Óè—>O\…e¡)ÛÁ.`¼š¿‚pù‰TöQ•è7{7´7õßšJÿyÄ3¦?0x)8÷w…ô+á^üÈOêÚë{­½Óž°ÊÇïh~ãùÝC·_?òûQ&´6ݱmL¸º Üà¤cÑýý#ª=~E¼âªÈõ‰Ç ‰¿þZÚ½‘þŒ—å„û-ßÙi.cü2Ú“å¾…ƒm³;Êjºeð6ðÈõ‰Ë¼wÕWÙ‘¼Ï!-çñ\åÚgéî)e÷ur!}ñIŒGU ïF8ƒ¡dä|[>ÇÍKôÝ)Þö]Ÿ¬r RÏétå8Nkxr™r3ý³cè«/Is®+è+÷šãJ‚W¼qˆ´©U'è+‡_Óß]¬—µR/ZD·Í(ŽõÔ -ã.˜€ã$•AÓè‡<Ê„·Ä8]iYÓÃ]«¬¤Ï[ãu&UœEýž)mÿ/}ɺPbýàÕeËO û«|Gùfž&º‡µÂUðÎQY@_.êþ…?¯ÐÏ}À†Y!aÃ,b#)³ôÿB4ÿxý§þ)JàŸøO”Ó#[J÷äºR‡¾~҆鮿5“ŸÉ\L¦-‚GŒ|·™O7ë®×)ÓwÕ÷æécö:fäSÿjtKû¥ÕLª袷‹Yôe’¦CÞsUÓ çT“u󯚜d†Pýû&~"-It¼6HšÞeËA{½ûª´ˆ’$K—fùª6‚öô]’ ¤>TœAzÖÂK’äÊFR9¤[6“4¼ÆÕm$òJÙž€,Ò¤©³O:J—*'°¾eZª@ßÚ ¬e串µ)¢v´~Iíi%º‹]°W%WÙ,¤£¯ X¸Ú¤Ý;7Ž´¯æha‰MÊ6‰%$q—øA“8m<Ô¼˜ÊEçþŒMƒhž$N•ùy]q¾s~¦@·(ªî[‹ ‹:É×¥É)"}RT§o9˜+yè[k²æt÷æÿÙi ‘6¤ÊD|Ó’’’ä3‘$®\|ˑӅª—*:–>f‹4õ|Ô²x–¶ÈrÝñx^ãƒøÃâ'M³ "ý}¢e,Ò{ qä¨tWâ®âaÏÐûëôNsÑP_–_Nÿ¨3^ÀÊpr•3I‡¤ ˜Î—o9pd©û¨¨³g„š…Ò²èÄ,æX~‚‹·ù”ªý=ûÒN\`ó£{ßSè§\rßu@Ó³Ÿ¸{o‘æJº)|ýꎎÏ<ý¾²I ±Êâ›´ÑÓmjå$Öƒý¨óïœ^i7…Ò‹g¾gÑŸŽKq(Ae]í’ pàˆu!}3áz±Åe€${äI}Æ,V¼'‹¦×8Fq¨¸ÜÜF eØAàs鸽€e~ˆöàõÚXÐ)€KoÕ¿]~zN§Ã»Y"ŒhW߯åtµýézÃ$"Õ¸­ºøN $0ÝQp^ç—N;—6¼MrœÕÆ…ÓZ“Ö1sÎý?=†…wgáy“?Xm,Êãóï8%þÜ\6Ø‘M¤E¶œcå«"IPÜ€dž³?ˆEí~ÉÇý`yÂá‘#€ µ˜n0çµ|J(IÊ#DÜùŸÚSEÄkl(:\·£mL u£ìÎöüFNâ ¿s¼wSÔvùr`G¼?e ?äœKþߦóéBù=åbëÒnNçxfo#€GìRþÀÿ%Äz?€'ØB[Ù§¡vöœ¤-Ë/ò@/yõfï“Ökq •õr‹ãÚñÜñZg€Ž`2îÕE¨Gɵ_ª»ÃSÛ‡ZØ‹%‡[ýÈ¿P¡RhÛ—%œ}¢?Þòà§×u'(?£/¶ðÞÖ…o’¬-(÷|kú‰ö=Z²‰¤w7[ð|Bù#V/|0´U¿Ïˆü›2zˆøWò[C9ͧ ²{ãÙ©ÑKìÔø[ðà”]fÉÄKHoic/âö^«—¸Žr86ºD1ÙXÀçãÂ'Y×’ã(ãǬ}Ù½€˜—`C™äQßѤ¹!Fħ †\Õ©Ô…UnC’1†6%iáŸCMìgñNÞëpziÙø½Z~—:¦ìÚÔFö|ÁgVÄÑrGá -ůijƒã³¨¯èÞ’11]%§S>ûÏs8/á·ÊÚ¢s{úš&HNã¾¼úL;ØH=Še…û٠уHw_ò'?´Ïä3â‘x€¿ðjòŒi‡ù÷áæ~òs§ö°YQñÛ|ÓÎD¯òGé@M.!ïðPìFÊ¢-f?£ùw;CÀ§¨xkxôxï½òo:úÖ¥ŒÅ„ÃEsNŠ<Ò‘¶ÚÒ~÷¡7!M¡É^oÜìNÑå\žÔÄÊC/Ù󜄨Ö/*,3UGÊݵ`6$¼“}”õà5‡8'N F9í&H+Òs©[ñÔQP^¡NAW•ï(¿Nåó¹Lôk§ÒF?‰¶ež^Ù[Ãw§õÐñ~›g¼9J2ŽÄo&ü®îsýÐLTiÜK¸¸~\ÆÔcÅ(t€/wn*ÿ”ÐÞ“/ã·)F?ø÷dÈÍ&±‹ØÀ.D5Ñ»0YPv2„jöû=¿¯É§ÊÈ£‡'ûz*]åZ Ù-¬|Ëu•lÃýŽÝ»ÎÜû³ˆ¾f(¯´¿x?ìîä{êN6µMœ:’nÍî#_seRvæU³s±‡ÉÊ™5͵9Pq9õzv;U·/x?'`F„6IÛÕ£¼)7ÌtáèîœPºž¹]}j5BŽEû8£¾Cm~òVÏ©ô1á!v;*%Gδ%› õß#˜¿IÍ”âÝ£3#Ök›ÛV…Ã8AÊÖÍ®3¨y´®¯’´ÿÔ´ênÝ|ª°º™z‡‚1«Ÿ6Ïãb£2ÜeùLR¾làÖMåðÄ¿pw¸†¼7«Û[ÐE˜ö½)h²–÷ ì7t=ŸïÌ«?ï ^r}÷"÷¹‚¿ª__U¡Ö-:í$’º©ßú«$"úÏâX6|z>Y"Îþ‡þ)ÿ| úÿùØk1£¯Ýá?6ÿ”À¸‚zZWw|ã÷'rïë´ÜI_¶KϤÓq»€’6€ìM:VXU~éXq·^ D¾þ*ùS¾48 s?Ì`xÅÁÞ'gÇ4¬ûÓ ]ôv# SîjÄ2Šc‰,Ð2Hbýšc·´ê†Ux~)Pºœ@f–} YM¥D­Ò©IÜéãÝ]±óIù÷u˜úfÁgÕð4­ý}fÑ¡£ä¾ë¢ôK“ôm±ªƒ ùîþèSRs"鮸ãÓrÿ%ËÓ÷#«_öþe£¬¯†¼ž[’ßPÿ:V×,·ê#H’ØL/“ЭZ7ïýúïºNå5- ]U×Z y“¾×Ò຤4Žl0üÞVA:$¤ùü¥Œ&ƒA*â‚$‹ CWo¾;ö)ª3”EÕx—t±£ÔËé©d 'Ù@ùYgz#;‡Ó‹Nw>mî^üÅ€oj,–iÐnYdK}ƒÔØXäêÐó{€ßL6/Ä7‡‡óPïR¯š~iõsU—… «.½èj€ûv!6× 0p”½w•®D_â÷Jt÷ÍfbíÓp'Íã}éâÌŸù¡V,$÷òó ôPå1¼oÿ²wë½u€g.Än žOlÏè­, Ÿª¦—Ú"‡®z•FüÒ”úд“#Íl»d…×c"¹ø€Ç$P飒·¦"ø±ÀÖ!š“3Ð==w~¢'xšm†¡=<{¤8GÇÆØ0¤C(>ÄQûÎȽôDÒìÂèÚ‹q§V œ ‰¬Mô6Wƒw¡‚auÁuviìt6'õì&t@NCÒt¯ŠÁ®gU]먨Uô¢ž·-XÞLœšn_]h#Rý¢ Í´p2‹6?Õé˜V¹öwfIŽžA!¤ØâÑ=¸t%Ç鈎KnaJ>,rå×Üv<“Ô´$¼W…Ž>Ü~D’ü6jRqàÓÜЮö ïíHBlx)¶áäÝ¡nËÞ-9ßvá"¾-‹ö²PÎt¤tâ4À{ Ìí ¿²eá¾€mËð'àB­ãëÁo©ãðÀáàâw,R…¶¤|Þ·=KZY¯Ô=ÞæJ¤» LEí)qÀ™Ôg¸½…ö4›0;)æñ±¾¼{}(/ØÑÔF‘â665ÜÅ_€tŸ¥uaj#a¹Ü;ݢͨ—3ðD¿½”Bí¬ÕNtáü‚¾ÎU¤ùÃzïqYTã4”‡‹[ì5Ãÿëxñ¯­ý;1Ãæ¦>ær´Õ[˜Ç‘¼>®`¢ É9÷ w‚5 Ÿa³ N·âÈaöJr½½Éq ¨·ïC~ÏêàH;5¹Ð_dˆõˆù 0¼+ñ£”i·L@Ð`ø ¹ Ã"äE¼;–¤Ñ/•÷$ƒàÝG ã°•{ì¶œ>èÒ=†¼F:¯³Ã‹íܲûì·ÄývTäPÂÙ]Ë[+k+怛»B_Û§×[Qì —²Q‘­¼ø’/†ÞÄsß%ÜÀ¾‘µÎ¥îuù]¤?fÍHßˤé[òÿß›ÙR€Å¾ÉzöPÉö!›$ÄÆÍTôpFÙ4މؿ³ÆEMìñò›\8(ÒÞþ­¾D|•˜;ÞåÏ`Kú¨à;þ¾V>‚8÷'¨Ó!ļҞ$fôNª¬´=Š»Úà’½r»ÝŽ.¿„¼ÝO=’G§ªáw8|KÂ}Øê•anJùu"KbÏ–Ý'2Ú%žf£b©– ë,"Oß7x®p&郗â“IÓ ¼cNNæ}`wjƒm„ï} ·=öðD ",7JÑ^M„¶çb½kíÙü¯þiÌãù ùYe­yö\ªµ®ycn *¿#ÊÌQàF³)xdÂ>O½É¦^®§WÜ•­çÊý¥};½Æ±'=^ u±a…ÏQŽ”Y-´„{V #\ÄnDÅÕÜÎ!í„Ë&ƒ•îno潇Z€MÓ.J9ÉÄÈ…gE´ §÷Øû ü]àmVšÐòoã+Ÿ,ý›gîâdQdcv„\q[ü\ cÂæ$Â}”ç°ØÝlÚ55ÿ²¸Ê xp©¹Š;Y>‡7€¸´º†7Ç ×w|†=/RÝ>‹ŸÜe£Rç+lEü žt}aÀüEÔf²Pâòß0‹…Œ¾&YÝxâ&M#™+ÆÄ£É0ÙÆNK\¶!ߥŒ]j'æs;±A[?6žðOpfîNćÙýÌYÚGÎ&]yvº8P!l@5ØŽû>ò>å-ÝþÉ×Lú4Ý3¤—Ø8Žß"›!¯ZñHº{«S;ê}$?øpþ?IZWeU#âÖóIÙ‚wÚ6§Ž¼Ëeo¨ÌuÂCeÁØ];5¡¼éOÖ‰*Éi2NÓæNúlj²”Klî²N”Á‘±aή7½Ÿ»×-¬ï§Î–„/@§ä¾”–ˆQÒ¨‰»mtî-vBéHwúZ+x˜ºüõ-}#•0§3_‚ùö û9‰W8‚ÿKÕéMZàªìrnaÛÉFseS˜gÅè=ý2ÔÎNN¦lvÉ%6+ŒÍ`Fx tŒ~yŒ~À]VZ´-±©WñH§^âò¶—S˸8‰*_}’%®¸[ì;%¼ðÐm¤>ƒ´‰slżUIÎ"çh¸o=ŒO€BÕú Ü—\J^îļ³ç(—pòét±ZêgÖQ )Ò°÷rÙÕ9’²>õ/ÙyõVãž?Å“”DOçxºæ)´?‘T.úY¤‹M¬x‡Eð2$Vz Wگу­áêÆ¤ý"û úP¤T£¼'Æn´Ã Æ£Bæ*lSO€; iÏ \ŸÐ =‘HðÍ„‡,r"õBúS¯ßÁÔ÷=.jKÜ@qßôÙy´;ñCÎ]¤ý@̦ñ›Ëïkzƒ2ü¾A4ç²¹Gìöü'¬[dˆ *Þʶ e ºäjµ{¤ ÐóØ×aG lŸŠ^–,xÍ^elMÍ$Â…Nr-[)ñª½'î#óp³i:Žçþ´ÛÛIûþÙ¾‹?D>¶°;*.fC£ÐºçO¶l®üÏ\“üÔ"±;mvE;&ñ¸ ß’Ó‰oz£Ÿ£„©åròZ¼ at$ì›qCk«èNü”Z×P1ªP¾³&á.vhxKì´…×?0p#$àqÌÏ£ßQõѯ|ª­ÉÁ)õPðiÝ™ò{†ðnåûMnH™o¢WP¦;Ç'ˆ[}TÒ•ò"Ï•³:ø¤„ôQ~ Qñ^èdì<þµð®$u†|Q.×Ö…Þ»þ:0 ¿¥§Â—Ô]ñV¿R?¯”<„œnýÁ±óí¼‚Wñ@9P&AÚòS'U;4ÔŠ²y‚ ½ÚØ…å½yŸˆÓ­¬a(ât)ýyï´w©¥¨8¸÷%ì©–(BžOåê(‡ôRvðf&¥2 üïÐn4Êе/.r,‘.Üݱ݈¸¾³Qá m$—6’ûeJû¯"Ò“îÝt)¡€|G\Â耲{F!€Ý¡É"Ê(‡£’)²†±»¬5ýóâÈ ^z ÅæÖcê‡àw+\eWG.v}äO¨lcd±v‰_(R—¾à-µk6 ¦b€§¾9ÆH‰›¤93)ç9Êø]¤ü)ŸŠå_Ãê®’cÉ#u›4v*ÿ≰xrå\¤öQ’6<÷Æ÷¦~i1^owæSs–9u4O:?ÚJÞ`pÎI®©‰7m§äké‘áWÌ¿rî$œð*§GÜ\z(fß9s*ƒgAú½ê!¡’RÊÿN½žóìc͸°u³*GîíòúÑ_L1ÝRÝÎå}óêf5¾?ÀkeŽk8X7é²ÕÉQpÝ¡Mß蕞¹.äÌ$NiXÁ‡°Hºžd> ?ºÕi’äýÕ}Š÷à»Z‰~×ñMéN<«·íì~h“•TTùV×ËMÌ3ÝÒæ)~}Oߥzfœ`»8“Äsc©W©hÊ$©+{^ ’Ô,5¡ÿ1¤ùER“7‰ÍŸ]Ÿ¨/ß3Ÿæ3:40/6 8³ö/ëër·ŠŠ ûî»ï,Ûf›mf 4ðÓó?ûL&“¶téRûí·ßl“M6q¿p¸ª"ÖGÆU¹:ÒøýSÿ×J@}@nnîZ“Á6Á¯¸¸Ø~ÿ]SˆšôÏånUe¢Ëyt´öR?À»Ü€´n>eБN+g›î4U)^7ïÎÕh€®‹$5í@º3%ŠO``bIÉÅ•KÛ?rI"ÑFa¬ }Ï$uKT<ˆöešö#å¸ùëK:kÓ+]¾Tgf'ü!ü–MfXëç{c&`[3ÒRœ¦€mÉ«€—u!A“®Û¿ƒ¤ZªBÄS’ÖmÌSj þ,µH‡“é_€rPµ‰ì‚Sª.åÊôá}ë¸|mj-–Ã;›¦:R¶+p\U¾’`%iø‡¯ƒ U³ ßô TÏÔìÙxîêvîLîöè’ﵑ.< ×Ž:óukƒFGÄ•ÊLÒ2JË’u&.3„(-Lí¬4èQ‹R€ªJÒ¤=9³YüÞ¬4®ýe[¬æÕ°–þéùôÁÉñ¥”í”­Ï+*§è;|•5ó©ïmÛ_x<@¨…ºÐ&ˆ+9Þ‚} Ù¹ð¹¿Ë9„µe´Ÿi–z!À£«óo´ÇJ.°/tÁ_qSÌ%üÉžo]ø‡¥¡³Øâ/ç” iÓ÷ñá„sf<+)D À0l"îËQýéHí7dûhoƦn´ÇÓ)—l´Cî ¸+nPUR@Ÿ¡9ÊÈØX»=>5-_Ó¿Œ²€` š¯ÊÏ%ˆEÁ(Ïð7ýg ŠŽ!ýý©ž\ÞµvÚ‚¸n‡ý©r»³ÞwIÊC¼³Ì韖ôÔ˜£²Ée9ô¨ø°e)]¨‘^ø¿Â-¬iÅPæ*‹QĶ "Ýí„ÄcØy$)­شꌋÝC¼ÓGßD=SÛ1'™9ƒMÅí[Õ ´ˆròI}û¼èu¤ïK~mͽK|'µ<ɳúƒµ‘€ÆÈþ”ŸÊ‚oFDµ±ÌQ–é[Rµ’:/é O¿È¦È1–bŽrWh }ùêÊuŒœJ”ÅØ¸Ü€ñgÆ´ž‰åéùD[d¡—‹þâN|xn«þjûs…Ýc¼ùýcÂÃ!ôܳ‘Üx–Df9¶sñ%;«BªýMk‰ ùL=:ç.•ìÃø:Ÿ(Q³ž3ƒqêu0 ôÕÐo|K¢òŒÓ›ËÕÝT}iÞ§ËÀu:Q›å7‚¯ö­CÔ…ê~]æ{R¡Rq-ncÞFè3ʯÀÿ¼th¹<éïêâ´ëõòˆŽ‡7á©)îȯHße0.%è××Flšýä…¥Ù§lJ¬àBÃ9ÚøÏcåhN§1¯œÊÅÆS’ÅÖ9źæñÚØn£SW”ñ/ž"|“ºj¤õö7pÛgÃÒ³¬ûGÂÕ\Ôñáv×áÆ·`sóÍ7[ïÞ½íÊ+¹p`Ä;çœsÜS@ðÿ" ð}òÉ'íÔSOµK.¹Ä®½öZ»ðÂ í¢‹.²™3™Ü¬GÒb"•BòbÁ[±bÅ_Yé¦zû‡þ»J ƒìÿæäXÆ ×úkÚp#gb—¹.𨅭ËíFkq [tÿ›H T6j„¡$ÎDƒDô•¿?úʽ@_IŸêöaÉqC–ò¿™Á¦6è+*å'ÐWC{žd±© vzWÆP]BVÒu‘&IA:(]^A3ÿÝ}õ­E8Ndô¤åšØd!•ÀlŸþ®ÞÔ?nîdzöçJobp¤ ñº€¾¾.ÏE”‡¨!¿>5jÌYýé?Z‰´‘ žÈIe'éd_BYß>ÝïÝÆO@¼Ê^$@;e‚¾rSè+7îò½d$6[ äs’7ŸØ3é†Þ-¢kÉüüh¯£J§–˜’È×±Õ›™4 –ÔžHÒÉ’îöI ¯hO@«­Ò’­úVžëë%ƒn'<•ckÊÁo…›ãv#~Ù(sʹi6G³&Ãì*À*õ–™m÷‰P…I%E%9=}·¬Ó"ÀSÁ0ÏyUÞü6,}Â’–‚¾rt]¸a%è«o•ÓòÀ´· SQ ÐW†©iH3NÒ[µ>ð}øü>êQ=¯;:G™o̱bm‰>bú®Z’nÉ‚±Q5ÐWöí»?¤Îuqšjl¿]8Ò/Ú”‹Ö.®&Õf¶7’MºñY\÷[jÓ²©ÉýðÙ„¿»¼AEvǪ‡…Ú[Ãz¥.me˜¶/F.A2u„Ì‘úâ¯z /ËUCúöÒ0 cý5f­_ŠÔúŽ|iêQc@²>€°¢K ßfO»Eº9³åH­ X¸¿}Eù³:r‹ë Xø¿ hp*iãå8ò ½“wG/@Þw?Û”Ò60´/¼ÉÑó ØÓ5½l^ø|ÍËQ°·µ uDEÂïHu+g­‚Û{‡Nþ\Îb±ŸÝš{´uNÜlKX}뇗bÁ†Tô$ÕvJn9Ë&#aú&‹žxã¸êÐЩÔ|>vÎBýO}b]б.jk9DzÀsR½ŸøÑÛ£H è+“ä4þ,Ö´Úõ¥îäHb"ßê–À´ü—?ßÄ1kiïDöshNjÑ+‘}¿³Ç…šÛæ%S×@TÒ·YÌÐWs@ìèÉ|èü9•%ò^ùyS8[l¡âÃÓÛXæ<^ð´“3²èÛ›ºYHy¼”–û–M¯¯ Eç>à…/iòµÒBÚ¼½r\I¶á-މ¿”üœ<ÏD5 ’fŸçß‹:†î„Ô·cyÒ…tœÄÍwº¿Î½Î4g¤aNx—ãVBî,7vÔ#¯ß:û}¸<ïT›ìÌqùQ©ŸlGx|—ðž\Úø'J.³ŸãgZΤÏ˳7sÚYòòl½é”i‰>S‘m)v·Î©¥Ö®ôßÎ\ïñ±«".0*+”Š©)½š_êz4€&€­@“²ÉßlüQ•´eþu<Òv(éb?+ÜäG°ó˜´‹]žxÕš¦Q¹”ÍÑyÙŽx±!¶·2æ™ÕCúÑRIò⨾µ(ë[1^ ¯*¦¼¥2ÁõÆfRTô%·è{nß!Ý´·PKÞߣüFPn·Ó¶~ucß»´Î è«(¿¢¿øŒ6y\¸€ 8ɤs+sn£OŒ"ú}ÅÆÍ"Ìô ÒhÚc;›w*äÞW¹6 Ÿϼ_ÍÅ©Ì:j\hÍæ©“"÷]¦ž±_â#¸Xí<Ϥ¾áâF¾2z$sO ¬B×p©Ï|oî)U4ám5ÂÐ6~¨ÜD¶Hú”]²‚¾R#õ.s¹!Hö͆‚Gôù¯3o)¬}áDG72nê¤Sð¢]]Ö«¼e#I¥ww›p† 6Œ´~*ÛƒpçZwüí¡\¤µ†ú½¢v”ÚOmô;öÍ)Çkí(TÕµ¦>Œúšæá2¸  6õeý&-K墱?“(cŸÊÏÂí'Þ—Úpùq¼Ïƒ/y†:ñ^Ưj–å9Ìþ·Ú¼3»“u3_ ÞYå¶â.ÞÓý–¤Çë}峂6V´›•%¿ lšØ59'0ï‡'Ò ¯œLÃææ;ýÖôÕ½5ùøë…ŸSöÇf̾µ®XÄ8^_Hëí2°ÂBìEšG‡öu¾ú^u9\µj•]~ùåöñÇÛŽ;îhýúõsp‡lþüù6räH“›ÿ%;zôh{öÙg­iÓ¦vÊ)§Ø™gži;wv`ê-·Üb_|ñÅzͲ¤¨‡n/¼ðÂz ÷ŸÀþ»JàãTÅW‚þbj–.6£½¬40Š¿‹V3ÈÕA}3Žf­Íù/k±|8°^‹³Z­Ö7 – ”šÈj))ð¥te#HA½–ÙÜüY3é8ÔÅB,s¥ü¦ÁÝ`’È ’¹Óÿ¬4®2ÆÚ´ I%ê²y”nM¦1ÕH— ­ =Á@ùˤ3ÊYÓËŽ:ÆÜ»þ´'n¦ç•ßµ½dɵ¹«ÍÜ¿`ª6{-äƒôÄ?J,¥í üIOg–Þ~îÿhˆž{¥,89ɼ°!öÃÓàa PG$ÉU_zÕ¤ÿ †÷ñ“4kW&F"]Ú&ÉmŸ¦3YÔ{%;"í™äV€°?‹Ñ’îÄÀ®y¨#‹8U$P±*Oüð¤†ã6HŽc!(€XÓ~ÛRMñ!n3éÀ´Å€ˆ>iaçÇï›éé«‚¸æ÷žŠÛ7è6Û»¤Ý×F? :0¤G™ñI~Š´1@I­Ô…‹Ý,ä«`àz¿¶éééIÔ«Ãõ,X>ågH>kâFå†S#Ž?_Jkô ŽÓB>@¾gaák]Ë=†zÔBæªt>$Á£Í¡.˜çGû;ç i_ŠO'-ö‚×ú90òTgüs+‹K©Ÿ¸& !‰ÇèÙ@biqùo¤[-÷yÞšaw³½ôÙ(xA´ ¼Ò §‹ÐÛÚ4ÝϪvÖöŒDÕ@tM;{Ü-pÃÖŠËo6EâsÇÒÇÓ_ÍklhbƒÐ§Ø>y?Ò!O± ¹MAÛh1]àÔ))ÉufúóNdÚÉr¤ºÂl¼m‰zço­5ªDºLg˜½o·J¬ì187ìt «Ægù¿%—%]>7²Tb~ŠTîŽKï즒Ô±ÜLéf¤O`Àæ‘sì»ÈjT?ü¼²YH穯ðôgÃm©ì¹Øpä‘!ã"*é<™…Ðíõ~´ý‘œË‘mÑ(ÚÐ',  Wñh6 ´ø90ñ³S`É› ‡º¤rïR&©Xw[“úØõ#VL¥²Î*•nö¬nÚÉénO!¦…×y6 æl&Þ¨¸ƒ—B7f·Ä.ª Qp„ž¼ƒØ¸XM}ï˜s—ϱN½çÜú -·ý¸¤ÊÂ~@?9ݳæ¨ýS,% ÷¢eÞ#ÐùÉ1vM è²þè×­ß qs‡mŠ8i³HÂ[²7¸'X¨=‹ÿ·xiè2ð‡ñîÓÞ `˜ÓýË×3·øx¯¬Ã0ij]ÃgÙùOÚĤê«Køµ$¿‰ÿvòÔ“ð/¥¼á„òt¿"o¡¶(öæ½P{µÐëÛŠØì)büB*Ûr^²îH±¾ŸøÒvÎûœ¸"ÍÇn&ùã˜Ñk‰ë;0êò‚ ˜ï⥺| Oõ)ÏñëïÞ‡$G¡þ†ÚÍ}Ï&$&[1}Ò饧Ø'€èûM ý]Ýq@ؼÕyØNÿQb‹ ¿$Üq¤ù;S-ÓÊŽ²AÉyv ­d+ÔµXùDë›øÂÞLÒØÖÐ&¨l#ï<’ï l”M™ìl°» <ò¢2·âI¹!ÁèÜIïjô$xä>âm€•“íù|ñ 3­€¼‚Çyß ?_ñKSäÜÓ†l+⾆ç¶üš } Rõ¿zõ€*GQ'ñiÁ+B'î„Üs«ŸAªu[tikýSaülNØÿ²Vá ˜÷Ô·kèëÎe õ(á6"¥Ë·’èCºRÖõØÀ›Ä¦P~b–=A98½Ô•ŽôB™”ÆÓµ0rdfì¾…nrBGkž™û.n·‘ÇJÒI“M4c‰Ï í¼É€@aÛ Dº¥Î€4ø¤5ÂIÌ&4ï‘„`1þ18N9ïFÔÍñ$å/ØÖ)„¸â–d°ê%ï#êã&;‡zÛ‰0uyªÆCÿT6Z;”ìcç ={hè+Ôú,Â_k~"ÒkíÝ›t'ûØÎ Î?¤;Bß"àz5ÚÙôçÕHîÐ ïI“óà„Á-‘ÁµðIøä´}4 ÿ¡HÊêôš.¼ûÞ¿2´ƒÃeŸ¸Þ½.Sþ}J a£ö%Ó¼ÝçZ¬»AÒ¼*Щ°³Âœ|*ìÝ?¸´ùt¯ëwÌö¢Žu ØôôØ.Rgõn%¯yócII‹šPÏG¶¶ùsØÐ\Eÿ£rõøçtæ(ÏÒžƒ”äÃçÚ*ó…xy±ê3ëÛï˜.·“Ê3ʲºð µ6õÕ«iîZì[žx86¹"`’~ÑÀÃý\Ó^&jïæÌnÍ4¸éïÔy¬ó _¡mÏÓŠ —Z‹œIÞ»þ:ÁJUiÎy¹Ê¼Î·Y¨p˜_¬°wËOgló‡ÿkØDŒÔŽç@õ,^{ ´½õ÷Aß9AÂ>i~¿kŠeÌ™èqª‘.ô×Àš_Wùªæì¯}L™2Å–îyä‘NÚuŸ}ö±8Àl‡v˜}忉>úè#›5k–µmÛÖ† f|°í¹çžvöÙgÛ€\V}ôÑÿ¥,ÿ“—JàO•@Ðw]6~Ô‡$þT ÿßxú«€XõTç11«Þ=oÊÀsœ&²ë@‹™ØeJ¼jùÏÒÇ ‹Âø@ß9:€F³ž9ñó/7»á€’³Liü¨tQÑ{¤E—ªUb}5ŸºMõŽ…‹^g(*âL“$•} Ïϵ諅2äZœyÆL¼ARáòA:%s²œ¶tutxWÛÓ„Ý_¬¯*¿ßu¿ÖU7š*Mƒnû AÑ^½ñ¥Þ¡ì}½¶*oƒ™ÌÜÌ”‚_à_`&Ú:ž™©»ZR™›ò[E”¹«L‚oS}:šE‘aiàl¿äŽ«/ñ­³>5áëÏ¡Êq¡Î^‹‘là¶ŽÍK øzòIK‹f ùAž‹±Hþ”ÙNµÌȤéÄSCòGZháY“bi£9,<ÇT³þµÈòŒ¥;OúIϦ= `U½ú7\ïÊÄÖ'-w¤z¦% „NÑëYnÆp—0éU_N™Mc?FGˆ¡–´/é\ÖI ]j’íäÄ/äK…å}\µèäøf Á_½öÀlÅ@¾ûðN›r|·Ó#Œ]•üÞ–Ä.³!,ˆ’l†|˜œbÿhý9RbOÆÁEÒ>Dªoaøk.[šbG“¹®H¶ŽgAþ\]­\ µQè,åHº}Ûpy`…¤Pr¦øÆnQ¯>L€jk,À(¸)z?õ±;ߺø¤4:Ê.O¦9ÀbìÐä',–>´iH&éwÞŒŸf’v×&Î(øØ¢·#/¼…]˜Þ¨‘$´¡·S´ÀµÆñW¬ Æù,¡?€zí(?´E³³í˜ú›€%Â= *doPwK¤'‰äߦ(|ñÝÊ‹TOTµW·p–ºŸJÛ$*Û_~^Üè;³HÊëýHö½Qqq2ê3V±¡óƒÛXYfQe¡ ÑÁñëû׸w‹=%ù‹{þéØM – Ÿhʼna¤²y´ª´ŠOsù‚¤Ý,$ÛÄïN{W]N]…÷©è¯AŠï~ÌÎs<á8ÉæD/·F€yãRMa“›ð³Æ~ErÛe9§ÃrJ1x§FÚR\”Ÿ¤ZØ9¨gQx~ÍÝëé¡$à?<$Š=€¿Mm|b¬×\Œ;$œäœ8£ñ;ð¿ëú/m—’íš#Ð ÐÍ‘ÿ$nIÀæfê3Òq¶?ò[†éã­$úo;IÚíØèîÍåŽWRÏÄ—bô(=€´·Â-iVú“ã\ÈþŸë¨¯>H™Ž´@®ÌîˆûFø»šòëá;K?ç$ñ0Òû›…vcr–}px}ÁtÛUs_b-v ~Ä“Ôqô`ò>‚øáS6 œK©­Ñv’ʳمÔYßtÓﱛkbûð^dW—ö³£òS^Ì%dåöóx/>DÞÑú‘Ý_Ô{â­x•ßpÜQæå·Y»¢mì§’–v—¿½/ µ¬3ík R™‹áÙbû•q1ncg–_@zw·“¢Û$Q[²Üþ%µ "$‘¢Gxfh;¸R*™^µœ‹ê‚O¤†‰6+=ŒÕ¥ÎùË·ËÓu-•aÊ“¾Ù£E¶Sùc¶Uø ü-ƈ޹˜|EŽÃÏåË[¤}gŒ¯¥Þñ§ÓN²ºî¨Óä·|oýâ~Ϧ Í{lô6Êøe~o^nÞåɦucùwÙ)/jtô.‹]k_–ž‡³fÎlOT±¬€_åô% ´AúÞKAN o9–yvw$¢_L}ŒÛöÄ÷¸jÍ6Eœ_ýi|Ê‹0§²Ú$-¦°„rîÇ…~¹qHé´ÂŸlBôtB 9¿¯R’wÒÿzÔ’ÇNüfQNwÂ9-ðß󵤃uT[caP9ù›´ü•M/=Â>N\Ïi¡ç®òOø\;yÖGIÓ™ô5¾¤JûØí¼6sŸÒÚˆqÄ‚+]ðBÜ7ód#"Ò:i`·•_f;„rlƒœÇ1ߎ^^F…z¹Ó{/AâµG’6—s£Ið$¬ºÔ†‰ˆ6Z9ÎÐ?›SàY¹vêƒn¾‘ê­?zÎR•6ôr•ßš?§Ýq)¦åÓOhË-ù(<2ž¸sòâT±ÈO[JX#r^Ô‡GñûißÀ½I÷í.÷T{Ï}‡þì2ÒÙÕöË}ÍmnjæqoòwÚˆGä”|zéÂõ² y/q bžƒs³ÝõÐ6'µõñó1›ûU‚¢ÍËsì+æºÈW´?åyu.z ¿m í&ôÄ p=’“åÊ‹GÄìúôô'¥SsÅ?K`˜ÅÌxkTþÙ@Òþ~ªôO¡ ‘ú¨º@YI†§¨Gjyu“¯ʹ„gÝXV··´ úè„øVEþ ‰k®¸R3é§è+þmA_ô ›FÚ"¯NkøÜšy¤ÏOÕmk~ Øu½™©ß(NfòjmäÓ׸¨ÇáWß Ës}Fs¿g¬æäÏêø-))±3Î8ÃêׯïT=äp¬;HR%pÓM79}¿’Í;×Þ~ûm'[¯^=kÓ¦í³Ï¾Ö¼y3lÜ^}Õ¬sÌ1NºuÑ¢E¶ÝvÛÙ 'œàT(LŸ>Ýém×®“,ÖQrŠÆ¤zaâĉ•žÐm¶ÙÆ?üˆÊp¿ùæÛ­[7ûðÄòرc­¬¬Ì^|ñE›={¶-[¶ÌZ´há]¹µéê:t¨-\¸Ð©wÄo&Ýyç.¬K/½ÔT&!Ò6ùé§í]âýñÇ”°Ê¼K—.Ϋ$£¥~áøã·×_ÝæÍ›gùùù¶Ç{X×®]måÊ•¦´ÊʺU«V6pà@{à\U†Ï=÷œ“8ؾdÉ{ä‘GLeF]ùtÒI¶ÑFÞ¤]qIZù®»î²‚‚ûá‡ì­·ÞråžH$\œvÚiîø}fÞþ¯}³ìŽZü_K÷*½Ò¾VÛñæõ•µ£Ö­‘% © ªIj»âílÔ‘Aq-|}éø} i?&ÁÿíTˆc SYMx»ȉt1S4¬Aš©¯#_ojú³Æ£&Kùý×pô >a´“MW4Òä¿$¥µ.’ÉH>n þ4†I^ðøïßÇߦ^ña]³ýH%G]ÒÙµ¥i1€Ÿ&ELãÑõ¹:Ä~wRÇ×uó£¶°3Í5W»aêˆå5Óë&-È6€'ú$ˆ^a±w‹­Ó˜ÄNh:µ š[ÒŸfê±Öãf&lZ`JZpK¤B}p³®˜•^/Æì.µÉó M×…´¸ J×gú9 ïuŽÜO½¶ÚùÚÒQ-œÜ·YÐîíŒÖ ,[B9Yl|zW͵ûÐtö·šÆ•&›ðö }¡`‡6Hµî àtúý6M=a'³€ÒÅm3С¶åß‚Épæ"X ù.Òšä8«2Øj/R%ri]]Í4ÛÇú‹yÙ7°¶9wÙfå=l 7oÑ“}<“kGHÝYÙ)¼~Ïbw y)*5"N‚Z*A޵°·RßR»!6àrâyŸRbÙÂâ|„ŸHˬmYžÏFzvo®Ûy'ñRÉK+j¹íE½êH°¨u¡ÛŸýM+ _—Úè@vÀ„ŽÛç du  zÄÒ+våZH»ôëŸ{ éÁ pµ]UÖ©ÄŸÐo¼‚˜ú®.ŽJ;ÀbÃð~öG*ÑwÁ,€ÔnH~lw±¨qõÎå[VÚ°x]Gû—Y>ËæÖ”ól0Ña” ˜O¡ƒxK‚©yÄÿ0¼œ?šrŠÌ;Ì^,Ú>.·Ñ±Ö¶âT¤Ê£Õ6îü ôTVèoèXVI<|jËË\⿞øR$m–|8 aÒ%IœÚHú¼ õ  ÇÉ#]ÅY¶ã÷%ñìI<3LRíÇS^þ¸¤~¥Yä\”{™5,¹ØîO~‡»g(á´s‡:ÿk„±ŠßüÒTW”£lo´IèV>6|&éàÒ}QÞù6zFi6çñ8ýõ~Ɇ€5§vÏq›Zjù2C%€Góöõ £z~½û´®W Ôb&7‘´ìÆûVTѼÜJšQ77a9IÝà1Öt¤|¢7Ož ò£žÊê‚ ÔlDù‚Tâá9ûÚAEûÀÛ%o6+à“¸—L<”SQkìxÖ z0µ·ò‘”á‹Ê©ãŠ^¼°XHÚî”)é&Hü‘ ⧬£ç‘^x«à9üðŒÃc¡Fäç!çÜûÓ˜r<sÊGÀTν¸=+ʧÈu„û”ÙÇÆ"Q¯J©žj€ ~¨M±ÒsÈïóžPwÒÕ?Wzß9¯–í)/â¦OðÊ‹•¯¤ñik¿†—Ú± oülÔ]ÝÓ.«xÖ>ª÷êTRÖ­¨‹-µeƒ(bCóúÛðÖ»§;Ñ/\ƒäç/„zGh;3\ʱ÷%‚S«Ø†E ô»ød»#vŒ]W¢< #¾m.Ž/îO]S¦%Çà˜ö[9#Ý€w¿ÔèA>BÛâf¢-¨÷½µ.{‰²§îº à­xr»7Ô·6 ªéêÅ;u‘G=ãšz|~êÅËOüÔ IzFŽ$œ þ`¾\Ÿ^úžMex"o‚mZºj@â6Ç!]à'X= §»Ïýémßpý·û¬üSŸ·Ob—[Ëø¢¸ õK—Qßê IÚºñ&F]pIb§’Óìst/—9[ÿÏæ¤ãYò O”w#nx‹M„{à¥iŒ‹•wuc,½£]•sŸ©˜j_¤&Fát8Ú‹Â'§ðÏRÎÑ.ðοxÿ-툸TÞ¬ ÌÙiãjæ|)…>D핱á [d/3–Š/¿b.Ð&TŠôi‰]VEé[»ÙÁE]íõ»6sOŸ|9½!à©ÚÀ"ñ5Q\ÁKŠÿSäÔ#í—´¼¨` eÚ‰—t[ _HÝÔéé”ucœæå79ik©°ª·\?*õѹã(Úªò':äq#þî} üÌ{ŒßrÀ×íSGÙéæ.Ký\lûqÒà¡ðšÊ{@´©K·ZÀM9 ¤+D Éèp];(ös§+¤~H'#«S 6"i~‘}æ6›6ò]ÖÙ´a½¿WH”î÷ŸÅÑó[MÙWè+t¼}ù:³zÐ鯆<§Î‚›µY®Åpsî/™¹ö¡Õ nÞK}IýPæšv‚'iSº˜Óµéáõ©Y(z3u´€ßž¥ÚPüVâ/‹ãõd„Þg‹õ€§Ž_Ç[ân1¿ø:¸oŒ›Ÿju'~ú0·=ý€Öc™ëøc­žÒšK÷ºgñI6éä™H=¯T‰…Ý×züóÓO^;uêäÎÌ *^qÅNýƒìä^ºp?ùäÛ~ûímë­·¶wß}u×Yyy¹ó®‹Ò_}õÕNŸ­Àá3fØe—]f=ôDua”Àc˜ }Žn¸áfêȸÔL|ùå—6dÈ•€’@hª·Ýv›½ñÆ@•Ç|ÐÆò'©]I'>òÈ#HõB®ùWiÜb‹-€[ÓÉžþý®c¿}Ü8›øÌ3¶é¦›:Éh`&Lpé•ÿÕqWÚ®ºê*(wìØÑ•ÇSO=eï¿ÿ¾o7ß\1Na¡ Gï‹/¶wÞyÇn½õVûùçŸMe%ÐWeõý÷ßÛÞ{ïm Ka 2$«N_•‹êDn¤ªcË-·t»Êó´ ÿ‡j/¿ôUÌvk»´Mö’öÍFê°ú®Oú3 ¯;6¹>QKXÄ}è+òAß™ªôíÂDG>¶ãÇ’Å‘.Ë Ò],‚´OAÐØ·ÿ³yè+ZÍ/{í9k§“Õ{«ú”HÖâ8H…|貉¿ƒê`U¶JFµÚ@_•×)”纀¤lTèäƒlþ³™dòöG(ºŽBT#w¡ÆÊJ#Õ•Oô•¾åìfÀ£>è+s¾ºèK´v}xÎIµ?òå«ZH×mÙã€ÊjG³¤ŸO²p¾(”ﺧê\R;>è+ÃÚp‘¢$w%m$¾è;‰°}RI:X oïw%×páŒßrÑI+€¡Rªêj ¯0aÏúJï›/u þe·ÌšT8}e«ÞEÍÌ~EvÕH p-¼Ó ¯ì¤'Ü[¨±¼©Ð"0·ÊK˜E·±Ž=ŽÄn1Ve|kGÙ…ÜÑýÑœÇ![Oêv‰ë{Æ"Õ0’2߀zl¨>žÅ–Ç5Ua´MÆlR$ûâI®$¥ªòV–•B»·<«œ'ÜSu+ _ î¹å'Ø44ˆö÷Ü­í/ FúÊ êPÊçryÕž„·Ã{#9r-I\I‹ƒÖD{Ùõ–Y½èHŽY{—GÉ«è3ÀÃfÈ÷ÕCµ’s(ñó¹íÝ'-,}ÐWfï3¦V3P»à¾²Ã€=•…p{}TÒ6gÖPò·£grœÊ;Ú×n °’Kp‡”îÃÖÙö<üó éÛ€é('¤—ê‡\ôy®àõ›"”…å!´³ViYq¯ÕGaÃà4軡n‘ÿ¾½Ññ¨vz¤iGÁ…¡„ó &äPÒ–ÄcHK±ê´Ðšmˆ…M˜X1ÒææÍ¦ÙŽ0Û˜Œ›­ø©öÚ¢›e|‡=}»$&ÑŸìÑ\ï¡prFÐÝCðƒÄñm¿’Ðgì(w­ cº|‹lžI*mjI;x%„ݗΙ@_ñÛÓ€ÁqI›Im*FØ3%Íì~T\c¯9ɰþJWxÒW鬛¹Ôí/œèxž€<¥C‰ç5~«9rKI2˜Ç/ômé¹å¯ú]¸_Îó|ÁíÒÕŠÔe.íôìøãä» ñôóÜK22ïÞñ/«O3ÈÝ73Î+¯Âw­]½¯‰û§´+ƤøLÊâX¾éçJ.%£¨®1'.íà+Šv!®¼|F} ç&sÀôoJ:óf/cçl²Âù6/ï;éãpÙ€¾ðó;áR7ÔÇ‘ËðwïÒîÚš¿›¸7K1nå‘N.4ÚC[&ƒ1‡ ¿!}´WºE¤7¼%æ¢8üô±@y.xzŠŠ)ƒ‘žãÅVÎ¥f …€åáü(µü"žËÓv›n­ LTÞŸúNpæW¤6´á‘ëyÿߤÿxRËÅin>´1qÓÇõK ZñžÎ¯d )dwL= ß¡ëzëD‰½‘7ÈÆU&›ßJwu¤“íL§âÚäˆRTl‹óo··Joeƒ«:DOqjQ¾`@máâ¼— ¹§}‘6vb­:6þŽ]› ý±# ›òŠv·#ÅʦiËG¾iÃT_jËsQ+á$ïÅGª_6‹œmìZa¦KÆD”y§H ŠúJï;²iÞ^ëä}»¿šƒú†&ø^ðN;©á}¡+W$É-ù9~Òý]Á‹Äÿ=Ò<|¥ˆû»©|°uœÏ¸ÜÙŽI|•n‡ËÃÉϬ‹k‰HÙ3‡*pqz¤gSµûZVL"î;½>¾³ðüú:‡•›Œô3Vz…} À^æ6Aà>@¦®H¸o½t!—· õ??úsáhìYÊo|ù@[ô=Šò9òS;ðlâuÒÿû¤KÙ,| Á쥟õª¾Ã´ç0ù@¢ÞQôfªÔ…HÙ“ÖHÞ7°ƒ#×ÐGŸè{q]NÒÛåóíƒ(ïij@_¿¿Ó?ˆŠ¶¤­&¿‡†gøÍ¢¬é7 É‘úòѺ^®l+¼¦%ZÇ3÷b¥¨í°¢È!„Å£¿}¡}£¸¡ÚŒq»³¡³Y(Z9s:†v5?’o·¨MBˆö¹ L÷ÁŸÆü$Ì UD.@ô@_ÙÒÖËåY '|Í\°>&[×Ço¯ä×7—rÓÉæµ€¾ Qj$n£¬jÌÕe¹ŽtcîDÀÞCÙ0yOmÆýÖÑs5gÍà›k0ù„4Ó?‰”þÚ@_ÙÇi3ÁM4mø­7з *–ê”zšv˜æûê6&@¾FYÆÈ“.š\'ú©ÒÕÉp†Ô°é(TPµc,×eÄ'Ò¿HmÔŠ'ë"Í¥7fŽ·„ùÕY«õ¶¡4àù,BR#åôÊë—6Š6ÙDƒkÝ$=À"]×·o_J-DQQ‘*ƒ!Hu„Ék®¹ÆÄrÓ£G$ Ä*)^‘.S›3gŽxàN§°¤‡H’°AÊÍÍu—Î)l‘$]wÝuWg&©XI銾þúk÷Ìü#`XiÙxcu u“ÒøÞ{ïÙî»ïnƒ ²îÝ»›$†z ¸^°Ö²eK»îºë¬OŸ>έB—Iùž~úé.²öíÛW“~TZ$å+ðWj6¦Hz—%%)Ië³Î:ËÚS§NuvÁ?’´Tž$ì_T§Kúd&Iã¿“š¬»ÿ)ø'ìõYk»˜N›%ÙH ð¯¿þš•4Üi|-@ZÐÍßñÞ?-å·¾ÃÖ®v~ ~¤ßHˆü r¦hR²€ÉÐÎ¦æŸ @%ÛǘÔwÉYö8êB©Ÿó7å-…›%|_ÿÜ\¦ã‚*â]—Müòò눴OžŽ?ÿ«æS’ªÒÛ)RÙ.r‹ šî|“û™$õsùæÙž¶lÏXf—’®?B³™dÖE!j#m$è8±ÊÅ}µ€w“$5> ‰ÆÀÂoRá )tQRš¨‡î«ê.úí»RúCõ®E”¯BÂþ£» ÔtÄM€íþ„”òYšJr¡îI 4ÎlŽÝÉì¬_ʽ›'$W$~ìËäîâÑ­ÈGáöÂòn‚ö¾Ù‘ip`Ê꤅چUé2_ÞMº&¾³é>(N8H§N@ݶHå~J~§2íØ€@ºX cïò±vgt;—ö9&´£í»•ðÊÇü.&dÆ\c¡(9·a–çÒh%G²}3ŸÉ¹ž™ÿ7q?a0/jKžêo­ úƒïFÂ{@j!@Á÷ÖÀr €çƒ¨n¸ÔÍ ‰Ï]¨:’“:ZçmaÍËÆØ¤€EÚ¤èFZµ&u&ðEì¶ ~²–º¸-ñ±]ñ{>éD¾ºÚëk:Ù=lú¸”²»¨ox3ù)áSnñ»ùMtab€{€9m˜H… u{>MÇr§Úñ9ó­Ñƒ‚žû0åVø£÷®™A¾Ê.×ö€ö."ßšO Ç©N(?Œg#Ï-—ì©ºŠ¶Óšª ‘¦¾=ÑÿjÛÐ ':¹üdû=!• j—¹”`œ~uCg§?sÌ»•ãþî2¿ÜûmÃäLg÷+`p“äë–ÃF‹6¤7w¦úÃX–¸‘xæóNÝv6È¿)½P=±%õæIR¿¦ H½ÙP6šzGÎvöÓÞÊ´ÙbÿkÁ{vK¸‹µÔØ*‰V–$©Lér¥ß³ò£+7;G„VÛ1ÑNòîHÉm;²ø¼oD¹ÝĶM·ÕÄ[Ø-sö?Ç.µ³“ZŽÃ·®8™ ¾ûm~êÁÊcæÒW¥ëT†›F .;¿_¤Ã§®’cxWÝlJ÷:‰ú>þÝ7m_õx™ÍÀfÑ@;H[í•äÒçuª¾»ì<ò¬´Iı}“^uå×¥ÇÚô™úøkÖ<±ØúŽBßê`ÔUÐî£í›üËѳ_n:(ª ŽWðÓ<±b…Ý">¨¾ÐšêÒ/ÚÞ–jçÐþyÁ‹* ÑOüö•dw&1ÎI§¬…Ôß\mÝṈó!úŽ!Ìß0o9?¼-1bãʺ“Ÿ¶nÞè]›˜÷­ù\ÃäR;‡¼T§*®¬nîmþÝàójöÚ¨¬T»PÍiùcQ·ruuÃj_%ø¥¯øC$žSû(ve}@UÀÚôÕ¸Ÿ"ðK¸‡ã“¾~YFTÏͨ«É×sÔÿ0xjA6ßYÍ´ñä6Ÿ¶RMx àïðÇμˆWže¼ïœ1' x©öʨAŸ¾´räZª›Å\áIú›ñuwˆÖ…êaÖ+I€È—l­+ðC9Ôî¾ûnKZU€­$]E€‚Ô¥K÷)éX_Úu·Ývsf±XÌš4iâ#Hß®H’¾R× IJ5oÞ¼Râ×9àO—½örª üoIÞJ/¯@NIKX$•ÙHq‹üg67A3I‹vÙe—Jc•×;ìྥfÁ'à~YJ¢Xà¶òS}ôÑ&@[yþì³Ï\ÙÈ¿O~Üß~û­oTù¬_¿{—º ]'UJ‡TK¬+¸]Ø|‘޾ÿ?Rä4Óµ]L׎ NmR¿ V®r¥á ·U“SõZù6 ca^iñ_XW£.utÂD"U“<ÓŸ¤z]ÄáðN> C÷J­º•$i¦tkpm´À-–= óT&u‘Ž×F5n”­Ías•£tc® °ôu¼ÖæNà.Gz‘&ü>éò¦µ‘$UßôC1Æ› I/H’ÂóuÉ]ɤt˜&¦ë™ö¨ƒÿþhtâ ]l¤¶Ù&¦Aé÷ $q³‘ÀÓgì¨w5ð®Êâ4ÝBêíêEàO¿R–õ£‹Ú¼©³ïbÝŸ¥¤óŽ@½×åÓ“¾ó\©øᣘ0eÒÊ€KMŽö5˵^á|Á"x¹\À©Óõ:ƒ£’€Òb‹2ÁóæL¾»iÞóÍÖöT-V“H°ÉJH†®•rßp“Π>h†¶–—öÈ×Ü6ÝÎŒ#ŒZ0×FwglF았ʒ¿fùVP~Hó8>ú@ÔbZb´S#’y ÞdÀmG©/ì_èXVûïà'_š:IÙG ~§FÄ£[úí+|ºõf&@{f }¨>oc“ñré$}ÂÍ“ٚś,@ZëE€$c*OzõþCÞ{ ]éZŠ&"s>Á-Ð2øÀoM«)§%el5z÷^w@q$‡¾´áaÍ‘—:à[º‘}Ñûp\^:£¥&%Øu@ºQz»Gò5SŠæ Rœy¢Åî°¦ð¨hk@ôYH¥JBu õJne= X´¦G/­?Ø×ܯàåhíþ%{Z´¨£“šþèbwN“.¹À†#Û´*2ÜÆbÇE5†õ(cqÚ†¼°O¾‡  2å?N¹±âXd¦ÊÓ’ûsYxmc7'^cÁü¦1ÏÿÛZ¥š:?ú£‘dmrHéAv+€Ì áÃì† ÂO}ˆ_Ö±[lDd¯,$‰”œHÜcJ½!€£#.Â3$ ªQ¤7ŸÍø}ÃQË;«FðågÉÙ˜0F'®wÀøÛŽ´F`=^ñxôÐжv7j"t ç3X†öy€gzÔŽ á{’ÛKICx’™Iî[6/²«½TT•þcPÈ·Š—ÒÇ­ ‰ô Dˆº€µHG²­°ÿ€gë»ÍrPâ.KÂaÞõ…“ÆKsNìpøü—?§àÅITÓêR:õ]ËÓvÁ‡ÌVGM±“âWX¨L!éßL åsáCl3øÙSÁƒ]vNÚÉΑf^LŸ³o†·rʪõ1zÚ»2gß©äõuªæsÊ` epãæ`ùŽæR§Ê£89êF(ï4ŒÂÈ$x¬ìêêê@}HujaÇÚ"útGÑѤ¥/¯Û¢NâMØãDÚÆÅ|gôÅžëêÊGÏRËpRÛǨ];~Qÿ’&ú£ßèëÇ3oh /ßËÓm,37xŸ’hëo±6jFÊÆÛŽê? ƒàé}w7JÜʦòƒ\hÚЙ7@ýÚOS6Îæ´Îr·Ùé¬h_ê42‰ò.»—²#mð³î‰Ù=¹Âö"ìk»§QÖ×À§¥ra¥oø}ŠI›ÉÌ€j|œeî#ôÛYaØçƒ*/Íá¹ÌÁ9p•mío³mRbZí´qàÔEÉI~9kqûg¬6§ŽïXw§áþXÜoëù 3&Ò³’.éä^Ápip¡çD›‰þE 2IΣ½¼äÙ­Ãß÷#C­5›‹§R^ïîyš ŸÊC ÆÎàÑ$‚¤ÄëS/ܯM`)î;ÌòÔ…¼sSqøÉ [Nµ×/5n¬f† ~-Gµe'µÒ|ÄG )»Æî»ï>øÊN$p3IƒO¾®ÝÚ$‹}pTú„³QTÞ²Y³jN>?öØcNÂUµ¥Ç÷$õ’À]›4ì¬Y³QÙðµPý´IÇi|0[ÒŽ~þ6ܰª²äVÀºÀܵ‘t K°H*#D™q ¤–š‰_~ùÅÙÿ4oÞÌIOž<Ùé@–d¥mÿý÷GGòáA§ÿ¼¯§`jR© k=ù_ S>·Óž™˜/ÒKZmÈl°Á•Öj—®d°€2;3†È¿º0îÈ »“É£€†ÛµÑ‘HÐ ÊuûÚÛr¶8ýc×’~Ó%HAÒP±¿Uüv!/AIZµQ+v}Z •H@IDAT—Kê| Ó÷#ÀÖ/éhÀ²†‰Žw,ÙwU÷SalÄnµô§ê&ölTŒá‹5¸£ºË*½fùê…[2$87Åû”׫ԳŽöïÊä˧ú,ºƒ$ ¼l¤#³#2€FIg/Ëæo]Ìt9Äú$ñ…¯ò¦¶6Z[|’Þû«´†îa¤máÊÈ'•ÕFþÏË gÿ:ݹãuz݃‰:KŒZI!Á6q/Ÿ/´Üy>]—k+ŸÉ4ê'zò;ŠÉÙwɸ}È"RrDC— dJÙã¬q9K­©­ÝBàÞ=,ë¤øÈµ;)ÛíöØžœ—§Ó‘nq}Q⾼ŢÌ%Å$µZÜj!"éê”s/‹uMÆ5VÏ•Dú!~+–µ×ªWÖô€|;£âà­Œø^J÷]’++{¥¶R^ò^$à<Ú.°2-ÒÜÇÏ—évØÞÑ¢ µ ‹S· ä³àUhàUávR?¢£Î¤6 x+L¡Œ·–¥/q–ÞŸ‹‘|zÕIØç Å¹ |9——õ£$¹\-z’ý¿Ë:¥Zý”¦ñw¢È•$iòÃ⾄ ô¡q6B¢‰Oœ3÷'p|R}×]±3s™7&®³JýÜ&ÃÓís‰k™š›ÿÆF$à‹|é4ÅUhZ¾!´¡ï1~~ŠÕ³—tØYø` U¤KÖmX»E+ùÆ„õ¡]/°.wº s—äBûô†Ò®,§_˜{Ø>+?M1sDûHüVñ&ñdÚÄZ@ßñF¨“ý@?b xR²“¥»S†¤7r aý øeC+νǾSúK÷¢<_0Sá·„óï³íÛÈEér§ìáí¡ãÑáK)÷FÅÛR .$&áv%at Þî°Ù¨°²S1kJ\=°o÷0æJŽ´ØRˆÇï°îØtKò3…"=âÙ8îqÌÍ?ÿð{ f×ÊË&ü>–!‹$9ýlòk• ¡.¡Ò¶?Óו FgQ–?ÿE§°îcM°+s¸Ýñןyé3lrïæR.a%s Êã®èé¶jRÞ`µÞŒà Ƽ&Œ—'+í"ÆëãjÎè­E Œ¾'À_ÚÜÐé‘¢T¿¦ÍLÈØQ¿Øi ä“.‘uº™Ë(O¾5—oß”¯Üüõ=+Oe—\kW,¥~iû„¹Âæ¿ä­EÈíúñÏõ’¡_9gj)~ËBÑi'úî§y|7ÇîÏ<öY-„×á­å!©_¡ŒùãgVxøßSÞ<á+Þ.öç×¹m&E;‰ó[FŠJ(ƒþD¼øm}å­X{ÑçOì½.=_Ói· Ý¿¿èú† mæ0}!ýÓÜÑÞ>‡xÕBEºÐ<ìWÜß–-[ºÈ$eWý/Ð+¾¢©S§:ÐW*¤î@—‹õë×Ï÷žõôÁÐ,Ëz^ŠŠŠœ‹tÛJ5Dð'•Á¸‚f®ãÇwR¶§vš]{íµv úxýKÐi(Ï7ŒJbÙus½Ü}÷${”ËÜÈúiûóÏìÊõ%}óܹñ-뻤~E¹ßª¬¬tà|0_Á8÷Øc$|Ç9ÝÀèkjj.P|%­ø([ñQþWÄÈ«A’}Õ$>©ýý'I@åL‚R‡J¦ ,ŸV(yÇrW¾œïÛ¹ ¯bÒ|àU&«š~¾N^ri¬¤- †$vsÃ,Ë{zzé¼²üB¯iÜf¾¯ÉPP²³±8ë} ·,îw倾 £åCËt)Aße‰Ï÷#£¹$©ÎÿV’dHÇË–‡&3QYÒ±ø é¦çÑ€”;¤ËY ¯Hœ}ÕzW]ˆ4ÕL…Z-è«‹}ª[¾‹÷¬È©ôõ}ùǰ‡j²LAŽ'fo"5$sHSséö½‰Å¯¿Ù¢Ö§%‹.¤S¨‹|©ÌjÚÜX¤£[ߤóÒ‹²ÕfÃôœtzßñÊ{ éq·W>.ž+_ëØ¼8ão9¾Ôxm_4+ËÇ™Q&º,V{Ãc-²\/Õz/±Û–u¡ê ÖG KÊ®ëŸY`=kÍCál½µpåXÀÖ3.ŠVÇnSx\‘Š™$OEoJf¨ú Œ¢òݼgðoYkï­|;¯ŽjS éïƒ(Ÿ¡¡ŸXxFÜÅ0ò"õÁ-,ö[„6ø€úr´tIù=\f‹B]›¸ãæÑ6l´´íj›~$ Ìh[32šonîE9 swÌÍíVÑŽ|7^æ¸IÜ®B’ecÏ´owêp?>ñ—~6wÁîžÐ¤Ê™ÝŸÒOiä,ÞDQV¶•†¶CÇ‘¶CzÃmgÊäòðQõQxb$fýIñ8žƒùƒåÃ׬I¥ÝîƒÙìÈèuö1¯’ä;=UÊ~é2*zй»?•ä/õ—~pt£GlÞ§4¥¦ã>#ퟶZøNÚü ¸ÃQö+ê;¾O/ƺ†ÏM»yvÉ{Ù8š(h’8¾Bd€„-©ÿ ‡µèÆ à…ÔG\E¿XâÞøS¶šIò;ôŸÅ,-q““r— š8qÌkòü=›x¼D›P1ðdê{ÐÉ¥Ž VoÛ‡€˜º(Ðm>§€²¨ƒ–ô?צfAºÿè›øŒí09 pn8qlå»VEïØnÑÞ^‚«ä±Iâø½j,ïçà_eàÙŸç/¤ï3o)_øåÛØµ%¾ƒyÎ÷âÑfˆ¤ ý2FªÐª%^ê¨joÒqnó~aÒätÊÃËWü~Æ_?òviœŒÿ§Iaœ6›'|ßdzY‹ˆ^ÂÝ5¯F¹ý`ƒSlxh‹¦âTâ8Àñþɨ踮I™Í,šh¿;5­ÉÓ±öLÁXÛªzð’íyê]ûªä)ÒG ǧÇé>Ì_RÇ(+‘$ÌD%Sˆ{kíÆw˜_‡Ú‘E´ûÏp$þ‚ç½zLL²óâïßN¼¿GžXvqâzŽYô¹÷Ðö‹ÊP ¯#ÊE 3ý¸£¸Êýâžh¯^²÷¢'žSL9Åá‰4ý ð¢e¡’ežm”X‚I ¯Ygtö~®±Ù€Nׂõ¨¾|oG6"öC“%¤ëF¾ö›,ü¤ïÈP¥3]ŠØ0]š')]6" m•ÇñNÛw4‡ô܈Ÿ-ø­Ž y@’Ð’ŸX+6…$«©ç›C×¶)±]ˆK"ýíÒèg¶ÐÔßñ˜Ë(7ê‘“Æ&܆£tøû¤Õ©]¢]Ÿêå•»óЂ¿´™ ¥ÐÛïÍ‘ÎfÞ¬qöxŽø·GšsFê;;:IÝÕÀ›1ø^ýPh{¢<ŸdtAçùv}äLbJ‘·ÁèåFb•~ªsᕌØUr_ÑÜbýåHÚb³äÍŠ;]°0~?Øô`ã–!êŠëO¨=?ú2µ ü»M–pžiR_&Iw‘Ôò„¶ñÌÇ€ŽÍ¨ùޝ2Îóàÿ] ÞT½,à·í™tÿÊíúÒÇS­l÷ÄX‹¤^À>@ä«È)ÃÅž%›Ìͪû`þÚ{¯:‘'õâS ¾v šê ÕœôuÒ¥ê(1Ü{º¿.טàδD±•(}Íù‘¶‚«¯Ü?})×öÔ»ÖXkÓæµù,uwúÚô©þÐæ­Æ^OŸjˆS$͘GÅùÔ8UWƒû£%_ÛôÇï1æOqgHŠ‘ŒÚ{l §î4÷0ØŠ¶£šú‚oÍe\x ¾Õi®ýùí /‡·¶ÜS—·nªÛ"vŸ„—èˬ³Ûè“.élZlƒCUl±ze–í¶lo#éׯ@’9ú*ä¤YêÌè ó“ÚIô”ünËjëúnñTûìÄc7dÛ-Ϻ¯—¾hÐû×¥_Z‡0ýIhÛýýÇY”ðð‹tóúÙç1†Äiç=„?†Ç´ _’y‚÷eÅ \P6ô,~-÷¤Œ$®°ƒøÕ÷I_¼T€¥[ZÎWþÆ«$IwÜqG€JU@$©*ðS䫘1c†U:ê('•*éÙo¾ÑÀþ÷HjDÒK+ݹúµmÛÖ]Ò6y2Q=äë–>áí¶ÛÎIº Œ JÆÒmס޽{;»ûî»YUS-é²5›o¾¹”}©h_åƒïÓÏ·Ÿvßþï<2 ÜUÜA ~Ö,& Ê$—¤Úâì³ÏF×p™uîÜÙñºœN$µ+É+ è€Û3ZI¯¾OëúUÿðë¯ÞD«¡u,åŸ"M3Ôz5<åR³\‹Ó»¤P³{”º¾àƧNI5ßΞžÔûó)0L´SzrêG÷õ“¾ŽU¦äN¿ÐÞaÒ"eøs}ÀÁÿÀßxzÓºÚVŨò½/–Ôº-‹i§4?J’S AÊ\ò´Ìc–bÿÿé(ÐòP>Õ~øà%}ÝÓ‹&ßíÊç&=¹ˆ´á"Nò/8|4“óÂÔ[~ðzŸ,áÝt\Óa]|(I mmøÇÞë è«)ºkbÂBºÃÝ%E˜u„^“&Ýþ|Sr±“ÆñÃ/mLzÅ[’Öñô°J® …Tó’ç⸧ZG2]OY4Foäpƒ¡Ö¾ØtÊQRÓ5Y¶þË*Î ÕAýÞ²ìkŸ¢[Ì£Né§,ÂïµÆ†úù:=ΕRÏVÔ/µÁ2¦¬£…5‡¸ßñs@Ä,а›µ RìdìÖ´IþÏsžÃñc|z÷ÍN 5ý¢>JGï|j𢾾KødþvsÖ#ò)@“Ï»«±ÈËK‰uô@¸]:š+ê8«®6¦ÿ”dúú“RêË»9}®bcèLŸ·¾’®‘¾=taÞ M|oÛ'¾ææð‡Ý%{:r,é¾Z*¥ê¦!-Vf…S°nÏQäJ‹ÛÖÑù$ñ÷g>H%=’¢ mèyI\Ë÷ÕgÙÞ—E÷žáÝœ›t ï,p’ôý p·fr¾ã­ë9úê®ÌA‡á„ÐÆÖ2ÒÁíxév]È•¦.eíìæ¢{xS«3$4¿¶SK§“Ï[]Ÿ ;m yúÊÓíERS’°À°ä£üÆsxþ{„¢ÖAå»À‹1ôÖîc­#G)8åu#‹V—¢Ê®ü\ ê™ï¾Ãûú6!6–>ÉIþzЂãA«á#%¯ÂMýûº,TÇrÂeuÇ×N×ldKìÛð£ÌQå ö%•¢5”ëló¤&#ù²³›O]N-À46ɾ õãx|9ßñÒÜ µ£z‡rÔt]ô¿…ÊGðÈÚåÃŽ<"'üÝ=û³ù^ôÂëR§ÔûÔ!ùöÁ¥«ècç÷&$ÿ>)ÛÜaDv€~xg/ToX嘿ã/%Xp›  4L!1æePóFÆ ñ;ÒŽ–€G ‡]ìÒò¿ç­SÉÊíb~÷âFœôë™Ë£x³jê0r¾Liú“ò#é½iñ º{Öµç{ÆÄ{ò ­: ¾¾³{þ=O¶QÁójÕÖÄw=ïû{á Ä{"ÚfùÑäáeT”ØéðIÅFžSá­ØG­Á<{œ>Iv–.BÇu{âê ÿÐO$gðMñ'é©¡*¨W¥•œ¿ŽÝ ÖÓI?†]S;·‡Ã”UÁ”ásž?å!¼ñ‘.º¾4£.T9¹> ë u̘Ç:ZÉ4÷íBxàåª m¤äÇ#ÙÎê“>¬qÐÇÇÎwÒ›ý´©iSöìè•Èd#ÙéÔo¨ß+âˆðÛ8ÕÌžç¹àñãÉ™Î{þîÊo¯TkNwýh3é3Ú& ¼üÊG„<‘ÿšè0ò2‹/á•OÕtOx1­ÆÅ´!J9À >LT|_¤NÇHš·Š>u!_!©%mBñ^ʆÄðø…äåXÂ@œ2ÛR=[„²üVByGγ]ǷΚslªN\»™gœhk¢ú¤}Á$Üéï §cÙšòJ¹“ à‡&ÀßÇS¡ ˆbSü] Ì÷Ëp¥¾Qà÷%ld­îÚߤç-â‘o8HKe£=_JØÁ烇NJuBGw6Ê~5©¹šI_s^ä ¤)Ÿ ûhÆãÎ!µGÊœ>I½ù ð¥ÙlÒ/ŠÜ<¨1„ÐUÿ.úxbÙûí­{j}å‚Ì&¼óïþÌOó=1F.ñê%´›sY€hÄ ‚qÖ&¡s/ê'ÙÆ~*f¿8ð²ÿÞ]L¶™kózŸ¥?uÉéG]Ó§^?r¹¨ëÅëWöåâMú uÕ»ÛIŒ)ÚÌ}ê±Ñëèaµ ›nÉÈŶãé„-¥üÚÒ‡K¢û*øgUÒ*pw'TfÒÎk˜GP×kUîɦZ¹]„š§ãCùÚv%¼|9u§ùŸ¿Áù ŸšF8Ëý¨¿½èÿûR§# _c^ÕàSæh IZÀѵT#~þÜmô½˜gþò-€ò\Òõo'«¸ý›_V¿› k<¯9áoF¼‚‚‡v°>¨ ùÎA¯´•©éÈ&÷Œùaø^„ßVAŽn4º´šYXÕØbNÁO¡ê²ãýFâNGq„S} u º`ì•W^q¿k®¹Æ]œ¶îºëÚî»{–6ÚȪo½õ–Ó½{÷ÝwÛk¯½æÒ4gΜ,°ryºË.ê9Âw/;ïO?mï¾û®]uÕUNg¯.C«tŠÞ{ï=w™›Òå_ú&9ãØ=ÁÒ',Iæ#.5Þo¾ù¦Mš0ÁIK]„.JuïÞÝ•ÏOýôS›@ºD~=ÃvMëE–N_Iø*ÌóÏ?ï¼èBº•ä•€vg3®¤¿W’&×…†Ú`ɧÆ$8PêKßÿ‡ÊÜ[ÆÕŸ×–õ;ýe—eJý£!û0­¨;Ô~^ƒŠÈ?~ÞÐ%žÏì¿ßå”Ï1=ÒÄr4£\Ò”Z“Ú I³¥&JÒ¿»¬$éµ6ìÚçRðª\·Ü÷Ú h®‹¡“«¡óü·%½¹|èÇô¯?ú ¾[î3Ý:2è/ é2žüÄ$%Ô«ÁºÏnÅÚúê#²=}¢Å0ðocQVˆÝé’­Áì_š8“2•„*J“$4t„/H,õÜÒ­0*.Ò„›‹3{7¢;#PZ}eç¹ëïb&aZ.äêW»Ñ$]*JÕŠx^‚þ¥m û "¹?Œ¿.wž$•sÈù³,;øÛk¡[/±8‹^F½×“wÍ.vm½19‡ÄÕvur rU”S1@C†È—»(*cѨAåSKë×sLãs6J´1àS–Îio5GÙïÉ[|gï‰Ýþs4Î÷$ ÿ +9Ô& ý*=в©9É–YY‚Éo tѺõ2z¡=¸3r¥] ¨ø*ü Ý‘Ëæ$?²/¼AQÎáyÿIªXÍy.Dæ¸aõþ¨WCÀVª3Š~NÜ«d•¹‚Ð÷VíèâŸPÝ—÷Ù6 0ÕJËŸO´™‚iÞKruþ€ïyþÆ÷žˆ ¯ˆ¿ßl@Ù‹ï×· ÑZ`ÒÅu-鯥Žçl~[÷iÕ},œ\•Æz ÀÄs.¾„“nùÃ>¨ìÃ{ʅݲèu»¾ìHÊ?nÞé”bç×û³ü>Þ3R®Ž &جp{›UÛ_ÈèGXGÆ#—¶Aí%»‡ß>äë0Ï¿û˲'µÀ¶rLj»ÁWwRN´#‹GúQy¸…¾IЂãÁ2Ö Žï´\_ßã©Û³x݆¸Þ&üN€Gûá¶fonÍ‹£_½v¯~3a¥#v›½Œê…wc÷S‰ô‹ñgì.©)(S\‹]˜Eü]DK<³)ú‚C¯Û>¡^äöÞ“´¼Ào*aŸ"®³,q«l‡”0é4‹.µ Iúà.½³POb+³S«zÛ‘v3ҾÐmŒ¤¥­ßB€;N¯QÀ«Pg“ïÔo6¢æ›Ç¥nÚ”ðè'ül‡±ß¡\V‡zc·9 ÜGÄãÍ”Ö.ïm» èL}LÏÄ?½¨¤”K¨ŸR’ ý7 _‘sñÿ9aóõ[€{áãjÃ%Æü•“–š¨M òC û²sïgÇ>ndžø,Éì'².%Ô¹ÔLÔÜÌw. ýãÿAÛ§à|Û-Œì{bV îOÈ×'V“á$@× ­G¾l3ÂQå=Ñ͸¶]Vy)aRv`òCëZr y ýNïð†”É;#ÚÇz–>‰ùzü1ÚÅïÆ;Ï*@_•…ø#~Uí·eRýUÏNïÌ;uå¨ã*}”“¢¦®Š [Lü¤mdâ‹0^˜à=ÀF%¿°“ÃmP Cý”Ã5gÛ;è/þ@ 3$)°Ñœh§1 0Í$Uª ì¢GRvØq…Âë`G]VÝh!5xÙÞŽw¦ºÑ6L½"¶Ž¤õâ™Âû@”»o¸(~IÚŸQÙmRø‰ßÇ÷†:' jÅnàbµmm‚+/ÒB[;´h§_x4àkx`¿l­XGE(—xÞÍ£6.t´I¦O½C­ÑÙ;—×ù m’/[Û¥a·Ø¶¶>H~d—VgíJÞÅ|iC_ý‚ȵÐÔ÷„ù–¦œıùÖL›-ލè|Úùª“Èi^zàãB=]~ç©Â”òϸ!@óå?ÊŠÝ»÷gž cCy°.-Mœujl-äp»"«955É~íc_GԈ̓-S/Úw™KH£ÔXäxNš°ªP7æ0(|  ÿ3/4ã˜Ñ.P¿Òƒ²Ý‚o§´­ÝÒ^oJ+R7#¾‹ØÐënWìb•ã9®þ¸w ½éÂÍ Åà—ߌ m„ò&/_KE—.v 3Hr' œ·µè/f`* "ÒœíE€ÕŒ4m‚ñ…“bu(q›%á“­—M‚ggr±Üs<úŠBÂWô%ê1º†Û“CúëØ½vM2îî¢Ðå¸ÒÝ~i¨©Sü!Ò·ë ›¹ŒqÆ+Ã!ðUŽw¡Î”‡.l:‚]k=…ÕfÞï‘Öv"õìó“ûhž?Á9œÖL‡ÿ”Ó'1úžÒ ›:sî?ù_Å>­B}ŠÏþAҜʻ¹ßÐܨ±É ³¬ïÍáÛ«ž;ÑOL ¼c¤-Z˜vîÁ&,m:ymš±i¨õò|ÎFú2Ëòâ ‰“$¾)ëÁD–†_š±mQð² óÞÕSis<@šgjÝØ­0£¤v‡ê¤E¸Þyçî§ ×z÷îm]tQFÕÂÞ{ïã.(9bÄw)Ûgœá€Q“?ÿü3ºqh€9”Ï.¨²A— Â` ùôgŸ}æ¾/5¢|qè´ÝvÛͩÇ·qãÆÙÆoìtÛJ•CCÒÂtñ (½ï¾ûœŠˆž˜,U¾>b©_4héyóÍ7»²,PU ¹ÈÏK¾4:i?J«r•ŸO¹avß}—®·Þzˆ f£F²¥K—ڹ瞛÷²¶–èþíÓ§Íž=ÛÆŒãÂHZv¾*ÿ[+Ÿÿ| DÿùOüÇ¿ 5'õé/u‹­ÿxó&൴DžŸóúhܲuÚ‹ºÞ´®6Lú  Œ…Ü}0X7Æú“;´$%àO³E$U©I1Ôýu¤æjûÓ®9¥¾åZµ¾6iôz€Ü‹´¬VO°s%ÿÒþ‚—PÕ4c-À"—< 9c!@.B[ä:;ˆñÈôpú#ád?¤IiÝ(·Ü#ô9^³€Â\·Ü÷|ª1jr=eÞç0Q™ž”fœþÿ²­ýC, ƒ”– ø:zW…Ô‘» eOí=]¢÷„«ï]ÓÄ.,š³ ùM0Mbt&ж€'³X¹© ŸJr¾áÛŸ:~µþtü/—¤Å?~'ž—ôHG³Ž‚O‚º{eÇ?P’l‘„‹7EþIô)Ô;‹Ëis´àeªTò6ä½Y‹Sm’Žù·¿ë8Úo¡žÈuμïH_’ykÀšÕ€c¶“úâÃXIbKu24õS¶Þæ²ö9îÞ+s|,äý•B~Kµà{· ØßÃïy Š÷Œ¢›Q}A:nÌPÙZ|ÿµÌ«ÅqC‚Ûªn#žIö¹ŽSGø®ê¡Týi¥Å*/Ã<›úJšXÌGÄà\Õ'»ç<¤Äo¨9 ÿ¿zq‡º’ô­qØPʯ“g_8g{Ϭ¿*m: âÂJæý0â…é<ÄŸB~MõFþ÷â·.?.U‹ÝÅf3•$-C| ¾žˆØ¡t¡•£Ÿ÷ØÂóÑáý ŠÙÝä£;À؇)êÁïfþÑÞZGÎ&|w¾'*¾r€hMÊþäLë"é7\b ÝLù]\¤ò©´èJnYMù!™ªÚðWt»^½¢~ Ÿ¥¨pètàôLžÿÂþh¯Œ à-øÆ‘@]Õ_ôÒ°;<óé¢t©bÁXêˆò-™oS^´¯ŠÎ”Û¥ögÕ^NùÌ Û”ú¹ÚfÇFü­‡Z˜(: xƒ‹¨¾. $‡Ãómmó‚a6§èiï»lV¹v“xÒÚâ¯}b†]†¯B$µpOK•~Ž4Tô³ù¨\ø=z3áV#‡ò¤ÎV.âÁÔÊ3°ÛßÅÙ:qá× Œ> ?·yüÅØïK¹;ý§j;Õ†ýQ5§Ù„¹Áé²]ž=Ïî)ënÏÅ)ÃÊKx¿€ò ]Ð-„vIÁîÁ9î{NíG¤ßˆM°/Â}íåâk(ƒðó™çÙÀ¶}+6³ÉU´ÚÕÑ mcXGÆð1HÒ^C ý¯—‡üzY‡°´CŸ¡/ ©¯|ÇÖœnC+ö¤>îô|UïÆ 8u,X)m‘>Dƒ½z‰n½ÂÇX‹‚½íÓbò>ÿðt^ò)t~~ä8Η42›Ocœ;jIùŽw¦÷‰7Tøv­ûÄD›Z}©­;}©*êQê»…úSŸ8°Y%åDÙ>ii­‹_C"™þ¦âtʤ‡}£$á½ùîb뚘˥u/Ø=š‡S&¾¹_ü¸Üž êVuv­)ƒÞž/úcņ̃‰àÍɧ×Þœ}E_Þ/ÌŠ-øòí4Tôê"Nƺ+¿Ùl:¶:ô+6«Ó^H{h=êe(ïŸ2{ ™ÖqCèõMrN,ØZÖ2~[¥ó2Çòå.•nñÊýh¡¿»r-—%´ZáÓlòní¤‰E˜¤ëŠM¿°u¼&üIþ¡üÚ"Ý©Ëß–g]ÛÑùlt—²Á`5ǹ¸jÿ´·õˆ¯¨àQ¬6ªµnԥ܊/ÆÆ Qžn“1h—ÏìÀSú]GMˆëmL”ï2S)º¹—¥Õ^ˆsçεöíÛgÒ,ýÄRÐØeoÒŸ«°R­Ð¦Mžä.aåoYÓ–—Ò*IÉÆÒ%‰e}Oß‘:‰ÆÊUñJ ¹  ÀZ·n¥ç87 +ßÿ½%PÄçèÎþ¿¦#L”‚€#Ýn¨ßú' K;±2#DžÈù€@YíxçÚçxs¯?q´£[r!ûvÒ5ÕœË#¼IH>¿²ëAܹ—Œ©—Ù Ðæñ8Ö›”½œN@ÉÜ#ÞŠË¿€Iæ¿J)5‘ ‚%:öÿù÷/£û«q¯Èp«Ù~/1ÑÓŽz¦nBÛ2¾³"?õ?WðR¾2Áàéãê‘Êô¿û3m =mÀŸHûöÁçÁL€gÀßùøXüßPØ`<™™Æ"s©£çº(Ã%öÇ'÷¬üOKkŽŽ{¥ùiØ„¶·„>HýP^bA´Mò6Û›ÉàÆH©ôÚ­LNÈëµ~K-\Ô'Ud¼hÁ¤NP…EƱ>CôzëZl0ù†¤*  }ÖÝ,¯–‹¢ãˆçR‚,¬L“ÚÖH@-Pœ°¬=¿ÙüjI'¤†#›ºñ ³¬Ú‘øÕ›|à…`±ýjr˜»H†¥«“•äÅ ú@Ÿ$.¸Yu%É5©ŸaRïÙwMbÕ:YµÓB¨`coMÚ†#}á=0RZ¨X±gv޵´1(·Lù÷ðdžØ$Û¾æTT)”±žC£!>Ú• Ïö´ Ù’är©_Ÿç·üXñK…vÆ?À;‚? eÛ’‹i p‚ÄKd!¾æ³H¿Ïß³€ºSAñw xýˆÓNþ“ƒÌ1'a]#7À˜hÍ×ñòšÛ ÷ —lR@8fSưXÓ‚w.?ŸÚbøÑ < íÃð*¶gOÑ^÷IÑjtÔº¼¥ó#Iý{tÜ÷¡²T€?°Døðž¤í;~ðGÑËL¾¶ÎöžõF‰GNE¿cÜÞF×îãM¿¶#—´° .±õ«ï´/ÿ˜S‘‚t›@¡6”ÙƒŒGžjWÒlz0_’⋉»Rºß,%ýÅ3¨vÖf\*fU»R1ûP†ýI'ü£6„oâüs$оæ%~‡òÚšp¬ÝªŽa=‹< ÇŸø ‰ŸàÚÔd·™ü¾¶ ´ /c×Ui_À;· 0ví©)àßéö`õívhêÂñc6* N{+¾6Í#Xk¡^¶ñ¯Ã ½j˜6Vtv[‘ÖÝùþ5Ø‹XL»Á¿P©:(¡®‘¾6z„÷?ùÑÞ îà;“ñóy‡gti]%ß$Ï­i ¥¤Å]>[ü5qÁ§e=©Ëþ„ϱù½­‚ ên¬šbÍïD½†|¿C}¹Kɨ ‹&NÀ¦ÊëHãðÝ!|w¿Õù%*/qñ|EÙO@b·ˆüV¹‡£òJʨ úhGD:Ù5Yyd5k‰TñRxš‚ç·>iÚÖËoPm5*„”e.µO—í2ÔŽ°€¬¡í© êV ¡ãKê8Ý7ßÞׯ†{ÚGRÙâH=}‡ëgT†úÍãG\¥”‘ô㺸ài]>ä`îiEýgÙ-U=‘nbë$~³Sb—ÛñéôÙ´w.©³$¼'}Ä}«§yeW~8nk[*ÖÁ¢Ug¹þGmñ xúíÒ?¼/HÒ6F^TÖÕÔeÍPìçÛVùﱙкœ³'ðï‚ò-±Ÿí…‰ªžŸ'½|G x ^®Þ7êMe¡6 Í®íÄñÞV¸ÑfEz/Ç¿ÛäH–r¯AÈYl¬EíúŒH«O_ô ó 6ÄjØÌÙþJ—Kî¨{{ô›Us¢>>¹Ì6Dº”\ÛvðBQìd{¥ÐÈ—$«¹š÷wÝçüã6’^Äï#YÞ†Pfô°cÝ%méy‡¤c¥¯Úñd­÷Ïøüx4ã‹ÐæP½À…WXÊ»vSµCm ÈEÄoKeNx/ž´'¨ ¿%è˜ípÙ<±1aÚpék¶ÕâVôõÍØlKá# ¦…h%„+ÛULßÛ#‘Kí'ᯘ Ð渿EÙÉ·(“ u¦}õ¡>GelÔû˜—ÂuºÛcÃá¾QÖ×ô73!0h#¥øJÜפNÇßäãFšó¯ç’âù1ËRë—cä-}Î…X«â+앲MPß@ŸÃ\a5T¨®€ïak#Å;?+.#…Ó§Ým§qŠåN_×.x!@:1³<«VcA*œNÄZ}ÌGüV£¼àÃjÚG`¾ÆË2åSpá÷oØ¿N¸ñÀ÷¶ †ý7£8ÞÓÝuicxñ6¾1 'ÊÆñsæ|~ýÐŒ3შÛ÷±øÒ·ü›OÊ*FŸ¢è+wÒ…Û8™/® twèÐÁ¥ay€Uµ;vÌJ³¤ˆWõmÃ믿¾ _è+Ê[»víœDðò¤Maƒ¤o,Kºš5kf]ºtqz}—¥\鍊nêÒWß%pÁt¬4ÿûJ€)Ì¿…Ôÿ/P>}ÿŽtû øËU_ i3Ù/ µfQ¯)MS¦}ˆ4D\YÇ™a1 ô•ô•ÙËê2$ðFGK‚$PyyI·Áú o‹t`]r•ô•žÉå¡EH¬(ÚéˆÊVådÕMêeþ˯z)X¶õzú/qø˜…‚Yn’tÙZþË?r}6þ.Ý¿ÈM¾¥¢#ÊSõ Õ$>mL˜n¸®b’èó±Î>À"‰¥è #Ì•‚¾jo’š?KÇúV©ÿÈ\ŽRO|¿cï_ á{YJÛólúvþSÀþ!©×‘8=ÙÎ7µ3“Ÿ¿Mõ—ýê @»–»(Ì$z7j‘¹<¤[ßݤ}]À˜qNâ¨ôeáÔgˆÏ»à-BëPü4¬3òz³’ ó% Ì$› ¢º /–áœ[}J‘uzV@‡OLÆúör .Œ…ý}™€‡Opúúnï¤.¼oJ7ï¥H {c¹*x‹I ¯ˆãÑÝBm¬]u_Ö?Ú+´J„E'[34Èyq¤Z@—Nù æ’ïXX Èiïœ%q¥cŸ¡è­&õ‡ÁŸje’T’Ÿm6 ¥{¿PKì98毨ôÒ‹e@±[¸èçÓÂ{ÐØÙÎþKUjq'ŠZ êògL’NéAê@_9%n¥ì5šyÔ:>)Ëç4ýý¨ªÏ#ÀfƒôRÍ*¿siÒK„yÅè¥ö¤ãÝôÝœrÁâ¬'nó½Cýx®Šý82õ1å}¦gïÿ޶½’”ÀÐ>Ès)çÒRNT`"f©Y—œÄÖŽ"g³°›öÃbT'TîÚjôUø2q ïe˜°ý—lG¨Å¶~xgòq®íšZÇÚE¯²æÉ÷pÿÙ•Ÿú¶ã½ÄéêÔè®:в¨âùM¹òˆME'žsXèïÊ “V©²Py=…JŠ-Tþj5Ò·™ü‰`3¨®øO¸](ÌU”]âaü¥ySõnë8ù¨ÈiyM—Ùàâ{íöÈÎÿÄ7lQüa»à_eÕ*2Ø^/&/Õgñ½)¤ñBü¥Ë»b0æù5ãÿ%ðʲøA¾vzp+þ€ö37Ê@<È(_Ÿ÷%öEd+$ ?µq…ã±'ý\@— }å. üJº§+oÄA‰T¸pª=ÁEQ!¶Ç ÕVóöŸg ù¾Ü®J~d7: z\^cqìþ½7#ÅpŠxe.õ>‰'$)ëø¤³÷´cWÁ'Ûâ¦?XïâïQIÔ黎)Ñöl¨­jWêÒ$6E>CçñIU'óÍÖV>Ø6”J ¤%}lQìcˆœú‘ Óéù™¾V—HÛ½Åâ9Ú£ôö†7¦ì¨ËÐV*}‘wÚÒ¼–z ?™ðèÓ$n”££…ü¥ìKhc’v ¯æØ6e¡§´9ñÜI~‡òüBŒ7—mÕèˆ]d¡ðptO`D°»SÄ%RêA á§Š“Èûäçê‚8vžj«¥6´„€?hŸÔ*övÁýø™îÊÉ´µšÃ0oÿr¾ý*É<Ô® ýjE€¾?1£^@_õIt7Ÿ›pW½ö2G¨!°üà×›¿™YB<Å_oWÜfz~õWV’®wr—?óíј¿µûØ´ú†¶µ3}˜;½îdýÂ[zÌmuN¡ýöñ—Îra¨½Ô3ö5›t¤ÚQ;€èõòºÛÚÂ+”Crß«LûÈÿЦžÆhïäåYøáÛ4µ†Gç ”^îõ—ÊkÉdâ?~owð½ÚÉ’½QÀRáݦ×duéáÓùCz‚ ¯<$n&<7¾ó"#ݦw¯Dœ‹æØSäa¯Äç6u‰Ê¡½ãôAVu]Þ»èã±ùù æ±áø# ôU?çû£xÒÒTÂsUà¦þ:ί]À.ÑŸ_eœ>Θd Ñ!Áù’s]Ÿï^‰ ©‚/GzÜÏ újS u¨¯ä½¿L¿×}H0#?ñ]4¤'H“ÊêXÌIß‚9Sõ8-™?€]댽 §2&nÀ˜nqúŽ僓#nóßüs-úïgà J? Ž|TJÖ.¦M³¥Y?•Õïä»úJ½VˆwýÔ-Ô¡†$ç–Gâ·NÄ+-V–Àß(]ìõŸÒñú7’ý_t=Êp–[VüW%k¹”øõ7Ôo]G§­Kþ* $­sa@ 2Iøæ÷ö¿dó/BêË$¨wÌ÷|j™´(`Á’Â-}+_zWøhvª'±0™¿+÷ƒdžN¢*]ÿùéRÕ±ú†ˆ¥‚Ød=ý‘PÎWËD>‡¿ccR\ÓÏÅð-ƒ÷úÉŸÝí½¹ùÑ͸ϸɷô]è&Ûsó~9WÊ3WJVõ®›šµù° mëÅŸ«¼éú'-uœý½x 3m¡˜ß&ð²¯òÀOO>itßÍ XîFØwYHTSn§Ó6¥ßWh-+þ MeÒ§ËÝü8‚m®¡ø4éz =¥A@YíM’ðÓà¡ ¤s§U#isÓX¬Î§ŒÄJW¶¤TtƒsP⾡ïþ§Ü´ÐXúÍÒ¿÷ŸJLà»^ŸWlgF¯°iñ³3:¸(ØÊ»¦ö{àI’Dð‰`žÙø­¥f¶}™Ù²Æ%HÚìØIÐå¦( TøÓº0+øñ’r½sU ­M²`$°òÒ— iñ‹ £å‹Ó iÀ¢>C`§ÍÏØÔ¢µ«‡œt‹€«›Òöpîà¬~-`(£P/»í»µã }þ÷jÍ’ŠË÷æÀ})]'üq/þZl¢ÝÀ5õõªú!¬#ôåùcžt?;ÿnA·?}õIxYâù«ço!öUA7W·/c#ÎØœEü&¬Û>ÆüAÀWÖI ð‰€¥-u¬Y€ø\§ûyqKpæ…¯{‹R½GÃPòøtYd€]9€º„Õ7¾5ÏN¤eÒ€â¨uQ p¿¥j¡Z:ÏÖäò±÷_O©/²—Ð7z}É ‘KøåØxºz§ÁÛ?DÚ[ÉÞPÕ,–·à{ÔAޤš”XÑT;«z¬½ˆ~♨4°rÔùÊÄâ›ÙüšÚ¹M~µEK[Ø­èQ¶È†äqy{…ïN†/zòëHÚÉ«@b¾nALPQ¸?ÒãÙ@Õ)n‘ød;ÅÎqÒû,HC]Û®ã›kó|0öâ}‘€€Â|Ô¥VðÑõvi¸GeàäÚ÷¡ƒxïA¸‡ÈËËVh×’<Ìv¼ÒžºeñÙ(êâÊ£TT]ƒÿÇ8Îÿ»m„^N§ó¶b{>û é@ÞÞ"ÜvAåP»39Ë~– t÷zìmð§…÷~ôD®»“x‹ºèDÙ~š+zø×µÍ–îkUaâNÁGE#¬gõýö@g¤¬½õƒ?ÞlËqø]‹|NABr¼$¦E C}È_/šô`g•÷mz;T¼|›ü=ïy‘Ôd!í±²?ï{ ŒÒ©«xÝûy|ÐÇI=zVN¢»{é¢fdóà¦<ÇÂרó­È&| úL½ƒ==+zù'‡jlX¸£}]N]¡V*hŸg/…ØUñƒg8±€G%}Æ©¤vÚ–8‰[@txwìàz÷:»«jêþDÅH/æ—4Y¸ÞÌŽ¯ù†<ïƒûïüh£ZÓ<⥥ÏRß­!?ÕûÁ[0“gW§ðgxKo&hÀÐsOæ#_ÃM‡¶Kø¾W[”UìJâ9$à;m,$oU§ó¢~žF‡ÛñË“÷â[ïaGº•§z¨?ýÓL¾é«\0kÏÙߺ—@:doªÏtåÈ¥©ÌYz Á{:έê¾{>uw=~º¿Ô™U]=œHŽÆzM\„»Æ™ùQVi’þØÃø“´¾+¿à+ÊŽùTYèk•øÉÁ*§Û™ïïŸúÃÊ·‘×g(o\üs¦½c?u¼°´Ö_ÆD]Mäó}IokÌÁ+ñ]Ã&P»D›ln¬Tú|Þ2¤‰›qÉåñ¨¬ù…ïÝãbÓ)–A”õ!±!äïEò7ÂÙ»?êÓ(?ȵÒ¨Óeߦ¢¶AÊ`Uò§¶G-'á7'ÙéBy:ß—þí‰ÄÉèû–lãT@KÛ èÍB¨þ¨x–,5ãŠ@µØäk_Üh9¤{Frç\>ˆ\-ùSŽïex•š õAÁ<‚IMÝVðÉ ¬5o;|Ìá׉°„OŽã9bZx.ÅÜ»‰¸çÛ\ý-Sî9®ù_Ãð¥ÚœÛì÷½Pß‘3øÖ…¾E=Ï5°ÿ•_s~â:Ú‘ò[ò0üþqž†]cDý~Jù uà»Ãò c§öˆVÌ©£¨=[0•¶0ÞxÒwúÇžRóøBÓè ò|œî‘œ»ú¶Ä $ûæE”wYhó Z“_]îsN™V¥¯¤•%ð?S+A߆«ª agçÚè«.¦!’Têò’:Óúh=M˜þ T»Øýkó/®ÊZ­¦(ùèzJ}uL]Ôè+?‹ôz©/Q':ö ù ÕHk aÁ¨.Ÿ©L£¤©¹Ô/øÊè5¹½•AÇ']¬ ’^ÆdL·>i‡tíÄäü¯€¾ŠK¬pJƒ¾ŠW ¯HÒ¹$Ð÷¢P©$»‘‡–¡¶¹^2ï¾”§»áÛ·“yØïKœÿ_}•§NRQ&ŸÄ#Ýü'K ©ó\ÐW4]oŒÞ¿ŸZðHêX ²(°œh,Š:î}E£Óüìo´äz<¾ ö`ãhñÛS5“÷ß‘:–$ï . Ù*KGpOÐæ¦“_úÖ?šxÓß¼äÁ?ßSG ëí)LÈ]xö ê…–Št×òÀuwùV”$þ«ÑAèÍí ˜«”UØ$-úŠÔ d¯Õ«Û }’×±X®qî­ÈGgêJ¤öìHúd×lï=ð7 ô-z?àâéÅ#þ¢„EGô6çðRr4푘³A_9z /†ðaJíÉ·gðÂâU ¯È¹V«`õp ÊøØPçìTh^Èd>¦қțw¢¤ž~ª¬5>‰Ÿ£ÑKúªïó›°,8ªà&¾­ÅÒŸÐW‘ϯ퇣,ôc“°ëäÚõAôû_Jš*ñ)i֧ŘGã'ÓrãY ¯.®ôj"íI¤D¶æ¥ Pݼ^ŠÿªŒšXµÑøDÊ÷Ú´çü^Ä8¾w$¿á“)ßÇxg@ÑÃYüÜÅó^6÷€-oJ¿=ŽoPHæ¼iç¼uü>óz` ŸyaFñk9@IDATœ‹þPß"'ÇHYôå±ßû’üï‡ÃúÎùyÀE«h0×n@ íH:wedmŽ^æŒ W¸ ,m+û,¼¦}Vy3G±«m:`Ô4€²Õ#GÙPÊÁ]$‰„ð³Éì°²Õ]ÙÏ+­ ­°µÊùNòYò·-ñFLåáȵwÆd ñ÷ìcÀÒ…¤¤¬úŠ àÏl'y%pýj$RopZ}3 !šÌ£ÛØð°Ú íNÇß“Ïa¦^fÖ\Š9@ÉwÐ_Ú2¹ˆïÝNÏ7“z#©z4ª@UG­(¿«±ÿ)]f$jü²¨lØxoÓ9ÔÍv6TÄÿÉùÝËÅ…¤IÒ $Ñ›œcå´ÃÙ…VõH|ÒÏ,ÐW—PÑX9¼™ œopRàûXW©¿_‘m–èˆý¾ðlû9²+ßü„4:WþÌ%-·8)búª¯‹ï"ûp G½W-?×:—oUµâ~#]ÖöštIW Ò€Ë¼v(¹Øö®9 é&;ªüXûpév6«b$'CümÀ÷æQ?§Ržê+(óI_ü¾¡N¤um¤Æ¯¦|ŸO»VyåâÚÃi»Ž¤ûh[Ëðü~ÙSHá‚â›Õ—ÃGcÉ7í¹”rD3ÛŒ²ùŠA{ùy™wêÏÑRêñ(+åR¹í*TÆ«ñ£¾c÷Sf´Õ—+{Ì%J yCó+Mg›§±Úˆ‘ÎLõA‘Mùy8œœfã›~ä…‡ 5óMçíÝ–Ø!»Þn^º¹=´¤Ô.ÕÙ‚ý¯t>ÃsÅ?8?*G¥l’‘H _ìÙÅzò„ï’³ CžEºôN`vx|\d¹÷'´dKhkâ×\äc@~oôQÀoá3Ò•âoÙO±¾ž‹6 ØØQ;Ø'Tc«&'b¯þ¥Úé×÷.OkÃ÷Oòü§ÿNÄý_á#v³fAû}EIÊsrÆM3ž,½÷U;b7—º{‚'õ Í9]:—œN•ìÊün‡ò|…wJ«t&n”‘úRWzfÐ'·BZ¿´¼}’ŠœU˜  p’`½d[»¿^kÊ}eY|•­9Úfks ¾Ø vI—½üdåRó¸m^x¿M‹@U­‹ký›i¢þ%!/ž*†WÔWCW`NUß›|ɽëÏ›ÖÖ‰ò-6×Lå$õi©W±!ñ÷mÿêQvCüjq1é‹Z;€)êWÕ¹„D÷‰!Ζ1š§ì£TœY͸Q—MU¸OGx ¸òd;¦ßrAßOÙ„–´yˆ@ßmÙºä¯sjM)ŠÛúyÝŸ‚·ÆÐ‡IHã »âÿ’7yªI9ŽóÂiœN0Öi##Ò »¦ž}ð¯Ú²ÆAŸT¿šÇø>ƒ°WúoÞS'v²@_YS§…„u[öž·º©o©4rkÎ?y~ãyIý@?O[‰÷ÞüÛš¾UeWìùJN$Ôw¢ìŠh/*ã¨WqÞt1­UMžÕo4LäÈml7ì+¿«²÷4Æ@¾7Ðî® -Ö¥ÚɹÕ\Bý=êŒß—_¢âE¤øîOcÎ"çOeú]iΉÑsiHrn¥ÄoNi®|­·ZÀˆÒ´’þ~ 4£Ó\h®[„bN§S0ææøi  úý'Í,ÿ‰Z/%^u^ ~Iüúzñú—HyÜÙn$–e•(^x~k$®¿â¬ôŽdiC›ûŠÊU³ #,ª“\51ì¼aØ„®a[$ëN„6„¯ßx&>ƒùFy:¹ˆ¤_ÏÃÊMƒP­$Ã_ÉÑ_Ó‚ ¿4|-Ü/ ­ºéT“A „â«å!Mè÷ãÈÖãH4$ ¾Qm¤ê`ÛéÃÉ“NÑìF9¶`ûµÉ×ÔÕ:€N£©·}]½)T•[ ×–‰À'ÍÕüÍ#éè칑ú>Ÿ´Ç÷þ ÝcÜÅ‘ ¢#ìÅèNvgÕ!¨ÍùÁÕß '²ypN‚żôÝ–.á+Í!õ•["cÙÁžäR®y©„g.Ó92Ì>w—O±@.zïv¶çØw  ¨³‘Œ´qÀqï8þeê{d-hþ /°æÚ숑ޚ‡IÇ6ºxŠS)P‰2à*ã|™À³mn3R£mÛD”Ó‹QÙÐ?3ðÐ%ŽÄÃ6„vqMX@Õ{¸=‹›H<®ºx›gO €¡2øXj»°èVâ߀r¡íßlY-ßÚvK~bs£×P_Ä- rñRç…ðQõ¤§Úµ©yv4q2ô(ÆÎ¶ c}­{yOÛŸôœ[£|¤)<øIõýŽ·†ºÚù€n{Ýæy¨Dh§\N(Ë©ä5ãUö<õ ‹óY€uW³§2ƒ ê+4VœÆËÌ_ñüÍù#v§½*ÝÅN:˜¶šƒ[ ?=¤‹‰býíÔê“m!üùˆfrºP³„rsa·ô‘V6ZÝfd„~&qyÝÇÂÌ77€¾‰]`S~÷1nHÏøÏâÛzúÝÕ˜ÞÊæì ¤içGúçíø…¯EH§¼Í:Ï*ó7:ÆV‚ó~ÊX¹6ÁEqV­öú ?ò‚r¤L|®§®f‡ö$^¿ÀËr'}Ë( • ½ 9;+tkÞ ãx€šîOØÌúp"íÔ&U[/êuwòüõå d¼h+ yRÌžƒæ¿“)×-Ù€¸}É!ÔFÔOÊÿ'üH«Û<[˜ñ*þ90Ô…y)üØiø©úlÆ?‡ò ö%dó9í"C©xÏõ rXƒ<¤ ­ˆÝ'ð7ïôJƒï§µ­ˆ×\yJ_ºµç·~êI~Ǽ|´&ÞÃÿç0ÿÉ6]¢Ñ–ÍXåtj÷I·ÞþE0³£uBÛXuhC[Ðès¹±Õ}å’ÏÄÜ)A{Ê¡pÎû y¥Ë]IÿÇJ ³ë¾–?S+Aßå/³úBA_ù‘"ÿ\úo}•&MI4Mý+¤á£>*Ãé‘¿¼Éòö! § ýຠgöo­ë’mS軳„kýJ¥€È—(°”ëG³,[èê5•Ñ žŸ4@ÿURz? ä_e9Û-Zkc’¾ÉvürA_ùrQ>ÐW~¤ÃWÀ²¤“Êe‘& ^A=­zýrûÔqï3ÿS7&ÿ¤)cc4}E:Ö¯ßò’í·šä‚¾*MÿIúgA_RêIþl×ê2¯Y†­Ò`Ø}Lh%ͧ‰(ÊORî*e×ë¸Þ‰Ð7·à¥QÒ’b™@ß-4³IÓÉ–´ƒUÒu-IR]B¤Ü£ë‡’‡‹6Q;’w7>º~óÕä=Hj«uA_ù —Œði£Fù’…[ä@wüpzºž‚yЄ_‹ ò½:Ðàá³ ‡Å»éþËÿž[Ðç}å®KÆ|ÒI€ÃÝB·©ÿ©Í Ñ%éö&Àp"‹ØØýUÐWÇ`}Ú(þ ¯@«}«zŸûÐy”r¹ú5Ÿ<ÓlÿÕ]¸µpÌlÓÆAßÀ„;n Б—šÓÙO»øiH¿rÕ¾¤Fj¶g©[ß+ö·Ÿv(Û–:¾û)éÞãM¤Œp–œ 4²{É˹èÌt¿Æ?´g+;²½lý]}-áÆúŸ\½“"Ú§Z*Rþ‘!v@ɽvxøpŽŸrRý½¹”+Þ ×Î@s!»Ze…¤àÑá¸k! …¶`° `¼›÷ní­KÎ@DûØs±12C1Í:Z †MûË}4±aå´Æ€Ÿø¹¼øuµ.uý`À#@Eè+}¥5$1ëÀ¶TBjëÙ¡mÝ©–˜Eùiáü  À)ñTÁXâ ’´iºxú}ùt–¨ Ð`÷ãRìŠ>µàg-üúsÉÓVÉØ"º=N}Õ¼ëʧ*ÜŸãÙN ‡¾A ´£¢CIIŒ‹F·ä²´¬‹6`â“ Rt鞦/9àå#‹lèë­®tÕ7áCìžé)à;ªÃj¤òýøí†ß®üηŸQ± Ã2}®fWëfª¢ôÁ_úšð.äé~lVlÛ}Ô’´û-¼)iy”|Œ!=ÏÓ·Ï·›ì题UZh“¢ª·½R:’ ¥-­{ÕÉvÀ1¤vC7asfV±ßœbõZô`ob -J»¢Ì*8eQ"ÉÄBò ètMÉ{¤ÿ¾îGʪ­Æ¤ ÝqwšWØì8$õþ^Ð¥Ì9žíŽŽã:˜ø.Afqnû8ù*€-ü 9¯UÕH»³h w£|j}šTØ¿Üf×v¡òYó´½l×Óî‚´¿úÉøxÒyˆ ïdÓãmì"•É[©ƒ‹ðóÕ±­…Ê÷·õbÛÚӀç(8õR2‰¤ßO:wôÂÁoÇpqÖ jWØÓŠ¥*èÕÄ[¤ƒ|† ï-JÔÀ·½»"Ì=hT¤7¼¶+ùÐ`Q¥œ*ȯ¢Ù"Ç`Þšøá²ÕÒ}ú*“ ØØéOÓ Gˆ?LšèÛ‰HÚðÞN,u`ù!~;Ã÷:/ÝâI'þ…ÿþ÷ #þðYø¹ÎÓÍ›j¯6­"Ï€ %Ïág°=Xµ3iä»IÚFÙ–”¼XHz“Ÿ¤ c“ùÆšÄý4ÒŠmÔ’õ1_fÝdmËv²˯äH©mîo/µÑ\7»ò.âkJàG§çåäq.€Î#¡Eð\úŸ8*¬6v§äß ô©Þ ©ïèm\¢ô¤‘|Ú^m²ÐFFŽ·Ã"‹’¯òG#–¡êSjj±3;‰[§{û8=äใӠ¯<Üå@_™~$Žq|ã*6v±‰!Ê*rö1Ê‘v½„tœJþŸ”çêBúž£÷\ï¤Yëaʱµ§œ)OÀ#KQ÷Y ¯±µ1ÏnI\î…JŒâ¹0C(ýì`ßh“ z€í.±"kØÚçÐà˜’öé?ú…¢Ú¡g ÿŠÀgâÃèXž­y§rfÞt?Œ{²±ó‡¯ê(L¾‹¿óœ«U&Ä%ŠF>ôÌú[s1eøø-F?ä¤W夯×Û¾•ftNš}¶ ¼)sÁ`¯ž¸X$ü{2·Ý4T`Å!/'Z%êZÎaþp€î–éq3øå\óµß¿þ ÒPæ@OÀ‡âÛ†@_Í™…¾ hœ:Ï/Ý>Ò‘©„jA߆¡<ôUÀêt蜇Ԉ¤7ws\ò¿¢·¸ô•—_ùÁÃ>©>™GlCÛÉ–tŽR§WÓ£•8!€àÎê=Õ¾êÇè-µï+Âä$½¿LÇ4›çïä‡þì/z„,ÐWqHÚ<­¼¾(µi hÔ®G!Î ÑW³•êxзֳ¿YŒ?-Ó–ZWéâv|Ûôkú1ŸMÏ…õÔiÃÜ’Ï2¿U,³Ï•ÿWJàsØ3õ¿’ØήËЩ¯€ÏüŸŽ¢ž!¦Ñ<3Ml”40ç’nñRöpë¹0•V©åäG™p{Ghƒ!6KU‚&x>ù*ÓäÀòRPö vJ¾üAŽ›Âøä_ç¿ûOOmßôíƒÏaLF|:1 °øïºYþïèeX̺~œz \ªvè ”,kÁ Ç´9÷Ë™jÒIYi §¼É;6Wksá™Nü-’›$š÷#¦ybÒ1JIü Ü ªÆ×m2pm@•ÉOµ¯ÿ˜I—_e“mkŸmUß[Ñ{y]2¥šžq÷UYd,Ò]Ôæ÷G.H²Á[™ueýYº$%þ3ºü#Ø.ÿ Ì›äFÚÀ»x¥¤÷,§ê}mO· +ÌXk:©I·/½,=ƺ„°!zše@96…7.Lâ÷佡¾£†“èšPS×r'rHÒµ~ê”vRËañ ‰ÿ÷@2¤>’ª•]åXÏ8Ù¾ÊJ²%—~ÃbTúxã¥,LúÐoª]úje2þtXæÍ2—ôE.ãb·uíÁÀBB7ˆ×G~¸ ]ŸßFíÃ':/'Å®áÙÞnƒ÷ÒQ`‘nˆdq‰gSç¯\>)=þF•ì.Ë×¹²Uüâ#ÞAŠt3w³ó«Å']0ç–=mðÁQé÷Ù~9îº`Á™,”N‰†[;ý0Þ¶—¢#‘Nÿàc¢`G:¾)ò$n1„Ö·™HUî%Œðôr Ÿb½¸}~tõ!ôia.'ª²‰Ô‡üÑqú¼M þÀÛÃéòx31ÙÇ%³n±}l S(ñ¡½Xp<8Óý ¥Ö‡ø[Ib—Åþɺh jÃÈtCÅ^,Èïsïf³í³¥ÍÙT[ßBeKVÛ1|ŒÝj…tD?9ÕùSŸ­&¢.´%¨^=°þÖ¨jÅÏ8ÿîØ~|æn,È $YFã¤LÓ>¬bSË€‚ôM:ÛëCìÏÂÁô!kÚ[5w»ªÄбݱW½âžE^R§ÑàmÇÄ‹veÅq¶YåÆž/é¦ÍM½Ëñîuí¥ÈI¬³w·3cçX·ð†HÉna§‡i#R%Á¥XáÄCöø*K‘T¯²‘%„~!B~ô6§¯èîmMÂgØëØý#¯ºh+-‘µ€çÚ”×'ÒãZ}öðVè#‰`m@Øq:]%Ȩ¦cæ±»9æ 8†šÌ軲Ó%]‰â§ÚᨂØÄõêevzœ­R~U?QôWZo]~†ž[+že)I­Š"ÙN‰úX¤GJšjˆZ9`ëWÊtñ?`CÐKzsä Ìcðñ¹'J­\`{YGò :-Àï½<;Žø‹GÙ£¨¦ïDZÔÊHýWê V-z×Ö ²¡±sñOÙ&¦Ú…èú=!\doGzöÛ-|nü?ÛÚ {wHÓYöFÓûìôò],T¶.m˜)þ@"=c”¶WM\´½áÓq¡:ø­ÁÒûÐØ\²H›®ºÛ“ë,MúÞ’Òóf¾Qúi˜L™m‰åº6«¼¿]=‹øR«):ßb z ‹ôsÁ$< p}PôX»¢æ@Û)¼•g/Þ‹² ˜‡O‰§~Vx"ßù“º~ w@ɪñaÙÚÀ ¢Ä ü¡]IÏkùÁ˜©;Iá P`¯rE’פãØWÑ!²ˆ:‘D¥ò U)1¾ƒ¤÷á®¶@¸…¦0àdÜ­rž|»ò:âyó/^ýQÿ:a ÐÇBílNÕö¶ÛÒ^ø;ŒïÛ¨Qr¤6 þ‘ «WÊ$|£üì™Ý¿ÒÏ·ÞEÚù—ȱV>Êö®Ü˾Á<#4—yô<›Ž(Ô¸úcX›ß\¢x™g·´Ì Ò·ôï!ݺ=ú¯¯æÂ3ŸÎsùÙ„<¼æYE7µ‰ôq/Ðïž½tG6šdtgZê Òt€ów6qeß©¡1Ûè¿›rì»wÍô}RyC:í!~fƒãnú§šŠù¯UÓ¿JU;?Ïó[çïg¶EÕî6½@¼y=é]BZÚz¾Š¥Û ÒétùÖ‰„9i pÞKkÆÕ]°õ¯1xàû)éî”Übzâ(§„6F¿¯$_-B½ÚZî”КéÀÍèûw mc=QÁÈAÚà}m<¸ãüÍ©OÕ÷ÇlÁ„í–ð¶¤s¿tH³½¸ä0ïH@\ÅAðÉ0.†—|r'8îñßx.àGZ4ï¨>ƒo ¸a¤Ívjò ßÁ̬Ö"=/ðí]3~7AŠ\'hƒ`¥„bv'¯z>Æ&¬NÏÌLJÀÙ/ªG"àÁ\ ÞÃfù ÌcuéÈD™eX':†€½š¢ÕþTg$¿™ ÍZ:þƒ¬b7ʲ]­c®)z5õsv®mà½=eæñ€mÆÝcËÌëß6èGòCw©s3çZ¢_KΆ +\é[I­Ÿ€Ic¾$VãðüŠ$õ×R×ó퓱mùh5Êæ];4y›}ÈÚÖ˜¡E˜ú²ûsÚ¦+ü»ÏW…SàµÍ3~}ÃýÉGìÉ zß'=ïJú'J@—Þ¬¤ÿÝø´Î¤ÿ¯åEC‹·ìÿkáWD(¦Ãÿ¢‹ÿ¯#M~ ¤JÇýÞ A80šrG|観¨/q- Äİ>*¯Ça ÃäY“ɺGâˆ4HÐHÿ#ýº¹z@59kŒöõ›F<>œÁs½ €.¾›/u뿟‹yQÞƒ´YN0AZQ¼ö“ݶ9å ]éy”½Žy ÜÔ%dA’´—è-žþ!ªÕqZûÕ1›‘äòíÅݳÝË.h*ýEf¶¯§Ò:ÌF±ˆY5ÛÉ® ¹é}ÆV’·~ièxc4vG‚±ÔRC·#çò˜T'ŒHǹõÚX_%^Ém#hOú²Ÿ6²º`N´ˆî=º@d9H­s°/åƒyi:¬ts—Lϲ¸ðûŽ|ÑÇX4‰¤ƒ[\me€:ç’ýçöèÙ¼‰ÿ¯÷'ÿ˜›ñ“ºŸ^÷¤WúTêJrõféç6/žfq$ŸI²KŸ®Úåî¶n?fž’Ë¥˜amXX^Ìò¬ÜÖŽŽÌø›§mgW áJ Ðé5ZèÌF‚|uÛ#ÜÝÙíVVjÇ¥Þ™îÞëû£cöÁr´ðqxíä6&ü0º¨O¼ä(¤ÅËÚ,ž&zïþßijÎt ø—®`ñXÚ…p…où¾Üs‰ÕRo"uE­8Éà´s¨—[”-F‚n u}Kõ¥,h‡àØÞëÅã'aÍ· CM\ö…?ߊ´&NRGÎïÙçÕ—a@ªžÛ£+eàXþtÔœƒ.À‹“Ú–,DÚÄ’¤÷[ÁÒ½Eh üಢú^žßÙL€¤¡%¯€܆Ýxð¹H(y Ñ,:ÖŸoÇS‚¢¹©mˆŽÏC7E™úËÝi+7![73cc,l¯„ž·«.ÂG…­[0~-rãÏÃÈ­HþI±«íÊ÷Zh¤Mj_²3wYWÚ*¼ †™ðâDÊk{̸Çô SP)@^Y7ÏìÿM>hW±ß¥'X‹¿ÄU”ù+$¯®´©·ÊË1ÃûÑ›\ÿ7-ÝZÑkÄ·z^×±7"Í­/Ò†ß |jߘÖz'}àfÉá¤?í 4À=9þ*ÀwgkÞ8-ᵡ=Pp›]dk(™´Õ3‘úMXsæ%îµut]ƒ‹×® }j×D{Øbê¯KÍ™ägafw€É¼â±^®Š¯´T“÷írõN­ÚŽÅ#¿4¨BX”<ß@~Ÿ²®Im/a{—~Å pëÝ ¿EüÑíûŠ+­@ÖMÊÈÓÄ}Ÿíi§>m‡");°ÉÏvàËô2;`7¶7i~Î¥Îý‰‰o)óŠ8¾ÝÉš†RVâÅ?l¶ÆáĨ•HYMÅv|Å!ðà~-9ûQ[Ø;äí’q”ÛëáÔ_†[äOî‹f€ a´eÓÎ.SÿËŒê Æ‘¦zâ­`œõ-¼Í6Hµps Ço²V§°EJ?‘&ݲh{Útº¬±?¥æÆÂ=ŸáÏ–‘¹Tmi§¬DÑùghuLªÑ"Ê€üB’•H Žî?Ru½uAZüØPSçf%ŸÏ˜kËø”ó|:_×ì=ÑK­'$|!€žlÂêôbK/Êw:íC|kñÓ÷í@=EîüJóŠ·#‰ Bå'¤ý'ž)9 ç¥ xY‡öAßÚ¨~?qê™öêÑÚyüͦnOËcŸ¶ª9mh¹h•ú}§þ…ý‰ø¤ð&ž^»q’c\“0@­$²sÉùC;‰ÑŸ0§EÑVŽ[àUs7ÄËEiC¤‘Ó:ù¥Æë‰¬ðu:Öãø7­u/Er†í[üš‹H'“O¢ÜÎ`®$’ð˜O°¹Õïgtúˆ±-—1G”š™|¤%•Ï¡!¿GÍ µ’V–ÀÊøÿ®Jȱ¦ÿ t÷Ýw×IFCýVг€ÑÜ[-ˆðI¢3Ü †m΋¦œø´£†AéXnLïSÚ{ÖCÓòoõÔ´ja–ì—¼vdôf`0Èöá½é¢{Ry²ÕÑv‚ ³*/µSµ K­ù¯Ö¿ÀŽ"~ÁÁ«6VÉÑ…²'òAÇÈ܇ò–ôgc$=U·ùƒlϪwÕÅ7ÔSQhšµ ÀÄ5Äb*¨LÇÚ*:ä‰mÙ­$í[øm<¼Ô-Hò¶!’D»¤W|õ% ùõÝ$ýë«`h|¾¤R¤ Êi_'à–+‘.o’h¯osÃ×=­tí ð$É3Ÿ$u+ö)»û ‘‘úÒÞ—åRE?¬–[ùcò}ðD¢Ò’·,–ͨSGùS}ñÑâ¢Tî'€u›3Rßö[Ž£t&Mµ²YÑ3øþÀ×ô«Ó¿·a7¿Ÿ–ƒòבºÕEµ´.ÆœELì~[«f0R«‹ìÀÕ‘ÞïÁFNWÂKêü#—gó«$cÄ4wGa©¢+•¦tO¹‚×=¼éùq©ÉØõ¬µ˜­ÁïW“NçßBÛpÖ½Ngg7ìµp]/Ý?¼„ˆœKœwä—Ùž,hÇ&Lw)ÁÙj"K‘8ýÉ^‹ Ç/ ¤ôÎF—ó6l8¼ë a{ÜÔ~*zD'%ï´øù?÷%Ë\[½—6{>}Áá“àå‰%Íõ,¤š2Ê=Úä%tiJ±@™ˆìd"é–âøoˆc°ÿ?€Ü_¡­¹^õVTà»\¶¦£ˆ-B«ìú>|1´ýßûįE“–ËŸ³¹ÒÚvnòƒí³¤ÐRjKâÁäÛ<¿¥ünÇ?}³ÝJ•Sšä/'ëò¯ )ŸÉ±A.ylj/S×Á1ÚyÔüç(¼üw"F½§[_øHÜC¤Õîž×Ì_@¡ž¼¬ûEò#û8ÔÖÚ ÏNó‰ÆºwßfÜÕÆ×Í¡–èP.µ9\–ªÞ#͹á#ì*.:3ñ 5·)É*+,œjÄŸ°Ëâ×qÁf›Rp¹•Tõär³khà§­e­£#í§ð:¤íIêvñÅlƒr$!C](ƒ{lpÁ]öPÁ^ö#eo•„sü±Žuhòµø ½½éƒCÜÎ)†ŠlgT!¼>–°wxi+¡|ã¯Ûcô³“óln͉¶6ó8þ­#éã8º}*y¶2ø:|0i \Õ\tW`gDûbGYVM!½ý­OåùEùUÀH×`¯:‚zÆ7ΧŒ·!ùÔ]u÷íclž¨]XèÄ÷­’ö•|Ç®eæpPìR['>‹÷›qëÿÉ•ÃíëÔÝö9u-•T ÈW'o7” ´¯i´¤›[õ}\ºvíÌ‚ø¬ØÑŠewp¹Ý@âÖlÄü›Dº¶µ#«®°{šÜnMogK‹óÊDRÒÒ\4‚öß;> ÍƒÐn¤á ÏÏкv^d{»­f›*|–M”—¬—u/}ÈΫ·‰ÿüò½ÿ{g`UÕ½ýunLÒ¨„Ò‚¨ØØ…(6v€¢¨ØŠŠb`bb`‹Š‰ˆ] ¢¨¨Øb`¢¨(!"’Ó7¾ß³Ï=wî½Ì Cèû¾ÿÏ5sî9g×Ù±v={íµíZt_”üÙ®¦Ì­| òàn’C½GÇk3Ú‰»P[rx>áV ä;€>ÖžÀRmLb.y¬"TÂ"o$m@Fü@:‰béZœK>Ã7YDù~B~Ü>æ+l0ÀoC ––ô -jƒÂ.yŽ>5ÜB»’ï÷8â°’̳FH¡Xu:Á+‘þo…®àbÜNç‚<Ü{­ñó°ÿ^ׯ·ñž‰„vÛ³p¬-ªCšúáw6iù ôƒ9G¡ØW3 ooFß9œƒ·v-ü…ô+‚± ß+xѶDÚzpäL» Þ}×{Ïv¥|ƒöS*“Ž¡¿:?Ù ŽøÀ.à©’xvçûŸ¸ÅÞÇÉ¿WØ™t˜×ß¾-¸Š<}‘ü{¸pgvM]áqÊ­¢zˆØÅ‘»pû aÍv–~ý“Ù#?— +í(ûAa&>#LÒÞŽðÏLÙS7¨÷NÒ/´ùt)qÞÅžô(/Úy _޽2¬{¶`©Ý­>Ò# oKÜÜJ™‚ã„w$'¸0Û³Ë$Füæ¤ûÒà¹áê2 ­¶–'p9‰:+G=hãüi/”åS'‘î¶èCÄåu„ø:ÖÁΑT¹£Æ|ëdž¸Ç/äžÇ%ÞJñ4OŽB¤/´é„oE©²±Ð1Ä™|\Íý+.òå57‡Q~îZð¿0Y†\ßFÉ"xÕ#}¨¢i\ÚžqA$½«X–œŸv¥vüCêÙšáñ´iðÐ5We´ßMH‰gm©“n ì—“T\”­‡c•A]ÔË™Y$”³Ž±àÀ`·‹E‹t·WçK–œ—üq{<†_çX¬Î×õ©kÓvÁ ænç@ 5_ŸÏD -Œ7’êã:ÛM Ý€nñ›rK¾žín¹opá"oëMŒ ꡱ]‘s0h‰Y (dÁ&Œ:¹¥.Ï¢#þQÚ¾§éßTçÄ_¨™õ!-äkg—„AŽÁçRøOáä’‡AœY€²:ß"Pž«ßÿÍÿÁguŒÿ4µ§q˜YC×ôwÄ£ƒÒßrR©¦“!ÝàÊQs¶ù57CuØñáglhJÊ5p˜Ûn Òö&†ý5RSLdØh«ÿ¸T#«F95t.4¬mOx®¤¢ “rÚZËí+z½•QFÚ¢-i½¥»íN©´Ë<ÌNÛË%i(Ê.3“ý~¦{Iréä,É‹ ÚÖ.YGéxjùÜ »à±›á.W5†IßVÃËÞµª)) ¥«!ÖuIa.ë»þ&ZÙWýÔqHÂW[««N¨Xu¹Ü¾h·\’$ ’—+K5ìUÏÀ4(%ªÉK k«s5¹—™V¶oËtks·2æ’ð–»p!ý’…e:´Pú«3)³eš¯ô³›ž’í=ÿ-é;»I©$osI¼Õ—Io )¯t´¡n¸C|rë”èn9ên2LBBeÿÄ|[äíȨïL«êçðð”&U¿:3Ï®$[Y=¼’æìÏ`´“'Qâ>©¶vO@ }ë}këÇÉHñy´µAmÈm“£ŒŸ7Ƀ]¼IÄ:ÈðÆÖÛ5™Ð2õ:hÀ¯?ð®öG®e‚r²oäõ ß”ŽiÕNÒO¸ÍÅ$ù€´‰…©›š¤ $K0isÔ‰_ñÙŸ˜÷&¼·éä;¾!àþÈâa;õCšž øuI g-Þi(þ\êßU·yd?¤ñäƒ,æìû 9°eÀ– HøoŒÍçÀ"ü–»~Bº%'Ãq’tÒî™±á5ÙÖy‰u¯z€”‘‡T‡ôâúրσòv´Â’V€KìÒ"º~¸–ÃÊ~\ª¸S®H˜ú*5gqaº5 õÁí´¿Œl"„%¬ ÑÁºaýÉa]Çæ9K‹äáWIDj«°&§?SuLh. ¶~HØ&t%ñºÖvfDõ–ã©màÊ´êu»'~§“ps*]$Á `¶-ÛíU6О§MúDzxÈËPÂö”-ÆÉÃpgê @Õј«÷fÌ’ÿ:íÀÞØ› >øDÚ¯b!áâ¥-qCºtxNþu°‚Ï›•x[óKK!Iu¤É ÀÝÊNçy>fag¶?àío…Cl1R‹Ó¼æHgÏf¨-êX,+io…Ó¨‘¹þB‹bH\[x=¾s9qO^÷raUƒê¼"=Ö5ÒƒïhÃËÎÂ_/kœ·D|¿@eJIñ8û®ìÜ ¶u‘ž>IåÒücìIU–µsáox>Áy«Q¹@¯Âá¾8È(Ìñ±JXxéÃÔ…§ØÆ±?p¾³-­i‡VÞngæ ¶=+/ÀýBê÷®ö V&}á*÷bÂva%ìîðþv9R—CàÁ3 î$Ÿà£øe„½n›rAygR,ï’vò¥ ;Ø”b¡Ö>+z?ßð —o‹»Oˆ7æ”Ï‹¥­™80ü­âÜ@©t܆*¢/©C÷UP¶n&Ë\”¡ÓïÝÄŽA:ÿ#€oÒ©ÁCwo?;¡ÁS6ªä4ÔXìaÇϲóY {”1€*´X8…øv#¼*®€BÄw6u Rst^ÂÕ•Uƒ«ÓÀƒ»KÂöõ­,bXôë‡õöÕõ>|‹¶“#tÒõ ,îõ‡¨±‘ä×{YaI’OºÝýö€ºŒ Fùˆ'"#¸Ã÷çYë²­‚âxƒà¸;pZùÕœëWYp(s=`"kQ€èc‹DQ“ríÓ–vg嵄{eµ¹óç~­•ZbÓøÓæ$Lña­Žëe¡ï̽¥¤>4€¹ë¥ð7„Yﱫ¬´ø›·Tõ²šäæ~×8…NuΫ.f®ð1mC @VJö“J2™mä¿å(™nV'ð›î¿ÏÿæÀÿõ^¡ÜCÚþ¯§ùïJß)c^²mÀgRmí–T»AVµkm¯«s &zÍð2¥ÿÒõÔšg˜TŽžZq…)Û´tX¶u­o•×j+ð8ä¶]å:©¯´jàOÝ6Ó„ZIñ8‹ßµHÕAîù帩É;C¸e`•šÜÕf¦ÁЮH̆˽¶]eë[cºÎDwIÆ@’$mÁU¿Žó²B?â‘° t™®P9Ž3yâ~À“ÚÃ,Äg-ƒËœ03‡‚l¨F듬=À7ŽÓD €Ð”•;ª+’‘ðže‘eR 6Ú8ïIû²ò°ÀYÖ]’È OCߺ(‰äá°Äb»0µÒ-ý¦Úê^_Ò!ŠÁe¹~¶“J̰X‡rÚ‰™$”¤£üq/e&}mÂn@ ÎTÅ ÷+ZŸ3>WÃccÌq©60`OM.¤‡w tM}HËILýÒa°F;ä$=Så ²åÝ?kÜi )èþ BýIŸy=h’žêEõɇZ’_ЂÆÄUÀ`})-=á@ókl‚WRç ZPBõ$L“ Mæ²yê`¤ŽÚxUðZìÔšÓzKI€Å2¤I\¬Ú½¦:L)=­¶I=1‰wƒô ?˸Á@ UrrM6fòÿDhb6•+k=ÀÙn‘"kàHõ,Òça€éý¥±è{ ³~\r¼Bù}L}±Ûs¢ýûÑGm`\‡øLâñ*áæPäÌášøCÔ™_àÔ¸?å Ãdjs[ŽÈBÅCàK‹ß€g&Rü¼ +ÙÕ¾àÀ¸ñ€]¨ õE’€*{ŽñW ãžUZøÁûåÃpóÖÏáž<æà@wÐøBÒǪۨ®0Â<¨޶ɍ%ˆVîMœè/à_×&½ƒ¤ÀÝŽ q2¼]LúK”·ª¯31zÎòbÏÚŽè)ìë%ìø(|Æ!rcHÏ%…7ÙŒ’¾„p"Šïü“ügÕ×’MàK£3›U¶±õF_ë—tlŠß,&ßEcù‰·¬ rŽ-’þÜò‰ÿ=X,ñó®€|/;–¸’fåƒÊ ©ÉÁÔŠÇ’Ep&ïEŸÛ¸pcÖWéRu!*ÙŠŸ/Ý£S½Ú 7·YßPoû®èq;´ì`ëÑ’ÔǧÙJ>€mé×#ñ|@œü)®¤Mߦþë¤wê÷?Ê è?ÜL“]åSæ!…«Xþ.Þsý7?QvÏZ˪«QC‡j¯â5û6TbûR+~d¢‰×ÍJÚºdüõ Ì+lRÕƒvHå™ÀQÄÓÉ€NãîÓfôðíhÆ:°ó ծ̱/X,ܘt{Ú}"@=,xN> š¶mX,ÚÎ[B«œÏجÂòº[;òñõø@ ÎüÀÂZRâÿ~¨Â_”õzN6D|ªyÉ=ùm˜£Íùb®=ÒhDu–:þà;IýJãZÔ¯øëOøPü#ò^͇OÑly£m`Þ.v{|&y9ŒoŒr>=~U“«uö÷àP™}’ïÑ®¸:Ï9ÄS»¶$Pr¼p`hvð¥ô´c¸óƒ€ß=Ú25FÑAžw0ƒgÕViŸ¨“ήÂݾ¸›Œõ$Búb·óü•Œ~ý‹<´ãš%“4iÞ³Uágv±ô;IÍÎÜ ›^2 ð}õû™(i®‚¿Ýòcuùc`:Lú #ÿ’äqä*¾=)[Ø<©RPÛ#> ïŠù™¸N ¾­ÂE íy¤­.”Ú$T“•®Ú£Bì‰.IIâiL3ò–¾ÙÕ¯áVе yC„9žz³"$à8ù~Ûs5á{“ßGÁÿ÷Rþ×Ñ6\VïÐ2Çþuz¢ßrÜ¿Òv .SþËë O–aâ©6G:|k%Ê—ò±$m‡øu3NmL­îS^ßmúÜ ‘;ø®øÚà‰JxÿšpÇe8ZÅÇÐÙ„Mûž¨%O¿§ßüj óîN°ãvH4*˜h—•mv%?1f>*ñ—ýÌœæ…D™;g!í`9öÕ ¤w€¥ÜËì_ú7þ¿Ìàd÷2ñÿ Ðws þR.è[WN¾‹ª ô•»_™Ð=; !eÚeøÂÑý7.D§#µØƒŽQ ú5šeÐ €­\Ú‡ s&=Ãä8“Ê2_2ž_er#½¯uUí¡†!Jµ%Oå\Ë}åad  ï!9i;‘¶,‰4é_êžüÁdêq¹7åapà”¤!GúœÎ¥\ÐWöè«g©X›¼R|¤*`u“$_u”9™ÏùˆôÕ—TÕúÊEmÜøfž˜zd˜›¦Ü@_9ô«H’–*·Î€¾qÞÏ`°<)R« ô•_©ŸÊ[ï5Ñ ºÌv÷|‘›{k}¥¢AÒW5QËZÌå6}3Ó­ú)ÐW$ÐW$ÐWô'—³³‘X hu¾’Úmíõ¿åOTªsHñAû|;¸!/ŸÉà¹Ë§àÎâ—Úªe(o‚S}1 -„´nò-`¼,i\MÚ• iúååƒûúèG—{KjáŠPú ”Ø)xûyÐW“SG©ƒèÄ«iÒv}¤-s©]¨‡uÓ$5žÂ$m)Ê"j&@¤L*ëDñí–i’zf°. ü0þB&tuyª#^‡h2ñéT“Ä—}e8Õ9+þˆ6ØC5ÂÉkÓ΋sãç‡üé$Àª´•Åû°}°Ôµíê+n¸Ü.4È_H¬êc홤ï)–"^óA-÷ÆÄwßS¼Ì¶m½|ø×ç—¦ ÌElÅíÿ˜É’&ætšh‡±m>“îá{Ó#WX okq‚íÓ³Ås?¶øÞšXj¿`§%¾Oz™Ü¥H‹1Y½ ‡e¶ð¾=“J @°!\G€ª®\£—¹ßSÖ€cÙ /í1R‰ÚÄÛ€ƒè¬è]\û­…Z-&:ªèÅ-áv«¸™m N Õ¨q‚O³É·;ìð6?v—í‹ÊC_¯…ö@ºrÝŸ’ ú")ºaŒ|Ì}<ƒYT£è«€Kw!ŒT9}µrbú^. ¦I®$g‘Ý´ÊëÂûäÉM”ëëÄçA7.i†LÛϱ‹ñÓÆ†•^@þýb›$Áýmùc¾¶)ÅÉ·˜´Ë–Ó5¸… ÒAZòŽÆ®]ÏAä)<ïK`Þÿ3^ó=ò’°o-|éfʪê:×oŸi/–Hm>õ2Bü#×:P—ðm§r¦oÝË.Šô¶BTK hða.°ÍTØäÄ'6£âQâÑÀ~ÖvcÅ?ÔÞ.-íd¥€Ò-Kv{˜ïRG$UL}=(ÙÍÎÖ(MÒÑ[ðÓÆ6Œ­Ã§T‹˜® ?í‡á?ÓÖêâ_‰æE¸¥Ïp’Ðå|g?xå[ò·×1öKƒÏmO\ô©eJ¯àI*@ÖÜa;xþïuf:üΨ»Ož#i{÷-µ z,q`À\¤ˆw,ú£_ú€òûù$à³:’ýÃù><¡E§ÒÃy¿„0‰“tò:ZÏ¿U>KÍ3©1¤v1ÿ5Ì»qMÃÿí„ו.]mëe-µ!°›GxæÄi#»<뮑-]~w+¿ÃŽˆÍI¾ú yRу<˜ÅsÇ«ˆÓN\ߤÞñ@ØjOöYÜ ¬±Ö±|¨í¥|€-IЂ+mýÄ›VDmK×mÕM„}­­>àáÅ, ý ÷@ÄxˆpÙúèËý*y5fy^GTÏÊ‘jmÂ"‰ïQ9˜%U¹%ž¤[‹– K Á…æY;æB›å?‚]W®9v2|Ø!|²uo—ß@ü`Þ‰+ ò­#IZ‡EñÖ.¼‹YÀù54j¼W<»î¤ï#›Uu•/>½ðPûiçÅ¡ƒ}µÔË¡š|ÿ+â7 ^¶èa8Õ·Ö JÀÄÉéíĽISw@ÞåÐ=H+% ¡#¬õ¹Yâ]; z¤½SÙ—ÀH•Ç>%?àÌĨ´oI^F9"z‹û¤Ì[§í­êL¾û }ySò¾‘-*P›Øݼ…¨üZh[Å)§bĸ<¼nDY¾èò@&×u¬úŒòñIõ‹ø…zç$¼Tª œš¯xV™¥He ¶U®T @IDATÚi±Ë6q;(/.Ûƒgê§#òG}Aø,û Á ´´Þ¨®h…ÿC£Snªo iïRï¢bÇ~óÇÕê“Ti¬¦<;µ£OêÉZÀ“o/mìúÿpXêe—4·^tæÍSlOÃ(gõ!ùäÁ î”@n·Ø:“ûK¼§È£¼´8·<’´p>mX˜öÓ©Rá=I}+#âäy¤q}æ<ô€|í”\.ÅI ¯h}Úô3ÆíËõë´\Ö™[ç,kži"€<´¡oÂÎ ˜i[ûsry£z^¹öôì$Mr]Mþ«ÁáŠõ°0 Ã#¢Ä³„ /¥¨ˆûÏwÔ5ÿÜÏ«°Qe©´¦üýν ô•Ñ)ÉhÊÆXhÏ·ÚÎÒ õf¾|(õ>—2j[®Õ?÷^ȧÖÕá_ú[r = ù/-›‹kš,/ëì?fÒ©†rk[ƒÙò"8E@«@ÕMÍ*RO¯êHŸ¶8¯*Õ&!˜®¤C—bø×¢ KRPÝ89‰Á…†$£€µnÁ!MêÊݲío lÍ©I¨~«~*çQ[h3A Ù¦#ªvš~R‚C¯Ò†Šçò¨&/»ݲ>%é%ò¶2{éÃ\½¯Ëúª6QöbkºHq_Ì5¬àYöË£7™¼>I|¶"$é%­/i€éK½(V‡«]SéH êp ™ÒÕé°ËÃS`üZ©2ý3í] H’¼Ò=*½°d€êBE€ g«å®‰O@3S òî j*áarÎqÒË›»Z¸ë‰à­æûŽ”·$¾«iô£xJªSzŸðÞBú°åÉÊÚ˜IÚÖ­ih@ïrøVc&þä80%€6¹$µ"ïäÚïZt¨‘÷*5‘ði½Э7éýª±ï 1°Î ©¢‘ ¿™ fJßÒpËL† `ØòtF§ƒM¾›~¬Îyw”º›¥íêûð€$DéƒèÔ[1É9éÎþsÆïMºu|‚É^Ф¸[|nêm“À¸Æ»k;æe%À24·ßøˆøóLÀ:fûEíB&°É¬À¹]3åʋà Nº/ePÒ™‡¿8¼ ÜÜ¡5ï&M*[ÛZp0Ðþm3z¥É„wCørÔ*´qe%)GHË-¤ü|h x6‹T‡Õ:Ö6t$ÒKÙ9lc‡íbã1§D´M6|.pÄè€éÍYBøs€ÙHè ¥½È"·¥6}°óì<¶+fÒ`¾÷ Ò/Hwm•`òÝßfWC¹|b͈Y^‰&ÈÔ @ÙÓ·÷×2+îU’@)©‡•{¸·ê¸=A~Ç®q~Ýä7vgµµžªú‘rR*ª’˜„¤ƒÜÏ™¼ôçŽCõÄRAãñi˜åÉÛšóÈû ⯆¨7XÃIêYÅvÎûÑagÑáÖ:<ý¶MØ<Ú Ú$Ò¤â#Ïë`/¿ktE×ås¸ñ%—RADF’¶Glh~/|…lZÑߢò|î)¾N9µ|&©HI‹ 2óÊúÛÞ¡µIÛé)w yÑ–ðvçÔ¢ j9¤?v˜·Äaò§–ö)êKç$½AÞ‘¼Í ÝÃH Œ€‘·ù:Ñ'ðS„¾Zß|{ªÕñ»7iû·1þ ÷‡«5ˆG|¶M)ÀNÍ?™HÞíû“Ž^À–l_}B=Rèæ)®ýøÖ ¾»0e©Ã´BÇZ#¶Ä–ê¿.‹îm/æ?g^åWHɶ·GÐG»v1ßæ ¬qÅ÷ZÛÈ„Õ8ÍE’¯ØÎ(?É~/þ€øÃÞ®¤¶Ðþe Ž¤;ªÚnJ_i-`÷‚±v^„ï¸ü…´ã»€h"™lùü89ý¶z_X!QÛ5/…UŽevc> AÉk0Ã.Hš¥-(=Â^¯¸ žèhïÄžFŠXí‰ÜÏW@öNñ·öH´©+±+ËÇÚVRµÁM“:qgú…‹(““ð3Õ:$À×<.Ê·l'ߟ"ÿFavõ•²ŒÜE|O¼nÏp8ÜùZ|DMK‚Á+Ïâa&W©œŸYTåù+õ»*„š¡Î…t‰b¯Økñm< ¹ƒ®\k°FPN¸Mˆç®‹‰ÿ-ÄAh÷X°ñ÷ uSÚÅS(ó…Öƒ¥{¼!€¦A÷åËYX`éšü?´Nǽ([! hÕ8ö9æäC#øÐ=ÑÝm(JsËΕWùüï;?¡<;»æ\9Ú¿£\~k‘ý/êáØJ‰_, Å$N(’V¾ú˜iãRÔ„{s¤ø×ÅÕžðùñG0¡\ ßä.?)¨Èî‚›“m˜“ªMÏá§r˜h¸ =$Éotø¦IzÒÙ9`Þ\ÛÛOðÊ ¢¾Á{gÊðrx‰t9"I¨³0dÑ¡)3nªŒBURR S¹ü0€ëÜm—7øˆç£©»Uö\h+êé$œ·Äí Êîë_z9*I—K¬sh#ÛSÀ®[¸òƒÕ¯T΢nb§Yµ´µl‚2Ô3„Îdt†/†ëÛÚ™”WCúÁÌCÿ;éiß‹„º¾”~êôh¬„¸Í²ÛË·¶ÝËxu’Æ_^—:8K-nVìÊghsE¡]¸È+tþá›Õò;‚~âQëE}éöÄ\ËXtWxbEè^jÝ uÎÃàYúU·ð¬…ÛÐgÒ‡N;Éòo5ßÕÏêìtøY û¹f×õ4 ãna=ÝÖßÙOðÇ$ÚŸfrÓåS)·5ªÉ=ÿgæRãÖŠûN^_¿|/é_ùÑ.¿mñÿK†Âă©ß3OigÜhÚäÌ9–Ìuœô_šÑÈÜãJê!—jÛ2-w‡ªE<7ÿŸ½ Øæÿ³¤ÿ›ÜÌ•á<¶°æê¾‚_ÿïp¾*‡»)’–J«ÃpÕŠ2Eãy ƒ)MâØD0‹Éb5R’•¿Æ4â =ê$5Îo!í§fsiÒ:t ‹E9–Ú.ö!@Ú6HÖ‡¤Ãvúrø#ÈŸÜð$©œ ZË Ã2'ÑÂpÌžØÉúd/ÊTËà›˜%¼#‘ЏòDjc,v¡4êöa¬Î»$À”Ϲ÷·l\¿½ª«†Èš6ºˆU kEü«nÌb £Armäô¤â¦¾$u+Ï0ÙÔ ¨6Ÿ4â’JœL·¦¿H©Ct+׆Ì%­úàäŽ ˜ÞM,ž,úÑ“ô]<˜„T‡;J«¨vRÏ'¥`i=Ý.ÇYÞSL~­Ó‘zFR¯®H,âÀÆJ$µ›°µÁ2~> Ø…v <ÇFíÓ“å’#ÖHÇ-p­p\»Ã6Ø78>âÚ¨¿–J¶ñÃkÄùwŒ{P^[#YZ} I¶ÛšÞÔ6KM†ÀÜ9Ô‡Ñ,“CÀ K¾Rí%|>ï´¡¹[x«]¤ž˜šX‰Ê°æV_iŸøÓVVjˆ€Ì4…Îæ;£™ÈÂgš¸:Ro­ðÔÓT“t* ”ðã_m®'íÒ×»5eÖ’º CèNÎÒTžmp(Ô;ðv{€ˆLÚ"›15o@¤Ý ’ÀÖ"c GýÍfÛ¿1evÚw£Cmª³88ÄX S¼Š‘©B§l)’¾Ô;6þ#I¤I„¤£g2= ‰ÿ¹„hQÒŸDIŸ£u0»œ¤ @–»µÁ6±ß9ü§}ÙÎŽO2u‰ûúØ×&ìÈ''P¡К>—$^'%Ê£I{2tßy•÷é\;®·û{{&®§ïLróÏ">?1Þó bòï€0M!ñJ¸÷xÁN:A59å-’GAži>2Îã°ÈÐéÌ’Öl&×àïJl~õó»!-Õò»_R\l6к[žô]ð`Ñ;ÖÉk§>r'qío­AÚ«ìAʶW¢-»v~³ÃgÚZèÈâPw» ªúÇÛ‰°È‡”nÓÉæÕ£ê1+‹Küû/ò ÜôìdW"1ôàýDIJ!¡æ[KŠ&[‘0#À% ¢9`”]è8®mH#vÒ³+݈ə¸bb̶ӬƒõÂÃט¤tà{—þHÜ©6ð^8‰2j…‰‰VtdÂÛ<{ðÚnŠ¿Ç߉˜Æ=:•çwxøÊ½:*ou@*îâ³ nE²ì†²›øÖ…l·>ÑFä_b=ÊOµ!ÖÞ¶,n—&: ­hékCüî"ö%.·Ûš€–¤»‚ôèȪê“Ïóp?Ãn!¯ÏJ&0§m¨H<(W¾‰ÿ ‘âhn#ó.µîÑmí-Ðl¯ƒüÊýgkWô‡Í¬Øhø(›Ã.ƒ‹à—uãÙíHýNq}Aªe _duLN±þ¡-‘6>Ë6)ºÅn)ÛÏö¢¾”³›Ã$q\u7ñxu4ž]Áöxw€«·-ßjÌ÷7à À9ÒÃYTq/qþûuÉÃÈx¼ò:Þ·Ãýe¼S±g‰öäûT뜕=ÝÐ6ç`E-sfƒR„¡ò‡ïÞä>"ö„ÍŸL™÷²-Q£0´á¶ÏøÍ•}+kîm/]Ââ=»ÆšÔI¥KŠ ]«<ƒøN·ãáÅWJÖAêx?K6¸ßZ,=@žüL|BzéëDÊ[éUÖ º£ï‹]eB(KˆÇFZØ$¤v½äÎÖµèAµòö>í§Ü9õ.€N|kªu⽟¢'³3¢3ö“ˆËM¸;zx qì‡îâX®ƒw’o’¶Á|wOÌ϶uB=í@¤”;Œ°!¥ÝíóðºÖU pÕ9|ô9Zßï¸(¼Ë w?ß8ó×ëE:”¾Q™¦YÏ”hjüNÏ>ž¸ˆÿH»ê# µÅÉÐî˜j—PW¾eaÚÖ%MUçãžM‘ûe·gäF;+ñ¨}š7Â.&´SÌg‘Vtè[ìæÔ£¦”y¯],´ÝV8gÊ5´§µOŒq½êl| jûðþöhütÛNåëú¸Qøù‹ !ªÔ]‚LÜ·!¾ÏøvsÕ¾H Œø¡à%Ú”}±›ƒ•ipý§ø:%)cgÿ»ï_ú”Ë/Dªy ÒÝíì‚"幡£Ü³Q,29é\ßeÆ/yçõ <ûbO{ü;Åc#²âžÉk÷*îË!Ò:õ47„±àðË!{Ú3¨«§k"ñŠF¹©>òÆ9“)—ÚÎB©)ˆ´Yè4Â?µ°¼Úˆrg9R -ùMåtvè‘›ùî\wd›×ç-BýÊ;œ,h•áš2I>ë:Ä9Ãà >Fq_òÔ^á+ðê&õtDÞ ÃãñƒUºŸFýÞ€ú}‹ã+K:ÈM;;~d ¥îÜÈàÉTh‡W0®T)”s-pjÎï|Õ´ŒQ³ðþià7ãÓÿgÕ´UüŸMÝ>a5<öŸÕ߃ÁÆþž ë 5“‡7¥¹ù¼^mA.ײÍ”¤ÔriU_…WÄE½R´&¾š·ÜC®tšø¹’Àù I¿ÅJþ©ñ¿l4­“:ˆ¥àð¸šxr c¨¸Â¤xUâ+è¼2ø °u³å€­-–·E]aª çe¾šžodâr`I.iÐ#žû®~Îu»ªï*éx=ŸIl¦N_…[=áH}ERS ÀvìÏö¶ºè8,À+Ò»œ ÎÖågEì2yâ­ è¤ݬe‚t°€±šH}l Ü „äÆCò2è3u“œ®Îë@a-pûhJ¤>@hMß4kϤsZÀ¢<š‰´Sih.àÛ,ÚK€† ’¾´<¯¶M1…3R:H¥fã{€®i“RÀuàMù?µà#›Ì‰Ê¾NÀ wú¯N.Ï¥—)Ͻ(Ï0ãyÎ=tâSòp‹ZòPàã@øøHøç6î£XßúêR5ÛÛ™ Õ–LÞ!î“_i;›Ôf´Ä°P#I™Ñ€‘5•ÖÊê ×€öPÊ® ùáK·ÃdY>‘^\¿šÏÝ'M’7Åï:ÛiÉ’”^Ò\ ¬a™k“z¯c""0vjÚŸÚϵ]Ç Eó€à鉿œì˜Êì €§Ë¢g°úe{#1Ͷ÷¢v4 ûó‘“ìô|>Zjí€g±å½}ùéÖÏó¹÷jÂr'ÍSf’ø½iÜSÌ´Öå/2?b“šcFòß¶˜“@ó£å #£-dýã3èÔ§¨ègf×Ræ#0`B>Úů·ëVXYé {'~/m*Ò/)~l–òvùÝÙ–k¤jNr”M´;]Ë•’ ­o“ùÞþ,0Œ…/û‘ÎéN’I»Â*GÝ¢Ž°ÕÙߚɷ‹³)œž¾y„¸`J/‘5áyaêËþMàÇÔ¼ñ¤Ÿ‰©ÀmëÍ Ž¨Nø1¼È΋£¯^-Â}ko}›Ôewo=U8‰Á›ñ9ö8¦}JÖÇ‘r{ ÛÁ_ãКËïÅ„¸$Þ'¼3ìÄØy¸¯` : B˜oT1I—{åéi^ºƒ]O΀wªG™¤Q:qKÔs0‹ÑÖëä <“’h×8P¨•ßLÞ á=EÒ= ÿ9Ý ´eyâ¿QäA'Xg¨_ž,kX?U×âI½ñ¯\ð½)Ü©ã% YJÙ½H_š‰ðl]ùy›„ßÞø@Ú6'.kÉ‹£€\íà›Kc„7þ8sâ§ÝQy„M²îötÑ“ÄùâG%FñÍÔ\»>ª(¯ç#GÙÇèÏ<°óO&Û£<_EĘd’¸œf\…îà÷Í+0NÂ]mbå5vJtý/9=Ã¥ðmÊ¿hŒ‹[ðóBÝ×€ÏÅ8O0úÔ¸NꘪÀ´Bü²ãÒÝ“´Èõ.qŸ…¿ûñFžD†òÁîm¹?!ÚMxª6àü}xrCÆ|LYߊƒÇ1>[œ H%ösä^Ì‘ÿàYÜw²×‘Ú?ß&çO´1åÇ©jÎ_r¦*//¯Áô?oTPPP¯H,/þÿ©ÃÇVWüë• ƒ£úÆÿoøtV…¼•e™¬úK¦¤°€NMþW•ÖbàùG²¢af¿>P’´ÚÚ-M”´šæsMáû;2x‘T“ôO¤.o^êåh³Y“Þ”ùc 8úÖ Y„±ªw† ¢º!Ê$­j•‚“T $›3¥Uk“VÚ×$?$…•IÒ%”+Iø&ÒÈÚ²¦Žf)—tê-šò®!pM$iê¼Tþ×d¿:Ì´MZÛúê"Åû;$×NI2Öæö)Ò~èßXÖµ}wu™ë0‘óSCM{”ÉÚÚ^˜ ǼjÎÒä==ÕFÇ“tTfRüYR/:”K’¤ÜË%Á‚þO…%]¯N½Œ$$ÊŒ,ï-xÛ ?ëzyè—U«àS'üfJ¼hâ7-ÕÆìLüåéÈJš<ÎôßÓ¿9´ùŠ=§Xkà«>a®äéóîcbÉä²Fê‚é÷ úo°áñǘd~æ§#t4é æ0“·nRíeÞó„ÅD¤FcG­ùe‚˜Cé¥SpjÚðzñöTd7{iÐï)ïfäýD¤.ó‘à‹X¶'WBШÒZ‡Û¢»SèPûPŒÒHcÿ÷}£GÛD&¯ç ¡è1âp™dèGëp<ƒÞ1h±ÇxíÉ“;íêŠ}l/@„›Òíhc:¥¹:öÀÝ~H-.ÏŠ“Ü"KßâWl'R h~0QÂNŽ„:ÅÁ¯š|ô¦/{6ͳ›aÂd¶˜–¤’<‹ÝEœß¦4ºÚÑÞ,Û›ö»`í@¢#° )R+Á·×²ž)ë‘÷w9ž©JNE„—Ù*x9€„ø–¬x{Ÿ7×öËŸ`—T^`WEm™ŽÝÊäû)À­ÐE¼•퀺 í­¼g­IåþöÒÒÛš×q(ÚÎ^G_uœí“ h’¥½ »£ã¡^ aHCG᫪!8ž‡ùk€ÞÞ/QFKö!­ßØ)ÄïÎps¹C)’ê=(Ã5)ã§Òy¤ï0ߨ¸‹…Ù!ö)嬱W 5ñ—b&§¥‡î¤ëŠê‡i꣜TÛ<Ò7|ÊòcÔÁª¸ŽÅ¬W$³¿E2°Ò*éXwX˜“ŒS=\ŒÄyWûPÀjé&¼gðª@IK Ð0¥kk—ífŸ„ʬoÁ›¨ï)!<¯³uCãWègõ’‡ðžGo…/»ÚÂ虤å Â"Žás1'’ø×x òQ‹ÆF=ÇïìJ(³ß9·ƒ'ogú<]ú ¿JI9†÷…׳\ü·T”1 ¿ÃÝ_ðÖ9|—ž·ðNû,6Þ6«Ä\§È#?hŒ-œª€Ê¶çþ^©ŸŽÖçw!þÞ&>äk¼$=žáø#ÿbÔµÁ/`ÖÉ÷Rº3XÖ$¾y9éy@ŠzU±#î{_î £ÀÏ·ƒ­õù7øÝPñŒ·„³¥€ÁòÖ¶OCqtÏ·CÃ[ü›‘q.ä[´w%ð€ùyþ;¿®ÜP¤ž`®%©[ÏVe7õD ÞæÜ§!²0J+ßw”‰[¬à–¢>‘ëmbì:Û)|êÁ´§¢£í†HO[·´/Àë :gÚ1hW¾Î[l&^ã­Ð¦äoC«®±sB mŸø<{;r¨­{èˆòÔÒZtwòò«ØÍ¤AÒøÒËyB¨‰m_Àí~vò™=“”xœzbEc©?oðS?Ç7,³}*Èw©qjU0…Ž¡Þg‰Wîλ^´—GцÍØ ›^¢Žkªò€lë}Ë›@Üé#“/×Ã熸™Z·;ïDa XøðuFûFY¿Ñ{áÚøŒ9I–½^ò_£ÚІ;ihê÷J‰-)  ’ZvU9@Yg6¨/b2œÕöØ ‹Ô§ •¸úü Ã)m½ëg.„£hЏJ3lk„àÏóÀÈÚ‘«¶¢RmSŸÚ½bÓŒëØ!2 ‰û;8‡¡7‹§™-v–çÚ9ú;T=d}ü¿ôE éyçWcìÔuÑEüÇèú믷åAü;2Àü)˜hýÇbœýቶÏú½ÒÀëö9CÚëÿúÅtYWAS¾¬Íê7Y¼!}ÝÓkêhSÑUƒGsšEE¼ÕÕÖdŸ üÕÕnn‚û¾ÄóÅ:⸫ïf½V°3C™¤±V†áiñÊx\E?Êû‹Õ™e95)0GàzË܃ó4„îŠÍ;5Ô½i¬Nve°öOR×úê|Λ@°ÍàŸ¬Iz®#ÞW¦î~C~l°ùÑ›x¼3E¬µ#î¹ÉÀc3Za?5kèØVßßÖ@8PuQŒUIµõj{z–‰ñA9 {MßZ¡|.œÅ|„Á7¤•w·}qµÅ˜±aÅk ‰¯N|GZIqÖµ„K·“ÉÙkm¾JJ#r>`.H:p÷IWßÈ•ˆWÞ,¬*B•LL3H:¶X¬=nZóå${ŠIi—1ý!5h}Œç9¡Öü(>òcjN§¶ô˜¤…”ç‘®Ö›íáµá“)ãßœƒÖüþÁ•ûð5dê…A÷æ~Fr”^j»B“ÔrüGî[§È•vIáq6tIͨÖF5xÝ.Ž#é®]+Ä7ºØHP¹Å¯„Vh0jÇ÷—eʯêU¤ÏO±M¼sìKAóp#pMê:àÿ–ñ÷ì$¤w-äÈ›i\º úü‡&í7YwÀO¸Ly8Éê_©)K,iÝb÷ÚKÑó k_sä$¸ó~½m ¸ @ãsHc®k·4xÇv¯úÒî¢îœ"IÚŠ‡QEнÆûÛK:“/ãˆûú~8ZЈó®íåH­JGòF%MyÊH{_ÜD°{™{ òïSç§©Gû½´+Ïs¬+mÊŸ•çÈѾ‘×¥m1O’öí¸‹g¾ç¹ vc°;œ÷÷1ŸÇµ.A$ßøFª~ /˜d7¸ÿR>’xœHÝ¥¬ú˜àCïøm×Û:&_¬®á+sžµ‰ìeW]@û¡Êó ±gI'e•weó"×ñ„×<»†4}Ãõü†ÜxÕÖ©àhó…”IG»;ÿn{*¼¹]XÞ“²ú’¸V—ÆN?å§ô9·j¡‡´k×Ü=ÔPSR Cì"ëÛž%Û ´ª¦ÚÑíìèÒKXˆûÐNÖÁ†HožoÝnfwDî¶3ã×Úcø;°Ä§5ˆë}Ö ©ð »¥K7'W§¦ìü³1>`ä³°Á´‰³Y|kCÚKàÓý¹OÁ]YÚ­“ˆfQÀ—ŽU›»'üEo ?ú’GÛ’Wç9rœVUÚýÙ_,tdñ`¡Q¦…~ƒT8*»ÓRyø!f_qfñÜi½é¥ÊGzõ}©WxÕ97QôIêÔ½61ï@+€O[s›8Bú~w<º)Qicó_¦¿ˆÛ HGÿ h>¹à&!©—òÒ"Ø›ñŸ-ûÀÆUÅ~ÙZøNÄ"šE)kèegÏ;}sýJª^‹:r£nVÝe]ç<ÊX±{ Õ¸–D¤/R†éßÚÎr¼üN¹õ`ü3Ÿò<¯ÌSïnJneyô‡÷ÓNh7U ¢/ðSÿ{'œ†¸ÔFææÆÂNMª¢´€ñ>ãõ# ïä±s+wnç$áéPÒ:‘€pµ ¼ñ9¡>g.°¥l%L3 >ÌÚU* ×ă¸Xš¯ùÔÏŠ“yPݪ‰jþNM.}³7ÊIý­DíDÞå¿Ç·Oĉx6“”¯‰Lƒÿ‘çT{ËRW}I;:¯cŒYÝîøKaQzqÞ@,|*|p}=‚ƒGÜ‚7åúƳq‰§²1ïPøèuö­,´v´™ç©w†Ÿ?Mç{“*×vhçJä_Z 9P¹Âø§‚øo}ÿ‰tÏMUâüßFÍi,ê"†Fÿ­oÔ«º@_yIM}²|—f½-û²<ûe}pƒÇLð‘I«ôU¸êÒÒ*q@æ¤^‚ôÚ®(©iµ¢žVƒ{å}&è«Ãg„+©é|Þ/ ÃÊYörÚ Ï}¥‡H::Ð÷q¡€ú1Ðh‹¶ý¯nRÎKÚ0ôeZà(̯¶ß/´òú m‰ÒP)oV”VôUØ/¬è+÷µ¾²[Ä5_÷èU€G•‰(~ô3EZ.i0²¢€¾úv@5}«)àŽîj½ú*jNf"1˜‰”t¯Rý<¹žÝRÒ IÈôïM;!izIXKÊ~sÛj=Ø1ºÌ¹ÕÏ™‰ê¦ mÊ_¸¿‚‰€º€tBóAH;ÓQ©úêÐ~Ë}%•{ €›hRV?éO†ÐÖ\O»šIm‚rQ-ߨŒ¶öÉœÉÐñ5}E3Iƒvè@-•ŸGë±^Ö·3˦R·BòQ“f ðÅ0í€BƒÈã)Ì5n LÒ÷“‘fëG™[ør̺¦Í݉æéArµqöÓZ¼6Çï¥ÙÆz«<Ÿ2g>2£s¹?±s0ù>m*~î‚´î ñFì4{œÅˆ W‡§û¯éAºöz€„±³™ôw±›á˜ãIËÅ’°½'œjÁòn·mŽóÊÙ&í÷: ¯¯›˜Ø×¨VH")\‚j…v!&iÑ=ZÄ$²8þ¼êm3˜¬~øw]å¶søpÜlÎäãfb™‡¤ok€ç%€ÐdîcÊñ}\šY5Ô¼0ÊÓ×I P}Z’>Øé¿MMR¥n ­Ž¡»xŸÉ%ª°GYäx‚“«‡Àë­‘D΢ȭ|rF³¹? §|L‘$‡ õ"MݘˆÞ‚ W§çW-8—$Ù}ú‘[ bÏ:¾}ð]Àvè!Þ#¯ÐŒŒ÷<Êâñ†SíúÈñvIâgÂ|f.u«±î¡* oå)©pS#œ²ƒø<“ðÈQèß›j'>';“w⣠×nOÍA8?Hkçejî›Äo‚1Ió–î…û-á¯}ø&fÚRÍ!hG$ÁOâ‡jž¢×í¡è[Ì!fßf»'i¥ªž@V=êì-o?$ß·%’,Ø)?ÔÿfÕKäe?O££¹Ï$ÿ§ÚWy®e—摾‚‰å+²t?— –vâ}&jh6@Ÿë¶yƒ/I+öžÊ#ÉúXôñÛ];Ř•Rßh“Ÿò¢lËIyX¾’¯Í0ÕŽŸƒ~©z›oÀóÒs¯eZú¡Sïº6Ø;ùWú6ÇRäâÝС­S‰ÚTI™+ I²Å® /&ñ\†‡¸:b7sxEõ“r[PxÅP&ëbÿ«TmOUì(¿bZù0Ì7$6´W´x€.`Ð87ú˜ýlg—v°­ .¯xŽs:Ù†åoÚƒ§ØQðÁå¡®ìZËŽ@u‡…±Ï#7Û)qÅ 7²?’yÖ·!J‰hÕÏ&ÃìDˆŸf_–oi•jrðá6T¡×eæ#ÍÚ«Át»þžUIÝ–ôeÅMØRçÔ®{Û¸1­›S8ÉÝŸ0qäÑç­‹\îò¤o˜|'ç¹gµ+gÐW€^3úÊj6Á“×$S·¢\J}GÞåÎÜ"ç滸S}¬Ÿ—2?ɯ z§ wTu˜Ýè}f}‘„<õ7Ò)ýÓØ-pmêX-3Wo÷Vœj[…@¥M©µ-ßÞ¶-ÝÎöA÷%Ú•ìÚ¸_S·DôÕ!iÞÞ”õóÜ‹ˆãï\Ôcñ›ëGv„¿i[Ê ÞÂ_’šr`y¼ú[ )W_ùu„äTiŽhy ¯Ü´OnoÓX¬˜‡¤ñ¹‘‘è}ÎãpÒ½Y4^”V¡%æ"WÐ má£kö*•Ô“šHêä¶CHCs›`n—åN»?ß…)w $¼S ¯æ‘´cx? -†ªžCšwe¾2ˆ,éïšÈ-æ}™e£ÝZü5¯uúº,;·¨˜hQ6rWð–ºÃã¡É›csÌs_ðñÎ}µ”ÿJ®ãZÞi'´·Š´þ3ªÎ .D:´È_/¢nÖújÌžI¯íC>KõÂñÜiÝL‹»R?ïJ,¦µX \ï¼Pv²sᇠwÓ™®åæ]ì,SêÓqöX.è+sõÛÌ6¢/Ú™2Î$‡s #ÕTåµ`¤ÚèqÕXSê’œ«Mâ7ÛÂ… M÷† Zaavdª#±úž6ó¢öÛþ $fÿ‰o­è7ê#qúoüW4Wëï¾>ù_[hß…ºÙä*»•Ù Iîó¶uô=¶:~­¡gDݘ·/Óo5ú¬ÕPކ7ÿKÔ‰©Úe£—mÀëj·‚ôIC|4Pô\`¾:ï Ü$SÛ˜„Òä×@0[œe¨OÈ2ü›_–§c3ØDCiêIÇv·$ƒ2(ØÎ-ÉE•Ïóc™-OŠTAÝàq6’~_dÌô ‹ü­óÕ¹©®í &~ü­½Ç`ßß¾VíKƒ0 僲«¶©ÿSæöúûZ—é½×¥÷~uÏLMká·eœ¦ –')Q¶¿â }TXÝȤaEz^ ts%T{À']i»ry)±xÈlo RM@"Ôî‰g3ÕšHo°Ôuc0Ÿ9­ «éÞ^}œ¨M¥…$¬w$càçk^ à,E¹?Hú fNÜ À½—P`r…ÞÑË¡~çDóÍ”O‚Ï3yLþ39_ð@#¾¨UØ€çMøöci°Ëÿðt@®Î€5þ|'éßɑߨn93qWÚÞâ;³ˆ÷§nÛpü/²æùm>Ûj—G[ÓIKö œURoãmM½˜ì>)P|I¦Ú‰:ãè¹Fh+ty2vMÎ"9–&¶†¶ü˜êIø=Nê&m›óК²Û” “¡O¦T–&š¥äOêà1y(Ð’ÃnF–ocGR·Ê™®Ç˜Îê¶L’ô•t0žÊN ¦¤¿uhk :#pæ!Î¥¶÷ÒsµÔPTŠÉ&sGp¿Ø%¼û½ÿ¹|w1 ëHêLµ¼6"M %Xü€ ýVèP[ŸÓì¿ Í±ÛKØ ^â&¿êZŸìE¾šjf'&™ªe’žSy¹m–}ùÆ\¢t;V˜’KH\:I_O™äò’€ŸÊûl3Ú‹Ïh ,>Öb¿—pL‰åjƒË7À|¡ w@ ñy€nWóñ¸=‹Øx,>5¸­]´âµ%G[%|µ$Ÿ |â»0v·Å‘ŸVuRöÓìL¯3µ¬ºQÉï :ˆ¥°J@ãóG‘Ž»vÙ%öpœI{¨;i.Aù+äVC¤2ggø\ÇÎhð£kƒB5‡]3âþ!ÏÝì^@Ö…èú<×6§c}ÁÙäu°ï’?Û ~rK› A:7þ¦ÍŒe Ù»å M"9jË/u ´7҃ߡòäSÂQ»˜‡ñÅ”ãu,´y60óͼ>,xäÛÍKײ§þa/Qk¯»Ét”iÛÌúÄ&Ùã‘ÔÁÃI×ׄՂ0ß@*Ò³’Òãí67!/Å_„ðGrçû’ÞÞ³pˆƬù¸‹ÛeÇÚy…Ãí"·Ýg9t˜YÅé>/䟌!¤-Í•Ïa¶=vGán}¾§ºx'å8œ÷Ím¼ÑÐó‚ò»aÑØ¿‡9~U¼e„“Å$-rþƱŒ³^ïÛï¥ëÂkø†zÀÏ>v3éy`iíëµ±a¯ÎF¸#ü*ê²Èð·-ô Þnø%^¨³±Ðè>ÍG/µ ªˆ_ÑËv3úw‰ µááaĉôÀ·C#G²í¿‘-–Ô¯¤¸ERÃâûªÛU6½l;[7Y€ŸµÓÊ|§1‹_‹Tw]/›°ñw~üûÚÛ´Þ‹9í=ó@¤žç£ûù‘Я¨;*G¯)í@ÁÙ¸{Ù^ëZjŸÄÞ³îámlËp?àû âÐÙšÃC ’ÄÏå?gŸVöüÕ¶H3o°Uü‹ò°ÿ%5€€€­x)éØÛòÞ+0Ù>hš7ÆÚUÂíÔÐÄlmI ¯¿=5`tŸèÑöJ塤ˆö1s\Ï–ñk‘ÔÿˆvéY—ÞT—|·>Jÿ>•"/Òç[ò[ §sµäúÝw°Ìo#ÀpŽodaâ€ìŸùj Â2~xÌ:Ó#?ÄX·¿'×¶høDû ýò—!m{¶Úךíg#Xt\@WœoìùÈê³Ô_$ÉWßM°£-åM€·‡ð@žItìežI_”´UÂß:L0/з9þØ/ õ³‹¢‡Ú_•§!µjßÈ[Igö‹¾ãÔox0EK¾Ÿ‘¯¿f9^þ‹Sû¤>še K¾Ä=U.< Ô»ଗtѸ²óß•QþÛÊýJµÓy,z•ñÝ¡ŒUB,Æ^j­Ië=+pŽ/”S¹ç|¸pá÷´ ;`û3WSÊð]Ú%ÚU-Z8Z‹2¤N&ßL½¯Ì´Ô«tó­ùÖ‹+ó‘´·¯bNxtr¡`7 O$¾r?ºÅ­ïRïõ¿éì z7þ¢7±/ØùsZè\{“Åx‘n$`¨EÆïÐ&<ú‚:]a{2Ÿš¦ &«ï¾ò?‚qdne~µª½~v»付«„ëï¡Ðê¶¢¢ÂÞzë-{ä‘Glüøñ6aÂ{ê©§ìÕW_u@ðêøFf%%%6oÞC*80¯Ê&Õwµ{}ïƒ_¥U$¾}•š¦=¹tßÐ"H. TÍ}· Žk›žHÅW䃾>Ÿé)·;|í[\L$/z2@¤`í0¤v¡[ír&Œ¯ñ ¾½å¾E„H@ë & ªsÒ9.jÇw¥‡][E’>ø4«§pƶn&è›ÿ†µ @=¶çƒV?„e¥¿Õm-«<K¥ŒúÌD¸>´y$Ò7ê}ÚCùš¤U“Tödµl –¢«7è« ‘Ð9ˆIe€(‹O}¤-“Ö‘ØÝg'JRWæu‚¾AùÒòÇî'ŒTY¦BsêçÄqc51vK qúª­½­?'Ì x”.ö®š…Žã褯¾´? ƒó]Ïbö!ÒËÓÈÝJ£c­¨CêW[|¯¡¾îR±‡M ¥vŸ$%/⢺•ˆŒÔa3N&„[&!=æ&o%"*aÑyH.DÃ/¨ŽXlïx›9×àÏõ3Úlõ’‡;›M˜Ë EìupoØ’thìt @ÈÔ„Gà@ì"Ü}é»Õoä@Òß‹ëT.¢å,Q °`°&eÌZ­ªê»0»)[%·•ê¾øë'^7¶Nêþ‡ÍßNÞMî•8ÐW}•Ê*äPl¾¦=ÉÉ×Â-˜ú  ÑqmÉããUÇ+/pæ>蛇Y{ëMÞ¤)¼·-ˆoÇå›!©ý]…öóÛ€ëÕ„`ØIKi‘!ñ!n¡/ÝlxÙM6<ïë¯Ãô<ÜHʸ¬3÷?yïc¿H_dôqæïïö FKí¯¢û]ûß°a¤N‡Ùæö\å¥ôµècv+¥#Ð÷PÚóö±íS$yÙ÷N> _å.Ú”ìk³â7Qf*úà¢Qv Á­Eðͱ{*F›}«Ä;Lc‘ŒµÊñökžCsxÀ²„r<÷éÄ~¼èi»`ò‚’~¶nt7Þß#ü¹¨9¥»[qb”“‹Â¥Vxá "=kÛF tK•ˆÚwñ·€‚Âkèkå—Ù€¾/<¬Å÷ºb¿—Ùƒ^;>rñ›áòÖŠI_¸é+å8Æ0Æa7赃©¼Ön‹=hs‘ÜÛ§¾5Œ~࣢»ØÄŠ“\¸îGu9ñùp ÑÙÁ:‘÷…Ïã‡ô‹Ôn’7‹ô\q?ðcÖ}bíkfKÐ&Á—þÁ~­Ñyœ@Oô¸»v¬@‹DH¾nLû|ùÙ?^iƒ8¨«9Mí'ñ)„ý’=ÄføŽ¤>É ¬·ž˜>‘6Kç>Ò.“{Ë»#}]š}¡¹\1ÔŽ¯„ÿTÚNôB»Øy,€œcW…wF²{‚õ£M@ßpñ€Æè/ï@_<(?##xHQbª]ÿ(z:áôœ„)þ.ÏÕ{S/ev'í»Û…Xôïä³u#kîK²ù²EêY·Ö¸Á¶Þ•,Xnû-íG«ÝÚz{|7ô! I‹tàK,t®ä0ɯí9o;Ò×/ Á[…WÃã]i!æÙO•‡ÙëŒ{ZY{¾98]‡]¾Iý¡\è/Ò õþ7ô[¹'?ÔwA‹å‡}Wáö;üÂsñ'yž‰Dßc%Ý o^ˆcbŒ]îÎa¥k±ëI<²¸½ È&<-x;ÞpÜŒÇÊ©f|:,r&ýÝÚÍ·|ÂOŠh lãÇZHta&+wÿœEHÀçU@¼M3HÒ”Åï+ó¨Cw}ЗvC’Çô(C)·ËTVè«xigã² ¯l*(¶.ÜÕ&‰P†´ôeÌ#ê×µëg…© ~wJùR¹fS>¯¡æd­B½A_µg5“v4EW‰Î §ÂÕ®tÎÿäþ\ z‰±Ø©±7-®í:ËbÁ¥(–ôH+¯câKêÿØè)Ì%šY[/ŠZ­Ö61cN®Û#"ªcØqXÌn˜Œß¾f /ˆ"ð½[@rouþ¨þäJ×éË F,Ï]­ö’"}ùå—mÖ¬YÖ¶m[Û~ûím›m¶±uÖYÇ~ÿýw{ýõ×­¬ŒFa5ÒŒ†¹%@IDAT?üà¾YYÉô?L½Ççtÿ>Ü~ž3ç?£e?¯!Rmô¿ÿÚâ.óÿÖøG#PoÊ‘ô]Æg¾g7΄ª“熻õ’ ®÷ÇWÍa]¼µj!×ß·ôŒæRf§ Ò7LrIQ}IrrckSÖJh@¢¡£¿¾Ù‡ï¨g8Í–´…üúú3?£sØ<¡hõÓ(&åK vS e‡¿ÕAŸ„ÏçГ¦é $Q&:+ñÛ„[ºõG ÔO ciµ•á-‡¯¬ÁV¶ê.G’×¹%ñ³ë®ëᾘp¥ÆÀ ÒRÀSÈ,jÄÛý)»¡)àv·b÷“Ÿ+@·3Ù©Oª•ÝÚ(ÜÔÀ&7òÛ¤¶2Ìo\a ‹—ê<Îõ„˜5àbë“þÈ¥L”¹¶´¹í²)G‹së=~Ã2¦{WõIë23 z&¤Õ|M¶f GÆ"É»ÔÇj kKV=䯶 žÎPí—ªŸ’\<~½ þ˜,’žañÓ‡ðb.À+Þ¬‰€j›ž€â™te™‘K˜úxlƒ.@úÈ/«“8AjA¤×ôUê£ÐIl“Ö»(ݯNg(–[ ɰ`ÝkÔ« ‰¿$÷u‚µÚ§ýÃ':ï*3¿Õ”šVЉ2©R¸µ´d¨&Èu6•öwëb½"·¦­”>ñÚÑL°3IfcÃǤŒ6JÝÞÕJBÔ[ÃÎú…c%ñhrnÅÞ)?-¿÷ ¤{Õˆ ú¨ âžØûÙéR¥%g2î@lw U¶Ê›e¾§‰Nä6ây.Å;™Ä0ÎиaÁd$. 1›î¼ªG8Å2ÚJyÀÔO÷Bg9»çc78¸ø9ÈiÅ¿Q<pÝnÎñLôQØ`—HUAå1˜ÎàJÚíùs0ס¶0ö—ªŽ³>ÌÄ+"½Ùj¥]Ã> Ác¡uqÿ:Æš¢ûô4Ò½0ÿ)T̆)¶5O+ÙÍ®ˆý@}’4þ”ŠñÄékŸßÇ.kø›­Áßá‰7H'|"Õ€õŽ¤Ú ¼¿¾àÀç^ecl÷‚3-=Ñ:19wR·Ò…[‚{TTXá«Î­Åß°½À"eëQ~Ÿs½Àú°ý°Ý!B»L¹Þ^õn£õZÙ|ò:M¡ƒø&å*B’¶PzƒQ/ e+z“o\ÏE}KQGò6B+ÿ´—´¨@ß脯rž œÛÒ܈ü,éJÚ6â~>qÑ6$a»¾ãOÓgçÙ6åÇÆ')@Þ‘vl¨¯Ý—? >èšv$½¿Iñ+å ˜.àõõÂÑ6w­‰÷ãÞ" Wôµ ÀÂ{ò÷¶­â7Ú%K‹mDÙ¦¶[>uRäÆ¿Ô⻃%—¶†7v$/¸¢£0:ó%ÔÕ·I嘘É75J®àÿAî¼'Ÿåyðíßë@˜Úý¢zXÎÁ„†Ý®ðùÁ6šr‘Žè$‹‹CÉÓÛQ©Ðƒ%™ž,°,°Çª®³ß21Õéܽ'~ùNoœ˜aEö¶è†Ï&ÿûW]d/ÄŸ!^|[Ò¸Zô¨•Zúe*ùÔ3-ƨ¯ª¸xS~QµcËÇX¶€wi,‰È§|âÙHã¬xNe=ÝY¥â·Ã'?òšïŒÖ ,hŸ,¼%oë^èñ^'Þi§ÒÔ5ý4`~}õÕõs)¶ÚÕ57í*ûaͪ‹Q1ß7ÔÁf•Çg9Ø ]É%ÈUŸ!Íe.À«›ˆsÔž6áQÒ~¥³¥–fQSª;g™e¿R¯_Á¨y¶qðF}·ÈÍÁ[Îz—E¥Þb”=¼‰Žf_}O–£:^¨ïQÊŸþÑŠ^­Õ­¡mÇìX»ë–¥oäõYÖÊ™tî®Å.ÛXÂ÷%¿ƒûg[Ôð¦Eß@½Ö%”åe®<é f\ײ€rxœz›¢¹±¬íÒ­˜ßg—{cv ‹°6ÔÝ«X(ëÇ–¨p;•ù±HaQkèK΢®ÜÊš&Ö™q™Ó®är”˜ÉüÏ! 9hN•IûÏ­U‡²¨¦|­vÊájƒ}úúë¯mñâŶñÆ[=¬S§NÖ¥KëÙ³§uëÖÍ$;uêÔ öÆý-·Üb7ß|³5oÞÜI=8î8[°`ÁUüµ:Qý/Ä¿¶¸Ëü¿)þ:ñ|e)¯g÷ ÙNíC6¢w„!q6Iú2‹Ù¤I“ì×_Ͷ\Å·Ï?ÿܦO§C¯ƒ2y+»ù«ÝSO᜴͙hf7qLÞrìD g´G†]I*lB% $ïL«>Y<åˆ[ ò1·é¬¶Í~RóM·X'I‚ðEÀ¨ý*Ûã­vÚ•ô’}«=ðºŒs Nþ{ogUÕýÿ¯scŠE)1,ÀîÅÂDÁÆVD±»±Ð»Ql,lÀB búÆÿýÙçž;g.w†ãyù}ý îœsv}ö^;Ög¯½v,±±Ûe?;çÖ”ç) V¢Å&äÕi„&i^‡ù'HOWÕŸÀF‘&iG ÈC*ס¿‹4QöF SÜEš:M µž@¸o(ÚÔ•Ö~mlñJ¾oÜ*gð®Q{h2׺øw¥\¦ÅÎæïú¹^L$îÅ-µ¢{Æå6øRtyTæÄ÷ÜC”v ¾n˜ Ÿ‰Þä‹€ ü5·bRjëÌúåô£Ë‚ÉþäŹqNk­o¼e€›N…>‚ z.•gt‚òí ¢µ¼ÚÃ#ÝB9ôÍm÷—0YÔ¶­úHz?åÖvÿdŠáÞuRð=™²Î’¶E”º“É÷´àÉÙ{óRl[êç4tö€×ßXÖÖ¶›O7‡ç•ϧÕî4‘ý­OåPZìÄlZÁÍHãp߸7öÚj9ûoâ”L”®ôÓ¾æqnúÊ}dBA„Á(5 §ÅÎÂÙ~zÔ w{eV,ÙaíÑ7JñÝ~?¬þvç[o®}twõÔSÉ||«h¨æh~ñŒ (J¿ÏŸPÝ8GÿÏ)Þ†u§ü: ÛˆÒLŠT èå¦÷Î|Ã9•;Ó¦‚Þ`—È‚ô“Ä™à„‚§ÑDY/õ„s?Ïñ@ »2Rd½8DM´}Æõ#æïƼÂi•‡ÙIØ›¼™C¶Ú–¸ÚîK-ÇÜB+Gcv ½8´kb x[4Ÿ]ãØtÛÞÖlÁ÷ý×¹ëOs¶úZìXîšãwóc{zÔPyæ[܃þÂ÷K…ZCêi}ÁF`Åz”Ùhš/i1s¨œÀX-„^'í8G´3iREFøh¥ZÍ0~àv‚ìs€8~}»‘Sýp2ÑP§½u‹3^Ì¥ ·uaTËEÐR·‚1v‡AÉÄÊÎlyV±+€ç2;Í`K=ÃïQ·(ÓË«¶¶‰û9(h&¼ÛÖ¥£ž à+z \=§jÔKT¡=ø…í›ø/œº–¼}â`¯S*÷³ŸÒs3>±žÎÞ+ñeãQ`HòjJ;½LKϾŒÐ‚¼3šˆ±øpK‹žß<û|¤™FÁ瑨[‘ÈaðÀØî£/xÖ-ˆú/–pºšâÕ9„½•ë§ü±'|bz¡=îýn}lºÑÔhïÙNÜÉ®«<ת£‡ÚúÑC8¼ì'`ù{²x¬="5r4áÍÎÜÛ·ô]Êw(ßGÇà§~¤=…±#õô=B¾êŸgGI»’ë©Ôû&±#2n,j-oe/rÈZ+ì{ž•xÐjš½m7 ±ãÐòuvu½6ã@¨eÉç)þ6°”~OfAyE»ñÝ»Zçªp_Û¾ÄÖî»v¿ ¯ØÂöK¥-V:ÓzœQf¦?pZëh+M\oç ²·OØ©åøÆÂZÔFÞlßÅ{“Öú®^œ‰éo±…×i¦gX4„ß-‚õ}Åð‹¶O¼MR_Úå;ØÞÖmà¡ôGg-ßÃ^m>›÷ÿNü>üZ+uÒ¥®âWø÷ÒNfà æwir/ÞIO÷ý«qÿ ¨ò"š?eìT%¸ fl³hgœ>çûIÇ"ŸÅwp¥<¦ýëεm§EòGÊ´›E¤9 0ø‡·©=; Mº¸M©o›a×:°—*»ú“Q¸*˜ßD/¦¼°§Õߣÿ—]Û¤;D²—õŠžjO¦?µEË;Zv‘— !ÿjîB£@ßÂ3È3`afQÀe,ógM–ñ¤aZªçÔ üùðwr¥_®à§Þ·NHx/ÕÜÇ÷·THŸ´P¹ßKûs°‘œ—a²ä›œ„ª>an%‰9Š2ҢΌþN°åŽ€Ò²=ûdfæ2Þ8[¿Ïùç½[cf=×Ïa‡¸ð5F†ßmdŠºAãÛå³^-ÌŽÔù`´øÛÛí‡ÀRûºÅ¾¶F{*<–oú·#ÝŽ‚Q¹Be­3 "ý¨×óH{†½”#ß·)á_ôy¤ŽfóBüz°;‚CüàÈ·L´L¿ÁwÂqÚ`ò2â~Ï;é#½]i«ØÄ柒n'~ŒÌ fJ ¿r÷ìþÄà!?¹$¿E,–ÔšP pÁ¥ ¿™òÆãÇÎi1åúSšïˆ^Î÷ùý¦óÈþiG{¤=Õ1d|gùü^Cº9$E”/c{ZË<ó¶Ú ~Ÿçj¼Ö5{—z…"¾3û˜{£¬Ž/ëx𽩱ÎEf-dάq´ˆúx•ú˜Fžè“,ÄÇ¡¨UÓ™šË×!ìHïÀØÖW#ml¯:^ÁÃ…Þ|Û©Šq§I”éwˆs-ãÌuòÚ̃? '¹’׎aÞ5©Ž4÷~k†ò Ñ'ìª:)ReG0¯Æ\õ~äÜ·X„|9”d…_é?ð;ÉKp€ë*ör@œøšÇj·—£Ô\rÊ—ž^[ŒöùOBv¸Só×/|ÂNŽr©{©ûØ´§šš›1c†5kÖ̼¹±ü¶k×ÎSICøÝwßµ§Ÿ~Úžyæ{ÿ}Ž‘À- ¯¿þÚ^{í5ûõ×_ÝõñÇ·—^zÉ~ÌhÒ~öÙgöý÷ß»à2/¸Ë샀*iOž<ÙÞzë-—F®Ò{å•WlÁ‚îJ÷¯’´œeÒb̘1vöÙgÛc1ØBzÿßI:±ïŸ ÿUþÿ‰¼+Í[þWðëùpt•KÕ¹™ç]úDlp/¿™îðߎJCñ—Ò9ÔÀûsçεòòòzR1kŠé‰ ‘ŋ۲e˂Ǖ^3]ÖJÃå”Ìr.ë^aJ…¸oÈÄÃë9q58„*H¦À‚ãj%5A &^¾® IYL:ÿQªÉs›˜bã‚Oá{p4_ŒÌt<ŸW·Uê<5ý!WËU)Èe ½˜çWÉëô`³¦¿K14É—çú¹¿ö=¿1ý«´„´; õÎSÜúL&w œ¼6x°`ôÒlÜfÜ=‰¦å<ôM\Åß™açFÝšÉuÈUîš|TFýÉVgøtZùµâ§­Á+#µ‰ÁL£êæ?7®Ê¯ïz‰2”`ö®×IË5Æ³ì ŸÍÄLߟÏ|A(Šåäx-Zo“NíÁNáÐþýC„ z Á¨«Ù‰Ê-|ø`û ÀµZ;›kíÑ5«­[ëêß1E¶]½Â\g„¥=}7\vt÷Þ²<¬¾îÑLu;erùÆDó„+ÕcçôÇÔ•¼Ü£q ú%°?qfæ}2W³«™äNÍ~yÖ9{#-ƒ<ëšç¦pªs|­àt§±<‡ºD â×Õ¹gÿ6Róö‹ïBôàDë U‹“üÚ‘ ð [îÚð.Z|—õù}Í·žÅ5CųÈʈà)sÕ8L/]ðólÒÏï6Ügøa|ê–¹Ö^æ5ûÈ-PÑè}Çä„¥Ðd,¤í,B‹8Ø;~„:x0ښŃˆM­MÕd\c‚OíCBÁÀH±¥ ®Åy;!õ.@§·÷Ë]9¢í‰ðð.}¨4ß´höNÁî“NC]‚gÜKàòÒSíÌØáN¨’mæ-}²^hF Üc”®¾ØŽð,'tt{$Fn^Q9 hä ¸À4É׎wJR›X×ÈÁÎ|‰´ŽÍCæ¾ÛS€ œ¥go¸ôפ| ¶U%\®‡Ð¢¼gù˜²=Pátò× Ô’)@U«&íŠpîQ¾ OôS¯èÁ5$E%doȯ ‚õ/äkoKG†q˜Ýϸ}nídS°L&VV ²^‘mÜNžäÃߣVØ“ƒ±³÷6q¶×µý¾h:î‡û‹^‹”ˆ½(ø]É¥Ãlnô(äøïx/ïŠÊ{6cáh)àÑž6.:‚…ß2±¹ ÐáUÞØZ\M³½ÿ ´G¿µëeû61v§­Ý >³ %Î%Ÿ ‰‹-ÙØ®J€w6ðð$€§òÿ@£ô ¶ŒO+šŸÝe:0ö©ìKÏÃîVl§'ß²¿G¸|ï§.• *ûÛõ¤ãÛú^ηÑßT)¸Évcé å-£]`¶PÀŒîGüx~¸­‡÷·h¹ßQôiÑæšá§œ±Ã¬îã„Rú©Hwܶ!ïûßÁì뮪í­}ùºvoÍ™¸Á£¢ôçÄ¡þבZ¡´IµX£Ï‰LÕíÐúúk.µÉÇàˆµì½ävnÄÃÆ2+qiâW?¼€ÜÄGô¡‡ÚG˜!i[s<í•yIì$Êæ ’Ü–pËÈ#}²úR·8“·n§ó=•þO ¶-z ž,æÓ“Ýìñ­Õ7óoEhÿÀ¦vüXYOÒùPUüÖ´6²8:(%Î>´Ã«Ò“§ãü05€æ´·;Ù0$~'ï a\=Œ]Ò«ÛÑôs2·“Ê0ù‚Šöün*8€Ô6؇ »—ØûÑîvqüeÐG¡aw¸»×ŸN¸,Žu°xÅ^ÔéUÄÅwè»ûð.žY‚]È_i˜Rû¢ «¨3FÉ-äŠy‘9é7íîjÕE…\BÄ·F‡ñS}úôé{1O¸&3?cé{"ÌZ./8‚4ÏÁ¸vÌÞË^ù ü6'/ÚÑaØP¾£ä=¾!Ê.§æ˜|…’ÅlÌIÑGxë¹xÚau}ô ä1¾¡¡_ÅbΩçñ.˜*b, ? äù{~ómxª“!=퀒>ÿjîŠ&ýæŒ,2©S»*Añ˼ן7À¯ÐmôßOÁ#Ûñ½=àÓ ŒY‡°H|›Ï0FhÙöYAY/±g+áo#·öß›é(F{–XÌšdk PNÀs Qê>êXýáìL¨®öRü/~LÞK=¾›v;Ÿ2éŠ;ãoHŠ(}÷Ùï;²õFu6ŠëóU]ϪÏÓdÞ½À®ð†VÌ]G5|ÿƒ¥íTâú{㢡øŽ£(Û-E‹³yè3ú¦¹”KÃ$~ x–>#wšy#ZŽæñp¨£9”Ç:€´Šq Òy5á†ÎÑýùŒ0{™ÌÀ}ʹ{¥Vµo Ÿ¶ÏÉç!Œ»¢"à)û1³³rSžN'íPƱGX¬?#Ò‚= š…5L2Ëx=rJ}¤¾Df%eÑæöBÈV¾T[›yß#M_Q‡,[1ù‚‚Ûi§œé… ÌBÈTƒÀâ–-[Ú¬Y³œ-àüUšóæÍs¶‚,¯µÖZN£øÕW_uÚÃ:0NéŠJKK9ÌÁ š:uª ÖÁrmÛ¶µ… º4~ùEx³Òˆ ¬w9÷¿òç»ïüµ_¿~.™ÂÂBÛtÓM]>þJº¹qu`Ê?Aÿ«üÿyWšÿWó¯ÓØë¥BuR>­ÙÁ³Ëwf…N~XäÇ[НÐù€d)ôgF²Êäjá#hk:> i©Ë4Kx‘&𠮊·|yíÔEîÿ´MÓÜî½6ÇA®j¯ ”j6P.x£ékcH[—5ÁËG²™`Õ³¨Ö4ÜÖGo8 Ð÷õ·”ÔòÏ»w'_µæF[ÁóÑELsé ²Ü¯½mË\:‰‰žMgÏ‹Üö˜ÚPÒ¸þŽ­Å¢Vwq •?W.JÞ?Ú¾¾$ë³bÞ³^ynäÀ«UæF“Û¦P_7áöc¨w–f…³Åè€4KË´%9ŠÀ¯Ákòò¬·¦‰ûd&YÇ¿éæ¹œ ćL\ÒA[O ¬@, Óo<¬ŸÑ‚ »çÞk5 ‘±¹”@ˆÉÜÊO}š_†Î¹Î¥8Pçp9éÃ5DÒ&X‰ïÔ®R{ý&ër;˜ÉŸ43ÎÊ€æ™Ç¼\²?Ü’ÞŸtÓ¶2ÄB%†óg®»-.[—ísZ`Èí–«MÀ•f|3bC^¤>eïL6ˆçq ÌY8C#€`ùH‡‡åÒ'ÔÐë9¹aܳìøAÛWŸä®þŸÎ‡»¹[™h˜üþ¥/£ŠÙ4]þ[8v“†X–¾ÎÞ7§{½˜[Ϥó=<ã”)G¯/rôføÝõ¯^Þoj[¢£øû? ÓÈjüÔ·9Š©.2„°,­Ç'Ê}¡@Zy&± $t„¶ª'ßwÚ®<Fx}´rBž­ï|‹1†E˜]“?£~”}xœnþ.‚O3»÷''¶„;"²çõ£ õwÕ¸Ù2z£ýŠÿ žª0A¥W"ö´²!h$¶g4z™tSéͶPì#ÚÿÂâÍh¦(_2¤…’éUý¬ÈŸ€¸fß»´†2ntîaÛÄö† £œÿ+¼yrÕµø·Ä¼Äê€y*ŸÚþCÒú€m+/e±.n£ÒeÎEæ€dcöpZýÓéïÑ™‰X6w~¼”«øD}ÆÏ”ëa$ñõ7Ð÷Š·:ù÷¬â{ñS= pÊ–jú-~3íknéŬké<[¥ðu» Ó ¥žÂµgv ä´…ûÛôkWHh•ýU@U²£Ü+d©cj ëZó-ï@ÈO’Q§î ºâ{î>òÃQN†¹:$m˲v¶>ß¡ôô®k9}ty»¦ú;$Ò^yËV©š`éšQ–Fc¼û­ÇV²%ÒþÔSn»ñôl`Ѿ˜ŸXjWÇŽ@°êmí¥e Ð>²? þ¸â/íÂfWÙ¼¸êò¶ä™ìß'Ÿ#ßâs³»›±ä]ú"»,(6[f=bûØ“‘íñ]Ÿx­ìu¯‡Í(¾ÝúDöµ±2’ðH?Žÿ¯ØQm]SÚmžR+à×ÎfbÏöóä‹ö>Zƒ,߉üLœ‡øý€&ór[ŠÆßrxóÌè(ÛsïbªÄÊÛ(ìï^ ?_À{·¤ ?¶6± lsÚÞ“‘.[>Ìzs€Õ©ï0CQd‡-]Íf'gZ‡èX›Wr å»…]º¼'¦uQœéŒl0‹''T^ÂâÃäùWòð4æQN¶aLeZ-˜‹E:ãÿ&ù½”öç±wgê„r‰n{¤E»‘.ãV’z—¶-üÑ¥`Ïh[–³Áå»ÖOX€#iIÊvõü!#kF"Î|ßhÂMá]_bË}ªÍ• Ÿ0½á´»3»"fi€¶;ªRõá·(ÜŸüÝ‘VÁå®CÕœ†^·¤Má“6><¶¢½èã:18MZ–Ü òÒNðªCçR|@ÑÊ„® —Hc  ¶³ñkì1Úã¡ËºÙ9€¿.NqÊJ »ü é ³YÄXà ¤<è÷¢›q‹ßÝðÕnüvæ`Ì”Ÿ¼uüò—]h™ Ñø"à7y'i½h;4ÿž·Å3ñÛ@~Õ¶æ¶Ó6­i̲°õæ È›oÀSÍá“ Ü8§¹×Ùn¾K^ 7ªá€ÃË­4õ²¥Ó×Ú>e´Q•gd˜u¥mô,ÜŸ÷LUP;7~¾ Hl›éû#¤{3 I&È«(y-aoòïù;Ç;Ð:œÉ!kä ugÊ'q+åzå°)Ï”»He]ð¨ú+S ]Ûñµ;ŠÐ×EÏÏ܇FñîÇüzbÁGÃË£gQ×Újäu øl#öžW}+‡†-õãi÷‚´¨Ãø!y­,zœU‘—Þ´gK~j÷VœÉü¡¨›y|ÁQ¤$q ³}ij̉†yps®˜s[ƒwŽÃ¬y†sŸ…¦­6žîh÷]míÓ™²Â÷–ø-´yÊvèÌ©hŒÅwó‡^–’O´É-¦òŒS®â—¡Ü·Äí8ò2ÀO~\ÀÝr~÷À+1f}Uø";JV§\^&,|çì‹Ì&&'n7û‡zuïÂái7&TôÎzY =ùàßï¸}YëÎvMIÛ¸.Q—Y‚Ç0íã“ꎟÚX––·é ÀVâB>Úž>A ]»$Ûå‹îÇ6è¯y'rAÜ!ˆ€óYLÑ6¹¦N²û6¶ÿ ƒº>ˆRçº}ž>B‹Z>òÚÌ.œJüà;ƒÖNò;Þu«íÂw<éçdo6ŸMi+x¡Oý![Ô¥Å(:'àŸ„¨ÜÊ*[ÂT7kg²Ÿ¼Š÷½øq]€i$ÀPÚséx]úßÇëĵ¶ÏÔÑ>ÛÙY3©F "4ÊÞ¬¨¬ï½Ä¿ç¯ë÷#GQ,»ã~.~_(»ÍÏ…F0¿W54SXŠ™œ{y=y)ßÍ™«)%¥kâ{;-õ[̽Ä;0X¤†,¼Ý^4›¥§ØË¤á%¢—G›Ù5Àþ§S~o¢e}ÄÒê1‰­ÕßmlÕ½ŸxÈVuöx9`-Ü¢MÉë8¶Ñ[Í5¶ [À¯¡M]’nKûôì^œ“0+¢E'¤#øïEX-}‡ ÛÁÛ!ë@>W囡èö=’kxþ\F®Z|Óa†3ø)­›ÓkX_xÓ'ŠOeîXŠØÙ¾¾aö8Ú;-9 0a#Êø²s“ƒá¼{÷'QGÐåÀÊ{–Ÿ„¶î »}§)·Œæž ù<·fFp@þÕì®ÈFô;ö3íoÍØ-|[êç=Þ±1õ߃|>N]‘GizK»QnÞ6äC|¡2 õcÍþ°Ý)É#Ù‚?#ÚmŸ}l—ØAvVÁPÛ#¾;@íƒ63ùXH‡ ê:ج>NN#!µ±‚L‚óÌ[Ú äGû¶†o†žŒ¬Íø^€iƒKí¶¥Ï-_Ûz-ÝÀ¦Õ Ãw=ò6˜:½ÔÝwˆdï¤2uTq¹uX¶ºÓ—éñê—ÔóîÅ”©·.ñ¶åSÞæ÷‹M/<Ÿ…%ê©z á|óCúuëT1”¼^À7ζµ(ï5ág+ýªWúµ+g+÷ lc‡5Ÿ‡_B_N¶9ü/bëÖƒ_"ÇÙdÒx›] 7,§ž3ÀúbÀÌ‹“㜖ŸÚ܇%_²ÄÑÒÎaßšOšá'2÷\°»¼Œò¨Kyl…ƒ¾q{òFÛvÏ ­ïâñ£-F£©Ý„c©;TRsHæs’”§(Eý8Ó!ÓI޶%¨ þL^Ã{g!$iЯ«ñ­É´‹Dv‚_ª<À®>÷wÝø1°©EùN6Ý{œz?Ï^*yÊ~Eÿèè…¶ æ\vH½EÄÍHás—ÊÚÑ›­kt”»çî¢yb‚¸,';rßÓ›<~ÏãW6„¹Ê™9¥ó¯Þƒ|^áÿ´èYð,¿‡lJjª]Uy'AÖ ¬SÕOiœ®É>ã€>·ë@ jäpÜ2„  JÚ×ôªmrúK¾Ÿ¶^}~àë_ÕF‹ˆï ªugaÍèÓb¸td<úÎÍÌ3Þ(l|ׄiÍÌÃÌ[^um6ìëß³8¶§J;ááí±Û0õÇ£¤Cv)üýÔ£N’­ë<5þáçÆmzHÙ¦ælWçêgÑ&×N†"Êm’_zNu¢WHÅwHŽ‚ßFû÷šO”uà™1<ý‚ÛÚ,š ¾˜ºÌ*nÄFavgKZzКòDÎ8i~|%óx)_ˆÎIýjý‘!O§­æ À{h‡0çºóGãÐð•ù•s¸×š/‰¬®C -z2|A”¼C_⨳I9é& ±ýp›Û‰—•SSÙâô|v|U8¿¶,zw#ÜÀ’’óàO$¸ù3×h”ʧí›/=GÒØ]uÕU¯´{u/’†n˜Ö[O€O²Ÿ+ÊÕ6ô}ÙL0¾»mÓ¦M6Ý4‡@ÙE‹è¤BÔ¶sgg7äô§oµµ>÷Û¥¬Çþ/PSò¿j–ÿ=_¶°¼ìRþjF¡aþ+€ã6ç@œ"Ϧ ŽÚøA¬qÿÙ”]ÿƒ|#HfòiÿÊœŠøôàƒ¶ý÷ßßzöìio¿ý¶Ó È«…“=öØÃìO¦ðAÍ_-ªÈLJûž:öFdåo "pº>ZÙJh …[œáÝðv%¢íé ÿ.ÊL‰]r*®È¦r˜)¡!鞀ޤþüaÃØjæwì_S®dEÚÞ* ´qà÷1ÛQ/2Ù_žç`¯Ú\¡mž”V·L¹1}3ÙôÕ`õ‚ä«i,ó0~ÚЀáô÷àº3uP:p­ûÞZ׺w#9e9—‚í5rwA¸ðseÇ`ÀÂ\ÇàðWøûÿð5ñÃnº½&×%q¥ÊÖ쟡=ÿÏ Fö“‹ºou6aë:5ùI\ü*ß÷õð¿KPÛ»3ô(e–››{áãà*D‡‡3‚8ýÎDç±ÑÒ|uB¦óÉÿGmM :&ͦX¥ÜÅ¢\MÖu˜p‰vu ÇÊS•=Áúè5úie.@¨µŠõÑ|bG‚WˆT‡Z|øMœ»ãrŽZ!óÜŒVµ{à@vÙZmˆîpoÈ"Hv¡ª¶ã²bš²? &[·=Çf&¤w#Dy1„5øU 3©g±ê}é<AS\é-åy®™.Ñ7õ‰°‰qˆÊ¿Yɲ]2nßÚ%þ¼ƒ^Æÿ8;,Æ`_‡çðøS'÷àÇåYw™ÿ òœuô’Ï uœÄ&YnÔ¢ƒ4SÿAüaÿîÚ°±»à·!øïb1 Þ}(‡Æ2io»¬Ÿ=𑌞Á‘WÖ«ú÷½Ò/m‚qî^;/Þ‹íoi¶®¿iÅ6ÑžPX4VÛÈΓٶéÝ\x 6"-Ò’Ï)œ0ÿX´ƒ’žcÇÔ`±}Jºà¾IO|m‘™Älêá,!ÑCùmŒðÔ’…ƒ`n;~<ÙÖŠׇ­Åýж­‰¼IÙŸÉ6íL]aÎc^ÉTìç—RGçØi©ëy~D‡¤­bÚríW ûðü×í(€…yÎ&±?®9!RŽÒôª9Lw¶) ¸%©ï<"JO³7Ð!0º#î§ö<ç5ƒòOÑF·œ6™@Ì€Þa»é1ËFޱ-X¤šŸƒW%e@èªÛàMÊDä4‹)óÔ,ÞåóNONÛh5?ŒþrÀÓ9JÝ]Åam;QÖï{}l Àþxù¶Öµú`ë©Cܼ.v\徿Åϳi˜hq6PÓOò^õOs”D]dHÚB[y[ئhôɤÇþ5Úk÷;ßÏÙòÏ‹ºá¡Ä0òhÙÙæa ôè`Þƒpü£×Ÿ¦ˆPœVÙ¤h±£|0Ïÿ%îÞØÌžlnJgÙa¥Uöcœzb íh¬v/<Ú~+¾Á¥7µE•=‰Íßqé@ü̸K”ºƒ°çYY|”Oø4 6 ‘ë>G«³{d]ò‚©@ˆWt!å³9yä;´ #-Ý êPìž²l,íD °ÅÕW|ƒ_?û5Iþt8²?Å|_øüÃ×Þ,: ?úÒÊ¡øL?¿¾“4àÕ‘ém«ÄöK%ïδ9Ãî3 l¡u”íQGåš÷kÇ7¼ƒ¹£üÝâAÕÅøÒ?@‹Ó5l§_b «v±hòZ³)ƒgø¶Óí´ÂKøvú‡È"¶»Wa¯» ã`W|vm¦†<^@–€ ʰ½ÝíAh–P1fŽZÞÚŽA³]‹§Yê%{¼Þ†sèe4_Œ‘¯QðÇ!Ö*v£ëW¬ò&› ›ÆÌÁCZLi6Ó*˜ËYÅQð<)¢Œç²rDº“Qý_»»‚r„Ï­æ6~Ôaá)öiâûMÒ›3F .e¯Å9µ¼WI9`®Á°oíz"ÚhÙálp|‚ƒ pèà‹ä«¯MƒWn²N¤¿'m)b7iA¤ê^Åm3®jÿ íQÆB¿Gb“Óà•£‹Þäz:õÿ§XáyçÙ@-„DÚÙ3¤½XP“l&{›º4tÐhÖ|™Ú7ýù @×ÓÞôkÒ(W^èÕ,~ßô÷|ßëÀÆÔí|ƒÞßžo›ßj´Aê­_3õ+ÂMÖ »ÖƒªAíÇ­ å3KÄ%i3êc‚ú Íž»ª?¿Æí ’²I"º¾Ù]Ë ßZ±y×꘩Q›î¿P¯Õ“©SÊß_4¯-¾¥ï“Àì mÃõ«x]´é‡²Ô“æDý{™¼È=9›2T»ñË£—úíøY„!_LÁ¹ñ]|õÝœ‡ØdLI ­¬Ã¯³öTgÎÆß;˜‹JNÒ9 ZHè¥>¾¶–Ú[‡¢°Q¾J­SâD¿ÔoPßÒNusÌÚîNgg48?eÁÔ¼}rb…áõëŽ(#êaQtTæù_ta7ŠÕÊ ó\®­ í÷œ¹æ!·ñóǹzé}áÃΤ‰v=Ý¡9t~Íéßúеà'™Ó’—RVç:ÿCéã¯Ö\RT4™Å”Îö²Àñjõ= çÜà8+1ÉIhñb <¿0ïÖ<6—ÔBþ45oNÇÕÈÊO6{u¸›€Ø?þøÃ***ì©§ž²'Ÿ|ÒýdëW$S a ›bæ°ø>x¿ÌAéê*Ð7ZQi²ÿ4rÿ.Z}õÕW°ºdÉgjâïzÇ?™NSò¯ÎïßFMÉÿÊòþOÙQ^Ù{ÃþG?œr@¯ß^mØ`T5¹5…íß ŽÌ¥ha¤S§NÎDŠçak/³¨¢ö!PW~*KÃ…ûn¹¥k3ˆdþAíªsç.¶qo:ñ˜â¡ôÿ®:™’ѧsk&}" ÙÁªZ¦‹uîú³&”M¤„—0/’]Pâ6÷:·Ü?¹öLÿ·B“¹À­±×ÜŽ>ÐÆ•ù†€Æ1¼M¥ vcÒTšbâPÉnQ˜ÂkË™$Þ ˜´{H`.ÎÜ k mâ&nM‰±bØì^ÑÇX…]¥Îþ|aÂnáï »¯ì^ôHxa4ŒKL¹mB klœ Ü’úè@HÂ×%L†|ë•KíUvšWFÙ­ðõÔôD§^%p=å!-ÖºÀ ³±¥µ’ûÖÏáSMÌDÒ:Ï%ñr˜ŠœäÉÂ„ÔÆ©¾VZý§‡Ìä.mZQ×öÒ'¥ùÉ×ʨ®æ+í02ÜEùmg²Ç·mánøõ¿³ùz#-*,!†ºcK¯›™îôwÜ¢­êºy W œä¡|é?ðÓàŒIæ@¼ü_(MÙ×B–hmøS*Ø ÔäU 3‡¢Iæk›¹àùÿ¤>Árõæ (âmä¿ôÔPø_qÿ2û,-SF;ÇñY7ÿÆŸ4ç8úîp¦õ³^ˆ¡l¬÷²Ïþ |•žTÇm[à“À$˾ð°¥ÞaB¿Ô&Vlh:³žÏ'³´‡Œ`¼Âçeì¶Ø `FoB×!&ŽbÜH¡¼9 ¯•¿±Uq—mS}”{ç;ô¹k&¦Úm˜t õì˜˜Ž¡§¶BÃhsÆÊŠÑ«°V,ôJt KEް™‰·XL@€€®ŽÅº® ‰DØ4i….âw€¸/cb"æ¶õ–J›¹ª¿Ýêm`OŽGHÉ@ù¸å… ˜e/R—“0w‡çÚ¢xr¤#2£Ú6k§ÆÙ=('»Þ†§vtl7B–Û ¼[üÖxê¢èÞ.Lw4ðÅßýJ»X… Ë¢» ï±^ŒÞngÇw#Ð%@*y'pßèlrK[~¥ÙK=ˆL& <—ØëÙÈä‚K-÷Ϫv3 Qºt,Kæcׯ®€Ó_‡G œ¡!g±ÜÌDhñ†ÝÈw<À2ÕÑ™y‡‡ÐF®DËtO×WT˜ÚÇÔä…¶ u ˃ý½ö¶}G eÒ¦ŸÛ*åó©Ú~7rqndÛðÔšM£ìã^¯?ÇÆå¹¹] ptßuRµ¾ŠÝ€ûWýgý¥’½G·kCm ÿ–h€~Ÿ;@Ž´–˩ݸ±SàßË=$;Ë5×Ûº,dœ)Í@mèp`Í~Ãx'·yå˜XèNÚ«ÚgEÙIÑãmÎòÎVŠÆb@떀ʳ–µ±ÝË×´~KikeëYÛÊ—i=íF–íHr½÷~\'Ñ \§æ;@ÂKØ]„ÖaŒpñ]¶^E³uéGlÈ’U‡ò á׆XŒ6OmÛ‘oú͆—.ÄkC˜n´Eú”fïßžú ¾!èî\gñîžøŽ?siáh[eŽÖ½#aV/ºŽwÞg7ůÃ>#ã’âûQ^7[OL!ÜW0P;ë´ïW‹‡«Íú±Ð°ŠÆ0À¶Ã½üÈñ6‰÷tœïÚûÒþ¬EY¿ Oíf*IS“Ó¥€”í­¸Ù ÖíåmIg“è ¤Ê¼®èbÊf$uD¼âkøî¾ôó[ð-ô5Uwàÿº=Vò\ÙÖ¦Â{—È–0y]«C@2O¡¼C‡Q–¿à]€6šW}‘½(ûÑ7%…Õÿê3­Àö° þ3Õ†Ëk†’öjäùtó…Æ­â»`¾Z=\§BìQö5GRžðj”z®<‡<¬ÇѪֿ|kÂøýëÏJGãsj*n¿ð{“ß瘫»ÃÞ¯>Ï2Ò}„oHýšHü[mxd[¾ãYÂТÅïk…ûQ²ã·ö ÂÑFrhnê9›¸ËN‹^G kaÿúG ˜´®Ò“}hxÍXø\µð6´Á§Øf%·±€PBù’& E]Xêl‡ÚÃøYу¸QrÕûÃ5iÿMÕÔ“[|Óã ¾ï{²ü0v<ÉsÊ#³.  V¥´Ww¡¦Û_<jmcaö"Šý9üD…Ϩ¦VlàÂHvûÔ{Á.Oˆ_*éý<úß–~üõæÀ^££í`°¸Lý$^öýëýK[`áv /š[Ž#š}OBsóÑÞöSå¦ìÎù5OJê»Ëãî;i¡waP¦ùBé@6-Ô¡õŽ»ëI({iR['ÜnsõàvÀÔ ÿox  ÀÆøÐ2€þ¬²Œ·#Î…×?z•\¹2¥µñ‹Ô?‹dfÆ-ò¹§œ?ôLØ s<9Ï`7»;v(»YWG¾¾ž28ߨ ó‘í¾BYc!íæcæt>©QfÐz”Ë}ì €þ;Kñ¹gÃH¶Ö®Ò|$›Ó¯dæà¹þpÒŸ§ø•&o\ R,++³iÓ¦9àI€“´}[´háÌ1È$CøØÉ â*|c)к•y‡pšºß~à€:¦š’îÊÞ¯ƒë:  Hö‹»áþo¡ìj@ž Õ—¹ÿ[(Wƒ5̰gþÿ);ÊM)Ç¢ª´z¥å+Z÷¡*ÈÌ€Û”„BaÕE²§lQ‹¤™.­oµÉ€ä×µk׬mÃÒÊÿùçnÑ&÷O]ý.ÏO=·NÖ«3!h8÷3Ô6UÑÒ‚nMšÁŠlnIO­§ÓTr~×-k“¢ZÒ»N ™NíÍ0µæa§¼N8”ìL]—Æ?šÎ¹1Ï ¢9>ulgÖõËÕ. û–èd†È¬sxª6¹ Ј›rʬQV€Ý+xà Íç|šÀùÂþU·|f4V–&¢v^ xNžÆz8à[ ðëjði}´ûŸ˜xMœN¤Ù— »€— ­Õ÷ŽÀ]s ¨jЉ˜À6ôB mÓò ÂшP; "è”@õ|_c*ð­ÿÚ'î¡F!Ðûä·)ûáZÅÏmˆUö¬´±òÐM©p b¢¯-Ѓ’iiûæ»ÉPü-˜Ä*ÿáE…«#½èÚ„Bù·{ÐÇÉvÙ™^1L0¥m•‡žË“ÇѼ;ܦWˆ†6¥Éž_ɳxåç±N¼{Ï¢÷WˆêzQSÀÙCêåE¥ï´©5Iþ)OzËp „¥–îŒ4—@Ð0©ÏÎjn9ЬB*ÚímË7ÍE4õ|`1›cgô°ì“ÓÇVCÛPýÁ´i®ŽD·ú#{QB/@IDAT@žqLÌ¿ð¶F认äß x‘õGÛð^z Šø9¶˜rÙƒz|,¶™Ý] {“1;¾º`æ(Þ¾ÄùAér¶ jtañ#¢ãsava lÛàêu´ô ÛŠÚÎë£öjO í-"Ǻí‹õ0Ç›‚F1u¶KžèÐÃѤ{8 Év^OLºƒI;¼n’¯†E0˜xò`á£Ä©#®ëB pü =ÙØ›2ÞZe+“IÛYÙ2Å‘¼Ø½}KxÚ-RVŸÌÛ2íü8ˆ¾y-è×äåv#`L°Qíö-BpøÔ’bŸ—N¬ÚÛæ0z¿¤E™ôûÖ头„!l¹\f\àÞ³06Öö±­/ÛÁï©ØÿîÔí©Û?Ø/¾Ê%Ú5@Ù9ß›.;ÄoϤô Úb\ÊçUˆ2Žú^—ûŽ|㻞DF(víîmåMl|ÁC84³§%;ëôù²ò»1uz0¿¾ø­‡™Ê_¶@““í@ôÛRÔqñ§Çà/Â7;Ï’I#HžlOÕLs` ;Å^ïƒGŽHL'üv¶ot€u,ŸÝ©b‰Ó(—GqüC6]p•¶enRòÛé‡YçÀj'’-¿ñ‘½mqy?›R3Á:KóèyÑ ûª` )û;È=“ŽÙãÚn_5ޮƬÀ‘Ñàòý'y—µ‰ì (½!eú©µÀlÉô+c€'GøÞø•Ñ(Ÿv‹}l½ãg€OÌiÆØZ¼ûâÂAö­„c¶Œ¿^v„Ý‚"‘£ò=m$¦¡1Û>ññžðÝÝ\§‚ï'ýÊÃÙºÿy߀ÝN²ÓJžàÉoÅÑ„ù›¾$“,Ê]î}j½09"ÀÔihj±¤’|x<³Û!K±Ñ¼>?Ú‡Íq&±ÞIõÎÞ­Ð"ú™t»ÚƒèºYôXÒÿœi (¸þ,˜?¢,:“5Õ€/ËiÉÛÑŸ]èýŽ+ã®Æ­Ô-.Œ;”¯êTÿž¿KQ 8¶èaÔb5WÙñ=ídŠñ´ï[i/CjžÂýHâßÈï!b̤ijߒ'¥³*?Ÿ†z‹mLáð›Ì¦°ŒcÍøf-`ÕC|¦²ÖÖ£jvú½e-Ä—‘-}€²zßL*¡KÍY¼kêyÿ£+¿µùÎÏ’/Ñ·ýlíá¿,Ê  Úº#Ú¾Žð‹t@îÖ×½o¨‹ßÆgí9+™¯´¡¼w"‡°ˆÓÇÆiÀÑ7üÍ´==W_jS©÷@†rAòýñ¶£Þ·¨õAÓ78plkÃzL ,±¬72›3Q»•r„×¥øÛÉ¿Õßè™üé̯½}=×zV¿©], ÌbzËT\¡ aáñ A‡Óþ:k¡À-*t -Ã7nI€K¢Ÿa×J:ÃG:è÷Ʀ^ ipW@?Òç,šxÕâÎ’Ü8Œ[¦qB$íWJ5K‘#¹íNyŸÍU¼ÐxakÜÅ'9cY„>Ë« K™D/ÔMÑ_°D$cèè¾;¼k¾(Mw+CÖînz¼?C»Ö;ªšLíóÇÀ¤J.µÂ¡óéÌÌÖe“æOâõ–«ð»ž9ÌãhýŠtNÉš^>H3·ëÉ¡Oh‡µ‡Ø…Ú\.víóˆàÉ¿jž\×-ÏS$[£¤‰Û½{w§É«ÓréóÏÕ°X»êØÑ]0ɼƒ`iÞê' IvGsmüºü˜‹ÐáSAºº¦OŸnŸ|òI#SɬSfÒ›ÏwóÍ7wÚ½—]v™ËÿÝwßíl ïàüo¡õ§jsV_þ˜ÿ[(Wƒµ(”±ÿ ùew¥·®igÀß!%²‡¹­4bÔÖDÒ¶H¶²E:`QšõòÓôV]¢îµX# |†?×¾{Ù~ø!.µ$û1ÿ4…»éï™À„)Ø*3 Á/“K·M5A÷Z•‡ÜÉä\µ‚¦ø¢m›ð=¿¹‚jOÍ85xY„oxnev@L¬Ï5ñ ´ƒ`®ô´âéP¶†(k稡@yüj mqFØäBž ÿ 'ihª¬KZPh*|Z_¼6õyä¸ ½ûø.LÒ Õä¢>ªoUXáÚÞŸ›Þ•™Å­&Ï!wrÐnkG0™®Ý¶]7i×3¡ÖÖmMǶãÜ XD=“O#¥DƒHÅɨF BqçLì5É’ Hv¶VvHœ ¨?E_p²ñö V@ÿ ´?â7“*ö¶¤é™‡ÊqÛ'¤lû ÜÄ]Õ‡Œ(^@5Á W—:|OùÓH´}>È¥çéãt:ôµ/e¼Vœ«ÉCÚÿ¹¤x"§1V—'ƒx²~eåfÁcèÊûrµZ _Êä`%¯”p¡°º­@H~0Ç-ÏcáëL~DèÙ$ãI¾tÒ|¦WŸ}囥·²·. ‚a7Þµ£³Ð¾ª»+ƒ :M"¯—3Ñÿ aî=@‘ŸH[ÒJ ÝJý¬¶/bS±—l <@z«¡™Šim ´µŽ)Ìy¤6² YH; ÞëÒí3ÛPUö~ÍGB<-AÔt Ͷ_õé¶[Õ>¸Z”FíCðè§ÔÿÙÑóù¢$þ)›êFöÌTÈܘ;µz¯Ÿ +¼ØÖŠ Ã]=c7Û 3iÀy|`N~¥u;ÚÕ‘Ù9UÛ!PÕXsìÒvX0ls?Ói‘7° ÛÕi8oØèäyKviR‚‰$ßÜN)½ÌÚö[!á+ä·¾ ÿ»Q´M²šÓ¢WyŽòϼnÔF[»¨æ#ÓéÙ–8•:x̦•¼lCÑp´Äó„iÍaTe6,ù˜½Ÿÿ]"ü‰Rºó«wqeüÛØøtMOLˆ(ï`£’“تŠýc@ů"#l®×ÑvM}ëGO¾Á»…'ÁJ½©ßcm&Z«ËkŽ“®wqkî8^Í%/næe¼½1p€ËÙLÆ(µ)÷ó…ì‰ÉùÛ‡ž3p¢GÙq%7`Û÷`ÛP÷ÕªÓˆ{aü2’)• /µ$Ú†Ûx½­Óþÿ¶´‰ÛIcÊf±­#|å(ÿ}øÞâEííÈŽV@¹oõ(4‚_ËÚFÞšìBH¢Íz%ZƒSìãÄYvˆÌŒ8жQ°—m ßìRx=éßíãÍJ_±Ÿ½.ô¡c8tîc{¡Ù{p¬ uÿøSÄÙÇŽˆßÂáƒ,:êœ|‡:>‡43‹ì¨2¶«K;ÍMW†…”¨Þ ¹Ào­š)ötl-;8õ)n|v¡ešÄÙYÅ&ð ÒÜM÷²‘(’=cv€7о” xBßâ„8-¾‰ü~â‡ué3#ª8“q@´; ïh<Æ÷¶mkÞ£NŸqîÎ$æ ¿MAo醸/e[o¨/-<ÙÞ*ÜÑvà Åb· ‡nšƒ?‚ÂÖ¡ê&Û:yš‹ëÐ:ôl[Lt@ ý™8HP¾ òLý[á!ÄPPÁ/Aýï„û î9‡Á+´Mb£(ã›Éå*’ äÊQv·ì<غÃëJÞä ½{l³”G þž±Wº¤¿²ËœvyÜmw·$}VÍãÄŽ¡umõfPýt#ðx ~,v ‰ÝkŸÅàáÔ÷ðìHÜ~#'S®WrŸé£”nbí¢#mso‹pк÷•²*ÿµ †·¥m«q+ ¦;á"+ÎÖ—yÊ×Îo¥ÏP+Óöö@Ðw}“çÜar©ºóQƒv™¥%¦Èq˜“`ñ:ì–÷ÏÛ:¯Ï_wœU7 úg ƒÙÅâ³èEøI»¤‰DYê !)*a¶K,¬­\Ah•°—xœëþKŠ¿âº&‡z}æÕä¾;Û‡Vf¹IG¾³Ûظõòcõ>@óœ½i ¤÷п¸~û·»:¬W},1üñþ@À²¡åÝxþÛ”wÚaÄßM«žÑI=¶¦.!ñgݫ¶†^qüÑbŒ4“¥íæÀF$ÚsYhy‹ë'â¶…Îý ‘Yú,{§›["­kÕh? ó ¿oP‡c÷±ãS™hù©æ``¬¯ìXò«¼åúÄàïÜöª’ñ 0ãîBïq`ìT[°qpRifpUˆJóŸ‘!P̆ö:ïTúi´Š_f“°zœ×›ÀŒú…oØ{éful‡ë`ÆÖú6´.ß.8×¾Àæqx÷Èêl;OWï„ÿ¶³¶Å£¹áû ÊÉQá4@™Jz l-l…Šîß Pßܾ¥’¬‰ç8ìixª†`Ž&?ì$ êñéwŸïÊ:.ÂwØ~/6ÀEŽÂ›¹S™ú˜BÛ¢z‚=Ys ‹³ðØ¥¶6åpŒ6í1–Ê&m\mÕBÃþk$á ‚ëc[¶­Aš“ùMÁ§@Ðe–(ë‹É“ßá¹¥Ö- ”¸‰¨ñÇÆ=`¹‡^…íÑþ,Ñͪ`H#̃øд`îUbm¥·bf¡†…‘c0ÿ° ùpöT±Œ¶EYʋْƒùö¹öZôHAÛÆªK¿'ï˜y²úzt%µï«Ÿµƒ’J{&é^bóR^cÛÁ©â163Ö©x @ò1Ð¥ÞÂ<„iKvÉû rŠ} mQ€o„çÚ´/GZÁ‰ÜŸeÛ&Ïïj»$æZË‚ýˆ ¨S@ņÿÙ‘U1Ò>*¾ßÖŽom_4»@oÀŸ~AZ†h<Ï)hד¼O§LÈOŒ2ÒÜVš¿aJL≶뀧 ±Ë¶Sð€æXdå{û¡ÅËì2Xº¼™kAVx7Ñ^µTž[|ù…ÛˆÚ³bSÒ›íâ=Ìøöctë_ÖžãÙ¨æ íøØVšœlO'~ œ[Ùm5'ÚÛ•”½v2À[Îörå­þ{Ó/’6õ'{ÆÇ#Ãò ˜„±xÞy¿N£Wòv›.“DU×þßݵ«¡ÜÓ† 0ï 36Þ§}Z G¦3tˆ•X´„÷óÊlQõµ¶a³Ç\xgÊAeÏço“çØÚ, èÐ;‹7zïڎ矉ÿ¡>BYËÔF`JÄwõÿ&žãÊØ£}E/eñj{r±gÇÑæFQ–[ûa oò¯é'¨×AÔ)ïPS½+×ß|?þžÈâ×µ´óÒbvt½®63Ú™»æ~]ÂužÛñënã_cÊ̯›ÅÔ͵…”£Z¤7€ßîÜ×ÒÚ,”&¦Ü®ôütN‡ÛeS¹?'üQC”u'o*Çop[¿bÜOæž|¤º»ÞÚF·9ô?%2³PuÀѶ d7â½[ú µÓžúÙ’ï~ÍÒÔѵ”“ÌÍãÄ LšnÃÂôÓ²µµ‘ÕÚ¯ñu-9¤;™Ùˆ*z•´ÈŸ˜UÓÓÁ+é/ifÜzPÖ—’ç ŒÊJkפ޵‹Gã·ù"œú”Ô3ø©.¸ VÈõ¦Ž·¡ p¯8 ~ÇumÊh_fndOL½Œåžö*J©¾çò£_psâÕŸŒ\`WÖ?ý²B5‚(ÿ:ôß÷cL© bžLn©ÔD/æ¯ê Iôµ“U&Îr¹r8¸y¾ ˜ýÃw$§òäÇÏ:ëû’¯Ô>†ï´è(žh Å({¥)Ê,[¼K ²¹”b¡ÄíÉõàPy¯gJ`²+Báæ7²æf!e~:õ¿þ$°wé—^ ”*“¤øë™»·hLüz´Á}ˆ´«e{¿àžÖq}àäÙJÏ¥=O—2>ÜG¿é´³5–@wÂ÷â¦Cpwk¸ ÙŽu;††ÐæiNXaK¯"ºÉÎîøsÁÖr^ÕcÎÝžÁƒH–Ñ·2‡­ÖG:ÿGòUøõ…mÐ]ÀÐN;íä€Û￟mï¼óŽû ’-ÑwÞ9kjAš¿}úôq@Ó›o¾iï¾û®™ú÷ïïlŒ6ø¢çZk­å€+iôÊ~©€çwÜÑ…-`Yîz`Ï4ýo½Õ÷¼ôÒKöüóÏÛ‹/¾h‡zèßš~S‹71Â_ÉÿÒ:L×Äg‚'ÿ\´l¬¿’ÿl"¡¦6Lüÿ·´Eüm:–Tã^J8>Oè3f¸-¦?iØo±Å΋øtâĉ®ml‰_™HéÑ£‡í¶F;§uÿøã;ó²óGG$êÖ­›[D)ûàc“Ý`QÐy¬•*W¬ÀL‡º¦eà?U¥ìSIp“s –"Ê”ÉÄŒÆSÊ¿LÁj[PÏŒ6Qð\ßÕ¦[ŸgîÙmi „ÉõòWû}×íBeß?tE¶:ÑdÒBL}¤-ï†0bŒÓÚÍ «úø ™\Ò]i­ŽG°ê½)\ܶÌr‰b­Ë¯«nþ4P¤^_À¼?þÑü轑LÔ¬‡p½T¥hÜê´†h>=“Ìg V®@úýÛ±šÍñ•öm}v“¥Á‡ˆÛ i h[õ’™úʆnQHŸfÅ$4yVæ˜Ãá ÅÓá;qï“›ØÕ.Ü0üöÎT†àøRª"¦“U!Šv…—ëÚË ]6ϦräV;ò•_ š0KÒïgÿ†¦VOåô18€ÄL¶[+½îÄö uU+d£5êFíWíébÒ–Dºaó ë#ÙGÖÖMó:× ‚˜k‡7[lýŠ>¨ã¾âÃFµN©»ìM´2ÔºÔ¹¯:Íi)Ièk2µorŒ:’ð8_ÆÇg|àa¥@°ú—¥µÉ8e¶R¥Ý_‡T¯Ö’O‘>ÂŒ¨‚øñËÐæd›ßôäœõ§¼p5ç¡=Œ|ìfãy&šJ"¢­%xÀ'_÷Фx¼?#߈03ËÛšwòƒdsþÖh+gvB¥„µÑóÞÀòòØ9ÖÞ‹¹>Õv:¹ :£¼§ }ÉÂä ¡aþAjÏF‘°¯þyBd1úhi´v[Ú›†^6TèàÉ©-q-xÄ çQ‰€º1š³2[q(‚Ê9”Û‹ ·Οä„ݘmç¦|í¸È ¶Œwd¬!‡ê${Jezb XMræ#Bos·Ò¨>A[Ä3€´L4‹îa]SííÞOÊ·îxÄÕ\´7€¶ ‚í«¯cáa‰µMmNÍ!ù_l|É»ö«ÀR4¢©opƒÿÝg‚[5ßZ5Á®,zÇÎJαr¶®ß+P‡Ô*ÍJ޶}ËS°3;ÄÞ(>ΞŤijén6¥ø» sO´³°<´ú]—§!<+ÚÕ÷johKëL°¸´uõy¼˜o¬}”­ì/T çûÇ[»H?›^u¢hS©ã’Jòüް|3´÷G•oàî-²ßÓÒ^ÃvíKÍÝ:6ûԿʤƒ(EÏÅAq>¥m1€ÿá´™nÕð\â%Ò~Ú´/ Ìz°Hp@m¯dlQ¹¨ubŸTæ)¬„¼_OV¤ñëÀÝLÒºèý%ñÍ—óð9QG:Ïk˜^u—u)Û”qékÅá]]0‰ôêÀ7¾Ë;u`^Ë‚ÛlâËø¦6¼ƒò@ô´â"´§Ëù†Wq߇9B1ß4×N ¼ íËë—÷±³+ðãCm¯Ä½¼¯‚/éŽ  pM|iÌyM±¢—õ€4àòè÷a¼¾äPÃ÷ѳL¤½ìšJþîá»(Çš‰,L=e÷xIúµõíE¿›=Ž´×ðËÅ¥Fþ5ü½¹\Qõ­ö;t¡º”íÕš‘Ö"r‘ëoÌ[ϺD‘' ‡ÂkGN}*em™Eøs(ÿ;ݳû÷íÍ™b(á;"ë“ý7Ð#Œ»Ev16·Ój›M\ò^uá†Ç³ÏYÕíõ–ªL V¿a‡@Œz@SËÔÝìºxˆ@?o3êýY÷]§ e¹²Vr¼Ryîð‹´JK&¯Eæ †=îE”G¥ÿ\ð<×uío+[»†û ò6ʺñ-¯Dèc£)ó©¸Sî@´ß¹o ÿÝÊÕ×QÖ®Gô›QLAÄŠ¾±rÙšfžL]ŸƒyoIwíå½è=É?&PœÙÒJq Æ¢ƒqÀ)»ÌòÓ ÿÂŒT™µ­nK«O³ù±œ)«ÜØ/GõÎLùwŸa\5§39ÍëÊ7POQʺÈ[hYTx{yoüèôíª³¢g(³G¹oOØí¹†écÒ:ËwHÝÿ\öDús›z¿Ž[ö±†„©Û¯íñxOë£}F/ÌzëL‰ÚZÊ:×sóß÷’Él‰”WZŸùæÉ¤sÃ0…s–ÒÑoܽƒì);ýÚݨy\@2­Õ Ñ×®H¤èÆ%Îl OÔ‚¨æûNïºÏs›8žtƇ<(õ_Y ?äÕÀíªøÉü³¤zIJO³ ÕZñ{ˆ¦Ѹýgíf{)+•t‚Ô·5@N¶ï b¸|hZŸì³b„hßòΛQï[9E§ŸàIõQUÎïaº/4­¹ŸdÞ€´~=s¸/âwÚîš3G8¯ëà‹˜‡É®¾ éÜÁœF)- ¥NçN³Ui§ã‡XIürÛš~. 3Pj»,µ„°~Ÿ)[دÚisPV5ó݃ð4ØÚÛµ‰F}÷‰ôÝK¸—݇p§eû¤N¸Îq x¯‘Þ¢èåäñü>ux˜÷ðõÕðiŒø‰ñö*àä>ÔÓR'R µ{â~*‘0ƒàl³Jk4CQ®uç'½ÐÜúÞΜÀ<èK¾ñyØd|r/!;ñûœŸ@†¹ü0 Pבû磇ۯئ´Ê^Î ñÿzÛ¿èx{´¢‹¶ýíâ«çÓ¶]Å¡Ö MÖç$€sÐRœÊ¨Áæl_éS(Gß4R âᛆÛVôÇ“‹²ÅhU^žúÌÜe$n²‹"Çڼ£(ÚNäñò8Áz”.¶·+ö`,Xl^Á¸Ófõ½¼ )£ªsI¹ îàÊþ|··­[p¼}Usñ'’÷BüÎc.5¼M²Ee{ÚaçYïävFj6 ”Ú„ÖꕇÚÕC çÙç2¯»Cªj^¡NNø¾šò߃÷·Àæµg%Þºè)/µ7š}ðæÙÑËz<ÿhÈó‘8æMÒv­÷‡m_>ÖZ&o±É˜C9ͽMšÍ¶SœBç&^¶+¢{ØýÄé kÉoÉû×î][ K™X–6mˆSu+~’oê¯zyÚ‡:⥬ل* ŒßãùS>>ÊЪ‰¤ Ðrõ¹)÷¤ã*Û¹Ĭé̶Že›XŸ¢{mr¼­Ý\1Á> ê…–ã•é»mðSJ½ÂT¾ OÔ¿W¤¡_ð"ùßÎÕÕUÞævvÉ~hiÆJ{¶Šq§x’ ¨ºÂNG ‡(í¦j ašóÃ_$-Õ4å û£‘á|Ïi\ÉŸ´ÝL¹'ØØ›[Õú•]l?J ·àk‡†ùl±ÆB„<3>Ìî…iö*cÛÌ ^o6Çú Ù^“¤/‰Ãã"i¬7{“kW®¼?½ˆ÷·&OwP–ðf)¬j'k»ÎÆÛK›Ù}Î/Vlˆµßr%q70ùÊ&0Èôe~â¨?u½*;FÀ[äÒŸÃïCâ½Ê§¼K>¶§Ó£šýÀõHüñ£Ò‡óŒ ÓoÒw®‚3oküg³ÈÜœÅý¸qð:À“§ç±QLŸ°@ ÕÉQ‘-Ñ̃â£(‹¡ÎÕýñúð]ð4ßå4ñŒÓ–ÞÆãíȵ·ç2q(o×_ð67¿|–oA}N²Ýc#1¯@›p*Š^A¹ÑÞµÁâÙÅûÚ(ï3›¨Ó‘ù\n¿¢0ÐÅŽa×ÃAð0ïôºPÇðu`ÚìRán¡/w'Ù†“–p bN`Þúäa¼Þê“x^hßLŠîo»±ð”ø\‘ÖÁ©ŠüÍäµK9IJ”1TÛæµ8ü%­[îcäÍÔN#hËóq˜÷ñÁqE­ü:“ù•,¨Çm_|]ø‚M¬<ß¹yBÈ©#ßu2I1¶:¢Îmô­x¢î‘@E*“+(FЕ҅Œëw3'û9R¦ÂЮދÇy ó³sÖ ˆv=­‘¤à)MN`ûÉ‚/2×ù(5AÀ&^µ³„ý¹š©P§´…0Ñçv¯>y+Éj*è«t´E}i(A¦ÔM}ýË¿Tú~ʸHU ¯¨‚ÈúÐWÏaÐW‡ÜÈ–c <’rJç@Ë-ýA_•ÁÊ@_Ù“¨!ÐW¤C—˜r;Ð÷)&Ä%ÎÕÿ£ƒ¿J(å'™ ´2ÐWáFö ïªAD®»óÞž+éI”«p=‡¢go¯Î4ú¦™(15wšsèi7·£ß™nÍ–Æcí:ì®Æ©êҞРߢmñÿOfEöb7h,è«ÈèKZñ˜\ÕriTÛÐ%Lƒx^a+Wê­l˜è ¯%h½L-i’Ü-A{Xt8Âîs¤q“û?G ‡HÀñ©‹´ÅøÏjåH¶Q¥ú€¹#o[„Ï#)‹‡íb/ÓFN÷ýø«Im_mkË€¾òP?+³ u@_ ãÂçJh’&®n²^ŸèÓIÜ­+¦’þ¡Ö­`2÷ÍýçÂrú}î×¥ï­ ÓÔ»Ôc”Á£yb…&ïhÛ ÛŽå8n'¬¸¾ÈiìkÒ=þ¾àtߪ~öPнå]Ýÿ"„/I[ªÁZÛ7uèYñ=˜ h‹Ùó€'5ç}ÔàÏÉh‡­7³9œÂ^p¡2ÿ‰Ÿ±½YB}pÕööaͱl1­&t"´Ðºó— ¥…ßÎÎ(ųÚ»þô–­[H)_B{|?:~8ÜÖGCR6µ4ÇÝvÀ½™r«-÷ÐÄèU,ž~D“Ü6ÅÓØ-¡yÇØ ­³…ß•cªÆ°SPEoØj©»[œNyM¢Ÿê¤ØÒÌSZèX¢ÐÁ€¢Ãp£L2Ú=¿£ý[…ZÒå˜`‰køó ?¨æJä ½¹¿Žã:׆áŸ}%ÀÕe_KƒX Tò2À&õ>>íímf‰ØA®? dÐmÄ~* è-éU-•ä=}E¶æí `ñšaÍ•«£"z€1ñlS×Yз~í)³™¶^ÅÙhv¥ýì˜E·†Ï¤`]¢ˆEÌ Ì bVLˆ QT0`QL(bQÌ`ĬE0¡ Š6O¸ïW==;3XLÿïõ<Û;Ý•ººêÔ©ª¯N à2ZÛ”ËFÙ4ìh¾Ä÷ÝÜ­rukê`K›èd'º~VrˆÒ‹ìÏ;ËO‚ö[m/²Öwi"ßNæÀª“cZÙ¨u‹7â¨MmkÈÞyOpþÚLË”·èÛ¸ñ-ïR¯“ýãìŒØc˜âpËÜ'­q¤—©¼‚ïkéÞeù¦›-ËÎAipIâU{MÁ ‚m(‡[ø¦E¼îF~³K¢ ¬õûIhyÝÃne¢»æœY”¼IÄ…gpæ½c°˜ô|¨¿=<šÅŽñv zƒ€¾ä½¤»ÝÈo‹Š¯m~ì9›RH[«¼Û&£Ñ9€º—Fí–\ù¡-O°/Jûx /_gáVöjîºCÛ³ \[«8tÉÆ²úŒøRø Ó¸/o~ÛóΞð<,Âÿk Øä¥FÚ: Xx'ü¿".|)7G¿ö«¤y¬{ú<ŒEŽç¿ÎEk=­­Ãì€íJÃiLF½¨ªãÄ[\OóL:Lô­âÏi}qb.¯º ~]@Þ6æ—ºC ÷ 5·/ÃÇZÏà.V£ {Øi˜ÖGùZ𾩀:|Ÿ°´-gW’ú+ÙÅÅuæ"|wÉ6Ö Ãß¡Amá¾È“}­ï³b¸Eœ_b¬{á$»$±Ûô¿µ_ÙaqhñVÄbBAEòùßpˆÑ§­w XVûi%BÚå½yØÈV§§£ã¸„l è{0afñ}§ÃOO³<Ñ2 ô¥çÌûrd/"u®íÀTU$²)@”IüÕ|÷êqKNây6~Ôgp ns-—²}†±fG:Â6¦Ÿ|µ'!Œ@_Q‚Zëh{DnrÏײs¡”EöéÖ˜œYì5Ò$ŸÎìß©÷P^®mcVÅÊ•ã•P’(ƒÐAäŸxŽ>Iþò¶¤©WG¡“ì…Ø4ü&ýõóu-_r¨ Cvt`K¸@_Ù‘=‹rl„ì^ª­þeúö_m;òÓ£l[î3)ÌU!e£ý Xv3%~ i+ÞìÆ!×wíƒl‰|¯“ûžÛU’ëB©ü¡0üìÎûG‘Öçhœ7Ažÿn…ÈÆs'дÆH³>úx^ øg‰÷õîÔä¯ðKCž“m;‘vf›§ú þYÞ³C›±ËâA/`Æÿ¶^~rÏMs]i7”ímÃX@¢2¸6sã±/Y`¥'ò(„¬.ø…ûŒ’ò}á¼ |âõ´(“bÅ‹Œ¿ÛSþ$軎uN…×âô³\Û¢rvߣ®”5ÆÔø½¶‘OFµ<¨ þU}¾bëF_t ¾WÑ6¤Õ\i§Ï•¿7°ª¾”ƒk;µÇö¥^õRN5Ï9T×Z—à‘6+ï¾TØ™|Ó”L‘„´Æ’,ÕHjÿZ(ÒÂãH)~ì#KÊ‘ñG–ד|…˜Ú‚ÇðP~¼ï¿®rA5&Ú5 ô•£¾³nßÃ_‹rØAe‚¾j_‘ã1õðj ¯ÆØ"µðãÉχá³wsîdX( ôÅ;t)×àdxïçÏ‚¾JE9©Ê‡—®û_“æœï]›Æ¯ïÿõ·6Ù|>˜¡Á”ê£qZ[þÿ£O¾üÿCþõ©ÒÎóºÈ¾ÔkmìõÈŠ:ÂTZð5Ñ©š¿Õ,nj ^ÍMS/Ìà›è<ñ)˜¯C§5~ÓZÛ2ÈÖ^LOëï¾—Òš¶¬×÷º3ŒÑ¡>mÀMo´ÓíEfk ö¦S} muÑëÿjÛLºÝDß]¿&I&Òýþ·ÝKƒÙ?˜èŸÈ»† 4íHÑlê¿s|9öFµ½§29uIy;POZbþSm@`©gs³*œ§Á~:ðù@äsñ2;!MsÛ[ŸßìôjŒ£ÁvìFé{°mtc['öcµ`jkš áúsÁí¬{|Vµ0krPù1-\+ÒÐUëèµn«’+äöz&b¾Mâ*o‚°„¢ì©Hé!2ïe>a „†™NƒÈMˆüZÌ [óÓº€n2é±Ìã ÞeÏ-‹@ÞͶ²=ãšìÔ@)ôC¬ z šxÙRèRÒdM&?êOE%íÐðÂÐzDcÚ™ÿþwù®ÅĹ¼æ8¤æ4š2&Ò5­·kù,ñã<Ûö8{´áLQr-‡g=`DZÅ÷èâ˜ðlýÀ66:x%q—Ýü,“µ[ÑÊ»‚ÁûâÄ7öf°ùHú}®oryÈÿá:@¼é€Ç|c6iK¦Ü’ O½ÃØtÙÀ[ã]ï1‘b2ì·¶ç°…zPì™TLi±œï5BžœÀr1¦2Í ö—î]B9?LXòWâ&лquêz)_Ý'X˜Wf»&º2“ 5Åïªs+ìaöc¹k@b©“oïV´½YäTøÛ\x™àˆP¢×&N¨EzRðp\w¥¿º ­M ø9²nð¾s³—\ÿdNé ¬“à^€Â¯'Þ ¥"”k<“­•Þ¢Nœvl ŠŠ IëËŒWè¡,|ˆ•²}y}ÚUÜÁF²ýÿËèd4ÅNîR¬@¾žÿ&2 ­jþaÛµÛuØYÕH¥g2Q<‘rÂv«,÷¶C&|ß)䳨¶+xÈf•Ò¾óo'þV¼õ—Á¹SÑ>ÛÒ-è8(‡²ì @ ”^ ÏN°}ói7¢Ê·(ÒýÜmkêOó”e\8ÜvÇ~ò[ñkmÓü…¶tu®½ìbwÅçÛcÉÑÞÁƒí¬üÇÉãRò´±ƒäo>÷ƒÐ@c;êÝníH«-aîÐ ¹¸GCòµèUö#rîø#·ÈPÜöÇ,9šm­]È÷%¬˜k ¶à[¤;€Ñr>~ESzO9Fsi‰èéÄÛÛ~Œ=i‡ç{u}:ÚõãBüŠ…­]Îm6?²Ÿ-=†Ãû³ñê_¿åië©^ »½ Ò.c™ˆ„<êàüÛã¿iñ>ö~á+ìl ’.ñ\în'Ðþ›ð—FÞ§­ð3;…»¯E–i W¡Õ¼-Ic/Ê#þ,´5íž: 6Ç­:9÷§ø¥Í åe¹Ô±O2a!mäJxœ¥·s8ïîáÚ>³ËßæE{r6AÀ†EçÛri Æ>¤üI_Ã3õžxÔ{ìo½éÝó?·ä@+Lüdß„–ÿh—!VÓÍfácjpò³-õ Ï£M}& !o²¨9_uš7>#-G€M!xVvqEÅølZ.núN-aר™o‘‰ŒÒKy&_®}«oĵ„˧ö˜›ÏlëÙ@û»Y#ìÄNŠ-"?[xÊà¡øÖ”mág¾övH³5÷ZòµaV8S¦CÊÇŠ7°Ýó?±›Ê^ùÙ¶´™híÄ1ò”û0y=‚}ËÐ?Ž¡÷êñ‹{™_g%Çð-Oó¼'ÒÛŽË9ÈÞ/»Õ¾Â UuRÝzí3ÝOs§›#7¢ÛwRFÒÄ !]FñQ¸½F±ŒæYüýS*jcî(E×7æwkÚ­¨Ò½% ­Û!eE¤uß3’­ÏSÖ[’_ê1÷¾ï"ò}2鉿h3øãBk ŸÏB¢$@î’&B18ì;q0iŸ¼J*?Ç8b ‹=cÝ÷". ®Mˆ×pºEÐv–)£úSgâæÚ¥öZ Öbj6¹9§ë×&Á§ð»Ð&T^·Ú5|Ïë LÁ¨¼éc¢¡Ö.ú(Úºì$ žFøÖšo¼>ç;<·ká/H9`oÝ*ލzU€v®˜rDÿ¼Iéöuhµ}žˆÚ%ôþª=ù|>ïiÛ¢ì‚zý@×ó‚ m#€áeU©®ÝoßrÑfk#•ƒøÅ_Hpáò[”1Ëó–¦k‹Z»;e¾*9^¨ % aŵò‹ve, yã”BJ©@6Ãm[麼p:Û"Júƒ ÕZd¬FìüÚŠE¥±ôe¿ÊôÉ4dÇ×ôÍ1’~êû£Õø»òI>lO¾? ¤øÑ«´#Möº1GUÉŒâ=(glOã’Ð â4'óg%£ C} ¤OÆBïœòž<ÿ’‘\j[ö»Ó5‘3Ôó!XÏpÿ«£®úëð¯×¿%,ÿè«××%Ê¥]R•Ô广Žnè áD"Cc1=š¨Èë=yÿs’î>è+'ôÕ½ÓNÒÍH[w²IƒªÚ(}Ëzmaêëž¡m\H5¾Š&;?õ! X6H ø+÷é ¯¼²5PŸvó´HY·>èÛ~hüOé ïü¿IË—!‡ ø»éï}e‹R[¹ÒI_¤L: ôM©ô•û‘i ¯žWpeƒ¾ í8yÜ{מ”[8­ìz¡eøGA_½o[ 變=ë<Å{é oÛ´>s%~2O" ‹îYu¤œáu“×µ¥Î¼³ï¬iZr-Z,ë×øp€°q¯è«è²™œšÞ•h"°*¤C5½þÿ•ÈÑe,‚%ÉM¥í™çÊÐw–¶Cè[C›|´èÝnb™ú†G’D3·ÝM&²é&ç‘êMÁ šË À¥6 ôb¼gÒ·œ±õVËeĹ¼¶LàŽÂoaíþX4Ñ ö¯#L–—ú:çb4k¾°‹›Ûã±÷±ÛÙÆbeÑÎâ699*ø=ùl›2é;8w¬… Çf9WÛ„†óm(³#à’€ý* LM¬¡¾e§Ù&‘“Ð]Iq€s÷þµ±ëãsûEÒí{@ßÑÖ‡>k¿­àW%s“ùÿ/¢>/à{ûp_ŒM[½©ǯ9ÍÞsàácÑØŠiË8«&PB|ÇÄë†[Í¢"+Î}Ói"¯Ÿ„ÜÑAKÒ‚ê(|‰?ŸÇ{ü@à\Ìt.˜è ørRâ<ËRáuÀæµÚ2TꃾòlºÈ~•Iˆ4‚3(oZ[ånâ•æe‹•W _P$àC‹N"ihÚ¸©ZrÿI ô•wNèV0ˆl¿ø¯IÐÇàa.w QjÔô%i]mÛ y“@ Ô?È5/ŠIŠâ›àÕKó« ª¼Ü¾<8MÂã1E°géÑ6 ۼͱ!º Rb Ú—Ñ<€1µ:´©ûTN´QÎö*r¨ìZÜ+¬eñæhíÊ;÷bë÷.6MÉhà{ë…Ù6°¡±oÐ J°5‘íÚÜ{·”œo×PŽ72QN”!ÝŠ[‘Ü™?“ð_n_ŽqZKÏ6,¶F‘ñ¸M²æ˜Œ8-¶ ÐwKäO™}¹Ò¶E3£¸)«£°MÚÐØKvü¥­Å¥gÃÚ#í¨@Œé–=ùœë]Ãs;ÛŠÂwí8ÚB›sñëiûa&¢l៱•×Ù%«v·1ðéÝ9Ôt5‹‹Cm0Ÿq0Î! øŽ@¼­=–{©uäà*ŸŠ0Ñ3Ÿw@ ôõÚ¦¤•æ]‡Ûz€ë%аO`r&l\I;¸mø/6|±§'ÓP"@9×>²“µÏñÞá%ü_øïÿ†‡\;€ô¡Uímañ Œ¸*Шg\Õu‡Ç}G½v·-KO¢M£.…îà>dGÙö”¼à@_q¶ˆ|)™¦ÈŸì\nÿˆ+\6Äž->š:=­Þì=|—G_!ÝD{üv%î9È€{¨Û ø&G¶‰™.C+\šÎ£‚m«¶`ëú/nÑSgÈìÐT±e¡ÊÌÜA\9§`¾¨¡Û=â@ Wô(·ýLûÙqðìK%ãÇ]²×T;£ ¤ÅÃïgÄù·"~éñ iSN'³é¼Ìý¥îøödûraXà×µ×!ûúN%ïm1™äøW<¹owfhxˆß¨ÜçS^ûØ·€ÌSB¥v>àõv¼;cöï4û=paáÿèäݶUÞ‰Ö!ës»UèRûŒ „iÁj7V;ò·uùUŒ¡~§U­#àúÈàú^8åêRÖ“ÿ\9\æ4ŸÕª÷úÃ?úü[•mIfzR´Ï SO®ŸÎ}Ãùk}wðøTߟ1ív%åäóçLOŽÝâ¬î34Ši—ð¬[œNK.ýV Év’ú*È Æ  ÄV]j Zaîkc’³C7ãÁlL¹S6ØÐ÷Î`w¾ëÜ[P&»¡i½¡ùgˤ'Ek`>Fö#¯Tç˜gZi>[ÆBp †€]àåŽùð²$ cnÍád*Ë‘ä[þ­…´3ó|Æ™*#Ï4E›ö6®fû‚˜dS»@ÄtäŸ%}Ÿ×гRúWã7«@’Òšýo¤úš¶ø¿ÿ– â²òùßDõ-åY]dô¿$óÿt^³Ä×òÛkR²ñ›M5É­l›·GŠŠ@IDATÑh°ç“o?Ôþ»~%®×ĵÊ[gÀ–KŒ×—díézr”¦o]ëÞ·™\[ö¢ ~Â×Ô:è¨-â±»l¯ÊæÜšêè¯ø„Æ$òW”ÝL´‚uG½)xšw3›—¿›ÞeP曕ÐpYm™a£³:š÷ᲈ!SŠi0%غµRÁÝÍÕÐÖ…oV#3ÅêOj¿²m}<ÒôNfà'÷¯¥|’+ýk“Tj²ûé´~ê#}«$î;Ôßnš€×›$a˜PÔF.§à[}PëG‘½³)LnëiE&2FJµQ'&’ñ„óÒµQ¥ù#ó Yà«L5ü€–ÏÝ)ndÂì'T%ði7´”ÔgØþy;š+³ÑxÕ¤dëðMöiü{ëÂvÊ·ÂÛZÚ‚í°¥:njbRz"æZnu§³ŸMyŽI4#•êy—w™üI§Ç˜„6ìu‰×y1æÄ¬tïê÷¼+ÓNrk;]ÿW)ÓoB×Ò ]Eo²wÓi2ãã¶žS&}áícƒ ì8ò Ü’FšxnÊ×|Æiõ ì²Äj{ t4&î§Q3ÒD3¯øš†„nØÖÚÎpÏÑÏmzžÎ&¿KT€$ gÓ¹dý”ìÊÄoؼ|ÓóÓ.ö8÷ée§|­bÂäÙ7„ñÈD7Mʨ­ÜûxÏÒ¢m±û@oÅòï³cJšrØüØŸŠ Ýy ™7M64û~!`ÍQ|ÛŽ”é„݆ï{Št•ôMö6@Y¿øG6×–_‹ÆâîxÐ~tˆå|@ «% ž²€À;X\‹ìøÍ¶&½½HïªÜáÓÜQT Pùˆ!Â[ÚUùì04OνÜî«<ÌòÊ ¾Lod·¢we’~iô¸>ö `Ú€–=‹Ù»ðå:ä«òvÃ6þ·âåÖ¸tÛ¶Á—v*öu¨œa;×"}xï4ø¢¸3ù¡_(˜Íw_IÇÚÔðùØ&~ Î\éÉ‚ðm¸Dž¯@TíÄ·P>f”1¼tSì=» :˜tšSú¹-œC’½­sâ Û<4ó‹8­bƒëUv·Žð»p÷½=e‡äœE\ˆƒªœf®Ä‘(!ÛÊ›†vm;LF,p ¼çµ¦ÿwgñï/½Üž*ø¥Å¿Št¯æùêÄ60pòש۷)‡g2“ŽÃ76·™¤w4i,N¦q{Ù…¶4´£ G»0ÉÁ{b¿Û½•ïÙŒˆø!ßžÆTÁ€ü±¶¨â%Êî Ê}üÓ@‚CõB­ì½’f„ƒ_óàs™zÐ.œè5¸ÑsÈ·–a kæøÕ,4¶¶kw9ã?Åî$ü±ÔÝå¼ç^çäþÉtv°7­¼Öždáixë:´!­|—T˜õXð{/~ƒí—èÂAœoÚ>c®=ße©ÏÈï>¤ `#ÀC ¢µ^€ìwxIë;Èhy.Û!™v;Þ}&üBAVÈ °f•}’þùý˜ ؉J=$ä{Åa¸%{Àælçr˜PÞ;áá<“Žv1¦ñéP¸î2€aôW=~E[ôöð®6±ât·ø¥…÷Ñ=Çv¶;¾@ÚóEheO_97ÿ9A"ó½aø=÷4ò·3÷'S?ŠûK2XCêà.ÊeCò|õ!½CéOeÉKm|dö@ž‡†®! ø[ rÊ¢ÇìD}„œ6•uÁ;”Ï\'lVbûó0L`S=ºœó;>' uT]Œæù‘CÞÜB(ïWž‹È8m2õ=M¸ÿ‰Ë§-¸YÌ·” ó=ýøÝœk‰]Œ¬ì†þôåÁsÙª i?F^zãGÕrpAÛþ”¼ýD9¿A»—b¹Ó®…b奘KòNyXW3ufwÌìüñðÓ Û)ÜÃvªŒ’Ê5–+²÷ˆØTÛ «ùËàÎÎ䥧íÇ ÚmÈц,L5ÁüÇJ…ýb¾Å²UÖYó¤„IrgÉr4S78î¯~9çV{'ú8ü5ßz³ 6QüJ×"2ƦÄ`·÷gª&Ù³•7pÎr­áåú®š¤Üp`r7ÞU7µ…OhËjgeÛ¹ þÀWøÈÞÂÎÉt§?[J_(“hT—ê@2¨[-i4„oŽoîÉò§l‚qËáwÛÙÌR0Æ'ùDÛ€6Å’Fvu…/–p÷ Wí$³„üÃô³ˆÚé@fÊvcbæM¥Üs¹óx>åTãÍòƒ¤ÂÖi†¿³0'sšÿi>¹´²²ÈávmôL«¬Nóùs·2Q¹%ß"^R ¨F5(~ ÿ¯¦üïÿ÷÷ßø§J@lcD×RÄP%÷õ4~þþ&ÁOþoý•p’&¯Š«‰²_âvǾ2ŒäPJ¿3HÓ›A—ÊÑ?dª¦ô²ÝΠ´ïª—XÏŽ¹vÏ:Åü!7À¨=^^Éᯠ¤çá dkk]îW:×úýF‡xù:Ùdíû©F³>j8Ä˽»åªü¼‘äQnÿW’UK·ÝúO}C;§y£÷u¥,§e 9jÎÅ' Pv¬Y6¡4DÙ ‘wð„¡"3Ø?¶~‘¥µ“ÜR\[ñˆaš ø¤IÚû ”—úÉ_}_¾¯¾¤Í»u.)‘Ô¢Tr:1¹íNv¬ÿJú F êDàiHìËh£WÐFOI„ ¹4Ý!ÝÿoÅÄæl¼úÿ诹Ïwõ7’-¯2ucqAõ¦g†ÛUÃÿ@wF pYÄÕÒö@ãÛøžj–ÙúcÎ$v0qŽçñ¨ætÐÑÁI: ïiÀZ_kÞÖiëhË ”Û)1ßömX ‹¤¿†Ö\’>JÔO¬kÁ7¶WéÖî°¼3‘vÁ7ûÈ4ª<žíˆ,RPï›{“þædóLƒ|o]ŠOe’çÙíÕ©ç…€Ú;Ù'€¹-bWÛaÁãì& 9}ðûÑÁ?ÃLbgž§×xw1åÝyK©;1A˜õÝyþ8ËÍlÇ ¤C¿dÂ[pkÀ ¾^»§Ï¹‚–ÈŸjß—ŸÄŠ?eö+î´zÊ¿âDÂL¥Süs¨•EÇàJï>Çvd;ô‡´Ib`¶ñßÙìð öXl’ çð©-bß¡éGs±ÒŽlPf#+gPn³m÷è |íÏNãjGIN ”ÛÎñžÜ°³ýYòí›Ùq¹Ùså{zmL2‹¯|06ÎîL¬gï^¥LgYîÅv{Ùnœ`ÏáVÑKíœàÅØ¯»™/pV¶rJ‡O²Dâ&§É½”¶ ÍÍSæ&£Mû[ „’ÔV/èQûü®|Á*¢cm~ÎH¼Á£¹=˶äƒ)÷M˲ñÁ ‰/±wÅ£¼Ã¢“ø–×’)ð^ÙOEÆ#úð^dSŒúC{×3ÑÛà‹ÑÀæªvÛÌ ìó*ïšHÙo ßô±ó+1Yñ«ÍËyÊvƒ×whðµ©Åæ•^mÃê®(ÙÐöaaùI&Ä·¡ùmoÛ€¥ÏÛ㲹ƻ¯çÝIP€J‹ÑoSG?ʶvþcktßøç¶!ÚW×Ñ.•ã‡öpŸ’­™l—Z9r {ÁhÃôOmJ1:ËžˆìììPÿÈäzÚÂR´tw*¿Ñ®¶‡mì˺žPß$*ÿ t·' mzZζ/@k¶4ÇÃh7;¯Œ-pÇóÎÝø}°{ÇVr Ðmóž×D炬ÞÂáq‹‡Ùò¼KÓ‚ HÑÔ=I:X.´ð‡H&5“2Ñp}Þ)vAÛÑ~‰ðŒYÃ’n¶Ê?¸MáKöqu·@í‡á-êŸR‡A~±£ìÀ³ÀVÖíè}Ø}WáB¶Ù¬_ÉuÈ—BÊúxg…5-ÙÛ~ÏŠƒ-ˆpšûñÉ›¿u]@#‹ –ÿ¾5 Ù²Ê;±¡»£u¯èiçažfm¥±°àLÌ´åý:þÐà¶96Uo•&/ßhåGMíeyUôTÛPOÏ$ÚI ~JùÔâ‰ø5Æ·8B¶©-IÞæ]HÙìËâçìêâå§+;Ò,ž‰Ÿ½¶Gy§¿Ÿ'‘σì?#°q2íi¡êCøòœKlVιö|ñØGE>ÆÇ¬ ¿R™6ëv.üèâèŸÌÀˆrbQøþb¾òÌ{—¼¶"Ÿ„OY÷&y)ä]2Ç‚ t²ÍXúÎYÐõöh\EY @N¼è¼ç,uG ‡Íi€e3^D³Û£†Ö ìF,ÝAÞ´sçÕÈ`»›ÇGÓ@n žEYnmáh;,ÐÙG>²0Ey³‘Gòˆo”÷·›‹ìiäF?Ê ›Š¤›0¬=‰´Y“èTû)ö0nðt9 k›À÷ýÑܼ‰oZ d˜jÐÂch?Ê…ß4’IŸÍƒ‡°Â3‰—“>”£Fô¤Ä}ú¨íml´æ^‰÷¼pÁþÔ̓Ü'g:Îf´äå·ž¿þ³CDÚÜ=À]ÖzƒîŒ í#}*°„s"èKííÌÙFÿp3õ°¹6 öúû[±§~ û3~›ÿtQ>}ÂLо´Ëy|붤-¥—Ì€‘¥ÕCâéUðöp?fÚïúø´¶±!´)²i駅ʼ¥½1+éâ¨íÉܘ"I ®bz…Ûòn¶OËO´±á¡”ÿI©0äf4u;—Äñj›Ò–-x„—´'©¯þHrµÄ)Â}Q–2ÙžJ–%¾Ãi¿0%s(c†˜¯…;¢^ À™™²]e2•:ìD]íX¿(ËïzP6ÿ!ëzÂ)œŽdh×Bév,m°M ÂBaúl¦ö$%E~ÈòÞš|Ï mfÚT§^«¨7æ°ZÑË&ÿ E™ ðý–è»[ýG…¶³|ìvôm÷^@¹3³9 ?œë鞯ƒ6ºòhúí m@ ¥m]I»Ê969h½¾8?E.²f€"óe)•­Ðg£…w]ì“Ù ‹]†ÍÐç1oKÚ­ˆ{y8߯ÅWÛúÅ÷EšºîÅvóŠœaviùpÂAÁÓ<@*òˆ½¹ÙŠ*_¶9¹}Ь}`öl†=å¶Cåi¶.@Ì˹#°»û9|‚i qZôêî$—”L$XÎõÜ'ÛjN/îá+Ÿ·ÓÔŽÂ\ôºh@&0;áúf_ã,´<ôE3ÞN oß7(%èPN™ HVÛ„‡ÝoÞc|ÿ£h77¶.yÏ“DÔf• â—þ^ oÉA–‡ÆúRª±HqDŒ¥´…º’p"p˜Üpr¾?š»@å×¶¸l; Ù8 ¸ù¸L!“TÄ3íÇÉœXújÎ&¡öKÙù^¸è4x±t†áOl¬Zî+¼¦-W /ŒþÇ%;(OÇ¿h*S&§Ñ´*MT;’gS¡›ÆÆ`k¹ ùÞ“º”9Ñ6î¿•@z·z÷,¸YDå³ +ÿž·Å øËÿ‡¦ÎñF슚ó˜}ûØ¢ÑôËsHr§ÍÍCÖ7$oyhH¯ š¥ðáS3ÆÁ-dZFàdôr~‘åzÞ7£úFÑrU²¦h{á><¯¶ï"7ò-º—!™‚Ú€zÛ”2¢m Ìt¡çŒ² ¤»µ+ÿз¹ «›i`šÛõ´g\³°|Û‡.ôå€ÇÉ®6òj ¾à‘àö ²0E²_[¦ú§=ŠdR‚üÿ.3Èô¶Ø7®¢¶ [íc±›ÉÃ$Þï&I‹¥}EÚ™u²!c^Ã6}«<7ºêg,ñ˾²“eI÷ÂyðγðÚ,â”W_{«ðs]ðå/ø¤…ôÙY‚8é ¯ÂÅ©S§U¾9½EÀ]8ð7Ö.Lä UØîô!=)+$)zÑQ48Kl[ðDZkŒÛ´ŸQŒí0–£«ˆCq{3fìK_6xïÑ.7qvŸùVQÎä·;|ܨôU g)T¸6e‘Q^û”s´0åû ñe’+U¡á¤q¾õcnx6òG†ûÎ);”…è¥ðÄÔT¼?z3šºÕÎ+GÚåRù*ߨ±zríh‡C«z4ê>ç~Òí[3®ìú­ž7E>•V5¶´ÐA6ê=ËÙÉ''{r§[›òݨ¡[‰×³á£Yí„Êȱö]7q‡ä´BVõ ö°>È¥DòÀ›ÛXlP]Þ†ÛÌPsR-³‰h,j”¢6©Iw×§ì³ÕíØ‰²òm\÷],Î^ª¶NGï%,U⟗O[9-n õ¸TzÉÉ.w;ÒΊÍÝÝíiêö€‹8¦#Å^'ß;…—ÉWm”>gÑbËÈÅ–8Ѧ7xÈòtHXééðèJû®ávÀê~öAà»ÊÎEó×òà¬-ß¼¾»Å>‹´gæ5ÂFòG±§­@¦› æžA<•/<¶c¯‚Pîãü=gNÖ/|Š=ŒI¦…oئ%'spÛ.´1€–|&¯"4€¬dsøõ"îKÙÂz7šoeö„À¼ØTx¸Ê€Ônh¥7¤Ü^ ƒVã8ú¾3]2Ýxw´Ž¯Š-²OÅ_‚_«^¢hšr¨Ûò’‹­ˆï(Ôžn‚ýÖæ;¼þjEiÚS§Û„‚+l£Òó­/ õÈœÓlïP7WL³[à1zOú7º·³Ó`XÓæ$×Ö|šßѸ‰®"¼ %`6·°39ùNùÕHLìwÉ;ÏÞ"~¸ìKäí ˆ˜&gc´ oo?T\kMÙbß„|ýX>ÀP? Ø·?š¦wPwy9=¬LŸ(@ÓÝ8æ¿”ï4Ìg8H6q£cñ!mÉ ‚ù/a*f ßDû6ø½p=_ÚÉL´€áS€(ɬ‹ÂWÛ´Ø髜G©»÷O#íààèN®gçK¶FzØeØ‚®äàÈ"½Yœ  ßEÝ·A6“?$´`£Ý h :»ÝŽ7sm)àÜž%GÚ—Éún‹9‡¶³ì©Ò^äw?òò<ñ‘ä`…å¾Ašgãö)²EùM̰ӱ;.ñ Zàì‰Õð‰“M7á¿ <üœ -Ùð+lpðh;66ÚŽ nc‡¯Á?bŒ}›©Å&F2°)qàÅòQäa+Â^b9rõü|RÿTì?ð«>â;·`t C¹…¸È7L°HéCã§ÉáË‘O9v fPäÊö(’Óˆ²÷äÛí|HÊa\Õ"WîG}^À“ú±Æ¼g8ù¢R.½*¢yH% ªLòõ{òÞ[ºvz¤G¨MBp ¾œ£qï΋½aeh Ü2o‚=ïÃîÛò)dzؽҔŪ8Hrµ³›«4ŒE)«¼š›îQ ÖÏÓWµåàÇÎå·Úؑޫ„úáš;5cLu¯S-rÁÝ¿üˆú»%-=€§…Î}gæš’÷ɉ¯´É|:Ÿµü'pZ$ àà±Ü02‹O!ïÔ+‹sÏw*þÇs¯2ð)ÌZ¾GnÛf[úsW®Éxé?:ØìÊðvšø#¦øU¤ö¶€òÛ•¾Uu=ß"ÅS_q>ÙoL›ûšbúxOGèVòIyͤ{i+‘/ÓR8å‘U'U1hsîÏT¹gÞé»&¯ è-«ÒÍ Sý©'œ‘uµÐ†¸+„4ª-Ôòºž_ÊL‡:ºq™ó¬ñŸøû×}jrì@}Òž*ϯ1–ËGh0üÑŽ0ȤI¼B:Ù»j [ÝQmçd pÍTU2.l`oÎŽ´¥Ñ3Ýã‰ð ËOIs^jR ÐXæuÆo]PÉn{ÝhÛ²èÞšvú˜ éÅKýììݦÙLá2…†Ó˜Sõ`.¸.q7åêM¿,Éô§©ôO§Pÿ´2÷wÑ¢Tcû»ÞP•.âü¿’j}×–QÖôýW[¼:;¨£©‰V'}mY=6¦)§‡_ÿO´“µ}õ~¾¢÷¿þÿÏr‚§Ïê;3ä^nÕ³Ê-Sxzîã5Í"Ùþ³¤mö'y'øÖý†eYÓ7 ÜÓpµ6›Òà« ¡Á\G¾IDIƒ¢&*MnƒóýZ¬Ï="É€V®á¦Mòk :–Iñɬö§Ó–|ë5€“›¯Å7§Çÿ³÷ßQ/þD Jö SÚ>è«wm˜ìêU¯ýø”úÊíy$ÕAß"|ÚÈ»Š4¨'Õúj«²¦e”žÀ TÃw¾" N¿¡^ެôU­À×ú*Œl÷zS=U‘´A}-̤ãË,iðkàÍ2Eáæ8†àhqe&ÒPê)Y o#\µÅ*EÁ¸]ßî¢U•|ÊwÍ7šð¤“ÓÜÉA 9š 2.=$ðGmD`UZßðk=@ß°=¹‚‰ioñ7“Æû©4cÜUMu%µ—¤üj½©ôE"…†%£xHK(Eiß@p”gÚÍ*î+¼gwpW‹*?ÀæýXÏ0hÎ&´_Ý$ |›óÑdÏñ@d¨{~…» @Žžc»Æ~r<úFì)›»º“³¥þ.‹@šFt¤×’)¢=c_ÛáÑ›,7þ¦Í‚/,6¢3VÖ­Ëk¬4Ä!\áM}Uûím‹èd~%©[Œá·Ê_TÍñ·»¾É„ON»ôgÿ „Ë8øŠ¼û<ä–‰e'/þ!yK/g?åï&®þóOkäC²~»‘WÊ+xsñ9~ÀŒßbä&/ž Åö2^SÓ½œ¼Y‡øÛÚaƒç³þ}{#6Ùºž¦]|‹i‚K9Aý|äìÝ”ÿ;ñW° ûŽKíkÌB ¦íàÒV\çžOÙŸÌGËrSkÎIóóV·ESxK›¹ Þ:ˆ¹ukÎ|Oi?êsní±I:ÝßÇœ|Œ6Œ 2|þšVÑ irIv¢¹aYõ£ÄÇöqY»¦òbÜ©CìÚbsYÖa:Ñ:¡©½5‡¶åp©Å§£QüŒÅÑæ¸‘yÊξÀ»º“'¥‘¤Àö¸]Ï5ÎÚÛØ–>ñÙöCåq¶­ØfÚR½“ké®cS‚Ûš@“½A@Ý•Iåc¬…ð’jÁË6àc%‹6V7°¶:Í>z<ð¨ÿF¾•>8x:õ©Éú(4Ôsí =zJyÞŸ+ug»xçÈH[Tø¢€6¸4¾ßJÀsXv‰÷À刼Þ6¸€trûÚY’‡šŠÙÏîOüb¢U½§O-°ê@[¨9»·Vv£m“ó‚-‹]c°{åçÐ>6»à%Û;|ñ¾Iß&yêKaÒ)ö9fhlÃNع€º_ãw3¿G'â¡Ü?ÏõW:è»~-Éé µ÷"ž,äL§ïo*-¯$Æâd/´·-» -iêú)ú¥åW¾dGGζgëX#¤B¤ò4¹¾ôb‘†À“V²´†+ß|…߯4»CqK94‹*WheÅ)¶ Ï:úÆÖ/= öB{¶à>\š:×’È$¬*¶÷ËzÁâFÒ²™{ßvsü9)ˆ*ï ÞÝφq[çÐQ6ÊMsßB<¿uæ½ôaák쩯°(¸•…KºXŽl[Kê`ëý‰ƒ²örï´Ø—öfÎvKå8â½ß7ñ×sð¼ºÐ¶¨L­í¾ƒ±ùFò½SÜÍN.9ܦ£ î`øØ~#îÄ}9°‡Ý'à3ÏS6]X8jSq~C8¨r¾õÊ'ýðäikÞý>|¿)ßpßL[«8Õûv½(Iï#0„EØ2âìˆëwÎç9§©GyÉr¥ò¢Ü&ÜUSì_½Å¹¹±lq¸§«?çøï;„»ªžPÀ·÷DGù^™s ždídžŒ¿íb5/ß ;¿ß;P×í9„ŽG¦ ̳§‚'»gí8yðãë´¡!T601Åóc¦u1€ñúžKÈçLteðrá6Éâµ$õ¨<ݺªK’@T2̃ÌF&îU¢:suDú³ã‚¶éÊJ½›ÓÏGÁ(l¿çRÏÈ i‰!øÚœöÙ2_ü®D-¢lŸÄ/—«fÊÃùIäÏf‰W¨ÿ—½@ê3C]¹Î%þñ´‘;mïÄ"4å ðÏÇý`~ág‘qCƒ[$³N2µô& ™ì7S©”É»„Ù–ë@Fÿ4fÖ"N6è«þÃß½©]8§E¯!¾¾3BÛ™ÅïfŠn0<‚ Á/8ªHþ®k»4XÂBÂFÉçÌŸ*ŽQüêô2f?¾r߸ ž(ÂתJ¹üÄí¥Òxu^¨‰µDRs€:‚WyEhSÁëž³îvCBÞOyxô1åu½w‹_è+S!ãSq³®ñqé#c’3qÀGÔµGyÞ?6”jG¦Sd<õ¿gÒ%ÊòÙï>ÐÞy1é^¿ñÅZðK’vê xÍÿÄ™ôKæcbžÝÆdBM|¯ç°+°i2”ZÉK|°Í–´OµàŒ#5Ÿ617¸ÜÞŒö¶"î;¸IɈ©ŸB‡w§¾¸Ò(Æý5h/ 7ãÝSè+úŠ”ã9pm4~]Jÿþû·þ-ÿʦÌo57s!œ:^‹_}>¦)ð7x¨Oèêa²ºm EêC¾Æ¯gy2B]r«¶4CxHHÖ—:òÍ«(3ˆþßDéZÓ˜nfüZŸ€õ £oÔ¶t7i¯g?Ølùú„ Ä+iÀ¡ïWÛïW N[ià½:’Nú‰šVu“ñ4PÕö´"£áÑ‘AÝmµ€ókxU=½™0è¨jeÕ£éÐ …¨{1ýö%íP€ )ä]9çýò81²Ínu†vö!Kaõ7fº0D¯£Ä2æ?­ËÔã¤$€¨)†Óq¯aæ²,·ŒG &³íd&;"´‚8ÜW™ˆiШÉBÍÄ?x BotÍÞr 0 K¼]»>ëá÷[pé߂ЛfW…Nj—U9TÝÉœÆÚÈ7Éàöð€oÆ¥$°0 ÞU¥Ü’Û…ÉÇöüüCô:2ñdÚÍbÛ‚72a ž@¹ŒAÒs?>Ьj;UÙr<(€:zvµ\Ž¤Üš¶Ú£-ñ‘ð»Âß3è]‚åy7ÚX´6ŠÝÃò{&)$ ¢#@b¬±ÄLï9ã3&Þ€ há=ϤéÀ‚¥6›Àíгø;ÕËYu+æä´˜Ýó9ÔRû8Ü×ZF™Ìû“V§-C™H+-Eðe.ùs‹ )G»<<ÌâLò®KÛ%På›v€¸%ZhŸkB–¢Ì1„°5z5ZUÝmײNönX`˜&Mmb`©[ø£5\]ĤgéåÛT€´[±aºcé.Hî̵LdH'‡²T»ü€¤}OÚÛ<´À?¿jà ߴÊŸ!íóÑšºÞ‘rÒdO­à+@žÆðt{ʧÀAÚeé‡aJÚŠ{7˜kÅ%ýl€å¾JyP®¾Vxx,é;ÛìÀmì|Ú±lµ¢±i¤–‡BWZ{´½çbã6ý®j¢¬×¤¨ u¼tœ%‚3³³ïB¬Kåe6£p î‡" øâÄ&8À>Ö!G¦­½€èF‹MÙF<꛳¯KÙ Õ”3JúÉQ@9 ~œv˜¯²§õ–ö·œÄ×6³Á‹ÖyU{Z¼ìm{ a÷Ô:¥vÊ꾘×`¡ 2]aØz{GìûšozÛ¦—&–ØN²¯™Ð©ð»xUS{;÷yÊ ¿½ày;»ä@Û °¥u ínÄî²Ó#hžFñ­+mì1Ï-9É06³«ýÐ`´ÓÕ« wÒÚÕ$Yýÿ倲µÑâPЮ%ìÝ„9 -ÜC\ij Ü㸌ÿU0h­Ò5y³@üå€<ùfäÏ¡k_çl €@5üÞ0þL["MÓÀ%i‚0nHØI´ö*eU¦ØìõÔ{ðDÊünž“T\¾„|É¡.‘¹†YŠ£Ù*žÒê€}Š~ðq ¤0J­c³òߥ¾È;Ö,û-´€Ö÷Ö.t|ͤ¡½Hso÷‚+c3m(|r3v¤}\P°˜z.±—8louî5¼ïD¿| |ËÚa³v>mv ‹…ÍWÌ%ïH±’ÉÚ~,&О’´3öv§ç߀_O¼ì;#s†ØÌÄ;1¾È ö´8;nÀÍBBi·†ì7ËN®L²@›p½¹N…µV½¯gíƒh‡Ç§ÁžP|ûà›äÝÀN´[dS)íÚŠ¸ñ½„AާìSέÇؗ±O­Yþ%¶¤øpÞó<}òöØI]Âa‹a¾© q#¸«~Ö'þ¶3²uHâì¼ú#Í"lD/ÔÙ­2’¡¢Òˆ4:Y ~sÅyÿsì5Í&‚}p/®ü•ÃòÞt †Dö€ÄÐÖä½/íï~pÎ)ÁêJUw‹l§Ðy¶2„-XÝÅrÑn¤/ðÂ"ÚÇ,d}ŸL#ù“ó8íîJ;§à+.Å\Mþ¬Ët”@êècä¾bƒÓÒÔQw9ž´.æ;;YkÌ:à]GÐÖSùÕ+ýC÷Øuá̄ğàû©ß8õ›M²Í+Û¶‰Y6 ÷)»“«ÒGVN¸œt¶!Ì;¶# [âÛô'SÍÈçùÉÉ£êŽÅ8íàzଠckbVQ éä’Oz!q'ñÛï9¨* w:¤ucùGrËðÑC[®Ïœ«¶áË,Hú;j/"ÇÉ—#™ÃIH6§Óæ<¬âb ’¢-à ê : 93Ñ-ÜYq;|%“i¥Â®é†v ”÷&àwÀÖ:ô´÷_sý¾¦ÈIÚ‡ú‹5ÐÆøÃíi5¨V¼´ÎX£…ü™”y¼¡Loa‘ìVËÌE5r»Ýài¾^¤EùÈt¯4‹pYÄØ?ŒmìB4¼W*H½iPð k +g‰ùA€Â:øù%dëCn&"×*Ò|@ãKnÑ/Ù˜þ–2–}lw ^ðxy&}/xûGxí âÛ1;5‚ñ–oǸ+cÊm9,p"›ÑYþ,‹UÃ.é?3ß+3=w’7™ìÙ—x (÷EnÚ ª¢Hë.=¯U>™wê¸DJÐeÖH >}L.ëEtJ삾q¥ç½Ù}õT™¼<ŸÌÿé«•7d|š\I}?-­ÌÆA ’ׂÖô­M{z»¬ÚdØÍ–S&\Y¤:Tç¸&Ò7þÐWé^Kg­°~G¾¦wÉ¿> ¯ÂÕúj`àÓö¯1P½€ÁÊ{šh“Fß0« ¾„vRm¤!sM$³ ÕI@¿•U÷•Ë”¥ú&ØN—N—3°òI èc X4½Nþ5(Ñɵ~;ôÃ\‹Á˜¶4ýÒpOZ¿>©>‹le“¶ÊIËÛ#À£œ§³‚xå¦óÏÕÞ¤q­ƒd{ø&µk†3HÎØ¾˜•¬ëú ÄÔ„8Ô¶uÚ¶&YªïÛ’|© ‰ù©[ÝhåS]òÍ“þ+@_@w4qɤÂG0ñ{ œ×†:dÎæÕ-’þÕëO{Å·p ¼ü°ÄÐŽR 35×®“ƒòÞs&]ŽAãLZ³>M’–GônÈë(4€;`×ç°Ý?Ѭ:苦­ø+¦ œÄ‚€*QJ¶"<ÄZ•]ÂâÉ”S±’ÿsºc˜5}ÊÁgëx%¢­¥Õ@ßöɬ0Õ‰/r÷ ,.nnboqÞI†iYÅ—Ú[yî_&ýòùõìcψkb¶:éξ з}˜8©°”&¿·<]¸œÀަÝc±syW6è+Þ×A?)9¿Û¾g³Ès_vLb¿Dkùgõ¥láwv¹Ù–® ð9¯m @ÖŠŠlXL»Ñ.—™L"»úŠŠáï&‰žLÞ–ÙH€,ôÎl GCäáh'Þ@øI"VúN¦Þ&@œÃ'0鄨Àa½$ØýaäÊî%¾ñ>¾}eȤX6S„ 〳†Å‡aûóϵü(&ä¯{÷‘I”uÂ|Ä„}´í’ÿ¼õÈ9 ‹:•Ù ¶å'§¦^øÄw€Ò›Y¨\ùiŠ[kψÛQÞ§ü0ò).b2{¹òÅo·hΑvb°µ5ð_¢ž ¢ÏÛ ’w‰¢·SöÔSä*ÒÝ>e}óØ4ë\z!n{áBýÉ'y•Ü-¦ !¸nÞehKçÛýÒzD#¸íÌñNhK”?{›Å¶Áïíl.‡ Î 2} ìf7åìgsòö°]ó§Ñ­d<°Ê*ÊGÂRðhås64B ~ Ím]y ʽ‰qŦ˜?8Ë6b+j&Ìs1@ˆøbÛqÕ&6·˜r˹ØZæœK9À3¢ØÕv\d»Ý6Ýñ±Wl›Èév\Îxgrk–@•Ò.è–¡]l5ò&~Ò=7@’­—?œt¯'Ìù\çXAy_ûŒ Ñ2kˆØ³ÅÇÚ“˜6gò/07oe5YªÙon,°šºÝÀsW_9Æ:FŸ%”3òü'´9“YGˉ³’Ky…×Ñ.$_ÉÕú*L ˆM¹*¬cåXëžµÂËn \ ÕûcãdÝtÊÿX-¼E¢,,çanà?HvÓ/C³±Ü=¥ýK1jB¦ˆç>ó¬ýXZí;wí:Ü®x À¿f\^¾¼ÅÚýT߄ϣžŽÄ-Sl}øÆ³È'õ¥Cá¦TZ~ÐÈݼ^O§àžZ¥¹üBñÞÂówÎÍ)DCÿP (”6&®+ΕÔvéxWj÷€%Ÿùј44¨ê™;í¶û3 ¯ƒÃ< 4à·Äâ7·êž,¿ ´ÃP;„’5ÃÙ·ìØ+°EÌ:Ä_õ%íÔÙ}ÑxgooN°“g•‹~b°ÀÖ¬ÓT‘æ8鹤¥¹Åó»˜#HaëÓƒÍq-p‘a1¼ ¯tgôÕŽAÉÏ&„oáóœ¢J{ÒŸU}•Ðç´3 f¹4÷ç{e¦lvÈé{®Î«ê_]šs½{÷® øïÝ¿%VRW—6äÿGÒr5ßÞšÆ)ãßÿ ñÒX-/–iÜS­ƒ¬%ü_áüg5‚kËÃÆ¿_Í».¹U-0êȯÉãÿ›Üá z€Êª¶Æ7â»ÒeÐ2ÀÀÆä'IÃ!º›jt3zŽÿ#”þ^T•$‘ÿÃqF0¸º$9À¨í3/¡c×V?eX+Ðg±µGƒŒlz‹ëIðt¿‰Ä91¹(ݽ¾÷,?E²·Ó¥Ç_—‡á b²µy¯ç/N~£Àµî€f#ÝVìôØÑ}ÉN5 ¿5§}ßWí$_ii›Z?V¾‡KDMÑl7t·nH—>ø»%ÐÜî oįv¨y¡þžÿéí+õM¸£#y\ d íÔ°½D¿% ØÚl+§â®é&tå á´©ª×Þ¶5yXe§1p¿§¶ 6‹=@8M6s¼ lEÌL¾51 †ÿÓ¸óMdž½4ìœ ¾ *còjhœ¶M²NhÀHÎAŸhÊÝ®*ú>r¥mZy¿ÙÎ¹ÓØ¢Ü•‰Ìn|Ó;œÿö&ôÁãH_“»$O³föÃ}­–rñº›vUÌý™š,§[j;ÞŠOôí Ézƒ&v¤©ièÊÜ{mƒòS,¤¢ äÂTlMÒùÉi'…àÃØ|Zs}É%Ò$d -Ьë1ELò]p|Ù¹L n‡¦Þ,ÏMÚ°˜º°œ)”å9¶iøR;ˆ“§ïªQšx žËw¸/ÈWÙyG+wKì"kl¸>ß±€‰W5á»ÚéØß»[v: µð|7À°ªqG8l¦À \Þ¶´‰³„m¿C¢§°¥rµ'W‚ÇÚahž•{Ú¯ñ7°­»  mNü%¶_oÈ8¨‰Œ\Ì·]H*qÒý„Â<˜²x0g|Ó’6 ¯ è4ßn-çrÚw7ø ÐÅbou:ÏGæîÇ‘Vg®¹ÿE&Üϵcfv%ïgoijÙÈ¿3eú#‡6XAü0e{•tà_Ç1åÄŸÀ}%aàïâ­¾_ä&|ù”W ÂNõdŽò¢­Øê{ªkû†k&2¾±¡¸ H[hçê¼à²8³#OXá¯òÇÛ,Ù˜rRÉy4°l€ÝŒ†îáÁ­­ Uøò·¼l4ûo ÉJ€œYÒ@,˜FÙN°Ûóε©«ÚTteÏFã|&ÀçÙÁ6,>×¾!—àÙžÖÖ$þžk0  ¡¡]V:”—ÁSyú…Ð}2¼ŸuÈ;Î>ä›.ZµÜWN™þh9yoØ ±»n5mA‹>…†ÀóÛSˆ{“ÇIîDßÇ™JšAÿ?“wlL©¿ì ¾³àmÊ£ I>Z±ÅΤ”; KÌéÀ:¤Ó[ØxÞéP¹zCž5ÂÜ”w\×z¼§¿;öuܳ W Ôløøžw…¢mœD2”—x’]*û˜íhÿ ³Ô¥Ú‰+§_)A„å}ÕŒC)/´íàé¤}ù8‚gÀ}äûá³ìM+B~_•upt+ Ú„`12o©}áì°_F¼Oˆzœµ?nc²f/ÆÛB‹=dýí)[ÁN†)·rBj|¨µ8“ÞÄ‚­È.ßFù8™”¬Ï õµ-ÿbù±¶U|6ß…\ ïO~¯¤Ìf¯±{·%Zxâe›ˆi­åIi¾µ•w/Ýew{“wÏLóØšûO¹:‘¿^¶{xW4m—³ØwãÛ†ðHž"Wð›,Ëù&sXÜË ÏÔ%tˆMŽxårµ‘ê-:Ö»weù}íï@_àV©ÝKã÷alÔZx‚­“ :%^fWñÝ:°è‰`© §C3=;²¤Éª û¢¡Ö)Õã€G?¤b{71½Ú¤â)¼>[®w¡‰÷hð»º|;>¶ ÔÓB¥¡–XÞ]@IDAT½B8Q0ÉnÌ{!ð­Ð`‚=Ð`©<ÆF EZøtÂîÄ÷¾Çw¿â½HrL>sFPžW3qŸAy¤”ŸŒêæù2Ü'ò _Åçpóâ†zòKþ;S¶-¹zÁs0LƒÙv;}¤€I¾O À#•}mòï@vúšíP¿¥TžGŽBóïeÞy¿-r­‡ö*þ‘eäA's¬r ìN–lÄuy’¸‘~™•­(|ÜÚL¿Qv,nP¨ÿ ì».Œœ‰¦Òp@ÌOJ··C¢Ï˜ìdþÚÛæ¡mlñoí[´š7¯üƪ#ã·ÂË ¶Ÿ2ÒŒN@;ò;$6½”ò \0/ÐWTI9A“rG0U]‡Åˆz!í3Qùxò0ð±—¬mÅÓÖ1v±|êM·Ãþ§òþÅ\-]çÑø¤y냾Jh0-ò<üÆã.Ý„ ô•fñdüûàvaÞåjß „¤“7šþ7\"5Z–³ûˆwkIЇ«ñþ"…¤¯]XÜÓrµUÛ%¯8I~ÂßrhEü<.´å°;eÅ^ÀdtŽ…J/åð¾‡q›aëbÇ÷ÚêTÊq‡— tµ6+|# }8½.¯xÎÎF˱QƒU®ÌËØ"_ØÖÆ`Îä hk‰É‚™,ãí¿ÏÆ5|ÎÚ®>€‚<åœ/!Ë*§)Wкvà|kîŽÅôÇ’Ðr;5ÒŸƒ„›ÛèÊ׋õ1?sñ¡>ŧÁcÔ­´ã‹|W÷+;Ü“©KíŠ{9 OD jSø’¾(x„õÃÿùÀ^œÄWħ­ÅïàzÞ• ·Ê²ñ켈»-Øî°9õ·!â ð*˜Êýw/÷ÿò@…õ“V¿H‹'‚dâȰâõy'å̘ÂJ÷§^~±OhÛ“±©kò:·ˆs%ùC^¶³®ì ØØ%äýë mmmGãÕ¾²ù‹-æG¨K£·Ø4v@<ÌNƒÉÜ ðm7Ŧ Ø{±qÆŒè§íŸñŽmo¯ Òå€À†fê@Ë˨×[ï¥þLººd* Gl”Óøsû%¯Ò)ñæ<³ì2ØÆ—áñäùWB5L†¤ßD‹VZ¯«1+u#š„U¹þÔr*î°@õàˆòs&qŒ±É·pQEÒÝOksÊnnJ¡<éWõ£]Írñ.ê.roç©å€|DûíÊ ¶":·R®2x~IªûFÚžA´¨Õ7;ÄšNÅé´ÔD2ä÷´'Ê4ÈÄùÉ¢&Ég™€:Dýý­¯™þ;~Gc#:}4ŒïY™•¥ê‘Ú¦0|ÐW®+ˆ·¥ZTè+€7ò°—†u|RÜØöilòV¹I’ûl‘ÿÄo”r;•wÍLs«ßí1á!öh¤ofàð-ŒOzÚXç´x¿’tH§u€:e Ñ#JXm76>=ÈîùÖøÇ„AÞäMâwÛºÃÇßâûɧ¯ÑØ·Êêõì'pV`æ¬÷8 Ž© ôOTZü˜ÉßàÉ6*r+8ázqàRÊ¿ ã@íîòÏÊÉeDwmY­XóÑÅÈ )âˆdëW¾g?–l??:ŸÌ§À“wÑþý605Ч›Êq_64ð;ÚëH°¤mcøë’‡CkIc½qŠËŽKÕPFÿ>üß),ÑûçÃþà—|.Aù¿˜JÉûßñÍ’“þ좩ËLDvØ?ò#’.‘7Üñî³ÿ×ç›ý- éq½ƒÒ]2ïó±&ZÓAQ™±þú§ªmäu§- )}K¶Bw^päR,gйzÎÊÞâ­‰z6 Ü[H­9[fÙžðYš†”ØyÛÿh UñTÛÁ‰Z‘ý³Ô•6åk~þ‘´Ö%’ì­‰´rŸZöÑ=u¬dß—>QÉJ<ÛF”¼¥}÷wЃ „¤%žN×1¸. xƒ³twJW…:9PB¾ÒJÈ¤Ö ÜOÎtªãiv@&M¥N†«¾hÉ00ëÀÒ¹ßWU U¼{(e/Òɽš4HkDô ñner§¡ Ïunþ?Â/0Q¯ºÐ¾5Ȭ‹j>Xq>Å%NáÉ È5+¥èEØèüÚiÈG“ö“¨jÐzÆtÊåAšÙ§k¢”N‰ÙUO¡Ë¹¯.7ªxw•üœâ&§L,¥I UÓ¦–£NT§Ä3HÛ{ݤà'&äqÏ·&ImAÛøi²›Axúmò\·MO p+&˜ÒÐI’´s å¼¹ˆŽ p-I×2)*€]cÂWþþ—ø^ŽŸÞÇËÁ14>Ëmšw—‚}Ñ4¼ @3æØã9åÞuœW*öbæÁ—2>‰Ìt[Ƥ~Ú[ïH6€#ã¹ÙÔmIvüÈS&1yнɤ‹‰´4ž9äGFtyFøà¾¤÷Ž]Z9Æñó—ÕAiç}¸€ÅöëlêD¹U£ü85pÎÒp~5±1Ú·ÏÎk|_/Ü×q§Õ_8ö~èb4KWØT&>‚ïàáN^ ’»Ù¢=À_èR¿è¿±S›Ãs!]ÒNQÔöÊ¿·™ÎN÷dê¤qP*z¡=Qq¶½…Û\Ò¸8>ÏÖÿ½±U¢acÚu1 Ft©ð+—ƒ—<¢ B;Sº_ãvšçÜ›2¼ûE\”´f¥ŸãÿB&/ðœ$Å,*9 àrxžù²p%&Öð¡lsƹ€r§¹ß^”ÉÚ“;ìJ‹³lÞ6÷Û¢Á€¢#x×nv!š{W†·&Ìúhã7±ÖšK¾ ˜ô¹ÚÔFòÇ×ÞžlvÓWY³ÕMí Â:1ö¼Kß»æUäS܆–·¦l? ñ]è,Í,°;÷+¨ÇÉe÷’ÚUÉaö+ 2¥züx!ö{)9À×/F6ÐØ%JÍa‹’oiºV>Kù,ä÷uøÙŒíæuxŸK"wgòÁ]n¾á0òüáÞ°×9ÀgX¹[ÑÕ¨3Å s 'ðÑ‹€®.®¸UžáÆÙ䤼“ëK®rêêâSõJF\à=¦þ¯ÂôC³dúýˆ«¥g,¶œ*w4žE7ç=÷²-(>Ýôõm¼µ;áÞÅo=@þn€¯w‡:»°NóZÛ~ó?°\F]R&hŠ(˜o“”¶Ê¢ìjÞÑ>ÛÉb€q³ÇÛ6ÅÚý«[Ù%h_¿Xm[ÜÆB*=T %Q([kŽMןí,޵’ílOÌFä–´{òn·‘KíýõPðš-¤õIìa« >teðyâUkYLÞd‚Dší•Wyùä0¹yewZ³²g-¿tœ[n—UžƒßGösôV{,6ËñùÑØŒNQbùiÏã¶ éðéy¡sØ©°ƒ«á_ü€"¾{'dPW#S”Åýü dÜçv$hû½‘§Ü 6S;¢ýl9mà7#¢]»Ä.«Mð1áâ¯Þìeä‡oVf‡ÞMÄd…éÐÎÜkIÿtÞEZŽ;6äÙìJ?ñ4iêšMŸø¢5É;÷-ñ‡ï[«ð‘öæIî¬ïœ$wN|hM0µ’¢8ý <]ÊXy v.ÛëIs*’€ 1÷Ò¤Lå·!ÀÒæ6“ þÈ]ޢŭ€<üž"Ç¡=á> ˆÉ—OÚÍuLí붤MiÁV rá¯ü@Þ¿°Â{Á‡éŠ=hÏG%'^ñSÊøœlŠû&öæl«H¾7Dúaú ¥…ŸÓFÏ"Œêèåde¤K[…Gòo wëÿëhg›é3ÈdÆè\òîH¶XYLÛ“S´ A;®Üâ™|›#ÃOÉqý•gY—tísÏÞ®ÂV§›EÒf¤rÈÛ”Ìêǘèºû"c­u ñ#ýÆ^ÝÈŸÆ"(â_.š×{‰ ëêà4çÄ¿4>ðüßèÈʇý§~ᛚ(·ù]‚Oß?¡*üh‰÷È÷„ðWåù×Ü ŠMs2·+ó”VôM:¬Z¦ƒ>¿À cÆ*kK=Yt™‚¢‚Ê×i7—Ówº¨s¨á_+xhtš;å$3UÚeÝøý(ͯ†ÛЩ°Ìy,Ø”Z[g£¦p—Ú‘ŒÙ&×ॼjZ¦®áê›5æMEÑ®²Š~¼#UOz)·8r»G"Ήp>ãJ$žÝÅâ¼Ìç½ÀØÿ*½D— _ΘpñVó,иê0hú¿$E‹°¥Ÿ"§Lñzê1ýf[ÊpÃÐEÎ íÈŠÿÃÞ[ÀYUuÿÿëÖ$!*b XØØØ˜˜(*ŠŠ„‰"bac`!v* ‚’*"©0=÷Þÿû³Ï=wÎܹ3 ˆÏ÷ùý_Ï‚;çœÝ½×þìµ×¶“4ƒ#ÔÖ€´ž@~îd]¥ÓfKð´ib¦®„ƒAxïu™þoVõP×EV5s¹æ&¥¥ zÿ…”——\W“¶ÿ¥5´–Öõ-ÿµ þ¿Ò›6M¢à»gü«aFí³$høßýËÝ‚Õ5nÝ­«wIPiDÐ.à‚TŠÛÂùRbÿ,Î`v¶bøØFPõ`º6t “7™þ2I`lu yGœ¬f"Ï ¤ßu·½zðì$Jüýaº¤Ó²¾$æãŽOsºÀ`Îêx8µ“, fÙ"(k«Ó8þ?}× ÊG¢§6“4U]¶>ß,ej%I™7Á½ò&²:Y´¸Z}–«‘,î>¤çÞË‚Á‘t§U äu®÷]ÇßÙu¿é¡BÒ0·FepÙýF§¤ATÜ2Ÿˆ æw+JiÙø?×[ÉÙ”íSvIôF›’øÑ>Ê£ýâ®]é=¨ÆxÛöãÒ­™±Ñö›$ÛbWÒžNÂ=aº Þúðþ¡-lô…5/d[WþŠÄïzÖœ£Õ+€s>h8ÝÅ=È~·Ác¶y‰ê÷7êê K¢†ä^ô[ár¯›‘VÜ€ìeƯ-ñ+^p›ðŨ›no'Jÿ´îñyl^ÄÙikýL·¯VmË)6£È°ñœÿÜV —uOÔ­X.c-GùÓ—¿å~È÷v´[Úké.ä{~S]þÛSÆÓ]ÍÒnÑC­+´¦ñ¡ô˜¸-Ê¡¼b‡‘gÚyh~´TϘæx sób+òõfØãÇŠOðž’tPTSž^ñ.¼Ê¬ó¶Œh:iÀØ‘¦–¼Å¼pK#^ñ°¤3©¾'Ú˜ú¥¿¨Ÿ¡¦õ7Mã›.¯‹õUIÌJbú ù"N?|¯Í¡è`ʉñ£hl–8{7ϳiè•ÍúvLl¸uåTPЩH om•¼;ÊÇ¿N6殺V!ð×Êö@¢º É¢F"Ç:W¢3ÛyØ ý ³™ç¸DW§F´1˜G¯Å–üÄït®6äoõñ+*¨: /ºiÉ­öAü5LUWð[ÊODŒœÉ“¼9ÀóGÞ[ñ£ìr^À¸ïKh)\,ÎF÷Rú½w™äkY”϶EÌF¥%‰q:’_ckÄ}“þKi¾Ê2l¡†üVº7Ëýˆ|öæ}š÷ùWÏqÊ6s½)=²Ÿm‰~óYlü‰Ùî•cw §åáP³–@YçN0-Êpü,\}Ðíš½_š$‡ÖÆwùÁ…OØÈwjQt©«æÉΗ5…#ÅÜ|Füv{—:z5DÇ”ó­ õÑ™6uÍã=ü¿yf U9uo‘~<YëøµÕ×}š Ø·bµsýÛ翤ñlˆ³©íÏnŒº“a%µ¼E§]ÏdýŠyôxªKUýõжäk1õºX"Íoù{ú×Èt4¹¼éÒí“áƒg¶×Wd½%Ýž¾áæÓ´s÷òdädæ©Vöã¤O]¿^b3ªI¢*ßNk5éøõ)ØB$Äu/íýE„©\^82÷Y{¬ìN\,aª›tïD ÿ78nÅ)¢bzr™xušqe³Mb‘ãÅf \OüÄïA·‰ÖôšÙÆãq[±b…éÙ°aCËÏÏ_³ÖëSBQ éUW]µR¹îƒ¸óÎ;muàãÿÒ¿îËݱ>åï»ýÿË3 Èî²ëPÉ 5¦øXå®lÒ¼« N l5 §ÕyX û+|]Ì´ 0YøIÓ’qCìt\xMo"•âÅ,(t©GR}ü 1ú:vÛ“g6’t…[Èg±ü@ŒcêÀä£Ë®ªè—ª×uøV\GXšÔ·Hhz_P‡«ÿ[+•Р5}•Z1!ôÇIšÒã䃾²[ìØZOï0À‡Uì‹7@˜Š«m×ðÁvRƒ=ÊOly<îk §ç?„_ÂPÌ\aѽlt9ÀRðÈÿŸm>*6Kðݘp_!LÚ£;^ZŽYþÚàøQG,óÓÀ/F’ò´rÌ$¡Gûÿ¶’X¨5æê·ómô[x2‹Ò8®âø§vst8i<Çncœàq@ùÛö˜$y‹#Ý'õY¸˜:¡þãø†RÀÒuó,§dkëˆÔæqe”@“(@·Øáö—ÏuÎén3Ñ µ—(Û³PNFÈv8]tl|’mZð°_þŒ]£LKŸ -*Éë%»>Ôu_ÛÂü +fð‡<ø ¯ÌR ¯^wâWÄbx(ÇñwŒ?cç”Ͳ ^b¼ 1Ö«ÜåÊì•‚É6!NdŽ¢ö¬Å'sÙÍÉ)lGáöƒ@»)éÝ›pJ‚Å)úôÂÝ0›p+;Z@®G}K~4.g}åJÞŸ!ŽŸÊî°ÞÑ^v—ýB:ºWÌ´Óѯ{vgõ¶i¹×X{êëI¾»¯ÚÓZ”÷³…á=¨×­ÈÏîõjcßKåŽöçXUö£\ϵ&•Sì'ÀŠílXÎ9¸‡»+¹À’Œ!m >°Ë7°.ñ¹Ö¸ârûMÀë°’í’Ä\k–ó¶ ႹqŒ]-Vg=þß@’õ”x¥=ž;Úv.»Ä6FrøÔÑ C©×AÌ#í‰û Úüxë[8ÉÙ#l$ÏÇÛn6·òI;‰ß)ó÷ÃÛwÛa¥Ã ã:»…¹ëcÆÍ¨£í£g¢„+RLuͱ«‹±õ¢§¡>àUt .·c9fürlOò},…ø­ŠÒêÊè/ÎçR´)€ÙöÉñ„ÕÄŠ#º Çœô<¼š“žÛ µ-ÇPfj}KѬðúå(B”ËI´Aú[I[¾›ýMך©È¶?rF-©ß¬º7Qý°Pý5Á˜&µF¨ôþÌÁ/?m„¹Ö³Šçïã2º›‘lÝ=vºÝŽjrˆ›<ÊùA^fê+Mû |ñ)þ"éG¿d쨸–ô³XéÎV!uÑk0;Å~BÔà–ð¯ó9gG÷`G.TÆÎ/o'•žiû»M#Ê 3]¾ùUò9{0árÙÆ„ý¢-+ÝÍ~ר"ésú¿+˧l‹î$OÙH<·$ ƒã£l÷Rîö€ï˜9ÜæÅ5OæçãqòòÙ’ú˜J]Rþ¹·PNÇyeœ²Ý 2IJ:|1î¦"yróSsüïJ;:ûÝ¥•§ügÌ=ËmGÞŠùýÈo\®WÌ6FŽT]—¿°Ê#ŒŽ—ºžÄ¾¡}çõ·MŠÈ'\Ú¯®´Jy‡’”uÂã8aÞ%¢D“N2RÖ¡ÔéO½‹˜¤˜*ø‘f|e§M)ËIÔig¬ewRÃT£ÚÑêij’¼g#½ÚLs;´’ÜÁ$÷òLý:ž¢ÊãÉðÙGÐ>Ÿ`¼Zñd!í¥GÑ.Ì´ «éM~µ)=ÖÛs"aS›ì«5«îÒ}5D¾ ô¾®a¨m¤™úE>sIÉž˜ýÊR€¾º(O¦-bÝ2%µvQHÛ0W=ÙP¯¶¼Ö¬TgúºÓ€‰Vµ²=€¿54Äf°žº©óÎ!ÎûP¯­““mxɶœè«q¤¾¤Á/s®ßÁ0pë’´S\”Ô7ìLÐWì†7íqÌœ­)è«x7¢üª@ßš)ñA_íŽÝ펡z`sMžIŠ­ªaýZz²÷­þ¶™¤þ“´…Žö:iê±®_ýó_ý’JåZÒ·µ‘XÜJb€>ÌhõÞñ7·dâ†éË»¨;t˜WÖ”EµpñHÌwý{Ÿqì³±ïµÆs¯Zxé8…ݪ“®˜« ]¨EVu¯í¸Vþl- «åEÒÃÓÙ­÷Z|F@ŸgФKùu€¼>µÅ‘áÇÿü¦O$ïtÈkNKªyYÁ—~nq™xýÕöGí}'˜àúÒDëÄ‚l@Ú¹Æ0IÔÞîfñ0 R ‚ ü9Úÿ=®’dü®ÀwÊOÁ"»0´3@ÅDò’%}•ZÀÍg ¸OCw)º¶ i Ÿ>¢íÀï§OqúÂ<'í«ö2Äõ\æ l³V©[»zÒ©(Ø›•óš;z'—Z <Åøñl¨'ñvµ‘äô/¼)Ï/Úl\¿Ï›H”ä7rUÚÊ"ׄœá¾ ß1V-=öD5Iô@à$~ªèr—»PÆÝVͬZNýçÿb&^ôÉûôª¸ô¦›ã¡B~i‰’@ŸïÌ_(¿;YÀ;@N^•ßgJ^Oƒ¾X'ß"®ãydÐR¨h€…Ýi -ðz¿·‰[íC3#áH­@âGÚñôMÓb`¯|»FàHøÂä©45¶ßŠH—;’ÿ8þÞeAª¶=Õó‰^cC/ï~•”mx;ÌÈ{º0d,v5‹ë³°Ï#ÎߟD8䇅—…öá·3q\Æ3æ…ÅQ{ÀŸFº‚G¼Ná|A˜Ý‰_ÀVQS{hU!’—Ô «èqÔ<$9‰Ð Ö…/òéGxªã–”Ã@ÜÝË»¨­÷H~ÃÒ÷Qw°ÊnK|‹Ù,âš@¼3ñ» ߌ<€‘E÷³7"§ÛewÚ&Sl}@!I,žQDzŸØÏÉ9§ o4n+oD¯áŽäµ;y¿†ò~Ä~…ûs½8ó‡ïRw"`¤ô.ÏÜ$fWmm•\mL ïjúªËŽ ‡,.µö’pæ{e¥@¤¦ñ– âên8OÅ£7 eô•ÍœQ*v a‰:àf;€àv¼OI™É¼fRñá¾Æo"ì€yEœACpÛ `<³|¼5U¾IÛÜ÷%ÎÏ*®DÚkž»N€½•t²g+¾±îÄß5r¢ÝÝòÉ»‘²¤Ijœãò5x‹oµ+Ê`ž §wµåmåÞCÙÞNºÔ,ˆlÀu^3í´ÊWhgÙ3íå|˜Š7q£­F¶sÞ#vwn»6ÿ{¡àS[šl›1™Ü€1ï7ÛŽM™r$7O”ÛaÅǰy%€àµ9áÚÄ768ñ—å!…;¢ä›Ç˜Jnnå Dö–öÊ Üow1ÀB{@µ„]_jRôú[CôßýÔK·4’Ây•o_Ål*êJvͽö-†9yN`3H»Ú ÄÑlGè|  ]·¼ëì±è©ž¹æÂE´Ù½ø ª€’si¾ãyùƒò;‡÷çd ýÊtHÚ½ìfgR ?N\80,ùñGùR–åôW©zÈ$¤MŸlXf'ÆÂízüö«rÁ¸y5'F„s/ wP½AŒ¾N~hÇÒžG·ïã„qeø7G­OÓ—&Æw©ÌPZ«õtÀ>L½*ÝsmçÈVör„ñNcEEçK ÑÓÌ»¿æFÉNU#7 ›tÚ?·_ìTË)–¿÷h/O¢®¥‘ƒð­R3*ý³´ƒMÂ÷¬ŠKíçnSâ’q‡‹Çý‰¿eÏÆg(•ð¹Äaóë6À£Wb;Û– >Ã,VåVº•Ž’ iSOÇþØÿ’¥ŒY,áœLT)ƒÂ'xõ+IßÓ²E­ÓËÉ-Ù8û..ÆŽ”a'ÆÑÝ¿ÅAYüõs¬ô‘¿Ì˜y%ë6o® “Ž;¸O!nçÄ®'Œsq…/'.ÆÚEø†“P1#ùš³Z`p€´´z(gšù‡Ž_ÒCòW+-ÀÍaØÒvkõíÔµ«n“3sê¡$Þ>“¿Ƹ´Sä|{c‡ &1˜28’ºC~Gó¼»Zè éÊɦHåF´rg·‚³¬š+}ŸÄHÚâ)lP>Àɇ&€ÀÃk¸’•ÂÑûìwúsq¦ IÇ;ÝIJ(¥Œ¶â©~[“¶¦ža­ñ:üxó”µ.Tîàñ@º·0e'p˜š±›Ã‡rwÀA”e.jnÀÉqæ /GJÛR\½¡ù"“j‚¾r¡öý¸8¢4i¤uÂfÉ\;ò-º¬n’‚¥ÅžÌoD–õÏîôyΕ9oÒ,!’ÁáËXËÇÜšàb–À{H•b°.¾a­1…M·Ï‰ûÛTßyž¶¿$Äó&ë-Ÿ»:³Ùc.Zê÷2xªb{"ñ…]Ëf#Md W*Tn5¨®#ÓAU’"Ðû÷ßÛf›mf›nº©“øýõ×_M¿ÂÂB;îðÃm£| ÇY£ª÷Ò`MTCüOböß«™úHÌþ¿^þkSzºpç·t^›þ;üèè§àjÐ)àWįÖ(K²`¶*`þä“0”mÜ’î¢þk(Q VÐAƒ5x¯M Bmukô¿ætjEú‡t\}&§Ì[_ëŠX“™$€Åê‰MÔ½¢.ka§ÉXØ?!?‡^éÎ~ôoÍcˆàåÕ1éÝw/ ÞuÅßiû3˜˜ )ŸbÖTªüC˜‚.Lü™³­ú\)Ëåûk˜‰#üÔå_£3ÒìüË%É—ž¿•¾$Fâó@ZÅp¿s¢ Kê"]¨þ~̉˜œúÔSèò„w¨#XF yU#mòô®‹ÖÇRúªåNŒÙt –«nüÕQ¯¹5J-{¨Í0–þ¬l:‘³û¨2U½jìóJ–[2Üäï’z²,üˆ:¯š›SÛ©½|`‹£÷ÀM²g‘`±¡,º¯&;´U'w—ü`5nSÖ‘¾„OàíQìÏ*ºQ8‡ð‡V•γڕzŒ(øî™˜Ž zv,ŸOC/py|,ÔŽ¶Lù¤ÊB—a¾Ï˜7!´ƒí….Ñ3Ë/´s ¾¡žÇÍ„ÓÊvÀÿÔð´Í·IÏü;(‡Ù|ÏÁÝU¸™N™ðÔÅZÅ{'uj…»øÞ»óq;‚çÅø'm9ÔCÑî0JïZK¤Ê“‘ò§ñ¤ s*¿Ä=a©Ž¥Â¤baþÈ|;»»pQÙW¶72r pÉŠÛ/x^„ûb~š Ìüލ|Âró^±oˈ7v:HsìÑœÝíÚ’Gm³¼³íY€ÒÞ€¡¢Ö¤RÒÔá–<Ú´èz€­QBb¦WzhûÛ—Eçl{2¼9æª1¨ò3Êsw;)þu,;Ôöh°Ð¨œc·”]oߌaÞ3'-{Ài&]\| ­*|Ïž ™4pwk ¯Çj2Ï?p#€WD*sîÝ7“ÞßÅ©p&à÷ òõ!ß]ñž€Ù lÐ ÐÐá©)ÀôŠ‚×Ëžòê„ÖÓ ÜsÙô0TeˆnÇO¿Ê”Ks׎œ¡þ (w ­2s}4v"v)•V&‰t.WsTz7íã Â0{6Äsé”éŸÖ 9ÑV~ŒÊmÑÅ;Äúè€HŒgÛE;̳9•³íû2©²ä$„IS2BßV_ÕÆ´YåR[Pzƒ5CJ|^£rÒQfEEûÚ¾ñ©Hõosb{ØÞã­ŒxÂqkƒ©ò;ê–v$¸Å'’ž¡½0£MTŒÄÍë|“/æU÷KNã9‡ŸO’Þ'½vèÔ ”P~´ÝÊñûaP¾éãè­±û2:3jRýÑIˆªÓÆ| _BÙ¼à·_݉ÂVØÌµ#Ž…§ùQÜBî8â mÚVD_¨Áã(°­}ä<[¿Ñ–…O%.¥ý{~TD¸iøÕ¶P}˜°nÁ|¿š4pö:ò2-v9’Ø£lÿP’¾°Ì¶fc¬oÅ;PéÄsÏòjàKʳ!åmiÈIŽh°Ô¦ífCó^£LvÃl ?µüŸ ®±Ö‘ÝlNѤó[ŸÇ0ˆ÷¿8¾>ÜF!%™çTáï³A(bpÍ•q‚ÅâŒ)îTŽx·~åü{–ôž6_]¼²£~òiÏ%Ûðîõ1 Cœ¯È²ÂOádë]ÔÒZ¿¿ZÝqê°šÛ»¨·x¹âQ6^«Éå0æV¾I´›§"Ä5vú`Côů¢xßÕÃÕצü46P>Y(‚Ùêï26!þÎbÿØ±è® ¿'SN¤?M´ÑZϦùŽ6᥂ß"ß ýÔ6¯ÔyÙ´å:iFˆôÁHWÚ sŽæ´iÓ{|×u”_ÍtÒÙˆJ_€*>&A¿§¯ÖF1,”c‘tÕ>–à´mZ*Ú6d½˜~u ü¹˜IuÜCl¸ú*í<_Þ_­aýÓwAóšï›a¤6®ñ¼¡½Í|?¾Pøº¤ñ¥TjêZkˆïø^âWüµµaó¢'ªÀn·[áa2Iz}ÏEo×ðuèRgܯcEÛÛ?©1ÓIªU”ÇùœÌžì¬î_„ʋܓPÎåÝ‹Y_äák~Œ¡)jÊóÊM=áÑ´iÓè»ÝvÛY—.]¬mÛ¶¶ÕV[Ùþûïo:t`B)²o¦Oÿ¯}•ñ5Q ¡Âÿý¯þ“%ðO@ßk™P- Z»%”@‹Z†‰5 sXJX¢gaâüüøÞÞ'޹){ß,ø\ÆZÀß%ùS`b/„_¿^ ¯ŽweöÅm[ývá@_ôÙiãìÆ5La“%a¸†ô þú€¾ veSWù°ãÿí,X`k!³ZЗ¼å¼Ê¼—ÐQ8‘EÑ0ES+©ìÝE€iÐWKý›2{޶°ÝXq>®˜í´˜N“f=B/9 ·Ÿ¦M%ýÙ9zEq¶³/FÆŒ:þ 8íQ)zSäæÔ·qÄzcõéo&þ*{Y¨Ì[P¯¥. WV‹ÃGr–?ÊoƒWI—:Z|œM.Dj,1 òo+‰ä²¹ö'R*íXÜ|áN¬d^5Æô!LIý[Þg\F²5²V”Øw;×H¬ñJú¬«¨5nt(ÝÞ˜¥ò7I¤KX’Ó’@EŠX ÀǨ‹¡öÌI„£ìµáØëT3 µ8h¶¤iÃÇÓ6)ûОü޵÷)…p$¿±ËÚ9¼DôÕ ’Žá“çnÞ¿§>À-åS² ù<³ér„=@^Ñæ.ãÒ-+øðÑ£WñÛˈ÷dܽßs0;“ô}bçjí$9 pкòRKVž…ÞÛWqßœKÍò­E_òûɸè¿öö ጷ‡ ÆZƒ(áEIGd'Âý–e‚~òJ¯ãRØì‡èfÖ¿ò k’÷þ÷±_P p,`èg‰ìY./³âÃÜ…iœÔ%g:J_´%åz¼ꤶI ¢•]H^r(“ Ö©ôÀ8æoê-&þÝ1Ϻ–ÌQ×åVŽîÚ‰‘¬?i~¸Ždè+—u‚¾r :m ïU\ è+7åèŸo/374ô]nÆÈ›Aå¸:‘éô¿Ö¹·vЗVÌÔ/k3*o*míA/Ðpo81Ú!$¾ý,.Dü;|Žûv€Y”ñºV’¿T?¬Å«qÚŠ‹¡ì|òߘrz3Ã5› 4¼*ëçQÚL¤W†?ïS—!û¼¬îö(ÌêÊ3<û ê°_½ðx´7ÎÈGˆ¹!@Òɘh'~`Ûn»­zè¡.o¾ýò÷ å¹hÑ"Ûc=Lu ² RmñüS‰ÙÂßÙ™o²a¤?J½øôÓO?Ù›o¾i¿ÿþ»í¾ûîÖµkWߪÆSê>Þ~ûm‹Åb¶ï¾û:ðßw´jÕ*§DíA›Ê»$Ä}ú§é÷ÃÑó—_~±7ÞxÃ/^ìâQÛˬK¹:t¨IMI&)mjoAºçž{¬M›6µæ¿>é÷Ã+à…©ó_% Ë5sV¿(Õ‹a3lGÝ)5®ú…±:W~¾w±¬Îeíöõ•ø­=„µ³‘ä¢@¬uMuí&®ë¸ÖUxb§a#«ÑËLÀAý¥Õ,ÿ¥‡ß¥&ëõ‰Ã1ñq] cõ»ãõ•ÒìÄqÇ’ßP×ëÖŠTxš©îˆš:˜,p´¿ôå\Õ|eûh‚áòj¾$µžPfC8ÞZíR‘j®ëûÑ ‡sëåÙ&¼J:؆êå™…‰“ž¬“1THyè6ë‚*†8¦Yd Ÿ#>žÅÉ|@ˆµÒ”˧©~S«£ul‘nHW]‚ÞØ÷`§Ñêj½¬]3ˆ|©í;Š= ŸÞ†I®Ì÷/\’–‹~ID0·©VcÛ£ÒAÇø´Pß*ñ§ó6?'挵#ÊNgñÑÁú!o¨ 9ž$ì¾”ÙI·îIXŸ…{Ù…I¤Ž(é1ÜY©¦}½Š~/Ý´I¤.'#·Sr[k€áî_¨'m4´¬¡Š'ï{ÊíICÂ¥£®?M€H—ç’¿( rhn„¥m‡{àýÞê^K’HÏ…ÎÛ×%ÙÚ‡ÅådÜПB»óÄŸ¤ñH•N'<Ø,Ä ؆E­Yà¤ú¥@óäØU‘Æñ×è—âÒÒ%*ww¹‹YÏyÖFW\ËÂìGû’v®›é tŽ?Áë÷¹å詸ؑtá/ùùc1\íò5Ï«þª]ÊGÇy¸%_€Æí ß²EÍíD$Zœz RúH|]`ÅÔ wØå‘ÿŠñ·‚Ñt…rq–x¡Ã‘¸})÷ Šf/;ˆwËPÔî‹ $ ×R¶½ðw=v€T"I/Œ&Ì›1'/NµÂ-˜ïä-Â¥¶·‹VÖÞÎQ«¸³YäkîšSWsH[?…–AÛRþ¤±r*É¥> FyöVÃ;´>”QÕh;Òñq¼Xù•M”ʨYQ+ç2¥ååWóõSZOÿ‘Œœ»†V²ù@yø€›vVr8îXHÇ®¶*zpDvKÔ‘~® {qÌ ~ÜÅd2ÿ›_;˜d›÷T;ÊÞÎ93×âd=Ú¥hÛ*v©‰@¹ã‡1Ü=ËÉKƒÙÖ‡ž$Žé ¢Eolå±ödì8w1Úè]-È=Û˜lˆd鋺”.cU*©Ý±€–bþ áÐ*ÐOM_‡¶Áßµ [27Î FùÐW(‹Þos¢+x:ê-0c ¯¼ˆwÚjø\âÌ{Š&ò]MtMwÛÓö¾õ˳¦µ3‘ŽXž'ó;MT‹»ÕK¥\?Ö™j}’œ’X‰T³Æôµ I[Ç(?×ëò¿©ÝŸ?Þf”‚ö ‡sÁa„VbïÀ¯ÝN?Ò)ž®´ÉÝè]›‚9<+^oy¹Ÿ~ÔžOëÓhµ#`šSZ§i.ÓE§Nç½×_zê ´ÃÅ1zŸŒk9ÒÎÏ€ŸYž…ªœ64_ìÚÞÖBãˆ#ÍóT9ªö¦S£‹H×2mÀkXü¶´}'ê]êq‚%­ž5>ï ;®üI[ɉ 6ŸJ‰ÔÍgƒipÌ?qc¥'áz¿Æ´Éh“·ð¾Â͸§Sï0GWµ± ’Ɔ™ÌÛIáw:ñ8ì ¢iD\k’z‘Ô;øl00»x`ô•{éý€Û¢E [ýõûþûï;V~åF £DI +ŒŸþÙ_~ù¥uiܬY³lêT îMœ8Ñ¿ºXNa <•¾á?ÿüÓ9­p?þøcûñÇÓ¡}ýõ×pܺuk+ž3² ¾~dÏ… ZŸ>}Lþ$×F7ß|³Ý{ï`Ûd“MzòÉ'»øä^€î§Ÿîòµùæ›ÛC=d·ÝVÕ䦾ñDäx HáÞç"—~2ö©ò:í´ÓìÛo¿uõ+`{ĈiûàË”W¹, o_[²d‰+[]‚÷ôÓO§\rÉ%.O-[¶tõtÜqÇ¥íÖå˼yóLå:{ölkܸ±]{íµ6räȬQ(þ&€žŸ|ò‰=÷Üs¶â¯¿ª¹ñÅ݇ÂôIñ¬-¥¦Õµõ^/Õ˜ÕøhÃò(Hjâ{§ÖúŠùù§ôOAßúÆ¿§cú²»*i› àÓÑnÉëIÁ¼JE‹ˆ*¦½Ê¶ú›¹5¥ànâšú]—î—#ÝXŸôë–ú¡)IÌ`üÿiÐWqû ¯ÞËõ' Ĩ/è+ï“ÑQ'ÐWÄ´ž¦¥¼A_íJ3=äèovªè+Ÿûfì¤g†&õ‘úŠúÒ†únÀû¡íÕ9¨åZ²NŸ^¡l$á' JRøöz ŒQßÁ®-@IDAT¸Ž|ÝûïW$–§^›±¸ÀÂüwaÌ‘ú‘úúïbòD’vÞ…4ÂŽ×v2ÉWŸ¡§À× è+õ’ Ò%!o±4«ù>7mTµëŸ6â¥-ùìÏs ŽU5DbÇ««ç`(ë$ŽÙ²ÝVå„EIÝÒ†ìdín¥v5ZLgòÀs«¯Z릶y(b£u¡¤¥¬ÒJ Ø)®e*HG5Êb)ª¸þ‰y­õ£L<)ï&HF•¦$VXjñïSäÊÔ[S÷ü0ï->Ã}<óŠî0ý{ñþ³wú6€Í§µtËíËåm ¹ ô•Nµ©ôÓs)ãÞHßlN~3ñƒ#™Ñ–¶‡íÙý¿r;ò*»0mŽ›Kÿú|ßOÜIØÚ„ðfÖÆ†°X9)—ûCS¬M„…9á´¡<ÏÎÿIÑ:F{ß-ÜU*E€Å[r9––¶uÓrd—¬lXíqÐáˆ>” újÔ\é\r0z9SîÂ]lG€mËý˜ÅÈlÔ4tbá¬8qn½°Xm•}“4é–ÌÒ8þù]FßñZN*ïÃ/K$J@_lã˜åLNy¤É©!Xþô@_}NÁo;äsäg:h ÓójÚ1/m˾aCÀ-¼Õ-¼¿M_µ+ ¯Æ §'uößñÝšbêÀS#ÿ–¶s´½õг`GÅÀ—ËÉ?!ÙK’¸-¦o~gß"M=1¹!åt!6´[]îVþ:ï"ò–œH˜·bP|ª$#§ð@…»y6<··Ý©0NN̼s» <>Œïp‡½‡ÈÄ·Ê>#Ü0n$ßGëÍæb¶3é¸\&Õ©ðKâîÈâòa›¨q^R¶åO±¬+ô¬H¾£ôA¯Q§KbñVè$c/*¦ÌuÜ_å—üÚ.¿Ön Nƒ¾ÒÑ‹*‚‡ãS¸Ì‹ôŒÃëo¤‡ü $©øÒæÅÎò@ߊ‰”ÉYÔÁ¥ä£=mõAF­E@%Œ2‰ù¶;Àí±])£v;Ú'¨k¸ö5BêËóF¨Hx3çx$ßCŒf7Å‹m< ç;Œ÷"-Yñ›XE¨[ðtó~žx0-h´–J‡›ø­ÏšíižäÌîáù.ö¨JZ¥½ŠÅz·ä·/¿^ØKò÷žŸòKR£>k0nª=¤L*?§Ìæ£ÐJ ©_@_QÓÐeŒ·eözÙ ö7å' ú…ð}qׄ –ÛJϤ,¢lÞ·ñ«ZÙ’¤.½Í…£?#‘D¼¾{. û |¬}Ÿ÷, Üãdöeû)ÿ5û‰Wöæª@²nƒAu½ÚYÅ©M¾m·Üa³bGã}ËõÓÜ>¶³tºŠ Þ¤’tÇîu*ÐÁl‘k©Or/©ÞèPüJzgr‘Ð.˜Ÿ†Ÿ¬ø*ÔŽœ`­rhŸ± K/Ìô_Ê&ö0þhÿlˆÑÿ.f³À¤ƒVí6|‰3Ç‘Wj<µÄÊùí”]êáÆ@Ú”Îܹ9­Ô³(§OÕ úÊ a¥+רÄÛ]i0+~é|Ì!‘ëú|¸I$}ŒM»0ê8¶èŠÊç¬Û£Òƒ,u§‡ í&.s²‚O(ƒcpñ›sg‰ªôŸC?» >ÝÖ¯8ÇþpS|9Áš…:sD]yJy‰d‡æ~IÏÿÅB¥aHT£í‰cÒOY‡º¤l6ãÙÌŸx8ÔÖ´©¦Š^í3vRã°±õ—쨻£÷L¾Äe°Ä]XbFï°·CØ€P ݹ´N¸zñÃ?ó+£8©ƒ Ú…Û8œ×Ü. m@Ù^…};ßG-Ï$æœ,G·,cP”úc¾Ü(|¾µådÁ)p-ï³)¯MÕàzâòF![+æÔ¨Þua"¤Té8>%ZÂ;îÕÍR_ZÓÎC³·Çx&bónÿ4è+ƒ…)З×pWÊ›:®*.¦¨4¾ÖM«}åûIÊE'îþ è«pN§ÔúÊþÄ8|Pj~зGý—Õ?ËÄÙ|º‡9ñ&`¨•Ø…è=¯ úÊÃï”ù»9Á†,m« æt¤Ù¼ÿÁ˜°ÌA½|6íe.f:Øùû]@Ña‘õm7øº¯hÇ1gO­¼‹sw?¹uL#ü¼‡93WVz3Td=àU´q2‰°N M‰´æéêÞÛWð‡‡ÓºÎDÆ}%|² “BçºúÝ9´”5p.‚ €¾r«Sˆ‘êÎô`iO.~Æñ<C|7ÚÀ¹žuÒŸŠ)ÓÚa^Ê÷_´É¾¼¯pßÅüý‚²Éúª N$/ºHX€4gcÐ˼ýžŒØpÿõ\_vÁÞ¼ï5ú+ YQAAA½ü \tÐA –é–[ni%%%öWØ&‰Ë#Ž8Âý$½+7;ï¼³’å_௤xE W`²¤h<òH¶/±œ à  öÕ_I·nÝÆI†vêÔ)-¥*³>$ »[·nNº´.÷+V¬p ÷E]d_|± >Ü$+iZѨQ£œôï}÷Ýçì ÜöÁõúÆ£°âúSOÊ ·<àOiQ™>ñÄ„x+0W’Ë™4౦ò0ܯ_?ð;xð`[¶l™}ÿý÷ì4h) …+‰êB›§»LõPÜ øWÜ¢%Õ­‹E™~”Õƒ~zí×¥‹í³÷Þî]$}>lذô·ÿ¢xÖ–j–^ö¤Á²»¨2õ•†W™ÔÿmvªÅxC¡çOÃFmq‹±RP-DÐü¿áý1˜µÐ¥,ú3©»c0%ÑÙɱ Uä÷«úLæq7ðVùýéMpºnuâBÝ›êºÈ,›ûui¶j]V°¶e¡ÌôŒ„à_i0³6ocYþK·°ôæúôª$¥²Ðc0¡ëc.æ¨-̉˜_‘ŽX‰4¾•Ñ^…û#¶iÕ'oêß_Á@ø œŽÿù i5‡½Vß×±«úRcŽ l¥`@ˆš‰ßb²£?‘1Gê|:ÐÀ?Šä›éù l ìx5:˜‘©K¶Ñ)«tçõ˺ä!HRï𔋪LŸ#­ÁÛ„«l²¿=‹û—CD/ Ÿ·âág$0VRÎÞt0 eÄñKoÑZ§«j–}`ÂÅ…MéUÛ¹ö»ÙI_v•>5r®‹!Tòõmªl;Éžpuð;ÌeoyÍN\¸b±ÑvqhÀˆ·²ºè{iɤ›iÛbFƒteâ6jœÞÆ€MâõôGÓðö¨hg½"'cÖ‰òœÌtÔØt ï„/ÇMGû<Ö‹˜~·üâV&P_¤‹*EßPOÛ‡r`˜Ÿ¶ÛÃÚmáü¸M‡rÙˆØXçN [¹=¯ò[·ðsÿkÎÕHgãtëâ Ñßå]+Žl ÒB.Ü|)½¿Y†”ÛÉ\Ú&)÷ãc#ìb¤°&FÚ åG4.±¥Iñ¢s*:rªò2M=~©þ)@HT´-ª=žÏ¹Ã½kSé€Ôæ‚ô¾Š¿`”ߌ§Øèo³²làæIÚ*E;" ò*G†}I|©²ðFr²R8é«“_bB;s€lÍü9Öß°ñLëø›ø¡3+ø#íj?^‰zî5^ˆÔ/«.ý•º Óo÷0‚Ѩ%5j‘ÓRæj‹íc‰ÈÙÖ"r™]˜á.KqB…ÞÒð¤Ø¦Å¦òÁÈnO8g´UéÒÔRí­’~ŒTú±´Q%ÝøŒct…Êk…ÝVÜÆm¨XøPòs7áì h÷ˆç&Ú‡ÅÑH@•1ü.ÁžøszñA;ÆŽ——Ôòã‰ó\Ò"-îØûö„ãäÐ<ନ_¤¯àyÒv'i8›cè»ØÑrf£Øø™˜âåæ$µ£ ï‹u¶9\0fáÍl“ðŒÛڼܛðÏëåÊ+‹â›¢ì÷RúS|– Ó¿ˆëÆ ä©rĬ’$tÁßT¤³šX²àIÛ§òA;5²¿=œCù·(¾ˆÍ(Z¾jo¼eW”‚çOÔ“.;_.ën”| ôüªÍóò£m«Ò3¸l±“‡ÐÖ¤¼oì1êWeYù8U7» )cú*Òæ3æOÊy˜ñy?JbuGÍÆCl^žC]`Wv(ÊóÝQDNtå`ÀìS+®&ÍR½ý©ÏÖ<ïÁ½ê‰Æá6«¨c¥"@c§3Ýû²žsí .uì"õNº:eQãA>àHÎŽßÅfŒÔmA§Ã-¬;›n×RïoSuD¾ºà+›Ëöm—Ôœ-ÉÛs±ë Ð^‘g37J׎§-\nÄåØXʾµK/ªcpr •dÍÃ¥¾dZ“Þ…/Ò cï ßQâ3Â{#õAªA´•ÿBÊ–R?™/0_ª=VLÞHò^`ùe^eYíMj>$5ü2a[ø,NcíoQámA¾¶cnI/ø¤a¡ûòþHød¿ÄŸÕxßúðP-O&üϬÏ÷ØÐ8.0v=çÊP[©IÛ™øT>½LÜŽ¤¶¥º»–|ö&=Ï¡BiiZÁf“t&Ä.&¿Ûs²îo$Þ#ÈÍ{u®#üÕZE"ç&›´o6ÏíÚµs ©Ô`+É\Q28ó-u>5hÐнúª¯¤9‹$-+’Š¥ú)<Æ:î¤Ö­«ÔJÈ\ íî»wv ¬ÔOl•ÌôþKBÏž=í¦›n ×x×%wÊ·€f…íKŒÊ\ôÙgŸ9@\`¶T&HŠZ€e£FbfÙϯg<Îñü™O¸÷ì•5ýsçε½ h—.]\9IÝF$-­:Øk¯½ÒÆ;íã ô•”ï<à@dß$³ÿ Í«ÖeªB’Ê… ºz—ôµÊÙW—Q›ù–Úµ§ë®¿>˜ü_Ï÷a‡VCõƒ¯š$íø_xÑehõ-%ŸÖš‚Ìá9›Ã­˜$½¡Ð³ÕÀ—$²¹šÍ©w*ņÕÈ©M·p0îë}n–År €§ŽÈ5°ÿ_’^j§ õ¬ÓM²é+Ãÿ&´'ÝÆ¤lÖAû5}×ò`MHl7šÖíK:n9ëÉv…ûÂÄ å1YCJ)Í:§ß»Á€%j¥—ìúã#Gµµ›³x—a߃çÓ00>[%³`zF&í³´›Àš ’žãqgX×øœXG›ð8^ Ñç=Þ›§ ÌÝê'µ‚¡¬¯¾YµÍêi¯À‚¨:ɺ¤!|¯ðâ—­oV×ó6˜+ï6é€+·CŸ½ÿû:‡®¯nYø®ZúV3 |ô¢=”ñ-@2¨"d£Šžž+\:ZºCb±c˜•*_¿uˆÅ|RÌ0¤‹Þú„ÚÀŸë@Kgèÿ©¸EáûVík凲ð®ýsÖHíˆ$± I—êx¯¤ºE'Ðo¶H±KÏ\f_rC»ÁÂòGpŸ‹+ï³ÍÃ7ÚãïíI±Š³0ÇöºèÎço/+op+½–¶`Ü_0¼3˜ÉöMõ×£`†¥7[GoŠÜÅå#âKÔ·“„êÍxKÊO°c"­í,–#Òµ70‚Þmч¨ßÜ$iúb:Î$’^GbH'ßwHÎK6@rªœ%OØ–ìN"ÜVH¦TVpT5¹ÈF#{J¢ÔötuÀ¶,ðûzáI’*º#ïmm8m©æb*- Se3ÛÌH~m'–÷X¦^#íàH7[= ƒNNïáØT@0„æ"ÅâÓTÛÆŽÖ†Sø2gô0méõ7p6uYýbfÒŸAéÅw†yÕg®d¨\9I!0àò®v& s6 ¸—Þ9iòL•7÷¦V;5ÃL%²w9~îm²~ÎcÄ1Û>ÍÕFæÝi s/Gzu)f´©ï;@Ί6à{&þ&³Ù´5ÍAùQ¯ïD‡û„µ­Ç»šÚYÁ/˜³ œ˜ˆÚÚM´?¿#ùQö¾°wx àŸÅºæÝÄ‘še*õN\eyæÑ†ß#.Ö2±Ãp?ßËOÎÑ )ð«ò’ñ…µçn©(°â‚汉Ð/ŠÚ¬EzÎÛ„³ß\Ô†ÞèøX—¿‰…š D!:Ï&†,‘FmÇaò¥Ãñ÷›vO![lßTŒµs£çyi(%½ÀH†þ®uú¨­Œ¶ n¢#û´ì?P·QXr+QŠ?Žœj Ø,x%ÜÁ^Ø»ùOÛùZÒ¿ˆ~e] gï°)Ri[Ç¿·»óÏÁ ØäK–¿ë$oßä;§_16KpoÀYéè}]~¡(?µŠ›)‹ëøýÎ{¢Ø™§O›ò2»ñ+R¢pX“œ 3ÏØf§Â”é_¬•Ô/œ4;.¤ƒÖ§ÊçSoI[ à\É0 ÉÝiÌE_°¡³¬ø\kÊ¥‹VL²¦E'Qv{ØÅçÙðâÍíXtê¾,~²øDÚ¹Öب ­Ê¯yoNÛœ«œâ…Ÿÿíaïsi{ãðCûž[ÞCØ1^mêmㆎÃb3üÀüe¹¢Þh\ªè½DÍpkÛ=×Ç–U2˦cÿšÏÅ|VjLÆØŽe–Ù]Gâ%) lñÓ'}´)!€\s .{”Ò(½É$DðsÙ!løLÆÜ#mTn(Éi@1«x’2›Èo–ù)<Êî…7ÿùëU™ùoÑãì°Ð–v:ù°²K}S61¦¸7xPÊ6Yv€íYÜÕ:Þ‘à´»/y/aÔžµÌÎKŸüjGÝuã´M¥» l0óž¤kטº%œ¤¯oÉ÷1.¨O(§Ç9v¯#WâG€ºg·>GÚ~›Ão26¥Z¥Sçñ&Rï4Š“‹§F4Z4Io!xékî=øÛ¾H–âÃÛ‡rýrú™ö­¶M_Íÿ’gª¦}nÄ[÷¥µB 0ínm_R¬ç¬ç–Îí[€×™íµÂXº›ua ë•Þ´§nü>–òHϳÁ€ö‡ºÑب#.ÁC¯v;ê´‘èžËèµôß^î]‚?y)ÿUõqšÒfA?“6.ÀkøöÁçr>ÔŸfùƒyÊr)éþ™qõpæh'ݲVˆŸº0”ç„LÝ (ïa;Im^1‚ýM©ÍüÚÖ÷ž³#ðq߃gºˆ8‹SéñE¼è¾¥ûlìR‹ê˜uœ}}ÿäoògx˜˜,u&2¾ Jü͉U¾ÙP~}ÙÔkÀ<Æ&¯¯jn]kjØÆ Irµ6Ð)]ÀÒ­«§Ô!PôI’»Ù(hîÏÓ>€—éÞ_*#²‘€IŸ¤^"H’žúè#;üðÃà«¶Q›z ?~éV7nœS ±ÞzU“¡üÉÿ¥—^j\pïÜ=HR¹hÿ›IÌsY- lÎ@ I±™)ÆEÎÔþ4(¯ª#_;0áûª $I]¨®0}Z”€òí2Ÿ ×Àm¦ß5ýÆÄ§]1ŸDý§HÂŽÚ{Óº‰5\[]¨šÐ¦Ðä¯#õ¹°ÎQou¡™òÔ|4Áh‡^je“°VHb¡4Æ.ÕÇÐÍÔYmaf#`B VÍÕ]èèØ $UѦêy L©T)|-`$@Ÿ×Rð8MÀyúõ ŽàEji‹,/bà$YCâ{YØ”Éùvz¶fF˧=4\Í»ÔW=š²p‹øøé¨]莊‡fè®ÿƤ×zKÀ<©î¹‚cÅ›R¿ÇC®ÿ϶)e#˜çë9Ò·<‘oÒ׆sÉÒ'¸~•2y½ô\wLqxä!{£r”ÍEnÞ§,$÷'ÿ[<þY~1`Ôf)«ä5ÕæÃ{“-ˆ~±ÛÈãBßsµ' á蕔͹S€–”ôˆ ů:.µ Vù# ÿÑ2®FgÄÇð­B »¯bGÛÖ´…F”«†5µ©5ò7Z#ñâSÌ6¶ ‡öÁ¥”Å ”ù®ÈÆC¤Õ]$ õË•Ìm¬gÎ@{¤¤/ß©|££YéöGãc•(ƒƒ¨ç­8Êùþ/XZ«P3¾ˆºŸÅs⤠$µ=ÛP¾ÚH(*˜o•=8²•u)†¤ç8Úu[̶Æ¥îÉsB’v‰„ù˜œëmŸò>V&'¼'ÌÏvU1T^ƒYk¾WZIáy¾Mi@¸[Hü‚ŽC—%Ç1ÀÑn¥®Eï¢èM0R½ˆß_>€˜톣¿A^!ÏßðÌ0ÖÆ¦‡:ÚY-Ë/tÁ‘hi[''òNÚiï .€9”á.‘„q¢‘s7€…ÒБ¶"§¯=ZÞß–æ³ÂòOhÇ¡Áí $›v²þlPìÚØÇË‘]¬jÆ2Î[™Ê},eGÛ‰t'¼bC„M‰O³cXs.”[*È>åQÖߎ-:•S­]ùÛ¶~Î!äy€EÊYaæ?j++Þ²ÛsíÁ²Ñöq>ùcF’¬ÛWÜbý)öÊÿÄæ'Û|æòGJvµG£Cì(]W´‘½×ðOÛ˜<E8º3¾Š Ùpa½t%ióéTÊJ=K—Q2Œ{rvŽ4wW­37qfµüa¼0Æ GÔ]ø òD=’žÞèG™“·)ù4Ú2×çi\K …}èBt¡ÖêÏ_‡kmòû¸‹»N8áó󥈤Sq]¤ï¾ûnw¡™ò-IZéþæ›oLéI­ÀSO=eK®T ¨lÿMZDc™Ÿê¬ÁxF ô”ŽeŸ^‹t!šúúv’œ–þäÞ½{›ÔT<òÈ#Î*/Ïgz=—ï¼óŽX¥‚្ÊFú?Iè¾ð NÊXÀïI'äi©ª¨K‚û%tøjSA—ºù$½Æ?þ¸Ýzë­i}ÐÛˆyK‘âýÛ /Ór VåÿI éYýùǃƒ–jñ^‹ šzï‘”‘úê3è[UR)«y(Ÿ>Uo1¾éº}Š}ruŸ‚ ¯oöŸzÞ¹ŽAßµI·v%ýIÊ—ö=Ö08ú5'`o¨ b¸úФÇ+‰©ÔouÔg¥ØU¹­/è+·g±ÀYij&c'[sGò}fAßþ‘î5½4-›„ 4\}A_¥áyÊmu ¯Ü}Å´ïÓ~0Ç™R¥º•V ¡ I 2©~/f‡\€Ê`yÊ‘–>©6ý|J?¯Òø ;ÌA¯N§]ʃ»U:àÇ—”µzàÚH‚ o¶ö¦ËÈè{ o›ò›Êo> _õ< ”)?U}½­3û§ÿÓ,r‚ƒ§·V‡ñC\°Ö©»&©hªæPÕ„0¹4 ¨Þ€:SßÊFkú*1yðj%Tø ¯Üh ·_,úÊì2$ÓÒ7‡v‘QMJ¨ªè*Àp©YXãË/ªY³(f aeñÜÌ4>ŠVŸ+¨S1÷nÉ>˜EøÓ6$~“]*3Iwìk®[ÔâçUÔnôŒçm{ÊY·CjSÂ'(8ë‹‘6Lt«‚§J«€èâè½HŸoM:ú±XùÒöAòæÒD › %¨ ˆÙý\v´”|Ò³¾º’*η¹ÉÏ\¸Òí¬ Ž®x—4Ñû¥·ØvZh¦Q9Kñu‰±@‘‰Êß©~ࡱî0~Š]ÇbåÙVQî+¼“†M$oÚw_Q¯sS¦,YT{´ ù¹:õ^õPÿº”#ÿ‡$ˆ‘#À¢ùñÇêH_ö¯“®cLj;1XŸïÜùvdQÓ Àa—Ä.^LÞ‡xV9OólKÿ[hí*®!üûùNÈèèà±m²½‹¯+àáÞ,uI_2‚zÈENZÚµÌ(õ…ú 'ÝÚÛ ÛÿKºž ÷³ÇT•Ѐït6ù\Ø͹¤áDÊê$Â~óøÝa}Vmb?DZÙ×ñK-ĆIV*x×*пý}ùñ\¼FÛ–ÿáõ$] ­i¤ƒML÷@_Bà2±dÃçmc.‹;¹át[Vr>ã¥Ñ©#.ÈjÛË&z#^IûžuH'žGòÊþJ¤{Ÿç'É`ÿ§Kßñ;7gó«IAîÒ8^žrƒNãj$=´å_ïÌ ôM:;æo—U F"Åjè?^Òàw»ÄÊOu.kãùÄêN– IÍÜ 6‡ü»kœS—qYЙ{÷A_}H]½;E3l8&À/ܹºâ9:º-ÔÊ.l@‹¡mZ)ý5×’–ÛY/\Kʤ_d¯ 7‚GkŠ€D¨ÚzÏw+½»’ÖÝ(e0™¹ýBÀèfð…ò³˜Í_[kYöóÛ®UÞÅð¼ ´•êµ `Š~mÏaðR>/Z››ÚÌÅç™XA?Ås½Ön][M5Œ³àÄ–·ù”=$ÝqüÒ}ég´¶ቯ 7vœºˆèƒ¼·rßµýIµY×m.ÀTºZ¥¿V¦e’Žù‹| :v/PUj$!*P±¾ºt3Ã~ûRšÒ=+éRýš4iâ$PƒÀýè]Ò¾¢íFÞ¢U+—6Õ¾îbÙ1u¯’ªÔNH¿m¯^½¸äí^kÓ¦3Æ|u®¥C:“ÏèÞÝÅ›©£x$&ˆ¦÷òÀ·ÿ*€\åèë–¹ÿ.º™$Ißn¸ÁrssMjî¸ãçDÙù$ýÀÒÿ+·zþS0~Æg¤j‡’Üð/ðYRºRÁ!5~[ÌŒS`ñ믿îô>uUëb½=Znf'Ntj*.\hÏ|ûMúÂ:ÅóŸ ¦å:A±œ!Lj£ôZ›ƒ”y]adóª|¶0yÝë¿ö‡©Èéª+µrI‚þ¿Jþä·&é— }S5 ÷ÁñV}nêMæ.ñ™1}8$iYY: '‰A|äLÒ±Éõ2 SßÏpê²Æ Ó9Šòƒ&ª!å/ý–N²ÆÛ†h‘êÌâ¥mUõ~御–tº€uü?|(~÷qŸSìTsL"½o’©xƒ]qés bøHçv]ü9†+Hç¤ÆŠÙôšÞHŸâKÿä¾g•0£"1€ºd.x‰KÜþ‰ÊQ’ækJÁËb¤JA] ÊúÝ<â-‘´Œ¥o u­Z@éæõJáËS X¶€:PÔ¤Êâ·W9ˆöeñèñæÏD E?§zÅ9 ïò9í]GëÄËœ تEÉP.³H7ØÓ*ç.¬9\ð4“ðû¹ 0¾ëqª¯ TëuñëXLþ‚»Wq®æ†êa\B·IªuÙOEŒÉ¯¬AJŸ K ®H÷ãŸiØÃÜæ„S¿‹þ±—áäyNuD[ÒÊ5æ=ÒË™nB>Èo_=0˘éËè/l¿û´×þ»™Ï¤í#Ï Z*¹¯å,xŽ´ˆcí'î¡Äó wK u¶¿ó·SJbÚé}DzT[ÁCèÓýi«ŽÇ øE¤ûTÞ:á¤÷sÏÇ´ŒåQ<)Gè.€ÎÌ…ç¡¶`©„+6­DëÏŒŸ@莟ÌÄãn¼Î»•²|–<}^ãMÉÏ6.< %´.%D»Ù¯y¡wôN¯å&¾;jˆ>¿J{±ìt¯-%ç⺔0–ðÌG áΧ­mË7‹áP{ü0ºéÆx€ +œÃïSÌ·lè˳ Þ‡àf ž{ãÏ«»›)‹»hZ [äFÒB9ÅyOLÃͼOÁÏN„1œ†ºÀ[þ*L¦,ÐÂ=ܧÓ÷Þ·çaFÝ–žŽÝSÛ¯l@8ä½âQÏ-‡2¶Í)ü·= 랇 sv:GÔ¤}.¸¼/ïÿcï<¤(¶5|&mÄ¢(&@AEÌ Ó½ ˜&TÅ€˜E‘d@ F̨(æœP#Òæ ïû«§g{fg—EñÞûÞ»f»»ªººÂ©ªS:EûÀp¨h¶x'Ùå7ømdÛÄöbkoˆ÷N·Nö4”+»ò-ZNž÷)´3<ØÄþ8?¨Hõ  Ú²¯‘¡Ѹ¶2Ê&²¯(¶Ÿ}è|[´•uŠíŽIÙ÷Å–fÁÞöäòƒé›=s ë–N”µT¶}{ff ,D~¨èpÕq›åž¨òwñÌØI:+iº4p/'w­Ü4¾ “ÎÍh™VH¼J¹?à9*_Ž éFZ¿d¥•–L±3àAÍÿÒ–U\jÉ’i˜\¾Íä Ò¾¶½©›š—x›ïc'wßPg4Ó·ëãÏ’„ìõ’'m`ìp{ æ~Û[ ›ð`ïs€_±Úv!õªÃädóQe]ó0ñEðöZ‘±¨õ}ñ4Û°¬%gcR‡1&ÔŠ0”Má6‘1ö´$ü(Ô½4|¢]„ÌçÂG`§“ô\‚ U^háÈïïUÔm üï<ôrKÂ>ÌâñcØUýÀNìeO…K˜‹Ñ6KÙíÅž÷+à]g ~^Ñ#›—4ÞK›\ö©E+Ç’s½çʼkì Þ8àz6nµu·QÍ©hªj[§ºÚ7:°±®–¶ui´èÄya­³»£Oië¾4‹ ÿh³"I–×¶J^ã[Óð3é߀ëR…¶}ÙýÒ®Œ2å:º04ÓÊeoçþW-ì”ÒŸ†Oµ‰€¥£Ý"ôæî=gZA‹Õc­Sâ7LlÜBŸ@[Î"êEmH;nªöÁ§?Ú¹(BúY ²yq ÛÎ5ý‡0˜Þ=?~CŸëöæP'–˜ìÀÍc‡Æ KXùñüþéº?´íB•·Æø58,«½ >”eÎZ‚wÂçd¢ÚÁ¼ƒ¯j}ý;/ÞSgêjIñ×hhŽ™#ê-M-BÛ‰´lƒl¡ƒ¬t(ñË•çØ”Êɘú‚µ®<Šòÿ…²yŸü–¦Å¤GòÓ–z»…–Ø…,Ð:ˆ°êTÊk¦ ²îÈ%QÞmgX¨=¬eUo;$>Ôd‚í]äð?²ônТÀ}'Êävï9re> àÇmìþÀÿŽ m dçI ¼^Zƒø4vL\”£ø1/ãºa^ŸZGÚ|†Öæ.Í·àÍoé‡Ö|ûAw¯ÃÒrIòú™”¿³'×_ÏgWDs‘Âd×?/ÃÕ/Í 1ÎÜecóÝ™1¡UˬÛtÆ™ëÓ‹ù¹që0·“¿ÚnÈÈí¨ËcÃMêÔåæêß ÿ)žèŒ8ÉÍAîÀ4ȶùæ…Ì,Dù þ#%)6iq¢qDI`Ç[¨jܹ¡æ3#²ØQ8«^Y,`!é©ô¢bmXµçln­õã”æ23Ùµ#’ÒÂú´ƒ±ôGÿ¤ER~0OÒû¿œ[¾?^ æói¤›4v\J{õõ×ß°9s渟3™yÀëäÕ¢E ªÊFì/¿üâÞQÑü‘Wk¸1ÉÐq"iÏ (•æ¬Ì>H#Tvë#¥G4[Ã?q˜ÛüùóÝ{rˆ,xUitþøãN«4š¶jÕÊÙ9Ö÷|p\÷¢å˼OàkZ¦™>èöwÝË6¯êÓ'¿®‚`®ï7mÚ4“ûðáÃØê燕ö¬\ëß¿¿;àÍï¯\e6cÊ”)™Ÿì>ËÖ°ÏŠÛ/WäÏýÞÌ™h`vCšáA’éä†-?‰§dÿWÀ½xÄ·i ÿgî¥iôWÉëþ\, ñRЯ,áä#¥^Ãת¢ùi¡¡ìOF˜;±l(šµðô5ôê ÷#:œé+寵†s£Õ\­†¶ÊÛõÕËã2´k=o È o¯NoãÉ ˜}8uˆö…ÌÎä£\Í×]HâZ£è8€%oˆ­\f®Ö©Bµ'¹ô! ì1Å!¸K“°! jÒ ,>ƒñü9þÁ>Å^¤¯¹}ÂPÐôÁN€9Ò– "¾îœg,9›é_êJÙ)ZmÎ%œÚvä‹üÇw#œ´$2×1‹‘šŠ+“¬4I{cuMÊ’ÃÝ4Äw×Vîçëu²°µ“w!'½æ¿b>„?K™Îæº?éøƒxîçny! ne^ø 72Ï C/|êçƒb¾ƒ­ÚÍ¢l{óI‡ÌÝÆdȧÊôÊñùð‚ÿNðú¡Á0“<ô†eJaˆmôöî±ÑéÖ<4€´K“VöÖ4Åð([öð]uÝÞ¹‡2mŸæD?7Úb¦CóêRG&B]Ò¸"F½ÑËxÄä3z2õøþO¤ÝÒ´½^”ýCQù6öy|æO’n2 Ó–Ëq>°\}ù<4Z~b2«6²€|4gº¸öò"L–,`9œ”“àug¶Ú®ˆäÅ\๧ÿŽ õ¶…á^„ƒ×Cíí‚ê»È§òZÒtù=´R¼Ú×pAÖAs\:Îàø€¶^$íMxÓ£Åäu©»•ÆÚIꈾÎ] ¾ ïkÃQhSïšçï&ºV|<@¹'‡Ò KK±P'îŸÎz»@üýáÃX\!¯ ÒǼû¢ë÷~òÃÅï$mߺ‰ÆQ€ƒÞµpS…FËkÕCáÄù^ÈðY6,ÜÏ-lÄtƒ4 ^^éMý©¯Ò!q²îˆ¾à®P+Ûû°–OÜgÙµ‘“cœ·úÙÍBÔ[Í™ðÅɪ¾‚gTP»Ç‹£ð~®psÁÍ\5i¦“óíT+@õfp­€'§éH+,P‰ í¢økvré8÷älj‹{ø ï¹øïü+ì0u$[¿Iü"»¶1`g?~W*­NýÞIz÷f‚ÑÅη±ž>OÆpSÝ'fðùÞ¤åtâ¸Û{Gí©ô;â§ý@js·1!>'þߣL E—Øà&UîÞIe:H {ÁV9 €i€çžDy¶ð Ö¬œ2Z‡&nòÎaQ—p¹o¦c±È®v-Úw•°´»¾IÓ÷â[ˆ‰¹p IS7ùÛ« ÀV4>þ©]FßÿD„r’R#GÉù\ürã–þÜvnå{ŠÏ‡óuE£ú꺡ÝOEÑÜNQß’+?mks˜Åz°Á¬¬¼‚°§a¾`ŒÝRÕÓ¦—Å7hëЄ‚ã8èr /-Ž`—ºšØÒpÇL†³Ç[4ÝZF®á9Úwò)v ú†¹,ƒïŽSÑ¡“hËí¦âáv’lºÖÜIÝR.á½À8A­·øž™ó&_åûEñ©Üb{þB´Ã‚WF‘‰ž´!Ê@ އ£ÄN»`;ç4^GÇ™ã%²Ä¯WÞ?“Ú k§,qÉîqÿä4Þc¬‰\ªäAéòVÆaƒ–úœŠÛ„4Ðþj'¹PV½?WÊ6i÷D;š°€X‹?J;Ñ7ÒT4֥Ѣ´5wH#qæRÁ#¶eÉhë_Ñ›²ûE§Ÿu1×§-HØÑ²Ì[Çѧ}UD»¶V4q› Ôœ¶7о*…n­U#-ŸØwà4Ç”n%¢?´êI<Žæw§ç–ï¯dâ@X…GŠT7â,ÒíWI:V}„µ¶{9@VÛº]?Æ‚ÕQáÍ”ºB;86;æî–#ƒ©÷ÓHý ;p0šk¯Ô ô ”µLØT_æÒ¿7 iEÖ±àî0zL·€÷cŸ}BÜٴî‘௑¦¹Àá®UN{Ø1vZÉ·Öþì=Óº22LœÇŽŽ–®,×">KŽ£'ž«;G#é¾q,‹’ÝO‹$¾ø}Í÷޲ÁE£0ÝÏÝg_GÖ> á>ÀhY?ù§“\âä‚tt\H[½ô)õ¸—盜BÙ>Ÿ²ºÏ_¤Ýªì<ž¤|^Îê=‘#õ_’ïCàEÊ4©ßݘϧ֭÷ÇæèUäñtüjór<¢Ö™E¡íwhÚ©=WWÂ,P–ÛjŒ¿”TÕ šåì¤ ò†êIÆÓÞ_Ó$wKY%HÃ#Ãl6ý]$KžŸ|ÄŠ«ŽÃóë@›Õ9½Y0š5ÃPYÑf¡V¤î[d™{0çÑ’{É‘Ò,/F̽v“m‡<­yλÈV[åäJö©ÇÔÄ/hgíÒ®8Íi}™GEïÊ|Û“¡fî`÷ì÷ôô+}e ÿI»¤…Û8šOû:ˆ •ðŶ q-½Oé E lø½_ÝÚuý¿#Wo DÿÅõª¥Í¦]õKÁ‡ôú>ƒöO{ Pv <{+ Ï}öÙÇZ¶liß|ÃÁ o½å~_¥½ºï¾ûfL-l±ÅN‹T&žþyÌvíÚÕÇÒÊômõ6æÛ²+ë“€ç½÷Fp‚@ t¨ïëW (lß¾½Óü}öÙg]åc«­¶r _CÚ¹qŠ¥riÞ¼yξ¬òµÃ;¸üÝqÇîúÀ§wÞyg÷šLL<†É7ß|ÓtÈœìË ôíСCV´ÕÈznÌCQcå Ó³gO{òÉ'ÝOàçm·ÝæLTèÀ9™§P}0X‡â]yå•®Üe3W‡¥xàîpº3f8­Ù½öÚËi9û<’ç“+å$­nÓþO¦(Tž“&MréR9J+X´í¶ÛÖI³ÜekXS€ö›nªÇ ºîz»ílihë'ílñùÀ?ûõ–yáOÜøwR—Ôšj[T¶–Þ÷Aá `¥¾"ðüwßÖär>(M¡†H[Z´uYšÄnw²|C/¬¿ÕÿD3p5æ=_3·¾Oø ïï ã?×w• ×)hå†û==ÙðݳìšùŽ \Ç å oÐk$Â¥4öòÑÓ-_ÿ|ç_/踒÷pŸÎj¼Š Ÿeg@Ø´ó‘´®¯ÓäÒߎºÐ*½4§ýkËS}ô|=šÆ~ølC¾kýW )OPÞ" [šŒxb¥Ù¤É§3Ç¥éÝ™Øðå“Àº\ÿ,M;¾’Ó× ÊGçÔöÃøf‹n lËpì„æS^ÛE ‘ˆ-Ïu á3ÊÕõmÈeó< ~Cáå§CìtZ> ‚ä'µ@P C¹2J©ïSŽÁs˜ð»•÷”þã&ñÜG5ÙÈëb±þ@jû÷"ü:JƒäÒBΘXA HÚÛ2)»”z–©ƒÎèO ,|Â{§Þ¿3#Îd’w“~5çr~:twÝWóSmeS­ÙˆÈ«©ˆZŽÌ2ˆîd«²×——ØZ`Ö¶ÓšôIëé.~2Ku i¬î ”IÉšx»ƒ¿à¾ÐÎi^ÞÔaqNb¼]Ä´X`õ$§ó¹{ßÿçæXx~TÞqÚg1·ÝSZ´>©­öa¢£Ó›++·Áþ1uE(—Ö1)]Iº” íãH[†BÛÛ”èÍnù¹ AÆ—t]—yØm›q€â:>¨éÑÌ­ã?Õ^ãQ“÷õjÝ2w[QOLþlH\š”¶£ÜߦŒ® ìz`"„ʦuâI…´®è~)Ü·_ÜóØ0[ý–­ÓîÖ$ ÚÓÚCJ­*ÎT@IDATî­…{»p¦^N€±Ÿ¤á,°ÇÑr¾Ë¼à1¾×߆•Üc«Ç&ñÞ¶ø6uïÿ‘gK“Ói¶ä×ñ'î•Û#T]̯µ e`Ñ„¡§M›Ø¸%Ú`}M»¼ª›÷]¤°v¡B»€®˜´m `yåé¼·õPv€{–ø’têÐv³? {Ãû›éº¯̱Ì6ŒMÆ&¶ÊÿnÒ~¡aͱúû•CÝ&&ß°!¼¢K1ñPºŒ†¸³}¿Ô†V_o}˜T?ÙÒù™üÒ¶ÆÞ ÅN¢k=påNû.ù¶ý³j} Tt6iÇŠP·×;íDÒý~4Êg#þH2÷è$€^¹ŒÔ=œ0/PÿÏÔ|ʹ 4u±Å|'Ú§ð|cb±÷R¤£ëÕ?õËt\î0Å} ê±bsÊ–úÅä#iN©]w毶;bAëmÊyÞòͬ¢‰ú„] Ðß[ïa‚ …¹”šèhúš¨w¡ŠÇ_!^êXkhá]ý‡¸‰þ.Å·“ÇbíԪɸ‘ñPìVò± ¥ËÉÔ;üàOå Òl´¯Å¤ ^t?àô"Á!r£—[ k¡¶Ôñ,{ ééªáÖ 0¬ uÑœ²}§ônÍ»Çæ–·µ‚²=I)„Ww@빫4ói“ê‹H_®è\ÚÌ%„Q|·Øý‘“¬ml i{Á¥Õ*o±_C“ή_Âj-ïPV©ùMòÂÿb ל]ñ´c|Xó4ŸðÞK䕸êPkørÙ¦ìB]r|›£Y:7ÞÃÄ‚¡­ž!úW«hK|À{’æG2^§Ý.˜Ä»ŽŠ–¾¿Ë,I}÷%¾i^øèP®â4~Úžnóõ€L³ØŽ`lÍø«=–¼N>Ò}«[¢Ãà§ípÛܽãÿ‘m_sý¼f”oÉBÂÂ¥Áÿo“îÉ|n&ù&”Ûpl\»^4ùeõælZx'oïíd6¡ 3K/ßÚVeÅk/ºÏ¥4’Åöõ¾#À”:ZkTh±ëËzϰÃíX #‹4·× †ÛŒ“–àðST¦§FÛêÝlßX_;.Qw¦\¼ú£ÃÕ(`S„#Ûúhꋌ5½jŠ s!e49õ3,µ›üÎÆzb‚õ*ºÉn†ÇwKµâÀÇìÔȹ˜xYßg·¥Ç>)œÆÁaã0¡ò m¬ZÃIÙeöŽ LÕBv ÑWnOãηH~K²£çÑ7ж3´:eý.OA·Œgú&î]Srý,×s%Ÿç¥Ó7Ž÷jø-³®”™nÍ"™Èª91ËÉ=„¦xw÷Ü«‘—d#[$ûö‰QðÊT¶r²Ê®´ñ¦hÕZìAÄýIÁGnáƒ'Ú©…ÿ™ñFüÝ`Z¸_IAÄ;+á{âœDðÅH˜I¤´Z~P$^ÀÖü'ÃäÆY$Ð3RŸàD;Í Êôá­HãnÎ}>¼ØôgY¸*žÉs4i?™N?Z’ë^B6ÞóD?!Sh1T²lßΠÜÅ_»³°“EÃÓ‹ßÒÕÜ hOê4úøxNþ²âÈy¸ñP‡Ý9"æäëœ@y³Ë07ˆìöÎöçBáãí‚ð1œ7ÓÄ© +Ó‹Ò’7É ãxnÝ.Áyiæ5õVÑÌÓ_¸€*PLZž:ÌK6wåæÛÞõ£ÖAaÚ†/ K™yðkØ* q»ÕšÚê]ºX~AÚu×]M¿ é{Aø' RÛýµ}_ßÚ—•¦­ðXð=}«cÇŽ.Ýý4KkÔ·i _ß}ÂHg&A@Z`©ÀQi“ Lh( RÚ¯¢½öÚËüCÛzõêå´u˜¨-ù‘ölÐÆ®Ü}ÅÁïȽ!R÷Ó !ê]'fç™/^ÙÉ•µÌ&ˆ¶Þzk—ÝËìƒò&jit}ôÑ&íÙƒ:HÞ&@ÿ‚ .p÷O<ñ„» èÀ'~U“лì²Ë\z·Àó¡CÑ@!> î§Yþ2?²AëÖ–„gƒ”kÃX~~é^ßYYڌ施ڕ?†6 óüFŸ?È*wM®¢UŠéatÅXÍ_ÛÛÒ¢ÎqUå¶6mõÝ ­VÇÐz}a}wV!ϸ|cBza‚ƒ\CoéÀ¿) Ôb3Mš!Mtw+÷§O= ×ÊŲâПðåàÚ"œ›?mò ±;rÍÜi±¾[c®GÐÇ2v[1ýð: y“F‘´QgBù›ïYÏUZ;ý+¥»¡Uy­¬×GûÀØ|arµ§ó…ÉuóT¶«óË1^ÊŠûý*e+ÍX­Tû`]n¼z–€¼9eØàÁŠ@‘äK2ùÇõÛ2¢ï6!ÎK)i7g¶ò³åÙM‚ 1QJÅp© º6ê^ Ù´â^ª”¿ºé4û<$Ó “‰W­ìêPÏu¿cö8yÌ|-E™8jg…‰ŒôóPü¬<޾S+û¼`¨í_}¨ïà®:àÏã`cÙSzu@@–ø½£Ñd[1ýAÍJöË ª>ù&}©[&à Ðù)OµB¬‚ö,œhkVŸm}BgpˆÚ4üZØ ‰E€0ÕLáC¶ <åÓ5ô=âá3àÜ6j›}˜@u î?‹l`"·Ú Ò÷šNš=Bûu#@â&ûh9?ß#x`'Iµ]I]N‘9î^túÚEC¸k¯¯ã¹—mÅn¢E¶‹Z„˜‘x÷%´ç®žúï“áõíá’Ûm\eG¾sïHýnÅu½HsÖÛ‘ Ò=Nij»†–ÚkêO¤ý[ù£æ™ø†–·²öáímHñı>q‹-ì)Îf©h®Íî‚, HŽ<‘1îÚõÅïØ‚ŠY4¸Àn)™acªá›êÞ¶k¤•Æ£Ö’ô_ƒ&¤H†‘^ª~žœê™±9MÓIã=h>/Ò\ÖÔ-ìo@¹÷ÑŽšÕh+´ÝÛqPôËÖ.ú EãÒÿLC q±-FãÛȶVÌâÁ›ŒogÊtr×êôñj¢5hk—bÚA £ì¼~‹Í×ÞÚùPù6ZÛ§¶÷÷שDHÚt†Hg9¼åÑ÷ÆßH9I¿u§øÓ?¸³ñ¯Ó³ sˆés©*×^§¤ü°óþOZŸýâæ‡æ;mdƳ¼Tsl–ó@ä ªÈÑ0êùxÀOíªkˆ2þœ¥ º~Ê¥…,önŸÀ.V\ëÙEHIØ›¿Þ©wÒz¼6™ƒvódh-û”q¬}ÁVÂŽ‹P EøEùõ—Ø=‰eìjjï¤Ó} šÝ“£kb|5Ûƒþe|蛿֙¯P ~¯®Íu‘?Èz}Ñ×ÎE}B?nIï 7ûn|O&ºÜ‚‚ÿn¾+2ÇNÑ+iCô}RP}$¿É²7Úoá8ú‹£0Sò;mbu'×ëÌZªÁRÜšÁW“Þúh=碯 eÑN4gþkMÜ›À= ]ˆ¼5/{³¢^xÁ]ƒò¨AÿÇ} 6÷¿# ¶/¼ð¿µÌ€”]â MY?Òêêj^¯³Nz`ñ=Vp2dHÐ/øŸM¿@yéÒô]É>²ó è¾¢w俪ӯtÈ<ƒLÛÆ¤åÏ„iLúÿL¼ÿ©ïÛ›:Ð0?u¶ëq'MbétÖwÞý¦us›ç &Ô ÚP¿U'ðÿBm-Ñ6à?KÒ&ý3€_c¿§Ö‡fM”ûæ¿.œì€5+úr)žW1KÃtE¤»VDöBoJyK;MÔŒŸìR 0ù2eô+î+Ò:߉´êÐŒgÑ^ÿ¤Õm»ÖӤöDù·à§å¹hÕh+ÖGòWÛÏØM< ×W\|KZ¤’†ç‘ÿ[ƒYÝ~ÀQ…>µäæg~LòZj$.¶‹˜8øZ¢þ´Et•Päj>~Q…²œ˜ÃÇ!üiòû‰çÚ¡”ëÐŒIÃ3[P®õÔÁ5>À.WM½$âgQh€ËÖœþh¶9šèob \WúÕ ûŸ¼ÏMŸ&Wé<7¯L<`«Q—IãüõLm"”–2‘× ö6Ÿ_{~_ðÃÖ+Å%L][‰ÝÇû'âw~þ•'GIÛ"´!±ñŒþ‰•Î&ß]zßñ‚…º3™x¯fM=ü(¼køt€ˆ]øõ訩m]ø°õc{râA/¶¼ìó×h™ô&mŽíJø-õ¦÷zú¯¶j[nî—×Åÿ1ÚÑ©üÚv(3ÒÖþÊåó<|ýPEñ´¥y"vÏ«‘΋|÷„¿Æò\—TW:èð ž'ÚV¼Œ!hÀ¨mjÿÀ€MµzßuãÉu‘íì+Š>·32*±;wÚ{Ý¢'X¯êAÄ•\ 7$í‚è5vsü²¬××àiC€¢Ï¥Yš|ß6C³:3& â@ŸZ‰†ܶ„Ï¥‘ã÷)APÝÀßüŸÀ„m[vguà˜ÀcêÐÙõÀ$À¡ø®k¹ÏYÍTÊî,Û|V0«ø{)”²}°Ïi„×!wð}ÓØŽ^ß”ò^³jÚ§ bû{•×S^—0†ÂÿÕOØehÓ¶NѶ3¥ßÓ…4Ë|ƒ8ñIÜOˆsH|&iW_àÑúhdèQ (¥Š’ŠmˆâÑ5V\0tyÎîoº®@cøêHìüB^¬‰½I¤höÊò5í˜<œüš§e”Ó(¾Ý‰z8ˆ8) =š`sÀ|i:·òT;8ÚMߎ_¾>€sg{>ô)üô ÑvvHé4†ò9|{‰ZB /Çßr;vFìÏáaäd„—fK’ÏYk@ïYîÉ.@‹W©X¬SôzûDöYµ0Pô«O«8“z†o?x-Q Ÿd³K.±v,œT~ÿ´°£IS¨à›ÂBÀA57Á£s Uô%_3ø}`ÇEÎ`ùçÛ!ñ½§¾A‡ZE»S¶ô¡‰§ìõÅî=Óñ=¿íD}¾B<ônå›nÉW!IkÁDëT}²¥^»ÓÎÊ÷$ÜÛòôH B‘í‰J}í&´ þO{~ZäÕn"úR4”§$oÅVÿ;¾Í•9Ò±ý©‹ó^£ýl €¶ˆSÑâ»(¹Ø®dáY@û»X”Y¬ eã?[Íõ©,¦é ƒcF±X5Üï/Û¨äG¤w2`Ä6YiQŸþsš<-¹ÌD.¢]°Ø°šµ3Ý‚fµíUøšuŠ?hï&îÅ.|±MAr$ D'ÿJ´AŒ;óÄϘr9íõ9ÕGØÅ‘ýí€p/Êê8Û1J Dãèi6]zkRn¡ÄäÐgAP¿ªRóᇷ(çoˆk;+ÀªŠíêãp´—ϧ7¿+´…í“|ÖŽLL'o/Ž~,½üc×”UíN·VÔç hû_|²›½šÍN–bv!”¡aù &a…ÃÍ.K>Êß<A¾J܃Ç|§åy rwPÌóFÆI ßFG³Ÿò»Û.ì^`ö@èÚ‰6²7þcêÒŒÂM¨ë ’ŒvJ¦ç'²Ø)€.%Ö ×%Lt¡NNÌõÉó¼6n‹ò¸ÿ 'ÙÖ¢dê½?ñrÝWdv£„òÍ>Ä®®Xš·‹_°Ý¦fì>¯NH™·ôÌ“ÔyÍ9ô¤üa¡sÓP…MH.wJ 3YhÏ"F>%íxLò¯5çJ-ˆ´[ƒí\ÆÑ},P¨ï”y¾ךè­ÔûÛÈ’³PRÍBuœC˜›¢T±(é°uHcŸ0ãbšt`Ü/þWÉS’Ûãܧ"WÐoÊ6æú,íâ \éãV@ê7dêd-âþ]aÝâØc(„1½µµ–ÈØì•·úžÃýà[úŒx99™øÚ¯?·tŽyÿPZ$®&áý¬/rÀŽœM¤òƒ*ÿ—dCšºÒpÎú* Òð]YÐwUf} ^.)­}õžï[YÐ7÷{«âYéP9þ+@ß|é­[ŠùBýkܤ‰þW¨5D.ÛÛ&ðLCè«°õ¾òkô­û%ÆMÞ‘°°"Ò„áÿ:ýí¿¿B½†þNèûÂ࿚ÖmÄ À[f‚ ¯£†¨ ÏÆ€¾Š#( øqj`>±CÛå×ã[)~e`»"ÐWàØÛ$}õ= ¹ ¯Üõ­\Ð×7ס-RÚB”ú²²Þ ¾˜In±µù&¡Añu$©—üIbVàú®d™+ðïN9j’V/Õ\ã€êm}jè{ˆàš­€w@Þü¤R¼“±³Pi[怾z»>ÐW~AÐWÏY%½ANt¶¯Ú¼ä¸4è»9@Ë}ž»´‰2„@*Jƒ¾i#Þ ÿÍMŸ =®á—|_@_kÏ¡%øº äòi±j0ásÞ‡KKwÛWšŽ +±»–$_Àþ%-ð|>Îå§é…;K otyP«ãœ>ØÎŽPNl¡«CÎ.ãœÓc&š–óªNw ¯ÂþÀϾaå ©€s`’î.¤¯õtÛ‰‰“ìրкš †Ï&Äê@(ÒRLÐBC«¼¾ù ÑuÐdõ(ŸñìmOþY­Kš­Økµ’÷‰“š ‹o© üs/mïÊ£™_~c£±›7™{HÛxtw”ÿJPﵨhe·”¿Aκ ¦¢»º¶¿¿™ì‹vI MǺ5õ0ÌÝ/áïi('ßdp?¦ôÅ}AâJúIòîï•‘4áJî·ÞôQ»~eê%¿§^´Yá3(ϳœf÷”Ÿ¦Îû×\\ú« ¥Œú‡æØ€ww“&Èg‰ëm÷ø³V¡±[Dú /wµ½>@‹³ËˆÖÎP€íY»àV4u?$»»ðÚí4$£ˆº.‡ï•V-2Ö/ïÎý—Äy&é~š]€›Ù4@!+Û)ø½Çï[€œÍmD¨ád³³˜xÃÛY¿RÀ€+Wú>õ¾ Cá¶°è›U  £~§…ã ­mÁsŠI€¥ó«¼ÂÁ†Ù «:ÜŠèN«$}ÑÆµªCù.|ÝŽôðÀÝ:ULŒEléP|r(n_l¸þ­] ŸN*ƒçbûYת{¬M¤q?ggâ~‚øZ¤KˆHƒ¾¿ÆÉ‹ìÊ2†=Ãß±±½‡ÝWÐÛ;œr^@ø‹ÄKÖ“ëU7¦U»©óó¤ÿ ÞéÝ’C¦Òñóü=ÚÞ=x ô*’Ö#´[Œ6…—#Ù?ñBÙækÝb§ØQüïJŸÑ“-åÏßÙ´ :Ô‹6é´`9ðkCâÕ;Ï4¡O§Ýj1êÕª­GÅ6ô»)æ9cìvxø%´i+"§ÙïüÖ/t ïžÄñ’=F˜ ùvWZ÷⊎֞…‡½¤};Àn^¾ÝWß@ß5i̼ñIëh}ŠY‹/ÇGi.dáãÌiÜXÞÅñ·Óžß8í;ƺ ʸlm–jÐŒ[Ï^†óËÿ°"Ûbwù¶™e“¯úb‚9RŸ¤ŽQ-SˆC òev‡÷µJÌ÷@_ù§ž£¹–âu Ô^¡õêà1Ïz ÈÕBHüï¨ïí àp0\ü»uEVp&LdÛ·p:!Ú¸PV2>œÆý¦üh7>è+߄ƺJxu׸݉©©=З6±'˜Z Î"7zm;èW¢Q§ú/å·?Ú¤ÊÒ9>-ƒ†‡jÜó½ÉeÔIó@8ÐWŽ‹3¥úZÛ0q‹mîÏærÚ´`°U]hF÷ô}ˆº¥Ì*vñèê°öL«ÝÚ–¸v,×åw× ì;þ¶Ih3£Ôw¸<íåªî6¬h¸½ÅalÓÇ|èËNêÐÐ>¾òNLŽ:þ/â Ò^5§9vÌÉörqÈÎò8 u |¼œü xý.&5—úÙ˜§YÄÃøQÙ›û¤m¬Å›Ï{ƒm2»Î*ºÚî*>Æž‹žˆ?¼ëÌç,ã^' À]ì´sƒû3Ÿzìæ—‡¾ /OÙ¡«}lßMe1°½]M™_–.÷Úw‚wÕ<Ðh¡ÓIËΔUcçÒPÔA®©ùVj¿1®.&OAÚ€ùÓs‘¦õJ{ë2ÛlÉ.˜ ž¨‘¿J2 }§N#ƒä¿W˜=;5²H‹°!ƒÄ¼Ä Æf\ÔãÝÈŽ$q©gr‰ö•EkÚÚѸ¬™åšyÐî˜Ð&™Ç•¹™ÂB»v'æR*r&éN÷ZÔ)z†oôð{ÿLð6”ß>ì4øŠ>äæ=þéK QèÛ‡ï= «CYO}çvºMPÛ‡¶N.²¦9;u6©¼#%n‰_PîøÅŽÐè{<»½záv÷_PwgR翲¸²6»Ý¦¦‘jˆ÷g~ÒšîÁó¤Sô(}êÑ¼ß ""iùû¤¹â-¡5Mâ>È÷ÏKÞäÚ ‹ ›ý}—Î>ùˆÅòßýˆÑ˜i¡¼yõj, ù.d8KŽ£ ¤`ŽtØäüt;ñÝò]o¢ìV}$^_ÇH»·¦/šÐU®'Ìþ¿n« þ*à¶ ’°ÒQ|é2yÞl×è.8ÏËÿ œêvu>Ñ íÿ1ôa– ½ú“•àUèoÜ´™£žWg5À3zgeÊ"‘çr«ÊãtzžÁjËÿÝY}vaƒeQßý£ V×h²ÚinŠ®°%{²Ÿô&+’ÞY$;JÈÿSgRÜ•4Ê´‚O²AL룩Y ~øÞŽ/÷ª‡;Ÿ…ŸViÛÎ Ê}fz ~p<—tÚmÔ” ®ížDíÄÿHÒz ŽêQ6v*ùv^]@þh$jEt†H6Z/wȺoÔwh^0dð ¹wCó$h?Li>mF Z+Cz¯_h—¬W&K.\@Ú|×SÝœS¾»ÔÓ/(©¬ eëµÑr'ûbÇ»iÞ‚,wÿAà¹h9¿àv}ç˜çLÈ|ß¡Xü~“–f6Á×¾¦^Ð#¦‰³×’÷çz0b¬ÉÞÊ‹4Ó$;Âj×ò«Ca&RN›±ŽOÆÁµmY¯CL<À)u˜ìºÉ-Ü ¯Â]qoÎ/!¤n˜†$ãx/Ñv5H¶†Ý¡~Ü+g½à“˜û Õ!:ÉbOþñ´—å‘›ÇÉ«@8Õoò>{ÿ9ôIº#@>ÜŸ²ÛÁ½ëþ°pq¤lSÂk»ãêãÑ¢÷&š¨{Ó].È¢à‰Øê1R‘_èf¡±{>æâØõõF?ºçx—‰Æ`Ò·‘Ó‰­v ÿ¸iAÈo‡çr ËO±‹]ü¹´` Å G€žVÞ™‰6Àät»ŽÉ5¥œE#’Kí9¥ß žÆ½ÈÚÅGÒçôÏ‘ëÉÏAÔÏC„ ö˜YQ¥(OÊë*ú³ôý¢+8½{s€ïm@Å‘ä}7]“¿lköŒ¯]Á;m±p»“!¤ýö²[xL\J¼À2Ò”‡Ú°uüGݘŽîfMe€OæržH|Œ‡ß“ühÅᣭUòI;25‰[±¨W[dƒ]‹&Ïmê *Hƒ°„ȱ.íóBífÆ¢½÷~À‹mc€óÎM’ýŸpÛ‘Àôº/7U¤È¾.‹J¢>:dMa´%¼wMÚt°žÚN)ï— ù“˜Ë;¼_q¢}XòžíÍáFVÖ^š‚ßlÞÛ æS«æð$+™貽ˣ4Š7ª8Ç}ÇHcOÄ[8 d.}Õ¾\í+¸(†ÑýÝÉç‹]HNŠˆŸm·é¼øPâ›æ¥Åù•óW¼[H´º ,¥%*;Çá„¿´Hºîw¡¿,L~~#oà¿>[NK­(Yb³(ãñ•w,éТÎhMmEy[A䉸O/þ-©ìOZw%Î=‰¯ÚšGç~ ÷a{Tu°uêNj/±sÑ‚5Ì€ü´¼•íTy€^Êve‹ïz ä kɔнÅÔÔRl7 {Ï¥ä'Ämò“ པ&ŸàÇ ñ©Iå—°ø‘$ÏÄX} !ˆ Ù½Êhÿ´ÁšTÚîÅWc/°Žßâ>‚xIFÉ–«4# ŽâZ颞O:¶ÇŽd ö˜G¥^±Õ«FcNöÞÙûtÙÖ Mììø<[¿ø3û[ËÍãïPþƒÐLlkg•wàüK¡6€ì0ËÐÔî%稃S”±üAyóQþO¶JÂÒ«Ò¡öSqߎ0z&M´ÃlT÷u·µJ¾°f€!+Îe¥Ø:ÐÞš„v§÷@F ÙºÒ(%Ÿ‡¿Ÿ³oK‰3âñ9ãë¹Ø)¸_Æó´•Ñ„%¼+ø¯¹úÄ} ý)c°¾DÍâ=Öžíítp‹MËpß ÷­ü)ÛG ÿ9ϪÌ<”z™t ¶7jî…‡ç¥lŒëæö1‹3Y„¦¬KCø\[0mã€èE´ߎ¬g-1a¥ô±Q\]OãüeSö^ú‘äŒ[yo]¥'L{aw{¦~D±Ìn‹îO}œl¯ç8À`+ïMÞŸâÍ&üà•èYÈiëbîãkL]b{«¿)b¼)y‹øN%Ì–ÄI9ûµl¾v$^Ä÷xg9qò꾘ÛÂæ( ]Ë6åÇ7# {nm\_»?}ä ¤öþ©Õ,^1 wÌ@1žŸzß­m Àþ€¾çÒŠÖá÷q“×ì¼’‘66rO¤Y‹mЖHÆÄ©cÙnf1ê&,бl7m¥Ÿà>ÈÕ§Å&Øðæ¡RJ¦9o¶Ô뎧 üŽ«Ì ýô+Àÿ^(Ó¡ àÛo¸_“%ø —8°N𠨔_o¶ñ»ÃÛ4²iA%I&Ö¹->Ê?³#se2"H?§Þµ),hÀÉ9T螥њ+?æ$ý´a-Z®ˆÐ*õ¤Â²CêÝÄøµ‚ãZ@|P â €)¿éÔ_ ý¦ëéØÎ¸ cé îu£]ÿûaX@µäDÿ ~éÃ}žò® ‘¹»˜¾ð1Ê!H÷Ã#w%î§ÿ¼Å9ßÊÜd“Jx õiæJµg@t£´£¿¯aü_ 3 ˃áçÐøáŠèM,à¯ÅVÔÉE½Dñ陲htª‰½ÚÑÙùÎA®M6µû Ÿc!-&ú»…½‘Ⱦ‹±Øy7‘ôÆ|d*|qã…(Nú§í‡LþïM$¯Ïã/ c_qä.xý;´tu‰ÊBç[ŒKmHS*pŸs+™~ÕóÓ/ ‘@æIðÀ´£mS’OÃÌû*Ôš=L©dsvÚ#p9>’æõ7+ò1ÝøËÔr5O  !à­/¬ßË}ÊK´"pHï¨ãöéÏä©¶TýX²¯ˆ¶ÂO]£æÙáþ¿?Ê UéP…ŽðÛ0F…B¼L \Þ²',pbÍÜd¤¾˜²ÝW“– ¤ NèõCÔ^„ŸV¤ ¬Ð1NÌ£?<ä©6VïîÇ´ƒlÑút"‚Ä ôèD2ï0==©‘Ðs4þïGš¦z”]Í[ñ¿gÀ=A> ·u¬^ Ô³‹œ?>¨, |´£Ìt!ãéY=¢ÿfðÚ1øàµ»­¢i‰¾J°ÍCÊë®é~$×{:ñLñ'‡¥KÐ&ëYàйI˜–-¶m¡ÉxR:mDYIó¤>>Q´lB³}ȇ4 ¾£ K>͉w©ÇçYñíÆ{+<ífª.|St4Û&9ÎщƒÁ^Ösµš£¸‘x¸ cÂî`š(A¶IˆÑ-1)Èlo„Pµë¼š¼ÎŽgö¤#óbú¦;mÅÛî^ëÃ4¾d“|&íȤ¯æîýº e:  ~X™¦y• öõÔh.ùyÍ>™Q1[¼jEK™Ôû}ÆfÉóKm´>ÄGÁ´oH]¾fÇ¡ ¡ƒ‹’÷0yyWQ;jV}ˆ²=U´_ì<þ¦ì‚4 `á½á­=œŸ%®õ®þßÈö{h'ë€ößú”vØ£3ÀD ŸBò,`U9ÄNãP©Î4C=“6Ù9À©(ùˆW'ðî¶Þ»¹ \¾ãZýB{€-¸”rU†ÀÌ|\¡þ'¦¾p|(q3ß”¬8áå=Ë?ý°QøxÒs“ÿɸÐÖ˜H9 ‚Éß0ÀåG9\§wjg›Ë¡a:è¦ØdkÆö~mu}9ºnËÙ¨_j_aOx笉¤-¯ß·ž@íN±¯>†±h&.iJé»&±€##íìíÕ¾´ááîÔrÏ“¿¥Ô'ÀÈïô 5J'Àå´IÏN¾Cþ¦ÙFh˧ÐÞ’Í\+!ï¤iN¨­=¹¼ù!OÔ ÜJyÀ"&¤Òâ n¬W ØŠÚêÚ*,^Ôr$O$À1Â7‘,¼«sê’˜ÍxFU•oåa|óiÀ±iÜ_hëjß\ü!áÔ ¯iŠn´§’ŸbÏï={2¼‹Â­ˆvü=Ëö©"»²ä{§ä^Ì À!â/œd»Æ?g¢½ ³ wÌ19/ëÈò®„Ô÷jk?ö !¾K¾¨s'?¢ÍëÛc^ ÀÕÊ»‘~&š•WÁÏ›Y•ò[:ßæ ðFƒNZÎC‹®EsЉ®Ÿ¨1€Ù(^£p8nðcÁµ¶ÀÀ¾gØÆ´…~ øXTqž=•Z»ÄûZZ©ÞÇmIì üN¶+ÏcR>Ç}Úû”ßã;ßê[å{1qGäv¬@cndü¼U7á–Îð*xœCä°­v¯{@aGÅ×¹ƒß ›ÐVò¤ /?Ý.TdÒ`-~ÌBe—¡¹nô5lg&Ê©|ó ´—Ûãæm榼uø¼‹Nz,UµòPgÛ¾øe[…}õk8çòݬÓ£Š?!­«ÙÊôHrŒmV<تÑ(väl½¶²±@umH@ÊY‰…v·Â'¨+êÁR|³fŒ]Yx@Ä‚„•·° «.· 输)—ùp;m–良óu$-fŒzx.,÷®¡ì©«Ð†´YÚ‰¾íÆÆ_m+úzC3Ûb]ùu hÞ=²?áWcÛûºÖ/r}ÍYø_µ}µßhÖ¢7ãÖÅF”hq4€¥‰=™vº/£åœêí›êóO¨Û4õBNY£l#ìŽs(«vN¨’þâUåÿ œŠì¶ê©6¹„2õäyÜá“ÔÊäu&]â´r-r i‰åôðêÖ ùU´vŒ?´Ö´eÊH ONÞ*·¦áöXáU®×Ó¢º¿˜ë^Rý"ñÁuÖ5tž}£Å/ÆV‹žê¼µ˜«Ý4Ž_ù;þZ„,·.÷Rz˜–šoK›=‡Jy¦Wpæ²½èó9ð.^{ètô`ž†xàÔ[äC½E…O&78GɶGbšÄ«¼ia½–æàN™j´vãr5÷ëðûÙ‘œ?’Û~”¯›+¤žÀ}±óóÿÜžúþqx*Í/¾{îU2½ÀMwØ vc@ýÙQ´™3’Ú{Öb65•E:£ãÁÒ9ß¹c4„RŸÅ}’‘pæA•s·pg{™Ý"§±¸w2˜ä͆v·y/)çKíK/ãä‡Q¦I!Is6Ÿ^eäÛhoÿÑ][„"¶…J¬úFv­…üÙÄÖ¼íGûêPó.ã߯nQG&A¤<ñ$ã×ï|íÚV[4ÚGòΛÔ¨ZÏ¥‹¨‹­í|šÇBÌï´·-‹/+$-¦ºÅŽBÊíÖ'•ÙæôY2õÒ3vªÝîÄrHÂã?Ð ®½)³Y,>ø G~ðëgjKÒwýïõo/b¾ðß‚÷ŠÙæþö"_áæÒ¨‚"D}Ú¯ E¤-C«’d+÷¯’ø,Ý%çJçO²½›K²Ù+Šð úÊÈ Gî®q‚yjž·‹­ÏŠJ•áÓ‘:ÆÿM$`(¯–Ý¿!»0Ù–éK5‰ƒ$fä1ˆÏ ØXÚÑqŽZB@®É|ñœÏ€uÂéÊ’lÈúBF¾Á;ß>Äÿ~íK}Á…¤#—¦e pÚ@“²{#gæ«sÈS–`Khµ¥}Óy”p&:ðÍ·•¦éélOÚ4ÐFæó­_#]Xý‘'Aö ÷¯YÝß:ùµœ )À¬!¢I&4 à ÁŽžBžÈ@mQ_Ò©í_]rG­p—LØ|7ÒL–€ø0"b>R^ߨ§ÍªM/ж{•lËÄñµ~B#Èi(iK^ꓬpÒü¨T~#Õ‘W*‹à¢€¶h‰*Ó“{÷ÀŸï¨¯òä]yߨíËÆíŠ´ð-~/ox)ýóq@þl’°Ëd-/5·ÛeçU¤òêùÖÐ÷¬?jëõÓ,Û*ã©É£žZ¦:7Ú"×µý¹?&6ÉÛÒÏpmQë‡Æg“ؽ@µÂïÙLdûu#´•ŽFˆ—&Ì ´ÉyL? _àú§ÑØ{>+"âhíJ8àé#ÊéÐöØsÝÏ}Û,®i(»MˆOfJþ°TYø„ó×{"Mxd'V€Þ¥5ðDšEÙdãÕ=×Ú¿lÖ–gOÈfÄà‘iøæ¬{ë GêS˜‚j"í“ì¿:­ã5 U·ô ÅÒæÅlÁvk3‹m´z@MY›s¶ˆ=(ÈÅ”+àËq@íV€ÍKF£ÓÞÕˆ.‘mÆZú-}K]•«^êÒIVhOEаK>T %½›0ï>€€¤RÙŸCÝÌ–íFŽ:~©ú\ú“Ï·õ¡žBt—Òt^šB¢’ïø£éøÏ¶[ÆJK+£ nCS§0Óv”Vò-ÒI€wѽö`üaë[õ,§†¿‹G°™G\«8›rgr.Ð&v5iÝ [±#ld”6ÂVýC!4/·NÁZ€$24¤> +â¨ú ܰclñá䩈´ÿ`ƒk.²Ë±é×ðÁMæyᣧSƒ9Á¿²ù'mÉ8ãï}P5ØÎ”ÆzÁ¸K’‚¢Û÷µô½›Ûוà§6¾˜_¡]MYý¨—L}k³á©Jî›.ïd C-þ ZŽ û0Ú—w|ª/¶79äiêþ65¶±µ+:‰"%?¥ïÕ©\—êk„#…ïðŒÄí´+qxkµŒUßP.wR/Ä'mp‰³$ùÖ½[ òÚ†•Ê;ÔWå@Eh-*·Äû8kW~ié…TIÙGwìmëG¶µŸÖ«£âã—Wn±Cš|ÂVW¶Ô–ŒÃíHs=.cí¢¢[ìÖ ñ6„y¥ÏªÆ¹G‹ôdë:éàÿ®É/í©P°!…) Ï[\Q äñ4~”g>¡ß…úúТÝxgo©ÆË?’ŸYsܬriîa»”u¯MZ¾ ?+þµ â@ª ÉÎsâmk«x_ÙÌâ/©ÎÝ(£Ÿ¨ûÞ¤m ë½Õ6-:ÑËî€ÏT¨TïÖ.š®hÀÎfœ(\¶·ípl©pç*¨(Ò8ަîÖhì:¾VXmN³ºžÞ×s,̾’–cñ³|¾t}V ?R+Þ«$^-*„áéõí`wÝpᛚׇ۰{%ÆC4Û/ø”šFœ° E¾ &ú¡íÊ,ܧùð|Ri‘d‘óS;øÈÖƒß ”dgû‚Š-í$$— èÏÊÝX˜®?þ[òt‡½P~–ÍÕ¡y‰¡|ç9Ò­wŸâ™~ Ö Ïà¹ÊÕücáBÌ=¨­-ç÷‡ð )9Ú¢‰Ûy~ÓZÒ7uJ>N<»/l€‰õ+†¦¿£ðÆ”í„Êã¶Ä}ñ†lcâ2@üíY(x4 Ÿ¤ëô6Æÿ6Éú“?ºyñ¸¿Pÿ'Û„&/Rº­ìájµQ(Ò?_ò7ÈÃuŸ8þÈ$‡ËLÓ^ȱRºMe—Íô™ È~pò»¬êXÀé˜í„ü+Å« Q?ƒ"»z âZ„•i€þýjŽw£€Ì7ÈŒƒ3€-jåG‹øK‰à=Úübýs€èIØ)Ðé"ÝaDHSäütˆàmú6ÉŽ>ŽÆÿRúÊ´=x5˜RÞNP7£6 ‰G%ë%mÚ§#™[ù²¾óhëï@,¸þ?Ê9k£Ãcóù Ÿ™ß+ë«ð˜Ì8ÚÔr¿kh}[/æ}+Ï+ÙNÚzt£YÒ^L·%Éa[“7É·")yeÎb¼ðqb2‡÷UgÅs1@½/ceyd*(‹6˜ _K{ŒÅÊ7<ŽJ;j·C͉fcHß9RB O}ùÌüL™V8›Ï~ŽO¥>C{WfÁ´¨¿o·æä“ï1O7™È%buf±dº%Ž’A!cã–˜ï™^n%¹ë<DÞÆÖq­u`Ü)8ŽGúО'Ï,Z߈<,Ò¼lß`{ ¼scvŠ¢,ždŽ•Ä^íåø6tHÒâán9ÉÿÛu@Ú¿“d›Í]u’ÐX»ºÿîô×IxÚáÿKúëË}îâþ4ÕÇ/~„‚ÖA@ü)»[õ½Ýu[:ùRõ DYAÀð}qe‡þkOÿwûk%¶â··ƒDWCþšZìƒ@<4!ÈUã:а?u}mGBÅ’Uð)(9hDMøf:´mèqÁ0J_9ýùtîdy(¢¶ñøÔŒ Š\H‡À¤ Ú›÷_â$ãP`%Zîõêòlµ Œ¦¡TI§¶oK(úÍË-X…~ðûfÒ¹„vþfž¶®4ßw«þ1ÞË/zx _…à?"-Tz®çߎD>ÃmOÓÉF“´ Rß!ZIz š8•ÉûݳþH\ÜœöãOz2î&›ã4UTX4Λÿ©çG»¥šr77 €«´4>…¯ZóQжµL$dø,| L³!8f\/ƒw+3#UÜžÈö:‹Á{Éù}Ay<ç´l~ÊyC››2fd4esüÝ£Ó€-'Ž×x¬²3ç²ù)»ûŠƒvÅoõ‘Êù•ÈA˜x MåEÙíC/E. ÞeL¶$_rõgš 7> ìåNçh v9›sú¯µ0* qÖÁ(Ì´…=¼7íïÞdRïD~Å2‹ßÊxP-&ñaÇ– ·ûËOBøP«y»uwÙ aôÈó–ÁÚ¼³?ùº—÷7gR€¿’{êÅ-ä÷šÄt 1jf—†—Ú±LÊÕ¶ÍZñ£Œ29nÏ=uZiwÂ#L¦¶+xÔ&T_– { €|¿pkž¨²f‘sìmÌ9´X×ärú_im_R²ÈÎÀôA9àùvÁ72‘ü.ñyxŒx¸ÉüÝðµ&*Nêt6eÝ‹òÊKº‘„¤‹oFm‰â™§‘¬Î8Sî'p…—ˆÄ­Ÿ7lë:@XÀ*i²â1„ÝÝR¡8ùŠÁ“U¸Q>åû’Gê¨üDÂÝÎ÷§òý£½Oâwnx[[-¨Á=¤^ó²†i+¹O§Sßä!| q '̳6¨ ‡ Ò܇ÿŽxo½ØîöcYÎòØØ¦?~¬à\êxk¼7"î¯ìöšÉörì`R+°lõj¸’ô$Ú€ä£,·×AqáM1™q­]În×8³2I¾ù&éúýâ«vÀ袪m|¸·u+PEkž)±VU'Yy¸/y¿Ê¦Wö°ÐæmÎÂÒ.‘æÖwY!¶¯G?í6þ$ßï’žù>›øO·ëÐÈ¢„f´íˆÝ×]ìµä<Û#:€²¥†8ªÚ^@Cmì ka,šÕ‹J¿¶ik—…×°û—ïjÃÛcâæ]ÌiÌdç@^ÁuvàvCymßÿºx’6»Åb¼_#ž‘Mú¼Z„ô(áã©‹¨×ƒ ÷<3ŒßðÅ òvnÓüˆáçCàò «"¼©äÒc—x«rø_Ú‚Âkà+ê:¨á½Ñ Y€¨<\Ÿ_ô>‹8'cbvY+{£èUâ>•ïò´ßDÆëä•B3Þ¹ö¸‚ÓÞËY„/e<}¡øSúœo«Wn x¤„|1Ö9“™Ò]‹·Öä§q÷kÊheÏ¥žNǦ ýmä0ü௚Çx.§žhÿ,¦fó^ ]Ê®²ë/±Ëá+µlM çb[VÙ®FÜôÝ™ò'¼€|xÕ8ÜÏÙ)ü=%õ™¥øªK ³ >ñ O_¹³4¦ÁƒÓ]®ƒoê¾cQ9£q‚¹¼P/ÑbCÈcB,®7TP7Þ;ÅN©>Ù¬À.–|”‡´ÀÛyüx4à=Rþ5¦~§RrŠ!zZŸk\} 9@µ "2¿5‚ùÃfÑ ìǰp½Ì™R îôßó®kRöûP¸GíèÒ®Ä ™ÛÀÝY&µ²ßkÜ“æ0"_¦*á¾mY½;¼¿´7tþŠâÚŽ<ªýGˆG’œvüÉLDvÿЇ™Irݨ=ÝM¹¿âר3ž”ªësÛ2bÍ[SŒó­Åe‹;!Oq“V»jºå™¿ÈL†ì{w·¦£Ëº„xò‡Ñ,ÿ¿µÅå6^ûXçN ¡n×U'ؿ͡#‚䌘ðoKÈ?üS~‡U_‚6 #[èwPy©ãÑ©ì"‰7…ü52!·nÕÓ× Öóª¦Uü2ä¬Ìºª³ñ/‰oOjFéÿNzPpOÀ…\*ÅA<2@`e4†„ D´¿D²ç´Ý¥-ö¯!Ìû‚€"—c\+Íá¿“>a¨ îC° Üb¨ïJ³ÖM*¸‚A¾'ÂoC¤ñGöóiüÕ÷ž¶:é`§\’˜'{ɲqå“„wÕÝ"~zCBS®hú+Jód¶Ø#3¾F¯L¼‚X37r’Òètl\ 6¤e/û÷£|‹©ÔJX²å»Âb°žÝ‚"WóýËm'Âfl¥¦ý•÷EÖ“L`Sߪsß~’¨À¤E[Èàû2Á‘M­é${ï0“&ö%´ÞÁ:;䊞dÛYf>T'?£yúýÔû@IDATP„šM›ñ8Û9G§’–ÆðÅΔ‡å’ÍgjÞí2g“–˜îq–¹—×UhS‰‡¤9lCîÃé?²™&L~P?-zFoF¸º‡7¾0õ›Ã{§ÒøšïÁø¼ûN\>u\Ë%4F¼…H}$ïøöØrßQ»–©‹G? æÝÔ¶bôBÛò„gZì[j{«¢ýÚp3ŸJ[½ÐwüÒ$0²i†zUúƒT¯X äÝH1sòµ7gB°  ÀÄø%ð¤AZÁ‘}áß7HçWOÿLmÊý{pÞ­—Þ£îu"½£¢Òá4o†Ïâ½ùüÒ`Dl"ù˜Å3u‹-R í·Ž&gòz[ìËýêLÌð¥a–$»™ƒÐ¬ù©ðy[—>mpõ!îs²ù÷8ß¾–thN€UYô; ñŒíÀÒâHlÎæÐ1§‹Wp§m[Ý{«c6þ•&be›¥]”9È7¦v¤'jÚÜžNŒÃÀ£`sýî¤ÿŸ<Þgë¢Aô6XÝVáÈ‘„) ùWp¥7 ŸD^_ä^£Ë~ô£Lüç–¼ ÈB:Hs³ÈÉö{Ñçge›“¶Ç)Çmxæ[n,UùÒs„¶"êË= ŠišG„/¼—"#|N{P¶ðx*"gÀ“°mФÂÆ?£>°Ï\s8 lÅv=À G}q0ZK{*: ûÃ÷Úë‹c{Ù€š_RþNûSÑ¡s%”c9åP2Õ}v›ª‘öÛï㎟ɯLÄöæHn´ WZàŠCÙ¬G;PÇÛ4ì‘ö¸ª”dißžG%û? ]¤ÝÙ¿°­‡ Ý ì@ÙŒý½¨»µ-‡vÞBÊíR—+Û–¶/Š\™hôZ½É*»tµù¶Œæbò(ú.Ìáh“lãälÛ2q«=†_ŠGðÝIßx6™LÀnr“èf6¢âBa¤~Âä¹øVÒºÜfVà½ÃñŸLÙ¬VvËIü&×Ù:˜%ø%º½ûžÕ¼@ºï°ÛK',nC¿…d«ÅÆéÊw²ßSŸÚ›¥å.¬´~ï«ìkÇ&¾¥>èÛ5L¼O¼R>Ï“Fú µS_Ó+ö ~𙟠ŽlZȈìnWÅùöù$ ¸É–üI¥ Ü7¯bÑbZÓ¯ò4#þ¥µ©ÜÓZûû7)·þÎÏ?c/V\c(ëã*o0˜lGQGÚ’Cµ>µ›"çÙRxˆ"]ðF@ê½kµ*›Yõ @1ã~œÖl 0·nsÂëZÓ´æwf¬–6¶l¶ÒwºƒÞX‚v9Ÿ²_€Û;<Âê-Ñ€£Óx.¥}Ãß•'áñYÆSg#œÆ¢à„ÔÚ¶kñƒ¶‰4êï·çúE&œ1Þõd1£½[÷øMwiÈÞ„|,ÍÒ®Œáí#gÙíÎ4Åï™0Y7Z`I~Àïnœ·ä73ã} sÐB-ŠˈØ$c‘¼nŒfù 'ÓÓêœ6©v;kÇ¡Hüã—ö3,Î>Çx{&eyò–7G ÙS±Ö¯æFLÛüaCØ©³1`mGâ8 ¼wÉ¿>ˉêi‚' h;,|Ф´ÐYzíks‹½)ÏÆÈ±éÈê\RŒ™d6SîÒüþ ÙeSäç¹éñ¦ÎK89Ø·g"/.ä0ºJ®®eç’¤ á»+\ÈGøÖ÷‘³íœ8|œ./2’okþ´mø¾%[Ã/á¶/uðgIùø/­ ¼&P Êú½þ#|þ¿¾+²;ûQ)«(`£+—lD&ïD÷þm.è«§Ð÷äꃾrBŒv ¯îƒñþ ¯¾µªè:×ÿ“ê’É–Vó¾þvþLÀ•¼ žª,máÓ“¾˜Qò¹‹Áñi¥è^Ä¿B@€éÊc3”`Íkbàƒ¾ÒnçÈ•—Øž¤`|¾û[¬^I ¯h€ˆ/DèY‚®è}ÿ𯾠¯œÉÂÊPi=ùYÄúzô½•rPÿÀÔ(#1Õ±Ò(¡Ð?L/ôÅ+úê^‚†ë§c¸óò/w‘V§eŽÁ}åö‘4¤òÀdi«æ#iAß'ÆÄßÚš–!@_ñéFzݤÅóˆu=yuÚÔ©3Á뻎}N'×}åó $cH¦ þ,è«÷úŠ4ýí“Èüí[ú>)ÛE+2²¶^ÍØ£ ¨&::Þ;^¤S…}ÐWÏS¾3yÕÖd@_‘òM`œ'ôŽú¶A¨—µø©mà4ž+ä¶Ûz“@-mÍâAý /ÓÞh,Cëb éŸ0¡’½Ø\Ð78¦e&ü”‡a{SRŸ¡]â@ßÐΔE°~ÄõšK’÷‡6+Qâ°&$kp¤s²Ä³„g´Ë€ž³û+@ žzIß”™¶©jÛ¬w¦ýšÿM°´È Œ››‚açÍiÖÅÄ»˜ÜÑÖï< ¯üNåP¹ è+‡ªþ¸­<ˆ4± ‚Ø‘ÕJô•?å*Ð÷TÂÿÄ óŸe4§j¥aiweû´¶UÞÙ3“õ¦#•øíwZýêMª˜öt°?¯s`Öº˜=Xb³ã¯b9¿o™ìCÛok7Ä&ñ]§Šv¤õ{:Œ‘øûm×—>J.Ì!,Ö”nÆåù~³‰«;WˆÃ¶ÔëüT 0%’f—¶â¦xÏï°U¹Z¡[¨MªîÛÙÜÔÇ€/ëqÿ­mRò¾Ý¦¬Ó¬Å¥yì:{0Öž;HxáÞîÖb7RŸðCÕžÔÙI¸­ë¹K¼j§4˜WÌ;ôÔþ;‰¡¤ç Qk÷~CüÏåݤ-\¾={¾ý ‡’•Ú˜äy žÒ$µ/í¾œŠÊ‹†‘÷½(ÙûR aò'Ên@úÛßðÝ­IÏA\·à×ÖvILÀôÁ™|ã8ÊöN⦼«ÆÓ&àÇÒŸq?|LpqZTˆÊúf»$~ƒÞÃñ+/ñ ê£øFúꃅhß_=Þ.®:Þû>[ý×_v˜Ó`³pÛ3²ëøeÌ jÿèµ<“ÏðÁöCÑóÔÝs€&åLú±ë-ÑÃk¨Óø£Ö›¸ÝJ¦Šoæ­˜]ÍOŽŒUî`Ë«_³“Ã[ÑfÇz ¯<à¿-3myÕãöM\uq /ùψ·8Àݽ8ô7ùîGÙcåç`>i>ï_z"ÿÊojÄ+ïJY¶·¯—w°C;zšX,f8ÍÊÊ2|$ßÙ‡t†×H³O|Òζ’¹¸¬á»ÚMáÍl{¾ä½–ÚòÙ©|ëð è+¿·1‹ð.ׄ]»²—Í-¼`4eç./´žñéÖ§ò>=ˆëN°‘,DzºñÝ©vyÉ|–Gê;ç½ÂãGiIB§VŒ·á1ÚNM_Â?D¾JZ§Sÿñeܨ„פõMßävÄ;÷ÄòË7d®ÃÙCݘ{žÕÇ—Ì3™¸ú4r Ïlgl¾Ëá7ýÁVìrì*·öÊ090o¹…½°·Ùþãq´Y÷µ!݇¹ðÎ6ªî*©@_Ñ¡Ôõ™¾¢# ùŠs—­ñow£MeH2VÁ÷ºR½pÎAÁ§ŽÈKšv#ÌäñvZ|‡ÍÕ«8ôpbJžÕ¼¾ߣ<¢c°Ãý‹í#s'äÛªþIúžç~MÚáÕ\ øûËŸpÈäOÒBûÊ.ˆKB¿¤o×äpo¾q"Ýf‘}‡Æj󲣩‹‹hø;yÆÐz_ßö‹îNÞÖa±£ØV+xзؚ±{$k¬€ŒJ®`LÙyšý›sO½i€ô‡gÒ"Pó êƒVŸM±[x—þ¸jôÕâío|O;üD§‘þ;‘é”?_1Eñ÷M~oó™…ÜDoC}w`L;ð[íÖ åÍŸÝû ÿ¡ïMƒ¾:¨ï<Òó8òšjz(sF)$ìàžŽ¥~_¿M{!¾&¾v+}rß~(=wÜ<c5–ùÄ5äï~É”dÉü ¯|Å‹KÐý;€].ŽØÙ僾zF|ci³®ïp˜¦¿ý#üã±@ߣ8»¢;|ôW¨¾ôWâü}W+vÿ× Qí/5ÿ å³²vgëËó¿“[T!°µS1M­WœêÜ­è¨sIa‚q%xÖ`¦uCãøo¢sjjÅ躉xíµ IûKÀOd#êÿ$ÉnXšZÕÃû°ªê ë~È•»Jh÷I&"l5D÷È­ é03Ù-Z}e^ „½„ÕÌ|g‚²+&A6ßáZÒJöíë÷0©‚ýx&!Ø5çÁÏÑ`À³\[¸ÙùoJ|ôʪNÚ>4AwxSº?à+´NÍÍ%Dvl›®éÊD&Nñ'ùóãÅÇ(Çû…—·‚‡¸Q[ù:ÛfWŸZ{Øc5aã4†Q¯%ófqO:¤*˜~ï#0{º²î¾¤{}k9u_4ÎN*¥œ‹>$šËyïXϾ("o‹ùî,b™´>Ã]þ‡½7¸jÚÿÿ?gzÆDLE¥’1™g™ÉWæ!3!c\„ qQŠL™‰kÈ< I×$])‰îxÜQ¶èÝt ·Žîs›šúC׌g{·øÞöÀ£#ⵤ õº(ì݆»l5N/ÝÔ®,釮æ÷‰ *Öj8tº±Ÿ¶¨­½(ó ʳæR$«#v zi-º+nN -ëpÕ5èß®°±è1>.=ÞÞ˜Õ¥WÏ¡záŒyL/ ½-Œ‡åÃù&Ÿ*/IU 8Š÷P,üP½÷ gâ‘ä3^ü2-=‰xá¥#ynÍð“·Ð¦Ï³}àÓmbgâöaê´­Wž+b” úíôüMÃí¶b>ØvüÆßcJβµµÉ¥ ±Àuå=•´M'¬¤i\ýÁâ‚Â)õÿƒ²Q›¶ªëD,¨ç†{©?ÒXw'AÿÊ%€mí¥’ ìƒE´ÜþJzú7¼cû1–isV›eýRãíúôdÒ7ˆº{w½ ocÒ=0ˆ3óa×’OÒ,Яô¹¥fבϿó=—”|ßGÞ;c¾Å6$¼¡„ñ޽ÊçΪ7ñƒx>~-y½…÷-È/µ,õ&ñ|·´OÙ¤ÙMº¨á^·‚¨$ ¾.S?pïnPSNmõ»(>‚­yœ„H÷qö{ý0Êðî5u/%¤_n˞峇3ZÙ͆®0Ó¶¥nâ^ÆXôl”:S?#)íº¿QæÏ¡7üt²«ìîÇŽ²†öŽQ¬SLjZ(îŒm2@ê†õÏXî2èFŸC½¼Ê\WÔŽ_ Çsú™É‘_(ãk÷Fg/º ns‹m¦’=)£M—3Ÿ‡´ù5HmŽë碕ö]í¶Zê[wD_Wá÷ñnÍi’©‘©Ä! ÓHûרüNøýÉ uË&‘¥s§æÏ/²NºÝåY!A‘ýà!êÙ½w$»ý0óÊË™ñG¡NV}¬9‰›_,ä)þ׊2 4¼u‘½¬ø8 BF Ò0‘wÚ5ô;~ª¢´Ù’ÇÜw£?âë` r–àQÚPêxüJÍ€)fe=®aÓ⇢ öTÖeM—æ…‘Ã~”䨛kx>÷¥®5ŸŸ P\…‘Žß‹/eŽ"¡´¦æYA¼ÒI\Ľ®Â®K¹®â6Ú·g£u ýi˜¤R¡_x¼ ,S^{ >=ÙP±ÔÛEv±«Ï…ÖJ¿$Ž×G8æ&ÊG¤õPϰä­N•äîmøÍ¹‘ê²i³»e6 Íû8³ ¸î¨ˆ7·Ð¼ER´MѪXÜÆ¼x:õ¥tÐñžZ·K%žèTÆÅ³›<Î(ûGs ,$­©vJU¢zJsþ†I—Ç]Ü5|0ÖF½Ÿ#Iý>ŸÈò-5š?‡à½EÁwø³/ýÿ.‹2“àÊgN7ýIïñ-l\E]Á-­uRu,`Þô¥%ÂnäC:Ì$Y;÷–ºt&Kõ¼ÑûK*)l@)놇ûÞ8K¡ É“ m¶‘À¿ú½`#C—6Nt’ží²Ë–Sd“ÀÛ’Ÿ¨(tÖÒO¾ùû,pO÷¡»à¡™õ’ï:ÿ«„ÏIÉk8š÷†³ØF éøõ¶¡/õ7Ž©ðçþt¸2ä›q¼ÐMìK@¹›ôœE½ŒßîL~¢ˆ÷g:É:dvæX,£›[¼Ì`á{/þev"mC›0ùô în ‘*ÀÕ¦©=q_ÐȺmd„Npv’€}%Òµ‘»<¤ =«#œñƒðO>7z®·¤ÿè¬t ý†¤®ÆÒ'e´$â½W²Òô!ÞE®%.$?›ã«©3Ù>-Ë^¶RjÛ=æ•CM&c{¥~áöhì™7ݦW ñµªÝÀîëÕÔS$õ²/qîÇo/úÖìXInZÃá„7&*…~Jº§k6ÀݶüèctS¶ô¥–öÃý~Êx$¨g5• ÖÑ®HM´•júY«º!¸koâë™t1ZtwË” ³Uª¯°H†üVï  pþk$=§ãõ*3§ÏYÁÓóEw±»ËïãÝy*賑ÚÞ ÜÉZòÅLt±˜ÀêÒ7™üüê…Wv%é:û1 ÿâX°cŒ­È%aGÃÒ7luð)ehSp£º!-UZ Ïæò§iÄ€?7>´ÈöÉ’¤>Ý-ÚéIbÇñ;ÝN¯¸ÕþQwex)á=(¶?áï‚ÔõlŽâ¦‘ ŒØå¥êëW&-ëaO»H\O˜°s۽܀ýuÙºâúI˜“@‹tä ¨ÞÐv6ó–'àƒ÷êžÆ9upk©i8ˆâvÛº¬ŸíS2ÚÞªÖy˜:)Õ¾öÀ~ DN¥Ó£¹gø§Œ ߎðüqdÝê/±ËUzßú‹ª' Zw=.jS{®¥ÌÀëø`´D?ølöä ¿ÚŒÓQo‹Ÿ‚ÙtÒìõ-Ì-¡ÌÑiìqé­Öe‡à¾'œ ¸lèÒ÷yÅóeüCJs-íV—¦?㹠߇ÙIð;)ûy˜Ï¦¾à»D> 줖%LäËèç¥úEÒ–$}ÚÐX i Øù zÚ'¦ ãrµsýeú5›%Ý­lÚ Ìa¬f²érLš2ñÁä.ln¬,çxV¼ê‡jiϱ޼ïlj£vˆ-JÁÛRý’¼¿Ô»OÛI_::Éï ÏºF¼àóš8õêÒ¾öx ÐŒ@ñÈ6ÎצéÏÛ Ÿ|[7V‡ê‘¢;»í#ÚyHM LŸˆû#²§)”ÆIÈౚ¾Ö‘:E—¼)îäP¤ëí}6{uªãç¬ó5Ývd{6ë’´óóÒRçDIڞͽ¬$µs?‹üô|êrAúèÛ™?î¤u[QLg’—ǰ_…zî„Û[pÓ“òòÒïyì‚ÙîÞ«þ*¼Ì[Ôí|ü‘3Ь \šptMÎŽ^Lc"aˆOÓÏËâñæÐ¤­ÉK˜˜7Xì\L¾rm`Hôt.kv‘{wé_'÷]𶽿b;^ÕeÆP™¢r§ºKã.ÿ¢mî-·‡cß*í{6úÂ@qAîs5þnBÝŽIÐî’7So8s >üZàá-ÖÀ»°&y˜ø—Š2ö®&Âa^Fz&]ítôJÒöiõ'äS’¸"m²k#^kÀa¬#¤[¼³ ý‰P÷¬¡5 äL'02‡ð$}݉W×'îÓ#G»tiîq:‘ïtˆÄ­KÅHó¶þ”w1Z+~2z×ÿ–‡Ihžï6Êñ 5­ÉW@š?v`n.aQÖÃPiñ:’â…4ŽyÙÁ>OÚižW\íY¡Ëüo]ö(U·1¸¤‰µWàC|ÓŠ6ÇÑ_áæ?„ý+ò¼¤0«–ä  û lºYÆ ¯ÿ¿"‘ZÍô—Ù)6ÝU-)”¥·W×ÃÔ5K_¹Ž×ûT=-®®ÔVd}6~ ‡Ø6‚‡Ý6ŽCO· }¾jø.,·9~Îtë©v?sÝl¡oï»X¼Å\æ”Ñ{¶ºEô?™z2ˆ2er5û“´:Þg.åNÞ÷@Ôâòûž&ÿÐÛ>Õ‘²ÿ§"KAº½U4“§QS›…/ \ wš4hR´:ïR„¿´€Va,ë–™Ú†|ò¨ô5…§sq«Kͤ¢An‡L\~ÃŒ%ÓÓ9Áï3ølDš0Õéwö€Wƒi°ŽKÒ=pÇi,ø5 hU^Æ™xözö$¾bS rÿk¢3+,­«Ë3tœ/t4º´9’ —]Ø½Žª…%À7MÏuaöZ†ú‡»¬ï/ûü}:e— ñ¼èF±©å_»wé.X8ó!€’S7Ê,Lê›NôÛAÎ7’ r“ÿTQ@^Óè(›¢Ä"ÓéVteÐèSŒ:ÓQ¿²À€§ÂÙÕÊÛXÌÓ„< 0¿fzæ«Äà’·ðdRGÀcÇxΣg„½å¿ÇóÝ’_I¾¹ûÒ‚³1ëxùäBä.«ˆžÆ[®¥I2•¥.Z´ÍJ׫W/5.~³ÝÙÀµ3>MÇ@oL]iƒŠÚñ­P>—DúVD¾•3À‹ÝHËÖ-‘Ô“1˜Íà*€c¿Ÿ´ KRe‘Á†GÒ>)y79’Ž;ñ²t{Ä4;º»ÿ^é|H†åèGAiù¤v§žtgI”„~Û\Yƹ'­N—ïÕkÁJÛ¦/:ƒtHÚ?LZ¼ôAñ~X­íT>›QêgÖø¸d _kò±n8²¢Í³@õ-º†ûqõj(jÀí9¦Í†ÃÑá(Õ0¿»0Ìö¨è¤2FE-mkíã™èlUKµæ…¶¿àèjøMeXíúÖã%ÍÄ‚ÒIj—¾DaGÇŽ¶9èùËp9M«Ê×qº Ì/ÞÁïZ`&îã ;:¤~Ñ%›j¸Øn(Òæs”UUÚMŽº9ÜÒoÙ¥[âï$Òp/é¥­Õ \>³ ^pÙØ ×é\¥õUïÈ÷|âØ”¸HKâtGN!=w`NM׬E<kÀêKº^«à—ºl›òv¢@Άƒù]Ý{ÖŽ606ÒÙ‹C ¦8 ÞÛé³ÖŠôåÛ+ÙKR£|ý¥„å¤&óü$H³«CÊÉP2n·UŸhC“×âŽ:_çÜW~'jC`ñŒ—MüžPžøßß^HÜ`„Û.ê>Û&,êFY°¼— ‚² Éçòm\«»Ù½[Éþ^}Æ)³øF˜­N\ŒÉ,"©ÄäÛ¶*`êiR)P:Îk/Õ›S>å¤éÜǬÇØßç¤ÒQÚ‘t*3gøB¼"ª%O+ F~Õ…ô²§¿Ã‘ÜFIãåL„ÇS–Gñ;ÌfWLµ©©9 ó·pÕ?Êï,¨Ö]Cër¡Ú8ª‹~„5š|Án$ó®§í•³™PŸ¸š€H“À_€û gDzØÕéÉè¦,4yûéþ€ü?Æ{5áM"ðLÝ©”=¼PI½éb;6ÚÑö.{7—ág*ãwÄæ!Qû!³¦ŸØ¬<•#þW»ö± mc¨UÆjî«qWÛ‚³Ôöj{Úîu}íLw‘Üdò³ aÑj×§L×! ª´öü 7–ºëȯµ@‚l¤SâzÛ¹˜‹&➥x‚yn‚º¨Ly¼ˆÙ«¸§m¸tÑ6/:@u !~&Ï>UQþ(KòLßàˆ²¶¼!ŒÛöÉîb» Å\æ@\ϲÈ_’>­Ÿy–Và]–*•K½)sè¯uɨTœÅHSwx¼%î_ŽÔY¼®=^ÀG »4¾«íݾ|“*‚Ÿð²Ê(3“ß\mÁXêþþÌíè‰î[B}‘Or¸2ÿÅfº'yªÕµ¬·ccJ±0[êÁÅš—V¤%cë¡€äomê©–ùB;Þ‡&O²ÖÒaMJŸ(ÍÆpkÞéŸ#ëÐa—ÔßEû¡ý‡HýúHèÏäâÀNéo©“°ý™TIñFÌ==çëÑ6_çµÌÖe.ñaùT{Rý«¶tbÔAþnŠ$m¤ä—äæ…ú}qE§éœÚ¤Ïù¦¥~GúÒ“çæB‰Ò¶c}rßyoøj›5‰œ­Ýã÷eÍÜ‹6*æ~wDy”\ï¿7çQCÞEB´}lMŽ¢I>ê{¦<#ð¤•,¶ÝÉfyvS§° ¥Ôw„¿.±¾¶nVèbß”= ŸE(ãXŸý„äì´Ï½R -Rú–=›YÑ:&†/ëÊ œþôý³ð·Ï^i˜úc‹$û‹‘NÀ]Ê8{»æ9¤1Z²E)F»uV‡ø³m$è$äù¤y§%!Z¥t¬ cœVž· ]Û¼~šrO6}ËZ™ë\…þ¨ý§¼zú&u|q-›¥®Õëôßâh¡6`b“¢N*þ-æÒ}}Ixù›CK]e>µásE6~¹K…u—F¾J¸–v?¼y@r¸›g~¥¯øh+øá^H| v9ULfó½ ù؇g¥åP»]Ç-‚à=U~W6²ñ Š™w”XÇFù-ÀC¬ï¥xI$®Ò(Јþw¹[£"ù¯5,¾©ý×fm©®.0Ưj©}æ{Ð0Áòo¥Å¥½¹u¤[ Ü¿¥£+FV³(0S×›ôÍâVÑ•¤ùnOØ…:‚ ¼ý\•A'¼Ð”£5 ëû&Ò¹¼.w+LŒ&dó ÿC¾Ÿbap@h\–dipY›²ëTmn8Ò—ú¸‰!Ò%eGiÁÞ Òîê& bO„QÌ«€bé›Ò¤{Yi5<Î.âYG¢?$ìrÒÓ²KOÜ€2“ü͉"ÞiÂ>§‘iqå=wùHÎ.ÁÊêPÍ/·7I_ëHvX}DS¿ÅÄAú£6Åh?vП(ßDl*™k!¤ãZê·~ŽlÍ_&gHÅäˆeX´ †›rFâMÎ}™˜Éš~±$A/\”q 9P‹þ+ヹ^TNÃÏÆ˜ˆ¯¨ñtÅ]¼“Oñnжè+Ú"²> x©=*~A§§¥ÆÛÙ±-¬$=Í®-=¿mlýªõmr¬ÜÎ)ÿ؆×=_çüfÿÄoœ:‰8˲F4NŽî ø2³Œѱv0iîb÷KšNùGJªCõ@û¡üz&®ç€sÜ@|JÚö¦˜ö(û¶¬xÎIN·/¥6þqAí¶g:eï”à^Ö ~5~oÁß­¤ãx¾)+‘ÒSù/|WmHöGR½x_³Ö”É8Û\—Ñ•`Ã#íª#í à)èÁ•T™ÙFøÿ÷­™›TÛ…H‰i…ùïÄ=ƒôlÀsßðEeP³9éx›<¼O\1¶}O¬ew,êè™GW[|Œ4M Çìã²G¬3ÀtûtÆ(=,P7E}j‘^;0ikQñGˆjhƒÔùæ¤qLépë§­#É{|$e3ª»Ùxx­uFk¶u ÿéRv–’üÚV«=ÀŽ%ý;!­x!`ôGlñßê™ 3V è÷.ÇüÉ:µ¨µç(óké—¾.ÙË.Æm¯ÚW‘ güE"¾ úÅ‹|dOfJ­²ò €eÒ]s,¬{$iù†â>Ñ•A9uÓ%}shl ÿ•ØWJ\O‡2å¿&ÆõH„Çá‡ôV àrc¡€¤,iƒ* ¯>ZÞN¾B˜E¦ïË:ɾèdü¨ËÙDº¤ilÐ+WÀóÕð›?n8Úpm¨þ6¡EUY¯ÌdT‘GׇVá¬;vÔ{f¼çÅý]›z§Îêÿæ¾vt ù;ó7–²Y´s¤µ•'"Í™Ìú“z,­1Î$Ó¸5|ªWÒ|Гl„w)E7fùÀð5€C/Ò/ ÒæýSêc#ûÂÓcà×6¸]dwî‡pîë•ïØÔ†Ûí>.?<6óv†nÒèfV,ôôI‡Âs¯^ÒÛ…~â6Ò"€:éúÑò?à2Áx ¶¼ÏIô³é©glwÆ‘`©3wD¬3m{/ÌgãV—üJ…˜Æá±mO·Éõ ÇÛ|›ã½GzÙ^±]ÑÑ~‡];ƒ:½6@{цµô’‹¢'Ð[–3·Xd/¢Û¸IŠö÷ú“ª–8Á“D]»™ y«üÐzÖ²½Ñ­{•Òiy‚Ñ|ûòÿ°YìT”  ߣ(û1{†O•‘ÚìמUS£Ç¡‡þ{šrnJk®´› $d޾õC’Û£éG=Ãè9¶}Ä‘õlÓhW{=sí ¸[‡íæÔñp_l{ྠÑJÛŽ9ƬPp…¯â‘uÑ'}_Ô³iÍ|Nzs%az`c{ê{L he^.‚OÅÿ9nç¾øúhøíˆ‚1Ûó§úÐiÓ™öD¯iÏÌ vf9"¦ô ¹Ïæ¾Å.cáPÈfŸr,ïâÌ*^(€?ÞRË“pUÙKC’ÎRΑ+é¶} ɇ¥ e}…Ò‘7‘Í|HÙLÌÅ»ˆ…M¯ÜwèmPf5ü6æ~‡S›ºE—Àº ð_ŒzëVжBAù¯³ìɺ>ÔO˜¦ä>š}åD­Ç£ YˆtÚHìd µxöm3_“¿ÉîÛýi”{Ï<Ŭö¾½qC ïë´·ù,8Ö¢ž¶ä'àZ¿;Yø-@æ¨ ‹½´"©¬xœÅÄ1HsvE¢Dª ²Ï’H F©9Œ¿€M$ºÅûŽê$܉¸Œ¾æ’‘`Е#›ñFOŠäÞf©—xЩ9˜¼R~’®ói êN,Ê%ùÓNg#¹ŸÖ2a³àYÛ«Åo¤åUÊûÜømàÌ*qÝ™õî\và |"‰ßú>¸ã[’,ô¬a<«-úIëŠè¿¨ÙΫ‡ Â/¹7*WÍi*ù?‡5ãż3¦X[íßèzð Eöf¡<`¾3ñ“ÎÄ>¸yφ'_ôûÚ¶®^ÀåFÀÕÙ|û¦î^›¸] üÏ{"y9ž04þˆç‡–\„ òCY´åØõÆ€¡©Ô+¶]ýO¦®z7Òr?Š4æÛ jâNߊ[Æ-Ô^8•2èÿ^Ðù¾|¬üÀJ-K.±·[Ünƒb+“õøIþèŸD¥ý óJÓ{–×Þ=µØUPVèìõˆ2)Þ©ÞÌ^a3 [ÍÕN*Võ¾60‚Sý…Ê úJuâf›i'”ÝEúv%Ío[§èžE™ 4ŠnÁv$URýÐpéb×Ǧ-‰.¡}$ß$]‡ðN{Œo°‰²{|?‹¢âbVÕA.¤JüX=n£HïhžÔ‹ÊDº’jæß”Ø×†W޵wÊ·¾‰#ˆ¿ÚÎIOµ}cí͹ÜýÈǬ0߆–žk#‘.š»=R ØDÙÇ>jQMþ'ÆEl~ÉC¤{.e 2KÞ†”<[»=êî°—ê¶w­fÛ,(µÉõ{ãæxf›ÛÙŽE5Í( ¼ U)u÷‘§}í_ñŽðï @}¡“ÜÍÿq+]Íá¾×o[ lm ¬Ð×àéðMß`U©W=ÐW›5E(#I{‹®µ‘ð=*ý"˜gi-Êûe` ÆìÆJ‹uä·.?Ò݉ç ü|ŠÂ?jkêK3ÿââ´‰€¾‡ßž™œÑ÷¬íņÄQ‚ýÁ"¶‹Í¤þrzp%­¨Kµ<Ð%·‘— 6.yeè‹ úLØ~©Ä?Z;Øï‘mmMÔxœíF<}qñ“œA‹¼‡{K:å 9Ð×½á®æLÞJh¦!íC›qk#Ü A‚«\¿ò•K{;b¸9]e§NêÔîúÍ}ï¥ðá¯zœ4¤‡x¯ïÅæûk)é¯.FêéV%í’Ì+S×d¨|v"mwh,§•8UNE@_y˜Íï æ;N¥U þŒ_)c'€p%›è+3ñçf”t=ó’çòÇ…/¯_z‘ô¾éCïÛ“®@’ÚsáýÕ<·©_{$r‚½äŸÒ‘›SQQô'šGå6^k/ÍæøüŸ›ÿš(Îfÿ5Éo2¡…Àe“ÿ¤EXåATSeš®—ÿÞçŠtÓóöürñ+Íapõ» Ó–Žu–ß!©ó_j(ù aSäM­š²Í7¸|¹&Pøâ™|Wù_ͽêç|oÿQ_… ëâ'ùI² ¿÷3uÑ2=‹]̦%Axhš ù& ¾ •ñK•.'’š‹0åë; ÛxïW°}‡Z}ÇqoéÜ•._Iö’.þ9)5ßR&Lc³¤ãÅÚ}*Rulª0E2}>4ð;CþlÍ$ª¹$~”²X®T>m©ƒ“~²b¨2(v±]1÷a3ç7² “¼žÎX’‰ç0©di€g„½4ï=u9ë"™PÞ¦“°ùÑ #dy¹¿ÒѦëX€5‡Š—PcŸ’>Ò‚¯ºxsvš|ޤENt'¹eŸ‘x]Õ>A* zµ7s’ sÇæCuaÇŽñ1É¥ÎmE ¹jæ«&Èô(¢mÙ,´c'qñ¹3ßM¾¶ð$áّߘOwvú#é«Íj•§\Ï¿3íz-&ìZôíÃ;Lš˜_ÏBù Ô0¨-jca'êRàhMâо¹þÁÄ7ó¾óªã‘ÛúÒÜÓR—`6%dè½»Ÿ¾œÑ´té÷ˆ–Zñ ¯q;`â`*èL]§ö"ë/‰:IÇOÅž”ùqËßÍéE¦ JÈÏIÜdå=Eõ§ÁÃïá¶Š'þ^†‰#صäW?GH®9³áŒœ«ÔDÞ?w'z¤Û» žr–¼•ÞÄŸ\];@>: çĽwà[ ~9¼0M•W3«Ú ±ì%@Š/S£­´d8½)ùÐ&™TÏôøù©Ìp¾Š#ÁàƒêúYYdÊc¤ 2üçâÈ úÔ¡Á;Ú¼‰ßJ¹³@Ï#@8ùGâïRT3ÌÌößþ¢FÛøéò§ÄÀN½½$°Ž2.cáÏ%s¢ ä¿W¯K¸#< 3 ( /Yu/gŸ»qŽ©o¼b_òŠF9u'@R aÍÉ„±?nnÞ6«¥µ¤[klAH·º‹ÜÜ|‡RGÃY*[|oÜlFœ›’Þc¨£A˜—!é¹q®ÌÂ2émÞ4\`ì ,à„Šá'úÉ>¡èK›#…ÖÕ"{ÖbÓ+§ººû•ö6•—HžÈÒõÂÜ ~lxMÅÝösü.2ΓöUþwÙêŠeZŒ¶-¹$*K,:Ϭ}Ž‹[s*â’ Öô&ý7àÿéOy¼Á;ãMœ:„Ý`¯ìj¯êö‚ªéikžó2Ù÷éɘ1D§ñµHi-Œ]b¯–ZÝí„w7I:Œ29޲Ü@j¬õ¨ø°‹¼ÓWX9í¯ì\ü×ð£]ÖÂß•31»Ø–?ƒ„g†.ʹú¿Ü¾ñ¥ M¢‹ªŽ²ªôXÒþ»ç¦j[û:õ[(”h‚þP{UÊí9ênzÕž¶^Í)öá¢5ìªê®6,y‘¿èhÏIi½«/"-WØWlH¶‰ rü:] ·ÕðZ¬ƒ]%ÝéI”ÉÌO"G"­ÙËIUÛl”LŒ °Dâz{‡¾} ä‘\ºt/½ÑwM"ÿ¿ŠTe¥ýl£Ô³öcÍöÞ·RÂI‡Ç3ã·Èý “žÎ´÷ÀÈ,¹ ü¶Àï#p`ÄnNþÊŽRmŸ™TKÔïœÀ”Å“FêRÒ§j¤Ú×Y«ºî¾vl_Üоø2d©Òú"|Ý÷ëóíSŒvéˆÛSyªÅâgŸTç wâ‡z/S'~ÚPÙy®Œ$A›DkpùÛyÑÞpw+Û1ºŸ$iï’€²£Ö@-Gm(cÚXfFórÆþ›.Ÿš=ºøÌ®•q$Å¿IGúu·€j—±ll¡|‰ËJÿeC*æ9ÀV Ž$j9µ»È1z„2m©žb“àÛ(igþ »²¡(õ¢õÙ„øõ21.u”¼Œ>¶cz{ºô1Ûik+§Í^ðéÕò/íJÒŽŽKžŽ)ý~úŒ^À|>Lº?Ck†ËçŸ I´fݨ‰I½Ðï–[]Pþr!^\#ëÔmà5ãt¤r4Ÿúz‚±k$q_ŠpL0ßC?P¸Ö„ö9~¸ûчNfs÷.ü!z`Õ^Íߢ,`,n9“2ýÉ×õäõF?¿ù¾ßŸsë.ö‘Þ_©tè#¾zk.IûƒÍ*ÀWú®KN¦L¶Ê ††Z~Ä_wТ(/sÜ%õWu|‹$Q~ªf6AÆ„Áˆ–%ŧr®ÄDúîáf[š«/Ò:Œ¼¨¤o½ ã}?×OVÚ©±«ío¨ž8‹xt*òÊàKŸ¯³/öeC"ºŽö¿ºÓ3M;žjÃÙ’²N콃´³.†+$µ|­3~ÖÆi hCr¤”GxÞ¸ ?a$KÝ ^â¬n¥¿ «ŒÔ¥Ö’Þš¹ÖÑÓóò¤®'㑌Ëp·#©£Øx ëTß©{lDݯ)a‹äЬñm‰Q€¿ûd¿û¢Íôø]èö¨Ð|šõôÿb tÓúJÒ!³<( \.KxõÄ4?”u)_]Pøïl@Í©Ñ?BqãnRS–b¦®sfóK¡¸KM*Š[-³i‹">ÃúD‹X/µQsÊw©ý7yxÜŸD„£ ßú® ¯.µûÄ_Dÿ$©™fôm†)LÃfjZPà‡N²Z¹ŽÒišRÈø;3Y×T©9¤‰Š$ËDÕühéRºÅÑŠ€G¾]oº¼8×Åítûî@7¡ó쵌x˜a=,±Ù£H?» éÕ¥æÐ¢È&ì :ÊRú¶¾õ¥e­p]Ä厊jâßÕtmÊÆá£By ·ß…Ï;à/oÑSh³„o]Š"©Ö±4`ѵ {kuȪٯ½êÿ†´v9UMȹþT°C<¾rÈ6x]Ò »ÂçãnÑShšÿ­›z `@IDAT€OA7š|Å(ÌOîHj¶µ„\g¾µ'Xd5â '¡õ•Óïr}ÝÕµMá=ò–šÁlÃ$þM.¹Ñö_Id˜J_õ¾b€nì[&˹B'm““é 2h(uãŽq7°0ôÛJpk´ÔmÜYP¦J£tEc6€è¶¯^Ȥ»yÜ ¤5,6ÁÿUðQeürK"I¼.7P;ðÃÅîýÑòmFeð $„Ê&û¶kóÔHå —\†IéÈõ‹£¤o8i±8¿½äAœVÝ`Çia8j ,B¥ïRä·Om,iAàŽH7 f£ëu¿^éë÷À!éh,¥€´ñôõn² õ{Ò XÕ@¹N‰rÃk”v\}D¸®i+\vô}ŠxëTàI¬I¶øÖQ¢?ìáöv¬—mÅ8žþ0 ËYvzýî®>ƒØÇ³ð™ÝÑBBíwž¢# ïéâ¤F¬/ ˜3ÈiTôô9v·!È smv:å¹%éùÔùU_§ÅèîÑcÐïJ~£”Mì|g7(CŸ»†âVÏÑ‘2ƒ_3ù1Ѝ¬åÇñ¤±lf”¨sÝÅeû¿‰Þk¢õlwGú{uâ~˜î`Jj8œ=Eƒ_¨Oˆ¤·n0xãºÖŸ¥?!lx§ô]ž]ˆ ¿åoòÞ Ç¤ MCÿ´ë§¥VÀФOãá‘cùýÈ7ù—¤% åî™ð3sµÚr;¬Å/6˜:y›íƒf?•ä)q—ŸÆJ{ ¶=ò¹±ýp_Š_òP~'á]@yó/3¶ƒNÉ÷àûßè¿GÂ] ×à^‰àŽ~äýžÄFö¯ÈwÖ I©¿Õ݆”šÇ7! »/êÞèT»/®¿&®a”?éŽÃ÷: ‚ºÐKSætnƒB|çòº&– Þ Èpé§Ì‚±;õš}œl{dº’…S˜'P—´gK¾‹F[®)Ž·¶yU½mTùÛ£œÅ¸Ê`ÅØŒµêƒøLPöºD,R¢Tÿî€û[Û(6d,ùõž›`FëGy?€9#Ct#žº ·ˆïÄ&ñjø·’ØÞ\ÜÖ?ÞUGsmZâ*@P$½ûV>`OJˆF£ÎaCõ#e´áè¶=|¼bé˨“¦¥Ôee™ö æ¦ùòó±ïFþæÛÍe¬c¬inc±ô\ÍF@<Ø$6{ÚG»Úæ¥#Ø z õƒê!I^'òdÐð<—|>(C¾$í^Õƒ²{ÀR¸ÄUñ—VR–ª耟‡bGØ®%äÛ‘Ò¨•ÉzäOÚãÕª±Þvf¼x—ñA:pYjÃ\9°ô侬@}©VIK@±S(kúƒ,MÂýðìWS/R÷¦u3-Ö]˜ÜŽÒû0Wew 3æû‚þÊ@5=Û­vºÛƒ´C]nú` YÍÉœWÃæº~Üœú•˜ÓH¸@÷”Ìòø“ïó Š®ÿH)¼·Ç˜ xõ¤ sùð{‘ú<¨;ùÙ>ø)qÙ*#é¿U­„Iª6&]¶pó%½½_ÜõˆÈÇ=´û3{%-¡[éƒgªèõ}ª²hf&y‹;»š,–âù9ü½ãcï²Ç|¯šþâ¯K_äÅýiÚø¾ƒr975ß ñ„}¬ÈÇ7¸ïBŠ I`ð›´ï¥¥S‰qO6ÙΦ~Fºvâ…ò|9–ßI„½å¤ù­Hý‹TÚˆ¤O< ©â¸Ðú³Å=ÿI™Ÿà$î.Áuý©l–j¼ús¤Œ°îaÕ`¦XÿÓñ[¬TàÛZu \ªAù?€Êʘ4ƒÂéo†ó<'<ÂÀbžåŸüø«Ó¯!ZCÝò&5w‡®++Í Zñ© Èq‹gçÑÇH¥D1ébÏçÒÿ°ý­[8N-˜'.Fòvécú¿ñ±¼uüêâšpÇýgsõ4“ä> ÊË›ŠñrK"Ñ„D$åñÀE“n{¤µ`^6Zo7²ó¹4’ÆALó[¡OÔŽß÷–û#• Ce`³¸6¸ù³Oí®Ââ}Hh`/¦õ𴘛ÀLzÕšÚפßÓ!¸ÎJ'ÕÝ>h¹*V}Sn§÷PùÏËwšýÒQ𷙬Í,>„gÝ…_$õ©Ëê|éÛ#ý hØÝâÞ߃¿4ñ)$M.tñLpÑ¢{褨K„üýà§U¼¡Ûƒs* C+þ­É^‚_ãØ»×»ðv]üµ&)IM¸–ôS1j¹òwüsY: :9‚gh²ÇW˜ä^õåµïV\~'ÿi§–#ìν—Å:¬{#c-Ø%¡ñu'iAw¬-äjkêJGÐ¼É ‹Ò#ÛR·’£Î¼~“ût[: Cßz¥Å°ðq 4g£)æ:üHWˆ‚¾V s-NÂStIŸŸ oÌŒöt䔂»(ÈÓ¨E@Ž@ì•ä`Ê$cGDzÚ"T,ŒR)9)Ö(ñ«L i#sp¥» &ÆÂ¸f½BG„q8þŸÄ@J€búàçÚ ¨$8ðùŠø­ö~Iž-?[Ú®´×FDs-vímë¡ct<—e ¸ÀàW–vÃ33½¸à%X§f6õžÏð"{ÌØs–ý+À½áàì'(@ÃïLhv%/o…̽Wé"~:ºRÎÆ h)-ÁPž‡‘vp›t?%þiן 3¿˜WÛ~0’¦°ê“¤n©˜h+UmÅÜ/a[žlœ¦2E õ–`=r†$ý-‰8TŽÕu“2xH¿oéHÜM¶ŽúNX§ø`[éæÓö(×nE̓S£¤+Ò‰°žÅÿÌñ‹P,¾ªVåQ£„Rý¹¼É/ xYènoëoÿ(»Ò¾dqà£@€únãW39;Ùÿ î+?§¼î ®öƒO:ÂÅHK"U¾[w¥ð¸.k Î4à ê%œ„séÑÔË>|ÿ‹p:8 ⟥GÚáHãí“úÄÖhxÐFÎd)q§Ç µ’µ©ZÒ_{ aßD~%?—`v7á¬ëy8$` )Tw™’f˜%÷’ŽråÓâ'Ò Ï¨ð+é׆q„µ7ïs0§LJ]ülÕªó _}jL¨‰Vñ3-zÎF•]m÷ÖžÆ÷R.¡zŸ<Þl­ÊN°õØ1 ÄQúÒ»™—¶ØU¤{飼Òð¸Ú£6Zê¯'Ø5°»pú Ò¾ßíµ²ëí¶šì‰ØñO¯àXîë¿Ó4\ñ+z¦O´]yß^ÇØ‘!Ý™ú‹Ÿ‹øY¶mõVvÛDš3Ÿ‹ •Ve§ÚüZâHìŽä-}õ|©.ys3ÿ ò¼)euƒ]•|о†ïGKŠUºZ]¾ÎÀÿ›/`Ëåkþè«Q2(º“½Á†É‘Ä?ˆ¶|I/@›o¬¬úN%|E\^Í.OÂOâàÂ5PKÇxôL»¢â:û¼v$G¥)oÇLÉvIÃgv•¾ØÉv eUÊE|7:)õ‘ð øòGÊnö´—ªö|Ï„î‚ä *¡‹7KÑS)Óá”1aQV–Ä“þ«é¢¬Ä „?–úK‰gýc#Q¸ˆãÜ vªô>/ê„Ïùüà+êÜVÄk™ñ¼³ÖAÝÍ¢Š‡ðÿq>NŸ±óH+yuéd¦'>¦~5\Húvòy{&=ß«ÛþeO³ñRc×ìHÙœj16£ïÐØ”Ä\‰ý ¨AXX|Þ%éûxLÒm!õn‰SÈ׋ÄIÚ\ÚÍz pfæy+3‘¤¢£í—zj ©í æ$Î.z”½”Ø—ý¨/³ÕûÛ¤ qút!ú˧²‹g-y>ùìMܼ'v&Úk —>Áé&ö=DÔ6º°ó$k‹7?#›͇tâ!Eûj‚4OlÆ:»XÙ4ám©ŒUS¹7¢l–5—R%1~œCXâ5º·Æô?à·q™4=ÿ|:ûÅÀQé;]ºnp1X5¾^wÝu¶$ðtIé/¶@M-ÞÿjZéWÕukò°¤Tn—'¦}RòEÞ¢¹xl-)_-^ÿ ZÜÅh "lXÆH—äWöe~;XÆ(šåmy¿ƒ[VuÌè"&!ÅÈf%ê/v´9@Èù¤1|Ëia”»2€¾–¥ºø÷|K½„$ ©‹kŸ½Hÿø‚ôK‘~5íDlH¢ö"áÆ£oÍ‘ž¦åA:JïIàå‡&ÀºÍ·+é &v…€-N¡¿B“„ð#€rë7¤×B+5ÿtÏL®òs´t_o3)—nàÂ^LǤnX—žeq$–Š¢ç°žxÌù ü L–„y ~#0wO-è o…Îs°¼?:$ g|1OÉ ¼-_RIâ6L£élʳ×$¬P#ÏAèCª#æS‚î8Züz[;y7K¯A$­WÅ{dÕNGìêHÛÿæƒMô`èqÈÔ)҉Ͷ²=:ªñ:©j- Hp-üp nEÌXÈH‡¯¤DÃF¶¥¼ÇØQ€e£%í))ËPZùl¹÷×ñR|©!þûUî|Rÿ"—Ó‚6è4€3¥ô%Õr]„¢g±ŸÏXó}è¼À§ç°ä)ïËî³®è±-_TîN ¥¼6)åRN}ÔLÀ<#œäV ðÀ-®¼üìlmlÅÄ?쩆ÈëØ‘ßÈ!ÔÇýÀ"£°£®»–3”à¥øë”qŸèæ”õÖ 6\XÛ ÝšmÕôx»% –Œ XÔ+<”Nz ~‘à*üIu€.¹Káfñ}—ôÝMo߀޿VX¨µt#}GòÌð>áNþèÒ/1 z‹´îa‡EJìaTlQ} µ¡O|§†¤èS‘¶v6*fÖ ¡-\aâG[÷Ò;°„ªÖæÒ¦Ul!‡¿F¶ Ž틊³¹üíÂ=û ÌS.wçÍx(!?£,ÂÑºì(½0ó6ù£½V¯ƒ=|Ôg’ð«>Žø£`®ðŒ­ÌæQ¤á[.=ûÉöAÿM€—Éô£» %:£ÅWèc%ì ʨá¤Fw‚_ʬ}|V6Ø^­h·Ç°Õg£K.³¯Î4¥¶hüÜ`·×?m§$ú§2{>Ù5Ú“ï¡Ó·Û:é{¹/`U'î°ìòøÉä·‰¿ˆgk;œÍ‹‡b¥¶Á½¦ éH¸v—ö1·³:ÚGì-BHhå÷OOÊ¢ßð¨€]J§2¡ôtÞiÇ’,×e6Ÿ:qê`Jòå`ý‡gÒß»ú´ü] 8]FÔ7¼ÜwgÇ´A”“IðÐRð‡£ö¤mgÂ>‹¯µIã½Ä¥ö[‰;Ú üæ6OÊžÅü|¾_óÌék-}ïøÍLÅÍü ³Ö±í·:Ú%¼d¥§\7 @ܽáÉR´¡€â7ÖWüîÆ$ʯ&° =Ûñ>'ô]üUˆÝƒ>ôΜ î\PËHò“³Çà»ÙŒ&½àÏK"”1ãSîøtgêþ}¢ß‡|võMô¡påÐ8Nm¹ AcCLr ¯’¯»ˆçHúÓýIËVÔïÉ\ú·zŧ6+Bÿè.ŠüÞ¤K¾¤ØØl°ÔÓÄ1–¸v" /Q?“ŽŽ¸[™ª ?«î=¼ ÅÙ@ÓµÅy£hô¾ÿÀßôtºÄÈž¸™·uè:àm iEèTèðZ}0öÔ©OÝp÷té@Û½îDû1z~Rßçð„ç$}KË*F’„<Š1¡)Á‘¸u¶«J®µaõ‡t¯a»•õïªø#ÜFŠ»XZÓ™,˜`¥¥…O>iŸ]쀾rYô² _ªã© zVÒ3˜þ“”÷óX!÷ê¿åI¢×ŸäZé}~;œÁ1౨,˜[àke-z‰§µÒïòš†GOeB¼³oôG /’a8¥ö-ý!N:giòª[¯UFçô•>îaAø.5Ñ•„ºz‚Læ û$2Ã>0Þ==2he#9ûý‚Úe„eÂêH|ý…£Sïa°€E?Þ§ñ¼ˆïn,}Ú;º>ý ß»°ÈýŸÅ8ÞméqÞG•z&Iç=ÇB|=€°ûp¿2 o0' fZšø$)(Is4zfúŸÖ9žk3ϱ¨ù;R]SÖtœôKæÈ!óé¼Ò7b °‡\ïYú •D)c–úx#L’&,ú 4†¼§{åøyÌŽ£oý,qR®— –Ôó,ðYÜäü®|!Å4Ö®EðŠBÐW.ê©_mvÔ€vŒ»,tvü6›)`ÂáJ'lØ$°¯ôËOúì$yª ôDžµÎµ–jçGÒ§ïDÞq_v¾is©Gæ)Ï}z2æ›9·쪤>œ1 óMÝâ¨OÃHëÖðO›R³+v_qÚ .{zPlߤ›cøˆÂ‰_ú|Bš¨oZ5'bOúêï£lpãfJâ0zæòaÖ 0~ˆfaAk™J9^ÊïVò0‚6A=ê²8I¾×ö!Üô•ê‰T~1ýŽ­ï‹}˜xÀf 4oos\vBWpeU;Òp<öp‰á/H½®¸2„4Þg}ª.&ž ±?‹´ïßÌÀ]+ÒÊ·6r2Ÿ¶Jo³…%Wî-ätÇᕲ7í9N l[E½Hâ:z,íb/íô•´£/¤½ô B ”c Òl“£›Úi¶‰ÍĬ#ÒU”(æííÞÔ[)õ¹}þu^^kèú&ßvéwñ“pßš_gÊBõXÒw«Ú•?á÷«gL¯š?hz [}¹C 7HÀ˸’oQ /[í¢Ím¤m];,5ÝZT ³ÎõãÇàŸªV¶å¢Uì³Ô‡Ö¾òi{=¼T„ߨ.ŒuµmÈÿDú]Ê3gÚ¢6d~†–EJmÝEýù(!/—Ù®y[ÕïoFæä¾f'T~`Òô·´•Ù4ŽºÏæ’©¶€Ô»•c+”=NÒrùÖŽ]a®-,½Å¥ù’´ø ©j$•ï\OŸ7!=Óös§ à›øÔÏ@ÒA¿šD~G’Ž8é{’ü?¦eÛ‚r8‚õ¨'êTºR®W“pµÀÁí>Ž¥<³”Äm^¨‚'6×'ÍWsá“ÓËJÍþ–jƒ¨KwT?u-aýø({ýþ9ÐW¡Áoœp/¢¦Â2Ÿ’îw¨×é¤k I>†ö}?ö«qªä!O=WdmÌ/vfî4ª~«Û›oHí¿þ6^Ä£7ñìdJޱç+‡˜N@eÉ¥ÿn¾™2<cå >£¿qºÃ9ü…ŸU6¶iR_’à'¢Þ|zQ`z½»¹Ð3Å8(«§±q³±H[\åj*3μ¡#˜Åô¬é ô&qïˆ mO§ œ´¿Òv6e¶?õDi~ú)§JhqÑÀkœAþî{Œq]z[Õ[Ϫ^ƒú!ÌÒÑ„A}õ’ìO³aMø!²!eýK 4Ø~´×éµ[cN¿°¨œ2Û0ÛO=Õ +j9&eF"y}›ÛF½Ëá‘Z›F±¢ÛèþÖ¹Ëý¿îKÛ⌲ ¯ÂääD5¦—gA_xµìcT5hï*„ÿvuw®ãÜEéÇ H -œÞˆö£Ôß–ÏÆvéú> ûñ=$Êoµv`é?©‡;©ËÞÌ/îdóÃ[m©uDì0õ{>é“ÑHð6›Ø@|±ô.JTJ#yÞ6¤®MÔ¿èiRl!ª]æHW”jcäŽðɬϧÏîÏOs®½Q^—¹t"ýÄÓn<L42§9—Vï žä¢½4c¥ÊÑ[Dj—áTÎÁýç¾{ç û§þÁ É›8EÛÎÎW‘. D´õðj7öÐnŠzÓ·´ÇÝhõ½Äüas­o[ʾ n‹ÑŸ}ÞAôo¸-ÁÒ˜et<êÞ½»­·ÞzÙ_ëÖ­í‡~°qãÆÙ‚ šäܹsM í/¿„KðJ¥œŸ¯¾R'íQ‹-¬²²E¶%Yù?úï(]4¶¬ôßT‡óù[†M¿Ï….–½ä¼fÓ .‰RKràÛoà·¾æä«™Au6\»òK à"¸%8k–u0l–ã¥pTxÑÙ’¼jBÐþ ôU\h&ÝêÚ\Òä%¬»¶¹þfáp¿^€÷Âz•¤ "I::˜¼—†údùëï&@ΙíI~ IÑ&/ºJ ò&>Ã䑱sò¼HŸqvR–g³lãIãu,ÚÞeàf)—¥ÔL28†E¬¦L¡¼eú/>] ôÁôj7ÊGàû€Ð¤âIŽªéú€¢$©!J¦~ ÐßúYrú¼RÞ‚Aþ§’÷Íæ&x‹‹SRÞ¦ ‘ÅÚÈr§’§Yißd°ºôCR?(²% Äkò9zê$ñd»8g'Ý´MÐ)N.¢À²,ì¾+–åt5¦¹K;´r# Úà"}…tš‰/÷Kÿ˾žeå Ç›ÀkQXœXŒ’ŸnÁ)ÿµÉJÎk"<†0¤û¬ g†hbÝàìh_û,SfÛâêvìý¥^¤D|¾&¿,ƒ#Í™ÄyvYêUk'‰ÃÔ4À×x²@P¦ãç’–bâý6À @̼ÎB{“ A<Lsß=£[p¹IЗþî-þ£»cîqù¬ø‘߯¬uúÀj#ß8ÿ¥±3m½Äž®fÄÁoSn×jžœ$î*Z|j˜s—û³¯A9þ@|£ùNÙõ÷[=ÒƒÒ‘]RAÎy£·Ðˆ$Ý”Åï`ñº¥M`\x?·A™5C}¼–ÿj/ÉÀ5ù؈ß:Ùï)H í—Îmf9‹Ò×éQÖä5í.WMbVuCr”Ý–-£ÙàPÛfÃʾÐy`¯oD>G¼¥žÀí"–¤®ÿ(«ZØAóT¡Ñ¦%ñ!ú{ü°¯»Ÿwå§ ò<+ù?ͦ¢¦`=6tqŽ!Ñ8?ÒÅ“`—ÏØ±„ÝÁbÕ§|À#’FoŠE‰ ŠíËbý"ìéå?­ìeÌÛºtߨ:’~¹q »£$!.âè~p4Û$aŒ>[‹Ÿb{µøÖ®+ƒ_hOÓë÷’K»¹þ »°á9[¥îf{?J9¸ãê‹.÷°•Ò”uÝn –÷¡k–º`ëôeÿáñ ¼jqò À)ñBÛ`ø\̶ÂÏѸ痼…üv¶Ó+¾²=#jùŒ˜’žÀ'XÝÉU¨Sø½¸{6¼@øÕ˜—áì:ÒMýTÐ^¢P½²­mQN»‘'ÀûïuçÚj ‰'I{cô—Rø[wöû`F¹sQš;5’æB¶MÑ,5)ŽJO¶eRvU_âo߯޶3“CìçÒ~ö$ òá’î”´*íÉ*?´Þ°H²–ÛÞ»ÀFI×úÄ¥ðý™/ÛNœè·}Ë“uÙ%±nv0ú|$ÞãRs0cÖð‹(ý‘Pu¦-LŒí³l0e¿j‚Êíöøû*õ’JGí{ÚGKúɃ$‘l=mµhŽæ“ÚÐÂ(°IµUò#[9•¶gn´K럷ïâýlňq!õBÿ3µ>³âý휊i6¢‚²–îf¤©0¬zÖEw’*esä¨èñ€nðŸ6G¤Ò¥î ²y&ñýŒ;êX$U#îÂ,ÜvžY›Ñ§:Ás ¿¿S—Ãñ{aüÍJª¿¨½ÕyNÐîIå’w`]šf鑼')êE<%µ%Oxîõ7v áÑ'ÖR/5ªcê:õ ^îÇO;û!>Ôfį'Úƒ¤‡Å7RQâzÅy œ¡ ®XmÆQ3Ãë‡ò8жœÊÑ\^IkÃe„w¯u´sÞMâV’õŽ:ziŠíÍý¼ä™´Ÿr¹Y…Ÿ9µ«0FH éò¼ž/ÁàÊXû5BçEÏ÷¬´5ÑÖHuw•ô³Húu%Q*’ôlé Äù5Œ?¨}鉴÷:üŠ>|÷ô$z¦V¦‹KoaÌC´s—ûó9Ù<Œr>ƒ0)Û<úžìÜo{3Ïû@õÝÐϳ•Þÿjê'ùtεú»´7–ÝíÅÉ#Õk˜6"|ø,Ll.š6sá_KÂNÒµqðÒ#ð­¸üm¶Eð|9 ç9ÙE¶¶_â'g¿ƒ0y.à³À0ô\?’D"þ€>×éœÈö!Ûżª_+œ?8?7ó4SSéŠPÙ`ò²Ýîɼâ,6“·à)á’Í9 Ñ3õêëÄÕEh!öué»#Ȱ¸( ©õdØ4Э»Y‰y޶Á¼¯lY¬¤“0MÔâ¹jYÄ$‰Qä Dy¯ðÒ½@ðs&³ºMÒ¢pèêcÇ"1R”HBÿ(£ÎBG~ ¨Ê£ÕÉ0€€Oš÷ÍD­¡ôf{‰Äw‘f?µO¡óí>ó˜Ø?Ë(³vâí쓺ãñù½ç[ °kc_¸ñû¬ÉÚ÷.dˆEEô xòbÜhjíÒ1Ì6ö‡ð̧O(³ÓãA#,\éiÝmÉ3óVSCÕ=hÄ÷ó{;òÕ¦®µ8Æ~ cÄ‘.]êEˆ—c²ŽÀ8¢nÝbºidAÇ'kb·*æÇønópíÌ«còÝr\ü±ôÖ?Ý52ûÙ°äÅVÙË>‹í ¬›÷. mñš¤ õèºàû6Zšô±‹:ê}õÊðg$çòûì_Is+çÎû£ò†$ ­ÑÁ-ÔÃ&Ë4ö»ú:Z»ŽÞGüôÅv´¹ óŽÔ@’"ÜjÍ÷útžðkD¼=…à4žä_ zN@:²euV³­uâH{=‰_˜V"þC1èé:Õ& ¼wé’­êÎ; *\º‘~ ¹Ï«ÚÑ Å9L4×Ð×}Jzvͪú¡¼ËoµúÄß=‡ý¸¸Ìœä÷o”Ù@êèDìð @‘ôø2†l]rEJ ,¬Z 7Û85 VÿˆÍ«>Ø)f•ÜCÚÄýêvv°ÁA~Ï+»ß^NìMyR’0ÕÆ‰@~©}PU\mª: Mö ñ½Åo,E4óŸøù$ð—ǃ®xŽö±ߪ[(~%i`¿Ð߸ÌÛ.ŒD¬UU'¡h7Á±ú‘´ß «ï°Ã’¯ ®ö0eý64Ey”P%veA eõ¡gâ$"_&½cmPí`;‹>ìãº+l…ª®ô¯›Ù½ñëp·ü#÷|U9WqOü¥CÉû½¶ŠÓ}Ê{üïv/ýð‰\Ðvví ¸{ÌVI½H?š¶þü®%l«Þ‘0Zذêk\ü¿Wª<ÌŒ_‹ÕÞ\úÃI ú§Ë‘·'Ðóýqœa£óã@’Ûj7Ñ#å•0){¤¾»–œÄE‡2Ò#ëzÊY€£fj§Õâég±ßƒüüN¸ïÓÊæäÏL9fí$ŠLº¾®}‚:›+ª œ®Téýp:ÙüùšïÏíШ·®däHé[h"+{dòÞ)7…¯²‘îh3O]Ï7å“=É@ùÕPß‘õ0'j3±KxÏ‘ßjÝ„yOÕ$à—ñÕê¶ã¯è ¤ñY—~y´*n:ûïþCR®P>œëæB¾¹ÿèAóYÅ;¤D9iLƒÅt –£º]‰w\cGLÚSßô3h—¥ê¶1]Hו¾&ÓØŠ<ÞH^šÃ/8Eö ïÛ§æ8Á’™€‡ÇSWZ¬$‡YZHýÌ~y/IÊ|4¯^l_Ñ?B;1vš‹Ásþø)éÚ€´¶™kGêeFÌÉ“x.r%5‰¹™æëºHÌ[»Â“RÏâòªŠ9ªµ#ëzÛiÚtóÐÓ·u©#¥À<äYær:Õ­xºDWù=IXRA1ˆ¼žîÏ_UÍ'\ ü-¤Ì—äéáBÓ¼o ÍÌbòð†þHÓïùD«áØJ¨§X™Þ:ŽžîJLºò[_ ñÍåäh+¶x’¶–O0öIåzftEZˆæ,SPOÒõ ÛJµô_ÐÁ<ž?:Ó¦ÿÐ °¾ŠK‹îìŸþ »ç57Ñ<2~SÓ„lªx ™¯y•~Díz9“BýËHR¿¢ª*eÕ£ùóçÛĉmÞ¼ye—t•UVqqee¥ýøã6i’:ûÌ~ÿýwÛh£,™LÚ—_~éä… šÂ]uÕU­[·nX~ûí·Ÿï¾ûÎŵãŽ;ÚG}äÌF‹¤ßVñJ’XáµjÕÊzôèamÛ¶uösæÌqq*¾iÓ¸–ïD"a;v´ 6ØÀ¹iîŸ)S¦Øë¯¿îһ뮻ÚvÛmg%%%E½7×í°akêÜÙ8à€¢á,«¡TjH%†Ê°]»vvÈ¡‡Z‡ö^‡ØT˜?ýÄÒ!6bÄ—/¿Ûvu÷Ê+¯¸zÛ~ûl¯½öÌ!5o¾ù¦I5ÈÚk¯m{î‰ÝšL*ÿúúë¯ø¬Y³lë­·^l™-®ü%©þꫯڻï¾KR×´ƒ>ØñÝâ’ÜšN§øÁçÅùZz»oXV§c›¥…SDw”¥ùtXéjf⯠k@û¥‘˜;á07$ìãÅ\Ç’„åÕÒSßµÈÇwäCîÿαÝ&¦Ë-9š hÀi. %÷ᣠg. ŸÎä»K ^a&ºÁg?á›ÀÝËþÄðp&Ë·°sªº §ç.ðã}º˜ŽÜ œà§àû ÔHýÁ½L WÄp “ó“T씕fý#lÅ{9?& ÔÔådÎrGõ -šùý®k›i¦+¹É^Ø«rR 93l]ôZVo.¥öb!ÒMÁ¥pyžý_³Ï4€Ï*VÚ(æ¾Ðlqõv{M–ß~bñu'ǃ©‹ºíÃNû.i‹b¤>éèlýæ\äêÑ@«¹TŠÃ`¼j®ŸÅ» –šbflJ1iÀº­„–UÚ4Èë™YLj®‹‹,²1o+£ßöI; °ì%5˜fƒòƘè€tœ’Ôı,üoñ7VŒDm5xAqžËáiç’üÝÇ—^øþÝn ü’?ù <Õ™$°½¾ËŸ÷$î|»›oxËÓì<ø÷ÀÃGéÉ£É1vIÅv_ÍÉô¯ ‹n6.³aiéœjíë'·° á“•±ñz޼Íô~ çódBÍÂeNÙ'6¦f#g,Õö°DÏæý Þ§º¾äPUH±­Š„ãøä£òš#é×´uýïßl üœOÊÓZü¦ñ›ÅwKÂßš÷©ür4*"óÝY°±;‚4}B’äW´ö̃ÐgJUý{ÎÔý)¹Ô[À75RFz ^Iú×N8;ñ®…§OºôIÂoÖ‰°Pu É ¯Þ(}ã'Qa£°_…ïY\¡ZôÈÓ}§Ñ(MiÏà‚g.É |b)Xe©mI»…™¾uKÒŠ™oºŒ-Ða*[]*T × ¦ÀJ'ÀM0œâùÓB8yŽyß™w—2t["<*ÆðŠß§)ÉŽÞÓçÀÿ¯“ï”ÅDW ‘ FN9É£—(«ãq§ô¬ /Å?áˆÒÔ_éÕè3&_%¸©Ý‚´Â?‘®æt´Ç.ÀiIÝtn&},Æ]û¦þ¬èè}wiž±ˆk Ü‘7èáò»ìGñb}â¤NÜú•èçWÀý»ÎÍ϶1:h?·×µ! ªßß=6`~«öÛ1ÓÊ&I‡µúÄÌLìà+IéIR2~½ùÙö£–]Ž"ð$ÇGF«lÄÃáÛƒNÁ¼š´f¡z MU7Ò°À¦¥(Û8­K‹ÿÄv¸¡ìKèOÒÄãò³­U"¼¦ÒæTt°°ŽÂ{±^ö@ÓÅ«Eq€K¯×Å{š:ŸAX¿Y¢º½5ÄàÙÔDꎺ¦ß!6¤ ¿GðUµá‹5cü2êá${vrêMû”yÀ1èǼDyžo“ïØ¨x€þÖª/"œÿ½÷€“¢èھϤ(‚("‚`A1`Æ|ƒˆ9 ¢#Š9‹(DÅ,0`sÀœ1gEEEPXED3›wfÞÿU==Û3;³,ˆÏs¿ßûßÎvu¥®p*]uêu¿Ï}w’Íô8…o~oÇÉU/WU ÆPèÊÇê&cž%âÚ†â›NxÌDÕ®| k‰ÔáMñëmûÚÖ„}‰Ç/è‡Ûâþ´¹Å“m8~›\ãú+¼‘,þhgÇúÀwðøF„Þ_«ÁñþYvMù)6©t1–õë`§ž¡ˆ>ÉéýíJôý¯>*E”óÈÄa6v³‚aÄEÕ³CHË#£Ô.¢sà'õI=5¸ÌT—T^’Õ /¿ ÿd ÔŽð‡‘GêAcu-åX@JöêV§"äÉQx)×w:ˀݛDñ=—U±58椽Ãí‰ï:âápKfxÒÖÒļR¯²sº.ÌáÏ¥?8É…_æÜô/JeÄá™óâØo‘q^ÚQ5¤¾Œ¿-šUv¦¾Ïòœ7Ïfà'¨®Ô;i[Ÿ¾p cê2üß@ýøDo`KãWÙ©ñ„DZù7–pÄîâ‘}l[ɃFi#‰Olïš›ì…ÒËÐOƒ7‡P,¨Ã£mãjD`jõlb›CyÐ' “÷àªuìÑ¢VVu-§ÈoÑkioó«ö@÷û§ø¤0Ó4—ïÒöŠÇÙÈÊþÌñ>âÿš ¬fPÎßTîGAæêÀâ´û4QÆŒ£T*¿5øÅl­¿Zñ¤.DÒw‹Ê«»‰ú‡¿ ®Än¿ûQåt‹ÝÅüzwç‘ïç"mœÅù¥ ŒìIø'lcNìB~H<ì;äaøûŠññÖºï¬*ÜSÒ!]¿¥M‰Ä »`¬¤Ýï@þ?Á…>¡ˆƒ ]†#õœMR7µÀq EF¢ów-kF}zd8-LýÁˆì îÑ ð6ŒŽðn¨E¢-¡Nè'6/§J´ƒ õO~à„RêŽù÷fÝ¥¹Q„÷·5nÞè"JýSOÑŽùà’ ¥6תúcós†­^RÝ—ã ¥)WIÖN ŒÏ‡1w„djc——…jˆò >lð©´ n³U¼¨Ö¥6‘›¦Ò¢Ÿd¨R7p„¶¡ˆíƒÛƒ”£E/_¾ãGŸ¡žeO«æôʉÞÝçrZu|©}x¾å¸·Iðö”Ä63­//¤Oš v!é\ŸŠ1<ÄœéúÔN°«˜'K‚š3Yž7©½‰ÿåiôÙ ×!¨yƒµ¬ôŒïïœÌXø={3îHÐøÊi%OK£Ñ6tt›ùðMò½ná6«Ðâ‡~p±µhÑÂ=ú>õÔSÜpà m½õÖ³yóæ9àQº}´«˜iËEEi³@ÜO?EPœcg '÷ƒ>0vW[m5×Õ \€,’Î_ýDz_|ñEûöÛo­M›6.ÈR)!€W¤ï (*½Äò§p‹(7•”®£>Ú…:~üx;öØc]Ú³ãhªßÇ{̦M›æÊÊCàë?%to¯½öZ¦ ˜:dˆ©žòÑ¢E‹ìôÓOwå¯òéò•ÓСCžå5ÖXÃ.¾x„ÓûìÇsê©§Ú­·Þêê\ þAä;­Ðs·´ÌDu8`ÀBG£Ü¶8v¬Mš4)g€å•ÿ9çœãò Àýý÷ß·Áƒ§Õ–è;¹èß}ÕYˆ4XøÔèó=že®•,RFÂY}nCOØ(m÷Æ@_y« ø]£Ÿ_¾Ë#ouy¾VÎ]“´Æ•‹53”@ܦ‚¾ ÙŠÁÃ'íÚj’‘4Qh*è«8üÝð|ñ±pt5 ï]LP›óv”ùm>èë,rü“ô‡Ò̃¼/:c:•óøQ=è«Þ+I6:’N¸-”þAÈ—D850êö\°Õ”åŸPúB¥IVØ$‹©½è;ŽIèN&#>½ÈD§‚‰¯·Y7W¯(èëÇ•ï)0ħž¼eÙ¸u‡Õ»¯˜)sQä…”‹; w,Õ3êÿ,|4™÷I7öþB™ü7R®Mv®ÐB:ù;’–}‹´¸Öt7H,„´0ÐÉ&Iîh3¨Ý$}3 ¿ÝêšjìE6ƒ|ÐWá÷²àñêUÒ'’^©}Ϧ]gPøDÆírÊXt*Y—:¤ËÕÇcžgNý_úyÐ¥évŽ[‡¾ÓãSlC&ë"ICm*°3X´×"YÔ‰ùŠ–°Ï;épêQtAÛé¨9¤ÏÕ›bZ›àFá;<»²ˆ¾œ'Tú‹`‡øæ\²þ±ˆŒ=€]'–+)S-º|Ú˜øÿ…¸_`1"`$›Ú`ÿ @7çp,Éω·OÊ#ócúò9 €à- ©r­9sUÊ_އô+&^K9hñ!>¯oÿ¢ ˜ÇqÞ×Z°ŒÆö*Iìä‡x?»ç±£N4ܘý9 ³§:f}ìÖå· åè‹×¼D~Tö5g2WyPG¢ºkøô,¢Oµ9•x§Rt?}Ö1’Ô‹ ¤,Þ$-«{ü xïISEoà{£TˆbÜ´ŽHjÞ^Ñ™¼óÈm*ù-þ)»ò–dqW6½B¶ˆ5d—оèS.·Ï¸äj‡øGø+'î©6õËÄ'É×yŸL¸M‰´Æ–¢-¬£îjࣺWpÿÛ:òoI|NÚ»Š|ÖŽµ6Éï8ýóvÍ÷?¨ÿôw®~Åñ»ñKý%_ôÜÝñöß6 õ ‡Ôˆù)â(¢EýjÃ9£­Ù®RYKç¬$«ëž„OC›SÖ´]þ%Itœ¦Ý8U9ÒýªÙlá}6¼æ»Ñ‡TK¦‘Ưpƒ—>8šÃêG—’]èzBÁ“˜7¶i•Û°qXIô¤E YñLž”ú?=‰^S¤£Õ_2‡^e»žø‡x.¨XØ)ÌýÔTõ}’èdÇI’¸¢]}`O…>áNŠí-NAI—±JR²5„×ÖœA”ðnloëTx™ÝŸ¼ÕZM…g6#¾‡QáÑÛN¬|ØŽ©¦^sUÿÇf°Ñô]õ@^UV÷­*öå2ªÛm=6{Ó/¦:L> ëÄüLÐשxhKÙ_C?t è«Óâ–Ù®H8ÿÀ `Hí©Ä oÕM Oô3´žõ`Ô_üzœÃ³qU¿N½ˆÔ§ÇgÐÖ±Á_uÏR&?ãPB»æþ Zub‘ X{´µÚ#0.µ{c§s2¯•Ûœí^øñÓ·AR·²[|:¦B÷^ÿouâí†Z§ŒÒõ5:•:ðçÝõ~s˜è+{ø¤ª'ϯsxÊ´Z‡×Iôc©ÞÑ9n¯¥ž_@IDATùú¤O OÚ‡tŽÆÏ¸˜Òvë¼1‰Ï(»Ò‘IU‡Ö¢«S‹Þðãx®áô(ßæù‹Á?êkt2*~µ³#¥èz^œ^×URæï¤áJ³¾ŒÅ­½ÐY oÊ2¥Ê’ãTËž_$µï@ªXª _£ Jÿæã)5gÅäG'½r‘N'vc¾T’íÏW?‘mŸùN=»Ó$Ûð~ô½×,ré 2èl€Ñ"xCc¹ë‡ô·¾¤u¨‹ýÝ7]NUÔÖDR—·Ós*Û.-,JÅH/Ù¨ð’.DgDpáâjŸ¢÷vjѤªB$לH«^J^wäM¤¾ê9ÏÈÿN!úÚÈ%é÷ A³€]é;ýË%•¶?˜g- ÀFZmÐ+ù¥Ï©½»v™öMy‹ì@úè¯rP8‡ÝJYIRWàŸ~Ôžyæ›3gŽúÖ]w]§/Í+)XIâöìÙÓvÙe'¥+WÒ¿Ò,’4o§N^®x6ܰƒ“Ýj«­l÷ݽNEú€o»í¶.ŒÀZ_Â×Yðo †H…—ô°¤?õMùQD”ƒ´ièÓ§ó·Ûn»9'}§©¤Kî$á;ažþù V²©)~žßpà ÙAíî»ïn`·¢•}kº„Oàèyçg<òˆ‹âñÇÏ•Àóþýû§uß“¤Öî¼óN—ïË.»ÌÎ=÷\ëÛ·¯ùåç‹/¸°÷Š+®0À7Ýt“«_?üŠ<T#·tMßu×]6nÜ8÷½©S™F°YMÓï¹7o])½ü±]~ùå6lØ0“ĵ6Þ{ï=¾óo’f4ð¿SŒì3fuy]!·`šü€êÈjü—ÿ¡gd9ß‘D×ÿËÂLƒÆÿ&IG¦HßM€ ¹½>uRÝЬþu¹&’ÓÐãV ¬†²3+^Õ.Q¯“ÃåP wñÕGéÉ{Ãkbµ‹¦Ë!݆ú7ý‘¤b¬vh†ïõ˜t5•®TõÛ®,žZ2’ü7Q(p¼é IoA§T{H2ºoó`MÀo¡Ý”<Ü–VÛÛ·¯çw_ø>Hk_0Kó ÿRÏ”nO–ED°×”N5¿]ÈÏhÒÍS¬XX¿àHǑ˹Ûö¹\–k§‰üÅËí‹”îÆHó@” Ui±«)c6}Âdqb¶eú]íì9$¿µíbbØ×™ôo­„õ$pøKë†Es×»lòÚߟØl’Ý®9îûÀî³)^˜ì&Û©Í–$GÛÓï8U±)İ–ÁâÉS…`¶]t,ËVVÉäýÒÿéàL·BR³{âëšâyO:œ´Ö@<4s~ޔܽzÿJËx2e¯Þžç4qÒ1Ërb ëj¯Eš#Y?¢êR“ƒ‚}ŒÙNª—Ñ-ÕŸ¹8_|´gÔÿÊ øWnÇú‹[-”%mÝ‹p”Vô çw‘–‘¡î¤éb÷žñO@¡“¢Lõ—:Bœ _ÆEmÏ_ìþè½kå Îý8~ù ‰é®ŽÁÅ/%ž¤«uü5[©&KÎpã—ÔePx7Ò»#V³Ió,ÞÁÜ…ß–ÞÒ/! ÿ®Ì$-ëK'Iç%qkDØÍIr³ .x˜·Mm@ø4»;Ôò¼ âð;ðKÞêÞÀî~ªêö‡â—0áq#MáMYÐ%á5–u‘QVVðkŒx‘ãø‹_)©HZ›’ïížÂ^öqMRy;——½N ¬G¼_Y÷ÒW¨—íÄög¬øÕ^«¾•rø•ßÇ®<¬‚|„O1é¦ïÎè´5D•pÚúÄkÉ(Jò¤ò ÷ ?Ô à«Õöa¸9/q¤ûŠ=éòP+; ’ïÕ}àÞ.Èp/ÜÛ¥ê}ò:Ýf!‘ª[èIfUßÙl¦ÔÙ.D"^RY÷ |Q÷±­S|›}Û‹8¶¡L÷G‡ã".Ša~Œ°<ã”gâ&{Žzn­~Hu,i¿Òo>Cº_‰µC`V} aˆ>Ü`ýPÔ2¼TÑ Æç P¬`:6^³í§•bŠØÜ™¬>IÐ “³í]'õK-ÜÑyM±Ý«-—ªvJÕ‰d¤#¤1?¯Þ•z"½’6uDÏ‘(³Ž”ëálü¼Í†Þ ñy6yµ/mÒ/ì2ÔÂT¡Jc0ú²ÛS¦qÝšw$þä3¼ µë¨ÜZ'žBR³¥mãìh_ŒÚÄ.®f*7³ÓþZÇ*x0QÁ „£×Ñ‹(¿~ø¡LjÎå¹5?ê®f*àÌo¶†ú$¡·‰v±$RŸЭ²¯,ìmŸßEÓìòfp{ìb6϶°#óÚÈp›ˆç:ŸMÙ´¶9{X‡RñÓÜTzyD'QŸãç»5ŸÒoúô3åþvlŠU®^ã6¦WŽ'>Ökð…Ó}í{ôŸ…WÓU<êÀJAû ­¹ˆ#þ3j¯¶Ç*ú{®NéT{ŽÐöÓ´ù:)ýfÕ'bþÆÞäú[êB}à‘[\=[äLÒôüu%~6âϤHG¦?¤´ï•d´ªEzsë}G. \hî˜6§Uî Í·µ¥N[¤yÝã#á>š²ú⩌òÌpïNk›z—ÎÏ:¦—Ø;ñKì6Б¡ýìæÐñîüw€0>Øåcn¤;BëZѲíìÕÊÞ¶VÅ:öMõ^ö›™k׌´æl8µ/z‚”U¹ Š?XýžbÒ¿,s’_PïÑ=ñ·í[t—†Ni¥œQ5Þú3_¦5ÚX€;Q÷H?é’>eéG¦ $Y¨Ó75ÇϾëøýÊe‚[Ú~Ô¿¨è9û¨Ù­v^lsÛP’äÕô©ÙÚ^…÷ØsªUœJÞ|m<óD]`œ&6.,13ýšÛ ´Gñ÷2ªLþ´1µÓÈß7Ϋ$nw+·eýÅJ™Ÿ7§¯¤4 ¼ì ¶TiHsjŽàÑj©§âá4€ùó­¸^@FUnm¹»¯‘—7øMO‡ú8–:IuCÕ2{&Qiï±^8S¬>in’R—á[éI¯¦Ÿ1ÖæGŠžb^ö5z½6Gó‡úÊ·”›—fv‘µ`Í·Ìn‡Ç$àá“6Χ±±Tˆµc§"ezo„Óò¾‡ÀS=L}/pHµfœ…ÐDûF}Éóúðïž~­b¯Õ%ÇÔ¢M½ÅÉlp)U»;°º%å±7ïÔHèpÌ;Я J{®¦}|Ã\Gjí:0ÇWÙJ`Dj:#-wR6R¿‘ÎE5Ä®ø{9ïEŒ‹Ñ/8žÿÕ¦&RÇ:OϼÎIšÍS›Å%·Hû0ÆÖ7#ýÿeú§‡÷§ÏHY«Ä6«÷¹Œòغþ=hBµŽ;í°Ó&Ç ´ozšUCngÍšå~ºdMR³"]²&ÉK‘@̵ÖZËš7§óJÑúëÃ(T?ä#ÛoßÓI“þüóÏNƒüúËå '{5_ª×ÿ–ì%%,uٗȵ݈;õRéU:,KMASé„N0IŠúä§Q’ÉÙ´<¿’¨1b*z;•ÁðR?ðOé»ïèÔ!â¢ÂÂBŠ ôÌER¡ ©×Q£üµÞ—Ài»"IfK5‡êZ$Éî›o¾Ù²Î‚ºoeHXŠyÙÜukžmYYºwN{éÕ«—«¿ùóç§í|Ãá'Ÿ·®¤òCj)"‹ü2ñ¥×õ“4ÌÓäÓTß§­šdÐÀ¶"$éíÆ(˜¦ÆüýÛnËÓCÔ$©¿mW]W× kÒ£´¤í¿g¡]ë|Ô-Ð|?Ù@ Pñ{4†SwÔÄ÷›ëù"ƒaNÊ‘Ž <yd¢O)ê–§| êÆÊðwâxƒ¾<± Ë þäñ›á±‘í2ûÒ½„tÝ’§å .‰IÜþOÐ/,ýãö~úßM¿£W.¨;ØwÏ÷¼ˆ‰ocäëù½8 3¤JR»ó^hñÝv9ø±±¸ÿ‘Ç3¹/3¶ty`}u· Ïô’û-~'öe¹Ý–c«o–1!ÍO0©k8žæ÷߸‹z{<¹H€ýbM†96í'Œ¦’8kŒ¢Ç2É> fП,¼ sò÷mǦ½mÓNýÌD—ÊÈå{$¾æ–d)kh`s:ê¶=ÀßZHñL"m'æà¿áÉ_8 {ÔÅþ‹ú–£Š£xÔ—»»àKp¢ìÈ>vUb tpÖåLÀ+Ü7 \žö?åf’{O¥ìÖK=>ZJjÕÚ0šÉäôy>OÙ•¾ÇÄÝ[jé!†&Y0¹Küh§âÏë©uc·ÅY,å#tÖ9´÷­œ´Ñ›§m%-ó8—ãy å9i{žM>bãXŒ­;·?]I-½µÞ_âòÁ¢ÝQ5‹·ÃìöÐ|c 6ðN6±AV¯Æ"èHÏž’¬Þº>@ù«9<Ã̯Â[¥<àUGâ]òv³/Ža y¥sß(¹Ä^ŽtÄ\î.è’nB+:¿_Û°èy\\°„t®ÈI•ïcÏ£Sw £‘€k²l=¾ÙKvsÍ 1ê¨äIÆŠíY{z êŽ5@†ter1Ù¦¤±«€šð çýYê²}äRÀÒ]|å3ßtÜ6¢œ÷±¡HÜ—£¦ÅØÄ°Ø½ÁkZWÀÜÉ%S¹l+ë[sšuFë'¡/iä+2\&»ÄÚÕÜeÇH%‡.CDrÖ¢ƒyžÀwîp:°¬d÷F Ïo(ß(±Ÿá©NÑÃ=>«T™~ɯŠ8ûPðwá¿´KÙb¿’Ä t—|=%~·ÉЛŸ‰ó6»Ú“Ñc¸àªU!è#—Êc õÑ1;ÉÞ³j°sÄ]/`Øüä‡öuÇ×C=­ <~nõXç£MÅ.”2T4Æ^BÂ+˶töNŠ–KÉ,ÊDj2œ^ØBÊ´}Ço£›šPèÐþ<1ËA{*Ÿ°Kjߴ⢯lTá­HÙvCXûX¬?P—ñ¨=Z÷¼mS®o´°Å¡]‰÷s›QCÙ‰gjï%þ_í“ÂûmhñWöRÖó ßEšºÔÖ‰žoÏ— Gò“ïV¿o)?Ê›| ?Àºƒ¶IªF#uNŸWA=WlJ¼.CW32ƒß#É8;þ ÅÔ‹réFÞà“H'~‡’—<÷¤¬9²¿'z¨çU]ýÊ~âÄ¢ŠíéŠ~ö]LZÉ» ¿ Á #Ù›jCµrÿÔy þ“$åë5}mõÊcùV¶Š–âz!Î^¤çb÷í/Šîát}Ÿ“:%ߎH‹¿†çb—RÖäÃÚãï0ž³ù*ª¼90éVM} áï67ökó,vv9¿þ¿ß0| ©x_õ†ÔJ 5§rªC¦±™ò«6¹"»~¸›×õf30M‰W('õõ?ñ£ dª=Ò¶&ŸN?¾/yë ]ÅUf¶™oóƒgu°7J¯|Šr\ßöG×¢·à¦²ÑþK–rqå2»Z#iúÜÕIú5§au¾3.ež«»ÌuaNŸAËr^´A¬ùºh* è¶ñÕíwTbXÉC$ ¾¤'·âOxú9¡oP?¯º9=½Ø¥©-þ_àmÝ´o¿ù)ΩäGKÿ´æ”óuyÐ_8¶aÃsAÁ\˜ÝƲïUx‚zõ/âÖI*’Z±~z²Ÿ[sÒé0.`µÒùðñÛÔßc^”£Vüަ®w ºvÒ19ÃQ§Ê›Ò 2õIj?¥ýèdÄXýÍØð¸žÍ­ü²Óx_ôÞ×Cò8‘¥3ØÅ{.$®V¤¯,UW™®Á7ú ú˜8h‰Y)lHKÔեعè„WŽ/uíè[dEµGà±B§¦RA~¸â‹mhª_ð$i)­Â7¹ ®q„¸›gj³É§ì/JPFkF°¹TËùáü§6Òn“Õwq)äÞŒ·í˺óë-—g’ÎöÚ |ébè%ɉ̟Ô ð`}õ™>ÑV“ÕþKæSsRÐ4øa<`uÖ(ð±‚F³Rq ßQGeýúõs ©ÀBIÛê’7‘œùÑG"[}õÕ­¢¢Â·jð”îß'Ÿ|Ò¤ò@R§™›JŠUj¤~@ê#‚$ðW”øéSKŠG€­Þúö=»víê€NùQ¼’„•ôòFÊÙ´<¿RQ Ðù´ÓN˪ô,Z´(Ã.ßEiž²^šýô‹Óq¥Öæá‡nÇsŒSÉ‘•+'•¤˜wß}w¨KòW¤8}©l½«}ÐYïM%éx­Ëò,®ú6`+ ¾niyõÁg#"|yå_RRâÔ^HuÄ…^èxZŠò©zÆßTsiÀc$`f –A¹K2lê_:0ñÌEÞýí¹\rÛIz;³•äö÷¿m+ÄM%IýùàXSÃÈß6t×2 üß")üÏEÒ‘ûy ÒƒLhöjdç‘%¹»)5&Û<9(atÔ¥Ë!í¢û$~N0¾eàÙ+ÀÃ&bò¢‹å™ ܸ¼G l Ê7F¼ÁZ“Ýë“Êv-ÿ%ÇSmæ;€5‘$…³u;‡á_+À37“Z…¼Ts’Óíiã^óºŽgAã_’'MŽž ôÍy$õ‘MÏä2³ý­ÈûÏÏï³ÈlœXü®$i"žqY ¦ÝÐ÷LêNNÛ^ Ÿ»#Ìi›3ˆc‡!E”‹ØŸXt3¿ÊÁÚþÑB¹±ø(x’¥qˆ´Â${<>>Az?°à„ÑŒéÏÐkv>GÎÇ mØ­ðn¤½…×0úI-Ñtò×HóÞQò¡=P+ ÿRÈÙ$Y;µÕ°óõ¨)z©0q !æßu<Û-æ:ʉ ð0þyíͽgÿEÎNÛê–gGIâqyš€.·ßX(¥ÆtZÞeÏë†ö@„0u’b.!çúý}ô¢éšŽ×»,ç÷®=‘{ eT-ìDŸ´åÉvFAïzÿ% ª |tH½½$¢w‹‡ì¥=ùn½[õ–&iÅ4»™2,±2 ÕZ¤¨U(buE/0ùg¹Ð·vÏ>ä]*x¤žG¤^z´@®úŒsê_‚Ë ñÉqxVú‡"òsùéKºyÒ¤šïL9î„û‘¸_ŽÊ¡6%Ò¨ei:ˆï=ÅïútÁÑPþoàÙ%?fAt˜3Ë ö‡±@sjÂg/åZÞ–x¶³îÅgÛ¼º œ?õÕWhà ߩl2|Tüˆ½‰*„jŽv¿€×Ëã-ôw?®Ão[Ÿcñ‚Ç /DàÉ÷£=™À”oÒ&$áðpw×ðéV¦‹²8‚} à`k]>'IèRêøóèM€Ã&³Þè²ýÕ ºÈk-¡¢sX ïœ ¹M‚é„ßËî(:ˆ…ù¶¤übÙ ¨yð¦@¿“»€Œò–ú éŸÖ‚R›Õ½ô('ÀA2A;ñ;#|XÕ‰ïè"1«¾Ú¾—ú”òŽvrÅ– ÿf #s>ÀÖ(Ò³;cíÁ…wÚ:1ëoß5ûÐ:'Ö²¨¸ˆ«OéûvO3ʧöòIÛ(zÇú”üø³3%Ñ\LÞ%!+ Ú*: S©ÇMlíÄí|wâ™ëŽTä’¹u÷[ëfŸÚ µ“‰óuÆñS|¦õà"¶™{kîµÛΨ~Źßýý§îgû©f§c¶îC;á‘ÙÅ£í§âKÉó€¶í´È1û¶KÕ%öa|ÅQ{tÿa”Ò0|Kº%E‰ƒ]\³¿Í7C·£Ým+ñ½mwøûpÛAŠ6ì.;K|ÏÓËÛ¡ôCŸ <+9‰þc±… ¥Bà ÆÛçHY^È÷Ö¾jwWò­ª+HuRx"ižI|m¨«Ý½§ë¿Ô€ç>uºVVw>%õ/’¸ï³ÙaEaYJ'ÎsíÏeÙFåÄ)x¤ÒœÑûG^R¤¸ï¢½üEØ øÞÛᨠÍQˆô©IÊ]ýMÁ)Ä=€ò¡=Fu^üƒ?­Þ‹MÁÿøÛ«Nk³PéJõ-N¯wd òH}HF‚Ÿ²‰v}"Øîl"jS϶Ø^YàWcê懱ížÈæ¶€±lºõ³M«†Ùýåmߺ]]îв5íj3$zŸe,ÞÉpZò#Ö!uö.óáæÈøÑf¡ÅüFrYžS»ÀiŽ–¡–ðI'+®OÍ…I×óöDl݉š¢´ÄvâB-³»áõy˶Ǽ)¿ºS1wÄßq3W /Ì·#—ëŽ>ÁÑjðãivbÁ9¶‘ë«ëRök:>XÈ”M9®ô£9!uºŠže¹$ lQÁð°ú溑„™Kׇç²Tø?±§ïw#·¡øƒs“Z±'Bp›)ÞF€³nðO«ŒX~kŸ³jß`ìûÃéq?0Õ–æsn°mÝbëŽôh® gjˆòêA7’j‡zÿ‡$iô—Ù,³ZúS·¶ú"#Æ‹˜£/†ç? <µÚ|ø)ܬÐñT‰|‡Ïäw ¼"æ.w£‡ø9Ͼˆ\Æ&yj.$Ϋ>9GÃ6¨M©ž#¿Gl@µÕ8³\¢-‡Fë|HY~ã}2󌙤w<¨›¨-8‚G²¤©ؾͯžÈSÍv+'`>Rió-E»ròà'Ê‘^/@p¨ÛðÒ™ï^©É 6LóŽ ‚÷jï롄 \úˆ9MÁ3„oK» Qs ëÅLÄQâ>¾wL}Üñ«0gòIÚQsª½‰ëY¬6qÖüÿ‰Mm¿$Ó~W…A@¢€Ö7ÞØE' Ñ—¢ ‚¬r¬­­uzS²å"Âo¾ù&—3DœZ]nvÈ!‡8‰Ý\þsÙéÛºÄL’¨AúóO¯z‚€p]’ÁHÁžYàŸ$[ýŸ¯óV®>`+õ£GvÀhÃ<›\~¿DuÆ]€ÆcÆŒiXçÉ}]|ù¾áÛëb,ŸfVü•®ßNz’UV+Kí7ÜÐ]‚¦x¤&!»ÌuYš@Ôƒ>x…>Ñœð3õáwY ì—ÿÌTˆ—T–Á‹ô¤7ZÔXžr•¿ Ä¿“û~ýöw*!¦OŸî¤€¼+̪"†Ÿ4ó·fªy²ìp¤¦¯†›‹´«•Mê4V†þ•NaeÒÄ0Zè%·šl¹Þ<é¯ÜÞÎÍqÄ#·Ïf+€9¨·©±Ø4qx/ÐgIZàp@ y€ Æâ ºmŸX|m`ÖäN—G=œÖf{ü´ÝîÀ‘lï}§dÔ<:8+ͺ¡7¤Åk½(=0.K_×?&@\¤²ÕD$›Zc±Vʲ7;êÏþ ¥ÿMI­çڌЅ.R«²í±8lèsR†”GCwÙuúæöQo+0ú‘ü ÏÑ£9øã,ñBÁSûGn»æàïMÓœØxÔg4X\6îßwÕlgLl÷ío‚ÏÿÉÂëqÊ\}‘Žljñ¤ÌR¡¾Ím…û#¥Ô¿Þ ‹ ›IóÑ60Qm{VVuHÅyAhëXªÙÂvñìºøTüosùÑÏ,˼eÂÔ]-îÒ!¸§Ôl„ž‰D[‚w]Â!š({…ü|ňÃG<šþëxòà6‹ªvÅ–É|lO-H4wSÌy(1Å-ä}×I@¥lÛÙCÑ R‹…õ<`1±„Y 9ÒšËFÛ%~ïšþD»ñÐî uÇMð$hÏèqÝ[‡Ënʻ;;÷/z ßèIÚž`ÑzcÊBéã˜ôÀ‹!\’•MÒßçëënƒã‘ª#|V"Y.µ”?“òúÕ—.dª¬z²ª›UÑÚ&8Ýxä-ƒºe¼‘aþ›BG°À9=íVÏCêéÖOÛãÉ}ßbÒõZs2a7â'@ˆy—.´°9  åNò<‰¹‡9&ù›Ws-͵‰^Šâ>ƒ‰±\Òp8zTð¢ý&]Í¥o=óGzÔ£Tüº¬ ]@âo8ÎÍå.ê3¢7»¼Ìª<Ì^+?Ìþ,¾Ò†üÝîi”>IñQÇNú•:tjbH»“Òù>ík­jŽ sbQ¼­q?þ”`Ù¸9s®õÙ°°šÁ`¤?1Ÿ<¯ôÿªÔ²¥H[%‹d¿Ç3íáØ=¤kg+§Ž,J^ãÒ2N—,;¨ÜNa¡*Ý¢·Q{C’?±ñQàØÑmIƒ¾+RÝ«•ðMƒ·Š‰)W«›L^³Cwn¨£= è¯qÙ’_ya¹ðlAôt¤¶{Ùûɱƾ‡¯`ï!}6=8îsÞÇrNê6j>¬8Æ^“Evâ_±AršM…₾læìGþ”_ˆøˆ[‹Vò·Éí±Œ+û¡VâØƒv;yzŠ2ÍûXùv”ÖcÔIJ×Içr?ù¤õB-‡¿Õ̰뢻¹¨ou¦‰\ìÌ&à=º=˜¸€¹xÇÊ3)÷;Yüm½Š®%ô3± ]^L¨UßdÛHÕ],7—ç9ø™e9¢½¹-’ŒIHŶf¡ÜIÏ@¥ƒþÞ’8öqpï5nñ|¾÷íʺúpÛ-ÙÍ>–ÊŒò.6¸â?öP-|¡÷Ú£ÈÿóÄÏw_b¦OøvJj+:œ´¾Žß½øÜÿ:ê‡v¥4§‰ð"TXXí ÚÙY…‡Ú%’>ºqí£°°r0ïßÁòìÂó6€٣)€ÿÛ¸>Àu[DÆÛáAžCdwüõÀ<›4’v¤µ]_V=‰ï@Ûx‹w¾¦i§lÕOÕžMÝÊó¸´kÚåÛqÂJmMüò5ÜîèUûTÚ‹o”áÁ9Æ^]:wb³·9À¿¡çpÓéO~HÝCZ¿% k-¼RMK ]"t÷NŠOàTJ?÷ª|Ÿê9åîlå~AÒz•´ÁÔùºlðô¤ïiŽÍ÷€ŠœWÅdîˆx¶²ï©}ÄΨ»Ì%´wë¦#> &Y¿Òë—¹ÏÆž¢¡ÝŠÊ׳Pérj˜v"¸‹ ŵœÒYœÐº iíM™+ÎÈX³ýAñÿl#9Ñp…›,CeÊ_ÞEO¨Ëø6ñ#ù¥½Äá?‘ëÏé_Ejƒ‘Þè¦mfÛS"»… €¤£6?|¼@k3yßÐ:ž_ÍbQ·Ò»f´}¿&e§üæT - Ø-Ç(ýÍô"éH½ÏÊBÛSï'9û5)ã~ôéºô¼©Ô_õ£M+]*Tª-Ș&õ™ô<ñ°6íPãÆÆLg÷ÆÈŽî÷µØÔS÷ï󙃹Tìq;ˆ4¾Ëx}6õæF_Ê|!:‘=Ui™)'ï²YtúT•¬­n’âh›“I}bIÊW›âãáe¸IÒX§œÖaMô;«9T…Â$ç¡>äG·É~rj=%‰êæäêtæ"ÒFvAZاÎðÿ<ò­ô×Ð3™'þ¨¹MNŠÚÓ‘âôÆ}N/Ö Öx 'q¿h3x‚~%@Úøo ŸQÁ»8^œQ3ªî´—™YéÑ]ÙB!Ru˜¦äÓnÃa=ú‹¿².µoŠ^¤…wMqnÊZ<[p./Qg!<íCõuAr'ÿ Ú`þÓÖeÌZµD»®S¾˜ÙdÒî`M´<,2ÿ÷ÿ¤Ÿ¾ gæ&>ñ÷/ŸÁt+CEDÀI¶ùØ4Àúús}¿Ùß‘´¯hóÍ7·öíÛ»8ÊËËo¶ß|ï’(–ÄqP’Ui’ ]À¶²$õLÿ$Ù,Rž†êT[Hj·cÇŽy?‘ÏïeãÆÙº¨H˜1c†S“° _éH–¾Zÿ»¼‘6â¼KÒ×’†’ÀpéJÎGõ]Fn» fA:qGŽi3gÎtª?äSÃÃ4Ò~Á8©p=W„$á™Ý e—¿Ò. ß¿þªoœ¾9_žò•¿Ÿ6ñåá‡æ.Á“Ú‡|ÐÁ¾$±ïïßx.Nå¸v%#_J™‰˜Þ¸_SÕ>¸Ä…üïø·z°3O%©SªƒÖ«@¹3&Q«&Ý\óÑÊÖI¾øòÙŸÁ`ŸSŸ)4XÊDR¤£mÙ´nªŒú.ôËר»–šÙÇBÃí¡Lªµ,ù¸ J â"ñdz¢èl½ò—¸çÉ—ªMqJòeÞIÎòøû.&¦Á‹$ü82Ÿ™ýa¦[Ã7†hkª*•G!I÷o‘$` loaa°ü|Ô§BÐÐÃÔRo™ejL"^ "QÙ¤‰´w¤2Óå!êò·ÈÊ´×®1€­%ÓcÐØñ¿t„Åîh,èJ¹iSEu—Mg86…ÞÌs̪œÉtc¤>VÇ¥v HÁ-]¨vœÀ½ £(ó²T+ºÐåOä}:ÅHÇРZeâRw‘çU\øåÕ1`G1“DuÖÆvL$…Ö5«Înv}N¡ÝégµñKä•êH×ÅUmÎq÷smç0õ•"?¯¨N|Œm¹[&? ô+>^;æ~Ävó–\6…º/žmrë:"µY”ª¥ÚÁ„(IÜÏD}2æf¤w;žõs,éÚÍÕâÉ#xü`7Æ/Fï©òÊb—‹wÜ…:Zx9ZÀÿ_éwþÿ¥ï„Ovïõÿ~`Rý ©X1÷‹æéÍþ®=ÉI_éB¢³}× Vw‹³÷0³8å8°.K¿ä¹ï¯ðƒpÊ{€'~K/Buºk׫[;pïzÊl3z߀¢sˆãö’oXÐnÊ¢°ÎÎNÒsGTvP!ßÝ4­î¹é¿¤iT¾Àæ;X(; ïKƒœQ€ŽEÎÂo9ï¸W^@ù\AúÕ+DXlý„; .usàWx{;ÉÃ~ÚÜhOܯp!–A†€ÃW•üرšûFÈûÿSm©†ïéÌ“TÔL`9‡Eþ;Î×De‹"”mxì*½u/õQ z)&ClaåÕv`ñ3vRœ{…Û’OÚ†Ó/ü2áfyá´,™>ÈuŠÛ’H_Û·–8‚ë puY‰}´¬+Æ §(fm``ýsÉWçð‰„™Á7&Ùxxtè{øåܵæXFÝ޹‡Õ!YÑe[>\…im×ö¦Æ&ÛAhü½útÏ5:ž' H¬é¥|KßàÛÔ]| ö3ù}a?#Ñú=0èfÕ7Ú—èÇm‰ŽÊ;áàutwodÇ%ªl‘ø>v¯½Uø¸}9Ó–¼ƒ^ÂmIï/vtù`t‡’Fwlÿz[þÒnÌ:G›H±¾LKFZµ2Úôš—š‡°Qö‰;½ú¦'j>ÖöÑæX ?á¡öjæ¨~èg–À¸H}ªþ,vý!žÐf®ò(ª<é=Õ’¡Wl¤Ô*H"Rm®j¾ÓÞ–”¼@9|Éw¿·1U£lÝšgø<©M mæíÅ£r«>¾?'Å®qÏ_‹ñ7۞㢽ѱý©ûgÈ[ͬÚÙšˆÿ¢[óݽ)ç+³À–„·æ¦øm_¤Ö7€?ê’eÄQíâi•§ñN™ÔŠg‰Ú…‘í†"¾¯Xc8;ïñWž‡õEÞkärÒ¦gwÅ^mñk›\¾':TË={ý—ä—;V¹æ2þQ“n+z%eByRþÛµyÿ}ípÇ‹ jÒÿÙ÷aîÕéÎ.ÿзªíVŸÄsçæþ33£|=ÉK­É÷¼žðC»t!ß3<;m"r¹åÃÕÏ۬ĸñÍÔ¥_$?ëÔ˺“¼pôñŸ•cwKz;ƒ(7ZÅñŒK7Â+Ò»TñÓ1¼¹Ý%‰pGÔ?—÷™ƒ~RV’ð@u›Â4ŠÆÜÑ~¢ï<‰au>Ißûƒ‘VèèÛ^ã×FGž£í«ÒrxuVqˆmܦyöp@WѨPÒžŠÝO¯éGhÓÎ:5㻱˭mLGò×ÀuëÌÿp˜¸ÎFWl@÷Z:¬3„é3jáIþ×—X½Ÿø(ør<§)³I\‚8"¹¶MÈÆ)’gh7/›ß$¨Ëð / àŸÏ“õ‘¤LÚüÉ&m ÁG™ÔŽ~ûRÊp6Ö›Z<6×ðVÝjþ0\Vú‡íB#i^IU;<™e¼i>pàkË Û¬ñ]ÁY–æ6ïÑNãsqóÚ¢ÖHZgèô’ôÝ–HnÅɧOHo¤kW÷fì˜Ü“¾­“«=–Ô,›’XìÜh:ãõKuc³yoOÛüŠ Úu¹üZuÙtÚ+TBª"èþO)ðH=K»P”­µ ˆqdÒÂ=„×e>pƒÛD0TöT¢‚d‰½•\dHxŠ´fiφ|WâžÈæ²Çž¤mfùa$ ¿ºáõ¿™v>£Oë…ý¯Hê9{‚ô;ßü‘»æÓ~˪ ?™ém´™3F_uåáS _ '9}¹7ïgÒ.¯äL¸»£± ÛÏè˜ß,þŠŸ2/¸$ü«÷ŬþÏKõµ”ÙéÔÓù”½#mHëÔC€$ñQþ]§•0fS~ŒçC)_Õê¼1,b¹rÀtjl’gÔé4D rÄ2EæJ/méKùJø‹Ê¿Múi¸-Z´È>üðC³/ìKkJ°€Z]ø&Z°` SVVf¯¾úª³S¼%¥«"“¾Þ\Gðý¸õ-ù‘î[o½åâð/“s/+øO¢I…ÿ(ÄC‡uË”)SX/Z߯ܳý ,–Nb©µÐOÀµÒ­ü‹ÚµkçžÿäŸ.Ý“äªÒáÓܹsÍ¿ŒÏ· >ì´¶³Ï>Pô¡´Œúú•'"½|õ5׸ ïtÁÛª \å/}Âß~KCO‘©^Û¶m}«ô³±ò)ó!C†d¨ Qž|•úN.Š¥,uÔhUQžî·ÉÑ«ÓÔ¯b9k“#üöøW` ó?=7ÕAûï+ólN ¿¾V&üÿv˜#ZhÑýš£Œ|]™Ùúnç§T,/ý¹nqmŸcÈXFDþq(M?z0!_‰JIê.Ïoн#“6I0蘠Oí|/g✈dO0å0ß—šùaWõó·Œð‡œuÑžE Á,’*åÿ$C_?­&%-²üÕ/9²²^?¨,—G£,@µ0Ï¢ßyÏuᦤ»W†~Tü‹€Nb/#‚?™±ÀYE$þÐB@êrѱes$®%Iᓎ˜å¢RI·fSɯÎFÀ=ËRên÷M žºÀê)·€i•‚xi`ÁÒ6Me„oÃ’ ;ºÃ”îËC«Ù-´m”ÕS9§–¤½ŸZÛòÕ¹L R’`)ª_fxÏqkózÍþ²ê¦ÌjæUïCŠ=n£S Ú1L¼_dA‘&ézL ¬áøX›xcî:ðÜ’tJÒÂéýMœ”9AOG„¡fÿè}B›ðkY‡xCvÇh· lR(…eþQü½]ñÇÅÑ“q’Ï\êáÕ³ÎFP½[ø¸L÷ø{LÒ'yvµým =±T<¼Oû}¢tNZÍU™á`¢9Â,èá§@..‹_Jq¾ Ðt¿Û¼0±Û­Ì¥Ñ{ýšòê°×¢à?l8Iÿö‡ç…4Ýc;—7’¦IÕÏ÷^ܘöCÏìêµ,þkç­B@ »GZL-qFI<–ÙZ¤e>[º,Ìõ’8‹“§JÀ£È|ï~~ÓX<ä=5ã¨ÂÞ•ùr ÷‘ä—H|«×Ø€<%oè1«&ŽCÝ÷¬†8aÒiI°ébA+~ÇNŽœh—Döñʪ~BRÉ*OdRÓ‹¸¶QaÊ®˜þ!QÆw'XoFöGBWYVŸ`k$°ªHKû*ôÝQÑÃúUíHøNxàû¢8ùG7­“tF תÆ`ù3iýÉÛõ铼^vxT—£pKâ—<õ(²' A; ¬Vô°m_ò͉ði€kÁe^Å1”È~g“®÷ù=I@.oã{…wÑXhìÕOèWHS¡Ž%»ÖÁ •'‘ÎÇ¿mª/µ+I/íÅÊwÂu¡º„§ß²­Ž…áû-O|`úå=»fq¼„¿ÿý¡öVâIæ…Ôú„B¥Dê&¨#I_÷D}F!uø]Áu¶oñ)v|ñùö:—…ݤí. ñUŒpÔC-߯¹¼Â;±»±×¼¤¹Íã"ªSªïÅ ðm‘ÀËŠž¤ã+’ÄÄîië‚Ê›ñçl]©F)¾Éó+ÕðÖyE¯Ø.ñ:{`Ú•â’tkäTÏR½ÝÏþFJ/Œ³Uï‰=óšé”õ%©[mŠDàÅrøGm®Žï¢–Aǽ­ä-ì¶Âü¢‹O¥µý¸ã2ÕUåPê•o¾d;Fη ¹tí¸ÂÁ6±à`»½n¢}ˆê„‹ãùüzwlßÅôyŒ©q-¶yHÔv*¹ê;çêý£ Å+±‘©Wx&ù¡gŽ_Mœð ó_ØÅ€³á3a}ãgZ }ö—[u!y»‚¼?Fž?ñ=ðO¥HÇë®Åýz,ZðýC­Kå)Ø¡oÀî ~‚'0éS{ñ=’垪ˆÃRÖÔ}Õv„'áþ<ûa_kO¡_yiñ­äa}Þë°Ç\2üŽõ£#ïÀÓ¼wÊ÷âð6œ !¯Žº ź=Œq‹Å¯`|HØ“HÐö¡-¦ 52{_­SM=9¢æ¢[òÍíy^IþT¶µ¤r@®YF%èN¾ýˆ åÿÓ^GÆ—Ø…l2µ)z—d?nÓâ·ØÀÐFðÀÉDõ¼]ÏfÏÙ|s4}„Æ›–þ“2.ïÛ+TÎVS;ü’/øh}ñmAkUä}çTú·ËcĆ_DÊ"2ÂõÔ¤,@­ñ³9#Êj»ÜÆ£Ù€Y§ö]{B}f¸}ÊÓR§jêÛä÷^}åŠÿá¤ï)~S›eú’¾u×v;ì ,»ð;»Ù@kI'Ñï<°‘lîâ¼”Ó«X•»uÖ€éRרi^{÷tjÌ_âQxûÌÜÑHeRÕžê ì˜簨.ì‹Â7vWÉýl>vpØ’¢m@õqW|î%=¹¹Hó“Å8HAÓ©ÌW'Ðwj.¤Hð%‡Y§ŸÉ³æ @y,üÔ‰K@E¡ï=~·0§ ojürT%DÔfðÂcùÿûõà°¬-–É¢ ´¤ ~ÌÊê}i“»îœúwL¯2 ~8ÚuS…õs¥”zÄvU|áa¶y Á¹«ñ¼±€·N,vª n¬þº„Ý›Zª°ÁÕ}<¿9ÿ§ÊN:;%-˲rÛèX»Ÿð·:u êI(¼Í¹y ZÚ†$åØ`˜/ÐÝ]\TíüÍBÊòÁØ Îìý[—¯ÿÎÂ#¡k•Üdc‘\S{r-_Ò¢>Ŧ±À]½~Œ…ô\“/v,ð}ò,²ÎÑëmdØŸÇiæH¹ãÅu|]tcÝ­@M@j¾^â\óÿH¿8½t‰=…úI_YJ {K ¾,µq¼£º)Ãt¼_:„K¾†O/áyQ¸§çÅ“|Ü[klŒEÏÚy¡Míüèòv4y¥þ$½#P2´µ]>Û£G…:±fë`ÅãS‰Áïo5“¡ò/jC£¤¡|ünb‡!÷,ÒzIÍá}ñ÷­=ÎæÃë\˜·)ÁC .°‹“­PÓšÅ醶ŒMäûôsê'/Ø>iŸ ÛÌ쀾w×Y4×Ùu…¯á¯·TÕÇÚ„Of!ZEºß²Ù·Û]6T= ýáC·Ne±¤[}(Rפ¤MU^¡øjWÜÉúl­è2/ñjí]¸@wüz¶'ÙÞ„4 nhcßmÃ#'aV9°®á[m£ÛS,3(nâŸ'¥rc».¬úEïzò[žà~:kàΨ³ØÔé±”äñÆ€ž/ –€”ÓF€^¡(ˆ>ò ÚÁ½ ¢ò50™}SÅQ€ qâtß$^«£Wa¦¿æ"=]¬æòQÕÃ{ ¸—Žçþ ÏÀÿ~ü¦s4uë¨óBE[nfW½WòÊ >ªnoRÇ£Å*ÖÜá±ÞÔ[‚žãº³ñû7ŸÝo?FÆ’Úâc­‹TI„ñ‹¾SOêŒr,PL¹kƒOR¢â= 5—ÿáðǯ(G¨Ã?å>–4l ou¶¢XO{«t”X}‰­ÞÑÚÓgüÈ%q¯”L´-Q­qJé4â&œÚ»O€˜ÁgÑ·^‡ÔíÜe…¶GíϩՑ§äï0¾M:DUÔ¥; སÿ½Eìb{—ŒÇª˜¼_Ìï,ÊúÞÉc JNJ¦JÇû¯Ö»öhú*œü©v$oÔ£5­9›™O¢µ¶T at;Ò6ò½Ù†Ð?_‘=^£ûü•»¬s„:(¾š4쑊‡‡Úƒ6Bô5cñy! ÚpÏ!}nu„©èJžyOÑÄ (!r”øŒªþ¸Ã{ïní"û9ó)°î^êë³à«îFÒ±­ÕÑ{Ô‘´¬Ï÷>啼§úRwYXʇk_ê›às‹Ð?èAÚÀ4 ¼…‡ŽŸ‰çT±´ç¢GT!Qþrt;­øpëŠZ£Ÿ°_Іvl×Âc3ec¤Gu}¬“ÄŽœj#«îæs·pQ­ˆ ¶Ë˜œ¬v^<¯n@ú0ǧR€Qqô¯ë“·%ኜý½.L> Pßµ8ùi-£ŒÏÁn#~)BÔ’/òÒ’z‚_ê.H øµö2Ò¸¶ËR.[ðdŪ6Xs=æ¥)ûÀC—˜ºž!`çÙ0Y«¦ÓÇLí›Ô·½¬³”?¹×8=áÅÍõݼT…KY^×.@™ºÀìÆ€«‘üÑ—õg,¼†¾óàŠî6%9Ϥ>lºê7›tßm7^ø–*xšN(B.ÓM 7 Ð–´‡p;?º8n<ý  ’N^¢³=¥6·Pßnë¤}—pÊGR¼ûÓ~G“÷­˜c1š¤Iz}¶møîÐðjêQVµn4ŽusI¯?W7 €Þç7/(j5èàõÕ?Œe-¨~È£EôÓ&áó¤ Ñ(ú¬ËésT6‹ ßÐFu>^u~êÿ} ŸÞ «;Jt2-•ãð ÀðP·IÏWSìIeåämgò6¥)òøiÍÀþW¤ÓWÁùšgë8Í+©”…ÿØ{ï½Qqe“.pû'$I]¡Ò­+@N€iK’Wî¾nàššÓQ~}¾¤U%Uì_Èæëqõ߃qÊ,°ñãÉö—ï]éö/,ËåGÒ­}úôÉåd×_½Sù PUjzöìÙ¨ßwÜ1#Ã]ºt±3Ï<ÓÙ+/¾Dµ,®¼òʺ€3"à%Wú?ùä;ÿüótµ@w]Þ¦ËÌD;/½ôR{ê©§2Ô?Hb[½IjÚ×,• &LpÀ¼êE€ò¨Q£l«­¶²Ë.»Ì±.ÒÀ¿>ªo¼+›þ@tΨr‘Nemˆºwïn7 k¸YË–ö’Í+Rþâ}Ibû@½ôK«þUî+Rþ܃ª=B1ô51áXÅT@|9†2·›çÑüÄs|W·WÝH—#È¿f¥¥GvWÛŒ®ªŠN;h¯‹›áWÓž_R©i¬ßJyI?XæS£öÔÃlŽzèHho¤®þIzTF>@w+âi,x¤çJe* ²•%Iï*´›š†5á#¥ø9Ž÷uL W”$¢ 5VÉ`Š[(¡LêÆëgi+]‚p2eÙ‹IÖŒ@Ë’~U]õ­Üûô+ýÔ4ñh ­‹§Ÿšâ± ~41;‰Î‘,8“†`¾›ÜI ä“”½†r;3М+î'G‡0‰oØò:á}n® Øi©¡)a6uAaÑw p¶kð]€ð˜iЭ1ó¾ðhÚ‹~CgÌ‚555m,\SÜÔ%Õ!õ/Eδ² (™'‚ŠÈ:¶&ÒÉÁEA¯ik-Éêùºo€8ÐgôƒÝ²€{éî’²;‰#ŽO@ýéjjް»ü¯F.°–¤õ:(õ—A6α{ÑÞV£ÿz,w…&àZü: JF$îKaÍ[×ÀN`n ~$j­örì¾ã瑎†Ž%=Â[¨ˆ?H¼hFæbœ×M¾.¸×.@²¨0@›aäHàCôx³XÀ¿ è¥^8Á¯€Ak¡µ­he;³@uƒú~Evu†žvE%=Ò×&×´ÉH‘MðDË‹,h§p)ÊÔ¬_|9©›…é@IDATÿÞEs–z¤Õœ±Ž½K¢¯¢æ?Š:MZx¦/KoŒI"°æ(ÜÕ²Ô+×÷4õ/ ÇØ üÇ3 .?²ê3øÞ1˜Oö€>\6C†ë’ÐR;¼ðM&¢]°!Öè•ä5lH-œô .¶©Ï{K+¨X‡‹HæÕô§­\ 8ÊÜ7þ%?ʸ–w+Ãþ,_<ÎnhÏ%^’æì\[ˆë,øÊŠFb·¼µŽ q4tmÛ½Ù×vPÝ‹¶CíÄ5‰ùÑ/€‡¢§ú!JŸº!]íe-ªŽ±ÚÂëíÏêĶ€r›NùͬÇ"m0R¯ÏÙÄ÷RTygœÐœõ!iøaQÒ]@9UìÍûOd^¶?â–¤¬£ìÛòqÚlØñ%6‹>¶>Ñ.Ö)ÔáEð®ô¼º#ÿ­àqʒ㼦[ÂE±»p›Mº®p¯:J݃Ì3ð­štI²³ðuk]{‡ÝÀæÉúÞ)‘ƒh [ª_Ú‘<_h=›-´ræ8_°p`´`ëõ¹¸îfêPNà·Æ^.]3Æ…½Ù$øû%¡w°úze@™9 $6Q\¿ÚwxBµšC8pj=⛩©o­O=Óv‹(Ûrõë”—#xFíŒÛš”u›˜Ão.õré%Ò \EG£œž'=عcõ”=·Þª>צ“Õ|‰u‹‚ÿø1ˆÿ:ÓÍA~7¾½AÈ–(ÿð)¸ÓÖ]ÿÃâ›þøj$‚ÏòÇ6·Q×—²¢^ÝæÞƒ>а÷¥l xR¡MmCøi~±Ê«'釷ë®XK ÿ+}Ã:|ç`êólòs¡JøŽ¬5œtaÑ'ØgFÿü`.s!§êà†° ­§v¶ahs›jgccÛ-ÕûÔ…´×A±í¹ªËm±ÓG[L°ßIû®”Ý3©(2zg·+­óWÊk–ëk–Õ*mZÛ6¦L~¦ý‰äð€øeÖµðaŽa·#_[z¾B»Q_·P6]yWziøß»Ù;²fŠ ŠîO:èÿ9ÕâøDº hưê)Ô=|“" xP{©K¡›c¢JËlÿ¿—•Úž|÷”Ø)¶fÍEv,Ç÷—T¶·ÑÜ¥ðE{­îxëÿ¦þb7·éò»uFõÇœÐÎÔÏõ$koâ¤-é⸊£á)ø¯î!ê|j*zÀ7Å‘¶1œôºƒ¹vÍøÅI3í©Z.9„¾³~s(¥-”wã;÷’MøVmΧµƒ°\sê^Ö°–¥ÛüÊ‘ö¹ø2AÙ©ðeÂ#ü«AÛLshÜ—`G{ÊA­±Ïw.@êòçî²: w/V(ûûë§0eáúø r>"äõ²úÐÎýiÞá+àR ïa[r ¦6¥ÆJGï7èÉ&d—pg»™6Ò ûô|£>¦ü&Ú= Œzêžßs¡>´Á[§þ€þ"z fÆ,0³>êÜÚó£Þ¡#ÿ÷ ¿¼q=ßœØyLý£Ç ŒÄA™½võ4’ªS-57*‚Žäý¥ÀÚD>5Þk£"ñ<ä€ì_å5'iãy{Êç´®Ž–RGð–úmÆ[ dÓ jaXñ«l˜Ž¶îð$ÈŸßÕÚ5o9°ö"øP¾à·;ê*tÑšNØ-ÈžN_t íP'€íDqÿJJ?w=ÆY[ƒw•ôoÚxGYþúÈ ¸¯R‡õæ\W¥V"ŠU„9ÙZlÈŒ¦ï>˜‘U÷ꋦÒ3‹Œ7 üSà7ãCÿ½äNÿ[’ÿO€Sí’ž–dt6¿¢ùø.PßWÑÑÔð+š~5ê~ê4r‘¤«¥Çy‹Õ×´²lÆÏ ;©‘®ß| Ú”ôg¢]Ó/nz‘íÂü«ª†Ö lԀפKšwUnV×Ñ|´Ö*üV¾o4Õ^Ào6û-É|1x$è_ƒÈó Ž‹±lO¾ÊÉw0Üÿ¦YSЋ Ïf¢àÓ%Lü|éßá)¦… À߯ž7²À.I—,º˜x®*ϲÏ÷z<ƒþ‹”ßÊ”›&ÖÁ‹è¤—IK„wó´‡|i¨íŽ…ãAyϼÙ5w¨)LDOc",\ UK‚K"\ôË‹×g*ÝukóᢼÕñ –Oã7<ý¯ÞƒvÚ5Ÿ¡-“Ï ùð° ;×›ÿ°T„½™ìi\Æy×J±Ñb" HÒÒŠ¥D ¦ ¼þ-ºƒº}~PǪkUÁkê«€"5ƒyƒx2’h. ¬ÄCUéM’TˆM®d¡’+òü ¥­X|4N1æ21¯çá|þ?€§æ2ù­F3}²Dq¼&[MD¿ ÷ç.…©õž ßcÙÓ(¿Þ÷7¼Þ!ÞÞ‰¥m¡>Pn“6Ž^c’›  yúà|: &àš”Kç±Ô_s8`ýŽÝoÍÄީИ…[9 ¢V¹yEü§öu6`ò½-®/‰‘ßÚÓàMîX`1åÀuÞÜѹìø3>Öž·² ›Ì¥:µB¢§Mâ;«âR¨ß¬‡X`K"*]/^îÔñ¤¡3 £ 9|Õ[éHæPúv´ç[õ)“ÚÞkÔ²øe§1¤“Ð#-¯=^cÙÞÜ–v€_AÍ9€A_§ü,ÿqñ?‘æ"òå6¸Ö|x 0e“t\ Þª÷òÜ"ô; @‚$¼æj§¦> t÷ºlfò³´xªGétä6B®ÀÈ(Wü¢õ¬ìâôŠ;3áÓˆûqÊ» ñkfô™MBšå¢Ò…¶¤úQê^áø™À¶oko´x¨;Àì÷,L´ãOÙcá™\¬´Œ6ÛÆÎ”„SÀúj¥Ñ?—Î_$¯¬zv?ÂÏBš®Ôÿµ¼,l¨Þõ5€œtõ¦¤€]ÚsýCˆUî‡ËRâְ݃°Úsˆ÷{Ô…­"i3µ…42àWC°7ÉÓ0û ¼È†2Þûëô’o©qRf|#r¬¥ºˆ…x=ѯHRžËáÀ_ns“‘Ê.$í”QíéØó­â·03f‰ï¦F×´1¿çIvrüU;€Ëÿöª`½ç»Þ$]ííÐâkìaT›à™üwáIZœŠŒ)Lþ=P,L~+(;âw:&å¡=‹‘ùþt¢g‡÷#‰{Ý…Ÿ#¿#[È©!él‡¢£¨³sQ…v@Ö—á3(ïØ©­øDž‹^§>úð”Z7·]Û¦®t F£zWæ¨k"!ú›¿5ˆí%  – , Ùè8´ðI;=²=i{Šßlò=Úsâ%Æ™Ÿü/ìÚ„Ù ’âÝßøY‡’ù6¯bGêfößn²BE»§Ä¸Ô“ú ÁK\þ9 iøDÕ­vdâJ\<êÿÅÆ­ö·P~>ñk']dOTö#%5ô!ljllx8Ÿ£lÊ·±­š}dQ.Bý@R‹QúÍø#ŸµÉ/ˆð'~ð•OÅeägï­úNÒ7›´v%¯Ä=ÝZ"å>,0÷÷ƒ¹²rÒÜjßðIÂâ©¥6 ~&¥»Et‚͈¿ËE®›SOç}6ͽŽt}ÅoRÓüçñÃ^ íøaxÞ­=›ù”ýáÔÍ7˜gÖ‡ŒMÅýRÞçàÞ“29”å¬3ó‹¹ðк¬!~Òœ.Jë4I Ï¨Ä7a¨r/ˇé´DŒ´±ü‰v¨M1·Ñ•Yˆ×d¦•{Xû!ãSæ•?çp÷­Ôžß†$$ÀÚÌî¯Íì­ÃSŠDöƒ´V~‘ùò{Ì]÷*xÒ±á <¹è ×áèbÞ-ö|ó*|< o³ùµçWƯ!i~³›W·$ç0o‹ÙV¤Ó¿Ä[y»õúYôÕôÎŽ´I©´i½áÓ(6ö¥†åúðHÒ6Ï.MNeÔ‰95~¾Ÿú'ý cUŸŒ•xèŸQ)ÁÅV9ù«2bzc»Šº¾œàeדcäÿÿ ûÿ) HZ—ÜýSÐW_8º¢ oþ”Õ»¨{R‚†ä¼TRRâ¤ÉË0}Þ y”ŸÆ@ß¼—ã ÐWC³¦‘ÙɶÈó®®=ômÖÀggtM¥lз=¯OÚùÏü–ïòßù¼Éuzµyw7ƒãâ”k·@>ó`Øõ"M—G0©Í¦GrØeûÑ;K˜ з9ïvPUÕ^} òå}e%øØ&1q,Ëš\hpÌG*ÿB··PÂß*àQRl¾Iv€}’þ%Ÿ°øó)8+é¸ Ê}å6&ñGЋ3/á¿ôtžÂd4›4°mu€&6š¼ê"µé¹è•î-‘xc¶:‚ÔQõq> X½¤oCÚ„Iî)뙈 kàeK&Z«‚ü ô<ÐW1þm=9Χ‰[ú0«§öiã‰äEH“¿¡QL¨»q<Øö[ÓÑ>êEUsOµ$(1…ßÍÎh¨ Ø„EÀ©L@s‘ã´ð çÔ·ÐW–újÓ¤ËBMdç—«‡nAjÎår'ÑÁ ç¥6kÄLi3èRâØÞ €HpCñœp›z­ÞÛ¹i¢=;2Ú™õoxµ¾u ³¸<ífȋ肓ìîðlÐW´œö¨=e=ÝýVåë<>‘ö*õ¢´«(ßð$2œUý¿ª­0g.\ÒŽ¡íYX¯ÄÙVè~Õaµ”Ž,ä¹ÄmH<žîYÔ%—¦ƒÖÖ¥Í\Ã„å ¬²z=Ž2»cøiÏ¿²?·.¤õ$¥Z k¤>ˆ 2 í+Ã9 è úÊâ\FÈ&Ã< Øó)ÇwßAjZcßEѳìÙ0uœXì§P…°¶›˜°³•„6Äþ(øª&úÊ<ÿˆzLHÒÇ–þ 7¤”p¥ËLþD¤ª„™ h+ý€çÖéËgj裥®¢/@ÙØ@pÀLôJ¾GûL|îB›ÔèâºÈ@.\[ÛzTöæó”ƒ#•ù=öCü{õ/nÁ;ÄìØô„±Ð窟?‹ß»¦pÙ_ ½Ck›Vº5ƒ‰ob£}¥@_+ßÐ&é¾[èWæƒHËÊËÀÇI”ó.¤sv(ÏZÛ‚¾Îlð+úN<:\@34îw'"ÐO쨂x"Ý0wÕX~”£.eè+ª¢Õ½í™u•@N@K×9éý¹Ö]¼½C^?&ÛÒ“ÜRü$ñ¼Â[5—ë=@”ª º$«íöÂ=lº-Šémž²= Všæ}+<ˆ4œgŸ4{Õz×=ÃÅwI{TýlíÕ¸S‡ÐìD•}”x϶® m„O¡Ìþ–1¢Zyý¶¥ý"Bž"È»÷=æžVt¡÷® Ú¿úv²¾ôz{†Çÿ±¤•¶UÒÆ^ ­°sïÄU;;¾!ÐW£Cœ²‘4y˜ü×]KùÐÖÔBj†ØtÔ°Ü*€ÚÐ/m¬ŒòÎÀ›ØCÑÖ€ƒ•¸üJµ'|gÌ™T/ I?*µÄ+ý–Ó7u³dŒË¨o›®kb»P·»‚?aÒo[è+âÉù<—Ù©ëî2¢õ'ÐWý€:G¤£ª?¦2|® <Ù³Âî$ůÇÒ$'·*¨QÚ±Ya‘#ˆë$Ò¿–«¹ø5ÇØÑºìO}}±x »qb¬§#ã(d-C' qJ%g`óÅy¥EÂGÛwÕ;sþÊuù¦ï¾X“ÞÿôÃô-úþz——&þeâ£í=Žvß¡(‡×#›ØÌš·Ð«ý€\H^#mJÑ ;%ÙPêOÚi‹_AÏÇÿBëËåƒûJÒ^ºÇëî±vl¦Ô„?¦léS‚ /oV¹õ¸.Ï܈ÿK™ çºXñ×¢:çoÆô=ÃÛ[ëº;±/Ó†.áYÅ/@nœŸƒõÍ&ãFRó%´¢(~Û~Ò¦l4yã•smø¯=›7•|@¿%©¹u+Æ Úšx2|FÃÚžÀÚUAaø¹d:Yx¸Al÷3&žšï¥Î¯')Tƒ‘ÚóW請µ<¯lXÏ ™úÊáæ`/³)¡½øæ‘ô©¿´.ª:޲}É ›ã¿æØ»1'5†‰«Žf§|•¹ÌÄI4_lj‰5 ;†{ —¤¹SyÓeÇî/¿ ©M^ï6½.pmöÒÐñv’Æäœ´”ôí‰Ë79]WÄR©˜Çx¾²ê8ò}Kë›7à;Zà*%Zç6}õ™R+…UúÉ?2šû Ñïšàü?L —nÿ÷FV×ÿ_ò¤XS²šn,3V’–57'°cÖÀq9e)à|m:¨…PÔræq^Õ\žÏ8kíx2åÎI’ž NiÓããG Ž´1šAY5vü{ÛTw{D`ÇÕïv¾[cÏ[˜$øÒ{Ùþ"XŒe(ª…˜5д&UÖ ˜}£Òïë]ˆY‡ÔY6ÎSßÊEê…ý£ÄQ¤ý˜Æ6 ]¸•9Ô{i–žAï}ØÌñ¤úê£@ì«×Žì­ „–CÒƒš‚;侦v/ÝÿP¢º8K¤£ZþÅYÒ5¥4k!šIšu›³$µãç9õÏ¿„áaÊä²¢ÜúW™tqótctƒÒ+@º€,åºÌb)yÍÞ0z ]ž¥¶WlRŒ^Ë×¢pEè þ®)át9Hîš\LG{ôr£P[Ò4ÝJ[‹å˜2åã÷\q8»ð@&{' •’t7aŸO°ìm@KiÙ¯Õžàìà×盋v.ŠMLûW+Ü>|7z “ξ#Ç_;'ùð6­ PÓaw$ÜnZ@9šÇ$þÆ´›\Éâ÷§CðDW1ÛœÖÕàâAé%õTFY[ÿ0e!|; §7”šºØ]¦²uÀGŒq"CXœ ߟí(dpòÄÞkZúN’h¡ÖéHßHÛÀèÝ¢#Éëë„7ÂAR{ÖOm` ÏA f$ÎÈÄË„½{8­î&âºÅó“ñ?„ªÀ Õ½ê]$å•ÞnT©…Ü8ô麾{Q}›¤,ãwÛ^€˜ÁçPÆ]`y¢p$á$çßEu@ŸÚ£Ðñ§©¾GšìO'ýóš2^ ˆ ó=Gë³} “FŠ ù-ªEÐ2e.K=Y¤;ð$Õ×ÖP7R}ÀeX°,w¸åŽ·ó¢Íƒƒc“0uðÜTžî·Ï?]_z‡€d]æè;‰î…‡XkŽ]ˆíêµcmG€ÓvÑÃíhÀË$7Ž—‡«à²$*ÚÙí‘Rn—_f;rlzãÒrÛéÀƒ%!XÙƒ¸Ç¹˜n`Äý?ìœTÅÒ·kÂfTQQPTPÀ€b1g# Fs@E1cFD# æœÓU1çˆ"$ª ˆ"lÞ™ùžŸsfÎÎÎ,Kð¾~ï{k³§O§Ó¡ºººººš²uA8:‚#¥Iç_æ}ÿílí‚^àÝNôÊ“@ØÜÕ:8ÁÚ, “Éû߃:E:¸±;\šzÑÝÀ‹Rχ´E׋Ôßïƒ_£¨ßæ|£“—61ÊNdîúÔijÆGÉ^½ÝHÚ/pìÈ q@Ǿ݉ ë÷-ÛˆÅñ–Œá+Û‰¥R޹¸VÉß]Àä÷›|k)ÌY” ÞËÚÄ.²+оÏÔ&Öº´ä⯫]N:v)s~»7:×®]Tìì}ó1»!û–‡QÎçùæpÚá 9÷h V`>£—=ZóßFõ(šÈ³Öz.^ÙZWìÌQrh€ÂÜœþa³ø ÚóÝMè‡ëÜ[wŽz÷­Š›1T{ÚÕ“¬í´®ðšM$w‰brÏß©ßsô5\R y©‹'17bÕäåp€9T6uµí}RŸýN)¾µÇo…Û¸¼©Æº0.Ö(¸Æ¾.>ÝÚþUä´o] mïi »·ô¿8.¸rPÙ“OÛ®\<6­Tå¢Oª®´Ù\¦ø3ã)ÏšÌö{‚Kí„{Ú s°ØÚøõ÷=2L†XñCôS?ßo’÷ÄÌÁ•¥¢¥Oj Ñéj7SÝšæWÖ³ûb=ì(Ùë÷*¬þé#‡Öß¶Á~ï^uŒÁè ºk€ÝÁi‰>©f¦»Žen[äøö:ÒS ~ï$_²v?<:Lø-ˆ]Îïb.SÝ• ¢*·J+ó½‰ü‹Ø&‘ˆÅ*6°­ÑpÞí÷ÁŒ©Qh€ž™øÞîâÄÃiÉùvf±PÝ"{Z—2wÏ€þÌ‹ìJòyxFß\ZÈ%•25"ÜI½Éì‚’Úv˜o1kÇ/€ÎvqÉ“|ƒ2§ÀÉä Qñ/¢Ík›ÇÛ¬èljýIyþÔb'*¶]i&¬á›ôÚÒNXk騾Ð[Áa^kpBÇñBÜÞŠÅÔ×ö*cÏãÊÙ<ƒî¼ãèwÃòùáþ‚ôœ´6e»01ÞÛ¾‰la "ëÙŽØäõ Ò£Ñi¡b&: @üOÛvcû<Å¥^s8ù7—øÛQÚô¯=‹È3mMµ]N)ùVWÙ‹Ô3Ÿ´BUg*¼qšêõÒ­ úíDÛ=RÏw™_„ÃUÔ¿öÑY\0NÂ;06GX=¯Bès¤ÞÅó8ú{>¿…õ¢èåBüZ²N?&Þüyá¨S‚Ñ­öãÓÖyaz&ž¶SôX›‚ ³=c&Ì!àM6O惿âÖI£ÍràÙ8ʲ·„˶ЫoË ¢/ðŠy@-·ø‘>”'ÞÒzkü¯‰wÕÒ&Ì_›/½ëI6rGÌ5ªsÇüùÖü eÙD“Êÿg°~šä6^pÙDù/ü³Z ý?ß´1°$B ]øÆ@£§±é!H»¢\ßÒ< B‘u|„%Ô¼¥äŠý¤nrÏšz¥ç'æLGÏVÎ9Ë?ýV)FJˆ)á­@Ètç—`'`–­ *¿º’F± ÄÎJh+!k˜=Yÿ} ¯y>¾âk·)]6´çøs “FòҚЅeÙ™òÞ‰:†A£á@´>_tqV.Ð ð²#­üø TÏ%ÁMötü' |òi3qZãm×ïÃxÊlBöÅ}Ú¡£1Þ°•y Ü^­=®­äDŽMe0¦1Üë]ZRȱ 0ƒ¼t”\ SHœ{Yÿõg¬Õ¦Àz¹Hèi°xô3ÖEmù®úPf?Ge§`gV¥yC‚¾èør¸àι0èg×Þ‘Ž¦Q¨[Ÿ×uBP1¯EÖ“1ÄW{¾Í- î(¾÷&-Ïž×5†=ø%ƒE5½Ý¦’lDŽkʭÃ#Õ60ù”à ¿Šè´Ø*l@Z€¬ßˆ[ìÆt/û©ö$gG#SL·4ŒÂšýÙñÓïåÆÃù!´ñ¡{M/\¹ Zð|D³n©õmFl úñÝP±péìü¾„`Ò²ÒÌ =ªKÆœ Fö ‹žBHªrW’׊‘†mÑÃ_°Gú¦Óšž¯½ÎÙeô.jiÅŽ·ž„œÃ±éÑŒ·]ÀSotýÌB–¼%"ì@c'€“X¬uEа'pIÙ]´£SahƒäBòÎ ±3Y4oÅÂp0u“Ð{–ÑJ[§ ¡\sœwæ_ám^šŒç’ð¸ŒQ=%¢Ù•Åcõ¡<çÑWW€Wmhn-HY@«Í£GñmÅû ÿ[xþÂ]•g3~‚ÞCù8èHš#™Ôö'þÓvA²V|=ÇýÙA5·ÙýØi^xªðT”‚ß·h ~šüÒίå‚ßJúèŠP¬k¤Š‹ÄJ°Ë·õ¿ž:õ°.ÛØ»qµW+;tQk»á®&sц²øÍÖ¢ìM{ AîkŸš¬?šÜS$¤u¶zÑÊw=L°´ÃKßB/±Â¾¬ì†ýê‹ð4´9f gE§ ìß ÚWl„€sêó~â‡åíyÊÙ‘½,;½XœFO÷Ý<Ðê´ª”ébânçùGO³”`a@ u—¨mÝû϶s5÷T¨ð=iJCé)ÐŽ2‚•Œ‘*úDµ…çâÿ“E/YÁè½6Ñ|m¶ÈÆ<ß'œymfg«¹bl8.g/ÁŸ:ɼ…è¯íÔ»šÚ­êzÔ€72Qá81860<ÛÛ›ÒDïÒ†÷Ó6ûQÏmíC¸‚Hl¬]¤‹â7Øùh‡ëHó­´] aäý×°ÑŬÌÅyÅýÞ’ññ= |:˜ö<“·*mf2¾-1‚2O±‘±³Ü%íUóêQ Ó(ÓPžoSêçh¢èƒó*â;¤aZ9Àùeþ¿µcx.KxµÕ ¯ãõy+’Mà40€ÑÅ㳟SÚKàlŽ—ã¨à÷;¿?øÉ™4ÛôÁÝöhlD’ÔÏAÿ[øîàá‘‘®vWröu_! ^hݬ&ˆŸyn ~\¿ÀÍ@}t©Ñ–u¿QýÕí;ð8íoH°ÀúÇoCÓwm;!Zfݪ{“‰¾Ë˜)í­™\åÒX’³=¼ˆnê½ÿe›šïðŒÀX;´!à¨Ow´@ï­ Œ8Þ  H/mr6ä6,ÂŒLQ?ÜÐke?Øh‘ºÓ Åàu§ZUò½ÝÇ8¿‚óe/Ö½f÷¥ ÜÜw¾æÇâ¡vR³ûlÌÅœx}ô`pávʈfÿâŽÖ šq—hõLTÙeuÏAC6tuÒV|šv»AB*áˆN MåÈæþã¥bÜ †t$²u¾ÂÚ&¦ÛãÕ×ÓÆy‘'ÛpN ¦ß¯‚t4”“UÞ™ÑÉb¾3÷vürt£îòï ×܆¡9yL𷍡ÃI´Ð.6TËÒÒE†Õgá3?í»Üm”¤ið©©_ÑÆqÎ5VÍ`?jÎáù8?ÚÉæèPï{`l椛T?³‰?1ÝGŠJÝevËê§SHC¨d¾ªâÔR%§QZ¹M–†q<i o U Í—ØLÉEÍâû¥>¡^ÿn$BÓ‚âÃì’8ô§hšÐ]sxCµ– ¶¿î‡vÉ8\ä.å’Rý.¦¹àë&4V®tÿS~"f?Õ ùK° Ù1ÖqS}¶ïß—¶D²rN.økz ƒut?®`|Óì4Cþ`ÜÒˆ÷ØÛ&DÎE¤&‹EÈóÎ;ß$ÂÔæ47—¦dWe {ÂiaýrBSŒ÷çL¸Œžê“a‰d0_K i .à©‹4Щ²m• Š— dÇÉ%ð „¶7aJA ¶[Ù°Z(o1”j›àøþg¤qnøÝÊ.ûH˜H}CÚ AŠ‘ô.ŽÛȾð5F6i·…¿'Á³ìýŽõµU¥¥¨…Lc #°áú—ðýr?Á.Òö‹toÛÁäj9€ŽÀ q÷ma´´È¥Az B¹c|ýùÂ4UƒSé¯lû\áø{F¶ ¿:·–<º¿]uÚ.I¶\—.Š6ÇôÇBWþpºß¨OXã:æÜñ± ¼ryÄüMƒì° `Á}8Œ%{&†…˜i߆ŽuÉ+0I±†£º^œ¦ìˆgç&a|.`V»a˜M¿—ÓNêÃîYcB7pgF_8•ç^˜bQ˜ 8šy ‚ªOèGѱº°Í;Þµt»I ­bÖ¯ƒùưÚs§,͉])Epl­G>¿N —Ì.+zÖjC>õáöªmípb,6÷¤iÔ£“ÓYª„/0¶m .>‘呸2âì »þýíBmš8ûëQÀ‘íMZ^«¢yÒ(ȤIú¸¥·pPü~yé14)|9M ¡ÄÞ3´!ºŠÒ°jé-hˆvâ•¶ÎDÚñ8Š‚÷Œa™žÑñu—Á ûÐ…Ð4º´JuêÊï@NIÄíãXŽ÷—Ù~ØÂû¾mXñ½äqŠ[|©$Üæ‘4¦TžÑã »ÐåÉ ßB¹ïA tŸFyvqçpìðFÞ)º–›[;¾†ÙÔëìDcc±ˆvùšI›º¡!'Ø\é„”?šÌÕÏ‘µø§vñà@êuÊ$z9íÁ††àrè·ŽõšVü>¢Û›E×[µcTí +¾¯ÑnÜ î„Üüw—Ÿé²°‚¡zË@d#Ònè½#ÈY»ôYû¡î ëÛ¿¯(Çûv9æŠz¦ŠTÝdÏð<#Y‚ýÊí­v?Ë7¶+G`?tëÃ8¬eCÍb󈲘ϴqnWÜ@~s%†¶ÕÐ$“`ùíX‚€ÏáÒ¨G¬ í§6–Õ`³!Ñ-í¬ÒmPåá‚¶MÊW³Í"¿Y·Ò鼀úÞå lÀ ¢FbÒãGqM1L VžÄ³õÿÑ”‚#¥0¾œ6dd+¡„à”k[ðëäÈ<6ÀqµitB™Óƒ ¯´ò¬Á-êÜÝÚNL¼a‡ƒƒ«ÚfVXðaWXDzG¬c³Eö@ÁHÊ2ú~âå—x ÷G˜ò¹÷+û¤èj4ggÐî_ZGávÕÅ´Ý1”WcVåjàô1kSúœ]SÅw¥»)S² \,Ü€6q‰Ý5|{ž£bx Ôלp§ð2êpßo$ÜJ½ÁûuDÜÔR˜:šz’ùË£ëfØz×£Í9Å:ßÇi]­†æ*eú42ÓZ!XÛ¢äs—÷Ñíàê훂é·SøÖÉíŠl_æ¸ÖÖZ”¼I_…[r€.Ø›ú¾k«0Ï–wÃOâÍ”íëh9T5m÷2!Їƒ2ÎО„V¬o§"Ⱦ‹ ëìW[Ï+ý‚™K>ê¿®‰ûvÊÅ;šÏï¡­+»±ž½ÒÉ”„Ði.Í)0óÝa÷j³@”ζét?,xh,Ö‡Ï ±ý0¯±!í2œ6Ñ%Y““àuu—Ù]HXúy¿Mûõ¤óá!µûh±“Ñôžk{³^„OÑA¤ŸC^àöë4Oá0c>×}–|žü)KÁœ¡ŸÒØŒnf"ì‰Øê6‹˜w1a¡Í2KM$Íý6¡jg»Z›jŸ™(÷d{!ù 3²Ж@Êð¦Ý޾Žxrd’¶¦Obì_Ø‚nËÅ‘ÅÇY)Z¼WAGŽ*¹üùœ¹õW[7º­ÝÂÎ/¼Øž*žh-£»Ø»Ì#¯Å)g„ñlBßÒ&nNÑx8ÿ.”Ÿº[K~<ã¢ëâ×1Aÿ¢=ý+í.;ØqŠçž}èspƒ 9-ê&¹‹ºðÌäçNh¬œÆØ„îê2гk²×ùÞêÌ«¹.Ÿ:Žyü²T+Ç?ˆ/™\þ@“|h ¬ÒfEõûÙ•´?†*臩óGAÿ¹º{Š'9Úµ½çíùÊ=›ÿç»™Á ñþ×¥_ÚÐV·ò¶*}¥Ù"å‡ÐƱa¾ûº\mXî¿+×”uƒ×ÙŽ¹é9Rö`­P‘ 9ê®/ná4àüg>'ã+Ú™z2æ]Ðëæ·|ÑW°¿ÖDZ_4Ë‘ï‰Ìµ_`úI'ëÃ,^Õ.*ó~кàË븡>J¨>Øè1NähS'/=X N÷ÁéÕÝ÷Ìýg<ê¤Ó¾´;TÝÌ™éì0¨7>_'`= ðùo€@ù#Èz´F6ŠÃÐ:·VâÎ4O[*·p6vfƒ$»ñ½s³Ú A¤¥ð¢•g–¤~¢ ŒÇÒcÅ “¹–îY5ÓÌŒ¨z9„/IªÀË?ñr7öä ò‰ì¢®ðw]ðöO„âââ&ë¿åoR3-u¤|íÏ”‘žxšCÀóÌi þÎZüéÎLšK]üÿÑšÐZPÿ™KYÿ%]î¶<•*"q†ýñr û…ÝÁw˜6ù‚V<çž¡§ì¿þSÍrè4í³œl¤»x¹˜Å)J+ =ÊíB÷†á §×dû,Ü•L€'K#(Íq¿ s³1 ƒ&êö|?׳¡$ œºÄH«ãé²,¡¯ìhË-6†åG£ð#ÚÛ , –/Ò¾ëÓr=™!,°n€¡ ˜•ìŒÄxIåKë3Ê¥›: (øÁ·—?Ö•|{(šÙíûnü ë[w#ó]Ãoh€Àíu°W‚GÕAZ¥òÏ=sÁlÅB<ήWà2®zžy^´ìÈà4öýbV;ÓŸ K邳þ­ÃûÌ,?ïu ZµõxÜ9¦í˜›úØ“3Ù õÔFGXûV™ï€ß{àVãP¿^*ÿƒ”_fK¦“V •l:“ŸÆÒ´ÈÞpjo¡½SÑ@Ÿ?ç;B£AØÂœSy6ÂØÛÍTðõuä’©¶#T‹cÜ=Ѽ‘)·$h’0""Ý\YdòûæÒFéÆ˜O}[“G1ïb·…—ƒ"›ÃìÊySÜ“Bï+È)•ÓhíH†Søµå7ÇJÆ=UµiÈ áñc탒!‰^´Ÿëž%‹Úä{¶¨à@[ ¢%_#Îñ¬BG øšuÕú¼_ÄØEÜÉ,Oµ­¹4ëãšKxÿ’ߎngוÞhgUƒh²Ø4wH<á‚Ý.O›gÃælƒ}¦QW´eÅÚâøf6u±R+})m[lçÆÎ¶/Sã¹ÐpŽÝÃÆYÆÄ ºI‡[F ˆéKàü/ÄΡ,wó¢7Ú—zéÛ,ðBÐ~”YBþ`FІ›`sß‘™]Ø&ÍÌÊ ‰gÖñJ;Km hÑþ}üÀî ÊuŸsyª½íY|vS§Xÿ$í® ˆ(g–½ ±N=Dº±´ëîÔu1À—ÄÓ¼Ó~Ò<.}ÀΨ¾Ê¾¨»Ò^ž@Yn$ÞM¤9Џ´Wå¡”w6]²=χ¬7šÉÌKêS;7ÚË’ñ¾¶{¼“ígësßÀߊž'Þ´5}ŒI“u¨ÇCNw(y‰oGßoDÞÊþs~¯’†1…FªU]Îwh—$ã à0ž´]òGÂ&G{i¢Gð,ãýeàùIß³lºs©8ÐFI€ª|jÉ#2Û4¶è;¹n–ÝŠÀFöÚÇío‡pÿ›ºïm~å¿°‡<ÈnÄÃûÕŒa£éë¿ÁùÕÜê••17 ðàäÕäž°Hj?Êó(n GĉÒh5dCfXÁºØ†NÙþ]­Eâ›´ø„HÄal—¾G¡­6vý»fÕö+‚Ñb„Ìã"?¢É¶ÀV‚7"ÇÜa,°Aç™9P”Ž´åä¹¥]Sp£]øØ~×f6Ejp¥žñóiC?=HF`—³Ø*¹ôðCèõ–¨Ÿô‹´ÅÔý‚æ¸ÿ Þ%˜H~Ã6 @‚KmÅO¦[¨ “'÷¢ñ9W/Å_àÿ!u†ó+ØŸfûžôà¼L—0úÝ&U‚ò50ÞÕ ?¾\ôi>!}s*¿úЊMyw[±)ñÕJOÚðÊqPŸZ»ÙÙ³‚¶)¡ŸïÂg ПQÙ“K™¯§,_ñìA{ öúE›´ÉIÖ…ùäI6J;ÁÓUŸŒ Œ2 dX4ÁÆÄ›Ûƒ˜éŒÙ…RIû¼ä+Û7ÖÒª….°yws\„~93%S¯ÇE¾B„Û×^*½Ô^¼‡Ý MúC¦„OY6ž~ˆ_‰û>ÚdmÊwÔ.b'n¦гho¾}åƒß“Ä™dGÖ³u0›ñqõ%¶*8ÏF6°U°g|;üìÕðÉOS‡PŠèd×ûÌMž¢ÚxùB‹4?éô„€1ßZ¸˜ÌÌÍëà¿ßüÀ‹Ãÿ S9Ùr 4ð 8 ¸ÁƒÏuü•N= §ö«J§hŠcðø1Úï ¾·:åç‰JH¹›iÂG S§ìsï Ô+6ˆ6ºŽ×rÏKÿµ™ë .]Ë ´%è;oÃËoG¹sñÈnS:wâ'GKðO½y¤Ðt¯ÇŠmwgޓЮ€ »“ÿ«âËB 3{:q™ nß{C¦<áK¿ƒ¸ÌÈ!|%6äË¡›Í0ͱȦ²‘¢o®"FК̶ã,ߥèA¾îé6#¡2#“>‡Ô^…ÐÂÚg²k*h¬m†™†ßÓItúª’qvÈotm<ôìlÞf¤ã5pÄΧžŸÑ˜/5J{D¶#\sI}œ_AŽŒÔµ9½¤¶KÝM4ÜÒɽ ªÿ;à?!ôUKIÀ÷Oü5µÿ‰eÏ'4ÍU§å-ÿªô_Ùröa§â²8 ²Æùek”g†šX T®*9¿åú*ƒ€Îûp€ã¥úþÝÕ¹ˆ‰1Žgr ÍH “¼ªŸ±®@èû¡¯mª 4>>‚A‘梀œˆÜ!¡jQȯ1¡¯LˆÝeº6Ù¿UN²IÛ‹ ÷ Qƒü,£âj.w#sáAÙˆîàKås¦¯‘p0Ìõ?L LuÐtêrÌF†Eòú ’ß+ÆIZ§]Ç6\9ïv‹3a׳´¸}ƒÝên°‰”=МüŪ®:–h wðû:?ãþ#ÄC O}.ãr¾ù™:…¿ì÷¥—÷ރÿš,ô%rüÂtºl‡4$ôüBŸ,¬¡%‹ž¾õ|ò½œG›{&òÅÈïú ï^×Í¥3©ÇhB7ô3bœ;Û³™|%ô `[0E¶Æ—ãt9aêE¢•/›ÐWH}ÂqèÝìIi‘²Õ섾z‘MM·UUB[nC\ç{Œõ3Ò­)i­gwTÍÆÚêàÛP-´}X‹§;ÍS6%ôÕBì6ˆÎ!^7ÖVµBw¤7HÅÒ$~\ðâžÂíw–¨Ù­žh h7 gDU .ò#ÎâYk5s‘S¸í¡ÝEƒm»Å»ØÏ5=ÈÏñ{™rb+Åv4o}’ž‰ âkÆÌÇä»îRž“ù1ιlëãèÚ¸ƒ1À"#õga5úJSCsGZèK Zt|ÂmÑ?Ñ8} ë—IÛ¥º›PN=|Ð1ܳ¡w_!Ò‚êÈä'ôM ` ~÷øן{º«¿°Œne›æ0T_èÛŽºw‡ž¦>Rg€FdF»Þ¦ŽðÚËð¤`âŽEÀ»?GŒkì2ŽK;Cb^¸£ô¾“­²ƒ¬;¶‚8¡/a‘Yè%?ÆS”¾‘ÐmZÙºÕáâCëþ°+ALuM¾aeô_ÒŸm26[S±¸cW3 µ<öz°î)›?ŒÓ)Ŷ5ŽÛ*7âh3å•àX‹ï¼X7무)ãò“àÖXÜrÔAü\¾­¾e±Xy:ñÖÁ»#ñFÚôèi¡¯„•sÅóIc— ¼÷Ð@žà_3ì˪ö@Í)Ô…oT!ÜP}Ë5Ö¾à ï¸÷Ï0§ÛÕbáD¾y#ùìM;þFXßÿÕn¨o¯ËŽl¤ éÖ´ŵ´ù.¤ÿ¿­=¼Gè«Ö;)5ÚÖ’p×ÚÙÕñìÚš^¶B˜È_Z¬¾…9ß»„zÐþÉ×í@D‡G6äèûŽ„hé;(g¼Ë&1ñ· ;VlÏE2óÉã~³¬Oõ®\òvÝVz¦Å¡¯J@\r3åÚÖec±shßoøÖOî}”.;*: º†>¿˜q¦YKe={¬Ù_¶Cõ„¾¯º¸{Öžo¿Uœi«VÞg'¦¢Öƒ‘°€}5¶Žç"¤ü¥tö€‡Û.%ë%5„£žÀkZ”-B½6ˆvÁGsº•ÍÆ|Áî±áVˆv_»ª½íéòäG[D6¶Ò ìnâ70bÛ‘×dêÝ|…®¥èå-‚Ðõ¼º©VVò¹É¯íàÄo¶8r}À˜ñá"6|¤)ï@íÝ<â9|êųÒÊëδˆ„¾ÑÓmçä4´ßÉÄ‹öÄ¿%eøÊ&²iû¡p“¹ä÷Ò©vÊJh±†h€øÏ˜«2À·³ç­Ô㘣SSW­ŒßšüÅÁ^Úœ6.0³â!L6YE;p„ñøx3ˆ¾€6`Œ4úâå°®–øàdÑ âÒ×:ãVßnæÞæéâ@.F›—zÞn­g‡0ËÃn·6Ÿ,z¸‹3©àpZ ÎÆP·uÙ¤tB_å_t2á”­†6CsÂñè&|º ›HIÛ'z>ô¶ù îg ð_¥ñŸêž±…UÛçÙñ±ö><ÞøŠíìäŇº:ö¿ï®l‹‹.E#ø(.ëBŸ¬g /bòdDô6†3›ƒFD¢6(ù%yhÕ[ŠñˆûJ<ñ›Ç·oS¨W†Ø^ýrhM…ÝPÛÓ~N½`ÏJÐ ?¶06þâ .¡lEí"lU&½tü߆>ÕiÝUp…¾„Dz;ܦ”M·Ù´­ `mNÙbG{ïü×föËà¼l|.ytz1‘Þ¨`y酾ʞù±šäè{sy8OÚ%17½ !›öW!| ƒÖMOÂÓï$NÜ™&…Aô‹.ÏÌÒz.„pßFsÇgÈCßÙÚžSèKøAÌÓD™Ëóæœèà|¡n]ú26–ާ{C´·E7ÛXú\}'ôžBƒ3!È(Ý`Ö.pb*bxè„\FÃu‘ã?ÒÏ:º¾èHè«ô:5¹$¡ïÆnl› €·JÅ;ØŽš›³€Q‰í"Ûu€æ(A~÷­zo6F}wse‡O§N%‡¯ý»N2­J›ôà§>;#ãí»ÄçúÊûZ÷›zk†jÆÀ!„ÌðSå{0ç†ÆaÎXnSå»tÐŒ\š¹éMqÄÎ%Vûz1ÇÁW/Yè[âÒèn¢°ÐWž {«^öÿw_DLþÛ8ÿûú¿’*ùKž&U.N,°0äÛdYXÑ\Ðwi;TVö‰pØsIù…ãþoq?SÁ]¾Zy70×ÏÃ39Pß/ü¦Å¸`!?ïbL䘌Ûp,ÿÕ`t–ì®°,È ß.Å8†$œÙ·€üÌi²Ö®¢„®_±Óž Û” áé½LZ[á§#ŠÑgÁq²¿ˆ´˜ßÞ0 Ù áQ:fþ‚$-ÒeÿV¹Ëî¥@£B‚¨×hÙù•÷#óo?Ê K ¯÷wîµPÈ~Ië4s¤2«MÒÔCˆ}¦°¤‰zË»ø%™È!׫”ë1~Ù;û¡(hu$ wå°'n±w]Ðfè˜5Cîï„ï¢6w$;+ŸàU­\<ø…Ÿ/8<ìSß½½ú—qFàÉiÞÄ”6taQ á†@ ,¯ïÃÉ3Ø’ñóóv÷_ Ñ‹àÒÂLìÜ.«÷JÑ4“IŽ ¬Á¢x%gáõ= q!õׂ´7ï,ʲ@ý. ÙØ]œŽý×&›§ˆßjÒÔÑx¬XLöIÏN:þÕ‘xxxŒðŸã—ºtn¦ nã˜j÷dÔ®4Õþ;Ú«o1‹üÄ0Ú;±ˆ¼Ä¦E…Öyí/mæ_Èp'ý0h!¦ D4F=fõŽBŠÂœYwjè½™­WôRó¡8Wa!ÿeÈ‹ö·®”kë‹ÆÊcx‡6ìâùGº&Z5ÃQŽ :´Œ£¾–zÏ÷=%O ¢°i5Âó?ˆmV}§X݃1.NB5f¶-~†±-ƒó ÞÁÉ08Zér³£XØZÁ–Ò”2n nrmD_G0Þ·gŒÏ÷…¨w¥V±‘Òœ ÓøJ=Íû+]“µIàÓ´{`b&“¬=õ¸™×?œ]v+ØFQú›™x!W¤£Ý!ƒƒhÆæ‚;9ϱ °4DO£ŽÏaº§&‡~Å{-~,ÿ’#½(Ú(q[oÞëÐÄM6C‚‹Ü.±Km=4¡g¦nfÍ Ýü^µ5˵ØÖL¡>_Û»!Ôʼn¦éql\ÌÇ~xK3ß¶sêîlZ,BàVHÿ¾b[ʦ-¶E%R8œ¼[eÃÆ¤.¸ú)¶–m ¿ˆ|¤=|)—¥Jú"zœL·ž@›éôõAŸÃ’ Ç ©ÚÑ–nÃ@åB°õ/æ!é ‚ÀÓ±5»7ÚÎÓé-+ùȾ‹Õbœb­W#辞¸û!ð«Ž?ÂeT³$2X Gü‰ä ¾ÖPr´m.á\ôßÜÒÐkGÊ2Ü@Øe–b¼îâ°UizÖ^@„ËGÒ.?“öhòjF½È§èDÓqßBÂa¢½:Vl…pÚ…Ó¨o?û,1ÔÁ$G¯ø¶Á® ,ÏðRQ>û‰>I@긖@IDATÄ®ôžúÏœí€#û­ÐƲJÆL9ÂÀêq¸Ï!ß!$¼Ïväò£‘‘Åv tkO¼¯Å}ÙWÈ“^AKñ+ûa­„‘_Ë<Â÷1AѾp8mv¦µDCô|4H?+¾Äž W²1ÑÓy‡>Æ÷FÌYÁaÝmdá¶RÉËvBÐØNmň:™Åö;\ö’´ÚbûÙM%cìûÈç¶Á%V‚@wTäGL,¶Z.sÛ«¢§Íá¨ððájƸNr<ÁhpÚaµÃøžS±iý+t¶â>§ŠIñ6:ríñAàpIª„h¬Ž¦~¿#¼…f MRà€xXCËÒ¶Œ±éàùk›A¾åâÜ 1Ðoo „{Ô §mïpaú'mCÁ¯Ð²‡µI=†ö¿‚îµmfd;â¾KŸœL;_D,pWÜGê1Ê%":nDÖãéƒÌ8ˆÒ/ãb‰OÑ¥}G ++ ÇxÓ¼?š´ÛàtA pYa6ë··µ9s2ÍÛo pþ›VÇfùê¶_´ÄŠ$ŸôUÍ–ýÒ0ÔGŸÇ§¹}.››ç¡»QèÌ€CТW‹/·æIèeݧ6›ùâKÏEKû4è˜ìç.†Fæ=]µÕîGZêëA~­íb.å*I” $[É Pµ¡|0í|«4eÖ¤äqâµÅ}Ï}Â¥óÜÚTäRA].9¡ô}»”óiįuaÝ W— å[ݬ=|ýìvòå4èòY]B¶Ÿ#¡VÝ)~ÝühÒÜ®£ïêžó< ißÄO¼4 £À~w‚IZ±ÆF™•·´Éؾ¾„‹½~z„¿ò®çÔQ~™ˆïÆ.¤ìà«N±´ÞO‘Ù(tçMÒB`?ؾçÒ¯ Æ££€V¯ A€èø4Ú#L=Õ6ù`5N ´Ô©<0*Rhgk£c‰þéÂ×`öŒ‚#šóš2¡ ²cÄíûút&u?'!Û·žÆuƒl"ê5Æ[d7¹?¼2µ—KX«¾ü maÆ›ègdW§£K±ƒ?—™•²IÿzÏè [^ÿm ¾ö6ÛEœ¦ LÂquÏÅIÑ•(q$mÒ/ží>+±ßá­/GIG §†ÖŸeàÖ»Ù¼UdShÝpš…ñói‰Kù7?ãô]kB{Å-Z´¸œ~…6ÜŸD|W¿‘>?Êsçø¿'sáNÔcÙ¯„øÞw„pT­ØGl-+Õ¯ÔG3C6«q¦ß?Š€/ºpâÙ\Œ[ï0_#+xÏùÔ"g*C!ŒÜRAüz¢w\ª$N“&K5A}>Ô";è§°MÂ]Áï¥u¶4«W]>(-ì³Æ^œfJ)s ¡ƒ*ǯŽ`HL¹³/Æ‘j¸·e¶ §þ_+’s:ò–êÆº# ²ÝW~ÏM{éNÙkÆ,x‹ùE3mHÕ¿ìf*nfA&_ ê̞İy¸ ‘ E%×rœ¶… ´™³¿¬ÀM³h†Kï_´·í]3È -¸›M„¶¡˜køn–v•!\)OìEX@Õ­&î+ü/e¡¸¾—ÎÍsaà§‚ þ¢Ø áÿ¬´Ës0Ó¢±êèÉwˆÿ4ÞÔm¼õJ?‚–Ë̶U[°€î‰{Žóqÿ¤­&>ðUoƒªh †Þâµ–8nÁ¨ñcv´í$h 6¸´fþŠÑÿØMÕæS¶}ù‡Y¼‰ 6óÍ:ûNx#1cœ¼hО7k× Š44„w "BµIÇowNäw÷\4|8" Ö¦À]|c\¶N-€‡1™ñ¢6·—Séß.Ä‚¾ dËšMEŒU™H¹ å:Õö>ÛE·Õ G Ÿ§‰ËÉÿÉÙóÕП@óepZ'³œ™%ÿ´–bïÇÓ$ц@Pîû-ë#»ÿ–×ÌD¸"¶ÿ)hí %s}/ß–7£Ùá¹ÞÅxúS½Ûá~Úh‡µs¥]’ŸŽG7í¬6Ú»©/wlÕûd„ X`[鱘'¬«ƒN„kGX A€4—ß…MiЛt²s• ÞBèvYHðª8?g èåw“»g£LoÈÖ ­dÁ/Á¦¸ÝyJ¸;ƒJ %¨ãD­ üò>±™8TÇdMЈr$Å΃Ù„h¾k¹°Ì¿ö fÚD4¥í`Z¨Å2îVÁêÀ™Š6¤9’öÞÕì˜ø~lÞ¥ØÈ/»Ö—ÈÕ4éòÑ÷¡Ì&3¥?Bà “·X´îzÞ÷ápÌNPêùäøOûVwÇu¾½¿ÎتÞ÷›Ùœ1¹üÆï6_ZÚ©™ôûÝÖ7Ö‹4´4ÆÊT†2Òìeík/ }ˆW}›=Œ-ëó*®„rƒ…”5>‚j÷'_èr‘í—÷Œ€ë±ö”}qÁ‹¢[ùÞí¡8p¾Ñcü÷Öîùô÷ªíVü©­7ÅWQ†{ÁóSø.? wµ}Éû>w¡d3¨ËN´³ÅÔ^ÂÉ:{ÛÒÛh#Åm:ÖPnðÐi‚‹Ø¤öLñH~Iþ£Œ7ø¹OÁË«esúHOpIÛ*®qzé@Ù: €á¥MKÚ¸w5(z”< bæ|îÉ|Ìn¬¡QNêÅ‚6+iÛÀƒœ]ÄxVÿä™" ìÏî>O}O¢´WœöHã2²çýóEø¿Éz¢·W3K'#¦CžAiDÁËÙ¼\#”ŠÉ%Э~; W·©ŠÓÙÜÿ‚ \¨Ö™µIsú›rç…ÊLˆhpêñÌ{#.2”é;AOó­»äÐÏù2Bz#+ˆÁxÉ4ÙZ«¶Ù‚ ‚uÀOgr$qi:æb\šZüº ó~˜w÷ˆA€•ᯄã–úˆß ŽnÉÿx”;¶OÍÀU¡WF'q–„Cwûs4„Ànà¢É»à‹möß)u¦Å¤•Ÿï ±š¯©Iâ:žÌü—Mì=-poú‰\± S}îôÙ¦^çß”¼ù{=œ7¸É¢×R·¾ Ü.J¯Õsg¢~ž;_M^ÏgEiÊån¯¿®Jÿþ®Øu×]ÍziÛ_C2ÙhŽ+&0N6L×¹kÅ|¢I¹´³¨¯’Ô œ¼»j¡¨+ºýCY/“sê35G}–)³h"6‘Ôþbh·Ň‹½".w“¶®·h¢”&—ìñ6Ä|èXß’@æ´³+¦~y4“—ôå Óµ8–}üü Ú(Ðò×Åi²¡[=žÅÐc&MÈ2 MÉÆ` ƒÑaôÅÝ)ï«\Ñà£Æ2XaÒ“æ@c &(°7ÜX¼\a2×ðNh¬KÛ:ЂÈ¿_É,.:4ðÖQ6ÅFÂxJPšmûI ÄdxlœÞ– Xp½Éºb‡%f$MòõÁ«;b0Œ†;ÙŒ_[&­{–˜vi"¬Lä…½sÙOnJ>²™6“…ëµ—3 ¨ú)óµmK›*w.¼¸,ÝÖŒ¹ã”cY˜#¯¹‹±§Ü%RÓðõçiµ7ó裥CÑ\dÏÈ~p‚œÇËn³^Ø íRy‰}Uú,¨µŸmP·/›ÓÀù+£!2)Á&ëd9‚´Ór‚Ï1;|N -ðV²ûÊ¦ØØÊö.Úîi[Ä.ý¦àÏÖ¸DAá;µyÝ–¶9“ïP¯êî¼SÇ£iëYvyíQh4ՠɉ°¨ôaòÚœ6ëIü‹ ÝC‰3’|„ÃÅüÀA {µèïA[óhoâ¼Ã™rÕ¾ÀÞE(æì] M6ktQ›újW„«ßÂQÝæ6Ÿ>]­ú K² "ÍêŸ,z8TB\莎Îh ‚nU—â·£]Hþ«"Œ9S&&%ßò ú€‹#uÀ ÉT÷$c‹o™.Wsõß…ÈíùÍ%»Nü¶Ç͸§?,ñ"ŸywĆ‘­í¨x?»V­jm†û,+劯ð@kK›}Vz´ ¨¼ßž/`Œ×Œ³¾ÅƒmZË_–>éåãÌy¬†;DËô-ì½n…Fôǘº°Ø¡viÍ…¶ ²ÓRŒ'´ª-ñ ínËv³WGÚÛ9%÷p’¢›³áÚ‘íÛÑ´”§1zj¸¼k2ö>ËlçýQ@=«ÈG‚olÍZÕù|§ù>`¢ûqÌúB·™!áàw¤½Žy½ÂÙV—ö¨;ñzÊÑì{)F!TižšmDçØl lQüÝÀ8†’Üùƒ¯WÛ l4¾Ûß:"<®–© ‡C´F¿mÅ“ñ›"].ˆö'þôƒ'4JG‰ƒÏqú¼Š¶8;¿ÜP¹=L»ÛÊä.Kt$à@›.ÐÏN®<š;¼éÝÔþÄŽîënó¬ñö'íuŒ¡Íˆó½"ÒvîuÚà9^Àáâ'ÀMê/{Öð­ßw-弞´OXqEGa^/]à’OCX|`ìT›”˜HÚ˹¤r úg¡ ³ÈNŒÍè¿ßòE û»Ížò¿?ì›v«ï·@³}|ÙSV¸x5¨ÒâtXcŽÕ ÌùýÈ¿ÝòÆl8™ÝØš²Ÿ–¥´ޓ׭^m°‘ùwÁ‰ð'b6c[Æ\õ>ÒŠð™?îGI@Z×¹ žËsiüFo$ø_XÑ-pÊw0tM€ÿ¶ÿ’ ÒÒÄ *“×YMhÿfDñ7á¿´{ :™b9Wk–ý²ö¿üÏ'ô]Qu }•ç\÷kzÛ6Eè«|%ôü#…¾±Ë`ކ9ÁÎì8ú®AXR8¡¯êS“|ƒÃ±ÈÙ(ˆ i ÛôiFjØ|ÿa‘Zr¼™å7,oÊMîU°æ?4Z–¥ C³:ã-}–Ÿø× ž0çp&Ù a ³!úŠ¡ËÅx+¾„RaÆLšÔºTN ñ0tŒ gVÈ-ã561[(!øTèB ¿Î!ôUø†äó¬ÏÀÅùF¶ ývëa0~á/—»Ö.ªÙÑÊ’å2Q¢ŸÓT“ž´îró-ËU"-*ÚúG⃌dïRšÕÁ1ÔA ²{›ÖÌ“à¯%ÐçœB_Ù|­XÍi]Ïqùnn›ÅØh3  O.f¡#¦ï¦Ì8”jþËFÛ‘Ó`žŸ¶6[uGw´n Î~¿•ÅÖ(bMæç= E;£%4غØI˜ÀöÌ7kpÜy\q•uªènÃ#Hs/€@è«÷À­S#X0ʶw¤=hÇîѨÐWKN(JtâCyÃÚºA†²¿š߃(ÞÓë» ñÓìßuãlœ[&“gôpÆW+„¾jj¯ \¡'‹óâg`îs~{±ÐñìY^œšÃCqë;ÏFæ«ØÁörò+«ÀÜ Yo…;à,¾Dò`·X¾(Òœ±õ'6Èkmÿê£Ý8žèÓ Í%UÙ »v‚[Þd9ÿGwoæ8í1,…¯¢:‹…·Ñ>´ßT7ƒC©ýbŽƒ_XÄÃ$mPÜ‚Ð÷¯ -ã×!H:…¸{‚;wºodÿÓâéqúr—®<¤Å BUƒÆ¦:„;Q=)Ú®j“¬ä|AŽZÖ ¿ÉÚÑCCq…Ü8«(Gí_½½ïŸ´#ÄšU{í8€´Wúþ<\_¶´>µnW<ÈKÚcÒFŠ÷¶a‹·µÃ"Ýì"pø HÌFÅöµãÑ6>Á_Ô½Sò!—ðA_똓oP‡Î¼jìÈVÛ›©ú‘û³©,­_ixgƒÖºxtat[?UfS¹”öP.£ „¾Š¿6F–lmû⿤SÁÑ.PDÖ¹õ…¾2K–ä;9•yà!núT„­øýÆO<Ë$áÈâ hñ}IÇöâ2‹×¥Í ‘®àÞêà„7fÃq¦À%Å?é”ÌÒ›˜²ÁûSÝ£Ì{Y°´yå‰ÿ ¼Â›ÉêF0/“P'>÷Bˆý뼟~/ì\ÂÑÿ3°.÷”Q¡åíÄâÿm’UŸÚ¬÷¦¼V5!RnRÔ0¡°Ný´4Oè«<–Vè[´4þã6fGtY>»ä[ßsçªþIå9–’;çã+íàåq0}K0ݹ —¹Œ¹Dü£^d˜Ú&@`Ž!WT1!ÁåZD 26Ër¥ðüdSq¢£©ó©Éo|¡¯ãfC:HËny@ù,ü½39i!õ&B71kûœ Ëvõ£Œ¹@öš‡ÂØ«þìK™2;Àš8îg—ÐW Á7àˆÊa˜AÛmD›ôá{-BbX’Ëd‡níH“0›!»A…úxE‚´³ïËÁ²hÉxN\õwÂu«o®KõT>iÏ‹u\Èvr=›¥$„—JÛšß>B—m"Âê#òF•æ„®¹.uÌ›(@è«‘{õòà ûªî4.µÙ†•E¸ód¶.ë\ZZ¢E›6ZT©Nî¢=ü»a¯µA®æ4]÷O8} xð»É>ȬÀ¸šc±•¹±uG@}¥½+ì2×´ååô] ôõ°2È-óÔ’ãnõqÁƒÏ´‹…zt/, }×eÁ-&ü{ÂÞI§hܱ–½ˆ ȯœFߦ^Ô$L~p,/øK¾;„6m‹ÏtßwB‰iËkÝ»×Ö~Pì<þl/íL¥ Á³,>ƒ†vÖQOÀ³uLÍk°h<Øù©oÔ¦{Ï2¬ô=„W¶² ׿÷|Ú'w_±ˆÅ0yá-¶ŸŒÅ¡ Ù.ô¹„¾:ùN­¸ˆº`Ð/ë‹þÓ¥ðþ½Åâþj_Ðl‰XOšïB1²œÑA64~6ò-ô6§0Óàì»hm¨Ãc´í´ÇfY õJÉ ÏÇ®îùöxâvÚu(8@z-¾jÃØžÛýŸ‡¬c{G Ûùtí-ê;M}6/‘NÞ;›•ŒõIüD/Û}½ØøëÛ!;—E-.²…øNpÚ‡OÊ÷³[*ï¶CëÞ"oá}ÓoÇ’÷•‹3Æti#—nm»Ñ>sí@tAt}|«W;Ò¶ø«ÄFëR°Êv^ûJ0å  AM™õ‹uÀTÏ|ëÏ»ìò¾w{ÿƒúþŒ¶zm!ôÆ]þ#ïÙ´uOLÆ­Px'§;™k¬&¶;k›cÏøIp…6W¿$¿ÆßË×RŒ‹h'üè ]€‡ö§lÞºGÙ[–-ÇèŽ|s;ü¼y­¡rè\ 1X…9ŽV~>{¦[Úñn\×ÊÞN>c/;Mûßh£—@sÆCåådRÈ7†%†ã^ \%¼€27Êì`-þ·'ø¨Q£Ž˜PqZ¼àW1f×ÒÉúÄÏÃŒ 61K.³iðçO§çA/'KM¢;Á¥ìð>ãfDÚ¢|Úmù¾A{“Ÿ´¥M( ßÂýïÖjl΢¬£üÌhWÍ-ÂÑÔÛŽï9D8œápÀím,Uz¼ÝM’`Õ -¹`ÒÊצ,ƒý¼"|7ÝMž8 l[¤åÛ p Þæ^FXœö“v¼.»‹žáÚÃÓ~/³ç“/ÙÃj{Aâ9~Ïxîÿçà·m¶*­s#Œ´ö¹jÍî„O Ãnïq§üŒWÀ@cëÛ$.™ûÕVGunñG$½Öˆø¡ŸĶ±ØHYaSŒ§Y%s¼Û„˜æÅÖ¦8À/±-Pyïߪ? /#\c#€Ÿ=G¼?PÊéÄ“ ¸Ù%wÙÑC]¸Ìì©>Ê à-› …¾áÂWÏÜ™.­ ÃZ|oêTÖå5O“Ý ~G¹þÝ«„ì öЬ|CÁ œ¹îiM¬yôI¹Ú ¸ñxHh“ÞyòO§úŽÌ!ôU¸l1!¾~ç'N|¼ ?¸e ƒøÄÎØÈWÿg 4ÃuRšÇÅŸ07œn,A0r¬‘5ýˆ(¿j£äèàÍ=e"l\ö¸÷ûõ"òrqï%û² Óé²F!yõ½ g”O1ô`~`Yá”äÅœÞ*Z–ÔMJå䞯¿—”@ŠDâsm.i££±ç£N2ßXŒe‹/{Ò¥N9#ô{I­¨‹Ô´+²¼°iV,!BË…e˽jÙ’¹c˘ô?’l=µ´õX^Áÿ²ö_ ÊP"~Ûÿ(T¯ ¯m˜…{K›íŠÖÞMïì.eA´|Ö1ï•\Ÿ4½7ÆøB·¥üÜRGŠ {y áÅNKÊMÓŠb¿‚és[1ùM„îŒ5Á]Y‚¼Ÿn ²çéÿÓD¥c[:ß¿ïI#›Öù »¿ÙÁ\œKÚ—Cp­ŠŒÃÇXDTx ÷–ûŸ„ÛÙ—ÕÕ‹ÉqÁ—aŠêð„³­}zòLÆÎL®Â¥ì#gõÒóØûk&Ð.úÑ”û_Ø«ð³}AO8îa08·2o c1Ägð6CkóDiÊK/ Öw ÃOüvWø¶KÁ¶“•nEÀdlDjaQ›#3ùëòƒúÀ¢ téÖ›Ô'ûR½ ¾¸/yžbÉg`–!¯f>ÇM5.P¢sÁú!º­›Ù †rÀña\:Ò3§™Ùç’É”%Zc¿, ZgÖ*²RF&Ð#M¾úßÈB·¶öqÝWˆ[šÙ\Б|Z5X„þ¦ÅTû0:o"í¢Z·IÆì˜È6î‚Ç1oqtßw‚2OÝÜŠ°Ò~qöcáfÛi!h›J›Iš‰>¼Að"í dÛ¯qhÏ¢þ:»!Øg,­Œ…´Ñ6ùÙ,²£ï•“€¨¶’02‘mmsi 'ýi^€„\Â舷ؔ§6ø=h«>JضòfÎlè½;m­6ny[Œ†°UP^ײ>N ò¿JµJtÒ“6:P¯Öa؇&¡ó!ˆgfIÅKì~ºP™êháÇ 0 Ó”ó<Þ¥k”†.ä<Šö>'5Îz¥&€a¼EàáÌÑ[£Ý¨èQ$×Hc±I:W¦¢»!LÛØ_Ó›,µ—â§vdÙ(»€ÅÃpû‹Í8 ÀèÉÎŒÉõæ”ó.e]®,|ØŒnß&”Ÿ6ª=†¹@t°µ‹´I…=y›í§aŽ/¼·?³q’຺{ía 5™™,É©G#kð£‹ßo‘ˆ“°:"]}Ÿux¶ãW`» €šëì&+èªHþvG”ͳ­“ëÀwˆnƒƒ%·ñ›Îú€ڷ@ úÝ-¡“<àçƒÚ,©2©÷ôφa#õôÊ=Ž"ò«ÞÕ~«`‡ÉÄHêSÕ’·S,Ñ,¾;ÕÆ%´ÃjÕßP®—¼8*;ڲ痜DFmšþ之ÝY{ƒõ­û7mqyìi§Å²HÁÓ6!kU¤#ähÒ «ü¼9»éÛmÔ‹kÁÇð„6\%ù²õ®›†ù“®ökÉ8›¡[ØkùFaw[=~-i§££9‡'P³/ÿ¦@µªù?Ó6Iu#Ÿ]©Û|æ%L3Üg­ÙµÂ~àÞ1ˆvé­z7¥vð¥[Ñö‰yŸåyÊf®Æ¯„¤¥¿ážÎ¯ÚŽCKPå›sÂʵcís¶Sx€¦iŠÓwßñº2yîGyFò\•ŸúO×P¾.ðÞxX°£ïnKüŒòïpA›±ÑGá(åÓ—9§-,\ø ÿ©|s¤u-˜`ýà ¼o3Òµ)ãéøŸŒ0¤Š±p­.”¶¹„¯aˆg?2Žu_ÅuÑ í}4š%Lþ‚~ÿض°ÑŒ¯ùl»¸ æ›ôñ9©1íýÎ̇Žõ§O çLý)³ôv"îç„Òz¯»<â;uQ÷Q\šw´µ¬G5iÇø Û¥`;û êZ»Íú+“²Ù½ØË× ×a“ÿO»Aâl¨ò¡"Ýe“.Æ<ê/B½½ùÎZ¸ïÂ7“~—Wt*õ¿Ùt3åár´ 0®+ #Á8Ö«Ÿ ŽŸÂÅz)[• ²ù¢Uº¼.ÂFBä_ÄéèÅ“MÝBÆrD Ý¥»Õ}½o‡Â¬8r<ãøíZ( ”ƾ㾄'iÛ°™1(MNÈÔº„æ::¹.Þl?$^Ò‡îÑaØtŸù„£ØËuO…6¬¥Q€øê1¡ïÜ‹Àù8hS>¸›z Î!ÄÎÿâj=´–oÙ&ŲEô¹òHû9[½Ð†e„C°/õ&Ëø­4Ýi$=T­Q`fY2ôibG,9§†1ò!lØÿs>Ër‘Z®Ò~çĹBšî7iäÑô¯5SvþþÉ0‰á·X|YÿA—µÿ„? Bm•!·Áâ0øBæY–q6êZÑZ´}ìû¿ ÷`§‚å_cŸG<£©Ð1VZÜ.Hp˜¾¼ÂÏHv¯òÁ¡ [qšó[3_äåð¿8`x—1e¥©1¾'Fa¾ÿÝÆlØnÆ¢Äiòùqƒ‹ÌŽ…É>Ã-H¼€õ9º¥côê. dov‘6$Íû¡q+û¶ÒŠ•v¯`’ì²ù>šø¹K7ü—…0IAÊuj~Z ·ö„´Ðšåƒg;Œç6¾Dcy7¦>Ò®¬ÀçÙ.>kçþ0æ`müàÓíœë¨—´~Õ*“Yï#/FMm¨v Ÿà4ÿ;ŸYø6†bÔÓ—À£¦gàµÜO–Ul2[ì*Þ´Ø A…˜Ý†‹Ùñu9AKIñ‘)ŒmœÈÍ÷ôë[˜Ü…ÉgëøoÕ<GO`‘p|Îð\ž—! Êh¬S´ãd^¡i°ÈÚF×ñ”`ÄÈ`ó¾j›tò\Ì_åÛ Ye:ÎDÑHAÁH+,›è¾Þ‚Ť@O]ä&QßI\ ’Ñ0€N£­+;ú‹ù}=‘¶G-x¤ý’† AÇ|{>çi±^÷D:Øiɦßr9~Dq„ n9y#¨N"ðÛ¡‹Öl}œx”Z‚Ë34'ðv?ò|ÃÙ(Õ¥gy!Nß"ø)(ØÃ^/BØ"v—§®b³/Ù íôõ(ƒøë¥ï†§ƒøÊ¼ñ3]Z¾íÇȸ_&õj™Ï– àÒÅP»÷4· bh¦ÕÚBšÛÑ“øíÁooÛ¯ê;×Ñ”^Ô‚‡ño M»×~YÜÖ~‹c}VšM]ii¢Ö>êÅ«8”oÝE]Æc+t3?-ø•Ö|ÓbA-µ[Kпƺ&Àù*ú®æò£$¼Šì㥕[› …OÚø‚kí¥Ú,Èi÷äGäá÷'¶› úfå«“†v‰ï`ÇWÝg[”_€‚)÷:ö\âq.í[‰öÜô=ÚæiŽ7ºŒ°Hø_ë}¯¼…+¹Ùùµ/ÚÕ̧“¿6±j'Úôh¹u¨ìDðB—•UGLÚdyMÚÅ_ócá·œP­voWC{»€8ôEyKü7 λABÿfŸÐgßQÿkìl6qÎN†6 ½B[Ò‚OÃW¸ªÈgs~+#<~Æ>)ˆV\›ôÈKGM; ©ç[ü®BÐÎ XsPq$Y]KŸÝD;á;ÓlÃØº˜ªø7qn ´WsŽàÓß‚(8)¡nå±›òÑ΢›5×J3*‚ÊÖ´ëºÑðOðmÑkž>´€&_SÄ·Š¥ÕçÆ1}è  Åz ?Æž4(%O<Æï9ª{^ÑÅ|Õ¼èþÿÙ±mfôhÊó}±¥çë„k«â†N9Pï:EPy>Žo)«âNå;mxÂ1K\Ý_?›€é™Ÿ”%IžÉO)5[5Ä›þ¬6Ëï6 ã‡Óï/ZÿXo.møâ;áÃhAÁƒüÁnGË n¡ÝŽ  OòQÚØá@'ëPöäì·%nà}„6‰«oÖ oÆkñ…¦¹·•¢`§øÈEÅô"¥­b,?GØ+Þ“1 ö_…üŸ¢]TlbçÛ¿bgÛÍ…£éÒä€ñ!Ö-ƈË÷…'@ ŒWg%°ËªE7j3“Ø‹6Æ=ËûÕ]BЇÿ½³°ªÚþÿº5I‡€¢ ¢bÀãÙ¨ØÝ¢bwb`v7&vcëÃîBQ BP$$¦nÍÿóÝçœ;çÞ¹3 ñÞûýþÿÿš9÷œ³ëìX{íµ×^{m ÛÒÔYùÝvtÉè G ƒ‡àþ<áևߠm#n¡e$¦,ó™Ý…flYl=葬WâY !‡Ó½êÓv~éAàĶNÙ0Û.¾¯e+&Ÿ×’žÚ HÞÂ7?!ÿƒùîõ”a«/ÛÙ¾ËþÈw_ãû—ºƒ^'d¿¡ü*ßhâìK~…;€L„Û–7Õèr%„Õ¢MMÂ\A¾ÚXßìŒÜ¢¥äFú`·:î¬Çw7 }ÚQí)¡wt[²8ÚöË&í ¯+É7Âü·2m ÷ kס.°²È ìÿAÞúž3;´È–”m"ñ˹€Ê…<Þàp–özí.ìø€¢ÉL…ê7AöëHÂÇÔ#8f s0ñkÉï]äåi¾}>÷_)4}íóí°º½4OÒŒîÍCƒãjOi‘§Þ!åv0Ÿ_òƒ†m®MÔ6*eàVùu¶f|WL‚tÂm€‹áýüncÀËKµÐãâ/à#xö@в)o'”’ÏÚ‹ðøÊóŒSÎøÆäåK®)¸u÷ý~ðüýß"³Þ#ûÚ˜²«ì±ÈjàᑸÓ§Uþ¹÷¬>Uþ5Ï«p©¾çp D×[Sž‡ˆF‚“#$#Ídl¥Ö[¶ä$ÊFùãƒmdÕÁöZ¤+¾´åæÌ±¥_?zÛVé7,²4t°!¦8zi ,Ý6ùŒë®y|ƒøYèHåÓw}ØzûSúa›Vw¸ý§­¥%«ñG‹PÒ–/{º™~¿3B´TtebÓ¯ÜE}Ô^A¼„ÝŠüÆ,q ßœ‰/.ð‡öhÔÿšýÁVã°¾jÇ¥y2ku‚Ïýa5²·œïå7ˆÌýa¬EFÑÚ¿2…pÔa# –2Û~ÕCت/ÜQ혃z7¾§ НAÖÀÓÜîçë•ß-@úïn}mÝŠ7loìy{ÐSŸS£=l[-tH«;J‰.ÆÏËÅO!ÍØ<`ñÉ™Þði5w8œÔršò±yÁ½Äè×âEÕ/4¦:Wçžõùc÷vD(ö¢hê?õœë÷§Å â4^&¿®cüŸ -žR@'é®CQþkî&vL´ ;£ÎYs/ñûyÓÃYžæÀÏÇBíP˜Žoµ^»“£—ó‹®&¯¾*H —ÿÝpZç€_7G [u›Ð§†½Üó®(¨èœåñ=úƒÌÍ@IDAT„¨@êÙ£h3 †W¦í;D6¢=O¼MfæîS¿Z0ƒ4.f×À²ÙÁ¸|~Ž¢Œ)ñƒs¢×|Ï–½ÝD?M|Åb÷Œ«›ë//f츼þ'Ç}h·Æ»XÛÐ<Ú _Ÿ‚{i,Ëß9©¤UeÒË2ÉfÓZ\s ÿIG04›÷ÿ„çÿtÿ'j9ÿ¥ù¯Eßꊺ6︲ž&@¬ÖÿÐ ìPÓ¹Ò ij·¤™×8æ©)Ïÿ‚»Xü `Êáí> —§1. ´k"Ò 2-C¥e·Ø_ ©…­ÔÍßRØé• &1?Å@#Ã5: Û^a%¤ ç¤Pó¸5X)¤ÝZø­à€µB÷à]l}˜9‘»¦|²†ö¼ Æ…ybfôGíñÙïpyÏ»¹I‘œ˜¤`OOPT{÷ë6êDîwî§=`—ð}KÇÆ6ý\r‹ü‘‰f5—™@=”¹¥Ét†hh¥^'= Æç&·MFoäáNbnäÚB‡øpz“‹îgs8“& ±KC L4iF»¡tœãüEŒ ²ðd¿`çv÷¤k€pøO.1òáÓ'¡Å.¼ë`¸‡ä6Η¼¨(9P&øLø+yעɃ.f!|9"ó§[4º:*Œ C_&¡à¬Êý;ÏUvTêh»¸QŸ†,ÿ9Ñ{N2¦×Íäw6/¿>AˆÃ~by$ó— 'Øÿ*BÞéñkÙzXo+£Ù²¼ŽÓäÐd:t }º cª>fFê.&ϧX¬ÙIh(¡&Û£7Ó'~šÓRY=1š2 k»kâ$¾q&Lï^LlfØð²#èàgñü¡­–ΖŸö“\ï#÷Wüb¯S>3ùŠÚ Yhw†‰zÆë¯yYa’YA¸úäI>Mëb7&FØ9Ò¦t áÐÂ/îùGúò·Ðœë™ÄZU&h»8÷#èßù"4Òƒ©©ë ßçùV’~­ä¤ Û‚²ñš¼\åEOG ù¬³/ÜvžWË7dæ zò¦IÎ:¶Zz"6Z¿àð^9¾Pàú÷Iû ÚîïÀ½`;«úÃpŸ6(·ù0xÔaiÚÆ×xÔB”Ìü8‘ì·Ê×eb‚©iÏÚÔÁÍ<ÿä…–À¹?ýß³uùp"90Û=âÕ9ûOz.í˜DÐê˜|¥o#<#Nõö^ô³v¤½så»Ð+lad;òˆF¶D×PÞjð‰^àÕjpKCf"˜²=cC=~™ç/)KiÝFÄë|C±ž"gùÆâïbç%h;iQJø=·ÁÕûlÛäÓö+ónA°ôp™8©»1 TîÌöl.ü«ÿ— ŒoµÉól(ýýQŠØV¤MEûœcq,[ ->v¿)›^M^j¶æ¾QòÂÉÇùÞæäéiÊŽßÂ2{›¿é„âÕÞž\#ðˆ<ÞDþö íGx¦ÿHØ£* ùϧ»¯üI_>¹–°¼ÄÏ¡ ä©nÏœøÊÒO|;;½ÕwÖ&¶a(·Óý’x÷RWªêNŽ ÜàGÚwWÄ&•\j{g¦Ø_™k•ŠƒC¢›Ú­8J¸¨¶HC:f/ã&*J>ã2vxåzPlˆõªx°sy›ÏÄÉ“Fv¢©¢]±ÕHŸoÖœ@Øé ¬„Û-¸I¾ßáÐÅ} z†üÒÖ'#ܾݹ©'$ø§¾KŽWd’¥Í ܨüˆ|Nôð*~I .û©Ø‘ÏŽ£~ٶĹ•°åŠUÈÛ&´É}î¼&i_¯[±P7›g3 ú`¡ð®–ÑNÃý(S°3tný˜#Ê æ:ïŠÉ)Øh¹¨…ZÖÁGü»úŒl»Åøm9ßÇé‹*g.ps¥‘ö !Pd¸ ¥€«Z0¯>ŠÞ6}$r&üÆÍ#– WRÔÀ$‰i- ÁnÑöfâ[?à¹ñëÇÁ«_ÛgªP| #ZóÐ oÙ j dÁ«gfO¶SþÐ.˜ô©õËÇvæ/ŸØ»sÿhIô¥ 6Ç [®]]´¥úÞâD6¾U´æ‚´ÈOu}üïç]j‹+¦|iwÍÚ¢4–U ßëªíƒyêêÿpƒÛ¿'é%Nµ¹ú;wºÈÜRCŒæ@8;4AÚa¥Z (ÆüõŸÅ‰¦2V]ÄCgK`P£‰qC¬ „w >-{’™€¦ì–¥Ð8”†^A­ws¿½›\†‚¹Ç×Yñ àq BPlZ ï–tCÉ8;ž9Saž=›]޼.‚Ý#Yý—Þd1ó[Ón—"à¾x V=Å$njœ£%sÑö$mÛvc±¸ÌX„AT]ý­˜íï§HG°¡?étûjð)8P0¼,7áT„&UÚ¾2{Þê}ù}R”e:PPá|øší\SòBoÍiÇkÕq °ÜÒ8b¨ò"ÖÏ<'i]é„y¶r¿€»0ã%¦ó¾úyQ _ÄŠ_nÔľ(LÁ5íNŽ—måçaðŸ¡tØÖA¾@ÿ¾€˜OYƒþ‘¾Ð’Ç4¢ä·Ð!:SM`¶ÓÇÚ}°²GÂ4oP õî4HµšÉŸ&¾IŸö}ù'4„ ÖËOg>ôÕawãÚ¿;}÷†üÊámúî\òÛ¤9M„tŽ@»÷&¶KÓ_t[‚“Öc uµ+uãíìv4'ûª_D7¦¶¢˜vÑôçQ7ùBƶáC0ÕN«YZEÎÆª¾Ô4tGˆ9!âÊ=]Û/k¼¼Yú5"}ɤ‘Éuúj{·ªB®C]B?VŽGˆ>Ø.ˆr|“&ä9 ÙšCØz¼•Ë·3“yŸ€ ¤I¯q¬$u§¯ã5“°ÔQl„;Öt¥}½I„Ê¿5± -.Çay¢(¿q(´±×á-GT‘.BG'”+¡‰ËÎö!Úâ'£Aô‡6kfô(}áÓ$òu”ý?ÎæÅÈ¿²ç§Å‹Ü¸POJ(e_¢n™¼Wï‚Ö:´ÞZäk3BÒ¾ Áp'|$bRÄi͆¬CMc\è ¿Ü>ý¦} Mr;L4銞ì…ÌÞAüѼ¯ÆûLûaÖt}-¨_¨XÅÛøAâ œ/ÏQp ‘Ú 0•º+x×äk ùïJ9>á@›²sd]ÛVBÌ(»Luí¶ÿŽŒåtP6…Kä'|“v™ Ý;²²K¢ŠiúQ“Ô÷Ý–w4­Ì C”Ç'¬sé±¶UÉav|«Ÿq«âB˜’¾„û\Ú ü(»†vV~iÃò»ÐˆêíJ´@¡mNˆ¹0£Y,!Ü©´ÇÞ$³ ~õXÕ\Ã.Ë~dz±ô½Ÿ~ÔŽ¤¾vTÞc—ƒ›gPfÊåœc+½4 ÷Ôd4u?ËRNhÿÕxâêñB W_NÓ}€”<SòïÈH('Ú_ÿ:eºøÊoOšh3ҤΫÚÃl11..Jã9ý¥ «»Êžoõ´U%·Ù›’úˆøÏp)ß@êP~¦wê¹ú òú÷mHŸ¼EûPgãO›¨.%ÐD+OÜGŸ!\Ë‘t'6œþXc—+'º¡u˜™ÚÔ-Ò©³ÁCò­KéI£6IzZ(ˆP'~±ë6¦ý²þ{‚ç?×RæímUiò2+ðú }Pe4Æ»Võ²)Ðl+ÆÄ’+qO@WÃ: C\ˆƒ?¢m¹ï‹Q¯‘Ml0ôïýºsìÆºS9ü–MŸŒqp­d'èæ× }£;P_ã)uŸ>Ÿ G‡ð.ÙþñëÙþ¥ÔňP/oÇ6¶ð'ø¾ÿåìþ>]z·ˆ èðÚìÖÌlÌ çõ³\8ýœÞL!üt¸£k9híSfþt¸Ñ™¼SÔñøå¶{Ù{„â¼LöŒH¿leX¥ÃÜœ¹á´î×SçÔQè“5ÿ ޹©¢6ÎŒŸ({Œ6=îîüsQü‡Û¡-{`M¸È ßM<ä[Ð w˜£ï¶2í}ô8€¶<¼ƒPægx…°ÂB× @£ûð<ÚUô@V‡ æ°X<×fk%ÄE8/÷ó=Ežôkpr‹~]ü÷ÞÜóûíï,[¦ÔeÚ‚:±Þß­¥vß(­å2”¥4÷KÆ3¨‡zªíéе„ Ö‰až©}lcÏ­ìl™ÉNœk9œñäøjŽÚx©,éï:ôíW‰Ü)/õ¾FÊ y!ò_dòh£vqŠw˜ö®‡>õ x³gØu±ŸÛÓúh ÌN ½±ãG6j³Æò]&=]ê ­!;Òr{´~¡=ÜÊôa!02ØÑì„[£ˆŸ4‘ó{WaìæÞ郩[A"ÆÇ¿ÌÉ |ó›gà?z‡æäÚu{5f6¾„¶Ê~zãÜæ%áu×OýUª¯F¯Ïý9ÙÞžó»õ.kk{.·² î¼¢µ‹%ì‰Yí_ÿ)Ð$k^nüO}uÉ¿3ß'`áâá—Åx^¾¤ÂöíÒÇ]{wémë·ébsÓ){zÖ$ûx^à¹I.QÐ'§ÿ`kæ/QÜ–Dò·–„,¦-Dèßáúªÿ§¨ÿ÷£þ½ƒÇŠçPìŸ&åK/ÌžbÍ›¹4Iü[ãjà\ZÐ bK@[l‹AÛÌ MÁòàÂÅaòb¶ì%7±n"ø¶þ*·¼uJ|`g ŠiKëû¯# „^,ø"7 Φ§˜€P-#âtϦÛ†´F#X @+ÉÍ„ î:p"¼h³pL–CP6÷3ƒ£å.í0îh«ò]>;-†mªÓJóĵrïÄ/°?¬oŠuÞ­eAûE»™<gà 3ô¾—»=ˆ «=C¥‰!A âJÁy%TtÛ†Ëâ™ñ£,ò¶ jU‘Pb–n¥_µÄ&­¤£X-çCfkоFÛ%û!Z>LàBP‹P n3äâ6äæ½«½>O\™ç~>}†qL¶Æªêa¦˜„ÎbKß«n) ÒM‡íùp~Ñz²Ø~ ßl$[¡Ã«óù±[òÆ%  Zð/Á¨YböÅo/ÜL”·DÛL:ê¡ÿ¶@…¨‡¦Ës•£E’± Á‰_dN 7á1™að55ÏÆçÄû¬ìS I¡qú7;ß¾~ùgÄ(wvØôUÕFm>×Ðîû0ò1~°ÃÓ; ‘Ù(ÿNžj‘Æ»¤aÙ·Éç§.fs?¢ú¶´àçqwÂ^EÈm^ÈË,g~Âê“ÌóKö [žÛb³xE´¢ö¥y »”P[ëxÚÞh‘&Z®‹–ÖÆÒêóA‹y—ð¸õtbM„ ©Ò3Éן­ä òÔ !ÈpÊÆ¤×‡§èã«ÇÇoGÏE½‚E y¨¤ ¦C B¶¥qÙ?2,·môòÈjvB×îé§ÐÆîewÒ•W¯ß€f¾ÞŸBìÿÂõ³‡’{Ú±ÕìHmA†Nþ#íá¿ÂæÒ¿P÷çW&­ˆ×†OƽÜÄŽ³É‘•­:~\nw‚FVÑÌëÁñ ¡;úý»»k#¶hqA'r_‡û‰„;‘‰ÿÃlD@:$Ram²c† Ø‹G¿!o„«LªX4ÏÁP9mõ­jåKut]BÔ˜g•±m¸ÖD×ÄïYhNÚXdу ã·HÔÃ~.¿ÙN(¹ GèDê,î¿ èºŠ×ÍH³Œ÷_mtd%»–òÞRù£'<*}„vݪø™XãìÐô|‹6•` mÞõÒoaKɃ¤±.æ?ZY¦öŸ–J#Dɾáá¹LÄvå›Ï!$¼˜º¥ïÖžC; ÛœoJʳ¹þæšB=QÿÕ"L™`ÿŠn#Œ¹Œ~þhtcû!>Ô‚]bˆd‰±µ·Ø‘þ²ÍB€‹ÛõuW³[š@ÞN’ †zÏZp‘`<ÐXu¾à&uÖé0´â/‰nj¿@Y…+;¶ZÕ[õ”×·Å­36¦­æÄ²Ñ¥r k‰Kÿ6ÒÙɯ ŸÖ¨‚X³‚ni[ öy‹¸Ú¾]ÀAª¹ËÌ‚4,+Ç&ïiðu3|¿k§¿ÄŽæþ^ñ>÷^ ~5Ì=ûÑÐá–;„„eÅÂ[êv+ ì. ?mýív7ÍÔÓŽ…7¼?[e#ÝxLñýe‘H.Ž”d.—ew–ïº-yé î_Bšú8&3§ÑBp.ÿaXr˜uKö¬¢iâÚfÐöçKk×ÐV9½ç¡œÍá–[ÂJ@“É÷«”ímøÏð9GÁw,Ú[Hç]PÞ‚4ÄÃjý¯†€8,n!:•–Û&íººk³vÝl„g­¸®K曪¦Û ?¶‹–h RíäËâË(ÀNNøÀ°e‘ô’Ô¿¶q‚ÐàöSÇÕBAköýßý¼<ä¨qM,»2é ,]…l±dP&x¿™­7K[RÂ@Y,Ed?’lJ «o´qÄwš:@¡TUL[º?̆NnrÝÿ~pSmŠrÌ–à~“ý6ñ[Èsn›½ïÛ:î¿ÐŒÉ fÝA~„½ƒ ðe¡mòÒîÎÄáp.ó® ¾Ü“ª›žE4´_ób±›»jâëCXЛgSÕÛx˜F´‰ç¢MpwôWæeᢓ0»#´XœUv±…?÷* Ã)^—Tî—£½`4÷à8˜±|E1ä0ã½\ ’´XgÀ$N]“$ÛÐ'tOÍÿ¯¶“¶_R‡ñ–OA_)†?hS4¼¦—Ó6>ÈÌ…´owBÀ)y†I~ Œü, ÅLIäk0©É\$å&Ÿø-¼H£}ñLJÙ8JüH&LØ­)„B“(ÁšHžðs2ÏšœÕ¸½èVÜW—‹Ú‘@¢„/šðäÃt˜®†zV›º>UÓ—§¦'‹aŒÕ[öÖ‚ZÉ“öaù%öAìðPÒ“xžI­äƒøŒ/%ˆ‰Ÿï‘÷VfžwšªNs9vå£oI`ôIîÅï\·Á{3´ŸGǖ˲"°Ø”ŽŠ@Ä/ï8úë–v>Ðö?>‹¬Ï&:(ª„| ,Žð“¸Ë-¹Ì©9šƒï|Ü‘ð/s)ä§hƒ‰„¢í ‡~siú‹È±@BC_ Îsð~ÅÛ~èŒ ÍíÌH®K„Q;Ú i1£Ù3Ð1s‘íǶó³Ñ;»\BãèÑ|W¢MØã±[œäïk„dGDjíšÌ ve4æ¼7gªÚ‘‰´úÜÆNPÑ•¹÷#Ôzý4ÊKªSGrGðÅdU;'tªý:\gf碾ݡ¨ëuƒ¿°WûÛ Ú…÷è¯p0™¶@j1pGàÞ`Ráo~ì@N7ß•þîJØ©zN_Ìlçz õ—y”o"¬DºQ¥ø9˜ÎXÛÍ6RÛI %mÍèZ„Ç-©»ÐN»g"­o⌇ޖÚ$ëVÝÃÖªŒ8Ùècæ%â}æ%Ýè=+~À”Ê${©j}ëR{¥õL¿cg°]| ‹w—Pþƒ²?6CÕœlñÈêŒ=w0µþŽ4¨#×ïR6ÿ;k7æ}  ­ßi•a [J/°OÙú*uýO<¿‡>Ò{ð¬òN%y ¸–·å3õjw0ϲE^i{ à´$‚˜ê£hŸìÍÒ±ÌãÖÅ E…M”ÐS€@Á$àÊÜAúýÉçPÒÞ†:Úß.²íìÒµò±àÉO®"úB„:Ð6&Akż$íQL_ Ťƒ;$O¿Т†4íû¡¥}@tˆ‹º~ô ûŸbë`§zŠ„áÐå‰ôð™Pû•E£H?H8ðIt$±‰~Ïs)õ3œ;ñ’GÙGéWXˆ\‘÷(ßǪVGw§»X÷’m\0R¹CÏß·aÓÛÙÕß6/yÉJÊ@¼wBœÀAAû†@X¶7cäsN„È‹[4>;"ÿ1Åë÷PEZ¢8į$9åêÓ–à_´Q¼B5ønSœsÃmB9ÌÔ7–Dß’Û7ù6xôgƒò†ä@Á.ñKì ètx'—§…. Ä/§žÎtÀÕrµ´»äè„x0gcÝE/4öøÐÍÚ_¢óÜÛæJ?ƒ{¢AùW È]mx%<âòŒ…­x‹¬wf:»Ëòv”B XðLòë4¿ý£R-Áå ÞFˆOjæã7#çOßÄ´ ’siòAcf´7±¨Ñ—ÞR·Ð¤×@ÒfÖBÎúô£{©Ã1¢uÀ^‘2É|K‚í?KÁ4fW ®\ÊùGCªŒÓ%çQîþ Nnñðä†÷<鬂Üy‘5Iºê8„Æ‘5Ç[%±bö„ë÷] `‰Gy†–,.hÆ?®úû«ôú©C Åá\!´# £4ýžÌf­f1 cõW3Êx?¨ëj6/ÝP³’5ö,ÚÁ«Xu&eKÊMBÊ-¿fðÇϰ“{®m1/{I4j®Ÿ:ζél;´ïŽFHµ=2“øµó`¨b¶ze{4ŒW²ÖhÿßjÈeœïJÝTg`B}˜I>ÕLJ[÷éY“mjíwµÊv¶{§•¬}¢Ä¥Ð\üÛ¦ÿ`s’h¤“6ƒ¶>³ç:ÁgÝÛƒ˜K¢½*ÌPi꥘ï ÛÏa3 ù¾Ú„'²Ü2n1¬†•ìnœÖ’Ô¿Èû+s~³æÏ²Ù©ZSýoJY£ÂcÒš«ÙÖ¾‘þ2¨CwûxþL›T=ß:””ÙöV°Úvµ×I÷ó³LýõòÉ_ÚáÝûZW4Å¿«škcþüÕ¦Õ.´vàŒ´Åwìáú²ü^6mÛÙžýkšõ*kmǯ°~ËÛRE ·ßô¢ÕÒtZÎ4T$ð. db¬÷aðyÎMÊŠòæ4íÕ"Ÿ`•°©ÀÊcgŸÌD¸X?VN!ý/|á®Ë(?û#ù’úR›‹£µ¸KÐÜøÀαNäC¼'«X^ÔÙâ(8&ñs˜Âà;y–âE̘´“›Ò´=ÆíñEB™ÍØ~ÑlÔ]R2Ê’É]Ý!$oi_1ªX*ƒC2Åü%à»Ü pVm{Qâ6¸u%\X³Æh5NñL4xªW›ÀÚ×hòç„S‚WèûR=ë׆‘ã$ïävν-¿*‰˜yÁpÒÉièg®õ‹üʄϕ0ÊWgÔ« a-˜óM6=ˆð&i¸IÅÄ% Ðþ…q;Ò¹L_Ѱ“¾·IÃ.…ÈÔ½:ôå æ7Ù®™'é7_æmY ¢:¡JËi{ë÷¼Äd@›m¥ý_ ÔvI2asÂXoŒ ÂjB6Áú Ш½Ev y{™'1Ï¿qåúñ‘v!|=—þ†šÉff¸ ö-ÇÂ?]É6ÐðÚµí³ø0‹°5:0åk¦NA”2Îæ§ö󜚯ž¾gZµn£\P ï• èlgû§ŸA84Úù]P×0aÌv)Þ$¨u?Åg3j]Ïat—gt;$@æN¤õõ õx]{بe?àz“vaRÂÁ{Nx„ðŒJô’ö÷-Â^¨9ËÎΎîâŠ6¿Љ÷uý™Å¶>jÏC‡ß'|Wèâ‡àÖ-õ/"ªV{Eìž²ÛX\\ƒz:Œ÷ \L-ë6ä.XÍ»i«½ 6„Ï_d/1*t‚–­ŒBAp(çδ±úÄwÏù‹g ­±Mø9êG1ˆ#Xžßç²ÕÞ. khùy{s/Z¨¿ôW<¥Èbî){úhòp.ý„{öN´[Ÿ±,6}¨ïÅ ´ÙÄ‹®_úÇohavÊþȤtžÓ—×.-‹nļ¢?{Hö'ˆ¶ò°Õø»Ý¥ mÝE”ñl¾µ ±¢ö[lo{/Þ×Ú¤N²IðØfKmG&Ÿß¤Çæù¶qv‰]bR‘vwò9—{ù9ƒ4®àÙÅ4žhËox ¾ Mͺ’[‰Ã(æÝÙÆ¤Îµ±‘¹´U;­v[âÏ´j-¸Ei#æ}ïB¯Ï~G½½E]l…}#ûá¨ýÌ=ÜÕw¤i•y‡ú[—ì¼@ÝôDàC:q]ëSÎëÖ•°!`±¢ ¯»×n†íZê´zeÞh£ªW¸—‘Îk6Œ-ò_Öÿ†ÈzåìÁþ–´µÍGÚ#uïrö }$ÒÛŽd'ÆN6ņ”Î{Û§æPkýÛájײ롷*Ï¿¾H ø):”_zEö~¾¹ ù<•¸½ì„ò_«KBx³vïÙXõ $e(9“û“Ä‹‘.´²z_Ò¤ÜÑ­¹N"­yÇÛHO‡ðÕÜEšàbd5Ü…@ùãÞ=û ÷V¤wþCð§n“·q]í…/ǽî;'ñ0~sМemSؼŠ7Éö—.½ÒïÛ²cðÐ2¸—JÿOê"Œx6J^´¨Þ‹ç"K˸Z}†.!,»#ê¢Nñ@¸–´÷@­F½HØå‡s©×2ø9mòŽÛ˜º-Ðv-Åæ,4/6 ÍÙ4øÞÑNªOÙ.ÐãÍj·`Ac i¾F~þ¤,cÉïsÞ7±£luS[¡‰;’ò>dߦïGÀ•ªµà_vá*ùý"¶¡Í¯¸Øþ™ÙAo©ÅjO²ËSÛ¡^JÔÓæÖ:¾£=›¹ÉöÉÌBg<…ɦÚ3—жíÄÖ´!4 Ký Xxè€þÝʉ;mõy@ýNt9ÛyNòãò^lGŸŒpíĺÍïU‘¾v¶Ì?MoŸjQÀ·1k:p-à«¡ÉanýúžL:‚F¿ M}P©`Ç™3UK5m½·ò ñ¬ MðÓ¸Pñ.þÛñFÿõ!·Öâ„ún3 ![RþØNJR¦è ¾^ƒòKvõmC=>ëdŸÓ–× iúŠ?Ú躱ØÇþ–DèÑŒ7èEûÿ!ºŸÑ÷#Aòÿ†»°~ÜôÊÜÎè‹-[òâQM? vg°óÀd (}%U´…óÐέåBiÉ8ñx_8”Ù…×q_ëmiÓs0ÿ4»[h)9 <þó7ÞÞì èÓâmn@ºÏOk ÊùlOû>sÑC5—vÌ€™îüˆýI{2ïô*R,Ñî:G†|·™¼Ýˆ–0µî@»”û7a\ø‹¾w‘Û7ÔʼnEÜ‹8¥ŽÁ‘1Æb\ Ž„ÿ.ÜmÙ| ':CG†S¢ç©Ã-Ëš;lÊØ¾/¹Öެb ùÌp°yèˆ÷_Íq¾ÐÕºA<ÑÇ–ÔƒóuÛZ¸uòÏ%ò}šz£~9 Õ3ÕÐT˜&Ü3#ŠzÜ$úõ$)¤ŒÌÎ÷·ù5OAB[ò8¨}7“m× &nO²µ}ÜÂ9þ­;: `¥‘Fˆ;¡”ÂíŠ)ˆý»®båѸ3E0µ¶Êú”·µé%¿ iͳ„Q2!ñ‚â‹ýÚf¦kl§N+:ÍÖoñ¿y*‡4Ý.-ÉþÿaR™¬«s ÿPWªÓÇgNtöõÛx$PîW5S‡ÜßúýùåW³þ_ø—=:ãgWG‹Š/c šW(keÛ´_!¯^[ñFº%ú*Á4WKš»9¡/lábA1¡¯ƒË¢þ•æ“WŸ˜=Å:²(²o×>œ›±{~ÿÁæ#L_TýgècêCj¯ñ2úXdâ¼ÿbÓéw}÷Dp+Müm:õà€Nîžk¯ü6Á¸©Oª¾† `õcAu[Z¤ùá_¿Û€Ö]Û.ÐWijh×Üþðؤaâ7!ûoä ÿ›}5Ai!üA¸Û|!TKêRB_¦¬T{½g=¾6# Ï:»Šzš÷Öð«ÌÄ´¢óÓ† îI+Û„Wu¥ (¡¯@­¿û‡i}ǽ 7O輋þH˜fM±úi)¢l®jëàÚù% ¤ K¦9®>ž)"ˆt‘øÑ·n1Ó»¶Îh•·{ôHDO«ζ7LN»Ü›÷ A¹êµmÈý)ÒÃ-ÍÙ“’&ìX—Ï®¾Ê ƒ.¸1Ö~™ }CYñ…ˆaïù„@-…$b²¾Ð«0޶A’ÜÇ9_â×µÖ`Úã42æ2TGïÓÀÅ=`”]B¡Ÿ€–O£=†H‹¯pxŸ1m&˜ó:¨ž+qMÁV²Ë†ŠÝÚK¢©Ì¹p!ܺÉ_9¡oàÐÄ]‹bÔòµY‚ÀӘؼë¶Ñ{B_¹#„¾àaÞ6V?Údõ¨2Mã¼Û"™¬Æoå{a0“ˆ·Ñ ë†@ÀÙí–æXH.-1' ̉WþP´‡J-T!ïõ+ÿmnž«Æa¡oàéL:DÖ'ŸgâÔåuî+ùÞ}­’ünS»®=YwöÔêíc˜`¦3µ\›•"lY §Uìóòýü>ëç35Û mEì k{¾³Ç—•8¤¢¥I¿dûgà™ÐN j²Ø}.~ØhÒ>Ï^-Q‡w DÛ»½³m,›àÒÆ 4—ð¶A¸u&=Ñ^KßI»ŒÆu6툼 »?¬ÒŒòž˜´`e±]íLGÌWûûB ÊSMœÎ}j+;óÚbû>tÉú2M• Ì{uÆîD6$õµ¹~äû»/¹Å³Ìy<ã²2~SNè«IŸÆ;:˜²£ ~:Dó_ÎÜv„žeÉÝTÆ¡{;RU¯îSNèW§àðF¸SJ™ß³§Ë^°óvüLHÙö:P\Bl ®­HîsÒ»‚þv€}Wr$6Vwµ®ÙÖÚY$» æLζo2×Q« ûÝM¢I7†¡âcÚî|¾CyµØá8þ Ìþ(%ÑÀµ·Õa£øÔĦ×"Žl)³…ýÄìgöcç,Åp‡;-ÏýòÒÏEÕØú‹´œXž±ÒÓÈç³\y °Š2ÔÝM>Ž'½'qï{Š« á/"ìk¤îeéÿÕ[ÞK×êx_$€èçòÍ«ÈÂË`hjLBtDŒÞ*Â7J.!¾FëÓÑñy¼5áiWmÃ/¿×îF[øØJøƒäÃJ­°I6¼vS>q‚'ô•cýgäö“¹ŒØ¥¼ÿÂ5Q>¼ïÍO†û„ékS3o“;샒 íçR„•³ {7âvÁî!?´—bcœu¶+%´Eã̃Jn?óí÷¨ÊŸ:‡ï¼ƒ[Ï»f]êaðf3Üà{Ð]  ÉjÒÊ®‡þ«Ž2С$eH?h'cWvž„·u”1¶ q6³9Z°‘p41„÷  <‚«øÉÞ·‚_ÙgÍÞ¼ywѬèÖ<ƒ;ZÌ({¹ÇùÍ>«mÉõob–†68ÍÛǼ´R‡:'õ²ùvÎgç°.÷¥$}<×W2ÔË©x­â´?‚+^-/oF·¥}ÂGñmŸh+@¢.Ý'i7™Ôñ€1¯Ž>ݜϼZ—vCjÎÓþ÷‡ªáHÌkþ7çB'¾LmÈ âT¦ëhç¦vvº@ü\JßÈÛA=×Õowï.¿ErõrØSÔË-Ÿ ÎÕ áB”¬ÁqIŸvìØÓÚÅYÄίlúv}û£©»ïr½­ Þ¹®úµjo£yØ»Ü#N]n]‡0xfªÚþ‰PIæ">AÓQaŸü=˺ ‘Ø«¼•½€F¢4G‹¸W¹Ç$õ,me÷LÿÅ9¶v+ˆéÿÃð<ªWR—Ò˜ü‹¶i®{!°•ÿŠÔkÿVѪŽZ×DD´ñÕe±˜u"Ž„þaXzE†Ü,~‡ %±T^i–* ™.ì@Æ´¿õë¿P;xqê-êïÍ9ÓLq@h+XaíýÓ'Ø@¦ …½¨öSœ"è?¤Ûªz´åKZ±àòµý–\hëµ^ΖK”ÛŸh«¿ ^úsªÍˆÖÛ•+ôcëRÍà.Nãþ]pE ,lܹ—[l Þ—æ¾äyi>·È¸ÁP¯€çÀ<ÝÅäê¯EÆZö³Í¥¼ħŽÓyæ^’€áA„‘͆ßÁÓÅÅì=  C $–•°¤ñAR.ªûѤ]6.›ÒDäçmúú(Bí^™qèîk³ÀLý)öƒ’»lxòì©U8{¡a?=‹M¢N~ZrŸK½ÆîVhr)L0«Æ"T­„ÁìÓÒRÓJ3 ÜfŸ²M";ŸDDð5ŒIЦ:Ù^‡Mi+„5üþ0G‚dÌ·w´Âvc"ó8šgçÀcÂÿþŸ pÝø«‘7¦†`ƒº–åV4”Ûxá<-Ëçða$ÅÒÕh;Ç÷Ðè%¯ ÃJ@¾>×c”ûjiœP'ÝÀ›)Üe2ÁêëÜiÞ2y!Vh4 ŠL/[87RéìXžíkÛjk^æ2‚ÉYF jÐMð½4ŨŽ/ é{Ï’¿ÓÉCƒçß@¬ÃsY‘fÔEÔ½gzd²­ƒÜé¸uá·]æb×îŸÒgžCxvn|Ï%ÔÌC1›Ø^pa•®& ˜è7ò®ñœav¼4“–8‡gΰËhˆ7ŸÇ£i¿Ó +9 í“ ž< îSáax! ð¦yÁ–áK óû“HÎÇÂÂZ”é Úçàøp{ Óí2:^„©HìL&M `€šp3ÒaCžÐô';¯j¸µAˆÿwéQøkLü ¡%·ä^üôåà&¥¹‰%N9`zÂéïcë6F3îy›!RÉ‹¤OÉóQF˜AÎ~t¡Û­‰Õl`dü/§' $t¶IËʳ¯ÛÜÖ—Úró¯v´Í³c§©8í™ Ì©«yþI< ¾¶>_Z¥WÍe[h&›Ûæ™Ñr:Çæ0^‰ó>Yt8ipuqUœzI]Ș2Á>u¶ï)ïÃþVC¬0?Ô¤±‰‘µˆIϯÇ݃ó G…¿º‹°åèÚ:L\GsˆÑ&\“æ*º„Vr1õsÏEúy.„Dz¹~¼!ãփѤ=äæÓðX‚~õö–u‹f[• ·1UŠ$Ü® ßÊ6®„t™kiÿSyßÇ®ˆe,Mþ*­Ñ`:åëCþ6´då ø÷ÂVóƮ`aà {/3’p‚YvTøž?2£yï‰påo´›µ·kâš-kg2¦ðE›úÄ~Ì>kÓ³ŸÚ+Ùï¨Ûãq…è&¿Á—¡ý*(?´ËjÏg웂fèàiE6!=ü«V´U[Oµ´´CYÎΣ4–\åê’ñ^‚9çžIƒ5±Šqa´K>Î}B…„êüp7,” D®ÀEm›¡™'?ø§Ï๫ý„ðñ¸ÌžÐŽØšÉÝ8Á~%Wþ¾¥ªæ/êR«ä®Ä[ŸgFNïûðÌüD‡™aÎâw ¸ °„i]èö§|J ÆwHYÅ;øÙW‰û¾÷,½p ´µà&¡p-íY•4OæÄuœY(pæ(” ´%Æt€e_æb‘ÅâÛàv'ï½h‡Oiê=u4aî*Üë(ù­êÄ3n‘•Á‹~<ÏÅÿîk¶ ÚOPÿ9ßçr@Ý—>E¾ö'žú[/¾õ”mXù}`U{#ß[¿_l0ßÚt);Hè/—JÃO+/\Vs;ŸnK ˆ”@E~@ô-2ˆ<=aÔœÃ!“°û<ùz¢ùRHõœWÜžÈ~‰™¥Ù„©Q¯´ ÑXd^Év«›f?ÒŸøjÊ Å¿ŸVÙ!Öñø?m4|‰ø•ðœZÐ+q¿½^ÿ8‡>c—ƶ'øvyÍ…–dñâ¯ò‘vzÝ]¶yz!ms°e 7Ç3¾œœéŒ–©Ùf™lFf}´á™ Ÿ›´ªcñ ^¨vKv6 ¢…žYâtû)ýÔÎËÿ.ð‰3“ÇÒ†'RøSíAx°D5u^~ íñRÏÄ÷·G“ÚÛ~ÅyhYeÿ Ú´<î[5;ÓNàbü~â­m_c æk'îçsdî§þW®ïx+l“~ØUG0…íáGâìªÉ`%ó„ã_¦FÖ¡- En‡ÇÄçrð‹÷nýòÐ>L š¿GO&ÍÉàëc„û£!lüFúÐï6Ößåô=-tXäÊ·;G!Oûr-‡mû›èÉfc¦`ý3àÓ•”CГìíç s%ÞkFýq¢÷,A3 KÀShåÙ•ô~…î|^†!//aǧ@ üN¿Û€§ýXŸaAu!µ´ÃãôïäK~8惡Éð›fyn íA±ÈG{œŸŠIÏ/?’þ"î Ns?ÿú€ÃPíâÙîîB?¥¶cµˆz}¤-Zòcòz (ÉbîÍûéÀ­–«:äöã¾áA;h x‰I“G¨g™SÅœq&žeº{²á¾.ôk]—ålêCAžiÀ(ý2ÍKíëÂÉtGË~1ó)A¡c€òt#J¿1nv­ÙߎÁ^÷… qaZòÓ@×Ãן ^CµšhG¤¼ ¿ÅwŽå1æÑÚ¹ZLi¨©w H`XdkèƒM…•2Í7,4mÌ"Nؤ`8¼d2­%.4Ô‹î„Ã-õ³Fº¤™øK <4E¿?Û~C`uÁJÿ°Î÷BüÕ‚Ùö6gÔU9A–>,MS¶£KÓð¯TìJÄ~¬þÛvç°8Áðçw×OÒGëß“:KW¨ÿ¿d×tYnq—&çhèŠ7‡&ô§(¶&4ööëO5³È:Dð¸6Û÷þþÃ]k`JcmÀëµ]ÎUì"ã·° ‚vs‰.ÃØ7¸/ ‘»e˜|“IMdèê_¤µžWÿ:T­¡EÕ_×d¹ì®ZÙ6÷-™19±ÇZîýƒy3ܽ©> Á½ §×s7ßDG:[œrª îøÍg˜xÖ"@f%Pùþ_ýÿŸ Ëi"ç3ðtQBß <Ð7x/¼o C”¾Pèû+¾ýaRÂðƒ;)Ùw| Aæ`&üaFjƒÙ E„mÃBÜ|&ÍmÜVÛ†Ô¥)-αµ&ËäFå=H`ÙöÕ6ý÷|õDªÞ>C%†H‚H }e³KZlmo¥%÷´ÒZLˆZI‰‹¦@ ó$&‚²]y}X°°†ôe!ÚŽ¡ökòrˆoºA ÇdæÚ7äw¦ßŽ•|ä¾—0‘º¡áL¶¡þ¾ t0ŒÁg0ž0å?œºÖöÂeZPø‹´ŸS•Fuo„ë°ivÌ胴‰VíÒÎ?RÎ`!@'4KÓA ÷GBí=†÷0ÈVn!Sy1ñ/ PeBp á YÉ‹©ÁëÐ×ܤ¼˜4sœp¡pÒ¶<á³O’N«DC öî „›ÕO‘8ÇmiÓB‚pJÚ"ÛÑv¢Âãhß•ÁGm©ŸWP^¥ñìçºíé´¬UÀRg59ڒχÙÁü܈‡h R‘™p“¦:Ôf2““+êû0I?¦ÿHÓ¡Š…fOÄ„n³”PL. _?1)Ñd¶1 ¢-Ôa+~×ÚŸé1v`ôC»‰î½iãÄ_¶oö #|#s„<²6óÂaîù#hÇ™‘ØGã6ù#XøZN«¦?¢Î˜Ä°ýöäºë‰;™ðóí]·0&|*ã¢.ënFx²Šý\v3uºqöÀ½·c¸'”\dS’{ñn~?èÍŒí,Ò/ë?Á•úG˜—œR^?Ó..¹Î¾ªáp¸Àí3“½ÍÒcR¬ŠëÔÀmÔ(ø&“ u΄eFèËô-ËwÓ?Ø3ñÁÖžz@þ«ž:Îjüðô—·=Ïî“K«Sæ\‡ÓžÃú$}OBs˾O©n}HßÍ{/â!Gâÿ+ßšá cÓcIŒ^úõD¸$é8#uÍ1ÔãHú?~Õ{à ñ?!¾Þù.‚Üõ£«Ú',2âÉç¥X€°2Ú£ ,ÚÚ­à†>ãÃlK ô=†T\ià=«TÞ@0Øf ÔÐWq‚ƒÓô¼µëqzjá‡4l²­Z $ô•0¹T#è @=Dš$a¸¦^ û]ó ó3y##FR 6VÇþäG¨âymÂs˜tiPow–¦¬˜0aãþcùa§¥z–†¯êvB- ]‹AW_è+?18ÐWïS"æÐM/À<7­ñž ë:èð4¿.n¥®Š„/‡„çJEB_ þ[©ƒ¼`p!©Ñe&mmi^‡A1³ù°<ÄxxÎ)À½A°UCãИlÀ¹„”=3DENè+*ü^áDä¹4‚µ+翯Ëü®Ã ߆¾€&[_–(Tw·‡ê¶´uÝ„>ÿ“L…sp¥«ûι÷¼‡z1÷çœng[»rŒc€;ǹ¯ ^‡A„{XÒâBÞáŒ%L’Ñsp5!ôU:ûƒ #š\ Íì?°­×Š>;9y}ÅctEAz¡‡¼Z›kZïØ©¸&2‚í²iúøá8¡ Pr?¶K0“éCç<âöíÆ˜—¹ æï.ü,¶{žÝºƒwú|¤; h ëkàWɵöu¬ÎLàâx?´sÙãLROaÒø “dT¡þYÏ;÷ ÆtÕ ²Á¥ûà*zAŠïÅ­Z‰ØÎ¸yÛ.µà°6¸8€úî+I<ˆÿA6 všnn6ô˜LC+ÐÁL/€–‡áúg4YéFÿw}$q!S±Þôøä·µlJ>䨛—ÊgŒÃ²Ô•ìÂÖíBíHy(£´5£Ú¡Ä±ô\Ó×4Ec"¦±[¶àÝd°þ-—иإ„§]¢Ô¡ËEŠ:ÅM“ÁM¿lýJžã™ÜFH+;…oL&Ì©ö]öë Bì<¾}a€äùÖ‰zú²Õ‡Þ»4CÞZæ1lÍ>kw$ö'/ßžv¨üÆ “¹ÿkìú8‚y‘¢åý)é4{¶j=û󖹋oßÿ G LšrÈÆö„î$¬¾Õë6$²5näÃiÛ.p‹D/E²6Èa9Ô#g'’< OªÐþO;ºr>šzÒ¼…žPOÚéwùÞX¬H¼?)ï'\gÚ¹¥GrèKÒÕóÉhAŸTþ2aÏ ÖBÄ\ŒZ‰Á„ÿ”w Tý°šÿ-‰{6Ïà± º“½‘zÝJìA:Œ5@È|€ƒŸÝoÛ(øRs&å;Št'NOPâ,­燣_Tïek¢í;œ>­C˜ü'Fo¸Û)Эô?Ô§ÂÙzú˜‹~"a'Çζ®læF誯èö”Ÿ:‡ É4‹U~M˜)Т½0•žÈ;a´$²)mz((„›Æ¹ÒÈóW ÉmL^»ƒ´£„E,ÂXÅ“øAÙë¿¶ålŠÝŒ ¿k¤Ô¾©éGÏô…¾|ÙJ†é ÎÁÏ=©c¯4ÓìFlÚ‹îÕ“ªïÀÖwõ!.4 {wwð¨W§rؘq¸ƒHóô›äµõuv£×EkuÚë ´C‡MÕÓwÚ§'S†-l<ôo—èEÌ&õdÇWœ–ÎØÉlqSxj×ßQ·#ŒR¡ò®b[}e¿&ް ­Ý¡‡WŒ‘ë×ÈÚv=4}P$ƒiˆ¶öeëwì!ÌRí£¿Ä¯±¶Øbþ>ú g+œ6?;û³½Y»eÜb n…€P}ŸêómôÂRç³´õ4®k£+P&Õ- E¥ö¢>n¡-Gºü˼ˆ_7úÖ}vtåshº¶²>!‘Ôãñ“m(ýO[ßÅ?']ø¾DKÚôEên{ʼCôž}î ê¿òîá_vØ<}^ m¯ÌŒbQ[4„ô"»Ñ¶¢_÷iM_ß [ë›!¸¸q *CîîÑõ•r®*ášÐI’iÙëÈÓs®Ôrà ðóËôµ ßq8A7’w؆õŸQ‡?zADS²·a¹Æ†Â§;s=ìÚh—y†>Y™;X¶ Ÿ#{¹1—øõÔí¡<ƒÞ9ÇÖ‰«í†øAÌD™ô‘ôK”|]b .’Ïp‰^uàê—Ÿ‹˜–ᄺc/šÍó¯åmSКú*°ùÊ }åY ú–=Ù³žég¿'çù¾„5¬ ýýo2ÈppI&=Zö´ ¤öùNÍ‹t†L!(¿/¡lДlBsQ™ŒÉ3ùÀN «`|–¼{BîÁcЇ½áû }åG¯Y6Ð:ž°/ÔŽEK´Ê" Ì@i”Ó|ÎröfOåÀ¯zôÃ~lOgcTþƯ„K0KðšÂŸ£±*íÓV¾ÀI&ë X“íY]ÿÀÚ‰%þw\€ÿá?SCâgVwïÜË™É3ûW›TÄXTÎDHÿø¬_œ©Ù‡½¼÷z¶5ƒIóó7„õAü5‹´Árš$µ4ÈyXQ<°:uslùi.L¡,]KI]aÔ%~oªþÅ®‡õÎEp¸s‰†]¬ÀùZ¸z®c²yîD¶ "Àê_} gÇn-êà !èotKwÀ¢ìB븠_­PZéúªhüßÛBì4I½æ»ÜTD X,ÜËíBâ½; (ÿ ÁpYÁG rJÔù|d#´ ÂìàÄW½ Åim_ÑéÕ²ÿ{ “t [m‹ê›Õ4ƃB<ФYv?WÖ–E ˜É /&º{0†Èî˜4}Ðî @ŒÉs¡Ax0 ѾÖÂN7e»5€](gPî=©—y(Á¼ƒ 4…ô‚Çoå¾¼;£±¦¤¿b_Ó× » ~Å(>fZôLlHP4Êc(⟄öDÀ\W˜F(ìwŒ¯û É=ÛÎ,Ò'Ãõ¦¨¢cax(9ÓA„íÃM<BO¯>{‚zpVla1×)8’yªQ ¶öa‚ÓÊ÷ù˜Ü5fóˆŠÿÚ© ™à|Ñ( m«ü&„,¥ƒ´©Ž`‚· šüI»í›…ÛÃd£ð´_$îãÆä&~÷þž[3¿ã(Cƒi ¯¯Ÿ%j)ÙNÐåîôùBýÞ’Lâ¥içtÈC eCNuÙtŠiD¡šN;Æ£ý¢:S‹JÊø#è¬~ºµßSú¾ÍN„`hëXê+¥ðÚS»'ø-xFø“þ”{;†6»W¶S‡òN‹gFqc“Ú¦f¸êALDOç¤çIVYÅ€à™ÝV}m7ö ‹u,õ› aõ”´aɸwB”5ÃöK õ½'s‡Ez¸ÑmUè¶¡Àí{´¿Þɾ3}½­—}ÌöˆÝBX,9€·º±±h8€>qTl_‹Æ˜4û0ZLœ‚þ¿2‚žy1ÂÔ‡ò*ƿ󡓖)Ô˶ÔÂC§÷!õy-ŒÍm˜Á™c³0xHð€¾–¸²­om#Q+Š[¨WgdÂx%פ9‘ëiêå0º‘´ßÙ™Wí»튆ª¶¯»ÅÙѬZÎV¯ÙÒæÔÐeçQ}ZÏØYv&[XÿDP¹£®I¸Þ| ±~<ù»Šð@6vÐ'@{v‡¡fae;C(%y?Ÿ¼¯ÆÃL;º¯ñÛ™D¨œ†{ÚåKè§³§¾³ç+^ÂM¸åµŠ øç!DXkøwnI‰Ã9àl9†é ŽÝµ…‰‡8Ø”ú©;?Ò_¡3h·Ylʲ¡MC€æÚö ­ý‡“¶¾Ìá¨Íj(³§#M6¨KiúJ€U½9î^?6´oM±9¿z'ÜwÄ]ù °Àõ_{†­+¦úL#HK7ÞŸz¾™°=h÷H÷]x™춺£YÜœ…»à7’zŠ<߉וlHíPv±¬O:àJQúŒeF˜_Âà’!øày4þ|³’Þ.® ì¾K_¨OdòB&4œ]lòŽYw°\âЕ—Ó¯Pö“¼x:pM#M|$õx&a!îüÃ}¦ÍBx}õ3“{°UþWo¬‹Šåvåÿ'Ïì§!‚k37qøÙ®½pµÝŸ¾ø=BÞ×+GÛ²‘œyŸ|­kß"¸¾ab&”VWhj»ÌÕPÚ?u¾U#ðÚê!û+~£@¿t§ÏUÒ7X,¾Šs;öfÒl™ì¨ >#ûæR;·ôi@ͲÅú V_ØqÔÑ1htvË{“Öºàõ e£l|ëOìùØ%v’íFÜZ{¥ú»'[mû”ÝB^ßu}%-Ä¥4ŽQú—[â^½ðPOù„³H²: ^jiÙø±ìŠ:E›/`¼>½E{LàSÔEýŠf{!ü¹xW éÓÙ†óËìqq€ ¹Ÿõ"D9ß=ØÞB0¸cú&:ÃÜ}ç’èØ·moï—ô'mp臖t¿„ÊÒÙ½{}äž§úïmý{þM;®þ“ú~làux7MdMÛ&¶®m'ó‘=ñó@½ŒÅäJ;L-`×Ãç?ƺDwñòËHãl¼Ê†w—Ò¯uX/° ñ¢+38Ý‹zÜMß´mɸ†ù¼ˆgµ’<-gÏE¾°Ùì†ÎÝûÝ«cƒo ï|s:„RçOx@{VŽã±—{ý-u‚—¾}õúà…kâ×iöOkÂSÎ,¤†¿^ÆVù ¢=Ñ)èõâÁŒÅ ^ƒËÙQù±/~ŠŸèlòæ{,ã·ÈVÐ5•õø¼„eúgã‚vGÎÌó)þ"ó¿²`¬yÐâÂßD؉1;÷ưk@ð‘?†¹—ýÌÇÜÍÖÐÆ‹è°æc8zÑ·QÙ6ÞúÚ%TÍ!ï£/- ΢vÇ~GfïoBæ„­æaúãÓEÂ霄#(Óú™™örtohÉÙ^´ìÃм‹s}P޲¹¼#ýý²Ëúî…Îû•ÑÂ<ª¼à}‰_;&JM‚@i—Þñû÷¦íè_/øËž›=ÅÜÖ«¢X-—¨pß-Ziÿ~Ëp¢M*H¢ )X‹IóÆ&A£”’Öi›¶ëæá ª¯‰ûæ$F¡i,³]°üÿ!¿dŸ÷4Í@\ŠqQuØž¶tí‚ðWõ+ñTìÊ º––çâ*Ò¥¾Ð² Ôú-pðW ªpl¾‡X|rÔ8b.:µúß ²[†æêÿV_û¢—#hýÚ¶opÀÛË\Ò¦œ>"ó)´î’«ÿ¥é:ØMé}†É |·ÀHu&e÷ƒ#k滾;ê Wå´÷Ãeúw<ÇI4Ü{¥ﳪKô9o*ÕtÔa˜Ò<ÛÔBHëpi@ƒož&Ûb$–·ºG¼QNÛŽS9P%À. H—5h[~8Ý@6üð!\a÷àùÍf{µ¦fƒaØ•ANöj2 AJ[¯^dò] Êp XøÀ_#ÐH¾)q̯‰ñ–ª¢Z&ó… Øüû-„dÒØL„ÓÁWÛ€\äãÓtØÆQfÖß.G[k´ó„m«3Ö |-å9¯ÞÓÇá2Ý £Ô6nü'@BÁðÃ}3}eÞ§ç¢U œ@Ñ ‹ù6¸Íâ1°×«zÊärWê£h/Œ·rTCä‚§,mp#ñetQ ÉLO„íѽý¾ß(Nì`œ:äœ{„m©æ\‰Ò¼SÕ¡©´Bá[ö¨2,~PÚÒ†3 ¾ÆñÀ­Øý7úÞ:ÁbIêr‚Le"}÷¯Š_"·ëéw¨÷§r¿DÉä"‰féP"iê(›‚Á8[0“‘WaÂuõ˜®hoÄï`B<ÄöÈÜh½ë6…9‰nV9û¤$•xœ0¸K%AŠ ŽIÓ*MÔóé’›péÉ¥ø`µ„ÑÍlrô»«â &úCðóA“˜òÉ6,~£ríLš·¥N Bäß“Ûñ>Í:RgÝ"1ž[ñÒÔvðÄyœ@ãäæµíÅV:¡Àtìn? ÒÛngŒ¯H_çÒÛ³f;ò=Å„ƵVH?gY Y}hX4\°®š<ÔV`›óœÄî8vweL¾c‰{I@is8Úš²Ñš¹Ö÷cú(AvZ‹` ¯³ÍŽ_…ßòCÚh-½Zr¬ÞõMv'øñ¹éP°Ô«¶câ0ÒÂnoì »»ä+Á®§UoÀ·.Ã}mqç{÷ØÅ´óŠ\m{"lÑe¶‡Ï™9„݆£ƒìKÜp‹ïJÓxîc›Wobk³¸”ƒÔ(„éÏ›Ìð”#Ü¢Dä‡ò)M`:Ú‚§r8[ ÕUkò5ð{Íù]T}qøö¢{×Ï\:ÌçoòûlÉm¼s Іøsl}t#ðs¬­Cÿ|ƾJ\Äô¾JMæÄ$ØÆì…¨«•Ÿñg°ÓŠ–€Ú"mhi9ÁºÌ>%“NcšzV]“ò8ô@BK·½9Êý-üŽ¡~îr=yÍúå8(Š8ÒÈ¥ š”^"´;¸úKŽA¸8Á^®ø—!ûËÚ¥=™°ô¤zÕµâeí…òŸ¹}†ÛDÞ 9Àw‚_ÛK¸„ÏØb~`éí¶£ «Þ‹ö– -vmþ­­¡Â«“ ©s íh™Ô!ÄÝÙûft(n+ƒ—à´ãýê^v…Fò^X’p‚ì$êHuº×&øßÏû>Îë!x”íÀ•§Ë)sbuv"x2ÿv„ÝÜ`ëûàŠ6U¸‹†ôTêúwõÕ{Éó^®Oö%+·ã&ž£‹ïçߨÞ{´å_ ò §Ú;8@ðf^Oð-Òv±;cÇÛî±sìž8íYËîníµÿ;hÙ>ŒmÞݺwRÙÀ­Ù‘­ÚhÝŠ|HxáÖ8^+süÇe@ø(‚¾‡â§ ñXb‡U­cñØ~¶i|ñ ÕÛÙ¦¥v²4ÜwM½hÕ¥§Ø.‘oH?dC£nQÝie“7z ðßÝ€ö¡}c¢!hõ³uAÀ!þy=/b©³ð¬‹Ö°S£ìÍÌ5¶*ãïãÑïmZútòù6þsr ȆèPŸ&Aóz”çÛËSçÛÙ!àïs¥PG@³Oƒgº>FN¬/ÂýóZ=f0¾s {ñýVÙ'§?_£]¢à•p3«vñ!ŽD Þ¸C¶ ÀiGžD˜Ã©×?é7y>ÙíŽô%v†LÓ8Í`³C™áèàKÁ8¨ÑV{tþ@¥©“ì—èC%‰øg~*¡—â±#i§‰_ã§¡Cò¤Ÿl"MÕ¹g㶉¾3¬ü"A…ÁajÒjXÐà»G &ÖhrPàe1a¯¢È–£`íFb ç¼T?²Å*Ák:„L|SÏOøi=/t%°LAÂM Ò½ÚÀoC_;8x·Äƒ¹Ç¦6+ÐhÐàw'‚W ’' H +Њ«v Ì VˆE³/E@«W[»¶€© Û-V¼–v(ß”Æfa¸ÇIG6ʤ­#ÐÄç|Þuºª„¢Ò.¾‘oÛdZ u¼Ý&ê,<–¨mN_¦f3úï ®0¨ç ˜Ãž¡giŽòñõ©ÈßLÀ(@{Ùl R#¶Ÿ6ÆöÆ‘®òÛæY&”jŸ´(£-ûÌ5 º¶€«ýd®A…Õ!%?Q·*oàî<‹üH3b×ì ßIÛ)Â1mô'—L‰±OÚÉéžÒ”ÿÃÞyÀIQloûLÚˆ"*A .ŠŠ˜1gDÅ0bƈ#*¢ˆ9‚9\#DÌbΘsàš«‚(ŠÀ†Ù™ïy«§gzfgv¯÷ÿÝ3¿žî®ÜU§Ò[§N©º4öE&A¯SÙ#ƒü”,Ð]jPœê Iª†å÷O…“j•mòøCÄ|I5ù)ãÒ¢Å_%Õõ|éûfÃX²Ðú9NÄ÷¥yr$m–§›NcÄû¨jøÃzÅž°F©mšõîgs+'ÙŽõ”mÃÁv;@•&>âé×X8òíºº|³T5 ÚÉø¥% Å50—?\¨ËðϤ4¾÷¹ào‘¹w·˜$ÔjvÅ<0SûY?ÌÎD2m´«ûÝmå½_ò ÜeI» üQ¯Ú€uCèi¥Íp@YݾÜuâØ'áÃÐç·[-Õ¶EmOÚØ-8„l¦ÛÍÁ´<<sÑ, •” ÑAIЫ@«—¾àž ÿ ùÉYIÝNnëáû˜aÓ"gR/«1˜Æµ¼ÝÚЦ7œÎ³8ªüë @éjû¢ô È/Qø ùäi›ÌäþÈ`òM€™Z@ÜΊFòPa%'p=çY0¿2åsŽ­ï€ÿ`Ø0ÓxR],.@0¤ùÆ<ò¿þÞ÷üéž|žç™î}Fb(˜¤;D×A1ÝÐpÏG‘f6´\±>¼ŸÀó·6„f@%@„ÔD¯ ýãùê)|Ö À²äQÜ}Éå#6¨¸Ó6CŠïàRx¸âMÌá¥(íb ù#.[—+Ÿ8š‰¶îÁøqX–ã-s[°÷D¢1‘<Ã~f7Ìo:‰¬!ÐOôì—?_rïÞŒü<‹gÊ›ç•9èî©ÐdÛMãSl÷v`k„ü ‰G}úÔnoÆâ ”¸·{ßUz´ X]Þn­$ä»O•jkOs4ŸÒQ0lý/±õë|• À:¥Ü¡‡¸pï•ÉûîzóH`7[õ-ù*ã{ñåY 7«Ùzõ#m«(|Ùó¶¶\ý~Ö!~0îI¿¤H€ŒŸú!öxM¤¿Å¬ aÝÌ=M)ꨓ¿rB2ß²ˆ'„õõ{PΪïq®žåQ#߸ˆpÈ‹Z•‰7>Zå¹PâîLþ ]½°#oERÓ"’Ê ‘Ê"Ô²ºËÅ=¶½g§>†¶ÀÀW¿1áÀ{úÎZΞ©†‰òø7¤t‡£ã™¶Æad-ÉûÊÄù[ˆ_²ö€ϰ˜qc2eïE6·ç“µ –Å÷TÀËœ&`e,BuŽÒçÅïM·S_"Íœ°{“œ!€Þ7AµõhMWÊ_Ñ™~– i|ÈF÷ë,:xvùħ¸yÌÚqHZ%})³ ë9wW««£þHR;CÔÇ2òPØ5RŒ¦idÝÅvzr4}wÊ…{Tˆú-~«„÷¢›âj5kÞÏ^"Nör4ÚmŒíW}JÓÃbjíÎH2¦Æ³{h un1ÂS~ïÝ…s t°nÜz6"ÜD¼j¡Ã´ImáÑ)¨äø˜¥6‹ì䟽 ”NÞ™}/}Œo™}wOøÕ‚Q=ßÛ8™¬Oö êFÚˆ¤#ð9*:¥T©Uz%ØOઞ` +¦0Õâ¦?Fr¦ÿFrøÕ­Hn %J_±Ù ã- ¯öø7Êãû€…ÆrMûy/ í R+F'ÁO¾Š-§p•ë‹ÈZ<ôMNÎ4­ñö÷,Âû’¤çÒe¶´‡÷'™~žR7µ(ê÷™ˆ³eêvԀͤŒmþßäëèý¦'¡÷¥^šïÄ ܉:’ÖQœulÑé­ŒÃßn²x]_ØŸ;Àѯ§…4kª>&3Ц”ÔÆÆá/êäŠä·Ÿ×…gur1Ê×­ãNõ%|þÓª„j’¿ ÔÚü¿þ× aaiùƤ}‹^·b<'çÀ ð-DÍå!÷A³eSGUÜ~eœÈ¡nõèû¹ºtÿWŸUÏ—"¦_óÊ¢#“ÿ€+?ŽÖäÿ˜{îi’÷ÁvKƒÃ|å÷~ø…îÔ©ˆv€ ¥¤ ®gÐÙÒá(;}R)`·ÙŽÖwœwWCæú=m.v a57HË Â½JšõÒôs!ËÌliÛK> ,Ù* ¸j{¯Ô=# PqŠòÕAÎ#Ÿ/ Í~‡FrýP,ˆŒù‘ô¯1AÓªµH’§Íþa’æõ%*¤/÷ Ò!Ý]c† )û§CQÆ3™^ÉÍ—~•[M ™®åÐÏx&Ý—>MbEú!ÇÏ1 ïNX§²Š+ðFj‚$s €‡g9DCz¿Þ£ÓÞ( |ë…÷SñÌ Zþ6 Ÿ·`2Óœ …`øÅž5 >™Á[ðà´bnø °Vým : mÂèá I“gñŽ.(4q„A°îIµÉPkÒéUè€0ß¿9—£OX'"‡Ü*;ÀùÞEðø<»]Þ"žjšø·)Ÿ{ö^;@ º…ßL÷{i+ 6­Áœô¥î›O1Á\†±Kþ‚E¾»…gjß$o´•Ýà²pHj÷U/´]4=ÜtUW’Fÿ`éP<6ä(æ†õî†Pªõ$×EËo3©?=©?Ùœ‡ªg­åR ÒŠ¼LáJ:…ì&u}eªIj~âç2°;2è‘ýžLLn8¬bbÏd…:ð4[§×q(’ŸþÈ¥D}7þ&qV‰Ÿ¬âeü‰OÒé`޷Ó¥ø?ƒðî§ÝˆÉº¸)gÛ_Ù'^[cê Ò™-.àˆ¤ %I pé‡H’So¶¥ž÷p‘¦®´?¹)Àˆ/—áN&~¾ÓQþUK«™N‡l„ïS ™R,´ÖLhwŸ˜2 ÐÌ’Æú6&¿€– Ž^Í®ãЧB´ zO æ¶Ðã/v5¢ÍFÊuc‹­è;—ØÞÃïjâ= ¸Òèz€vš,}éÜ´ç¯ëÐëynh%æ g8sµ½•|W¦œœi?IT²Û#øÞ‘äëÍ8 ~gÀ&åÔѬ¾jå·Z“å0ß‚2§L2—޶®õ'ÛÔÒQ„‰yËdå_ú&ï”aý¿`Gú|ŒÔ.}Aº0²¥=Ð8”­úýì¶ù[Ùäk/r-2 PN=Ä{Oòm(~_gî|£UÓÍ.p[Žr8‡>óÂèá Á³L8¡½9Éêu@P~ÙÈ{2õ"ËßX7€-×ç9•KÈñO™>òYÝå„ûUaáÝAºTVèç/½ÓÎhxË>àžYññ…à—nçÄûèÀBK' #0[–|ù’]8ä§ø8UÍ=›~øýŒ0:¸÷M=Š>FcÏ«(‹W][Ót\Oúp+=Ú&¿Ô'I“…(7·ð¸,f¿r¥É:ô,ãHÿÃäãA¾MợDZ¼p´:Lý‘:k×#µÊÞØÆ½–æPÚñcã9àï,Âü-]äcE®ïõ&ø¾~i¸ w§ÍÖ&]ÔñB<ê\À;ZX«ß›·_¸Ô?û|è,Ðß•‘Slˆúz-ø,*Šþ‹ôÃ{úÀ–£ ½)áû8DÓèùòI; Ãðþ‰êOòHg‹ÜCŸ°ãã@k’ç*ûÊÒºý—pš×‚\˜r×€ÖÕ«·ÕÜ=I¼ÏÓßeUä•¡õi­#ŽfúNÿûî¡í\}µúé7Û$nàfP/ÈvExcOoyàTÃ\Xú´}T. ï0/+µŸ$¥^Ûw›|{Æ“7r¸íÈø%¨Ó£ß -TAR‚ÏÒæù»#UN´b9syUÒ@)ˆ®ý©Œùöaùžf#øŒ9éÌA;!ÐEK¾pôƒë(‹û˜$õê ‘[ß>ƒšBŽZ0Y 0kÁkÆZñþÓ@ßLâþ†‡`F Ä'À±è+çAÿùÞUÆÿiÐWiZPÐ7ÿ;š{ïþšm~áBÖÅ@_ÅÛ\þ·”®H(ä¤äƒuTú~U/Uv‹š 0ôUù okãÝŸˆ$û´í£5úŽ×D;Ö`0éÓÀ4è«÷|ÐWaézÍ'©(#=/ÐH‹|‰Ê ªïG:`u¸UK¤îœ!X†$Õ(=œei“Öª§ýs&”æ4±R!ÐWö>è+É mEÒŸè ’@MÿtT­–Í}år——oA[òèe1í°|’†úÊms ¯ì}pS ï‡’…vdr| e, .Ý‘/¬Mh$ñ,ÐWÀ^Uº'[™ô,_ Wè+U wD€¤_¸C¡jü?Å$èE€ÝH¥ªÓ~‹ïðIêtÚªô:•bèëB–ý ˜oÉ:¸uÿÊÿ¯‚¾ {6—Ÿ/zÏ¥6VYöqÆÈ©nàMõ7‡F‘”ŸräSEÚàY€ïÖ´‘þ‚‹ôbö Lüƒš›`H§á£ÔµgÓù©vLƒ•]>è«ä(‡³¥1HÚ>,V“…Aô Úæ>«4x>] /"Õˆ“á±Btæ…èB® ˜EF00èY„Oäž_k§òÁúúÜí9 þ÷&Ÿ%MоejžC“‘Ý=•m#¿‚ ¾ò4ŒoŸ{´„uc»­¤²„:怾ø àêÏC¼i0‘#„38¶ÑÄ?úʱ€a¤ÇöðüÆîã¾á1É×VÉùð,ØHb˜uCšdçºrÁD$A³RF¿óü»­=kñì§§áVòx¸©/‘tô ›Šôî,¶ÙÞAß”úâÓê6ç~PÚ$].}¤’BL\ÁäõdÌüZƒ„'mäv ÞÓƒ÷rÚ"I×X%ªR}ˆItôå5ÜkÓiì—¸îMxîTìØÝ„½ q=bû¶H"^1uÉ™Höf²óˆ…¨é´W’2–ºß–æÝ!kž¥û¯ŒÞ`ýJßäÄhBb¢áÑö\Ù!¶Oãg¶j1$•¸gˆ:§ÇÑs¡Câ#\I"½_Âw»Ã“ð_r8¯žœ þ<Ò×ÎH¼è;Ò¹UϼCt”" ›–µ( Œ¯xœ|¹ÈsRr0 ÑIYçQâÉ|§Œi¢‡pݘvSOšÈÇÈ@.Ê«fÌ©Kõƒljì4& ÷À6L]ÛW¿=ï=íÅä6 qfè ü_/ñþW±£“Óí©Ä•´û´uãvSº¹ca#Ê0õ’s¿1ò:&^…Ÿn´nµ§Ù¥á­Éd‹zײ£yjcW”Œ²éýf8sPý3Ý›Ø)¬P”ÖkIÛ?µ¾!çît˜^ª…ZÇÇÄëòÿO@˜„ý–úx"”•˜ËÂó8ü—‘V&¹,QZe5‡s'?øöðÖ˜ïïbÔaaR™qC¨ájêÖÎ$ðv»§rŽÕ'?õÜàckû¯©)¶ ýß ¶7Aì„6Äž>¯âAî´) ÊòXà§ò¬ì2Â^Žðyáð¿MýIì˜yÄ.`‰×ê©gÚŸ}•;«xnSÁ®7Ù˜pÒvnC9!% ÷î¿È­yv¹z:IÞ–ìA<çâ–°¨¯¿ y¸+å4 Už}×$ÿ)ƒÄ;¸!.If–¿ nWͼqÃâêQ†(› Åòíc1‰»›Iÿœ }Þ3Lú÷²_¨_ß„È÷Pg»‹>ª*¼‡2•\—óÌw…©SÑk<óÈ™¸ß—6æ’ô,yö©=CÙ­§CËŸ³WÇ¡ûu’Óx© u¨ÓnÑ+¬_›_ð_ÉÕ‘ý¡êuù¦Æ—í¬8u&3¶«ãy: "ǰÅy&=ÒŬ¼-¹†ös;ÚÏÕñOýRž¢«ÜXDP{t;ËÞϤ0ÔǯO-ð%šAÛø0ñ__:ÎöI¤Xô*wcœ§ΰßQç`ú.¾U o’6W’ÿúFµ¯/¸0œïÔ+<·ÅÝ¥˜Ã|C…¶åu…´Ñ x³/­Y’3ˆ¨9yËk˜º©oI}h]ã—3–¬æ?%äoŠüLŒÄ>—Ä—ñí:8˜áQøp{&v§ÝʸW‡O/†Xˆ·Š€¾ èvü¯Ïôôâ­z÷i£ÕNoM{œ¡HòjÃÌ«R«$vÍú3VU ôI»ºž'}Ò6ö ”×#Äs¨·%"Û±¬G> Dt‹'¾KÚ†J‘þ+ÉêŸO¡•)‹›ÈË|“¼û$Ê fâ?Q^Í ßÜŽϲÅÿ! ø­ñÔÝ-ƒÆA|÷] ä%ëøWªýž¼~—5 µè+'Që´ô•{µ ](Ë¥TV:оFޤ} þHñfA_©)1Žöy`fÀåßù˜WW[õ—Œ7$è!µ7õj¿ „мœéI{@_îûGô°åÔØïì2iL½N9üÉmMÝVØLL»r=ŸÃ4­¤ÝÚäÏÌ82íŒ~cùè•vB&3ÞÝ™7:ħ?yøÙIßßc¹lYÊínvk謀|ÐWÎNdî3{•ÎÂåT:²ÿÝ”/þ(210þ¯!US5TÿTªL'¬;“¬Bä«chÊ‚Bþÿ3“næhÞGßGã©Q[!’äÍ%ìå‘@Û 2Á“]úTM ÉðN |$Ñ•O…¤¢á}:\‘¯*!ßÞ‡†Ù®oHÏhK”mò=Àî_Lê4|iå_Vp+ˆg³ðÿ[rúq@1’^,FSéÌtJr– |4×÷ê0+MNTžÙC"¦ðæÞ²>Ö eóy¤ú~ÇÊV|”q³®[~Z?-M«SçKqþAZ?¯ÔÜV@šQ!JšS¥¤º×àò¥Ldç“!tJî¾’¤çˤqIÌxÿ–m¿ê´7ã;|’†¶ß #î×q+Éè Åá§m™Äü¤E s6f0sL·«S‡0â“M^Ê„L’¯6äë˜ý‚V°VJO«¯[墭ZS8¨¢%RÞ\L¾ù—¤˜¬ð¦¯É%é]®Jç±$Uß`uDZ"·2zŽ×ÌõÐâÛŠÈDu³.š(E¯Êñ¯Iv‡¼òôƒÓ"„› û zOŒ ¾þøêÅ¥Ö ·þaÀàýebÏòš3 üi[Ü8òïb¤¦ÕÞù¤Ã[¬~;&yßQGWF7åF4f{ûÖMî>hïYÌåL­M¨£þh¢[÷­2Ð6ëäè<§]±¿ #=Ësõɳ÷_W²³*.²kׯÍö*-?j9æsy­çq,ª8=ÜLNùwï-çtòi of€ñ'¬7-c )©HõìÌvê›#{ØÌ²§? õIŠ[¤V¬–ïSÙˆ$U @$ +tÿ ð:ÍñŠÓ¿Éöù½ã§R*!7i÷Ýëð¦Tèy¼Á6 ­cÉ’kMGªÍ:S ‡Z°Øh{0ÒÁ^C'Ü×±Sl*íQ–>æ{vK¿Ö±{a”=Kÿ:Žúø»$iêÓZ‘!èÆiq¿ýÜõ0¦U”•ôòÎs{Û»D²$îu¨àD@a> ¥¿ .é•Mã/è/uäâžj!m&ÐpSô ×"©˜B€ä=v^œØ q¹=|ƒß%~pÞ]Ò¤±qéç% ó>—°E5«S5.òžm îíH'Ü.€Xˆeñ¸¹Þ{wf*Ò¬íæå¥zp}‹[@§áùÞ1£žˆŸB«ØvÓìáƒvgOFê-ß‚Pf²ÑÖú†˜8"A<°ô"«ÇŸz¥G-N½õ•ÙüeØ%y#.ë`À Ž©#ñ$=ÝÉÕezÍòÏñU%¯$“ü¸5•2‰»ÄY rª6”Çé>D:†•?ð‡Õíá\9·”< Tw%}Ò* ‹?p«CÍJå¹R¦ÊI~Ìw…ì8 ­/j©›5}ðÓ™4Ý›É^Ú&ß\®‡±§îJÂORÔ%ý “´ ®!ý5ëààÊÕÁž6£ìmÔp4в†(›Á¸«ã­D2Ï©âðLT6Û4N¶§kÏ'œÝÒ†ìÍ@k¹tK%‡$†Ý‚@+ª¾«xØÞga©m ?Ž>ã;ûÑT\F^ïEÚ)÷äTôROgñìžGáŠ|¯Ù›û'žýGø¦èa<À xˆ ™RãN«Ò߃µ£îü§Û¢´‰»Á×^øiÃRòǧ¼‹¯Cke\K¯åòBÒ¬Íê&b¨a8-õ}Ðp…-Â×Û Ú=Œwžc<§©üX>yõJ޳e3ü#XÆb5[/õ„¹6ðçÐøYÖ6ÒÙF—ždcYhWö/ŽËÐ/Ù×N %íj‚9Üv硺ÁIV§ªíšú]PÁrq½Hn;>ÜÍneQxExn@Q‹ÑFÅ)£Ô“¸›Hl˽Špvðü%'ÚÛ€¦ZðW›V_|NŸ÷&í“h6×Í€éfß´™b*¼¶z ~6£F¸>-T‹Ãuƒq= ~Xþ©ÝÔÖ`·‰ã+þ="4ñmí.¼Ò)º?i¢¯Ð{ÉWìGdXÆ´#‡'¾Yy úpÛa6Û }`n1Tzâç/…Y9üÿam•ñ£‡,¨ˆ\ΘPB$—öH¾N¸ß¹{›ôè,ŒÂôka〩À@…!êK>jÞ7ê–¸rÒ¾²P‘¤}ÈÐÌÌû‹ðï©Ùj)2ôfi·C€>$*gÉÛl§ø 9 aÀYÓGnÁy€k7¶#Ê­(é{–,l«|SY„$¬ƒþri^îëbz“Ú ý"‡sÊ"–ÎËÆ\µeÿW¹} R]狃žÔ>¡æ¥9ºŸ6é¤;ÛšóóWíÚÀ@×+$Úß’ç¨Oý I›Ê­ZI:6w +k8<´ß}Ï?r17u;›ôî‘j¡ÔžLN½ÆSmÚT·©gª»{KYmJjÅji—ÿÏѯèpêâÕ,ÔïË"¬Ò$í`{ ûßt£]Û‡YĤmX‚6C‹1ù4zzPz~šÏñùnÿ÷þÎ ;ý‡“òŠ^ýlü•¢ÜÄx•]C( ôš’/…œÑ1ÙÔÉÿL9 ©`ur B#³'Íg}ê ©m$å’¦‹ €Ãs°‘7˜‘s­šŸÀð–ÌD$H[fš¬çÓõ ¨7gÒ?öa’êÌ—ªÒB”/,ÉÏ.pî Ù3s¶´õtæ謚v#Yÿ ¶–ô½þ QåùI:Ýz(_òÉ×%–o®÷ÕCš¤'¦”Žf»UÛ¦îèÊÑQ7ÍY\„NÓÎ?ë§y¬´êðBôe)}Ñ"©*ð¬“äowÊv  ®Wõ>‰Ÿ|T[3 J2‡p¤C2¤ËiqTI¸sÒoL)¯Ë¥ïЊ~1j:PþÊ9=~uJÞÎÆ¿/)œŽó‡Ö’¶½ ÄÏ;ßmwV­5(.T|7 Ö…6à0«‘1” ­Áxí`ߣ<¯i¾NmmO\ÕéI³$¾gàB“FÑíLP÷nFŸœç*÷ÿ†pw$ŸV°aÈ›ž‰ž˜q M´”|¾mÑ_|wœ|é&W õ·y€¯äWáLw“Z½AsÒ—è¨ì n¥ JృÓ픦 ’èE»¸fx{«ŒiOw“ggãýJK[ŒÜãT€@I:r Rh=òF“Éžžy¡ÿÐæ¸¹&k#I´ÈùÙw7ÈO·ÉÑ€ãvÇð†vÙüƒÙ©AÝ–ŽÒ† =K*ÌÌ8ÜxíòµLz.E¤U§ÕºÝDÂꯇm=Û[prjz6‰ç(fØqIÂ(=3¦ û,¬ÃhßK§Td8 m ?&ØgHÓë\O1WX³XËMîdÁùäV]› @{lé3lݧìB½léGaöX ]P_òxmÁÜ?4Õ>Œ '§à޶§§ßxÒvº}ˆ»\b€jFš,ÎiTÅ¡+ä³ÚCIKEË#´Øß9ðÓ›^ߊp ËL$2©91x>ÔÃ>@Rn©hK”Œ°%ÃÛE,&H%Ã)´~{¯°wƒçÔÖ‹ ¶¤‘Ýàž®¶"ÒŠw&r  ‰Kýå÷ØôC7©ô N‘ä°£ð&ä«êm€»ø<¼ªg' œð~”ñ(ÞÑ'/1~ÏéÑcÙcϧӻ jP¶u²™}XÀ;"õßy7vy}¥P)áHcáÈ®®í@¿´}ÀŇ‚­‹ÔlÀÇkN´S8ðïdÆw0¾èˆ[w0¦Ò‡ÿRÏ‘–‡m$¹û(>¯œ(¾#̓Hó–ÄC]N¼Í{.ê_$QëÔ³ÐÖˆœtô}¤¾*£ï pÆ3CKØÝ5gÛÏ©71Û@g ÝÉxÀ£:oêeüjââû"ª‹=œÕ騵Ø-ñ½'RĘœ@8§F²}*g¡Â+[©ÏÒ®Ž¿JRpã!íŒÔ½OÒ¡ŸÑ‰êt–º…F…QˆÔG4ÙÝRÈᚥÞÀçÌf|÷¢þ¿‹}–÷3ŽÝ®ŠI™×ü‡ðÒà"ã˜|·­{§g¢ßn µ!ÿۑ׋‹Ê ¿˜vþœ8#GRφæå¾4ð:‡Kù«º’G¡þØQÁÚþ¦ÞýZ®ˆß5È»3 ’àÌ1|«5ÜHÝü´‰k¥úuŽ·Ã>/¯¼}õù»E6AŸùNÆÍ ¨ÑbŽWš£:,¥îhÆÏS³/EÓîy€üJôŸ±à2†qŽZ툺Ÿ´êÁXÜ¢1$ašZïòYœp/$Ü•”Ò„SÈ—æ8+Ýz¹0ÿ÷÷ÿA“@Íÿtºï¢Ô¦ µýŸÅ‚ä€7Õjªf«rëÂXPW¾dò‚ú[÷nÒ]Äã‚äS‘ œ±¶öoÄ¥Ãü†±ØfyX–ëæ YÉû›t"­!†vMÔDÈ_!}L:IÛþ}ÒJ¿w˜oRüÞÞuÅ훳H9"¯òÝ«“Iµ@5ƒ» -è°S:“ ‘T@ˆ4¬Æ´µ4©ÑRÒ·S™|ºŽXýXtŠ–.°Ýýsü‹~vÿfû¤;Öôk“›<_·ä·¤Q>$õ<°Nåº2@¦×+HÝ"ñ‹Oš´HíÁâ¤ûü‚Rî2‰Ê_@ÐCiñ©G2ñ(D—S§”O’ŠÚÌ`(“WlÃëO™{Cš¦!J‚>‘£‰«Ôv@⌙4",pR’þŽ’×pû I &$ßK’R Ñ`UÎÜÿk‹]/'¡ƒ‰t\º©—o›{W; £Dé üq¾Ï»-è†ÓA'Mw!À­á,`,ÈiAé|&poÚùW8A½ÓØ U×€/cËvLráv€—%- Xðë‚Ô |‚é²7[=øl”7Ùü"íÇyŒÞÂmÔ{5Ë™2¹Ýƒ‡é È?brÌÀ\¯Vµi’,@Ì'•_zû¼gÄäÜ?,ÊwSè®mÑ:ÀJ°Ê©Œº×v®ÄÒ ìÓN Ä'‰ûÜbA7Ç ÃÛïä„Ü›ZÑ·dÕÆÜ5*ì‹lOØäy’€g¦…ªHç—ÄÓŽ-ÞåNÿ¤ò¶-®fÁÇQÅ&WÛr ì Ph%^äÒ§À4‡Ã‘æÁËý »)~©í™˜È6û9Ìç¶Ó7ÒnýÑW…ÙyÖÇí4Yàè8ÊëK¸`Bò‹t࿲XV‚¾ØeíKÎð5õcÖ]êj>…'ÖEZÒÂǤ­Ö²$‘ýɵ1€Ø<¢†”ÉMv"iûCÀa|û“ÃɤÆdYxâ—pWONT<ˆ°iZ*2œ<æ¼ÿ¦Ò®vжX@ðûê÷!íóí:ôQú;,¤ÂÃׯ1¨¾ÅÛNMÉj[x†~‚Nͼ™Jc£¯õa"ç $ù*ª[—?•ë긹S&!uG"q Ú(‘–CÇL¨Õí_½M܇Qîò~>÷Kqÿf=XZÁ´³$†›¸åöÏ^ „÷ØàÙþ¼qþá­ú͹”þj’p,wx³M­}Þ7ís̼6ŠGñ¹€kôT{TçÝÐ l5¤[uϵ²Là%夢pŸ¡¤÷TÁ7éð3ôðêzlI´6<†ý\mHÏænçŠvݸú{Íéð[’‹~\qâÜœFq'ŸÊ‹Âð~ÃÃØó.Ú+wø•Sñá9q’½’4”´ý¾€ +Ù Ô’iK¾7´%×f„µ^ÚÌ¿QÇ^-v¢Ò»pןš=J„+³ä€íçàÓÓf¶>ÑÎõFÒüDôñ{4“o8Ke ðŠÔ{õô5kb1Ã-DXâYâ]1í¿ª#uës‡E ‡óm·ò€¿ø >É!µ)’^`&ÜÈA¸ƒÊ/ñœ©-‹ ÷žSÕ„5É©b0 <ê‰Ûwy$|ÊX ¯ýXÜÙ”zÒ1z4 ·Y¢ìT{Õ'–iÏ–^ek²¨"µCÇÑîÁíù\ù%÷\Õ\-Çûʲƒ^biB ÃÒ5­¬ökn«ânRN}ˆ ±­SÓmrèKÛ³üqv,…©ŸÐ6œá¦öÔµ¹ëØ,hâ»ÐE/Ç=b7Àqɬ[ä¾õ ëœ| Àx9¶Tÿa:ŠjªÊn¦¾µ¢NEBVES ˜úhë”üÞªÃÕéP’^P¥¤¯ô&øó6®;‰s2éšR8šèõÖ3röÚªÒªEgÆs zòAx[b¤·„P‚Þó²‹hU=’4žÔµUrx“Ó ¡n„ªÜ®§A,™Î¼*InbŸŸ_ŸxÑÚ$¦ÐvFmy‚x›vP;) ½¯SeÓ½°uÀT@´$óéøf¦¾…þÜJ‡s_…ö>E 8UJüz0þ<¯^Ù<8\8aç³0)€…&ñ¬62Ô>Þ‡·v“b:ØYsOê{f¹¾˜ó‚æÄ x;@c _ê Ój§ÇR5åvvé›î…Îðcý×ô>(9“g¿Ý˳N¿v(lœ5 I¾Q÷òHýÌ+Ô•òGzÙ7@ˆD Ðm»´Æ|AiñtX3¹…Ï¿…¯¯#?F«¯h…É“¹ìöPM(FuÔ‹Í=€$Œvx`¾©öî;êóäQ}ÏYÓã7N¤ü$8¨ôûc þ,€ÅøÞWiòIªûî" 'íÉøËO·×®ÓU<¨o¤ÇnJÁC’òmu¸›H‡‹ý_ôíÛ7'pm%êHýoÈR‹CªÕ7J©Ú«^9ïM«ENV¶ê%?ÿó=-Hþ÷¤³Ÿ”f¨"ú•2?ÌÅõ®‰R‹ÛÃQä ;½Ï†×ƒ<ú60 ËÏÖüýé¦ ùž{îq÷þ4…¾þØæÚ­üähRp °ºÆ:.é»Ôªœh8€—E*+©†tÁøß÷ê.çÈÑb$©ohNµXÔ’0“Ôh3Ãâb^sÌ¥÷èÂ êœ :ÔÁ9C­åcÙù[þƒá-̳NÞ?ݪ®aIYþ¤-×}šÓEë§Gê8òUÙC—ü ¸|¦‘WäpEþ~A de¶c.n~,ý_4îÁJüï¶%P­“–kVªÃ‹ª%>’Ô«¤X%Õ¯€sSðøm“tvBÚsFìZ& GìzÚ±ÃìÞ†³m‚}›ÏU©§¤²êœtà6G:@­o¨ÔÙmp˜3ô·äOt Û ³“T¬ÒV¾’—°™Kzïjâ"hP¼OÕuºsª€ƒ'†¤ºòÎd1‡²½–¼>œAœð¶š:Z™‘ó}t1Ñ#íÔes’H²¬Â¬ï¯;•;?Ñ*úqÈÖ‘¤­Ko'Ìi ;û¦9÷mpB¥ÿ²—êû`®é€8…RIJðêv ÷W”™t‚K¤“ë!€·É_jv Àð{¸ÍJšÉ¨3ámIZÆz¯JÙ‡ÉT# Oÿp?‹¥îãpÆ:û‰vfÚ™ÞÑ‹íÇÄ«öcZ?mÀ+=íšÐt;)¼„™ìbWT¼f«Ì/wõUhäüX [ìÊõ@:r.¤r—JÜD?ãM¼«Huµ«GUäõcÖÐl=¤d¿ "ÒÖÆ&k‰þ°µ)ŸŽ¡-Ñ¥ÿ9Ð`@d;»/¶ƒu¨JÍ@Êáòl_î“IÓ§\i ŸÄ ”ô†fˆéPþêv¤²&¦z!ÉîrÊ»þRîÛϯ ) j—ItS˜Š{À¹ùöà7‚P ]¸4¢Ðµ²mD®™úÖp¾‰¡W1í„"@•R;„U§£ÄÍâˆä~µaóc¶nö &Mç’l9RîJ£@Ý÷†£Iç$¾[æ\¤ .tRþ±óà¹]y‡$ Üøßµ÷NJ°äâ»*UÔ¤øxâäBe‡î(KQnLÌFX½\x|Ú…`Ã/i¤s‡ÅvcQìGx*šû±–hˆ¿a ßE™” !ÎÇI¼¡t‡Vâb´^‹0~Ãýs¸y÷𫤵$q];;âÈ<íÊ@õ}×lk‹þè‘-í_HþIõ»õWÀ‹Ók=-wÐÏÒÅ-½ŽRï I_¤úƒÆQX˜•“î¤éFòòDë‚ð3(óÁÒo,xªô$ŸÀÍ5¤²š¯vpù´‘­îk¯iñBªq*p“³‚¢Âí†ÛP|år¬W¾~ Oµ Á¾uwZ]bŒÕ•Œ´€]ŸÞqÃDnüÁfJ§yê]â›Ê$•í³@ïòÛlÍú»l›Ä©¨’IÚ‹”ÏámÐY`aògÛ­l´µ­ÛÎî¥?h‹/©QúÆËñ6)¨Æßq`^Ucý¨úZ·vuø¨“+åEòYëÌnj»”bv·x!Ô‡«vðy1ŠRžÉl ÀÙ÷à ¶JâßìÔ‹¡?;Îè¥#¾fðY†Y{®ì·K¥—/ôà{”ínð}@“*š Ê\‡F&¿„ÿÏ·•ê†Ûw~=ò=º‡Áõ%Ý®râ›i—ÖŸäuþN¶Î¤/F{^M*]{è{Hß7ᜎloêB}žÝßþ*Þ‰îßïâÆbú幸Úœ2z‰öœ&¨—˜eÂ`Y·” íj{77ß± ¤yésô×kÑ/;®+Q Ïh×KKTú"m׸ëæM]YThng¡8øíðJ6õMOeG’éX¨c™e¦k§Ý‡¨kêÌ"Ôœ¦Öi“uàù.ä#ílIø›„ï ÞäY»×ÿ‰BÿfšC> л'‹ÅÕŒjIsf…æËúNIäë\Ž|õŠj˜ÈŽÕÉgíöIUÒƒªŸüÃ.aÖ)”ßrŠæºðŒ¥9Å~]ÈÿûËÉrÞTp‹ƒèæ ¸GdÿGÃŒò]Ò­ªÓQÿ. ‚¾Š³”ë?ÞY.‚tˆ×Õ€¤‡º<-^êL¹M ”›ü®D*üN¡X»u[“Ê68š×‡£›åˆëçt|z^üx%²R'+à·%î–ÔÁXNø­ “(D‹šGÏb€ù«”AU…â]³|Pjfk}0NMÃ4.œ+¾F¾zŽ ¿bÏ:LnK:tÕI{ÞQ æûÀ¡tÎJú@[¬áY[„[" ÈÜd¦%‡ÿ@{ `6 ½<¥IÑESÈTßÁ(rp5ÿkYpÐ)HšúƒJI <Î`çôäï¶_É;6HÐ ëU®³µxýwÆh+$Ù^s` cH¼2Û=µü§ú¤t}'ðé¿\êÁë·¹FÅÞ´µ¼áˆb¶ió5™H÷å »[ีE8¹~¾;oW¶øÿ9…4œí¤áî"/·¦n• ‰^Ædâ¬@|ÅG8 Wk(™f)Æb]ÉT²{é<w¤ËQ@jä`â9.Ïk;À€wìÆº­IÿŒ‚a߃vô§ž'Óø¶³½‡ð©ÄˆSV|·´™nÔøòÉ ÜªHÇL†˜Ì J"]û¤8Zá´È.6HzèH’4=ÑW?¨Þ‚‡px¾ÿ`d‚Z(zöŒ ³òø~NÐå‘A ì»r ä…ÖÍŸ”é ´Ø1¤q%ÒÊåSäLÒ<‰ôx|éwâaWK}ïþµØp;5ñ£}”íŒÔž†yÒ‚ñר–Xµ8g…÷DÁglÿÖIâ\™ü3 N¶˜ø½ôi«¨ß^HšÈpª\"#H# F¸'é¥^•_kw¡vá³Æ3ÒV.g+„÷µ$õ ­WúšÝ ¨;­~”m ÔŽøÿÀ\ÛœW‹Ý€$ Ò—«°x“D7ð·‘á!xKäòäâ{Á{/ò¯ö¼7‘:HÓ$½Õx .çx©ï'Y ¿<@š?'¼áW@9úk›°­ÚTÛ€¥ũە€­¢z¸È¦Ö)ÖÛz× à€¬.¶aj‚ýÂb¿ÔH„cU°µGq7wxu¬²Æ:×ðᯭ¼ämʸUsi ^Òü `çT9¨w‰]H½‚¸´:÷/‚ž²Ïæ–ªæ[‡ò=¹oÇ·íMºkqÃ5_me”¦÷1ôEƒ8þiNÅý89 |[•ñþã\í©¤´ód]™SŸxNòQt —XIøû7Ûß ¶–¡öäyåzü–tá耹 V÷UÞ ,OÞCþÏgôƘ> åKÚÞæóèÒé ¨eöQãÛö}mÛ'1ÚMäÿˆœE˜Ë8‰çâGÞM\P.µö-qg@_ÅœˆèK:Mm•<‡!åè¨+ÿºª¨i! 8¿]Ðx` .ÀIÅŠj‡ò=·qkk¶À¤Ò/:r,×A–ªc5%”‹/ÑW;ˆBèLY]ʶí”=ÃÁ€RQò~h;«¦^Õ !éöI5ƒô…|›tj²i´/«&˜^×fyE»œ´’ÇèáF®æ€M 1K’'I⌓VÕ­øÃ æöæÙ‡|? Þà›çè¾ù݈ÿBLª|ëÌ½Ž§ÛPË1°jW¤Ö—‡gøû©ìwÙ.¿“@9åyÅ—´–òšt”žë…½€ç7ùŽ;±Û€ôÜLÞ^êÙ¹ÿ_¼g©!àнßY<{Ÿº­êè(¼!~ßÃ/eÜà…y*q_h66eh*f¿†ªpÊA9À§°(()7×Õ_‹_âS;Z²8sŸjýîð°Z•ùݹT– ØeµõxxãôØív)¼³fòЇLÏ”’·e*µù”¢<3 ¯Ò)"ÿbGq×hÔ£ûÃÛ)mfÙ{o±”0ËFl;ßf„wq;†žäœÆbƒèX€Å›ÒFá[ÇçΘCÚÞ³£‘¦ÿ,²Ÿ=_³-üNº×­l4Ê£‡¨ýÿ .¡_þµâg›=²|Šôì‡û>é¼#_ØMð#Ö+%dwêR¶×ã‘ö»Dª 4‘K¼¾]‹ml:}싔뇴+¹;E;Ùi´p·¥G¦GÚ?ÏašK¥°öñ}ìÇ’½­]åxæt—Þ“C¿²û`>Ï_¸wý Ÿn§–îZø£q8‡ÿm ÏÖØÌ šR)ßvü°V¹Tö¾!5‡vé±ímP!ÐׯµòfÑ}lùк¦º¬špˆÂ ä¬ô׉Kªl ©[ׯ'Rü"õ/­}À_½Ô^ô²(r*‹Óø–Ý듌w´;N¤³JèË”¡C»D#Xh=µf+ž>uï>”`ïñžùŸ\¶ð ¯òsAÈKkqêé¿rÖŸÀ—±Ù”0KLÀ¸PYç¹N¼F^žgX蕼‘šHœöu|¶D¤ß{qƒ¾á)×s:—ý¹±)åÜ*ú´¦Hs½Ó“ÝþÎ á·`…ìZ2[¤ ¯‹L©I´mQ{ŽeìRÇ¢‡hù¢îÊ"|2õýú¼®c/þÑaþ‘$·¨=×ÁÔåe¹oËð#v]UÑ~èpÇû³ß~‘?«ÕÍ›áçÈpæ4Q—C:'fahÑnw‘Ú¦”{Êû+&9'gÿ“øÍˬ¿éUìà §O„^4.DК~Ä ø+f^ÀiA£¿ê¿` ÿÃÅ]nÿOjU”‹û»}‰ß`bšk·¤ñYA)sßžFQŠÖ‹Ñu¬˜ dZœ´ ë@¢†ß'4;•š®áÌáv «v·ÓØ«ñn‰Ô1T§›tIú@¢üé›thWK¸$^að­íò L±Û˜x•ã­’·>äwðи-¼ a…ó-Ê-_ÒV’Þí¶\X¼%ÕBó¬'Sf« Ó7‡JÆÓHˆ‘7ÔȱûG¾,Iª²¹*IŠALJ€ì LæÀG)ÀÅB-¿sÔª¿v¸ŸçPŒ‰Nâ3:»Qic­Î‹w礧}9®‹¾ëÂ@C’ÖìÉ-TÉc”Ëî9ahö3@q†õ@Üka^陜yÛvy—¾ÒÚý°#nq6½ô~øPŠøBe§1DÞáá:Êœ-ÙÆÁW–¢Ž—\O˜evº-MݺÞåêÇú¤ñÞpë6ÉýJ޵ý¥³Þ© x÷A‚O¥ß3~8†³ˆ{ÞÏÆíÊ\€m-—¦“³Ü‚EP1†M ÿé¯þ¿’úîQgnEÀ…PoÔ'|oû3éìOûåô°†z„žïµt«’.ê|ì8Ò¹+u…e0rr¬‹ hó j|Á¶ÓÆÕ]‰êŽêÀa-tH_s”º4uÌ~âòHçHHæLß L~%äøœ$,6¥„¥\ç·±Uࣚð¶öSâßîú‘'§ó!î¶;¿ÎÂî ´_7rlSOvBLjAúgãç;·¥”½¯Ö€lÉ仨¾Ñ¡³³­:õ õõlË­àý­p­]ÀVè&zƒÃð¨mJFQÆ/ñ|3~ZGçÒG®Bùìú3sK½@z/N¶±{‘Ê| rL¯Ï„§`Ò´øöõ³¹ÃŒ›Ä,u©'›Ë`™ÈYö[âaž¾Õype®òúÉ ‹L&NåT.-É럹F™7õUâéI“…~(}™zCž¤-m Åp—xcòß vÚö-ðw_kbiâF@ܧôwûІ¯Pþ1\ܳsóÆlM<þRÉ¢:WàÀ±`ôþn Yþs%×ÑW_B_Ý!ª|ÿzÏŸkr³hÍhs\¶p¡jŽ»<õó´ž 6" õ¿¹$Ð\|WÜkêÄít¡—ÜB{tòûCÚ\æ –šXæúX†]ÝA˜Lt¦÷ÿjõ÷—Ùl¤ÆWbþ¨ÃÞoc~»í§ÆQÓÙáŸÉ"»íISþáõš ÐÖ|röO*EÆ3ÑÅÕ„–nbò?ƒær@ú6þÒzqRãB.&+DÅÌ ¹-dVÈþ G!ÿ4³¥òøcùÂÕ®h²Å]ݨèÿmİ÷ET}ΖUÑd·æÔv b>³A_ ŸÚpýÆu ÃS$I€Êïiƒè.m%è+/Õš8¦é¸¼pd7¤í¶`Ð˜Š¬â;õîn0±!Û•j›}%íè“rn œO=N÷3wu@ ú*Ò©¡gê¿2­AÊq#Ûk‚ «"Õ ® JÚö»3ýÂ|IúZ´Z>è+ÃønüÍÕS‹¤-Ö>GJ=Ä€x¢>’YL4pýHâjKøÏ ”Ë› È€¾Ì¿"ƒÑºBô½ž•ç…3ø|žóY g1 ½.`”bác®a—ÂT #¢,ò>•?/Ÿd'R]ÐAs*—Ñ …þôŒÿô¨q&wy4ƒwm¬O›Ï`k­$´k@ ¯¤‘5¡+úÊ›úê¹Cr& ïøëåMDd=ð–C€$SƽZ úF¯"Ì=s‚ð_4 Ì}%Ñ£ ×£nܬ©ˆÉ§´Kߥ?éœñ €p ßäó¡Ó [ñ9Ó¾RÀ¾yî 矿÷Qgr‡ý¬9 Ð÷$g¬E¶ü“‡ÛSb’$6M¼“H”ui=IULàžm¿xIÓ€m{¸çe)ËüþÔwåî‘]o§£Š†ýЭMœ…( ˜ ŠÁärŒmNº—µÕ”o€¡?‰í}Âd•zô•7ñ¯Ûj> Ò8šphÿRÚVÍ÷DGÉ 4•‰Å:ÞcúŸ¡?uF¾óÓEþ ûUÓ¶}Eõ4õ±sìYÛ¦@0-‘¥`: Ew{è}wîÃTòmG@ó9ÍÙ?hð–ÆÑH`®h—úe-OÒ¥ŠnÚîɶ“‘ÛØéøéÆÔÖ“Š•£BÔÓÚ”½„@Ýz01Ú O^÷ñKi,ý,“è6!q’ÚÒU7ÃŽX§%¼Ôñü#ù¹§WϳTùÖ¤] P³gãëvTM…”¦/(“‹ˆo ×B?Ï•Å.Àl$3ò6ô¥,ü>A‡Å5PÆåOcFßÔ­[\@¤§k9èt"fÓ(ÿ÷±§OTBw꫉wlï’syß¿ŸqPÅÏäîóů<ÿAT—s‡úº:Mo8îô9׆ÔvÜ¥Ou¶‘-8«Eµ,H3í\ÚàãB=È»MIïm¤ë$»Ûj¨!ÑTm %}htk¾çây+è9ý·ã““íT&‘ޤ/±þpU§tµç»ªm)ÊfÈðÁ%ÎYæ/|òÛÆ3 ¯fmHÏênáÃ3º.äéWÕÛËLJÕWyÔ4?‰æ:ÚΟ² ZhM¤OuH¡O_“”7ÈBøE” Ï$ý­Å´ç@_YPß$eÌÂ…+LF§¾´½çRsè+7”sdm›N™“zÌŽœ»“MFÂÚ©C`+ºêØm¨S¸Ð6wã)‘uQi0 {¾] ="¸ ÉOG‘á¼ àèY<3‘/Hól>RØÒ“©AohńڶGKÑ6·Ï8©¡½¸ˆôz´q”±s’z‹ïkËTÓ–L‹jÙ9ãf=«²#ÛeÞ‹>°b '°­|, 5BÒVù—9ÞŽü;šzžCžb×Nï¶âNM/Rn.±Ÿëv·ÓëµÄ¥¼ëm•Ž·ccMÀ7Óù¡þvs& Õj‘ÖìB/¶:R,RXâS›†ÃgÊ,/~TºUþˆ “z XÊ-~¨ï¬À}’=_ׇzž´î±VÞ×Îä®g y?¾õ^XcXÖ_ò-»#r˜ý¡o²uÁwn…×Þ€ÿG“G£pKY‰»Xûð-½ÄùÕ$û™äóÖpzòh»†8s÷—ü‚²ÛÀ®*û·Ý`}¬Nº®i‹Êàªì‚¼[9‹k.y° ×^x]°Š48©^å9­µÂ•ô³¤MÑÁëôì2.MúÚ9µ›Ú½ïØ;RáÞØEïøÆÕ#ïUÿÒ]xvãùð.ü¿°–±Æu^²äÇðäP ¥v”e"j¼ÉF$ž¶×)³q<¯~ª| KÛ ï lü”ŠÕ4.ê¥S]!>€8k·Oªšôêó“SíÚp7Uo=Z‘v팆8ZŽ:;›<냟§Þá]8 Ò¢ð\ê'+Ï ä—pÔBÑ;8xô æØšµ½ìëä%@ׯY³mGŸ&0®Ð.ˆ,ýÈã\â¡\8¸®QsØ ÑYíÛUè·Ër?ƒë~ú«9„'xÐ-®tâ›—âê„.êþö»%—ÇÞœ¸êÓö´W…q—¡*êèc”Õ¾˜TgLýíòÛ*ñ‹KƒoæßÕ^íÅ¡‘ã³5¡’'àÉ ðÁYM¬‚_¡âÇS¡4-ò§_h˜gù-ïu3òÚµ ða!r‹z*ƒ"êA™« ”í“SÝßsø(‡ÈwnFI#©Ú‡ºó˜×ÚeqйoùìÖ9cô’O:°î€À¼+߾лø`e„>W=YŒôeh 7dYañÆ“ý„I´Ô{W«²¦Ù'Ú“Rú6-Ä¡‡iÓh%pC›éÅCºÈs¿?s¡›©+|[@±jKí:TomŠÙMÊ%À‡yáe_Õ×dI»ovg<§1Ÿ×ÛROEri'‚W8ÃôŸxtxf\ëA[vG^ÝV­:”4¾ x|9¼=‹Z_íï;iýD‘­nh? 1¨_ÚIß%UàK“j:’Ñ]š¸"Ïeú5¡I懅ídš|®¸]+lö­ÍÀ|ÏŒKoÁÁ¤Q‹“$‰-©çþÔ›®<ûê%´í]ÒDÓ¸Æ×j”K!@±àHF©\>T|PVIØù}‰€Í癈åPâÜœW·uWRQMh%&ŠLÐòHjnH˜O8È!ížoÛô½FnüÖ±eÄdR}ðÛ+Ã3=¿@›Ø¤ý=â§®i=³£ÀôN#¬mlxh.åýÇv'ß9M¤úWZi)„ºá¤UÇ?lŽ[úfmeŽn/a¹jõòìø#£Lײ*¾±;’¤“¼`§<—+PÅœ÷h·ðç‡2Vþn½(÷›Q¥ãµc¿ØÆ/mrm‘O!¯®'ŒÎØ?OÝÛˆ6ñb‚ {ñ"½Þ3ù:‰S¶œt>SÖõQòUÒô5ðµöWêÆ–uC¤j$õ­•ª Sv #ù ß~‘}X·#€ðTÌ$£Iÿˆ8)þ€m’„ßÕöÆiót¸à·U¾Ë3uU¥Ú°/÷‰\óqs9izý̵ö¡@aÚè•l.51ò8|4î´¤óíIµ­ÛØ6 WÖI˜v‘USЍkËÈn¶VT|½;i>ï·àßPE5‡þ­ó¶„s+éÿÓJÂíH]Ø.½@ÝÑ>+}iº¶Àün»“±¥ÒãÑH¥û ¿?“•x%>POäÁí^üÞÏ}lVälÀÖ7gñóï«¢ö£M#õEå[r5Y½á>IyGuÈ1ôÃÛ*ME"‘@÷»©ûýó’¾,%=T¶ßæÿ ‹Mû3¶ùsŸ¼L‡NLÌ.|rùØ0–|›–ï-ç]ÂãZ;æKòñrü7}¡ rÀ-÷…!HߟÊ3Q{Â;ˆ{'÷Öô Œ|ç=ØMOÛ¾½A{LÏ ʪo\ýµGÚåòçbÙœ`FÃýýÅú›æ_ÔÏç“ÇUù¦á•1_5žôX¯Òµ]”hO´˜•Q•ëPêÔ<ª·¥jzXÕ|úàÌ8:×-­ =rŸŒÚª!¯nÉÅÕÌ/ÏtE»´o°\ÂÇymCn0­z»*½ËøUÚâ¬J‰æ½J§o„IÌð¬ñê+ÌOާï:ÝöÛ±ÛÁÏ<§…_§aý»)÷³þîØÿYñIrå?E3`°ú¿1ò9€mQ¤+Ègªm |+CV†‡^³Ù•¬ÞEj6Re¨WÆ&cô·?0]T¤ï,ÔY¨Q\XRx~þµ6Œ)Gþ%u{(PÍÙn¢V<I§‰ážà>>0ˆtüíMã½ 9vl -Ôdȧ[ó@OÓ‡Ni@æOÞbÐ$IÕÝ™TøÛ«%mYˆtÄð°|c@ºv&#þ=A¿ï¶ßVkk´ˆ¡0Rh•¶'ßY8&o’ ÕE}›& ~ @ÈwYWÀ´B$€U[ Ñ”S>mT´–ïCÒ’E¸Jt ù,ýU"é¿ ’ôëJþêH i tÚ ìøÑr‘åkWk pÖä©*¸-¸÷yV#$U”ž\åY-äkî ¶¹@öFÈŸa€áŸÁ›ßä t"xV~—í¾ðªÅ˜8jÒ—G¿¸ UÐbägp<“iÓj,|²à·g=HL+l@×µíµ`þ|×dÒ£…õmGÚ`ØîYR…á­›75hpº=?›â‡I=¤ü8O€‰€•IBºS¸»?¦Þ£tØuO×írÀÆîÔ¡\bBÂÖ¹™”Åfe_:+m=J{äO·rÜ#íf ‡¤èMË¿ÊZ‡6§CéÍûæj§Ì×§S / PwIˆÕóMh ¾»ƒí3IßÛ’í³WÕh°.± ’À© p“fGÓê 'Ÿ~åáÒ2&ªÑ™ð½é­²Õ€4k§ÄKmœTøëò)2”ø‰7‡hik»cRÇUël†Æn°wÎa¶+”-ŸÇÓwck~*Rbk3Á¸:}@ͯÓlåè}v:mèI¤¶¤ñbtöÞI{žîs¤#3zm6ÖÈ©äÇÁäë¤g'Ê eþ ðç8Ê.i%f!¹®s/>9˜ò,ÍúnñéâèIH«’¯äÓ¥#øsô"·%OWCZ­RãÈ)¤éRšɕî/\©ö‰~‰:m¥Ç`PjU OÙ¤è Ò¾mÚÁTêÎ^|ÇêþŸîtjg‘¸Ç*ÃkX*¶‘ËÃ¥““lrâGút¿®#ÕY?=Ío“†þ\”¥TøïqLJò.rà3obØ0>½›xû’Gÿvq)>G’¬»ŠÇIÞ»t2£›v0ýöfé~U*`Ô×dGŒðSâ ¾m¼gV·?ñ´±ŽÉ»íØÅ¶1ùV…Ö|wÈÔ¤“ï¬=·â(õé¸w¥¶¾@½ÔËž¹ûÿ–ôoÍn•âV@ÉÜWá6 \:òÌÈJ€·Úmñwl ÷*.¸Šìa;–<Äe=÷«ñ ¯—=N8Oó¬¾t2ä€`êh!J=g¿ qx„€äÚ“-ªÅ”Ä]v|:0Ô‡ø×ÄWú›œÿêt{@k!€1õ ³ƒ“@IDAT¦Œº)'ì`éÓðÌ›5UØ ç"ÿ7ùVÞ…ž{ÂÛ°õ¹þÈßĿṃlxñÁt}^†…¥ ò„¯‡’6©þ‹WÜG^Ð/DFP&V^·³=®FY~]ñ*Ò¿½qùX¹áX[ ¿$Ipë–ñ“í`¬¹µ¨áúcÄRv¶ ¥xFõàûËÈ®¶eògáQ=€ˆ€=̨z™˜ž‰¿÷Èû6ä9e/ M %’Ù“à‡U벟9¬Ì‹e– d»…0ŽçùE¬ªìTï IU`?†8f`–âú‚KýÕZ„¯ö²+q\›»ð»•EËﱉK¼o¤‡WÀHýõäá布Â'ÀÄ¥‹ôAbÄ.ƒ·vÖ%ß@?íS¸Œ›[ÝÁC_¥PƒC»{3 $#ß <êQHõ ¾9ipñ”„Ïãf^¨ŽàU ©ú†qé¸_'møi|˜ü¾ÂÞ‹l˜YhÖÁGßÒÛð |û:A„=ÕæÄÄOËsu¦|ù¶LON;YÓt,I}¡^ºÅ¼™”Á5ät5ïsHÓÇÜáã4=I6Û=ž¤pCû‘¾ÃÉòчÝ`ßù^¸Gh¢î ËÞÍÌlFÓˆôÇ=u#.°ìw.ÂAw¹êö¤ÔL€¦”õ“ê ­<1ByC|E†´côíÔ\Ái W–W0¦xÅ-eœ6y¨ÁÄ? ¯‰¥xZúZj-EÎÃ¥êz3†_BðEQšN^ìŠíE]xc9øµU4‹ð¶Ç%í%µÛ´0$•ArßlŸ‚–Þ³Úøq¡ìa[=€+}J{º1üºb)õÂõáÞÖ=Keˆ„n–ôlþùÿ!Ú¿í¶ÀüED{æÓLJR¨J·UÅžaG‡¢~Í͸€cp™$ô2¦<°pg·MZý¬9÷`ú0éµáÒ9 ˺·…ÿ{¼ãF}UJ©þQÒÃá-a„ÿ$-è)éÁ´6«ë-èðÿÈsã?ð;T~ =ÝÏ¢Id—¿™/ƒ]@=ÛMçø åÿŸTÑ× üò¨å]ªO_fl|“ÿ¾;ñ é;½¯Îýå…ççßÂ&ÿÚ’®“*Oò'j-´;¼z! iw—†–°U™ô¢‹q£nRR%¯2P+&…Ú7Òc)µ>è¬Ãêšèm\u+ýi¸#º?uÝ`9¨‡I'òf¤A‹ä`ÇðU é@·2Ö·ÇÄV´:éëH£†"ŶóìÆDõYrV9¡ƒ*‚tåÔz޼nŸ4KÊA$uj_òéòyfš+¯á@#O/m®+/ç+ä’ô“õçÚ.Ú7 ûf3)HÒ_,‰Õ…§z;™iÊúÄSœ:1ðþì TU×÷×Ä-@Q B”RA l±±°»ìP l±ìÀzTl1;°Q®R*(·'¾ßŸsfÎ̹èãû¾ß‚¹çœÝ±öÚk¯½öÚ,~þFÐå …¡Ìó¹…½ ¸~•˜. !ËL-Ü¢ƒÜ·Æe°±"‡ž!\µå^.±Ôbƒ ›;Žì¢û üh……AØ{“ê©ëÂÎî}Mòž·¹Ó(P§A«¤IÒ$HhÔÈ~Û,^>m턌¶uMχoFò6%øtÏ­`*¿J½Éu«w}ŒdS¬²I<$-4354T³HÍi0·%ÇlZÓ3#u=ɰÈσM¥YWv®y¸Yß¾‹ï‚-ôÀ½¯­ ;ÜÁ ¶©wâ âÀÇ¢1õ:£}·ÄD:›¾‹ïwçy«ŸÓOÔáÊL®¿9aA^]êÆ±h<œ0¹mçìy¶ý…­¶NhS˜¹’×’ö h…ô'¿›ÀÑ2ù¸m$ Œ¹,ñ,šwZï†q™¶–ï}™'þ ¾îP?Ü>G˜r{Lì~Ħ×ïnëö±+ÛMs<Æk, -¿ÔöC`ç´%LŒ!¬:Ö/›žÚÞk8Èw¤ÞP˯¬Z‰°þïŽiÚT¶—&Sd Ê=”¦<=ãßè%IÛ–¬OѺæxi~9€£Úß´F=³5'k–tó­6‹¬ŠCŒRÊWs*nßX­.ŠB#r-ÔiŽ¥w”æµÅ'RÎ+ðæA»+ÉØDcø>„½¿a{¹Ú5 ¯Pùè1„\ÃnF«}ïå)”X7žÏ“÷zAr¦9øt¥% ºÜ‘ÑZÚíVzàw.–ÒtLžï½K¸Q:ŒfÝ„¼ÐU_ æ´×i‚uˆs?¸ý"q¯KþÚ½o7¶ùʉÀO*;Ã+9Ùf¢¹m à¡ GÐcÃÍ™kóA=™Þ¹%Gpêœ ·¿÷Ò@Y¤Í-­6ÕUa£cÌ–ÒöuB¡™<§ Wé^û'ß´‰ à§À `_#ä'V^»7Q¶ÁñwçUðO|4ÎKû^ËrBàh{½ªŸ­Êi‚ïc´'Úè»Å¯µq‘µ¨ýJY¦Ðž[ã>—ß—yþà‹ãÀÝèöÔi ììl{!ØëÖæÆù²öHOáIín´ÅøDÔ¸íngµy×^+a,ˆ–°h·Øðë#úp_/Ê&ÙŽÑ6öh¬—ÛÜ»z›ÚD4Ïó¾‹fz_ñØ¿»¢mÛMl«†žÀuyÐ@ÞÃÉ{}p7¿9´yù}è¤_çñƒ{µÈJ„Ï÷ñöðêå6ºqc9àI¦J›­n,áhÓ0ˆ¦§Tç i3;àot;B¾úÏ™Oð#DPuÆI媿ÇoˆûŽ%jGÚðªƒ0ó-n«Ñ&gÒW¯ÙøØ %´H>ìx‰§ÀÇm(Õ‘\²zÍंE ã0˜S m#›¡y§š~pšÔÈúã„¿Šƒ¯Â43…ŸQÒÈý,íËt,g¾%ðæò5«Ï†ȸ=8%ìX{£þPRé`¢›ö Þ…»Œ‰ÚyÖóóA}Ðp¿;p`LÄoã)üªó¸|Á³|ˆôfν€ðôUúkòx? 5þ|`ƒ¦ŠÓ#ßbåŸ_ ¼ÂÏ=S¿ØÌA[7Ã&IxÕ¸À›oО¡cÙ X¶Ý©tp BÏ$#ѵR^‰]DÙFeÜWµ=Ï¿ XG•M‚Nˆ81V“¡šôli·Ç‡[«åUn|€ õ¢ó­ƒæìña`JÚ‹˜¸´¹¸u‰,ThŸn/T\pÖÑþ.Œýç雽x^ÏsK—ÚgàÈõl*Ã4‚ö¾‹Ì4¶[Û(xQi˜ê¾Åé_ëŸs‚5åñ̹›ÄÎr§€6ý³CÜ;˜gObGüfší¡ÂI²YͼÖSn¢|>DO#ÎÁ—ÿœ­xÓ0FGð¾¢ÿÝüCv¤ƒK̵îíK_¶¡?º¢\Ôú´¬o×]flâÅÌ:,JF­‰;-‰V³Š$·æ²%s˜Ç{ÌR©T£TU~¹6öiô/wÍIiyü•КþZØrÈìCk4€6ŸE‰§©/Äj0Y{·+.JšÅâFaP,ð… Lyy¹­´ÒJ¶öÚkc¿/mWæY[Ëâ ®{—(„ÿ ¯*ûß:Šÿ¤´Œiª]º¤eË|è‹L?ˆµåWåGíP#—û>ŽqÇ4|:a·Œ¤.üŽÜi„ý ½K û]F±<ÅtA§F‚«Æ±%Ðv—PåyíÛ¬˲á%áí„kª)¼#i~„FåÚ,\r„¿%ÀPˆ]¡ÚÌ2u¯¼|r—€y… }îC[‹uƒú@eæçãˆÎ€³Òˆ»Æ÷Nvã¥Á,aš–Hÿa'û޲¬ÿubŒ}#òý.ÆXДíc?Ê"=Ô^ŸÓV«ÐVÒŠ›úJ¬ÉÛg C©ßIÙ®¦ü_ð kâuž]‡M̦…RhknCJÓ›É?0™P(®lfªµd^ca ¦h}¾…YÚI¦I !HÆ(y¹÷Í_ æïg ö§Ì1¦`ËHÐ5SBšÔn¯ë5ý?ˆ{èò‘£#u¶jÞÔ×MV©çÂÉF÷„.çê†õ(¿›é³•aÜ$¿É¼Ï¤û>DOI~óÙ¼±ÖÜ*áQêÞux†Y*§蜊þ‘¹‡2Â˶—ÓVroʧ@¯j7oä™ÙÂVj.ÌÊýäK¶k8Â>WG‘ó@‹É¦AsÇ4?HЊÙYOÙH0Ÿj¥ÙùŽE¾/ˆ °«Z:Cg³Á°ìœê@ûûcºd¸R²žMhxÑžÒñTO®?„§G]ÓhVšhP|3úæp­q}]Ú®Íg¸W ^‚~¬Æxt‹$„Y9 ãÙɯìÊøHû£tmë]=Øæ¤?ÎÑ…QŒ˜Q´SiåÉØ‹Í…ŸüÒÏ:ç©!Ï9ê]º„Ë:~°‘ÃÍç8îv)öØ6F˜so´ÁÊšvojioe‘†©þÊ^§¼G }ýMýý6)u16 u"´PÒ¼ö¿FB£åvªB )!:­ B.¶ÕÑÚˆ*<ÆfJ©t@ @GÚ“òWÝCmŠ?~¤@j~ÿ⫝̸üv \PM)ßN›‹¾IWú!Kí.ƒ*‹vµñíðŸh‡#ä¸;=ñ•âˆþJöStCÊò BVã˜âr¶3“^̇cNG#æKhygû-âµÿ\œ¦Ü¼XÒú¤¹ˆ¥üJû¢æMÛ»fˆMa|{¦~ ùOçýnDC¿Û 6:œ6mê#bOáÝ/šÄ,±Ô“y-‡ö{Ð$¢sœ¹’˜­EîégÿF£rëú½±;,¡ž€v*¿‹ñº…-µàINÎlà\Ÿ[ ™ˆžHˆv˜’èà.ŒLk3"º'2±ÛÈ—rÖ¬ê‚iÓp „8%àæ3àþ‡ˆè-¾+mûþŒªÄ .œ¥¾à }h8Õ{V0V볋Xmæt¢F’žäÅóÿ^½mG^§"ü¤ÐY?'ئ.é=7„ZYÿ_ìð†{ùžãü¶‰_N»­îýÒ´_Í^ÿïKôá©yÌáKR×AÔózÚèCÊüo;+u‡ý A—„VaÌŠæusñáeãýÑXúÉ6H½gÓ«Oc! Ö½„ï/.„þœ ý8<Ú–c¹oØ“hPÞ‹1¿à ñ iô?Wãs:cj'[.µ”ýR1²®eÝÅñ•?i[Wíí‚ÓJއÐÅ +–Üd£ÑH·H/\écW–æk6Ó=Ńu©ËþüplC™jŽà“vÍ„‹¦]é»Q8µ§Žäq‹ªÌ+„Ofâ„^h³ió¼ …ò^gÕ¿l;6ÜÙÈÝ9ÄÈ?]IÞÏ󹿭'_¦#ÜégÑqü› ¸ð¨›1ޝÂÛ×ü 4˜Ûtm&äptn&PÓÞmc,ŽÀloWülðí ð‹1Pu7q¼u_NdmÎj,¹Ó9>îã½ô·ö^¢š÷ Þ?5´Ø\Ö ¥ïùAÇ–ah=Äp¾³OÙðä8æñßã;“¢7&D-e./Di>`ͲólxÿAÌ æ­ãòãûþˆµàw9sg±-qŸŽ_n¯2¶¿)Ë*gô}Ò’4Š„gKsïÀTèârñõm®6ƒ¡ßû“×ê(2]èË DÇkHâuòÿAe@&ôÂ<`àžy2ß³FõèRƱÅ/â&öÍ(v´,Ú.ôÝŽÌ Á‰_ÅzÞáëd½[‹ê[Ê`G[pà@&oà…^pÏðŸC=4ü¹Xß‹¨…¹h‰«ãÍIZ§P°Õh@iƒG}Ô ~×áæâ”ü_±­uÿ*ºu„ +°`]ˆ„š‰"¡¯~ÕÕš Ãl`=zô°°@UBß³Î:«p„ÿ²ëW\‘SVGæ2~…ÈÛ¸½ß‘ãÇ6*y@·^‡n­ø0L¨ ÍÔèX€¦ðXvqóã‰|«ÅÏ@¥±¡¸œiÿ2#X@ df¡9vÅsãàÒ© ¼Øá‡X î^`•CÙzlòˆ¸ŸÒá©AÊ­{žs.ÛE ³–á'Óªo?m¹Ô®eTaQÇSrf—Z‹±ü¶s…ý/þ>¾à –U áˆLcüŒ½Ô%Ñàø§AŒi_ Ôïòì7燓&v%8¡¥mгã6|ûlàך§4Ge«J6dŸÛþð#Ëä‰nË–]S]¢¢›° µ¬–A»#X ´Æsò–½Kgúâw·Y°*¶ú¶Eëéò‚ V)Q{üu#Î,Þ?všåîò»ØHpIcö'„7÷`ü%WÄAÇÜÇÀØK³ÒJ.¤<û é¼} áäúÐH+ÿŒEX¥/,%σ…|Å×,Ò׼*ä  O&„Ü‚W(uìLòS΢Qâ`Æ¿Bõÿíhs1úÜÁâTd«",ú”ì(°|É@dsÒ¶}ƒÀb0ïÔGµVÊ"¼þrü(£³«e”fš),@oç·‘½Z¿µÆ¦ÔÇ”A¦o.Cðx5‚Ý!h›l; Má.vAâlÒ>ž4fó{ÂöÿŠ)-aV[4ël# {Ð>‹$¨Ÿ„d²Ó‰­ÐUl²3º"Üø“Õ6Þg¾€6zíó0~s'þG^dÑwê"ÁdÅÕôÅ0â^OŸKŸÒÿUâ?Ù ü¥Œñ{2îȯqnÅ7ÁûØ%}±ÏJ6½ÝT¥×Ñ~ÛchZîUMzº¥ú Â2˸͌mÈmšJÛ‘ŸD&ÙðïT·1C0ÕEÂ4ÒTÒæÃÐø¶jr¢/<Ð~hEk£eà%(SQû€àYéEôå9$<…_+S®hÏá.ÏuE[¡ü.Œ©NÖõÆ,a«ãÿ'Þà†nÅyJà@ýÛ~Aü»)Ô)E½SÛÁñ}í>ìѺ ­ds“½©÷Ä‘umà®Hcç¹tG›\ñ iÐØTtÚ~©/‰K¼zÚÍ à»ó¬ä·6?-ñgÚæ¥‡´AÀ¦ÀÓÁz&FýtyVFë~yúöMúv=â¨ü>T|Kû®@¹Ê=]þ”x„¸÷!ü'õ,}ŒrÉ7ËÕ¶jwÄQ ÊÝü {ư7)¶5ñoð…2à¹Âc±QhozÂéžä¹%“ý‹šwá"GÕà ·.sÎYв Òj‚ÅÇ`›õ#û(ù¸½T2ÞA³¹3¸—¨¸ŠE}Œdn°7ÒÂßÛ¾Ð1]ê›g‘oñ­lm„ПÄÖ¥]~ý.!AðJ Ó2iáüoKÛïCW¢¾g ×ý½ í•p³v{Â0¦üzYí¥|SöØ6ö@Ù{©êh.<„Ž$ž&¿=mù†ÃÆ–Ùù±õ­BèH¤§ˆFòµ[qd?IÜ0ôåƒþ zeÁ-)\- p>Å„+uÑã mým¶Uú ìå¾êG ÞâhãŒgmV&ï¢^Ó¿Ð57ÇtâIÃ;ƒð;ÙƒõGY 木a“kféööndë_³~6´N[”€ß‰çÁ¯Á¯IûNß¿7íô)Ùß‚ÿ©¾cƒÕ»—7ý@_ Úñ“ޱlËnekr©Ý>m¦ï âC_dº¤þ,ʾeÛ—÷\¼¢bçS×§ðž\ ùG6²ÝÚ>fga_¸36Û2vÒAÈè1¼Us ïq„øííaÙ‚¯Lû=„(úÔ\<¿¨¯<úP—QvJñ˜Îàdb›7I{KÜÉ# -;»]]Yÿ’%êw¦Þ‚£Ùl¬´ËjFÉaF(´ÿJ?'ž·±w!)[Œbü¯.ÍQÌ'mÕº« _U(Œ}YN¿×оdZ2ãÛìKd?¢}E0hV|ý~ïß7 ¼ƒ2ÓÔ¶3pžà÷¡&Òú;¼èѲWÀ[ÆHˆ§Ñ½ }˜·Ñ&e‹aeB _ÒÌ:«Ø·m&CŒéüqŒK>h #Û²É|"ß̬6š‹^¬Îš «‹vZÙÓl¬\fGa¦èÓXOÛ7YÇn¦L›¯Áœð)óÃ,øµì ½89&)׿:òvôá6àMX«¹PžÑBŽÿM·… 9ÅMñµ¨B_%ú¶ÉÉ!û!ßRõ–L2XþüªcËH+U$½žI„Eü‡{7([sé¬F¢”¡¼…åø£…ášË·)iòÒ2Ïb¤÷öþûïÛôéÓ­¦ÆÞåû·ä[ËÿM¹ŒbB_•ë‡f¦'M ¿…˜ŒÀCõ*6žƒúæ }å.B`mˆ«Ò[!¨4k¥u:™ B¶¹š‚à(ß$&”°àRv|½YØ<]D`{6 ÝëƒÅnS™àWLè;ábÐå}OiÓ ­J }:Žœ+ô•kÐ*zo ²y,a4ÏE¡ýèCšºÐn_ß=èëü0Í}»p®X< ØesNЇè0H+Z88,9­‰¿všU¯N`qÝ3wý ”Oý^Lè«|df)·Æb±Ó,Õ1’`WXõØö‡õô«µ4<íæ„¾rÐE?ú>ùmøŒ 7evBÇ›t©Ã5Ñ•í1LŠžàùkÃQöCþ˜“PËA=?ƒ‡~šçÇÎÅ }õæ´XÄ`ÎÂëx~ÇRê– re{„­ì‘ÞùÍ{§`ezÂF#x Ó-4Ç:a”.¤¨iRè«ãyõ÷’ê­˜ŽÈÄ—žÐWþµÛ²°Ö8dé¿q+ãè}EÎÍí^@¨Míþ¼VåÅû5´@BX'-Më°p¾?ˆîˆõWFi„Z±Ø±ª)ïm~ؼGÙ­,—D瞤4CMÙ›Xô¯&mÂèɤǯŽ_éÙäécddòúÄ‹ÒÐ<oв2Ï¡>: 7ã»"¾øù=À>2ué<áâ>„0ôGõ¹97ŽûÖÑÖ Y£«ò²¿®‚.FX´$愘Q«Åÿ‚îd{-ØŽpý:œGŸE³®kÇ®ÐÖ„~‹mÊÅråðæ•²ƒ¾“þöS qþYâR{Hš8‘îø©/ ¬õv‘Ì+¸‹¥¼ùÖyäü_tœ1‘ù¿Ï¥øM7 ¬Ïâ¶=‚¼s(×7öQÙ›˜›¨¶‹G`HIËÀζ³÷æ§.:ÅŸóåàJé©V›¸ÖÙÝ·†!¶ v€¦kìp[ϧ â-îÏ®ˆžI+O§m(Ctu•ÒàW|7p‹:Bß¶Sp»Ž²½ƒ›©¢NI;MÛOÂü’„¾9‘ŸI»7nm% 5½è£³ßàTc¡¯¼»S®ôn-TöªZÙn¯ZÁ3¦Ï—ÜŒ69uOM¶A¢ÚÉ×»U)ì²vOýI¡¹„²qŒ@ìtÛªí&§ºÈ¦Ð׫)RŸYUê+º ôC}€ê!l¯Šyp ¼³Øl8>v¢Ýnëèµ%¥<ÏÚÅuƒ¹¿Ú Ëä$^à.Üü²H1è$3—ñ³T_—1ÅúÚS”Ë6´.6LÞ>mf²qqWùtÝ Ê5ÅÊú¾ÎFúÆôõè4eC+<ÍfÌ Ÿ_‰FYÁ§ t$ÑÉP_ÈÔ@ ô1fà3 m|.l¿Á¶hÇ8“bæªÀ›§3TMúøÑ¶+:ï¤n°Ý“ב·v¬„óúÝÍŸÔ9 Ò^§­@r=>-õ:_îa»ÔÝagÕìí(q¦?°»ï. “°×ÙÊ®'5ÒpõøšqÏxÁ´D¢àsÉE|Bg¤1ïóMÙÒÑ&Ù’¸\ŠIˆcØ³Ú (â5¸$°-ü ô¡¦6$êÆ­pÚº“rGè[Æf»ª½ìᆓMÈÒÃ~Iè™~H}L[êÎò”©ü¹"vítpNú25þ[<怠èã"=CÇDZ/p÷' ¡á%Õë‘Æ·^:±óHz ,Ío]. ìÖŽ ¤É\ÝÏÖ (,ôU˜øaÄßFo-°Û$U mØú7½˹àªÅ I€_» ÁÕ’ ñàd_"‚ÇN 3ÉxƒFÇÎ/’4Æ(›]ÄßwÖZfó+7行m$þ=@»ÔíMY&8ÜÌ < ¿v¼[!nOk m„5†i8yíü+\ÁÞÕŒ×Ðw°æŽ" õ2-ë`¹"açS í÷û|tà¶hO¸KÑ×fa-Æàë„êÐlȦÔØÓuírèI%õ>69¡÷LEÑç@3wgì^·–ÜNO.ž¬x¨è‘ÅýÿŸéÁ°ÐW¦(õËavÁÑhÎýÝ¿ùü'|37Ú_Æo"‘ÈÿÛå Hý· ñÿóÏ´€˜ ç%k±XŒlÌâñx“¿A!öÛo¿Œ&íÿ4ß M=Çï¼Ç¡Es »¢óù Ó­Û Úg¢é&^—*É$€˜&“»XdßÎwXKó&´3e[Jc¬’Øš(P?'‚fžg³§ ¤òAá©¡4óýÿªo‰.—kO&ªË¯ü®¥­ÆÀp|ëhë&*h6bq–i-ZöGÚõ7•€î$òß!OëYViËͧnâÈó0€“”%Æ‹&´QØ O~¿4R%Ð ƒ4Gæ¶ÜpͽËtÁ”w^s›ñ¿Âe.Hœ‹{請„òM‚ÓŽy b¹É z ×ßÀC ¥fc‚´~fqØs.™Þ‰†`±H•mG@;ú3È?ß4ƒÆÇ[ Û ]~?xâ.´Œ ga}±f20öB/ñ«ŒÎ±>ÉRÆôt—^øèÁ;–ÁÅ 1]!S¹i¯ÊçTgºCô5XªuâÝ•¢ìmE /qÄÀ],]›ç{p³ü´(ÐqqidGÖÄíEž,^…Ùh›zЇÇïU~Ñþ„¿"x«½Ðøxì>;¡ŽÚ="ÿ?¦ãtO»#ä,Vªzâ¥Rw#<ÛPŽëÉÿ"Ò»ÐEÓÑøÌGÁE£)Ø¥Z¯† ÁáÛ¾äa«¬]×¾vVF¬'&…žG@4 aÇóhÏNˆnMúŸSŸrkb~¤ß,Hòàxƒ£Ÿ#€x€qôB-i8†(‹)Ú®V è©IûÆ«C9B¥ä—ø ÂíK°OGB—³=ÐâÌáä´dz" ½8´‰rYì([€ èlöZíA„ªGCRÆZÒ…5…¨»Óh—ÝHåAðíc%®´wÛ| \n+¤¾q‚&klGaSøÚ¶·Ùò 6°êØÑô cU63ZˆA{» ÑF¶N`´_o_'F>±¦@ý§ºWiáüY6ddCÞióôK<µ<žâüV@ÃwV£U‚ójüGÂ%4µ×dëƒðóSFû-´é6‘ý<µâðAB¼Hoúäe;ªdÑô¤?D¾ mÖ6^IÀiüèÃ’ûù¾S9Q»[ÞŽÎ ·ÛL à§£ì*ó\~@dsšy_Òú„ßs0¬o"äÙ ¯N.Œèµî2³UqšêœÿY“Ó³ìÉØÉv«Æ€.ôaðvMD×o¡É>.º¡m^ñå銯G[¯f¾_Üyܹ9ÀÑÈ–ø—‘ï A2<G³S˜öv´ññOÚ(–ƒ‹6ã7Zð¥-W‹"ô*§}1Ÿ€ÈI ãî|{ “ “YìOr\ù”?F6/‘Þ¹X…ã!²鬌ÛÕ|¯DR—¼Ž@Û݉ðm3Êû—`þßÈž´5}™¸è1à„àdû ý½LÑF±½ G;ý=ÏØ©¸`òLÆBà6Œ²Ž¤=%(fŒt­b"öaKm–kKR¯%¼ë?%C¹™ÛE·°ßÛ•‹úÄn˜ÄØ;"Ãcg¾§Þ”Cí’~RѲ@9G¥§ÚmÜ4>[›¡âQ¾dœ{*h6?1zuÚ“vxˆÏRês—svø«M¦’MhGê¬ µ$í/Ó2m§Z¼úpKhC,B[cj³ íEÕ¦á‡ØEŸÔˆ#ô“ÎyDøZŠrüȟö”?Ó"Òµ©ð–[%FLY…¯AqwlÈDÒ÷˜wÓŘ÷‰Î š€Šè¡¶|ºÒ¾w›Ù€âm×ÏÿM¿8~¿ìE»íï?1u6Õ–(Ÿrì~(ö¼ŸÖÆ•æ%i‘7<Ÿƒ³Âec¼Dà}Ú¼ÞhÊæ÷¿úÍÑá$4«•p,´z£.Ÿ‡ ’‘Ù§Ð_šš_Ð,B®¥Ï% øG ‹—íž em*ÐéËí$dütÆ@3©}æäK°> ]@||ñ¦(ì(5)ÄLÏ…O– ù0tæãxÏm1O·6ñ†b¿{=ÛƒñÖü;/Ô½wà/¸<(58©öN=F¾CÈÊ_ªÅÉZ.ÎÔØévPdï\<ç>¿ÉeÑùØ:e/ |¥ôNK³+B_iѯ‚°à¨(›G·ÄÑ–ŽLÀß§6›Ž‡º:Ñ%c‰•¥o²ß-ÍyiÁ¸>$ôEÂï>´Áïls„%`@'%¸®Â¯vsÂ|ɯ—%Ñ(=’q)Ûë7¨Ž>讎əù¤»sIÞ7Ð?NhL¢%;4~> Yúµ`6¤+B•ƒœ÷(û-Ô÷{—…èÀÄ[´ÏK,ÌB6 v±D{Ûµ÷؃¥×ÑÿéÃ\›ýƒ^x©ËTBß©Ó1ЊsøM‰|‡ŸZØúÄ;šŸGß’’Ï$ÚbEÒ÷hl•e‘ú(~-/·{©ÛÞaL•!:ˆXû&Ï•‚(§¾%NŽÙ#Â4ÿo¿wÊV%*ÅìVú¸÷IJf3î×+?ÈV”0¯d7Ò<‡ß!àäqJ HQÞlIÆBÚÏ›«xB_篾]vzÖůµ3£ÄEkÓt,<¶!ÚÑOƒyNq¡Ý™8HM'ÌL>{9bîø&}'WèÏì@6kDÛzDò2Ýý‰g½¾oȸ”)‡ÍÓ´eƒÆÅ/\ØÓÞmd-ü­Ÿæè páX ôí,Ÿè1Ö à£Ú1¾ÕVÄÏ¡.CW‘î”÷VÛj÷¢x§ÄÕV-mHðÂÒÓhÛ%¬k‚”šM¬$ñJOÌ×Ä.µI™~¦ï–¸ѳŠÃ8‘-ÚäU´?ù—ÞÄ;tCÖiÚH´À }ã¡tý×è`úrHh,SnC±ƒ}ͯ‹áâ!mrã»ÖdÂÓæ¬íoä9Ž<ó§âkã¾£9H}æ Jз NSlXí:vSÍ 9ñkHÏ }iëN]&ñdLW²±-Z'NðUpÌ }#ë“þÞ„} Wèg¤;Mt%ïyÀf^9›jeЧ݌÷Pø©«¡Rd‡oTÀØŽv…K+·‚öÓ/z¤ï'ü½zÿåE(…¯» ÐÍÁm,Qz"á⶜ƴ̵DOðãyóþ$Êî ²ÐxÍ„ò_¢ŒÝ’1¶üÿR±Sœ£„mÁ¿µPrV¹èôÍãlÆ”ñ¥“OϤj¬/Âô愾JðDðäÊÙÖâñû+1îÖ¶ “Ïä}LæПíé8zßK¥æ(ÚçZï=ówI+®Eo¶ƒ·gÿ ScMTëoGÞ\¨,{0ŸÊ|]aXÕf!TÞ–ñ_Èná8Å]Ûã5a+³j‹@kÄ@è«ãÁ—S˜›óád桌¹#HÉàbh·gš'ßwa¿¡¡uˆ<»™ T‹Qè+Ínµî†™Urtew?ÿÙÖµ~?{ÍxÍ'ùa¦|º…Ù! làä }µyôJƒ–€èð÷Ð Œe‚Òæ'¹D¶¥ŒfüôÒTÚø†…¾þÁoN¶ì£{.‹Ò²Hq(!Ì?d¨µÐŽÒÚÉCS½ ÿdðwa`q\ºöÃ?,LÖãü0V}£Øªæùß¶°w²Ø„ j2Z«:âÀìò³t²vŸõ˜<ÏgS&ºG6ú` ´ˆžñ}Ðúèx¼–ÙT MY±Þa˜ÏG¡ã‰ ³ ãD¶R»ÈOþÛS&¯DÒP^ÊÆò{šÉæ`@ñ'Á÷,êpõŽ`~ê˜ÜæRk½¿ì'’I8˜·P*/: /=SÒ„Àº0Õ[ˆÙ¦¡y£Z¾D[OõÛ[îÁ·[¤ÜæÀL•ÈÐd¦öyÇiÉ9§FD+N×¢= ™3èé÷ÇY0ÒF.Òül-ï~nô›Ü‹$ÜX•ðØä/á½|¦Ã˜ì,ý ºlî]pv9ÿ[Çû¼pè)ëÈç*).9 Ó(DÝ1n8¢L¼ÑF t¢æÆ7ˆx…@¦Z¾ î¹@oU©7`~}X ¡èà™k£ø5¸®xµà óØö›†&Ï3Йå] Â~úîÕŠõ’? ¼Æ€ÅYÀ† Š³{t%Šp«÷’,\ã¶[úÚu0M³ÚRÏ2F¶@ó ³=Š€hóÐø‘’Œ4„Òlêõ>63ñуÈoHãh2 КÂtã"ŠZJHŸ–=‰`c±çÚZh2.Uf©¯Ö%ÌT—ËAÚÄF¨Óü L8¥÷oiœ¶_¥su[^±Ã(û \°ö'—xˆ¦çûßYxòÃO€â}Ñ—nÑ2ƒ|Ç"œ§¥½&]Ù(ŒmË)…ç8E!붔ÇîçØFem.¼2 V i8ƒð—1z6hSiµØž»>õ§‚¶Ù(44‚žLUcëúy;,y³/¹„…¿pWiþé§šû@läL”޾¼géÍ<5 Uý”„gÉ‹ÐfŸî4x½•ž¿£›R·ý{ZœQ;yð.ý’9EP†Ð†YôP£_ÅJlc–ˆ–þÙ&¢±ø ˰ ô(hCp¬VøÝ‹…y.L·Xý^ÔòkÚa°´þ€ƒ“7Ú-‰ávWÃÞXE#pßäËVéåYÔ Žôµ»hß“Òóiß?‚¿e=ÊžÉ&/a« ýÑ¢*_‚#;Ðo{àÒÕÍGº<´»Ê Ë±’ß Ì{Å…Þžv8®ö› m T%®ø‚wÚ+Ò\x·\ØÌ «Ö =d œÝ×]yIñ›KyÖ‡^L£ à¿Ó´œiÇb.`FòmÜn'íŽÄy‡ý‡ðØ^÷rš]ƒ#ƒíÛ2ÂEÀ§šSè^å¥aŒÔ_kO%'Ú=VÆÏa<œ^Ÿ?õ©xˆ§Æ‘€>(9™¼©¯*¶%-êè®i^ÑEBñ3Èga`s3BOÖƒßàc”¸¸Y|g{mØË^'íBìèæŸé±³ G¸\sž­Þ­EaU.l ˜놀rKë^s‰õÕFmòyÄ‘“–î´Ôeö‚æßt¼>²Ž‹üý±"éÞæÿ̾Â×,ÕÆß¶¥5ˆ6f|t'Œ<|^=~¼}a,•ìLˆõ^‹'}á€0ÉIüH=Åï#âþhâ×’i7/øAݺۜGÛŸŽË²¤KÄNâÚlëªt¡Üµ·Ú½þ"¨t¼k{[¡cÜîN>Ôq`5'оzñ´ñh–¶ýζÁöëèôR¶Glþ¢I®öLüÛ>~ q7fSt.§ `å=´ßwa²V¯ß¤»üŰŠÛxLNá²çøéiîôÈ wbèæ6¯ (M-é8Q&ØPҽض¥×»H«WP1†?à©@íäx„Râ>Ëoe8ÒŽª^Ã6¯M€ÂØr6»(S#úŒ“4¥#IcK}™ÈhŽk5¯ÍˆôœÕÂå\À[6* |~”™pK„¯u~*»€ç1çµÆ X;\ø»€yèAõVøx•h®>p¨ÜjøÕê1²?8…øØœ ч6&â^õ_”~~²R^ L!äûéû…&×G+Ó·{ Mù'ü…èlˆB£JN%oöa¬ê4_S'b+°Û’ô ¥ó ŽÅƒÂá¥ì¢±è@¶µ#Ûpºt¾ã—™)2 Í•©8œÃCþnÓXøVàâ;­St¹šÖTÿ`oúi<|{4&cÐ ÊÊ ¾¤=,λåÁJ¬õéǬ)´}ëdÚfdˆàßð0Z‹E‡…}¾úꫦ#×Å~S§N-˜tCCƒ‹óÑGô/ä8wî\[°À#¾­Ÿ­z¡”[çVù?å½z­ª ñ/†înúô2™^ }–ÌCÌüâH€«å@sáòãý¾eïòŸ2ï €*uuuöä“OºKß®ºê*›1sfÁpr„,:˜5k–üñV_¯ÞÊÂ×_m×]wKëñÇs™É–棗 _Dòm&w}–¦®@DRB.i²ÄmÓ}–µðY„þ…ŒFsøzlH¥Ä´;›ç­½ A¾¯ éë&àJÒ*b O‡±PÞÒÄЄ»½Žºûð6D\ÇwòaÍF g/Äg®ÅDHë 4Q\hzå{ò­Åy›1Öá¸.Ü „H»Cg.à{&ز“݇,ä µ‘l?ë ×S¯É¬²ìO2™C81’ª©´{ºhâ?-•?3ൎïGZ„ù‹ަl%$t ¼´‘:ŽºcˆŽÞ–Y8ïÿÉe-c Ë *¹Âej<ÅoEí¶q54»“zèØW‡å™$­)°„É‹ „(ît6Lç !öânòM>h~ªÓd Sðx姬»Ø°,8¡ÊŒUö$sŽ€gƒyc4sCøz,&¦lG¸#Ä5ÝÝ›÷Ç£×C.á×8eΔºáаï¿Ùmh›J ã,Ú~.õýEâij¥?vë#_ê»ÿë kì ¦³aò}ýBÉMioiŽë‚=VáàïÄãµs¼¥î¦]®ÏÒ›´Ñ ííøHó°äºà«àS¸â6êúqgd3ì®Í°y©›½ð"eï­_fÒÙ“z…Ø|Ü» '8Ž4öAs*ß´sìlž!:-{™©›ÜFÞJ©b:ÂK&9†¡ë¥Y‡Òyï—ýVžÔ@ÝÝâ©þ`Ì Ìr4*³H²Kª÷·rlczvÔY„ÆÈì굉_kïAGuÙ—l¿ŠÖ½ÊL°B…9Ž©‹Œ¾Š­bù}*€÷žøõ”0¥~7Ï=v:mp!ß-ß;Û“|z|8žÑvŠ]ä ñ¢«S¶¯i£{œÐ®;å¸È§ÍNû.zB6ëT%ï)Ûˆ ˜þÌ›FÊle·êímHúS\“ÖyÙôÉîíàdŽ6`.N‹_¥}ÁÂÞ,Ôß ¶‰åïØ•±k‘ýl&Žâ^+–§í3Z™6éuÎQ¶»«ÀñÔç|à 4—bRá7DÓ×Ç®¶Ê’‡À‰ÐÃg`Š^tIlÝËÖ¬îÕ'¾?nu¬â&Îã=iÏFãö*ãKB‹dgF[,6˜6ØÜVF0¹yý¹x/˨»‰?„oPz@t(õ@”D„àÖR?Ðwgà¾åQX à;¤ÅíŒÃR„»…ßdò9Ó‘†ï˜Oø‘ ¹hHú¤/ÏÙ„?˜Ïž›“Îöÿf‘þ.¸½ŠÛñ–ß#\h%¡rl5ú|jó8Ï=1CÒ\ŠpÌ<®¸Â¹YÝ&4+øÇMóV.%þe•57Zxˆ+£)ë?ÆfCe#H¿;?´y<œ¾Añ­l÷o8ÑÞgSâT™ˆHhËÓækÐV+W¨Ý sŒëر®¬sKÆYß²Gô¯€_wÊ.ZÝ‘ŸŽ(s+ÑÀ¶šãÝXõpè‹Ä¶kiIÀ^r=‘–bzÏÔúà ÒýÜ:`*Á ºû‘æAlø¬ˆÆsœ/¸¥‹à'þ~ߑןЋØùØVÀ^xê§-_Àªã.«=Ù~^ÐÏ ?¹•Ô¤u$õÜØn¡_%Âuæ9Œ± ¬îo7×DiëIƒ>ðdz4O ý<-Á˜Gèº 6»/ßÅî¨H¿[’לLÔÃpŒøsi Ñ _&» ãzþ‘­y.Ç–H’‹«|.Ú g&AÌ9{ljv~t)Û^Úæ ò®BÀŸ½ÀÒÌŽ®H¶àð2ãfk4¶×£œ=%€ÿŸb ¦npß¹˜cë/!~‡\g}ESŠ>à õ£L_ÈT‹êIÉ,ÅÇ¥¤©K‡QžîP·{¶ö£ßÒ¤^À4ÍD—bá4Dsšƒ>ÍÈøk-ðt¦<Äwe<[ô%z+ÍÄS]ÅC.VБú¸Æ]cßzÂ]­‰ ÁÀÐúÈógŒY›t÷ ¾ ?5¶ë+ì—ç*-륛èÝ øìàe!Ÿ2ÁuI„¾i)èÉèúÎ4Þš(>üÔDx±û'7³{öCS¶- mcèKe':ç]À]-”ÂKÄGµÁÚ¶dd߯ɠ©åŽI7öiÖE#Ø»›œãÇfÃGqï\ßhBg}ÝÛµ°oóœÀ3ð€éŒl|ØV©·Ý†‰è…4ZÞF|pXugüg8÷ÃІ”öÛ'L·‡v¼O›©×û"Pßï¥Ù8‘æ¦`t¥Â.g!²Žs[¨?³—F×V‘Uðmæ%àFÇÒ[=©'e C¤;n<—Š©<»xïüÝÁå¼ÌF–ԓôÙ[ˆîƼt´>™ ~¹Zsyí&Û#ìœ÷þ-k„½²nIêä4#yÈ£.D«Ù×i`{$p4£ËŽ´ƒ±Ýú Z{—±éö"’«q “»p†²¾‡Ú®h¿îS£¡ÿ:µ± ³ßÖ$ÏMÅí¬¶ó­gô|{·¾1ž.§ì¢碒@d}¾½…ïhËn†&¶lœ/ƒ×±,˜ÍC@Øà㢢ŒÌ·U)›%Y(¥ß•‚i‘xóMwðÕ™Ðæ¡Ú»dóL߈Æk>ð6}¾²±Ã«Í‰3ö±]ëÏc]Ç@t@IDAT,|b£lk{»„öÒVžÚ®„66iÛìëè6Z¯Øôáäõ¼ËeBâÊí@ˆcmÏ€l©Jˆ ÜŠùˆåkKÛ—a²‹¨ã™a¾ ÷´Ë BSîØ¡.žôIkJÐp&ù>ˆß!˜1x&•rÖžæ•£~ä .åzdAðáz~-œ ü(½Ÿvèiãàdzæ&ì¦~ªÒÚÜL‘Îü~GÏ6n[Ƥì~¾Òþg£( ¬`Üøý§¤Þèy¥øv,>Û¼í¹qI՞»?ßËl@¹êñy½á…‰íO]È#£‹±j$ÿn„ywÑî~^XXV;‚÷,ÿàiÔ«ìUïwá*à®vŽ·»¡Ù‡T¯Kù5ö%ÂØÇ‘×lÕÚlý7 ³*çň“ú[º³Y‘tè÷8eôáfæàqé2[?º½—®@«ñ7Lø”ãÛ`Ë@KúÄv²£Ó>ÇÞök/VâfÒü7õc)âç´“+)Çêø‡x¾äUôK¤ºÀ©„KOD€z¥\Î>dƒ£Zâ_ˆ‚¤ÁAiXÒ×¢ßF³½Pv{ÁCñiç¡Ñþ\4i IË º£ëQ&µ¼CóOͼžÉHC§–±W"ÄÆ¿ 8n@wMš¹ÀØ&né ×7»1.âßÑÊ’ŸÙ±Ñë®5WÍö„üÂ…-æõ’˜gSî q«ã×_»ƒ÷@b®h_ÍI¶%Z×—Þ@½ÃÀX‘ÍpitWWVw>ä¹,(Þ³}áL)”lË÷–¼»Í³”íFgLùäÃ÷àùGGÚ¤3À±±mgÐ/ÓÆàªõð¢ÈDHÝÜ;ÀÍSŸLÅO{sfØTÓJ„èéÂåþÖ±Ñbl¯HŽL/ŸkêÒImœtIbêÚí"òçY ´æ[K ½|42…‹,…· Ô¯Í[P†î\¤WÚlÚP¼=r„ÕÆ}ÚÐlŒü¬ªïÏ÷È~‹.ÆÁ¿þ Àá F[3ž‚5Q ¾m^ÁØq¶øWôÜdª¥á ÷.^[ë«‚bçô ;êáãòÖdaÿÅõ>…Ÿ¥ÛQ¦KIúø¯{,qx,š/î³Ñ­‡‹Én•ô/~51ü¶oUܦW0fW&Ï‚”FùkE|L†_)–È2ö<É-‘5G¬•Ÿ· Âÿdó4×§_²Oñè‘ͳßþ›îçxž¹Q'ë^‚~_Éé¯$‰0RàÆ¢œökãÖqAºwCow…§ ï5’ºï‹üâDøŸtº>\ÔçÆol›l²I£_§N &­ ª6ÛegëÕ—Ép!@ñwÙeë»ñ[›e1²Û³‹ÖÝÍ—XúwCKÞ‡ËÕ%„vPBàEÖ›ÊgaË¡ÁÔ¨W:…ꬰªwf­âÌî±x8tëÞuqV>|ÿý÷öÚ#LBÚaÆ™.+H[W¸î#¹ë’Ç<ФuŸ7Þx£‡cÇŽµK/½ÔN>ùd»ÿþûMÇÊç‘Gi”O~Í}‡Ç™„® "ÈVg!8Äisx>caÐhË‹lü£FZ‰JCîù Kk(ùiÉñ{vÍ—ÿ},L`˜"~#º>©5žÎYkåM5¿å'Æ·4Åð…íBæÊ«‹Ñ8¼Ñθ–T,) ¿¼Ò˜HH(¦Ke¹ŽI%°ó4E.d¢DZ¿2ùp&Ïøé"‚æór’/`ô̰σ3¸pêRêy‹"Ùñ @¾¿ó!7Ñ€ `bÂÐá¾¯â·æ)ûw…YÑÖ¤ÒtXi¨ÒÆ.kdÞ.o±pÝ;¢#1™²h©¦M„|¨Êwhá·„×CbÒóA}/­ðGgÒ*]½SÓ÷ÃÌJÐlßñÖ£\Céwáîw:ö^r‡;†øOiFÒ›Ùj¤/›]O›¹“/A«4Ði9¯¾ìb—Öq ôl¬‘»ƒŸ.ÉÈ…i…¾P ÆXòt¯Œ g¿XP H/ç*G™žƒ×KŒÝ’SqÑòHwâ­é‚êø–.qÚo>S¼ 鎋Á€û mÿ½H$¹äHcýÙøF6—Í“Ci/ÍGWƒOS: |ŸéGߤ®/I´þ)“ hš^ÏrûåH÷ñ(4Z°¥Æå„ÚËhÔ¨ ŠN8N0&~n© S×òÌÎN{¸Í¸q¹%âÁ«Ù:Í3§Å"2€Š¯éðÕ wñ]i÷òa¼ûxR¦á^>ϰ—¢À³0^—â>žü®¹·ÿ$^7»ŠÙ%WrÑé€É‡ýÀý aØ¥ÉÀ—ˆ G¤oFÛ½®jÓv^à›}ŽGóíÅÔ…\¦å ¡¢.¡*{Ç0w¦®báú#xØP·$u’ Ï‡1‰kíÛä%ôáSžKd#4±ûØQþ‚÷ æ‹uÓàÿžçgÁ¡¬Qoœéä?Â.÷<„ÛZ¥Í— Q7ÔG÷‚9áê ïìcÔÔAêEô'Ð^:‘h³8|¥žv·9ÎÓ±¤],!i͉jwóô¯L^Ýíi6'QùÔú £í)¿¿­ÍC¾Ï/<é3õmtòìK]ŸÆéaÞ±yÎÆýÁÐ7¯õ{§¯=\6ÔúCŸöH—#Àò(Ã8h㫌ÂeÜÆ\Ì5…@0|ÔÔ]ªUr7 uÆýeÊK;¡!m‘¸A3Òñü¿‰<àø¼Þ£ë‚“ÿ²Ñ˜>\Gñ92Ÿ#¸EðéÕK}Ђ8qõ¤µÏO}i¤5‚6™éûš=O'QŽÇj–c>âë•þ•M¥»§Ê¶´ ر„ŽoN?žˆ7ÍQÕ'Àd›'І^ÃŽ–P_П©¯ø×‚7¿ éJ«ãTÅEÏ…/ê«6A˜¢9ç’ËI^yVf¼Ùæ$®²HÃm60YÉʼnï[gµ‡»¸È e±‹‰³‘][­bhGí<ÑxBëÒ߀´§»ô)ÿpâÇðkç'Pæ?½‡6YnùËDÂk‡câ×eK¸lßÔ_àèfÛ÷yïçømr- j·’ó³~ê[ ðÓ´uèbA‡ƒÑS¡/GsÒf"›£?g·œ€ì/ 'ÀþKXõiO6õì›Û¨©Ì~7ñ¦õäÙÑ~Üm2æt› Ð“&á;Ú¹½ö½ £¯5b›‚<ïK>i›'ÇrÁqКoþEó‚‹ÍÂï´¸™m¡Æ1d‡‹#›…èñ´ã?X×¢ÁÛòQ_ø‚Ë|o°Í_s,—ï•÷Í‚X 8¡“y°?ís2ój<ÏÝ}&¡g¢·‹û¼À"¤DÕšYV‚Oƒ`ΙçuðmD–È|·æE÷H€½øA4Mó¦Æõâƨl¹3'üµPÌ„ž,p\¢6ÝóŠb ;5³~£âýÌ ó.ŠQ x6†î4—æ‰n½œ <‹âãÝ=þ·ÖÁq~”8zÿNù”ŒcéÌGèba)EHYF½ó—ï¿þj/½ô’Í™3Çi N˜0Ái Nš4ɦMƒ©ðA‚¢7ß|ÓäÿÄOØ|`óæÍ³ªª*{ùeo :Õk¯ÿöÛo;á•„MÊ3,¨’Ù ™¦PyôT>Ï?ÿ¼Í›0AI²ÏÿµØÐ(dÖ ;÷-•û¹Ø¿ #Ô¢gÃTŸrÞz >™y/A±¶yn¿ÂnB@‘3¥»°  ïŸ æ³µØ A¾I … }‚~·Èbñ ¥Üç·ß~ ››2mØÊÊЮX}›Ô4YGئãö¯‡ê¥¾|€6Qí€ã–øùÑí¼Ô<ÇqÛ'ZÁÍ繂s–IÑÙ[õ úº~G°ü; l£åù-Ó&»sî˜uË3òB¢Y5(ÒMÚšÖÆ,þ*OiQž¢?ƒ Bg^tÑœúþRí@3ÎdGlÌÎce¯ã+LðFvöHÕšoß”Åùo>îÂÌK˜âƒX¡œQ‡9Š7IÿŒh;މæø2Áÿ“ƒ¨.Þò,V i(ÊÂ-M‰¥½œÛ"¼0§©•ïã}_ .ËlBË€z'G ªœ«x[tWÞ 4Ñ .œ°¹·´÷¤L‚j·g^þ2¾éÅIªÏÑ£lcW! °èA.ŒþL€êïZ{œÓa{'z”íÈQü—²¶54Ašÿb$݉ҼB §¼j›°ŸI¼©Õ•æZ>”°8“–f+abÉvµ4„ÄNp,®¡YD •>J>»³n:Œß9´I¼4>£¾áµÜ~§Ü=Øà“»J‚DÞm¸Ã6N>E§l .ê¹ÊvnõμÉ(ŸŠñ05ãì˜ÄóÜâ>È> 4]h8Éä‹ôU%âå9”i/ðè$.zÚ“ U~ÇŒÉTé zÛzÙÍÆùvBh£î) kÐo¥eô}¤‡õCÛ.Íe y7ÆÍv©z˜â›RÎX`OÖŒÖn{ÏÖaüÚã/ å /ü¥±› ÏZÔ  `=h»g+¿ N”\BÙ¯E£ØJh .Õ'yð4øÚ |ÕeŒ §€·zq™oiHªMÕþ:YÉO%b÷”œe7pÛ§{uùÙéÑSFìÛÔ;ö»LZD{Sò×Ò¾á%.Æ«²wK¯bþ8Ùi ¿ŸšMŠ4VAûg‹§R6¼îAÛ²a”}ž8Âî¯^™ð w ^ƒ±7¦XYéöeôPxð,zAú0‡Ñ?”-(3›-Úû™Ûe“àè³µy¡âÛ<_¶S‡±IÄh§uóON¼  zC³ñ ¡•ƨôâ5Nœà¾=ñNç{=nÛä'ðkÒçÚ7‘µEA½øÑÃ(ãþ´ÅcÞwðWón›{pÿÙë‡Ä]øÌâB°IÐæƒ¬’ …tqâeÑä½5iœF¹çx±Ké;@vJïà7—ÜrÁÇ[9Ư!}Æó®‡[ÝA¡Ã)û.ÊI‘/íYìG?£î‚?çL¹æØN©×ì°šýÁ8Æ6˜‹°2ÚæðQŽocàD\‡†9s:¥´s>”^ÿ Ês'éÏÆ·”>{™w6XêôêÙö+œ÷¡?_äy%aÔŽŒß ˆ2vó¾’ÏáÕŸwúÅÁRïÄÛŠü:zNî/cQõsf¾½Ôîàúøñ õ!eú6øjüT_8E•0šù”¶aAíÔøÚc‡L¸â/3Ýe®kCgÕjÍ7‡Y¥‘zÚ™:º‹O›í8ª²Id\„Ϩ¿èUsPMƉhVùSôÍ."HäMÖD²ãÝ2ðqûµVþ QèÛ¦ÀÙù¿“KaKíÖ@mCa¯`“ê$hT"ä–yec)8Ý’qkŋcöùvW[‘DNÐ%ùZŽUã§©ËQr™—ã§ áÆ@ þYðôâ$ŠôC¨Xëz4É‹!…~UëîÍh¬ ±ÐÉqãfÏ)EyˆÈá·6ÄÒÌYNÎÙ “WSüjfl ô_Rsr~ÔJ)óE-ÌM!É'#Íj’ÉÅ ${nƒ8ãâ^²*×}±~É¨ŽƒK¨+S å˜wH1éëò©ùó综‰„=÷ÜsöÝwßYÇŽm©¥–²/¿üÒ |eæa‰%¼A^ZZjmÛb•)_Zñ¥•(ArçÎ0=zØŸþiïM|Î~þ™ øã?lÆŒöÌ3ÜËáVXÁ¹½üÊËN¸ìåýiÈûþ¿ø ›’ZÞòµú‚áL@ÿå'°×9 –2pKñ.âBÕœ° ûÑÒ©¤¥éæ´xzjIšó1l-‰µøÂh<.»ÜrfI€L§üöÛo39餓ì°Ã³’’’FþJKæ"P:m°Èo¹ùaóŸóÀ—ð˜[=„ëEJ ŠR‚Ë©„›\㬠@š obñ™î2š|Çз´]ð*Üæ`u˜·w|ZÙ‹íªM`ÿT6pƒ–Ü áE¾MnÉHÌ@øVõ¦ò¾Æ dB6‚[ ²c, c]ª•_6¥UÌl…üd,_š¦ùp/u¸ ¡yhf0w v™“y¬(çØ³ì±ýðŸºT¨q ³„ÑÃJÿ°D$;žZœ_郃¶ù¥±[! DZ hºÜŠæ†„ñ v…4Þt±D°#¾-,È`dždSײõiubR2P¿m‚ j˜;òé•gŸÌ‚=fÄÊ9Ò-êkvZHø¥oÙ*Þ?-evän˜{ ½¶@(¦ËÊrY2q àÈI|Öè"Œ!ãAHõ”Ï>È>ðÄè|ë`[¤YôâAûv²‹$áœÕ"[&¼E…AV£žS°É»m ÐBoŒåj¥¾'ý¹ÝR·ƒ½émgÒ/Ëró· ØàZ-âµçÆõƒL–¸t¶ ýv†>…¡„:E;KsµÆÞ_ G‘¬f¯L§b4“U¹ý‰F§„Ô@æ *®ÃyÍ×l„ £`€ßùÆó»•Å븿ÿZwq%Ì‚Éöm_Þ–ï±ï¥´²m…¶Ütô_¶a¹ýÝq0IÓBö_Ò˜­Ùg ëÆåFsS_Ðx¤—¡ê¼’~7úïéXw˜ „NŒÇ¡h5/›PèeFO¤9`Øw³DšåÜÜe+îÍ;q¡ºÏ¨Û™lo³OR#ÝE¡ò¾ Ah`²¤AíZö¢Øî GëÏM}F½>g]¸ThOŽ:HœC™nãì*}œçÒž;[ü&!@ÙÓþ7äÂÞžùöÆ23–ÌiÄ/̸[ÛJòú•ðï@'Vã·nÖ·N´Õ@'´ÌqvTcš€Þæ"i ´ˆŠ0ÊÓYš¸_|/;©ÍhÕ­(Ïûœ±œ¢6f¾Fssb¿%Î „Ïl—ª£ít´GÓ‰V[5|"ÌE%ç*€´#ûó f_¹uÁ_ø©9k }J^þ±úv´¡Ìsˆªèhÿ–9Ü!x)»µß]ôñ‡†m°1æÙJþÐ]Æ3:lµë‘ cOZ¸N’OA <ˆîHýè·ÄGäß—÷©x’ºÆ~ˆînÓ"à€ »=}7šò’_JìæÈ:vnâKØP”ÚÅëLÐÌ‹ÊÞÉö§ìê™ ð€wÙímó|&$î½§§SžW]H‰z„¡æL̈Þϱ·°w|BÛÕKDÍ8”6_Ù)<集c\ŒöѬ§o3{mhÚh„;ˆ^Ýà^uAß;õ§’×ýüÒšä·'þ=Hïžõä}(ãióÍŸŒ‡M0%±5a?ů¿mømX«”žŽÒ?á¾õOûu[ ·^ü€Hòߨ ÂG¨-cè7ê€ý⮵[{%ï„-9Ùöm3ÃFUÐ_º¬Ìٔ呌ËíŸflDÉË·i;›ùæÆä'œXƒ:ÕSvgŠ 'õQGÒuPê6MæÆÎ嫯Gß®Lð¼õ7I}¤)³4wurC›2N#¶ÜºÓ°ùqH‚öM?|ÀŽúòhã·e+åløè,@Gh§š!´áV¾óïĽïÉëòxƒ|ÉÇÖÉFÓ'¬doêº)aÞ&4àLNRlÌŽß¿3£œ s‹|¯tŽ¿å{0~™çÄÓ&û…\"¼ËÆ~Ôñ+¾‡NËý•‚_iÏJË6ü“-Ð0,/±wÞÙýÂîz—6®³n¸¡m¶Ùf6`ÀÛ˜wiûJ8,w„ºÒL ƒ´âK;Xš…ýû÷wa¶Ýv[¦…´}ü1“DdbàÀ.\¿~ýAhʶi(ª{•}á¬ìOM©–‘ÊBIwÉarÁ«ß¼ªie±AÊÛ9TF¦b˜ÒÆ}VÚŠ»Ñ¾ˆI[㟴®VWW;{¼áRjSE±µdã[.*npXiË'›ÿ\‰v÷Tx“ágìâ…ý‚¸:¶-8&„¥ÂY ([°•ÎŽ¬Âʺà4Üšbº@¡?°HšÂ]n 1Ï›£Q+ÍljñÀ|B5ÇØ×õó}LŒ8BÙ™lÛzy—ç€j’#0Š…©MÝr«òcpí^B¶Þƒ™*t™Úà`‘â’*·þ%·9¡œ>+Žlî÷Q˜žìˆfZØþpPŠ¿ š-IóÚ·¥,gažkÓæÂ‡…'thmäzñŽW½lø«ø{[w‹Îú¸¬³ŸÞ¤¹¬¶( éÉþÏ zߊÒ]Ì•çû~ƒ/{W–=À‚:ØøP”ìÅŸX$ñrÆdÄ$4¯Aã\ 1£›·u •X%Ùø½–ñ0ÿ·÷ DkŸ„Æã ±Îq6 ]tÇB‚jÏ¥ðßY¤%þâD§Áæ…‘VùªhÏmߎ”çºAv\*"-uŸšEÍÆà2~mö›·×í8ð.ü‰°IBc-š~F8¢Sg3>&#ðëÝ›1Y‡ÈB´o¾mQv+‹¥Oí^J™}èmUÞµ^ùÂï+i;ÙÊC¶ïL=vjþÝ•½'uÃbý€Æáµa`½CîXb8°ýè ýŒîk!ÿB¯0ÖÐH6ýr.ÓD fÕ›áó‘ówJÆ^‚aŠúìw~PÐ’{yöàÀw,–»ðËö‡.VûEÞõçðg𳕄Î<Ñš³ô$›Š]Ì#ýxÓ…ŽæÛ‘÷,V#!Œ}do~óätxùëq »µR¥#  >ã¸í…Ôqx^Xs(jŸW|j+%ËÖE¡lEÊÞƒ–R7 %4©ÛÎXÐÕ^¥—D~Bc~óo€7Yú­öÔ²À·‰¾ö4òœ?mÞ–´¥I@ìúï¾à✆¯-4•A‹'$†ÚGÔ'Y®Ê\—uá^‹_j»GÊCóÓõ'è—^p´µkkúÚéÕ+ÚM%ƒ(Ï¥Ö ×J¯E¼CžaH^ >li”Yrн[Ê&E^ôYUÝ$ Ài=†çpÄõ»áŒ‹O½hìVÅ.E<îNºs*× á•…%JBÝo¡ýÀ=4…({ÈÎiû½í;‰äX¦3\¤„Uf°ÌþA¼ž°U@§]~Öt÷²1øfùÕöxÉ1Þ·ÌÆÔŸA<à}ëolkòß%¤+£ÚÎÑrê8|ŽmBÕ¼è(~ÍP.¿šÎæýC¹Æ«ÔÄNç>åÂ=§YÓ/Û;%ÙÙQú#~¡ß.FËóíº ¬'Ts&ÇÖ;ÖŽ¯n$$[V±^Ö¹ä@ëß[¦ÀPxÊ`®V¿“˜iGÓ>—s^¤[—–š@_RŸr~ÂÙEfN<€ò5àò)æ¾i;‘7(_Ý8üÔöïÙ1Ð;qZæšCe¸\цQÛ ~¥ ƒya,$¯âI_%Áùú?NØ96Ü×›Y%îƒlFò{û2¶:í¶¼drldÚõ(ü÷É:‚® ×ü¾9±Ý“¯ÚÆ\‚8€YìÑÄe¶nòfòe‡R¯í uíI<„ƒåЙd(¿|–†GªÀÖw/úcCwzc[h½EÈs3eƒ²Ü)^T¶lí×<íñ‡„Ek>ÕI#7óNŒ¬äyI¸Ë…k9ÂQ™¯È â>ˆÓ>ÌÕx¶¢¿§ï&Þ×ü>ä÷‚óÎþ¡}¹DÑJï ¼K¼J˜™´§ÚüGÒ;”vx…4Îô¢Èqæ@6K\hÃS×µœw— º%EùkAÈedÜKØ=¸pøøhÜûö“ÐZÂë¢ÐzïQÔ7ã‘blhÓ«E°tæuBøùïuy1KÀ™ñä½sž{‘OmŽÖŸì{N+HÔH'>š_§ï%ЮŒÓŒÿó¼4Wwô|><ǘ• ù'¿õ¬äN5U ÆlÈЩ•^±u\^”âŸîdU_Ï?ŽFO+¶YÚ[¦[š„ |¡§`L8=ó3.‹úr7ø3^ãùT]Ö\Õ¢&êâ3ÜζESûšyê·‚¾Ìu;qéáÛh‹fŸÆ¼¿¿]ÈÉÕžp5Vz±²ò’‚IàØMëƒó1|M±µÖŸ¦·%<ÔÙœÎ\W,aÜ£MøµÊK¶|;uêœókßžI&Ëö螣òÊhåö쩉̓\'°´›ƒ@«wųŒŸ´„U®Ÿ~Òd”…>}úd>ME ¯þ'Ã41$ü ‚‡ Y¥[dd3Âý“  h]0ŽŒ©˜0ïÙ’Ög_›}›AûÊDÅÿXvÙeF|¸>Ú`ÑÆJk ¸dQ›6ïJ«X>AØü§ð8ÜSá¯à¬zQÐeb B»ö±E Qs_´à ³O/÷“³Ý­'`ª ½a8ÁLÐi —²i1Ò1sîÁ1Øfé†\i_£2–wïm2°pè}kwo&wq쿎ÐL 2ƒ>ä52£‰™ µdöÕ½åѧ5ßA£g =Oà¦V¹“¶‘½WÙyHXuŸß^‡&Å£h×ÃéK õAž.’ @ö} …Üö) p– HSôT_`•ŸAîL™ïÛÂï’‡(Æ· ¿¦z³xz:§>]!ç2±Å€E‡µ˜Ì¿q8<~og¡™dö§lCÛ5Ô—ä9È©6F¤iþ,ŒžÆ ðG4AZ.Èzö¼{ÚáÑýr6fáû´ojàMŽh9¦*/ý¦>ó'…´9tÄï³°9HO·ë.|øOÙrÖ1zÝ\¿AT–žÀ÷Íà“'‹Š÷L›4¢:â«Åúf2–§ö§¿&­exoI!,ŒÙ]ÔϦnmx‚¶§íÀâ]—$Jë]iìÔëZïȤD3á^Ëf_HkY¦8ÆÄa^9rÝœ¶×ü‹aVYü僣›!¼ŠŸE}ï!B6,Vs„R8r1WpT× ¯ÆÅd‚+ØôsôUGëÙ*sþÀmˆôBö#hIÞ.j£ì¡èV0Ô±Ô\ñ¤êCõ¼ø¼Ttmy´ß2,¤%ÄpvaYpM±÷ãññúSm}¯%#«w¾ÊùeÁ žJ$D[ÁXU¦:#Dû¦á,»I/æ† ®EÏD^•R¥Jþ Íó3sQ B>ihÑÅcáM¸<ç²ðEÇ6v4˰f#~-²Ø†/ŽïÀÿJ≦OÒÓ0‘o¤?uÿ¦y‹¶aÑŽÿ·|ßá Üp% î´mý8¾à\¤‘k©Gy_Ù ±_dü9ˆßå½»¿ë½—áè|Wð¾‹ð¾ áöÈt§±¾ô¦]´¡ÛÙÝœvžüœ´¿E)ÛD6U%L“ MÇ÷}舠oI™‘†mýÈ鳶ħ7 ª[yŠŽy04y–m$abs€e:ý#½Ý¸lq?[^a+Æg'2¦ïvõö˰Ñp¿M¨>¡à½A0ïcÅ<\vBä9îÃo úr íJ]“ÓöôEư £7A¸šãè¯Í¨ëú^Zî¨{Ú{×ßþh;!]u7ÒÑ8Z6믷èá¤ýÈ<™òJM{ÞêÌðŒ“0¿ŽþÑñv1–¶¨ÊÄf¿ ÕîEØ÷íRèÎÍ´ÿmeÃìÙ–IøU<½±>|]!9Ó¤ ·] ;«É˰|¦.H“¾¾*X£á »'¶¢(žõˆ”ðW zÓ'*gt(ÉÕÚGm¾çbJ6K¯³ãø¹MžÚ­xfëþ¢KÅìöºSí½ªýñšNý'¦€ÛNÓs.4óhÃ1\AVÊå†Ë¸Ñ¬»:¹¨¢1 ¼šÃVµV^‚ólZÝví팸ÔniÌï ¢ŠÛÎo{Ñ7™# Cô W·Ñl= åvcS¨ÚV³W1MÑ‹ ÍÇ+ÔCêèoÙ`mxž4¾÷ûã-;>éI„þÇà{w›m¥vUôÄžÉåwÚç[Í~;ÚPËxÂõW9øv^¬_PÂLœ9šŸã{Û ‘>~‹ú^ñ›ÁÓñ|,þž‘ ï^d*%°³c,bçÚ]W6Šgˆö‡cE6 Ð…†{pOü“yVRVáÐ@Ý%„âGû‘n^ÛÇÈ7>œ‘BÁSˆn*©,t„<2ެOÖ)ÿÍ í!ï¯øýï[ø;ýoÜÁâ’#h›3ù›Îm¶&rÝ‚¯äÔü+ñë¡Íÿ"]Ê“«d¾´I(oNÓïÍŒ»g"šRû\DêN=ô—#xSǸNƒ/-‘Ê…l>ø^ÛŸñ;º‰ óh·×ðwãN›¿ù›‚µÒ!4Ǹ˜2ÕS®öñç·pr Æ{J4¶t¡o'26Í @­8í˜Ìq[¸p¢ä`ÆãL$/Gï¼O·Ù­òçœøñý ƒn³)øOQF{F¡ÿMÌ•ßKÚ_c ²nS¡[ä·d^(a™Ì’Ét\jýq¬% )\aš2Ÿ'¢¦5´`ˆz.¥ü“•ÕÎw{æÌ‹ÓÐR] Y­×ÜÚH鬒uS7ø ¯³õ G«‡ÖI>¤&Þ<|‡ŽVý— ”dñÀ¦›nŠÏÍr~½{÷ÎI<8žãèÈ,ƒµaÍA]à¶ÒJ+eÌ<Џ)¾Ä2#†ÀD„ì‹Åçÿ•ÏüKºþŽJ.­‰§… Þ*kaØ–“–¨4½r“±°‡©ï téÒÅiêþø#„Ňo¾ùÆäÞZÉ”°­niß ºvíêÒÓ¦J~>-Í£ª¥[.ÈQdƒëFt‡¥À]™ìríñÿØ;0)Š­ פÝeÌ€ EĈfEŒfĬ0¢¢"¦«‚bD"fÅ+挨׸ˆˆ ¢( ’Ù0á¿êٞÙYv1<ÿ=ÏÓ©rWJ_:åëP<÷z è-D¢ °-²÷Ì͈”jЦ8ž>ÚŒ«ºß”Fà ‹D»„JMWþyk®|ô-q©Kx­uM/D/p dW®ÜƒÊˆï©§=´" i5ûX7¿8zÈšéæä ŽqI²}ŠI´¡¬fߌÏžLw{®áßô!)͉®„e&‰MM&ÈÏy`HÆ"øMR$‘Kƒíjzcþ ¬]¹¶ vS‡©ôÙª¬/X=¯¸oÎåMÃs½kPZHšâ@&ö]s‚%% l?·üýáJÊ]’L e{ÀOGPÿTý[à3Χ2°½ÜÇEŽÍ¾¤E‡ íð•+ñ¬„bU§xñ¤·n»5‚$×÷$>‡s|tôr:žÆ`Lñ¤:û´l«Ã’Ò7·Uîä~1Qˆ=ÏïÂn€/øKÅk¥79äé*¶ÈîΦ" ‘“Sp9ͺ÷ßÚ¼dZ¯ÒÙù&ˆN(Ù¯?BøLfr)õ&˹¼?Îq`¥Ã¾Èj‚Þ'óôV¹?¦A:ŠÙ³YÎÄÐçfy²Oz!®†/Ô 0AP˲¶•S?7‘Ë!RfÕ (“J‘$½ƒ³¬ÿUy9o5“!3iÜlƒ\ 1æ µ®u›€Ôž•°Œ¨ ™ùHj}*¬ª‹L:¤žh6 ñáÑaÖålŸ{ÞÀ~in–ôª†Ö·ßG²=<;k{³¶R—½ 1‚4]Ï„ãú‹eænx.—´Ã$ª%¯8~ªöÂz†ãDRcvº†ó]} á~ì¼—ôá}ï¾Tjòhfƒð7Šx™<©µw§ÝêmAl«²Å‚ƒ‹qsö.I²µÉk„ù•]àxÚªvP¹—˜ÓÊ6ÿ¼~—EÛ¤¿ðM<߆ƒ4©KMi[²)iÖ¨îA^ÿŒNqøQå!*éå\áýyO>’$º$„­npÕIⶤÙY¦Ä¬ª™jÊÆÛõàºÒîtHÒí’€%nä$õ“HP£CŒ$gI‹!áÍyÍý/ á·ªëyNµNo¶-iÇ?úíWæ;}–ê.‹‡ÁDÝEÒ;XýÃì&c§¼m,"ewX«L€Ô#ÃBSîîëÀ.`|q›~£tÿÛö¶æIxLüÒ@ ŸIåOTÓîIï±H;NÒÀosÒ°†‹þß룬Cnaê]ìï‹'eQÕ›°¨o…H‹•;îC­y®ÊµâÁë8Æiêã QË…Ô¹šBŽŠ±³:ÿ§§]®æ½E®{x_yŸZì\([õ—Ú)¦,Ϩ壭{O‹?´b|ÿKô_«Ý®M‹Ký¢÷¯:´ð8®4iáʶÛMÒFz g}­ÄE›oÛ¹¢È+ðVº{=’nßyóæ?hëÙå>å_[ËýþåÆÓ!ì{ ¥#7Üú÷¬ÜNçOø¡xNëi‡hB€®òSë~óûrl²Y½.×ÿ¿ì¥:E¹×]wÑ!l÷ÝÇ¡èÔîÞ½»ÍéÚ1b„‡ëÊ™C=ÔŒ;Ö^ cذaö°·UVYŪh Ч®0Û^ ôêj«øõHæ’7$Ðöo©h,ªK²P Ww:²>L;'H Ö•ŽÜºæw/€[Rˆu‘$ŒÇñÏÎváº\gÛïä“tʶA²ÿbX“&F&°z9+™û"u8›s@­ÒmiÚŸo[þEà­¶¤úÁ#µ!õ%†:V²µ¾þâ^ݽNT=@Ù8´ÄL‹ßhEš€hÒH^Kªœ]1ÖB‚¤[¥{ͣ˩3oL÷Èp­¨ ûÄ] Ð4{@Æ1|ç;åX¬~Šò!(:EW´¶½gnâæwÐé¥z¤Š¥æZí"IÙ>˜§·‘ôíPÅ’b8|±¹„ÁÀ’ÒDÕ‚\Žú¤5Ã=1Ó€ØIÜJ-Í¥nX‡ -8ˆô·ÆÌ9Ñ{LOÓ‘”s¹•D]fýé&i\-ÈHe†U6Dòp·ð9üåÁ¯`. Ê+kQªÁ{ûêÜ:2P|Þ÷üú›¤Ž„`yLèKo·%é#-ÿ È€ƒ1òYþg¸_âŽ|¤Ú%jÍU¢—lè¹ì+óUä:Ô1lkJ³¶ŽÔ™&ˆqå3å‹Ê‡v¶rm ©H¼Î—’ä3Ò–ýùG[†Z²*Ä¥6ÏS‡ûôß½h?¥úå`êÏv;úÈtÀÚê+©wÙÑtON3“*™´šÉæt¾G{P¯ˆ/¼ À­gë”ÑOy6q¦ĵˆ;È^A¼~Hw€¤}ȯ'ÐÓ||Ó—°nk¬TçìîC­ÈŸ×0û•¢:šç:˜q¡R$†táMHÒvtˆò ÷ÁÝ>[µÉGªm©îF:Ü|”j‰Äµ®{=VåŸ;ò„¬ZžRøâ¾ÝvKNä~ùìÝv¸ZïïÂó—ãWá÷Ç ýͲ½1ã]RïN¥î!Ô³Ò‰~}5ó3`8©$òOÒ^¶ìjìN¡Ñ³É ÌK‡îS‡š¼ @Ç!">4ý"k™ëâO˜qÕý¬„[)òþ1â ŸB\>U¤“mü–ª/ FiÈ¡HäWëï–)ÎqûéÚ÷kqyÔ’vúbÓ™9ìçgSè©%‡/# ³ÀëÍts_bºù]¾¤’fçÐBþ)Šwâ± >ùÚ„øß&?¨Ó–H{É\u0³“ï™·âðHÕýØ«·qÝs 㦇­îÜ®üØ–ÕY½±™C¥ƒ§Û¸d…I£“ŸÔ{ûÎ,«¦~zý§TQI¼„¾ÏÝ1†¥KôŽÞ‚ƒg”$Ï—ªÝW¿…Ï…O~âyå1¾‡áº²É¢ö´t?„[8‹V¿u– ÿ‡Êo‚ß÷ðÚ™’ç2æô‘&v·ómU®Ôøìúg·g¾Y¼º!9ÄêåKƦ¸7IÞæŽ×ÆükV€Ñ›?¡ö§Ub—Fx³}ËÚ©OÙÝМ嵑¢ïÉ×íy’‡iZ7µ´>ª>ƒ¼î@>ÃY´_Y&úˆpcÌ—ýß”_)yÚ­–û?Å J»µ°Ul¬•”Á`‹“ƒH|Y_bܲ|;<Íðy¤mÍZˆñYå}UtL»à<+¯Ëâ,胵kÇ.nçú¨Î5p¿i×Xܱýýt-ÒnÍE9ÆÓø&_ë$ê¸Æ ÚIâ_8 ÑŸØ1LþÿüóÍ7·Óf¸~µã'oÑÄ(\·nÝŒ®k®¹ÆôîÝÛ”——›XoùâñÂÔÉÞ~’ÄÁ*õ«>Räþð¼÷Ù˜M„—¤?4XôÜ©©Ÿì°o3ð)He_Zë\ b¿Ÿfþ€wÍÿeb:’iÅKLj“$q,iÀ†’€4†òæiØZL˜ß‘ßê„E¥\R# jÂJæ &R¢\}&'A$@òþeO•aW:8Èm®Ù…Ð䳸)¯“Ì}NVêëÝüC†æe^Þ Y(Ò¾\µ Î`1Øj ä€_’ö<Òz4ÀʧÞx7Ò~€š6øIƒ•íÝÅ€ÑÔŸí¨ß/11ä8U¿³À÷ÖüŸL‘¶+‰>fÒ§­ì2-çêmy(d¦‘'3X@I݃ôÞŠf° øÔÒ·í{}oià/Æ@— ¶…H|}¢Çƒ tKDH&šÒœwÝS5æÃpÂ^ˬÌÞ¥Råà,$í/“"=µ@2|ÜÉG¿Â´X~˜y6Õ½¢eðýëÎÓ!E†ËŒ€É5'3•ÏL.'É, ¤ª#™`>å[ ù†Aþ³NÃP4Ýîµsñ±)ûü{ì ‚÷Z‹€˜i2\EŽcÜ©¶«Ê݈g’-EµmL¶g’Œ_`oØR’Ö o%¦’_ã.æ¸ÕAa±ûÒþú•~h~HµL Þ„ œE+`J['Æ/°à“•‚‹ÏøáísxE뤞DÿõÓÀ Ó…ú‘5uô7Ý–÷7ÏÓÆ ¸…­×íóNzà×IÌ4#Üm‰Y–Þç7ú‘¹Þ|»ÿ)‰ï, =&l}°5ÿÉH@j¦\Ê?—Ž£¾™ÿÅTyœMk^¶•®VË%çS—àÅØ¿È£Ì½åߠʤ—r |h$ Ÿg~ŽÞb_µkÄk»n§}ò«Ö)X\ßNÌ©WÔ¯'QÑ>>ÓÝá? Rº“9«¦v›¬<Öa;õG¢)¿Ë~úG1Í1qôæ31^¶ƒ)ãP¶}–ž„)`ô+u]“¸ @ünâ‡)ý´$e~‡Ï¦µ\d:/»žCë&È‹C’ðTŸÅbÍ)á Í=åã1_ß³ xRŸt(^øž»3餎ÅÿÃû¾>·ü’_›È­&ÀË–ÕøéH;ÐwÓ¸UЦ•ž‰=í¬íKÎàùnަ|®Ä|ovñüj>]‚}r:ÈÑžŽ?îïP¢ï Œ\Ö–4܆?Æ”U”)‘O/e `j(Ù©ÚPYf$ÑÞ„6­¢æBÂFšÏÂÏó„DÍ嵌à>‹áHècjõ—¾ÅN‰öæ3Eü¤f¨y¦f’Èÿ1/"5þ{¸íj¹sèŸt[Rpx ®¡]afù4²)ÿ±ï/ØP)೯Í!Vg·5â6Õ컓ƒF_âß{ñ]›O®ã€²ëÎø×ó•~ZÀ£$ý©—G€ö@{çèhÓ9|¸éé`nNL1­Qwqk²ÆèQ«ú#Bù«¥ Ú€mï² Úš¼²;â/áj®Zb6W…ÿ¤¯;9 {§Ý¹чJ)3µ‰‡œz&oÃ[Q^ÝxžƒÙò`/ú±çø&?Ò-KÒ)Ik#‘&Ò"ërË\»f¬ývn­y4#¼íx rñ“rÔ%IÓI¥†ò±j(q)ðY­o¾ã@¯C“Íܽ¤A¤…8+qG»1‚]H ´÷ ZUQoK¯'»ù̲_5¦ÿž1¾'° ç:8×ûÏoÊöüu%ã !Œ;ü¤¹”æyI‹œÑ‹ÒÖo  4Ì›³ÑöV˜vœ÷…²ÑBM!RØ5´õ%©ðµAõž„Û2ín/@÷ÿ„>®5O ×7ü•åÞS ñÙgŸ™Y³f™)S¦˜‰'šŠŠ +™èMÒ×+û\òüúé§V§ïܹsÍûï¿oùuúæú[‘ï„iÿš™ÕA¬x¢'çLúƒB’VQ$Èòfžk38ÎLCjߤI“Ú†>“m¶ÙƼöÚkvqåÕW_5Ç{,[ @ÆJþª.æêüÕá‰2÷KÐë07¾ãÇ7o¿Í@÷ßoš7ožŽ)(ÏRñùi u+HB<·¡jÉ¿"IT®ás Ã7ŠYAcxd:0È’ÂTGtVÕNÖ]Y†&ô±½ƒÛ¬ã€ÛNn)nʳ`gàWF{Sã Ä•ö?ètäªk‹ûLŒà§ D>Ò©æÞœw\ÀüK€9µ”’†ªâYŒréìÌGzk;¶$M5.–t(X.I$ ‘$‚HJ5”àKk&4Üw$å ©UÈ„÷¯Ó2Ÿð¦7 ŒºáI6ÃÖ¼´K¨ÎqV HæÉ8ç ÂLËàx½—І©ÊU+™ÇS 5À&f&¿ÚÊ®xTã5ÛA[ R ³:®"¥ë=Ûjñ±lcSS© ²1ÒçZ_:[ 5ð6µ^ÁíǽtiVïóû¥é™xÉi{Jži‡Âž‰è6~\ªþˆÏ?·€ÿï¦cr]óÓìg’ˬN[íðÓ¶í!ò¢χ\à\ö’íOÞÎ?õOzuwcl4Éöˆ¯ª¹8ÔÔêÊ•yI%•Kþðrí²¿m­µmÐÖ žæ|Ëó¿€½y¯]O­ä „“4ŒBª‹8)èc Y5_Õ‚c¦ £—¸n¯íIÜçÃ}8x/ž±“¤ R,Vç8À¬•²µ¶k"õ¾6畆Iý·zc¢ÓB@IDATîeBÑœ|¿~8åŽ3¨ g7¹òºätÂ;ülsS{ÿ[ô`Â*ARo;s:b—º–z†74ï$ß2k!59š~ð‚p3úB·.8üˆ´ ð™+ñú[TÙ},;«öa–=Dï’PÔ¬“¸ÙúÐgÕG8_Äy2×¹|i¹Ï˜Ž€Ÿgñ™1W…Ž1OÖ®_r(§â*7_[£m‰$=a•¿OìCü»“7W¯á‡z)ÐÑ’F>%Îkòvxã6>_ΘQÓ¶Š2ÛÛå#9kgŽ+¹Ê öMŠæ÷¶¢wа쌛¦r˜!É‘ËÒßm)£ ‘¼ó¨-Êéè‹64W» ™d®)§<:]:4“‡ÀÒåž7ûœ’Û¾T+ž \„OÙš Úü²+Ž<tÝTv‚iQ}©9!ù•yQMÀÒ­¸©íñQ„É*ÀšpoIMùc­ (ð/Êí´´µ© ?Ë3—Eºš#“‘α«&>Ú—O¬”fåÌÉóPK~GåñïL5Gòýtæ»z$ uÛn½neÆ•Ž¤}^ {¸Pµ‡ò5\?†¼{ž·“ûPóu¤íØ·á3ÍàÈM¨s˜oÛÈîuˆ ôk#å<™¶°eätAÕNœVISYBºb{›é¡ÙIñ‡i‡ŠT)eÛd¸™ÚÊòwküü;¼™qÍÅd‡ÎéàK¦ï8Á´]ºµ¦{ÿÚ´0‡Foãk½´yYtäo¶ÏÊžëPŸQ14˜ú|l t5›dYD_÷L$eæ"7¦tˆÙ¤äóTùkfçÒ/ͨ˜ò]D¹Ô¨_™o¿f‘ö‘HúÏ´© žj wx?äºØQ>ϼUÚ…Ãw14sF˜©79ñPêT/Ì௚AØSnÉ‘ü> %•ñ{)£·(ªŽn|‹qëë±íÂåi+óÖ5C̯ÒéKOf"ðš]Œ¨Âï1˜Í‘³`ŠÂïVg/À¹Ô½HWwÈË— Ò"%Uƒ8´ijvɉÄó(ü0—òšm§/µÝâuQø,ÜP¬ "ÇÈ.‚„œpM2$j³v=DO4QuˆjaÚëÕ3NàA#uÄ–JiÔb®ÑV„¡úžŸ>D€#³Cp1};<°àà… ÅÛŽÌ{¼9»Œ/f!·mÆãÞXßsì{jÜBÏ•EC˜ë Ôâ®KjÍ^DHâoüàYøŸRÑ¿)m¢ô¼àë¥mÜ}4¶j" ¯5Î¸ŠŸA;ú=lŠ1T"mþi¾+¹¨Öþøc³úê«›]vaÅ `Iî:vìh$üᇦÕFxþuˆÔÎ;ïlý Ìzå•WÌÏ?ÿl:wîlZ·n]ßätK´þÛY6¤›2P*ªñý5gKû¾VüõŸ"Y݉U–èŠÿæ ûÔ‘`…†#:è0•*̬ª?’ˆ÷VV81x”¤¯$˜x¼;h‘A:›ErSÁµ àlAäœZü‹4Ñð¶fZý~ޱ½gšLcuÿiíl ×~tm%À©i˜«¦€¡k-zŸIótRÒ·¹WA ŘÑnG¦­vÈêK_ÑÐOóåG÷¼ÜÈ€œ‰N.ÕµÅÝë|§Ðy{TÏwIÓ.7 0¿‡ ö~.`)=¦"îDÊß±>ÀÊs#€KùïI^oÏ÷¹TõÕ[^Õ‹[ÏCø ÒÌ?“ü<ÙØñÞÌjx_—?Å7N 3pl* yp×@xâ¶ôv7¥Y¡ßÈD™>­ïY–’2uc;ý™ §4•–ª†Oà#é©]`]8š/¿sÛFñ®©1·r¨ÚC’µ“È´qÁ»í»A“µSß§ã¸~Mÿ-w¯r—zT Sq+ÁÓ3 Ñ%mƒß¾s8,ÄåUÍ÷lµ896}€Å¨pk3)6Àó’~þ_«ÑžKZBÓ^Id 4o΄ì°ðáÜÑs+I¼ðAi?ö%z–@ÝW"p@â–l{ÿWé|mìšD‡=Ãw[¿«Âïüõ=z’39=àO£ÆVRNU µs@"³i€câ¶æ s‡üI¤IlšÚãþô—‰_læ&ïõinc.KÍ4W†Z½ÕÚýt5gZ¹=Ñ/HBž¯ƒ:<² ÷tûõïÈ&Òt¶éB}˜øðÍ Õ¯a×’mzšoC8åxîF?´&ú.GS[ïhý,ÙßìeK·ÀæZ¦/5c9Ël·1à¿ɱòèÓæ$µ`´xç‚Òs}ŒÛÆéð(é÷öHv'øÚ¿7RÕlÁߨ\]6žpÖ'ŒW¨j½<ç|Oäú"óø ïÎ÷F3ìŸO~b/ß·ß0`Gž€½x‡^ÝêOméÔŠŽ¦¨ÏZÎÄ^³ùF¹ÆÎ0·žØ…Õ}ÅÅöx^¸Ä' Ìå‰ pÞ„ï{ó@õ!Vÿ§ É$‰ [ÿk”¾ ßG›ó©é\Ùß\·¼ùn9eQCÚsHzêËhî 05SÉëë`­áäÛf àãøäx„&‘ô…5}ȇì"ΪCáN´×£Ý§ðœã¼Çï'îÁØÝÃõ.îºbN¾°i*føÊv Æð±ËR5d)Üsò~YW>Ý0›à{âsÊ仌]uO¾Õ@ìØØ¨üN󲤖]ékǽÓîšåð´[âò¬$-oš¡v¶߲ݤæ2×Íš˜oKÚwæ¹i¶¿|_5§ÂOwæ³Í˜W|I>Íæ»©c¦ö(ìÕ³Œ3ç­:×À.ôÖ­–Ma·&Üy®S+ Ç Õµ_óØ/ÎcŽ1õÁkÛaN²Ÿ¯ýÏò”šNž|›e´/cÇG˜óôpÇ’Y–E}”P&äYEûÁãçÙøÌÒ£,Ÿ™ïUiN¼ç3ð^çñ/zõ~j>˜{ÖƒvŸ=Pù—‘v¬Ø…·næ$d¾©Á¥àY] !ÍM¤üºç e„©²çuÇ™ÓÆV èï]Þ ÷†O/¨—jdzMœN¿âuœöà7>¼9¬rçÒìIz ÈιòY,Í%ëã¾Øp³ÝEɽ hÍ6NMbîÒŽ_Õå\RËÈûîë4Z¯¿þz®»»–a#HGï‚ ¬¤oiiíÊ,=¾¢ ;™×ÔÔ˜?þøÃÃŽ¥ûweÐ3Ï«–½ ´2­޵ÅZÓJoú븉Õ2&ãRuÐG$è9¶ÕJòUú‚Å㨨ä·r%…sÃñ¾¥êà$†vF2Otƒ¤ Ô+{º»ç¨ˆçϰ|:pðY| '¿©$7O|¸"ŸT ^%m©–ü$pÂÓ#| ¦ÇðïåŸÜIÊÑ/ap"(©xø«èT⿯>ñ—Ï`âÓê¯Jnýã uì[Ó|•|V›hï!]5+|„IiËqÒˆV•ïrý<ÎjkÁæ`ê@Sʹ^ùZ+$Ç@îƒ àC酖ІÒ0ØA€HByPR1aJÊa½s5-¡#ië Åë;Ò†ÿß i—qºu,f–ý0¤ oNÝ’?I3Ôêkµ%\ää^p<·`Öp­“xWê:¢~ãkêqM­úîóTÇ«Æh€7v:Â8#| ïüqò±Œ¿ÈµÎE[ãóQŒÉGüeܽÊ$œ6V`‚6ìÔ8ÇWéxÌWV¼#€Àh¢”¦µ™Ä?ËÛ¤MìKhÜX)Æl+ÁD½™l<äªm‘öú„­¶çÅO€×Ãæ7þ4¿+OSãC\a¥^ëÃ=Í×éÃRš`®É;yMZWE m!@O;òø{òh-¦2sÑù|mr!’,K¨SQs3¿u)yÆ´ªÜ…í¹Ëð+¢|"g#íý0ÁT{ˆLUÀ3þ ‚Ìkþ5™³€Ì6à¢ý(+™ê¸îH}þÆ7±è¹Èì ¸xtJßµ@ßÈ[$)%Ü"—3gB/ 7>…°/Äyï§èMäù,³1<8•zdäß\YÕlÕÂqvR‡4è:©9æ=¶u·ð[M¼Éq9áQKèwj¢g“/ðuÕYØê­ö»x*Ü~‚ðÔ&®K>ÜMø×ð>Õ´µ5ãÊǘ–îJ ÿ9íÿ @ËÑ¿üŠøÐ¬ÉçÈüŒ+Á¬°%­ãQì³gâ ÓÞ)ÒS¦è‰«y<…; |»ŽC”‰ÛK(jò­’|ïÀÂR)àSæX€›Ùš Û}~÷bpž‘A˜¿À;ÿ“ú„rxšü8‡² Ì”fI‹†:s‡ÛÎ8¹+-%£¶#d.ªÂ_l_â¦ì$1ì-zI:WÄás[£g¸O̓¦Ÿ@]¤ö[bü ùÓ-|‘ùU*%t `ò~úƒ>dê¿ü ¬MUûÑ”<<—w¦«‰NšÖ.D6.ç_îAÕÃo±û‰ãYþ‰x²hS3‰ã…¨ÓÄöYÔŸ>ÉWÍvIÚE‡Ë/&]w’C«ÒÊ-IëçÆÍðÑlò©’¶u_ ã[éŽcÕ': xð}`ºR†ÓB]ÌWRÝ“^®ôÒ–¼EÞ "žm¶ ÷1ÿ ÏÄr¡é½ÛÜŽߦfׂ×n¸6cɻ㓫™)MÆRÞm¨_^Ê:-p¾73žÞ•í‚Ùšèï}Ö´c' ø)A?Ó¹r(:„´À5¯´íL~-&v¥<¦ñ|à*àÚï‡(—<ÿ‹Û»:ï´–‘“)Ç¡¸¬Îãzcâ¿Ð¿ Т@o[úAÀiisx‰r± ?V"}sNäóhâ^G$r%õ„6G qÀ¾ÀD+OˆËÄ©«ÞîŸ@‡Á†Úõ´ ¥{4Â"yÉ«Sñøïü7Ï\’›ÄdêÏÖ¹6uÛ~w)yóxÝns\hNp}àGì®{¯öH"ÇuÀ§úŲa”ÍîXÎÊq 6‰º/Ûù(6’ÿž@ÚoÍçb¥˜_ÅX¸Œ>þ2”$-êo„Y®»•’€Tª ÿ`~4Þ,ÜÚ·çh'u X-Š\‡u5AÒ`Zƒ ÔƒðñØÃe}c¡)ðŸC»ÑŽozÕô¥§N0F¥åŽÞAŸ¡î¼ÙàTþ¨m×QÐŽëb÷>óƒs“óÍ‹v<­åè96ñRú'jvšT›Ré/ßK@yÝgê¼J犒 ­¨ÿ¿‹?ø•Deîr—4®¬tHߪ€³•E´1üûå䀤r%9[ ð+õ mÛ¶M«d¨ ø•Ôò×nc‘íJÿô€ßb#@,¹aÔÈZ^ µ[žã¼@¼.g&)ÙT¿ÌdÞNzahZ{ÀA®j‚ ÀsmÜJà Ì{'¼@VÆ3ö$•çÈ‚!·§þnCyÛIVA—k©mä Ühª&‰´¿ œ®ïà((é’0×BCò¿­e†1˜»’í½Þ0Ke$=‹×סrC­¿ê@)×oõø«rÜ–p-à’z-¾äR‹Èæ°²«Í½HAYý‹¹üßQO3ŠùÜoj%.Žwk²,ò|\B]Î?˜fš;à‡9ÔÞë$iTú£Éx³Æò}Lªì}&d{àsI–o Âb0þ@¬_ýÈLÞï  ÈbÑjlVÚf0°Û‹&¿4„¤\\•ü””Û½9‹0û&².ê ffâ 5 ï›cCÍm‰IæWÀ“E›2ßjEÞ2Á΢v (0÷šR‡“å%߇çZ~Ë´¶ãðEº7ý¤­ü’^ ÉK!ùý4c:œwZ\ø¶÷(4 *È nðJ{h³6ÀX_Úö±ðÏö¨7†ÔIÈJ»'÷Ãvôo¼ 8ð þÂKj»ºG&­Þ[)|Ru ų ×–¤]-GÔL,ÝÏô‰Ï3¯‡~2;qý|€Æyè'<¡úL32œáɳüOEÍü&äŸxÒ–a H½¿( hRs-_ßølý¯‘Æ«pÓ‡Jûa½ÊdËpVŠ”ä/¥G˜.•½\~Ü h¼é¸t7Bœä·xŸÂ¿œÉû=Žyú®De½×·\Õ\Ù´5yø¼¶Ml þ¿§8¿æù@˹¼_ã…ËèóJ¯6m˜üO‹ÿ›¸Ú˜rº:8õeºŸ{#Jüôðûh+´O`X¨l¶mŽÙD.?­ÍÇ®œv*ö8q¿B±fºÇǘu œ˜b:£©È¿x£†[}ÈïñÚ™òœ†™Â[ƒoÕ¯ñèç^ÓÜÏÖýÿ0ò•Ôœ ®IµÁM„ë+.E.Æ;<žç3T«JÞiK³&¸¿Gø×Ęy­4ft›äO$<,¹‰ì?û¹ ¥·ìDåy7O¼ °(9ägõ Ä%yŒtÀE"žòQ¹2/bøNÀeõSæ.¼‰ÝH–‹5±gÂns°º›ã>´ ÿôáu%¾ áIø0%~ùÁ±ÂW â•ÞaÛñ‘˜)Ÿh÷,è:ÝäÒäóxK‡Ôr\# Öóá\ÊÛ¦ ž›ã£{™Sãg›Ý¨Wi‘t¿pWsb¸‡)¸ß”pö£Ün è7µhø Àà鹤ã}/÷5=ǘ²ÔG&ýYü :ßÁ=SÎOT\kBKÎ6ÏñŸŸ³`T»ïiM8ÓÉÿA<áW·pz]`žÔ3$íiG<—ov¥Eo,ic¶^vœ¹˜Åénè|ÇØ¬Ö˜Q Vwúkøƒà…w$>êe-‚WÔÖÇúR~ZÔ˜_ËEý *Ì®Œªf›Ö懒Á¤s3€Ã.á–Shgøˆ6¢æ̪¸VáRþË•âú3ic"›š7ÂŒy¢èTžXsZ^7ÎB eiûã<ý—ÕBN‰_j¿¿:qÙ6©’²£.Fö¦Úõ+†•iµÿœ?µ š-2ØÃ$áUD^þG…s +}Ьtˆòߤª¡3c1v16¾Hê®Æ3×ù0G-J½•í±¬TVÐ R©ÍzЩ–ߥ}W/+,âk„×^KVšCYP¢†§Ia¶€…”†¿é˜ÿá/~øþ}‹-6 W6¥)Hš³5 Ðt×Õª0éÂ`VL‡‘ï¥)KòYbÞ–ÁN1úE ñ—XùÓ]N TY—þ .$ñ+зuëÖ¦Y³fiU~à7o¥ÿKþÒ˜ú¿š>è5ªVŠ µ[µhÝH$ îf[öém¢ŽÃ…èÜ\õ ]ÍW07!¡wwŽÔ£¶þ|С®Ÿ|Ã\éY”žÎ¿Iר¶CRíp&ù¥-¦[ ¥vn€ôbP8;_¥”æø¬Ö"Èå?×LÛý¿Ȫ[O\¾Ü’<šP¯a‹T˜¼Ê$ô•˜¯}˜h¶Ek-~äþ…ô?_â“fi‰£;ú´@ @suÓ )vÕ“¤ý‰T„z$xxÅHzµ¿¨‡ M+æuz¸.g»‘+µpâ¹Èþ € 0ƒy2´†Uï’íÂ9âF m!Òâ€Î˜ÚÉבL¾.`«ëº¨YÆPÎÑ‘œß[Ä–f¶J_ÃöØ~­3ïÔkŠrzNH¦m¿V*Uö͘¨?Àä¹—>²ÈŸCYõùÐAB*õ´ÔV}<çº ƒ}<•§ì­’§ùŸ‡ˆsŸ?e"ÀJ[%ù¤KŒé ¡è˜SŠñA¸Wi†Ìm὘¦®b†&ÇXPå|xüÚ=õOÞ¤ª?ƒú_åGš8Ë¡-øVzsy“25¤»í!WÖuÌ´ ÃAq€fÀüÒ“¹1K¶ÇχôE_ÂEÒÞ cqb¦éˆý£C—ãæ!Ú„3±¿’4‡&˜¾,$TÃ{ù'ð9`ɲöäq²S'š!y³Øƒ‚MùÃÌ…ÄäŸqÞ‡ô?O&ÿÎÁ<Î%fW}H½>Ó•0a¨|"L”ƒ÷x6‰7‰GcgJs²X÷DÂÏHuúì¾ôg~I¬¢×šCšô7‡W>Bxۚ͑˜žH8ozăÚìRª¢}É”¯yh`¤ª8Ƴ€ !õRá“\Ó. MÄÃÄô*øç$Ç^ÎZ€§üs%àÂ3A-È™1ù-`תj˜…åV\~ภY¶i¡LJO¦Œ£¾ìŸ $Ô“2$/*_›1Ïz#$XÓºPÊ–=J±~€9eÛ ^gs$l×$Œ Ñ„¢§·gqë{·ìœèÜò6›îÊà÷T²œ´‰š|PæR?¹øÏçŸi·â÷òNW’”¬X~ôå_®ä½Wòàsà¾]¬PYj›ºv<’™Î‚ÿ~©ÎìÄú‘¯É\%–Óà`¶ÕWÚx0½3A<ïŒãÖ!îÙá=mz;*®è &žœÎ÷pËù;§sh›1W‡»™Ù¦6|Ézøúz‹î\økx¤«ÙAªDv61þÕƒû¤G|.mD6¯ø’æÞÅÿ¿@8¯ùMƒßCGQ~G›®%û³€³‘¹[‹•Ë»£Ödª™N½Q×xB’¯Ñ>ðUïÚá[‰U~¦r ù@ùy‹µ]m"a÷™áÝ0­„¿™~3å`¹ò¨—^ÃïD\UÁñÙ)ÍÉ“—‚í-/çþÛêðþ)”-åkwˆäñêëÐÄg™kœ…Dà89F‡ï\Êä ‡ŸY nIÏâžØ ñ'êB¶MÝÂd°aÄ-°ú‰MËäouû,jI'×E])ÿÞugÒ1ièUÎUîkN8ÍäÔ¯ºÁ–òÌ“Zô«®ÐНôKO›âôÛÂÎ%½£‰r^è½°÷|¶’}2zºîÒóbxäz™ ¿Ó–œ;És^P‹Øò›`ÓcÄÕ™[ËþÏ2ˆc€t±ý’#sI'Ú-ÅŽ•$Xo§^_^ƒØfæ")¿è9œb~©¹ÈÝ.W´Í‰8Ï'¹]:Ž9ÖNF ûóÔ5O¿˜¦€Ç—}bn­<œ·Üÿð‚c¢Rö6µ0X•õ½L*ò,³žðOѾh»¹Vç7 >è ®‡ÒÎÃnú ë/i°:aüaæÄδüìÐ NWI”¶&«˜Œª˜Â·5w'n1©X?³nbŽ™»]3tu`EëEÔ1råXRö¨87æk!—‚deXÁ ܉ìÀ‚®ê¶\~܇|{(¿SIYI8ñ³Â§O?q1éñé-LL,û°u<2r²ù(¼³^s²ý–Zšð‚¤6|¡×zÕÀþbÚN[ë°ÇŸ"Î×j¹Ë5x0v»™ÌBÉÄõFmé§‘–vQÒJýù@fHRû#‰öÓ© {òžH;d_•_ÑSÙªŠÚbªòó_äÎ×rIk剘}mu@~“îÔÏäÛŽÔiê—•´Sý&Љœ¡ft3íÚaTw_Ôš4j‡ÊAfòWý(avhÂÖävü íOÜï}&À¸¤AæÐÀçÉÑß~a^¤‡žÇù{Føhs'Ûúå¶cô³ z€y8<›2ÜÊlX{ªLf¥ÂÄú»OÊ‘M½¸_z™‰éóqmÕÿ±xzJé§æ±eé?fP³ª9~5g#Å¿/ŒÔµ&–è=GÁ OÈO¤µO°òKé“ä]þ¯=ÿ5#“}¤Ûui+ÌhŸH³‰ìE_Ä7à†À[ñvòuÚAÚ®ª=0×(J»F*ûð>›ÏAØÃïKU¿§s¹> ¿#øŸˆe¢¬6‘¾Þ$i+醨¿á½‰ãjþ@If›–Hs&½]Ég¡4§œðµ€¤‹0{^gqUºF€^aÊ\í\øTâ!/ØúÚѱ oBž\AÞœ¨|¹Y½•E˜™fש‘¦æÐøGæy;ojJ˜e\ó ™+à¡åçºy`¬éËy}ˆ —RãðÁMäwøoùþf €\®¶Q|¦Y}@[H˶«ˆeêùÐ&ÇsxQ÷§² jºy¨Ésÿ²“Hÿ öÐÆ ‘ü ˜=R>…q`ÊŒJ|`¾®:(½Èz#c®_(Ÿ¢Ô-…Ïä_>"æ/¹²‰žÎ'³X~ééBßã ÕPsÓOÓ…H`±ÔëØ¶bKòa ÿ¥¾3§ý‡;²üº  ¼^˜p—»óÃç¡”wåi~ÂjSŒtìJ¢òøw·kM[×¾z´@B)ù(|’IÐo§‰úžÅOžÅª¨ž:Öì›zÄ uÇäžÑN1ž)ú–Æ$…¡Í¨¦ïjdÈxçIæS²xÞ¤\ý­àÒµ0XR æÍq׎ÿ¦m…¶ãý^~°Ÿõ»mŒsú˜ÜÝ€@Ô…0¯[P%À3FšWNå¿¥r®ÿ9€òTMW˜þ í;@I[ðòg¿aâ£{üÛ’¶Þë'ÒÓ3ó­ïÆ rɆ5Føõ C[†f1hZZ¤§Ü­ó-¢4Åò %†°Â!ºª@ö­üÊä#ý[u­ë6ÈM“ºÆÊº½eºwýêF$î™Tƒäm@y5ʧ†ò)–GêNŒJÝ‘ÌP>ÖñëtLZå¾Iš“ë¹b¦nÀ?LÔ i᜚#Œq­ÉWŒ‹!Fƒ¨+Âq…‡2u‡>ÑìȤl1ðk®¾ÝºÙ‚ÁjS6웘›€mp–ª“ê,3&áƒé ©I– ¶í —›G wÿT?p9+°ô‡†ËóÓ_A/ÔŽÐAô°c­¥!jh®´¬q­\Íø—jl9ÖNJ3æÞ[ýÁqÏgCŸ- à·€@Ôrµàú9m' V¡ãc7˜ß⨠Ïu+ì« ØbÀ¾_úç¼ÒÉ¡^äáSé°‹{aRD ¤ãW‡ídó°j¨†?+F{“Öõ¹F´úWƒÿsõv3u+b˜ç,œ(eûÑ¿v@m€·@ô$‹Mԩߘ½Ùl’cþ@'ê¾=ê€2RRºô¶YƒÒèm0Þyž3&c/Ði]É÷W³¼o™ÖPÒkiárAµÌ“F«g8z;Âé+0ÙãïÂPŸ™,Ö‡¢w2©?< H ÊËßcl}!a +Rkò´V⸠+âç¡~\++uÉ$ÊÕI» ü0›±Ùõ!#ЯÒA·±˜¢•n*}Éì[s&á3Ì ‘Ã+Ûš¾‰*$\wåzØàTGFzö~o.ôiO½O›ëéËÄ GÅøg Ôº¦’‹´ÇŒ¶ËÓk¹†wý3¼£åx¶¹›Ò+S_Øû ú}ÌáÚk—Kásq»üw¾gÂSm a˜é\9$ kø-éwUö'¬ÃékNSß«tÆÀë¸(P'² ¼FøüŸ mÆw7øa,‹Ÿ™c¦g²3Ú óQh 3€pUú¦;¹^‹ßFû½Ž¹¯ÙfýE«£«šÅäRÆ R‘dÊ›L5ã«ö7C΄ð…W£W˜’Wˆ•˜v,ÄIøö´ñŸDµrT,©½ÿ†…™·„GèÂ[S6û±¨c˜ˆE‘Ö$Ý:ÊÕ[-)Îi—ÎŒ4Ë™¨êÀEV^Ò¾¢j C´&Ò»º”6.ܰ÷ &ã,ü8D}þ ?ýù$ϲx’2eÉÕ!€ZÚŠKB?Ò®”¢§’‰,|ÝŠ1ÃRàç ¾Mzï_„_g»!˜èâC‹á…»Aáö$=1{_ru¤L˜èÂ3¾Âü}MûV|MårŽ©Ò¹£X|`z²5õnÛ– ¢ÿœ¬[_uÝB8X…Ku«ÒJ}I Àêµ_`Ö¡­J |7Ï~Ç3ZÛ<†‡tPš¾«Ï¯r‚O/€4…÷àíš'Í/É[Ø5‡N¹m°þé+üÂ"`Ø|Í¡œ"©4êÏXêlþÇæ)ã2Óäk']Ô ý:©Íå%ä#*,BøÓ„¿9¶éÈâáįó/©|úMFu/Â+KB;“·Ë‰“rM½LŸÀ;íâ'ü÷áÁÁì´Ù.c^IŸê€kþÏ] Ô8nÜØÅ<ëÄoj©Z6ôIòêì¿vc[çL÷=ÏC‹áv¤>ÍKØ7yƒv|7ø^NŒ$mŸù\¯Fš÷ÇŒ¶ µ mÉ!˜3LwÆq¹“”d¨ÿ2ÚñÅ‚€IŒ'§¼ò>?¸+¤%ŸgÅ=žòåZŠÞ„/j]ÜqP{ñ%¤C]ÿ¶¤¶@3Z`mÅÇ\ ÿ¿ ‚XÆ•jxPÿ¸¨ÓjfÔÓô"ÊæÔ­çÝš¹9´¡9°äÓ—þúÆêAV>i< öúû•UN íí 2õ*}E2ÔÕü”š‚‹YÖ•TúhgÅ[áÞä!mMNŸTqŸ±‘´#?RWç¾\u¥í8‰~áú •§zÇÀšðw~áŸWzÐë=ý悾Ã"IëûïÅåÃf³&ŒïI4ç«xWmûg‡vPQ¼·Z.7eõ#¡h¨ZåJóz`¯÷¬Ëÿ?Ò)–ØrWMâU–Å¿Þ ºÐ8Yz.]Kmú€Pý“ m‹-¤çÔ ·˜§¤’ÏgÂDàÿÜ96kBGµœIäJ"浃ÌC51„¤ÖášÔúLþ|“{_ÜšØnAðróY…uÒŠ¿ù ýïö”wl­„®ß¢¾ïa<0à©÷vâÂñhûðÏÔÑ¢¶XJ+Ü‹Nþ¾ÂæØJªS ŸgZÃ'Óá“rÜ>`ªIŒG­°‹ñá_yåt%`I)võ>à u'¦ŠI¤;R¼6×—J=`²öyFí3w Oq¶[ ô½”AHŽ_ ³øC€S€!G"å2þC5ê›¶u/Õër=nVµÖK¬ŠŠÈïÓNùHÓ¥\? õµ A]yx1e¯Å²;™Ì nÈO$/²R¿GP§z°Ó@CU+á: ¾c`Ÿ”ß»Mq†‡ò:”4{Î}cv Ø@+W·y-ÿÚöŸÒD£þ´#õæ :çêòß Ì,,ÝÇ.甼Lš_§îÝVG›bÿ­ëFþk¸œA¿küJ¾ìHt€ù²ê`³UèwÔ/Ô˜v”q†Ö嵈°2ò¾F ?‰ÝiƒØÌèØ=š÷šçÐÕzpx[-½Á´®gfZ‰üß3áh{õr@À4©áÕ^tÚ˜<¤-·’¥?;~C”9Í&9r$E¡n¼OãÅÍ/«O–éƒÕ[êöoR#=–ld¢ä‘åÇ­àÇ«<žä=Do3ÞUCš—#ñw>@Ò0€¤£Éßó a½µéÞÉ„’c9ìSS–5H/éZo–œC|;b(â£æH˜Í¡¿×RJg¶îSò¨i]z¸93IzEK›šÒÀäüö,&¸p¹+A]Î?Ñ× µ¤ŽýjÃÁOìª~ß\•zÔªÒ˜§pX”ú ôž,NšDeç‘À©èüÛ)ž‰óT—=ŸŽ3ÑÃüYdm³!`òUäûÉól¸6nU×ÅW ùîå³±I’/ñ¬Î¸è´ª1\ý¯i‹Òwp6Ôì0+hÞ¯Ò¿_gEEÃQMá_¤óüÜI7œ±Œ³¼&yöýå`¦ÇÞŠ1QÒl@{xav@’¾6­ƒ‘ÊÄ9È÷wT‡8:r[ÖŸ“?±qÛ+9RØÑ~á§\ð?Gšñ¡7Í®¡Rs|{½Ô8TOôNåÑælT›Lª }aÑÅ Ù µnBm¨Jóþ æ}­Û|7F'¦ ㌗£wïNÄIZk®ƒÏûã~µb(%<á%§Ö¹È޳‘F2ä[XÿI Î[iV \=Çû·\Ñv”ÜJòèk<~ð¬|OÕžž´}é¶pòiCÊôjŸ«ÆxmK ¿r-iŒÀ²Âhp<'ÿ» ™kaû" % Ö)}p¨õ¡q/mÀ_N­)CÚm¤¸|íQ1i‹\»MÇ?¼Üˆ$¡•¹ÔCíÈú«èJÚœ—hßswƒæOÏ&ðkKòc|~'+d³.¾h7þ?Æ%jã=‰]ß?N¿0¾g!c‹Jž2'Ûö”¶E©[™cüĘÐ9{CmSÏ犾ª_¨äúcE(Ú_—¹óˆ~Ôµõ¦uVÌ[½ãù§xðÀ6Oço¡çüdªˆ(äæÿ›])ÕR.ï¿7H†Òïž™žšûݽ/ÃߌdÍ ù /×ì®^Á´ùÃú&Ym–7 ÿ)õ¦®t2dèk¢R¿XµÅøC[ý¤IÚv[å¯f2-ýP}·ôÅÕÐW’o" ¬%õsGîvkëÜbPO—^/Ú— ÔY„›¦F}Ë P€¡H€´Gƒjïö×Òåkõ×yržš¾r¦“‹½‰týCµ(5ŽÎù¿µŒëoðêZo¹>J^Ç„ƒKÃé°‹}åÔö¥zÒ LDý ïÚø?Ô-o‡~ÐWA‹‡zЙûékøù&¬íàéÍÈóúR»ª}Íëa N‰¥åÔ§Bá] x¹CTÍ¡5ÚN{Gh5³?ÿp}rpï.as0IH*36âÒ;Yu"úÊé ß L‡Ù4wý×÷¡áœ$j‹¥wJ=n'ÃÇ2¹9,íí¸pv¨-4’„ö‘CM Q/ 3!Ð 3Ž¿—ÇóÑ:RoDNä$íöfG&SÆû3‰°EZr—ĵæ Ì=’¤n6ÍÏþÌ÷%i*«—Õq ó¾í F.q® è{0ÿð fõŒžœ"éK´“g=¶ÐnWÿ ãFìqSMZ”…ûyó<¿µæšŒíi¥U랉ÏZv(qŸ™Í”€PÛPÌ–µ$sÏ m (FÞE“†s­}ðMeÜÔgÕѾûÛ_°¯ÇÇ1ˆ=³5ù¼|OžK¹ T(8“*Ì«©SÊ-A|ËÏÃ|2ö[²“®Ï}ÉÓÓ2Oýœq/Þáóå}y®G<óðÿŒy—6Dýý½±ͺ¡-ÌM‘­ÌEKv2—V^cZU%̸éÄÖ{©·¸L¯!KiªŽÌ S»ß0[ÛéËmn𣦿(T;t®ŒH>mÕ¬ ÄØ$v”é’l†j§yæ ÆåÖܹ©dµPØ\ÛàsA_ìÖI}@º7Ãn±Dͽɥ, ¥8¨ñ+þƒ–” €¤ðdñƒN Þ©Rc¶ñ¾œçò“˜O^Â{¨‘|…tAx„#^ Ón‰ªN$žÌÇõIÕ©ŽYò3slô"ôU˘MçÐ÷O¢²M‰×ªÉ3°{f¾Èõa2Õ\ÿó/s¹_K›?÷ÉóëòÏ[bš¡;x¥ÿ=”˜è+Þ™oîàßæMÓ&šð›úâå>ÛÚ¬ÂÑqV9ïñ˜çÇòT’¯ï-—}Jü¡o{§&OÆSäu+ʲ ÐWýPXÇ!„¥ðÍS©·Í¾]ûŒŒÃÏ€°Z´Ô®-º‰´°é©N‘Ô¯){Óòæ0Ó,v?Ë„ÞXvºųA_…Оÿ€ÿø@y¼ž…ê° s¼÷Føh¾&¿Çâ·—Ù#ñº™dùƒ²ˆ•òŸ‚ßwˆo.@ùÐ>\*à@ZˆéËêâgÁ7Œ—ªI3q›øcØÐH2™KÒ]è+ðSÔ¿ÔO[×­uúZ®ËÝ<ûŸ Õö,æ)‚¬ó·Î{ú>‰4«Žü‘6 z!$@_øÃŽå:ñ# ïê § 3+¹‹<;¼aaà[êàÔ‡P2iz2ô¹9Ù‚¨Ž‘8î`ø. ô Ÿ _?œö³R^T>1êO.I•Tô^LéGD¡6|ÊËZ\9í‘ìó|m å?;¯«µhÆ8©<]W4”†ù[…ø±OaêqTíbcӬưpxR¯EOW›ÚÕ6jlÆ&¼]`¨càµ}ô£>ÝYymÃS¸mÛfv–9pžlé a[GÔ®n³1›âö’ʽA!úN}BŸ§¶E½^-úŸÄo­,©—_Ò7†Ï²L½âþ;:ÖAeÓ$ÅW qÊ¿C‘¿””†ÜÕ“¿4AD.°WÛä¢\Ú VI:yZ=ÿB]ùòº^Aû†„]¬Äï&:í>[Ë ÙļȤC[gEû7u^áþçR€­ zKŸžìšç>j˜dDzçµq¢ZàLüÆì“ õÇ•~×6TÀ9Q ®à$tR¬4%Ó0oê® IRŒÓ†{–ÕTS}"Ëbs @‹€;uªÒ_z n<½“¹tÁ~¼35Ëuö§}¿ÞÆ>èģ¿8;!ꀙ 7 :ðãB„Ÿ‰¾æõŠÐ×ÜQ× Bª@Ö \ÞËÓ–l‚½_‚^‹*:¼l2×Ùn½ó¶Å[µ‘ i\o¶ñ°òêd­ˆóÇ{<òj‘=ÌæÄá?Ìj{Ò÷Ií§jï0@â ý׫a· '})ÜáýñHö‘äXHôÀœòE‚žùúµ­?¦‡;p ü8tÿþ‡ÉÖÖ¾3Ì© Ö¿ô¢z§°Ð×í O¥"h'¬ˆ‘Ò{YähsbÙƒ(䨳mù‡#£§™wjÎ ^`±€W%iy-OhšDÌᬱ.W ×t®Ât:a_Ðeôxæw&å´6’‹W‘ŸPã¢E-v¨¬îˆEÙ|Њ/@»Ð6 öÏcxTp€‘+œ|MM'©—8ï0鳎G2þ+ÈánfPr¨Yþ©u BZ =ÕÓß.B«¦a¦ßdÅ!~ÚøBøn¾k£6d:mÿÃì)&BwЧÂÝI#“I-ƺhñmWÉãÄq9éúÔ !繞z{·ÙÎ:š6ýÒÜEÎp°€;á‰fŽGM~¬ªˆõ0•E'òhCÞ?0Èã[ÀbÅb®¼žKŸqfÞDi#Ìn$MG:áé® eò^¦9f´%1ú½šä{fÍÈIÔ½[9<5†:ƒYfVɵ¦Wõ0\ΡJÙk }ù‰ßìû9$õ!ZÌÎšØ ŠìLù¾‚kÚ!OŠ›/µå¯’ÖXXø üÑènäý:ðÄpŸÂukv :QR»UûdÌü”¿•»b¶ ׄŒ­ 3œo[þc|vzÕÈCÿ8SEá™äÔ <À8AË¢¦\4çZ@ëVc¾&ݛǡèCðÄhüP¦‰t„à3ÀÛ,Š%­}qû3ù;õ'÷š³)ËÜÝ'ë5­b)è'óÈÒíÓ‡ë¨_ëøõ 3Xp8›úµ?£K-ÆJ—dGÚ›£h'ïçðÁ_¨?ƒì¶a¥ƒòtÕÕçµ4%¡Í͸ò{ÌUKw§OýÕL¢½†¾çè+Ob¬¡<ò´Ú=Ìš˜åŽ/ÔBÀGÔ Ä%®ãDc ˜^yi£ÿ¨E´a¡ÖؽWËÆh§Y»ô¢Fêñ;ðÎ6XϦ^Âs–?;þUöS/:ßî]ú!_߉҆ÅÄ”vÈãé\—.n»(çzXч¤ÃÓºˆsˆîWÿcm?CãfUÒóH-f-¹~ËcW¼±Æ&½YDºØ×–®wz“:ˆò”nïÔ»µÜF›Ð,Î97Du®¦–Ûº!ø9çàF£Cý´@•¥b$/¼T þ4rá܆ñAEo¡ ¾ãº7ÀÒ5*y{úŸny+çP¨ÿ@Z‘¼\É¿iõýþ/<]DDQšøG)Ê%Km[;úú/iKÊN."œÂN$<ñ<ý{ag+Ŷ-üUpgehê}°Uo£ö|e|Rò ùüoø8gœlfb™±K:Ósþ`çÑŠ5¼2¢þ_˜ð¶«{UÏjß»ßüÿÓû &¿5+˜Ê¿å\ësØF]yÁ]Y@<ᳺÂʵ7B¹a6ô[u­’Îl¹Ö]ó~ €Jëð¦Ê•E«úÂnJù2hK&´}ËrסL~€©ƒ-†uE‘@_‘äòS¹ÿƒ÷lÐW–š(C˜_1‰ÍÐËGÚò«éO1ä¾C ƒHÛ¶“‘ ÓV É;L^DÅn¡ Ô# NEç3YQ,ð¢+á;˜ÏßI’‹ ÛÛ¼ß €¡•G‘­Êúʾ+“«Qä³¶±ÿh/™â@_¥vņ :ôçKòO*G<’.Á <ú³@_RÔÇh^ÌšN†àÃüõU ¯Tœ[€{dpY„ÎBô•t{ ¯¾5ñèZêä!ÑëùÜÔ5Úœ‰ŒÍ„2®¬ËÄþ£™ORenD©®.úÊ©jýU.€«2òpJ-’j™6´=ÿBRN A¶lç’&_ßåq|¨¼JSøäôkoñȺfnò.^Î2—#…x[>¦ÖeÛºÓvÈ¿úÊcç}Ê[Bº5dr,¥54eNÔiÏñL¾«= Ÿ¡ö™× ·è=æ³P'sqÍ­Á ¯@F)µ@_µA”¹¥ï)ûx[‡k×t®°d·/;v÷”úÊõ=”ÓU‰HÇó–§;“æ]c7[}Ì‹°_ 5±Á@«5¦«Ó| ¯(#‰B©æˆnOÙS¦¨™ëQ€?’|Ò® ¾U8óMk3-þæKŒúƒTò s_h¦Ÿ­g÷&~šG`!°R‹Ä¡Ò1¦ pÝVv°y8²¿S®aʆ­ã·ŽžRÚêþ±ú@âáò‡f ¤Qý®úŠwš ‡'>Ëø¶[-6LôåŸíBMœ÷K\ÐWN±_J—DquwÒñéâ Øm¡øSrd¤oÖ„v2»•ô3´±´ÿó  ¯€GÔ¥¤¦›Þ´×Ëɵٔw(´«=Œó$ã§P‡v”Ü6t>0z“Cm(ƒËÌíÔ¥wÍnf~¥Š<¨øÁ¼ÝÑìÞÌC°¹-þýiŸU§»†76Ãñ·OÕ‡Êp5ê¦]2V ÿnôÇ~]1iãXôU’pCjU5øð/桚cŒvà&©Ë0`,$®¶íªÔ6䂾x ií‹88î9߸Dm÷©Ñ„M¾†¨'>ÐwWRß’éFZ¿ ?ž^}*C”l\&~ q÷¹„š«FÅoÙb{“J.å½Kè?6¥ð¡¾›?+éë<]k¹³{È÷ó<ç©]H8>L9éàDKax²|¯ôðHœe÷§X8îˆä¶©¢}´@Y'Ç­½«­È€¾Û)/LünþqWØc'xþN3$¹Ð|êJOóù…Ó8l-´ô”4è+9ÿ)ìjxÐöPÜŸbîB ÑZƒ®¡Žžnû†¶æ¼ÒWÐ+«P¤t8<«/íæxÚW>&r¶©nò¸™Zý²ù’EJ@«(ጛ^ærvÆ4ÅgŒkORŽÛ:À¼ê Ð}Õ5Iê—}Æ· m%nqÚLD]^Rò ~©[–¦òNÛ»ÈýÖc5Ìöp¿gî÷|vðŽÀÜÐΘµâ}CÊÈtN&_iÃl»K%Gð¾€^D¯%•AÒAžCí}H@IDATî[©LSs±YÆ5“k þo(i;–øˆKdÛkåCQºâÿBû~´{E‹A¤zÚØÚx»ÞÁ¤V“ÌŸ)±'¾gøåˆl3ÿ×@_ž`2ýzü6¬¿uœ„ÖáÉÀÒMõ¨6‘GÚž‡T»kòŸ¸ÙuFÏ À–ú†ûÓ‹í–î Fx0z 0›´ÿLq!Á¤+z Ü3ì{æÆÁ:mB¯SÇ–ºÄR rgb„ù1ôUÆ[±ovؾ–k| XŒ½ZˆBÄ ¾ê˜´õ¹’xLS `«ÆùH‡ÏYiªï²]HÇsÙ§t¸;c.à’Ä£G„"'x_¤á$ÞÑòQteÿ:qÍã‚J:®-µ$î‰T<À.ô9`ôÕ÷dltH“œNô7Ûøÿ?ã¢ö[âF&@e˜Wsm Hëý ‘o‹¯t‡÷0¡øi H}Í]cçЯŠÔä[©Õ€…²yYÂ@ÚÿþômRmð9ur«¥kš—¬Ž¾ÚoLï²GÌ‘¤sõÊnNš";OýCü%þ{'ç»àþ,û­)‡kñØg Î\ÚŽòØÃýÎ}ˆóé©Rïó·çXNà{CX/ŽwÝæ©˜‘?Ñî6¾û¢·ßñfytszÔŸ‰·3åö4á©­ø‘k¦yi¤mB[N5Ç”7ͪIPZ7Ôcˆ”šWK6•MþmNGM»ø/æ¼TÌtM|d>¬‚ãÔG$« u2ÛÇ0— &¡R±ÛGn3·¢™ö]êáÉÄÛ«ì~s9 úª¾ž·7£[µ5Ñ×—¾c®Cª÷<¦¦fé6È’Îpëú–ŽIN¥Þ4KQe2… ßqú‹¡Ld©¹Šü¡, —Ókjä¦Ý«ï…äöÑ]á»·Íîô»?úÆÊ ðñ‹m&~%ž°ê,¬gn]PÔ•…=…Þ'ÆlŠ+É×˱]‹Ë©óšt6Idµ‡Ôãû(>|ºÍ1ˆ\ý¤g(ÿ×sxCÒœ’†¬ÚÛy¦½F(RþÏ'™g­BÛóh‡…ùVzÙš% Gc&¤¯-ˆŒžç©5¬bº¬x»Í¬Ó ›&Ñ}Å›#ùä,fhìRižG Cu„ôúÊÎúO>Š»ñ™ ”¦Ø)äáÔ_[ÓéïC’?˜ƒÉCV,€ýÚ,AmÅïšÝ($)ê£û“KÐ3¼ÈH½LoÍ 7™S+O7뢧w^êCÊp¸]H[ÆXáÖTÄB¡V 2F¡j‚ú’ú•K<ß– Ï3æFúÅ“âÿ!/IsE®2eáÓMûRêfø@\¯ÊXB5 ×¥P¬®áéž±}†©Ç’Þ/^~Œ<À/§‰4ù¨9ۚveü—Ô|ÄûlW-úR½F=QæàÁ0ï!øåHþ¿Z4îp= >²<‹¡è`Óõ$­‡(9Žø‡›nÌfF#mm)Ü—»šö£ ©ònîÀ u¢dÿ´fÑiº­Çüc¸ÔÇ”ûô4!ó8ÿ«r*šèKŒ+´QœŸÙ”û#8Uœóº\Iªß·xUÛ9uc)ãŒÊ­ÓV5ŒP½ãú#mÚÀ-våQ°¢!ßG{~LPÝ+"À(nF3vÞ)`ŒY„÷ú;‘äj’v hRïDê¯3Ôvqú¤ú’Hv`áõ5µIyH»2¼y·æÉ“ØíÓERù³ýîRõMvq3»n®›'=AÆÚýÚ…ÿWŸû1gƒtsûÝÃ0»Žñ€ ÓÏ—ôÇkYÚ»æWQöíË­‚(úµ} ç)æÎ}’íÁ¢Ÿ3VñH­^`iüOÕƒ—E+ÿ© ›ŸUW~üþ:Ád_HMGì¿Ã^é»(˜}üA¦ßÕY òḨê½Ì5PWï j]ƒ¿ùCC õ’ÄËGEý lîw>+Ó\éVYxù^î{_‘xÛÁ³àˆ¥+â¹~ŠUõP( ©C¸Ä‚ŠÁe(wE_ŸNã¼#Ǻ¶yûÓÈÔÆ7aôÛ÷®Žb¿Ÿ ðeq!­¸+uDYúÇòuÕ H­øÛ9•ž3U¸/A<¦mÚKøW{ú´çá/xÖ¥öÃêýÓ ®¨­Qî\À b(o}IÓ°uÄJíºžë̇:"QY«m ®‘ù=ë–pŒ¨ÅM~?CžØæ•Ä`¶q‘–¬qÑ«Ûm»~ßÞ»ô™J]Éi)Ê36جÊáL@ilNL™›¼ßÀÄ>waÉó›û”®ÕÁ\ªzaÅ$É#+Õu´c<“ÀçÑðÔ×ÑkÌÄø¥v"ç—¦ôeH VüÄDiL5=ÊC%Ï“Ž«)”/ò8X‰Æá³ˆ÷&á€(ñþNDá³,Ü•©¶ò†Û¹B¶•‰\Š9 …í¥6å  š‡Ã4à®èû25Ñ $Âd_aq&:ë|$ñC.–øÌÜšh?ÜŸ›eêÿèA­yÖJëùM½wÊ$ @ã|]«U>•O2#9k|~N¸™¹)¹È\ÂLUè·-M>ÃâÐL/ #IÀÝàI4‹Ô×FEL(j:%·EOèFè=‰DúRÆ}5ÖMú&é¾ÔT.€ž41¡AÚnM›8/ä;ºT-ЕšÆ»ò7 ðO½¹Ÿ2ÝœA!e#i˜,Z<¼s€ÇÄPâìCœzž†¿ÞØm‹?ÊßžrߟԛŠßi8zâf¾š‰!Hö_eN– ú“—‚/¨e¦H àì.¶¨ŸÅŠg“)Ó£þ¦ý›Œdïá•/™o«ÏÇËžæô_h9©Ð `~ùuL¤°wFKÑaþ “•›û£ÃÍ;Õ—™7à›½Ésºxc‹YfKæh:ÿ©­Øe71/»ˆÿÀˆ\Ïóuâ~—4Š6cpŸ&1§%ž7÷†>2½s¬”²cïÞKÆðïÏ™ö¨…¨„'~&­‹ÆïãYNýêWG²`Õ7tªù1ÜÚüRC~BÚ$Ý—g ÔE’ïä‰+½ûN¸“QòyܪrÈL åtm®£h{_`b©öo‘tæ¾6›¦~c¹N|gœ5ß2Àß QÛÂIbP“déR®ø–¼m;&oΛ‰`GÃüÓÁäÛéL’i)f@ÀÕíÊŸDOñ‘æ=¤½ÏAmƒ$ÐEš{©íN~ÁܯÒA’Z#gš/ã^Ï"xSºŸ]:™‚(¹ÂÜV}šiM¼Û> Ïh·W,…zðH01§|Å£”¿©!ìš“ÝP¼G‰9¾d´ù­æNñÞs cUOãçUç~2)ê²DWÇL³¥YŽâ±à×b¾i/òªPÝø…òT{ù9~>!¿v"}W×[¼¯Åó\'Ìô]=ðRìöáN%íGñíä§3sˆñ k16´#Ïá~È3˜Ž¢­CÞU§U<(M”í_I‘AæJxo¯Ð7f¯d“(ŸÀ?Ð~TßÇ.½!´„íàKʾú)þ¿w­”jÞð6%ŸÑÿYËI‘ä€Ú·º=os¥nP®Z8kQz&e(èãXúEci±£ÖÂã?3´«ç|ËΗðÿ’†¶c¨yXã¡R´WÚ!(’Äï7yÆuŨ35VíqœÛ~ºh.¬Ç}†çÖ(öM¹–pE¸´ði¥—³Š~´cË9Ä;»Ý¸Š±F5ÿ¡óa<*åÅõÓ:÷@‹Âò/¬íôç@|%ǯÁ~1$éÍB ¯ÂøÆ}õúFe‘‡9æšØgØÔaô'Ú§*I}IÿSôUx:¬ÃO~Øoþg¾+Ýþ|o¨*‡ïWôÝ”†ÓOY¹ù-VâûéHìJïh‰' •ïᵃ¼¥ÍšñvÓŽ ÐwUì$ÁDÎô'Ȧ83>ög€¾×zÕ›KXiœB<’~Ñ|´Ò2þ\ØÖí’zÑ‘ù‰iHÞ…Å$%í÷ÿg¼À³“Ö|‘±5üïú*¹+úÊßo\~ÐWfÏÈE¸ØŒk+3:6”a˜1}Üø© %3‹!ÕÀwÝz¤(ú 4²ñg‡<'Ѫ_ÙÁ•g#î} 0,I‚æ4 ’0”ô]ˆÃé´#«ð<#\¬•²·›%…>šÉE·¬ ”nI,‹ŽÜšPµ+o³íwú–úŒ°/Læ{iŠ˜5¤¯šD\Ýí{à-6ãõ}V” UÏà¤Åg‘ýZ}¼û…ÏL%·rHÀ«&Öi’*ƒè+‹\ÐWfHK^ è%ÿYdZ„‘×ß|Ë{@›ouPóR_„•ô÷ìÚ_6mB™º´Nøóa¸—9.1×LR{”¼ƒâ°ppƒ¹¬ôS=÷€mJsò>ž¹üÀÓê§ŸÄËôÕÃê~œ¢·üT2Š0ˆ£æ<€•¸kásë´ûÝClœBxS±#>ÀjKÉç˜ÍGg@KkA_™þ€=á‰Â;’Wp ±“§o’_2ØÙÓ¬Åáu=—îd¦”]Æ^ÓͪÕ=Ñ¡ªžЗúºcª¥ÙtÙÉfÓ¥»úÂëÚYÞD Ìvq•çFlûÿÑìŽrÿðrèÈ!šóMYâsjUo@ô‰¡!+m#=à³”&`Èk£m‡;’§D^†§ÿÞ’t^NÞµ®œÛ$þoO 6=—|ž ãÿÇÞyÀIQtkÿLØ@‚¨ˆŠ˜³"*êkÄ^Á€sÎbΊY1aÎsDQ1Ì ¨+b ¨ˆ,gæû?ÕÝ3=³3³³ËªÜûÝó›žî®ÜNU=uê /ß*’$uüï¹þhÂzÀI†Nç’äUÃEÖ3¶­gïþ¯Üt1c¤ÅÔQùðD¬Ò~,[´š“(:?Õúµ[Æaò5â‘~ß6ù9 /ù†QÓVH9¯„ s{»éó}U7øÆ]SíÞ¨ê¢zˆß(‹éL|gØ/‘½àÁgcÈ]ƒ{5Ú2}8}ÿäXW{=ºUÖ) äIšXNkWg~…“q¨'uîý†—lH¢ÑV¤¯ïz#Ut/îÜÈûv‘®¶vÅË<­¤WâÜÆÆÆw· Øëêgêÿ¯Æy3Ï”y¨1Ê‘o8„´ÒÎ*»Ð©wš‰@¢ÞóDÕÁ´,X]>m)î¤U€b)×»ÚÝÏúVel2·Ô™wé1•ŽbGð¯ÚDzÚaÞþ^ž—áblUN;µÞ\}q.Œ*¨gÚ¡Ý-nq…ü¯¿Ä‚/Œ¡×jJ~™¤¾Áß“X ¯\ÖqÍ嚤ÂRšßöž üKåPô]´¿†K¿=ðÓzcÂ_‚wåq!" Ú£|ßùÉÙÀôÓàëQµ÷ã{‡o¯¦ä}êåôýcCt…b*nNZ+ŸkZfÅ=ù¶S)ŠsÈ{êÛ|RJþUÏ´hšEk“—g™´øE*%¢ÇµØÛ?çzŠÿsQÎOLÒy¿.‚ÙY èD@ßwZ$ølë{'Úí02 ­vãæÜ]†¥DÂÈŇ ¯!(ðetŸR¼æuóÆ];æÌSC¿£Œ!|zHöRÔ Í š¹‹»Š\wû ¯Þgsy ¯Þfé¾×Snss•0è+;qê€äo%úßÀ4°ø¿{áÈ>_³°»Õ&\Š¥1Ÿþ×bîsí$ýªC´ Qc! ÌÅvŠØÿÝVš ý¤ÆûwR{ÏLëŠÇT[[kù®ªÚyyÍÃnë ø »iéóÄÚ¹Ö«6S+Š©”(ôe:øK[ê[K9PKV0x+VŸæ`–bô& …¶‘(ÚÊW“P,ŒBv:•ýߦˑr™@:ÉIÈ%¬:¾îº-Ïbô‹.œã&x=ƒ0¼É±g²ƒ@ èd¶(—J¯—ôÿÛ¤C]»VaÒD¸U´ D»´ê‹œ§–%¤Gô@Mñü%> Óõ©ˆÝWÉ„²Iïqšªc°›K:تɖ9tvú0 L‹#…önNýQ/¦AWU2aé©“N­»ûÃôaG"§ØÜÈú®|Å¡nž^ÚïÂÎluxÖJª‡Rú¾^è¶ôO­Âà¯Ì¶JÝå…Ÿ¸ß›ŒëM’ªí¾÷̃ÿ†+yš¼y÷ºÜСŠG°\™Žú*î=éJ'VÄO+­¤#ÐOÞ4ˆ¥›¥MfIüÜt¡íÚÕ«áJ\×§è¾<¬ä½ÄŽç¾*ß¶“{ïJ}x4Yc:Ç#Õ¯/ýçÐ-~—ê@W! ܇Ò„ÚmÁöF?ñ}ƒàYT1žÉÂŒ1-„¼þ°3Pe".²å|ijy¶«6ÚVnÂQi?Ðޤþ_™ü‹~wÛ‹45Ô UQ¯ÿ & ‘îø?PKñxâ‹Ðîò ì!€ÍÓìa@ØÞ©žLªÖÔÄÈb›2ÀØŸ´ AI‡ZE3’‹¸{ÔÉЗ㇯¸;˜|¼&ó®<öï´! «q(ž§ä™rU}-¿÷{¢6ýÅsWFB”w×C0‘t¨8lWNÿšx³#i;;;^!u€S3QÃR+³NÕ‹Ø’ G¸1“|{rðê…É_옚µX¬ü† /l®˜…* ©´iTº ÍkK0[>1ËîFârGÂi½¼IVÙÕ|ÿ¡îÐÈaÆaú~4+²¬g¤-ãRÖ…ë;V¿)¢ô¤‘¶ø,Ó§¾"/9;o¢Áön]ü›”<ÙúlÑx¶ì<0“íQ‹!“àk[üYÝÁö bÏĺÙìø26·lP_ü¶—$íÆ'oñišïæºO.¿]ÞÙ^ŒgnðÁ0M°7Îßx’U?î”ÉX€CHæÖèÚ=€r?•ÊL;):S:‹Ä¶DÍ̲ÀÌŸ¸¨NâùTl8©àćܽ¶|!yu‚×ÊÚÝퟲn´Ý`§Ñd@ÑSö‚…ÉèP¾½þfüúü/²¼m?ÎV–d«Ô,úŒü¡îW\îêïoØmœüξ­]ÏæÔä$½ŽGŠKê}*Zà+;öaGE·Çtåuõ(S'ÝT½Ãw´‘8ðv›õÔ~¹~¥ì@ü„Ê4ÅwJJW;¼Ú‡ð +ÚÓ6‹DHß­8ŸðîZôˆí ת#MM$ÀáO©gíûÄ{Ïå¥ï\ùéGY¼€Á"\Uø½'°äž›G¾U‹nŸS‰£ICØñùIñ’6K½ >â–_ãy]/ŒéÓè½\iÞ–pô_íåô8•2I½‹-÷4}“~Ê}Ðø¶µ;3aM§ŽÑFµÀÛ’:_J¾EÞL•‰çªWñI»KO ÞCÒª9¶ù_Å×BDÿäÕÓYÖ#ý*HŠSê¤êø*ŵÆVRèN?3mM0ÿ˜õìjðŸ´õÞ§ËYŒZæB®®¥Rz#›É+é.Î#Uj…¨}ÄÎÔ«…æ:pÔ-F¨9{‹¹j0.rNœ^ï¥×-¾ëœ£èç…?d©xÓ.ŠH†_é!Np—Îù˜_@|Á¸ðLú€ë"âj·IôüjW­$Á¥ZU¤ù¹£³þ¿¿ ŠIýnä{âJœ¶["I–ÿm¶+šÅHƒ;Ø,›{þ~j‰:MnJ!²'tR§e˜,ΗýÔÌ÷wi̦|å^ !³Š”u“Hš1¸çÒËíÏÊòô¬çYÖš„µ†IWg!Z‹ÿ20Z?Ri¹“¸B~r̓ÙKúuâ©$¾Ü¹\÷-y_0õï$†Z¾v¿Â±ÌÆj]Òñ @”çM\kRÆWâ ÇÜLðÒ’qTâS §5 "‰¯êÜ[ߎBWçë|¿xdéĤ²â&Ë[ôrYh«[Æ‘@V]=Æ@ùi£Ûà:ù#Ø7 õ]mäonʶb ó-ýÍw€úŽH~Iûq:fÝ‚Õ×´ß›i¿g`PÙxlƶ»Ã—%ù$@KÛ®·`Pk}ésª8ˆg@¬`Ql:6ÒAoñ#lóäÓvwòY`ø'‘XY‚þ¸Ñâ|÷©*òæpÛ‹Á~êÊH*^‡ê“ÎÄ÷+þ7§ÞÿàrÏú–¸oåµÞ¦¤ž" cyÂ8hO“·Õ¨ÏO²UP†i" kG–e‚uŸ]Ùx¨Ý|pü6=š…‡‘|@§t*§·¤So>¤È§òK‰z[ìˆXü²þ@ì¶²LØVj|šƒAoER ÊA«šê‘Ÿ!Òá ”Õ¹©÷C¦ílûè`{àr¦ÔO@‹ þ¢œû_\…š„Æ3ߢGõôèÎÄ;i™ëR¯±ã^?NÜÇÖî ÍDgéù‰)ö €q4²¶-ÝÃ’ 'GsÙ§B÷UíÞ8þ.íaô\ ä:×”ô-3]!ô²½Ð;üYí68òx‰“lÀE]yóòD®–Müa·Tް§ë¾°Ñ¨²°Ô…¶!õa <ók–ŽT¿.Ô×UH<÷B]Áa‰ßìÆòÍ(‚]Èãc‹Sg8ìíyT.eHj"•“ ÷Å n„gƶ³_(¯0ÕJº¶þ"Ür×ÈQz³ëåùß-,E>ha¦î5Ìà,½±eugô¾úÄÂÌÑÕ­Câ] ’ü® lìx&ÞõzÓá€ñ-Òæ¯'–ßd4wòÆ´¹·È`§^7žgãWÆžrʻջ‘tý¿xþ`p›¤C¥Ng8àÿèØb¤Ðb•¤ç-¹ õÁ,´ðmÚi –çÅ Kï‡\WP×bÖ#ºò̵©_SƇÕMH<¯8»”u˜·´mݰ¬¼×ÖŒ¬‰ÑÛÄy»xrÏ÷ù÷éYï›çNôCÇSö¯ )ýŠÿhçê7¾K¿ñNÞÕæB%~é4mP~Ì/Mm€xjùñä÷8ìÈ»0ÅŽ&/»®#æMŸËHrîkjç›üôrå}öGÚUkhªò9$ñ <W©'_c)¯g²}¸0êcIÔWS2.#Ô÷,5<«¢OìZ°„ò¹¶¨³,ÎW»/qN×ÊŠº7[’öA{“.v§†çÍ逸£ì“7 Îæ7²ërÿ{­O>/I=Ü7xiá½îâúºeþ¢Ã)[ê= GÍÓÏ”íP€“åHçëçZ s‹Š?eÌBOGÀŸúÑgm럟#šÅ€*dü£€ÆŒåöpAÄáõ Û-vý@=W_“rãŸ{2š!>TG͉KéÒ9ÿa,9Ikǹ´C-DÝy~ž±ÓËØˆJ¨íQÝ”Yp 9,ññDÆ®ï0º•ñÒXÆ}á3–4F½‘¹öæåËзÇ饿–ð Té‡?°8Âs('|na¿G2½64™2dHÆáòTA:ê´´u2ÐMá>öØc–LÒàÿÀ'07k.ð7ÄÛŽ0k „Ûó pòÿq4µ¹s™|‡¨²²Òzöìi}ûöðHY»vÊÕ ~3.JÊeRÑð;òÖ¶¿1¿ÑÊ ¦7yYQVÂôUQ.¦ˆ6?:~·€AŽk¦fkuN Mkiq¼æÇ¡ÇL¶ò^ :ìãb˜¹ëtœG((‘ÔÎæùn×$‡× Ëh"ñXBXŠßK `f’ÀYp©'I›ú%Ooñ¡öü'å€w諟 ©Ú˜ÿP3!h%¾†61#cäŸ!éú&ŒÖ.’(¸8Wg®œ)§¬²H«Ø'³ X'ÑO µOI ~u÷@Õ±Úμ!ËÁ{9T²êHcÞ¦a³OMaö¤=ôR›`QC'·$©Ûi³?!Y0Z\I  ƾÄ=:¶CÁ˜Ißj?ì´PÖˆà+«Ê‚ËOŠ,ŽôBª6Ú±õ44© "ɺ3«`P[·!`qÜz#)½Zt/;„ÔYòy ZÃ~Y>Š¿,3Ý¿wp¶æ“ ØùÆ‘]ùæÇ‹»Éc+ݳڨýGZ}‚¸ú?3® x”NHjû&'ó oÿj\Ÿså'¤õ{hOžHJ14©}i’C"‡Û§Hz*0´jcv`qCX{š€ßz=ß¿;€0l©ß¡|*_ilÂ3õاAºo ñ(IFI±Ü‚´o¾GÆN°‘|£zw‡2YÚŠ¾á’T/BR^k Á$®D*Ì¿*íDU;0ñ¬mÞxcšÿ;à¸âjêë7ÿAú”z¢íÓÊoæ¶3ÏyÊ\Ø:¼J‡k¤¼IÁu—3™¼SåO#»ç¸œTg`(º<þ>´{;ÜjwTB²zÛñg{|AuUü^’”"tªn‰Ôãl¿æ0 kJ—*ZNG_ëGu[{zócËÚ¡´é#+Ÿ°S‘Ö[2º¹mßþ,ÛQåÓðé›d»¡ âq±ÇÅ“ûÙoØÏžå ¨kàOS#4ÄÎF¢ô&š wäRÙôåšH½à›“_rÝÎ{µ§üjHwꥴ…¤{5Á6ñvìÜ…Ò9†¶îA x8kM §²ô¿jGò¥æû‚{¶Gòr?d”.aüÖ‡+U5ò¶Ýe”Ûƒ¶|ÃP{‚:5 æT'‰­4—£ÊZüÃÑHòÛ¥3ÿ˜J»'îˆb¯Æ·"§Øjl³ïOúnr…F~U\R_‘isMq_D ý¶ÄË,´ÑžšäÄrìX ?kN$O)³4 ‡zvút'`PÂrVsþî 9(ðH~[|3ªÔîÔµ³É†cqø‡ç˜0§Þ²­c»R"ÕvuâNN4 )v>q=‰qÜFSfsé_ŽÀ°Š7)‡+ÉÏG|/½i_o“¶ƒ0› F’‘Ei*´)6!¶H Ÿâ¨0ÒLùé´ÑzáªåÊ!©©©¿›ô«ÝUåX–ú /£´ÈÔý!¤‡¼ ƒ˜.ú·¼cÌã\ø/J³ê¼+~q\ͳÊ?L|£$‹=Êa«Üg©V‰ $œÃ° ê,qijÁ‰g7ØŒJšGi£ðý°Îh/:¸jÅk|ö9Dñœg”÷êu°z=l5NX™òÇ ]}Csã/À2nK¸ö|‹gžï_;¾`'À¶»Ý¥Ì ¨¼hê·„1CSO ¨É²ô'Ô•äM%§ï>xØ…ðÍbBE%Öæ#Ôý¨gð©oŠ­M¹îß²XÚ}ƒß¥©‡½ðGÝ,§Õÿé»ìJ}˜ú~éx!¯oµbaOn1ç´}A•:Kä Î4Ø¢™y}b(éq-<»ñ\>Gj¿GÓ¿\ƒe)éÏ„!ÉÞ}™_Nù*þŹ„Í)íyÎŒç2~‚§ù†OàkÆKŒ[òpÙÀiž;¼=²&ßô¨³#m(e;ƒ'lM^„ã—ÄïÀÝk“sÒg;„'† =‡AßBnþms±ûš”-%ÓR b}u56rÀZ‘+²«lLXׯæýä†÷[#‡.N4V®›–¼÷f4Ï}²Høs°«)bŸ/¼ÿ‰fK5¦òæMî·åŸ[‡ì~ýõ×öÐCYUU•SÝë¦Ô÷e`NÅ(wØð S¯ùi3ð_Qb»¨!aÕÅW¢]s ¯‚i è{hÛEx@¨mŽ£X+F:ÓŸ"s–¶Óàì â @_YHŠd+‹-}ÂßhITø¤ƒQZúÊû/A Üó¾wO[lw EÓÊG&’´Ú½¢õhe…¼©_øÐ;—žb%¶?å£Îùï¢Ñ“¯üM ¯Ò,ÝÑÀ/Y¤AÌ.€°¹ ¯ê•h…Û¶À–ß+¨DÖ·ÃmV,•þô%|÷º]CÝ’t€Hõwlâbí7ع Öº1D³JM¼ŠPê!¤ÜfØÇj…±½pØË¥GàÇ3ä©ë¹#ë2½Á"©Û³ÉïÑ€2™” мý”ƒÛÈ_ŒY!ëd¤Œ~ì8¹ ﺔø6NÖÙ£e—g'Lr ãl0«ÛÃi—†‡¸Ã$UÖ"ÐWÁ0\UÚsh 7,ļüŠ›ÜW ¢·²ç$‚²Îu‘~Wž9 8ÏDÒÞT>Ï‹†±•´Ex£@Ñ6£>„”ái^<^àúŒøƒ â ¾ÛŽaü*ƒ•¸@4%|õ@ªŠˆ°C ¯\nöiâ\Ìoñâz‹Ñ?ÀF˜T·Q³PˆòNVÊ_t“’I±ulpå¨,þï@¿z/PÙfFÃx¨âú™‰Ÿâö¿›§,JPï ¯Ò%éõÄ­Ô·óÉ@¬Øª¸yÐ1eÓ4™uj)Ö ,™’±±Ï_örò1{ ©ò€§ $½#½‰JunÚË¥ö  Ú`IÀ5Csû‚pO³Æ žJ¢Žíó“ëv²Õê™C~_Wñ4‡¹ÔÙŽµÛ³¸¶”ÝTyší]}ˆ-ñ×*, mj‘Æ7è«Ndr–NfÚ825ðm¶‰äû¨ èA¹lÌuQd5¾¶L9o|°¿êbùÞ\‡ûA-OµQµ­4>È×­/ÑU)kRP6†÷…¹Ø™Cݹ+u ‡UÞm»2ÚmÇ„…p±Þpô¡ÕÝ­7uEyólÅy¸í¯÷Ë'ºqnƒºö,òdY Þ”Ó[èésŸÝw\a‚7D×à 4¢qR€þè 9§eØ«”Õ!‰«¬GÝE”õ;6¢ü:»‘EmO-@U8p÷|3cí¨JSå'°4ÒŸßÀîA@¹¦ /F_+JþQgŠ„äêÈUþKCdu\M¦í“ŸiЗ:TŒ¯¤>þ… øPòsîú®i¯•Ú}‘¾6ñÔB ¯s­2 ω/ ¾_éÂÃŒƒì2 /N*î!0SÝ Qâòòa ÈíöoØ´y¾J¹N·ïª­îuêkê¸ò¶p̉¯œ:Ý™çÖÀsåx<®IúÈÚ’uø”wêrÑ¿å¥Ù˜Â ›¥é¸[åW¼7Éîù|yÌÄK¾ÆÜ¯³Îq$F²`–;Ü-‘ÇW^#éö˜qCž×ŸDoøF´›¼ô%u¯?6S|ۯৄÞózÊ2<@èÆ ôÍ’Ò:3VÝW:œjÖ}ÆÁ¤Ìc›<‰Ktn“ZˆôÌÇ·Ã<¶Drúò³êK‰ÿg+Ðá­p|·Ã•G…‰|/C[U?¢š-½³t¯²Ëû¨¶ð=9 MÕ‘§@_¹P+@_½kì-=á:_êÆÛ„P˜$9SFizߥ>3§÷‘«˜jþ ?ó‘F_sG¸Ç¸Æ²ðy™?ßgôZ”$ˆ£¾R÷B\¶`ÿ”iìD¿éû_D¸çj€ÝÜxÅ%…מÁ¸ðmòìv% @üéôÆ®w#I…Õ&¤Œûÿ‰Ô%·”þ˜¦ Ê&ø'©ÎD"QðªÙýÉ¡Ó Ý §=[$V1·Åì:'’HÔåMCá—µAÅâ_Ðí¾MÔ7Éå}*”/*s•}sôþûïÛ÷ßo555Í9ÍkÿâšZ£ß÷ŸNc)ñÁd. u'Àt ¦@’4ËG-š58ÛÅMn2¡§ìZºÚ¬nù4äÚŠ¾I´ebhþI[þ ‘ÀÁ–~g¡°æÏœ¶Á)íË3ÄÐ ìMIÝ|ëˆé1úû4|É&étNPfá­9Ù.æÿMÀlî` I¨åLÛ˜¾Èé×4Ÿ  ¡û5þàHQ2ÌC=p{e™I]\_Óv“Ðõ(•Lj‘Ci(Eên[BCBÿ3©Ãý¸žÅ}˜&!‰<[ÂZ€Ÿ6 lo˜x÷÷˜äJ¯çP$ OÒ¡?>©íÎ_L}ÀÈéÔÀ˜;©IœçôÁ¾Ãv.MóŸA?¬SZD:p®¬v„ÝRÝ M¦á¶¡èÒ\ ×êwqzÂö ¤lm@ìø4™¦WÚx£mã›=à}_#ÝÇ-£©¤ýBßK/î]ÜóM W“$hý@ß®Ð:ŽÔàAá¡}^×ç“Uc³­jWå]ÃX†ÂÕËÖmÙö­}ÊÀEeHRfe€09¤ú¢™}Äy†Í«ßƒ‰ÞÞWáZœ+ Ê,~5á0 €Í>’7‡_^ÈœÀ©êh|´{ëEYï+0Ú„Á{/§;S¾;¯-çÌÝ’æÒà¬Cß~‹-j×ìHÝù˶Ѭâ9tžÏfAB}?y58·òË€P“ö1 }azz¨&ÚÚ(¡Îª9 @‡ôÅN"mÞ6™2¯ß•ç7ð°°ûNçWS­ŠWyT;È%ðØ‹"kq D„³r@—Õ^BÝTxï;k*1w®†k¸]á›y·Û‚0x„´ð†:.NúbGb2™KS%Q¥u-…ùºîÙ‰gTžÄ"Þö6-ÕÈÛ×v$`K;òs ×OÎsÒŸó’cš‰ZÂL}ì¼JŸÞ¥Ñ…‘Ö( ›J}g÷bJÙ ¤<´+`<Ò˜úÁN‰þaCêø6ʹ;ÊwH{E2Ü[tS¾‰ß éöë¹úöi’†ŒãM»Ö4©wãÕ‘öêeh뿺I¾x‰GŸàŸïîÉýO’p¿3¦vPbQ¿„óݸU·§îƒåŒš3&üÚ½qDZ$Þ@ú«õÍß6ç± }þ1¦Ï/h;Ë%N³ ¢uNýXÁ@š³P¾…¶ƒKÐâb¤§»D¶´÷)Ë6ˆcÁØIøëpÅŠ'ˆcq¿Nç&ð‹õC¢.\ðÅfèQ¡ã„›qX÷cázd’º«Å‹bŒµâ—þ;±§^£íïLܳÚ>þøu´Ûmn(ÄÃú56Y8mFQ#~ôo¢Ñj!j€G}@Ùÿ”ã€:—¼3øb«)à¡- @“¶¤Í}˘  »ÄaÕ:Ô×tPŸÐ8œ·]~+‹ŸÛÂïbÌ;"Òž¼Š±ß&š7a¯žæ¯â‚ÛàgƒHÒÝáþ)¯·ù7L=@vïA8ô>I8ëáТ*½—ÝÈ·lä§ý Z¹Æϲ˜úó çi§f@ù¿0°mὺ…îÿ§;ºñê;ÊC ,f ,ìÐZÈ>l¾X"UØý@ò/|ìŽÛ°ß–<Ï|.ä~áW‡ÎBîþióžÌ%þé8Ãñ)ïBùR èT¿˜Þ‚FAÖÀsYÇz[ŸÂ‚‰I+‚`ZR4Àk-I÷[@¯ÒQHÙ~@3!¿f³Ðd'°û'îÒ{'’ŽË\:’‚¶±¿Îö®€N¥#l .ò}×Ò99)¦ÖÜ–~¢C -êtõÝ•oSþ(öKµeŒ.¬·é€çûP ñ+š¤­Xo©6WHðú·ÝDÐù;`SÄÎdp$ UtJd}@“ìaG7¶>ßÜÙª‹a­f%@¯…íëÈFWf’½*ánEýÔA‚)_Ÿæ]€—E ŒÑiZôO9Û›3ñ¾2’vp'¤ëàƒ\z#~µ½Ê²€ò48)£»š t Ü}„¶¾ @Êx&žØ÷Ä]žtê¹ ‰ؾþcˆ?¸C/¢;¤CÒ–oév”¸ˆÛÏi;é5“ÁoFeaiYÊ–ígùƧ< &Àæ@€^¤¡YêÀ„\€ÔéÕ–,å‚þùãÊ|K~ûZŒ?Ëo•×´G^S'%ÿ/÷i™ kihM•‡… åÜ™ª7NäD0Ç©ˆé¤®SHôN´QåG`?šºµ?:h/q“ªWËŸbÑl™PZ{óÞéÕ2éCêú#¯²G„Ýx¢“,y\¼8¶!“À¥(ûˆ­8÷=Ò“Wp ôdÕ…Ô‡Œ÷¹A¼Ò8‰­e5õ@•À çÙT$ÃsÕ<ôIÎ ¶õ¾€tWÛ5wóm_ª=9šäd>DbÅ«ÛËøÞÄîSš I “CI™ä#¾íà=¨q$ðžÕúh {âzŒˆ£‚|rRlÏd‡;Ê–‹ŸnßÓÞoíðù07³2îÒÛêõLø%q+J¾À÷¾ËåSFÚ*O&ÏÎuV„ÝË\Þû6ºb¬í\ù~¦ìÊï"œœÛ}áCîDoIñ%óüûÿË35šÎјrÆ6·6yò–mĪoêS»ŸÖìÑ_€2qíÅw@+m¢È¦øcRé÷%:Ð¥wâç¼*wħ<5kÆ8ç]šBîLù¿¯q ;áTÜÏS÷îòÊËHû}þ»n©i?³£©_{òý½ßÁ;6Ò£¯k°š2–˜iUÄ{<à_yjm‹¤6µð+âûØ‹ø^¼â^{µá^W/ÊÑ{Êd>‚k­ ß™’ºàûI{àHðõ‘´8ÒÏUòí”%Ày@š„Jm¼ 6æqÒì/ù©ÿÙ]‡Öö„úÒnä«=<`¥šMs}Å¢Ç[€lÕß)¾v”«öç¨^”=ÀWG¹.Ç—¥õ80± 4q µ«Ïëϲ˨×XÂñ~·ØÖ6%=ºGáÿmëÏ!ˆ§°­¸\‰r½ÀJî)óG]¬Æ+‰ÒÅobɸËó¤ºÒ@½LŽÁß@I®Â‘r­#ïÔgéü.DR%Ã.‘v­]¥ü+#ÍÚŽ¬Üþߦÿir.v(Ôl.oŽfò íÐJ#m3Î9$N}ãP?‡š$‡·Çô·àon!©l'ǺÃ%Âe Ï?¡[sI¤¯l~¬µ´;õ*uÀŒ“£öü@>¯Í¢…ëg<ã’þËJq•š`êGRwëÉÿÛ¤fÄz÷;‘6¦4¢U°XÄ•õ›ÁøÂwª2¼‚Å’`|AcU]ÿ*%á5é-ç7%”1<Š7¿ñŸ vŒqe©©Øþ½ áRôS+ó­pÄ©å—HNåÔG%:ž_g´_· Õúp¤¿ü*êrÀ6GÐ&Ââ…FëÚÙ²ýÉ­Œ·¤«^¤‘ö,ê·v¬;™;ÃB¬æ ù(Wvµøy @ueòXÂ]ƒB|P ö»¶²Ži^£ïhJ·œ®¦x‘/•~äð˜ÞË–‡÷>þ… ‹/‰ÄªÃê!òyÒVQ‘$-5¡X‘ Ü5wX®ÿåK3×Ï‚ôþ}N'¹ ¥­”´LœÈ¶I*EÃõŸ~úÉ?üp«¯ÏÏðD]];Öwùå—ÛÓ4ÉÉÐäÉ“mÔ¨QÎþñÇ5ñÍPØï—_åüŠéÎoþ~Y¬|*^É$ ÏÓmyÀÏÀÙ¦0m òÚšÖJδ}l;€¡­'¼tÁiŸù‚ßÝrBR&—Nôu¾E˜¬ô,=IVëŽiI˜.qasMhf2Á™ì8N–MÁÕÏ(Úž˜°ÔJ¢,¯ÄPÛ§¤%!^ÍÀfcANg_¶Ïá”o¾|ØÕ– Rò}W·°£>;ÉÜ<~nbx$¤î¥Ø_5@^Û‘À € Õ’©òYòc#¾õQ¼ø9¶é»ç†}±û×6ƒ[ ~æµ…Nª×±»êNµíì@ß%ÇØôú˜$S²ñ]ì‡Ä‡ö‰$mëÇö çÆûƒ·JW£¤yÊÎtFÿu€áƒn²ç;F2}W;KuR@qtÀÅ<Ú†JÀžx3œxº3ÓŸ$¶0ã¼è2¶UtEg'Ir²)r)`P>š7FÊ÷µÇé+¢Í„)[Çÿô·T~hFûŠn†)÷ÈZ\«:§:pñ$¿ßpÉ»œd¡Óg¿Ä º™À¶ÊfsÔ D» ÓL|Næ&1@¦Ì–KíÈdŒñC²ŠëFú/º)éØˆ .è°9󱃹†yNjv ~ìS³¼÷ål‹{ϰ%´ÌÆ«\âK2¶~ÏOzöÁT“ónnz%àCô-=Ü»€“‡$Æ{Ñ5Ý}±š ˜4ÆÉ…¡ÞÈÈËTÛLaö·7oj}ÔõøPÊï#ÕÛÍÜâ¦áE`^}"­Ñãp5ïŸXOã;ˆo”d–¤7Cwù¯é|[ˆuÈmM•w¾¹¦/÷*½8øõ¶­l£šÁ“?ó.Ré î¥Ô½1’ D¢·Ny%Ioô '“_Ù6{³ç¼µìB{ÎN%ß³øñXäU›ŸùxmÇÔÒNׯTì¬Z¶“=ÈP@1|ÒÊï¦Ì§WG;H½”D”G ¿,¸Ck³0¶±ã§µnëëlêзvÔAF/»DÖ¶›¢Ô`ïõ{Úê±M¸v&ÏväZ–‰ýBvJ(,ñ<î“, þå&áø=f)v,ëô“f÷2X‰®ìÒp¹}×ñôq÷`‡F¶ ~oÀïöðM—ì¿69—²X‰b§NèB¾Ú-JðïêeåØþ#Ó-\R›¤ ¹9Šn÷®ú)²õò?’pà_ÉOˆk0áhü‘MG“oûp°c–ªµó†{ðwoÚñLöUû›R†£4µk¹‰tßv.îÎŽ ñ,ß¹ù÷~¶y oÚé¶ZâW–Âü…šÔð ¾Û³½¹«Û-$ø$vÈlœ@^oÇýcœôjâì:5.Žtræê¿'^°T-åoÙï×yÍÑP@¡g}Þ\Üítû+õêfÛknnÓ<:ŒôÁ‹QBmð†Â.TwµÀ‡*0ë'ÌÀeyý›F±­i[ø)èÌwÀs»¦)‚oXübÌɳB$µVôÝžüg!Gói®]R “æÂÓÎ €þ,ŠÒbeÍÏËeô#ã¶m«o}Šj½B“P"Ëñí[b\ÞÄ*¿tÆ\×õåªË›2îW»Ï%-„o‡@ƒ8xXݪÔÞpª‡[ˆÝ+®Ÿ\¡÷'ØI¨u¢ \?Vi—±«GútïC½›„\DW ™œii;áw¸ÏoJóṚÃí0ÆúÅj4 æyÍs­c—.ÒÙ³gÛôéÓ›¿AFj†{^‘I`“(õ5é×ÜcúÃï–”]àÞ þ!¹÷P¤a%Ó˜ÀN¶'’vÚÚ„d90ùzsíÖö…©]›m쀮€ÙŠIï/ØÎB—é¬r¥kõl·õÛðþ©3Ënk«X;¢yéKLÃ鼎<ÃØ©0¤Œ é)¿¦ mÆ"ToO ÏïÌX5÷TËK'Ú7NÀ¥ƒ:y'ÇYd°Šñð†%¹ Ôãè! ô{¹Š @IDAT váh¾°¯e“v£sò y³`å¡fʶ³Ïlž&Þ¥Õ¯Ä¯Ê feÒòÉ:ÐM¦ òd±Ë!Ñ~y¤Æ/sÁCW|êY%Ç%¯åO ®a·B„Û÷ÎH­>Ër-¼H’|“jèHzõHÒ*Ýø.àÕG”Ùižÿ¯Ó±E’’99º=iÌLP¶ÉQé²`ÀiIr¹þ‹K­§÷³dà@¦Å‘0ýÙ(êúQ›‘'ÜWrN0Û3êLãÑ6-P14¿·¸;Ž„™ý71 éêÛݳû‹îEº7ÀÝL^+é_¦ØQ‘¯HŸ_¿Èc‹…u©~¤kxÒf×õïösÜé>Ý!žøÁZ§ÞËh½Œôl¶]dykßîÌ~£¨%ÝL+Þ «>¶'b›e€¯è¾Ô[åCˆˆOH~Äan•ôé?¡£6ݨqʶ }þÊZŽmgÛÇR~ƒHÇîöEí@[ë¯5©mgߤŽ_O:ç’WºtýEÀÝëéyî”UÙÍØ÷r‡Û§4fy¡vÛƒ ô0“J¤€®l8Ú.N¤âç âü‚*$­ªøŒ1·x101ÃkKHÀ¨¼ÓNNÌÎ;†Bù=ùšt`ëPgKÖÚ¡ÑŽ¶ÜÜÎä_g$¹—±ðßÛȃ£”ÎH/Òÿu¾AªH•›[œ¡þÇ×Ǿ#i§ýR“Iäý…Ü{p è¤ýÂßèw ÿ×a®#é (~#ï{;u#?ÅÏ&/Î lˆ²váø–à}kÏ>N;Ž͸mÑÓ"¾k¯žJ4ÈÄ÷£‡‘·:;é¾×îo°,ùqæ]KŒåÊ`y!^ÒrÊZD•÷²+ÈÃçÉѤi†Û-´%5ùF¶p§)ºŸÆÀ„ú=^¡…ºy´3« ,Ò÷»†ƒWô2 Ñ«¾bŸX„8¨æaæ#(l9oÝŽ¤ïñ,¯ÁíC]~€þ§³;@‘ü,Dõ´ý¸þ‚ŸÝé{^r@s¡ÚÐ\Ò°A) ³È⬋µñjÊõ?Ý(gü §)Å1jÇkj˜¤Þ¥nÇåþw‘ò¹~/Boly )øžÛ5Òr¯ù|¨]þ_š/Ê‚f”œ½ÌâpAIXïöÆUvÿ¦ó ¾Cº_gg¢;˜wžŽ4ïH@X-,†¥oÃ~þäeoêºÚp.u#üÁðrÝ3jr2®æ±O$ƨºÁþ≦Á7G k4‹„êY59¾¼;õP‹æ—”À2±˜ÐÂO@Û Ís6Mï’<öôÓ¸]*Ùn®‚Whg¯H­e¾éÓO?µ\éÂEYÄÖZk-[rÉ%ÓáÏœ9Ó§;z‘§-BåIW÷¬ký.p'¾à+ÒþÃ*H¼ÜÌ5ãðüÚ­0U*[AT÷Û^dSÀ€˜p“3…÷ƒ¸Ýš xm@€‰úkø®I¸\kÑ·I¼ßR}>á<É«¼€I4¹y(kFTÑåù´4Œ&n&ýÈk[L&Oåm6¸¦Í›SŽßËñ‘°ÁðûÍiŸƒ¦V£Î¹­tØÊ"×w¡ê@s“]É韘“ŽÄ›Òø§ýÁDçÝa‰RãݵeõƒNÝ›<Éð]§6!‚Îþœ €FÑÓ Üè—¬¶-Øÿ»I•Îníp™Y¬7sÑ¥]t¹WP÷çÑþ^ŽÆmt¢Ì ÕYokp¾>‰üO\–*E¼[Å•¶?‹{;''ÒÖ)[Ã%Ék]û90õîå72¡gB^Ý À¦ÑþŠ9õ+q>~îâ° òÀ™ñÖ'õŠ;”ìÚuš\»…sĶ&ï÷ÃÝ=XÍâZˆ$ åþ-—G+Qfž@BÒWÃTCÜ÷!A8Î~n<Âñ)9|Í89M ×Roú“Ž'\:­œ{ý™Ô õxÄ»¿uDwñHÂ.Ep½HýÓljìP[¿ì`Ü-†;¾©þ>žœ`»Gû kv4ûGm1ñ(ú|×µiµ7Z7T= Ⱦݪì5¶ÛnÎQ>Ÿ»°~^ yWñ4õ™|Œ­™v:²¾£]Õî]Û¼†˜$YZM±ðaqòªñq–¼‡};Œ:rt|oëÝpñ÷ÅÍÒögòV»Ÿ {GÛÛJc„¶ãÔ œkå1Þ¾nxsŸs#ýû¢­B>½MèGdªð}obö¯'áÀ/Luð6Õ5‹ÚM‰y¨ãÈPw/¦×8¢áy·Àð”³êÉ?å;œïz•ðÆ\Ñæ% Œ>Òj´4ªÎ÷÷íâS¨Ä0¤ oæ0ÆË2^¡lX4êÅä¾®QõÏ'FÜŽN$ÏâñßR·F®ýwÊ©^(Ió,"?]ÍÂ^‹QaÉÿ´»yÔÃ눋:•œDZøÆ úI ñΣj—£ðXðè/p¦ƒAm’•D={ÑËs§|QÊ›p/Âß] •S_¢K¼¼aéî@ùÍ÷]L8÷æ !Ù®PÚò¸Ê2’îä¶¢†c‰zœS)=•T»PSù*ß“|Gm+__á©ÓÈrz9!5›ÚM¸ÍZF šVçT¤hñ²mF}]Þ*¢#Y¤Ù…²Ø‘Å‘ôA“°W®§ÙÅ­smªê4ê„ÚžszcÝâFÌMáx~ YQVuð­Pÿ”¶L½G½äj–š/¯fƒhÖAnûoÖƒç õl3yQb8  3qÍXiÚŠ'VzÍWb<]ì\˜û¿è¬ƒ‰ìô‹ªÂ 9õCgê¼M[ñ{¨¼jœ¶1‹X{1v: àv潤êL5zÐGÖâ3¬C»û€…Ñ>´Íö˜Oæ]ãýÏ ¤å;Ú«jZkj›ütãÒá¥Ý褊â!Ú;=DQZ†´ÿ!þ+!vZâì,÷G1Ò¿"zò–Qf çï믿v ï /lk®¹¦m¶Ùf¶úê«Ûï¿ÿn¯¼òŠ“–Ké.}î¹çlÒ$:µ"T_Äîß´’tc) ¯Ò(wrß’oiWàãZÐÁ K™šjèQz5\@_}PKAß‚™°€ZÌlÉ@©™ohic–z†aÆÙ9çœãB^¦n°…9ùÛoqûÖu÷ŠŠ [wÝuM >¢ªª*Ûx“Mܳþ àIý?•9W•kåfûýµÈw ì/•4ÊúŽe"#XC§Š©Ô,5ŽRÝI×¢@_ÑætÚZQ*=Ȥç;—'=‹xa"« g}ÇJj‹I’Y>?`:‘¼Óá&¢Mýò8V ¯èII×åÐé¸óÍ­¯&˜º•ž_ÞKtn 41qŠ^0…'8æ’¦%{9(¾3ø¥¹Öyß5°Ù‹É@úvÍë*a yÕ’…$ob'1J8$çÒŒ´ò^0͑ʘ©V¶Ž´þR¶=‰4)*ÖëiJètW `ŒÍ"¤'¢OTºQEÖb¢¶Y–µ¤t b•$ÏcCí¿,ž¬ŽN/æ,§9/y8uìdÂß8䮸t.ÅMOÅG†ÜsDÀÿ"EÚЗl#]³ñ&ïLäKHeVàîE˜H#¿|˜üfsgYâ“ÊºÝøf&æ>Ÿ)ÑcÆYê¾å*êçîÇfÌ“_ð<-óž~êÍü«‘:]¿+é~.m“ÿa€eWáƒ1é·¤“©Y·À離P]ðÛ‹G>þ£.Ô ®“]q3¹øâú¯6 ¢¸Ÿ|ÔVýOüÝ×’—çGöâ[ÎÆ6Ì ¿´ßQ’@¶+ hõWÅ¥¨QÙÉÅYV‡”ßø²Ó­+…1,>¸Ý&Ña8/#Ÿ©WÕÀÄI&ÆNÆÎ˜ûaE°iÄù{òÛ?q¯-)sàsO³÷ˆú@ &:O× ±£¤þAú’ªÍ"ú¨²»ØR­¥Ÿrßfòë4¯,¶Tª¢µþ–¸¬ºÑw³ þò øà•ú°ÇзÃïH`Ö2ÀU\„…*ã`<ôå¦6%i`õ™Ñ!.<éº{ÞïÃv&«íÉvJê  Í2·•³*ÚmÜðƒÈò¸ ~mÇW’ŽÛx¡î—QÎN×-唜L¼Û"ý×`gG»Øbñ©¸Áî@ÅÂ.‰°Û:qe°^¯D¯í«özZxì•]aÓÊ©—Ô~¤ãˆh¥=]{ý'ù‹‹^Á©ÜÁÅ,n erÊŒ<<—º:š°¾"ÒrÚ£8ßγF´gI#wøœ¸¨'ÎD­k÷œû'·ØZ0^ov[c3:Êk‘îžçëx¤^hQ+‹ðî#êÇP¶”kÕd½95 ¨óè‹&¡”Ïé„ñdÚMF5uY‡ÎIï§t37RwJ=ÀÓt¼ã>Õ 5öh¤{`›}—thý.žYêâ¢Í‹ïð].¯²]ûoðvÀß1®?†:»é¸Ús#°†¶äôm¿ê$Ût(R†¦úm^=ê¿AÔÑœºãà“vcÓõÂ.韊§/| ³ç²|„¶eô!ª‚WeZgÈ¢É#ãÀÜ=Ñíóè&.s 8 ÞíÓ{è½éB•¦¤tG .ƒ¶¸Çw#Ï6l‹š†‘zêx¹gîöÆd»‰àñílÓм÷ÿÈí¬”Š½àü‰ÿ YÒRÐWߤ9´'¬ØÇúÅN`ŒUÞäS¤мûIúíN vË¡¤wïeת¤ÿs)*uh‘mÓÆpI[2·38PôÏ´©÷ >bCÀà@Í–LµÐ¯]SÏ0~»‚ÅwÍ»4º+GpF»Ed'UùH£Áb¤^äKüÇN=K}–Æ6Ú©bœý`‰››/~5Ó7¥'jI]˜¾ûî;÷ºÕV[9àw™e–q’¾ýúõsæÍm7‡µ ?7­:m—âÜáBÛ…Ìx‹À4€Ê.¹¶ŒaþÃà›`þ-}Â’¸i)…5kË2iiyä‘¶ÿþû[Y?(}°ÿA 0 I$ÿðóÏÖµkW‹F3ìBýZÜIÕƒT@ˆtuwìDŸþ6ÃÞøùǼ~ƒÐŸ@æ }ÍÔ@u>ig¤OK¡d ¶µ O’›gH""Dó aŠ!«¼˜¼kuÓâ§æµ÷ 鎂A}ÈÕr¬¤†I[ÿÛ‚îð%3ƒrQ˜W²-Xƒ+Ã[èüÈN¥“Ë:¼ªÄDH/ñvtèÒ'ukqo)=\¸Xr˜ÚÆî¾=™ß漩ŒOq[tK‡Ó耙Ä |:j5¬éä¾ø¾|fKT‡IêТƒÙjÛ¡Y5#ÚBtUÖ!AÙa¦ß—º rúÝýÛ%è^žNžèz œâ7b°\Ú0Е6˜Ïw0\ ah>K*aÒýPò:Î Y}ØænKøÂY!ª®?ÃÈóL®×¯À^dr-É„}©ËšÐŠnŽ{êP.± Û¬W¶©ÀÊ@&Û¦ÀÛ,ÛÁW¡ö(ýÒ1œ1 xsÆßæX2±m÷eŽYðªþ­×Ê€GãàO—ávr`ÉýSêÇ]¡÷–<ÎÂïMH’©uãbè\!ð£äTÜÊàæØŒ§v£‚|žer ¸¥)„U>˜qSÒÓ”ÿFò„]]YЇ&3èª> aî> ¹eà_»ï¿ÙRšèæÈgIOå^ðþ#˜à;  PÎo'Î'ÓaHUÉ™Š5€ƒýø–•¢#ì¯Më³ï“ozîRï“—?°U0â&?:üÏIb;À9E¿\n›Uз´š2Ü ?=ìExøªRï@Ï#R¥ ‹÷€±³ {?çÇb»§A‚z¸ÿ ÷yæ¹ÿq•A=€äŠÞÝÙGø÷ÆÎΙéOàc©í&z§ñ<×å“×ZŒJÅ:ÚÁ‘ˆm‰Z/MÕê÷¤ }€;Ÿ´^Ûgãk“7¯¦®ï0ËnˆY1ó.¨ý˜eš@މîfKF×@õEê9u=kQâÊf„NÍ¡ ¬ñ›z°¯Äìk{‰£}Qí2µñI{]Çê0µÔ[ôðQ'ãÿ…çl‚(†7bê·¬äMÖê¶ÇíS¶<“Ï+“slÇÄðõ¹øó&ÀÀoi!¸§N#¼Ðn«ÛÓv”mk†~I½ÄóXÒº·{×ßôÔÖ»ñ,›Q»¥=T¹]Xþ¼=ÝÚN¯§L#;Ú‰•ð<¤žÞÔºžÈ;ˆÎPßÐ@|ä³£¥p»’Q³ì#IîI×´xzôpl‡[L²‘¯·1å‡8}¿êo¶m9míXO‰yô³mU³@{•inù³oÜfó ƒ>g Š(É娑zœÊ×SåsWЬººÚ:tèàt{†õìÙÓtI×­ÜHúWôÛÆ¥#4 Ç/¿ü²=úè£6nÜ8'=Ø%“Ig7eÊûüóÏí‰'ž°Y³f9kÝ_{í5·Ýüù矷Ï>ûŒÃ ì†H’ˆ²»ë®»ìÉ'Ÿ´?Fcºˆ‹Q¦hšº*ûÁ$ö7%p¾BÎÕ%,VŸ˜ÛBŸpk¢Tg´Äïß%ÅüMs §i%7Ý›4}În=Ò‰™b2È©—óæ5ißj×:´-•J9é^½‹$É›òä¿°üª] nÝ‹ÿWó jT]Øâo~޶`[{8)]B/’ 9?g°")ãÚ›ÜÇïs¤tÅè5fOã–܃| ûY8üRâs°B«Ó£Ò¶~™çêÝ0¦J^I¶°y¾g¦¹ÆÏ„ÓýÜ™ i¾Zh–¸ÚŸðLep~Œó¬2¾ØIÍ4ƒ¾²ûBŽ2ƒö{x²Êó´`·6Ji=î𛦂|¿˜rÑ tŒ ¢¤@ðÓÔ¹3´ù. g ,j ¥_õ{#&ýÍ‘¦J¦r9 †W ÿj¦ép^¯RþTuKO^› ¹mìsu O£†VFÊPß:Üm)~"+Rà?Ÿ¥Ç÷K¶(×;{mÍbúbU±Á X%r©vcÙf›ºÉiŽY¶ ÷´ãHbßÇÎ,Ó:s=CÚ\sÒí©ÛéAáÏL7Ïuä½ ¨‰ŸÌóW€G[ÓIO`»'¿Û´©Æy¾;mŸóPó ˜¤K¡Ž ƒÈIb¬à=û—®ïôÐÝû¢ÃçÁ«Wô|%iˉ‘< »ŽgæþÛѦ¾æÉë·‹ìI ¦RÃPçñ€Àî‡ ¨…ndà¯í·óè÷`²0œÁ¼€‘s5YqäñE-ʼÒ‹îY ³¾ÒOb¾12eÔ•õ#kÐ:?"T<0­¨ª|ÝNM–ÙÉ ·ÛNɱDÕÙMBÞp L• Š ã{N²!±£üwnÃùûÕ½÷/ÜNHzõÅî$;É–/µ•)̘|N:ÎØ›á|äûS½yPàS.Ÿw$ž"ªÏ>!9Û„œÄéxéX©e(›8'xñïLæÊîdâ¸>×ê˜M$ÿ¯#ŽÃ­G‡y‡¿ŠNÞŸ»±½Yÿ´í6o=“˦ĨR*Hæíb¥UNŽêOµÑuœ2¥ªÛ£ÏÒ^ë"K!‘‹Î@퀨9’¼¡Nº)m©ìAž»yn¥Þ°.L+PrR,õ0yˆoÇy¦¤:|Ãwœkµí¾±m™&þæ\¨~hôKôù‡€'õ‡«P¦S>VM’º£ÞL`•C­ê|z>RAž+|Ô`<;Ò>ˆå”d¨+ú“«§Æ=©ïíâºWì\òÐ8ÌÌ⣹k«×¶2TuŒ¬nµÌ"q’ ÒóRüJ¾áêåß›çóðC8•ÇûzäÓåö–úòê6ÉQé‰~žñΪÚsQ: é¥=žtÜÞLߺ Û}we7…Ê¯ÈÆH?×{áè_[ZÕ7Å/öÍ*hëðªÔ‹7é§Åxê~+øE Ä.DËûÔrlF½Ì÷_@Z¶ÈñDþ4ÞMz>È2W‰/O™j‹ðòHÚ{s¢„·@x{Ø[؆Á_,¶~á¿ c{›Œ>àgjøVú&‹ìâ…§|£~¼Š?B9¨­8µ4„¡Ãâ*"¯¨‹©*ÏOúÿGª©ª›¡%àm‹RÏG§~°Mh÷Ó]ÛÀSÙ%¤óàf|ãÜ×ÛæØ5ûÚŽpOâ:>íRsN8À¿L]‰¿Õ€~´‚ösÛ³U½çÐÿ€ª·BCŽ'=­xr~:†Nàyë0FW‹¼ nÔªœ–^üþé1¬ç?É­+Üfª²ÞŒ ´Ø´1a͘hwÞk˜³ˆ'@ÿ»âDºÏ¤©Áߟôg±Ìîî¥bár@cÿëëÌ]‹å¡–Ñâ}¼Éí,óüÝA^ÜCšÞÐn—|šzG=5Þl7¡oö'§79¨+)[ðù+òCRß™ôy6%Z°¤ÏPÉ Y‹üLêõ<üŽ;Ãö–¦ÌÖ²¯³Œ3âìNÀÿÜÏâÚ€O¡^–Ó¦+öçYÄÜ*Z[U+Ÿ †"‹{•e²¾~™ôk1B;@uñŒä[OŠÐ¦’_b¯6Õ×…:€¤NiHÙ¦”É\ `‘j±êÏõ)ê£$’EÑCñÏ»¯¶ä9òì—ÄeÎj!÷ïÿ9Ž»ˆëTûp0’Ú“És["AÙX”šŠËlHãíÀ7ÚÖ >BzS#»Ùo•wÙRhQ•?µ¤ö|¾ƒF•ăԑ~Ôó,6.R{‰-ÚÝ 1ÐW¸gò¨ qîÂãÂzƒžžS€èm˳µUcÄû`¶%n%ÞKqß™ì‡9¼7Eãï\Ï&ÒïÙ7Ÿ«f̨Wp¤ UñÄñíÔé+Ï(„ÉÃéï|×3‹ì„?®IÖÇ‹(J¼¦ìlëÛ>ôO[ÊI†b]«aOþ;ýôÇ’â/kP~â…Û·›ò¬“ðîj³µÿ¥®Oô¶­¥3B»]€ªJfÎ]Ž`4ò€NÂì#â=Ä{Ïú§ýÛÒ\¿QFÇs÷ýd¹É¼èPá}XHxËŸ/dlþá§Ô›dá;äy[½ð¸öNŸæ«)šú‹ïå;©âN™µ ñÇ{óÔŒ#h)­§Èò´#Úz¨ïU`´\[ ð¶:»³iqò­{Ȩ|èdÞÕWÿ/ ü®¯ïF“E{ ¿È%m­ž™£Ô,×Y“w¤ORç ú¿&®þ9©… @ü2úšØÑ¤‹òn¸›4üâ¥£á øÁíMÒ4𾡍/p$a‡ZúÅè´•+›¸•ÁlúÛ +¯a1sYÆ2È9w¬¨Íe÷‡r „-ÏÁ3„wÛy*5 6Š>i;xo£Ï^/²±íÛ qÕ,M& ‰Ø±¤óST«Ì#ðÇI÷ËÀ¾ƒIÃ7˜‰ÄÅ—Àà½à »%cö áä¥äËÔ ¾×'íô¢gý[I;ŽN,0góFhó½t} È•$¯‹º÷Þ{”®@Ýàp©víÚÙúë¯ïbX$?’˜+àvçw¶ 7ÜжÙf÷ž+½+@XºƒwÙe“.áÏyE»ï¾»õïßß¶ÜrKëÓ§‚ûí7'Õ+ý£¥¥‚¢oß¾„^qÅíÇt[Ó]ó_1 ÌÖD-M”hê:ø¦¡Î¦‚}åq«rÜ(´pX}™Ô´†Šékmixíð ƒåþ7Qœ)̇¿S ¦­êŒâuFaԦמ- ´çjŽzôèá¤zµàVdδϖâÇÈ”ªÀʤ÷»š·.K-iËãfÞܹn±&é»ßRIà´ ›Òö‘¶HB«-¨˜*‰¥}PWñ\ã=$½=²iÌêœ^ž¦V­6Ñ‘\ÚO9yèÉô¤>eŽÑt~¯åðj&l¢ùÑ¡| ƒ¿µç³Œs’ú/¿2!ËCšÐ´†ºâiM9¹$Õ ÃCu«³òÄq"€}˜nJ>V‘a«£/(g]ibÕýõøé׿>Ãï‹.N‹Oè “ôqI2y°_7¥£ø—†!8ñaÇ<ß[Ì …)ëW¨¿Òã5»¶o¥)ÊdP|TzÕ{%ñLÚ‰ZÉBH’5¤M˜„ÄÎH¿‰k x D»ÔMò‡Ø×Háé@¤bÔ®e ÷s‚Ì_Ìž)²€¤áñC´åÅÄUúÐße°z¤ûY?tr¶ÔG¤˜PÝŽ`‹ -lÊ6þÅí07Q[…9ºÚNÞ$O›>V"M ÉéX]Œ©ÑÙõ‡ó¯“W"Û äÓÜ¿Sáùú4ÞÈdêO&ƒ×á€QÑ!ш}¯Ó¸fÑ\yßûØÎÀÆ›¤Mf¤38‡ ¤pú<‘ |?q‹½¢…ŠòÝ4mä‡ÀÎæmÍÄû¾Aã•"ü½îÞ?á¹1‡¹V~`s+ž³[àQOù‹2n¤Ñp1Ix˜²~ ·ªo¤ •–|ËÞ„—=L{6Ib'¿$¨ùæKÌ:ζ³ÊN¡–ôÐ_¡|€xŽÆG±³ñS^Ä½È I‘&¿%ßGÈEšà>öeª-ªŽ¤¶FÒëÉç¸àqø,ek ÙX)ŸÐ}Ý­þ{-ÚèÊê…Ä£¨az[~¤-ÝÚVNÔT„NåãÛ“Î8ߘq0ªP&$/±e]{ïm£ÓºW‡9¶tô({2)?Z éˆ Šr—”QÂxräw÷¬?6K̰¿Rí-IÿÈH?@=ÊÓß wXÝŽv³D Ђ;¢Ï`áÆ©‘z}k>J^Ã7ûyY÷}³]Ñvµ!¥Ùç«nÂ"¼ÓcÔÆH…†ÀÁz€ ñS·(vwò·Ez“V®BÔp}’x µÜCZ<õIœe©Èãèø Æ”{ml~â;È·²ãœ;“òÄ<“'NWðÏÜWéâ¿´ÁxYþ2vPÅXHhB×+¨ ­Þ²”$F²P> ÞżT‹RýÓ`™¸”jr´ã–»õ§e±æu}@q¼„´(Y*/ jE©~äîXÆSZ°rººµ(G9‹f:T7Ø-(?ŽÜÎò$ {ÜŽŽŽIÑ»x~¦7+ê´í,£CiS%ÖÛÔgðkˆ{:×,®Ÿ¹r©ƒI¹†Í¿§èêo†×ÞÊu³îb|Ú”zaTJý Ï+D±Óàâ+ê9]ï¡obüé¾79†{6I}µ%Mª=¹ðQå‡"{‡’ÆÓ­Ð­ÿêZöKühÒoý¾ºöÁ½2x(á~ãºíý9rD=úmô9ÛD6e„m¶+cëé›?"®w¹„Fql«íVv{±–!ý˜dhúµ~,€O.?‹<ùœšÀw¾œñTâ“zIáÁNµlÝê%”ã,z?ÙÒš^ŽOÿu¡…rÀm Ý+é?Ié Ô}衇lÆ ¾šR`Þ½{wKJP`°t… èÕáhuëÖͤ;X –ÜÌž9ÓN’.–?]E wöìÙN Y s<ž©L Cè&u/ ÀŸ€8xVRjòMîKò˜ãHÉ0h;14Îqú½jËÿ‚r°\[}´ØD!`>7ŽdŽ$€KQÓã  Bì*?µ#uiËÂoþ R.ú‡¥‚eôèÑðÕ¢ÚÒùçŸoßÿ½ÝrË-öå—_Ú;ìÀä.j;³ så“ÙWO>ë̯¿þzÛc“Íœ”ýºøí†ßÓrüæOYSÓ9¤/nj[š‰ô‹´eû0˜è?Iß„ò77^q&mçû7i³0€ÔLBÒ’j!wÎz„Þ[úØK!ݦ•Ú•*ß#úNÿH$QØš‰ãB‡ä†#©ª€v‚whÛù,¶K…éÚ€8¹¤íè7ùõQ rÖðF:#ÝÖqÏ—&)~™þJÊ1 馇äXA)ÐÀaûõ;³œqØÉqѳ™ööeåþ7à¡L|Ygm ÀÊi°˜A¦›¤‡C?“+šÔy4àD ²HºÂ…ÄE7¤ŠIÈý+Ñ„­Ž[I³"H±›¤8ŠÐë¸ÑÁZ“‚ghŽfò—Ñå+iàÏ­!²,ƒÎò†4ƒ’ÖñÈIŽkrⓤI¢ÇoÜK fIv}O¾j¦œöœúœ¼ûÚw@îìO"Â~Zò¼J‰ŽÅ–H»ÕDä³Áö¿93•ïTvhˆ™t|2€Ÿxù(c¡-©Sƒ¸æø¾§av4[ꉟáLK~èI¾C½Û}dÆש;ߢÉG™ÈìÍu.eÖIŸ¤kî,v¡ÝÑ~Ò|ÝÙ^µP»²*5M‡jm’ºË^íôµý·ÃCöAÙ–¶žtæÆ/od?¡^ÀKµXtütµo.´=™l @«-Œ"-ªh¹StØæMñ#ìÚØñ¨o’ŒVÄî÷ÁÉ`ü·_‚)Yü&'¬¶µ¤ q'×Ó|ÃFNòTGXý(æ47¸ðõ§IÜ^Âö;ÈiÀ¨­é•÷²s£ž|æMd>R±ûæò@bû_¹4 û“Ë‘Ms'(ߣCì´ˆu]Ðg xtúb}÷ᛤ#çÝ!~Õý˜^α!Þ(§jSëà”a÷.Ò¾Ïir&]ÎZ¸ D,~-üð1º1@ï½\[a÷,aî/<…L^w~9D¶q¼]_w¦]üËž˜ ÍFJ)ý0Q“vµ©€'kL2€]LÛĶÀþ#®©ØMõ¶fÃÕnù«=ê©§Šà'ç!Õ9W“eÊÅ¢;aö• ̮䜨kÚ?ƒk]T#úy¥þb;Û^Ñ~6 áÊ÷4ü?h;“¯P·Ÿ¤þ5òíZÞiŠ-Ö‘ê8ü¯þn›ÔaiœÂülc›WM{o¤nÂ/¶‹ïÊd¹Á¾—¤? oò½ß£¯øäh­}€Š1Iư‰¹v·¸ @Ç!íz ‹yéÖ¢è¾Ñ ì›òmŽÞ¶{çõ±äÎ ó·ƒß(-èCÖÑo•sS%0á.¯j5¬CÆŽü¹¥Ú»;¨10ç.É5•E@ñ ȳsx£^”ŸJ™S¯ýäß Ý¼ïÊkË"ˆ§:¯-†|_ŠºÀ¼ãdÁB`ÚÛì.ÈÞ½§ï•ôyQO=û§‰údqêuÙÕä ý£“èÍI„ò¹nXŽá|¼ÂÇ,:¼•P.í_ÃoŸù‡úéÙuÐ^ÝñËXêÎ%]£ù©•$1·;CýNkH‹eÚ ‘Ê ‡§mê°Õ¼¹é°ŸÖ<¿Bý{ÖçoéO› h $+5%’ u iñ”(O¥o=ƒ¶^È*è%šc_p‹bâÕ“Òß“‡UÜkÑ?-*¡¶¢4ú•6ûPiN[쪊°)ÙWÓƒš+éWî#ö.ã•ìE¯çÉ·Èq𥫸®õ,µðÈÈ; ¸ŸÅèƒrûü\ǨÒá¹ÚïœK1ê£IögŒ3ÁË_E?;5åñ¾¡Ì§jX°Î·³67<½bœ$ÝøªWo0ösíרy¸s®±½z×iMÃS1'Ħø7NüDp­MÈ™«®…PÎ[ôÙ¿$ÇÀă½t¹ÀÚàoqÂäÏ·â~'yè0nmðÑÙ¶1·ß5 N ‘ÀUéó@$0H¼Rñ úðC%yH`­HÒ½ÒÝ\“&Mræ ™Ì Ž’…¶œkX%p9ðó$þ¥jBT__ïÀ*=k›y˜:vô&5$rØîßz›šëW´RÒ év²‡%ºQü‹IÛ\¹q—²â°ö¼PJXš¹P;me§\(L™·®›oâd³“Ü€¶©]Ødab¬À 8l®ŠÉ^@]rR#5|ý¡qZ*ø‡h`Â4`WÀ¯Ú›Tµœs§?#É»çž{ÚÃ?lÇ{¬­²Ê*¬k&mûí··í¸ö;÷,2d§E··#N;ÅEßPÀ¯—6:¤¬õÀ ÅÙ÷Õ˜(¢ŒôC!X¢ Á‚GJÕ:³‹Ô1åäOÑFtp¹¤³ 5OÔ–’†ËLÍÓxKý·Ä½"ó¸_nqî±€Ûù4–JƒBS…ù Ú•¦T"ˆ$u«òY`=hï…þÔîJ}T)¿Øx×…!‹`.]X¢ZÀ«BthùÞ9j—ª#žÿÀ¤ÔûˆT/#à DƒùÎjôCZ‡·nÖ Ùð˜8—¿¹œò€$àŸæzÙÎÓoµL*}êÁ¢†_‘þß–TGrBVÞr³s’õ·j² OÏ¡£±Û×»XcÞñSY Z¾d€»!5f2[­·á›OÇ´íÝá'f"€×ï†ÍÚiÛàá*¹Þh+0 ÝõM:|Jä¤Ý.öž›û.‹‹¢-*;¬àÀ)g! ,|r}¶ëfÞ¨C7àFýG3 p³Ú‘¤MöÌõJÕô·r¤CÀ’äeìtòc8GeF[×1éý:¼ñPßí«0°ÜösôïBÛ9;2)u„ñÉ;ɾ,¿Â¶Í÷gm¾1o$Pm5q‚èVöy \í:6 ïæ®k'Ï[ÆŽ«dï'Ÿ"¾ÛP0ò‹,C¼ÜËà¾qŒ¨\‰çO]zt á›ð9I˜-$ýFlÏtïÚ¯ñ5›—ï®®NK_¬¾ùW.©}9piçäÇ語V]¾9®v@½ª?Ê…íþ·†Á™÷BOï’+!õÕ‘m¿3{uüUd¢ ¯hW%§‘SlTãýöIÝ%öaüD›Ty¹[„”´ô,ÊPñ¸#Û XéánLþlÏ$ë­?i›ÀtûûèÊÖ I«ÅgXŒñ¨íd8 !GóêÕ%·Ó£\åC.“·}£ûòèRHz)ûÆÏ}¿Ånkz–RBÝISãõ„q¯µÿ½ó€“¢ÈþxMÚ]XN `@PTT#FŒxž˜@1+&Ì9€9cQ  gˆb³¨p*˜T@‘$ JÞ43ÿﯺ{¦g¦gvvÙUÿÞ½ÏÎvw¥®®ðêÕ¯^½¢9tå»F¥¼Ò7k¥oí];ÚÓ{Ü­çs'oá3XNØÐšîñydÝ’ §ŽÝ˘ Y¤ÕîvΡŒ.ÿ±ZõÍíÁž-e“:p9׋ÙH×øí´ý ø1Þ à $úK¥ Fva©{'Í”-y(ŠÀ–ïDÈ)E…Τ6hÆsZ¯ü:ÃkoààÍÌP’Šö2X'ê&­å˜ò*ê&X††ez Ž&i{Âkk[Ž]¼ër’:8W 35ðÇZLwä¦þÆï¡Ô™ø\í$e°uà©ëò+«-xô.øÒå¾P`@Ñ}©·u·0òQ“Û`j«äÃ#ÙHŸâ=9×äg´éa™nE?‰qþUH¼ë$¾E< íÌoçÞÿ?ñ&¼î¿Kæ}r üïµL·Oqü´3íqߘï.ÀÕ9ýGXùîxM²ÞÏ=zŠv-“¢.„¿›9‰úÚhxâz˜4܃°WÑÿD…”Ýl÷߯ºÒçrîÁi‰:ªýÞî=üºd0mÉkÈ:HX‹®YÔ}? ,mv–ïÊ=–}[ƵëøF-Ø?Cyìì&Ùee(|‹oP)”P“€C´õ[§}ûí·Q¥,s²ë;í\Q†PƳwÀS§N,X,ÀØÿËmm"ü“=_QÛ¶mSá÷÷Å•ÉOËWšÃ~’&°¨¬¬VöáÖ¨÷*—pÞ /ò§$ê9 ¨Ò’V¥è¯¨i[jsVø_Þúù5VÃÓfÑ}>šÍÀ,¡·¡©Ð9û2½P_úo¨$²õd¦âÕo¦kÚÒPçÎ͸qã2ú…ú¢Ü¤é+R?}óÍ7­mm˜xõ1ǧسúäõ×_omjë€Æ»zÈ4s5ðƒâsÌ16MGP]æÞç¿L,Ü~lk>Ü¿ºO1‹nAß ­íò‘4 ²m´f‡ÕÀì™ :AyšÏV±âú'íþ´tʰ&Šu¥‡Ĥñö€m¹u]·ð:ˆ,hx¯[*õ ­“_e'SBŠH娼H\ñgÓA>ž­gÙÃBÙ"ñ~Çæ\f(-&ÈÞ¨H÷­îe¢4‰Q£Xî">rCbƯØfLO:7$_÷!x‰dv ›$˜lÊoÚŠî€KÞ(å e5"¦ûòÝ®‡‡tã^”Yô5¥8?p¾||¾²æ6°¥ÚØ+‹¢gã°u–cÀcbHÊ1·oNC¶û0å¯E'ÙÜÍGÚÂ|)@Ð1á¦æFL\¸#ÛÇmâÆ²ôýTti‚wqý;RŽkã³W™Žy>·a|¶Ñ/M‹Ì7Ë6à‡4‹\)"Öo•_>jåñ=ßô±ã°×ªæœ,ÿ<Òx´*…Ûinl+T ­Ѷ*»1· Jk=ƒâýoÎp²À¦Ï%­=mÌ ´§³ö]ß@Ðz× ¥‰dèj¹QàGÑs¹ó¤7Gàø+maµ“í¨Su˜Ú^?\S}„zü›À÷¡õ9/Ü…ÎúmüZ3?´oYl†Ògg#ê›ÈyÄ›ì´;k[ 0þn‹8l$iî 1!ñѱôÙyÿZÏíá.€Þ(1¶8Û¡í„ûFÀOoË zøäûµÄý锤Ýo5_H»Ù»Ü:Û!´­µœ (àID ß*üß26ØÜ@ÚÌöMz&€ºh*ËÏÇTìO¨4±Þ¿ò½Î{²×-(ïoØÊê,dM$ÓLuôë×ÏÓ¬Ö$Mà™¸Žõ7³5ѵԌ‰Ç¥ðÖԹؖÃj‘+M›J¾{ùQ¤õ Úæ©øH³ÓŠSMi/&ܾÑTeî«{›^ ý÷Lòx/?À„ò¯píHé;‹ê§Uœhš×´-ïq/~¬~ÿ—ÑʽKÛ¼#Ìgáã)ÏGˆG{.y“TæPî;pmžìcú Dvý{’¿š¹USÏß›s%‡D¨S¾Æ”Bü©¤C¹t@÷EaÊ ~D©š×ËÎ7¬UÁ+:»!}iš§€'Ê¼É ”û^¼‡òNõi…ŸÎo†n JçéKØnνÆ)´šM¸—û¬Ë÷¼÷)®Ê“G´²«L{ÚÈAhú›È¥¤q¸ë¹×&Î}h7Ò;—o|‡öruî‚]‚:“†œ¨ä³zø¶-ŒwNˆŒÿ´~“sx¥?„4¶£ƒý.EÜÃëãGŽà{ÚrïQöœ¯%| €’Þ_ %î£þ'üŽˆ rmGùoêÆ\ßqÊK±øNøm])Dš2â£Bûc‡;É'¾Áu‹É— C], /§9ŽGÎÿÈfÒæUo¶¹šr¼Ì{²×é´ÑsJ¥g8é·ÍàAm “–tV Êxq‡ChóÄ£Ìtêáá!óy-¡ÜDÜÑ(å—Á¡r"é»nƒ_ß ¸æßE–PuoŠfó®îô—Ë6ör䣊CA><Òa“aÚQ> I»z _}w-$¾VK vT ¶…?7!³˜ÈU´Ù.ü$ûºß'ê—MÖ¿ì²Ôýù[êîAâC¢r¾}Õ¯uz‰æC£Øi§Ý'«æEx{WW&ÎȺ‹¿àþ:³›M‘‡uŒ<´Ã逬v½#/1Žõ¬ys8">Kò^è­(Ÿ+X̺öô¼\_%?)Û æ^iav'¢”ŸÆ1VåÈ Yá‹}dÄ@©c zGûøóì‚}7†¾²äI­õJG¦‰tÈZ"0žEróuS†ýä¹ËtƒÌCì¶úšöª´tà›wHœ?Žî½wê€(Åó~Ëé“;@IDAT–-³ñdÆÁ 㙓ðÒX°`½•íß ÊÖŒ ÓÐnš\æ–\qoÑûw ±þ|T;,˜ŽYàZ¢\vÛL§Xÿ»ú^‡…½:¿PÚÚ~À8¬Èß¾$J´J1ÞìW«VR"‰õ”©•5×Äâ.Z¼Ó(5¹u@ˆiЦ¯úžßÝKUqw_“··!©VA ˆ—¥E§"7bILÔjF2i@Ø… ¾úÆoÖ^en¦¤a02rM®‡ÏEë£ ,¢½™*ùe5\º}t\äU„¯ñµvµÝupA«‹ì,€PÊøÇ0qÃGš ÙvÎ,Pù¥›@K¸ñÏÊ  ö-;¿~z àrê6 ø}{ÕSµ/@Þ÷hîÊ4Žlûš²§y6K`Râ’jB½H`oÏP¹ Hø Z¾7ÒUà¤54°£5éæIl³NÜfPøÂ;¸¸Z|ðì“fD+ô`·øL"¼8>ÐÊ.u4áù4ÊU& ‚µ¼õ@§—Õ“ã ùš­ô;¾¾ÇAnZÊ:ä*ÜÅ ãýŸ¨0ˆqÕ°%¯&gJ»ýã|ú÷>LúŽSz´áA5h#_q; Ðù7úm50Pˆ…øÛûsȵó·<ãhø‡ÕvÀ}‡&K2|:wߘdË´ôU`ºÙ¹M¦°51µ0>š¯+µýÝ&âÀ”Îm¾ÿÄÑѤš¾m‚Vó¡¾È1U£í³&}=«úppÝ3fi´/eÄ_¶ÕÇgÇEzÛpé«¥o«‡2ÛÖyŽ\Íu3{Ä-ÙÁ2ÔŒ©¹Ãô‡/øwŽÊp×$~&|µéUs»Ù>z”›&ÒQåî1?¢ä@ÐÎZëÝ„û/ˆó¸)‰ßhNE»õ¨ø“ÔC”´ú\ïÄïã¦Ã%:€oØáŸð¦184囟0û )|u²Ü‚=m4[QkÒߦò~wÓ ^í¢Ào<¯ãºé·fAÀžZ’&qâ{«mÿý¢óî+•3L)4·m]6`_k<ÇÂÖ\4Å1z|Sq‚Ù26—•¾l‹Ýç”·“¢å%2aòЏMdW×Õ»”c÷¸˜:…ƒù†¤¶qCÙñn™:uâ`¸ÏÈ×Ý6Òy€@ó’Î,¦‚¼HK~ êè]&𢳰Ï8³â$3+ôs’å=3©šGM'$™sC 9XïR³yùËæ tܤú>ßĶ•ÙŽúÜ8´ÜìX­:œhn[¾ šÃ3lºÞ¿3Y´X£þª6 <\q·ùF¨Ù¹‚²»ÝqU{mr?÷y¡öäg÷-ñï‚Ô6ãá# ¹Å„‰"´e-¯Vö"^OÊX¼×#Ê6cQ^±¼µ±l{s¨Ì8hÔʵ„>J>=i¹PÊ£×N¬#ÿÔ_亄’_ì1³1§Üïvò>l]^èïã^”¬ë1”ý¾ËyO–§ÛeÊÃO2­â€|rå›)Fþ×ç¹ëKÙ–¼ŒÛ^î³ žÖî}Ð¥Šò„÷O ðÌs3hìMü@iúzÞ*k¾k"'9÷àMê#Ù_·#n;Fá 2§PÙŤé_*¹ÐâhùI¦àdƒ¾VRºÈ[0ðôL_ØVFÌtN?Á¯ã_ò¨þW %¾&쫵RÏÏ-ÃŒHZÀÌY ¡"_˜ð1A/§ŸêÖL‚7I– u¤tN{Õ\Å·^™~nà»UI/’'MíÎÇ|O§\òÝÀ­Ù%)¾åä1_¤"Ýóqò¢¢ èÙh£Ì/¿üb¤é7}útkcWv@uЛÀØ7v>P[¾E +3 Ò$”]^™vfá¨Y?™?þØèP6G¥¥¥¦]NC¦ÍcVB[ÌexìØ±fæÌ™æ«¯¾²q ˜R|™žˆ¬Cæö‡~°i (nÝ:x*ÄVt¨\>³Ù…¥p ß”:ø£ÈÄ‚k¥.˦ ¦˜·îÿ,ªl ª¿zE£&ƒ8W4iâ£W¬~IF£C·’FBbH`’à”Z.\‰hºý’P† ¨í6‚ ²ýo¬~aî‹ßkÃجÈNA\£I¶c?ß! ¯\â÷åIëVO»3û Þás„‚†ŒnÔ²N’íϪ¦VKÌõní5ÃöO|D†_öƒ¦œz§è ÞKÙu¯xk‘¿ w›fôV.íí­÷)V=){®-Áv+±å®¶´ ùÕ©Â T½ðV}Xe7Ù­ý»£w˜ÁI&µÐ5)0€Õ+™œûH¦öð"ÜÚ±-êi¶ðfÓþ„ ¢._ž@u ad²àwJà—`D/Âî xGPØ´í"vSú‘» eÑÚËpN?xÛöÒ.öζdi—%†˜ÖÔµZt¡ƒUôå—eââ?‘²@_œ•mŸôÊÿLi€†·· ™4»EãÐ>jáMÎ|)=ÏoãP‰9U‚eÅö”ã<33œy‡"\¾0<­ji|Ž(ÞVw'ÉiÖ)ð^²Ò°Íd¾8¢þWlLÐà÷ÚŸì¨Ö‰ªO ®^Ê¥Ìûü(#K´ å›»÷Á—¾Þ^› Qȵ•Ù¢äÔ„À†Œ\Â,„‰Ní@fÕ¼ÞÔL§ exðÀdòÃäWæú„ LQ\me¬5`ÝÂôAÚ‚lœ…é!Ôáq¡M0Òt4‡X e²£í‹'ÆM•~Ú.Ú‘´&RR³ ÖD»i9‡zÌ @ÁGšÜyvá…œ ècÙhsC NðM¦*O%ÿ¯øRpnÕÖRšÏ‘½q\“ÞZ™-C=˜‘>h¡]ÐT?÷Íø&ê8ƒ:P¯’ç p]fž¨ìÀYiNü»ÃlOÿ3ÕÇúbÌE£˜º¯é˯7‡·GžfÒ ï4''ƤÃI²ä)øÁ³¤ h”üg;ܑʮdªC+¹ÒüížOéK2Ù0PPú6Ï$Û˜L7ÊvSÒ=×\Iÿ¢×˜âß™Ïj,eÕ9`oÿ’óÍÉÕ/››}a¶í YjBj3ð`>‚?xêg\/¦úí‰;;>–rÿšûïÉç3|×™¸Mä0»{ͦôkš§Ãõú`D‹oéi?¥o¯-?—J©»Ò÷øæ}Iÿh¾÷'Më-)Í¡»ªnv7}â·Ú%İ«;ÉlAû¾Ðé3ó ‹Âš ÿÈ"ߤðo¼µÆLˆu5㬖èLjXo Ùľd»á Ê*£žð¼½[ŠKáQGâð oËBæZ"˜½K4D/OÉ…}i˧¾îæñ¹hÍ­¾ÀN;¯‘lÉâó½´ŸË"Wc»·+;<Þó£Y¥|‚Ùˆõ*=Ü‘ñŸdÚiÇ.å­hÖ6E¶8)|0ùhkæ•ÜÍD|C7On¿è'¾[seöŽuÿ•CÓfeøFO§m‰´)ËVãJžXt±¶©kÞ¤-Çé1Óß?]¹KQR”¸»Êÿ!<}ÝòåÉà+´i™eñ(1»õh*·p¥í¤ˆ6-J¾„Dx}7Óšî'Qoíµ•Z¶—s¨†* o$©-Å?¢-D{¸œÑ~ú’¿Œwñè£a|ç¡ð÷ Ÿ[æíäkp†“ìhþÀ"‘%¶Fk§?#ôµ°–ðLùªLEZ¨-ÉäcއïR²ê\Ÿƒn[ð]c¸¶×CQ–,Ôx&?œ¿àv-·ä!ÅÒŸ•¦êheˆwUÐ.“ïñ½´« ê:ñÏ3ÍõŒýý ó¯y“ÒMÖåž_ò9âRÆ47Ò„>Å,Óy7ÒGf8!ª.ä[qî‹ø"LË"ÂÕdcÇ€îɬ€Ç"לĢšñÍP´Ë-¿ d‚&à)ÕrCûΠRÚó¥¸lšáú§=¨ŽeÛ¶jŽÿá2Yà7“'εðØ ÅüTðˆÆƒ½RÎM‚¾t,ã/@uŠËLgŒyvöøö<íCm’¾Ws¾/ú«úl#Ñý,˜õelöèò¦?™uè ¯Àî·m‘ƒïBÎ÷ZƒŸ >ƒï™„ ¢3Itˆ¡ZÄMô!™iÒ<ÛÛU©ôÔïǤLRs:¾Ž$¨ÿª«Úvµª? ¡o,暟Öd8: >Ú.ŸÏ¿™Oµàá‹SÌ-ÌÊ‘ìú Œè_¾-ñ +PYé¼õŒªè›Oc¹–¨9ÞþU†d»ŠD'¹e•ÕõsÒ“ÃXö†0d­d,¥S©¤ÅËÀ(ÿ¯¥› ýõ!2ë¨þö‘¼M›6Ø ,\³ËÜH ˜«Ù ÚHÜ—ð,ž&k¥:[Ú)NüC¸Ú˜Üeg>q=±ß—dÁ[Á"-ˆ+“þ|x‘4h<Æ„§€h}éli4 ÖÒº˜ÉQ…¶G ûÌœÉ`·J( È£^L¯æáƒA¡oŒÒA?}$Ðm,í$†'œÆ@ý\A[uü¤íi/ u¥­5~¨•—j.6špNwH8ºàÜú”{ä"GXL~š÷u&ör þSÓa§še‰ÓÏö[º=͵ÜG|=£ ‡^Œ¡µËäƒv¡èp¦ êªgùzuž¹-¬ån¿~; [Ì¡qW#ô¬â&»„ô<޲no:îXW°0E384ÛœW}¢Ïe#s·•Õçšq‹ý^žPžáç{X›0]v_ÈÓÎôËrø^./vàc€x_dñRµxmg6Yš²žŒç øbìfïi„ImÍ '3y^¶'÷·»êV@al]cb—E¿So²Ùn“ÁïœPKl›m‰òrVHg± C¢aòIÚ|OŸÈ £ƒÛ2& ±7Ã{†±õýëÔ!I¾$ê|»11|}NÀÆŠÓpS ÷(·|=]¥]šÍä»GftM|²H‹cštìD»Mq7{ø?ïéHCá©Ú ƒ¸6„ô`ê ÍÛ¦ÉoáßnïH0q·€Î ó@¢9co‰Õìe3xY›Ï+·íí©Ú’cLhÙù´@#FRks/´ÌÜ–øÒ\Ûd4åÖ‹·92±r¢íµÇF4wÆ_Ä¾Ý &—1Y– ÷iR4SAJ¾kÛ–3B¼·iª.òQõIØÈm…o{Òðժװ ¾‹9”ö:Œo×!f‚®:Ãû4iÖ®¼ƒÆÒíñË‹JÌÇ,DN¶y¢ýkŒ(¥‘Xԉ章ÝúA¤‰Ù‚¶Ü¤æó/ÒK>ª>&ÞÖNǧM¿BÛîNGÝ‘­±‹9p¥ÊŽ4SÐ&€fì{ɶhŒv6wÇN3_%(G4æVKŽ1ȱ&•ËD°­Ój=K¸òl\¿rÞ'»]è ‡Â#ªÍ,øÎÑê-ññ§OAMÉû~”µvQ´­<ÄLoú-®ƒÌÆË×¥¥ó~ÜÙ:­eº—½n>¬<ØœdªI¾µ!ûEŽ3——îjö­\lÞd›¸ w$OG9ÀXxæò½ÉÓ éTÃäWæÈëtñšh³kÕ–˜Sx MÃ=(›^üHGTÓÇ^´ °"#nU+.b.r÷q,gÇKÌœL[V‹ý‚q¼3&"´à“"¶Ñ±ZÕ8¬eÐzÔËÖæKù»“¶Ráú骟¢Ü8ÑŒEMÌp@éï‚Mè;ÓN7LÛf­¦+#ççÃ̘ŠAf àþÕ+ö@Ãö8üǹ)º—Ê^ÔÆLÕÊÌÌRâT]cÈæ²Ýìë„¿|LH4¥¯?i¿D \Ý¡m(’‰”ÿ?qã;}|B5ÜŒ·ôëm(ÞβQÁÿ½ð\sKÍP3" ?¨VÚóùýÎÏ%úŒsÄ^ˆwÎ;ôîi®'aæU×Ò Ûƒ/NüæÓv–Ç–üˆç‘C¡fº–Ú"´ûø ‰?hÌ›bBÖ)ÿ?«•ëŸM»AµxÚŒ ÄçO¨¡}ób×À÷hû9‹5ÆÜOÿ÷›Ï¼‚݃2Wp4òÖwŒ™=…7a왋椸¯ÒáaÞZŸ×x¼kq)4~( µY"yÆKzËTA!’ÞÔVÈBÿ@¿H$bôS¿RÛ¯zôè‘2{¢þyÉ%—Ô%Ç_¾{‹!»s›/´i ²×{Ë-·dØ.æ] #ûâÙTˆoe‡-ô,{¥½WFDjÎ å{I¸7BÍf'Ú™N n(ÒÖý˜¬ Ìš¬Ô–~¶@¢m[ÒL<% 4S~šÔì[‹—K[ò¶:äIö5A]Y:Ÿ÷N°{'|å}oQÉÉï hÒM‹J£p Æ>+ öß®ïN”µ€_iŠdCJ«ÎZ—Y!Ò‚ˆÄƒÆ&¦Öbš4ße—mj-e؇úÑ »ýƒyÒS0ïnµf[6®÷BS¥ SŽ©ƒ|¹©5¹:ØŠ÷I[ûc@ï]f=Ñ\ÚEZ˜í ´J/-†EÔÝ&†@ æ4!t2±&àªQÚ¦E–û#k0¦Ï±iýÌAp2!³$}˜Hï*5›{ïÉÉ,Z®¦ú„©—]/8Où/¦gõ«æÉªÃqóÐ_LM¢k£Íyð%Ï™šªËðüΠ®·”}ÙûÌE˜èû1êšÊ_9¼Sï«1éÚ•þ˯æ!úäxKĬ—X—¬‘“ý›io¡É9:ñ9‹óÍk´…7Vaìfky¨šòŽ¿MõŒÌЧ- Žá§–ø¼H[ÅÂÍs±!æ)¶£>ŠÆŸlãíH»¹:ù¦3rèLéXúü-±@#Ô‰ï耿F¸¥fth \¥¹©”‰vÅ)¸}ÎÏ!Éw‰ÈzhqV£³[­Ç™æØ|Ð;µAOsDd³ËÄ&>õPØ(7¥MK¶ZyùbãÞäGòqùû„|Âõy;inŠ—À+™óÐu4ƒo‹v1GTßDª¦{hsò¥í™¾Äá+åL¼ £åÚž¸…ìäE‰)þù ÿe¯ï~½Ý^3’qOv oN¶œ›Ú-ãåÅ÷Bn)_[†ý~ˆaÿ6fžG2Õ.÷ÑÑúHö"C“¯‡˜ÐÞÊ€KZ¤‰é”ÏÕÄÃ?FÙU_Áý~íƒu=“éË íØ€ÉŒýP¬9rÙZæLN!ß²ëS÷×Ò×&¹¹áóXÚQ%çzÉsõ$èõ¸Ÿásç6t”¹ ‹ïÐŽ=“B§ndÌ’h?°(³Tß0:ô7Ãã\¦ªâC¼«|"ïû˜oœÌõtkç¿$z·y'þõüù9ƒ2CØïŒl+¾ ¸û€e²ä$lvádaó‘Êÿ7~A3ÊWZ…jÇu<Äéê`-î½VÛ®£'ðŠ®zÊ%iÓÿïd¿wùÞ¼ó=w)$7¸u¸Ô wyOÂ>Ÿ'Ϲô#Þ-aGùÿØ[É}¯ú2î§Á­ øn¾à1—(ß¿!×+ÈEöoUŽ+ó%/Ñæà¹‰AAo¨»›€µÍ$ùʦÈõ|;ã‚̵MôÊeæjù*Z9?Ù…Ôµ!²þ…O1kc’¨‚]2;³}UÎ5Ũϫ([Ê8¨<ç¥|ªQÚ²v¬ €Dâ8–/Hã?¯¾—™vüú ­þ“5–ä—æ‚’tÜÂ}¸2v"3Û](’wþè çÓŠz>ÑnkFÇ'˜E¨YˆþÍ\€–aÔ–'PZœÜÙÙ.—ÜOÝoZr²h¾ýŸùOüvEÓLwL¾¼´i†2Œd¤ ‘³Ö'Œý£H£k§sÊÌš}ñšüŸ×xY(AÝÁïÆÔ;´ìKÆ·ë'‹%qÆ9T@ ~s(Ò)p-âI‘ ­D°2âFùiBöW õodÿ$ˆ>þ‚™RSió »ÊAqünÒ$ÒÅ@Æa"jE«7¸—ó“›'~©33äEž(YTà¿@ ºh «­A9ùô‡å Ú†cff(a"E¿:Œ­]»v)@Uõ>§bE£äËK4/3ñä¹Ö÷°ÅÆ~ÅÔegèµ:Ð=ÝÁ@Ûhæ^šÙþ“áóÁ_ÆY}ºÃtðI§élê;_c°Y”v*x÷Ãc}@®ç¹@‡!÷ݰZÛzaÚ†«&Ë“1ÿ‹êq-}]¤ñî|š‘–äÙCü†)ýc/{QS)Ïæ×©|׃©‘w-ódé}æÙÊÃíãƒeÏ™£*oÄöêÖ<ųŒ¬ZÄa¢_hÞ¤<ûf³í€¤}?M-6ˆÎ¢Ý¾¹Å|ÿÐl’c¦”>‹ »7>iÉJÀ½jk¢´®o×wÕÚÛ /ì³Û„ÓmTšÅ:­úqêå[lT‘XÕüZß|úŠ…¼ÅváL *·sïlHUM&™™p¸A-&ù¢Mßù׆ @bøLÞÍ»R’“6iZx*»Øt0ìÊáf7WSw–Öá¿@ƶü¦ós„ú*v¾i’lMÔ‹¬[­ÿJǘÙŠüIr¼Q»/ã0±»’Ó}ÑbCÉVÜ4Y“„9ŸÛæ4¹®éÇót~‰ïµ¥mŸžlŽßÒ¤c‚í”ï–½i.À„Äçî$Ò‰©ÿ-(‹ý0e2Ì<´+ÇR& Óœ;l*žI‡.€5‡/bQ cñëÌÐHoÓ&¹Ð̈”š'°á{fÍóô÷©vÑé4gß'Û»›hð?À?‘ÐÄw'À±¢™N¿6¥oÑT¨¸E.mŒÀ‚4’!JÊ+Ûž–bFDtæžS½Ä·§ lKÊM0ñ`éÁfV婘»Á/v;e¬:Ó$Û1 ÙÖ3ããhb—›'°µ;‚‰ã *W˜• _%'ÑF{S5/Я'“Îp®7˜^ðŽ „ûœÅ£–ŒßóÌ4Ü—›+Y$^Â;¼ ³$Sà2zNÊšŽ……í`ÁÒ#Ñ®îZÀ"Æ>Î;µx{ˆê}’~ÛP;•4eó¶†~Wp[u³ISñÔ%fÀòÝlNøç’Êq–÷PËu*bUòôi@8µGõ›ïüüN«óÍý)јþx›ÆÍMçD9[@Óý{ÙíMŽæ^ããüÔ&ô®éüüT :Ȉ^qŽäÑjI~F~ÛsèÞffhüÑà-ÍÒ•Ž=F½^HÜ|¯U¿¯û ·-±$ÿÏçç$:¹Ô¥M©ý©ÔØ]´˜«ÈH]Ú$17 sd¾!ôU§,MU%ÐWÚŸÅ_ÓR¯„Ÿb2컢%7yHC…Â&Ý«‚yoÕðåÝ˽)ïņ-”Î姺©K~5é¯Køú|ÇÏ ÅŽ&зmÛ¶VóÞ3ò5X_€5_^ÃxlÂ@$pS“iüþl[i¾ÿÜK1óºåÙ†>••†§áW·‹ -{®·0¢bÀ8¦} F«ñ_KÒÈF×æØºóÀ9Ù*~– ¢V:ýÍ<€Š5±«š®ËX å®ÎjrX‘µý¦XÐWµ Èä|Ó¯v;l¦ž/ÿiwqMM¿O;ÕóNGù­c¹FÜè•{©?­„{Úh=fdË[vövE€è+z €„ù"²I`é㤣ƒr(ÒÏœ´Wh©Ù R{Am¢#ùr@?yi3éukàÉœ$ƒ¤Q×r<íDZÊÛó OgˆréX[Ú‰Qæ¼:[KÝì߇põ)“¥cЄm0OË,v|½UþšKç¸&ç3ýAäþtòzßt}Ü‘ ¾ÝÄäGö­fëšlQŽ"¯ŸF®D²½Íš¯2Þ›}qÄä_†zœ1ƒ+Ï3¢™³Œòº!ýtú„@ÆZÀëàÿ%ýk-õ¯¨ò üµî}è£õ)ºÒ†v2Á¹åh†þ®¤鼨­fšC2ýÃG›CÙ~~ib Ùªhù'´¡Ͳ«3Ók¬§ª3Iy†/õjŠråÊÄYÀG]ûghØÝ;Äs„x å+¶Y-Ä7éO [[÷F«í'ÚójØ«•T6…ÿ:4Ì£ 3¶ñ! 7-1¥Ñ ›sã!³ýöЦuhOòþ“/%Ýþì<Ë”@bîÞa"š4#­hA`Ùô¡ßMEHRx; ¥ïáéÊ)˜²Ç’é µó&´g‚÷iò™9nÅá8M3GÒæ^§<¼‰Ýuô‰VU=Í©LÐNˆ 5SéÃeVKåZ/ ®|gü „¦¹Ó®ÿIÑß…óKN˜å3M’—ej¥¿¤f4ÿ¤enš¸ék8¤²ÙÚÆì^yi¼îD ŸLØÝ苽x†ÏÇN4ã+ǘ­Ð¬} 0eqèdóVâ¿ê„×^~¬~ÀLHïK_^ mç5ÌPúÜ–ê ±ë̘dXZs˜™V}¦Ï–ûôÑ®:Ü*º‡ÕÅü)7[¢õc5œ'r°óžÈ¦¤=ÍÞac® oÍDâ(3Å!¦UrŽm5Ç2³8•ööoÑrS“s"ðvä¾(àtY¾%`bg›×+#f±]Là{]Z•Åi8N €Íè^¼Wí,€ªŽÀ‘þ>‘ïÏŸD8„w<ï ü³Y·âtÓ^ ­«‡g="•ÝMlÏ£f9µÚi¨U§÷dÓ"ôËC¡]ÉÓd<çÿth,ß·ŒÅ‡%hdšhW>ƒ|g“ß6wÉ@|iûá è—;q¯öˆzNΣ xG„~TF_­iË·œ¹bÉ’!T»Ë¢ð±TßU}I–G-*oÚän´É äg]+“è‹ÀÛøí8×ëD´’sMçª%¦œ­ôÿQnPú©öÔ,OÈ/ËÖž€B¼Ô!)PíÉbáçȹÙÀ/ÜÏŽÝQÕx\)þåÈ:õãIdŽ·ˆ7&^ûe:[ak¡=‘GbÔñ`v§ü^í´»Z¢í]Æ÷Hò)1*S±WjÎN=5øÕ²}—dH¹¼ R¬£ö¾µÓ-Ó,Yöô^â:-ÎïU‹O]Ìöe&E?ªç¢¶vZ¾âÂ{R‡¯»m-ó}Œu¾ùk?Æ2¬ß§LJH&Ø¡fXÁ¹—8îdkBŠ.í3 ¨+½[Enßšwl“¸—ñ³‰Ùžò5srâùkø}Pª>‰Š ÊË«æ~Ÿg#úŨ,^Zó8;ðº±@šíçÅ_™k¯ùI°âR’ü>#Y’^hu¼d;þq»cp^Á2ôêηŽ\uIrzX\ /ðÒq®Î!ò#‚1”Æof†þ{žJøTgŠQ·o~þùç-è[·XÁ¡a•µ¿^̺hÀzq¼«¶—Ì‘àý7¥ß؉é{þÊ|‰Ÿd^E‡n¶ÙfðÈë¾þp…î;À\&3è@6ÏœG¡ð:$P6†uãÄËÑ$ì²›](í|~Ê[Oå¯15~½÷?΄áz&ä'n ' sÅBö…Äü÷a²ób’˜þ–h¸Þ™Úâî½­þW…Œàk¿ƒ§“Ë$#h:‚¥÷ÆõÉŸ=é\vòô57S=ïÔU‚žÚû‰±w1yÞ€-éÓ ´þýi‹èûÙÿ×"íÍÈߘœÖ”ÊBÆ€èû˜€Ôfx?#ÒJ=lÂä«3Bâc+•JvdiwªýhÛ¬R»­¾Ó¹öf"-*GpbÚ—"ÕƒÿÙóPy\HŒÈ¥­pú*ÃYÛ„ŽC“˜çÑíyýyÿ|8=©ÁÌDú3‘»Ü ’q ÀÕPÃÑHÍð2:ÅW+þ:deh Ò˜íÏg}Òk L0Ë],XäÚ$%°‹Ø¥æ\5NÎå'-O:sѸ»ŽÓ{e¶%î ø‰ÿK‹\‡fùéÊR6ó;Ú{Êí…Õ‰í¼53€úê)[à­Ðz½e”·ž¡Ì½ð9À:K áøJY;¶ëAúvñgkv‘ „vC“o4 Žr•ž »á‚E®£o¾B…Œ«[š±§alôª*€¨Bv5€êµ€›Ðò^ ¯g6ŽÜbbÕG›ÑG?…J&ï»3´¥¹2‚ q»]>lî î·LÔe·Ûá²LéKF2)AÞî·éw&gü·`æw³nâ·"ÚEÀ¤¦/÷³YL ¡!\f¶ Ÿ†vÍ ¶WaŽ/9“-–?™™Õ§šJøV«ùü‰"['ÛÖ²K\ˆ4{#Â$ ^fV-a~«îH=ËN°´£ã¾£"SÎ ROÙ#´ vq™è@ÏÁC®á]ž=xõ‰RøÙÌÒ±ftÉö昊¦gÍó [´ýý·ád:ÌáÝ¡àu±[ÌñÕ™Ž!:¤{ã”f‘]æÎlÕaAÏ¡ ?,±ŒR«2´ðÚŒö÷moÊ~e‰œ²ÀbÂ(Ï‹'ÀDÃA}Fš§ôƒf¾l5j±ë&Ÿ·˜ö,¼lkf¦Þ“ì€ûvÒ¶!ï·‡á ÄkúÚɳ,VŽ1²­ùŸé+ÔëZñ;›ò» ÁëéÒ¡]Ò:vµe£ü,xõùpH“»K‘¾ÜTšñ[1²=Àà?Mûx?ê$‰©‰5Ì¿C'š'rºÚã6VôVžçó=kò7ã$Ž”:š>Ñ^fLwʵ;GÆIÐïC:ÃÔåêb<_ùV9n@ÚqÏè/meʉ‰ü†¦^´í©Z¸Ã3LeÜñ[×=ij͋Ê_€Å塎˜ÃYašDO0TFX§¥C{?”4öL¨^·'OÏ¥Þxc5„_ õZ:œwM°ƒf:RöV.ù!Ó9ï“ 9§Gç ’ãAY”Ó>*)'-f„º’OõU•ˆò”-P·ß7oá–Ø––I“×Â!”[Æ‚—ão¬–,íÂö^" ”a^`›p¡väç[êåPŠâAE²TpA óÕQø„åÜÛXŒÍ1;ÚvÕyiñíµQøòð%¿÷Ýasükuxˆ·p˜fG¬ÍÏ<¦Ôw‚W<"0áLG-ø®‹¢À5¾±73„ÿ‰òŒ]Á·_n: gtâýO¸2A}Z?eÕÉ{ÈZ磹´î«û»Ñ"njkz%ÛR¶/ÉéoNêÏ?ó®…OpúAžÝQ©”ìá›ð#v=$ËGçä‹‚Áê9Ÿ|¼LÎ\Ÿ´4þcLé„ âíJË—Žæ¯Z H͹׹Û3õ”‚·)¿åºšñ»6ø/L•ý‹ù'sk,߯žÞÙ;1_‘rš³“ÉF/úŸh–ízG^sY*nZ‰%åTïí8ÒnùAu˜·KÛz<‹Ç»>û|V!ÒF.¦+Ã2uqòÄÈYâê+Ku1«ÿfàwc yjøÌª¦–wW¦OÃ<é°2kÑe &矙.ËÊiO Óv,Sšè-ÅÌF9÷"ï@C$ ¿«e’b8ýsWøãûQwù´N6,ofaéýZ:ZVZ6Ò(´È“3¹ 3_é-«î…$Ö{•®sÃ\Å¢ýp¤^ ÑëIót&ã/ÕW‘òŒü©c+ÕÓ‚T ÕÍ8þ7_!O½Ü, 7ÿt”†õð»«àcƒÌÎf‰Nd/Î{ž¦Œ¸Bª«ïiSË£·ñ1«×\êÇ20Ñl`ú—ÝaºTìgÃ{ÿ„w5£Ãû›¾‰ÛÌÀÄ"ó¢'¢=ÙŽ<}o·`®O»L•WÀ©‰Ä·7å]/óv`áMÖ+ãßÛCÂMíAV×GN3Í’sÍé‰0Ñ{Ð2Ç"å›fgxÏGAãxôÚÖt~÷øÒ,¼¿©¥'ã÷0à®1Cx×¹LZüãKïÐæ²ÈfXÇÎw qN3cÌShÜÖ`.e©Ó/"R½(×ÇÌêñ,ÞŠï›GžŽ®Ø×ôg!M‹.e€â§iB¹îKÌŤ Zˆd—´¬/íêR@É>\7'ô«­p¨•¹§”É{ÅA¸ÍrR0È[†³ŒÛãÃÇGÑt¥ž®µædP“ÐG˜8ÛI`¨3«S¯’'&½Ë÷'Oo;ieüCÛ×Dv6ÍkªýœšˆÛè"‡ÓOÎ̈‘ÿa5¼òH#ÑóÔû²m ¯T¹*z WÇX[òùŸ7˜¼Ð_i;ÕÜÍÎÅÌ,j8ÔŒ<^Éßuùú”d>KZôƒ³äŒt¶&ý ‰ÿnÚ+tn-Éü¾æ,Ü×£m|M^·Cóme˜ˆT‡÷¦åº#×'ÓéeÜÁ¿b·Ò¦®1]Y°Y—ùߣj-Ú)Ù w¥€½¥£Y4èK¾X< -ôLgÌÙš-Ô2÷$ðNô‚h@:us¢• _§m!`eؼÏÂÊæüW†Ë…äá!/3xm©#‡rÍŒþ=ðeHrÀ«³àqíò^Im[ ¯qlòÍï¦Å›(¼22³!n}-²Ó€N ò§R«][Óÿ¯, ø¥ÝE(ÇøæÔªÎdá~_«ìøø&qGÚ9UíÔ̬»Ã̵fPÞ©585`%—_k ù÷ å‡ýèÚlñ_Fæö\Œ'þÙÞ÷Æã €±!ÔÿWðŠ;|¾ëp¯Þô£Ï­·ásÈ'5«<ðNÇ$J=Òq£xs…ú¤pò˱Œš›jÌÕ\ù³¥½gLÒ²×6Cb—š‡ãŸ™5QþœÄÂy¥ð£]ðU©¬ ¿ 2¹§ÛÄûü€©M6ß?ñÚ(¿ªã ‘)dGÑ.—ð躘?¹ %)MÉeù„S¿Ö/Nš×ž÷­Ø“rÚ…ß~fNär4Šgã·Áf¥’Y­è»Ð->Œ’šXrÂ@üâü*S ¯o9×” é ¨g€Ñ’?;nÙÿU×%§21ÞÕ, u°2A*ˆ50É}l›rÎã—FZlƒtК¾ä…|{ tìŒRÕÓ‚ÔÖmÅ^”ýf-ì&¿“¸ÉLgY$ ú*FSÚۼж´£ãä"-`? ÏÝÐÊ-mñ¿5øµwïWáÞå€aÞÓtßün"ÚX ^ÀbŽIŒ7«‡PþËToSÍî´ïý3Æ5ø´éÃÔYt?âmNÖhÛIËXæ"DÑ»×ÓmS2oÃéãyÓ/3sKèáò†ð{¨n~õ;ü]î­méò~Ím(L ¨kŸ‰Ðþ£§äMÓz¨Öˆ·¹<&_h™ÕÈ} 9 þsm¾Å»kÖ;VôÕK½¹BñH‡ÔަîŒÕ×px×ì°óÈèÝ|ó¥ŒlI³7˜ÄÜhsLÉÉ&ÖlšéWö 9Ø•'æ\è«·hGŒ~¢ùäëô/ÁV3/GA·ÅɼKãjñôJíXÒˆSú«¾Ê»¸¹æ—9ôwÓø ô]·ªÎ)–¿œƒ§Õë]½ f?Ë]•-m¿Ty÷¯€ @k3–öÔ+5„Ö´¶’ÖPKˆ‹9$àßOÞ³wõü¸N©m ro@ù±ˆÂЃ@ŒÈ̃÷Î?êÚ¿…ò¾ž[Sž~›H:,K—JâsV;;!è‰t˜ËhÊ=“6LÍ øj°r$`õaßTT'§öCØ ÚnŸïM»÷B&$Í;H³3bÿCØJÚ³:X“ÙÛ‘;D$ŽöŽ7ã¹½[.Ö3Ï?µ#ñ¡ z€D6/ ™ØÈŽ·1ùÊB@–MA—ÎCØÒêB»SD»÷ÒùzÞƒz®öð:ž´»’ö:|‹4~.$ÚšºÖ)ê²;Í´8'§Þv&™CÐÁE˜ìC îäÌÿë#¼É o)&¹³.i¤AÌ[³Íy‰9 ƒ­Ñ@,Z 3Ó–2€jåMØkôm?ûBmÌýL~i®;\òÎl‰FÃwZ˜·`‹·#e­Å(¶Šü'-Ã;™Ä혤œ­–žSoy…2Ò’°¼.ßqà‚ßœƒbÍÉ"3ÐàÁZ“¢Ê.1`vE{º)8@ø|Ü©À¿L*å±2Ó©àÀì[k­EGÃhöàÙáÍÙQeÖáÀ|Ù×]„MºÖ´¯¼|4r@àZØ·»Ü‚—†Z3É_bO¬bD»­¹Yö©™»brü“¾ÖüÈ!MÃoAõÒ’¾ t¥Gë00ÿ☼µÀòs“·L3½e@¹~RžîKe °Cói—²Eþ3‚ÃÉL•;1– ŽÐ&”éé$öçnÖY«ä³ZÕéæNë Ð|‹Fw–~`’Õ'bjàKó`Öõpú3Cߘ™LÞÚ„¯¡¿\A:N9ÞIxG«Š>c5'·Àojê=Þ):°8²”ÉÖûp›“Ødþg<ÍDúÓö§‘¿mpðgà…&·.©? ­ËÖÐY”gˆÑ“]N¸©UìÃ$òFºÏ2ÇÒñêÛ¦ômz>¨Ü“›ÕC)jª mp™Bzïá>D)¹À”WlÌÍÔeo:žsgb eTbš›“#ÌpÚË8êÙT^Nú_2NDÛö_¤±š£íâIäRì¾Uõ¹‚¦Î·gQSže’É9¡»OÁmÖĆףhפˆšCzá/™&O(IMÈ¥‘[q5ñnµ:hÎL Q¥”KŽ ÙœÔÓáÞÔá6Ç;nÑø¾qü¸ºt/å(¨$ Ò;™2Œj3»·åv%­÷ðûÉœÄ2Û‘2Ó`´ý\i{¢x2}%'mú$ËŽv/oŠ>0¼]å!ŸÕ/õyþ—ÜFð¡Æ¢‹ðƒ5iÂÏé# ˜°›”zÞ1;0i) 5‡_Kù£QHc[ôTÚÌÅ$?'õ Çá)¢±çFÕ±Ì3êcr!•¨nç,émÆÖ|À9 ×á =tkšxÉ@³aõv7~”ÑÈMH£ŸHXÃDÌ%ÄbTa›ßíX¼ûJß[MÞZày'ñ:ü—L°6yñLZÕ!êJÕ¢§p»¹+’“’ÁéP´øÿ³OÕЪa¡ èÓOÚžQ~1ß³Üü¿ ÄÈDœCæ 'Žß¿ØûæÄe¥wa¢&ãÝÙþ÷ç œÖ»²ß¸È­« tüõ.ï9»­ä}¥y«aÈOÅ€¾ ¯‰B}A_™@)D%…<}~Ê»4µþHÒpé}õîõÎ4`Š<ÐW÷:Ì%G¤¶ägÝoH%€´]Ø#MŒŠ ˆ]ÕsäÚ4/XÿfÄ“pá§@Ð×j‹À1VëJv(G ŒÜ“Xj¦EÖ±&/Îf¯޶sWmV6=Z½7g×+#/Œ®ð+ƒèDÒËqƒe¹å„ïAý”§´Õ‚@_%1Æv¥.ÆÕF;7 è»ï“Æq_&û½Ø·%í6¸IëY´n—SCÐt’V©Æ0ÙdÔ6(‡Úš c7[: mÝ@IDATDæ1: ú*®LKž.1AM|å¾§±/›:<–ómi–ŠgÓÆenAø´ÅúÐtÊ(%ÄÿC*_ZÑsœÉº›¸¶ûL™îœXÁùßÖ®Ù^h¤Ýè;Ð\ÛõT‰Vá×£þfºÿ>2Ôi„É[ì7¶ãñ9§o˜ç'ÙE“¦÷S€Î"õu?è+·¶nÿV;úshfáׯ©_N˜ÏK‰A´7€©l*y‚:Ù1ÛµÀ³êô{×6VGîä /QöŒ¬ XèðÑ`¾$ Ì­ãCÍyálzóÑFý”m粉;mÔ+h£_³ÿ³pãÓ?,Èi|§¢ù¨C“¾\háåMvN¤Ú£ÏOZ3k­8'ôUçÃë›N¡m¤Ý‚%ÁÒÐ~m‡¹¯3Íå‘öf£*Þè@«Ôövé©”}Æu}3’oèÁ,¬<ÉŒ,}ÕNȧíÜ.7m+6s±ö¥MÏ 6•,œ­J™´IÌ¢n¾%þ~P¨‹¹€VêÐTÀSÌþ”±ÌËh2é,Z૆Ú2å‹Ú…+!£é72[¢wš`KâÒÛ í8s ¼ðòps7¾sI2õ;·ÉGLz¶0«ò{‘V?ÊöƦ-è‹úùé6è«Ò^¾€Ë£æ>€ÛU½@ežæ}wÒŸCð@pÆwf»[³Ã#Gú¶`·J6µ‚ÿ¨®ÖÁT4²Ãå!g4î€\UÃð;„©F™7±Ã©Å8^ÀßðÜ»„™¿†ßP¾áyÉä‚dô•«Úlæ·ÉUý»sõI޶jdK\4%¥¬¥yk5\7â›æwn¢6äáV®HM’A–SÖÔ¿è¥ð¦˜­Ù̃ïHŽ£¯ßÀåØumxû/| ßåöG•eõi¿å­ÃÊ|tex?K‘›(§ìíá­HGaÛ‘µ¸¶²îÎ?ø_ù7|ÏeðÇ8­nV/{ ;Ñ»˜ÊðÑ<¯J=ÞÁUÓ{j<ñ%áúp¯rwI Dzͽ'®”‹ikæ•ÜmºW3Ÿ‡vöù9·Úíu+¼Ýîú²‹%n{ÏimuÓ~ÃÈJh0炾Ôsädüài©£ÔG„rΑº³xÖ¡V²E] …èç:ØC\SÄaû†d´q¸‚l8Wq/¥’úÓÒܨðB úˤ…%õÄú}C¦w,ÐY( ŸÕ…ØŠ÷­u\žS{àê‘Ú¸mŸžÃÏô=©‹·<‡À«œzÅMÙi¶`¢´¾Ã{“^ç¬8I³ ËÊW"çhNS+ɬ‡TšØ^õ·MÚ¢.áQ‰çü Ts5÷Ê~±fµ¿5pšþäò0ÛïàÞ¯e¯ë±0ºQÖ8¸þNâ¿ ñ}§CÒ'dV&DZl¾‚±w[WŽ4Œµv¡Ðú¶âÈ dÜ•¹ÚhÃäæÁø“ìÜ„bŒ¹¡í‘sxUv3Öú*Ú÷§«2;úZócnωô2eÑ‹¥#Åõ%½¤¤ÖѨ oärÊ[cCRî`ä°4ÅÉ“ Eªñ@îÿwÓøm¨kètã¬|yÌ[Ùù"üî:DˆÔ›þSÛ•›p¿"åó¿ hØ/,þøC7ì½õøÖn°”V˜Âx³þ—Ö‡iK3ߣ­„¾ ø²(æ }2ˆ-Ä·¼4‹½®CÀMÈÏX_~· n­ùy‡6øÓ[…‡“BM¬ QM®G³bKõ-­/²˜¼Äƒlº‹4„ â%^z ~eÛ¯ ¬$Í›´xã4´§[`¶¥ …{‘Îp‚’ ¦P”§l@j;p •¼Dùž$_ÉõFp¦«ûÛÃ*¨±·‘3Ù©Ñ6Ôð–&ÃYmB@¿²‹jV±ÀçÙáU€ë8ì3±:pÉwïÚ~¤“ӽÓþK[-‡ ,ÛŒ°š€MöÅéD02†»s…»×\æóã6„òóL7³)ïŸb´îÂBÞ»€ØGÐçÚÓ¸Cf]Z‚¦«ÚÆiÛ`K\]¥(pE ¡gòM}¿ÙÅOûÎðAÔçg„5õîÔIXDµ Vhò½9€m?$‰=åû?ħ¤…¶Ä­4§4›n¶©~Ë ¬<Àš|ðÊ&•p]nì„y?Ü&þ,ùx×\L¿~;ö˜OŸ6Õ7èO6wª­ÆÿÎŒ%ëòÏ¥ÚMkâoNÜ·yójöí»1eíÄAfƒJùÞÊ+pkŽ?éç­-U… 7•·S.wë "&¤±bî¿·.Ö­œûêÿÀº¹nÆÌ£ÉÜR€É„O!iħRßï’ã®ÕRõÆÚ„9žßš„ù˜4ˆ˜ïÑ:¥£Ìœêljóaãú^ Ï-¿‹ †ŽÒ.?GÌÍìRY…ö{ýswäÑÍiÇc‚¹Tfz¡]yí2®¶\G’ ÑîøjÁ&Þé—+ƒSd„/_DýtZ›ªÁ¡Ò®«Ñ§Î&á47íÌÝÍ&kðÝ2S'Šð½ì4°‹%nD-ÔkgˆÌ`ˆö‰\kþÉ¡t—&ÞrC¸D h÷>mx…–<}9²Ã‰È6çÑ»ÃB†]xpÒÓÎ3‘Ïbñ°)Úæ?$éï–ç¸ïÓbJÙÅð òQÿOñM׿1.±‡yÍÊ|@žÔKhß7âÿ‚i ØWC¨ßò„üïtfŒ.I=I9ºrSÙW´ãMƒyk£’Ù•1ÛoŠ@š¥Z-…ö7çÃ+Û$ž2•Ÿf^ûœǸïŸû®Cà9NŒŒÿšÿþ uˆ<..9Šo "Oê›âÿÚA¢C}cTPÇ­-mþEXgO¿á׊_¿iüþ’]dj·¿DogŸñi*rÆE̵;Wç ihCP´!ùoN£[€Ô—–¹q©nRbA±¾ ¯þ¹,"ñzYZK¼5és$!NþáÄÐ…FgˆÉÍ_±ôÒÅ!FQ~̸[ƒüÿZdþ·aÂøE2_JN²Z¬P»»oùÖéK2Þ©D·p&‘*ÓÁúÊ'ô•»„ˆlí[¹7$i0ÃZvN’c¬[®».æ7ŠQ$í¦ah“}ÀàZLëYhcåþán{³¶'3HzÛsrCçºÜÇv¾GDô$÷nÀw91´i”î Þ ¸º™L#;ÖŒâàYh‹^‰Óµ ý!°&ÆgÅõ5ÔûWõ®UÒlÐW/p:Þ-+=7U’h1 ¯Þ:@©12⦹Œëá.0*PQšÍí¯“N lìÉFôØùâšÒøD!e~c$õy%+ó=HG@|5Gá}È6äé”}ážë½©®×¶D˜ÁÏé½;Û05cK¼ºªÃúŽÅÞu!’°‰ˆ·Ò¤zµCh®FX,°¥4sK±Ï»ä9¬GÂ,ÐJ– ´Ù6ÖA´ðyôõï€AªSÑêeXÜáƒ:ð"5¶U |H £ûØ©$S-ó™¨pÁ$ªj_%C™~nö¯> ¾‘.«£˜ðõ"Þ·=H³Tu}t[èǹîŠÔ×äÞ¸Ä×úÚrлÄ×ó@ßÈ…4§¯‰3 ×­øþkøþCh¡v\Ëù¥JÐqoÀÿ›ÎK=ý”nÕ™¸:õá¹0ôqÓ.1×òúïm¹od~(9Þô¨|Ó|dÔH4u¸käæ!|—5) NùßKëo“Coý7cC/vö,*g¢¿¼þO#îNãëf’˜UpŒ`Ó•¶øà]‹ùJL'¾zQv_ûœø8Ç)WZo‚q“™è![Ét”PèbîÕðiÚ‹zÞ@l­ÜÉþ`$Äíe£Ø›P5f( RŠ/èI0L <‰–¦À\Ù¼_# U&ºùåŘO0ñ—,D·6q‡2}¯"=õÑtûÿ¡ê/L‡šOíä´(Ð7Ìâ‰lØ t–¦j˜÷Õ0…š&?Áöò'fiÓY”ýCÖíVµj‡ê KËÎw¼ÜŒÅ‚ö,V>çÉHÓ£„¯¦N#'#lšrÛúâ •§ò^¾?E´'¨“ [Sõ)šP×âO½k¢Ko6•·rU»÷7Qì_„_“¾ÚžÃÒ‚½7Ô}+iIÕ$ßâ£a€éoÇö3Wö Ÿ7PœäÓ7©žSy<¡7­Fþ·Åÿî³€_4Lñ< |Ù&|CGž()®U€øz$mX¯zí¤ÿ8Šoê§‚þ"ªêê\ãßr S†G:ÏÞêÑT àÉãÄò˜O8Õ©WßQÇÛ½߯oÑH$ —æ«í—Op<*×#?ôã ª:×ß|>mI’ò«8Š÷ìÍ·Ä[ŠòŸ|‡ÉwâîÕ®=ò À{†Gòþ³ÿ/cÂ_d7:î´‰BájÈãî¼FsKuñwhÏ{ÑŽï¬a¶A“.ôäMDR±Cä?ù1÷?å ™ãXiv<:÷c¥?ô°Œ2ÈŽµ¼o™êQ é̈ÌÒÞB{ÝÄýýlj¨r à†µ¤Š·]PÝ9´© ÿï ³z6C©bV2æ)|­9-Ð_²ø»>ÙÜé£ê“mj”Üh¦T÷1Ý(—›Tw„§©·§(ñ2Ýs õÃø“êTܲ‹Â1•2=´Ánj†ñ¾…E%'ÛöóÉ×ùvçDQQþ ѯªntù‘û¹}-µä?<¬I£ºôÕ«2xAr¤Y/5¿²Èhª®#ŸÓØå³8#GÂQ$—ìØïŸg)÷["Ûü 9%Î7極h£[“6í=¾cÐ\ùîäĨm63^»á~uSŸí^ÿŸ^¬,ìä=Îå øÖB·/îÎ]‰ÏÔ8 Š_HsîøãŠò?·?©4¨©¡Ô…4TÕÖ­ê’ÞÿÂ6L Èž:äÊ,&dç$o'ÏXäs áü¢kP4½Sš13ëÜ2ƒR vû£mü®J6~ç§þö}ÚòÿGŸ!µ–xÃçD[z¤Å;ÜåcÐæب2xAî¦O¨ß3Ÿªi´“÷n´¿™ˆ–Ëæœpò$S:üo"a¯ÄþÁËj™(5|¦(Ó ×!àfŠH¹A˜+žz¤íâÒØõkak ä‰5 §©Ðc€-Ù'ËîM«Ü Móy/w×L×Ank%~Ñ+r¨Eäz³Câyç/­Ÿ¦V5ü®C“æ²ÐÁL,†Zwg…?Pœ°þú7Piÿ"' ©Ho6Á— O’vì-€W’_ 8é%#ÛÊMh³ý´²Iš—Ú’®E ÙÝêöMM5 Õ‚F6yÚòÙîùžuˆß&½ïþO =×ÿ¡ÿíAÿ5åçÏÙ¡„x“þdžÿ ܮ׿fÄj<àÕ„Gd–ƒÑRË ãMôÛq/NÓxƒÄëH¥0ÿKÌÓ´÷·™¨øíñ)÷;еXà JУä):ËsÄÛë¤ÔûÖ'ÜvLŒÀ¤Â´Ø]æÆX*ôâND«k3-´-e3"^7êwëdN±€’ʪáèD3Á|Ðfh‘z‹Š:ùz @/FÁÌáñXUbúȆW® ·7ÿHŽ10ÈL†øHcãêü˜ºùhkVï4ûq°Õø[{BeO8¥ x@|×ÄB«Ýò*“ÉÕ×ý¤Ý.š˜N¯;MGû¥Œ þ`ØÖµá0=>ƒr„Ç¢Ec¬¶)íJà:$>ã½ç5e¨àÐlÊ&M`Õ"^Ný§Ú§/¤´¼K.Å«‹Ï1èvS'§=Âç’¿ É翹v˜=œ6òù¤ÎKP–ó®ÂýîèÍÄ!‡~íÕi¡Ø…>4ƒúó4ó&ëy„ö"Þ{<ð¦‡6æ}—{± \™Ì# jËn›×vº"i`Ë\…[?AARn%oRf€´-kÖD³™eŽwølÒ€o$²æ¡Ð!ägmêüÒ9ëhN³|V)Y³éƒ”ó£ÔÏùy pÑ”ká 1†ûQÜßÈ;(/ú’] 0Ó¹?–ÈðØÄ}‰ë¤Qo°S\[ jÉÚâ-óPÅy/}–fA[å€É‚¤²ÕB†oÁ.,” 5-«ï5ó” ’Õ$Jû¢]ÿ…è8ä³ àQ÷c6ÆÏû?‹´ónü>Ìu,e—I´È]iÔYÉ“ð”ÁÔE-eÞÈ!å¼ã=øÿÿ¨P ´Â“ú6_ ”ò»ùa1ü} ªMâ6½YèÜûïôè`¸æHdÏJÏ«äÑ{áw;±H™wì uA…WbÒ*mš&Äa³šå‰é´wøæŸHQÞíÉ5b6åÕáú¤:wîÜúD³q¤-ø?jØXXWJwÛºÆü_øÆ,%b ú*¯ÅŠv +bðé^Û7ýT`J ¦w6è›ùæTò¦.<è@MÌ ‘„`m¹ÊG²[Ø‹‰°H¢‹ô8ç‘À/M(‹%m»†Á»­,èÛAQ‡Ðå#ÙÙÕG~’y†h7\Œf©l ‰tÚúïn žhïSš$Évqµ´šû,è«8O¸ƒ°Ü5TjÛ”l «IpÌKvËî)y½ÿh/)żTmNÚ;µu¯éËçhùûÚ“L¶ý ¯B.à þ”›G‹¸É}¥õùZرõ¿> ¯Ònô•ßZh°mèÛ†®éµ„¼Ÿ%…6PKÒ€­ôÕÛ¦¤_Äýégãh«šàzv& ´¤TÜ;¤ƒ@_ø…´Æâ¯“]ÑP[±ž?7 ï‡Ð4œ»;§Æœéj™I¼/DgTë$u&)R?[ˆf¯´.¥ß?ë=Ýà!Ú¶w‚yŠ¢€ Ö¦ã2 A¼…nç2‘¼4,÷†2,{;×¹!]î$Ç;ZlªZ  ‚GѳÈçþÞSƒ^UÿÚþëP;÷šÙn\Gße3¶ÔóbŽJþ”3ñW¿Ô¸`y`*\@‡¬%†8 oøLÀ½Û­¯ÀÏ á­Ò䀙9¶“ }Ê.žAÙŒH¥âÝèÀÇ‘æUþï:z±E^vu™âIe„keö Ÿ îTAü®6ÞúhðŒ®úÊñ6w2 md†ÇŽômnÞfûóV¥škK®ÅDÏ3˜1BõõfŒ¼—~ cPÓô% ãŸæ8Ƙ›n"N:†JïäDp‘½ÛÝåâø:ÏÙY¢2Ø(¹Ð!c¦´ÙldQÛï‹¿å¸ Xè+’¶©T”霾Z„’É‚ÐWQî$¿—Ú±qúÄxó ö’e¢(ƒdr rß §ÜòÛäEœ7M{ÉŒ€¶Á‡Úð-i#´¼ o[¾õyÂÂ9¢Û‘ÏÅ_#ÿÿñž¸ò®’ÓÛÁŒ¡EÚ‰·ÝrO[ŒÞB?L˜t{Ì zk­…ŠRüC;ûÒ ¸•Æl" ˜9ˆHÿÜÀaÜ9ÐÇRäP¾s—Ü0)µ›Y©§Œ›ñTß2m!ÓÒB—¹ - E¯wøY¤ŸsUDi©E.ãfÕŒdô­NÙUñ €¾J«É ®­­Ïs¶ír+Ðs,u}•BRuL¿’YOƒ+þu ¿­Ü¿éüDš­Ä,LåQ¾„ßTò¾£MÑÿO;†<[œ’ýü}Tá4~g‚¾’ÿZÉ+-$RwÚÎ1ÔÁ y¨¬iËMŸ¤=’æ7Êr ï”,÷€Ç꾔߃usÒ!¼;d÷ÕIHN–Mÿ|s….Œ­2ëT'лÝTP·ãõ å¡>´Ô.¨oŠŒ:«:vøç‚¾úÂQð¡â@_GÞ)¦TþžafóYY¼µÀ‡.‡èÐÔ†"™+;%gtvR—ì¸?ã„LSùI«ŠŸøin³@_-î®ídÇãù´Ïy)ד‘zÇ&^ÅØØ”ÛŸqSÆKejAgxCjµ™¥RL¬?/Œ8Ràl­ÆoÇŽͶÛnûçåú/ôfMÜ«ÿBùñ²¢F÷Ü«´îWwVÈüjà5ù½ÿçS¹Õ—l¸ü©)™Zþ'eL‡Á}ÐâÆo&M𔓫B|+'°ÏAgÕÃŒ€:Ð`µ5´?Ù÷êy:ÀAG´Žt‚ï<„ÁÝä&ã§©Ëïü‚jò;m‘'Îe/1©–½Îì>JÔzQò·àë@w².Û±×39RžWâÕä÷Þ»fÀçêª â¥%¦¢Z¦®s­—Í)?G Ë *-Ç-Èׄÿ6 Z  ƒsUû]´)»l;ÐC@¬ÓLboC˜>$_,VFÐQtÌ5\Jyoàpt’ÕS„SÝJ]U‘†·0(ÊI¤«-Ó"AÊæð°þ#å]ø;øÿˆB(UûÖîuùª<Àû½^;ÑóÙûK;ÆA†Z|_,}Mߎvðu€ï™ÄDX“óåÝÌ sßò€“ìt‚ù†ÚH ÿehµF3È.búÅ{”ÍgÙ & LÙ €´YÛ2 šBa×fªZK ¿ÙæZãÜ<+W-ù!”7 PЄ&:XM×òÈt/y¡ë ˜ìç™îux’`_aÚRF¼³âpŠãnúu×àŽÆÎÀÿ|ü“Ï5 ÀZzZ-BÀˆFS§,t˜äa”ÿ´‡vðÊï9`l-šWœÝs@_î`^à )émµ„; ´ˆ= s£m“-©ÿ_9ë4óE“;MM$l®ãÞ–nG~€€ìIôè[ÒÎ6¢´ä÷4’­–ŽõŒlg.N>@=mbH´0ßÅÎ2«z°Iáa&I'¢.à~ ¾bAù·´½-y’Ä8Ÿ !Ç,Zí­ÞÏù·:Š3´ÐSò2uq9þô+;•(wI6 uéº|GM¸—™—˜€OV;‰ò‰Où=äE˹ 4zŽrÎ"^¼@‡ÑýŒŒün^ ÷1×|LûîÆ£ýZè=œ­C9ÿ}ü!|"õ×ú"7øV\¿rï³/¥:v„yÓ4ï©oK£¹ºgV ¤Øý¸_‰»ÊÆG2;Â7<ŠŒ0ÿÕJÀR&Ø’:%ôŒ? ³#¨; û5“}Éä½2.ÒVLM?êç^n7¦_é{Dj÷¿Û;&›WEï`Ö¼MeWú õªCÛBÝ º “ò ÎâÙ&á­ÌMjË1úkíxh(inŠËDHµi…ù‰’è1fzÉ1´§ÈÇÑ÷ŽÄf4 mo¶¡¾ö^fn*¹|¾MÒ íI:Ìe-°f_U¿,§­c':/ ¸¦=ìdæ%-^&¾ãGúH‹óâÿ =·Òÿ8å%é®b0ù¿Öó±×~¿a·ˆÆ09ݨãì㌴i[þd^Ú¼ÉÉÜNʈnµ¦u¨_Õ¡N™Z­ñzÎý©“u©£Ë¨ë'¸>Ï;ŸM½o-îGû˜[‘¥Ç%O¹gß´‰4?Kó;@sX=ñ&OÅ{=’ì´7²'C{îÞUüK¼ÎÎ, Ëƒßή0u]ŸEÚC…ê½@»H…¯ÿΧXLûx»ÁfõÏK½c†YÕV»3&hUï”ÿ±ÈÐ.Ÿ©(¤8gµ¤#y;ÖƒV¦‹¾kIÈ÷‘¯Á<`Þssré¶(²œçë—E¿¤j×ßðÐì3~²_#žp¼D»c/f1|e¨9‘­LEÆ .#ØŒ3ÌÏ?'KÀþ;Síµ4~)­,è«f×gösãÅßï <з¢EÉ_äU·Møeƒ¾š¤¹ ȧÎRˆ4©T¸bÈú*/lŽ·¼G<¨!Iµú~¤ÏOˆÖ@ »h¢ºº {ˆ[#tŠ¢~£-ò{òEŠuöoh&‰g“@_Q”òiKéi{­G;ò]ÒöhMnæfÓp4RVÏväy _kŸþ¨lÇê8¹«˜$Új@j—ú*™!]U=ÐWn£²¾_n"»o0Á—½Iüt-`óå|ÿiîwÉ©ì0 ú–¼BrA%gVôUªwóQ÷ZA_Åü‘‰ Ÿ<$ÐáM[÷N¹üˆõ¦]¾7=Ñ.:æ}ò -è´&pfbÚ¾m¡è«[1Añ¨š›9îö¡õi‡k{^WåJæ$þJä`•ÙŽ²ó÷ Úc‹7óƒ¾ÒdÐa^÷ãä}õÞ“Øj>•6[N=Ì`KùªÒžó‘Ò‘v\6]ÁaV×ÛºÎöANB£p8 4•9™~){]蛫‰/ ,M×Ðoÿ½³€“£Hÿ~õÌìn„à Àw9܃»kà°Àáàáà\àà€#p¸»Kp9,‡ $!¸Fww¦ßﯺ{¦»§{l7û¿<Ÿíîò®®zê©_=õÔ2) ¯BýŒ¶‡ÌM”‘4'Z†ÄîùBe žû’Ÿ±[÷ÕnËÉãUåî ¸4c—ô˜LòsÛ¦{Ç}Úf"ýFܵ³޿ôÅ t¡1c|p 01 ô%Ô`ö6 jÈV›ÌK@€ ÊPi2üÚ®°NcMÓYþ½ÆìÔû¦Dd.AUw€á%uˆõ'‘ì¢æ˜í\ìáFÚÀ̼îÇf0g EKw×Ì®æ³nÿ6÷1ñZßçô—M>Çl1yÚòÇ6åã\4rѤ<{³µ*S5šn;V›”2‡Y¢ã:ÜDjÛjÑ"jW6|{¾Í;lî9ÿó7’æÎæ‰üÍæ“)ýñ_‚¶s:¾Kšsšn罚}I´@Ù44ˆU~xÙ4"Á 7ÿ,`ëåø“†µU¹ŒÎ}3{ãws,.ei¸÷ ï”ÿ"ß©+4ù/p`]!y\Mú+Ë :ï³Âuȧâû_³›âUs&I™ãtÇUð?ÕÁ(Þu)ÞuG»=ùoù§Ég{ÚÐNøA.ÏEЗEºF)³ï¼M$¶Z^ˆ ß‘ñ£"å¥L¯Fƒ°(öÜfÛ¼h¦íǸ·ŸD™¶DŸÌ˜é-\Îd®¦í¤ƒ¾-^^ÚG@kɶ/RŸ%÷..ïz÷áÿ…‘vÿô\T§Œ¡5“³íæA‚ÏçEqi¿ØÞ¶iä¦>> Þ6æ™÷…aMN·&WMÀà&à£i« ïÀëQ„ïà Y3:{qNœÿw¯Ý^ƒ·ÑÚJÀŒLûhÇE-$ÞÝF@Mük¦,m&/0ãÍP¾³ßöëvZ’3å¯3åÛ¾—¹‚¾ù¼ø‚Ìz„ƒ[µïC8€™`±=ðŠöQFËàP£ÃI{‰üy´ç¯Ì•ðÀÐè¼Å‰Ý¬–¹À/Íz|{A)s;Y3<·¶‹§˜ÝšÿjŽº«ù`ó9Þúƒ¬Þ~ŠéG»÷ÀéhFêí´Ý&úîR„9Í—å0ÑXtÅÂG×ý>©ÒSÓÕÔÛË|/®eÔˆ?–ù$9Øþ[Ï7S"¹‹È{,7pgÐ>ŠM:Ðx²þ¿y¹tú›‹¦¦2馅½þÂç;†o÷ßrϨŸÿÔ›ëWº—ù…Üš„{ˆðCxþ;ŽÝÌbh¿­‡œÞ^>¼v¡?o&>Ÿ»‚öDÛÎU*ä¹;«õÿ¢(ð-ÚT„¤EëÞï;© dùM¤ŒÁ)û´ }?IªgÑÂü>µwÞ¿þÔË"¤£¶¦ ’UZIv?~K{¿);áøj©Õû¤ÿ¬Ël”™~Yr ný43‡¯‰w¼ ð]ƒiòw´SvùE¾9êOpáÂPØÐmn8ïÀ;æOöç ºŸ ÎÀýîb@\YÌîÅiö1]–Ðÿ• ÿ+¡FóS}vðëbrv6Íh†·µz—Ü`{m³€ûšUΰ¼,s¨—qá2ïê¬Ë·=wÌ3岇ÆÊÖt-uôNJÝ*ì,äÅx%`ÞšªˆÅïìcæò§=dÖáz5©Ñãäì‚~#‹tŒ‹Å:ˆªå™™G³ú}£ÀÂÀ ¢m9·¡¿E{;þ8'¼rµ½ŠÄ²AËMTÇÙ|·pIŽD/²óæÉ\5¶­G¾ÙÖ%ýµÇHÓ¶rŒ3Þüƒy\#BKþ½¤¶ Uǃø^÷sˆ3ÛMÙ“sž4‡ñ¾‰ý§Žd×AÚo3ísšxîÇ3¼¨Ò8=2ÓËÏ?Ì_Ln´’ oÇtÜ!ùï­¢D+ícmÜþÔ7Í$%ÞË(éàø µÈ<½©‰þ¥…Òü©Ó;§šÒ—RÎÌGÿÆXSiF¨ù¸IÀš2ÒÈÚ xy饗Ìo¼aímNš4ɦ#¥ù†Rü#ÒÿZ ¨qv%è«÷×ÀõGûQM4FÎgE°`ºX•¾APOeÍò²Héz¢­k·5HÌo„Ai!RïŽOÜfµx‹_ññœ8è«Ä¤UzTHC$1ƒ:ŸD+LB‘VÊZí’>ù‹ƒÇâuÂ-Fý„IÛ®¾ÜÉþiúêyÖüWÞéèÜ?ÎÊ¢ü¿•‡O ú Ю• ïLD:! ŒË†“{7~Z!~×}‚ðš (C¼4«!m© ч ü9Òà(€®Òà¤h'"ŒÈ”D5R9âÚÃÕâþJlY‡ðí* ÷° {Ov.û´}wïsU¢–f8ï^ù$Ù{Òv mÓHÛ1•¶@å0 ì}ˆï¼9§h_ÍÀ|ki×Jè+‘4‰g+ü‹‰Â=ö»ÆÃNÄá÷úª|¢5(ÐW‹:1»)|½ ¯Òüˆï±)ß?ÐF·ùt05—½Õ¿i è«g{ÂqÛŽÜ} G³=[¡# ofæúƒ­ŸþÌNÚ7z™þbÉéÍeN{+‰i9^¤±«ϳ “Ò‡Í ÷¿f“&Q‘²Çã½hÅ ÖSÉ ‰)}~o´žGk ±=!¼zòF §L5ü^©íÔë3^é²Ç2œú.Ï;lzƒ~Ü{ýM¼¹.ÐW©X-¼7¹ i^ dóA_¹ª0Ðð}xïk0ãWlÎÎ…]ÐÙ;3ó·ï­x†ÄéøJ”æôÕ˜&õ¢ È( ¯¶'ïÌb]X`×LN…ײë—üú›‘ؽò]Ë"Ê6í×¢ 2e}´Þþk®!ühÃR"Ú ‹Áó?ΞB½æeã àÚÍj3Ê.ðð4-0½y¨³«Y‹IÛÚôUð:'pZô(ÜãåSöÿ3\JcšõF”´,¤’]ƒZGµ³w… §‹ÌªÎb,¾äÉQjãd–!FÐÕ=S!Séõ‰€•ì™N=–þ‘B_îí&,ü¤ð.<à¤ý î_çL¾}i’«[ôUÜŽÓik—©çEï ©ŒŠ ¯|ÔÆò£>óÄiƒGI‹ ÿä{Èg¾ ׿¢å…1Ðw|oº€´¤IŒ¬aòï‘æ­øŒây®+ò±œ•pk¥Ì#¸†Èò“uø€·PD†g’D‹[xsÏqø÷EËÜ`ƒyv»ÈnÔ–ím¡tƒ[-¾µìÿ; Wä´ ’D¬Þ”ëô™î¡‘Û,ß-» NïI£nêgwáÈ”C*aæÀ‚¾™Ýø.çl‚ÔéoŽÎd†Yp9Móºç8ð°içq]–x´­$ê¸ÙìVxÚŽ7aïRßùžpéð«•dO:¡¿”G‡Seó4ÖÈÞ2í:‘ÔÞ&Ó:ú*e¾Oûòz 1›éá(-IÉÉ:€«ëxüЗñÁ†Í}³ÌúæüSf^ôÕ"VÜNs8=ÏÎå¦EôÕýcòª”á#¥rÐW!iµŠ—N“ñbœ™r˜ù¥p·ùœyÐ4Û?ÒcÌñ›ì™dæg]”³´º§mIbó[Þu3ßõO]”xõd¤bY~››\™[Z:ë'ÄO û‡{ù7Oª´j®ÿ¤ø¸ÕVÏ]UOodæì²ï…® °U–Û…\ÿ*s¯ç°M™Ÿƒ’Rýªåç[€u.ö¯N¯Z~ÓÛ­a&ïÎW?å`û¾BS|o¦.àª}þ—3kÑý$§‡›ÏÎ[|ÖûD¡É…[‹uçP(ÉH˜®|oÀ2Ãÿåé;Û¸ÆY§Üݯ ½«Ó.‚LrgU÷T§Ÿ«wWygáÇji1,§Ä»/ÕØ_”§:ãzïßßÝžü™øZ÷µý´ÙjçþÙé Û9> þ†¶k1MõÁ©|7ÌGÝ.Ãÿ.g¶âs¥oXä¢u^SØJéÔëÇA†.6gx¾lWsßÉÌå"8…ò޾?Úá.5÷«ÌÅE -ÀuŸú;ÍíÛíUwå„~U,§³*õ²uyºêãªß¦Eý2{¹½4æõ¤Eg žôb*Õ)ïgûÿÌ¥øA:Å«øþ“„[;Æ„>/4-Ï㯅 uòóú@e™4--ñMlK×W¨½…òöÒ¥ÏtEþ;”ù×öie¬ä·‹áô]Õ¦¦c9ÄoâŠRšK‰·Þ@Ÿ¨Å–)ƒÏ·Še¡ï£õ¢¹†æ+ê–OSÒ4ζ>?‹æ•V†ß“ûpƪƒ+õï´wîR÷e’yyèÕ'wü޾:kåÏ]ZŸ¿}ÔŒGdv£¯ÂÛ¦ÃûkN•¦k½$'P¦Ç‹þÿ˜&Û‚]ý}÷ðJ¬&ëÔ4@'ãFËðG¼_ŸžÖMå|1nÛÛ9í•Û#íì]&ño€Åq÷ZŸÃ“¥ZãLÏpµò-¶uºI+gÒJ*_ê%ùÕ⦕<}C¶o»l¿MÌC“Ö8¨SKÚžvNsQkc “¬ >ÂÚBÒØ½᳦ôR5œÒH“Õ锵3i…ã 0ÃLAÃé©ÎF%´m/o«òVZá–Ð> Øp…µà”VHÐ ô·ð»$ÝÏ>ÆY¥á÷KJ³Q7ìI×QŽ BhQrPa9A••S@‚4ºB~Jyû%ßWndž·â^C›÷€+GUÍG‹ s6]K¸ ”\ƒVdr?ö&éÑI±@g;GÃM|™mɉe9‹v*^€ÔŸ³°¶P1¾G’†\V øÕ{µZtÁÂErYÓê¥wiÝ×¾Ö°ÚQhâ§ÆiºŽ¼—ã§ñ²•º=5T€£ 9u.­ÍzëÏŽõüÑ‹_g\o R¹¼z×B&;^̬xð ÷K¼k$mÚlóc”wwÛ¾ÔŽÂ ’š²A¶½ZíÐIï)úò6vwǾÎêܯY,C^‹œ5ãΗøŒä]ÞFÄ—ëÝmS`Ñ#2ù‘¦mÕv½8a6¶ÚÌÒ^Lí—”W;€¤Y-û²<ëWþr»€1¯ ûa2‘ç©q¢iµP·#i_'„€EüÌF:B†ÅA/—qÅ-À)`ÒjŸ®ìƒá»¦ç-Ms 0ïøoAårù/ZNÃ)aºBÒwʦv°Kz^f‘r¿ÜÅÄù“ç.ÀZå¡í.Æ/ÈO¼0è7o^TÃX}vîT÷*¾u×ëÇúV}Ü›p™Âa¨}7½Ÿòƒú‰^5îüù4xõ‹áÅzÃÍ·—òE3ßd,=ó^j¿øÿì>šºØ(âo¿sÓ­¸¯ã»Çy e¶;H›´¼_^ì¬A˜…‹aÚv^ƒ2eO¤Œ+Ó-•…2 Ðî9¿¥|)4t÷î‹ 1õÈÁ{vîºßN ùe²½ÀsþÅúôòQ¿æ°Ö2÷ \\” Y+Oÿ‹ð2›·òj¬©Ø¯Jõ <Î?H(cõþ¼GäªZŠÕÅÏ¥:¯X™¿Ð?ï£þàë±: ‹«ñp¿Ëçܹð²}ÊÞ)½¬õõoi­'¥¥maEQ)_HQ*)ìoëÖœ´ôcß=é9Î;’„ݒûx©MŠOa˹¦¼Ãé¦Ü—¨@ Çùã>¹.§W½„ˆé•Çé6þM­¦F "©Ž_`r"€íîN"€*ÆÚ>Ù³Á2&•û·vë ¾%s'Ô†ßEÀ–÷ÜêkÏ4Þ.F¤°2ëpD!4\ž¤ûµˆ/wMž5ékÚ¦Ž'3a“©xÒ` ‡ &ÓÒ¤ »wæ^€¯oi³Ï˜Uòø«ò7”–Ʋ àU¾W©õGõk‰P‚÷?/MA“Gmõ Âvå5¢½m¿÷¼óÙßY´¢±lšð…4½Šîä¹·õjõTø¤·qöw iq…·DÆóMyÖwšÚž¯I“W^&whoEÊJC@¾42ò„ÃH›Wi±Ú/?}ÿúØ%±°¥x½#iÈ]æUÔÿÍ5 xNxBå*¥[éž÷k~€°ÞÄRš1ñ¶Q[:Ê ¨ù®€å5ç_©la¿¾Î*< M[ëm1ð¤ëó —Á¿×8shŒKÓ©÷¯aá¤Öô.šÍ‚îÂh4b@“_ œÅÁ³5µðgÐH3{’mÏjOÁbˆzµû#\¿½M­£™ÜÄÊÔŒöá™hÛJ›I²Ä,ôdÌeá‚xš´Él›vª¨Í—øbkJœ®æ»¢ï”M×2y({2@?ËsÊÛ€vÛȤO8 Ó|wˆŸ°Õtþý¢aÂõh5?—$ΞéaÂáí=íGÀ©½÷y¦¶Ï[-è#‹”‘²w{“òÜV%Ÿòï¬|8ÖÝYÚæÊ#7¼r份N˜€Çúu'€Ûïß^[¥^2æ]¶+¦w3‡{M¶Äµä6å.Ú4¢&Rß”M2˜•qhë:Ë–É3kg5hãõø‚4ü¾×t=ßö˜hšÎJ¯›áIZ8WþœóP”¡ly´h)IüìOÀÒÚ¶ÏKY@è`ú ç² ž,­× |q¡€þ›9Š8‹•Ò Òkä*^mÓóÊ$ÙDãV±Œ¤ ïðÚß8)¾ÚWÓÍ%?[ÏõžŒ`OŠWÁ­»4«³~ÂUJW ÷ÒŽÕŸJá¿JÀ¯Ìò|ÁÂ«ä¿ ü¦¸C¾¨êˆWỄ5k¥]ñ_ìÜ ÷‡ ýH?®ø­´-ÆÌ4‹Ëö¥ôj«‡?Â×SOô_õ™ä¡îƒºÓÜH¼£lçWÅïX=_,íG€Ñ ¿®¼íY»jSA>oy–öÇxTÃûI.—C\éQ‹šÀ/µ*HG¦f~¿ ðôÞWï §˜¤%S~2M•¦¦pÂ%’q:yMnx] t²`zÁÀÞbgË É~µ4´ý»Z˜éá/{7¤[i»y8=1ƒ®PÄPêÙv.ËÿÚ½²Uç¾—˜Eµwõ¶*–{m ÕO6aƒ4f  V^– ÿÎ^ÃB-iu†o ôQ}Iãªl¢Ç·0SÍÄA:iëÈ9€µ”9)Œ¶w…6œ4›¥ ìÊK,aÍÖ  Á½ê!È_íIà¥ü‚•Ù«I3›t•©À´D’঴“´‡¯ +v]Ô¾º^±ÌA™t}<Ôe77À5Ì&VX›.7~/! ¢­Iò‹Ç©åY  ç¤°20æ® ü"±8L–ë*C3ᓃH:Vk¬Þwëãî϶׳hUdÑ´,\‘²¤¾ßÒVØÒd.?;[¯çeÛ°ž9i×ÕdO9mã G<¾Ӹɞ… ¼k1/= ŠÄWP¿¶ø ]0±P_¹ç‡_lRó7¥\¨Î(¡Ä)Ðo'~ÈQ¥÷¨uE1œ†´}ÂÏÿ¿Ý«3hðý¿úÞšTÅMÔzØR­u"ABànRø4Æš¶Q·JŒlz Nõš¢è ß™h¬“[£u]{<@Ù Ì? ä§ý fânœüZæ„ €Ðà9éh™%ùÉ­•¶œæÞ"'3áé§š6}gA¯å*€PÁ6üxÙ5Ù{>Çýgäs]Zt+Ÿ¶œ²Ï™9,ùûÈ&¥Õód‡TÍR5öšÃÉŽiØ–eJúÒÒ•0[±? °f¢2ÐîL¤v<°6\. Æ‘h9¸ëéLß^¯3Ûwðîl€ÍiÏÿ5€¤±ÒÄ—vXÀW¥µî7á~.KÙ=Z_« WæžR?u‡ËéG]•^޶\ à žk¾ÊC¬ÍV‹›L^ªìZÓ¯¿ôÓ'F“5_š¼)4˜<ƒœÉðkÅß7tÿé¶;/“ç×z©ÉÜ7úÍ-̬ÔÞסÚWâÒT?gc/÷?$´CÀ3K,®Â«ŒJsB(M¹×J}iŸÙ·LŽÑÈ».fræÓQ!Õä¼öÜsÏd ® Q_Ÿ'Ô—¢ÜèN2Ó*Ä­æu›3»ÙÒý¡Z°ªþl‰1 ¾©®cޝkPr,Þuøw4æ¹”Zž9ÿU1|pó2µÁÖJó|Bœ×jh?Sƒ„R®—8³™Ý©¯I þ¿@ÃÌ<çN3û¹?Cñ¦fÓdFGzš1ØQ6S(×8·Ãœ™™ÍlPøÖÔZ‹€¶f¨3«ÙÞýÑæ3ªÂû}˜Ò~ÞÅ}­Â÷Årþ–7_¤”1±L•;»O#F°””Dî0:~>ÈÌaV/|<–®ƒí= ºùˆú/l”JÍfäñmÛf$Qa4ÊìC_èg¦æ‡˜# ?ûsbÛÏífLÇí€Ö< ÿ úþMj5nyËy‡:}'ܦô7ƒy·R[|½¿º?›á´ïœû޹£ãß‘°Çh³Ûä%ÌÓ´éãÜ_­û´S€3øj›Yšþ¶‚Ól.p§X¿¯3½Í²…¯Í¸*ß³oÛNæ| ÃKÑ…7îË âOULI»9Æ™Åìè÷¡ŠÏ<í¬Û¥ÆL=–:¿?p­áúE abAÄ›Ýê­ð2¾Ë‰mló£yÛÜ‘f4æ»&ŸÙ‹fyN eŠz@cTÛƒ¸­+ä¾ÍŸVz²uÝnþî´š6÷3Ò}¯ägú£„{¹YeÚFfwg&sdf&³~þkó&íé„Ì,æVÚÒÏæC„®xוù5?t{”úÝ’4ÆÓØ3Ïgç5·&›ÝÌf…ìþ䳟 ÓâlËT8®ÊÞ[–>W¸›w/ñ`“ý ýll 3¯·íÿnXòQÌŸÌSî2/™Em¾¾0cÛñGÅr%ÞdF p ÚŸz<©ýÊF»±4ªÝ‘÷Ý•2÷Ý Æ/uSW ùCºR®êë„Ð+š+Ì\f ïj²Ôaû“6 6ffêð$ú¹¥is™FRãùòÜüÿêÏ} ÌmfÊx…ïú6áøU%• ˆ¾ù`’WºØQæÂ¼È„ÛAr—“õšÿ?äC™ÔÍÆåMÌ‹ÙÉ´›!æMÛÎ2´«¼Çï7d}‡wyÁUÝT ÷¹rO Ó™¹äž;ŽöJ»r¿$ý‡p³äÇÝUôµïâ}ÍÙ™ð¼Olœ‰DŒ=è+Üë—¥ }à1Ðh'æã’säŽüòü|¢ÆÍF΢æ?™ ÌèüÅÔåUWÙõUêé§ø;êp÷Ò<ƒ» ñ÷æ}þS·‡ó –ͳ츴ë1ÆL^“$¸Z"¿õë&ŒÅ~ˆ¤K3²O7•±9[ð-‡P\Í·~è9¹öâ÷™ÿœ~™‹±m)úÍœ…‹™”ÓÈp{î Ÿ}«ÓL[™´ ž¡>š=•ú¥ÿ»/”Ep'cË=|£gü¾ð)a=þ²8rÆ®NOsíe|‘çÄ’rŠ9TTÛWŸ¶”Ñ\¢´+ÔæÂHÚ!wa¿ þÍF/ýóÊlb<½äßÈÆ’åÍ+µ•º>šÔ³šÝ[f9øñ¹ rÀDêä?¼ïwª?x ƒw)"w’o4§~Y±vj%è˜Úƒ'„Loµ ÒoE¾8oøH1&;7‘$]£ùH-t+-áÖ|R¬%vy˜9pZ•z–L]1fÊ¢p Sf€‹ÒFÿ¦î‚Ϙ°o•{Æ5g êçî*áþG½ 7#¹;5T|æùXÌÏZtÀÞž%æ¨#ë¾¾îO„N">@›Gó0ØH€—·–ä“Þ³–xIÀoÀ·ž…oÝ›™Ý ü¡c®Ø='îÅóÀ~«9á¢.Ð,–ÈL<ÏÃ×ø¤ ÛAEZ£yÈÄ,Ķ5Éë8Ä`Îlð©f#Íhó;9sž³+ñuóTfÀ¨Rªþ¿Ïö6³&€¿AÞµ\Õ61uaæ¨ú-h2•0;õ3.;ŸqòL»’Zž§³m@Šm]™ji!Èø“ÿ:#N¿àÎ:0 ÊU@8K ûX¹=Ö‡XBl”ØbÏ,ÔæSšÉìÀDô)¦–?úaÔ'o¥ [‹2ÎêH=Ìbôô*•êYL”>öÓG{Ç\ŸØ¶3¡<ÉÂUÆlHúsÀ/Ã@šoæ0x[ŒörævÄPÙ'jZ×Ä­}Ï„\~_Nhš7À?çå%&tዬ@Zov.½Ì!à5‹ÁWôÓA‚iy†þ¿;ÏcËÓÎ]IØ3£~ÎÖL¬á·íŠÓ´´é ç_žöq ïîè‰ÿÍÉš» o˜~N“9ˆ1ny -ÿÏnö¼}Ûýȼ™Ù›¾9°MÞ\‡´ 1Ó=fHfV³2¾×îfÖ'Ì¿m!WhºÙÌÔ~2y}0ê˜ëùV~MOsq¦É< Øq¡@©€œmè_¯›q™vÀ)Œ©¿Ö1/26,Tø …‰eö4ûÐ?û^6'g¶Dè95ÀÙˆg¤ 4"utÁcsêa´y«ùvÂÎ8½©ÿƒ‘š½É®$ßI~*—Ø)eÆÐ¿ká ~4ï’Ù—´%“xVJÞdvm³ˆéŸû‹¹ªã¨bpz©Éd‡š …Ç)ë³Ew{Ós*õKŸÒ7ê~r›ZNDˆ[Woº¯þ<ª‰ò8>q8׉fA|ÄOâ÷ ¿9kQÉ>%03Bõ1¶þÚð’°Àì½ÜóM´a—W•í¨Û=”_íýSêm=Êð4÷óRuÔ_þQ€¾eá%~7ãžDô³æ[Í®÷šu³kšAyê15lRü7gsÊÕÚ™FˆÏdšës›%òWѾw17¶JI4ê¬qcuªþ¥š ˆíÓô¥œy‡’0Ïüƒ«ˆÇ%óÞû&n<ùõ0Òø{5 >H(veG_È -PÆ„î1yÆbûs·?•¼é]v켿ä”=oú'D¢íp+”ÜSîJ=(%@Θo°s¯[ÊÊÛ@bê7ŒÂ¼(mã–P<ç.¢ ?ˆ;ý»ARéÔÜÀÙž¶±å8¥XL¢™C2½Ì¦Z/ºÖx£>*Žì¾‘¾XÃ7Mˆø»uÂîA˜çüæ)«›ÅÑj¾—]½ÚŽf2H]úŠæ& sÚ´ü07ßúãÜÖæxgqóí³vö»öÌþÍ\Æ8£…šeœô:ÖÂækÙyh×z‡Ù)ŸxÍlÅ,}Õ›4Îi1¤DéåȨ;Ü$›¥í–µ‰où†!ÐW æo‡ÇÍøHuò{Fõ€u„Xz]‰~Ò%”£fà=Õ¯ÂÄh®E@Ò°-G…¶Žºçìó_¾qç$Ñi¤8ɦÊùöú22é%…_ê}ÛÉï ›Nù¿Ú¾iy¼ß¯ËpƤk}9#Á`!ò"§0‡ ï<Œx&=hÌš&_´ÄáŸÿœpaÁ“B INæ&xêÉÎwÏ“%Ïf±UÊh³¥ä¥yRãÄ …´ÒލuØyÔ(è«d×¥OÏìmó`ü<–g¬©ôÕ¸˜e‘¤ý.âý`SJû'Þ¯¿oJçÛõ–[ÜK1°šük$n 9hü†=ÿÐø ׆w¿Âìç>ô%s2“Çv¼fåþ'îÿ ?jà¾ûîët%¨ë÷ M…Áß´D·ßzk&B†ò|)˜Ü»Á%-Ý5à4jn¥ž,ÏÝs¯2-ÀJ|KiK”–FîôIl¢Ø^³9€ lØõ3ç0°~î×¹Ë %ÓÍÍýáLÔ<Ï(Âþ®ùÀWüTþ|†ÖÔ¢fÉ@ǾL,ï@zMý+êÐç#³[ŠV¤¶Ö£Ýàzp…mg3ªN°‘Ç<Á­h6FoZlj1÷À¿úõY&T»ÀL¨´zˆ&&ÓíL,%ý¶çO¬wF†8â¾6#s´yÍÄMb®˜s+¾hõeÛÌjl¿˜IüLˆVaÒ{˜Y?Áˆ¶Î¬'/  °TÅTË=5É'Ñ—ïÃàÌ'ù ±Ôü˜¹?ÿOsgþ>3‚ə̖\ÄâKXèÖÔ;’v[žúÓ¡Í÷˜Ú†PΑ^þÿ0\Gìíµ˜° i.RƒYñ„Z̳D2ý?óÐ4ƒ±wÊ̹Žbq‘ÿf}écÀ’Mô±¶ÊßVàU3<½] “‚¢)ò VÛ1³ aFÞm4ÖVªI­›ÙÔž­B…4(¦MêËë_Ó?i3ñÁ)žö4=Ý'Í’,âi|+b¾À½¦fï¶z˜‡` 0e¤N"3·1&ÜT\èêU¶'.‘”‚çv'é<͘8\ïX`"Ì–ptѯbjj2p¿±,¥X.ÝÚ;NñÁéX-¾hLõß?æ[|Ô ‡ÂNõÇ`½ÛÕÌwü#^€z×¹A³›}(Y©}“)Þàljþ¾_á!ëhða/“]E³šÍ¬–­ÜxÇ;ÆAGy;ëÓF6ãÜÁuOd&ü–4ÕÕ”Ùû~â’WÊM½|g~³,|è¿!Oõû-ñ%qœíÌÏ–ÛÞæYgYÞëZ\ } @JoÁÜà{wPìÇÖ[Ïæ?¡ô¬cñý w!åý.oñƒ‡*m;ÆòCÍK{Äž²…Ùðôcêy¤?öâCêNúVw›ÂüüÏ/ÂM­OñŸC»qè/…K,<9Ðý Úë·Åº¡¬!—Ã͆Œ’Å(µY‰g-Âõà^mY‹ö› GS®-tãô3ï:Ÿ›%³+›óh+·_ʲ§mÍ„% ¢ÒVÏ7·5¯dæÍ;f›iÌcô›ÏÕ?{ÜÊnù#Ìwù{ªî„ Ú’Òk˜²ð«Â»|‡‹I¨=|cZÍ$Ú‚qX`ÊÒöÚ÷Æ_3Ô$Z^1ÔÌÕ~ õù]R€ÿ!7@ܴźķ`<°ËS)u£>Ýt ãÅ„û&19î»›609¢±1±Dx”Ìð& òÒÈ9ŸY^„”¡L–—vr+Ï«ÌÅÙ…¼‚w +óꬃ4rB3¾8·’Ölvuº1uÞ…¤þ¾3œãAtó“¾¢´{{À©Žaž×e$~Пm;€$KßHùÞx ¿jº‚z}–ïˆø`S(”y3¢L]™G ‰UÜdÚ@@:búïŠ2™Œ™8Qâu‰ºuëfZ[[Ík¬aVZi%3Ë,³XO¾ƒ.¬áN í¼w\x¢jâ8'L>l8ðÓuذafkÀß0wÜqáÇézÞyçM×ôTüK bOêTf)>` LÚ&-`Õkdëjì-Þ¦åN‰ÒhI èÛƒ{Mâ̤Á˜d·IéNá'_“ü8èË £æE&4×…ÛÍ îû ¾)Níç2MýÁ|ƒM×ÙÙm  Om3>éLI±g&Òþä?5r¡¿ã MŒ5ò€]@ÿ`1åKÞw(“÷$ŠrY?„Õ:À'ØVcrЂp4mgsEæEsßçCÚáH—Iœ£Z‹Oæhõuƒ¾Ê{*?¿u{Ç<evb!é/ô‘umú¶mÌÈækÍk;ò=úC!²,ÄA7Øe™ÉÛ—J°eª™‰më?`—5Lk2ŸÐëK³å¯šÄzqiúžLÿ;׳‚𽹡õ”Ñ©„?œv<&ηÊB¦8(tÇ™CÝ»Ìe n9NI¹1gg3¾)Ú­…kjˆÿ rUÚÆK¼™¶Kˆ#ê3žù%¾JU6F§$iªç¥l_6B:\Eíô‰ö¹arœÝ>2Ä:럶åÎp8+éµÀ¯­çL_Ê·/¾c¤õÁ©){Å‘<5x·6;˜§ã++œ Ö‹1Φ-SìR ü·lûÓf rÛH°®°:ïÖcSÆ1 ~’1o’Ù6w¦¹¾ã¾ñ/æý ¿d~5Þi`¤lážÊÁMfcıŒ%¢½™4ê,€ÜVò”Md5þY[œz—ÐWñŸÈ°=žóõÞg+ëëî÷f_ík##i:t:Jóò8€üGð®2‰Ô›þµ}Sc˜v½Ü@ŸyËrÑ~`‹çS÷¶¼g;˜‹ö†8$êlÑÜÅs˜ÏøÜõ&Ö²½;³ªé°+ÍéƒüEPmÙ×Ywµ”£å³O2Pà» Ü,‚¾ò‰ö^q, úæ†É“° éM¸ç§äϭŸ˜ÂÁ‚×í|ËáÙN×Lvòäu­_ÿ³ô…¦u}vø Mj=JÑa¶§Îž Î®bAsÍ€ú”âïÂ#å…‡fwà;À³dƒÑÒßöì'æêb¼¢7 ­nfªä^šôõàá­Œ7,”õF;—~ª¤1;ndÿŠÛì”ïX/¸4úó¯óîÃí³Î™‡ï"Ú€öpý•ÐL;ŽtV§7.‚)¬3Ñ`WÛŽ€7?Ê7^Ó ðt'u-¿ëᥟrwÇ#îøü‹‰w’gª¦=G>ö —÷˜¼«™Ýý9!  (ƒr LDYd60Ûg¶â]þBÀ u&ad!Þçsóú×%´éïl’sð& žÏÇ!´I™y˜”Ù|c¾ù¹ÔÉTÂØ·´1Êÿ}aµdÖCÞ¥Ò§*Ø%.|A³uðmQ»ô§Eiðð©‡ó=«1™ï­æß¾´£]XÄQMEH}º_Z”ï&mkm+b’8õíˆ<³¿÷ÊrS½,Â_‰[ÐorkR6äËÛ¼ÖP±+l¿óæµÑäúó˜Ä3¢¡*=}ãæcs«hþ¯«¯^?™Í;Ò/Õ´ø'i÷v9i‘²íQ’eÜóIZàï²0¹4&—¬"VûþWÕ«Ìÿüýè;71–„¿í#¹ÚÍ߆I H™ù`’K|’”–3@™`7AækÂÄkóD¢®°œl.fÂVå§ ó˜|{DÓü… m¯T¯~î¸LÉT%™4™jg#€Zr£kòVã‹ø2qõP®UeÌ &NIºVÕeºè~lYí†À®³Ñò¼!4ÑÓ–<Ù’ÝÅ’ßËÓ¿· ¯ ve#s#@úªóšŽdŽ “ö®°¨ùS¼ Üâ׎ã<—Â…Þ5³/ÛÁ[ÍO!p«%så{…Ç·ŠNi7ÒîÊ[vZÉd~þäí*Óq%ÏŠššu†ûaÔT”&ŠÚ­ÏQw=I#ð4&òuHNfêrÅ'Ù倾¸ÎLùæ°‚üD«4S,^oè¸ñ¨9qêŠæ@)Ù…ÜÛYÀ\Ðr޹ }0%_Ls=úÃT¸üV¿.ˆ[i2w õŒuàÔ®€;%´³­ÃšˆÅÔk¿iÆ.økÎ/ôMq¢PמD׆”6|wxJÛ|Û6ÚÙ³Uú€ä´C(Ûu–ƒ `5Ð@‡ µ=J÷]ÿ^_;:>„Ü8‡Ž=níyÀ˜€²CÍ’Í{™‹¦m‹va¬ß䘌aÁ¸L÷Ý1ä 0"õѽÙS8Ûž',å~Êï{s3ù=þníôumñuôkgÌËšŸ³ öGóuóxfe&}»z Fð€XH»ÚÆÓâ9 tÆ”J‹É3Nš¦ÁxÛ‘Ö”çe~ô]K½ùÄæ,L˜y—|wqu™6àÐ϶¿›Û3€Š”{#@¨k•^@)öÄå-3k¾?mõøºÆ<|÷/2/ÙÝãõ“Hú4Ê{™ŸO%ž®yE˜æ§^ZWƒCޤ™¥EcKÎjæ—–“Ìm¯™¥Ðêð{÷rôåUXJ½ ]\ãÐoSÀmãô#]Ú—OsqMžÖ‘FËá„¥¼:°ÍÊ´g 3žCkúM€ èx<&:þ^˜fî°‹ϱø¨²dá]óóÞ|û"u`pÂ1'RGOV-O|Óý "îÓÅPe7ƒt(\áÊè“2—À¡7w¬ÍÂUùa³ŒæÓŽ!ß·¹* Þ×'m½~ޝ)¹Ìd×1WæV0ub>˜Hà†Fï…ŒT¤Ž«IÇ3p¼Ÿ/t¿]¬îƒiˆ•Ì´iµ¨Ë©×ë_Zôß¾­@[Ú„Kkúª0½ø¾æ{hÚTvGMd‰ÏÌÚf}õ¹i/›gYè™fc|6»9Œ$pOÙâðVÌŸ¡,irË[¼c›x¸Å+|ÌïB–þ¢íÒÈÖ+öCgšº¡Yœj«ý]X8—ó~1c¸pÚ"<™ËœD›y°ívžÜ—T²Š$ãže¨×ù Ÿq_,ÅÍʳxÒ]ÖMÊñq©Xx<ûùƒ©¯H¸Ì¼çk}“19kÎ8=¶ð#ò¹òL!-¸¡Q¶o IÝM=ÑÌŸ+µ‚hˆ¤§ÏáÙÏÂ/yÓ†Hf£VaNi´n(ê|Säøô”uH ü ¾ %Ûƒ^±oûæ2-ˆ6Eßù3¾¥¾ ÚùÛ¿ÊÓІîwø >”û×è˜À*gÁÏè×Å$€ou¾‘å%]œvåäBcUŽe,èk¹E1*\Ûœ€û:}P JÒîý”þ¢•'VdÌ<Ô\«fÌçÓ¶ŒwÇÃÃw`>b¨ <¹ž¡íÝo§_æ]Ü;ýq#:7i$ùß[èø½+TÆÃ¿.¢L¥3]“†¿žü‚í÷yQ ¯Þ) úï(à×e™Ïçÿg){G—¿g~ÆÔ‰ê¾­ëW_}ÕÈTÊÏ?ÿ|ºŠ×/¡³b(Ïs1„GQX8‘§ßô¦~~ÞÓ;ŸJ oWä=À/‰8Ì܉6Ãß0ÐNbp“Õæðfå:€ÉÆ÷ÃK˜h¤N„C:÷1x7BtºsÉÆ£NP½ÿ¡ŒI4†ÃÛÚ}Ó‡´Ò§C™…̸¿€­\PÍ=…fPíô^MAŸÎαåZ)ÒŸ™ÜïžèU \ÁïF&rI ¯¢„''øƒie3~Ýx^1¦Õñ2í¦!=\oц¤m)Òv_õ’ÀÉoц° ¯"ëà—ƒõ&S9|5Ð7!všdã(•¶©–“Úk…‰[(‚¶¼§}“݉IöÆ¥Ðù§¸gÚ#PÂ}µäîߤ] \¨…ôNïŒf™¬Ißì ˳L0èkëð_}`g ýó³bßGpžº !¦±8ÃÁŠó›Ýà2ëO»ÈŒ(L² ž–ÄçÅoÝ$ôª¡¼«ø@9è;žóE6ðÔÆäk?ÀßÃ2ƒø»y)8«s8ñ[P`jÊ¡€=Ï09L,úø¤í¢ÉöÚÞ ‚L‡kÀ]ÃI3)‹iÄkçÁCÅvà…•¡@{Kù潎Í`W#Kˆ²'ðçï\ÞõRîxx$û aÕq©y5¥çdǤ·l[ ›XÃ}Æôô„ÿµC‹Ü̦f·ú¬@ãösø¶“†¦êÇÞ¹…;™ˆ“L\ÐÙ7··õ:ÀõFõý€‹©gÔ-ÇoýR´Ì®h¨nnmÚÙ¬GØ"ɶhnˆ¹ÜÙÏ\—Jù.§<·[ØÅ0±õµQ‡jé"i¾ÍX&ÅZ~V?”¦§ø§Oü/ö~/cœ<Îùɬä~htp–H;e‹É`×ñ§ pSc}ÛÌ;o”~mcÝïßÍYÆŒèþ<šñ¤é´R}€f‰´,®Ô{á67ób×ñ]¿ÓBT9ÑŽ&-O=R7ØC¶D>¦Iß§Àq.òG¶\<ÙDëâ+e!,|fÊ@ÂøqlDþÑ—½ƒÃ€Xž±ì+ëD»˜²á௢ì‰å=ùÿû lÒ©"Çw–ù…8µ<Á÷¼$â*â$‰/x˜ HÛ´&´ â“zÇ€+²g@µí<3¥ ¶£ö)¢‡AM{pÛÇžWø? ï)-ǘVßV~oêü`xök|ÅïÃáì=ð]þïfXË%æß· ÉpÏë‹c;Ó¶/ÜÕ-rX„¨³""£Oq|J£úOÁ{È1F¯P/׺/¢íÅûx“ý'bíÒ yn¦îÉ"ÿ÷fKIÆct~å¿8Ñ~ÒZÔ¸zQág@`¹ÆÚ.¢>”Iý"•2ëò ÷6Ëg·6ë5ŸJ{8 ´o‘Låør´xîË™¹ÓÒiåÑXå–ÌeòÓj#ŠGnK[¼Gý#•hóÍ'›-)›ú~ZéÁÛî]ÍžšN¹‡Ø» EÖ‡æÍí÷ »÷}•vœLÙŒ›¯ƒd£W™ïÉ©z; waÁa~µ—vxSÓð€££á«Ä$÷®v ƒ¾2P‰þ¾þúë•‚ý%k&iÅIâo ÇýºòY+¿Â`˜FÚ'’=^i!$ÑþVûƒÉbþï#œê„\i­~ÈOC¬¨´üä=ëÿOü¤%, æ° Yá6ð«•‚²+Fø@«Zâïžb‰‡®î׉NyMÚÒ2­– ü0L Í!íÆ¯˜´$ŠšL¤Z“ê.ð_3¬~_ËDûZ„ùj$ üò¢VJµÐéþúvSÙn•4~<ʶYïÀÈÜÕkWšœÝš+6Q|g'[ñÜÑý'å÷¬áYÚÂQÓ$ßK_aÝfžÎ•¾&ps»ë3MÏìöîÚÿêo!Ï0ñ§þ­ÍÌ_#QöìÐéé¢Áh \¨•‚-Òµ†‡[ƒ|Wµð_øÆZ<º H$w&UÒ,ÑÇöVúýóß™ÇáÜ÷fÆ™—iÓáÝÿD˜ÿwÂXœo Z a’¹ -VEHöC³X'mu—ËFÉÚŒÓa>Îb¤9„d”¿¦(¿Áy ×ð»žÌ?á'Nìѧ®4:ƒ„(3r¯r¨å–wË<!ÅZâÔæ!ø]`oÿÙÌOf 4·F¹#£ å¯ežw±Ù—v¾†Ïƒ`.á‘°%@©4Î} °9Õìn¿Ï¼÷êæÚâffbšÚº¥ß_ä(NÒÚþO³¾´æ³";»&®‚7 Ù…hXм/…'îã·=Ù¥>Š8Ñ<€,.¹¯™™ógš¿L]œs Îþ+\ÌûaÌ1aÞ¤Hy@B”';ÌÁî<5~è´òð¸ÄÑ|`,2IxüúÉAÙ³|f³üøt¶ðo ÿ–™¢<âÒ˜—éc®ë~±Yš tËÞ¥ýËÁåæ÷<í¸“1ç/3ëù“ì­áý7»cì6 _æuœœfŒ_oíê ÍCÌfZDÌK½Ÿ–çÒ»Þw¯çç>A}nJ›¡ 3¿»x>mïSÍL³éawŒñÀ£„Ô’œ¢òç—¡ êK±þD³6©;†áÇב~•­•Ÿ¡íõ ^à5¹µð¦Œ1ºÔ ¸[Æ\Ó5άIý»ttØß ,ö|>yNÎ-øMÛ$¾°ã¨ Lj:ì;m'ømÎjÒÉ”\+ß0iü÷b2Öjk³­û^8•†Ó0Ë´ }EJ:JšÓžj ’%ú_û¸[Ú>i÷ÐJh>Z‹ Í‚‡lÒÏNÌò·S«Ǧü»™i"ß)nÛS‹©‹ª´øÓ2›S²û’É_ƒ¤¢W™ÊhßÏlŸ?ÕÜ~&u*Y ¨-½¯'<Î;íÎ7ŠCEáÄèÈ?.c"ã¹³1O’:H/ÞõîbPí&û¹ø”tÃ7á°ÝŸœ…éÓ‹b2½È»_Ž}ï@†O՘둌é—Ó¦/}J=1n5« Ñî2ðègIðµù¥ °òøx¬¨1±Cmç…ºŠv? ~O‡wU„bkqLšêéÄ7tŸJ÷Nôùžr>‚ÏwßÒL"ây4x üÓ"~5=ÈLX÷‡ ÚZ ΂i9ŽgŸÇ—|¸Cæ1ËG\âª^Ù“ø^ÛÆ½j|öú‘k<–©†/‘[á)Zп´8×A¦¶cc˜G{ç_h.£Ež4ó’‘“nË ÇÍŸÇ7}­ÌKüèXê^æ2¡Ÿx'--¿œ#$Ë4’–É eŒ9…ß »äPm5©-X)”a%Šk¨ $?vôñǦ­­­RÔâ·DEæU{¶…eÙæ­é÷ï¿Ùpµçè…Œ‹_õÆÿ­Âw¯Òn~«ru6ßZL-¼øâ‹Í¦,¾ùašøh榄mô ԟʬ³¢3Ï<5ýzõ’ L÷ú«òšôê´ò0y§•l`þb6ñ'©ò×¶~‘´ô:•¸'3) W¹' ¡©QIô‘ Ý™˜'Qp°LÔA"{|Ô‰'h*x5"IÛÈúVþ×è}0ä=„‚´ÁZµ2mzèo¾Ò‚-€ÖS@Û"¤„' P×=Ï®b±O¥Õã3)S5ÒÁÞ­rç©¥ÇâÝ^ÖN¬7­­fiïŠÔß6«uðÿ雉‚¼§ÒÙG-ï?ÝK~£;†«†úD¤ù[x Mçº=Ïs©O¶“…ô‘i¦­ƒbœJNeI"TgQ*{VÓ§‡Ù¶jO³vVDлÞÈžéz¤ÿ_„Ý7´å*׆eÂÔí?Ö¯Ò?D?ÉYáµS‰¬ÝÐ8{(iZ-#M¹žÅí&¸ËQÇj7ð>ºªß]ƒ,›â‹±eûJܹK”ÙÛÈf`œd6f3Þ=L‘ÇWä¡ü4ÖÓ­ÓâòY$lMŸÞ5ünbRð,“ÌËjŠåˆIÕ½â{&4AêÍwà¿ÿD:M7>eWiÔÆÁ»åÞj…”¯à0…wä}9C…h5y-ÄdþQBö´ :6îQ,Zhá1J³Ó~A;f&šoKçº|‡þmW؉Ô'ìê4-¼/@%³!cÌžl=þÙ<Üý1êpkÜöÇíÏô“uÑ$êg³Ò”³|¿*‚0ôÀ˜…ýIùúÅg|xŽ0KJ£°úÄ.™ŒÛÉ#°3 R±š§³³µŒU™Ë\=ÓÏØjo1¯€\ø¯¹°í³jþc³Îdlôúz®a_}gÇ[¬ ’ ®bš`çÌ@óB·«Œlø5“wfù¤Ø§¾ÒA·—ˆÒDK¿²ý¼h¯Ó@¾ö:KŒ¦@½Ë~s"m~f‰v¢¼»1­\¿2OÂkNÉ]l&;;xAºâ¿´Ì‹æNàÅE¸e møJʾmå`´öÅï ~Mf5ìô.8Œ„:e1Üb$¥iŽÔ%$ l9•(Þ+‚°sÐfR:‰‚$5XÊlJÛß#¹î‡ ˆa‚c2îŠÌò!y&Ø>Œñœ²”¹é⢷´S·p;­um³^þmë£çhÂ]K¸Oá•á¶v1NpFBÑ¡ÊÍÕôŸSb¼>E2𾙞Ԃc¶Lé'Ö|@f÷Rtgó÷æ¡æL—ñY9NƒY,ÔÀâÚpPáyÚíA<{½­üq6÷ssã™Æ6k/UÚðÝÞŽ'eû̉½„ t“@¬¦c©—pß/‹’ê E®ÒŸwz›Sâ‹:|Ì×>Å·-_8eN‘=™´Õ–§ËèwGÑn¦;¹_Rç‘ @©ƒŒîÌåe©>™–œ½Lo¸¯$û¥¸¾D]%!"ók—¿W4ì4YÚ%üd_­ýmõ<¤œò(¼p‹„ö«Ù‰ÎQP)d’g;Ú¥Ìú4B+R/1¾eÆxÑÅ/µ 1u[žÇ{náÿ¹ýh¯GYÍ47£|ÃiË'Ú¾á¼1â&G¼Ç)ͳ0 ¬¯F™ÃIÿ ByüPÁõ¾çaJnÓÄ‘S!ÊIã¤ÆÂr:žäµ#|dÙZƧò¤‹.ªóÝH§o¨¬EÏn¤À²7 ¶BY6ªó{æ ‡1$mô »u2ùé]“ ?Ó›Â@yWä%öÚ•¥^žUû·*Ø“M+s-mîÛÏ?7fÙeÓ’¨Ë}œ·59ƒÆ3P/ʳNMHu’¶Ú„iäªú–X®)Fg©¹¹Ùôžm6¶êmoÊ„ƒ:aS¶+¥E¡‰li:)¤ ôO[éŠL _ðWP÷ iÉJÓtiv]äJëN´1~ 4»™©s»&AÖ®â iAíá g’ÇhÙ>Íó‹Qø`¹âé¶±0Õ‡ðO2ùWÙB Ñ6›3ே ¢Í7c’¥ÉÒñ~}Hä_ M”zè$Ò HGÝjŸiaºÂ]`Û-5åZEŸãÐÞ./E}5âÅ×¶ÉJ^Ki“e¹4ßfúµÍÄó£2{€¨mA>¡¡· `M%¤²vÿŒIú"‹¸)Â4uéðíý®èsìIuýc‘[3ÙÊîeÎ@£ïqÚ§úLUê8m¶´g&Õ˰À`¿‰ÊÔ~’Ù&Óo>¹ÌVw4Í/MóÔªæt“¸x-iBý€×œ¡…hÚ¥'MÇverôwŸ¯>áC©Çÿ&Q½A¼Nڽȣ7é p´‡1Mœ¼ w8K´.ïDx©Â„ 9{ÕâHÜfôÝ2žÿè^ û·„—©KÎj\˜ä`²1×X´Ü¡ð»'“ÛKFòà¬ø¥ðͶÃñgl‰vÚñoÿ¾Æ‹ìÝ6BΧ ! akœèL-˜bp"óÜß̲î(óiþjÚHÛ¹KÒå Ì$ó Ú¬ý§ú°xÔPqw€ìÁÒí—×ö}§ÕôË 0á¶}n[31ÿ´™ƒ¹¶f\ÐDL“Æ€–`ZýJÓN¦Oû?̘tù8³5@ßÙf|Û6fE¹eàXŒ‰ÝÏໄhnâ‚i&Ãò߇Fa/‡V¦ŽcšŽqÞÖ:Ì‘ ü¢9$[7ÛP7‡YÿeXhKÚë¥úŸÛ™8˜{;.1?3ÎÁbï–ŽcdN¹‹ªñ¾ÇFù&r 7ÙèjÈdM@ÝÉøªï dPÿ”†Ò[Øê]yêcÀļ{öHêrN3¼ãA/™¾ã4ÜYbM¢5¯@F²ÐóT‘'~ßUŸ¤ßV<Ðq#á&Ø-Ù”‚/|ñÉósç`mùÍÞßM.;GMgчGÒ¦.÷Òi;˜ë×h€L<0£ºlŽ© Øi¤mìmá«·0hJOdŒðîoè|—÷9¾^ ÙC Í’¬E^h6›¶‰è´YíGS¸›zèm“Ðö‘ï÷ë\¨}A_ Ÿ'°9‹ðWe4îÏáK4/alÊ_Ïûß)–vhH[| ´]­„X¸Íd¦L2C Å5í¯”ã‹HœztТøw%ú"s”TxÕ܃-û=hóç3æ÷Ìí|Y€ö¹Ið]´À/SQPOìv^Ñöº¹ üb_‘Âzøÿ±ÀÓÊÜèòüd;ªR¶ØûÓ–7už0k 7Ž*rŽæ=ÃIïõuŇ^¦ÞLضO%ЯP7™£©3€Ë”ñ`mò>œ¾úHÇÁv𓌩;"Ÿÿ¨¸Ùi ð•)ÇðžI “Ìd2ýð›…ô¡1¢×–/´*)gmÒ ÆaÍœüyŒÌ¦ÐVLž~T3¥¬Ã½Ðù¿Õ««ʾôsÈ4º;G³3~H#¸÷y?1)õ%ÉðŸyc)Øš|óë³sšùo°Áœ7kU˜•b%ßH›þÝX$úf¤\_ñ™åŠ˜ý鸔pâ ÆìˆÿÕ”Ct‡«IE'ÇÿÍE/Ê_lV¥žFràæò¸9ÚY˜t']…®@jëjQÚ9Ú±‡ÃH™›mO +ÑTH!Õ«'>“øI¶ÝŠ:Ó}gH;aס\M'± ¹sè;p‰üɉÞeŽvAR#+ÙWü( ó‡CY dÊ\ºÀAZ¾gõ7 ú èÝk¯½Ì¾ûîkV^yeóé§ŸšóÏ?ß¼ùæ›]Sç“P‡M¢> Õ(îÓšäj,i­!~µôg„£ ¯lþ–¤Ã º’}kÍr x9mÚ4sï½÷šÁƒÛ>1vܸ²$ìŠþóŸÿ˜aÆ™½†e¯ÿvžyá…Ì(µÕ?üÐ\rñÅè¶LŸï’§ öí‹ðïÛo¿5S±“\­íutpÆ/ðĆ_cö²vŽJî­¾’‹w§á4 `E;ÎþÒ4 @_¹ucÂX IËPB­ÝÞ™"¬é¼Å¤¦G•ö»=¿&Âõ´yo [6ð‚æ}äƒhk6§žnb#0à`>‰/äзž<Ê èÎŧ2ï®qèÃG¿³jRZ„©xšÚ·àaÕ ž¿RÏǦÔDm[™]œ/írÝÓúÞ¶¦ôbE‹°ɾÛîSV Xx4«+ê/3 ž¦Æ<õ"Ç2˜ Ø~Vèë§t\—ÙÉ~7iù°|q¬yaotóƒ„Z$’güáfçWìèz‚{Ü/x> m× “xAŸ@_Åi¦ÿÍI¹¤uùZ+I4Í{ ©_Á©{dO3 Ï¢­xôµywr_+è?ÂbÍl¹Í„üûëa–”jàF½wU¦µ| À×Αw§-éЩMð‰´ES¸yªò¢}rb-—‰´""Í/1Bª£ÌØ1E[7Ñ¢ìZšä=t8QþÊIsxÖ üv{¸CÛ 2‘û†þµ=}b‚M%ZLgCÚ‚ÌúhˆŽÎ¶™C³1>Åœ\¸Õ\6Ÿ=âô>@üüfE _Hsîì*oÕ¶ƒyֶퟘÀïÏ7»yï@¢ºVÛ2àyz»€Þd"'S a’ £QY}G8qî\ëµ!õ= §"¡I¨Ã!÷´Zâ³y»5g’Ô¡lqÚ‘þ»°ÊŶñÙœwa2~q&oŽûµ;‡U}mÎBóùJìÂv EWÐqæ ³Gá’âî”ÇÐþãé&LÄ¥Ì1š1õ+¤Kð0Ó1ÂÞjŒuÄ7x'# )$“RƒÑ Õ‚«xE”Ê在¿ž"­i×òÙŽ åÑ4ÿê_d7}ùºŽ¤ÙïÀf«@IDAT€¾6U@)Êõ²ÅÝÁ¢ká#œÎÆý[~½l¨ä¥±F6s=Ù¬¯ùGó½Rë·ƒÌAȃ¬m|i½f—¢éñ›Žû>„ü&`¦ ßÌžlÕx ÷cyXÍu” Bs²Ð޽p4LþkVëx˃ëšo$aoÿ^¼xtÑ]ãdµ7³}Msó]¸/Hu(Ù£f,ÛCÙÄÓŠ En懇݋Kë*-Úÿ†Æem¹ÌѶÍ.‚Y²9¬îù~ZÙ_-D·:}èg›óín¦ÝžD³àÍy…ñæ‰Øbeà@ç« i±QKŸ¾‡ï½3fŠT6TÅ2NáV‡4]C]ô.Óa¹?‡WS@_Å–êÔ±ÀÙRŸÄÅÁöâ;/­÷Ë ›Hðœö}L?ŇÔk&lqWnÓðß–áð‚ ü$ãù)î}ù­|{8gÍF^ÊXÞ8é oëÍ+SÓÒÁyf)ë}¼[f$¨µNÐã´=hƒ×¥¦#ÙY;¦Êw½l>·›U|Ð75=N@þÔnÐ"n¡_\e¥8&Ôß ZñÚGkpéHÈíu‡2–ÈLŒ$½~³²Ð’CÃ÷•Üæ~È@j{áÒ8›j~ d9Ä·Náë2&’âRŒ»[÷ø?ÙÍý…?ñ ûÓëîgQD¥Mò.þßUé”DÀ²€%R½F9´u.ÿ—;ÞÆïª¡– Ìleü¼†ˆÕƒ<óÌ3fôèÑfÕUW5§ž:Ä 0Àl¸á†æÈ#4ƒ ² ÜÿýÕŠ…ÐÊ{v‹QýQ6‰Æ%¬8ÅÃy]3êŠhfiL ñ£1ÿ·ž&„ãoPôŸÅTÿGˆéM"] P{ÑE™9æ˜Ã¨Ïüy¿ýÌ?J|)Q Íûøã›'žxÂŒ;Öþ>û è¯V˜}õ˱f=ö0oåi£”¢×u×Õ}+-sŽ>ð®“lf Ž“D—ôà»õwit);¤xÛ™›’]¥Î¤+ÞÀd@ØGƒœV›5™¸ !Gm|oÙ$ O€6BÀ—À«Cçö@ ™¶®­µ9øåeL\㤓aGv’ÃD­Šx®±ç,Ây!°&ÇDrךBVD}4Kð\aá‚bpMQº’4üÀaBµÒ™L¢t@_œ–Í€ÓâLž†Å½j~–&t5ºŽüŸ,m–¶Á[i?õÐR´¸ÕILî$&>;§DÕ;¾Sô“&V-´`á9ì;¾d%ü똖øÌo¿G¸Àí§Ä'“Ù`+š«ŸÅéB&OÒïâ45îzÐ¥ƒEš´HcRÂúc”1n¢A'3s„–ùZ"«0q½8LÛŒ¿ Ö5uX3$¬~ì8ÖlÈr ¥šèk3Û[„I»!"Û]e«Òõ;¹ïöY²…y^qP'â—¢Ž§éÛX;wy: ”Ù;Á±'™µÈý™€´ÛàÔìH4õ=M}¢tc܃>ŸX¾/ûº2±‹{¶—· WËî«O2±Õ‡öÐG´·ÓÚî6—q(Þ:˜"‘y#µød­¢Í¯íب8,@Z÷“·4KÄäÙeÒ i\^œ6®4_A«0ÈõÚ߃2ÓálSŒ6e¿¦@Ý6o 0r ~›b⨻Y–8GdO¤ÅzæîÜîfæÌ*Øã½ÈÌ™;•¸‹˜àG1ÁÜ aÚ„ôŽ þ™^, y‹7Ãö˜:ªðƒo5Öa9w¢­Œ‹2ô!‡½‘á{d¶¤.òØPÎq@ #éÿ‰Ÿ4Oÿ̸%ñå7­Ó,!žÖq|4HìI‹’*ÌÉÁsÝÅë  mÅ‚Ý^œ+믪 ðŽôÒ‚²ñ]/Åy˜§7VžŠì¤kqÙ’Nwâ¸Á𾍡?;;zá‘b^h» x¼MêpºÁg™ £ußh–ˆ]jÀþ#_4[Ýñ˜zZÈ,Ùð ³²¹¤íóUþsA‘,‚¯byà…´cí¹3¯…åE¥ƒêq€´;“WkûͤùZ)‹ÐF->j ü€ÇyÞ}éüX3¡ãq|~ sÙõñ+š°ß6(gu:K môµ¢É¦o‘¬YZ0_œvvá< ¬þmý‹c’¨—¹ï?¨ÒÆB ךøÉ…Ü‘ÔãxsTÆÅ¬Œ×þjZˆ°‹„¿Ó©T;^õúôÿ•Õÿó|»üsé‘ñ(¿<‰«ÌvÝËœë•ì¦ì2YÞ\Söæd¬;ƒ²oXL'íF¦¿†ú‹hG2¦iì­F³:zdPG&Ðî"­«P$8š´ŠLyãVÍ‹‡EêU>JK¯fwÙùvï³Á/‚OŸ^¡ŒÅ4%›å‰÷; uøâÕ:Ä»‘ñvú¾ÂŒÇ#hƒÛe³.Íï°Ï>û˜\.I{Í5×´Ú¾&L0“'O6=zxl÷½÷Þ3Ï=÷œy÷ÝwÍL3Íd_|q³Î:ëš¾}[mü_ýÕ<8¦Ã²¾þúk³À ˜wÞÙ,¶ØbÖÿ·øW}:][©¤±|#‰S|Üÿçÿ §Ï1ÿpûí·›K.¹Ä¨OhQd«­¶2÷ÐwŽÛïÏ .ÑÖõÁ˜-¶ØÂqÄ‘¤´@1|øp³ä’Kšk¯E¨h$*ÊÐúô0‘T¤o¾ùÆÌ2Ë,Æq» J[)ä]ap8ðÓä-v¨7hµ5g“ `¾þ…¥ jW_%lè °îlÅ­Dˆûhê¶15÷HWÙ]ÙÃÇBñOd@èOFµ £íêè÷Ùù9Ìb¢ÝZ«ÉüžeV›\â? Î*û‰b޲½šhÇ‚yLÈÂ$ð^§)§S;ú]4‹*…I]òa Óæ q:qÞ§›Ø^:”ºJ:ô.SÏ8$ybÞ¤tÚøÖøVËàí{ד=a»ÌƯ|ñ$žÓ³;Ùð6o…ù“ÚáL51Œ“LPôgÜ Oü,¸¨íVjsVë,«ü¹&iÏaÒéàHWvZÌ•€.?㹋󓹥Ð0I‡—þËÍë\6<ç¶f–â~{„¯@€VЀd—ý{üãöãž±çYxÐ5`08Ã]vÃß!m•÷“bÏ-EÜ >5„ºy AüVêôÚü5ÖS§ÉO0Èr?Z†Kl‹F‰héÔ³…íKò÷ú|+±Õƾà'Mü†Ó¦4ªEŸ$?á9ǤÉÚ'‘àwê†Cô›CL]‡[õžÎИÎD.ÆÕÁ{±ðäò+#&YY&rúêunjç—F™I{]ÂìŽÞÛ\èó°åS€nóÑ|²l,µ?Ù7Ð4MæÇd&&léT&‘ï™ÿ ¥Øa6f Ù’>&€J[l)s¶Z7L!df$‰>èÛOmÒŒ@›Î¸ÒÖü¨‡&Yš¤)æ<ìRkVm:ÍìÛq…ù‰-ܿҗ6––~ó¡fö癬hž0Å|QcÄìÉæt/¾ÿ_' /ä÷*´yuâ(@=iÞF?›‹{B'-/'§ç1ÿO¬èJË÷ÿ±wpRÛÛ® &Lff̘3æ+¨˜P0çœs˜*D1cŒ¹*@ÁTÀp%m˜™ïy«§gº{ºggvÔÿçÙßlWWêêêªS§Þ:u*m&^Ë´9–^mÑßûRæÂé$Ô@N#t.ïB/ §Ä%¼ó÷´‘œðŠ ˜¤Žá^ CdqŠ|J£ ˆ J‰Ë¬ù”OsQ´ûá&´G»°•®|ãýõ²ß‘‹SÜ«•¼úUñhˆ+Og\¦ß³uwùÌPs1mSõú+‡vFvÒ©#òÛNÕê5±¾ñA#\ÇÔ æ;|‘]@M½g¶àŸ¬wZÝq¤KÚä"6ΩßM‚:ÉS;ê`]LŒ6g0ߌ嚈>-Þ¿ Ñ›ð°î˜ ø±r0e?6[/ëÐ6Sø[äcÝyÖÆÄ¿5ÿxiÆ&Ïâ\ƒß4ÂnÉ…IS|<‹0&H¾ëòÏø2?ó]½ ÒD¼0¾¨Ùž¾ý9 WÑæ;óÝxC«m»¼ébúµzÒÜ8oOÞprî9Ž£ ekse~ây!m®ò:Êùj üNjßd{{øXêjÓc4¿£›‚†1î%”E³;\ —7Ûw¤{Í&ãHJÙ ÍT¸ |ó(þE´w…C²ØÔ³èܽˆú–ãYôÿ$ânOŒ‰±¨áøÎ|Çé¶žÄQI€u(ウĞ@ß,¶E>=’8mù!Ÿ¥Þá _ %Ê]§úkœ÷…&_`žÈ3©øVÁ6[Úu¸§vd?gÇäÎø1|›Ñ$ÇõIÚÌܶõø3¬?§À¿g¬ÚšËëK˜Ë³ïcÎõ²à#õ'˜GxJ8N/ø§»Ó­?#ZUÝAÐöƒ=œu ÒëlˆÆ¨žòÖZ9.s]¾ó=Èæßò<)úˆ¤%ýæXà–>Þ ï¾3PâóÈÇU"S¿;ž1òCÉêºÖ†WxÏ¡afn¶Äïòïmâøo0ü9äk>1£ïö÷ “¯> ÔÕ|£x#ügá—˜¥²Ú“lÿS”GÚJUŒ´5û×_µfì„Ñ 'œ`.¿üòè+øŠ+®0cÇŽÅ꺦S§Næ½÷Þ3W]ueî@¸¡C‡šÇܾ²,ÍÇ„‚CaÏlI?um¯_™_KPè«CèÜŽÚÏø'ç¡ZNþƒ_@,ØÑ&MBˆ€6Ûl3{­ªª²}æ³Ï?Gó ,Aµ0²âŠ+š^Àvæ!ÔÇ\ª©©1[mµ•{y•VmT‹Õ£@_•iA‹’ÞI‹9nÞ©@îaÚ¾n”à;œ‚`+ò®NnŒˆ¼°IÛåÃH6 _JÏ7£®Eû¢ xtúÑî ÕÐWé]ÐWnmÕ½@·EÈÌ€4‚J!/‡þ-¼LÁØÞ¼hhR€çh”„›mšJ‡Sw½$íN ü.Iû*çú6çêŠIÆôF#´© ¯ê%à^qš8RfIQ›).m ¬“¾Z8,S>¯|ØpIŸŸ{³9rÀ¡Qm™_J¥s|“o'•´òÚð¥µ%ú`Ú€„èKK}Z«DµÚ©Kd†Û„×£!ÐÀIdÁ× F— ‘Á´y2ÌÎsa´‰Ÿœ¿u]1};MŸ%,Wq‘´ž–¡¿.aýÞI,‡©'\‡øI«ÄKÒÊÔ¡c}½¤òþ-×í²åo¶]ìD/Þa_ïcT¿fã9—{2ÌžÉ!Ì‹·}=Í ÚSÖ â6È­xÊß™¹Ë*H¯ƒ7$Ív´dñ°ÆHräy1~_êaúöçEFÇ週ȿ®R‚´ˆ]{æšË?G¾VE ê¶"ËG­ÿ4Õ™6Þƒ’u]Ôáånº–¼já]‡ÔI.]¦XƱ ù–ðë<7/{†éPúòÍ”NèÃKÉÜöù´À2I¦½‹¤ùé§Ÿlè2Ëø?Õo¼aæÌ™ãK)›¿Ë/¿¼ùä“O¬ÿE]dVXaë~â‰'ÌsÏ=g~ÀîérË-gFmAá£>Ú†O™2Å\}õÕsÁ3_æ ðFCJ)¦ ¢ŠPE€„›blUvf‚¤-òÿ4“S‘—KêÈyaÒõ¾AÁè˜}ˆ¾mpÈŸ‡Ÿ¾·—¦M›f–^‹¼lcÓ”LáíÚµ³í=Ø.¤,ºòÊ+­†»Ì¨H+þî»ï¶÷ÊKæ"´ÕúËvcóoß )u©òéjÕ°%I¼¢Ý¢‹òÄ­å)×|ˆ@á É ïsWá='òžÏ²ˆ>ŸÀöütú© 9íÎàøQv»Œ7mÐ-mŠy»Ü6Å`÷>y-ñ5Ü9À’ëí½/²‚{ ‡4d M‚g¿ØFØÜ€·7‹’Ü~kâËZÍÆ`‚v¦‚”Aè¯`ÂSÈQ “÷%ì‰Ö [? :Žü9}Í„"¸(áq#Úå’´$»Rÿ'gÁ{ozÙºÒŠ·K‚Ü4¥ »y,ˆë|÷Só­.…òB1þgz@@ËÞq‘ѯú™8²¹¤;8mR„e7mó’Õ¦+fí nˆ‡ ðŽ÷cœn3QR¾.fu¾#è—§Ú\FÐß¿ÌrZ©öégËku&ì™ &ëÚBÊ$Àn³F›ÊHدجM™ÌÖFžòÝÞôqõãfhm¶îbnHO &kÒ£Iĉö&¾>å{”äLbõy†h!]Á‚lËy5?òéÜQ*ïãw•Ø®µ•¯Ž ¨—Ä’”«¹?6>„´‚bÊ¡e)ßFTÏ-Þ[m®ñøÎÔmý‘%ù§ô©Ïhwq]¯Bþ¬x2ó:¼Ø™u9)‹þOœÁ÷'ïô}ùh:hj¿-_½Œ¶pÐ eÈÒ†ñ:žV2mßÞ àÛ¼AŸ÷»ÅüyrpͦÕó*‹Ï½^Ý-í üöOˆ™€Ì”Ôîö¹’d¥p/d?r±–`·@&lϯWåy摆¯øF|¥†; ­1Çð,ã³4þd|˜m 뺚ÌÊU‹‡ó?À1ËxnÀo¦Bü¤…Š4ü+Ç«~åþ¶|œ†Kp§Ñ`OšSк}ŽwÜÊ›æI<Ð.dä½lßj8ÅëásË”Fž:£‰÷}T‹3ƒiÓÑ^.ÖìE¬£ãæjlŽÞª>΢ÊJ³WŠ÷$ƵÚ~„æG#Ù;E_™M~CãóÌU(2¼Hÿ&rÐÞYpÙ Ò½5#£E‹º'ñF "ø uãç’nªÂ«å{ Jæ\´Ëá ú\Þñ;“¾†´çfAV…?Â3‡Â»Ú–5ÐdϘ„¯w›Wðzexí^WAÈ.Å"-pj÷ž÷™Þ8Â^¼ŠC2öXˆ,ïgI)Gõÿ˜4ØK¨~ÍQ/ø–Üñ`#uì-[˜û4xJWøÏæa¶Ÿ$IV–t¡û…ͱz!Jx ¤ù¥yóœá=™ôcÊÒT˜ëýISQ´Ë.»š{î¹Ç,µÔRF ×Çl´µ]$HÚmÚ´1Ÿ£ ùè£ZûÁÒ~¼í¶Ûr“6ràŸÔÕ&¹]˜‡•1¬FÚ!EûÇxé`1Ùa©+¶â'((8Q‘}·¿’Ä”šC(ëˆ –)ñÝdæÄí#®}ÛÊÊJ£ß‚$pxß}÷5=ö˜mÿ#FŒà ‰JóÌ·-Ìž=ÛÞ}«È´©]Ì,ø ƒelʽxÑú <“ŸK³fúŠ\ÐWnM|tJ³K—dh÷>ìÚƒoµ‚d±zË¥k¸ §_tpÃdX_$m¾(’–¡KÊåáÌ'0·»]¯È눬¡7‚8lû´o*á .p'#@_EÜŸI`¾ÖòIÛæ9—&·äîšçЄM$§w,•,mô­Ïû–:¤ä[»×7ûŸ´ÞÁ.WnôÓD'Šî&\6½\’ *€ãhÚ’~-CLöㇶLVÊ%¶%¿½ÊÊoÂm9 teey=d¸gm¨øÕÅhpmƒFÇg&2œ zלV_ó¿éß@æD&$–0Rsùæ©‹Y$úÞg+­mêV&Ó“ÍY@' qnOy 3­·^µ—›7˜|*¨"J[:÷°‡ŒÀF¸ ‡W™V# ¯¯°d^?íœp@ߎd1,ëÉýÓùûf»K¼$íVß¶go`”›IV«+\.*‚ãÛ`þ^ëÎË‚ÔÓ¼]ñ›]<­*-£ú³Ü»âWi V½@ß=Øï"x†ÚVQXŸº¤0JÝ|[fÑ‚ƒw,±‘sýzàÕ&x-a®Š­À¢ÃÅÏ`Òy4“dÇV ;¶LMúQ‡¿Äqž‡¶öÑ´å¼­éÎŽ?ÿ…è/qýæøµ™m¾j3/dÛ5 L¦ô½Ê|;I þWBOgÙ¼7ZŸf^ÊøµÕZ…qîÄÔofàá3!c¡J»-NPu2àèIÆÿÑ€ˆùŠ* ý:˜¾7ó2‹R@u›çþs!9µ–# ÞÇÛûv½¼ÀsÆ1‚j¼Íå²(¶3Ñ×Hò'¼íTp ÅùÎgýœ[ï}àâÚåHóï?l\IHÇÓÎ6àþ•C°^ॅøvc¹ÚõshÞ^öÛVÉk2©ò Ê»Z?ÍóäôÔü})®œ›Ðoß+ûý½iÔнLV xôK6Q—̇æ¨Üb–7§ÒÜêí²gîm'k×Ws-ñÞõraÙ( ä²2m’¶ê!qSÿn O`IÎnäÙ½¤˜6R¼+ýe£ÒãgcjŠ»{%˜X» úyP¤­ªæJ}eÛ½3 ¾*Ó¦ðuÙ_«]qy‚?"·Ìƒ³ÍÆó@Túس&œY¤h%žF xÚ‘là•ñÅÍpƤàálaiáà¡ ¯âþ—Å™qü¼¤öØ\:Šþ¦]÷ø{‘-ü-CÆõÅÎÔ×R˜Âh.ÉÜá|~â9¡D?6 ǤšYg\ŽÖÂrùH g‘]‹?­}{GˆxãZz¶´sï¿ÿ~û;âeHÝHrÎœÙæ®»î2}ûö5矾ѡW?þ˜„|tÒIfÙe—5½dâA‡Ä)¿0 Ly‹´*Õñœùÿ®¶aÞç_W¹5 O[ DÚ‚å=TÄ›—Mù+©¹¬è{Ê_(4:o4µÄwÓ"‡LˆÜU?Ù´•v{dúä¼óÎ3«­¶š ’6ýn=z˜×'M´6r¥),+ÅHC8({á…¹;\-gÁÑÀïbLCŠC@½f2Åtæ[É6’L%4NßDÑV$}­ñÙ©±7Â2ܸ€°×ßuûp=C®}ì,°|Þ™s©=Ë¿&Ÿ?2ÉR¹½M“•hÿ5•dƒJ¤‰pÔdxåÿ—vØÑv°IœÉ ¹JÏOú´5¹¹äö0†w@(ðæy<gÔ*µZ R­ÚIÏú]ÍS[IƒTž¦¯Ú*˜…ï^ù•¾õJ‹Î…/“o\D¶¦£¨"* ,ÿÏǶµ75uƒPõ Y9UîùØ*›à1ñÂ7™·3OóO0Ã^·²!½NjZäöwi~œEßÐD^Ú ããU¦]}o<² ?ëð8µ/ÑkÑbü4q˜=8d¤Ý®øž7B‰nym"â~ÊDäA^}uÂ=m3& p­ˆ4QÞS@û†äuØF]&ƒ[ã×9ïÿ—º1æ ˜¡½£ÌçL ÇÆd•‘kI±UyŸ}Ì~¢^µ£#Ôœƒ2×è4SŽÆ‰6kêÈ+¾±/îAŸ4:ÎHvò|»\pÑÛ-ø.FÔßJüÚá ׎üDL)ÛŒ7÷·™ŒVœ‡U\0¶NúF'šþKË'7Iü‰_ÍŠh[ª~‚úÙÑqÿ«ÞçîôõÜWÙ…0í iLþ^¤îL39þžŸ_ÚSß<±àëaîhó»é–^Ã+MÕ, hÒ­&ÊsàIçc—õ4tµ+dƒÌÖf¿Š«ÑŠœŸ×Äx»M­Ý ²úG ËÍWŸ—›uÌá±“Í Š²‰ì’4¬¶O·3S*_Æk ë-³-º7]ÓŽhn쮉½i'›DÔ¸NÍŠ­žÀ¬¾»³knsÀż-êðT>_iåépÇ¢D=[­sç¸QµË£œ3ÜtA9Íñö›CoDÛø'êÐ7ÌpÅácünn˜žnjXÖ?œ/ïÇnïT4ã4F‹dÃV‡tiÜ•V÷ˆìâÄÒ©ë8 í GàÒ¦‰ãÍVÉëfvè;|'š¶ Ç´ñí¿ôWô~.UÞO8Ý€¨{™GùöÛoÝ Ô5•JùLÀ3ñ 1˙â8ÚܤÛ>5ØÎòÙ§»ŠA[Û/Áÿf@íÒnÑ-¡[$¹7ù t ’з`?`À_AšÀ‰³ðéŒVp?a‚ᯀ®ãÙ·„NЍ“Ï@ ˆ¢ã©ÇZøó.þ™ä \’¥(§Ä°ÁhÜ>¬§†“›‹Û’‰O£ÐsÉ+¬¹~Q×íø"¿—`ËwíBöZUGî×Pøn|©¨¬Cüš¤ Y„ô–A¸,‰2¯3YZU'o{©j4wÞIšl)ÆÍo¼wŠÉ]ÐÖ´›T‚n]¢c~ U¶Ðîæ¤+Z_Ô•Õ˜™¿1÷S¼9·NVDûñ 1áqDØ4 ZLýsó3‹ÎÏF°–f—´…T-$ÌF£Â.er«>'›ÐÓŽÅm·’k2Ûæ#Ê ðñÒ—ÈÑÀAY´¯ÞRQa´'cž&à*‡Üö´ã¬VQXüP¿ø›‡õ,‡~rò‰­Q4YË® °ùdx¶®½ÂðЬ/”¶/;®ï2y˃P­MÛDsrbÚ,À®@ÑxoÒ”*‘)ûÊì3¼dm/²mÛ»$wüx¢IRVÍŽ‰ƒáóý¸§õÕnËÕÓîëž6k‡‰ÿiÛtÖŸ?¹™xR9âáŒý¨úËæ¥h>€Bæ5Çü¯zO;Àû&/%¿Ëé[,Bdµk•TZ·ÏÇW_ù˜;AGyú)qmZ·p*žÓ7þ‹ù ñí’†'Ö³e“¼Ø\ÛŠcì’—;6b3ßaŽî3s Ú‡9‰@Îõg笞Ùlëì.€@v¡Ÿ}ÀôvêµéóÓä5a‹o0*ç5¹”1\ΘsýümÌ‘1Iå‘d¾Ú"Ûžï|Q‹d¥L.gb/[Ž>¸5©ëñêÀ³r]Â’lLÏó_ð3éöÂ~¯Ï¶,±§}\K›Ùœw~³#»¤Ÿ3?ׯ?í¨º’Øâö«ñœm¸fɶqÚ¹Kõ7ò­^qïJ¾ Ð%eÏè¿?ß±ä\¼i«ö ²\ËöZ÷ þ}Ï#bäÂÖÄdÌVèR»$ûøÑÞƒ$pZàÓ~ãi‡¿0îOEkW3g:—4]XHÈmÇÁôô‘a6km]ïhã³w߉z×·uxAw8ðjäñ }ò[_/”N U/±×>§x#Sm¯$V6#­LÃ|M~ŽHrÛÑüQyþî¢YH”F¼ÂÀ(82»3î¼ÀT\BÛ‚/,JߎàuTAιqN2‰µ½N?j”º"»ìL¿ƒ‡7œWÁSªJzÒÂT"lamº.Ü’ùŸVª ªð»ßa¢›l²‰Õî½ì²ËÌwß}gMè·=öØÃ&š)= øŸ8Ðt{+{ºy˜ÌXê3?Ç¿ÐmSøCŽ80ÒÌïÅ­#A_Ь+ÍYÉWW–IS1?bæR‘'cd=ßÉcÑú¶\«¸‹«ÙVF)~h«'/ë$¿ù=pM 4ù¾sXÙë¯c|y Éy6ž0Ïk âf>àOÂþ[”V¡_?Ęv±ÔN%){© aðÄØømÆû¼+?ÓôàzOoм›¯ÉÁ~µ±­Ì>ì`ô<—^‰Û—_¶gžâ[Žg°ù\]—ÉÖP››w<^\oßu#xè’•‡™¹qx´(q>¿+w‘ÿ2é6ÅÞ¬Ãå‚ô;:·å¾ôŸ´¢Ú\°úŠÚ²ÎuÙ•wvm÷jöBúC!·vL?hîµ üliu€ªLàšeŽãW½Â\QÚöÒö—é Ñ9™¸9ÏJÝþ§#ιôk?-çiÆÈ<ž´‹ÿ)t}f´¹¢N Sþú"gÞ£Ë ²;*vmá9bÁËÅáÈ­n\¥Ép€µÉûÔSOY°ê­·Þ2cÇŽ5º¿ä’KÌb‹!¬y¨K—.Ðzï½÷¬ýÞ‡zÈ(H ïò˯`ݲq*-ßéøæ‚Ê›o¾¹ +åß?§Y–ò6åÅѶ“åsb`yidlÞþO „’–"M?µ@r饗Úö®þ2lØ0sê©§šµÖZË>F °o“9ˆÅ_Üš7ÑÂÇN;ídh¤¿Ûn»1áÄc®rËJsIC‚Ûò …¹juÞ™šøÃÖb *—êë9qmfiûz0l[BF^-c|•-ËÐKi¥3(¯lSùüÞ!,J1íå5FtÆõ½?KZ= U8ÔZŽgGäVTÀý4òqÒ@‘ý¾FÉ[Ézífg/W´&eýÆ‚&ÑY¾Õ‚<#ú)Nˆì‡jËdÙÔpf É÷{çv6Œ Ïykðoš°‘«‰â³YTúP ,º$RZ[7â ˆáNE`? ƒ‰šÚÉÁvn²¢×Mácû§H6/ˆ”:ßê"¾‹Ý¶=Œe´¾òa4ù–Èå¥R   ;c ñ5Ì‚ÿyí’;‰á9¹ /唆#‡ðxmÉJëNÚ!+1W <©ínÌ(Ìëž{¾ëЇ²«}/ m)7±]©‡ÓÜà‚ëÆäýFb»K—g›až‰ˆ7² CMÆ[‚Öfñi¥#Ff‹§I踖(jaҞεFåºýˆC½ÊÑ:Ü)ýÓ ƒ¾Ü«võ‹¦§§¼Û¾µ§É«¶‹n}¥ÄÕĶv2 püc}#ycá«0ÇV^fžBKÒÏé+™îdÌýh®›Ô…f+·=hÙ·C²¿™œÜÖnO|<~¬ßrþ6º®¯,â/©­g>¡­üÉbÕ›ôÁß|qìå(¶ŸÙµJ¶³£nÝþÇÃÙB“çýâsMÏÙë›.€¤&¹™y{N³gz¬yBý:,5Óôe²œIîižHîàYdâÛ$® †zyé‡Y‰XüÉ~ò×±5M«doNDO›åYTzÅ;Å´»Òñ,À ·\Ö_£Ê,óCÝ®@ÑŸ™šòçÄÞ,½G¨?'ÎÈæ¤:hÇbDkŽòsÞuüæÀ—æÄhâp›ocÏrŽ"^|vÇ„ž€w4:µ_:˜EÈ=#AÝ=´Õ¡K ª¤Ò½q®V{~E¿_àN€õ•Ê “Lw«ÑH+ªí‹ÙŸa¹]4nv/жžÍÑô»p¨šœÁo]ê±;eûE_±n8mp´Õ@¿BÚ»©¡Äø™_‰$€[íPWÚ€vv…Ñ¢Oòþ‹UY¹A %Zt1Iú´xu–6¢Gh1¹8}Ã8q8QÆg£­N[8w<{Ÿ¿ÜE[ðEZ›š©ÀDɪ¹(Ҟ݄wÒa‡ 4xWe‘»/ýè(ÆI—¬ ìr©FvR|3œ*ð^ƒqX C¢£¨‰ëm[á]²¤qZá©Ò’ÌC´Û>¡ Ò$÷¹Éœ+ <Õ^J›¨ÏZl牀ÁF‹©%‘$›–!q«{âíÌ)1GæÈçúÎoó·¸dXuá%í껤 ã²Ú”½“2H!Í£ÍÓÖ#-hI^­;›k]Hl$²ô$>ccÕ-DeüÃ.¿ÌNïÍâW²¤²]ëõº›ðí6 ú6z¯]X´4ó98¾ÿc`×R0Î_qïµûì<ŸÑ$~ΕZ¦8™qôÝW²ÒEËd™Ëe‘E± ÔàÁƒ­¦¢´E: mÍ5×´¶|Ï<óLkŸTþ»ï¾‡™:uª¹óÎ;uk6[vykÓ÷Á´v|†xàfÈ!ö^lUìZþÝ»o¦$%‘D#@&[´ Š9ä&e-–βš”]A"­|Ïlfç.È´ º|V$÷'úc¾µÿY͹kê÷уK¶ ‡Q7Ø`óÊ+¯˜3f˜e°ùÛ0ØÕ¤ùëjÿªÜvÛm3ù×™¦šþUQ¡uHn¾çœsŽõˆåmá*»€†É%|ÿzâêTÐ +D”Þ"õL•HZtM!™‘}_/µ¥¯ÎæMt÷,ÊŽã£ßÙ·¸G q3VËvòÅh™ Òﱕ¬k5E£Jñ5yviBž²ÔTú-­NY;¹Z]n ¸èN¹ Eæiç#äõ,;ÛáMMk9%>Šmö-I'zì} œ¼‚û°mQÏø©¯—FÅ,Í_[—Ç< t0ìµ¶0iãØ/õ÷_ïcÄWt¢ºKv ³/Š4„ÛÖ€,5l1­P)ÖÎjJȶ×O1FnW³9¶‚‹½©‘nV‘×Á£Ï‘§l¦=FúɸE3ùÝþÛ+Ìï«Ãê¯ÇÇ™Üjõý²C„-éû(§øÁ—!¼t õE”×Ù¶>Mõƒiüde7ÝŠmÍËš#“7˜E8ðèÞ\u,Åäù@Èã‰YŒ€22ÁIe>þGUï˜ík¢n†£‘MÏÅW›DÒ”ÑAré»Iž4ƒªÇFl‹»¦Äì+Â4¬L8ÓÉûiÂ;#,ާ>ÞìÙ®Ä|ÑízéÁ€Fn5hàç#nŠ1Öhq¾o.4‘å3© xþÖŽ·Ýš-çrü¦£%7#ÛÚœàå©GŠ+Ðò‰ž 0cûíh'rðú~ø-õd'îžÀÄE|žM)â>M?#ëÕcLýü}ÌCY0âêø¦móÒT[ä„_˜errH>ÇöðöëâÎÂkš˜>Ú0ÛìE¿šŽO;&Ô¿¦nb|ßxw¾å£ù¤—´{×m¸ËãƒSZdGšóë÷5wVýZ}™Ù§þyvT´A·¯³Có¤*Ìþ#Ã0eg[' O¤¬kÃ{¶½ ÚËû`¶òe¦›ŽõG˜þ,ìÓ˦ÛÒç›cbo±U½»ƒ|K *9ßôúó„´gäA[xLÝf ÅZ‘o"k£‹gF›ûë¾æN ? xX#51wXÏr€fk‘J¶ÞÏ.b2‰L(ï7ü²íKpõ9fʼCÍoQ&]d£7Í÷nõýn/2ï€ê63ÿ¿#ð€¾­§øoQŠ÷¥½_`ì¼$u ÝD–tÈ`ŠŸ¥NüRY}s~$Á$ß´^üÊˉ$!:%‘ùŠq5œ’·ó^ÔEúF'kÏɯÃ[ïÊÔ˜‹ØöüT¦:dÑ–¤îuý?ÈŒ¤ÿ©þha?xr­þ""3ÿIŠZiüp¤ÐÉãPÚØP@Ñ`Ýj›¶¶[³hs=œþÚL í^ÀbÜ,”mÁ{ßPýï·ì3;š•«Ÿ3ƒêbÑ~¢Ñ‚_$Éd“K±5è7½à‘/â3Íõ-ùºâdúê:|óÝmš5¨·Þð‘ǘ%ÿÎ!nÒÂ]%Va^ôŒÓ™§oà›¹µí ]‹ú]ÑlN{Y”ÚþÈJw«Ðk¦À_<ý$íc Ñr‹½Q§‘ÖÁew\Z›©k³›† ‹Ùh“®dž—=ûô;Ô m¹öÂó}Ô[²åV¹’…()Å4FÚɳ7üí>êV;'$oHÏ:ÛËrÉeîj_À´_ËÅÐÆý„9^óòM“ÏÏÑBæ|µ­"í(uuuKײ 6±?ÏüÂïOi¢œêgPÃ9Dhzðhjoú[š»{£Qû,»"WÇþDê—FcgÁáQÆ3¯]Z4©]eåÐö|“XÔ:ž±<äiêørÔ·ô>çA¾ù'˜#ëA~*ÿß™n@k¸qZ…ñíJÚÏÕ, ô@ÎzþûJãÉŠÆX’qÿ<†•{ùŒƒ‹ÆôÞTRy½)þ*w;ø²MÝÔÕwÍ/„ä[~Éæçžƒb;ûì³­Æ¢@]¾:t°šŽJ¡ÃÜ\Zi¥Îöð·ï¿ÿÞ´jÕÊèð*Q·nÝìAOºoß¾½Ùh£ÌäñãÍœú³ôÒK…ÚCµ #þ‰©.HÐ7â±%ykk©+¤•”àI¬Uô?ùý:Â|µ Ê¥NÜdÇWÚ¨ª;up“\c±˜mïZë_œ4smŠ‚hh.TXa‹p›Kš,J8sI&RDJ½É×·aËM#`º<ßÑy‹YÐWqó¹º1¥=Qa>/sóáå¹!z¿™6™lýé§l]üà©wo/èëõoi7zQ%¾©¿¾s2ËF:èl=iiñˆÊ}H}Çä¨#§—† ÷“ 5±Ö–úWØtn\Ð7è¼ÿ ú¢Pݾv$XÍ$/0qô¦+æ."nKö·ûMªRAßoò{1Ù= `s|½ø‘ôŸÑ€›jâ¶P- @Ü÷‘FêØJ¾8`G‘éª/MØòÑv3ôyÙD”†šè'ýË|Ä?ý ÌH~ÑüÔ‰”ÿ?ç4ø™Æ©+—¤I«­tCK™OR\;¤þÓ1ôUŒ“ÐÊZH“ów™(‚¯4¢'é÷LJÓ÷Í´bb´Âèö'ÿ§X‘ïÌi ÷š;ã»p¨!²ÌCh;c𕸯(ó11ô‹ Ú£xÓšˆÀ¼·l$^/[~™󞥺Ò/“N£…ˆ ¼ÕT–~I)TÉ`8Œ æîD†Wxɨ³ðYƒ8Læõ¥N>ubT<Š`nÛ¼©Š¸©S[ßE¢”ÄwI…|›Ö”I¤ª¼d¦¬‡ÞÃjG¾åxT=Æýmf–4Ý=æ7d'/’èJ ªatd«˜ï×wšóÙOm§v!Áö¡…$2¿¨Ã¬,±5}õÍ\D=ùnÚÞïqÚ7À¯vc®M=;f†œòç"çqMÉÝåñÿ·5?Ó¦>äËý¦vÕúCóä\ž™ù2MޝXè•ù;—¢L7ÙðØR°3µQqÆè4ñOÙ®8Ö©Úñ%^¼ï17cÁB"™«(…Ô~gÛ»»È¬ƒ¦ƒ¤EíÀ\”1û·ˆñO¢=É} £êCïme†`®Q÷â†ÅhYêøú ýUÍÁ~‡ ëÖ€ZØ–YÒj ú9’Ë´qï‚EÐè4¥„¸mL CkRʯ+Ž6]°>›—$÷g±P8ÙÓKÉ3,Žú¾ü–U•Z?2u¶Wj†Çµ!£¿Møß–ºFM ó>‹a¾ê²v˜*RŸVv§…9Nx¿°tŽßxÆÍÝpÒÎb´%ßòwtªâ!³èó½‰òcñhÿØÐÞoJÏ\¡ ‰0üëiK¹(hW~Ò¼ý;RgeM³šuá[5Uƒ·0§ò|¢ê¸_¿~® F@YXVÞSþÿˆÝv!0¡¥(‘H˜VüÒɤïÀ5oþUÜÔf=Ž8â{€ÙYgåR¶;/ê8IƒÀo0Ãk®¹ÆôìÙÓç-Mûçgµ\]ø2÷Üì¹dÂ\{íµŸÒœ½“7—°¾YŒoyÓ.(w;2~ž‰çžíq=ëh¼é´OmYŒ¢ÑäÙ'½"Ûãk¬ Ý`Ÿèúõ¦o2½Ì&†Lû87=—îÞ]@¹¶wµ]}m‰·¾Ã[r)šæÐ$"§a–…Ìv´w±~Ó—~) ß(’}ÄýYè¹pÈþW¦ìk2ÞŒ@¿ÐÄùþÒˆ ´×4‰ß+èD_·«3‹8‚À¹%äM;¨\©;’¸ìåPâbÓÚSו“ª0n¬ýèñBÿ¿µ£uåcÔÛ9”r?€ˆÄaÔïQA}Ô÷ÇÀB”8÷cÒŸ¾É¹ú¯vŸl³[»’ÏÅäóŸ¨ØŽ™#=–4¯GÆ“©‰7XŒüx¡¦r3é—8 [éO£!<ÛïDÝé ¶—Y’É€þñ¶,‚§Ìˆt­ÕžÁ´û¶ìëSô·}Ú¨szzMlã¯BÜ£YZ†ÿCØ¢½;¼mÑØNf‡Úa¼½»ß1&ïHý¸š»7Ç73'U_ŶáÍ(€mI¿.ùßå-´•1µ‰þæ”ľ|ÓUѶޔœõí¡“·ªt½gH ǺãÏøj*$o}¿ïø}Kœ¯øÉ__ÁP{_A^&€°Ø–\Â[ùy nX=Áõ|ŸÉH¦ñ~W'` ´òù^sôŽãrq£k’7ßyþæDYÜîPÞë}žÃ{1¢DÀÖÔÍ[|£2k˜»[?_†ú=„‹Ò:ï! íYÉk™"êjWBêJÂêùÕ™ÿ²‹£[²¿9µþs ‰²™†¿LX£$Mlms·»ÝH„U̶¦Æ¬Ï¸¦%×–Ô³•ƒ5éÈ.é<½b«›{ã›A —ñV¿5ò°æ’]f¬ÊmI·í@õÿ£=@tHB0cÜtÂDÊ”°6êÏ’»uè»#ø[á® „rð*fhF¢?Ü~¯Íï‹@¼–¼UOþÅÉPæ¡*¯ -Á½ÚG }ÄIù÷û;€w9•w¯D€¿…NÞI{/X”)ˆY¦ÇòÄ/”tðñ™ðáC‘i¼óqݹe>Aòß6ȶ2WÒÒTE†n®ëĶ1ã0ís'²Åb´ûÞ- 4·dÙuw%þ®Õžº¾†ÅiyßÉ"“³Û®ñ§i—ÅƵƒ™›6åüõ ×I/sRRÛ‰èCY/Ïÿå’}T_²õ\ɾyæ³ø.ss Š4»û?C-sjê‚«­¶4•Ôˆ¼$± ö¯’g®š¨\èÏmÎ{N£â›“>˜Vuߘ¸è.:Ämà 7ô~ºPw5¾Ò¸ ’´uÕI5`m4»÷­“†qaj‰{?’ö÷²”·MHѤ] zhJÉÅlË%Ù ¼ ½RhKÜÆH¤ÞL ƒTÌ~›´¨/` c1êÁ`=• Rÿ0€‘„^a+,Ù8uíý……Gù}Ž !-•ÿ_Iž^šÃ`¼‹Áò¾¡ ¯ ®F[#i÷Ä™òç3Âõ!Á¥’´àZôp-ÔO`šL8<´)€· úÊ{œ&ã€+Ú¢h©â .úTt0†ì˜öƒ“éú*“ø(Rß—Ù„p‚k4ú&Î%)@[ ´»çÛx£«>ºóíe77оà½u¨ì܉ßïlF$Nç Àúê¥É¢m€#ê6ˆ%ˆKåWëÛü›óÏ_™ªŽ‚ö®|Ûz’…“½`ê‚ûÔ`žóHw¤Güd>…Áè\­Ð¡úˆ”óÀ›Ôh¢V2yï‡Í”úÞ\u\J¿Í$}Œ{}•ÙÌÙp¾Gðè¸nHêÒ¼îÞ…^¯¸Õ–Vmöñ'÷‰Ëðï\îîØ*hÚU™}âÇ1~nc®Ç\ÄVJÉGoZ’Ë`+Tfmj*†¤®‰í|sž]H\“È@]µrDt©òYpš½Ü;j0c é9†Ã÷FZ-Ï̪hB?”ìÁòJŒ“áÿ0ç ­µ}ëFÜ»>¸ ¯2yšþv`êsÛ°W±ƒsyËñ ¿Á%ˆð©Ø'æXÞc_xlþ¾˜|9“ëÕÔ§ã'˜ÿ¦?1ŸÕ4/&޶rŠ~V®¿Í«§í8y®ÌX ™»%¹ñí«E¾k;ôüýðóÕ0÷YÒ"‘l$ Mh«µ˜Ü›ð ƒ¡Î}ò*žA¼b”z‡:EË*~ k_®;‡ÄFŠP3/ú ¥Oôø+ ô%ZlUxÁg8Zó«ä'p–vúZî+±À`*n3õÔ÷€äÕb ,0sÄѦnö¢‰nLh.‰óm+©+L‘Ÿ#©ïFûÙ sdluE5ç`;s3À×P§Žr„„rAå8bÛRÞ·Ìʱ¦[¶¯h×ÎŒ"ÕÌk€³:4t5™JÁü‰±&e `íl€â³w‡ý談8-ÜyÜ8lÍ›ô],ØýIßúÃW†‚í²ö´ B©¡2¯bêpá³Y8l.If|žßE”õÆNÍË4ï(ôÕó¡^‡ÀGtõRgúôâ^·RIñCŠ/g0ÞºçDD7šÃß\-ãÿ6( SåR{xÛvŒ5º–E:ÿìWr’ìÈPrü’" ÔùÙ ’áѵ½Üµíª-óÞ-ôá)þ:ß(ÍÛ…]¢Ua ßH#ð({¨$ÿ šÊCœÿŠG·È3Õ¥$¾¤š‘›¾Ac$Ðw‡v(8ÌÐM· u8)[ Mæ²âÉV˜H«|u„Wáv¯ Ù0mø²<ýN[#—#îw䛈)UóÞ“ä$1UÏl*i ¦~a$¾¡üõm«ü²Oݵ!ÂÄ.wuYöKÕf_ Ö¦'ÑÖXgâì”2 Œ“³0›CuÚrwS—4,ê½£DÙé}‹m°}ÊÐÂu×qÚ’ûœR¯aïSjÚz¼k4n¼piÇR5¹c0ÁÞÕ,R?Ì,‰†[ MØ>ÂÝöç>/êú[Ù+wE9,¿÷™ zvÙì½4ö¶/¯5l\BûΓ&Ój¡¿Y­oè©3ÝÉdý@ü§0)kgŽòØ7“ø³.œë8×1¶ªa[Mö7£H63„¶íõ™Ô`ÄD®Y!>ßÖµè±òªy—ïð’/e„rÙþ5ë»Äõêèù|°\™—ý÷Þ;mIŽ- ¸U@Þ²©xÀN7nüÊ3Ĺ³ æŸnHþ߇r#Ô6›÷[h®jžÄèÛ‚zâëvnÚ“çŸM=¾ž6g†!<8ç+;Œ9¢ÿd†åîsHR[OÝvîÆ×àM´x®{&+O¸a&3‹g¾o=±öOJ „Âުƭ?Jg!óûIÆ}J{ÛÍ·áI"L·»%f‰‡´ù”¼i[õWQ¿[S¿çS¿Gp7ïö__f%NƒŸnd¬ýȼHÈ·©Af™˜øÍŠfI–D×èÒièׯÿÌdØ;keÌXûÅSŽË¥Uy‚4oð¼¸GãvH‹h¢c™Øj;ü¯¦wS­Ÿícég×’g+úÔz±¸Y„g ˜É!pýcÓ)ƒìÊÆ‹4jÿÁÁO”X ‘a­¬Ø0Ùæú/t!FrÀ+ ™ú#C“YÏõŸFd¤4}Éã.æ¤ïW_G7:ŽHKù³gžìG}Ñ.Òßq}–ëû‚ Ú÷çü !ímæð]Lúò¿Ÿß­ÿ>|™Mƒ¯©í… ùjìf.Éâc‹èÁÞ`£óžäèN>Ô™(¥vV-O[=•2 &Ñ—¹„ë4í0o@¦v’8T©·Ö´)­ÓÜH?”lüq˜CXp{^‡ñ@ªµû0‰r_ÝÉöÞþ“V«4§ëÏàVí.O“™ÌLž@è[Ò~fLüªH9“pµ ½R•æ(l‡ÍE´ +³N«£½ŸÓ´‹rùçz]²i¼ Hçôt£¿·&óÛX žÈ’Í™ŒÕèþíçîXBíiÑ,PG*›ô:àdœÑIQþD?(Û¾§Ãϼ…¿ ™&]y¶9ûé&}«7(àÞ€ö¿íþfüýíJO‰-o:Vö1§ÕRc™(~áh®ßÆY »ü?ð”¿Ë­W™!ªL½ã«š×bk›™!lSf)d.ècÚ`V@þëJ[-_ïÙŸÓˆlJŠr¸š?\wûÁÓ`ö¬€È{)zô¥´ëvøíF†1ÆÉ^¾—örìK™Öº:ÕêvͰ|“/C#᩾©CloäûûŽôÓ‚n%|©¶1~ŠJ¾@ü¥Ä³1{šJŸ7¯C?Ø~p;å)ü¶Å y.Ùúå’Ì3îPçdü¨8ŠrŽá÷YID¢+¬É"‡7—b ¯â¹ ¯Ü¥€¾UÄص2UYì€*“VîѤ-å²UÈ£Óü•!^ÐWå(xü+Ëûw~¶½€_ix4•ÄØ–ŒWXY“êN:™M7ÝÔl¼ñÆô•ß0rAß°° Ÿ±`¿’¤­Õ¼&uÓ²ÂüÏÎTÃH€ ƒx€d†¡9Ô\Ó-ö¸ ²øDxÍ–~^š¸îTTÔg„¾/}ÀVttÙ Ò¹ÙÁ~ í}äSC™ÃH',?¢Y|±©ü)öf®äÍï·\Ϧ»ŽI}ŠØ!-Lñ¯OX ´Å³Ø«^r+š`M¢ÌXslý1qßaMÊ+$ј´j¢XŒnaÅy(“¯w#'ùNê 9iœp+íæTðhŒí‡ ÎCR×™}ˆã?¤nœ´ÉôÀpªIÖ1ô“-àXÇDMº³ÏøZH€A¾J”– Æä["rò6óyÃí¦"õ(“šÒwíLY$Ī\.©=<Ç–àÍè[ñG=,‹MÐMÞƒô_¼ ¯þ=Qw€,å/^ðƒêÐW‚û4¶¤ù+á.ìíÜ<ò×%ŠSp`ž’T_‚Àö¢9.^m>õ3_§FOß.eK®‰÷§¸Ž¸ý’"wcêyuÅ•fBým€Y°°åéqÌ*O¢ï¼EzoÃZ•­«€ÝÉ ßPñž1Üš{™˜z»Üí8‡msYê=N0WŸ1,p (vt µ[CšÂ²çúfb³1@ô„9K:Ï.•iSŸ¸œG~bóåÖCU¸áqOáG½Õ¥áM㢃¼!2UÐÀ¯)[‹É%uR7´hj-0ølÚØôáyÛÑ6VåŽÉtcZÂõôÍÜûôZÊÓ’ZíD€äS Ó2€C?Ô‚ƒ°ú%¹ò³$[µK—aæ¹(íâ1Æô^Ôõ/n„àUÚ¶öpÂjBÊ™UºñåÙœ}z®ŸaLˆ³ããGSE|*³$o0ÕÜbtÀ£¨Oj& ¥O䇹dÛ6ý=!’~ýô6ï,«ÀtbºÈ5þ@Ý%™­C·€/ɶìŽðŽvÄ•¦å»ô¡m¸æ‘w¥é«ö¿kjFô-̵Q•JÛÖŸ½FŸ-Ÿ–5/Âg^¤_ŠzÆ[™#SsŒwïÐïÁØ]°E:ó1uÀ/„dÜb#xdêÚl1ZžœÑ|­G;|¦Ãþˆõ$„¾žùšë~†ƒ,ÛÚÚŽ:ÄFjôߢÿXH|Js »€œï¡Y¦›øêÔ™Ú?i4……„ZQì‚B ,xûðAXºñžxþ³ùÇÅûpO_mÂ8›Ï$Ü%ìëjò­MÝÙ’B:Yñ9 =àááŠy'-àÎ@/Ì¡tŸ£ÐÌ™üÂq¶$LóÆ'øÎ:+È1Ögô׎œÿÒ7¾ôŽƒYœTí¿)“±ªØ¦6y¦„uøç—_´‘Fsoõ‚C¦ˆ•'¦n€š e*ÛÀ«ÑçϘچm¦oz4h_ƒœ‹¶ó.é?Êú´!x¸Àñó˜;ÁÇóÁ¯ÌKÓÉJ~5Ù{; s¬o –ñ<ƒÒ~ó?ÆÐïÝܸv‚7M±ã뇶-ÅÄâCšçá´›=ɨY9ó õ•ɵXÉYúEµ`õ{ÁWžÔ—öNmï¥Ok¾qfì Þák~o‘“KUô«W©@aem‘}Àwe&êð^:˜òŸ@’o&bÓ¹+¡®Ís_¹+îç»N¤n¯òy·ÌM7Ú;íFÚ­ñ<çÎffˬ³¼þ.òl"X(½‚E¶¦gb_sF¢¿y¥–ñš™--ÏÈäГô-™X8ØØ#M³8Rü(dÜoYxù»¬¿s€a+t„“vLÔx\|Q³g\×xüåú–zliÌ@¬W;0÷øƒ¶­ —d‚Ñý÷±]ø.Á_ö†ÑX®…êe¸Û‰r?F†ÊGþ\¬är”5„¢¿• {†ÃT_ÈVA…ï“#ß* øUâæš?@Ì):xx ¸ Üê CœjÃs æ|½îU_ÿä|eS6Ê´@ð½Ô6EÅ„'Fôÿ èëÆø[ ­X"  vÍîzq°[g„¹¯ºpýÚÀ”RŽrâD2’r2)3î‚~ÛS來±bI Gä°¤ƒr®ä•8"Cí×Âø{Lܳ‘³—Éh i’'rФIù*‚â2éŸùv1kD_óα*Ó•mâ^è¸BzVò-T÷rñ{îd/únâzÒÇIW†D Ë&Þ®IºñML[j2F›6Œs{R/€…úÐ,úòÏÞ,Ôv«oa·¾ã¬Íì‘jdÏ×KTÞ/˜h«úzœ^ ]O{겚§ã›}Z¿mæ×Ž*:˜}¹6³ˆI&Ï7OÔÅbÒ‹l”VßÍÄ’Iþ\&ë™w­ë7BàûÅ´€Uy&X/r÷½QàiÎí ô¹W™(wB§óU½/ýó°Ô·˜‡JÛ'Ôð/+6aqâ¸Ô½Y-ÏuyŸ1_Õî‰ù•9@¸™æûé˜øÊæ¾ä@soÝi%•E%Ò. MôÆäfN›/·(œ|¯A-IÈ"ÔÍG\å'+œ±Fm(ÚˆåþK¾§F’jl¹)KŠïšÊ ¬/ü?~Í‘jÃs6 ÀÀØ ¼ÛpÖަ}EÄß­íúÁ,ðjÑÒÏ­•Ïjü¾á§úŸÅ/@±ÿPTñ: ÄD_'0ÅsšHS4~¯eG8»a[Ɖ!Èp{ ×=`x¼ìŽÈåÇÐҳĄ¦¾ÓOÕCU·÷—R‹ÞŽˆNK,5…OéŽe<½ 1³¼¤eÄvJ'Ûª­c[š—*Nä{CúâO”tt²Á=ñsÍ…ô®#Í«fd+cɆ«5«Q*InÕÏç®%ÝЂ²_Ç"Dd¡Q,piä™/h…2c4Ž"Uùä 7b“®jË’Vá÷U“rpi<ÑAœ¹šÍmv°9ª-n¬æ^)gõsÔß)äýZ™™I"ÕrÉ ›Nßuºu5þO-g8}GfwDƼ»ïƒÿÂZô urÎõ,àËdP)¤ƒ{'3vžÇ`’×2}Æ|íQ#s×úçê4"3Íñ¾f>ì_?4¨k¬Ö¨¦uÔ ™äž|¯ý³Ñyÿä¹tгrÉÊÏ‚Ç6Kã+—›Ë&¾Ъ6ôƒ/$ô&«-v¶´%ŸÈFeÄ{¡É<ƒÓcÖ¡>Ò®£m$tè"±Þf©ÃmCòˆÛWÙRùvI"=ΜáïaNÚNÔ›vй ²àVY×7w¢ž„û' A=“þœU›µ¹§ÕÅÜöè—ŠÆ£Á¾,{ñv¡§¿ ßœ¤”±åø~·R†•±%Ô^’ ¤qïËø²h½ij2Ç‘nUÊ”má ¾ÌGdFç’õ–vþƒÒcñ{~3ÓÄæÄó„'Ìã´]i*içÌ&˜0ÙMIL¾Ï¶õ<ÎâTôç«¥8€ît\etj.9@kñ‡†Op…Õœ/ª½9Šwî.íb€’™ilÔïÌJîp¾×eh_V å|²bws@åÅfÔÜ §ífÉwpŽÆPšéÇÜà¦_•<Äùþïçf*íÏ™™TŸç›Ä)Kú~7jÁ5ÜDm-kÑB[Óm{¥ë^qh¡¡'è?¾ñ0q |õ'ΜMq?@õV.u¸¦I¤m^u(mæ|x™w»Ð>´Su~l+úñé<ó]røÍj¾nJ[Õ»ã¤4~‹‘ÆÑ§Wsr|üXøÛ–¼÷~Å’yÂV$>‹+ ÷à÷…Ç?ï̷ļ_).¥ _på™qÞ»„6Ü™¾XQ‹’ÿÿ²ýdkÆÍ¥àO/5è»9òw±2j§ñÆôÏgÙ qšáÄdÛ_uÝ™6À7f6go¾XF6l:ýz\~žá&û/àÝzñJv\¸ð¤ñ/LÚqôc7z3¯+3ö¼Nyà›%FÅw=òÂfÀhcNÝÜGÞ'“÷sÅ–úõ})y~VV*'²¾™új¸9kGúúO4­IÐxÎ:¨fVµÝuÕ¹á ÆÎAÌ]¯D– Ê÷Á76ÌŒK›¡€cûèØcgÕ?-eå/¹95 ßfc\š¢èàϤ&Û.§ø½Ë¸z4—P7ß#˜M)#Ÿ£–©Ïߎ±yiøò‹T²fÕÃð{xVHŸÔìâQ!ñ™ŒNÆ SLã÷ÐCÿ¥kàoTšÀ§ù±6ß,ò<èÍHæ~´Oðú6ß­Eå.Fk"h¥X´.µÏs?•lçêÀÅ”ýßÆÌ˜èÙë3è}’u’¤â¦À- òjNÝ·"½˜‘&€¢R´´zè!'²ç1¾å‰f$0H«Zö6[”*Žêzšû(ú²µgq2×6!1ãþþÚäÚqÓseƒñn„,™ˆx$¨uç{èÀ(‘lƒêЫ…EÒNí‡t]Ì-« RœbíÜÓ³¬” Lë©H)â;lÄ;»ØÍ¬ÀŸŒÐ¦²_dk˜LnD‘4GÝ­}Qq¤¿¶ JÓ&L{!ô¹Ò $MÁd,q6RÁLüïó%ÛÞ³ñ ž:l°˜Æ°ß°/Ë’oT·:@)©_ŒÃÊ&À—?`Ñáª'`VëmhaàßÍ'~Uµuv*%SÍ ü![Êj4öŽg€º>{oŒåüñÞ¦zéy-_M™4YŽ¢$E»›Fq ×”u¨Y1{p^ؤÕ5û¡S¼•kg´újzL‡D=éãQ´§Æì{G¥7ñ#éÿƒ öK'ps#` w†wmó-ïòãÀ¤í³÷¶kÅ`ü‡`üiŸæ°â„Ñ[,œÞƒ–£Íò-º=Qëø%U½Ï•:À‡D_³4ƒ½–ØÃôJnÆ×*²ÌZhK¾¾ü—ð£+c‡š°Íw |£ i/ÈÔØÜþ M¦>Ñ‹qçqzÀR¸§#‘š$ß¼ávôËÆÙùB)\P¦gŽoÁØ”1d_|?xÎaðªTX)¤É_Ï«£-¥Ÿ&FXÿҼŘ*¯7gaû‰Ô5¾œÄ'&V4["ƒòµU‰ ?²´íaožówÈ1¤EØÜÛœúOÌâíP²L±>ðŰGh4¸yÀŒ1Í“¡ÃrøÅè_Ús ìr×Ùür×ä;‘_¾}ž¾ào¥‘ß^t-m}cÜâ¾ýfÇ$ÁC–»Gc5àÞÏõ1ËXS: ìg«•v1½ã›™³ìq¡il^ÏȶÚeù9fkzbÓº’˜ÏfçßQOhŠ¿ú¢ w4%·–I£⤰p0 þ¹¾Q$kµ•0ÙEsW-²xwÀɦHP{ÂÔ‡Cþ¡´(åFòk„³èåÄYíÃŒ5V>£MÍ7JVÉày_´¸î‚ ¯/Æ_t£Šú—þ~5 @îïH-5$… -¼pc ¯c‘ÀÓrÚ®Z¥ÍÚqkÓÚf–ý炾º}¦ƒöØ¡S ¤8éÙ_dA_Å” ÀyÅ“D†FÕ[X½£«Yí†ë¹.è+?XW*CË®\zHæÒøâlýª68šKKÍ¥îx»•U ¯Hÿ]-˜'õ®FxIãgCž/rA_oI\зáý²ilä…ðouʦÁ¡ù+¾&$€á_F˰R= Ck«%IBJë ÕzA@WøËwx´ïúŠt8W1ÐWqÊ}ß`h)q%ƒø!Ž»™ÿ¥qjôòÑb„µ–ÖdÁOÇ •sxúdzJÂ…¶ÈÑg‚ ¯ü]ÐWu«If¤½$˜š,F‘Ä=/©nÏò|7L}ëû@ß.¦SÕhsàÅ´n¢§}nεÃÜUqŒã[œÄï`#w2éâ’@_-‚‰úŠnæ0!mÿ,3ƒþÿdz®éÌ—:$ý€Ù#u¾ì'jšèÒWȹý²7þº“y Ù|•P~. –¶»6•Þ¦,/0w· ia.vB"ÍYi³BúZ²Yö·¤ô-Ô™@_‘jþërþÍç›2Qˆ%ÊÙ<ЗŒb{ «ßföv¶Ê7ûˆ+£sê#Âà)õ‡óÙ_²“&ïÄI‡üÈT‰Hm[¶žÝ\¿K´5§ G¿8aZ0T{z>ì+J{ÚZïr’‘ÐÉò]ÌÞñO6+tï ¯V«Ói{Z@Ñ"œh¿7íí@²Õßgýõï'Ê+9lTC_ÓmÉ ’§²™¾ñör‹²Íé“á’òžèä]w:ÏF}Ìe¾¡ æFÅ­S¯pÿï²3«½l\}ù˜À?[Wµù˜<^MÅÆÈ˜o:¥EGcSYp ‘¥6L¢*v¦_Õ™;eb$Hm¾#ü^köFu90¶>íª¿Õ!õš9k£ç°uxóÚ£,è«€@Ó›bošBxÝ3”êæÄ@Æ•ç¨ËëƒO³-ÊŽWÈyз[A<ëÛ•×}ç²áá9ßVö³)÷Á<ó€Ï{È^Í…Êqcü|óPr€ãW=w¾Éqk‘¢Nõü›sßÈÿóø&7!1 \&¾V #oÅø-ð/÷öSèK®F ‘£KýѬϬn®;„CÍàŸÖ>yø; ê9B×SF™Ò‘Ýé,i1òTzI&öD3ñˆôÏ<óE¸¯ÓâÛFí`:oáîòCÆŠ c’uwä;|ŠöâzÚ=am×3Ó·æcv¨ÒáÒŸ°üú»(<)u§W>‘ä"(3MľîGÑ_·ŒˆäÔòõÒc â|#žUu¿Ù¢@fìn=’|wΦá}*Oå~ £sÂHþxw}+K™÷¸üÕçw8J ÇV %ï­|þa7j5ßÀצ±ˆ²ÀÉšKø£äDŽ׊“\ã‘#õºÙÁ³mÕõYø×Eà’CÔ«éá±U¬[rP(è+CÆå7ßúöâÞ´¦~Ð—àØºæ1ÌÂì‘neNd ‚+æH»œú~ÄXóIt«ÎÅoŠãÆ[ÿfòT ïúã»(ÕØ£ù1Ü;’¼²‹7ÒZ|§=‘Oý½©‚næÆ•÷ñÝ]Y8þϸ¿E[™_6NµD)¥3nX¥õË(DQ Ñy¢#‚–y G±ÛÏy2Dè‹5Ã@‚o›Uˆˆ+¬4+ã¸<[bxu7U–[Dç85m„L3T4¦“~^Ó“gSÎå:7Zðõä/V³°IïØX=ÿBœRik&zå€Þдåg†·–Xq݉Ú^× ðSÍÞ è¯`cÇ¥¶EšZHS@ÛMµ½{´­ûÅÛš³‚¿n»žA"HGOÊÁ°uÿ ­XBUK’„É]­J!=yý’¿ëÊLFÍv9BuHÞ.!½¢Ž°ÞWæ]T:.9Šƒ³*wë:ºmî™°Ž^ÀaV7ÀQüz ‚¦¥Ô¹ âCp6wòkŒN„V®y£»87²àؤ‚¨ã2?°íÍéSKÕîX?#\ &f­8ÀŠ—b«R¦®ÖgqÒt¥œ’éd.dMF3 LªJ nð?ö:ؘj:©Ü$vŒ}‰:UqHrO?ÚÇ#Øý•¶·!–w¿)XÆvc;ן Ï(˜g4ЮRÔ©»ÀŽF*x-”-_Î,Ÿ~Èw¤ 5œbl’Gø7÷Z §á|óJÃó@\OÍH»Z×oúôŽ·1KÇ<=hþ€SÌhƨg¤‰ënó׿μÿáõ¯ðüqØa]œ_þÒžú]ÏÆYM²û«qø•WžøEˆý8@h´]ŒÐÛfI€|âL365ÙodÒĬmÇ™`>‡o•§uwævÉœ—@=›kf mñLüÎ……;4W=¢Øöщû/üQÓ¯óž”Gõ;gE¾ó…N¸´"Íúþ¸Eî.dñðº>äs+õº ßä¸@ì©”ygüÆüÃo7‚Gê4Sñ eß×)¶õyõ“RÁÄ+:ÕN^€F_€ °Q%Ÿµ ÌMÝx.í$õb.ŠÌtœÍ€OŽÈ†Î2kš¯«^㮳y™c%-½‡ŒÚ³h›Ž,™ËÒ:Äχ0. ú*FÛ3*HãÎ%mQ_SZ¼>¹ã+lý©Uü(^n>ŸR§—UÒ&âÿq½œkf’y¿á3P»„ŠÒg´ƒ½éÏO5 Íý?­i°@4Àíó™›;ެ¦ï¼£É÷ýlÚÓü]ÍÚ€¹£è÷êKARìL¿,wÝÅÈîÇ6|γf³ ½—Ù‹'bðÊØFðuh7Wâ·LÀ¿±[@Ï Fv cHyDJœO’µKL&Þÿh Ê< b·‡vhÜmèO®ÂþVq"eÝ5˜…[Ò¢ŽõŒ9 æ»j÷g¤&«lƒsÿ~@F›Á¢ÈTL÷S¶t–Ë&Ò1yý4¯<³”€•hc[–±h”¨EcÍ#4æ@iϵ\’y¼žðšßJL¸üJrI~dSÂù|â;?Þ,1—¿g´·Ò_):†,ürçG‡ˆgÏŽð_ÐÞ²…$Mâ¼Ún¸¶¸ÿKåÕâSè€èÍEd Ó4õ_*¬·^ [m>®´‚’I[š-ÕÙ',îþÊyh×ZÊrJRZÜÆ—®ÔÉ &f×ÃŒ»°Ý: ˆ8¬VK%Àÿ,+PŸˆ`º3ƒÞ‹Là­Ž†Çq½ÜEDÇ#pßÍ$q ÊànQPåš°Ëü'¨9JáѬ,hÀ$&‚Ž?#¢¶ˆ÷Loô>¥Ñd¸m‹FNèÕa§«þA˜ìhÎÍA߬Pð™‚=K¯­ÊÑ|ÿQ‘‚haÆÒ— ã¥L«fôêÀíË ¢·D®j…ÀBãIûl8Â]ÔiÆÚFé¶ÉűJ¸s¼Ú‚K¡™y¤Ä¾ÿÀ\F•ä Úë¼÷”Ø>´‹ñü^˸ë]ÕÎ}ââï‘q/~'Õ¯0ùùd0¬427Ìȱg!Ãí¥1#9†`Õ €L2w@Xº'm=|<ÏDk`5\Ÿ•Ù‰QaÃIöPŒbËQVÔ_úÅlHž¬{Ö!s³¤½~oÖ¨¿»ïì™:ìÍÛ¯Ô·×NþÀξ\¹ÈDØc,üŠÛW€·Å8âÁhÄîõ$ãˆ$ÿxãoÉØC-ø2Ù[·¦¼1¾ÀÍÛ¶½~Áû¯ƒeIéçZïøè»ü—ÜÁÊ1d)o¯‰ŒR–Ç BK³v¥o–"‰L·•L2mRÙŸàÑåWrZ™€â£â®V¯x±æeú¹<¹Pšâƒ¥RGdâ b-òå«­úY©É”NÚúqÚÇB"Àª•ÞÄ÷—ŽNä¿(>’å¯NÚ>ý/•WuËK©pèÞÒ6‘húO'rÍT[™Y†é«Ò!Ó‚I{ÜTîÅlüFeä³²s•Rùîj'²Oì%g€öº”w/£ý'äi NZÕŠt8 ð )ÐnõÀ¤Ô #÷I”£!õ³[˜<Ÿ)×%ñ[áeoi úÅèS&nk©Óèé®â'íT M)õÞÑÂlXÓ iä×#Bd-æ‚ZÞζèˆÀ%:Ÿí1 Ÿ%‘µI˜/ÂÈ4‡쫦½š^…ÒVßÔbÄ &õ7Û•àéL¨¶Ê‹²­­7?è‘ Ty·kf½72ì?9¶:zW¯sö^÷ßFŽ™’V͆ýï:”ÙÊ"´aFÎÝÍÅjí[Ò èþ“'õÙ¼m,IèU&À¸6|e°+ï$6$…ˆ L&ý}c³Úæ=šo¦ ÍDeUpª´Û#Rf9&ß2?³˜ç­U•ך–1ÀóøFZ€×±ÕÁã¯[iJ ¦o…‘÷—"ë9,Fa·CáBý}ïšH96/Ã×L%ÌüV“™ µa»û/…*Õ7ýh©!b8€Â˜À7?ÕÙÖ]œ—ûc•øߟùÜ#Ld°Üa#}H{:‚~®~%ÍÂkcnK=T4AM_‡›ŽóHdçQ§ðÓA–Þ] ÞÄ´`2Í'§ýÁ“¢–Aí*N480cÁÏØ:ý%;¹ ‚Ã2£E…ÖŠi@–`1cŠùÈ5Eƒ³&Ðç¤7ÉD/…ÊQl âð^Lr|e–e‡M7³OÅæÆª}íÂÖ–UƒÍR¬¶}b 32ö#Ýòl€Ð¡´é¯tûq}>›Ö‹‰-Ìpíu˜œžŠ¦poÕ{]z¾j„ég¶ÊíRÆlú ÓÓlƒmÕ ¾ñi3 á"78~¯Q—“̆-ö1;W’Gi±í©ÏjQZ“þ+Ñî$Wêê¹' O‡Ò>ÄV55RœRzçhO8@ôûžgxÕÑ›¶¹ãÿÞñVõ³ùô‡þ—j«{ɸ¥üî‘OŒ¹¾)0ù&ŒÅH€ÖL¨ÂÀÐ~´ciö‹Õ›·b/¡F·Îô¥þ‚£ ‹cÞíyä„wø6ç`Ö–¼¯khκQ–GÒæÎóÈ:n@¯Ô¨…Ƕ®GÔU|'¶NÀ÷Kž½‹ï?èQ@ÙÕ!2­%šÄ*nÉúkA\v˜›“~¤]¼Qw¼ù1‰Ö»—Ð^6õ}p™æu-|Ÿú@óvÂÌ,_Ùc.˜y#éðÆËA¼®ÅîáuZz½X@Ç?õßx÷µ%…ßœ:ÔBøR¡5V\€l!ó ʼn¼2£aðWŽ;7ÌÓã&ÿå‘Ë–å7ÚÊQÝéë{B4ývFå©fRË[QrñöNc–ˆ%Xd/p{ÞŸî€Ýð”E[2öÉîÕÓQ`èxÞ\$ Y »P' Åìl¬+²ÈFd¿ª¹>a¡§£!2'Ôµ‘Æ4ÄåB¥ÕB‡$ýw1b„I¥Jz}ÿ%_(ö%MT­nýi%ød›¾^Àæü@!”¿(xˆœ×OþÇÍœ9~á«eKlÕvébÖZk-“N§M«VùP{MM’(Hš`ISxzÉtÁäò<•oᥕw/y™R <:#ôO ϶»÷i™ñƒ …ø–÷½—æâhôó §~º)¤p,Àóã`Ç€I#øl’— iL+àlcÊqcM ‘4ŸF N0 6[@IDATÅ)ÍAèºÐ<5×TÞ`x$Ñ +²³·ZM>ºÒîB#ÊhâÕº—:xàUÛ›BLDÃvÚ„H6ŒO¡M:XfNÄ;HùÏ&Ñ&Jç]‰Y‰Ý|—#ÑX'±£9¶þ¨‚‰ö@{ovÅ©æ{$’ó¶´Ï“ÐxwmT{¼›õV&R\ÍäÆ$¬2—í'Ñœí¾Û¶Ã®ØÀüœUуh[y’õr%sæö6À‰gHgIx•¶¹EM ^Ý™i¤Eñ4ÀÖž´ŸÖtÈÛ[ð«Íö}ÑÇU«ÓO¾@ÓP)ïAXDá˜]5Œ~Å€@î”&ŠÑ¤éF!^&€Àš|ÑÖêĦ€+§G'fºâWê/ ^Û´eËõÏMâ°?GgQ‡jÄYTÉÚŽZ̧=n‡_ôÅ´€wQ$?žÆÇŽü¦æ{á"—¡Á´z*É“¦«…ÚÒ˜LhÔI¸/ù9ZÜâ‚gœŒe\™ÉØßž¾5Þx:nç¶ÍÃßnC·íŽïoyÚ§Û06Ϥµ'g?()K’!z‘§g3¼UÛd,Õ‚Tî Ç5³5ëš?Œ¶ú6Êa·‘7ÀmÃÍvÍ|¡ NÕøkfÌagô4f¼ÔTüObs³S|óPýýØK~Ælމƒ9ñ^æjlïî[Æ §ÌNlù^ŽüJ£rOc|æLü)Z^-×ÌÒaJZÊL´P³Ñ"ŠWìcÎÞ&)û­"j¸ÅƒTÇ1ÜiËï$øþ™Œ÷ób;Z©jäµÛ±G>-;†(¦ÌÎȤÇÛ^9¬’2OþØÅHGëXeý À#á2Ï´P3´dºÏïÅ|ß7í•qÀZv1ø–@Ýä_zyjI9ˆßõ‚·ŽòÈê©¢½•tjb«Qfw9Ëø[¹‹Òã̽é Çsg±bzdÊZLy¹± ‹Ùϳøwò³»$2Ja´ÇMå¡´«Ý—ûzÉXï/RkvL¥Íësg<¤qyÚxŸŒÌñ0cƒÆ«{#ù1<§Š¶Ûðœù/‹My²›'í?í”!zP¾vÇQ©Òa…²Î÷©½Æ8<…´»ì¾–oJMåOnirô&<á 1ÈÔ\ óÄ;#ü½u²Í¥ð¬‡÷ú6ò¾â³hò5s9F^¦Êì‰HõÐDŽbÓ ý[—úÞŒú¾Ázk¹+ífÙNãûjC#ɱ —óÛþ4áj_ÈØð®q¸•ßfŽŠïÆ‚VW³]ƒ“'²§Øv¦¢ª¿Y¬ö öœ›©–’÷E 4—Åä+ÿ8cf)ñš#LœDþº8ŸJn·XGsOå­ðèÇù”{Ë*¤ÀÉ[}ÝŸ×=x¯aUMg~Ðãßç¼K/9æEûÛ8L„±I3w&B«ÊÂârŸ÷¹ÇEƒ«×ßë§ÎÚ.^áM•Š0º“&M²¿õ×_ß,¿üòÆ ¨ÊÿÌ3ÏTð? 8ЗW/¤¡Á8úê4HȴŰکÉÁ'ÛËsÒÆ€¯ÜWˆQÿ–‰t-­ž.«ðmk>HÖ0ñ«ËÚ›“ðs)À¤{@œ÷]óúФ2©è«Iá<~:-}PåA•”´&9úžÉ‹‡žÙ9İììM ÚHb‹ya€Ã“pàVš:M·S(臣i_QàØxê@[ûjk!ÿ?Þ|”ö§ô•ÿNï!|æ‹S«à;1˜ÏÕV3b#&sO €ˆïšüLh¸Ì”"Líi¡›„µÝ¹ez´¹®þ)tÿ —în±yf&6-ÍûLjÔQߘþ oÕç'h§:DkAѱ€(c=ày¹ïyZ˜ª…¨*ÚƒCÕÈ¢*ó©™g]ðký-LöbÊx4ÏžVr@ªÚÓ¸oÞå[+ wgbIóv¥‘Nµfýº=Ìû𪕣~~Z,Ÿ~xj.ßSkâmEyJ˜[„Dö«yÞ1ÓnšïÇ$å+â.ƒŠ5ÝVÜÈ !^‹´EÜjŒU‹ãB´žãÑàØ’mðQö¼5Þ–v4) àTTó»:òuÚ PíÁ4e[ߨۋb¸hÔ 1Û²s '‘/Îz¬A}QNÖ$Œw„Épn*¯%é±”ýà€GæQ‡ž.Öðx!®?áö¤®^Ò¢ÔÿàÙƒ²å:èí< $ܰԃöáð è«WÓ¶Ÿfbq#BlŽ˜ˆ{H[*¥%Û8waË:yh )¯š,¿Á÷ßî›Ú¨my‰Ùá¼Õpà½jÃPçøŽfPå&f»øZv‹©´|g‚´€l4èk£Z;žšˆ‹v‡ÉîbÛ€Ç3Ù–YŒw]:šº=…íÁ·Ò&O„?žGýÈ6ä€W8KýkàniÀÔ±/Ì Ö¬„›RþU“ð]˜ 惾Ëਭ¨ä~LjÒoX·Æü‘¼£II^ë.t’¨8+“±r)q `ÈÑeÆú„‰ÛÉÄ¡. Qý€C …h„ŸFW€Â)'‚t¼ÿž,èôå9¶ @Áš>[ûdf’×ûÜ‹=œÿI£{ù;2±4‹ ÌMôýa r_”Sìhù:& 2/Ïíbî®fŸ9½òv®hÕ•ÈpÑ¢jQ¨HöÄun7¥Ä!€ïЯVB–©ŸJ•3Éiq!¾*Þºøš÷½’÷>9<‰éŽZ×O 1 õ3cµïežêl¶iËþïh„j·‰‰mÏ/ xùÅn½^³&A_½@ !ñõuJ’q†¯œ ¿)FgFí}a“ä¯59$±WÝ^.[êæAÔ}Ù¹¸kÖÊ~’ÓÝGRã›·á§mô¥µO7nøUc¶”>²¤EVögIçHì¡vëåóÒy4Ú¹¥ó“ijõù8/À;´˜é£äå¦7eý ië|gLÆWÕåGL}¤†mÉ“øæz®æv4ÓRÃÍǵ:m]ýHà‘µkK ³ø–¶½‹TgŽÐœ(¯±ïÄ ™TkA¤ÆË7Ó£ëðó싸N;Y‡E‡ÖvKj:á5Ùr‹:8X»o4–8’ cO| ºôe¤ö£7éÐ{Ù{÷}Çèç!i‚ꬃsR¿:ãCżcOˆü[í¸*J©ëigwSþ´k xì÷gWÁºŒ•u¿üÇîÈ´2 3ù¾Íë²íIæ¹–AÝBtýýðÔS‘/‘¬¶m§Udˆ0`¾ÊÃ)ç^ÖS}í d|„jI€dÃ,þ̃/ÍZcn'úõ…ô÷|™Ó9³(±n\ýÌåÓN™VÙ‘¼<Orgm&¦PHØ—¦û”tXod/}u~Í¡­æ‡ Æiêóò”®Àf“ÐDÚâôLÛÖ¸ÐsDZŒ¢Oÿ>}:Ÿàg04?Œ³ø)3JÝhÛzKòáj;‹ŸžA¿|+²ö»P[‡%›çö»?æ9ç9¼ è:.ä{4ÏÚy3»Ã"/fÎA½æXì2ož­Çœ_Ñ»¹ÌVj·,ìï`+ñžD{k¿~ac‚ð¾Ôí°Ýº9ƒî”)S¼Þö~Í5×Ìsó:<õÔSæ½÷Þ3UUU¦ÝRŽ˜æúÿøãæñÇ·6HÛµs§©®oÓ®¿ ÄbÝo+ô– &XSÒøÕoA’Àº9ò¼ ßýoÚN te/ÔnV9Íð ’/cÛ×ýÕ××Ã6Ä­íä°v¤0;w¶©544˜Ý7ݱ^H_Ó\ìmË=Cp$µÆ'j©àê+~!s¾œK gƼeZV8+ŒJ;˜?7 ­†*¯.}üqN wÝ¢øÖlríÅnØy×ÛšêϤó¾è~çrà§Êw4 _§Þ{Q+Û"p¼ÃóCL~OgþÜJ@v?@µ:îÃò¾‚Ã:‡ïùJ¥¼¯ºàHéO*¡ý)剞m>¬®b’òš…EQ¢aWÊbiúò'a\@qÚ:É8,þ+¡íÚ™ÊC'¬—CÒÕAD·T¼è%ó*šqÉÏ(½Ÿ,øð+ÿ#è?É»%ô}‡†ØµE¶Gåe4i¨Mí ùï•PFš ì/GYJ»zÁÓÓ¤y¼7Z¯ÊÂMçR4ÖE`}›ô¥­"m€Ý_WCà»Õòé}Ú¤oŠ—¼×<k@«¡Îj6«Ÿ3ÒºÉ;Wm]J3aÏKͬòaÄ~8L÷8¹ŸDq®êü€h–ØL¼Å7`Âv~=Ì¢éÑÙÒz—É’úñó™múÚ¿,‹l¯»ý0Ö!} ¾eñê?æ²ÊÓÌìä;ؽÆBz"Zê„xn½+Ü“Ê' ¦òÚÆâü¦áçe[­Ñ¡F"LD˜×#”·¶óæSÌíð?…ô’¾Ømš,¯¿«¯`”¥“טHóQ×£=¦)ÿúhÏ¿‹žÖoYc’IGóm xŒÆh×V¶Ú¡—_©ŽRk0éÿñž“yRˆ©üdØ% 'qƆv<_ŒÆó;”Yûm¸i ñÒ–'/FUžnÞOUšy€²ãr¶T»yŽXßçûkr4×w¬ëÈ £ãûcÚ`#&šCã¨+òä[ãG‘¾z}Žû«7ÞÅ‚`jÑ~ðøÍâ-9`²ÞLNð&ïd,úÖ\A[œF[üTã¸æíÈ(ºhjºeëÓí-’ö2 ðêit¶U*Ý|z”Šûí°÷?ÛJÅK’וáÝÏgFïØë-Z&À^"I«µÜ—r˜H¯½Áœ{Ù€gLè¬oØ™2ùÚÎÎsÓãðwÆ i©ê€FWsWr’ÊÓ¤Þ"Ì›Üx[¿<CcÛ“Þ;\3£’쟶º 7´àS_Òo>+šðÓ”Qy2­x£øSñÖEàm«†š§Z¥ï=A(§GE·ý~$mR¥^:Ñž“£(xíW½T¦u¼» Ü´ÄŸšŸà‡Ø.v ­åÚÑÖÈ#ÈÇåÉøMÈ{mmlKúöéÄý?ÅwΠ±pi1³õpÊì^\³AA,i¡ª©/\h¨1Çh ôøómÌ/*i«ŸQnÍA·Ò/7ä»'ÓÞG!_Äø7"Ý…RÝÙ¥6ÉQôˆxÑDòða®wE„*ì,åÄßIG %‹Á¶Cöç-Lñ}Ì-N3G$Ÿ¥gúVDŒk‘MÚŠqþ˜o‰’uJ"åä%d™M+^ã¹$µøq š3ˆå‚ï9߯Ýw´™×zzhžÖ]z¹!¦Æœréj&~úé§fÞ<ÏÀÉ«4˜õ5å1ê¨Tš×]eÿÒ[Õ%2rôí`VM%uM%MJDs µ¡o¾a"á!¨ö›:âñun[ó…h‘Œ¿&aa¤­A! óÉ[slu {¿ìá¢b|°PÜ0?Ù mKY}ËÔjg&\"­;л1ë£ r'L_€î=!Ú*ú “ã±÷)Û¥®øy ‚™÷,'%çŠ+-Á¦Ð hû4“ÊHì=Êt¿€FI±è—®lÝÈ6R(m•‘kº!*œ;É—ÿ¢ž@Ò”Éi28‡%½ŒVÐD@Ürèj4¸´uþ‡°H Ç2t„kÕ˜¶Ð{ÁüuSþXö@„Ôrè &ØÞ4ƒq5©P/xöv²lczèQ„ð[½å"­(2$’09T:´ Ìí}¥­±& ätëZüÏ®hL†ßži·®„–ßyH(¾ ¸åuâå qðóµ\ÿÝ㔟4½MúK~ßû=³Oí¹ó~uÖƒÉÚäq5&û‹šŸÑ¢pCɦdÐ,‡o˾N½NÝi\ô= ~§zÒbÚ$Ùqý†Z9"Þ–‰@Ì–¶Â¿À„¾’÷ÄàZ˜ß}!ù>øÄmÔ…øÀ,ÌC¬¨ï³ðR H©¯Œc—èqI[×#)ù*eûÀüŠS"ƒEyæ]Þ.GfBHãÐjŠFÅXpîŸPæÕ¶‡4æpìxG"ª=‰º¡ýv"×*ö+}+7-Òݦho>©ÜÚëUøBDm·zÝ[=3’„‡uÛg¾ïOæ¡ù™}*Ï5çÑ–]^çÔjÌ\€–®»UU[O»ÓâZÕFïWl$¥K:’~ðAx& ²¤­±5= ³=Àÿ×Vs½¬­1ñÚ¾ö6\ƒtK3ý¼ÓT â{Ä«&3ÖÌCÛו˜pp¾±Å£æ&ý¯dÌ/íÄ7¿çáõ˜Œß[‘À|°¹¹Ïž«R^’e¶ÒÂŽ$gÿNx¦v5uG›‰˜q^šð}91].ê^q§ßÿÌßá›ò úwMzYsS‹gøª–fÀà•*®4‡Húbw¾­+¡€ø ëÈ]3ýw.mû{Àwí(p5HkXܺòqóÕ®`‡ÅÅ£—p‹ºÂûy1qh'!üUõu{ê÷ìb¬ÌÖÈ®¦‰„ϪQ©âÎØdÍ`üj4ÿ¼Ž8Ý3|î xú6´áA\õ©@φãùjeóc ä¹::ÝŠÌÌÄaæBÚPÎ^µc)÷Jä®×ëzóÞûÌÅÈ\ç«ÝÚe8Or‰öó8„ߪT6°e#ØÚ1@¡CÔ´Ó«½Ìø¨E=g¾Jí×@Ù¾ ‰µboê|ë§vÒŽ{‡m«ùÁ¥YXÉâd>ÍÄ)ªçÑd ¢dbœµæOmfHß5Gñ”mÀ²ÄwP\Ê®Ÿl$Ú_Õâ—u)çæC–.L¬Gþ^!ÚwNÔØ|÷fö^üPfÓšB‡Ðï7°m)7h·Vãhu¾õd¢FsûÐte†C¶¢=t.ýàzä‰HŠooÎky´”Ø.Df±Ò·h± ±ßáÌ_÷F«8Šô&É>¿Š UÄ=~*u €!ÙØLÿÒŽ¸9Ô³}æ–JÊ¥â$ÞeŒ4ñµÃÂ%èwÀo¶%\s´zÝøµs`åWR ¦îgœ8¾ q­0Ùñ±pfõ•,\ØÊÉ7“àÔç6n©dó\i1•HÃÈßWjkF´:º•ÒÎË#µ ×&¢<­-<'ÔÊøÀùÝ™¡œbZÀ’ã‚$"û„^Ò”ºýzºø9mN¶y½‹ŸÃ D÷#¨vh| ˜pBÕ[&ÄaìkC¼r`bnÚ`T·®å/—0É]»•êŸw,ð"íÔH²vž¿¦.‡ñϤtÿÈ a‡|¶Bk³õÖn/ ( ]ÌU¶? áMÅâ9þ€s7å–z°4™m[î¨)iæU=qw.Ð÷‹q(P¾6x­i=kl~Ç®o¯wÒÒßEøMw0«à/Ðn?Ûk]zpšú­íI¨ÍÊ1Òøyðl[p±ñÆ<%-(M~Yz=;ÉwÓÉ]µkàa@8`—$®RÍ€¿ WÊ#TûL~è×:H¦Tº†ü]SøXÊÄŽªì fÉò•éh—3$ÑÃ,×–´cû8Þég¢v6±¨²2}Jô,=Í1ýÀxVù€¹»â&3¨ê|2ã[ò"Êï)V:o¦ r•*H|[À~[O—ã´xŒÃ÷3kÃOeÓÒÑ*tû)W™¾x±Á â+ÇcA|eLÍ<ÅáBiÚªœ ¹)íaƒÔDΨ3-ëv"7³©ˆO¬ßÑÂÜêIæFѾeóx´¾!Ë­)ƒôîDÐØ·#ïvÍMÙDcëñÍÛdÓ·7 góΗé÷C¹¾ä÷Ë<ÝA{s\oþñQiÒ*(»ØCÃÛzŠ™õÓüS‹¢n¿ü2Ñݘ  Î”¶K2G0­Æ™…ø_Ãåðò Ü(¾«¶´OÌpSõ%Kƒi¹¼˜†Ä¿,ã©KwÏ}î¶žÛXØùŒ…ثѴÕݼ Ï…jê]Y¡n/óZìM£Cäv¥'>CÛz8¢D.v W^ŽâÔwÕ™8·Éó*ÝÑËg»¹HÌ8íBfQ<´¨ç>ÿ–Q²~mvØ(úøSÝ®ÄÎŒðH{YŽx.‡Tê@ÊÆ’b ÚÛ4êB¹Ãñjfd•ÆùÅËK®áÚî…Ä¡.ãGsíZ<> þlº™9ËÁ!ý4ãÝ:8UûœË}(¤¨ ÑCŠ)v”û«èKç³(W:Qæ €r´ô]’ƒÿeÆx™7Û 3,§ÔÓÏëϰA¦ãö‹c}OËŽºÌ{èàÞæ¦Gà9ê—ôh¾y€ö³iÿ¤sDöBièÍ2Æßá;ÿ¬ß‘lš?{Kaâ¡S§N櫯¾23føÅ"‰>¥jö)ƒˆ­Yúº™JSÙp6CÿÞ,´˜ÚLu_J†%pW•°Ì0LšDê72á/ ñ”þŠï¿ÿÞüß)'˜¯ßÿÀÈ„¨Žß'Ó¿5x ‘9… Ì0\vÙeæ¶Ûn³þÒèßµïáæÉçž3Ý»w7#_}ÅìuôQ6¾DñW^yŬp­[·6ýúõ3#GŽÌÆíÛ·¯½÷þ‘mß?²OnŒ Þœ$[rÏsðÓ¡l¯íds!L_l?ËNœ­ ûz4fݶñ"ÁHϤÆÍ—À š9ŸnÚÍvMx'Fù©j’m·Š¼Æ í,íÒB‡h"ÿÀ˜l£]‹P$Ù€Ô„Y‡djKs ³´GÃV‹Ò6wI‚غ”³´oFeÞçú¯:l&Œ¾–Ñ( ú ¸hÊá%2A±‰2e -œ«#¢ê€¸ u°ÿáïÊöNšå×FhylÇÛÐÓ¾.BóI§ek"{.QÎ’ŽM&ïOx)øƒmD½¨¼Ê‚?2!|Ü ¹žtÂx¼¯y6¶–ù-~(©®núÅwFk£Ød‰IEU?Â;ábíš p8cá 0Ù ðT@—>WÛ’=¼ M¢ 'eÏ¥þ0«˜.:$9Ó–ïhŒ·ƒÌÖ{—d>$ p :Îlèø. °ã=Hn]ÂJ˹\šQ5ÀÔÕœM´Œì¦!àeÛZ9n8çJ Úu·ÛÁC!wÔìÓ”ì]ÞMl};!Ïs_¨…DæOÃs’z„Aõ”¿jÀØp×7H‚ :gçùï€ægPÓÜ žµ%qáàY‰ÓùQÁl„o~‰oj¾Œ­HyìC}<š¥…Ú‹©K2‘kŸõÓÍž§n1?6ÜàqѸÖ}å˜ÿÔ5šÔf~‰öÆGýâyø²Ÿ+·Ã ǹð‰£h“dž°Û¼Ç}·œ¿çn å}@ô‡ÒJ†>ö‘€ôÄÅ89õíó³ªyI3+ä{e\fÒûd2`m@ßá,Øj»‡ƒéPZÙ ~‰;·…G&RÈ#uü©!l«)² ß‘˜B9²àÑô(Îz&/ <+Nõñ ²aAJ¿€÷'yA:à" û-ìò‰×›6è[@œåõäýø]bµu¦Ä­ò’?’ÿIZÂ*û~½I£ f­?jøS|GšÊæãÔ{æIkêª:<\¤«Ú½ÆÚUåþ|¿·ÝvÅ`9j0iµ¨¹6q >Ëäù†/æ…k&§ÙH ³óx²_Vx}¯þpøÛƒþ,t"ë}lE¾µa"›J;_2¯æ¦€J;u³¼vä$«™ˆ~JÒØÐÚ¿‘yà MXÌøS~ç¿™*«âe….#pŒm%믿¾1v,ÇÄ5†KХƥâÄî΀èR‡@'Öð›p=ÿ½þãK@útböVZ¼„¼©Mw(^2ûï¿¿ùé§ŸØæâô¬5ð\|óMfÕUW5wÝu—¹üòËÍI'dxà#-b»“&M27Þx£9öØcÍ!CLee¥7nœ5å2Mà]wÝÕ 0ÀÜ|n3à¨cÌ›o¾i‹Rq¿$n°¯ýÌû;å¹.¸Òß—ïÌÓ mÄëÜ­ðÁ¨âöLM2§Þ0:õ[Z/$I[û@ÏY BWœ–Ðà‚@Jg¿Ÿtó§¢ÿ°kù5_Ž\-Ÿ£4Ë"H€Øá슀Xš²iòEšÈ¯Š)…/)KisIZ©-pÜ0Ý™nC„?w§®ÖŽÐÈX÷Yà.<ª¹ˆ‰EpZ"`út€þ‘عÔ7 °ëjïœD®´†Ññ_ÚFV.iúº<åÕ/~l[ÎìÃä*HÒºN@ ‘оAï¼ç!‰%ó@K™Á¸ínÉ’Fa]‘î˜m³jÏã<šÃnø# sð‚ëuÕ{°:*D¤»=”é¼5õ°‚9 ž‡Zç9'äǧGÒgM|7ë5N(ð¤TÒÂŒ¶e‡.!qøži@Æ1a?è(@‘&:xJ&$V¼»gÑ:”ýËØ‰'\ù`ÿ:V»Ú:þH“þŽ~–÷8¸v´ÓžÈs/ÛAÛ=€Ž_ÜL¹oJ 6Ý 56 À©–c‰¼b™ ˆý¯Kéñ0U*=M9V™>üdCÕGh¥Ú…”úûÌ)ñ^¦Cåyfc´Ë¼ ¦/<+ΰéíð'-¸l˜@8/¶R޹ZÿŠ7ãÏóhiŸœýªÚy$ _ÜGòÍV3ZÀZR`Žè¿ÔcuœGv®5£Mi?ƒMۆͭŒWy‡ˆ)büDŠv‡¼$äðð\ýLÃ1”︤|áæñ,»—–¤=œ¼Ìç¯D{Ó&Eß%XŒå§qÞRÕU¼{?3…ï\‡1Ñ®éú•}ídM¡½ÈâHµxV e2ÿˆl*'Ç;a—Ò•Ú>Ý¿)YÝx4î¤Êü”øº{˜§–R›…‘Ðäªûé§–Ï0T]Œ7ã†VÊ>IÐBŽÎ Ø9e,y~RÊQ“èORnn³‘̪¹Û¢wµ„ˆêQ‘óçLËñ]c‰Ð**R¤»äæ.ÔkqÊO[Zš=èO ž¾cÓ‰oG¹žÉ­?ޏÁýô§­ì¦glnc— .ÉU…¨ž£«ÏÏð„YÕ†uX¥øG>åêÙŽ-à9h6Àž»¬-ïü¥¹Tæï ¿{5\ ÒýrÏž»ÿ‹¥Q¶¨7ËÐfaÝ^ÆÒý âW^’\ËŸa¤™„x‡LU©Î¶“ý*Î þåyÑï§OiÞ&JðÛ…©M#æWïà÷ç#Úibøãú¾¬ýø5íçÁ¾¾‡vðõ }.ÿ>üõK 0kâ÷-ºè¢f5Ö0¿üò‹Ñ¡i4i0t)¨9,p™¡þ_ú—ÀjaŒ?â{f sÍD±Îš@…C!…bEûÉF®šb$ÃàÅHmzVp2åpØa‡™‹.º(k£ç™e–êêj³ùæ›g_ѳgO3gÎ3uêTóÖ[oÙEÅ_ÜŒ3ÆL›6ÍŒ1Âl¸á†FÄ ³Ë.»Xpø¡7_7›õÞÛ\wÝu6-ÅÝ„!GŒÏ&oºBݪ»ædVe•\Šo¸õ†uïÏÎh]ºÏîµùmK©Ëia>DÙ®…€.ïX7¨Ýê³5Za$¿rIÛp›Ÿ‚jÿ¯x²©{#èÎá°ª( »å Ðp=¾[¦-¤$ñéOCÑ ÐVý!Øó<!;¬<µmÔÑBõÎÜz'rr’–­×ÄÅž€ÌÁiI?„ÀžÔ¿¶]jº¾;ùê´eóù9@‹›Ñȹ‡0%MËDÆ0Ç2] ’ìÈYsچ˩ÑÅè@4MÏ'ïî¶iAŽg1Z—ÞªÓŽÕn‡†nû_™‰ÔUÙä5!íÑž³ŠÞÖTœV4Tx€˜ØL"OÀà;ÚJ]߇IüG Ù`FÕ3‘H ó±ßôÐöC –£â}O“َɬÈÅsºxú»´y—"œ€Åù¢¶mê#lšÎ÷FËÞ T×öÅ?%Õ.r<0—·ÎÜ.•}”-Å»hkn¢¯ÖN'—ŸÍø¾L0/+=žHììæ‘¶ÛK‹'”–Lz‡$èÁC±œ NÇ0îœ0q8}T€Ù´áÞæs!&´õ!ã”Ð- `½Sìw4Ñh+ï5ÇÇ»˜µ+.¡oÝCš'Û0‘b+&Ÿ/ÝÍh@–?|)ˆèë§Ð'wË%eͯ|’{¹ÎÄü}òzSâpókò~so(ëFLdâ¡öhîµ+ ,ËeL=ؤ-ØÏ(…RƒM×¹]ÌZu‡éH7£ g°ÛOðy{òºæ•äP3¸î<@ÌæÓ8tbÊö¬l…ë`°­hnLlaÍF¹àxO$D"íJx„Æ­8´Ýß%ÁlªKípÇ0½]¿¶Ôé2äÛj=r aïÄÚæ úžÌƒà¡R·2)€†.ßaª)¹:?As\±¯8c|Ò$°”“mô¨Ý úE‘Î8­ó(êB¾:Ä©xvGœÅîÉÝ7ó}ÕŸÄGÝn£þäK@|xŸKáJ¬åËæ?hÇi«7ªJÔÝü;,­n¦W`¾Ëô ÇͿȭ§2‹Ï™Hö"Ó+×eä˜ ¨£‹1Ð5-yO’Œj*G:áñ—:ʾábxÀµxvÈ€_¶zŠ>P•¸7Ú5ô ?™eQ½’rø-ù(ß´ o›1„¢_fHmã ò¥Í膋ºÊ>¶LÁy Ò­z‚v°^&8®ƒLv;Oã*; !´ Ç4¾çHçèZ¿=]Æ«É,Þ:?˜;Ib_3~Ú|:mîpßxïMÂi÷ÓqÒ®ÌcXܥƪHé>Ùk-?N"²IÛê¾gëLøowŽ!ß/ØgÏZ|k<éK=Ôp3míCîöëôÇfdý9•ù>çØŒwR’(Wqe²…?xÃôÆ£Zš…¨£sW¤ŸìI<ƒäô6IÙɧͰäƒÓz­àc§ñÆK©—‚¾øÞðå)x‚v¬FÝl êcKfŸÂodW~/ø\ó<¬žïI^íKöRòë*ø<¤Ñb(>ˆù6Í-q}"¼îš–ð¿±óK€¶Û,ë\‘½[@7«­¶šÑ!S²õ»üòË/·¨;bÖ ä¥³Dayaðoö|Χ aR¬µ[DêFSpA¢Ñ e"ÖHàÄO´¾o¿ý¶5!!Iƒœhúôé¦];WGÄ\Â2çÌšeê~šifΜiµz‹ 8Ðüø£ƒ <Ø|ô‘#èµmÛÖ 4È‚ÅÒ.VÜ«ô?Í]wS$¦IèبA[öLŸ ´¹K‹"Ù £!™/”ýJMÒ4éëÏà^øð “»¥ PôkOÂ=™Ð-0) ¾{%ò3ádw ‰`˜ÒŸWGØ: ¡Ði'Ìœó¢ˆp­ÕñRH¢c)[Þtàƒ€.•l˜íG“–eŠ¡HFÒhÒº ó 4¯¾µbŸaëÛïL˼Sßü¥Ùê%å²—G[àÊkàö…©_9Ý7÷ º·fòxk¹½#y¾÷u¹{ ôñüåÜ Ü½B¾î'_K§âæn¶¦ª'„Àû[Â[’¨öobûj°¼îŒÍ0Cêz3YÎQ®<×"ü"ÀÔ\ŒÀ]¬íîJᨕC`(öXmî«?êIcäaxÅÊ,ÜxI‡8ηp®ñãù64œà z/ÖKâ´o¢½åÚŸsýüü{QN£gX€Ô†FP‡ÒT/F2 ± ü%jÑ£Xü&ù'Ž£àØ-§$‹ðC÷}ÒÊ: ¾ZÈ> ¶ù¯{’D|®zˆÿlÜ&{ÜCn94Ëw¸YH|§°QR `NDù¼\‚îZLTš‡dæ$M¾ÒÓøA0é(´ îâY_ ìœ_ K²´Æ*¤œ™z3"ÆÿäxÓ³órÖ÷'ÝjëùGv ëùA{3©]…Å#V*>â?;äÊf<®Ó´dz—±z7µ;™4õgKS7¶;¿6¤w¿“Nååäû žóüy6íyÜE™ÍÈ*pS wÜ;£‰øß43%>Þß'V27¡ý”²ÕÏ¥ž„›Þòz3iþö8}ë:g¯ûxÇ©4í«žŸ€÷Ö€vó÷ewÐ$znõ,¤®n^H½eƧjÙ¶ÜMãzÀãâ-¨é?»6­¶3ÝÊ!›3mŸz‰ô¶Â^ðpId?ÜâJÞF‡Á+Ón¼vìÈÆøgdï×R;€‚´Ã¤JœÆÒþäL‡ŽJ8 ð¦Ò¬j.Å|±¯X„hmö±à½ŸKÚwiq¯åM|ßf7›V'P­9™°/f]Ýõ`x–#•¯¸UŽNC¶Ó¡\y$m÷ª3Þ¯iزþŬšLdwA™eÒ.“l/@8EèÀKQ~XCZ€®YÊhèÒ»ÌõȤZü•)®æ!òJh1ÓÍ„Bï±fÒ…¦PÈQZüC©6gî³°2UäXOÚzê÷~È v™ƒK–üužuææsxÇš©6ô<Àêõv Ts¿°&jŰûÓS™ þD¨ž,ov©èiŽ/íØC?s¯Ã“çÄrüÔõþŒ¸3¶m’®´¦Ñ\w÷:^ü%ý^gbDQ'øÅ4ÿŽ Wнâjêa}ð O0å¼ ”z•6­òȧšª5ý×üòlrºùºòÕÍûRh5ŒqDãágœwß°(7 )ÍJ²ºîºëÚ4µû¬éÒ¥‹zéåÖ]þÒþ3Ä&¾" mAÐWî´Ûë&‚"Ì]И€]X/íÃDt´Òz"€ïZ<…N³uã25(¨ ㆳ׊›¹¬aÍ 4è«T@³ ¯}‹ïOGžæÛÝ6âó,ð°1vÏ´Ei ÃùH“<‘þ†™“Ðâˆ[ljK"WRÇQ´%¯qA_…è«ü—C?¸ Ó.ÉtÄ:uz¸—dL4¼þº?í2WƒËõS=|Ø×Òup¯éé¦sj¢y íb—tçÚ‘\’û“˜Ì»¤íÏçìÈT†%4ðNæPª§™kAA¤­ÈKÁã¼Ücƒ6ž2±ÑU# ÆOc)¾B¿r Ñð\‘Tz0Á|#/Ì2L½_S|HrF–dóMv¥™Ôâ鬳nZûžrÒÚ–I¤KÚ>ø:“–ËéÿÜz¸Ôp^&ÈŠnÐèkåäaoëŸàoÛÀØ!­ž£é–¤éÒf,·”i‹·sZÛ÷‹7­í‰§íþ½Ñý šÉ¸ä×LæÕ„*OJ B©¤ïŠŸš\'"óß”õWÛã¿Ùž›~L¾>èÛ™ 蕞\è¶ÿ#¸Î¸û¨v¶ª‡Tþ ÅŸdJCZìQÄA†×Ó‡ßVÞZœFl`Cjñ±á÷0  …ôµž‰íÿ6²}x4¼U$#S49b™ºá'¾ùFÚõxyýs!».>ñr‡«s#›±h½$ çtþüÀ ·AÛ8¨¥%Úw7Ò½V·Ľe´8ÓB¶wŒ¹$°À‚¾®CþUãL")1€<÷ô~‘²î/3Hm>#ŒÓøâè°¶ T±YfŸšÍðÓdº=g(ÜäЕûµø­Æ/Œ¾fÒ¶eôº9.I]@?±káŪAæ.ô¥é)n)^¿ ÁçÓGVÇ´DÌJuS/õ¦ºž D[s?a°èèÐW–©n>t<Ï|9!ôE²½üî3U^. ¸’\“#ä©:ú5;ò(_ŠõÊsŽrXiéIKV´åwfÕᦚZ<_•ݲAïÒÁ´ïgã)7ýíöx}ïzç®­n¡oóF@qPÝ ôÇN7?$.5GñîÍà¿Ú.>‚…›å抧k4'ɯeúÏ‘,Hl͸(z‚<ÊìƒK9Þ¥ú÷“²ò~“©Cn®S½9iÉ ‹µW]ß·oó$—mÑvìxŸUùMö„ +ÇV3×Uì ð[ås/üЉììݽæO‡<µxˆï?Øï|Òø’Ø2ë–›¬gàFcÔ (Uøë(HJ(رV| s¨f0Tè3r&e^ã[jêwÞÝj"-Fj×ߨDÌÜ<‘÷ñÝR¼½™ëäÙê&œæ+›±èë·žø×³H­×BtOà¶e@IDATa¼‡U é—‚ç¦&Dz7΃qÇÚÐ.;Ëk óûªO¤îñ»ýž4sÐi(©¿·„O´ÎCÖ㨅®yÛcŠåã‚lB™Z9/ »Ì2N²ü ¦ººÚn÷Qwp; ×ýßûðС^/,Ô|ËFæÀ¼ŠÑÊà°XØ’¿gxgZ”ZèŸÞ5tðw²Ñ’K;ÚŒr5Ù#ˆŒG AàÍò™¸šî¹ß!°X¾³gÏ6ëk!¡ílgàZºcGÓ¿es{î„å6vdÏW‡9~ûÙ#ó¢ýöÛÏ´oßÞ,½ôÒf=ö0“¿ûÖüú+Fò‰+ ø¯B{‚[LŽfâ¥MÚv:‰uv&ÀYÝ–†ìƒLÐpÌ?x›’±šÈ²GìÒÌ8‚nY-Ãÿ…ùdÝøbòÖ\¤i‡*Î9üoì„ÀÚrÑb˜~pîüa = œð!T¶+áÈ…9ÄíÏÄE´¨ýøSu'šp¬”õ¸ §ø;…­Ôƒ&ü´Kb/Í9ÈPÂ’JÄYKóø«Ã™ 0p‹zdŽTG#@ç£_™iéglïSŒåx÷ÞÒˆƒTîKfßnì¡o¾I<F-NÎ¥¸å} m]“ÛY\'ðS¶èÔu0RºÙʺiý…Üi’,at8×’SyWˆNÉk8?püŠj“|VžyéÜÏAU*ÙÊXŒ±á¦aŒ/ÞDê­ƒëRùØ>î“o@¶3‡“þOY_÷FRd·Ç­f·NGLeMûúËÍϱw쎀½¨Ui„ˆ_¿ ˆ¯<¬ o–ÝåÿªœÓï#¸„+eZ{<°ï|³€ÄÆÔß½ôé,øK~'´L¾±Í±•7§Ú.sÍÀäCZ²-ì§æâß‘ç©þ¤Ý§Ä©L(wŸÊ¾N/;FGb¬Vv,FÈPÌ ë×÷ÇÒÓ£$cãGç{DºtÅgùHß‚­§™‘mæ8Ú¬ ãè{Õ6øf€PkÐ^zæ´ñ”nâôöb˜7èÓZlЖõhƒ:ˆô6x˜vXõxèÀ(µ«ˆšÉM^<œ]šŽYÞÂVz Ó“^±[úi’~š¦MYZÊþ½‚ÉòþɳfªÞÔÞG•‰wdè¿hô2ýC ¨KŽõ€Ö‡(ÞjÂ&óÕ©ñ”cá±U}ÒÌW;r’û.ïõÞ1>¤C8/³`†|9,Vg.a4Gð;Ø%pïLêŸD|.5ß¼ŸzÒ<]³ aˆ _øéÀ=™(˜"ðiC¾mÉi€éÚ³áhϾC/µ‚GVZ~îuuî÷¡ï£=¬Ïâå ,”çFoX§N¬‹å×n]y ŠoèqpnÝñ/è1‡ž,¶=œwïæ€»TÃæµô­fäîÊ·‰$C=ƒ<5ó·éÊH“­ÞŸ¹í©&ÍN¹isFÄLÈÔ+TËkf7¾õvÊóFÚÏñ˜y<.mÜ ‰cçSoêWceÓéCÅý©¿GJHJÓyýº¾kÍÓÁ†óè“Òø ’l.שÍÕ} <3ªÖßOž¾Éáe ­û+é§UœCZQµìyÌ­xs>¤_y6ŸöY'mÕ?þÔÆŽ‡o,…Ô ôQ7‰õ±öüPò%sdúKk×\6Ïã±õÉCTÿ¤­¥†½ÎM¢ôkÙÄÐ3ý~9¾òò—äÄ>‘ƒà)ysˆŠëcÏHϘ-é/’ÝÎÁ.ø!¤áÒbܸ ü¶oÔ]K¾Þq½Ã¯ÚÙÐÀxi±pýP –BbeîMéwIÿ4ÚÙZ 'Œ9-."l×èðE|ÖàÛßeÇÀ(^ˆ´ .s%¢-YH:¯cÜ ÿK¦Á†ÌÿSÍ·DÑ(bzá ×d÷Ô}´•'›œL¹ ˆÇ ͛딛Ê? ¼|á!‰œ_Ø'§'ÂBn§#dç°8%¹i¼ÿ42ž½[À7ë®»ŽiÕª•ùßÿŠPeäcqM)¤ƒºþ ´lËÉϵ`–o#ÙO~øætñÚHnÎtÿ¬iI¬hZŒtb²H!Ý× ü4ĶVÀf¦ïížh Î:Û±nä£7Ê®®k&OžlK|}q êܹ³éÔÉ™ͳ.̳’I3þ|Óqñ%Œâ‰¤5ì’´‡çðЦM›l\ׯ9¯§f&Qiôüpª¡TOÄ­é³#K™a†.iEXÈ%û«Þ­þaq$l¸šØò_­±$Í“Ãÿh’àªÓsEšÖ›×=Õ×oõvIÑÏFhéI=~ð‘Gu»ãD­U^šõ’ÉÍ:fnÀäÝ:hfrp ‰ ZZ²wÂ’y9–«k¯1˜VØóYÊ0¹àÆ>ok“Ðç’{ðú‡Wi»â èjIdÁ¬\ûÍÅ4öDx÷[ÕÆ]’~šª+ºÙ(Òâ¾öÓÐH¹<6Í›Lø½‚ú#ÂýÊtx"ûˆ^’…¸„î­‹1N8øvÚÝ.G½ÍÈ$ò1&4þ›~Ü›¤ïþ®B“åÚ­ û©Ï:12h2ô0íÂK?²ú¾Qjº…Ǻ²À&¿>>ªVL~›5}µ? zÒ¯ÔñDÚÁÛhì›ßl…h6µ[—}界Y7{ä7{ð¡$É_‡Ro;°`rãšÓÚÜ´Y$«z˜1}o×!{ueã¬C©7± éÈÐÅ¢¬–V~W[ “v?…šOÅÖ |w'ù–%¿Bò’Î…h4 ¨ô¥•Žm‰\³”y9zZÏ&ïbËò[7ïµíq¹1c7û£õ?”Åf—ŽÄ”Ê#ðFI–Òâ_ÚEÉC%mMAfù%˜Ÿ¢ñ€¾T1”²É´sÈ>´ïMãi E¤øíüVüŠaÑKtεî#46XëÖ­Íê«;ƒycÓÆ %“.$Ò7Õ„ØÊ‚ìlæù£ôãDQÒ•P.{«µ!þŸÔÂeÁ7ºåQ0P<çwV¦•’ŒZ£ Žl‡, ±ýIùž:f[†ã,[½<-IÃr ~+1ÔiqA÷.I„YÐ! ï¾ûîæ©§ž2/ðûvÂD#›½:ìm‘E1ûîµ—µÛ=lØ0Óí·¹æÉÛï´¦!ºo¶‰µ ¼Å[˜[n¹Å|ýõ×fæ„ÏÍ+OŒ4›m¶™©Ä\Ä^™¸îû›óú@ ‹Ýä^ñÖxoó®ž~gµ½oÐü !çTÓÊ‚)g#{à—fëd!û÷Úî+¶?׌B)aÿÊa®Bãæ…ù«å}‚ìi_€ÜÑ}Kf†à`ý¾á^W«g NVÇ;°½KÄC/}²a7ïñ=®)›Ùn€º=¹›bŸÆ ô'][¢-U5Ϩr#åÔhõ¾?[³‰³<}ü„¬¼Á²÷Ú6¾Cj ¶çÊËŸ&ïsÐQç à#³Ò¾;‰mÃîaö%pÝI·²-?ÍÚÜìmÏ;líC[è!:‡ö+mrÙæØ1Í·Ç(§&âKÛHq&ÂlMÿ>ÑÏœm[Œ^ŠRof˜òm·ô¥¦r³ŠžŠÒá´É–¨¼‡rec1µª<áæŒ¬Ki7ä¤åÇm PxÓüµ)µ§ ¾e=j²‰_¥_ lY䀴Øs } °&!0'/ âs“v–ln{yzuê óEò_¸B2cÓ í§h_°ð%ûÉ#½Ðò_Æì•á•,÷/¹?;o[…éÿñ´ùÆm‹…"ÖÑæøN/=Ãànñ˲;k¿häw0ƒ+öà179߀…’Åøfwju%š¢g´¸0­hË]Aöôϳ8¤úCª¼Þ©¶~>ik–H½à$. h%ë"u_r‚[›•'/®>­4“#Í’é¯Ñ8¬äje°“BòÚVŒ#Ñn¾ŠçEàS²)ú–üNðŒ Ö‘?Z[8ù#ŸB‹×«Ð–ŽÆv³lUº´3õ<¼A;ý¾´ÛÞÆ+fMyMYm˺+Ì:¼û3ò%mÌB¤|\Â"Ld}hð*KÛIÏ£0I €ÆöýÖôÀ­ IÙ'ã¬tƒY“¼ª¿È.z?ÚïOöÛá餾'^§Vù<š×zd'Ä}þk3¤íþ.|Væ<$#¸Ô“2;-†4/3DÚæ qíø _Ô]ÀøYBt÷ÅZ:¾¼ê2³k"HV@íüðÊîy>ÐzewüO¾E›á—!Z<,ºÐV× º$õ°î!҉ƚù[ã7!$N, ˜?ÈÜ:GíJx˜€«ù ßMõ !ýc¤v›}ˆ_òclGúù@‚çÚ»໋E®•hWÒÒ”í{Õ»LYsN€Ý+×ÑÏCöVE½7Ek¬æëÅ3O0~ë3þôáUýÍ}|ƒìüÊ^ºäÃì‚¢LJ #Ì yÞþ÷ ïÓo¾°½É ô"n—;¼(±„G¤d»¸¨²Ë#н"‰R²df7¼Lˆ4hašë¢Pš¾/ûÙ9MýC}Ï¥cà˜ãØ%¹3 L2É8Œ~.37[ÐÎhÙæ`À\ï¾0¥·Ð>‘¶°}†¯ªß‡.xé%Z±fO6p_ÉuU†’Gðëé¸!fùºãò·ú{7|¦”³[þV„ï®YÚw1¶×…Ü"9cNæWZÌrBÉ|KaDÁI-^N¢ «aÜ¥îÝ»Û-áîsS¯nw($ÒKPh ‰ÍkJEâ*ý¹|ƒp¿)¥¿v özîì <_Ýw¶ùw§ Æïï´%m÷­@ia¤‚ßTDM¼ ¬¥³rìœdçÊjÇw4úñÅfßC6Z¸9÷ÜsmšÛöXÕœwÞyæž{î1km½•¹éá­ßª«2àA סC òîpÈA¦r‰ÅÍŤ#êÑ£‡kšøGöa%ð¸TL`ÑvÓ‚¼ 5 á¡)„$è rI}q´5é÷xèÒ^áU‰¶‰6aµÙ%Ù =!äïB²_h·¥7ói YÞv¶w´ÃM6pƒ$Ï¥LEÏ¢)Pá:f®ÍÃ_ۙɉ̪©V€îAÞëç¨DÁ³Z˜< ͺÕ=í+5ÀÿZ´¤Ë8«×æè¿žÉzÎ5pWqƒ²ŽÞS}¥Ú³á,4[žGœñOÚ5Ý[,ë#úÏtÏ4¦#îÞÐü½Úf9ûqÿeÞø`&•2/‰óˆP\”™ŠvÜ7±} '^y/þÔrú[_8}¿€/]F}é°ÇrI­ë„Ø1fZâ˜lT•áݘ…81[òêA}ì… 8žûz9D’Wöx…ÉYo¶?·¸C»™†6ÊŠh:ÛL%Pf( /Kê{C(~<ïvò§´'ó~§WðP£ ÌÇ!‘òzÒ×:k»oVGÚF‡™Õ]”ù>;·…5tÈI å‚5Åæ#ñXï¨×ø”ÏcB¸o_Á7 ŒÙ)DGÝ~U¢”× óY$ûz¿LfX,( säX •Y€õÑj<€“èOm8‰ƒÃž5÷*ŽVùm‰%Ê€:œôŸAbÎàygÅ-´½>‡üÛ-9²D¾¯\&ósya&Dz‚ù¶áùÌCæbÁÊ]iƒ_ñÎè0;{2æ:fÖˆw:¤ß¦ÝÇ}¥Y®æz®ž>ÅS(ÕŸÎ;n….ÂÏ’XøJ \ÚÏì_Ý`ËF}¾æjhKÛÚRœ÷‰?Vð>P‡à hû­™¼Þôc÷Àøôh³ ý^œ_ †—`Çùê¦3;€ÜCƒPÝHréHäi ˦¼ìþOÏxÈ.¨kç]àã+‰®fBÅ.f Ž[*†™E¡ö´µK¼…ù0×Ô‹—üÀéÓ)¶$r¥ûJô;òú¢ÝY±+‹SxŸKVc^êå®’„T>—­æ’SoQžã;›ºÄ^´eÅ¥ïÏ£¾°g¼yõŽé+?‚çý@<µÍ(»šîû½×éâuªÿÝ¿Ëô™½x5¤ßi|;ƒó~¢Ë8™~ ò¬…ÛÓѦ¥ØŠæ÷ø´vÿœÌVv«õ^§Õ¹>ô—Ú#i{÷X™ “Ù†u²ò hH<)9Øà»ê 5ˆ"ñ‚Íø‰´€ò¨§ž|î˜IÓûÅ ¯CË"íb©8*%_²‚Ÿ'™_Šÿ†’ÚýÆC’7¥8á•9/sµ÷Ù!ÑŽ…£þ”ˇð ñεàÍ?ówè߆Vï¶!ßð=i^†ò‚vUس{b{Ò‘ïÄ5·pèÉù­¦k<ÎušÇyCÉþ¸½á¸É|‰]€÷)p«¹£xÜe¡Ð å- ù%:C qÎz)m¿Š±:qB™é«Ì-3NéÁ»Ðn†Ñ®÷Œù¹EX êϹQÚb»í¶³O/½ô’ÇÕ¹=äCòÜ¢ÔÁ5øÁŠþô$Û¦>ú¨ihh°Û×µ…ý_ú·þØ€ÉdzO"Ö7?ÙÕ7o^ÑlÉ´JïÞ½­Ù…ššsÕ™g¡‡ã=ç¹hö=êÒô ’pûᇬ_/h솓`QX\7Œ®4-[†1+‰°þr ªÂ»ôÀ³zh­ëÀ5Œoéôúq øM!iUö’þ(Ú˜÷¼ È·[f«)ç{˜ðœMx»%žH3XyîÐS Qïý³¸ ðž…)ï zsäM¶ÜCX+fÿVïRh;óR ‚ý˜¨íŸnaîFƒæW„ä ÀU{ÚÛVZBÍMIpMÒöì¡AV»“Íh‘Ú䎉ÃÌ%ÉxpøIZMÚÎ(]wBàfçZÀ i­¸“¡vxhº*Js:ò…,\©“Ÿ¶Õ B“e7´rêÆeJ澌 1[–A¡??…î8Í,nuü?É‹º6u³|åp3¢þÄ"ñó¢Zq©|¯©¼6f’á% d×Á öò,Y€'¾=“‹½A™L¯KÁ޳nšjc«dôáy¢ ù©éÿЄ Þ“˵?-û§=Ò>õlv ƒ@f×,ÿâ!’FcäÓ–Ï ¼ÌTXq“¹ ®ZÇvmiç PÝŒñçEƳÇíÍél_¾6½ñ»0QêE^'|oÉïÿ¾ Ù€ß iüè+m¸1ÙG¦Ž‘`ÑÍéÿÀC^7Ç$?1_ÆW0¯Öžeö‹uæìWÍ™˜5¸‘<>ÅøäÊä3¾yš}_ØÍ&ÔµìC 8–Æë.´ÍÁ€)ßKC1§­NÚѹƚ šAþ¯¦Í©¼zÿ°4àÁ&ýD •èGA[S·*ždèôh  9_ïÄ€Õ-¾·”z¯üÎ:*_7ðÝW  8•²sûŽy›ÌÂñóä­Ù(~(|æh„Ø]ÓgÑ‹ÒŽÝøÊ;(çéŒd_%tYʖƺfŸgÀgo%oòî[áÒÊïE_عDø½ˆ£E”¨:[Ò¾º#ŠRûÄŽZX¿št½ .âÌj-Þþi£–ñG@öà9#*.%Ö¢æóÔùöÛ†“ê#ðôgáÁ:hQç(ÜÀn-¢$vƒ˜ø–ôÃASîr$>Ñ•w…j8ç‚•}·mì,vsô¦|ƒ¦™ÂÓB¢zl)aƒñù¿†Œù`Š3 šTò$¤ñí)«kxãRÁ·/Àçx_ò‚,Ýp/Y`«ÝŒL~w~DùIk±f"c„ò ‘0Ùf' *.‰–º ÚóG]½vIÿ¨<ü“Þ+“úýIšÄbš žÚð épæ3B]9ÔkÙå}¶ve+6UFbÒôuÛ0/ ìíÈaoa ¯Â)ž×§ƒ½JµÍíLwrÓK7 ÷*A# Rº^Ùk îú#À4•^™~†ÕZ%è•“¬5™ÛÕ6"ô• ÖÐ÷&ÄáÛºVÎË¢£¿Ý…'hMÔÂ@ß~LÜÚý}Ël(i:¸kì­(‡ è«÷ÝÉ{ƒ$\¯¢±$“ Ρfµæn츞Ä6Ö¬à‰½jØ«™”.Nd™ëhÀ¢ƒtø¦Ýò’ÚÂÀô•ßäo\lyì J]Ø´ªï…Úªõ,‚¾_¬UÕ0ZQèžœ`j²":–ãoõøYÇÌmgξrô€¾­¾D(;Ì<{¯ÚØ?“¾uLÝÅ¥0¬éDžTB¸|ÐWqÇS7#’Ï•_¡óIZXWQ.ê÷§„ØÐ–æ}7…žŒ~Mè«_u5/p´Öâî:&'jSAMl«i5™ßméH»/H˜ ]/­âª!~/ «Q ¯BV^ÄŸåýqšñ©TÐW»ÆÐ? kΘ&Q¼Ê¦ô;€7”­´”m‹QÕØ^­Œ `÷m¸Ð7—¿™˜ö c”—Úòp; z”ñý-(óŸªn¥gúµìÂǰ%9×jmžË„ôóIJæç6â»8¯«Hø>Î=U_Úuf Ð!ùºu_’t¯$îÙæež?ʆõß WÇÁec}i·£»&¹:¨VE4ÈÖµ4ߥµhÁ$šª»Á—™Ä{lIªŸ´¤OzéjÀŸ#¢@_™/ÑÄ’má'ãëÑgNà©@¤•Ù¤â|ÞóaàúÐtÀmü^¼í0Æ¡GA/£,Dã0u`€ÚMú%òzׯ¬»÷Ï<ïCæ~ÊSÚ´^Òx¹u&Û¿"™ˆz‹…h|†4fæÄ÷µš²Ãÿn¶îuš!uû™Wkv2Ð0îÃa ²iû)ù’l¥1+§EüµúÆO¦Yœ®W„RKò&«½jÝZ|U-}E>´àÍqôÅL‡i!@×™9þ@;þ™E Ñ=ŒM‡!G™Øš„9—NÖ=øgxlE»©/uIí~4Ø+,6Ä{Zyâ ×V²Â­Ð}Q«~æÈøÚz´TCŽ¿¤ìfÇÖ73”· }ÌH_€så@€¶™½ïsï’Ú½ô ^2¨5€³©»„§™v˾x¾¥†G(kÚ‡$ƒ NéÀ¯?möFÊÓÝŽ®ó"v„lˆ2ÆimÚmó\Ã(qå¹gÀÙ¾êêõ˜€{Ä#šè¦þìO9gÒ‹Ÿq«ŸïcÁ«°öÔqRÛHÏ}!^Û硾ø$|. ôU@ÞÔ¬ ¯Ò\4¦6äírÍщ|ß¹¤O‘¯Oi.mŽÌq¦t\Y%à'¸ » Ll-¿3OŠ#3.Ý㘾UWaú`;—Ô±/ `;Ó¶Dçð^¾³¹—íÞŸ2ãË äå`äœ9¸ŸLø»1!~ä’ä7¯Fðåð€A1-¶}bƒhqã ÒsMuÊQ#©=#ónÐóGZÍΒ‰¤˜ ]u.½L%k›†+ùõg¡&ineñòú­ý¡p5Õï ñÍωðö«tµKS³ÝŽ–/ŸGŸSíûiøú͘mRlNz’nƒò@©@nOòºN6T(o’®c÷Èkcm¡¸ù~‹Àg;ãìÌòý‹¸Äá=ñS‹*Ñ[;ÎŽ%ðÜ#”L6ÿÿd ¯¾@¦iо 'Ž3@7AêÖ­›uš2eJÐˬ¹&ƒpR‡ñ2Ïšaÿl^&L0Òdü/\¼v4XxëºxŒ\‰¼ú-l’ýÛ$ƒOcó­üJ«CLsAS«p®ci ² h­¯Ï QyXsóMMe Öî›n4Ô½+ìAƒ´êx)ÊLf"$^ ô–p%jIИ/(Ø3Æj1‡f¢€ãÇœçä[ª£‡›º’Ike¿9 ‚’àq ‚Ôõ茼Òª$´ì‹0 äóÿgï<¥(’>Þ»ûÁœT1g0œb8sΘsÀœÏœ³ž‚¨˜s@1³‚áDA’=•øÞî~¿ÏönÏìÌî¾ê}ì›™ÎÓS]]U]] ÆŽë¹ÌL$ì'7b¼ZR>Ù }Ô†'(;ö ºƒ/ÃyŠó@æÞÿ{T?°Ý~j¤¿ƒw)†J¨¾¸wÞ]c*!ónËÌL7·¤&˜Ý`¤¾„+±ËÓ®S`ž/By–„¯z…¿‘®–¡=bŒç"mOpA}¢-cñ›©­c~ãù_<‹îáäðéÔ7ùøìŽ ðv )^ÛL{¯xý!¼üI‘5C7ô§3@%h'ƒ]ÛÜ€Eòݼ{©w£½Úû팎e¿DY'ê ²4Ùo`ð–/ÃwåÕwOùÓü‚fÇ}^BIG~ú2¥÷V-£?îPêĨќøbÅ'O²½¨–@‹þ÷ pnOÜ÷ÔÝZ­12†ûÁézÆíð˜1:’cK…îdM=×1,IÝÑŸ8¼Ä-Ò¯u;ƒw÷ñ¾/‘œŠƒô&Ä‹“ Æf„Q%š”Þﯹù͸œ^˜ð.¡|›Jåc¤ÌP޶å?ôÊjÉ-xS {ÕK©L]÷€È8¦ØŸÕ lq É ¯ØqÇËÐ÷Ò»Œ/–«^Ö8n]¾éÀ>)ÿßç›?ÅøœÄ}<Ð÷Yá=´9Âï§)çõüÌ4Üã A +WbjÁ;\_á§ùRõê'½Õ|"P˜›OAQ;ð i‹ƒ#¡[KAçŸ*ä–´=‚1-^F Ì{ޏñÞX×ՆŞO¨g4áþ—’<9€Å«®©:ó"ñtÀß,ìÈ'¯_V¯·™ÊalÓé¯`QôuúÚÁËfyÚÄ[0î£ ÿ¿Û2Pz¥¶ŽaA-íðqCcY»XDß$S´&44¡0ñá2$i.|A^áTËáKÆüãS‰.„kq;4m¨è¸•ƒ]inø ZÞÒѶhëµ±ŠÂ ¦Ï¼ÿ£='Y1Vk§˜hG,]Zm×ø_‘a}[­àýY Ò¥ÃòšÓn}M J³4éik¶ìIÉ:¾@Ô›‚“k¬ÁæÊ6mZÔ¼QÔ/Д>Õ›Xä)7ˆ±Ñe:Òö‰¤ˆÃˆ?+ÙÞF{AÞëM&y­*û ´3 $x_T`ŒWãì–o}¯c“x @RE á·Ô¸­:!{‚W#­/ Yµµª*pxƒ|ÂÞ‡€´3JZù!kŠ/µhùÃal‰@c]+@§‘ëÉ;¯ñ ?§ïÎMEdMv¢ ò8o»ûÛô]ÁÞULñ±jº;7­{¸9@´[æwÉÈqsyÏj¡Ûª§¢®A]ÉŠí-pc~ÏêC Àjh›×LnÿSq{梤˜ÀO–fæÜ5Ÿ¡ÜFm<ʹ‘V˳ü@‚ÁïõÒ®7Ø¢ü ¹ï’Ó°~s ¥»¶Âý Ë‘ùé÷ð„©†¡ øÉô éÛü‚™çUpïUOaS­œË¿¥쌟Áƒ8müs$¯¬cñqøxÿß¼2PiöàW9a86½7 Ù6ж=ÃáÑ')‰ì!X(8 pBâüv¶{Â,6ãB³ ªƒ’ÍBî&—Õœ›Ù×lÎlÒÑÙ ÿÝOÆ}ÂÜôÁ óÕ”‘fzús³{N±rùr å<‹Êašy¬þT3µþRÚMž¹5áï˜g^ï7Óô#µø¬ÆüÕ–ïùE±û–»eÖ1§˜ÁæÅÜ“Âx|Óé›rëñ)|[3SÊÔ»˜…Èm•8òß»8¨ƒÿ²šïƒ…ò5X†½@ÚÉqþ*SÌ™µøÎ H®R°¢½: M†òÝ/çwø+ú)wsr `¢íùZ—µðÞ_îLì ,SÛžNÛ¡žzb-”t:”Ø@ÿVBÉ9tênZ5²è–¡Pp„S3æÎ/¢Ãá–Mý _íIJÈ]M½ß™þõ;šþ¹{°’žh6„wø ëÏ™‡{âïõ•Â8<œ÷jÔ)¢Ó&vÒMèÏGgrÇÒ7ZL_¨Ÿ•µÒµ¾ü*ú5¹ß ïSØM¶*Šû>(¼Á!T€©ÜÓf¡ÜÜE<,åž›[OSø&AüR\ÔZf]Y7満¦óQ-ý›¦¼„ô¡àÉß@¹ƒˆ‹éóò­²šÅë©—±k•âåÅŠÎe$|ÍÎf‰¶¶¹oø2ý¾}Ã7¹sYãÆ/Ï0§Ë †ñi¹ù‘µ²Ü»Ì‹ýévð/qàà„ já€Â«¬/mîOÑ ßT¹^ÍÓPj’M-÷v>È‚µpr‚¼:Cs*Ìû’oeLEùÈ,§OÌÃRÚü¡Ñ®I;ÏDC;«Ÿ}ÍTNügIYíçzšï3^µS}ßü–i±S é>HÏ%?öË Ö¤ÛbabÏÿKübj¸Ÿ4Íçj¨ ÉI¤ûKÁ®ˆ2¸à˜«æ›žÐñ·¯`ÝiÄ$, õdºäT¦h’•÷ƒÑX¹åšWÕc­£¢¯ó+“åš”QX€­®D·ÛFÓIé›þÀø£+¥`“Õ¯ü©6´,gí>üÂCÓKòKh½{1Kï{ʃÖ(Y§ŠJ*6®$ ¶FÉž2Ä/´ã·èÑœ·ø–LêÕ9lÏ–ð©寔¾]ºt1òñ[«ÕzS¾N’Žn+õßñÛþûqµÜw¤|)À”Zr43Mæf¯Ëb3ÅJîT„'(hk¤,w{X¢ô=”Y‚Ñr“YvÌKl¡ÕÛÂxÏØ–Tp,ßMg Ø“ˈ–À»³tT.AÓÆðc«Cønü¨ÙM•"v κVï.?`þá Õ ×”Öhþð6Dq5[ßžÏ>cDy÷}Ú†”ûVé[±¬IJ_mÙc)¿Š·Q_=J7v~`+%”pïAgåÚäûû¿Ó­Ã}ЩÌImóÓU»ï@‚ŸøIñþ(ÂéRR+ÌåÕÊsñµnéWzùO\ ÚR\wu7ëÚpˆÍ¶DjiÓP <ÙÓÖ’K´HB=”¿2Úk‡aØ„ ks" ÀÊÈ+žâÅ+¦#÷“ Ï»Ek†Q¡,Û¤ä˜ M=GJ3üQÊbqÜlÿëÑw‚.àŸ 4Jlâ°œs>L¥œÔÏBݾ Û‰“@æÃ„ñû³‚`©.0F7Vxhg»ûÍ ÷˜7P®U‚§’¢óâ^l9”¥Ú£…Œ'B3Ã3T¸D- ¼À|æüÞoÍu¢yñ{EªÊ-²cmf"w)VPgãÓ{ˆJçDù¯êöD‘Í/€¶Ïá+¤mø‚Ì ›JK™¥v³”% ñK£ÔëËœ§¦ñ®¾÷K¥ûšîPl¥Vî®)µM”>ˆ6½ÏíÅ<—3/¨φ™ÔrÄß‚ŒÊ÷c©}c ³Ùªî³ÊÌæûTWòt4õ(Eïe!¦;|ö¡ÙéXà2íÕ]ä|dÝôÙ0,p_5·Ï|˜ÝêÃsˆÍ™§~5{§ß5çæ0ýPä\èb¯fmm®§4éè…Íí¯æ? û°Ð'˜ÇÑž6»¶X¯ÕÐ|;šï蔾ËP×Ý™…Ø ž1÷ã“ø1JwŠ[Í?Ç{ð+BªucT œ„‹«9%»è§|ðkqׇUà¡TÏÞ]®½eòƒÁɦ 3Ô=ôë!,PHýœùvRá Ö ©s­ý2+Ýw¡ W‚Ó±$\6×7?ÛBÂûŸÈÅÏ-‰Ú_E?ÕŠÝ3:„Ñme—Õ®`o,h×Ï£¬‡ÖätȢ⎡íóP§þ4•_Ôn²·@»÷ûx*œ’¬‘·¡[ÝxÖ¾&Óövæ2æ ü;A¾â_)oQ^æweš»­ÛüèYj}ê{¨¼ˆüêþöå [;„>ËòþùøQ£ÚŽ÷k͹>µôs‚ûE»s?<îI#”;?0š6ΗÈZÑ;ã?Á"<\ e‘çкÃÌ íö7{ÏxÞ<­]—N:dR¢ÂñÚ  ¼ ô9{É¿+Ëò&ã! MéE?¯T³ô‹'ÚAà´‹,†ý&ÓÛ<\·¾=c7’J\¹qYˆ¼NnSþ1ЉƀUrÅAw¯¡¿?æ½.*…yw'…oexá$ÜŒ¹3ŽªÆæwoÀÊU—p$nÁ­i…UHÍ<9€þxž4Ã*¤ ¢ä³¿¥re0ÔÜ{!ºYµ†õÿb|NäÛö©ÞÚÌ ðþÝ ð-1c¡zá!&Né«ä“í¯vBÝžô¶…鉻0øJàpÌïÿäkîÚÔ#Cemššsö¤ÿb5+Ú—¦ù]øê£=¦fö¼Ñ£MZZ!”•FkçX~ÑRòvîÜÙtïÞÝ*|uXš¯ôÕ³QK‚îÔò¹Ç.FÓ-È»Í$0*tFÓùÏškçàW´GvÓ‰ÍI0íZblãSêÿà?•¬?wFÐ9Íúóc‚ûè‹ÇY]t H—Bò‡…ïh¯V㫵)š¯ìyFP¬ÕÎm3Ê^ÛCúhŠëkK[–ª Ô…ÌC{ƒàné—%jR€ës-6n„ ¸œ%Nb‰R$ÜŽ ¸YYŠÛPHÌŸ›—ÃÈô•EúÕ(–G @ʇáLOzß\cO,x+…üéäÝ3ÝÞ̃²íï(æî ¯Ä¹àŽüÿõÃò÷8¾]‹q–élEyh2èMJ­8á>ª—Ÿ¶Ï¶ï·7À2͉¥r—ño˜U§x;„~²§°Ú!…åÂô‡””qÐÀZ„Œ[øÎ]B\9³3ìÈü×Ê;Ù*×fÁ@[¥¯.(´Ä 5 þ>ö ˼ü¦˜•é«ëØN¿ ¾ÀÌÇÍÄÌbf…”Xº)8$ ;¹Ò–Þ®à˜W-¤èÑlëä¶9Ö÷3 Šuð.ë“wPŽèp9÷ ƒL-ük­qµ°Bö6(vgÏÃÈpyž11ž2çÒ§SÑG³ 2¸aÆWe€Sžq98wCådÄÆ$³ JFù5…ý_J%¸›C 6®~½rP¢.¤t•_Q{8» œ1Ùç Q¨R«˜·Qr¾ Ö†Áfæ? Z4Kr0мfðv','w3ó&â<¥o8sOÔŸYoxUéáV3Ò_ŒæüÅó0íð‹=ÎÁªXã®`=pà½kŠ2©éÜü#¡Ôòµ9 é³!?ØÊ reóCús2%ἓ#ô•^ "¹îØùNsJ~|Åw<«ni³0>´µÈñ]jwóeývf©‡ØíöŒT #Šá€~/j%ݬå(ä'Êãá¼ÛnÙŠßL9d©Bz óqÝ™æõ—»e´fqÚ<™E´)ï©x Úrþ4 <žQ\± ¨èRº+j¶)5— ½rè"ÌiïV Œ%–ÏÅã¨õÇ<}(¡cÂéó¯P¿YâP–ä÷ULé`HöÒ˜p-ÃO4Úÿ&ó—«ÑÖÁ„GaA”qwÒwˆâø²¼ûеӈïj¹æhè\ ÍôZåšÀa»&ÿ°Ùhê(ܽü <wâ>îÃ]ÂÖð¬{ãÃ[¼^M ~#³.}÷ÉÁ¥@2_ †­!q!‰Ü‚‹[š#°†,„,–›”Má¾Ay»cM;Jöcá©7¸ö".C®˜ùy¨¶ÕIÛ<½Ó›ßtFÈ–Ð/í‚úÙ5©ñfºç÷Ôìk5Ù6TpîºÐcmPÌÆ!´õëÚ’7;øžéÉ<÷u «ZJK•¾U+øÿ@†Uµ#…ŽÈAçqßÑi•žQ¢^‘={‚ÀˆA/ØÕ&/Êì»ï¾þãÿä}*•2ú9%›®.̽°ž[T¢¶ù]`¾Z»ü?Ryòµ;±Àäþ‘Úõ{·EL¨úƹxˆ¶ÇWèê^?Y¨ ZÛ?µX)Y)üK&2Äê0JÛ‘Ã2¾µŒdÖ2ÓÊûs$ïJLòÃ#ì)Ykt$å³È_ÿþýËò8ºÕqÐàŠ'¶2Í•˜€H) äËIJH1Wßq¯Snÿ'!}0Hóï?è«Õã_7‹Ð9§écWËã›)?Q²ŠÂ<$ „^âca ÆRg Ì¢,©n± ᙌÁ:sr~a¬=×4gáaõ„nùÞEN?Ú#ë°rLÌVá‡P°ìgåÕ{ÛöE” š_ÇÛèîàûƒXá®l· ÇæHL†2¢ ŒcŸbÄw,Âi-ý"Kö×À{ ë)qÏà}óÞwÇÌÑfíÔ"æôÆ3lšèŸÔ'_t¾K‹hšYó¼Å~9kŠ®¥T;®> ¥~%Nn…`ò`„‹Ò£ÅH7eVgT'# éàDZ¸ºåñýàã5„K¨s rÿáGJÿÂÞ1à¡ïçUé7¥ qV4ÊSm…ÄŸBðWeQM`,™<Š´2XšÎÞàê©e1­° Å}.2ƒž}š°'½4r ñ-ÃWxJJ?æ7¨B¢pT"3ï%; Áù±<´ÕC_Ÿ î|ÏŽ•6ö-—LsÞdh[?3Ϥ¦›.ø¦¾’m¯G¢ÜEðîÐæ³Ìè„G¶qE$^…{§@??Ö>‰™š‘9‘íø¦•å”/V~vº³š,KapéZt´ÿ7ÆS°ü>ƒ«8œ°à%%‡@ŠùãÉNÍD¯³¶ ýó"  ÃZ]=¹D\ee+¿Ô›Q__&:ÅΣ‘z½ä‰·ôªÜÅ™z€¿wóT;ˆö?ŽSpÊÜWù^ï§¶¤¬µé×óaû§©.àÐá¤@Šb«Èu WífØ…oP¤iÌSË£\ú±á€â"¡pù æ½ q×z´¯¼HQN©_à7)bºþvÚýò\«,JªW94ò¬ëË,nSërHë|&›Ì.z0ÕƒT»Ø+˜þíï5oL?‚…â·¬[$í¼ÐážçÃ;èPY}?Yæú° |r{8ŠO2‡™oåǺ -•EÝ–ôÑÅÌt§|KèA[^BÉþýri1¾ŒËÔ5¾é`Ú/­­âî&½°c Z™Úù´4‹J¯¦,àa\òÝÁgò†!.´¯©ÝPºž ÒoG¡_×^pæÂ€OòS;ðºW²…sÊSVÖ&éCLæ±½…£ ø˜Þ|y’Çðû—Ò¤æGKM‚¥e½œû†>=¯þfÈÒfçÌj";Î쑽)T²~ý>îøZíhè ?09”Â{Ð{[ŸÉÁ\'7h«’¿?9UéÌ/žÐέ­1÷Ù‘E5QÌZa]êÙ…¹äJøê …L—ûØe 8èchÐj(vïïÿ¯NÞE» ¥O´cuÊ“ì©q žhuÊÒÁnA¥ MÄŒJÿŠû“õ3ceXIdÔÿªÒ£Â«;åšiRð:ÅšË2+”¾®l"5+@Û«fR°šf\=R<;ÿ®qñÕÂ'¿&øjð3íIòaV-oKãÛS€XúèŠ& 1VÓùEAƒE“‰ïgÉO#±@>yå©) þ–{?—XXaÚ"Ÿ¹ò÷ëƒðSqsÑ«”•ò· v¸ë§OºW?¸)=.ÄYØÆƒzDo-¬ D¢‰¥¯žõ>Q¥¯Â›«ôUÞ¤¾WœwWYÑþÙO¹×iØžYˆÃ_íÚ›¯4 ¡-nK”òjª½%NO„«¦Bs§iöæügµ¾Ò·3¯1Å|ÆáSçæF•)¸ôŽy,Vº1ºÕ³ìýçgÞ›ÚÉL¨ ¤+üÜÆ)Yÿ‹åbÔãIW¾¬ 4ž†§¶ç0£ùñYø.&¾·‡Ï(и§ˆ? r7‹ƒ{ ÖÒ)ì­(S ¡ÿh £s 檈p*ë±2Èœ‹ÐÌ/R»Ò1¢ô=’ØñˆˆÆ<†à» Ö›ÍRúªmU.•¥P¬€áe®[@\ŽPŠ"ˆ–DÙâ¨o6YîúJ_-ùÜÉvÎ.žßâb…›=Q2û jñïåo/÷ã[ç¾ BÛˆ8J v…`q'‚rô¶šëD)`òRåŒJÈÂÛµûˆz·"eBW½Íó £J_E„{I°¼Y¼¾¯é<}=”J.É×É(!t€àº@ò¹)pJ_ÝûJ_=_ƒ2Âùžg)G€ûGc]󊩚 `ýŸvy‚¿ˆŠ EX¶Ú– x}ÆÃi…TÌ!ö$z=ª¥]­)8«Ü•™PŠglÄ}q`І‹™ ™ÍH®vÏFÅ/nä·Wú=X˜º8Л—¢=Üî·ÕvõJ(’„—ß’l&jÁV¥ó°Uþ‡¢Òm ,VÛª.hžòýS ›ÃÛ 6Ýfî…»‚ñìzá ÜLüÕÎg¶OÏaVÌ 64¼ÌA\»Hw£Þóð å \=£ =/ÏÕÄÜ7Œ½6±™ä»¼ä[6œd+æ-2Ê·ÝN/¥aö-¾ýuàêQ(;ׂÉÙßfÒÜ{ï¿õªþ ³cýæ¦Sn¢¹aú6Äø|8Œ29 Ø’ù>Öµ[ãovÒG2Wn`‚þ•Fþ8ó錫1nQä+¦“^ü„ü ÄG!°Ô>Š}–áÇ:ŒÂy¡Þ–¾§7ÿ [ :vŽƒ¾ ý£#2 ÒBñs(ËöÂâxW,ë'æ²Pgqñ-„ìyU »+ÃX L»5ö替7ýFŒ²:¹pp¯îlúíQÊ~59™bøvf†Ô_WN•¢4+IÈù÷Ÿöc¼ÀÒí¦ùûÌràñƒ¥ ÂôW|W«ÂÜÌ[wñíY¼ñ\qˆ‡Æ]–lNÎÐßݘÔ/æÁliQwõncñèPöD«ù?ÖâÈ!”í.1ݧÏ|3Ð.€ýüû‚GO³ƒÁެô.X;£±&)5 2gÌ`$8,Ç&? eœeÞðA‡0~ÆšÎü{Á îÈÅ‚ô7f0ýÙ%áƒRÊ_½ñD:´î÷†¶4à÷oE¥^ðig¥tÅýÙ{@²Û¹q/ѵkWüÜÈeÑ«®ºjYØÿr€S¤éê~z_wÿg¹N¡ýÓ½wHj÷Où\‹Þm&ùj¨gF i’ÚØÒpµofLý¼¸Éð‹k¶G׸ú>•wŸ‹™Gñ é¢yg'Ú.¯®?WøRöê=–Ïg`ëƒï7W„ó5*¥rÔ}ƒD&_Íìß»²5‰eù·u~ÇOJt)uZÓÕ…«+éúñÇ—E9º5räHÄü”ù®¾ÌÒÛ¤‹C˲ó>äñ‚ÄBJ§pÛ ;}(ŒʃÐiÖ^â˜[¾+_PI0ˆ»ã”„I¼ð¡‰rtrÃå^hõÛ1°~+´)®Y3½Š2÷æ$¦½îR²!’¥–À`.³E›>æÒ¹xÖÖ­SÎÔH\Ù#ï•㟯Ø*KC@ÉEóRýÕ|ïGìan:øPNã•Ï0÷M¥<{‹CØRÿ—ÖúmŠy.ÿ¥U¼½]P¬|Î7Ã<‰ën(Ý>DE}@j.³%Ûä6aÛëèBß ß%M;°ÿÕˆp%?jeZÜ.†aË œ[Á&h©°õ˜^)ú:PŸšp8w•§Âbz{Ê~…Ì0–O‡ä?Dhýµ8"tˆÞ‡éŽàá”Äo¢uwAõ¼Çº «ï%ŸN tòs5;®ïᔯ²–ú±Z¦jñõwá;ò+”X?£|µBøj¼ÍæºåÌ››Zà[¥ý'|§èb_µâ‹ñôƒÉR ßðvÏ››S?™ÿæ_CdtB#bo›g ˜‘<¬|Ùº| ñ³^¿e²©áó„t;ó!ý•´=ú]I«ÃF¾ä]^‡ÛQzÚön^ønÝÀÓSÓóšËÀý»*Òœù)@êŒj€Ò·Ýüó$ŒÁ÷ŠÙç¤/5Q+¤ú…û7‚ûôáæØºšÆÜ³ài2í¬X¼)…½c©+Û{aa4N2½iÃK‘íIÕáïf± ܤ7 Ï{åyZ-Xœ#c®Âå‡^‡ðÍ¥8Ôxé…BHÛÔ}—ÞጿJsM)=³Ž¥53,þܡŃ)çÝÂ÷”—e@ßžA)÷ ã,àaÄäì¢f¹ïÑ>ÔÉX×™ãX KՈ̙ôÛ¦¤¬§¬FÁöi$¼ò£0V|K XFÙÃØ-’6{‚ûox´Y'ËëDù8XˆÀSiv9e¹Á”3$H*²VYþ¥}í×!_/1? b,éÛ|L¿õ£/ÎL-oæO­À7úƦ½˜ñ½ãð]zlýƒ}ô´¥î(pí1óåéôòZF`иÒßhÇpã=ê_Ž–Äм˜aåˆÍ‘¦¾öÏR'-Ê¿iƒ´€Ð»Qö¨~m«†«4àö ¼½w±#mžÚÿ¨×rø G9É;:ÓáêÃ!ÐÂl›{i‹hìPÛ§¿ÈÍLš±éñ\rq,86º8–Ţ͟5Kä^Åoµ£%_{s§èj©¿—·ø1ûüT¡Ñp1Á<ˆr÷ºÜ›æ0˜ÛUhÛ¾ÉwôÊfX;.‘Êç;ñ'Â)Íc›Sï[Ði¹íði³Pæ"¾Ö¹n­~€ºŠ˜š—®{‚/‰÷Ë'ÂÂü}”ºåcøúT‡Îξm±®öŒAö®Ž=ä: Þ±âÆV1a•›ÌÌ1‡!X a0ž’sˆ*©ÞHû±åÝýÊ‚ÖÏ6Maä§~SoÆÃ«ŒõA”œ4F‡îàl¯È+Æ—Rk(|Uncà20O¦¶€Vô!ì}ženGÐtÞUüå™ìÈ’ OÊ«™?´ûàL¸Íÿq¸±ÆóR¤ýØŸCR½(sw³_Ãif×Ô¤­3GÀ;Ÿ ½ˆù¡ØZøÎ~HÉÃù•ƒè džÖ}Í›üº½Bé³6o¸-tõhnÌìì¥þýoµ £ó3>ƒfi±kö€Æê*çÍ´ûDt+éAšÝÀ«CÁ=üAz î2†ø*|Ò·ZÜ ¸ÅɲBÅlÊ9½ƒ¹æÒÖ<Ðágãj$ jœü³DS•j†ô¡~h=³Ò²·Ò{Ìʸ–lµ¯µ]sÔšðwL§6Î ®Äù”­Ô~ •@8+A]o-Yk®%K(ÍœnÔŠøûás«bñ3Ü®ÜòKðv²rM¼òK蓽Š•`бLDbÄÔîf+ab*™'ÖKнN&´6¶àI¶Eë”òËaœ&Êz X­¼Õv}Û8öf^ÂfÒµZÓŠŽ»HÛ R–?q`ÏšºQ•û<šW‡¨ù°_n?w[a_SGEËSo‹ö·²éŒWb‹’BS>Hg |Œ€°¯-z ïxVXí¾e#Û¡¼Õɽ+c}8Œg)ûÅdJ.Iý.¿½/À0ï„ ¼³P¬+îDzà„ÉWŠ7ÃKÖHk£„Ù¹V—:%<^A¡¾WöŒ•Â9Ô{ÜU,ø\qò;»¥³h¡ uà4yÁ¢°|sÞáÍÞ{³6Ïšï§ïBØ×JR—ÀǘX Êc µ‚³Ææê¤óŠxUK¡˜¯QÊÄñï"š9¬NGµTaÓdO¯!éHhH{ þc|6ÜBÞQeùåÇ×úgõbVäÛ,ȼ¤ß×€op³5a‹€sòÑÚ‹çk#Ê:/{èV8ä@‡>š›j¤Ü9× wñöš>æÿïÅqŠ+{øýðÚ„&‰¿Ê°(¾§ƒÇ¿™ûCB?ÂcN¤fœˆ`Â8™;?¯]HT -†ô¶ :X“ÂÞÿ.~É%Eš:l£Ý©Lbú¶-í ùߥ—GƒE«ÖˆÐJ¹2 g%˺ ÞÀwÜ<€r^þsý’˜Zo¾?Ÿ¡·ùózg~n[¬¶®h-±øÖ°î[ ‘$#¼Áw^”ßNXožÃ ÿ)ñZP »¹!dA›(MMfcÚR¾h—Ü…]ÁÏÚ*‹ÞG¨³³ÆÐ2MÀ"íÖU80,(×WÓ}@Ÿó+€x‚èá•¢3²Uý4ý³IfEÓoæ«„¬hK­aæÁçûíšÇgÜjzç.”â]r¯óþáûˆ+©k°Yûí³Œ1ñ®ùýgæ2vaèmC=¼ ýå =} >î1xŽyÁ͋ׯ€¦ÀqÌŸSXh¼Ý©ÜR[±({Fæ{âOߨŏg½¾¬¡¡# О±¥ªð mÒÝì°]€Ð]á4ï€Þl e©ÛÒoPêê‹cú|Q–ÌN”µ2u µ~øßK ÷gz™'MèïGàÒ$s‹'eÖ6ƒð:åòt*ËD+Tm9ïE:ñ>œ~~Ê·99bÕè§©~ï«j4ëB,çÎé|òÇñ§á”5>¥Dã;ÓÇ%g¨»ŠxøììùAZü]úÌðÞ& _–¹f¥w¨æ:3íXòk,%@Šï*U½ô@ qéq‹}zñî¶7O‡Ü`z˜›sÃyúÜEÅ\yF…úVc#J¨;‘÷|†6¼fù!ÍÑšÛã¿¥ ·faF r’Kæ¦XEoÀ98d‡Kþͬ×îF³êo=à?ÜSͳ“0ÉÚ±è=Ýú¿e°,iŸÃê}R¤q×ÁGÝ Ï)%ñ¨|½÷Ž.!”ERý^`qq^Úð¢y¯°fnRˆ¿×‚ÏðèÇ“÷{µ1¦^Í ÏòÆGèBLÒ˜ Q>Í.Mbê¯n2oû.R›šýÁžSS_CC¿/ʹ¶d;TÍä ™Z®ã´¨º,¼[µµI->¼Õ¡„Mò7Tá:£E µî:©PÔ7*§Ñ_áû4¡åéZÒúJßZÒ+Mû„„qÂYBR;i')}UþÜfD[åˆäµ¦ßXß:ÓÕñg¿¶d«ýŸíÝ…# Rq UFœ’ÔOÛŽ‡jŠ^?½î5Ñ–Ä¡hlù³˜TKz¸ìª]ŽÊJYIY áÍé5 —àX Ó«Â,\+$Œºž€e¯ªôUq:ù[J_Œ¦‰A¾¸¿R|“çÃõšÈ J_ù]ëZ¥¶íºO È«ƒà°÷TºÞFÖ£QkÞJ_•;?åk»_e0í=×y*'LŒ•P­ÊËÄRZ±(Eô— Ð"D=XvßÈWúªXy)}w¡ WŸ½Þ¼Ïo"V¯ò?8ž°Ãên庸9œè–Êp˜Ö|V‘ ­Ô×å~…©ÌÙ²£J_mÍÖxk ¬攡ʧ‘»ãµÚ——åÓ—´[V-¯pU½vdÍQ?-ËÌ9¦t>J¿‘ô‹é¯ôUFµÅá—\uÈ·± Ñã\\¥«ú¹¹J_mQ/ í»Q¢'©+^÷Õ®’.–hoV«B+éKOWÂà—¹1±>& ”¾î èž6ƒ Ü %‰O›Ö‡2n cü&弯ÂÓ`®s¡ÀîjËâz(uo2g¡È8Ê–#÷%ò±X û4ùûÅ(ñŸfÑËâü}7œ\©˜H\á$|§¶‰ÄÍdž÷Ìù…°ˆKø u7ñÇDòqR9‹6o'(fÊW Ðöê„E— ëj´a{¤U+)Ÿ¥¯¦Ÿk擬åo±óUåƒ/š£j†Tð¤Gåär±‘ûŒ4yÔô³–¬Í)÷R›ÙùS»Æ:“f2cq¹s&Jí" )ZÒ 7ùû,†V}¾>‘展{)¶u(xT¤Ì²Xævoî»+ikž¨Ûž…ÁÇá¹ Q©Ì;<á~ÂÌ˰ØZL¾A9–»‹wоŽªõ)}þ“×6ÿf,ûp)ïu ‚qÒ[òˆjhÚÒÔ{K1êT“:øIî8Ž*ôŸÿÍ\éQÜbúÒMÞKÄè ãö)/GŠz²/;+âÉæ’ : …Õ…õ×^˜O„f4¿än§ON²‹Qfo,}5ï3¸KT~ßv*Š+“9H©ö€,õ4gow„¾¢Âˆ…óóóàS¹£©Ã{bzks"ʳóÒ›Òÿ´éµc¢ä{·T„–‘”ðÎhªæd)¢FCå—µ4s©¼^².ž\^wUâ‹ä«T6|‘î×ôê"xíç]ëûó-‚ùEe̲\€”$ÐH­bs2¤“3eƒGoâYúŸ². p…^¦ã~.û,~hW&¢7í’ú»×:™;pi0¾ëøü7SŸšå3'¦~1—¡<;ƒ]€Ú>OÜ•àÕ#¡l{b Ý!³‹¹¶ñBÂ=\ ¥*=Ÿjç1¡=õàhÃiä]*Ä»“"V>ýï¤Ô.¼ó*ÐhÇûML/ÁaÅYÓ>;žܰœù^îàŒ†É„h+÷gÐ`AÒÿ`ïp»ªóNŽ…_Ð|¶9óèéÜ»wR¸/J¹î‘Åùq(ñÖg^SOÈŠmsL¨–dáù#¬£o&ßÐðƒð«=wv^ÓÀ<¬íÿRüê ëcWÊÇÌ |‚Ľ‹ ¦öæJë£9o€ù©Ð®¤‹0Bs€Þ) RÈy¡òÑ^Ï7Ÿ¹á³KÒZÔƒo$ãšÒü-‰e»wÓlêVèô¡ä…WÉ>H~Íð€)pyŽ{@É¿óü!¿H雯ÃG.¤pÕÌZ ˆd«øØ :¾#‹‹+@'×aŽæˆy37ô¢8ûwðiŒyVF"FiÁY |èNÚWø.r!x›>X´X0w¹|–›3µt&)“xmöX„‡¾C‡D‹éô¤›sJCw«‚[Ñ·7ZâMÄSË 64‡§6DÉy ;+ô W ígžíH9’Gók]Ø:¹(®ŠâLlÝššRÚjôßæôßdнéc ‡ÞÖ¼¸Ò…ô¢>ø%}¬E®Üs$ø:6T†óij;¬4Z€håvÐ¥[ÀÃ8Þ>š¾–g(a«•UK}ö4éYõ —ˆá°ÖxʶF!Í(Cäç/(ïmço bÉb;iÝGÓ©‹«²d1h½]Û±è@¶j |üQ“RE¸U* ÌswÞ¯(Ü!4$ô,èåq­^…IÜÝ7¥<—Vc±Zh[R- vdZÓÁk{µ²Ô·K×~^êò¿¿zÞï#WÏ!(ümšÚîEÁ±R¡m ª¦ôUYÃI7Ì~uWrÓ®²¬‘ÒiÖâ@þ‡Ø¢[[é«J^âYÏØ>»æ[J_ùDÔvt´ëä’š­·¤R̳Б3°‚Gîd1ó_üû¾ 3냘ÏØžù>[…JðMñV}û<‡[F»~Fþ" ‹Û²5+a1±=À3ßBßú¬bn©G±{s ºN=6 Wð<:Y|Z¦xg2'2ØPTD Ž’µVXé«èoaâúsAÝ—Ø~Qß4d¡u: YîmÄû•)}%eNމâ#ûa´2Š·m®&¦¶è %gÙ+Å¿«ûJ_Y7¥÷£Í*(š’lÔÒ. áDÛÝ=fÜ ˜ú ½Ð¤[ #Õáúü-,ØUûá6"Ööºÿ5( { }v½9€Em“¶ß_¿77ˆ":<&×|[ 7‡·\è)ÐöOÆÔ"º´µN³ˆ¯ôU¸ ½`Âeõ¿7³ð¿QªNdžRT€ôêô}ÜÑx+‡ö³÷îÇÊJ_¥TûÁ“&ÀX„U‡ïMÈV–ô,#%ì±)ž>¿¼,~Öt¥è%j*^Êúý¹£šŠ¨šè¾¹¾}(âÛ^Ä­Ú˜Ìo)ÑÐÈ<§mÞžÒW¹uÏÉ,¾v@ ¾ŸC§¶c\ æJu5»´9ª)‘-ÑÑÏÙ¹à[¿ÛùP*^íÐ2üw9[ášíãˆb²(ºä ¡àäàHè¶ã/‹IŠ7+¡$z‹§ ÏÖÊf³0—°ƒê—Ìöæ.E´ÓŽWÉ]›kÍ¢™ƒÍyÌ{²UgãÂâQ~À‹>˜tÅAùÿÖ°Ÿy/÷`Hé«ÚŽk•¾Rè[å˜Ë÷½»)»ÊТЫ.¨gæf¼†V„Ü(lö%Iܼm›±s@Xé«ò>Äßü<Ù MŸT•ò •‹¯«æBåºBÞ$˜åhì+1pàcà&xá¡à øÿÀ@±qüÂJßc¡›:˜W­Eà-Y$ß>¢'åÈÊZ£C<Ëí……6}ƒ=¥/ÑÖCîÄ&Ðo/Òϧs pda~{ï \oFñv‡ô½—9ucêKsH×Wôç;ÄBÛiùmé]ì½”kó3ϧE«N‡ÎÊbYÒîï_çrÞ*¼i!+Xx>ô <¥¯"ñ©oŸá&NÒ/ÏÝZ!²>=W½éßù‹…Jâã›aYoRkCMª$ê>ž-…ÅÞ ÷òÌ•£á‚¯mòŸ)S‹‡sعû£p˜}ª c2V ë_ÅŽ&)>û1޵@q-ßT<ësà“¾ïx®[öXËm›Ü…ùõðÏ“g{)³sׂ½?š} Åë ?° ¿J ÷<Ûc1¨ôUæôŠôy·äbtHif-âç,¦åÀ8ë­_ƒ7â}iovb½ÏlíØ `—Ñ…ûæ_äN+ ˜£–ÎiÑD³õYs,8`çÔŸâkÖœíÇø”…ÐÑ\éçJêÂÜ´å.“˜ê7b÷<‰I#D+µk¢5x8WIk–åÊü_¾ ûc)VK-~ÿ—;ͽ›HÙ÷ð×µÙ= I ŽŽÕÕ”‹2¹ÉbašŽKR “B7ÉeH1QäFJ}±îÙH¸—VÊïqLȵ²E QªÖaµ‹&ÇùxGÕ¥>¢¦–wõš`E—GïS­-­‰Ï²^2YV:¤àåõh-ý(#Ü–DFåþà~ü§îý&ý-ñWþ1}Pÿ Ÿ+²]í†ÂìKáà°Ð½E©$m¿»‚÷èÐ=ù_ìÓº }ðÏ2·iŒ$Ô¦­J*„ð* k¯¨BeÙ‘òEy³[ö;¬C£Yb¨î¯Qé$v)ôË µDy ðl@YTMéƒÈ¢§FåŸÆþ"|ÉÀÇg^{}» UEû¤zíÎ×Ù¢,øøþ‚Q_~èü£ªÿÿýˆ*o5ÆÁúËmÍvµŽ¥Ï:§XTÅ}>5ÏVÂ{m^L :˜òXÑ®à¯ýP"\ƒ¢#êS¼ÆìÍHÆXI ÜLÞêßBJ„·"c´•6=Kæ"ò0³—xyQfzö//̘£øÞ_cí÷¬åBQŇgÀÝ+P0½R/fòndY(÷Nƒ‹ùÅ}8ÎÁKØÄÛá(3Wd‘h^p»;}}ôsí\`»5Tøò9_5ÛOéÅÜþ9[FSæ,&ïAA*åD2£ˆÞÇtžº*­† sSW²„çI)j% ¶£€û{§/2csÿ¿®. Í©;íBèÇ L¯Øðí¡ û ˜Ú :¬¹¤äž ‚à½É3Þìˆ{ƒP vÁ_eCú’/Oòˆav·çX„®G¿íG÷2%–-º©0¬ŒMfwÚr< ü…±Ó2ø“/‡4*ø¥ÆÃ}xcX¼óáMXt-/;>Düæ{àÈôÏÕ(Gû‡øÔUB¥ä³s@ÿbB¹]Ò6xõ]ÒœëËR²/æø‡bAÞÍ‚¨Ô"gî ü\Ž5§Ó?¯ÂYk>Öüþ2ï#7CP Ç)$Ï ±ýé’vÍCy¥ü àŒ…Æ[8›à­ðüœ¹€w‡ólw-–ÞÎ\ÁŽùºŽ—÷°X(¡íJ‘ÜS ¿èò´öµCúx–P¦š·s·–½,ªÙƒèó Ìd×Ñçan Ê}Ãï°èb²×QŽGS¼R¥`oC}kº™ûS“ì®Ç£‹¸¾8±cmj-¾ˆ–5GµeßP”ç5¡Êmg¾;ß—ï®}X‹qóO/0_ äêêp^çSWÀ==Ÿ‹¢|CÆ€ã•*`)R|¯(E­@ÛBÙËyØ-éÏÑ´W´¬’Ì¥/ú7Ò>7ÿ¥P(×@ß\Cß‹ ¡jä¢ïc·wa‘橸rò•gN£¿¤îGÊ¢Lj#ÚvlÈ9Ä.¯Âš³À­MÑ|<»a*œ8»+ý«¾ÙÔµ­ R0é×Tðýô65ïï•>½ŠmŽï[ULî_Pêê"Z)­»“/[1(µ@œÒWÌ—RË*ÐÔ*ñ,tÞ%ªpÕ!ajF‰ÄR–Êö³É¢Rd]V¬ÄŠ“¯jP! %ÖÌÏ]ù^[³¥h”Ú”¬,üÐJ÷_(3s2ÂIO›pRª[™om5·‚<ûæ°ëcM¶3Œ°³ØqøÕžb¦o®)¨³ ì”mKÜŸ8;)/DÚ‡“ŽÏÒ*{ܘ•¥¯”¾VUú*ü{~z·Š0}]¢ô’¸·(u¶*¼àY~Œ…ëžu`¡ a‹i”ÓÖ'Eu¥¯,+.§LÁÂôäfv)Ã>¢Xj0ݱ*WúÊzè;Ú¿+Ûñà´èvi—X¥¯J借$¥o@תT­…‰¥¯0ׇÐhùqìËØ¿ª`ÅõO–÷zY…zacktp̦Ÿ“ûº«ÀÿÝ#lˤ¬ÁGP,ÉúÔùÞ‹ß«ÞÇfèÇŒ`N±Ã.€«ÒëëOŠ`§Ïºc…¦– ÷ªá½¶ ëзÖPúR‘?ìaŒ…ÙÐô ¾‡³•ê>Ÿþ S·J©«Å-O©*ƒfC¬eì%á„Ör§<¿v@TRúª{QÞ|™[Ã…W~’/ÊÁ¡üÍá6ÂuH°ï ÷Á‚Qóî , a¡ÔR ºËÍÀÌ¡f©½9œh¼9E QÎpEÖÁZ ݵHã˜fÜb¦’Òwq,n‡0¾6-6B4|(J¸µl1¶i77ó.c³wCgÚE” p›¶›vOAƒF }‚±(ßïnnT,ÅèÒ [¯ ¯¯CgÕW–‚à"Âd_„]\²—Ä×lƒÙ€q,W]‡âS1ÙáÏ…6ÄK%²?>weŸQªß»ëžú§ùtŽGYÀ\ûQÇ5â¦:wwÖ)ùÙàé?±)4©^^îæÝª¥²(\…w³j®êÔ²ôŠ žâ D¿&͹ÂË¡»ò.ø_˜§³ÁÅ?2.”^ÄìXÏ»c *R/¢ Ü‰W;–ngÞ¼…¾êÉåÅ[1sáFg¼DÚý#ú¾¢××¢ãŠwp"‹¸çñ¬Á[\ˆ’ùXðe7e¬ì“½å×é.ªJ¬±ò*}ÅçjqXïñ÷—ÃÏÈň/BZ¦ô-ñF~¹•îõÞråâ`o+û¦ëí½ s×™ƒLŸú±z;^/jrñ1ø~>Y p ó+ÓËA?^2á<áÙQ†”´A»–Æ¥‚ÎÛý6™>o‘Pb\0=ßöò¬YÖ…:µ³ª£AÊå€A“Þœ§N6DÒäÈ/ï²{­Oý1&‡RÙo ]Bz¸÷Ï}y§¨ <™2¢½"7avGš­¥Ö?µÌïå<¬¹RúêPÓÇRDǾh¬ÒW‰eüÀ™)(¾ãòÇ…©ÞíQú¾Ã3ÙôšGÒ{ÇeõÂÀZ¹’Á@,0®ä;?©ŽØ<~ 3bv$Ögø³ñ¾9ØllÞ_Uµ¨âµQ-(R &ýš ¾%`Só&¥/M%I)f_¸aµiI¬jj)ÞjKYKi®4Røé—ZãLšrä b‹ÙðAcô ·6~‚Â}\XL²HجøÖþF½b½þÃÛ"â”A­Êí²Œç*õ¡ŸGSó´Åư|“Õj£mÓÁ0®_e‘­{_°³˜k 8W ~Nè;åsÛB—g5Þ?TCJ󯱦p° ÖþVQ§¤Sü0×Ú'ˋ柺ü’ë®¶:­Äkk¨²*húX†©©:°cžH|´7Åü¾ß" #b4eµ=¤«‹÷}æ%Ï»X&,¼Ê wA(²Á½m£ßEøì®Äö ÂN\|\˜;¸æR™cŽ7 -A hÉ 2‡eï,Z’IðD¿‘+{ Ö»Ï:)ãtP†òh;ü[JC= å‘¢YÖƒ³ÒX4øÛ©ä8¬(lÅA9*äDŸÉ¡±+a,,þÊ‚¶èzÂÆU'B«LÁºuGšUÒ›Ø{ý‘ §\w¿Ëú,&åæsÆÃE6àu{§ÐôS”Ý×Ý\Ã]–+6@B®@¾‰Ï)[ÌYŽ>]3Hð·#ác± qôEÉVd,¬É¸–Š[µ  sù’)ð» £¼ñ2ðÿ‘b*ÿF½ýOÂ/mÔÔ(bޝ»Ñ¬.…±YÉ&×¹f%›ƒË°æ‘2(nAIJ¯æêÊnëcŒ#YÀ‡g‚ø’¥<Ù$.Ä爆",5`eSô°?ëNÿË…c›øT.¸»SÕL: êhT¥”5‡ë4´u÷$¦i7MߺÝÌðú;­¿D$Ù;;ÙŒ%å(²÷3´Y?&pÀ{ò~:`×ñeÏd¯4'7Þâq,Þœš¿ž²Â类¦Qà¶æ.õ7æàݧícɉøÁî¼Oæ_”¶ßù'x 8ldtôríU›¾#8Ý«åo} ܬÁm4^Í":sKzãbî¼Ã1\îü Þž ½T?FAî=ö#n[Þu±Bd²F“ÉP«‡$¶²$­ð8Ø/÷F1ÍO雓P·Ù‰u 3]®G©¯qÀâîÆ^¥Lž ñÒë@ÓÿA@y–R5hôí¥ ÿÎÎç'ò­Ú„{FðôÁõýšç¯¤õ@m= Y +‡ÿW‰/Wþºe!ݘ¤4‘ÿž 2eÕ¦O¯Î`Vks\¹ÕòÔ/‘´ }'ë¤hÛk-£µÒ‰lKõ×®]»f¹Øô¶Ç””ŸRUÉWnS‹¯\ 41/QªƒÞ “ÙH¯=•rir+¹3eü@-Å"±bq£Cn¤@—R}/K÷ñ0Aø’ÄøË]†NéN%|?¹zx…“¬Ö£ (•èUYÔ08²¤ÒöüÁ\7AК ¦zC.â:+àiσ6ä“®¿íåR-WÂàãúEÂK)‚»PÒj+à=…ttÝá.­q¹©•)“4Î×°”o"ìHÈsp+LYïâ–:\µå¼eµËu#eSP*ÌCà/üÎA°zK€¶´©tˆ±J¨û®–ŪNÚ,†µåîÃZç~,ç€ýŠgÎô¦6ò-E/÷‡¹Ön›!|=IÿްO,vp’òÏ2˜š²Åh^éÃA%¢<Ñ·• …¡ôÍžŒY_¿ ެ†²¡¥ /ЯEµ¶¯AÄä~ÝÒê¬7¹±†™ER9ë(—ùž5Pru:„BþÈ< ŽÜ ®‘+ß`ÏJà’Æ©Ô)Õ(‚”µ«‚c¢+±”ö@ºÑú}”¡@–QßѦð˜”jš]¼úDÂJûxÜœ­Éöp—;)¬-²ÕÿÈõÄAŒ¡¢KŠÔÖÈP§ò}{$Ö¥ñª-©@“t€àŸä÷pûr|pú³[³ýZ4ÑüïRÙšå/Á÷r»-Z«Ü•Ì fxªÒÅc±EJÉ¿/cp›4ûŒØEàÃÌoA{ßz›vo›cföáÐÀAUÇݺôÓ‚ü i”P™#ã—QtÓñé|ðw"cNÖКSA¯È/Êœ2ÌhŒŽ†–—(?µˆ”ðžþ{uÂWñné¥ÌMgYºÔ‹rÆóŽïÑêâÁp~†Vº×¢öVð#ò«X Ô’$nhcìò–L-dúç¿$•øW”R€üenœZ×Ü”ú; Jªf‚\è|‰Ë‘ÇO­Isœž¸¡Å¤îFŠÝëQüž½H»ü†h½h¨§½×RÚÝØ úxh¿äxWvôª3'öŸçEɼ)x èʘ ¶þ‹Soþ¶§˜Ëµð[.çx‘9igúâõb¨»a¤Â—'mç'Ykxо<€”MË®žÒÍ@ý´ó]pàòRpÙ8ž9~¡+í¦ÞÆcËR̪(_dÖîBÈ7ü~D;³ÞBY.Ùia²HÌ ?ëF¹ø—7Pö ?§47"+]ƒ¢{{ÆÙI,нëáöúЈÅÀ½ÇÁ…06T¢¶Ê&ý‘…ú(rŒÊœà ÊöX°¾È§ñºOÆF+pQ~ËRÞhÞãjÆÒÕðfÎb_Vü;ƒó'2¾~’›¶ç£HÝ¢]Õ@cD–ö?TKøWü_=ð§ìq«ÿ³PÙšþŠ¿·ÒW-Ž*N¥ô=å”Sšþ2‘úèqJ;%“:Dn´2Ý\ÐÁhÚÖþ{ƒ,¦µšÙe—•¿RˆÊs.SÙö¿½êëcúZÖÄ“(SJÍæ‚üߪ~ga¬þn ¿dªÓjã šUyô]—äo`}›F!Å ¸’›¶,õH¹…è·×{je o6¸ï§~’â}\á½ô¾gä‚ K¾UŠ*’UÜ"Xã½€ÂQªû÷È#rŒÏõ0Ô7À i{¾„Þ ø¦ŽaPÞ–‚¬d†Ì"¥¯Ú&¥¯Ä•¨ÒWq:<ÄÁ4”_íQ~ÅÁNl±”R×ATé«ðÍd5·ÕXV°¾¯[¥Sú*üc,¯ðêRX’ÒWqQ¥o5…Ù‘ž@¤·Wz)‘úf§"ÀÍkN‚ù“ŽhÛ{ôÉ„ËE ÖeYøe{§CÌæ#T6¯ñ-eI öOé‹À§¤RÌ¢˜c‹> £#ïWHTv‘uò«ôGÍ~O§É pDY9 •‡,+ânî–Ø¼qJ_%<’w‘…ž 5”¾*§ª ŸÚ_ꆖ+}UŸ”«íðÞÔÊÄ:†O‡ Pä{Wn|x–ç‘Úㇻ{ ëþâ†Â»‚uë°¨ÒŸoþ"Ê£¿¡ÔÝq “eó¿ÇrJ¡œµÞ¹7“6£òR>gÍOl1>NŠ-\wLqœ·áeß2«OßkáIfeõÓzQêx”V®–Ùw=Wã­ÍefÎÆ+ÌÙ™l7æ ,1ûÎøªb#¡o7È·ƒÿ¹XÒKø>­ø’‰‘•82cvåîèmÎ%ÕĹT¸œ-ç7ßø4,õ7”#'¢lه˗O*U£/Ï¥d‹ñ‹f2m¿82Ž”÷¾ŒžiI-ì§\Ï×òžÂ Z;}gé¾cRÚÒ"¼ÒE¡´µ+ïó¬í'!(’¬ £y£Ïþâƒæ”ók˜!ín…‘Ù¿¥ñk¶yèI 0ŽЮòX²'-ÿ$ªÚrP …WH¢Co?‰ùqY’”¾J»<>„W®ëeúÏØš'§®ÔbTÙ1Ý™ƒÑÖ…¶Š¢ý¤äM†npÀ¿¥è æàxXæ½uÜí…Š¿ÄÂeoæ³rþµZºèĵcs™õóc­wÞü {@–øñê€ú©í%Œ“Ëh×}ÅäÿNýdÆ5ö3çx.@Š‘ÜLæ·|‰reäxjÍ#ÛÀ£}¿¢¥gñ,‹@Ï'ú ÷%Ž/&’ ‡.ΣØC¹½döaóUþÝÒ׫»ŽþE_ÐþÖ€Üm1¥ Ç eÂãçñ²²ÀŒ02ûúVož„ñ9Ã¡ÐØô”ó2V ãÌï"zB8IÙ8.Ÿë–ì”pa+ð}&ßǹW(ËÖ 3ÊÊ ?»3ø!¾tœr©s(Vý‹x¼„­WÎM²†J¯À{È`éuøE#éïîTÁ[C§ÿ_ñ ÁÓGéïYƒ»µØ2þ üóô€\…XÞ¢òL7ÕlÑ™]ÐžŠ¤ôù ZÞIS¬JÖm©èå+};P¢¾ÝïRúFA¢¼Ü+·ÊDTy–û_é«þZÚª”Je©Z ¤LsJ_¥Ó Êšf’”¾JSÉÝâú®Rú œÒ·5GL'¾¥{3Ç Úʼ?þ·W°”mš´Z“^°cXî8œÒWåuà·Ö»q —²škO}Îwñö(LÅ<,ì)^æ&³”ÕµU–H˜·Ð÷aÚ|£\ $)}•WЦ¨±6ß×ù»Sš~`z¹Ð¤,gaºjf-«c!CÔ˜”ï²ÎI‚߰̾ …R- o(1Z¾heŶ ]·õò ,$6Í-d–k?†w_̧ƒÒGã¿W–‡‚#a„G¢l[„{ùë•ûƒ±ø ¶­˜žß‹(L~4£©§PÿΖÍQ!±޲x.‚¶°&@#õê'Ðás­Z I}ÁÚ¿{r9‘˜üK0æ}"­ô(¿º™sJ…±5ÐääoZ‚MÜ£(щÚÎÅC4›èâ‹(tØží(Ð ÛîеýÀ•3Ð|Oí³=Ü~ùE.Waì­¿€ ¶Tw>,ÔÍt0·gÄç&ÖGE`6š¾+8:ܬ"¥¯ ÿª«›YðÅBåûYZPÏÖÙNøÐÓÂõÚqÕ.añÂå•‹méŸSœ°æ"þàW- ÔÊÞ÷`ìë†ø@o°X?)­¢zãLÆÔ'Ó‚p™µ?½É÷:œ )}•]J°Fh\¢ŒETÝÄ/®Ô! Y¿´ïa-;šƒé¢ šú1á+€ßË-ƒTAc_ÂÅŠæöF¶¤W[¹¾Êï~É*ò[Ú Âʵpï)ƒPŽb, EøÔŒ-‰ö•¾I©›Î|¤mòØzV„ÔÚôû¥$©eTKñ RÐ糄d:ìK‹èâ@nÍÞmŽšqwáùN‡´öD¡hö„¦®\¤« RÔPÛ®ýó*‚\éȺZ8!ߤ7‚3‰êÆØhÆL,À#ÔßG¥=-s&F(b89¸ÑpAcJÁ|_“{›n½ Ûnv·OqA5Ó‡2QDņȎä½?û:ü‡¸—òäAøÌyÜv¥>h÷Tox ×ÿ4oLŒ&²½[X žæ,”zãà>Þä[¢Î,Avmÿ°ô<ËîÂsRÍÕèÀÄé«2–&ËŒH6áw—HXð¨Åúû¡%²d €þn=H­h†Á®”4þ 6o|` ç÷CÙ( êß6‚?ŽÃ wKÏaö¦M›óü¬“¤|ÂúzcU2ïCÈBǰ¢µÓ0~ šáçRÛ׸Š9æ@â•¶yp»‘~ÒÁ•Z ¨4žµË+ âËuvÁŒÎ½Mç Ö¼¦ñŒ@éNž-Àßør¿L¹UÙå÷~`ìý×àx_b~åô·ÉkÔÅѶE /ŸCgy›þª`õÀ²ì¸ëǘÙl•_½XíHªFU¬^ZLЍÃp?I6›5?þø£ùî»ïÌ´iÓü¨fÝ«„Jбf:›3µiÓÆ´oßÞÈš´ÚOé”~vƒÖÔ¢Öy-iƒ&kù×oI âóú¢s|ŠÚB¥&Q0ë`·¦€Sk0j¨òÜ¡ ¹<:1D®šƒEj‹Üø …y8Ä¿WO8ëqá–Êh-€-±>ÖTžÚ5‡n"0˜oSdà qß®:Hã¶?–7ð„Ç-DŠœm:u¹µ­ s3K¡í+߯.@D}È%Õ{¡µªJŠ ‡Kˆl;„D3””aŒ”ËŠÍ`'±-MöBQØ%Ò!(:YA§ ‚Ó¹Ñ$öY‡®é ä‹V~‹¥|“¢Y~òVGñŠVÿ§îÁÖ‰f?£^õ±`>”_`™)%´ðšHiã°¸q>UwD‰¬ƒÛš —”ÿ ä®çÁ€G­ÓýòîGé÷lhiÉ ß_MZ·ý[§Î7ul†K«á©îl„‰}­ .¿)í‹Ðö%nW,>Ö~ƒU_¬ 2'S_ï²àb€Uùä³ûî»ÿ.JP¿eÑ-ì~\ܽ¬>¥ÔáUï¶.e2³Æk˜5×\3.K(ìý÷ß7|ðA(ì÷~Û$rC¾~ï¦5¹~) ;ñ’,W›R`5U˜Ú袅&ÈîÔÿ™ÇªoŤMc\ÉîPÂFK!z@›ÁßF€¸:ôNQw'YÚ%ëYû.”3º†r¢e‹MÑögå ³!Ñ”¥çÀ/qé9îî}“ÍÓ—€0)o@1ñS!^ŒŽøq0åÙ~0OXkV+|Ý‹ÎèÅÝLCAm_%¨l†ªÄŠÊz1ð‰h¼mêAV)¿÷²-M ´(ÜKÛî-ZiKŸ IG³2j¢¾|3×OsD˜Í(o/€=ó*„ÖßPê"ý0)?†&m}Þà1ð¹×@ ïC} x²)ø4ÁXþÑâàóôâfíüÚ”ûh\t1ìS˜s$S²F+FÙùb‹+kÒ"$œÌ>É’âQhY  #tàúÀ=7å¹ÙÃ|@IDAT*Z$%¸„ÕŠÐx¤~ÛÎmAÝËÒÅÅ’Û0ëËQ¤×j‡ä“fzrQ¹±Äñ:P*^˜û‚2øE!wO4¤ús[3Ž'ݰêiIá|—GïÊ 7}gýç2·ã&éíNFØ9<û³µÀ‘¥ãÆ(Gš#äèkmõ]{ì@yƒ­šÛm΢?Æš•˜)6¢}ò‡šË¡ÎAî ²‹™ÅPw À7ùõ¯\Qæ§&{e(ÔQײ˜ø"}qf^´tZ1>i÷Ä÷àÓC¸¹ø<–ú‘=µ4?”ŒŽÎ¤÷'ñW­Åš[ãFØ*:· cÛPÿmø q÷#ßew¬¼þË^íŽh 7äëôbð}D~„y ³ Bþšì湊¾|•¹ãR,ÒâŇ¢ÐUdó¼d1Ëäž¡ˆI‹q;G”èÞ5•*pÂòGÙæ`ºg‹ÄüjáalÅ~ŒyåÓØqÝÌ]•˜ßEŒË¿k>mü˜Ç^)Nô[ÚjËü–( 5ß<ÛxÔù;z'Ú?(èÚÞG}wñ{Î]ºÖ]Oø‡f\Sh‡Êxðø^»jÌ>ÔÓŸ禲`õ“Uà¬ÛæZsÀÌ'ÌçòsŒÿbÇCÈeÇ„Ô æ³ô2æéFŒ`ôØ-æGæ×5_ÖmNÞ") š+X`°®DfÊ "á[i7‡ÚÙQ¤Ù¥áhxðŸpÁõ«¾O/JÓDŽä0µÖÁ¥`qa¹øÈ§QrßUйoú¡<é»”ƒ,ž_¯²¼MîI>XoÝUðNuS'ô&býø*é7ÿó)jÊ‘«YX[kåì;\i_ÐŽ¦C²¨žñüòVÑ/Ŧ›ƒÄ…xµÔ†ôó™|›ÃH?Š_+ÚžO0|P_6¼GEâ[–§ûrâó48°ivXÌ‚ê$äªy8ô9^‚oµtìhè,e“@|ZC2:ã„õùfçA«GSÇ-¡¯Íÿ¼ ÁmøM° ^þ×/O/e®àöìoV¾œè'‘Ë »[¬¼­.Y.ÿ44k(åcéb”G«A§æræ’Œ¿™ë“n„ËšxUm:ÄS £¹0œ`ŸÂärOòn- ‰LF:+’Ã)¯eœ±ã!º¸"¹¾f#©Ô6àÖ×äø¢–fÌš4r9‚Žh¶‚v`u­ýÁA-=°‰B£«r&íRø@#o äÖjc Ö±û"übÅu “@V¬´Jß%–X¬¿þúf±¿­mæîÜÉLœ8ѼôÒK5[ÿú ¹|>™xÙŠþÈ'(,N«@SôrVŸNé«FüôÓO5)}•VÊa¥ÿ½ Áol MívºkÞ—ø½Þ"¹^ Œo¬KYbD‚–Š«¶LçêÀ¯@ýè+}§Þ–u¬F“Ë(¨3v%[iZ òé =7ó1Fj±ÎzW‹9>¨¯Úùîu@]8w…ÄD%)Þ\.Þº —üKV‚A0J•F”}ÃÔU*³µâÎE)Ý\@\i!T†ZX²Ëî9î¦6q)¦T‚lQ—´5ÿà»Ké÷2Ê5maóAÖ—wb=v[>µ XÚÉïc®²ºÕ‰Ü4 ú£€ÐT+ÂzË÷8˜_= Ö»¢çÚ™ßÒ+»"Š××iË\Å'ÃéÙ3͓ԅ#P2ËEƒÂêe­¸¢)ÊŸ‡ÀJï€ÐÞ8.OeÌ“X[ɵDè€0$Ðmßü“ËY¶¾ɵ ˜ AXw  ¢È"º PrTT²æ®%>¨Söu‹¼WYyMX¦|×R®‡s?¬ô\ã|@÷³J³Ñ¡_aáî¾…`z‡wà'à)&N"ªv$l„–¸ý â¿Þ#Ÿ„,mݾ\ŠÂÙãx˜}@‡Í=ÆÂÁ’\åo:ºÕ[V§:è±: lÏØˆd_0‡¤í"jR}!ßwRºŠáù!+/WLÒ*‘Ù{˜_¥(‰ß@ÊIë—zþP"‰Ð×p@ÝÀü<Ð(:`e-p=´È?„2”G ÞÆS¼ }õÒØñ"fÙí®u—™ 2gÙòµØ³Jñ&R† ?÷¤?ʬkhø0™ •òЍ•Þ 4G)µ£av{HÑ¢±'zIð¤îð{ëj‹K‚PW¿7ù—&r©¸±a²d.Îùç ÇÆ¦sâ÷v`‘9iqÄ¥«|modAUAᆙ{˜³6C Û ú½NaÑA§§² ™ÃêèHs>ÖÅ'ŘŸ_½=ó “jx¦¸8ª7‡2-?’sëI?—釲Z ¡‚w-Eâ]Ø^.<Ø žiÝÜû(å¿-ñ=×É–œ šôYîpú2ˆÐ‚ÑÂSíXŠI¤ ô|~¾&í#…û.XÿšÆ!æp'Eøƒ²Ü'SöÃeÁ4€ú·#~€¿ÙEô?q"ç¾6ïÕ­FÔPž–ä-²=½Ý¢8^Ádëw?èO?uúßKÜIjsèòÔõ8. :ä¡-Q%·_XKïëφĀÓu·šÕÒGà­­yFJoø¤>_”çûÛ pØ©H±*ÊséºøD9×([ÓA qùÙ…¯ø^¢Õð×4ÏÔõ0½ò+س‚ððßãI‡jwMàʯ6)WßJ»¯3¦ÎƒÏ“qL}˜Å²ijùs–Ô×ó} ;L:ž¦HéùwáddEGé—$özcâ)Á3@:“ vÑR³WÞp‚YÁŽ9)Øk‡+¡Mç$Èd¢cƒB¼£œŸ¤ìŽÀ¸vn]…2ÚIIÚ!¨EAßÊzKƘ|Çí¶‰–ù0y'8›î*{Ö.ÊB[+~2ÿAk†..~çk©¤ôzæñ s^@.SÝiBžÿ‰¤ÚqÐö~^eñ?Í눧ª%êQ-eB¼8Zi >ÜüòË/f•UV1=zô0]»v5—[Öl´é¦f¥•V2S¦°Úýé§6íìø#Ö¸2髽­UNí5¶NÊ©S§šµÖZËŒ5ª¬ÀW_{ÍôìOø `Ž?þx3÷ÔéfýB~}e1;•@ó˜£?hóçgù°•0ã@ß<î»ç³GÏ-]²â5É­Ü]Ä4N?ýtóᇆ¢ó]ísªésÒIfäs/Ù)þœsαßQß2ú»í6˜„Áwã!&eJá E&¹ Uù²®¿¢,€\$mïI.±¶¿ÿtˆ_: ú&˜9mí¿Ö³ÞŒ+yhÂwpi3(ù’@LIkS°UBªÜ§ Â\íu öaÍ¢ß?D$fí÷ùF^/2~ä^á¶™/Ö†iµxTq:} JŽû„9±åÅrȳÜö«Ó¡ˆme-|Ž_IYÞ¯iRÛ(m¿¤ç¯¾Á.X2݃¢¸|[¶J,iÿ~' ˆìy œ3a ÷³aú#_i:2ÒD¼,n¢0 æZãOi%߆RX;õÒd ѩ߻•à(Þ¡Ü%@CVÒ>ÈTn,š óÕ’AVmžˆ¤üæî„é­‚¶\ ¯ïÜ,H­cVÎ/ƒÝ©O­—Gؼ¶YÅ3¥å»®P|L¶xËÀžLÝÕúšö[æÒ­NH9"Ú’¯¿Ã\‡ çGº¶¡ü^ø8]¤¨vÑâ¿ßäÏZ‡Œý¦‚tä·³ˆ1;B/ßÀ]‡Ô}ÅxX2Bõ#ü¬kÅi÷Ó,0ÈÿêͲ*Kª¥ka|&ÅÏÂðükÔÍ7o;Jü™&Rgœ–ÅäœÌm_³4Î Erð†,åfœA¦ÿD2¶Âcîê¾³¦‚’] Ô”½˜¨GöÜ3𾀬7ƒÞúƼ„×"pÛ¼?Z°þŽç×7÷›uóà’h»þ/îÁ]µÅÛ_±t[K$ÍVÖwïhl¢oá$Õ‹g÷U“Ë^‡è ‚õ”;Ró‹r» …Ý)6liþväP&cžcì”|zboÉÑöÞ{“b¬ÊÃ\ 9MÖ·r³¢ïpåÆ-‚-ÇX?kÄÅhc°å_Ë1 ïÌBË“àñŠìÂÑØÖ|¦E*™à@F:L2Õp½ùÀ.Ò¹Ã{eÌX¯ì¥/ÅwZ<Ü<’ÌŸôòKQR˜KÚßH߯iÕÞ»d¿3kxÅç?4#¦ìHœ1û§–£¨Á\ÒR]À!†·b½>8á”V¥úÀþÆ>ôõ“vñú½Ì²fó4ó% :ùjôá<¾Ã6,ú§úöÍÍÛUYÏÑ6Ñ™wÌD,ñƒã8ˆo•€1”8Š´Rîb…ZÏøL¯Ï{‰ŸþŽ+¿Ühîc@|Xú¨˜ˆZƒ„â0ÿ Ž\Hõëš§r¬w;JÇí#}˜Xjº·y«åc‘!P!×r³%Ëlµà<”œL¥\ÖB^s` |çý|畲Í58ȨÁ‡Þpú¿½ÉN4-× ¢Íñôynø×åÌñ´ß Ì P ½ »ìŽeAogæÁ73Y˜«<Ò¢mé ] Àt¥~FF"Höì xt2î„Æï þ9¼×¼°´G ä¢Ç£øòe=g&®¼©©f[¬Ó¯®²ëL#ð.ú{ÏM+³9as ÊÞ*°*øpôðhx¼d/qÅe²&'‰‰‘áãçÐF÷ÿ/È §¿îå'ζ÷–<°NÆvsžYšSB!OCCƒùì³ÏÌœsÎi•¼Ñ¢¤øþꫯÌl`t­««3óÏ?¿½—;ŠÎ;›•W^Ù|ôÑGfܸq6^ùÔN²R–ÂTWùÛUû–Zj)£zRtÿú믦S§NfĈöýX`k­[«K W׬º¶k×ÎÜpà ¶íÑ:Váý/¹ä+úI€Œƒÿ¶kc®LÈ—^,¯¬Ueh\š?J˜ÄaY…»NýƒÃÔF×'r5 W‰Ãr]2…í¤™šÃàÞYî=>`%5NØw9dý>dÈ3hÐ ³ùæ›»`‹' pïÕ«—Ñ÷¹âÒK˜àf˜cN<Ñ|øá6Ý™gži^xasÌ1ÇØç¹çfònè}ôè»I­«-‚Ið±e{ãc•¯RÞø\Mý"²ô0 å…| ´òÝÅÞ%ÿYˆ(ÔNM sbù¢mš#8RØÊÔ¤Bb‹5\æEP›ïXèN˜ñÿcï,२Ú?~v÷ÞKùZ¨¨€…õН¶HÙ…ÝØÝÝ‚Ø&¼ Ø* &J˜(páîþ¿¿3{vgfg6n€¾ŸûÙ;3'gN<ç9¿óœçÔv'¢{²“ù îóo¤ÂÖh“mòšäþIDò­éVÜB@Ó Õê¢ýÒ ’IŽÝÐìuÔ…þ¸º©3¡¿ä´pufºÆU}°¹I Öîa>¢¯ ¤Ö€"M#žå;´½µ˜Ö\N<Y²)®'yíÐy»z[6£C¼Aþ³ßÜ)ÛF¢CF» H‰£M|e¦”»Êèc¿[ŒImÑí¦:D¥á)ŠRGâZ £½Ä”ýNÉ^L$?æ÷v>ÅD'îáæ™×ónÀ‘FÛèBÚ²UÇ/‚dÏô4P6§­Áµé×̼ÃÖhœT{—ù9ó,ýs]Ü™ü¢=EÏﯾЬš¾pW˜DúË\0¥ÿýI@¿-£Úæd& W§«Ì3!èþO¨óO²‹šhå̆ª8º‘‰Ø½ôÁ 뢭§Ãç—ÖO–ÔJ/ïz8bñÅÌé½oe·qCÚ˜€×Ôx ÀÕŠ ¾XÅ÷λ˜ßUÛFȼE‘39*2Þy ÓÇØR,PãRxÎdiÂYJöÇý~ï~ýïD{|µj³ê¼Ç2—+ó‰—ÿ){ð_Îà/´gådZ¿Ÿ¶IoÅX2Íïs/°útŽ’–²hsÞÿ5ÀÙWÁ_”þ-whª ú§ ë`›V2Y1Ò7ˆÜΗÎð$ÁNÁäÚÔ§hš£m_÷‘΋Ù2F²I‹¼—Ú$v6·%>3§§¿.ÜÑ£;ñ€œÉÊüÏ-yžlóy4ÌãVš¹œY…8MÕü64—Ô Ä$CÚìS7Ú_ÖôIíhZÕ=@Kúæ‘û—:ƒ¼f›/§ÆlÔƒ5ÇÑß6Ý MÝÌŒ ,\‹—ÖA¹Ã²`Z.R7ÒÀÍh ž`C^‚¬¤9Ú”{!×^È5Ðke£Åõ|ó”ù9Þ1Õ¶¸R™Í¡¦õpÐ(‹9 7ƒªËI{ÞK0N®Á}÷®Ös±bodrd:å;LfZª¢ŸîOXµM¯u+%™ÌËl±IယêL‡¢5fF é§O{2SEfy¿B%º’Öxn&ë© ZžwØß<_§1o|¸©­ÊÇŒcÆŽktI«X`±ÜÀ:úþûï­¥ìãJ£Rvue2A ôèÑ£í» ¤Vœ'Ÿ|Ò|úé§Öî°Ì(ηß"tAúrz^f™eŒ]À– „ŸþžÓFŽ2˦ªm8ÈC‡µÚÍîüWMA×_½¹øâ‹sIöÙgfÿý÷·ìÝ|óͶœš ,”†h=¬IŽZÁT ¹ÒèÖ­›éׯ_®Lõ­.¾¿õÖ[m˜vÚÉÊa3Zrhîe*¸‘Ö¬¦Åèºë®³õ£:Ó7”Kjeô-GÂ¥Ž>Ì 'îYå&`]å±-ÚÓªƒ÷gyǪú÷ékßc/[†ýû÷7jŸ¢#FØEÝ7ãÇ´ÂÒÃ?lºvíj `ºï¾»ÙpûíÌ£>j^xaÓ¦MûS_Zh!NBÎ>K“^ÚÙÒÞVÝ©žEÒ$>òÈ#m™èýŽ9æk»gÏž6ìM71‘€Feú£y|Òç[÷Þ½{›‰'ZP|)„¢GyĶ—]wÝÕ\{íµ¹öî¹çšAƒ™>}ú˜Ç{Ì.|œ}öÙfW•CÏ>6í¨^ý!$%Ú¼KÕ_¸ç¬Ö`¯-Îå¶5ÛmTåö…ÑäE‚‚hôÕŠ¼ê¯©H”-žKœIXí‰Ù§ÉªÏP1ïF¾v¹ÖeƒGþÓÖ±ð¶ïÈ€!ǧæ´|;âÆ‹ô•}Eš 9û½¡h?úžØˆ QoCZ‹«ÀSM¤õýßlßìp{‡Ýz8Ð\Šû+ÄsxOyÍL2Ck÷¤,<¡{hÞõMÔC;2A~à÷)´§‚„0ï}­§ãÁõ}’0{b@É¥Uuýf»Ü£ÿÆiuá[÷Œ¸†mƒ‘”ì…0"AÄX ±e¸FkƒØ ‘ÿTF‹§gƒãèN“‘bôuÖ.Wåñë`s æ)µÀ©\vbÎËÚ³»1÷è݈»¿rk¼ÇÉð‚+˜¨x4áð>ÛêûÌ;Ë 4cì$ÍÔÁ»¾jù“”F†Ÿ´Ka5”=°å»^&‰y}Š“©»-mËÎÇØ†ÉAô•¯ZhoÒøù»¢´í$z:0fiÒžŠqŽ%ÿvJ }ý¥ô¾>GŸP.ç2 +—´¨V@™§”+p.ÏzÏü@PÕ}‰E&mß/AâüíRn»¡µ¿vjÓ,-FÿÚ€~ºzÑ"¾¼høJ=?0ÌrU§Übª-¥.æºb¥É”þÚQ”}|™X@ìyx¯IíHsšM¯~£EEiePúúÛ=Î sPßStDO™½“×’\S»8‚«Àj·cBýS‡:ÐW¥~,㥹§0nßbµ¥/ÌdgÒ‹é<rÃbv$¥‡àÌ{• ê&Á;' „‚¾ñ-p» Ù|Qç¬ÃfSÛ~9뢶½ mCJ £Y¬Px™ú˜À"í‰Øìm€ük2þ—ùÆ€?‚6·Û@fÄt˜¤£»›nËOÎÕ»~ÒÁpO°ˆ¿…-Ý ¿´jÎ|iN÷8¦efú$`¸¸'8¿u‡Ò5üçV ëü´[Mg»èJH@ó+¼Ï˜2äÌêŸMA¯ÌÝ_U™*ÆÆêóË̦#me·@XÙßîO¿º¶Œo Dlò‡5XHº÷ݼÉsjXð-HÙÝ  K)›>¯ò¸VÞµØÝÚ„ú«]Ž ¯¨9þ"ÞRHêõ›À+ÎäLµ³<`_®Wûæƒù‘´0~E.Ò|µl©!¹8I%_®H`°¶® ÞrË--`*6L3¦ƒ~ wèÐÁtéÒÅtêÔÉl¿ýöfÉ%—´€­,ž5k–Ùj«­L÷îÝ­–¯KKym»í¶f—]¢o¾ùÆ^•‡Þm“M61«¬²ŠµQü믿Z?÷¯ºÓZfõ.[Zíbå+ Eáö¢ÂÔÇm•UV5?þ¸‘i‘@ki9K[å)ûËòPþճϛ=öØÃü P*pRƒË3Ï:Z•ûd ‹Í‡o—8™ÚÅ‘Ví~õØÒîâG]·Fø²['£<ô$6Šô)×q “À`˜XnTînÉÔc:ñ^{æœÎChìÎà´³é}ˆ0Ùö Ä†¹ÇîndŽ©éf„ä¾Y‘ó 6`—0qÐÉêƒgãɾDîkS{›oÛdqE´ðeK÷´˜4RùèÐ!†Q“T¹ÝÂÄR@¬Ÿ$ÇõÏ­M<™DÈÒÔƒ4È"©î5„ý ^âc‡evióˆ§èP¹H€Pw5`xÂŒ¤~»319À—ß9´….€pZ:ŸùÔ?WÅÙ3¥?:›µá°ÞsÆ<…&•&1Ž4ÙO¹‡r®V‹²éÝÂWX'qŸ =H›_(ë®÷×Ö¬iG‰ºOÌ£Lª·ñõ¥IðŸ«5 ÓÉØiœH‹ü@“Ï,é´ôcà×8ÿðU'Àë`qCÑŸLf—K]‡.@a~^ï¿¶¤j«j1èûMªMɳã¥11ýk8ZÉ“K¤éòÓÒçh…Ób¢>WÀ«„¸ê“R©8»SgoÍZË,óG@/süÇû4÷Vt¯…ÏFƒâãøö8>R*ïòüÇ›ßgkr °l*sLóÈaJÄKi10‰–¨ÀO{ÞÜ㌴Ñ'1f;пmgÙˆ¸miK…¸C›ˆp~§ÞIMqVö;…îÕKijÊ!¯Ùœy§æ#$ ,»çŸCwÅ{Ý·„pÃd°ÙS¤Ã8,mcg[V¼•Ø~Ê.þ1ИX1ÆŸ¯†ç¬?a}$ÛÍîp*z”¹˜E.‘ë1kœú»vÖDRþ”.a|¾>7á\% '¬ê©'f]?’¢ÁYŽ 1è¿€7Ö]C™<Ĥœ²Icd¾ämúŽÚQýˆþ0{3q0\W-íÌå‰Y,8¼[QÒ: Õj›Îíg¾ÊŒ4;ÚyQµÅ nή\'…Ò¤-¦ù®ôÀ¼»Ì U]?iíÉ$KRçáß"&pך§…ˆsu¹SÀÇ{˜AÞû‘ÇàœÖŸé# kcUf­t䙟VB§óòÄ8³¹@Ѫ[ñ*ÿüás÷É^=Ú¼5÷|óìÜ rΑ7Ú%•y2ëo%3ª5w*^¯±ç½½…6€_ß·)‘ÌýRjÒù Í/!´òóÆß͈3$1~äÍÓ”V1ÒØš£';ûÆÜZ¼á7>šíÛw°‹z:ÐP òS˜0Mdà‰êH"d'­­L)ÄuæË¥E«]_|ñE«Ù;mÚ4 ï´SWsözãH`wmÖSfEÔVuЀ:pk´Í‹µ%i÷~þùçVCxË-¶°ù¨-Oåµ%+sZìØðß½Ík¬a5t{õê%/3‰~85+ôÜ4à\³Ë¿;[ÀZíH ÷Õ.^é÷êÕÛÒh#óO‹)\p]Ñ¢ÊyçgeVb‘$ŠŽ¦n6i¿J¶þ2scêOÀnŽmDZÖå멎2ÓôÉ4i6,b›7—F=nžPÌs§èdËK+ÂÑ„`ŸX/Ú«LWD‘g×eFò‹>0H­>_–¿2(h‹òùÒÊ ‘løl7©:*ª'€áp߀’óh¤Ù}Õ$SB©ÊB6ïeÂ)íIõç/„¥1:Û†ÙÎ;–ÐX¡´ íéd¾1Á ñ~íav×®7Ø? ÔO=¸¶Š$.œ@]1Qòê"/NK‹2ö$nv ûºl»§´h ì.a2jÛ´7"*ô4«)¾õ{ -ìB_Ý÷É~oÔ¤^é ¸u‚~Ò'j^ˆV–´X‹Ñžu÷ÒÖ¿ÂÑ^) ø'.–Tãù¥N7+&G³&x(ŸÍ æL.í’ƒ€m§U} 2NËDš5odÉΟHZdë1érÚŠú¾“˜¼}–ZÖh µŸd3Ó/fÂ1ÌÖô©¥pɦßE´· ­NÙôÊ9IKf3B,?”kú)S;÷Œœÿœ:´-ž”þ‘ÞÒöYþú›óMöån-÷”»~ùm¤“ùNgz'çs£ÛÐþÂßb’ž/Îóþ›§ðœ‘_¸dtrzi̸Á<~숈T±“êþÛz‘&8¼*IŽ^ËŠN¬ Î~&þ»G …FÇ+ßu¦5AÒ‘6¥ û2D<‘ŸuBýBéÉöï@̰ßòšìíb¾ -àØÕÍzšÃeb%Ž’­2ÕS19¾FMnȸ޾â‚kko }¼tÎ='ô9ç"7²iz(u½° #[Ðuâ_QÄ|jÞäÿ5ß²9©ùãh zs)Z‡Æi¯ÑO}U»ñt òóð7in +©›½lž@kÍ·c‘ ÞÍÉ_€nEž/¸,rW-t¾ƒfã6\oNµ¶ ië'jì¨7³$¨=±9+-¿kØ)ñ›€¡Ö[.Á’7|wÍb|Ýã÷uиšáí¤6ü6f0")ÛÞ.$D^¶’í^k{¶ê,ó`õ¹fÿoÖh ×Í8àÍIÕKÄòXb[oñ@Ë&È Þ”ŸKˆ–`£ºŸî`œŒýe«Ý™ìM¸oåé‘LÉ~ê íœH†c©Y™‹ ç` |‰}p@çÝù^·¦i¡Ó’ÀϺç¸íÀ¯™çó_3ñ_Ã~ÉÃ(«,wP½Ä–I8ï9=’µ®ÕíaiÚ]WËÌë§y—q0â°\@…ˆ#IÒŸ°ÿm}9ûÍK“æÊŒÆ;ÀkŸÕ•º^ý!ú“v®D=ßO™y»gÚpþ/‡k[ybOú…¶nËþïï¸]ÀØÑ Í{GŸ%KÙ6î•v±ï×Ù :åt~žbÇâìÞiͯ TÍJ‰ML¯ê«Ž–Dk2öõ¹v”öIhþÛÕ¡{¡†^S=Ì*ÕýIÅkÞbá²f"<¢qh’I€ñÝD8>6IÇÇbÔÇÃjšZS¦Å2ϺG¹HbN/|òm *Ø‚póÌ“Tù8 oaÍ$åyà‚x¯ÒyÒö™3æaKG(+DüS .RÈi<šLR¿ˆJS'^ÇbŽS(ÑnåÁp7Gntqϱ×ð‰éá€ÎÎèL´ ãH•wsàêK,*ðX[Ú[q X˜ÙN™ù»]]^ _? ˜¹4uïlðê^¤©Öê-¼pÖ!ûï_‹.ÊZ¬»dâõ×_7:äLàž€TŒaÈ+;º~RYHÃv~’4­½6r¤Õ¬ï¸ãޝ òjß¾}»4C×]7/0Ëþ±Hð;¼O@²À½ãŽ;Îæ¥íý®ÞmàzüÓ Y ©}Üu×ðW°}_ºk Y‚0~Rwk1±P}Ÿ ~1«8h'ú ÷Û…Ö„_§Z€Y‘+ϸ<œ»%dšÄ Ôj×:$1ŽTÏ"ipKS{£6²@ñ»?Mµš~óš/¬F°ÂËLŠÚBv­$\¨Mü\SeA`™?ÞÚ¿–ö¸;PîŠ+.7þE™\q¤…–SÐ ÞtÓMíÕ¹û¯‹/¼ˆ¹á®Ûmý} PR¿C­¦¶«?Õ­êXZ˜+Qg¶g±mÿgÿ°1‘~ÛWÚ»³76Ç19©„üÛ¢‹Åëg·Z QÊo2óí¥-ÿÜžÊåëN Ï9”¸Yk.¦=¬¶µ1ç–³í³D’‘Þíé“ϱT»ƒjïdb+M$Œç#,KcÌB³ì¾‰$Œ:m ”}X´Ô–P ¸~MaߢgohGW„¾Ob§@½åAOñÕÜ–_=—E Æ@ÙÂK2!NWVé¾›ª~¾°}÷Åon{Õ¿˜ë}JàBAZö8µ­pÿ˜É×öLÜ"ÿä Á7€¯Ú픞k~¹!ªà‰÷ ›‚)ÓXÍϱ‹7³å®@z©Ý‹\>±ðÓ[üÿ‘ö'6ÉÑÃÔ| lãuƒ_8’γð0™ Ù >öžmLi³íF úÑlÇÔb‹³ï­Ãž&b7y4›lª‰FÓöŸçYt-|ÎÖ-›¶Ö-úŸä”‰9¯Ž´×õÉ+Š4éÜ5± ó«Ë¢¼­[@êIÒÖyÌõ'i ¤{'v¤ŒN¾˜™šèå¸J®Œþ^ÔÜ쨔ôO“—\t—ñ49Þ Ÿ{}‚xÑ€õõ¬:ϬÌoWß•Zh±7¾¾~¢o¡a 8æY³…Òs}_Ê‹7·”1…_gúÊpúJ!' æñ6mì8\&Ç|A7´%­=ÔŒn[\íÛíܵ}‚ WòTu¡‘Ó×T+V@‹v©X²_›Ýà'ùvHÕÂ4~V(¿ç²7Á¯ÔÂxëDʼ8"­ØâÄh%Ûâ3ïB_ØåøW{òÞ 81eNÀ¤’ Òîãá?²û«Ý$I~£'‚”4‡Ton2Í7-‘Èf§bÊÓgvÑP<ÑOj-o|”qþéô,Œ%ä’}‘ñr3´Ìc¼ë0 øÕþcU[ÚììOÆÞkqM>åi1ÆË[ø¦òNEîÖä­wÄ//µá5ádôº°,*[ÍsŽ"D~>¸õ¿“úä¼Af<¿ó_È÷ÂýHÂgG†Ú=_Öã>K²»mÍ0¨;¸/9ŸÜµ?íê¶[ç\Z›}0Ç53v^—gªXØ.PGž¦iÛÌ‹•µsÌ6ÙÖry•¾éÌ‚v›YÊŒ&¿—iûÁ£÷.9šIXÚŽ«:­ä¬@`ÔŸ›“Ê'F&/6AÞ™L{= ùò_ÚH"Eé2fɨŽnc¼„4nâv£u´¤_#ñãþ´›ðÚ á½·ÑoÂô,}aOŸ”\<€~2”{½›ä`ɶŽª¯Êäƒøk±Èm’GÐþš›K1ßr,mqúšÙ?®¹™ƒ݈îÅ\œ‹}ë›IÒJ«Í‰©µèGËç’ÕÂeWÚÓÝ|s}Iý5o–jY’IšnéAæí:–²ò„#^aéGžKÕGÉ÷vKÚ™Žæ=H›{-6ʾ~¨Räª|âz•Æ …ÿ•ç !·¿Æ£æTRÆÑ\í¯NBŸNc|Ó"gS‘*µÓ8ÊL[SåY*]ÿè˜ ë¶`åŠÜhkŽŒ¬;P@˜ìž Xò“¶¸ ¨ä4dÃà¨ÀV« sðZn°Ï&"íÞ?¹ÿ ¬µÏœ„cÀ:òo —›ØîW‰´éè”q• àÉ“'[ IiñJ3Y Û˜1c±Ì…¿Wf'ØÜÄzo¼äRó"[ïešB‡Ù9Ó¥²^a…rÍ ëE¼ï¨Û9dìôÓO·vaeOW ¤l ×—â€wz”ý&2øÛ·¯ø*lûö, ýrLDgí†ÏÔ¬/“ãìä²oVsØÍ‚ Ó^¥Íê·s-­Ö±Ô®„GÛ%ôySÝ¥ÍJ©mÌÄô½Î»ÄU=”%@! ¯rÈn¥dâQC+lò´å2 Þâty‘:×· †ñ¾Ù˜}è?h²Ÿ'³gÐvwa‘íY»©}ÀF‚ÃáZ›Ÿê¦QzÓyØ¥þÝœBû=ŠÉØŒGQ?²åêIC²Ñ'­µ2ošêÞ(þ²>ßë­Æ{3Ÿ·ÏñMã±Dij‡=âžÐõÓ |Ã-hß ü.E7Ðÿ&Ð~¤‰Ð$¤icJ×$Í¢F8—`,¥=¶W—ó2©=‰3™ßØrBÛ0É yÙ1)༛±qIÇèÔ»\šq³$íJ}øDúþŸ¶Íy¾êïÏ3±w<Ùʼn¾®Ž3å^ С¹ÃäGo·ZÉý·$/zr€dzæ3@Fi³Eˆ)ã]Ljö€9œCùžJ?–뇄r?åî*¾™7(¯kÃdÍJRû’~Ý’/íeõêü_’û­4çúÅðóSÉæMBÄÑšY¾¿¾±´ˆ”âî;ƧHMrõ…¾E_/õ‰ØïèÝê²+@éNf|mwëv Ñ™ìD‡3æ[““3ãáE?Úg•¿d½OáWKÿ±2‡ËýiÆf_áШU‘ü»†v…vå÷ ®ŽkèpOWFäèó™÷±·|JÝ·h,æË}Û" ¹ íÍŠð°ÝiOÒŸ M0ŸNß’ùÙ÷DÜÇáV|ãøS>r#ÜÕ]F"š½ÿµIcÔ2üš|zƒú¸Æµ_è•öñøÚ Î:ˆÎf“+®ÅÌSÛÄ¥ )€I€®k“¦`˜ÜS«««-`%I€‘#g_wQ4qÃä!gnÁù+ }ÄòhŠd¯õ‹’Cµ ZôŸl• TÛ`ƒ ¬ainº÷ GôoÅ0¦2pï۔ϲS<£vŽÞt˜W%$ÛIJß*àRv¥¦Ñü”ÝZ‘´âûm+‡Ã7ÖsÔi²*ßÑw ²€¯4Œ‚¢ßFCì8„ñ¸VXâ¥ë,-|¾[`þ@ª½¨,dæA&¤á+0U6€µ8!³aú(¢véÒÅ<÷ÜsÖTÄ÷Þ2:ѯ‰­4$~Ê;kˆvšÄO<ñ„›¥¥Þ·o_ÛVÝ4BqJ‘ú‘.ÔBÁ!C¬ÙîUûסˆS§Nµ~]t‘4hÃA´h£EÞª›³ zR§¹í¯¿P°Üc‚»uzDF\]èYÂ…¶Œ]`RŠ4É̼®C©{—Š’óO—iK8¡ÈLŒ"½–1aŠÛJ‰TO穈ÒÎѶr‘¦ 25E‹0‰ÓZs9¤p›ØÃTŒÕÚ'²~>BлÐ&§Ú•PgË×å±7&œf¤s÷_%êmÄ?¡oôˆì?øO‡¾Å¾²7ÞR¯qpÞÿX&©:ˆC$í¥>¤£ö®;m´[8mHïß6¸9³2SqÚ7Z¯ \)çrñà\倾©sˆàMÜ]L eäo«.¦/ì›ÝÉÔFT›ØÀ¥SˆHíì7.…îÌÛh#lk«`ä ôß–“ÉyéPîñ¶V+‘^»ø@E}$úM/¢\Ï'ÐØ•¯è}&’/3a|•v(͹}ô—SÕ Ö_“±þh/.šH¢U·ÚFÕÔG¿ó `,ðO&6gÑêtÚâŸ,ÀXJlÍ·—9ÖëÀÛòÉÊ\…;Œ1ïZþÝ~LÆ‚ ïʱ‘¿£L¦3!o2Ê|ÆbÁhENa¢wmt6Òb›{P´_1Wm”ýÕ2IœôWL{hlj,’fháa“ÅRÿOýŠÓQð7Ù¬–-s¿?…‚{KoAÇ[hƒÇ/O,Ø´¢ì[LÆÉ§ó~…w·Ê]™]“ïK´Ãè/»FÄÕb‡l’Æ‘Le}GüŒu ïـ˚UÚ°fNd¶©^”<†þµ±/ª€3‹¢©«Q&OÒ´“Íü8x-í¸Ý ³[VÓQ»VŽÉ.ÈIóÈnÁ·ÛÉK÷ïhÇ·qý6F½K[:ÿô¹.ãî”Gb÷Mß‚Ð8Ù“NÅ|ËŠY¿eïTsÙ(¹½ÝU¤iNm‘2Ìn6®bÊÎm[LzøA_ez`ßAÌûæ’ª-ÛÔ%4•BÐ*®žiSÕG›dª«å§t ²K…Òˆä™uÊÓLs^úbsVFEë16¹ÝA.ŒGR‡0DžiŽÇöÝ€¾gó]íBï#mË0ñf–>Gž/Q0Sf’~žÈûÖý×Ôr(W9TE ·{©œðá03ò m€ªse¯|H™¬éì}åó5xe(`²©É_ƒ2+³ræe²¬DíBmƒ±5ÛOµ[&j(î;Ä÷v„·%°5ikç±CgæþÙF\*A÷Ù<~MÌi©Óx¯ ‚žÙ'ÉøÏ³£MÚïG"Sʆ¶ê{ˆ]¨ÍGY¿úÓ£ê$ÍVhÁó쮇©×¬`Š,q/üS$­à>,B¼L ÏÓªêÎ7Kp0¦ø„Æ}ZD–&Ó.ÆØûáëî8-lÍ¢ŒÀɾÝB ¯‹wÄírˆ£9xÀ»ßO°f æÞÃ{¼kÁj­ñ¨b’Frõ”û DUÞa¹y/óî?Wta¿ŽìÍ¡¢ýü®A€ÝïóW¹ÿãÓ‘˜ñÈÛêý«¼Y㽇Úÿ>´¿(%§ÆË寗RåopB@østÀS›V ™wÞy‡C®FÙC¥t°ÔÈ‘#­Ri3:m_B% H@•Õ)S¦˜·ßöfù…©]»vV£Q¦"² lÕÁb²‰º*öxg7«±Q4ø6i›¾Ìú  ×ÔwÞ·ß \´ßXK X6€¿ûî;{¸—òwßéià¾ûî»~§Ø{…[’ð•PKõí·ÙÆF€è'if:íL¿»»— Xw=zô0p€Õäõ‡×ý®»îj¿S&%¶ÉæÓ­[7—D£] W¢èû…[ZðЙP˜ÖC]”N@0µrVõ$þè'¶n5F³I¸rÛsÏ=­‰†'›¿íÛ··vwȪ¬þjÖ¢G1:t¿ýͶk­cÓèwè¡Ö4ñûì’ïªÓ `/S 0·ß~»ÍÿÔSOµ6|£úPT¾®Ž¥Ù+[À:`Q}RøIË^f4¤ù»×^{™®]»Ú~'­oÙ=Ö÷¸ø2Q!ÍàsÏ=×~¯p?-GèððëÀ_ýùã¸{iÉÎôXT‘òu¥!Ai }Hs&è*”=>Å»‰ý/ñG"Zy¤Cj‹d—3ÁöpWý e·À¤º±rNG–Ì<ÈUô=?g¿Ô:4àßä¬à& +þ`½/lªÝkÑ| ÓOG"€ L,‡dzâ8&~ƒ™”ïÌ$¢wجm°3QÍ -±~¶%¢m=˜œýùHðü 9o§ÐóMw$B»ŸÚÓ2ûòžšD8’Ygÿl8íô„ Ù[šïÄÄ„8>ÛyG ?ë¯ ¤è“´ƒÃ[˜¤¬ƒYÒ¢OÍ­î©ÁWÙÇ{&»“@¶µ¥Ý®í˜ÒjÔ˜Ñ>M^óŽöòÁ¼ÂuÕ÷2™K™—˜è- ìH¦S‚åå|8H‘°áߎK‰Ua†kä½û–r_BS£v¡1· PjŒ—ä·I™} /¸¶¦7èÝGü—íêÆâ+ÉÓ~ßâ÷0^jëõ˜0æ]”»ÕsOÁ›ŽÁǘ'õ´4ª~‹ñ¯ógôÁg²>¸:ák9¨ö´~Ãô:m~8cÂò´QHøÃȦù¶\hßY‡šZÇô«Þ^Te€@ªŒ+@-ÞÓ1ÙŒ“è×WR~’¦áWŒ¥èhxÅñ>à[ †‹If9v(Y¯’!—4 YžW¼ ‚~‰qÇuôßô E_mY|e×òb8Sß®FFÑN‡Þ,ðì 8øšÀÖ9û"k=kM(M#Ì.C’!D/o4p( ðëâiÁìM¹áüP2Ù0ô]³/còPÞ5È᳑$ûÕn¦ÖÝföB{W`e%4›÷ñt_ÙŒ¶®vÖŠ\?xšg“þqðSð¨ï‘ÆæºÔ‹4šE²å>‰Åœ-i«’FÜìl!x÷8úÐûôO‘ "‰cÍY¤¯‡çŸ‹“_fð­Wð<‰€[p]‡_ÔÏ:õb •ÜÐ&¤þO¶Íǵ ogI>YÉkŒËÞ:ñ-ÿÉG.ãN¿lÏûi)Îg·Ö6´ƒ8Š.µ ÚQúƸh>wñö í–hfî`áKöÌsÄBÑ´T'Ó-TÙ·®ýËtLmf ÙÎviýBkìÿ޶)ð{ÚÚ©ÅisÁr¹fÞ}fßyÙåiÕ‡40þ<ÆÑ^˜€Ðám~Ûú2»´ý$¬ù/­.ª@IDAT NòÙÍäG‹¿;¸Va´;IfÖ4?ñZx\ÌB÷»à»ÏÉK1ôMd̓Úgæ½Â„*q‘¹”9{’Ϋôµ3Ió!b{e¥d–©$-®X…Œ™L.JkÃËî!ÜnECý]<ï¤îNaÑOç<ü/“¤Åÿâ°Ø6e´?qºÈZÕN"Ù÷ “°Š#i¼ À•É?é°4·^î²-*pós´ƒ=eÌ5l¶Ùf¹ÃìŽ7Îì»/"·ÒJÔ!dduèÐÁjX ¤{ß}÷Yó ²IêHÛàezÂoóVöihuéÒÅsqWáP*mW×Ö|`N›WZÈ:u2ïñm¿a¶¢{÷îfìØqÓŒ4#úŠfI 3Ê.«¾AšÑŽK‘@âåᲟ×ÎÊ•i ÙPu¤Ã~£ú4,©"%f+´+On+"–Õz Œ"iµÊ†Êc©¥4T7>é;þl„d%>Ȇð•—_n~l^p g.ë°¿ÿYmGå¦úÕawC¥õ=fÌ –ª\µ¸qÔn»›'ßm2´ÕR¤…µ•ñ:Õ5ŽT^ÓÑÔ•yiaGiÌÇÅ•ûСC­f²úŽÒV¾ßT‹3Ô†Ôߨ•¦Ú¶¾W‹< ¡æDv¢ÉC—_eꚣqO[—°.!A‹O Èâ\&Mc¶ß̺-þÂX„Ón§[ ioJäÓ¡Iº“Vß{£…ÃášêYö½$TU:¡iª÷ù»§+ ]÷¢´eçVæ‰Nc5{)&¶š¼Š¤Œ¸Ó{4Y‹†n«=†!Ê.´K-R¸C6² ÙKì`ëô¾Wÿ)’» A„c?+n2lrN’ݽà–Ë%´;›.جÔJú²YîZ›ÜZ&ûš_í–Îò2ÌþIêu,ȆpcБ€Z;#=ìì¶X'ûSÎox¿\kÚ»EÌÇL86ý} U.ˆ½ñsî veL¦o…'£ÁP§äLôöLƒí¨”wNþÍKNZ /ªE•oigj›•lŸcûý>˜qšÚ°îè8‰öçlKÇ¥ÉØòKõiüÎ…j1"L:S‹^ÚQŠæ¼…Iã˜d†wdHŠƒßøÍ í\ŒÙ=Óƒ³‹è´Uõw¾÷8Þó ÀŽÉã~tÌ «vl\‰æÚ¾ôȯIöBèeú #; ÀeJ0zɧÕIÿÛ -½²ê³å¦Ìý¹ºÙ°ÒmÏ¿V, ïÓN¢@Xø?„Yߌ‰÷ôΠmi×LIóu6åYé·æÓÓÛj|­å§Ö4Õ‚›©³‘äíhÆ—Õªv5#çjÞ#¸«”ؾ»¸Y€iOîý’­(ñ;þÄ÷›ó­#)“á“ËQF=é'r×=ߤ1Úž{o"ɨ·0öÉDÀH¾m³Ô9æ4Gw®ëf£¯¯Tr l ï#+ÿ"Í^¤­qC@àÓe÷óUù>ztÖLˆ@êOx¯é¾ô‹Þ&ûàÍ‚Rú¦¢Áä™›§¥NdQpSª²®Nvò¢ Ä?¹úÊÂ/w˼Ø”§¶ïûGßÂqÒKGÿ‡Qo]“-Ì~QòU>dÜß…f¹©Ãkz ñ¢½ƒòúœ·834fWÃcçf4âRô»ûG>™ “ÍçƒiG¢T±#<+ ¢Ëžï™ÌQ.O·b·ÃR¢ž‚2†?ýð½ðV"í¼Fw>„æ3¥Ú͇.~§ö}1mòFÆ|Ùoý{OÚ®ºþ —Óí\ð#vK­ÍÂyù6hiÅUgÿeÒyªÈç¯D¸#÷,áF ÷WÓ–€¤çùMhÚlþ‚©/Uu£y?9Ûô«=­¨,áëÁ¯¨/ðëRp&X€¬ìãê&UÏâ³çš±¿Ì°¤ 4õNáðzȦtçÌ™c®ðÁjQqâ&àW€žBji;¼Ì@Œ¥-ð?i{ʆ±¾9|°\CòÇ ƒ‘‹Âg2¸HôWE6ãç+ÂÑËzvzGö^ÎG]ð[i; —u9ùøÃ¨¨]HóV É|ÃJíÚ™K/¾8 è(Ž@Ìïðý?­¨ûe4§dáe¹×!Žr™s(quÀ¯2l,’ð+m¶¯²Âp%éª Ê®³‹ûÄåW›)Í«ÐqñZ§¾Û ׃’Õ6ÉÔÛZ·÷Y°Òôqä?¡E6HËSöߤçè&…Q‡›i{Òp„t¿V’‹3?®7òΗ"h……ÅÆÈ»Übr='Á‘%iH€Þ:ÒåÚÅ…LÊîGˆ?ƒ2ÔApÒ¾ôÓ¦´#m––¸Ÿ¦¼ÎDÔÓ~aJÐò#@ž¶¹ õZï\ÒÜ’¶ô,“‚©9߆ÝH‹òeòv@²1ÎarßʧÞ°Ê­I­ìÉú'j‡~BÛii´óßÌ¨Ó p`“©yG•ŸA8¤&›™±üÞõù¨&ÿð=Ïÿ[M¬NHí`¶Iö2Í=·É°0³$ƒí¼ëKæ ´ºbBë n§}<@»O»òƒ,š\Nì˚L6T¦ÒFKÛ)¬ˆ/­•X àL“øšüã5i=ž×úðä‘Z£É÷ÕFÍ÷Y&îϱ¤{“ü¨o‰Mx?Á\Ÿ×7…ñ¤s÷SD˜åpû.½ñœ–%©shKÏѶ¢´ì’“ôž¶¥ŽG#m8^ã¥' 2¸ ê4£üàÞH4#Ÿ†»¶[Þã¼ÄÚ&“m+:->úGO‹ñ@ÀžåÉ?¸ßäÌLD}›ÞcŠ´Í·«Ð Œƒºõ²•ùåöHVš¨|*üìrÆRÿwDåS?7zwͽf™¹·˜á‰·ÍIìèx14®”“®Æ9?¿õ⬃ …9hßM NâÐê1©­LoìmÂ_µ&­T-tï À¤ïÔn‘v•"– Ì´J×JhŽ–0@¯äIŒ¢­£´ëJeº¦YÇ´¨êkÞ™w9>…i¯Fü„/ À¦Öæ*ôþú="ÑÅô¯ÚÆl9¨©$&@C(u2ŸH¾ó޳ŒëPn¥¦J‘ŸL¬¼IYkáOòà ,žW÷sàp٨ג ©Á)–ƒ+jcý ÍÍ0Z1‡¿0\/ÍbÉ'Ôç{SgÓ6Ö± ¬ü͢ɀô/æ›OÔâ& 1Û‘‹“ýyn xu±/0ã]ê Ü®ÅÍãj3ƒ(“±¼ã™VÃ]œeª/Žw+óV7³Xs.ï0Æ'‡ÑkPĨL•-å¾É…Ð>ÿ)rA¿ ó(‡äá|ëD~/Z_Fss=Àï Úºv8’ÖûöŒÁýù£FÎ]u^GOêx$‹%â­/ÓŸ®çÜÍX‡*PÎ܉Ø>™r>2ÙÙ]s‡é5çH³Wâ#lm‡Í”¸ ¯Z:‰Z葹÷XHÚ€<,ã¦ò?è’`a¶êxúøÕÔóÛ´giê2°æsd2P»Gƒs‚¿n)HQMñ×}ÅÀ›uäI2Ù,_™¬kv&ó’~Ôù›·ûßXÌtã#GbÆä×"ë!)EÔ×K@›4G„F¾Jw.?ib:-ÅR ¯â(Œ´a§R0Oñ+%m¥w ¯âê[P‡µ[µj ú†ÍTš±ðÚ*â†*  }•Wuö§ûEíç'iÒGÒªŽÖ}ŽŽ!aF…ÚÉ 7Ü`µÏe.d÷Ýw7Cο04Øå4YÑamê •Ð<ß$Íú*hˆL"µÄ­B’6¾Þ¯ ˆÕw•K‚lp[nNC†?®lv«ËXÆ”DLé‡Éoœ]‡2h&1U ¯H6ªü ¯Üf¡ÍE™Ü.(ÐWï#A´)@_¥=Áü)ºri|ÒDVŨ™õÔ´>>œtŽfT`ô_„ïÏ6Nq'\¦özÏ1qËQb:ìvr”ßòø i…\ÝŒ–P ¯Å¶M0ÚÒ—ÐjK³8–¾ù|´6DÒ¸(õý6`ö߉¤Õ,rn·1Á\¤‰A_•ÌÙ÷uùêúý$ÌC´G¥MôUHúÎË!`Ÿ¥‡HêÃd­4iÒØ"¬Ù‹<¯t›ÏO2a±M¦=‡»½Òjã§ŽaAàû&¯è+‡¯ ÿyô•;-É´Ï‚¾zÖbÄ0ÚÑbš„Úí¾r ’£ú¤™´4ôU›™ÏS²ƒ¶¨KÓÌÔ]b6`»¤Ÿß¯"÷ÒDöîPS_(¦îhL6 è«„S{# n΢ñž“»R7›F¤WvEj“ìÙJNh’”h6ò¯¤ü8rÀµ€Ò„õˆ6^3œÛ•²ÏÞE[‰Ïçç'HÑvÍMle25çñèÉ5’âèƃ„»< (ºpÒÌÝ~G°Í|I_øÖÊqá4’ ˆÒ"º£¸‘I›†slÔ+½»¶›™’y{öÓêúêuè äf´€ÔE#Jõñ¦[U´C})ó<÷X£íÜNzù’¹+=3÷¯PGŸ`~À‘4/£èßÔÇÆŒ‡ï[ð.iNF³:MªN•†øH4ääð2}'RØ$O®g¼:,u:ÞôÏ¢˜Øâ%Âm– u,mà!Ú¥ø‡£û3#M·¹çÐæ–Ø¢÷…JIc}—¤Ì,úŠÄ»Wƒ·ßjã^Àüµ¦^ð®!¾ÅçgÒsìÂo>TáÀøO©—ë^2Ç×=gÞb7ÑÍ|Ÿ4(Å[‡¢™uH„®±ìÓF¾²ý,~¾\kÝ­´GÀmH¶•u8Ûóô‰xZ«K×QÈ@"ÙF€¾¸ N.ËM~,ÿ™'õ³3Xl8À¶ÂBЗ V«r€Z?è+w‰“m<=•Gjï›ó=Q»¸ÊKP2·”}g?™ óƒ¾r§/)ž(œì®?I.N?ÚíÛ6´á¯S§˜³2ëpHá;'·6®“ýÌŠÉcÍW,ž×±,93Æ·¯ûÀ<;ç,scz¬éŠ)• àñÒ°rôè$x·vih¾¦±hÒï’<׌KîcdϺ3¿ÿW¤~šù†_v–˜yœ{oý}@_Þ7y Ýî.n–̾ý_ø¢ó&Z<¿@vФå#]ÝQJ$µ×˜K™ÉÄ‘äbí²Åü±xçdOó;6èÞ?¾5(¡ÈÈ?£,PôU´ÿÁ YNM쨻4 ¼ôÛˆ-ö>sðÔoAÒŸà>?Þ£M@Ô æ8ƒú›t*x’ö­À[‘Lo0ìX’É„^½z™³Ï>ÛÚNžöê´t@vrÕ:>CÈØ¡ÁQ9rZÅíJЩ`´B’=jÙv.F?P'Ùa´X°ù©<ò¥P˜ÔšŽ°%Ê‘X±¶KEÑõÙÉ¥›NÊFŸÓ‚ñ‡¿%f¹DƦ iaê ãòðJqa*sðïM޶Fxþ°âöSYnšÈêp—8Z‡>óºlä¦z3 íŒÍ³sg G’aàÐ0RQ½mšµÅ¬C¢Þáaú+±C˜#išÎ»2÷˜¿ ®Z˶šÐb¡€w:ü¦#-vU&• ã& ÞÙÖ6;MiË¥NÊŽ£ë™¨EG¦£ Wt ›ÔÆ„üe ãg´Â‚_è% w‰ZtÐw;’6¬ÍÀIÓÀhêÂ%?]ô…‘f>šä<“¹Ç4—]6?ÍéÆÜ'êpÞ“ö-ì!;efx+gðB&ß|òþòlv2`ÃhtÞù(r· e|$‹`aÂd5lcQ õhú‰K\–VêØÑýhzJPÐë'Zþe&ò|Ô¦êü²÷LŠðªÜiÞñLâo«<^¹1’kr~LT¨I|`/³¾’Ü×zH0j&Ë›jñ)H^J[ÎöWmË®:ƒƒ€~7=rm\}9O˜êç§W©ÙÉ>>eM¯ÌÙ• 'Yó^ '°Üͤ\`‹ŸÚÂ׌æù`Ööããùç˜;í ÉÕb"4г÷íNî«_’ž¤¢>ò'å+fîyæ‚y7pð[<Õ.É5­áçž,³:cê™l¡ÿì‰ö` ô e?XšŸÚ¦nŒ1‡Ád²æE€(ÔtX8]Ä\”•ŸÆ ›vjØ:ÈeÞ7Çak7¬©{ Zvýäm Ž<«–W|‰¦Êä¹²qŒ=”u2ªãÒÈ©±ôjÆÃÓ"ø˜¬C+›=Áí2¹´ò7HÓ5· cwòݽCxï.E‹ó.¯S†»R>+ñk.q¤wÂâòG}ª›ÛÉç°·hVÝþ‘ØÒ Í ýN9!ÅSòHÞ½Ÿõסƫ’wq©@½kr.=™ˆšÊyeo>g<Ûšzx9ì‘μC÷þ€˜þWu£uWI:“1¯‡ø>âß÷Nåvã¹k‹q_²¥OÝmNËh6M["SÝ•jmJ*E°›ÉÔœÊxÆY#‘vEÊi”x™Éˆ¿œÖÙ¨8qb÷ºë®[àVÊ¡¬Lª ‚~l"A°U«–VóXÀqTçQw™ dGbçb‚ôŠ‘ÄmSý»P©Á¶Òï=z´ÕÀ‹§²ÑäãW_…ËÚÅ•½Ýi¡ö$ @WÏ®”´ ÀUýÄ}Ïo¤£)ƒ~~@X& ¤uQª^‰V!ŽIéd¼©Mépå†Ð4gÊ\àœh-Êu_õÍqõ÷æØ±œ6ëJÞ&“³­ý|KÓ€»¢#›ÏËNf¼ÞiQœ€ÅðpѶä=¶÷Ì\ ÆQ_áÅÖî8ò{Ÿü¤õà'ÙÙû¶¿k#ÝËkŠÁ·‘·o×÷í~¤N1I¯Ë¼Æ;½W¯d`’8%Û~jüDOCŒÎ–³™ÖÓ{¡¨¶<"Ôn¢Ì èà7—~Ô j¢ÿ6õø ?é»p´éEÝ>•]Lð/”¬‚Ÿ&Šåž’ý!i¾‰XïO#êZãØo—™ˆ8ºÁÿ‘ÐÇw”Û)möÓF¼çùL°‡†Ü]ÙvÔvmiû´äÞhãüÃ×'È7’ë$6¤ÞGÛ¥¨æ´ù· B©tKQ ´ã§Zo"0 »ÉO@óí1&À32¯2ɶÎhÌ}R¢ïM¹Í!Ì)”ïÜ}›cÞOýA¯ÏÛcb&ÿ!ß·ù”ó  å^xÞ Ø™h#S7ÏDÔåB¼Çø‰9ú÷¸É®Äå»Cü¼è¾oN³çéßCkW­Ý#ÕÒ«ÿ—"Ú£4à>*QF.Ý~M¿È+|Ü„¯!é€efC¨gzHæÃ`^ÉÍVÁ}„‘϶DQeêt$|àøÏýÙÞ>´ÚÖÍž5“ÒßþO3ë±@ôrúuóGú5êv¦oc6<@àNˆGüŠ‹~¥‰i„´<3L¸rËÞ3)¸IÞ.¸¦®Q½Zí°íÓ.ô•ίBhìka¾…ê€Ï{¨Úy×üóÚÕ·%¾²;-Æ’f˜´Ð˜Iîff3n¬À¨ì«>L ¹º‡¯ÿsXH)¹ ŽÌfà~`çCÞûgê™Ü÷q´ýr ó¶rßJ»v§ü_‘é߯}h–oJ¦š©v•~†²x?˜²ŽxùÍŒ¯Òþ¾ÖŽŽ®¤-“aŽæ]uF€ú_1ÒB²ÊÞÖ‰ÀøÄš¤? Ÿ41V|ÌøU•™Ì!±µf0ý¹8ø_,§Õð¬ùK9ÎO  ò5²{þ¨¸ÐÿëîÓùÀ “d¶by@”ÔÀ;b<™6ßÞÔ‡iö}ñcúQqú‚éü³ŒC›kXdmI¼m(«˜¯æ”Á%öb¡›ÆOrÙ|¡É%YC_¢\Ö†æã¿ÄK˜¶mÛúJÞKä8Ð$ÀÑél4Fzó# MÕÿ*ä?, °¨ø@ëÞy|ŒàüÝU‹ ¿S?Ë2ˆÅQ<(;¢ˆ9°HîþåFív’¯Ìµ%.Š{úJÈèÙ/Ö~d»ÉO^¾&m^Œ#ÈP‘K`×(&!ý棴'siÿÙ†ÛÒ=òLLŽøZ®Òd”aµµîÚ£±6F›,Ÿ Žþ¥‰ZjëeT:Þ߃µ§­¯²m'ÀM[$E:`JšO:|ÈOCÑ^ÂïÀý˜Ä°Zž!wÿ£j§Í)Û–q‡™½ÄÛÉt@¹¤°XF(I Î@°/F=b4ÆtPRx,Ç7È6˜Ñ)Îa’KŠþp.ÚªÓ%Yâ9}5¼_ÊÆblJH«ÏŽõn¨‡¶c:­X‰²0× ­ÌaÚ6îäoÙ^>Ë¿;r¿4÷ýatÈÏ;סyþÀD”Ga>&u …f0(OÑ–IÝÈcxf˜Íl¸OÐR‹2ë"³7v j(µ)¡g÷¨ÝŸÖ^nÖN—† €“¶¬;íè°ßü{n7ÿ² äØSý.+{®Éþ<ß„†L_&DBÚi¸f}ÒAFöÐQ»}~…@Ø^Ô½lr‹n”êÉ.- j òƈ ›Í½Þ,ÃÖÇ«éÓg2îXà,u¡²qç£rb9’jHî4xH7xoIöS·D+Þ7ßï_é=º5ót\Ääê€UëÄùVä. ¢Ô8• ¤ºc¯JRµwÒåÇF‹vËhý“=-±“ÙûÀ2tíM2´U_Dî “dÓ÷25ÿptiíæÎyP>Iž2Yµm4Jn’íþ‡0!!mäsçÔÜjæÊÞ;¤±üt€ÑALÎE¯3.ilЦ/)‹‡¢½pÝŒÅåNÖî$m1µ mu\iÿ©õ¹/ü¾¨„4N>›žMÉk¬š®²çG:ØP´Gz%Lcœ†vçâÖÄgê0göæ{úýv驌…‹¶ê5w œš¾s›áaêë¥hw@EÉ©•’lÔj¡&–êÎ¥]íG›TD»#’›ºû\sS*-ñIâÃE¦ÙݼÛ>ö©>ÿ:¢î@;Ï ÒÓ¢Bbwœ ëÓ…Õì ýVEÊtØô‘½©3-|ˆdÓývl‹þ£¦ßä÷‰ µ*u¸/mo ÒÓN¹óé?õÙ¡2’XÁôdÂdwž}FôÜŒ1CÊUåε1ë’j²˜L¥/–¹”xäá´ÁƒN }ÿÈ7~Oï+;Ò®n-°ÖèQUÈ{ÉCë½d,Ɔ„øÜò¡Èf¤¯ÈÞ¾ =ÜÍ—Ô|¹dRþ´z¾¼R“d¢CÇšš$²èÄڦДր4=ºÉ5ÙgÅÙ‚–ii™ÆQS•µ„ºbµ(±ÍŠI8¬e÷Î w˜ ‚®É|WöëÈÔ…À;™ùÐ&Am‡S;‘†õ$vÓ·‰Ô•ÑéÞ­?°!~ wùÕõb|«œ—Ñ ÞQÂÒDÞ¶ÿo6‰ü5D'åäQ*Ì6o§¡Q²[ÇŠµ“RéÄùKó®~§"Ç¥Ø4îõçé‚'Wâ÷EÁ‹`"ÛE‚%K¥Ìù S½·ã*ÐU[Úk¹v¡¬þ(ÑÓM¬s1ò7W¼œÚ÷õlÿ:Ž~ww/;‹ â´d»ÒÚU{–I™±pt1Ú_=˜@ÔTyøIÛÆ°è P mè:Ì䀯¾—v^晲RÐz?ÜÓA©ý«pµ¬„WN—Tþ?º¨˜OëllžN­:ؼ9·‹Ni»mózÚ†;ñ]1upßP&}ÇÐn¶ìÚœÉÿälïŸÓápù2oKŒo³ ôÓ—|Ÿ}ö.ªõw€O»äñSMÇy÷[S8o2¹ÔVT?i+rˆíÂ,ÂÀnmI+Öv]xÿUÀÃδÝcüaë}Ÿ<’‰ÄãD¯¤å­Âa„hÖÎ:‘¸áEHÛã_£Ì€Ï H‡ò|†6ÒÐP™ ;$!ßq´£dM‚QE;Ê¢Å1S¸Ž8,âeI ÞÊŒ/ÒEŸ¦OîžÙÌôJüjša¾F š2…´}´{®.–p>ç4RÐ{4ÅÉ9jSCàÃûar¨Ò¶|Û%xD‹ ññ[Ñ|?–ï®wÉž”5éc¿7Ž4Òw°\"ðµF¢Æ‚0a°Z˜^i[ 'ñ¬q¥íãaø³£‹hGs@ÖlÔvg Ô4;a“_óAô‹™˜vå·&læpsÀ½L¿üþ”)=ó ]yï±¼s<ëå(¾ô ²àËŒ-W„ø(K!æAüþÅî„î´…ß]Í)pÍa™ñìI{ 4Âÿà$WV¾<â’ïB¹íOYõg‘Î%‘a1qaK$ó¬]8v­Q6Î'¾f³\j/s vÓ¯ÌüXÑ8/³šƒé¬ -ìA")e¢Kã¶è”ÝZoÔ^Æ«S»¸43ò䨺®¢ÏÄ-\F\ƒ„ïi×s̵–²?¼Þ·ÂèȾ¼“ý8ÛÇÝ/ÉæB¿etРÌ$‹<0‡–èß™v{‡9¦ÍŽ€µ;Ò¦æÊ†´ìƒ‡ÆÌlre]˜ÉøÆÀäªóX™CºðãñýU7ã6ZLz$÷„)A: bLris4²~”]g£-ò,±˜Ì)i¹âxêú9ú”ß~²ä‡þÉ…M/Ò›ˆQùÙŒso#¿ùœÈr h«úAä>8 ¾7‰MÐR~Ý< ßè{ZÄÍú&ýO¼¿q T]Aûga³î¢úñ®Ì×J _ø‚o‘ÜÚ¸Ô-ÑѬÕü)sÑlòÈ<é%.žX}2#²rˆÿi\nZ$-¾ùEó/§ú}‘ÖRWdÐòk9V’’°O(Ï8³’´J…•È¥IŸ÷Æ&‰M‘n}Þ³è«ô\Y·â^`d%¦Jеi§ÿŽ0§­bþCÌüßà/£ßõ.~Ï ïÕÉûÍIT˜DYÁ;ÒF½UZ‰œš27·ï5r { ¡âŽç§C·dìAmD¯¶€Oâ^ôy–—HÛP}Éœ˜Ÿ1aY›ÇøUv…½A\[ç.Ë.×¹LÔ´Ê~Ûà%$‹ÚðÛP&JƒR­ Kuc&Ú2²‚eSоû1lôE•[cä· Aßð¢FÜ÷,ŒÇhux¬|âÞ !¼“ xAÚ›:ÛžIkXÈÖ„Jž{!Јїv0ð]§¢¿ÆäÏÈŽ`j Ò<.˜ OŸ²åµ ä¸(ý+ÛB 'œZPèGÛ|KyÄÐæ´øÞ„éŸOdr£:µåRtß3‚ š_û£3`Ä¢¤=Ƀ¹1²yõ1vá.CuEìNÎNýéë•ÇDz÷?˜\¬Lù.Cþ1 Xu/º÷Gä6™_qZ  bê۬טuZrvskn§LÙ<„ï×6kJ Ðx!€¤™–G¨>´ÅSšì:àrrúÓ1‹›$ Û–üh¸6®þ‰ïÅlŪh0û·€ÞÇ;_@;Tkú„{×M7·Ñv;°@âÕv.‰¢7ⓃJô‰¢ ”ã)pÊšc¢NnÏýeÄBèžCyÚÆ\#|°íCù~C»þ ^¦å)ÏÂðÊô]´ñ6HÓ·ÙZoŠãÜé|I1™¶jŸSèv¤Ž=hâGÖŽöœ]¸ÎÊ…½(õ~&Çs™fçšËðÿ4µ!ÚÁß“öîxCÐÂãw´§¾¹4JÝX>íþ}á›jŸaR›Ó€OúÚ— 7~ɤÈ@Únô¶}»Ôµ#âÀËìnœXx2ytE«®|ZˆúÛ“úS?˾¹µYû eŸŠ¼T69bÑÅ‹¾ö¨TØŠ¼CJÈ-¹tëq³9¼p3¤¨‡³ò’HRW'Öýœ5ÿ¢òƒ+íeÖf\Št˜%;eD‡À VÏ´ºLå­°4žÐ°öîô,xЯœÑ‹HGø­`yõà+!›¶þ©UŸÈ»-™‘bƒ¡ ½iÍ#|V—jð^¬ V_á¬w “µÃKÅçB³ gR»ÃFÏ#¨'/Ú8Ém(ÔuÌZsŸ3/²à׿n”yˆs(ZIÖ‘!œÍ{ ­çL±fdC†ÿùën+“ŸG¿7ó¾™7H¬bLÍáÔó(\¿0Õ7Â}ó,êQÀFy„ÔÒü³FíUfGïÁ𤟊|Ýçh‡'I\ ¯õáØ>ÞérÎnI,f>ÜX¼+"w²_½ãk¬ŒÛò1Ò}ƒq«K ñÜMèߨq6È]¦d‚ψ¾8žÙYøixúb˜¼ïçÂΘÎK˜u˜-šQ‹É×CAÀ¢Ëã›â7™]PÆü˜èÂ\îQ3oÖ~æpßËrxþCÿ¿K`ÞÉóýB“+Àˆ®ªWzFÏø‘¼Œ$ë.+#Pý‚,”™d–œ½+ÝQ‹4Ž]´@ڦ؞[Ls®gÏžátÊ~–]Om¿.‡˜v#¢{[xUäbv~;¬å¤ñO˜J 1JÀu U­`S+#ÿ†¤¡¾£ÁZ‡Ô—‚ЗÊÚL>ôMø¢Ø¢„ß(Рw½l+ ?ÐäT}}E‘¯9a1l¯•*ãÁƒÛÑ_[!] °UŒoµ!ô6Fþ_”[Þ];VãÛú4C_P À¹Æ]×gåà¸'ùèÏôa\ÀÿywÚý4`Ä}eLxU¯¥tMò´"Ji"ÈÞ­å¯×pp×ê< l{³ÞýV=`)~L’+$í ð€È #ú‚o•µIJ~_3é o” äÑLdgX€À¥L~ÉŽ¶†QZ‹â¸;SÖQÔlÖåC¹ÐMpMì‹@¶6BÊ™å'^ý0øÊy„ÿ¸ü8MÒiˆ‡ùËõhõþJ›;› ¹HÛ:¥e=ˆòÕ!Õ–c!êfÀ–£™|[-!¹åq1×#5o ¼¹¶aŸÏd÷XAAô§~Û“öq€•å¿=í ?š÷|éÖx¨¾A@²4ÍhR?µœD}aÔŽ¢&“¾ ¥o«®äÛÇñóø|d„Ä,òL[8o[z‘ÁŒYw}™+1¬ Æ3#;ãð¿æ€%Û’Ô³.@™×¶ðø+xþ„ÿ±Ì8ŃilZƒ¾ùJŒ¼,í¾ÖøŸÀü´¥LfX6A&ÕÕ—7Ê»ˆ¿î€7 gÔèÚ±{óóyŽ«ƒžæ/USOÔ™Ónip櫞߅°Ò›”&V”Ú±”϶ôênš“}‰ ääk³]é_²Ã{‰çk5bÆÎ¹Êtª{<—ð+€ä[ó}{›C<;ûu—æüts |w:ß[É®’@±HØÕðͺ—x¿' 5Ýš#y•Åô³Ó¿˜z›WrùY~ó ÿOµ`;QG#é#Ó\ZE®›4*ŽÊò@i¬?ˆ|ç×|>·ï"džp²«ÐvÒ6®`^&šŽâf=•æHi5“yãÓ¼S "7T®”Œü"íaït•yµúBòB{~¡ Ÿ(‡aÕõðïVæ¨yG™¨¿XDš™Øšü_#»ù=–D}a}ܨ¯Ôaô·Q|õöýµJ y4ãój´óãx¯òGr:›öú/âÂóÿÈiK|È0á¨ÓÑKD‹ô.ôUd?È«jò?G&ÞŽ¢´Ý|úÏ?›/¿üÒL˜0ÁÌ™3ÇdÐôSË–b»ÿÐ?%`°›šª‘4 à’ɤ™9Ó\2ÒN^a…L§Nl[mÑ¢…ó²oÖÐW u@sZ·.áCÂST D¾Š¸lI ¯HS ¿‰òa{e)ÐWq½Ç~o·›9—èko´†£  ¾b«¿´>Ï@ð~Pæ˜ú‹}•z¹Ó5 H“ÌþsäщÉGiÎOÒ¤ì1O'b~fköñi•Ê8ô•=Ei* ¼Ú† ×h¦¾Ædà<4ÕŽ¤½w"ÀØu€`¯Dñ½mØÞdò­mÜ«3›ÊÂÒÚ-0ñÔDá{›V¥ÿJ§'¨|ÊÄåÙPòçS®(|!m9<W:×eû‡o µ³n~EšÒJƒ4G¯¡:bÞuf@¼Ìù5ÖMæA&¨ü*¡¹—Z‹( #iU¶¥ýÄmÓ.7u¿æ¬?α! V‹†­Ð–F©Qã¨w¿G²ý:ES=ÜhsLGÊz<Ç(mPµ“™^û d° qÕÍçäm·-_OÆßždZ¢k²…9 MÀHRýk$ZYG×ýd§–¤mÖ¢AUç[ù,cïX~ÖøjhG‚/±1CuLBK™ ÒDq®&‹"q(H¡úÚØS°ß! è£É€}'ñüUÖ °!Îë<—Ëåõ[Þc¿l¥/štÆØ3€úv¹˜øB ƒÃ’zp@Ö®LŒÏ°í!:½sh#_þwôÕ]&žþZ–{qZo?s|IUý^2Þ¬J.Ó‰:‘º;:¬Á$[Òã]qéõ‚k‘Ó‘f-T wæd¢ÔùĬ2gÖá’Ê]Å-ßO …û 9?ÿxë¾|¿4Ø›’‡tæ]Ÿop9ó]³wáU£ùx)¹2ò“ûSFïãõ©Ï7bäY0–I›È^™J=¸d(ºûq¨6£-~AFšiç}Ï„ýÝ‘?R](߯yñ€ß¿\íiNHQ΋Z›úVÒ*'ô_>L‚7 "šÙW.¦9ÖøõÛB•9ƒ¸íëUB«ËºÜVýJóà;nÜ8óë¯Ñ WéÕÔh‚ѸԞ²s@Xã¦<SÓ°êÁyó7ß•› Öò§Ü•½¥ZY^lbÁ ÐW¿?ÿŒö7Úh#Ó¾}ûœ© —£¿Ÿ:·¦¸Æ2•˜ÌZZË”‰_{XA5UZ$ENã×ïçøÖ¥/½gµ×`rRµcÒ=9š–½hl,Ø„žlž¨à&‹ÊJ& 5È»?‰°ß`"ÍûiÿòßRX«Ñ®ª˜‹ó+5‰)?‡ÊBÊÞª´*s-µúæˆÇ’ˆ„ñùK[uzí1YÀ©†ž9-µ¸Ù´nt'àÎßwmŒÜ41mG[(Ç\F¥ù,÷ ÜÛ‚y1‘][!^ïìʵ7†FÎs#„€Þ˜äÊr– Þ›©Ó}0åðDv~6 É,žLɦ “ëÃïø½³²|`$Êg5€¸ÿÅÿ¿¾Q?¼Ý:ZwËóû&èÔO²¡–h‹pxLS S9X~¦k› “k`Sûe³š’ê÷Vb2b­ôÂæû昳ÿÃî…?Ø2^œG—ŸgñÚ–/3GGÒæœÝÖž´È¡ÍéHU<þÿ´ob;fIïñ‰9B¬EzêñJ×°ÅpàšgŸ\0–´ˆWãùU€Ã裒G'—4× ­¨ƒý´ Ñ~rÀ) ¦Ål3ÙŸhÌß0ï4ˆ>Kz)sdóÇl·f'Á×Ö,ÇÚçã¼Ïeü*¡Ró5*ö#ykxÎahU¤Ý&•Òi,ìÏ&ž·¶¼€ïÏk&Ì÷Y¸pÛvù/«2¢Ýν™r¤¬¡5™:žü_£žFÔï ‰·,ñ¯_ü&µ(©gw4i>ÿ$þO Ô¿4&7ˆ&rÊ) J£ÒÈ2ùð{hÐ’%ÀÑÀ]~ùåà—ËKá[‘Ž3ïÜFÓwìØ±æ·ßÜé|‚׺ºÆbèùt'4Ú ‘O³1î´•ZÛUg—™XCl—™Å_*˜õ¦"Ùðü…²—¨/À7NÛk±üÞ~ûmëÝ®];ã4ëÓO¥Ó¾0ùÿ@þ•̹ü@[NOâSP?ýð·9A\c’†Q蟚ç³>ê0IL“‹¨CÙd“S‡OÄ‘dª/8ôW}õû—1ñ¹³ » õ*ñ¸Þ¤íصm½ªyáýDB~XZ`”€êÞ‰^t†Væ©"ÆþÀD‡¥‰ÒÄ”=ÚáLÂçû.X‘Æøy€·1áŒ:دàEJ8,„+~åjg²5ï:b¶ßÙÆzWr˜ßñ}4¶‚«¤íºSp¹Oïþš]”Ñâ—46¿FS¶+½×ävg²é¶˜ªNvg‹ãÞÉæfy4c£]Os=­êR³µ¶èš—°Ë<þïõ@ÕêÇ‘42eÞ¦Rj˜÷'Ôó¥v ¯H ̆hÃ_CºŸÐÎgÑmjÿX_›ØgxxxF°Â,³Xõù¦e­ú÷%a)Êf¡øïîìÔâE¢õà©°{±½ÿ;’ÏÆ˜%9ßdU턵߻ðÞÚPSÏk(5%èË»%:šÕSX©{% ´Û “BßKçzö¦hÅýa)ÆxÛ3ñ¥‹ÀwÙ_-güyvÖ™ø54½×¤ŠÏV{‡"yWâ¥Ãžtxk” ôJÒYšÀ²§~Z}Å[*IÓp̧i~ ureñTeQ‹†þ(àÛ†2¸³/#³S~@¿kA?½<¹˜™Ž«lwŸ(ÿWqß ¯nb¼Ò–÷žž}á%Ï×ÎJ¤9«¡òýÑev aŸ£o«Z÷¦_éd.…`%º.ÃûÍ>œ»¯H¿•ÃÂNoÞ#ò`fv…˜Ô&ôÏC Ÿ—Ûð4Í}ÏruSrmÇi‰åR¾{é¿È{ÕPâõ¡ÀžÄºÆ´|€r8WbìÈÒ•Èxûõj-Þ-¹³†_ñ_6;˼²ãŸsË_eògmðDêÊ[([™sšÙ>¦ßö™ff]ÛÏóqâïà˜U™Ú:Ž9‰%7@ZÃ,4÷Á‹åþpSy¸®{t2ƒ<ô8Àó驪mÊbçˆt-‹æ¿pH_¾ÎüqçǽڲÌÐEüÓ÷ ~§ý=Mý2àµõtmzrÝ ÌEÌÁœU­é†}aômM5"kiwDÀþz ‰Š^¢ÅïBQ ¯Úvå,•¥æOð}ò®WUü>ùß²ž¹7\B‰ˆMËʼ["•ëà'pÈMÉU)ïOKÄ-âì¿éÌjo"ñÕ²õg¬ãÅ´Á¾ÖUv€ãèÊX›À—sõé&v0-«.¨Pê×!~šÎ»OœSuùíaŽŸ÷ßù³?Häý2¼Ó¾‰Y[áÕǃÑö¤œHa2Ôí<&½–Íj£|6Î=ŵ‘\€“yÄÜ‹Mâ]|‹“j/y»•…ß`žÌÕ÷J€ZO£CÑo9ò;ÍrH²ð§ŒÚÛ¢¥¦öhÔ²eG;±A9ѣäh‰nÑ~>×.b‡%ƒ<Êçyûük¯\yxA~äò$ ¹”@LÆê!e½O š«fý‡6ؘ ¯—ƒìÎËÔŒL+ˆ³Vñ,³aŸbZG‹=Ò¬ÕVl¾"iŠzuãõ-’‰¶eÑia½PrÂÌ<¯›-ÎÓV𙳓ù‰uÉ7\ê«ÁÚÖ}ú7ö>Å‘VgÌ•çOx§9³ßëÞåÁK]}Ï/˜È{ â×~uþº*½Þõ·Qo²¿ß Ò}£?\ì½@’ÇzgF”ý—úÊžÒfÿЬÌx1¿.6Þ%ȇ×èpÑÔE<3V¥©ð"ƾ̨ÈP’9ºPgZ ˜6Ws§™\Ð pÊV¼ó ê·C¶îv¥Ö¯#o$'‰ÌijŒ“žÛß<ȼl8Ï»NÍŽÕÉMÛäòvp/ èv´—–ÔçΘ÷Ù0&çˆ$æÄØb’¤q8øÀ•pý©&Å(üÍ^XÅÑ”¡~¤Éˆ@ÿ—J»Må›T¾QîQ9ûEªh1˜ÚÝöu.¨ýä É èÒwöKû kk`¹TŽÖT±´^c›fcSùo_ŸœÛiùúD Æ©»9ó [Ñ'‰æQÔ½¿Öfm!©óº áÚM•€ý€ÖÓº ü\9Û®ÉdLTšë-HGýJ¦µ¬»ŒIú-Vkhi@罕ó@Žee¹Øo~Õ4@hMjhëÚ\&•>ÒQ¶ERÕEàÁðápêáÓíuHÓéL°¢?Œ§]ÞÍ'¥¨ß“;OZ'uJEñù¿Ï ¸É.c¡6¾ê…RK_Ï$UËA>jöùMC;­×™ü‡Ì~ s,‚ÏoÖ¦d4×J¹)¼d"äã–DÐ8øý7hþ~Kš÷g8F(^$°J´'mà–¬îݳŽ3Óÿ5:ñ(ìì.Ÿm•ýYœp‡‰¯öGão8ë!vJƒì¦ßeBZc®.øMH-kNP=•ïðt;×.âЀ ϧǚ³|¦S®"Ïyo?ÿÃóMfÞ äw –÷02!D+û4@ŒeõA$-+µÅLî«ýVò?Øûo©ïáÕR|=wè £Ù¼§yž\þïæPÉvöÖtæçÐBún)ó"t%õxå]ŠdfBv/·‡/ä—úà¯é·)ç饢ñW¯.N7²±à_%t*ü蹈þ&{»Ž÷ÓC²¨Á!l“Ý*£/* zQÜ56m’wz <†Y…sÑxÕ ‡¦g˜Ñðñ’•²=˜ÄÒ9¼³¶ÁíÌ!ôK§©yÚ¥7úú¬ ú÷ì˜ÅÖ†kfw f‘lœ0^s¸‚{ ˜sº*æœsæœ3‚"f1¡(‚¨(æ€(¨¸ˆ#F†™ÿýª§gº{ºgzfg þœ}f»»ºªºººêÔ9_:¥ÓÎcœÓdîKŒ)#«¯3»™gÙ¹/N+ËKo»Ô×Ó¤øÆ]YfËÝÄA92=ÌòÀ#-€»$ít[Þ«-G~ËóÌ$Xµê 3¹ú ó8¼ÐKWð.ŸH¦JÀm°|åŸ÷¶s^·ßüšüp¹™©c Kaï …—=ω&¹4ùEŒµ!¹D‰Ï‰?ó†ˆ~ÔÍ[¶þ4É|;²ª@×Mà»Z9ñ8ß¿3×AYØ&WúÆ>”~BÙzuxÂï!÷Ä;fÌ­Âj«wn°ß~èŽlÔׂßòü÷…Žq! ¨™f\±ß$,nrÀKd€d/& ²öX$mî+9]¤‰ñƒ×ôyG—±Wö6ÿ¾FÆØB<ëØccòPsb‹ñÁ­ŒXuGµ‘ê÷jÒ v.Kø?>*ù)Œf¨¿eêߣwüŠgN!ñ ß)@IDATm4¡« xCI€ïŒƒ)çèÐÛþ@d“ä­è®ØãoÝ©ÔÛíÅsl|šö¯þ'ýkêþ|Žíùå¿?%Züäâ—µŠº®‘|뎷%¦÷FO¥]^É·sx÷Ö‚sÕ@_5TZK¾†q⧪¥öQ™/(ybÝŠÚ¼àor@a~¨(ù­$i#·"-ÌK•Òô¿j2sŽ_‹•ý‚ñŸSì¾­¢bøÃ%Rt <ýÑ|Wˆþœ +s¾LŠ\|÷ s†NG *Š¢o ×'ù$áF¢Lù¤|ƒ‚­ê,ìi _>FŸön4ónˆ&¿©.y—\IaiWcwi`š¡‹'¾›.x”ÏÎr¦µcú>³Ðõe€,§7À¦oµ\r3>пŒÎÎ… Qôž„´J»G7^_4#Ä^Ó_Ò¢Ö;SŽïRù^Èõ9 ãZUnŸ}š‰Ï2ÂøgÜ“%VžÅÂÌ-¥$¬½ý=é&bÑ)%d§o!k.×¢öjÆÚ#2“ Ú´dxF=ô+=þñGJøàä)óÇdŸ)0[€ðøTo³ˆ¬ŸK$m°§MïănÂëöso?*ñ1•‰^Õ …áp«ø=ޝB_ÿ›}8Ïø2óœ1•y^&³¤òiÊÌ‘O±·¾àûìF}j³5×]‚nH9ØNï9žAß…ßÈ’íFÀÚ0ò>i\êscjœL»¼‡t‡‘·ÈÇÍCÖ¹Ú´ÐRúU£äÖ™Ý)S¹“ÓQJÿ¢<ÛÓ?ãÎfšÔa`IñçÄ‘»ï Š\¾(s-ß°–õßjƒ 0¤ ‡#¹Tùg°`}àµ}·®åý¨böSª×ß{ƒWmï¹9­¾ÃׇBbÌ£A]l¹ÎØ;#Ë×hƒ37$¼°2©–6ͦ.üï¤äâXW™m้@[ šMŒL,ÞpL!åÀ©&Äò4,CAñð¦úNóebãüvQ,ƒÔÝ€1Ÿñlmàé‚JÅ’Uê¾$©¿á»õ™ /¡}×¾uC6ø‰0mz¥{ pä¥ñôÁ/3…ÞpkÒPé ‘ä*Yà:ðbÛ’3Öh|º«ËíbÖ¦…Èèn|'Õ4žsíð›y+“qöІ3¹¢‘{¦btc²@ð0ê̆cÙ^Ûs»­G×ÑdSÕÖð¬QÜSí) `‚“¦"ò6Ve<-~^(õòF0“®—É›ü‚'»t cÊÒ€®À£[S7O¦fšãàÕy<ÎMPà8‚6’ ¯°¾smÈu5Y(ŸÆòo{3“¼r•¨væ5Ž>F‚vRðFȵ&ÑÄÁ}Ð]jWU—ÒØT½/Ù¿¡¹«¦§i!7øÇÑ|Û)¼‡ê"H²F¾‚I‘Õ¹'— WâöD“çúÒ~)ÉM9Õ=ÉSï›á³n6oþµ>a|c¹$Iø¤ˆ\ÜÎú¨›\u{.õ ±ÛjVɃ8®loKçÐ$Š—ýp󲤹‘o#==4éÛB¶#VæÇòŽkäeS¹€Z²Š3!èéúñMP.Õ}ރʕªb95>ž¬áEiÉ"1hsÖŸrœú*’Õ?ívb?°õ'y«.öÍ´‚çÆ¡ìÊ ¼ïŒÌ¸ñIÄØYG”ŲÄí\8~úÚÞâ8cSáÈåßMw‡.?+'eå`«x%[a€2ÍÒ¤9EX$Í ’p0Óó -·—Ð’/×yö’³Ôÿ¸õ°KEè 9dÔß°Û%…‰ÝOŠÔÃ2BEbSœ0Á3,v‘°€P"}áo\$n{7s“Rh“ˆê¿Í>ÆÛrl€ý§o¯Ùúb$k–`KÓÒR¿øãä"uÇÝ‹Zù „#YM lóÎ’?í± 9€·cHÂ!²Tóò¿-õ—©õ)NYÒÉÊťᔸ˜‚åÆÇÓ‘®k¢ §œÔtÕ¹Ù"KÐÞMÖ§ìš. wIi6B 'ß²9U<Äß$3íEh-ÊsÄdå)(š¿âK€viO,bNa±¬'ŸÀ呸þK¬OÀbJ Ÿè-úîf™eס`o•ìÜ’±+ï´3JoOñž¸¤ZÉZ‡r~¯G¨PêyRü¼ÖÀ¹ž÷‰ˆ'?ÎWXpce–ÐúûßW¹T]†²³_î:ôlBãµÆõhõú•Kí.GiÝ)Ãe!û“a¹”Í !wÆ–œ}æË_œ:7…ã»e/²J²Àª}kÉ i|†>$€hNoVs“°_&%µz‹ÄˇdP†"™Ø‚>Ò3/¯í|5YPŒÔ~´:Â¥5hãûÃOsÜÖ½“;ÞÁ«s1J=ƒ#¦äçÇãæòmù^xžÜìZ²å/VöŒ1„Œqž¯>vc¶»£ÒhryšXñ\uoSN/©Ÿ#ï}MÌhÃÖ¶Þ@ϹÜDh…€CiÜ9\D¾d/µÉWwxëCIúxõ©6lOdŸç™í”I¡ƒZœÜEM&ȧ´Kí9—ßáÕ3üO€7//±}¤Ÿ9ŠaUÖéa4¢ñBSS§9(3ùe›04½`Ú¥ÚšÏù÷ø.,é [˜>ºU«'aöÏ>W ·&Òqe{ déQ$mÚé~ÿLp³FŽæyƒáõÇÒ‡·. q-O¹¯C®9–ï·4çŨö#ðvo&én'Øjß $›eRi¦±ëŽ3 ¥n³í\.àä¶ñX¬ý½VænÂåÑõÚ3©y2rŠü%oCÕØò*Ï]Ì%ˆúÄ–lIkÎ$K”«n/ޝ»ˆu”þœ‘æ#×Å þßâ4âþ‹ñY&y\^A<ûâbg{|ýÿw7ÐŽÓð Ø\ƒ“3kÞñoàÜ?x]·7åøfî—¥h ÔVFeW¶¸‡ozT.nb[¾õ®¹ë&Ÿu†'ß^á<›\¨Êdþ€6q=y9ü{*ýúMZáåúi.f\ÑäXÔ$âG™Á¼šv ÿ¯¹„oyváldÕáGa´¦pq’«ð§Šû’8ÉóãàÁœ7yòdsùå—›o¼Ñ´hÑ"?2!²°}饗Ì[o½eÚ·ooöÝo?³ì2ËdãÞ|óÍ>K7Î<óLë¿T~L‡ž«“9½Ñœïá%^¨–Êkâ%>($º–Û{i.ªøv¿½q‚çòåZŒ$ø}Yœw¬r9 PŽÊ;j³¯?¥œæ²(ùLöå©z”„#©É¿ê¢i”þÞ—^ÀQSÞË—YàBÀððá#̈ÃÍšk®iþûßÿš:.º¡š6;ìׯŸùöÛoM×®]ÍN;íd:vìÈÒ@§tr‘òâ‹/šŸ~úÉæÓ}ÓMM›Ñ©…T3™2ìéÁfðè·MÛ¶míýÖ^/ò»ÅÇÐäĬˆ6©Úž’ ™!Y¹õÈœŸœUrÜ»ÎñT„È‘k8}÷1,»£›S=Ï.ºEX&óE˜xANÑ”‹‚Ï2õz~„Bæ¾–„Q}£œ¥˜{Ç9®€°Ÿ’E£ÊñXW Äî‚2)»ž_”)³–÷\Ü«ñg³ Äú¿Ó^Sûù…WAßïyN/Úð €­Õ¤ööÇO±û€vò-ÝÙ2Ç_¨=±ó¹J–†ÐæpòÅøÊZкÂÙµ<$Q [°ª€ÊæÔ‰¬k:ð&ý©§ZOûÎ%„_èóÍŒUÌk`-á\'Á4B”—î%ß«ékQ}C`Æ­|è·…y§+2€”bùŒ,… Ž6) ùC,D5ç¢>K<@À"´oúØZƉì»ÍHÚò9sÿìÃL{*-ãu—6>B}Oòô3_²À…ü–¿È’þ&EÑXÙ¬ÑpöžÑõ§U ¥Í4¬{×ÁýÈU<ÿ®Æ¿d÷d.A©†oqCf:xSÚÃyËÏí賟`ÜKO•admjSaa J’‡ï–Xß\Xu8¯}2w¼½Å¸Œ©­9Ú|V÷&ýfz&°Ö½™=vâìEÚݹ,e׆ŒAzëLÚÝ'¥röÜžŒ’»ŽiyšKPÊ#UÍݼê´-wKS__qV¾T±1Ö‡fî`ž l £zÑ—àìãÃþÙU](çs¾»Ñ­È Ðï/ÛšÝÐgø&ÏÁ õK×ZÝMãåÿòWÚ¥k•è†?òc¸™ßyÞ ’ÆŒ é¹÷EŒûÁ¸ÞëõБjðä´¦ܶëQþùéríè?K%ÉHøz@6K­y•~u&ýÊÝPÏNÖ4ÞFÝJ"Â=õ3žoK¯EëPî÷«:˜ÿ`i%‹ÜlÇĤÒK!¿ q cåé>¹Œ‘>ò}îO‡¹PüÆCÞž¿hz‚ù³î.îNu8BêVOLcý§ÊªÚ®ÖHlhVJn‡«ƒ[áþ<}‰Ê¸Ðê¯î´awoˆ°Ðº¿án³~êã,´¥@»ç5&•g$öd\¸}þ?<­È8UFyâ$ ²m¬cUOóYëæõô×6™t¿SïF"ó½Î·›H¼ùv7ðÍ.d×gOñ²p²–­Þ¾á:ád™G£õ<­ÞÀäfž#à3ŒÚe¸ÍŒÁâ1Q2FXZ…Hýk×3rRhœâÏQÖÏ÷Ua‰È¦—Y^Rµ ó6W ¶­oùæ»g"¿•™ü˜Ú§9Ïy®65]yußã'Èþ—%éÓ»°gû„6Z“«“Æë<1ý2šçÆ\>-½îçr‹<þÕ0Œ±`\.üưLJ©œ [pQð+#9·e€óVÉò¶“¶/c‡V¿ƒþ–?ÚG<>üsáÛºKmÞQW¡üš–OÓÖòçÒE´|P4uêTsÊ)§˜wß}×44dÙŒÏ÷H ßpà f©¥–2¯¿þºéuøáfútG úõ×_MÿþýÍÔñ_™©“&™Iü¦LÉ š¯¼òŠyõÕWm¸îé7?‘j%·ÌÝ’K,*½Ë.³¬)Kñëæúº÷Ý#êV–Ê%r3X,Ð.ÝpQØ+‰%{™'ªo2Pt%®&~öfåÉ#|ÙÛh×–´:ý¨søý_¼U´]b5sAUÒ¬b'0ƒ½ÅMX PÛÍôIO²Ónhð¨Övkê|-‡½õúSO•¨T&6cFòÆà#‹_k³–ª3ñè­Öò0/Xy ñJâýˆÿò_|Ûî¦6/Á%Ô·~"YYŽ ï¡j§ÆkQ>N(O¾0³=5]m7È㿌1ò¼i®…å2ÉœÉ/ºV“4I|މ g¼©f cÜe>Nµ»ºM{$ü³7ÞæMËǸŽU뱟y9ã×úÛþÔL©¤–'ßÜÇH(_ê}R¿ayìŸR¿³ouÖÃYþ#ÏÏ[÷Æì(O_UÿÕó»~ühÐ]-ýž]Áw'%1SÇ2p®Mü'þ:’B­”z€[ášêB”ZmË¡öf·ªuñ¡¼dž ¯¹d½].i¥ék™ILåñ £õ ú~aý8_àx;÷÷<ßšrßdÞ05³O%fN-÷ÙÓ©.#,QõuOM/jî­>’)‹Õ²Ù,N}uK´Âf¹*&€SîÔ6Nvg!ÅË„;í_4ž²VW¾Ò¹HVò/§$J6Ôäód $ KâA=kõ÷ ãXÜÉÒLVö gheËÙLnx¹²7Ž{.yCåre7Ü=¾N þ_ã/9ÐW7Ѱ/ä$ކ0ÑfÕ þ;„i¼bV»’[e•Ý‘ryɺ~‚GøzmbúNW¢åËàÞ´ ÎãÔ@é|W{ø,ȯ¦]œça1ãÀ/Žã €äB6…^QK?Ê­™î馇4Sæ¥e[]Zôx±‡júöíù'Ô)ÀèñÇ77Ýt“Ùb‹-,PܳgOóôÓO›Ã€¿üòK›Çå7ßd]h!Hád­û²<<ùä“CŸU(PBæ—ƒ–¯…Ò,¸WZ Ì"ú4Ï÷.-uñØ‹#äyÛSñÑ1 •³ wÏd Jù ù VteyÒ,’:Ú’”·Ðs‚išóú·ß~3ê§]t‘Ùe—]Ìßÿm¶Þzk3dȳûî»ûý “+ò|ËÝw› 6ØÀ¤R)kµ{çwZ ûÇ{ÌtêÔÉ\ýõÖ_Öýw>þ˜ÙõðÃÌ?ü`t_ óŽ;îhó=餓b‰K¾B.$x÷gv/.IÜ aüð«Ý”oܱÖ.q3ú§Æk8Û÷f껓ý"¦ï¾{qµU~òáC-ÃÛ )u"Òž|›•UæÒL/À32õEø•UŠ—®DP¾ÅÒµ Èû?€¡0º¥VJ¨|*îÌsßAAßÈËX_¸éëÀg[eé¾kÖF˜ž—ñ@óá⑬Ï'Qjˆ¬ ÖB¹ú°ØŒsò4–ë ò›˜í<]má|úZîk¢¶¼ð®;jÌléÕµüú5ÜÎu­ óþëÍ7“Z©¼DZºFâ“¡ Ê9ÜÇnX*ÂÈ¥¿âÄQ²"bù‚ø¿Ï¦~+³Ä²Ø²Q_ˆ‹AX1Ý~›ñhióê¼±–Xøæ¨•ofx–˜O#üCÛwÂ3³KfÍú¼×*¼×cæTÀŽÁ(Êýfžç»ìÞèˆnŸ’Ï´tÊHyý7`FÿL½Ê³·‹H…¼)Ñݬ‰^`| ê¬Hùì²óÔ0â~ž-ˆÜ©lºß|ÍóÜrò*ûøä^€Ò÷™«Z¿a­ÁÔ6ü/$féA…,Bk½/XjÖ|'“žVj*â«ÞC 6ÌR²¯6u±~D³¡%ŸØÍþàMA@èr¿9{œw‰æIî¼ç©]Ï‘ß×ÙÔÁœ3×ÕWRþ7.ŠˆP8Xµ#¿ÁïÒ~Þ‰˜<)枤ðŠßÕˆt­;i!»¹ÔG¼S¶.ž‘ÜÏÞ Ý,ˆi7ê©È­\­oG_’…¢—¯ üâɱI¥[Š1ðn€Ê–€¨ý"xdì C"Êïø±lì8õ ¾Oä³ù;xŠ& £@ý`]«=&]½”<’HL‚ÀË\¸µ|bió^ëÇMÛ†Qæ4~Ëa9Ú IºãJðù_Òq!Ìàx€Ë.ð¿3½í-ý¢¹§îE&8yœË!§¯Dü‚«Ü‚†å^?Ç}ƒãœÌÝÌõ]ÚV1Âaø¥ÅÅÑ5g˜Öø`Ï× B2¤1_Oµnk#ÐÚ7ÀTíIÈ"ŒÃ#­ –mxÇ¡<Õm‡ƒÒ_šgíÉʸé|/‡äfaWê ìð0|¥eúí3‰8ÁF¾”IÆc¿´Q©,iÃzTwúüñÊ¢|dæ+jϘï-FŸßx¦#9;ñþ(›ÜõÄZÜ»${å"9¥öÿW_“•¿µÈMlÄæ¦Õf*cZÔÊ?7µô‚AÈ›0FÿŒEúö¼“dG‘ö„éÞ´¹'ó¾\-m™_ ôyÜMûô»µ  V¢žY¹"Pý¤ä"v_ W–uáÖ¥{mZžêÇiåZ–/ÿÿ/rc•@цµMªË|YþÿKÎÍ÷,IWhƂʠC›xÎëTʤnìw7nœ‘U_Ÿ>}òÒxE-m¶ÙföزeK³á†šO?ýÔ^ Ø]n¹åLmm­ýð£ÖzØÞàŸÀ§±cÇZkÅçŸÞ <ØÈB8.éÓäÄ縩Ä›—j з¥™÷ÝäFÀQ­µŒ_•¥JKoä̹™0ÐW·ÕîçÐWåYxá…Í"‹,bÆG¼`»&aÜp{âù÷é÷“L q×__",̸pXo½õÌÇl¯e­/ ¹yu¿òRޢѣGÛã6Ûlc>üðC#÷,×^{­ +埖@.TJ‚@\;^ÐW·¯.ôÕFç •´’KqŽãRàfÀs-{ £(÷Á¸Ìi³ÁÍ/fY–˜kÓ±B$‹ ו7Þ§(Œ‡  ­€KaÖàrÿ0 ÅL ¯è(MYÏ r|=Nb0»-ïñ}}Š˜–Šz¬n| /±—Zúe-ö$y¬yªú _”‡°—Ÿ@ÿI œ}g¹z“™I}M˜¯ÔÙ33‚v¥}\ Sãð:ŒÃ^Ð×¾@ ÿöc¼{–þÖQiª37ñ‡ªÎa<Ýß—«\¹=L¼¡”­-`±\s¸Ô¾²*ãx _ÆI𨆶¾›Mö¨±Z²€Ÿ£S$W'¾Ã)×riúgntÎfyìÉ‘܈ª­PJ}ËÝuUEÿŒpdd­ôJTñ×éÂH0œ}d´ kÞ"É”ð¹Ø–'¨é§'žx¢ÍdÔ¨Q3“Û†ví€t’\jÓ¦ùþ{:!4}ü×ÖÒðì³Ï¶€’€%Y'ÊJѪ®¸â ³êª«ZK`Óˆ#lÚrþ%óÎ[…aV³¤ó+I9þ“˜Œ]»AFÕ¾ç.@µ!||]k¶aaMM¹ð ú–,5iÒ­[7û ¾G‡Ì_ýe-ð×Xc ;é"ÐW5¢ýðËýÆoX‹^õAŽë`Ñ?þhAàƒ>ØúVØ +¬`-ýu—ùèßD~N·ÆWBeiæ],QÊs1’Ò1HVbóí‚PýÃ*§¬Ïü*Dó>X.‚¤ þ¤¶Ã‚iIÏ Û–ˆà¦y,A¿M&ª£›…'öÜ;ÍSĬÒ<¹`¹÷0 Ú|ÇæzK$’æ1vï•\È.Ylé©£¼7l¼˜‰.¨17&ëRþ‹'À÷Ú’vSêûÁô‡ öSÊéóþÀ"W‰l~ÒÇt®ÛÆZ®*¶¿ÏòkñÞ¯{2ê`:WŸiú¥®0»óζE4œî¹Ÿ;=€²n ÓCÉ!Thgó¼è-žB/¸Ïôü“ÅÒ»åL KÚŒæá‹º'™˜µù7ŒÊã~î·ÖfYZæy ç\4ܯáùe¨ €ÍE€"µ fQD¹ìpIJôH2ßK¥?°M¥Ê¬05ºªSÖGó]ô£°Íñ½§ÍfÁ¿Rk Õ¿Ô âÏñ ÕW3Ž4¾K?ÿ+‡èÍÒ!+*Mµð)’ÇüJ?§äü’LBµ81pW’J„=Ùÿ1ÿÉr¦’höŒ3LuµÄ€i™¸6|­²ÎÚæ˜cŽ1Ï>û¬yá…L¯^½ì¹@'Å{챇yä‘GÌÀíÆRQÈårŸÎâ,³ž—ߦØ@]NÙ[—“¨Ì4Å@ëZ8 9â8I Ii#§9IÚ¬M–·²¨p»ÕV[™÷ßß|ðÁyÅØn»íì\ª\}õÕæ ƒ²@±ÀcÑOûì3 üî³Ï>6î<`-‰ë·ÓF›˜wGŽÅg÷kœ÷"¿ÂTゾ¶L̼áá54W M.=ƒ•dú‡ý#És~ rž¯ ] š¨÷’ñYÕêY9ø)*U󆯂•¤z¯qÊÃÕX/j£«N™"Èb˜µe¥KØŽ _ÄJv]Ù‹hYSI ZÆ*3*«VÜîÂ-ëÙCñO'ë—3ðë(Wv3–¨L2á'öF©vI.l¶@Ñåü±åÉSÖQ€Âì¬5ÐOÍæ•ØÀå¤ì¥sÒˆÿÚßÍã(HY€!‰ò”<%OVëXбä´"Tµ,ß`/¬n·÷X÷•’w?àP„ï|ÈfHû{¬åŸ#\; ÀÕt6Ðv½L Èž,‡½¿åæ¦êãC­âܲ¼Ì{Ë „C‹XŸž'‘¯HŠõD|OÅÍEeI­¾ŒQß‹Œj g–<˜ûk:qªïæû_8~Sîj‚Â}VÁ|g[>ȰSÁXù7oÜ“à ù·ÊI™Z-ÍÛÚb&+!Ú~ð“ÓK±l ä-p«; Ÿ¬ÞO‡·W–VÆ—)Ê$îP,5ÜfC›Ê:‰kÝ>•½z¢I弉)€iÏ ohfs0ÐòÓFê¡ážú'JöÛ¼÷Ô‚%P«Ñf}ÚØQ~scÜs$C2mÀs|sFë«w0«Ö<ÌfŸ YÐW+‡ŽÊðB­¶cÔ,J¯dL»1±šé ¨'îó|ró¿äEÓ6)Bj,u2 Âï͉ Ob›ð‹ÊW.nŒhûòý~IÈ„vT^ÚØÎ¤nç¶ÃÏs o~ ¾çðmîÎøÏÆH¬ˆÒËxò¯“nqd¬¶l 7pà ªfsõ|Æx~.ßçc\(´Ѻ𵡟»:í¿L `Ì[›¸ûX^±¸ù‚qꚈzó>Lr¨;y`Ø6VÓÇ~©n`bK>Y#H²cPv.ôUÞôúÓ8‰Ç+\Ð×)Öšæ ]ñ‰B´uu+›Wî’Ç]I·Pê÷Ô@sÔÀrÍ‘©'O¡,è$Lž–OuôÏóáß—ŸE”AþŒÚ”’óŠ}-€ÙÝ}•.^•Íÿm˜½W±voÈÊÊûBÚy2µmÛÖüù'Â…‡~ÿýwë/TA»°¿9âˆ#¬Õ¢€^m>%Òòôå—_Þn"µÊ* `PûöíC­íÍùð_Ü!c^}5gþ²²¥swymJ®Ú¬ÒTMû£29ÓáO³ÙYxÊÒBWBènAYèÊ‚þÖ[oµþ´µÁâŠ+®h~øá¼ åB@îNݺ™úï¾7‚<ð@ëfE‘Ÿ|òI³óÎ;[?¾ÇwœuñòöÛo[0x±Å~ï#7U Í0k®ÙÎl¹å–yψ ¿”qh}6¢òZäIÙr"Åã—ìŽ÷ƺˆ“ß^(œ;ºˆ$œ†)&ò37/¬‹< ]T™´œò^B ÿfÅ|þEfîNÖaù>‚E£6V«4ÝB9µ!KÉZÆ+ #X÷ZZx@šH eÓH_|ÕØY´¡nwH¶f'œÜZƒ…$Õrö¼Ù2@ÑÛ†$ ZœÀ´É…w²½;ëUjF›¸i·nm@WŒ´I—ß7±V}Õ£| høelE¦ØS‚÷¿ Øëü?"ýróF$c¹½ÀN×RÏ_{#Ùs~ŸzyQb, ˜7ŠüUÎ_¬{Ë=Ö½Q,Á q¹›pû£À]m ¸6}­=Ö¾wÈdô+ÇïZ0_z£®úHß21ûr³i}¿ìr_„ÌÅAð€-ݶÐrˆé`W™l—­gB@Ô Þ¸W¦<h£ëb¤ÍzüýœqµÕk$[·XÒÊݯ¤K¬ää'KÕÔ ð¼µ4:) 9Eð®ê}ÈcóÐßgïN[y:FÜ`”ÎÁ€9x=œâž÷S³=ó*&ŸŽôøìÔf]Å[š¿8‚C~ ýœör|œ’¯X²oªfCËn|7ø¥Ïø¯ŸKŸS?gr1Å (ûH ™gH…ƒéó²ÎÌÑ—æœ.uך=ë"Ã[¹XîY{Nv€’lùãÆS€˜aò¿\4Õá’a(cQWxßx¨™©;mýW†Ûõa,Û.Æx-®ƒ‘5¨¹Ñ<ÍÒÝŸàI÷ÕF·8 Póf¾io·ˆ•=Ê5«>Ä›åÓ9Ì¥Tð_R/c"¬Ì$™Õx×.˜°Œë qÖô•oƒ_ĺ/9‚Ü1 ÖcªN3ßÔ\ƒ¿ÿveÑC|'õá("wAûÓçåZ#Híyçí™;gÚ^ŠU»™L[ØyûnV“‰¶iÐc³˜Ãÿ$%çø¨€mï “ÜYã¢,_0Úçô¡>L~¿î‘Ñì¦~­3&þ»`Úf»©}û5[öÿÌŒ[À«nE¶8|þ~=µ¹V/òýi¿ÍFÈýÚè.5 ÙžÐœË…›ÆÚ‚®öš¥ß0^ñm<!åzbÄ8Ãö‚ZqÄ—³Ö€Ó¦a™¡¯¾úÊtîìÀçwžoIøäÉ“m¬®]»š·ÞzËœp FVÃ.M:Õ=-xœÓÖ• qS»žzE±ˆhÿøà•=‚Åw vM%m F_Ê…†ä+:NÉþ´¢bØÓÑ9ƒ+ªeß©ÕWä–A›²¹Ô¥K3iÒ$÷2{Tÿ|íµ×Ìqgžiοõf ôÑGø•¯mõKwF‰dE,’Ë–e–Yƞ˿  bÌÄXM•CRâv™öÒk|! Ÿï € .•´<õ­Lž¸‘ÔŸËre· >M½:;ŽÉ¾È+¼6û…Ê/„|wsŒòsqRz˜Q>è»ÁA ˜Ž¥íÎT¤€‚%'›'‚ŠOHá´Su”\gª I•$¾-?©zîwêÙ©ò#Y‹@ú@ãÝž{€Ë­âz)ö8V¡Q`·'‘uµ!ÿÕ÷2 ß퉽±¢Î%x^ÍM§¥|‰—é:>t¿¨@û°¹YKM'ß°ÿR–s™¤‘ž6á±”ø ú¾°èNç˜ôóÑ÷›"”î}?äNG„[Ë·º>kùŧk™¬8V9´ Œæâ…^™)/×Ù=Íå)¿°†F˜‰6 …öƒiYßÕ³¦n†d¡Éõàî†5«Á‹×S©ºÌ ÂæÿKÛ²ÑCOvar`=_š4CêvcC¡O²ñ£¼šX<Rî mTNy’GÙ2fs©?œ÷z.sYË1¢Nª¥_éûÅ -ã®À -ÇX‹ŠÙ}„eÂ8ÜêÚ(íüHò‘/Ç…C4òþG§±’&ëƒ[õ.¾i öƒn•#qôUò/b©œË#æY‚ ñ‡ŒÏ옩l4¹þÑøFG~~‹eë4 .ñLÄ{ãÊ6üý±‚glÑd­äúˆ÷1+IV·WNrƒq…GÎËåëLŠË bŸ8²kj$À‹øm wD ™ñý<›²irS†·ƒ$_×ÝcŸG†=x_ÎÈuš8œqk*þæ÷÷JýmÞ@fo.Ò„Çnžq¬ÜçìNýÉ¢[ß HçðMÏŠø®Á¸&Å„áì“ ž˜wË 1\|p¤5¤ibºîŽœërÿËb¾/¹ýcGžZžnWnqKI'_Úàæ¢mÛ=~ŸwŠTNIÒãh{×ò._–“:"Íòf;d¾Û+zx_nœŠH4ÏË}èÇŒkåìMUé—ÊçÔ•~B ¿ &XÿŸZþ½ÐÆXàé²Ë.³þ?ï¹ç»aÛÁ;÷´©:vìh¦eäJwÇwØøk®¹¦Yk­µÌ;ï¼c-åŸT›»ÉÊ07늓fNÇ‘¯!‰£¥Pç·”ôM‰+Õléfx¾³_nSJ/í¢ ‹ÌSB¼r—K›ÏIüÜd“M줋ú”úâH,€în¾ùæ6Ko?•ů|ö>øàƒv¢EîU´±¢Ü:È_i d-ñß|3ÖÐl`­ðå·[¾¸ÕO‡ f^óÍHÏ&,ðO.âÐglôFÚP«©ÀdÔ®èg³Œ{~ ãR ¤Z=P°ædùåó± ”…ò­ÁX;4a)ºŽ"YIÞ*Kà ]ˆÒè%M#ž’±Q¸£Þ81žÂ"ùЛšld­ìÒÆZü.dvlŠ2(Ëøê+i§ãi¦»ÙÇ?¦PFfß/¾ÝØìÙxq+ ¸c6m×ú˜­hÆÍ’Ùð6¯+˜B‘Uàë´gʼn©Q|ÝÆð:#ᢊ à¹“^…ò³mÕ×%.Àиáò€}Ž&Œ/+øÈÈ› øu+¤f"ùê^+3‰ýˆ•©»Ý£o‡Ý©9•ê;Ñ̬ÚÖœ ‡s6u"J.Ø›þ¹i`H~á¦3bœ‘\¿°mŒÀÿBTÃý¨/züìdº÷óä”fm|…ñy(‚¡¿ƒO 0Õ©»<‘*q L+ÿ¨·Í=cEÞÅu›¶QbŠwßE?’ë„Q€x¢'ÜñÎ^9ÿ”þfêoϼeýÎýðý®ä¥ ¶7¿÷mü0_=¬8 ø<`¾ {=îÙ,פ‡ñþOØhO³zè;t‰3qåÐ@È™¼ÇÁ‰Ñ÷Èfs*å}z7@i—4&íâùÊ™ÑÝÞ–›-:äñÁç0Ž6íìù4e n¸Ø†nDYÜoúü„p §EªöÔ~ábytz\/m€ûL¦Žßòºª>~Ü;£ëÊâûÌu9‡æµÞ+Y|./÷Æë‘ÑN#X-b‘jk¸°T¬žŒVÒ¾±rmb$=vÅ&æ3W“ÃSý(Á”Ê•"±šY¸å)fŽQn+÷°æÏ {exÙŸÙM-Mâ¿´]M$J—›³Îí+Tw³4ïæmŒ"õìÙÓZöéÓÇœsÎ9fï½÷6ˆN=õTÓiÍ5l ´!”–£k³8SK/½´¹êª«ì½%–XÂ.-衇L=l˜|•6•Tæ°Í°ššos¦_•t<ÌV»¨Î-ªãÁZ¢3?PWêkL`p’ÕzlJÒ>Sãˆþ¾­¸#·“J†îc— VÄÖ­[GÆ[{íµ­«´÷ß¿·Í6ÛØ¾¦ o?•Uði§f'aî¼óNkÑÛ·o_# a‘6‰»à‚ Ì`¯Õ¯»î:£‰‘| +ÎNôÓŽÔ‹|s#Mb4¥=¯ÉRê(¸çŠî²Î\„_)¢åIÚ$,ŽUmTÊ —B±Š »l;*Ÿí)ßô‡!8©+Ï ”8–}Á´ºÖ†ak³K)uÌGõþ@î#€Œ7¡ìTó^^ÒòÉvX%zŸ±=Ö!^EÁ¯”÷cYö®n÷x’µ( ïë²jEƒt£6é(·H­y¯+ÿ0µ¡¼0*bv\ʆ,:ZêÄàýδ¼›Æ‹EÓB( !hÓwÓƒË.ë}ÔÛöy9L$ÿ‡Íe(Fõ¶üë`QuÏÞ./¦¥­iÓ²Ú¨‰)3õRH4ë Íà ”åÏЗø3`í€$–ë8½SàÛ»ôNõÏòÚè²Lä’Ø-ÃûšóÌòþ ¨(J²´«9ÞüQ¥^Æ ºÓˆô7?MÀ‹¢ÎÚ%ªÌ 1\©·,Ò8@¯Êú#ý¹cXÄëL8úËá^ Ab›Ê9î§)§`»|… !Ϥãs¸eA·TnÞÎq†.´“}P÷0Ç «W„7½I]ÆäáŒ#®›D•[î‡ÔzUóâÁ[`1­:§•BŒóÉ-xŸ9Ÿi]úœÆ»kCÐ0DëPï’Ü Å%ÛÏóMœÍ óSi Œ4q¸<2 Æ6ÉÏ Wý^µ}a;Ó¯~ ¿™•i'c±Û×Ó†wEžhä}Ÿ ´©%ÈãbÀãÓÉí-˦­?¬x™0å:“Ÿ+íçG½þs85ß Ðy<Í¥)û>”uéoç:ߎ«ãJ I×3QmæQó8ßàñÀ;¹±µrj+dvã= Ȫˬ֒<™+&ŠM^4Jö«$ŒSg¶™}yz ¸ŒEâ'úÍIBÇMô­dD• úå–|“‡ˆ vµ€šµØÿcðÌžæ8_~ÿkÖ'7{ægÀo>¢ÍK Q5ûó‚†äŽœ¾{.˜:tèP_¸.9$§¸Íš5ËœuÖYyqJ Ð*€W>½ ±›G]]õ¼ÔRá³:²$\tÑE»Ù”›NQµj¥Š-N¨|-Å¿ÿ±GÞiÍ_5°. á'V|Ÿ¿ÊVZžÚ 7(®TUUý´!¢×ÅIX [uÕUͦ›njoGõS¹j•®&RZH¢gaR?m×N"J>É_÷Ì™3ÍòÜ_—Þ]ãå›wöO?›™‹/j~m᤽ýTýp:ï®ÙóB®1äGü¼þ€^*Ä·UK e°KfƒYc.GÙ‚K Ùú.;p¥òó‡ëKT¡‹Žäóož®e•…hÞKP]%…·Â¤eº>À¨Ìü•Ïð\WT”ß¾ÏSËÈOV©ZÒX¸VÊÈ8›¤ f'FË·³!•>Q}h×z¾+¡T…̬«Ð›„…ŒSZÂk­Ï OKooF¡qëK#ë/Rh)ãYÉŽf³Ô¤±æ¯[Zž*‹ŸŠnVç©IÇQÏkðE§¡üÉ2]õˆ”ƒo_QŽÃŸH_˜UèØ$ÿdq}br130Uo¾®¹Ã̬?Ç «j0«evÖD‹ R·M]Î{/B9¢€%•=¤uùžÞ’«ýx·Ê.»÷="pAÛoù$Õv ¹)ñ“}¯e re/[¼ÀDÈ£TbÿÊæÛ¬¹é åÚX%õ“u›ÓV¸± ’&>dRð0@¬ €O*Õ,@ÅsØt±9Ƨ`¹Z±®dZËgÖ3é ´!¾aÝŦUú5ó2ï#i¤¼`ý“™·ÄŸµYÖœ&ùÝ¥õ…€QÇòÐ ‹6­”ðµä+ŒW¥~÷µU—¡Ö¢xGØ“oÞÙÞª‡0ÃR†‡Éj´?®B6ÆjÜT½1eeÞ…oz—µ¦úÆ{+ü€@$á8íEn:–§M¼ÑòÅ[žCö;ž¨M\MUoò\l¼ŠcU]Á ê¸ñâˆ%'öAÆERH=RbÂÿ'Ñ{Ò/O§_À ×Î'/]Ã7ÝŒþô)åõ¶ðr‹¿ùµ©Lÿ,·óyº–” +)†À ïµW¢N¨‹×Ú絪'ô•ü0”ñc/x´«#ÈÿïÆ® i‹±8ÿÛÙ³bÿ´²*L–-œ®3“'i{ŒÎÀRv­RãÌü&Ù Nô•¬vϽ0¶8šñS†AÐWÏÍ÷Ú§á3zöy¼ÿå‘\…HY7û®­LÇä‘Ä[!/®𻹳õ1æ”IJör þkGÈ.è«zÿ $6”qôB¾Óð»h¾ð©ãµµÓ(Ð×-Ãm¬¦ãÀÕA_%²«m&è¬2T…ܘÜÐæµ72¡~ ÈSrÇ0{/j=óøi¢;6"`»=*SЪýÑ©Î!¯œ|èϸÏÚ— %ýÁ ®²5 ùG~ìŸï% AÙ\ D×ýtø‹¦Þ­ PÿÏîuZeÓb1GA*4+íZ_ÎKÇeªj¬Eè¼T¦J—e|Uºäwü «Ôr,†emÙõ™œÆóf=7•±Ò-åùÓ«’¡é‚#-è›QJ·üëjÂdN“Ê3‘t²+Àd ðwbålQ 1©Úl¨ ÌWÜr(ÅI9$S;GÑÉ<ù>Y£b;ávf}Wky×¼Iò׿ßù»’åt¾¤6…q$¦û öÈ=$y×»`Ù˜µRÍÝtÎd¹a?;ƒš¹“~%À&K(CU}²W…Nµ©£ùŽMØ®GÙ8,¹ÑsÀ™Ò®U öK0¤¿ËŸ$ý%%>y\X46¥YÄú+ ½9/&7å[¬cK¤Íez£ØÅ%íÌ^àk…ÒNåæx‚ÉÎ@ñ혪ö½IjŠùÛºmø:›|/âå@ŒÀƯf „êäšÙ4a'«ãÆE0‹&§ÎFq}+íO°0{ˆï}ŸwY}XâLØÓð¤«éK.Iý[ Þ'K/Å7‹À¬ê[}ý Ž9)ýt3æN?aƒ(wƒµ.¼óá¼ã†X«Å^B«tŒ€UÝx|ÁGíù¾«’ß_}ѼIø[”RWð›í]¹ƹ.NüĶ=C8oÛŽYÖúóðçóª‚ôŽÃ-‚wüׯÖ|ßò5Wñß(p¥U6êV~HìX ¦ÿ––ÂwØu'ú{Jööo–&Ç’·¶LÞœ<ô«´+[­ï§Ý0&ʯ¥(õ©gŒ‘ÌDû© iJLîÖÁå’[?R÷áBùd'­»—¡ÝPn&4¶ÉÈtõðÜÒÆ@¶\þÊOþè Rúp¹‹‰ò­/š\u9{ ´%Ü}µ‚I¾x¥^hÂL>{‹õ^mô¶;qD–¬¥m*¾Üw]\Ò¬Nëîfeqp?-Îå~ð£ÑøÖÝ2õƒ·Å›Oe¥Ç¾„g© «Äšk¸ /‰Êy‚åiÙ%ž ?×ÁëÙ£@ï·´‘pÝ&jìû/“ÙíRÓÌÜáN¤ž¨ïù6£VcjgÓwË%yŸu¡t-M{®åÁæ˜Did®ý.,Ç`óµ%p]µ˜X:á¤Ix­ZpÛ½K+¥zå„îÝò² —«¤ ¥ð˜Ô}|ƒË~¨jQr½|+Ûmèͪ– ,Ü»íI?µ¯0Ò$\o› ‹ô +OwœkU‘ XÝ›qâãÊ¡ñ tªëÉK=6„ëJ\ëÁHaÍ…hï¿=RO»y}$üº8Â2Š÷‘4èT’–\rIÓµkW3fÌóÇDf- o^£)Îæµb•TžÖÄ^á§)þ]ƒ”xXj­H™Õ¯©õYêsƒe^{Á_mH'ehZvî߉-зK—.F>~›ê¯Zö•â»YŒ×÷ŸT濺ôD[¸…ù¿xï+El\1ž8ê÷q!²‹]ÚÁêN˜}„%7r¨3[¸"ÿÆæ*ÒF‘Ê^*É×[–| e64ö‰–Ë5ué_Ôú"ìþfë,œßÊ"ð¾ÉåX”Dɤ¶Bà<& …L>ãžrÂÓ“9²1ʃ¬S»T2Ä…Zx3:…J}ðZ<-kŸždî  ôhËA[ÞÆÈªóp,ÿZÔÜžXPtVÖO˜<–nZšw6JÞ!€Ì›Ë) ZfsÚú¨4`Ÿùro„ K`÷´ ›ë@Ƈkd@ð°:,½9Và@2Ú ’Õ•Yüˆ¤öIV^c¹Ÿ$&½3á|æm="Ë÷ºPÊZ[Ò·y'gᮽ¤¹2+úÝ5©“i›™”óȺ¶üÆFð­‡Ï|RwM+0#býÁùY¤Ÿãùüòh9B¾Ë µI¬£¬§™d#6Á÷·IÊÒ÷ÿò™v%ïq”·ÙJÂtH>Ù½ýÝ w=àºk0 å;Tvv¦þV¢Or“øŽ‚ä4å}­ø‡[~ ÐÓêØùb|éKuµ!kTüJ†kC—&SšºŸu!BÛl›Íø’BÁsIrÕÝø^{¸!±½íÍÛlð)^F^ð«7À™\Õà÷’'J_Kù(Wc_O|ãY=¹žDëMšÛð,WNQã®'¡ïT2Û@ê°ßµN´-ü¯i®ÿH4R_q îÛ·t0'ÿõ¯éF3|ƒµ(KÞ¯˜4ôÕq­UpÏ1ÙMIpÔ’ÿiëFål€Ü5‚“ÀèsÛ ÏœÂÊw\¼}<î¼2·I¤m,nóã_uFNÔXMJ+MlZ6Šü:¤ÎúT–L“!µî¸Ü¿ #è¿„6óWÄs!á“Èu‹¢xÆ;ôMP»rödZÕ½³v5dǦàÓ?4CëÍG6ÍŽWUG#_v£¿Ìýø-rEêágµü"W–Tß’Qç‰ÇnŠ  –ð0Ïþ)3…«6ue¦NuFj›c2a÷„Í+5Öj†Šã F«îæûgù›?~yÍÌ\}í†,8ÎÑ@Ö[ˆ¤©…Ž^_™Ã3ж›Q¥}üºùVúèõZJÞr=1}út3nÜ83qâD3{öì,€Ö‚Œêø…-×2m„ãõmZÊsÄ]Pqj@à¯6;ô’@Þe–YƬ±Æð•ok/èåã×ÍC~nåy­ˆ …=ô(PwQú€ˆ^aUyF |ʨÜ~Ú¿%÷‘—o¹7$R£[ G臑üoÖRR0çEzo3¬'¢H–²šý'“T’•hcre1’qéT”ž |/Y»jsNÔ<,DZã·­°Ý™xSø¹¤|\Æ sòýõ6Mxàp\²Úõ^¾`Gx÷™:vä7‹Î°¦ñNGÛ¸¬6êYò}•¨¤_²;Ë^‹àö-£U¡Ûˆ½†e‘Y‹tòÑò?ù’ý7 X!‹•é®´Õ<ÁЛH~Se=£Õ~_gtã Mb?Êð"% û‚NÕîÞâNBé~Í£àÊÒY¶9`Ôû‚îy¾˜%Ïâ© ˜WuÀɽ$ÿQšü:Ö·‰Ôž(ÚÏGpkµñ#°èÚxù9ÖO H°¹= €r;Ö›Cå÷2u£Ù“oÙ†¾ô#ïs »Æïβîn¸3¹.…ðF‰ÿ`ÁÇ÷šµ1wWä÷Y^¬×é“ýxäo;œàÆ­F‘>1A»ð|š+t]0Ǩä-x@4¯m®§çç»,^£>affSnÏ¿Ýl!+Ñ—¦_"Å$³îW?‚¾¼)m°p¿°°[èÆ3´ÕÑävEäDb'ž65fÉæíhr'ò.@’—¯D—)«úø}HK³´Bæ{½™ ‰sÒ‹ëç[ßmnšu«^*šd ø…\Èø6hK ˆ¶¼üÒÿP4R#È:u$@é§1ÇÅØù'6"j5õøvì$¥F¿~ˆ¶üR§&Ÿ8D›D^p µŒ±ÚXú,€¶/ð=„vð"àçàÞ®qÈ;ÖÇ+Z«Ü^”É€‹²Î=?IˆV¥¸¤^e˜xœª•%vrªÞº¡ÎØw+Hú5Äơõ´?æÝ¿à½£\㨥›Y]Pˆä#¸‘d¡ø¾{r«ÃlÝjønÌ… µÃšÞÔ÷U”'>¿­DIå¢C6ë[@ j`A üÿ¨|$óÞ^EЄעŠü*ú¼ºZÜÝ2¯ì;üÕϵ°ÔQ×Ëá¿tRFñÓµ—$øŠ‰Öyÿ¡çòøÃ‚Á"Ö×Å{5‚ã»6V‚"‘¼€®ÎõÓ&m"ݰ`6…úi‘Ãç냹8׈’fm–]}À²3/•šÒ¶¤–ZÊó•..ð«¸qh„òo<µ¼½¸!«‹-‰/ÿmsŠ´‡ `zŸ¹)µ·=`¦wó-Iàe=áM_éóÈA¥Ò"?ù|{Ei„õX[˪e”2×–o! µÑ<…íI(O9ËØ™„D0‹f×?ž¬Šµ!Ȉ¼ž¡Þâ´QYj†=WÖº²ÔôŽÛ*·Æ§`‹•µ‰hž­ÉLùKtI“;*WÐɽ?oÛQ¬Ÿ"ŠFkÕòþºó¸_X³ÀVbK*q"qõ%9/,wÔ<sXN ¦¨ˆ×X„ªq^«‡ä*¡áΧ¸!yGù‚üšM—6aùuŸ’-‰“ˆs#>oÂR®Ö~um4× kê­ÌW‰Žž²8Xž|×ÃÆY­0Ýfl,‘:;1»pàžÀÛÙ'S¯9Á™ÿnM —"î?½+±ÇøÒ•s¡¶?w‹†ëËÉUiÖà÷E¹‰›!ÝÊäùu3äK–UgÑd!"P¨|’E଒øÇ"/NnC™pxp÷öÈ£vþß6ª×ÆÉÛ‰“ã•ñÓT6¦¬Ÿµ‡È¼Ï!Yßø÷ uàòý(þ j"ÜŘÔMþð²®–2Kà.ä·ÔRÏKm¾¬—)+Qy`ªó(íÑ ë÷+3’!§2^oyaZ­q×…0à[ËB«ÏDâײ¾•[]ÉI+ÃVgØÈö€lÕAŒ'ý¹ú2\ð ~Ûúyg²-ý–Ѧ¦Ãª–ó>›6ô¦IÔ̦Ö>më›}Òmà ™ÕVܯ•‚†lLÿÉÒÜ_Y¡œÕ:ÚHmkêñ8,‚e{Û—±ãôŠ|#†¥Xö?Êü2®Æ™;ÃWÇå…d—ÕyþÊWù±ÀÿžÍsµßô@¾éd÷›6OI|¹j£àª½‘I®&8Z&ñ¥Yp± ÔÀ<_Ò[‹’Wy ‹Ü€5,¿y!Ìפ àu5×µDôU™£Åæ~m"&Ÿª…áªÊ–Bî¡"ž(­q–ëW¶DóNnÁ‰¥‚šÅÞÆmŸn<ìun¸÷X¬ŸÊ20.Iú™6Њ¶×‚sµ=µGÍB@³ðR>œ¤¸+Ú‡¬‚µlôLjöD’9B«d·J'T‚ИÿñÛ£Ü"›%X&¬9Ç( ôÕõîKÀO/]WÄ¢Õ·Òç²rý‹2iÙ¹v?«ÜR¬¨£€È`9LNdšm„zW«a(6åë`´‚×ê«­ùEÁ\a‰w µþ Åî–:©Íÿ7-õÎ`G™°›–n‘c“ã‹n¦k+Œ@8 ñôÊ»¾Ï»îÇÝñ>ÐW›Ÿh3Qߪ^æÎÆ~V¡´ü›•9ùõvßh_Àj)tð®ÀÚÓµBrã¯G?•ÏØ—=KúÔÊš ú&vG¡Ø†q‚û¨æ;&W[\ ;êñ ê«n¯ˆ{þ`˪ƒ™‡á=doæl¥üq½W=.2¿¤Æ’Û%F„(ð¸XþòT§+4œ†vß… ”òÌäšf¥™°™ÏbfG|”êÛ Ì_‚M—‚mL€ÃZãEl¤³–ÖW&Ž1C’¿›ÚFÞ=C×–Ãkg'& §º™£ø÷/lªsýŒî€¾Nî꯲Êàb– ·§‡¬’¨Ç-D­“bÖ6²^EtBìuò}}arq³+VÉá4ÆŒ…™üEšj)¿r~‹ÿ~ÈÕŠ<ãiʦ冕ŸÜš×°ÒxZHuE%4V/}?æi5÷3ÑPxäÌÏì.Ò„ÉŠjçM¦d/øL·ŒåbüÜV¡mÉíI4ˆÕAggÚéýdª 0 e|ÒØ0 «ÉK±š nÎ긯(œ‡ïn€]“hy,@;!»¼C.¿ú^ߤܚ–x’çˉMË3~jã˜$-ËÞQ«zÂrÔ7]˜vâº<ÑdÅ5øé}ëä 3ž_ÄÊš‰ô ¹ÅñtÀM¯.ð$a.ÉÊWàïÈÔš0;8¹+q[˜©ÇÝ(|·ÅhÓ«Òö-{vft™2³yÔÚ|dY¾uãt3;ñ‡™Òè—ƒŸ¶N¿I<»›\¶™eþmM-ÈÏð+YÈ{¯Ø¹Œ'îf’Ðíñçyä Ú%Švb¼þ‚ÉþÏÍ86ÁUËѤå‹ô­kRX?¹þ4áW;Á~K®dÞj8/çIkóžh½Ï¨ã"¨èi©›ô-Öo½ùUò\¾.Ý%Ê7ÃOÆ !Š~åÆU+¨8s2\–ó =M=¾©›«Ç—èËrã=„ø8¤¥ŸÙå€vùy@ÒêC²CÑ €ÕsQVnAÈr)z4 öB“ˆ¶ˆy¥L o®%Ÿ£°Ú-‡´lñx+d;©eý«ñéÅ€fïÎ8 X§J }¥¢„ÓÒôLnè+[£|’kŒc3BåóÔõÅ€ÚtÅ+ÞK‰}ƒ÷ŒVÂòó‚Òf_)®R}]ô} w5ýòïWIØÒþp þ³wð‡…^€-,¡Tö_¨¾ÌÝ}Cciã¥äѾ[[ Ö®Ÿ˜‰þt áÈà‚¾úƃùÂ5Góý4™q Já9Õ›p§ w–ý>Cˆ#Kk¹{ › Û¿ÁZYò}ï,¾eÿÆgÌß©—|åÐÅYðµãió^ð|aò{Ò)kÂÿ òUZ\iþNödRC\ߥŸŒ6IÒ¤È .B~ýRð€°¶ë&÷5V44~Â,Ì5œ¯í»ë¿pÊ®I°[ ß¡|•£êÊe5¿ä$± æ Ÿ:‹ŸüÃö/q­›Ú±€¯&O$EÕu ¾ß08êndx&õN¤FRà´zOn,Ä$É8òðýqì>áÏø`¤­› éeUÃwŽ÷'ŠºJRöšÞÜØ™ÛTs#|ñ¬¹V Ya»ê6¡œ5¼hÿf´|~ýrŽ+ h²b[Àȃ[·fòíW®gòë_íWÕª5H!¬åE›"sn_mQµ…Ù¬jkTõuûSä÷üáWרq¸gªOÀÒ•ø_ ÉIø×¤ñ^$P8>ß¶Iì¿´î]Ë}ÝôŠòÝì}x÷!6ª FO¤ŽÏƸ`XÌi'U'%ÒæxO!Ò¸8¼ªƒ‘žH“«ò×ÜT’…¸ô¥y ÷~•ôp&Íw2«› GžswÿíþŸ( êWE=`þ"É6ç0A4?Ð~ð4É®ÅHäîBßñjvÚgdòìnzJ±4 v ×¥Fœ´Ú„Lß*´1ÇÉ F)*j/^2F²f´ü­ääûçýósß w6$Ÿ™Ÿ6,D iy¾}mDŸÌ¥©6ýÙ Áä°\g^¾å»ãâx§W$P ^ȇö"2IÊoXD„è`Yœ¸¬À;YœHÀ .û”¸)ž»LÑœwï$¶§Çóõ'ýúRU °XQ²ÃZ—Öà‹šÆÊçÑÔLs2Jš¬jei)…à#”¹]°®ü2”“HˆüÁ—÷¢¤6ãMr® ŸÅbÈë_7$Z“ƒºðîµ¼«|j·q—´Û³€ŠB>€Ý¸•?Â’§°^‘5ài²÷úïW_úƒ° þpßbÛÀ­ì%y.Lº¿—'$úg£ûNVäê{~Ô¾ÏI®a–NÝ—]ΛZu)à¦Ê7%¤©ù‚œÕº¶B~¼ 5¼åX¯C|è{?ËâŸh(äŸZ.>–€SìÀ÷kOOòûXå ÕQG½Éñ'ekI{8܉µÔ£!Šð¡´‹Ý’wMÿâFÏ̱rE¿éI¿i:ˆ×–üÎ>#ïÄ‚ó„þ>©‘?n€@³ªO›âO£ï*üÆÇ~7U’ºzÀ¯´qW!KÝmi[0Æ^Âä„Ú©”¾n´™\KŒýØfXŵ÷ÑIó å_  /­KŸ|6ó¾Þã¤UÁÐÒWÄ«C)y2Á¿Âän'Ob<Çïå\˜=Ódz/@Ú\£ ×KìM hés­$zð–´ÍÏøâ¿G”â6&€§ ØÕþZñµ mÿ)x~н€úÄ!€½ÿM¶¶¾Ø·AÞ‡´+à–ànÜì´‚Çï²ùŽdAíÃW´´(;f¤)×_E7SifšóȸŠzÐ3Ž+£(ÏûðvWΔ,yezë»·y¹Z8ÛÓ¢I~¿ Þ¯fY³jbc@äWÍ&Xd|2'Œ™#f°„ÚÏñWt!€~qê5¸1 Ü£ýÅ»¯äוÁúðM»òœBç—,–_-ÂaÂSÎÙPµñ÷A!²Áœ-Iîi70¦In¹‰ê¯F~“i¯^w%¹BÎ’§(þÙ/äæ‚ °¾ª£Ÿ@&õº ‹«ñkQâ{WThŒÕž!;";|~p-Æ4.ï Ë#^˜Æ5=­6^ôÿ§±T÷½ÃÞ}¥•V²Áß|ó ]½¯Û¯»îºaIš%ìØN9¤á+»ƒgDR¶4El…ukAxD È—mSëM÷ï%~w-*RF9v°Ê¥Íû +±³›ç# €ý=ÓäN¤1s.AJ}G$ßu…&WœX¥ýÿ-ó=_`§û,.)óÉGOäW½|Ë¿øÕÒDùÖ¶_ áO(X”Žd£DœôÇ?ì@„vY\œÀòV çq|ƒ·þÛ“æÇ­°Dê=QæÿÓjÀ@4[‚¹¤ ÏæBþÛr„@×ò4ósA™3¯“ìîÐ$O07ŒPÙ/æ¨ù]ƒ+Š3"_¥Îu·žßÛ(3Ÿúž•KÚä½È;ö+‹$¦,ÆÏ[ÂZ¸ÙQ´ƒ¡”MﯲUšôÌo«:š~Ôùµ,Q}nvæ!ïðü÷T¦¿!Äo} K˜(¸CÐÑlfó!95Iˆ+¸áΟ”ë³LI=‡Ô+\L·Á96‚Œ3«ƒ¬줽äÜäYq2ß8±3ç„y§Z’Çñ\©~ÞBípiî½Åñk3«¬qôS¯Ë îçߟ,ÿ]ØlƄٛ|U)û.̬'ë[/K9‡Ô?dF$g²Ü|Sx°|Î45锹-‚’Ì®Px<¥àãù/ò üÒ`KkZ[ÎWíO›“‰©‰Yì Ìвãi·Ñ2µ`×D@An”Uª õ³o™0{“b’üôä"æRÆ¢[]az5¾ˆOÞFëÒb8%lNpûàÒ×¸ÏøšrFÑQ€²›1©4œ·éB™è—ÜÝœ‚äØƒ±öyÂÛ®qM¤±9J†qb8ÿåCÞ¥·yס|ø=‡qDþ¸å/NéÒBœœ‚ÜùB¶õt Dü67¶ºq P³©îMêS!ýžÐæ"•ú<ʹî¾^ä½Ãù`œ§·%RåøºÜ<ý–H!ã*‘&׿ˆS8ÚK•™]µ/µ–~#Všù7Òr]ý¨éýV}éS&´2P²@!ŸtûžO=keé†LÐÙàÚ˜$¤R¿¾Ò´üæ¡Ô‰}àó_'4¹P’É‹R|ñ¦hVóE„jJ)kßTz ¬ æ9((€U²kÔ\j:»tsš÷ògíŠ=²¾vù€ÂÔ?4ƒ×œ4gº pøsƆ—ú C¿ß/fFÖWÞø˜‘ýÑvÊ,“0~rÚ`&;(”jFõâ÷þÜæÁ«ª3)”€”Ôp:‚× ND›® BAk³.௟&Eúl•xܫࡠIøÙ½Èϧ.µ¤*(j¿/¡(¹?ñÖÉÆ­Ä‰f¶7ˆSú–Ý Xê’–@e‰¡ÞZ¿)€³Ê*Mjmm§F¤ÍñlVÌœMÌÜ^ï<] êp¬;Ý¥¡Á2‰7ŒDÉÒ2×¹I[ LîŠ2h¼YúžÎ!ËOâímüAºª>À*Ӿɕ Ë}§ì½ºcÁ¬Îⲋ ú¼ú6Àe²·ƒ'£–å¢@Àè¾­€4—Z'60לgºÓ:_ݨædž¹‘½}oVQtcçŽ ¶Æ–¬#yµËÛ¶…i/&Ž_0N'ÄÚõrTÌ„9Eïh€i`#ó mP~Iå³÷=Ê<å;Šž¥'é‹Ëz~ÿäÂf'ÆÌ‰J?€É­¾w*ÊÃÅWÇ¢¨8ahÐzÑ–Bg¹e:'Ï¥pŒoà=£È$ǯ GõÜp?/Ð Ôïiò×›¤½¨žÊ¡ô³Ì²þhgÊöBÚÈÅð,/ÝMšb€Èã(ˆ®ú ·¤îîr󩹄6x‚7Ëf9ׯüŠQg" pÚÞ#ûKSì¾ %ŠE*t?ù_*íbˆ ¡ähËßó{¾HÄ Þ– žV¯áE2m`œO7]-ò ÊÜf#½ÓÙ×aE|ß Oަvøe?Òì˜Þˆ¸SM{|± L•LùUf<|G»5>iNHmåá’÷.Áå’x~äó]­~Z…þrA€j¯òß1rU6½h½Ê37c\í@»BûÞ™DnZDA*÷hƪÓp¥ —0ïÍÜÉ•ú‚I›3°¸u"Â^”]–£áÔ%<8:yL~ÜÇS…è dçãY½µï.×IK'Öbrá,s’“ÂÃÂùaVÌ„Œô…²õÝ“‘/Iú¢.4"jЖ£½¨‹Õ±˜vˆÑºÅíðíãr|gèÓ”½òãï!¡âç×0Ñ}=¿¿CcÄLÀ‡4ɓ؉¥I›­|x £ZKä§äá{ƒQ´mMš•Kâ —Ó¾NCçŸ\@Q5Ÿ¨9ž¿]T„’Â…ÒõKQÈr.ÌâWlL"pùM ¬s'LlZÃTþ› Ø$«¬ÁÔµ#•ÆüÑ‚™/QÞ¿QZ?uûròD@^ª ——ROrõ7„8/s=ÙÆÕ¿¨£Aƒ°œž˜¹‡Rž~ŸóüZØo{®VA¸ƒû7£ÔƒXÒòÏ^x+a±>1ý±ù„6°WêEó¤|"GT|}ÃÎÔÕFXæ¬HÙEñYûîù‰!h8u0Ãg†âübòGs!ø£|û¥ÈÏu•5žk­Œßö- ^®¯:˜wMÜ_i£iSWaY?ŒpMʸ¤v}/ñ‚áî}ÿ‘ÖÖ0‚ ·~/Z¾Ä÷˜J€-8§’wáÜlŸ¬%ù´L|„í|îÍKàö3Ôåx¾“,W"©úšáúTÔ[‘Qšzc8õ;TÍñ(°€×Eaب'z¿’G°ÃbüÜÖ,kþ×ùý•EÌpù;mÅòtY¾›ÔGÔÏ;¤‡)´\ ~ßd¿ÅÊðñÈ÷Ól†šdù‰㵈ÌÞôœüÉù“ð5mœŸP¬-´^3·À[·†·Ê¿,Òꋆ¡$1&¥¿ÎÄe’4@â†ù_9©¬K¾ú¤å}a'S§\îîÉgNœÚÕ'_Z®¢qN¸šh™_ÔD›Ú”&O-¯hÄæ”~þ0r.M[ ZÄv2µøEwj/x­—¾¤­…ÕÈnŒw2fhï„_Ü Ƙê½èëéï7 å{C¯Ñ¶´j,Øj%ã eòn8}Mn+Ê[E¹Õ÷~WÂà,=øúÁ»v”Ñ;þjË ž÷&aÞ–~12ôVŒA#ÕŸ3t7õtIÕ’fÑD’%á3m.î=“ØŽ ½¼ƒäx›ÖÔÕä7…ÚèN\¾éýT–é¼ÝÇ”í= bÓð<ÜäóÌûPT¿íŽû…2«Ø=FæÔ«äö¹/GñžAÔ…¾ƒåªé¶|Ãqœä {Ÿ±ÏÊ&a÷l„fý'ãw’Öyí.ÑS•Wí¡-N„…y‡w9f[¯MD¨¹¹s5ÚÆ{þ–aïþ·,·ÕÊ‹ð™Â™¨dæV&–ä]n£ÿ“èt Nd€\²8}¾“i`ß`››ïM©"yÐÜ,×¼õlÚf ŸM±üý¨œrV øUŒÃÊ)äÜHã ¹sãÙóó3ê|$€PT¦¨ëËk^¾¨¡pAa«”òv¦žZ hsORJž¨Ç€™ÕˆjñK&ßBC©ØeYòû õê,¡ƒP¦ÜåªPº¨rk²¨yº.%Ââ©íhYš#(åbTøÕé0ÐWOû†{Ÿñ¾¥Ššq}k·‡³*s®ìÍ}ÖÔöè/b¸–¬¥†øƒã\Y°#§¸I.A( *Eî={´ €€»ÒéD”k)·ù`ŒÔ q¨üþ  <Ž LZ"ï(Ríp;p.¥$Ÿ´ÉÇBä3!dÔ{–o^ÇsäòæA,D?‚ûÝ0³3À¯vøžHÓ‡¥¤ãPÅ\÷ZÒèd˜†Âò=­¿5íéü^_¶¨6Ü8AûiÊâ«[¾"}z*e~%íà=Qºªèœz¦–s{Éýš§`aäº ðƱç‰&ó Ž y1óþG}ôÅýÄØŽšÄŠÜŸ'^Àz€„7£\ö3#±pAyo§'»ï`w}–•gú¨œ“LA)w71¼E¯{¢ÑiKB.S}¹™•ú·?Ú¥«Þqq{F€óª’æÆÆQæ"ò — …è”–Ö¸€K¯?¾ 0õ5Òxóõ¦_ÐB\rÙ pË!ªc±¬š‘¤(¨<ÆO«ð‹[.“ ˜Ñ¼I¸,˜M’œ®å§ÀMè9î½$KÙZòó…'þÍŒû­|«§‰l3?y’ÓñGi'”’»vž@šAX–-nƶÆùâ~í‰ï9­¾ŽöD›( Â2z¶¤ø¯÷‡žœ(aš÷n°Ë¦§{oÏÓ¨—vR@åjrZ _z(ô /Ûùð‚}ù=“i‹úž^À'Fn9ðx!®4œœÓóå«v[lì5髱_³d^õº0’Ö6”ñRúƒ&„ƒ¤±.øûv†U±\h¹ã«êâiÆjù­‘ö’â=,°î—®¦ŸM~’Tü®€œ’´á°3|a2åð–NÏиéPîíd•%ßg"ÏуO¢²†ßx¾WZ+mNÜJîÆ¬Á… Ï/ó´ü/š_„©|ßçó­E Ý9ZÆb#²-&?‡üÎÈ ÷“/¼Q.nš@:‘6!oEʱMÈn&•Æüü#èƒY˜c^}³åZp£¦'¿ÅH˜¥bÀo^Î Ô5PÃOƒ·‡õøêEÀß?Ü ´uEŽRßIÖ²M}QSaó~ÒõôÈ…×¼+°ùs’ßIÇ¢÷gòûðf*;\ëºú …,ÈD9Ð _j9EÇÞ¶ÿ‚ï¦ÀJ¿¹'åŸI´ö‹¥ùqÂB$Í ÐWey ‘a2Aáù)↴ÁêÄ]µ!‰ª{s‰Êæv@ÑhƒåA·éØÐxn~Ýi¿êA¿s‚%Ne©ûH¿ÑUí­<)NRÝŸãúΩÁ¾º•%­WyÓ{$)ôõör}Å',7vßÂ9Ka-XoUÈ€Ÿãü)ââ¹ ¯&1æçoO‰pÝ“wÍe(vŸHWu9Gõí1ž™Ó$àLÕþÄ•2áÐÇ<[Ë^“Å8 ´|ëB¤zìë¥0ÞɆÖ›ý9¦ÙÌf&`fèQ)Ð’G’tqh“<šMˆ úêæW”ë8dN%Ï%™€%û«©i7ùw?@ÑïµïVXA> xåmÊAн—?Kù½›eŠ"|ÍûÉÿ·ÞFå #>£pß°¼¼Ñ¢˜Õ£›‡ÚãElþs*Ë}¸$žø;Š…¬u¼Ï”kˆe €fª®0¾Ëð'÷^Éoº–o½¡!ç´ª¬õ¢¿Õ»‘õn}mä/ù¯6“´,¶æ"žÿ ¢ÛÝi57¯P‚óê+æsbF“?ДÃí%òã¼&¼Rü!­F{¸ŸÉ°7É#X“R@ Á=é'Òö8j¨ÚƒÈR~Ƈ>V–é[½ϸçLÈQï È‹¯UD›W’ac»úÖUX+>^ þMõ¥½ð§Ô Nþ¸0ômËsòžèø©åÿ!qmPõÕð¾uxçaQ1Jö}VãœÎË!ÉôO#_hÌÌ÷nNKpÂhÒ&e%» u^¶Å³ûˆ’]ùNûP¦q¤\:Ýšãç%çâ&Ð$×`Ž5¸ôIÿ{ç'E‘ýñšÙ@0 L€ñ0Ç3‹ Ã߀§žY1+æì™ó™s83fT̨˜1aÆ¢˜X‘(HPIËÎÌÿû«™žéééžéI»‹òö3ÛÝ^UWWxõ«W¯:š·YDòÖk¶ ©š­ƒR5ò9 ÑòS=P¼`¬Ìxåg™,8þì9Æ,FÏPÛ©"Î,–*Ž7?;ÓŽN„ß ü¼ß]»îKßtåBõÿ&xI¡ã>âä§•ðžj¦³@½cËËôÎbŠÞY6À½ùIò·Úø¹-P£Â ø]Êø&;¢Z|tS¯È®f`›3Ì[±hë‚ÓsÇGwø÷*Ä8úÔ¡0cÜ-“T«´Hý:ã‹ä–ð¤zÃŒ×î„Ò¢ué$¹EÊ5ÐW%¢þÇ­@Rz)•sgÖ¦o° ¸å³›o8¨-Ô{VþeP”©9šö¡Yg®ì£ô4/ÌîQå õîÝÛ^_}õU{uÿëׯŸûqÁý‚(©èºé JÕJJ°@¤.v²ZÜT€e³y˶²lò†&mekBêÛüÓlÄw|%ì;Â\Ú“m îÆöbÑgjå“jD¸÷¸ýöÛs’Ë×oå.ÃA *¹ä¿2µHÔU˜´Îc2„¨nêE™h×ÉIÅ­Ì{ÝJµTqÖa^^s²y üŽÄÍfL8dM´¢'2êµÆþÈî}&Õ[/¬ÈÆnc ¸6Çj³vt7ÚüC6ÂEäá@¶E©Çf‰¸,j'íñõÔ¾ ÅM:Õ\Ú—˜³ÔÁÜßþSóȬMÜ55ÍO‘ÈhAM Ð>©ƒÖôRr"Ô=a§/ Ø6êÓEÄh«ä€ÑƒM—Ú]ÍøØIæáø,sk+°Ì h¼S|J–(—à€´oÑb~“ßqöà¹ü© l¸œoù “­B'1[NµwPÖo™5O29Ê?V8)_ƒVyÆÒ£‚òä½cû‡Í´YûÒN_v¢\i+mž§"‰a¼Î]pЈ5Æëáy^›¶¸$|_÷¸7ócôXÚ&ãq}ƒ¥5Œö Ü¯âISS‡º‘ßuÈï³I‡è¡Ü3ñå ¨æ¤Ã«~¥·x!d¥-ãÇÐß\ïhâº2{7uQ Èÿ±“”ŒÇUÔ¡ä ÙsÔäijÆ+ïÝÂø º‚ÝÉßÍ€Fa¨#¶¡Î²Ò¿°1Þ¬$Åú+™¯¡O~Ñ7é$bãëµÀqA T·Z²#ò{³é>”_¸ê¸…<ýÒ}{2‰‘íÐ(5þ›7‡ocÊGÓ鵆ò¶ç^ ïHî{âVÊÉ©e+;ÀZ¥ÒöùÖ@R®Ú47ÎNÌ*—£õ?+£êäæ—⽂rvG¥@_¹²*Øx~¶wÖÓ|&.7îÉtefÎ¥ôÖ¥÷×DklV©xdz0`éZÚ¹±­÷6Þ@zŽ ´É?ìnè[˜‡ØªÉÙƒ¼­¦í€ÏaLÖn@Sgs úªÍmD7N¿PÇ–ç½>áÚ-új²¹ñ>ðÑðqƒŠKî\ㇳwZóê¥â™"R׃…£eùõ6Ü3P7R+Óú…¤y°Ñœÿ?+ÐÈì€lØNv'8i|óy}ݮս?h&4 7hH.fÎFp¼2ÀË&ñ-ñ?,p ƒrÔê@­:À„ÕùŽÒ¾ ¢e#{š±uûóíÇ™ÑM›cîň•Wà]ÀOirØYè+ûÐÚÑsÀÞoäñv€¶3ñòÖpûi3UKà 7“‡ÏÃN†©9šwZÌìøüq¤­]ÍtjzÝ\ Àðåy‹/P¸|šøUiÊ¢íª±W^‡óÌ/M‹ñ>›óŒTÏ»Í}…ûÙ|ÏûÓ!šóæÞ"§Z=Ð×Ȅˆ—´È45%µSêN*}§`%,mL½¯fq31ö«¹écRÖD¦Ù‹…ç`®Ü1äÛ°ìK ×za*Ù]Í&‘‚óœ\$g¡¥öê÷c´é;•±3æ2» :’N O‰çh3C‰¨¾ }º&ÃsJ`ä‰OŽó׬G¥r<§¶múJ·Iœ”á0Æg?sQôzþ / ¨¦–~mÞ ñEVZê£G0v‘è«°·Ù>/·<þ—*£>Œ/²ÅíÓL“ýòœ3¹ºû« çþk¤›¹[VúÐØ1Mߘm"h1'w ê\7¢sJ¶Ì~9æëTr(ß]Ï}hÏ?û¼µ2{ü÷I3Øi9¼Ô‡å—ƒãÏß>Å,̵®7•L¼ ãè‡dK;ˆþš´0¯5ŽC„;psKÑ­ìJ£ç-R‚RŒÖ†…/Á{ÝŸl}_{HñÜ}Éhð÷Î+Ÿ½âŸ:âUPˆ–s¯'iAP8PåK@`ß" ¬Úæ\.‰—°(—_¹ñ[´VGVEvøŽWÈ•ó^ç‰Ü @Fš;Å~ƒÏ`¸¨¼†Çdx¼'Jxµè·ñygr&>>B:mCî!CühȇÂÃ\š~!Y¤ƒ-¿0“ÖâêQö"Í̬Æ<éVÆÞí2N¼ëA=ò›ô”“ÄaLøû°­x7V–ÒÖs´2“¬¢©f¢ €Ä.ÏŽZÿ€Ô©¸%Ƴ=kÙÝOcÜ¡î}ê/´éoù‰¶+gú M07$º¡ã»´{¥ãÒé=¤htM³: ó V¡_ß°ã$„4‘´CG§ã§£Ú©ÔLúÿ;óÑ&”›$Dx;«Œ:›Frrë—·tØú§˜ßE3H&ê‚ÿ/„üÛ’®ÿ—²¾CYe~ÑM‰ËdV‡NùÒ¸Žpùôàþ'×s©·Øv¬1+`Þâ´~Ò!+÷§„`A,Q¾Hpä—yÆâèON®™ÈbÄP d¢Ã×›¡ˆE·D»”ß¼ à÷Q'¤ïõzÊîr!ûô—Ôô2OǦš‰ØlÜ MÍ ÎG_&8îN½ÛùÈïgˆ­e©˜nŠ!«Mß¶pŒ8}bŒ=ÙmK©úRí%Ôú“Ø9¾Þrü7RçNäý\êùøÀP‰a)çÈútAô?sv¢/–†4 ï_‚Ú²ð´aÇê¢8‡¬>É‘ÂǪTÈ`ÐW)Hº•J¼ùù4’¤[ì!þW-“íá¬ß_‰4ÍúúÓþ% y¥JÀÚ‘Ì€7ÍQ. L®3ЂáÀ@C— å÷-—Dü=Ò#½ñî zÚµð´;¹Ò*#biêwÊ£µÓÑÿ ?Ù±,‡ΕJoR†ËS Á ÐWé_Äö6GC©Ôüdâ}[1Ðׯt_äð]¨s•¤ûúº@_ñÖv¿Þ: ªŠ] àã}ŧñ$þ9Â⢥pGš*ŸÐvp…îÇ$î²y}qch’&»þê “½z4Éî4GÙ‰67.’XÐ%6! úÊK‡Ç  þèຽia¯–kŠî%•d [ßâTò·0JùíÕ›‹ÏóîD˜ÿ̽´;ýhoÊ* ôU Ø˜`ç=Û@0µT2& FZØA´RýnÄ @Puh ßï9¾ƒ#ºÏ÷>hÕ¡‡Ñêr´±· ëƒLvǾ1ŸJ =¦ êô¬  ÇïE¸>ÄÖ“þ€°ZŒ)DÛÒW=DXÑé|ß#ÎèË—žÙícc^Æ–ðu؈TŸæOÉ÷y¸o»´ÔV¤¦,^F_šLk.—,xÛ? )WÕgms¾&¥]¸‰Å‚Xÿg·ƒzÕqwû¥ïk©“ÑÃÒºéCÿ'°"/ÉÆëœí 2žŸ[ºMÆzÆUwòòimžÑ=h7“«ìZ²Ev9E{  DVÓ8%R5Ù gêÙ:–ðO#Èì´Ð¢™/E¶Äy)_/9fµ3A’G×%h˱.Z—4¶FIŽZ.n%?=M=l(q.³WŒ>,öh6ÓôSGî‚Çþt°ùô¦=u`©-{ÌnP4»}ѳ-ø‚$ó–@‚ÅÒø‚¸м1²<[ôÍÊL+|(ORj…/äÍR碅G/‡ÒŸUeK«¶¥§Ù’1%ÈŽKXq=€Ü’ïÑ"iG4]ÎLġêd/²,ÓïzÄö$ø¬œ,áÌÛñ¼.Ú€öøºÄ³º %7ÌI[ª«+¬2¨-Þùh B–´+…¦2™¨U:ñ6u'§£÷Xùg™¥}@ˆ¶=—J»Vš2ñ^2ÈÃå~ßVš-A*Ÿö'¶ŒäÐ+‘?tór(¯vhdkKzçÄÉï  ¤ˆ2m/5m÷+Ü úžÒ¨]ÄìÉöãö)ùOZ”çy@ˆç™ˆÉd—´¸²&åïGÓ}G£i7…ƒédòaw¾ 4K‘X¿(õà\òÉ0JCuû›bÛzÍÙli½ÎI(çº)ýÍËhvRâU¼ÓHßtBú©ì›¹ÛÀâ‹@ž9š¤Ïa{:åxSd5°­Ïð[>ÇßDö1¿E¦蜴ßôo7'VN?ÏN$ðU_©Idq$0'ÊÿÕ€IíGÔÙŽÁV»8t-D“ çl)–÷‰Dœ3¨él3Ä<`:n.9É´caA}š€„lZÀï}œVÀ>t,kßVvV››Òõœ„/f÷…´®3¤ïª6LÒê;¼m§¶1c,Ä?I3¬²&b =ÿšÊͯüºÑOíáv´÷{„Ôl9~@¶o[’tHäî)@ÉÄÒnö#;Ù#Ÿ®6DëVu#4 $ªšà™6&àJšºÅЋŒG~vû‡ÒŽžôÖ˺C7ógŸL›µ¹5 ¤ùG¨ +‹FÞ2­ ÷V£Pøµbçè|C-âzɽÔêöCNoƒì(´P¤–gœòðXŒçM<²´'HÑZ€Õn2¿^ÕÛ3‹¹ì0ÿI}oä*z»cÐXÖˆH¯^amé&Ÿ­c ÿ”¶‚vÜæ»× ¨ú% r mÙXjí%î7X*ä _µ’”=I‡fPŽ~‚šã_ñ+éÿÂôÝå´ÕÑü”–„´ß°™·åÔŽœ}€å|gÙÝO~ÝTHxêJöš¼;vþûL)åì;-·CÓÞW!´<à*û´G7Wül‡Á¢ä £‹Re«ù§’K¼ä¬Ùˆ*ý¶úKÓ.?±üÐf(A*XŸ ·Ÿkq%6‹&²\ú±˜›Ç–µQ~)&ZVXÊ2N‡ßˆ~åÀ®Wxm‚Mº\{ŠÖËþ[“8šV®P"ðM •h3Ú©½ƒy¾mÌÑÑ¥ÌlÉ+DâŸAØRßÓ@»¾€íëK+Rb4 Õ°Tôõ(¯õZ®b‹|~aKšÃ:<¦hŠÝprS`´CÌîÓ¶×#z/-DLåà7ßœ<€&ѵ:M~æšøñ G7aí1 ìOÖÊ´í}„MÖ{ÙT}B_¤·èá><2N²ÿû8‹,ÉÚ“qwî–âæ{ê‚íAýØ%XñxÀ0}ØWôõwÒg>ÌílÇÉ–³h3êÛ‹¼ïLÞwj, pB~^A[,nüyþ2bpm…4«Zc¯Ɔ§ßXãßu”ù•üBM=m›ëG,-Æ$» •ó¥Õ~ŽgÁ(GÃ/_˜bý¶´1— Í›„AÔËüèËBõÑ’L,ªiA! Ð'ˆû€°™>A2]÷2t“™øßx}âÝžgö2?â÷Ä,þ1`¿³øx-c;ZÂÒz|S,W1Š0ºEÝ=Å€ÿáâ³Ð¦ƒÜæžË"èVøç‚ûÒjYšj΄׵éÇB7Ó ðG–ŠÓ‹Å)÷J¯à®&’‡âÈÛg-r%õ¡¤Or^´ó¬Kv, í><‹qšF`¥£E³\b¬œ{<õøÕ\¯"\42iì‘)— éûBõ³„?–rÕŽ•²áZå¿Qß ¿ù‹ö±c÷’f'êtE¶¡ œf»QÿnFæ¨Ôî½CY@-‡Âȹ^þð®ÚYµ€æÇXˆ>öE3ß"yvP-’þß&Ñš"ß49å+2R ×Ð=Ü%´`V &½‚޶ֻ)©QåviÙûrÁMi Ë$B©4á.¹mŠ>&2ñ0ðp,?m…\ÎS~Þt X£«\WVJÞ|•þ\Â*tí€C•¤ÀŸçÑd”À–¶BãÓ“…÷ønCøµIóÂo[^á¼Ð æF°«¾y ð†íã~“ŒøCL0îÍ;Øër«å—†)r ÈëKÚn’M9‡na«ãføÌ7t4#µõU@núŠIóá)­6i®ïÆbC8¤3“¹½hcÝ3 \w»"ßBK.rÐfbWá+8)Iý©{Ç,]Èâ•¿ÙrðÞÐ ^ÂuNø8‰‘,d=iÖ)s9|‚ÍrEÓ ‰³H¶-;,E¶L:Ÿ[WŸçyÝë.ˆ.–^$õxçyô¯:vß% `¥#'~Äý}3u)é X{;}îö€Ñ\èsúÕÞôiŠ¿ÿéÇJݨ¿}š>ø‚hóTÍææüÚý¬‰É×’‡àÌäÀQÕÐîžjÑÚŒÿYcX1 %Þ"tªn„ˆ'™_‹Ýnº ÷ö4H_Ht6·S@áú)wŠÞ{Ú}âE¯ã|ñü-òÇÿËô e—>öœ}Ù÷_”Ÿ®­NFv-–þ‰¼ÚÜ‹±™Â ­Ð/CÈ×µ×"ËöË8Uë.²)‰ŸwÆË¤q@&›š‡˜©È´[,¿lÜ” }™¤vµ'“ÿ!ÔÙá.Y¤4ÀÖ¹êŸ@³“jŽJMpu¢öïæy€ícÐÆ=Xœzsè½ñ™f”=Ì9x'ÁG遲À®ÀjÀlƒ´~ÿŸdŽ]ÊDh‡Sô£âc_§¥bkV‡gÅ.³¼ú4ïž­ÿ´à´NJ³MÚª—&”™‘€ÍȾ›¼×ËyƒB‹¾ âƒO5©¯ ˆ‹!|AnÐßjŒIxÇPŽèr¾×i¤&ÊuaÓ pK$0} kSZË(Á$íS©S¾:3‘Íè»»˜vñ¢U6-b »LšsY¸åŠUØRë|ƒ_ñ^›ÅŠyOæ÷%Š&òs›™˜K{ÑÏüL•ø… ã&ÒhSÉJ:Ù²R1¥­T{u6͆ïÜæêÿÎÒd’š°a¸9¡‡‡Žá”øUäœíÝŽ»êà2h2cÖp€Ìb¦ÙÒrΧUž]&ÙÅ%“xǦçý÷ òî€ÈZ\ÉGoîùÔ‚XV¸è¿èœÏÍrªÞÃ(Ó'>À¼ X—líÕK©%8/ÁîAuG›#ÊÒhfá#Ò…ìKñO¢ñ3ÇüÞÈ‚u÷’Äí“@NÑÁ9¤~Kýšj±¿{0 °Æ»4%†"¾”~¬ÔúÛã)‡Ç9tÓÄzf·èžœ½Ò‘%µˆÝ•—-Õû§z#»3V ¿®ˆ]þœ‚]ÏF¦9-%ׇªŒÏ®ÈÏ7°äìœ×áŒCÐþ‹£%Í;mî1ÛÖ„¬/E0ß )M»`š"»“Ô²%%§¹þ‹Lâ¿8, ãË÷ÚÃ!ìxÌ·3­¤ ¸"CŸà»[ϦœÛ+™N íþV“*ƽŠÅœk²ä5æöVÆr÷CËÓÇB.Ö(:'Z É{],"+!/1VFÂ\ÇПfsqó½•IÍ_J&✲_2¿QÄV ü–£±XoXª9€VPÈΑVÑTÑ»ÐàüH[çý§G~¡›ßMߦœub½›4“ZŠþ,1ážåuO©JPÔ/›‰åøü#dàJƒ×Mú.cRõUð£êèJüï@œ„ 2¡½àyÆÇçÎaÄQŸ¸%8­Ç„P¤ÁìœÕr„ý”mɹ`6gÃ\ç‚.Ó †pÈ4ÆHN"Þ»ª›—¨!MÎÒ 'Õ¼~Ïœ©¯oÒzÝÒ³áÐã<=Ì8‹GvV’ð#h¶0i ¦´=ï¶“ÅdxM÷‹½ `ø¢uøÿ ÜÝ…ày'?Q?@jýÂЀ¹Ï§Ú«¦££Y¦Ñû:tþju½S@¯j·lÝ…îe´•9ñ Ûú;iß±µß­¨‰UOx'Ø2ꈅҖ<<µ]O‹.Z‚ÙA0?19©?„ ãò³¾Ë™ë7[•²Ó‚H_…H#dÈúÄš\¦å£GqhÕÆž€êýèÔßšš´îHù¬²<Ð;qŒljÖ‹žTÐÜ‹©;“Af_3§¾‡Çu2å™ðk" IªõÅ,Ê*‘r‘Ü|ÉìÌÍ î­ÁnP÷¤‰é%mß²ŸÎŠù7ݲœô pùL&J—³Èñ)OÉ%'-%L;.•¹ªãùR¡gh<›|½›/P ~ãKˆ“Œâ?ᜳ’_Ô|Zÿˆ¹$²WüU{ƒIòÅK£2“!ÿ¶!õ–þyÌÄÖüž¡_\Šûn¯ø]”y?·KUïÕ&6fq¤ô/RÕì0'åO™w…¹¶éaó¾OÛ`œëœxLUß#X£ýÚu8m|Àµè±ðZ@lü;Ú–àÒ0D=­½…~}O``¡&€ 4/À½²Î=ÌÊôãR§÷E®ƒi1G‘Ä4k‚AZî’ÂÌ8d÷ab…såGÍ…DX2|¤ík#Àæç~2÷QÓÇ3sâš™äa]úæßà1›ßºÔO™uŸÈÕ¾Wí­”_M&.\$m¡WBöÓ™h ³Xp$“tÊ,ÑqæNölßA^ ÔÒHÊ"ÿ1~•¤Èöäå&H”%Rh¶–Ä«Z‘bf8ÏëO¹Ê<绋PwêO¥|¸f»”‰¤š¿ Ÿ£Š!iI}"G;7ù9{ÞÀäögêÂ,xΜl¿S‰ßª˜ ºÂþT仹¢†¸\‹DßüaýÇÚL2A¢´­Zêsã¸4ä£Ä0Ó?Ÿ³ûó×íɘB+Ûô±Ë‹ÈêŸä±âêa ”tÅè†æ“vçš¾³>1ÏÊ,…3&•ųZ‘ß³³Ij¡åÓ䟙¼´€>Á%G—•æ‚—œJç|+²ÒB‰HÁ] Å¥ÀlÍjÊ×{¢Q7fïŽÛhûüõ¨ÙÌÉ̃v µ¶·Ñ!ßϰˆ½.¦Öö‰MNžÏPf&/ò™ä°Ôƒsµ[\߯ï댅S‘?ýI#sƒ¿W€ë©(óôã›ÝÇ|£#²á©]‰~Á¿§ÍÞÆ˜\.náÇ»$·Óµ€¿O½ºýê,b­©5ïƒ;a›S~ÚÁ=ª‚ãQ„7óÝz÷–Ë6½W_µW÷¿ƒ>Øýú¾–:4*ìFfucKÐÉ{Ž ` úžyæ™ÅÇŒ¬FIþD¼RñœĨb ”×W6CÒ¨FçwÖÕWYð·wYð Û†¼¼4á€ë™ {ƒ¡ÕWCË‹ ÕÕÄÙë5V r×l²¿ºD£úúzSSSc¦M›–ÃËëбcG‹ÅLc£›c2Sù¬P0‡o~|ðÁd$×ÿ|ý–‚É€¿£EçŠVò­ú´|e)àMÃf1$Á¡”ÕgiþÆ7ÑAÅ’ô1ÚQmïbã7_ø5.3µ»›ÃÑA’ÒMÃܑ̀7b¢w}Õ²#s?&š°`²Œë¦‚ dlx¶¬< 08o{›kÛ¢êÖ.€²Oå,É$“:–íq·×,nöýj'Ì=c¯ çœNïÊYÀÌwbnD‡x‰^FÛ÷X„¸‰ ‘hÈ»ˆ:<®ŒNÔv¨#7Ýè'>wõŽŸ®âçd«—òd–f2ØÙÔÔ a¿€;I×ý?˜‹nB]¹Ïåt«‰k.oCGO„Ç Ü–7¹Ò!z“¨W½WdÂðÏÁuÒæ?ïtð‰ÊZ”¿÷íIˆÑf¨G ¹½[ù§ÛXîYõûcÀ;ã8Üå±åµ™9.q? oó‘—jë¢ZlSMl/ÃW;QÚщ¯ù sÅ,ç¶5ŒàÊÿb´ò^¯”ó29q /9*îPÇ{mÉ{½À¹î?óóF¸ât…Ï{ÞåV™Û;hZvv8\µÛG¦“gRõ½…MœÊd«uqú´bIZ\š„5çbcø<®ÊØxýãy”3m¸\R}ÖÂVj‘/»u˜4Âø@?m ½„‹åêv8ü`ð`És*I5“ýXÏçnÆýîf(ÇõN |“Õñ‘„ÜÏv£=4l· ´°²€•¦T>ª÷ÑmÀþï€wîY¹i¯5»ÎXŽ+“­Ð’ÜùT™L[,º” ¤lôa€ì˜1Érô;ñ8{Õ“²C6Ç“v¬3Æù ¤ÞU/•K¡¼ú&àØžw:™-CýÑ Ë'Ò P!©ùHö:ÏbÒ);­Î ÈîðpOb’)››S'º=î5U¹ðm¿&¥—(ø Ö:%²¤éÑþKsíÌeÓv;²ØÂ΀Q€¾ª:Y:‹*á¬)‚×Ñ4]‰mµ•îi¤Iž^ubxˆIxÚשÔ²%‚âÂ$ð)ù_í7'ÿ²ÿê%fr:qžCSÒ¯<À× L¡Yg‹áe±ßÓl:s7‰ßRü~M¹Ž¤ý“ ã’ðvh'·¹€­¦\pë6„’kˆÝ¼.bë˜4ü÷÷9¥Yæ"nB ÉÕÆoÇÖ‡OØ<#Íï˜JÄGà>Ë÷}ü¦¯µw2©bqHš^ŠðöÑ5àçõð>Xµ#þìÍðð ‚L¶jÖ…ÿGüF{#&Ÿëú“‡÷Hçÿ”ë%V£c ìØÉ>¯óÕ3Q¾¦Ï¾ÄѾÍ8§ï CXÔÅ6”Gãõæ;¶T¯Ì¡lþý5bÖVðý)Í;yóïð0B(%Æ‚`&=û×e²“qñDåñC×ÅÔ?%Êõ,Ë¥˜öXVB"Sš}Û Bs¨ü#-BÚ&Üönê/’ãÉ#GÛÇ©S7R7³~FÿEŒsóæf…¦í” ú.I$~ÙÅþ|¤ïùɧ!k·Ï ì6øP½Eí5f,y2±KÍ#hé0¢Rèú4™ÊF«”ÊŠ3Šo±*TWl¿FßV i7‚Ý/–”‹‰ê +Ð×½ûËë_øyrá óeˆ„”Ó?{^¤ö@ûÅÁÁöx$µÅø d×Ê™ÀòM¦zŽu—ÓŸ{7Q…4º0®oD_¡±Ê”¬H¢Úš_ ð‡w)ø­P›w¿Xtêà´{—‡Û¿J÷Z8_`Žö!}uAÒ~ý)ÈŒÈ)»ÃãT)€ì/D/ä« 0¯¸¤— ø”⢴¢Ð_úŒË-•=Í­5îûí ÊΓdJ¯¬Ÿ¢RO_(Ÿ È0j?a:£’TþèìÊ[ÑŠ+®hVZi¥œß¢‹.jjkkM§NL]] «8¸úè£ÌСCÇŒ'ÐW{U&ÒaÈ ú*Ðs˜¨U £ÜWôãäÉ­@îä‰V’WÊ()zI‘·:£ÅEm|m&¸¥Mâì®m½ä´+ÇÝO¯ HÄjç:a‚®Úˆfª¬\È$ƒh6?i‚ºÛ¦Òs“4}À¾Š£pa4ƒÝü+u/0w ꃀøÈמo2¥0mO@G¶X¯Lð#ií ôؼ~ˆ­©J) ¯M¤Äª1¾rbÖ:†¡h¶h?XpAû.д¹’×Z6ÑŠæn‰ÓØlgŸ'™LÐÁX"‰º}sý€´]×:ÙPÆ ÜÄôEX¬¹ØÖqƯÁIíþÈØ³&uß9ìê{´>8ŠVœÓ§©}ìɢþ©'îJßÈâlMrâu-ÂIO@V€#ýV©õ¤5ªS·³‰ÞÆôU(7×p‘%è)Ž‘(ûÄ&§Åt¹#ÎÞ‰g?AÉÖ¼CI#8ÒÄäQ»‚Ô€y¨y ]Ä«|@~EïͤâD4ÎK#ÕÅ=Ì–€og³¨õ³eÄë'k+\0/}@ñZ5îÃiÿ€p& ;¹d.i¨KãÜ!M‚ž£Žë¿Þ%ŒÇŸ¿Æu©P¯1—Pâš/›Y‹I‹ÈiR;™­>lLÚ)s3“¶Ú—úý‚uêI}yƒ>¥ë­+C©¿Ý|^w}Wv}\ˆd1Kæ¡&9‰Ezq·¼ódw:hLµÛýëY¢ÇZ ¬¡®:¨¯¡6†§oô3Å&nó„©DƵã %>£_=š ê/Õ·3F”HÒô}+Ï8šmdCÆ©‹¾hè(ó}À¦þ|‡[_ã#úñrAß^´!iôµ5± ;öRI÷¤œEø•Sq:úÇÕBW¤k'}M¹@¥“ÔÖüÄ'¥óИ[{ýÚ eðŠJ/Î"^ó!É|4 žŽÂ£a×âÏQŽGù¿‚¤YÝÈ\ZÌ Këso/äžo*Ño™nÚ÷ó ö ›|ˆp‹Q_X¨ y.E†­2ˆì ߉\ꦓ˜k<€²‰vþÌ/${å’7­ÔbnêÅé5*O›l²‰‰Fƒ„]vA«" È-¯R) web×sëˆS]è¤A<Ëp'¹;¹À檞*©I)ÈGiJãY¶ûªA››ŠÑ…ïÞï£Lœïîä¹Ú“0ozJW‹ªšjz҉ȱyÛûÑ©AÆ("¸/©u:õ<ÈtÓº4)mMãÛy½…)¯Åp«ì›N6uãέׯøçóR«ÈÚDW€/îð£™{#ÚXa0æÐ´97˜¬¢.“T¨«@ü(“ö@ýc«ÔîCe¤‚ÂúSLr×£é52šn5Ù±5þYÓÅÔ²}ÞdóW¹å#·mK…Õ–±ÃÑßÍM¼ôžÀÖŸ‘c·Týv†{œíü³Í­9Û§êx7ÚèÝ5K˜±ý2³‚N×M²«ßC¨‹™¸ùr˜œÊOç=—C£á©Ó¤µBÞÜ©;Þ- üÞK›p-“#QcÛHÝ£€©:,Ê»Æ(/~:ö·é»¢o·È{9”ÌÞÇà´–B“D¦²J§)”wM²¯9ŸÏ¶$à€ÿ„ñh€°]X¬Ú* T¿¿+õ§' ŸŽ¡=ˆ â·|ï#ù–2RÉ^ñ Ÿ¶²:£:[ÙŠ­Jãâb.B™l@U~«¸hÞв÷[³e{º×‡çÎ ¤/SÇ XÓ•¸ëkßëh¹Z@I4ñªÏRœBÌÈœˆKàò ¢ÿ¥ÑhI›.ãðMúö{êÁØ4€ÔTì×UºSÒœÜ7ìOšw¿9Ÿý2^MÉ'ÛQ?3’>@cýÅ”íc”ÓOü>#DjŒ—Fò¼®<·£ÜwÇdL_„ú£xžŒü1So;Ñ/&—Þp BþÑæçÿ2ÞŒ@;_æÚ•¡2¸÷f]¶ÁdPQ@lÍ™`FËRðûD»¶iá|4gò_Rÿ«—žÆísE¾Æ<Ñ))å›ZdÊš>ÍzV…r2 -ü:'G‹€åPMâ¥âú˜õ°öÿ˜ô|H¹ ‹×~5z͘Dz®}ýEØær^ÄéŸ|x0RPZ:ß$߇¤§¥½ÒÈ|8”é¿?3IL±ÒÖó5˜>Aÿ yCµ(Ç.Þ„èã©~>o¸â<Õÿý‡:þ;Þ̳ÛÊÍõ*´uÿW·cÉ÷ÒŽ¤T0¾dþóÕ'ÿó›«®÷ªÎ|ÌnM)NÊÛžæ·7--¿~ÅL‚|4eÊ3bijÆk˜%–¸—¡·ß~ÛüþûïföìÙæõ×_7Zh!ãÄ‘–`»víÌ2Ë,cV_}õ4¸üé§ŸZ&K-µ”ùúë¯M·nÝÌj«­f~üñG3vìX3yòd³ð [ ä•W^9“`È;my—íLu­Õ¶7¬.¼A± Ýx¨·•íVï6þP[q ­Y ,  ðGÇà§ ÞR¯8Šú€ØbšššÌóC†˜UШŸ:c†­Ûu¸-Ú¹³Ùpà ìSøá‡fÉ%—4k¯½v:»þù§u—iµ iØÇ¾i^ð‹³ãR]ͬÕV2‹/žÔÎøí·ßÌçŸnÛ˜L·è§¶:…¶£mþîj›ê`ÿ,NKç+Ì5}e’ÿ*Lð¢Âä·½´¼~ÈËoW¹b'¸b¸@[k#Mh‹6o“óšXWZHÉI¤EÍí Ä5qƒ¾KóŒ~jZ8~-ÛG¢Ñ[ÌÖ'Ùâý4eòc `Ình÷ö ân˜Ðv]Ô˜j¾‹]i€ŸÒ$S$½k»Ð:6 Å9ü(8ÏÍŸ¦›©‹5 5ÏüNµ¿‰É}€ µ>OóÞûðÞî“¶ö·´ÆÔn!ÓôÞcœ E^2ÉUµd(2SEg²ž‡. «“‡‡4¹ßP‰ÝG¨$" ïä²? _ÚbæGZ„ݱ#¨‹ã˜ø¿‹™|©™“£‹šw¨§ƒ™º2>§¹@-¦Ì¦ÎžK~”É>-J‘-e¯€ßšlLJfE'¦K3«(âMl;ô‹ß¹çãñàð>\U_CP´€%’LS%€ß5ŒYè¶ôlHÂ?f%>…§Ûã¿cÎ@=ZaBÅ=Êäz’Y³MRðUkN¡®=‹Oð‚†æ :òòøtÿƒpyž ÈJv.©×sû²|÷p§3ø€;Å®KÝ­C9¾Å},Ð"aŸT?+[Ôƒ¨÷ZdûkËø5'ðm^ã5¿°¯ª6¸ヾ§ì#…öör,djÌÚ @¿(ÐWãïñ[ÌòNÿ“½èÈ?p¿3픾‰ìÄ ºß­ ŸŽUøFíw^±m¸0Û¿sµõói¯3¼í„ÅBC¿.ÈÒ üªïÍbñ4êÓs!4¢äÄcâ`äïÐ’´Ìæ‡×~©÷Ì'ôÅ^ÜHßÑÂjj‹ç£±(%¬S1•5Ñ›Ïô3ýTì‚ôSµo¶aÇÑñ˜ôú(>­ÖVÜGÚ…Rä—|»± –äã+X-æÀ@ÕÇðj2 ¥ÐØe…é?±1ù/N:÷ä)y„ç%Ó ¯. ÐRg¸¢R‘ ÊG§ÆŒc'o8¼Ò¸+ V÷'N4/½ô’#@K&"J½ó‚kŠT=Ú È¥ƒ¦Ò&#ºwïnæÎk>øàËljö:‘JÝÇlœÃ£ÂÆõ §­ûçœsŽ}·ÿ;ï¾kÎ>ûlsüé§›—_l½.¼ðB³Áøþîºë.wôŠÜKówfE81~rx×ã?afͪæÚ`áÌê}€¾â$MZGû¹0çæ !Àvüøñfõäûï¿O.œÐVt/›¹j3ªÿß}÷=<ÍÉÚ‚âÉÔŠHí䑯†™¶ØÖîÚ¥‹ùpâ8óâ‹/š_M®Sê»)ü{ï½g¾ýö[{àœÚ& ÒD\mÓá_Í«µY\Ð7_ž·†èZ[¾ ÖØëïM5%‘Þ©2`¡¡þ–¿\yìŒvÃÅLVŠ¥#Í ®íÆú:ém ˆq+€°X º :@íB°iGò?4eÒD&HÞ–gBQ<-  2ñiQ;9¥­žèáYld:Bö„EêMîF#~&ƒ—æ)CiÿmËÐXn4Gñ½žœ×Dó8&uk¤ê…&0~ê}W£N­Ÿ„ýæ³3¶’o?”ÉÑ2váhCêiOüßÈkŸIÌu0"Ù2Öb…êË#ÔÉY°Àñ {ÕD…€ÑÌVæ«Sß÷(.²iì¢à8ªã Óta’àcpà”øUÌžå@ß`œ ú:yèO[ »´ }Üm›~S‹¶|Û|¤yÃ!," ¶Rv¾aüFhßq'ÊØúfEèŒßŒK<Ëuqž¶b_Ó2©þ+˳Â;ÐFʧnàkOP¾ÿ,Z°5Ä‹h‰2Is¹è€<í`Þ ÐWý€hpÈ…€dèÔm ò'ª9‡ßH;à ÒöÒ >ÉlP«/™‹È#âÒO1kaËCjç§Q¿.v-þ%ƒtñ„Ìÿ¨3bÞ£ŽNËÌlÎøÔ=KΙŠAÌØ%ÔMê§/޵0©-N ?Ի꾵Ðô‡ÔÞn¾ÑΘV@g0þÈC6!ÍÔK³_¶s‘OÂ|´Å¾è¯"Óù»gÄAf\@­¡ JÒÚùµÈœ>õÔS91VXa³ÑF6ƒI§@\i6ê 8‘@*Ñž{îiµu/­F[ ëh)JKXZÄk­µ–µ#ñÄíó"‹,b¯­õßÌ™3Í5×\m¶ÚjK{¨_Ø|.Á`Í1baƒÿåÂ9ÃØœ€7ÓˆCÐ+0ø¹çž³mAÁ»wïn¾üòK»`"Íx‘4vUW–^zi{/-øž={š7ØÈ®¾OýãóË3Ϙ‘#GÚúe#ñOö¸ÿïÿþ/ÝÆ¼mÓ WÍk Ì5:tT|6U>õ± ös59i=t:àÙïôwµ&10öäÓ_²Ò³g@5×— H¾ gÁ£Q~ùÞ@ ½\{±ÿ…‰ëm6µcÐ +gúÐîv¶&½È÷þ€­f…(hr0”Ös!öÒR¸¡´¢MTmÅÜ Í˜ mY'¤I|œò˜½÷©9ïä¤ÊW—>Ä­E3õ“T=}€¶ÔÏHÉWž.vÅÝV[ó%ò/7úKgÆí‚Œ =šI°Iƒ– öH&²/S÷zùÓx&‘,Ûåxâ;ýo²_«+`†D“Üõ7M²1“¬™l=Eý¾ŸX÷'¨ó³¶Å¾ï÷iÞ2ð5𬴠7Ñ#`˾‚zêLãÜæ°º™6„¶ªFͶʭ¨ë'ånFî{@«¶ƒøçÀïU·G÷c]q¿æÝ6åÙíæòž/n‘&êî¡É>N½èÊñ®ûâoªìN=ÐIÓí£jÏ;Øú Ü?›¬ã¨7}Â;‡œ,„Ÿji(y:zu~u‹ö]…wî?¨íøú]É­ÙYu´¨¸­ÌA¢ýÄGð]Kièÿ ãlðÛ’ßwrûuo%‘é¥éÛ8¨*m%†Òñ[@ÙйY=Œ‰Æ>Ô Æ°;}8TÀ›v±hWÊS„sÆ™¬t <ÙX¤ݯ_?sóÍ7[€ð„N°`ùÁlÃÞvÛm6y•4/½ôRë~衇ZóNÞž|òI³÷Þ{Ûr»ñÆ- /¿‹.ºÈôïßß¾£@þ©S§šóÏ?ßòP=&@ȘSO=Õ^•¾@óÎ:˼ùæ›ÖMÿÎ<óL#-gå[`¶4˜•žìÈêœü*œÒ˜ß覛n²õÕ›oÕa}› ’GÇ–£/ÉdƒêHZñ;v´š¾z–™‘À^‘êšÚÜvܸqÖK7Úè¿O7¿‘-h¨M)œ›"; +n÷jÜ'[m.çµ#uF“AQ7‚V2hYÿ¥íÔÚj›ìŸ¶*Ð×–ð×ü/TRköDýÚ>ã”sÇ„8ö1e4.ǧ‡÷¨3}EYS¤ ôpç\¸½š:u ??rËËÈØL-ÀotmÞm}ï§~QþânÔ/i.Jù$ }Kb—/’4¬¥ Ê’’/è›/n3û÷̃=u‡jÙû6$LÑš)ÁTÓ`r8½¢¹”é"™Žy©ðO¥v'òÒy2­ÓÂÔš@_[»cçrû}ÜYõ"ʉ޸l’Ù SQp‘ †T6G°è%€<‡¬ŒñUŽsa¾¹5Uâm Çò !yøfêÑ9–Ÿ_ˆVâ=òn2ÃXš";S6Z,,~DüØ9Æ¿çl‡³Ùi.dÐqܺ²ÉxDý¿ÌC5Kš£c…u'V¾ëšxÖÚ–µƒ9$ÀUæüè'Ê%}¨Sìb&ëwe“&c@j©;PG.‹.fŽ <>¤”¤­|§—¿LBÔeú»úúEm@ðl¡ºTó_²ÚŸd ]CÒ¼}¦ž LÿÙ;ÀÏí¬þˆ©Uý‘\—u{pŒÿ‰4øå¡OêNFó{cjƒ¾=)’ê@w¶y;à­ê³úK7é© ¿õùßYð ¬Ãõ&:§¥·þw£¸î! tìjsKb9 hÙï03iW}Fïþ²Õýå!E2µ¢ãÉ–­ö``»e£¢4o0ï=²b,w§žŠ"WĽó§gQ*eÇ5À¤¯Ú÷ç˜êK2ïr]Éí(êÔ9,,d¤?î&?œ”½!£ÞžÉâÁ äï±÷n< &¢=ntQ‹S¶\ÖâÙimˆîO§q¹*W¦¨Þ‹uµ´ŽÉZæõ¦4Õöaöõz´®çšÓè¯%Oî×*ù–2yל33G*ýjDÍ`O°Ð¤EÅbi.Æ2ýú·² '÷EÒ?"õFvu Y¹´.¼Žc ^Ñ#käã›\²ÎÂßÏ_ú%lü"NåfŽÄ"\µ,ú9sƒQ!vìÁ¶òA뎦Ùô†ïŒü¼kz1Ø ´˜ù1Š?âAÿ£L`!æ1L‹Ã8îlvog¿9ÚóÀÐbO8^ ®U*Ìl°@·aûÞ´T¸jr¦d WÚŠ2ãà%•A$›¤2¥  Kv‚¢ °¸¥mòåÒ Aƒ¬Yi¥Št˜'`U Ú#-WiÉÊÜÅÓO?mo±ùææµ×^3_¼÷¾é2qå•WZ UõþÒB°'€Zeá´ŒeYš¤25! V ¶4‰ðvBßCÍ£>’63¡²•¦°hÛm·5t‚o¢ƒõdvC´îºëXþö!Ï?iwïÞÝÜwß}VUZÄ‘HÄÞï³Ï>VÃ[ ¦—fáðsJkÉë—ïYµ‡®¦(ÒTþæɶá7 ‚”¿Ƿøé§Ÿ,Ð{Ͻo2AgïåÖ½{¦Pâ?Àoi¢½rü[\e’Ô^üOÁf¯4·ãð¶­;.iæ°…åýÚ!VCç Þ;™±õâš!ñ•V°C…ÒqÂUâjmùÂH ïÌ"¾¥†}ÿbiÀ‰íã¿æD“ظ‚̃eÖ#é/·¥¿œ“Âüåpg ¹oXÈš¿².9véJ`/ l?ºmê~>nÿWÿ¹2ö¸Y“ŸèÜÄpóìÜ#c+˜)3Õq-ˆLV Z7_è§î<©+[¾6³€âÐC®2!áÐîô[›Ol¾wÚEÏ‘œî¾ý_€AMÕ$xoƒ}νÞ6œÒ¾Û+8ÇãO;IÒ_gÞ/íXèfPÑ\Äß9¬°f%^¶‰ŽøpYà¦~®Lt@¦ˆ#´Czëú²0"`8¨Ö¬ ÈðóÝHé¬G:Éq ™ßqvÏ‚© pÓ²ù,;öA©‰ÑJ|ß0×]Mý9™­ü³‰•M2¡þ0µÝs7|;ó쥃h25‹6ÿ×?mäãñ+ü=pü%œöµ ;>VÕDÞ\ž‡J>=M½]JÌ—‰yh¤fš]¾¡ü®B ã^ïh¿1TÜbéÐÓý°I–~§.Lñöáâ.G°_²‚ÍÔ<p<Žº«¾ãB´ù>ªdáf墔ä&-rÄ>¤Î hÿ»R7^¼ÁÿåãOÑÍ}„ŸÿØé©y]ëéû¤Ô4&Â÷ ¢Æ³Óà2üÝýpóæ3TjñïÈ£Fé¤f~¨8ók Øö^ÆÉ3yçgÊ~‹ä!¹I›¤ArWÙ‰´Ísªvd{Öcàg³˜&0>$ÝBŸüc§sP°MÊjK· '‡ —~ƼK¦<ªIR^¸‹EÄgÈ·v§•CZÕüÓO*•[)fFÊÉOIqç=Š<û:QòGoºÖ¬ÎWݙ좰óãš#3¯K¿ÿ¼“‡<÷ÿ N’¬*¿c9ø;Y^—<™x#øV¤¼ò| Óu1š›œÃ¨¤¥ëÖ>˜%S믿¾½ù¸ùòË/[¿uÖYÇjŽ®´ÒJV»Uk9´•^$-d7½ÜrËYÀWö‡;wîœ:e~" µiÓÆL›æÀêô¿Ô+®¸b`T¸•†¬›ÆLýÍ,¿È¶¼Ú·dÀxˆ†¨£q-`Ofbì%Ð*ÐW$"è%Q‡ÒvÈ1/ gÔÕ½B™cÐ{޹‹jøuFò£yó2C‚x+/"ÔÏ¥µí&á~À¯;L1÷‹“;M‚‹¡&MGº&?ª»÷Þ{9üð#’àïáÛØdÐW~+-º¶ r]ÉëOy§ë™·ðûßnæ7ßÙr\h£P)kì1.´ØbæG@ÿŸæÌä{w³‘‰æ7ž2555fë­·Î0*óNß½šåm ÿËÙ¿-ÀS±tT€*ÕÈ“CÛ˜ NuúËë§6òi€esÃnCèi$¡æoKµW"ÝÁëÿl‹ Èî©»|4Í K/6jÞõÔ’oèzØþ2)šK{V¶êî‰Ï4GúÔÙî±?Õt5OÅg™½Ð¢Ý‚ÆÒÛ€DçícŸñ+®ê¥³£@4%9õD›»A_È# ž±óÌèýÏp¹Õ§®Dè—3=»šú7ÕýP½Ÿx]›0Q"»£a{2üThABLQ'¼Ú`ò«Ù˜¼?ÿ7÷MRBnc!A÷û$È h?ö6}8“ù°Ñø©åͶÄKŽã:¤ipþí 42.G8*öpî ä î›?Ç¢jëU±+q“Rå•ÌpFºI>ûý¿˜zú‡kZz£]`÷üÂô¨~\µu·ƒy…6ñtÀ÷ñjûsiYW•Bx)|Q´sþKù?Ì·ÿ èŒZ“]æ…øöo#?iAÁ¸Ú~áÄ–#ß³Ýs5whá(„p»‡CÊB¡W$½gl0ï2º"ÜæK&‘Íø¦„ìLöGø¼€i‚_ Qodè_é[{Ò~ÙТ‚ìó©/øEo]n‰”wëÊR±¹‘ü3PhknåË#ñMÊìϾޥ8Ê,ÒýÈ^O0ÞY&@XJú[S_WoÐΨäÒi)\²ã¨ß>­Ú— Ó;»D¦fŸÊU]xÒx4ÙSÉ:á¶12‡Æ‡G™-#¹„"µ¹jƒ¾ÊÈ,ò;œ1kR‘sïKtÁád™0ò;XÙ>ý=‘[cZÅ.²z¡òWTHŒùº˜>EJ-ñ׈•ìQõÚÒ¼TQý¹˜9…[æÃiU¹B¿åæ£ÀTOáÇS ä¤I“LCCƒÕ@Õ!T:tJ¢ •­XiºÊ–o(pS¦¾ÿþ{«Á(X™è»Ê*«ø%k7ˆ ¾¤a)W ¥Dè©ä”/å_à³ò¡üzIÛ"–´“ûŒÓ &¤ì:@hÚÑuãh à–v³Hï+­Ý¯)/xn ÒÞ”F´CÒÒ©ìÄþûßÿ¶&"ä&ðÝmÖá§÷”æï&›lbî¾ûn#Snð0¦¸@ ‹‰ äÎ2Cà€×IŸÌ¥­ïtÏ=hP@kÆ"æ±o†›=zdUà®XÐWIê}‚a^ðWñ¥é+ÐW~9„¤©¤¬´Ã€þ¥¨óïŽINBê»-ŸŠÖhV^yeóÕW_™>ûÌl5kŽ™¹p{óí÷ÓXȘо^^öÞ¶éØÓV$Ùþ žZŒq(ÐÞNᵸQIë, úÖ\ÂÇy4»E õ{¨¥ Q\ 9ZzÊ b–ZZH8€hŸ€š<,+ÓÚ¯FÈÛ­„ÃödtÑ.t–M3¬ÍUi؉$0íÌÁ%¦O*w ô?ï«Q 8ÒÌígÚè!EŸQ§FûôAÐÙ;„]¹öN³JÓcæ¿l›ÞÛyÿØ pÌlÔ˜µ‚}<„».A°üš…Úv{ @¥¿†pgT¡ågªß™?Éš³@[17ŠdB`îÐLйZ¸s$çšò°6_AêH}åä8›Ü‚ìÔØµYÎdšI‹@7€¿ÒL_íï©ß»úûƒÐ ‘ ”bɭ鞎«²AØ4i”M@Ú^Ýòì©§‰ÔqÙŸ®$µ§òÓèÊw;÷EãÚ¥‡ç&S‚ïðÝÂóñ¹f]–˜x-î,ЇV:rZù÷£ÈN¬C\Ȱî ò÷•_¨ùÓ-Èþw&ÙÚžÍ÷/Ùæ0Q£ÇÐÖW üOEKRZ~ÿEIà-v|`JÄIp’s³àÚ¢% ™¦7 —+Fk‘»ÍuÈ2ãO@Hñý½Jtý“´> ¾Œñ‘¥üX.ã(r|B^%S•K›FÚ˜}ég“ßq±„$‡®„Ù¯ÅQOlv^è Ö >Él—‚¾H64 åï¬}€Ã¬cÜ€o))PrÅ´‡Gö•Y¿ÆGGÆÎöü > ç]3¯% Xve1àø”2ŠýIeuñ‘íÃüƒTÅuYÆ MIó]¸O¨J -ÅT‡ þÂñ¶þ²òã€~L‚@_Åqo/—¶¢Ì¼óÎ;Ö.îÚk¯m¿v?3`—ôË/¿4ÒŽÝb‹-Ò¦ ¼<”þj«­fM'|ñÅæù矷üCÛQ@²CT«4u¡SJðàÁÖ†ªl ËÌÂÆol¤á+0U6€eÏÕ±ûë$¤ÁL£“=“^½z™—^zÉj6̼ð FïDŽ&ñ3ÏøíÒÃÍ”)™‰ô?l×Ä!é.0Wö|eÞA‡²I³Z¶{àÉ>±Гßå—_nú÷ïŸÃLZ»ðz Ì”‰½ @ØŸ'¥ì*Ë|…¾«Êåñǃ'±J[à¾JՓ=¯g@IDATÛÚMvדœŒ´RüíÞ½Gè«ì†Õ†UýžIxoûZ‡Õ`ÁûKQsÑ˳¶{¹XZ»[wLÔY3 n›ÀZ$iuµ£>f~ò¾™6îKó´µ™|äm›î°ôUo¯¾úêôïÌ«¯J߻ݿ#ܱäh° uÔÝÝ<ýîÛáØÃ¾µÇׂ¾?xsÆi’ Ђp—À´£¬Å @îÒ±+ôÿßè맸úûkиØ`Lm÷iL‚HkW¤qÛÏÐ×qSÛ÷gš½¸'«ýÜd/ÕM‹òЀMkiíúRã fbüINÁÎ,j¤r/‘¼Ké8:ùòH9.Cé¸#ê@‰èAvQðÊÃ~ä]¤¡aI6èd·Ð¡Èæ©»Ÿ—àk‚‰aÑË=Šãìuœ³®ô‘m²\²d²þª,§ôCü &®ù@0É-K¦ƒ§ojÎçvõôcÖMìRÀkò3gKœ³A_MRƒ¾ÑŸÑ“ð]òzœúðAm}ª1²§èÈ÷ªÏgrœS¯-£rþÅîH¼&É¡öê̱ö~ÆŽOh?êÓ ‘4r^ œ`ŠíïjÚ–±¿é®Q¢ª=èZ*üÆ. òç¥2Èï|&Î'xA_Õýz€»øÁqµx޾ß:¼§¹ºû›àhÍí³"u8 §lî¬ÌGé± h5aÃô•¯¥…U›Õ‡±´ù4‹P¥’ûº2>Ãnï.•Gõâуi«}ˆùZõòÐú8Óƒ™ÃÈ.B#P‹Lm›«EF£ë=Äê(žÇ¸v7Hwúë#t µ¥È¶|×–P° Ù8ýYÀ?‚ÝŒ•}ÅT æð¼¿ÊÌ_"zeP½•Æl-ÂÌ/ú*ÿSøiwhrWRØ7BJjbQ+vyØ©pÓx›·˜W|Jái9ôNúц6iÞ2ÐN¹öÌM"T5]Ió’3òÑé,$ÉòÊ„vX-AäÃS¶•+ªñëÖô Ê 4R»¸ #;²nêÞ½»‘Ù©Ž©‚ͱ_;o½YfyCÙý•Fª›¼<ä'wÇw´·&&ËÍ#UÜêž爵˜ÒjŽ[„qo»ì²‹5'±çž{*9«¡*û¸K,±„‘Í\ìuýõ×[€Zþ޽bÙGŒÉÁC:$N¦ÄC¤w–]Û @«ttøÙ]wÝeƒÉ†¯L:L¬ÉÞl9ï"íZÙ–i_xá…HÖd#FŒ0{íµ—e! Ük®IM¾pqâ „—fðE]d®½öZ[n/À>ï!C,Ð~ÀÙD>|™CO;Õl³Í66Ÿ˜Eµ‘ìÊ¿ÕV[™°Ðt"½«“/ëÐ ÿ–åûOáK!Ê–Mø+FºKºÝmEuÈýüEJûüŸ[lfÖA°u&Þâ¿~·¦k·,tãMOå¾;‡ðÙƒöššÌ*hi/Š–°ÓлÓqâûµÍÕÈ¿§ž9-ºAf'ž÷:Ù¥éû]ž:ê§gAQ?ù¶ oý‚ç¸išY¥»];d—êq4¤¶¤gС޽RÇß¹®Nþ:«´ŸJ_i(ÇoÒà¸íñ•"ëŠ_©iðÜP%{«ð ŠšocWØ-p~ñÜnÍ«&{Y·ÿ½WxŽGï¢q`—i Eçf¯þü´h1Íàí0'ò•àõ‘„<ÿŸ”>°Ë.YË8hÇn¿{€ZSÄf^_?_ë¶íèˆ)‡{!oŒF@" ënL˜NÅÎ×Ëhš2íÏ¢Òb7„±×2ïÎø'žBÛ—Ÿ›"ÿ8Ø5ÚíÊ»/û¹OÑ^”Ív”Í›þÜã˜Oøû%^J»k„зȢú‹™0¼Ê{¸ÞŸva"øQ#ÜVD=ä.‚¸ ßȽȎ]~Ñðàê)eX¶Ñ.ð]¥…¾u3o–Ò‰…¹™ ÿ€|³ÏÓ†ÎçÀº–ê!Cmיּ‚oØ©ÈBÓX¾õ ¡C¸AT†3  báôË"™<±6nÝu4+ÔÅįcþç&ÊêdÏ8²>aÒv(5… –s½ŒeÏü#ÂOôz¸žL¼É‚ÂN¹»4\ç³Û³ióR:¹)ݧ¶’YÙz–VW‚þ1§ãèP5S-Ž„ZZ¾Ÿtõe¥q¨R,i—µÈVÂ]\õºJiµZ¶Ë$w9Ä^¤ ÞHçRò€LÉ bA`nÚµÊ7ѵH€º&!¯ú€}a&Ú Ì˜\w s6Ŧ̨nká¤b#„¿%ô.$ÚÎ܃á2%€S9ÎZDԌˇ¤é;k_^ºˆvëæÓñÈÒ½Ðê?,>5ÇF¶â"ÙƒE´3M‘×Ù…Ë{B·òÜÔUŠ¡X$bæàß³8`ªÛÆ«Á??PÈñk-×6d$lGèÎóœ9s¬½Ú(Žê÷W¢!5 (ú‰–2sðóÏ?[¹]»vµ`¨À´¡C‡Zй PM¸seû×½5Þ¦û^æN™±p‡Õ½´qe2Aöyl‘>ô2€'nóÒ„–fòÃ?lyÈ$†[«VædOYZ½2³ °WÀµŸ=Ù'Žò¾‹×oïíØûU™(o²ù:¶qޙްã§üêP4·}\çd&CÉÝ»w·ZÞŽ{¡«@{™FHVñB¡ýýµ(0Ïß+ËU«?S;J©kYŒxVlàÔ¯F¼ù@ìZU§qòÌo&¡Ia˜·%˜‡‚:J|OR9:L1I]¸è2 I~ý– çŸì‰=Á $³4ËPWßò~4)ˆ Ìð.§ã–­¬÷‹•óä!ãE¶ÐdæÉqªŽ I«ußIspE¾Çëžï¡’)V¯ëÙj´GÖV«%ÐðØ‘ÿa³+mã-ÚF>0é¿hìJ¿Ñ9¤”ÒÓÒe_Vseê£t¡zù1“^÷ Õz ØŠÉ„º 5˘ËІ:ßtVûа¾Z"cÒ¢B)%Ÿ?WAýH&!"ûÐW È8ùÝÕ4=B¸çü| ¸­Æäó8âçÝôîíùà•”¨Q%‹«££K™ÝN‡§Ë[I1ÁÊ…ƒåQ2IZ˜“úå2Y'½EƒõÚÅ’c£‹ðæ³}_KÓ€XßÑ&žóHfªGëÑWH nftj.¢_J}´o¢¾¶c"eœŸs>Àlfñ¾E²SJ¢2¡š¿árÞ…ñ~Þaô˜Åê.2ÓOÁÿû‚œe³ó+§ì@ýØ061«?õFÖ80Œñ& ðâÛZŸûðN’SÃj06Ï{ÐÏÔ?N½P7»â7ßû¡Â}ód¸ © ëI«-AøÒrŒKçP>×Þó 1ÿ;v𼌱WÀÿó-ü:H_³ÿ? æCšx?1NÇüBJÛ×-#\¾¡ºóûÉåæºÕ.¦èÿñ­éëÐõ-Dš§Þ<;†qðù­P|¯ÿÅÌiþ\º c¼÷À6oØä3ãKÝä÷ Šìÿ )W³²ìû(>|Rá9“äáŽü†U˜oÞ*Û³'íúHÊåÚµæÛó¿ÚôM–ì.!õï7`“úæ¤ÒT¯IF¼‘úÕH~NÀ4ƒÆJÐÃð|”<¾ä‘ß*Á» Í#jw¡¯¹‰rÿ´`ðjЮÏUQ0Ó‰AõR$®žh_ÚÍÐDWê·B樅Åb\äË9X̱ûê“Ï;\±÷+ع)6¾;üòðš^BãX»Ùf›¥cN¥ØUð~$ T¶J¥ÅéhÀ ÜÕAg21!Ð÷¶Ûn³f¶ß~{?9n²ÝëØìÍñôq¨´ŒÃ†sy+÷5jÔ¨´V¯€Yçœdô¼Y‡ÅMm'@ÛH|Chèáæ£ðÒ¨^m¡E©’ «ÁÛ&ewX~+µ]ˆ#a¿­@íNøiøsÊÕköBqDÒøÖ»…Ì“1’ÿ•‡Ò`Ì 'oÿ;mãö–…È®Ÿ`Z#Bý Cݨß*SP§­Ð³ÈÇìTDóqwô:©ñÒÚ'žFØÌB+ÛëÎ39¯Ä7©’?³òšá¯;™}±Tw3•i·SÒ"úõ[ÉÀÁÿ?¡Tfáý¿ÊÔM`kk(µj:Ž÷0°ŠÞb8Ì€aÖ©‚ÿ¨Uó®…ß\3€-ŽÏ2ñ ['+˜‰VÁJe.áÚK£•õ@ý?oXçY¼ÏZQ{CHÂÖ¤Ä}lõ‚©_z7øîúöÅ“Ät ýÓh·³+àÕ;|ß‘}:xÊ;ø¯E›ÝŸÿfòôïù…«§Ñ{ä£þV«F¢,Jäï %N§ÞoDï°<á¿%¼úw]ûš´³5Ügš.„ÙÁåKWžòå§2~î ”?Ç]âÏ0}“¸÷)×z~@òÂàBøÎ«9¡ µø ŠàCÉzúß,šˆ[ðÈ'P€Ó²¸c‰ÉâOž ±ò?¯‚ýtOêŸÆ­§Ég-ùüÜõ¥y8ð¯Õ¨Åcñ;b&œ@âOÙe2‰œHP/(³…§Çž—©À㎻ÓÉ󈬚j̽ÔûÓ¨ç’/õ™y)Ò›Iö¶|ïÏò+ËÓ‚9?–Å¢¼È,7½ÌújM‰üšÍ. €Ý2x/Û½5=%>!ŸÛ-ʵsümdÉ?©{µæ]LŠLÌ·ÂÝpÐB­N›ÿ‚þ[ÛÿE’Euø_Gž¿Jµƒï¨Wծ׋‘n¦†²To·´ŽQ–ªBßòN?xÚKPB["?.‚§§¶/Ãq4FÝÃNú@§'Ò%ByØ‘ˆñ+Ñ‘ûáüÆ•‘NkŽªž¸!O‘L¢[P*ƒêÕ<h/$šø ÒY•´d*äwêþ´PÜo–ï6A>{t:c¢[6Ë$¡úêiA‘cãüêüøùÕg¾¯ÕxžjÙ¬CZ° Z€’œÖƒyúØD, –Mèshs#xŸ`»«^VŒ/ñ7È3‹›Heÿ!üÇ!¯`™ö>eŒC£ cÃ{N÷µ¤Ù—yÃé{È®û›G–c!sGÊOríf+úÜÿ!gÅùÞCBÔ?o¦¤¦žD; ¿èçWw”0ɬ—Cî³ZÂ?˜L†ó»¶?Î SŠË ÔÙáðKjǗ¡Œ8‘ð=zPîÃdBhF] ycÔLÊÂÓªCóp}ç|ßBÊ‹òÿØt3ÚÜO;£.ÌOܼ Ý·*à·R ¯^ºÐWñàW÷¥ÐŠÑ:Ós³M¬[iËp¿~ý²4iKá»0‘ Eù9«Sp“\¯¾úênç¬û_é@hÅGñgreJkŸ5謎ðí·*s‰ÒP‘H  ;m‰t"·[Ò¥´ÿªÄ:0/#DÇGÐOw:]nß’4éýÌŒZuË…Ie­rVÙê;È–îµê©vä}ÅMÏþŒ÷k)tu¨¶: J‡&„! jx‚( üÆŸ#H6Ë·`Ä'Œû¶ ïQë+Ugó¥ÙOÕ‚XjðN)LΡ•úé=òÃŽåeüA&ñþ‡~Á·þAßÛºv<ƒIòÝŒ¯s+`ü€ôRKvz:$Nò½ðÊ¢È.`‹çña?B«|'ϱ}ú»hýJ»Sý˜Ó2Žð=óÕÖŸ ­p“–dŠª3Súav=hëó@òÒÅ ÙÔm¨ébûÓWà!°ÓO³X{ ò¢-µ¬·4çhOƒ§¿pÔ€c"Œ·‘tØ"‹rj÷œ²Û–Êï4Æ2q™j;Ù9+ôôG Pµ/æ_î†÷½ñ×MàÉ.–Énd=ß8Z)‚ðcÒÒ¬t~Lƒy…þ1»µ#ê™ëïä[¬ð:´ÐK¤üƒ%•Óÿ¨¿‹#Р~ ‹le¦G„Sps'î–e àØ`îfö2Qš³&öB𽽦“éÁ¸ÿß~&GÁ#;ÇD IJK°%ŸCKàâiAé0Zxñ‚¾³ (¥âÃÌ•1Ó,’`J”I@üU—£ÿ­ÊìBÚÇ0ÚT¾1É?v…\efÅ( ?YI¦,¡Ÿ_‹\wÞNàê|@'RgOdÒøýáH:S™¼}€ä¶oœD_ú<_Ð}ÌHD·1›<6Ð^¿)©ß(¾à¤±~2`…úuÛçGÚ=†ð,~lÑ µ Rõ² ¼î¤¯ëJ5$×S/ƒÀ†Â/£ñw ÐÜhC_X/íR1¿¤n~ÄÕ$“ÿß~§,^äEÇüÅ_¶éZµ4|øÓik?Ówç[ÐWáJƒ7 ¹.j)Þpš ¦c´êHnþñÖG&L+(`rK Á£[’ÇšäåKÎ#Ù›¾â!´ýÆH…J¿å<'ùó_ý¢©âÈOÊÑÈî¨"ÇM¡OBÎØ÷þŒ>!Hps•¬ ¬âÝ–ŸÝʺ_–þì2Ú0‹ d<öçd.“x 7æ`©»›Íÿ/ØÉô}²ó³îaÿ D}¹(¨èû óíhB9…¥÷«:VIÒ@‹€¾ö%FRدq÷KQ¯´>rÒ5ÑÅP š‡ìYl)*)ÿÀ‰:êÌWøýìï_„k8”©†÷ ã©2™Ð·o_kçvï½÷.ZcÕ¯ kh¬•&ÙíUþd¢}æn¡R¬Óm|]B‡ 0Âͯ@Ò½ÕH—JêÒôÝZšF…,Ki J«×M¿5fi³´VÒÂÅŒ"´r:‚ÊA •hJì%ÙÈLÑ› ŠŽŸãæ\µ­¿’uÖáëwÝím­vH@ä´Ñ§kèúç Òvë70PMú³Ìg0Õ¤*Ia„L™ö8B‡ÐЇøë ÄøEê_÷L y7ïa³dí™f[ŽªŠ´=MŽ4m͸ àpxè@ªë9HÉΓÁÿwpùHµuµE½ßþ{²S·)Û Û“5øØk ¨)‘HÛ ì ¨>6u*¯&(·¢1çPg™ð¥è-{rZwÕ°žÄûímŸ÷°EiqžÇû"½ÃVlQ˜ Åïÿ<ÿƒ=Ùäá* „&p~`š±ÒÁ`”ÊFú~nšÈ©9VÉç"?Ï|šsÕ2õà—Jº©‘«+M [>ÌÃjüv“£ëfãðÖD¦íF7¿ûL÷àçÞM«=ÑÜC•´AËÛÂç¡!ÝïS ÿSÅRXÆY˜oÑ™Æ)M]Ñû˜zp›µXAd:_jE†?™”pHwêz2.Iq)Or©îw9ëýÛñ+µn§5~]YÎ×o)X¿€Yw¢¥Ö@¹¿ƒ¦‰#, Cˆ+¤íéJªj·²-è]Áà°(Í{aAªå.ci7H{¶õSiðqЛiáà?€kOR«/ ]9R‡ÆB“…­NE(À6Yli¼žI¿[ô`ÿ<å5xÉ´¹…Aêü“!h·Úëôq,­WdÒê2Æó_"ëó[™tF—•VOæòG`nBJd¹&ÖÔ?¥úhNÊsóŽ;’aß¡\ývÃÉþòÉìà“lãçT0¹ÜusÊô¶hGó3cW¼þj$Ù·Ú´!ØâiÌ!Ç#Cû«•ÎÁQ(眇vò ÈÕÓ}‚‡]Pñ‰:ÿ99CD%r.;©ß‚HbÙO÷(-Í:ܵòÒR¤eMæ Omì&AlÖ˜…á”ZõÜž~IgWIZ<ºšù-þ=, —€v‘ß7—É—ÎGŽ6‘“ïï)[™pЖo-\TVÄ·Ùk‘ê|š»RM袭V)ÒÇ!i1æ#ÙÛªäi©ùÒòú}OÞ´œM‹S€g0òžíìó¤ɤՔ8<mªñùá—¿4‹`4_•&ÞH;&ü–êNAËDcÄÿ.â¾KS-3mϼ\ò0 @“¹›f¹ó;…}IÜÕ¨wÑUüîáûjáçi4%µ2}Wì÷¬Œ#Œ#H@“ÉiJ䣑jñ»ÓAü„¹ ø>M.:°‰l†ÎLL‡÷Þ<‡†ýóLd6G›Ù¡ý\å⸹¯~¦&ÜþóÝ}ì!†éfHò„ìæ–K·Wå$˜zd¯\‘r›”^H”L4Ègì ¥×"ØË¦àíš^ÒÁQ/3Y-DËQs?CÃýt4 Ûr¿õ1­©[{(åó®iˆ3¿Ã àõòN5}’0¬»“¾ñ6À€O|ü‹wÒh¯Ermñ CÅ? †;$' Gÿ0ÚU°rŽÚ`¦Uù‡­ªkÍž°§Üc)ûÙÝðe? @’’ˆWåçkp¨—[$'~'\$Wýò;h9Ü$Æk~yïºÃYݧÆ%FeÞ_šÙ*Ïæ}¨_!?èpŸÖEHõçSßÅcç´®¬-ÈMKàs¾7¿,b™¢©_–K±’uFæÈØÅr)^‹(Z@l`¬<™-ßh’6IS˜mI!´+í`Úöäch1¶J ë![,FJi“mš<òGph_”AʾJp”ä«3dæÖìÃXÓ‘ñê\4C,dùØøT îB²ŠvŠeæ ¥¥VÙX:̼ ¾èI2Âî/A¹åcfŽ,(µÏ‰ï‡w„áùW ³(/uå´æûÖÿZPAšþÿ¤­=ŸÜ[cò+³çéÇFæß5oôíwz÷îmù½úê«9|>øà·¿’Ü9¹š°ï'1Öi6¬AÚIÍo©"È¥‰™&êaHaWcBTŠ9/¿©“>~Ïþ:o•®´jçM½:ÏÒºžCÙ6·m^¥ëõƒÞÐ6ÃÀâk·]8 3q²µhªµ=,óË£ñ7™É¶×Óan£[õ— *ʸ˞³ÚÙõÿÏÞyÀ;Ql|’KQìŠ ±€½wÅú¬¨Øë{ïíY±÷.b}T|*bA,`ÅgE°R. ‚`o´›äÿým²Éd³»ÙMr/EŸKvg§íìÌ™s~sæLïGòþ›IkyBì´¹æÇ·ò‘.´Mg ÂØ¬’y©×òüÌŒ×À†Ó¼©âÝ`›ÙAlvÀ¶¨I¼oØöÐ\>—°Uþ*·¨²xÀòa„c7Œ×€¾v&óé•`¥ œ”­8r­ð-²|j²eUÑŽTÁ ­U|ÉeÆF¦îs–6¶7œ‘¾eO€¹+å«0v Ü¹ BÈW¸1/’'1V)2¯WYʤŸ˜G”þ”¸ ´<ä•QtMkþºÀG´` 9I~Œ{•Ä Ê¹.AøA­H{7¼¨0ß?²±ÃËgi»³Q¢üŸ¤:VÕõQb‡Æ‘µ“æüwª˜wÏaŽz‹-þ‘O-étŠ á0wÈ‚~Α Yz_ݱðÃ+èDÁ èóa”Ø·$šmC¬ú°˜ùgçcͲ+m¹½µx”XÅÅêÔ}¤x"¿Å€K5#ªÒ i$þPiâ¿O:ÇW¦x…üû4A㾩´Ø¦—Õû@{~‡9"h²qß»qr'YŠ¿îÈKß!]ï£kT_ò°a 2ϔͪ ráyìJé•þÝôÎÈÞ^ðØÞè 28±IÞ•åìðZ_Ëmg@æçøþNy‰.¼ÏŠ]Ó¢„±¤Ì:´e擊ònKªÓ°–ÕÙ÷U UTh#$’;AÖ¥;Ûü ëÀ7ºžŒ¡È¢‘`N~ù¦É¿¤¦ ŘJ.•ؙ٠)@8CµºŠtžOpí·(í»LêÛHV¹ÙEò\ƒü^v4½øíãâRñS’BààÄ9*hVTí²‰X@ŠU|R'i“SŠšóatÈ– $on‘t˜ØØm7ÖI_Õgrl°–¢¼o|Ê Ê*å6wþq@+§|ËÖ‚´Ê¡C⊧™ðœ #”öÍ8›½Ô沊mlú™ôMWäýº”„¥5ˆò VÓßç3pÞŸ˜T6brÆïúüŽnjRkGí·Y7Y\yß>k ;¿6•ÓÙ% n!/}ãRЗßê+”ÖŽ<ý£9äê<*­2»+¤vÔÎX‡Åžd"€¾*£±A_©²Â²-¨íwk¬k­:ÇÙF-¿›+ò÷BÌ ól¬Rƒ-O¯36*ûŠ#ö×à‡q( ‘òmz †ŸúêòvjÎúJ‚¸@^—ãâr” ¹!èÛ— Ǧ~0“-Íýé?Ø:”BtΘ•|‹Þ'±/ÖS'Öìhz’~+ÆýòK˜nîÙ_ÍsjwÛ’ñR†ëxRÌéÎ%¯¾€Ò®5§¦8Wÿ;-«ˆï4…²á_ƒ†&X ¨ÚÒ‘P‹ 3O£aö@k¹ DAýK}E£kõm¿™úÝ¢|S òŸÓ{®@éÛ“oy.vÁŠeÀE-2œ˜ëw]Ë׸Ð=…3Íô1A ú+&ÅðPrKºVW,_âo(_§Éõés§{"nøDSß)ž+Ú"n^Üf…TþW]¼GÐ.ïT¨¨kÌtFj¨g´Vü&¶G!YØ»¤‚Ï¢l}ž‰ú6»žùˆ&|º$Ÿêr£4u•?/ðÍ?óke‡ð¨Þ÷±”ËUøŠoX_ñ~¾ÁÓ5¶ÌÐðÊÙ»XÉH¯Ù>=Y5ÒâArÕ\? Y¾U¯20^ÿ¬²°y7¹ÇEÍÜñ"ÌZ͘ So1ÎúÌUª¤‰}P‚ç" =6%­Æ˜ï Ù!šæºpPà,çÏA^r ÄjþnÉm‘åN@&ý„¬Ç‡f/²ÚuÃ(à=ÚZ»(Äÿvg~ «Ý$[¡³\AÅ‘r>4ÏZ<ÜYãF¶ªa1ómùÂmy#ýð~æ¿Ú¿YÙ`„#RTZoíäފÿ‡KÚ`.žW©°M²0›Ê+*y­zúÊÉèEê»1ן@ÿþ)Sº8®³BNAx=àmk/ɰ„Ó›Hø˜Ø¾"âÉæJŽ®nÍX8ŽÝšÅ2¤Oº é<ë±ËNîùr³Wú[wû·Á‚Xú\\’\\15&è«-úA¤J·z8ÃõQe"›/mãÙŸD–Šmø“ò5¹ª®ädú_+ž ˜iÚô}‡ÿSNK‹±Y¤í©µ"å¬ÕÐ8¤¶v­}¥ÙA,åøv:ALô" ›Š´`t_Îê»ôU¤Ì³€‰ÿ§+óÂkØÜÞÍ´4}9Ñ&AuY¸®˜7éÀ8ôµãG½ˆ}=N<*å¡ñÒLj-K”Äî…ÍîåþÀ½}•º<ÛÎvXìkD™ʧžrôÝ‹ö°ìäsZþ§K©™9–ñ«í§çñ½îÉõ\ü_ $ÃèàôUúž(‹!Yh¼¿Ç‚F×\=¢ŽÅkȯتR¹†Pú!™C‰“¨2ãPH> I`Œ¬Ðo÷öæ×35Ÿàœ~2ÊmTêF»¨ *%ñŽ}P’ž±¸ÈŒO-DE¢ÄÚÔ{[ߨjÇ7£ÎËÈ ˜¾ùÔ*ðÚü2gR³¹ãÏÕ§ôäñf÷äÁæ*\‹ØÒ”͸ HÞÜ‹8‡f9ð@ÈÎü¸>=9—`;ìòvÏŒå.×ï¼Íá{´)åš›Z€1–ù‘ ý97Uª‚º|xý.é~¯ muIt€îŽ€å\QUWJÓ¦–‘|ë‹Ä…+¡`TÄÊ-ý2ÂÉŒÇ5ƒ \ ÒŠc]ê°¯/Ñ)µ“T¤C.ww×”Y˜ô2ÊŠ.eX‡\nÏ’Ájuël±‹oY ðkÞ§;óý«!9ιG²=yãNvÎW”èà~ræz¯¥¹X$_Ý0VyÚfÕY>2“a™9ñ›â?Í‹}Ðþg~Ì|ˆN$üûmp–Y,:ÍŒ*Y…¾R=}¤’1ߎ\[ærÖ,ï>?íDÚ;ôbqÒyÐoú ¾ÍÜæêA/ﮥ,NçÑ©áßÕx$û"úùg,¼Æ7cÆŒ13gbÛ’)n¦V­ÂU1K‘œÅÏ/ØYæƒ\›©m,Ì+l£¶&?Æ&qI«‚b€Q]Dh«õ"¹û˜@Ý\12™Lš?þøƒI³_¶1ÃJ½}ûöfýõ×wúê‚ .HèüM½{÷.yÁ0¾UÙ'@«òö¶y1Þµùæ¯WðÍ}²/ Šk-W’Á\ m5¹Ë_èG€›»"8–[µ¬EÕÅ…+‡cjQÿ<Ô—¤Øc“„pìSެjûÝ£–µ‹¶ ŸAÿ<×0ïd99¹i“úÎñï¹_·œ9¡ àœÀض¤“àáGÈ‚NjªhoxÊ(úúW¨ ¦å¦ÅÌÝ©+Þeãèïvhù–%rw,C”ב,Íê ‰¨ÛÆ 9_iü5£þ‘AB(­“glVœQcÝÕKÎÌŒ©³%$AAùŠëaU¢“o%s±„A-µ(‰‚X·™©›}߯ØVËÞ%-õk¹êÀÆ=OÚ.ú>ã^sÜ‹ŒýS±–¿» Ò–BíŒ*±(ovm7Š¿AIc…K¹ÔüY ж'ä/|ÓÍ wf"í„Ø‡ÍýOU$ºWùfÆL@öèÀ¶ì—ï² ˜à÷°ÉÂäCÖVº Qí2¶¾#y1ÚÉO¦]úÖHÛ#£¾ xÜ,RÞ ]©ìØOƒÄü—~wQ:ðflÚÔ˜…Ù¢éÊ_E‘æàzCoÛô¡‡­ybV韢£šË–g,<Ó¹òLì€Â±=sÙ„M-„ÿsÚrƒ´<Å©µ,®Ã=ß‚MMý¡ŒR:çI,BºXHXjD:DW»Êï–f/h%yo<þ2ËÅXXÞgšSOf7ÑE¦ÎG‰÷=+½—^¸ }J»ÂƒÀ½JóŽ—®£ÙÄ£}"ãèAzD¹<=-yüeÖñCfSÉZAKÆ­sϲF&á¥î€üw<‹ ÿÁº~PNÕŽÉ…O–ôèð¼šêé1À¼[ã2ã\Ƹd×G¿ƒÐã®CG;–gg3föA·ÅØó’\J¸ âÚÁ¤³^äë÷"Ÿ1ª6®šªñ‰µpabvzÝ)Uê²A€ïðáÃͯ¿Ûà54uÍlåX+ý[R9Õdnm”¯`5a“©`ÔéüM#ž¾¼¶µK¹ ï…·üS žù]­ƒužœ¤‹†Sʲ°¹Èúº$Ë?wøk Ž=ÚùÛd“MÌJ+­ä€Án\ý6Å8µË‹z]é8šœx6è«t{³jöBhψ“{iÜ/9àhE,æuú‚žèú"? б"зùCÌøÏÃäûEnŽ9 úöÜ}á5 Ûí„Úà úꥢ€¾Š·3mè¶5Úvl‰ –§´ž’*ÌSêI{²­È=°K‚ŽãZë /iN½Žg§d6+ÐæöÂú°Ô4ŒoÌB³®0×&›™µX»T]á-—‰ˆ½š6ì«¢þžˆ€ò{úW€ßì[H`kÁŸ;w+/ñÔûF¬‹Á¾àóD'Kct)) ¶ ,Ëú;÷ œW£”ýÛZÀ)ûb ¾Sóã@îO"ªúÖ8Ž%Þd”©£Y{`É'Ü ¿ÙÜÏAê[¹"T¦ŽÅšôGŒÕþŒÔ´ùC†©‹„.ψK!JbòÎ}é¸×"…ÿmHu3#žÚt9þÄA³$@‡²¹ìñ#g^E;Ç¥d7Úö-Reó‹’|;æ õçx;h±ÔRE'º2ZöL.hn²ÿ°º‰Ïé[ëÛ”,n6"¼$¹3}ò¿€ÀWòû)a7Ðnª | ¥öÓWžWhA*ªåtˆ÷„¡9—Wç\87ý·[ €O^HÛ”ZIJáséìïXè—/DÓ= ³#~úÞ|©êÏMA_ÚM¦E†¸¤ñV_FFÙ…ñ÷r][³vj ´¥ŠcÜ2k_cËÒš]Æ=õk8³6EÔ¡›ÊÊlö±ä'uºJ@ÿ\Œäô©9A)úcêqJÖ,Ûˆ”é ÖV„ùˆ/\Ù"bôù.Ú!¸š:¹Xn"ÜÅs÷%Ç ±Ýl?°UÖ›Õ,0»yýî,.`÷+äo/-¾ßúú÷0GŠ Ê%Bx~q/8®ÎijÁŸfA-×Ù}D2ìÇhq¸xgæÔI¤%¬f™ûiÓ™^j új©lij]­þò²æ#|s—llâÞCßÁ&f%¬ÿ’osï6ÚúFö¹v]KÞÈÓóX‘tµ×ü“â ‰híhPù8×H~F´å>ÈMĸqãÊ‚¾zç¦~[P–ê+k—FÒîq:¿›®ü¯†[¡œòñ ÛCˆ#™¼jô+ukI˜{ðKXò,ëŽèS“nr…0Òý£UÚ‘ü'ò†vo—]Ò2Ô¿V_bIÆ“ëWS O³ üº•¯«Ë*Gî½~¥¿Õο+­¼Oÿâì¶Š{ý`ÅOV’íÇÄ7që1?Ç¿Z‹Çe¾žË^sE–KQ†©HþÐD}øíS4wv íå¤ÝC#ÓzðšuñC<ðC<~jQÊ/3:BÒƒ¸.‡ 2-ŸhfnòûÅy’Ÿ87EÂöN@ܰa÷"` 'ÿ?ÀÞ!ê`ÉsXAö}ÝU·66è»4á÷#B_,-6JHêúâXi-²Ö°3§?!»‰äY…Æ”\ àw©ª²(~ɸwê)voñK/­Ü}ýžG ûG_j)ñ|?ÒáÂÞ†%¾Ê ñ/¾É‹‰¯Ì¾™)̧Փ¦jõåMá ëõB&ûÊk"ûí ozÙÅÆTkÉp÷2‡ÀúлSå4Ò,‹ÔS; •QŽÄsjÈÊ×èÏë±à~…þ1Ň7IŸtÎU@»| ùMþfývíe+ orà¿ÊÆÿ³ÈâïóÍ?ó©‡òψñ¤l½âÿ9à¥8YúèUX÷>GݲøÚG´Cw¿gl=- ¢îtøò—tŒ§Œd]ãI©Ÿßl_æÚËÉG$@=Ê<íDŽñß1Œ£ÕŽGGŠîº+F>Q— Lg¼è ä÷ÑÙú"'ê¹ jÍáuÁ ¦>F}•©º×¼úªîÚžúêùرñ?¥ó’Û8¤ÎîGÂþ¬º$æW…ãfãÿ›XŽpu§è$ë—ì0˦‘Š­6ŽBúÕNb*Ç>ì(J¹qã|Ðì²T¹\À6Ä¡ 3ú¸ù+¾@_7o)ÝjW¹“ˆBò9Ü ¦çíCÃ>%ù\GŒ)ì€Ç(•‡@A¤©Ì^Q ŠÞ‰þ ÀI›{cÖÎßÜGBAßðúÖÇ}¿d\!±µs°†„”ý$ö.Ñ*ÑåD\fßX#?Ëä¿ýP®`~å7›´e_~«]ŒÕ¦âÚ‘>{2€w—tŠóÒw¼õ¡, 2m¹Ö_, $k½`:—­þîáYoqøW'ÊûØš¹4güÌßyÌ9Z•¯5ÉÅfXW úªB³É¶qŒÊÉ:óö0Ð7ÐÕÌvÄÈ_Qße©ï®f½¹Z­lÊO˜»ú§9H)ùâºCÌÌÄê%iѺ®;ä×4ˆ$Pqn·óIšu\Ð×yðÿÌEùÑì%Ýí,þTOHK30{6¼óÊYÍÖ6;$W„#ÕˆdÍž¦_ÔŠ2ÀméG«Ë-u5 çT—G¤Ôþ}¦lÒäñ|㻉¶¤uþÏÎàeSþ­"ìG«ÜÊ<t XíCztVKü˜…í£f]‡¥p… #V¥äbèqd½ã|åB+b™ËvŒÝ­yågߦ;åŸÿkŸ9^V¾rCóA‘ ˜M­àç/ŒOÙåTr½ï.á(¿w—;¯ä¡•d[ã4ËÇÊïyd³sØ%÷‰%{3ø-÷¬Ž÷÷Î0Ù¸|ÃŒýºÓ¼I#ßË€@>omé>râG”v#©F†ƒ«à’a(lBÂcQ"M]…1¡ƒ$‘gâx¸e$îñ씾"O¼ûÐ+‡æ¬äg¶?~oeAíG*I.Ê‚DóKã†dAä9äþÉ>cÇSí¯êÝÎÊäw®GñÝ´ËþôºÐÃèÞíqâ©}ŠG~Xª&~¶V£²‘à—ÑAnµ åc*³:'ˆìuˆ™oPž¾á™o–:Ä,l˜W<5&-é3hÅ ƒ¤>xI«sÙµ#ï“ò÷r5é‘ÜF¸yÿȵ|4ùÑÊ>m¬CÞì~¢t'~é—|ž“RþyÑ’Büj¯ŒßR¨>Fò#ômoS¹Stí¸Q®u€×©e˜z”|æ\¦«ÏS¼ß× Î®óý9WÇ9[rÇLG3)±Z¾w!`ßW2¢óÍõ€¶ZØè0|6–(²Ø¼‹Œ–‰„ÙœvÞ!Af@ &íÒ5œX?„þº‚¿üEÙ. t‚²\1¸[­ÿ"‘V‡§ñ» œ.8hûð=û»Yúþj‚Ñ.€W/xÆH6ÑÒf¡‡˜|ÀÈìó¹ù)ÙmâWб^„¡p”ÚO!Èîåÿ,ŠåQêMî~tTý0qOÖÞ{óMwÏL3õ³º2YÊç¢ WYÓ¯þt8Û¡ÈMÚ6zJާ}Ãø‘Ϋ/·Þ±TŽâËTÊìX˜Ä!AÖנǧ‘h3;È\É·gA!"54»ø‡åcŸ—ù:‡¾DMtA'z• [MÈgÿBý±YOÒ…õ´8ÙNH;5»°‚ ŽH Qa‹´Qr—´ÔðB>yeÏ„!J þqü·=Ãû’X#™UË#wLé°Î®ðÒI²Jp¿Ú.ŒÃÞuK•¦éQ$ÿëñEÌ>Ö"`åyòj$¿hÑp…i–îUU «©GÓ¥¹ˆ»bëÒ˜Ôey¿OÆÚi”žÄƒ™ìB4æ1”û3QòË‘„o¢O4%§ôWU£]Ê·çæÈC~:dUò$e¹¦ÅxÏãœpÙkÁTóFµ$°k2štÃr¤…™Ãà~ŠÇ½É¼;†êÉ_ÛɃ¨'2©Ÿÿv¹“8‰]3ÅZPP.þár-výqqÿÇÎBÙz$,Ë7,¡äÖŸÈ6çéPß–0~7¯i t``;ܱm†.ëð½f÷B|£¦åÎéÌtxYúÕì`à)yñ n¾ ޾“uöï?IßéT÷nd¹KÑo¤¯ˆô+·dòy+ÒØ‘K– \EøLWeׄGÄ%ñr%²G˜ÅmÜ<íør8šÉÜ-pZ¤·ÒáŠ:—è|Ü…¼æ³(ãDÌý÷ ÏoæÜµ‹FÑå¹ð¢ŸUVYŹ÷³€]o½õòquÈÙV[m•¿¯ÕÅ´µŒwß}×4k&ñ^œKä?xrù‘ßZPPç Ê[UQè—ˆñ¢äU.N:[\'î{—«ƒ÷9blÕ¤¾P€B‚³[ÑV«K6EûJvŠÂµêng öûØyÛ×nòñ«¿™xøŸ>»¼ ª±éŽÕƧnÝ‚~åCÖ-¶bo\{œÚÏüÚÀ~î7Výø–N¾žÄ¦lÙùêzQþfz#Þ?Ly/±&Y«‰©ÉU‰U+ÞhÑþ`rÖ’àÇ?äß™å>ñæú½ëSÀ²zßuV}æV¼%ܬ€°´:Bò@|:î„ ³ÓÍ%"m}Ïy‡4Cø;›ç»æ:œXYÏ«/£` ïpï¿õQù ¢~ª—èWç¿ÿþ4ƒg¿F浪õÁÀúðBíéרâо33PÝ–¤ý„ž68—F¸OÛÖ]@x[þG™ïQ•¦˜ß×a¹Ú‡~@IDATžêcÛk$Õ’Ûç=âfÉ9Ø©®þ‰â˜%-lèýžís±€°JÕ¹(p^…0Ý÷G‹ àÉŸ6Dô”Oì¾ôF-(|iFö­B“.K)í˜Ç€Xª f„ô8¾‹¾{TÀ¹ñ¿%þ§\ ûŽDâ€Á¹S_:ž8ùžáÄ‘­KÖ}eU@%0cbEÚ¥(CÊe¹YÙ¯ÀE¼ï#isúõ^lÉüÝÕúü”~ôuÔAi¡ =›o¿zåÂ;sà `²´}[Ÿ1¶´]¶àZdÞù>ÖpsHbŠÙ—9}y.¹Ó–ù¼µZš€—Qð´ûë äBŽ›G+–D”;}"3˜ß æ=òĽk\ §qHÊ¢ûõàê/Ƽ¶~Ÿ—/ïó™Þ¨`,˲y þôýþž$e¾= ü9¿b6cÊùƳœž"½M1}íÑ]¼™¦ik¾ZIŸ÷æ¥ûé³7b¤ ·xƒ%”Ü>¬“‘ì´E]â]ºð«Ã†Ò‡}fDzŽ ¿¾ÂŒ{EÙŒ‹"0ŸsX®ÉˆÏÕ=ñ»ÑÁZâŠîœìÇ ßk)á%J4û“v«mº1'˽Ò"žTHE-ç*: з=ß÷¸£;3®À·w—8µÓæU€+É­6ÿÔÙûñEÇ|þnÍÎ %ÓüI|?½OÖÓ‡b=,ý¾’¾TÚx­ùæ,‡¥ÿÇ£ê¤oÞâ×ö;{ŸFzm¼ÖJ¶”Ì^Ú¿JËŒ"·D:ŸB‡³E1/Ô±lÃÇtØéÝŒû+ê–`üþåq«¢žœå5:ôÌërÅ®¥žIÏñëûZ\Ýœñs ê›”ëýì¬*ºÖá¯"o×ñþqõù쬘1èï/Ó^9ésò ëGÂ2´8›8Ïòn¢,*ê\Öþ¿o¿ýÖ\sÍ5¦G¦E T’…íË/¿lþ,µÔRæ ƒ6+,¿|>fÏž=¦²éÜsÏ5 ^ù14hý(ð 9ùÍÔT}ŽßȆ¢øíŠ«$a«5¦@xqÎåï²æôåãÍk1V¤ÛËOb¹Á-IÕÆ¿LI=”j¢ ‚bÂúKœúj\ 4Ø <Ȭ¹æšf×]w5K/-Õ£”tØáÃ?lÆoÖ^{m³ûî»›víÚå#jhÀ€fÚ´iN>›o$Hí¸Ãà„îgûõ3ÿûG´iã<_guòiý."°RÅ›ß>Ö¡EÞg~÷ß$—†¹Î0ÇÆðýhç·<;mã\¯H¶Ñ” ùãÔÖì8¤sJŸk)ê›6ó¼op-ׇO~€?¬@V1%&i=²ÄûEY¬C»tFP¼'”[%iÔ½ádøÓü„}åïP$9ö¿ÁÅ¥3  ˆ×™}Øf_  P¾Vâýžu‚~ΤͬD¹ïZ±Ð0VBnP:Pñdç Y–¹:aïáÀe)HMKÏòȯÙcÞ:%O¢mèÍ©ËÃ+T·qPЮô4óí‡uJË>´)ʰM™~ö]ñµÞÏ̆á#uÉÙ÷àûí ÓÛúæÎCY²É÷Þð1Ï—é#{¦¦¢œnÀ@BèT¬H墩>æØ—ßæ¶ˆ‘oÒWß,ò‰}­Æü_>à$Kµ¢Î¯ñí'D|W{ÏÆ}À;4ÀÏcäz[`|¤4é»ý£ Œu<1£d&פ?ÊÚÉUa%·@ŠBŨés’‹™ý±J̓:1Å_Å„*ŸV}¦æ°+åË5ˆ+tb`š°QŽl76~q[8“ašŸŽÜ0Ó>5Ƽáý¾‰ÍKjWµéØ|6jµÑV/.ëò}[Ú1Q z\Ytå+þ&7r Q|®¾'ì6Æä—ä+ 5ž¯pŸ ­ üŽ +,êåD¼¾ù&íªC¦$›fw‹dßyî·b~l^f'üqtÚÅ´=sÕs,fæû]öÑßäÿñŒ±‹|ßU UTK2}¯o¦ê»é¿ä;=¨exSE»×w¤Ôµ‚ls0 àóÌšŸÆ2**ÕKøTrÃ)èáåŽF–J³Èå!»^}N‹$=Xðzš1uEnaÞ“,òíFŒ^ì89õsø,²¸wjÀßh'©¼ÍáY/S“‚”^„|Úïñ6ã²V¾KelÐBïÎ{ÏHÅ"—ŒžB–Á‚A–¶ã}î¢mdÁ)7òI|ë›V–õ:ÔMþ¼ËÑFôúµhƒK0…{оnKCZÚ–qq;²¼_[ÿFæâëåx»_ä÷xÏܘëOX–H,Q4Mž<Ùœyæ™æƒP+°< "·Ýv›YrÉ%Í[o½eŽ9úhóóÏÙõ€Ÿ~úÉôîÝÛŒ=ÚLš4Éùûî;—5óÚk¯™×_=ÿLq‚H‡Å‚ePÌÆ åç׫-\“ek u§9úª¢NŠ[ ’ãi©/µ"ù½má“™] ò¦$½›H²IßÕ¯”~”Rç`çÿúÊ+¯dìÝj–g¡Ecï ƒ*YdQ®³fÍ2Gy¤yöÙgͲË.ëÄ=á„ò‡ N˜0ÁI«ƒ[l1sÑE9 ±[£oºÉhg¹å–3¿Gu”û(ðWãBSöœ þ0ås*}çD}Ë–Ù²7Q:”¦ÿe›`Ü-·šŠãµ©*l’M˜cQ;,¾¦1m¨M˜kYŽúbéeû™èiÚJäPB«Þ‹g¯­ÿ×€—ìly³³,Ê‘ü•Éßn\Ò¶èí­ÑÑaE+áîxqü/æ8Å<“–´L³m‚ÿ“«æ‹ÔÁ wÒßÉ™ 5’tkd£9¿‹p-U(œôuÂòø,ƒH¼Š‚ %*õCÉúÌOØÏŒFè‘ÏæZÎ sÛªòºH]†w ?û߬N°O;¥_u:µ$É¢„ìÔ'tœã‡”H33ߦŸ4ò}Ýó™eú›%3O£äÉ«gVØ=š­ÙíqùXÎ…ÒJÖöA/n,w$èëÉŠR£Ã&?qÝ%ØéóZ6tñùºw®=÷Cy¼>¹¸¹»®µÙ(¨}*´ Н‡“¼  O\ƒX­6“’‘'Õ3u×äokr!¿£éô‡AôÅCÈrrÙ"mµxŒq}x>í×|Ïg~ʇ" à+Gjï’јþ ±ôór îÍ/6ë4;³\QÖóbUFþ‘½.˜¬ÈÎ徎t”0ûjÓ½á¿æ”’ï‹ôØüv÷3¦íl:æ³À<ù"‹Œ²âil•»…·²—Þò™\lHÛ¾sßahŠn@ö‘‚×eëò]ɽͺ¹÷ûTRÂu¨Ž©÷0sKnJ¼¢»ïôKLØ­,<]Dûõ»€d‹à¨ oÔÆÐ³²ÇŠì ª% ü?¾a­BÕM Æ»åeùwý¤N§¹ü,Àjèb,HïdñÃg6Íö ʾùÇKÝÈZˆéÎí~, UKoÑ6{³ø4 ·˜­Å(ùm’r² äÌíù}¼® <Ë•ƒKußQg˜œÍ{—œ·¢1ΤˆKÊw}úžúŸ#&V6 ÉŽléoa:ËßÜ—ð~¤ó#yÓß—ÊN~‘ÿapÞ†w˜£G½­\Æõc®Ó‚pµô&‹G¦~dçI0h—¡¹U–¾r“túøÎ©)¦Þ§Øit-õNÆ›ú½K[‘—üûÊÏ·—6ÒŽGÁvC–vÔOe;€±ëGÒ;$¯Û²eYEg~-ù`XÝYÜ}•ÿJ´ïvÔã_ä)WQä,¥“® ƒ†0’~~8¾»ïDžÐÛÜ‹Lž",·g¯¾úª¹öÚkCbd 0zê©§Ìí·ßP¼çž{š~X »§Y)ó²é–…þ4JËó*x-X»}êaOxÜ[ø]ÃKTí«|BùÖ°¤ö‚åøø°àGŠÓ k”ê 8vº“û›v‰UMïÔõù`$¥zà="v”ÕüxÖÛ´I¯°ˆEÏÞƒ§?€‚x{æ°yl6â,„ù¤»4_çÓŸ`s$ß|X®oåÔàBh3Ù%e“,à†2^KÁe;VøµvóÈN>õËÑ‚Éθ úÅ,”~Þ‰*ùõïÎO&ö8Œ»4ÚéOÅïìÉ~¾úoÜW•þp$²Itif\…4y—:æÿô[ðA[Ñ\îMÚ+{S*R<ÁKŸ#LÆRôvËæ ¥!éš9mÚ“PÍÀÃ#ŒG¥kÇßö€`¯3ûõ&ùõÒBtÀÒ!aç t.wžÆÖðš«Y ½‰ZÐ:@¬Ä½DÝa¼œzöû” ,©í®G¿R›|¯$,`¶`Nî‡<+O¦ÞB.s´ð%})À¿xŽÍwœ¹ÿÊi2_Ìavå®Ïa1ávÍ=Xg.WJá¹f!µvÜ>XÈÁ¾²ù£,ÝÓ~è\ËÆG¸ó±q=ø”PÐ/âÚ[.ꢒvì¬Êxû”Þò9óáç>ýÛ//aAÂTÜ3FG’ßD®ùÖ'óéš Òr¶}¯&Üo±AùmLO½ ûÿ!£Üþ-°*®KúnZŒñ£è?/¤gùõ?ô-êêì–ö‹L˜r¶¢ïÂßëÔáeKÖ·“H'{}ñ1æníþi7T³cåkÝiGåXõuèÐÁœsÎ9v=Š®]ÿÁ[l±…Þ²eK³ñÆ›áÇ;÷v;´oo¾©¯7~ú©éر£ÙtÓMgŸ¾üòK³×^{™g^|ѤR)³õÖ[›Ö­[•v£ƒ¶~ç“gÙKv‡:›ÈZS¨õ#eù•æ2À°zȘ:‡7½4 AJ¡ò”õqÐ!¬üZ?+À´Å9{¿ÉR¼÷Tg¨â)í_ž0=•ÃîŸh!¦<‰I,Šà¬-gj?õ  !ò¨Ë3Œr ¯ü-Ê—”K:tIŒHuðëWšä²lÇMQøó’?ùíÔDù9 ¸º4sî ñ*¹Zh¡…Ì /ìXÓg`ªZ„)ÜKß|ówƒ 6`²_ cÍúë¯o† æD•µ¾,üåæAãOy)o‘Ü;ˆvØaóÉ'xrdœÞ|3˜Mɵ¹c‡^kGíeƒã‘}hÅO>PÏçù?AsO  Š™ÈþZ•X.Yw,#Å<"™#„ÆL}W?ºá?$)ˆ:dC$Àí¬*N Xp¸ðíS8× LØ¢Í^ÖfØ|¿q®`òÿ†ç[a1¦¿nþºVнÈãE„ •g¡N¦ü'Å$“zŒÿ|fŽÌ[N4Éϱb|®;~˜™ÿz°+ãê$¶Km•E1-a%7A¨| 8¸†wa‡à‰›i›~gÚ u®˜š÷ ^Ÿ£iÜRÎÝîpÙKÚ’XfI$ÁÓÑèeúÄú€¾Y $Zš¸±º ,.Ïû½iõ7Y‘¿\·”£°æ¯{±°’?O‰øÝ„˸åÅER>']…Ï7^òd´èÙ†²/þ$wûÏ¢Ž%¤>‡oÜ :¬;Øü X÷JÃG%у4&–ä„K”—k&‡ͺÐ|ŒtRʳ μNã/-J\Ÿ·ÊæèÐÙE—ÜWù•5Ö¿¢å+§>y"‹K~–ánjÅYÐW@Ç™ô³(“ƒh]Ô³Ž|ÇÞVqÈ}µ@™hß¡¼ÇØÈ)Q2•lâ벜Ï?m†|}è3dß~ç7nÐíZ€´H ï›ŒÍ}mÊY1[ÉJ.¡ïoŒeÝnðè|ß,‰• ø_ÃEXÍâËi. çÁ`Yµ}…Œƈø¢‰Í«WÁ$zÒ?_Œ˜¨ÂhÉÓIÈòTú‘²ÄŽp…âÐìãøið»eŒ,[FÜš«cKr‰=i׉¤ü,´8ÛVpĶ|+æ- ¥²z‹WòS+³£]_肉BÉÚýÒÿ^Š8¯Êöã€MoýË-*Ö¹Q{¶Kdz§Â†%_”#}Ÿ kþû:ðMõ,¢T/n]3œ¢'eo¾,jęȬ3Í™™„³È^RNÙÜÂ#´äñL)$é³)ä=´ðZÑd´_ö›µfQ ±I8ÂíÈù²€>ÓOæ‰Uø|w±ðûL(¿ÐâíÙ>eÅ}cU-Y† ?±À::æôéeÑ݆X…¿Kã¥^(žÍÑØC2oz£æïåc÷ ܪ|Æxвø»ÒÆ-XoW¢å³t$¹Þ̹o¡³¹–Ç…§ÅWX(銎w¼¥¿G^°c _Ô,~ ܲ¸ø.w…ÍÞÀÛ†]ŸvÚiÎã÷Þ{/,š³í»mÛ¶Ž/P7¢€#M"¹x¨ÇÒðßçŸofã.BÀ’¬e¥èU×]wéÔ©“W`ÓàÁƒ´Qþ›âéPK°º ëÊß#0GoþêN«!™ÖO$–@º"õàFbÐ~é'•I§<]?&aùÏ-Ïb°Ú iéÐÍÎÇ^¡AÊÇ‚ŽxŸr¬týÞG> ÿð´âD«ý$‚OµîýÚÛÎ×}îÖ[ß4H„öÖÛÍO«´.óp]É–/ù°«þ$áÃø›7on.¹äs>cK–¿r«²ÝvÛ9nùî¯üþþñÇŽþk´Àâw–új¡FïuñÁÝÌ;ï¼ãXôj *®¬ƒES§Nu@àÃ;Ìñ¬°•VZɱÖµCé¯øÑ´Vžê­oQ>vi múÚh~i Ñýý¦DZe̯}Ó „½ÝG¸r#Ÿ…# Í^$WÖm /x:Ç.Ä'í]„ìŸf¾g1Tàñh„öEï3Éw5Ò¹ÀK†ƒÀDZMö :1·à©C•/‰·O ld°‰N~ú/kýcþ¾pñ-Šâº˜ª/7Ñ•zÇ*€¾¾ÄïRu, Äß‹ûˆMýÀbߌ¬ÀÄALFRës óøU"gI:}8K+ó3ιü¾x0¾×d «mk²¶×ÜãgE”M[æN_·=ßýÌ2 ƪ§ì‚žÓ›­ùb syÝùfzÃ#\û@¤ '2v¯“9«u7¬Ìïùl»“rw, 960¤ð‘XZô§½¤àZ$%:™>Í.˜îR&çàÇò-w&‹(‡²úú¤Ì F›}— \éÀ“—À4™'=<·‰µ@>/åä˜a<ˆäDY ‘œ²üç,Í&âSnTP=)õæôÃÁ‚‘§º¡·éÿ„>ö{¨¶-·˜!0ªJ“$&ñ.…Ý~96N˜¶¸k÷2º…«¶@üþî?ß~‘ˆ\Ю­üXoÃVÓ‡I›]ö›ÛÌBìÚV{½‹G— ½ÍyÞ㛹ڼ=éÝ3ï8Ùó Ö·Hâr᤹vÖ$ÞëÍÚÐð„•×ò?šûxsš•ïå҄ʇô ÌzÒ£Ñ2€WÿfŽÀ· ~µe|Oøõô›‡—–ÁÓÙðîLØ2_v+¹vt½È ÔvóQ¹àû9pWºèP.¹m“ÁÖ‡‘ò°ó+¾^Š[¹ç˜’lg®M®FaN¬øÕ!¶mÙL#à4ÀX\ƒZܵ!“lÔl?ØÙyT ý<>§ó æ2ÒŽ´­™‹dp·9¿—þ¾³/x%£¶ô'~­s¾…¥NSÔ!Ã'–‡HÆšéÛ:â#„`§íxnë•SÿSs;2}3´Ã@ßE‰'i·cú}äOÉA´-í·¡ãB¥oŸpζpÆ. ’µ0w÷,aɦÿ0¹‡ý…Éq3ÁsÒ6qø&ZwÝu͉'žhú?ÿ¼y饗Ì1ÇcžçZ–†:8jß}÷5O<ñ„yüñǃ¥‚+ä~%+Òñe„« ÄÀゾAyiX—}•6êôèWÎ~si˜ ÿ>ǤR‡‘Ö6ýLûí4~ëìÏõ‚ÜËÿJ\ò¦òá¬|y Øqi]B6?W0_bä¦ ÿU;ØýE‡µÉò¶=õs ¢¬å?úè#óñÇ—d´ãŽ;-Ð\ÆYæÁ¯5ݺus€bÇê«·>ÝÇŒ1Â~<ð@'îC=äÎõ·É&›˜·ß~ÛñÙ­œb f^Åñ*»“P!Òêœ½í¹²ÿIõwj "Mr×DR%tªï³Õúq‘xÄZ‰µÍÉÍ1àî@Šˆèq’‘¬îJÈ邲V®ïϤïß—þ£ôUšçˆÝ#ý{ 諃Ž3-%OZ•.ËI-^áv¥BPÑæ¬Ë ©/ •Õq£SòlŠh×…ø:ŸâÆö}I¸€øµ*}õNÍ ¼5²yjÛèÏp­/“[’Iê—Nº¾•æJ‰+P êÖ—2h¡_ùÓbÂOüö_žŠµf)­É·îO0ÀIYú@±ìòSná¯Øc˜'°=k V·1 bœ¶•µÏ/v‘e¯Ç²ˆò3§æXk§åü|d¦!œ…Ê•3-<¿†`½yUëU’ëÓß7³<—‰}øî[ÑŽ(ñÓwäáHO„ênqfKHùÌs€âäQh‹~ÔÞ/p® §û~°5|ð‚äb‘}ïe_ÀoTÆ5ɱÚ¢±Fû Øv§Ž›Ðö~$ë-1?ZŸqt@ý2MÁ—ý*ФašI5§…IþµªÐ8–«k¥ru­JÈæ¿ÁJÛ@›ŸD¨¨LÈE@²—V”kQ"é:_1ÿLñq‡W±èf2²Å¼ó“E¡Þ›åœä?ÓÖ±¼qò÷ 91ëw!ž «KíŽB[RþSÈaÛyú›€¡ï£d‹#] ±¶%ùeÞ…ÅÞ() q´Û¶q­ÛcE%yá_…‡>Wç´žÏBYyZ Yâ>æ…cJ¢žˆ|+ß©V!w·%×æÔ½ y,–~Â$fŸBHœÖ.TKçÜŠLtüÑKôÀÐÝ@Þø±îeyÚâÚœù:G÷òõŸ(’¿Ý'q‘ƒä^©h\ÇÍ£qâoÈ—S{_Ì\¶cÆk˜§Råsx…€¹J.÷TM–ï‹%­\v•¥†‡à¥·-â²4)äŸw :Ú¸WpAÜò4ÿúÑÕôwÉêåHüã)ÚEnäÞD ¯tÓ H«Å/uE¸•q”pÆQa!E%Ùš€7]Å÷‹PПS”cîV>z›6mÌï¿›Þÿú믎¿P~衇æë ËE>õàƒ:Ö½˜tˆ”KK-µ”¯µ¢û<ʯ>‹+N«q´âðM€ %¿jâ –ÕdxWS‡JÓj‚èPnˆIesÛ»’²´Ž犕ԛ *2Óø:‹U>‰ÞñH¾€¿²Òy-íÜT'[•%w;B4†øˆ:pŸáOM%ä}Yè €ÕBŠlÉ'ïc=f\×+n9rÿ ÷ñ^½Ì'ÁrÇïDyâ™gÌ{ìa.¿ürç^îZN>ùd ^tQ­Qá(ýØcñÓ½YsÍ5MçÎ0÷?½¿Àîáezƒ8õ¯›7Ÿ ß}XiËBbA1jþL§-µ-íŸÜ³$ôÝÍd»)BC5$^w0`Ñ£qõu°TáXûn”h(”å¬:M6‰By]êTÀ^ÃEÄqàÙ~¶‡#­&KÄ‘•ÇãX?„õ¢JO~âAn®÷!пÁÖð"?Uu(^Í7g²šDŠ,µãç7þ²–c\d Óäåú =˜±ÿ #»²¹`92˜çá(÷²€J/HùÀš™oŠ+PæNõOðêYû’ežcuv÷î›I!Òž¢‘奊ä.Ù|d•á¥f·ò>Ì©›œ'r×uDras|ÎÞRhùW"ûõvÈ•å嬳4‹ºíÍNoqkTl÷|!'iOÈõ\tçÅ©áY®hOi)BVy Õ̇ԑ¿F!¾÷ì>|Û‡É]ß¾r*'ƒ8§ÞÓŽÅÔÿÝš±ýTpúY̾Vœ·ÉY{û³7–}O?—/éYgHùMR:– §i»d 5Û; ʧÞåÑw%« X˜ž°®YÄÜ›³&w@(¿…„$ò}‹3iÆ=)2‚RžØ•1캜øn?®¶¶áéõåÝmµË2¼#Ë_ ü¡n=˜à¶ô®›€Ž…Ï—úµ„F)Z–ÌÚb:)¦<í›wòH‚áuéÛ}ÏùÀŸé>§Í¡j,A¹‚F6Bù@ƒ I{õãMPd²Ô|̓§ÀW,v,Y¿¿Ž<.à´¤Ùϵˆ’Ÿf`í¾êÌ‚ÌúÌ«}ßJJGø;'I=%/§ùTD²ÞÛ´ÝdŽì3Ó3™O}b‚´kGօ副Áî2¿¹ò äVùV•_ÖJh7tïތ⳱ªFÒqüºPúC³À)skÆG® (lõè‹QÅ7yÙ) b­ƒìMZÆ •I£µ®NSå÷£e3 Yöb±ær¾¡˜ O¬B{ôìL›8X•“àì›6Cîyýû+¾•ð„'ùn®kÛZÛNSz=‚þQZ.DøÄ^È9«°Pò .õ¢ÚóËÝÔìcF²êÎäŸfx=Ÿs%(ƒÂŽ?aÌh7³K-¹Ðyçb-­ÝŸïaHà’ži鳿 ¦%ÀIÖ€S¦Øß×_¢ìê(—¾ýö[çríµ×6ï¾û®9õÔS¬†]’ŸÑZ‘rS ¯ÞAÀß¼DêL;›ñû£FÀA9’¿Ö(SDP>.(ôÜ/|uj­e³CÇ/(a6è[.¾&&{‹«úÝwüÙaR@$€]Jõ… dW€®[MoÀõ‹Y¥Ôe(Füù‘sˆŠXŽFÑö/ðÝä‹×&)mîGdZæ}ç®L]âKÂy̱dYÐNæl%èÉ+ˆ8Àä ¤O!pK¯%ùŸ&ýõèA1Õ¾ri1”1´+4»åb¿2Õâ®;‹á¨òkKë3N+@ÎÁïù uC¸z4O–Ðì3ˆSIp­&ÖjökvMÊMßCy×”fŸ~¡æ Â#€¾Nê|›h\iÕ…x!r÷S¬kÍì=Sùÿi:bd 'ƣʼn.ß“d^wwâ UPb)¾S»*2˜“&däQ^pÙFzIæº&Zhh¤h”lËÉ=n¡Òn Ô6éZ‘¶©k{ùÎðŠÓ±]>2Àe‘,c‹k! C`mE”Ø™ñ}1I5âuP\ç/´AoŸu&;±á~a1È!¤Ý”¯X²`pÔiÌ W’çÀ’(#s^ èth[IO€,DÇüéWÈ’x¢ÀµÕðÛÚ¦ù‰´Á!žá·ê;:Æu¯»–O¿@†=‡6z£–™ÎUy©çÙ2£ôõ·cÐgº¦§™~îl–Ü™‹…‘ÉçïT¾ïµìPô’ î§ït=™Tç5“ä ÆDxñ'”}9F›sF‡ˆÏAWÀyòoK®ÈíÖ ‹:˜±u @ø}®[1"kwç®´“wó²ô÷­pó ­LcÉöí-t¢Q€ß°Ê7Îñÿ)ðGµ xºúê«ëÁûï¿ß9°MÖƒ¢víÚ…i¹ÒÝsÏ=N|Y ®µÖZæý÷ß7wDÉ?iÿþýÍ”¡„íûlí2à‘o¢&´ýÌÒ=™,ê±’ØQ„3é&¨šoZkOÚ‚ú_ˆÏM #«ª…4µ¼ZÎüd ë"a¥ˆ©”2–°µ|¦:Úm£ƒël‹¾°²Ä<å—¸°ÆS{³Í6s]4¦4߯Xàî–[néDNŒ›§²ø•ÏÞGyÄYh‘{¬(«{¹\éLšWû<éXâ $îÙ³§“džnèXáËo÷u×]iZþ0Ô|òÆ›fÈ!%òë7rͱ¨ |7»=4‘o 0FÚÞ|iÂUXž¡Ï’ǰ‡â¾—p¡éruNV$3ζl:òÍ-0†Þ»-–acÒÿMfnÁ£ìˆ¬WÜÓ·gÂÿy èŸÂoï³n•BÐR]$ß{¬UâEÉ`—±E£P¹J˜fPõ±0ÌÒ‰ø=s¢Ëþ¨=¶¢¬è«ñž¬fø '°éo7d¬êp» ¹Î©QúQ¬23wcYýŒßzQ«-KbëDõ!ˆ·75Ó—kãŸ|)¥-ƒ9’5Mv·Œ1Ça%Ü¥¦Zz ‹ƒœn,¯VJ'rº0¹CÍr,~K)–âúäk—_§ÒýRɾ«»åã6Öþ(d€™5£UPF/4%—3:$>I‰´¶,±ÿƒrp‹Dò¥å¿µ˜ôŽölZü´ê;m½nÈÊáyM¢/…G±ŸâÕ4º»Ê ýl\¯ÂÜ´®qç‘ERW`J§ðobÖ À4©«•b¦‹Ýî:˜óEæ‰5¤‚×m@FCKÙJ¤n¤?2VÊPw;:³ÆêebÎGÓ¯Ñ}/å…Òþ¡¹®tøS'fqm®‰;ÄŽœQ€U‡bUÿ.óvUõ•Egž÷è:€ã[,æÆ®mÂ(¿¨ž`±¡n}¢fÇ·@2É!:j~"ù@ÖîÛ²²d—Ô³ï„7ýw®|]é{—°°¹ä\Y»ðJInÙ3\úÍg þèöö“Ð)îÆPD -ÒRîcAä+ô•]Èë=ú§@J‡R}‘Ïå²Þ±^šd»Ÿl´)üÜ‚<{ÀñîŒ5ðÌÆ°þO€u,ˆîÜÑ ´/U#¡ÑI®XþçʓѓEŠ) ðbÞ¹Ÿe$§0÷ ;é„rmq€l_‰”içz-÷ "C. 0ˆ$ ¿âŠ+Àè€p¬{Ï:ë,g;¸âë@(T:,N “, o¸á'«Åç ›+¯tüýî¼óÎæª«®2›|€[LäßÏ=P¥ê,àln¢YTFfÜÓèPîaaQë'ûˆJÈ#Æ–ÍBÀë{׎¬¶”eïœ$Òs’¼¯†dý °ÎBD3ˆ¿à‚ ¹Î:ë8®Rúöí뀳g3î´ãÌøÂ—#òãt‘E1gŸ}¶³¸²Í6Û8ãôÚk¯5:tpò¿‚Câ:-»¼ãšE‹6ò|Ë-·8 7ò»-_²äßrçÌéçŸçøæ¬˜õ@LM–Ï"D6Éÿa¹U²ÿ  ž†FË„=ŒûLTÃÙqSåã·aœ¸ãn|émÞ]ÖqFOâ7P’¯¨ÏűÔüNK õ‰RQPÙwwVl-ŽØðán%YçÊgSš\&Ò2ä%¿½{‹t@Å•X‘Äœ sek˯ƟKâa “ÞƒÊíönD`»(ç-:s³«É¯ÀèÅÚËj’}¬LtÈ—€}¿¬âŒ~t¿¯hßÚ-žz‡ì¬qÒqÈs´ ³ÑÉ…ÌpÕ#Û;ܧ•ý~Äë|5 ÑÚÈýDµ¤Å€ÕðëkÚVÅµÒ Çy÷é["ß(Ž`ߥ^†¿?NÈ8â½g?©î÷-òÉ»aº9j‹e•q:Ô1ˆ#¸ ”·j;°s8» 6­Zúðæ^|/àA; ô%í3´p;Ÿ^]h"ËÓü¾ß™µyËÄŠäw8«'«úæ'y~ ÚZsüÙ,·/jwß$%²ô{1êú Ô¡QûrxÜW™gà݈_nf)ɲ¢€;i¿¯“aŽ×›ô5•Ä*ªOÓ$ï¡(@º<-‡-¹R ü¼MÝàEMqfB¥­$€po¶QûŽXiž?ðXX/„WÈúWô’ W£KŠ‚Ø‹1¿±ó¬ä¿äaÎbÐYŒ÷λŒuœìW±ŒDæÚ+D¦;”öˆUogié‡ßÇ“ê;§í »‘1ø“s7ÿÿçÈ9Z0¯Ð"^s­ÎIš‹«mAíÒÝ“oìîr­6¿¦L¿zÃeì0 ’Gܺ¬‡q;@ï¿é·Ò%¤Wh±E¡ô íj^KÕÿ+éÓõ|·!ĈN¥F*ž´™¸/LNûÉ*ÌGÍY¼NžâIÔ¸·‡0^]*¨$Ùœ€|­ƒ»ÃHàÒW˜*zõÕW_û¿ÃG0ÉÑŒ3ÌyççÞVô›É |àoT>mØÍlÖ¬YP¼ä’þk!²ø8¥Ã¦ä+ô ºà¢o¼Ñ,°€ ¸†ü>ÖÀ;ýgn|$k`Yñx3kGxT×ö{É_‰¶Öûv;b…×r 1‘Éof…é•,°óV‘gc$]ŒšŠÁÙ+«²ÜŽ â×ÕÕýé@DÛÅIP;uêd6ß|sçqÐ8M§ÓŽ«-¤´jU~JÓ8ÕAo~¤ÅœéÓ§;ÏÝE;ÞÔ©SM§Å[›_Z¨5 dÓ6´‹½eYãY‡>Ø ”›²wo) ÅÆ·Šcf-ßauqí0æ}ÞT÷âPjy?ì(¼lù¢Òápþ†ápwÔܢǓÚ&¥–‡…JÈÒ:û¨9B›_uš?ŽÄp O¾È=¥&‰M˜Õ°›é.Àó“±é˜úα¼­öZ“Á1e7!à øíµ¶þøÑЬ¹_ÞòsôÌãáûßûE) Øãú Ö÷èøŠRÝ ì&‘;¤ D ž,rÈWÞIew"dõ·Š¢yóe\<\Ƕ±Á®ò™kqf/x-ݰD¹~ãZfËšüJɳ±îžJÉ[.íθ•㲂·›`^úÕÂRó P O¦Öõ5¬¹TŸ,(`gª¶ÜePVþn ì»åéqÂnÄ%Ë)€ /–|µl.R®üæN»Œ ëí t¢{w³†ù¥î`:ÆÕDŒQL:¸HK†r]œ"æ]p0¹ìÒ„ê¹+ð7¾$Î)XMÂÚN¤UBR°¶c^}”yØW/HìN¶Œ‚̇NöK±O©o²¹ï!šÑÊ—ú<ÅŠºs63Øhõ2æÄ«å¿²ÙAæ„L;3X¶eHcø8”í{Ùm ÝRGp}×~ÖBe²ªñãUx·Ž¼ÛË5ÎwÎN<¦ÅµðäĉM¹Ç¢VîççQZšz„| ^qk€%ZØ«­I¿uýuÚñâf xÕÇpȼ… !ðzi,Χ:sÛÈ̬šXXð˜–Üþö0qÆ”Ækv½Y*ÑÖ<“º—¿ÉoZ¼‘Kƒ Ãõ|'øßdøŸwçÒB !â>ã‚v’bV¥²d(0CY‰&Ú3 >ÀM¥³–2jÒÒëÚ˜£SC'‹S-÷|'Á\W‹‘KÓ×¼ò¤·z’Ï@nŸÄ÷Õ®ÀVDX’0Û·¼,Ÿe¸æçºÎ›_ãÝ/ÎbîiôÅQðܾN1Z°nU›ÃõJk.þع|Z&íX,ë Õ3˜§¿d7݃V“\Ö>z;º\7|¥¹…‡HÚœã$H‡³yAßÔL…Éj0ôU嵕\ ¯:Ñ:”+ ëY’}ä†+­â/ÜÅÉ(W€Î²t–¦ Y{A_•[ è«tß6"è«ü5 ÍÔE…Äú¥‘¿Þ¦&}ÓxRž£’¬S]Ð×µ·ÓÄÚØ$—(åHcN¾¶‹ú*¯ ÐWÏ´ø¢qìú깞-Ú¢¥.iAÏx‘ƒö0`Ú±¬¿Ì€Ò$$ÎiÐWU;⤜¥¥·ªqŒÞsôU'óWKÐWyކ“Ë9ÿ4ÝÑl¬%ò ¯"Q“9 úJtCI¯dAÖQr·D[Ãî‰h]­Å¾”ÿ…à}5gº4‘–¾xö3”ì}ýßÇñÁÖŒUqv}ÿÐï”+duÆëÆ®e±[¶ó›X €®\ŠËfIVä—:~=ÝÄ£æ÷s³4Ö*¿›k/Ê‘€2„[K:0®ù6 %¶¦M7k”¬•©zÖ.€…^!]Ù3Xº¤å½Ëi{-o€ÆâÖûjîžÌø”6ŒÅ3ÛE¬™æKÐW “A-kx ©«µ¤RÐW¹«-ϵ@_…I)C¸Àõ3øŽï9_AOJ)lîTlGq,/-JÀÕXðl’hÎ×W?×øñïïà/?ø€ÖÞ<+¾¯Û– ã’1ìøN}–8‚Šiuæ×ö‰´ÅÑKîtÊøy€­ò íKrW‘}õ|jó;ÍQÌê•“ ú’KÝ>ݹX+ÁYæ>ífº7³˜/ºHà˜äÖV¼Ã‡|«S™gæ<諊¥í^Žð£(ò ÝПv™˜}é̘ÂÌý¼ úêE$UìÏ"þ*}µ|Hré¢y\sÎCÈr}ð|][³+‹3‘)ycª·Y?±²¹”±ý8é+±ž\^XDí2Ôõ}•®á&3uöEæÈôkŠíµ«#ôUR=—Uï¾ÌãG"•#ÍþgO;¸tP–À.¹Ø‚ß(¤]:W" iËÿ}ÈgººO‹öf»‘”0Jâ†ñÉåþ…‹.æIÒÎ1¯<é÷"ê«Ý™{Ô'Eúß}¦®}7¤Ï©ßý‹yëˆ8c[™@I6{ïþ¯s#ö)ÛG‘ï8$w;x®vIÊ]Ö¶XÛ‹OHfj ¼Ý{ÐiT÷ÏfÇ–{¦‹[¦Úy»ôTG”µK[r}c7ŠDP¹4ä–Öˆ¿³È{–£¦”/DŸBBÍ_ã»9ÊRV ¢Òéü.Úq³è·“Ìo¿ýæFñý•åe9’Э‰f¹¨5y®•ùïË©šd9Wg"ë£Z¶-K¬ÿú+`jA–­‰ÓŠ?Mmå4½(®@y—´:)$À¯îJó§Ùóë]ñ<.º•]-˜D!?Ëà G(Ú2®~õ”ʰ2mßÕ‚wfX‘åWK[=\s°`“êj÷oY¾X iç9§¯ïÓHáµçtuæšò~ޱüÌÎ5 «HÝQ<¥×¦n‹åûl'8ÂJŒC§?øÆÈÊ×÷×ÌŒq¨'„æ7›N`Ý ¡{7ç„Øf&]·ßpD¬½ápO9K.ˆ­>gÐmijz'¹ A?յòü=Ó>ƒòIXù/‚|—܉³ @=‘|®Ë zO³R-ÿ£}ü¾kæ]&|þ,B:Z ÆJºžÛ™‘ÿØšwÑÁzÏ£tVL‰=`Ø;óiOw`鱉ù”oP_v¨ Ää®$‚Ë¥þWAb7ÉÊÔ÷ ò¸“€ßÝ@ÿߺsˆ÷ªù_æ ,¦ sÕÂðãµè w1#u¡íhùÓ¢’ØûÖ§gb…ìZ&GM=ÞÞùÖèÑ!æTò<'·àQ©?yUK2ó](C±(‘ï8›ÚòTòô…Vò“J¿çÏ‡Ä Ž`‡Â°ÌlóR„EŸ,Ê¥aÖžxKð7Í?~š83eìM )çîÉ?ÃB¨,|†²+¨¾ˆžÛWW£$ÎL½ÄAŠßØÁÕ]§ž Ï½I&åo6óDœ•yÒüžúÕ<!wùÝ ¾lsÒÉB£ìÌ|å=T&4ÁÜøP`¢üﺹ¨v|åÔMùúhá3ô}u-?^>÷Ú^\F]Ã\°UjEúóÀEð"ýº¤yB:Ãäÿa©™Xü–[ærSò+P¾ò¾@wNý‚„@N™[8+·hìWJ—3—jÁÐK‚X5ÏÛ¼MP’摌«Î€§}‡ÅðÐ" ûÜû¿ÜiGAR}tÎÏ™Ü7MÝÃ7y”¤“£$¯,N٪١Ìg½èƒcåñ<ï rÐGX‡Ï$À~WôKè+µü2ºXœ~!£$ѯOcût,ƒeEü#ÜoUnß í£ø']êtÛ‘IzP„ýèÏêóÏ#'¸‰¥dá{mò>úÊKü]§y”Å‘9}F©t6úýÕ)UK:oB‡?JÆHOr…Ÿñ—êÿ4~ù¿¤/®…l"–¹> õ e4þJǺ)÷ß\ üÚ-w­×´·ˆ—‹ï>·A9…-±Äfíµ×6Ÿþy(øë¿bŒÌ ê’›sÓÿJ±PÖ40sÓ¿_c—ØŒfË«¶]­I3+“ôµ°¶ë¸4qå£Öxµ¢gßÛñu-`x&i‚†mðW o‡¿r£Rޤ¾J«­D³kT.÷Òç’^éÐ=ׇ¯bÉõE]®¹|Xˆx lj›KãŠxd‘³ém«©]¹éºå(»²¬~ÂiÒ×J‹"û4EÖÚj®Iß¿0¿Óëýc–„.Io'Ë4€›Nó5ýÞU¨dYý…Ïb›“ðû¸p‰U¯òò³îê*ð²[Vò(sFÝž,"xàÓ;Ù ôxÑT³Î_°“E :™ºtÚAÞ§I#t¤óì'þ¿EʦÀy3„” Þ*àXyØÔ§D™#ÿT?¢—c§ñ^k;Ôi©3 òç…¿½±í{\K%i“ôùÓà«v·Àm'°–ÿ¥ºWŸ;&¹m%ø5øÝ‘|£ºõ±4ø…†Ó’­ö–’~Âq ¥^æÑÔÐWñe%±lݹæÈÔûôçYø=oï‡=&¦T%#è±q](ùÕa·­Žö Ðà+WJÚ¶($Õ€š’ .ÀzÔÝd¿³,¶·aüÃÁvŒìµ`ÖóÈ£‹Â¥¹çB´ƒcÖn$ª*‡÷TC7c-£¾w! S^ª§=Ji‚´‡K€s–¾§Ž³Óý¹—¨MàÝø³Hcsgxi°Êï ¬,c_Vú²¨è¨¸±‹®aø$Ûè³PW­[¨újJ…—NP‚W¯oÇ\$ž*mbî¢W|ä’ZÔPýü^Ϙ’Û‚ý™ŸÂ¸B`ÙZdNñ½Éï}6±ãñmfY¿Xs[XÐáÌ1%YâK}ëTYò×],ÊIræ8d§nnÃ"¶ò•1À/qëâùKíÙmªQ´¨,ËÔ»ý âe5ׯ–«P˜Y ¤v¼pGä”àd}‘™£ÎŠš‡_øÔâó»ôÁ¶à-QA_5°¤ü¬1gé{hDÚ@§*…?=…<99H¶ Œ>&õ#îfŠ9Ž0‰MdNß_…únÈ»>$QZrirÁÔ™´Ï¡‡éýD> Ù¹ s{˜;Wfl-B蜑—ÙV¬=––©ùøõ½ø¡Ê:vìèø5j”™0a‚™9;D€&?«¢1ü%iFu$?R,Y’Ê_nc“:²XÚ?µ²–Ö”ü%³yþF Of€jëë¯X¹„12ArŒó=¤HáÅ7[XÀÿ~à¯r[~ùåÍk¬aÚp·µ·Ï²·‰ã¯V–*Ò”•O”µ»¬»–…œvËŽYå‹uêÀB›æ²TYùI­ò’Öó±¤*ÎÉ›j½¯û7³ÓãT>@ÀŸyÏ‚`´¹ø“k£¸¼B£ƒwAo#˧+XÙ½€qxðX»:Q9kk‡V~퀜ð/}IØÂù‘á}Z¸oÉårð íQz…|êg¥7=g½bnÂbçˆVCÍíAðä—0ÃëRæ…tkÓÁM‚ïSp½Ýµ”Y¸ü±.‡1L6ÿ0‘e™hQþVgÞóúé «×KXßÇat~+ÜN¦Îp…W9„»‘…à2Wéæ7Òç¾b<ñ™–fBØVМù$û9mm#êÛ¾ÆazòËëµ`(”´—Š9,[WÇ Já©ßÕª|ËígŸazñm%;Èç¹€¼'-Ø/–ÀåOw#×9–ñ•,娭ìqh-jÿís:‚ïä8 çÖ¸ÚÞŸä{Îê[CÍ¡¿D˜õt¨ÏÖlQt-î½™iÛ¿K§³¨ó!ŠÄÐ\X9Ð×$ϤŸM0d´ãO:¬R»܃ÿüc‡JÊÊ®d·Ú'²ž ^r÷ɽî,¾ÑZ|££x˜åÛw4!00ÒåÝ>U›ëƒû#ŽžÂÂõTubÌê"Dz¨dÒcèOÆL뉞¾‡|µˆ®{ÈÝÀí쌑lú@L¹d޼nb'ŠÅüÏr¡#—k2‡ù§}þIg¬m­¯a¶¤Eí³ê››P»®r+æž)¢o·f‘v*ßb –è÷û|“!Äâ1BqßÊ=—ĽûU/è+Ò ÿ¨~òmv(àv[žïÆrç.|É¡ÄQxÏÆ¼Ë¥ðª‘™)óòïXÀ»cQåú‘x@k¼Ë$Q¥æ"„I‡é|Eá^fí,ÇÌ^2:Ý%,Åß÷™„m ÂZGÌÍ»"#;¯ E’À!(Õ¬Vׇ!ćQ«)5›Ötu­?Ò&ÒïÊ™:3°È†œqzÓÓL#³™Tõ¿ÀA­ÑF¨bkån Úlœ“®¸pï8ÕS½«„éûi/ˆÞòÉ3(î<îüöWôÖþsoÀ¼qß`PCR/¬*­b‚³NJ.ÊàŸÊNÒÑrÌÆÚ‘¹ïj‡.¬2ËúBôFHÿ–_º uËš­3[#èüdŽ©Pɪš÷2ÍMfúž”:Ú)û[8æ–©ÉÙm'$»Ùºõ|rÆŒ{¹Ã"Í"¹§ÙŸ±øãkƒõ²kIRôÐºÑ ùå>‡!¤i+âãŒÍÈa–Y•´›!8=ÇuÙùbö%¤)˪™.¿¤˜½=aÑn÷£^r3ÙŽ$OJE_ñÊOÑr“c ã(ÃCú‚ãÚíq¦aX`¶ê·²Õ‚…@}YQ=Š°Ù…0Y–Ã7Ð!9Õ’#¸†Æå_‚Úª jB/aÞ?…1e¥ò$"E‰íY _æpPÒháI>Ù¾’¬ÒµˆéõKWÈH1ôµâöµBÎUêV&À…=…[õ«÷ …§Ù+-œŽà[Ê]J9Z‚{ ¼ü,¸¥7±#òƒ@º,o0ÉåhòAq¶Ç‚E‡  _¹ü¬\]ÜçG¢D‚‰Ý7Ñ$ 7ee¿E}ÎqíBÿinó½†ésí6G’ \‘ÕP~Uˆ9÷]‰oHÿ«™ • }höSðœ,x<ùµ|²mÿ[hßóK/lMÊÙÊ<€ç‰9ıf,.ž\· ce Áô™˜T4zeý>yÉ19Š™QFß±Û™±¯3 \Rÿœ\ _µ?T|Т›W¹ßbKÒr±ÃŸ«Þë1ÃÈߺɼ…ò¸¿þ rá9e²þ hµ:œ ;2,q%ói·W¹ÅVY¾Aûˬe—-÷p¤²—˜‹¼ckmò¹™ëmæýóY¬¿#õGMeX»å®dïÌüð9Z_¹µx.8¯˜Ä—¥%@šbMe‚{ƒú¹ ¹ÂjM’ŠæÇÄn„ ‡Å–çA¹ê@îAO±cMs\{GO"ì},IÃæŸê%¿Ò–ÑÙ{à tÀªÀår´;cãáº%ÍÞ©iyWB' ›ì@¾~Òš«6¿ØlŠ!Ç\ƒôæÛD<ݲ…IØiz³Cn1ÆàþèPÑfs7'¬ä¾C¾™FÜŒ££€¾’¹úÿkø¡‹aháìý¢šÊñ^¹ o'ÚWò@êñ³7’Ͻøˆ°ÐŒÊ欦¸Æ÷íéY?ÓýËZФ2u(¡ü°4–¥·Û?Ýj¸°~GûX *Þ-ëõ?µ Îf%›§5&€{œö¶Kp«UnìíàÀk™Éôd1hùÖa€e)y$Qæ>+ˆ²õо/èÉ?á¹stÀÊ®ÝHì¢ð};MصzñLöµ$Vò~s¿b¹H )$ tQ›™ÏÐÂÖˆƒ:3òñEé§VÊŒ**Q€C=–lòC|_fÀÈ™ì7O‰—bÅ;Ê!ã°Cè›+o„ß ÕĽÉÎLÛ¢\ÞH^ÏA®¥±£ô÷›Ò „è·Û}¶·n„ ýí6aw åü7ý'ð‘ÏŠQ‡‚Õ$}³«h{ñ äM]Î}ÐÞ£lAú&w`½R˜v'¿›Óvò¥½< ý²‰ Í=iÀàBùïÔv²«–&²åFýuÊ=°¯ozf‘›­¾né‡ñýߥ¿žÐ>š_YœQbU” -ˆ$¸ ©ÈÙÆ'’u¥²1åàKÍo¢(IÔ%5_4•Ôý•’÷ïh‹€8 vgÎ|±nióýîÈŒfQ©%n”š"HæîJŸ`YŇ4×B7ÇÒ°aÙŒÎÆi8×'nqPO,¿x«kqŠÂÝ'šóñqaæ/$ªÉÕX£v§ìzr« Î1óº/Ï Jp1‹2ò÷_˜û” ÄäDáó³ËGÛ„zËçb·sm>QèÅpÚw8cw³&'2ž¢Kn7³O%wÿñZlœ‡‰u«äGg7¿eÄÅ+%øÁ¬ƒIls¯JóRº1Õ$Ž™–± +ãÌÓÅéê.ç~’º²8t O/-·xŒ|_¢-î& 94¿ËùÆ&ÝÓN¬v×]Š"‰}ʬãˆŦ¤­–Šg,<³¯Šw.É£qÝó Ù>nÇmôëÄÚ3Å9Y®^óÍ´øÝX¤CtžäÔñÝRS飮DV›Òä²§~ OεjEc¦'Št¢åWÝ–kQ¬ÌS0ÉPÌkñ¾µ}‹jØø7Émà'ÑŸPÖ8«+ä[á•d¼9M€èO!yÈ$í0új'€ßÿËd-hÝèwRç'‘P²zú¯fÛÔµæ¶dÊìM¿_f,¸yd[˜db³pâÓ2Í\LSk¦< ½K<K^ŹĽӻÉÊ^ž ŽgþŒ;‡oãÊÄvÙl3ƒ³¿>ÿÁâDÛD¹žÅ’:Þ½oj:–¾3ÍdÆÐð·¶ð–ÒsQJ3ª#èòÒ`¦÷U4Á3Å-ôÖ[o=¿$s$L²¿ZC¦±*!@ÍÕtý;+nxÜߤÿ‹¿Ù¹|ÝôÞ{7Üû»sÜvØ‚„Ë·ÆÔÏ0ƒa‚ò磭O`Ýö¨ežªò"Š´,ýì7µ-¢Joßµ¸'ÉR”aŸ6\£×›/³‘…^~c“§I,Gçù°IÞó-ÄÍzú$Z×1Qα^þºÚš=ÈìPÖiÉAÒ§¼Ó0ÞÑ«^j|^%ä3¹þÛ–ûÞô÷Œ…(‚¿Úì-Ò*Ÿ§?SƸšµªOÃýfÝÄ @À”c«±×©ß(h:áÖ%z ÛÛ°Â?=¯0©Nõ¹ºtBZñƒÈ ¢X‹g™è¾%— n1Ñ~ÙfæXÖf>q„.›ÿDË ÚX,gÞ “iXË%̬Ä{Ð}Ïfgçâ¸ò_>ù„}âôµìH«}U£3ï›!é×åçU¾ù+9mvò8³RrcÜ€¼oþHžMÊÓÛÓ”C8íf:˜¸ßêI€\búÿì¼ÕõÇïî£ J¤Ø(*b‰ kTÄ®±EÿÖ[bl‰±k¢FK,‰ÆÞ{‰(]é ½7©¢ÒÞ{»ÿïofïîììÌîì¾} †ó>ûfæÎms˹çþî¹çL¥aCžÉääp~ò-í‡Hþ+ ÑAºÃàÓâéš\v§- &}·Šw¢<Þ¡ŒÇŸ&;›ñ{ƒgí§¹mØ”s¯ÒÚÎßþ§c~d¾Ù´j ÛC-ˆÄ^ @óSÿø¾øÿœjÙÁ¤zëÅwà[2½#×ï¼€61[å‹Ö¬IŽÂ“¦M ÕŽgµùÉü¢½Ò1ñ ^íÿ–(á ûÉšV\OߤU÷! j:›âìJª©}v¬Þ§ ¤Ý‡9^Ç<÷Zj¨N¿×äì(@i£{Ií¶üJÔMœ¾ÎrÇ2¦ÀcXØüœº¯X4~1õr Ÿú!é•"‘Ywµ’_oá—t/ÉöÛ’Bþli ©ÌçŒs>I€#ïËÔA‰ærˆ“ŒÇɱ¼Ò˜U:ÉtTGxƒvDxiSÎ@Ën%‹ þ~éõt/€Ö?VùÓ\¡cMó¼á¾±FÀÏ Œc:°TœÂßÞýÏAñKv»ÅÎeÄEÎÑÝ‚_wƒº ýPÖRftµ.¥]]ò‡›öu¯5»2~’\(œ O|«b 3þä+)G)“,Ù¥´¬Ô(Ô$ÊTǶ¤ë“ZO”:©Q¢©Àj«=w†;-ÃÆ¸”› ©}Ï5Þ~|ýóK‚dع¸»²„úíÉÈUZøÌ7.á”M±Î¿»QŠwAö»ž4‚KK½Ða½Ê› ¼ŽCnúŒ»¦<ëw+¤¥¢ŠÍwñcÌØê^È«²¤%Í“BNœ*U›ÃCMdÙ³ ÒÖ±ò7’_jŽ'åÕ+iÖ!ôï Y®™Øä1ãÜ0RÔÂÒú„ûÌ(Ú˜ù›¨Ó=ˆ¸?¾×˜[PV:Å›¾„³Üì4êPšéÝ‘o¿?z™V‹?5׿Q~Þ§\lÂ¡É›—´Úæd¼ï6Þç–€ÊËÈ#Xd@ß²ãy·ÐW¾¹1ÔÄekó ÃäôúM“ؤ&4ŠuÅý,Üz·÷°Z¼UL6²+Ó —7gšž^… °3«ÁkÃHÂÓ£,ˆ#h[«>ýL‹&Bùè&Xgóûú £§ømâžÅ÷ åjAñŽ$‹lšØxÓQÖ •¡C‚híF|ûV½¥¡N yÕŒ­PlÉÜg&Dááã½-iΗ¶·…çB}׆ ÷ãÕ¶üT “ù ôM‘3¡“[Tâ["ƒÄx_Î÷íÍ÷iâX%Ék]ø°¶˜'ºçDp¬€oÐî³ãŸ5qH·.'É,Ú5Ò6’ ÞnÃäûò©Ö9Åñ{ZymÉ£&vµOÍ)«K(ëE(’Œa -N.*Ž2¦¨`=[hû‡Hí©pO/6öâü«]»`lWäßû·¿†·fv˜[°oúIrh:`?Ñ\eNå<¶mÏ÷Î/Ôß‹ý ”Üç,öÚQ$Z¼Q|I®]íÈ.•iyƆ@ú6‹ï3ŽYYǾ‹zÕÈЃïú45FG WOâ]¯Â+óÉ…Qâ òså}wE;s3`áS,dëifBÄ”c‘ GÒv ™`ëå®%©¼Û‚íò­Íÿ‘ÏÃÛd’p0å,ókàæs™Qci8ภþÃAÇð°%áɉ }ˆttxÄ©7šµö`<ŠVöȉž„˜[0Þ¨T6çSÿ£‘‡¼ýÒ~ üΞcq=ãí¥Èóƒ³æ+ ž5ßVlkÆT=†’=?`ônÌ¿ ¢Ÿ„íð:…yT7ê¹'àiV¼þÌ𬅤nô EäKùV[y˜N̳¤LdéÏäóÌ-t§ŸYβ'maú½U`à–'GN$Ï&1δq ÊP^8GfSdIWMmñsGždG X2mæ-K•›ômM5»ÑN„)—*®}ˆCr²C:)O"ûú$ïñRÓ «‹ŠJÀq¦ú‚ƒJC²5*à¡ À‚˜ù¼¶¢J´ê´!IÙšO4‹öªÃ9!)JËMW-$œ¿²ìËÖ”d_Ù;õ@.!2Ó `S+%:Ø­>WÂö Õ­·ª=iû£·Ó*ß[»âŒBÑ|Ç$QRð‡VÓ¼$íy­L«­–B›R.ùêÊÆ©ØU¯å ³°9÷¢Ã Ù®ÌvKú„£EöE WmnB¸ßÀÜÿ×H+…ëƒNÄÆßèZäÀÃöåSÚñ‹™ÞÁ zLö§Én§³…?ÛÙÄÏðzÂç¸~Ïö mSì—èL:|æQ„™Ú"(â_øø £Ú$8š|iµ]´˜ÉÏ\ߨªqmÙ·4‡þ¶\ý9hb9̧Ë^5JkKc^ªø#§s!ÐÆNBÈz¯ òzÏ÷rßà+šáùn4Ú¥ñ·þH &…å÷ ºSÉ–¦§Ô˜¾ßG÷!xf‹Ñ)‰ç(«Gy@L®<—ët_H™H2æ‚:×ÓOnÈyw£ÂK1$Š5g›ÉÉOœ4ž`ò|Šoûd¸xæÍÆ`D3‰Ða¡qŠBš±`s‚ó(>'‚²·ý(L!€ð[âÿ¡Vù›]çõ ófÅ–æòz¯ò¸µçEo¥ÝÝà-"lUÆH#FE0!cåEð€gœÅÚ|q§­œÃ¤Vm.—zÀŽek¦5S’ë£ü.M‰ròÞζ»tL«ªµxÄBfŠ ]¥ƒÇÖ 1Í\GY%^L§¦I^‡/N;–åæ+ÆÇÊÓÏ>’øYôn¥}fü¸o!l»ÒÂ×R(88s™1%j)Õ⢕ÔG±S¯Q?%¤Cß‘z)é`™ÅÙeãÛ>?Ǹ66`‘dô$aFѶqù¯R<)–Ä…®eÜ”\(ûþÊ!Òx­ƒs-ÉnxFrs]5þ ÌÛͰനò,1N°Ar®£‘]açÉí‚)’”Öc7/2\Tï?àqlÅïÌGuþaî@à+ fóý‘¶ò׺מßÎSþÙíiŒyç1öE‘ÆœÈ}ÿö@v•ÉŒè¤v ;w^â ¹Rµª¤pÀ…©ö¡ï6p8íS\Ñ«€qÀí˜dY² :‡r¸?iT!ù±9†s§ŒÎ'—OÓo§Ñov¦MìOžu&ǹȾ2xáZEêqëF™„2Üñä]ùºšC!_Iiý§Û¨@›ôƒ'pmÞ5-ÑÖd8 ,”Ÿ½`D#(¬0kKüÖ.H¸nT&†¯¨èB¤A1Í`b…´²ðs!Ù3[¡såû^?8 €]ŒÚ’­•ÚÊҒưX­Ú[P )~iê6â§ïÑЫt”žl Ö7ê€>i0ùÁ\¼åPX{’¦uTHìNV£†Èö§~¦“еÓ/¢»ß›iƒÒÌ•¯¶r:€ïf^` ú<ñ¨~_ÓCïï,‹·6Í Ó È¶Í‘é§òÞŒ¸(e#«¼1ÿxc“ r+“t²µMá»\)Ó²WïBd®à9|利s|‘C–àç§³î±~ŸÿKûˆÝ¶²Qü·|×K%G'æf¶.E 8{±VèyVšn¶âÄ¿\ö7Ø~…C»$<ÝÎ-œVe/Õ¨-ÞŒ†íÉ[Ã"í„`ve dÑ©Ð:dN“›Ð[ý4j Äß À¦{x"¼¹Ã÷ Z4ëÌw~'³„ Nv’íxœ•íôT÷êp2ßrWÐÛ`·Ø ÁûìrÖÄ£#C±6‰wˆ_Fèi^KLÌEX]挘G)ºAy2Âó'd+FW•ñžÔ»@wí1õ.§KÿÕÛìŽ0|[ºN4¹Ð($eâì£L8ß‹ÚxL¾gž¬\df; OQ$e¢#¿éÑs“ø”v(è«ßèÔÀgõ¡%;ÉÖzáZûxˆTaµ°>I\÷TÆÁU9‰ÂiªïÈq­ É©—2yÌvéìóV²Ûøä&ðæbí²×Fžÿ7ãlËbÔ¹´‹¡üú_‰軓75V¶ö÷ûW¶ÃõÒQ;Û€sÛuæ½½Ón›µ¾¿W÷ ¤ÞìÅX1˜dX¬)‘. ÿÉ8s0‡¬å“`ÒÑÇŽäv »G eXßl‰‰Åü¦‚Ò!sn´ý½TÒ¢ü[€@Ïüh7Œ´`¤soE‰f’Ú!Èoî*d<.«1"íŒÕ{Wâ;³5f$ }“ÚÎd3ÚÅÒA”ùéü¾D¦Ò_~RAÆxœo›6hø¥ž…ÀÚSÍÍàF{Ò¾¤¯ ý—8ê@SGòÈ?K¹Ÿï“ØR¾QiEºçÒ¶†Ð¶ü zHñS‘΃_\Âx92ÐËÁñ)ÚÒ¾ëVƦF©²-”/Ç7c]6!ÓT^ƒSpOýgžF»šM9?Ÿ–Ã|ºîŸÓæ®ààA»3 ˜=ç ?¥Ö|JßKÚ‰7 :ìðÄ`•’eÃI,GR«F)œI}æxæ{ÒâmM»D3i£ÚÑØ>’A>¼©fîuÞÒǤoϬJËÚ±ýˆ-D¼:òH1,¶H÷‚YúèœsÎñ¹”÷QNxÕ§U]]íÀ»6Ø×†u­W¯ž©¨°û6h›dvÞš5kfôMëÖ¹@EöÛÿݧ-`šD1×RÚJrsºÁwt®¨ÃªÀP‘Âä#?Híõ+ÀX±ÔõÆY÷ÍÈ¥¾²Ð·æK[ JAõ•/ŒÿÝv,KMóÅò΋/¦¦õŸoY?¯KàÇT¥%PÔID÷#­ŸÀù÷¼@´3h2œrLJ×jw†fPlâÒˆŽ0~ùj ¹ú|g€Â ø¤ Ò¾ð´%g|¼ÌbE/´>íN€Ì›Ú¿kIÝkÆï:a”k æÙ"&yHf0|BOA¯6ÇQzQh‹h«xå_¹_¸Þˆ…qNUŽ Üî?…(±PáÕ¶î“|ôS䥷ëäùÿ4Â~OêãmT'S_2ò>%“¡$˜7"Ü–|U3˜§¥X4hE9èЙ„VÜ…& "tÕUŽ{9ÿétæi¤¼ œ‘æ‹+v0}â ÚÃaø*¶žóE¼ñ]ÔÐÎ/8v(m@)ûyOÞøb{Ñ?Ôj¾Îë-ì¥K·>ŒF£³¸àQ9b‚¨]j.ài£Sí—@loêzéL/˜Vk|ìE[ú(Ϩ¡ƒûv´¸>ZNºÀ¬ PÃ%Œ!ÊíO›ÚýÚåÊsÚ1wOÿÒae#¨³E¡…Ö ‹1å”m«þ䌣ƒ€ËÐà¡/¶flkKûêÃÿ‚ÞÑÐŒÈêOÞó‘Î ð¨‘Wò̯iŸ"?ê`W-@è,É|·æ¯…âÊ—NÐ;JÌ”½e*ÐzkʼoH¾Ï$„¶Ä œÖâ1$ÐX¦‚§ê``¥õgÀí>ô³w=òJ1q–êWßnËw~òñÆö§-ìF{‹×ÑåøÀ¸Öƒ£Å¤4ríj8eVgRöîækLþ3üKç5=雄}ŠÚ×UhfO¢¼â™©-çGsÂb̸k>rãïchêF©ÃÆÕϸâ-ß’ íúžÏµ¦ùÉäÌw“p $ ì›€lõïßßÌ;7'ƦM›šöíÛ›wÞÙ˜]°`éÝ»·éÖ­›Ùzk˜˜*}ÏQ»wïnÚ¶mk~³ÿL|§åE‰ ‚Ÿ5kÖ˜7ß|Óì³Ï>fÇwŒ"Û‹@ß=÷ÜÓìµ`1b„9rd_™×DƒìÐf|ü<Q«#Ê´ ¹ˆ;­å+i”Çaµ‘ƒÚr¾ÒŠ ‚6¦N¼ö‚½qºP^—òÝû')5‰YŒ¨ Åk°1ç'¦ÝN]ì›™‰ ½æ÷ÿYÛ+üdWÉüîëóù?ö´Üõ™îÆ´~% ø3Ì$܉æ„4zƒè%€Ì÷X=~+ä}P˜Œ„†ýRÅ)8Ü[a ‘yiQ±žŒÐ$¡xLJÐè+aùP„¸BZ¢êÁ'0ùØ~ðºGs7“WÝÁGVÏu|¶sÀS‡ëçNbÎÞ€ZIåÑÇžLœÂ}U&ÉA%“ß鯥KŸu¼ýÀ¤A“åuΓ£‰-0¹áÝ`®ÍÌ¥‰»ÐBil@[Ãr`Ù6~-kiW¸ôÀñi©û܋̔¸´+—©üÖ˜ Ù¼f<ÂwÄs¦]ºþø;œàM §ó;¶ÈßÝô w—Ífæ&}ýYPHk9¶a%”ê<„€Ñ—²~ÏÙÖ÷[îžBèß> 2¤…&ø«/àÝÄà÷\›ãG5¼ÁÆÌX7—Éárûãò"Û¦·±eó&x´´pD{€Ø3€¬¶äÏ5 -õþÉBÄG´¥ûó{ yÛ…ô.agÅ<àÆÛŒÒÎåŠ!‘Fpn‰Ÿòðµ‰ý\¼xÎ/(ôIÇ0v>RÑÌù’0­Î÷á¾ï{¶Š3êûÄ9›—Œ?mŠHÿb‘oÝuðiøe-‘´÷dPæže øÅÕËèo•ކ}îör–€+;ï¤æÓâÔ¡¤õøE2wVÁ˜X‡ïuÆÆÜ±Sf·v÷í c„6F¯ðZÚÞ7‘¶gÝjÓ”˜Æ¨?±ðÜùPÛÜ- ¨dSòp´×ÙúÀKáQˆ÷ h)­ËÒòŠãTô?™ØmÎ')ïåE„–yª¢wº—õÅ"2[¢W+CîJ»º›¸cL•"HÊ3ÎÉÿfüöcLœãì°êS˜ÑÇöaìÞÞÖ…9I¾ƒÿL;vtÄ>”’´½¥ÍT•,få1™gz¹ò=Ò/×âÞIäDùñÜN¾“àZ…eIV‚D;í´Sú×¾}SYYiFe¾úê«R+.ˆL*¸S˜â­ßÒôú*/òE3Øæ{eNbýù¯Ò¤¬çw¬¥ç¨¢Øä·ç´b#¦Öýµ²"ÀW4+Šjú¥M e¶#Ê‘¨º¡yu2YƒÅ­yÖ !”íA$m<élM@p¨ 諸£|“4bÖ7 …QŒondk#m,bK@‚" ˆ%@îÃÙ­›†ÝrSMÙ6¹Î)&«OåáJ¦Rô:i´Èé¹·º ¤éh½f]µ}دåÖ¾ªƒ;¢ô€‹ÍéÐW äöÚñYiæØ4ÿëŸÂÛØ1”¹êÈCÎÁ_žç¬[¦HòÏd·9¶zwl=Yœ ÊÒ*8‘É•¶Fº4ÙìR} öú~H=s©s9àÔ~· ‰ÖÀ´C(®ª÷éíçø‰ &f"ñßµ4{7|Ýì;Ê}¡m·«™¼úîKþsøgr“æ§x ú*’™ =ÉQfm“ÝøFz!Òäßx£û&ý?²ÍÄälÂ0‰‡aŸþýgýRM²'Ée&äJÛ90ˆƒF¤)Tˆ$¿-µM·ßZy_q!ûW QïáôƒZˆ8åR¤!héjIۥτe׈}týPê.úý¨D:‡­| ¯¢Õû¨ ïm,èèÀ˜|¤Éã»È7Z,’¶ß³´£úùl|Wt ôfÆú›êo8(4jÍDhmß¿™E¬°yShÀÛ‹äUzÀ"ÖZÎþNÑ¢§fƒ£‘Çn´ÒaeWÀWï _°´“KÇ r©ª×ïmÄÑÒÕ,”_—«auz_ÝÇì²î~scì;#S~òƒ¾Òö•Ü_¦úú©Ì=e3´©Ÿÿ™|Énm±¤C±Šï+½mÈ[gLGD=ëÆæ«Ð׆µ ¯}.õjkB‹‚§!cIAr#—ÀúÏ9ÕK͇ÌAJ%õô?3ßYP¯…›Ç+X°]žµ7@5V[’MeV®WÙ'>^zº70Ÿ:'+ë=ù¦3“¢¿ÞWdŸ‘)M™e&r9Hú-ßò+„i[û¹>/Îã&ôwé+è­ÜÆñÛ‚ÎvdÚÃ,ý°² ÑÕT ÐÔ»;ûUž' <' ðäAdrtˆ¯ªÿ¿Žý¸#Ƚ@Éð`d â:L <ð"å;”m]ͳ* ÷¼>ß0‘ªû4e}ˆ›h|g„H¬¨ÛC`|{ý;ÍÁÞ`r8å̉í~úÂ¬Š¶F ¥§zÀ÷c™\j{cšªgà\çñäÓªç3©þ ì<ÇM¶×$t·â÷ “Õ𦠣ôxDKÌ5kN0g™~¹Š%ŽßåÊ,X8‡ï¥¼\¾$Ž‘ü¤¡³=²Å§œÊ}­Ú¯”¿ *4ö¤£©¾Ÿoÿ,ý(^<⥽ÔèFuOm„ÆõùÐî¶´ÎiìXü¶ôÿ:à·, m0ªúI³ÌUÿsÚý¾åËFÅa®©Go§|Ѧc‚‡Œ§õÜÀƒ ôRÑSVÇ Ê$oôþ?´‰Œ–¢rr³&åÞZèù>™ ßWÕ $Œ_Eý·A¿»<‰kFS¡må9‹°å‰þ$–Q¢÷ð­³kí{Óö¿æ§Y¤xÁC5_qý˜Pâ¯ß”˜²ê} I½1&ã0;4mÏLV3UmÐ~‘±² j;pÈâ5–¢6ÈÁ§÷CƸ% ÷pÆüB$þö›$C:DN‡ŸWäÑyŠ´îPì«“øò{=rúãÈW£=œÅœ0ÌDd\ix–‹t.ƒ€ýŸ ] ¤ÌÆQÇéZ%f^óÉ¢Åxí{’ERô-WÉa¶¯ >Vќ݊õÍÅ©'jŽßÞìÛÊŒ§íéƒÂáæÿœ<Ü\»~å £lHžÔn;íì(D2)‘£(á ¤¶%Éû1úÒ?èK]Iï§ef<æ¿%ßíר‰é‡ÍZi°Z­`dÓ¦M3óæÍ3ß|óéÓ¤‰iÑ¢…cN¡Aƒfâĉfþüù,³–Fm–.]jºvíê8É&®Ì#|;g®ù®ºÊ´jÕÊ £8DUUUN\ ,0+V¬0›o¾¹iݺµÙa‡ò“H$̘1c“k×®5mÚ´1íÚµ3[mµ•^çÐôéÓÍÌ™3xvÙe'ÿ“&M2Ûl³cÆB‡“ù(ˉÄã œæË<^ËrëBKŠOp…†“ ol !“ 2Á ÃôòºWRâ©@®WŽ—€kÑ.ªkÆ¡ l'§;¬W bþ2 trªÓmjC0“9G[;¶%éhØÌ­÷ÓÖÛý.0§°ƒèÊ‘ µ iÑä;8N€–êHµ+Qeò$@ÙÒ·ð¶#6´áp+Œ3mY¯?ÙˆŸIÅ¿EömÏÛŽFÐMÁ]q F51µ´ÞÛkC‚6#®a[ð.DèŽÕ<iÄp¨ümœlT]ãø¹–ÉA+~×x„“¸l4~µŠœ©7F I¿DX\¯?-’ºv `‰OÀ›Týǯäþ{Úij\¡ê—øå}îKþ×½Ú씜bUÞÏ6®yiç C™&¦ómÚ€ Ußç^£þ¹æP”M÷ªóM÷ˆ[€¥9>'Qeúú´Uµµìi¯ÓdÿtN\`‰$ñXÚÍÞ¨mN þ©‘`’V¾©ºÄy)cîêŸW&ã¾8àMt§·ø¦õÌ |‹—giŸfõ› ²O¨Ãmj:ñ¦_ÊýLîêþ)¥É"€ý¯LÜ_ÌêŒÑVÈÝnäÝåIzPƒi×Ò®ÞÏIöƒœÞ—ã%ËAr(R±+099ާR•„›‘ŠWq%ÔÙÛt«ÜÕl•e´hZcÒaÕ#4}«9QŠfOäמ$ãMƒg(SÚqòšG¾^bˆÎ“Ó Sìúî…•ßÄwÈÊå´±/RcÀdÕW4’˜-TìlnL°Kªºâ§kƒ8¦Å†+Ìë®âÄö9æ¹(œÞÿ=e5–¼úgBpò’¼šN­òŸIŒå ̸~¬Åõxm•ÃlиJë&Æ€8WÉNÚÙÒ„'‹ïPŽ_aÎ!ŸBÞšÈXr.‹ü3èË’¬Îc‘>*·,Å&ñBRyž1ÚÊm©|D¹ÚÑ`ãÐn5‘æaöE-_µ»FÂÕ¾t4‡·g Ä WƒèÄX¾à7nN£²¥_¤÷ÄßÉù æë»<¢dÑbZǑDž”Ó˜xhHýÉLJɦy ü„ٲ߂€ÓÒÆ†Eâq×Ε®€óÿ UmÂïøÑDÂ˾d¬£>GŽEo»pw(ý]æõ´#ãt´“eòD‡/„ßw«¼Þô«àËÐîþ;f >ö(Ñ„ñ e§ ¿‹â›™YØ%þùßߎä'iÇÞ}˜„èIÞþ07kDàçxÿšÔC^TÙ¿ÂwI9à=Ò³s0ñ”Z§ß〾›nº©s›?A§ƒ6ß}÷ÙvÛmM:uÌØ±c7ùU¸¯¿þÚÌ™3'½Â%W~öÖ­+Vˆˆ‰F±ÌI´¨«ç>}ú8`®Þ>Ü19!p·}ûöF6{¿üòK3a½vhРÁfܸqf“M6q€[Äýúõ3K–dw©:øþfú 3hÐ 'œµù»jÕ*#0¸3 nÊîÿÖ4öiÐî/•RZãÄ—q1C •}£’â`•”7‚þ!YSŠ‘a o*ri ï8ܦ õ7™½½ ߪdI€£_£Ø¾‹z•æjÐÖ1B‡¢‚(QÓ¬©¿Ú}U¶íSmi:í"è«üëˬHCÝK;¥@_¯ÛÆûÒJà*ˆó½´ðeUqQ¶öD; Ð÷Pž—{Ü<·u5ÿ‰1áJ‘`5ïÊ®u—N܉ôõRI<¢¦$þrÃmÂc² o9Ò O%øÍ½'Wó» ðW+ôT÷E&¸ç¾ò;ÞˆbícêüúÚ™Ããá\ØæB&9s©‰\˜fÖ¿‹ëNzH:5MU÷ ÚD­û•ù}¬½ïæc=»kÑzs7Íj}Ûè¤x,;’U—"|²RWÀT*îXþI« h2§ÑGnbZ9@{Hd8¯äw§hÍò]ý ÜÓi'j/`¢S×̨ÿ¼YÿMÚ_ä›:ÑþÎu¼ËáY¶®–à^—DPß…1þ?´–PŒ­h…< iÀKyPÝ­z›¾ò>?ÕˆàWÙŽÛÒ‘IüùÒñ‡UùgâM¼’É)«?ò®~ÈûlçHõóŠmÌõo"ÜÁf*ý´(É4v ‹t©¾!EZTú¡©94ùº9‹‰UW±¾#^ÅågúU}nø&ÜñÆ¿*ZÂuTÖÓ,ϧLû„øQ½—Ö}~pFq¹JÎemí¿|'m¨Vi­éXy‰91ù_ZuéãoY²(›°èMå§<&Ã×¾4Ýb«Ì9ñÆ9Ûó­û:ÇSóGŸz«~õ§2kìíÿµFÊBAO^Pø]Dþa#T¼‚qþ1LÒ<Œã½Jj›T–? HI·wÃHÜÍÂc:U[ɵ#Šú:ù7ˆ0 RK¸VÙE\ezL&_ ‘òü2c’WLZÀßÊA²¹þægN-¢Ïh¸‰vèÕÎ,6/2Iõé^0r=’⬇’Q¼c>6‡˜×ßW…z,îÅ=È6—"«^ S Ç‹€Uä»Iá5ò!ÓÚi¦¹ŽÑ;‡qÿ%þ®çL†¯óÄ| íÉä¯Qˆ¾ÿeœ~Å‚(mrsÚŒcuuÞsÐâåhï˜Úi)ÅIò'œFîÛbrD2Jê]37Jk…Â~’£ÜÚ¾"a{hHf=Îêƒw¡…û4uËD ;á÷&¾7…ÈÝš3>ÂA…:LY$\ìÌ«î%=oŸO÷DÙ©Ô–å($[©YÛæ=dïÏkËwùòåfÑ¢EŽí¶ÛÎã3s;kÖ,Ó¨Q#s '¤5o{õêåhÏÊ—´n6lèháNûÅ/œ€dEÒÆõÒqÇgš7×Ú¤q@]iß 0–¿)S¦øv02?±+•ÿsQ¸(’’ŸSWh^VÀ+b1t»«ÌL|½ü,ËSć.ŒWÒt’Ò Ò„SR•Õœò“v‹_NûØÞô®ºÜüÚ=ì ˜öR;7[­ø¸;GªYLöô¹o£A1’ÛÑ&M'\äÍÿš x1Œ²–)‰bI€ËéÀ$2I'þ²Š84[2× ‹¿/tX×fôûS9¼.h!>,l©îÈ·ûÅÁF3ó³ ¸52Ïo)µê:üªJM°án†G5‡É^x’4$™Lùà© -Vjü–é‰BÃܾ(iAíáªx3™ÝEƒS;Qò¥s1ÀÝ ü.Sû9žsJcÑR®ÑŽÎ6˜„LøÏd?3,d÷´ruPš—„IN·©Ì™Âººÿ’ÒìO;_ì p¿˜8{ÑOg8ü:ÀC!'v›uOÚMÆgð˜U(Š ÷HPö÷ùsv!Ô9Ф¯6cKpóiÆA„*í2ŠÖ ctÜðô{>EÞ¿ÀR#í´ò¾ ¹M`¬GúÏò" ö´-i鞌Ô:8@ûÕ@æÉ^´Õ·Š¦ò­Òl;Qû¸ƒv=ŒüIñá¯ÌC.&î=à¡Gò»°ö)⸄~zX’¹WÅN<š¯ètÙÿ~–²œ¡zyú­I‘ÿ^-Ðvü‡„gÌ9eªó˜d*&*è« ‹ód\‡«%1Áà¥N›61õlwÞyg¯súþ ƒrî×­[gV®\i¾ÿþ{óÃ?˜Õ«5x³;¨¢ÂѾpû=@òf›mæ€Àz' aK2¯`A_¹É<ƒ€_i‹Î<óLGãXiôæ'³"k~bñb·ëwèÐÁq×?Å' ·qÊ\„ÜT˯Àj/è«w2+fBï5ª‘-Ð@äßKq¬ˆïu_÷Aƒ¢@ß B¯¥k±ÀµUç:x;»©|ú†… 2_ ±ÛOL÷j<11ŽõCÕØŠPæO·Ôç”wô/5•š‡óƒêQc0dÔ¬ÖpÔðQý h“ý®,„ÉÀðS(Ó(ß¶žÂ¦A6¼Leü´i†“}v&±J§ý‘4{1Y]ô2¯[;f¿ažÞ3¯¯(/§Ñ²ŽŒ°R,Àê$„eïF}á „pMê¢L¶óåGâËŸê½h†VõBxz6Ç«WK7ç%bS©ô¦žµ%6¶­YY}³¹Ÿ•ê1Y²E±í‘éP¬?Z{ºã·Ró®%Ñ&÷N¸ª—¸h™±Ü4Á€ŸC[ó §’6Áo¬ù‰ä»%ÄP HÅ]ä³uó¹Ïã$ß³û(-’r²9ÿ‚KâùÜpÎäêÜ8ï†y%¦¤€lwA#7 ë¢IÔaÈ#îV¹ÑóÖÝFÚÄWÿyúþ“|Ëlùk„6Hܱý*_AtVj²{0ý·ˆVâDu8!Î]s½ùKb´: J)‚[òc¾£?øý$¡2sûœÆZX(­-i–p&&CS‡š0YÚ’I¶lš»µc]K»î Ÿ×!…/;»¥rãø­³Ê #ÈõàuQý&êPêŒý!r­×{è½cc¯'„zÉzQ÷vÚÖ2P§«³œK{ ÖªïÌTŠ0‘}’±¾<ŽH2ÖgQ`ÍI8ÍL;ëF ¯€¨‘—narÅ|G킾Àzu¤.'R”m M£ü…rwå…@/ÎøÄl­á|÷ Ä÷’ãípÚÛrxÊ[€ êÓj/Áôu°ózr}•úíGû/ôU¥á{+2ÆC*ÉGßz}påu4zuð«Ÿî1Â5¦˜¤uÚáM­s™ù<¶ ¿OŠè“×&[ñmç²è.Ú¶+MÖ#¨×ž”Y“Xm¼5i€H~Úð+¿R\~z޲©ïwÌóLë6£éøYbÀç2‡[WàöÀLe¿¥±B©9oކov@¡j @XT@n£k^¯¹ñå”×:îÿEÛðR ö¡¥=Z÷<Ó|í±ÈCk#.ì/!±¶„QmF˜zj{}ý‘ëÀ¿+øoGýj&>ç,;Çö#ºÏœ§šýÓt5»ÝÀdP-»5¿$ ê +GHͼȹÓiÈ[ÃëO§ w‹eŽŠ¯2—š=Ða\ó¿9¡å€ædâùÔFŽØÁ„ëç>+|XÇH…Øòê‰6ÇÞÕ‹R\ÄÕkÕ÷]­³Ý‡ó­ùè-ÞMD[¯vÈf\AŠC6ÙS—xü3] ˆ9~%ޤéL ²ë+Û72qõß²RO2ÿ£C¾z¾(hgJœWSó ¹yy°žK#vb{(õÀ ®â'´ºh„ùBm£pD-èb×RÜïQÞߪWˆ¶X‰¨pN¬=ûö¡þÞe²¯qCÀl"±s&h:F”¡’cèêÿ ô,mÖuñ ÔIížè/Õô÷$| â%É¡îXŸ¸Š¸\î©è®BNŒXjùR¯õw:ô’FXr:Òx¿ lZÄ8‚@_›xšZ‡¼×­èCR‡Ÿò»ÇœÁ˜r$òúäL-äF 3e«ØÙñ8íÜÝçú±.2‘°_õz€qÌÉ´àR¾³ qì úüÊÌ¥E»ÙöxbEèÎo)×Ình¡¥/7"O©ôÊŸ·†ñÝË™ÿsÃL܇ºòçÄ7q.÷¦¢æÁ*´"º}á¤!Ú Þå…ûgžý(s¿ß’VÕÁ glO@_ªäðLfо›GŸ|„Púòà/•çKø-k/F S[û¤ÀÌ])«1WòD]ój|OâH˜Õ•Ž&ºæ ÅЅĬE‚°ÁЉ«|~©™zÒoÆò»-7Ú$ãR5¿Y¤@ÚüÒìŸ[k¦ŒmChÉ~ÑH .ÂHsÅbqš0j·çcša(éûÌ_,™-³ï¢^U½Èø‡=?høÀ9ð¿‰ä]ö˜u°%áJ׀̥Ìîì͸7Ÿ¾ç—®TJGž½Ž|?ĘûWòíçMšíÏB¬3r,›{mÀ.¥Î<Я›rE9"" SIÓ°ˆwÖd:n¤¡K/Ï¥| ¯|Œ1Â}÷Ýw_ç@¶Æ1¬¨ÙµôÖºo”·¬žWËöƒGT´AÞÅüÖìfúU?cnM~ƒ}cd0ò˜™@“­z ÝìÿrM.­ïÏ¥U}D{›î$ü žd!µ±0jÀ‹=é‰ß9fÊ2€ß¿Ì!\Øþ1qz·CKÓîuäþ–hŽé ú*¼Ì\ Ñ|ÚÒ†TŠÐXþgÉå€Óʯ4}‰×Có±-ÎÂýY>¦ÇÛØYíÊÂÜÜƆEÄñwÊ«š±Òò>ÙÝ= YH»,î'c½2€v9Õ¹„Pp¿Ê‹¸ú!2œ¢RÀ‚4¶~«½j7À8 J4L«Í…Œ]³()8J!­;ù›©EñÊËñ±Òñï]Œ•ù AÀù+´+ôI´„EOcÍÝOîmZ½]‚ÒÔ<Ò-³—bÛ]twõ*È*ÌññæxöJ™ÈÓ»–øsOFˆ9Š„Ýˆsqzͱw¸€ï*tÖ‘âË%ʬêvœ…v!ßAt–@ü(¾œ‡>IËV6€e^¡S§N¨+ÐXftJ¢¥ Ef¤m+“¢­·ÞÚ¾v® SYé®Ühú¶(eº¡Y³ffÞ¼yŽ™›h`,`W»+Vd ÌjúZ›Ä mä>øÀLž<Ù ¯Ò–ß½öÚË ?:eÚbg1¶cþxÆ>}ÕØ!‹£ÿ°mˆç6¾Xî`bt9ýu½Q’v·úTúß$©ålRž{ÓwG˜Lf‡*þIÚ<ÿa‘.›¦ñ ÞHó€tFÊÎYê“ZNLŵå·<Æ"Æõ‰­Ìñ‹Ò³Œ¹Ì©Ï ‘vĺ(Ê´K u'm ÷%Û‡æ>ÇŸÓãxß#ôíÒn§B61 a®fœ»5¥´VšwênÙä "`34þ²e#Ùg¿ ³·ÓsŒ‘±صˆ\N:œùªe͇ԗeK:ˆ¤‰ý|M öÊïðå§œ—À¶t™ )D’ýÞ†7Êöý¾œÑ–òø 0Åz&`Ô `mKÎ` ¬_³51™ Tý—ßdO3q/mÃCK¯Cê>hN˜ëMØ‚l±¾N|ÈA_¶^X’5¯7)+»³Ae'S î|OòÒYj$¹ -mÎ|µ»§g|-uÔÀ|5ò›ê¼|Dý'£ïd´zË·É_Ö=úu'4\÷€ÇÉfò[ÔÑÄ𮲹ûõ"\Ç¿ûQ¦`^dL=‹þr¿¸—Ò†—tP«ÌDµ2¯?{/ó`:7@mh‚L0ÇýÈsOÎZÑ¡xRxB¼Ì‘E/Ѧûb‹î§íÈ¤ð‡ æ1‡LàÐØVæ>òçÅÃF‹&¤‡¢IíxaÑ¡Ägn Õ±cGÇy:‡˜ùi·Ývó;9Ï3gÎ4:ÌíØóPF{g̘ÁAkíÍ[…“¯ÀT…‰9¼àd·×šd¨W¯ž™:uªã§}û€ÅíÓILœ8Ñr¥+K–,1ãÆs´z»téâhë.Ÿ<Õчjè«ø‡ âh÷Ö¯_ß9¤­E‹N¾Vö{ekxÔ¨QfÕªUÈ+ãÇ721!¿ú PÖáqrûa“ÎsïÞ½|é 7/Y-â¦mý¢³×—{/-hÎUU*…`’í#­tI˜+…t0†¶¸ˆ‡U+5.'’€Õè- PÖ ‰V8²‡Xë#øª©ŒwXS7S\–ôNš£ÛâNå+:bT\ÀuG¾±[L8ª$Õàmº-y’ƬWkUÃNô.:àT ¤AG¥-Ø%¼Ž5˜Õ‰¾ß~·Ž˜`ËG=OšSz/mÝÚ ¥”/Z*ïæÔ‹V¾TÖ*_o{Pî‚W»Äµ¡ò ¦1cÆä¼ÈÇ·²oIí©5åöuH]•o[楠\_d2dùÔQLŠ´Ïà‹´‹/ߣ€I>/%ùͤœobu¯å”ìÏà®=S¼~€ìó¸dÙw.c´×Û1^„×ÉŽ÷jÊn*%39"j„¿Yøå)ó|éîÁ·ìƒÜT(þ5á­Øœm°«ÍãhPÉÁÐT]h¡{Pr”™Z݇I¨;A\N¢ùÛbkD¯{à=ÈEÉAù²ý]ìüJKªøIjôDÂ}zåþp_þÍÈ›±ý3ê2gJ‹õŸ„ï0³¨¹á?«Vr :}ùbæw=éWùÚ´OçÉî±ltziøÑÕðŒ5ô{Ø“÷½îÕrÅÆèOR¸‘i€|é)Li7F8¯l}ýüU@%õÛ_¯ŸC›ô@xêƒÈ?Aã•ÂÛ¹žÎDøÙj0?-VG4~_©¼íJÚ ½8 ð-*_"X)1&ÌÎyc¦3¶LsÊЕUäÞ’B}˜+chÖίÎ,¤þ7}©Ëkå;ˆT“‹Y.‰ofv`î?¾gÍ6è;{Q.SÒ¥CÆÍmÕÎîgW!o4&¬?¼”®$I<Ì™^™˜ ßCIÜ@ž?˜öj˜Yn›!ü¾†#r1¹Jô¡·Í#Ðgô#ÿ÷kæß–~b¥w-†„Mîi´QW&ϤF ¢ÏÔ§•¬3 <7¤ß¬H‡•Ï_Kf©hæh£çðÔLTwZ`ù”<þ@k—þ`þküìÇ7ôMñõE-2Ëü¤Vö!?Z\i‡,;3Õ¯Zö°x}ó/ÚÌü•—¶&:qC-¦Cñ+ß·yÍ „¿³fÍ*ø•Ùµ ,pb, »îº«cÖAî²é+Úd“MU¦ºtÙ+˃€_™\p¬ÝdBñtíÚÕŽeÿweå:3 û»©ÓÍ׳g›-Ñ2V¥©ƒåÚµk瘔˜+­âÙø¼ûî»;ÚÅa½À¯Àm4'p\ íöÛoï€Å »Å[8qyËVšÌÒ&îÛ·¯4hó[>h¨ywÐçÎýܦÜ_u5¢%¿0Òöøð·™P{ÁƒLÜô,QâÊÄZú]©f½…ã’‰u<;Ù !Ãíš°k`¶ÚÊX4s€]MD2C¡-*z–m­T œ¶¤; új R&šXÛØÆSøjcÎ÷EÑãÕw¨3[aD€ñ ÏwÎOÍ|h[¶ÊËËzô,mj™v°e½ÞU“ökróÇéocç ÄySĶwž«¬X=V;+ÔV?g<æ ÞAx‘@dé—âÊóÔjØÆñ㽪¥«.®­Ÿ¼oC2v˜Ï—¢òš‘òù,ÿ»&€}ÍÌL¸&„´i`ÞÅvî×hCj½µEÒ¸˜-lý.V|*ƒ¾DPYt>o$6»$0%`ÛØ;a±ïó]Ñ?ûSVØHq-‡Æ>¤–£E7© ¸‹l~ëO'÷ø(ñ¿/xµ<÷uT•u.#ÀÞz7 ‰090ZèØøkÈ/ÿbJ”ȤMy/‹u³h+µ{°\”Üä÷£öô€ŸÌSå³™—IŒâÕ‚Ü׸À™òHÍA~²Žâä2Ë̃Ó>¾e?L@Rø‚d3)ùÔ\IÛk¬’ÂÐ%Øá_ “7îcÁR Î3ð•RAß-HëUd/)>õ£-ZÒ<~!ãóPÜ:À“¿aŒº‚Eö e“Ó™ÜE[þ”|xç)w‘¿Sá{}q/ÄCC?•PCã´/fÛRB ¿<-Ì ŽÕÏG2†I¹-_>Uÿ’54Ž\(máÁ”%'2N¾ÍˆêØ<ŽuƒÇ]B^%;.eÉ:{l$ÇÕ¹†oÔh”‘/g¤xö礅v#?wPÇËÉ‹Ç^”ð¡~*þŒœùGäÌx *¹Ð%¿,’oq ýìoÌ&ÒµóKý môSÊÌOšÃ¼O‹—†nÔ‹Lk}„ߌ\ï.Ú4 Ï=DE+m*Z®Lš´1¥w'‹DÇÐÖÁo¬Œ,÷/xߎz:›1{÷÷":‹)Œe£b±-=Ú9ä.7öš¸Ð³d]:ÆÇÖJ|K¾Ï¢v4l6^Ò`¢á·cèÖNÚ´icÎ9眴O5íÛ·wÌ;H+ÖÒ&“ ~àS ±ÀVÙÙõÒ)§œ’~h*PÖšn°/vCó·3ÚÈÒä•ùk—W€­µß+Û¿'t’ ¨xlIùòæ[î޽n2?á}¶au¸,²ß§{±¶©jÐrT1Ò€.`’D¥v—J&µþüWåF“Ûiü×R.öäÃÕ$/iùj°+0ØkòA«uu . T÷2‹Dò³%~ÂÞ{ÃÈŸV{ŠùoxÑÞ õ/oÿ½KöÛìóú¸º |ÒÙx©ïÐ4Cík‘´Fj@šÀ•‹b}“ mMÈžêiãØ­O~zºäZõÇíY鉴ÐOàùÇh±ð˜·ÁIÛI벺¿îØ ž•ügKL´lj²e;Hoé]&Ýã Â@­üñG+¸>gàŽÜœ\qýò}:Ö‡©P›s ž`ËžÕ«l£“MÈñLN®Œ`GΛ•EÞî_AèÛs0¶FÉlÏelÓ¼¼È¸}Qƒ“Æ÷€ñ¼œóÊd>c¡çJ´§´ ?*‰¯Éž¯ñÈûUÞ<ÇÔ†¥’½¢FîOõ³Ž_i ³%¿ 9o‚Ä!¯g2Tpra„¤³ íq8d ?¿Ý·ÍÉŲ %fzcnânìzí(Ú¼I/HUÿ¡ k¦ª4ÐáöPs:Í™Té'Æ¥ÜÃÛ½<œ–9£¾ž±Õ ¸”ÙÝ ÜÎC–6Çô°,¿˜ ïþÄy-m™ü¦6îD[°ñøKašö"}±%ä?HnúýíY&âw^”Ú"­EÔ×IãVÀ_mÁ´ y7¯ÂÛTQÏšHgÄs3‹ð2#4È“«­‰»+“½gIÓ.{‚„ÞÊ”Ç#HÐÏÀ—º§âÓ¶ð¾L²3ÒPhðâ_$Þ¤Þ?#õ^6jŽvÓ2ÇÄkð•÷<åò åñ?oýiîwLšU_ÅÆ_‚ÿ¯©Ça”í2OÙ/s¹‘‹gª?†,Þ7Ž^yE[Ó¡úët»ÞÙI»’º°‹¬˜2×xaÇ^•Ã>´--Þ ë¨ëüpôÚøúÔ´ƒyÈ$irLÜ1®ŽSp9§ý†ÜœBŠê;¹v×ùrÇ®ó¬¹Î’áô=·z_IˆÐЫ9'ÌÛÃ4~½ñ{/ VÚµŒUxÝ è­]»Ö1Ï ó Òö›QŸ_P8:}XÀª\Õ;K:xNéxŸSÞtåWflÚ6lm_UÆ5ƒÉj;‡Ùñ[“ Ù®¹OÒ8V½Ø¡JÌK‚«·Må†ÊvQ+‚Âe÷Ånˆ‘¦®e¥$ g ›ñ^í×Ve¬­–¡Éd†:³´V¼‹äÇNB¶‡Ù¯ÄÅúwI³Ö’p­zŠ·sÀÝ d-¯ª€¨âDSò¿übPñÑÃ,‹=­H{WI%pI£/ˆv„™fj$ׇÊE“¡ òò ç©ŠÐû´À:?}̪³ìmi«ë»´ŠÎ0J’W^Ú ã¥Ð(e¼~·êEÎ ꩈ÷¢Í¥Š%Õ£3¨pCû S÷æZË…ÐnCJƒLtÈ€—Ô¾ìæ£‹Ñ¤{ab½ñüØa¬¦ÞcÖ&»#˜Zøšþµ–­jÒR•½gCû i^kð¦4'•Û4n·¥ÜFdÅÅwZ“G†ÂñŒ%Ýâ{†ïHÂþ1¥)™N©Z@âýéÇü7È0õþ‹—¶YÞ4™ù íB‡Uˆ´­ó&‹4éȵgðèŸú¹Û|Á>䯙”xIqj{§®^:Ž…NX‡Çè¤ó rãXj¾¯|Þ\R=’‰TໞŷB¨~#(hŽ›&÷F®?²­R[i³(Ö‰¾|>N­³œKy¸˜^ö;¸”–C‰1 /pë,¤=—øYßg˜[ŠÝUR‡ú¯¸.‘&:u\£ » •~Eìhº7ìg–ÄŽpu Qâß%Ü©žeÂD¤ÞÚ±NeDš HIM2• }XéG²´-y~ŸE•¦¸ ‰¦§B»«÷ ú…AÉåuû!à­4ld‹8lE}ƒÞe¢];ÄÔ1ñ5Q÷…À4×^ºwàëlGfVhë|CK/ùÄÓ—¼þ(U‡T7%¾3» ÍÔ&±Èï³ôgi„WÜE=ü9R—fô¥?áök³mòdxÌž·,9¤#Þ¤6móT0P$< –ä'8Ž xQš“´ u¸³dýT¸tØ×þ>™¦p¨<>ÔGÝ1ùû’d¦Céó`Ó³mg8ƒ–ØñÊò1oq\™}F­ïÂ.­é#^J›È·P¯>q o|ÔbÝ[ðÅŠèÞ4í½w¤ý7ã|ÿŠÖæïì(“9šS+úûƒŒEôy-àyý@ézóáóVÒãõ”÷ß+‚hm#nïÓ/ߥnþ€|)Ð+* L~€¯më:ÒõË2’¡´+«%þЕ7/2O”ÍVQ7ê±';Ùì¯ÊñCȸ·§dÜ„ý qúë_`âÖð¿KiŸ3ÐêtMh.†¯žez×}€·[˜½}òWÐn:x åWŽL¥Êª‘ðyœF½|Šœ©rF²ýÚ5lù¾Du@é[ÔÍI%¤´- †:LÍ»¦²ëÆydëw¤å•…KÀ·ÞJ<„úžœ¤s 4†ˆ´¿8Ó…ž9‘¤—io!3f2ÚY›à,<ÞI|…¤Ùi›·Ð÷¦ˆ4îßθÿq;;ÄœÛÝ<ËÜñ`r=äþ×"ñAÔŸýF}ß­¹Þ²5~ýïeÎ@övó‘tgÁ¸|~£¼“ï>v4»ìµ§‰!ÀÿH“¾Ä ŠnËú´Íº>Ê135ÌNMöâ²:HöëHOÒêõ’ÒòÚ3V;¨+ØXÚM¶óËŸ4$Töªå%l`ÊNÏ©` è´XMÙtp[X{öj‡Eè”}l{^¯ä—á&Ë–-sz´á48‰r4~™ˆµ@Kg•gN¶"ªs¨îSȯ¸Rë… ÀÔÌG»òÚç-ô‘˜@\#/Áž–;çuÕdL§Ñ¾çƒ²4hxµ†òFây©•å¨v=Á6ümr õƒõ–µ“—<§Òn,µá¦? æÃ|f,AˆÍJ´×^¦Ã^›s£m^ÙSû6÷*ˆ©'àŸlø~“óZPP콎òÕ­Ú3±Îå\‰-? /è`ÛÏÜ÷L. Lì“l»ºPom^"}_:Ùàœ2“opõÛÜ4³ÿ«}ÿ©!å\%q:Ö‚ÉÙÙ´Ù>Îgðd.¨«Œ¡ÉŒÎÂlF%Ùþ[äi_aúIª^#ÍÙü;”…«æ„T±«ý Ð\•ŹނW؃-£”¡@Ó·YÛ‚Š‚0;‘T“‘»Éï Ä?<þ;ómÍ~œÊ®ÉV˜vŠ&w’}:"´J;£7ß÷ ùîÇèì•…Ð;†å¿‡OMUѤû6biíe·CçuúŸ_“G}èbô~„“Ö–K´œ|¶ Y$©ŽµÂ¶uÿt¼Ñnë“óñ:ÙùÆÛ™ÐoÅáà Wê^Ä÷¼Å;—W,£‡­³Äp-}ËD¢31²+¿~ÃlŒk áiúþ,Ú¥Yöuu<šT ô?ŤÁ;¨/¾(Ü–įs¯¹vØÐ>û•©ßß|_ñÊkk|R[±­¸Îå—Ÿ1bà/¡€z¾à¾wÇ1ñ9­ªž´ M¸ý¤É×ëÔé¬Tyª}žK^%ç}ë÷\ôs'Cäˆ/‚C dgK©I¨^s¥‡ìl¶³; ÜP;s¶JÕ…×Ïqð‰‡Ðö ?×7I{_t}è<hS¨ÿ"hn8µ&ü‚j/׷뢖Y~’æ³´ß%ÛýX(ÛÆ¼Z¯jÒÛKk?§²¯Ý¾"~aI£ºÚ\[Ü? Y߯ÒJü(¤É©9Ø(ÚÖc€'j_ÇÒþ¯¡OÍ…oë ótûÆŸøÃ“j¨²²4Ûy'’´#»·ŠSç!hW œÌm¸¤sè™:ho$ñÏrxL]Ò¯r¶qA|a³-XI‹Õ.Êø˜x?ý]F\”oìûÂ÷?!-¿µeL¬ÉŽ.7=xP¬#Œb6_ù³àôÊï*¶47!›=ÊØ ñT­OòlM¨ cfõ¹ÚÇÙ±ª.ZQFn¹ ˜­d‹ü𢕬9Ç{i¯2Ýñ:íÆÊ½Òp܇ÿ„1Âω©IV–Ù"É\ À¿4ƒu°°v¹Èfì~ŒÞJ úŠ*þú×Ûà&ÒÈti;³*ÖÚ\Š>loLV ö„,´?oÙžA»TÜ25âÏ[* ~QÝØ9 úÝ0ÊD»þUj•ÔÏSÆC(Ãl¹·æÉžÏ ,ým?Úª—û3üEç½à;¶íhW’L¯NdL|“^?‘<  wÚ>­-‰IŠc03ó-ò–•×$›é^€ì~´0ÅcI{ 'â÷ ¯µï¼WÙ!F»vzgxIG~²oì;×û§©ÛÂ,LôväÙ(£ë0òþ±§¿Rs_Öq['/:¸ÌO2ƒ ;½ùHŸ+&V.’MÞŠM§»Y¹âÝñhjB¥êj´0lCæóÇv{:’5½PL~šNÚ‹2±&P¦'ôv¢cê]:¸eì­©ßzM1Émô›*™›°Àº·P4àfDLï›|÷׳¹‰p\¼8#zéSر¨ã6NDi¾Uqºy56ǬC#øhÚvÔµw ³ÿÿ2~“Öaû`¾œ˜z/ñ=ðÉw@TlLßÊM¢6e[²²´~KÀ?ðJÀÅÀ-)@%!Ãj „åøß]G2g\Ú ¯áp§ÆÝºØÕ†¡ O6Ñ_e¤‘¡%¯œC"Ÿð~LvÔ“¦£Yß"{¶õ£/<“ö/Ó¿ z'‘'“÷VLd^B w4l–tˆHR©½(辑vN¾yF’ÉŸ(“›)“—}É}&hÈRÚÞ©í|J\ïÕ·Jó5õQHøý5“¨.Œe*÷¥„×8×€{y(ë/د@Ûjî"Mˆ³¹©ãlŽ'ŽÝù¯°ê¾qÿ«mmBi†·íhª7·wÙÒ6$1ÁœÂèªÊý‡ß©}(/HvBè¾—Ú·fôÝŠ?‹´5Ý3iÝJB0“3mµíFZˆä-@!-ŸçЄx[¤>Ђ:ð6ÀX> /'€ý—¸+ô%°Ó&;±hœû’ÚÐdi¹òð–Ëûßê?bÚÓgÇf™´ iÙ²“F–Û[Œ9‡~Û:¶- €j9—Vð¥o³y0»FÙæ1L=0™¹Ÿ“¤Ç´O¾ éãcDZºtŠ­¯»í0~¬™ïh†%>ç™o©ø- hgòß3+ÑMxÊm¦áœ¿¬ÀFð Rn_Gð+/Ò|žºŸˆö²= 2bÐ\o”©w…ÿ ïr§æúöÁ›[qÛ—>¬;Iò(3Ê- Œ‘?ç„oŒ4ãZR'ZQýîB½Ï¤Þý ~3Þ5¥O~Fò8§é|øÉ/è/PV—oS⢃^R ¯<^ÆävNr&|Ê;¼¼\ß"³mZÜ sWãG'±? ÿ”±­?uSXL0ñCh7äxØl'Ñ™—›ƒÆÐiÇÝ’_™óø–q´0¥ýc ™÷),+-³7Y o˜oâ1p_K·ÕBS¹R)\j:TñfRŠÑf¦RÖ’´õ[ÊØ#° ¬=޹t’ lº׺±}þ¯,dÞ°÷íÏö«2†¾Þ›|k<{˜þ¸;ýH€‰Hã`þË4‚ÆiñJRïOyÿ)UŽ·Bº™uî¤f:™áÉA¡ ¯¢»‡|È\ÎÛ¤maAo2Z˜ìÂûÑäK'»h¼êKÿÖÙ_[36Jé¤fDx‡}v¨ (ý“t[’§×ÈG–+u+¿ì)Æì=øPvß®G*~Ñš§j›S·ÖT–¸R!Y/Y¤Ý-Ÿ¡%}‡imŠ9;˜°Ù²{™1O$óv½á œ§ì’½ÕN'¿¥ý˔ٛÔ@_‘ä†JÂ/‹é°aíÈd½Üñ…ù(ããPìË¢î¼ ÐŽ'ÄbÇr™XÐï•£Lì@‘äŸQdÒ½‹kŒ>:‰onCCýB ¹#aÑÑ  4´]Hî-Q€‡C';#/[S£â :0O@«÷̟δ%)e°}übgÞ©½ Cú?êµíþpâý¼ÇåRÞ8ƒERÚwn»Éͤz¾2¤øwÚÎQ¤!>æŽ pçÄ÷È £Q¦™¹¥¨={Çü@àW“ÆÍ9OþV¬Xaš6mê²æqκÍtó,çôƒ—A?ý¶¸kÛת¨¤ÚéþÔŠ‹ÑêX’<Ö”ÍGI}'­€ÎŸ"ùíÖªáFi›Ó}~à›Õt»VS'!Ô¨ìëSî:®škK:¯kNÂ=\ï§XF?¦<«ÿû[šêlg˜®fÉžen_Èæ&óçÏ7cÇŽ5^sš¼ˆQ®ëØÞ‰: ür2üëÉ%Ž&Kêz!igViÝ<Î$E¹i¥¾à©ÏÕ/ÿe*â|ªóoùÿ±‚¾Ò*ø" ­ÿ’@çhKýni…|Eût«@ôþ ñ"ѹjP¼4ЇQ7„”™Ä›%t†yvÜÑ|‹íÉÝ #-¥˜ |Œ¶äŠêž¸e&©cÂf’¶¢Ðò¬6ù8“€Û˜XHXV¯×äyÆ¡(“ôüßo™À]‚ÖÌó©ÉF¾ìJQ­­¦~fQþ®vÝd„õ~YÁ$ˆšê¡¸ÀÝ/½4¥.öÃ}V:Ì1,hµe£‰Œ%Mž¯FøìĘ'{c¶-¶ÆÃG«™¾òLÆNšUû×;¤Æ¡÷ŸÙ?¶7KHUzî–ÆÙøtd<µHiêžH9Éœƒ&XchÒb^¬DC©`¤Úz¶á­t¾åAJ®'a½ÜwSrñ éÙÅÙ¨Îø>·YÐÛ™ñ~8i=N¨Åø½  ã{Êצ­ò°@BhV ¾hŒÿ¨ÅD´â¯LeGp6ïò›æÄ$Ý>™¨C~–:uý™ùÍÏo¦oØæ©—7ø†ö,èiBÚ'ÖÕômøè}bÈèÝȬŒó=‹+ëßC™}ì¼_D<½ˆ? ìÖäªÛ–ï¯1WŽ4óÏn¹ŽÒlÉ"Ð$Ğà ‰Öh Ñ3ÍÐX>vô=e¶P€—S1u£É¼@‡Ï<å-ÙÀ„àÑTõâVuç’ΠV¾¤ï?‚6=ŸzS÷øò½CǦ7o”?5HþŽ­Ì&ô÷?¢=»2–Ѩú”xü ¯b<¢Éi¾rЖ\õ+M˜¥•û=`…µå§8¤åTMÆŸ”[MlS˜ ·÷Ú!ò—vcQA ÛÑîµ ¨ñÀ’Li2»Æ:8×úqûÒÖæ±|1Åœšš8{A”,ïëñAãžøæwðÍrŒ]šõé°õk·'ÃCd:P‘F´Þ¾LmGç Üøµ9udíêJÕÛΦ µ$Wa¼¤œo¬Où,!RQjÛ7RVÏÇá’óÅC ¢­ØÚY%·#רL½çYHÒȤ2ž È.M̨€’æêG®„£¶y€9 ù vâ‡ûÚ-‘{h)i §¯…-K»Tã§ÆLµíùðÐ!ô?ÉÇÚ!VsÐד™Ûã©ÏÎÈb2FN|MgÜŒ#ŰÒÊÖçß¶÷¿X—^ÆfgnSa°ƒNöÁe¢nO8‘Ú¾Î6Feeð…´&ô6ÚIU©OjÛMˆsFL&7”Š¥)Ü̵%]%¿Ü2ð+ÀE‡­Iów³Í6 ž× ê¬0KÛu¹çsòÊóR[ƃHçª6S`A¾\·Ñ/Üsߨ<–1ÊШ~ª ¯>È/ ¹ušLuÝÌ'ËÄ€ºˆd“ “!ÅÐ%,¨•lÆ$JÚ·¥ë9bé˜8ë€7‰µ®)Z ÕYX_µÉµ¢ü½‚гx ×Õ)ãýuUÿ»pñΊfæaÀ`ûW1“6£ÕÚ,þ‹ ‡fè™ü¬†Iá§Ó~5NM"41ìÏÈÌ(&y;“Ô—R¼'lKu&UM{%úù(v8ÒMÃ/óÂåTf­À)ßV#a6µõ%£›b²¤|6$œ´¹¼É¡K)¯ÉßÞ”ÙJÜ2彟[ƒ¶Ü OÖ”H5( e£áRÞiæ. ˆ}¿â)[*ˆžï’]bøVö)úÂL}¶àémÊS6ÿ0ix?ÁáÛ‰”;cX5c™sx`P[‚ÛJH ·8WO¯}l¢KQ[®Íø©M ØñLöÏ0«“ƒéãî!´Š× 0(-ºHËI:Á}j‡¦íTÞmd®¡ê`Tµ$‘xC.wÃ…í«‚û€}ˆ¯é­J£5åž© Ö3©_&ø+¼`-c"¥¶¬âvÆ×½‰üÚÆ—¸µhq–âKõ1 Þs9ÀªÆ1»Ã—k²¨«±áﯵIØ_daYPäò%RÏT[èÉOãæÃðO)ñÌ0=‡|ÄXó&üï-Z·}Õ_oGK¸œÊŽwúva|“LV¤zTb€y#6S•éî»Ìÿ¦ÜjT.tn;r»á”¯#èŸmÈSäE•L’%ÝM¤íïß^¦…Xqu->IQN¦˜²5vÓÐbØÛ,.%>ÙÐUÒëÍÈ:(çc;R•Œ…‰Ïˆ$Óã5&eÆúðø3oÚÏn< ¦vI[ßu¨§@Yù:„Ø úZA×Mp<™l>áÞã»ýã®ÂˆWv/X@ŸÌŒ…zL’c¥é©-¯¬¨+Þ…¸àФ–€Ã9» ²¼HC\²²·ÍdyЃÆÄ§Ü 7ý“Êjk~^™W2ë½´AíªÑB›ÊÑ•þò”ú¥ú…Hf¶þß‘¦¶¢ìB4Æë’ÞŸY´ÓÎKâ-~¹«/ Ïi×Á ´“δ­=À7~›ÝáeÃÛ«ÜÕö¢ä׆‰r- øUÄÒú]¸p¡×­‹êt½z&^Àö®×’ÍOéï Ü‘}– Æ,&È]é‘ÌHW¼~ŠZ`Ú꤭‘QýûÓYÏù¾s}¤oÓWMIeš¹}SþëvtŠe!­ÁBfí;¤ü)_bعmÚšüJó·×8R®´>î „´õ-7Ù¤UÛÛü–¿£Æ(¾ •g­ž©n4)YYUi–`ÓWæ¤éë}mܲ«Õ¢cGç1­ñë<50ÛÔ}Ò|Ÿxϱ) Í/^«­Hk϶gm^űf9_Y›©lø¸%”e@_„@§‡*_pì* +3õM ¹}Å/#,f{ð>;bxÃn¸û†$}#bi{H & ÎíyõuêuÐ(²l®²Ù è+R.]MNê[ Zn±$m *r¿Åñ"&üû:h—…®Rï²ö=~¼B’@Ùå”ÖNÙ)v ëÃЦJN×9tai4æÅÓwãÉ£ŸÃ|Áá}×ögâ˜g_±C¡RÊÅÕú¹p޾ØÂÞÜæ¡6¼“æªNâvó4…n#@Ôít•o›š´?¿b’>^¦­k^Ø&ó%õ³ØyØß™|˜˜'SÙ“²mýî£ Œ$øÞÑš²q]gþ%O¿˜à‚úVï";TSrqíCZRÙN÷«Ä#£L¼²cu·ËM&m)->L::ðGmS˜5f‹¨¶»åá!ûù.„õzuî0Ó¤ýîhæé=£e}8¶Åod0ƒopãÒDî}l«~mÚ1¹–¦²¨=yØ’Eæ«?¢œÂz½ã™£êÜ`fVD\ð$ßÓí+ç*¾ªÃÍ2‹Cn™I›X2±øŠ[¶Ä·]\R›qO„N:§DK#_õ} ‘² |f‘vX™B;É´ŽÍéG»óëÄ÷nBl“h 2¡Ufª¸0óˆ4¿‰{pËÊ:~ßÿ’IÓŽë8®á`³&òK/2÷@IDATè:PîDÚΊØ/Íìs'à(®ÍÿÄ¡ñÊ/Yà”&¡òïŒ|Î5ç_[ù~ò•èžóJ2ë²?? qx™ß¿x㶯 Ôµˆ#3 êßÙÔМ^q‡G6D;iNÀûlßG"?‹ë¨…Õç•[‹ß¦ì¾ŒÂM×BŒ×SÞWðiÛ¬us}¸ÿ÷¥Oi·Â·|gÁ[©€ÿ¤²d'öÞø‚î5×иæå3±«üoú^-RçÌgÁ L5®¦,\-EIÇÿÏÞyÀIQd¼ffYA”$ `ÂŒŠ9Ëyæ|fÄ,&Ô3aÎ9çœÃ‰Š9¢‚L(*ˆŠ •$ìNÿ¿¿î©™šžîI»‹xÿ«ýÌNOw¥®zõê½_½zÕ8A ô£ðÀ7á•Qo£±uÛé߇ÞËØT¡'t-WÀŠÜ H·ËâObDåpÆÝ˯Whÿw˜Wµ ³"-#ž¤ ³]¢­~JÔg·_ë¾æÊ›™Oe>CÏV!­8m0¢ü[þ?Á/Z`)æ{ôÆ’p’¯}+C,Aj9Óиç<ˆÑ#þìαÿ€ß *½ ìW%ò™OÓåôÞoÊ´ B@Ž~¸°|7æÇ§áü–—´äIwƾ]¥sƒÞqBÆ’Zt¤²ECc¸'÷4¿ Èôsy…G%T®Åm¿çjÔŽÁ•R›IµgÂ^_ç™JjXÐÃ=úèÐÇxÙ;Xà.WöèFig…ü3‡Û©tmIáŸ9ú „z¶!ëF >¯rÛ8>‡¿ÏIæÈ$¡åÀß0¤9†¹âFd;~%ÖŒKéZÜ('‚ËuàûËÈÔÚ=÷¹ÞOÿŠÎÅiÞeF”›;~lÞf'¬Þu(œz¥Ÿ©±À¶0‹™ÓÍJð:aËÇÚÁÏ^ÍÖÖæPþ·Ægé·*ÌO3HÁg»í¶óô‰zæÞãÔDÙe•Œ§40½’ñ`^Þ­‰Ö%ã¹uø»_7/£]píqåÑ.À,ó­]M¢¬²Ê¡-щ—ZÒ»­€¾V/«Œ¿;5UýJ´ñ:”Ic“<«¢í}MbQ+ƒ¢qyóž/Aùß$÷y–øÛ³Ïwtª§ÿûÒÄ"L9û¬©Ú¤X¾ø÷ó®ƒo‹ó_ù,ÙÓê»ÿ½k_Æ[ÎŽŸ¯CÓœ4ë’hѤeDöy²mvÕü/×y÷ÈzaÇhzzµ]ëæÌO‰=“<¦¬:"³{âX äÅǪŻž„_¼Üýľ\¯›ûí·W3Úd0åºO}»òÙ‚gGz¦¶áó˜öF ò=ÀºÈ4w1—Ýâ%¢U½Kt¿Ùû=Ó|„÷prkïÔ’t½„gR—’_‡yï(³÷a²½×>\~⟼÷ ¤_&2üõy37`]ù¼sÌq‰æ‘ÏŠ¿gð¾[!;qР÷ ù¨¬ýøÜKÛPfž]Þý¤ÝG}›Ø3[É'ç%ZzøöžK´õÖsi$Óz®´åÔ³0Î’¤[¶Ê´Á»k®9”~±yc™ä¢­QF²÷ô …ÇC!ö$kª/V‹¢¹ÔåÐÂyél¾ó-[ª¢ü5þSiû&O†¯ÞB9-+*KïXCŸRû¨÷d³+½rd÷ŠÚ%± ôµelž‚Æ&Úyñ P¯¼2—övƒ_Ý—:ÊC.š>½‘¯þÐ- ×’‹y®EÓ”W‡èrq»åá—ÕÏW1ÞUðÀ^Œ±(9«wï}xá•Ueï•d;Ÿ°eÅ{ruޏô•ÞרÎ{²{¤Aõ®´\ÅïBÙJÐH5ùºi.„O‚®4×ËõP®Ãr¾ß^‹7?ÍxÐØp±ˆ(ZQñþÝC´¢ùJi5÷Ovô8稪6æ¬ïtä{ÍÛ¶~wCg¤éó #é úÄÕϦ+þ ßJìF+øå7ùžý0|PºPñ´Ás`³‚öïÇuC^z¬½é©%¼|ëÙ%! `ÌcA*/ >¼½Ë‰‡ßõ¼û%ë%y­åï¼[¿/}ý9û¥äÈ+cŸ(™Ÿè¡X9X;úc_<`µhÜbù¸Ï4ÇK¶pûÝ}^Öµd¤Ô%ÔgålD#’IÃí[,¿]é#ÑY±8ûg5×x¦Ù¼c»¼÷|¾þã7ü~çÌÛÆcá';×Iþ‘,È"`6>-zoÀw4§Ø<„Gh¼ÎIuòDö~±o銪‡#—H¬ï [ø#Ï«=Ç“>¡ñ*:Ô§´¬OϸÅñç²rëåÔ9:S üª‚Nä‚kMÆÅžÿïYñöSûHs'8Vb"Ñv“—ˆVÄÖù5v RÊŠå§ôKõL‚šÚ¢tkÖ—òÛ­CT~ÿÍ÷Ö€^Ê \ÆYªM¤\–ŠSÎó·aÎX·ù“úÔ‚É–oÅå!a$îY9÷š^eH —ò]NygíùXVaÿjÜåÀöÜ„Ú8ï¦ò¦‰®âòéHþl¡oòrâʯôþEÐyPÙˆí£vÜ =$Tá° m¢ÑÂËÌwR0*}§‚ø©ÓÈc’ùH°Z»L­ Œ¸v«¹ %d¿ˆ²¸Ö\PÏVD©Ù6:eIi”¢UP ºÉ“ î³ Ú" âgë½J& rÍ]”½a.ÄÞ1‰ åîeÓŽãø2rqE’9‚¸ Ì#+ñþ§rß•kÚz‹'¶÷$X㊃¹¹³7e,Jy: ž)À©œ:„ãÀ¼ÅŒ“Äýô0DRÒ×-“.$§lQ¾@bR ]ÐÁ­n<“î½èk”ù2Àu›VàX_äDN‰/#ï\ Dû"£ ¸žÃ]PXc[‹¶Üé[4¡ù_<¥òz-Gšòæa-hˆ^l»ÆŸ’`ñ¤Üñ’ÜŸñ{Déø‰ÝQhï%žð\ŸÙk-ˆ‹¯6DY´yßÙóùhúf ¤”º<\ÀÀǵ ;ÉÐaB´tC¢£·fò òd¼GÔ¿œ{’÷ß…\™¡9•¹/eÏ|ló¸¤1 ß¥ $wÚwÑ·€”jù‰-ÿ¯ø¿§hÒŽ]Í­’‰ÿŠz5F™’#좫xµÀ_áê;ñ¹Ó˜#¢ÊQÉÕÊÊ_ 1‡ÞúùkþµúDTy•Þ;ÞqŒ$kQm*s†èZ@T¥ùùñÝ=Óbr–§ÀÔjÁ¦RùÆÍW6ÚUm£qδ>ˆ«Å×÷›î§æöZ“^£ù—F~<Í•3@;auIïúû'ÚI=7$ñ›=’F£ÚLrW{okè@‹ú–Ìg¤&ÞrbFS;4tܨ}dàΛ¶ËþN`œTû*ïÏâuªæ#9ds»Œ‹ªIÿ·I#C ž©¬Ð[YA`é×´•dCûÞšSî@VseªM¸§ñ¥Å/ÅS‹¶lš¨oáyù†š­¼£.§oŧ£ÒTsOï úV¡ëEW¢€RM%ÿ—&º­Ãí"F^u Ç©äw)ð¾’¼*«ȆZEh%¸ÐEŒÝZ~ÞG=†1aEMB²Z¯xe²o±6u'XÅ“§w)–¦1ž]pdåJóӤߘ`B©ò@°²qŠñ-)à ]PÑo[fôwW€–²õŠŽS?hpÚÚW¨ÇòY]ÄÏðgü—•ßàö›Oc]õè%KŒ†ÔYãAŠŒUãòo©fŒ¿‘ÜÒ;2ù¯lªÊzÂìd•ôÊ@TÙ²V [ÜDÅ+yO`ª¬$J´—,Ád±â. j k燔¥RéísÍ£¥B)sq–g²&ÈóûY È#ðv¿¶ÌìwboÞuÛÂû1ï.%v=ûµ˜BºíHÇøK]Ìw·è<`¬9#òS»YGu´×Qqe ±,1j_ôVÆBg"µ šÙtK$vò&-<š>XÑ“p-0Ëí#¯Úoku,%p´(¥HÖ~’ªïãhÔ-S€„â»V³ö¹_ÏÐyXF‘å,)¥TJ!wA+›>ï;uÊm¿È>È‹Gžú-åDVÆaš°-Z‹JÔ%°8“R¬ö–â.ºŠ¯{zï‘âžÿ•÷W¤ÞRæVäÈ´ÊêÀ  ÉcJ¦8#e± Ñ™;u>òÀµ%ËòǪơøš¬ß(»šh]ŒË“Kå#ÀH²°äf»gÁ‡^ä½Ë“wŒ8½šìîmÒâsꮕÕ] µµÖ;š ËòJ–S’)]Ù]:Å3о,)ãÀ·|‡÷ÀïqWáƒÊQüÈÿw¸V{‰¯ ¤W}Å{µU-ê¾³èF¼M;ÜûóÿºoÍ/^%C T•ì!«Ójë¦vÔ»Z=Bï.àF¼¥Ú<£ÒiY@HÀœø·WÓÝû2µ©§9&*Mü=,’»f9? +¤³} ¨,k|×L|@ §n^gâöžÆ™êdù–Ò @½ÚróÒü¯ßÚUdBõ^ÊKóís©=½ëR'“o4Ö‚îgÔiY§,[f©oɯw2fµH€´Zh‹’)é{-j×\ç‘IöÕ;kÎŽ”¥¨§[¶výš\"RFqã5ôZVälŽÏKïhë×…ùéx~¯éܳÏâ¿Õ7M»¹:Å—[iY«j‰+MÈýù?Z”,c£òÔ.Éšz&V»fÊ]ü·ù© Í=qzšÆ–v’…w¯ `–ìYÍX°e7âwt£P±ðÊ;¼‘ˆÑ,ÈõûÿV·bJ¢Û®Pgï7tk‘Ò[寘Âޘʦ­»ûÇ Ü8q×Gz®ø•b)«ÛÆìµ¾embWeå.B–nîs{]ŒoiBøkãVó-aDÛŽK§h©¨)_ôûþ/ÝW»h[`Cv$ÌH ƒAQt"+”òh9¿Nö$¼¬` Hý„±) LJ”¬ò£x«\Ìo ) lÉê1°Y²l¥Kí¥í”œˆjSYJ¸‹#¦hÓM'°ÓÝR/ÅÊ}^͵×'L%Xã±°¼üÿ@ÉÂVX¾,r¥Dé™ÀµAT<ÝÛÀ÷¤Œ%IöB»Ãë:%r›°”ÌKKù®!ªÝZWɲ¶íí*oB—ê{)”›£|Û<¬¬"«&ѨU‚õ\ý©¼ !ÚR)XãÍc+ ”ÅÝËšD Ï“OK2l4à‰_ì#åÑîqéL ˆæHµ¿Þϵ‘r®ÅÝX·8Q¬Üé™,Ѳõa[°*Þµ% +³V.7Ïе¬``DÉ©ÙzdÒð—Ū¥§ày'Ê©À-NêLÞiiòùE¸¬¸ß²PšÁb×Vfé²ÞOùhÀmAÉ\ݱWž½/þµˆé ×;2iÌD-J ˜•ÕœÍKß2È­‡ù·žiñÐnÉÕx“…h\ÿhjì®Ãø?³ãÔ-«øuxëåÐ$õ+ž_ã<×nI \HǨ쫣Úó¥2³¸<|_ ¸:!±oý©üÄ›¼X«9ÑŒÕ|ùHI]H»/ïi º–¯FËu"W⓲œ”.Sí{ˆ7‹WÛ<´¨¢…¹²”;‰ýYŒõš?薡ߪ-ÇM'—Îhõ']-zh±=l€¥ùMíi­«%·ÀÕ¼¡íîZDз,”]WÒgµ¥^îuôŽsþ\ÉÜÕ¾æÎ ß Ü)/ñ·ŽQך,ß ?W?Æ->É Ó—1µ£"ã‚Iò™Æ€¬z£v¢…óo‘¡BÞœBúp¼†þÖ®¹|*[ÆKl¸·HìÓèu)ö.êSE45ö ÅÍ“ÖÊ¿Xòž%6Å á3ïŒTo1ÖÊi26“þaÇ[^šFìO-4‹?ÄYUë}䦯.ÈÙz‹ŠšÇìóùüMàÅ”ù\Á²ˆ^/åīʩ«¶ÈÄ»œ¸ÿ‹M/ÅÚ%ì“j\„âh·uÙ|¤ÀüÀÄ“/2Íúy=ËûÖÈÖŸ¥[^þ5Ö 5w“W??x;'¼Ø:íð¯D7ï­fWÑî7¯‰7»@¶KA´[æ²å'±ž¬}:¯>þv¼ä ù÷œú*­Þ¥ k6î©ï4w»VIîóR×ê±¢Gµ­hRå(”Ű2ª¹e.ñ¥¨ËrJ2á³(˾”FD47Å•¶²U»Õ‘§¬¸•¯h«ÇåQÎ}í:Q~n\m=”å[îKÞÒ{ üàõ<ò‡m YWZ-NÈZ%Tsë1?®e9-^“³D[x¤oØÐ…ݸú‹%ƒ–rO÷.ª½²|@c±Ù£ð‚ ЦËÏSrK0Öóï—Ç¥mžØàà}x¿Ëù„A‰R[Ñ£Á4V´•=7åÄ»|Ÿ¤Æßš*™8¬£iNrÇ— ì–ª»¬¸ÆÐ/î–z¥‘…¯À+–Ê'ò¹ü²7»Ÿök´aòð ŸëìÿÖ{|•Eª EæUf?,i5—†é¡Òz)½;'W”>±cæÚxE§vÁÊ…•αº‚S%ãûe$¤_^õ–(–Ÿy­¶<¹Ÿ&×õ–e>®5Å-(e‰«<µxÚ\@†GZÏX­ÅñmÑ¢]°è X:,µS,@¹9uÑ{@¿«ÆÅ‹d0Yïª l{è[ó“GD«=í\+ÙM ®ŽÆŠ—‰§¹i­5¾ÞCÖ²û'6öÌÂ_eäâíkè#P“2õÑØ¿(}Ö@>Óî½—À›×‚ùÈ šJ¹(¬ÿ*¼[þ¢€xdñøräÑÂ<óÛ1ê¹ð˜rÏ?ˆJ_νe¡-W®tÓˆ‹€¹çà)¸y83±–P[àWÏ­;†lÜÄf´ßŠJ×âAV6Ì–Å}{­Å ¹>Ò³–ñöÙò«¬[!  ¸÷šúZÔeh «ÝâßùýË£œÉ¾åAVPÅÚPµ¶v¹q¤”G­ÜÈê¦Ôd삤š0íêŒVÄ¥À¸qT®¶¸åÇ]—„ãÒ5å}MÚÕXî5e’·„ )®Vq•õ ›§kÍäÞó- 3Z½´ý+aÁ_éuå[²V^¥õ«.¾@¢¿HZü]T9¼Ê˜µÔßÅËXúV‡Ú…À¿Ru–eƒf«<•Š~®t9ëˆ|‹2˜e.gVÁ ç÷[cÞ·à¨bœÊ’Ñ–-«asOãßóÊæÐ²ÅqGw¯ lùÁ,Þ×ò h­0³qeQ(Ð 2-ü,u.;:úÛ&¥d‰ÇE)AzÅ—•ugßO.kivùŸ—ÿꉵ½ÓšÝ ¨’Ï/£ëy§š›¡¡òò)?ó>a¿É§%ºz{V¹ËAó´Wµ¡@YYAË"ÄÎÅꣴa¾¬Ý#òh·¸ µF&y,@–IN¿¨Ý¥ÀjÞ‘ª-®RŠ-Ø)?j¢#7M©k)øÊOJ´ÆŒ~Ç)Da¿„²Ð^ª,=×\'ã @©L øjÀïX€Z徜ülí®¿pwäØgþwb+hWóiQ_‡{ÕC‹øê·¼ºq¿1[kêr´D—Ù²ýC]¶OsiºÀä|Õ\=Bùäâ:uÍÄ‘b.‹¿ð–U›F™|{†Ý&$’|£5Òš[á1»øõØŠºïž QX¦½§q"+EùåÝ™ôÒ$ßë“5ÿh[n¸²å”õ-KB´Ïgr#‹lК,À´h4 ¾‘å1EÞ­¬2«H/Ù¶©€ jë¬>‘ëje [®@–I,öÈŽËëEcÖâYq»0?jŒ vaÄ{wdŽÛ°ù{Þ´Ôn¾5oœ»)ÕU`­®mùú–Åžt÷^©kկئç¢óð§ÍWcOú à–u¤ôWµ…¬ÛmœRßKÑtHX8®Ü8ð-×ý­ÅTY/ ¹c¾íÛ?Ù%ÏÒÚM“½–‹®Úç©K°¢1+P<«ÓI)Ã…—ònPŽUo¶lÒØk½·@nû»œoÍÏDV—¿òØ µñ>ð§ìÜ5;N|ù3ßËÁÀ´›NÖè’¦ð½9|8—_îýJÝ/ç¯z.Ú'/ÙzùÆ‘íªvð]èmxõ¹þž’‰JÕMuTÉXlAãËÔÝ'¾òŠãÕW_ÍÀ¯~ýúÜkŒL\ffcdTfœf^LÜEL‚gžù3æyCo£h˜v&i¦štdVj …ùÙA‰x45fœ©‹©™1 6³q>!Nc”tŸëËÈLe—Š·45lNÿ¨ŸÚ]µfÛK^Û®—hfFxqÔ@dB;òlIŸ}]²AüýÞ§Ÿc{ú¯{õ“;öĨ:Òö?PW]w…þº¸¯™ªbßB3¿ðÁ*ÀLJu2-꧆Rý?ÿ™º”€ÓÔ_ñ_Ö+ð>ÿ{Þ)u:}ôïóCEïH[aŠ€ÇÇñY¶ÊšðØC‡PßÓÉoºì|£t˜aÉæ–ôïæ¾<Ž“‰”Ø–w¼Í˜Ù[š}Ì4³%eª›bĘ™CNõþÈ$,þÅ¢9ˆ4ÃÁôFX&›-h³GÌܘ„ÌXµÈ]ó¤­Ÿ/Œ“ø'÷§qÿ³ÂgÜ óÓ¼H©3ŒI¿Cú·ónÛ(Ÿþœùt¬´ÄÄ¢ÖL`›cá7 ¹5ÙÖ,NàÍ5GD´qaªàNÿD;ä•yæ,§íÕï›BGgê Hf>„^‡ˆ:ȶ‰exç{³Y¢Èš+mLÏäÂfhöÞe,õx8ÓQò˜ÚeÄ"æ(ïW“O­Ùl н™”¡ÿßRK˜OhŸíÓ?› xÏ¡ŽU°ù‰¹myêü&4q5t¬2ŸâÝ$ÇXºx ‡¥Ç൙òÿÄ_€æSÊx9#û°ƒÀÏóÊ:šqp²7=[å5ŠÙ*ÑÜ\êÍtj£'E‚èEù×_DJô4¦fWhó$~ÿX$aÓ>˜ñß:jœ%3 ’{ÑïÃø99¸Uáñ ÌÚãs*ý?Ô›m^µ–Áæ”d+slú3<,_%ÁnO{K’L*¸ª1‹!‰OÔP:‰ ­ÌCÞ óF¨¾Q©QZÍqÉEÌöõÓÌ»¡wÀªÜœ–l ­Õ™‹ ©‘¡çQùeï%zÀS‘9æn6öÞ5k0Žž„÷þÚ—ü9ó"¼ù:ÆõŒ‡¸Ð0N4n®ŒˆWÃóÆÑT¢kÀöz³R¢ÖÁ‹Ñ;@¦é›\Ìœ‘þÕ<1—®jº˜g’3MWúë#æƒuÓ?EÖc%ó%Ù¹ c2ó!ºƒÂÆèc:2—Ò71n2Ë&jÌ ôWÜ{±޼•0‡3æ_†OoË\–ä÷ ™±¯çø|ƒø=ö[}Dè~±Z–›™CQŸw2é0Ä13¨Ý÷NB•y.¼d4/­ùâ.̽ðŒÑŠòƒÄƤ€{z…Ø>|ÍAµ÷š–é)æÎº÷RrßÓðŒ/áƒ/Цݙo€/–»jÓ~ÉöæFÞñfoNÑJiÌÞ™X“¹ñÓ»nïûB.¾x JKtE®;ú0s’òÈ{C“ãc©£8I‰e«/ÓmM½Ÿ ¯¦ær޽’ŒõE&_vƘk’혟~6ƒÊ˜ ™â6™'mÞ«€¶Ây4ýïE™ï÷¡Ý'ñy­é‹‹(&fô‚¾ôQœŒ‘,æVãÍd¢Í £JYa)ͨÍ'f³µÏ¼`f›÷Ü3{¯1/ŠÃn•—$!­X@ø{f HÈvËWZ)°Ñ°l±\Ë{¦†£C ª‹[Ÿ¨8öÞZ°b¦‘Wt­‚ôLqlP[irÿÙ¹§gzÇåùÿkÑÜ‚6.Vžò²¡œxêÓ_œºäÈ)ÑQ}ª<Âm«I>Nå&º$JYzÉÜú[%©½ÚÂX”R?…C+úP‹1}µ0ñÄ3Ï„£˜(¾¥~cë®Ù4±Ù¡¸)•€‚ qƒ-j ”‚r¨7*‡&¾ç½!¼ÝÄ…4rö‰]™Ôw¢ÞïÉø×"Ïþ†R->¤âå‹k@wo%7Ï# #f–ðh <=EšÎ¤¿…òi”jK½#á”Cù=C#©NÂûÙ|5ÖÄO4 "ý0‡g#Å\hœ¬€BÖÇw±Øò•Š¥ÿ ÷ísp @¹ùGÙ » Aö Ê”*‡u µK›×ˆÿ5 ‹èíˆs*Bö»p”ÏP‡ñ Ù~Ǥñ1±G›¥h—Ö‰$ Ixö0Auæ¹R3HòhvghöM_p¿  óeB*ûÿݸˆ ´µ‡ ëæé…1š]Å=Ô€”¨ p´=㸟è wŠ ÓN)À­â3¤Ú¥ø;– %F=©6hæ¥}pÿ€¤îÈ kðù<ÔÏm‰îËé”+šü.C¡D÷A!Î}µ”æÿ“ ‡ißøtb1ètT É<· á!(»[2?¬œþÁW¢^#?÷]¢ä±% 9Ñëĵcƒ-¬>-Ö#õ¨ž6½ç¥euÄœ ühª=´:“XAxŽØïA§+| p+:W™õ¤ÍÕÄL;È Áæ)pñ”Ðç yµŸÂ{ÄŸà´ÙdÊÓ;+ý”!HcÔ†_Ûñî¯ð̽¯çX—™OýØØ™ïäÆÐ3t†÷^”˜‚ù* äÏþ;‡bÏ·Ÿz·0mä¾<+ ,âÔÊíÏòUðËr_ñc «¯hsÎÓië9f%®–Ö˜Ê/x/´aãçŠXŒK¨ÞÊw–rr˸zƒ|C/‘¢ïrdšÅ¨ÏNôõ(ÒÙň¸¢”çÁÈL/n5”iK”lÌ‹ÔAøÅµÉ6fYÒÝÄìò$e©¯Š‡ox<Ñ¢Åú­©cN¦šmþí}oºy#Í@ï»üÞI¬Kš…øØ·ô³¨ø¾°ÑÉ2ÍÍÓÉ^,žzðÅ_ÍKðºRãýkâ~àÓ×¼’4 Vx—ûzz-ó¹SOh±öb@ßVÌEðAfó†±Ð›W©7oóÖÅæ1ñ3ÉòÓøHž‹£§@ƒÿ[Þµƒ¬+ªá=´Ô ú/W/°ùiüÝŒìº}.šü«‡¹ÑŸµ¼‰ÚÏP¯„¹ ~éÑêã=éK“\ÅÎä y¯Ò¶R[«ŠµÖÀà­ˆSç/(GV:5©Ã™7Gð8šûˆ®·¢OÆQ¦ ’…eÔ È )¶Ñ\U©(¥¤¢¸{f€ßZ®Û’©&Þ†„³èªÆ˜®¬ Ô‚P'¹kåˆ+ðÏÕ#×|önþU¯%yO•«üã@ßJK©ôíBùP;hÅ/Üw*efæ]»¼bù-zGÕCu‹£ÙòÎm‘­X-›þY1I¥KxŠû]3«Ü74_õãô ­=ã¿m¸+â“WXÚ/Æ]°Ò™¨za™UÎ$égЈÿVõùhhüq׈Õü{e•@xB`6ÞG¯z7¤¶éWH]J4Í/@À’€¦· ¸sOBÒ®M Ü“ð¿;ùe(8ZÔkÍ?J6H¹Í# ]OÏE˜Þ“~y‚Ù\J˜,-µÔ’Pw?ŠgE¶Û>­ ÊŒé×ÉwHæWR´²ÐwÌpïÓÕ¨LƒI[ ô¨Qä-)zÍ  µâÇÉŽ>¸4EJ=ÙžOZ€s¼|c”óg¨ÏïB™]:§(ŸÎ,èK#ÐæFøý§ÐýÊR>A>#3}#š0µ˜yz±é´¨z”ìÄfä&ñ?£^ÇËó-˜¾äÞâÐæÝfHb.f>§æ±;È3ÛA×RîÇ¢èfÊËõ ô½µÜ%¡iÁ‡­—X˜ý™7U ú*žÛZ\"÷N Íòf¼º¶[%ûå,m¿.Ñ6aÀª-e]„B?eXªö#,Ìà}ôþ₳à¶$Ïgà½ÇóÂÑÁÃ& ¸ñ±Þ_í'ëÛIÔ ^9ãIÝ@b¾×(5|š–S[ Huô-ÄÇÔœŠ‚¶³ù:ý´Fskø‰¿@S½< ÝD0Œ,¯ÄZ|}*p¨XíËŸsÞþ‚p4t‹†cY¬øúi³'õÕB™hR4,C5©¿«ÇTRÕ‹fg÷‹hÊíû è_¼ŽV€¿kQI;Ìâô“X‡|–dªíGÕ Ù!ÕÇì‘Ï£Þ¸lQ½ÞÒO$ow^­‰L²éŠ^ˆùTj)!A Vƒ\"CâŠLî+h»àòû.ËJJç\IÜÒ¹EÇ¡zEM“UTÄÞû>¦üf›7¹y¹”{¿)¯µAû·¼«Üh e¾øæ–ŽPÊ„ë¡{Û¹–å²DW;©:üü]ëg÷YøúùçŸߊü­ºkÁ¢Ü|£2‘ÒwÜ®{"0h„ýuA–ÖzŸ Ô#оE÷jW;é»5U»GÑ”˜;¾ÕÌ%L” º÷s‘w].=Í÷Wü{¾H½šª>?´è5U±•å[ûwirU~K16»26çßÖ/mÝ)®ç¦ùxGcë‰HΨšèlÒÍ®6-ê.2ýÒã³­£Ý×d¬n%–ÚÒ¦„N¿M.aÚ4»ÐìT‡P[£+$ùŸÙ‘\ÿ"ýÆs‚ž/ÊÇ‚Y}Å}ë¿Õ#?H6Ù–mo§!ƒr€×“HL˜X•oYŽä‚á’1&óÎcÞ[)&C<{° „ÉMPñÐ…Ç“³Q4·\žžžUN³ÂÚÚèÐl? 'ÉÿÝ™Ÿ‚¸&æßöYÎßüi„‰óÌ™@7Ðþ=xÏ¡¶ý#RÞjGç`\~.|\åÑȦÔgüP.®¥\ZR[e%®p>}¼üDz¹ ò.Ò7b‹mgܺ‘Å  ¹+µ`‚>Nš÷S‹›õ?ù‹ žm=l}‹riáE ¢moEp=•‡[¡Œžoj™‡øó¶ÐoêæžÄBÉ4s‚üâ^5éßÌ˾˓ïó\ž|šñv—2Š€¾>óÔ2ÔMF£BqTŸ|:R«±<×û}S„ÆB!’µöæ´…Ôî»iÓöäÍXlKŸL4Ñ®ð¦*óLu§¶KŸÌîn­¸ÿIò`ócúsã%-û-w'—ÜRìîý9Ïg#7Ò…¬|ÎjH˜whç¶€ k§62mæK )er‘gÑD‡qcUc_ ‡Õÿì+Ò¦îQèbîæ¨&—ëâ\þûYôйÞ-dp!ÐG €B>’U-7Ð=ÑL9r*²à×Kô€N-ÌkЈxî$> ƒùW`YU½†•~aÐ豟§º³ ©û T_LúÕÒ?šC÷ѯgòÛ­³ZT4\,h¡PÜñûb‘ì³Äj€Ò["<Åú$±9]6‰ë¯üO"WFí{\’ÖÝ•¶ ßú0b|Úìó[n[^vÚWà¸Z5Š&£ Ƭ‹(xUúlY@”Ö> ŸŸ» ¤·Ø­þZ`ÝšŒö‚þåÇ/öKóƒvéyrjÃnÙ8ê%É5n¨á§™o‘ÒFµ(:§^??7WÂg¦_aAyôíá‡_½@ºb³]T{»u¨ôšC|ÍŠ´×]¼•\.ÊG˜ˆÞíÆÄn¸íx"=ˬ;.^¬ñ­8îØæÍ`Œò(gÆmö† ôDÚò-ß*ò6ß¹0ËÉM¿S‚ÜiqòúíÌÌB¼ã~  ž}†]Ádãhœ-Ä\9‡Qé}š½m/ÎcN’XÇ¿í>0ÌÛ?³ñÚƒæìU÷ˆé}ëì/³O+ûÖbÓué?põ1‚û£|ÎkÏR¹áÓžšŠ¦³R‰íóŒN³2å6Ðß×ТÆtTð Õ'ÖÿJ[—G…rGt1¼ùiÆÜ ¼¯hç&§Ÿ£Ê)vo9úæwŸßsÀ›´%™ñ4ú˜9+}[±¬ýÙGȃ—àªmL†+è]÷väÉ‹3΢0†r+ó&m§åÎÞÙ|S»šÎÍzšýÿü™Y‚ƒäÓµâÉEË^¸Ü(Æ{Lº/íÚØ)Q¡•y8ÕÝ<bÂ]ž¦î_ky6±!ý3–ÄßÑU²ñqçâ"xaæ­`nõ¼¨È§%Äù' ‘c;ùñåÂàãLgæ?)ü% èçL£Æ«…©Jß9ðÀó"I°hÍ;|ß 2ÊËÒÿ±ƒ[@¥%‹8?ÊG}´I§BÂ…eϯ;²ªÖ¤-Kñ@½Ë/ÙíÃü'•ÿ²¨üòjå²Ò€úì3w ÕÚò¥ü~ å“L&MrÆÌ<…±yóæ¦K—.fÓM75ë­·žiÝZâ¨1}O?ýtÿz~ý»úê«Í®»îšWÜ•§ž'yšàÇ5×\ÃØö`–õ±ÓÕн&”°;í$ï«È¢zOŠñã@U=] ƒÛ.óblq,2å¿öKÂùu²b8­ˆÐö—7Àܳ©Â¸øj$ÖB˜Ø–Ž'ïÉšüúœOyÂQ^ÒðÇglÍZe84—7ZM_A™Q?ÒŒàqÃKŒœ—D+ ÃÀ¶¯»ÞtG¨~‰ßª÷>apÕŽ. YΪ8œâk^Xp­ U¾ætuR²A[ã]b²wò.6"öõlO\ŽzXߨ6‚¸q±ÅîÒ‹4]ÿ(ö>”ÿ¦Í7•µW6a%©Þ :Ë¡R$’Õñü¯Q¶“øÅŠ„ì߀ÅB+ ü ¢Œ» èÀ'øYTå¹i3I‹|‰F¶Ûnë€[…pÀÓžþžL¬™ÎC)k÷¡Ä<bÀ ­¿.¸ëD7]x÷kPžOƒÆG¹¶§ËõÇGÌ7/¥g›šV¶È@è–Lr> Rv­ÿSMaÑ¡µ¹<ç'Úd"Ÿ ?Jnˆ0þ¥ÙvÞ¥æü$[p9îc–°}*Σì{¡?)ßÝê¿÷Z·~áë€ëR¶¬?Ž ’;FEðîCQHÙ¾PØzù¹jëªÜFHÖnËGï¾m«vù ¥€´¤ðê§³xZ6±äù+•e“‚èâÜÄŠŒ¹ßÍ–-(Ð>Ólù¹Ê*¼ö2ªwÏ_ÌVðkoc»œ¸Ï çgÑÏGAã=i'ÛŸnz÷Zòùº¸ åÕ˜áÇ¢¿ŸòB‰Ì‚œ÷½ ?Y¯‹†™uL"¹câ²b|ä…o™»Î»þ±:Û··¯íaîœÛ—w d†¥ÙƒeYK‰—%¼ä™Ç)S‹ƒqAßk2’DéרèÈgEêén(}'}ñ81sYÀ)úeîýŒç[øm°ô C‹þm\Ô›Â}ÙÉAœ¦þ/×ѵsK†£' M?ã3Ð}Рë³áË1öAf˜ÊI;).eqN s#µ8Kµi/h¯Ü]¦ÛÂsµ³O@¢úþÊ™Rs=9¡‘Õõ¡_æ æ¶1¶FRø81‹·€FäøhBà—h 7«Ê–è îÍRõÌ|ÿX|?®wúPÖk€;rb2Úç*Æp˜\Á\ä'tþiî”îªúV6ÔÞÐïáôŸæ<êØŒÄ¶_Gñž»±œ~?s@Kêkó¾ö—oïciƒÜ|”*ëiKV†Q½š'ÿœ:Þ4OO4;³0ÿs³^fÙ?÷ÅÚu<ËÇuæGÞÝ ÐÒF;&››çêÝØ\Ëúý"@tù­×áAy1åFnΡÐàÇy·í[‚¶©ßÍbã ôA?Þ]<ÀmÍUxçs‘*¯á­?°‰«ü–æá2JtgÜŸ…,÷)… (;·Ý ©µÐM‡@×ß—*:âí´÷cè­'÷C{QAå)ªÖR*2EýÍ,Í#åßüÚÿ)°òh))”ÅÂ(z«!„æ@ú)T{g“ì‘_õ ¤¬€Y3æ‡Rð"§_›Alƒ?‹èÛ"ÅÏIåÏç3#â_†b»;ÊðªX¾ºs¾úkL†ŸK¸…Sòý';ãçg¦’d¹ng=ÈS–…ÿ _ñÇ Õ*£ºˆÆ8Ë¡¬ÌlÐÜ>Z²Ö”>°5guû˜¾à½ë[šHx!Ö~—yr;‚öúéZò f/Yç– RN$S÷D©<%Euú7`Çz€Ãi·ÉŒUYv½@ŒcœD‚¾©sê'д‚ùQ;~.&ÏU¡‚}Ýò{3èß?œ¬þ³Cí•æìä·fä¼³|`PãxÀå‹Üö7uÇùÊÂ(çïÀGei:‘ùùÉ Tñ6uÓ§éƒT–:ߺ¶;å?ÇâØòȓǦ_4·×?ÕèÅËú_œ­|>F‹qX Z ´ì=z¬Ž1‚ßÊb“Ì)ïWsÆÜÛ‰;¶XÔÈg#¡Ø»4²ï¯€¾’‰Ÿäø± ”;¿ó æž©¶Y¶'7€ÖïÂÏá óOhr¼[¼ºo‚yØ_$zˆØaà×f| Ü*z‘r,À5˜zdbdlINÆv? _å—>Á]²†Ôø“+íÜ+&0.æ&rn{äçûdÚó@Þ¥pÞ—dú½ŸÝ¦P~KvH ’ dAÚ…Ö< ÓÖWÔ÷W‚¾Z ]‹¹èxxD”u®ú­M·š­Ìæ É~õ‹.væÒ”¾z~o OÏÒ†“D l—³ÃA|W|D–ð’ÌÆGО@IÍý“g¹w.ÀñxäV $ú€gz4O4‹Z~7?Ô!›Úåfƒ)ßœ±: ÀÆè/ˆ/”1Êw^H$c\–àP³…Ù¡ÔÜŸ ¯](ó1殯¡Çe"v êe°Õ‰±¶i`ì¸r¿ÞGz©}F ¾<[Ã?gûÖ¢×Жr¢:(XÓõ…ŒÙ;‘¥D¡‹ñÙƒºÈ•ÎxÚà{Ú?#ñ*ªtp×SÔõ}xó€ŒÜ¨…ÌŽØœìfŽ'ÝÕ‰aŒáßÌïÉfçú$`X`mkóÐwGÞ~:@ñyéo9ïN÷‘­CîŽ$ßW¨·€tîÁ–bÜÄZøT@”÷%÷¿ðŸi1ãŸ,Ë"R|âÚi(eØú4~¢}?oÌ9F.#f÷ •AÉ÷07¶Œ¸Gá«UrG-Æ¢Ò—:€­‰t8æðIÉ7šïs£!*×ü{:ðM†Xâ¹áàÒ¦â$?䤪º³¹•'5äGù ©Mþ’€!À>ŸÒf_†Ú¬3»&VBîÔøÚ@œ=k× YÝ):öäèÛº+kû?/… ŽôãT üZA(¾¤ùûÄõÏ[iÉ¿4&3q wAnçvƒ/üzL\7Ë_¹™Z¥J 3´™Ê vK™€3¹¸å‚c1†Æ¡>á®…À¯-Àº–%qŠø >:hN‚¢&‰Ê—&‚I…)dÁ3¾j{ þw­ÿæt–‘¿ÿþûþ£N4å7]¡•ð¶X¾ýØ®ôƒ=™=.¯Æ¼ßÁ¼'LzK„¯(`£1ËŠËKý¬÷½<Ðv­ÿ¾ð+Bìp^Ëçnùšm¹»-ëßâóïd”‡Èˆ ØÍ8ßlŪ©CH®Fh¾¡¾PÙÍOé»(hhXœ îº îNÑZX&Ì PK@ÜðŒ%£,/mØ€²1ÃªÌ SÞ¦ŒC ªýÙÖ//r½Ù’m­gÊ-OBõø÷ú‘÷ûÂWÏJ¾$HX²$ÚÜe³±^TÚPNnÞ ÖÁoÉ2% y‚”¼^ðÍQçG(]RªÅߥˆ Ì)äÛvxÜl—:×l ²°ã(^ùÔò#Âþ#ô÷øØ"V`¶i ¯^ÕçÔs| uZjIjÅÎNG‡k´8YL}гi§bŠ—\<áxÍ­IN¯åÓ’g;v­‚MÀd%EÉ #¨ãˆð¢:òx,%xÓD½¹zwá<ÕKÀršó¥QŠ\…“|¯õܲã\ÖÉ J©q|mª rRŠCÖZš¾lùÄsY_ÊúÙ®-‹— ´À²£ÖëP7ùé-0ð[ZܲVi»dè¼GÌöÌTòÕ':¿KyÕøê0ðË=•rãTs®üiAOŠ4EÐÜ¥6Ê[ IöaL­JEŽCAMûòŸ¬Öþ@~À£)±ðv,d Œ:Œ7è« 0&Ê ÀùoóÕe‘OÜ›?ð>¾Åôýîí‚k-©?¬e¢"¨_î³ãØáÁïA÷ ²^Ô¸P\X™çÀÚ¾e«â,Çõ¤Ä2ð³îÔÿ¬ §›/áÙ~I? ÷*1K·†|ÇßÁ–å{°Ì–ŰøÿSÞ óíœáæ”àö<—e•JöƒoŠ/ È>…ù¬TðA¢Œµ§âŽƒ6¿d|º¾ £ò8 kÙÄ{lýþ9º–|©%L}nˆ¤yT²‹y–­áýýÖçf(ìÎxÜ^s)|#Š?‰†´œk­2CÉó~êDxùR¶ãUÌÿ†w*mÒðµÙdÞ1jY‡¥¿ü|;‘WJe?JÕ[óû&, \o{ˆ~>8ÃÓÂ¥È'~WÓµëÈ"ç#·ÈÄãð£6$ÈÛ%Æ‚ÍÛòQý–/àc°t¿ ÚZë‰ê;û‹ÒI3%µ„YŠÅa•1:¼ˆ…Iɇ7!Ç(Ì„óÊHIáÝ “…«vdhëXxëÕ¼#fXÈ3óØ­4;»Ð¬ƒfwCÿÕ¢ÅþŒÓZDZJ§:пù³‚JËK?ÿä]žawÆÑÈ.³áuÛ1ž‹j֢݆AÿvtOa,é­Ì¼>Ð3wŠyÿ¹g°àçñ'—?›P«g1l ãývñÚ’Ûo‰©‚Å<éŸ+ÓoC¨¿¬ßan‘|îß„Åç‰Ì¡¥å«aVWÂ7ˆ €_ÕÝò7niÜ»ahöÍÜ» ¹fÎð†ø>¿×…ÇÝËïâ¼Ìå¹ )ݦÕº|HK^«6lAk= ìR?…5Q–ObE³Ó<4 W Ò6…f»‘ÏÌ¥ù’P.›Ué“§»W3®,(Ÿ{*ÉeA¢'i+vd5e]¥uæZqgxâÆ®»kH;”þ2TP…ùŠ’{í”?‚42¦™DßJÒÁóÜÿ)Œµ{²?n‘ý¡“¾Ã~ÉâcÇ?Ñ Ùw àœÊw~=)=‰Î¯š”_Ž€ÇúzËâãÓÉRG ~S¶·Ñs}$¯ƒÒýQ…à ú?`MÑõ–¦!#æ/àvbÃ× øÓ/wžo­£>´­aOcmJ6”JIÌrÕ»uë¾]Õo ÖÆèGå#¥-×bAutg5èy%¹@IDATeLÑÞ âÚÿR$~£VM£vÙR ¿gP¦¦ÉÕ(ÿßî}QTÃÛZNB蚘¯á9!BíÂ5ÕU!-y8~CuÉI¥‰DJk䉪Ënú„²éÛË*F¼dW€˜¦(ʪBÙ‘:ó,…ž }´•ÎFürü‹ÖrJÖ;! +œZ¾¥>h >mЗ1Îvˆ³‚ë(<â%W°èbƒ”¡•¡²Êih8ÅZ'X¿GýEã{Uh{ +6ßÅ8|VóŒ¬pNÀÆœôxT yd+”·Ø9‹íÊf¡»0ý1¥âê¯ÇŸíÉæ×ÄîuPž'g¼ÿ(êŸDÑ5°xɤö6Óáfaõã†Eø¡ï´-ûR„ï¨pí³5ýûð!ù<Î/—\…z .H¢ÃÃä»2ëÀwº2¶t+Ñ®[ôDéÌ}ƒØ&Ý7sXφù­úå1¶‰Û¶ÓsñǧÚPVè=‚vv\‰é s'¬LÝ9½}™ô XoÕrðn!h‹î|´­öºú?|!Y>ðndœý‡qf%Õ£Tö8í‹Èõ‡Î6†¶7ƒîÏ¡­sUK”íciÏõQ¾÷á=tÎtļM:ׯ°+¯Èªpe@µ‡KÛþMþ Èë²·6†¶Ý›úsí#¿?·å}åË2pCb{ ´¬÷^ÆbvQ€ížžÉXqë’Í” kµr(pCŒ-Ü<£®µmùvÚy=ÆD¶ï-LZ-!Ÿ’ÆTä¾ ÷fQ95재e߀×t¢­Ïä´ûcÏ#í‡*+s t"¹®¥Õ•™q¡“¸m[‡s“äy6 ™úËkw‚þN€¿º‹nÚƒ±u€Ú®€ª–ϸÏu}4²:—aùèPÂ[øOÀ·à€¹xú£éI¿<Žeawmr.Êk—áÇ:é žßÑ_>Ê,Z}%í•ìl–Æ2óYê¶e´µ qï©“ûQŸ°Œ©3d%/ËŨ Ö›Ô¯E¹Ä‡NÜÂÜzh;½ô7Ü@¿kg/Fú”õBrü5îa™3’ûÓܨÙkùi/oZñ²X@ÔnG¹qQ?ZÚnÇõE¼ëXx‚O5_Þý_?ÑN üıáxÆøO€ÁQ~mô¾Âj2gqjÓ”ûÝ™Z|—܆1÷Iâ;·âM64ú‰þº™:Û Ãœõ3ï²#-ò&ý¯]zz·  Cwׯ¬YßÂoð ð#~ ñI*W9òIý‘ÃDŒÏ¹éàÐÊÖÜÑ,* Û w7ÔÐÍœ¹ësÀâ÷ŽÂ/ü¼Ï»ôƒ¤ÝÁЊå™+ëxÞaý,1kFÅóE-o´äjò.òÔ)ÙNþ‰„G¼íë×9:Ш;1_ wÞ÷– Ý´C99M®[´ûR”y´Ö /æÉÓÍugâÚ‡ÅÆþnŒG×/éPj1–9Zîdä7Us¹‚,­ÇÐâI¥ÃD”÷ |Gø‡0ÞsT©1o%z"dÜôò1sèSä\iU_Y7ËÅ…ä¡K¡•¨ ™/ΦÚ>{“þßÐWü•4§É­Ôýä.+ô«àI’µŠIèÚ9òõ”aÝß.$ïûî,8œHÕ£i¯QÞ)± å==?ë/¤kåÆÔ=ïVYv´hGêšÌá’Ã;Jb\ `œYþbë(Wdw1FïaþºÎŸ«ì“Òßùœ®H|’÷qf¢.-ïÑܹlIûá3sæLÓ¾}{³Øb‹ù«•y‘ø±,/ð•ÏÖÂOJÿÖ? ¸TÉ«g”N>~] Ó-;Î7¯ǽûvŸ5ÆõgU Ð-¡':hº®&W…ðVþb ¯â‹e¨ßä#ªåªO’a!¡/ÊrZy|³ ‚Kú4”¹QðUü ¢7êaÆ™5×\³Ayªþ+#ÉokCƒ&Ô(Z£hl…ô¢-ŸÁÔ_~­Ú´icZ(üâRihT0}Ö,óÇäEíÈÂûJ&-<Ðf¹3Bˆ˜™KO?楚??$$MÈŒ‹†•Hûà“+šµq¿ŒUK ´òô¿¿®iÚÊ` Ë=‰à¥±^Ä(7Wb=µ±Òp‚G+,¯´цëfA„çÆÌý½PdÜqÖ„²lo¡äh@$)·RztÈ[9ÛÚlÙQß5ØÜ¬Š¤S¤58\ÄöÐwž5öd…)kAYkªÌÃy'ˆ~|¢š Á\VL?K,a.µãõ_Té¾LÌß°bcKBýÉ©Ý!0=¾X.ye<‚µ”üü…ƒ€Sü6M›ÉÔ+*ô§ÿFR'ñs-žú!¹%¦s3Y¿ÉÏ _¹aÐá9Ÿ¡Ê‚s„Àûäf÷Ôö‚óœï3ýÛú«ÍC´h)UH ä“X‚ (ŸtnÐ\­v¼eó6@šµ)'v¤ 8½?àÔ^€:ÝúÞã4—®<ûWÖ‚6“2A›¦x'”ÔÀYCæ~ÍU€ð}̳ÁªPç¶„CQaëx{,•ô[a3ò\`bVÅ‚)H²BÔÖï ´Õ“ÔësæI¹Òx…wÐöOâh[¯…ÏMò@ñÞ0žë¡‰OØrðLoìÉ{¨=4NíÁ¤\õZXGRVôµà‘û\`ö›(íaHLVkÏÑOZ‘/ïEø­†d!®÷Ñ¡xA¹«AÏë²Ý¾»ŒòöUøÅ¸Öun¹uý2´°7m›÷nФ%yMÛ¢“Üȸ.:6X!µ‰øŒÂöÔGcÎ¥mÃlËâê­8çGý7†‚ím=…¯O'ôU¶]÷ñÞuNM$‡É ¦˜t¨ËÞBA,Ö^w1>‚D£’cî‚ï÷K ÆlÐ,R1~†eG“jU:ˆæß†÷´¦ï´ eƒ,ZoÁ¶Í{cÚbiâô…‡ ¢ÎSí¸µiô-W*s²©Ü'Áõ0ÞâP€ú~ýóŸïIýÏÈ?‘q 0«XP9šÏ^«ˆÏK´ß´lôµ©›À¿q|¤¤?’®úJO:”q/—er?¤v?ƒò47_/Ô˜ÓB¨ú\s–\,œ¡¬´s8›5bÑÁWÚ­÷õ°ÜSsí”2>Q—õkË­lP¬›ìÍ2/´«äžÔjæá…N6wÏþ‰:}›²+|t?Æâ¹¼›ÆJ@Õ›¡#Ñ¢Zeúò–²}ñí£…Ø‡™ß`úßÈX ¼èG럔¢rHÏ÷¸Ü™ënÔWþ|_„^d½è™Eütí·-ürß²ô½>Pßúç$þiFËW5¶¯‚£ûã^ 6tXÝ“ùhoö,´YÏË Ô–±Œä d=×¹EYÚ½ñ óë×ìNµïjãû>…q w-û­ñÙ̯_&{0·ìlZÌ;“ÖúÖÏB øyTг´ËHꩱ÷ ñü‹‘>– ƒ2\C°x0fÀ'Røÿ­Ë.æ»ùŠþº“ÏEŒŸKX|ž ï;„vÆw1ޒ˃†óúç~F\IGo!¹<óôkˆESäÄùV¬ ’±Äu´áøÈ¾[Û,•ÚÊÜ˹¥'š¾Œ7ÈRxxª“9›q'#‹JÏ™Ðܺ'óý|+ˆNJ톰(§(]cñfÉ­¢ù fÕ—°1m¤QáÏã hê?äWŒ^}9ù)Å TjÀD˧Àãr"?v9¿Ô7á0…b\â•áxQ¿Ë~Ïq˜hTFî=¹!xöÙgÍ3Ï<ãÞ6K.¹¤ÙÿÌZk­ Û@¥W‡Oo~jÛÖ†ó"Çü(K«@æÄ…˜1·u*ãÌ ›³Q´Š¥•Ú3]MÍ$rØEM_é·Ú@[!e½¨#*¢Ùva®(ÄÀãü£*…Ü%(„$óo–ú''Ý>1Ó@8º†ý§íJmËwÓ«M%¸Ëgcn¬àZí¡•Ä¸`'Ñ §>áø*³=íeêðó¦þ=eÊ”¿4e9ÖæQÎ{Æ·vtjhŽQx·¶e‰1_,(_Åè8{BU~ÐU;LtÞFÏÜï”õoü ñªîŒ¼úëÝŽP8;µ-‚J¯¼gåVÞŒUZÿʺüee×>MŸÜß|¬AU ¸FÊe´fË’7ÀG¡œíýQÙ¬‰’z ICêçd³’Ÿí?á×Ñ\A}þ Ò¥l“´Bça(€ë¡PìÀ¥ÈzëAÞ+A%R¸ªøš¬kñÙoÖ&d!%3ظJÖo\K9ÖöË¡ð‰'ßý¯¡LVÊ3¥ôÊ•Â-È<'¢LþL^®õ²_pÞ¿±LTksçë¼»G×oÀÒæœt`$2—d½§yÏð:ý[–x½ Ô~q[µ%ÝZ€H'¢tk›t^H?ÈÄû·r³®¶nL=êㆴáT%H4½±(´êÝÙ€þÆ<•Q õ8.L¥¾·£L Qa,QÄø¸¾ÍJAîH/€öDoh~UÓ·î(ßú¯0GfÊ“€-‹IÕÖ÷ƒWw÷©uôU:)ª°u[dúw Šj)ÐgèöXu “|@¾@ÏØ~QÞÖºR–Íý2ó‘ê2/ MÔq%?‚„ÔQ;cÜ:ø"þ]Íiaýh4K…<`4Yòe8¨Ÿî¢Ý¬/mù”Þ…±j%½,Øœ\Ϭ^{Œ¹î»øÜü¹"À"\f5¿Eç:Œ..haxShå>,­â·±îGµõªÔ¡32|ÞâC¨À#—ûÀWýƒªhÿp¸z}ˆO¹ó§ä"öo9Ô$_ËÅÜnü€mÿd ³”·4IIXzGµ«Œp\…•|ñ‚-Ï>.Ò7n¾²ÊÕ"æýQúÈYo0öÜ ?º–7\Âõn´™À%;¯¸qíµÆáüТ’ÝmbŸé[Öàqn¾ôŽëÀ+ßÀŠóöô ÞŠ…ÊÍ«×ù»DÏŒE!¾ o4–ôZü¸ðP`ÉáÌÙÒËÎbìÊj÷1rÔ‚‘ü¡j×\܇’,$vcÌ1ÍymŸ 0ëM.¤bÝ*èýÎcqS¾sÅ?ú’k›µøÚ±Ì Õ,ò’4Ä'Â#M;¦¦ß1Sg­ŒÎƺxŒwïS‹-mØq¢z+ ¡Ö{r8Õ™AþÇÓwÿ¯hLQÙ v¸ž¶rÃÒäsóº^‹[À. 7Aûte))—ËÑÌs‡3uÈ¡‰'ÑGYh ùꛚmÌ­ó^7ï§'pèR3s8}7‚öµï#ÿšÖM•ÍY}?†|ÂAi4oDËJ?Þ>± DçãcWÀw© GY Ê­‡á½mði:Ý7µ¼9Êô1=çÝLdw‹ ©<_ß× †¶î–Öíä£üîÂ}| ×r-?ÿíéÁ3¡saÓ|Z+<‡GéÞ¡OÈ—¡Œ®ö o§ó®‹ÿnaÉú[Í•é¶ìüùž±ž :ס†Wz‘E–£ý†ä6â•ÆCdH¬`¾iv ùdî'ôÇø‚(²ÎUØ:¹"sD-¸¡…zíÔöe¦‚§¹¢+í—|3?‚Îâxš% ïÕWÖX‰«—øÏ³¸O[yòèòìôóHýË`WÕ„–,„ï ãÇÌÙ\½á€â™9>æËÖÙ…ây­‰_ZâË¥Ìx¯Ö°+™Ë¦ñ®^ýuôíܹ³Ù}÷ÝÍqÇgvÞygóí·ßšk®¹Ú|óÍ7>É&êêÍE]dXvá²þ´ ¯¹e¹™´ b0dòSÈBÈ‚¾z"p8*^~ªòA:þM›B“­|”i-Àe—ŠÍzg×Dz„@mýtƒ&›JY7mpEXÈ‹A« ]Ø4Êa#jw+¤-QU%AÓæ_úªž³gç€[ï?ÿüÓôïßßœ~úéæÚk¯5_3.âÂ|`.½újsµóyçw²Ñ'þ¹¹ñƳ¿ä‹dÃH K+Ñ׬º:óéï…k]êKÑÆ%à ¤\=“ŸŸAÛ~e%9¿‚ý‘iD“ôÕ»oì²û|l‡ùÕÞå•Ó5ˆ6ïb‚Áå%‰ˆ%¥ñ"”FYa„äšX7nWpÛÞŠþŽEè_aëCùÁª¹ÊÏN>©$ünãsíüöEex(%À†!^s@Œ?}UHÂŒ…ÑX= BÙ«&ìÈœq v æ¬öU^6VXzQ ›¬[tÍs|ßOýÏ’æ +2v÷£þÂA‹¦ ÉŸÝ©€dÅA_›zù} ñz+JÚKk² Ò2o9A®™ÞóØÀ#™”Ëq´èoˆ†r¾¶T)±³d WŒªQ.ÈíÅã2 e{ßÄw§‹Ü>-ëê8«7”ÕãÖ´©¥•sJe15ùV¤G@_r/úÊyϲКÂ5‰ùõ’ï“T> ‚÷ hÅyÜncöaÜì’„ Ü¿¨ zŽBƒGWP—UQäG³XxŠw’³”õý nÎôK8èHàÚGÌAð³+³\º¯o%ÜŠgoðYÿ«áġ߲*+EÝ Û1Î&_;ß9"/ÕOae&?ŽnèH;?É;X0CÏÖ€çð,}ÿsÆÁÆ÷”Ë”¹™¾ìÊrìRÉH%zvBb_~dx¡s»Ø¥XäÒ¡œ ™x%âÛ ƒ{Ðî6dõ¥vþ„ƒÚâhd‡·ÊBð8ÚFù6$h'ÄQ uAŽ¢ ù|ÕîªÆâŒD›ßWP ¹y²ZWèÎ{ (RÏ¿g0ÜpßU÷źr!Ò][³ lwÝ*¢Óãñ‹º NdK³ºÑbI°ìÄ’/æm#æ=Õ"½ÛÃ:åÎâݵÏS‚–svƒw˜ÓþíÈç1/Üä·ÈDZ7] MQLÁ1ª)xˆ>™ êÄç¼§\0ìí¨ßäbì‰ ¨-ZZ·í _QÏ8Þñ}/w3{6ÈCøðá,¶‘2ßµ'Ÿk° œ€!Ø)ÿ?ðCô,@Z‡€ÉýC¹átÚù.Úp´á£yQamòZ1QošyïðKÜ=>èé4ê!×,½ÈOá`z~‹Â²JœÉïi-íÜФ‚¬å.¤kÁîey*ûø\€3®u^."?À’ îQAóçã¤wubÍëÙ-Òð»KçJý'1Næ[Úy,id ¶‘Ã7¢òŽº·5í÷ct'Úaz|Ë:6JŸJ¯6=ÐùRg1"K=Þ@óùÜȶ6?Xk~Õ‚·Ü¿h,œçŒñØöQporšÉ¥‹k÷€?,$=.ÕÙ¼5©ÎGˆ ’$´X+ líðéAÛ÷ öQéÌ{âÌ…Ž‹VV׆JîŠh'Ýzåù[}ïÖ{šÓÒƒüCYÃ…‹wC^ßÏ—/ÃO‹ý^kûƒÌµøËïÓ¿6õ6ð’³°ì–k¬ù´à0 šÔîrHŠÕI|óBŒR‡'ïoZ¢ê÷`h¶sçÆÅŠtžå0Y‹?+HÎ V‰{u_ó‘Æx¥!Bˬ4‹Âøo¼ñ†óÜsÏ5=zô0o¼±Ùo¿ýÌá‡îß—ßÓ@ØÉM`6—° Vþfs(ý­.‰ÚêN)pÕ2B¹£pM_ФRCZ,œý­ Жí}WÄ×'¹’póV¼N܉ë^áá|Uo[w[΂ð­-’j\EU[žIÕõ‰¶LUÔÎk8ùU›O5éÔÞpà ¾ ™Ã;Ìüú«+R¹®Ný dl¾þúëìç÷ß%"CSß}g.8 —ùøãƒ ø¾¿ýVšÍ –0¬±^ØÐ+®…—„B…Q”·)"ýGMöO"f¹‚\cTB<³ë»Æ(«’q T.<1§Í$¶ÞÆY³Û¸ät&A+¾£F·€=ÃêÂI*0#7$šV¤ÿn|}þ½˜¶È†doúMÊ$¶ÎÞ¶Z¬°þí=ÉL’Ë•éT××x¿Dvƒà9=±4$Ô—²wò­GÝK.<ÜrL6ÎÈǨA–å ¯H®nni~i×òÓÿ ¾ZÞI¾Œ+Û¢X_`¡H°AÉy9‚ª±`­õ°-uº°#9“€Ü­pâ´s&ɇÿ¤,Y¦Ù°&ï,KnYe64ä·p.·-¡þ~©öF;óÎ Œ¢Ÿ\ìò¯dA¹î|Ce²ÜzŠñµWFûh÷ «S(ç¥S·3¶žž{ #1 ÃïÜêß»…³Â€¹EÍiðöÞ>Àè>ß…Ztnû¦ƒóð<øÖÙ`ÔÞHm{I»VºAí©ÃÑnt@/÷¹{-ËQ-ðm G¹†þ”+“¸ e3ù—Uð;›K°+žÈø8—² ýkJ>ÍɨrÍp=cGîúJ(«aÆî€òŠ™; ¬s-VwÂ’R–y@hJX¿U]t¯X½Ghi.ù7u}ïpØ“>ÿ×½(OîÔÆå„ßq%ðô"þ<[7 äó&Œæ†r‚@–Û /kÉ-=ï úåò ú[VÙõÂÀÛÇ2žÏgÌËx"൷0¿»•¹ÒBOÜNY²øÐ@ð-ùCâ¹Ìm •ƒg&{S¹“2'±øñ åÉÍÉ ëûZ¦]µø¶—à,|m;‰>õ¿rAÀä²ã*ò‹“€v€šToµ‡j¦0•åòÁ®Z;V>J?mn4,† øïçqzÕÊû:S¦juÀQ0^y*-¶i,Ü —С‰¿p-ËÁ“½¥Í¬Ôé,.®¡¨~°;¶ìï¸ïÏi?åSy€[0†*]4¬¼œ¸íÀ‚Úø¾ÒŸÈ´›©}Ϧßdò½‚ÛSì£ßân.]—ˆûX³ý„اz0–v÷\•˜‹ze„¥«›gš_‚•ðÚf2é‹…‡óuîBS»v²uйë ÇWkÉjó±ßò›¯¹ïßX§? ?Êîh²ÊþžŒµâ9ÌýÊN¡ˆâíc öodñ‡ðW-Ð54•AÌ£Œo¹Y)'t¾•yLî着A2`züü3ο[·.ðã¹ÑF™‘#Gš-ZŹí¶Ûüú>Üÿ}áI'›:¬ Ÿ{i =z´ïxëåW2-V[Ål¿ýö&™Ljvå•WšîÝ»›éÓ§›¡C‡šc=Ö,¾øâFà™@eù^zé¥MÏž=ÍÊ+G¯Òt¦&ÃŒË ²2tU­ j²°9¨!åÛ°Ò ­Uîawr ׿kó›TpÇ>iºoÝZ+ A«T’¢ò¸®ßbÛîq´X*w½]¥~m£ò\ˆ›aa;*ž½'÷O>ù¤¹é¦›|Z>ñÄÍ.»ìâ»H9 Ø ªßgŸ}æ[Í÷éÓÇ}ä_ßpë­Æ[mesÿý÷<[PoL›6Íç ¶»Es蜤ÝÌ?À"O RÐ7©büÙôò°&Ç"•ô¢„ݨC5ryþïjÁjÁc3øda®lõ´ ±Á¹2Ú“0ç—,º‰ +¢È\‹À°{Ôü{‚&u]5AÖ{Û"œ5,t!ùd?‹6@]pà¤\Öw:|ç€.7ÜZw¹‰6–šüì™óors\<âF+ûZÀÚyXl0˜SÚò“["ëä{8ŒEJôCX[â¾`'°ûû‚ÿ`º~!›ð T°Cƒ%èìíÈ Yr~J|w‹Aĺ¼WíY¯Y"?hëóÓ(]Q¾Õ@oŽ%M6èÀµ?õ>ó|R ÕæçûØú«XÇ&~7ßÎÝ•íÑ…ô­ƒk–á#K37èУW¥r!®• ‡ÄýJ0ã²eºXã7ìf¡ Ï$éPEÕ[cæ,‘o& ëAuÔÌfÝÉqŸòµ¼cüIvwÈRl( ñ¡9ñ`Æá Ô}Gæ’ Énæ||XÿgÞãÑT[u®®FA« Cše”ÞÁ;þà­ožªEÿóS†IÀÂ<êDTö÷9¶1Ûà !ìÂD~€{XI}åN šð}ð.u|Ÿ–Z˜ Ê•$üþ%‚+û XÆzFFÌû~D矀ôÿÐÖ›2–|w™g[„Y¾•ôC¸;ª…¿‹^>ô˪Çl½ù ßÄùn*àu/Ó–SœÊ¿lNT ÈÁ;E§†Ö\H9˜€ÕŸ•" t$ ]K$6ÁÅÂ:f¶ö뀷v|.¼Ñ¢ä¢¤éÈ{haVÖ°w±ð8óœð€K‘SnÜ•›YdlÈb¸+%›gb-Ý ZëùK#Z|ËI¢“‰™ºw0ÿ©¹:Æ<¯…ÛLÐæoèÏ¡­!´»ä‰m'7ãoyWŽ4÷kN8àèßÌ×§z]ÌCÍzš9éqæÆô@ÿ:Ÿ'Øüšä;±:‹Ù—0†Î§þŸå±í¸ vÏÓæ8?dè@¼³îÔ‚ìscy´Â§¬ÐÚâ}ä ÄCÊJ^m¤ÔÈW«" ‡ïbsÑÁ™ç%Ƙ£fbóÆdã-Ε«OÛš7ôišWjã–¦úçæ±äí½[qbÉÚ­ |JÆÏAËòÓîäa9m5é[ùŽ[ ¢†ÿÛƒW?eŒöƒW”Ä;–cLMA ¤ÖrR9qt²_±°ÔRK™±cÇšÛo¿ÝlW^™s/kRfá…6§œrŠŸT ŽÀÚ/¾øÂ´lÙÒtèЇ“ÿ0Cp—_~yàõåÈ·Ûn» 9nÜ8#àl6‡ÆµiǤ‚ÏÐÇÜ7ß|sÓ©S'3jÔ(sñÅûà²@èp˜+J„c¿3]•}ÕmYAÙX¥/´ ê¾q¡ªÎÊd¶“Wqë™ÂR ¶?FÉ»#¢Ò궈1Ü^y«øáû‚"݈€ÈÔM 6¬´†¥Ö)ùËIù–få¾ÚĉÁ¤½É&›øItØÙúë¯ï/x„ó*šý¿øâ‹¦¾¾Þl¶Ùf¦´¯0yòd³å–[†“•õ[B’üLOÈ(Se%r"IÈ”EÞÄ;Q .$y'ÚÖªU«‚çº!k_²Ó‰²Ð—&+l®K¬µj¾T¢jBêä@àLßSMêÿ¥ù;´@Í¥ôñxÆuµ=² ¨ ‘£Ø¤;¦Ì>tE ~½&5èƒ /Å´‡¶î˵Po,Œ/eëâyÞ²¤!m&Ô M'NvÌ{ ¡šO± ­ã(ÛÖpø¾rÀF±ÃŽn¢<Íš7`Jé×çD”«@D™˜Ù§ù°,T>O²À&°Ý{"\lÞïK§ ,SHOü©Ô7Ì'> žÇþÿ5ò‰¸œk!Ž”¯¾ŠÖz;Þ× PA ÷ÄLk¯ ÿK&D"DˆÕEÖæù%_Ö@KÃ3e eÃ!<)&§ðØ'(gô± iûaŸ˜@ËøÁúô<&Qo‚×Ë’\ÒPêº>‡äè4ⵂ¦«.-RGš™€'#ê÷cʧÜ/Ä»+¶˜_¤ÔÛ Ë°°Nßïß¾Sÿ#Þ[·å[úäp+0CênyÁ¶Ž¼ëßG‡ޏÊõÃÍ,6Ä…ƒs$£äÚ:˜»5ŠÖ{Í4'¿ZžËß^pŸ^b%&=ä¹õÉô­h9ŧÒú“$ºQFWèàÆI׺³ümºö¡­ƒ¨ÉW‰•°ÚËgû\ߪÙǾ¬5ËóµßøŽT¾ÏÞ’»% ¶0¤G=êØÆk¡l‚Ô™J¾˜(C~YuÔK™Å&ÉÛ²LœM[þšžÄZÄf%•ÕÁHV¬•`0j²9û¶&±#û/÷ î“Éù%:]^´Е6L°ô!sq>²ÔÔƒ¬øóÇrT•ßÓ"‹,ÄmÐnÁ€ïÙ;ñß²<º h82õcŒEW§YÀE;P…¾5ÞüUÞ ¬¿“pžÀ‚ú$¥‘€¨Z¸“iå1œv—1‹dEúà×c(©Ò¢¥®ät9sˆÀ²¸°nÃJi"’`œËVÄÙÀ[‡J¿ŸÑ ¯òF½æ0¯é09S3ŸVfrb ú~gú^d¢?Æ:reù˜Ò)hQѯ4×£x×±æüÐ<Ïð‡ßäá‘T³=íû‡„mž¬e¼³#ƒkÍgá0Žz? Í ^¿¸LeÞæ%þª WÕ„àP´-Ù<äêàXæØ»á¢c7h«ÿ!€]Ïåâawú¤-º( ï €Ò ­å  žér¾´Ÿ¾Ý;¬kÍ [â4rPzœÍƒ§L!vá»»sz—1¢½Ïbßɼûq£™«dQ½&ý3€…²Y¼» ~ MdÙ¬ñ¤]XÕáh=ˆ+c0-˜>ËøÒ!;ÁÏd8wyÞÀ2âpî‹§ ©,.’÷YŒ«^ÞÂ,dŠj'[ù)N˜Ui£Á ~kñ ɔՎÂ*ß²þI: Ð9$ç†sÓâÁßxCy$Ú1>­è€ä[£òï¹ëÒAœ:ã+ˆí>mÀub/ŒbþÍ@%°§`lŸÃ7o½õ–éÖ­›9ðÀa¤8‚ôÝpÃ Í 'œàç?kÖ,sä‘Gš P¢ öôÙ€¾÷’©Ý`#_©C‡õÐêÝ»·﫯¾2W]u•˜YpÍPå?M.KÀL'7˜1TWl­#|–››&Џ  Dâªü'»!JXvŸ‡¯5ɨÃA§ÕJX çŽWì÷<¸Îh˜’œø«–Z=ÑÖ)‚Zåm °¹X}Ügšì]¿[ëï¾ûÎ_ì» råÿ:´È¡pÅWøìZ,Yd‘EÌÝwßíÿV^‹-¶X8YY¿ÅÌ¿Îíq $|ŠHAlõwxñ".{_§4‹ñ|ÿý÷‘À¯€nëòBõ“Ð|Ê 6;o†rw?г¦*);Iý«6ÔŸ[mÊÿ¥+Ùëc˜ÜýÃò~/YÛFà}بÙf¦Ñ©Ù!,Ê×¥¶¤KÝ|º  Ÿ=kms$jD± õù“2L§±éðEo ®.ž5GÎéÁÿo9,äwßÇ^”…p±|}Ÿ“(åÆû¨ šÀmóü'¼¾ж‡ƒNªßÅì=ÜÈý‹ Þõ°¢jgøˆg±Âpbž1³g$Zm•bÀ±š9‚gCš~ö~ ¾`²JÂòDÖlJZ‚Ô¬¾X Ii\H@Û ¥´~–í5RÊ ÚÂöʧú±ÐÀ*@ʆ½à;òÙ*£´·çÁÑlq“ï'±3}ü"ÊÙQ¼÷«6yö[€ŽUæ³7¹­èmfL¾ÏPÏL?äGÑ|½ýsв‚ø÷íXÂÝÊêO¶ÕÖæ¡ö¥¹¶ßþ‹¥°U£ò’ í°oÃ@¼swì“ò¾ïåð'Y¦½-…­pjƒh×§‰+Pdj(‚¶ ¯r•9È›–}º4 ‰5QŠúÐæïp+“×¢ÄËâ+¼@%Ÿ¿»Ò=Ãû‰†ü蘵úìIüÌIqruðó¥lâ@kݱˆÏ¦!æžÉ-Ì»-1oÌÚ›¾âgõOgWÚYpE>­8±aÉu3‚ó*€¡èá.€?Y[‹X'vÌå"Üê/`khÕ5:Iñ248žÅŽ éËͶ´¹¼5v$ÕŽ(¬3ÙÁ1ÉÖwŃ”xÚ0"âcíLœé‡QÐd4v¢ËwmʇƒŒ9 Ðgkhü2hüE¬ùdaµEßI^Ö¥xÇ:”24Ä£lâ+àwaa)ëcY5 {Ã3§ÂÛÎaÜ…ÇðeôS+ZsWò¹žÅqšþÐX`5,—&s8H‹v§½Õ·òµ+¿ê²À:€K‡ ö<,—«ûÜÔàÒ¦Æô‘AU®ôŠ_©g\ÐŒþ%å~Ú}Ž/¿¾#²’ìlë³Ý®y@s¨vM4÷q<‡~s­Žý‡™âkõ¼Ï*Øÿn͂űÐõ¾Ð¦hÔ-rôåþ5ôÇ®õ¿øàyÍ(¾æ›×3@¯À>Í êçJ #mû•»¿í©¿)e‘ÚX¡cL¼Ç T?ÿ_øhAPó³€Î;‡?å·|ÿŸ¬äO ÄÖ2»±êdóÙ›¶z‚ñ¥…Þ›ÿa~kã|§o(¸•½Qÿ0Ü’Ÿ?ûÀíðó€öZÄùž÷“ÌäÆœÆoFgÚ|´ nrmö,ýú×—ð­Hšwµ ä âŒ'?…ÐþEÐÛ€o Sè?Y•/íÿJ˜uéÓ£YhW˜Ìøy®£Jw%/ùŸ¯Sƒ¹×U'Ò£¨‡ruˇÿò³|>XÏ{[?à{œà±/ù-únê°´õkÄoäªè½ÞÐ(5Lßæg³=vm<˜ölX Õë/hXÕ¤Æà¢œàóÀ ¯±ñ§3^GÃ;å"(úÊý‰Æzôrg8Eñß:«@~¾_gQ7˜È9¨xÊù÷T ¢«À‡ï„ŸÌh@±j3íœÑX–µ}~`®¨»–[?å?*ö+±Ãu%ó ;(?)²`—…å./‹›½ÿÙæe ê&ߥ²º3fŒùœƒ§d±¨¶·ŸwÞyfµUV¹¥S±áÞ{ïõ]:È„\6|ùe†aå¨`›O.ÖÆu„¬d-)«a¹‡xä‘G|ËÉW\1ëJÂæ÷-èM ¢Ü9¸¡…óCêæäÐóu!Ž‘0¦ù¤²Í̾}e%j’eD\Þç„–¸x¥îçÔÊü˜Á6Ö†å/ÐWÁuŒúκjè¦m6á~Tº¦ zïYEÚ7ªl-hÔÔä?Y®k#ï¹çžfï½÷6+­´’‘›-”hŒÉª~ÆŒ¾Õ{8]¹¿ÅÐU“(Æ®ö]ާ®{p¾…5ÇÈÿ¥EHRV¿‹.ºh^}7D0YXËÒDõS&´94ÖôAŽÚUöÿB9-]èv€ƒˆ<¾œM§Uª «Ñ˜Y§NAèÂà=2›«ÅyáîËŠ¿M Hì[_=±w¢¿µMp ÊþzŒÛ¹µ›#à@»Ø …‹E”“7Ì’Õ̇EüÛš¬ ’Õæíö<²Rxàl5¬Ñ¤d)œP bð¿þììe±‹IpÆ×)÷ÿØ;0ÉŠª W÷l ‰°,K–¨ð“D¤"9HFr• ‘,’T@É$H(Ir iÉA¢JÜéÿ}owuß¾};ÍÌ.Á­}fû†J·êÔ©s¾:u*áJ}'7DU˜uÛdCèÙðnÈNaò¾ÇÂd½‡'õ“ ïC9{a{b@@á™üu úÆrâ·Æûv¿iŽu ÍyüÅ è©/·G9åþÊ6Ðò;JùhY.Ÿ‹Q„Ê~õý½V Þhu05Ïl‰+pµq6€éµ(oOCeøŽ„oC+åßaa$mà!1?é=%yÖî?ý.o‚ÅRè+±ÖKg"?E¹†iøÓºêlž•­U“ÇuÿyÈ×SÔ1ï„k>Ðú3uLC]©XóÊ›3Ý]³$pø‰¨åµÖTàžÒ¥€¾çáKñþ6?D5­ÉÉñÅ<ÐÞ¾|ŸVƒ5œÀ1%û«¾ÆZžÜï\9i.ö1f¶è»'¼ÿá–<º¯ú,{±åQ(žP¸0ÏR×Dn×þôl~ŸI²(Ù €ñ¹¼q’ÄÈù/³ÝĘÒÀúŒû]@æák¶¡´ÂÛ’:­ - J…âŽèVk2ÿlÈM“E• c®æ}­-Lš CSZ•ê¦`{xÑ­\ r¥CänÿvAJPoUÆÓí´ýÔDŒ5gèJ¡ßøÔÛ~Æœ Ù Õ?­6ۭ瘅}'þ‘Ï´ú=à¸~³AYÐö»‡¼ö”ÒÄ”ÔY— WÁ±1ã‚¿À ë­ÎÚP†Ë;q=Àg·Ã_M?jY,Q^ªÕH+©<Ù´#„3`Fú]ókÆ›ÖíÉa¹e~ñà—ÖTÍìÛïüÎm5ÿ-\Z…{‰ihoø|*üƒ6Ýz˜š;ª×¥ïÞ`¼hÑy#t‘׿& ¬pTn­/¿ÑäY}¦-„§p¡ÓÁ<ÌbȽ<ïĪQr-8ÎCô¿V¾wÓ?±äZ¥NEŸï‡e£ÐÆ=ºd›<1N*%gQÌÁbžü)º ”ëoM;æåΗ¼ï6ÌB™†”“r¶­¨ÿsÔ:Zý§ß·¼.É{ËA?â ÷GhÐP÷Ç]9vù-·wæ;×,N†öÂ9Z€¾NwJ[Æâ(3%ï”Áž§]^£­·Ķ÷~ÁÜ£[›#(û™¾OñÁ>$Á)Ôçb1ÕE›xX.!Öc^ÔÝ H}'}æ!súŸÏZr&‘øÏšçA7Ka]«¥y–nb<õ¿8ßusQ³EŒtü–×…ÙáËÁŒ Ó2GD@*f4m2ºJué7ƒí®‰‹2 éƒ_Êç3GÏ8ˆqü3æÏÉéÛËSm®¥¯#+Žý~Áw ŸŸA»;ô>Äî®{š]Çéý­àÞåW­Â·¦=àÔCp›Í­ÒÇwîz8˜EÓ»ï¥}qŒv5‰9¥~‹,TYyæ&ŽN½èür¢Ö#OmÒvúÆ,±´ÜõÏ Uâu×]nºé¦pÞyç…C9¤Úé)ê¾ûî \pAõð'ݼ0Ë,³Tk1-„Ï8ãŒpÍ5×$¦Ó§ðÆoœÃyyÄgŠ=,ì x¯5A3@Ì´ã ôµ£•kÐYPÀÔ^mªé,]:–VÁ Õö•«‰ŸuÐ5ˆB˜Â¢A¡;Ï%@òrPþ›Œ\²T«ÄÈ‘#гö„~ÜŸè¦$fžyæð³Ÿý¬úxÔ¨Qa©¥– ·ÜrKÐG®–Âÿþ·võ¡¬^×?Ë»óà4•@·Rfƒc#OÍÆëï½V¿iàWkß4ð«Ba­´ðN•ë<ï›)ôéøƒuý ÊÜŽ r'ù„ÐI À)>Y¬“ˆâô§zO%•³V-l€ÀßMPè>¨…⼊ƒÖDË¡8^âø©žß>]·ZÄ­¥)ØÎÿv²ÍÚm¡ú­k†œ³¾%ƒ¿œpÊÑ‹(AÉöÚÊ{ýï–QfÓIªÊ<€)/xý]ÄyÁÃÙ´èûJn½:TÏÂl$y`a¯p V‰Óaå»a¥oâá þÄ»hYÉ}ˆ’—•ÿ<0í-À·žÂ¡Q;ÓæŒæpVjÓöÄU¦¹TëK€Ùt»˜µ>I³>(Ĭ:n«åµa€†eàW£üµQ6Ó ïŸ|OÁgç X?L*ßyØ^:xÀÏ4}=€ÃÃH¥KéëNÂüÐÖ¦”}d]?ñ5Åmp9rÊóóaMêôjE0OÛj¶Â´a2·ts ŽàÒ¯P¨ÏA¡ÑGµJ÷ªHz>ÂÚ»Qª@s¡òâ Üí mþ™ú¿ÂVÅÃpMrvéùp'q¢ ëvé°µÿJŨß÷Ñœöáî½Äïlè¸q¦åõPÇŃÔóXn^â¯ê,"µ¦² tõÛpvãKPžJr5¸e·—¿œéj ÚRCŽE°úÏÞׯ0åÛäúêÞ™ö:ƒ6¤;—ßëPˆÎ¡ ÏAOWÉlÛ¾6CéØÝ^ß-Xœ;L9öXú¹$ÖzäöØ®e¨…Ç}w«T°âW6+)Õ~uQ”ù”x1Dá­O-ç@öØÓ÷w:h‘®¬cIC³ðgëùùIs<®Bö!t÷}ÆòwQ0‰’Œ“fÖ¥1ï n9¿ètòË}+húê8 Îõð ÇÌZ”e]´Ð½ž…:9üƒ1÷èÐÃZ®JÝÄÝœ:œˆkˆ—ái1‚@òµ8\w›ùcí@·£‘£ŽåyçÐ>ZC¦ÝœOûm„`|Šoh'u pÊMò¹uÙùˆ–â‰ßÊâ&Œ¹ O£•ñ¦‹™ó*<™‡uÁ:zÀ¥‹?¥5hy²g×ð·!K‘Çf¼}¹ßz^M^òŸøÂcù6Ã+,tHû#øf3«\Ó 0—·ñ'É’ÿ¤;yÊ-U*/¿S_5hÝ)ð÷qùqÝÿº£ÓmΕX®ÞX"k.sê¢&7ó@7g0Ç|DY³1·¥9GŠF[rõ8j‹>ŲWWæû úÿøiÚϳ>é=ðóÏäz} yà èÆí}úû¢*JàÓÆàN–3{?l°™ÓÿîO篞n¦]: Ê:Ê9G3.lG-õ\Dz­Erü÷þnAú͘§ Ï&­”\«Ç&sCv'– ¹‡óD=Oá{´²_”1{,cC>bnž¤-´N€+Í®*n†û+è„[’ý^æŸc‹›†u˜½fa™ùf¾á­ÊKñ„ hßÇÈOPÿo,”Í`¯~ß*h¹&õº–~0ð«Ë¨”Îf§ÖÌaeŸò­j0nß5ãã¶Ôq“ûtd»>}•>× UI.*»(ýhÂKjt$EÔîZåP{7'y-ÇÜv-sF–Öoa<¾Õ׋KžúyÓÔ.6ùt\ôÃr|ÛÅð³ÝqÑÓn±õxÓùŒÃÄ%ëgp~ô½•¼:“pë Òÿ¾;ÉÝùV ½ 3ÜÂíèê£ô…¼(ÎÃéçñÚ9d7xs4˜‹Ïü«U¯Öˆ°1ÌC£k¼Å[$ ÕóϳvÏJs Q€t{÷I'”X/z`Û1ÇN<ñĪ/Ó?þ¦Õ]ÝH}ôщ‹‰ 7Ü0¬ 2ëû7^údßø<ý«èA`WÓRMžŽÖp=è ™*a Ý¤ÿÖÆa–* ƒK·Ð™‡„åa-AÅþý9—×sósð`œúJ Ó@ÔÈÑuA‘È~7ô¿&åô¡o0‚"ãAK]Ïž~úéäy¼¿wÜqGØqǃVÂ1¼öÚkaN¬æ 3Ì0CÕJ|ïï,´_zŒ¤ß¥¯õýœú¦ãŒ«kýë¶EáGËÿèâ!–÷*uôu{°!êëÙxÿÇýX öÄrZýzR¼+ø¡€¾*b“·*l» -ÐU h¼ØUŠn#r ™ïarnE‰R(¹Ž-ņ‡‚:CÛe+Â6%ô=вRã{ÙØ.èiÓ,ÈÏ// Ò÷Fvíùûê”|%ßç-þ欀¾Öé0[ØD%Ö’YkBÔ¤_ãÛoÚ©Yø=Šæ(T{¡zN^˜™ÙPË;Ê1(l‹w }v:ÜvyÉž)Óü`Öî²AP*‚gÉ»âîáÿz¶L|Ãz/eßœ¸«:ó¤Duº1W¥™"'8êÁj[Ñ5°™¾“À«ü¦)ÿTÿ¼ø?ÚÀùZŽÅâÓÂl”ZBv+ÕÕ_á}ͰP¸cèn$™†;­«ÊÀ…y¸D² @ÈÑ9 ¯ïµxÑjÓàvçý±V¹¨8Kè¾GZ\¥uªð1‡¿iƒí r~#ï–¤ ‰KK-‡ºöý{qïN?irM¢@ù–Ó«”i-“²®†¹Èç*èΈt»[ŠÀ÷XZNYL9P߯‹3ªàªŒç°`a*„Ñß…Y‹«WKŒȨÐV×?êæŽSîÃУêRXŽG^§Gª ùõ¸ÐÐW²~Ç´Œá%ÚðÊÑ•úy—®(v…žÖb̧ƒí<ýƒ|å‡ðß›Q¤=ô²j¤ònÂ'èsЃS-×û½;Ì›Xkê«´› ¥ïšÐ7¦O+›‚K0WÌÉßE2ÏbH†”ᜠþ€å¡í’+ŽtãAZúœlüö_Óæ#¯5 ÓGǸËÎyÆœÆÁ‚¯&´­åìÚÉU~j³ËéCyÁ|ﻤùTÞÔ@ߘz?hàê¬Uåm|×Xr.Ýü ÐØ]$1Ø× §ÚEw5×BGv©rM“ùæ@û›9s~8‘Ök±%cî˜ _´¶cÞ,£Žý8 „‡‡tõO´<Ï òhA©ÆCÙÒÁv|q_xx亸9Œyh3æ?©jk¶Í  ¦ƒûÛѧÑ~­‚cjuúb©Lúfi" ™~?š²v¦Ïl0–fÎÜ•d3T“NÆ÷º›p!¾âfúÀ¾hœ%«Ñ{êy%méÜÿ"ý£õµ /„ï—ZXÑìÔkÊp"~]…ÜN{ÏÊ»…ÒÇhŸvÏ@IDATîb €É_t4Iò<Üz=æûs†ïÀ}M›¨q £M˼¸ ߥïUA_ߨWû³pµtjÎòÙ"ÐD«põ[›zÞ[¡¡VqÛ¿“Ÿe õ†#h§Ä‡xûDbtØZþ/” 5%È߀¹å¨]zHX1s8cÀÅÎl¸…þ>:ÊʆRñ±ðåZðîl^ÝÜ?oü) ïÝ-Gs9ÇwøÉ.ºuSVŒ» ãõÿ×oj1_ǸÙ_϶gá*ŽùÚûW˜·î«Ý¦®Üs!s†c¹Yw‹qÖÏÂÍb·y®U^ Ñbñž{°ûÁzÏ·âk9Mo)rµTÌ}V[mµ _^z绢1ÄRõçë¡qº–Ð:r•UV p@’ ¬9ݼ2(L«±.sµaš):ÒêP6½òtPËOQ)¶Míië«É*¯ã!jÙØe5£L4‚qíCyÒO»@¸K¼ú'é·ø:b°6RI}œx'1ç—Ãxîþæõ‘u¨ObŽ9¿žÔÙep*µìt°®ZÂ;V=ôÐàBÈ©§žš¸BYuÕU“¨º8ч¯àðòóÌî¾ûîp '$cáŠ+®wÝuWX~ù哸k®¹f¸òÊ+“¿t9ZêZV·aT×ÔÓm õñ£ W£½ÿVMŒ«©Ò(Ýð‹i8Uùu¶77&sÒ%‚³>kÆüT”» ÿÞ?L-Tu›>_¿\Z#LŸm ¨\ì”|?ÛÚ|ö¥¯Ìüõí&ã^ß±ðçé±çf©tÞ…0s² V*ü …îö °k¹ô TZ± ¸K˜kI}ê¦Ó¸=s)úå‚ÚoQÞD©Î £ò¶}Æ,„ÒÖɬ)€s\5ª®@ƉC‹_MMÛäÁ)*6­„IÝ ¸uÝSD¡Î† Èõ=âØÆ¿Cp. èV‘î°$íµç˜#°dî,•·­¶‹ÿaسðð3}åŠmFu±TݪÚspØmÈáÌ­ež¦5EÙ C |‰ÜÎv‘vHZ ?¬u2ÓÌyûÑó'Z†v¼K ˜¯oãûWE9©·Äx 3‘µ8qýÏM­[þ¯ïBâì"üSä¿é“ÃôvçÚŒ“[–î={ðP½´^ÙK½'¬û>^+lÜ{1×e[Ž2K¬Z¾ÝP÷&Àÿú½‡àë­ôÄü~K— ÔßÙðX«Õ‚›ròØ‘[OœÛ-¡Ý€­U÷ZÖJÔi×sòÍ«p(‘Û“Ó[öu¡äB§Và›ñÝÏpïn›wù¾Ï2ìÙ÷bØæÃ½Âƒ}í¨òz—Nƒ.-öÐééûm}]C2|”ëÎ,ÿi Ryþ5|R«ËÑŒ¯›Ë›ÐfÒ­4«•ô==Šü§D¦ù% ‡ÒôPzácë’óߊÓTi_ÊÙƒ|~A?dam^Õßë‡×í¡sàÑkàbs7u&-¬ïÙо07Qç²/y½Í_\á²iÈ} ÅÅçizÔÒè`¾ÓoÌ ž¼.MjYÃ|ølÔ´ gNhåÇ×o þóTÅb×B°*Ñ1èÃý汤K÷Ò®W°ã«IœE©™®Z®Û6_º=Ì ÉKu?|úfæ  s·¬‡xÊs=¤.m ½3ír(@‰ãÜð<µ»Œ¸«‡cµç¼T¶‚;`üíâ–a¹¡‡†+w@–´­Þ䯦ã]+å³*s¶ QêãeNX:‡EÍÆÅ”r}Ò³>¢–¯%Oƒ¾h_)¬?ÿa]ÎZ¶wR×± ×_õ@‚õn6ç®EQœ?|s؆ŒÃ™’b”E¾G›^Å7\ ­KùË0¶ÏdŒÏ’¢¡X'Ûþw-úó¾€4‹'‹eZÄ=’1¨A‹€ðÄù²ÓÛôÛÝüíVqc^¿!wVìÇßôýŒ×ä 9"8îbüŸå-↾×QÆwb.¸2™M/ÿ‚UyM¿áý§?ôž‘¸a©=wR)lCß^L91,ï œ{ñ]üµg³îvâ»þþºpà˜JÓRóš®Ööñ¦ÐL7A,Ä];eh³›<Œ{ cg ÓFíòø€ž5ôz'rR»ÌrÞ#%&tV¿+/'â >ºñu»êäÏÝy¤ 6îîé4üÞ*V¨D³ OÜÞšž—›Åmû¼,º”£M9å”á;ßùN¸óÎ;ÃG‘lI×úV¿¤×_}xîýwÃzë­—Dîé)Ã~O>ùdxôÑG“à|qï½÷†¯}íkIšK/½4‰ë6xà)¦˜"¹WxX 3ÍTfܺˆ(Öÿ¯À³ÁºŒÝÎ8>Ê´ 7ÿ”AÞZgk™¢eF­·®S•Û…þ QÜßá¶ËÖ9JV ,VûÈøi:ñ^›„B "´ò¶ì#ص à=uvÛU«`|TnòúH­•¬U-ß©D¹•-ôuOrÐA…}öÙ'ñÝ«»†ÝvÛ­:´œøudÖéf|pâ"e…VH²Ú`ƒ ÂÊ+¯œ\û뢉qV_}õtQtRL,vl¯Né!Oˆ­Ë´Ée|äðM’$ÇŒÞüðƒ0ìãOÂ|P…Û£´ä¶ãžìj?5[}×—Ú$ ²˜ì"@m<IJå›!ȵÚÖã¦Ý~=Xa0ó¬:Å|FqÑŸI*¦ÿ"ýê»m)À}ƶæ _¤¯ê]7GðŒÑu_ZW{“–šKO–V`÷¤õvA€h¬¿~Â/hÃ$\¸£#‚اàÿoR¶€~„?¬O“±¾E­„pVfÔ»…øÓœñmü± û~LËêg,-ç·°>ÐrXkmÃË7Âб»„Ãiƒ‹ñ\µh)n £žàgçjúC+ÂvÔ~øI<˜ë¿ÄʾUMX¹p ë<(ˆ~ƒÀKtùã =•C‘;ð]ß'þ˜¾2€Š›‡9†nN»IÇ`ø$V«oÅlê~]Ëã‡u‘šÝôÝ¢} ƥׇOJ×UتEþZßmaƒâP¼w÷ PÆ™tvÒ\ÂJÞ†ÌO†³Qœ`=Œs€ã»D/ëûúI¼gˆ¿"¿šdþïŽHH»m¨• žÀMÃ7ŸÙæayxÈüøY} ug€ö"¯JðüÅÃ9C– Wõ^ØéÒúËIñ.H\Dz·Cˆ~ ·ǰ&e¬²¿'Š×<<€ë™X´\ž9ÌvˆÖ.îþP–¦Z-a”SvÐKi\—ú…´†—,xb·Û; Z^Ç­Âéty×WÐS_ Û„7ùÞO€Ç›…•ÈW_ž‡ìµ ‚ú©ü3utì<ÌMÃ!šX¶$۸飣´¶„ ì5“´^Ú¾¢Õëq\o pŸÝê© ÅKRüÌFÛÑo—öÝláΫ»RÁõpš§Ië0ûRÆ*‚{¤@•¼´ú”¼ Ÿ’òÑ™‡6tÙ^<‚þü¼a;ò‰ ±é;¿nñ= z¹Ž6¿˜ïq½N}¤“苞ÞóÂ\u ÷?£O¦¥í²ÛqõM8 éÃèL?ЏyÀ<|Úø ø¶e:+\EùRuº×/æ»&#´u0Qê‚Õ€º¡Ñð¡<ÚÎ\ÛºX ZŽåÁÕftýí½[ô¾]u9PÚQÚtßµã:ŸƒÆí÷+ñs.Ð9+Ö¼é,ŽQuJGÓ¶ßlÄÿ-”¶p¼š:|•y'–]žcþ.íýꊜ&$ V?â[f‡ù`gŞƞi⢜A+^çñ ¹×oz:j>@o”ÛS€w$”Û*H›ºxŠÁ…aå¦ô‚Q|篻Ð÷`@d­šk ¸Öàe?ÕÓ2^:õ܉U¼[I5¸ ÊfÁýø=í~_yå•PD¼}¿B›Z>Äà8M÷•þˆ´È™žtÛðC•‘¼BLóûϺ}s²ùR>ÒB«“•Íê-êÆãç CF¨¥7ÇuÐ$vý+kf,/Ä¡Ò*$¯šýËë³Oõ#æ÷Vb— ÎZ(&“2ê»Ql™ù°ØŠŠPüRÁ‘VdŠu ½?â'×ð6®_vD‰ù]ïÃÌÑú*£$¥­¥b~þº¥êmœ¶ o’h4`æ}É•Û9Ÿ×è*H~é6Ì;±Rž§t?qÊ*Ø4!§\|ñ -­åé@ô²€×&¼ÜØh1§¬Âãý"ØÌMR RFçÓnÛ‘Ïæð¼Ÿ7QH''­[á=ìí5þnÀÊ/ºãUÛÛ𧥉ð¹}Dؽï@ä³»?ÊëúpË·}ä·ZÔ_(©/ð?Á?¶cû½öƒcK“„†žþøé ,¬\ÑPÏõøæÅY¬»›t±ŽÛŽo¾&òÁè¤%kÉluÃÕ½¯$¼à¬$/ø/meŸÉk—¥ÞwÀ\$QŸy—]]¶i6¨‹n€Ì·1s²…ödÀÅÝ*üX+}Ýüž ¯UƒÖÉ«ayv4kX‰öØ”oø‹4ñpÈ?¥@W]íèG]ª+r}:c/ º²V¤=Îå]Ôa'õX5¢Ÿ—‹ìƒl=þ-Jõ±Äê6h]ÔªSÙ8_ÍòbÄÞ[)¥Ã¸Hã\-˜åöµVÁ4bƒ‡ÄiA<þ,+Ÿ•·™0sm-mÜʾ5Jζ(è*jeåð— ×ñ ¯ú&ù/_qŽo§˜z€á:@‡ài»a-´(àÔ®(1œÐ|õwÑrøœ—Ejû~áuÚäm@Îò÷6œf«ðᇺ®i¬¿œÖ*DÐ×8ËÂŽ@Ñ}°ï’D‘¶ÝëСçÐXסQž™›Õ÷zÖÁGäÄáGŸþ­ ÜäFä¡&w2c¾œú¦•)Wðia”\•ߘÄ÷ã@·?¾‹ÂJÍÃ2¤¸‚Ňe =£OŽÇÎöªi?9Œ„iUbê0ËðCÂóEèqìµ|×iXš § ùy>ñþð…“yþxDkYeÉn{¼‘í»U…‹íy7¤ïÍ+UysaÀ¤0mwn˸i߯.àåZÁ=ˆþä[’ÃæÒÙ=C½×çAk™Ê­Ú÷A“ÇUF$tveaÂ÷Î]éF†®C= ¥e4Žw-Bo”Ñæ£ÔUyübäÏ㘖¿ÆZôÆò‘|•>j4*XZÓ2x}Ú»¿KÿÞº±ï1þ^©”ܬĿÏýöŸAÿ‚UW¶øjákxZ–Öí}“¶©Ã¸OcoåÛ,¯1œAW²@‘î§_-êNë+awx”åÅa<ûcÆàp®Óü 1÷ú'úо”:­@+ح̺²Þ·X ;?cA«_Ý5hç-á×n‰Ÿ «Íáˆ4è÷HWçÊtãÿ×UúÓR÷`1D`àÈÞõy Ö}~¾ata,»J𑉒mÿY™h6(Ýótå!¸< ›Z]~µ€Ì›!Y7ÝyJÃÅké>#cA*ΫFtQZÿçÑ †€çª˜¶Òž…Y|£ž%hwÃØ¾‹à‰å–ýAqº°ri]¾ñæ$'[/€AÌÒY–«¸sDª\ˆ>±×Ü +è;;4­|žß‡NÎ虪:f‡BÓÊê»Ù…oÝÒt¦ âÈàj½BsrÃ¥áqðgñçÔi‰âÅðà-w)ÃÏXsþj6€OÌ\·=ü0o7Í"X™oüäkyó^³|}®|µ+sH”ôu£8' ²®Kj¼¢Uýç<¼ mp4­{ŸPø>‚ÆILÈ¿l+ÿœÆ¼ïÁ»ð§þ×ðË‘²-ð;ÏÔ2Ñ¿va«tð^w é0ÇT#“ínÍû ÉX@a­|; 2Ç´ÿÙNÒ¤AµNâvœ<¿ÈíÊP@ˆàZ»¸¾‚A^ÜYaLƒ ÚãnåÛ=Ú¶µNÕ_‰Û}JLf¹“MÁ¥4:¯šÕgˆ1 óÊN²Õ-.,sR@ÙüÂ[$Ìy¥¿j0k„0-·¬zÔÇÖÛCÎjIýûØŽé´#H#xšêSõÿέ§­BzÕ¹U¼ô;… Ç÷ÌôÿÔÝ­å~áGE3ÛÁ1|aÇ¢^L9á7¯¢žm9úóRÕžéN%M{µ7]\õîßEäÙ‰ûlñÇAÔÒ_ÆA¦ŸŸ,Ýò»1Âãb}ú—LI˜8_\ŠBûÞë¯Mξká=NÆý¨‹y…úëöèh€È}±˜q+”aQž›oÒW®!%^ß?ù9òL·Ë{*¹“×êKùzê—Ý6Ü»öÄí¾wØÌ›Ìác%"¡’^Æ V°ÍÂcEˆ ‡ÍâùÜ^̺̱ܿA¶Ò¶´• Ç¶,í¿“ˆÏ˜¬e¸‰\Wô5¬Ž²ë–Ý_«†•^̤eiúã H– ofߤ}ÏÀ_®ðnxà£} .dÍSÊ{ƒ‡ Æ-Ðu²kqîðwæ4´™LÞ9·…o˜ü@0¦:âÊñNpz‚gÙÃéš*{c£ÊÀmcIq47¾É>Ù0êûÌÈ[ÒÖ‚:{a)'`10þzŸûôêwkÓlü®}±¢Ó…Aåð¶_ú àúèJ›J'Gñn3úà úgúD ¸lPî0Ö¿ëÙJP¢ã=ö[6~¼M^ÛSÞC€hwaY;¤²; ¾ïü?~½‡5^X&|CˆÒ·àg^PÙ]Àq§¯`·‰êN¦‡è/VÒhe¨oð$ôš—Mòìñ¤ê¥fÛY:%y POuKyÜEð¦ã½gßdU×M H½¸t«1îîJFuyŸ <{$V˜—­”S绎gGÈ›ä{SM-Œ¥ŠH.«Þ‡¯Ç<öp“6”úwðPR—ºn¢=WcîÙå€pÜ¢ÁÂj¼‹»-í™mòžiùstÅEFóŒVÀwA‹‡÷*å~Ìuu†þõ¼5W–YœàørøØE¸œ˜¾2šS+QþºÅ‰8î}¬Œ?JZuÑë"Æ\6”Æ·Aëmv/ªŒ—Züòh3Ž~ª'*nþsì]4¬Ðk}H€Êm&Æ¢w_Ò¾…ýÅl4,«å/$•òÒ'ÿàâåæYôãÍšÅUÂþö[~ò{€sÛ´»}cÊ[2ß™}3 <÷°¾_ræÈ˜ð:¼åuú/ …9œÃö O} u–þY~Æÿ.XmŸq†lÞFŠ=éš´äü{ƹ³EN1žáö¤/ á"*¿‡Ë ®aüúÚïœÝKšþì¢kB¯•öbL|ÙdBç{óêO´ºðâºx‘v½RaÂMC È÷ÜÉ0dXÃÛú3'œ„õü3¥³ ³»ë_rw&|âjh ©ТþA¤GŸ.D_Î ËèçÀï÷(k)øÆñ‘±—Ý>Afé{¤¾‚9w~k¿7'»/õ£A~=uÙiíÓœ&S¸ª|sâ´z”]!5®„hYnaaÔ¾ïO€ŒV…Ùôéú;mje™¶:1¾€f½zìÓÁZ>w¬sg1Ûç¦òØÃ¿²­‡"MíÓfc˜úEþ¦cšñÔèÇøsÅÒUÏr(×Úú[¢Á6¯•øw¾q•š¿œSâõ7ûÑèâ)êTöÂG1©Ð®íÓ´î!n -´P¸í¶ÛR9ä_ºÕ¸/̳ÊV‰Éùóóò©í:.@ßæ%ÖÞdÇEí ;ä¹ÉûÆ2 Svá!à«âdè¡F†ø>¹™ð߀Z@AßSž¯®(\ÊŒÄQh>¥ŸF{3Œv>¢§E”ÎROˆÕY h5¤ýÉqX¾]Ÿ™ÕwD@[޽2Jªá\jÝ%$³Íð‹ÃJcO×qøÕ¿R;‰˜óŸ@Åæ(LOüWútIbÔ„7Sö¿òÞSlŽ+Í®²ÆŽøS|/á{â­gkóJÙ:lV¸ËòH$*K` K1‡¼…¿ÕÇŸ«n“> º;I«Pÿƒ^FkJ´—¼Ü`¾ÿ¬€pyãÇC€b8ˆ9δýŸ³ ¯–ÐÏ2öÏÊôi,Ë_{RÜê ˆtüôµ›Í/虪o³¥u^˜÷‘DÁæ ‹êI¡—ëd¶§i£ƒ›4³ó¾Ûص|ö´ùfat¥ŸÞÂÞmóÂa=xœ>0÷ ɶA6!ý´å̯ gÝ0ľJÏé¦-Pû7zÿœ€@‡ÿF«¾Ò9”WLdB-G=pçÐÐmÐPÙjp´ê_AÇð@Òâ[,£úNCÐý#·JMõá›(гSNø­•º+=@å¿‹èt2u8ŒJa݇熳0¹oR»ËëûÔ];2ÛúŒâ[Pä¿ÂK=‰%e~NEB[‹±z¯G7Dq‘$"}dßÛ÷;îN?º?ê9²³áÔœöΦD<“C·Ô{ÆMÊvPع÷ÒÐ×{v¸P¨YÔ.ó ¬¢‰7šº-ý­›æ%1ý.~ H䮂héßeõ£üÀ麸Ð`F·/§èWYÛ€álÒ–÷ð6]¶½· G ÀíÒ ¯–ˆ{À·Þ¦‡ôaGoiϱ§^èÂcØ‘ãÁfêl­BÚ¢í5"N“Œ¯!qº‡+% ¡î} øBèäOX™yJ½½¢åéñ,ÄxFÅO)+/$TR\êp,%’NTº%ƒ>¯,VJƒ×Ò±Ðò<Ûoôà³<;I\ù¯¿«.Tâ{ö$•Ÿ ó—W7§³t9ñZ`óyá*wlÁïçÆ‚;QvpA_Ëz8 ×O&{¬Ñ¸ý-ë/຃ÙN½€öW½ïØ_žêÝ”¾|°´€Û£¡ÉAqSq­û”‡°À,Ï®ÍëgËK¥Û±¨äîT]ˆÜJ»èap.yWSA›;á‚⻥aá"<ÜZëàùÁ0^‘ê¹6¸H§.å"X» }ýœùÒY6ÊL‚ÞËR££™ËŽ/Þ·¶ Ãy¹ #ÆÅ×ôI—Óñ|nòÉún•×ÿÒ;]vÂbš­ÿj›¿ž~a$±líÆ`ßµ3ÎjLUÿDÔ}Aèé Æ˜üp\ õÜqq3|îoÕyœ–è;i\û9Ê{&Ô§%ÂÖ¥;h÷÷Â9 {ƒUÙA~%´fb¹[qœ0ˬhpª/4¨ ðb•HžoôÍs+Kašk˜€=Aw í¾ÓÁ¤c‚ÀcþPãáxýÉ#/Ê>$ò\`(l”Ŧ¯r%ki'~… gg2„öò€:ß+6Ë&æ–ÀÑâX7 ú›+agÎå”þ/M^ᯠT'·-ÿs’RèO×ÑÞiVŸfà¯lØÕ1]Dú.·Ürá«_µ½Ú‡f`©ì(¬þ…à­‹- ÊÔÛ×f`1Z¹z˜‘öÑq|31Dª´6¨ôwjÖP¸¬“È]ÆÙa2ú`ß&Â|—Ù}æÑmÓ˜`µª¯ÁPŸyµZVàw(åG¢li]“Xú~ø-~#ìÐ2é„—©øüÃÃcÚ…5O—cŶ+ʇÊs6\óXj–«—ôÏ'[b¥4ƒw û/­<Ÿ‡[/÷á"l¯}¿õ¥©ð4;HnY­¦¦î»/ÜVìüj¸ºý…<†¨ÀÄ{†«ËWá'öQ¾á~Çõ­0 “÷>–ÌlnÍÿ€Ý+ ½Å”Ýýn 9ÙD—‡?9šv¸œEÎÀÉÞ#9håݪ¿K/i­”}+Ü>ö¬díjøS:¸ üÛ€65.…tÑ÷´ ¼Ôd 7áRDßé鬸®å’~qQaK'ó;5ý8÷Zùn@€»é«!ÐAnF<Ô/é•)ÿ¢éxºýy*kçšÅ4êú4|˜ú\„r¹þ½=<î¦hA—dü¢õi“[¹£Ý†9v¼Íû/-º¸ã‰á‘æ×FÂ]†¾ßÅ*}ZÈ^Ï"–m4%À³²e`Ù9˜gÚð°­’þÌ«EwÏÊtcàvÕèÏ3±ÛütOv'ȉêé+/÷c Aé]Ê ú@Bù[ʱ×ã õ®‚}%.ªv­ÊHþíÒÚxY•Øíø„€¾æŸ­‹Ûúó,£;©q?³d›‡(.wF>æëd•g=¤ÚÞº¬r-d6/ñçy©æåÄÓÃu6¼{ž0bLWAñEà·¦–í ,âߺˆOßÍ]4,¼ð èÛéÊëò`,Û? ƒ1n¦ûêÚéìÇÉu+E‹ˆNƒ§ ¿Ã÷¿›ôz9•4*¸ž®–¾Ä„QVOÒoÚ_ ìþ“É-{âsLé N_– %¥··¾ë.¡úC+åB7- ÕÆUlœë˜<åѱÃúz¼.Çáh÷¼°7ŠÆÌ~·¤ÞŠò3’2¶g{ÖˆžŸ„o³]ôúÞÛ«òÖÊÏ!¿Eпª¢d–s¾~} âŠ@@CXz̆íQ fB ̳vE½e âKwLØÞÿyÅÙ)›Oö~{€Æª¥Þ˜yý~xŒ°WðÕ/q' ¯Ÿ,ÿØ(TëK‰ÕéˆO± *Ý—¼pÎ:‡ï|:%#¸÷ÍdÖm¶ F¢â¢ážï€y]^WÀ<´«ó=BÛ ¸.ø¥Rv0ÊO_ õb³AK%ý[f}HfãåÞ—PàÆ^C¼ƒ¯JðÆìNß>ÀîÕáG„Wì–Áÿá˜Þ»Â=…wéX+y ™n´&¯ÌÁ*@é eôÏû†…õuË™ò­Âj¥¿‡ùƾÏvð÷ÂIå)37ºÁm ½Ž†ïç¿éD*Ë‚M }8÷6䨏0_8|è Ü]Æ_¹Öú¨Ógµ4MCQsòû$¬ûzL®Ö%/-ªÓÖŽÉ‹Âì$CJî@ŒtœŽ>¼¤ ›ä—úo$׫A«ï÷ݾÓûPø À^´Q¶½SI.µì;’¾üŠ} âX? âh@‚'µblÒÊ» «Çôþ'4œ«×m{QrKW°õgur}®iÎ?GÁ×¢?²? ×Žë´—–”õ4pÈ“p•ÿ~Å|Ÿ‰‰O¤ÛrojZ^|¡ò¬?Øø.ïWµzYèåhß§#ýw@kuÀ*ÖÍ·5d0t2o¹=ø¦ÝW4ƒÐÏk:¤Á3ùû>/‹Pä=˜lCè8ö¿õù5}ÃÁâh(‡]ö&þƒ\¥ÅôÊÔmëVHò7ëq,‹"ºÌH¬Xy– ê‹*¼’ÐmY´ìÉùkcÜI›üû\úF`%ê¢õï_šÐ¾n.îôäpž·Q—˜‹r]x4iÁ€ÔyóRóî»™vÒJëU@ß×*õú.mé<2º’†äé¸9=¹°ôõP(, íž ín–+]N…þ=èN—ƒßîMüãZ€~¹9 ¬›”!õ0·lßÉ"Jš™i•ú!ºÌiÉLÔX-Ö€FO`á²´SzÐÙrŒèEÒá딿`ôGŽ÷5)+æÑîW 6;;—èù?f el~:yÍÑEÆ´þx-å¾ÚûÀw¹M"m ”FÝŒ¿?4qŒX]àÑ‚¸†ŸÓo÷Òo‡Ñ6¦Ù>©ü¶rœú‘4ꃻÃÕtÉU¶!w‹þp,þ‡‡ÝXh~“øô÷pÄ ùÛ ºŒÿN)½M~˜~¸#õ@æWé´Yp±ÂðUþF~{«ÐÇA¸ÿÁçþ{ÈVåö…Qâ¿y(vüš÷Ëu-Õò¿ ù—ãÿó üV? ø.i?rL]ß, \ʼZ¦¥HQéXý»A²8˜Ã‚ŒÝC‡G3®ÊÓû )wXý+©1•O. ¾ ==ß¼=YˆlŒ÷¥â3²ÆÑþXuÆÍWס'à§oC˜qœÎÊ–fã²”ñ“÷d0“ný7«ÙD¼Ð¢utdŠM"žtÒIÕƒðŒRª‚N¯_kÝPb²û²­_mónnM'[WéÇE(`k(-¥ öÆ|üUY­Å$›ŽÛîú¨£Žj¥úÞ¯lJRˆRxI¹Õs.<Ä-¯“³ÍT!< Zïs-¸_Ov?½Cï„Öv´QUrj8þ)tGÁjü—ž_¢×úë º’Ÿ9OÛù[lšp‹DøÞ…üxè|Þ& ¯'žŸÉÖÕ£hèrG+ß«ZÈ¢˜fw…ÜJþ*†ŒÝ;üååz®ÄZ[«¾dëR²µµ¬ê|“ù3íoQë­›Pö<¼æbÆ®cÚð8À»MæEÁB}žŒÖ‰%³ ’–4$õc¨ /„ˆ«š¥zßI?ÝÃ×E>dš=ië Éÿ°ó$@e <ÿ=ÏÓAåj%Ô*¸ÕÍíþ™p‹Öwik°L”êí-´ß-(^Õг ã˜-ªâ…¾üWE1<¼ÓªÅ><»•úÇ“âcüúß§©ëÓ 8ø1”·mé5ïÀá7|ãÇ«q5ºÜV½¿¢¥…FË»Œö¡n—#jè6i•\ÝŒÄ]——üÛ<!ETm¹LOV?w—ô½X77¤¢$—ÊZZþ0©Ñ÷e6vÙÅÐ Œ‹tpçÁá¥[ŸÞGË~Îå¥Âôv(ô.lÞÃ7lZ\5œ?ÑïC߇+òæñtò°"uu¾º ¥* Z!ôÄmy e•±º ¸Qæ›´÷è0´CH‹«u*Vg»²UTªÌ›[³å¤ï¥swã8žó‚íâÂI[GЂë'‘¢“ðV&’–{‹c vÉÇkòæÕÊÛg3±êoŸ§¼b¸–þYð$ºl¹€û :Q.YDý„~y(fUýŽ+¹X¥÷¡8}‰¯Æ·ëf¦“ ¯Ü«Yˆ[eZK*ݘüþéHÖ`Kh¾7õûE=ß0¾ÊrÓí<Š{%®Ñü•[oGèø5â PgËQΜ™ùaVFž`ÕÔO\9P‹Pÿ´ücóÏ€ž¢8;ßí!{¿]<×Çrû¢¾²¯d×·ŽoT¡ÃøboÀzÀvODÚ‰ïü½“¶¹3Y°Ò6°”,Ä,ß¿*Î[ø*/æTh C‡âlá±OotªñâtþÙk-ÂÝ2eß' ·±%ŸÀÂò-¾o–¾7ª`©ïcýþTÆãÔ\¾>zH¥î ¸:ßz"ü¡Ù|µ->/ñþDJc‚Ð+“ÆçÛ; ÒìÇ?MhWWI@Þõ7(snM›Ü ÿ×:^yù@<9Uy!±>gÕ{Œ>¶/ÝÕ2%1oàÙâð÷Pjk íÜ _ö[; Ñ6¿ÔÞ”ï|ÁùΟ¬ôK|Þí¯‹9ºò¹›qÏHò(®ƒÒaýOÀü8ôüVƒ"éô…~´ù³ð/øy•<Þ"å*ÏëQw2H[ºL˜ ݃6=+d|Œÿ:<ºË.òÛ‡.N>ß´mÊšÕ¨â¼aûá?Oü8[ý_JÆAY÷öÜ ÎèPn7äH)OÞþpZ'ë;˹xù:—ã(¸Àú(}dŸ ^ ¤ƒÒƒüÝ=HÙøç!/êÙ§¤Rãç.ƈÜòDߘä܇A*0ñK~»ß®‡ƒW‘Ou+s(þ£e,ŠÞH¯Ã1«lóÑýÉNÅÉ7%‡°ë摦t9˜¥~ó¢¥YœA.’;VXa…¤üë¯Wu«­ÜÔÇl¼óà¦W`XýŸ‚óü²<éh‹€¯ßž¾nÖ“ñBÁ_eã‹dƒ_c¢×B7AÑWBWˆ! ú¦¯¯¼òÊñQ|µŒÕW_=¹ÆÿúÊí,w!¢Õ† súçSàï4Ôè/MåÑð5ÊhæªEÅ6«œ$‰*ÿm²É&éÛä:ò­o¸;ìÑ¡ðØÉçø>—¶íÀb|}ÆœôçC=Órjø¿-ØÆW%&”Ó´f¡N*~#lÙ÷TË-zóoVÞgFŒ±ÅVÁ›>øÄk©j9ø4³* ‡Û nþ¾­J[§J*Ûê¥eªœ¤KOHŒv·ÍÎŒŸF¾§ „7ö&ÕË«yu{ñ+Ƨs¹[~/ÆjZkàÈdžó|gèKPÔ:=ˆB>¨µY¶=TÆ]@ëß¼¦E‘=”n!n›„‘[ݺmútX‡÷*êÇ–F§ç^kq«ÅãíÕžÉV}˜+(V ÇßL„r¦ÿÐC°„õ÷:ˆQÔ㩞Y9´oþpEßÕäS%ÌT™äSþÊ’ÌÌXç]‡ŽW*Ë?Æ:iMèdc€ºØC>››vÙ ÚíVŠs<܈eâáXnž›”l-*¡çPÊ}Eó¼ø$ùõP´;¨w§Ûâu¡¶à5“_cÍó<åE…²®€n6¢¿ØY kÝ'S_­%¥‡ïÕ裃Ì)Šcá<ÆÂÝH?Z}œµZúCª$Úþ'MÍ8 wðt9ÃÅ•Y8ÚÐìõ–9LƒP;'ýv[Kz’Ǥá´åz ?ò­°'®w­³çÊÏØÝ ¯’‹ ÉRŒEù§‹s3@~ýòM-ŽWòÓ¡×ËéÓ'ù&wP Kׂú³²=ÃÜñu¶Ùwô_¯ËiWëÔg*Ûó¿O{¥Û'Í_\8Üz¬Í"Âݤun0¢µäge­èÜ|2 ö™ô>» 'œÎÃŽu¡é(Óç/Ýu“k9îWùY„\ÿNÅ…âîsižBÙé—ŒÑ;¡ò:kúÂ*t †E9Ç`á ábÚÄ…?)£íTJµ-ZµäÒÄXZÐÆÇŒ‹í˜#cΓ‡@¶éäI.œsÄ¡y &DoÒ¹òÉ´ü:“kæÖ'¦Iÿ )ˆ5ò-X5>@=~°[iÚðìÄg‡k>Ú:‹‚|]©ÈÈ /ìM\¬IŸaŽžë»ÍmÏ“b1öAµoëéû¢þ€ø£ù+‡§˜n Ýlwƒ~÷@9::¤é©ü¶ýÿŽÁC}ïNÑC9³*€VH¶d×òØ—þ8˜4Ñ·dímþUô5Ön9Š\~ê٩ǬÔãÆº×—"m>’}—‡Ž¯gÑÈpm€G]Ƹ±O®¥oµd6¸ˆ°1Šón€·eêM·ýÏ~¼ú×4ÊþZZe¬ê3šAha€Fnå`0ß×ÓS9ÅÒ¥[à }ÿ ¿(”Â:mˆÆñóCêñ¥_Gv…Ö<øN-õ+Aþ¡/Ó=°puqæ´f…nn0ˆÅ*ðÇýXðº‹E`ÁÂùi›3á3mŠOøó}Ÿ†½áËèå5Þö³!{†U‹3†#Æü&ñžžç–@’½¤8uX½wHˆß±ò"uIÁ©éø‹ S›ü’û®LA!Ó_¾VƒÄbNµ_­ccð»Naëôm,ú¥A_´cá)ÿ ïcy÷'æàÇ¡-A_Ã9´È5Xù½U`þNxzòxpÿ+, ]­ÈêÁ9Ì/$nGÒ‹vö·»´ï6œÉ|ø#LOv†vN†jÅjP¦ðw‡Dü€¼!ÕÆæ;˜a4uÕ/>¶xõ¡Äb^ïÕXQvw”­ «ßì:€¸>UõNÊ­Qoù±ôôj5F³€f¬ ߎ9íAh î×1ÝHètòâÒðÌ9“cOïôNÚƒcóKç·2vwÎáÑÒäüÈK¡4šÅÖRµÊ} ïü~…\³ËسÿÛéDs2Çüo1¯ò!Àé·ù׋ó?태ÖRÖÉO>îÝž6zaËŠÜ´>[û½c«¸ úÖ€5P¿[ê3ÊÜÉÃß™çãôµH¹ÁÞÐ`3Îâ‚Ùa¸ òŒ—²~’©hõé¹ç'Ì“éÅÕ§ãóBžf¨qýò}³ÿÅ+u¦+—Η›åÖùs±”®Âoˆ“dRÐÈP: ゘œx‡ü˜v§¶vRÇ/RÆ<1>ïöwºBO˜ˆ<_a;N^Ú‘¼Ÿ¤RfÞû>û/sþ‡TÊÃïĸR˜¸rß;ˆe÷—`@êü1õü„¿lˆy¥Ÿ+PF†~îu»•—o"87K›Í+}/`ƒÛ²ò@üø¾›_?Ùj Öiv-àë–·¼wá †I¹pûp^hz»Hýº З×)粂„Ûæ>¯A~8‚±ò¿:HþWd¾st?Àéüå0݇ßû^ìºV›¢È«üŽ‹}Íÿ ü½øË1Z•?êz6ì`q ç\ð}ðt³ R¦ïG­ÆôÌîÁ ²›ðu@ø™™ŸšzÁ[Ïr{{V˜}gìua™Þ{ñÙÙ(ªlÝ@¡5[:R.‰•YôM¿o¼ž‰•ðÓP —¨¼*ã¦ô£Öz†òlŽ™BõÑ<Ÿ®³ÕOc[ÅÖJ¹vÁÂZߥ1ÌÍõ½øQ¼3¸Ø âu? WÞ®”í.õë¹$ÀCoµRñ0¬–¥Í®ã@´Ã{O‰’·Î=(s%Ú/­kAA7À²ÑÅPëùû^ ³}´6€7²0ÁÝÃðܾ½ ß-À¾Ro/~„§ e;ÿRäçn—æatõ•±tOå‘~fâû¦‡îLfªQ±Pœ”º”ëY{šõÀƒì¶_´$€ò½ù9».‘Šö2,Àè¡Ó %š {¿Bà{ØÁ$¦.¹ú‚4’eÀ¾xÍ[TJÇ­¿ž—Û9ëõóî·WÐçéAÙ' ä´Êz/xÒÞüIÏÛÀóþU‰¬VÁ¿¦c­¸í´ ±¿Ö*ûäÝøÒÞqìelϳÏÃEå» `Ðv®£¡e¿êXhùDþbp‡†2˜»À¤Ow‡Žêè×ðÙ¨j!¾mï;Œ\E`½ °i; ZÅê—9Ksò w¤Ãd©e­ypm°xË—Ò%@IDATéÞ°êÐ]ªmŸšj‹Gñ° ;Kn­ôW*iÝ¥‹‘ëÓñ«¯â…¥Â¢>u>m² ßy Àl´Â–—œÅbÜîÙ±WX¾»jârÌö]&Uo]\lG_dK•ÆÝâX-ì³¾xßâÐÌ0ñ…ä½Tgðÿc!E?ò…©ÂV|“ úëNÛ'.榟{­ÿÑUšÈ)gloáÝ*$x€ö7‹#h›÷OÎ}Íš´Ý¸ÚòÖn÷þœEg]…Äe‡ùÎ+çÃwnó|c^îX‡9DZ{^ô@¸Ó·wf@ÝÄ%ј‡÷K—…%X˜_…E·[šs£˜=@Û³áŽOŽ?{/ ¤sÆù”³ó÷­äUÉU~j»ºsèsJPµ·?Õ+½Ì¼~3ý2>áÄþ7ƒ|EÚÌ ê ºÒª¾u@êY€1üûY<š¿NƒóÚúNFÏj4ŽÐíOgÒ^»ÜÊ€»#«î-Ó%ÿ²Ïóîa;I<&ã“|Giòòéä[ëòG(ý£8uݳNòù¢Ç`,ù7o¡X$M®9l¬4пً=MóX°8¤é»–kúXöFʼnêÊYµ8¬4ý |[«:N7ùÇ>IÿJg€¥M°~Ñ(b@×龊¥;ŠSu®“11õ9¹ðÕ¶y£þUã8F90(¹¿´0eõy«ò`r¥û‹#›Æ=§0E a³úžƒŽ’ënøV«ò'¼«õß„¶è¾-Î…>»Ý~PÜ¢tàCÛç[XœUÐMëâð•NïGØ¢Wâ@–º¼²ßðTÚ©Mœjšž_°-爖ùUã¦xH³g(S¥1]Rš«¸cWyžO[”z¦Oxs³¼;}L[º ¾†rUz­8M °¼«º¤Ë±^øÖl›þ¸Âä¥cùK§µ\¬ƒ+Ïf*ÿV/…áwq=U‰mä¥Q?@‹’|\šÀú¶tu?ž¾õOç×êZúÞµ0qÛø–e>XE–N¡ à…ÒÆÔsªÊóVeøÎz:l˜iS«Ò‡=Ó–´K¶¿q¯*Œ(Ÿš›´K§ro¿œÄ/ðwÛú:oq8XéòÂMAßëRÏ Õ9̹ìùâ¨I¹ù9ßÿÕùwÈI¥¥‡UB™/ÅyÜ:íRi7TJ yÌGþï§-9¦ÛDZ8ª°,c{gž—i̺گéx,”/LVRO?ïöZYÞº¢Æ´ÍGZ²í¯/Ž(a=Ú6~.{öHÅ›ž­k‹¦î;È£“rªq†”Âгé“ãÇauÆê=·¼£è{i7ö‰m pV½Ïó¿ÖQ¼­ -šÆÅˆ§„æ&ï§* Ý#‹ë$ãüïŒAÀê°\IÎñÈŸ¤u\ý‘gŽÉt}¥\ÔÔ=K¿ïäZÚ»–ñ§¼ã³í7¡3i.>3Þ,ÜOWX:Z«ú<¾oý[ËÛx¶Ù]è”Ó9šùyÕWÌ˱±Z]ŸQŸ¡§&4&·.¡]X8Jx_L×ѯsxB«ß¬|ËŒ|×z\èòÛi²yùey°º¤žõúæñkyˇž…'F9?›FÀvð¹c"òCu•ˆØîê-ÎÙôÞVw4·8OþÚ5€²:ªSÏ~¥5†]Xz½gÎÀkÓïô.Ûö拵|éuæjiH^‘-Ë9ïÚ»Ô3{i©Jšlœô½s™óe«¸¸qiÙvÒ:gA4Ô%]ÎÿÚµrŒsm—ê¼ä=ÍytçùôÌ|nË(¬Ï[¡KzƒO†öøÄ¸úfçÀì<ت,@ÜDfg §íwîAÞoÁ–ë`¼·*³ò.¿Àþ(2ð•Ç1cà@„†ŠGÛö‹žfÏâ$¥ÙÓ`㸺>¿8eGåŒ2}Gñ²õ¼¹gd¿Òeóñ~Š ž÷.ïÙŸPX–( ´ò?kºêFhŒºªÐBɈeD€6ÞÖï£ê ÿýá[ƒU‡Ïs>øï+q`Ò—Žÿ}žÛ¼Ûº©µJ³raéÒ!ÅMZÆIÒ6@i<¦i¼)rÊp¬†­ë‘®#+ЉrÒ ¨rsxq9„9ºtŠ?âÙÚõÏÒïÛ\/W Ï0W9}Ïžäµj˼\0z%( ha}Ô2M]}pa•ºø‚Xì&‹QˆÄôŒ~8–ú,Y·.¯Ô·ÙÿCñÝ1¢×ò«õ ÛÃJXÂÕå난ʬù«8ª0ŽÓPþjÉ3¿±œÔo 6®s…E\mVOŸÛßõ G¹n*BõtìBßÈU‰€ù²Í·„oÔê}|žý¦.-‰r„­r5î·ø>óóëÃêótZisýŒ²ã V’á‰Ø›–®hZ+•NpöæÔB*~ÖK½=Ó5ø¥«ÓøÎy KÐÎõ`¦ýû×1÷“L_ÇúgCÇl¢MŠ·Gÿ@)Ïòþ¤EÁ¹ºçÅéäÖ륇˜Wñ+˜[NÌÃþ¾š6s1¢c ¥¸}$£mc>ãü·ðúcÙA)ƒ)KC;¤ŒdR]Þ¶…ŠšôÒîÛ¤\ˆi·Ó÷*Šéñæ¢ÆÔówÐæá.DÞ¢ZŸï¨Ò€˜7ôlVÂ]K ø2hÔ³wiß!$ã.~Ö¾‰lႎˆ|¾>¯lÞÝÝ 6¥+y˜@–‹Bý+gè`EÒÚÖ3”Š[WórÜ$4ïØ-|·ú<]–‹¡ùÆäÇØtѦ¿Þ$ýÌÝöka)êuå/’[~º.ÙëØ§Ùçƒq/ ?λêê%-¸à”-CÂÅ7ç#v °Ø8éxiV^·»P0]Þ¹çòˆËÖÃ{y¿ï°´OŒWœ;Î|‡´œ^PÈ˧«gÅmXàú%‹„ÓUÁîféÓó¦ôþüàEþÒ Éé8Ž5çÐl~`O?Pö{ÿÀœ“'ÿ9¦þIYŽój:å*å)l—+àñ¦Oס—÷ÿ‹×òk†³rDgml4ä·´1ÆÿCíç<á_ëoÿ»ˆ¿s‰W–S[Çÿ’Ð_aeèao¾Y¹¹þ›äUêñQæÈ¾ïò>”büK'œ Ô7zºm&\·n'p-Ÿæ‡¦T€Tˆ[µYZ©jÏw 5­”=ãhéâji»¼:yßr’rt¿ÊPá¨RÖIÝÇg¿´p>>ËN—5o嵿‚eÑÂ'ÝÞ®kí­ÅDÖi ¾±Å´_<%–¡Ÿb^‰p­ ½Vù‰iüUàLK?ëäZE- ®5K§%¤¼;ò Æ-U\zÀŠq‡®ËÎ-§çL-£jý’wVîœSžàêô;BÏ¡¥ƒ{~ÚTÁ¬+/©ÏwÚÖ'¦ÉÎñ´¶‹qšý¦ûT¸/']´$ا0{i·á×’Çìü}½&~‚vZ&ÉSÀ_€(»K#]ŽŠªégy×*‡u €CŽD¹9)Ùß`NŽŠªsôm(éZçå“~6ÕÐ K¯ Ý·Ä!Guqs¤ŸV‚°åHgƱMu#`% %Hš.Ëët;fßy¯å¼tŒ« è“… ÁçÍ)G°Ù6HÓy^C÷aEéX´g’gØÉÏÜÕºiQ¯b>}aMÊk½°ay@^ëñWGŸ¥GšÅñ¹ Í, ›§«J›ÅSVú)ô?PëÑfù÷ç¹Ö×wÒlÕLÆ}. šÍ«Ù¢D6žã+^Õªžžò̦íôÞö{…1(mú'çB‡V—Áð!k´³8cS ̺9­gøõ~uu²´V¼v|¹¤Ó: V<篎çÎâvð¿§G‹QO,›CaýjýÞýZð[FûÛ…ŽVõ×ú5o w&KµçÙòûÏ£/"Ÿ­Eo²ðÑÈóêã5çtWººwQ¨]¸\Ìp÷ÃR¶ó°«JÓ =‹ysʺ…5ei=ÎUíòM¿W–p,ÆgÊ8éûøÜ_ÁKšK)lwüdiÞ9Á&QFI§ïÿu÷rn†’Ìi9ÅùAÐ]9ªY]ìÓ`qŒk4Û½åìAð hqœ¤)þ˜ÝB7RÖlIyòºf<ÞÝ .4's"õŒeþ/ü ´w´Ë¥¡]§”å© ï>ŸmØJÖê䔵]<¸ÐvÊ]AYg=øO”ß:)o\Æ'Nq§„OâÊi\ÓD>MPòÛe\Ò`æíÝ ó—Awbiãwû«åƒÖ4¦pªO23ìòºgnmŒq¢5L¼÷וýô*®‚jú½×ÂfŠ©J–´ñÜV·ªÉ³l^ñ^!¢ùV8Ò%VS•ßžƒ›æó‹¿ýa\ÝlÃå¤mc|%v\ÇtZÛ+«tg•°¼Uætý¹îÏd:è|Ë­{Z_´ “ÿ…w‚b ÄýÎÛµBYëÅvi>﵆‘ïö·nZÌ tœ«TªÔ§yªõQ™Û0Uv\”ëo=»M§’«¥£õz EPÅÙ…©J»÷ìH}£%[âÊõ¬Ĉ‚¸JÖ%Šr'ó‡‚9ÛUóNÀ•´Å³À¥Vv”˳-5<ÈI’:b©9_a¡šöóÎc€öͬ•“ÿƺÑ6ÔêLKÁ¬ …ØþšOµ?†ü®ôµâ‰¥ô­Ì寂~émy*ÇQ‘w ü åÇxZ Ùþ57ºçH¹kq¾mµU¸Óº¯x~«îCú3‡gëè.!û¸€ ³ÔhŒ¶Ì¦÷Ž;A0Á"¯ûkê8pÑ/½`²4æìyQ·'nEU>ü˹B@¬nÜR§yÃ$¥ã{6-}¯0OËö•–Ým 5±yêb|´º Çu'.ÈÊiØRX‡:6Z†ºøÇoÚ•Xº,¯µô‹í—}×êÞ9HôKšVñë߹дu_±iûÊQuóQŸ–>Üžä~s¶OXœhé ï²»òÒ8ÞÅ]u±­÷ïÙ¾tÖd–&-î^WŽô¨Up' ’s·z^\U¯ÓÅO;š‹|ÈöטDZªžšu‡#ßÒO«‰ÎµÊ-øå­û޼vèï3AV-zãi>Γ.°JÙÍòÕø@—J~G-Î4\÷‡_ªÃçPÖE 8.–+8'–ݼ«•ýżv¾õ¿èß1˜õWw±~ »R”‹:Yu>Š®E,ÓþàLŽ×VƒùæåøÒEšòn§i:‹ç¢\Y>î,~¿i4?á (_ÐA­¤Æq' 2ñ”ûôMV÷ë¬H*}0.@ºÁjŸ8¡§óó" œ§´W¥©Ùj¦ï#wÂWùOçߟkµz²ÑÚ£?ù6Kã6}…–fïÛ=wkVžuS»t¾WyÎNè*­1­ŠƒVñ~0~ís·#Ƽó,,Ò.¢Ò<è|+’±”@ÛYÒvâ#³?ߪÂÖÿ³ý)ëËžFeT…K0¢ÓouÛezk{§é:§5£þ$Ë õôuãØ…­ó,ˆÜ’¨µÐ`ÕM°[·KÂϬûÜç  ‚0Ñe‚ñUd;ý^•µ®ÇJPvè9—‘®Ë­´Q\ØL?ïïµÊcTèú›G~:üS¿7Lk”´þõOzu;«´Ñj^VI—®ó, µú­Ÿ‹ëçÉVN~=ëÓ·ß…²ÏdMÜ⟴”ÔU>q>t¡T€ÂïRÞr›±Ö“;`'î?Â\É·»Ø“xX^ç4×ÑwP‡ñOKn-{ºÇg²‹<ì.fÍœn‹Â*¥0ÑCUKÓï±Ï¢\a»)CEÞÛQ¹t ‹uæ•·ýºån3ÒÄ:tók*¥ÓȃÕsûêë´ó£€[ÔÅiµþÓÄaVâlSš¸2Fâû¼_ý]çñú¼¸Ý<°M÷U7i³qõ5ën‰ìó²ÀÀÏ“±#ÙPïš?ý˰«±¬l ×b¹°pC|éVWFùÌ^YLš®ñî°ëx.HتµwQ§Ê‹/hiœcÝ­ | 8]éÈ!û•†åìÈÉòÓ¼<}f™‚ÉѨÉCç«fñ}î{ýDç¹¢Hïri•Gú|Þ9·•¾™ŽŸÝ?pG½¨[=pþ!‡—–öGÚ(§ßÉ/¿~­Ÿ;ß:Ÿ¥«ÒF!. þÕŸºv›FYïÆh¼Ömú/J|Úõ3ïXí¤ÎòmÛ¤ÓIÆ‘v\äpWŠãV£#e*òæÊNóH<åzuU9”oÁ“‹<H¾ŸEZvµ%•æ§> Ô?˜p7¡&´À„ø‚´Àõ×_ÿ©é„j¢%á M1(-€2öªœ6~0§Aç@¶0M¡‡Ù? (ºáÙÒ˜p.§H· ,œ…×ÚDb¡(¼ÅIÞ‹rŠôSœ¤~×€aᑞéÃ{¥Þ0åé}%9ßE†Óí¿.¡ž÷/š (ÙˆÞ …*"ÛÍ¿)Ó“Ö±Ì Q£×É .9½ü½¼<‹›óѷ︼·É3,aøÞ¡á‡œÆ½1äàz/EÒº§išç›8W7ÄaÛføfaxØ“S¸ó mZ Kqòñç?LFp†ý3ÅiÃp:wVN_–ïûZaH8‰“Æß¯| Rø&ÏOªœ#oŸ¬ÈýŸ¡ÑÈ'8P*¬Sœ$ …–ÆöóÃéÉp|aŠp;ãàÔÜs¥i&º2„O§ÏÏ (IáÔž‘á·}ÿIÒ\<{¼PÈù¶Bx¹B_lßû'‹ðBg_áý¿ ‹A?aÌ1áÜ á#ân ýï€ÕZøíy3õ1_è²Jr»LS}÷¬aëâw²½ÃcÊí*íïD® mÇq‹«Nî^žþø3¼TŸI‡wŽÑÿlÂ͆ş0 ¾°m齄Waf‡w­Õ÷NÀR0yöVå±› Ì=ß”¤è Á8ŒP ¿<,Û÷¯ài鬪ÂÙÅ©89ý­ð×T7§íW+~=ìÚ÷4ýï$. Bá̧Ënëalðm'ã$è–áG†?Ù%ìÜwcx¾ =¡8ÇìÛ¼Zy…¹à²ÔøI ø¼þWX>pGÏïŸðÏ¢š€mŒùà€Î+ÿ $Ç/cnÌüö—Æ/™Ÿ®V¼5>Ð/ |ÀíDø}ÍaùÂÄa­Þ7Âå¬'Ö®a¿¢\»¦ï}µ*‹`ÉÎë åÐàp؉±|cýÏ©òÙ6&¿#à¿Ï‘=Z5VÏá êЯV/|³9bN9‘ϸ¤i1,‡Ý¨ßÓ¥OÃYô:€|§6¿¤º†7ùË«Ïñ=‡ £Â:cO¦½>L’±€¦¦].Îi¯t¾^;÷ÎEüûà ÿM½ïÑJ,ˆÂ+‹I=ÿ;hé>æÓ_2§· {…k˜#¯t<¦Üb¸Ÿ¾ØÌ’εý5€hø|ò dâRü°}ÊÏ. 'a+dÍ¡“»;¤eéé×È>ÓO¿¡ýÓÁ¶·Ò¤cØÐ\öÁeK˜{ªÞ‚qp´tËgØê"ì–”> çÐ΀ÐH¥FPò¡ÕÿØ='•zQ³ |±%4ø84˜ž“›Å—ÿÝI~ÕTX ~>'sÕ$‘3Ô‡býí„» -0¡&´À„˜Ðã¯r"˜3þJí¢¤ÂZ(Yw‘೪ü"ƒÍ‚Jø0ƒàG' ¯àí=Ó•ŒVa€…ÇyÏ [€RðÏ TZ ç÷}€¾¦U©1¨Âîˆúú¾*Èx“EÀ³ÂÂé' ×S¢Š`-\÷¼…°?€Îá¥2 ±; ÎÒÔÕ ø› ú&o™Z‹M¤=«ä›$IÚ¹ÜÖåû¼ÿâcAß¼˜Ùgûð{öÙ’Ÿ]`k$ʵà_»ðn2ÖGô½&]eeM€¤cR ¯9ÌO¿ýPW%Ì`Ÿœ°æ“TèwlÏ^áWÅXì˜$‰›÷ßbØB_÷zQÚ‘á·‘ó×Ò6»z…´y k]üÂrÜ6òŽ|¤ËþÆ»×ê¢goy. ó0Nœ¿öü: @•$uQ½¿…›׳ÁyyGà²ûzO¯‚¾ÆY‡¼ö@w!¼]ø!óú9ä¿BF–’‡DppEæÞ£hÜ$½þ ­ó& ;„c™û³ ¯éÖf>9 š_5Sn'y$Ž3òqÌÛ_Ð×ou!bGÆÆ"ÐK§AwkdÇ,è; ÏÏ.Œì´i“•ï¥Ìn°æýùÛ—„õòy:§k¡!rž„¢Ô>KÐ×zÉÍ4¾ô5<Ç“æV¥á?Ü·¦ŸgL· ÓП[ÃÓkÏãûþþêß§ïÉNóÖ§šŸt_‡üÍü·K¯„´_*Ç­ÛšM‡"Z-ߺd·+¶Ë{|¿¿mvúœkV®ôýyvWÒ¬Þž×èð‹Õlï,|¿)=~Vߢ ýnÏòÛùvN›š?ýb1–l‰¿Œñì6~]·¸åóÞ ¬Îlz}¢¿Øf45ui™‰î+=ܳFsß±™´ºiê?SßOvf?}Y°²Å¶Y]¿ØÏ=G7B7!{8w‡"T 9®EßÔ¾×탺ñÉëÇÎd<|¬ 9±TºIB[yùøÌí›ú L&‡èlWmv“‡éSNzvn}yãÙžUÙÂZs>—žÓÑçÛ,Äß©ËC÷nSt^wke³ú ä9ªSc¾Å]ؾwãóT\e[] Xv*nQ “~̶ßsÎth¬›òˆ2Vþ7ÏÖäyc>ùéçv|“ò[q+&~|Ðù·>¯7¡oóë0 üéÌj™Ê€nSíDÞ/çç6ð²k‘üüËmá!OÍ|[·Jç;,2“À¤Óvq=x΃ìÒzˆåz8V3!oìêÆ#š4—è–Ž½࿹•«–võû_x?3mç¡yÑMǸßòŽ3}ñf]Ué?U·+õu®Ï7¾ó y€íË¢Onù9Öü‰Œ7X˜‰þÍ=(O÷uû_þÕ…‰nâ¼"•¯´MœŸuááy%yEWó÷Oî-Ï·ÍÊuÎú¤FfëÙŸ¿}\×j]á»ÇW^bð;Ý\adÓ¶Äʤƒªïe/Uþ.,Ú*mcÆú°ÐwÎøjˆNË9 æµ °ú¡4N¶ •¼NÞì¡VÙ|ú{¯o¬Và• G|/#SÊËûCèO^ݤQÑÑ_³4ñû²ï¨=Å9>÷P“¼op’Žô òe›Æ4ãúW_7Ýú¢Ò x'õQøÐçN:n;eqŠ´Ÿ'|{GTNÓå|ž®R²Ž Ê Zdø,NŽþ<µÑ8« dr€H º7esècTÖÇ{Ùsà¸ùÆÁ-ÇCÓÒ‡G vå¹Ýä¯"!VÙPñäÞX§É+déÓëãûqòË¡`‹óò× WÁH|þz˜cæ¤å"¾.®ßôTë–õ/n^*hZó»¨ì1XJæ@ä –õ.¬†ðˆOÚ6ãQ‡uà­>XÊ]ì §~3õÕÚ.¯þ¾WFŠ H³ï²ÒRÿ¬qÌ·{¿vÏÎ¥s{¶ªÔà׊ZZgÊ(¬Urè@°£_§y7A‘¾:•¿ñ”(ÁÐä[(ïg¬y›¶ûVAÌÞŹ1ZäBzY»ôÍߣ¸½^ÐÞbI¾`Óùiõíií>ÓzÖC~Òïz'¯h%”7j¡òá‰çžÚ®ºÀW^\ i÷éÔ¢µ€Tü¯Tg%éc‰Â­•f™Žìc}±gõÒb…Õ«Ïãûô¯u肵‹Uà£R‡t­®»iZåÕé;Ë7‡VÆqqË Ø›×]¶MÞ÷Hs‚tÒsÞûøLy;½ Wð”c’4‚¶ytÓ†ÂúÔã–ù×|¤5ùÈ `PÕ<š\»àñ:tïÂF{~ð<ôìÒ"€Ð;¥x‰ú‡c»iY…eKa’á¹õ´/ï×Rš-ûIZõÌ«àƒŽµø'ˆ×4ß&ßd|­ª'ã)ðÝæ¡|é|×mºnã»Ë@€ÑD½6½<ëfúÃCŠ;ÍÏñ£ì+©yRäý1y–úA7‹·1m(,Aÿm@}\<)/˜iùY}_©{úÞ9G£ ¿G5ý®Õµ4aÿ+S´ çá›5øÿöÎ!fÞ}ßõ?Ì&ôtý~·|‡@îj›ÝzávÕÇ Ånž}üG¨ôÝRÅ` .„Êó]cBiÖõ™1‡0ÉmçÒPu‚Á>?ÄØíû=Í/1^™„¯Œ}#Ââ‹ËÿÞ\u]† s„?-÷Øî)´¡4ëð ¦SˆWO‹6ɳրŽp®¾}Ö.ÀW¦Ri+„L˃yË|ü¤¬{ÌÒÈ@”a¥0†…$ýB¸0M‚¾“ %ìüQY`ZÀmÑÊo¼Nž7= ‹,TJ žC3ÚcßbÝiûI, ϱ¢ á¡oˆÚÆ£“¦LY¡ìgìÁå¾$ÁZÖI/Xýü‹X,Z±¢ð1¯ @ÛÏé @údqakéRðÕ¯éBž‹¦´mÞÓ§5¢îÚO[à6nÖì¶gâIÈ„L»ÿºú"T.ã4ÄO×g•?vóïdM^¸,Î+,Y˱RážËœÀš04¾ß.’%ùht¶“góºBcAP©ãdNgõ?{ä*@_£…ýÃÜèbegùô}Å P×)¡üÝ1zÏ3¼ÁþN ›ÍÍH»[Ñ(j å‘û°¬M2'³@Öñƒ¦8ý æŽÉfŠ›^³ö4­(vÆ÷T]ëòàk˜ñäXÿ"`VeLŒwkj ,å# §¾Ù ïDWÞငþÈaRí €BüjKH3i÷\a pg¤?s„-÷Ûþÿ® ̗ܺӗiáOn²›×Vú$7ïîéä´rF0½'öOŸÖ§É3z©7`J.0™[¼+²| wg„@,Kxoû+1Maú±üaÝg­ F–rxŸ¸Væ¥%ÀYiÉ3Ú|“À´W5¶@ƒ½‰¦sT]¡—Ñ páŒA„t€ïù¾x@óâõbhÓÂ@}?”—€u0ÁX›Îé`ms´îüXpÁ”úVƒÐÙ申º4e(¯KÕ¯!Añ…ð2kæ8ËwôcðŃŠDXgÝc|úuDàœˆv~ã«´»e—Ö‰. Äœ»NãÄl®‡õ£|\ŸÉ‡ùá†>™—+Þxÿ‡5ÿŸ]£ež¦>6Ìê>dE:Zû+/wÏ&Tô¹ñÿSåå¼cþ¼~|fm\úß8KôáJJ_³^¤ÿûœ{€0Ö¦f ÂjÛþÓöZGÿå”KاZÈÛ‹ñ@¿1nV„Œ¶isÆ{ȪßêŽK:q€Sq^QN1wûwFWö¤·j­hú±úÒæ¤›¦é?sž³­(kÊ%ÂQü+û^'}ÛD§ûÀ»¢èQxŽ‘B¹iúøOÄ{f=£n¡~Ä3·­å5 ¹xmt£DÅR˜5ìý¡ò-ýyd¿|SíÓ—‡4ûîõ’ÃbŠR¿Yß±`'¶0ïÝXk˜‹«éG(£Æ÷ŸÍ‰Ùz6yš”ï×ë]©˜óë³wßn-Ú-í…¡EÓš´ƒëšîÔaâp¥Ëô Cýwî=qæž'FÒža™qŽ6D»ïz¥óWº`©ü®´·ýn¶öT&àL£ òµ¥‰ôÄ$CPhúÇ/K‹ ˜»iÙ7Ë¾Âø pš%D›òlÛ¤ïšAŬ>ºæá‡U}†pj‚¶¥¡^XDU-¬–SW£¡íµÂ|/©_ÚÒ¸¾ée-~Ì75–‡ ²¾õ>µ°úòÀ>êÅhî¡“€±”ËëÇyCIA :'Bvi¸D‘ƒµŽ=¿\ùa9ì‚R£Ù{¬‰9-AÁ¾ˆ/y«ä·~¾V?ÿ9÷†x˜°•ëßÉÓ3Cô‡§uFÐ Ò±M;–gü,oê èËzjÏÚ\/P~—€é•Ø®Ûe¶Éo=ÒÊ5X¡%Nš\ZÄ•óÁ@ÌÏI) ÏøJ·ÎÍ_–ë+áÆhk¾±ý…qjJ êNß“_iÁ¶¿ðzúËmp0<*q‰×ÿ2Å~˜â¨ÁÚ¡…Òð:6,__ø€±p[cÙUZCã"JrË@Ñ€=<]¾¨¶sÛ Ï®¾AÇï8ýÚM¿>ýæÆÃ +NhÜG]X'sŒÌ‚eÁsìûí¸ÆÏ×­ôy{Ásr8[Î7ãs”V‡7ï•—>'ÏŒ4ðt1|¡.ˆõï0(ø‘†á²ê—A ùê‰u»*KËš0aZƒ‚eaí»Ý7Œ?øß`ºLö¾Íã{éÚã­wË ì½Od Úðb-0Q³›|—ø¾9s£ðœt¹i°@ÊM»“Óå‚ç1FÌN‘í³’‚·ÅAÍ4ìXß$™NNíqÌçæE¼Á˜ HÜB7@ñ&Í­ÝîwüGŽu–al”þû¡ï¬Mº¬½ü»Í—½vsÛM‡»L.Ó<‰—»©½ ±b Õ‰5ÅÖe˜kFK‡õ B;qíYî«$„¬ôŠ-I:ÀægŒeyô·{·ÏBÿgZí¼rTFšžSŠ÷(’]å#ëj àHå‰ÅÓ—ÔŽ1ЀÜ8ŽÍÚ3nétºèíÆ×Á v:¨±›Úy˜÷XÄ!¸"ˆÒ'(W±ú%äÈÅO¢ˆ|xîøÓo¾BiN+Ò–ÑÆØ«ñŽòËnÆ>v 0|ËYU¥²j‰ý5yŠÖÇUêÇX)ßMé¨ 8œÔƒm¹]7¢ÛøÅJ¹á¶Ÿ· ¾µ{SÚê{ÜÜëJÇjšy9{Ï—ÐW”ájFí¼lYYë8èÏ•j_)/Í,ø±xb²O5¯ø÷~:¬àQ 6É\þwÅ=¡6ߦ9x®W—¦ò‰ç[_—‚e¤æá€ïPæ¸ò?k%ÊPwÝc­AaÜg?ê€U· ÷Q/ÖöÙŠÂjrÉ6x¸eû7õÏî|¢(ßbº{0—\Oˆ>úwç!kßÍ«µöi?ïqNÃÛ;Š÷:£Õgaëž×‡,Rgà`ˆEòð¿…‘FX÷Ÿ‡îYàÑÆ‡Þµ}ðÚö›PúÜÍ ¨šcA\”3ù…^è ÑÜ×34¥&¶ÉÀ¬Ðp·i:p¢MÞ_MïÙ}#h‡PHµ©ç^Ú~æïÎjÇnî÷Ùu”E\vÚÈÜÈýæ;u8ëªo×Ͱæoe‘À0€Ëª½'>jwèÜuÜÊ©]'O sºö~Áö²üر\+îÕ_?uÔgõ¿º'²×°§:®S³ö±|Xƒ‰ƒh÷þP P?ÝcLh @´ï x$yzÌûQJ‹ýÓøÌh¹–_ÎaÎ @?²ÏÒMyÀô÷"c™i iÀW‡MÒ`@-àÀ£K/À.VÓf5„‡®÷€“ì_X8bÑI°$4÷ôTÜyÞ]5‘ÒbòÜJynÙüGb5k(”"ïð»n0„É ã×ѽo3ÖÝï°fü˜xÒüï7ÃíŽ{ôÑ_×»3ÃïÕn¹ügü„•3²œ™\2ý7²Ç«Ìëá8<1—c%ke$ÃëÅÀñ4O%¥†B¥Œ6~¹VË?v½¿;Ö1Æx,Mò9 ïæ5ú¶\›Qz ûÉB¯¨ã«»ÑÙµ~;ì;Ö³!¬áP~ÖÆÂø>š OÒón¼Þu‹yí›/ˆúˆzy¢¹#¥- >ƒïÉ K  •‘¿¿wkƒ¾ë1t~¬w(ãà†.+;”}ßsÒúдÀx y>e·EC¹ðxϽ¯ ˆ°µÇý0•~pf÷}ùŸ±êQÕ2jߌU*ŠÆ÷Ò™o6WÈA+|o­¬Öý }`€0>ÌYBôI3û&{DŸyæä…2 þ;´–ä|KƒÅ'cÐ_·‰¥ŒK?ßî ÐÈz³¶sx寷ÓXÐx 4—¥Ö&ðŠGEåß;Ø¿ðD)©–_s£Uµv=G´Ÿ]ÐßÙÚ?P&ã#W1»Ìv@¡ÊØÄkªíÁˤ“²jü|\sÍçsú¤Oê}æÊ Å­ï½Jç?cï|—Ö±®Ø‘ŸßÞ}e?­Ü >BOàð6–·.#cê[¤„ÊXæ3êãoŽmÊ7Â6ß­=‚ šœ¶ßÆÒ³#T±Ùá(±´þsctü³ÁÎ.Ü@q—o½‡Øîÿs³NÀ¼X÷¥5î{CS޳ñ¿.ÁæñÏ-}Ü¡­ámö\Z9ìË›j¿}°Œ¥Ò¨XoÙoS Õbå•c!—±aኃ ²Áê2ï?È’”ý†Ã}‡š]5ç w¬ø(@'VBDÙ8flcÉ‹».Œ¡1 ¤7€:”/Tn|\€Q>¶ôŒ[ê.`Ê3?¾`ëÍ Ë2~Üì[Ëý¢dÀ»*d)h/²ØõÂq¿·ÿ¡}¯$:­Ç ‚èQŸ™^²ï}:höfsú7_/Aõ5ó{µ»•Ï~Éš‚wßü]QFqÚñÐO[¸£\ˆÍ[¾aøFð´„"óA¿_BÆ5X¦« øgè1Þ©¬ç¹²Šün `žµ»µ—+ãœ>c|5}oËamH8"ÚùEb°‚öKŒ`1`Oûs¨ÜT†«‚Vú+O¡Y|w=oªÇ2Þcy½H»¾hD©ð»jGl÷•*x†&ù4ö=ßÒ~5Æbiü硹ê§ézÏz†‚åVsò*Ø÷‡ÛÊ»õÍ4‹Nø3ž^È)”SXÁãIЇe}-*ÓM‡¥ïî}©ã!•4V®ªÔ×­ûZüÇBOý¶G€,CIDAT…Q«Òöc<~µÏªéVÿ޵À&, Ï-<ÃVMø]5a©òqw7F!–η(‰¥sŸ3ù+L’·@¸iÛü‚QeÓƒ!áôÓËúbi—›b¾´Ú6µÉ­|Ü`p#ôã7 º¸[pmÒÎ#Ä ÀY¹MW×r­)íN~ îžÔÚ¦.´' Að7T‡š HÃpac!.°\J 졼Öñ‚Ÿë¼Ž4®Š¦EÖ”UѼ›ËÅ»€ˆƒÃü5›z³nc9h±(ñD@ðF÷×”…æÄ(=ì›ví'oò„ìõ{ž7Ö¬0—Ø$æï»þ§¶i›G(È›xËà‹Ó~ûšƒŽ”Ó…o2úr¯¸¹³¶ÏOBnLžm?3wM,?RH=d|ÞôGS] ´FBž?Rþs —=ÅÜ®q›'¬„ûVHÐHhBuÁöòŒŸŸÞ)ᥠߦø ¬­úòbÃ5’ÃycýÊ<¨l¶Ëm«nÿ7§7“ÕñgµÌ\Š'ÏÒ8xz¥ü¼ýõÄßí^ó—CÞÆ-BZÂ_ÃWÖHz-COÝQ )`»›šYÝXèî{÷?1ÏáqÚÄ>w¿oþ/c µÄb°Ú6(+Ø;ÑStÑ^ðtÖn.ÈÖ¶¿Q5Y$#x[¿2öKã»yÚ„¹ Hœ¢}ïX·àµca–EÖ˳5<5/xèŒ'¤)œSuF&LyA¦¾íû}æ+ñ(Ãóé2¥Üž\®9üˆµ—iºY࿪k÷X`ƒ»4 úv¸gsEñpeÔÛ¢ÿ²~Zm~J°Ýû/«ÏúˆŸÞ¸r;ìÐúƒ¿¾Ë®ƒB.§_QÖyVÇ}v^=¯‹IGK˜CñW ÐËIï¦A¨òµ¸îûeþgÓq—TÙ,‚mÃd œ¤òìòŽL?aíÚæû6 1üüü|øÙsÜ'ím{–sýˆ˜£hŒ—S/\ÎÜrþBt¸÷MXú wŸïÔÿÔ‡Sl»ÐõPÌ0”_Î&o`¯û=n«0äî3þ‡¬°ü4{÷õõxÝÚ(‡@î” tÝÚoz,Êmññƒ¥#5Àó“®‹€eþ²Ôñ¯¹:µ°æž,a穵9ß©®›Wo[žŒ ÷üNùhÍ)¿}ÄÆ+\LíYÞ•8©® g:&O,3€³]sb _(miyqç`>ÁôòrÒåì䃗}°˜5~î.~‚úñWÕîç6®ý'Μž3~ô”ÃKt}úPž3^C Sé|Rq ßhíVJÞ¼Ky£`ù @Œâ#—¤L”ª/ÒüòéöïQ ¼]uq÷axÉ&ï¸P½Æ9¤‰2P8ügñ‚CĤÝBåûuúþ6òBxÅø…åø\ ‚÷mö8+iS[m]'aõµ~‚ßdýD™ª}D[‡¡Pzž!SªÆÞùœ1ná=P, éZ‡b)¯b‘|hÜâÙK:å;~¸Ö…wûVeS&ðk¥˜ß[uzdAû†…RèÔ>Ûu\Õ·(™P~Í×òù^e4=Caqñc÷>.P aRσo1ò ~§~m¼k¬ÜQÃlµ·ŠB‡4ñRó²ÃuÚ{?p»ŒÏ‘¢øzñT]Œ ¦mÀ¹ ¯Uò â·GuÜ/ˆ¥Êe“çi?}µhÉ݃WÛös¾aµtt\†'Úñ`–c„§ÏÅK×öyk϶y/š·¨PŒrBñ_ÛÒŠK€0lÒ¸Ýs¨‹5„Ÿ?@ r5¹LôïuòÇ¢$FçœØkoc‹fÛ·$Àʈx-ââ·Énº‡1O¹yýö'Y?Æ {Í4vq7÷• Cæ¶öEZxÃäpá¶K?<tºJŸŽØ=Ê Âc4ð]€p®2¡PB(`iç"6Fsì9ô*\æú®2cô¸Ï Ìœ=Ä-âRæz‡I1ª ÌòJЀ2âŸd3 Ç1¾‹Ö¨8pI?ÿ¶ÖÕE׺bi½ð×Û|5{µk¤ûÆžc$Axî™Ä:&¶½çŠÈ¿¨=ºñœ×·Þ£yÿï•gÉCTç[Tæ—Þàÿâ dz+õª¦Kñ7²pèݳîÚ…×cÌy¾ÿ3~>,™2ºno¼hzíÖÕ’m+ÖPä8ÂnäÈYì¾—*r¬gµ!À<ñè6Á6G®Ä;õ‰-0ÖtóròûüÆÈ£E×ÎY}±Ôq.Å÷æÛÔ¾çû}ýèõÇF·{”ËžÜuzÇ‚|n­î¬ó„f á)ìÃx µ÷¤[Yÿ ˜Û¹c¦Û q»»x̤[vΟѰoBÚÞ­ë5u¸I.Í1Ë,l°Är㘅@ÝÜvKYÃäÒºŠthžMûÀè2Tm-´WQ‡¥— À2¹¤¶y´¡K ¢ö7EC›üöÒ¦ÖöÛ«¿.ÖÆõý¿P¿¥ÛY‚Y!”­‘+Õ‚À/‡‰¥ëœj÷½?F4— ®£(wü:°¶å†$ãänÜÆ¨¸«¯ÞθQÑ:yš4û/¯•?’ÕÏœ¦º¥æüÝPm(à0&pl½[óE ƒÀ{@V€Ä¨`øÆÍK&Bb]ºÏãÿ-] ¢±4Ì_/Ý6ª]›óÓ{Úô7ùöôšÉƒ ^„ý%¬¨Ä° óœ²upÈéã{ß0FñdòÃ1ÀçsÜ?x‰ø¨”a@•ÛþI¼#Ö0ϱ–y–@«·ÞZhõÏÊ/T`ñÅwf­O„Û¹{6t¸s·¼”uœK—ûŸ~çP6—_ánâÄRuÓ.òå,!Hrò@™N<Ç…ÁC#Ö µÍ`?ÖÈâp7•1y¦~OY¨,€åU/ë ™ÁÇÆ¯lƒ|ÇFËÃe|U–ÒXæ2¶çs´ÚÐ’è',/QÀíX®úJ^Þ¡ŒÌSVUË.ÆÀù謢í˜7(-ðÌlœ¨ŒjÞ·’"îOµ§½Â{¾Žç¯ ¿#¯É³åEò9ñawsÒÜA÷ËO_­×6ýµú¶ž S±ño§ÿJñ`OÜÍÃú…Ìî¯Û>mð*„µ‰2ùéï釭ßQܱèB+ÂãØw(Ù#b€—¯ø³ïz¹'ý&ßטxØŒž^òí¡owó~î»>¬wx#Ãô÷"ù~í-ãÛLn¼JãêQ5ÚàgÙ¿ý2lÞÏï¿?2î%ÏêÌ‚ÑÖ»Ô>â;ì[¼Ã–[~ñÁѹwAÅÜo†JG „Ö¡ò_u¾}€Ä‹Ôx†®Ò”.1CDÖTvì}gfÔ[’ŒÏvZbÌubô/ï9@ânt•9SëÃð®ÐËë§Å×ûÖ´n¼PmX·¼k7§ø~ÌÔª,¯j/š¡Јؾ©r°˜ &% @¬ø±`CQÆ} Î(¼G=*Ю­{dªü>Þ確&ËŠá›´ãl4¾ÿôéã;ê[·‰‘œceÌ÷3 (P>ÀY*Vlæ@>똀 /#Æ ôÝT}€ïð›ްNçð§rl• ÆÆ T3ž«¾›Å•Õ{ò¦O|6¡¨ü|‰WŠW{3ùaÞ†|F /#ÞÅܱml0Þ¬X>Zræ,4Oüx¤óò«k9ÖÞ„ÒrÏ<À‚™2< |‹}?Ôs,‡j$”o¾YàÙËœ¶¯Ö½wÚ7ߪò®Š–‡0×Cì Æî'ÕÇ9F§im¾g´¬¶uc|3^}Kȶùä¤@¹A[„ÒœY|ÝÐ{žá2 °ÜÅu–ï÷–7àÊÇœP)öMî•v¦å¼t €âä²ð;Ò\J†öÖãÁòB4¬õ3éyŠ$¿¾ŒñþBÒ + XC‰CÍØnò¼€&æaé|úú»?Uã Y‡Þ—â 'Ïò1&ãpîUü—×ó'—¨N·[BûU×öÝR8n䦄AB /òò­ÒØææ¸Èªø‰6´š¶0š:¯q<å(„P(¡ )P¨Gé¦h4‹'PPÁúy QN,Ï>*LìN±ø$þÛ¢ÀeŸV»0ÉæëŸÔ m@äpðš­pÚî #šaBN¸ù® Éêkk›‡î·©ÿ†v¸7¥Òí½ ÷'ÀŒ[¬ÖN“K%¾t¯m5¦km³âgÄå[÷ƒÿpá·C¥Ö­ý*û¡::6ƒÁätt|,mXwØcÜz¹1F‰Ûˆ%¬ÅuÓùÿ]°Ê×ß}i†€ö9­ÏßS(9ø+ð p¥œƒ~¹Ä$.1@†zÇ÷X¥†¬á`âHŸ_Þ/Ë¿G°Ze¬@öÞÊxéqîÓ†€5¥uU8ŒíËIèœ:=+¢À쥭¿¤¾µð<ÃÜ,jQFÌÜ+}øå`˨nßÚ;€>„rÓ…¹BZܘ & í½hÆ)ÖÁn^£Ñ™«xÏÂë3@8ÖÒ„¢¨æNßwæ5 þâÖA€2Ë¡¹, *<yÏš†lÌ{Ž4( B 'g‘N ZŸu›% s!²ëA\ŒM iÒD`(s—°,©0~©|Ph1/¾¥ñ…ìk(Sù!Ëú–ý+­'ûåÑ_Ó¾ùð¬:-Bkà·ö¼8Û °g=ŒAö3öW‘¾H[íöoñæÂ Ã…T]ÙRØXêÛŽïÊÈ«C+Ú1³…¿ëO{W_hVWU¯ZÆ]2³äG(BêÚ)`8˜§Ü‡*@/q´äÞîÇï ~»à"¹È)Íœ™ سé®Ò„ø}þ‰çC´çPy—4fµ3T™{ùÖ׿ÝÚ&€7먔bÝ(-_÷¨¿ÔZ™÷pæýš9ä åô'ñ=a|TS‡1–‚ÿ ¶;¨ÚXA ›«x|¿éè˜ohï9¹P®¶A·åå_¹ýçܳæ5ÅÚ'~,.ö¾(qâ}KÓPëö KW¬Gû¥K–ŽjÏÙý¶Þ«û8Ÿö ¦Wý¥©[>¡ ¿ÛæXbaÅk–Äš`:¸ÍïÎÊvè à•è°j$kèð`”:Ä`ÆbØ?øð±€¢«˜ÈÐÿxŸg¢ö+öèVÅüñÌÌñ§ˆn?–q¨Îö,5ß,MÛ+BO(äAÛ|ªéÏPý€«ŽËþÑwrfá;šÊÇÖ¤eÍ‚ÇóC4å“zÏzŒ|c!8šúP‹±éæ ÄXa]åLŒ6À –Í(fÜüøÏœi:@˜¹Cø–˜UsñÏT·§ õ||“šãF'à;û` ·4\?£þÂû`H/J{_±äÒ÷¿Ú§yß, AàɵZƒèsÛ!èX·<Áv~Rc«]œµÀ!»™ß±¦ÙAª„èÀ“:¥ËÍ·¿t ££ƒa»ÄdÏ¥ÀÞ03½y¶ct´ùïDw< P.}}¤càÜ„ÏÑG»)À\Îà#8'$´¶[º#é ÿÁaì(s–Uo\ÛQ¤ìËôœùŸÓv¬5(‡¾á-rò¨§a¯¯óˆõtë9ßwò:!„ÃvÈ—Gx |öÖ€p®Fë°†u&§yš¦†Äþçæ¾Jëã\:×5]È¿­99 ¡{¡4f½ÕƲ Á°ÒéûЖ­-{õÍ:ômÕÖªœ¶ýþä„Ù‰àÇ`(eeí×7G†Í?×:ÅÏ7veÓsµúqÇA&€rX–†úŠùené¡÷GÒ3,·˜¿(6†Ví–ö_»zL.,C‚p]Ó5´'ºâƒ¸§Š´ *×½úÍÜú¤c/¯tcbýk+¬XpŒ½Ï}ŽꓵHZú6@§}»be{;h —Ä̬±êõc rõ÷”‡P/·üðÄMç3àæ–Ú”Ï*Þ# cÝ c–Tˤ ¦ ‡ôÖ,CÑ…eÙùLx¬ôA_“% VK½zhë>ê°Ì<<–YÞº•  `‰Õ ‘j9c%V:Á(¦W!÷÷T9äé¿NNšnõi ¨lß“˧Ço^3}—莲V>n¼ÄVg]Çr3ð¿ýôN7¹Qa,N+Öh¬²c!¸NÅÂA É똃!¼pk2ÇpÓøJÒïÒ:Ñ©ã#bè ”ß¸2û»[.î»Aß¼Rß ÌSžþ~èÒ€2ÛUD“k¼à¼Ÿ/Pù)aZ&—Mïµõeœð«{S:’ P—Fö¹xÊœÏhÿÇP(•Ï‹ŽÛ†iqÛQÿÏjt=wÛ¤Ëx-bSGã£jŽ8àÌ£m¤ö¨?nk[@ßokÎÇÛ¶ù­2=ë[0DÆøAêƒîa.Ï„.YvÝððû®@¿,ðT‚÷Yû~o:Ýs$ÕÙu…oÀû¦ÉpaÙtí•—ÇSÀÛ÷wý§Å3^®uIW­yåOžã»9÷ö|ç\mÿsøä|âaBúT "wu-æQˆ±7YóûÎm»Ø˜"¬|RXÁ€^nmûÁNÜEÛÛd±â–Óæ’AtݦRò†žÔ©öIJ͵‹ÑŠäÛb~]áÕOËáCÄ+ôŸ¯Ã=ÂHðjtº€Iˆ‘´tm®¹c-5†Û”J ãŠ+0›Nèý:=C‰këDë‘D qкZt6žMíˆë7¡`xÕ˜§AÓ·¯ÓÚ›3Š?¬€SqrCõ£ÜÒë@ç¹ÃÜf2E!nL„Èi¢=õÞ<&dtägߥò¶w¬› ¡ò´ô;òÊéö/ϪƒYB¿v|¼˜íçÉ’ñcò:¡p•¬*´,4F½m`doÐZ4$(,T¯•KþÑ…uŠ wîL7n]ð;ð<„áAÁ·¶¤"œBÈ› «Ïwj¯f.[úðõêÇ)é²rnìß3¦Wý…éŸo<ÌY·‡‰ï㯠«Áñc2è­÷Is3¾´/?9£¹†2¢éœy×  zT6üaTñ¯³#JzqÍͨ_C ÜÖ¦Œè#_x2bj¥Ôi õ Úg7¶vˆVx}€Ì˜²¹Ì–…—¼NÚx­æçYE( P~<£ì?ØxÔôÁGYÂy:T¾àz(óþAÞrŽ[µ#ÐÈE@Ÿù?Ï VÜšnÀç>,Wû¬×*òºNº£ hÝŽÄ Ë=˜úÅ@ž÷íú³Šöl*«·X€P,ÌXúØs,¶°ß'ƒOY€=ä  øwZ+Rnx€£„ôEˆðé#0ãÄ™‡;Ü @oa‘wV´¯bm¿÷¼º§¬K{°¶ ¼¬zÍ?Stü£æf‹âÚîO0@ÒÄŠþš\¤q_u•G Gx†öPŸÀnÐ ÇáX\Cé»>ó­beâ0 #è¶oÌõŽõ Ы2âZ"xaMìÓöT­y‹zPÿ­›¸OGÎ= ).Œ9i£i´Ç-,an3ª·Gô;µg›w(!Ê8ͧª¼óŠ8ž¾3íñ=K¦{t»Vù–4ÈþD±"‚=Èç쵌g~v8!‡HqÏ80ëÀ¡”Óávºéô¶²žþ¹ñŸiKÍÁ}ïp¬p-ÛvýÓ#kÌAö ½Xjo½[ïçk ФU)ÅYO8ƒó'*tZŸL.•…ô›$€ÓÃ]å|Ky é0zWÖŸR®k~ìXYÛÚß5xC¼Q,®xn>qûU\v…gd^D‰¶Ê¥¡¯t6ÄBÙôUF*Ÿ”Å/ýÌ/õ}ÿïdm·ÿϤT!tOxoÁ½žÈcïwÊsö<ˆ“ºÓLxµY¤­vßs)EÇ÷8‚êžëܯðëH6è×ðë ¶`¼›ÿ®·ûñ£´þ\¡1vh{œ¦ì<ý?eíǼ+òü,ò™cP±óR_Špšçþ¹»Ûh&d6‹ÿ¢ACœ®¾Ú<•Z‰>ÒTY±w,$M‚^ëRí+ŸÉ¹(ã÷RkbJÂävDS ’ûÎãX]üçЕ²ôÓÇî±`uçXÌéØ÷Ùϱ\_°”±ŸMS‹±´—guÝÆ¢‹}w쬪º*ÚP¤ oµ¤Ã¤ö]¯q¯X©ÛãØyô øp‚â‡ùÀo ր›|XJÌsn‡á˜åiyûW7Ô k-J^wû hà /ÿ;÷žE«°06í(©B1ÉÄZ&$V‚1á–þêl[ *¬¡k7aÛçarâïZz‡3DëUýä¶Gëÿ/ðö^)×o>ýÔþë§7ŸüÂbù©žÐ@{ûÀ}ж3GŠ[9y޾]°>Ûå»eaÍK›š+5ëc•ñ'9¤ØM¿²ÿª6¾w„]XYêç?s=p†V>¸e3Mã·z8—Ö˜­÷H ú¥í9ÒÕ5Þ-7ï¿€Y–æÄ˜æÌ÷LŠPþ„Ïa­YXñ’ÑŸ¡ò«Ï%Ú¶}»¡8í+f•Î9-CYn!ç嬳1º–õœ½/f½,Ö®œñý5ŽïË´&Q…LæQ2ìäÙâ•>8-‹õ‹”[ò¦¡¿=¯¾Åò ø/düp›…¿‚ŽÜ<[i5„9¬~½Bà¥Húy÷qÍÁ}äÕGX€ce‡•_,?b}±@†Þs2ÀƒÅà ¥qŸ¡åöÇýÕª¸yìêÿG}Ví<·¾¢®¹ÀÜeïå^¢í2y‚ÆC "ºiCa Ü4¡ÿô=ù˵·‡Þç×ýˆCýp¡”rÁo›åß—|  ñocÊ£ àüû“¢(Ã>& {(«°ÇjìpÀ2W£©~=C.òŽnòcµóCjéPZýw͉¿\Ï«\‡PŠ`ùH½¯ÊøžqF¨ ólˆåÙå9k ±­}cs¬KÞ{ß”}½×Õv`ö÷ñÆ6R¨¤26èê@äXÂ!ßFé.b)?"þ¾X·«íÍ+’6<·Ë£Â/h D¡ßº_"´5Õ…}Œ©X—×”˜Ežù÷ý¡öòW Ú†Mtf½'½CßÜ`½2t9»!bÒ¥„ Åë8¬pŒàu[ÈÒpqÚ›÷—ÅÊÐÚ³öOHÎUt,V_Y¨íÿˆæå¡ÚÜDae`kÛ2pÇêÂ@£àAÇŒX»V.´YÈrv[K÷¥7‚ eÜ H®}WNU³µ²íJÌHw?8€Kü¾ßWž¥E –·(ž°â3¢}¼Ž§ñö˜â"À389eómbú_3kÿ{€ñ/å§õï CÅfäßWÀï5j÷ãz‹íξ üVSâcaºñ²h]ºiOFK*ð&ÏšÞIñ\cÖ ìC„öðy!Úá…z²º4àEÌëì ÒÛs÷ °Š‡ }I~Œ€ÏÙ<’ÅùhÿŸªÏeY¤w¹?<ƒˆG›šSðŸRY'Od=9v¹Q@t€òü¦íðdîØwéF±Ëže›„<#v?iî±è¾?~ì¶” w.­‹ÆçÎÛ¹xÌß«]8Ö¢}Ýúôõå¼kX¨Ÿ÷=ŠÀÇØzu?}ÑÊeFèyõÙxzêø^Jˆ™!«ïjóz»ÏC¿x¡àÃPå–óg/:–c‚¹Ê¾™3,ø( ÿÂX.c†‡évë°sÿ÷öfç¶Å÷3¡PÈspµt‚Éä­«¥3w²ïBûÜoúH?”â9F㻊žS–JSõÚË£÷ðkȘÄ=ì–4G žs°±Ô\‰•%±â*†(üjÀ™C*_û…a‚Bê;{‡Àk̳=ëëÊÁu3áCíÙW¾]ó¡ ™¿³™¸®›·0b~9KÕ‰{syõÓqË!–â~Þ0©hcà3BWΩñ¡öñËʽÇ5!ÔMe´;çpE°vÓ¤þÓWXj¤Ò„Þ!Ä W”ÍtÑ]!ºíÙ¢n`–OìJ?%¯cù¬ê9Ì“;‡WEG§röÿ‰ÆtwkÕNåz}z¤ÆŠgÿc½8"¤Y².Ú¦x ±VbK\Öæ™Õéè@¼44¯B\±9- se´c[÷çß9ëðÆUé^Y¸HcÕg<@Ð7ΗG<Âroس/J¦k¹©ïSïw3ÍÚÍ;¨kü)/Wï\~}Í]¼N{y¶oCyúMðð:a¯¿;®íÛ|oœÉmÆ>çòîùmq¶Öå»÷3O±È&vuÆdñØ-S´ºîfœß8ÝÊk›ÎA(Xvº±ðÐN»n‰mË܉é±xqÛª Ê¢NÔ‰sCEÈpÃn4YÉr»¹¶m3Q_0l›‡¥Ïf#sÐa9ã7fpÑwGn«x@еúç^a¼‰‰@l@G鰶ÊMK€ºûÞ\²‡¢Û-kyÿeõ™°,ì›)ß »ï2˯ý˜^¬ÖY—æÜo›ÒFÁâ…â§ûÄJÝ|G„?`ü¹Ïcÿ‹X/ÞÊ[ Q>ë°æ'­sÔg(O9”ëïX9}>gL†€l¿  û~ Åó‹«€À––Ð59gzìæ[ôìôr®§M{ã{òL½#¬‚÷|ï~Ö&Œ]Î$0Ï€ö&>vÝÛÞ¯‹˜—[ú±`çÀÔ1 óטYgÞ:9wzñ1ÿS2Ä“gmݦ\¬®þÂj¸a„ÜïáMÝÔu§÷[Q?âGóµÛ“ªí†¢§ˆç.` Íü-ܺþ#øøÉÉÅz{d¬+U–ûEívÌ·µ~ ð¥Ù{¾ÛF|‡ŠN.Ü´§ÖƒºaÝ‘1nÅ»£ÜÞz¯ú³%Œ Š3Úyšå¶õÿY+j:‰dÉ%IEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/idv.jpg000066400000000000000000002715401510711153200241640ustar00rootroot00000000000000ÿØÿàJFIFHHÿá‚ExifII*JR(1 Z2fHHGIMP 2.9.22016:03:10 12:21:10ÿí:Photoshop 3.08BIM720160310< 122110-1221ÿÛC   %# , #&')*)-0-(0%()(ÿÛC   (((((((((((((((((((((((((((((((((((((((((((((((((((ÿÂíÿÄÿÄÿÚ ù4y©ú#æÍeG¤xç y冃¦ÓÏ3±ä F²'‚zg˜\}`*<óêOÚ€ü¯Ž‰ˆ˜¶&e‘9æ1Ú 9ÃÝæ¦â¸õO,°‰3Ën*Ä ÅÆÔ+(=í 4¼´¼ó æaŒôÌ„H›Î2”¡å“&RiQÞ®IZ¢ˆ˜¢è¶Ó4À8yü=Ù“ ùÃiˆ°=ciâ<@¬y%‡´^HÌVb=ƒ)qäšÍç˜y§²ZyÆ’ÃaãÏhòöЉ0šL™€¼¸ÄVp—oF{õì=òððÍ ¨Âm1ÓCYæž‘æ=3Ì/="¢HóÏPA”ôΞYq¸´È Aqy暀óË Œç¬ ŠHLF²£ooEWêè˸謀ÃÑ// âNÃÂ6˜í L„œ{GŠh=³AÓÃ>¬ÎPxgºn<³Ô.<“Ñ0æžá äŸLd),(>œðÌæB£Û XyîÞŠ¯Ô°æùó«ôâ´w;ËÊíV@;F<¼¼3Òy&Ó= PzçΞ©á=£Å/=â‚$Œ§¢f8f7–AÃÕ59 áqˆÔ^g&^Re$D°ÞDó '¼ZRT]ÛÑžý@vñŒ{–œéÇXòõåyy}ªÈ;F<¼¼3à§h l3…&¢£¥fó A…e=¾ÞНÔÞ#Ó:qæÙ¤;•ååö« aèÇ—w ùÉô  æƒÉ7žAÂGÐ8јòLf öûz*¿PhçN<éÇ›fåyy}ªÈÃÑ/ÍáœiÊÆã Hâ\R{}½_¨¶Žteœy¾Içyy}ŠX;F<¸pÎTÞ^TtñM泆SÅ>„Üe:h8`F²gÌ=¾ÞНÔ±<êÆ=Xs|’ÚZ^Wj–vŒyy¸gxi(1š &#Ù3!¬Ög.- d5•ŸJ|Ð1L@öûz*¿P´‡«Â¡%ev–—•Ú¥€ì=òñðÎÔÐD¼ó\ÈD´¼éQòà‘¨¬¬¬™qQ"$O4öŒäÈ<ãÕ).0™œÔ|ùí<ã9è<òÒóQ匧š=¾ÞНÔ!êqs|À$Öœ˜îvD„H@;KKÊíRÀ°ôcË ÚŸ ôŽdN–y´ÄyF²£a¨ô †cô‡ ̧ϞÉÓÑ8tñÏ0ö'’:@ùóèŠO Ök6¾`=£æÏ{}½_¨ƒ§(z|`$„ÂÎ^‰†v'°D ­H;KKÊíRÀ‡£^Æœ Á4À æƒ8Ð àỲŠ{}½_¨#ëðsJ€&¼sZ&Q$±( ‰í,ÎÉ4«;3°í-/+µK°ôcËòxfÄù&ÓìOD´ùCÑ=“IçŸJ|áõGæÆƒèÊ3aˆÔyEÆ"ãÃ>̤™°¤éçž{}½_¨‘ìç0$˜D…ëͳIz¢Vªa5JXÇ-2ΨC{’­”²²½{ŽŒ®€í-/3±ÒØz1åù¼3™8€-.&pÈXHÒf54”™ e¦RâŠæÂF#y˜™a2%{}½_¨«_‡–€KTã›ä½{JÙÍHtXrħÏrR;OjÒc¬òÒ—&ܼ)§qѕլÏÌíat€°ôcËÏÃ=OÏLG¬h.4Ÿ6o8^t™ôO0Úg3ž‘IàŸ@i<óÔ0¹æžqæ€íöôU~ „}.Nub - BôæÙÊ™Ë8ž·‚‘ÖaÓni=Ê/óóîq_]á¼Ïž¼ÒB…}‡M«éÑKw+±ÑY‰a¤¸:™ØØz1åÆ|äù`ËËÊ4õÌÅ LóKÁÒ£Í1‰¬Îm*:Py o·¢«õ¹ÃÖâ´5§-ß+0Îÿ;>Ò–0!ÕiáÑoc¢;Z:̳‰asÒ¾›×Õhé0ëµ}7äʗliiy]ªXÃÑ/ ÖŸÐ$Láæž‰œÖDéQÓÒ0O£<3Ñ(8h*+-+<óÕ4è˜ŠŠŒ Ûíèªý@„=^.o˜/^tä½S­oó³³–œ´Ïž¨‰Q í,ê…œt޶æˆídGhTÑËL5š=-%Du˜t_•¿qÑ•Ñ(ÒÒò»T°ì=òòðÍéù£iˆõ͆’ÃæËŠMG²PtÌd=³Ï>|©¸´)´ÒPn<“Ê>´ó¦óaçŸ>·ÛÑUú€ƒ«(z\`Ò¼êÅjv"YEœYÏíRʲÂ; gT#¤Ï Ïž!Ñ1ÖS=¬Fò™"YE}W‡M•ˆíhtÚ®»òš²¿ke%;KKÊíRÀ‡£^©"$Ìf‚F"òãœAq„¸¼ÄÐgAœŒ Ñ<àíöôU~ &#ëpóJ€’õçW<©Ig *»ƒ;9k¦Þ:w8í\»¹Ç/2Î'ÏXënhC°¯k™Òy¬Ž^jí½}E£w T•l€í-/+µK°ôcË«†t§È>ÂydKŠŽžáIòFÓ>¨¤‰A °Üf:y'zÇ k:y¤Ïœ>˜°´ñÏD‘œòÏ`áóG®LØyåæÂ£aãÃ<€=¾ÞНÔu1íæw0­y¾Rco;HÎ'…l㬩„;Iv‘,á^Q©$«é¼u”¥…aÑhëaWmù¤òÊ»4vgnÒÊXÖÒòûÝ ‡£^^Êž˜Mæ3AÒÓ9aÐk1› ‡M%åe%fCyq”´é” õ Κ4ÚTb=3œÔX^PHáAi¤ÖyP=¾ÞНÔ/XúÜ<´–¯5££9g¼t·’“å¯s„§Ï±Òc´Ï ÷(‡jØrÒ”u˜ohÞ{mË«¾érÓW]«èÙK«=ÆêXLü¾ÖH;F<¸pÏšŸ0Ô=1`*,+ˆõŠL§’zžf=¢Ã)Q€ú²£1Ó °ùÓè L'¬g4žAçVcG”k=3aÊtÐx†·ÛÑUú‡aÌ#èr€’a­9¶kÖYÒÞ:]çg,âZxW´‹9kjË8Î,ůž×Rò)µo¥ã1 ‰DÓzBb^LÓ´CªðÚyi‡DË8Ž“›GI†ö‡E«ë½]·å5î:)eeîvŸÞ=òñpÍ òÀ5™ fóiYŒöN*4Ï ‰¼À ÊŠ`I‚“Î=Ò’ã!´ÆR{˜n)0p("Ro0ßoEWê Ò¿ @$½yÓŒ™K Ë4ùéw ;šÍœõíb|õîk9©o9yíù§±=.­çYÙ–³¬ÕzŒzåEè3iJõC¦ÕôÛµC{rÈtÙi«®Ü´Çi¯ªÕvëÊiÜ4VQ*ÈK“yyýHz1å¸g"| `´¬¬‘ ò̇ºtÈHáåžñœ‰aqâ•€o·¢«õvôøùÓ&”æÔìåo.s笱¬²ŽÒ%Eœ”Æ'Ï^ÄYËYe¢ÊM‘kék©}yk¦—¾—„ŦM2£Jö~øÓ¥a´Çiæ“_E£´Ãk%‹Gi«²ñÖÔö苳·qÑID€üÞÇ6½‡£]\3ç§Ì6˜`Âi7˜ §–c=’’òãaòg¦z…¦|ñ{}½_©¶põx€&”Z9¥e8[ÅK9),¡+9+g5îq,ÒʲÊ-嬨Ñî­´g§¥Ï¾š^Qm9Þ›×5óͦyuÏ6”˦UÚ*ÕWLâܳ–˜tZ¾»!Ífžý+êµ]wLñ2åÙÑ Â^7 ‰ì=òñpψâgœhsYÌ€¼  §˜h3€̇¦y€hÌ€Ðg\j<ðo·¢»õCÕâæÙ€a­yzµ¤«—kñRÞJö±ÜbÞJOž¨O ÏšÑǘÓöe®¬µÝŽ»ñÛfZéÏLÚgço†m)çï†-rÉ®P´Q¬Gi‡M¹(ínY«ÃkGY‡M«èšú®´Ãk0Û¼ú"@ÃIù]¼—aèÇ—Îù„¦#Ö=CYã——’>dú£i畞‘qIaæ2zæèÊDÊm>Xó´:VP|©ìOH‰¸ðÈ,->Œù2SÏ>ÌÊg7ž9ï™O0ïoE]ºCÑä’õiNi^ÎVòSµ‹9«o ;HBÎjKŸ=eGiÊ,Å· êË]øí¯-wã·¯Ë×Nš3äòºy|ÎŽ7£Ÿ¹SzfÒ”t+벥谯®ðÞaÓxo1Òjì¼w·3×¹hÊȸzeÃÑÉvŒyx¸g"|ð Éœ6Ayœ¤ÜPZpˆæR’ÓX2—‘@m*ÆP,7 †¢‚‚ÒÒe7”‚Â$Ž·vô{Yòõ’KW›g+ewŸŠVrVÎJKìGh–5•²Ê;HÙÇ:sÒú_~;oÃoGOÊm¿c‹ÈéåÁ¾8vÇØã×*oZ·˜u[’íê´7šú/fV†Ö†ó¦¾ëÜ4RÈ'åv±ÐvŒynÈŸÚb=sÑ2š“5"zÇdÌh0šOž>ˆÈTg=B眦CÖ<óaA敘¤)+ LôÌ(ÞZXy瘳í)ôd 6§7˱K¸2·–’ÊK*Ï–uîK9ë,+<¢yDé:3¾š_n;z8o§== 6¶-L²ëŸŸ¶>~üù´¥¯-u¥=–æ³-™å¦;O-5õÞ¾›rèé5vëÊkÜ4g`k2ñ½;F<¼Ü3>`ÓÑ<ãÐ)/7žAäž¡ÓÑ0(8k¨ñÈ–—8R@ØLÈTbÙ÷éOf€I­9¾KRÎzYÉIåÊ,奜•–Q,à‹9+†”öìÊýËFV!?#¹KŽÃÑ.\3â'´Äz§ `=’€i"|í’>ˆñÍ'žf=cäl‘Qy¬óÌǼyæ³!ŒÈz¥Åf"&óyQ¨Úb3l€‰å=Ÿ~”öhšSšÑµ;J_çåÜÒʲÊ-ã§`¬YËYá^Ñg5Rž£;vÖöÒú)x¢3´¤âFM3ͽ#rªû/ æ:Z;Lw˜oieÒhô´åæží9]{•Ôº’B~7¢€ì=òòðÎà´ô Iq€ :Ph3‚â †3I óPòÍŒ§¢yƢÒFCÔ3˜Ïgߥ=š/ iÎŒ»¿Î˹IJ‰a¤O Ë–1g-ùê%œYŒÊRÓ‰º—Z´iNÂú^«W6´†¨ohtÚ:O,޶޳g—š»¯_Uêì¿#VìK; îOÊíä‡aèÇ—o äOΛLG®LÔi$VL"{`ú!Ó Q¬ùcë ÉŠ FR'®d>lú#93Â=“ QèIô§˜y Úi<“Ø4Ÿ< eG y'Ò–Ÿ*{>ý)ìÐ&´ç^mã¤ùë<+g{Xîq,¢ÞJ÷8JÎJK8æ“ÜÓʲ‰•fÊÚÈš/^ÀFÑV‘ f:Í}vŽÖä’C—˜m+͆”÷Þ¾­jÎÝ­™X%ÃÓ.ŽHvŒy~ àN` DóÍæbóqˆòÇMF2ã84aô'œH(-+.0”3éiq¬qaã€ze$ gŒ d ˆžiìûô§³@Ò¼ìÁ4–Q<"îçÍYóÖxW–žÖ%ŒO𳿬«¹Tð¯4´±¬âc.ªI§¡WE’YW]ãµ¹h¡3Wm¡´Óß­]—å®ç×µ•,¬€ü®Ö:Øz1å¸g |P ‡¸dç’PzÇž{GÏ×5˜L†cy¬´ÈRh,<ƒ0{>õ)íÐ&S›æ½,䥼t–Qg%'…Vw4ùë, %,kÜãµvæ¨å絎^{X†Ó_MûœsI޳›rÄ̲ˆm4z:W×xuZ¾‹³Ó¹]•Õíf^7 ì=òópÍ ñͦ#Ö$^ O,¤õN³òû9 ;F<¼¼3>h³yˆ‚“Ð:yÇ Zg8c,.,$Pp‘a„öûz+ÓªŸ7Ì 4¯:°ê“æ¬ñ¬°‰g^Ô„²‰áYe!,bT€ªYDñ¬t;tZ:ÊGiC—šúl”:mËʱG¥¥}WFåÕ•ÀKƒ«¼[¤aèÇ—¯†kOÈLG®zÇM§Ížqï”™A¬‰í(m"}Aaâžõççà·ÛÑUú»(z|\ß0&Ó›R̰Ÿ={H•#µw(ž5Q,㵎罩.^e”*–qË£¬òò„užÂXV:Z:¹¤ÇY‡E¥œFÓWuóú›c~ó꬀Ÿ•ÜÆàÃÑ/ äOž3iç€ ŠKŠ@h:TVk8eÖd4‘:g’¢³YÂ&sÛíèªýC²‡¥ÇÎŒ€´4¯7ÆJv•–Q£ËϹÂBXÇi^õw³š±ÖVGYí ÂIvªú,;HKšOhå•õÞWf•v_‘¯i,tg`¬ÏÆô8;F<¿;†Iò ¦#Ø-&i:VL°ù#Ù8{†RÓQ¨ù£ê ‹ Åf£)QêŸ>ˆÈLñOl¸ÌPzæ3âzçËŸBVRyú1Aë˜Èž)íöôU~ }Nub$˜Z9­åÚRxÅÜo%”FÓÒYD²¯-*ÂÅ]«—-<²[–-=¤!…á¶ŒïÜìÎêYæÚ^gZ@ÃÑ.žóSç€j=Ï7”›O8òOHLE†s†³Ç>˜óNœ,(,"^`2‰<ÓÕ+*&j+&xäÏXÆX@°¸ñÏD¨™¨¬QíöôU~ £ÉΜRZ9¶m©ÖwyÙÙÏTD³w*ö…“¢6™V©X‡Uù`ä‡4”‰Ž³G£¥}Wc¯r»;+ —ŸÕÞMÀ°ôcË«†|äúÆ£Ï<‚óÛ*)(4ž©ˆñL€éH‰Ò%‡–^h.-2‚'š}1æ"\tÎTZz‡Ìž&L‘IiY"óÅ+=¾ÞНÔüÑëçH CJó|¥L¬Æ³æª#´KBYÂ\²YG/2¤pC—ró<+ËÌw˜khë0è²f¾«r4a§rº²?½KaèÇ–áœéÈ@ié0páˆØb< m1餙æ7š HçG¼yÆ P|ÙÃê ‹ ô<™iœóOdè8i*2Á´È|ñöûz*¿PÎîhös¤ªÚ¼Ò)ÚS–™ãYóVÞZËÇYÎGY–nYÍeXí\³“1ÚR§ÐÒ6²š÷)u$¸é?+·€vŒytpÎÌËš=²&c¦‚à<  &ƒÎ.).)Y f@z'œ=3̃Ûíèªý@çw4{9Ò’õÖ‹Ô/ VYRÎZö±ÉvÊÖÒÆ'c¤öª»-_Uíä¤7²±›Ç=»•Ôº v¶ïGyw;F<»xg"qšç˜Ry'Ø‚eG‚})åžA¬òM¦ }YQÓ1¤´ôÌ`ñŠG®péç>dú£I#É5—˜Ï ÷ÌçÍÑ#Ñ<£I¼ÊnçÏíèªý@éÌ#ßÊ&•D,Z90Û4JESŠö‰á^KµK(íb7šúï˹{¢93ÚY»ÙÙY2äÞ\=‰aèÇ—æðÏžEeG Md DÈ”šŠJŠ@4ËŠHš H”‘(=ò&Ò’“qA2ó!yY˜ÚPRX\L€"D‘i¬ó[·¢«õvìÂ=ü¨• ´$^9¥”Om½ZT§jåå ãR#¥¹k†vZe~ÖÊJ¶@yõ—ŸÔÎÉvŒyz¸gm36šO8ùsYQ ÚZf6•›‘>”ÎXPh<‚ãaAq”¤ð@·ÛÑUú€aÎŒûáéñó|À$U9¥U¶jÙ•Ö„JÊÕcuˆv—RQ(r¼¸:{ϯ$aèÇ—W áMådˆÐy@±A"e…'’zäË Ì@´´™ˆô‰™‹ÏŸßoEWêK“8€ÃZóZ4¢–g~ÒU± 9j¦;[+*Ê%VeÃÓÞMÀvŒyz8g~lÛíèªý@}.Ntâ/NLu*Ê¥lL¬€ãÞ\=<‰;F<¾pÏœŸ$Ûíèªý@ç£É¬@yõ—S+$ÃÑ.®ÈŸÛíèªý@ïæg8Üï.žóíÄÃÑ/ÉáŸ5 o·¢«õwóG³œv²¥—¯xº;ÇЇ£_“Ã>j@ßoEWêÜñíæ¸÷—O ’²°ôcËòxgÍHÛíèªý@víÂ=ü Üt—ÕÜïÀ°ôcËòxgÍHÛíèªý@Â=¸G¿”v–ïGywHÃÑ/ÉáŸ5 o·¢«õa¼cèòK“¢\=‰ÃÑ/ÉáŸ5 o·¢«õì9Ó”=>0ï>²àêed€vŒy~O ù©{}½_¨ì9Ó”=>1ÚZ^WyõàÃÑ/Ûâ€Ã×ËTÿÄ3 02@P!"45#$1A%F`36ÿÚåá“/j‹nÇᔊJ½„-geÿPÑ8‹¸²Z¯ò”)!¶h]LÅ^@p‹»–Ÿ³’ÙvÓ5Þç/ʃ8ü7žšÂ'5pqYŸPïÀùÁ6Åš†¡ †é¿ÿcõ¶Å‡vè‹1ÆÔƒ[<ÚU¬‰fÄB_³qfâÍÅ›‹7n,ÜY¸³qfâÍÅ›‹7n,ÜY¸³qfâÍÅ›‹7n,ÜY¸³qfâÍÅ›‹7n,ÜY¸³qfâÍÅ›‹7n,ÜY¸³qfâÍÅ›‹7n,ÜY¸³qfâÍÅ›‹7n,ÜY¸³qf¡û¼!‘ŸA Åü±§ÀÒ¬ºŽf:±¨[MêUt†¨åLÿ%‡:“Ó ¦ö…j®´KY0ß\ÓÑTÞÐMV¸Z£Q¦ûS©X×õ:â­sThu^ÅRj6PjK°ísULu^ʺö§h¿º¯eêJ¼ÿu^À×5BÿQê,k¿©o©¡ýM|ÍÚæª˜^·©œ»©‘7ú“PIÿu^ÏÆõ]ŸÝW²?©ï”õ. cýÕ{?º¯g÷Uì_õ. Âþ꽕í·\Á_xlÏèÀ²á¯ ® i±‘´³ifÒÍ¥›K6–m,ÚY´³ifÒÍ¥›K6–m,ÚY´³ifÒÍ¥›K6–m,ÚY´³ifÒÍ¥›K6–m,ÚY´³ifÒÍ¥›K6–m,ÚY´³ifÒÍ¥›K6–m,ÚY´³ifÒÍ¥›K6–m,ÚY1Û'ajM¾¬òc2©­oésöëþ.›(’âºjÍyÔ)îò}ZOò™#"¬mäVßîM6@1¢¶ÓbÀWco%©DjZ–Âc–+&co&¡·ñ Ô&FЇ†¥šáOòšXYÕAœL5Ê(•X9YDë!!ªDÈȇ†­?ür¢Sjjb”6ùÊW+ÓÄ€ñYA½¡07VÙ¯"¶ tÚ'¹Útˆƒgwÿ[ágdß¿cʴDZŠÊ¶Uýàe`¶Ñ¯,)S.=±¸ö‡ª‹‚½¬YHµºWqê ö»9ÙÎû,|bî=@›,H¹¦ãöҮ»P`ÒmƵŽÔé]†-ÞuÚÓ–°ŒYaŒ@^x þy,å³váYÔ]¨=ÓÈ\(y§ÂiäÞ|ŠïXXŦŽcÈaBŠëÈ%…*­m•àžs%ðûýoƒ•Ív<Š«(²ªyßÒÔö¦¦öÒª)nÒë/ü u€`ZŸV“?ùQ@°[ä‡n¦#_YÓS eá)^vàM­QP—e¢†PíÀÕÁiG°ïHáo’ Nôi}TùÔåeŒNÄPØÂÚ„ê 㘎ómkVŸ\à¨Ò#ÍQP—i£¨ŠR‡:¼3ß`LQli Ú”9ÕV OJ¹_¿Öø3—ñMTƒÉ¶Å8r¯?KQޚοáþFÚ×/%ЖJ.¬¿^“ù^Ý÷M¡¬Ì©`?Q©Mk5™aÌT:³3zv,n7=¯&ÃáÕ™Ždµ¡·uì×Üm6‚øx4æ)7þ3n\› gü8ÅZ‰³a“rž:º‘jU)pJ|ymÍ& .Ö±Æ\õ›‡tFÕ‚Y3-ZÓç¬Ü›KM,B˜ÞßYß앾UÈü:îZ×Yí¥Ž#¦Ñ–«m¢¬ðXWsÆ@©Õm·¹ AwøÏº(YxJÎ'líñÝÆÜÀJébÍeö‚øýG³ÖøBÍ®MfÖ³ ²£J‹mÕ›KR¯i²ÄµÔ´šl³Åa§«)¦L¤µMkÍ‚©ú+ÑYÒœ:í¯¥jm>hŽA ÏD!mµ]•_örøý/µVø\;®K>’ˆU˜3“?VÙÛÐFJ~Ò_¿ÖøAW\2)ÕšÔ© ¡®•YÓZ¹ÕœÁa_Û!bj œ…SÍ@—;ò of Ñs<…~<›ÍfOŽün¢Þ»üzcÖœ:êÄ´’F[‹O1Uñá«lXÅÙ „ºú•fðÙeˆØ«QVD¬Å¤®ÍÇw¯•¥eUR¦Ô]šâß>8™b6hfžiˆ[ÕN#ǯŒñì¦õ»¸™À­;'DZ!dKPÊÍà²õ"|{»"zŠ%JceJ©y‚Ç&Ú!Ôß^Z‹ÙRªú ã÷úß !ÉzÒf»z­ra ™¯ëPoŸSCcrVP¯Dødcè•ÌWè…ËOèD¦#Ò_¿Öøj»Ô¿q‹„ñe/Í4ÿM32xr‚ÎØ³¦°Ò½PfÅ…³ÈŠŸª¾šÖsi¬4®Áÿäôä²,!Ìߊa¨ÿöšÖdÎÕÂ)«OçÛ*y¾±™<†ZFaË­Qßí\­_ïÒç¾­1ÿ!Ì…šµ_pÝÊ çÂ×¹/¤/¹?KÛë«|.…çØsýR×µÄËV ²ôŽ LA°Ï Ý‚ç2»–Uæ™ ð­Ø(‡6'ö ™nÃJFVæ¬Tæ$‰Ì9‚(Z°ÐöÄTÃQ‰ÄºÓ¦Ãð-Xî¢Óm‡7&Õ‰YzF£é‹ãõ·;z;fÜíÓ¶vÎÙÛ;}Moƒl2µëfÉ©‹®æ+)þÞ C(Ù¨ é¿ãz¥/¢”·µk*ªˆáª¬¢¨N±§Ö›Mvœ]âÄ‹ø^²N¢’Ѝ²bª­éN¤:½pHã–W-={aUU–)Km•Iþ?o­/ÓDglˆôvÈŒˆõvÎÝ'ÙïïÖøs*¾¥i©tõ¬Þ"7®-©AI‰ÌIi­Z…Ï U†ó0m*r„Éê5ÛÅ$幨1¯Ä¤ô£ðS—tuÛÂÌC"۲ȯjPµÜB°m+pÕØZ®}q|~–#;goOn³èŽ“Ö~ž·ÂÏšä®jõB¡ŠZwÔ*NU'´2i·iÊ$‘R|-TÜÐ]'˜xæuV$lRRU'´]—(’Yb¢“VK/ÃìäÕw;+1ev‘UÎô‘Yˆ¥`šúÍD`RqŒPaVš®‡º³S Ó[8ªOh"».Q$¾¾?J9ß×Ôçlí“ôµ¾ -BóXTñl^R˜Úꥢ’@pÚ>K:yН½2“Ô{•·n¬À8S?MÐL&ÑD£"îåÒÑD¡é‹%«0NÀ”¹2œ¾;±UÊtäŒ>‰° Qøv/³Wd—ᨕ#Í ­Y0–r„]ܾ”¾?IÛ#×ìôŸ£­ð²£uâ¦øaS`‰S|0)8Ý )kª9+m7¨²Y"»ƒJÁ2R®ª aØA×o²¸¬ Ñ>©YB½&²èŠæïIV`#Ü/ÐvôNÙ¾Š~–·ÀÛ ¹AKòȪHºÒÖ£þvÃNýõ=ÞN¥û‚þ--$¦âÄ(âÖ¾…Ô©tY'3ÅÍ?l9p.ÀŽøQ*AJ&ÿ&\ý¹²+™Õ¯½uV©»)*÷› è¶ðéÞ–ËE§†÷Ú±;³U-áMpÛ`ȲUìšô¬¢#Š/2Ø2,’ÿ‹IB(‚ÖZ•VÅA»YEÄúã#§l푽ŽÞÜôŸDý oƒ¬2½éºù2¸Â°˜AuÀèiCmÎ[n=¡ç;²í5dVEä7Å꣕™ÛqÚS d»ŽXM5mµ‘:…£uâ+²ÕKœo,òÀ«ÏPs*m:\ëLp5„ÂÄÜzUå;Èm¦·'P³Ù¬&Ü|2͆Ù,mç´!… Å4Ôn°Çco= ›ŒE–¢<–Í”°”ß!œk½acø‹|w4Ú~’øûñ‘‘ùçn±ô©é9?C[àb3qÖyUÖ˜Œå7Å›ž*K*PçTÑ/ÃŽI¦‰ª®Ø”©UÁŽbAk¿;Àjª1÷1U¿R*áieÂtÐÀ%³ì…ñ÷ã zãèç¤ýoƒ[ ¼ï‹°å«*ºHeJï¨ØKyë75 Ac+ºŒÛ§ÔmýëĵE]-Jšæ$k1Še^j­Ï'q!°µ>ðÂÅEÏ‘ì—?ì…ñúõG¯¶Ltí×·¯¶vé9?A[àăm9+ëQ Øuu¾ÿA[à>‹„ñe)dhLJ«‚0Xe¤iÖºÔ$´œ÷jU­:Éâ›a:*à|‹oß_V°ö$˜ •2Ýw lÖ´ë'šgîTÃQÿí5­:Éò‚d5aƒÛW}¦¾2¹’"óŽ¢âdñ­°íüZ ˆÓ»O(Í‹šjÙä@Xb4­9ÌçјKÔèµ­ÕëZu“C™¿Ka‹tØýV÷F-ߤ¥Õ«ÕÞËwKŸJ·ûnP@-À¨Ô á¥ñ÷ãÙÁÁÅŽ vÅþx¸íïòÄûc;c#' p²}SéŸv·ÂËš‹®·aãÕfK'9®)iÌûb*a¨ù ¬¼Ø%#8«.No-î°ça=²˜aÄ©ÌQ“Xl;VBR3€RìCYnÃJFUeéXrg™²}ËÁFÃ<ä¹ë9>åoƒÃyÎ WZíá5ÚBY]¼,‹j˶üœ¯gqXjëT6֑ļ!K° jµ$|¿õ¡é1@EWB¥Ö•4ð^¿/M§sŒ”Ò´¨‚ozµ\)Àp®fR¢´¨ŠüN[í XªèTÒ¸˜iZVXo3â|¥¼ç’K¥«5·Å’Ê®á'<%6‡Š”=&!a{ЫL$Km>»R6•8û2å9¼‹ôŸÇÝŽ±éŽ]#r#¶wÁ>Ñ'›²Kò2é%Û {äÏ¢}‰ö#Ó[àkQÙr’UzÖW)!u,=”žªnh.“Ìf«aάĎMŠH¬ÇÔibVMk©= ªOh"»>#y|îu'¤IíÖc²µ#u×(’VÑÁ„£]F™¹F–%dÖº“Ò ¨ç-uÉ4°m\/e'­k¦æJeaB©'Z_¦Òz‚4û=¦$gÔv:G¦=1?œoÀ<ß‘=ðç7~RXS“=§¿å?ó?ñí—»[áËÂ÷Ùk‡Àå"ÊHŠ×(œE…mÛ«0À” |:FP1‰ˆ}*€ °†@ x€áw-?g%²í¦fÞ]5#£~G4ŸåqXùÂ.îZyH¶¸&.Šâ±ò„çË]Mµõ& –Û©˜«MÁM€tõîzOò‚¸¬fqøbFFüŽ=LÊ¿*(v‘²ÜÂu \V-@÷¿R˜-GÔr==úǦ:ŽwÁ,ÝùÌþfX3ß$"p‡¶OYõFwÎù>íoƒÐv.>³SVÚ= a(ßeŽy,%4Ö@;gj–L,K Mè#3V“`bVMa ©YÅS{Cr³s ÌȪ鯉Y8ß]ˆŽ©Y5I¡¸ªoh)&ÁÃøû‘ìG¨g7d äÎFIaNDæü/Ï ¬úç¤ûÕ¾²\èP鸵/(ü4žÿ‰%Ñd“Ä€·²’ö.q;nUlבÄÌ"•^βE‰nÖQˆJ·2¼ù-ð·OËN„±Â4—þ¼6è‘W¿;ÀìŸá:tþº9Km£K¢Éfžó]j}ŒÝ0ú93·IJÅ„QZ—î2ÆÞMCoâ°Óý¾QÿòwÑ+D j_¸Ëy1ýÇñ÷cßïƒ99ùnÉÈÉŽÙ3Ò}™Éö£Ó[áq„DÈWÕfK9¿cÂY.õ…‹¬1Ù^Á n]†V+¯ ÄXb2,¶:…œòÆ«ŽPs3ãI¾ãܬv¤ÙÄYjcË1Úiã®5Ë]†«[ex‹ ‡•ÇËÂYbi {Ï²×Æ)ð5° iY²Ë2Ë bqê 2ÔS±ÕÚ€«a•^–Möø·j[ Ò³e–e–Ä.ãÔ_ãô?½#Õß;ú·g~Ÿõ»ò’ög¬äûõ¾WïòöꆒJÞオ©ª¨Äéñ#i\PXUU1a\, •€nàÛ»QUe¤U†x¼²®"Xå ¥u/NŽÓOý—!p—ÕJF¥u=r—}yü}èœïò>Š}™Éé?A[àéOšæ¦*âì9jÍ=‰Kù–òÕVsUnEñ,¶Øsk²RþZªÂmVäÙQÜ=»²Ûá¶ØsîÛ®æj)s¼YzÍÆßQ4/l³vÏ2­¶ÞzÍˬÝúòøûÑÒ2=]ó¾wõFwëßë+|.+qÅrÙÕ0©+ÒN»–°®æ-uÜÀ1 ,³\«àÔ°C”ê¶Û×M¾aVx,+¹‚2Däµ2už ñ,mRXââ>SK@³Áa/¼9Û;tïÒ=}ó¾GÐÎOµªß À-Ô5óiø¹OiÕŠÛÌ\7¡VTr«-`0sSýÈ ™_©au52,j‘©i“©QCQ«Òi²ÌG ÚžötÙý^;Ë[; §šŽÖiñ3øÐ=•­þÂ_xzwéï|ïíO¢~†·ÂÊZû®©aÖ?<:¶~†b0FJ}€Yœ),qÈ– „°•ÔÐÐ^),ql-íY¨ú),qÈ– „°”+2/¿ß¤d{Òzwö;ç~³Òg7dÏÐÖøZY7P¶ÁÅ YCôÆ”Óó×ãØ$© « ¯M|Å)+¶ºvb…K©\ö—V³ÉÊ®t†ªÖJˆ±bÚQáå$Ä®˜Š+Ën«b¯ícƒð;l%eU"ٯǰY§°µQY›aM©›¸ô¤@Í ó¸tñ¼’ä*¦SS¦ž"w’\…ˆšÌã5?ÜtD п;‡O;É.Bªe54™ÿ{ ãô=ò ;ô{¿¦:NNLû‘ë­ðeóœRÂIéC%F¤rÍ£±•ìÏ%«K‡oxâ,JÀ,È65#ò„-þqFWt¢]p™[,_ʶCl‰Çk¸¾Ø¹Acµz–¢¾ž7ùÄ&†J+>S*±*sìr¯àñbVNe‡óXeÒ1­j½5©Mœ‰˜–]#»bbV |µÖìùm‰šØ‹°°Òs,?šÃ.‘kP…Òh °¾?E>ÄûÝ󾻺ߛ4ðßj] ec¤icÁaià-¿5JT|"úÉJïEX U¦µ›6+4²±ŒU•KœÁau%PeMîmYu^!UI:ë«%…X…óL°« é D‰-raZ©ØÆÒ‰ÓÊ1UH£Ä?-õø£B7*¡C ­TìaR˜…Ò“@W뤞o¯Ä»HBhâ+Ë+½ˆà‹ÐJHQQŽ5ÓÞw)\ðÿ)ÓÊ0¢D°¾?G›³¾wÎù߬ú»ûó¿ÒÖøL‡‘XUZá×T ã:‰ŠÝšyН«Š¨·Rx6_Y+e•1õ¢µk0áe[ —•RØèÇ‘«‰´q`Ó·Å/R Mƒ4)Ùˆ«GÉu¡šmzÉqlX‘ôŒ#4î2à5Ìej ò.¸]SP´ÑœSl'EˆžZMcgNK"ÅÛÖÛ[ªf±-(¤ W!¶Zu­Ø^•šl~¥±–mCl´ÕüZ ˆÓ‡¿âÔ­5ú“œÇiÕÌ‘ Õkj?"øýwG|ïþ®·ÂéHÚ‚(‡[°ñU‡&$ÊDŠfaÙG<Û\{§l0ön¾Kø¤ÊIw,ªö¼Œ¤Ëª˜j3°æ0JFWaÊ)ˆ‰‘—Xs°­Ø( VjsLa°ð­Ø(mç.ãU_you‡;¤§eÉÎCäsšò+v ¤¤l8Æg˘½Ó·¤IÎk¤ì¹‹Kš‚Kš’ä>@)ä=£e਼áQ”—Çìó¾wúÚß̦:wX+LC^ÚsE䦠 6ë™Í†¹Ò¬Jò1*k‡\;ïÎf3IJÒ5‚1Í«Öé¿£)Õ®ÊÞ Íë•u¯ìØåÒAYW„¦‡SñÂþÍŽ]$ÕUl°”&µ{8š=þ´¾?­ðcV¥•¢|”…æ(vYãÚ"©#ÔfDŠÂ +¼IxBNÊØÓ»ÙfÚÒ8‹|I‹‘-;Iñm>»Sa¼Ì¶ß"Õ+3UÓk}[O®ÔØo3è9‡|dÎȈ¡3?X_¿Öø5ÍBUnÃê}ì¾?­ð·ûj·ûÙ|~ÿ[àûN›cÅ÷²øýþ·ÃPýßß ã÷úß C÷|/ßë|5Ýýð¾?­ðÔ?w÷Âøýþ·ÃPýßß ã÷úß C÷|/ßë|5Ýýð¾?­ðûé|¿øÿÄ) 02q!@P1A"Q`aÿÚ?ûµ/òd/$I”Ê̦Wö—Ýn*fbS'ý ¸31VLÿÛÝ8¾ÿà/¼×_zéÅ÷ðê¶¥Õ™{—Þk¯½tâûøZÒ‡nÿƒmþáEyEVo>Ý÷šëï]8¾ý·R_§ÿuþ‡kJþ•[ÿ¤:›ýÕEn‘Tªóí_y®¾õÓ‹ïØu%ú;ô‡kSþîQ[¤U*¼û7Þk¯½tâûõ]I~Žßý!ÚÔÿ¾…ºER«Ï±}æºû×N/¿I´¿Gn¿ƒµ©ú”Véü)©Uùëßy®¾õÓ‹ïЪÙ/µ©ûÕ—ÉMy½kï5×Þºq}ïÚU•¾„íSV_%5æõo¼×_zéÅ÷¿i^wè2I$ŠjË䦼ޥ÷šëï]8¾÷­k…ÒI#xÎNºjË䦼Þ÷šëï]8¾÷««3©$‘²I$‘31˜Ìf$’FÉ4Õ—ÉMy½+ï5оíÓ‹ïvÚ¿âÚl’LÆbG¢I$L’I$‘=ÔéòŠ-~÷šëï]8¾÷*«*Ÿ;66HÙ˜ÌNI˜œ$’I3‰'ãÉE¦oBûÍu÷®œ_{–ÕË™$o$’uf'"DÅŠqävÕL”ןzûÍu÷®œ_{v•äZÛ$l‘²IÓ8­KŠÓMY|”×›vûÍu÷®œ_{v•çz›$’FF‰'Œ“¦DÉ$L‘1=4Õ—ÉMy·/¼×_zéÅ÷µm\(ÔêÕ$âØ˜ý)ÂI'M5eòS^m»ï5×Þºq}ì·JªÌô¶6I8¼$l™'ab˜˜´ÓV_%5æÚ¾ó]}ë§Þ͵Å¥²¦1’I#d“¢I$—‚Áh¥”²E¢š²ù(´Uìßy®¾õÓ‹ïbª²©‡ƒÏÂIô“Ô°B)d‰Š­ Ç’‹LÛÞk¯½tâûص®\jd µ¿Mb‰$X'¡8òQi›]÷šëï]8¾õÚW‘jl« 'np’qœd’IÆI'‚ÁiN<”ZfÕ}æºû×N/½v•fz[ l‘úKDêZ$Z“%™´ßy®¾õÓ‹ïUµqãSx1nv¤X­™µ§J-3h¾ó] îÝ8¾ôÕZ§ô©Ë2<['Å“‹ÅRT" N™$L‹Zqä³µÍû÷šëï]8¾ôZÚC…©±²GƒØ’q a£ º#¥`°X! b…5c}æºû×N/¼k«*Lll’F=oE#)$HHÐ1¢ ƒÝ¤BذRñ¾ó]}ë§Þ6µKLht콈)D44A%Z¸„'±b¢œo¼×_zéÅ÷…¥YV·ƒÞ‚”* ¢FQ¡¡Ò4<'e2DñE/TO(Q÷šëï]8¾ð´«3Ø‘²pkÂB‘Ò:@ÐÆ2°ZV´-Vtæ«E÷šè_véÅö[W 6`tF†NÊM"PRYÒ+)RVм!¡±è+!!j°_ÝÞk¯½uqKúTó9Ú’¢Iõo¼×_zíJª†¿ô®‡NÛÁàÇ„AAYÁS‚$ZìTÕªûÍu÷®œ_dO‚»<»Lk ‰'Ù‚=$%‚×`µ_y®¾õÓ‹ïŸvyvš* ‚ Ùœ`‚6–„ˆسQN«ï5×Þºq}ãà®ÉÓ´ÑU$`Ç®°XFÄŠÁ#(– k_ºï¼×_zéÅ÷£ô´³+j†ˆ$`÷ $$$$@–ÅŠÿ-wÞk¯½tâûÓúZYÇ•µCCÂÝ‚–Õ‚…®ûÍu÷®œ_zí,£ÊÛÒ:L¤Aà‚5A$$@¶¨YiwÞk¯½tâûØ´²þ­¶@Ñ4FÜ¥!"µE ùؾó]}ë§ÞÍ¥—õm²Âˆ ‚0‚ˆÅA mSK«ð¦Æ?vo¼×_zéÅ÷µieý[ ‚0‚# ,`KiØjKójûÍu÷®œ_{v–_Õ„uA ‚ hAcAlQdê)¡Sù¹}æºû×N/½Ê¬“rBj ìòìÁ1$AAA®šEJëï5×Þºq}ï~––y|­Æ<0F1„­)ðŠl#—¡}æºû×N/½ÿÒÒÏ/•¸Ñ¦Ù¦Ây G…èßy®…÷nœ_~‡éieW·E“¨¦ÍSùéßy®¾õÓ‹ïÒ´²þ¯bš_…JŸVûÍt/»tâûôí,¿«ÓJJ¨tþ‰OáE‡ö¯^ûÍt/»tâûõ-,¿«Ñ¢ÆEJ§ðjEJ_žÅ÷šè_véÅ÷êÚYVõn¢›5O¹}æºݺq}úÖ–_Õ¶©oð¢Å//Þ¾ó] îÝ8¾ý{K)ò¶h±þÔ$—ç¿}æºݺq}û–såj¢ÉÔSf©üø7Þk¡}Û§ß³ig>V4Òêü(±Tþü;ï5×Þ«—üÿÄ.! P012@"A3QBð#a`qÿÚ?}ö/ñE‘v†Ò7#r˽>ú•Å'+Ø÷÷äÚ‡Uw§ßqüWz>û⻹ 9ƒƒ§öÞ¾ãø®Á9þˆøH¯vF1²Ó&%‘Q(8:ièûî?ŠûqÇ){!xIU±`ÈÝQ—ÈŽ8ÇÙudƲ*%Oì½}Çñ_b8å/dCÂ?òd|<#úõ2cYƒƒ§ö¾ãø¯«9{"ÿ“#áá×ÐÉdTJŸ×z>ûâ¾”c){">OÝ‘ðÐÔÉdTÉã–>Öz>û⾆? )«l†„~Ä঩™1¼n¾«Ñ÷ÜëáÇæKèQ^”঩™1¼n¾£Ñ÷ÜëàÇåÇèE Q]s‚š¦dÆñºúoGßqüW­á±n{½Z6›M¤PÑEQCZÐÖ“‚š¦dÆñºúOGßqüWª•³<¸íô’6”$$(›JlØl6M£‰´HÚKYÁMS2cxÝ}£ï¸þ+Õð˜¯ó}tVŠ"FÓa°¡e›Ï4Sg¿¹C€âm(’ÖpS36è=}Çñ^¤ ç*Db¢ª>… X¢m%%xŽO93y¼ràžzGþMže¢2<îO4Y4ó¢)E–J#CZ¸îá™°¼o×z>ûâ½O ‹jÜýh–Zc.K#1LÞO'ò ’.‘~â•yæÙÉÈHýIDz¸îà†…Q“ÆýW£ï¸þ+ÓÃÌ—Z›hŒlQ3d¢Y…r'Áæ4É> 2 E“dçÀ¤È D]˜ãeÐEtN j™“ÆëÔz>ûâ½<ü¸õ(ÙÑ´öèËùXÒŠ.ÆÅ1d¹9ðo"Å3}ŽCä¢$¦a™„²˜fn²QèœÕ3&7צô}÷Åz^ç¹õFF{ÉHÉŽBm–1ËMÅ“™dHÝC™±È²ˆÉ˜ò™¹ˆÑCÖpST̘Þ7^“Ñ÷Ü訹:‰Ž ¥Ó‹ñCÈo³rC•™„é"'“‘å±=ä²H~ún<ÃqZ£iïÀ£E‘™&b÷Dd)ßCEtN j™“ÆëÑz>ûâ½ Š¿7Ò¢cfIQ ’È,¦I’ÉFë(±H”Íç›É)YðF\’|ém™Lr1’1¾D¬ÝB‘ nDÑD£Ñ8)ªf\/ ô}÷ÅzáæJ„©t!2:&¬œ¶.ãxæOû,LöC™|’zp6Qt]³ufú!ÞY Ø™‹”Id$IY´hqè”T¸fl/ëz>ûâ½ ‹jÝЄE‘GŠ\Y‡vOØŠ=‰È²Ë±ÑJB‡*d]Š6|K±h˜‘‰ðK’(€¦pÉh×D¢¥Ã3axßSÑ÷Ü׃™.”(‘DÝ1Ì–Jdš’£fÖ~Ç$K‘pNBgä9Ó¸ø7´G&îðÉä¤]òBDrQ¸S¢9 ±1LS<ÁHÇ"÷$ŠD¢¥Ã3axßKÑ÷Üׇǘ¡D|vdtd‘™9äei"LÜ9Œ—ñJ»ˆÇi)[–È;,’àN…R"!!•™4H}2Š— Í…ã}GßqüWW…ÇoséH‚/’L”¨Í+DÔˆ¶"E›¹%É“Hr$ÓÖ2†nãH¢9(ƒ½$Œq¡"+H½"@ÜFg¹!õJ*\36êô}÷ÅtÃçñ1ÅB4º‘?rR'1䲉DöD¥É’tÆá²d•ÉHˆÄË,²ÄRDgBÊÙ ‰! œža ¶)›ÈD‰.¹EIS3`òù^Ú=}Çñ]’ß#Û¢ˆÄQ6NDØ‘ú,“+’hfó}–Ì’,´±OMÂzÙ,¾²ÇÉŽF4YŠ6%´ƒ´Heuå{`ÞGßqüV¸¡¾T.:bA{ÈØæM“"É> 1¾ 3u“g6~äÄKFÆËÄ&Y¼d]‘àO"DO‘GöQ.ÅìIÐñs¨ÖGßqüV¾Ø_R¡ÅH”h“,|Š$äI–d• òMY%FñèË$Íʼn‰–QèŒÌÇb–‘ö#în!É\Ÿ¢‡WŠ•ä­¾ãø­0cß.¤-"NO(ÑCö%É!2Q³ØœìŸ°Ö­’%3pÙ¸LLRÒŒjöEñBàÜBDy'Á‹) Ù ‰ÞCén¹$îMèô}÷ÅiƒÇ¤!"1$ÇLˉ!ÄÉöI’btÉ«EèØôÉHSÄÄ!EØÑŽ?zFD$UÇDH‘=‰1ôø‰í†¯GßañG…Ǻ[ŸZ…2IUŽD‰˜¦7d• ’=‡&Æ2R²DžSÛdHòD‹$9 Bvm ¨L€–ö"‰1±ôøÉŠÕèûî5qF8yj½$Mþ#Ÿ#"sÜI;(š'Hz2D†]>L™¸#*ˆ‰ iŽB(Úc‰ò$"$ycTB\ ØcNynÈõz>ûàaÆ÷è¢(¢q´ûàýþ½(±1±™މ"Hh¡ÂÉñÁ$8hÉ=$Pâ(EGD„„„ˆ® "µŠL‹1r%H‘.Ÿÿãèz>ûàýþ½"%Y8W$Ý›x$†6UŠ.ÉðJÍ£$¸M£´hŠ(Ú"Š#î(‘,ˆ„"èS´$F&ÈØÙ.—ÿfX¨Í¥«Ñ÷ßüïõé!13/±&1Ä”lž3ô?c!´—°£ÈÑ-YCEpQ4n¡H‹l­H¡"+IÆÌ1;#‡’Q$ÆÆú[¥d»Õèûïþ?÷úô“"$d\C‰J‡+$†IŒ”x)­%hÐÑZPÍ¢#Æ›mÕ"„) b˜¤™‰DD èú¼Döáèûïþ?÷úô‘ €àJ‘5¤™.Q4(üG;/‘òmÖ•ÉB™Y°‚äPM¦Ò…È¢F<˜•ãbTKGÕã%ú]Gß|ñÿ¿×§EŒh›I&1‰H£,O,xÝØ–ÜÛÆ›M¿½hlj3ebTAéc(Yí,”­«ÄKtú¾øÿ¦ˆÈR≠,–2p(D˜ØØ‡8ò4RÒhŠ6¦Qˆb(€„W1Ã’ h~ÃCCê“¥cvï¡èûî¸$Ñ"ȯÒDD‹2:"¬Ë 2lØld ú¼Lª/GßqüP¤ãÊ0æY¥EŽ'ˆtFt‡•²·QDÆ4(’6Ùå%¢hkú6ºhHPD$m?Db(Œ‚ä„¢Lœºüd¹Úº^¾ãø­œyFË"ôQ FxÙ³ã£c%2Æ4Ui!–o.Ê6n#‘Xß$Y˜™iL³ -‘T†É1õç–é¾—£ï¸þ+U'Q‹:š¯ß¢„Å-ÜVeƒL‹fÞGèѰÉ#àÚ(EJ(Œx6H± # …#áèŒË%.¹ºõ=}Çñ]Fûÿïé&)‹“Ë2øtÇ‹iÙ8Ò(¢C…‰Qe±#h±‘FÒQàŒI›HD†;1b¢èr½ÿã®§£ï¸þ+¦èßâýý+#1H“31:$ì¡­6’DQ( 68ÖŽ:B6´!ÈVQ²Å„Ž#(cceú>2_–Þ§£ï¸þ+«Û“‰ÝøËÒ±HŒìž%!øbXÏ$x c­&ˆDhØ3a¶Ï(ŒhhdQŠ$`yD`F4JCc~–Yn›}OGßqüW¡ƒ=þ2ôâË%ù6"X‰ÀpHÇÀ”ÄÚ5B’D¨X¬XŠ®¸ ù#ù#â9C~–\ñ‚ëz>ûâ½ïñ—¤„Yb2Bù?G,C€âyfÄŒ±c(ÚaˆñÇÉV#’+j†Ëôe%lÉâ›â>ƒÑ÷Üé`ñã/A¥‰‰–n8C‰(pl<¡À¢,bÂ.‹(±ÈlcôO—œ¹~‹Ñ÷ÜéàÏ_Œþ Qºé³q¸±2ÆË,S¬’#Ib#Š…Ùz6&66Xß¡“ÄFÄòÎ~þ›Ñ÷ÜêGÄÊ1Úniî0æY¡fâË,L‹,ls7ÆáHllR†ËëÉ–02x™Oþ½W£ï¸þ+Ö¶¹F ûø~þšcc,Ün,L±²Ëë””yfOß=ýg£ï¸þ+×öäÁŸâýý;ÍÆãq}Y~†O—%'._Ðz>û⾇·&¥ÄŸÛÉâ#Ù<ÒÉïôž¾ãø¯¥ƒ=þ2ûË{™'üeôrø¥"JNNؤâ휧òúïGßqüWÕðùëñ—­“<`dÏ)ý·£ï¸þ+ëx|õøËÓ”ãddñN\Gî½}Çñ__‰qüd{ó×ìdñIqÉË—÷ž¾ãø¯±ƒ>ÏÅûüôäñ‡ìžid÷ìGßqüWÙÁŸgâý~tœÔ9fO)q;ïðøÿè?ÿÄK!1A"Qaq2‘±Á #BR`r¡Ñ3Ps‚’ð$@b³á0CS¢²ÂÒñc„4“£DÿÚ?Û›©‘ˆ~²r5 íGæj¨¡E¹è*}ì› Gº ž©çÇÛPÆ]ë’<“ýhÆFË"e|&|ïâ‚ì¦5ê]”bq4“4‘n›jÌ2ȧL¿ ræ×ŠN?pÔ$Ñ"„E9=ŠØvsò­³÷Ïï­­’C›«)Ç×Z’9¶†w3Gˆ’Næíò­˜ç ÝÇ‹™IõÛ¿¾¡t‹dlQTïe*E¿ˆ~úI‹ ièð?8¼)óÝåÍï=\»ÿe’M˜ÌeS„!to«Æ’Íp"ŒÈ*)#– j¤;…ª¾¢W$y%l,ØÆCãm=”›*6B>±=¤ÿ¦=AYÕc”eí¼òçn€¢Úö›VË>Q¸A¨á½U[ð£”ñ<(ް…"ç=<}´1ž$…Ñ`Ä\a§²ÈTÞ;6\û…ùPÊݶ@7?ï¥ÿ»ûöÓ…ô”Ž=õ“ˆâ2×¾¬…±*­©í£‡!svµÙþ.ʱߨñ:”¹ë‹q#•3rRA»vW\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×\Mq5Ä×OåÑf:ü”•±Áý^°¿³*ÞFÐî—™1ä¾ÝG:;N7‹< _ Bئòìà on}õ“˜ˆáÕ•[Üh³¨°ãÖ¯ˆåòÄqc™à ¿¶ƒ.7YA÷×7–Cí4Ù êúÀ0$xÚƒ¢ avðêÐ%ùqµ@KT7˜ëÀ«÷tD<:Âíà9ÖJ/ ³c"âhíªzYšÇ†¿û¤3A³.W·õ~úlÄ­à/­~«fþSùÐHáÙËXŸÎ„ Ͻ6°›ß‡:làÙz¾°äxÙ«õ[7òŸÎ­3~ýVÍü§ó¤A³©uÌhx{kõ[7òŸÎ”ˆvN·M‰òÊ„iÏ™6µçOóC‰?*¤;9f6)ü鳃eêúÀ‘ãf U6·eÿª¤QÊ7g$Øã•’œ0î?~«fþSùÑoGÙtô€ðÊõú­›ùOçLD0u=SùӲóYGCÛnÞúýVÍü§ó¯ÕlßÊ:ýVÍü§ó«$;5ì[ä/Û_ªÙ¿”þtÛ@Q”š4í¬wbà m@4»àˈ¶H·'ñ§Xseu±,¶…iâßF8¦XßϧaýÉÿ1ëhýô~ç¨c”üÌÒHÝ¢kåL›J1Ãd³kþŠÙ7qº.õrÍÃsð´o?áI—òŸËØÿ|žþ‹ %ýtˆB'`<ÍGmæ÷wç5üipõ}/Oç­©¤\­€xŠ3¤B&WB“cp{|(0ž7'è×…Gmæ÷wç5ükjÍ]¢Þ5‚6<ü l»¾¤1†_DdxùÞ¬²¤½ëˆ­Ÿ÷Ò{’£¶ó{»‹sþ5´áêïÞÞ„#B"‹üµ£$¿®‘D왩UâÊF¶—©Û§:Øÿ|žú¼†;"æØ(cÈk[,’´lÅ í"¶¹¹åQ*EŒ‹|ß/_³NT¾“¯Öú-ð·Æ¶çÞCéM+G¬Š–HêyðöÖÕ|zÒ3hÀó5q¡£$¿®‘Dì™§ÚOû‘uûü¿?*É(‰•ËÝ ÞÝž²¬m•¡ÔŸ[>÷ÔÞ.^­ K#¤à;z·½&úÔQHW6±]œHMøjOº¿JÅÇ„w¶Q†ÿx£mÒ2#|Õím=uåI½XÑ·ñÇ’ ]ýž2¼»&68,q°`yk¾¢qfV•ÆL€ée©”¤x¼r` ¬„éÙ[[´i&\óZC¦«Ëì6ô°NeEÏ yMõázXßÄ_ÛÇ¡f±‘xtÀ˜0’!ŽYhEÉánú0 Þìñùµ¿¶Ýô#'¨ xÛò¯œ{üØ‹‡Ñ½ú » XâoÏå¤ÎŒøÀ¶¾Î…eâ¦â˜Ë¹bÜ[r—öÚ‚#‹uð<¨I9ލk{k|¤+ÿ…@ÊB1¨QøtG"ëàyV(T¯2÷ÖR6G @w{±ÃæÖþÛwPDqaÀâ.¾—@am;Eê7l2Õ´j>Æ]ËâÛ”¿¶Õ,(ÖŽ[f-ÆÜ(I9ލk{h³cð¨ð¤V: Ä{oñ¨¡v¼q_nã@+/W@p>5zÞmœ›'#ñ§Úô [!ÝnÆ]ËâÛ”¿¶Õ»¿Rù[¾ŽëÄ2÷Ñw%˜óè °Ôb[•¼xÐTq¦€â.<øÔ“d7’_+¨ ߺ¥µ†óF°ï¿ÂŒ`õ ø‹þtAeÔX¶#çÆ„dõ$~TD[½{cV?ˆ©=Q¼lTýiQýÑöΦ\¬Øä;ô¤–G 3ã ¾œÍ(¤¾lRÃUüz=äqßéHlNÆPBÄâ.Nl8ñ©º±\:-Ìk{÷ÖÝÔ"Áwï#àÖÖê«øšV!Þz.W-¯¾·ÒÓ…lðMèÖ’@/þÚ–-ÔJ˜3&+b¶ãχËÙÔª2¼Š¤2ÒýýÌ¡ÔW¶§ia…!U:¢Äò¡Œ<(ˆÓç=xñöW£ãoÄg5¾™Z¥'›LúçNÏ$—ÙÝ/Žp®>ÑaÐ1‚'…bÀ\ç¯>ÊŸf…vrÈäg.>KJEÅVL|}[÷~I»ñר¶ÒÉùÐÆžDi‹sž¼xû*h¾£• Ø+[“p¤sc„NÈ õ<*v–RSª \O*–Mäk»·Tž³_² £Ä’*œÖú^•/ˆâÍõG3[3Ã.†¾ÊŠMäm¼¿T²Û¶‘"cÇÕ³ã‘ÿ»ÙR˳F­óÅ/"eŠòÐÿZT,PFe2½š‘ð« x‚®ö);Û[÷€­© qõ#Ë,zÞºóó¢‘&Í—¥Çþí)Y0ñõoÝøVʬS*‚=jy÷i†Ñ…½^¡&ÞZƒH$ÿÀ¸&¦ˆk´DÖÝŽ~‚¸r8‘Âô¬@67±ç[q°(‚É~þ…Iÿ‡pT’/¬‡!R¼Ýã‚¡X %øëÏåììYREbYÂé~þ…Þ_ ën6¬`’tR=ÐãÞr¨žq.ò5 U@³Û†¼©v­¤¾’oz‹{›ß¶¤P]àuÄ›XÿW­ÎÏ›`ìÎ-Ãÿt±É4ðRÚ ‰çï#P¥T =¸kʤ}¥¥Y‹]‡q’ˆVÈo¯ßñ¬¦‘äa¥ØÞ£‰^]ê±}P[\yߺ¢yÄ»ÈÔ)UÏnò§‘½g92ê&G› Uè°i¯uc“¤h:‘î‡ó•K¼Þo´Ýãê÷Þ¢–bác`ýU½ì|iý®¬1ùÈÆ£ÃZ,ƒÖѪësÙãQn÷›íw™z½Ö¥f;FCŠ5óÿJžf–xFÊðÿìTj—<r7'ú4Þ‚ä¬MÔf×Qþµ4n±dì Ä(4³_—ju™å "áÕ@yƒÛÝ_<Ò«v¨ QˆÁ à2âyüj Zø£†6ñ®¹b$U™)O¾ùµ ºÄÛ¿•m;L9 ¤”$z ük(T¨T$xdŸ¤WJ/22,AE\ÃB*X¡RX÷pï5ŒÑ¼lu³ P‘á‘c<®”£»pÓJÑBäGÆÊxöxëN 0)ëiêÑkF—¢û™0»c ¢ñÅ# âBè*ÐÄòÅ[Ö2##v0·ì˜þèûç_¤$gsÑEϬ•³É¶A$pï"è@­ª]®û¦F»žy[·[VϹل±âùݱCϵmqlªK©Ë"ñ#—ƶ8ðyf>¡zÇ×jF’7Pû]Ô°µúõ1v-œRå~}Bke“dËtˆ¶aÁ;ökzÛ?|þúÙY€•IöÔl‰‘CÁ‡½mú.ÒsV¼0(åÖ’7 mG'èî¶ÈB“)ŠÁ9“šþU¶A‰ß4x„ÀyÖÐ$,¡Ó ׊ð¨ØJ6ž6õu'—áH¡ÝÆr¯ê 8Ôch:³ üã>VþúA%S"ìàã­µÌ ÙHÙãu»hoÇ]bH»–ÿ[#*òŠø‚Hví¨ÓÑCÞ4g%Ï5J”¼bkLÑ(b@êøxÖÌÐFPID^úäÃáб¦Î 4HK: éQŠ 3UfgÚ°¾º ûë#Îû÷Œ6G=ž4'ÂÑî7»»óÏVî(„ûLjlI¶¥½ž5U%\m*Ì|EýÕ¯’V•—¬M­eìñ©†ïÖŽB§#uê5m/:Ŷ¹(øÒnâX_|‘uI#­~ß š?EÇÝ‘²7¸SÇö<t}€ó©N¸ab®. ݤqÅòÅ/©ó¥ f  Åî@ü) †•–FŽÎêÚœ…¾Ñë6_ IFC ûˆ”sE½ãJž b1V\®5¿o}kzªºw |+h;A„oyŒŠØßËQQa£¨°â>€ÍoUWNáo…(’¥Ç@^÷ÃB2ÌúÛ~U±²&#Ö'_mncŽ(8“©{Þ›û<ž/cÊ’-,¬[ÛoÊÌE#j2l¸nF¦Ýƈ%\J‹é¨? h×K²½ùÜ_ó©L ò#/R6 n9ßAåRÇ»·–ëÖ[vRÉ»ŽK}Áèi7qÇ£°°ãû£ì‡Ó`µ±î’Û4ì½[žÛ˜z*Å‚Î2Ú[¶äÔQËy(g|ÅõÓʦ;Ixå1°ÖËnzZS”ÙQÚË!n¯°Š]lѬciÝܹ[¶¥Œ±b…󌶖îME°ç’†wÈÜ_]<ªÏsI¿’2X·Ñǰ÷Ô›˜xvq*Æ.JœÀ÷{ê?LÙäL¶˜£ë‚·÷øS“ú#v-ëc/W¿ÈÙã]ER<éÕ!XdU.¸1±¶§!ibŸÔW:Ÿë¾œÈÉ )Ä™;{)6tdbì0á­$ä¾%ˆ4ö–'tõ‘N¢;D(Oboî¦ß:BŠuïÄqáX$‘¸¶Yƒ dŽXɶI~>}âIÑrëpÝýÔÎÒ$QƒlŸ·Ê‰R§0yÔ€©VKu[Ž´ö–'tõ‘N¢„í,QÇ‘[½øéùÑÝ•´ƒˆ+@±°eø^ÆÉ2±Äû{)í,.éë"EkzªÚw‹üjC,Т¤$ž#Ê‚„Ï;õqí§˜I¿:ª ÓF¿Ã¡2–wõQަ™v†D݆%úØË›w»Çÿ+Í­¢äÓ^hK¯Qð¤ÊXQßÕF:š– RÁ$9rRžÞêÄXX\“À WYXɶIÛç@É4 ”«sr½)ßCvÇK›Ý…Ôp£”±Ä7ÿJ`$‹÷;èVö êé,dÛ$íóù1ýÑöΦ•ìJ¯UO3kVÆÌ‰âpÖ\WŸöV3O,‹ØÎME$³aŠ…tÄÜÛM<ªYÎÕèûC9mAµx¤³ïSý3Û^‘—ÌúNó+rÊš-²y9’ØžF¢’Y°ÅBºbnm¦žUxf’'yä‘Â1q·Æ¶ÍëòGa~g5? MãZÛL2pä2¿¾‰d Üëÿ/FJòë o}6×°Z¶y$6D‘XŸ:vI–iJ. l/¡ãQîÍT D›8c ·Mµ”Ú£X䙘,±d¤vð4Û¼x°vÝo{ßà+ ü. _¡Ãà:&…äX‹2¸f½´Ë³Æ„2J"erà°$۳–6% Hp½½c|ýúVÏ%òÚ¤ïLJ¿ð© m;?YHlÀ>¾_Š;õ–Gb54M(1Șoãð£ r‰Yœ9 ¯ÛãJPÜã_bQú 6V: –j)Óè²årö:ŽËŠ6Ó#ªìx4® ï½ÿÕc”JYÖØZý¾?&?º>ÀyÓ-Õo͇ Ý–VÐ5׸¿È•…­ä} |iåÒÊÁ}·ü¿Ø’®„õwËtÉ[k©Ðô ê@>üþK>K¡××úÒ”hƒ#í·Ç䤺Y˜¯²ßŸIU·ªÍ¯p¿Ãû“HÈX÷ü¨þèûçOåKû˜¿Ë^†ÎIœ€Kmú6Õ“ƒøÖ®cÌ¢;/×¶Z~52ŵí)cÉ¥«gÏ´"SŠB ¹çôµÖ¥ÁYW#en"¶Ù#b®!о”‘–·öÉaýÕ·¾¶¨ñÚ#‰ÉÞ8Ål4êÛJœ´ ú5þ—õʦ]ãÙâ—1[¨Ükm’6*âûé[39õ6÷AÜ ¥Mto›†\´áÔ5yœ3ÙÝÍ£ ŒIã]wfÔ¶§Yç•€ š¾ñî,}nÎC#ÆÇK©µ#I<®Éª’äÚ®¤ƒÃJuŽWUmkÖP»#v©µ1y–щ5{>Ù&ñCgõVþZÔ‘7¬ŒTÔí´ yõyêÆ·Û>`Á•Íü5ò4çZF‰^÷RVõO½ÍÔ1e"Ë~îu2åšÌ6³eÇQL,ä"æB “áQ‚x È"Æ~ÓÃ[S,[âÃ’,Þ\ªÜɼ.ã-àãeîáÝRƒ˜}Ô˜²‘õu;JHŽ5ͭdzãPwª2ÄêÄ/ßäjHáßfªÌˆ³X_‡.ƒ.îYÈknâ:Žþu¶« £MÇY[Öu­î̲ 8B®rã{{ª@·GE-×™ Ð_ÔãQ¦Ñ¾ÍÔ1e"Ë}m³g˜zC¨XGml°8‘bº8³MªÐï2Öc£~_ߣû£ì<“C¾‚åmhº¤ÛÒnZIC_ð!ò\‘×­Ü0‘AØ;ß/wmH6x ©S”—ËRúD,ã(1•µ™T01c‰6¿Ykuf8ï‘ɲ$ÐkZÈ«ìP>O³ç"ÎÀÛ…Å&Õ+  (‘Ë0ïçMÕ ¬1eí³DѺ¶Y3åð¦)³”‘¹ç ð¹–6kÈÁ­b|»…’)ÈeÒLEˆ·e9HɅ׌×6ñ¨7•Ž9D¤3ܱö¢Ö½Ñ—Ú¤|z-4,Æ÷ ¯‰©&͈]®@È7Y[z’‚ÃΤ1l§)’í%ñ¸å¥FÓìûÉvÜ.*müE£’F”Ö*O}A4Qb±°lr½ì{¿Ç÷GØ:“ž=‰^‘‘<,æ÷ð?#hc›L‡óñ©\¸‘ß—åE¬^ H¤* Š,Þ­ØÞŸBBW"ÉѬX¡çÕpÞêÍŽ^°ÔZôBØð» .È©,Ál|ëu‡Z×㥻oY0R¼.®ÝJí‹HÃ+¬éa­¸s È¢ÍêÝ€-à9Ó“"¢çº‚½»t`}Ý í‹HÃ+¬éa­¸s«8±°>Ñz{ £paÊõºÃç8Ûº6}öÐÉã`BIÔ_…èÎ~¸Ì;øŽ<¨”ijÓOˆæƒ";» áq<Õƒà_†NªO¶ƒ¨³ 6 ­ÛâhBSç¼h AÒèÁ‡áO½[*#³Y…ÖÊH¸åA‘E›Õ»[Às¦&EEÏu{vèÀû¿ºÇ÷GØ:|$XÜX‚[|iSjœM8{¯_½º ïq÷TQHW6±]œHMøjOº¥Ã#j‘º°é­C"¨S,yÈø|¨þèûçR4-‰"ǼTm¶ÉB  *V m A𫹹°Áj–PS9/‘hÕ¸ñâ;ëz§ãÕÒ„nW ÞÊ¡uò¢®ÂÇÖ8€[Äó¥Blõ¡Btò§*W¯©\_g ³\È1oè ô|þg,ñïù—ÿ‰A.ÐJï”åP5«¡±±Ñj¬,8]A·‡e:©ÑÆ'Û…e±  P¿QBëåL3 CYËǶ€ºœxŠHó¦Å¯—­Ème!ׇ [£sÕ)Êê.<éUu}RT¾•4yu‚Gm Kàߺ‚¹P¼l¨ÝWss`=‚ݤaº½ñe /ç[ìÎó¶—&°]@@e7]F@†²•ûjînl°Z¤|2›%áJÓ>EF#¸twoX…·‰çF0z„‚G…ÿ>€ñ¶,)s+§ T/ºŠ»Ž·¬B€[Äó¦Ã7Ê~4ÁÄñ ¡‡°ÒϟΩËRH†Î§ k º¸nørË/}Gh"ãÏc{»JÒI’¦öåäk)çåG÷GØ:Údq‰3Ç·€øÖ/A¸d@§ÃO‘,Ž2&xöêƶH¥‚×&¡Òü)Ö ¼¨¥­…· = “ÿàxšfÚgÃZ,U27üèZ_™)½Þ[•íÃÆ„›,ÆBdX°)‰¹¿uH°íÊ€’0°6ãcѽžCwÄYnI§ /Ì¢ä_^»FÏ&ñXæ–±ðÖ¶GżW8([õÛ²£YöŒ$p\ ð¹­¡%qán׿Ƥ¶ÐÛ¨Ó2Û½xÂýõ¤™õÚ3¥ºËÇßNñïÎ*ZíT6凞ö«I"+~¯E¸æoL£)±ý‹Ý`<ê\…Ñ—Õó&f’ü\èhãšE¸¨mCd2G\XwVÏ${çe•\’ÐyÑf掾Õ#ãQ4ûï›P»¡lM»ùS`^y%×±­ùT0fã-§áK‚ʳoÒA¾\IµùSÿkÛÎBÛ¶m=·×ÙѹÚ3U ’² Û·O!R­œÁ"à~·o¼VçgÍ|ÙœZöá§™¨.&‰qXŸFóïsE U@³[¿•m¯ ëÎ9væ¶…7ùÄÄ0? *ŽÔóÙ¹ƒåSI}¢IdGQ[j.û5†+¨ÍÔùT’·¬ìXþÅî°u´™]‘#@ÇÈò½ô$ŠMäWµíbȘÊì‰dq\;{ë}¦DË’âA¢û™0»c ­û Xê7~UóQHÿuoCgHÛy}nW¼Ö3Fñ±ÖÌ-Ð^8¤dH]M³Â¯+FÅz«ÆÕ8³ #[Ꭷ¬¿XÞ)Fà¥u5ež:Qxá‘q`º *à†yPH•Ï%4ð¦NCà:¶'Ê„ ‚3ôŠéM1I÷|î,òþ­V†7ö(½`CG£xðȱýb¦ÕtŽI#ÁX¸MÔZÞCØ¢ôà«u=m8WQµ  ç@ͪ›uŠ›j/n‚±«•³*ÞÕ¹U—-WL[ÙJïª7WC[džEë6 wãkß±†7vìQzœY„‘­ðÇSÖß,o‹#pRºš²‚O)¦)>ï€ÝÅž_Õ¨¬jåF¬Ê·µ•e¿c¦-ì«MÆ{Z‹îdÀZ펃ý´t}€ó¯Ò 3¹„X(¹õ’š)F2¼±æóèlãÉȇ°ö[£mfEq¹õZöõÓ²„KqFX¥õ>uäl°Fò —šc”‘²Ù¢ÏP b¦{$¯ÐòH~­Éî‘«£hÛ±…º6Y6<·h‹fó¿f·­¶%Ši_Òœ²Äö$räoζ„Š7†oDǯ%ØõÇÃJÙ†ÙxÈÚQÔ?_¥ð©^XÊ*Ã.­Ï¨xvÖË&É–élÂwìÖõ¶~ùýõŒ1¼Œ5²‹Ò4‘º‡Ú¯×©‹±lâ—+óê[Gï£÷=nö`ÌâR]‰øÔ !¼ËxÜü-Q¬Š›°VÔÒl‚%ÜÉóŽìKõO}lrE– X°:'Tqì©—`RÃÒŠ'~ŽžÚx[j;(R/©lÁ·Ž5.ù š>©ãÁê)°mËGm/€üº÷2ÿ–Õ±þù=ô£h…‘Ægã[SI²—s'Î;±/Õ=ö5¾›ÝŒüèıM+ï.ÉØ‘m9ó­¡"á›Ñ1ëÉv=qðÒ¶a¶^26”uÅWé|*W–2Š°Ë«sêµ´~ú?sÓ~æ_òÚ¥Êù´d&-‰¿…èFviâÜžþÁaQ.FË` ÿmÝ`<éíÇJ褎ü2[|Œaäa­”^­4oìan‚Ö8/òЏ!†„TÄq=Ý n!r¼½3±»1¹=\ÃB.‹CÈ{oòlÀƒÇ_î*H#!qßýÂ?º>ÀyÖÙ õÒ+¯ü£ÜM߃c(õ¸:ÛÜzÁŽÃ¶EÙѶ«: ÜúÍ{zéÙ[…›|sÊàWNÿëJXÓgš$%‹ Pt©:€•5õ´~<©%x²´¬º±µ€^Ïýéã6'OPçm=•u‰"î[üODQɳï2Ef|ˆ:‹é[dÅ"‘ŽÒèYB <ÅøÖÔJ«!Ù²1¤™Xæº\VÎÛ‘ô„‰Ðbþ©ƒ‹Ü‡Ø„ÔQɳï2Ef|õÒ¶ˆãD‘”: È®>«^ß…ZíÚ±7¨‚ìÆÀT¨=:£0Á›A»P$›>w†,ß#Pp© ‘‰Û|Ñ ‰«áãRÊ‹`vq*‹ú§xó©&(–TöY¯î¤Ðîã9×õœz †I²¶ɆžÑR¶sÇf0Þ¶¶ÐökPEK v ¡$kãRÞϪ’®6•f>"þê==ãFr\óPt©ú±ÊVf‰D²„^|E.ï²Üª¸|OeéQÙ€©Pz8uFaƒ;6‚ÿv£OExÑœ—<Ô*R龴͆$¯‡lF%(»ŸVü:íQE{fÁoL‘ìá~«äoçÊ£@ÙÕ˜_çò·ðð©âÞ$ë‘b=ETP#XV\ â5 ¥ÏÞ¡&0Ç&XáÁî;xšXÓgš$%‹ Pt¨ÆÐ6ufùÆ|­ü<*x†¢7+ì?.?º>ÀyÓºqïç¥nÒ¢BÙ/©ó=ÿ"U´‹‰öƒðè ÖõUtîøVëÑ *lMËêGñwšH´²±om¿*ÙÙl ¯óñ«¬Ir_ãЙEŒž£8ÔT­,qȲ¹r{^’@dÝ•ÇK^ô›¥HB6`'onµ&ïgÔ«1ãQ^ ãáœlXx[CçSKkoµªíKÜ×øM©a‰%WÞeÖ7¿m[z¬º÷‹|iâÒÌÁ½—üé‘£Ic&ø¿o•dl,,à+¡³)¸5&êÈ¥X¨:ßΣ-ãPªÄ‹S¬ˆ“+ˆ’ü{h¹T ®[«en(£ öPxëùÒ†3Pb÷ ~Z¥â¿Âƒ€à uqì¤)C /ñ¦þÏg‹Øßò ÍoUWNáo…JÒÇ‹+—(×µêá ¥t6e7¤ÝA«[ùÐf·ª«§p·Â¦icŽE•÷…ö½F]QB.(¶ŸÑ TØŠkÃvâàj~„à J‚Ë# Ggu3N³HßÎ,hIeõmß Å`Š!þ üh3[ÕUÓ¸[áHL0´¨,²0ÔvwT’µ²v,mòãû£ìm¡#i$Xà ^Þ¤¤(W¬¿ Õæ‘ä=¬×¥(nq¯±§Œíp«»£Ø‡ÒÁ¿ÃßQG~²ÈìGˆ_È×è×½ÄÏÿØMY&YGjƒñè™U7g±­p?*Š;õ–GbÀyÖÒÃýÚ†?€øü’(¹cÀ FY#•[CÐÏ’è@µõþ´§e ÈûmñèŽ%¶NÁEúR],ÌWÙoϦÎ,l´_å]‰'†´ÄMº†l î@ì·C¦JØ›]N‡ý“Ë¥•‚ûoùto–hÜ\)Q{­ïÝÝòˆÉWBzǺ’4v8QĶɨ(¿I*èOX÷|° ¿3Ê£ùÄ‘]r ·í#Ÿ‡ÈîØ:ÿó¯Ò_¹_úÓ¥³’A' ãÛ~‰7qo—œ_ðÿV¨v…G‰w›¦ÜF¶«…´û-ÚC~'aÒ¥Fv(“& [`†in²Xs]}•´IëíKêK_ò½l&vwùå³7ˆ©c™ËDbáôE”‘aˇB6ÎYýƒ:ñU òÜmŸU.s¿vã­+¬»L²¤ƒdLqî½Í"É4Œ›¸ÚÌÆ×Àkï¢Åo6Ô¹F{ûMÅO$sM pÎ$¹À1j2|ö{Œƒ…Ù¯ë¿gº¶+¾Ñ;®Ò–’XíaÙ{š–9œ´F)DYI¸t0â—þ† ñ3#Žjlkÿ¹ÿ}KÎZ#‡¢,¤‹\+gÏ´"SŠB ¹çôµÖ§™=!¥( ¶í:Û~ŠbŽŽû¶lÍØõÏÅÞé{…ä¾<úS³¨kDáßÈÞ¤M˜î‡¥Ì.º4“úÛIÙKƒÚÙ‘å¦m ³ú…vâz­Ñ´~ö?sÖóf,®e!Ýxaoo1þÙèÙÚßK.>8ëAv‡6m¦4YÁ«j©Ò8œãŒVÃN­´¨w,QŒÒu‡U*q™H¥.Ѻ[6Rò(k\^¶3<Žî%A×7ÚÝî7È™dÌÿZÐ<¬à\Ð[œF¶¬ažXוȢò;3ýbnidy¤iƒÔUÔxiWÞ=Å­Ù±†yc^6W"·#´ŸXjóHòñônÒyU>¨sjʵM«y¼mçÖ¾´w²È÷úÍz1 Dx¥ô4V)¤E¾{>Ù&ñCgõVþZÖØ›C0ôu7Ç™È U…&Â×"à‘ç[èô³„+¾Y8ßêøtBÛ™7…Üe¼l½Ü;«}´fÀ¾ ¨m{q×ÌR4D˜ä\Öü{>Kë;"A¾Í°f"Ín:r¤žmã3HÉ‚›pó­´¼R>)prL×»mÙä±n¯Ú}u¤;.ñI•b!Íýnê‘ ßfŠX3f·9Ti´o³u J‘e¿¾¶ä‘œG³ÞÖâlàR¬)6¹:‚ÝUi’&eëááR&ϽͰf"ÍouE½ß3b¹R=Aí­ªÏx‹*‡R9+_KS™ ƹµ¸ÿZÐÜ,‹Úߢ(Û“Ƭ̲ÝoÃm‰´³GSêó9NÃzÆ™°$Æß[íŸ5ðes{_†¾F¶b™›‚ [&¼*,ö}²Mâ†Î5ê­üµ§;VmŒ† ¨mr8ëP)Gx™Ô$vŽ:kCrŽ«ØÍ—ÀVÈcÌ!‹@ÄuÛ¸Ti>÷7PÅ”‹-û¹Ñ;Nõ¤Tt·ì:”•¬1eí¡˜ã¾G&¹'ä^Á²žb¡xvr H²Ò\›ráE­{£/µHøÔo.Ïœ¨ƒŸTÛ…Å7SÌòñúÖÓð¨c!p˜›&°a‘o*À%ß,‚Ò‰—æ4çLFÌD‡ÿ“ª<¿×£uîGÀ¿ kY}ж¢H$/)~ØÞ]ôæ8É…†%¯qãCq'nO•ú"¼ÇH£ 4=QÄVÓŸY§'øøS]C£.,½¢·0Dc,ŽM‘& „²îp`Ö¾=ž¾‘ ;¨ÆêøßÇJhå‡(²ÉBµŠùÒM cƒU&ü(ŽEš—¹öÚ£D†Uh×&Py“õ{ê6ŸgÎD· Š ã­¼y våoÊ [[v˜øõ‰øü¨¾àûç[[L¬hÊÖ¾ª;;볉σ+›þ>_ äqE1î­ž8÷èÍ*¡ AÐóáL̃«ë …LjãA”- ¸ð¢á,Š’ÌÇη8uøÚ·®£w|rV /åE‡ºøŽ]v‡Ä(f·8üçeê5Å[xØ®._²â·Žƒ7 EoVìoΘD™=Ô±XmF,öÐg ‰æ¬~ ´gZúoýü(”ijÓE$\Z’4v8EWÖ³WÄr È¢ÍêÝ€-à9Ó“"¢çº·vÛ-o@[6¸º¶¾TYÐu}k0%|G* Š,Þ­ØÞ6Yx–!@ó4›1*ŒH–ѬX¡çÕpÞê„,™`u<-H¤jàï½:…Cf%€ΊH,i#AwcˆYÐu}k0%|G*Þ*ÝñȰQ?{(1f ™­ÓŒ_¼ükwcê¯0xŽê,è,8Œ…×Är ê¢Ç…Ø ¼;jWWŒnÅú΢úŽÓß[°S.÷öð©bPÃ+fãL΃«ëÀ•ñ©z‹Ö­Ü ¯ÙV:òâûƒìmÍÔÈÄ ¿Y9 äb8*¨QìL¹?L–¸üz%Î"TÃ.ÍAøVÉ,³ÁúäÐ8m/Æœ¹µâ“Ü5å¶4‡›Hšüô:û*@Ž­}®fÐòêØÔQïÙŒw¿ªw„ëýs ò:IŽÕ”F ¥š¥ta•†bV$ß»/‡@…]E½±ÊàsòüjEÂΩ‘êeý^¶}üÛ(K‰ˆ‹e×[ŠÇ5¿¢ãkóßßÝPºE²6(ªw²•"ßÄ? ý$Å4‰ôxœ^ùîòÇæ÷ž®]ÿ2É&Ìf2©Âº 7ÕãЪ¬™G#± À¼;xP…Dµ±¸ vø~5jáÌQâXp½ÉøÖÇûä÷ÔÒ¼ÑH¥F/rו ¤[#bЧ{)R-üCð¯ÒLYHŸGùÅáLÃf[—ƒwVÄpŠ9ý!:±I˜µüêi^h¤RŽ£¹kŽÊ…Ò-‘±ES½”©þ!øTˆ$‰eß´ž¶*×쿇ã[9‘ÔªH¤•Ôq¡hÉ< ÈöÖÌs„îãł̤úíßßQÌì¢M–öê~¯ã[’<‚Bÿ<åo çqQ‹@0Lm½µ±þù=õ4¯4R)GQ‹ÜµÇeCmw®HòOõ¡ :,‰!k;cp@íðüjÕØ£Ä°á{“ñ­šFTHàZ•Ò=„V‰X“~쾑Ë5RÀÆÂÕ´Ç’«I—#aë)øQŒò^ÙmzA1¼Gh2 5ÆWåSÊóÄèQ•q{—¸· B­p"Œi÷mL„2±xü¸¾àý‹ÇûßL#Ǫ˜(;hÁ•ƒhùPcÀ ŽÊ­›`1pzÝŸ :5äb9*…‡ût‘ N@Ò3 d=¶øPkN—« ¹±>Á~„‘ N@ô±œEÏwÈ‘ucc~#cKÁeK8 ƒÞ:¢ avðú.7ÿ‚?/!»ƒ>49ß¾Ý#cK¼ÍÀ© ˜ù#c[¹E›NýѰ»xu# ¸ŒdÚòè‹î°uúCzYSr.T\úÉ_Ù™¤S ̸±Scm=½Œñ¡@†¹ü:6«zû“´_ð½lœm¿O}IC Ç»v\P¶RxñåQE!\ØÅvq!7á©>êuŠ(‹ ªT 땀Ƕ—hݦ^½ÂÝRsLJãJ%Š$'i7ˆt9TÊòì˜Øà±ÆÁå®>þ2¢4!Kºå€åçøSÊâ%D\Û©§g:Ù¤?íQÆoÆ7ÓC¯ ¾ê%>½º ïq÷T ¼ØÀ(¬âXر¿~>êÛÙUfñä?ƶ4eB«äß7§°xÑš=Ye –äGÈöîž%Ù¡ÀÅ?6.÷QÎ¥1*;oÞ,CX/þê9$T‹{C«uV½¸y~4û,‰•³…q·w[#âŠ^+œ-úíÙP-¢õä_Õ/bw~5$eP«E!ë(<û*yC˜£È)á{ñ­šIbŽãi:¨`{¼¿’&†v츠l¤ñãË£m #Ò<µOÓNÚÚ%tVdBám¥î9yÑ™‘D-Ñq¸ öx~= Ó˜å使¬Ò¤]퀢·1Η÷1–½Ûy½ÝŹLJ ÚpõwooHa¡ÈÄp ¡G°tM ®Jö#_TöôÓ´^— º¨ÝQBíxâ¾Ü/Æ‚#‹uð<«b]TáãòcÝâÜrZþÑK4 Œ‹ÀÚƒÆl—xE—€P![, Hñí:žÂÚv‹Ð2á ·Uûª(]¯WÀ[…øÐDqaÀâ.¾•Hp¼Œ¸«_Õ¿D_p}€ó©ï|~l?Ú­G¿Ù–'׬©Ž_‘uO²÷Ô;!Ž/Ifc¥%Ò¤mÈ€’0°6ãcQ¬ûF8 .ø\ÑßM»pÒ.8ßÔ5€l…ƒÜEé#MYŽ"¤mÈ€’0°6ãcAo{¢·µAøÖßòI”ªŒ ñ£‰$r$Z†DÌzQI|Ø¥†ª øÔYÏ»-fËÀê5'¥I»TsUr%…-›4a’·h¥Df6Á6‚ò/,4>ƒH$ÿÀ¸&œM6¯"Ø-ýAsL›Ï›T#è1[Ø$.—Ää¸isÚ>q£NÕ½Iÿ‡p÷4P¥T 5»ùQ2™iØÛüij ¼‘Ø |*9WÖF *Gƒ{›©P¬–ýüê9&ßfªªU@³X[*ÛYómÔ ‘ÖËáGHäH·B:™7¸ª+¦‚Ü|¨2òD_bð­£¶˜#’f•LG]{EèìÒØ[)Nµ©ë#Íø1à„ /Ÿ:‰§ß|Ú…Ý bmßʤ`¬ûC:ØVñ Š­ DJ›6Š8yŠ é;\ÚßçŽƒÊæƒ/$Eö( ‰§ß|Ú…Ý bmßʧ•o‹¹aØýÑöζ‰/êÓÆ§2] @hGiù|î«Ú«—ÄRMÒšÁ^¨#Û3Š6o¥Ë´ÖÔÒl‚%ÜÉóŽìKõO}@/¦÷cÿ:1,SJû˲Dö$[NFüëôxhž2­³¾Mës¥D„,Ž3?ÚšMD»™>q݉~©ï±ý…Ý`<êE†7‘¸ÙEë) –5ávB>G}(x%RÚ-ÐëýÕId.;ú, “ÇOö-‚–Ä\ØpŒ1¼Ø¢õf#‘è"þî<‘‰K9@¤› [³Æ‰ªÄL Ø?—rG¤$L"/bÞð¬ã„#îw· âaQŠ 3UfgÚ°¾º ûëmÞ|êBR˜q $j·q™ ýp­ô;›¬‹;jÖðè†?E¾Q£;for£…HdŒNÛæˆ\:¾4ÕU’,Ѭ2½­#JÞŒ‘ëëÅ&J[#*òŠø‚Hví¨£ÅúÉîßU5ãR« $Å%šä²•å,Iž=ºñ­·"?í ¨&ÄýT‘¦ÎÈ̯‘¾‚úòåѶåoówëú馆§’DêF¹à§¼ ~5¾Ž!+„ A½û|::¿ï¥³x(ùTJòî’I[7µø(·¼ÖÊíë´]oæ#ÜlÈþ«H ûjRò—yc”Ê„z¶¶)bUaÐ_‡Î'NÌê´Š¶¥/)w–9L¨G«aqѼ»¤’VÍí~ -ï5²J‰ZXqâÃÝKû˜¿Ë^˜•åÝ$’¶okðQoy­•Û×hºßÌG¸ Ù‘ýV‘AöÔ¥å.òÇ)•õl.+lRĪ࿜JŽ?£7Í7èî°u1Á%ÆÍ…Yvh£=ª[â~FIÇßQ˜ ‚<I :‘çE–Þ«.½âßBa…¥Ae‘†£³ºŽau‘åÓµ­ùT!Šª"õ[‚.N¾ÚÀbý`“æC-­¯:oìðfx½ÿ.‚Œ‰$dßí¦p‘Ù†&;umQ¬qBˆ’, xŠÝYm»Ýyg—¾”I Rã /{a©´_[;ÁøQ +²·+ÑÖ(£‹ öAÏú=a.c]”ÜYGÆdD™XäD—ãÛ[ÇŽ&ã] « IY³~7¥OF…1«.Wß·¾Œ-:êW+ÝM˜ŠFÔdÙp"Ü#Ž5XÇ©[yÔ&(âb}àE½‰ï¢ËoU—^ñoCuUÕ†,­ÀÑtU® qì¥E"Œâ¾}Ã(:œÐŽMLI×íò¬šÝ€Q”"DOÑŒX {E4ž»¨ÕªDôx¤ÞhKåÃCÈ÷V÷hUuU$# †=iíHÒzî£V©cÝÆÛËuˆë-»)‘£I#:âý¾T$‘TÚÝ^Vì¥mÔqõ/­…‡[,PªäŠs{X±?—C#F’FuÅû|«&·`€e‘ôcžÑD'®ê5j‘=)7šùpÐò=ÔòÆ@¶²ý½ýÑöζâÃTŒ2ÿ2lñÈ."©täþˆÝ‹zØËÕïãQîEoÆž2ÝA< 2¾Ç êHü_’“õªëEÜ·øšÙã]ER<êS?èÿEEBwa¯/XÔP ‰^麳fn öÛŸemïÙà½õõµ°­¡eÕ ¹6ç­«eˆìÁ L âÆÅh,é}¡ÅíõçQîEoÆž2ÝA< écA…Î>¨¾•0‰U’KâùuG•» ,z©4A$ ôOôl²¤9Y”œTØ-4S²õ£ox¤qµÂŸ6‹fqeêÓäà®(aÎè@éT";ÈÔoDq¶Ð‘2Híf À…ìÕŠþ«u¹ Èïo…„ÐKŒ«&C‡û g‹h õS>Uwë,ŽÄx…ün Ř2D{eu§Kˆ°I,Bö{}´#}¾Yåß#åÖ8‹©7òì󒦨E‹ßÆÂ£;Ýš2¨‰6pÇAn8š‘ËdY‰ÊÖ½O€•1ÊÜ5á[é).¦<†­z^çûñúcœ ú x°B°®X3gÒg×ÄeJÎ¥½ÎÍj†êðç¼pUÑTKfªý§–¿P=å‹«˜“Œ4æQ[y¯xubzlÓæà yłÖêñÞTç'{Ô§³÷—™N©‘µú¿¼-ƒäßÇîé´Š–+Üx虡Ñ@÷\&åÈ2k"y‹Z»UEY¦‹¬]ê (ÛÁ_ìø»oaº,§yŠxFWŽ ·Švî³Û÷Æ›„+Ka3}D@é÷Í3RàΔÒÀï]ñ@,9õˆ.êöRû¼Õ:í3z—féÆžØ–ŒØÚ JµkùEdR-2Þ7?4ÏÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOÍOñx:¢ ÂXüWÒ;:—ž³\G¼½[Á´Ò|EGâ ~¼î^…AÈÎÐv@ÉŽ×ð¼­3 ¬ ðÝ}wâä1z,gÄߪŒ¹>ñYסé)ïÓMRoØ2³™n‹Z¶jöàË©—¡Ùi/'E4ÓCÔ›ö Bj¹}ÚÑÞ1ä'“¹ÜŒ\„ïx»ù@ª1‚¢UˆbᇼÓnmžµ`ô‡¬ªÊÁðY‰Vìˆ_þaU‡¿HvÚXdMº-Ýé `lË8zCª cuNÍø#Œhe1×HÓ'Õ™MtJÿ˜UaïW ^ÏkĨ-Ne`S{:aú¼Î…Û©íÒ…ƒÑaž9H¢Ýe>èú!ÆéâhŸiÒ ²fáèÒ@7R 1gÝã˜âŠëMˆ4nxÀ"a…/fb»ÛàAÛ¹ÇZ éE1hƒBl{œË®2n—•{'â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§â§à"*Dõ”/P·Gܯk”l®» w.z@©óoεñÓ,õ•£|ý>Ói»±¯Þ§ÚXP ;)†Ÿâwp‡ ö«™Ìoo¾ øýævô2¨¶'1NRÞ7Úþ4ötÏåï8,;i/‚媶·ÍUß‹‰‹"¯YlòæRx¯Is`øgƞΙü½âF4߇, Ý›Ìãw²Ã\@#’ë'¦¢=3ù{Î'Æê­]uXœA1NRÞ7Úù¾c0~Dÿ3¶eÜ‹— ÕøO©·s,™Þ¸œß1¹?Y±È±æyá-š¯aa«—²-J¦Ô»©ÃáÌ2¨¶'1NRÞ7ÚÁ¦æ9Xþþì$‘`#E³Ã–YJUyÏ5U^*qîcX.5E3`jÓUÎåÕ\3 'þ®%l2Ö*(l®5Îá]îmnñ±WÆ¥qc5ìŠñÞI¬naq| ¦Y21ÓŽyóÞÚ*° ÉyV™Dqµ ûÿQl=ƒÿ|›`‘Pêqgó°’Ú0_Ÿî+¡…MØñ}2PXMY\õ¾0ã¾g/¾çd’YoÉe©»8–XìÑÉü%qNÈÒƒäè­ZLŠð[—"^XÇJZ‰ež¡Ck¿r&Ù=@¿c±ä1²>e^ÁåU¡Uß¿3eÚ)]àô2M²z~Æ/DpØ÷Áñºš¿n’H!¯%´¶íæ&Ù=@¿cÑ3C²î8cwxV†¨B‰ƒ Сµß¹8¤*s^\{Jñr½”sæaà°–¢Ö‹%8¤*3^\ûË@µìUió¬ÚË,ÛnœA€0htS³¢hPÚïÜŸú$¥NV‚kMæn¶kž‰Wj/"«`W->ÅiìŠ"ζŠû@2¤A)¦ŒJ$wÀþ_38倕MÖß2Ëš9?„ Y¬%%£UÇ–$éIP# £!ÇüK_ï ¶+Ú(krõãïˆF%ª5g¯|GWk5EómÕs¼tä—íõåëNQBÜUຈ Æ´TäpÍÙX™Pnõ<»'»P÷ ;º<š[Cœ‰heõóýEqTí«=°¦Wë¬&žoO¿@æ%ipSÇQ2ÛXWÚ寮ÕP£ @ V»ÑWŒúCíJ72ðeæª nœšµ÷ Î{t¸ÕÚª´a¨¥„§0pÿNâ’NNó«†7]^;BíÛk¹q«µThÃQB¶]ú5.ì¾;X‹á@Ö„­.ÝK‚˜¶:‰–Ú¸¾×=O±Xó\Áè8ÅdŒßhâ£C”ô%üÁ4îhfz†Ÿb±âøçQp]ç/ð€æ¦ ­-¹´Qr¡hxr÷Œ@«¢/ï—%}Š+¸²í±Yªí¬#,¬ßèð~h„¤““¼ðªáÔ*ê…8Ž—U”B㊗‰\§Ü>ÃýA‘¹at^êMvÌPÄÃ<‡~`–¥NÁÙ%Ū•™—ûßi—‰\§Ü>ÃýB+Ž¬ÎŒ¦m8N«WÁøçíÿ×/©û²mŒÞ,PÐSwJ¾¦Z¾ÁК5îZøb¹éêÛ_ªñºëNQFÜWà™ª€¥(¤Üó¬y…†|õ÷Îm‹ì(Ögʦ Ãê ÊG1ÙÁM¿]á4ð{}ºZLÑscÂù†Ëi{¹zôÔºe;/ 4ê…G È®s0À¶×†êÞ9ñ1õ ½Ù¹DB¿i(¡eÓ(1ÙxQ§PIŽoœópN7…hÓ~Ìz -×lÄ!R„Òn9Ö|K¦Pc²ð£N¦OSê-Ë2Ä,Î,¿˜¤"­ùmlñ 7–Ò÷rõé©ö]¹ªaA4 dV·9ô§RÛ¦É{ãŽòÞªï~³ì»rþÜ`z†„¼_*óº]¶•÷.8‡‘;^ê_yŽ¿i,Õ>É…£À h8)ÞòC´¼43í=åBГ‰Ú”¯™”m×#f¼¬q¬Ãm@cïö¶,Ám±ßºgX¶Õœ9XV’Ã9oøv«öFº1ÌxtMPà/Â;÷Là+Ú³‡+šzŠ™ï°Œàk@fÜ“F/PšÐ°žL×ïW/·ÖMþä›c¶5׃ñÏÛ¦ùLB÷› ÑCÜœ¸oí/!’â­[ïcZnŒò>&¢œÁûÍßßr-•@¥vbP±!ZÁ£;„[®ù›~Þ Â;UÞbÁ–V¦<•Øf£ƒrʳÛ(zË3àTÀ·Eû?…*»BYo‘»ê»ê1˜§+øÆf¸>ø7õ ¿ÜlTñ%6 3·/&NÃ+b;sÞ«¦ %šŒä+—ùæä×d¸ôÜRU@5Û½¿R#¾Ï˜ÝçYÇ+Ü#‡c^8fõüÎøje‹¯#{0ÂØ&½}7B›à‰)Ü<ñ¸vsÍÊʌޖG,¦»îaòf¶Æ-æóâYÃN"–¸7‡<2ÃÌïh«ã£h¢ÜʪÛÈíÔ1…,䥡4~&Z€:9Ùže™9Rüº¼÷óÌ"ØüÄG'9yвC„Ek¸@ˆÁVÙ°ß«™DõH4¥—ÀÝÙ)6NôOoálVÂlÉlóæ@¾¡½qx ÿ×uËýÁ‰¿Ú“l ‚k"—¯cH¸KeïÏ1´|0Š<+‹Ä«D1H+ µqc¢fñ¼XÃ=Ç´t©WÈÙÁRþm? 7aVÔ-›[ï…ð3ÎkëtÿîçD#ům“m3œ_Cä!| Å ñMS¶ ÷¸" âÎb»c1cv±Z{ŸÛÖ à‰v«ÏÛÀ°Fƒ¹´.¡é%6ó`¿Æ–¶ÈM/ÌÆ¡Û€ W|•'JE|âQš³±îwéž¡§Ø¼ø¾g$¿o«'N ~ßVúNuû³´&ØVÛv°'¤a{áù… '¤,9Mq–j¦Š Å­Žbƒ€Í¦C¼çš„ä UšyüB‹…ñ·Äµú••Ê8ã,&ÕMA‹[Ç#!.”ò¢Ö=œž¶Ýœñ)G ÿ´WÈkÛ;«?G™+2ÇCF„oÌ7j£Bä|LàFÕ鯡Žb!£7¥y殹¯iª}–7 ÛÍâšæ–zŸ4JÍ ÷©ul«]¥Åô¬9µä÷;EâàQØÐOŽ›Ñ¦ÕŠ8è"‹RûM4Q_ˆ~~Çeú1^¬~íä‰@ß7†íæñMsK»hP/³³7øU«¾iÜ ¿(ÎäûH#혒km¢aÙMxXü¥/˜L™Y®è™íÇUšÏÈvã½â®/K»Â‡n?ßDÑ[n—Z(¿)`Ðm§µÏLuâñý‘ *€å—ìhúÞµoe‰¢¶Ý.´Q~R2«LA†)¾D¸¤Ð¹XªÔºÑM4â7G­ì¡Ï5¬A&ŠþC„¿ƒºk#öºí7–&»Žü9Ç|Go ê)¦‚|göŽ\þðÄßìɶ š1¦5»»â\+jE"²XþS›9IûÆæ«h¢ JÐÚf3JºÛ&-nÀ M«Hg´û˜û­ëÞ"º´a‡Ñ§\nj¶Š Ä­ ¦bNJÞH,™+È=åÜ2yÌv¤E0)sជ(“K¯@‘3C¶ì'(™*²‡CFÅ' ÄÔê¶U‹ ‡)_Y\dû Á£˜•6ó µ¸†¯ïMÁ† >"•1…9J?¬A mÊn"ì,Ógö8µ,=>èJªòÆ0%­÷—zpÅa‚üx‚×FÑo%ŽÇWŸ‰Ì­4üš5wéY`“à")SS”ø  Â©;Ò.¸ÙÑPPyqÚ71‚U®VÒæSpõ“s‡PÈç3ÞÒ܇4⧇v^œ°$, Ï£“£6´GµJÈr™™’y|ôTþ穹t•È5¿±c6´GµJÈr™~ jÿ¯FaÛ`dZA«£ˆmyŸæH"_q”§ìÀœ?ù …¾+•EJ§ܽ@ÂK¼Ö}žJ†ÑÅ(7Ÿ³s¯ß›ý‘6Çf™†Ø¦žkèä8~‡ÔþOîˆqm³› þ¡¡ê Z½]~½Ò\ån”XîÑÉü>›ªÞ[‡oìKYoE?ú^-6sa¿Ç«ò¾ÿd eàXçÜ?hç?¿17ûmñx:ÔZfUëŽiO‡¡HSQ›¥~Ã2œÝN Žú|‘gÌsAÊlö—y²²7ØÉÅq8Ÿ(oO˜ …Ê$h“¡…«Ð´ts…Ûa–>à=ë>¼qPy‡Y¤7«0A ”Ia#ªx-•Øö<ûÏMÜöš/¦¶¡Pã}8”öðâˆÔ«¯ £Qøìõ˜à ÌŠ^"ðÝx”u@©+dÕg¸°1YHR×Ý5ÄYóÐd2ƒÛ}¥¥VÛèâÈ(erÏiŠFªÚæô~ÈHX_o°½<ø©šŽ(¼´î&àl/·b’†•0¬ÝU¼RÀhy\¢±°0Oj?&SË—y²²7ØÉÅq 5}¸K;ŸÉ‡2ísßþ—í*Qý"]þÀ›a « Uó9}ò3n¯èˤ…´¤|ÂÒ×@ ö:*ìh|"­¸E°([^ÇÄ õMo5Çm¿0·éu¾ÖüÁ)j1UW¢“p-üJH5ªµK¼÷ÈÃsú–M%åJ•–Ñ&gCм¦WÖ7úäÛ¨ÛÂÜÈìãïÀjî<ÒÒz/=,ˆWí%¶‡_4ž‚ä—7@?§¢÷›”Èqc³NÎÓï`€Ô#‰[k=ÿÎ"Â>Š*]ö8šÍI@©Uœ½Ì<ÌžÉÀÈšpGdÆT/Hx‚öÚ.ÁCÕ=7RŒnÛc ã„§êKäÊ=$ÎùœpÎs3° ¥DUÙŽ&799ã`™¸o!ÂPãƒÌF‰¤.–¼—¡Jðˆ*aSéÄ"šøñš*õ#?mT„üT¥×]%ÈFZŠ­ ›0Žˆì›^ZöYº5³†îr ´rnÇ>Œ®»¨‚Žá_ å9ýaTH: c Jéé>³£ªM°Ð¿,ÁB— ×hr¿d„}“‡ K>¡äTX eÅæjAtéœÞå'„cE-¬¨ÑP,w}]^Bßiâ|P¶[Ç3yôw|ϾPfZ>Å~0åvPçGS*MSYìh¨.„íS;r5ÞŒ{¬gqà6ZÏK˜¹y¼_9f\dùƒ fË1ÄìT (4¾9›Ï£ºæ}²è“C}’§àÀ¸¾Šã~%IWAšçÿÌ<£ Š,é¾o*»{a0EB;sTNÜpD'm.ÌÍ?ƒþS—§t:UÎf‡RB ]Eú&&ÿP›b‡¶†Z1n½s‡ lÂØ«%rwú[ ÷½”2|Ü•ZöŲQN.l6W¤âú"³7ìŽ7¹qí¡C–ÎŒ°Ê$ÒëÕ$Á‡5rÛ‡ˆGü¿Î°[}¡w`jÅ[Éþ¸…Å^n¡WG ßl™±Sƒ&•·]Ž/Ã8¾†H¬Íû#\£ì)a)3ãJ$¥ÖÒpôØ©Á“JÛ®Çီ¼û$(TãN-&¹=x‚ Ø Fö±ÕyÔßòÚ=â|̘ ®/N;Ë€]¬ðù¨á^IJ}¡ ë GlÍF|0ÏlêZM!tZKèTdʳSÕÁª] ¾N&"nïUæ9r¥ÕÚÓŸ'F§¶@^@dñ8¾†H¬Íû#\£ì)a)3ãJ$¥ÖÒpÿÀrþL:ƒ¥JœA™Q%J•Õ `ƒ+ë17údÛ´…à5Áç0a@©h›»t·\t´ñV’âòtHi+TQ_|ÁpkÌØÙîáÄrý–.Ák‚[Üá<Ö¨n¯{İUüë«ÍKÍ9ìaÆébz¨ubzlÓæâáÛ;S1VnUPA˜=µðýæwÎÝáÛd`}wºqÏ,lDHím>Éó-îpžkT7W‡=à#‚®Š¢[5WíÕ±#µ´û'ÌK3=ï@Á„`÷/€æ%l[†Ùa^‘½ñŠ[T,] *½6ÍT1šè)}Þ5©ˆÇ9A 8bßÙ¨ò g;²¯t»ìZ2…Í{$«IK¢µÌ#¡#ÙXö``h•n×Ú¼k‰T$FÕuE|±ïôE eg|›–÷8O5ª«ÃžðÁWEQ-š«öÿ€çôjÇ §PéX•G¥JJ•Ä•ăë17úDÛ o  ®ª:®7Š\œÇµFEð]”µ=§hø…ŠáÀËul“g5D]ð˜O1Øñ>ðlÓ† V™Vøn +†ï>É⺌é6LÉßf·,±Ù£ƒù}Ø_ú,%x¹ ^›9ñúIšuÁ´BÓi6?];´rª[Ùo了€ªŒ^‹AÃô´,,]6iÙûw?Y*CÁ0ëÇKëR¥D©}.TH,HŸY‰¿Ñ&Ø%:àˆÖì„QŸ#`U4ïœh‡5Ì„2е#Ô>*Άÿé‡û~—+ýÿ39í÷Á•?ÜûÏ÷™“¡~I ív7ûÖ{t´ñV’â‘òõzëÀé*ðHž÷©a‚¼ã`²ä«¬ve°(_ßßQ=5¯ 2ÁÆy•5³†Í*Ýk9e寇GÍ ÝÜ©Ê,˜ß$EÕd`(ß.Øæ_Rº!dÐ:§µ½xQ‘`Q.ãi„Ô´/HZ†þ«_CÄl*ÁE¡T?«˜ ¡—«t«yËb¤D¶>œîú(ÉAÓB³ýCÚóÕÚµþ¥‹]Ìúˆ+P`Š?5+ëXCUmÉí]÷*l¸Xº*Å··ûJc2B­´;;‰Õ¬ì(J*, a{: ®àÀœš7³ß\tEȳ¦Àx³í2Ð+’Š‹˜^΃+¾™Ñžr†Ã6þÆWœ•¬vKW˜ó­g±ÙØÏ7û¢Ù¯)ÇþåÕ\3 'þ®^®pGN „cMâèûþ»—èHt"¾„+ ‰™P:¤aƒ¡•* ýdýmж¦!(0!Õáß¡\v‡jŒ‹ä¹T5xöØ®œ¹vÁ6af ¾¤0#‡ÞM ræ+V“"¼û®j‡(–îPª­(úÍ/Ä}¶e‡j‹†ªjïzú´?ðXBÛ×h·Þ"¸kð‹ì±ùyÿä—씢ÖÊ!4ù>òkƒÓœ ù‹îVw>°Î`·‹>c^VÁ§ºX]ü¡A@^€Á·¦é³v¸¥‡¤ ƒî ö@Yùl.¿–6ч€¯ãy’8m{ày€ª¼ûGyW;šoõÀ(âöÕUVª±ZÖM¥;€ Æã"OMƒ;ç×pCW€`˜_{·V¢¹xœh” ‹GŒ°i³ ¢ãÔ!€¿s(±Ý£ƒùtd\ƒîL qy v”\z„0îeQàX ÖKƒÛPÖ°$\R“ ©XÖ(”":WHØÏ…ÊxŸÊ…rÓìVžÌJƒm5lUß1ŸöãöîaÐ)*WS ÂW×¢WJèWGèAú#Y6ÀƒÍ6¿k/´]r$öçCömfšñeöw[]ORnÀ¼ŽÃÁ2ñ+”û‡Ø¨Ý’vŒ¤ÈW³Žòêg4Ô¬»©Ußž`ñÉ„¨åÿQ¨1èbò³ß!®ŒûS.¶‹5g<ÍŽGBaò´7¾y” º••.Á{醺­(©º  Ì”}Ëgj?§¯’#ÇýÚÅUòa™±´ß„~3…5©¾ˆO[dÃÿ5Ï×_@ô5 Ç ú ¸"FŽ%Íôz=gC+ôFþ¢mƒW®í-æÁö”#;µQªøÏŽ–D+òÒ›¥¨v–ÃöAö‹2Ñ­Vë¶¥Â&¹Ÿxwî™ÀV-µgVv87Á7¼¥t\7ý]T€¨*xÕLó~!3ÒìÓ©ì¾— Æ’ï¹ÄíÓ -ÛF>ó ¤ € ®ç1`=–ì߆Y9e mÀ 1-뀾P¥°¹ß{ÙL_¢ŠJ\î0ë‘,åݹxÇÄuÌ¢™S&øg!w[ÿ˜å¯Ð%@ÄÁ—d 0›‚WJˆ• Ê# =1Q"uN¢u~ˆßÒM°‡T…3]Sï—>-^4™ãûú, ¨S^©÷Ê íAL[ºyâp¥WhK-ò#,­ îD×Ç—´ÊØ[¥^»Yó ò•à3(·]óÓÕwÔfj饲®IŠb̳üßvpãØ¨†‚ð¿7|S}ȶU•ÙšÚ…ÃG±ƒ-7FyQN`ýåPôWI½dÁko’:-Í·ö˜1¢¶h¥ï0x,4Mëè·6ßÚ)ä/γÛ)ó)rÏ–VœJ3 m‚läî=ˆªjÑËT±jö°Uª3}¦ÇsÇ¢ó¶h¥ï- ªE õªg›…ßÚb˜³,ÿ7ÄÝœ8ö#j! ¼¯Åʡ讓zÉ‚Öß$"©¨G-PrÊÚ¨K ݨi÷6ßÞp¥WhK-ò#ûW?Y/¡ÑP:õr¥Ô[•Ò71QF‘?Hoè&ؽà(”Ú™3й8¿âôfUãŽm_“ #BÒÏX2æu`*•çžbp¯Có6¸ásF2dÏ’>"ÁÝUü°Ž"À¿€ø˜—bÇL=4ik£‡Þ^ð×Çþó‡3ޝÉâR¹Z)ÐWi‡0qìŽë8ÁÑùÿN#‡c^8fõüÎøÀáeºï‰{~¤G}Ÿ1»Î³ŽW¸tOU,Mæ7Ié¿1%n€½Êýªz÷¸,²ìßaQKwœ‹žþ|L·Ë1N N­L¡ñ¹x ’_@x©„`°ã™6ûKÐê /½¬xëSüÎØ÷ÉK«sìÌ»7ØTRÝçbæ}ë¼*à½.Jµ®%ñÕçd‰Håh§A]¦ÁÄ ²:?¬ãDcçý8êž ð`Bé,ƒƒí*Ó¦Q†Ë,âßNuèoþhåúø„æ,f=•tT©ƒ,LÒ¡¼N%n,a:G£ŒeDý!¾¤ÛUý cßçè`p²ÝwļsË~–gÀ©n‹ö~>¥²¨®Ì e€`XgÝ:¢¸Òœðƒî1¹ ŽW¢ÙT Wg£CܪgÇÒZˆ(+ cñ_± e‚`ZcÜlçô `Í ʕÔfY‹pV:*Le b#ÒØ—~Š˜:Š•Ô?Do¡6ÃvÒãÙT‘ÆßîK›+Z™}š ·ØèˆÐ°£Ò,"W$A±EÛ/&ÐE¹•U·‘Û¨éŽí ¹ŒÊNRå‘wYRnä_+ïÍï˜pUìõÈôlFÊ©V´›Ã=Š]­rË =àp‘]•«½>ðK×V¨å̤¡ïBû„&ŒbªU¬ ±Ìå%@‚5¼‡Èa›B•R¼¯ßqä2ÑŸ¹Øà‚7Ä»t¶Wxmq­†ñæ$°¡7¸/µ¨‡ï+<®uòRÚμA‚¬émj¶0À·!¶œÇ%HVáêywƒ(øR†žkæp&™9_kÁžÙY‰I±»ç·yjAÉ1 ›]9…‡•Î9/~b¬á…‘Zànaòf¶Æ-æóâXoKY&™u@dË“ö¿F!À;ÅŠÏ+|”¶³¯“5¶1hß7ŸŽ]8ŠZØoxˆ ÜVäÓÚîTþÇ­©UõÈ»ù}´µAL+wÞò¤©c9ù•])›p‹åÁÞ†¸ tU}É´QneUmävê;ij‚˜Vï¼rÐuäCôÜ¿¢!ÐÎzá Rº`Pó6 À‚Ý< Âö”ĦbKF1B/ ææß¤o¡6Ã0S,BrKBôNí}ªüÝ…ð3“ÆØ" [v+qâSo6 üe*3п1£ß±\ç'ÿÌt¨›¥Q5Í5äalƒ)nÊDßY˜WË[ºï¿ê—Ïãöä¥eâíþJš´â¥ëJ‘XãüHeÑàÖ¯÷ŽJ¼ž¸qعA\ö­Ãå+|ÁĶÞlùÅ¥VŠl°Ÿ2ªBà#,#¶HnŽðïŠÔ>‚—O|Æ&F„oÞYe¯Æi¾Ïxíoˆ€mUõvâ6†@ç…qxèÐëxØ÷Cí¢œ6Ñõ|Þfhåí^3i~ð, îm £èzCä!| Álƒ)nÊDßXb\R ö¾ñ–Û$·€GxwÅj! àfRÝ”‰¾ð`œ,¤!EXœ2Òéì­Å ¬.ÕÅŽˆ( xwßûþç86ÐÚ§ ÷-KÂËê–! àf*Ñ R Âí\XèƒÒÖñúN@ Jê0„uqÒ³0fxDÅ·‰wC&R¬"ÔȤšÙ e‘ÜuÑŒcÑŒwú‚m†$¨ÕØÊ5ìPi~ŸCª°·x.a * ›,¶Qyá£Ö„vPçšÖ!Aµ´7˜%Zò»Åy†Öøˆ_Gn&O.ö#›;ÐÖ`Éu“ÓÐFa(ÎÍ}eæ²gqd;È ÏêlùÙgñZÜ0]$Ž ]ö¶;±/²ÙìútYòóÃúŽ%vi²ìúzÞ&©EVmä2~’ÕY[¼.‡0Z-â—Š;e>" U#Çü‡? 5‚Tg l¨8ÄÏF¼Ì2CÆ „Ô³Q§¬XLÒÄêNŽã©Q‹;ý#¡6Åg<˜ÿIE¨ û:&eöê<"Ïg¥¯NIkªÍayOï>d‚%÷Å\‚Û\ú( ÿ @â©ü$ JÝOð3Ð…2ÂGE|ëpGè ƒ»úÚ˜ œ´6p‡6ã†Yáâ<^¥{î·—“ Ñ@ ›È…‚¿ý;’ª¼±Œ k}åǘ¢‡\ƒÆ«\ÓPd«úYZ­¤±ýZ¿ñÜþ’Š Ân Í®æ/g0SˆP•1ažIX—'¾”2¸çs7ˆ­*=*äDê~‰6ÃòP§µÿ_éº66­U¾õ2svت ›íÒê†w–Ç!ÛûÁ[ÑOä:,aKKZÏW‹MœØoñê á»À²}E¨‚Êð9ê‰öª–ƒÉÓt—9G#ÛôŽ-¶saŸÏ¦Lë .ÁËKõhAzƒ¯WG™B#µV­X––µžº^ Å«ÕÑçë3¬•äÕ³)Áz®À;\}NH:>„¸ºœB Žè˜1<ã âv9—5%J•ˆY1Ærþ¬Qê‘&£ÐÇ£õI·èAZfUëŽiO‡£iÛ£c¶pÚüE,;a`86gd¡©rk£@½e¥VÛèâÈ(erÏi„Ö°·;rÊ7Åuy‹ø‚P ÛÊ#]1Â:Í~#Ï]”Ô4}ÐTxáGNk8¦Tûv’º—/YL½T¯¯`kÌAx¸uºñ4Åz;ô-_7´î& bfguÂS ·‡Fº!±òÖÔ*o§€^ÞQ—y²²7ØÉÅq*?½ "ña§4öQÕ!T[Aľáþ éBF·“½¥|“raØù+£V“|/µîã”v%» |ô_þ½e÷15ˆlÕ¯®¢¥̽X~Vc"Ûäy |ÇG8Q-¹Ö·º<Û?hü À¡‹ n/•~ì›%½¶¨Æ`”öðâˆÔøÝÏi¢æ1€¥5YÌJƒ¨^ök¹µñì)}x²ßnœø©šŽ(¼´îj  &…÷TÖLMÒbÍÕ¯Þ.·!Œµc»ù†øÕûÿîK¼ÙYáÌd⸞. N2•]‡™Žæ¶éO+ß~3ÑOé„ •BF¥Œ4Áz‚™Üw¨ °Ìú™Ôn—]Žò#ø”6¢Æ®jÊ#¾†TI®¦1cÐþÔ›a]ÀKuF1ˆ8d{ÓïôQ«ƒÞ5å?¼H;½î^{ä¸_äª.#Óißò…÷™‘{o¢ñQ •a)>/¡@\ÛUÿÄrÙžSÖx–/¯ÌE3j-Ü5Ë7ù˜ù^;eYäÙµòLák²ü¢ž[9~;FÔCeXJO‹èÚÑIé ýТì¾ÇÄFsnì]h¨†Ê°”Ÿ7ÑÄØ]ŠßH]l¼ÍÂïí§.óçÞà:)oÒë}­ù™×‰?Y5Ún¢ˆð]oˆ¯6µé§ô÷ ‡ÐKè!¹¢¨Þð¯xiÎfV‡d] ™2êŸW1×h0¦LE¸:ÜQq/£¾‡ô¤Û †WTºMµào¬q9ê«·O—¥‘ ý¤¶Ðèl(HYkï¦~Ð VŽÁzDhšBékÀya[›«ÕìËöÇâ™Ä È׳¶¢à Æ[ÁÛ~õÄÆàÚ•ÎÀû1æPð‚¸ì/J„ˆã@)CUØæ­ì]%fÝ{þ Á´îKäuÛg÷xQVÀs›öó7)â,Çf¡†Q‡|÷·ý‰cáËØV²ê=Ã̈oMðô£ÒA ï™Ç ç3), )C^‡3œ¼ Pñ´NUîµ2hgC+£€»fà;‰»¦¶ã´4Ÿ“rYÖ[íŠæÒ°”ã/Ñu çkÜLšÐÊèà.Ø](“ÙŠtGdÈíÝ}oû–1^½…k. Á´îKäuÛg÷IEA6à8 ¶TMQg†Ž™ÙZr8•ˆVÍÅ:ŽX°£ÊƒÄ°ì(}9âº1ªæ´¸0Íì—àCŽ ùïl4Ré  ËÁ‹æa4‚47ØâaÍáCxÃz›”Èqc³NÎÓ ã7#°ÐYÆnnGZÏrñ‹ñªÙ¿R¦Šî-Ygy“Ù8Nì˜/zÆ™Ú?gIJX',€^róû'*aѹ}W@XŠKáñ Ìxˆ¥±Ê · ©ŽîÉèÙc»&Q%0 ÁžÐÁeWè‡ÐM±¦¬¿‡ÚAÄ"²Ñ¬ñÏÐëy´ Çûšžw 8ìÍçÑÝs>ÙD©câ0mÀrO¸&¯ésó*s“àØòJâÂ冡b\Ú8(õ¸hÖKÉKù]ö¦]M4îŽ8—Kê }‡^Ñ\»áiŒœc;¹þ„ôÕïâRxF8RÚÁŠ‚zˆ ¼žøûÎD}„±ÃÞÄVí–´8YÝôgqà6ZÏK˜Û”À4Ó†Ž8˜v#7±ßª±@§_#ˆ»Z¾ÅŽL®êö{¨¯÷˜†¨»©a§‘Ää"2ž]g‰èfM…qþâ.ÆÖ¯±c“+† r¨j`\^5æW1ЉWT ÿœÎD}„±ÃÞÄ廡2 ¯egØöK¥WbÇ&Wyôw|ϾRü“¢"•¾Kgí2"X8 ˜Å){°:\Ï8ìÓŒ&¥†7&©¶ÿ¼Ì»šÐïÔ[² d-£¼s( P•\å'„cE-¬©EÜêUqƒ}ˆ6ÃÀåö˜Í*”ïTV<2žyóWl¥ìlâ ÁëGس Â`–pÌ› ãýÌå:½ß'ã·ì°Cé.ºTfX°_C%‚nŽåMÇ¿S©ðÅ“Yé1EÐcôIê ¶zñ5ÉV=2ˆ ¸¢½¸íô~ç-0>Ux1%Z‚b˜<4±sð´÷!òÿ:Ámö…Ü«o'úâA;”‰ÜuU›Ôƒò°8´öÿ\%ŒÝË÷t÷:†®‚w_ÚîõU›‚*š=XA)!¡–¯¯ß‹èdŠÌß²5Ê>–ò’€{#ŒæpÆ€JÏÝ­9ñÓ5Ï‚|—Áf;Ë'ÚÞ²pAšñÆy”";UjÐA”4pÆýÓ‹èdŠÌß²5Ê>–ò‘Ëî*º5Wæ"º`bô*CÞ ¡£†07îœ_C$Vfý’Éz¯U©‚–@ŠdÏyƒpÊ$ÒëÕ$§ÀÃòn0{ÜÇFØ›5c7kµĄ̃šÞ6#ÚPˆíU«APÑÃ÷A9W‹@iþ!–`€v°%k²Ã:rªó©Ž3wQZpLãÅBX¸ÍÜ¿t>^ðÁoÙ4ìÕªŽ ;µÆâÌÉ,8ù¿ÊØU©¬_¤®#Ð5¿tsÑRx (¹ß›ŽEÀGö˜ 'œt%•F8q:Ë×âg†¹C,‹‚7Ê:–0Ç£¡r³ÑHu&Ø õjÑp/´Ú¯€^ôKOi.('E Ë@oÜžð*êa²ÓêÂNojgÝ"áÛ;S1VnUPA˜=µðýâ^Í8aý–Q 9³Úø˜ÖDÖL4Ûçn”·Ð1mÿi’ëvèÛ§‹©[Ô(KpÓ¾ø÷Ÿbø»zK{œ'šÕ ÕáÏxà«¢¨–ÍUûO-~ z5ËW1'iÌ¢¶ó^ý7Æu»ƒ1ç(!‡ [øs/‡mhkiþgl¢IÌÖ ØØä5-îpžkT7W‡=à#‚®Š¢[5Wí2È£³n2Ín( ¿›tãžYD“™¬±±Èj[Üá<Ö¨n¯{ô"0Û¶ÕÚ%¤ý@Ô¬Ìì+Èh÷™õñR€³©ÃíòÜÔæ›~"Z2`¼ã˜îà ä;-¹îñ?Ìí”I9šÁ{†£€Rûâö„cœ †1oá̼µ y¬=§’4#1¬‰¬˜i·ÎÐP<âÁaËuxï0z5,•-Æ”k-ÄšÂiöbw W»Œw©MMÁL/ccÔeX §ÍÇð¨Ø‹¤ý¦6‡Q‡AÑpܾ†Ûƒ)UÆÑLf¨b#AêåÆ1è¢èqŠÿP›`èË"mVÒi~Ð=ò'Ñ€9r sí¤Ž_¢†EÓ¾3,º^hëЈé]#c(e½–þJQŸ¦«/Üùˆªü"ûJ+¤lz†2À0,3îŸE©n@SFÿŽ‹†õË,+2Àرá覚hz“~Áè}¡ÿ‚ÂrØMWøèFÚˆÕ,ihc¢ñÖæ v6–ò'иoP|±T’ÊFœ'E4ÓCÔ›ö áÆ*ÎêÛúøÚ•TtßRS¨–²’is$±•B¯ªþ…D‰´·QúÐM°CÂô»?’PV꘣“„r¾8®–ž*Ò\R>^ŸúaþߥÍG+ÚÿÖUà‘=ïR˪¸ f:Oý\e°j‡B°wÏ÷*íÚ( ÓXí‰}EXUb‚±½z 5Ì./·J[è# ¶ÿ´Kjã÷×¶ УP!ûqߺ-šòœî P)E[¬w¹|ãOð?Ô¼­,]!lcxmìôºSžz!8j‹aÂÇŒ2ˆZRQXJ¶Ù®12øïÏÙíKViÒ¥CŸªr<¸†º­(©º !17šÑšùgËP×9-E")yV§t0Õ´9«>Ðír>¢ Å8ò•x¤O{Ô½”SVÃgs/3hn‹J(³ZÄyÊk SùqѤ}ÐŽ ­ž™fY+ȺzÔøÓÙÓ?—¼â|nªÕÖ“ÅzK›Ãôx€×ìxü:¡[=20Ì ²W‘tõ©ñ§³¦/yŠ÷JÒÁûŸÕÃÓ˜ Âƒ8éy†óbRÅЖzEâ\ÏFâ—.\¾EÄqG?ª&بElÿô”y©³èÊœ¶‡Ìúé—Ö"¸kð‹ì° +–Ÿb´ög6«®ÞôøŠŽ)[ç^ê ”ÆVø&qË*›­¾z{ÃWa” Ác³J¯¡fÛõ +—Ö|.SÄþRÁ·ß[Ð×´,Jl.\ÈMÉ¡²8I…äÔ¼€n‰µ=°ò™j5kÐ%Ã'¸Pg ÁŠŽ¾i Ý%>ðy2Éâ›×xŽ«–Š¥X¦^H³XJKF«,ãêö•hUw”©°³Wt+‚"¸kð‹ì½¶†G„qÆÙ…WKƒÛÞðNÛÈèc²•Ñ3C²î8e¢Úލ0.€®ÓŠB£5åϼ2M²z~Æ!ôŒ¬øËôy–öŦ0cÄÉAa5es޳üŸÌîm-à Râ¹/o¦n©š”qÃ-ÔpÕAtvœR¯.}á’m“Ô ö0iÛ]N%®Z³Çìqhu¢Ó©©rú;c.à§”f_F?Eô1bŠáú&¾‚mŠJ[¤;ÅãX5@œ ãƒ*îwú¸ð’‹Þß8á‹1õ«îYåÈ`ƒ0zQ÷,lp8e¾S®7=öXiaRUˆc<0H¢.³Z¾åž\† ¼û:®'Û굊”pü¶•æîQ–)`N1m|Ë2Ä,Î,¿˜êíf£(¾mº®w‰ßŽ2»7—¹žO6„Ìç˜u¡ÄW»Øß’T°ºÌlÀ×[3î/¹O¸}‡ú™cί{8XsQP–*½Â|Q”Ÿiç‰s,^ö}¦^%rŸpûõ)ãwÿ£HBêB-ƒ ènâܺ—ÖËÐň¥ÅúäÛ4”A…•;çb"8PJ÷Îx:Y¯ËJlˆÞƒØ*M50o4»ïTÅ윣·4K', d ¸¦h‹F½ÆåÂW»Ÿxû®$71{'(íÁM:&˜ªð{>h #\ Ê2Å, Æ-¯žƒhM÷-|1\Ë„ ¯w>ðÔòOT/Î%SIÉò|Ñ[9hC¸Ü¼)ßÜ—øߺgX¶Õœ9YJ¸0?s?Ü¿¥‚ê6ýLs4­ø‹ÈÎåÊVTåϺŽýÓ8 Ŷ¬áÊÃmJàg)ýsˆú\0cÐ ~‹Š= ¹qz1„¸Ä‰?¢oé&ئó+]ÏØù™õŽ2 WŒ+íôU°5~ŒƒRo WG¿†o”Ä/y¶S=âkÏ,/¿säŽÚ(tžÝ·úŒNw£>|BÏDH&kµ=²…‰ Ö Üf`.£ÿ&¢œÁûÍßUßQè‰@p[îAÛó¶h¥ï- ªE õªg›…ßÚå-Þlw‚+­Ô%‡êcšèLjþ‘ô“l ¸ n¨Î  ¯k}þ€ª Qn_Žÿµ e‚`ZcÜz6¢ ÀZü_è¢rÇy›mý¢fc!IÓn²£ÐcNjŸ¼ýu•ƒÓ›mý§žÞSÒp19×^l·ö‰™Œ…'MºÊA9ª~óïÝw—©É_ª¸ª+Œ¹pëqK‹2ãKê¹—Ô[‡BÊc~‘õl4ã–ˆWKÜô>yzeöj<&ßc¢mƒ²¹ ³µñÝû±ä2ÑŸ¹Øà‡ÆÎÚZ ¦»ï¨J{Î|mñÏ1WàÕç¿f’ÿzñOnüVHp‚ÂÈ­p7И¥D­‡2åã°Â³µXÆøó,²Z黥߃ÌAGn9ÑKªÏ¬1…,䥡4~!½XhÂgü3Gš »U2áÿìbKȪ'5ß‹Ñ,c-:7Èæ 1 -ÈsNx‰ì®ùVø™bã¨vƒ"Ž „²Þ|ôékùùû&Ÿ}‡˜3kžhpeféì‹—j2 6^rˆz£v—\¬Ý=‘ríFAfËÎAñÓ Ï¾ÃÌ ÊâR¢çÒ{} 0ÀÜûì<Á›XÔóCƒ+7Od\»QY²ó|CÕ°ì¢úï‡í¿o­ÉúÊ!h±g¥ô¸Kú ÇKè^ƒ©Eú¶ßQ6‡žøHjÂvôXò»ÕùO K%%2'"MªOœ+Tׂ¡ò•¾â­Å ¬.ÕÅŽˆmo8î=±†O²hžKãüƒZJ€Èáà¨Ðw6…Ñô=:a{ˆÖ÷Ìnz°Åo»»œŸö³«VÝ÷ã=èÔß¹ßð”'Å5NØ/Þठ]Õí¤K%û&³ÀûMà)4j¯«·¯E%½‘æš¶ÆdhðyØFýæ0àNOf埅…ªäóÌÜ]…[P¶mo¼[Ï+")JNÆ1 Ë Ûd &—æ%éb™2_¼TCÍ›~{Ãå+|ôr>gEa‡/˜¨4WU9'Ì eZÇÞz˜€4÷;ŽéïLtT+ªœŠæ.¯€ŒŸ‚[ÁÁí ©ƒ'­ìŠx¨ÃÈ{ÅôJŒ«>2žAÉúÍ:WKépŠ Y}Œ¿¦úX_³l^sÁô”á%bWÈkÛ;«0w˜jämu)íX©ÁžO9€G^Ï\Œá%b#`ºc˜d»hÇx/–#ÉÉ~ Nìñ)]O‘[ꀪ‘ˆãßM#jÞ²'1‚U‡÷`Œó¡Þa« µÔ'†@CýõóŽ ½ž¹¡²zˆAϼìœD¥žëï(øÕiý³Ä¿cGÖõ«{,|í U@¾ÎÌ&óæ|Jøþa,LÔÝÿž7;±ÚŽi†ÎìKì¶{>ݨ‘ñ¨<1õ¿“û¢[Î;lå<ݸƒ 0[)ÄQ¶b·\”Ý^;ø€ˆ3–– ú;UæRq{Q}Ò!¶ceV©Z ¦g™+‘'@½C]¯î†ÑGPyñ-(ŸýÑP¸¥@åÈqÅCbÖêéþ¨myŸE¹V¹¦£6´GµJÈr™ŽJ+ækü ¤ |ôÕö¶—ióyÀ2`¿´*Àþ–V«i,cÖ¯é9?XÂ\0è ²àÅ—7pFâæ:Ž#ŸÕ>²m‹Þšj¼EØŒPeZ8çâ@+•˜yGx¡ŸzeW£yjYºsŽCÛŒ(p¾¾¾Oj ú£‡¥Lr¨âö“˜ÅI[Áò‘+ˆ, u^øƒYÒ^ÙÛЉ#Zµ5—‹¹‰JvÑ{/³ßW)CÝV «{`˜ ëw‡Þ:Õ—d]ä>†Æ·€ø,ºE1 4˜¤¶TB•ÜèZˆ,¯Añ_SŦÎl7øýOª]|Ý]nßû€Q‚òúpÒgXQvZ_¤µY^ƒâ¿QÉûÕz._I‚\p‚ºrýZým†ž­Óü1ÕÀY[`c]Í ÿ°¿´ÌÍÏŒjôX€Œ€¢ŧD¨:…ïf»—û_óªQtS•37>1«Ñb ¯‡ÿ^²û˜šÄ6j××Rã*¥¶”îßïº| àqÒ7ßm~­=% ÜNö•òAûœÄšÈàñâUqîßï¦äý•Á—Ð0‹eè¿Ö®ÿ¤M°PÀÓ}Âq 8ø€D2=é÷šüAïëP‘ªºÅµoØŒ \ \{¬KTœ‘ùé¬Ò*ªõ,Ï„[Õµì|A²¬£¯cà™æâ+âÎ~ìþM?n¢ (ϱ AëÈ-;Æá…ë¶bÙU WwèÖÔ*nSÓïÚ²Sx¨†Ê°”Ÿ6OWõ eàXçÜ#–Èž²Ã¯Ìn´DMöR*ìh|'˜×É0ÛïлhˆóÙI›Áa²kY„!Ñê“Xÿn/vg”õž%‹ëó Q•à(>+¡@\ÛUÿÄ;ØËÖRÀ·÷…Û@@GžÊBÔAex Š›·‡æáŽá|ù^› q#ÚQŸ¶ºkÝùè‰bª ³Ü ³ænÄháóßÚ:mÅ·ö‡–¯¼[*!JîO’_Àm]æàœÁûAóUÅ ßû¸ß9”µýG'ìï ÂX`Ô&ئCM¶/ŒÇ¿ F6â5g¼¬Cå®»J}¥yeToË…k tPPªx[S£QºVGÌ ,Áe‚níÀ¾!lî®|ô`p²ÝwÄ[*JìÇB›¼ ‹ã`Û/ZYßrüÃh:ŽÚ&RX3@R†½aÝ5ôn{.ÏPk‰žÔ6ðúJÀW‘åèÃa¸Êm#‘BêAšãWøpó¢Ó|3tÑ]Å«,ïïsãÍ€˹Ú2»ïMK‚ÓUŸ»/6ùŸŠ÷Iºh®âÕ–w÷‚9ñæÀ åÜí,læ«kFŒœfàµ6ààøi/Þ¸˜Gæ`âYtªþpÇ[UµßpSþK«¢Wü2Wê“lÕ! ßvûeNÀ˜¶ƒw¬sEdSJp»X˜‘£ëÛNUUÿòK~NÑúð諱?Õ<©² \9®d ùú.Vva×"rK±]æ÷Q°‚ ™§¿ˆÛ”À4Ó†Ž8€e*&ìî»ÙË4~ ÁŠÀñüC@®²^J_ÊéêatvqW4~¨[Ãa‹Í7àÀ—‡†1æSÏ>j픽œMçÑÝó>ùO*G¼‹Wf5ukØ´eT¼”ì+oÂSÏ>j픽œMçÑÝó>ùF>`M@¢vã‚oÇI¿¿ £ê{A†Ôº¶Œã–3¤)]ó÷?æ9?à××&ØUñ u£… ™ÔÿñŽOßWØ“oÿáÉû°•û"mƒBŒ†•ª£¾cwVt§$Øÿ㜟¹¯Úl‹ÁÿÈ9?h•Ð?lM±þ/ÿ"êýˆwýÑ6Çø¼ü““õŒÊýÙ6Çø¼ü›“õ+¿ïI¶?Åàÿ埢ÿ~M±þ/ÿ*çê _ð ¶?Åàÿåœýÿ›ï>ãÿÿÚ àA A €H ’$’I$’I$’I$’Iöníÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü’H€I$€@ I$€H I’I$’+kzÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô’ ’I$ HA‚AÛm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¹ÉI$’H$‚ AI$’H¶Ûm¶Ûm¶Ûm¶Ûm²km¶Ûm¶Ûm¶ÛnàA$$’H$IA ‚$  -¶Ûm¶Ûm¶Ûm¶×MÃ[m¶Ûm¶Ûm¶Û´I$I$’I’H$’I€I ’ +m¶Ûm¶Ûm¶Û×ôý´RÛm¶Ûm¶Ûm¶å@ ‚I$’I$’I$’I$’I$ŠÛm¶Ûm¶Ûmë]6Ûi"–Ûm¶Ûm¶Ûm¹ˆA‚’I$’I$’I$’I$’I"¶Ûm¶Ûm¶Ý®’m¶ÚI¶Ûm¶Ûm¶ÛnBI I$’I$’I$’I$’I$’I$’H­¶Ûm¶ÛhÉ6Ûm´’H%¶Ûm¶Ûm¶Û¼‚IHI$’I$’I$’I$’I$’+m¶Ûmµ)ä›m¶Ûi$’E­¶Ûm¶Ûm¶ï$A$A’I$’I$’I$’I$ŠÛm¶ÛmM¶Ûm·öm$’-m¶Ûm¶Ûm¹ H’H$A$ $ €I"¶Ûm¶Ú|›m¶Ûmé´‘km¶Ûm¶Ûn A$’$A‚A$‚I ‚@’H­¶Ûm·»6Ûm´Òü7Í4‹[m¶Ûm¶Û°’ $’A$’I$I$’ $’I$’H$’+m¶ÛmÆM¶Û}ÄÌ5§€,ZÛm¶Ûm¶ï$‚I’’H’$’I$’I$ŠÛm¶Úp›i÷WH*_œÓ  ÞÛm¶Ûm»É I€I$’ I$’I$’I"¶Ûm¶õ6×þ)u÷cpï‹íý4öÛm¶ÛnbHHI’I’I$’I$’I$’H­¶ÛmöÍ´‰ùÜdÔíÁ6ïl$¶Ûm¶Ûœ’H’‚I$$’I$’I$’I$’+m¶Û\›mŒ·_óu+â¶ìµ¶Ûm¶ç$€@A’H$ $’I$’I$ŠÛm¶½&ÛÔbŠt«¹§±^†i³e­¶Ûm»È @ $I ‚@’I$’I$’I"¶ÛmâͶL %ÈÛwéÕØ_*ù»-m¶Ûnâ I I$’ $’I$’I$I$’H­¶Ûk“U²Z€J7–‹å‚ -Ùkm¶Û¼‚  H‚$‚‚$’+m¶&ÃhËp^t¦ÊÉŒ{²^æÂ{m¶ç$A ’$’I  €$ $ŠÛm±M_žøR›ÊBÄ‚ÌfàâU¶“Ûm»É$‚$I$’I$’I$’I$’I"¶Û|³EÎ}Z»§€t‰?²Gßv?´žÛnâI I @I$’I$’I$’I$’H­¶ÓæÊ€ËˆäGô²ÄÕŽ_/[eªm¤öÛœ’@II ‚I H$’I$’+m¤I áC=.¬¯ oêv‡M!Ûm'¶ï$’$’AHI$$ $’I$ŠÛ[³EކáV>tÝÓð†ÈbÎÛi=»É ’I$@ ‚I$’I$’I ’I$’I"¶ú¶ŠÊ²Úª£/ì "ouN³öÛbîRI€H€A$€I  @$’H­zí*©Ÿ4¹òx4©hIª‚ï·ªY¶Ø»˜’$A$H$‚I$A$’+_ÛUÝ­Ü„Ñ:óž—bƒOwý¶fî$€$€ I$’I$’I$’I$’I$Š»6Ñm•ô?µ yfoq©{g«©»mE¹‰$€  ’I$’I$’I$’I$’I"«m´f¹}ÅÝa‰æc¯B²¹y¨®Û=nrH $’I$’I$’I$’A‚I$’HŠÛmÕÌÐk¼å»•_QQi³…ªÏ¶ÙÛœ’$’H $’I$A’$‚6ÛDÿL"™ÈûÛW©‚Kì"§øëí­vî$‚ ’ ‚I$I$AH$‚@„m¶Ò ;PXý¢1ÜÃ{"›lm»É$@’@$‚A ‚ $I I&›m¶‚lLêÅ€Ð!(b;„ýPލ~ØËnÒH’ $   H$’H6Ûm¢ØU,‰°ô2«Zžš0Ìß¶ÖÛ¼’H  €A$’A$’ ¶Ûj$ѬÛRb©þ±79Œƒëí»¶ï ’$’$$’ ‚I$@I$ƒm¶Ú7•°åÀ3̽¨1=\œ{ÏÜÛo-¹É$’@$’I$’I$’A$ @$‚A Ûm¶Ámí¹ï¥etR­Méqs6Û;nòI$’$ A$’  $6Ûm´C¾süaë­6ÃÀöpîØ}´Û¸’ $II$A II ¶ÛmÐù/føÁˆ¤ËFîrûmµ¶ï$€ HI’I$’I$’I$’I$†m¶ÛnðÐ"bqZ†÷ñзÍÓ¶Û=m¹‰$€@A’I$’I$’I$’I'Sm¶Ú·€Ðd:žDA Òtñ2]¶ÉÛnâIH$I $’I$’I$’I$’H…»m¶  s;angçl9²¯h?m¾öÛœ’ €H @I$’I$’I$’I$’*-Ûmµy®C3oÓ"P#o-?ñnÛl-¶î$€I$’I$’I ’I$I$’I’I ŠÑ.Ûm¸ Ð"œ cödü‚Š0¯¶Ø›m» $$I€I$I$H$’@$’A"¶ßvÛiT²ÉÁ1ÏÔ?S2)m¶ÞÛnbH €A$‚I €I ’I$H­¶ÿ6Ûi4Ø1öo6×FÛHIÅöÛmõ¶ÛœI$‚$@I‚H ’+m´y¶Ûn/餺 «pÿÑ•M¶Ûum¶æ I € ‚A ‚I$H$ŠÛm«Í¶ÛõŸ¾‹KÝcÝÝûm¶ÐÛm¹ÉA I’I$‚I$’I ’I$’I"¶Ûm^m¶}‚ v(Ë ç¶Ûl=¶Ûnð $€’I$$€@$@$H ’­¶ÛL³m¤hLÕ›ò‡¥m¶Øm¶Û¸$’ $I$$I+m¶Úÿ¿mæâÜøñÁ`I\=Ûm¯öÛm¶å‚‚@$’A’$’I$’I$’I$ŠÛm¶Öûm¦›‹ÊÓQ?ôù¶ß×m¶Ûm¹ˆ$’I$I ’A ‚I$’I$’I$’I"¶Ûm¶Û'Ûm¶ÝcâÜî›m·¶Ûm¶ÛnrI$’I$’I$’I$’I$’I$’I$’H­¶Ûm¶Û>Ûm·Ë…þ—}¶Ú÷m¶Ûm¶Û˜’I$’I$’I$’I$’I$’I$’I$’+m¶Ûm¶×¶Ûm¶Ûm¤Ûm·žÛm¶Ûm¶æ$’I$’I$’I$’I$’I$’I$’I$ŠÛm¶Ûm¶¶Ûm¶ÒI&Û‚-¶Ûm¶Ûm»É$’I$’I$’I$’I$’I$’I$’I"¶Ûm¶Ûm´}¶Ûm¤’HëúÛm¶Ûm¶ÛnòI$’I$’I$’I$’I$’I$’I$’H­¶Ûm¶Ûm²í¶ÛI$‘ ­¶Ûm¶Ûm¶Û¼’I$’I$’I$’I$’I$’I$’I$’+m¶Ûm¶Ûk¿m¶ÒIg6Ûm¶Ûm¶Ûm¶ï$’I$’I$’I$’I$’I$’I$’I$ŠÛm¶Ûm¶Û]ûm¤m¶Ûm¶Ûm¶Ûm»É$’I$’I$’I$’I$’I$’I$’I"¶Ûm¶Ûm¶ÚíÛi86Ûm¶Ûm¶Ûm¶ÛnòI$’I$’I$’I$’I$’I$’I$’H­¶Ûm¶Ûm¶Ö®Òyí¶Ûm¶Ûm¶Ûm¶Û¼’I$’I$’I$’I$’I$’I$’I$’+m¶Ûm¶Ûm¶µ`Ûm¶Ûm¶Ûm¶Ûm¶ëm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄ&!1 0PaÁð@A¡Qq±`ÿÚ?[u×8!h쌛ýë[:ë¢ÍJ5phªj1 Û˜Ý5ëKn»ú^:úÜö;±m×K¡6–­Ÿ`! |µ¹ìwbÛ®þ—ÏÓíšã¿Àö¶õ HùKsØîÅ·]ý/*ê±I°×¡o¡®5 #ä­Ïc»Ýwô¼|xo¨-iŠZý¾[…òç±Ý‹n»ú^>.²ÐZÓµû|ö5 #ã­Ïc»Ýwô¼|-U ®šŽÚ6Þ­ü64-¿·=Žì[ußÒñÏC˜GmoVþ;-_n{ض뿥㞖ÿFÞ§¿9 Á y,hµ|E¹ìwbÛ®þ—Žvú=el¹ìh ‚Áo-_ n{ض뿥ã›öœÛ³A:'0LA„îl-Ã-_ n{ض뿥ãšÚHkùW¡à0àgQP£B¡@«ˆ˜Ð)jø+sØîÍwô¼sXZ"Â#¬tjˆeƒ ! V7CÕ‰;–¢ýý“ž·=Žì[ußÒñÌSõÄË!ÀÝÙ.MF¡1bÀ²¬@T,1ãqKÜœå¹ìwbÛ®þ—ŽZ? p±ÁÇÊ&, 0‡¸œÔQ G†‚Ã\¹àaèRUÍ[žÇv-ºïéx墿lÕh÷â€è€è€ñ61CpØòLll¸&'pN Ѧï-\ŹìwbÛ®þ—ŽRG°êý.)4†5á Ë‚ Ö4EÉIIàðz1(-:,± RÕË[žÇv-ºïéxäÃýªŽŠ7ƒPmq`ÝÅ(Þ)D7 Z-(‚( XÐ)jå-Ïc»Ýwô¼rP´9ðXiy(Þ)Qo‚cÀ!¸”LLA„ï-\•¹ìwbÛ®þ—ŽBã¢ã òŒjÄ,ÁàÔ7 ž‚cÂpLX\°LA‡¼ hTN5¹ìwbÛ®þ—ŽBÞã6ó`ÔnpJ6aF! Á±1ðØZXZY‚–‰ÂÑ`Æ€ë4àcÆâ—¹8–ç±Ý‹n»ú^8ÑþXk,n0Ù¬1‹ŒÜ£eÜbtHØ´BpNˆaº!¦-$(7–¯ù…¹ìwbÛ®þ—Œ#ü÷ ”ÄÔ(ðÜ¢´hk WèI½Ä(@°ËŒÖpjð‡ÀB¢ ‹L Ñp£Tþán{ض뿥ãjÝÞü/%¹†¦Æ!ªl7G”¨›ègÙd)¶-È Hh=Oà°´B(˜‡‰`&QµÀá@‘‚Üö;±m×KÁ£Ñì6ÿ\Lc‚Ãàj Q„Ãcxj¡%5W§¸’kAbƦ€¥¸õ¸‰n:D=‡D Ä&S”Ja D‚á†î¸[žÇvlë©ÕàÿAÆÆ¨ì¦#pnŒl£ø%b‡¸íÂ'¸Å«ßàZ#S=D}dˆCpBÑ!!<°K‰ ¸Xw sØîÅ·]GvxÆä1¸=F–0Ã^ ƒˆXJ±3{ Þâ>Ä» D¾–Ã6 8(´LCW‚THB$z4¢à•2·=Žì[uפ¯–<Pñ5I0Ü´n ·±Ø ¡e6ôc4AFCSp‹[F\‘c àÝä¤I¦Vç±Ý‹n»ÿwŽKŒ8ÄÃhk°jš4Ëíob Ô/±äöë†Ã qYBÜ5‰N9O_Üö;±m×îñÌRL ÐÆJ%ƒT˜cFAëš™BÒ@j6OAÒÔrè1BaD„…‰8‰µªÜY{²·=Žì[ußû¼r^ FhbNRaa „Çlpð©­H ­ò¤‚Z¢à±à ”¤ +sØîÅ·]ÿ»Ç%Q Ðj1Œn Ñ¡äÑ&œ“D£ jPjÄྣ\Á(!&&žäa D¦ †Bºån{ضë¿÷xå-aíôòe("Š)™G(.$›qÔÚ Xiðç±Ý›:ïéxøh{ £åê R/Á[žÇv-ºïéxø)REoÆLaÕ£øksØîÍwô¼|$©"·á=âDøóp…ý’Q|U¹ìwfλú^>Ti´þùúý ×´Q6Hѵ¾:Üö;³g]ý/ÂÅ9¿ã‹ûå-Ïc»6ußÒññl,S”çF£ êiõóç±Ý›:ïéxøÖ G¸4Ö~4›Ñ zš"ió–ç±Ý›:ïéxøö _°i­ü:ƒ‹óÖç±Ý›:ïéxù _°i­øeÔ52/¥ÐVç±ÝõíïüÿÄ*!1A Q`0aq±¡Á‘ð@P€ñÿÚ?ÿïDigÔÜ—i6<.íËŸ·¨w»–Àáõ‹õ(æ ãÿÂä,VÊõ%/܉,çw“„xÙ;õûHB~îìmÄ%Cr º ø¶áß©‡ëá?wv2ùoM6GÍó¸wêaúÈMã2ùoM6y?ønú˜~¢¸ÍÖÅêµ€ òòÌœýýJcǧïô‚~á ÕkäþGøAjzý”Là€:8ð&ljq&y~`µN½~„\úî=¾å ´ðáàÆ&y~µN½~|OÜš½x?ˆ6åŠ?RøMq3•‡€ØÕ‡Z§^¿<$Œƒç›k8øWHv8J8%[{šâÚqá8C'È-S¯_œoºl|f3/„Ô»CÍÆ¾IË8%<`Á q¹l<3Aßé/Ç'¦>ÿ4".øÁ–o†ì½1ˆÝ,vÁ´°Nã5Å•Í —e™!,þìÃðËip¹öÒÂ̘‡vú´‡'Ü}þ`Nç}¥î>A¾ l˜Hw0âU˜ïĬâ]JÅí6 ø•¬ƒ‚×HS¤Nåxdß<3Éó¾ >Ëî̧—‰èîã×QðÍ›gjÈÎ!ã;#;ÜvÁ—¬C¾ ^bwix”îÁÙÌ'pÍÒuÍ‘Œ‹o ‹3á Të×åDï®áÛàø:Â…Ê´b:RÚóh kn(ŽàÌ'ÞÊ;n³v0Øì@Çn<‹y€8ñs!d=–Ö ™ð…ªuëòb~äúIÏQðg.;Úz“9˜êÙækÄ æ`óq[c„<ÖØÏÎoeŽ––×HFâÞñ2Å̽¶ƒˆ°ãÂâð…ªuëòB$\°ðx —rÆœ'„ñ OfÒqž.…Ë{°‡ ·æ gŽ,¼°Ð{Ì= ú´êKÊÝÄÄ–~&&|!jzü€·ðÎ#Æm‘·bmâe&;dx4Ù)ÞÄõÆÚ¾ gÜAȰͧ†Åê8„©Ûgõ Í{ž·¦W«ˆa#•“ðC ÿOQ÷øá(R°Èì2L|Ë,Ý-udp‚y&ÙH'B#KG†áñܤÈ0náÈ“™!`:ðx“ÉrXÙ%‹c‡>~w«@r}ÇßãÁ«Ýî<†øq5æHë3„ÙŽóÝ‚¸ð• 9´8µ²o|'q§×bå—¬<(––ýË…¿²”…Ç‹',o¶\\—oÀáõhO¸ûüX—EÇ®£ÈÖÀ¸dàµ1ƒšñáÄáêûgÙ’àƒBLNù®„|RN§ƒ-ênÐn¶9k9OœH÷àòløá ~w«@r}ÇßâEý»ŒžÞüÚ6ÄñlËI¼NÁXš9 å˜aÝ‚ú ¼Ùå-âÒÁÊY×L)Áì\›„¼‰ß1¦5-²nC˜‹Fs!ùîV€äû¿Ã‹qŸê.;#Àm¥ÊOGÔAÌ+`à™É¹§sPN&÷&ZàKî\Âî×f÷hI-<­ØXV1ò•dY—>@«n!Î-Ná{‡áŽ? ;‡Õ 9>ãïð¢~îêòCm¬˜bmõ³¶äo˜×‹9­5'½çÌäÝÛ¥í–Ë,nV=؉‡¹‚s'8òË:‚«"ÞîW‰/R=Çl›3å€xŸ— y?/éÿÉm,<8Y×Yâ î$â÷[3ÉÃ9eÉͧv™/sÂ^.Ž[ÍÛÀ+Ôì&ÜÙÌ.“n7-õ˜y‘êA•·±"æg—)‰Ÿ#Cê3××àħëïû@tuý¾6rp¤ží§$Ë–-%xH3ÐÅÈXÈr%x¿®#Ôxv¸\-vÚáà±Ä[t‹&ïwy€[â#…Òhq06l!ŒÏË+ïfqø!C»švÇÀžOȉ“‹a5 $8%^biؼ$âÆŽ;9îAÔøøF¼Ñ¾<À›pHIrÀ8C§ƒ]!A\Ù&2Ç:¶“>G©Ùzü˜þ‹…㯘9'v#¤þ¤ív¸Ã[™ÖÑÀ°€ò‰êeÈ=ËŽ$;œÀ^-,í-KIýXwÉhKN.ƒaêáÒ"r.âÁÔlAÄ1—˜ZÍ—¦k|3¾)Âíñ&m¿·þŸÂ9eáȻúަvgr}žQÜc™Š#0é"øzeí=ÎŒ½‘¯+ÔSƼÄÀp‰ÄY™êãÖAÁ7Âþ¬œƒÌüYø3ý¿ôþ!ØeÌÉO ¶(ssäá•‘’i89˜9)m»ÂÒyÆ¡‘¢ñ:A9[s I:›îrÒO6è¤{‘êlm•§ƒðýB‚?ÛÿOá%ž,%—’[jíÖ±lÌXm¾-52á4q“< ™¼f.;&×H˜ìg'ˆá¾ 9´´mÁåÔãñAœÂuã ÔèâBö"Î]Û•ƒ©Žá™“›Àñlü´Ï¸wŸÂ ¬÷`«ßðY1<2IÁ¹YÇgß ƒêXä-›a–“y^bwdâägðÆÑ°Nã3Ý„¹Å³†à„jLFu¶bf¼&ÉŽì-\ùnXT}~Hug»˜ý^ÿ„§R³-†á”aëÁ%ÒÑÔiÜÎk‚sânô!Þ,a¶Vé12Ö6ÏÌ—$=Î&:î[,·~hÜc¬ÖºþHOÔøÿÜWÚ|7#‹•ÊÂ8øJxÝ ƒ,œ÷oÔÝ¡H^%ÂÂÐò€»óãV¿êg–‘õùA?Shçÿïû² ×Ìr!¢+yx9¼1°csxÎËfÒÁ°´´ð]ù Ïóþ páÿ/÷̈n¼Âsœœ?ijÀüt•¯RýÜ®VS¯ Ô»ò\-ŽUjþpB¬w ô.Ná܉”ïÅÔêÝð9ñunü”9[Þ¬¾ºþ|B¬wdOþÁ¾~¯éO¯¨úý¹:âºsù_‰ñìXÞ .¸ßÒ “¨ñþ+§?ù뜑G°zø™ôȵsúqruá„zþåǶåmóÌAqœÕ¯ê„ýKñZ<ŸÀü=÷q+¯ÔŽ.Qýÿ\'ê\ŠÑäù?4Ï÷þ-®þÌokû?VD Ç_œ›Œuš×_Ú Ç†WìB uå‹|4ý_ÒŸ_Qõûa8ðÊýˆAަ\ÿâçLËýî}þÑøÿÄ+!1AQaq ‘¡ðñ0@±PÁÑá`ÿÚ?”0E [‰Ç7[Ô9n' ÷ܶó,c@'pQô |9% JÀ ‰ÿ³`#"žk=ía,¤kÀÍþ:óÓ¦LXé/)ÔÀÊ"ózZYúŽ1Q‘Ée]¶F Õ8¿7CÓú™9]Ê]5žŒO† Êúõz†NÛe% é£uÜYç5÷\ƒOoeò†,ºàbÖtët¤z2·¾Fœ3—ůmÇ U¿>!£pXÂ>0ƒ¾Fu{<«ª¸@ðž†u3‰O­5§”ú#Œ X¶•ƒ-×ÂúÇ “©Éx¯™@ò®±a$k†P¼)¼Ì8ô~³ÌñÙ£-t§T?u"Xþþ§îU ÒÁ:”Ç.´2°E"Üa!6j@0®âñ¹± €$ZÖ; $D¤BàrØ`!GR’ò3…†c÷ c!ªÊò¼‰„ ¶(Ì> –‡ïÞ~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû>~ÏŸ³çìùû?ªL)4g…!äO„b…Í\‚&P¯ $²³§kE=©ˆ¦$ðEîpê å)UœæZ)Ò5ÞFÐoª »‘[1ñžqÖе [ÖÊ¢îE“³’·“ÃVµ\ˆ@×Sjz4cFñ9†0#râ5 `™r†içòç,!Ô4Aa»=“¬cHÀ^'0Ä8²8‚€–”wš˜DÒ•Ž'ª£ -Ì.ÆP­¾¤Rމ³á`o˜zžõž€ >1ÐHÛ)\!ç&Ë3u*[Ðô$°éWˆÙ Á2úYc(…‡)*,ô%@0M‰ëlÈÊ>ØyM#•×9§3 q$8Ç ¥+ K“e™‚º•-èbù‚Cp‡ÂNOÀlØ•íÑŠÁ 5‰‚""Rzb×3Ñ$ªB燡(ëuÅh…=§x›–UÚ­wð$I_:"æ~^„–æWn&yB²áÕý#„!Þ¨Þ¡1 P@­ŽÄ¥ÊÁìeW®SââÓ£r•ouX7k KÛgK¨FU!CÀC'ìEÜÔ( Ø@îA4K;•¨ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙóö|ýŸ?gÏÙò#kÔòH΂¸"¹E1Áx‰×ÁÎXè@ƒ¹iÜÃðžöIËxœ( Fr¥œ’ù#á®õË[¼?±c™hÀ†d«kÒ[¯«üŸ/ÊrìÄ<¨g:”ù¡;ϦZðŠÂ Í0pл=´Û‡KUm !ˆ¬ìfhÀÚñ?O¤jÍ%d+¼°³4¶ª®°cH–2­ÿŒµUh™ŒM ­8þYû’©Ll$íU€HP£þþÙ÷/ðŸ¨cT3n?öÖ %í.;Q  Ghk£ÓϪã]Òñêæ\$BNjð^欽ÙŒ¨I!H9ÝŸGßs”©§ÇÃÎ 2a˜’Ž ¸ØŸŽ=îöíYnöz/e$df±µ=°B&¾FL;Ü3,ÅêÕnlx,ï»j$)Šm. »0 ‚ ‰ô;Ü3,ÅêÕn=ÂP jÇF8^$àP  £Ð^æì=™œ;Ü3,ÅêÕo 0ÈHÑšhxHrù(D•2PÖ6§¶DÂ7Èɇ—æR˜/#8êh ,ï!CòPˆ¢¸0m¿O ·vûÃËò)L““…p3ô 5N"˜½J•YWž÷¯ÁR¶¨öÇ–[ šHaÆÔöÁ˜Fù1•Nˆ4$±-k©{p`ÝŠil¡+ݹumð>Õ°WðÓÃ/j¹T_b1hƒì†-Å5™<ÀC6#¨ÕeNJÎèüF>L/€w Ͳ¡$… çv}|OL+öÞ†c(à·lSûp? ,èÁ7Aþ~?ð½ñ&„olû—ø`îA6Ù½U½‘…ç&yF€–1޾Ílš•¯8úúEjç Ó”VxŒF‹Ç;1Xw—ÝÀD…"8‡¹ƒ¡é!d$€Ò‰wšôQ)fšûÖP€gån¾>MçØ­s’.QÓöß0™¦GË®&1ËX1ž%:ùtM¿, Uóòk©±Ëºi\AhºFˆÓÆòP€¯iÛ&Ùì¤æ Æ‰©&í€[k%‰¿zìÐs3ÆK"°g->™¹oUg8* NÖ'À"Pœàµ!1p`@ÚÛ^ŒAÁ÷ɬõÐ[3r„ÞE=%Uvn>%¼˜8ˆ`s™“q\K‹FÔ2æ[3%»É¬õÐ[3r„ÞZIÆÄ¡K˜˜“xˆm7qÎÓÈÄÈp÷ ‚l;wý¹‚òd#sV,ï…#&'û;gÜ¿Ã*¡Ü÷å=ÅðçÒWgú†CŸ}Wy„”`‚Ó¸wÁ´9Χ©¿ƒÙ® >b÷±c Ê—9¨dgÝ|Õ€ƒ¢Ì5„#Æ#Ä«X‰á’8ëìÒ€©ñ»Ô2oæõÊn#ÞB‹åþ‰dZ´/Ë .³]Äðà[7Í[Wh%Æ \ Á¡!a¶%+ï¢-1ÃÞ ÅƒYÄW鬡өc*V,:c3+V@yC¾F*R·VÆbF}¬à*â íh- ‚Òœ‡]Q£ Dæ`Í”RÓUeºÏµÀ‘Ù[±z„ò¶+CCtýí¦>BÁˆ´li%ˬS…ãóœæç4œêÓtðp>\PàÈqÐO;¿sǪãM¤Òsÿ#ó}V@¼e£¼}ÿ#&'úûgܿà v o¾Xwâ•‘Y=ˆSØõÞ§pv-oÒk7kc+õøÃÄ¨äš Fyuê@SÒ‡F8^ÿ•ù¾¿¥ ^'¼_¶_?ÓI1‚%[lû—ø` ´ Ø[6F*¸æI¢àrÚÄæ‚Há°<’¹ÞîH5Χ§ÁŒ&ŠàŒ|(†ÿ­›šVê¸Ä›KIeíWÜV±‰Úa¾7ŠIyÃÊÚù5¹èb壷̫ vgsECÀ¶W˜_m`4”h"-#—qžÍ¾ËO#:v‰@Úf¡{ Üˆ)ž9½$ÁÈ‹´ŠºkY?ƒ¡0j•‘š#Ò¤qy˜ 0qdƒ D„[‚ᜩ‚yɈa gŒFEÑá‰$ÐàÏò¨Ù#ÿó}9‰œ_¶_?×Jž0A'õvϹ†Mx€h#€A`µàa¡.ðõx¯ê˜Óx7r ±@_,`•QOºª\íï”› ˜œ}L^K@I4Y×Q¼ï|.Nö@L4L—Ì ó8•ÈÍŠß%Ç Á=@O‡g°Q²Ë}a Bd¾`O™Ê&:AÔ Î5kY á#ƒH%Å©Á Íô""b9é( —«›P¦Å3\“ÿ!᎙…±Óâ!÷~H# |x-ØžÚ…”L™99ĶÛÂ6@I©¯üHœ¼AB¡Ÿ•4¹'}áª|]Œ/z½Ol˜™<‡¡{Uê;lLKåáþo¯çP±fÿ˜þD©ãSlû—øgÕÙ_-á ærýÝmÏÓö¡ ”8‰½£c@1˲ə"ŒbôÜäˆtW$ó`ò•Dãʼn{ 3¨Ç Ë`‚ JlDÞѱ åÙd1òëù3Ɖ†°(…%ÔâO¤ìë1=Å•©Æ 4%2sʉWð_±!‡‰,§Œˆˆ*ïˆ,ÕÐ S[2ZÍ2wÛ&è*"Ô§—ò 1¬öU S¤Ô—S€ ·Ê¶‘V}ñŒT¿í˜ûf…ÑŠx~B<ûk‘„P®Øb,¼bÓdjZƺY,y¨¦5ÑfŒ1§ÐI ŠÒæ•_3/‹T£]Ä ˜L ¾èñs aÄ'‰Ãnh¡Æ1Rÿ¶cíšF^àÉפ’*wuÉØòd“µô©¦øP”º,º%äÔø&VKD²=ï7X“ ‘%ó|ÎIýÛ¦MS’tŸ V'™‰0J:À• í6KŸAÐYíC•ëyºÈV„J’”áÏÝYÿÍ Ï½™í1Q…ÐJš “%P„á]æ: =¨r½o7YJ: RÛ·äî…ñyT r¸»%J5ÑdÁ¸ÓŽØ„‘‡%Z¥íÄB’žrÊR–Õ¿Yû.R§x1Ë…¶µ&‚Ò(¦A$”K¢É䉇áüß_ÌdÞÏè?È•<`‚OéíŸrÿ ¦€Ép–0O9Œ8úGÄÞvgÙb5öÏË`Y*J°ÕãN€¥¼¦Û;7sÿëËùb1½‚b‹Œç(9Äkí:#ž–JÙd ?ö;f§³Ó0i/”<úJèâòa­Äýp, jÍ0<^Ć^ ¶Ž2 B ªw®·ulÅÎä1.âM`ä(ÅS<«Á ‚”§ ©•!âX×{nç¶’/E^u#Ú'ÆN27¢5xÅ©Œ™Çº­×wtG&ªxAu†}§µú>üÓ¥\2ÜÂ?Ï4_ˆJin²^¼JF#p¨½¼”en:ÝH½yÔhœ$N'4Û:·èy=*å 4‡¾l Ey@Ûo¡[a,®uHËnƒßP‰5ÍÄ„öÄø»Øì‹ŒJËšIñà«rF;ŽÂ’cd&œq$2;Õœ7U>ù1â±^üDú15uãöÀIÊK\¬O-Æ’êΪŸ|‘©ï-ë¬½Û øÏÖÆây—?ÎâY](ÞIaFÈ!¾€ñe©ŸÝ‡žØe%Ìc):¼9k K­‚Jzå êt¼>5+Bü?›ëùœ:c'à?…*xÁŸÒÛ>åþÔ¢k=¾z/'0†aúf‡ÀM€+óáTäúÖKGˆ—Üù|}™–sFø å?tc¯twœHõép’B’1º>¯¿Ã%ë$–÷ ‡¿Ùð„ò¯uk¿†= k§ÄCìüýa¢B$®aG˜þ”¬ˆ²‰ìJŽÃ¯‹ó},ÁŠSü¯£é|d¹8993èz>‡ªTñ‚ ?£¶}Ëü>OáÚšQ¦".õ踔UÑáþÃ’kMùP±÷°\<ñZÂ=·!ć#'–W–ÊðWƒà˜¤•D,UõÀ§£Õ^Øòà"º3\ÕCA29¼bW³s¹+¢­¯œRJ¢*úá«×Âw[WåÎMÖµ¬ôrƒÿH™Ì’yñù$ YJ|ýÑÃ`(BÓX!Z0$ÅXk~X™Óo¢úÞöÕ,÷ÏÎbO=Ð=ä£ö`¸yⵄ{n4C‹=vƒ8Òmà~’„aPv·234ÛÛ»6‘9ÔŸbÇ¡“xŸ{´ÏËF@ã€Dˆ2U1ðߣw²È7ÔQºd*#%5\e´ê!q•8ÕªEÉFO,¯-•૎JLN{a¨,ŸIµnùþ¯æúþ@œ¢0×ð¹3é8úº0ÞD˜MÎÌH`†½WEœaŸ?èmŸrÿ Tƒp×h<ç?,LCàj¹+bkÒǶz²å:F3îñ^;UWœ•‘Q=‰QØu‚M¡šÙƒ¬&”e ‹Ç,Í\!tâ1°Ìä%¨>LÝgêTk~Ö 4n†=þkf°^Q”(<)ƒ«XA{¹á¯IuöcdT…xÊòG2—›Œ`)éK§('"ãöú;‰ ¼£_ºÂòÔÒ&­)GX਌AÒ·‰w7(ùÒ1‚€¯µ(öÏÙ’^AÏ“2·Ù•7ïQˆklòô@d‡é•ŒSúKAI߲ʠ(9¤½r»ÁÉ…Óbl¼>§Þ5ô=˜”CÒ™÷ x¯‡ZHõH¾Aãúß›ëù RÏÂã’ñˆ¯ÑA½bcÓ9#IÊ™LDag"Æ8}ìKf(Áœ>‡Î@òoùöϹ†3‡$2øS¸Y|«áy›²+Æ¥óÆ]Á„ ³Ç¨-H.X×à™ÂÔŸPÊ '˜Iš*¹d$¦Æ PÍO_æDHÖÊ»Iá†âQB}vôaD#´à½…å!÷žZbå\Vq,Û÷|y˦c’E©5xô1YˆóTsÂxe,¤Á7¸g)ò8L)ãɽÆ]ä¶dž]À&Q†¹-VJÌÄ:ƒB6³¢Í:™½bÜ6hòf‹på…àlb¾,£sÇ¢¹a­’[‚ÞpíZаX Á•…¢Ü5„ßH}ö/aïU;õJ¥Á6žßVîØó„¿­Ç‹!1¨Ç3ö«XÂa EÆ oPÁd¥´Z—g÷¿7×ò9†rÝbsè°Â™7¢xÀŒ e¬‡ ZÁ;Æ©Æ#<2 > b¤ž9ÁŸË¶}Ëü0 ÀàíÑî±Ò‚†`”Í”Ì\ü¾‰mÑ¡0ëN!Þ¨3èéºÀ7‡§Ð³…‰Á½™J|µ ´°,T=ñ_DÀ&n¼àøÀRÇÔóˆ.ÊbFN!˼-æ5ýCØq1y7g(2X› =ó`Ö¿UÂpgxîo "M ÏczÍûÝ–@”6-¼\ ÞÞšcŒ V=mÈ'V¼ºÛSŠÆcUVÒ`)©8ñ¶°ß6³_PDŽ}àk1«êØŠô·¡VêN†÷ˆÙ)i å¥ô*ÙäRž ‰{}õŸZ ÀSµÚDÎjÅˬ$±fùcB—foÅ’¥—ÑÄRxþÿæúþ8>4ââúTà 5‹ò1ɆŠÄbòL5‡7ˆ ƒ–pâ¨Ä]b&ðÃáJž0A'òmŸrÿ „Q;6a§ ÇÆD·N†ß€{M ‡á+‹Œ´¤<´•˜}Ü?X ‚Rýɦ9tÔï ïª·X#‰$™DTu“td.Žo$ÖÔÏ׸F ãİbæÉ~býh”o å‡ ÝÆ Ì/ú¿ç‚õC±Y&zÍ”F³WzÛ2ré©ßAßUn²\v¥(•Õ­ ‡NýNHaa¾=4~QÍ]ëlÈ/níß Ñ/A½ré©ßAßUn²\v¥(•Õ­ ‡NýNHaa¾?«ù¾¿ˆÉŸo‰¬rcuŠï !ÈFJ¹Fã¾7ÀŒËŠž“37ÂI¬•Þ =ëYÍxóY8ÂyÃáJž0A'ñíŸrÿ ñ£[˜ÈHEþéFž„ù^RþÈ–jvK{’½!ƒ¶î Z”˜ÇÎz/>à[Žˆ­c¬WˆÈ2¾½^°¡“¶Ø’¡¯òfã< N\} ›0)ä›Ö˜|bR=[ß#NÂ8”üÛœ#FÀ 苦!Š€t¢¸ægS—Ú²[tb`¹bkìÑ•ò ¯¯W¬(dí¶RPÞš7Y Åž}EË_fŒ¨èw“ýÍJJv¤¸&GÁˆ/ 苦Î/6 p÷áu†”]‘ A3©šã.a%Vªí ¼n9²ÀMhÕ0`—ÝQ7RΙ–kPº­8M^ùê`€è2¾OH=alÄñ”™‰0ýÕŸEë*[½™IKs<@ÔïÏM1¶šÜâ°à¶»TP6™W׫Ö2vÛ)(oM¬†âÏ?Öüß_Äàø–2r'ÐrWÀY³!qÇ-òÓÓ|œ³„ `.ne 2pf°ÄïN CàJž0A'ñmŸrÿ bL9 LAÊÖú'3!ç3p’q˜È4¤­¸˜d ìÓóÌc‘ꃥBU\?i‚ ²ø"òA9•Áf¤PÃNFÐoª »‘[1x|ƒp×nùå>Èã´d,0L°äÛØU×kTåÂI HÆìú>ߨ~6Á×.sÔÐ…eY×ñ#Œ X¶•ƒ!áØÈóTûãã¸I!IÝWßâoÂ1Ê­U¾½mÉ ÙlÃA3OÃ{ðå^·JËIü¿›ëøf5¼RÏÂë}9'X†ðF 1&µè Ý㬊ÉÄ\áÃX“8àÊ¢±•㉌gŒ² )¼`}%5„ó‡Â•<`‚OáÛ>åþuÀl¨ñ¹ÃõVÏ ¤–pk›d[é’x“ÜÃ…‡ª7nµÊúgßqŸ û˜rS~5þóñS×á˜ôu.¾¯åpÄÕ*^âñör½ËZ¼?,óékû"¹Ñ î õ6Z~PìN3]¨ @Ü)·Þûޝ>|_ÍŒ,ý’©qª4ZC0¢:K(KŸØº­håkožÔm²tÈÝpR,9˜Ö!ÄæìuëT¢ÆìNdä 0ˆâ zЉ¾ˆ­/ÖqEÓv.ÀâºU‰Õ wlBùc‡˜“àR×k7ˆN†mMÉ“cé/îe… .'Ž NüŸ¾Ûöe›ÃûNŸfÊÍóÆ©a¤(xc.Ì¿ìÄË<í;3ÊÚzµÐ¼p:t#Xà&ººÇŽE±.?G#í ^·ë934õI¥'%2Dô|yëæ˜ ž°g® s]®ç#í ^·ë>†|ÿ3w$à\‹áÔ'·'Qn¯|N³´ÉQZKk¼¡;ϦZðŠÀê¶Ê™[†"\Ž3ä˜?-|_›ëø Ñ Ž ÉŒ¿&q¿£ a`ÆNo$2œ»•0\zfQ„b ‡Ç àäf³|2e1•änpŠdúK2ŸÁ¶}Ëü1¨ÙË'98Àä‰Úo ÆBdH¡%ìdÀ¼>À¨k£|±ÈõAÒ¡"À#nMÑô"+$r»L*E‰mÃ1%Ap7±9= í•®€–ñ†mÒQ–,¦ÊŒ $UµA‚pV* ©b‡#SðJ‡à±d½òdéúÌc'µP¯8—X5 öožMF7ÏgYw/ÙðŒp¯Wk¬˜}ÂðĤw†u×´Dg‰mwŠúÄí²Ù•ö' tßø¸Ša„;išk $Û-5µtÁ)Ï¡ÅüÈygbûp€JL·÷+s*‰Û——#ZØ 8‘ OkK…ôI[³Ûx}P×Fùz)Z‰+ `ÇW)Ñm¨ö_Ù4¨É¥drX£I£3#º˜ìų}x}P×FùdÃMÁVWœÁÂXÈPZµR@Q‹¢´ƨ2'Ê$éWuåBI AÎèú¾Þ“´ˆ^±)âH6®+š^ëD‚#Ý*àn¼žaFh\1$¹ "\»-ÄÞ5ö*à1¥"µƒÖ.Úpi³œü–q>}ñªËÚŸîUØŒ\Û¤eíÀx⪄(€£âüß_À°N,¿èá]blà‘ê¤p^& “L8Ó°ž1Àbý"HôÞ@ s—\a¬^ÞDbNpI”eRäúÃç y7ñíŸrÿ \$+hì†.(ŠYR<—ð‡E@6h솢s  “c[¼ek)(ÜÖLÑ6”µB{Š÷ˆ¢¼Æ©n²e„m¢É«&*6¾eÓxSMCOw:E¶$ƽ%te€s*w‚ÚExn}v’ሉ¥ŠÃpîpÄÄTœIm HàÈy»yJ‚ÌÐaí¿U€ÎÏ$ ™÷IYâ~,ÿC úƒ’ÞœÖ ƒ;ßšQ[q¶ éì6Ý9TÁç[:àúŸñ7×ð9~п¡$qP¬˜p±WXÜäV%™æ™d$z:`S!Œ]1 ¼‡“‚1Ó‚ZÀ…æ™>1w9‡¡ð >r“Ù÷/ðéN‡ù¦:¥&n­‰á~ž0î d!ž='! {çü×ã^~Kà¼)ŠÁG¾ Ÿ“Yë ¶f.å ¼”Ätñå[¸Õ¹#“o”…X’ä8vVÃ~}“½8cE)3§¶Êz%u¯ƒƒ§Rú„A‹j»dñ‹SNmmÒ1º‡È†§•ƒXÚEsRÅ·uÏŒ€ °3|›Œ}¢À€Ëð…ÍÆàÜ/Ð,·2Uóé—ŒÅËQÕ}Å¥N¹ÀŒ8A-}ßø¿›ëãÄìÃ&2\s'f)3‘Œ5‰Š=šÉ8øèFÀ0àë’ƒ̈¬®òL"ä‘•äBâNSŒœ`z&=Aè|#œäßö}Ëü2}‚PLì3Ó'IU!›ð›£€æÊl$ vé‡ošª™4œøÄÒ: žˆåõdâË“äå§»öÌHy}G¼sœJŠ`‚œeŠÉuöi@Tøôû[û° ÊP-ywÑ…NÓ3¨6;iáÎ8y`K-aOJà x>ëæ¬a¬ é2O>¿” h_–f¸,ù‹ÞÅŒƒ*\æ¡‘ƒKv^)…ŒÐCœï#ø+<”¿¤ué«»mC#Z³"À™âk;Èðî†_¥‚a?i_ ç ™J‰Ë¯ÄlÃÖ ÅÐ.ѪSƒ§éKÇÎy<£FNI×/ B«Id‡˜Ï¿]³">L‚Õݶ¡‘„‹‰ŠÆô ô™XÊhÞàb§i™Ô´ðç<°%–°€§¥Œp¼ƒKv^)…ŒÐCœ‡¥ãç<žQ£EYتÙ!çkÁðìŒM#¢ à€Øó~o¯ˆÇ/ÂúYÁu8fÉ7€ŒA¯E8Y'œÖM œ ˆÎY%X‹|dV&±$LÁ³8ð x£~”ÇN ÄV1fòŒKÈaó<›ø6Ϲ†]æTRm òÈüTûËÝToåz$OáÚš¶&*·éÑMzÒk)T £¸A0%Þ)ŒÂqùÎsåGy“ðu{Ù `ð;Îãï`1®?ÕW£{`£L &HÎåaUè”VÇ%½næ$—8âU&Í­÷(0iPs¶¡ƒ¹‘g£ö;)Þ[ŸXvš,ïHÚw¢á 5+ulf$gÐL:û0 jRüåKR±aÓ™Z²Êò=ÉrºLöÇ!&EŠE{à½x3Éÿ`'Ë%ÇÁ!A•ÖE8¨®)·R&'ªl¬«G‡“xóÉÉæÇÈX1$¹uŒ‡JüCÊß­/“ŠCM> ~®°B)Ï6¡ª³Š²@œih"ƒ•;j;™z?c²å¹õ‡i¢Îô§z/ª·RaQg× æãuOº©}‡¿Šc0 \~sŸæüß_ ‘ÇÀ㑃y&E€!ˆ:ÄŒÓ"ð‘‚1FAÞ&+ àA Æ“ˆœ) ƒ‚0Š1‚ù·¶Lщ.Eé©91X§1NœI1NòÑÃY«Ð„Ä"1àœÛFIl×`e ZDàȈÄ'1¼CÎ ‘)G!8âRÜïP¯E¸xâ.±&úqˆc{Æ&3— øÆ3™é¶}Ëü1&‚&ÍO":o ÐPú7døËªúTIñÉ-é?Ñm{PïóÊﵨ;1‰…öÖIF€B"é{ð:Ö„CÙÈír‹ˆ”éÑ“8U 3ÌÄ›KIeíWÜV½RÕÑ#ݶ¡È=<ÌÑÙW÷\ÓSL™Âª"à¨Ð_2# @¨© 5Ó¾Y ÅPša·Cí—ìHaâ[hç%Aò1zÒkœ'½b¥S¶Ó81pIþh$$TP$!á!BìP’ÏùâüJJwçÃÏVÉp›ÞGÕõp^mP7ëðÚ™ @€óyËNØW|¥„;DZóTøø#j<“‘ÒÕ•R´A˜ƒÜÐI6’W8ò0Œl{hSX¦WX²ªJrbˆú-†5)’e£Ñ“ø:©Q oâ9[_&·= ˜÷n¸¹Ù–xDXàÈ3„Ñŵ@}\›T ÆúÃüßÄr¶¾Mnz¹7ÈïúUжò@ï4ÕÒÝ)¸ã3W^|¡—ªC…’I¨now$çS„SàÆA)ó!–A­?cX¤IZš&XA`„Ê„š,°ÕÄÂûk¤£@!½ÜkNOƒ¹©<¿Oó}z˜óXÏ? ¤aƒLŒ‰0ÄŒ¬eŒ$a|ŠÅ¬VðB ˜C……ódJ7Šë#³„a*É‹¼´NOŽõ€f 1€w€ À¼¦L`ãœ1b»`Œ æÙ÷/ðÈa@ñ¨õ ¤d:Mj–WþlYŸ*§Ð&&Kæùœ¤·\ÌBˆ:Qè[ >"wå OÈI;fù&Ae1nÆô%éi¤ˆZ®µ’ ¥*ñµ\‹A—n•væL&˜ÔBÁqCF8Š– ¸¨&r71WN¤ÂMqቒŽ8kLj橳ˆ6–ÉÚ«¸½eÒ(.A®wˆ †I’¹…c'ж0µo's[:·èy1žÔV$½[è÷MdÆòdÈJ½ŠÂ±!=šò ÊS²}'s[:·èy0*C—$V_p8Ñ]aŸiàDA-yaF߀g¼ŽäÉÖ‘ š9óyÆ q¯ûÿÅüß^Š óñ>޲—äÁ—Å\˜X¼ŒÆ¬á’>x("|aå‚‚>g'‘@#Æ^„1‚á&·ê0äà5^Dk$ÖÛŠ„‡±’ÐA‹ Þ$ãè'ÆÞrzkÆ ä9¿Œn3Ué¶}Ëü0JÝFUó?"~ÕX܉^áRªð‘d£•Ëýwn=$½d’Þá¢ð÷àÛ>žUî­wèÃÖD,&¥õBÚÁiñû??@¼>A¸k·|þ"Ž”sÀp]@˜àÜ{zGF:÷GyÄ_Å>…µ’Ñâ%÷>X"CX•Snáóø»3,æñAÊpzÅÛn óŒ=d@rÂj_^ÌË9£|PrƒŸŒñ%àÀxÆ"(ÉCœ gàïÍõ†9~7ÑʰXæðga $¼+ ®œ­1‚N V0–+!¼A„Íñ ”ŽBšÉFO,¯-•à"jåÌ")ŽÓ´Å6¡*@³—bêe¼úåKÁд÷“•T–ÈÓ6pÀ ,ZØñŒ=)tã…ä_Aîænír#z®Ì§‡Y^@ÈæRóaÑŒ=)tã…ä\û†À¼W•2tz¯ÈK+9Í<'‘õ(Xæä±…Ì¥Dáßæ6`ëŠ r'¤œ«Î1V`u![Å~îý ¢ÍôêQ€äøu(Éw*£-…£ÙÎT„ªaRrÞJ2…E bø‹ÕAIß²{¹Â(IK‚Þ1ñ*‡ í€>™¿t£ó.PlŽq€§¥.œp œ‹ƒ«XA{¹á¬žîpŠRà·ŒTôŠs3té…¥bñЧÑ’¦V2±”Q½JÁ„èþ¸ÌàäÂé±6^SïE…("I“'ÐgÜ6â¼Ü§Ã÷ÊñĬµÞWx¡ÿ8Í¿…ÇЧklx8^ðN1c¦)2X'L’Q‚jb1 Rˆ`¤‹Ë‘Ð}Ø@DžÉŠ“œ:‘•J±WÕëŒ4¢òÄO—5ÀùbøËÑŒHclFWŽLzfr,21W^p~Œ Öo >-=vϹ†@ôx¶úä@xÉx¥´ ¯'§Œ»ƒgIÈPXÀ =žNC4öq%ÚûÓ ÀØÅ|YFçŒJ¶#3-Ï_8LÀùΪ”ϹÛF^Î’:pà å¯òŠ…TòÅwD¢² ¾}’º×ÁÉž‘‚²Áç&58"ãk å­ÃŠzâcjp_œ­| $Í\Œ²GÓbã ±"®‘Ó Ä…2Ì>r,î-jèíâ<ÒÃé W¸Ë¼–̓˸0âTÁÓ[tŒ5€)gdš7ZŨ´?Üái…§uÇœLB[Rýk­3¤2ð,1Û¤©'v$tx0/X+‚ÜÇáÝú¿iáyÂÒ 5 Në8é©}íö·vÇœØ|‰ š…4!tF Ë0ùȳ¸³µÐrÖáÅ=q1„Ö×Zw\yÎÎÇHjƒÉ8°ÃR…$ÌÄ.c€0|õe<¦â0 êy£·ÐéóÈÞÖÅIJ2±WHxa¸†]Â(Á~r; Ž3jàéÍ-;NOFÚÊÎBV£ܤÍ\Œ²GÓan‹Ï_.?‚0%"Wra:exÍ;&ô<BÁQ±:ÌJ¸¬âY·îøó‰ ãŠý®S' Et§(¢H¯‰GáWñ8úE1ˆÛÎ@k à¼c;Ã1,$ q²dL묦¬#±“°ÖrBž³¹écHze°ç©a³ HwcšDc Öœœáë8\u„K9 ÂZÂÆ Û >¾ ³î_áVÜriÆõ‘xŒ5Â'Ë¿«*Ä:ÔYØš ŒŠøÔe{”u¬Æ¯¨{b+9» ³˜&2æ­ýœÈ£ˆk %¯¬*“ƒ„Ó¡D˜r~V ¥_•/`/I{aàJ+]b†áþFC¼Se+OȽגª <þ‡âÛ§ËPËKÅCß+•<±gÈ{M[¸|µ_[ºu¬Bß/w³ŠDPð1ƒjÇ­¹ê׆ (Y §ã죞‚ض«=®³Ä†¦rú™¦¶’‚Ìt4÷É Œ[ƨ¨ç7í¾;îèpÑ7VBdŒ"ƒ8àäñ†+¢”Š\¡ßßZki),ÇCO|€¬%tÝŽÊ,c9DŠú1Ü>Z¯­€Ý:Ö3‘upüµFÓ‚uªÂ 1ÐÓƿ¨{&/ƒ¦ñ](æ÷CpÊvHÛ΋6spc&Ìr#®vú `›K€j™œ;f%g/†+,"¾ˆÁZ9é-H3ØoÑ¡À§!˼ «7ÇìœÓ=e)òÔ2ÒÀ±P÷ÀªIY* X=Ùcv$ Ñ2ÉÖè2eå]gÑA1îõ–Ú ƒu5½Ä37g(2X› =ò ¤R¡NþÛ“‰îs€öËŸ‹ñ‰Ç,â¬u`b9Ê+N V' Á¡–!P (— 9Â"e0.Db'¾<‡xáÉà”[UpŒ±8T/PÆ)Æ‹q^.mè±à¬ÛrŒ±qeœ0øvÃ×lû—øb¶ŠÝ…[û1- =ÿ2kîø'–`Büâ½ÇjÍɳuƒ˜óÅÌü’2_†¿CÚ%ÃEÅùcaÂwq¨7Í|®b³ºÚÇeCQd!,9ê§IŠ3ˆÝÈTߤÝX:Z+ Ã’ÅÈ‚‘Þû‚"ñÛ,¡Ja‹¼Òf.¾ÌYuŽ]5;è;ê­ÖCëŽÃÔ¥ºµ¡p¡*›“‰s5 äpQæŒÊ<—è`BÖX.d&”Šä|I2èd 53XND PFD§¬]¶àÑo8%(¼wk¹ —Ž]5;è;ê­ÖCëŽÃÔ¥ºµ¡p(Jט!tqŒd2aÔ¾F J/ÚîB¥ã—MNúú«u…‹–tV^Œ9b´Šuáº9¼“XkS?\B²ÅY5Re˜ñŒP,M0ãÁ¬ VìÕ$;ÑÊ+É(€R†D§¬]¶àÑo8%(¼wk¹ —“G!*-lîâe½¸qIÈi©ãޱ«ì[‘½.c!ˆ0¹kVÅÞW8;g »©¼“ˆÆ"të*­àÞIJ£¢#œÉ'6Æî¹MU4j&LI {L-Õ½'X,13 íw!Rñ{«ŽÌ7ИÓI:6?â<ÿŽ dÉ‚ŒÅ&]ÀŒ oÓ,äx³ŠÆà Š©ç³ÝÅf2E–22ZÁ“™b²`¶ãÀ²a9ËîÉÁ¼O8§;õœHsC[šÊ±I…ÎDaðÌd¢ývϹ†ŽJG˜ ¢†-Äàè*™°|Ç¥/ì‰`F§d·¹+Ñùv =°¸˜x%ƒIœñ éàÐÞ«L˜±Ò^R©„q)ù¶-8F€<ÑLC8±z 1Ãß‚ë .²éÆ6RbL…áJCóUD'^ˆaq:²Iº‘Ó1Ï*ñÑ“¯c,¼²…+k1•dO¶Ùú]í¬ƒ+ëÕë ;m””7¦ÖCqgœ×Ýr =½—ʲèO‹X9Ó­ñ¬ëÔJ RvéW€ }Õu,àVˆR©p ž¸×¦™Ø÷x5­úÎA•õêõ… ¶ÊJÓFë!¸³ÎŠ­+Ê’q¸=Y-º1 ΂2Cö=Þ k~³e}z½aC'm°žTà±—8 c*~ ð;´^ªªq xÐ`~£È-Œ®å.šÏF'ÃbQÎV® C9ÃÅšÅÖPV ©¾ÏÊ!Ǧ™Ø÷x5­úÎ €Nà£é@øpÁ/º¢n¥œ Ñ U.3×Ï1?Ÿl…áJCóUD'Y|ZöÜp¥[óàV¦'Ûœ´ÃñûbÇ$–VrªÒ0…š×ù900Omµ¿YÍsÆ©a¤!8g MH\ ¤ñ‰ÇÖÓpk"r|YsoN˜ç pj\%|H€L’ÈV6r8Ó/ ¬à€1Ž×8Yq’°ñ‚˜âK„MåK„$e£,Y;ÀÂŒPåŒTÎañÎ]³î_ášeC#*]>ùÉÕ"š›*~ú0¬,ûw¨éøÔT%iH{ÄØËu¡æ'ùǬ]´àÓg86ß„c•Z«}bø ÕƒE Ïð/..éß/Aëm84ÙϬ¬ˆ¶ÉèBžÓ¿€æ+TØ%Vo^’J׎IàgÆ+M´$%C‡˜ôN±#xœÃÒT?ˆ‹%ï“ k$E  ìMô¹»h/¡ÍG SÀ«@.[•t RI&I> %kÇ$ð³ãtÔÁºÀºž}“¬cHÀ^'0Á»J\êDùáóÍtv $mp\¡;ϦZðŠÀ °¾˜Gؘ¸‰`ŽíZâE@Ìì«,òmµZ Ÿ§Ò 5f’²ߢ\N¬nàôÂ0C@¦*Û“p½?i;ΙŸa:ÎÓ%EhM-®ò—'ÕZl§Ê`%A¡Q´©R˜¥O ‹Ô¯œ´ºGâýeVy@O3X Tp^ë+"±v¸*Q5 ÝÿŠàAª  ÒoèÑvŠ‚‹ƒ¥/¿¼ˆ©8’Ú‘Á—Pê±(Hg²Á Qn÷ T³Dë»Ó÷O€"ëdÐ> §ôM•šèí@HÚྂÕ2ê ©CÏm&ò Rc¦9Åž¬QðKþ$MÜè(ÚÍ„KIdíWÔVñð!àÀùÇ«¯Þ~»}1ÚsõYòõ¥ý‘,™Ôé–õ%ú‚—šW“öœý‘ù¸ñëƒii,ªúŠÞ ~< øõuûÏ×o¦;Nœ/øŸôˆóü‰‡rKŒ1·$áFVY™aNE™déÊ X!^ +ypœç-‹bŒª±™å«„ý8 Å¼‹9)NøøÎXúíŸrÿ ¦[Ž‹ÕÚë#Ò˜ïG“ÉËð5Ñ„ÅPBϯp»žÜ ˬ†û7Ï/j¹T_b3ÝÝOŽžÇF‘€^ý™9®’È4jÁO•ØËáÇÂI…ðá¹·¤ýX¾[©,1Ë’º=­Æã+¦³C6åÝ\&`ÏÁIgç߬  ÏaW½/ ƒÝ_6N³f)–íŒøI€ü9’7S0FTWêtÑÈÀ—ib n&ðÀ aôCE}?$1à}tj³i ÍñxÍB&]¸‰:ÌÀé†4´u‰é…~ÛÐÌe→΋HÓD2Îò³D'œì./ò롾Íóôo†„Å!¬£g!>NÏ¡nR´¨Œ•.]„‚âkÒdõ¯°’J“%× 0ÈHÑšhxHr9ˆ÷e`A˜y~E ’rpì;p̳«U¸ €åŸýÃÜõã, Pä…ß„Ê'<ú®5ÐM/)Ї¤„9Ò)–å]¨‚X– T qÑÚæ=A†BFŒÓCÂC‘ÌG¸C+ À¨ÃËò)L““‡aÛ†e˜½Z­ÍW (4Û!e¦ý?çøŒ} 2G(²&3LXÖ&0X¼G90Yש¶H„ÉLøÁ#ÛbiÙ„X}œg“Ö%/ 5Œ»ÄŒ0¶ ÅVÞC79¥ê>#4|gܿÌ06Õ³AÂT€=z’‹O–¾ ÷&FŽÜO×9X}ik¼ÝCµ·»’’’Ä64öÈy»yJ‚ÌÐaí낚SÒ#aÝ=©–löPa’\óÙx>®mî䤤± =°·˜Õ÷qÌMdÁ• ÷M„ $HÙ#‰{¸bH‘°¤G÷1›˜¸’Örª&QÙ³*j—ºÛ“+á{ÅŸ!F»Íã:¶à;-Þ(Ag•õqhä'x_{m†<ÚRÕ =î+Þ&qiíè-B˜” ·òO8@8s„‰4#NY L|È>^ jy´¥ª{ÜW¼m´IÊ>güÆõüŽ91…ÎAˆÈ²\šÆrqx'kÈ2L `¡‚ b¬(\vÂi’Pú4Á‡à+/x$˜½RÈôá‡ÆY_Ù÷/ð˃ C±åY>c_ Ìå¶šôñ€×pc! ñéÆ ÀØÎ–w¦Äå›Ù.ÔYaHå¾ÇUFw,Ú÷|øÆÒ+š–-»®|f¡ËÜÕ<3ÃS¬)šÁG®$ƒ„:F?ÌUܳkÝóã êÄ}s¹ç ¶%Û³)°woDI6Dˆâ^ï¥ Tw ¦]Žk«)šÁG®$•lÙÂÎaÜànÍwtõA,Þ@çA#¹ŒÞÈ›!:Hg5žº fbîP›Â*g=bkjcÞÚé~b44Ú$‚¥0©«Âè¶xŸŠ#&³×AlÌ]Êx(À"F&áÿ€þqÇ# ã§ ò#7ƒèÃéi“6LàÆhä+–bë!3£$7„²ì\ I‰¹ô´Æ¹ÑéÁ†?–Û>åþ1oºg5Ä}¶BRœÇàS?øÍ;"´7?,A £Y±¨ÂWgú†C“òº?Ü’ ZqÞ ‰üÇ%ðè¿¥Yè‰D™¢hЭ6°BP’ÔôG;‰V±…æ2œA•.sPÈϲ¯»K áÅÐaiX¼vÈ-]ÛjH¸˜¬o@ÏI•Œ¦î ]“(“=¸Ï¿]³">L‚Õݶ¡‘ÿ ó}`Hõ“ƒ8ÐÁk4G7y2`“Ðgè0Ž4ÄŠÄ›Âñ b 帢z‹³lXqJœMÆÂŒ/µçfãa‡Äg/‡lû—ø`]¸ü¤/;C„~«è‘?‡jhFؘªß¢Ÿéèä‡j¡æM <æ&Èn¾M»À¶ošþ¶®ÐKŒ|z{nf$gœz|Á¤¥JíYq:,ÇXÚ¼cµIJÞ½ç&ëGÚÖ\7¶ ‹8 ¸IÖ¢nVXÌÊÕPï‘›œÒpc«MÓÁÁ\bƪÐ5%9!/båÀÛ‘þ¢±gª£íø$(!øRºÁ§<Ú†¨^Î*Éq¥ `ëŒJÓ -xù8¤0ôÑÃà ‡áJë"Ÿø_›ëÐ$zÉžâq+"Ãso‘‹¼‹ÅX`ÎG§Ë"uŽb˜ °œ%¯A¼MàÞdI‡*e°%ä’\/"aŠ1KècñéðíŸrÿ ÏÜÇ jRüäws„U€—<|܈•pÖib´··õedE–Obö=zô¡ÑŽ—€„ jÉqêŽã‘Qop1…ÅÀt޽>ï‚LHr¨$E‚˜-ƒîø$ćÑÈ(·¸ӽ׳Êr?¡-$„«)Ÿ«‘Qop1…ÅÀt޽>ï‚LHr¨$E‚˜- TH(¸Õú~o¯P‘ë8Zãø’rÍd¹Ây pĸ:Ê_ÑÖ c3œ„VE2LU“Yc&²,3|‘ƒ5ôbûãÅbm‡Ç·Å¶}Ëü3•yeÄdo ~vNÞ•Õ}*$Çøä–‹ôQ6ü OÆ5 ¥î>¶ \𠉽ÜkNOƒÂ’G„˜²q"tfCqÏñL°œÍ„­Ä¦Lû¹ŸÁИ5JˆHô%_ ex¬%4ïL”K’S‚>WpVdl'åªPª«4Ù/w„–‘˸‹Ïfß :á_!©lý›‡¾,ÞDÚZ#Iv° v¢ì*pF`f{ÝhƒMÏ{ùFž„·†U?°V²œÜ³\½ä™}Ö‡ØÄí0_Å$¼âQvp:$‰Ä73eš‹OÉ4åÇïqç“‹ ¬\-o% C …FÆùÂ0bà“üÐHDtÛW0⺄ÔíÎ ¤Ê¶EÍägöxŸ…±f½š‚û?YùzÄ,j-¿ëž1¹m䘸ŀM¢WÙº¥iÚê½ Q™qjüýE€M¢WÙº¥iÚ꽈XÔ [×<`¦˜ÍÁ ±ùÂVYY—ÖBÆ !Ûþ¹ã–ÞI‹ŒXÚ%}™ ªV®«Ñá€Ð•‘‡¯Ïº;Ùùyôüß_ MŽáL˜¯‘1’`q¡‹†‚7‹.œQ8²2cN+Œl–/"rDÆHd.*ÖJïCù³î_áƒU«>¥Ð;hs‡«J"†µWÔÖþ?)hµ ‰‰=0y“Ï›`¼ †I’¹…cîA6!È åŒ‚uDŽ>Ö´sm¸«Ð‹&Æ&À ÉsC`K¿ýƒ· f¹'þCÃ=”lB§•^Iƒ,¿à÷k—&g#fÿÎëÍÞx9'ÿ©Á¿A×I¼¢c¤J ãVµ‚.Šâ© ±uŠy,ƒi"1|_$Iýür:Dm‘(BØŒd~Þ3ä3ú甬…¨–ãŒM;ØnÜŠäg…Ž`èÄâÎ÷Á2äéÑ?d"ë·z:ÓX‚1—ǂ݉í¨H…TbyŽ¢ži+ˆúã¨Â'a Rd®aG˜ôFHÙÔE¹åÞR–‘: ÍåñvJ„뢉q·Òe0AÊç›F·bXƒ U¹œ™pŒ…‡àþÞFÖt ìr÷\‚“ðQ—8îMK(1•!}講$Æñ&÷ïEXÑHDç ¤Ãüeλ’wÞ§ÅØÁ£V ÇB•ng) 8Á1"E<™›Ë[ÑO¤ÁeDÜÎrå´ÁÑ&¾b½bXƒ U¹œ™pŒ…‡àþÞFÖt ìr÷\‚“ðQ—8îMK(1•!}è¬<ÅüÅtÕ€3 z~o¯Š&¿Š=·€)ˆ;ÂÁÃ8Zñœd£M`ã'Î8áà 8‡8±•Çâ­z Æ>-³î_á†Y¹)<ÊÝ[ï/ØÃÄ–SÆ0¨i¡)“žTJ¼ä¡fìà>„Da2•<"êÌ7„›KIeíWÜV²ý‰ åþ|ìGäß#¹àúÛ`hKzÁlôÌDKå8YJ†ú7Ë 1¢‹P×fùúW±!—ˆ-£Œ 0ð’³ÞVNm¢Hiø"µ ¼Înî'DO¸Ú6ÂO>®L÷ôˆ™ÁÉr÷r¿25„îE®K¾á»²¤m”¾Ð–1–þ ˆh„MÅúÝWÒ¢žIm¿FfSµŽmJðÎ}^Ùõhµ ŠJÃäSFÒ _¬œ¤µÊÁòÜ$N'4Û:·èy1ì¶Î>Jå%:1Z»z ë©Ñ†JÕ©vWžºP¬HOf¼¨2”ìœf.w )‰wk q «]Nf `TO‰H´(·¬¸pp«¢fFÈw%%¨Fº|i¡ £Ÿ7œí#hV©‘²`öЧ UkUewè'57#ƒ­î¶C†¶Â½ü%ŒºÀÄN·oÖôËÆ‰ pÓbè#­j‹ 5Ù¾x𖍫9vª}ò½‰ ¼Ce<ãõ°óÉŰF.õ’çùÂÜK+¥ÀŒ<„žîíwè(îÂñIKoLdÍ©Ïí Æ%Ïó…¸–WJ7–møqÛÈîLi!‘ެẩ÷Ų.å—Ó5ßë0ò8U«·ÖIrù`÷ B6s_ÑNá(b–ÉÖ‘ š9óyÆ q¯ûú~o¯àçþ7H)“Ÿ‚ Háê*8.˜ÅcMä.8¼ S‹ð‰¼kâÛ>åþÉ• =<•[*Vr’åÓ“RÊ åHWO²/§É ð/€¥N]¤ÕœÅÜ?5)PÒ`mfmI·?n¯KˆCÏ<“éG<Ý)¤|)ÞH~$p#´ç “uÂ~ǰˆá÷¦£Žh[é±Å® 22Wkª"rB—Ü)¨šh™ m“$E#lb”ŠÈk!´™"°·&€sN )wðJƒà*fö:¡Þ4׌»Ýh›$½ÿtY†²!¤yô (éA§<|Qè[X->"gçð“`ÈÀ|ùU¯1u2bê.¢Ÿ„D†±*§:ÝÃçðt Óž€=7×ðóÿêú‰¼¬=!L§ã6±ý$2¸œêË/ !Ã)KÅÃ8ÃøhÂ%¹Q_Ù÷/ðÁû'Qª—ƒ†Àíác&¡€¸#±^+ oÝ‘ïÆ§V¢¤LõQ“ð}ձݮýt€©8¦€ÀiW8c¯v.AQò|#1á ÀXØ Ò±Ã­ñ’òŒ‰m8úÓ‘Ê\3SÊ$®#^ùÏmq’m-%—µ_qZõÒ?‡jiF˜ˆ»×À>ŽBšÉeYÛè)œÂfže!‚­çËÔà m kÈD#Þ+£5ÀT1ä7‰ñ×»Lü´c œokÓ Z2Êcp‚ìF×[@·97Z>Ö°a69 YL^wPÅ©´è(#ÎŒ‹Ÿ*z,»Rù¸Ínà§j1twœ5“Î :B"=ÌdÌähîûj?¡™ï‰I³.P¯›yɺÑöµƒCMú£Ð3ŠTg–Û¼7±DÙfátBíg€OX&í‹c‡ºþ¶®’1ν²Eö€(mH;.=9d±Â¶XS·‹c‡ºþ¶®’1Î!ÊÌÇ3õÌÁ­WA¾Øÿò7ÔA’ë‚÷nsÃ/o `@âLÏÊFÿf$\m–tN£ Œ÷ý< ÛCå±Öò›!â½ù3ó} œ¸ÿcêúŠkÉs—æ®XÃç5d,Ö#¿Aa9 …'[Í)ìÕOQ°‘)ÁÌajèíâ<ÒÀOã˜# 6'Y€ÎñÅ €!éN—ÐT¤íûaÛEÄÍoÁ !(ì’‚qò0 üsaFÄë0Þ8¡=)ÂR/¼î<¬—‚1ªÉ¿½§f!é BÖð8_ H våÝüß_ÆcÏ£Œ‘áÇø£>ÄÀÈÀÄŒ>ã0,Æñü»gÜ¿Ã#Ø%ÁŽÁ°‚’Žá|'庥ys””þð%×Ù–zP$´š3\jÏ(‹ïV~aFÀãJBV®|Î ôK(ÄÆã›d[é’x“ÜøÀð±ÀŒˆ¶šOXDÖÃÄ vÃP,S‚qáÖhÂáÜC-5C‹Q$ÓOt-S¦ ¥_•/`/A)Ÿµ‡ÿ·˜­à(#ëíŽ^L8Hö#%œMN­´ êk{ˆd·˜×õaÄÅá‚~‰e ‰ÜbO„X—ìæ¹j”«BŠ?"í´ êk{ˆd·˜×õaÄÅäØiRnX$YöØ|‰nÕ›J÷Ä8׬›Õ‰ÛòœÜ'”ˆùRoÄwó}åþ÷¥üß_ÑŠçûrŸ@¯émŸrÿ …±²Óu¶xÍ_h³ †ñî~o¯ér\ÀaÑÇú{gÜ¿Ãþò_›ëú|ŸÓ ÊÎ[¬åw•ý]³î_áÿy/ÍõýGž?Ï|d¸€Ö —€_lû—øÞKó}PÎ\”8ïý½³î_áÿy/ÍõýS9qþ"]aY\slû—øÞKó}XôŒž2\Dýí³î_áÿy/Íõýƒ:ÇáÜøÂÀÛ>åþ÷’üß_Ù3¬}u¬oÿlû—øÞû÷ÿÿÙyt-project-yt-f043ac8/doc/source/visualizing/_images/ip_saver_0.png000066400000000000000000000674521510711153200254420ustar00rootroot00000000000000‰PNG  IHDR{C­nñIDATx^í½mŒm×yß÷œuÎ>/3sîÜ™;3ä½d®$^™¤ÄÈ¢-ÚV ‰’8HâÆM8H\ )Š…Ó—|) Eó¡ùÐO.RÔ jqQH]¶ÓøEn¬&”Ñ’hJ—”î¥8"ïs‡wî¹çÌyÛ/§* ϳ ÿÚùs焤Êùÿ@·×^gíµÖžMýŸg=]L„B´Þú¿þÀ®_Ú²'¿óOÇçW‚U~CdǬ,Dí‹s» ýÐ'tiŒ`«F÷®¬m)*kYÄ-ûöY3+l`)¶ÝÛ9· ëG¿ÕŠ®÷Îí¹ ½ŸÈžÚ®÷™--E;÷ñltnoؽs;³ù¹Ýµ3¿×rX;²ît”–ݱ_ºf?mZ··ˆäV5k¬LŽ¿cKº ëá#à–(㥴n4¤,¾žlŸÛÆÌþ^ßþ3ìgÑqÛZer¹‡v7ZÖ‰·ÇÇe­â%³.lWߊñsávÚŒÆpÉ^?·{Ñ6ëØÌRdp=ž[ä%û7ì³þš3‚åd»:þŒ¸–¶i)ràëà¶Ä×°‚±áx°ÜÒwíö쯞¿ÂSûý3ûZô+óŽ™ ìÚGì?…U™Ã Fþ€Â“øª³@iM¤=ù-6~W¢c½¡ønÚv þ¨ù.Á—[?j¿­âåhÌу-jž«áÍè/Âqò~߯ø—Â׎¯;ßm ì‘wüÖuɇpïÍI{Ÿø9\Ê~rØ­èzˆ‡m—èVlÙVÛÿƒöý¨Qd‡"¹Ü{-Zâ?~ÆÂúQÿƒäV\EÏÛ‰¶S/úÈmD}öíl?ǯ³-ï ¼æùÂvb[na—Èó9>Ÿ[—Ž-éØp½bpÛèÙ‡Î×½o×wíÏGÿÅyØyû„¹†]ÂG‰;ߨ4Á8-ó}…ÎîBü+ƒÝ¢æ¿Ög¹¿iÖGí‡ñª$wgË*øH;Ø&À:ÂZàÅ÷®‚•þŒÐÿŠ®¼Óïåûd=èýyôŸEßé3»âË:Ñ3næíä²®¬UX7ñ-ì“ýù¾³ê²­ˆÓŽmøwÿ°R—ƒÿ/¾Ä+¿å]‡ÿYÃW~ýþCƒökìb!„B!„Ð`Ó>fï q`Û;CìØ'í!Úögì!ìûì!¶ì‡-é˜Ù¦=å²`Ú»UrÝ=\;æPÍ:XIÚiŸÅíͱëfówèdî@XÌQ,^V›Q{KŠÅóÅ¥g~U3œ )A–{ö1{ë®@!„ú!„è¼­=¹\õ½_¾„ïapQ複°ðL ‹e>H Ä+³Ø¼·k]ø]C±Fl~@“ŸKàg/ï+ÐÄñìÞ4ŠñŸ=òŠßðý¿ìööKrü„Ûwænü1«ôô†"í˜Î£1gtZœ"¾Üeí–NkýËÚ1ðåsZì4ìúÐþc»²ð^ž3 mšÿ©Á~‚åÁ>à!„$„B!„{€dgMáZð:™žž˜31ÛóŒN$€×Çé6/ò«ÞéÖ]·3ïgµ™Wx>€¯c:c?ŸQ§‚8}Ÿ[.pc{F2¥Òƒì²7ú¿ïÓø#?n?¹ç]"MÿðùS¿÷þ5i¹&sÙbË/Û¤.eÕ‹Ëúsü˜ äüŽžñ€vMUÆá<ÿ£ƒ)¼ÈY ˜Ÿ†YZ°V’»¼nœè€BI@B!ôBù‚•ÁJÔÜ׌¹æúòÔ¾èÄþË0/“Çòcàp6KŒWQ)«Ñ6¸ÜWÑ‚îD÷_*¼–uão¾ ë§+@íÝvûÑ/Ÿ›?õŒ·ÿ±ïóeºuêKó7ßx1®Üææ¢q{5ƒhôH´|×’.™ŽÝÅ×s}°OžçÇÛp¸ï¡.ç>‰ß_þ àøßŸ:ð’ó’€.,B! H!„>B!@iYiݺ x½y±u®ç‚˜ˆ}¶iŒ|Ë*ÔÍñYÅûWÖªOÀç áÅWqÌËHë·l–ÌTÚ^r~ŠX̵~ü[¼Bw܆ækj.ÓD=8ÿdü>6ü§%lã•ä1‹,ÚM۽ؿŸû°v?Öô“¿»io&9³lš:ɹi+¶±=_zìY?ÇnÝæ¿Å—^pýÝs=®è8% !„v±B¡€BȰ²vem[Í®·Hî.~aÔÙ‘’‹‰Ü‡±>,8¯;´àja]ž\¥Ì,I»@1‹¦ãýÁOãíß0q €û–'Ãý‰:;¦±JÖøýü·^ñö>R;šTÞþÞ‡#ûú¹Ù·¸”è#.(ªSsø«Jjš0ÿ{I@B!$ !„Ð@!Dçm­3؆ ®ó¸ì¦¹ƒè½™Íh? seͽKë7ªвM‹ ãäyöqÚ<ñ‚s¾ØJ›[È“çrouØÚµ J®îþ¼86áÞÛàõ¶å¸Ï1ÿOo•ùÐíWŸ>7Ÿ{nìöÍSo3Ýqû÷ÿ|tï'#ÀótH1%¸‚èt5N÷øo¡Íµþõ¿ð¸~ºåÊæ5ùõ漇ó“âû9¾Ò vB! H!„>B!@e¡²6hèѬ óz7ˆY®Õº6m09­;•½ÿຆŽBm󢬖_v»„RϨÈ7‰>NòA‘Z»¨ï¯¯¯Ð<(~nC‹˜šOc™•i·ÊG×û飓}·¿ù/+P½¹€|¨®Õòü3aý’øÊÃuŸjt}ulNý‚sy÷‘$„BB}„Bç-Ûô[Ð…K¨O º0å½Éj¢3ðxòôõE4'Ú= µ(’ÎíR£ó%øÀ7Àòð H]eq}ऽœÄb.­ÇбÌ_ §q݈æÉëÑŸÄkEœÙ•¤H}f{±Ñ4ná ñG{|àöÉcçænåõ~·í5˜j–oÇí%ÕÙy\?NQzJƒ?:¸ßÀ~ßáÏ‹~nQ|^þ»–HÁ ŒëG¦¶~ â1/mì „BB}„B>€Âº-ë»Zv|}•-ýîP¸ÝÎÓÑyß›T+Ð!FžØÕë‰þÚq£ó ½HûnA‡=ãÒ6  GÛB޶Çþ#óaäp»E_SóŸ7^ ì‡Ãû_Ÿi¸d1W_p{ó”N®¹;d´}nïØ7½;W?f‘.ÍKp‡µV`Ù‰^ÛRÞ,;‘Ùúàš¾ËP÷íÂ6¿ë5,lìB „BB}„Bç¶*»ô]¢åö‰·Úøv: º]¤uç| ”.£@éj±IcÉ+’›ÈmI3›Ñ:¥uQ Ã[:ζå Àqxm€Fdó´x½qš^»3_—b­‹I¢HÎÎ…`œ+>'\à¦k31FýG¿’žÒÑU·ï»îßù4îÚ­s{hÇÔÅ5b¯̧ÔÝu˜«ùTwl¿JÄE„p7gE¶_½^çç!xþ}²v0Ÿ Ý{aóœ“ò‚ÔB! H!„>B!ÀYgËZ—Í̾æ v1‡ŒÇþ#U¥]wFV• ©EÞg‰ÈûˆwõùŽvÉÛÔä&ªlþ.'í.ñø³€Ö¿­Ýå;éuYDöéuÿ­¼Mêh‚š@ê"4Ðý‰Oˆûÿ5ö$cÿ/G¹}6íÍX»çn’fG[Ô¾›ÆžãØðÕƒ¥'¥¿ßœ?ë—׿¬¿FÜ÷³>µ%бì‡çZÙv° €BI@B!ôBùìãÿ;lf¶}4e ï§uä³]h ¹éçãdÿe|V ßä¹ãYÂt¬[KÎø½ncò“žQ=:·Á¿Åú¨›6H¶±Ý!˜DíéN:_ÓÝÞÿé·£y˜Ùeš(Dvצ°œKV/ƒ¾ý:ÙŸ¸¥—ƒ¤î¿±X‚îïÛ¶%y4zÌlÜ}¦As—–ß :uóÚ¿•òŒ ãýÑ¥Gްppí‚È9v)#÷úZ=CçjÔÛöFým3³Ù·ƒ]„BHB¡€BÈÐÿìÿ13ÛîµÎÚɧvïºÛù€×(;–$qÎúäõUÕIöSV(üÅâ& y\Ä´ÈÎlF’¡»Gcä±=ãå“9‹–½hü½ |_87÷öî» _¹=<6_ìûÜž> b"óÇ ¦1@9X§:ícàçKÆûVÇ|ËíW?éþ·ã-A\PÍi^š;€Ljÿ:ĵö¾×ø-l°†/„Ò¼6/Ù~¤OÂzîÃ7ìcÞàGÿ[·ÿü[ÿï<Ø@!„$ !„ú!„|ÿÏ¿¿ýƒ?Ð1³_ù– ?ý/\ÿühÒ`Ow¸·ÏûàKpXÞ!Häí1øTĬ H&?áÉN»a{#×A×N »ßrû!?ð¹OÎíOô<õý–çŸÞþ†¯ï_óߺõÙæ‚czü´®òªQ^+‚'·Á½Q¸ËãýqënU§4.»"©ˆÞMt|êÀ©ƒ-9ý±ÜÚø9À^KãÆñcz®"j3µ+¸{ÌÁñãórȺœû$§?ÿsÑü5|`{ŸOýSoôô?>7á¯efvûke°‹€BI@B!ôBåúé™í3û±¿á¢ÕÏöZ ×Ã9€“éxêéÏA_v €:Ãuvž<PX‰be$¶©pٮͺ64”ý`ì<΃eóH÷÷ö÷mx›ìÏþÌEÃ=÷Eo³qÏíézÛù‰+föÜv+ØE@!„$ !„ú!„|·íCm3³G]ºõlé­Þx"™ÅŽžrakºÍãîc;jSYHjú« ΀޽ÊûÞcµYs>`N’¶ÍÚûl,\Hòw ò-C+šÎtÎ¥“éʯÿs·ÉçüÉ?{ɯ/¶’s[bí Faš'ŸÁz É> ëÒ¹õ}ÅÁ¤=y¥T+gú{ó¼ÿ0'8<ï?Íq+'¯3œ€gákºþ±!|®¸œ‡iT¢ãÔ®§ËìyÞªëqà‡ZffW.†@!„$ !„ú!„|ÿI/¿Ô/Ììð\è¼ù óùQ·£\1—ì(êèÐ’ÿ‰qE>HǼ· °A¿Žj T9KÒ\_æAÊܯN,Ž9–Á¯Ûð®ÛýqòŒÅs¯Gsr¿åöõ99ÚXE"ò‘Û[Ç>‡£ïsÛr°áYH¨©ÒhWuç˜˜Ž‰ÚIâ{FnÓ͆ Æí®,¿<É|Yí¿ìöS¿æöæ=·ï}ÈíoΓGsª‘Û,Ÿºs‰s_þºñZ»<ÖþðßÂã&æB­b¾Oz‘¿çZ¤û?ÙQÙ[÷|<é›Ùý—«`!„’€„Bè „ò<û[ŸµËoÉLw#Ýÿ5÷ï½üñH÷1IyO룘èèú„"%›¹¸¢xöœ`uˆÐÉ>€ŒÂ_7Ù¹íöþídxÌÍÃ(Æÿ¿vÀóyåõ~ÍUåöå#Ÿ«×žŽÅbX;.ÈâsÝ·G’s2°û®iÛ›PŸç°Ë“¨€_'Uçy .²ôàrÀŸÃzÞžêþãHçµü³¤îýSÿB¡\@_þw-û¨™ÙÉcÉ"¨;¢ÁѨe»ç¶ REè@,?Æ5úþÜšùªN|΀ ¾m‹)Y`8Âkئçdû0Üã#Ϻ½{hIF. ~è½ynÿàÃÞÖ6'TnïÝJŸ ˜lÒç¢F¨µÃdíâÒœI´÷ª“×6íU²÷ ïî1˜º] ÌßBµìØ^mŒ¢%¸›Œ÷ßÈZh»ë ©:$ï?lã÷,… ¾tãÅÛàŒ–¯Ì­BÇÓ¥[ Á~f¶“Ìç³Üy3zý£WþÚ éã;e']Žåø ·Ïv¾»öõü0ØE@!„$ !„ú!„|§6{“žƒXYFÉ}Ǽɢ”ÅÆ,-Tm¼˜Öýçô€UfÁ…Î|uŒázÙ!…Oá:hÐ$‰ IîÅEÓsÒcB×ÐÑ’sxx´í—óûÉàqk—qŸÉ3ùä‡ Ÿ&ÆIÛ«{àÏðuÄgœŽþ²o™|“ΰ’ˆ­ü|OgD´~–÷ßµl^>ºˆS<%óüÜ<âKqõÛ‘¾ÿ,wïůdÕAŸ—mÇuÇ?±‹1? vB! H!„>B!ÀŸúïí¡ÌÌ~òÝ‹bÆWIûÖiéöמôž^y&"¹N÷yÌþœø¨Öz1êþ1¨#c°0Ò‚<õxûGÚ‘eqrÃ^¬œÍâëT@Ìã9ïÁgƒgÜPD÷Î'Ãhž§I»8~ú‡±åƒä³LÂUŸÛ*#¹˜š'£ß²cÐëI¾û¥mÒcЧ·Gð6¸]—§×Áî´vηJ<½6m’¯ÝWX¶ƒK}‚ëtH)}²µÈ ¶·'Ͼ⥧m‡zЋ\’ýB:®òøÇyŸP÷\qöʧÜ~õ“qŒ?ºÖü A<†ÉëÁ.B!$ !„Ð@!äø³Ov fö? …ÿ`áöŸqÁëßÛðë·礪ªÉ~¦¿åöt7-nŽ÷AÓ 7äÐâÄÁ¦õ`± ж4x¹H^¯ÏcÓ­ (Þ°û‘?ಋ¡'?ZS7¸¦~@ÙAÛ×Åãî%½ì§È>äv”|¦[ù3öì?Ëá3itë˜ú!â3(ªˆ×… îԦķ¬j´ã~ ¤\n˹÷Ÿ#ûÜ$`—ºÖÖÏûÏÏ=ðÜúncš,š÷?®‘»ÜXD[4íFšŒèšf6§kQÕŒsÍöé?x9*ÝñO!?¼V¸v¨õOwÒ¹ž¾ùYß{§øÜ†ŠŸ'øþ_23»; vaBE}éÝÍ–™ýø°4³¿öcÝïüc$„Bœ~ù;ÿ¼e|ÛÌlñ–&ð©ÿ°ãÐ!„;Oÿÿ˜Ù§îm ¨cfo…‡·ƒ™ÙQ‹ÐÎ žÿ¿ ¤àÀá§@ëw-Þ…ÎtÜýIÝf—ýzÇÚÏHî~Ôýyrs#‚)$!ψ}20‰<MÏrŸ“kO‚Ä«Œç#Êæé³Ôs†ß]Fþ€Öt;)F—ãýšú \‚ YDþ€"JxÒÊ»5"ob½Ï-]Ì_ïK‰vÚOà´H? ¡ÇË”Y’V¢­xý“çujç¡OræöMtÿë_ç%%¢-QF®¦Ùh‡žÏˆçÏ%`É“Yæc¶«_…±=<®©ß» îOÈá•˶—?í^³|lΛ>ÎÊǹÂ[q™–Yì „Ba B!ôBE=ûj¾Ñn™ÙÍKQN•'V®AýÖØ}ßr‘ξú\p¼ù'=焽æÒ®Ý¡‚i¹Ú=éC!_?U[ “äï<жM³ª ”t®â~:6CMs@ 9Í!¨ÙûÇß]EÌ¥ÛŽûP0½þ%ø-ŽFŸÓêÕ§]8ŽkW,†¸O0Y‹”ö-ê1×€OWPޤ°zÂ!=Ôlêvo’\ÖUlçqŸ`7n×jü¢ïuÔÙ—¯Fqë¿ìöGž5Àõqȇ³}:é¢ VQŸD<žrçuïÿËnï݆àÖ*;i}ÿî ·Oæ÷ï¶¢üK>†`%æJò=‰¯RwöÝþ†é<Ø@!„$ !„ú!„|7ÿù·¯]6³çæÃtNÿÉ~²HéÆkèܾb¿W#´• èFóç¬ìh¸.¤¢@»OÛί§†ðˆŒ7z.¡ï§uù6Ø1Ø>@²_w›æêÓç –}hã÷b•«~Ù‡Py““ý[0~Ç_Fc.« ‹@WÊÖaœc=é)ç[nG±Þ‹¨^ë šÀò5€³ç¶Ìàõ!õ(KÛH»å <úQ‘f=3wçL‚Û¶ÿ%HEGCÊNZsyrNò¼Oóþ/Âõ}¬Ù‹[Ú÷ºÐFWc}?™Ç¿õÚ'jܨ‡¤ô…Ï'Œ­?IÏÕÛç$F÷‚]„BHB¡€B¨&ðçÿ–ÙSfvÉŽh¢’¸èÀ~Ÿhå¶ŸðÎ!7~Vs á‚yR¸ÜÈZàðþI=älFÆŒTº(<Öó½ì݆Gƒi¼éåf/ý _²é0^VÐå×?®qϵûé}52xõÒµ|yy û?±hkíÞÂ¥q ëß–hëîÂÄå.êŽÝô¦Ñ¶¹ÝLëŸáx \¥kùnEý¦½äÝÛ ÖÈç9ŽýÇú–}_ŸŸøÌÄòå`!„’€„Bè „òØ×»¶0³žkbð R˜Üœ>mA€p:šç šÁûtAÍŸ‹åùÁüþüº÷OŠ‘:.ä¤ @. eòÞ" ¨¤EÃ7½½ëì48} º‹ªñusoÝšcжU‡žc@xi„«/¸ýøoŸ›ýL½Šöc>Õ?ÿû^ÃöW6O-Eñò§A/BކCV”¡ðU°WÝlÞ†nïÒº¸Õ½¼–ƒEÚíôÒçà¹`éãÔO§nÇ”½æÉÁc+¸u1®ÆàcÆñtF;Éÿݧu›+k£ïÄ}08ζ=Œm0¼Õsì „BB}„B>€Ìæ];{Û@ƒ|‰Ø‰ÂIÎ}RX•ô ÐA¥!¨­¿O Ô¿‚TŠƒ¼¶_öÓbh™ñ>Ûã_{VÀEaÌôÏœ¹ýñžë±smúY$¿ùüéšÕÓ¿??¦Ð.’.ŸüÃî(}‹îý°Û_|í9OatÿZzªOnÄCåËWf<­Óæ)pý³Ñ# Scùî’ú ë&s•¡eICôm`iklƒ®)<áµpÑ 2÷Íøº Ö¿Q=ð)·w+ØÚTfŒÒàGÚË0fLã6˜úœïÙ­·z{5Ø@!„$ !„ú!„|[d6OÔ¡uÊZ|}nŸ¶Uêî¶ × ô"9£í}{`Ö Â\aƒdÐôÂ6éâsa?æ– ¦ hÙEdÁ±Ÿ Î0?ˆ¼˜;ȯÿÔ3.Ðî¿LÏÿÍÿ(sûå»5gv` ÉR}xdŸ.Ô…ož¼é>€orì[úúvp©?Î#”÷áñ!®|¾…ÓŽº9ŽušÓfŹOžJú':6ƒ×ŠÕRvªôø³èõ ãWƹã‘ã%²iŸöhïܾd¯GZÿó±ë”º[VÑW>·>ØžO }-û>‰+æ9ކvlf3ûà×B! H!„>B!:ïBܺÛ(rQÝ?Ö¸[Ö¦É18ø[ï-¨×¯øüðgDÝŸžX-¶x™S°¡øírIW Ë|Èíé.ë.^c›ÃQ$?Óq{»åõ´q~k|€¿ë·àÑÇcðÿÖ¯þÏïÿIÆóÇnß:…í‡zw6OoÓX—ï 5þ€ýÛ¨›ûáØ¢tLÅÉc> ùf³WµÊp¹±†Aúzn€÷ã0}Ý`8Ÿ[Õi¤ûÏF`ú/þZ|$®õ{¼?èþ>ŸÐg·Z&Ý–[vëþßåÂìØ2Ø@!„$ !„ú!„|—íÕ-Ë!©7Å' ëmböiߎ-QðÂþñ^?¯1Àaý ŽñþX@•ŸhR?Ù¯â|&“—Óí¨…àá1ÇxÞwûþU¸k•ñ¢¯ù ™èoþê?:·f¯íCëר¿ÍÊÝÇÒþ‰7ßÀù£mÁ0€ã'â:«ÉZµ?ÿ[·yÞùvqÜ)qŒz:Nãøû÷ö¸Ü{·A÷‡å;y,y}éæUÕn”nkUBÎ%g>leŸ±ýø ¹{‘n>´W|ì5(‡žþ3å6£4§Ý•Ö5S·á8Ývß@ì(mÌB! H!„>B!t ®@(^w*¢k» @ÿhß¼OžÄüýƒ©–µ¡Ö1Âçyû²9O&Û¡ btÜO»ZEãŸ%7Çjêâ{‰é±€;Ñ<·{È ¢¾ú´Û‡ŸJ'xÉÛõe$æÆ(O¯øŸþIœÞ§Á€:ûÈQvo þè³7qû¡yÌ;ÖÀ¥/;Dgw¶GE\ƒø VÓ^¡?n¤û·=Ÿ*ó{ûö²»fì4Š¿ ×}o?‰àH–÷€Ø×ýÑ•ˆº?æbêÛ89æ,jƒ¹‰‚UÁ.B!$ !„Ð@!ä–¿§mæôÏi›••$Ÿ‹Y(¼.›h÷ëÇûãø9ž ƒÇõó³ ¿ÒËXL„yãà½ËXxÅb§×?ç…R[‹XL‡uDò¤.Ÿ=›s½Ê’ão‚€h߇ô8‚ËݵÝE¤í.nð©sÝ?ð€¶.!1}?X•Ö Óº­_Åd¤N Âõñ¬Ú%qôñ¹˜H¯Ö…1óó  \Œ®¥ÅåvžN ³¢@‰6…Ëšñç( àveÉbØq 2sº4MV¼E-TPçòÞLö!¿¿kñn³ÇyÔÿÑÞ+IÝ?‡“~}Qs^á~z{´ª?o³c(™ßkÛ‡¼vÂ|˜,I½•ã<þè@ÝÖË]VD߇ý€¯á†äÙÝ诉‡½Jvßûí ד¹€ò`B!$ !„Ð@!„ΘµxÞ×R1ÿÞ‹P_B}~` Ï¸ââ£ÏÚugxÝãEÏœ^°øÀ[Ôêþ9 ®‡=@ï-ó¾%ÉûØm˜gZ¶—S3etØ–oñ|>¡H¦Ej/¤H‚ú“ýdœþóöë•SnyóÉNrm¼Oû¶l¦û·íà·/ÁÑ÷C _d#Ÿ‚Öïñþ4õ®Â^%Œ÷ç.F¶±¬7w_%÷mtI@!„$„B!„ ´ŽË—kÐT£_ÛÀud$gyüy?ðË¡Aå™Í“×—¶AEÆ ‚”׃¸+"2B]î7‚ul žEsÀð`jõb¼Î·=.MYAÌuÓν½ ãéMêÚËÑ^ì¡5rOî?ëöð8ççlŽzxŸ¾¬M^¥¥mú…Ý[nïßNçý_l¥küNýúÐnQÝ·%nõ[;ß6èϰ͸ ‡ÙW¾—ܵÆ_« îåG—*Ë‚ !„$„B!„:@4ñ5h¢_Рÿã×iŸÏù·Aß¿·Q.¦9~/Ž}xÝò>ˆæ<“?ï{H߯I=z¶¸ì÷â!îú²õcÉ8ñrÚsmúÇ ð©Cý=äiýceh%s•qŽ£ÞqºÖqýX?ù*ÓúQCçœ?p¤†¦«r{}·(ÏçSQ€$ !„’€„Bè „¢cf… <”•kÁѲ×Õ›xáЊt|<<Þ¿á9ƒÐpü]Öêþ<çôÏJ^¿¡|7t6·Íicò¿Žâr¾è7Ö&å”óšëw-ÅÌ.Gm–´O¹›Çµo´«©ÿnqïzZsïA—‡\üÛw’9yVe‡húüÖÖqì3 %;S¿>°7 ¦Ó‘•ô5YYIÚÃëÃbÿ±^4/Në«ØØb¼wÊÜfÁ„BHB¡€Bx×òÝ7mÿ®íý °nd÷iâÔ¯1×Mb%&Xм‹¢$êþ’Þ6`ùýôm¬éÀç-W`>ßeP Fíž1·!qóø”zŸxþ`çôßë§uù½[h»?5ú“ǸϠ ËQûõ¯%Ÿ%3ï§:{“xgõŽ\hXGØÒ$ÿ¾ó½<Ò$ H!„$ !„ú!„Îô\ê¢Zm£º¾õTëøšÇûƒJóìógA!õešç§ Æ(Dz›¤æn÷Üî½iÉd/«<­×—0~hƒÏåÂ7 TÇàkï‡Y[6q‚ÊÛyò— ŸÛ±XÄÀîÃOû°1ö<ÐÔIÞƆ§¯gäñ98æ^åS´ˆsìlyû_<·¯ýu8<ùŠ%ùò_†%@Œ¶·ÇÒóaÒÎì5w·ÝÀóá4u¹7!¸Ç¨»‹—Çà%L°=ÂÇ“Û ˜BI@B!ôB¡s(`¥ic›÷¿Î0‡ <tp‡‰¹¨û£=µ(©KfX@ÕõnŒ…¯:Äí¶ ÐjýüÝ“À °[Dn9¹B¨ÝÃr4ûO…æåâ:ú©ÜúÖåC%Ç#ü:ÞÛ!Óî:¾kÖÜ èù€Åb˜ŒÇÿ‹OúóþäöºÏûøÿö½gýÞ—?uƒñllX²ÓëñvMöÙ¶7HÎRb„Ôò]@þ¥œÏÀRf¡‹ë½<–D^7I@B!‚],„Bè „ò¬, Dµ²Šˆh „­#fQ±¬\¯û @ÒBð²Éóò>(J&“À‘–½sb+èûÓ ¦vQÛûs5'°¼ü˜ç‡Ÿ)áAÓe™Ñ¤ó8,Š»œ -¢go±ÿ¾d ùâ¬[‹¯ôƒºsßR”™÷_å›$FÞÇæ9vp*®oûëð™ë>½Y òüÀT/†i×ËC/¹}õø]¸·ìDs>‹çŸÎUa]VnÑàuÌãÄãëi›*q—áG—Lá…>BL!„>B!tà}‹ÇçZ0jýï6<™x›ŽÛ‡úŠ^‘Ž÷ïÎÝ®j’¨Œ@`A‚©k5wlýnq‡6H*¹}Ø9ƒU¶„ kиÏvÒ±ü“ýš¿3–̯§×”Ϋ*a}1y ´ñ½á}Ò1äùåd®+Á€g†Çn÷-"ŸôùüÑÒx [äÌ®Ôiд¬Ba–|Ì2ó©Xä›ñY„¤½|þ/œÛ·zñÜþ;§¿ì¡ÿ½–sò1H¯T‡ß‹ö4wÛÚ.nK~ÌÈmôøuzn ƒ²ØèŸÄìã3âs‘cCàs¢õ*p •u‚]„BHB¡€BÈPZ§´.ÉÛÃõñÐP£§ðDé1óàY´¹‹÷²x®ûûü ~=BnI«Uð7¯I³¤×Cƒ„æÞá Óq]xò“tÍá"‡€n Tïܧb¶˜< qô?Þ§=éÆhÕ »EÎøk…c(lì»còˆ·ßÑš·«º3#üås%¹åFÛwðºO#²ú½#n¾9‹ÎTé%(;ìuãú;ÚôX‰‘£0ÔÀË}“ºÍPZœúZ°OÒ¯¡$ !„’€„Bè „òTÖ)-#yü‰n»~½_³ß<>ØÂu­%€¿‹Úz¨ŽWU$/:i]ãúë Ì¢Öâµß íyráÞfµ<€ã,Ê „i0ß:߀Ÿ¥p¦»®³Ÿ^ZÁîÚ©su¸¶ËèP÷’û| Ë?›îZš3Ø¢¾4¸%ìÎSé)Ú»ív€˜ýÔô£\C7§‡Éc+vôÌ?qéÑ6hã<ÈùÐ?}<–ŸÇø;nÏíõtùúr·\° ‹B( èŽýR;Šc¹lO_¶°B!Æö»cû—o™+3+mÖ1³köã{Ô>¸!„ÚçŸs hn‡·õ#?À5úoO´xRßµF\[q±ŒƒÏ}Òö<ž‚Éë\ÜäÉÊYŽŒýçº?]÷6IÐ4 ~_AÎ(âW¨@$…¤ù¶{ˆº ¦ZNÜžç—b÷ ™^¶d\Ÿu¼ÿ,š–¾ÓÇ&¸>-’µyï_ƒi„\ÿímïõýÓëéþOú‚?#ÑåÑ.àØM£×ŠÌ!Áû¡þƒ¢¦$øÌ.ãø}﹟Éû¤ÇŒðÏTiY° €B… !„Ð@!”Û Ø&°ª›»Í…N‡ð’>2 IÐ7«€T(nr¸OӃͯc¾Ö|}×› HwÉæ X»Àíô'Ií;Ò -ìyLÝ&µdÉ”‚Û |˜®ªOÜ]ô•A7I•ñTK½š%¸w=}Dct-Ù¦·ñ¹p~ˆöMþt ÈS„ñ­ÎÝ¢¸OÎì ì1,µ]Æ÷6|ýç°+.F=!„’€„Bè „ò,mÃlñà$¶××uxÐ4‚9vHâlž¿ˆ‹zë |Áªdl{Y3~k 8¢ \Æ ÎX}`„äñ'Z?/šÊmÌ„kŠâ5&¯ï³ö®·¢Û`iWر t™pýèþÕvKëÒú¼2©gŸÑ9±nܦHOûd¦Ú—ÃÛÃÖõW‰Ñ°ÔGÙ°Œ¹øïò:X¯Î.øü#Eåm–ðg°¾Ž4¦ÒÊþÀû{!B! H!„>B!ÀVe—êcÆmÅõ\Ëm€â£÷ÙDg§±ó-¸N…TžOiž|œãcãqôDÔƒõ‚ë$ï?™Ÿ†õœÑ7@EçÒºdž™Ok#—ãoôÆüLÀ|ùpÈ’Öì%h²è'è³%ÀÒÓü¨µW±MÒ[ùñ‡žEúÞ|Óë׉Ý<·>l9–&‹»3CÍŸ µ¥aÛûžá¥Â´¥ì¹=«üÞV~ êÔ•Ðp@qq|B!$ !„Ð@!” ¨²ÍHß.†Òµ^ñšn;xÞÎ-“Ú#ù€æ½)sˆï&n•ÐÄ5ÅÝ!¸U–_Κ¡€yò>ØÐgÙ]ÎT¤4Øäy…¹¡K^UØ¢ÄÕ¯÷¯ #û®¥XEk´¸wæ]ƒ™™•Ä „BB}„B>€ÊBËÚ ré4µožƒd³5„¼`NÆéÐ$$¬ɹÄbÿ3Vµ~"7ÏÿcQŸ%šuº?  +¶^XÍ‹æ~½<¹é ¤†¼öÀbË"µþù¿é6s‹¸ÙèÑ·a«¤óÅ·éÔÅ®¦rrÅÛçˆ÷@™ÕÄø÷kJ2Œ“ó¼šö˜¶îÏ…½ž@ÿe5¥ª+²àÕ€þvžÞ®{·ÒóOnõåä ì+ë»!„$„B!„ X¬L‰ešpƒ÷¹>é¡1¡F¨ ô^ô `?Íó¹ÝæÅ<ÝFá˜êûü:ŽÙéÒÄè++‰¸¹†îó €¹š&Ûç“¡grP[¸ê$µì¶Í`Šb0µQ¼L¡I^®eãV<0oõ Vc _D.œûQ-ßí;`9}ž…”ŽXWëonKâ?W´‡³ uç'ÊŒçkª:0'i¿E˪`!„’€„Bè „ò´mѱ9$‹XÒÄm[‚^ 4ÌÅ_Aý[‡×'h7­ß›aÌT÷çý3_úN¸Ýè úi8ÞOM ÍõÏLp‘ŸH‹°.p;•­Ìá.®)ãv"#èþéÙYmÉètúNn§äo“ýš£ÇèVÁtLÐÆmô»,O!†Ý•8ñ°‘óLûÆë¨ûûuN€ö.4ƒ¨ "2´÷ç%,k4ý3Ò?ä/¢…[;.Ô’|î`; kÒòã2ÍŽ}€îïß…¼:¬ÿN¾áK?ÞØÓðÀ9€­»Éöåéuon¹ù1”æ¯'âÛ‰ëþÏÅTSº£œlFöÇã}˜Ô÷ûv }¦ÿDä6vB! H!„>B!À–½Ù· þ’bÓÌ.[À¯ÀÍ „HÖ&4né8< {}Ñ0-(·›Äû£o€Î?ÖFæB*€k„ãÁµfmpœØ†ß‹Ð,™õy)cŒÝðH×ôqŠ|iø6`î4¼Î§ˆú-º6õ¹šîB. Ð÷‡wÓ>€²Óè|@±èÒW â¥ª}P‹'9|ð^Î[ô¡3æ;Ú°ûP›º3}ÏÐRá¸gævìB „BB}„Bõò¶-y2Ì-CãʱO×|1Q’X´…{ kùxqQã5Ðæù”øZðö|œX€>{@_Ot“Ái|œÃÔ€f®áºöÊËSw¹oÀœÕ¬ñ1 Þ]J´­¶Vm…G.¼Þ/’Í¡n00ÝöPO¸MjP—?Ûá´y{°Ño´´èúÀà|ÃÀNÁ7à{rý×)mì „BB}„B>€Ž-2›ÿ늎‚ âáq 4bèßÅ2ÔÌsekåbTì^ §¹û;ó§.¶ççp-œ¼Á9€'“±jsdàO·Ï~¤`b¿Û¼AÕ|i0Ï>ö“ÿt¯NÓçñþã¬g‹íñUÂó à³öry{L!•Ñ~ŠÐI¾JíjU£ûßÇëø§`­WÇ,€Bˆ`!„ú!„èðúÄÊ“oðxðÆÈ£Ï V ]Öˆ} a©s¸²ÿr:“¯öà`6¨ô<äÔñ˜ÕTièÙ8ía³3â„oz'¡sïÎÛ™íÃûñ¹mžÐñ¹B6íM‹`UÎù©.êÜ.áÒcÂ5jWpÉ­¢õë}KÄcর¦ÓN£×¼ÂgÁ{ï_s{ë8íøÍfàgoÙI_·3pÞÒƒ`~æ„òBÊЊžeìv;O޹gjæÐ×nÕ׺dGt»âV篼$ !„’€„Bè „òÖË­OÈ1´Y*ì“ôߨ=¶a×Qø#º??©Á+5û½ï¢e+–‚n§@]GÓâñÕ × ë7z=1ÿÚ- $ç >K’É~:1\þÐÍô”yæ·Öœ-ÀWán-?j§´—A¯8 ‰®µÊBÃúQ•$ F!„$„B!„"& ¢5¨j]ÑÓi|ˆèéí ŠulFŸulN ˜`ÁîcX²±5×—Ï ÷Ó4NúƯó~ ˈì¾ä–’ÆõÇzns­·néªáv‰.·Ç ‘ãk•ŽÙŸî¦uÿcÑ}½Ž?W~…ø9`§ÏlŒ¢þ'~Ý"ò><—ƒ´>Ÿ};…¤oX÷iF÷ÀÊJöº¹o Ú°WÆg/Ø@!„$ !„ú!„|m+Ú–³d ×ßyÁæÔhsÿ&0Içâc&6ÓñÝ/ò@@ÞzÜÄ,4NaÄý ÝäY3»âmz‹H žyû('O¨`ë‚MƉy~6îñ¼@!çÏiñËÅ&}õâ¹Zõ¦ÞÑÕÜÞ>r»ê¸}÷1ðg@›éN<z (XE_wG¡¿g=$ !„$„B!„ ïØ²FDãqâ'us…Z|¿VN±âÚyŸèo@Ð?‘÷“9ºö&Ξ €y{?$„BB}„B>€ÊBeíõõ ÐÍi°*¸&:>èÚ\§n ¡cl{ ¯Ég ¨/ó¹Jû!ˆÍŸ‘Çì#¹ ñ€w×úygJã3%eÓó t/ùuïÖ—#©ï6€Üú÷fû×¾#@9ºvnÏ"»´3,/GaÀ†¼öãÐý]»ÇqÎ'W¨«,~},[¤ûï!Èûéñ,noG×ûà'€~ª`I*¨ß‹÷Nw’±ÿ—í5?*`'îæ°q£RÁmŠûâõE÷á ¶4‚ch[ìB „BB}„B>€Ž+§Ï»’¾Žv9úQx…‘p›h¾ðÅÃ~° -Û¶%‡6Ö@!•ˆƒ|Àï]^;š\ÿ¹N ÏX‘ÚÅü íÊBZO·‡^tçû×Ü~õ“.w?ácÈ7¨,ÞBå|hI¶îÒÜ;+³Ø€1ï?€¾¼^uxl~wýÃ6>—ƒZÿ|˜¬p©:‰uÿäŠý£knWY2†}5ò8úvmÀ`3šÏgµº·Ók¶ìsßC9æÚ}(Œ’÷ù9ƒ2ö+$çvc±Œtÿ#XkòªBÝtÝR¿½nÍÿôñ2ãù9 „BB}„B>€©íäv nu LKš´VŶ AÙd ´Í*TT(lW«D €æ:~ ·àwiPܪ[IÖ Ø²ëèÁ}âëËŸ‘CÚ³sHAêðÒÁ´„ƒÍwâ8ýäÖÊ+K²‘µÒù‚ön[’ñ¾ÛÓ]ø-ÐúËŒæÐ÷ëŽß‹:{™Ç¿›Ã³ƒ½€?^Ã^žÎ§Ôsûþ5žßÿôºÿÔbåe3z´·7R°Ò&Ø?ÞËÿ´¢Ë ìúcL^[¢°Q° €BI@B!ôBùÞ´´ìcqP3Æ\S :³&Ëö>AÌB± ¯/B$n¥ƒšóO€·EH:äõ­l q“¼FLÅÇZ߀SZ ~—ïa0Ç>Ëúµc.êNhUÁ§ýþUoðêÓïéû·nƒotí½[–¤µYܵ$%ÄûÇñï¨ç}^ç 71J>_Œ¹;3^?`Žþ¯·Œ>’ù0ý,'7’þ’Åô)ZÁ=° ]X;?™O‹]’Sî³)»5ó6®ç|]æ{oõ|÷í®ÿ»• Í,k4ÁþlÛþœ}B±ü ËÓ?*«ñÛ€ÿªeOù±ÆB!ºú;ÿx´êëÁ.,B!€=úÕUodfËXD«2ª“[Ó«I@Ûrš ~çyßý–ÛW_ñ Íñ>ÑpüŒªQÍaoƒA»¶A=éð3ON®ó åæàœ8yƒäéY#x2<ïÊmZ&ºˆ·å'ó呼ñС_}±ö½y uk]×&î.¬€úøxŸ»Ðz51øy~ | “}šß‰ÇŒõ>sÝ_áÑ|åöÂíÃÑo$çß¾ù÷¼ñœ]€ßÝ8MŸÕØ:¦º¿ÏžÏàí36'X{ÙÁuñgœ};ØE@!„Â@…Bè „ò\ýº O´”P$…³U$ü-ÆÞ—cëÆ+nﺽ› [g; ®1¸îÏ‹vVèõ`¶Ay=}g$þ€õ!}®}vÌ-/¸ê·Ð¥D— »˜€Îî[ÝZ×·ÛQ€–×üÜ@ſЀZ0Ø _cÎ"k ó¸Ÿ ¯û˜1–¿µüè_÷¹òyþÍèõÿÝÜí¿ì~š¿rßuù_ ÿGÚ¿òò]ð[À³£î×CÁó僴]u oÐι¥?q;DíÑ73z3ØE@!„$ !„ú!„|ý¶ÑÁ€Üš¤9/¼9Ý…àb"8ò`ÛXÌ(Æa 3¼ EºðiÔÿ2ßôɪÕúÓçÚ–EvN}®SןÊ^‘ÚÈ®õ“ëž œ×9xÏjJã´Àrx·˜¶¨išðÞôq|}6¢ËÛ½VÛÞâúò|Û\/ë/;0N°³9ØÞƵfJÈ“¹}~ìïŸÛOîµ£¸þhlÿçÏù{7}þÿ¦9Ÿø«Þþo.yŒ?ÖÆ6X«©jæ0·¦@?°vû06ؘ*mñ ØE@!„$ !„ú!„|ioF„Bo,ÜÞ¢ ˆ>ì…6¸ÆÖ.xàí| |˜Ìdµ÷>‹ÅЈI¼¹Ãj#¼î.‡·o2fî?Àù!ÅN›×^测¹ŸÀsÁ£;‡J» V5JŸuóäv¤qû½'ÓUMÚ«ȱ[½ì¤_¥¼ÏÛWØ oá|´™îÀ˜]³ÆxÿÿüOyŸ÷áMoÍÃÏDÛãwº®ûÿ£E$üw|'~=ÏÁU™¬‹@)ac°ÿùÌ?ÀóûWßc±c(zÁ.B!$ !„Ð@!ä8Û±°>€>Dmcp1üÌÝÞ¸Ç Æñþ[wýÖ~a)¦Óhœ½Âˆ_§×ݾ{ÃíÑU+Qu»Ì»55{˱ƒµ…½šG(÷ëÜb4ôOt:æ´§5IÑ]×îñº@÷Oךn^µ~,eAŽ€øt›÷“ÛlúêãnÇíÏvÝ>yÌí{×c›kúã}ÔŽÓç lžÖú?ô¯Ü¾ñUwýµÎ£1~*=Î<åù‰–c±JÛ›nþÉ_ûYE[ñÏÿ_ËýúË$²Äí;Oáq%<*ÄFÅs»ìÇ}’5â.XÌã´Åóa^¦˜I?ØE@!„$ !„ú!„|¯ÜN?L„§tBŒ{ :DØjÃÙÐýðÑ•'ù>Î~ò÷\ûŠô·Ÿý½Ã(°úåÈö1½ö°ßðêÓ‘ýÉXÈ£Aµ+sÊ|@Eä˜Üª5µ{¼·rÄëuãý½OçúðB!@çÎGƒ}ÌÌ–¶ yöA÷ß„¤Ø(B¡Ø·Çxÿ²“ôŒæoxï•¶Û/E üIå ÁŸ˜ßìm¦Oú½û{Þçaþbz y¿æú€×F®:àÕB¬S' Ì–Öæµ,KjÜ–‡A±’ãýshŒn8·~Ÿ7·>Ч8'ô{öÀ":Ä}âàÀ¶xüðxZ·Óí'ÉëªHº4Ú6¯;rA·Ó¢Á+ïî:|Uoìú–þ3™÷iÛ-wÑ] ‘[ÎÇö?=rÙÜ™õÿG} ÿÎc ão} ãÜÝ‚.½o~&ú«æñò;¬™·eã#,<ÕUnòjãõ":ŠÝ“£0KÛëʽ`!„’€„Bè „ò\±—» «.&Ì;ÄÇÒEÖƒRQëß:æþƒí;I!ïVð#Ͻ>ösŸŠÄ¯×ZnÿNîö·+Nîí?ó‡\ûùÃYÚ'ñÐKüLØ'a·@j ¸8è¾èÝôú~+•àžkúer +”Fa}Oñy“í¶Y7fˆÝ±9ˆþ®íÒ!µI*!ˆÏ·âíÍóÈÏ¡„hý™M"{N’T¨¦O¯õîC®ßêx.áW¾ñe?pÉûŸæ+×ýßô±ím´Ìÿ¥eIæ}nûøOÎvÓ)¼ÞxÂí›?êºTCø’5z`o_ìèŸ@rLÁŸ2NôÄöÂ^ vB! H!„>B!À†ÝØæwiúý(>zÓú<&=‚ˆGî?8}ÜÛôP ~‚ÝäúÓÙ?>·¿pècûëŸìy’?ÞM=y4¸»áu³óù0í“5gz×AØ·ê$ûYUѼ‘˜zØ‹ý—U×ÁJžÖ¯/ÙÞÀ¼= ¶º ÷úÙ·áy[Uð~j|];KÚ›ö&j¯ØUnýät@Cµûbt­¡»âÑý^˜v°ñYr\>ܺãƒ8o~r«ßŒÚü­›/Æn°dþúëOÞôó'¹çúµa2…Wö»pÔ_½c÷´G{ÑŸ²ÃF¥&ÚDOwßo'Òž¿zè{€mkÄúÇ×ZÁ.B!$ !„Ð@!äÈlÖµ3ÔdQôD-)Š-ì,s],"ÁqÑñþG:·gG‘Èxr#)ö}ñÖWÜþƳî'xd‡dÐñFæö“{þ,·Úû?Ï( ÷¡.‚û H±ÖÁ·„sõ`‚ï…9 KRø[.cýâ‡ðë<'<}™1JËhñÕn$”÷ì òüø~vÑ–Àã¬ÝÆéE—^çyäyŽšõ—iU§‰O·½Í´¦&Á«c·_ù”Û—Ò%C¶ýúa(<¿Ïk |â Õ¨¢×|Èà,áX‰Ûdyž„·_B!@ÛŠzyE4Nióäy‚*ö Ø2)l•‘¶;?ò󓣿êZßöŸ³suÀ­‡^tÎ`û½íÜõÉ^ä'xèÄ…Èm×ýóI,nžòšÉÙ<íX ¡öÍyaÏ|î§ê4_;~& ¾NÛs*v¦âfÒ‡Ô­ÊF1þ½_Tûtsž«õÖÌf ¦Ú4>¢ÑÜoSц×Ä’ç¾°yîÛ¸œDµjŸH–ø¶èõŒk vƒ%_Ùá-‡g/¾WÀq– s ÅÏÛ†–y° BI@B!ôBùª·e£ªöËPQñqem‰îïb"ÚËdàíÐ<ÞkáIɼöpdGAÊq<þÉcÉ å<:+pôˆÛŸ¸ºòÄåw\”ì·\”<½íu 6NÓ:~œïh|:>ä½éOŒD‡‚\ž‹¤d DëašžÉl_‡@oNö'lÝÊ" öÛçÖåº?ºÓ8Ø_~tƒ-ºO` ÀOPFv>€Z¾î@{´}ä—‡¯Áë%=b—^T7xùæÇ×ùÖý÷víŒõÉãýy®'~€¿z´€BI@B!ôBù* .ç1*ÈíŽõ 7–ä„hÁxΠ2'Oú6ì4y†àôÈEÀÑèOıùIÑ–×÷ûž„äGé¸?àªÛ7vüÙO¦ž ýæä…´^ŸÍ!ѹ· ~«]¼ •wYñ¿èÂuY_ÓGZDÅÄ;s²¼=­7À·w5u±ðÂ6£ël:>s½ð£ è*ƒñó©ðq2*r$Î7T5ÇVBÎËh¿ü#þúd¿íó§çª,}Ôæò¤{¯ˆ| ‹É&q•}ÏÀµ~Ž$ !„’€„BB¡sùO’¢€ÖÏ‚iòq¿ÉùÃØœÚñoµ§ºoàåò1,û–b ‹7¯F‰Ë#¸ZaÏ „ö|ú>Ö €H ס @‚‹!ÞŸëû§O…T^ñæþ$¤@!I×Ñõp´çv Ú{?H yùIIƒF©´r·aZàuFhéïÒ2ú —ù&äêíÍÓõÚEòèLþFMyŒÊGc¹ŽýÛ>„Å'£æ›ñ%×biÑõiÚç¯ ¡Iþ¨õ1‡` !„’€„B!„:@{sÄmÀÆóPãK( `ñãÞ?©[c Úë £×>îÏ ‘ÃcÏó“ýº5}ãuï¿õ3_¥ÊlÁË.’Êöƒ$û,¦[(Úâ¢õjÖ¢âyŸ¸¦¿>\àFaõýEdÏl'²/ûjô5åš³èr;ÚÆSHiEt’¾É—€¸CHRs˜CcêË|oûäkeÞík/¤é¼ò loð”´Ý»½sèk§«Œž«XÙ<©Ñ·Xélô 9_o-$ !„$„B!„Ðý¹ÖOüLˆDá²°.mo$évƒ&4©÷–¹(9:|DFL\~s÷£FODI¿î¿å6ô‰çèï–e´¾‹>ͳoÖ­ñTæ”\sçûÁÅnôýÐ5Å„6¸'Çv÷^¬ï£î¿ìEÃÞx=žêdl{kºí®¦h; ì~dŸ¢î¹}ðz º»ˆk S:·¡˜+‰㈿<ùnúçÎÍŸúÜØ›gÞä§ÿ‰AÊ,ð T(‰TàBƒ±Í'WÐeˆç6Ào¹›Ð€îÏæPŸ OÅ& H!D°‹…B}„B>€ÜKÛt1+ÒRÑ.!ÈZ_.Šñ e"xÑ“äšßˆÎ L_ùT:‡Ïî!‹ ïožÆ×‰¾ï@{Ì„ãÉvhÿÅéuŸÏé0¹¦™ÍéZ´`ׯöóC<°«1Í6Òyiúcš²©5zèܾdGxÜÄ}¨û§R*qx¹‡jôN;¦ðJQÖé »¯¸ýÙ¿ïKöwz‘ØÜþã™_Þ>²ˆäüÛÙNS@ò5Ye¾óüÎÖdW¢Cý1Ø¿ÃÛ{š;ÛÖ vB! H!„>B!À†-»ìÉÍ™îOE«¶åT|¬ÌiAÁUtLr9$Gôï`\|·ò±-OKëï÷ÜîÎÓAÐ[Ç(.{Ôî{cî'˜î@p4 Ù<9¶2OËɾ?ãôJMâš=C­(iÔoTXMaû÷ó™ù³LìÀŸqëÌb†·aªa‰£)êN]¿Þ±—"Ýÿ$šº1Ä’ûÖ‚ÇG 7¯C€¥ðúªÆµSÚexeüU¥ðRØ¿";w»ßŠJm{›[ûX'À5}|ÊŒú|Æ–]"q÷þšøºãœc-ÿsÚ6F ¯Iný`!„’€„Bè „òÌíRe;®“‚~ZZV“ìb–Œ_QÁÑA=7_#Æ é V·‘Vî×W‘(l‹-:!H9ïC°9ˆ•›÷Ò>€þúŸA•ñ:±¯bÏ’b÷*Rg£kn]C,6!à¥)(bÒ»¹îÿÀv·ˆ==ï-(›bÙOjʃ©kÓÛörd¡k5_ª#‡ÆÛ5#ñûËÏ\hX'À£Qá`/y}uåö¶ÛÛ-‹Ö¶±o{ÂbˆZ<–¼NÇÑCü>ú¦v…þ‰÷C9…`8¸î0NîÛXØV° €BI@B!ôBù¦¶»´ƒïÒLˉ9E”èü2MˆÑ±9]@ÄÚ}éÞ *ø<z#æåP¯#¡àç°V0úì'›7:g0{õ“>W‡?àÒº{÷ö íÒZÁ\h†Úï¢1?ô‚ÛÛw <媉Ò%mL]§¾b·Àíá1àð8Xú‚¿ô1sˆýgº¿Û™ÍH}ZvŒsU’ùñ—¡-Á‹É-úsÑüÄßõ{ö9ߊ·þá‡Ó[1ä†`ìzæÔOÃ}0¾¸ÖxxÍgÜÜ@òØÍÒî»!„$„B!„ °îÊúfVf%ħC²ìH\[.6yÂk+øÞ[PÔC³qÒð%äâ÷gÇç­bß@~ÉncŸ¶qJôwˆæš~È¡v±kâXÛ`{ç¾­ZùpFÑx^úþŒÑ˜¼üi”E„r ‚®%=ÇØÌóc{/¦Ÿ±]àü£î¿5-¼'û&èþ®#7)W’ÛÏÜ­Ü'îŽÂß·Úbüýiäñõ±G¿âöC/YŠÿâ×]ÿþ·h ¯ü‘tΟ7ž€#/è*Ë“Û~UµIm¨ÉL]•y]±üNèï¡cã%ܱ,vŠÊÂÛw»!„$„B!„ Xõ¶ÌÔÎÛ>Îóçdµ…4¹0Ó^'A6ý­PÅDçý߯êsKQå>†bt-#¨7©Éÿ3«±¡ö€ÛìLÀ¶%uÿ¿þÉžhÜíô5ÃÏ”_ˆ}Ég™>ãÏë×!š;e>×úqÞ0Y¼ ÊÎ¥…Oû®½ì³h÷I¹ ˆñ'¥,¼ =’âÚ:©7‹yfÔ©¡ž÷cÿgæSZ^ý†7úȳqŒ¤¦òõè8üTÚÝ5>°$!¯)=O»Ö*2ÿ¨×Ó¼=Þç}üøKºdE÷‰¿&ißOeY° €BI@B!ôBù2›döà»Å¬é¦Û¶W£­—Ôà‚)&+øõæqý(ÑÞU¶1tÿvÂ"&©)¾’íËé ²·½}¨p<訩3|úâgE¹ò÷6|ÌŸÙóg~ÏÛgÛw}wÓAßUæó÷1·#-¾U…Fhañ™ ¨ƒ"ò½(Ø1³ˆ-;Nêþ›S ó³»ÍϹú>\÷qú–pœ ÇßNº^–{¯G±ÿ_†r ³ç©äQ{ùÓØKbðåEÒ®¬ËukÀº€¿æ3Ý&§%Óƒ!Þ?® ž@7[×–àý½!„$„B!„À¬½²öwéÑ»"KhNMœÂ…QL~íâèþ.\¢½ˆlë§ÅIJCDô$yÀ{W±°XùœW‹Ž__\©£¯Ç~îX “¢êÏüîožÛ7OJ·ßô9É';è‡ðx|Cì']õçîFþ¨Q ôïÀ³Àoåƒäoõ+LòãK€u\y b×ϯsÝ?§>< ƒä–Õä´á9dfvÙwÍöÄ\ÿ’Û{·ÓË1Þw;*#a·>ë»Ã“¥8fU?>bÂuÿªöºåÄ+ ĹƒHÍ<2•®Õld­ù˜Øÿló­Þ[fÖµÏõìsöB!Dn¿‘ÛošÙÜ 3«ì¬cf[öwì£þÉúÀ!„"³?ýÎÿ×maß vBå*Ú–CÞž*)lu1@ØÁöÞ'OrÍá¢*ˆ^'íYØŠ4ëC.K·ƒ-›Ÿ]àmèY ³)s7ËÑVd»Î^œ<–Œ‘?9¹qnÿüî!×Ùç[–dûœ€ö'7 Q ǽ1ôãY Óy~"Ƽ­£Ý¯ÏG.4hƒðXþŒ%GÝÿÁVtïG¾àöþ-XpáÄÛìð™dÎ¥Ìæð ùúw+»½‚9$yxh…6ÔÆù\Àñ#_k~d Á³¡Á³( H!D°‹…B}„B>€ÊZ.!¥Å¯øzÞHÐ $¹6×»Q8+­KkùÆZÿ»êm6vÎÎ nož¦EÃ|€6“d/ ëßò¹å¹•¼ |ó,ÊÝïú{çϹÍÕC/¦߳ݴ¿$Ê/d»¯ hëv¬û#|Åí'>ŸãqèýXgTöyžK‡¿ðs®ûÒ³¸~î¢ãZ?Ö¶íÒó £Þ¶ßüÑ_ÇmàG1QºôÂÆ¨çožÝAÝß_y$$—Ò¡æxЊÔrðù$×~$í•Á8AÇo1W+¾Î´þD€>+ Á.B!$ !„&„B¹€ˆØA¯LèÄë(ñäé¨ï£îÁ¶c;ð6[g \B²òP¤“LÍiçëw7õ4=OÀç‡ñ·ºÑ§×›Õ%žîò„-ÛGn_†|Aø[ùÚC™SÿAw–\·"§é+mÖtñ­‚í“þ€7í#Þèñ_qûê \÷Ÿì'w´àܾb¿?/Õ²-jÓ1ß*}{Р4›+ŸÛõk`nžÖ¬Û°&Aˆý%ÔG ÔöÊÚÁ.B!$ !„Ð@!ä(-kYâÙÛDüjžœ$ÔÁ`ä3Û‹®yNÿCHTâ׉Ö\u,I™% –f6aZ=`™ûN8|-¨ÈÌ)ͨ]Ž®r}?ðëÙ Åb¼7]{yº‰eÀ3º†u€1ERÜöKh–ܺ¨GcŒ?ш¿i†[öÏ]k§vÝÛ<þÏüÖ} ^`ºÓîyÿ±ô†Ʈ»äxVÄæ£kúðÏšÛ¸—¹Ö88NFY{½KÏO૽²V° €BI@B!ôBùrë—¶I#¼^Z—h¬\ðrÑ EÀ™¹°86׋‹¸Æìm·7¡æ-æ£ï¹¾Se¨}{0²?;êþ˜Ô…û?‹’MXaÔǃ~‹|:ð{§Û«ËØg°Œì]î±øÞ;P'}§×“~‚žÕDL‹ÛÍ\/Íuÿõ©,P]_7Ôýùšß|ã é£3½qºôÂñãÉr—F-_m;Œ_®_·Óå¸;öç_C²ÕI¬}³ù'sÞüÏ`°’ú“`ßÂØóV6vB! H!„>B!@a½`}L^AELô ¸`~ÿ´ÖïAÄÓp)ûeÿ6èû ûc¾d1­ ží$ƒ»ôís‚6 yÜPõPßç…C‘6$ZŠ›Þ?jžnEׯ§µþá1ˆ¿°Óš] Æ6ˆÅ,ø:·Aü´´uÏÎà•q=š.}# º€ÜñIÍ—¦Ìºoz©Œ«÷윧~Ííëÿ _+_2B!@ËÊ–•HË´lÌAï-#O"ÝÖ‹kɾàö壴X¶q ×AGЉ®/£ yïz2®¼WÍjÄßY? 'Ë,Ѽ·áùýÝÆöH‹ ÐnAûì߸VÁ2ö”ä_‚`ó*K®Wkr¥&ÿÒ\#'µ¤¼èûdzéTpx¬·_§úòÈ®úîÞòëö±ÿÓíG¿ ®5`t-톹õ¯Ül^ºyÓÞ¬ÉA´‘LóµÚ~¶ø˜šŒäðß‹÷w ·SüŒ<6¿7¦õʸ´ÉbÇéºìNCBá}NÞî3 v!B! H!„>B¡z½Êú M·A&Á³D”„œ?éàßá· ·Äò—Ä};Þ€œò7Òùåïî ßÛ 7ÉÃcÔ€:>Ç'À89¡‘ÿ óþ±¶„“4Ë*—·î¦ë0Wxþ18Ïdµ€äéS» [JÌ“·Öeº?ŸŒ7çyó-$Çpfî&y½÷!ïèS?çöGžM—kÎæi·Ùx?™ëgäúò%{=ùìsÛLêþÓl#ÃËi7Þd¿æÈQ…×1—ýoYI^1€»—BÇ/ Á·[Á4Y%þ‰CÿÁfd—즫y° €BI@B!ôBù*Ë̺q" ¶ˆ®ÚÀÀІ"ü;5¶0®?­¬+ëZ?ú6ìÍ8–œ×°%­ÇÛr›ÀÎO xnË™0Šëå×™nÎsC¡ÀMÏC,óM·GWQŒFÁ”ôÏ ·úuZVº˜î€ëã»ÒÀ¦—^ô=ô“>Œ»ö„ßüt¤ûô éxÿÃk%^þ´»^^úá¨ÞïÁGâψqñc;Hγ ï¦Ç¢m3!®8¿Žgª¦e3èÅÛ,³YÔèŠÛ[Çp¤ ˜oՔĘs×f|½ì`?î:ÿ$Ve° €BI@B!ôBù,;³0~K‡ãô]ÜlU¡Aœµ‹n€Ø×ý=Þ?DbÝ?ù\YÃ3Ĥ»ÍüÁ*ôO îÏÄPïÁµFŸÌ‰ƒ‰tÒûªS¹Ø],¢yˆû9¿B º"í–Ëî°N‰, 5~!¿ÿ‡¾ëþ¼Æï½´îoÿò'=u=‹¯ ºýÒ±ÿ½.”÷ðñ  •wiê-,ÇÍ]Sé2Ѽnì¨ Ž³¨²ô3^¾CËZØøÎaÀzMw¡´É¹@BI@B!ôBùÚ……Ü5tªV‘ž¾\¤sè·ó6Ƀïøu«¨Ë;>ë¥EÆl‡ûâØ(Æ|PM"À)Љþ[¨Ýc‚okïÂ(DÅ‚´TdìÙIŠÂj ;ü<Á¯c 6 mÒg ¼Í „oRg‚î=L&“L+´]KjÐf÷ãŸÃ”V‡¾N®š>kv"­ÿYÌï9âA;ÝÿðSIÝÿUëž±´A2öÿ=ì}^{| _/Ýæéžx­Üm?èø$ƒãéÛØ×(>Ú²{ˆ9|ü¬j÷o<ók7Ù‡½ê㩯I°ì „BB}„B>'T„ª<²ËÌÍHäjW~"™$TïÆß”`´O>çx‚ÖH¨H ò\p–Â×íVªH,Ÿž“g#g- ÁmÀ} ¨¿Î›¿qå¨#÷a;AŒùKŸóÐüÉâýÝGR·48W÷í‘Hãþ·w_1Àµl¨C0°ß×MGÆ![Û8K|ýñ¨PòÞr>L®ÑÞî™Ï¯unß:¹ƒí‘t¹‹Ø?¼Oëß{«é(ØE@!„$ !„ú!„|¥uæ±8è6Rv’yü»ö€éìêþ4'ÌÊ*Öÿ.Šw$pEÌ´bá/™}Ù‹ÆùÐ掠þý[(òz¼?h­ ¸ØAÁ‘ƒý¤cäažy?Íýë“q?µ,бáít{Äå¬mûµtî²ãí£%.ò~:]Uû^¬és;›ñ³UÔÿÝÇÜ>|ÆwâéåHs?L‹‰)`®0OQ±s7û¾?‰Ç™ôUlÙ1ø!ðÀŒº{6µ Û€mïã÷çJ¿bX‚}E¸-}-ðyO¦+÷+”æÌñH“ƒ{ E4ϧèOr_B~ì" „BB}„B>€UÛªŽ™t¡ÿ‚v×Iâl^;—%Tqû$cà``ï ™˜eº‘îÿ•tr‡^}|-ó!ÚþŒiÁÚ!~âà°Þ'¶¡EYì*Â:8flSY€¸iŒ¿.Ý ³ËQä®×gs·  /†ü˜Îâë¡,vßÞŒ]eüGüjX?™ÿÇ.Ãí´oÌGtô”ßjßJ—醚ÀkƒFðqËqà•÷µÃù·¯þŸ’lFYÜf«HûZ¶ïàó¿xì" „BB}„B>€ùÐZ;fÖ³(ºa¬}R8ë@>zÔ@8ó{  wƒF\Ùœ'é6LÀ‚vžîý (fsÐŽ$?Ô"öó„••5ñï•9q› ü=¼n0¶iQߊÈñüs–´Ÿ~Íž©``Ÿ@^—jÓ›U‹àÚ7,q»ÀmõýÑËïÅ2Pê@)ï&y~æýÍûœ†K SƒîŸ’ºÿöÔcö/Ùë5>€9)?ÎÀmPY ÛµcKØ6è3ÉÒEÕö¬Öaññ`\úÏÎ!úÜïRu‚]„BHB¡€BÈÐ.¬åâÚ2›Q­?€¾ìÚ4+(Šú2ÞkµúòŒpPƒ&Ï^ï€ø Bç|Aâ`羚oÜó.§'jèëÇàóx¤MÎàÜ®/B÷Rfmš' }(ú£»‹nѪòëÅdÓ¯O†4~¼ÜºÚ=èõ“ýd\ 9=s€9d°®l'™¶«þ•Y±tX¾‘ÉA²öïàÈÏìÙWÓux‰_#ž+ßm_—†[·l~tÉ×õ}žëÛ`{ìÿ Ò»Åó_΃]„BHB¡€BÈзIÛî›ã2€–îhÎ}¤4ˆ‡ë¹î¿ëmH¡N Ic[ì@`/$ú¨:é"®Ù{áœæe òšXûùèRMʬ4&=îg1Ý·J2çOßîãÖÅTO_Ú&ø6PN¿;æÛþ’ññXiŒÊ2ÐÏBŽûùg®JÏøuž"¬CÛ· :žùÈìø­ÑÞ vB! H!„>B!À®½ÜwÙr¯Àâýé˘Û`,?^Gµþ¢6ï›Ö!hA.#.«éÐÇ9œ'‹Gvºðl™o5ˆy‡$*ø\§OEð×}Þˆ/ú\\#|.ž‡ ó¿£ûm!·]+GMR9y{„/™ƒ.¢®In}HçEsþLí2,·kÖXàRu>?r„¯/ñÍKMp~}÷U«fM;5ÇSÊEìï9°ÈQ“. ^z)@ê6lþ•¥‚½'!„$„B!„:@5è–…X\ãµpAl(àrÍ”G…dg£‡üÞPðü?ôs8T,nQ}ásÅiá¼ñçâ×I°6Ô¬fãç`WmÈŸvS@³f.¨¿[ßkätZ ûoÒúu¯r—Õ¶½æ;ÝNkbÕskÑýý:Óñ PjÿòòÝ8·‹~t}ƒhýÄ¥G|XE° €BI@B!ôBùvÃ6i¼? I˜'i5/ž‰m0È—¶/¬K|ï"¯ËDsS(Ô‰µÊNZ` :yÓæÂ¢ƒsÔ[Ș¡Ïõtob46vËñ!!KÛ0sÓ7:®Ñ¡®>~Ì_4 º¶UÔïõ´û6&å»ß OÎŒÕFWµ'Û^1øä}"ü(¿×Ÿqeí`B!$ !„Ð@!„μ? 8à«:8µMâúK*†¶¬B¡É^Z‹Ë>þÑ5#¸Àê€o`U‘ÀgÔÁÁæ5{±žý\àvJ lœúˆ` c¦Ã^?‡ ÙNX[Ø Ð†éÀ6äyA†%&ºs߯àbñDؾCÜNkÀ¶ Ý~UÝz5Øfø¼ÙMÆÌ‘$„BB}„B>€`Õ»!½ñxÿÁïĸ¦Z‘Òr2N_vn%ˆ¹p&àÞuKÒ£Öïg°N@e$‘¿Ž"#ŠDO'…U™ðê=’<þdlï68E°°…V¨þžÛ€žEè]x]Ý¿$Ç><¿jý¨?LüŸÃõKDTAË_‡5òQp~’~ @ÿôáñ\ç,Ø:!„$„B!„:Ð\à VøÅ´xÞ'¿wZv2.žÔø˜+Kû¶Üî¡p(Ì3OüÂãèQ—‡ö¬Ã{ \w¾Ld¹î^êZŠŠ¹©°Oª¿s˜ŸŸH§ãºÿ’œ]àKÆáiʸK24ð$-U£-ÍKhðóüðªýjÛúQ½æïßµï·B!Æö»cû—ѧ}Ö1³GíÏoØ5ûà"„bh?ü¼t›»°!„|•…ÊÚL¸äB ‚&íµA ÉI°6øˆ0 ÉÁ©`ŠñõñÐÕ娆ðÕÆ¢!OSQŸãí9üL@úz»©°N Øâ~Ãë8˜ª×óLJ¼ÿx}3™g¿°.ÔÝ%ËãÇ6ÍÊbÃõú¼ö®õc¼9Ö¡å¾ ¾…¿­óÜ IÖ×õ}6ž£‰¯‘³þQE !„Â@Ø7ì!îÙWí!Föœ½3ÄÜþo{gˆc{ÉÞbb_䀱O(Aœú€ Fö{ö΋÷ã €>û€¬ä‰>Ø9 Ïe»Zݼa .ú p ³É~MŽ âÝH’ÞPì|nWÁJ’@ŸÎ9Š¡~Öýë“Û†êZ?>fnýX/.lÖå¦i—øòñ©ðíJ|ûÿ„—¡^ÿ5dëŽ.:þšÈ „B!„Phi‹©½A”3K¬h²jÓ¯¬ •µÖI7MóñVÖ©‰Ë,¢´ùÔŽÞ2:p/žéãùzI;î§°AÔÏÝhÐ]·Ë©ÛUÏM+£~F‘¸|?O|ÿAd¯ÈY|>Ÿx½UÚlf¯®)U–Å}˜SûK@+˜ÿ5`C]Ú(ù˜…u£ö“¾ñm6¢ë›5¿5‹ú95gÆå+`ùp+Ƒ͗’[Ý¢~r»àNÔþ,²ï'_ÿ¶Íé|ƶÛ1iOà †œæ6ë¾¾¾î$‚™Ž¿&>N>þéÂ^u?ùÑÿ I‹^É2²õIEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/ip_saver_1.png000066400000000000000000000201471510711153200254310ustar00rootroot00000000000000‰PNG  IHDR{C­ .IDATx^ìÕ=hSm‡ñë|äûhl‰é‡5X[¥`¡;¹)òN"TAÐEtsÄEPëàðb—NÝÄ¡ƒ ˆ›µRAh?(ÕVMÚÆ›“ó¨SbÿRÎf~Ó.îé¹ïÿ©¥¥¥¥Å¶&ês€BÔCÃîþa—?(U”BÅ Tý¿iÊë&”fa5@)Uƒò#¼ÿ6nâ.R§g£d’ºéo×Íàvå@—‹Ò™°iàâ寣7S4ÃG*cP^,êAOÞ×P&¯ß&$^)Âf}ãn‚ ¥Bj$ÃÚÈU”xûÊ@Æ ¥™zà¿Uë£çøAYZ Pt£ÏpH¯GvF‘^ùü¿:z.ÞxíSþäK¨ÏÕr€ ¿®ÿ³[)`¢Û »…)(•šA7HѪAq¾(…¢…âV,;aÜž ËŠA±#Hñ´…²ÅÓM6k£ôîpPs.J®ù§µ-måó.ͨ!­`P–Ût3MÉí'$16ËÂsØ Öƒ!E(²Q»Éë²P²6J³rŽxŠi¥V7(‘ e½H(ç®w—ƒ’ïs‘ʦ±ƒ)+¿§1öç÷•‘°r¦ ̬¨zÖ»¼œ¸VØTêFªÅ‘‚H8sê¤J膅‡…ñÓ¢Q*h³Éb(ÍÓŽ9¤®7:é{‹2qiàõ'ÿðø*ÍÒ1 e~µŽ2ý!4{Akqï?yyt®ÿèÎ#yñêK éK À>( ›ßW|ýýLqô"á›;Šƒw\Dààÿ@ÐևЄíØàÝŸþx    €BÒv7Û½ši)œÉ “4#y%+LemôÓÜ3Ýu|KÖ鎬j5Íïye$kýE’¿W·³NÚ’³µîï ¹¬ÞèÌŸ‰ÙAºýGÅøüGr^éÛr¾¢žœŽ^É)4U …ÎåŒõU9óá YÝ£4Û;QÏê%ú˜šË Ó4¯üÉ·d=¾1¥õWYûµ–N/Ï€- ’gMoÔø=œxWN’ q„Ížœ÷¶‹$û3û§¯ä<\;I3 2Û&J¢3J2x2YÝ•–œn̦?SÕ²þ0|$ëÅ®bl¼³XË©gm9ò Ûùi©‘“«ògü³TÇìüäó4g†G²®¦™ÛÚè§y Go$™ÛüEÖ ÑÒh‘é’0     €BR¯\t‡–ªUYuÐE ¹¬µ¢µQãÏdiv~ÚYUGV](ÿ¬È ¨ál(§jdíts9k¡qFV¯<—sð¹bôÞ’u²#缿íÏ芜 ‰œ’ÜsUÏ’ì¾+§N5GV^—•Wrº×r†a"ëå4[@þýùi%«´´h2]¦ @@@@PHLš^ÙŠÝiŠÛ2qÏû{ôñø0͆ÏxSÖñ-YƒÝ¨g%&²Ö_$ùý §ÿ’SÕ²®­µäÜùF!gk=“s8läÜÿ÷#Ÿ¶/kpSÎyÿM92]”Lœ\•œ–¿GmÉ™ÄløtF²Î6eU«r¾s-—3ܘÈÙ›=H³75[—3Ø»#뇟iéùåÙ0     €BÒã“fÜ£ˆßùYÕ…É+YuHós7ÓœiŠ4ûE+£4?{>—3îË镳${AÝH9Ùüb8Ú4|B¶i åôg’+:ü»Ç,«Ã8üº>ìr¹kZ6m 3!¤ HHˆÉX˘„E°0; !ÄL ™Q7¸·M¹«Ë¾UeØÁÿG–Õz9<:¾®ºÖ/{ó”`@@°t{šýE’y®SÙËö‰lœZŸM¡m¦©l2k¸g?¥Ù«”fË”f·)M×)†T†]J¯— ‡~8JéÓ†3-£1óôòâpÔpèeJçMi˜¥‡ûÕq}fyšÊx÷Ðö/Vë󺎇tq·H©áóÉóW}ö‚Ž:m“d2Î(O ¦ @@[@7ßM¾“ä0×éa×Е‡Œ;íóô2î²á³mÙŸ™]õ )m'éb8ì2Šòå?®Sùùù$•_ýršÒíCJÇ{ipñÏ–)¤‡”æïR:¹HéöôѾ²<»LeÛ´™sÖpÏ4¥qË¿Æ&¥ñÐç3\÷ÙnZ\6í õ0Ÿî%™M2ÊS€)@@ÀÐê$£³$ûyŸ¶™¶îêÔ[@µ.¿ë>Gõ™Ñ~Ÿ!—éºÏÐlÙg|fqÙçžÝ$•¯þµIå¾KeõÓ‡T~ñé¤Ï€OòvµKÏöR¹iù:ž½IéýIº˜®›^Å6»ï¥tpÛ´óÓÅzѸTßÓÅlÙñ=|6ÝåIÀ€ € ` è89}ä- M¶y,C»låùEŸÍœñ&¥ÑÐk¤¥Ï°ÌÑUJÃa*o¿ý~*\¾Jåëÿ¬¶€¦©¼˜ï¥Á7W»úÌ»úÌÍÍQŸm™Ý¤å«ïsÏA§{vÓ¦W¨‹í´å™û<Ïê$¥Û³”ÖfÄÆC}Íx/ÉdüĶ€0€ € €ØºŸg´HrÐi hÈ,e׫aÓ)ͯú °Œ6iÐçyæïšž¹‹åiŸá”ÿ(•¯Þ^Ôg.ê3çÇë4X )ÝÜ=ôÙ„ûlÂ,.[^û>Ï3ÌZ7…ê3µ>ϼ^ôù»®ÏSzõ³>ßÅÝE—Ïp5,“¬7£<%˜‚@@@l­?Éî8É$XžmzØeœÊ4RÚ~’ÒxÓ4ÒÒÅhè°)ÔqÃçê)-ÏRº9ïó9ÏßuÃyýâeãTTŸÇ~œ.¶“4èóÌ»!¥Ý$ænÑeçgïnžÊ8÷©l.Ügk˜¥tú2¥†WúíêÛ$×ë'¶€)@@ÀÐæ {‡I&¹OÛLS™d›Ç2d–Êþî>•ûåÙãÆlŸ¥´›¦´:îuOŸM¡õâÑ·€ê£ÚâMJË–?­Ï=»Éãm m§)ݶeŸ§ŠïbŸG){A=uÊÞTI{é3ñ])Çæí1‘~qDŠzI¨l“žÓålÕ«œÏS´„òfÄb9ï°lIÑî9=ëá‚‚§’$©`GH’ €$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’*àOøÇ+q9sNˆ4̈ÜåÉÐS§Œ¸-¹š0Ж yxnŸGdxŽaÆ5’¤ŒÓmØ'Æ@Aä¼¾š0v–yfEhú$á®ÄQ¹¡Jù-g¿E¤½¸š¶YÛä|EGè•ÿ$òÆs50k·;B’ä„$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’*žbRɆ%-¡¾"TÔ¤h.röyš¡ÉŒP½"Åâ(ç=· ‘n}H¨X#ç=>ÈÙ :¼Oäd:Êæ¼$Ir B’d$I@’d$I@’d$ÉH’ €$ÉH’ €$É- 9ÇdذO†’ - öøˆ cædh8ß©ÏS³Ú©Ï3aNŒ-%‘ÑPÙBήN½ÌyÎ$i£f¨H1–P»G¨}B¨èí_æ9mΆÏg>$ôì)¡“ÛD¾ò| ülÞìI’S’$ I2’$ I2’$ I2’$ I2’$ IªxŠI#vOÎÐôqÎÖM3#E_Z&ì%¾Ãæ"ç=Ÿü„Ðï—È /ýœÈ´ùÚ‹‡ÀÎGŸ’$§ $I@’d$I@’d$I@’d$Ið?ܪó¿Í Ïñ»|ŠH’ÎßÎßßýî0;ßVÀ ^¿ÂŸ^’¤g¾P<ó€¿û‹àGÿÝ<•$IÐ1iÙãræ“¡¥!Ò3&2¢'ÒS§ÜU$ÝU³"2P)i‰tŒ‰Tlˆl)ÉPæÝ•ò ú´]oMŸälË<{Jè3Ÿù1¡zõñmïôuΦPÑZš\ÚLèw~@äõw‰¼óG nN  mŠ‚!Ir B’d$I@’d$I@’d$I@’d$I@’d$I;l H(X°ó“lK™òy¶¬â3 ÛD ÒßÏp&¾+__ªœí+÷Rv~¾òÙŽÈÉÞ˜ÈÙzKäƒû¿$òmCŠñ2g¿èà¡ÃûÄï¹ rý >ÓÄζÛ‚!Ir B’d$I@’d$I@’d$I@’d$I@’d$I°â°â*—3ç˜ \#ÒÒ©Y¥ìÆôÔ)gâ» dC†š%‘’6åÌŒkdXq˜²á³äjÊw±fŸ ŽˆL†%‘õdFèä'„NnyõzKä­W&D^¿^¥ìÏ|ð‘ïÜyH佃ïºû9B³k„Š–P3#TtDNÏâ÷Ó¹~X:ª¶ìI’S’$ I2’$ I2’$ I2’$ I2’$ Iª¸”|%:ÆD¶)»C=㔟‚!定‘‘1s’¤ìü Igâ» £!ÆšgRÎÐ|H¨l •‘+“‘WOJ"ožT„~>yë¹ ‘¿ùò‘oþÇœÈ?ݺEäìîK„Ú†PÑ‘áöãžH]°Ýä„$©`÷H’ €$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’ €$©>âùÏs9x… ‹zJ¨^Z*ZBÍŒPý„H×6dØ,®©X’aŸ‡Dñ–\MÙù™sL¤§&²aŸ\#tr›ÐáBõŠ)[@7¯U„¾ßú— ¡Õ–H}£$ò·5%R#"ÿÜÜ!rz6Z¥üµ¡³õ–ÈÃÅ@¨ ¢`GH’œ‚$I’$I’$I’$I’$IRÅnËÙù©WIg–„ŠŽÐPšÌRv‡Ú!>Ó1&IÊÎÏ–">C™r¦g äëkB«B_ ôàe"§ç?%R—Äî „þ}Cä_ïDÞ~½"ôZ|æë_œi‡-‘[÷z"ÿû𣔙Öñ™EKl:hF;B’ä„$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’ €$ÉH’*à/Uü—³˜ŒIqø‹œ}ž¢Ëyä܃¡J:S#e3gÍ!6ì“aÎ1–\%“)V‡„¦O­ã眞õDîÍ"/ìkF¤Xl Ý鉼z³$ò—Ÿoˆ|ïZGäƒû%‘³Õ6>³ŽÏ´=)Ïá`°·;[@’$§ $I@’d$I@’d$I@’d$I@’d$I@’TñIWv씾&E_©X©YÑó´êƒ¡'Ô69óMeG†ÅÙ‘ûñŠÈ_m½3yûýŽPMìñ–Ð=‘Wo”ñ™ÏMˆÜ~\“aÑn‰œžD¾sgC¨ £`GH’œ‚$I’$I’$I’$I’$IRÌþìï9ÞFßú6¡ú¡zEh2ËE©›œ}ž²%E½$Ôîå¼ÃuE†žšÈœc"#†”»Z2¬8$OÎW6}Lèà¡¢#´8"Þ9%ò½?yãk5¡õ–Ð÷;Bïw„îÄg*B¯•D>{½ ´&¶"tóJIää'„î ‡ à¿î0®€-ßà­ʧˆ$é½w7ï½ÛGÃøè|[|éÅßüð|I’Þüóñ¯ÿo´pë‡]ÁN“$9!I2’$ I2’$ I2’$ I2’¤ àWìÜÏjWÆñgÎhìÑØŠ_'!I1/) Ié"›.{½„ÞM¯£›nÝuc .]²hu JHRÇX8J%Ùgþô-~Q¾ŸõA3ƒFúîžf¦ÉBÒ°½ˆ<©–1;?e«ÿ¤j³qt5Ñ&”äô*å\k'dç§ÕxÍï"æX=—U”ÕBv~ôþ¡œ“—oäüðÇ•œ§_5²Ž Y§œßÏ9OŽ’¬¦õ ɺ+¯d-†M¡§G²Ž[Iz7$msÐ    lÝz§ýB’öÞ„íül”2‡m…Èã˜AO½’œA¥œB½"tªädcv~R–•Z­cû£¬ÃÓ˜ŸF®e]żfS9§/eÕ…¬^ÖåBÖ/³AΓ½AÖõ³t˜d%ÅlÍbö‚4ë%éCŸ´ALA€€€€°Tou©–¤ÅÎL!RÖÙž+ÄÖ*æ¹òXÖj"kq §Ô9é7|:mɹ֎?£FVÊ1Ã;ÍLë¸}óQy¼ÞWïÅ<þÕDNîY•B6sö—r¾û4ɺ—d}>’u”ä<ŸwrΗ½œÜÉzt§ô×ò÷£G{…$íIÀ€€€€€[@‡M±½[H:mf²ª¥¬3B¢n$«¯Öúœ 1×ʵœR×a>®åeÕr–Ú—Ój,«¾U­d5’b^éËû1¯ýj"«žÇ<š]µÈ²Îó çðÿIÖ7[1»Cw“,¿;¤éDÖ³ç×![@—«AÎñ«ò9ßÙHR«¤ ` @@@@@€- On•·ö I§{otcú‘¬4 W¹a}òì½ÊŸ¬ZΕvB®Õn·²&ïd5³˜1œÔj=1¯Ù?û²òXV™v~â‘9/Î;9_VÊ:H²ö Y÷’œ“‹Vο]Éùùe–óöc/ç|Ñ}_²¾ýb[Ò‹¶OÚ ¦ @@@@Øú_]4I’ª¥¬ÕD!ºQÌ®NÊ1gªUÌ~Qµ ÙºR+§•BΨ9“u0•µÎ–ÔÎLVµTˆùY¯k‹}Y—÷cöyê¹B”­<…ìØO³œæÓ-9ÕYÓœ“_W!>'¯[9ùìaÌßæüvÌ_b®å<ûó'Io_õIÀ€€€€€[@»[Å^]¬»‡“Ç °ÞµÊ¹¬js&µº1W“˜ñ²•µ{&ëö_²ö§Q#61Ï~yOÖÅQضUÇœIYV™cn»…Üóô²ÙÕy~ÖÉÉý çtÖùkýÝÅlø¼÷gty?èý©c~òÕÒI…¤Q*’6€)¶€FIUòcëSjeÕA;?õTV3“U­bî§ù—»Gi â8üw$W‰hSxà5<€'°±±ó–)­,´TüH,ÔDQaãª$obr‚™b«ø{˜~`øãè•–<l²y 2#ÓÒ…=‚Ý+M 2½­ÊÔš“©Q‘éiS–éfQA9YÆeú«;+ËoËò3Ù¬Yâõ†,Õö£2ñ3%ÓDzLŸ‹Ù¬YRÎèmW[¦|K¦ø5“÷_k¥HR÷" +7 €#ðÉQ;_Qßí±úÊ•A @ýePÒÁó·¤4éå$m팕×#I‡ûÛJ€•…AI»{§’®C¤À @P?ëÊ µ†œp䃎ªrÂÝ»ü¸úÚpä„ZSN¸ òAðîÝ€¿vΞ¥a Œãפi›Ð„B­ƒ‹ƒ ¸(nŠÄ ]Ýt¿€«¸;ø9¤«]´(Šƒø’6Mš6/w¦  Eú?ô6ï·æÇó’ô¹BýdRgcM=;)¥ÂÖe@Ö€BGS‰êE:ó•ÃÁu4¬ª¡ìoŽ?ö|ç$йxM„ô¼\Ërm ‚ï;ãè9þ*í ‡… §iÆœuðõ<-qî)„É÷±Ó›ûý^.Ís²¾úá6T;X©YÐY™Írü„ðˆUò AoD¡sºnîjx™ÂY :eDUX½¤pŒªŠ³ŒÌôLiôÇuZ7ÿî+ ‰D"‘È—À‰D"·ƒö|ÖjÇ©`w(A¸C†‹*DšAqV1Zäè—!ŽÎ|—ݶ“©îù¡G› éY·èŒÊ8ˈiË ¿Úä7nT›Äã&®sS:àfúSÎeü‹÷‡fþhçÞR²8SSñj¶Û5{¿Ë ESÜÄ5n^ïàúÍhn^#¦.ƒåSx2vÃL€çS¡PÏ‚q¹ l®ª€Éɺ<ütnU¡ˆ¶ªGmÍÍÏМåÆ]¡ÄÁ5MUäç"Ëp.54Öã2T©éóqpà;®¸X7)Ÿ¨>7žÇN¹q|nÒ)nL‹› ç\hÁüЄ_@;£))Í9Ô>7î>7ø¥¹i—¸‰nŒÁd8ƒãÕ+#°·«p"“$I’NN’$I’üH’$IêïÖ×RKµ[Ž¥” îYJÉÃ][*…7l¥´°D–GïÛ)|›-–ýb­ø’27QÈMã%õF.ÑØ€ª VÝw¨Ùje µOs“nÌ!7Õ&%ýN…š(š&ÿ½qí")d ìá ˜§€ìo nÔ±¹ëƃƒ ´$(æöñ›V—P!éÔÂäœú™«@bõ=ö¿F9¼NµíW¹™G’ÒHýaçn~$»Ï*ŽŸº/U·ª«ÜÓíîMÇ“x"HPlA$Á.›l6°+ü °@BB!V°@HÙ°g‘ƒd#aÌ(NÂäelO3Ýín÷TOÕ­ºo(›Yx¤>¥ñ£N1|?šºuëö-}–ŽÖ€ÿ     €LR¥a«g3i”‡œÑèTV1•Õf²Î÷eýàu9ï4ß”µ}_!ʉBäó˜ç<¸S¯rÏm!cå÷0H¢VN£þJ;?NÔ6Q¯º&§« YI-+­ƒ~ªy̆Ïé¬ÁTνћ²vZ9ç‹.f²l¶}?ùl‘hM˜‚@&©QÖ=5oÒ)‘Ó¨rFƒ‹˜›&“uòrÌXÇt/f h0•u±+kº³)T "/åÔ‹IÌûã©§VžRUW¶)ÔS#¯’“«”³,'²Y£³?½*ŽÒþLÈ~Ñañ¶œªQÌO¬*B6Ž49Ö%ªi¢u` @@@@@€- ü©- TN£j_”³˜îÊ*¦aSQÖ*÷s~CNžôäl&rÞ¹þY?xý“~÷ºI´&LA€€€€ “4У\g;0Ðã}•TË€¡’€‘–`}å!Û;¹æ!Ïp©ëTm!§S*§i‡²’JVZ‡|V§D–ÿjñüOcÍ„ü {톜NžÒ*h hOÖl[FyOÖèL!Ît‰GóDk À€€€€2IúÛDO ôF¡7ô<|ï\ÿ}®'M&i¬ßÍõ=ÇŸÝüñ¿'Žç™¤BÓ>zj$¨ŒÚð ¹N¦2dW'Q+§U"§ÒPN®BN©‰"ôža²q´”×´ýÀï~e—ê]ákß*•Ó(y][%òò3¬Wy…š\V>—ULe5¹¬Ó9ççr‹™¬j¤§ŸÒ%'Z¦ @@@@IÊUöõ8tÌ$~S¨Ð#9}=W锆l eZ®t?Úï9B­~Ì÷ÊçŠÐµÃ¨çÜ(x=ÖÒ•}¯DmÈOu¹Ø‹ÙùÉKYU!ëäeY'7ý‘ÉÛòóÝÏ÷u‰²K´&LA€€€€ “ô^ñŠ’Û;ð¹Ù¿*B¡Gа£{Š0×5­“J…œN‰œÇÚ‘Ó(ÃIb†eòyЋ¿Îb¶¸™ðÍOÃNÅkÔù^tCNOMÈ™®*d .bvuª¡¬ÃÏÉ™íÜ“•Ö1÷|šéMšhM˜‚@¦Ÿ(ôÔÊIU}V6c%UÌÎÏ`*+©e5¹<Õ‹BN¢&äOVi(§ š]jý-´!§V!kt*§«†rzU_Öl;æ5K«˜WúøeYçû²Fg¯½ÛÊ‰Ö €)¶€êzŸæÈU*@ØuR-ƒ®S­ÕýäšË锄ÜO«"hX&‰`É粊éJ× ÒT7ü™¶²«Ó(—S«òzT*äÔƒZÖèCY×ÈZrº³YåDVUÄLN¥µ¬Ù–¬“›²vîÅìºÄ`–hM˜‚@¦Ÿ($jåôÔj4êË*Îd ‚v~rym.)d7fy±§’G]©Ó?”µWÖÞwc6j¦{²YeЙñq̳Ôf1s[“#]b±>[@¦ @@@@IútýïC=üØV©œLK9r9©*9REè© ù¬D­œ6¨¯‰šA˜ZØa™ÁLÖæƒ˜3y)«Í‡\B>n9Û–Ó[åQ'­¬â\Öö}Y?ý¶¬O¿©oÉ:¿µ)sfróš%UÌk?>ú¤s[Ë*Ñš0     €LŸ@«t¥3Ï£VIÀwwÛDžß&Šÿ›jr³ó³7f¥*d}´¯(M¦]5”•Ïe .dSYƒ 3RÌu6c¶€Î^¡ÀïU\„ìüìl?–S5—i]¢5` @@@@@™¤JÃDϰ “j)'Q#'U%§Ô аÔFÔuBvuj!ŸÕ×c9s]“³H†²&Dz¶ï˺qw¥ë„8=Ð*Þ’uø9YÇ/ËZL"/e%µ¬Ù–¬“›1{8ió U…<Åü-ÚüÊ6—F“ 97·29yªK\Ìÿ¿n2I'úÇD£'ÆúÒD¯é98~³=~«{r žu™¤ýæ@ŸÒó °ûz²ûºž¸øa—èÿS   ÀP冀™N‰œTyÀÐúi•„ìütþŒjõå,´!§RòYJkYI3P³y(çöÍ#æÅTÎùâDž¾uøoþRï¼!뻿3OÔæº2Ó½˜×cóAÌë19R€È©¨˜çÓŸËå=9›ƒÎ—9µ‰Ö€9hd’š4ºö Û2™–º*Sí*ÂB/(B鯣FyÈ>Ï\×B¾W­BVR+D>ùêgs9ô¥ájã*Þ[Ôrþxò/rÞ•§ý’¬‹]YÕP!ÚLÖrs?I-k0Ùj³˜{®Š˜çãiVurªV†{íçY/Ñš0     €LR«¤Uú ;6R9™J9¹J­“ViÀÃ>çøÏJµôgršéž¬Ù–"Ü~1“sp"ï´‘§¯~:“sçV_Ο¾)«ÉdÈšî­ôY!Ò*`3'ÐèLÖøHÖæVÌßb1–UNä̪ ½ F—¨[%Z¦ @@@@é詹²í¥6¡R¡Kä)ä{ÕA÷œªŠù›.&1[@þ:šUKY¹Â”œW÷R9¯½TÊyKwdÄœisYåX!ª¡¬r²^›BÅ4fwHŠùiLwåœÌ>”³3ê]ràbÙ%ZK¦ @@@@€€1¸D­œLK9¹J9‰=×üžhógÖë9϶e½ÿy9_ÿö9¯^Oå|å ¹V‘ʺQ'þã^Êåäi%çÝÍïÉ9Ùù¾B´YÌÙÃ[²N^–õx[!òRÖäXVZËj²˜!¼³9÷ü=k³è]öÇœ­Ï€)d’:¥ÝS(­’¨­›3¥^“¨ ù¬FýëÔ*tU:¥r*rê$“•Ô²ÊqÈhÌwß–ó/Î䌲ž<½¶“ÉùÊÏär^Hý™ÍDÎûµœ{gœÍAONž6rÎÇþžó2f3§É"ŸËÉÉZLb¶€Ž_¹ç“Í^r`Y®Ï€)d’Zeòè©‘÷8`Ÿ'N«ôê6süU^ÙÎO£_é:!.v™‚xv È$•úËžÆOäúµ\¿®ç ýÏÿ{¢+•I*ô‡©^Ñó üüÿ=Ñ=P¢ç€)d’Õ©ª§þC)'U¥8~'B¥BNíϨS²óSk(§Ô$ä:©–rr•а\Ld- Ym.§j:9‡­Vq+—u£“S{r¾õ_ 9ï~ØÈ¹wÔÙÌi3ÿY[‡rªE!k¶-k9”U e%•¬ªµù@Îkgþ2ƒÃ×õÝ“FΫ×ÓËþe—hM˜‚@&)UõôJL¢FN«TN·ÒN_žB®³ÔFÈuå!ϧRr…l7eZ®ò|B¾W'OM&çdÖÉy稖§ôäT­¬ûçœoü°’sç~-ëý/ÆlUCäø,fWg1‘5Ý Ù/ÒøHÖ§¾)ç·¿Êù“_ʹ=NåÌRY÷v)ç¼¼ì§ñAÒ&Z¦ @@@@Ép;?N£~ÈÖÍBF![@MÀÖM¤T•œ\s9‰Zy ÚùIå)f æbKÎ[LåépÚÉ9™µrî5þÌ›²Þÿ¼¬‡·b¶€ã¨?YÈÄ“Ú\ÖèTÖÎ÷å¼úÒ#9¿ó‹c9·ßie}½”3÷äüÞrÎ÷.»ÎÛihM˜‚@&©V¿Sñ û<‰šŸZÅ•íütùRV>—Õôd-&rÆ:’“j)§¯ÇŠPk¨+Óæ1#6_‘s'¹«U¤µ¬é Y§²Ž^‘u|SÖù YÕ0æQ·YÌgåsYƒ©¬ñ‘¬ÍrnŒ™AOÖûœ¿úëRÎ/oõäüÂ×úr6²ËÞ —hM˜‚@&i®Íž^|j¦’Ó)•S©Ó?”5y/f`DŠ3™îÊé-F!;?‰Za¯TËÏê©‘Ó5™¬Ù–¬‡·dïkM&ëbOÖGû1>Ó=Y»²š<`çLjÞùÙ8‹9ãïG'óVηþ§–ó깜ßÿ³‘¬QOÖÍTÖi§Kœw‰Ö€)ô$ië/”æYF’ZÖöý˜3ù\V9‰Ù„ñgÔ;¿.gCʹ®oËi”Ë豜…6ä,ý͵%gª]9õæYÌ»1>R€È(ͶcfŽ“ù¦V©œNiÌ£.¦²6È*.díÜ“uý»²nÜõGÞ“ó[¯ä|õ³}9yÒ“s2kå|m3»lÝè:Ñš0     €L’&Ç*F ·ú0HRÇìüœÈ:¹)'[dr6t(g¨ätJFZâ$jBÆgr•rêé®B<Þ’Õfa[@åäÊ6|zš+B§¾¬Ñ©¬ÑYÌlWZËj²Ÿ³Ndýè‹rïËùóÿ¸ëÏìû3ùøLÎndÿËÎÝìFr–a~\ÝåiÛãÌ˜Ì ¢‘‚Љ-Bˆ q  †ó€3`ÅX€‚„!„ !BB~÷xÚî®6¬X$’_Ky±œp]òòSUu«Z÷ÊOJ¯^ö>¿¾œ†Ü˜‚@@@ØI’/ý(»¯äcNÒb\¥ÁÕžçÉ—S¹wSYäiËÎÏnž¥r'o§r–ƒt8¯¯“ç¹Ûò<›ì¥²ÊaË3OÃNºlǦ Ÿ)•!ÛT¶Òa3Ì{f»n-Sš­Ób{&ž†MÏ~ÑÁqÏVÒíwSºÿ8¥G?Oåûßúà’½±òY€)@`ž$Ç?ɰŸÿÚÿN^Íçoþjzó×ÛÿXŸ^Ì“äèÙ}%Ÿ_¼üêì?þ€ì$ÉâǾžYï¥4ͯm\ežU*·ò¬kçe/h–u˽ÎÛ¶€ö[îµÉn}&‹–{­³¸Ò™&™å&Ùfv}ŸkX§ÃNÓÀÌ)µôLE-Nzv~n¤tø^J_üKJ¯¼–úÌor‰·2äÀ€ € `z\͆Ì?±csšÊ©eçgÌê*›9-Û;û9n¸Nñ¹Jýßá”ÝTv²mùì›ì6ÝÔŸ«º©ïÕ§å‘f9o:³n9s±Z^¡yÃO£ó5;Û¦tz;•i\¥–žÝ¡iLiu˜Ò;r‰WCnLA € € ¶€YÎóÑ'F?¦–›yV-×YäéµmÝìdj¹×E†T¦Œ-Ã2SÆ–3g9HåyîÖgr”Êù°›ÒþqJã*¥õ"W±§´ÞKeg½{•y«–3cV-¯ý”±á§Ñé…t8K“aÓó*Î6=¯ëòÁ§½×òdÈ €)@@ÀÐí¼s+{õÀÈÕõ_'³œ§AÛ†ÏYSÙf–ç9¸¶ Ÿe¤2Ýy?¥ÛMiq’ë½”Nä*–÷SÙ]oSÙËÛ©,rÒ²4djøù¯Ù u‘YË~Ñ”ÝT–§G=[Rãó”ÎSZÕg2^úÌ«!7¦ @@æÅÀH“z¬ãsªeŸçYîÕgòb*çwž¤ôÒ/Sº÷8¥ÙºgÌäøaJOŽRÙ9þJjÙËG©äý–Ÿ1Ï[¦´6ÙmØ j˜Òj6d›Ú¶eçgJƒÎéªUÓÐỦ ø @@°4Ëf–õ'GQÒa–ót˜šONs”ïåk©\d–Ê:‹T–ãaJ/ý)¥—ÿÒ½×SW)-ï§ôáÔÞúF*{Ë1•;ùsjYäi}&'-¯ý˜UË+½Í,•)cÃó÷j¶ÈÓ–çy–ÓbLmؤÅvžÒÝ·Ryø½Ÿ^6ö‹!7¦ @@æÿëÑ¡¾Nv2åÿÕòh•Ò£Ÿ¥tÿqJûÇ)M󞟿3¥7¾Ê³'©æÍTòAj™g•Ê,ë† ŸâL¡Øù)¦´ŠYªB×÷Ó¤å^;™®oÃgj:óð·©¼öÃ;—øãï7CnLA € € ¶€¦Ì?9'r–Ãt8Ï6×eÊØræ$R9ÎÃT.þ.¥G¿HéÞë)-NR:ýBJï5¥¿}7•[ÿ¬¯ób§òBþ•Ênž¥²ÈÓÔr‘Y*³œ§Ð6¥µÉ"•uöR9Ï~×ÎOË÷3fÕòS2µl]dÖ3¥5Û¤ÅvLéì0•{û—M`Ý] CnLA € € ü›»Wi ˆ¢8~2®Ycl"j)¤ô´ó¬l}<[b AËl,Óhµk4~à~D+SÞ ›õÿcË]förŠ“Hš+ü¬Â¨Ô•—]Ö¯Ñг8%¾ç'O·dÚ;—ix%ÓæD¦PÉô:)ß•éîP–ûmYv”ÉÒÓ£,©^dé¨öËø»€â5 KyW©5gÏOüøÕk"ú‚"tÔx¿'þÜËžLM)§øj¯:‘e2ýmfM"©ÐiXüCêk¿¯µàâìc°ÿ*H“üÕÎ7P D–(‘ÉÆ Wl àj¤¶E2‡TD$_"õ Ò«BIœQÃ}N±˜CI<236YÌ bæ=ù5 ï8þéÀ ŲÞe(‰€b8”%QŽÌà5ùë4t ‰R\ ‘¿¤Ó¥ï†Í:2¢Ú¥7ƒÄàRÆRkÂIEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/mapserver.png000066400000000000000000002752401510711153200254130ustar00rootroot00000000000000‰PNG  IHDRÑ åp>zgIDATx^ìÝmk?@ñû iÆIvCùÓïÿK)…¤±gF÷îzHj\3vÛ$8çB’“@^Äh,[ÐwþI¬þÑ äGWý„r~´ˆÖ?Í ªó#D´Ê%ÝØy±ãÖwÏJXÿÁœï!¦õZ·ÖóG€Ü˜/Ö¯Òú½Ϻ±}¾>É·i}ã€Ö—ALo ž·ÖùV!]Þ: ·Çg •Uª|Д•¥È—ãú|\†²žÍ'§ý¦oж憳¤Ê§T‰8ÿ»¼Ú˜€êù‰²YÊ7MÍç Žõg—#^ûDZ_3 7†Åó}·õóÌuÜg¹Ó¼ú€€®!ýÇùeˆ¤|÷ø%¦7‚úuBºÈßòû€¶ç€V‘n?ã9ÂîòÖ2S3ÃâVDrù( @Dë.ok‹ãþACîEO1ýYD¾÷±Ž —v¼÷“hýÝ#çó?&ŸÂ$ÂNñ–»X#Zr·†ôÏg¢sP¹NÐCÊJSÕBô)U5õÉâ¸_cÚlòÍBäëéDúlÞ~-Þ{Œh•sºñ ´tï.v{ è]÷Œq iÉf9¤J¦ Ùô#\.€æA§ÕЃ¦è´´Ú>ôÉûãKH»÷_B:6ž‘N9—ïïqŽí÷>_ôýâÒÓîâÆ3Ãb\\b´Œâ9„ áž%U²Zäq¾úˆ€j6Ý¥èš+]"CÏq¯·û›®aú ?DîCåû?"òUžÅ/oåÐp~7­[ëóq:~ èˆî9žÇ¹¹¯ñìjaniî5Ü¥¥fº]wHЪ=d֜ͺ–Þ›†iµ8X˜N­ÇàªûC¿“y°"Ÿ"åÛÏÎó=í/Öïñ$zûú³Ê§Å¤w? èìžµ–Ö‹GuO«¥÷ô¡x„eq“ô«¿\À¥Â¾†tZ õÃ2/ÖµÄÒæÔ©ŠÊ<‹ŒƒÄþ kHË1¤EäÛçùbϧÑú·/–׸Xxzt¹Dxº@Ï]¼d¸—±Õ´VÍÔ3Ìuª˜™[)âr­ÐEz,‹–›–YëèųkL¶dÎÍR'YCz™{M‰x~ ^Ïç·vl|ùÊŸéòzï…•ûn/—c\þ _÷e«>*fÒ™‚ ¤zzlGØYᘎe£h;’‹ÅŒsRfAô™|9h:ÂÀ÷è˜lÛJuºò­8÷ÂðVÞÙ”)lÇD&kŠ”é­Âa¦s3•É™\6—««Íæëê2ùºÚêTñ‰›ß=«§»;Õ^( R-ý†J(ùe¸/}>• Ø©| r]å%¨Êðú(÷iøù \òw ðh%`Úö¨+|%>²î¹ÜXE¤ÍtÊ%ii#—ɤ3™lªº*ŸvÃ82Õ5µµ­ù‚èÁ¡¡D]Áfû7œ8ð„–«xòºôe“«zB„Ök¨Ç•¥p¢ûFvìK-²]$ôêŒ Þµ¾õ6Zß!‰Šæùb’ÉŽû&¨ ƒ” o‚Tž·Æ“à‚ìJð!û@çj¿Ab׉@hâvä'üÞ p"±À?Wõ©äW Ш@Û ÐdÇCYù×q £mô¾©/Ôæ(ß c¶[DÀ XŠ×.!’R’þÞØ½ßªë ÐÃIУß. ¢·§'U»õ¿çK”<(¹Pò¡äDÉ‹’%?JŽD–ØR¨w¸®XçÌJ³2µªK”öY#“v\€6Ma¦ÓN&—6!—ëp!Z‚t¶ª*]]S#C8† $N©ÌnPÛ¶Q,aÜæJp”çòõä=úð­WÍ VXž˜x>‡ÝdÊi î nË”ne]zàKÖ® ñ÷2 '‡ò‘Ê>Ùé¿´9<0Dd§†XÖ/3F¸¤¹Ú%D(H Ò%(¦vórÔ†R9 É–ß\½óí•¶¯“ú~”ìX?M]BÕ6-åùê¶ê>#x'èf-¢h¦G´ÐÙ¯*Zš–^…æåy¤·Q·/¯É¡7´É7õ•©ÐpšLá¯ó_Ôмú6„‹ÑÀŸ\…®Èc·²Ml3ªÜ4è‚tµaÕI”<èsa&k"ëñ¢äFÉ’#%OBÀçËjáó&ˆ?+½k޶9‘¥Ý#åŽ^Ê0Lá†+E›™¬\³#cf³¹¸À™©›ŒIsδ“®ÁÔ“cêñW`÷g"íæQŠîà8Ž7èãÆÃä½öB}}½÷ÿñîßÉ“'£®¶V±(í*¶ jÃ~>Òäq»r’ˆn‹ºõ¦±7¡,.ÔGž Ý,á¡(»K¦ñ‹Ž’\&‚Ø o*§†X²IÒ\MM ÒÔOÒãclËam3žÉ–êQƒ\ÖW²+_µQ¶Gp;?u{èº&(&;:ø=@àJþÔ?ãs€¦±Lм¯‚ô ãØä*t¸ÂÎC-ÊW¡Ù= ÙÒ %\…Žsžü3]÷¡ÈIP¿POù¿Ìî–PÉhÝéaØ‹øõî!Ï=þ°¬„üÁÙF²Lýøñ>ÛobÊ‘Üw}ý.q“nÉ’%J>”œ(yQr#]Wúм‡S£žÊWl«>ÕÓf"›M» [ c˜ÏáÀÁ~Gœ…¾–w`5¿šè"³Ýc¯I“$8ÈÁäàžËyùmmmH§Ó0M“¹þ¢#»Š'jGTž^í!¸Ñõ#CihcUõ±„F˜/ ‰Û2å%b Aùâ ¯‹ÆÚ÷K`H[xäŸìT¾II’¶t.óBÛà÷‹|œÆ¸o€—£¶ªbéÿ:&9Ð?%˜sxUÅÅ3à6È+]ü¤þúãýúäPÎËécsÕ¿D©dQP¤H8°:Ž˜Ãipœ‚J>‰úûƒî?‚gj§ žÙÃY Ü®Å@¯Q²K ÐIáœ+À4NÜ·Ÿ— ¹ § á¡q|,©Þø*4Ý3Q)º]É] …f‰ ÉTèè¼düaÛ˜4a‚ž?²YLrù£½½$DÅCäÚv\¶!ßà&ò]¡÷r ¢£Sÿ¤#àTMŸlŒ´‰ÞþA@6[ (RpRYtç@q*P·•@:LÑ-X–h¦Ý@Ú¶â1a¤s¹\"µW$ Þ'0¢<²,¯LpgþBÛ¢Ä ÄσÐHöêäÛ1{7‡t°€t^Êçöt®é•i_¥o€yãMvA¥þéVK ÿá“ yøƒî²ašO<Œ ÔT/ÕÅà4ò—þ Á•bÊOË<ñº («ÁÅß>¯/½ïP>›(¦St©¤L3àäàlÖÎÂ%‹ÄŸÄê^ÞQ.<ó>—¯>Ó¹~3h‚î艄ÆT¦rͬbM&TV+šÛ³ë‹ßOå„{éî-‚buHWøü„8 GE…–V…B“&NŒÅãÇ×ó¯Ç÷€mvìØQò­õϹ)¹ïk2ñ1N5“gà°ãÎA*[…®þlkïAó–vÔ×dÑ0yrU9ÔTç1a| >uÐ ì;óÔLœ+©ªª ÿ·m ÒÚCæË”Íåd9äXæv• Úá7qÒØhnËÁ–Aûå ÌÃ:FØ ¾8EH—R¨¥W¡¸}ø—)W•"” ¨Ë1ß‚OøbÂÉ‘a4VAÀR‡ Ài¸éÎk0+*gäÑpÜ×ðí¥wáþûW¢éÖEXxpiÕ³pý}+q¿{Ü·óðÊsè«Ú§^~3šV6â¶ËOÁþUÔ‚ óû‡+nAÓݸõò“ÑPEp7U,Äwï¸Ò«O(ÃLLÔ2—ý`)VÞ³Ën»g>ž>4s3pÊe?@ãŠÆ-‹N’uعy'/úG,»ë/oF)€!Ë çÝü­½<$?„ÚƒNÄÅ7܂ƻ—cåòâ;}“Ó^%¨«Ë–úÍ€ß!8†"iBDxbÀ4LzTÁŒÁûžü2ðb`C€ÇÀ[aKp¥ÁfJ:+öAqö*ÿ*À%¸e´à«qìi€fác \Úòu"C+r¹˜Ë_‘cw)§’#$X–ÁJš|«ÙF2Ê·´—å¢úƸé ¾†3Î8=àó”SN¢E—2ߣ”Æ ºáè³ñ©CDM¾ƒ¦ÕãÏ÷^ŠÛ.= ן ½é,|ïëÿ€Å_=ß¹x>~¸èt\øÍEˆJ–eIˆöÔhÛ¶#i—ÏçéBK¶{*6:A¯Sö\½ A!‡D6"@ZT¤P†â ÍØ5ú'µ6êÊñ&ÿè©}jõZמ]ÓŽ:×^¿3jR2ìÁ„IýxõÁ%¸nñwÑø"pâ…_Àô´úßŲ˯ÂeÞ±ßd 6þñ%|88r3˜qÚ×qbáy,½©ÏOÀ7ì‹ôÈøg‘Æô…áÄ¡ç\»Æ³Ö ¸dá~ÈŒ€OYì=ï\³x>¦×˜AÈ4à¬+Î@CZ@dpÆG£óé;pýâñã§:0ï¼/âÀ¬_× ·®vÖu<.^à×d0}Á…8¡ðþÉÍ{f8/íÕŸÁ~”ç—›¿/2B‰ êÀ3pÝ7g£ëw`Éõ×㺛ïǯÞÞ€npD ÕšÔQþÄÀ–A6ù 2pzWáþÛÀª^A¦ñÀ™)ý Þùu˜ž b¥?fW:ûàå\£”Ì àR[ØçAR€6Ô*x8@hІ “/i‡2–´£sJ• ãH®Bû0JüQWW˘ƒ^çü-r¶™={VÞÝ„£Žú¬’m$Häû™çžÃ±Çƒ3]–ç .˜§ŸþeüvWV¡ƒèqûŒššZöéO¢uk¼áLÜöØûxø¥f<òû ¸åÿ¬ÅASr¨Îp,ï©gÆA‡ÅŠë1ü¿tð§5:l[‚‹ÿajôèL2,Çè‚™ä ±g@šà. Hƒ’¡V±tñ¢T>Ø>>aJ=†OÜJ¢J3gª4·ãªW4L@z Ž:¾›Ÿ}ƒe…ÓÕO?…?¬Ý†žÞlúëûè4ªñ :°- E÷ãÂù'táÉ'× G'ô‰Ì4Ì;ÂÆ«Ï¿Mm›ðúó†=k¦e‚u‘Ý›hiß„7¤ÝGbj:8޲½óŽk@K©½0 û´‡Ð=”B]M¢P€cf zw ßp2S1÷pY×[ØÜÑâ×uøLÉ¢”÷Ü[huóÞôód;¼ºΣrse¹DKÜ ³sÏš‡¶'À/ÞhF[ß úÛšñîêt†Ç37.iÂÊ•wbé÷/ÁIûçýñËLÇiWÞ„/[î)ì+oýȘãÃÙ×.Áò{–áGמ‰O×›ðRö|éÆ%h¼kîqíïúÑ58ÿœó±xI#î¹÷n,[r%Îød<ëüáX|ÇU8¼J@äÀµWcv~’çË®Á¬*áù<÷;?Âwû¿@¬øñb\àú¼î‡Ë¼ó¦^%}bÜœÁ9ááG…Ñð,„ž nÙ½ÇìbªÏ<ÆžÊAgGPÌÛÁïmoöްɤqšBÖö@‹]è8sjÂC +½.´ä‰œ{î—pã ×#•Jí|íÒKÿ'.ºè 8Ž^…¦‰zŒmæÌƒË/_„G}çŸ÷ýwG3¶‘çI¹iûÖm¸³i9>챸á†kqúi ±ìÎåhiÙÌ|)Ñ£¸q‘í1c¿iøò‚cñö‡ÝhïèGÁ=†ÐÙ3„WÖì@uV^|¾½ˆ0Aq¡*ˆ–OUt1ÓáÛûe*Ò¨,Hó0)H QHs°,¤5ÏÁ] ÆÔ%äò8F\± –*-¸*­ŠéSô^•Ö‘  ÓB¨ašÇ)“‚gm¯—á¡—×c@aà”fb׌Ó.˜‡õO<ƒuC÷58ôôã`ýæ9¬íµyܨY‹©ù>´t!sŠí-èÍOŸTšªóì6wXž‹ìýÙÞF<ü‡ AõJXëñïw?f €µ¿}|->qÞ·±äöÛqë×ÁÚÇ_ÂfKÀ0‡ëê”mìŽÍ^]u¦ÌÚyŽtSÊKQev–›"Ë%[â.½fNìÀê÷º` P®Â<µôF\½ø¸÷õzÌ?ÿó.ÈHÇÌñ«Ý€«®þ_¸¡ñYl´j1û+çaÖögÐxs~µ}¾öÕYaÖa¿©xü–ëpå5ßò— Ì;vþü“Ûpíâï¢é÷&ŽûòñØ;%vÒ’A³…Iy©Àp}Nø41ï×çÿ&ŸÇõ쓉ÎñTgÙé¤ëá™ìʸטú¬‡g&<ðû^ÄQŸ ©>Þní„R¨[ñ&–Ђ­ØS&@  Gecº×‰?þõáC¾:ë®[ì±Ç" §Oß?ýÙCþ¾½ }; àûè£ÂUW\†eËšðÒË¿ÇOø).8ÿ<|ÎiòÍÌ·ÜÛcýú xý7pاêU«ñѺf¹tã¦Qš@86±Ðv¤úëÀ6>yè'Ðå‚óö®mÇã„Îþú EÎN ):@&›ƒU ¥sÇÕ`YOþâiyð¶¤R08 F­Ö‘x‚_$†GO4ä>…ÐÁ0S2™?•Ϩ;t“ ÉÞ/?Â^èÁ˜|!R¯HÓ9V|Â!ù×.'ü~¡Ô÷̮ū ‡Q/9E>ƒýçŠ •¦:tKûñ UlGJòð »P€H³M öóà´oÚïÃ#«:P%sü,üD3~ùó6íl¼U_ö”œàØr_Âñ7³šNKJ®Y9§ŠÖG—à®Wú°ß‰ßÀ¥ æbüÚWÐÎüj6¹> ¢Ö'Õvqã7q0dz÷^Û„ÕT0`R}bK¥Ñ_1„®Î~úñÑŸþˆ-'ÏŤ4Ð Ùw ½=( Á;;³ÚðâÒ×°®ÃÁ†_ÿÃ,L˾…nÈTD_ï ƒƒX÷ÚŸÐrêgе­}@óëÿ‰ÖùŸÅÄ4ТWϹʉ¢ß†þ~¬{Õ÷Ù¹­½}ý¯ ûL l²´+Ph:A*op2mP-Uƒs0ô„¯èÁaž|RÂ~Uâ[æGMLävòPÇ>óU@8DrÑ!yøõAÐÜ>¶Eô$ÂòC8¢ZÏå4O\…ðGo_î¼ó.\}õXÞ´ ]Ý]hjZ޶6iSâÖ6ÞÎ6“'ï…ÆÆ&¬]»=½½X½úüä'¢¡azÀ7yC"núÂüSqÌç>‡+ïÁy_ùŠ«F/Àc=Á¸i”Ò˜]°lŠ0Zº-Ìœ^‡Öž¬ÝÚ…µ[ºÐÒÙÃþ‚Õ€!ÛèÒ T¯#!Ý]]ì5™ä†0û°Ú­Š4Ÿ!Ou•«H'‰‘VÕTf9ìñe±•"Í>±'Zeæöz5'‰*-D¸*œ DÜS—µ!€N5æÊtX •U ßÄ„¯íË”Ÿà ªºd›ž†.ù2¦üñA<¶ª–C°a`â‘Ç vÍ+øp@ó“¼Ý‹m5Øwœéå™õû v`+zìãPìñíêS^¹”g· =EQ‚ë0àêZvoÌÞ§¯­ÚŠB¡Û½S>‰)Y@Ø~]ûÔ§¼2©ú½ýºl@8½;ó„÷°ÏÎ<‡Ëù}¡rEoÌèøÿãÅ—K€†(Ú°®k">ÝPÍ&MúGŒkE3ƒ4‚HöY ‘öø¦:†ú]_)Ã6Na¶™†iNÀÖFÁΠ:kDN´µ>Y}¨½ÆÂ Tœ½ƒû ªÎÐOH ª§\!å¶zåY¯nºÐ@Yåg&=´³Ð¯PõYˆ¸êspU¢èuâ¹ZeÏ?Ï8@S[XßsøØ‘ýžè‘ ±zÍü144$WßðÁ¹³ÃûÛÚÚ*™CÅ*€Ö²Ío~ó"þêtWw·wÞë‚ôªÕ«ÝxæT¾qÓ)'Ÿ„3¿øE,»³ o¿µÊ í8þ¸ãpæ™_Tú…4¦Dwmü+òUYL<iÓÄïþ»÷^q$žs‹'ÍžŠ¢C¿$¶w¸p½æD%®ÑÙÙé-S°íXö¨Îç)£ÂŠ´L¢ü¥ï¸O!â®!Íò „®= .½|ݦ ÀD@ÅN¬JûõEL>2Ôª4ÕÏP‚L¦ø•i¨•iõ‚ÿzÅ9hKõìX€ZŽ^C›Ùê ‰/÷Mó˜yöy8hÕƒø—×¶£àÇ0êpÈ‘°ù™Vèu Ý€s.›‹7ïùÖZñÆ_ÒøÖü9øÏG7¢aÁg‘Zs?¶Ùç,šãÙ5[[vÚ½*íæûv­®È€s¾åÚÝëú³x›¿/ž¿³]»·î{ ÍÅNl虄¹³öŪ?ub¯yGaêà:ôX(nÁ›n]—ʺÛˆ_ 6­Ãy³Ý¼M”g[Õå,¾µ°CÔ:§ ¯=óN½àb,´ŸÀÿ}o;¬ê)˜¾·ƒõÿµ >‘‡ªúah3Voš„ÓÌÃúZ±÷©'aâÆgÐZPJÑ#Ö|Ø=Çs Öüvz29¤d‡OZä*g NÔS«•ºeö®8jÕ9Xž|—l9FMŒg®Eu$Wž™j® ¥ 2QË×iw 5ªÏH¨>‹] ß vWrŽä!±+ñDWW—·¹J‰?°mûvÜ~{#Úvì°äövTWW«š³ë{¼ïÛã–‘©»»[å;17Éz$8¯}ï} ô÷{aMËïò6®³m›ù…4Ñ[ß|µûŒqurI˜4vôZxüÍNì?©=ýÞß2ˆ™Ój`(7ÿ¥§‘Éd¼£ýîÀç²YX!K°dÒiÏ.ëÚeÜÃ0i¶¥AÚ¨HË#&H«ëb÷…wð턃ñtÁ6Qyªƒêc}gú\µö(LCÚ’½.Ž›ƒ+‡dxS=Ü6>PëÖ¶ÖÌpÙÿù™øLÕ-øÌ…¥Œ÷°âªF¬‚™ºñÁÖÁàx&Ì T@ó¯ÿÿqÉE¸éö:tÿ×óxðßš‡¡›€(`ý°Ý÷–Û=²(qH¼¨OÁ0‰á|«/>ü".¸àjÜþå<·¾ƒçúZŠÁº¾ûO²®ðÓG6 }žßÞ!}^\5Æó#Ðûî#hüùœ{æ•8uŸ:dœ^´®~÷þµƒ4æ neŠn¼ýÈ£8ðçâÆ%5èýð?ðÈo£Ë@ˆ/z‘'QÜŠßÿë ØÿÂ˰ôŒ< g]›^E¯SJîŽ_cÍ<&W¯8rð áàÌQfKPKPªŽ ‡gD®zŽ><'Ž}6âMÔ¯Í ”¾A6úøçÑèHþtAº¥¥EÇòƒ%ÙD2JѶ“³M&C¾crÓ»kþ˲Ðßמ¯÷Þÿ7µ ešÌ÷(¤1ˆìhAÛG« Œ9Ø{Ê$dÒ)¤S>ØÚª”ÀßZ!ËÁPØÐºzùy´mnFT2MÓÜ>wmÇAµ¿lK hßL¥Íd$@{eMuµ¼0<Êin[>HǽɓÆIë|òøBjƒNp­JS¾zã ½*Mþu±Ò¾ßò`šï˜ç§0˜æõÒ˜“½Z ŽV§“up‚&ÙG¨è_…Æë×Àd?ô¼î*ül„–Y.9øºi)DÑ úúÿ¾b=œ¢_‡Óóž½ûf¼ ûãØ°Kö…uîDÀ »çV¸v¦ ßÎKV³g'ŠÔ ¼ƒe²½±´{òîõ6 ÿéÿðEÜëË^¼I36†=ÂîûÏùmòã¬m‡z8"϶©Ž®\Ò0/g[Þ| +Þþ%1ç@Ñï›(ÂOt¬vóÞõò(Ymoã±;ÞÁüvmßgé}*ùê/½oª÷‘òÆ‹{oy)Ð>»ˆa»wá†}ô¯.• ÖY `f ¹û Y¯¦Ì’šœ£Ug®„3Åš>ÕÃá9z!Ê…gFÏ”˜]RŸÕŸ›I6Q©høFr€¦dú°+Õ[9òqø£¦F–‹ZŒ@2Š,—”mÈwnÀ|KEÝ– íþe¾Ç ztÒ–W~«¿ ÅC?ê|õõã`üݬ½Ð5`cëŽN´õ°æ­×ðꯥh5Z†gÈx¤ímmr¿w Öž:æ ïú³½«KÚÈCÚˆ„4‡Ú0°MrãªfjW:¼ƒêeía*+o/)Ä v…x&‡iúË'h©`Ðô:µ˜íЍXÅDù¾‘=#:Ü#™BÍûÅ€*e‹µ€5d¸V$0±©Œ›_((¿ìŠE;àÛ±,8A ò'5ZEzMž-ØÔ'ªIÚÑë~Ý–iGf€pÄÙ—’?‘Æ ^7Ôb·œ­€!»JåÊK²} ÂKýuعn·Mï-è»GUÇ_E:‡7¿Pògòà¸çîÁï×®@Àê?XÙ¸p`œ¤ÀÌ'º%fÊãà\ª—`*‰âL¾Õá œ©¼Vu櫉ˆõÀÍÕ}Y6ŽIáIàY=‰ë—‡7ŽVUj3Ù«Ôæ(?@Å™0 ÄE“ý®©Îa!)|,¢cž“…mTžYèÊh…n$h=¤3O½íéémÛ>är^Ìt–øƒùd‚ŽmŠEbß7c›2¹)Ú÷D~²:Z°õ÷A¦f¯£œD ’N§w® ]•Ë•Q‚t鯎lÙOñ‘áÑ7f…TéʇxpÏ•‡i¡‚i¾òlÖFÖ¿Øê4ëc“µ~§0C­"=LrßñÂ94t°¢ßZ½ó£b›eðmÊU»R2?¬Î°"lÑ*(Wÿ4Ë,V6zhƒaøÅ¶#ô@#tajdJ'ƒeÝ:í#ûÉ®Aj—`›¼ŒÃÇ3¾õ€¶Ñ“ríu¯:pF¸ę̂ÎÐl î“@˜O0dFÅá™Õ½GB7ÊPŸ“ó‡„çñ‡|M²G<õYï’ (7‘ ° ùÞÜÄ}W>A4}Œr* ¾n¹šrÕ\%Âq’ÇJëýFÃ2ÁL¤3Ð óë”ò<8Ž9¸Q>ß(„Ï–g*#môe‚ж:NºôšR “68_sš‡wP9éO©RþõÁ¯Õ )l£%üQ»Ä0E)¼Õª# ×ˆVœ©,ËW*Ì!¤Hóå\mÚî>t¥ˆh¯´îâè28†õƒ'º'•ùB÷«[ЇßqYuí± «lò¡nwPfU{Õ‘Pûy® ªhmûðÝOƒŸé*¸Ñì,#tŸÐ=ÆÆCpZ¿Å7©ãʼ %Çñÿè?ߨuV:wFNäÎó¨LœîôauÑÀ)¸~ÉV ×Ôgæ3´›©”w|—@•¿ð÷•„¼Ò¡c›JrS\uðo¢Ç’„˜81ÆZ˜6ø a¶ G¢‹0¹ÚÍÀ&LxFùåJN´"Nýg ±fó}$¦ÁáNµéJ  8P‡+ËBÄ ïB:fšÊ©â¦)/ võªW¹rËA‚_s᪹ʖ…¤0õ’ƒ1_#˜ÙË:hÜ ´8Æ + ¿j°‹åù•O|œã=ÀèÃo8œð xúp®3PÛd‡Ã2=¨ÑñÎÊiCñPDX¢ Š;O€/ÙÆã¥ šU1Ïz::|CˆHåÚ9!šìÕP©WÍ–áê'åëÂ>¨Ýê2H¥s>ÞXІç$P®÷µÌ\t»ø¦:‰ëM¾7ÄX}ˆé8Ûis˜¦<Lïžú[)˜Ž šç0­Z‚MÇÇáY0XfÊ•ᯩãžã5©G ¨™êFåôPÍá´|¨ŽÚì@—¬éœ·_iü¡‰_zÀ&ÿ  *Ë|34¶Z0…?Vâ>w§JÃ\Ñ@Œ¨X{M˜ŽÐ‡˜h¯%¡™¼æÉ`H̺M¤tã¿ËÐÌã RÝ<„C³œéÁ>8«Cjt“ ÉŽ‰|% –Ôs(X&¸¨ãÍãÅ&>¯½×ZsŽ1âùŸc¬Ýç´Ýu¨²G÷ªµ×™sÍ9æ˜cÍñýÿÿýߟ†„{JF€ˆ|—¢™«Lº@Î3f)Yx|1m'¬·”9 ÀBJ ª¨5¢Ð1¤_aˆ€úk­^ú»VäíÔ‡w’"âè»üý$8޾/ÇÊÈy¦wÌÓ´¡Œ\Ì3Éa•þ ˆZ0ÐÅ£¢&þ¼[Lá`BÚ[",FÄňåÞ‹qÍzƒ+—¯ >ržÆ¥ÖÔ"}¨PÎ7]Bi,>g„— ¤8Ò½X.WÇÛÍ›i‹˜äJâës.$KF‡L\Tc\-‘ÆCj@ À¸\b„2äÊ`pˆ4n|oó„t^sÚSÕi+ã˜ù¾ ÒópHüLÌÙîý–‹‘%¦Æ%¦y&¹Áï¿11Êï.cLý·Åh¼Ý­l«ÊõS? ß:ºŽš_ðÀ8ð3e;­©âÙvš¢JÎtÎ#VãõÎ3_?;ÝÃÍag6ðï<Êö ¿Y_èwMçËyî Ut.©ÝŸ9g½ê{¯Ù;óÚ‚ŒR‚LŒ¡lT̹ðµ.Fú~)…žüh v¿j©òŒ¢g‡Uœ¦LÛ#–ö  ~êsFŸ92W; u º3ukž€²hÏOÙï§ìëÐÌw~bú~P¿u>3œ)YM×LºŸ¹K²«%Û3»Ô¢…€´ÿô]þ£¢iþTZ‡¹Ê™µk, tsèŸE4wCÏÝ{’ƒŽ‰©?&øïš‚þndß¡åÒÞC÷þžIÏyÌ;ð<ÕÜí›"a]¯ºózZÞ3ÎæñÓgëüw&Í[Ã(iùØ´Î=¾Í©0$;z– |žÿÛ·èêEQp+½Ê"Ä( Õ?BBUÀœiÝæ9óbŠZPur‹ÆlVë>FT:VPPkfJEË/FYK-ü.¶Þ‚³(ˆå'ðw=\g —E€œAtž·|}%ó¼dæ ú#NkYP§ƒd”y‹@à,H·æÍج½’™=ˆAçÒE 5é|HPfÔ©¢¦KB†ŒXfÒ…×ÀÇ$ 618"€Væ¡Ò`Ó;¬ƒŒ¼ È1!†D ¶n×ÈÃèCÀDïÕîÉ3§N³,<„Äd‘o#; V@ƶðùb¤W1ãÁÀŽŽ·<œ2J‘Eƒ ¡Ì ZF¦j‰"RíŠ%2‰*!>ÇÌs”Ayåë,H5s±Ž)b·dÈÔ˜SbÀEÆYbðÕ‚6Z b\*¨sQ4çp,ˆ5ÐA…ü]»Ë%‹á¦öˆ/™Æ1Ò¦Ú(%`7â"@š€,Ÿ!™²r\<*­…o¡?ˆ ³Æý!ÏÏ^tk5ÒZôs¤þVñ0Ñþôwäù“R@:#% BÀY.2—<”YÇÓ€Uç‚­¨½œS ®ó”Áª,0±fU·¹Ì00jïˆÀ”øH¼ÀHX­V¤§œy®Ò3H >6º}üúÅYúG†æœg„ FÅ´å¹ ‹ôvc bðÔ4 éü7¬"è$ Þ-¬)  %ƒŒ®Wœ 1Îâ‘,<÷ƒÏÑ\äwRK7Þn£C2 ˜Fé_-t-ô¹ZcüAyØÕœ Úˆµ€l@$”P¢9›´ƒîÀ`­MŠP]§ãB‚6¯+@}¯±õÌ‹!£7<Õ uUr¶Ï1™2UŸöÕÁ¦G‰¶ïUŒbÀóX[³c¨w#?è|»Ufh;ØÏ¹48Ö_Èo6Gy æ$y÷þ¶÷ÛæuT¬aE‰+r‘õ­èu« EjT¼¸èˆÏ½¨Æ¿üíFG°[(×Xtëo#…®_°u*ôT, Þ{°]ÕX·ññ~ *èZ uÞ)ð#Ñî·::ÀŸËô¹58¸ŸôÆól¶ñÖïÛŽ<Ñb…ú¤èÄ÷$–+µ<¸ºi9âôM'k!¯Å'·­ð)ÙV#pú8€3¸ñ-Ñ뉷Ô-á-´‡Fp[âS¿-äõØíà`KëË8Èa¹GÀlR ƒ{öb@(3~êQÌ0žoW¥t – ÞJÀ‰¿ë5Öݳjç)´]Aâ8’Wœ5LóÔJ5¹I©½ÐI”È@Îzóe{AÓ œ¬wáóâpñf#¡h½6äiàj/Ô¢F1í# B<â´o[g\¶Ë8ñÅ9XFEÍ%¶”07Ô![\æyVCD¯_#])I†‘Œ”oŠÈ~é"o×å «Y˜°„ŽuÊ€ÎKˆQï³+åˆýÙ¬ƒ¡À‘æžL/?X˜ù~å€Rè¼]ÿ¡í½æy¦õCúÏßs™RU öç\¼Ÿý ¹ø=5{4 t×¥€>àV A@dO,P¬HE-í|{ãéw3 ö˜—Pl¿Â0ž«¯ `ó©(ËXWÒ)éʶ ŸXÖžå"ãVè¤Fß“mp@¥ûItÓïo >5HažP›ÑÇX›{0ésN÷úÂÀ_Q'U Ñæ ?o‚#ÍØÕLJ³ïdؽãW ÈüÛâûFs,I?}.‹”%óÖVŠ$L»)zŸ­ÏA"¤1Ð×p2è@—,÷ T3n#øà‹¯¨bD9ãIfZ¤¨(4² }Ž¥æì‰YÔg^? EÄa  Œ uÎ0 gƒöDR¸Æm³zœ÷µ$ð4á©7ŸÄ-Ç÷HèûÂ¥K´~º´£vÔ6› ƒ+—.a8yË›o%zG&ï«„ÙãŒVˆ!C‚" ”4Ôf A£!æmS@í!BwSh˜V™G(Q‹,«½¢„ê¶Û ó% ÅÛwrᾕvaÄ5«kê¿õaç WŽ(µ€Ñ.ÊðÁy«Yˆ†=Ü‘k&O¦0h=O¼ðÐ>êAëúHûvt—¢‘'é£xc‹¡ÕøÖ}íûCJ {ÞðÝ ŒË=•bä¢ÐLë í³Þ¬™0g ÔÃÐè2:nµç‘s!zAO7áÆó)Ô&„Z®/¡;ôL¹È˜òŒE€„èuMè;CŒ·0ŽltžÊ|3ï9½[„ĽÃù*j<pTÀ¨ßñV é.f—†Fž*Ôcªôý-èõë{_!RúMx;z|¥ø¹“Í%}‘¼Ù¡€!;pwo«~§…(tõD h’¦žßv\ÅXeš{~•êÄFB[[Œãa‹"«‘Ê â­…(·CÔ(Õã9åhûaû–b4ú»—)‰Ôd£Ù„&ZXPÈx…£q7ϧ«ÈªÀ£¥íi¤‰Ç•ã9:Í¡hÄGúi/ûÃÐ:%Ƨ/ˆa0Ã×ñ6)º58š÷XÌë¬Ô.‚¦Ê9zŠˆS£BŠ|jÀ0DÚ–KED±²þ~ŠØE>Øà×îùá928òDßðf4^çÄáBõ„Ä6h-¸ùØ ·ž<ŽsçÎáþûïÇ•+WèÆš´£vÔüA`ºti\ #ªZ†©&dŠÊL–Çô0àˆÉ9ûFÖA¨êõpgœr!`Ð¥J|΢Çáýè½-á½ZX-Wtþõú€À4·@0Âîc‹h詼ƒI:>e8ÔjœÞ9Ó^ ¨…—©/åÏZè’ö¡ÉÂQS`úMõ>v«L^¯ö@^¢…4"2Ð) ò+FÁy5ØâÝ­Ì ±ç´øÎËÅãÞ µV⌚¶ó!§»ù9¬/xȉ €Ž#ûÀNÇ3ä£\]{ŽZ[èÚ“zÄvRÜ5,²æ)(¨×yÅÛbêÆ)õ(W¿vŽ‚0•¾# T¶Ë1àó×ÂÕ•Æ9ÔŠ²çµ¶T>€v§0Çñ˜¹!äŽ#üº#Pă  KóPîs•­C=¤µ£ZDÊ=š °zz›ÓàFŠbǧŒ¤•!ªÑQ=Z˜ñî€Ï©[ú»0ÐðÚ>ο Hò¹ŸCµA¡nÄw¼h4-ûýôCòûn†=obB"Ã(tî¸+CƒRÝË_ri9°ª Ö¼Äº­ÐܪFÌÈtƒýæJ^c “;#~ŠÌ®†¼‡úé@gn€]Š dïGA‘'òõÞþȞ̔bãq£Äs U'T@@uEŒ ž0YJ‘¹`ÀÁyº™ÁrÏc’äÙõ¼±ù„Æ#Z‹ðΠF>–›{zWŠ‘œ¢˜, )Ižv\Y[ªŽ¿òóÄËž1ÈË“õ~y´ Ò”Öc<{zq²ò°8DÍ»ÑíDç™ne…§'©ÕŒ9›{1TâBŸÞ[`5DÜóÀ#Øß¿À-¬Ýv8Ãõ°7ìS¡µ£B?e³Æ¼ø@É#…ýÀrE™¶ø£j{G?)´4]„в2KLäñ.È5w¼Ë9o È iÔd_Ož- ~ ‹«=äý:>ïŸ(!*”b¢8ȵä¨l€¤¨÷”Ð@×¢ÜWUX¿ƒßêá± 3úQòm‘pTžžx7+æõV@]Ÿ•¼ý‹a‰ýƒ}9‡z¼0‰‡wÇ\r•GžèÚtRï¨oQäæ²EgÜzóM¸²¿‹—.aÎyWw³ a¢Vzò‘oáS¦ÎûQ;je½iïÉ™Å8©PBR™f|Á_ø|œ:q IÀM¡ªË!i´9[ŽúØø}³Æù‡ÂÝu2PÒ t´ÉN)#§zh¶NAD•/¤ÀJ˜h[ ¡SðpŠvµ„{ZPCúU2Íe´¤˜œ”KmE¦X—É3ïhDô$@¾p§4DõP¹7«`‹Ü?b„뛾0 <ˆ.-Åò{¶(fÆØ“År ¼RŠA%Øb“ÙOÇ0úÂ4Íô>~¯çÔâ §‡ç“ží¶(Рƒþ‚8&,·_¾gÔQz©H2%ªòV;zA1@ øòþV4DeÞ*……ó,r–ª¶ j³ÑS‹©øìFFpÈaâ’lˆÀ8.\­FCÙµêù DŒTãûv qèù &á–ç‚8(ß==BÆ%*nùÊ2ßb»„ÙqJ½ƒ©tðí ˜Jb>_ǹQ¢ó†«9˜B1nl¯ž° –ì³óÕiNËv•Ê$: ½X¥«¨+çê%Ö‚G¥w¤"uÌe¾ù¬èŸ1˜¡ÚAZÞö£×¢Rü5wJÑ32ÐEØ¡Cý¡;¨+ ¡ú=u£ÔX@4(˜aŒJ“³öaæï‘Âã+w=ÍÅ;os@Õf4IXŸÃn•ozOzºžËáj¤n„©ážš\O¦u•—#èÝHÇ5“%UC3¼m[$,bÀ¹‹IStÇÚé´‡5ÛNg¥îjÚþYnGíÈ]§ ùày±‡æ·Jr͘l¦ †2²’„,p}å!ÁuTUm+äá¾í¶ÛpòÄ üöïüJȈ¢#í%…•_kRNt0‘\#QÈÓµÙ ó»´zBg0‘‡LP,óµ;ˆ¬/ðÁ*£Ë<9·•®‰CÄ¡Fë…óœáÀ¤6ʘ3:¾´Ë‡…†Ó­Ï«¾LKL÷e±,—KŒCdOóf‹º…¨*yž§•jï/g –R:ï  ¢_‡+ Øø™§\XzršE#GÍ5nÔö’𼡓R ƃ-Ü×9Óv ø’p”N2«wÜTU\ÞQhëç×d^Ç‚.n\äÞÃsl“/Þ4 `ô?÷â9mòŠFPjˆ;ÓnŽÊ·¨-ðr°çÊ¥LŒŒu.µRn…õ‘ûŠ{’È ¹¶èƬ÷ö3o²ËºÎuïýuÐéûÇaÕŸ¬ -:…NÍÃZî=…ÓQß&'Ñ?•¾ #ê-¸r ïÛ§3°A‰ ölõ‹­m;žˆYÚjŽúpQ­J„6ºíϱÌF¹S3L6Ï4Ãcç‰n@°'—*…ÇA¿ÒWLR‰v¶èÈL‡tÚrX)$Fƒ~oU©'4£Û%ÒRßÚè¥ÐëØ'µX­°Ú;Æž´œé5Œ#>ë3?¿ý¡aä°«{¨Jå0õP0r0RÒh’ò'K8žujS¬B«²`2j­aÛpȲP­_ê3qí[Óew0œü¼|ñ¢•0îäÕR—ÔSåP¿}qñ‚"^ííQb V'±\Ø[,‰b§ .]ºŒí<‘q”ÈH€ÒVÌ8(TˆgkŸ{5tt.ÊA¥€~G1~̉•Ϫ/@SX# Øè¹ðÎÃ7~¤D…RP5 ˆVëVh(¬*uü6À\åƒÍ§¤€Õ€Ÿlµ"þ9¢BÀ6ƒñ^©¯ËJíiïÒIèeÜcFÏFcñ„ÀÐŠŠ¹„¢Îg·æ5’Ceû<:RP’‘gMxtm0S$$*øƒó´‹·UÃ)Êå¤ë ‹ÞúÍ^ДbbwÚ¬ è´ø=ò£‰¥YåѬϽԢ_GC>ö²|Ý ¥$zô®ñÙ(ÜÝDŽcgôdÉ6¹×ÞƒQàzó ®³ôÕBåüýüô$R~9ÚŸmźæFHˆj˜¸RK!ªOc,Y$-z1¥ »ŠL•ó¸ópG“Øâ{ôÅŽkÓ³{îôM €> ÔŒC9‚8K@ôn^¬ÎkrP¸˜ÆrŒxðáË”ù¸"´Y8ôý^þ±vò:ÜŒÜ/‹­.λ-àÔ_x~åßý%¼ñy/Æ»/h‡Oãó¾åð–×½ñöþà—ñCßþ7ñýï;|úËñ+÷ÿ þ/´í}x链D¾ï-œú||çOý^ÿ%OÂýï}^ùoÁo]¬ï~×ïoßÎ~Ñëð?ò·ð¥wœÆå»~ oý{ߌïù¥{0_÷\O|›·£¶··‡3gÎàŸøÚö´§=gÖëõ5½Ñ˜¶ú/9K2\D8÷ÐÜvÛí´’,è…“':Ñ?ÿöÃçÂÿûÝo M bÚšÇcµñä[oÁ=Œ´ÚcpqãBôä*~FGäZ›˜¿&» ljÁZ8  Ch$¯¬‚™5ÛožKçkÃÂöe)+Z@L͵y݋գýŒjÜ*80pî Ú¼;jpt d9F)JGÒo¦B({{+,K캂Q$Ï|ŒP®DAª&tÕreÇàËû×÷Í•=œv‚¨Æk>W“9£Ï4îRô$£-VÑò€Œã\`¨±ãÕæTCºÉÖ‡…°½`v»°—ò2I1˜2‡©fŸôû˜nstp(˜³‡£Ýë§}“ýlì:ojO-ФÈ^s[è<+x•u©7YAÌg5JLbOÏcœæ`Ô4R•ÕúW¸¢œRµ<ñ.Å.ª2Ó57öYoõ:ÑÌu£ÓfŒ- R¸²µÂm—Ñ ^w%øºÂ,ŠL {¶úÜþ¹á빿|à‘çCÃA~š ŒÝµkBPª©\Ìóê寀"ºËÆarå–žªò¨…‚*‚ò6U¯;fo(¹¶vP£º]3PPÔ"ê¬Ålªi`לQE >TËàçS ¨¡çzÒA»;Þ¼{Å5!y¢?I-®””Ã]˜3N­Ä ÜßßWýÙGÌùJ‰@Fí÷éøÏµåH_Gvå¹_ûf¼ýÇ¿ /X½¯/Òã©OïøŸƒ—Üy Ïøº·â=oýn¼÷…¯Å_²·n#žùMïÂ;?ï'ð›—ùûÞöð¹¯ÿI|Çþ›ð—ïø ^øƒ¿€ñÿý>ïuÀO`¿pÏ}Ù?xôþîýy¼é_…Ÿ{Åëñ[ñ/âÿ앸ç;¿·ÿÇs8û¥?€ÿOß„_~þ7áׯ\ï\Ûóÿd[ߎÚÙ³gñ¼7ß|3>ô¡цç=ïy¸ãÙÏÆo½ÿý¸çž{Ó]÷Ž#JX{L \¸pÆ¡-¿›AMI*Ûí–€ú´Ù —ªÀ°“™»ýöÛñÇ÷܃´XÊ‚•\–)Áh" E"×2I¸¸¾-j·½7$9¢€8µÂB‹Ô/§'èµô¥q]"Ó¿à¶ë5Ñ9ò´E¢àqxé¥ =Äh€2ƒCÅqH-OµãcƾÀŠ…î·tž„ê‰AæAN!µEC´4¯€8>öþt ÀÕúäe¦ˆÏYÇÐÔJô\Z¬¦÷¦Ãaí"ìDqz„ÍÃZ¹¼×.ˆöP½éüF­Bk}¬±Õ¯Ó¾¢+¾K pׇÙõ­Ú=rjG”ßft¾1÷¹Pu âM$]å¡·¨ÑøÉª;.KŸÄPË!Stdï—P?Ø?B5 !gûEd¯J)Ô›–Ã[€û(»Å|`û»ærÎÝ๧“¶Ë9ªR¸”Ñs’]ÝGv±»ÖEÇc:`î\oé‡7—ðìuž­PÑy 2Z#Ôû£|[m<Û±ãÏ6±SÙq×ñWC²1(J1áÀ»'-z<¥¥ÏuNNÑ+R Ñ"})zéÅè{N=i»å˜¬6Ê—8ÛmŸõÍxëw܇×}ÑÀùÒoÃê¹øšOxÇ7ü,>rïøý?×¾ãexîßÿ >¸~û-Ÿ}Íþ¶id˜/ã¾K |Ö™cÀvBW(ç>އçpýsáñoÃë>ˆ¾µO|âqÓM7áYÏ|Öë m¸ãÙwà£ûî¹ç^á1 K>-Ùÿ¬ì€4àÜÃã)Oy²OœÝ¿ú»¹t‘¦<ØØn6xøÁûY'w¹Äêøq/Íœ<éì“ðð¥ËËBÉR" Ãàà(%òCUb"€8MÌáu9«¢Év X-òR³{ÕRŠn¯UK\ôòÓ¨zI,à°n´U[l"SÐpÿ$ൠöYåÚ=§oTÐ8†órA×|üæ[ˆª<ï{ùà@<Ïk:>%n¶˜ç­D(æŸï†S£QZ¢&pΤ/jÒ˜f:VŸ8WLÅÀ#~1bˆIÁ»q&ÅEÒÚIo¥Xéï1k6^©ÓwŠÓ$¼]Cæ  ë™,Zàß…ô1›Nî0ŒŠ‚úþ†½gÍNChù¤6ž§LFYWŒEÜÁ˜‘͸›ëìÀË {¥a^|ó¨'$»÷nØ vÃì±H²mëŽÐqî8ÕÒ<ÙÔUµL‹„Ó§NÑœ=_Ïãòå+fÐôa„Â>¦¯¦!cQ;y·£»ŠHë÷äKZm¥ðœ§í@Ò¯7½§;²lmEÂHcÖk_ñ}G§‡í‚qÌb€±vyÚg3îÝ €i¤£vZØ2—[°ìàݯ۶»Qƒ¨QËU)¢ú€)öÑç­r¢M‰'­ßÊ7#ÀÇÙ#E «ßSò½aÑjt£ÁT¢R§PS@ô l% Œ’ ¶'÷ÌHeÆÚ¿âYðÞv*™ž©ˆ'«pÖ6O¶0£÷þÕ/Nýu¼÷5/2 s·Å›_„7üøËqçk_Š÷4û„›ñW^ÿj\yó—ã7.>>gñœÓçñoî›x!¿÷wqîôWã¶E@Ø<ý6ºv×ïÇë¿êý¦?‚zͯâÃïþ/ø½7Í8}Ó%¼ã¯ý|dN^ç\xÜÛ®‘°yÔ>üáßÅf³%4òHßu×]×/ÏeªÚ)·$ IœÚ‡ÏŸÃSžü$ õΙÍå+øWÿä-HÉÒmgÏàßÿôO_ù%ÿÏ7aïØ’="[÷ÔÛÏ⡇D#j]ÿ•’xÖRDVPK~3PQJ6­Z¿–N^)]–|JQC“t­H»‰çÒ¤à[]1™¬®š–ãé¶2…û]r›IÈßq`aRO¢í#ïóÞ©›°Ø»¼Ù¬É8Z¯×Ø_пía¯”Зì«áo§2À)Òtî=º‡=ü 8ÛàÔG¡¡Hß9ùš88$ó@–’ ì›W¼JŠä{ï}ñ_×Pî!åfŸ5ri%Ü Z%Qš.¯{VƒœÈIö ß[9Ñ£¼HÚHŒ'…‚䯬\²jK xN0ÞNS‹ëÄcìàsKÊ'ˆ «Õq*`6\º‚‚Ô ¥àu#@u¥Ñ•w Ý€5ÄV¾­CHi1èïŽö©–Ø™EŽ0„Ø/T3†M ÀÝßj|bϾVÝÏ© ]2®inô‰•-U¬§µ´×†¶ª ÓÉŠ)49¬Ÿ‹8¡y£¯Ê\é«3Ö®K÷=3&j)‡Ø f˜úM¨ú<ƒ÷üyp#C€²½ÒG¼@ži`÷´=>{Ñœj9’¸»Ñ“{\ì_LMÑ·<†ižHÚ.—|-G´¿Ö£D®^Í'\”ôÇxÌc÷›Nþy|÷»Þ„›~økðwßs?æf{zúW㻾à¿â{¿óäëÍ™`/oá ìwýþzKOÅW½ö‹ñÑï|!¾ümçñÙßþNüë×½ Oÿ·á\ï\OdÛ†~ìû\k^_ß}ük ³ˆÊßï_Ùlj“'(¸Ý »^Û¬‘'ölW©"·ZÃéS§qy;“¬]ž*明[ÄT â Ä4hÚˆx„Z!9 _.I¢U@œ³îªÞdó¶©Nm4/ì0 ¶°¸Î5„:£T«¨`Õ– ™»Sº+¥<Ĉ-¦#€¬Ê}á*…iÀòÊEäí›ý\¾|ëÍL{'Wª¯X±2/¾¥´T”Žël-d„Ä…4j 1V• ÇEŒ#ZTN–À01À5þò0Œz>úþ¼Où<1Øqú qÄ ~ÑÌãž¶ôÝRÈ !`WKÄT&Ô¹—‰A7S÷”»Oçhª÷+ám`¨æ TVF^1.n 9ÑRB4Ï.So²p=½fŸàäÕÚ%–*p¥ã¤-"â^=yãÐxgº$3ÚÇïAœ'TÌdÈt÷ZÞâN²áb9È~Š”MÈÈT.¾"xQÚXbã•íDÜüZ*._¹Œ[n¹…øúå¼P¯¢P2:ýæŒ(ø3þtD#®Ižd|C…¢å<Œ‰Ôw€Ú)ÜxRŸR}r¯˜À4–B¥Ø=ÒdnÔßiXÓ8St"%6ð ðõM*a·öû …kª#Áeü暪c­€9çÎf¡ù€Ìt†‚Yü¢Ì’gMŽZÚÈKß-2@)ʹ‡¶.bç2™{‰Ÿe–¼1¦V˜t¦ìÝ,‚iê+ø·ß1š¤TØw€Ü$-Ö"GGsÙrNA kG º©jSRâò¬)¾jÅ™ã'($½Yol‚öMùf}•·A2v«*·;µ£¥tÐv>ßc£èžÒü™øÖ·ýcüoÿ|Û{@î¶xæW¼·¾÷5¸óò5Ž<ŸÇÇ/žÁóŸ¼D8·Áòöÿg.~Îá‰ìwýþz;öYxÉó>w~ÓÇv.¸ó?…»þöKðœÕÛñ‰ëžëqo»FŽÚó>ó3ñì;î tÎ/|Á 1¤¼ÑšV7kÌÓSÎ Xs&oô=÷ދ眼ƒÃðE J,xñ+^‰õþ>Iýê/ü,î{à¼üUßB``µÚÃé[Ï ‚yDåu)8söIxä®»‘ÒŒ@z´Jª•/HÐm‰®0%k àö]ye¦¬«;fè¼üzïlö²ÐÕ%+SŠ­vl†îõ·d]møTÏI µI*DBÓ˜):vá‘‹ F牋ÔI§‚ȼuç]—‰úâ‰]ÙòÖعNcPj@&Ï ¤­´·s9 ÒöàÞlê'yrÝëÜ•s~u~+ëwÓ¾ZL%§È 'ƒ‚$.ת%–s’uqî«Þ¡z¿Ô»'Ü[OУ>íŽ‰í»«^CÞUHp.-úVÉã¹ÿŽô)]Ä©’˜Z¤`r~%”Äx4ÁE]z1¢dáãGþž'Òßæ¥Bãuuþ¬‰öY¯Ò9¿ÉIÆÐö‘²Ö^4¤TÒ‰UàÛ-b½Í hÙØqùÁ¬¶ª&¥åì7 ϳpÅéÚÕAÑHhI±Œs®AÛýþ©´[¨¦yë niúZtHM¥FÛ¦¼Tžê)4OŸ·ÀŸkÕÄFU‹èh®lžÂ]Þ­îo÷³¯ÒÖ±0@Z²ôE«ÖZvã³Q+ÑÍ΋¶óè)bfØþ–ˆv´Ýn°Xœ BfàˆÒ€¬z ÐkÂw¡–Äöã¥Èlñ)O¦ÏÎCo)IIáSUçÒ+‡Ü¼ºN»pO³Ì*×퇆š^èoÛ5¢åün°tÑ™V=¬+`,GƒÆ]µ½Ýð€¨®¸uÜFG:ZVð¾ `eo5¾:`T ¥#uÕ¡R4ÎUÓDOs6Ì´«rÓûg°3|LŸN=BcŒ±ÏŸ€ËÜùóäÈ}Ã[þ‚êH–‚[Ož ïÐÁf}ýwâ@¼<£pôaóN D³Ð-”:Œãã£s!¾å¿/;ù»øÚ·BÚ¯ãåOý2üâE{Ï‹žrþó­Ñ/\ùåø¾oÆ[ßþÏñ?‹~åûñªïû ÂÝïšýí÷Û~ ?ò­oÁýø»ñßð4.ÿÁ/âû_ý#øèö1Î…Ç¿íð˜µsçÏáýwÞI*:>wÝ}7i _¸xáO=fes€O#0xaÉr‰{ﻟñŒ§µ°BñwÇDeõ•ëÊÉq{+6>çÌ¥ucdÏzbqëM§pîÒ>"©L”ÈTèã’*á`»=†Åb Â/ËqÄD!HáB3hëÒ}0 à Ä=… bC`þvϳ³”~‘^ñ0e˵…3‡”D5a`ÀJ,¿ àvŠ9Lzµq­àc‹$‹sÄ4mP²ò[-fëW¯½‹nñ4é<Ó†û Xø>T &‰å²iÊGæ6WÕÈ—²Ùõ4¼ÎF›ÓJ¼O^m’½¸¨3rQMjKÒò"-Ãà¼T=ý|{ë$²Ý±UÝc¿«܃V¿­ „ôÅÚ(8 ’b8¤»R2•§¢tŸ³ш í#—*wÀ+‚§U3¨VJÚh!‰Œ UÇè«XDV~ÐûÚSž|þøw}îð:¨¿ Ý;ÿ:Ï&AÇ2ÏÀìI~)&=ŸîcÀÓËSG16¬ šsü•Ú¢0l°ý‹Ì' ¹F±ÌŽúaϬMe…Ƹ3ÌÀs¾G?G¼´©\ ¿{õEôÕ’ˆB _îr¼€E?’î‹ r•5ô|ô7ø¾ôϹFš¡‰>¹“°gœž…~RàäÓǧj™¹‡XæT@Bhµ©;ÊMÓˆ®U‹mc©SÀ’±uw×ë׊¨.Ùè\zÞçF·#è4xW+‰»Å™ÓgpåÒ%«Px¦\?þÞ4‘Ú@hëä£kªJûÆé»×l—Þƒ?ý=}6êþû𪧞«Ð5ßçà7ñ­Ïÿ¿OÈõøÿ_z§íW/þÞüÒÏÆ›ûky‚û]»¿ý~—îüQ|ýçÿèî "à:çz‚ÛúvÔ6› î½÷ÞCcóàƒ>®1«Ûò … •†$u›:›o>ÇO —™@lÈyÎÄ‹ü²—½‚ö=qóÍ(› 5ºqè!/šÍ¢±{ÛÙ³xð‘eÅŠ Ȩ5s¢ÜLsÆÁzi˜çÌÚ¢uP5 P…Aˆ*PÒØHôÉQŒç¬àˆ~× JØb%¯=¤c_¯6¼4oŸ¬ÃîY š¡£ÚÍk.|2oè3Þ Xßsoµ'TY¸Å`@³$žzР ïcLF3QdŸ}.½î^V­¨G­ù.wH1Ê9]öKxÖ64=ûp‚˜sˆµâ_m%í4ôÛ~VpãÍZu¦öbª)Môç:/¹7¦´„`´‹ít.\^ͽj)FçÛ'O¢ÌJ_q`'Þ¹^ËÙ-!ȼ5O°ÎyKÄÅ$Òt!àm[l<çÉ%Í\C؇õ{ªen;ðN£WÓú—/\ @ Àj°8} ¢-´a\u?WD”HH Ú÷`üo7zÈvC9„ÎSê2kÙç‰Ý_38q¸¢Ûüè{mòÈE…jj“D;Ú˜k/;XNVÂ_*ªšŠ·ª'ÝɼA©g¥é¨)5QŸÔȵ¤ÔØ`Œˆd×ìÚÏ­ÂÆÁ1':fèµ¾)Pé_óÔt4øçÚ|'Ó~ŽõÂÚíï¿×ôŒë9*¶rƒ[ÿãâ÷m·"4ô|¿<óâ{¬cħb;jG­ìSÑ”´Y#†=ÄEBâ?àCÀSn¿§NžÄ ƒ&ÍI¢IlòÂå—iµ—¢ ˆˆ¹ ¤Š„ŠõÁÚžÂe©Ùû6 H2xfË:[‰×Ƀ£…+ÔS#‹D°…0ñ¢dá?ÏÞï’ }1tmh“èK‰¿‹.‘=I)°ƒÊ±1P,ìÁ,äQ5qL=lJKÐENÀ” ­ò¢Õ*¡ñ1Ô™?ûB®Û“…†ÝÓÓi>——÷Ðs_­Ué4ÖÉ:›€†ßÛÃ@n/¾b€ÞyØ&§àÕîM:ÐØÕ. Dܹo ÞOO’íΗVZª•¶~®í¼‰Æ¹7Õ†,sßCÚYÎi•­\}LNÝèM26ÈnZÜEYs ¾ý¾¤ETz…öÕåõXFp—cj”‰TÃacÁŠ˜ªÊ­òˆŠ'ÕÒ~zßµZ¦J×Ñ߬|SÔ(塇UDtcÐmUùÜ{ ¥©ä¯ÙÎÕ-OõyÊœö+4 ¥Àú)Íu“µõ…Aä{>?[~½ªOXá§U3ºà Ò«EZ´4®ü<²¹éüi½×bìvV @ÎõcA€¯_b›tÝóïcôœŠ€ŽÏî×G}ÖbJ2f32û 9¢}Öi`j½$Ð'"zÿÌPI]5ÉOˆ>Ñ™¹a‚>6DÜzò8Ο?¯e¾¯×ºí!E¬öö,{~š¶‡*ŽËYΟÊí¨y£ëfó•ËÈÇO¡”Q²ý ÂPæx÷ÝdLªN–Ò&â°×Õ s¤*$2r¾Ž¢-<›"Ê ,VK£J9ø-e&õ‡Å°TO2ý— (DK\ ]±JŸMÁ·ÖÛJuƒèÎÖ˜´HŒ|'ºGDKP°p€TÎ,6À†dºÀ×´¤Œ4Þ1,<ñHu¥‘-t<ީ󠄶|7Ñ?‚ýµÓß²èÛKçj LV‚AeÉxà1Ë“„RÍÃÔ%X­–'šc;øÈy–Êk–hø)OÞˆj`yq•Ús½=ѳºçJ·WI䋜«€Ù#n4:0PÐ s#O™®G眳'”úý°±®ÈX¬ˆ!’ÂŽö±à`³p£FVb*ªI¹´‘&rɹütCxO@ÕW“@{@FÙӼ͓àÅÌ»»7n´¹‡x`Z×0x1£¹l ÄÂÿžb »50C5j«AíÚÌ t —zl ã•‚ómÎu÷‚OŽ¡ÅŒüù#ŠC熠zô @åȘÛN-IS ÉàK:ì”o¥˜V:L<2 î5—K1:ÇÞj…5Ïš74’H8ˆ|Ö{D¦Ò£Yä4¤ˆÁ4+ö¡~ÍÓD9Q$kJ&Ó™KæßИhmˆ=­ÊÕ;Bäs×F›Œ—Ô ‘ŒÐ@eïxŒr‰d hÔíÈ}Ã[­3…nfœÚ[áö3·ââÃã`½¾N²ÕQÛQ;je³Ætù"Ñ ¦,öŽ!ñ6f]Ó´Æf»E=Û(¡`ä‚)H ±È@,*W´TÞŽ #‚&ÙþsL\õ® ‰ ]õzöay®ˆ&A&ED€ûâ”öö…Ê@̤˜Ø´›0W-dÊÀÆ“ÂØÓ<ð6c†d¥œ=äÌs«Q Ü3/ p¹äEj±Øµ‚-yì» Ø?z™âRÚÄ0?>º"®WëºÑ †Š€=õø88¡¿KØÑ•AC´¼èÇ¡‘è‚ÈÑ•ù—¾`o;©7I¿½\¢ ˆ•¯-³‘`k¶0ë'ð¢žt¦zÁ\t¢ §zÎÙ½`ŠHg¥„ €´ðâ®üyTúmtäŒZšDp>GÊÆÎ£¥V>^‘±F„…³#Œ¶ˆ€ÅjtÛ¯¢Ó"væ;IøÈÆ‡Ž ü-™ÐËá÷¡}×&»P²êš$IºTå‘©Àn"(¸Éü›¶“ñ³Ç0ê~©‰A…Éè`mdTW6a}ãn^°ÈVÒÒÕÖìŠÈ»sè/.[=1ѼÝteþ̰ߔ߸xIµì 0cB(\ét@dÀÈãUJö²ê’|]ôXb@V}îp5Wþ˜at.)]DNç‹£™'^€Àc;& KÝì´à J1îŒb&לÕ²ÊÔ©ºÆ¼²føí–Y׿FDÒ8a·/ÿm€U×­5©sÜàfš•˜&œØ[áO¾’þ¹xQÊ™FúozROž*³‚º^ Ja¯+Ä“_JÔHãÀaXÇr¢ž¶œœ¶™Ø»6sX»¤˜R¹-cèšSB]íoHb(–J Zé|]øct yF6B'àH4 O´1ž]òΕS¦ mßn'áé'ó:dhNÆ¯É ·xYq ÜÁbnÜ‘o‰–îiG¼\£:ˆÒÍ¥]<ß/) Ô43@j±.éw_.¦ó®^nMMs%¬=à2ŒĬØMÏ·ö ¡h tWXÉsQ#ºÓmöj„¹ƒ q 'ô¸ú»HKàt)7®;]ëΘ±~ô J(n·žÄØJ([ÛVÐ34Ä€mLÍX&ãzצ²k ̧æ1ÏØÖŠy;“Ç=HT'©7¾ªH`Ѳ—R§Ï˜³8Pš1,yG»_ç+vé*$qÚªU¥ç¨÷9 ñucÛˆ&É[NžÀ3žv;-~™A¡K†Ù1ðCN“ˆ:% ýFL l3ËàLØß¬²]ÓŸÊ3¶§YB§Nc@§ÄTL.þo*Ò4º‡\¥õ$9)2å‚!¡x# Š‚Z‹cš8f¯ ÇýP33Â<Î •* ¶ëI<^À $U#ˆ vŒÝ$f;„Ëð#qÒ9Ÿc³¥kÜÌÛöžk! 38{{ˆe§HÔž1]YS_{¹7óÊ0†³‡åЉÓy³+WÜ+­* •Õ7â<.¸WËUZE ÙfÛÀ Ñît‚{¥‚RÛB- š<˜®—ÎÝhÞ¸sn½P ŸõùRÎ xs^Í£ýÍÏCž ÜL~-¢ö’1ýÊ™o= uPŒoϲˆóv+ý ÜoàâÄ[KÔ̹Øç^þO¹ZKê ×Z4«½¡ÊX @Š!ôjVqӪ빱ØÐi‚_»ð@9^•Ñ“í°•VuÆŽµ+Ù‹.X×±öÖ©o0k¤-äÖˆXBÑpƒÊÙ]&Ïçã]½ #F“Úóºmz_Z­m›ëƒÒÀ\¢R¢ZÁõð5BeÊ:méñ¾õœvSô÷Š—ú ‰­·¹È»[øÜ”Œªºòª_ÝÑ¡r™=Ñӛݳ¨´Új´ÿÅÞý¬$`ÿlʩՌ3ËE-²µ;Çw `t#=EøE¯ã"Ÿ ¬EA”ýÇ‹\œEvgT<¿¥¸ËÇÀÜÈvÍ=ëi$õ4cmoÎãø¯ÿœä|ïn´¬8>:dD/j4X#zñ€ - '§x؈ü¼­¹¥`3èãñ› Ó`D»‘分¹Ü½R9ÐY§£Z­¦ÝbQé܇êõz:ïv5ÜšÂü0¢mt #05¥“jUW— Ã0[p `ãûå²öJ%5¢H§Í¦®û}*ZWà™pˆ ³?ï«ÝŠU(äM+tŒèÔÀlÇœ½­8VÜj›»¢0¢ÿw¸d5à`BQ½>жÀ•@2í>ш"sSÛ] ž)¤zúžÆnr&ýo¢R_醵~ý}¥Ø,¸;¼‰VN©ƒ-A`“«óü°w&ÐQ•wÿÿÜÙ3Yf2 !@H"$aªÈ&²¯®Ô½(EDE‹õu]jk]ÚZE©KÝZ÷úªÔªE6x]ZŠ ˆ€@! ÙÈ6Yg¹3÷þïù2Ç#Í$ð};Ÿsžs'÷>wž›Kîw¾ó{~¿?½ô7vì.ääÁù,ZpѱĴ¤¤$¤Z"J·’PHÅ×ÚJÀïã›Ø $8X¬¶n1LÒÒ<Œ>œùùôÌèÝæ ¡ÉKII {÷mmmmü_A9R]°FŽªRê$=%EöUzÙÓ ÆlÌ€Æq@Ôn~ù‡Ú÷dzsÄòÝÄ‘ºüü|òòú³}ûv*+«¨¬ªâ›œ9{6«V¯î¶kí÷ûÙ´i“Œçõz@b¨GŒÁ˜1cp8ü''Nœ8eåÕì9PA²Ë#[ãgúöéÉ¿…"­Ë„To]-ß…!ª¥¹ŠG‰ÁNáÂ9s¸øÂ É=)¸ï­J^_[à öâg3z`RäùÁ“–Æ”©S°˜ÌØì6V‰ÁÓy&:Í 5%`êÕGD±©o/ì?»ÍãAki¥“+…ð¦Ò¼pz›!¬QÒ¿/Ï+J—ôn÷ýê„rú㫪ÄRQœ„“’17Ö˼Z…^“fðTŠ‹k~œÏT wQõËÍ¡©©™êšš¨ý€èƒºú¼ÞFé§i:MMt~€Úº:²úô¡¡¡!"Üuc;vÌh\1Ÿ—_ùK\DÿïA‰]@G¦ŠéDĆDt8–±EH£t‹ÎÏÏ“÷SÕܰÆ'|i&“"ÿI|>Ÿ4USþgó0~Z0„ÉfAÝõ5mo®$ì×ûB¢Ð5|Zlñ2f Gå–Ë.!èNÃZ^ŠÕl"س7š–œ~÷ú1göƺs &>o= yÜ3g6—.{z³…®‘ÞC óT×ÔFÍCŒyaMç`é!q¤[[Û…Ãt•½û ¹êšk9mä©<²ä!Ѻ®–.<ÿ|CD¿ÊÿâNtlZÄ£œˆÌººZy}¼yã¯å¼sÏÁïó£iaBªnß[X³¶kBú¶›o>"ŒÃ²ðƒ>ä½÷WR^Qi̹çœu6ãÇŸN ˆr–eûµ–øg€9sæ°eË–¨8(‹ÅB^^^û§_i†€o›–éŽx•+fßÃÖúID¡VðÁ+ëqÌ}/VœN޹„•¿¾yó²ØºöJ²]ç±A׉@ÂgÎgÞî9#™hÐiüh1g/Q¸oÕ~f²–[|6×ÚÅKÓ\(±ô=ö¢Ñ¼lýÛ;ìêuónÌ×ýŸ-gÅ›N®Z8 Vñúg0ù²èÏ~ÖýõuV¥/fN?;еãÇ8qâlݾ›§ž[qQÀfµãHLdPA€l÷(åîß>MP €YøÇâ«/åÔƒ‰v¥}/¿v˜ïc º!¤qÕ=YÝþœÍîÛ—Ñ£F‘_@(¬³~W3A¿F’ËŠ+ÁÌI=|="¤í 4Ñ#G²Mž/Ckm¢ñ¿b8ç™gÑøÄè>­¯½FÆóÏ“± "#Q28ö4þ±c/ êX<¶6öo:@úôEŒèˆÂfOÿŠ?ûÚú ©wñ8Ç‹8q⪨ƙ˜,ám}z¥ƒhÃõ‹lw~] ¢!5Ê+kåo¥qþQ"¥ëB"-S„4Ïßgé{ûv«héÝ»ýrsq8$[O—Zà šÌ+‹s)ÈtYäÖ>¡È7ÄÃ!2z¤““Ý7ÆyšÐTµ¬ÏÝw£¤¦Roˆd|>_vÁ_ÒúÎß#Ú}Ûí¤ÿþ÷”WXN7Ç4®ŽN_Må´Áƒ6zqøZi;ùTÌÅ…XÚZQ¬6Y܇3 M bnmtÌ~?vT»)ƒ x®j3jLóFB)ƒªŠ‰ÿ– (àÉÔin5  kDð65“îñ€â%VfÏžÅÌéÓeáÚuÈbÆ gŒ?:=ݲÒt<źܜqÐ#ç¡ëÐ邏ø.´°ÆÁ’»%?/G—>BRRâ·o"iÁ@0òÚ¶"4[º”›n¹5&!=lðttùúëÝ÷VF]›Y3gH°¿1ޤºi§­Í'ýºp­Åqž%+q‰ á0^˃C€H|tmm-ý Áÿé§Ÿò„*ù轜þü–ÎÚÍ™OmàûÑiÜõ1‡³Æ’ë µˆçnZΨ%Ÿ1ÜÉÑòiQ3Gz0`Â3r&÷~B‰ßÆ éÛ¹9è`ñÐ/µŽÏ·”ÐÿŒ\’´:vnø„Ú´ñ¤[°—ÒúDòz'  Ð;Ä%xCƒpÒÅãŽqâÄÉéÛ‹ÕNPÕèRgNË·Ió¸¹þª hgÕ‡ÿ¤¸ô°œgœÿ]Ï=i]¡® 6íR¸úÞl”nN%ët8INIA@²ApÉØTþòa µu*Í~oÒä×Hq˜Úµ•ô·Ø,$&&Å4OÐÚµšª’vLj¾þztU¥þ¡%‘Øe×=÷ÐãÞ{$Ä1Œ8¦1_ß<§·Ó‰ÞP©¥ sjjÀ^QŽeÀ`ÂªŠ’êAoiF««¡%7ÅÂZ¸Ÿ#¬T7}Ð(‰Q†9ìvù¶9êºéÒ˜ò“Ÿ¼e¢ºÄ5»’knRL1/æ´Z¬¨A•éS¦2cÚ4ÐSOŒ»îÝñ……O*&³Ü ðï"ýó$5\'‘ »ÝÖQú˜H<6(² ‡5éoœÇÒG£³œvÚÈHê#eÝQ×Ån·SW_'í›ìÛW(}»)þ\B7öìÙCVV–èh ˆÃÒ!Ö|nX½¡q7Ç"\µ‚›nØÃüן&ÏJШ]ýk^ȼ‡Æ$£p4„(iJb´ÛB;w›þA}(æ¾Çžƒ¥Ó/냀›á3†òñ ¯°ô_n\º—Æ@2£ÃeB>ãgYí˜ÉØeø4€.?ŽÄ‰gİ,0™yýíxoÍ'|µk/?¿úRœNQˆ©áçÏ-ã`ÙaRR’™{ÁL†Éë0SÒ÷1}AO‰…NM ÑÃb©Ú)裉€¾æ¾|m-8“ºq¿ˆÐˆù$š }¼~[~ü`ý®ˆ§ç0{„‹7ÔñÒûÕÌ›Á5SÓÛ]yvªj ¶”:„#fXà¹æ”ìljòðzÑpÿñIz,^Œ‚€˜Z@øÈ³4Ò¬fLªŠÖÔˆ5$¸a –‹~ŠË&Ü™ ¡Øíþ±Qòs;.¸õ×P¼õèI.캆Ûj¦4¬Ä”RÏdlýèëÖ'_#³?$¹aÈxHë£Q¸ÕD((}ÐŽ˜y†Á&Ûu]“&L„Ц´ki D¹õwÞ~=ü? ây¢M&S‡ýuôW9O‰¥º_{C¸¥]弘ƳZ­­ýAwW®”ötŸ†§ÙX\ÈÎ;EPηˆic 9 -Nu·ÜÏó î¤ù®uÜuJbôTB%¼ñàL\ú¦ãTÂGúvÅÿØA0g* /=,k+Å^åÏŠ˜pñ c¸ñc<ÞýĉG„pV¯<ûòß)ªª+%Å„Y€Ü@¹9Ù(.> ¥Bâ£,ÁjµI*:EQÄ!ð‘‡¥Š ¢•U•”*£gF†ä„¾å¦>üdÖ}ð¡1ç*2{f2cú4ùô§YD`µZ:5žr ½|ùr©F( B:*öYB:@úµŸº—·ÏåÕÏðÑÕCHT8­/Öבÿ«¾Ø‰ßfî¸àm.|û!NKHgúMS¹ñÊ›øË€‡™Á:n½ýk¦¾8]„yì}9гsX{2,ËËÚ¶“9}0éÔòÕºñö™M†PœôÓ5+Öòeút$EÝ5ô;¯?N KÇOqâÄù|Û.š&ù¡wï)b铯à÷«lÛ±‡[®Ÿ/û×mÜ,ý¾OD+Š´nÖ†·®ŽÔô4{Hv§ÒP[“4|Ø0©Rxòð“Úãm£Äô…£S? ‰¯Êü èã ÛcE ¶±sç׬[÷ï®ZEsKsLó êÐ$éf»œBšÆ’W·óÎÆb:âÍ”qlšåf¦w=JS)ÅÔaf³â, 4£`2c«®$°k; §C8„r$ûGóóã9\‰?!‘æPPúõ!ÝÔi\D¹3AÌ«¨$ŠB„ý_𣵓"}"qц’£@0Ä®Eîûío¥}“{~õ+&M8ƒÖoépX„4(°äÄ é¸ˆÖc[ð zz÷îÝ@/1[éii’Øý8#‰Æm6»¸€Ä"=ôÈ#¬|•Ì¥ HZ»Ÿû³|ú‡aʤÉLŸ:5²à –tzróº\nV¼ûžëZ8‡ˆeÏF%BÈF¦Ù7räHÉm8Ï" p9n„~HÅB£1Ñü/}fïú‡²l`˽¤  ¾"6”öbB¾ó{ïPpM}Š•·^ÉOgärPYÉŸ§ºPbéÃ=Œ’ÈÀs/¡aå*Þxâ])¶’Q0Ž‹Ïù€`ïw&sÇ­àï¯>Æ{¤3pÂ\ΑÏ];~âˆ'ζ¯öátØ))«à­åë°Ú ^`êýüî‘ç¹pÎ 9nô;v8G÷©hÐ@D4G¿¦ÓcýøìsèÛ7‹Ý»vS[[CMmÔ*ÈËÏ##£gäý2ÝVišÚJaá~öíÛÏšÕëX·q½‹uŽVtšFcÍa|è(áÁ¶VŽEiæ`^ÉÈá¼C zA‘ËÂ6N I7ëX‚ î°†úă´õ ±ÒZ0HÓûï°ìe4[µ ª¦³#fæˆmÞGÖCE¡Di¤öZ2ßùLW"k«º›û÷;å—LÕ +V‚+Õ#Ù7ÆΧ›¾äõ·×Ê~“Ù"iÐ×Ö“‘î9†\ë^zõÍAG§ª¬´KÏÙêšÃ¼úÚëRŸàPE%MM’7úäaC—GªÇƒÍnÑ×ÒÔ"î¥ñÿbÛñ|®Gxµ± „¦VÚ±Ûöa „ø6VWgOY‡Òzp5ŸuI_˜Oua?\k Ñ—ªÕŽ¥©‰p]˜Â6»¸Ïš_©:mÄ‚I=F{¸¶NŠœ¡¬óùüß;U ‰hƒ"d+ªKÒƒ¨sºÝ‘~€_ÿò.&Oœ(±Ò»ƒòò ÷ÑŠ®w9SWC£WÚ‰ÌúUQUÉä™3ë˜ûŠ ¹öç?§w¯^ä瓟ŸGaa‘gÈ}\åß+jc,”!Nœ8qâpÿ/±gßAä "¨ï¹}aôqޝí:âZ6Ësj««ø&Én7)îTPø?A öKiß&©ÿy" ›ö¾‰æ¯íê|#Žô?±Q´pnXe´I%ÓlÁ¨­Á0eüOØÂª°€¢DèXQÃ!ö8@YUÁ@€T-¤CüÁûŠ‹¥¿¦i’›…ãÎý< ¥ÆrÉ%¼÷þû¢[Pþ{÷÷×™×ü}ΌĎ’v)uÚ‚›‚`ùnµEÚ²Yªj·âª—Hõ–.qân%¸G€øÑ°!HPñÀW¨ÚfÛ’- HÝVÀU·B´ÛÝàÆv{æ<¨#¶ŽIã>¶Çy½¤Q<Ž?çל™yë£ç<'‡É˜èRî4 ËË+“Ç˯¼º/³7I>¸q#÷Þ{g€Lò§ýÿRJ666Ò”ý™¡~8œÉg~øÁÉã^vë½ïdãí¿Mʸò¸î~¡á_Žù›QÉRÓåTÓ¥”&×3›•Ò¤ûŸ±&mvt +Üܼy×ß÷MÓäÖ‡·ö˜êýÑÿéä±×lRJI ç¨Çh<ÎÕ«WóÕ¯|%‡€É=FÝX#c×ßI“I 8°«¶¶š&ïdÇMßš¤™†[`ažèzŒFã<ÿ‹ù¹ÇŸŒqÖ>¥”¬­­M>ƒ'S´ ÑÕ@':‡…ñxœ®ëòK¿ü+ùßþ­<öØç'S\˜žÿóý÷óúëßÉïÿÁæÄÂäfº–À……½t¢Y^}/¿pîK¹pñbVWß›Ìåy`˜Ü…niéÁÉÍK^ù‡+ºÐ=c¢«qýúõ|óò·òä—Ïå‰/~1 90Ln—|myyòÙ»¾¾¾Oß{€&‡‹õüÝÿÿûìßy D †s€}úÔbŽ9¶¶…èýœúêkÿ˜éÀ‹/¾˜ÕÕÕ|’¥¥¥<ýôÓùÈã_øÂ½¢à…^ȳÏ>[ñ÷G[›ý€ô^ñG?©»'C4¬®®VÔM!„h¢K)96\p¸Çü'D @Oþ="Lq×,þSv5ûaªÌ¯¥Êâû•õ×SeX¹ÿ¥I•ñlª4%UÚQ¿õÝ0UF³©²}¢²~¾ß×<ÓïñoÇ©RRgæfªœZM•ûßM•ÓË©2»™*kgRåÇRåÚçR僥T)mú=›ôlÊ÷¿éwû›qjüõ_í uJ;èR¦{H€ô]ÿ¾”¢ SÕq6&„h¢Àì°÷iì„hxæ™gòÙØØÈn^zé%Ã9úBtÓäÈÃ9ÆIw=)kI¹9y~›­õÜ•f˜4sIs*Ü—dã!ºl$Ý$e+û¢Œ&d#龟 Iš…]ÑÝLv5êr›²žtÿ~w‡`4›½+Éö»Éðl2?›*3›©rjµrý¦N©,oSeûDªtmeý0UF³ýîÿÖbªÜZìwûGéW¤NSY^ú}ÿ6]¿çÿú©²ñ@¿çiÒ¯¦º¾_¥rëû}ÿ”4Gíó Ž'èC1~7)ã*„è’}V¾ŸäVE¹•täîÐÎq•µìæÌÒBÎísyôìéÉÏk»ÜÉÊêFÞzûzž¿ôúäçÛŒo$ƒû€Nôô*7³ÓCgséÂS9÷óMô^œYZ̹'~$—þäóЙ“»v£¢§Ü(;ÿÚÏääâLjœ<9›ó¿úù]gíØJ™†ÍÙÏžÞŸåüøý9þhÃŽ!5ËYÌ‘€ K)éÓS_þÑüÀåWß —ûŠNôîÀp0œƒ§ž|äè í@'„h0œƒË//mgÇGЉèÒfWã»Ø r»æËmîü|§ñlþ—Ñ\²=ŸO­)©2s3Uþ+UJ“*[ ©2žI•2L•vœ*ÃíÔiR¥ ú=þ£¹~ϿҦJ7¬¬¤Êh¶ß÷߇§Re{.Un,¥Ê­“ýžÿMê”ÔêqhRR§éÿÕ¢¹üÊ»ùhCÖ×·³VV7Ò;„è’ƒ÷Æ›ïg?|÷íµ´!.þkm7zRáÒ›éBt)9h×–7òÜo¼’+ß^ÉÊÊæž‡p\yíZžûÍoN–S €R¦åÂB–W6òõßûçÜfx+džs€ M#D} D€ K)9hg–rþ×~:ž=3gò±¦ûÄÙ9Þz{-Ï_|#+«› 2÷•2 h:³˜KþdÎ}é¡I€Þ[ø^̹'Î¥?ûêd9Çm˜t OžœI…Iýùç~6ÇmÈÙÏžÚ§åœÎñ@î4„cÏC;paáô(MvUÚÏk—{gÝŽCº}"ÉÉ|j·N¦Êö|ªŒæª_—*£ù)ßÿA¿Ço<“*£Êúñ°ßíϦJiûÝÿÁVªl-¤ÊÍÓ©Ó¤Býçßæý©2êùüII¿šL·2åëoŽòþ£ B4ÎѤO=ùp>Éå—¿Üö8” †spùåk=\Ý @щ>| D—’Þ ÿÕ‡h  Y_ßÎ~XYÝÌñ@òÆ›ïg?|÷íµCуìtáÿVÝ^ßØÎ…¿ø—þ1BtIRJɾi²ÓµåÍ<÷ë¯æÊ·W²²rsÏC8®¼¶œçÎ+×–7v]ßÝ`’ûJ1OôÑs:Éì´¼²™¯ÿîÕÜf°*í©à¶ßÓ­ù¡$s9sI{_Ž&t¢ç?È®š.·)$ã·ò ê;ÑÇ“A—¤Ë§¶=Ÿ*£¹TÙ:‘:Måú+÷ûD¿õÝ UJSYߦJ7Óïëߎ+ëG©2šO¥~_¿ñLªl÷ýþ_èyÿçSeòs•Ç5âº$¨>ëû ŠûL±~ŠûÅÜ,æUßO|üœëýëK¼oõzËÏ-¾b|´ëûƒâübÿ'ñý‘°üHóäûWù\ÿù×müÑžËW~^þø?Ñe@ÿñßëß7Äú|óÏ<^}îÓø¯|< ç‰ñús+Î#Ç‹ûÚý…34žÓ’ŽÚZáÐõ‘ß—‰6ŒP žR-‚£n  ·ºfŒs®æþé¼TëûA¾¸ßTש™Y =žHíHû¢îÏü|=¨§b¬úÇÅ@ý¹[æÊtÆxí¹d⡘çÇúeòûv[pl^pÔàð¸3 ß†5ц1,3ÁH€©AÑÖ4Q.¹­{¥a†af¢ £Ô³Â>ãV úá6·ŽÑ/Ær¾g—oãP2ÉœbÆ<2®åüƒ‹u$e?ÑÞ­ÉÖý8õÏ`QÏë]g’‘ëÌÇeÖ4£|5&¬¢ –²ˆþHØS0ùHýXY¯ªwi*³ÖZ¿Ÿ‰Nfëõb¨ï•\¶ŸÓ‘$Ý ô™LúõÏc˜‰6ŒduŸD•£&§@ê¯ÈdÔÍWV0¬É6 Ã0 ÃL´a`UC›uÎ=á‘AóŒ%Û~l­]-˜õ¨§à>êÄ‹Lu«¦mL•Æ8ú —’}þs«BDå}©Æ£õº)öûU†µ`æÅÛ„£YÖø&&—¢T`ÉLk· ½Í¬íV6BkœK@¸Ù »K ΃}T07´õä1댚Yþ¤«7múñešfu®2˜$ÖôÖÒþI³ã˜ÚÊu†a†a&Ú°&ȾÁ ŽåV2õ¿Ó4L3óÀTGbÌòi!üškF¦~Y¦‚Ò–R»¨´ÐÚÊffkW6ÌÓtþfבNF%ºØÈL.y|ߺÀ\6ö_CÍAâµ2ŸÌ€µǹên’ûi7·£kcÆû ·è§>÷ãàÉœ,Ïró#xè:(W‡iw8ˆ6ŒôC²”!€QîÜÚBöQ€• C©+Á÷øcWÃ0 Ã0DÖ™VýTƒ±bf¢ót™™f*k>Ò®‚1VLªž'@ôeÖÓ ôÎ`Es|qû¿ÒY8‚“EÆÃÄx h!’âóÄ}¼ÑÈHKÆYìkýƒ;¬µŸùù¥àöeb)æ‘þãØY½ÇFßóÁýœx¦ X¯ãœÕY+í Ú0U!3}kK¶vhtâ Ú‚X ÖywfÚ0 Ã0 8ˆ6>ïg¾Â?˜s$o¦Š|ÆÀ)[Ž(À×µ¿œ33ÕÆä0¾ÓT fúvj¥3ˆ=´’QbÔ­@ W™ÝÀ¶ùùªotí%#½ì\ï†ZÁC0ÐP7\œ'Ê6M´Î(2~æù—ìÖZËÈ(î«paŠ2$¿(Œ¾_]<ÎæPùi¬á Ú0Œ4£)¤)À G:Öè2}î`ԂÀaàY€ìàÅ0 ц!4§…o®fz‰Ä$!³ÌÙÛ¹yhM¤Ö&·g‡;…^Ï ë[¨ßp¨´ð|F‹m>ÕÌ㸳vœ¢”ýº‰HÝŸ•šŠñ@ºCP=RãD¹¯ëŠÖŠ«ëš}Š}ÒO\Óœû)†Z>ç;À¯Z/DÏr:ë~GðCƒhÃ0´¦ZOÆ‹ùö߆6 ÃpMìd€sÇ/?ÆÆý96ž?×3‘ayÑ–ÂgVi<¹Óûƒ~Êh;®™2q|ìzµ¶™Ûº×Aêy5t?¢Ï¿wâ˜Û^ .!˜b,mŸ»yIõ¯<®(ã8ou¦þŒãHçñsÌÛäB¢5½o–[‰äß¾Töoþ>îóm<ÒøÛ¸™qþ‡ñ…†{½fÜóú™ÖŬgþ _jå§ÎÊqI5k&{ü7z0*;úìsê{`SÎ[Ÿ¢‡9!g,4 Ã0ª?ôå£d#èèã Ã0ÌD!Ü ê­ÀYi&óü;1Ñ£®è=Ÿè/êÃ`cûÈò´ïqÔwhí¦¶ù@}:=^3Ðú¼|ñ>ˆuÈ9XjžË ZÓÂýdG©'±ÑLì“ Ò:oÑ2«oÁ}sâaLë^‚›w§ NÊ€h8ˆ&§½a†aðIpÌxσêIÓ=Áœaù©A´a¿hÔ˜ÓF …¿kåxÃî4ÌÄFÚé†~MšfˆýÃÆ&;šhåø  ¸žìWÜÍð52ál`úŽƒ©Œ>ϵÐKüudu”úó=×tý$ªô§Gñ_²–¸2^¿Á£hfy'f{‚8ÿž¹óÎ'” k¢ Ã0 ƒ)€NtÖB—}µôÓwa–s8í£ÁìΉáA%XA °ÒR¶hê4c¬×ñª‹F—Ö9@1NÇR0ò²(ÚÅx¢Ò.ü›±­»FÆ:SIµOÌõ÷stÄÓ`;ﻤ±ñ#L}À™Xí^Â=iݮ秘'Uy£ëº"•m¢/Ã0 #£ÔäñJIV†gÃ0 3цií gåË´µÚ¯y(Å5ãÜlà®Âq¿fÅÈ1•Bë鮑Ñ6.·S1'‚%HuæÂ–qU¹Fè¢sÛõª+?ÂúøiÚôŠš¢>ÕÎ3 W•Ê÷”Ü4ºv­Fò8SËcRÚûhÃ0˜ãØ×÷Ò÷éÈŠ{»Šœ"©R´}?ŠÚ0 цQjLS’™œ\¢‘ á¨3¦/éÁóSõo<Î DƒD š°NÑŸ ̯pmh¢¤æVƒá•Œ†ZÓ©]?¸‰O¯7*!fú›ÂõB´©BSNõ¤g0].7ÒFChäGçå$´ÏÃÌws¦Éœ¯‰ÆÔÉø†Ý9 àYÁ``-%)ñ¨ õ3 Ã0m˜‘ 㢘]YÇ´~ˆö1 ×ÖW×Í»]™&ËþØè¸mf5²•n…`\ûæ PiÃE»òMÎçle·¤tßKh _èTçïvÄò ¦ë~ØÌuˆŒ—rÓñZ)ŸÀåþ‚ñÌÌï3ªýÒ¯·\¯a˜‰6 eafÚ²ˆlkW‚Õþ% Ã0ÌDû?ªf¦郡™dŒýŠ›’ÞÚ×¶ÓMEˆ 3­AÁÀ±Só»(æ7Ï'ÚÕ¶rÌ¿Ý ûN™Ùfq>‘`<)§ÌDßšñÌ?wuôeöc»+͹`Ÿßõ83-ê“è7æc`£6ùìÌ†ï©æ‡;Kò}˜hÃ0 ÃÀ÷c3 Ã0¬‰†h?… gD–cãþ96~þŠF‡;£B¨ñ Œmåúuÿ¨£É§WkŠ‘ÚÉÁ˜ˆr¿¶Å>¼Ê¤ÎëýQcª‘ˆltš£ŸâV 8DW0Ÿ_¸a¤ãøÊÇË9Õñu×Î[uðWD=æ»;c~Ň6xNvÍ5³Fÿ{ rßç ÕÉT.éþÌi=øZs³ˆkâr+¤öÜûϹ–9µ2.÷Ëǧu 7§Ê¸ÊyAå«,´ÞB&¤žb©ñœêПkò!î“BûúŽ>A+`&šät Ã0 ˆŒ/HÇñ`öÚ0Œñ¸|s&Ú0(~ýÍÔákܪ¹åhf­Q÷ 4öÏÙá ‘Ç2PL«ôWn<Žu7¨}ÀŒ‘îÊ£ãaµlJ'èÏ5·‹è„&:ú¤)Ù°¿{•åàêªÖ>¿h­©Ví]¾^´ÆíãÇû_ žïm5Òf¢ Ã0 -þâ¹3âu †a˜‰¾:o7ŽIiv!ˆŒ17dºF&ï Ósp[¢ ÛùEk¿ãúõéé)4ù®ìë¨×Jq+eá)ÚU"ùC§óŠ‹q¤r¹Ð·¹~½ú~!7µ”f|^ÝŽ³ì0ŠqFú0Ï·37m†aqÞêÑdzM"¿uvLÃ0ÌDÌ6—F½Í¥Cë:}Ÿ1È@¢AÃxAfÓ–ŒjÒíãÑ5󋶸èdÆÅûPÔ» G°cß™Z•H…ÑOŒ¡`*W2Ÿ’!ܰ÷y‡®s\ëÜÀ°‹Ìˆb¾sýšqM¿cÈ󺌴a&Ú0 Ã@gmtf¤ ¶öó†aÆDNŒFfš‚‰sã|¾·d€8ú¥é³Àìÿ,4ÚL튨îóçQ`Mã‹ÞL‰b=|q®1ÌQG.Ó¸mty^>f<$2ˆ*­{f°+ý)´ÐØšKľÌ3.bȃüë' ´ÔËL´a†a°&kI,ô­ æ¹|EÿÔæÈæ3aÖDÆ8…B´ú)·f,óûûnãCNb¾;!ÁTª£`šE]‚®/h¼œC}¡±/òµ2µh=)os°>O}oñ"CKQŠñïFŠL‚»IiÑDËËiýwƒûs®6:ÀKºy¢ Ã0Œˆgµ74ø$Æ£}°:·™hÃ0Dvç€`ôæmd¶Òla$X!a .ëKZõ'^gÚÀAfJvñ$vfà¤ovÛq(í9S¹ÔûE»ø»Ð` &šÂï[ºimt¹¾}RAr¯ï)ŠþL[ŒTZýä“lIÓ¨_ôù’íñuÖD†a8&Ð ó„ç¾ÈÇŠW d~½k0`†™hÃ( Á\c'fNJZ7öÅ6 ›˜éè2 Ü3 ÓçEµLý™Ù5é9]T´ûàó “¾èçST®$xqñ|,Õ\\ͤòÖΣ߇énå‡ÜþDE3ÈZ«¬™í®õæóéó£áó}¼æüúL/Rý 3цa9Èóé2Ìn²`‘û¬'e –sÛ€Ì"/‘ûÜ;Ã0 3Ñf"O€ƒŠ›¯ÿ1¸üEø@i«5ÀAfh™†@ŽÇW^¯b¦[íLv2-ý(ÖÁ&Zib53†šæ©žŽ+ÆjVÚ|h†Ngè¬_?Tû’ËÇãó“ãxÒþŠ÷kÑé|i=Ñ/Æ=Ó&ã Cü9E³=ßÍxþ±ŸÓ÷è’îëò£ò}‡Çý#Ó„y¹iÞÜSåýÈ\Ïý²ÜFÌ7­Ž ó Mõ´z~ÍÌŠù:¿ÿ5övkâк(Ö…ÁxƒÅ|Ø8z¢ƒè÷…atüûm)«ˆ2ªÑ¦€Z¼u2Õ¢=¯%þÎÉUêË3 ÃpM`ÚƸ¦w§LVÄ÷ú±7+Ú[ÉÈP0­£?: 9¢ß`™®_jw{‰¡ÐÖ"j?ó\J-tŸvR÷ëϤ íJSRÃÙ¯LÚíe"Í>wÜx›q‘ï# F´I]5¯ÒdóÅ}oÕ‚N*D}vÕ 5ÑgÃ0 ƒBþòÔlÒXÄ«9HgÔ…üg,qLª_†a˜‰6ÌH·§œ÷b¹ýy ÿf¢ÊPúx.5tp í ½ ¨õ·–îîæhͼH­5/K½ PÕÅy”S*úçæ|‚xRÁÇ}%7à´pE†^RhŽõ>÷Ž1æ8|¨O4÷Ê0¸ëy¸{Ú#ØÃ0 ÃFÊ«0°–TEKE| Ã0m8ƒ![}•ñæ×·ÃÒ6f¤±W ÒêÒ¡KÍ„jÆìò3×@w’’cu¬³¿Ù‚wå„Ç¡x¶æè«soFy¸†Vk5á«×ÕÕ®A1~T=Ækð¨ïõÏc˜‰&§a† fCg"ìˆ0éGÃÛÀ0 òS˜hÃÁˆÒä ?]jŸÑ>`ÛÌTŒô»GuÅ4¡Ñ'zu ÷N¬5Á^û+öAì·šO3lJ­çÉÔ33cávQ©s‹`›ƒÑK¾ù“8Ã0¬‰6 Ã+¥ 4Hte ¬²M·ˆWj3 ø0mØ'?ûæ?_[¶;a9ê+|,Ã(•ïs@1К© p]³N9>ÎC| Œ„Q‘ŽXq;AéOÎêy]içT«7JR ®ï fûúE fJr^­IΚíñëÊõ]Ü4Æ¡3”^4}à Ú0 Cd)’}Äá2ˆû@²†SÙ¿=l$Yë1ï®…7 ÃA´a4jý/Á$ë뉭nû€'Q ¬Ô!4Ê”ÇÃÜ©Õe…™.ÆoËDÇéZ®K—h­7Ü_4=[lž»OÞÍ_úàY1”çÀÌ´ƒhÃ0ŒqÝ ñÕZTÆOb†á Ú0Ðì_ÜQ_ö;Ùk½¹[º3*wÑ_¸NP0Ø*ý4÷qyÑ`çt;KsÁŠòEPêTŒ{«¬=Œtè¯YÔǘMž.¯ée€…&yÈçy|].³ö—îß×}Àkó܆Ý9 ›ý† ‡TïÓÄ«-·\Ço°a†™èÿø_ÏÛçŸÓ~ü=6~þ*À³6,cã—¹Ó79ß?ŠyØz?„Ÿ°:ÔW]3ø£2~‰~Q/Qoð‹p<ÃjŒgf:Q¶ÿýÏAW±Ï:3㎌XdŒMBòwVL3ÄS#4d|äŠ_4^c¬9§kõ(§8žúõÌëZR¨]E»= _ÌGˆqê‰@@_G`\>•]?D¿až»;ÏŽuéã0‘å Ú°»ž„a/²¯ÐOz®eŭꢙgÃ0Dû¾¡Œ+Ç2Ž×ðsÖã»è1P0äL9¢ìÈX¨AhïÑ*Å|=zôýÔû"ס•‘3»o溩d0ÑQ"Æårª•ò¼ßÑåaü $±­?³Öpwj¨»}¯÷ù¿ƒ¨^ç‡æÏtm°ÚØJ~Ä•ÇÂøæò{«¡ÃÏ›Ó)0 Ãpm8V Ú sÀ4ƒó`'?gÔÎÃq·íÆǩֹ42ÑÈ÷óÔÄù:Çý¯)´æ¬í_®+ ¶‚øü µ¯iM)´»¯jf!4í©=P¯ó°DŸcn9ìq§÷Öóô®ÃHkÇ}£Yj¥ »s†aM‹¬|ÖŽYg†a&Ú°«_Õ®ñíòøü­Zh©‘Ö.èÈ È6Ÿi0•K*ó:£}UvjF“èƒ8ª!m´/†Úw6jËË}.Áê=U£¾Îx*fy­Q 7 é ̓}×9ì<šKNOd4‰GØáýŒ©/Ó!XŒ+KéhÆ £.9 êô¸]0ªí´•Ú:J¤þâ²´ ¤­¨ü-3ùiæù|(7oì©_Ôq_÷"2)ÖÑÍÄïé ?U£ü^>Ò¤5цaœ@‡T‚ÓéëkÆš¦™µ@«)ºA¡6 Ã0ÌDo«=Óý¯®Æùû ¬xpóVQ~Æ‚YÖ¨1ÐLô¼<ÑD£d¤¨‘鎈cënLØÆ„ÉÛ¯]T´ ‹¾_Ùå¤n÷Ç6Fž™y‡`"Ñ)I‘>Тߔú7ª`ðÎÀ4ã<îÒÁw¤q®•)wÒû$Žâ$Ÿé€É‹ùC•~†a0ÊBOŠ«²æÙã9j- Šñ〞öúv-b³Ý.Ã0Œ ‚ÌD/Ê é4|Ó¤÷Œ,óõ³Kî´né2Q÷‰Ž×/wq›$³Ñs\‘Ýr½;5Æô»ÅA¬$øâòãÀ*³‰Ø .í²fšÏô/Ö×a¦ÌdŸÄàsFUŸOÏCÁ Ÿæ‚a8ˆ6 CÒ©Ñçº~ÅK%)H ŽmƒÉì '-4 Ãp]õË]¦!€çŽŸp^  ™]ŸØv}Ü{ÿØ–‘óú8T´Ñj<°ªÉÕ s]gŠ+AipR>Ñ4—ó †&zþ]ÿúÝãBךç(í-³vzNû³W€§sÎ_鉶(!ö]etœ¿DFÈTÎKE;ÎOá.¢¬»A`€ejïC½¿K®§óÏ·úRÙwßð%½_æU†^ûY/Q×óÔW¼údA¼Ï‰çu ×Û¾gñ"# ü„óGÇ|šÊæ‹óaZ6U c÷ð¢ Ãø(ÓüºªWd££žó©D™h3N¦‚ Ã0DÆgd¦úxíšv­…Ïœ¨]>%òkIV|÷zçùWÛ|dMû¬5¹üNöáX¹ï%³Z¶·d2\ÛPT™Rî¹<ʆ§q<Þ\˼õ¾q“û¥ÝDÎv`¦ÿߢ͡†A¤¸·æÁ{!Øi 'ŠÏ…‰rÃ0üÕà Úð¯¹qÚ:?ÚmD§”$kî i>¶h£Ì³N¶ÂL/å ün±¢±Ç35άe Ö;³ÑQæûVg𵛆f!p¡¹Ýšø¤`ÄwG]%²Æýý¾7-Óó“[цa(D²´CØ×±ò÷Tö5j Ã0 ÃA´áç6í<†@çƒ*Sâ:´fõXæ+ˆpÙx@ÏQç- §ð–À;Ù÷ê÷QyÏP3ôå¼e;(üÖ3ýŽu:f±óuHfyÔW9×ò‘Æ6Ì<ôe©¤&:j]ßóù¬ƒhÃ0¬‰f”A3³G…‰žïé$-0DÌq枆a¢öÑÀXöÎÏ7fƒBùf+-p#s-™àMŸ îÁ˜8Øå9Ë8bo”V•T½v/ЀÚXŠ~lLû‚µ÷*L±z¿¡Ïõ 9À¾vÌbãëcç“41Leæ„OýQZvœ’×y*a+ˆóƒhÃ0ÌF'F¡Õ`«®žê†¬ë@ê¼`Ù¶a¢ 'ó8å|ãëùHƒ`í²pîuZñ f¹¾hGUÚQ2ØÑWìÏÁLz²CdMÀ,´ÎJ+×ÅÊ8%åG~=k³,5îÜùó†}\Àµn¸î÷m}¤D“Ó'Á0ÌŠ"¹q¤( ú” ‰T&I‡…Ïúš™kÃ0ŒëÇ¢ «¯p’%§ó¡]7tÿN†‚©´­W3¤j|b£žéRÂ~m*;P6h ÐÒçűîϬ—¤ßOzC÷yh®@Õ…›…`Î9…#| õ¾AŒÃ3-½UÄÎÔ 3ÑûÂ0 {D34Ñ屈ràí"ˆþ40]&ßVT¬—H¥Ñ>†a¢ çk¼Ïß þÚ‘ yÉp!ÚXOß]·¯ÃJf»¢ýM”D2þœPÓ8G0í¥Ð.«z>¤ûÅ`|ÏÇs1êþÆB+Ì‹Üïkù:k†ûl‡D¡Íï!d¯ÿ[=ÿø8ÃA´a×§±ðf]t´Wú;ÑÕ!}—ûm†á í;_ÞáÐãõ6 â&q„n‡öEîZ ÑkWÝé‘Û—4¬•a¾®Ý÷‰éóÁuÆ\ÿ<ú‚pëlkîó’¯vSгùëùú°Õ÷Óœ¬Œg¥žÞXDÑ.Ç-e=P®gªÍƒ$Õ@åsü#ú¥þ©.ò Ì·rIç«]žï—äbÂmìQ¸¬gŒdjÏ×Í\¯ôKë“Zi"Ï[;:¯èÇ]Ÿ(iù2–¯€Z'u¿±õMxïцa&è}ÑBªb†á Úð¿þþ±t07ï¦eã¦ç/}+õ bÛ¨”xÝ7—‚ù9D®šö¼ÓGXÔsƒ®wi5âÒ¤õÎ× ù¤$ÍÏ¢ü,°õÆŸ´>Á<7ƒ}Ì/D;ż×ÿÞ0æi/†av‘fý t‹­Â§†a˜‰6œ°¹Ã©ö–ã§çؼšÚŸµÝšÙÖþÍu0´ÇBÓ«˜_Á@Û0òÜD;wú‹}“~Ç£Ä$;÷[o2ÓΊ6Xl€ôæõÿ“¨oðF_G¬Ôüµëý5ÀjL×a&Ú0 3®¬´QOæ}æ7}Sá ÒÞ- Ã0m8àØ% ÖF7dŠ;ˆ²îú€_%£sÅe úp®1Õ‚á’ÚÚ‹‰ï9¸ñ™•À2µ+—ù¾þh ¤!dÔŠn d›íøîjœ¾G@å°œ_i‰÷ñ«ß—cÌ/سƒß„‰6 Ãè‘eøkßÝm„ñ*Çèq†af¢ ç}ÐZ¼>œÌ@3ËCÚ訋ëab!k÷!—¨L”¦À©T 4Ɖ¥qoÝ›š[Ë8Ø3™&¡˜Aæªb ëûW÷=·K½ƒžëi?ÀÊyø6Cq]™qÞeú¾™"}“L¹°÷×aL´afW™2 ~,¸¶_Z˜cFÙÉ@[ûl†™hÃÐ̇/oœil4:2m¢2ž3Ь”é¸v-y5þ‡p)¡F‘m°Ó& Z~®e˜ #ò¾åqê‰S]ó­ŽcJ#KD›„bŽq-¦’WÕFåvT¸Êäu]]-4ßâzòô<Ú=—¥ÖíÎa†Ágä,¢œ`ö~üçÄ+ŽiÉ•a†™hÃÒý?t¾‰¨ çj܈Fí,«ŒO?ã±¼š©P¬÷¨}2 3ñÅ48ò㤩pÆq5.·¯‰9¤ü!j ©õî©9øqCƒóÈùÐL>Úü¦õ|«Ìñø1îpižªi¾¾úØL´a†\:KÞU)uÃ0 3ÑätE4ʤf<4üxm:;béÌT˜æ` 6º@èŸsÉ$pFÒ<³1¥56“xnšÊ0á' qúOð܈˜Tve‰<ŸñÃeý¤G }©GµÛÁtªŸ‚Œ[5ÒFÕ3цa†Nynd3÷†a˜‰¾ÀÉØƒÙ`ÿüãŒôùÌ:Ñ:©%Ô÷A38å¼/ùƶÚé~XD¦1ÁÀ@¬GŽW EƯµãBÓ­¶Œ(2î¥:*÷eZ*ýU¯í£Xkj'òO}žõ÷Ǽ~ÝTû1W®‹•ýšÓ2–Ī/•ÏWK= ?ßPßâ·ËJóSÌ—Á¼?ÈËû¡¤ÌØVVµ|u*ÿ]*ãq1p8ÌD†awü1ža†™hÙ ñMRPqoîpBF ÆkbÅ•Cj¡µ+¨ü |¢%´®^g£æ›Uw„Æõ£¡ß€†–So -ÎCQæîü Aî¬ÎuQ¿çÎßÐôºlˆqµáT jh»šëPO™‰6 ÃÅcý]ſƕ¬¹Y]X* Ã0ÌDÛ½ÐàX05ãš 4¶f¬¸±î¹î˜_á³!’qbÉÈ`)¾Þ犦XNƒm) Rw+Ï«32Õ1¸¾RÓ7+©‘ÒÄCjîû{°•>9#"Û|³9zœ™UßÃë÷*ca­cŒ1·ò‘f÷¾ßßqfZ/j]ç¸P9ˆ6 Ãà ²Šßt;Yö‹¦w‡a¢mÍoœåh-#Žax±Ó¾¯o-zµÙ˜Éí€1'oʱTÊL…a¢úmíòÃÁÌvØéF²í0ºgíÂo ,5m3+Œ}Y®ƒoòµy>¾½/{3´êz0ÈH£ëþêLŽ£×=臮;þÛVm†ÁÇ*ÁR¿Ï†Yh¾ Ö6 Ã0mØwš'ýZ˜;3Ï|o±?ØÍT7Kh¡‘\9PÏXX>Ãgç}áký(Ž£æÌÖ ’Gú¼†ß2R‹6?âû;¤€™ÑIú’C»°h-n¾<¦þ<‡qg\…Š»0¿zߥfW,ƒû8œo¢¤µ'çdÇD†atvLÚèq,ðÁ ¶PüÓ‚BÃ03цa40éÃZl¶Wõv&!åz¾cJñD ]É„ÅüJ Fô§¨ “[»8 /çÑD3Þrþ¬‰îù!¨A>jë)´û•õJŸh}{Ý"´_ø¥ÄØPŒ1ödصÿ;c¡;ù=®wëßblü¾Asý@˜‰6 ÃHÞÓÇj ÍF#^W—€9[¡aÖDÆ5}œuF;5Ž;¯Ÿ'g@d£4k ô­oE 4Ðr±à¶Œ>T={Q½¯q§A&úhÝ@›6[£r½¨L!D•ÚE…¹Tû/N+Æ“Ç|ËìîAÑŽQ7 ÑΊ‹ûü¡‘ÖÏ­~£³•Û³?¸ÏÍ>Ї'f¢ Ã0¶ 5Ð|!^Á'\wð¢m{†af¢ ƒçž=zý›YÓ¶ißÐÈ\ÎKÈ5°nQ†Ü@üðì¦QVZ"Ê<–¼¯¤rþÌ’¹Úl@u[ mr!˜Ÿb€ë~ÑÈBh¢ÞÍFn9çúBc¬Õñùu&CÍôn«•fîß9¹Ð£žØŠóQÕ?.•´™hrz †‘‚ ¦vå'üv׸™ìÇàãßvÊ0 ƒ|{&ÚŒ&/˱ñ8g[´6}'ß+"3cÁ>J“c§´F]þÈB“Š%ÊÛ+˜WÄßéÞG3óÍLóS0䯇î×åüõÃ[ÉÓFT´ áNRÞ/ô}Ì î–õr¾•Sî/êÅ> ‹*°q}|òÞá ³‰GZhá­>§~=Öù{øXÇOUžºF,‰‡b¥ŽTªûÁü¥ãÌjM›œATú1Í[[/^Ó6âzDªx¡ý×@[&OÎjy½cÀ±Rü<ËtÌD†a&1e$”~ƈ®ï F¼F­`f:× Ã0®ÏD¦cÁA—¼É¯Ùæ[Œ±}ÒÚiöi¹ç%ÆcY3OF!Òî`Ò‰m·n5³¢ïÜ µ¹Y9®\ФÁ×€pK`õ?XñwñÔ²> y±®Æ’®®Þ×1 ÙuáÚ`^ooD1N¸uÈ ÓØ“ÒñŒ‘׸ßPßóPuK§.D†a-tD€ÞT³›míä¥?Œû^ ‰•Áu pÃ0 Ñ»¥}4¬e§¨ã¤ëagÂd(ê}@ j°DyÏj‚]Æc ElCøA탸`¥œ*îìÌJˆÞÔb; 3ʱóhW Tö;?Õˆþåú0?2}K‰ž…ÆþdèÛ•:`wÿg}~í¾¡¯Œ´pûÑü›Š‘ÁÑŒ˜ƒ :U]Œ3ÌD†±ƒÌ…ñ +¸uàô­l ¼ í̾/3Y–òŽÔ?úîö¶1 Ãpm˜©Æ¡5Ø™ac>ÎPLÃ`¦F,% f©Fž*´Ä&ËiÀ[‘‡éœÕ ˆ¼`€`Æ×²|Lmã” Žôð€ùÆÇkm³è'ý›Ý~³Ï_@ß?vgÒ¨ªI5s¥¦hÔ< ͰÒDçÌyœzRI:žkªAø†N¿¿Äu ¿l©%Îc‹qB ½ÄûjbÖÚ§µ§D=Á0ç®>2Ñœ—ósš¸µÏ·úýÔû!‚úT˜åQ— ©±Ö¾Ç”Ÿkq]¹‡5Òš1Öû:ÎØëïƒa’9¿þ뿦¯Ÿ?? ˆ6 È~E ƒœ’;Åwû•Š-ˆòW[¼ŠÀ:^Èî's z—'n ,Æ—¾ÔLß Ø3aÿþ÷¿§?ÿñ駃è6Ÿn_ÊC¨©lÃ`C,â|íþÅcãAÝ/»+ pS¨Ø×!þ&â…ò#²-=)fzŒc…I'ú®‡‚™—ëÎç£_g¢µ(õg”%}«ßžpÌ¿Ëå‘5ff¢çø¸Ý„‚QŽvæõ”ÁõmlŒgº¯Ê5ÏïR»Ìܧ´ÒØ[½­k ›3`¶e6ÌËÔ”÷FûÐÉ€¿z|[‹†ÿýŸÿ9ýë_ÿzC&Ú0 ÷eú!avåK Ód´•ã‡!ë‰e`õÎQæù>€Ž8±ÃG€¤à™åùãU‘"±QÏn†åŸblüòcl˜w"tqÐEtØ~¼å†a˜å Æ0±ƒœ ¡Ž>oŒ!K/¿_™_9ÉJ~e¦ûVŸë—yÏx#˜èœý0æ@JK ‚agÃrŽË‚œŒ+G B Ø l®‰>7€;ü°Ùpþ.æ_DzÎ@OLŒ`¼;&˜¶Îuë { t£&š·¨´OOúQßN&Ph¡5-ü£¡3cêûèÚèx•ò¯‡¿Ã9cŠ’÷ëF¼ð»\*Úè©js`:‡Þ—ò~£â^‘Ûkšfñ=çþ̓Àiþµàl»P0ìÓÅùv}Y…å†aØ#ô©)ÊA2D•OÔ—þÑNRÀZ{f¡k龓d(;s]ÆBDy9q+ëÂ…˜Uð;Ç?,ç0ìÊ!ÐÂ,jh m0EûhÒ Á|vj·ÁÎã|A{Τ f©Q£Œ ùyðÌéy¶CB0 ×q}P t óœÁÊ< fŒ¨1” ^«F|Q>Ïõý…[ [ïKÌ‹ª„#:æ,é`Þ¿ì"^˜oŽ)sœ£ðð¦f\³Å(üЧ³‘ž„ë¨Ú;ÝaÐø½”ú³×/ÿUi¶i¡É>]ú2¡j¼º_{»aQÖÏÆ_ý5ýÏþÓL´a—gK',!åÈÁq7Q¾»'4¶`¦¯I@k‹;•ö=ÚÒN0Kã3e¸¾<ô‰S§}fH/b]¥ä#ß§èÊîÀÁ0 ÃA´ÑͰ^ Zg¨¡5ªe ‚ ³hÌIrOøúõúãïðî ݤw.¼Ÿµî™­Ì;3ðåvÉd«}‹~|¶…{Dš/¹6דÜ ™Å¼.ÍDg†8Õ"Xf>oÉЗvvÙ±ª“ŽWÊT:è´?Kø@çë©§×~׌yj¥fž•O´Úö<^ÊO\1Ó;¹Gôª›¨2A¸cåñ:3ã8UÌc¹ŒŽç˜Ôú|8ˆÆô-`ç8D0•¬ìÂή|„жRfQêì|ˆ:ĽÎÖMƒt¶_×cå5Õêr[BûüÄ«Oä‘[Œ;ãsh†ƒhÃà1ój·‚V-^cf?ˆú ó ‘Ñr_*çjíKøúþø9M?þ~ôñ–\¬$P³:ÀôH^C0Íšç ¼8 6ówv½¯[‰¨O¸{1odE;ÞÀ#ßW]Æ9Ëyê ÐšÇØj` Ñ–ŽøèÅL”çšR‘Ö†ñ¯/?eŠŽÂ¥"æQã{3±¢Që˶ýÒë«ôƒ`Ê•&›{h¾õ0²!…íëÛf½ø~짃hÃ0"õrXÙå &ÍuÿgâÍŸ+‰Àpâ«Ö{ñ#·Ø·pŠ è"ÜÐÒ"©ÎÀÉZ†–rŽ5éLøÇ~"ý§©òK­t”ÇÁ0 Ѷp·ot´ŸŒ]>(Ú)ˆv¬[3Bš™Ùù+êóÏ_,4N ×)¢Ýê¬fØu{ŒWn/š}…ÐÇþ¤ªÍÁÜç Б£Ø¿\Vî;Ö÷L÷;û5#1Ѭh®ó1æ½Ëk] ”k/ÖÚ^¹tƒéEÆVV>oTZZ¬_L­ãÕªÛ‡†f~‘©Ž7û÷j¸Å¾)M2ú"¼:Ž l¨ê©Dsýóã?3цadºLóª@ȲogêÏé:È~ÂmCKâ%ù瀚YÖñ<$S—ìHݵ…!KçŽï[¬S•ð©}¸`¢§ìýÈ6³Ø?©oWÈ2·†áG †ƒhÃØGCÍFMô2¨ ãˆë‰fbóq¨Ìu_¡ž—äÍçÌïl5·³Îà8#ݮﯖn”û'ÚHòQZ§)ãÞ¯zËLõï¿«¾Å¹ŒsÍq% ”ZT¦ù°ÂÎC8täýC ŠÕ£_U:HŒ2Ã,Ü3´«…/˜Ø(}˜µØÐ/³Ã!sˆ úy¢hÆJÀÇtŒ©\¦€f™YwD°_bG¦zéK®>€hÚk¡å}݆õ †ƒhÃD/öÖ@s¬?¤ßpƒD:™œÑÌtTîQŸo¯ÈH˜´²X±ÝË#,(Дù«¦ÖTë ©Bf°ÎÎòŽñ–Ç U)L:Ȳ2­5’Ö™¿JT\;€¨sK7”¦z©Õnô¤‡”þ”¾åü]GÈŠˆ`ø‰xM©âzYa 'é-ÆA¦Êí£]«¼/˜Ï?êj!æc—Otm.N”×qN”mŸêÙ'úc`Ffû 4‚‰N?Œ2èܧ’­0I@nã÷Üþñòžå!¥ýZ’¿ üñ[ÌõTÆ‘íZt€ÇãXÉ8‰*ËuæÂòâL#!bä·J%1 ¿—¤Ö0 ÑšÑÃÉö óàù9xúÁñÿ½wÛ‘dY®Í<2«ºwï˹ñ†CJÔŒ@ èiž©Çÿ@Oú¨ù¾jþA/‚iFEЇÔÙ—Þ]•î6Yîn0O3¿TTœê]Õí8,Üã–‘•µrÅòe¼ìrÁW˜SŸêû\alÇÌæxÿ1+¶‰ÞÎ÷+ÓÉxÇ…ƒ„yFŸ—½¸qh6@’›b§®ÖźØ`Ç•%{÷oèÙl]3BŇY3¥Ð)öáP3û”ûd«êaÁ‚¢uMØ£ÒêòþZ^ͲŒÜ·Ç$§ð#W—8Ëu”Š„æ:«tçFÕCµN½ ¥F;¯GÉ.û|Çnî—¯)ªHél¥Ê2.7ƒ>Àx?;¾š¤Íšç ï«dö·ë÷¿ŸíûïÿþÞýc~ìÓ½}ÿý•€¥ÿ)Æd¢g̘aÀŒh Ih”mÖ ©àÄYm@3çg }üm ǘE€Z£œµÅž j0?þÑ @\I¬Fºó¨¦Éæ>qâþó±Ð¨~”µ´ÐåûÅáç­ÁH£ÝÞŽ½C‡3fL=cj 5>Ö^Â3ø+oÓÖöµÅû%Ð åýŸL©0ÐÒ\Pì2Öïʸ-B ÕA§ ³eIwh*ÇšÜñ“ˆ‘‹ ¥B…¥eÂÃèõ†Wô¼®Ô¨$#2ΙǬ|#gèyöºÞ57Œ±ä–š”wµ¾fc½³ÝuVZhPãÀm¤Ù”ûÄ¢³ý˜¹»IŒ+ù b“–z¼ÝæŠÏcŸè–yÄœÒF¢šv×ããl¹_ãØnÊ!×kß\!œ úuÅŒéó9ÃZ´ÑµlƒòÁy?K×jŸqän¤÷¼!çé[}©á: Ö²  %Û’zZζn×KmbpÊKºÜÆÅÖ±¬ƒR`ƒUQ³È=YL(–eŒ[÷˺&]ÌÍ’è=ÖJK‡3fL=c2Ò;ìÀ6IÊh¯öšóNMvxàÇžs…޵ϠJhT½1 <÷¬Ð°Rb;€Ì1{MIáÚ¡IìÝï1ÃÜo±Ë’Ã"˲^Á‚ýDÎ<4++ÍùØ °$'à$‹Y’þàIÝ`5PŸ±¶û™í[¯çy}¢q¤¦’ä-Ìô^s ÚáÿÜݧ+ÈNM4ŽúôŸéé=cÆŒÍ3mA âþG ¤öDB¬ú &úUÕå%ã ~!Ô˜ÃtåÄŽ•­à²f½ZV›\ш.ׇ‡œÁ³wJNЖ½HÆvÅA9€a–¥õŒëåbÖ­S"\Wr,­å¸ÏÇÀÜÇÆ.ËÄ|ÆŒù—iÆþüÏÿ|‚è3ºŒ€envÆÆJu–-ÝWjZqä´AØ •}Î+纖½,SïÜfÖõˆyÛ"‰·ýýÁ¶û#ÙÊ~ÖTÂ1ˆ–ÏNtA˜X—´;,  ó¸Ñ"á¹Ï'böÙ(\—VÅhu@4*6h`T tT÷™h LKßÏ€y[ SÀ³í6Ü?T¸L;³Ùœ1eª\?2ÌôÈÝ¢UÁpÄìÍWÊÒ^êy£¹Æà¢9ÀíoïÇë_'ÒO[m©ñ>éÉçù‹¿ø àxÿþýçÃDϘ1ƒ –]þHÑ/#½ò§öx ¯£ÅÎu³Œt ä鉚 pW@4zÅ@?4ÏM3Ñš}– 1' GYvî’ƒ²ðƒ¾¬ôz=A/h&šs$ƒÛöšé~óø6È:n”ç¼>ÑÀš_äܨ3fL=cÍ=¿¬·kʰ£‰K fê”–¸nfÑ.¦a£sÚÐÐuŽ˜þˆÃ29Û™˜–Kµü³•oqî—×sv®:¡dÉÁ²JHVÿ,Ž:ˆ¶® ¢…ö7V·¼èb8ÈÝ–ËËØI™©é™¥ßg¦ mǬ‡tÝoY4Ë¥T&&p˜/ÆCf ëÈIù;Çó  ¨˜h,®›T,D5>`Re|ã¼ßÁ2¿ƒýž¨‘~ìå þñÆŒ(ît÷€g©”;Ö@Ó^Ÿé½nW;Ÿà”s̘1õÎý/C;Fýü:K’Žwƒ¨µ®ŸøXÞk ب)C°EkTc(¡kÙŽ³¬3?€,ˆ(Xj_È;j?Ѝcá'k ¢ƒÍ- ´îK¶zÀp‡Êýb Œ¹ï Lú˜ÒˆÆR*ÚËDϘ1cÊ9fÌØo4ù¿ð÷ÒØåºþ·eÂ…ÜîG-ãnU ôªeÆAdlo@ª_cš±¯9§‘.n³¢³ ý.WéS­ªa KvŠ©&§äšY^S[Ωo]TlsÊ¡«ÊV½Þ¤±àhMãüy[”íõ‹1Òu/okégA²Ùh‡æqd,èkQ¿ÁTTtK¾G>eO™5ÎŒtðc_rÐ~ê€í€¤/Û{£¡•~ùÄÊÜ:úŒðX½ÃWzGÇšww—ªd)NcôÏÖã9çåti«^lìW¥d‚T,¤%3£‡4Èû“¯T4tº xKß8vX&Z­—ýÚ’W¾§ m¯¿è¥ôKøº‚+4Ïqÿœ¡ÐJû´àã»ëâ6˜[@ÅL‡|nÙþQONhÓ÷VÏ%§¿~ì+= p£ÿ^æwäê³ñÉ&íôƒÆL¬íoÓ:Ós3ó–ÉÞ[™ð)çxy7fÌꇺ ¶E^|þ°Nu)@OŸ«› Ö×5¹nÕÚgË$ P6 Eæ±ê}ì2RÝ#`j”Z ÍjcE‚$à&ƒSÅHSC ^Ó ãà‡ õÊÌëmÆÌµ0ô@Û NO ùF`&šû –9“ý;§—Q0uÆŒDϘéçaÀñw, ˜´Ë3wg‘©ægY<™Ð¥úÚ÷Ù*n±ç¢Îq ¹½øfS÷a0®‡G tTÒó1ж¬Ÿó=Á”€d£ñµ€š4§ÄÑ8Pä–t¾’Ý5³ådB¡äsé&Ña¢9D÷ÀtOªÒ°¸£‚y®ÉZBË•#7W÷.ÝlBéjÃý€²Û •6‚6Bv>k£óØ‚Ð^ÒyBÊâê‘]<Èq–{r8äÇ”}θð1óz—2å¾”hoÌ5PÙþø¸Ú ßꎳSL ;æFlòßw~Úp¾-ÚhÜ©)¦­Âç„;ûú$Ý:&=cÆ>Á#IÇ¥=UÇúHä³uâøÔÄœ´­ì÷8*À— ð¼– Z€/PʼN7”GàmıãpÏ X,¥ HÐr”.Y ´¨(ï¯$ DW'ÙŽd-ÕÈÒ®Å:[FÜj°»ÙÒ/4Ѐý‰®¨~ˆ,úbygeôS2Ñ3f̘ zÆ ü¸‡Ç ÄšrÐ×hP QRVZjW]í›L­9=è Aji­Çnà6h»ÚæÁ~ô-5c-´-ÞaQ3k—E‚qNY@tISha°÷D¿9,—­•ËÀËgÒ]F'åÞ]±§Ý:0K¼0Û€ÚÏ»ŠCeœÔˆM-­®ÕO<€õeRÕû*›m±"Ûa¿h´’r:˵^VuùÔ‚²‡.~ çØãn÷…Ï=Ø1¡ÏTñh›±t÷½þñ“Ð׉F.$;5Þûç6=í NŸèÏ1fL—«¹Äg(êò,¾ÈÝSÔÛÓ«tרöãˆZ€œÌqÅEã\H0Ô2cÑʃ÷}n'¸=¼= f²xŠ Šj…Ö’u½W²,€d;Ï 1eÏc¢§N:긬€©‰&#m'–ãÁL¶µºœ€)7¥è­LC2·E5@tP dÑ<‡úÄ 4Õ Õ·Æ~ø3fL=cúFãó¼=õ,ëòXÍ®KíC%ó¦×!g „ŠýÎ8Ö”™hÑË’4'Œl‡é!7b^6Y-Æl£–›Ìu[ÛnYS ò”Zœ4LCVÚ­V?}¼¸ùËa…ÛàÝ ÂG—¤çØ(¿dŒ¬ô!Ðe,ÀO^ÞCb”—¤w.eâVÈüÀ…<²MÈûçÞ¤SsjŒ?ÏˆÖ }kþ€¸¤—›lç.x–\jêQþŽ]ÈoÝWK r^Nƒ¹Ð`¤ÝÈÀWí°ñï qSÝõ›x/3Ý펙éýLs?pàó]ý^Š`[%Wëβフ™äþ¸q{š úåìŒ3ðÊã–@BÉu­*'k;ªík,˜3™‹™×Ï(ìå–{Ô›PhÚŽÇ׬wþ7çÉÆ¥!¼;"|yƒpÈÕ§O€€€ˬEpLJõŽ.í >žÿÀ Xgàevîà"/Šuv©/ÎÜ´~¸eبXˆ-g+‘±p˜L€°Ñ<Û¬Öy‡WÛ+ɉd&†ÃØ}W”ïfýö8X‡Ÿìɓν/ÿM&zÆ”Bîð¯4º>nm5¤Ìh hõšá3ÛWƒFrê;¦÷óêVŒ}·r ­ïWí3åŒz¼ÉˆíŸx(NV«ë´Gt¨N*”&ŽWc‡SfŸ„/Ž™y>`Ì_]ÚqI'ϯ 6B±Ü,«OLõ9œa ç¸o S`ćÌBŸ™qð,l´€hŒY¶Å¾Ü¨W°Q•ЧÌA¨´Ô Ô ËËUm4s{š¼Šô\™‰ËcÈš®5Ñá¾®½÷Š™ ªKî’þ;“¾¥°ÇvвBuõ¸Öؾ(á1ã*Ý=Ììþ×gŸP>nT™¨¥…~žÛ‚O¬üK¶ï¾ßéý ÆŒI(H¶ltùE'ʺHôŠ…~^ÇË®VmÝxÙÉçIQlÖŽ­ÁD Ðì/göù–Û»˜¿ŒÀ9è¯oÞ’öù>3Ð!ÂÅ%`í‰ U Q»ç€H¯>í¿A Tà³@g!K9üAÀ21ã\‚hŸÖaˆÙ0ÒVÎÑÕv’¦Õ=×À.i™ûM°+~Ð=iV?×öæË9µ KÀ«ã,ïÑe;:˜1c2Ê?%™6AôŒÉ8µ„¸ÕŸººÿxÇY LV…"rŸ m™Æ›šòNQ jÑ`·1 ´¶Äš9nùÛº>±;Â\ç‘ÝÚCó¦ PŽYXg <3€>Þž"`þæcþòF– >œ˼:ïÈ;0©Iˆ2w0d|xö'pvk ¸]\/ :ð5X—{ðáž™æÌN‹„ÃV$ Y[½\K(@9U¸ +\ªc„š|ÃÊ(À•÷ZËåj¢œ¹¯q¾H/ü¢`bì«Ë \I¶´ /;aÏ®´Ñ‡;¾ýìë.1âÝ=`¬!l«:Ö2kfzðäk´^ÎûÌ µ~Ý=Mú˜©&Üù½ÒÒtÓ¶ãØÙg¨ 7à”63Ê×SÚcQø§ŽÅ'ˆž1™i´,2‹E ¹ ¨Í@wÁðþ@ê•§áj!`bÒ'ÚšM1ÑNI:œOò㸹Y€~ƒðó7î…Ží—_`ÔDß­Að1À%#ÐB°QáV‡‹KXê~RŽÄJç×ît!‚GGÙíÃÃÉù ñ˜6@då äÌŠ–Z€²­Ø)–¬79¡G@-Фæ!ä $å²ãå¢ $ǾöÖ¶àÐá1a¤É÷ý®<¯1fÇŸXY¦íÎ4“‰ž1ƒ>ZiëçMÍàYîsɰV7: „ –…rÙ°¤Šu¶OCà Ó.ÚëtHÅ€C°ÀZ\w’þä¸1«>ß|yO5Fº¡•éAcÇ2±Ö­cƒ{ÆÐ¯}ÌLãÓ4Ìãï0bz7|_<5â~±²eüÚuÜí¾±íýÓÐç¿8Ý9>˘1©ûÏ\±`¦Ü±ÕŠ^:pÿ™~dT'Í)Mw\÷)S ôò& ÐêÈebÜ*mYÍDÂÃñÌúçžþ69qÜ.¥ogmbŠ—ÄD#2»¬ž’R²¾;8„›hùÌâ¹mP¹¼€1±D#Еµ^®Õ&®irØØÖIs)ËßÏÚÃÊ5b@µÙR~Èc¹9 ¤í½&í2¢¼ßÉhEÔö| œe¦]P…Z,[F|Ó„WíE=ƒ^-˵ßA G@z‚è3ÆdÕß½=õ™mìú2ƒòKnjB}Îx ¸ÐØÙéÉ}Pì2ávQtæ8ÆÂoáÒ÷¡Ø‚¢ò”IK ºob…WV ¤j™š9¨Ëç3ðûô‚º‰´ßsÛÎN°÷R†³SÎ Z´ÉÀ… ÀÖ8Q¹kð:kï¨4ÑJ–A¼Ì WcH㎈[pgfœ/| LýÖ #}Èç94´Ò‡º•WøH[íòFm´>΀™WôÛçÛŒÏõ$’TÞ Båóª2íǶ–™¶AÝ7hãŽH½'cm }>DϘhÃNÙÇÆÈËš•Sö=NwÏÙ¾êÉSúüº„ò+áÑŽÝM(M´€jÓÌB'hˆÎ_çI„· ‚CˆìôÛcÔpÀz³Ôâà’\™H@´'žD˜^˜'ŠÛ9,Yv,t–v¢ ÙC@¤ßƒB§&Α‘ýXog,€,¯«•­v1Ë2Êz$2Âõ¤TªþÍiЬd5¢µÐ’ðv˜b¢º>D²"r fÏŒCI¬ €Ïn3ôÑØéÔvõ´èÓ~¶Ç~â€/Æ÷züÚðã’ñDϘÚi†öjÙ¶ûD[æ’Áq`ÃüæSšãpëD¤ØgZAeçåuôBœêFL54¶“ñý¡˜F{ôu·6lŽûF– Š¬Dà,}'@ú¸$¦øÍ!å¯nSv˜€5K:x² Ë3îÖds·æ—vtiˆ.VÊ“ YG]`hO€$ˆ´‰„s%KY-¼Õ:ËqVL³’4ÖJ‹³eÞÐuC‚ǰÃ:W&úiöN»lX'—ǹ/àWB¹¹Î„Ý‚q_ÖÒóÚV*Ó—5P2Ö9ã±Îô…¥_ñTµ§}ê þɵC‚h_…Áý’ˆZæž{ÇóTÈoGúsÜÑSÇ%9÷„Ã8Òã fÇ©5ŽŸ Xf‚蓉VŠ™Æ*PAP†íã9‹È˜fÄñõ2Ѳ÷tíÛ]9@Ut<Ü%û:ÒìÖÁ-··‡dgÇ€øÍ!VALàù!n#°wsxÈ| „°þþDðíÁ‡5ms¿ã}ƒãö+ƒiÆlÄ@:5±f+t½®¼>JÆa¤.DËç…ûœËqŽd€ Â†ÖksÕ5À6‡Êd¿ÂHç.žuPn9;4~¢b\IÒf‡44·Séš Øb)­iÝoQ%jýF&›62V»©lýPØûÇŒf‘AI–3À͇ä¾àV–u0°Lì2"Ý¥-‰~sÉKÑ—5Î!õ.ë›%­sèâÙÞ!j¨¿ºÁ¸ý+Àw÷þþGŠ`úÙàoß8{öˆH\4E™ú„–×üç²1˜.d)ÄNîµ°£JÍ͆ežn<Þ˜LhØÜN˜*…V»¿ÝÀzªSÉL;Å ¶Š´àÝ­ìj#®T€èÀý žCÎΧq·^ÿò9ãñZíêûJýÇýº†–TÆ'ºã vWBÄ=R {`rúDÝ@ôõÚÌ$·Y^êé°ÑöšëñÖ•íOüdûîþãûÜø|€Õã¼¼˜ zÆŒiýGذ™ê€Xѯ—¤B…¢lÉöÇûC/÷·ï£÷3¸µR„e@ø¸¤vrb”±P+œ<—ïøâ˜Ö;ŒòKcÿÝ1Yà}ó&êÿyàg·ËTø«ï¬"@ÿò†` â)}rIiâ}zß!¤L$,4‘Õ ;ëcnHˆŒÓ†„š8ØÐrÎŽ„‹”4HëÁ¸©aÅÖ­CŠ´¸b)Û8§õGð,°Ö甌ô%SQ:™­_àb¿ûÇt·ØïŽÝ9¼iCƒOûL?}%¶ ¢gÌÀæçVH! r¸ÎN÷½Þ®¾?˜¾ߦ3û¤^/;/x+7±Ì‡e É ´Ñ®ã­Vúi–KØÕjö]J€ìýC¯]9D+.:蜿øàæ½S1%ÀW–`Ä|{HžÐ‹“Âtwxb œ|jwëë @Ùú›[wiÈ u´Ç ààÇ3Áoï1÷÷¾øýw üú+ÿù·þë·>ç1f‡4À !h_iö}̶§Ž¾Ôg„33ÑôÐ7h+ÇóDËŠT6ŸSýZÔq Ø­ºuˆó†bQƒä´Ò)™HË ˆÁ²ØÖoøi@ì·¾O${²vñ¤dÜÖ>Z$+ä¶ù [wŒ5õ™i þ~¸µ²­ämóÝTÅÖÍškÚþdYÜu´|C•°Çú÷6¶5çÖV•FþÐÚþTǦïù± N&úÕ`·ÓãyFõ¡dl—×ZËɲôÍ‹•òÛoœWe­Hó$Á^2F03Ðþ!{ŠRŽã‚Ô¦¼8HÀ{Á XNýßÞü‰sð‹CË6æÐêÂg¼&ºñ™ÓpjZR¬4îæ’–—ð}?AôŒc¦t膱‘ÅÐ×;³Þì?f˜ÇÙ2Q–íBw¿1³"¯ÿ:¯5Æ S‰Ðõµ•€}–ûLÖØ¯VlÊ:¶a¯[ûIJVîÌØ§Éƒ_üàæGÅ>›–,é Ìu›éÒ`†ž•¾Ïˆyص¾¿'¸LtÚùä)ŽÿÑ—‰¥¾Y’UÞÙüýŠã?œˆ«ÆmÏ™­ 5MØžåú°dp­Áó¢YàÜDô„ÃÎ5»\j*ÇÚgÜ6§CŽi-ö,#©,ê `–Kå^ƒJwe¼Ÿfeß!-}ÿºßuï°Dë@‹­9¤¯°Í0w÷2¼¤Öïñ¡Þ_ÿÀúùWˆ Ô“fyAY,Z}õØ]ÅõJÆ=*š­®/S¥3AôŒ3¦ƒÊXn}¿Á–q^V€Û¢GÐJ½ÆÆëÊ•lgwpqˆKvÇqDÞ,é3RÖI'Mô/¿p©†ƒwG€¯|KðëËØ¯—¢ô‰V‚ü3€t€¯Þ#PAZ½Gb-´vå°ÖU H;ɤÀ3J± ±lã2&¹µ¬Î¯˜h3 ·O|³@f<É µ¼sv×.„–e$9/htbR2ÓcM´ìƒ×ýô¢d@ë' Ž[Obω·XŠÊv4ÀÑDðbbÆXÒßë¯Ùæ½Ê¡¡1ýõ$L°>ŸŠC¾íºb•¶ % ècÝ×À=MûŒflÌ@ã&ºv_ÈæeMúçã]ZF_è5W&Œ%¾c[–¤s^À%iFć ¹ø:b?ca†àÆÇ~d—ÿéÏxwO÷ð#ü]¸,g¦q€Ÿ!À{ø…ƒÃÿÄÜüé~sÙ–(DúÃùõˆ²•Ú£Y{:/å²0Ò„,ÕèhÙŽœpnöµL´¬ß Xk:Ù‘¦µÏL ¬™h[IÛʧX0Ô¿1 ƒï§`3I6Cδ4\,P1š-z£«™ëÚoÈý§2Ï&˜6øHS_ =>/Z‡¬y@£Úg?[`·çDû:vL‘å¡ßÿ6 ¢_y̘1cVÓ…XØ𙿠ž.n“§ó%'K»7‡Tîûí!Í;fK;DŒ¶t'Ïzå$çqŸwô`ÇAÇŒ Îð×!‰ª¿£DaàøƒDuÿúÆÁ?ÿÕDï#bˆ€ýè’\‘ v‹C\)ˆ²È2Äœš?Æí„¡®Ê.x{eiÇË2Ö”t+LJÉz¾v¶¬>=^"ETR‘ó¶bâÞÁM@©“kµ™L@Ο “Œûõ€/Ê2¨1†Ï3‡ ˜^ô¿ ¢gÌφj µÆ¹ï+-à£ì‡ÚÙÔÚ~¸7ÆÚém.n½~ä†îÛgÛ·ŒíðAßSqlÌDKŸä>•ZèÃ}b 'f¡må:CßDðýc‹EVŽxqÖppï‰}¡#C½8„svåX ’ƒ†§h]÷ïþfÞÐÿ˯À·à7—öß/íßû¦)»/àÇÑí鉚äSNÛ´ÖÐõ™)ŒÊ{7öSµ dpçÚÀüâÀMcAŸ>Ñ3f̘ñÂíxhøXÊ ¯ÇûlcÇ2[,EàÆäá!if£´þü-ÂÝJqï…l®\xN6whß]Úý.Àü‡/þáß"À}”r$ ýŸá¿ü&ÀŸþG€ÿ}øøc;~ñá¿}ŸXo’úèR;e«½ñšz/ÚšŽ´Ï Úßð¸ÁVþC%›Á84€´Ñ¸ÃÇË0Ú É.5ËöË9£ìÁ2ÒÄÇë3€ò:tá&^y?ø™kšé…ÏÕ ó<°ÝùˆÁ:} ôÑ3fжõ–‘í×`ž÷e«qöz}‹a¨¾þ1CÛ\8€Z ô6WޱGðÞûÜ´Ý3K¶R#êûH²^ü³ÓØQÊyÇ>‡ÈtŽ\¥0X‰-êïNó?úÚÁ÷'¸4JÀ6‚ Øú€íÅýâ$ÁN¢_Œúç{†ÿë?xø³wú~í.@úðG—¼$­ô×7ÛMTxäãd fOBtIiîk¶™–ë–¢É5@´ró Çû‹üÃh¢•.:,ÖB¨­k–hlg™f»\æ`Æ%Û°šé &Šy¥=w ™¿õ•ŽáŠqj¹¡e V:”léñ“(a¬\sƒ+"@fïV`­ô…MðO±|“+þ‹þÙÿçá_ýŸ7ÿòðg À—1ÄV8ÚÞvþHÀ¹ÐÏ^±PÜæcZææ¹Ð š³¶¾“ý$·hôE—d½yÆ}¶d¹/ßà& Túv»1 ®&ŠÅ$㆕Ïcüª¥ê5 ¶&3yF•þص Æ7J(dŒvk¢s£EP>~L=c†‘ƹ¿½ó›4ÐÖÚçÜÒ Rc¿ÍÌt]»H]_ëíZ5„ÆyµûÆò8· ptÙxª&ù‰®*ãÇölS×ÙŸl¥B,éZeÂsf /Ùi ‡©m^£„qç¹.úš[ ´eÿä/­@2²&Ú§¾ói•;²+O={f¤söÂHïú¾ løµÔ›ÄÔ¸½bá~zû“4–ô˜@¸‘˜’÷ ‚Ÿ±ænf Ÿ>»§Oô3ÄŒ3fX–OÆZ t ôò:ËB:Ÿ€Èá\L$ Üd;Sâû·]¤†Ü0äwG„ÿõ üü‹ý7±ü7^@5ÀýJðÃ)‚ç(ñ¸_B%mõÂ>x¿ïþ8˜ ª `…'JcG€Ó-Ào¿¥Ä<A ²Ô7rð” ·»G釨DŽx®²ËNXëõ&î+ZjW‚è"7&O*«d8@T[æXÖ#*0ÎÇGUæ¯iÇ`Üu¤T)u0O2f2¦,CÚ¶Oÿ—uåþ3>ª÷óþ¾™ø{Öë§×/«˜ zÆÔ@o­à}&I10ÃÊ‚?8®oǾ6NöÓïËs®§NoÐ:~ÔûJ|^UÁŠ®kƒiDºcnMË.3ÏKb¡¥”wntg¶zíÁ3"û>`ÄJ?žÞ~më²9²ÔÑTãÚnHºeDŽñ³7x×þúàï<Á‡ï’ŽúWoüéÏ܃Ô#‚ôßûÒe“i`@w+é¢0¨æP ú˜gaÅC@´ô3Ê)S19Ñß0æópÖ Z˜l’¾ªãJ£ Ds{7—–‰–,Ÿ„bÿÐÖPSÎVã,áÊë_¸k»;’\°–š™®¿Ê*yQZiwP u“‘î#ª>4˜êmŒæöïRû—<Ž!q}›AÒcÔûúoK—hX4e‡EîÐ2ä¡_ôtçxáAðIÅŒ³8'’ñX|é-kj–qp“2ài™·=Ks>j±ÃFlqùîü»¿%ø«ïCtÌøò&YÞ}óá—o¬"ãüîÏ”A4¯¿rðë¯]\÷ïÿÎÃg‚ctüø­ ðŸþà¼ñRü“Ÿ-fÛˆÛ1»z)`p¯‚œ^‘sm´0Ïœ]Tû,Yo î®A7™Š‡„u(‹îÄÚ}wÈ™ ×(@œd½0ÕX‚íSlÌD'¹¤av˜d_øèÂì÷…O@o*çýæÀôÿöKˆÀÚ!°Ç4|s ±Ü÷×7ÉCúoî¬â>ñý}špx¾»‹@*®ÿí=E–ûý™ào~—ª…îV®ŽøÐ¼Kìø!䪊—æùú¶X&fß…û² c±ÓR‹œC±›ø!ç:SIÍ-, &:\ƒRtåzÛ_”f˜ó,Ìø*²"~¹E³m¬ïP;lF×2ñ„Ò “ƒÓ}ù> ”Eól5ÐÂØaQçA% ‚A¸Á×>Uhé뉻uïfûBießþú°ld¢uß=r¿éÎñŒ1cƌԷ“õ -¬4.Ù-Y¤j☒ •úéAËè¼À œ–3x¢N8!œ}€Ã±ráûóÃE׎·Ç0äÿìW ÏL“¿th¯!9t|x`–ÏÁóý±cÜÿ~¥È:{J únM`ùÎüÇðÚ#ˆDÖ•Xk¢C©…>ýƒ•]„"ƒ«ôš™ heÄYä @a í÷ 9ë€-«.²ì7`é’áZ×ì8G€,},5 KmÜ:$¬{ÖiNZc-}9'ªSì´q$=š”=¾b­Ÿâ:BŸêcù ¢gÌÇñdª sÐÓw*{­wŒ®«‡e¨PÜ>ìþã×omŽt†ñû+Ÿ ™"ÎnÄ4Œ*Òî ÚÈ®=>¾‚f8ô]쓇‘kZO*7þî—3÷õ÷ƒ—¾Õà#çJ¹yhÈ2 ÌîZF@ Þ™‘–í¬4„˜Ó룜…¡–¾SÚèpÐŒtÇÈö·3™9ÚÈTê'™Ýºcì°¡ÆÊŠš~æÖ/[ÍóˆÍÉÃXcߺZäm×ïÿºÇ—Q‹}‚è3fLæ‡L´õœYžÁ`X²XÙI3‰·‹0ºp9Ç.ù=®Qrqs¸4qÙÌÄ6E {t Xÿ?ÿÓÃ?þÆÁ¼sQâñß¾ ð×?„š¿»úæªWJ€ùìÞEÙ†‰}þö>É;Ž™Ìýáœ,ò|A~:”K’åƒ[ÍûœcSàYúT™ 8—-n¯(Y‚Õ@K®°ÑûíÇà‡p¯?˜b°µ-#ÕXM^Ÿ3¯kdÑ>«q5¡‘PÉ÷?½ÀåÆÁ¬1åÜMc°»+M2ˆÂTƒ>•ÉzDϘ±½2Ÿu³°ýÐažívc?iÕ7îgî÷µuÒï3ÏØdžGÌšÕ(Ú<֠ѳc÷”±æ¹y=¬GÓXùB+ù*GieÆêž‰.8;N›š@xv:8¿…•îa]V8e`»’”GÄK¦HúžÆòÞ_ÝÈ;’ÔcA¢$9ûT!àý âv\’žˆ«#fÀ«Xçx‹É²ÎÉñJ{%Óã\?ÖÛ:üœUÎãNõA>;){îK¦Ê8 V—N’‹B.–Ñ'l‹v]ÌÂDçìüú•–šó¢úCô& ­Õ¾/mÍ.=ʧyc9qR¹xK ¬G’ÑõÏoeFiÙu¿PM|UÇmꥷú6ãv_èé=AôŒ3^âS·‘\C1pÈÙQig$ËbK³ ˯mÙœ/ÀÆmv¦8F¯i¿œcH`šAîÁ¥òÞ ¦‰ˆû><èœãdÀ÷'‚5³Åi[~i'Oò„›.ÙxX áì)zP;Œ:ê§Wbýsj[Ò Úi÷ ­…nZÕÙ}1·rYM†"kÛ5fŸñÕ=ôX 5^»}å>5 a>5#­ú(åÇ…‰æ¾X¾î %…ˆù£{~YŸîžKSßÏÉ:­`Y°§ ÇÑ3f ™ËA}ßgÙn›FÒ…:½¬ííÇaÏ ´Ñ¥$´´‰}_hÀmÌ >ÖYÆi7mÏGµ‚ <¯UðÌM¢5Púó:Ëë­€Š©Š¢/vkÊÇ»¸îÞÝ%ë»5Ë9"ƒ¤aþ!Àß ²²‰õ„"Ç@ñPsD€€pˆh9ÙÜC”ˆ'‹T-Œãèú؉ ³bœ—Îø"™¬Ú2Ð8[·Š~Idëv \ †ß— X+, µe¦m–sZ;­˜iÍPkYYfZ4Ñ–)4Ü<Ô2:y=%ü°KÇ™ûʽƒû:*ÂF—´lû9O¦ ‡>à¤Ö÷×ð{»d„Õ¤@}Ÿ¨ÇD÷`ôr\ òÞý¡ÃÈãã]0'[3AôŒ3vÈ.^j å?ÀOÇuŽX‰çìÂq·&û»›%ÙÑñYW_g¶¥;åÅD–ŠP*Þrïùm€^‰âyÒù@gWz8Ûfåv}Pcì¶Ñ|¤¬} Ußn’&³±Ø>M-– ÐUÚú‘Õý“€é´LF–AÜØ©D–l¿.Y üÍYúY˜éçÖ PåÇYƒ¼À ?㦌• ’ï ™x:ã“Ñ3fŒ´Ç ´u§Ø¢m”ŒCa­}Öçݤy–þ˜aåè¼Ëøø– vºíà¹_Ùk»¦[oØß¢@Öà9h¦Ï>j%T¬Ù¡&bÉæt‘'î X|fB 8—hww·BÔ)»\Ü!B Ñ-x{@8.¦J@9Zä¹d?êTZA ½œ<Åc‡ì]}öXé˜Ó²€èƒaŠ9KÕ׬¦¶«ã&ëe]“n²Œ–¡l€chnÑm¨xØvï‘ûNÖ_ÙºvÈ~Ëi ¥–ÌãR¸ÅÈ<ºçíF¾Í¨³T6\ø¾èEõ퓱‘Oô>&šp vm¿âñ÷£eþ©¦¡çe-¿¨¼oçÕ{ãÆ!?Æå˜…Ö:È#¨ê= ‡¼þ§eDìU]2Û™õ¯\,=AôŒ3èÕLP•1 ²©ãÌ ¤Õy™§‹Špég§=};:Va©×pŽZåÕ ø=:‡Á-®.k¤ P´Ð,ß(¤´KH’Ã%L¬µ§‡FY"òc¶É{K€gÆÛGP­,ì¤j 2õÏœaÑ×`[õÍöðk·™ågCé ¡i+¬6>Ž5F:d0¶¼Ù© ~¨]Üú6áø¡CǺŒ +åÁɾYù )cÑç¢gLÀFÛ´Ínà¾ýãØÙöëãÎ8Òòö\BÆnâÛuãki#Mûý¾­ÄBeµ^3Ûzûxš›õSµ ié( ÇËe±UÁª_¤ë‘¥%p†SÄB QÌQÚIÏ|‚ó’ªbB‚ë‰ò„@€´=föœ­ïÖT11Ê9~<Å-QÊ')‡§‡&Öv ¤Cýq߬.¶b´±Ü¶ÅÆI¦­•ìF6^ÚBƒ0·aEÇßݵ‹DÂL—±Ü\3ÒÉ]mu)C‚`´¹²LÊîΰµ–‘¶Ëò:Hi¥T<¼î¶ºõéI²f¶…q6˼½*æSDXì“ ª}é¸3÷Ý~ª ¢h´ë×ÛŸ•_´ÑÞð¸„c ¯®1Þ Ç òàûð‰Œ2ÂçDϘ1Ùç½³ÀûÿìíãÑ à™3iЬúàZ§T]³òŸ–íÖ±Ÿ®pHiŸˆD^ðà¼ýVSØw%‚@8z€³#À „x2a Ô(¤²Ý’¶ùƒpÈRŽÄ88_²'8û4¶†¢³W5ãᥤÕ?ïØ4“£Kt7Ùç¸MË,ÛÎ’ÀZ;-jÌ´ÒF‰‚a6eœo>ß/6ÀꊌmVw¬%ßH&(à«0o—ý·î)Òh«ÿ³HÓž/´sÎÇ ¢gÌp^õW¥‰6äžFz»4úº›Çr–õæx–ÉÞïõ†]í¯uß8 *áÆ ^f|ÃD>ê¯s_ûЉnºk¨×Õsß° ¹â³KfB’0Vž¶˜FËìEE€dô„ŒÙ2´°"DI}ÊS±NZΗúŸSÌ@#xŠƒé8”@òÝšò)‚h®’x鯗¢í]ú!࣋ˆ4ÒÍrI´Aµe«å:G™iÈ@ow@}<’T®G×ÐLc]MƒJ«äêÚi“ó}ʇœi)ëär¥L‡J+b²åéèD躠p÷ŸÆœÊÁ§×äÖ”ÃZü­Hz÷ä€Úåà€e{ʰÍDËýPûã`"ãhކ²¤ã ¨Ïkíî°ò®¢UAÙœÒbçs-) #}SÇñÒi–Õ ïã~æ¹8þtçØ3fÌxýúO ²­L@å6HGÒ¹ ¡\¯ÏËÉhw¯©$B¥¥b¹džÊc¸œi­°ØÉñδB‚ÁÖy @D…|#0>ºk§×AÜva¨× Lôýš˜èû¸œ@º÷Žž×7É¢ÍßrYníÌa4Ít¨k ûi„ñJúñõ3ÑDcd\%h+‹¶h,Óð›¶n#Räк` Êd;ZR†%ƒl~ Z^±"´Êý+ÿkóã¦c‡ zgb%'šèØØ•ß1n³³ˆÈ}ìTË?¥ÞÑ3fŒ+‚-ë†Ê„f½„ÕH7Ü6¼®(ÖØ_e¨3O[],@1ÐÇÒ‡U3)ÆÉ`|ΣíšÚé=nªb 4ÉB—jœ–iH_öp¡+¶Yÿaì}®2PÈ@Ø•Ò Ê¨$#æå r Ö¢“v‡«Ï] 3ÜÁ9ŽO 7 ^Zróp¬ìØû9HåÁÌ6Ü% }i)Óz¼žDHKìG}~{i·]–òvÂ)Î2ÎÒ·  èwüki+n¨2Õ£í±(¦Y.\ë U_+Mœ—þ§pÌùN˜éRnAkΦâ¡r$ ­'>Ü7€8j¦Öþ½º‚‘Ι°!û¡kH ¬÷ÃÒ¬œgaÀIV.G ͶëËÐÈj¢mÈ}2¾ÑA1ÉÂHWej²N—A¶dqí¨ÅùÍÝòóOv.àf·±KÇÑ3f̘æó0Nm´¯3Ó–i–1hN^Ô@N³«mK@Z4ˆ«± ˜®€ÂJ£0q1|aÑ–ÌŸ“u™¼~,ýΑe!c݇L˜Ë…s[i=E½slçÌDßgð¼®.±Ìë±Ð1GÐ|~“šÏÒ±âSNš¡.™wÕ kÍÛn¥õfXí¹ô¯¼£IÀ™­Ž§þ>ˆ@ÒÌ´–o•ltÁDÛÄPÁH{%Ý(&ìú›ß±Eªèeñ#Ô?ž­ ‰·ï‘ãrà¨î“ ÅâëuöqÍäà‘ÆßFR²/´Ë.Û˜1Aôáþy|•pçþ˺ó}ÝÁ®xó=ì 2Zà‘æyàŽA#¿é– Ç@{Ýu ‘@TÓÛy½}ƒ‰j|é»°ĺ°‘¦þþØÚ>4˜è=Ö@·& öÙÊ1,X¶ç†ÇêãxΖI—Ì=”Õ¥øJ B]ªjÈžÒR2œbŸ0Àé’ѧɅ$ØÌ qåÁ•.-ºnˆÿóÉCì̲’]8i1Hî/Qn%ˆŽ¯3õqɯ?ä¾gŸZ[¼,TãW¾ ¿^•õ¸?êñmL´‘»@SDºÅïÜ‚¡àÆ®?ruÏò ”ûÔ®pŠû)ãêo§ï2!×G;Õ´žèhÇ—rñÊŸº"×þøCãþiÿñ%g¥‰^”ˈ΄}w Zº÷YçÄDËñ]_rn|.©õƒF$ÆÍ†Ì}’׿Î×ïÿþœ¯}•°è± (­Æ}¼?ï7Aôž˜1c†µ)2“W^A h´½ „4ÐÖºN˜çnÑ^g '8•GåHÊkPE^Årf›—5eçs[“™[®XGO'¸£ˆ-í`Ée‰íe9ƒg5i0¹‰ˆ k¢obSwöѸd^î[ +ã3¶k¢‘Ô癌&Úºm€WŸUn¨¤+FÆ «ê…2«}ok2¥±×ÛRŸèëêrÏò^8·%–¹µF­üÀ’ ÔkÇIe Ñœ{ÿìáÇìôNMôd¡'ˆ~ þü3úþÍ. ˜fÎa󬲭f··³ƒ•ñ‘PH3½J_c· ûšvh²»ÌòÎ~¨ÙèX5v[k/[³ôÕvÐ#¸ž¨°è‹~”Ç,˜–ésÙµÃ#` ½æ q.fín£Fš(1ÒKHÇ ”Hë5PÑ! €aј K)Gn  …±¶úWÉ×LóA²a·ÞWéÚçíÑ“”lÜû  U&ή±¿…’…ÙF3k¤—žå{Í7hŸs!5‚œíßµ@¤•AY©•ÐòD²ÐPÑFZi÷Ýë$>Öš.]V¸šIï~/Èñíý¶û¸ºæ”u` Õw×ÁÝ`* ³W•ØqT>–ë°¾‘×7®' dzã¯Íz‚è3¦6R}Q¾’°àØŽÙõA˜]Y×ö•&Ë~YG Åd‘f 5K¤µÔüX–YaÄ]`-¨ÊeýóÂÚâSÏúõYY‚{X#íø‰1 듊m–Ì@&5Ѳe“Õuäå²§´Ñ¶øÅ¯+gã#m½‹L5„ªC„0¹8}ïfe""jHv°È¥V™ÇƒÚžÊý PŸ›ËÊ‚U·V§Ž”<¶?·Ô“eZÿøñ×2jë;Û³Á…¶uñ–O6&ˆž1iÈ0ÛíÆ ¥oÖ[-´]?®õock(åºÑÒ@ã†SolsEAŽ.3aä ݧ†ŽX3^ ; ¶­L ¸2×ÙO‚þ:ªU¦SÚu–kï €DLZŸè”¦z`IùkPf¤DÇÆØ‚h“¤ÖS)‡f-x.µÏþ ´Ï–á“}Aúæ}JÞn³>ŽýÅ‚pTA±Î<£ñ‰Öëëï?`»‚jF“¤Ò!­9»Â½£¢vPÈ”r =gþ^­URÃÇü˜3ŠíÎcF‚çõm³¸qÈurÂÔ 2êÜv ¡û¹b/® Ki½'¯'”ý¯c½Ò"aÿÉ®«j‹%/yÅñîzõzkµæ6Æ• QËPZû56°eÚŸ…ÙÆODϘ1c¶«Z ‡ÕJk«vÑè0`€*d¼}°L|hÁ¾ûÔ@¶£âý…ÐzŸ²/Ö“‹k=KE5 N™Ç,°fÝÚÆÚ‚ *rhni(ÏØï” ØZ©´gúÂ@«>gåCMjÞ•,ó ÖŸÃz§‡œÉ²¿hžµr9GéBaÕ ª•T-„RÿÝòh˜ñæÄ?ÙÅâ2Ä>×ï* ™ÖJÓHf´Ãõ†^ ÔœLôŒ[™ËÓŒþ‘̳f®ºÏchiÒ¶h9m…²õ¦îO ¸åºýèa…BÎ;h –k–wFûÜÑ Ìp×`–œìcAµ1ä°ʘáîéÒI352抬²&ùp B‚féµEŸË}_ޱÞٲξÌKéM\´ƒb¡«×NƒgëÂa¯Å··ZÆ£Å0Ze—ž¯|1aI³Ú\ «–<®ÌH긇S}î‡sÜo¼NÞ„©’ï[Ë:ë¦t ÉFÛDûã¬Ã#)&j®7gwn1̓ϭÓLì@nÇ®7ÊjŽïzÉÉb©È‡8n˜vÔ~öÊ<ž«CàºU¹t‘ú¿t`íÙFS²!ƒl®ß¦ÀÏDϘ1cÆxâ`P} úKÆ -xP`YÔàr^îª{d´³ëE§i}gEÞA dÄ£ÙqöR&]^/2Žè_·Ì.%ˆV`¹³LÌDó9¸)gËBW¶©îc3ÇÔG×\^(Ó ÊI#¤qÉÊ}FI9€UûjŽ.Ä}Å+Y3è‚•2“K»˜‰ìßq a³Ò“Ï Õù¼­-Ð40EɤsÜ–¯ÉXFd½œsÛR¥ñ¥ÿ}N=cFßÕ +>2“öV}ø8Ó>yAÝ}ÃÕ,í‘”62û£ýö¹pHÞÎ@K_še ­Ÿ­uÞßÕóiÖó²õ”U€¦å¦Bª–CÃ7†CÞ)‰c€Ò¼}0×›Ï'Çr¤«jÀÌî¼,ˆæeë¢QöuõBvã°÷„q¶òš^A ܨ}¦§™PÍ·3Íãéý$[ ­³DÈÙ‘0’*º¾Ú‹a\9®ûšöaæQtÇàYrãÉ‘w¡1A5,Í'Ir¯ (ü¨;· TeÙWþ6êZ ÊR¯eÓ4Ö 'ý|<ê1ÒÚÝCr)¡—©”Ìt,F}ÜÞÔÃð!çcßOÚJê¶=5>Ü>'=cÆŒ]9G«Ò`£¬ºbŒ,@÷±-ó”œfÆ»…m¬–d2R©Nű ë/ v—c )0‘f\g5nAt º×Ï2Ñ£ a“ÕëT_¾Ç»€6fd{îJ’Uû¡‚îú3Ô„Cýc ˆV whxš7d<¸´e'rœDK#'»ÑÒ–ƒ`—ŠÝù}²¥`^ƲÊiSRñ·Ú÷΄\Rã”Òë ¢g±äã „Î?Qí«LôØýu_¸M³•…1° ÂOË@cu|œqݯäÖÔ>Ûqn+æ´¦’s³bÙ¸X ‰EX-ÐiÀh¥)¤D`"ÃCÎÚ«si` t½¶HPl´x7ǾqëX2Xé0ÐD×µÏáP¹¾ƒ7’_ivó¡þúú†c—}¡åAP—³Œ´ {ÝÖ†ßô²>މ&µ°¨Ê›XnOJÚ‘³Ó’ÉÕÝt¬¶¾._ãÆ\ˆÁS¦àZ–súõÕ?ÿë¹;@˜vÊlô¡˜ Q8 `>ï²×±þ=*ëI@·8íoS‘ÔùûBñ®åT-7n™ð‘Ö®¤ÆáøLO=cÆ$}ñ¥ê,[wÊ• ©:°6iÜ,˜Sò‚f1Y/3æÕÅ•n÷Ÿµé`Ë—l-Ê#†lëâ~4œhTEŠ$ë嘮ÃLGoŸ“0ùNõ±q}9›å);¿P_M ò Äu¡Ut…s½X v˜h+—ymÑÜ]*˜Í±“j¿~a)e’!·ÀºiŸ†(H¡Î wË<µÒ&-º„ÙæÕÄÑ3f8ßÐBëqßrÛùF›<ˆ#†ýr±Ôò¶û®ƒžÃ.n¿Q&n9ÕÜ)ãAQ1:éžÃV"3ÐjR[è—¯%É-æÚºJhù¨‰Q®,ŠPÈ* köËÚÓ ,²e¢•^YþÁç&cx[fñ[Úr Ð {5 ŒÁ€Æ'Ö ÅQQŒmÇÃÑî¸ É£‡Cíµ«kºƒZFŒÞHÐtІ-8ÈÑqí›F9/uå:ë–¦«‡ŒwîOXzß÷cYúr¢²ü8 ÇB:%ÞÔbwçr „‹ÿ‡ú:í]£Ô“ 3e™H¤ÎåB_+Ô¸Ž}æÙŽ|§õéeýK‰ ¢‰`̘¯S$´ß»¾§q¶ ÔZݰÕ?ãкÎú;Ý¯Ë PVA²²>Ýô*4sdúâÙK™]&ûϨ´‘s» ¬5ñ1 ©_¶%¹ÉÈûÝ´ ´cmyçé =–Y½†°@$Î#M¨ªZÿ÷"Ÿ²RqüøÃ†óQŸ0 ÎS0q,)‹¨ä¿Ó<î3˜ö³€ó›–}ÙêõV3Õ}Áqø1›AôÊAôŒn½Öè¡ßéÎAwˆÖþ#pŒ[ZWwß0ò2A>•yîjž5ãÔÒöµî›eÆZÝV…B=1©Íì6ƒr˜c`hÑ“–.÷EÇa¢¸[Ó`â’«Í €nhët Øè’¥vahM'ãÆ—Ç;nýþX“‹OüÀR¥*QŘÁFj“»aß“1h¥©y=¯»ó}I¦Ô€-ˆ³~Ò5¯ùó©åMÈ™?Ïb@jò1Tº uÆV¢¢Ý1åPºëðß«cøœy¾ÃA^ƒ÷ªyP÷—Ò";^·¦¼dM6¬9ûzÉp¤† IU2$c.äEaÈÓ¸º.´Ü9ö±94åŸj̘1=iQXh«unUâ“Elu<;&­ÊLÁÝXÃ"Û ËS+»-Å@£à%ÃDœ•¬“S„õ”#½¨\¬ß¶#P`„×éÜ‘=¼  É3vh¢©’15é È&Ôsƒk¥û~ªÛZZ9Å¡WTiü”„Bk’³a¾­¬‰=Ý•N”i(æP½Uþ8÷ØŠ”¹ ©ÆÛ˜å1Q$Ë=Rî(M7¹>¯%'ˆþhqóc}ýî/¥]q¼ƒ]qû슷ßíþ˜¶÷³Ù7dwÄD>Ê™P€fÄ'È*^ר}î©èô7‰§µÔû„Í©'ˆ&ø‰bFŸIÞï_<ôQî@À¡îMÎ0™Ý ýàØ†«ûÑËHk®™g›-ÆjUÂ36v]wŽAkÏò r¶¯õÓ_§µ†öq§¼ÏZ•5*Zá0{•CÊ@éÜÞË1‘ø:HA™LX¿¾*,tÕæ«ë.³]Ó,㻘' šæ8Ž Ò0ãˆkŽ\„œrñXúOêB×^˜iri[§À3&³öž´eî^fð,vÊ¥*вô˜<¬w9÷P“|ž@ó˜Ñ몠DÓ~% ÷T)º¤Ÿ@'0Í¡ª}*×Wõ=·ò¸s!{CMªÕä“ëþ¨“û¥´Úœy{Šs¡®èj´±Â¡êï š zÆŒ}pA‡úBªá$+A û(Ïê óÜÔ<¿ðPÖ\¨lñ¨ ¦¹¾Æ¢/ H&sä V®ÈM_Ç‘õYg™¶Ó®ôZK^Í`öYrlJÿ\ÓÆ=ÂøÃ76GL ;¸D,×jÛ(6ƒ 9‰ó»-ÄGˆO›Bð!k¢={äP! \D/¹rÆb]YØgB1tÀ‰ORéå~%¿p ²]Ê%ð^ÀW7¿|ëàë[Œã>Ü­?ž î"Á¬ÄVDà#€N8­$­ ¬×Â?ÚKA% –ÝŸŒ+À*m)¦´Ï¢Sæ°•leG^3É–Äèq3&ˆžñ‘´ƒ†É”ú=˜ê¡V³;‹^S×?áëVñ#gj0UTÉôØ I©‰×p‹iÖ 5ç±°ežU^y¿¨¯†!ì?é@ͺq8RILȬÀãÅ#]”œ‚]¿/â릮«r×Hã®Ï4û|õ8nó…–[âVw¤~E5YÀ–Sºj@çÞz ʱûÕ÷—ûså ê{– õˆdI½éX 5dÀ-ÇŽž Ä8ß°$‚Á(9Þù–@ÊøÐ„œÿøk¿ÿÎE0ý'OðÝ=Áoï~8Ñ¥Ÿ¥¶‰%ö["Q– 7«ËFIŠ¢»,¹ÞÀO”äXtSœ™i ¶©æ²$Ëâ|„ª.CJòìðUÎ|^Îö;Ž9üâ\êû{º'ˆþ´cÆŒm§‡à&]¥i°ÔJkð¡ŠQhÝ´ãõÒ –  µã6šÛ7ŒÏxUî¨&öºE&&b ‚WUy³°s$ˆYèÁgMØ\Ñ4±Ï[Vð´.ùîàíá¾tð‡ïüâ-ÂW·×Ý{‚ƒ#â)+ÁJô9£;OÈxz‹¶¼¾•´Èk­—AµŽ(gÛhú"›S„’qå0zs=Ã0ÝB¿“ѧYeüiæ:L=cƨ¢`Ç÷Ùæ±&8 |)-­²~áÙúAlõ‡ÖýÝn*ÐC5Kô?%ïP×Ùkô2òy0Ðêþ—0ÏCIjC+m‘ÚcŠT2ÂÇ'NÛ¾aÅT¥ÍÏ=¸Žáx;¤Áè„3 «È2 ëE ™¥M,±H'Û©2àãkˆ„…ŽMØžáàxþ°Â¥Ü. 4³þùÃÀ5Eš™gÕâ±C  yÙH;9 Ë„u°KZç­î{(çB\wâ!-;íâá”>;¬˜aÀëï'§A°ì/à¼ü!¤ÖÐÝð§eô¿r‚èY;ýeÄ z!œ8#xÕ!ÀxäÁë?ͳöÞ »iè©!_![iÐæÞgÑæ1Àül˜é©‰FÎÊ®2¨ hXh4’0ÉðT#q³náÒNγ|qD¸;äÊå˜æ“'ðÄvvÒ|Hãž—ƒ–r8ÒTfT@Ĺ¾ê=öi§@µ²W2Ðy' 5¸ÕÞô¤m5©™ÓÍýÎ<ûc‚è3l%,ï3Ed*riÌ¢È>£,Ìe— Ý_´„ZcÏ¥Þ®}¶ããYþPx`@³f¢Ç>Ñc¦r¤}®@Ñ–[¦Š¨(6ÍN¤*’ÒW™hó9û@}¡e¬GBlñ‡æÀmÌ3Ùýv„q >§Ö#×`œÝã\3ˆ•_Ã`ÿÛ¡b¦IYä¢ ÒÇ+:å €‘Ár•‰PÚhÎ ˜·Ü9¬OõU¹ë“ûw+D}i‘‰ÆÂoÍÐ>ˆô9@j>.³”ƒå9+&š8×*«Šc¿f;±r¦R^!`ظrxËD£’ƒ¡Ë ¸þ›®]_LÍQÃQg‰YÉEl¨á¿‘þ|ê ¢gLfi*+;°’ ê°ÔóóØÛNË+4ˆ€dÜž;3íé³®ò4jZ|–‘ ÖuôAÀŸ0 K¡v9çf'ÚÔä´\OÀ³/Ø_¿ÂÝz¾h€÷'bÝ3,NꜜCÏë%û¼|ö[Z†pP2Ž’‰vðlYé À41ÓÛ§)Õ·ƒsMûõP¤®Qh[ÆZB^«Ä+¨RHôÊAôŒËY1)štC· RrÒŒ¦ê£b¨1eË`ƒðuþïÏþøg«>hð<(G 9‡eîz"Û÷p£¬‹F×g¨¶îŠ…æ¥U”VNö1ŸouüÐû|+qÊø¦7»fŒß:Îs[åáN;vÜyWgª±ÕoÍ!ÑãAùë'/að$ ë_®Y…Wpp)» d²ÕÿR™+cP2ßrlròJÐÊÁÛ­NKÑ7Kzg/ ¹èÊÉC´¼»{h+Ä|éÇñà]r Çœ¹-©ñ²°âe–Â@3ˆNÀ‹÷ÔzâPdjg*ú¹åÓ-`Z}÷‘0ܤ騇#Ž$}~½MÄÐ`¤]Cãÿ‘&4O=cjÝóæ‰O3v€‘¾GqÇ®iXéÁ Ó¿ Ú ©Ð t#ã$+-ë¬âdmgÔ*g*K3±g¬ÿÍÚ(5V =Ömí4ÍY¶÷>mçŽë |XNp8%Œw­îŽÀ!Ë8N  WŠí~X#€¾¹´cl¤ ˜ƒhÂʤC¯æ4©‰ ü˰ýª—&’]E–‰€ŽÅî¤-óx¿ 1c‚èÛïëãÇ{ØèaWÜ|€]qܹÿ²Â¾¨Ï—À:C®=ᨠ–-ˆãñ>Ó8ö¹Þ¡ ßưãëÔ}¯ÞûÒ×ËVä³…=wKÌc÷>ßÀUÜö¿?ljöúãní3ÓHÕ{ Ëí* Êí?ÔdwÕ¸}?bû@©ÆÇÇ=¿y¤unðSßpœ°îc´i#.î1¸ÍÿÙ^W²yü¹– \h0ÙÔµA€‚4鈞Ï9£€Lç×Úk|XÊûS ö¤p…~Ùáü é@‚˜rUC¶ÀKàúÞܯ)û5í+ ú ÚÝÑRûHè“ÚVØÙòG‚T:Ä\í¥¥6š¬tK$7E#C> 貿a¤Ëû§Ž‡ƒJœÔr«ù 5ÏDϘAÕÇU×Ï]Imj%mßæç9?à€ ~ l^£è…x¾JßV,Ô. íâ&ûå4ÙèùtO²€íÜPŽ—E„¨(Ô¡À.[äq m3çeaŸ3– ÙbCØës€;¸Oç%m³8̯RM*ôA³i"éˆý¢KwŽæjåÜÖîÔ噎¸*¦¢œI$h§ õŸŒ!i麡)º{÷—ñŒÃ¬Á1cÏ®m^ep•\@Ƥ¯\ hii4?²6ŸXYžånÝ)Œ, Q± -Y32Ê• ËDbã¾9„µ“Ãín%c?s¸—4‰êK²,¢zËâëAü–{®4¾Û}¢·ƒ÷ñöôLZe€»?=ñûÔþ¨þ®ƒ°ƒ¸ I– W²â‚EJ¨,* ]YåÎúK“ø¾HÖçÄXÎAe{6³y\küO/èç„= ’( ¿ç1x¶`ÜÎ!ØÀ8ãçGCÌP`LɶÌÇIXj×™5ÕVJ ËckgˆT_&ä±ó‡€=ÞØJ¸ù‘Á$PY峜(X‘mèå¥WHJƒè¾kŽL¢LËN@tr.et |ZfZ²ÇC¤hÃätÍVO3­ý zÆ «­mhóØ›¾Ð¶‚6AU¿V©/aC¥³1³fYJ½HãÚ1c°3f ƒbz­ËIÉÌö4Ð zè2¤Ö÷Y塦ë÷ ©÷y“°.C9ɘIT ía0[¾ç0Ð6þ4Æ·1Îãû³‘ù%·)¶ æ'µÉ‘ Fw“ìÆ¾/o\\Cí®ûôI1ØÚÝ¡yýdØZÔI†&óÜ÷\‡l)‡˜›0ÒÌD+g ¾ËQ&&ʹÕö‰a–vPya&Ú0Ð6ƒfq`ZYú¹5·ôç^Ô÷K=D-9·9;­¿ûGÔÿ,¢y‚!ïÝ S-p*ß„¦¬¸uØRÛ?oL=cÆDtœèù*YNM´eb¦/ðvRh –l߯pûsýù2Ñ3PɸÈÊ·@97ƒ;µlä("ûh€hÎ Ž•Æz)µÖ ÛÕSƒËÌò*OàW@·ÓÒ§x¬˜-€»l¥ƒ ë7*¥0Â2ð†,e‘k- Z1ÿUWPZçBÏNÃ/,µ, y{:æt‰ß ¢gÌWÎÓDn\°dN]è2?6Ìùöi¿ ‡ñˆv0ÎãóÛ¾fžM¿_1ÏnßòQ®ßÇÐ` agåHÑö?g.­¡Š=ß#ljöɓ于@/¯1Ìvêjª72ÎðQ·ß_à™bà „£ûß4xâsDBÎÎ×+ʺ•ûÖ]!æÔEc]t'ÍP7míø54›0ÍÂn2]Á ³ÈkzγŸµ’«TìzöÇ=˜Ü«€tSF%ò•«2ßÁ¿#þ! c¯3Ï:KÑ”Ò×Yä2J&fT†Å÷#A9¡³ÎÂ3ù´ÿa—ŽÿŸ½+lnGŽlÐÞÍÞUåÿÿÊ|LöžEtjc\Zœñ¼1ËöêMW© € ‚’˜hi©û(-C ó]»ö§¦g³q%çÝï½&͵+¸4ìòH¹ÝD{G§8QŠÊ1^cæ\iâ¿KŠY­ôƒZ¿våâ±Q¿¤~SIc­G•o%½‚ÚœëÄ*‹¿¸µÓzw½Gp‹ öNšýŽ3~)*ˆ.à‚Ð8‰’`âvÔ'™‹0Ë,¾¸Y[—->!Ö°“ãã&– Þÿ£Ù´“£ÜTC}NîýÑÏ ¢«M¶i†¥† ¹AÇÀáƒmòÕuŒýî}àlä {.qòç;™¬ŽèZWv—.Ý®¡Ž­¿a¦µ÷^ðZi|¶"¹‚èBQ¹f·E tÀD'ÌhãϵªX'™|æÅœÁS3ç5×*rí'÷„VÝ•>óÙ<#³é:EÚuƒ5Ÿl0ð·%ç5´9#ž­9b¿ÉåÀíšfÞò€bº~‹þÏxLKNpQ&³ÝvhA»e¨»Jé–mЬ€·?ê¾<ÎA[“ ˆ¶÷QǶ5sü&÷Š3S٬Ñlô:·¹xϦ¿Æ˜@ÚÔG¿þd‚ÊIJ”v}ŸÌÐ:¾vKIcÆ>.c-Ä8‹‘îãý¨‹}VÝe3DàÆBi´92Ó¸pÌ+¶óÓç/Îpø·vë¨ ºP bI‡Ú¿…$¹É½ˆ/rŸÁû¦Å• Ô/“×’à¹ßG\K]r'§‚tÔ„2 ‚rœh£ïÑF v(o‡ôþtÆ·È £‰aí×Þí±4Pöœ>ˆýÆx‘G3‚g²ï’wñ¤ŒÅÝUðÚÕÿì–b¬>I#×0¥K’‚MˆÚk³K¨ ºP@¤­\¦tO\!ìñZ5$Zc,ïX0Ù˜–—øí}˵Ð*ÓÌx¹ï/üu@t]…ؽÀ÷óA$&4ÐÙºÒTÄ”÷r³Ðö©vaê:yò&Î;iÏŽ;å½®‰Fæþ<´ÖµÔ¹uåqÕ†Ø gÂZŒ¶Ñ·Ü90_^Ëý寶גÍhq†z¯ÒP›}‚&ßj„m½yF•»ÿÝQ™K4¸G×(aƵUôÐz‹ÛìC‘ðÞÖ  ¹ù@š^+­×•~{Q;ßÔ\ë¼A}ÈŸÿÞÓÖIwŽB¡P 7ú÷Ð6âaÎ÷¾À|¿uéÇg¢qßó+@Ú\¯ï—v9{\K®ŽÝÙPªô |úñe\M¨ —’ÓqÉÌn#aH¢$V‘ÞÐ=ËkZÚóÑÝ¥dG?ÙµŠÍ¦™žÓJÛ¤)¹¥(¶»¢‚èBU1“ìËÞr?c!óÇ̓$Zã´Ú¯äé|Ìk=r7“æ2&õhЂwÈm­ƒT|ìïg[õynkò¿îq_W¾Ó]ˆÉuÎÐÞ÷ù"+7ÓÜMvBªe3”–Z. lgwýÇ`¨•aQžÓÔþxœ‚ZD«lÜKdÕÕ~㜱¾ôw+Iwí%·ÀÙ’ðIgŽúîµÒñù«ú~TûÉу:÷øÁý×}û:È ¢ …B¥-ü`» …Î,&,c½9Ë5µï’%´A¾ÒUƒb¢C`ÈÌ{»Mó‚[·­$Nå»]q`æÁ¡uv^§ 7Û½¯´ÓLÇÇö ïb¢ e¾‹´³¹k‡¯ ¼»vqNCÉ4䋤©Ì!Ssÿ$ã`²î¹ÛjÙU}º9§æ 4˜¬ÿ{è¸>9錹Ï$+×cþ¦šûlümýðh‰Ê°ä¢þL|¥íïĬæ7úÔ#c¦ýï>6ûp¡ê[ðhŽÕ¿ú?òI~Ó£Úiõ‡\CœëøvÛ§«ÖüˆïÕl·'‘s³˜`I/$ÅèãšœL8Ê0ÓN3=ö×7•ãÊhhÝ= óhO’p ±㯮‰. ¥…fŒªo±Ó…BÁ²¿YB$ºÌ¥òx¦ImÓ÷Ã¥•Wðˆ±ûë =”Ž™,¸.ðµ•>AI"ýA¦•¶Á´Ü9À·³6ã3-fÚf:ŒÿÒviÚ“¬´«¨ ºPLô¼_±€%÷Œœ)cÒNÓŒ =aòrÚj¢÷‘”ÉH˜É°=Ñxâ}Úê=Ò´ú?‚ë D.1Ífý9©Qžº>~¿lÉuOÚ{ûË<.&&·cIa3Ÿ)’3L|ºõÏ~O"F÷í2–㽚j&Ç϶k^êÏó8Ø’vÿJî‚n<£Õ_ZgjÝ.¿šìn•š¨}·]¤sþé^,«8£ÍÖ±¢SûÉP·CÍËkÖ Ím¬Ñaê]%%s‘Ä…¯¯ÁP«<Ý-U¹_kú¡P‘Ö•¦NÕÙ ºP(Š|( œûV°,gГMµ˱Ç(å Ñ ïCDc´ËnNC­ÖÙ$H lîÎÀÔZjnpgÉ<äÞ!b¨·Qb”èê3öuf¢é™hÐýsr+*ˆ.˜0˜Î£™ú2C6Ãhû*{l°Ÿ3ÓbDÀÓ6s‚Ñ\a¢Œo³®7j/™0eü’`>×ì&ë˜0Æùºî£L™è]†ß“¯•Áð£w‹ûÏ?gøõ˜eÒïl!‰Q2×FûòÌÒ"“Z3’Nx¹@übðw¦ßççóÜÑ\ÿ…6˜hëw ú¤/ò³Vúq±Ú'wÕÍ‚0²–rÚ ý Æ#;ÌŠ¨Ì‡ØåÞÁ&­4ö³W8úØ>æÓ÷QòM6~@2™±¬J£¾k»ÉG ú:ø8At¡P( Ð[<: …úÎ{ÐÉ?Ô.9„²iû»L©1±ÿžj÷Ò„vÞ7ÕG o7NT9,ò´¨/hæfœ;¦ËmÄFCÇlÛ•{U=×ÁØ7s3 V`R„£‰™^ÿ/&ºPÈÝ Z„r1#×/åþOæESuóËÆ/f(L2&™ 5.\G·eLC°Ñvb©öûh™=·~É:n3 t¬¡íûZ@b’aÌæ=ÿý_ƒÏ%Mu|ðü1G©3½e˜ç$x Ü=°ÆHÇe·Y6·!¦4V¥Ú\¹ tn£„ÞÚïïe?OÿxõcÛ0J4ÎÒAª™S³ºmï­ü µ‡Aª9¾Þ0í¤×[ûÔ烩¿b¢ûÓkÙžÏ 5wSofüË`ž/F'Þå;¯uµù!Tg¹s¬¡P(ÆæñQ(ÊO>È0 cx‚@‡Ë“Ñ:Wì ú5G/óíJÖ24żª“‘Æ3@ÓÓjó{b ©¾)À>9tèú ê¨7ê³W](ä.SZO|’(*óiÍHÛ¬ñb†ToæOæLtî‘kÛ×¥¦¼ñ:ж»í¹On|þê†)­¿êÌÜT’íÉõßf3fÎ~¾‰w2–F"/"—´ÃœKCÎäó–ù<L~[ Cò„Ç™ê-Ëxè5ÓÌÜrü9Òhx±û©kNÃ/™'¿jÀŠ•dƵ :^ì®ÒÍœzv½]’™áÊ!¦yk’sôÁ@—ÓöÑ_¯6èÖ¥²¹f¢ß4?³¾zDí¿n]( …B¡HLdá³öv S„ß…<ñNðì5œËÜÉŸZýûhG Ù¦ÆI^“¹U¡×3oi1Ê'MwkW 5Cmõá}ÌÛÚb‰® ºPhrH˜Í»d4>6u5‘2ÒB¬ñÂþñv!ßOOîd )æ5£õI4ÑkŒô<“Æ–hOÝø¹Œšy&Èdû’_ºoçeÞÉ… ™F—æ‹;h Ö³>ôÍ3•^[3þÌ–9ò±í8Îl+6•rÃðévŒ’f{æO=øàQs¬¢k¤Å¾>ïî˜u½—%ž~oGE#àlcŸ}ìÿ5£¢Xù#bÌCÏì\ ÃØþ Œþi0™ ãܺ4Ò½m[?Fš}lß_ÛÑÕ_×{ìjß/òïëu.?˜‰*ˆŽP( …B¡ü§ïï!AªNŠùÅp½è'£ßxï¤/Q–oªDP·Œ½v’{mSç!ßèþÊ<÷¦sâ˜7ļŸ“ÊP'ç#=b¢ …n}:ÛGn ˜Y óÍÎ+cJ‘l·@Ü/ÖBçÚÜž0ݸ³#'"æ%Ñ<3÷¯NA³.œÊ8¡ùŸw_É•+Èàuf:w£ˆÙñœ`ª–ƒ³|˜ÌKwÌ|FTDûM´üɺ4šÆï¼ßòùÌh¨:Cå«ÊÐ È'X¡¾úÐ&1ÙG º½–8†L¹ŒìòaÏ=a£Í6¶äù›f0ÆèòwnmÛzì0«Î×ú(u­©$-݃C=î0È»—ŽCŒti¢ÿî( eB|×þB¡èû[¢y’ÁË9F!ŸhãhÑ69[ÒLSú4Þ:žÛ!ÑôUõ”mÑê©AÍœhtìfüƒ£‚èßþûíöýeÙ'y /¿¯ÿñ¯fL[Ô~#Ã5&ˆæ "è”1šÑvÅŒe;æÖ‡v|ÏýŒUŸ7ÿç{Í 2eÆ EZå–ŒïknàÛ„_?þú “î8wŠd?½mKèÏ Ls:>Gß§™auÃñ²jü<`~¿Cæ7aûÓ"s³&ºmq·šß>á^âû‹iì翟Dâ*±ŸƒáÎó|· ž1õû%q#òíù:ûà—rëãˆJëâѺµð“ÎØK8|ÊrµŸ×†Í®{”ŒÆg5äÐ@³ MôeÌi}߇Kw¹tðì=2#ª½ÿ×åY~Ñ6úaBÌb¢ …BÛàPà(ñ·e¢<å¾€M–¶_;ë“I“ñðZ7Üõ‘ä^ó3Ðf[°tMðµri¯­{èVÄ>Cç=‡b¢ …) h®ýämþÑàÜüjžã zóçŸ3ÁïÔVcÒÏyª_|=øØ.¯#Ìpç“›¬g[sÈ?ŸsëÆYלÁ¾+xgßçÜcÍžI?$ëζ¦ïOI‡Ä/½ïïyÀo=¸sƒ WŽ™ë¯À*θ™gÖówàpÖP7“Ô Î#[ßî×ÃßyxÌ2Ñ‚˜àØeƒP Œ’zm]cí5Õß”&myrì­_ß‘O4Æ1q¼jœÙä@‚}ÔGÙG‰&{?bÔ¥‰VÙÇáL‰b¢ï€B¡P(æ¹P¨ï%¿9ä5Òj`sŒ±êP&ÃN9`8kÑnÙèx ÀP?=¿Î’eH7­tì ì})çsγÓ(&ºPÀmšèõ f&£^Ì„"sáHQâ&Y@{Êöη˜<>¾2†Í7pšažÕ–¯¹h óúLyûú÷]àÚxÌ|vb’ïûL  šã3aZ¦š0þËý\Ç1ÛÁ~’ã²Ûqb¤u>Ò¶Ë(i˜Õ®ùí‡iï*í<|F¼ÜÙÑçã3Û Žmri»¶;¦˜†¹ïƒ , –¸Æ¤ŸƒÖ°å}÷íÚ‰ÞÓ*V aèš; †Z>Ñ*±Ká½Ý›ä ãT£Ï¡ÑHoÿ¨ z…B¡PŒt¡PF,÷ZżœÊ²Ï.­¶Ñõ»Àx ©»læF©‡‡îXì¯^4R›1rSåp­ö™jʳ$¦¶>âtè<6¥y‹Þ4ŸUT](MtÎŒ®'³fî©yøŽ-a(“vòçÚefÇᬖ÷so«2[÷E«9b¢=^‡uFzVÓ‹‰y~£Œƒ\N °xÛW}‰ ²;b‹Ï4SZ]m¢Yžs©ðÌp{2î .3¡©_l»êx{Ì´÷=a¬“ó<žƒ~Ì?‡0wé¹Û9@c £•î ²[W&ÄÓu¥ö+¦:˜6T7´æ7ö{ŽÆ¹·Qîrëè]wQ­k‡×ô{טƒDpmx¡P( …"ƒâôÜ*£dMž‰–³Ú(&zØÃ™ šg¹0Æ7ó0¢ä"g@sò ôû,baÖ q0ÒÚ§²6ʵã<Vü÷¾ ºPÀm.àÃÅäéñ”Q‰Lû¡âMjO|™3fº'îl ÊEí©mç¢UVâæá6#™îãÓÎôs;ïÚ¡úº&™“ÚîŸ2~aÆ÷>ÿym~âaê˜ôg'~þ}ƒõ¶Ì㤿5¿tšé~xæZóPy–‰>—ÛéÎÝn˜i[‡›¿0á`d“œ€j÷mšúÛ–v°kÛF{ ­fé›­ËQ~cÈSz0ÒOƒ‘åF¹v´¦ëq<ù;4×ýPAt¡P( …b¢sxwÀ¤R¿fŸðjÜ`Ÿ{³‹Ãè§G MJ£ œõÒl:f’Ãõ _®S¾Úo»–Ó@éß# 4хªËAîßÌEÆÎwÜ6¸:³ýFÿæÆÄ…#[Ïî˜Ò/€gtòë6w½Áûø!ûužc óùOjÖ?ÉGš«î(å‡Ý¾ÖªXÛ-‘ŒãÃÎôÙŒ„ Î&Þ’õܹ£Æ«ž»ÌÀίY&2ÐPgus†¹Î™éX=¾Ç©¹õ^)À±©ìcþýzYiémŒµÚ/4×JëžH7Þ§!瘺^ãÜ•ñpl?1ÿO£jÚ·ÒD? …B¡P(„À.Žq}0ÑjF”qHoA¹~ô}ž’x(Pó_%»‘ˆô³VѼ5õMœc‚„¶)s—3¦ó ô:cœ»«[ƺ¦Yšýdwf¢×Ç/.0º/k¢Õ@äL²JÑ÷mTáÆ'ÄtâCßúÛûÛFêö0Ñ—€‰6ã0ѳ™'‰ ÑJwÁªŽoXj0 Þß^WJK­ã›óBOœV¨ñ.°Ö{{le&ìCïÎmãþªio{œ©°FÅD?0 …B¡P(”&gÇ Ú WòéˆiÒqÃ3½¤úA2‰ÿƒ˜h±Ìè>k ­Oôd4ós®ñÔÜÕWðm”¬„}̵<]At¡4ÑjžÓ8O¹Iä~¬`àÇ:ɼ©”7ºu0Ùž1‰ø*Ó¬itÙÃ;óÇÏ©éuŽ`ûlªßçfü°ïá 3ÿý“íp•Éfàþ“Þñ ÆÓôgÎdçâ:"ÿyÆÇ÷Ô´¯÷QGÀˆ7ë-æÒŒÜ9žMµÑæöç© ‘yÆIzÆÛgŽ”[¡ú¶q{®oÝHD¸aÐØÖ™sƒÊ<ø)9ËÐIC²¶sâ»ÂlO¥‰. …B¡P(Ì[~~Ä„ÓIgÔï$+#Iˆ±ãDçqPA4Û"MÔ þüϵñ?þXù}eôúùƒ‹ÚÍÅÌo@®aóÚ@µ÷[}°!&"fäó:ºmŸc’°[†kŽ™fûJ7{1,tç—øJSeîÆÌnhDL¹/äóÑnv{ü¹ïÁËk /¿¯iÛ_þcá½ü±ÆT¿­1ϗ絘èÇ8Üøê{ò}e²¦ç3 ñxß7 ƒkËžôïŒnêîáËÖ£;•“óçm[i™ö@›ÏøYãÝñŽÒrK#œ«}|îà‰úeÛØôÚ÷W¼¼öß_FýéÌü{Ñß …B¡P( ¶s‚E‰r;k´ÅLvÐ4$ÝxØ ºP&õyvÑ;k<¹²=×*³I£—gnŒÇ7ÚqŸ{[¸q÷ÉyæLZÔo ¹ñ’;‡ßN¬iÄ™2ÇŸûç†Xܾø®6îŸ/8þÍOo³i¶¯ižc0¢Pç4Ô€eT' þh¦gfô̲g¢{}«ÕT÷í˜áî‘–}”LæÅË(÷s}£aà!):h’Á(›a¬yf­žû´áÝÆ~‘Üñ;8ž‹‰þŽ( …B¡P(&Úk•õ‚“8Žþf ?Uÿî¨ šÜ¾! üÞ7bòy$ 4ƒíjÏ·ûýÅoÔâ!7èå®"B䆂À6qMñíÙ¼ƒíÙuZÎÄ­ÃmÃ6κӤóøR0©¯2Ï|ÈÐÔN$ד‰»ÇìïmÝ0ÃŽI÷ßwÏPÇZëfû›²'ÚmSÆ`à®ÒÎŒCu‡ø¼¤&Dùß—˜É– .‰3cý7~6ÆØùµ.f› Ìó¿_ü_öÎ6ÉuMÎHJuÎýœGøã߬¡Ô›±×Ò;è}8îæ¯{bÜ1ã{N‰é‰[r  o¡²ÀW%U>'x@ R”*•JìôùÏJ´1ÆcŒÑém†´H툊tDˆœ f%Ú”·'˜Q)ÐKg½Jóh×/JQUªÄ_êËh:G"çx@G™ …×2Ÿ}/3/êAWy­çÊìþÊsþ7z»F{‡cúLO)…˜©Px²Ya±_è§ÎŽžé^¹Q’ƒ÷xéä97ãÝZ¼yè(Øüàyö¼ÜMµòÚôû9Ú7 ê‡a¬ÚOqo£ù/; 2k“gÐæa%ÅcŒ1ƼŸ½TëEóCÂZFS¯–#(ÆJ´™©3­ÌmúUºöâ%Gy »ËÊ€Mt<ƒC W>MË2Ž{\˜J\!¯°ö@—m 4EùÞdÎnj*Ã0‘Ë/óÕõ}ŒU)×ê9<˜C Ñ^›:޲Ü9îÚ”Ãüß'ÀÁë'ò¥Á6•£³Üx¿`ígB³õf#ìßS¦k}‘®´œdþøga)…1ÆcŒ1V¢YöÅ ™ÍA¯²P"ÁMÁ´Ú=˜êÀN{E)Ùì Ó6…N+®ëOè|¤²,”r *ÐÒ]¼Ô˜˜~¡·ÏÏ…ÇNB2†N+*Ùt ÷†Y—&C¤s¨é•B©^^+Ð+^——v}iÖ·9Óh–ŒåÚ‘˜¶BvêŸÿqyÿÃ*Êíù¬Â¿Š41¥¾îr·­DcŒ1Ƙ]â!•hãô‘G*ë /­¨§sœ›z*Ÿ˜ûCÖ>©q<yÔ;(ÔõFkïèUÀh.´ØLsÃÜ †áþÂùNžÆ•-Õ¼Pw8§Ã\ÆŽCŽ]p(OrS^ZïðÒ¤N4¦è^ÚØ ÷x>—[eµ9¡_F(¸AäY#x˜›íb)s˜/ämçÖ8|6Š7Ê 'Û9Œ1æSÊ"ÆCm/3œ0&V¢=ÑZÂè lLxOCzF(K…Z§{lË÷ÅFE™E ÒIô@Š4$H*Ðñz ¯³h'ÁÔ|hRÕËm×ã¯ìþ¸Ï™YÑ/ñ @Ú³–›F§ètNTå^‹ýÃö%š± kK)xÏ]×G%6¦"E´ðH÷ÀªÒRÄ8´žé^Îuë)¯ã­¾éý,ØmŒ1ÆÃúë>{’?'V¢ÿö_Ê›|ÿ©¤ø·,)NO%—}ëcMz™¢NBqaS/ä åxy?z=Æú±ÜWâ—¶ÿËÝ><çÍÃ÷œävüÞÙþAïÞñ×ëq¨¬áaWé)[Õp}5y/v<Ÿõ(ö÷÷s¹ÏßÈ)Ýß¿æ”èç/%Å)YÿùKν.û*ÇHÆ­,§Áö9¶}YÇÞoÏMýïo?ïÐyÿ8~«íÖöêq—S¿¨Û›ç{h/lñ#¬å`c}Ôýc HÌç_(<æ|GáFmÍXtëëz¶sg=ú<cŒ1a’¿Ç S}G¿3S™•h㯹À+˜_õÌ~ä¼Å©¯Ó*鸰§–J 8õ+}¥@35uæþÞP^8ƒc©Àû^E ¦/€¢{ºÿ9¸³éeGŒV²sz6ŠoH/9ñhˆuis«E^s£(÷¾ [ùª~(;”ûóUA*óÒA²?ƒ/ª=& `%ú±°"ýøcŒqèÄã¾çi¡‡õ=Ÿ“Y5žrZ‰ÇX‘&„’©@N‘¦ðŒ"ç¹W_åL#r’‘VòмP)MdÓ; ”fáy©qûÌ4‰üŒ…ùû Éã'êß¡’›¿Þœ<£#ËMCˆáôŒCïòÁ2;ų̀ëeê£òÊ%–£ºŸæÁ5ö«_®û/åö¼d¹ö¬ý«J-~ó£ßa;ÇM@s£M<¤)ÊcŒ•èZn²©v–É ”µX…ºœiÍ"­Dß-F+ÀÔÊWAîxã £ bPÁƒPF³'Œ„§ŒGšÒÝ <¶ô>cp|)ÚÛÛ”—;¶#Ûß}<4ÆP>‡2èù–ªØ’õàïh•[tR9Jg=DªÔ‹g¹*Ô‡×ÊòÚ(àËhùïv6ÇGÿi†ñpN´1ÆcŒ$ìtH†õs–ž·YO6VˆNÿëÒvc¼jc‰) ÎzžåA¯,¡f&óž9/&‘Ê…ÕÊ>¶]RUÇ×èœg&g$Ôçãžè­Ûq5ÏpìÆ1ÓÓŒ¤]YKu²þ¢ìÉç•8ž|e¬Mn}Ûïž'MRÎy;ùz¹„rìO\êþ£Q´×6Gºio)o×[NàŽ÷Uf“7íô+ÑÆ;’†1ÆX‰fU^ wÍržê2ˆDÉPíý±Ͳ&*ºlVOð6+/˜·ŒŽ+Ú}8GÜ_ÀÊg­@Ε–m .ö‡¦1V¤1†ÛNÉBÞó6¿8úi%Ú˜¡Zæ;#1ƒ]%ï9F:z¾kØ_JÅû 'å}v§^=\æ¶æ¤Üp^è>Æçš1Ð s=õîD³ã]örcíä;·^bÖúaÂZŽÊr'_kPºƒœa`jq4k˜I±¶U=Ñ/ÞnÄãÕÕâýœöDß @¹$ÆcŒ16­¢.p)½ÀžhcJcÝ ŽÍô©hó8ZqÖi;ÍH¦=ÊcP´«¼ÎLt\äC'Ø_e¢Ý™ÇgP¶Æúyžh{r 8i²0t<чf{£8Ou;#aPÌ_ï¿¶Štlj¦}"Ô{7_zEÜs¢ãø.5…¤+ù3> D’Ê>ØÎaŒ1ÆcØY‡S}ïÓm%úßþSy“ï?–¿ÿRRpçÜT^H €P¦²&]Püèu?¡¬ùÏëÒ¬?ÔíÂKšŠ€ÈiUoÛŸÖƒP˜E¿Ö¥sXËýõõøàF:kyKÊ騔ӹœžÄý"úÿýQAähû)Ì86”«ýü5çq=}Í÷éKîùº“×ïxÙœf¤ƒ«õss¼~¬ƒõ™Ûõcÿt Ðrz½ýðÔäH;·sÞoY÷¹®¯å¸½üéF^Ûöÿ~†ýûãthÇ¥ñ^/µýÎì~ª”Â_ÓLî+ÑÆc~ 4cŒãˆX {¢Ï#aÃÎE™âÙU࢒É¡œÑPÌD¸³eV+Ù:n;ž&á=ß/xO4vò r,%…Éó Êã3®ÌÚ*N±^¡ržK“¿ŒNºE“¾Á¥÷‘Æ;½6õ=×ýv(ž/XšP¼>naS½t¼ß*÷?þH±Ö³'zÆx¶c|ócª¯ù.?ƒZ‰Ç8ƒØäñ‹Þ]&Øè9í(Ä€2ð2ÊñÙKE*¯[ß/]%3©ü£[_0w?†MjB„¤ò qÛSl¿°g—xìœeBŽÏuámˆ"Øøz_—×»­qT^f¬½r“Ý(¸mzÙz‰›|讲­gd­”óÞù’­"ÞŒ·ÈÍ&jÖ4쉖c˜1¾×1:Þ-ŠEâS=o/zt:ÇÆŠtØO(%¨>e åçúM±,a©Îsî8“S›Ï{2;©- ðb+ 审H™ife§=NU퉞 è÷™kÎk@¼­£¦E4;žãŽ·˜â¹PgŒJ´°Bwó¢Q=Ùu;TÊIeeÝOÎ4Œ×çñŒ…ÆX%3ƶhc æü‘Lhe¹ Ÿ»½;'Ú­QÌBÄ%@$oo÷¬Ën2:^[ ,uú€÷k=Ñ^fV$SPp¥òî,¤˜Øž?µç=Ë„ëkÓÇcV{"/ž±º¶Gèç ºÍS>µÊmS>²—ëÜÈ–ìx•Cÿ¶¡fG&%Çò¶³Ò΂ª”Îæ¤•èa ¡'0ÆcŒR»G-HNç0¦¯ ‚ZQ¨ æk ÔÊ¡ø¶BÌ÷úƒ±elÿsæÓb«7e Ü·0JìÔÇze|yXTÄ€×Yz†û3v"x¤;åÃëvÖc£ì®Íñ–F™f<¨TwÞ{ÃÚ~£tWÐñl·Šøé¥-²IY^–XCÕÚ±±ÃB£³2·±Ü-ÆcŒŽ"íø¤ÙTà¹2.ã÷®¶ h ä\¡àÍaV¢Í|¯,:ŠôGdv”g•ƒ‹¾·™‹ðB?šÅ+IFLv<• ­Áäá \¢¤•éëyˆ}¾hê©xôP÷Öw<Ó­R{:vÞ÷ÖÆ‹Ü¦X´å&½k³äeï,"¿¿ãñƹ/<œë®u6ÅRª"MÔUôd+ÆÜAŒØÛ-Œ1Æø[Y¢™ Jº¾ð”‡ˆADýÁa7™ƒöD³mF­ø_ŠæÇ”MÆc¶ñ>Õã5”Îa/t&|$ÿc–¹pV{˜£ÉþCô39C!E³æºÂ?Ê3­ï[\à7;QŽÇEh7îg@¬é5¤Z5û­o+Å8EO2j¾t…2Ä$z´+1u„o+åËs)`õ?¯çml¾5&«R½ï}g%ÚØˆ*B™ƒ1Æ{“÷îƒúA¤ŽÌÛÕ~ÍØa¬Dß%ß~.orz*)NÇûÎû%J ˆÜNZ¨ö^=Šå[ŠòÒY*OôÒQ q%o*ësãP/GÛoê6×±-7ãAW(§+ežô»¬kN‘^GulSz9ð\É1ÿý§°}¨ÿß¿æ¤Îç/9iôtÌÕ_—’‚‡’ƒ¹ç-˜i?_YÅþMÉþâù¦Æ§}.Aä!‹¢öù·^o_OMù”ÜWåÓóÛÛ ›ñfÇ#M1Îçñæy/§ºþïûq8/×cÝëK<+㬠5ÖZ.<÷Ÿçö=cáí`¬X3Lª2kªhcŒ1ƹ†Ä«5 M@G °m Þ—>Ù®¹žb®ýAsѤp,½õM»ÊûÌVDV ãàu¡PF8&¨3éÕÝŸÜýCÑ^: É~£\ÃA4ôþwUºÆSŸaVB®l§Q¬›eáÛ3 ¢)—ƒP’Ãûæø7 Q᎞èJT¨—2Xû´–óÕ#]pþ·žËV¢wÀQÿ†!'(Ðx»4ÆcŒiÄ©¥ŠTDUŸ êÿ‰&jV¢‘1±qCO9HMS8š\èÞaý 7œÛQä¼™ZÁæ˜"ªk׳C%”Íé9Ñ–mòžè»Î…ÖÍ1Ù>S‰ó!4Jòz.‡ùÁOµŸµ\‘žhÖe%ngÛÎéýîÓyÿJYÏ*ôŠóé£*Í/ŠtS^KYqw §v4þ+t;f`Œ1ÆX‰ŽË?@#n¡nX‰6&z›µbÅdüúžfV[GL£@ÏÛVË»À[ P?(&”ãO.ÒÃ(ƒVçñæ½ÐÚ‹}OžhClË^ñCÏ‹˜û,ê³UЇ~ûD¿Ùl?¼í¥.AÉýV©&MyA§žŸò\ –³'µNáëd²ú¢³ÛÒJô~ÃúbmˆI,ÂcŒ…Š+šñ¼\Jáú²,ˆ‘±Xë~X­DÓ1© q?­`“uZ/4:Js§ Þ£Ò¦Ç ³½Ð e1cám#oÅÌŒ¹9ïØy =bB:CÜ¥gX£S*ôåÓb}$ïæ¾!ÓëÇ>Båä£ãqn²ØSÆ›aX…ò¾¬*×ZäeSx¢C:GSÆë —šÌQýÙµ|VœëXçýV¢wĘm¤÷ÕcŒ1`Ñx&ÆèƒnþªNž-<ÿ¿¬V¢Ñ H³>©LƶÐ_G„íaÛ¹m ·æ…¦œÙK¢¯wŠíùfîßþ)LçB‹òLŒAÒû,”^4롼ÏèYßgšt°“"÷ït@x¿Ù9_7»6õ€¦ÞéÅ}ây^+ÒåôÒbŸ +Ñ7Y> †è(â!ÅrQŒ1ÆóVDìòÚ#ÖÍË)Ѥ•èÇÇŠôeúÃ[y¡aw9nùÒ ³> òxê€z¸çä³ì¬Œ—œç°h‘Nìî‘ô0sÏyò `c;Hžš J±.WȎР鉮ot,GezY›ò©ãÑî§x±<¿¬\¤í 1ÆcŒ©Óœ//KÚÎaLTv!ó s9»ìU[”çù%­ñ™É=Ny_qÆh.tÖ[ŽÉák{Þ‘õ( ÚKärïƒ1H†àml*¨ò.¢Mº…(WØÙ, gŠ¥ð”£)7K®ïø¡­÷ürRKµff `;ÇccŒ1ÆcªWšÎ‰¾;NÇî× )¸ì›SJì×íEÕ뱈¿hÏÛ©ò9—ºþ½¹ô”ðX/¯$jqŠã@Dþ‰j\Ê€É<²†¶š²è¡Ç¿®õÁ×£ñøq«ÒzÌ)·§ãˆRûùí眒üíÇœò÷ü5q|yþšõ«O$¿\9m‡¡~Êë‹5gú^Nêü朞9T·§Ñõy8/{ý]zã­Ë:Z˜ïå¹Q–Ïååôºþéé¼þûY~=—ßÎËïuÚµ'ÚcŒ1Æ£”hc´w5äunlO)˜lÊè쇋)ë ²3 æÌÎ….£(/û„æL¾4Bý˜œ®‚r/£=Èõck„œhU_x¢{ýžáF§Ô߬áÂa.J÷¿ùéÏQynûß||X¬DïŒ1Æøó¢1Æp«`¬De/ŒVVóû Ï,/•â€á”ƒ}`,çÑ©H¤èœï¼÷=%GAáJæ(—, õîñây%,Ïã¦ìŸ3½ÿó 룈õ †R¨pÙß(‘cÛA±]¤-Íz”ŽÒŒZ¡RhÂJôE0ÆcŒ1ÆJ´1á2+P%cíA48 ç8 ¶¶Çv)r /ì…æÐz ±‹':&bwð ‹1QáŒpðylÞ¹ÎÖǹÐ}ôPSy¦s9þ$H(ÊxL¥%¢gÔí`jFLwá˜ô«ó£)ò¸õý•ùqÊ+lº'Ì ˜s=â|0Ï3CõùýÑ3Úêú”iB ïtFªeòm¢9s_2·¥½t”“x_µ'úÓaŒa©cŒ1d²mæÿ*™Ù¼èaϪJõýNÎyÖûç¡hŸ±Œ°>”5Èz¥óÞè¼·9ï)&D?„¤¥…´G˜9¹ñè½s£™ÌmFâù‘ó<'sñ—·ßWÑ›)¸éÿÁvŽÛ,Ÿ cœ½L‹çÆc¬Dß-F{Ô ÷`‚’ü¨ ´8N£(÷·…ýÒ^h½>Îfgÿ}_>ye;çÁÔíììæ…Ç“å¡ñûÏœ”© R–j]_Ÿ¢¼Ñ# ÑŸv¦àõ¼D£@/q¼{¢¥N^_Êõ=fŒ1ÆJ´òä\OЙ鸾:D¥  ³^(Ÿ{Þ†Ò¶,”Y±Ÿ ã¤rˆs%"•çsíç1AéÔ»éíã¹ÔJ1³e¡t÷ n_o×éìõ‡bæàåõúU¥_=Oÿüg%ÚcèpcŒ1V¢oL \÷U‰û®ªõ#3àõ¡T,{ž®NY(µBÐÊUnöe<Òð¤g¼‚ìÌ<¹=†My¸ùÖu9…uCÞ_.#õby]¶œwåûWQ_xÈŸ¿æ<ÐÏ_râS²þzÌ=ŸØŽ?®üü\J&Ã)Ö\{Ë)×_Šô†óœy™ß.ù } øüCÖ«Íp}Åøªû!Wþ Q¤{©MXÎãt./çñ±¼ð7{¢G0ÆÛ Œ1ÆX‰6†3¿)%¹”¥o^vF¯—OåÊ´ø¦T6ˆ|¸’טö(³×ýÍäƒé6|î°Y–€ðr§ƒ§5yS5;e”ŽGz=/–Œöa%ÚcŒ1Æ+ѯ2 (²3ÞØå½Ë+бžF§p )Ïd=¥×È Å~‚q>‡x²§7¹£ûcïñÚ{ü…5©42«\NÝ?ŽD‡ÉÏ=&Æ/s}ù¾—Ù\væÒ8 U97~eç!éIX‰6ÆÛŒ1ÆX‰6¦“–‘Vиm&1B9Q_ogúF»íúL×ß=Ò#¸müÀÿàuÒ68ðúÊÌÈQ]“÷‡è;õפwñº³mŒ1ÆcŒ•hc¯“¹Ál6÷¶ãÂýâàxpTyÞT®p'Ï-7Ž?ÇúQM{¢çÀk̰gŒÎ‘Ÿ 1ø|Ä'K³Â}ÍóÀÒ”;yÞ=ïtÁͶ•h²ÌÀcŒ1Æ+ÑXç(9`IÁ‡NÛ˜¯@CüŠ{*yZÃÌÄQº"—¤R1˜ŸÌ +¬<åúɃná\aûPnöó—ÜøžžD=Y?wÖ£:_U?yü%éGIÁ%­ §XNb¼Åñ—5ùüÅeÞ)V@¼>÷b=dϰ>³ÛÇ>'a Û»ýŒ÷C,/Ðâ§•hcŒ1Æc¬D{泎3”‡Xx¸ į±M9 PNòžéñ¡¯ "÷›‘<í™3Mˆö˜ôhs¬,ªK tÔU¼N1ìÝ{¢­DcŒ1Æc%(æFfgèÓÊj-÷¼vlÊB±A³#GgJœ¥@ïs§¼nó¢ º‘>¢WZì¡ãê륋í‹qNt|Û÷ ÎRÚç¼1ðý^Þä”<Ååi_ Ë®L=P)Mn%›%D®n­¿íz­ÇœG<Î88’ ]áÖü]ÌÌiÕû­‡D^pÿ€Ìq†PÐU쩾þ‡ê³SŸ"GšâüDÿ”õEÿ9iŽKS¾¶N´^c¦J}პ\ðýîCÔon;o›“s¢×àEƒÂ5¯¸Þòøª>F¢µ^X‰&­Dï€qB‡1Æ+ÑÆØmnQ  ÜG+¬Za©pHaŒð¢9¬ãÊ@"ãÆÈM!1®ª¾ð„q…îÎ|ôùzüŒ ÈT •›<ÛÃ*^Ÿ‘CŸ £àXê”qN´ÅOcŒ_±ÆX6Æï&V¢ÍœÜä“ÚE'ýÇìóN)ÐQ í”q¡Žrtœxe¹æ(”ï[Uì‘ót:*úsÍö‰]e$ŸS”õ‘ñëçgn-’ëÐzÑÏÉ÷£®Ÿkã;J*wZ×O_?ùþó ØmŒ1ÆÜ…Ôg îS‰6F+ËZ¡û©ú½zx?gS4ú‘žÁƒ©$œªÌëúœ«CäLxÝøYNªÇÄdj»1Z)TŠtm€ÂSœ@óqPy%7kn¬DcŒ1Æ3S‰6&¥E¹…ãý ÕO{rÇÚ'&ÕEÝ>ûÏìxP¤½P4—¿?vN— 盜!rLkºYÏóØû;ãÄ¥i¨–í‰ÎcŒ1Æ»N±헸ъ«N­ÐŠ®Îùï’Êzçɤ'Z)øyz&Z¹âû )¯_äÒL(ë«öÞòì8‘Ýnçë:RÏäê³×<&˜zu¾³nÏèt–EÛí”O粚´å~ÔCMáÕh Çde”Sãç9>òï}1´oxŒñó&÷‡®1$­Dß%F+ÂÈæ(+å’Ám#ôG¢ó5˜e­dVÁÌýºžPf'ÀI9³,Š„'z†ô…túÇ]c)ù6L¸D¾½ùhO?8yø±¯Ÿ¸°Ãw™y"V¢1În5Æclç0¬K‹¬ëaÀëêéÝâŽLzežê ”`!×)1„ȵƖëZË”Þèœ':ÿŠžhαfB3Fyœãæmu›R@q:‡1Æj´1Æàmœ1V¢ÁºÑë,ìDž4›rO¤šÉP ”ytª~â23R6)^ZÈÂãx¢óíVxiO4®¬têûëšàÊõyÓi=ƒ¡~£Ó%ì3 HÈ)ùƒždfÒR4|}bt|Ö½_¶s<Æe“™1Æc%ÚØ=®H ѽœ]‘SJô¼¥B‡Û†â|9âÁÖ •ÿlšÇ½x¢' ÀtN´™®LÇzLxªñŒ…ûbŒ1Æc pçJ´1X…¢, tÉ0_©¯äÎdÂŽÑ_¯û¯<Ë^9Ñ„§8®óúqÈË<ßÍ\N«n×õ$%’Û×»’ÄŽÎT ±=…NƤÉ©æò0¯ûÜEª?ZÙã+·ÏUuz œÎaŒ1ÆcŒ¹ m#ÆcŒ1æOúSå·ß~{4%Úló¹´.9ÅŽ â,Wc)%eéí>ÙÌÇf¯]Œ{ˆu?Çë÷û®ýR×7— ÍA¥2ë)NÖŸï‘æTO-nÜSͳ{˜TÊ5L攣d`¶ªwÙ´”áýí‰6ÆcŒ1Æ$•hcDê‚RÒÖÜLD¨—ãf•éD*Æ O´ð*'è![•^uŠrÑ/9¾ÆäSžï'Ø+ÑÆcŒ1ÆX‰6†åèUF_Qã’IéÐÊ#˜>¿©°$ÁÎç‡í!”7yŠÉ1¯p%çUæVÏ?.y+é,‰í± }CïëYž\wó®Ò ù'ö?ŸÂáÑG:':D{ú|!î×lï™È­×X‰6ÆcŒ1ÆJ´1ÊûÜ,¥Â¸d?¶"Ýbºë¢<ÐŽ`NÞ0&¤{L&^ïüþHçç¼àµ|[cŒ±mŒ1ÆcŒ•èÿúÏåMßJŠß))þöŸKŠýo%Åÿý‡’b9 %Z(—Çäøý?BøRÞÿ*êQN\9·àï¿æ”ûo?•ß~Î?çí}þ*î³DúEÞ[­ëóóhžsÄõ9呿éIè.¹gN÷$3¹ÿrùLÔŸ '÷‡éf$û¿LO2Õþ"‡œ#ûçó_@+Ñ{bŒ1Æc H‰6öD«²¨/…rÁZ‰“éHäLQÓÒ6“‡Ož›ŒáýŒÁfe˜¾ÏŒ±mŒ1ÆcÌí+ÑÆŒ)ÐMg R<7¶3Z/+,OV†‰Äqåõ™žDn&FJOâ`{ŠÿÐøcë£Ç;'àd”ss­wΉÎ >?$gLäMÍÙˆ!O±†ÉÞ!ÙÿS¹Ù(«¸Ÿšòº8â.‹1ÆcŒ1¿ýö›•è]!Ë.˜”­Á¾JOþø3¼Ðz\y]%;?#ÆS)ò©yzÿìŽcŒ¢Î ÌQ¢ç£‰ó3þùÏ.£üå/¹‡˜|+ÑÆcŒ1ÀùjyiÊþ=‚íÆä=ÐTÍçf$ÔŠeGùEBÉä”`ä”`NR’õq#ú¾ÑãÅ%¡0ç”v}<ÝžÞú ”7¥p#íy¾7XöÉã3©E"9Z¸–‹R–åey8¼üYX,Ï#<Ýós¢EyÇõ+ÑÆ'‡cLýýòïx,åééåßñÈòt䪠ì[Ňè©Ç·mLÎʱœçñ Ô¬B= í’jq®+•H\0—˜Y%´êø;+Á¼wOu2wš“ïÏ((åïó—§R~ú‘å~-å¿ÿÚØ>îꛦÎF×ø—ùŸáƒ2€Y ­Dc°­Ë&Œ1Æžh–ãñÅÂñó/,³0ñƒ4ÉO`ç0† ¯êž©Ì·£„ÊêL*ØYEûÊ×…R^™L7á[Wx{¡ÆøC4ÊñÈòõ+ÿ°p\&ùÿÚ»ÛX¹Î« ÃÏZ{f·:vzzJÝDò¡(ud) •$%Ä„,UjŇH"A ”H ? ATâ‚B â£HäG¤™U!V…ˆ[PÓZJBiCR@Q!r;ñ™™½÷û¾k±÷žcoÙ“ãã£9Çg2~.iioO¶OÆ‘"Ýyµ2ƒÍÈ&"¿¸'ÛDDŒèÑÎs§‹&¤·M]@3¢—^Â[Z<މ;1‘ãß¹½¤ŽÉød'ƒÝ|²jœYxÑ8Y»¦&Rô×Ù%^GX˜,®co²o,Æb~C?/Çw°ÇþüéßÙÀnÿvª-›pg['ØÙŸüy³&!°Évª'üˆ.EÚâu,Ÿòÿ*—){²E‡ˆŸþ˜»·ô›ÿj"‚Ý»¿þ*®þ®ý›ó~7ñËN~øCÕ|q×6ÖÇÁgžÀ¡þèÏ“âCDDD$ÒN{"-XËoU!ú·ßþÓøÂ>ó£ååå ~ß œDMøÍyíUÖø5d6¿ìÃ4Áç)OpR»¾­ýt È»ÌÓ°#3´NCD"8/UÅõ×}ž|òP;uêu<}øOñâ‹ÿ†oünýþŸÂe—íÁ§ÿè^üÄÿ6ÞùΫð_ÿý>ûÙ_½÷~²9½>~üüÙŸÿ"~å—Ãpx Ïùž{îi\wÝpë­÷àòË—›Óã»îzÏ??wÿŸ`eåÄ[>7¼Çû^{ípç÷c-¯¼òu<þøïTïí |øÃ¿Ž]»Þ…Ï|æ>|ô£ciéªæç<òÈÏàÁ?‡,ë`='ËŸÿöâŸ}·ìYƽ÷,Í-à[ý7ñÈKÏá[ƒS|â#xôÑGq÷Ýwãý¼O¢/I3Ó‹ˆˆB(ðìWÿW^¹µ¯~í¡šáþŸýc„ãK_þTª ¾_ÿAí›ß|¦ŠÖåêzµç_ø"öí»³‰Õ/ÿëßb÷î÷àø ìÝ{Žùkœ¶¸x9îýØï¡ræ¹Îyî‰'~{ö¼÷Ý÷)¼üò³XËW¾ò÷¸çžß­žÝ‹'ŸüCTš÷ð ‡Q©âüŸpà ·µ½ŽCÿû2Þ=·ˆOí¿×îÜ…Ï¿òjŸ|ñ(~ð]Wâ/ß•&ˆŸzê©:Ž×›ú¹æù­Ã“èæò«¯¾ŠsýÕ ›‹ˆ(b2¯c2¯áâ"¢'â\=ôPõú㫼PÍš_ßtÓN×ÀüK¸í¶kQi^߿יû[nyw5?‰Zû{v6÷7ß¼ÀQ”‡qõÕ¨æ*œ¬^¯ÿ~ÀÃü©jpæ¹òœçØ‘#ÕšÉuõÔ¯½÷šû‘êûTšgÞ÷¾E‹Íýõ×g®¬îÿî춺qôì¹îB#õ¿†ï™C5õs/â‘ï]®ÞÓ h¡úõl7Ft{Ì¿áßsðàAÌ""""¢C‡Uá¾›íèÑ£uwMÐjÓ€ëS}ÌODDDDí ÇaD7+ë#""""6#ÚÝ/xmck‘ˆ`Ë™aRŒè·G@w×¼ß""†´crŒh³K( ‰ˆˆˆÒΓèÉ™û%ÐDDDD isçGÜMÌý|ÿè…t¿ßÇV#"ê÷ûkÞÏ"""wŸþŸË“èµCz[1 §ü$š;ÑSÒDDwÜqÇš÷DD èíútžDO[H1 y=ý»6ËˢfDDDD4y³%³Í u~Nô¦÷Ó8ˆˆˆˆ¨nµ¦Ù&1 Ðëõøé“r³±ïcßè×I>|³ŠˆˆˆèèÑ£[Å9Þ”õÚ¥¥%h–1¢'e¢ °ì¦}û0»ˆˆˆˆ(™!S…dÝt×9Öà.^ <Ô2—zTD]QªH¦ó{¿ûuÌ,""""†sÍÌšUŽ…ùyôÞ{Íq‘¬éAW¨ŠèéVDͳՎt¹ô"ZÄшhfâ0@ÄÝÍ|ÇÍ?ü tçú˜DDDD4Ϊڬrdó ¶óý?ô 7swØjž%¶=¹MÛHDõ 9‚›˜›[-¥è1D‹e!‹ïxmé#þûÂÕûNJwÎ0ˆˆˆˆHUÏì@×'{¯=¹ãG~þY™¿ìxÝè ­ŒbnÕ 5ý(ÕÌòN´·×æˆRzOVn!F饨‰1ÅÊn™!/ŠlØÄ^o®³s×ÿ]~×ÇNeÝ^3‡ˆˆˆˆvH¡,C¥WÞXIUÖ=he^Ô}(1&óën°Ò£®ˆ£±â@ÖöçôG4€à-‰£²`q .óâ.p ôÄ2éšwÕÄ=…d±W„2öò¡ö†så°Û¨ÂܼÈséöº˜IDDDDä¡ ©È‡å©7O•ÃÁ Ãjò!Šú/X4÷E-E³¢)J‡ ‰i ÄW´íO¬Í§ù$Ú›i¨C«1sq‡8ê)Å¥“’dÕh2 EЬܺ!æù@:§:’eên•³n¯/Y·‹™DDDDDžBH¡,˼?ˆÃa? O­Ô]胦,AR²j’ĔőÚòÌš°ªêãM:ýë@ÆJ_Fñ,š›”]“Îs×äQ´ìXÐ4/¼ƒ,Q¸À€f¦ˆ¥v²nÖÉ2Ì$""""J1%‹)¤r0 ƒþ ÌWNY™ç) ŠXæ…‡$•I“Ç$â"jRM²`@6ŠèÖØýfêlq@;pÌqbc—X_ú¶8XP™W+4¤^È4t;š™Ä̃jÔ"䪧۾ˆ)!¥2uûtç{ªša&‘™%„¼´Êb¸2ðÊú®z)÷ ó“YLš’ä1YJ*jˆ÷µoÌpBFý øV†tg3¢yý˜Î êwWsÉMÊ^s˜RT (\à™(€€Z„¤ùXZŒ®ŽtV2ŬF4,yLI,Æbé)M@W!m!†T–!ZjžNŸBCKéTëiºóãÙ1ΖžBŸF»Hßd¸`˜ÏR¡Aç‚‹tšx–äQÍL3ëú\/H&Ý,W‰ªŠ™DDDDDbfIÌ=y°¢ –Êà1M@{ j’,JL©Ð˜T²$C5Bû9§Ðí´|êw¢ÇCZª¶â.;\ š‹”½Tô\zÁ%è@ÒàžK2‹YYjšëº “iW‘¹`6QOLÑb$yjV8ʲ èŠ !¦"‹IÊj²ÒD2[Q5Ô èé݉?´|u 8.8±GšÝh¤Ã‘ù"¡ÊÐ @àPG).šÌM³d!v<Ó˜•ÌnD‘¸¤äA’I²zR2‹ÑÂèºîé,F)«Ñ"É0kº¢«»ÐÇ €µ ÚÚìé¶ŽŸLg†7ì‚ô½ é|6_àtHÏEqñ¤Ñ4¤h6ŸÁ‘™EuÏ€`V‘KLnª†˜’¦<%Í’š'‰©9>Кg©/ƒ­û2³sv¡½·Ó—­Œ¯tÇ\!P5T$  mH£è‰Ïq”Áb¯›<Ô­£ê¦^v3ˆˆˆˆD‚GQ“ÍTMòÜDÔ m:h©:ËR»ÆqÌ4³Î*‡OÿNt{ÅÙkW»TÒõ‰´/”îA­ôÒ¼×SĨsÞU×\#zâ^ f‰ˆ+r—¤VXaÈĤšJÖì@7+m@N¨­qŒ´c“Éþû6 z""""bd3\ÕDDDDä¸ôÑÿ3”òIEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/native_yt_colormaps.png000066400000000000000000002671401510711153200274700ustar00rootroot00000000000000‰PNG  IHDRXû‡™ð(bKGDÿÿÿ ½§“ pHYsaa¨?§itIMEà Mü‡ IDATxÚìypU¾ø?§û.Ùw¶ˆ,²« Ê'Ѹ1:*Ο¾çLi½¥,ëÕoRþA1¯†Ñ¦æ½)çáãZ*àÆ â†DЇ$€Æ@ !!$¹Kwÿþ¸÷6÷ÞÜ„,|?U§Îí³|Ï·Ï9}ûÛçtŸ£JKK-AA„^C“*AAKAA ,AA1°AA1°AAÄÀAAKAAKAA ,AA„£¯ .-- þH†CB.¡—ÍÇ!ò³zù$ÒB²ÃÎÝ›Âõå3zYùôÙC{¹+8a“~i;Ó§@9Pò{‘TÀŒ ù½Ý £ig íxhg4½,Ý´E¸@/ʶ€àtȹÄÿ­À©kŒ^”ˆ}ª×ÏÅ;~<í3fÐ>s&í3f`%tý'§Î!+6ÞÒ4p»ƒ2ÀéìUÝÝn7‰‰‰¶SJ]ÒV~úé§å®,ˆ%‚ —‹Ã‡³cÇ~úé'NŸ>Í=÷ÜÃĉ;¥khh`Û¶mÔÖÖbš&C‡eÅŠ¤§ÇЫ¨¨`ß¾}<òÈ#qã×­[ÇáÇ£ ]¥¸æškX´húÓŸ¸ãŽ;˜2eJ§¼7n¤¾¾žüÇäÃ?dûöí(¥°¬³;ÔåääðØcðüóÏ3|øp–-[& .– ‚ \z|>ÇgÖ¬Y¼úê«qÓ466ò?ÿó?\sÍ5âv»9~ü8Ç…ßêÂÆTaaaT¸ÓéÄívsÕUWQYYÙÉÀòù||óÍ7,]ºÔ–3tèPxà(KÓäM1°A¡(((    Ê@‰¤¼¼œ«®ºŠ%K–Øa™™™]¶Óé$%%%nÜÌ™3yõÕWinnŽ%ûæ›o°,‹©S§FSÉÉÉÒ˜b`]&”4‚ÐËýÉx*ÜÊ„þeYìß¿Ÿ¹sçò /P__OFFóçÏ;•Ø›†_rr2UUU,\¸Ð¯ªªbÒ¤I$$$HãˆÕWW…4‚pe÷'K*[.š3gÎàóùرc‹/féÒ¥ÔÔÔðꫯòàƒ2zôè –½sçNvïÞV\\ÌÔ©SÑ4éÓ§GX9r„ûï¿?*ϱcÇX½zuTØ´iÓ(**’KAú¡¹š6œ8q"sæÌ`øðá=z”¯¾úê¢ ¬iÓ¦1þü¨°È)Ù3gòÉ'ŸpèÐ!<•••dddàñx¢òäää°råʨ)N·Û-'Ö%Bf„ÞîO2E(š WIIIhšFNNN'£æèÑ£%Ûív“•Õõ:?ÙÙÙŒ=šªª*ÆŒÃ×_Í5×\Ó)®ë½òN˜00èûÏd–A¸Âû“L ÂÅ£ë:yyyœŸÆÆF{z­©©‰úúzmê†nàõ×_'??ÇÃØ¿?«V­êV¶ßï§¾¾>*ÌårÙ£V~¿ŸÖÖÖN]bb¢}|õÕW³iÓ&ÞyçÆGZZZ§rLÓì$¢§ÛÚÚ:é’’’ÒåWŒ‚X]#³ Bo÷'™"Í…AG]]Ï?ÿ’’&MšDQQü1›7o&;;›_üâŒ5ª[Ùü÷ÿwT˜Çã±_Rß½{w§—ÜÇÇ}÷Ýg;N¦L™ÂîÝ»™9sfÜrX»v­}lY‡ƒÿøÿ°ÃöìÙÞ={¢ò²`Áéb`'2Ë \áýI¦áÜŒ3æìkÝ0sæÌ. œx,Z´ˆE‹uÿàƒöXVqq1ÅÅÅTÎù–%ôd YAA„Ag`É,ƒp…÷'™"A«÷‘Yá ïO2E(‚ – ‚ ‚ – ‚ ÂåE•––Ê8¿ ‚ B/"#X‚ ‚ ½LŸ­ƒÕÕz&áEä”Röq¬ßU|wi.•̞ʹ=Õ­'º_H\ŒŒ‹Ñ} Ôkoé~¹êõbtï­~Ó_ëõb®ûKÑ÷úëVoý—Ć?ýôÓrWdK¸rÿ ‚pi®¹ÆAF°„+˲ä —ð:‘kìÜlذªª*û811‘¼¼<–.]ʰaúÌ÷Ç?þ‘9sæ0oÞ<;lëÖ­ìØ±ƒ|1cÆØáëÖ­###ƒÛo¿=®¬ÒÒRî¹ç&NœØ)®¶¶–矞'Ÿ|’„„ûxèС<úè£Qí»fÍ–-[foýÌ3ÏÐÜÜÜÉè¾ñÆ£ôxá…øþûïùÍo~CnnnÜ: ¦¥¥qõÕWSXXˆÃ!·{1°„Aõd.rÈ5Ö[PRR‚eY´¶¶R^^ÎK/½ÄO<Ñež1cÆP[[e¨ÔÖÖ’žžNmm­m`~üñÇóÚf§'íØÔÔDuuµmLu•oñâÅÌš5+*ÜívG777sôèQ~ö³Ÿ±{÷îNVd†A]]ëׯG)Å’%K¤]fdŠPèÑÓµ †€p鮹Æz†®ë$''“’’ÂðáÙ7o---´µµu™ÇãñpôèQLÓÀëõR__ϼyó¨­­µÓ=zÃ0¢F´zƒÙ³góá‡bF·é\.)))QÎétF¥©¬¬d„ \{íµìÝ»—@ Ðe¥¥¥1qâDÆÇÁƒ¥óˆ%‚ çÆëõR]]MVVIII]¦3f ^¯—ºº:Ž9Bvv6“&Mâ‡~°”ÚÚZ222ÈÈÈèÕ‡™9sæ`š&_|ñÅEË«ªªbÚ´iäää••Å·ß~ÛmúcÇŽqäÈt]—ÓÈ¡ ‚0 Ø¿?«W¯Àçó‘ššÊÊ•+»Í“MZZµµµŒ9ÒžLII!==~øÁžFìíÑ+§ÓÉÂ… ùàƒ¸æšk:Mû…Ù¶måååQa÷Ýwùùùã“O>áÆoŒ›æ†nèôžVZZšý»²²’)S¦ØSüS¦Láý÷ß§©©‰ÌÌÌNuäóùøì³ÏÐ4I“&IçKAâãt:£Œ‰ââbÖ¬Yî]»X¼x1·Ýv~¿ jZl̘1lÞ¼™¶¶6~úé'F ÀèÑ£Ùµkùùù˜¦‰Çã¹$zkšÆâŋٸq#³gÏŽ›&))‰¬¬¬¸qíííìÛ·Ó4Ù¹s§nY•••,^¼8nÝvÛmüå/¡²²ò¢^ÞÄÀ¡È’Â`#üUjjjÜxÇcèdgg“œœlXo½õ555deeu™¿7¸úê«ùôÓO©¨¨8ï¼_ý5iiiüò—¿Œú âàÁƒ|öÙgƽ¦•RÌŸ?Ÿ-[¶0uêTYªá2#/¹ çDnÆ‚pi¯¹Æz†a´¶¶ÒÚÚJCC›6mÂï÷3a„nóeff’žžÎ—_~õžUzz:©©©ìÚµ«Ç£W§N¢¾¾>Êù|>ûá%öa&’%K–PYYi²Eâóùìs ;¯× §'OžÌ!C:t¨ífΜə3g¨©©éÖ°Ó4/¿üR:ÐeFÌYAF<¡¯¹ÆzFMM k×®‚Ëäää°bÅ {ʯ;<ÕÕÕ^d=z4ÕÕÕ=2°”RlÙ²¥SøªU«âʱÇÇÃ÷ßßIƇ~ȇ~vÍ5×0kÖ,Ž;Æ­·ÞÚ)OBBcÇŽe÷îÝÄEÑ4fϞ͎;¸îºë:-ý \‡®ÒÒÒ>Y€Eö"8{öƾe¢»ìE({¤½/¦ýzs??Ù‹Pú2E(ôèéZDOU2RÒ﮹ÆA ,AAA ,AA1°AAÄÀAAÎÒg_ ‚ ‚ VdKA¡—é³…F£×ÁR¾ãÇ:­›tZýx2ãÉé¡,ò5-èôðïP\Ø×Caa?6M8Lô#ÒǪªGwçô.~k1§ßU\¸,="Š“_‹Icû`†œÇfL\ø·ãÇþ¶ÎúV(Üö#Ò„[¡üö±qlŤ7¢òi(,4 8Mp…œÓ‡ ×cã"~»LpE¤ §wà €Ó:Í=ÂwøÏ:=ü;pö82­8çðmyf¨b»mäq¨ Ãá*¦/D¦W±yb.Ó?bÓ©˜KQÅô£ëYžŠNW7'oºÇúQºEÖ…GÇs•×E]¨˜kª«zRçÐ.ê+®nªõsŽ]êÖ“>ÑMxúéR¹+ 2‚%啤iA„¾ÁA„ž°aꪪìãÄÄDòòòXºt)Æ ‚³"÷Üs'N씿ªªŠÍ›7óä“OvŠ‹ÌwêÔ)ž}öÙÎ(Jñë_ÿš‘#GJcb` }€|.ѯ›FF±„LAA%%%X–Ekk+ååå¼ôÒK<ñĽZŽRŠû￟!C†D…'%%I#ç…L Âå@‰ú¢ëPa—°òu]'99™””†μyóhii¡­­­wF,‹ÄÄDRRR¢œ¦ÉíRK;[ÿÃØMcIUKßìG•ïõz©®®&++KF–„~‹L rg»BšFì_a ³ÿ~V¯^ €Ïç#55••+W^’²þö·¿uÚý©§ž’FÄÀ„~‡Ø¨Lò¾9*ßãñPTT„eYttt°sçNÊÊÊxøá‡IOOïÕ²î¾ûnrrr¤o…L rg»Èá•RÕWvß¼„•ït:ÉÌÌ$++‹ÜÜ\Š‹‹ñûýìÚµëœyÝn7~¿¿SxGG QáiiideeE9AKA¸bçL“išüôÓOQáuuu(¥ÈÎÎ>û0¢äIQèdŠP¡ƒËAO^ì;X2E8Èûæ¨|Ã0hmm ½½/¿ü¿ßÏ„ ì4§N¢¾¾¾“q5tèPÆŽËÆ¹é¦›ÈÌÌäĉlÙ²…)S¦ššzöZ±,ÚÚÚì²Â$$$àpÈ-SK;›Ÿ½Ø4é%yy¡¿+ìV~MM k×®Àår‘““Ê+=zt°+Å–-[:å[µjùùùÜ}÷ÝTTTðÎ;ïpúôiÒÒÒ˜4i ,ˆ¾V”â…^ˆ2¸”RÜyçL™2Eú‹ – ‚08())¡¤¤¤Û4¿ÿýï»OHH`Ù²e,[¶¬Ë4ç”#=EÞÁz÷IXˆØM#S„Ò7¥òA ,Aãó V_ìü>¬0©|AKèçO‚4 ‚X‚ ‚ ‚ý`[ZZ*ƒº‚ ‚ ½ˆŒ` ‚ ‚ ô2}¶LCiii&ŸJEû‘N×ÁáúaÏE¦§‹ÍãptוìÈ<±écu''öÜbË‹Õ=Þ¹Æ+¯»:p8¢ëX)0œufŒ Ð}¼q/màò=evoœC·À9të*Îìá¹=ÔÝe‚²‚ïIi1N?‡;WÚ®ä9bÂzZ–ÞMyçÒÝq²ô‹¨­‹òºŽ·‚ å=lÈ í u1ð|:¼ÑÃèÁ…Û]¼SçðtW÷A,AAAAú56l ªªÊ>NLL$//¥K—2lØ0 8+rÏ=÷0qâÄNù«ªªØ¼y3O>ùd§¸È|§NâÙgŸí”F)ůýkFŽW¿ŠŠ ***ì} SSS)((`É’%$&&Úéžyæš››;ɾñÆ™7o^§ò].éééŒ3†9sæDí™(ˆ%‚ MAA%%%X–Ekk+ååå¼ôÒK<ñĽZŽRŠû￟!C†D…'%%u›oèС<ðÀ˜¦ICC7nÄëõr×]wEÉ^¼x1³fÍŠÊëv»ã–ï÷û9~ü8Ÿþ9ÿõ_ÿÅÊ•+ñx<Ò2E(Â%GÖá.]×INN&%%…áÇ3oÞú(º®ãóù˜1cyyyX–ŧŸ~Ê‹/¾Èï~÷;\.–eQVV†ÏçãÎ;ï$33““'OF•ë÷ûùì³Ï¸óÎ;xóÍ7yÿý÷¹ãŽ;¤‹ÀÀ’)Âþ©§0è«L¦åŒúó9íß¿ŸÕ«WàóùHMMeåÊ•ìÝ»˲¸õÖ[íô·Þz+øÃ¨­­eܸqx<ž(yEEEvüUW]ÅÁƒ©««ã±Ç#++ €ÌĮ̀<¦iRTTd‡Ïž=›íÛ·Ëž0@ ,™"ìŸzŠ‘5è»±LÊ]>#ïü{›Ç㡨¨˲èèè`çΔ••ñðÃsìØ1m,L  ©© À~o«¶¶–3gÎ`Y~¿ŸææfŽ;FZZšm\ÅÃétF])))œ9sFþð„b` ‚ Â9Œ›ââbÖ¬Yî]»ðù|äæærçwbYÑÆ[ø½­õë×ÓÑÑÁ-·ÜBzz:º®óÜsÏaFðfè8÷íPÓ¢W6RJž<…d`ÉaÿÔSôU&S„rF]cõÛs Œ1‚o¾ù†¤¤$ÜnwÜtG¥¨¨ˆñãÇÐÜÜL[[›?lØ0ZZZ8yò$ÙÙÙò'&\4ýo¡Q™"=¥iEg9~vF}?Eh­­­´¶¶ÒÐÐÀ¦M›ðûýL˜0iÓ¦‘””Ä+¯¼ÂáÇijjâСClÚ´‰––²³³©®®¦¡¡~ø7ß|§ÓiË3f ùùù¼öÚkË#<ÂðáÃå‡ÚÚZžþyž|òIú¥Ž6l ££ƒ{î¹G ,Aˆú§z^_–´ƒt)¡§7ݪª*û811‘¼¼<–.]ʰaÃÎö™~²?`ØQJaYÉÉÉäççsÓM7Eí©Ø†RoÔQ¸=”R(¥HKKãꫯ¦°°°Gû:Ê– ‚ ô ())Á²,Z[[)//祗^â‰'ž°ÓÄnöܧJñÛßþ—ËÅÉ“'yûí·yùå—yôÑG/Úȱ,«ÏÉp{†A]]ëׯG)%«ß‹%ô:ò=jÿ¨/kpU‘Œb at]'99®Ü>oÞ<Ö­[G[[IIIÒWVV²eËž|òI;lß¾}¼òÊ+”FLGîÛ·íÛ·ÓÐÐ@jj*Ó§OgÁ‚hÚÅ¿¦œ””DBB))),\¸7ß|“ÆÆF²³³ùì³Ï¨¬¬¤©©‰ÄÄD&L˜ÀÒ¥Kq¹\@pÊó½÷ÞãÈ‘#†Aff&K—.eÈ!üïÿþ/J)Ö¬YƒRŠéÓ§ÛÆç'Ÿ|®]»hmm%''‡ tZµ¾·Û#--qãÆqðàÁ(«¹¹™÷ߟƒ¢”"??ŸåË—“‘‘€iš¼ÿþûTUU¡i3gÎìWF²X5Àn¢« Hï_x½^ª««ÉÊÊŠk\A×Sa‘á‡fýúõÜrË-Œ=šÆÆFÞ~ûm”R,\¸°wo¸¡©3Ã0l=n¹å222hjjâÝwßeëÖ­üüç?àÝwßÅ4Mzè!œN' ¸\.ÒÓÓùÅ/~Ák¯½Æï~÷;\.—½qõÇÌž={(..&++‹Ã‡óæ›o’œœlïÛx)8vìGޱ §ðóVÚ IDATy–••1jÔ(zè!4Mã£>¢¬¬ŒG}]×ùôÓO©®®¦¤¤„œœ>ýôSöíÛ‡ÇãK¸˜!5€ôí®jéÚ_îŒò–WN¿höïßÏêÕ«ðù|¤¦¦²råÊ‹’¹}ûvæÏŸÏôéÓÈÈÈ °°­[·öªuúôi>ýôSÒÒÒÈÎÎ`Μ9v|FF‹/æwÞ± ¬––&OžÌСC¢ÞÝJLLÎŽ>þøcxàFŽiç9rä_}õU¯Xáö0M“@ €¦i¶îß|ó –eqë­·Úa·Þz+øÃ¨­­eܸq|ñÅÌŸ?Ÿ‰'PTTDMMÍ è¯b` ½o7 }[_2E( R<EEEX–EGG;w¬Œ‡~˜ôôô ’Y__ÏÑ£Gùè£ì0Ó41 ¿ßo …9|ø0/¾ø¢}\\\ÌÔ©Sã÷_ËâOú–e>|8+V¬@×u<È'Ÿ|‰'ðz½ÊýÙÏ~Æ;ï¼CMM cÇŽeòäÉQ/ôÇÒØØˆßïçïÿ{T¸aŒ1¢GõñÎ;ïðõ×_ÛÇO=õÔ9ÛÃçóñÙgŸ¡i“&MŠªÛÆÆFÛ(hjj¢££ƒÓ§O“——gÇišFnn®XÂÅ<—Ë´›è*ÒãϧÓ5ŠS\\Ìš5kصk‹/î\7q¦ÃÓsa|>………Q†Ady±äååñÈ#ØÇ)))]·R<ôÐC¸Ýn’““íw« ø~ÕË/¿Ìu×]Ç7ÞHbb"GŽá­·ÞÂ0 œN'³fÍbüøñìß¿ß6Æn¾ùffÏž·<ŸÏÀ½÷ÞKjjjô;‡_örà 7œw{ÜvÛmüå/¡²²’™3gÚúäæærçwvz¯*99yмk%V¿º)Â+zÌC¦Åx¸²zü%#Ä OJJÂëõFDÕ××G¥1b'Ož$++«g7M‡£Çi!8õo…ºº:,Ëâæ›o¶ÃöîÝÛ)]ZZ×^{-×^{-Û¶mc×®]Ìž=Û‹4R† ‚Ãá ¹¹ù‚§“““í×ÏëZUŠùóç³e˦NŠÃá`Ĉ|óÍ7$%%áv»ãæKMMåÇ´õ5M“Ÿ~ú©Ç#nýYÉ]A†Akk+­­­444°iÓ&ü~?&Lˆ›~äÈ‘8N¶mÛFcc#_ýuÔZZ .¤ººšŠŠ Ž?NCC{÷¼üâßnFh²²²0M“Ï?ÿœ¦¦&ª««ÙµkWTšÍ›7SSSCSSuuuÔÖÖ2dÈ{Jô»ï¾ãÌ™3ø|>Ün77Üp›7o¦ªªŠÆÆF~úé'¾øâ ª««{¤×ÅpõÕW£i_~ù%Ó¦M#))‰W^y…ÇÓÔÔÄ¡C‡Ø´i---üìg?ã“O>aß¾}œ8q‚wß}—ŽŽŽAÑ_e«ÏžËá´›5€tí/CI®ŒÎ ä«sPPSSÃÚµkp¹\äää°bÅŠ¨ÑšÈiÁÄÄDî¸ã¶nÝÊîÝ»;v,………¼ýöÛvšñãdzråJ¶oßÎŽ;ÐuœœfÍšuñmÓÍ:UÇçæ›ofÇŽ|ðÁŒ=š%K–°~ýz;iš¼÷Þ{´´´àv»)((°G¼ÒÒÒ(,,dÛ¶mlܸÑ^¦añâÅ$''óÉ'ŸÐÔÔDBB#FŒ`þüù=Òë¢Fl4Ù³g³cÇ®»î:œN'«V­bÛ¶m¼öÚkx½^ÒÒÒðx<öˆÖ 7Ü@kk+6l@)ÅÌ™3™4iÒ 0²TiiiŸüËV9ƒp«]u½mŽl•Cç½N¬Ðö8½¼U޽]ÎÀß*§S˜l•#[åÂA¦AAÄÀ ÈaŸëÚ)Â+éT¤Ç ‚XÂå¹mXL_ÑU¤Ç ‚ – ÒWj}É2 ‚ b` ‚ ‚ 6úl™†KêFŽmzo`†œƒ$…\?}<Ó§´”ük‚ï/RAAèEúl«ºôîГ‰…‰…Aíh#@;¦òbàÃT>L|ª=˜FµcÐ ÷‡â|ÁôšCu`h^L¼xƒñø°`©¦ `)S0•¥ ,e‚Òƒ#JC)¥4”ò•µN”²×¤ =¥†‡ ù–2±B>XXZ”¥‚¾Ò”-W) M9Ñ”Ëöu\hÊ®ÜÁpœhʉ®Üè$ «tД :N´P>Iè$¢“ˆƒDt’pˆƒ$4ËN8 ÝrâÀ‰n9Ñq¢[~4|è– « ÝjG·ÚqXíhøÐ,?šåC³|èt [±Î‹nyƒi ,„ïÉö£¬ 㬳h("ÃÍÐêd&*¼ÀNhL@Å,¬c…×S K),¥aiúY_s`jޝc)–¦cªPœîÂÔ\!ßᘚ COÀÐݘzF8­æŒˆKÀÐm9COĈ’å  ·ÐÛ1ô6z¨?k^ -è´v ­=ä·ÙÇÁþÝa_¦òc¨P¿¹`\ t}ø±,?&^,|˜x1­fèÚ3 `Y–\åÙ²SC™zÐY:ÊÔÀR`©àïðzX†:; iY`‚eZ`XX†ôMË`™–aØka©¯™šéD7œhfÐ)Ó–e:ÐLºåF3Ýè–ÝJ@7ìßšå ö{+x8Bý>Ü÷5\¶Ó•‡JDWI8´Dt•¼Þ”Ms¡+ºnáÐ,tÝÄ¡pèíèzG;ºîEÓ|hº/è;¼èŽvtGGÈ÷¡éþ³ÎáEwæô¡9½hNè·Í@éJ7‚¿š3€rМÐ-”nÚÍBi!_™`\WÌ ä–í+,SSÃ24,+è°4,KÇ´t,ˉi9°:=ô[ÇT. åÂTnL\š;ä047¦rÚ}ßÔœÁ>î÷ÿ„Ð5¼F åÆ$0’0ŒD Ã…eª NfðöüeõEÝWN:ųÏ>Ë#<ÂðáÃå/\™– ‚ ô” 6Dms“˜˜H^^K—.eذavø¥Z¥ù¤¶oß>^yåJ#¶åÙ·oÛ·o§¡¡ÔÔT¦OŸÎ‚ дί)»\.rss£ ¬ÚÚZòóó±,‹ÚÚZf̘a‡9‡#x«­®®æ‹/¾àĉ¸\.<Ë–-#99˲xæ™gX°`×^{­]ÞO?ýÄ_ÿúWüqÒÓÓéèè`Ë–-|÷Ýw†Ann.7ß|³=%Z__ÏæÍ›©««C)Evv6EEEäææJKú5 E ÒUõëF¯×Kuu5YYYq+èzº02üðáì_¿ž[n¹…Ñ£GÓØØÈÛo¿RŠ… ÆÍ?fÌþïÿþÏ>®­­e̘1˜¦ÙÉÀŠÜ4Ú4M/^Lvv6gΜaË–-lذ{ï½¥S¦LaÏž=QÖž={ÈÏÏ'==€×^{ —Ëů~õ+Ün7_}õÿûßùíoKbb"o¾ù&#FŒ ¸¸¥õõõèº.] ,AèC¬Kw–OôûO ·>öïßÏêÕÁá}>©©©¬\¹ò¢dnß¾ùóç3}út222(,,dëÖ­]X‡O>ù„ÖÖVRRR¨­­eîܹ†ÁW_}@cc#ÍÍÍŒ3ÆÎ7sæLûwff&Ë–-ã¹çžÃçóár¹˜6mŸ}öÍÍͤ§§cY{÷îµõ8|ø0uuuüÛ¿ý›m4ÝtÓMìÛ·o¿ý–k®¹†ææfæÎKvv6YYYÒÑÅÀA„®ñx<aYìܹ“²²2~øa{„ç|©¯¯çèÑ£|ôÑGv˜iš†ßïÇéì¼ÐÛ¨Q£Ð4ÚÚZ† F `Ĉ˜¦I[[§N¢¶¶§ÓÉÈ‘#í|uuuTTTpìØ1ÚÛÛí÷Åš››2dÇ'''‡={ö0oÞ………Lš4)ny]é‘——Ç¡C‡hkk#??¥º®3jÔ(:d¿—iòù|”••1~üxî¼óN’’’hnn¦¬¬,J§iÓ¦ÙÖž={?~<‰‰‰¶ŒÔÔTV­ZÕéeþ„„-ZÄÔ©S9pà ¢¢‚»îº‹‰'J‡Kú™"¼"šX|õâ†'%%áõz£F¢êëë£ÒŒ1‚“'Ož÷(ÇãaïÞ½´··GMæççS[[ËáÇ£Þ¥:qâííí,Y²„´´4~üñÇNr§NJyy9uuu|ûí·GéÚÚÚŠR*ÊÐŒ%;;›ììlæÌ™Ã믿Nee¥X}€¬ä.‚  ൵•ÖÖVØ´i~¿Ÿ &ÄM?räHœN'Û¶m£±±‘¯¿þ:j--€… R]]MEEǧ¡¡½{÷R^^Þ­.cÆŒáäÉ“øàêêêðûý¼÷Þ{ÔÖÖÚquuu 2D:O #X‚F¦¥‰¯ÀúH£X555¬]».—““Ê+=zôÙsŠ˜LLLäŽ;î`ëÖ­ìÞ½›±cÇRXXÈÛo¿m§?~<+W®dûöíìØ±]×ÉÉɉúú/£F²—_1b„ž——‡aöra’““)))áƒ>à‹/¾`ĈÜ|óͼüòËdO:•÷Þ{éÓ§Ûe„¹÷Þ{ùàƒظq#gΜ!%%…Ñ£G“’’‚¦i´µµ±~ýzΜ9CRR“&MbÑ¢EÒÙÅÀ„>D¦¯ˆ&f}”””PRRÒmšŒŒ ~ÿûßG…Mœ8±ÓôX¬ñ4nÜ8Æw~7O‡ƒÿøÿèq8À”)Sìµ³ÂÄê pÝu×qÝu×Å•ár¹X¾|9Ë—/×]wIÇî'È¡ ‚ ‚X‚p‰)Bib©AÄÀ„^Æ¢i©AKAA ,AAá^ó˜qÒºhÂÀ9ª<^UÅV« O£˜¥¥rWdKAb‘¥A ,AapêÔ)JKK;mä<Ð(--eß¾}Ò ƒÙ*GAè5.ÅWS6lˆÚ¤911‘¼¼<–.]ʰaÀè=/µÆív“Í‚ :mÇ#b`/M©©ZAú1”””`Y­­­”——óÒK/ñÄO ;ëò}ûí·3nÜ8¼^/;wîäµ×^ã‘GaèСÒP‚X—þ™Kªy¦ê-t]'99€””æÍ›Çºuëhkkë”¶²²’-[¶ðä“OÚaûöíã•W^‰Ú·oÛ·o§¡¡ÔÔT¦OŸÎ‚ дîßžq»Ý¤¤¤’’ÂâÅ‹ùüóÏ9tèP”u.Ù'OždãÆÔÕÕ‘™™É²eˤóˆ%‚ }÷Låõz©®®&++‹¤¤$|>_´‘×ÅtadøáÇY¿~=·Ür £G¦±±‘·ß~¥ .쑦i²k×.Ûì©l˲xõÕWIIIá7¿ù lÞ¼ù²Ms b` ‚g.AªV„Þ`ÿþý¬^½ŸÏGjj*+W®¼`yÛ·ogþüùLŸ>€ŒŒ Ùºuë9 ¬7Þx¥~¿˲ÈÌÌäꫯî±ìƒròäIî¿ÿ~RRR¸ñÆ)++“†Kž¹©ZA.ß3•Ç㡨¨˲èèè`çΔ••ñðÃ_¼úúzŽ=ÊG}d‡™¦‰aøý~¶lÙÂ×_mÇ=õÔSöïeË–áñxhjjbË–-,_¾œÄÄÄË>qâiii¶q0räHé ™4iRܲ ¹á†âê’œœLVVYYYÜvÛm¼øâ‹üó?ÿ³ýŽXw²¹íŠ%Ï\‚T­ çÉå´Â’’’ðz½øý~œN'@§5²FŒÁÉ“'ÉÊÊêÒˆ LÝ‘——Lj#øè£X¾|yd2„––Z[[íQ¬~øAÞÁdÈB£}ò— U+‚úè#~þóŸŸS¶RŠ{·Þz‹çž{ŽŒŒ –/_./¹¶‡ÒÒÒ>cè_›=;¸ðŒe³çnuw\@=Èfϲٳlö,›= ÂG¦™Ç’ªA1°A„~‰¼¦-b` ‚ ‚ \úì%÷ÒR³‹¿´J_~YBèÃçÿàËfá¦+¢¿ï'‚pÈ– ‚ B/Ó‡#X¥dee1fÌÛ%¶ èͨÍ"P›…ÙáÄDÙΉ~ø0BOùÁ¸àwL.,ܶ3PBß=û1ð‡| 7&n,0qF|{¦0B2!ßÀ´óø15n,‡ ¬A*g]$†}A?Á©.HuGøy“Mp¶ƒ£í µ·¼§Á×f+¸;ÀÕîP:Ú#\М:+øNƒ·¼§Ñt§np¸À¥‹ s‡:†3ä;§^púÀá·\® ïvEçuš {Aó‚î=pVŽpkìº$$8¢ËÓü€”ðB‚L—ì„$gh`!öó¥ˆÏ“¼AçëŸ7XžKùÝ~Ñf‚ùÂ2¬¸#ò: Û¯Êü~ðzÁç úÀ¥Àr ‚/þ‡]Ì`ÞøŒ 0Îæ ·>òó«3Bù½ð‚anuV†]oqòbË —í5‚íá9 "t¶,ðš¡ôøhYÊ å ùN+Zwà æ÷šà ……Óº-ÐcuŒÑÛÊç éáältÅ©âØãP×¢#Ôœœ•¡wQMáß`÷Ä’AÌõ¢:Wm”œp¾°ïˆq±ùˆ9öE8ƒ³܆?Ø,Ï¥óG¤”ûQodY„òù#dÄ~¬LœsÔÕª¯ðåùÁ¯ŠÈÆ’¯ÁêÈ«•ÂÀï~J®8Ñ{€œüã B?Áê},¹ä…¾í~ýGÌ@:eÑ{€žÏå.wÆ TUU¡”²‚¥+¥7n÷Ýwß%-ûã?¦¼¼œ¥K—v¹?¡ bKŒ+aàw?Á½Êù¨>0² ())± ,¸<›'WVV2oÞ<*++ÅÀ®D«ë,1½„ò8/#XýCo5€ÎϺ‚ÊÕu½Û ˜›››yï½÷8tèJ)ÆÏòåËí •ëëëÙ¼y3uuu(¥ÈÎΦ¨¨ˆÜÜÜ.eÖÖÖ(,,¤ªªŠ£G2jÔ(ùÏ®$KA¸bŸq,‹—_~·ÛͪU«0M“wß}—×_|€7ß|“#FP\\ŒRŠúúzt½ûõ8*++™:u*š¦1uêTvïÞ-– – ‚0xØ¿?«W¯Ž ›?>óçÏçûï¿çøñã<þøã¤¥¥pûí·óç?ÿ™ºº:rssinnfîܹdggÁ¯Ø»Ãëõòí·ßòÿðL›6uëÖ±|ùr\.—4ˆ – ‚0ðñx<E½ƒ•˜˜À‰'HOO·+€!C†@CC¹¹¹\ýõlܸ‘êêjÆŽËäÉ“»5²öìÙCVVÆ `øðᤧ§óÍ7ß0sæLiA ,Aaàãt:ÉÌ̼àü‹-bêÔ©8p€PQQÁ]wÝÅĉ㦯¬¬äøñã<ýôÓv˜eYTVVŠ%ˆƒï³lAä?Cˆ&''‡ææfZZZìQ¬ãÇÓÑÑÁ!CìtÙÙÙdgg3gÎ^ýu*++ãXÇŽ£®®ŽU«V‘`‡···óüóÏsâÄ rrr¤â…+ÁÀ’o…ßýd™Ñ{ œO_|ii­­­Qa𦑔”ĸqã:t(o¼ñË–-Ã0 Þ{ï=<¹¹¹øý~¶nÝÊäÉ“ÉÈÈ ¥¥…ºº:&Ož·¬Ý»w“——G~~~§¸ÜÜ\vïÞÍM7Ý$ÿ}•``ÉB£Bw¿þ#f ²è=@ϧ/Ê­©©aíÚµQaÙÙÙ<öØcüò—¿dÓ¦M¬[·¥,_¾Ü6ÄÚÚÚX¿~=gΜ!))‰I“&±hÑ¢¸†Üž={˜7o^\=&OžÌgŸ}Æ’%KÐ4ÙÒWô–WÂÀï~2‚%z”ó¹Ü#X%%%”””t›&=={î¹'nœ®ëÜu×]=*K×uþýßÿ½Ëø¹sç2wî\ùߺe™Þ–˜^€F¬þ¡·’v´í/b` ‚ ‚ ˆ%‚ ‚ ˆ%‚ ‚ – ‚ BÿG•––Ê»Š‚ ‚ ½ˆŒ` ‚ ‚ ô2}¶Öÿûÿ€“'OR[[Ë¡C‡B®ÚZ=ätth!;P‹°-Î~$ìÜ€+äëqœ#äk€á€ÄïŠH§ÅɈȯæÍ ÊN\¾³ÎéG Âg8ZÁÑ,Î¥BŽè¼noŒ,à 98|d@R èÜF° gþ`Ù‘ùceÚiƒùœ*º¡³ûî猈sÅ´€Ë‡t/è>pšè‚wÈ9¢e¹bÊÓü | ¼A— A² ’ä µ„áŒèfñv¯|^pkàÒC¾Ö9}¤,#Ì–aÀ‘×AL2"t0Áï¯|¾ ï5±;äTd÷µ¢Ë¶LðÀgý€q6¯K Ö“ÞŠð»×)Îêa^ëå˜`Yà5Cºà7£ÛÓ¯¼™†Ìï5Á sY!è±åÆèâ åó†ôp†Ë ¹x§¾’:B]Ááô8§é‚W -ƒ˜kE£Ê»k«‡i"ãé&ÎéêùVÌ?£:‡þ§"þUœ²ãÕU ”׌Èùd9…’RZ*weA ,AYcL¼ýZÞè_lذªª*”RXV°u”RŒ7Žûî»ï’”ùÌ3ÏÐÜÜl—•’’Âøñã¹é¦›HLL”FÄÀA>”””Ø€ÃqéncJ)/^̬Y³°,‹“'OòÖ[o±yófn¿ýviA ,áÒ!Où‚ôkár¡ë:ÉÉÉ]Æ777óÞ{ïqèÐ!”RŒ?žåË—“’’@}}=›7o¦®®¥ÙÙÙ‘››Û¥L—ËeçOMMeÆŒìÝ»WCK. ²¡è}vùþdˆZ–ÅË/¿ŒÛífÕªU˜¦É»ï¾Ë믿΃>À›o¾Éˆ#(..F)E}}=º®÷¸Œ––¾ûî;FŽ)@K›ÕåùwïWbÒ)÷K½å¬þÙþû÷ïgõêÕQaóçÏgþüù|ÿý÷?~œÇœ´´4n¿ývþüç?SWWGnn.ÍÍÍÌ;—ììl²²²ÎYæ¶mÛ(//Ç4M#GŽä¦›n’ ˆ%ÈMV¤_<EEEQï`…_6?qâééé¶q0dÈhhh 77—믿ž7R]]ÍØ±c™ø€_|‘‡z¥äSK.-2E(z_]þr¢N§“ÌÌÌ Î¿hÑ"¦NÊ8pàÜu×]Lœ8±ËþøãÒÐÂ#ï` ‚ ‚ ˆ%\ÉO±ý™"TzK¿¼í/b` ÂhiÊ¡è-õ(b` ‚<Å Ò¯AÄÀAA¸ j¥¥¥2Ò+‚ ‚ЋÈ– ‚ B/Ógë`•––ÆPŽ.|ÕMxWi.•L.²œž¦é‰n=ÑýBêàbd\ŒîQ¯J…‚"}Zı #N:ÅÙx–)'&m¤-žŒ8ú¯ÅÓ%ŽÌØãséFl¾‹Ñ½›z=§îP¯ô°”R(Th…¦tTÈiJJi(´_¥Ó-$#¯tZ„ ³¿D¤×”ŽŠŠWr#äØ2Ï–¥W„nuûzÝÃñ‘ºjñQQæYÝbuWQç¯Ð£tï®^Ãr;ÕkŒîÑùO?ý´Ü•Áa€¢DUAZBí– ‚ œ­­­|üñÇ8p€––ÈÊÊbêԩ̘1§ÓÉ3Ï>žÙ³gS\\|{UP¥ÈÍÍå³Ï>£¡¡AMKifá¦ç™Ñòw2䇿%òóóÑußÿþ÷|ñÅÔÖÖRWWÇñãÇ©««ëu‰„œœΜ9Cuuu×MMMx½^Ë¡iš)£ëzX¸×ëí1Ÿ»ï¾›‰'røðaùdˆPèão«(Y#^a–!ÂAü;‘!›âñxX»v-ûöíc×®]466âp8HJJbáÂ…½.‘””Ä”)SسgÏ<óLÇ)Å¿ÿû¿[.¥?ýéO™8q"µµµüæ7¿±È8~ñ‹_ô˜W^^ÿõ_ÿضIÕ?µƒòj"{"{Dô.{öm?¿¡¾aOe“½ïô^„áe—½¡wdˆPaf¡O&aÒÌ2D8Tþ}:J)–-[Fvv6>Ÿ“'ORTTD\\S§Nµ(S/¾ø¢EQˆ‰‰1Ï“““Y½zµEÁ ÞóP)…Óéd÷îÝÌš5«×ý»£3}MÓ¸xñ"[¶lÁï÷³jÕ*3ýPÚÛÛ9pà€©Lõ…={öpðàA~ô£1eÊéL¢` #y<ö­Ž i)éëB/lݺ»ÝÎ /¼€Óé4ýÝn7Ó§O·Èº\.SQzðÁ)--åܹs ::šÈÈÈó´ÙlŒ3¦×rÝsÏ=œ>}š#GŽôºâÍÒŸ={6çÏŸçôéÓ¦‚¬Øu²`Á<ÈüùóoZ6€mÛ¶ñ—¿ü…çž{ÎÜcQ¸3È,áØ „áPGC±¥”´£477sþüy,X`Q®nÚ"†ÁÉ“'iiiÁn·ß–²EDD°xñb>ùä|>ß-¥åp8Ð4­ç¿¥ÈÌÌÄãñðÉ'Ÿôš–®ëlÚ´‰¯¾úŠ5kÖˆr5ˆK„!­²ˆ]H¸ví†a˜˜hñÿõ¯ßï:,;yyy³{÷nü~?º®m™ƒÕ©|ýÛ¿ý›Å/!!Ÿÿüçæõ•+WX·nEæÞ{ï5-LÌ›7²²2<ÈâÅ‹¿Õ=VWWó—¿ü…ôôô^F€ÜÜ\6lØÀ<€ÛíîVöÈ‘#(¥X»v-cÇŽ•N$ –0òÇcßêH†¥¯ ýåùçŸÇ0 6mÚd*Z .$++ ¯×ËÇÌüùóñx<ÖÖRŠŸüä'–9X¡V®±cÇò£ýÈ2Tþ u8Xºt)Û·ogÞ¼y–°ëׯóÿþßÿ3¯-ZÄ¢E‹, œ®ëèºÎÝwßÍ£>zÓûž:u*wÝu»wïæûßÿ~·2“'O¦¦¦†Ý»wóä“Oö{~˜ – 6ˆRGRŠá¯²H_¿]x<”RÔÕÕYü;­7¡Ã†ÑÑÑx<<O=õ¿ûÝï?~'Ož¤¨¨ˆ¸¸8¦N–æñãÇE™DÁAF'[·nÅn·ó /àt:M·ÛÍôéÓ-².—‹˜˜|ðAJKK9wî\˜‚µ`ÁöìÙCff¦ìÍ' 82É]QÈӬë·jä÷úææfΟ?Ï‚ ,ÊÕÍ0 ƒ“'OÒÒÒ¦@)¥ÈÉÉA×u:$PpÄ‚%Œ( Q²„ÑÖo cÄ+Y×®]Ã0 -þ¿þõ¯ñûý@‡5*//€ââbvïÞßïG×u¢££-s°:q:,Y²„]»v1wî\"""¤3 ¢` ‚ £›çŸÃ0Ø´i“©h,\¸¬¬,¼^/ü1óçÏÇãñt›Fvv6dÿþýäææJ¥ †  # ±^ £®ßŽ‚!BǃRŠºº:‹¿ÛíÆãñ„ FGGãñx¸ë®»xꩧؾ};µµµÝ?m6–-[Æ¡C‡¸qã†tFA,AèÙ÷IuýÖù½>::šŒŒ >ýôS|>_¿âÆÇÇ3{ölŠ‹‹{”™={6III”””HgDÁAFùùùèºÎïÿ{¾øâ jkk©««ãøñãÔÕÕa³õü8ËÉÉáÌ™3TWWé¥VÅ4//£Gö[„ž9XˆB†…Q×oÕèèõ‡µkײoß>víÚEcc#‡ƒ¤¤$.\Èüùó{Œ›””Ä”)SسgÏ<óL Ú¬õ–žžNzz:çÏŸ—)ˆ‚%¡ÈW„¨뷣à+ÂNbbbxä‘Gxä‘Gz”yùå—»õÿ›¿ùó<--_þò—a2Ï>û¬tFaÀ!BAAQ°¡gÄz%Œº~«¤× ‚(X‚p›‘¯…Q×o éõ‚ – ‚ ‚(X‚ ‚ BQb_AA@Ä‚%‚ ‚0À Ú:X'Ñ€'èˆìÕ¯Ó >næ"z8\€3p8nwáa® ÿ°ÙÀ¡ƒS‡v½ãÚÞyîï8œ¾ŽÃ®Y§¯+¼S68<8nèy°lçyhzÝ¥êÞ,ïN×®-è°ûÀæ[ ,˜.¨ \+@a` œÛèúNJ…øÙ‚ä­ñŒ@ÃÙ.7\>ØíJÛfM³ÓÏš¦õ°…ù‡–«û²wÝ·t„§™ÖÍ|»—¯ƒð²«2v_öîâ–§­Ûz NÛ*s+}"´Ì¶Ú-¼{ê/áááý¤û²Zû»ô·`„ü]Ýøt?±Ýèá:Ø_9:›Ãzns€r‚ݶÀaw="èÜÙ%ksXål®®4M׈㠜wÊEtœçmsòäiwv¥*§œ`³îGu¹¿úÕ¯ä©,ˆK„ኔ¼Ô0º5 ZF„jÁA„þàõzÙ·oååå466‰Çã!33“¬¬,œN'¯½öׯ_Àétâv»ÉÉÉ!;;ÛL§²²’õëדœœÌßþíßZ¶ÍyõÕWY¹r%YYY=–Ã"""HLLdñâÅ̘1Ãô?vì[¶lA)eÙ÷Ðápð‹_ü€-[¶pìØ1”R(¥ˆŠŠ"%%żu“5΂Ëa³ÙˆeÖ¬Yäææâp8ÌrìØ±ƒW^y¥Û4Ö¯_Ojj*+W®´øß,ÞáÇùøãyå•WÌ} ÛÛÛyõÕW¹ë®»øñlÊVTTðöÛoó÷ÿ÷¸Ýn^ýurrrÈÉɱ¤YRR©S§X»v-¯¿þ: Ý¿ˆ(Åœ9sxüñÇEÁa(q'7êÊËF÷` bËÝS__Ï›o¾ITTyyy$''c·Ûùæ›o8räqqqLŸ>¥Ë–-#;;ŸÏÇÉ“')**"..Ž©S§†¥yüøñ^•©žxâ‰'˜2e mmm|öÙglܸ‘µk×’œœlÊDFFòâ‹/Z¬P¥iÚ´i<þøã躎×ëåìÙ³lß¾“'Oò×ý×½nb\]ש©©aË–-¸\.–.]z[Û#-- ŸÏGuu5'NàÂ… ÄÄÄðõ×_ã÷ûM%¯²²’„„ÜnwŸÓá…Ðu€‹/²qãF^|ñE\.—©<uDÁê jèd#CˆÿC¤UºaëÖ­Øív^xáËÃÕív3}út‹¬Ëå"&&€|ÒÒRÎ;¦`-X°€={ö™™‰ÝnïWy"""ˆ‰‰!&&†eË–QVVFEE…EÁ3fL¯éØívS&66–qãÆ1qâDÞ~ûmŽ;f±¼õV€¸¸8f̘ÁåË—o{{Œ;–˜˜*++M«²²’3fPQQÁ¥K—HKK3ý;ÏûJtt´yeúEFF›>+s°†Ðk¥1tŠ"È#y€zŸÌÁºÝùކ߄ææfΟ?Ï‚ úe¹0 ƒ“'OÒÒÒ¦@)¥ÈÉÉA×u:ô­Ë¦ë:GŽ1•¥ ==ÔÔT¾úê«~Å«««£¢¢ÂTxn7iiiTVVš×ŠT°¿Ïçã믿&==½Oí5’ – ŒÊ7"ŠJ‹¼@uϵk×0 ƒÄÄD‹ÿ¯ýkü~?ÐaÊËË ¸¸˜Ý»wã÷ûÑuèèèn-AN§“%K–°k×.æÎKDDDŸË´iÓ&”Rø|> ÃÀív3{öl‹Lkk+ëÖ­³øMž<™gžyæ¦é;–+W®ô¹º®ã÷û™>}:ßùÎwîH»¤§§³cÇt]ÇçóQSSCZZš¦™JçÅ‹Ñ4-Ì‚ÕÙFÁhšFRR’(XòÂ?8ÙÈ 0âÿi•>òüóÏc›6m2-€… ’••…×ëåã?fþüùx<žnÓÈÎÎæàÁƒìß¿ŸÜÜ\KØÖ­[9qâ„yý/ÿò/æùÊ•+IOO§¾¾ž>úˆGyÄÊê$""‚µk×Z,3}µÀ†aÎ×êK9 ÃàÚµkìØ±ƒ÷ߟ'Ÿ|rÀêùÂ… üñ4¯¿ûÝï’™™i™‡ÕÒÒBbb"ÑÑѤ¥¥QXXˆßï§²²·ÛM||¼%ÍÎ6 æÐ¡C\¸pA¬Q÷Â?D²‘7\aðÉ߯jd"4ýúžº1H-cÜVnx<”RÔÕÕYü;'M‡*-ÑÑÑx<<O=õ¿ûÝï?~|·Ö›ÍƲeË(,,dÁ‚–°¥K—²páÂnË4fÌ3¿ú«¿âü#ÿãüËœ+¥T¿&vSWWGBBBŸË˜˜H[[›6mbÙ²e=*•¡J`[[[˜kk«9ßiüøñ¬]»Ö ëœóåñxˆ¥¢¢‚––&Ož tÌ%‹çâÅ‹TVVv;<ØÙFÁ„*¨Ã™ƒ%‚ i¢££ÉÈÈàÓO?Åçóõ+n||<³gϦ¸¸¸G™Ù³g“””DIIIJToÊÊ„ 7nûöíû=þ|˜7nXü½^oØŒ¦ix½^¹|ù2{÷îåÝwßeúôéÌ™3ç¦ù¶¶¶âõz¹qã•••|òÉ'$&&2vìXS¦s ‡à£¶¶€ùóçsõêU¶oßΕ+W¨««ãÀ|ùå—=ZÍ‚IKK£ªªŠ+W®„)XGŽA×õ>Mp¿é߯0œ/C„Cè×V†…¡ýÆ C„w¢edˆ°{<k×®eß¾}ìÚµ‹ÆÆFIII,\¸ùóç÷7))‰)S¦°gÏs‚yèzTéé餧§sþüù›·S7 €N›6 ·ÛÍÞ½{ÉÏÏ ­­ßüæ7%A)Å?þã?šÃlgÏžå7¿ù 6›ÈÈHRSSyôÑGû´6—RŠÂÂBó:&&†´´4–-[fQ8}>o¼ñ†%®Ûíæ¥—^Âív³fÍvíÚÅ;#¦iŒ;–üàL™2å¦eHOOÇï÷3vìXËðèäÉ“ioo7—s¸å¿ 5üz¹*((”g¶ìE({Ê^„ƒµagü;±¡­‡:»Õ½{k·[ß‹ðæeµö÷Ú‹0ô%Jö"„á‹ þËr¿²‘!BaðL²2D8[F~A¬Ñ÷<º ÙÈ¡0âÿiAK„ᇔ¼d%÷¡›¯ ¢` ‚ ‚ ýWÙÁšä.‚ ‚0R – ‚ Â3hë`ýEÅØ”ªk=›£ålÅæjE9ÛPŽv”݇røÀîC9Û;üœmàlG9BÖph‡ÓáÐÀa`˜`ÃF§ë°£Ûíèv†Ý†aë:°ÛQ6'Êî2]›-Â<”r¡”¥œ œ(r¸WÇ7ɸ0T$ëPtºáÄ pNt\èFšáÂ0\`¸@w‚áDéì† »aǮ۰6œš §®pê6ºêZ6B§nàôë¸4§¦ãòk¸4?N¿—ßC÷c×ü8t­Ãõ·ãô·áôu¸v͇Mó\?v_ v_«y(­¥ùPþv”Öí­àë: _+z{[‡ëkG×üè~†æGóùðù|øÛÛñû|øý~üºŽ_×Ñ4v»N»³ãhsê´9±>øì ŸCá³+ÚœZvÚœÚv”ÝÍnœNGÓÀáˆì8ìØlNl6¶Ù\`BÙ#Á ¶ › ÃæÄ°¹Ðm.ü¶Hü¶H4[~åBSüʉ¦ø”“v›‹vå§œ¸”“(ìD+Q8ˆT6\("°"ÆPŒŽÃ€H " ƒH " ƒ1†FŒ®1ÆÐ£û‰Ö}Dë>Æ>"u.Ýßq~lZ;~½ Mkïµ¢iíhz;šîë8´64­Ýߊ¦µ‚Ötø°ûÛ°û[ÍÃåkÃåo¸mè~?ºæGÓüh~?M>?;.·U3hóë´j-šN£7tƒÜn¸7œŠF§¢É©hqØhµÛhqØhw8ð»øNüN'v‡—ÃA„½ãˆtºˆtFéŒ ÒÓ‰Ó‰#p®ì.”ÃÕáÚÎHtGº#݉fs¢Ùøí.üv'­ŽZí´8"hµ»hµ9i³;h³9Ðl"\v'vcìl6ìvl6âl6Æ(ˆ±)Æ(ˆ·[xlౌQ:QÊ RéD¢Ó¬ü\ÇOƒòÑ€ŸFÕN#íÜ Ãm3|´á§_༕V£•6£6£ÝhG3|èF;ºÑz+- ·¢ô6ìºêCa IDAT»ÞŽÝðáÔÚ‰ÔÛˆÐZ‰ÔÛˆÖÚ‰ÕÚˆñ·£µ3Fó­iDëÑšFŒ¦ãï8býmšÁ ¼ZG»]óÃUÔùમúì\õÙ®&ÍŽW³Óä·Ó¢Ù‰ó¹ˆowçsëséw¥9‰ô;ˆô»ˆôEé‹$Ò‰Ëï¦ÛPº›nGaÃöóTy* bÁ†12“vDTž4ã¬1%BDÁA„JAA§NÔ2444PPP@MM4ˆ(X°@>k•'Í8kÌNÑ[¶láÝwß ó¯¬¬¤  €ÖÖÖÛk‡ã1ÂT°¤# Ò ¤!¤B¥”wFÑ4mèë܆¼J W†ØfÏÒ‘ät¨÷‚Q4ËŽÇ+ôVw™V£·Ù›››Ù¶m.\ µµ·ÛÍ¢E‹ÈÌÌ4eÖ¯_Orr26›'N’’ÂêÕ«¹zõ*………TWWãv»Y¹reXú|üñÇœ;w¿ßORRùùùL˜0k×®ñÑGqéÒ%|>cÇŽ%//ŒŒ 3þ믿Îܹs¹ví_~ù%QQQ,^¼˜¹sçš2—.]bëÖ­ÔÕÕ‘œœÌ¢E‹,eøío˼yóX¸p¡éwùòeÞxã ^zé%<<ßDÁDƒ‘Ê“f”ð"޲NlÝñûýŒ?žE‹ár¹(//góæÍx<&L˜`Ê?~œyóæñÓŸþÔLãOú111<ÿüó´¶¶²cÇ‹e¬½½·Þz‹¸¸8~ô£CMM™{{;wß}7yyyØívŽ?Ά ø»¿û;âããÍt<ÈÒ¥KY´h'OždëÖ­¤¥¥‘˜˜H{{;6l`Ê”)|ÿûß§¾¾žíÛ·[Êqß}÷qìØ1‹‚uìØ1ÒÒÒD¹k˜›VéÒ¯СZÊ3gΰnÝ:‹Ÿ®ëæy\\œEñX°`gÏžåË/¿´(X‡åË—›×gÏžåêÕ«<÷ÜsÄÄÄ››ËþðSæÄ‰477ó³ŸýŒÈÈHÜn·žššJjj×K—.嫯¾âôéÓ,X°ÀôŸ6móçÏà;ßù¤¢¢‚ÄÄDNœ8a<öØc8’’’hlläÃ?4ãgee±gϾþúk&L˜€¦iüå/aÅŠòç% –¼~É/èpï2D(ÿ6V¨ öHzz:«V­²X­.]ºÄæÍ›Mekß¾}|ùå—ܸqMÓÐ4 §ÓiIgüøñ–뺺:âââLå `âĉ™+W®0nÜ8S¹ ¥½½={öP^^Ž×ëE×uü~?ׯ_·È¥¤¤X®cbbhjj2Ë‘’’‚Ãáè±±±±L›6£G2aÂNŸ>¦iÌš5Kžk¢` ‚ Bÿq:«t̋ꤴ´”C‡ñÈ#œœŒÓédÇŽaÙC®>=(½?*?úè#***xøá‡ñx<86nÜ–·Ífý®L)ÕïIìÙÙÙlÞ¼™•+WrìØ1î¹çžouOÂíG¾" qË„’Ê%QÒñïx…9XõÙ/^¼ÈŒ3ÈÌÌ$%%·ÛÍÕ«Wo¯s(Îëõš~—.]²Ì}JII¡¦¦†–––óÎÊÊbÆŒ$''3fÌúUþ¤¤$®\¹‚ßï·”#”iÓ¦ár¹øì³Ï8{ö,÷ÝwŸ<ÓDÁOþ‘‚ ŽˆÊ“!ÂáÖvÆí/â(þŠ011‘sçÎqñâEjkkùàƒ,JSOdddàñxؼy3555\¸pÝ»w[d2333f ï¾û.UUUÔ××sòäISJLL䫯¾¢¦¦†ššÞÿý~[¦233QJQTTDmm-gΜáÀám›9sæP\\LbbbØ0¢ – ‚ ‹/fܸqüáàí·ß&66–™3gÞ\'UЧŸ~¿ßÏþçòÁ››k‘±Ûí<÷ÜsŒ3†ÿþïÿæw¿û¥¥¥¦•kÅŠDFFòæ›o²aæNʸqãúU~—ËÅ_ÿõ_óÍ7ßðÆo°gÏËdü`²³³Ñ4M¬WCùŠp4"C„#¢òdˆp¸µ ~[üñnýÓÒÒøå/i^?ýôÓ½¦óãÿ¸[ÿÄÄDÖ¬Ycñ N >>žüàÝÆOHH`õêտί;yùå—Ãâ­]»Ör=qâÄ0¿Ðr@ÇÜ3»ÝΜ9säy& Öyò Ò ¤!„^¡ÒìC¿ßOSS%%%Ìž=›1cÆH¥ˆ‚% )ÄP8"*OšqÖØX¦Aº|ñÅ2nÜ8¾÷½ïI…ˆ‚%‚ ­’••EVV–TÄpy¥+((‹° ‚ Â"_ ‚ ‚ 0ƒ6Dxwýÿ &Î e<$‡”qàŠ2Ð Ý0Ð Ã00ŒŽ 9 ò ò¸zà¼S¦Ûk‹«ºn˜y»ºa`è=§Ù]ÙônÊÖ]¸ÞÍ}ô§ìݦCpzzð¹Þ÷²……ëFŸê±·v.ÛÍÊ®‡Ê„–kÙBËZF=¨nÞ'n^¶¾ö›Õ«Ô÷¬›£ e€Â@6e ”pï·™›†ƒéÞ4e` ¸ª;™Î´݆ßî²uºäm¶¾—-8¯ÞòésÙU?ËŽ5nê54ü¦eïö^Ãë±×²Û¾]½v†w¦XÜu» ä©,ˆk(¼òî°(oÐÿÂpï|R­üvIßQ°†0ý]9wÐËô¿0Ü;ŸTp+¿]Ò÷A¬!ý(,aPS¾åo—ô½Þزe ï¾ûn˜ee%´¶¶J'DÁº½obÁé©#Í(šÀ-ývIß-/ÖÂèAÖÁA†=ÍÍÍlÛ¶ .ÐÚÚŠÛífÑ¢EdffÐÐÐÀ믿ŽRÊò2ž––fn¡sáÂvíÚEuu5cÆŒaÆŒäææâr¹¤‚Q°A„ÑA°¢ä÷û?~<‹-ÂårQ^^ÎæÍ›ñxø¯×k†_¹r…Í›7óï|‡¤¤$¼^/^¯—––¾óïpñâE¶mÛFMM W¯^åÔ©SlÛ¶M*WøVˆk0Ë‹X°FNç“fnå·ëûžÀâÅ‹©¯¯çøN§“¹sç2sæLsÒË—/ã÷ûÙ»w/{÷î5ãMž<™ÿøÇ¤¤¤°fÍvíÚÅ[o½…ax<fÏž-•+ˆ‚%,aÐ4iFÑBoé·ë”,cä+Y?þx·þiiiüò—¿4¯Ÿ~úéÓÈÊÊ"++«×|ÆϳÏ>+RlR‚ ‚ ¢` ‚ ‚ ˆ‚%‚ ‚ – ‚ ‚ð­´Iîgܿ꺸8ŽJƒÜÝNŠU€]jç6È'`CÐ*¡`W„Q…X°AA˜A³`ýKÁ¯Bô<p€Ót•rv¸†r‚r‚rrb({G8öŽså[G¸asd®­3­Ž£Ã¿ó蔳™2fz™Žp{е½C^Ù[àºKÆè S¶ ðàü;ãÛ0” M®@ :üAç:á: )Ã"«®;º®»Âƒ\:ÂÀµ¸î ïÌÇr5[ ý 8†év„fxPÚ=ȇ‹šÑiбXª2¸»ª³ÃU ì†E&ôÀâ]aö 4ÝO©nâ«îå:\£#Žê’SÊO…ÆW]Ÿä›áª£lUp•AÇJi]Gø57TÆj+ë ë0«t¤o WaéaIÇšV—_gZ˜áXÃCýU÷r]çÊz­]ÿwU×5¦W!a„Äí>¼³¬*,]˽†Æï%¿àpÂä{ë¦C±äAòê6}Õý½…ÅW=Ç O;äZõTvX÷þ<•ûÀúõëIMMeåÊ•}ŽSPPÀÓO?ÍŒ3¤Gº‚%£%£ƒ7¯#©é·¶lÙBkkkØZW•••¬_¿žW^y…ÈÈÈÛ–SS{ö졼¼¯×KTT©©©,Y²„I“&õ)cÇŽ±cÇ^yå•[’ûáˆÝ>°:îT=Þ*_}õ‡¦ººš––Ö®]Kjjª(X‚ ‚0ðJßí×úþô§?¡ë:O<ñn·¯×KEEÍÍÍ}Nc ÃŽŠŠðû3 ãŽÔã­ÒÞÞÎ]wÝÅìÙ³ùàƒ†tYEÁ„Aâ–Vï-u„X±¤ßö’’N:ÅÚµkM¿²²2ÊÊÊxùå—M¿#GŽpðàAHHHàþûïgþüùݦÙÚÚJUUkÖ¬aòäÉÄÇÇ3a‹ÜÁƒ9zô(õõõDEE1}út–/_ŽË墲²’ÂÂB”R ”bÉ’%<ôÐCý¾ÇÐ!Â7nPTTDEE±±±äææR\\LNN999f¼ææfÞ}÷]Î;Gll,+V¬`úôé444ðöÛo£”âÕW_E)Åœ9sxüñÇY¿~=)))8>ÿüsìv;óæÍ³”»µµ•>úˆÓ§O£iãÇgÅŠ¦e©¦¦†;vP]]RŠÄÄDV­ZÅøñãihh`Û¶mTUU¡in·›åË—3mÚ´nï}Μ9444 ùÝ[FÖ^„È;·­baØüµ)ù»e¢…ü6Ù'NPRRB~~>©©©\¾|™>ø—Ëe>Àƒq¹\¸\.N:Å„ p8=Ô‘âÑG%!!úúz>üðCvîÜI~~>“&MbåÊ•”””ðâ‹/b.—k@ê`óæÍ´´´°fÍl6}ôMMMarŸ|ò Ë—/çá‡æÐ¡ClÚ´‰ø‡ ..Žþð‡lܸ‘—^z —Ë…Óé4ã?~œx€çŸž‹/²eËîºë.222ظq#.—‹gŸ}–ˆˆ>Ì;ï¼Ã‹/¾HTTï¿ÿ>ãÆã»ßý.J)jjjÌ!Î?ü]×ùÉO~‚Ó餶¶vÀêE¬~ÛÄŒ0lªJþÚnâ1ü:ìp(q§åj0ûí™3gX·nÅO×õoeåZ±b…9á;!!ÚÚZ>Ü­‚e³Ùxâ‰'(**â³Ï>cܸq¤¥¥qÏ=÷’’bÊ[‹X¶l[·n%??»ÝnÎm3fÌ€ÕImm-çÏŸçg?ûãÆà±Çã·¿ým˜lVV÷Üs¹¹¹:tˆ¯¿þš©S§šÃŽÑÑÑas°RRRX²d ‡O?ý”óçÏ“‘‘Á… ¨®®æŸÿùŸM¥éá‡æÔ©Sœwîû÷ï§®®Ž¶¶6t]GÓ4|>ŸÅ"4\½z»Ýn*W Lwó´‚•A—ËEDDD·–®ÞâÄÄĘñ®\¹B[[ÿú¯ÿj‘ñûýÔ××ðÀPXXÈñãÇÉÈÈ`Ö¬Y¦’uÿý÷³uëVΞ=k†…æ' ÖP° V¬!dFF|§!ÂÑTâ¡ð2àt:Ã,!å /¨¦i :¬<¡s¨l¶Þ—†t8ddd‘‘ÁâÅ‹)**¢¤¤„¬¬,êëëÙ°aóçÏ'77—¨¨(ªªª(**BÓ´Û¦`õ‡ÐûSJõiˆµ·xíííÄÆÆ²fÍš°´:Ö‡zˆÌÌLÊËË)//§¤¤„'Ÿ|’3fÍÔ©S9s挩 ®X±‚ ˆ‚5ÔÞv1# ›‡ª Þ&ÅC†oK‡Àa_ˆŽŽÆëõZüjjj,Ö—ØØXêëëÉÌ̼¥¼ÆŽË©S§¸|ù2†a°bÅ 3ü‹/¾°ÈÛíöŸ˜=vìXt]çòå˦ëêÕ«´´´ô+Îá½þ–oܸqx½^”R½ï%&&’˜˜HNNþóŸ9zô¨9DǼyó˜7oÅÅÅ9r¤O ÖPÿêQ†A„áý¤¤¥¥±mÛ6öïßϬY³8{ö,gÏž%""”Yºt)Û·o'""‚©S§â÷û©®®¦µµ•x ,ýææfÞ{ï=î»ï>RRRp¹\TWWsàÀSIðx<èºNYYÓ§O§ªªŠ#GŽXÒIHH ½½óçÏ“ššŠÓéìѲ¥ëºE1ìT‚’’’¬ôôtŠŠŠXµj6›?þ§ÓÙ/$>>€Ó§O3mÚ4œNgŸ&›O™2…I“&ñî»ï²|ùrill¤¼¼œ™3g’””ÄÎ;™5k 466R]]ͬY³Ø±cS§N%11‘––*++Ãî1˜––®_¿Ncc#†aPWWg*Î111¢`Ýη]±b 3‚ÐG‹€üµ ´eG†oKÕÐí·ÁŠDRRùùùìÛ·½{÷2kÖ,.\hQv²³³q:”––²sçNœN')))–IêÁ¸\.&NœHYY×®]C×uâââ˜;w.‹- 55•+VPZZÊ®]»˜o¼ñ†ÅÏívóÒK/…É~ï{ߣ°°·Þz‹˜˜rssùæ›o,_;ÞLÙŠ‹‹céÒ¥SXXh.ÓОyævíÚEaa!MMMÄÄÄ0yòdbbb°Ùl477³yófšššˆŽŽfæÌ™æ}ëºÎ¶mÛhll$""‚iÓ¦Y¬€¡œ>}š-[¶tì° ›6møÖK^ÜÖ~YPP0(:‰l•#[åŒö­rBÏe«œàseõ“­rºuc«œ®od«œ¡Êõë×yíµ×X½z5éééR!bÁA¡¿TTTÐÞÞNrr27nÜ`çθÝnsQTA¬[7Ç!C„·­b…G†ûPGÈá)£ ývd¢i»ví¢¾¾žˆˆ&MšÄ“O>yÓ¯"Q°úõc, ™§œ0â;…tØÑTbQ¬†.S§NeêÔ©RC Qoa-B/u$U ýVDÁAA`¿"AA©ˆKAa€´Iî¿ø_ÿ èZ7Ì È….Ûo^‡È÷.;Àá}–§×ðÀ²D!÷ßûuWZq@:=Õ[Ïént甞a`aa` 7¯ƒÂ ó·º]Ur¯ÖªêöÚâ­Éd¨8ªËß Ž,G7ñPáé™®²º†êÞßLÏ<:ï¾Ëíú§[ t‹œÕ_ï!\r;ÃuK¸åP]ç˜n7rh*4MкÂMW³Èc‘ï”n®Ž×¹—B7×÷²)[ç9õÈÌõ¿À†›2‚ÒèŠcÊ…Û®{ ¯ÀjwfÞ¶ k{ ¼3ž¢KÆn†e๶a˜åQ†M7Pz‡ÛQ5F Z ð uJ „kF‡L§¿¿CNiùàë 8aáþàtè¸ö¿òé²¾À¹F <]²]®~¬eòÐÞnéü/ÈS¹¬_¿žÔÔTV®\Ùç8<ýôÓæÊóÂV°„Pd‘‰QÙäÒ?¡OlÙ²…ÖÖVž~úi‹ee%ëׯç•W^17¾455±gÏÊËËñz½DEE‘ššÊ’%K˜4iRŸÒ8vì;vìà•W^¹%¹þð‡æÞŪÇ[AÓ4vïÞMyy9õõõDFF’‘‘A^^±±±¢` ‚ €¾«ÜOÿô§?¡ë:O<ñn·¯×KEEÍÍÍ}Nc 6zŽŠŠðû3 cÈožìóù¨©©á¡‡"%%…––¶oßΆ xá…DÁzìÞR£±É•ôAHJJJ8uêk×®5ýÊÊÊ(++ãå—_6ýŽ9ÂÁƒihh !!û￟ùóçw›fkk+UUU¬Y³Æ\=>>ž &Xä<ÈÑ£G©¯¯'**ŠéÓ§³|ùr\.•••¢”¢  ¥Ô·Þ?/tˆðÆQQQAll,¹¹¹“““cÙ_±¹¹™wß}—sçÎËŠ+˜>}: ¼ýöÛ(¥xõÕWQJ™{®_¿ž””Ÿþ9v»yóæYÊÝÚÚÊG}ÄéÓ§Ñ4ñãdzbÅ RSS¨©©aÇŽTWW£”"11‘U«V1~üxضmUUUhš†ÛífùòåL›6-ì¾###yöÙg-~>ú(ÿùŸÿÉõë×Í «EÁºMo1Áo 6Ÿgèz>;‡r™ea¤wC%Z¡–—þrâÄ JJJÈÏÏ'55•Ë—/óÁàr¹˜3gN˜¼ËåÂårqêÔ)&L˜`ÙD9ôô裒@}}=~ø!;wî$??ŸI“&±råJJJJxñÅ1 —Ë5 u°yófZZZX³f 6›>úˆ¦¦¦0¹O>ù„åË—óðÃsèÐ!6mÚÄ?üÃ?ÇøC6nÜÈK/½„ËåÂétšñŽ?Î<ÀóÏ?Ïŋٲe wÝulܸ—ËųÏ>KDD‡æwÞáÅ_$**Š÷ߟqãÆñÝï~¥555æç‡~ˆ®ëüä'?ÁétR[[Û¯zimm5•/±`ÝÁ?4c8h.Æ0Ô´dïAxbɬaõ[t;»¡1zÿ´Îœ9úuë,~º®++׊+Ì ß ÔÖÖrøðán,›ÍÆOHbb¢™F'Ìš5‹äädÜnwŸïÝï÷S\\Lff&¢` ÃQkÿ‰%ýCÒÓÓYµj•åeúÒ¥KlÞ¼¹Ïi´··síÚ5 )**²(j½YAfΜɴiÓ¨ªªâÒ¥K”——SZZÊc=FVVçÎcÿþýÔÕÕÑÖÖ†®ëhš†Ïç³X„’«W¯b·ÛMåªSénžV°2èr¹ˆˆˆèÖÒÕ[<€˜˜3Þ•+Whkkã_ÿõ_ÔŸúúzxà 9~ü8Ìš5ËT²î¿ÿ~¶nÝÊÙ³gͰÐüºCÓ46n܈RŠüüü!Ù_eˆp(X1dˆp¬B‚0 »á("t:aŽÆÆÆ°ç@wã`«ÓÊ:‡êf›#;222ÈÈÈ`ñâÅQRRBVVõõõlذùóç“››KTTUUU¡iÚmS°úCèý…>3¿M¼öövbccY³fMXZ ëC=Dff&ååå”——SRR“O>ÉŒ3ÈÎÎfêÔ©œ9sÆTPW¬XÁ‚ zU®Þ{ï=Y½zõ´^8K†ït™‡»Uh8=…eˆpXý!C„ƒEtt4^¯×âWSSc±¾ÄÆÆR__Offæ-å5vìXN:ÀåË—1 ƒ+V˜á_|ñ…EÞn·Ø—„ÁeÐuË—/›V¬«W¯ÒÒÒÒ¯t:‡÷ú[¾qãÆáõzQJõ:¼—˜˜Hbb"999üùÏæèÑ£æm\\óæÍcÞ¼ysäÈ‘¬N媾¾žÕ«Wß–/*L¡•ŸBAa¤¼\§¥¥ÑÔÔÄþýû¹víŸ~ú)gÏžµÈ/]º”}ûöqèÐ!®^½Ê•+W8zô(ì6ýææfÞ~ûmNœ8Á•+W¨¯¯çË/¿äÀ¦’àñxÐu²²2êëë9~ü8Gޱ¤“@{{;çÏŸ§¹¹ŸÏ×ã=éºNMM娭­íVÁJOO§¨¨ˆ¯¿þšË—/³uëVœNg¿–]èüïôéÓ455™–¾›1eÊ&Mšd~ØÐÐ@UU»ví¢ººŸÏǶmÛ¨¬¬4ê««IJJ`ÇŽœ={–úúzª««©¬¬4úS®6nÜÈåË—ùÞ÷¾‡®ëx½^¼^¯ÅJ9T!¡`Å0†ºYhS†Ó¡ÌÁ±Èáÿíï$))‰üü|öíÛÇÞ½{™5k .´(;ÙÙÙ8NJKKÙ¹s'N§“ŒÐãï IDAT””Ë$õ`\.'N¤¬¬Œk×®¡ë:qqqÌ;—E‹ššÊŠ+(--e×®]Lž<™¼¼<Ëü°I“&1oÞ<þüç?ÓÒÒÒë2 >Ÿ7ÞxÃâçv»y饗Âd¿÷½ïQXXÈ[o½ELL ¹¹¹|óÍ7–¯o¦lÅÅűtéRŠ‹‹),,4—iè Ï<ó »ví¢°°¦¦&bbb˜ý9H#®ËÈzFÒF2%%%œ:uеkך~eee”••ñòË/›~GŽáàÁƒ444Àý÷ßÏüùó»M³µµ•ªª*Ö¬Yc®ŽÏ„ ,räèÑ£Ô××ÅôéÓY¾|9.—‹ÊÊJ QJQPP€RªÛ½ÇŒÃÝwßÍÑ£GìcÇŽ1cÆ ¢¢¢(--åøñãf~3fÌ //—ËÕí}¼ÿþûøý~~ðƒ˜~Û¶m£®®Žçž{ÎT÷íÛÇ矎×ëeìØ±,Y²„™3göXçYYY\»v­OJ¤(XÂðzƒäÄå-êbÊØò·ÖI¿Åýĉ”””ŸŸOjj*—/_æƒ>Àår1gΜ0y—Ë…ËåâÔ©SL˜0!lèά"¥xôÑGIHH ¾¾ž?ü;w’ŸŸÏ¤I“X¹r%%%%¼øâ‹†Ñ£"”Íÿ÷sýúuâããMååÂ… <û쳦œÝn'??ŸøøxêëëÙºu+J)yä‘o]ŸŸ|ò 'Ožä±ÇÃãñPQQÁ¦M›X½zuŸ‡BQ°†ï/Ê~ñíƒÉCÞY†O—exÿPzŒit(YCyÖ0}ž9s†u!áu]ïw:%%%¬X±‚3f@mm-‡îVÁ²Ùl<ñÄñÙgŸ1nÜ8ÒÒÒ¸çž{HII1årrrÌó„„–-[ÆÖ­[ÉÏÏÇn·›sÄÆŒÓkù¦L™Bll,G5-\ÇŽ#>>žŒŒŒó[ºt);vìøÖ –Ï磴´”5kÖ0~üxî»ï>.\¸ÀáÇEÁkب-‚t™Ñúœä'ç[‘žžÎªU«,V«K—.±yóæ>§ÑÞÞεk×(,,¤¨¨È¢¨õ6I~æÌ™L›6ªª*.]ºDyy9¥¥¥<öØcæÙ¹sçØ¿?uuu´µµ¡ë:š¦áóùp:}.£ÍfcΜ9;vŒ‡zÃ08~ü8ÙÙÙ¹³gÏRZZ–Ÿ¦iØíö~×ïÕ«Wñù|¬_¿Þâ¯iZØp¨ –NîPâòÀ"¼Ý8NÜn·Å¯±±ÑZÌn&¼kšfQ°{ì±0¥áf›#;222ÈÈÈ`ñâÅQRRBVVõõõlذùóç“››KTTUUU¡iZ¿,è°íß¿ŸŠŠ t]§±±ÑTä cÈpÆ ÜÿýäååIee%[·níQÁêkÝ<ûì³aV¶ž†EQ°†ï/ÊPxñ•!ÂÕeDÄ?”cÊá@×ëµøÕÔÔ˜ç111ÄÆÆR__6‰¼¿Œ;–S§Npùòe Ã`ÅŠfø_|a‘·Ûí}ž3æñx˜ûöícï޽̚5‹… Z”ììlœN'¥¥¥ìܹ§ÓIJJŠeÒx0.—‹‰'RVVƵk×Ðu¸¸8æÎË¢E‹HMMeÅŠ”þÿì½itÇ™¸ÿTwß«õ¢« $±‹ÅÄf°1bÇ€!²ì0 ‰c3¶3^2É8sf&Ç&cýó“ü2gÆ“sœÌ™33! ãx‹ÁØ`Œ‘˜ÅÆ –0ÈHl„$„„–{{ù¸W­{µ66ZÞçž:Õ]õvUuÕÛÝoWõ­Ú½›íÛ·3räH/^õ}ØðáÙ1c¯¾ú*ÍÍÍ]NÓInn.[¶lAÓ4×k#++‹%K–PRRÂ;ï¼Ã¨Q£X¼x17nì6½ñãÇ“ŸŸÏ¶mÛ°,‹iÓ¦1yòdjjj\™Å‹“˜˜HII uuuÄÆÆ’••åžgW”••¹† RŠW^yÅ5dçÎ;pï·j±ç¾±TŽÑAF–Ê‘¥r®±TŽÞA¦.•Ó.#Kåôº¥rÂËÜÈR9‚Ð÷‘ÅžAAÄÀú&‘!ÂëM\†œúŽÊÈzßU(C„‚ –0šÎ-Î_úùUèˆe.b`É© ôm•­–ÆA ,AA„¾÷¾t«þE(‚ ‚Ð_‘,AA1°AAz7·l&÷ùçmt\—ÉqÚ]d¼O×ñQÛ]íÓ!-®µ­:ïGÉ«á]øãÃÛŽë·9åžB—Õ³#eÂeqº‰·èê‹ôíHÚýÈøŽ2Qq2‘ûNľÓQ.âX§£S«©ãÜŠ®¯uŽ’œÈ4<Á§{ŒÖ…\™.㵈øH9­Ý¡u½•ž!žhÔQm~hBP '4%l‡};ì;d:Æ·§Ó}¼å:"¶Û÷#å"ó1Ã2vT ‡‡âÛdÚÃpËÓ^®6šä4œðD¢zľÞa¿ÍÍUëD¹Žié×HÃËD§C8üÚòZx»Ío;ΈH#ò˜69ÝqÐlÐlÇu¡*ðM è€é„ö­ð…d…ãƒa3,¹oEÈYDÄGÊtŒÈ7ÒEÊZ#dÚÒŒJ«C|H¥ÏSË/~ø¬<•o"ü1ï¾û.?ûÙϤ2’%_'½yÍ\iA¸q6nÜHKK «W¯Ž ¯¬¬dݺu<óÌ3ÄÆÆ~mù_½z•;vpüøq‰‹‹###ƒyóæ]÷ÂÈ¥¥¥lݺ•gžy¦[™>ø€;wòÿøFô#: ò¯ÿú¯,\¸Y³fõŠv1M“÷Þ{'N¸KëŒ3Æ]rG ,AAè«Æ»úúç·x饗°m›û￟ääd9yò$MMM׆ã\û#//íÛ·SVVƤI“¢âŽ=ŠeYÝ.º|+\¼x‘ùóç3dÈš››Ù²e /¾ø"?üáÅÀ„þ†ô”H›“ââbÊËËyòÉ'ݰ½{÷²wï^~úÓŸºa`Ïž=\¾|¿ßϬY³¸ãŽ;ºL³¥¥…S§NñÈ#0räH’’’:th”Üž={8xð uuuÄÅÅqÛm·±dɼ^/•••¼þúë(¥(**B)ÕåbÏ Œ?žƒv2°JKKÉÉÉ!..€Ý»wsèÐ!7¿œœ/^Œ×ëíò<^{í5LÓä;ßù޶eË.]ºÄÃ?ì%%%|üñÇ466’––ƼyóÈÍÍí2Íøøx~ðƒD…-_¾œÿýßÿ¥¡¡ŸÏ'V?yéðMWo”è¸@êW+b_vQ}6qAzáîÜøððáóbÅ 222øâ‹/xã7ðz½äååu’÷z½x½^ÊËË:th§¡»öGâÞ{ïÅï÷SWWÇæÍ›yçwX±bÇgÙ²eó“ŸüÇqº5„¦M›Æ /¼@}}=IIIÔÖÖRUUÅC=äÊéºÎŠ+HJJ¢®®Ž7ß|¥Ë—/ÿÒõùþûïóé§ŸRPP@JJ 'Ožä/ù kÖ¬¹î¡Ð––”R_ë­Xßü•ÖÞ™³H}å"öÅ^ç¨^ùÞ§wÖÒ&ÂWá³Ï>cíÚµQa¶mßp:ÅÅÅ,]º”œœü~?ÕÕÕ|ôÑG]Xš¦qÿý÷³iÓ&>üðC2335j'NdÈ!®ÜwÞénûý~.\È›o¾ÉŠ+ÐuÝ58z,ߘ1cðù|ø# ,€‚‚‚NFƒ¦õ<5¤adgg“ÍܹsÙ´iÅÅÅL™2…ºº:þüç?sÇw°hÑ"âââ8uê›6m²¬îÍ™:u*»víâäɓض͕+W\CBC†þóŸ™5k‹/&66–ÊÊJÞ|óÍn ¬ë­›‡z¨S/[wâ‘é¼ôÒK466²fÍšn‡?ÅÀ곯È2D8à{2dˆ°WÖÒ&Â×M|||˜ .PWWÇÑ£GùàƒÜï¸RRR°m›½{÷RWWÇ¡C‡8pà@T:~¿Ÿ@ ÀçŸNSSÁ`°Çóš6meee”——wLIIÁ²,öïßO]]¥¥¥®1Ö£GæÌ™3>|˜ššÞ{ï=.]ºäÆÇÆÆr×]wñÖ[oqèÐ!jkkùâ‹/Ø·o_·Æ›eY¼øâ‹\¼x‘x˲hll¤±±1ªwl "C„·ä=>—!¯%qé)é…A©ák»õ·ßUÒÓÓY±b%%%ìܹ“ &0{öì(cgÚ´ix<vïÞÍ;#ÇãaÈ!QGâõz6l{÷¶Û¶4hÓ§O'??€ŒŒ –.]Êîݻپ};#GŽdñâÅQ߇ >œ3fðꫯÒÜÜÜå4 ‘äææ²eË4Ms ¹6²²²X²d %%%¼óÎ;Œ5ŠÅ‹³qãÆnÓ?~<ùùùlÛ¶ ˲˜6m“'O¦¦¦Æ•i› ´¤¤Ä84++Ë=ÏŽÔ××»ìï~÷;×àUJñÈ#0bĈ«—EEE·ä¾'Kå Kåðõ-•ÓÕ’9²Tέ]*‡YY*G–Ê‘¥r„þŽ  ‚ ‚ ˆÕc?qÇ€ÞXÈh_ÝœÔúT3}‰ËpTïCÚD1°ú켯ü‹0—ÊS\AKú2–´‰ ‚X‚ ‚ ýé¥òVý‹PA¡¿"=X‚ ‚ 7™[6Ñh°è_€Ðì=‘óµÍ„c† Ï”ƒ;ÃŽ Q³æ´ÇµÛ•oEȵcFÅwÎ+òx»mΣÐä5 )ÂØ„}‡ðd9aa_‹oóuÕ×v¬Þ…œ!«w#¯uÈ/2®S¾]Èå#ʦFD~ÝotÈ£+§uqnšCô„8V‡–éÐâªC+:f§–ì¬VÚev¡Ö5ò²ºÉ««ãÍ.ÎÅêp®áÊŒ.·c÷¬ÍÊê¢l‘sI9=6AWêkt£‚Ý©«Ñƒ t¯Ò‘*¤uP½Žê§wPw½ yMuPKçÆórå:ëæöµªêX׸Ñ\ï© õQÖu¨ÄµÔï:óÂìAÝ»8‡(uµo¼”E§9ñÏEòA¼qãFZZZX½zµX%b`}³ Œce¥6A„îØ°a‡bÆŒ¬\¹2*nóæÍ|øá‡L™2…¯­ •••¼ÿþûœ?Ó4ñù|Œ1‚o}ë[èºNii)[·nå™gž‘úÙR9‚ Bÿ|UФ¤$Ž9²eË0ŒÐãË4M>ùäü~ÿךuu5ëׯçÎ;ïdùòåx<jjj(++s×B¼Þ…œ1°z CeåÂAè‰ÌÌLêêê(++cÒ¤I”••á÷û;X'Nœ`çÎ\¼x¥ÇgÙ²e¤¤¤¸2W®\aÛ¶mTTT`š¦»ŽáСC;å]QQÏçcñâÅnXrr2cÇŽB½[¯¿þ:J)ŠŠŠPJ1oÞ<”R=z”ýèGQéýîw¿#''‡ t~8»víâÀ466’––ÆÜ¹s™0a‚(X½æ§.ª—×Û8Ío´ÎTÿ:½>:¢-sqõý»íÔ©S9xð k`ù„o}ë[¤¤¤PUUÅk¯½FBBB—y b`Ý‚›bo~8ά7=±œ›÷(0sNÿ®ˆŽ*áôÏVzÝNš4‰wß}—úúzÇáôéÓ¬Zµª“Õ±·§  €_ÿú×\¼x‘Áƒsøðašššxâ‰'ˆB=RÝ1aÂ***X·n 6Œììlòòòˆ‰‰A×u7„„÷8¯×˘1c8xð k`>¾“\MM ;vìàìÙ³455á8J)êëëŸÇqxþùç±,+ôð3¾ÜãÏçó1yòd&OžÌÂ… ùÍo~ÃG}Äüùó»=æ¶ÛnC×uÊËËÑ4 Û¶»ýž*ðàƒâóù¢؆ü/M ¬^sS”!›S¶¯˜Ô€@†ûC+ }£nÇŽ‹eYhšÆ˜1c:Å755QSSÃ}÷ÝLj#¨ªªŠ’2d¤¹¹™¸¸¸/UŽØØX|>ŸkéºÞå7\𦑗—ÇÁƒÑu‰'vk,¥§§cõõõ2(– ‚ |shšÆüãЫ©êü2G||< 11‘Ë—/³}ûö(ÙI“&QRR‹/¾È¢E‹ðù||ñÅ 4Èýö)’>úˆóçÏ“››Krr2¦irèÐ!ª««¹÷Þ{ðûý>ÿüs222ðxÍ›o¾ICC^¯—Áƒ³zõj·§iøðá̘1ƒW_}•ææfæÍ›ç¦¦¦2|øpš››»œ"’… ’À®]»¨««#66–ÌÌLòóóEÑúÒ5q«{¾‘¥r¢W[è¯KåÐõú#²T²TŽ,•#Kå Kåô~ó›ß0sæLî¼óN±>¤KA„¯ÂÕ«W9räL™2E*D ¬Þƒ Þê²õޤz72DØZIøju+"èš_ÿú×$$$PPPpÝÓBb`õ[Óì›)›êÿe“öŠ R·_š¢¢"©„ˆ&or–‚ ‚ @KA¡/qˆ=E¿Úÿ&°€TÃÍCž°¾Uu½‰¯Ïš¨ª ÜÊKPAA¸‰Ü²¬óÿôÔ—:N©º@Óh*€a5£[Íèf3†ÙŒa]Å0Ûn7cØÍvºÝ‚ÂDsLA4L4ZÑ  @SA” ͆¥”Ò,”nµû†ª1Ó>ñ ”¶©ã ì€4pLÇÔp,ÇÒ°Zc°1®o›lËÀ±<ئˆÇlÇ ÄaB.öÍL3ˌòâ1ÍX,;ÓŠÁ´b°l0íf,» ÓjÆt®´:íL Òˆv-رU› ¦Ö„¥5ajM˜ª KkÅÖZC¾ `él-ˆ­°õ ŽŽæ¸s‚)Ã@éša t …2èZûCÊ Í#¦Ù8ºá[ ™8ÊÍB)…R:šÒÑ”®¼hÄ¡v1èÄ …· ;ÃIÀ°0œtÇ‹f{ÑœóX>ªÙObF ,pݘ1cøÅ/ddC,Aè;ÈóMAKAA ,AèÝÈ=‚ЧٰaEEEìÚµ+*¼¼¼ü†æ˜Z·n[·n• ÄÀúêÈØ BŸ¿“+…Çãa÷îÝ´´´tŠû¦±,KE¸&2“»0îÎR‚Ð×ÉÎΦ¶¶–’’–,YÒ)¾©©‰-[¶PUUEKK ÉÉÉäçç3iÒ$6nÜHee%UUUìÝ»¥O?ý4'Ožäy5d¸ IDATí·ßæ™gžqÓ*//çÅ_t{ÇŠ‹‹)//gæÌ™ìܹ“úúzž}öYÇa×®]8p€ÆÆFÒÒÒ˜;w.&Ld±ga v¶ ôý÷$¥X´h¯¾ú*³fÍbРAQñ¦i’••E~~>^¯—ãdzaÃRRR:t(Ë–-£¦¦†Áƒ³`Áºíë^[[KYY«W¯vãJJJøä“OøÖ·¾EJJ UUU¼öÚk$$$0räHi41°¤ëBAèýäää‘‘Aqq1Qqƒ böìÙîþÌ™39qâGeèÐ¡ÄÆÆ¢ë:‡ÄÄÄÎÛ²,î¿ÿ~âãã]ƒ®¤¤„5kÖ0lØ0’““9uê}ô‘X‚  áÕWª@ú K–,áøC”1`Û6%%%=z”††,˲,<ž›3•½ßïw+õhƒAþøÇ?v2Ä233¥¡"Aú#GŽdìØ±¼ûî»L™2Å ß½{7ûöícùòå <ÇÃÖ­[¯ùAzWC„]ÓÑP B‹:=øàƒø|¾è«!VA†…€td B¿bÑ¢Eüçþ'©©©nØéÓ§ÉÉÉq?jw‡ššÒÓÓ]]×qœèB||<­­­ƒA׈:þü5ËžžŽaÔ××Ëp 0 ,y² bg BcÈ!Lž<™}ûö¹a©©©|úé§œ>}šØØXöìÙCccc”å÷û9sæ —/_ÆëõϰaÃðx<¼ûî»Ìš5‹3gÎPZZzÍ2ÄÄÄ0{öl¶nÝŠmÛŒ1‚ÖÖVN:Ell,yyyÒPb` ‚ BßbÁ‚9rÄÝŸ;w.uuu¬_¿ÇÃôéÓÉÍÍš7köìÙlܸ‘çŸÓ4yúé§ñûý<ðÀ¼óÎ;|üñÇdgg³`ÁÞxãk–aáÂ…$$$°k×.êêêˆ%33“üü|i A†…€td BŸ¦°°°S˜ßïçç?ÿ¹»ÇêÕ«{L'55•Ç{¬SxNN999QaÓ¦Ms·çÏŸÏüùó»LsÖ¬YÌš5KIèD?ŸÉ]ž¬‚ ‚ ˆ%7éÈAÄÀAAèãïöEEE2Ž&‚ ‚p‘,AA„›Ì-ûáæmkA):: Xtâ0ÂNsbЉAÃrN :^´p˜îh´ýtG¡ÓŒá4£;}ÝiF#ˆæºñƒüš@wZÑœVt'ˆF @9 …rŽ…æî›(À û °Ãûa_9 œ¶jŽR TØ×p”†£…üоîõ¨m[!_ó`k^l݃­{±µ,=[ûš'Ây»ð£ÃL#KÃÒã0õ8lÍ‹–³4¦ÀÒƒ˜zK`êÍXZ–ÞŒ¥µà( +䫦֌¥7‡|Õ‚­‚Ø*v-XZ ¦ ÅYª›ÖÐ6­!Y‚ؘ8q‡Û1±åg;&ŽštÐq”­£=äÛ:8*äl å(°ÊlE¸©\çØØX!ß±ÇÆ±íоmeâX!_Yš­£YÊÖÑlå(Û@9:šíAsBaší ùŽÇuºrv ºº>œÐ5ºV¼í×ÞˆýtåESíÛº‹®â1´8t-]3Д†®©¯·bèÍèzsÈ7š1Œ¯ëÍ(ÍBSJY(ÍB7ZBÎò5=ˆÒƒhzÍ¢{ZÑ<×¹`ÈA”n¡ +äëJ·ÃûvxßÝF6J³AsÚ}Û°;Ô¶mmæØZ¸M•»í8ZÈÙŽc`;FØ×q0pб mÛÊ‹[y±TøúQ1ØZ ¶òà¾æÐB×€¾>bÝë"tÝy°´8,-S…üP¾ºëÊG‚-5µœÝ¹›sïïâìÎÝÄÖ7°`Á×3†_üâòT¤Køj¨~wO•/Éû½ÎJ‚ ôþ¬Žãô7#K>åë÷:+F–p‹©¯¯gÇŽTTTÐÔÔDbb"999Ì›7/j!fAk ÷H– -,×M]]ÿýßÿMZZ«V­Âï÷S]]ͶmÛ8qâ?üቋ‹“ŠÄÀð½°K!ý\}½…¿V•U½Ó¨òw°yóf ÃࡇÂ0B®¤¤$222øÿøÞ{ï=V¬XišìرƒO>ù„«W¯’””D~~>S§NàÂ… ¼óÎ;œ:u ÇØ1cX¶l™ÛvâÄ vîÜÉÅ‹QJ1|øp–-[FJJ —/_æ¹çžã»ßý.ûöíãìÙ³¤¤¤°råJ†. %¸È7X‚ B¯¦¹¹™ŠŠ î¸ã׸j#11‘É“'»ënذ#GŽpï½÷òãÿ˜‚‚¼^/---üñ$++‹'žx‚‡zˆ«W¯òÊ+¯¸éîºë.žxâ Ö¬YƒRŠ—^z©S™Þ{ï=î¾ûnž|òIRSSùË_þ‚mÛÒX‚X‚ Bß ¦¦ÇqHKKë2>--––Ξ=ËÑ£G),,$''‡äädFÅí·ßÀþýûÉÌÌdáÂ…¤¦¦’‘‘AAA'Ož¤¦¦€ &››Krr²áÂ.^¼•çìÙ³7n©©©,X°€úúzjkk¥±"Aú—/_FÓ4FŽÙeüùóç9yò$k×® WJQWWGjj*555ìØ±ƒ³gÏÒÔÔ„ã8(¥¨¯¯gðàÁî1C† q·q‡«W¯vk b` ‚ B¯"%%¥ÕÕÕääätН®®&66¶ÓðaG·ÝvK–,Áq¢¿õù|¼ð $''SPP€ÏçÃqžþy,ËŠ’×4-Ê@:¥) ldˆPøÆ[p£ âˆÞ @||<ÙÙÙ|øá‡˜¦×ÐÐÀ'Ÿ|Âĉ2dŽãPYYÙe:™™™\¼x‘¤¤$RRR¢œÇã¡©©‰ššæÎËèÑ£IKK£©©©S:JÉ_1°z-2Mƒ -,×Ͻ÷Þ‹eYüéO¢ªªŠúúzŽ?Οþô'’’’X¸p!~¿Ÿ¼¼<^ýuÊËË©««£²²’£G0sæLš››yõÕW9{ö,µµµœ8q‚7â8qqqÄÇÇsàÀjkkùüóÏÙ¶m['ƒJzª„ëA†oÕ[±L4*ôÁ#K¸U¤¦¦òøã³cÇ^yåš››ILL$77—yóæ¹s`­\¹’íÛ·³yófš››Ýi 4 øØcñÎ;ï°~ýzLÓÄï÷3vìX׈Zµjo½õ¿ýíoIKKcùòå¬[·®Ã ²êâ¥Y®A ¬ÞÑ =X‚´° ÜIIIöüP3 –.]ÊÒ¥K»ŒOIIá»ßýn·Çgggó·û·QaÏ>û¬»í÷û£öbcc;… ‚ ÞªÞ€~×ás} ý¹…o¾' ‚ – ‚ ‚ – ‚ ‚X‚ ‚ b` ‚ ‚ >ž¡C‡²xñb† •FKK «W¯àêÕ«ìØ±ƒãÇÓØØH\\Ì›7áÇðïÿþïÔ×׳jÕ*&NœU¦çŸžêêj ™2eŠ4’ – ‚Ð÷7n………˜¦ÉñãÇÙ¼y3º®3lØ0”Rüä'?ÁëõÒÐÐÀ¶mÛxá…ø»¿û;t]ï2½—^z Û¶¹ÿþûINN¦±±‘“'OÒÔÔäÊ(¥HJJ¢´´4ÊÀ:sæ x½^i¡[dˆPúJÊ%ôKt]'!!¤¤$f̘Avv6ÇŽsããããILL$33“»îº‹+W®péÒ¥.ÓjiiáÔ©S,Y²„Q£F‘””ÄСC™3g·Ýv[”ìäÉ“©¬¬äÊ•+nØÁƒ™|Ø5ʺÂëõâõz)//Ç4ÍóIHH`ìØ±”–– 9räS§N•FzÖQ©A¡/QQQAEE³fÍ ™ïŽÃ¿ýÛ¿ÈÉÉ!--­ëžMãþûïgÓ¦M|øá‡dff2jÔ(&NœõÝVS¦LaÛ¶mÌ;—£G’’’BFF†4„ – dˆPèŸ|öÙg¬]»Öíµš4ióçÏçìÙ³(¥xôÑGñx<œ9s†’’V®\Ùcz¹¹¹Œ7ŽS§NqæÌŽ?ÎîÝ»)((èôÑúøñãyóÍ7©¬¬¤´´”iÓ¦Iƒb` ÂÀB†…þÉèÑ£Y¹r%š¦áóù:}ÿä÷û‰%55•ÆÆF^yåy䑞€†Avv6ÙÙÙÌ;—M›6Q\\ÜÉÀÒ4É“'S\\ÌÙ³gÝ" BOÈ7X‚ B¯ÇãñœœLRRÒ5?.Ÿ9s&/^¤¼¼ü†òHKKs‡;2uêTªªªÈÉÉ!66VD¸&Òƒ%ý "Žãt2ƦM›ÆŽ;ÈÉÉé$ßÔÔÄ+¯¼ÂÔ©S2d^¯—sçÎñÁt)žžÎÏ~ö3<T¸ – ÀG”Kx¯ª³?sæLöîÝËÑ£G¹ýöÛ£â¼^/Æ cïÞ½ÔÖÖbÛ6ƒ búôéäççw›O\\œT¶ – ‚Ð?(,,ì6nÔ¨Q<ûì³Â“’’øùÏÞe†a°hÑ"-ZÔc¾?ýéO{Œæ™g¤q„n‘o°¡½ËK¹AÄÀáæ"C„‚ b` ‚ ‚ ˆ%‚ ‚ \ UTT$}÷‚ ‚ 7éÁAA¸Éܲiþ¥hO‡aó©¿£»V|W2ד¦vÒê)Ž/™Æ—ÍïFÎÿFëè뮫ŽáѲN8Ü ×«ÂlBŸRwÜŽÜïÎïIÖéAÆù ù´…·Å™¥ãB~ù¨ðêéàGnë\¤Ld¼Öű×,PV»¯L ˜ag…áGÊ´ÅÛa?Ø!¾»ô"Ój“±º‰vã[ò^GzÝ•ÅF ;…Žó•ªÕè¡ùŒrz7M¬]£‰nâ;në]”­cz‘yé6èa§¾JµFÆ·yq¿(+’§² =X‚ ‚p]ôÃY:6nÜÈ‹/¾(m+ô¾,AA¸^c¦´´¥𦑔”D^^ùùù×\—ðzÒnii‘œ1°A„>ÆMø+Õ¸qã(,,Ä4MŽ?ÎæÍ›Ñu9sæHý b` }u‹ŽzUSŠH“ÞBt]'!!€3fPVVƱcǘ>}:›7o¦ªªŠ––’““ÉÏÏgÒ¤Iî±Gåý÷ß§¶¶ÇCff&ßûÞ÷ؽ{·Û3VTT„RŠ5kÖ0jÔ(êëëÙ¶m(¥1bË—/Çï÷wY¾îòÅ¡ÅÀ„k¼~ª[p¬Ð{úH¦Ò¤ýÜ 4 ƒææfLÓ$++‹üü|¼^/ÇgÆ ¤¤¤0tèPøË_þÂ=÷ÜCNN@€ªª*ÇaöìÙTWW(,,Äqâââ°,‹õë×3|øp}ôQ4Mcçά_¿ž§žz ]×£ÊÒS‚X‚ ‚Ð'¬ÀŠŠ ***˜5k>ŸÙ³g»q3gÎäĉ=z”¡C‡ÒØØˆã8äææ’””ÀàÁƒ]yǃeYnïÀáÇq‡‚‚7¬  €_ýêWTVV2f̘¨ò\+A ,Aø^?¥÷ª¿÷$ˆ À&½|öÙg¬]»˲˜4ióçÏǶmJJJ8zô( X–…eYîÐÜ!C=z4¿ýío3f cÆŒa„ ÄÅÅu›×ùóç©­­eíÚµQá¦iRWW×IþËä!ˆ%È¡ô$ôÁL¥Iû™8zôhV®\‰¦iø|>÷߃%%%ìÛ·åË—3xð`<[·nu 1MÓxøá‡9}ú4ìß¿Ÿ÷Þ{¿ù›¿éö{ª@ @VVßþö·; óEötµñeòú?2– ‚Ðëñx<$''“””55ÃéÓ§ÉÉÉaÒ¤I 2„äädjjj:?|øpæÏŸÏO<®ë”••¡ç;Q™™™ÔÔÔOJJJ”‹‰‰é¶ŒÝå!ˆ%_Ãë§ô^õ·žÑiÒækìfKMM¥¢¢‚Ó§OS]]Ío¼Acc£æÌJJJ8wîõõõ”••ÑÔÔDzz:~¿Ÿ .péÒ%ššš°,‹É“'Ï‹/¾HUUuuuœ!!‡z¨SxbbbùFÆ]+a`"C„Â×üú)½W¥'At`5é"˜‚X‚p³ïŽrg•è€Ô® ˆ%‚ 7éÄÄÀAA¾ò{EQQ‘ô0 ‚ ‚ ÜD¤KAá&s˦i(*:ÞJÒ€t Ò¸DÕ¤q /™@.LÀOéḌtÊH£œtÊ|`.0‡Vî Ä¸D‰QM‰~‰ÃÆyW62’hËæùÄA:Ÿ†Óú”4ªHç’[žx2Ý´!ŸrÍd—QÎNãSJŒrlí\D¹.a0"\î\ —êݸ4.¡"âc£ÎËÃ%vÕì4.Qâ¹ÄÝtërˆ!5ªžÒ9Ee¤‡Ï-žAeK"ñQé¦U°ÓSF‰QN‰QÆU5!êÜRÂéµ¹öc/‘J}D›ä¢3"*ÞQár—Øe\¢\WQõ‹¯C½&ºqK"z‡6¾–m{%¿O¿â¶óN㩪<ÜÎe¤rÒm_ÈGgJ‡t¿ˆ8Ç2ˆª·³*™£Œ£Œž2jµSnû¥q‰8üeÉav‡ô#Ë 숺Èa—QÒOã{Œº¨óÒÙ¡#·«Qò±$EåW­U±ÓhoãóZZÔ¹ùhЏ>%óQÇëLwe!ŸTއõëS’9ÆÎp¹KŒK|d4Fé„ÁPÒ¨ŽjçÈzðaDÕ[<ñQõÕ±^j»Ây•xªQ*º.tFGÝ'R©¨¯jWÒ)'…³a}Ûn£ÄSæ¶ó ½"ª}$FèÐ\âI×WÛ}⌛W:—ˆ!=Jßéöûq‰XU•¾Æ¸ùÛ¢ê!•†¨´ †EÅ›ê¬Û¾%FÇuÜkæÇà¨z ÝÓÚõ=†¡Qç–Äe7.2Jõ n¹KŒK˜jVÄ=öî¨k'cQmœLK”Nxu]øº÷ýŸÀüG•<•éÁAAÄÀA„>Aii)¿üå/¥"ú02“» ‚Ыٸq#¥¥¥(¥Ð4¤¤$òòòÈÏÏZø¹?1qâDÆwCǬ[·ŽŒŒ –-[&J#–ps‘Éf„þ©¢Ù¸qã(,,Ä4MŽ?ÎæÍ›Ñu9sæôÚ2[–…®ë_îál†<¢ÅÀz 2ã†Ð?µQ4[Ðu„„f̘AYYÇŽcΜ9TUU±}ûvÎ;GBB999,Z´¯× ÀsÏ=ÇÔ©S©®®æØ±cÄÆÆ’ŸŸÏÌ™3Ýô‹ŠŠX±bÇŽ£²²ŸÏÇ’%K˜0a‚+S__϶mÛ¨¨¨@)ň#X¾|9~¿õ´µ´´••Ň~ˆa<ýôÓ<÷ÜsL›6ššÊÊʈ‹‹ãÞ{ïeذalÚ´‰“'O’œœÌ}÷ÝGVV"ܺu+Ï<ó ÅÅÅ”——s×]w±cÇš››7nx½^6nÜHee¥»è´RЧŸ~Ú-›ðÍ#ß` ‚ }¯wÀ0°,‹ÚÚZþïÿþÛo¿ýèG¬ZµŠS§NñÖ[oEÉðÁdffòä“O2gζnÝÊçŸ%³cÇn¿ývžzê)&MšÄ«¯¾Ê¥K—€PoÔúõ뉉‰áÑGå±Ç#&&†õë×cY–›ÆçŸNMM ?ü0ßÿþ÷Ýð½{÷2bÄž|òIÆÏk¯½ÆÆÉËËã‰'ž 99™ 6ôxεµµ;vŒ||ÊÊJvíÚÀ²eË>|8Ó¦MãŸþéŸø‡ø’’’DQÄÀn2"ôOmÍ"©¨¨ ¢¢‚Ñ£G³k×.&OžÌ¬Y³HIIaøðá,[¶ŒÒÒRLÓt1bwß}7©©©Ìš5‹ &°gÏž¨to¿ýv¦NJjj* .$++‹}ûöpäÈÇ¡  €Áƒ“––FAAõõõTVVºix½^ HOO'==Ý 7nÓ§O'%%…yóæÑÚÚÊСC™0a©©©Ì™3‡K—.ÑØØØã¹’žžÎˆ#ÈËËsÄØØXt]Çãñ@bb"JÉ•sK_¤ ú2"ôOmÍ>ûì3Ö®]ëöMš4‰ùóçóûßÿž‹/røðáv}qBsùòeÒÒÒ6lXTzÆ s§È°Žû.\àÂ… ÔÖÖ²víÚ(Ó4©««s÷‡ ÒåwWC† q·ŸûïÁ@ ÀôéÓ¹óÎ;]ê›9DÈÊÊâÛßþv§|Ú¾ ðx<]ßÕ¿#ÃÚz›:¦ÝSJ©å1°„›†t ýSE³ÇCrrr§ðÌÌLª««»Œ‹äÌ™3öÛz·"Ãòòò¢ö333Ý|Ž=J||<111½²Žt]ƒ«!ß`õ+äÂú§6Šf Ý1gÎNŸ>Í–-[8þ<555”——³eË–(¹Ó§O³{÷njjjØ¿?Ÿ~ú)wÞyg”̧Ÿ~ÊÁƒ©©©aÇŽœ;wÎý§áäÉ“‰çÅ_¤ªªŠºº:Nž<É[o½Å•+WzE]øý~Μ9ÃåË—ijjcë#=X‚ BŸeÈ!<òÈ#lß¾ßÿþ÷8ŽCJJ ·ß~{”Ü]wÝŹsç(..&66–eË–1f̘(™ùóçsäÈ6oÞŒÏçcÕªUî‡ê‡Gy„wß}——_~™ÖÖV ÄèÑ£¿TVW ÕÒgÏžÍÆyþùç1MS¦iK¸yÈ@ŠÐ?µQ4{`SXXØc|VV=ôP2111üÕ_ýU2>Ÿ¯Çt{,Kwq?ýéO;…=ûì³Qû~¿?*lÊ”)L™2%Êø›?~Ô1wÞygT/\jj*=ö˜(L/A†ûÒ,ôOmÍA ,AAècÈœQÂÍF†A„~MWCté8d'_Ùh/**’ÞwAA„›ˆ  ‚ ‚ ˆ%‚ ‚л¹eß`ý¿‰\ÏI¹NiJÓQºšŽÒ4е¯i(Žò(ehN¯Žòꨘ¿mÛ«ƒ¡¡ žÐ1xõPš^å ç¥k!¹ð¶2ôßVÞV ÐPh¡²Û8Z´o«v?¨¡L ÔÀ o›X*ä4T@ƒ€Žjm÷U« o“j¨ Ž ê!™@xÛÔPV›œŽ² 0 w[ÙFØ×QŽ.»è(t” ;t”f‡ÊF3‚(Ãtº2,”a£t å5#œ…Š ;oØ6xì¼ÇBymˆ ùʰQ††Ò°ŒƒòØ¡0Ã-‡î ”*ì pÚ}ÛLJжå ,LeÙtP‚6Ê …·9t `¡6´Ú¨ –³Á´Cá Õj…ü ¦Š·Bû*h¢‚Aš(ÓDYA” ù¶…rL”cc¡9Á° J™(e¡”¥ßÒóÑ´9(c"Ê¢y̰DéV¨ <¡vм&*Æ í·Õ»ÇByìp›„}£½®1·Î•ǯŽ'Tÿ:(Í ½’….P Â—®j›H!üÁr°B¾²3¼o®wÀ û¦6Âí`†Ú Ó ·‡… Xí~ÀDLh5Q¦ªÿ6?@3ƒ¨` €m¡l³Ý9­(;ò¶ú7]_ÓZQ*òµ44}JŸfÌAéÃPzøZÐM”Ç µ…7ˆæ †¯‹ÃÒÍk…ÚÄk¡ÅØá6ûF¸þèªonšBéa_ ×»j¿‡âhà¨ð=H¥ Í·T¨ Ú|SAòƒ‘סöhµÃ¾®ï°î·šaÝ·Úý`„ë=VA¹â' IDAT,,ÇàX­8V+¶ÕŠc Ç + ÿï'ä©,H– ônäAB_ÔDÑÛ¥´´”_þò—R‚ô` ‚ °qãFJKKQJ¡iIIIäå呟Ÿ/•#ˆ%ß,òçX¡÷h¢½ýÊŒ7ŽÂÂBLÓäøñãlÞ¼]×ILL”ÊÄÀ„¯uÓûì ¦UÕ€Ð<]×IHH`ÆŒ”••qìØ1¦OŸ%W[[ËÛo¿Í™3gƒ¤¥¥±xñb²³³]™ýû÷³wï^®\¹BLL #GŽä;ßùëÖ­cðàÁhšFii)º®³hÑ"&NœÈ–-[øôÓOILLdùòåŒ7Û¶yã78yò$$%%qÇwtZLZKúAoê¹7ÀéËg(ô7ãéºõöG3×V/yxÍÍÍÂãÇgñâÅèºÎ¡C‡øóŸÿÌüc’’’8wî[·nå`øðá477SUU•Æ¡C‡¸ûî»yüñÇ9räo¾ù&eeeäææ2wî\öìÙÆ øû¿ÿ{<Žã””Äw¿û]âââ8}ú4o¼ñ>Ÿ¯Ó¢ÓÂÀA>rú±©%}MEo¯‡ŠŠ ***=zt§¸ŒŒ ¦OŸNzz:))),X°€äädŽ;@}}=^¯—ñãÇ“””DFF³fÍê”ÆÜ¹sIIIaΜ9†A||<Ó¦M#%%…yóæÑÔÔÄ… €PïÚüùóÉÌÌÄï÷3iÒ$¦L™ÂÑ£G¥±0Òƒ% ¸Þ‚ì³g( ˜VC„Ÿ}ök׮Ų,&MšÄüùó;1@€;vpüøq±mÓ4©¯¯ ;;›¤¤$ž{î9ÆŽËØ±cÉÍÍÅãñ¸i 2¤½BÓˆ‹‹‹ kûîëêÕ«nØþýû9xð õõõ˜¦‰eYdddˆr‹%ý§·@†…¾f<Éáµ=z4+W®DÓ4|>šÖõÌÛo¿ÍÉ“'¹çž{HIIÁ0 ^~ùe×0‹‰‰á‰'ž ²²’ŠŠ Š‹‹)..æñÇ'66Ö5ª¢Ú0üïÅNuâ„Úê“O>aÛ¶m,[¶ŒaÆáõzÙ½{7gÏž• @ ,AAè½x<’““¯)wúôi¦L™BNN­­­\¾|9JFÓ4²³³ÉÎÎfÞ¼yüò—¿ääɓ俿~©²>}š#F0cÆ 7¬®®NM ,Aè½ÎWïVèåg(ô ®§ççºõöêD_ѼÔÔTÊÊÊ?~<;vìp{š 4ÔXWWÇÈ‘#‰åøñ㤥¥}¥<>̉'HNNæÐ¡Cœ={öº BA ,Aèc3"úŽI,C„7¥K—òúë¯ó?ÿó?ÄÇÇ3gÎZ[[ÝøØØXÊÊÊ(..Æ4MRSSYµjééé7Ö®ª½6¦OŸÎùóçyõÕWQJ1qâDfΜéo‚X‚ ‚Ðë(,,ì6nÊ”)L™2ÅÝ÷ûý¬Y³&JæŽ;îp·GŒÁ_ÿõ_w›^Wq?ýéO;…=ûì³íRÃà¾ûîã¾ûî‹’Y´h‘4ÞF¦iúio  ½çfêí-Ô Ñ]Çgx è€i‡– ––ÃÕ“†‹+Í& ͉N{3ûlǕŲÛÓ ÚX¦MƒéÐ`Ú4–ÏtðY>Ë&Îrˆº‘¿?z#EEE¬^½Ú!¾·±qãFZZZX½zuŸ0>JKKÙºu+Ï<óŒX‚ |ÃÈ÷΂e@”––ºë&%%‘——G~~~·ëöæsPJ1hÐ n¿ýv,X€aôþÇñØ¿?uuuhš†ßïgâĉ̙3熼‰'2nÜ8éÁA„[͸qã(,,Ä4MŽ?ÎæÍ›ÑuÝ}Àß(–e¡ëú-9˲8wî6l@)ÅâÅ‹¿Ñr”––RZZÚ㤫‘|üñÇlݺ•{ï½—‘#GbY.\àâÅ‹_ªÞ ÃèF¥X‚Б¿ŸBº®“ÀŒ3(++ãØ±cÌ™3‡;vpìØ1ž|òIW~ïÞ½ìݻ׉½­‡%++‹?üÃ0xúé§yî¹ç˜:u*ÕÕÕ;vŒØØXòóó™9sf·e©¯¯gÛ¶mTTT ”bĈ,_¾¿ßÝç0hÐ ÆŒCEEE”u­´mÛfÛ¶m”––¢iS§NZoñëà³Ï>câĉL:Õ ‹\^¨¸¸Øí+**B)Åš5kðûý<÷Üs¬ZµŠ?ü³gϲråJ€¨!ÂââbÊËË™1c;w¹™ñãÇSPP@LLŒ›Ïسg—/_Æï÷3kÖ,w¦~˲غu+eee´´´˜˜ÈŒ3¾´.–ð  ¤ìÒî¯ÙÞXfÃ0hnn•O]_ ?ÿüsbbbxøá‡£Â?øàæÎË‚ 8qâ[·n%--ììì.{`Ö¯_ÏðáÃyôÑGÑ4;w²~ýzžzê©ëî»pá§NŠ2Ê®'í>ø€C‡QXXHZZ|ðåååŒ=úk«ëÄÄDªªª\æ#³gϦººš@ @aa!ŽãGCCÛ·oçž{î!33Ã08qâD§4jkkùôÓOyðÁiiiáõ×_góæÍ<ðÀ>|˜ââbV¬XAFF_|ño¼ñ^¯—¼¼<öîÝËgŸ}Æw¾ó’’’¨¯¯çÊ•+Òƒ%ôö®Õ‡Ë.OáWw¢•ý¾ÌTTT0kÖ¬:ÎëõRPPÐÉ1bwß}7©©©œ>}š={öti`9rÇq(((pà øÕ¯~Eee%cÆŒé±'híÿÏÞÛEuåùÿ¯{»iš§…il0QÔø,!¢bLƧ‰:Æ­$3‰f’LYëf'›ÝšïÖo¬ì–µÖÖ캳“2™©lœçÜsO÷}ßsÎ=gÓ&Ün7N§UUY¶l™æòäÉÚ.(( ++Kp¿|ùò ‚¥Ïëy“-^óçÏçƒ>`Ë–-ÄÆÆrï½÷’––ÆÄ‰Q… .—Kk¡ó&##ƒ &ô™†ÓédÕªUDDD°téRÞ}÷]}ôQL&¹¹¹üà?ÐÎ;**Š‹/òÕW_ñÀÐÖÖFll,cÇŽ 22rpä¯NäŽ&Ãqâr¹˜éGGG°bÅ ¶nÝJqq±6¶éF¶¿ïX«ˆˆŸ<}óÍ7|óÍ7>é„……ÝÐN||<ñññÌš5‹™3gò?ÿó?Øív¬VkŸñnµÜÐÕšç-.{&@BB/½ôgΜáܹs|øá‡Œ7Ž¿ú«¿% å¦é"yw4iB“’Zyö'þŒ5ŠË—/û»páB¿m×ÔÔì[,– a8yò$£Fò€íMLLÌâŠBVV`òäÉèõú~ÙŽˆˆàüùó$'']­8uuu$$$ôš–ªª>y G¯×÷Zžý¡§|zÄN§ *û;>®µµ•ï¾ûNkŪ©©AUU, áááDDDÐÜܬµ#44”ôôtÒÓÓ™0aï¾û.×®]ë—xhd&wAaØcµZ¹rå _|ñMMMÞÔ¸¤êêjòòò¸té………œ:uŠŒŒŒ a§L™Â¨Q£xÿý÷©¬¬¤¹¹™ŠŠ >þøã›TžžŽªªöÛöœ9søâ‹/(++£±±‘½{÷ÒÞÞ~[ËwÏž=üùϦªªŠ––­‹0<<œ{ï½èU__Occ#W¯^ÕZûÛê¦×ëÉÉÉáÂ… TVVòñÇ“žž®éÊÎÎæÈ‘#péÒ%êëë)..&??€üü|Nœ8Acc#œýôS>øà:::0›Í¤¤¤ôÚêÔk+‡ª2{ölòòò˜5kV¿lgffrùòerrrP…iÓ¦1a„Û*²l6ÅÅÅ|õÕW\»vQ£F‘””ÄÚµk53}útìv;¿ÿýïéììÔ¦ièo VLLŒÖêÔÞÞÎøñã}^˜>}:ƒ¼¼<<ˆÁ``ôèÑš !//¦¦&m\ÚüãA«“"°„~Þ饋päÝѤ‹PJvèäyåÊ•7 3sæLfΜésÌ{Ðv_6BCCY³fM¯þ¿þõ¯}öM&S¿òÔŸs˜;w®Ï\M7²­ª*‹/fñâÅß»<§NÊÔ©Sû~„ 7| 0<<<è€wÿ²ë+ý`×Лɓ'÷ÚE8cÆ f̘1d~3ÒE(‚ ‚ K¸óHáˆl2.B)Y© ‚ð½‘.B¡Ÿwzé"™×]’½ûkCÏR:Âà±`Á‚›žÓl¨#-X‚0T‘&AX‚ ‚ B÷3òÆ¥@Aa‘,AA„fйoܸèf¢¢¢¢ ¢¢óúèÑiÇÔî}÷§'„NóéúÞu\Eß½Õ¡j[ :-%=ŠŸºsÓõ]°írºn§j[ºn×åïñów£Çƒw·ŸnT<¨Ú1z<èq£Ã…:ÜÚVÕ¶]þ:—v\Å…Špy;¥Ûá•}o§ïc_ßG8`ð*}?âú‡W½\0ÿ¾léýÂëüìóïõ|< sê¾îèq=%Ù 8»·./w÷q§—¿ÿw·Ÿëô³çb¯³{þi»üì9ƒøûÛqywù¹Î^òÞ•®Ò]sÕîZÜ[Õð¿D†„ëO•ó·§»…ª,L°*ÓWõó?/ï꧸@uâìú®¸ú(zg?«ŒÓï»üª€ó&«Œuð¯Î½UÁ¾ªk°¼;·ž4=ðJ÷}A¤K . “ê'UUD` ‚ ‚ €Ìƒ% OäÕ a˜T¿á<‹ÜP!''‡’’EAQÌf3ééédgg£×ûÞÆþô§?QTTÄš5k˜8q¢_nn.eee¬_¿^ÛÏÍÍeæÌ™,_¾\ wáÂ^{í5^zé%¢¢¢hiiaË–-š¿N§#22’©S§2oÞ<Ÿ4®]»¦¥sùòeFEjj* , 22€¯¾úŠO>ù„_þò—¨jW;‡Ãáàßþíß;v,ëÖ­ÓìUTTðÇ?þ‘ŸÿüçDGGKe% {É“ C‡´´4V®\‰Ë墶¶–;w¢( ‹-ÒÂtvvRZZÊܹs)** XAo‚z=ÅÅÅdffsýïÆobEQxæ™gˆ‹‹ÃårQUUÅ®]»ˆˆˆ`Ú´iš¸úÃþ€^¯ç‡?ü!qqq´´´pøða~ÿûßóüóÏÕj¥³³“ÚÚZ’’’¨¬¬Äd2qþüyœN§&ív;QQQ"®†!ÒE(Üú#ú`äIÄ–0L´¾TÕA§ÓŽÙlæþûïÇf³qöìYŸ0'Ož$>>ž¹sçRYYI[[Û íZ,¬V+‡êûïÇã!,, “ÉDdd$“'OfìØ±ÔÕÕia:ÄåË—Y»v-©©©DFF’œœÌO~òt:ûöíÓÒ4™LØív-®Ýnçþûï'**ŠššŸãV«U*€,AÁPê'UõŽP__OUU:Îçxqq1S¦L!44”´´4JJJúeoÑ¢Eœ:uŠÚÚÚ~çáüùóÔÕÕi-P‡ÒÒR¦L™Bxx¸OXƒÁÀ¬Y³8sæ ×®]Àjµ,«Õês¼³³“óçÏ“’’"}"]„Âðyì–¦A±”——³iÓ&Ün7N§UUY¶l™æéÒ%jjjxâ‰'˜2e #Œ„„ÒÓÓùôÓOyæ™gz ÷Æo ( .— ·ÛÍŒ3˜2e W®\¡½½‹Å4nÏñ¦¦&î¹çRRRØ¿?n·›ÎÎN.\¸€ÕjÅårñõ×_P]]Ëå’,Xˆ}DŒ<‰Ø†‰Ö—ª:0¤¤¤°|ùrùùù¨ªÊ„ 4ÿââbRSS  55•]»vQQQѯ … òꫯröìÙ€¨Ö¬YƒÅbÁívÓÐÐÀ¾}û0>ãÀú‹÷8¬k×®˨Q£°Z­ìÚµ §Ó‰Ýn'::Z/ˆÀA„Å`0h½W¬XÁÖ­[)**búôé¸ÝnŽ?ÎåË—yå•W®?“y<÷K`ÅÄÄ0}út>ýôS{ì1<žÀ§L³Ù¬ „·X,455ñÙgŸ±`ÁÂÃÃ1\¼x1¨ýžã=ñcbbˆˆˆ ¢¢‚k×®‘œœ @DD‘‘‘TWWc·Û¥{P–0¢Ñ#O2°EDdš†Aþ+P²²²8pàS¦LáìÙ³8Ö¯_ïóö_CC»ví¢½½£ÑxC»óçÏç·¿ý-¥¥¥Aß" –·ÛËåB¯×“žžÎ‰'ÈÎÎÆd2iá:;;ùꫯ|ZØ «UÎn·síÚ5zè!íxrr2§OŸæüùóÌš5K.ø0E¹ ·~Œ<ÉK&ÏRUoéé騪Jaa!ÅÅŤ¥¥1zôhâãã5—žžNhh('Nœè—M“Éă>HAAAàßÇÃÕ«W¹|ù2mmmœ>}š‚‚RRR àá‡Æd2ñöÛosúôiZ[[±Ûí¼óÎ;¸Ýn–.]êcÓjµRUUE}}½Ï8«ääd¾þúkÜn·´` c¤KA~­ªÊìÙ³9x𠪪²zõê@q«(L˜0¢¢¢~·effòå—_joûyÛzûí·µïŒ?ž… jaFÅóÏ?ÏŸÿügöìÙÃåË— #--ýèGc©RRRp:X,Ÿq_ÉÉÉ8m:a˜>ˆmܸqPÚ*d±ç!¾Ø³þ&üîÔbϽåI{F{–Åže±gAbRBP<’'AA%Œ,d`‹0LªŸTUA%‚ ‚ ăØ`ÁAA¸[‘,AA„fЦiØØËÛ"J·êS‚8z9Þ_§zÙ ˆß­¤s»óÞ› uÒ¡—|Ó‹¿z‹e4Ðy¿Ù:ð}lô§¾­J·S½¾w;PU/¿î‚õ§¨ *¾á¼·KGõM§/ÍÆ Ò ê¼ó®ô#ï7™7íÜzìûm{ýÁ©7¸è ÝëÞÒs!ºªEª.øVñßs¼¿~áU]/qüì)}套tT5xþnê¼ül÷•·¾ò®ê{ñS}faXÂ]"y•ë,Há2999””” ( Š¢`6›IOO';;½^ßçû¢(¬^½šI“&a·ÛÙ¶m›63{HHÑÑÑØl6222ˆˆˆš~nn.¹¹¹(ŠtEQøõ¯ÍÎ;9~ü¸O8EQ°Ùlüä'?à?ÿó?immåñÇgÒ¤I>v^}õU.^¼ÈÊ•+™:uªOxèZ2Èb±0wî\ÒÓÓûÌ›ÅbáoþæoxóÍ7©¬¬ìºñëõDFF2uêT²²²‚žïÛo¿Í¹sçxá…HLLÀívóÊ+¯ôYÙÙÙ$%%ñÖ[oñ«_ýŠŸ0ÿñÿÁܹs™={¶f¯‡ÐÐP, óæÍã¾ûîÓŽ±{÷î€tCBBøÕ¯~%K¾ §¥>dY);)ü»—´´4V®\‰Ë墶¶–;w¢(JÀbË«V­Âf³ùó^*GQ^|ñEBBBèèè ®®Ž¼¼<ŠŠŠxöÙg‰H;33“™3gjû¿ÿýï™9s&Ó§O=ùôÞ"PQ"##)))ñX555\¾|9@”(ŠÂÂ… ™>}:=z”íÛ·c6›¹÷Þ{ˆgíÚµ>iªªêccÆŒ,\¸§ÓIEE»wï&,,Ìç¼Z[[©®®fΜ9iKUU~ñ‹_háNœ8Á‘#Gذaƒ–nhh(ÕÕÕA—êÕ«W“’’BGG|ðÁ¬_¿ž¸¸8-̨Q£|Òé9§¡ŒŒÁ¤K²“Âèt:ÂÃÃ1›ÍÜÿýØl6Ξ=.44“Éäã¼NÏ Ûd2ˤI“øéOJxx8{öì švHHˆ=UUŽùçÓÛÏ-Ä)S¦`·ÛikkÓŽ3eÊaäŸ~ll,Ë–-C¯×S^^î#¦üÓ5j” ƒÁ@xx¸Öz5f̘ åW\\Ì}÷ÝÇÌ™3)--Åétj~Þö{–òN×`0Üôuí¹^±±±,\¸—Ë…Ýnç~޳ߋÀ†íõäU®ó èE _N}}=UUUètº±g0˜9s&ÕÕÕ\¹rå¶ç?<<œÔÔTJJJ€®¡KKK™6mÚoܪŠN§Ãår}ïô+++¹xñbÐò+))aÊ”)X,bbb8uêÔ¹¦.—‹¢¢"E°ë:˜H¡ ‚0,(//gÓ¦M¸ÝnœN'ªª²lÙ²€p;vìè>Ú°aCÀZ€þX,ZZZn©u¤'ŸÞdeeŒwš:u*Ÿ|ò óæÍãäÉ“ÄÄÄ0f̘>m;NòóóéèèðYº¾¾> Í)S¦°|ùrmÿË/¿¤¨¨—Ë…ËåÂ`0‘‘áçìÙ³tvv’ššªÙ(**bÊ”)·íº~øá‡(ŠBgg'‡˜˜&NœèæêÕ«ç—’’“O>)KAn…””–/_ŽÃá ??UU™0aB@¸Å‹ûˆ ×ÁëÞxJˆ|z 7~üxöìÙƒÝn§¤¤$`<—7Ÿ~ú)‡Æét¢E‹HKKó‡O=õ”Oš=]xÞ‚kÞ¼y\»vÏ>ûŒ{ï½—¤¤$Ÿ0ÅÅÅLš4I+ƒI“&ñÉ'ŸÐÜÜLttôm¹®K–,ÁjµÒÜÜÌþýûY¶lY@—jXX?ûÙÏ|ÎïûtGŠÀA? ƒv“_±b[·n¥¸¸8 [-<<œ˜˜˜›¶ñâE¢¢¢,Ÿ}¡ª*S¦L!77—óçÏóÄOô633“©S§jc±üÑét7L344”èèh¢££Y³f ¿ýíoIJJbܸq\»v²²2Ün7_~ù¥ð,..fáÂ…ý:ÿa×ÑÑ0`¿½½=@<™L&bbbˆ‰‰á±Çãý÷ßgÆ >cÈE¹mO– ‚ xÝp³²²8pà“'OÄ~³tvvRTTDrrrÀàðÛÉ´iÓÈÏÏgÒ¤IÂÛQ£F}/ÑØ!!!Ì™3‡O>ù„õë×ð—¿ü³ÙÌ“O>éÓRtöìYòóóÉÎÎîWë^O>kkk}¦[hllÄápÛkÜ{ï½—øøx¾øâ }ôÑa]GE` ‚0dñxFÐ@wá¦IOOçàÁƒ’™™©oooçòåË‚¢§5ÅãñpåÊœN'ÔÖÖrôèQ®^½Úg+Rq¹\髪T¸ÅÅÅñÿø·ÜÝåv»Ò‚¶võ0sæL>ÿüsN:Åĉ)..fâĉ>‰]– IDATÓ#˜Íf>ýôSΜ9ãÓ-ÙaaaL:• ( qqq´¶¶rðàA’““¹çž{úŒŸ‘‘ÁöíÛÉÌÌôÉÿÍžŸ,aè?)J^å: RøCUU™={6yyyÌš5 ƒÁ€¢(ìÚµ+ ìÃ?Ìܹs»Š[QøÝï~§ ¯èèhRSSÉÈÈö™3gøÍo~ãs,66V›ô3˜ ¹U.^¼è“¦ÇãA¯×óÏÿüÏ} ¡x€ÜÜ\¢¢¢¨¯¯ç±Ç g47nEEEýXË–-ãÈ‘#|òÉ'´¶¶b2™HMM èf Ö"vß}÷a6›9räK–,ºº/ýÏOQþáþᎶ8ÞÔÏz°{–¥rd©Y*G–Ê‘¥rd©Y*G¸k¤iÁ¤ì¤ðA%Üad¢Q¹Îƒ¦;d¢QAD` ‚ ‚ "°AAD` ‚ ‚ ˆÀAAq Ú4 ‚ ‚ w+Ò‚%‚ ‚0À ÚLîþzOh©óÛzÏ)Ø—ŸŠ¢ó£ó «x…÷×—½.?¥;¿=ŸëÇÔî#ªßñÀ8Š–þõyƒ}²ïž 9éɇ÷ù*Aò¬øåýúÜ‹ŠâgQQo¼ÎÝ?ç^“nzm½ÓÂ/žê—Wµ—³$HJþßÃß×ÒöΟâB –Îó,œw –kÅ/ÍžöýÒ¾QÞú®¿Þõ2ÈŒ ø÷®e†õÙ™Tà3”«,Jï[ÿxjÏ,®ŠwEëe†SÿÙZ{K¿ô¼âø¤u“y÷¶íïtJन*]ÇÕ q•î8*×ã{Çé-=ï8jwÞ{âhÐ+]w ½Òå|þ”½ütÝþ>ØJà|êõ¿üË¿È]Y,An eÐÒ•Y-…áXsAZ°AaÀÈÉÉ¡¤¤¤«õVQ0›Í¤§§“^ßu ¶üš¢(¬^½šI“&a·ÛÙ¶m›ÖbÖ³¡Íf###ƒˆˆˆ~çAUU"##yàÈÊÊBUUJJJØ¿?¿üå/ânܸ‘'žx‚û￟––¶lÙ¢ù………ÏÂ… INNÖŽçææ’››ësÎ÷ß? .$$$D³³~ýzÆŒfII 999(Š‚Çs}¨õÖ'D` °Ç3Hm<ÒŠ% »š›––ÆÊ•+q¹\ÔÖÖ²sçNEaÑ¢EZ˜U«Va³Ù|âFÁõâ‹/BGGuuuäååQTTijÏ>K|||¿òàt:9}ú4{÷îE§Ói‹H÷EQxæ™gˆ‹‹ãêÕ«|þùçüïÿþ/û·Kxx¸.>>žµk×âr¹¨®®&''§ÓÉòåË5;}a4yñÅ}–¢ÈoÿN!]„Âÿ $¯‚0tkî¹aêt:ÂÃõ–›ÍÆÙ³g}„††b2™|\O W£FÂd2ˤI“øéOJxx8{öìéw"##™9s&ãÆãÛo¿½y‰êñ†Éd">>žyóæÑÑÑAMMï ZUµsNOOgÊ”)>éy §Þ÷)o'ˆÀ†ÀóªäõîƒÒzu—Õ\g„ÔÜëÔ××SUU…N§»e[ƒ™3gR]]Í•+Wn*®^¯ÇårÝRú£(Ê Ïg ÒîÒE(ƒxK•.BAjnÿ(//gÓ¦M¸ÝnœN'ªª²lÙ2Ÿ0;vìèÛ°a‘‘‘}Ú¶X,´´´ô»…çìÙ³œ={–9sæ|¯óyã7P…ÎÎN<÷ÜsãÆë5|mm-'Nœ %%¥ßi´··³iÓ&ŸcÉÉÉüøÇ?–j,K H¡ áš;BºSRRX¾|9‡ƒüü|TUe„ >a/^ @n4x®wµ)ŠBee%ï¾û®æ÷ÃþÉ“'ûˆ¼žV¤É“'³`Á‚ïu>kÖ¬Áb±ÐÐÐÀÁƒY±bªêÛ©T__¯‰J·ÛÍøñãYºti¿Ó eýúõ>]‰ƒA~n"°„‘ý¼z÷çUº…©¹ÏYƒS ÑÑѬX±‚­[·RTTÄôéÓµ0áááÄÄÄÜ´í‹/EHHëׯ×üL&S€ÈSU•ˆˆAJggg€íöövÀw°=€Ùl&&&†˜˜\.ï¿ÿ>6lðé&´X,<õÔS(ŠBDDÄMw‰*Š¢•™pç‘1X‚ °BQ²²²8|ø0N§ó–luvvRTTDrr2£FB¯×kÂ'&&†‘ÐÚ‹Ûí¦®®Îçxmm-Š¢ë“oÒÓÓQU•/¿üÒç¸N§#::š¨¨¨ âJÞÚH –0DŸWïþ¼Ê,a@jî¿Évâôôtæñ«®® —1X•¸ÑeìëOìfÓ VåúóÓèÏ®3HøÞòÞWÞz¾{ÿù¯üóF¹+ Ò‚u'‘!•‚ ­››Ëk¯½vSq¶lÙ±cǤð„áÕ‚%‚ ý!''‡’’’®Iv³ÙLzz:ÙÙÙèõý»eff2gÎ)LA–72 _ad“––ÆÊ•+q¹\ÔÖÖ²sçNEaÑ¢EýŠï½h³ ˆÀ„aƒr‹þÂP¸JrCNGxx8f³›ÍÆÙ³g5uðàAÊÊÊhkkÃd21yòd,X€ªv„ÉÍÍ¥¬¬Œõë×]­bíííŒ;–£Gâr¹˜4iK–,Ñâ@×z~Û·oçÛo¿Åh4’••ÅìÙ³5ÿÖÖVöíÛGEEŠ¢ššÊ’%K0™L´··³yóf^xáñxãÆãܹs444ðÒK/a6›Xµj¯¾ú*µµµ$&&’œœŒÝn'33»ÝŽÍf£±±‘ªª*RSS±ÛíÌ;W.°,A‚#]„wÃU’“º¤¤¤°|ùrùùù¨ªÊ„ 4ÿÒÒR hnnÆápàv» íÓf||<Š×bÙ=­_Þ$%%ìÐØØHdd¤&®âââ0\¼x‘ÄÄD¬V+ÅÅÅx<*++±Ùl˜L&ìv;£G¦©©IZ°îRdšA¤‹ðn¸JrCƒÁ@tt4£GfÅŠÔÔÔP\\ @uu5}ôãÇç©§žbýúõdeeár¹ú¾ª¾·@EQð ðâÙÉÉÉ8jkkµî@«ÕŠÝnÇn·c6›‰‰‰‘§,AAä‡nE!++‹Ã‡ãt:©®®&**Ь¬,‰‰‰¡¥¥e@Òª©© Ø·X,X,Z[[ikkÓühoo'..£ÑH||<………èt:, ÉÉÉÔÕÕQ^^Nrr²\PXòÀ&}üåߢ¿0®’œÄð!==EQ(,,$66–ÖÖVJKKijjâØ±c”•• H:ÕÕÕäååqéÒ% 9uêØl6âããÙ±cuuuÔÔÔ““CJJ ‰‰‰š «Õʉ'41F\\'Ož”îAXò"·ö ò°&'1 7/UeöìÙäåå1nÜ8222Ø·o¯¿þ:555ÌŸ?@ÒyðÁ©­­åµ×^ãÈ‘#,^¼›Í¦ù?ùä“„……ñæ›oòöÛoÃã?îcÃjµâñxHII 8&ë.~ Û¸qã ü\of-ÂÞ–õ’µ‘µe-Bd-BY‹PÖ"iÁ’6án}V¹Ea(\%9 AF”À„áÿ ò°&'!"°äMAáîX‚ ‚ ÉA›Éݰñ)ý;AÏ`R‡ÅÀ †n'HUâϪTUAÌŸ  ‚ ‚0€ Z ÖÆµö×GN;¦vtè»?:í»®Ûé1hÔîJ÷w=*:Ôîcj÷w}·Ói[|¾{)üúKàÔî—áU<èq£Çƒ¡Û©ZPq£Ç®;Œ·¿Ë†«ÛÏ…:œº]×17ªÇ…Š…Îî­Å÷Íížé$ôàñŸ~¡?ßý]H·3toU?gð*ÿïþïäë½ìœ"ØwïpþéôyNÝïÕ«nйºß±w^.Ø;õøNãìøÎ^¾û‡ó·çÿž½œÎ~Øvõ’VO\ÁÞñW´)\—пX ~—½·*ÒÛt ¿Ë¬» ï8Áf ñò7øåÕš†¾ªc@•ñÐû”=ß;ûqÙƒUwÿžËãðª~þÓ0x‡÷÷ïìÅfoUÄ;¬ÿùuú¹;8€¾øÌËóº/ÜNrss)++cýúõý޳eË222´ F‡#Û¶mc̘1,^¼øŽgÒ%‚ ƒHNN%%%(Š‚¢(˜ÍfÒÓÓÉÎÎF¯ïÿ­,33“9sæH "°A --•+Wâr¹¨­­eçÎ(ŠÂ¢E‹úm#$$ä®(‹mÛ¶1uêT¦N*C– ‚ |t:ááá˜Ífl6gÏžõX¤¬¬Œ¶¶6L&“'OfÁ‚¨j×cÿ.œœÚÛÛ;v,GÅår1iÒ$–,Y¢Åèèè`ûöí|ûí·F²²²˜={¶æßÚÚʾ}û¨¨¨@QRSSY²d &“‰ööv6oÞÌ /¼@bb"‡Í›7c±Xxþùç8~ü8‡âå—_¾måçv»Ù·oÇG§Ó1sæL.\Økøüü|Š‹‹inn&,,Œûî»GyÄG¤VUUqøðaΟ?N§#))‰Ç£Ñ`¯¼¼œ;v°|ùr&Ož,KaÄ¢ Q[õõõTUUås<44”U«Va2™hhh`÷îÝ„††òÐCõj«¢¢‚ˆˆÖ­[GSS~ø! LŸ>] sôèQæÍ›Gvv6gΜaÿþýX,ƇÇãá½÷Þ#44”gŸ}·ÛÍÞ½{Ù¾};ëÖ­Ãh4’€Ýn'11‘úúzEáÂ… 8BBB¨¬¬¼íë–””0}út~ö³ŸQ[[ËîÝ»‰ŠŠò9OŸ*«(,]º”¨¨(š››Ù»w/dÙ²eÔÕÕñÖ[o1}út–,Y‚N§£¢¢·Û`ë/ù {÷îåñÇ'--mDÔQX‚ ½á¢¶F(ååålÚ´ ·ÛÓéDUUífßüyó´ïQQQdffRZZÚ§À céÒ¥(Š‚Åbaüøñœ;wÎGxŒ;V³Kuu5ùùùŒ7ŽsçÎÑÐÐÀK/½„Ùl`ÕªU¼úê«ÔÖÖ’˜˜Hrr2v»ÌÌLìv;6›ÆÆFªªªHMMÅn·3wîÜÛZ~‘‘‘Ú€óØØXêëëÉÏÏïU`yvŠŠbáÂ…ìÙ³G+ó£G’˜˜ÈÒ¥Kµp‹%ÀNaa!‡æ©§ž"99yÄÔWX‚ ° %%…åË—ãp8ÈÏÏGUU&L˜à¦´´”‚‚š››q8¸ÝnBCCû´¢\obìiýò&)))`¿  €ÆÆF"##5q‡ÑhäâÅ‹$&&bµZ)..ÆãñPYY‰ÍfÃd2a·Û=z4MMM}¶`9r„#GŽhûÔÔÔ°oß>íØ† ˆŒŒìÕF°sÈÏÏÇãñøœgÏžå‹/¾ ±±‘ŽŽÜn7.—‹ÎÎN .\ ==½Ï²=uêW®\á¹çž#11qDÕWX‚ ½!]„C ƒÁ@tt4+V¬`ëÖ­3mÚ4ª««ùè£ÈÎÎÆf³a49qâùùù}Úõk]]cÏÀ69&''ãp8¨­­¥²²’‡~“ÉÄ_|ÁèÑ£1›ÍÄÄÄôæÌ™>bfÇŽLœ8ÑG`FDD X~[ZZxï½÷˜5k?ü0aaaTUU±{÷n\.ƒ¡_oo&$$PWWGQQшX2Ѩ BoHáÐÕ¾ŠBVV‡Âétj+**Ь¬,‰‰‰¡¥¥e@Ò«©© Øïé³X,´¶¶ÒÖÖ¦ù744ÐÞÞN\\F£‘øøx ÑétX,’““©««£¼¼ü†]gaaaÄÄÄhÎ`0îsÌ_(úsþüù€sˆ‰‰ ÚzU[[‹Çãá?øIIIÄÆÆúœÀèÑ£9wî\ŸiFGG³víZ¾ýö[ŸÖ6X‚ ‚0DIOOGUU ®qE­­­”––ÒÔÔıcÇ(++´ª««ÉËËãÒ¥KrêÔ)mŒ’Íf#>>ž;vPWWGMM 999¤¤¤ø´ÚX­VNœ8¡‰©°°0âââ8yòämà]o:8p€ÆÆFNœ8Aaaa¯“ŠÆÄÄàv»9vìÍÍÍ?~œ¯¿þÚ'LVVµµµìÝ»—úúz.^¼È—_~ÉÕ«W}ÂÅÆÆ²víZ¾ùæöïß?bê§t ‚ ôÚL2Dm ]-ªÊìÙ³ÉËËcÖ¬YÜwß}ddd°oß>\.iiiÌŸ?ŸÜÜÜ[NëÁ¤¶¶–ÜÜ\ŒF#‹/Æf³iþO>ù$ü1o¾ù&Š¢––Æ’%K|lX­V HIIñ9V__GÖ<€Óéäøªª’‘‘ÁŒ3‚†3f ?øÁÈËËãСC$''³hÑ"vîÜé#œž~úi:Äþð ÷ÜsOÐ), Ï<ó üãQU•G}ôîÿûظqã 4\ËR9²TŽ,•#KåÈR9ÈR9ÜÙ¥ráŽ=H‚ ‚ ˆÀA¸3H¡ "°Ay‹PX‚ ‚ "°AAîJí-BAA„»iÁAA%‚ ‚0´´™Üe¢Q™hT&•‰Fe¢QFìD£ŒÙÙ×ÍÆ+¯¼rÛ“ÍÍÍ¥¬¬Œõë×÷;Ζ-[ÈÈÈèuY™»’’öïßÏ/ùË^ÃäääÐÞÞÎO***P…ÔÔT–,Y‚Éd¢½½Í›7ó /˜˜ˆÇãaóæÍX,žþyŽ?ΡC‡xùå—¼ÜÊËËùè£ø§ú'EáÂ… ¼öÚkÌ;W+»]»vár¹øÑ~¤Å;sæ û÷ï§­­±cDzråJL&“O¹=ñÄäää`·Û©¬¬äرc(ŠÂÏþs¢¢¢¨¯¯çàÁƒTUUa0°Ùl,^¼˜Q£F‰ÀA±ÈR9C–úúzªªªˆŠŠ hYYµj&“‰††vïÞMhh(=ôP¯¶***ˆˆˆ`ݺu455ñᇒÀôéÓµ0GeÞ¼ydggkÂÃb±0nÜ8<ï½÷¡¡¡<û쳸ÝnöîÝËöíÛY·nF£‘„„ìv;‰‰‰Ô××kBÇápBee%V«õ¶”ÕØ±cq8ÔÕÕ‘˜˜ˆÝn'<<»Ý®…©¬¬$++KÛïìì$??ŸÕ«WðÑGñÉ'Ÿø°/^Ì¥K—ˆgáÂ…x<ÂÃÃiooç­·ÞbÆŒ,Y²„ÎÎN<ȇ~ÈÚµkïú:*o ‚ ô†,•3¤(//gÓ¦Müë¿þ+[·nåêÕ«ÂiÞ¼y$%%ÅøñãÉÌÌääÉ“}Ú céÒ¥X,ÆÏøñã9wî\€Hy衇ˆeΜ9Lœ8‘üü|Î;GCC«W¯&!!{U«Va·Û©­­ 99Y4v»›Í†Åb¡ªªJ;v»–ÑhdôèÑ>égddh¯­­¦¦&’““µ8n·›åË—“@BB³gÏ(oû:ƒÁ@xx8&“ EQ(,,$!!… ˘1cxì±Ç¨¨¨àÒ¥Kw}}•,AaX’’ÂòåËq8äç磪*&Lð SZZJAAÍÍÍ8Ün÷ Ç ÅÇÇ£(×›{Z¿¼IJJ Ø/(( ±±‘ÈÈHÌf³æ‡ÑhäâÅ‹$&&bµZ)..ÆãñPYY‰ÍfÃd2a·Û=z4MMM} ¬#GŽpäÈm¿³³“ššöíÛ§Û°a‘‘‘Aã[­Vìv;™™™TUU±hÑ"Nž­W½•ÉÍâp8¸ï¾ûxä‘Gʳ¯¼ŠÀA¸Û‘.¡«}…¬¬,8ÀäÉ“ÑëõTWWå3–¨¥¥e@Ò«©© Ø·X,X,Z[[ikkÓZ±hoo'..èêF‹§°°P8ááá|øá‡”—— ó›ááá}Š2oÆŽKGGùùùZZV«•/¾ø‚ööv|ðÁ[*N ¢øæ›oˆŒŒ l#ƒ%‚ KÒÓÓQU•ÂÂBbccimm¥´´”¦¦&Ž;FYYÙ€¤U]]M^^—.]¢°°S§NiÚl6âããÙ±cuuuÔÔÔ““CJJ ‰‰‰š «Õʉ'4F\\'Ož¼m㯼ÚèÑ£9qâ„–Vrr2uuu\ºté–ÓŠŠ¢¦¦†––®^½ ÀìÙ³¹víÛ·oçüùó455qæÌrrr¼…P– °j&¢¶„®˜ª2{ölòòòèììä¾ûî###ƒ}ûöñúë¯SSSÃüùó$­|ÚÚZ^{í5Ž9ÂâÅ‹±Ùlšÿ“O>IXXo¾ù&o¿ý6111<þøã>6¬V+‡”””€c·[`K«Gà™L¦[•™™‰ªª¼úê«üû¿ÿ;---DDDðÜsÏáñxxçwغu+ ,,ì{u9»¿7ŠŒ”¥rd©Y*G–Ê‘¥r¥rîðR9‚ -X‚ ‚ "°Aî2¤‹PX‚ Œ¼E(‚,AAX‚ ‚ w%ƒö¡ ‚ ÂÝŠ´` ‚ ‚ 0ƒ¶TNÏrþ:ƒ"Œ &#„ð/æ»nw¹Û $!¡‘D˜“‰0Ådæ2† IDATKHÈ@®ýåÆL%fª0SEµ·÷ã*Ðæåò§æê¶ßã:8ïÀ5/7€}ŽPÚ.%ÐÖ˜HÛ¥Ú¯D hÖÝŸ:pàPÛ¡–hâfNÁ2s q3§~o‚̃%ÜUH – ‚pד››Ëk¯½vSq¶lÙ±cǤð„ï…,ö,‚ irrr())AQEÁl6“žžNvv6z}ÿnc™™™Ì™3gÈŸëçŸÎéÓ§¹pá:Ž_þò—aΟ?ϧŸ~J]]÷Üs<òcÆŒ‘Ê"KAúOZZ+W®ÄårQ[[ËÎ;Q…E‹õ+~HHȰ8O·ÛMzz:IIIø;Þyçî¿ÿ~–/_ŽÛíæ³Ï>ãwÞáå—_FU¥cJ– ‚ ôNGxx×8Z³ÙŒÍfãìÙ³šÀ:xð eee´µµa2™˜}ºfìØ±šØØXª««ÉÏÏgܸqœ;wކ†^zé%­5iÕªU¼úê«ÔÖÖ’˜˜Hrr2v»ÌÌLìv;6›ÆÆFªªªHMMÅn·3wîÜ~—Chh(ëÖ­ãý÷ßçÏþ³–¯§Ÿ~ZºE` ‚ ÂÍ‘’’ÂòåËq8äç磪*&LÐüKKK)(( ¹¹‡ÃÛí&44´O›ñññ(^óº˜L&|Â$%%ì]Ýu‘‘‘š¸ˆ‹‹Ãh4rñâE±Z­ãñx¨¬¬Äf³a2™°ÛíŒ=𦦦›jÁêììd×®]Œ;–Ç·ÛÍÑ£Gy÷ÝwùÙÏ~ÖïAÿÂíGä® ‚0ä1 DGG3zôhV¬XAMM6¼ººš>úˆñãÇóÔSO±~ýz²²²p¹úž0Í¿ÅGQ<ž]Ü$99‡ÃAmm­ÖhµZ±ÛíØívÌfóM›:qâ­­­¬\¹’ÄÄD’’’X½z5ÍÍÍ”••IE%‚ ßEQÈÊÊâðáÃ8Nª««‰ŠŠ"++‹ÄÄDbbbhii´jjjö- ‹…ÖÖVÚÚÚ4ÿ††ÚÛÛ‰‹‹ºÆaÅÇÇSXXˆN§Ãb±œœL]]ååå$''ßT~:;;}ZÝn§8D` ‚ #ŒôôtE¡°°ØØXZ[[)--¥©©‰cÇŽ XkNuu5yyy\ºt‰ÂÂBN:EFF6›øøxvìØA]]555äää’’Bbb¢fÃjµrâÄ ML………ÇÉ“'º[[[¹pá---x<.\¸à3(Þf³qíÚ5öîÝËÅ‹ihh`×®]¨ªJJJŠTŒ!„tÖ ‚ ïu@U™={6yyy¼ôÒKddd°oß>\.iiiÌŸ?ŸÜÜÜ[NçÁ¤¶¶–ÜÜ\ŒF#‹/Æf³iþO>ù$ü1o¾ù&Š¢––Æ’%K|lX­V |Õj¥¾¾>@`}öÙg?~\Ûýõ×X»v-V«‹ÅÂSO=Enn.o¼ñŠ¢ÀÓO?Éd’Š1„P6nÜ8(mвá ÿ>µû‡¬EY‹°ŸÈZ„½"k ­ÝÅAAX‚ ‚ "°AAD` ‚ ‚ ßAä.‚ ‚p·"-X‚ ‚ ̠̓U·ñMí»Ò­ô@UºœNQPUQ´ãJ÷¾®;Œ¿¿êÇ×½ÚRẽî}¥;¼¢((:UUP‚9åúwz Cßþ~vœWú>6EUAíÚvù+>Ûžã¾Ûž8 Šâïz¼ÓPºýuªoÅ7ïø§¡¨Ð½í âµÕò¢¢(Áý}ã÷n¯wåºmï0\ëQzö@Á£\ÿ~=?ŠÆÓ“®·@|ÕkßÿÜÃ{PðO×üžçñto÷Ý^ûhþ —=wÐpÝaüÒs{<]iuïÙï9æî>æö ãéÞ÷ßjáð ï•~—¿·Ÿo^ÝAì¹Üþq®o{ü\î.çñ¶ÛÇíÁåñàvûƽnÛ×NOœ`yq÷„ïNÃÓíO÷Öãö:ÞóÝ}=Üõ­ß1÷uø‡í¶p\KÃ/m-¼ûúÖíÆãqãq»º¶7x<×·n·æðçîúÞ֣Źî×ÇãgóºŸZÿoe²Ü•iÁH¹ÂHD‘ó²Å¥(R)AþKA’––6nÜÈ… ¤0„AaH,•ã‘ç6a$2Ò*¾gP£ßdbžáYHw)999”””hÃÌf3ééédgg£×÷~ó^¹¥¥…-[¶hû:ŽÈÈH¦Nʼyó¤…»S`4”av_U-òÍ›¸žeØ\ÏKY.ÄÈÌwZZ+W®ÄårQ[[ËÎ;Q…E‹õ¡“=‚ë™gž!..—ËEUU»ví"""‚iÓ¦I=î>5ÒZ¯<Ãì¼o©¡å6·Òø›÷xJdÝæ%eàêÒàÕâ;w3¿£Z@Qn®Ë3•Â3¤/ã`¡ÓéïZOÖl6c³Ù8{ö¬&°jjjسgÄÇÇ“••åÓ‚Õ#¸Â´E‘'OžLqq1uuušÀÚ¶mcÆŒañâÅZ¼÷ߣÑÈÊ•+E5ÃK`I¡0"‘.¡«¤‹pHS__OUUQQ]‹[;Þ{ï=l6«W¯¦¹¹™?þø†vΟ?O]]S§N•BîN5Ò.ÂÛ—7é"¼ûS– 12ó]^^ΦM›p»Ý8NTUeÙ²eüå/ÁãñðØc¡×뉋‹£­­½{÷Øyã7P—Ë…ÛífÆŒL™2Eê°pw ,é"úù•.¡Yñ¥‹ðv]é"j¤¤¤°|ùrùùù¨ªÊ„ hlldôèÑ>Þ“’’‚ÚY³f ‹·ÛMCCûöíÃh4ö9–K¾2Mƒ ‚0ä1 DGG3zôhV¬XAMM ÅÅÅ7mÇl6ƒÅbaâĉdddŸŸÓéìÖÖO>.—K.€0<ÖHëh.ÂÛ—·aÕE蹸”ïæ1XʪãÝZ¶…¬¬,:„Óé$..ŽúúzM$A× ÷`ñ‚s»Ýšˆ5jß}÷æßÓÒ%ÃR`Ä.BÏ0ËïP½_xnñ¾8¨7º‘ØE8\þ'nV©{†P¥!O·éé騪Jaa!“'O`÷îÝ\¼x‘òòrŽ=D7{¸zõ*—/_¦­­Ó§OSPP@JJ ¡¡¡@WWäéÓ§)//§±±‘½{÷ÒÞÞ.jA¸id» ‚0üZT•Ù³g“——ǬY³xꩧسg¯¿þ:qqq<òÈ#|ðÁ~ºYáí·ßÖ¾GDD0~üx.\¨…™6mõõõäää ª*¤¤¤H 7ÿ¸´qãÆAy†É‹=k‹7“ÅžEï»Øs‹9ÄbÏŠßBÎZØ[^ìÙkqæ!¼Ø3_¼ùö/öܳp³,ö¬åÝ= {ö^œY{„}"¸óHáíËÛ°ê"Ö¹”)îŠâË(w·À’IF…‰"ç;d‹KQ¤R‚0ü– ‚ ÂÝÄ  rOØøì-Å÷®n'ÜbAzèÔ3`åʃÒý§ŽïiÈÒ §Lë†S†á¶#-X‚ ‚ Ì =ÜmܸÑëÉGív=O@z/×sL â§÷ó æïÎßž>Ho[}ٻټ©7_Õœ.TÜèpu;'z:ÑãD‡7jw˜žc=þ×÷»œŠ]wx.œàã\^·Πþn¯0Þ~(8»]'Š–_—–ž^ËK§ÏqnŸ|z¿~®=á»ò¯Çz<èðø\½]A}°¡^NßW³ˆdo#†~$¦U åºSUÐé®oõúÀmÓé|à ïmOU@uƒâÅ ŠËï:w]»à[§_8ÿëïÛêÝ®Óë»÷1ÿçíïŸ7 Uô:½NA¯SÐéUôz½½AE§SÑét:U§tùºüôª—ŸN§jÇõ:½Š‚‚ÊõmàÿŠ÷±çÿßå½Uü¶Áìèz±å&˜­`i©}äçûæ]ׇ½`y»Ù¼ûæé•W^‘»² KîÈ`ZAßòðáÊ•+|öÙgœ>}šË—/Ƙ1c˜?>.—‹mÛ¶¡(Š6}‰O ) k×®¥¥¥…œœmF÷ÐÐPbcc?~ëÍWŸ+Uy=/53åÔÔÔxHâØ±&Ø`Œ ˆm6`Äf-¬Âh!Ýíœóýqï=ºW [BÛó«êê{ºŸ~ºOwŸsžÓ}n÷½’’’ÂøñãY°`“'OF×umçt:Ñu„„;L×;?À„gff2sæLž~úiü~?ï¾ûn¯e8tè‡æá‡fÑ¢Eäææ’’’Bnn.÷ÜsO7ã  ªªŠŠŠ ¦M›ÆzÔ;cÆ >³bUU3fÌèù#GŽÍ‚ ¨««ãòåËrɈ% ìÍWF°¤#ƒ‘-»\.\.ÕÕÕ1†E‘ÀôéÓ9vìXSŒ‡&33“I“&õIç—_~I0¤¸¸˜éÓ§søða@7¹œœRSSùâ‹/hii¡®®ŽŠŠŠËr£òUUULŸ>·ÛMII ”Kæ&!S„‚ ÂÐ Ð4V¯^ͦM›øä“OÈÉÉ¡°°òòrÆŒÓ/ydffâ÷ûioo'!!¡[|cc#™™™1a[·nµG¦âââø§ú§æ¼¼¥ÙÙÙ¤¥¥qäÈ‘Gšf̘aB¤¤¤„øøø^ËÚWùÆÆFΜ9c;5}útÞyç-Z$êfô[©Aa¨SZZÊÏþs{ì1JJJ¨­­å?ÿó?ûmD&2ú£n`ÿE‹ñÌ3ÏpÇwà÷ûíp¯×ËÑ£G™>}º6}úô^§ §OŸÎ™3ghnnæ³Ï>cÖ¬Y×Ì·¯òUUULœ8ÇÀĉñz½ÔÔÔPWWÇsÏ=g»C‡IGëGdKA,‡ƒââbŠ‹‹Y´h›6mâƒ>èõû£᫯¾Âív÷:r”‘‘Á¥K—bÂâãã‰ï6âõùçŸ ù¯ÿú¯n\cc#Ýô”””ðæ›o ™8q">Ÿ¯×²öEÞ4M>ûì3ÚÚÚb–¿°,‹ªª*ŠŠŠ?~<Ï<󌗘˜(L ,Aa´“™™Iuuõ7ÖÓÖÖÆáÇ™2eJ¯2ååå¬_¿žcÇŽ1yòäkꫪªbþüùÝ ¿·Þz‹ªª*î¹çžnifΜÉË/¿Ì‚ ú4Šv=ù'Nà÷ûyæ™gbâ/^¼È›o¾‰×ë%..®ÛÒ‚XBŸ‘ÙFË?ù…J{{;¯¿þ:3gÎd̘1¸\.Î;Çž={®i]Ë Š^¦a÷îÝÄÅÅõhøD˜6mGå/ù ,`„ $&&ÒÒÒÂáÇѴÐ7çÏŸçüùó<øàƒÝ¾Ù*//çÃ?dñâŶ|„’’~ùË_âv»ûtד?pà%%%ݾQËÊÊbëÖ­:tˆ[n¹E:—XBÿ#O[Akyxàr¹ÈÍÍeß¾}455aš&ÉÉÉÌž=›… Þ.ŸÏÇóÏ?„ÍÌÌdÆŒÜzë­×5n¾óïØ ~ôÑGv9ŠŠŠXºt)½ÊÎÎîf\Aè;²-[¶pâÄ &OžÜmä©ëôd×ø¾È+¥hkkãäÉ“<øàƒÝ{‹R”––ràÀ1°úʬ¬¬”!Ù*G¶Ê‘­r­r­rd«Ù*G™È¿å­W¹–AK蟛¯|›%Ý@ȵ,b` ‚ ‚ ˆ%‚ ‚ ˆ%‚ ‚ – ‚ ÂÈbЖiAA©È– ‚ B?3h+¹Û &Y@vØÏ ¢°Ÿx€¸°¼€/캸Hx´Œ/Êù£dýQá^bWAŒŽ÷öà{ S¡©Z]Òå"«MF|¡å+»Æ;zˆwwÑ劒sö’—³‡øh=Ñ¿;qÐ N¸ÐqÄ8 7N\8lç@GGÃŽ=*.VΣ/²lªŽn;µ¬ªŽ+ìºÊD~;Ãò_²ƒj>ä8;íKY• ÚW×9Žø]—Zìíwor½-»¨Ñó2Ž×Z–±§%*uz^¦±¯2}‰ëÉõt.×ZòR¿r…øŒ­#uzí©þívTÝÃè¡­{ô­¯W8uBi½ÈtÓ£B×:Ò´Î0YˆVMû(-j!Ùð‚³º#ìtp8C¿íÅg£\$Üá ÇwqNWÎ ®°¯;¢ô;ÁåîtNWXo”sºÀéGØäãp€¦ËB£‚Œ` ‚ ‚ CpKAúÊÕ«WÙ±c'Nœ ­­ ÇØ1c(((àý÷ßG)…euÿ¤X)ÅOþøcš››Ñ4ÔÔTÊËËY°`4’ – ‚0¼xõÕW1M“Õ«W“––F[[555deeñ‹_ü–۲e ~¿ŸU«VÙ†”Çã¡¥¥…¸¸8~úÓŸÆX]7P.))aÕªUƒANœ8Á[o½…®ë,X°€°uëV–/_NAA†aÐÐÐÀÅ‹¥1°„áŒì÷"5$ŒÆF÷z½Ô××óä“ORPP@JJ ãÇï&ët:1 ƒ„„„uõA×u[fΜ9=z”cÇŽ±`ÁŽ?Nyy93gδ峲²¤ ="ß` ÃYQDjHîr¹p¹\TWW ojÞ‡Ã0HLLäÌ™3´´´H¿®ßw¤ A„!= i¬^½šM›6ñÉ'Ÿ““Caa!åååŒ3¦Ïz¼^/Ï=÷\LXAAkÖ¬éQþÔ©Sœ:uŠ[o½€;×^{µk×’‘‘A^^%%%”••u›j1°„a„ÜÀ¤†„ÑÚ襥¥”””P__Ï™3g8qâ}ô+V¬`ÆŒ}Òáv»yæ™gb¾Ár:12Çç¹çž³G­¦M›ÆwÞ @RRO?ý4/^¤®®ŽÓ§O³aÃ8Àã?.}QK®È˜ÔÐc£#ËápP\\Lqq1‹-bÓ¦M|ðÁ}6°”R¤¥¥]S¦¨¨ˆûï¿MÓHJJ²ÿ=Mvv6ÙÙÙÜrË-Ì™3‡_|‘ÚÚZ ¥? 6ò – ‚0,ÉÌÌÄï÷÷«N§ÓIZZ)))=W=•è÷r#à…@ª@>ȘÔ0½½½×_™3g2fÌ\.çÎcÏž=L™2å†tµµµu KLLìSÚÍ›7“””DQQÉÉÉ\¹r…;w’@^^žôEA ,a¸"`RCB>Â,—ËEnn.ûöí£©© Ó4INNföìÙ,\¸°Ïz|>Ï?ÿ|gÕYJ)~þóŸ÷ÉÈš0aUUU|úé§tttOnn.O<ñGú¢ – ‚0ŒTwß}7wß}÷ueW­ZÕcøŒ3®û­Voi#”––RZZ* "ô ùKFÈ˜Ô .b` B?#`RC‚4º ˆ%‚ ‚ – ‚ ‚ðÍQ•••2À,‚ ‚ÐÈ– ‚ B?3hË4TVVàâ ¤4’”"Yi$£ˆWŠxž°Ÿ¤4’ÃñIáxO$^iÄ¡ˆSqJáVAA¥a¨°ÂP EP)Œp|È…Âpx$MЖ‰=ŽÈ™¶¼F JƈÒgvёɫKYƒ=¤1¯Q6³Ë¹õXö°R¤ H ûºÒPJ¡”¨ðƥѾvד‰Žï]¾«LOñ}Ñs­|²lý‘O§>ÂÎL”2£|¥‚(e ”tþGËw—‰Õc†Ã":;ÓE§W*–‰Î'ò;:M§|÷²˜]Êì%ï¾”ÝìR–`eíª¯«Ltœh(¥‡>îô•Ò½‹Lçñµå»êÓ{Ñ9ŽÖ£õ £ºèìß=¯žez/{Oeé^GÑ:7P6Õ%ÞÑk½ÿú׿–§² – C•kÿo_Éßú…~îS7ƒ«W¯²cÇNœ8A[[‡±cÇrÇw`ëÖ­C)³‘sgŸW<ñÄ´´´°qãÆnr‡ƒ_ýêWlܸ‘ƒ†_~ÉÉÉL:•»îº ‡Ã3Hàp8øéOJJJŠþç?ÿ™¸¸¸k®©µÿ~>þøcš››Ñ4ÔÔTÊËËY°`|ð|ðAL¦L™ÂâÅ‹q¹\´´´°víÚÏóé§Ÿ&77Ã0Ø»w/‡¢©© §ÓIff&3gΤ¢¢MÓØ¸q#^¯—G}4F×éÓ§yñÅ)))á±Ç‹‰‹äÿÌ3Ï0vìX¹<ÄÀA†+¯¾ú*¦i²zõjÒÒÒhkk£¦¦†ööv&NœÈ/~ñ [vË–-øý~V­ZeR‡––âââøéOc`©.o%%%¬Zµ Ã08wî6l@)Å=÷ÜÓÍ Ù±cÇu(æÀlݺ•åË—SPP€a444pñâŹììlžxâ ÃàôéÓlܸ‘`0Èý÷ßoçý½ï}¬¬¬˜tñññ¶qõÇ?þ‘††/^L^^n·›3gΰgÏrrr®iUUUqë­·RUUÅ•+WHJJêvî‚X‚0ʸöÿV,KF±„þíS×륾¾ž'Ÿ|’‚‚RRR?~¼-½ÕÓéÄ0 zÔ×[x]×m™ääd&L˜À©S§ºXsçÎeï޽̟?Ÿììì>ËñãÇ)//gæÌ™vXW# @Ó4» S§NåË/¿äرc¶eY§×-~öîÝK}}=?øÁb ©´´4¦NŠa½–Ñï÷søða~øÃÒÖÖÆÁƒ»mIÔÓH¡Ð¥ ¥ „ëñÍÞTäI.Œ–ëd ÒÊ5är¹p¹\TWW ojÞ Ô××£ëz·¸¼¼<&MšÄöíÛû¬/11‘3gÎÐÒÒrc£!Ç5¢®:tˆâââG©4MÃétöšöðáÃdee‘‘‘ÁôéÓ©ªª’ ük #XÂõß]â~ý7_y@ÜäGý€µ®“¯_·×N;®¡ÁÍ_Ó4V¯^ͦM›øä“OÈÉÉ¡°°òòrÆŒsCº¼^/Ï=÷\LXAAkÖ¬±?ÎsÏ=‡išƒA4Mã[ßúVúî¾ûn~÷»ßQ__O~~þuó¿ãŽ;xíµ×X»v-äååQRRBYYY¯÷ÙsçÎqèÐ!ŠŠŠbÂÿû¿ÿ»[šù— ©©©›|_©ªªbúôéLœ8ŸÏGmm-………r¡‹%£úQ?`†€ }j°(--¥¤¤„úúzΜ9É'øè£X±bÅu7qŽÆívóÌ3ÏÄLquÍ)**âþûïÇï÷³wï^4Mëu“第,***ؾ};O=õÔuóOJJâé§ŸæâÅ‹ÔÕÕqúôi6lØÀxüñÇm¹††ÛÈ3M“I“&±|ùò]?ü0™™™½¾.]ºÄÙ³gíÞ5McêÔ©TUU‰%–Ðïï®2E(}¸N*­\CöËá ¸¸ðF¸  IDAT˜ââb-ZĦM›øàƒnÈÀRJ‘––vM§Óiˬ\¹’ßýîwTUUÅ|7ÍwÞÉüÇP]]Ýçrdgg“Í-·Üœ9sxñÅcF‰233yì±ÇPJ‘””Ôãerr2ééé=êÏÈÈàÒ¥K7\ÇÀ²,žþù˜p]×Y¾|9n·[:¢XB¿½»Êáp{Ô˜! \ë:‘)›Mffæ 5_÷sáÂ…¼óÎ;L›6-f©†)))Ì;—÷Þ{ïºÆ[oç¡Ë£ škéºÞ=yÚ´i¼ÿþû\¸p¡ÛwX†a`.—+&Ü4M>ÿüs–,YBqqqLÜŸÿüg:Äœ9súáÅ[ ,AAtÚÛÛyýõ×™9s&cÆŒÁårqîÜ9öìÙÔ)SnX_[[[·°Þþ¡ñ½ûî»|üñÇÌŸ?¿G™ °ÿ~š››)//ïU×æÍ›IJJ¢¨¨ˆääd®\¹ÂÎ;IHH //ï†^|ÛÛÛ»K\\‡ƒyóæqâÄ ~ÿûßs×]w‘ŸŸÛíæìÙ³|ôÑG¬\¹²›áuìØ1¼^/3gÎì6RUZZÊl˲¬GȲ³³Ñ4ùÿœXBŸßàFÚ›ïK°‘áZ×É@¥UC¾O 4.—‹ÜÜ\öíÛGSS¦i’œœÌìÙ³»-p=|>_ÌôWd„þç?ÿy¯F–¦iÌ;—>úˆ[n¹§ÓÙí¾èñx¸ýöÛyÿý÷¯yÏœ0aUUU|úé§tttOnn.O<ñç†îËüã»Çƒ>Hyy9‡ƒï}ï{ìÝ»—ýû÷óî»ïât:ÉÈÈ`Ö¬Y1ËJDÊ[UUEqqqÓ€eeeìÙ³‡††Ün7J)ÞxãnrÿøÿHrr²ÜÄÍže«Ù*G¶Ê¨­rzÚ¾¦s«œN=²UŽl•Ó×­rô.DzUŽ \ÇAAK¸éÜ2E8̸þ¡0×É@¥•)BAK™ëoôD–³0Z®“J+× ˆ%Â@–innŸA ,AA„-¬ ‚ ‚ ŒTdKA¡Ÿ´…Fwüá &–2°4K˜ºS÷b8¼˜¦æÇÒ˜ZK „â~LÝú­…Ój¦ÄÒ LÝÀÒƒaßÂÔ­°2)-–q:–®ci,MMÃR_͉¥¹B¾ræÆÒÜ ¹A¹@9C'¨¸.θBq¸Ba¡•ºB¾r¢¥UVHFáFáBY®°ï ; eé(K³fªðo…²@³@Y L Í4£œfÑŒ`È7ƒ(3ˆf(3ˆnøÑLŸíkfÀvÊ ¢;Ð /šá ù¦?oøÑL?ºáE†âtË#èEúÂÇ~t#ÖD78‚tÃöƒv9uÃ@7L†‰#h†~+phàÐCN×@ך¦Ð5…Cwàpè8tº®£ëtM;Ý…SwãÐ]8t7NÍC 9ͦœhš ¥œ ¹êZA-Ž æÆP. ͉¡\•+®âD┃ rbòƒa¹€rTNJ'¨p„Ö>Caò(‚@X`baDœeÄ `-ƒ€$hZVà `XA ‚!ßò4}aç%hù1,?†À´áð ÓKÐðb˜¡xÓô‡t>‚†7”Öðb>,ÃeøÀð…Ú.܇t3ˆn„œ# ·a¤Ï…|ÝݲÐMÐ8t…îPèºBÓš®¡ia§;Ðt݉æp†ŽuJs„»Ðô8tÝæp£éq!þ­4Jw…|ÍŽóØr('JsÚñJw£47šîFé®p¼#´F“æ@éÐÂaš¥i é(-¼n“"¼xYø›6Í ‡Y ÌNT¸…Ub)?àÇ"ìG `YÀ‹… –å ÉX,üX–ËòbY˜–Ëò…â,¿í›¦Óòbš¾Ð±áÃ4CílYALÓÀ² ,ÓÀ4 LÃÀû¦iaš¡?¶˜&˜fØê††aèAËÔ1£œa¸† ÃpbNLÓ‰i80MÁ È ;–¥aš:–º9ÿ_OŽÐç΋/¾Hnn.÷ÝwŸ<ýÁ„¡…|ì+m'ÝëfŸÃË/¿ÌK/½Ôc\]]•••444HÄÀA„¾2kÖ,¾üòK._¾Ü-®ªªŠñãÇ3fÌ©(aH!{  !ÿ ‘¶“îu³ÏaÒ¤IÄÇÇsðàA-Zd‡ûý~¾øâ –,Y@MM ï¾û. ÄÇÇ3cÆ /^ÜëbÉÁ`÷ߟÇÓÞÞNJJ .dÆŒ†ÁæÍ›©©©¡­­””æÎË­·Þj§_¿~=Á`qãÆ±oß>LÓdÚ´i,Y²ÄÞ𸣣ƒ·ß~›'N`………,[¶Œôôt¹$ÅÀº™Œ¢é™iQm"Í)H/84M£¢¢¢›uäÈ,Ë¢¼¼œ––þô§?1gÎxà¾úê+6mÚ„ÓéŒIÍo¼ÁùóçùÖ·¾Å˜1chii¡½½=d'Z)))<òÈ#x<êëëùë_ÿJrr2¥¥¥¶ŽS§N‘˜˜ÈßÿýßÓØØÈ믿NNN3f̰°ÖÖVÖ¬YƒÓéäÝwßåå—_æÇ?þ±m„ #´ßÊ뛜ªp#m¢¤9ÅÀ¸&ß`õÊÌ™3ijj¢¶¶Ö;xð eee¸Ýn>ùäÒÓÓYºt)™™™”––rçw²gÏžõ]¼x‘£G²zõj&OžLjj*………”••…Fî¼óNrrrHMMeúôéTTTpäÈ‘= ,_¾œÌÌL&OžLII _~ù%_}õ'Nœ`ÕªUäåå1vìXxàZZZ8~ü¸\’#™"±Œ¥í¤ˆCþ233ÉËË£ªªŠÂÂB©««cñâÅ\ºt‰¼¼¼˜4yyyø|>®\¹BRRRL\CCº®“ŸŸßkžûÛß8xð ­­­ƒA Ã`ܸq12ÙÙÙ1lj‰‰455Ùer81iHOO端¾bÊ”)rYŠ5ZÞ0åTe€CšS‹~¨2kÖ,¶lÙ·¾õ-cûöí,]º”ñãÇãr¹Ø½{7.\ˆ‘ë:ͧ”ú†û· #™"”Sn¨MdŠP œì`2ExM¦NŠRŠÏ?ÿœÏ>ûŒY³fÙq™™™œ>}:F¾¾¾ž¸¸¸n£WcÆŒÁ4MêêêzÌëôéÓ0{ölÆŽKzzº=2ÕW2331 ƒ³gÏÚammm455‘••%—¤X‚ ‚0ø¸\.¦NÊ{ï½G[[vÜܹsinnfË–-\ºt‰£Gòá‡2þüu¥§§3mÚ46nÜHuu5ÍÍÍÔÔÔØßXeddpæÌN:Ecc#ï½÷çÏŸ¿¡òfeeQRR¦M›8}ú4.\`ýúõ¤¥¥1iÒ$iÐŽLÊ© 7Ô&–4ç°ÅúL¾Áº.3gΤªªŠ’’’˜‘©””Ö¬YömÛØ¿?‡[n¹… ôªkÅŠlß¾·Þz‹ŽŽRRRìÞrË-\¸p×_¥Ó¦McΜ9ÔÔÔÜPyW¯^Í–-[øÓŸþ„išòØcÉ?ÅÀm7À›|ªòT†m"S„òV3€÷75:ÓWs^^Ï>ûlq………üà?è5íSO=ût8Xºt)K—.íþpt8X½zõ5ËòÀt [¾|y̱ÇãéQNùˆ -‚ ‚0² ,™"†z›ÈáðE¦GE5 ‚X£üÊ”›ÐˆjiNAz‰ CØÀ„¡ŽŒUIÛI÷’KDÄÀAA¸é Ú¿ïú^ÒЯ 0Âî)ñ‡]ÛàšÒZ·¸Ä…]òת™@Ø M"ívs_þ7åÂŒ´ÛH%Òv­ß\•výØÆÑþMk;vòZ-7í±+‚ ‚ ˆ%‚ ‚0t´)€7dÛYXN®o:4L]Ãpè¡ß~ «CazÃ~‹†Ñ¤c6k˜Máø Â h˜A Ó£aƇœ¯c¦k˜éFšŽ™¢a9U(§†¥f»Öé®j˜W4¬6ò¯†ã;Bñ–R!Ýá<¬DrI!ßL‡Ç‡ä,CaV äÌf «Ya5©ßå| KWXްs)¬4…•v) <`Å…}‡Â2 åáW¡È«!g]VÐ VkÈÇP Ó9ÿä¢\J×ÕônµP­´‚ºluæÕªÝ‚ (ÃB-”²P) ¥X¨ -Ù‚  ùqÊa¡ô°oX¨ :@yCùhÍ&ªÉB5[h^´ÐŒ°ï2QËvZª‰J·ÐÒ¾ÓD9-”ÓBs˜¨v ­Ý ¹«&ZSØ5›h­a°‰o¢yÂ~‚‰–h¢’,´¤ðq|ÈéñJ…Òj°Ð.‡ôêMF(«&z»Ê»ÃDÓM”#\F‡…žn §hé&zš:Ÿ¸Ðùin-h¢&zÐ@óuÑ}9¤SutÖ‘î0ÐtÍa†Ò'š¨ -Ñ •?%äô#T?˜(,Z[¨>´V­%¬¿µÓ),4,T$MŠÒ—j¢%‡òQ‰ZB¸}Vçùš&ªÕBµX!]W,ÔÕP?RW-”ÏŠicå°PqxéJ±Pi*Ý‚4PîÎ>„”/ÔðêGͪ ÷!‚„梃 ŒpÿrI„úgÄŠ³´p_ïÚÁj ]kÖ•°U…ÂÛt€…Gø¾æ+¥óZ&¬…åÂ÷3 …î‘ûCSèž—Îû„?t0u-ä»´¾ðýÇJR˜)ší,— ÝÐýÑò*̶н-rŸ3[;e(Ìp+[(ÌD #E·õuýê á^avê1ZtÌËá{èÕð=µ#T#¨‡ÊciXz¸Ž 4EàÛqòTdKèJNHN[®©zá›òé§ŸòÜsÏašÃùý~~ýë_³nݺÙšš*++inn–ŠÄÀA„Þ(,,$pîÜ9;¬®®ŽÄÄDΞ=K0´ÃkkkIMM%--M*NTRˆ%'$§-×À7M.£XBff&‰‰‰ÔÖÖ’››kRS¦L¡¦¦†3gÎPXXh‡G~ïÝ»—ªª*š››ñxú('N´ó:zô(6là—¿ü%N§“ÖÖV¶mÛÆ©S§PJ‘ŸŸÏ²eËHMMB#fÛ·oçâÅ‹èºNvv6>ø )))Òp£Á4””}ˆ‚íur³ÎPÂÞ©†N¹‡kO-,,¤¶¶Ö>ŽRÑá@€³gÏRTT:W¥X¾|9?ùÉOX½z5555¼ûÝn&MšÄ¡C‡bò9t襥¥8N Ãॗ^ÂívóÔSOñôÓOãv»y饗0 Ó4yõÕW),,äÇ?þ1ßÿþ÷™={¶<Þ1°÷Nf ãC:Ö ´È7Ø£p$aõ×5`õÓ%4œZÂ:å®=µ¨¨ˆúúzLÓÄçóqá ÉÏÏ· ¬Ó§Oc†=‚5oÞ< IMM¥¨¨ˆÅ‹säÈ[çôéÓ©®®&­Æçóù8~ü8Ó§OàðáÃX–ÅŠ+ÈÎÎ&33“+VÐÚÚJmm->ŸŸÏǤI“HKK#33“ŠŠ ½™"”;Ùè>!9í!^w2E(Dˆþ«££ƒŒŒ âãã),,äÍ7ß$ R[[KZZšmàœ:uŠÝ»wséÒ%|>¦ib@§ÓIII š¦qìØ1ÊËËùâ‹/ˆ‹‹³GÀhjjâ¹çž‹)K0¤¹¹™ &PQQÁÿøGŠ‹‹)..fêÔ©$%%Iƒ b` Ã}ŠÐqÕ¯F{ܬ+S„ƒÚƒ†kOMOO'))‰šš:::((( ))‰””NŸ>Mmm­mµ´´ðÊ+¯pË-·p÷Ýwãñx¨¯¯gÓ¦M†ÓéD×uÊÊÊ8tèååå:tˆ©S§¢i¡É¿ßϸqãxðÁ±¬ØZKHH`ÕªUÌ›7“'OräÈvìØÁã?n+&Œ^dŠpÐnª2E8ÔFVdŠpÎP¦û\(™"¼>EEEÔÖÖÆ|ÈPPPÀ‰'8{ö¬~îÜ9,ËbÉ’%äææ’‘‘ÁåË—»éœ6m'OžäâÅ‹ÔÔÔØÓƒ999466OzzzŒs»Ý¶ÜرcY°`O?ý4YYYݾëÄÀA„!Kaa!õõõ444t3°öïßišöVzz:¦i²oß>š››ùì³ÏØ¿:Y¿~=iiiŒ?ÞŽ›>}:ñññüùϦ®®Žææfjjjزe —/_¦¹¹™íÛ·súôiZZZ8yò$MMMdeeIc 2E8x¯ŠjŸÐ(›"´úªP¦ûTwêÆ’«›QÎ!Py2Ex}ŠŠŠƒdffÚStËï÷ÛË9@hTiÉ’%|ôÑG¼÷Þ{pÏ=÷°aÆnzËËËÙ³gwÜqGL¸ÓéäÉ'Ÿdûöí¼öÚkø|>’““)**Âív¸téŸ}ö$&&2wî\æÌ™#Ï?A ¬Á»©ÇÏw#·æ‘;E¨¾ö{tLªþ¸n¡ým°zÐpî©©©©<ûì³}Ÿ7oóæÍ‹ ‹žŒpï½÷rï½÷ö˜gbb"«V­ê1Îívóè£Ê³Nè™"AAk˜½ò÷ë»ùP9¡Q¶Ð¨õ5Ž0kŠð¦”sTž,4*b` o}IÙá¬Ñ^'7ë ­QØ;eŠPÄÀäUQÞ}å´‡JÝIÕ ‚ – ‚ ‚zA¬¬¬”cAA„~DF°AAú™A[«ª² ˆ¬ª¤ 3쇜²ýN=*>"£º¤ëî̘|T·¸ÈïXýz»—ÍìV>eÇ__ÖKhÝê,ÐŒ° ‚2@™ ™a?ÞUÆ7{—È)³Óé]uEÅi½ä£ËèÌ+¦GÅÙe1¢ttÑ©º”]õv®FLœR&J 9M³ph k„}…®9 t¥:ã84…ŽÓTg|ÄéJEÅ…ôEÇ;4eçåÇGdµ°þhÙ踮ºbŽUgÙµ.e.[tÙ#yé×+» Åk¨PϵBßB)“ÎeÐL LbXVg˜y ù®éÌÒw‹Ögõ¢¯k~Ve·®Sv³—²[}Èëzuu-Yë27ZvËãÊfõRWagÆ8‹ AÛ÷Yà7#Î"`uÆ,ðEâÂrÑéMà«ÊJy* 2‚%ÂðA ˜ð¤„¤¥¥…ÊÊJ.\¸ •!ˆ%‚ ýö¡Ä²ÄÀá&c ˜ð¤„¯Óí,éxÂÐBö"9ïoR£µJ•ô aäsòäIvîÜÉÅ‹QJ‘——ÇÒ¥KIOOïQ¾ººšmÛ¶qùòeòó󩨨`Æ üó?ÿ3qqq´··óöÛoSWW‡×ë%--… 2mÚ´£m÷îÝìß¿Ÿ¶¶6233Y´heeeÒ ‚X£èýMª` ªTŒrªéAýñ –tG¡ŸñûýÜvÛmŒ;ŸÏÇŽ;xõÕWùÑ~ÔM¶¹¹™×_yóæ1kÖ,Ο?϶mÛb¦ƒÁ ãÆcáÂ…¸\.Nœ8Á† HOOgüøñìÚµ‹C‡ñío›ôôtêêêX¿~= H£b` ‚ØŠ7ѰãJºŽ­X±‚ßþö·\¼x—Ë·ÿ~233¹÷Þ{ÈÈÈàâÅ‹ìڵ˖INNfþüùöñܹs9yò$GŽaüøñƒAvíÚÅO8òèÄÀA†9ííí466²råJòó󨫫ëU>33“'NÄ„={6æøôéÓL™2Åþ¨Ý²,íѰ¬¬,­­­2(ˆ5º‘ žQ[¥}(§u#jä,aˆáñxˆgÿþý$&&ÒÒÒÂ{ï½×ëÚW³gÏfïÞ½¼ûî»öGî õópšŒŒ ¾øâ NŸ>M\\{÷­Í6°Ün7óçÏgëÖ­˜¦I~~>>Ÿúúzââ⨨¨†ÄÀÈSm@ªT¦¿žÁ'ÝQèÏw¥x衇زe /¼ð™™™,[¶ŒuëÖÙS´±•––Æw¾ó¶mÛÆßþö7òòòX´ho½õº®°hÑ"š››y饗p:Ìž=›ÒÒR¼^¯­gñâÅ$$$°{÷n𛛉‹‹#''‡… J£b` ‚ ßââb~ò“ŸÄ„=ûì³=þ˜ù„qãÆO}}={öìCIK§Ú©R5zÊ)=HI466²sçN:::HIIaþüù,X°@*FK„þC ˜p/éÅZ™¥K—²téR©aÐÍžAAú™AÁšY9sr¶CZþ¡=솑¦‚ÍoH¯9—†’WWAä6 ‚ ‚Ðï ÚVË¿þïX;O…^û”Ò@…}´ðÚ&‘xÕ%^Ù~çïP|w™ÎãμT§|Œ®ÈqT™¢|º–­[þ*6¿žÊ×%ÞŽ#6/…Öƒ®¨<ºÈwÆGëÓ{ÑU~’ë^¶H¼Þ™^E§QX]ê½çc…nCK…ÒZZØW¡6± ä+…¥©p|ø·íú­À W‡®aûf8ÞRV—8Ë7µ-‘ɘZço°bâ#i;Ó‡>:²”ÎÃꢯ󸫼% £«l—øhùÈ7S]ËÓßE/±eàšùÒ½\ôRÖ®ººÄ_¿lV¸ôOt¸}ÜÕÅ+e2C~”.¥,¡xŽW]µðo-¯a¢…}eÇuúÑñZ·x ]™h¶oÚÇZÄú-£"q¶®N“=\[F™èa‘2F§ÁêÔGl¼.{§\´3í;„h–}·‰ò*ì‡œŽ†ò•†B»Pìÿûn—§² #X‚ SÔ¨ËX¥-wðàA~ó›ßH b` ‚ ‚ ˆ%ÂðÃu £°å ø©é¡+²Ö ¡¤°7©p_sQ&™Í˜z½é]âzåú‹v©áÒ¸*Ož<ÉÎ;¹xñ"J)òòòXºt)ééé´´´°víZzè!>ùäΞ=Ëý÷ßo§­®®fÛ¶m\¾|™‚‚V¬XAJJ |ðÕÕÕÌ;—;wÒÚÚʳÏ>‹eYìÞ½›ýû÷ÓÖÖFff&‹-¢¬¬L.A ¬¡þF¨¤°7¡pÖ(h Áxb[_3cë&w kÎcô]~ª/u7ÀUé÷û¹í¶Û;v,>Ÿ;vðꫯò£ýÈ–yï½÷¸ï¾ûÈÉÉÁáppòäI»víâ@×u6oÞÌo¼ÁSO=e§kjjâèÑ£<úè£ö¦Ñ»víâСC|ûÛß&==ºº:Ö¯_OBBò®‹L Âh´îGWÆÂh¹²²2JKKIKKcìØ±¬X±‚††.^¼hËÌ›7ÒÒRRSSILLÀ4M–/_Nnn.999¬^½šúúzΞ=k§3 ƒÕ«W3vìXÆŒC0d×®]¬\¹’ &––ÆŒ3˜>}:Ÿ~ú©t¡OÈÖpDÂ"S„ƒYï2E8º/¿›;EØØØÈŽ;8{ö,íííX–…RŠÖÖV²²²7n\÷QMcüøñöqff&qqq\ºtÉOMM%>>Þ–ijj"ð‡?ü!F—aäääÈå/ˆ5ÔßeŠðfN¦æ‰-S„£éò S„/¿ü2iii¬X±‚¤¤$LÓä…^ˆù(Ýét~-Ý]Óùý~Ö¬YCRRRìCÓ!MA ,AaÐÞÞNcc#+W®$??€ººº>¥5M“³gÏÚ£U—.]ÂëõÚ£^=‘••…Ãá µµU¾·ÄÀ•ƒRXdŠðkÐ/C"_§^­Aè2EØ_]æºu7€Uéñxˆgÿþý$&&ÒÒÒÂ{ï½g~-4McË–-,]ºMÓxûí·ÉËËëq:1‚ÛífþüùlݺÓ4ÉÏÏÇçóQ__O\\òÄÀÑÏ8),2E8XOl™"M—ß`O*¥x衇زe /¼ð™™™,[¶ŒuëÖÙFVoÆ–ÓéäöÛoç7ÞàÊ•+ö2 ×cñâÅ$$$°{÷n𛛉‹‹#''‡… ÊLè[¿­¬¬”;ŒìE({Ê^„ƒ´a—ýoÞ^„Øç!{¯½#q²¡ ôY¦a8"HaéÛáHo ›0øÔ/4XS„û¡‡öá(¹fA ¬QöŒ“Â2`S@²dÓð­ ë&ô©inö¿\Ü"¡ IDATA ,A†6jÔe,HË ‚X‚ ‚ ÃAûaê?ýbÄU¦uãÇ ;!ò­†ZƒË´ˆÐçÞ«÷‹¦È]! •*CÁAAègm«²²]S¸t…Ë¡áth躥;Qºt'¦i mçtË£pÅ…\0hÃÎ Ñé"Ñé&Ñé&5.žéù¨(˜À´‚bJÆæbøýF @0 àõðùBÎïÇm tqÁ@€ ßO0À0 ‚Á †a„~FH.Ä b˜fŒ †elgš–e»€e0M¦‰ÃGÅü¹¶›P: ‚þ°ó…ü@ø½ð†Â Øùüøý±ÎçóÙ¿ƒ†AÀ4 †?hà øÂ.`˜1ÎâÎ0mç3L¼~o 䜞D¦ÏšGù̹L›q+¹bÚ°[FÕ¡©Ãk¸ëÉwm—žtøýþuE—Ïï÷ãÊt’1'Ì9édÜ’†;ÝI ÍrWü:;‚½!hh ¸rþ6?þ¶¾+ümŒÃÛéJ (-( ¬ €Éùù1õ)K¤¬¡ß±.¶ìþ@Ðv.—ƒôÔD2ÒIOMB)‹öŽŽkoÇëóãõð…3>ž¸ädâ’“ñ¤¤à ðú|¶‹®Ó R1-Ši©TLKcòÄDÚ›¯ÐÞÔÆÕæ6®6¶Ñzþ -Úh=߆i$S¶€qe É)[@jZ.47‡\S\º.@CCÈol„––P|K «Wñz½øÂ®Ã²èÛù”k»«a?:Ε® >‰â9‹)šuÅs“–;‹᫯°ý††Ðï†hm âóùÂ΋i^š€Æ°ß ´Ùnæ´tîZÇâ…yÜu{.ñF \>r­ £:Z ½«£…€÷jصð¶Z¶ó,¼~ƒ¿×o@|:éw‘Q±˜ôŠ»HÌËΧÃî p6ìŸáÊ™ftpñ —†\ùÊÀ민>hQNšu7ͺ‹&ÝMs{2ÍmΊ©½Øšu9‚,.Mà®) ,.MdNA:´Ï„Ž è˜‰·eêÎs¾öêÎÓÚñÚø6´Ü+!?Åϱk¬®.2‚5ÊPr.Â@·‹4Œ ôȺuëØºu«T„ ÖHÄ’sº]¤aAÄÀmÈ–0àí" #‚0콿!2‚% x»HÃ~¿ŸÍ›7S]]Ûíæ¶Ûn‹‰ïèè`Ë–-?~Ã0(((`Ù²edddØ2_|ñ;vì ©©‰¤¤$æÎËüùóíøµk×2kÖ,9zô(‡åË—“››Ë¦M›¨©©!--•+W^s³hAÁA†Û¶m£¾¾žï~÷»<þøãÔÖÖrþüy;~ãÆœ?žÇ{Œïÿû¼üò˘fh©›sçÎñúë¯3mÚ4~üãsçw²cÇ<“Ͼ}ûÈÏÏç™gžaÒ¤I¬_¿ž7RQQÁøCÒÒÒØ°aƒ4ˆ – ‚0¼ñûýTUUqß}÷QTTDvv6«W¯¶§ÆÆFŽ;ÆÊ•+ÉÏÏg̘1<ðÀ\¾|™êêjöîÝKqq1‹-"##ƒ3f0wî\öìÙ“WII ³gÏ&==;ÏÇøñã)++###ƒ péÒ%ÚÚÚ¤a1°A„áKSS¦i2~üx;ÌãñØÓ—.]B×õ˜øøøx222øê«¯l™üüü½yyy466bEÍÃ3Æþ˜˜@vv¶–€eY\½zUFKAúôPÔ´k†©ð¿N,ù8RKAΤ§§£igÏžµÃ:::hll 33Ã08sæŒßÞÞNcc£=ú”™™I}}}ŒÞúúz222l£©¯(ùk¯ ÖÀ#Ë4Þ.Ò0Â(Çår1sæL¶mÛFMM lܸÑYÊÈÈ`Ê”)üõ¯¥¾¾ž .°~ýz’““™¯¼ò .—‹ùóçãóùìø•+W²uëV^~ùe à°°5kÖØFXNN?ü0;vì`çÎ$%%±xñb***®ó‚£ú&b`õ÷肜‹0Ðí" #¸\.V¯^ÍêÕ«í°è5¬<OL\O”––RZZÚküÏ~ö³naÏ>ûlÌqjjj·0Aè ™"ü¦£ r.Â@·‹4Œ ‚X‚ ‚ b` ‚ ‚ b` ‚ ‚ ˆ%‚ ‚0ŠP•••ò ­ ‚ B?"#X‚ ‚ ýÌ ­ƒõܯÿ¿Ø€ðb?¡ÜT?:^Õ%^ÑC˜ ÉúÝÑ¥bý¨ô¡¡:à çi©Ø4VŒìøHxìq(ÞvD~wžø4l§…µ° çÖN8.’.ÆW1rª›œ²õ)¢ô÷ )GO²×9†ÞâéßýœúTvºœCÔoMu— é´zM«uÕ%¯õ” u‰·óí’Þ®W¬n²ÝóêêÇæÕé¢tõTV;¾Óï¹ì~Ä©néz—QQ2f§¯,PV¨\ʬð¥cuÆi¢®CPšºc|½ó8|¾æU$Þ–U±2§évšX™Î´JuÉ;"C”¾˜¸Ø²ôßé"÷´PèA¦Û½¨Kyº•=Z·êROê:i¢ÂìzìÁEꥇû¤·Ö5>6•Oäþû›ÿ³VžÊ‚Œ` ‚0ú(S¦L±Ã~ó›ß°téRf̘alݺ•£GâõzILLdΜ9,X° ÇüvìØÁø»¿û;ÆŒ#7A ,a F¬ácdYýsï'5#êY>(u¢FIÛYƒÙ0ýYcWûÛ¶m£¾¾žï~÷»$$$°}ûvΟ?ÏØ±cû”~ß¾}?~œï|ç;¤¤¤ÐÚÚÊåË—{”}ûí·9qâO=õiiiò ¾2E(£Í^– à ¿ßOUU÷ÝwEEEdgg³zõjLÓ쳎˗/“‘‘A~~>)))äççS^^#cš&o¼ñ555b\ b` 7cA¦™"²…S„MMM˜¦Éøñãí0ÇCFFFŸu̘1ƒóçÏóïÿþïlÙ²…S§Nu“Ùºu+gÏžåÉ'Ÿ$))I.zA ,a G†Ñ0‚5¤ÔŒ¨g¹5L ;,ÛÎÌÎj ½ ðk½vï,†aØ¿srrøÙÏ~ÆâÅ‹ ƒ¼þúë¼öÚk1ò&LàÊ•+œ=_¬®î2=—íÚñZòöA)3jé¥Ì(ß@© J(e¿CÇÑòÝebõ˜á°ˆÎÎtÑé• †e:óîLß©«»Lײctöœw_Ë}ÎFe.—ÙC¾]—†ÐÃm££”î Z” ×Ã2²=Ê+…ò•†Š‰ïLßUo$}§þè4×.[¬|l^±2zŒÎ®ú:Ã{Ó׳Έ.¢uõ±^{.»Æÿú_ÿKžÊ‚Œ` ‚0T¹ö—áJ>ú¹O Ö®]˾}û¤©1°A¡¿øÁ~ÀìÙ³oº1V[[Kee%^¯WAˆA¶Ê„ǵ?¿N; ãO âãã§f,+f›o‚iš1û+ b` ù|³›ÇÍ}’«ai9(ä?‰£ì¢nŸÏÇæÍ›©®®Æãñpûí·sôèQÆŽËÒ¥KY»v-óæÍcÞ¼yìØ±ƒƒÒÖÖF||Ÿ}ö=ôEEE¬\¹’矾GùË—/“””Dqq1𦑒’Âøñãðxùä×2²fÍšÅĉ9~ü8§Nb÷îÝ,Y²„¹sçö(ï÷ûX³f III±]Gìc72¥M×2*¥°,y nÈßA„!MZZš¦qîÜ9;ÌëõÒØØØûèÃÁ¤I“X¶lO<ñ§OŸ¶¿ÃÒuý† –äädæÌ™Ã#<Âm·ÝÆþýûm]‘—»YYY8Z[[IOOqÉÉÉÒ £Á¥ÈáPkþ|;•o°†üE5ìÜÑ·ÛMEEÛ¶m#..Ž„„>øà{dž®[·nÅ4Mòóóñù|Ô××GEEE7£LK…·K™"n/™"´f”)ÂcéÒ¥lÞ¼™W^y·ÛÍí·ßNkkk·)7}Ÿµ{÷n¶mÛ†išŒ3†Ç{Ìþ ý®»îbóæÍüÛ¿ý†a\w™Ó4yûí·¹|ù2n·›’’–,Y„F¶îºë.¶oßΛo¾i/Ó°xñbؽ{7ÍÍÍÄÅÅ‘““ÃÂ… ‡ù ¡Ð竦²²rPLhÙ‹Pö"”½j/žöìÜ‹°SìE({öu/B½Ûñ`ïEè÷ûù×ýW–,YÂÌ™3åi.È–0üÞGeŠðf´†LÊ Ïhe½qΟ?Ï¥K—?~<^¯—?ü€É“'KßÄÀ†çíR¦GþÃKè§fÃu@Ù³gèúÿßÞ½ÇFqÝ{ÿžÛëWðcýZC ¸!TBpHìàNÀ iUµNPDÝæ¯êªº½RüÇUn{ÿ¹Õí•noÚŠ¥×­ÁÐÄ8RÑF„`b0‡ vÀðÙÄ^ïιìîìÌ>l^¯×ûýHÃxæœ9sæÌìoÎ,3* ñòË/OË+rêëëqåÊ•iååå¦ÛzD °ˆâƒE3Û¦f‚Ífî]»"²ì-[¶`|| UUñØcaݺu€¡¡!455¡³³B,Y²UUUHOOÇ™3gÐÚÚ !êêê „À–-[°råJîVb€EDDñ}™ÕÖÖ†'žx¯¼ò º»»ÑØØˆ¢¢"cïÞ½°X,x饗 iÞ{ï=üùÏFmm-¾ýío£¯¯_|ñìv;¤”HNNæ.¥ˆã-BŠkר,+húäççãé§ŸFvv6V¬XÂÂB|õÕWøê«¯Ð×ׇçž{6› óçÏGMM ºººpõêU$&&")) Š¢ -- éééHH`ß1À"^»ÆlÙX Ä(~z,£ôôtŒŒŒàæÍ›ÈÈÈÀ¼yóô´ÜÜ\$''ãÆÜuÄ‹ˆˆ(ì—•bþºB@J^],âµkÜ••@sÇì drrr044„Û·oëóúúú0::м¼<€ªª ƈñÔ:WÊÆJ Fñ‘·xñbäåå¡¡¡×®]COOQ\\ ›ÍÈÌÌÄÀÀz{{qçθ\.îRŠ8þÒˆˆbÚ‹/¾ˆÃ‡c÷îÝB ¤¤UUUzú#<‚ŽŽ8Œñ1 Ä‹xíËec%ÐÜÝnÒÚÚÚ yÛ·o×ÿÎÈÈ0M}Ñ%$à‡?ü!w#Í(Þ"¤Y~jݲ±ˆˆ`…ÁnR"XDDDDѾ,©««ã """¢iÄ,""""XDDñÎ;ï°"(&Dí1 þ;“Âç)TÃà|óyò~60O‚a:py a–§„XV`<*<Å÷ j˜Å(aЍN!}ªy•0U®J”).ÏXMw»mSÞN°< PÝ ž±¢jPcEƒP¤>V…ŠÐüðC|òÉ'FNN***°lÙ2 bÏž=Bà—¿ü%„X±b¶nÝÊJ$X3ƒÿ’ˆ(Ϫ'NœÀgŸ}†gŸ}ÙÙÙ¸|ù2öïß´´4á…^À¾}ûðúë¯#)) ‰‰‰Ü9Ä‹æ1Ç×gZ±ä®"¾3Àåráĉ°ÛíX°` ++ W®\Á©S§°páB¤¤¤RSSù,b€ÅÓ/g§e}"6”[@³³MÌÄá×ßßññqüñ4Íw»Ý°ÙlÜqÄ‹_)DD<«Þ-§Ó øÑ~„xÀü¥–À3Ä‹âorWÛÄ4ËÍÍEBB†††°páÂyTUõ|’ÒÄ‹§^ÎNÏúx‹0N·€fC›˜‰ÃÏb±`íÚµhnn†¦i(**ÂØØ®\¹‚ääd¬X±€‹/¢¤¤‰‰‰HJJâN%XDDDá|÷»ßEZZ>üðC 996› ååå€y󿡲²GÅÁƒù˜b€Åkö9†·¹«(®ÚD$—Ëeê…Z³f Ö¬Y6EE***¸#)*âü]„üJ™s1¬Œ— åÐìl‘8«jš†¾¾>ôôô //;†`ݯ¾¾>üîw¿C^^{ì1VÅÞ"¤Èâ-Bî*Š«6‰£   ¿øÅ/¸C(¦°‹æV Ë[„¼Ü ¶ "X¼f'"âY•ˆM"j¿Áª«3^çhÞÁÅ=-¾]0Ϊ˜Yªwðܬ”Ù{9ÊKÒˆaÏÍÅSM£¨õ`ýïÿü‡çªE$@$X ¨ÉPT E…ªŠP…g¬@O4¨ a+¾1EÏãÏ«zÓTÃ|¡_ŒšÓ}Ÿ÷/OøÓŒËÕó ò<ŸWƾ4a*³¡¼Â[cÙ…q;Íù|õ ôú1¤ëiþzSCºño! ¼ŸÆ|Šo^pºðŠ·0þt¡§ йâ…0T¤¯ÀÂPQÁ;œ_¯ _Åä3¦,CêcßàÉ+@ Ï´/MóMÓ…€¦„{Í8_šâ ÿØ—Op+BOs{çi†±æ»…'ŸÛ7 áO‡7?¼ó¡@ƒ0 ž4M ¸½in(Ф€ôæ‘Ф·ôÌ× @“†Á;-½ŸÓ¤)hRõ¤k*$H) ½iRªžùR…¦)Z‚až§’ýcRSÍ3–R…§"};C4Õû·j˜öü-¼;Axw”ðÎz ¤ðš€âkBBŠ7Ÿ¢yþöŒ%TýoÏ´"RŸV½yT)¡HéizRzš­”P¥æCúÇÞAÏç©ASšgž±Kï¹Å?66yϹÃ|^0Ÿ#ÌǵP„ç<¦ýXÖQýØõÒ7Vï±#¼Çˆw¬hŠ€T4Å“GþùnUÀ­(p«ŠçØðµï2ýß›üV&ö`Ñ}ÜÞY[_¼_5«8477³"ˆ,"šy’Û;kë‹€"¢û”À*˜~‚Û376”·‰ØŠhÂ=­8ôÒ¥Khhh@uu5²³³qìØ1ôööÂív£  ›6m‚ÍfÓó×ÕÕ¡ºº—.]Bgg'222°eˤ¥¥áСCøúë¯QPP€üàÈÊÊô÷÷ãý÷ßGOOÆÇÇ‘““ƒõë×㡇Ò—ër¹pìØ1´··cttùùùX¿~=-ZÄBìÁâÅ//æÃnh¤7\ÄÙQÁß`ñ¼0 Ξ=‹††lÛ¶ ¥¥¥p:X¹r%vî܉W^yV«o¿ý6œN§ésÇÇÊ•+ñꫯ"77 x÷ÝwQ^^Ž]»vAJ‰¦¦&=¿ÓéÄ·¾õ-ÔÖÖâÕW_EII öîÝ‹¡¡!=OSS¾þúk<ÿüóxíµ×°lÙ2Ô××£¿¿Ÿ „`1r‘íå-¸÷ñÇ£©© ;vì@II  ¸¸Ë—/‡ÕjENNª««1>>Ž®®.Óg¿óï`Ù²e°Z­xòÉ'188ˆåË—cñâÅÈÉÉAYY™é3Xµjrss‘ÊÊJdeeáâÅ‹€¡¡!œ9sÏ?ÿ<ŠŠŠ••…µk×¢¨¨Ÿ~ú)wñ!/~y1vCÙ‹ÁVDîÑ™ŒCÏŸ?‘‘ìܹ………úüááa|ðÁèêêÂÈȤ”7õ4@~~¾þwZZ //Ï4Ïårall ‹N§ýë_ñùçŸcxxš¦Áåré˽~ý:4MÃo~óÓzÜn7RSSÙ@ˆ/~§¶="^vœ˜Á Ç[„ò>ëk¦.öbͲóœÍfõk×púôiS€uàÀŒŽŽâ™gžAFFTUÅïÿ{¸Ýæw"(Šÿ†"ì<)=[öþû³6l@vv6°oß>}¹N§Š¢`×®]úg}’’’Ø@ˆÍ~YYYذaEÁ3Ï<èîîFuu5–,YÀsëîÎ;“ÇÐbâ+œîîn¬\þý3 iIDAT¹K—.ŒappÐðI)122‚¢¢"î Âß`±c"î¶gÊé —qvTð7X;Qº¿ì"ôç û@ZZ–.]Ê>1À¢Yþ­²òaD¾‘y‹0Âm·§,''v»{öì¢(ذañÑG¡¥¥‰‰‰ÈÏÏGYY™!øÁ°ÀúõëÑÜÜŒþþ~àÅ_„ªª<á,""еµµ¦éÜÜ\üìg?Ó§KKKQZZò³™™™xã7‚æáµ×^cåRÄð7X™N„h•5ÆnÆJÇCToÆH‹Ï[„±‰Ov'X4KX:qrÍ©`0†Zì}µ=‹Çgìl|7!ÍÞ"$ŠÚõìü+Š•w­‚¬E‹…¼eH4ÝØƒEDDD4Í¢ÖƒõOÿ’6 «CN2M³÷ª?ô^“³®MÍPû’l¾DDÑÄ,"""¢iµ¬{»Í|/=ä4‚ËjáÒ'ÿ¬˜¨ºÂ¥ Hï8t~ß>ïþQ„Ôó(†´À¼ŠaZñ.OAèüJÐçaZŽ/=plÌS:ÌùB ŠÌë¯VÓ6øòHCçÆæ²ä‘"lYÓƒÊâMSÂäXv@~!ƒÓãüpéÞåzÖ'üë5æ—Áy Ï¦±4­èÓÐ×'àÉé<[â{Z„†±PÓ´ðN @ñ§ Å;ϘÇ0H}~è<Ò÷·¢Ò½5-„']†ü†åÀ¸,ÏßRQLó¤!Ÿ4•ÛW?9‰€tÃö ¼îÓæ“0l{`YD@¯W % oàöšÓ…0îK!”€ãHà¿Î½ÉoebÖì$XÔYVVþ_6IšJåÇÞA&â¸9477³ÝÒììÁŠ [Es¿¬±´™ñŽû*š•ÒŽ“1ÔŠ1::ŠíÛ·OËò^xá>õâ-ÀŠ¡ÿâÌ,b“$½òc³KÆY+r»ÝPU)))l³o{°f[YÙ+Â&IS©|OÖ=]"ÆI–Ëå‘#GpîÜ9Œ¡°°7nÄüùó}}}8zô(._¾ )%l6¶nÝŠ¶¶6œ9sBÔÕÕA»ÝŽE‹¡¥¥.\ÀíÛ·‘žžŽÒÒR¬[·ŠâùõLkk+.\¸€ÇÇÇÐÐÞxã ìÞ½6› ›6müú׿ƪU«ÐßßsçÎ!%%Xµj•^þ¡¡!9r_~ù%„(**BUU233Ùþ`E‡/ª©©AFF>úè#Ô××ãõ×_Çøø8vïÞââbÔÖÖÂb± »»š¦aíÚµ¸qãœN'¶nÝ )¥Þe±XPSSƒôôtôõõáСC°X,xòÉ'õõö÷÷£££Û·oןêIð'OžDee%ÊËËqþüy¼ûî»X´h¬V+Ün7êëëñàƒâå—_†¢(8~ü8êëëñ“Ÿü„·`Í<§Ó‰S§N¡¦¦K–,<ûì³øòË/ñé§Ÿâ›o¾Arr2¶mÛ¦÷>eggëŸOLL„ÛíFZšùù‹úß™™™X»v-ÚÛÛM–ÛíFMM RSS',cII V¯^ xê©§pòäItvvÂjµ¢½½RJlÞ¼YÏ¿yófüêW¿BWW/^ÌÌ‹ˆˆhf @Ó4<øàƒúÃÉ“'Mù§´¼ÀàNé}¸ÓéDaa!ž{î9}žO`¯1À""¢iÆ·…–••EQpåÊ”––ðܺ»zõ*ÊÊÊàt:ÑÖÖMÓBöb©ªØtww#33åååú¼ÁÁÁˆ”ßf³áܹsHMM ê!£¹‹eQ#ZVþ¯46IšJåóA£IJJÂêÕ«ÑÒÒ‚/¾øBÿAúøø8}ôQ<þøãßþô'\½z·nÝB[[nݺÀs›ïúõë¸yó&îܹ·Û «ÕŠ¡¡!´··£¿¿ÿûßqáÂ…ˆ”ùòåHMMÅ;#˗/c``8|ø0nß¾Íö?Gñ1 Ñ,*Ó@l’ðA£S°~ýzH)qàÀý1 ?þñ‘œœ °ÛíhiiÃá€(**<úè£èêêÂ[o½…ññqØív<üðÃ(++CSSÜn7JJJðôÓO£µµuÚËž˜˜ˆ—^z Gž}û066†y󿡏¸˜=Zsùº©®®.*QIdÞEèY^L¼‹Ð[¦˜x¡ð¾ßïÞE¨—ÉwÎêwú—ÏwÎø»½å¾§w½ËpfÞEá=ÒLŸ ¨?¾‹âØ»E(YÔYVVþ¦„M’¦Øƒu¯}?r6µ¶"¢9`1À""""b€EDDDD °ˆˆˆˆfTÔÓð¯?ZÁÚ§YÍûÿY‰¼4ï0ƒ ÐÍZ'ŠUìÁ""""b€EDDDÄ‹ˆˆˆˆ1À""""b€EDDDÄ‹ˆˆˆˆ`1À""""b€EDDDD °ˆˆˆˆ`1À""""b€EDDDD °ˆˆˆˆ`1À"""""XDDDD °ˆˆˆˆ`,""""XDDDD °ˆˆˆˆˆ,""""XDDDDñêÿߨ‘¿bœ%“IEND®B`‚yt-project-yt-f043ac8/doc/source/visualizing/_images/scene_diagram.svg000066400000000000000000001077171510711153200262060ustar00rootroot00000000000000 yt-project-yt-f043ac8/doc/source/visualizing/_images/surfaces_blender.png000066400000000000000000006541501510711153200267160ustar00rootroot00000000000000‰PNG  IHDRÀ¶?P, pHYs  šœTtEXtFile/Users/jillnaiman/blenderScience/science_blender/blenderFiles/forblogpost.blendˆ^ì|tEXtDate2013/03/30 13:51:17ÙQtEXtTime00:00:00.01÷ì-n tEXtFrame001ÁQ" tEXtCameraCamerahÿïé tEXtSceneSceneå!]–tEXtRenderTime01:04.63Fq4ž IDATxÚì½y°eÙUÞù[{ŸáoÈ|9geªÒP’@nI –Å`ŒÌ$ ÓpÚ4¸›Á¡ x"lÂîÆaÐ&:pc "€7Æ`ahaÉ–ÐXR™•™ï½{Ï9{¯þãœ}î¾ï¾Te ’ê•×/^TÝw§3Þ—ßY÷[ßÃ0 ÃxnñŸËÅßá%ʃÊK•—)/Uîi™ÿ íÃ0^ˆíÃ0 ã¹dï'Ùûj:|z(Âpý¸ñWl?†qr1m†a–M ]Ýi½ Ã8q˜€6 Ã0žÕOuf͹tÞÔÐGX‹#0 ãäaÚ0 ÃxFìüùad÷±Š™[Ü)›O›Ù¾4 ãdaÚ0 ÃxúÔßÂΆÛz‹¤gŽ»óˆÙÃÿS¶; Ã8Y˜€6 ÃxÁRòYs¾åòÖ[oZ Oѧ’ËpŒÓcucGÊ0Œ“… hÃ0Œ&{|í+øÙøG÷qxŽ_Þø\¾ûì6ÊÆ}aÜz¦·¿dË0Œ“… hÃ0Œ wò7^ÎOìrF`›É%¾à~áï.økÏÁ»×ßBµ½vϱ6èxëæBÉn¸=;^†aœ,L@†a¼Ðx€ò2þæBI(Ñ‚IÁyÿÀ}þ‡Î¹›Îýè³ZÀÖ—œÂFwàfË`þèÓ[h†ñ|´aÆ Š¯½ÀŸU€ ¦4S3”z6/wÿå×=‹e¼~u[Öÿ›U‘[;76Ÿl†qr0m†ñ‚â^þ§)w/a ”‘NYKGW'0£˜Àü/<ÃÔ™j¾vÏ‘Ús<îU›½ƒ£æ®ï±£fÆÉ´aÆ ‡9Ÿ±Ëë:è EÉbF˜£Û°3˜BMQRÌÿ4þÏ=“eì|é¿ql®s¼í÷Y§ ;p†aœ,L@†a¼p¸ïœro€…£­iJ´@|Rª©«Ï)E9aﻞÉ2úü^+ÇLF«žåÖ6èUÚ©†qÂ0m†ñÂaÆ«t"íZ¢G%û[¯ˆ"2ÂäOÂîÓ[@Ÿ¿‘k„¤†o§ö,ë탽†./Ú3 ãdaÚ0 ãÂËøé)/‹Âd›Án!8IÒW(ÎU2ùö§·Œ7“¿¡½fßÈ­#=ʺt–T±Öì †a'sž†a¼@8ÅÃþ: „ÙJJúŸN‰J Íì5,žÎ2æ¯_Éß± 0¦:ôˆ{ª½å/†aϬm†ñB`o¬ØZs&ŸB‘AÈŠÃùÕ_}ÛWÞó4–QÿTɯœ‹Þ±ÓO-4H8NCÌÅy;|†aœ,L@†a¼8Ë["4ÐöñÏ5Rà8$@@"ÔB ¾xî3ow;_¶–¿1rlYAô¸Geý¶³&BÃ0N&  Ã0^Ìx9ÐÀ,àà Ï¡Ð*M`XF– ࡤsà„²š2û󷻌ùŸ9fôÉ5œ97œ"|\ƒÆð·ÃgÆÉÂ<Іa'Ù_¤ëh~PøHÁYQbo(hÊaa_†îm¦u ‰TûõŸàæm,´x3åqm·Ðǽ :Þ*Ãní-…Ã0Œ† hÃ0ŒÅ•¿Çåï¡·ÖºwU9\²x}”Pƒ£R¿_émD ;¨â·³\÷ñaÏÇ¥×É‘ï7u£P½6„Å*Іaœ0L@†aœ.þu^ô=hA î.`ž,i=ó)²¤SZp ʵs0§)‰-îâê×°øgO±èâ |ú ÞœGx;îÀüùnbÖ0Œ“…y  Ã0N³7pÿ2Ÿ YP…€F®Ÿâæ9«£F Ñ¡¥o¹Ëõ]šQˆH —à©—^Þ¹2@çƒT6p¹èËî†W}ˆú‡%$ÝÙÂ:ph Ш':Ä1QºôU eÞâÀª˜3(áâ.ÛŸ¿¶2{ßÉ4 ýIž ”¦P%©¬i‚÷šz–a„d^ŽòŠsÃ0N&  Ã0žgÜñµÜ÷ç¨S#ÞKàïA.pP"x‡ô…çnåE&À>,À1‹l+µP:p”ŽI$Úˆ Df¥‚¡î²éÜ.™—ýú? ³+ƒ]$ÓÈN­ì ÎÒ û7“ÔPõÖ›9ê餶ÃnÆ Âš Ã0žg|Úw3‡<<ØqÏ{XÞÇa%0-ÐŽE›…\ô,`9HRQÊH)LR“a š).0ß§nÑ’&ðP§§j&{§k5¼H¾‘äâ@× ªÃDAP…6ý;³é~Ö4¼€­7ð˜xÃ0N &  Ã0žO¼æÍ¼ô´bƒ²¸äÀ¦`ðw†a<ß1m†q”—óæ ³$ùÜWßÉÛ>IË~Ý×ûÓá³ï~øžÿ‡ß|ôåtóAi¤p‡Kb¯G›ä“–pd U@ ²¦(¥°Ý…ÊÓ!”¸È¤cH؈ãPÁ,jîô•µâqÌ*tU¦)ÚƒÐä”–¤ªsSšõ˜ hïù&šÁ0 ã$`Ú0 c¯æÿ| ¯/ð’¼¼-ò?^å#ñžßà§öùà'jÙ¯ÿ†âî{^zå_ºò®ëáÌõÅEßJˆ7hRi×'õÜwõ-€µú®– hEWÐB”p¥Ô-‡5e 7AvGÑ s´Kv r}`ëû#È8Bœ¤¡Óbûøæb\#Y@b{ xn`(<õF´ƒÙëM@†qR0m†±âå|ùý|vÅ4-ÑHôÔwðà¼RyëùªwòöŸáŸ›åÕw²{ó;Ù>‡lóéŸw¶xäàúÃøŸî˜ùSžºØ§Ý×°”•c¸¯½˜>Èd¨ ?:nDh”J’³C©;nÖ´;LŸÀ•¨âJ\‰l£–ààâÃ{–—Æe°úU”BÙ'4Ùœ šR7Ƭ=…æ ¦{Ôǽ!0û ®Ù hÆÉÀ´aÆŠûùØYкd’px‡ ]$‚ìpùÕ|ýý?ð¬–tåoòàpöÃøÑu,[¤:}áLém—:q´Û;Ýá}ßµe?ÂUh ÌÔ³Ouò˜bìÚtAãh ‰ˆÃy¢ãÆvEû.@AúçO`ΚŠÝùÒUm;¤Ð RÆs¤RêÔدÑM¨¡4E¥Ió=jVóÀ¡- Ú0Œ“ƒ hÃ0Œwð™çISA‚8pè j(aÞ÷ËÃÚÎ>s0]°1ˆ[!rš!¢#­¢Ðu6†E²ÈêMr Ý¿ùö=_O÷ãv6†ñ<ÇFy†a\àue/Ó=)"ˆ *µè˜5ƒ+ØuT ŠK|æ[øçoâÇžbgœ¹g4’ Se/\­Ëë~®n~ ?W¤…C¤Ï•[À!,Ò6+èözºTp­úˆ BY íX4v,K¢òç‘'¸~Æ!ÓD×]ç=¿8¼vúª~%£è2 = ì(N‰JPZGëPŸ´¸dÓóŸ·nð(á®ã¿gò?Û iÆó«@†a”l7@jrKs?hGí¨è#+Ú¬[˜P¾œ¯x¿ô.~渷þ>ÎüÁe!Ã$¿>(CwÜ‚'芹lObåzŸF—5íÅtÏX©Õô¨ Õr€ý,Z²g‚Œ‰Ö‚Ä&×é.³˜ñè6³G‘9*É¿±|ˆ6­öìôPn×s åbd.åo4 I•ïÞ}”#¥èñÉc(‡À f¯æÌ«Yþ}öÿ€«?Jx)èþ-,ÐHñYø»(.0{=âqÛø½á-ô‹wXü‹Ÿ„«vV†ñ ´aÀ÷ÅlTH*Œ6à[Ü(JÝ7Ú¾m®…‚Ùgòm›ZÊàϧ«QOl ]&pOÄÊ1ënL8ŒœVÝN/A “æÖum-ɶqÄgœ•{Ë–~açPA2¹J˜"÷S, ½¥äŠ…÷ÿæÚ›ô¸L +så”UæCh ‚ZϪÄîÓþw\þ†d»//TY×s˜gþá°uqcÓŽ|:¼ùKâ_dùÃ<ñS\ý;± ÃøD`Ã0 €‚ÓaM:÷TŠ—,‡dóÓØºôs'ŸóͼýÅü•ñÅ~úk»{ßYÐ!i’õ¢‡ƒ­úÒÄß…ž%LE×}yAš´šÙ*8XF˜Ï=q 4/”‚ïÕ³Gh ÙcñqOÀ0ÿO¼oXôäo¯æ{Q«Ê¥È÷â¯ý³¼ö o~ÿïÎìww¯„‚àKp4J×S=|¤ ,)‚®£ôx‡ˆ¨$ÿF¿]’¡Múïè¬èWb²ò ë "´Ž x(.à[|Ã\™\d_pip >³–Çÿõ°ååÃ;÷8Æ_+Ø CU:¤½ë}ã¦é¹ìã9V«tÔÅql¡¬¯ónžõMîàáÜkpïâá¿jñÒ†a<·˜€6 ÃÀs¶HúXŽ\=Z;ÈuÉXÑcBäóî+?ïáŽ-Á•zÂ(û‘%e‡6a ªH‡€¤Å+ñ0ëj싽ˬì²¹€MªÎ’•~pNµèCÌö‰-nÊÖÚ{é¾Fï‡ðöûù½;á·¶¿’|Ï•«p !(Á„}x²÷bŒúõHu¹HîÍ<;IåäÜ"2Ö°5“Ñl’cï”ì*¢ïJ<÷R¶~‘÷~!áWì<7 ã¹Â,†a[p½wäŠQ³F¾#³D4…I"°…'ᦰšåÎ(§u¸‚-ÇnªRãa Já):b $;´!v´-Ú¦”:’’ΚÕÃuÝm\$çƒÃ |”êÿ£z7^¿DJ[p Î!Àÿ˰yó`˜+Ø¥MU¶`W©^‡:¦ÂD†"ýÑ` M}“.›e8’›CÆ2³[w̉ϻU(Þ‘%ö·û¼¿)œù~;É Ãxñ¶ Ã0€ûùŽ ó˜$\>pz½;0jë>knHΘÏÓÎy¼dQ£öKTðB‹)áü  §5¾"”´½gT3q ,YµÐÅdÞÐ$¦{½31=ÒëzJ¹¤~ˆÝ÷Q÷b{­¨&ÈžÇ9Y8:œ½Èå—¢ïÇ}3åzõW®@[c)\“AØ;h+ºu·ôÊ0Îz¿ ë^M÷Äþt_^ÜE·Mó^‹æ0 ã9Á*Іa0ÌÀ\¬ ×Q@‡TmSq6o(Œ§áZÓ9¼':AÞ³pì ÇÏ`ŠwÔÉs=L YYûˆsc\ƒ˜uã±^Ž)1ºŸSx‡‰W™¼w(I×àÛaKŠ‚ÊS¸v§¢tˆÇpî{¯øv²¥$á»5T§,ÓrúA*:GÚûr7…®½Õ›ÖŽ]EŒëp;I)­áÊwqÿqÇà¿ÀÎvÃ0ž%æ6 ÃØç!¸BfÍÐE6”ºï™k³6¹ZϧýA‚£€Eanxt‚î Vëi‰sD˜iŸî«Ë!ï!u v™*ͶOr³¹‰ƒßxJ ÈŒY+ ·µ£þQÕ'½P̘ÝÓÛ?[Hâ|Œ¡,á@Yd¸ŠÁѱæ"Ç ÊF;`ŽdÏ׬ ®O;²É¹Á]—¾V¨¡~%ûŸÏ5óC†ñ¬0m†p@“sýzN³ÉÙ+kÇ.n)q BçQðÉÄßÔ'ìïË¡2Z8‚aÏ^ˆ€ ¦gíUù2YCH²}‘de’º Ç2pORîS%5é@AQA'8akÒŠslûÇ+ÿ‘I~€p÷0˜°\ièL­eŸA=£4pè‰.‹ÏË]/>[g]7ÁŒ«MæÜ¥v·ÿÌúVo†r Ë^Wêº!b†ñœ`Ú0 à&„QÅ œm‡ÒEo¬¨`™‚":TªÔ‘Y*èûKæ3pÜ8¥¬¨Ë~R â•(‘¾~¡%¶™!nø"òy„dk tP®Ú gJ«+QªK¤ß®çG%Z— ߯=ÿ‘«þÝîµ´PÁaÐ Â%pB–BÓgäJ !ÙZ‘Êøcãcníã4ôˆ¬›RŽ/3ß’rªó_køÜÎvÃ0ž%&  Ã8ùøû‘be¹• t‚·-¸héâ8¨dw@lW^]ÏöŽöº QDPŸìà>•E+œ§j©<ËŠºBªH„€Sœ#Žæ„˜VzDBNG&@e½|Kº‡ '%¡"6„Î"8bIUà\Ø)Åi}ã¥=S½æàÚy:¨ Ié3a«Oz†éÒº,á°/ºëÆd·‘¼qdV"ëWGâãsrb¬—´û%VwÛ'Æ0Œg‰ hÃ0N,åWqñû#" ¸¾?nm %^#^cѲÿ×~ ýЭÞ,[˜€:tb@Zäíˆ)[M+´Dk|MT‡ q+ÙfôÎ…Â3+¸èÞVQP8\fBð’ẗ€ä‰uɬGÌd1­‰pdB™ì5tÝ0ä¯ö€ŸW‹yñȤ­¯ûó;Ñ•OÒÅTM §úq0à" <)4~=FƒÕFá²A*Çæo¸õ2óH¸ý Ÿ]‘Ñ£ÿÄõ—G1 Ãxv˜€6 ã$2çÊ¿a÷UT„¬ïÏÁ4/7Â$³-7°ÿ>®ý>ý&ý‡ù;ð¾¶×—Iÿ¥¿‡ ±BÞÔ!çP(•¨D·ú;*I@·‚‚¼‡‚JØÜľÄÝRv„eZá JŒInj²…Äu™˜ÎmÁ²žø3´BɵSª}Èúñx¿DEÜõ­*ÌfgËû»Wy¨×µ“á:`"ì(Kh„m]%ìÝ€f¬ìºõ”º|}Ê$"™µcdÓΡ7½îVwºã4wÌfÍÚ1¯½Õ>?†aÂ}´_áý?ÎGþ/Ú÷õuátJã("ƒé¡\ p¨ ‚•¤:B9‚ S ‹¤#k@¹!¨Pt’¢=ún<1¥;k_Ä^d³—Ér¤j› ʘêXP¢ðd‰_²× Zk\÷„ÂË¢”2¸vZÄe`[¨ –MºiØñøÀ>L–ÂõÞÞœ»5úCವW²Lås?ºd²Õ–[u~\Žk¸ùœqÏô»qËØÈ0Œg‰ hÃ0N§ÿ){÷¯L ½P®’~*a $­ÖÍvY,Zß8‹pÇK¸ï‡Xü>Ò|ì]Ý;pà'K…PDzF ˆÉ§ê«†€ÈÊV¡WË*„ ¸H)lÃU¡.“z\x舽o³ ŠqœõØ#xD}æä3ÇS]:¢ç‘ÍuÎ?@;&àtŽÜ¬: D%ú'?ŠwìMøh/Ük¨Ù:$„¡™X ×Fi*ÙÈ@¿>>07@bÉVŒÛ°j1F?­¹*>{“þRÊÃÝðÆ/cï›ùà¿ãGÿWû0†ñÌ0mÆIãÂ7Úhí³ðã"‰é>Ó­„2ÓÙej…'’L †­óÍç» ÷8N€,„Â#QX*8Š4/°KN‘vȆ£×úh¦p¹ÉWW¼'D¤^Ÿ·§ÃØíèÒj2?+IªÇŽÉmÙ4Eõt3Šˆ´¸ 7oP+~Édí-‚VA:G=qÜ“Úî<ùÎ1­˜U4àØƒ*Ò@'xXt<¬J*sK¦S]–X7>¤ë¿&×Êm¹œõ©î?¶ Ÿï~oÌálC„WÂ靿TÍ+^Fù‘`Ÿ'Ã0ž&  Ã8QÌ¿—*ÓdyIRÖƒ ru¥ÉìÑ Üõ/ Ü,iOà ÊK´c©Cè›(1 ),„@J©ë)YM[édX掂°ªhGѱíËÛI‰Šâ§tmZí¸TwdÓ6#8ÆB]å¸ÅŠn›âIP„åáз]±ô,A½cæ‹'u‰[RÞszÂA†.²T:ÂM¥úܽQCçát.[ÕQ+{Êeqr¬Ä[!W(zé|fлs¦áZA×p¡ä³ÿïú-~ãwìSeÆÓÅ´a'ŠS_·òc„äúëƒëŽÔhGWC—žß«ºxXR¯+hç7ij*7¤;8h™v8zûÞ¿¡w%¸q!ÂÊ1-WçÙNX*®DßÑ5àPOŒ)C‘”ž1»54¹¥%BöèøPX›ø¢*tw³¿ Rê9µPB]¸Ajdzó§ÑÊo IDAT¥ B+è„YGq:š‚TX@••ô›¶2=“Ù9üúþOûgxÔeߌ+œo¦<ÍSÂçcQ؃SÙ‚Úþ×ÇÑH[±ós¼å+M@†ñ 0mÆ'‹ùwsÇW0½DuEÅ.Lâæ»¹þ<úv>ô[tïŠ7Ù~ñpãHý5Ÿ]g÷L¡^¥R óF³AÕçn@ KZ¨KbM … %q-.¢JŒ¨"eDA{÷sRð}R‡ºÕŠ©GNÙî]5¡@½•ÁïhGì’£Ë|&@Hê™ãÆ$ŽÞîU©³¤ŽX¡;Ã$”æQægñBì˜L\!…ª4—ŸøÞ£ÊTÀ±UR.P%Â$"åjW‹C{÷y¿bu¶>>UúÇð°n’öë•c“å>~qZn×—ý·ak#º¿dŠ)èj–ž;_Á·}ÿû?µO§aO Іa|‚Ùúnö¾ŠíW1…9”½Šò38s™Ë—ñŸ Š>†þ:ÿùWùõÿãxé4ÿ¡A»õ¿^.3×–™Ý¹Ï‘˜$ÅY&ÿ€dƃ"ÅÛݤ­Ð—<±@€H´@¢¸Ž¶YµŠf5W¥‚¤ ¬YÕÇKan:š>4ÐÅaœŠöOj ƒmêOt™_E6fŽŒ—1•Z{Á×ç¾(8´„-TÙ‡É."¸H¤¨|(=z7‹›ÃeƒƒJØíXì[tàŠbÍÇÕå]ƒ’Yº{‰ÜdGrÔý£U:dÙv›Znq¿®`"ãà4LÖ«Ú$Uí÷ BES1·Åë^ǯ¾•w?nŸTÃ0no»À0ŒO§ÿ7îûe.¼‹Tßì!EΈB5çÒœ_Âg~>W.qøŸx¢]{Ãsÿ˜Ý½•Áw”MEšÙÑ è*ôc›5•?ËÕ¼@ÆÙ~è5¼ü³›HIÎÙÊàNö×n$á(²ÊÎÓ’P :GôˆG Ä…Ö±tƒ%:î°œ %jq©ýûòvŒ%aT«z‹r¬d¡ut™ç;ÛÓC_cA(˜z^þ%Ô§é:¢«÷vOœzïoSƒèl{Ï÷c,iáºpØ‹óŽÃ’¦"Ä‚X¦ù‹¤¼çÜÎQdÊx\C J“ÆCV_g}`a~ûHPô­~í·º„Ó .y·.Ù·àBŸÎ]Rº z Y²qóïÞmŸWÃ0n«@†ñ‰áηsöµCð˜Â¦)άKÚèIØîÅ–²<»ÂÞݼþ‚¼æ¥Å¼µýñ¹zÏââpcsþ³OeéÞ•Ñkå:5if–¬Í'%'PÒL PA}i8”²Ó xDVYê×Bçɘ„`áh¡*Ùn(mŠ”î‹ÙÒBúZrºjP]·@älêÈ^x¤d› Ñ%§xäCÀV…ìq-ÞóèoC (цÒÑÁá5š}Bɵ–N " ÁŠã&r“­Ì‘™&½Çc™•¥‹lµG¯KLÛ¸iÛØ¬Cy8¾‰‡Ýdp—õ¥ôsaÚ$Ù· ô4J¡äe÷ØçÕ0Œ§… hÃ0žkfßÊ?̶_}¡Ÿ7–ÅõIITy¡*˜œbÿöÙMÝ{Åô³šé}<ü¹?j€z>¼‰¬—ÉLEJüMÓLV¾Û ñ)Î šu_-$Åt¨ iä‡z´ S‚â;J7ôóõU:Oì§y·ÐOHé—Øø‚Bh"Á ·§B'Ę´c@íïY&Lãˆ>ù¹Ab EvÝR®Wy'ë²^²`ÁþŠ¥H…jÍ:G{ºÞb¬÷¸aãÒ!î7‚GÖRYFE>…µÒ‚£)Y–\:ÅËf¼ëÀ>»†aÜ&&  ÃxN¹ð/¸øeÔY½ùH [ÜøÜ¥É&Á/)ðaº,.nÝ÷yÛߺ{ðö÷^ýÇ×Öƒ7ÃÝšô†ÅPHF¬# &QY:€Yä!Ž­÷/gÁ•,„H²&»Á&=HbG)"_D+ľ ­ÃꬆŠ+D áf*9;(3áf\aÉÕXa]YnžÇmÇB²R}¶ÿÏ¥(i"Ýu:¥¾ ‚‡Nyò1žx„PÁ eAˆPÛTוñ|ýXTëÝ![Õr}Æ «ô@º$©ILo]fëÜò¡ëŽ‘yšw˜mO°I²Â;pB 2ÇDhkª9Û»`Ú0ŒÛÅ´aÏ÷¾Ÿ½»‡*`þm~ž.§ëâ/¦oùé-¼;ø9në°Õ6zI¦¯žn;¿çÚ#¿NóP6tÌ@’wÀ~’Sëf–Û±™iq nRॻ‰D„Ò1¡›²tLËÁ´P¥^7 Dŧ-ŠŽñu)‡.ë E–Ä\@7ÔܽPM‰UÄ3qHäZG›E;ÇqÈK.—ã-‚ùHŠ³Í–H¶«YÛó}úì;{´žðÊ«îRNYÜేX „¨ H¿uc\‰[_—k}ªº˜ ý.R´\Þb(Ù$ðÍ„ Ɇ2’<À$©p·ñ¾ø}*=aQR£SØgQ³u†îæíµO°a·‰ hÃ0ž#î}gî†äo&“ÈGÈÅ–¦êc/Õqã2“}Š%qÖÉî¾T!\k'¯Øyɷ߿ˇ–ëN­‡’i¦Q…š4t~exZnxvB{ ¿U7c'è FÏâ4 ¨ç%M¤ÊÖ?F\Záè¼">óZôžŸ%§”¾Â.žVP¥øH¥h\ÉH×WÄsŸÆé¼éåY®ÈtÈÎÙ;óqŠ*"”ðá2UB‹\9¸¥{ÓDÑ¥úî¸Qa½Éϯ‹Z=.et&”0ËZ<ógúµ<“¾º?lDŸÝ·×/"̾ñ`#«¤§„­ì%]ºèæÐà¤âîóö 6 ãö±Ã0ž5åyàœÚ[ +aÝ~ʺòóÙmÍF] ÙÌJ,Pw8§U×é^èvÛxçôôâÌ÷?ùájú¾¾÷OOSö3PgÉl­ª’eEUÒ¹Ð:*8U9_Ä–à>=ìØ÷HÁC†]„ZŽàè2«±ÄAì÷ 1tˆˆî#zJó(*H òtYùY4œ1 °“õ;þ2ëóÈÓy~Z7—Áá"ðï½Ú)1€R8&[ì{˜@“TrÿþÓTà_¤™51Sðù ³¬DíR£§CºÁ ^€.Ѐs¸8ø7`•Æ­ã{–i3ýú@JŽ+?ç#{¿x™Ö¡_-Y¢ž½sŽ.ÚgÚ0ŒÛÁ,†aêY’×õê9Ë$‡wƒ†Ž‚:$"%T¨£«†‡´ ‡"´êà¡4Ÿ6’—êûN͵ÄéÆ?=Î9­œ?Åî9š@ˆfRX)æˆGΩ“9»wK(ñÂ"¦’óèÁ(Òqì²ë¢þžñÜ(Réw{é;–IK• ðC§Ho7IJžº!éô¡²î–lÊÎ7ͮͼண¼ÊtÉG?À;ÞoŸlÃ0nó@Ƨ”s¿ÅÞë²)°x7W’þëy½æ~pÖ=’U\dZvD×k¢ù8•qxµÏÜ}å²ÍÜҘ®é”ê¡Ù¹òo.½éõ×~:Ӛɓ0FÈÁð>ÎÅÒµ¥Î;Ù­Ý¢õm§êËGe»¤-ð×hK4R¸!TnKð½7»„%]›¥ÏåðIï¦íœ·!þp¸ ˆø8˜zCD{ù†x;c=Xåx¬fqˆ2εܙ“^RT™ÕD².15–H·*±Ÿ½“¢¢ 9œ¦’|­8[É[¬ëåç6­—lÐEö5EŸøE‹D)z¯ ”h›ªãš6TQŸ;ù—ù>wWL×NÙÄÇÕ¹´à²‹ÎѶ.Ù$Ã0ng»À0>•œ}ÝW<ÚC÷^̽‡¿“{ö™}ïótµÏ¼•¹;¦kÐeNa’J+’Öõôâ#m1›\í’£#°‹AUG®…î‰e}u9?8ÿÎòAXdoÕ¤¹Öã º>)ÙiDQçt6¡*B\nµ×˜xê *:ÇÒq3p³c?²”d¨P:Gë „ЗEq¨” Ã£Q(üª¹°×Žñý|o‡ësŽYOÊ.!¸e´óªŒº9´[2QœŽRßÝ(šy†;)†ø 霭3à),$»¼É‡xÇdäËÒe©ý¥T—•Ï޲|M¬‘ ?‡©`:XeœÇ{|ßÎ8*sɾÜÈ7\Öo¿î¤2ÑxmÓB“ÎÕ¾³©Ñ ÙÞ¶?H†aÜ&&  ãSÇå?d’YZóoä=ì̸çïroé{~¹­&߯…·¬tL®ŒÝF5ÔodØ‘Íô>’Å1¹ý ,:ò$Õª#½möqïngOpÓ=g`™%W´™†–¤Â8:´!"ˆSçu6¥®Ãá“4KJ¡r[Ôx%ÐÁ¡§­VÅÔ¾q0$1ÖBÓw :¢§“áþFh|ê2tDaé‘QÁEB? Ü!Š:‚#ÊÐt¸BÖ÷¶Ë¬/n}¢u~ ÜQ[°ótuÈýeºÚ æ )Á#žK÷1ÙÂ׸’˜/+OÒ'¥×Is$':™‰"0«¸k›Ë§¨¦È ‚G+4¢}¤÷¶íŸ.Êt0ò¬*Üü![\™"¢Ç^ƘVi‘²Aú˶n‡0çâEû›dÆmbÚ0>ul¿|õ´Ûèëk±ó’;¾žû;ξíù²Úg¾y5øM׎d¨Ù(]ÿŠ?ÉØ\è³"tÿÐ$%}Wᆧ©PÑ®vqRžÿ÷ò8êÐÔ™çÕ%ð™c·`§”y©U¡…à•BÜÖÄ÷/tB uM] "L§:÷·Œ—Ù”¾[ŽV!Û·GP|™½:½É°¦“‚I«ÊéËœ¾ˆ/(jŠšsî(ñ~ý8æëPo|ù3õœÎ‡r—Ó;ìÕ”U ²+ˆ.)ãÞ_1M{túM/6\‡²žû‘Gpy(®¦E_€¤ËªÚ-š-N_´¶ Ã0nûkaŸ"Nÿ$g>íhŠ‚¬ØF*Øz€ù÷ÓzÚßø¯ù¥4˜PÙ(6çQ\æ48‚dOŸ\fú;osà {[qðÄ9Šwâ î| ^¸Ì‡€T í;ÞΤ^7Ïý;Í|²˜a»ÔYÁÔI¡âÕ—¢7?VÞÀW”•Ç(ŽI”à¡Ç2hû¡*âqR >UIGGÐævˆC&mŸÈÑ'Ä‚…æf‡~¬‰M„…ÇB¯Oþã˜)ÉŸœïÞãöù—)§t!›z“N<ª9âi;¶v¹ò2ê91 ( Ï %”ëMŸdEñ#Á ‹dÞs Ïqeއ³ ÊaC×àïEÇ/Wîèˆ ­R¡¬Ÿuù)—›Å·²!…n]Üût£÷U%õ5¤äÈCÛ'Ã0ž«@Ƨ=ÄP—~ƨÝI꘩¥-ÏÝ?À•Pþ…OÙjϾ°… mþ9q¡¼›£=òL1Y¯¶vI%Š'd^Ò ‘–¶ÝÙ_œ árî“‹ïâ ³q½ ZE‹o[ßÞ¤½¦]+­øÆ­s-t¡¥rq8Ç´b{ÂÄSõòJ¤¯CCW#8¤@ŠA¹jÑùNpÙð%Žt¸#^ Ð)ªà(¡ˆÄ܉áR6v¹~eRl´Í¹ C¾‡×í Cí8®ž,Iî±#DŠw¾’zW"ŽÂá„kÐöà*ÍK?òH™y=Ôi–M§á4Ô„ÀÔ¡°ëØ.ÁJ\…x¤F8º'»’®Îrý8Î÷¼É4;…Æó0®ŸT‹>ƒ¯@ Š;æâ0 ã¶0mŸ"Ú­ºÜF‡CXKr8dÑëžÓwqïOsî×?5«}êëÖÚ×$Ê›\G¾ëgݤKöÅzR–+bºçHÐÇXʨ.FÐùeWùå.|Œû˜nÚ•-•2ˆÓi¡[žÒáEÄá¼"eÛ žÂãS¢s]2sH$DGðh"7‡ÐEÚŽ6ÒºHi•Nè<[mSttB×Û£Ó€n |$T‡'û˜æ‘»ìgÜÕ¹ÕgÓÆ ë9w>ÕòÉ^8Š’8žln56¥ÿoX Â]ŸÎö%ÔÑ)Ñ¡ï¨2-’‡øHXè±Ë°ÏTédçIIG:˜ Q˜8®L™ú¡<ß_-ôS¥€q”wD"”¨l ‹ac´®VÙg'?‹ò‹±Þd¿-™F^ùéö—É0ŒÛÁbìŒÿNó72û ÿ‘æ_ãϣÓ'ŽHRÖÛ°âFùv>É£|ìÛiꓷ³¿ÎÞ}G='²¯ËÆæÈq®Ù‚fÅìì°¹v¬³ç»¬¬C~¢²ßÆà[ïDâ©ÑL=ŠèÌË©ÒÚ¡ê'ª¢ª“®E*ð Ð â)+ÚÈŽ OY  @ã˜;Š4¢~ÂNAý0tº_…G'¸r0Fïwt‚wH„–¶`áÐbè’Ô¾ýqܹt‹Ð~£üL¶3óý¦™›<…UCIò“­åKîz wá1¢ B€ÔgJëNoFß4ö>œ6;úvX~"­çBÉV…oˆŽ+3þø DýîF‹T€oé*´@òczlÞÈ‘3m²~AvŽå_z. –{Ôqéû{iÆí`Úx¡ã¿˜;žÊ®®Ó~”æý,þ3‹?¤ùçŸÊ ××¢Êô¸`²ÜTÊz)×Áé³Lþ})W?YŽŽâªõ1Ñãß’#\›lvzå÷¬Çtd›&E÷RÌgSÁ{‘ÔMq7‚ ‡[W¹ç4œDgU‹kQ_ÔE+¨ªê*E!*•ŠÁ}[*K¡õL+–ÊMX‚+)…ØáÐ1­)Ò %e˜S8„%·ëóóúq†}‰ZqýL–HS<@Œ™ËyíN6î&Öxdhˆ®ûó†ÎL…k£Ø?Au³ë'#^¹“{ïÇ×(,J `‰t%œm¹Þ¥­ë64tÕeÚ§Á+”{ >}— %!°èÐÈ¥€÷à€ˆº¡V­-AÁÑMi}6J&¿¡Çõ­Ž‹žeŸwÜ…œfS{:sÚ-N]8#c†q& 4þ ¹ëmÌûf¦)nNy™Ùk!hþ‹?`ñö‰ðo?Ùë¶üÚ>‚GÒ|s÷3ëUFÖ«’3¸òßQÿG>öIùêyòêÕjä²>ïðc£ˆÎú£Ê1o²ùœ\U÷„Tí/<ipw¿ê]Ü>!°vNW<@ EIq€†›rð¤ÛŸKYŠYˆ÷45ÝTÀõ£R2Uê óˆrF™ED©ktBÓ•C˜šÆ¾tHD;4®Z ‡õw¨ Ò VžNÑ€€wE{}ÜKºª¹¤k€:ão×Í3›6èñû ÖæÆq-’®ÄbÀ—Ü}?g.0éPÇ2eB;Eðà+\Dª$‘Gã~A(Y a›†œßæÁmÎLðDˆà—wʆ‘éÕRþöÞ<ز$¯ïûü2ó,÷¾û–Z^Uõ¾N÷0̰ˆE† „BÙÂ,’ÑXØFÆÆ äÐj[a‡„Ì` ,BȰÀ8¬‘ƒ4hÄ: CÏtW/µ×ÛîvÎÉüùsò¾<÷ÞW]ÝÓpÍýEEÅ{÷{Nž%O~ó—ßß÷k¨ PÔ÷;Âêi¦SŽöÒåñ¯«*Š‹ xÝ ©ö(žá3>Ÿüž¿ 6±‰Müÿ-6zðÓýÕ<öÓl-8va§3¤|'å;šofþN~“ŸÃÿß¿GÍÓW™+NÖ§»´/³°”–¦ŸŠ¾øidG\ÿ3T?ôö¶Ùî/CÛE&˜uàæÞ!gTa¤êöd«$ûHÂâh±Ú0^Ý‚-ÈWà8^· ìîH\3ÁÏÔî‰äâÅ ÁäÆœ¸|$3ÐNçA„Ì ˆa+P›Nì93†º"x¦m™bK#nbZ·A%žAèÓ]¢¯¡“õðŠBH®F‘ÜèÅùš3ÌéE³}æ†öŸŸ$Ù¯±med–ñ賜»ŒsHÍ0cÖ€ZÛ?‹4XAqÈõ‰™KµÂ†_\dÑG½fØ*É[W%ª˜9¶†ý!‡GI ¥D2j:©“î&.=¾ÿ ùäç´sñ¬’hqØøÐV‘.RÁNM6àÉwþž茯³<1ä+3.8.†¸<è´Æ'úÒŒ÷7ü¢ç}›Ìø&6ñû'6zn\þËlÅŸ-ˆ!Ô§òâlìsÙú\.üŒ‘Ûæÿü½háü#l=ßµaul¼w‰o*màÜ6åßâE¥úÛocƒGïXÖ4ÐX4ÎpÅ[üºäD(ëŒBRÜÜnÙ¾¢š2ú¾˜tÀ6ñýn [pn/>Ì®‘›ÆÔ)º€S¢u¶1¨!Ð\g–: Ã\i tXhæ4‚'“è\í9Úœ21¦Ð,XC¥x`j õ˜‚Ðâ?¿ ¥Ä™Izî¶O_ÑþåM•é|r’~w¤ã[«'ËxìYv.v¶äõŒóŽ\ÚÁúb€ñXOfóâ °›Åié•JdH‡˜nh”L1‚U, 3¡α[pض7VøÕÐýÄÙˆéŸõ¢³¤“„¢Ï¤—äy ɵèA÷zÁË<öÔÛÚé ßaÌWáöGõ#­'¼XÔ3O:4§àç›æñœ¯©ßÝÔïgú9›Wû&6ñû!6znèäTÕ¡ËÞ…~Fʦ0ê>ÈvÙû2Ê÷ðÊ7ÒüÓ·½…³ß€ç{cü’Ä=b•á0€'~ˆWŸbü_¿=o‹÷vþÌi˜hÚ,+)kWÛÓVý8"Yö”Ÿ`£Š0±¶LVlYZFÁ,¦éð ࣘš­ºcÃe#T¥ LðŠWï‡øNuN$žŠâ b©Z+é@¨ ceõ(f…qM®å`‹–˜yWN74§5mA8Œ¢5ÁpÔbh¢É<ÏãÔÈ$¶‚ó$íjúOK:¡Zºìɨ` IDAT–Ácš°½Ã#O3؃‡ñ+Œ4Bh­Ëá(PÁ9ËvÎÑâÖ¤©èEÚÛ%OfÓÝÜ  L×c `™Ï8‚*`C““´†€j‡Î»zP] ãÎô¥ÄSçÅí¨ûa’Ƥ<ŸX9.Œå›‹øC.=ôvõ÷­bë?3£¨aRst„ %.`fðÍ0 eôÍ‚M.ŸÍÁoòÚ»7o÷Mlâßzlô&ÜcˆK½].*ÉÚIÎ0æ´kGñðp÷Ç8þoßÞNþ%á.+Ž-Ñ4Ï ]©ÆSÀS‘»ÿ!¯<öÖ·6ûŒžÕœF´´V®î±T2¸Óç|û„é»`Š×Ѭn±"oûÒ>ÞîmﴙцüPU*;›ÚñžÑbàLQøz–…à›Æ`pPÅ™¡ñÆá,Þ¢mÈ”-éÒ„¹cSyÆ5’ ¢;"¤À„ît‹ `:t›Fi*B‰.¬°‹d*RÅäz‘`Ç&"È”ª‘*rtOþʃÕV'DöåáÇ)Kê¦;kcOð.Ãf˜€÷(rŒ§ŒrŽ\âœ"ëh6YÈÁR+;ÂNƹ’¡£™Ó(™eh¨Ai”Ì´bfðM7!Y.—\âo,j¢Qú&¦-”¨ßRÅËÛ^60†swxrÀǧoeßÙùa.}#š:± šu?Í©ˆâå¶›ê´M•Å„°…ÑÛŸJþ›¼øöcèÁ·P|:'ÿ”æßj±õ&6ñû56znø›§™*»xà[*eÖ@”œ]ùîàŠï${‚;ÿÉÛØÂéwS}W,ƒ‹qÿ"K°{q²ç%|˜›‘æ§ßÊÖnÅrÛÖjŸõ«¬üOWK™ø%O߯°$Þ_¹°Ò±)h¹+‡9GvWL56SÈw­'’ÙÌ õu- f‚œÅŒ`,Át¸¼ÐÎÍÛ yNSƒbµ„3…VݹòÄJÓÖ FJt àR [ñ4[ó‘v0L²éÄ“jb]½^ÓËØ¬HÚ%· ÅÐ>ÏåÇq M'Z'‚s4žf†+±B0ÔÔ 6à*¶L,ò³1Ǽp"\PGÂ)™ärÎÐ0‚§Fìed cº*Æ­ à°A[1Á ¨¾‰™øT|‰’³ND£8ÅZr›_­Á âñ´òsø´?ÍÇÿ‡·¦×Œþ<ý•NФ½8eâ†3ˆâÙÛ`>Œ¦ŒÙ6þʧÒüs^ù¢·å¥T~ »_Ãè È3 Œ¿šoô&6±&6znÈ^§ÜG¡'HÞ=ü’Vl­Ò',\ø“HÉíÿàmkb`òQÊg“6Ÿá±jº¶šW#žûϱýyñ[™ï[w=‹h“dà_Â(¬Ó`Ýyq¶ŽÄ’ÁÞ'Ä$ôèE²H^ü* V®Ån‰›‰­Æ€I!8Œd…¶) ê€1Ð⃚6‹l0¡ÃÐâ%c© 'Î’YT™Š2C<|R@[¦ˆ"¡ko0x¢‘G“ЃÖÔ#¡+øŽÇ"­…èJOm«3~~ ¯*¹)Íi¾v2cÿ!®<Éù‹X:ÂÑXËê ÕŒ]‡H—–nµù¬Å9L X¸4±›„xµIæ0 (#Ç#2ÅŠX=é}gž+lg4J£µ£j,Dsˆ–àô ?&ñÙ1‰òô Y”0ý>¢‰´K0´ƒÓ(º÷è—Â[ Ÿ¹Áùýî™$)¯$¡ø§w}wôÕE†vŠ’ÁþòÚçþÕ[ù6ÚþïÙÿ6Ša×Îv~²uÝ÷qø_mÆ“Mlb)6zndï€$µH†V¨?-øÂÓV®ug°pñ½È.·¾üíjääÿáܳ½ƒ¦!g`P)É¥d:èà™¿ÁÝoåµw¼5M5;§‡ Ñ`f]Ê|•ˆr#EW0ôÒ]•"0Irx©;†‹û‹¦: œ0²Mi Ñ,³&ϼzšm¶vnÝA2Ä`¢”„œàŒ FÀaÀXÄ0÷[FQ¯ãÆ%ÞÃ"4–Úà<.tÒx˜®n¬„öl’Ö!‹ÙPTSêµ!´P>1 ÙLÚrFCX=4É Ç|ðÖ.Ï~Y©:­=“ákp…Ù”‹¢‰3TY'óWx6.ä,(ÈmçÊúÒÝÊŽŠàY,ˆv|èî+Úù§ ;ŽÆc•vÜëSÕBõŒ´Õ$NÛ÷?·ǧR$!>0ô3Ö!¢Ò…ÐÇåw¾ýåS”‹ýôü¥DVˆìéòËbÚÐþ\÷IÞ[ðäÿÆÁ¿bðØ‹Lþ9‡?Fý÷ßdS/ü=öÞK.=CÇE ÷ÿ4'?ƒÿ›!e›Hc 7ñàFþdO®¡‹Çéb±ß€]Oœ¸ðeèOsûkÞ–FüU.Ã)øà>(ºÎ’mÝù‘Áþ³èoqýëÑ|Âo‹ Ý ËèÕ#.µ„DOcq=;Y’Z[ºìÒG &±Y©ûŒØE6z%“ F0 ™wm0ˆmd>YŽZë37T#Hƒ¹³³³só&´‰ªyV1í£¡¨§žPî` P°†}Á4 žW-“öÖXBËv¸#2HG5Š4ƒ,RŸ}üb¹Ú­ŸXO9Aî’W8!X°Ìì) 8œ‚'š6½ú$Ãþe¶v™O»°¬!RUbqÂ|Ná¸0`Ú…FØ XÛ±lg†#í R}‡ëû<}˜½k;ÙÍÌ6b êCSK#®Í=[Ô¢‰.}:Ô‹2>€®õëV2¡p4 „À,ðñYÇú …ZL)‚u ”Lp/ÌSßw“®"õ™ k†GØ1& ‚:Ôb!Wîšîz¤U…6`aÊŠ<³BC5Á€8Äà (& mEë@„ª"Ôì `F%4 qXÁ²†Ñ‰-bˆ€¦Ÿß­Gí*¥AVpgÚï¡þÔé}Ø qH·ˆ.æÔj9ô¿®ýv?Gÿ.ÙüÖ7õèŸÜKAym¯5ëXÑ«±˜lÁSžGoá¾öm­cY‹cµÙ÷á •èÔÄ{ñk“|¾ˆ¥|»ýfÝöÖwLe×_¦¿u«¿Á­ß¼ròÚ{šú3ÑO×êÓðÏûœ ®3çSÅ{‚¢‚ZÄ"–¸ó*Æá=uÓb‹¡É eÉ#C¶óÈLjYs²[°;`”1pä”à ¯¼ˆ #ºdhÉ+L… 8Gæ°±äÊЀÃX¬ë2Ù2€™ ­E§uƒ‹j}2‡3ˆ’Y2‡s]v|:&·”ŽÜa[æ· —“[Š–k^&µ›‘ta…g؃ÇW2.Z.v-‹zD±¦»imž{·À^™Ô .Qq¶ýâ9› K›¬?d`™²R!Vj S<%»I|ŠþÜ7ÙAžü(õQò*$]ÒŸÑ„V^Ç~eªœ–϶˜¬×ƒðÈ÷½~ wÞdzÊþ×uÜæq‡ *”ïwF!!¸lb›èÃMlâAŒð/i>Lvîô“MnE(| v™„°$·ˆý»¼¨ì­lêá²ÿ•=3?^Fß[án•9ݦ—.^`맸ñSÜýcoê’ž`F§<Î{$˜ïL¯n°J†N‰&Il7IJ2$žÒº! íŒÉ·ÔàLÙÎØª8z•ƒ—¹9äBÙ¹L—%×oríãOÀà.§Q )‡äÆw™såé®ú­ñX‡F),sˠ䡜|Îíú4¡®Ñ¼ºMY´Rw~!™<œ ‰ç5êªK¥¨Ðc¤† ¶ðV£„lË! ¯O±ŸæøMWayó&¾é´®ŒÁdÝ„Á ÎvÃÎí“[Œ™w;K‘S˜SAÓÎ=ÀYž3”+”‡2Dq Πž¦!‹AQØ)É-× JŒrÒÑiaå½¾&VþµÅ¬-Qg»qˬThßgi­É'¼ù9 Aà]ŸÏÏ¿©î|é\'Þë‰K•¾K5Ž!¡>/ÚÙôÉKK¯Ô‡|q”ÁÉú¶í|—¿íT¹oa»³$­Ë _ÃáfTÙÄ&ÒØèM<¨Q"þ WÆ® %Œ;&&íŠxÿ{K£”ôÇ¿!<ù£¼xžê¯¿e-m~Š™gÛÞïöoÂÏw;ðØå‚rí{9ùÖ7°ûÅŒF§~o¬Œ»i¥àë’;׆œÝøÐÇÐD`”ÇCÔ=ÃfŽØˆÿªˆ [“‡¤¢šs÷ï»]Í'ÞF‰•ù„“ƒ#9õ”¼$Ë1B€€kA¤R(AÀ"е\ÌÐÀɤF"¢ƒ4’œ][tØÒyOàlÁ˜!ÜÂNaˆ !ò7¬…5 „J0B!!"õpBD] óª+ÝÁØÎÛÅB ëP!À»<2gP0©:¶Ën?¥’{.Ô¸ƒŽ©2rÔ0 PÏÈ;¼âVžæ.'(>@MnÈ„cWÔ@ÉîÓŠªŽü…„í“ì©üÈ)ÇÅŠÃ;PÂù$/¾¦ºÂtÒÓÜy—ó®ãdìü£oìémãÙÃ$}ûºzÚ¶´Ë„ ëÏ®yXV–üBšŸ[Þlë;¸ò â´SW^qéϲòžiE?7±‰Mô{Û&6ñ >ÛötüYXl„hR¬‹ë8«ƒßb`Àãÿù7¼•=úÙ5št«J9£m¬k-ý“ÒHíÀÓÿ%O(ù·ÜWóÜWñÌ?cû Þˆœ1´ËÊ–$ZÎK5gK^6«rxé·öEü) Rß!¹ºÁluGh׫g-ÓWÅEÒ‚1 l °69!‹ØNßm>G—c,âÈròÛ2‰mǬØ2\° -…cèØÝ‚؆mtÒϨÔa…iPÁ]¸×À"–¦ÁWAr$Ã)yEf09Æà”L)VÈ…\(„\ÀQ¶’-ˆœ^À›G43òS`ÚÓÄàrŒÁX\F)r2‹1ìäl•X‡3Xa¯äB´¶1° »ŠÑN§¯ŒZ{"ÊÀ™LQßawßà=(»™¡6Ì Ã-ì( KÖ ¥¡Is$Yš¾ôažö ‚¨ÊSƒf¼ã ï<ñƒ~ýb8ƒñúØwu>i¾ŠY÷Ð3ºÞ’rˆûZžUöŸíÊ.}Ÿßú\ç&™‘†ä“Ži3¢lbK±¡plâÁú&åc„#„ŽS)[PÀ#åix]Î1 Ò–ýÃ\­Þ>tóSŒo’ïŸNiM"»6ä>v»¥/.ìͰ÷å\¾ÅÁ9ø êèÙcœÿ=ß9¥¹3ÔV!õ’±óÒ'z¶âµ$êxk“Ð-!¡Šé¿Æ 0ä58ÏPØ/¸ÑÁ&µ"°Mç±gl·§áG“®Ä-hWSØáéV§BÀàµsÆcm§ W ÀaKKHN<´RmÑWMÓš~‡x‹ëhš˜‘g\,=Ï|Œä°ƒi…­NýCJÔP·f‡‚qH…*uM€0Á¤/ŠhfH\†¡½--² …p9“ MEQÛY XÃLqØ2cÇRfT5"h`«aWÈ V¡aÇ`Y+ô!Xð y†5Ô5 Áás¬Pµ®Úó¤B´­V˜&ŸØþS—šf ‹£Ýàj8×þ匩i–̉LôÇþàèÂÅ7òðSÝô³—eXñG\tUÓço,~¶+=EÏPˆ—>€Þþù6}Y:¿Ò¼S“í\s–ZëáønÆ“Mlb)6zn„)ØN‡ŒSD´gÑqÎôãpš OZ>ñr5Pÿ½· µÇ?ÇÞ7ôdb—œ‡{§¶R<´ŠVúz¾Õ¬ðÂ^øò§pé/÷ºx=Ø„oêW®Ï½Çe> ë> QÉNWPˆ¬ø±¥Ò!ê—)x¦ ¹O¥„m8  ³XÁÖ¥™Gù0LvZê,œÜÞ£: pè ѨvƒxŒâ CG=i¨•*Ç\ÝÕDž^êÀáMÎ?EÐ.­Þ¦®­â6ëHš†»wxä1 ÁY‚' a7Ç+ûÛ”Š›“ϱJ¨°[°g âÉ ÖBÀZ¬ÁœAÓ³9™£qX‡Á9&n¶,äq… >e”l£¯üèÕ³šNõä¶r!bèUosÓ—\Lå–[õåóOqùrýWî«ÿ>ûÃlE%9{E+埵ҕvð°²ÃUŠ—I~G¹÷K¿À¥/êé’œujLsºø#=åéEýÕoÆ“Mlb)6znø#´A‘‡Û‡nUâo÷š@É6IöøóêsL¿ãmíÑwÑ|CO형Zø´ a¥åKKÕ«èÙ$Zìj)‘lV’[¶ß=sÁãÞŒméÿ’º7Yñ› ‚iAa}Š j¨”B)S³—1ÃD)EÄÐ{A:ð]ä.ÚJChëŒ  ¦û¡uº²¼QVÅJGù(•s9O\ÆIà5ƒQh°í6¾•]›Çlº‹—Ú€%d¨aÔàͭËCs0ˆï NK¿6††©€ÅDç‘`15Ýäê¢Z± Ç­×xʌ̜âÉ–Q-BØÖ,Ðqë€G¥plåèü´`W¹<ÄŽÁ‘Ìœ$`VipBæ¢h]8å´ådÎCê LkÂoãos}Ñ¡0‹>2Ñç”Ñá"©959ñ aé\”æX2UI'i-y&‹‡k™$ÛCÞõïÝ€¾øðs:ÃÔþlS­Õº Y×Bé¹H.¯G…Š ØâTj³†[àÑ—¹øÈécµçj2)µýÞ½ú&¹ó¾Í`²‰M¬Æ@oâÁê%t Í z.3–¯'Z|«¿öÿ-xâ/ñÂMªïû„Z«d|›üB(¢+¨TÏÈøÊ}ˆÇ-iä­•¡ª8^V¤ë„{ÉW§#tú•Ф]k’Ö'UM¦Ÿ¤¯‘úyèh(é&:Ï<Î;j%Àq@Z¦Ã4(Hèh'HWhª`$옷¡ê¦€Õžq¡t]vœø­ÐCçe@7öJ­ñÍdßey»c™W¹d©c•a0 Ò:Œj@Zœ#40jõì<¾!xD(Õ¼+Ô #„¸mÁqÔŸn•àh‘eäo´$çîŒ,(áHG¦¸ ÉšÃÛ„£ÓäIIhÆhÀ£9[®¹é~ùÑ­/½[žÛ”³ 5‘–:ëYhÒy2g™+ö€JÙžb <§–@/ß`6&ßÃG¾öb*‹1„…Òa”ù”=Šˆ°' >` ˱9aÚ•µ¶,!N‡º{¥Ôó®y6ãE .ãpʯ]¥ù71§íâ;‡—g›<9våÙ^J¦¶˜Ù Hmà6ìŬ6ýb†4aÜŠ@»äÐW>§¼G\þ¿Ø…Yâ4²Ði挔óY]o±±ïëoœåKÚ©´|˜»Œ‡~û=ëŠ#¯·@”î¶îðú·oF’Mlbmlô&Ü!Ìi-˜§à˯ÇÐm¬ÖèкÖQÿ7ßàÿÀC_ßeÑRvãý‡&UƒúF¾«} ¹Õ¬•ö³¿kųSï赘`i ~xÞt‚Ê]û-F°¡ãt\X“ì3•q 3ìYFc) ÆP¶ÖRC¨9\¤£ÿŠà¢tÆî6“,Ú½Ñ%¡»k LOØ$‹š®$5(ÒÚõ)¹Pµùie&T2\À*yÁÜsí Áý±EûÃÑ€AN–Ý–ëèÁÜ]çŸ)d:Ê54Ò©/ŽÌ¢ŠQrØB*³š#‹™ÂfŽQÍ~Ã5{zG&GÝáÜp­¡w¼w^q–\™5]ÓnßâÊÃXKî`¨,Ö9lŽ©)-Ît’sjÉÚËbžñ!Âåô-·ŠãÏ:.I×^\×’ùnF’Mlbmlô&èȾ÷äšÄ•š~éP÷ÏèXÒ¸Âã?ÂKBý#o²µúJ·ü¯÷l‰žýùYäæµ!gÊgm¿ôóY9­Õƒú5í_dO­@Œ#XDÈ•(²$’°­•~6Q:q 4P×—S (™ÁBmËöY¬ A¡Ø‚#ÄEØðµÎ0›S·í4XÅIôFh¥£„LvAžÌÀQxŠ@i8h`uÌ:à )À”´ÀØërÝòèu¹áµÛss5²à°¹m•­Ût¯bÚ& ~J #«AGeÇö4ppƒ Ïtõˆmª¸•vTh¬!Ï:©¾ñ¸+t`.0pÔ:`OPC1ÀITÊs0arĵ«ìlSDíðºÆ+"L*~÷þ Á©l'ø8u±nU8,ìD²ø‚qaã<Ê'§–%K%KOf Gp.yD=w¡|lúƒ'¾ˆ÷Ÿõzù<óã]ú9ôé—:”Ü“þ"mCﯛ/š½ÀÜaÿ}<üžÎ1=¢$]†$•@1Gúsæ®ýî¼w3†lbgÅ@oâŽú¤b]\‘ìÆŒì>曤kÇK­xü‡¹:xó|èj†”§yµûqN‘3ššŽ‘kSé¬èZ¤'h"“d®á(œž%]'‰Ñ‰¬§M·Ü '4F- ÃàI`ÍÚ»“"øDn¢M9׬ÁVØ o:¤^ÀVNî;ìÞòulÝ4ëØ·¡Áƒ¶út-¹…+q‹¢Jf;$*ÚÉ0·†)­%HæPÅCc(+æÇ¼4@ÐÅwpÌ}ª%·d‚dwe6õÛ×ìzΫ˜O­BQ1÷™*¦hÚ¢F0ҋϘÏQ‡ ˜Ã.©ï•s5%Ìšx­ 7>ÎsŸ‹¶RÖíD%^=¡£©äA83>fo—Æ¡Jã)`á•,£±HÓa81(T3n½Æ+Wyè!Îïww¼ 4 5¼pÌø8:G¶I÷½èŽ`\,ÔÁëè/³·²0"gähódUIúÞ è¤š}ßàpIÐ# ä³`nö޵÷=\þSìæ=Oê‰V¤ ×vXy–ŸÀ IDATÓÿ5õ*×3|=Óþ•ZV0ÿO}E÷VK«$Ïra<ëM²Ðå¸þÜù›d›¸GlŒT6ñ@ÇÑ?é4Îì’Å@jÀuF¬õXYûIJ]؂Ǿ÷Í7xò+]¿4}sÎpdU¤ ÜkEšWç¡Â[<×âÈ,"¼´I²‚Kÿ¯&q[ìÐ$¶ñ „P¥Pf_`®ØX×a½ç‰‘G%„ËV-£ºÁÖÝ9f†‘PÊ ×BjƒÄ¡( \†:¼Á HÙµ\,X4Âå¶=*j‡J—„& `,Y+Óf±ŽÒ1°%G55ñÂ..—ëH#êÄ`쉹cõ¶uÏ 46oÈ*dêtn‰’…ÜÞ,às(1 î¨ã›œçâáy0üÊ8¼†mU8â•觯ˆ!ÏÉ“c†Cé(2Œ ¥#/:å0c~ÂÁu^ü üûvF\y´ãˆ¥öånÃÍ7Æ0`‚K0Hì¸Mbû’ò"<Ը̾y|TVËõ&R‹ÔõŽ£â‡IôãÚÌôΧÞÓíêá_åYåÝÊ³ßÆ^އIÐa§ÞcÞkVŠw5š˜œõò‘3^8îþ3v¾â”úúöœ«Ž-Iì¦+‹Ofpó:¿û ô¼‰M¼nl2Лx cöýüqö¿ $îÇHOV<´×†®£@,t9®ÜáÚù7Óàùo£_ØíŸ•VÖNW×ûŸ§à;D&îZ nY1^k3n“ÝÑóâXuõ8Kaº„q‹áfq¦dLZ\¡¨Æ¼Þ½…ºÃ©¿¤Ì•!XO.ˆc`© wé [;… 9YNv—*€ÃØN‚#˜)¶ ‡`°œLC›êvXÁFÃË´¦‘Žz ÕŒW5¦H%þâ’P0*ØÊvMe?>¬Z^‹Ïåx¦×ÇÙsçŒâ3ÕznÁ¸Â‚ eQ ÑÀÁŒæ:2e.pª•=ÏËÉïc>þœ{•Nl!4]¡¤((Ú€%3\¿ÅÓ’A& •Ç)NÉӜ٠®„×^¡žS´æÒ%žx&ª×Aå;‹ÄF¹3¦:‚\‚Kqfk¢‚ÊItlý´«Dgí<6."‘,‰,ÅR‰á‰¿u–ÙŠ°s¼&ÒEö"cÁ-n Oü¨²—S&e>*(/2Á²ÂÒ^û1§ÏêrŠºY¡•éÊëH Üý³òÄ^ÞfuÍGסötåj·®rðøfÜØÄ&î'6zzÜü³lý¹ô)Âc¨:<({§¯­ßC©•ÅÖŵýןzíõ7×Ô!Ý㸬 ç7¤iM‚žS?äz°gX™,Åê’ú*´mz8@`U8rÓ™i‹’ [p Þ 8†·‘ÄnÁYŒ£v9Hæ(-2Gž1(±J ã†!vÐ` X|ƒxL›Óuhm=\;¯ðà©<8ƒ ˆ  ¨⺜®8Äà•œà ¹P ·Æ r€²“¡0’måÝÒÝš•z¹’ó7M°Ê\oØwçF缨z÷F²I0­¡ƒaF€IM]1˜Ð àˆ¹Á{Æ­ƒŒe²€S~ýWyî󨹨y»ø@¨ñ5H7»–-Ü=áö1ûÛŽÒS+™aPðÒ5nþYÆþÃÜyj¹m{Š¢DA»L¶¡S¼1†ìÃe:vª?¼æ0˜x ÇP$Z“e‡%JU±bà—j0&rušì°Í|Oa»š·IèDzîmÑÍíú¹Þ”MaW2Ð÷X/ g¸u6}ôÕmÐ| ¯>Ï“z*ó·8ë*I¥svÏ] ù)¼ö¿3ùò͈±‰MÜglô&ô¿Ìá?àâ?ýD@f„)Ì…ò“{J¿-ñŒ×*C-¾¾ÿ$|˜ëÏ¿±ÖŽ¿æ/½~¿”7øùÚ –taÝŠäÖj¾ªNþšÂk^Ïr¼Ir%ƒRXJC Æ`,A2ØÆÁ BÛ–c‹Ú.Wj-Ör<†c”SyêÀT( »¶c#baŸ¡1le¨’I»ÖƒR7Tsê);24JðÑ$pH  Tžà:_½Ì¢ j©c0¬g”¡`…ÌPYn4#HòIF3BŽ:DŒsZÚ鉷¾¨Ùº=Èkhê—ŸÞÒ•JC%^Žëíë¾Ó8‹‡iCVQ:ÅÍ¡¡ª™+ÆÇ*>¼pÞ´\¿Å«¿ÃîÅŽÕíãÀã=Þ`[~‹K€ë7yd›2’F¸ý ×~“WØ=ßÍOØãDyôq[GS|GjŸÕ¼6¡j`.EE9‰xq1ôªˆžé ÝÌa¿+R<åAmšput“­µÓà —ñ™\¸[·w¤†àtØmÎ{«š}rP?I»Òë–ÿ¦¬Ö5r‘xvëvrª1÷ßa¿š}(“öÐ/0gì!Åñ'ðÚÏ0ÿªÍp±‰MÜl8Лø$ˆÛïe~ÔW¿2ˆb.`¶zÔ^â6o"d…•{ñ9.ÿîÛÉÖ·*v-©È­¥bë=Û#+R«$Ë6l_ü.jÉî*M†É:£Äµ)ó4·'Ýq¶„‘Á92Á@f ..\ jÈ,[¶“´S!3l Î"u¨A„Ì2‚LAi#ä–F<`B´ðXÜ °eÙÎØrQFC°‚8¼Rµìä³Û0@Fä#²’C†f„ïhÞR;æÞ ¦cH«Ã &Ã:ŒÅYJG–Qfd–[SnÙDq¯ý¹èÊ%ëìÁ1óÒ…pk»ºmA}ठuž7öú‘©Nò05vBvDV ‹é¥¢v‘b^¦ŽÑ”¬9ձΚ3É5”$¬ëúUfã.9ë¥Kœ·ê{Mûu:šø­#jEÉæ»/òÒo`„ÁÖ‚Gk¬ãég9wá4±Úͳ”ÊswÊÕ.Áñåp¶¿ëuL3Wž«¶OÝHÌCSÌZº³$"n‹½-8ÊS˜Ç%‘ á&p Gp^IªúB¬ h"æÖ¹ÅѬâ{Ï'5Qáõ|¿—¾î ‡®~ÇßÑ1„ìŠÃ¢ís —ö¿ø¤¸ú§6èy›x£±É@oâ“#îþ}®üç $UÜ>Zž¦²Â=ÜîKâÐôå±.=¼Äµû¦žÿ“ËYkù›ú­Z MÈš©]Ë’¶ éTÍ€•VÖÊ×¶¤õÎ ¬åœp¾ÍSZªÈrFº¤rÞúªD¾Ì0Êis5‚¸®%J4糄¦3=Ñ–T½@«1ìæ ,s¡˜(A­ yAáçH…S6dž¹¡Rhð j óÎI¤=te)èÏÎEàh "8ÅAn˜Îùø¤;—¢éœ9ææ©`;-ìx§˜ÈIAExe\‰äd‡ÇáÅ—ÍÓWŽR†¡™X[ÞÕ¢¶K­ ˜¬G=°„ÀJáᆲÓ:¹~”Ïz‰ÇßÅ4¦ÆC€š@c &c®Ýä¹}*¸ú!n^eT"†£#æ žÐðØ3”Ž0ë¼oÄÄ Beî¹]Ãe܈Ý£€X^›Ð,àf c8ã>™Ø%öÊÎ}¯ç–G_ø¢}€gñ»«“½ ŠøÝ°)Ú5–)ÁÞ%lÁ=yœôpKZ7~]!Á¢r1ô¿¸è˜¦ßVúÚbª/ÿ§Ì ùGÁ`e¾í’:BVtÜÛMáίsûÓ7ãÃ&6ñ&b 7ñÉGßÃùo¢X§ï24‹2 Å0üº¡ëhÇÚ¯®KI™—c[yñÏQýµ×ßyöÐiuѽº÷Ÿ5+éd³b‰lW²ò$kÇ>9¢&ÔÏ>ª(N™X|è¸y sl Ú¿Zp]…™Æ…†NB BÎ\ºüèrËj¥¯¡vZÐ&X“à“€6Û6J>"·¸–"d9™ÐxBMf±˜×0§S0>æä„ÚbG v»Du1BA¨±ŠC°×fÍé°{kÓ&¯ …°#ƒ(Î0&¦›$(ìö\})÷¥Ô'¯îh#Á7*âkå¤S ·ï4W*q¹Æ5NƒQÅìhqh÷BIq›Á!sƒk0–à™€ džŠ}ËMwú”~ôßðȳLÛ‡uhƒŒZ‚t Ù¯Ýæ‘}~ç·9¼Í°ÄxF*K ?ÏÓïäåß"DƒÆÖÚ°ÌƆƒ‡ØÊÙ\ôd‚WîÀ¼í ³ããgf 7úlÐ$Ö¬PðmÜÒÅÍÒDl ÜIÈ>ÂÍ9¿ ÁCG­½ÛëÈ]æž¾BºN¡ª¹h0'ƒ(ZûëÀÔQ ê0ù´0a˜kS¹ƒ«ÖÕê~¹j0Ƙ2“WÆatÕØÇANÊÐ05ÈHgdÜšqáU좿 zŒ"•PÃs†W,(yÿoñžÏcð$'£ä®óVÄa´CÏÆqõ5²PÊœÐP€·l90œ»È»?ÜqݤSJi]rPæÿ(MÁ^Í•#J¡RŽ*Ž[ËÀ îÂItË6+-M¾Û}Ñå”J±¤´c¢+¡Kô¾»¥Y7+b› êëp%JÓœ[WÒ·j·¹ÔÅôŒ÷CèƒìÅ›'¬sñ ö}ùïpøË}ùµoâ©Ü#j·{he¶ë$©ßN&p÷C¼ö)›aa›øDb 7ñI·¿‰/!ör¥:ŸŒ6ìJuüëÆ§Pú*o‹¡k~ Åu®]>sWÃo?5x¡o9æNŪ9öÚ÷´"g]„¬³pÓ~–³i$±Y`4!¼`¶kòŠ‹J±ÍÀàb»Å*A°±^¢Ëû’&GmûsÈJœ!k( ŒvëµPD|¯‚Ñ®¦ÍÇܤiIQ<ÜnË–%Œ"ÑNsm<åö”´Âá!êû dËÉ fŒî24Ýö…Ã唆ÅxÔcl÷Wg™Ì¸;ËnN]wšnXDÈ…¶ SËØ¡â¼œ#ǯ>=»}ÛdÞÃq-ƒ\·ÚbDC90™-ôhÌ¥ ö‡6LJc³]¼ôâäñá„pm¹Å‡ÐrÁ[¹âY§cñHÍ(ðÑœÊÐLùõÿ—ϾÂpÈ,P+.C,¦!´þä1LOxíEÜÏ>BfPÅ—.óÎOãÒæGä•A´ÐLÎðqªì˜ËcŠÇ 7«H`U g‰¶˜ÇþÖô øŸáê«<ù0ÛÐÄÊ¥N­1…ø ¯þû„_Ú ›ØÄ'½‰Ož¸Åñ/páËOó:ÄtÎ k5§ÖÆZ‰è%i޵UAö/Ák\{hýžG_Ö5Ì$TN—X™,y"¬ÆëJ\ß{³@¯z²Üãd(¦L=u«d TàrŽ wÏsÑp¥bäÕ”Jo:¦© æìÞL•P+61ǰÂå’Æ2©»÷W&3‰­øŠ1¨t¼ŽV—-Ô/àFÅå‚óÆS:¼ÁXꆹgÒ¯m$¿úHÈ;oB¦Ìîâöh e`·`è  N°­´!w8h*NŽœÃf¨E¼ž’ËG›10•±fê™çjo~x[f'dVð!˜»ó …”NZº¶:äf®å‘¼ã£nöPÙh348Š;;óÿTS[[ €*¼À5yö…sðŽÀ謹òÜe>õ È׌SÙ ‚‚(R"qƒŒ®Jσ 9yFP2e$]}› ¹tn|í– «6kPEkD:‡í,pÁaÁà,N-ëZ;ÿíqŽƒ¬€œÚÓdÝ-Åy.d¨åÝ#®õœkޱBÀ´Éo(3BÃ#Ž&¨b1hDTÝÝsˆB@ jÛUaš‡²ñV–ݽþÄäÃâ¤ÑŒêœ—YÔV’ÙŽß™5ÅK2/Çþ\m·.ÉC-åÉxRëI Á= ˜Š7 œ´¾ÜÂņ™ãÕ ~ù_Ó(Ï|.¥£ª©”Pϸùqª9"¥©¹u›ým2C€­_ò#~;ãÂõòùêÜìša®]æ ÁUXGv‹—_a6gì#Çà:¼MR~êú…ƒ«Ú-Ýb' Ð}6 ÷#ô5.B7¯*ؘ•éî’f…a~€>CÁ v"¯:Õ¥ ëúËjíàCû¨0­+‚¡Ÿ\'iÌñ¯Þ÷‹èCÌ>tª¥½‰Mlâí‰ €ÞÄ'YÜúoüéüMúÕ„±ÃÍUgúê«È…™ÕTñòu ¼/uÒËäãOÛF×xLu©§÷sÌku„}Qö&9}ô?moÞÛØÆ»*¶zOXÄ_áîų?ržZSŠ]+˳KX\nÀEäà´1MÜx8ï~˜ôaª¿Šv&Ïæ˜×êŠ6sÌ7Ù^óâN«†/\´Ý>;3­z ¯My÷Ö¬‰Ö|®Jihr½óÐàƒo®_彇¸@ ª˜ÇÉCë‡'³`CÆœâ„:vSŽi£­ 8#)’æ‘ÆÐ#Eâ´äJ7ÔÚŒuŠDC=·lj7k(15AÂÀ1ò\õ sžÞ#$rCîâ_»Êä˜zU …a ¸V€B ³ŽÍ.dÂ"BcÞCÕ4ôUæK¸Ûì#û™eI`2Û9<½~}ï¡S NÊš,'/S¢¸Ü… ’f÷-¯Ý,d*wOö‚ÄB³ç¸%ÑD}ÚÖʪµðr"ìå „kÆCik%ÿà÷¸wÈè½d»ÄûÔ%Ì‘„TœÌ8œ²3åãWyô_ýu¾ü³§ûüì댘<€4æO|’—¿‹|ã(=ÃŒÙ_ùnö;±ç>#hÙDu£­1†—ú0Ëb€ÓNÕ®ß×âF>˜ËŸ—9Ï÷9ïtXß o:l§]Ýï>îrS!zûÏjYäZO\ÆéOûLaþßmoÞÛØÆ»*¶zO^Ì~”ãOrõû/È~­Ù 4q¡ð3«Ÿ°ϵXKîæg„­&Å7!ŦAÃe+— ârÿó´PÖк‰æµÑÅZúyUMo‹¬Ýl&©µÙk£ÁâE[–|~„yaÒVþ™`¡-bsŽÌ\»Â$ÔÒhm¬#fÔBæPaV7f~xZ;b«Ðœ¤U&˜T\U\S€»jRa/r(Ä„T1(¹áð …X1)9Zð¹ÏðÎoQNyô«ÔßÉü/3Ž« b,À5*r 'çÕxÁÐÄ"a†tç_eQK“S²Yˆ_²A¬ª£Û›è ìh²_d‹ñèÈ«šÙtE\æÍ©ÄI 0çÌ;©ëº_ÖàEûO³@ ÔØœXµù{3RÅ‚gWŒ‡Ë¶çxx—‡w¡D2lK;£3f‡ü·yPqõ„›üHÍÍ÷q¯þ×oR ¿ûy~õ—ïðW~ŒoùÓè„Ïý~ïgàZ§’6zJ¿E-w#nøc/M­µ×€S' Á%¥½k}g­mo¦¥7FÎYE¡œø® /kè/0[í¶:M¯ùb¦Õެ½^ïVu回~q{ÛÞÆ6Þm±ÐÛx"ãáßdôi w±¶~î„v¥Ø¾±r(V·k‚V›8~O6s‘Dôc¦·'¬îOÿ„hON‹ÐCëØÉ !ÙF©V8GÏäüÞ>Ìxï1N ê8G-@» ÿT;v«¢`5–£PÄ3ÐUì™`©Ã$BŠÚººZˆi‹øD0’ãVƒøç˜ÜÈÑ”EÄ´ÅîuÆä×Ⱦ†Ë¼yƒñÿ£ÿ³†‰£N„λQë³¹‚S2¡Œ‰Ï»™¹ ©‚éH¤zk\¾!iHð';ª‘„ÉÃã§Te7?Ìd67?FbHåLAj˜!'±D2¸Š*”2ŧöºhÓê…3Á {‘èYŠœÁ ;ƒGm«vÂõ 7¡Nj†°û N‰‘S—}_yýÀ´?Ùšã ¿ñ¿ñ¡’Îø™Ÿ†+½VÔçûÒæ¦=øžd$].é»RÎÆ‰pMɑՉ£MsïÍñáã¥*»ÆŸf<¥TžÙt^’ zV,ô¶x! lm—ú'a 7Óí•öºXsŒ‡?µ½gocï¶pÛS°'1ìUÒMÆßÕ>ÃÖd(þHeåÍ·Y@.2k ÛER/17¹pk[é{êE¢ZkÇrá3ÂŒ–žb@ìR†ý­T½Ì´®<´;Ò&×€†1ì‚rwH>d?Š–³¹ëpN§—5b âj<> ÑV@M”˜ðÝkÓVEä$#xÆ9#%Ò!i’#וR$4c¡F)¡Š*™gïM²‡zx޾I3y–ES³˜0#e¢2ªDeÌ1µf+ ÃT}‰‹¸¤Zî:½Üô×o.¾®ºãpê$`™Þ¸¯¾],j}\Š˜‚"š1$ˆEo“*¾]Öµ]‹ø*í²K 5Õ«Ñ9&˜)³€ œ©²îšN{%n€ ‘bNmÔ`ŽÑÅW¢¢¨¹_ÞúÏêQWþ' äøëDøÒïðê«uÅ‚ÚküÒâ%,v=étVöUØ…ä´6è©×¢“{¶Ë»óùèkê>§Üz™á.&Ýù9í6/ê_ºÁÇà’žÅª¶^Dáh°õ îþùí={Ûx·Å6½'5NþcÆ?Èþ‡Ú·}*ðeäHzËl²8ìòlþdó‰¾ÉÙ´T`õÛo¤äñ2éY[€¾È±¶žKê;ù IDATØ;Kºa÷½)éÕ?¥ÚS·­{¢ Ãó[Ñë{<;&?Ds˜£6"ˆ2r+¤kíú0t¬TÁ),µ;’¹k¥ ãß*f¨`{²@4²‚Âs´`¤¨b ‘zÁiŃ„l„¤@ÚcðóÜÈxøÝTCpÔ‘EÉPñ®•à°¦ºÑÀå?G*LDEÆ^lš»Ïù¨+|îÚ”rº&)Š›G&ÎìÙ›Óà§fÔI\îÔ°dV‰9*v£†˜’!¢P gHõšD¬»Ëá©<Ç0N,”èàªn¾Do )ÊF!?—¼]¾XË™¹U½5^ÇÚ><þÛ͸0f×íBœ±i±iî.¡n–0ú:4ðŒÑìYÜ[ø‡8¡ð9ÒTÚAÖ䘛Զ´N„ ÊJ‚3*k« ƒ£2Ì:då0H‰d¨²“ãm •j BÀ‘ªä5¾$9ò ɰï©Ái€ÏðºB¹‘]Æÿ'¾âÑ¿A *1¯ÉÁ¹–ÈÑ@¨F;¬ÑÕC€‘“à­~mÌWÐ[¤S,æ|#‡m†Žï×'¥¹AhTÿ¯^©Éx'Ý îã*ÆãEâhqt{r`€â ’µÅ”>‘Œ(ˆÇ fp½æÞ[­ ½؉HEª±Nîz0Å?ú¿aGÝ3ñêžj‡Z•Ø5'QÄØ…ã€jµèªz†ë>v­«I„§î‡ Z‡Ní1Ê®!Ñ¡ê´*±{< ¹¨œ@.©^ë#Ž“·pûø†Ã=„8í@|Ë*úg•³‘:òRÙóïëÖÙª_—f_ØÞª·±walô6žà¨ÿŽŽƒk%fT~÷²Ïeã9zY¢úBzñ&éySÛË6~¸¶äeoÝE«ºp‚[./\ÃîrQš<ëÐŒlöG5ŒÀÃä-¯T””±xž$¸m¢€jË„6ë>idÚºäf%ÄÔò1ꂨ ‚jéxØF®ÄHL :²GL8׉»œ(+ŠÐn%9Ìzrx©3ü¼;7Òù¡2fü Ç£ï#8JeXcÝ¢À¹*3b’ £47Ï\Y8«O³ôo—d¸^ÄÈ1|w$ZЏ³Ù`˜ÇáÀ‰&>y‰ˆ7ÄL% ý'i¦w,â¹þŒºÝaÑjB xÄÚÐ3öæ, ÖÖ×ol\1¢ÃjR¦Hbp„c1Þüd´ƒ$­YzÛ6 Wáñi½HnÂ:1–¢£g4g¾“Š“F׿Âõέ:õ&7«êÅjsM”&ú…½{µwììÝ:zóxß͉±ëJY··åêÈpm ¨½Ï—䥳 ßr:Äß_Ïòóÿ·½UocïÂØèm<Ùñð‡Ù9bOVrÀòͬÁVçšûêxö øžlr1e£H¹¡þ‹Ç冷Bd."kö•°×@£kà>m²áTì6dFXÕ¼kDÁÓÁEy*Á yªçiJ4œà;kÉÊ5¨¶l\óyj±u2L1mɵ’ŒÙœ'ï$¥½"‚HNR6ª¾=U² _¡Žº"«©\ëQҤ×gI”0&ÿ9ä÷^ÂjjZ@¯u¤Î„ hj¤î$‰v{l¿î)‹&A?…ùu²‰€É‰h]¥x6¢šex'‚X2‰†53K¨UfbÔ(jV·Z=>P7çБ%ªšbh Š”S\b§¹V‚ƒ!‰ªÂR§Ž¢ Îp¯ H»v¾×–ó5?iê"…V‚Z…Wç«CÄeóktG“ÇwÒt™æ²ûa Œavá $¸ÛãrÐq!b—´N«]f³wlÎôÅã–ŸgÌðb¶8®²Y˜Mº:BéÊZmÕz°ßS|¯ÖÖ÷üb†0¹¨Þ‘‹˜]s˜ÿÄö>½m¼ c  ·ñ„Ç wþƒ¿×’5Ó‚ä±)gºG©[Í/“¾õªëuÿY«—Ëe,+ó6êý½zÌ´¬Bd¹$ ן€¾Ã½f4”½ƒÝ,mÔÕƒå"Tº{ëPTÏÂaˆGNýéµ–¶Û¤ŸAèFº¸€ÔYUgÖ!ZC„BZ/ç΃’bM>Û“w¦ÓNQÅe¸…ƒ•“Q( æ‘ÊÁÍØÎb=v·´âÓÁÚõì†zÄá3Ä%u5!Ò-ºY{·§Cqg#÷é¡ÍćÐNò›Cª!iˆL%í:WY4¬ª-&†êÌ"âMÌj—¦–’–ú…nªøšºárT‡INmT \ÀJFÖÁ<#VXl¡sއX|£æzyãJò„Žhbr~ñ7Ä<öZE¿­ äÝWY‡5‹«8ëZ%cy> x2,™ ©êybkÏê/]Ntf5ì7:T³W@ͽS¤ä=ƒ°p{ò^Y¤ë(&ˆˆuÍ_ºÃ]Á4›Ÿöj-Ö3û=ñô•í=zÛxwÆ@oã‰òäásð‰V"}3è¹ ½„ò¡AÿÙWÌX[Ûfjy­œQ6¸Ô1‰Ðo`ç7‰û¾†‰µSó«‚—U=n2MµÅÊí= ÎÔ\qïE¨èÎ#‚‚SrÖåqéà2XÃx62AŒ2‘!cÑÑŽƒA“ZöG¦ˆ’".c8@=4dh¡PJÇ£HHä5Ѩ,2v‹Ò„š° 2ÜkÜøä/òè)469æÞØ,-OUA(‘ˆˆ¿³ë>‡:fsBQ¾¢Úeç+¦‘²Á0wyP'Ή£;V ŽRÓÔÅ”ŸðY“bGk}[$áBÕÑ+R7†i.º¾¾¨øu­.‡˶á;0{¼ŽcpaFèóì¾Ê*±ìU¦ö+טÐôZ»ëíëåÈ­½ ¸ÍW‘é„+#žr»èÔ¬Ä'à‘+`HD8T[Çr«¨û=½¡¦h§È¸èm4]$óW㿳½CocïÎØèmlîýòYnþ‰ö¹è»‰ãÍ‚z.‘ÑHŠË©°Ê¦XÒ"õ¼PéRá¼¾ÍïÚv/SªÞ,ì»LIzM:W.Ïd³¡ú7~««„v‘CëåÝ—hÆNà©!{…#(ãœð"ö ¶hÑEP\C$0ŒVh9¥6- ÖÂ’ÒXÔ¤D0<ÄHˆ Ôñ =¹‡D¬ŒÈ8ÁŠfdÊM%VÖ8‡à«–×!ž¹‚ôTp ©°!ú×>É£§ÚbǦ²Uoh~žÕ YíÎ*÷éìüD5L X#5‡œ½,»_P_F³ELÎ'ïœW×$~“‘ ‹š»°ë똬N˜™ƒä!â ëˆàMþ~.X†yÊSèΡwH†/¡AÏu{­ÛÆ¢ŒÎ'_²ü‡â*(ä=4ܬ¹Þœ»ÆKDÕq‰AÇßôHK ÓÎz?™ÃÝ΀ð Ïï³{‹É}†p&œeÓÀš„t3>í9Ò›–É![õ8”ÔÝBFš0øWÆ6kk {Ñ©þ R ±m!K“vñ0Ç|?†·à¸'_ÝïËÒíªôyþcÛÛó6¶ñîŒ-€ÞÆ6¸ûýÈ?åàûÏË›ÜÆsŽLL=Û /#ö°²­’Œí"_‰µmÙj×6Pïea¬³ºµÇÅ\‚šº›· ¿&"Ö_UZ-áZú/îƒ&½È‡ε¸S×#8*&ñIHÉ¢B²öЇ#ãÎ¥Szvo\ œ%ÒÖ::FuA´wÇÝ0¤y.pw8øú§8ºI H—¹oêð–(ùÚmõw½ûçØxõò%¤ÆÚ‹e²¸i³ùï‹#beŠNUL;…<52u•äâ¢O!QÅ Ù[I3KpºÏNËvð‰Â33æg¤•–³¡Må¥#ͱk¨Þ5VµÍcxŒPÌn¼X»¶trYÚ× Ôίù²£ŒŒÓ5‘DéÚ0ê`4=qä.G[õÚêfpïpw—rH(Ø/¸‘¨jîœrjÌšešað¸·Ì»‹ÔlúJÇ̪JLâ%µìŸùpãÀaS#;ìÖ™ Är€8¤Bh5RR{ìFç=>ÂvÁà¤WGWGÅý>xúõÿn~ù WwŸ/ŠQȇ®#åû.˜ŒGy𦘟”_ÿíGo¿ròöOß®¶OŒmlc  ·±.ÞùCcg5ïÛ¼éPÖGï…ÐöBÓàÍ×›¿ÕÞúº¶›Òr 9k øU§•å¼vSãµè0tÚÐèè+mÑWô³ûäQÝЇ–Õ¼{O÷×Ãû ØÉÉ;9ƒp¾©ágï´€8vÔç&é[jˆ ¾o’ÇȾ¯âšŒµ§VˆdÊ0à”¢`8nX´K@Šk þÆŽ[¯)YŒ©#ꩤëWªM86ã“ ÉÑw8ø‡Œ¿ƒ;§ h:ÏÐ[Fn}‰½Ï 3âN—ÒÖ¶ªR©9(Å%™?-Õ±èC”ErMšY÷5C2!à’é\-OIS²,éŒð€4¦~QÀeP``‡¤ñ(xÁ- ¶¦‰f$‡Ñye&Œçäo}i¶÷o—CÄÚJÊF·.ÚþP{E!«yù˜SÇo(7›è¢×B–Ÿg0ëá…ÎÁ$u/J˜qöˆ3Áç ¸ºÃS|½I-×ÄŠ4¥ž1»Çñ[¸†o¸œ+¥¨‘ oœÁBÉ=^™Ôœ pŒºãµÀ‹ 7àæˆÛwëC"2ŽF~„Ì)Z·åŒQ£þøüÓÌ ËaŽa¾z{‰«7Š)¼óüt«{껲áøÂ¿þž§>4Æëî<æ&XžÔ!µùJRª2©]CXì=Ëw~äzU^)'îw?ýίýø›Û'Æ6žðØèml£oþGü-vGç™Tëeš›XÃÓ— Þmª&¯ý|¹Ø¦É ÛV7Êåâ\ýo}׿ûö‡ºz ~Õ7xé ˜V…DúÖt¾Wé%=LßÙ²aŸ!szI?]@໼wBqž¨-ÇÁ5)g¡ØåêMªˆk×´Ìw6ÿ©¤]qL¤ÔR~µÂÍ¢¢bCæØ³;„áUhR§Ë9÷تD7§e0$A“êòêy“ŽmÄ(ðm£e`Œÿ)ÏDÞú8¥ž'ì%qð {?¶×BÏæ·6(S±hÚ–ùèv¾’  ÉÒ(h̽/ WG³dỈT†š0Á´VÉSÄ¢>tUâ‹(&á Õ¤Šé„T‘A2ÁyÄp†&*! &QH×øw ³Ùâ)L;eëæª6ÚÛ´1­(J×’°“E>¦|}Èõƒyç˽VÒç»a[Ö›²¨:¸)°]ãÊ>çÎwÞ‚>ÇóïçÙòÔ{l%'Φy$#ž¹FtX3)á(󈜣Thj'"Ž…EÎÜ1Æ9û;pŠV„š`œ•mÏ2¨g¤’Œ¬Áߊ•Á}žôaF/‘JÊGpÒiŸO{ ÙkapøEÒoü«½½ü™Á{¿§Øÿà^qåÚ,Þ2öÞáN»‡G£ÚTm˜U Bðò!ÜÀ$'…¥p Ný}·'£ïx¶zOzlô6¶Ñ‹ò§xó§¸öë\ù^@þ6mùÖ^4!}ÈFjù2w@.ÙÊZ¸„7=ÅåµÙÔ[~îW3Çqƒø±üŠ.y¬·ð&k¥IúnáµÌ÷2µ–Á°ûù€™r §Æ0”ª-mnO·xçØÈe4Òνó£Œk\ Õa‚DâìÜu%-e¤ùÃ&E"kpù2 !'xÊÔ£9Ù‚)¸&˜P'ì!èy­¦ë¦ ÚS«-“¤~XÆè—yvFµK=!ø’ýr^N*`ÝXÅÍ2Ìk© LS%ê,óþ‘ú3Q!íæâGq(-Še FTDÅEM5I ¹Fú¾ÃŽ +™R—H3PY–ð5¦*e'Vm-z–'ŠwÞª¾#æççß5@WHP`§‹âW[¸9D'>4åÎWC¯m'˜:~|“`ÒEêùöµedu¤¤½þ ¯ž|—ƒïåà#ìì3¤@ ¡fï ãaË|!BAáÖP#ŠŠ!©aÃ{veäô.“È÷q³æ{wÞc9ÅJ¨Ú‚ÂT6¥JÓ»µî˜ü.“g_åì âIóÕ;ÆÚDÖ¼óÑ%·4?‹¼|³zÏ‹ùÍgÃéFÿð…Ó³[”'„gr•o»î®\ «(cÈìåA>u(qÇ#IÏŠ¿k1Ö³ “~apëµ÷ýû|ý§·Œm<ѱÐÛØÆF<ü>Nþ2?É^vÎñíÏ®>Þce‰­X¥/sIæxÓ#m3d£1õÖ©`b­lÑVÍ·—Y=ëYZ\èÙæV1·ÛÈ—÷óë±·6íRÎÖs‘hdIŠ®ÌkÀ~à™¬]ÓØ±›1Î2ÅCGq³ëœÜÅ5Zi‚8œ¶l í´8°Ø%ƒšFÿX Øäq#üPêŠäÈ1bÙºqô!Ç"d¡ 8 ² _0¬×uë@¤ušÐ »ÙéJ mÀàÿa8ï `çmú¿=y‚8*c!¤ªŠUJE•LD0—"I×|x$.E⬪vÔy'–*´IceJ"’°ÚÌLæê,ˆ^Å/°¤õ€DJÄ’Ù U‰(’COG€Aj²ª%ÄI‹cg‚«ž9}ª=ä†÷bÖ_¶š]“pœK 6™u9„È{Îо–õHóg½Z[z³y§÷RuŸ§Õ€t‰Q‘‡‰Û¿Ìí_gïiDI‚ d‚£ÞeÚ­¼w™¢L[ƈÎ`Ayƽ¯ròÕ‚ÉWÉ_æC QÞ3æ0R/Ppuãó2lŪÍÃõ *2º¡TÀ½Bõ-L^ÂåÔ†U«Å‚¬Î>%8þâÿ6æ>ñüîǾ{üÁQºææve^g·çz{^#Ê|Ÿj2b\T“·ã·Ó}=ü`v<]”£Y Âá¨.e¾cY!ã1rHÊÓôýâžvã¿sð=þë?½5߯[½m\Õßå­¿KùE®¤5û°K,»/4 \“îê/ÌElµ6ÃV7Çê.-“Äý¨å_׳ÎÖžLµõtÙ{[_Ä´nþ•¸˜l¨p°Á®¶^Þ:tºK9¼U†ddÆ>ì䊜0r8猄—xuAŠX„H‘ «Úª¤ó@óD’aC$C=NÁaÄFÙá`^!JpJ–ã³V»7BÚzˆ4àÈ=W‡L(_mG iuÒÐÔ·{Õ°;Ìè“ãXòj„ºžO¤<•Ù©«Ž$&I‹*"â´Øs6`P¤+><Â¥$uÅ)Ή¨†ï8$‚ˆ¥D„ 'Ãá§'u&I©þ‚²T2;¥R$‡²EÏÚ]_˜ë€a«1À18bv@ô­[Šk˜èJíd:–úÇí‘Þzˆ†À0ñì”·ó%—cÇ]µ_¿^¶ÑnPu¼ˆš¡z¿ÅÀ8~«ÇûÉì1x‰´À2BNž–ê9éù)w¿@yŸjÑ•º Œyõ6Óc‰§ n×xC›Ý+!a£žè»£.±3Š%¯©Æ¿AöEÍ;Rõ¥iÕð8ùc¤Ÿ·ü·4|ëÇõ…ëõ•ƒ‰>»¨LîÏÕÊäÀ”j«Ñ) °Ïì’†G¸wRõla^$¦¢,÷ÕM| ÇI;Œ§ØvÝô¹˜}Ûþ‡?ó¿ǫÿ`û¨ØÆ“[½m\÷_Fþ€j;J¼Ä?ŒUÅ&Øí¿]ræ¢D”\äódžŠ-«b t/´·˜¬Bê%_¹îér¸ž_Zã¨<ëÐ@ãGRwoS—/¬Vmºéôú‡{Z`ƒŽØºÔÆŒsBΕ1ûž gpÂ@Û¼¬ã}®ÜäÁ[ˆCX«ÑžS3ù.xÅ I±•ŽF°Ô–j‰Ãö 92Oæ(y…wä9YŽ(*ÔJÞ°AÀ„Âá '”æPÏ©Ûc”NµÍ^Ï5(ZK×Ù4…ƒ ŸA Îét.ÉH•“á£ªŠ“ zM0/^ÈëääZ±ãÂpb)ÊÔkðáÔiJË”2ïœKfN$‹ˆFíMq{^'i‚œÄÁõÙ!$‡ÔŠ^·\vÃu–©êÊIý9H ‘ª¤vãD‡W¤ÑÿX¶ÜÎØ\hÛ}åךQÍs3¾:ê!È»p}•Ž¿c]á¢sÏŽ«ÅvkU³êL/‰ fïðÕ àsBA,©æ¤wºñáÒÕÅ÷Äì |Äí/ðþ? ‘›îαæ46}a 9ìð%îRO3tŠdèÛØ‚¡ƒE£ÝÝõ—~µ®À ÞøO¿é;ÕGþ ßõ=áýß1Êu¤T‹µq2 UÕY¨O2}dÙ® ì }‡bÛ£:ÄÄcþv}Íï]÷¦jØà´²¬t,eµH;Am(uqøà“׆¯Àç·O‰m<±±ÐÛØÆcãÞ‡IŸãê·7 ­âĦä—£gzß®éiØåEý ­û±ñv“m='ä~Á_™>©c Íc‡˜û“æ}¸\_ä0×`×½XZ`4(Ü1†ƒ¹ù¡y>5p^Ì[ÜQ2œsª¦Ú†øÖîÆ œS—ma–yÔãŒT"†*d¤ˆ«©g˜a] †FLa uûÚ)NñAˆJpB¬!)!u <—·ª'‘Éa ÓóÜ­t ^¬Ë¢Z¯ ³gÅgƒP©Ìkä̬TilÛó%…Ùõ#ò*!൨oîÎnŽTÅ™¨Ó½k»1Ó©™¥(`&I´‘#AœTÔ“ Évœ¹Å4¹”®OT±%æJ“VPwŽ3ÿyj”§;•½ˆ8ÜœyŽ-Ýî=Ñ- l2îËZÛV“¤ñ¯ÄιÂÕ9÷²®5ÃC«\)íÑýëî­ïya®iÀÉêÀrÓg¾¹TÐõ)ÕI¯„oбk.+oØá/óÂ÷!9Cx¡à öKøœ»S¾Zu¼‚c˜‚§ºB,Ð nF]’ŸΈÖKWÓ«TŽðàË,~òòûÑÇá[>C¸ÅS×xÙñÙÁn¤Ì$¦Y¹€j„ui¦%{QIó"ÁÐf§Ì•,ÃeðA’áj4 £{qönŒ’Ф8¨«ÂG‹éAˆ…ˆ¼.êƒÃÓÿÖïoŸÛx’c  ·±?*|œã¿Êóÿ}[Û´‰¡Ó%î*lð7ú¶kU€—¡g6Òý5§U99º§8=;ëq euÓkòÒ±K-§n'ucšOÜFý¢¬º/ Ná a»F±Ï­›ì^ÿÂ,Iå®Ó(‹™hŠ1)˜©)ⱬ´<¥y‘Ù{–»otyMŦP¶icñˆ çOòTmt  ±“Ö™ÏÈ œbBØ2ð-‘ׇsæ†qá•ÚÀ±HB™Ÿ»Ä}ÞØnô Ýš´t+üLI»s¶cÔ–Í…¹Õ¥".‰:€˜¤™;Hfµ'Ÿ!sU’Gµ]+’WU$¿‘ù7H–’ÅêZ\:¡ÎT’ˆW[ÂÀÌ {nv:ýw'sªºõ‘HH$ˆž:ÇHêtv.¯Ñ\>1DÉæm— ¾¥n7EµcQ*]CèŠSó¡WÓÜœàj®TLNy´G½¤!½ WaÜ5¹>)vœ":Y½ØûJ.29bu€*—t± K~/Õq¼òû|ôÞûÔŽSÖœ•xxægÜŽø> `ŠL!'&ê ËHor¼À´ÓÀ–U9H… œ}†«Ÿ(žÃÝ \Ãy ƒFÿ®±iÀuƒ«$ÝwÕ½òP'Õ…¶4öHÍšqRŒ#2i% ]@ŸÇJ´f\à=æ‰3tJXhÚ¹¯Qž]f©öµY@H£ºôh™’e“ú´L¿ÿåíÃaOrlô6¶ñ Dõ·y#ãù?Ï“­ÁGz¨ô1ºÎý¸L…cÍieÍl…^âêÂMl’FÖ*Y%u°¡”w᪚X·z¤G‘@Û*'[¡‚SÌyø§¾Ê•áw>÷Âbo÷÷¹¯8%ší„úª+w|’+ð.lj=ó¢?"ÎC8b„ñHÀyr%*dŽ:²ˆ¤ ¤ñíaj†C†*Nå 8OœÇù–‡Ð^a!Áµú»9£Œ3#Ÿãu…cÝÁšµT í¡Àe]eã¡qîÚ0q¬¡žSÔ‹¨Dƒ˜DH•#Ÿ›ÄK$;®ÙÉmè…°Oxä8RsTÄ)æjb‰˜¤ªœ!óN†O'…|¢ž^«Òùlƒ›ã"Éá@˜c´tmQD±„Ä®FB³B#)É·É_À¤e675ˆMÞÙyͪuª¡ph“«väp° >ã+;X“3n’ÐE¯h5u'0öf~ꋘ÷¶!É*C.oÏ\Òg/Œœßù 7Ÿ§¸ƒ 1fŒð콯VLbçÀrƤ3Ìö‡%N6XÕYVÔ0†gþŠÃjÔ ÷–ýμ8ÃRC)V-fiA=" 2EŽˆ#b@¦XE剤½ as IDAT~&P“{BFšqR’CÛ!‰Õ†«T³ûåXd~«Xˆ %Ã".ÕYbޝ«Û>¶ñ„Ç@ocßX”?Áë9Oÿ£žjr_jÃV™ÐiU(àÂgùf}áÚkÝHT³jœÆÆz¸H`ÎV1.«¯é©ƒqQ¶»¿Ýåò}×å~ž;õr„²ºÃ´ ¸6àWîÁg¹þõü_;xù¥n^ãÖõÛy{7¼Ã^N®Œ—3@vTdÙ`/;]´þ…Öxþ)æGJxC}«¾L$(  @'ˆG†0 ‚ãÊ"Cò€o4(ºë"¸„úV>¯ðÜ*pSÞø ÒK­__&[º|jSÒ×r ;Óï1ˆñºp¼ Ôy Á¥¦RÁtnR'Q‹J5_¡¥:‹‰GUt¹wù°ÖgÕ&,JIàT\-™·ZÄb¢ˆa •ºÖo?™Ø€™aé¬I€:,Ãj’ö®!IT‘[¢³(’RÛ9†^¶=mNšõ(÷ÍçÒµ i‡€9®Ï8ɹ3è¶û6\å!¯8òùVz¯e“—½ÚV»¼å¯5×Mlm‘¯¸äõÈyõëüáoóò¿ ޼Ày¤Nxc Œsö+qŠ‚â`2#&0<äŽh­S`t¸@¡ ”kûC̨%<µC:ý5%ÝÙD;þŒ<€(¹áFäÂ?+Úd¨ï1wb'ßÖàHk¨À©ýv;Ê›ÊÌ‘šË P`ã)Ôˆ™$ s)îiHIQïõºŒG¹CÄD|ôå£q™ò’*³¯ 1å^\”"Q bª O )³—Žª—g2B&†t2C"î‰`˜bYG·˜BÕR¥S¤LšˆŠë5§¸Ú|t©×ÖøAö9téz#çËÈ™ñÑSž3JÅWß$LQž0Ù¡ÜcVpÄ«£®ñd=¢°®v™oR¯ PרS¶ú•\2!3â—ž§ÞÏõ<ƒ!'sÔ:*c/°ëy&ðvà BpL"§5ÒŒ<®w„ çH%ÖM+žy¿ð7cè꫾Bªh µªªý½8€ ™b‰z—Z°DŠ,”ÃvÜcä¡êqɰŠä‰qFÈ3P†&¼C†‹TZéFYT‘Gõ8ø…Ã-’½63C#eƉð/¾º} lã -€ÞÆ6¾É¸û4úˆkW.¨RòF[ê%/‹¾(ž^¾Ì úÂÂĵ•¬érô¹Ú¬ª…\X¼¨½unbníyzW«Û•Î..Û8ÌåýFº‚BQßãì.»ßCNfœN˜¿ Æ|œíÏdÙ3>#sCr¡œ1ÈÉ B@=dxaЕýÍÈ ¿8®(#U—“ë¹øÚP[ýf:sëÀ_ж¨®±!a Œã’;_"ÍÏ Û¼´É×6[*-¢¥søVix$$ሶµ 2dP1¬Ãµ… ™ µú2…ˆó® >ÏUUg Ô»$æîŠ>2ŸZ¥”"Ou2'BrÁ†Þ/®Û¤=áÎ ˜‚#*®£rŠELZq:*¬lwDY“[ÁŽÐ,©S€nε'Mûœ ß¹Ìttð5ôÜ´‚!xFnN:9 ƒ·‘šZ3ò²_ðàœîv= G?­¶ü c©wYÏ’Çf¯/\¾sôüìÏðC?ÌîU’²' •ªbìXtª7 žs\sTs’˜9N¦ ÄVvÓSäÅ/˜Í‰‘ÜñÔ.eäÁœ2cäçW]ÚÏêTËñ”ÃI ‹z@Á‚¸Gå;Íœ’ʳÐ.•ˆ4“ bÖªj ¤!Q(.#SŸ0Ÿ3jäÁ‹Ÿ™d.ÍÌÍMFANëÂ,¡%Ê]¾üùƒ_Ý> ¶ñ„Ç@ocß|¼}•úk\y9°Á¦XVõcøÊcMI6ß^Æøì¿í³/¬;<7Ð[]í&Ãdèêæ².Ùì.9Ƶä÷2=ߨçÖ»„#Òorú:üä×ËZÈ¢”%©¢6fGX"Ωî2­xà0ENqÖ¢ØÑçÉÇøDðìŒ JŒGT‘2u5ˆ½C B€Ø‘ ¼à J£‹Aj Y”Ò‘{ê Å„Ù¹å!`Ö¢g‰Ô ç…†'ÆYb¤§HˆKrz’d £3sPI9•‰K ;%ñš{‡ ¢qjzÕ»‡©ˆIR¬,8Q¨ói]_=rÏ<Ì󨯺“É)vŠ&¢´ / Í`„aÿy„EëS#‘º"FT)*|wÍwžŽÍTÐi]·"ƒEKó“f&Æ¥^/Q4uZØ®åI›µ³MSi--¸ñ6g£®%ö«]×jgûí¶ÿö2sP6òÐvyǤ÷:çõ¯sûs|Ëv&; fTÖJk7eì+µ0nÂ#ãXÇß(Z‘éFʼn3‚CÕ MÏx.¤–x3«œJLþîtø°¬°)xbN ‹‚E§Ën³–èÒ’Q7ž=%æHŽšŠU‰T Jb¢;˜1­ÈêæÚL*‘“är''õ­ñBÜ{“_ùÙíC`ÛØèmlã_*î¿ÈñÎ{þVK‰î‡nÞ±ª«õd¦/c9_HÆX.“ViÓkb^iUnOV]# ²¶$¦G„†k dBiÌ…e2:ëãdÁ|„_ #ìmNÿväÏ™|§(ÑX$¬±ö0"8mÙ~Èþ5îÝéòé‘hX 5yxDŒ”0WŽ¿È³sžÿ0ü·(†DcQ³¨)cë¥Ò°5ÖÊÛ:©fí"xGjÈZòæo£W1;?‹f=7›Ô’êÀɘ ©)”wsÔùœ¬2qÒºEcDs Š ¾­ Ð\Ü 6Î-ËD“xfø D¼"»Ž¡÷“è|R‡++jú`r§–»æNÈ?殓FHĦÈQǬH4*–a#Ò"2ƒ„5ÙåÄ¢êNEìì¦-{¢ÑØ–Ž¹! Å\;.‡v[(”޼n«[œÍ9¹¥±á\ ¤ŠUÙ}Àµ}쯊ɔzÞä5ÉåÖž~»æŽtY5­õ\GüÒÏã3>üýˆ'.2´¢Š¤ÔkÊ8cW¸êy*¡L,f$¡LÔ†EB³éu$xQœ“Tçö•y:5¦ÚÌFÚ¡g™q ³Õ3°ôBjÑã…“wûÁa2´Â¢ËÚÛBé(®°HÈ!8¤0cRCˆ§!õ¢J%!Ç+ó~ë§yý×¶O€mlc  ·±Ù(Œ72^ø¯Ù6Ç&˜é¾zŒð3½…u•0½¹Ì…š‹hÐô4ïdUà™.‘¼Y#Øÿ¡! xÃ+>¢'hÂOŸ2XàŒYÎ4Ô®M~ x¨,®Ã³¿‰åh¤ŽhÄFõéÿ^ÎN’ý AŽ"'ERl)€Ãõ›ÞÇ:ÿ—Øp!Vç±µòjâíŸà³0†g?ÉÇþ$úvž{?{;xA#Láhý¨¥gIí»ºÀÔ}Á+nNyŠä`­âÇùIêlD€Å€ã{s²S2‰÷\y¨µ—š ΛJqâE³)®FbƒFE|R‡WÔ‡L]Y‰¢*bb3!…J®eÓcKfZO’NÄßyc_>úµ^üâÑ|8Ÿ”y WlAZ<â‘k<_’I à×àé‘EMl̽šÂJ׿.±ž¼á=T»"†Ӝ؄Á\Øñ‘¦”Ð!Шíâ ®@ç~×É”+w8Sæ¾Û°ë14ÖæL6c³€Çvº aôæÀ2çSÿ€:ðÞïf'JÔ(2²DYQÖ™Ã{DI†YÛÆ¼c¯hl ÌHñ¼¯MKÊØ NtVÇ{f˜ßEŠÙ¸ø×VX¿Ò³ËÖUoù&×/S¢^Eák™éËñèrÒk匶úW6´óMˆá+­q®™¤v4öÑØB“‹Íã²U1#RûVãLšØ‡k·6¬^ýaÈ&ÐHÍ%üt¼ûåþøPþq–ÿ©, ‚sä9)"´¦Ð{×ï³X` C¡h§-k,1‡wò¾c%ÎxãS¼õ)~ Þóçøèòáïའء®s%L¦q«ã‹¦ËÁô”ñ‹Üû%/µÚKké]Žé.oí²W2ˆ˜'s,²ûÕdâ2ÉSÒ©åŠ*Ô¸Re&N¢ŠiÄÕ1–u¼3ETHó¤bY!.¨&>§*Þ«¸p]ýÃÓtzç+WÞø…³¿?>zþƵñóÇ¡84b…#ÎHžÀ!‰Ø «°Fg-É›)VQÇ6ï Mû`¡ÆÞké¬R°^Yi—iVcF7à¡WhèòJ·ÂF”ÐZÁF5¯!XÏxö¯P-ÕoŠÎ gÓ‰p»ß'è»Õ±è…]©ßõ.ܩۇ¿ô?óÒ„ïûûKP¥Èž4îA÷í[·òFfFdüãÄÿüÿTâ”Z£Œ‘YÌÖ6&àÆEg`¡mÝ¡W”%[•´µÉ"´>”£‰tÁafxkÛÑññ÷~mçàÚ‘Cuþ‹òÖ¿÷ÔÎÿ=ßùS÷_iðÐB…tt‘  ÆvœÅ„ÒÂ’Æc g+Ñì ž!²E/½,T—°–¬õw‰`,p §0¬¿Êô' m¹_g¯ëMX1F Ê耑pï {eþŽì)¸ŠË:Œº®ÏÈàýG¥Ët&[;ÃÌ´ÂŒßÿ?¨„?õŒbYj D³ž2ûÙË€(8ÅŒ…_™À-9 4Ö˜ÏypÊ™ÑBëá<TœfÊJ\æ6´ƒÄ³eÇ™þ¹ÑéšÁS¸´׬-?¿0ê†Ðàþöž[´gÏøJ°9tXGaˆ#8ÌAƒâ‘Π£9ÏrÎBaLçh¸ÔZ—n±Ä—&-‹ ´D8B´‡TS ¥(¬K™ï0 =GZp´¤ Y::.œâÀšÌ¹~Àõ%˜®·L[Š婌2° ÛÆ¶q5°+^)Á„1FPLS#{yxö”k}ü€*yð%ÞTήQe—U˜š ƒÂc‚jãÙŒß|i:L\&⟭O̦Ÿø¶§ßÿ±æ/þÅåÇŸ}݈ã†IM)„ë¨g%!z¦Ôì\ç(3~*èjçc_…ƒ“’™#téÄC¯¬aøY"«DãÂ7žW~ž_…é6xì&R£#Ž›ê9Ê2¡C(%i¼ã`›FA5Æs+Üä âÄ•QãM4àL-X¡bJ´ÚN܆Èrᘷì´së(î=ëï\[¼Ž?eù¥´ßÞ¯†<ÌÙs×Q­3zäèS»ú¿]Ï~×ß–x¥uPâ|¨¶%æ1#8H&y]..DcéèÆa×2àDYh½Ìr i-)•‚EË=¸«ÌÆ\‡Q“òµ.Êh½Ãu_1èÈíi@ UÄ2Û$çgMa®´¸Ãw4`×reÉ–r^p¯Æëz¡í°l× –šáQ¥„ý‹ü._¹<Íí]¶x£ë Px0*‡‚óœs²Ï‰çí†QÃÞ1p¡jÈŠs˜çã64‹Ö‘ǯ_w?½,Àg— µ ü3¹ôT>¯Žá‹?Éþêæ¿‰M\Ž €ÞÄ&Þ‰xøïsó!ãA&¸ŸÒò…uãÀ!ô¸  #nÔŽ‚¯î’§±Â|ÀÇàQY( m]8jƒ.€5JFŸ’3TÈ僭H¿Š»g¸èYQ{0ðøŠBxîmv¿ú¶QdÚçJ¥O’0š(^ÿ ¨~ü…;ûæ‹Û‹ãkíÙ 7ŸþÓ-DY.Ú fWÐ;8%xÌkGWQÔ\ )÷·ŠÔÎ(ꬊa‚ÕØÛ« ~¹MQ¢ vŒe¦A8eüAè—B–dïl?#ù.‡×a´ÂÜX*´%6ý•”*Õ¶…6¤µ‹QzAT,„¥uK“iy|­~mê^«ì¼:Ú»æ×;Ѱ¿(´Ü,¹ d¹HlYþ‹™ÿ1O|V§R½Q-9A`X [À)æóßÔ4§¿fEnŽ“)»§«Ü3¹ºÌ4oM {Fdt”«¾Ù R`áH+½Sáh P5Œà&Œ³ ¹×ż|!xC£ЬeT{·äz“‰ Áð™p\å'ª–³ÜÖ^±{ÔpÝØã‹Ai \ú»T­øÈhàK|ŽÎ9ôxa©#Ï3؇8Ï!9.ESú4´§yNö¿\2 kûH«Ï‡Á:ïbÝáàE9h‰ ˆc]Á!|æ¯ñêOlžî›ØÄ#c 7±‰w$ö9z•ñs àúuÓ¾Ëp™K/ú²ž Õ tèŒààíŒ}{/@˪^!#ã2ëGzú<¡zPŠmWÙ¾Þs΃“5Ø¿ÂÓùà(àÆ)£óäÞLoY—·Ñ_ØgçÅ3ÃÅÄXd•¨=Z‰ïDmüß‚ž#_ᥗ¾¯¸òWžú¦Ýç¿n1ÙÂ9fÛ”eY#q9JO;⬥ù*ã«,o+]´JŽÔÅ ¬Ê”åmÂyº¼–уPb£LZ ûr?^8žsPPjØê’™_%˜áƒz/A´ÔÐ…d"!¸ÇÏ«#-ïÞ(^¬å¾z‚%¦ñ£V¤ é¶°mœ>h<ÊTí™ðC¿m£ÏKøÞÒ(¢YŒ$M ›@ôå0äS³Rš¬`¤œ;¦-õü ôÜ“¡)r¹a XHZ§Ø"˪~Dk„†ûP®ã¿{îq4/ °•¶tÈÎ…ñ¦EvC<‹dÁ6–¹K†.$Ƀ$%0aÔr]Ø›àõÒðtë¸ó‘{D=Â.á*´ðEî< %«çÀÃ|€MÀ†¿°Ãâøm› ½Ý¥¿ô½1sÖK‡œ¡)éðGZÏHn­ (—œ)¬`ןfAŒþ÷†gcŠÀ³_:R¿(4 ÷çû²ÇÞÒÄzõ¿7aª/GŸX¸¶Ùž‘”Å_Ju÷·¼P¿†ßxa±>Ì/P´‡²Ì’²÷8y9J ÌùÄ¿ÉÞ?Þ<Ô7±‰?$6z›x‡"ü|ÓR:ÇÖç*]Œ[ÃÇú(ú²¬ýÖí2›±ùWEž’{•€³õÙ=B ?h믕²;Ý€`åçzgY—›ˆèfÖqõ€ú‹R ½„™!†w,*\ÇîÁÈù´cmEìRmuÉ2±Y+ê’ª½Ó¼ñ o¼ò©ýÓp}|U\Ń»œÒµt-æ žÎóðŒ‡Ê»œÞ£©¡à™À$îïKRß‹©Çàš¨Z!%Za52F&ˆ&g¾„ž/¥ÅVæ2]ɃTÆ%eIYSÚxŠ3Žúz+»´hˆ —sÚF¼n½XU_Ò.‹úÆkßqexu†z ¶²(ØÔ…¦ÛeÇÖ|¯Zu[–O[£âi:º%9ІF…Äþ„Åar(4eVq½¢îi$$„J£´‘Fh ™£<†YZS5 ]ÁH‘ë’•7k³t´ež†g ÁRyÙáÞ2àZa‘á2Yˆb(ï+!O …Ú’ù” ÊËžä=Ù_©ª¥„3·NvòŽHŸÄ ëð:’.¶à8ÊÏÂ"ë•s^å†êœ_u¤‹™æ~—ÉåE2yÐø¼„V ûÄe‹YgŽežÿ‹ÜN…Þ¸Ëoþ[Ì?³y¢obxl(›ØÄ;{Žê.7žX³PÑÏ_7H!óZ€^*<£k¨•ê)Ëò·q{·Êóq½~¡ÎFü|oá6 I¯TØIüÑ”†Ô”–*·L@)…+s¶öÑŽy‘m°rÓ”eËrF½l ×ùµµ„x,5TÉLÚ‡ÍÒhz IDATE‰…‚ªæÊ˜zþ¹óßýÜ^ûžé7ü‡ªŸÂbY$…>p¥åvÉû¯rë€iHég0L¸„ÙE’rÐÁ•“‹Éf³KéçÌ8>ºB+ì´Ô åŒÂ Yš÷…Kú5ty±óíæÄOÅÍœ×âÕ£@$Ðe®µ¬–4n1蘔hv&Qëm^îaÏÝÕ©êÒŠIU %¡D£Šœ'T ¦9ÅÚDÞ˜–ìYR#~‡'8´‚eºPé}…2+Ùd}b‰VÕ 0 £:¡¨Qy*!Lh°\Q}ð‰V>°³ch „z{9‹ØÁ¼$ éHžé$±S¼ÒD¥‘*¯UºCÉ BpÁ¹•tú$\Êݺu¯>YOÙÆq½ãõ¥²h·wy¸5ù°— ´™Rë¥3d fGÜ—‡<ë}Å.­Øû³è'—¥~xñÿä•Ú<È7±‰?Jlô&6ñŽÆÛÈ}nÞ¤ZÏCËÀÖAÖß¿2ÈÈšáy‚mnо#è €šœ:ÚËùiw)¥2àîò ªPRgE³˜”íeP¼Ãe­åU`² žS.)ô¢f‘0O'RÚÕ1ΧLª¬™c„×aÿ"BšhÃÒ›i$p‘ÒÄU[Rž}âü“ŸøÉ_ù®ßÂîS¨#øìZ'lw¼p›ò_#üoS¢|²æÊH.–t7òªF.ЄÔÒ—KÆ+àGß@ž`fT·C˜>0J´YXm 4uL8 JSÑ¥¯ÒŽMðŽp«Ôû MF4åë 2²¾Ô-αˆ¶¶âÃHq§k?ü@¤)ЉtD"r „ X‹_fNpÍÎm°°Vsf-!S¢ˆ¡h*AK½5cèÄ/1p ¯œul9܈.{P» (a™÷I ïwXÈ$aY1Ý“G^§GWÒ4Mî^œIò¿ %!¯EƒàøÕ2†VM¼ê”g= ­YâL£|ò:ϸÌ9f¢öxåiI”•И.S­ú?Œ’ÛNu(©ðx‡EÖK–ûª‰øýÿ„ãŸÚ<Â7±‰?blô&6ñNÇ[·°»Ü~"ÍvC]Ž"o¿^v äñ³µÀMË“;OñÕûÙ‰-&ž#‹c·á¥ü~dše øJ¶ð€ëçè’ÆË2S@•á<µG.P´”Mª— $›ÆdjiÏ‹[Á0a^ÒV³²9É:θ’œtn£‚&c»˜/’ÎÃÀŠä]È’°ÿ3¼òižÿ>ÞÿmÔ‚¡F)”žÝ¾½{È'°¼P€‰"î†8¸¿ ƒÅL§ó«òØòAAËšmfŽ-‡¡Âê#ü®G½,U « ´U’Ý-YëëáÂníXDˆèp>›ýõD€Ç-µJÆp-pb¨­>(J[þ^Ù}÷[¸¹°³Tš(ý&0]d ´‹˜/§*¹¾MiX“w!úL8à1E ŒD*èÍžÓ‹ˆ§e•‡Vð  cVàÊdÙàÆˆ#,³°tü;„¼ÜËÔ|@³±¥íˆùï0¥ ˆàÕ«´JW&4m…)E‡Ô£òE p–%íx”bÝðÍF©V2½?ÁpÒ¾$Í`Í ˆ¤±ã/|×p¡É 8Ö¤žå/ ëϓ𨕹 –î èòRüÎïòâG7OîMlâ_*6z›xâÎmä.O>‘àlO¦ÔõŒâÃ)°¥8æŠvÙ¿ÁÞ T—y(_ƒ7UGUÜúž/²µXéôžp&©_¡´dlá3–ŠNCà^6—Á­c¾=Ó½}¡Ð”º5Cz¥ø—åÈ¢ötÊŠ £€„Î1n¯rxùŸ°÷U>òý\¹ ¥P\ÃV‰|ûwðoà ,DVWc½,ÒÊÌÇࢤ­Ãëí1lÌÁ6…çÆ- æpâý/ÞŠûæDÆ‚¨5•`¨Ùx)‹šÖ¥<`ðWÊÍ‚êr>Ñ.y}\2kaÖUC„ ,ø§cýË-³Âís’´-"0F®b÷Yž% Ý]ꊰÙÒéK–P‘"åžEÒ…–:æ LzWàG„q, ‚QM8[Y±h›ÄÅ-«Œ§þ! +ŸBéûÃÀ)Wf§¨§8}Õ–Õ u+ÜbÆýÌ¥{I-AiKÄQ EK9Ç-( ît6¸²Nçv’x¸ 4sz$-+‰’‹X\s’xÝJIm}i4r–õ2Áˆ¡‹¼l>H÷ô=Cƒ¡/HõõŒ”xãopô?lžÙ›ØÄ¿llô&6ñîÄ[·‘=žº¶"S²>›rÉãà°ŽÔ½}ž:cûO<ÏÞ¬K×Åt W`/ÿI/ûÐÍ]T´]ìj²Ÿ¸¸o,«´uµÞ4»„¡m Q=tAV8Ÿ0B#s#cèÐcè°Æj ²r3ô²âf'´" é¡è¤[Ž¥ngÏ?àö·óìÇ¡dÜ¡†ä6G_Ox=íÎ ¸,0Öóqƪrõ¦C3è2úœq6cîØ™3q@“©nÑJÙ†-Õ%ÅC“¹Lvá*]i±ðpÔ •,]ŸA ¥fzB\ÂD3“U¹l3×ß@eŒ æ1ùk 4‚·Cu÷o‡™uy.µ3Câ±&øvŸàØš0ž¦[¸Ö+¡D«Ü”¸`¡]^MM`„6¹0ÕŠ”ß^'Ò¦uÒi4`xW 'É-ÒeÓøž ¯¥<¯L[¶ß~¥<ù@…e`e‰hÝn¡“­o\ÿ†{W˜Ga MKǤÀ¨tJphM¹`¼@[B…Üèë߯aœçÌ Õ{CŒÛ M^@á=eÂ.q3.ÞÏKÿi]¶ê«ËtCÖÃån½Iw_ælÕ›ØÄ¿ZlT86±‰w-Ž ùëÌ&I1ƒÁTzyêýÃ_XIáØ}‚z{oÿdÒWšqÛX”ðæ¥uñµé'Û„k Úö‘‡Î‰CYhÌ×AW‡Œ$`Y_Ah!(“3/~™3Œäé‹JURRŽvΣ5û]õ6[4Gã» è”e³dØí SÎwv§Žby¡&R€­OýÖ[|d„ô¯©Ìϲ$ƒ žr"JAbL넇Ÿáø5n}3“Ûüþg8{‘QI¨Á+… ’ÄÎ#tè|9Ú²Ê}®~émÜ œ]á`Êíõ6~›9v_ [Ÿ5œW‰ÃÎ,4ß™6rþ>ñÑÍ+¥ÒbF;£ýÆ-ýô©¡CÍbýÿ7‹P»j’3Ã;ºz›÷ËeYœ%ÓV±š9ìCK؃Ž+»¸¬ªfYãe¥Âaic‚9\¬½H“Ô!;Ok°BñŠ3Fžy·òÁìZÊ%E7ˆ“, b8ÉtŽ(2jL¨ïýÏÆ–1‰ë.Ÿ:’:Ä0ÏZã‚'œqºó±ë Gg8ú´ ršÈj†‹Ê<6‡¥x–ßœ°’Ré×^~=o™àÑ­Ûºü0à+çUtŸ_´O¸=ŠOrÁ2ðÒ ¾(cÃÒ‡¯òÖû7ÏæMl⽉M¼ËñÖmô'¯¬FÛPö²?Ba@²Lpˆ;÷¸õ$x‚½3ö€z Å)cG½ÍüCp'WÙ%+µ>¢¸qVsuI'IfØ`!k Éû$fìrõUdmÃÀ!™,ë1¬)<Ú¢>šºåiH‰]Ë€!n×''fE‡xº'¾†äègš½äÀ¬¨4ë-¨áÒóæ/a%‹–q AqÐ"É¿"Á‡)šÏÓEVƒeqº¨Ç7à˜_»KQÓãä×v˜lãŸÆÆˆ,`!£;2úŠç \sÖÖŽ=YXàÆacªSÚ¾N·:^©O×ãOGSF5úú ëMÞHwR¨[Kæpè8‹ÆF·Dº7ã¬U‚¡žÖ³h‘ r»‡o˜^e\ÂÝ€5b+>M¬ Œ…ƒ’—s2N_Å¿)ðc¬Iˆ°ç܆–¢edI~M:lÉ¢£0JYYÙ>oOÄË"Y›bûˆé½¤l+eܬ‰ð“T4<™;ñbx4ˆz‡sŒBÊTÇSÓè c8ð…£ƒ¶'9¸œ†Yûcœ½—®ÌÚh8»‡ÎÑSp4Ðåhã:G¸ä !.Ó²‡• — *.¤«û7ûãì¿Á zÞÄ&ÞØèM¼‡ãÃÔ…òÊÛXƒ^§»Ïñ_ƒ×¿Ö ycÞàöûRò–› 9ý|¡hÉç¢Ã¡mǽ}îñ /Ìÿõfü¹·9“„ÆÃ)•ƒ§aæëS~Ï¿¼ƽ’«Ž‘g.+mÅê÷©*-mHü ²ÔpÔ ‡5v½”í¢àäÚí;Ç’ ²e Žè.çI©Æ kúÐ+QgB®cTº¶À“€¼Ë{ã…ÒT´0.10O§x(£é† ôHNÓÂ@ò aÈ:•&æˆa-[Òôœ’å qÅc vh†Èä&¼!â4&ßEA¬.âOKoGâ'&&å)€¯z[G¤¥Ù¢úàŽ{ñ¡ n@“—Ç£çÜi¤dê¸)L–Ü)xh´KÊWìE?mÐòÄéÖbÁ¢WkžÒÔl=‰€ Ñpu ³#·&¨JTs(¨C #UÒ:ŒléØÖBÆb´`”€±è°’ÝIÝ­6´ß,Ѽr& Û÷>o£nÀŸT£Í‹­í–àt­tå«…ÎXF¿j£0 ¨•“b=¹ëe÷(ºÏÐWehJGŽÏ°»çr¸ŸÏc?¬tÄÓ]´A'ŒÐGЩ‡÷èqEÉv L<äÎ3›‰a›xGb 7ñ ÷g¸öÃŒÿlJ I®Co~€38ý,‡ßú5mÏO³øçÜú~¦ù~þ“äó…äV ÓÌ?Ø;àôdü÷Í‹n|ÚR+á•£Žn‡7?DøÍµJÏÚ ëZ@ØWÎFl%‡äxÊÁFtôIñ1Y¨žÁ¹€¡%ÃKºf§cÆÕ¤lNAm% sÏ=9¹O?“éÎ&0Yùj/Ï*:K)ä'† ¥Ñ@›ý\(PÁK6·‘ÄPYí¨G`žž†Ñ5¦X‰_0Æ¢úuÀŒàYn3m±(6¼¢0¹¥¼lа%RDÕ’ºòªr¢óG¶ù±È-øiºŒ ,ß·3yñAK*ƒš± *Ã{ÛÓ „rɶьh¶¥khÕ*[´Ì—X¶4cyÎö7Q_ÅÞFÖ­rð«›1J×Á¶`†z¸‚Ô)a!#Da™Å' Bt‚iÐfÕî¢e¼Ä„´xl`.TQ¢…ìà­Y’\qÂÖ®ý-c7`–Ô2$[^Æ3&ܱþR H絺 ¶À7ù,¢! P(*TÐ {¾S;”)n&¶.7³>‚’S ‡Óì®2mf“ëÀœe¸}“7=V«#»DÆK†¦èòø<ôðu€ý·yëæffØÄ&Þ©ÐÍ%ØÄ{3¤¥Xà.•Ú„ñ:<ó-¼ßÿ½¯i“Þþ·yé?ã,·§¸m/;¼\J‡¬X§Ƽãó_eÙÈÓ·p0R¦W+žðÁ)ú:öOâ. gõc.WÚQSÁ$ÿÛ[öÔMËó}Ǫè/¬íH¯LPúßFƲäð©Ûm9 ´ðïñ )½k>yrÿ³Á¡#x|G×Ñ6BIN·¿Š!Ãèq>Híí2…×Krr6YýI¤Ûª¬8Љ‘­˜®IèuŠ/%ÝŒ²D»äÝèÅo¶O8©º0ëD[Ô¤C–½;FU†íi[Õ ÊC)ÍJµÑ9e—Ê-A±ùMº^ƒÖRë—ÊJ¹T6–/¼3Ä®´\1Jù/OÛÓÐuî,dP(XKwJ8cú,reÝñÇ0ÅF0ʺ%VÁ ©@‘©.«pDeâQ"£GZ|€ÐÁ’"ïÐ K £ ”J¡†RÒEŽ@4¶¢¡j(ö?Ø ¸¨àЀ$€G:¤C\‹¶h‡vh@L±)6ƒqX‰P!%R€£+˜Ì•CÍ#ÏëÕr~Ö5Ûô‹ª‚4ààì®[®3¶ëY"dä=ÓYÿ–ËÝ2 aëzcôQmc ¿oƒž7±‰w66*›x¯ví1û®Ä-u×Wï÷2R¶¿úGXžã?ù5jU÷iNÎ(þ£A^ªÈ¬h?d0t‘ G„óЇsnÅ Ïr|„y Á)“ oŒ ÊÍ{¯ œ‡û‰\¼ãéŽq‡Fq_!E„ŒQNrþ:ÿíÐõ:ymp ”X %âh&øéVyŽtǽýŸd­a[éo0ØÓ&Û¯˜!†Z¯£³ÝŸÌœ×d Ø Ý1ñRR(¥â@qé2X.†‹'«Pä hŸý^³qŽ iO ;A 56ÂJl‚ÍàêuÃä0ØÉÖìð}7^Ý®ëÝQ}c"ÛeÑ´f‚DÞõ²Uif²¼e~‚ŸpŠ,±i7¤œT/ïǬ¼çJÿ”–uÊO‚ Ëjöš†EI¥xcdT²P÷ýgÅÕËù¸íÒq$мJ]³}ÎáæU™cy\.LÞ"S˜bŠyd„U¨à¢G¾¿¢Ðá舻ޣ-ÒdnÃ2'eû›•o[•;dÌꔇÇ?V»6QôE3žŽ+ ±‹“Æœ~û‡:C:¬ÍÊÓ’ ³0K ]  8²Œâ(—97^ã®ûºÃ5˜1`¢ òÙº² – ¦ÕE–›¹¨–(—Š ‡|’f½m—ñ·æ'ÀþoÞØÌ ›ØÄ; Ç&Þ£¡· íI" ê`Ó³O5íÂìÇ8ú1ÞþðÿðkѰùóòóÌ}nÞL@¹Ëf(m®’l³dë„8•Žñ«wù¦çxö6¯¾¶Ê‘•J!Œkžý_þÔS¸Ç°h àÔq‡§ß¦°t© ¡ƒ `,%ñ”ÐÈVH‚ ÄßÊ::Û¼ÙÀOîtÆòùë»o_=ø²ÐE ðPCæjJ÷DÔ4¶íä¹ã«·Ïªìx'««Õïx§r±Ü cš/Úe·ñ Wà\ºº ”˜&8<’anS£xEA¡Ân`7 †¦·‘2˜´Át6YÜÚ®;fF)¸®Uõ.,XÏEQ‚G ÓR!†¶+MÀÅ6õ7$K1€­ÐˆõŒŠU°‹Mú-øÀb„.(ñ˜©7)5Aıq¥¢ÎC0C02”b_º‘¦Èë39ÏTåË#YD$]Ÿ‹èYVŠÛ}ýe1f º@-½œ÷é¹èBa«Ä, ,qSÊ'³¬`&°[ù¬íÝ¿¿•ÖoâÓ=Žªáâ’£µäf… i’ž-°cè“\›I9]J¯F`[™ƒî²{»zdþ%ÃGÜÞNû½w¤eRGâÐpÖá 5Z@‰3QºCºdê#$}ï+YÊ9Ó9]ÅyE£Ü<¬É+sâ²vB]2r,C>ÓCk¶/a]un=,oÇ Ýݱ•Îj´j>Zû¨ ÇCl低×ËÍ„°‰M¼±ЛxÅ ÞŸYVn ÂаÎ_rÙj¸±Ãµ¿ÅâoÑÀù8ü/°_|[º÷ýœýM>øw™ jù‡Û²>7¬gbô:",Oyð*O=Ïü„åœQÁ¨HŠå6ý6Þú"\ÉG 9«-ƒw²þ´ÞœñásjŸ2Ã4†"PìŒfà¿Ö»2¯=Nˆï®aõ¨:O«äx‹æùçëŽrIy~V,Õ·º<ÁY!0'¥`_ݘ_}þxŠÏb—Å ¼HÜÜ$R>/&DË쓦1‡]"SL³ù!X$T´´NRf:JiçØ,b/`ó1BÙ˜ Rˆ¢Hܨ+уE°üDDŒ–âPºI¢Œ`‰4‰h¾Ø¢þúç«ÏYçú8-޾ŽPf4–Pµ‚°_aŠÓèxDy›b7àò5Q(RQcZrÖpJ,Àƒ"™3-q·ÌŠÉèÀ 9§h0ŸÁ¥C–ÙÀÒî–úu¿° yM0Z`‹_ïØéymæÓeMP?ýk #¨‡ÝAåÃñÈ“Ô* 9ãÛwTAõ‚¢å´b9Êš‹Ý ­_\I>¢çx#4çÝW®Í ØÖûKa@ê°AÑ`\É:÷Ùè°nÆ4Œ~0Ô °¯mÐó&6ñnÅ@oâ=oã¬^sûsŠþÍ ®c}ªi SØù·~sxøÏ8ûóïVcç?ΗïóÔ?à¦[ù8´ëÅømnX•?ÎZ–•çÁkìÞ`ßÓvÔ1Ô(…ç?ÂÍgxp¸~Ža]m®WÊëx©àÚž|˜ ¢&Æ Ì£šm®r[dÊI18^7xŽôº§ujþ±OP0– 3ôêTlªª£l£hÍO‰wÚVeYá5ë`ÈÚë_3¸½«1]®0´‘!±ÌvÍûò=!À­£) Ñè¤[hU‹˜‘õ¦f¦Ì'^Ú¢Ñ3ñbQV-æ¡tŠUhÀ0=wª˜d–<–ÙºçOR¾2ã0ÙÇÈ.]Rá0,cc=ýJíOªeC\àäñUì®aS(“×`ÂÐÛØN¾»eÚ†‡]KLý”ÙvP"}:¹ƒ!rƒFï =6GBÚé%WÊX®*³œ|våIÌ=r½b¾Ñš1d‚Î<Â[µ‚«`)#.1S·¢‘¤O§#Ù¹'T‡ ÚR/)–,·i«þüâÏÂÖ%ça]ÿ1“^®ÙÝfv‚^ÒßÍ- ½¹h÷-IZ·Ñ夎‚qÉ"%Z½äù?Ñ|#8(ê“Ì„Wº ­ÒýQ›™!ƒ}ê„íò·páv «rFÒ]–n`›ë€”ŒŒ¡ÍùÔü‘YÇM—°§Ñ/1;˺0'­S³PÌ u¢ b""ˆœã»S•IQïÛUÌYÂV. °$ ØA‡yÚ’å O󹓼lºl.×ÿ†–9¶þÚ@¿ˆøÖÓ6ÐÒ-α3Â8ñŇ‚Læ k‘._h…œ"ΖÝTˆ&²8Zºh†âá óIÉDÀ–«Á¨C©Kʃ–7 ¤§ÐîôMc;ò†,ÕÚE Ž~QÖè5!¸xmÊù=Ê'ÌÁ81Rl5BÒ´±"‰îõ÷{˜ÀOË$O}@Q³ØÁR2~w¹Ïj 6œÐ对µ§ŠõÔüuŠyȽ©JO€ƒT};!w?7ì/Õi{„ûoÓüw›Ù`›x÷b 7ñž‹ãĵïYsÌ–u­µ¡?òÀFmü«LŸæª±ÿ:Ͼ+m~ðÝœþW<÷ßr-ïgk®RÒüolÒ<)aKÆi`¦,°Oh¡Â)*ˆá„ç?¿´~Ü¥æCïÁ—g|Ë’Q—ü· `!) X@Ð$óÜ‘¾ˆ(Á*üÀE%‚€ÞcÐÕuK<Éf™‹EDáZ& ¤3lÐjY·ü`•¨»ÉÓ9¼ IDATpK{ º!ȶ˜üÜÊ©Ú1¶…•Ø(yJÓ`Šõ-šÑ¨À¨áê’¢1]˜6»³ßzáÖïGÑ)ÜM‹éTF]ë„ ‡UóEð†"hCyNsS)qƒâÑh‘˜ég·=¹¥wæBi‰p³ºiƒ°ž¿‘sÕ’³¶B£Ø²(h  ë¨nAI8 ;§çZ¼¬=’.\öi²‰U‡TÙî$"æ&åžE Å–©]49çšÇ`Ê´‚hæKò¥$2¡»ƒsLdÓ×ñåZÇ•êÄ wˆQÖ¿õrñõOøq^œYdà…@0º€7œOŒ#>vÇ eòõ\ÿç;4osý€IM0h5‘K:0E ˆ †f@ÓöˆO÷–Õ£‰¦ úöÊø3×;^öH°vПÝ`DÅLùÎàál¦‚Mlâ] €ÞÄ{.Ú@ûÓ«Œh‚Ë]Ú¥ÖõDžýØFý 7ŒCx ï|³Ïÿ6¿÷·ùˆqkÝ"¡Èd&0GfhÁ²ãÜž‘§l°Áh  t-×nó߯'-Ϭº®ü®—°5¼&<5æ}'Ô™®:ex.×DÉ¥¤hÈúhíE²¿÷š•¸"–,ªSÅ^Iñ+δ ¸6^‘iórf@ í¿qUJhYȹ'°ÊI'·Ô-S~­À²ù…)¶ %A‹Ïš¿jZ J¡æœxÃ{Ü›8aºoÓkà‚7ʃys^VnT³±ªÔ¥"ÁwALÌÔ™„“eQ(“æ 5MûûåúV@‘”±»’Å“LîÈ…ñ¥tôÑíx-ûÕèìm)nC ëðnš$h|X_IæD» ù7õjC⪦ÊFèi¡¡ )QLƒb'©Xƒ%Ò%úM:²¬Ñ{VöƒÝ õØò‹•É{H‘Ñ3kãxmÝ$ˆ~é°j™‘‹ 3V=ž#.3]¢¦tƒuI¸$î‡y5á%¹³Ìîsó(orô&£—™ î«§´š¿»ËC©[±L0¬Kô¡h5ž*5{îáz¾· ÊÚä¢bô]¼¸vyÐÀþWi~b3lbïjlŒT6ñ^ŒÓß\Á‘¤±Xs.°GYÈcè}9šÀnÃ×OÅ» ×ñð¥äÈ0†zPï?ÐXR´4Ð,àÜŒ Á7´-]KÛ"ÊG>žóºz‰З2(K^ª86J¨-1*waWØ…inZüÏ­³|&EX›Ý4òŒù¼Dru„LÚ5ÁJB±VÕ;ˆŠ/WeŸäªº0px¾V_Rc8€}aö•}áH8„ƒ–}áÐqâ8ÎeK+„ú<èy(ƒvACPoαP½Ê=/æ'át/ˆ7§U©•vÇç‹y×á@½¨Õ•¸Ò‹"j•“Q%[#u‹%uÅ¡Õ÷EC* ¬°j â&¨Kë™Åí #ÁË _ Y«¯]Bã»Á•ð­"Ëó9B=ÐâŽð½ÀÆ0ƒ«pn%¿nÙ‚nŠeÞ‚xX$Þ¶3Ü9àdå}YCO…ÊPèd¥ xK½HZ:NBV¦ëV=Ír¿2[uÜ a¼Ø¹d?”gwm†íÂ\‡)lÁ66&8ÌA™ô¼»Šw‚Ò¹\F)T§l½Âèÿýûîífq{…­‡*.PZÖë R i.iÍ!qˆ0»£N¹¬SÝI ¼¸œV¢}9YvЭ{µœçî~ÿë6“À&6ñnÇ&½‰÷bœü,×¾5uðˆâ~s//Å G¯0uYÜÁå4ì®ÿŽþwþcüßÇZ~öÏiþFš]NÍ ÙÛŒvK’ Ú~C¨¢®räwº–ë$3óG†ÌÁY´d" ƒN$ñ zm½˜áê?>Ïiñ~—½X±VÆZ IùÁ²ºˆä¼š €}úp,Òj·B.Ò {U»ˆ³"žòÐ| {bkTa¬ÃÍà)V© …)DʑȎDGîê½C››Ë#$„Ù ¯2)…âüÄO'&΋µZ«¾épèñb[ªË6‰[±xk?jŠ•igßzeëæ˜ß¢¼?–ÓE¯À"뱃%Lè]mm háôQߘ3ÁÏÑk¸Î™áF`؉àt„=•—2S$z…Ôi0ÃYJ¸Z–.f§X $$’Lt‡Y53ߦ)t™é𴤎>N­)† HÅa@– „–^Ìnòòëg¸ÕÕºZˆ.¡!L0Éå¹`P DwO(ðFÕ0¾|õ“ºxÉóÂÃOUåG¸Y0*“ǺQT1õ6Ÿ%´C*©ìåi¤]1ŽÄòçvIp:‹ ÝX¢Ü¡9ð„È9ÑuQC—œkèàînf€Mlâk½‰÷b4ŸMSq?»”5\2» ¨;œÉä1Ÿ· nÂä§xãßáüß™–‡£•¸•dˆ˜/¯ü† îÁͳŠK— :"Z0uÇt‡ïø“|곃òÄ á×óÓÂá‹#ö[v²KqüÂ.k´‚Á4Pf´×g•TX‚bQÖ û3Gz˜KUHÂj'žˆn{Š«&Ί`àW¬lx tŽnXI×Ñœ½Ë­òIuaõ™¨÷\Ÿš)bèBäDd‡âTô|ÒÈV°ùQgÁ‹Fe$niŒ õ˜S“ª² 2oC!êà ØÌ¬23³bI7N²n‘xÝb™qXã8–é—°o²Fõ½IòŠÆ²ˆqñ²„EÔ¡ã8F°sð,Þ`ìÑ1´ØHºãÖ³v|#_ ìHró’àÚN0O8…9ôŠÈ½ONG2”Ë\]Ú“­Ü`aP¶»BÝëÓEjJHÚÍ:°ðK”h[7â åOÞ<üÂñwäø‰‘E1í ?Ç)´Ð 1ªë@QEF-;o¡w°ý—”±ñчÿ¡äÖçÒ¹‹à“Þ Óã›ÁCc>ð\ì IíN4ÜÕ-·{¥Éh+¢ÑÛ£Jȵ^V’éé/Û$±ÂÃ;,~d3lb_ƒØèM¼#ü gwÙ¹=Ègf”´–˜€Yg³î¡`â{ĘÁ ?À¾qï¯âòÛrwcê2v©s*• 4ZN¯Ìy¦%Ú6ͬª”Bé°Qž~?Ÿúu¨-×\Ö)Vù°ŽÃ’å‚ö.‡Ê‡óßYNŽ”¥²€)·RâZÏ|Fr§¹U¤e²npchŸTá¬Llë˜Ò¿žGÍ[Ø+®³¬l¾câ¶!©ÝÙºóq»Š€˜ª¬â<Æj؆«-”6iËí·ÆÍ[•¢àÞÖrT•'âÏ+¼¡u½¼ºÛVE', ¿[scVZ¹PaZ—'ºl#CJ8ö¾ó].ºó¶1m¤½eæLê¤k–³˜b#XÆÐXnSßœ”Z¡ò¿W:‘bl@†Þ÷{]uÞ,ËšPdšøôËctÉø 8ÄamVζÄÔ "zµÌc^­aÎá|òX~,²vVdÞ&­¾’¤²àrÎu‘±eaL–„¨Û“ÊÝÊ6Q.Ôý^Ò–;¦ûß÷v^þlõ½Ï?ü¦§üvru %æ)Zhá˜Va„¬C™=`÷EнbTÓ³¯ÿP{Q(>¬|¾KË´KléH¯wäÂS¨ÞŒ&§íéôW%’h<¸THXãŠHZL„¾žµ×–n!À Ü{jóøßÄ&¾6±ЛxÆü3ìü 6Ie.Ë:UqÈ`]—cKý¨ï*áÌ~‚ÿ5Oþ±š½ó—WþÀ.ƒŽÞ‰°ƒ.gƒ 5ίœó¡m@[F­q¾` ÅœÉæ'_€íÁY)6ư’+Ó9gLá öà‰öî Î–(Ó˜j\׸ƒK]!‰1'~mÞòN®%æÓf<™¤4'`.ÂaRu¸O`)ùÝ[?wdU2éMTVòÈC%_Á*, â猜/ÍMîîÜü|P ÄIYÊÔ-WTZ¬ES/&½ó~>WYÒv¨2©uݱgò†‚ŠÉ±îDDHZڼˮß,ê2󬘼ÿ…ÍI»2އJÀ˜mƒEÉôÓ+½±ËÁÌ4Ü)ôÄÈù{‡85L±d:Ød]ál.èÀ5_à „œc6%úú¢ÈKúYб´Ñʆ$ÛÙoòó³þn$$íĸSÉK-µ#Ú–ª¡Ñ蔇ֈK6ùõë¿Õ®ßÿcº¾F¨ˆ`ù=ôEÜçîéÝ“i7ïX÷øŽèi‹o{çÃç°y&«xG±@ˆ!"¯t1ó”t4g¤-›Ì«làµØfÖ lùÑ'ÖÓà|”9Q¶‘—°Xš7Ò©<‡õ_ØMü»ØÅ[;½‹¯Òè~ŠG?ÌÍoÚ¸÷]r«ßb¡j ðz,©5ü?a¶íW“ßÄÞ5ž0NàÞB÷Ÿý“mö´Ü¥d•ŠrmJfZäŽé®§¨ðå5SáfÅTp‚} í‘sì{·ø¦ïä_ÜØ€oí×H.`  ¯j˜à–¨ðÜ1…ÕÈO ysjÌðlºÇWù5©0É,ÙZ„ –ìE%ì>OW©*'beC‰Î”ÜHú‚nd™¼©:X°KÉéÞ˜´ÔJŒôB'ÁÚkcï¾; ñºÄƒFNCFN{NÔ¨¢Vjâ…QT÷PïÕ³ØQ5¨7\pªó¹èRÎ×&ÎÄd"‚è¼ÂÄD0¹g¬Û‹RE°$‹Ö ZŒ£EJ[aÅêÓבóK¬"Ù}ÛmÔrÙ|ôÜ#½/½›–{ki`ATVŽÙ îÀë¡ÊtóD­È˜4!¹³mG€œl”Ú²úIŸOIeùJÕgÜ Là´Øµá¾Ë.=AÐÁ Þ69Ag-ñqBC6ü‘€8æGÞdßü½fñ÷ÞB‚¥ZÛ3sÿÒ²£2n€‹LzB’³[~çí‹çð3"X‡›Ò5<êÿ"gzbG•ÒÌm–ö#ÂùÆRTz¤Û@~OGB¯¸‘A=ƒÑL)Pt¦SmâwçÂË ÔÁÝ]úy»xKc'c·‹¯Þ¸÷Í}‚6ÝÌ>Û\gÛîÆþïáŠÆìexhoB§®Š7ñÜ÷þÏó_ünpýï&ÛäÊžû Gºq3¸F»âô5úS\$G“¾ÜY¶'ÝŠõ_÷õ¼ïë -²$ƒ4žß¸„lêÿpZ³:D#bœŸ3.ŒÚhŒidÒ!=1d_´h›^¨²±#fP'NyèbAg±*ö%3l‚¹¢Í¼€9Ö` qJÜ#6˜Ã*â„8'Ö›´¤Jñó8z‚/ªdZx²Õe–žl¡]ÚZ¨¤Rq²:°‹; ‰Íú¸í+Ç¢Ñ8óT>«T"¦—N]ʇŠÐÐfâ‚8sžù\' &fÑØTr}ÔWè^Ô¹ikî„â¾%‹×cfò=]Ãò¹«Â0aDÛˆ#½Åë0 íq%õöåµM g%ÉÝq±"4PgXf’ó,l$1¤…Õ†„ 9AÎF×B¿ÁÜÃÀÐQÞx³¶Ù—A/…5±ÿhƒsßà¨rÕ6T". =Ñp‚| ¶¬…J©…ª¥3Ä1uÌ”YÅ~§õK(˜LÙ›s0goÊ|ÁÞœ…|ðVû.ªSÏ̘Í” œö,cÞ¿óžeO+\DVn¬òÊ!UÔpgxp¶-R„í¢"RÜ:/íÚ0°eÛk1MPõYaµK?ïbouì2лøªŽ£¿Žÿaâ‹Ôº\ö£çáqºãž8yœã÷ØØcœÉŽ0I*ʘ¥q ~šÕßÂ~ùñ›êþÏýÍLØÐâG˜n–U„¶’pª² ó…O™±ºî±ôHÏÂã’pÊò‚7ø×~ßø?÷«#FõX K·MŒåœ¡Õ¬÷ÜeÁbDÙw<€vœø¬ry‚±éÆÛƒ DТÓg))®H UQž H(ƒV™‡‹‘XzТ°@•¥ÿúÓö ÒHvlIв‡ ½aç¨öïym—zTjï\LMd>aÒh­{OŸÕÙªÚ]»îÍÌB¥"u­3ïD4ë1¨aXZ À¤Á–á¢˜Š‰jô,»õ»Å¼§æ²òF0\ʸ¯dþ©«,æ½^í1¹‰06¡z´dÃß <ˆî³'³Ú[%ñOAσ㽰îiÏiŽ©gÈ"1bþ‘ÄRCWÈÀ«pØ .2ÓFÀz¤ÏÄwa$¥²}î¬dX¥dËÓ0_ ôI®-®+—Ý`=˜¿2–Ç.+fŽZ–µpÖ°/¨a+Χ8ÏĆi9„¬'L‹gv¿~a~ôNbÑÔé™,²æÝªGÀ+<ÎDql–É51ÕO4”4Õ¥‚•lM<¾X¯l¦šòÎMål°Šf§A¿ãâ¥Ýd¿‹]¼Å±лøªŽÕGè_ÆÝØŒtÝuRø ýÍ,j{t… ØœNrûlîmǺÏÃ)éxH~¶Aæ9¢ƒŠõà3XÏ Š4C6y¸xŠqyÂßbyAB©,¥ ÀÆR'»É~»x‹c wñÕ'?ÍüòHÒ®—hÐÎÒ+ÀzÌܸÔ20êš †‹ Xi–¿ôåq‚=Æûò©UVBXòä{ÙlÒ•g¿â…wã[¼ÂƒÏptgžàÚ> éñKªšïü~Lø‰¿ ÓBó€NÐL9?àúCÄm~-9œ¡’Ó^õ°ry2ôÊØ_-f… ÇaŽèHÕND‹ÈÀd V!käíˆq³ØÈÚ2¤dÖiê|~ú­^RK¶Š×ˆ ›÷OÊþ]•='OF9Â/k§ªæœ©P{MtUõÄž°¢š›hÁ²¦Þ7"¢bˆ"£ g^ÜáÔ«„‹6š®µú ´u”YP‚á< ³Y†xl’¥ýl HJ{›æ^"ÚÙvQ‘Í‘Ž”–»$0h¨ÿü=÷èé>ñégÅìf]J©”7#výÕCjÅ{¤BÒû GåȧóÝæqž7´Ïk¤$µLi«4¼“ÍѨ |D¨ŒNÐ5~ý1Ø(h8¥œó”|­jTŽ©R%!–ˆNX´¥o˜ÖL-ãÏ|i9|=rªGWyxÓïfr [œã["ñÚ¯£Ž Ä†¶¦«Š&_Ä ]WólJžz’=É›kD6×ý`{#q£vGY‚’Xæ¥i7ZöN6šÜ­ÝL¿‹]¼Å±лøjø›Üÿ‹L~”é 3û8ÓÁK9éK§=~Ã%ØÈ6;Â÷µý6)IÁ8òoG 9á‘f Ö›£UŸ[ñÞ=žýz–g½Æ'?ÉÜz–ÃÞ¨:.–ì-øŽqx_úûüß÷ŠÈó%m;ʇ<ýÒf·´¤Ó¬Q—„çÆF»|ÓVæFY XÓ¹âjè24$­†AÀk8, xd &°‡ôèñ¤€z0ÊCC'›$tzÊ ë‹v[‘®)Öäs,€Ú}ÎÅæ`ê˜öwzÜH_×ÞKjDEÔ‰ ±³~)õLœ33q>:3CjjˆÙŸÊÙZ‚¨戯›Ü1)>ÓD¢Ã’OGsX ¢ÄXϨžaòâšÒDXIªÂ¶Wfº€¾üz}útw ™`-$Ñ ,‡¡é³_Ѽá§T5õ®Eí‰I*ñ–ù¼”ä`´–º­ Þ±¶ÁÔp IJŒ€¦Ç KWů[Ø^¨ŽÒt ŠhY2eOQÃN9ßÇÕ9÷ ˆÇùB—b”÷ïãþ‡XÞBë‘@½Äu`t·"¬ˆª£v,¯³¾‰UX"âk&ñ+ÆA%-¶ôøXØÈˆK“Š”5g>´›.á‘5Ë¥CVHG/„ÎèZ¼±ÓÜáêîÒDVwWj,.¯x­۫̃§Ïf*Í‚bÛÓRâo”–Øl$Û·n-–¥îC„¿ýOÍœ<ý™}É73ynÓ$-#ï÷Kr3éÕÃðÄŸáâ78ùEN~;ßÝÜvñ6Æ@ïâk#Ú¿ÊËðÌ_aQŒÖâ¹—Áˆ« IDATºWóÊßWö5¡¦‡¿Âú¶’ƒã$ëØ“­µo³ÝvŸ_ÂгòÁ‚¤=œ>{ÊsS®5Ìxò7ÎypÆ'ÿ1³)wÞà /pà8k¹õ4?ôoò?ÉoÞ¡óQ“åqÍÙ-ö^ÌDÌlÖí‰nCúp²uärÂpÈ2Ò ¡qÌJö—¬%’¹žš;&-‘Fôˆ®–v–®]FÉC®NÒŸûYwà Vˆá‹üD qÄ$I¨:ÉH¯± ØË’&XO@¨ŽM?ŠÌ*WWøJ”:¬ÖR¯L£‰a&ɾb>†ÞbTÁUf&"¨Z,â-.×™<ŸÞ]â–ížh‡¿~Ñ\¤ŽxÉ'"¶XD)Î2ËB‘'§ç»†öiü‹ëË„ÅÃ.låð­P‘mòzp#)ïA'±\‰F\#ÞÀ*Zª3$²ìh×ì)Úãß ê :UG"Q°ªlmÈ?ß´BÒI5{‘® Í’ê•_ ÔU|¤;·Œ®9+,âaYOçE#ÒÑ^p®Ô ;géiJîYb–ßôíõïçµïåô(Ð"[gUÄþ9ÃzÚâA˜æS5À¯A°Š˜Ö/?•;W”-'`Ãæ2JgÙH=ªVIYlf•íõ!Üié÷YÌþSüöÿ8þ“‘zæÕu†–F#Ú¬R'0ý¿…î?âѯòÚÜÝÜvñvÅ@ïâk&Ú¿ÊËOýE®ˆ¿ovËy3×n¹ÒxµéÐØÌ—BG0ú’\(B¸—rÕUù¶õ¨ß‹’ õ›wšÒ:Dx¸Æ nF#Ô‡<õn>ÇÙk¼ôÛÝå]ïä©÷P;žx?ôoñã?Å?ºWldž2ƃnk1ÄNýmnd:øXeÀò0£ç´®ìH}î#ÌPÛÛˆ´£ü‘é#‘çié'LXÞdúê”öT6òÏÈ–ô@~LUüô³w'ï{r}­(Eâ¸Ä<üLí`{Ú|Ïä‚Õz<¸@ìYGX!žFð†os—[´ à•P¼©C¾Ù„Ñ Éuã1ùšÙÝ7úøF¤ñ™ÚSin–Kå“¡5g£}ÃL2ÓŠzÂÔ€‘“ÍaF5¡¦—:©8C «ž®î}'OãÓöUô=Ñž¾ÅÖø5+£OšaË‹Û'†zÒǨ±¾w”z‘:¤-³­¬TÛrV(×r9(›'G•h£ÆÙARGWš‡ÙwÑþß3­ÿ“TïeúmL¿›éM\12uÛ&Vl'ÆaÛ&ŒJmÃzσ|7¯]‡‡»›Û.Þ–p»C°‹¯¡¿ÆÑ‡Ñ‡êÚ†´à®¸;¤Ðm =¾°íP8þluÅ¡øêGÆÏÄѽ$^y¿Û–“Q†Û~%fËëz%Ñ8Š,â¸h8÷Ø”'îpë9Ü÷^áî =Uͧyá]¼þyî>ÚΣ AyêM‡(ê³D2ô~ ¹tœ7VF‡'±uË6‹Ïê)Ç,5Äœr–™”dÓÏ¡§i?%‘G´¥y•ù稟"Aѱ Uæ ã0Oeå E„ ëc¨Ç9â>StåL GgNšûOÏÞ8˜ÐbÓZÅ4¶"&NÔÕ"ŠªÄ€@=çTTçq' *R‰Š‰õDUmœ ƲwOƒJðþte²êûîЩ«$ÛåÅ5’€WÂëçø¢£¶ª>~CˆÐY„.-ƒ›ÄæHMXõáëzøÎÓƒb1±˜—aY¼¹Ï‰x-zŠÆ¬œLD:HˆYˆJWÑ5„ j¡‰T‘:"Bí¨:æ"D%ÔP U‘Lñ€²\xþ‹Æ¢Œ(NPÉ;¸Bƒ4‚‹(hÍÄS®é'L&LkOUQUÔ &s|®+83â#:#\£žöÓs]ÜÃBK¨™´4‘·¦^¡ËHŸÚ#Ñ2¢¨«›å(éH©½ä‰³|Ê 0ï €îKÉ@Kؤ•¤ ¤tþBÁèlÍ6¦x œ}„óŸ{Û¦ÖÙÁíŸæöŸç©?Çí?ÁáâÚ Ìg™£uÉë”2k1šEÙfÓÙöþò¸)8þ,á7vw¶]¼-±лøÚ‹Ó¿ÄÑg‰ïÇÝÌ2¶W–Q3ßÀи„eý9R&Þâl°m×2þl‚7Ã-ä±:pƒ‘µ]Ê«g¸mš`¹—¸íÙ«aD‚h¦ìrëif×xð€Ï}‚Õ Ï<ÏûßÇÑK|ù8·²eG¸ÈÊq'°8EjÔmöi-<(\ ¿y\’Äï²Y´f âÄIÈEü´#ÓBö@ƒ4¯¢†$Z¬ÐSüëø×qÇÈM™Î Gâˆ„à –ÇxÆŒ9Dp…Œ›¬=H=wsHîÙ Žß„yÒq˜Èþ½'ç¯_›ˆˆô‚IlP:EAEµÆ9QÕ£™¨SDDÄ‹dY^)—ÛeuºX{[uÝŽV"Dé̇x°^>ÛItêsm]ZçÒ™]æ•äÿ4”½H`«az¯#®$'ö“0q‹Å­Îrqb©ÎÛÕ³7Ú1‘I7¾ññœ°"FbuÄ%ñ”¸B".æ6Pw +d´”eͺÁjBE¬èf¸Ò—e É5„^sæ»^±wŸÅñÏ{åBÑ£ЬŰϥ••âÚ óT‚® =aʤ›HƒkpVRÎgðõ»^£š‚¢“,^äú'¸ó1VS–{ÈÖ¸ mOìð=M¤†æ‰GÍ”“'!a\°äyi…•áŠÍzÄÀê"à8X®K. ÿo.üqv9n»¦ÛHÀn¸ä¼ö×hßZ(9ý0ÓäÖÇSÿ5·¿‡½9Ó¢íÃãÊScí ¹¢ƒtéývåƒÛâÙù™å,ÿ×Ý=moKì(»øšŒî§yí§y î|’ï©ZÛ©_®4.Q;âö„~É­w ÁÇàc¸|UÏîj :ÐZ’¯Máoô%ÇC±±HÂJoàh;ú–Ú=àzÜ„ù ®ß¡]rqŸ}”Û|ÿwóâÏr·ÛÈL§½{°à‰·]cMÔåV¶’ò~äç°9f)‘9Àý~0 „:¨°iÞ/dš*èêή7/5þµ>®»àÀ–î ëõ©ÌÌ®­Nö^°/–†Ë 9'žÁO‰‚®‰† 5¬ãFÎ;&íŽÉˆwÞ"{P%ôÓt!6øJÅ)D JS ˜83Ÿ`ˆ±[Q7âT¢E‹]ZTtF´ˆàÅtÕu!`Qj'•ð¨‹+ m R}ΪD1NmZEhÀ©vaçEñuí»éêÂ>Åm²G ÄD¶YåuTZ<ˆÑw¸uvLL?– †F$ÒQ©ª|̨¾Cˆ‚Fš%"Tùöð·ÂêãFÓó„Bs"lÄYanRM¦%‚€‹ˆâ’°rK'¬¦Éå”ók(Z£š–Z§‰æ-á!=nÌ"žÂí¿{ï¥õâðÉ+‚ÑEÂ×ÒwpF%T=ܤšÒ'X¼.#¼Év3”­Ï—g•}ìóŠ(^»n >2¾xb™äòZ}(4 œh_~‹&ÏùŸçðßgo–|ÆÖT®Ì”>1Iã’É”nä®·Þ7ÙŽ×cØ=ý¶ÝÝloWìô.¾¶ãµo൛<ó›Ü|:?³-‡¼ÓooöÒX£ã’£¡mßKÆÉï«_2@í°¹YÁd¤Ñ140ëQèjL žÐg­e1úÈEÀZhæ\¿É|B{ÄÉ˼÷¯~›ã’êV™VžŸP­‰’i#i☠§J.†Þ)9ºI•øq £dU—Ùꩯ:½ðîXÝÑíi­öÁº_­»ûöâ¹x:‰iËõ…ôÏØ½Âåh6õ"‚óYÑ"mEW0Š[aå¾~¯Á;:kÚ¸~òó§Žëן¼VyÌ£•“yMÛGATÕ‰«D\c†Ä,†Ø›«Ì‰˜e›—Ì$W/ÖÌjúÐ÷ÁRËÛ““Ê_hMTU{¸Ýü¢îgÁ‹æ%Yrö– ™Â†Á˜õ.,i^uB'™±<–…NÇã1éÎ?òKÏÞÿþÕrvƒ‹}ÊZ|ðF/Ä._bÙ 2Yƈªü­´§“L7®€ïi O´¡î98!¬3gƬ¬ãüâ@gÌZöÀwt‰°á²Màà8¾õÿPêPÔˆ?gb(¨GæÔ=ô„%«ë,*\@.àQYfNa>ÒG¤¢‰#é‹t…-¸-ÿ±åÿÃzAßz¤CÁAóÛÒPy’›S.gJïËØŽ0ÍàØ†Ã7òµF—ma]ÕÒ´+&ƒÛ=¿C‹øúço§Êù_àðO³˜æ.@®¨Ê!žîÊÎpE¡È®$d›ÝÁã$‡WÿÌî&¶‹·+vz»xÀËÏ`_àú;7Ö€Z²Rú83ÛÞ,®ÚzÛWÜWeòÂ6ý#5! Wm(Ý9k4ey÷GÀx¹euŠ÷ˆ°ß0õ8¸Äž=OY·8OU3»É§xá|ã/óW’0ÁU„”¥ãþ>{÷7=<Ãù+ÇaœÒf™ý¬åÕ˜ ©²ôš ÂUFÔ¾ª©^¨®×¦µ×>VÓÚíOÝÌ_XçÖ•OõÀæÏÈmì XC dtYò»wÐK3\n²3’IX¥p¹ñß­x‡!®ã‰{ñî¢×f±h8§§]°LJ Ð[5‰F"rDbpšv65šàDš RaS‡ÄeÅáÜž²ÂÝíD´®D;«©‘ M¢"”¬| `Ë¢­™þžh0ëýÍ÷V~}»UÍØ.5a¥?êæ ¿R¡ßÓýîß±¾)«F:O_!Ž>².^zuKÝRµ¸ð-®ƒžê¡AWˆa¡E Y¶•ÔØª—¶ôÄÀÒJZ2Ò;f©ç°É)åîÅaQÆHˆ\‹7¡§øŠê‡“"ÄX$˜µÁMp¼–Nìh:ACá.ÚHTR “ùà4¾úùÜ|ფÏÏFÝ*Y>ûìWL…e ¯òn¨'&•AA¢Ýj@Lê~›kœr]”)âr9jàCËö$qiþÿ{¸?Åô»8üXè¦bsI‚süضý\u¤I'K6ë¶à£Ï² £Ç£x=ú‘¡w¿S.WXƒyzOðœNBN^>!L=b¬;\EoØšªÉ}‡×|ïÅ”ÿægšÕ¸ˆ|¹áŸ}òR8ð’“¡l'›Ç[.£-—ñ*bšw!!›Co¡ ·×t"‡µ»hܱjô{§þæþr¿Y¿v4}õdvÞÕNÏÅc3ì!Ìs¹ œÓ÷9»­ŽØd=cJá;$Þ‡ +L6´ûˆÖòl§ÕÅqÕ~¶µ÷úêpª¹Äïp^» ""NˆÞªJÌQ3ó¨"1Œˆ5Þ±?uˆ]¬c¯­Š=3Ó°Œ¯ôxuÍÙ"bîº5ˆÏý‚ƒLÂв,‡:õüEĈJ{ƒúAøl¤ 70¬ ä8ÂмĮúüß÷Ÿï(x%šØ|zÖÓi_yúõÒ=KvìSX8jǤ§‰#_£ÓøZXùœ9OMƒ«RðˆÔTdÉiSÄS¹B¨ Óò_ê ‘G„sV&.£gI¼çíà¼^VÁI£¦u8Í’Ò2ZXéŸæ|ú‹ïß;°Ã›Ä@¯DaQÈG¦D0ïC'¨2qtÜëÁá„TØkÊ#}æCg÷o)§øÒì1ˆÙIVÌ׈pã¥F ~º™n~œë߯¤tEsE1îLŒ:zÆF+ì0š¾¸Ò™®L¡—&ÉKqIZTaöÏîô.Þ–Ø5îb%Δ³}šïʲmapã*iT¶S#\ÁÓEϲ-A=þ6w¹D&¹$,=zC®øGbElŠR²ÃEbä@¨„Êh"êð-ns]çh4 ïz7·'|ìÓø:ƒ¯3åÙŽ¦Ãr‹(gÊZ %[(¥½+5À º Y5B6*уŒBò7¦âQB=;:œ¿r8]îOÂþ4Üš:§‚ˆ ˆTbóöpv!Ò­ûºoïH×P#N³¹œUøžI—dPL !¥ðM•72³0¯#)—Áí?y6¿»V¹°8S×8ÄE稜ˆCUD"&ÉOA$Ñ­¥†z£J8ü "¡ªXµöò±™x¥Þ÷ ‰ó“³§AE¢°„‰õµ)â_ÈîlD¯å¢ôeJVäH)êÉ©—õCrJ5BŒô‰ì]”¡·ŠYJ2Pð†OºsÑ‚µK[/éVº<’³õÙ±®VP)cn,`"L¡ #ô !н++ÂÈÊEX".È4`3bÇé°1u¨Ž.˜„Âñ Å ÎáuÔ>èpŠo †Íh*0ÁÕhDà¢Ð6&¥e ¢KªØ¯£êR’Š)§_æ^wöêüæP—Ë®8M`™?¾_Ó'1;xØ‚¢©ñ 挷wÔž8ýX˜Á ÝöšÙ¶IÏãK;Qs.1%tD;ÎÏü~ôw3ïÉóp”ïÿ Oþ<ýç¸ñT^å^‚ËW»¨‡auUŠŽíöè«Å7Ùž9ßLS·çÏKïi…Ó¿¶»}íâ­]z»ÅÅŸá‹/qç/±?âù1êO·{Éy\®úÒã¡Ï&Œˆ<ÎÌå’–S©‚0ÒL¨ JåŽÞa†ô…aÒ´dõ8aa„0–ç™AYuÌ>ø=|ô£|ìEªŠ.Üm˜öhÈ}[@c4C»[Aðʈ=Ê@‹mì»1h¡Ch`‰Ãy–6¨û÷VrÔøGUu¬Î#õ¤ !Ƭ‹X¿?µY½>¨¿ð²«.ÂŽ=ìÁ5ìxØcåYœâÖ›L}ڞ܀WÉÍy»La5¬89›Ô‡ÎÌÏ*{%öO¨Sq01稽Äh‚„^´NXW‘“sØ–¸†Ðkˆµ".†³ 1wݯïO^ëâ~0†%á›`¨¡Zì´(@×0ÃZ0ÄA›i}EûÔÍÉg ’ š±­Gús @̶ÊêyHÙf°æL©äîA-2Ì•£R|i‹ R€¡"}Op8Éí…}D\±7BÍIÏ$k_€¾Úöå$[}‰òcsøš*-NY g7Ù›â=bpàˆÞaUi©ÛþòÁJ|xrÉiä;ûL×åο8:‚€ÖÈ Ì¨ ©øÀé;,bF𘱎Üíh­{}«ñ é¦lSRŒ2¸;¹’¾¥L Ï>Oü$/~Ãïh¢;ü[\ûשæÙ¦çÀþw±(”°¸=õÉ›(` ›ao"¨?æ=Ë~÷x¿Þì³z%ÕÍ6É_ zbwãÚÅÛ» ô.v±ñ#œ|˜óëÄoϵâ!l»6>.YrEèô±ÙÛFÏWßùfÖ\º ÐCö,¬…*â <ÁÓÁ¸ åµÜ_Žù„¨,¡©˜8êD±u˜Ç öTBí ‘½=^x?Å—îfÛç Ü®Q‹X"KÉFÒIXÀ1Ò~.Ò iÄmÐJª²çn¹²ØôŽ žµû¦oì5Ý­étêüÔë¬òóªòæ­ëðæf¡úB_½±nŸD=^í,;­D¡¹À¯ 'Árš¼#·Ê‰ß!£È^qKüìþ©/-§/×âj{ßq(Oí«‰YlRéÔ[„ë0TqÙéb0)ïeƒÎ´¾‹Ú¶¶:«bœ¬£ VÇ&ImÅ¢´ÙÛ蔤ˆH› PÒÆËÈ:~òÊk¬hq„MáÜFîéä\òöËCSËùÈ™ãÄ”ðà§Ô•å|p&C/Å&Ñè`„H̲¯f²1ËÇ7!æØr‰ è;¼‚Ï…Ša›†b†*NqïðžÊá'4S&L˜ì3ñÈœjFå3ô¸0í9_qîP_êJÉfÇá,d˜œY¾ÄÇ_ç·…—fOÿq©óUŸÑšâ yîp}Ÿ©rà˜ Ö1Ž©°P®y&JYF^ëYS$½ã¨k–ÒPÈ6ƒK¶ó²CøRgó`è]—/Whnqô:ÝÇ¿Òü¶÷_ò¾_â¹oåzÓs¸ñl¶h•m…ü”˜½tu=¯h?gѱl¶<îB/1ÄóŸÜݸvñÖÇ@ïb‹îïrú#<ü0g7‰ßÁ@j´ÐìJ"dÜ„þfÅʰ-{7~•+©k)•c½â½Rtš“`AP‚'{‰¼…ƒ&@+èkZ81Îç²rBH†)N™x¢Ñ·ŸàÉÛüü/@ ÂÒñ”gßCCçs®õ\FÜÁɆ³ÁˆÚ‘dD‹˜]yÁ$nô XdY7aqó³ûŸœÍOƒ·k•ß«j¯¢j¢TN›Ú!QYÚƒ6º*º}ëa`½`ÎÆ.zgýk…(&X:¼·`Š%Ý¥~ú©Û‹k‡•Îü¹ØêÝûz£qou)™:©4F5DTD0‘àÉŽãÉ‚&õU™•]ïNN'WE¬3b”‡«§ÂT˜€—vKd‘@®tˆË’äbh—“—YH»/éeG½¾éÎ^*Lè¶T¶ ïV0ô˜ÑÏàž”²eÓ·—èî^¨#.ŒÜ¡W:!Jæwš)3)oÛ)QóÏEa-˜æô|è8'CDUTA1—õ›ÆNýepeh‡ÐP2[ÐL©™ìQ×è y½\ .XÚž6ah£3zE}¶Œí\žý§øÅ–á|ÿÙ¨› ºUÁíaï@æsÄh‰XŸ»-«T˜qÒóbK›4;$+œnƒeÜÿpi6Ç™]Ëô Xå¢ ýMŽÿÛ7ÖÞc¼ûrm”Ø£©‰7™»ØVŽ»ôÍ]8o¼~þÜI÷e÷uO†ýù)´ µÚ¥ýHKk#Îy’åíc&$ø€7TEM]Û»G'NE•h¶ŠöÆ#ˆœ eA7‡h,•…ÊÈ)Æ!iaUŽ$hŸk$¦¨úúõϧ"ˆûQM}È@3hmçÇ9mXúÖÒ°M'?I'™³Q)µ§Ji`“\·X+Ác.ƒæÖªÌŽ1¥M4‡9¢Ðú|†E‰‘µÇy|C ŠÔø¢—™ö’±µS49 z|MÕà¼Ç-¨Ú  ÎÁúbæ¶ä±vÁ2ŒØÑ V=' {®X‘—=šã/}’Ÿu¬ã›wþÔbJ%4ÆÔh ®éŸ£#Ó9µ FèQË–æIÃ;Ì—³HÛgI;IÒ“eý#ñ™K’ðãÙ@¯81 /ùGË`]^]®yøc™[îü|Ãs ª‘a· yÇ?-W’Ðã9ê­">nf‹Ûo·”<–Ë¡W m¹ô|÷þåÝmjoKì8лØÅï âßáÞsÜÛãðç8ü^æ…)xé~·6tD\þEÏCŒÍ¯æŸtóq#m¸m¤n®ˆ2õ&å<°Pp9aø0r&%Oˆ±2Ôh Ìf|û÷ò?ÿ*^èáx4ÕÚªf:cÙ#ø%f9]9l‹–2|zJ(©bh,I¿y ÖÐÁæY¯n­Ûë®y®jM®½ôÜÞ²v’»Íbèֱ酪VuåWÚ#GëÓ01¬ßû§ˆØ:zωqØ +œ#–¼£uÄZÌQ Íë¸ßú¢¾z$`ôà ¦tê'ñ[¦§×ªŸý—®ùûô_÷Ò““Õ5tæÄcÚ%ÒdiãÔ3Gj"¬°->±Ó“£¥C'R]ø ‹(rظ.Æ ÑµNåe¬1¹i ôÎî·ÒpE$ÂŦȼ ‡ øzÙÁ»9þ|!õÆ¡9u n¤”Ñ6f‘Iº,[’”$rÚp‚*34ç ApÉäÛ‘\¡…ˆé†u¬Ø'器䓭T=}{’ötj˜TÁy*ÁM˜ÔTuƒw¸ •ÃÕHƒvŽÿ' ¦§]ºÏo xØ^ ×Ð$LØcý<«=Ú€I&¦ôÁÖ’ùBG˜Hñ¡hM$½!tÛ–.ÛÀUß {\ö×QHãÛÊn :¿<ŸÔÿïú)ž€zDnkQó&S]ÉÛãþ·+\!`ŒŸte~»šiþ Šø9e4Ìw±‹·Ä#8ünˆ 0º‡]•´î.ý›¸\б#÷ø«läã] ª …C£œ#Yu书r«üê9XÇD2®‰Æ£ŽÃ˜ÌÌXjÔŒgê> IDATßÇú¿üi´!¡Î”ˆ 3¸˜Ðîá.°St="oè†2¦2€ëP¼ Óž®à‚¬ØÂ¦pû³Ì^lÝùêoõšó®qâU‡Ä°^÷«Uèzï|ø’}Ùí}éÑùSr ‰ðy¤æ¸f®T¡Ãuô B'ÔFkLh^½§'¯¤¤+Cb2žëM?v>Áäõ׿Õîo|ã?·þ¦o^Îd½Àn@‡=ÂZ$e"b! Ž©¡=R”èÔp.h­² †!È¡óOWœóæ°>Ê‹§M-ñš3Å.°sªç¥È³1+ú¤,Û’wL‹@_ÑÞz¾>þ”Q_ñºÜ SÃ^ ¹/µ÷™C  =ûYp,›e{ð8´e$`ت¬ —ÐQòb*u &C+äôª ^{>þ–uÃŒ¬¼œÔî(®ŠQpkÚ*ÑY`ŠopŠÔ¨B»&#ÎÔ¶zžüº@]ÓF–g|¹åhÆ­†ƒžå’û§¼è_æåšgA=×Âù1vy- §O³: Fð¨c"4‚/—­•8íyÐ!ƺ-‹Æ]¤6¶šñŠã÷f°muKÝnÚ»T9à©»2­¼ë˜'ö7 þ¸M>fÔGêFß6Ø»ôÛ¿"Wðîc‘n|œ¤ýð`0U½:õ u›ÖòXpŸ6æìWv·¥]¼]±£pìbÿä±ú Ž¿ˆü±,Ž%_‘°8¾/~å^Ù€Ý 8¾”–My{Lí6÷Ú”™ÎPgpûs9רث™:¢²®¨•FpʤÎwíÆQ9&3®íó¿ÿŸÌ¦¬žUrY‚ QX{¤R#–%Š!ó;¾Œ²Ó¢%…–žI mñý^ rS–ï¡ý†Z®¿kÿÄkˆT©SÁœ ‘ÐYÌkûíÑÉÅó˜à÷×'ŸrÓêîÆá9ôY!.Æ’]T¨°í‘íF|D×åš®õѤ1#Ó•í³Ò_¿ŽçN&P›öH'z æ%ŽZ0–E\%‚a!§R]¤~í3à…Ø°lûo2±–ü«ƒ“?)ugIûÂE–K,×$a‰ ƒù eÕµ~ Äì,’³ ¶ gÚ±®q… ¤‰©\á=~BSSM¨æLöhæÔ @OqQä>þ´¬»Âq8æ•–SE¬Îy`£_òÆš“G¼tÏ<à‹g<ºàøœ‹È:ù­?²¸–sïgïàøZ^€Åš8áÆ„&¢z$’Îg8í8í¸¯X%B+b†lçhËq ׋/Kºœ«‘zÝ0 ôåÏG_à¨hº=ýy÷óL˪µuì6ü(iÏv#‡]qTåÍWþ_Á.ê’F‡•bÝ8í®|áWVêHyýWþsÂoìîH»x[b w±‹ßUÄÄé‡9õ4ßKµ ”¯*=]eYpÅwàZ Æ~”`f¤­›2 éôþfdUè²(••>©½ª9WÔ1©‘о"€P*— #œ£V ¦û¼ü îŸÓ O¬ˆ=' 8UVJL$Ý)2‡)B¦ê]Z[¶Þi¯u”fsÅ–<ÝÈgyË¥¢ÕÉ;oÜ¿¾óÆWâ¨Å“Ÿ=N郙Ἥãùƒå„öi¢ã‚x1]SwÄP2zJÓ3ÿRç¾–@d ô‚•N2)€X‹¸DÊ›… º_vŸþdzƒ?zq}vp¦Èë³Ò´¸âu×¢+œ 5.¢-¬LB`½ö•º˜$3L.–R#A⚀ɺ“S£1DÕöðN,ûàIŠ—;Å,:µþyšG½¶'`@gûE) ½RºÈ5½§0³êŰnÔ²ÇÐ jš]ÞSk`_:MB?t BH-’W.úòÓ6äB5K,ša=m…óxòFªåWdBµ`:¥žQÕø ­P8ä„úÕ²sÐÓ9:–=ë¤àѳZqéAÕšÓ¼ÜÓ;œâ [Ñ&s¤;¸ñ¡ƒD8~‚×o”h¡w„Š›5MDcYÀ .:ŽZBäÑš£u¹ÒycLoŠ“§`¤Êè²u£2WÜnÑKߟ*Gð…›øy€÷¬y×3›õs{e%oyQ}™06‰ÓºÚ<ͶžÆcfË+ÏØ¶¨<.'}ɺ…+oháõew/ÚÅÛ; Ç.vñÿ!V–ÏÿY~ž›ÿ<Óíž¿+þi[ÿ_É(o`4ÛòϺ­m'Sn³"›rº„r›Ü/üãœ9> ïRžjPGthÅl–Á£ ÞÓBÀOyö>þ ¸ * OgÌàDŠÂFÌas8qW$÷DÊ šÑAè³K¹8,Ž`ñ«{ÐU5g‡:™¡«U|åû€7s•gǧÖÚäp¯Û¿ö©£Õs²~Š94È91pfÜМÂs8¸K8¿èpg]Nç&JnÊã·ç#ãh‘Ì!‰×Yÿ—?}Ýÿ7þÀa²ÑÐñœ!Ùèµôyi“œ¿­MË 5…y­ÀEˆˆ9ýØ{Ó˜Û²ô¾ë÷¬aï}Îy§;ß[ÕU]Õ®ìîNÜ8vÚD$"óÁ‰ £ dŠA€|e°AŽˆQоX|HÀ 8Èq°ñ·MÛn÷PíîêšnÝ[w|‡3í½Özø°Ö:gŸûÞr:B¢ ù»pl¸2µžGó׎ßÅÁÏl´vÈÝF±bÜLÒõã§ô_™Ÿ½€GŽázzX¼`…d8x`X5´J¬6nàDß³P¢ªV²}6§~»ðÙo~^®Nÿ›§Ÿ[8&Ø ®ÃyLňë=üYu*U1™7;®Ê’CÏ[q Ñä Vt`!ølÔQHÄL«34–™0_]áycŠ…NIÊ2qj‘6áÖàI XÞêyë1ŸµoSÿ!!¦cŦW=þ§cSÏ\~‡Jáøæ¿WÔóWn€q6m€ÙmE³K”K—ºã—G¹ôðÍÖºjV‘KàêËS›o˜ºøàä”ñ/ó»»ÿ—÷§ }}k/ ÷µ¯”ºþ“t7¹ñç™Þ ÃÚýí` æ°„ùh˜Fêêrd'ÏPOåõ¶«gÄ4£¦úÀ¦þäS]m‘×z€t•¿1A4UÝœP32Pö˜€i9h84¨á^ji2ÝY™¶4Ž”H£Œ_þfÊ‘ÿFÙæTÐÎŽ,gãxÒÂ!²‚sd^v…èhÑÖŒ¬ùOK…Ç9à —GÝ:6!=é†;ÆÓÌJ<ž›;æN÷xwCç»kןÇÿôâ;ž¡ÊB9p\=ÃߣÇXŒÅ)Ò1 Øžy"&Bϼvd͸S§U_j™"KïþmûsñÊŸþ¯|êÖü#4“rȲ$ypèj$¦ѤšT5 ª5³&% Qm¢ CœÇAÃóµe¸¦Ã TEVtS¤#!æ‘#RBF‘€&â„çaÉØÄ©èî|«V˜ÝF¶Žõš¶{`lHJH‚µX†$»žÙš2ˆŒûu$ 7É$‰”ç5éY­X4R!zãq“óÛb]AÈ”wqJû úžlùöôfûBsKrŤÀ’sCëhò ¬k ·*’H‚³¶Þ¹û•‰e8ƒ(xa’SèMµ“ŒaÊ$i1Täþh]9Ç]«Æ˜ä³ùâlDó¸÷̨áz¹Y»‚oü ó¿ ð½ÊöFòtóéŽAŒsµ·ÿð1A.Y¢Ç>æXMèõ—.›õS9ÈBWˆR©6ô¸ãrºÊX¾¯àâ?ØŸ‘öõ¬½€Þ×¾¾½zåoð=‰«0…i=Óä“\î¹zXÕ"R¹w­míÊôuqÖÖ“¢cË¥mF¢ÙÖ±BÍúÊðb®)4¥‡F³"…2 Ì8œE,ÃAžÉ: š†éoYZa˜’áÀeP$a£„ÀÕ;|öEÞ{\r,Ø~VðÊá¼B†533yGè.leHÅL"vò!h&L[è‘Üj]#OX ÜþøôðÞŒ¹2œ³œ1mŠ%ׂܯœÊõYóäI{nÖ‹ƒ{‹áv91G$ÑGæŽ+¾Á£$K7pš‚xºÅš9eú H‰H$—ÞsU Ľ÷óæY_?ýÙ‡r{~¿Fz¤+oQ]a.ÔQdia4Ö¬*ª‚ .M6¡*¢'^­è:åÏp!þ‹ªŸ‘pU1 ºŽK8@.P[È›+СL¨[>Òÿ¬5;_ïȳ|]©¿ÌØ,U5Ó´e«e¨‹ýd¶æé(#&p ¬OžvåOJÄ,8ó¶/8O¤^q†Æã Ö!S|ƒíp-¦A:Œ­Æþ5ö=¬©\u05Ié¸÷ÊBˆÞ1ÉæDèY’ûк#m"&–£Àyà<4@…S¡¤+††ÅªÒ 3.Û` Ö2Z\ÚÀ7{7ŽúÁéé¹&æ”ÛϤgÛÑc©7 œÁ7ÿÿ]€Ï)·j8°e·ÑFö’ø¼–3Ï»-Ïck\ÎU¹¼/ôTÓ4u»3”Z'Ù}9.=ùøO_ØŸ”öõ­½€Þ×¾¾úü¿Ä§_ä<×' 긪, ‹<ÊdX)ïÔ“â•W8ŸpNGídŠÙ·œ8í¨?éÊ0×Ö =êÔn—¨7tŽ\×b„Îq£Aš"[¯5´ R6lÖàpˆÁYZGt˜,íUÄpâ™yZ)ˆ`+ÄÄ:@dÖÜ^ÑÆ–49©(2±Dh`&ÌeÛr,d½Ìè˜"ǰ€59ë„Pûâc`¬úO"¯ýÓæèµÜú­—çžxHR¤)ŠÊzo—ËØu¶íäq/´in³¬W)©ðzß/ÿ&ƒ³L{(!{=ÍÀj³Ø¬ µ‰–»§:+Œ‚móÿÕþÒÏ|túÓï®21%x[ä{mÖU)Ÿ¹oÌšQŒŠ›’¨Ã7„¸LI0˜Íïç%ÜÈ›"y]¾[ò eDó žtp[.Þ¦€Ÿ \v€ZW „xp£Y´63?Î I~žÜ\Ï85Û`úüZSªÖs\[¦u‚¢èÖ}Ÿq`h™*êm[Ô³í°-ÆÕKżA»¬ß’€ÚÒ8,¢2WBFbÌ=Ózõ§=‹–ƒ<5Xµ¾Ù ±ö<ެ„…„˜À° p^=»ž‹¡¼Ÿò54Žóœ…™wæ<:¶Ë‹Ä]æñ…‘F§gJF­èͬÅ;ðÅ"ý Àç”Û@Õ¦cz©ê™úí[}ôåç&is©?ý\Kư}È3„: #¾PÜÝéƒIùãÉ šû?±?/íë;[{½¯}}õ™¯s㿽bßøôÉ¿ýÒ NÑ’wÀ·z¸î8ô|+ð•¨^ô剆«öÉ Ü¿Ëú-Xâ­¡µ<Îñ¶j ƒµÌZ† `!Oé«À(!yKã¸â‰†Á`„;¦ž‡Çž[SÏ<²VŽ“–¹¢B#\ñ¬¤´™<ƲŒh„‰£I«¥C8 \œòjDšjæ£é@ˆŽFPa‘!Ó»´bAq°‚e]êíÊÚ®ê–W¯"4ÏvЗnœ}é›Ì__¥ïïx…˜IzÛÁ‰wá0-zrìï_Ø$QfpZ Úc”ÙÛoŸÇ#^­­—Ž®ãdÅ£Ül6XXkáÝy—¹Ÿª…,¢?ôÅ_þÎ+'?qjY SÁDâSxÅ(FXG›áÄ*V=d%#Å&ƒÕd’Dtæ…(ë(YÒ~ ù4áN6ù uÑ£G""ˆ­ŠÕ’Ž>î.ÞHE˜dÝX~?·”è­ËTб}¶*D[ž>ID¶R!!Æ)© Q¶øïq·H©8ê:Ú²$RÞçõþ¶ÁMé<Îc'ø ®Åµ˜zÉ æ &O¦¼Öm¼ó ýÈ)‰80÷Ì´,œ¤ÀÊ3 LÁ$B`¡¬"çRSÛÚ<‚E$Hœ÷¤Ì±x_.S¼ÂTù8î=?Q6õÜ«#&»f†Ë ?jog·ñ±¨çìÜØL 7»L©³ŒKX×&±ìNepÉ=þåèSüÃtö†ñ?€g—×8ÆïÂì¾î?4«5ßùì·H¿µ?/íë;[{½¯}}`‰ü8Ÿ8ЗÿÅk×o¿8;úØ•7§tÂÄð0²c˜Y>ê¸7$¯±Ã¾æå†Õo ñDëåw°wìƒ×š³w™¿G“°ž‘o­Yiéþuž‰CЊ§Sƒw\™’ Yœá¸¡±<ˆp¥á°á~À G-ËHJÜh¹ÒðÌAd™¥°ÌSéˆM ½¢ gé\Ñ/c¤ëì<îñî=>3ÜÇåöf¬ÿ~$ˆÅó›ÑKGl0¦rƒëÈ¿´ÐÂæÈbkÍ… ÌF1ÕëZÎÄÚ¸¶µÝûÌÏX7øÜ´L6šÔð×Óãéð^òîµåEË•ûÌ}c`x5 Á1i9YñH ÖáÃVCSg#µjh«»€\‡<ý7ä·>óø{ïZÖ–öó)aDP±ôk‰ FÔ8M¢V˜ˆI1ŠÍò%I±[[‘#ãÎ%ƒ]Ñ}‰Þ±¸*¡0/d OKÆJÎrWAñhfï®!¦bÒ@¶Dgª)%_•l‘Vf\eqhkä„B[õnŒ¥û™EsЍAM‘éš5t}Ú e0q—vV›ÜÆa¡¢ÃS ¶øü ß`[ÄTõ0wiÎq› ‚LÞH rbÐ’¾™G´J ,RÙ< ô5¨gp ‰eäqbj9Ô|É·„ÐCC!íxV–y¤i Îâ^=áhÂïEÞ|·ú€Ë¿#õ¿æ®ÎuùÛž' ó£Z˜Ôm8ƒ×ßâô˜«“W¼ôž}1t•V{îC_ÀÓ|A1²A§ç¹DÆõ‡ü]ܬ7Œ 'Õ ½sß9=Ÿ/ »uw7FxúßíOOûúŽ×^@ïk_;%‡ÿ‰ûø?ÉÍÆÞ^'ppÀg®pËÓù$œ'†[ ÷ pÍq³1ƒ¡qêœù˜—O…& ½s⠢ɟ¿üBc–íýoðΛ´†W:î÷< t 37 ¼`˜9ZKgX)]Cçð–§=Leh 'žÎ 7;fUBâªç¸¬õ´hè@‰ç˜ÀH cèLųE¬Ç’2$ŒÁ9-½Ç{oò©D£¸5m–kT •ÛÎÚ…ˆ±xA•>V–êkÇ éª<…ÝP‰ôÃÜž´Xð¡,d3ºÕ|ìf$.÷8[¯ÿ;&ýõ>ûÇÞ~…RÖÉ VÄ;iœQ$YéW‚hŠš")¨\2V5¨Š*ª>šVÓŽYÛÕiTQE.h¾ æÚɆr(5™D#<@– „ëxBõ×äßËv\´²#UvÚ€BEOC^¨}Ò åH®Äwç­Ñß¹¿;Πa™­Òµ;Nɽ|“ /9Lƒ=¤ÐxlSH-š ï`иÆClJœMˬ˜?寗YÝŒþpv¤ã"¡6‚5‡;®Â•†#CP x7´HÌ «ÄzÀ `åÅÍK„5OÎXô°Þu(™]+óؽiuËè#’ÑTqWws‘^~™×þ+¦u˜0Àûð,ª©ÚŒÄ븞‘§ŒÞìscSŸÛ‡N»P¹Ú„–Kƒ€f¼ v´ä’D>`Nßaù_ìOUûúŽ×^@ïk_¼ôïó±åöÈ fˆ/¢`P^œòR—µòu§O½…ȉá¸aÚÒZ¼ÁZ’7Æb-",D‚±Æ*†óêA×z•Ë;×^ý¾Á»w¹Õcq‚­Ó÷¹k›1`b1„k9(Ù ‚k¸¥å÷"Üìp¶ÐŽ=Þ–»‹wE¶·x‹uˆãÀ‚à\‰ 8t*LÀ"Êr:O뉊1¤Äù^´h³"vȦûн2 ¢ %(;Å:&”Í÷À¼‹7ØüB^‘޶gÄÊ"Ê0°j˜fàôгޣŒ®SR$ºbk)ñŽ›mL¨¥ìš3sr6«"›4¡ÇäFe>Ð5˜ì8€K| nz.bq–%*óXíR©aRaR]¥9¨f C ‰8 Â)<‚§ç(‡‘Z}æ¬~9Ñiü3þ”ìôLÎË3*\vÿŸwçÌÀÙÜ7ÕyU è¸Këû D•ñ/ç¬ÿæþ¬µ¯Cíô¾þÈ×êm,\À)†àèX{Öˆ$5S÷ØÈµ¬éu:;ŸÚ;¢É*’–¨ŠÃ8±ˆÙoEÕ¨1ˆÉÊÅXcœ'¨jÄûÕ˯…_9¸õM^ÿ}Nϰ#´ Cî’9Z˺ßJ¡Öã*˜ ˨8ƒB*3\’A :škOhÂ"Ä„ÉqBHĈ±•»›ˆ±xSÓ5š8}£/pr„ $_Of=ÚŒ†}µf¢až$ôËȘàP©MègªúQ;j3}(57ÄÖù§C¬ðúp%úÉçè`A˜³¾Ât†»ÐÁ†5nÎÑ·¾œu¡©3s‰eä}ËMQGh½§ƒ“%•([G)Ùaö> ­Í»ÜÿGïüÊpç¿ø¯¾ðÅ.ÛfÑEDט$Òa¼J,Hf#ĤÖçE‘(P5ˆj×°LHÑ ×ZÇ gŒ2x†®ì7A² R¥¸¨‡«/êÂ? IDATãßOõ(2JXØ ÕÛMýëV ™"êrÖ  )¢‚ú¯­‰dÊΔŠéÈ;G>„¶ÙY%§°"Î;C¿b$šE Ïþü5ó%‡Û€4!KôòˆÆâL ðÐ5ë'¤žÐÐÖËYr*uº1¡KÎ&Ú2¨Á‚`7hrÊ‘è¡X@¤;.vï;y“8px¤XX'ÖÇ cˆÂý÷8ëë’xÃÌVðµ'†„‰J0è=ñ ²äeÔ‡Î!)kÈðï3x ¡’æ»Ú“'7=7²ä™°îçÚ06O£°}ÞÆÏöŒycƒçËW¡ÈówÉŽ£Ø"TvbÇOÅ®«doýðþ”µ¯Iíô¾þÈ×üïp÷oóê_à´üûž&,] xÖ‚¶n`Àt 6.±-’DUD• yº*« 5"*¹(ŠZ—œSASJ€¯É»fñ]ŸMWï¼ñ^ÿ*)â ­CckÖb!¡mðÙá ˆ¡µô±´œe¨fR#…¾›;‡Ö– jÍdº ;P¬ÉÖt%íopˆ‚¿àzæ/¤ºÜ,‘µQ©5ßB‰‘Þ31`XStÀ6 1§×èom‘Ý(ÄasÊ´0A}=ÝÏ«à ±ù²oÈg˜ß@¦ ‡¸GØ@4~!ép½àÎCüümeB½Ö \M¼ã¸iÁâCÁL"‹TüÐÇ+ž(Iv Ð[é¡;4å\K´ÊúîëæÖã¿ð}gw~óÅ+Ë—¦ÆõmP×H3UãSîñ'‰*V›–¨H$©¢3±f5ÄÉW×3ÁœE/ëÑÜŠFéëõ’)$yÂÁKòøwkÂ4—HcÏ ùAu­PâE"ˆ«ú(€¬¡óÔ e"PØî„gH åF×IKP{ÈH .¤¬‚X‹U´gY¶%4 O·aM×âkŒ¢f(ÊÀ0°Îÿ»fiè!æ'\ð¤å°¢TÒ’³–ƒÍvE‚)·¥:OlÏYd‘8õ³OG7"%q«¤/V¼ë1-9Sþ¸ášÐ9läýG`·Ú/Õ¼GS]#FiáhŠ‚0¹‚óÌá^âéÀ: ÊeÝq=<­à¹Ìë3úê‡N»èe¹ô%ºLÛ0ÐŽˆqþ‘Ô,U.eÀ3zÂñm)Œ R›´-i`½ÑÙ©Šû¼Ì”téÉÇ/—»ìïý$é×÷§¬}}Hj/ ÷µ/xýG9¹Çá-fHƒRÇÒ·†¡u«¶OÎ[1„x–¤„ŸŠM"’…°Í\Ûèj H´N IR"¡ÖÉPµšìÕÇWn¿Ì»oðà)ÒXQ:Ï‘Äâ çCéL‹°^2 „€·„"8‹¬Å·`˜N±Žv†õtš ÎÑLq¦0{ÁJIvÎQj)aíz'(¾4ÎY–mFà–Œ‘+¤lY¶ˆ­~œˆR†·Z¹ÖØ5~ÙÒ±¼=âš’œåæ±–Á¾xÎÅÀÒ`/ˆ÷Ðs:¥mð+`Ê`*iÀ…+È”>±òt€£‰ ‘hhêKËPçR³»SmÉç°ÉØsY*kG#³[ÑŽ"ú¨4wSÌЧ¸ˆŸá ÞáÉ÷Þä윉Ãg”T#¹–—i§1; ™L¢LŠ œ æ‰ûk´¯šø æ°†Uùˆ·DŽ:KºõL˨{9ý{ó!y˜Ug»ºQœÇÏvGŸ‰æ7¿Ç ØìRê6¶ìçòžÓ°©.‘û‹ùOíOVûúðÔ^@ïk_¼õ3\ýϸçeý19¦õÈôxðjED’ÉXâkbÆ%ˆz‹m%@â*Åó´¹qb½¨HÔkŒ1‚ÚÜ‘5'Ö¦W_>ú±öé#¾õuÞ~¥Ä+ž¼ÃÙcVk´ )f£³É³WÕimd4îox’ °¦ô¡³‘º0™a ¶ÁX¬cv‚kèŽ0†Á`§ˆ+ , ¤5‹ßD&:礠¾Ê£±†^¢ t%&nX:3amèk‚ŒÔåì²vÞTä0ak'÷‘—ƒÇK­rhX¾C‚É-âm–‰t{6L½ûù]nþÎ{fù®âu[”˜ Ær×ñ¢Å6t+J4xÐHŸˆ †vàŒ­Ú­þвÌPö´0²o쓟zðÖúÕ?ö§›—å¨]ö½ë{bÒ©7·fV“ ççéÉi쇜܈&IW½6b¬ADQ•ÄS¾&Cû^Iìn’zš&ÙŠø+ñèÀÑ Þ i¤’t ’ù¡¥þlšÊJ*G9ÿ ‚«ˆ˒ƸdÜE¬-ò2eh0Ÿ‡KSu'æ 7 ¬YU˜´&äù2÷Ž.h"]c‚ÁEz‹x¼Á¬yj0ž¸`n°žNA0=½ÐTï ±°›+Åù’ߣ«^1L G–°º>£+c¥^k³ùޱçj‹q<8åÑ{œ/h,kè/%*Ý„ÃމBÓá F«X¾Î!ò~`XERB7 ,,}Aò1.+æ8¾ }ƒžùpd÷Ì/#ßÛ7U¾ƒýó:ÍÏ<á?tôð™ˆAS{Ϻ;PÀȵb>Àoýô<úö§©}}¨j/ ÷µ/îý4oÿ³\ÿSÀ¢är§Ž‡4ké` F0¤S%l4fRÄæ¡§Ó 4FD“Ѝ€I½QUQD4ã2TŒ ¨Zc×7^ì¯Ü9üØ÷ðöð/sÿ.§OèWX‡±%+y¾\¸ z%u€ÌTÝljÛÉä –õšÕ ɳ9Ðù­:Þ—Ç Ò`q†tÆÅ;˜´1¸„‹Ø<h·&È,‘²ZSiEK$*ʱ¡]ÃÀà EAºÚ×5™¬êÊsæ'Ñ íËms¿•eÃ0åÌ.¾Jx \ãôñã¢ÿ"/Aýéï%¶êysƒHZsnxSy5·ÈV‘HVœ& ÊPy/z]¡ŒIm‚ŒD²!Af?ýæ2~â›?öÃñÚ¯Ä'‚#¡çé™>yJLÙã#Zi1± :Å[Œ¨#}*x¸üäÒ_aq)q72 ‹ CÔ‘ØÜ¿ö‰Uw° u[ëjY1R>ØÜ¦5ñCq‡Ì.ëXwÛži´Ï))-åEZÅ SÏD°ªµû¶Áz[K¥X/ÁFÖÆã•Y „N,>֜ɧˮþÀàŠsˆp«fŠ˜ˆµ/n´Fƒ .p±¾ X!Àjk„È|\ ƒ„G ¾tÎÅ&\vÖŸ¡evÈì™±² 0() Jéƒb“X­¹»¥jo¸4Õñ\?$¨Þâg–C쮢µ‚±yàåÀ”ü¥ë/-l ýüSù ]NŸ÷WûWF¯¸©t‰"—°›ŽõyâîíÏQûú°Õ^@ïk_µ¾öòÊ/rˆ,Á#ÖhÃãùµ—|Ñ!¢)ת­Ï½cm4E#ÑA5 ƒÐPÒ%‚*b<ª’bHÕ^҇ХhnÚóSú5§÷é,·n0iyôù» }@¶jRØš¡Ë SoŒn?ƒÐ„©ü;¡’ìÊsc…”‹·Y´¨!å…s`•&áÁ&ÜPCU²«TÐu9ùeoÉr ;Ä·H$¬j✇d†ñP-ÑjXó8$ YØLϰªj#f¬ -sEÈ#Ì„4ãÎéW?qëW™¼ý‹J;†B˜Ñ¬ Ðs·á¶`ÚÀRQÁzšÀ¢g^«¢[3BN"ˆÙÄ–k &¯}y@&ÿù×Þ\¾x÷G>õêw¿ñ’>l5!ê“óôäLª’YÞªªZgÍBÒAUD#ê "²NIÈÙ‡,_‹é‘VXÔkƒ0Ã1IQ}†­[cœn‘ƒµóþ‘T¢‹Õ8‘ Æn‘i ¼ÓKÞØTPÐ…ØW£€laQ$>’–ô3K£ÅXäòµŠ¥1Xƒ‘­KŸ ŠN ¢t©À´fžÑuJZrJIõ!jp‚„X@‹²y¿›ù;% Æà³ fê¹qƒ]ÀÓ¡<ØÌ¦Ìö­’˜0›a=÷gxÇÞ8]•¤!&q52¯™|ý@èG“‚}íþöð¨Þ~Æ1>Ÿ™öÛ4¡7ZwóÍÈŒqYCoäoG޽Üþ^ž7˜øÜ&ô¸¾±—¸Ñí\iWÖ?Cöؼ5¼ó¯~s‚Úׇ­öz_ûªþßøy¹ñgi‘´` çñ|œ5Äx&“D4&1“ˆ±y¤.#&ª¢" ‚ECB0.g*“¥³–<¸°^MÞù&Ë9CX’b,'7¹öBÑ”çÜýß|ùgË»ÈޝQGèÒ£­¿ÉPß"£7/úäÙE÷Œ"³©†ï °Rì½¢…[6+KòD_ÈVñÐ$\Â%ìøy ‹€.™Úb öÄ:ƒˆºâÞ´ÒÕúº¬µƒH4Ñ,<ìSN@;æ è/à ?b¿Éôÿþ,1¤`7x’íЛ@by ÜÉ€Ä:›f‰0Ðç3ü¥¬Ûåg½d Õgº»Š*¦ûëï¦ß˜}åߺýîð‰éÙ„þé<>™›d€¨d´V1¡ š‚¦HRÑ”0¢ÖЊ$z/’<’û`…¤úò±¨ÒwLn|Äg¡}³Úa"©Ú?ÐëvGIxl›ŸÊÀÙæai1¶Ø¬3 1S£¢‘u¶Põ¬«|mسt¥1+J\òtÂI6o$R¢w4Ù÷,(CN€´R’=róÛ&BÊ –È­ôˆ C²2²ô@‘­ÉÇp¦X¥s ÜN¬.ÐÄå¢R5eÝ×o\,ѾêËÜ6ŽpVsXžéËŽû¸›ûëˆi³ù<š΢5ž0ß»™s?{xž†žÔÑ=ÝùŒŸ¸<ïå9ÇsMñÏyNF&éïÿýÏíÏNûú–Ýï‚}ík[ÿ–|äßl®Ì$w_°×:óÚ5œÇ´Î¸'iyS<¦Û&IPÄMŒqŠX!ÅAÀˆÉ æÌŒ1¶F‹CÄX1.œ?mÞ¿ ¦ø4¬ÃZ$óȺ)7_äê5ú%12 ¥¯ó—yáŸçÜäï}Îñ¯É|Öž™1¨šZoD“ÔˆX#“*êÅä%cBtâ<’’¢IĈŠÆdÅî+Ë6#NFë¡í„éO¹y‡·YÎyòˆ'O »iŒJõíVR®ê¶}ZƦеJÈ‹µªƒöÄ€©¡ÌY|Fî„íß¶åª2 ikŠæ‰`9³œ[ŒG&X‡o8±4–”c½t`ô˜f(Þ,z˜•tL.j›˜œâN± 8áé„ ~ô½ÇWæ|ö¥ßžò?œrèfq7ro‰¢#çné×îf‘µ2(A‰ëTZ×Û†keÛØe·/Wš„/$e °݆ãaCù ·ä†`ô‡4|"ôßµÐ[7ÕëêDíÆZwSÉ=xÕSYÎtp9ûFБQ²Â N!’„´Äô7~ðO4ÿóÿivFè¨B¶toÀ©z2$¥ŠàDT¢Åæ«…H¨®{Ñ¢³7Îòg-Ñy™AwiÙWc$0ÔDCQL%U[EžÈ ê¥zC—+ßÐö,”4áX·±‘"åyrJf´8)&‘m\F™Y^/zV ‰…ÃN0×nã;­ÑX.x8ðèí¸þ1®ßÀ{ÖÁר0·0¾ºxÆsô,’p†EÏpQÓø´Î®a9J'y†L7YlDmª2Ww£~ìhÕÄoçtiGväÍŠÝú¶ŠÊÉ)<ÍZÎÆÿ.Ån]ŒAŸãëM; ŽE<þuÞûSûóѾ>̵ÐûÚ×óªÿ ¿ýgùmpŸâÆO<üó?ò•ÎZ½ûQsÜ6˜ ò¹Ã›L £4*ФêE”EŠ-g'M*Ækà…16C+óQ`88æâ ªÃá1‡'Ü^óþ]<$)®f$T7šÊèØœagp`3H-áç § ÃÆ6ý*ÕÖ]·¯nöxí5ÂP÷7 u,Ñ@O°,-g–ÎcZÒÒ âI~Ê­6€E=X¤…)ôј€Úš^e©’”95ÜŒýŸ oa[N®ÿÒ üïBñý“úòà“ÔT”Œ'‰…#¥:g,|wÓ@tÌ~®±ÕryMÚÒX|¶S›â=Ðñõ‹”ݤòËÊ/Gó Âcx¬Fh’ÿä ·nÇ;7㵃xtœÉÕê;Ú4 WÈS4”Ë0#È’éÕû¬ûÚ¯É׸]GY8ך(ž#¥ &”¾£Ë¢3•èi¥dÐ$³Û½Þ4õsc;TϬ¥1ÈšuõÕväsÂv* íñÚIîë­¬ SzØk)“nY)iÎ™ÅææqÏ´ãp`‰²%‡k=*ƒÁ™íˆlf¬g3…«ü Râ\1æS/ú«¬—ôOxõÝŠ~Blxr—»óím-OÇ‘©Åâ!á ¨‡œ 4cµºr*†Cƒ  8RADoåã.v³n¨tuŽÓèk8Î ´u¹&ëÚ.cE€ê‡Ž£`ÅP•7£9¿ïb#ˆSE³ÇQ#9Œ¾òãYƱ~®z}©Šè¿ìNðè7xï÷g¡}}ÈkoáØ×¾þÐJ¹øß†×ÿÚð©ãvúsÖü¹‰?±‚±X!,ÔxcÛâ:±ÞX/)JJDƒXk|fÕYã$³í ø.†`?¨z´¦ÛmíÕÂ`=ó34mqÓp|…ÃC–sÖ}A lŒY#äÛT´>«‰*¦}^/8Å€CîZX=¢Ÿö\ ÀÙ¯¼ñLñìÎûûjÙÜt8 x’ÃæÆ Cn¾ÇñcèÐÍÂô¦Ð*0‘ƒNDzáBeÿg~ó3ÃûØ#NnüÂ÷wÿÇ ºyp÷Ö¯üø3R%ãMcÔ.¯Û7ÀFÂÀÜ`"ƒ’"ë˜!ÀÛË3z˜•‘»€ân,€–0<¥ô©l¢ ±Ù~ ¦jÓ¬KÜýè¾ñØÿηš_ûz÷kßš¾ñ´{Ó6ïÈœÁ ]! dUúÏ¡ä·3A 1³Òá6éãÁõ©\¤J5ÜÈ™ìfξá ÄJêDìYƒ:l5Ç쇮ÈÝ%ëÆôl±·†Å8¼AòëSÙg›¶¿±²]±ßnÛ³†ÁÐW7é¹ `CÔ"kÎH¯$‹[r‘ogE>Ô¶tñÕ]vÂZ&3ª®œ%ÃQ÷]·¯îêÔ =rxHH,Îèn†“" $¬#)`¡ÍÓ qË‘”ˆÖCyc&bkhÀ4ñt5ò3ä‹ ¾Ý¡:SekSqi·™ku£µe n¬PT±kë qiæo,Ùs,b=ш°ìªmùFdTÇ¥—°»I+Ô;?þ¼÷ùý™g_þÚw ÷µ¯o£žòþ×>þÅæ_/}ä»<Ÿ¼ÆÕ)Ç3Ž—6,aÅá±ïŠf=h¿eSDD“U¬ëD 1–øÖ¨@¤O4­M” ý+w}³…âšp‚ñÌ®ðô~ŒRRÂŽ®ñÉ#=àÞ]†~ë'Ìù‚™{,Õ™›” ˆcA9{æ(»ÜýËñ‚š–ÙuÎîAC’‚ŸËèMÒHíR§ÚÍŠ£uò ÜÈIwë†6a—`i…£÷Iô\%çAba8º@ÈËÛ}âæêþ÷¯ÒÏÑ ý1×Nþî§Ú_¸Î$O¾þËÇé’yãR‰³aH, 'f‡-ò,<Œç¥oï`ðv‹çÕT€%Æ<¯ÃKJzùߊ¿HºãxM²ÆüÁ#ó÷qê¦Ü¹>ò½ý±Y}‰P–X¼ò(\'Ý$JŽßÖÉoð¹ßYöìÚ[èß?\þ¼ÓÁ7¯v@ÒÖvÒ@/hG—?ÛÈ0  ÙëÀjY,Iõˆ°p¡Ø’ÃkV”à˜:k¶€çœ˜ufÙ),½#lV+C4ˆ"ubÛüJ Á))Ò'Bõ®”ö$¦êÕÏË!–+M£X+Ñr"´í&- i`þ˜Y¤I,„«ŸÇѬxiàýsÖUÖ‰U¤UŽ&Lrè=HÄ\`7$e„´Ð7Ø ¹Od§ó½m?WkúæbðZMÉ ¹‰»áTœ…“¤rváÞùjû_ék;Ü0JèHuÙ=%Ò Âõ§-é–4«££×N~q}ëüà»wþ©þꟉÇÿ˜ 3Y}e9°Ì,G–™ãP˜,°=nÀ„bb‰©tŽ û/‘´ö¡Ù¯È(J¦DýT$ Ù S‡1e&”M|5­ÃnŠÓbg4Ž^8Ÿá[¬GŒ0Dæ-mƒsXÐHttyQ1ji/8…ždŠ[€Ìܨƒ¯¦ß¦¯kî#7Üh¸¥¤ãüèÉÿÃÞ›ÆÚ¶¥gyÏ7Ƙs®n÷§½}W媺Uå2¸ì2ESÄØˆ±A" )¢èÕ IDAT‘PHBÚ¿(ÉÐ%¿ EIHƒÅ"A¸7`0)Ê®¢ìjnsnsî¹çœ}ÎîV7çãË1Æ\sÝ[Š0vý¨òú´µµ×ÚkÍ5Û5ßñŽ÷{ßç3±ªžÓ{Ì ú7¨nb' '3šš(Tq¨Ã[kÄp¿Cj©jTY@¬¦4ÿUHE×`Ç,¡sß -36õ_Žøû \Á¬ÜÀ“¾¹¸p¸AOá6ïc…ßcì'Ⱦè¡{Œ+-Ç{¦›Ì¶‰Ç®˜üw¨Hyüyîþ¦Ý­fWß(µлÚÕ¿Zù îüŸüó¿ÈÝæ­GŸúåÇöÍU`FLgL+š[#ÊòRM- icQCã°ŽˆDUE.N aãî,Ù4zƒ’î4†ªf5§k7°¸çKÐŒÙ?F`9/ZŽâdGÁ€Öæ¦C¬Á(&–™@ºß 躡k¹8Í&wIµª…‡Ì˜Ôlû¿šÍ7¤×°ã^Ë}Úã•zÉ­×qï‚Çxd§èUnáW´÷Y¾5š¿=YÝÇ·È¡£ÇrðóOüÂÇ8ŸPÕtsä—ä…Wþó™É^¿½ÿFÜv ¬kªhØSšÀªc®t- °.šAa¿¡‰.íq¹‘MñÖh¨'ëd`o÷žÐåtšÄìt‘b>òšzcÉÁ¹zàÞùBõêWÌôCÝ!¡@§øtr¡54 :›½2©^·j.l8Úãæ3Ý3ßnÿ{øýc³¹ü—NVÆb*€°b6Äq {ÑÒ)> ¼y#1!Ò¼§ øŽÖÓzÚH Ä5ëŽÎã#ªˆ'†Ì¬7Z\ðt3‰"1‹ž“¨Ã¬§­1¦ã|Í2êÄUGº%J´T‰x^³L;-‰RZZEM¹¯uÍÊ@BÉDLH½+ñÝ@ÀF*äcîy<þ–gOàjD¸8ç¬æÆGrXq<Á •a2BÑà ² âQ$%¿Ižnï; #X‡Àâ|``דÖk98"x.Ï6”©‘„c(Ë5 g””Öš…@LÀÚ2òè:½ñÜØüfìnZïûÏí>!¿éô×H Ì.8|‡êŠZaA ¤øÆÕœÅcV—,³zŒŸž³)LîVî Sû¥§ªåmëFt‚ùÒßýð¿ï† dÙ }í)^ÝfÏ•h9¦JëYF‚g•°ZÇåà¨o1Ð@3ä_û‡EÍ®²Ý?õ5S& ֔цä0;È=ÑâF¨IÔµÉà(š·>×\D½yk½Gè'èô11èHÅÉtr§n^³j ÒÔ7FÇ»WÕvï¹ËÛ¿íâ©?1¿þÞ½hõš\}QøšjÌÌá ó<£˜íD´·=ìG#%E”‰ÉäÎR%ã Ú²¤eF4Ev Vó,H:ݤe5a¤y<£]$ÖÔãYw,"Á`U‚ïÉêÛâ4#æ˜hoÀgß:’î<äVN› {·¿Ø€‹Tžed :þÐÅœÕ%gsV×¹þQª#Pì ·¦¶Tâ¨+ºÈ:rkÌþ**سLÖ²\DV19E,X4ÒzŒ%– Zí6­k1„s0psàRçËßõà¬z%ÜÐüÎn‡çè×WôËé`¹¡¢Û?ý`¸*?5Œ ’Þêá|ß§ô ‹Ã%_žsç÷ìâwõU;½«]ýk•žòÚñ3‘_úÙÓõÞί}u=õ5q&ŒÃíkg¦nš™ÌŽ9»O»¦m¡ÉòŒT¨‘(œ=bÞeËÔPZÞ}Ä+f€ài#ꘟÓvÍ,_z£™´TÃxŸ®ãê"CÛĈŠd†–'ÅgLddË !+§‹ÓÛ¸áâ1WWYn‘„®:`_‡M„É!¼$gy´Í„êáoãtUvO e Ë*е„+è"¨Q‹ðȹ;ÕøÕg§«ëU]W!oüðK_ý7d ÍJ;ecHÁÍýßÑrh˜)gX{Ö@Äw\±íËÖ/a û‡ï±Š{o`ïþ'ÛÚdÍy,{+‰Cè{JmÉÀ1˜€wX‹‘ìc-öìAõø±ž<ã§è!:=SQ‰ÓÙ[UýšRÛ8]ÆÙÍýñ~cU¼x¬ÑÑq{ðìâÖo9âßœÿÛ«ƒï±™Ê磲¯tQ¥:%Úí“ä°È„’Ð7ŽÊbŠ/GNHZ6°nãÎ X°1!·Õ‹ttjÏeß°Øá#±¦Š¬;Vé褅wù`%(l=m£õƒÜÁ„Ò5˜Èï®´Âl@:Ö‘Eà¢fV}ì­yÔqƒ—q<ædÌÈ`—'\{™zã–'„k5㊩aê8n˜*SωpX3¶LfŽÆ ‚³ŒûŽÃ†IÃ^ÃaÃɘªbT3q4ÃÕœYlÍdÌQÍbÇ=ka–PÁ>3¨a{p¶¼eš®íˆ&%»; ì>ºÁ3]Ñ:¯ -_7 |¦íöOc˜–O©‹¬yhW'áu¯Æ–—×e¶‡û_åÍï#þ³ÝdWßpµлÚÕ¯SÝù,Ÿý»üïÿýéßøë¯üÓ_ù>ûà[>ðåO}ùخΧãååÞjÁbM T Æ"Ž tÐì1Þçü!ï2ž"Ž`JçÄB ‹+Îîƒä‰ÿ,ε˜¢)Ì!ÞÍ”Ë3bÂå’I¿ä4f âÍ~v Ù^É[Ú†œÅYŒpuÆÃw‘Òú£ïËïÏt¶Y-½© ­‘kÙ;!Vt##ְްŠP“ߘLKòÍ>mºjäí:˜¯ÿc£å_Ø[þ¯Íûˆ5éãÃRÙ8"§Õ‰Žcp‘¶ãÒ³P„À2ÒJVPòz_AÛãÂ~x¦C2CÙHGâ¶ŒdóŒæhÔßW˜úhrÇ^¨Ó+¤“›²v÷WŸº¥³•qÐ,C}þÌþå“GöÖQóÒíæ¥Í3Mu‚ÝÃMql…Lƒá¬Åt5ëý6~àrü;æõ÷ÛÉó=xîàx%Ó‡êw؈7¹…m#á({5 l9ÚY«˜rTR¾‰h»‰ÇL˜ìc¦ÈvjŸxD5C+Ö#lƒTè7ÆŽ° WÑ*“9O#%ñ;yɽdÒ§P|»%f tÚc±'L ãä"¢T–=Ç~}û6/ï?Þgö<ûOó„㉆Fh:BKTœ£©³£µƒn +ªäf±RŒ’ë°B0ÁYÚ7d9lP¸qÂþˆqÍ\ˆv1-,à¼H&x&Å#ù<ºâ°áŠì8i¨ÆÛI6}‹—°†.·"¼7¥÷âpÛÍÁu!È›²|»í$-ÛÙ‡v;4Q—&ÐÂÝÎ[Ûéžwõ Z;½«]ýzW¼òËüüÿè÷“Ïý¾Ë<ùîhÿ Óç—‹õìñyã×VÕ*Ú˜ƒQêýppÛÌqï+Tìh“{ÒË!ÄÐ^ñàM|W"3Ñ€ÆlzÞSÃâxÑ”³w1Å0ÌHÆÄ„lXkž`ètŸË]õ.3ÓíŠ{w‘~_s_ß¶iÎ'Ö9dÅ3õ˜–£3¦ÐÈê€å„Ö ¢‹Zl ôNoIwë=tL´hæ{Ó;ó½j«?_Ç©åì=~ u+/g  ^–5ÐŽƒH ¾cXBìX¬¹ÒAhHñ*ÞôyòD0ÃPTñJèI[ªk-1ãyš<曄¡fÏ QbRG¼:Ö ÎrÃß»kFÈ>æ3ÂŽ0#L]Xm ‰è›k‘¹‹«Ã¦=þÔú£×»OïwŸÙ_â ^NxG;u\x–žÎ¦%tÄŽЀéÐír$¼MG>"!h£Ù¹çð£l<‚ae²·á:ÒY¬ ž¶o¬$Ðyº„Î=mjìC"5ÛÄÁ¦CQKg¬Ó ¼vPGŒG"1Ò%6:‚P[¬~ò¥ó—°½ÈSSžh¨mcØH»Â·ŒjšŠÊ¢ŠONŽ „:ꀮQUlÀ(>`Lö\o=Fh,{ç9í˜*ÃØ0ïˆ}£í)œ…t7ává•êíí;ºfM§›)ätŸ³ÝôÜŸ‰}JÏÇíë#†Ç®0Ü Z‡)‰f îh¢÷þãú(–ô{oüÜû·v·‹]}ãÖÎzW»ú:ÖøÿøàËõ‡>°<âÕ}ÛONŸøÔ[o}ûâѳÚZ©sGX `­©ôú'åôŠ·_ev“ýT“¢5Ëüг7 m1ôW]H®. ½à1ÞàÆÓ5z¡ˆJ£4dg—¥²l#aYZLÄÜ(ÙÂ\Ø¢°‚uYŽ ´–ZÐHçQGSö›v¬“V¢e!ÑÏ…ú0Ì:yäÙrBdù­dGŸË'‰û7Œ…nù2ñ˜£[<½ÏS#*A•Ê` 3h#vx|E4ø¸Å®¦mYb‡O3NÐÄœ¾8VPZE#Nyv„i9_#0Qžv¼Ó±ZÁ)œA <;è5<`ã“Ôë²)SX¢Ók’²yZ0´)ìµÝÞmï·>¯ JGhÏ+÷FÔýé š…á¦Ì õ;è|Á?Ãêoïn»ú†®€ÞÕ®¾¾õåÏ^|ðOB5âÁ‡nþ_7nþøk{ãô׋Ûõumg󳱸Ê?âÞ¾ÃÙ;Ô˜\cï³1ÎÐ]rñ6±ÃÚܺ•²N²GƒÉžÊZ¤¸Îa+¬åé±¼ [áLvêˆ>kkJ_UjU3%%ºd}þ¨ Ü>éÐfL§ö}íüºéÔäíQeºæ`‰‹,§¬j”ADúHÌÁ$2:ãàŒÙ©o¾ðUû¹S‰ }ðÍôÎ-vXÚ ¡ó„`ëèZ–kæ«nÜø²kß`S˜&¼ßqàk¦®˜Á“Ãùìž(MOE“øz\“œÚ&Œ#Z ïú>EãîÞ«Æ·xö±œ\<·ך.»U1Ól@œ|âSpz,çÁñ0=¸Sˆ8Ù4¯0Z1 ýâ8>Ëyæ˜ÕL #Ë~“ÇxµE ´%BçYÛ<ˆ‰e*A”‰¡UºˆFf‚¢#³š ¬K¬Ëþˆ¹Ët:#žyÄëéVȈ8÷,Æðä j8DœPúÿú“ý ÖEË‘ðî`áb€z}ŽãÙÌ”ô±‚)÷»“A;àðÌMO&å4ƒ©'´$êÀžâïqïñÖgv÷…]}Ô@ïjW_ßzé»nDdAXcÀE.níÿøÉþß?grïê[Þ>ûH¼ziÙ>…ßß·Íì–¿ñQ÷ðKtçï°º ÞÃÕ}¼g\å;wR_ˆÍ&wjr&v2kH)I¥ÛLùàË|é³Ypе94¹ŸùN,î0Ü{XF8;åÎ+» å}¹b@ofÇæwR›´Lç¨åjF[œ»m­Õ¡ÁQ¯8zÌÞ}Ž^Ó½öF„µ ¶ŸiÖ¯aí·í²Þí¬\´ŒkÁ³¬[® zî­îˆÙPÂôÞÛMŠª[¹éúµ¢‘µ¬ð‡î1º)k{Ð,„ȺŸhÈc%뉞ÐPIñ˜KÖˆ†j¼­Žª5'OÄucÜx„DÑŽXcëŒÅƒ ‰ScVP£×±Çƾ8qwŒûÅý;q´¯ËkÝåÔ_цìôb0ŠV,+Ö3D“ ö9ž3-DÌs‰¹ó/Ì»÷mX*u•ƒf´¥k@¹4‹³Ù/­§ß»’”’v´<mßA¨h Ó¢½V4àCv­Îb¦M¦³£ŒBÇZè N°§[S*Pƒ—ßÝà‰c^ª™UTeàU[œ¡j“i\«¨2R´em²æÊi–ìˆRÄÐi>ÌFˆ&ßw­°Ž¨b•ËJY®¹ºžs9BD-û‚xæãÜD›3ÿzåvËmËCÉÀÏdiÒ•íö›Ž÷°ÑUQ<÷m…fpr¿ç-à +iEli åú[Áݿͻ|wSØÕ7Gíô®võu¬ßúGoßxfæ‰WhŠ;îPÃhÆtD˜Î~ñúìg.cóÎâ÷®>vÖ}˾ܾùÉo™?2«Ç tKÎÞ6ñ ZÓze'IáŒ7©}è b*Å8ª´|ŸÅ*b–µKH,»`kÙ³Y|ø„®œÝh4b&YËÃ5Ü nŒÄ|TM@[´C:PÄa!.aŽ.0®¹@Tü¤ši%3­ ©›»Ì³¦a•TðµHQ؇ëÈßæ–`e ëÁ7€-Þœgk^ûÓ¬þçÝMaWß4µлÚÕ×±>õoטÇÄ•Wã’ 1¥Ñ1#G½gÂÉìÕçf_¸ï§‹îcñä×?õ§^û‘Œ}zµ­ûYrg°8‡5¹ÿÏA#ÁI–,¦»aíxúEÞ~ò½?gˆuޱ8>˜Ò¥C·æÁ;UkIØc@ÆnÝM·íã’Hy¿c²"Xæ:SÂÕúü½l›Žé%“‡4w^Î>Y·;!(±B%#||Ú†'ÖšmHl?´G5‘˜( 'cWnÅÕg’yÞ¬6%^&q‹¦ñED+E úȈ9’“ ¯ûìî~ °q Ì>ÂJr»V2*ôÅÚ"Bˆø„yV,-’^'WÞzF{Óћǩr£Ǹ[Ø:K)n2FR÷¦GÄS¢ ±(N™˜õ“ı“jRÑÍ`„Fl sÚ«¬@°à4Ÿu^7Nuhö¾š²¾ucúô½×Ôw~ ‚e ÎÙÑðz‰Dɫ蒘¿¨\$I$ V7§a,€6½=˜¬u!…°T“Á`?ÓaWˆ+L”JY¬üÞÃã—yù˜}ƒ*b1 .gÕUŸêK³§dl>ijŒ™™–$Ä14âóÎ!gIE‹û<~‡®Ë7ãhèed9-¢‘Ûϳ<äí0ZøÒ,˜‚Tz/Ž¬Ê¿&Åm#ýk#XÙ† 0n,”s5èDt…Û‘„²m mîxW0­E"ÒÂÛ?Ë¿mw;ØÕ7Yíô®võõªïücGùÝ7ï£í"šJC™ 5…*L›Ðì1½å´s_„_~ç“‹ÕëÿÁƒW¨ ÍWjÊ¥@±i.?ù¨E¬``\ÌÕLi(Ä]DŒC: ÙÁ#ßÞc,eæôöÈ›¯1?ÇT™NËt•lÑ­ ¼â4ËúbKØ_â-ó ^z™ñw„QËô‚é#ê{¿dN?F‘1TujùR¬«lñF¡û&)ÝFÒ½O\¡cÁЉj‚™qcÄ‘Ç✋3$´3LC‰%Ón@~3ÀÐY2R>7y?÷“ܱì'H¥¿¶Z¶¾R¦sÎ¥ˆ´‰“Îâh4”v‹-Ë’Ž¶Š£Æuíރú½%• =a| [Ë18Ö‚+]•ùlAŽàîï5fÄò)íöIžyNñeãlE5¦³h[RLÀÊF‘›Ðn",5Ð.Y|ôúì‰ïž¾õ°yóóŽ*ä=wŽlæBšðD¥“|Rg¥JqYÌþ4iDTÎÙ„‘Cj»MÉá_åÜïñ—гBIÏK±ŒÇ?X}×KÜLÂa¿ÆMðÈ1r™w߈ٓ¯‡ä±ÔÄ‚g^l dOˆ¹²i[Ô³^2¿ mñ+º9qÅä1]ÑA¦‚©8‡Pó©8YðšÏÔµ¯¸…v°(Û_ëèT þ¶4Á<éÁ.¡+(Ünl|6ÎRúy ÝŸÅI‘µ„ù@‹•΃ÎàͿʃÿdw;ØÕ7_íô®võu){À÷ü¹WÓÉüB».6µƒÖ¦Œ2µ6ÂELD¡š2:æ‡ëïùÃ?öð¶ÆÌÅ:Ïý;¡¶Y^P™ y¥ ŒŽ,[žÈ«Àºc4¦»B,Q‹+ëvv^ˆ„%"éÉ·¿ÂW1ÛH붇«ô Ù<0Ó ÂAG¬˜×Ùü7Jã™^røêÝêÁÏBˆŒ”i2aHŸ ÄH ÅùÔ¡‚“~cƒ›‡½‹ÇF|Ü›ÖUŒö¹mi<>¢sÎs_ß—º’*}ô.uƒl”ÞÀ®o[ìmÀz¥KÔb#­[Ú•mWÜ!9š?vƒ¡SÛ_(zRéßJÖ»‚Ò4úz==óÕÅÉ8ÔˆÀuô&±F-TYøƒb-b²¦(‰:¢g0ÂÎ0ðçUËá;Ò¬tq$m£iðÖâÛ¼•Î 5­…ñD“ñ_쇦ùe."TœßàêàÚÞ­ß¹÷úëîÁk!;œlΤò0ah …Ë7ùV¥¸¯¶¨ÕÓô‰-§Aš‹0…)VOçht€žc–mŒK«jtŒê?T}âƒþ…QM@„‘cV£-±E•ƧÈ@—áBíðid¶Î¡/iµÚ–õš®%xº–ù%Á|9W"Ä")gXCN‹¦‚ ÈOÜÀ8lM-Ü£,´pơ軆“B:PK÷¢çôúXݳl½_4½ÉF4ô®yéa5h7 páëÂ7Sä×sxý¿áá±»ì꛵vzW»úºÔ¿ó—>róƒÇ«•vú€®¨ÖJ‘`ªÍÖ¶”ßFò µÌxêC‡mþé¿ô/ÿ1Á+‚Ãëõ8¡îݹbqÏè§ÎN %È0 ‘éŒË{Ùé€>¥0…bˆÊ²E•áá[¼þÅìe (¿7!ýK7ÙÉ%zêq‘«†H¾7'Sމçä.ͽ/ÉÙ!*£ž’V|ÒW(k%Æ5˜€‰Å;êÔ»7}„±*oL9Š 99(' îr0ã¶e W<~Ä»qÃ#Ë0³°Â)¼|cB7d‘‡cògpÌQßûß÷j^dÃC'iËÃ5ÔUw’¬W#º´ÓT0ñÎbé»™?¸qõ3 IDATµ¿8Ä^Ã<¨PõF 3Ј)^Ý¡n 3Ü)1Lß4“7å`ÌêÑfd—B§“újS†v¡DÿD4€GSFv±wˆ>O?DËÙ1óæ¹ãÙsÍkÿ@³c!ïkô ;ºˆRîV¦ýrzÌhéd¥˜$"ˆÐ)nñ*ÑÜPhWQ?õýÝsT¬Œkjƒ³Èˆ.â;ˆø€ï@h×tÐáר²nÑH¬"óȺe½"tÄ€÷„HŒ¥/’¬ ÉÓRæ)ìf6C".âcgÏí¨Fx…',îù2æ2¥KO¶/É©û„”z`ü<87»’kØ”1YSÐs78³ãÀ„N·ÿ^ÀÙvosxmwõÍ_;½«]ýú×où3·?þ½O‰Ø¶Þ‹1âƒjËx$ C%¥ƒÃÔ=…-Y‚›ñê·üïÌïü‘ǧÄ1:E ‹´ÅtÙi.¯)FJs–A„ñ‘„{ަáå˦ÊÍü„¶ÛnÐ=2^–۾¨øuhï‘]8œ%©É) Œó.à|y½‡3xõ/pö×v·€]ýF¨€ÞÕ®~ë£òÚwþ{/¸I½ð>c0Vƒ,×Ñ9±kr ŠCFTK¾š•A„[þÞg¾ï÷üÜ?ØŸŸƒCÁ:¢Ò oM½Â¶¹í/±m¦˜=+„Â屑Æô²Ê!q¶Æe’2S¬e~Îkÿ/«KÄ Håu[¡'°y¨E+‰‘é 5ÄlQqrÉáGîÑÏ+F™õ¼V!ÍCQn,BÖiºHgò„{ê`Œ+4aëÛFFyæ ¿ ‡û<¡˜ yb ,h/è c¥MûÉd¼¡g³1›aãR¨b%cªÎ ¦ÍÆj RÚ`KMq„` /é;{3Þa†[WŽ­ ÂðÝ`‡ºp³Íø|Áš…nF˜ ÖØPJUг…ˆî#Wp†Úl¥ ^*»:fy Á .Ž "³œ›cÖ0/ÀJé6KùóÒ`"ˆj¤¤ß‰jNosM¾»¹ó㑪èðٖӤቄô.l¹_§=àtã(!‘J©Š/Š)C5UÒoÄâsbi#>ÍÞo…ùþ݇Œ"ÒeóÆ•CU:F)ºÉ1•‘H°è6¢Ÿ°@Ȳa#¥8}‡£f»Xp3%Y1•ÏýfFSÔs(\ ^p†§kBà—"MÊ«„…œ¶¡è+êÁÈ6™o´F[˜ 2 «Á»Xj0V/ž•+8/‰-Îáñ—xûoröWwßÿ»úS;½«]ýºÕõßÕ|âûžúø÷IjÔEd‹÷\d-Œ DŒ€G$G§\è¡ z£{îÍã3f¯bÒ°çéÖ¬QÑ–pE§¨£±¿â¿Ðs¿z ´àÁ—¸Ðþ$]Á,à N¿Ä½¿Å£¿²ûòßÕoÀÚè]íêסžüþéµïÚ»ö]{G×f2¶>D¢tѯ|ê‘«›Q+Ðõ1ÔÁøTµ²Á‹‚+1ÔªÄ&Ç?ÉüÛç_ù™Ÿ:J Å%ãgp Ëkš[câ´'¸H0‚‚ϲIšóVÈio1b qŽ/°:ÇØMË[ßç…»b#Õ€A×\â¶+ê€ÂJpžkW¼qéN62Rê¯Ú×£¬c&µ¬´½‚¢ÐÉ }%sŒªxë°GBÒ@7ÌŽxÎ`Zž6«ÐÒžÓ†‚³-MÃþšó¯yX{n³~uƒžMéßÃͱؚª$³Iîfëß» s|_xf:…3lÊË(4`,ª¶BœŒÛé…uWžuGcz€¦°ŠX•ÖÕ¡! ÍðZÌ0ÀqŸûÏÝx¸ô×?¾Ù®™G1˜Ñ£cdEL“L—k@-¦¤f "4@Wü^Ò §Ýì_qz›cùÞú­FwV\Ùús#sÒ’½PR0{ÙÚvTFÏÅ«®‚™d ŽtžŒb½ç'¬'´b\û½½ÀdÂAMÎ`ßB±OëiÌ —Òäšh‰ Ú`í2ì® ]̰¦xR´D!n Y4Eœkñ)40¨2;âƒßAmèŠ}O ²´¨à 7#¢”/Á¨ßôFÁ–M_rŽK¤~&5) F:l åžt¬‡Z Í)Ã…ëö'iï/\üït0û.}®ŠlÞ”“AìÀ…ñ%váL·b¾ÄM‘Œ%&[·ò²> qk —"˜…õž{³¹~¾Ú¿Xßðók¬ŒÑâSàTD‡xÄ£ëÁÑvà³àf­Í&XÄ[Üb²÷‡_þ,«Ç ,êû+–±DÚÿØ÷Z®‰T‘Q±¢6-¢¬ Áàb5[ì¯&t.K…€pÅìe޾•шÃ)c‹QŒÅ$p°&§³$ôß“Çùê*4yãD¤Lé T5øX„ÛC§˜œÐ«5zD.ˆ„XZ 5òü·²wB»¢¼Á ê€ÀÒÁn("Üc[$ ŠÏFWÄó‘BÁ—Äþº åwb»îSÌ$\Â[ð‹ßKûc»¯ý]튀ÞÕ®~-eþä'NþàÑÑó*¤êjµëå õ'b‰H£ÄhCXF@Œ">àCDˆœ·M•\8¢€Ë Ã7\ž‰œ<ÉŸûí¿ãøì¿àá[—}j\ÅhÌã3láŒ+¥êr!EMàòQéX*¾e*„@§Ø€,^ayï}Ê ¶u©Zn´´ltÕU¯‰ÊþŠ›ÿò«¬^4J#D}ïÎë™c™kækS€³+Ÿ1ˆ])Ñt(¹ßÎ œtÌaáRyäW\>æ~—³)Ô2UŒÏ´±º ¶TPÞ$@âröqF}Sè¸^À’ãdQ€5‹˜{7v$)9/n’â’‡ñÆÄÚdýi~‹Á ¾°Ñ ìnØ=+O±‚î-ö Ñ<Ûè¡‘KµµX‹msÏž8X ƒ9 JIQñˆ'Dh‘DdgÖWè¥4¯bt½'Ö‰©Š§D$áé:¢I…Zè`™MÁs 9ÝDý՚̜/&TOü¦é«ÿ@³÷HR=˜÷´cœf…°D¬¥&ÿmZF#¦”á–C¦Z_[ÌX×&b< zÁìŽãc&cfãLe[I¤ÑeÖYú¸—òÁÑ+’=¶†¼ Öl„ü•C}ñ¬6 º±YÙ"›Éƒ9gˆÐE¼çéóäóqÅ…’`i¨ ÌË¢NR¡yßÔN¤|›rµ^+§gšÂšìêbi1,:u,¬àj£tçþÿ²CÏ»ÚU_;½«]ýkÕsÏñ‡þlüØó¯½{÷Ùñ]‘µ‰•m¯_úÑA=5ö:î1œ‰‘•µfü÷ŽbDd:º·X_ DtTU·öͨª0& B´à°‰ÔmñzÌžä?»ùm¿õŸ<ñû¿øÓìäfA ûSŸ–0³ÄKŽ-u†5ßÅÇìy—RÐÄ!¢è’Ë×X߇jÝ'¹ko+;YqÅí®½žk-rÞp|]Ýìižv—bW èL x-tQðB Îd»ºa¯=ÜJp€f2Ž88â…1#!®h¬—¬…¦bÒr‘D+CÉy¬¡BžlOšF,5#‹ ¬ÒfYœdë-t¡(Îoœ7Œcä˜\ònÂÐ’óÀmŤe¥ùÉ$™É{%Ø1©:2›™¬?<2É´zZí¡_YòéÍTb°×¥›X·‹f•>§¥HW¹‰o¡`®][$ 9F>!îs¶[Fµ"ÑuìßCE—{4X"WEé+0*óþéˆÖ¨ÂS¦2´àÜ÷xŠcf®»=ñ«~$ Eñ¼‘Ù÷E D%TÙÓØvDK¬óC´Û¿¾<”Õ({»dáÈ9Ís<ñƒ¼ÀÈq²ÇÔ ¾Œ#D¬Ég>”KÔ Ád6:[(!–(>”ë¥O×Mˆ<¥ 2J[ã`ȥϧ>È¿Â*cî„¡kÍ8•ºÎoq‘sËJ²¿ã}ðUi(ìÊM>õ^bº/ ÍLðä îž±°€%<†û_áÞÿÄé½ûâßÕ®úÚè]íêW_ßþi~àÏòüËð÷-öo°×Ä›mwáõêFž¬!#è4¶¢Y¾Dm»+ƬƒÚ!‘ʸ•ïTu\×Ö˜ ºÂ7XGÇ1›Œ•GüÈwÜ\οõÜûeìcÐÀÙCºUž•ïãÑ,ÃH3Èï¼Ábž[ð´¤yg×´sü«t—…K6М0´naè¡ÛEʱØÐ­ à…ã9õÛ?™è,C+7ݘ¸%H“TËÉ7ÙG¸ÜÍÐõ£lœÄìj•P³Ä‹cNœ>䋌Ð5cE\ˆ¥ö¬µÔev]Ê,½ñAjÆŽJ \¶9£&ôR ÉPÊ t…Ï*Æc¯x ™åµ“mñFڣôÂä2‘tºë¢å¨“f£¼l¨oÍ|e­¤øŽ$/©ºÃ'Ãr$ÄÛTõfÄblŽ„Wø¦€¶`QuÈcÐ5š„æ×‘O÷9ç—!bT]'³SmÇ’èÌ´ â:ÃbFÒýs±ólÝóÐRdEC3ñô̺a}ëÛFoýÃþ¡e¦$‡‚¼Í£@ÁKÌŠ,Y ¶Âùfo~2^N¦DüôŸuNý<7ÿ{/QULØŸ€Ï°8:¢æË)!:Eƒ±Á̓Ù)©?½¾$FâÀ=]&bʦ?¢„HîÐñì‡yñ“¸†è‰!b«|šš4ˆÔ{?VÄuæÃA_«Y¡"ñ£œG¬€œ6Éy1~Nϧɒ¬a ] \ Ðó ޹ïü ÝÏí¾õwµ«÷Ô@ïjW¿ÊúðÊü)žŽÄ‡˜ Âñª=¹è˜£\Z©”[>Ž*T©àfä¨j´’µUU+6ÇsD‚„y»WõÈÕ툖Úâ’É@Ì~À>p}Äç~ó¿ÿ§ŒÚsª†nÍÅ9FƒÓ Ñ›dIËÕ9wߨ¨–cI‘ð^ƒ9’(CŸ3Ã{™Â–EïÐb¨Ñ„\«b"³ ‡ïM®îK%PÔÂ:‰ E6°.SÔeÒ;‚ |`ŸçÝwk@‰KÚKVk¼eâ°͘½ç)—ÛR¼Á%wg›í¤¸)]ét,žÜwáÝ›ÖÌVàiɃœ0á(Э87¸š‰` ¯lÊÏÆ…£˜¦eÚb|>¨)T]ƒº*žyÙî]…ÑÔ/Ø«×ù,H ^ue`åÚâäjØæÁ@vkHÒ‘­À ×0ßjÝç$œãT ž³_cͼƃü. y 4¨fRÓ$ÇÁIÎŽ–¸9IÄäIØKEáêdÜÜ›â×).³œËÃJ ø2û’šn[—S¬ìÉ ‹6/<çü¥Ã|Ió<7þ{/RWÌÆìM¨¢Å§=â@rD9Å.]c‘‡Mwä¬t›¢.ÒŽá¹NÜâ¡Søyod<מà#ßEÕ°^#1ƒvëk[1E—"c¤1¸Öš?F¬¬äE^¯ö*V+ÂÓdî~”óM¬PY+q k¸„KXÀ<†S¸‚ XÝçÝfýÿ«Ý—ý®võÿS;½«]ýjêÓƒOüiœ_QYìMBDº…,fõÒJ+Ü€&9Z) 3á6œBÑÒ>gÅ+Ÿw²62*¡z!9·±‹‹—<^-GpFœRß䇾çÓüç~ËSNÒµƒ8-^…A÷Þd½ÌâÎ$€ðŽ Èä tCzÓ­]»â÷\h桨z*²ê ĬH(íýhÄÀù¡§°Co6+´Ðêär ö~ï…„ÉŸ3¹ä~Ë<±•-Ý’õï ’Ñ¹*FcÔc -ƒH¿„’Ó&ÕL\YmÉrd#ÿæ,ÌØN|3ŽJó4{™r-Ð:)ÙÇ‚˜Ì¡nmHqIÉëÑ`•z€Êhe“Ï·qÞ`{A$!Ê O† N9vt™x;ɱÎËç2.I릉y`X¨5êá&æã¦úyѵíPÃørõRnJ«Ð=I:ø¡ÖE2á`œSW„Õ±èvx^Úe>fvë;›·~ŠÍÑOTº–¡›(¼d*Štxƒèž\îO»f#6×™Sª§8úAf/Q×ì×LFXƒIJåk‹8b‡5Ù*¯”f À˜·Âô±7+PºMIJà»?LKÃ|¹YÉĶï8¾ÁÇ?Íx<]pà1*Fk0±ÃÖbD£¢+ŒÅXŒÁxÌZÖTݾi_Œñ+Îxk0W°¦Þ/ãÞ®‰Õ­1ãŽÇ Þ½€ûð*¼zÉ£;|õïðð]–s÷í¾«]ýªj wµ«åúÍ—‰TkgT/QAùGô'xx¨¡‚Á˜b:k0–à™?äÁÛh2¹K„¤%zâ}ÌÛ¬×ÐCbCœ¡®àÜX9ÛÏizº¨yªZ%£­§ÛrÅP]¼ÆÒJõ&w‰Md  eQ—\õ$òò}ŽÎùÉ9÷M;˵8`6¤ÛX¶ ›ÛÕR%í€A#ÞàÌ`d`²KZV*›LÑö,ª t±äZ*Kâñ#5·-š’%¾It¸AÖw2îè‡ •£’;Þˆüw¢c+3À÷ ð§aÝ}ücz‚¹ñæ»ú`åý3.Æìý‹­‚ï…4Z„)0À’¸B=Œ‡3ºwsh·ˆjÕ½»jGøš ˜{:9äêšø¢=¨4èZbV ¨G»œLûµ"NÂ_ím‹M_W™2\ÑrÜ­bc–'n£"}1Š Lm½·>žF›“-©e6¾ÂÁæàû?Á´ápĬA]qt.h-žýÄD>õõ%]uŽM$u`C£Ä¸qQø´œ\ç[?ÃþÉãà›¨.ÛwHÓ!@ði9ùÔsQ‹u jYW®8éÄHøÊx¡€}@ýú"´ÏLÌž «GÕé—ï>øÜ?ççþ>¯ýo}q÷¾«]ýZj wµ«µúÌ«<û<¦$PLp—ÔeªØõÉA+¶¼sü‰K}-vô‘n­{=ω»\À‡œ/Øßáùïav™OþsÖ-vš•ï8¹ËÑMÔgôì…^ ŠãîΑŠjä'¨]\”átR!)8ËŠóðœŽl†G!Bö¤{·züLò I$¸Ô aDÛÉ;­¹i9ƒ÷FÜ9ozN'ìxÖ=í™ öÀ Îqe_“,“ó±3(ØhÍ`I¯KeŠŒv´*&@ “¬å¨ Î1ñt‚TÔiï†-‘MG`Ž„ÑÅ%!}FŠŒë†œÂ¡‘N³:™»elf0úâãÝe*w¿nœ-èX8ßPèó‚c5^È<‰ƒâ7}bƒ(œ&Ý® öGªc²q"Éý/ ¤‡ô!6Y”ÌG2†›Ã²ñçÓ¼“{:–¯Gv .¢ÌF5žèó:¥ J^Á˜Ef‚í÷.¯§8½Qc»B„‹é÷²s…ÆRWÔB¬ ÇÖHÌÑBiê£!q„Pìô$Ûå\% ^ñ!w Æw4ÔÊÆ¾#_\£>B@±C ú^.?ºŽ±VE+FÑÕÒbE0F„(m0KtÙ­ëÆ5UmX±­i|³¿¼×œFó2œÌh÷™=zû¥›¿úÒ«ÿä_ñsom¿È·µ­?ÚÚèmmë)ixñ“\y?{Þø{ë 7š`A5ë0GS´—ñ˜úzÑÚÓ@;±—‚«hþ,îu×b<Ài®{5|¯ç³Göî Ó;¸ó³ë»­Ç/iï°x‚Ã銳þ›î=3‡U8[±;åÆx¾ô«¬VL¦,òðMús¬% A3 «îæ63¬“bú0ø>‹Œœ´¥m”°<Ž“Î “9Ö5sšAß<²ÝJK"·¸Èx!ï#}„¼ÃVTxBÙ ,WܶĖEÃ\G"m ¢£Ð½&ÛÞ&f NòÞ'bÝŒ7ßdS‹Ôbèd¨ºÑCgÞºbZ\¥ëô)5Ó–s‹M‘vy)~D Ê†„Í}r©0¢Ð°˜0t¢·S”¤{ XÜà Ú=ûD˜B4“ª}|Ò6ù‹=Ínòàk^0ØÈÝ ÝÌÆYå ÔÈs&1¹‡ˆ} Nˆg?Y§œfW”´OZgç#h ÝE =Y¿Ã[DÌhøü IDATpžæþÇcÞñ$÷HñšY¶™Ä¼,"ŠU…‹Äj~vÀÊP—ðt*à!õsÜø(×¾…a¢Ì, ÎP–‚µ™ÿÏ„¼%¹·3ÊX“ñqú›MÜI ½šÙªYF3?)¡žEü|µ¬ ˆá…sã™$´Õˆ`­BT UTL‹Š˜Îš69Ìw,ÛÕʺ¹Tµ›žÕ÷£yS9ûóÜv¼Ýß½ùÒÏÜþÔß{¹ß~“ok[<µÐÛÚÖRüO>qáÑë‘a(O’îø3ŠÓíÂL;嬬Úvý~oYDNí®/ì;ê 5`]0<𜼰˜N©îZÌ[‹ƒ·ûåþütÒx¡¾ÅÎeN”gW¯?£=¾ÁØ |¶ÆÀå÷¬«Ùäõßâþ8¹M\cjé[|"ñ<î>r dІ y#;6‘ÓˆÉÒ+&%ÉŒq©;ŠYÍe×Þ˜8Â_:@R½·XLøC‰*{Ê# kî{Pèæ]^`Ä:'JuȾZÇþi2 v½`ô–•‰±G§BZ½—šy$8j²ZÙULJ #…cÎÙ‡# Ǧ1±ô¡ÕçN:6›wa( •ÁÂ*î†hûËÕzÏiñåà¢ÚvCí³IUŒ¡˜d H/)#Z!SÄUQEp÷QÅ<‰D#Ó5Lô<ï @½ñf1’g_º,9õò.9ÆS“fu*…”ð“RQ½ ‘Æç†T62]c Þà–WXW¨Ò¥é­ =þ5~˜'ÿmöŸ§±¦–™-d¾e­0£2z¶†èKÐÉÈ F †,ÞÀBåN?ÆQöPžf<ûÒ»Iq­Ÿ•íÁó¾?Ésß‘V£A$¢APk°Nc¢Õ5b:è’¯ˆqØÞ÷«åõáË;Üž±ºÎú!ÇŸúŸ>ökõsÝö;|[Ûúã¬-€ÞÖ¶ÞUOþ¼ç§¸“QH\ ÅmaQøÁ0Š&¶£uxÏC‡Vå…§ÜœãÓœò‹zB“oº¶~šÉÓ¦f6áÈqcÎ|çüÆîí'w­9sœŸ?ö‹·žåá¬rÔU¬¬Lê1¬'gkÞ€[sVÏpå.ÍÍì*`Vp°ÚèPÖ uöbeê0÷ÄH×eòS€žØ\‘ÐÊH~KAÅé--«wÂé¥gç÷ ˸i¦ÊÛƒt$ Ínk ΦŒnOB“uñ°ô<¬Ø5LÉB…(ÌÓ@7Tlèä7³ioAÒÈȧ¡†& ¾/6 €Œ"Õ1Mð×€§«hõ¬¯˜W+ÆJh Ë™øï¤ ˜Ø,tH§QK΋¢‚³†>+¤!Äoººzñyf„ýÏ›Ùçßìs©Ÿ¯LGh€x(2øÜ°§H€M4h€3tEL …s›î(ÄD1HÛV±‡( =áaÚ"‰Æ˜itTˆbb‹Šò]ó;Ê0Û+ÛG?ÎWì¾ù’ÐM¦«Ù“ˆ“lìˆR+Wjj ÔÍjBó$Ð0Üøoøæïb÷*®ÆG:¥v´}îJˆš£^b9#‰„P~4¹û6–ô¾l ÁÒª‡!ÔäÞÁ¤8IÔ2`+bÈ„¿±ˆ'tDÂjÁ{¿…ç?‚kÄXÅ­ºSnh:·ê¦¯wC9ß¾ÓÓå:DÄšÅ4ÞWº‹¹¹{ß;¤zƒ[¿û¿~ùÿʛۯðmmë«P[½­m•j~÷ý÷\þfvKçRâpˆ•Ìk+˜4ÉÑ¡È9&¥ƒ)åÛr…õø”hPàÏ™ÍÏÓÃL "hÁÑN ávì­ÞÛ¸³¶zcÇ5nzÖ<÷êÑ¿éì–ëÔ,ÓÙî¡mßäSbŸù°õuí˜ÞC"r†¹ƒa@Ä(΀'¸7h¨d“â +¬) Z,½Æ&n¹dcAäÁ²˜ {ï™}R/Sõ¢™#c‰6‹•EÐ «Ùx7U¬™E®G‚°2`r\„ 1d£-Ö‹J”!Ò/3¹#ú–<09þ<ŽWæ‹/ÄæÇ¢vE`-² ì'¼Óª-ˤD¨ ›ÍË‚ŠèƈÃ$NŠN,¬6c+Áõ÷¸¿ Í‘­Î§;÷QÙ÷ÎT´h HŸçÙç0¥UL¿A¼1=ÀCPíÒ!¨-•Úï°xŒP«FQQ?aµk ¬ŒŠírpyöûXÃj“=bo ½Qv\zóÓþü‹]Î×K¤¸¬ñ\ÂЊ%[þÕ«J“ö%̲ €¬Ñ›ì”G~œ½Ùꀫˆ‚‡ œ®i,“¥ÏC› Œ;Ç6φÍa,ª~3È£c>íÔç/Sú ÓLÕØLòGƒµ„íŠ|'ßôXw7'¶7bCoóˆ j\t%Ï'FU´¶‘Ư}ßÅ^Ý­Èç˜ÏÍe†ƒÏÞyóï|ð7¸»ýßÖ¶¾JµÐÛÚÜø/¸þ—¸ò'Ø+hJìÄ€ž¦%?‚Òú‚¡«²SÚˆ’æ ÑÀv”²a¼Na ³Â*®`¦¡ »÷ê+fÂÌ1y„õîù›¯í¼ñ*¼AµÆÄo>«µ¼õ OE&ŸBOGÛPvG}îÆê4O/Xƒ‹Ù»¡¡$Yér¶Ü;p³Œ¯| –;Æ?_Ÿ}A7¡Ü°¡ªy·*Äã#â‹ RcZ\’›Ö\Ùãú]vá¾a¥Ù½7­–‡Q¬‹\|ç8ÀÓ¡ŠcÝMİycŠdWsCaþƒ!¾dô*[D RôÖ¹­Ð ~hÎÓѺ Å`ÍH^¦.9ß 6Òƒ˜b‰2°àIKä¿ï‰å‹1=BÚ¦ŽóÙñ¡oê(È9Ú (’ Al®'_5ã!€GüËòè¨ÔªDÑl:!ª~&«+„™¨AD}#«=¢ÍÚæˆvÄc‘4Ah‘B™(èf;R:eÚ½.½ò+6¶=uÄ÷¬“Á ®¥3yŠ’¸üià@™pkŒÍ©•ú%šïâѿεïFöJ©ÆJkê¹/±ØSB±'L³Ÿ yÿ‡#Å7јœë)EÁ,`,Jê—EãˆiþÆe‡mS$+±¶8ÃÙšÃ|èOŸÖîíÞ„¨oœÆÞ¥ÖAãBÕWvò@yuGÆœÔ6ö­Z·~âðì:=¬pv¤§ð ¯oÑó¶¶õÕ¬-€ÞÖ7v]þë<ùÓ\»Š…gÐtœtª )ðWFKÏýÈ.‹9‡…dH©lRþ¦-/_@jγÐA=Á9àmÖp=ók´Ëž»®ì0™í´Kä67ÏÀÑ÷_è1¡_ðÖ)O­2ü”ÁÀŒb‘@mé%Çß9e Å)ÉxíÐôÕ# ÍE£®ºÁ Ws“¢Î/=¶ßŸšõ½ˆ;õ+hª~XUE¢¸ ]el¨%5âU¼˜Î¢t*­±ñ¹ëŽ*Ú–ó5ào~êèçþÚ+Û¯ómmë«Y[½­oÔºò·xúgØ/p›ïîŒü1P¿ç8’jè(_yøò’2´p“ ¤*݇éC͈À~1j–pÎÍ–Ó˸ êØ3´Ë×¹ûiw8l˼q›8úQê{Ô_ [ðð2ó5;÷!nœØ2PH¾c=•«4’Xï¼O5Ô†6yá?L6a‡©]r,‰N`ÅšLÇ«¡ßa§?’pž\íbÖšféëøA±aÎyˆšyèzŸgk-ÚÓxBÔà”V³œ^ÔiHÑ4ÛÑ!— fdŽV@Oñ1+aç\DÏ2Ö¢¤ ÎS\#X!$07¢‹Íf |ýˆŠá°…ÆPE¢ÁÉÆ‚ã‚ò¼ÿèåî©(f…š`yj÷äªzƒ¥ˆ4ÒÜ­CG|ƒÁš™•éí‰É—CѨ5jDñÚKªVt¢"ÒOtµ/±V ²°Ú—púÂCSˆlíÑà‡÷˜~ùŸIX(uÀ­R§¬Á>;àì+X“­2Phorøxö£\ý0mC X(»Ê$ÙÌEºÀ:°Œœ÷„™Í™Ìð1t´¸”¥q.+ŠLbÞÏDÚ+“ó¨yø’:Æl­ 5’XꞸ‚š ÄÈ¿›§žùMá,2‰LªúØ0óÑDõÊÒÎ|ŒV¨¢*ê‡ô¨VÄZÃzºsÿ1óð€ãžvÍZÐ •EÔûí7ú¶¶õU®-€ÞÖ7^]ù›<õßfèŠÀ1! PÍ‰Š¶£ %\ðÎÍj…¥É€+'y„©ÃÆ!ŽUq•6ŽßƒöÀÜBMßr´€ vð-_¼Ëâ$óÇÚ2·Ì-g‹²P½†‰ìΨ"«šè˜ß£î²h;AÎlô¿•0àʬAJC•æÅñÞ¬áXþÿ]è9¡m;ȧ'¬®~çüáÇlw?æP䡃bž›Pýæ± /4»¼oÂ5Pk‹wT#ÞwGIâ.ÏŽ&`‰2AÝÆgC ë,‘¸BèÐe?bw¬ÚÚ^»ž£Õ4ÊDâUúC³Ú‹‹¨M´¶iáíaœ£=ôè ‰Ð£}!kôÝJd~ÆÎCì›?o˜ ‡@D}žŽ¹böfɇ«èÖ´KŒb*„Ч>Äœ_ïx,"kÀ0Sc ­áŽá핽yžp•8{ ‚åÚ$Þàvàd…ZÄQ+¢Äˆ¼ð'ÿ«?û-ÿè¯|rûí¾­m}Õj  ·õT7þ.ý$s0lÚ´( qR-X¨aêB0ûâ^g‹)rý«Fï?ä¦߈L3g+ Eѱ.:Ñ©Óò„•F˜Ûi'ð6í‚SGåÀ¢&\>àlQ&·Ð[4©“L„åj™Ó,6›™ €QœgRgc» _É"€4 ?†º¡´ &€P°uZ_Niù¶“7ëãÏ€gÓEv¡JG¦„-fošóØ 2î3h÷B•PM…Q4d·k†ÔÍ+ð*›Ð>FÈ8iYãEE·ŒÐöÐr©}Gt…'¶ÛRù‚§ÿA-6`4{6È»úÉ6 :xÞlħ´GóK-µ¥ö9/ÆLû]oŸØëÁ¨½«Ï¸ê1âk0Ã$%Å™%ƺHºKÆJÒxĺ".sFºÜÅ~]wΨF•xöMqý$ÆÒÏYíóðºˆU[UîÃzXdÍx’W›)¬hJöN^$“%÷™¼ñË&/1ÐÕØDÛ+•0MžŠŠ*•aǰ+L-UöÄ À>ü<ù=Ÿ½}öÂÉ‚uIÎÊ"ÒjVñd5Ern>÷ìFî¯Ø;á`gà}‘ºSXzòõ»ÑrlzMG[R~;̣Иœ¨‰'®˜5!"T–ýý‡s^WN<½£µÐq-pMX€Îá÷±{Øç ¦6ÇUur‰Õ%(M¨yÅ&")AsÊôû?ú°ÅÐÛÚÖW­¶z[ßuõïñØ’¡óXw¡EP‘Pì¤ÏÕˆl–‘ðÑ”ÂáÒèµå6¬V6…)'§_õYªÝ’Q0zSVò—”§ «a ÷AYU¼©\ÌMN|agÆ´au7‘“ ‘&ŠW"8aú)ë½=ÉÒ$0™ -l±æŒ;Æè¶È:ÍÈöz#ÅÉBuf6n¤÷–“ËO4ûOÌîü®Yßã’h¸[#~Îûç¯Ò[©‘äþ–Ð"gkhÑtΧþDF&‡w©oýª[½‡ÊzO§P3i©ÍŒYÚëŽË5{U9–]é/°ðÿŒüÈÇnð-xê>/̪ô¥RÚ@-:'£ ÀG|Ä–ž…çcæ uƒ3ˆ¡èç’ÿ®ï˜ýŒm:t„£›á%B>°Z iET|ÈM¯{œÔÔ†µÍù;±†¥ám¥Ó<©gŸ¨°°<`2¡Ší˜ÀØA],õ”ùŸþèûŒ‘ð—û}¿­m}j  ·õõ^—þ7þ½lêüÚsmØ‘óÆÐ ˜ú•Š6Câ ’'m…©27(%¢Ï+«ÁŸ, -’•;X@¶t}ŽÎáKøZºCžÐÃ9Ü;•{ÂR9HŠ]‹4L:VoaO¨-jÉ:”f%ñÙ ºGñ§4†*M’So$”.«8$33¤IdHxÁÊNFÏ”¬òÃjf\WSº'¾}çèõúþˉ{G/kñ×vyÑÒ$R¹‡%}Ä ±bêY{»’}¤ôí ÛG¦¸¬E2ÁÈn@$:÷`t4¸ø|y‰"ÞQ2S´ør8/§Ø†Äç¾Ãeo@_ÉÂR‘[mŤ€ì±§ëãzwuŽ­Ž™¾íý¦uU4Έ£Wœ¹`'XŒ)kÃüÄ"]úz¬Ð7»ö­ó@B Ýãá8~”h4öÒípr_å³Á#÷ѳbŽîË\ô–¥›ÐgwÊséó›Ó³OEš@£tŽï驘GXÓzâ”YÅd]æØ$¤re}H>|ï›ä·žå ‚ô뇠„ŠÞbMYJ Øåu•¥©9¯Y` Ö¡Ð µ#Øâ¡a ;2ëe4©6Zä¡”>;Ek64#Þ:ZÎ.?5™?5¿ùËÒ‡;-‚ Uúš«{|gÍ4Qó)ô-…`‡k˜yZemØZî4ÿŸô’ØÜAa¬xÉOæë¡M”Ñèr1•\.üf¼ëR¤)½}§•1µ-Jî)”æÎrž!9eü²1Ù‘k›‚8¦¦˜©¤€êþ/Ζ¼®"êÎeþ2îž`¦mc1ÑH°Æ‘Øy=êãy¥Ó‰Ì9bQsÁâ\l1š[Á§Ïúë(Q„hÖW¹õ]\J‡Pû=Îzšû[äŽGæ‰YôE Ó"MÏÎ ;¯ý®;ùT‘Û<­R¥á Ä@q†*À’¶ÁY\$ÜŸU=á!Ÿê?¾ü'O>ñÖ¿ùþKß½ŠÏ?ßO¨/1™ ±XÁ+•¢+Î=kÏÒ³ œöwœœç쌴kœpmïóy[×DƒOzöÒ/˜%/å(æ,H‡YF}š&E*ÓÈIൣrLç<Âà óÖ„¾¸ΰ=Ó’Ä"Cã1yb’óØŠÖ:½°%ty®X|Ãô‡>úüÐÿßoþÚÿü¹ÏÿæÃí}`[Ûúcª-€ÞÖ×cÍÿ6ךÃ}؇b®<´ªk1Œ›íD2ÜH·¬!!%ä{gë™VìXöj$àŠ9ðIÏÃÀ´F"=¬"ç±|Šæn*ŽÑ.ç÷jqê¹p{ÜtN`Ó"ç#ã¼Xzµ„®=GhYç½ð†62K\2Ä z$ä¾@ªŠŠã†Ó.eÓ76wl;6ƒ¯6BOEl6£8oc˜Ù¢Ù,vZí3Ô^ÖôOþÙYw¯¾ý1úS-_DJ°\ßã{*¦=ÚÑvøžµ¥rˆ#Bmˆ¦éýjÉš©¡ËÅü8Ž>6^tY¦4¦ì’8Îä‚ÏÆØ ïz¼äØÂ²tŸV饨/Ü,º‘¹d´5Ò$‹-B“¢a„Ê€âK”£*~õƒÖ_þrcº+û¯bß;yn.;«+ƒªÜžÌ'b-êÐ:›©ˆÂ Yg4H€;ÑßóªÆ­£é<ç?ÌÙs¨%TDËɾž”… ô‰åìmá¼ðÐ1“¢UÇôœ½×_nîFaLÚ¢8enhL¶„¶=‡ ; ¢ab˜7Å9èèOx)°ˆGõö›ZÿýîÑ¿?]°sëý4áð˜?ÅásìÌYyžµÇ|¤WV‘ed©xaÙ3ñ؈7œwÜ;e·¡qx‘(‡Ä>Œ,ðR¿^:ó†Ntu¶º„@·b8†ÎYk-O7üŠ0­óo6Ò”eGê§ä—"H—Ï´”ˆ:¢B…IÖ8šWQ¬'ÌîáþüûûßöcïùÒÇîþöϽúëÿðKÛ{¶¶õG^[½­¯¯2?ÂãkÏ0Ë™¼ÒÒ—î{5ì¼àT½È=›BˆìÂU‡±Ô–¦¢oóŸ-<÷=Ž“ÞQ³¹®ôp† 6v–Ðo¤Ã–™0K,xS:—#Iø 2‰è™Òd°–¼b†lÆPU\:d>ãøÞ` ò2·/$R|úbƒ„Vß!k©Ÿ»Ø|ò˲¢ ‘Í€w–þðj}ð£Óû_v7?}ij¾Ý1 O×qæq= Om™$:".7uªÍzŒh²Ú\µXER"`âwcæù6†"ž¾àÂWXeƒ”‘2¤€MÄ7Çäb² Á%áîX¡1jj”Œ2Í:Fá`°%aQ?Ôãå•«×{{oÎo/;ñ+sØØ½Úø“#ƒA£v1ÔÆ*br§ŸZ$Å·¯ÑõÄ@ìb¨4Xã[÷†õuÚKih¥r §YIÍ úÒH°ÊŠçüc\Ë¥{ÌÞümsú¥Hç™ÙQž†Ï*s¥Ñœˆ2 (3P¡ª¸äF½¸çœßæŽÖæŽÚ| ß ;æ|ú1pö«œücæÿ×¾Ÿ½ˆ„dE§L,kϼfmèWÔÐ V¨¡­¸ÂÁœÚ¢c6¶ð}Ö&KqÛ `è4ñJÓ1›¶‡ü+h×¹+јх¢XÃzÉkú¾o‘¦q¸ŽØ +H()9)K(–¯ŠáÛËlÌÂÓÊ’©1 OLK´DЀîN>üÃÏ~ð‡Ÿþs?u÷_ý_þßþË—¶÷‡mmë°ìv¶õõSýkžùϸqÀnñrNMC%™Ä>ìBñA -iò%Ã㎧¦6Ũ£"ôˆ° Üî3䂉9ˆ[–°†cXÀœŽ˜o½—ð~iÄ:Sþh|>!`w8xŒ¸ÏúŒ[¿CrÀñÔÐN˜']C#ÔɯXs‡š*a^ÑÔ,WXGo7-PÙmC6‰yÑÇö|®¬VÝy²é&L…©§ê/Ê>©ÝÌ|C{é \zÑ f}´§ßQq%­ïwœ*ºf™ŒÃJ¶§g $Mf”`‘g‹û²ÈÆqd³¥”6¬Ñ\`˜ÝÄ:;‡:§¼Ã‘º:§¥6GMޱÓ@GƦ^Ñ–sÍã*‚TLGéѦb^àu:ÚÎfùG ¨Îî")Ó9Ðé·ÊêÇìÎÁÉd~¿ŠVè•'göZcwg:µ¸Z»µ RU"‚b̵žŠtÐ;BOTä܇»«®ëöïž?ÿ`u…ÕU9¿"¾æ¤–ddU4)èçéË\±§j¹t›K¯üZý毘ö\¨böÓHý‘6PGö5_œ‰ÿžE®Dš4³›2Ö„Î8~ƒß|È+¶¢*ï)³FÀxÂÉÞ“}Må0—‘Ët¯òðsç·X¿—Xå«ÛX¢ BO»ÆG¢25L-fkrø¶B`Ýâ=ª!‚'úìM²¨+Ø?–Ø›ü8:úJ'"#=P 7†ç®|E­‘–P­Ù —KnŒ£“VÑž8FÂhˆÔ ƒ-*&Á\¹±óâ÷=öýí¹üÀ#v"¯ÿþÑö^±­mýÿ¯-½­¯‹zä¹öçØ/܉ÐóÀ:ïPYb2ŸÒ‹ò^É‘a_x´â°Æ‘í'œÐ‰b˜A› IDAT¹ïó;JìèW°D’äc]˜¹¡ôbÉ‘40) Þ~Ä1‰-Ãócú*þ_lá_Ãkð ;ßÅ€O¶¡$dQ·¤Ú¤Z$¡®‡fƯ³…#fÉ+ëf i "Ø9q/ãËû¥#±…ÀQ`¬”Åîa& -LXïÒîhï½â-ü›>pÞ³†. =KG t´u‘a”Ö †*"Q‰ ur)€:úQ¿zN¿â]6vzx¦ðÐÁâäB/`šŠO(žtat ¾£¤8©0j(4Åϲ~ˆÁÅy¹jvÏg»÷Ĩ±Ì­tp8qM…E²[«»êÞwÅÕà³RçŲä>¶øXVDB_-®¯mýÄ$±ÑªÑ3¤+gã1²†5Ú!¡˜b4@ êØ¿ËôÍß°ÇŸ…J©Ç‘¾‡ŠyK%`±Iú¬\2\qL›š2™ÄÛ|¼åL’}wø´L& LK\ìÂnÁ úÂã´w`™îœ¶"F*å’e–Hó@o¸·ff1iJis„J¾bðè³c¹šÂC—Õ„ô8F¢“ùéu›ó½c$F¬ÍÉ:(]1¼ö9n¿ï?<à?ïÙu˜4Š+:—Ç„–à‰&):Z|Ÿ=ñt’SrbD=qMh1/„€vÄ€F‰žè°–ªÊ ¡j07»üÄc~ðGŸù‘¿ù­¾ ß>ë³^ýÝ{Ÿÿõ[¾ë»a{ÙÖ¶þ?Õ@oëk¼f?É{þ.¥ÙN ‰[•H”Ô@—~[c ól´Aé¡™ŽÊÀ.žœ°Û”hÀ ‘ZXƒ¼á^Ï*-¨‡üOµ8¬`UðRÈÎÆŒ,–Õ+ÔŽ²ÁûÂb‹r#a´¹#ìÃÖð:¼^bÆÑÓkæ3{‘ Á™ õ9H9¯ öæô–ãÞäpgL`ÛàøB9ûqÊHœ±"Ù¬köìÆö+-XõàŠ_A‹X,,öX¾€{ÒÕ·çö‹wïó¤ TY×-k©V2…œÈH§ÄHôÚø4÷w%uGž zåQ¦õ[–‹™0ú]ã8@K™ éF¼a“©Š¥ x½½3ŽçâÑMžÕ»áج¡†6¼¿Ÿ\»EÕ*‚‹Îp ì×X£®D¥ìX5WÃj™:1h£XëlJ[Ä c°‚x4 K/ovRËÑG$Nèö8IͪÐÃ)ܧ; ñ4-{·hnÿ†;ú¢à”™äƒœìãr.QD{€&`Á`{ž«Ð8l…MYŸøÂC>Ýrªˆl‚5àÓ,þ’¦@½AáNÒÍL eý$çkÜ z¼àE Rê†iE´YY´ŠœµX)†wÃÄ8`ì…ùÓ XôéÌ!3õ´€-‰ð}ÄI¾j|À‚çívz©9í²›Î$š`Ñ ˆè?Í™ðÚÍÜÒƒvÐÒ'߯~É<0™ÑYÚ@"QgŽÃžƒu;רa–˜c±G1F[Ë¥gç5îñžýà®—ÿñr}ÒõÁͳ{_:¾ýÙcßë­ÏýþÏ¿iqµ½Álk[hmô¶¾–ë‰ßáÚwdâylðì`ZrO&PmZKgìTº©©“Û²úH€K†+S&ŽªºHTp`}àg©ˆÏx4ÿej×;+}‡¡ä&Ç ¢D‹Öè„X1¾nÐvi„ò†Œ:Œ€PZ áKpTw¼0$AŠ`Dè.ÐDB~T#5s¢™-‡3œã¸§˜ˆÄÇÆìmh²«³Y›©²7Óƒ„ÑW턦Ý^d:egËÁZˆ ËñJU?ÿ¾jñVýéWg¬ÒXlÍÜak&E +‚8l±¥‹‘àK~о§÷åH¤g”PÜcë´mz1Nqô› ÞÏÛ>¸ã  XŠÃ† tKV÷6nÓAØtb޼¥7ó,°šµ¸D»@˜MVÏM×µLk« Që©8#¢Ü`k^Üœ¾O  mŒVPUETAï¬C("ÊñYmîMV„–rn9EZ$õ´y6%If2дìÝ :Ï„¹lǤå“‘¤(µrP<-Ì„K†z˜6>ä•û¼¼æ¬^ÔÐSGæ ÚŒÚ_åaµà‰„§yð MÄ m`-ÌÖÒGV1Ç% Ô MÍtÝ ó g¥õT6‹*Œ 11ºå„·ùŠÎ¼ºÃfÑA=Îáûœõgò¹Ø—ǽÖð©IûÿËcÍý ¿<ç— &NašoʺÂ7ØÖx‹©²­v<Áx\·0S#–i`¿ïfwÝÄ¥¹‡Ø¨ÑÄvªñZ#Ât8éWVá}ÌNz«\uãŧû`×ÿÐÒÙЯü_ü™ó~µ<½½ðm<½½ºóêÉÍ—.O»ÛŸ>]m¹êmm+mmëk°.ÿÜøOÙ…Br ¶OæE¿Q± •¦€Za©õv”œpµá‘9ÓŠÞ³î )RMÏWk.ðvσã‹Á(C¦èñ†Ÿ:Å¢%Vhƒº‘ÛÆ»©‡çcQnÄÒΨ=ïÀ}x½ä2~HFù”§Ó¨t–^p¦И^2Œ¸4¡qÜYoÔ"Ð<ÖcêÅP‰BàJ1sNƒŸg ª¡‘ìûÙ»ºˆ*(XÄ¡5ÆåVÅdS')óîÙÇíóËí³ÉÛºßXÓx‚£’¬NŸ&£„@€`³N5¢”C29‡å„²¯úŽÄfÿôÕþŽÌB)îx)ÑÙŒï ¦¬È8H¥à¿Ô™9èwLFd”®«ÙcÁfÅþ—ª+~^5no÷mÕuQÐʈ5ì r¤I”ʨ«cÛUQƒj 1ˆFA5 Æëù’Õ€®¢iOU}€y¯Ä¢HˆÙg뉤dp°s/þŠ}øÅ»]PrÚ‡º,¯˜"Û­#—a^°Â®a\:…Ïxí”WŽy=©ô‡±çGj¥ÉZ;7D·‡™àV¬žc±‡ÚHˆPÔ®a•6R_ìšåŠÅ)Õ r®#k‰‘•!FB²o7%š2‹Øóº±ˆ{d³çê³±vÒ:§”ï´,ÓŒ!’ãTîßçî?¥Þ½rxý'žxþ'žÚ¹§ü¬á<»+.š´~Ò-fŸ`-Vé{*ñ¸¤Ñ¯™6*0×~Z5µ°VÎѨ1{È<†‰ª3!ª.;Úµ˜cÃj¥Ô1Üë¼e5C'ÝR4 îÈí„zwa÷OÄ´7Ìúùð¨_{ ´‹°¼×>üòùÝWOn¿rüù~§ q{;ÚÖ7fmô¶¾ÖªþQžþGì_¥ù(´oˆ>¹ Ó‘ø æ—`¯Ú˜BTÂaÅÌ!ÂÉšuà\G:ò*£,ιï1=9mé C"¬7iišþM ‡&)g.ÊšÇîÊaôxÀ³ÉÍ+ÂÛpþ.=mñÍBkh4˰S³ ÂÚ2§ô`[eǮɨqpæ¸6áæ Õ È»¸%ˆ8V9¿»w0ªȲfÏ—ðómlµx›l~LjbE=½1Ćh‡<¼¼;ßnõ‘§Ý;ÕÇÎí.bÈ„©­*(^P‹hvÉ ²1èHŒa”ç_IŒ<„ ¾C¡Á;rʱъLb¾Mqê.À7y‚§—›á…yE#Á%¸nTÚŒ¦&†lbR0aÚ¯Õ/4«¿q©yïù¬Á*•#¨bbmÄCÎh’ß`¤¡[›$0¢HJgWÐI%L9]j6<5ïÃ<#HÊõÖm¡-òe~ÂîkŸ®¿ø;`•)v0i ‘ÑL12WÓ,Ô†ÃÜ€'Üã³çÜ4,V<¼xº%Ôš¯®Ä7ËætOÆÊÁO®yC%ùäG1Osú4¡å¼‚9DöhmD;¤CMöxkDé{nÝæRŸSROéÉì·O f“oÔ숑½)—vÒšTYÊ#|î²M!ˆéøë¾æ×„Õ GøüK4ÍÕç~ê‰oþ©é.»òê„ð‘©R)ÓÀÁ’nM£Ü œÃTh•§ê94\êèU} jLƒS5ŠûIébô!D:ÑhT‰Þ„>i´"£éÁô!ø°¼-U­b­ËTÓ‰]wlê“ÉìöÞ wåùÉ{~àJhõø'Ïßú܃û_>¾÷ùûw^ZŸÝÞÞŸ¶õ T[½­¯µzÿÿÉ>TÅ¥ŽÒ8hËó¢-–ò`(ÁY&¦Îa«ÀíŽûžuBu3pÝ0’yA én/ª"´è:Ä¢žèJ†å5¦zŒ6ïÚͬ\. éåÈÅŠµÇ!YqÑ ¡U€zÀÐ!Çg8C¬Y÷x-­W0uì7·hÈpPz$»ZhÖø´Ãî4›lzCßÓ@¬! ]檣+º†i^(Ïbç)Rƒ! Q¡·ø ?ãÞ{®7Ú›Þï/}&ʯ©C, 2Ú˜-b,=‚&÷á‘ĉeÍ€Q0áÅÀ”ñÑB|rQ=>~‰ý$â¡.ÓMî“ ÃFòŠŒà’ A‹ây@êºÙÒU’¯ˆ`•`©¤›}:\{á|bDˆ•ªDm‡ZtNƒ¨‰2¡] £hŒÉ„RVZL¹j•àó‰¥ëxã³¼ö9Ôspõ½ïÿ¶Ÿ¾ñøçôKfŽkŽÏÕž+KºšT1-Ú®.K˜j °TY¡5±ÖЄÞu½ë—½Jlßö½õÖ‰A£jŒqI´Ä&`U{+Þ¦S1Ðu'nzÅ jüŠ®óÆ)+b£±wdê;½Þ¥¹Ú߸úü#ßÃ:œ÷o¾uéw_º§/}ŠßûÜöNµ­¯ûÚèm}MÕ{>™ϳâ:WC n¤yð#¨êŠjA>˘ÛzÚÀYÏÖEvnݵÓeá ܺA57®QœÃ*Q¡!º 8‹øøì îæâcw‘8—’ç²NÑÛçÕèÝ {„N©+KnÅRXK¶ÃNöÈbY„ažõÐp¹å¨FÚf:l¶¾ãsGâf0„% g–jÄL¢§ÆDˆË$a"ô†uUÔç‚…i$à,6y}V«+uxVºïô»¯ô³ 7­Ã ¡Î> 1:z%šÜbbîöÔÂɆï”qÈW:õ†…úq‚ù ¹þfÔû™"ŸÁš&Èi u=x¨^øðAÈ‘|ØÒ:‹X%Þù¿›oùq[Ñ+ª†Úˆ*¤*'û]#é¬P™°^Û¤M2"+QQ™T"S}¸¬E¾Iä)üDÚ9í%©aiлd~ÄîË¿mßþtQâ›ñ7ÎÖ‘‰0‰ƒö”«P Æ2IÇú˜?XðFÇÂP9h;ÎÙäT3ÂЃ z¸–ŒE‡Ð§kÍ'õ ºKü·˜|„þBÖjóé31ŒGÖH‹F4  T*,=kOmXŸÐ/¨'43lq DŸ§s2–6éFº#]È®Rôã–a¡&‚A@ˆ&ûxh±»¹s‹·Ž'Ÿþ¹?õüî•ßh¹;£q4wŽñícµÛÁ.<§ë° :rÇßÓöÀkßÅvú¾Çz#⢠A%vû¾·˜Þ¥±"Q4˜€ uŒµÃAbˆ8 •ª³~¡»¡ß·ab£3ѱºí“° m§f%ö {ÜÛ ]<˜ôÏ\Ÿ]cñÝßÎ_x—?ÇÇ>Í«ooïZÛúz­-€ÞÖ×N=ñó<ö!v`w4:[ðå ¬Tq“A¸T^]ƒ¥«GDõ˜Ð¢ V9òÊÓMEKX代ÆÜ¿F1 fŒËßQc =¦ºÇ© ©B±AØÄs—·•Ñ[…¦·xùR+"$iI5jï²lÌ–RÜJl5\ž§}‰p,ÝV·‘}˵F•)½D²¸ÅAÅ©£9a~LLzh"¬6²k™ÔH3âWªKØl@«ÁÄÞ‚­µ\N1ý·Xùr=ýî“ _ »ˆ_a&FbOס _€Í0–YÕ0:2ãžÂ †wEõ¡"[­ –\Ë¢¡†Þ@ÄE¼dñ–ÚRG¼´,óËø<Û È!"Ý— tþK"Ç;Íι¡²Feã`.®ÐزáHDTL;•P‰Ñ œ¶‚ ÖX6“ztœ\’á\&»Xp¸3>õÊä_|BPဠ•žt.;a MÑ¥XaGØÔÐæzü)/ó…ŽS¨%_AÇ™KÍ;z6ZÅŽ*Ø~]ѽ†ûw©~‚ÙwÞY®÷ÛvbÛ²t“¦ÖG¹R<¬Ñ~mqEgkfk@· _1i`;AMeëÈòphü,íÂ]i±ØíÄü4h$$Ï! ±Ï¢Ž$IIG+x|D…›_äæ—¹òÜ÷>õ¦½™,Xú81f©Ì£¯WÝ•¨{ÆÏÄu}pkïÂzCÓëÄKOA¢¨Qo¤òJ*L°ÚHï"!XOè/‘؉ZîImƒÆ.ÚØïÀ5Ôxbh+˜0±¶·hì«Øí.Â#GúäygW9YÔŽçg<ÿ ?øíܺÏ˯ò‰_á•­Tz[_oµÐÛú©«›'ŒÙÈRc %‡à²DjÆ‹¢ˆ„G ‘ÜÅwÙÃu£·*™jôåSô]±&=zj‹ÝPq„eÇ%ïúq å"|l¨Ì8Ú.¾ó0yˆaoPÉXác0ª2~`¬†p…ãÑ@Àv`(mýÆíòŸA)´ß 0Ù¼/æ×\fŸHQT¨„)ÔŠç sþpÎ7<'ŠÝ¸‹L`‘ƒñ-çŽÝ¥‡÷¼X¿k]!kÌÓþUûLÓM¯åÖQWsŠ®r×YÑ+H ÞËêÑ1ºêå ©ûÀÏ¢cZb1De¹B»‚ -pFˆ95^rÖN Öôr‚P¢«þf1âÖýœ°r?ªböÎrC¯øÌÙWGçyéw¹ûMŽ>øAW#\_±¸G}Èá-Šjºqmc×Ń%Z Ñ¢Ab$J xÑhÔˆªªi¢-À‰ŒÆ"ªS/ÚUû»r™n¢¡B­‡‡!8£œ &-¥m¨0~Þu'^C„Bü¡B.ÍÂ#´åî”É”÷Ïxö&üGùýÇøõ_âkëb*»¨mê@_Ô;¡®ý5ÞóS$L–PÚ(ÿ+#3ÈSn8 ã.ÜO܆k±ÈIÚdw¹a¢žAèºw$ØiýÆÞaAcó-‘ôŒÉ¹—š@—Í®vaW9¼ñ™·&.‡£Õžvœ>'DX #¡æ0„ªL2µÂÍ’7´×…inl“ö¦ £šÝ¯]OF×M.L‰–=ÍCJÚ«œÎÙ€(êàú×ÕO˜?Úwš¯ÿ¦ŠuÍ÷H(Lð2þfЉé.‰Ÿjœtêœ]ã}q"¶-ê—‘2êTµR1¯‹¿Ö•WŽöGËGÜÞGÜÁocoMo鑬SLÀ<ļÑ™yè9Ãt7vbpD]H¥5JÁM’´¤~³x&b)­¢5`(¿FÏ®Šv±ùéLæ¶Èˆêk©x÷¿9}ò鉦kˆ‰ñì²”0;L‹T¨í ° ˆé ÀL5#î­4F#QkÉM«/ÝÌ x)Vqü¿¯Ý5P±XÓgqïév+G„)ìA &NùÊ‚oxNÌÍŒhrǺ_rè–²]ã–8¡Œ9eÆà"Á¯³Ïpù£Üú3ì½ÿ+ðÍ{|,"Í>ñzD/nìH ‰Š¤ÊÒljá$rÅ1)h¡Ó¾nÁ+‹éФƎzªjŸš¤D¡°Y.éÚ>Úpc ÚöY‹éÁ Í>…J§==Z#ËÀZæ ¾ô9sÊk{rH„ñ!ï7ìí«G4Þsÿ”Ê2)Ä€F]w*Jeœ¢úVõQm[mµóE};1Q‹b©jà1DgRœ ¾áWW*ªÝW#‰… ~®¬äNíÚÚv…Q 1ÕZìÌ§í¹Ž*≫ëܼƧÿ$ïÿ3|ñoòÒÿ}1§]Ô¿u /êm_õ¿ÅÓ?ÕëÝtà÷ìáÛ›Ê{ùPÛS™ÉÜ…n·±ócv9s;™19æ{y´ëÕEâÝßÍãìú)À…óM˜¶C0)ÛßaðøçahÙ[#µÍ'B£DE”Æ2JZ+aÎqjY û0‚h q‹7©¹^sgIˆo(ÚuŽ·]äýüì©ÉyÐsZ$‚C¦œÎ¨Kê7Òb`B}ײz”¦D¬Vß6ö@»KêK7û=coûxH¸F8¸RÍc,?ñ^µ"«R–•]Œ\%R•`»àN¿äo=xŸ¬åàCõä³Q^Xf·ëéÈÑljk“³¸'%n:Óz®ë<ÌÒÑíÇÃài#…nÉfàhãpÚËðÊL˜0´€¡’>I.9¾%ºÂæ»ìýìrÀJÚ!(E{$Ÿûû×ÖNmŠÉK­A š˜ êz®¯´H…XÄƈÅaâDFÜ_ ÑFаw$3½[ ªÆ”wšß¹«3HA•èñ&¥QKŸÉb”IdOÈzÁ7—<ï9ÉC'©ïz‹%B;8\Îø¾ì”D¹RÃÕÿúÁÞ'§O]âßø83~í”/=àšòðuntI|­‘;èáxжÄyæ1—hË|Íé:0Tž& p#Ô­ôŒ Iíý9±Á”ýr$Qéû¬!æ? ´`GÈšØÒE¼RI/@LžoÖ†Y+J„•b´?÷¤=”Þ\h+¥éhÖ` íë¬^æŠ?þ]æðQLÍKK>?ç‘¢}Ì™›µ™á$ȼ‹L,B#'­òZ§wOd¬\7BlÚñíS‹ò®Ùd\> Ç˃¶9¸1»Æ6‡¾{5˜öT|MsH;êß°›‹„±S]W»nT´—l<]MYÎXéJ´ì¿vgy懸õ^ø§üÖÿÀƒ/]LnõŽ® }Qo愈ïù ®IPÈg½ P¼±¡Ë}hyÆ–é‘ÉýíK/ø B”AÔßèwÄYee}N"„­ËÛV•6„çÃï†Áݺû 7wa@‡Ý·yƻà ®×$ZG'=+V)ˆJ+a*ܰ}°¬ c&p·E$[Ñ%ÚÆŒ2ÑÙ¢\êW5FˆsÀÁˆ“–6b© ŽûîS½ˆR ʼn¸ç(nZy1mñÊž[ÇöºÑ§DºhEíô±ƒ¦²1„#FëvjÜd\ùÂVLŒÝª#†¨±ñÝ2ŠˆÞjKî}‹'ÌÁïOëÿ7êò4 Bqïñ *6ñQ³?š(gl½w¾(³+õ“–Ädð  ¬dè,ˆÁYŠ.¥p#¶'Ÿo0tŠûNþ)Qv7’ÃDom( .õý_þ[å ~öÑO,]ånªDÓ/{—’4Æ[ ƒ˜m@ $i¨ÂÄSë}o;f…qpó¶4Sî•ÅŠÙ¯ýí·~¶Kúw·É+’ˆREÚ5/Îùj胸ÏäÅôts ‚w»æœ/³½°ÓŠ f<û—Vß÷/ß-ðèzš§ù;Ï3™ÚsTðYá“‹°oOIÒBÐømsŒ¼ïð|ÌüVÐÑ–ÃñŠ‘GÇý›L/½·à„%b @S4z쿤´ŽÙ¨J;Å• H¤‹½9öf=ÞJÿ˜ª¬´§j£tÊ©G#M S|dîYGÄ€ÒDžë%—J<Ü 4'¼ü;Üû}–ßœâfĽòë ^žºfê=Ú”Gk^Ÿ3_Á ¬„5U…3ÖûÑñÒ¦3Ì+sûظq\ø—O›ï_U¥[Û6öKp_áKº @<âÑ‚P«[.B½–Ög•Ž¥«9µÄØË„¢¤QH„ò÷ý{<úý|ëŸñ¥Ÿåþï^Ìrõ­ }QoãŠïù{ì_Þ^cóÄ{_¹žeqFSH¶ f°Š-Û³y;ÀB„à¥l }tŠžîŸ:¡ö €–ÝWz/â7o¦±ëd§ovåfšÕ%Ãæ÷È®Þðae÷ÑdWkhòš^Øÿm™»œ8Øö9‚‰q xÏCßÛÅ)PÁ%´ÈbA…)Jº‚-ÑY®úY IDAT13á^Ç}GW½æL¨ë£ƒ'ÞxpÝ–VgÕÙ‰1«uw:F;Ž•Ö˜+]Ð.ÆŒ0BEÒ˜Ò5>Ô1b.ú(F"zÃ7ŽNúÕ„ÙjÌzÆtf?»_¾x¯CRî±ÃD,=ZrH¹ÄAZ3ÛOn“ “ºÎ‘­l,½õ˜³S×¹w¾Ûøþ™ÞùØL=GÚÆ›Hc(sVô:,EâÇÞÛNò`2߇li—ˆÔj°î7zôÈÓúÔ£]ìÇ¢ÙøÇŒEÆhÞÆ0S£ÚSD1JˆÅÖ.^ŸÕe3qD‘Úsó„5£¯ÌË/,…ÙÐÄZ°Òÿ¶J•¡€âšWNùÚšW©‹ÚÿVˆFŽÐíx¡›AxQÖ3¯£?”?þ¿ž|ð/¼Zñžo½üá÷?½>à)0SJˆ¯‚rŸ½ìn=ʬ¡C}ÿn{¢EJ?b`i™§çl!pXt\1-¹Ó±×22Ù?%@T 9€0"M Þµ÷ŠŽÙ‚£SliðÕÞ§1„”Á‰‘•gái#kEÖŠ…4Ê:¢ÊeÚHPî·,:­YMyAaM8ay—¹£Ü~•â¦ë¦Ðæ Éä¿ïëû§²èúsFxétïjÁþ¨^o9éê'&v*«{ª®‡e![1†Ã8dS€‰Ý>aB(4š}а¨„eíõ*éšåÿ1O~šoþC>ûßÐ^¤^Ô;¯.ôE½ë»~Žý«Û톜À ²›ixC}æ.Ÿ93ç`Ö¹±z&Å9त›÷SŸ»é[Vý3®²Ȧû›b¦CÎÞcÅ&[ݵRnb+»i¹†ùÞrNÏ&çÞàæòÙ_¡éw±ÓXFŠˆ1h¶ÙC0#.⚇Æ¡j÷´lÁT5kƒ±_±.8¨V7Ê7.Û{ÎÄu{e¼.ǧòÈñµc?³è|ƒ¢{µÛ¹&t¡»o㪨GUÙÅ®‹±ó#Di½F%Y®5À¸”ÂcŒŠ]vºìbP‰ÑŸvw–qá(^“ñÍc#«ë~ñJù÷#MæˆY÷åcwi²ÃŠšo(QvÜ 7kœ°ûÍ ¿6Í>8ê’IËB$vœLUz¢Ž¦ 6ؤ:Ó±NÆ”1÷ñ’_‡œC^ýÇòÏ«ÿ£¿fÄ€,pc:×o„È ëb€ˆ. B¦ {˜‡H@!Öv9½|túð¦ V5È~àÑûrç~ Jï‘èÁ寅*¥ŽèŠWV|kÅ·€¼¢9¬1uZe³C ‘à("Œ­òæwZÖÙk‚OþÌ7>xç„zþćcÂß+WK÷â‰¯à «}ÿ¯üè»×¶?6“E£ïy-¬…F í`¥Aé„×ÖÔ7GœH*…â´¿•j晴ÂÕ=ªTSÖ‘uè­r|`•ý¡£ÒDº´,˜@«,mGYxšÈýØ¿ÎJé6f>Ê’ÞÖXºØÇÓßqšÜû&Ì÷8ím¯IX¬PúWÒ={¹ñáÚ>gíy°–ÓämŸÄžÊ¸ßWºþÀXø¢2Å­5eGˆ4²« #ÂH¸¼VŽÎì¶5»ç¢Zc™Oeé·½Åc?Χþs~ù¼q1ã]Ô;«.ôE½]ë{¿ÉáÕ­érÒ¨™LÏH-á„€‹ر L1Œ²ÜpØ»*ÃómàŽN B•´DiÑtÆOzs—÷ÈÙF!jŽÈc”s:„ļϻ“ñ¼ûôÜ•’©Øöœ…ˆ¼õãdáãö^EÞOŸžáÙUìC–åíoÁ 8 ¢p”ì8*ú€ºi"˜Ò[M) ËÈãÓNGoìï\5ãrµW¹©£UsJlGÆW¦:)ŠÚ6Þ/;ïãÌÈ•¾\„îrè\iÔXm‹":c*뺠>èžWTbÔcT)œL*Û‘Âò /ƒ¶ñnˆÿ*ê­ÆÒ0z×åý_©äöý,ûKÞ¸i'o`«l²›Ã¸áæœÙWP‚ÁeHd燥*ÐŽE¢|tœ°TôۣʦÔg¡É<ÍdS老é‘+ôÖü‹ï¯?ñçÖ¢ sÜïˆ)æ>NðcbÚÃh‘êþ¡dŒxHH ·Ü½¯îsòˆ#bºËãDÈÖwõvÚwrSwõî‚ç–¼(xÙf+20rÞ¼«-]¢ Û—ãDvj3вB>øÝþå7f—æÇ|JùÈ5ø0¿ÿ{˜ißÑwŽø¦#¼„uÁ_ö_q£'Žºý®CmoÇá…•c­½ÆÖîF¼©ÈZxÁ3U. .}‹Yº6»Jˆ¥ªí±òœÎñ-ñ‘¹â#DN=ßò¥PJ¥ð4-WyˆùZW£Û}ŽÂ3óÇÑê6,É+§FPÀŠWŸdH)ºÎâô ¼5}Œzƒt–(Ì;œR .íÂx¬Á ½Uo§˜5®¤¤ïÄo똚™b„c“_³Ò«Fëübl~k§°Þý²'0±¼ë'yá/^LzõΪ }QoËzêïsíiÈð4D÷Ðyã7w¾-lãÍ¿Ü. Þ†7K<ѱމ½ ¾'9 ž‡ímÍ|èŒÅå‰m#C¤oonÂ'¶5ìRË›¹>¿)aã|¿™üqmî:‡[˜ŒÿJ0¼kÅeɺÀÄô(ÐQ¿k®‚©¹\!G<Ôœ\³‡ŒÁ¢ÀˆjJQM‹‡e±P3æJwi‚DLÐâõIU\¶•÷Y¬8*ë{¥y¼=-§c7:[g±F–Vʱ3ªâCÑ„èCt¢6à‚øHň$Ëíé¼®»h‚16jmüµ:…Æ©×øZxþ¡y<ŽÊæ€åÕéô³ÓòsßH½çБõ.d: (ÜÍ&´¢Sû/Q,4Óbû&;n,äLŒÔ¦¹]‚FŽ%´œ–àzî«&¡öLÚå‡fvýHM–ÉfûÕJ‰þŸ/oèûo¡…9nŠ/ÀA p?îG„†C‹#` ˆT§²gåä^L$ìÕ|ll~+ ”‘Õ†Ìèæ|uÎ7#Kp’:è%¦giIY ÙÚ@¨ +ÝF0îü4ÈåOéŸûÿÈw¿jh_ãc>àÃ0æ ÏP<$6àÑ€±ð4È‹Q|\Vá+WŠÃµ}ßÜÐÀZYb•é[2ðóYgf—Ùn[}»a~™Çãu…µ˜Hû>ô¦C.PLè nJ»êÓkV‘W~mtÒÑ%ÂX‘-5uw©|&„‘ÁÞZÌ'–ô §PB‹luŸÌpÒÝçPæv0‡®C‘S™c0žÃˆt‘Ðâ“dÀ® ‘Kì?ñ˜#éÛú}¥ÄKA*&ŠæB»!wÍ3ó&Qå«|öhv7å:(áæ¿ /êWú¢Þ~õÈ?ä‘Ïl³¯7ÌãxVÆ”0è é(W7°„;ßrf—‘›CÚ2†îû“m~Í7Ö°=³aq˜<_𼡬oânw¶Ì.±ûü >ïéawå’EžëÆô ¦eN«+ àIÏ“âÑW=Y¡oYÍÑuÏô0W¹T¡sŽ€"I W`'\¿ò…ýêõŠñÄ)î~U•ÓâЈcåÇFG‚N°/,§û‹SO'¦.Ä›,ò„hi*”‘ºûtmÑ–èm£ª÷x¯1JYR†"§õØNÖ£c,$–“W»õ‘«÷t|k9¬&ŽÞcÿùW X JÈãKè÷Û£À¾˜[ÒïŠ7óŒ¤SNJ T–`蟠”æÉnÐ §üú0—Ðr"ym{½å<¤/u#ÂK*µ”ž…)§¯ý7zòè{å=×çB È)ŬGA&"÷q‚Ô`ã± Fˆ È].ÑΨVÕ{‘Oj”PÛðte~k‘W ’œCËks¾éyÆôoyسßh1‡X‰+£ëð%EÍ(d"ÇF¯±ú«Ýÿõ×oð\V~xÆw×½‰â!\EÆ0‡‰¾@}¶`çG÷[Ui­”§µžt'ä^¤å…ô¥žôL€&oéÄA*Ÿp"Üžr¯âû„™é¿E1[ÛfLÍÚ²jhÇ¡ŸQEÐȢ㥎EÄ(§ë²ñ%C¿Í§¥ç\›—”!o(šhiiW­Éè9Q&Â`®jˆHDçÈ` ¶EXc*Šã±+´£ÛÇ+Ì&„ <¬ 5²‚H1Ãj³Û’RWa©+(Ë@gñдŒk{Wø¾ßMô6g{Üø ^û/f¿‹z•½ø.êíU?Éc1ú·m¡6Ÿ‹ÏÊ8ÐØ óÿ6µÛý×™ìsî7ĸ•¦™¯Ÿòü¹œ”3£3žz!¦Ï4˜å-:Ðìò.x ­áðfg$’› s—atÂiöªúŒÀÂð%7;\E,ˆ‰½aZÏкÿ(t{˜š‘E+ÖbŒÃZܹ<~þÆl9±ndÊê»<ö{SÙ¢r:±Ë 2Ř?ãd7°Rï‰u½J¯òªkk_µ¡ˆ6¸ÜúuHmÌÄ™IáÊÊX+Öc$¨ŠàœT…TÎ:##kJ¡0XçOã²)N»úe?½ë§.Á?~Õ¼Ôèò$ì94õ€µ·]Ñl8ÖSŸ¥§¶ú€—ÜN‰Ü–¢dl±–ÚPfi Åj~çÁFBò^q”ÈÓÉ|£d?ùt'7š}Ù’ÖÅâé!2”6FºÀR09úš¼z[>ø™¦ê•lÒb)‹Òâ¶Æ:¬Ë.{ŽèP‹ÖˆAîÜê>•q…tW¨(|Sþ“–žñl„RY¯øúŠR$ÑÆ[CñÁïuù'9“XƒuØWöŒjÉþ¦Á|à¯wŸùÉÛ7xãˆIÃÇßç±§p&ü"”IÙ Å,1±Vø;̼hРíÈøk/./7©Í<„n6ÀšõÆ¢ºÝÅ¡ã¾r¹bä áfÓÒQÔ¡SÄ à#«M¡²ly¾c C[BØ5Û l»ìJ8Rƒ¹ÉàÞ°&.OÀeǦÒy¦p+Lƒ,0G¸³2ÇÎq'Â~ËtŤcê˜ Å’ÒS¤€ÒHZ¬ún½ÒwZ‡‘Ü7X`:¤‹X é'ˆM>‹Ú­Àº_Å +jÛ}¿Âkûb¼¨wP]t /êíT?Å“m»ÙôÌͳ2Gê,­Û0í9·Š0üLäÊÐ­â ø6Ðl·jÒMì³Ä¶]B³ÛÕ>S1Ç 0˜-ÞʠüYÀŠa«U;²u—Ë1|ãEÆÊ[ërð[xï’+ ÁS_¼B,,Ñg'°©u™B¢÷98%XŽ…Âª±wL<‰rm½XWÌKÌØØÂ(¶Åâ(ËÞ6× ƒ:Úˆ-h¯qò*)ß›”é ™"'øÂ½6á¦c! øb°IÌei(Y!‹6´ú âu¥A£–…8ãFu4®}ضǞúå¼øúÚ¾wuÀèòã“ÿ£á…{±ÏI®Æ]$¤àëD’ÞÈÆ ľ]ÝÓÉHÉ`¥ÁeoŠdÁ$n°BqT.ÐÒb{ïÃHè˜ÃÌn³HÒ¨ƒ12¦iD$Oè’£ 5òÒ?°Ÿÿ³ãOüèÂâÓV|Äî b€€õPGÄ€„Å¥7+йŒ<Ž?e}›ºabê•<ùÁÍG¦þŒ±ÿgT¬@à¨á¥–;›¦[oìm±¼õWA"BKg0[S%v±B‡:äùäû?}çGw9Xñ±’EŠJ¸Æ¯¤"ëxM‰<…ZôLƒTØ… Õ4ìëg#Öɪò$ïÙŒ_Ý ƒ$½àVbÑ¡sÞ°ì_b&X¨ ^QeU²PÄ÷ˆù¹¶%KNÖ¬4[dnØ›è%?8_ÅAÓÀL=·PO–ÙU£pÉbþ3€âZÄ箾¡Œ²†¨„v+ê´ œôAŒ"pœ•i„9€ ¨ö&<4h 1.XêV[J€#¨‘=*¡M<}Okðµ·Í/8=K9ޤÏ|ò}àE½³ê¢}Qo›Ú g3ÐÌ ¡ð†‡à3 Þ„x»]¿ä =ÕfÆBØ1tÛé@3ØE•àN žÑBšáîFë9ËÝÿšÝžñù¶´;×f7®Üä™Þä]Ñ2_žB•»Î·`Œá*Lac˜ÁŒg×b8L2µ­À"‰•0Í$Ëb¦ÄZ¡3ô€K{ÄŠc»0åŠqÇÎ-¦ÍÈ-gåÜ'«I±.9®XXŽ0!8ÇÒ²tÈ7ÂÌeR`,¤¾ÞfL‰—ñæ#ºÎáKí¦Kbê• F\þ$FÖT%U%£‘¥ŽFR–b\ŒÄ&„q«²•ëÑèþÚÍ»ª^_¯ž¹¬ã›ö믤|儞3ÐØ·¢•mK1¥ŸÄ€´ûmÉÄb•Á8*‹KmÔˆ Ä”ä¨DO“:¯‘NñKŽI^z!Ý4 8 ¨ØËyãFÁâÌ–³‘$©½HQi;›æ´%üÁÿ®Õ‡íá»âØE"¦Ã:Ô¡`"¦EÒŸ‚hoBW¤¦{âöï£Êê%œÅa§ÒVÑVÜ(êÿ­UŽ;^òkïâ—(?gdÇ„n{Í™túmóø.© ŠÄ-yô¿ŸþG÷?òݯMY,Ð9ïñ|Ôp%yOó`Ä¿€";Ô#ÜÅΰ#ìCäe¦«hfÝÞ$NbðfõÈݬîe™atEÞÝjòqdíÞ AËsoMa™VšÉF©èj|Ú§t—=]dÕr¼ìŸû®pÈ'È$6ñõ¬Ü"Ÿtw§KRæÿ¶pwYšÏŠN±‰Š‰T±¯:Lƒi†j…iÐ]Ñ­(×È] KhéÐ;¦¨ò™*B‡µX‡–X?ø¸"¶êhtN¥¸”V¿á“éOøú[W8þå‹™ð¢Þ)u /êíQô¼Ùiõ»ŒgèyHtç8…² jσã3(6äCa‚—yÒNcßhÝMí>“ÎÍà)ÂàUñh›]lmw­64¿M;𱶃•CÂÓcØË(¹†K°—ïx òÎï>–'²Ž0#ÌKè ¡×ЧîqxÄxÌ^ˆ{ñê/ìÝüÝ“éërð|<|áCOüÁ»®¿&®[ê©qÍ•ñÃYýÊãûóqõPÊ»û“»¶zcÌiÅ©å¨fi™Y:æ%m…a'¸ ·dšÝAäÙƒ-Ð -ˆ%±f>"”Ôƒˆ¯$މÁÑ•ø¡&T¨#ýãDu±(¨*F•Ô…(ÁX-¸‚†õ©¼\Ôß–Éõ¥ñé“{Rͼ®ø°c Ñk "ž>%寴±§š%–Œ *ƒX ƒ³½Ÿ{ÏŠÞNÎ÷)*4Ð.y°¦S0KÑ΂Ššé`Ĥ]q#ÛÜFžˆÒu,$w|…èð_ùy÷ÂWËÇ>/í¥ HHrÒh‡)úѳIÏŒ]¢5r@¯ ,.LÆÐ¢%îcÿääOþ—¯<:}`h”§¼×ó,<é0®ÂM~)Éx4`:ÄcŽÐä‘SaG˜oS<쪑¯›4Èh}ã(}é€:Í¢:²‡O¢3蓯ÏIàwVxËþ„:ó{LM€à‘€¼çNGX³^n™%,3ߺ´–ãà‘5Ÿ"6ÒäáIcÓØœd6"Ènãµ¼õö±ÚS|ŠùvŠ·Ù<˜“Þ q³)Õ“o#ƒ3GOÛïÏS\µë&Ôa,Æ[¤íßš’|…5tKD³öDrŠM6ÞÞ—¹4ö¼öß]L†õN© }Qoƒª~ˆwÿÝ^Ã.#Bs ¹y‹û¦[ºËp£¿ »vuCæô™íaO:ñ .ÇÁÍt‰d—ž(瀻ٽãO‚óºÀó<æ!Þpƒh¶!¹yÃožÀ*Á4 +f'¹yÀ>Á÷ßâ]¢x#0EKLŠ–(3zf÷›é 0Sqû{¯>vé6âŽbŒ}bìoî]:¼»?~è;¯¾Ûsæ`:¦a§£ò°QJgˆuìRÒ†ÖH ûˆa¼¤®Ñ)ì¯'Ä)q+´è­¨—–¦2u…Ú9é ¼£ÓØ!Á—Ä - é‚S„µHTÕ„º’q%³ÚîÕ¦tŒêÎUwŠúŸwzrë •Å—n’·ÝF¢é:V¦GIAx¢tI}XR×L ¦ 0XÛ;cXCÆu ?ðlò'$â—<Ì~I8‘žJá EÍÞpd8J³måö×&³D¥‹¬Rh_ÒAb‰<üCyéEóØÇu/DPÄ÷C¦Ëë°uí=DÐŽ° (:‚kèK¬˜ØÕ‚ÑRÛG¦õ í §‘.[—˜=Fd'eã2¼Ð³¨7”hE"L‡›ý@ø‘_½÷ÑܾÌÜÌJž©ùPÅ£`Oó9ø¸Ž¸ê•iÒ"Ð’˜Ãk¸=v4´8‘±‹edô Æ*¯<ǃƒh”B`²lÀ V§lƒα–Ã.1åKÔö[ &âávËrMÜØh6™ª!òÆæÏn`¯áÏŠ‰ƒSÁðÌP¨þ†xl¤R\dû0*a#¥–~•=Jg¥ja?öŠb§½iG¡8(”2R µ0Rj³H:W(F³gI‡Sl@›^^ÚgTf ]®‰¡?7—Ù“»òzŽÁ•,NX~þbJ¼¨wD]è‹zÔãÏõöÃI³‘Ÿ{L“¯ôÝžÙí2å—sYiŽ©Ø$ã¯78Õž“â­óË»çzÙí6œéó‘Ýçqó™´¤ÖáÝ7]íMúÌŸÊÜuÞ\NÈ´äú^æ™k<~“÷^åûŸä£ïáÙÇxæ^c\ñÊ—¹³‡)1-‰¦—C1[QßU£TÇT¥z«uG5®ç—f÷®NB÷0ÚwO›™ µ0ªÃ¥+íUÃÇ® U(uí÷–ËÐ- j‚ã¡¡‹„ŽûWéf´û´3Ö†ÓÅ{™æ*°Ü£­ŽV¶Ê)Ÿ?ëSN–„qeÄhµˆQÛØ¬b,Tß;fh­°R€CÔWhUR—ìÌØÈ¾u3'£b]”/_ºúòñ3V§µûB£„Ž&â Œ@ m˜ƒZ¬@¤i9Ñìr[1­˜œÅY*Ca)§`(ZZÁz|’žzBδB¡%{%SKU0v}ãÏ)±e1árÆÖJ SºMYŨud%[ r¯DC÷àkñåG7?&ûû>[ˆ8Û'¡8ÅtHÖƒ1¨ÉÝC‡±°Ü„ç`é.ƒº.;®ê_x=q]rpãÆ’ñ z–sèYäMnf@V˜gÿ«ø§þç»ïºq€oqï¼O¹¥XÃMVc~*®Ác<& á=…ûè ÄŒ;LG{¯™PH7ÆOno²KÃ@@Næ+sz¸´9TöÜ=&(“W# ÜUže`éy£!BögnÐon#Úg‹Œ.CɸûvÏ›¸]ÝÛw!PN)”* =L’¬‚Õ-ÛËé6‡}¬Ô§¾v¡Ø€ ØÂÞÿ¬ Ìñàç9Aޱ§Ða{„-Ši0×=¢}“Þ$~‹&Mg“å’kd‰=BÀ–ŽÕß¿˜/êQ"‹úNWùS½ñ~„%Â,’˜|_çÜé¸ëÔvÍžÏä’lú7 LÎ¥[ÇL…4»x—Á~âðrØõ–ÖsF Øq@ŸáVÊ®ÿëмyÈ-ÙÌ—.7Ï6*¢iÐ{=﹞2òÀp«b¯F,¾dßaÇB ctʽ¯pïÛ”Žh²HÍk£ÓçSPŠŠŒ1g«úµ›WŽ.9Ûˆ­>upÚ-¦¶Ð„@ö±%áÑkV›îá¼.åxZZïÄŒï¼öìSTzD{Ÿõ!³ñˆîwñcV·°‡ÈŠx—æ)*ד"7Ë Ü Ü[­xà»+"ÑH'&ˆê¬”ʳöÁ—ŒÁŒ%úÞéLÒÎsCh‰+Ô£Ç.Æu7s2våáH:îê£/ú^úR~ëA$˜¾Ý6Ì5ÇþÚŽÍÆÓšiÚO¶©ë¬hjNGL ³¸H1–*mûçÁeEGi¤aõ¬[@$˜7ºžYm¥8¼”¿ž\w5"mz¢^ýó+þÂÏt5>›uïB–YQbòQvˆ†PEÞ|’ûÿŸûÊ‘{Ä!`êrù³{Åv—­ó†‘ž)p^úÚÿÖ·öQX"úñöGþû“GŠÕ’xæXžzœ'àòš½´Ã®ð6GK Ìîàî_Eç˜^%. ¢2-b)â#âHÃUµw›£:r6!-¬²âVvË:`LåSÁ×¾¬y÷T%Þ°2ܨ'ò^™ß]{‡ìò™*îš å˺{J9#MšfÆÁQRce@6ð4ÿ¨bd;F5Ÿ¥––2lÏ>cEÝÀ€(36©’ ’rzöH—ƒyÈQ“9|ÈDš¤¹ìO6X-¾£³=¨7Ûh0 ËßR.ê¢ÞuѾ¨ïô¼ò«šLëX„ £É, ¾ÓBOv=醜‡MÀžhìL6Ó°b`~òÔx>ºÏÂ"Gœ±hõQ  §ÏÒé€qQî2LØÜü.v{áî¹ÍT\†}¸°×o@{ÃÊQ—ŽVÜéxx¥ãÕ–Ó¯,jŽ˜SB…î§-[ÄAÁáÕ¯Uãçf“×.OöêõÓÓö{/‡¸g§î‰Jõs}– IDATŸ(Øwv‚½ŒyºŒ‰½Dq@q™â:åuªûö¤-YÏf2ÙÅ‘LÅEa/s«Â® -mÀïQ˜|…Ù]XÔ£ zJ˜öÒŸHº,ëÊz5#c¤´ª(¢Îie1(D«¾–nB!eH²:cö0û˜¢Ðj¢ã±Ù›¸+S7v´íF'·¯î¹ç¢ÜMˆ64,Ó.½A„Ðq¬Y‚Z3­˜&¼î(¥à …f"°- H F¼Á†¾kÛWÀ[ K‘ò #¾å´e¡Dƒ™r= 8*‹Habn?'NiX+A·=û¤‡½ûÛvü½îñ÷ B‘°E¯At‚éz ½¦¼îØS½“ÝyìˆWX¼KFÑbÒ¾kRt§òùµfÇðÌ—óÆN+:“¡ûòLäÅ,‘OüOþ‡ÿÊñ5·:¥»ËêºâÝ+Þ%€×‰¯¢ EVp=Eñ8¡*»Jƒ‰A´˜®j×îA—qâÆÂÂŽÊuîOÛ¬H6ƒÕuZÄz€¶¡.h"ãëð±— ž4ÛwlIhC·x`wݵ“×]çáêš|ßM¸ààz08a,”ÉŠ®ÂL‘IßH(dûl¾ž4˜Jí×T‰³f“·Kœú?@ C·B¢AuG+’úÐdSÊ ï÷̩֔kP°>Eœ»Ÿ‡ß¹˜/êQè‹úN–q…˜c; “¦qÓO¶ÛpÄ„6»©Å;XXœ©y–àlê­XÈ©‡´É÷Š»ÙgxÏ×™ÉoƒæLâú r8Æ] “ûЉÍ<Êl ›áø¦å!¢ «1_w4k[¤`âh ¾ v,^ÏÆ5²›úuuk[Zyâð ã<"Ö.Æny³ªÆeáŒ5¨…}Ì%ŠdIaúïD-XÔæ;ºuå‹o <Ü@>Áí_CpÕ™•ŸÔ‹ÿà‘ñ—ò«­à²nãùØ´)³t 0úÓ¥)àG~ýñ›iŽ ¯Ó.(Ïθæx¤ãÊ]$}—yéˆ/Íq'Ð[h0 t‰vHDäô¤?žÄ­v.˜j5îšêagX׳f7£$hë ”Ël—¹„%YNÐpd×ÓÈj±Ü¹Ky‰Ã‰<Œ»vÎÃ5°î¤™ïxÏmFÄð™•öê×àÆbz†q4ªëpsˆ(,)O‘å Œ~P¡2”¡w%™' í)ãÖO‘AÇàÌëݪGÝ@Ô(™ß38Œwî˜^-eE¿4t ÕŸ½˜/êRè‹úŽŽ¿½¿ë+Ì1+œÁDÊŠœ`Aß|‹nÀ~ŽƒN-ƒ-ɡƊ|ycàï3º&ÛÁÍÌ€ ²¡*ÆÁˆßu7–…üJt°!*Òöƶ©Ì:¿ôSgG-;èmË ;^åÔ±p.(±ËLÊuß/õaä8°\ŽTë)Á²þÆ‚`*¿¡aŒçK®ÍÖÖb­©¬½R•W'eDUDf„±P 5fŒ-1ER®bÑËÌß=]ˇ̯[SºÚˆ›X³ß…=µã’k¾§,¡ø"žv.P.¥h¥8%¬0'˜7Ð1v²ýÚâ®=Â(SW²ãq°ùIßXgi+Â7AFÄõxOÁúô§U!âN^›JSïËïGHÑÜÚç…÷#`Ìþˆ}×wŽ­`]Á)€Mý¸HHž6à›]¥ûq¤hÈQxBL:¼ô°I‰8አ‰÷ ”Ô1D!HoGÝ)]²é˜{–‘VñkÏ:Òº@Œ„†îÕ×âû>Ã>6öæÉ ÒX¢é}9¢#Zbèy)dA¼‚ÌÐË4¯S/Û®ì®Öþ»êâ¹}±£9K?ä:s®- æÒ÷óc¿4ÿÞOÚSÚçiî²·æýz]qó ¦_UVBÉ7~ñ+ÈoŸG_DŸ£ü<‡_Gîïâß@_a|›ƒ–Ñšª¡êÚ«½›“²¸R4ûåj/,­<ÔÞ¶{½q0†Öp MÏÚî –rydµçôß1)‰†{+4æ³DØ=9ø]¸ÎqÀ8L8tþÙÇ«.⣈âÉÎÞN("±:Œ S¤êÅž:HNº”œ©åX©«sÌgqžb†l$FF– ¢%Ûµú¼§9÷§íŸþ±²¤ëÓb‹¾}) p‚ &·2ÆN¸Ç8ýßú§Ô+;2ª«îÿÉåâWºe9È`Kæ¼#úöHkK×÷oÏo>{ô*í)æÖ·âÓû÷fübƒí C!¶Ô/qí˜Â`ŽYXz¸H-ë}ŽoòàQi‘Ñø²ÅÈÓ•:¯ÞŒ8`•³™Ž`ž}Ö7j„”»-Ï.O¸ßqïˆ{Çt#¢0žý {­ç,Ÿ‡çŠ3py¸H*ŒÃ®v‚m`¦ÔàÁäó:¢` ‘5ØgcÅ€éÐm±§œ:*¡ph¨4®ÃE\À$O™x–ð¶uJ§7ÊÙ¶‚ewùQòMÓR”tI««Äð‡SâE½ƒê@_Ôw¬¬üådöd•"àšÞL!_ýÁ(xNòľm<‡<~‡¾17{6ùE~2éiÖ}[÷MÑóæÔ_gä ŒažgS<`7x¼E.w€àxÎä.ëb ý‘éÙ÷ †Ê^U9+)KŠŠ¢`âúDŠéˆi5Ke) ((+*‹:Ã+⸘j}@Å´UÙXêkU¼^”ç¬5*NR²ÄîãTý|åÔôˆ‡@Tp‡VpƒP¬ídšÍ_ûôF+ÃMeuŸ“Ó;=zFo>kEàÁ#zz•§•Ñsîá§é~ ÿcŸ¦¸Jµ¦­Yd)è öÙÚÝn²j¢í‡Ì†dS–˜Qµ–ÈcØ ½OhѲ‚Üåq7¹ñàøßþÄô‹¿(„HaДX<ÎèyÃ#u8³ H( eåé 5ÅGš)˜(^QOg)À$v5ØD,ñÄÀ:â•dyIL©D‡KÙ.Ò6ºÀ"É&]cÂСO’ÇÈg M~þÅ?kß÷zù¡ë)“^ZŒÇRõøG:ì)n„7øÔyœÀcØ#bGŒà˜_á‹/UÏÂ%ˆóÏT㟹Zþø]=»³†¹±éC“ÐóáÇùÑ¿szõÙ‡¯ÒÜGŽÂ¤¶ïžðžÈµÈþQ;kµÞ×áöï}™Ÿ; ^ÇFb„€{™Ë÷©]’ã†K–C»ž±˜°Øçd_RPßrB ¶5èD¥RN¯,àV™Xuf½Ü Çöp ¦2~º-1Z ¦D„qäå%óû ¹cm¡9ç·c3+lHðFñ%™`:8™äÓˆÚ^åŽ e– FÁ(Sz­e„ÒcL?R­Á•hÙ» FeiJF5¶"t¸5‘–¨´BpºÞLƒ è ÍI÷Ã.Ý5æ3Ãw™Lbz®=JÑbJÄ&kÆ{ÓâE½“0ÌÅGpQß©ºe~£° .`»¾ñl 3²Œ²2>mÒ§¶çŽ ƒ0BÙÓq0/ê.g:u˜ÌàÊ4=5žÙeH›l|áÖjgö[å\¾ ¼Eò6ƒk†vï²á–L\jˆ†Öýÿì½YŒmIz÷ý±÷™r¸™w®¹»«znv³%¢E·E‹ÊiÚ’áûÁÖƒá7A° öƒýbÀmY„d MS[ -NEKÍîVs¨žj®ºsޜΰ‡ˆøýçì“y›.? …̼gÜ;öŽ+Ö¿}MWãSf옎˜ÕL*fŽiÅlÆtF=ÁÔ¸çg|ðÖeÞx6{rc÷ýç§þδ»1îîÌÂÁ¸rNƵWfêìnŒa+LId³C\¦-C,bsÒ‡ÔÈ(ÛèM¿w¶W ®ÆìŠqËhE]'1¯ ì>q_š™×”Yèo.ÛÃØUÍ’ÐsëÚãwù›OY>Ä câ1æ}n=dÇäND„›¦ù8ǯéñs‘ffί‰ÇDÄ#i ·‚HG½âú1/ŸsYôŤy½‡“ÚšÁEí  ¬à¥—×áÖŒÉ.ÒZe 4°*ð/p™Krç¡Tší6ežÆÄö=dÐv¬©¯üçoñB´ˆÁ¼¡ hE´¨%¢lZ×[6=kB=£šP°c‚¡whMÓÎh§Øk˜}8$Þ ÎˆÇÏúÌ—ïõ§ŽƒÌò¼½óŽ¢–‘§„æ¿ºê ¼ªP]è«úpjÿöÀþpŒh–3$Åo=ÁºD9õùx‹ca|ŠUlÌ( s‚²Ý¬c|- {Ý6´OOaVÞ¯{–¸9ôqéп q¼œ˜È³^Á ~þ“–Oh]é1£œ:¡‹8‚e¡Ì`ÇP âCå°â0#´ÊšÙ÷î3÷9üÚXªÑïϪÇ×F2®LeÍ­I=­5b©¬ìJåA-R4&j_W -›VBµÄ•ò‡«»a…Qd…Ìq-N°B¿Ä·,£ûîýG÷0Àú™Î¥Ý—h¤«8¿Ëî\ë÷@„»>þ°}å}7ù O§HÄ;Ô°_âËuÏ(Å$eóÖ Ó•ÿ²[ň°:¥óøž¶§ؾyú§Ü?þšÒU¸1»Sƒ3YâllæžÅë Í᥉e£=zrÛš¢‘>”}%$ýF±Ôˆ’§숙 Î [SÓ{âý<{îgxîN ˜üùÄ€‚ÍM„xÔ¢ŽÐuˆÁ´Äÿ5‚3ƒÂ>¸ØïvŸ8°w½ý¿Ï×vu—¼8( ò¹ÿ²ûÂ_|´|îñ½föèìKËù¬ym<½Õûývy»YìÏ{éZB`tøhjùÛÐ3^R?b´Ãø}î¼Ë‹Â¡ô·eñÍ]Ξc~Mº‘D#Ášù¾x$B ÃèçpŒYÁÓ²£ÜPÚÀÉÚŽƒ"îrÏÝ=§ó ÎÊã+¨¸=e¶“ G†EZ*Åå3õþpŸê‚Ç¥^ò|æò[Ÿ5WóVt#ãN£*m¾ø–¾Ê×°Oë0蟶™*¼‡Vk¼d—t“I&tQè5DÉ]'è]Åm~<…Ô'…Iû{Éj¤¼h·‘3YîûŸ¾š¯ê#TWŽ«úpêyþƒkÊžÏvÌÝ0àÏÓ—}÷®Hç=+“QF~\©FPjˆe“*º*ˆ{2`v=Œ²•[V´…Ó —n,¯9*ùÇ%+!Œ¥×“ÜÐÈxýsšDÍö˲•²n:v4º2% b¤÷ˆRWXE+¥JÖÁBˆh  Ù¢l>çÉÓò¶iigòAŸúîÖ´º>3ÖŠ©¬qFdã+ŒGAmVçî)Ùø'SŽLa¦){ÆÃÛ{|p†Y[Té ^1J»¢Yò`Î{¿ià9¤BAÿŠVåX­&¬~’[_ĵ«0¶á[»OÿáA—ƨ2ûþ0ëU—ƒç.'ëÐ_ަzt€up9'ðÈ.öîÌ¿rí;oâîìOý‹;¿öO,»g0‚ÕL«¥p‰t”Lò©H^žFñ6ÛHû§Â˜Ü˜¤§!üª½¡ c6¡ËZ¹«zb±B§¬"‹Hè 5H`¥Pƒ „äa`°òúO$Çz¬GdÿþóêÎÿT¿2écrx\ÖoTÄ*‡³˜“ÒÓ3gЧèS4¼mPåÓDñ“êôç^Û‰Rÿå÷u0²%K8TˆXâgÿöùŸ8~CvçüIâMºƒ÷ÎnéÓqôUÞ}‹³L®?˜~Çø¯œ×ÕÈD1zdÂoÅ*Ìo˜ù®o­FbÀÎquÑEt˜¦–P„#±D™,‘U¹‰BŒD~4r(ünºfK¬ÉpÓ¢øí´%80²”^3½Ep0³xÞñ¶p,k7°BÐÜÛ'‚Û¾º‡±ÞqÛÉnÛHî¢Fªá]eà’‡ZZ@pf+Jm–=QA$FæsF3"TÉb¦¦v4+¢G“ü{më]!7ã ο`Ü—â™R¶¦“’þ_bbm‹øÍã–ò}Óf¯êªþÿYWúª>œªx÷ZøDš¡Fƒ´pʪ8k´¼–7³­Í€ ôÄCþ¦Ïb`4ÀéÕ€Ôº2'‡n»mh1{Nlë)\· ïž¹!«vùBrá….íïVæÅ¾¼£¢02ì)½„Êà•> ÖeÛìi‚åhÁ"P™üá­ó2z„•¹È¢‰*ñµkFUDƒ3âJ ^â»cβοê e¨$-ù6«‡üÔãïâô<·Ø«-Þ³\qÖòÞ«¯a[äÛ0C]Þ Ðqþîªpí¯¢£’¶&/þ7Ÿzañü×#±">àÆÌü/ñþ”ú€·Ùê²MàËøH¸9n˜ É@Ý% º$Xâ=­Ù«käSŸ¶ÿä-š¥`,•ÁÄ` b0 JÖ‡äñ % Ä/8[²04#vL^ŸÅB5j,Âe›…Î:DçSabA,AYÎ=Dƒ¦†ŒãMÊ|‰ XCéʰòZº\¼õ¿Ä_ÿÇî/ÅY–póbÉx¬["WZœà=1 µÈö.ÒbÔòÞŠÛM¸c‚ £ñùÏ~f7Úú¯¼sI_% ãõ/ü''Ý'?ö§?®ÍM§;ó`²s/ìù fÔSÕØohýþ²y^⾑ePL4r¶ß>Ù3 8Ì« „4`F¸@Whé1+ô 9‡IÞ Ðˆž@Œ—-Š_†%¼ÞÁS¥k0Äeñ½‰eß"]€uQ-·|ù9~â_æñSÞ{Ÿ‡GDåöˆ];DO]b"»;,w8ZOú%¡+xÝ—Ý_xk¶cÙŽ¼pÓ`й1tû)H6r ¥A5cS—¶ª3Snž#¡ñ´ç˜1®Æ+LMehVh—›×7$áœä,ñÈÆ)o}Û3gÈY¾T”ͺª‚J·„-­¾s5-^ÕG«®$WõáÔ„/^çÝ€o5cèçGB¼fæ3•@0D3ˆ·½˜b`ê »¥Šf¼"ÅW®PÔ®xĶÅä Wer•!=ül»|pÁ™`;ÈÐ ~Á´ØN˜’d,8œ`„ºÆª ,b±ãP…ª¿ÿg ln»Š®êeï;‚Ý3Õ¡³µ½>­z}T¯Ñ:Ù‘,ð°­Âú«dáÁúï†(¨euÊü÷¯ß{[#>Ð{úÀªa¾âñ‚‡oÃëˆ@‡.a5Øò6[ËÙÍA&ÖSÅéìp±ãÜA¸ýÄðÜޠWúütKÌn·5Д³2 {}mÐ9ñ”ø‹ðú“/Õ÷nêU÷š»÷5ÇÌQ§Oa1‚˜ mس*þt’©#aÎyO«DO§xS” š%Ô"Ytb²¾ÜY¬ÅXLÍ(Åk[¢ÐöœôŨ.â#QJ‰`La@ Nóf{² I HþîÖ0«7ÿ/ÿ°}é5­° sSbM".BDÆ#Kä)q â–àñ·‘;˜Ä¥¯Ï?¸KãM”ªÿØ ·gìo?^gq¤Ãnþ-9ü‹wík//Ÿí/vÍÁérÿaø€qìÄø_"–ÉáÑäú7¬;¡ž°`<Ëi|t‹ûæè]桦Š,ÐŽ¸$®ˆÉîæžÂSäY¢ÍfãA:¤G:HZdôLW<]°\l èà ´Æw…~Në²5ªV~ê§¹ñ‡û|æU>û2ÎSÁΈلÙqH…8¢pÔp83, ]O¬@(6”ë{Ü3íØ¾{ØÁpÖg%@Åm»=“ÕEë›[({(é×*5nƒFðÔ.rÄ¢–¥'W» Koé”öˆ¬B¶6êgÀ,ößËfÖ{óÙ ,ùjÃß¼š¯ê#TWúª>œšóµùª Ü mñëÉÅVXCï‹èy ‡þ²íwQÁ­Ñ«ƒlÂ8h:ÔíÉI µ,{²2Ø¢]o›®SÍêA68dØÊðCšmjW¦s[´Ú~+W¤6ìWØ2'M,1q¤`„6f“».ÒGDxð€f…QŒ­­<:°n·òÆøk#{0©œ1Öˆ*••‰1”¾·ÛžðÖ*ŽÂI'0Ýyûéþàwq#¢àM‡ˆÊÙ’ãOæœ}“ê,#{Iε]F—Il#‚TÈ ©03$éYAúÛÜ|àk{ë~Õ¼„~Ž“í öòMÌ^êý”J†m÷“M&{ gÈ9ñ÷–öáÓOUOnÉ#:ÅÉçGO^7Œ ¤…‰Ù2nñ‘F²è"å§Ä9g=]Èšš &àㆷ^/¦B2­s™ÛNúã¨K{XY5œÚHXAŸNµd4o¤´l FKÏbjvLLh2Zkج…oümÙÿŠ}åc[ãQóPޱl¹{´ÍB€¨H‡>¤‡1ï1zïá]=½FoѪ­{÷y^üA×<1ŽÁªûŸMvîóãƒhæ/5ÍÁÉrÕìžÇê‘âÁc uÍdÿúÖ/ ú—$\£«9ÝãdÇ»œš¼HñH› %£E¤Ez䔸ñZd:Ó# Bné“iá Ó`#»snŸq s[4dg.×УhJï ™åýâÇù¡ŸÂ(/Üà…Û\»Î /²7ãä„“¯ôÂÂÓœDæ=quI¤áÎ 6µd{é'ÛünK¨ äáH7ƒ®ÝÊ[M*"#(TB-éÎPB¹M¶‚­²”9‚8¢á©2W‚Ò:‹‡POЉù5Âð³ ÒBóŒ»àÚMtx¡>Ὲ|ýjf¼ªP]è«úp*röÿiâzÜ Æl;,saÖ0˜Š*B wÙ "èõ„ÁvFwBŸ‡ƒÙ‹âK}ñCã¹8ˆSY̽V<÷Ŧ*=e£ÊtåŠÔÖ<Ësº|‘­{3 ¸AÜ`´0kƒ†E¤‹ì|d‘|Ë">Ò4bïé{Þyi©m‰WP¡¹]¾¼X¾¸XÝ­¬oâiãcT­Œ N%јhò­R’îÔÇÙ« hÌ^ «€ûj÷§þé¯cªÌ{už „H8k9^ò´áþFßÎRƒö›fÙCç²¶Ò!‚¤ô„ÜGÄ¿GÄÝ™Tg æ‡8눂ÛÛd ÛmÅè…°>Ž[Jù³À>E¿9w‡£G/W1gÄÛO޾+99+1L†¤¡x6G²rcÞÓJ L_¯DO«ô)s{­1¹M³²ƒØlxb$wm­"ÝŠ£À2ÒCX€P`tY̬ãZ49¥ølÊ‘œCÄPY°ÄŠðø>ñoº}ÛËuQD²aBm 6hĔŀ>¦ïïP½ùt*Ë[¬ö´S¢¨LEot2Ó»Ÿ¯ž<”|dÿÊKÓþcN^í—‡§Ëý“Õ¬i\Óq‰-²¢¦c?™>¨ÝÓ(Ëç9û¤<~™£™¼;澕ÆÐ ¡\=Pž!ÚÀ}ÌSÌ cÁ£M‰=\km› ‚åÎS8+#íœÀ‹‘™å})èy}a®÷„I¹+žŸü3Üz ZNN˜8,ˆpm‡›7×<=aÙÓGzE……„ZÑ%º(¯!Z†½q IDAT#(n;ÙÉ%h-7ݦmôÐê ‰É Vé\TQ§E•!»Å_@·‚Ðw˜ÑM^:[¨-«Š®ö=òtƒéמŽ)<Åt˜Õæ#°íx4TtG8á¿W¾w53^ÕG¨®4ÐWõ¡Õ¿}‹늩/ʈ\3C™qNk'Ì"²`9CÒ3‡¬Lš«K¡ƒÉ×ÂîÀŽC1 fàâæ›¶lL^hd; aÝGã/™~˜ïïT%ƒáظkAÏ ¬“+0ø5 >ÂÃ#ÅFBOeËH¤]QGêšMÄwtÊd‚Ký…*‡ß'»Î›ŽÖ¸E;2òÅ›ÓY4§ÎïŽÌf„]nHnÊÞY6SªË·_o~äk¿A„>zDé{ú€W–=A™¯xó„Ù븓`hŠ@$Ÿ¦h„m桵‡ï¡Äà (ß|InÁ”ê®þžkÿºLg¼eÑŸáé'²’Ô#T'ì²z=JÂ1¾¡ZÐWᦉFFHÃê9Æ÷îØó‡ÂHò÷4Ò ¤sw,ZÚ†¥Å•8î^K[ž¡Tñ^˜šœûí ÎálÍk}H¼ÒFzJV†CâìqÁ Á@Dp!rcpÉÇ:9¯ôĈ·ˆÅ<ùUó‹ÿÎî¿þóæÅÑ2f;±æi±ÝdßåUi@Þ¡þ0‘`4ª]ƒŸkuoiivdõ~núZØ™½}rr·í¦]Ïù „МÊRU8AÎõsi5•‡zê3[æ3)a£ŒÒI®´$£â4àc¥Dæ(6àbÎþÐñÐæ¨tI!Ò D¨…W–<´¼(¶n†ã-Ÿ{™O‘*дôKž<à¹ÛyM¹7âS/q¸Ç÷xýÞ;£s0¢î0ÂV+BÒ,¹m¬¼¾ [õ’YG,‹ÄuÅmây}•Áx&ºœÐÓEjÉR›áS`$¬"ýŠzoóRªŒ,7&œ-‰CØ}½‡œ4^å.»N»²Ûסlßÿ|¾ˆˆüŸWsâU}´ê @_Õ‡V÷øŸ_åÇf¦1ôxÐ _ÁlÅ\ L#,hgÛÞLìº.`ÀôT˜šx¹ƒø›“kñWg“0j…£ðsVŸÁÔ[¹Dâ™Á†÷šåëÞbù·~áôàôõÏ<@Þƒ9Òc:Úó/üÀµ?ø¦9}$T’©óúõ¯-«gŠéhjF)`EŠK Ô† …¤TLk¦ —þ“LEçt“Å!­Ò%Y…æØ”´%¾AUƒ ìu´óZË‘N²y¶–jý«G…X¡÷ÿ®ý%ö~æ¯óâl©¹aÑÙ¢Ï -žt 0ïcÞ\áŒh¿«j#¢!8úžp–r¤cEû<ÑÚöé'æç`±#okc{cq˜?Î{›E¬¬½U&àшôÐaónOì0‰0WDñ=â1«8Åj–r[+&à&­7 y} JŒhॠoŽÊ0IÐ ÎŽË×s^û âXœã[,CÏs·‘ˆxj¸¾Çl‡ë7ùÆwùÎ}öSb{@*Z%tH€&Çf^´Ï^“fpX Â`[åBn”n«¢e{Œ›APyú–JKv¿(Q0T51`ÁÕyK%ôˆP;Æ–ó¦ô ºKxŽðí­ûC'ñø¬ÖnVaÅï\MˆWõ‘«+}UZñº¼Q¿ Éîz5ÈФüÝFY`-èÞ\ŒDËÌF=HÁµ%eý°õS³J±¯Ò—Ó˜ß=ô0 a=-øÁß/[jTkµmÉõÚÓ×yÎ:Õ ô+¾Lêé-šLK/±Œ#jYŒ°ÂÄ2\®¡ïQ[L‡ÎO¬1±è”ùù«jÏÔÎÕt‡Óh\ŒUal¬¢-qETâ,'?Ç´;=†û­wÿÄ7~ ¡Š$ë+ ‘®cé™÷,§-'gÜ~Ó Jîs†™¾?–­µ˜DŠzâ#<„ ¦!´ìBDN̨¹ÎÝfWžŽhï£ç„Ooå É?¢kHîÉSB‡.™~¯ñ³sÌ òr†i+ƒ}åö¿ûvù¾äuŠ/Ž~ÒÓ6œ­»·:šŠ‘ÙÄÖ®ÁFz‹«Ùu9±²8)¨ºôq‰ä0í®D¢¤‘ç™É®Ì–ÝrV°ÚÃ\ ,`°nCŸæh˜”hXÁÿë~Y¯ýìÏË+ãN#bD"¶G[\OìÐ>ïÒWG\›i+ï^P>Á£ÏŠWOtHä <«`Ìj‰“ýß7ÕˆéY~–æ“BM¬³ý`$­’w †nóU&)!ØÚ#¡\Ö=Ú´$Ð(ÉûyÍÀZE¬G=(N³¾g½IK¼ÂˆH ‚;ž/W|Í«®Ãá@‡7yá³,—„QbGK¦û;TŽ(ˆ'zvvxõ“˜=Þy‡“c¼"B=b‰zV_ T²%üG h¾Ðžá¶û]yðz€ÛA»ávW¢!-Îlþ0Ø'op)9³ÃMqcˆÅf¤˜:·Gôç4÷ðžqE-7¥‘Ä ¨|Ù† GWâU}äê @_Õ‡VžvÂ×ùòÚò9Í MÙçœ@WZ æ×;—PEvÏñ# x‰•Ê|pÊ40+IvÄwC¦‡‚žãvˆîÐmJ¤u*½ôÃå zÛ ¼>̳¦ü ñI8[§ãâКyBD"REjC€…P˜ÄÌ:ë(“|ÉÊ!ö,Ì«‰~ÄðÎül¾>2£‘çô¹Ý¶ycÕZ­*±¦/¦fÞ ¹ÿè_ýÚ¯ãéCÖ§˜±£ç‘^AhVì>`üLÎÛHN|¹wp ŽÌ £»°€G„CV3‚gï ˜À ,ðàyww)|«B"úmbdu‡ñ¥µ ªk  p ó¨Ýó=»Ì‚±1‚iYMÐ/~n÷èsã·ÿºÀ%T薜Ĝ}í%v¬*FBúöuÅÄR‘QM†pv£ßÈÑrix)q½tT6 3­“;É€X ¸gk žvy PbMRF8äñ/¸ÿÕíÿÙ¿ºxq¯s(XE=Ú;¤DdEHØÞ Zð÷®&Ä«úÈÕ€¾ª³,ÿ|ÄBiêËl²Êà0“ÉQj ÓÈyK7áE–j*Z—ø‡uù€2º4Ó U Ó©lÌžÙ¶A ¯ÖupàMt^nÏ‘©sW^$áûñvP0Rvà‘'Ê»2ÿœÀUæ`G¸sê1gvkF†ù Õ Y‚!d‘u 1uÆ­Ði`4÷>8þJœ®D$ ¸ó®«O0©º¹kÿTêûÆú±3ÒÜßÿ“ßù‚aTƒ§…PѦøm°¶$G,¸yã·”6kåAvôØ>’êi"¡FF°+â1>àfØy‰¸)ºG[½òú÷yã9Ì]XnÀ‚hb.¸ n¶À[4`Î7[Þ–EÆgÆUn²y‰tSŽŸg¼ÿ#;<¬îý†`#¶c XBÉí•a"ŒÉA€jEz¥O$±Ã9*›IbW\5ÔlTÈÁäᥠ5“<½Ë¯c±›1'8¥'”©Á†œD‰` ^³u’y‰þ-þÎÿ¶ÿ£'þK?}4Ãwè"ï¯Å€®ˆ *õFN” (æ<;iÁ!+æ·ÐY2Úû:a°ËòsrzM:´EÎá8/¢pÈNró(§ÈDÔ#©qµC-R<˜“ú9±ž¹ç1nåh öH@"ª4Ê(fAºèF*¼Q]in]MjtlÇ+‘ò;îÀA¹{„ÍÓoÓÎ ž‰Éñ+i¹#²緘í2=ÀòMlW8±ìÝä®çýwé;eÔÒzÔP[hY¶h]æánÀ+ëCë ­©/,ƒ{‘̺ŧj1o7V ñN ‚fpŸ]Ó%¡2¹hé«ZÙ!N¯¾vÉA¿@f/ß¼ý¶Y#"´ß' +lÚù·^ÜnÿQ· ž1É%Pýõkjý§ÏϽɂl9Á?ÁçdEQµK´§¾OT‘”6W±¸ÅÊP=e¶ƒñ5ñ32–%$)ó!I¢\;åJ‘¢Ó- ÅT'YŽkÙBŠAL¹²Ô em¡J@©4o&„Ž(y€Å„¢ºVŒâʯ¯œ±¸ÃïÝ,§ËlùÄ©¡÷H DLŸ{ã`±=?e±`wŸk7òÔƬ{w¸ÿ€E‡ÆŽÔPèXzÔäuˆ®Í¡íCÛ2r¥Ü †fÛSH.©¢õ’êºü Åb&Z2sAéçˆÒ²|Ìüþt`Wl­õ,£ õƒÓj˜)× €é a4`§ƒŸGð§W³áU}äê @_Õ‡;þ&É+·ÂDÔÓì¶–íõ۾̔¬À¦ü“:>¯°–0¼AB/γÀÃMt ´¥…óg}ÄDŒ/ï¶Éé!“}ancÛ½éÂN«À vÑâCª)hh£ã@¦™RÎ]áêÍf┞>ð4ðâu¬ezë8ý&¶ÅUyw°‰è)ªÈ!4C32·z¨üâµØÍ² B£9û1毞¿Îü-d†Ú®|-EV¨Gǹ·)¡g »ø ÁØÜÂe4Ë{똹g[NkVª+ ^Ðd˜à@ðØ0#·ÂõÄl…aú&Ÿ™ïÜáÈl – 4>ðæSô]ªÿáé§ç'×ûß|Ù>A>€yŒ8L—iN‘Œ%Î÷ioj|ü)}ÿëæá× Š»à|0±Œ• Y,ã][3±k³–šž :D‘h2ÎCÛ1޶±W¬l…×§cè±HO½k•Åõƒ##ø˜Sœ“ ‚ûõ??û:ã[ÿöµÃ?Ö~~µÿjÛÝ\‚檮êCªÛüø”!B´à°ž&޵’)âÀTëÛ~É«r¯¶‰A‰˜Ñ õà9©H`F%˜ÇèËÝX@{©5^‹jb(’Žƒç†íøÄ áZº­a›–®K†pØn z>ùAêx¢´Û&µf@H(™r2ãpÊÝ]¢…;ìîsöóvÞØ‰ÏïDG¼†qXrÇO¼¿›Eµz‹³û9é fІŸõÄò Œä,F)mLQs÷YlÑÚä™ÛzÜ1@sS‘)›Å-XE4ïƒk±ˆ“ˆFŠQhÀ J×2JØh…íq=úºƒÁrðøÒ§yï+¼õ¬æ·Û¯úhGøç¼ôþ²Ú}ò’=‡§èò9FñH@(ý‚qÄÍü˜Ó[øÃÜ{ÿ…êõ_.ªh4ï{'ž´¶Ï¾¹fZ3³8Á% ´ÉO\›Ó)ô‘º˜Iè`²8\,µ§o¨#m Ài1„&G¨¤Sš›«l ’Ë DÑÒ«)Š*‡88ÿõùß°oRG‚îíw}\« {ÞÐíÑiqePÞr¼¡ÈäË·ü‹Ÿ;ù3_î–¸CT`þŠ!>çËñ´`P-€Ýf­Åp"_‰þZÙØü–ºÞ ÃË–k¨Hd¾èæ‚K9¬ŒéÑHk‰BqžÝ?ä ×øõ—¶IhXÌшXÔb¦º^Ù±„ÈÓ#NŽ©g˜]܈hØqpƒãcŽžR¥ý…éYÕL”®Ç÷[ÖÉ\Ò‰ ÕûÍÀçBîi|VÄéð/Åù.Ë‘“Ÿ‰%½JìY-Y>"¬IæþûˆÖÖ!Vý [£Ÿ5@Ún´ÜöÒSëí;üU]ÕG¨®ôU}h5ãŽÅè‚MÌ:4ÀDÂÃÍÞ5K;ä™$j3‘CfAšÇí%K†nŠÙÁ÷68µÉ gyrº`[z¡.°ÈñY<©Â³ž»ž]Ñ=Ç0cÝB¤Û1¿…ÍÅÁx˜¬VµpØéÀ͈{°cÞ¯ì‚má6úoÜ9ûûLž Š9.ÖhS Ä{’´·}„ލ”>À;&FÎß ¶ˆÍNæ!ÉÍ é’YZF×äÔFSr¥]1ŽàV‰RìŽ= ŒP“}ÍLË|†«¸[œŒ£Üú6ñN’vÓSý3^¾ÆèùöA>2Å$fÌ~@ó«,ßfñ”Wýá§M3îÚ, b2Gž]´Ð‰:Müh‡X‚eþÜͽ{7åôéÀàÁ ‡adð† 1«™I>Àbp6çÁ©f×ç¤WNá=9~E0ãH qØSZ|Åž Ž¬}pd™ƒ#Sà•€ÙPï’ÔàŠ±!!Vòëäxö@ìÏ’PÄDbGùø=Uqðè#Úã8ü.?á¹wo´®¥ ,Fñˆ/mpƒnãDoûŠ÷,ž°|Äl—Ùã1ÓšjÌhBì‰}áàQ\y}]ï5ùÍûQï&ËiØ1,ÙÆÐæÒÞÀmZK<”¼ÚSš'Äv»Ùcý‘.ÔâWeüV™;7+h·’^/|ÒÔåzÌW¯fëú(Ö€¾ª­^ä_¨™­-ªªÜ/$ÃÞ˜¡-4ÛÊ‹QñDN=ø¦@Imˆ3vŠ—G,ÇD´uÄsØ%5-·âï—™rá×˪ŒË¹ÐÇ3|ðx›"z»ÆKïÞ•>Ê]Ø…=èa^„ÑkhŸ%ÚCS¨x8†#0ìî2ùó<½ÏøÛØL̸)IH̓rÜ#Ú©!íYwÄšÕœ¸@Æy>Ôu¤XÀ¬—AR˜iƒ:F§H‚]‰ö¼b¥8Ùp•XÞ#çQèG˜Õ”£)cǾEŽ©kÆ{Ø1Qèþ‹rí³Óÿ#F§¬>޾мŠ9DjhéWôï‘ÖÓ‚y÷ä“¡Ù‡¯…÷=ñ Z¤ßD`Z¥Ž9Ùʨpw1tºWrò»ËP•nB©u8ЊɈ4c¤/«33óu°®J951)@u /²f"±æº =ÇPÅlÙa$‡â¹äy`p‘>柭ÉzÝ ¬$SÑÙ‘CK¨OQÅÊf ˜G‰6:S°«ú»Ï‡šQÇò1Ó‰³‚VH§u%}yÞ9¹‚S™Xä/’—O¹¡­\ZùéRÆ›n‘ÁR8iøTH•ñzÅSè ÈZíéS„ŒÁœpã«ÜÙåS/òÁ î¹WðΜã×ùä!¯U9|§&Æ}ÄRGzO¦fß`:Ž"ôBL(¶bJÆŸV j°5Ó@dl&Š uê´k£äuÈb1ÈÛDø)Üà eÞR£$‚ÙDڞɘÔQ sdY.Ÿ Ûg-ÓÛøüj`„†ý³ ÉÒ®¼XÖ:70Ý {ÜF‘ÑG§Gúük U@bÉiRê~ƒÔÇ` §ûo3ûŸÏ~‚£—øÝyPqö¿;â`Ÿ‘#ô, —lCa"“XÖðä„.pÚrÚ3?Ã^Ä8zË"f tôŸõ9‚>c¹ë…K=‚ º(s»í±¼å$7hµ–-½Ç2°Qlƒ,çÄvpÕí|ȲSÙgñßư€ÅEeÆð®i &·y!ðèj6¼ªb]è«úÐjŸ/»õZ$ æ­JÈ®lN`–rÍ,ùTKXà’:K¨ˆ ™BQn<³tàJ4ÐgùÕŒ™Ûˆù²o](c‘²’¡áæsjÉ(^o††AcXÜžciŸÁfð´øæÖƒ¤µÅ¢t\¦ij‰=#X¡cz‡¿Î¢xXíqsL30´3t‚ ý)ý}Ô!uÖc$¬#ÉÚY±JÓS[&Ê‘â”IŠNùkÉzïžÌFS¼'Ö”a› 4'Q©PÃØ0®‘f 5.`±BæœÐÁá.7çá|ïD¿ößõ<9ÇÀ®ïn…úNäƒÈe¦Ç¯èŸ‘3§z›þ(È)öœè‰ƒèãupvÆ•é$§“‘¯(Z¡Ïÿ¸}ø‹l Šõ‰Ïç¶6S¤¼F7¯š±äúM’¬Ùà’®#l›{ÏÄ`<+ƒI¤3GÛÑ:¬a"E^[F¥1¬d;qX[šëBÁé&[c1"Q24åÔY¨Ñ œBË{ZÕñ8ðjshƒ8ÄÁF1k“ÁúSÐД«oý]†l”!­ÜÞB_@¦h€s˜è2Þëõ‹—s ×.”.nÖ§&2*öä´¬²{€|…ǯ°œ1~Ž›‡|ê.ÏOXíöyPk-3¨"GK– ÎÐ/q‘ö¡¥kñ-FˆçÙ¸Ý|ÈnÐUÍ$²RbÌz!µ¨A{*e<¢øõ”|A†qakËíY5XKë ‰p˜ Åà_ô?/¯9DÞ—õþ³Ýhéž÷—îˆ:@ò óÏá„ÿîj6¼ªb]è«úÐjÊTè„%(’fîˆÆ0 +µá AÓD„‘0³,yRé`§ áªÁÌ7zX=ë3éæESŸSæ{ú<½ÅùvñJ2Û==a Æè1-#‹´‚ E' ôuË–z«UQ¶ ò¤|»]˜‡00º¢àf`÷à G›â¼[¦¯¶Ãy×a[¢%B0,¡'ŽrË`Þ®MYÞ±LÕ&‡°¿ÚóåÀ>,á}å7a?fzÛ\8¼ë™7f†{ó§*‡2¬ã°±˜)U£˜°‘CäуG—èñŽÞÚa|Bø»ûR½@‚Øf?ÊLG{qyßørx’Ó7ì)tt#[:rN‡t¤¹ìq›#n$då}s­š™±Ä%[–Ö[nš—$i鱬•/¦Œ•8i Kàt4ˆ¥R‚*œa ž(h¤3¨d)O½Þ†±¸;‚yfz.oZç&|Ú IDAT“}^¦–ɹâ}ÀKFÿ±¸½E-i$šŸ/™™@LÊàžèqëè£qN&Rºß¾R.YGhD\󈔵ŠÆH¡™µE;x/ƒ”®ÁŽ7AyX³iÍ]ï-e:µX[¨Â{è±ûòñ/1½Îs¯ðÚvá쳟ðäÓÇ|²‡]Žj®õÈý}õèêU7/Ý ^ñ'4Ñ«79Âò”Ø=Ý’>b]Þ¾ŒÁBˆN³b u…¼)m{áâáÚ܆·È¾ÜFœê¶í5H s˜o·Gó,ܼþû0øÄVa…ÎÑkR!õm®ßaç '×4Øx|è;ÄÄÔ…w÷ :BÉã0š¥+¶œhWhdÀ ‘D[ƒµh X˜Œe±ìŠlá „ºõ+ƒþ·$îMÔjhw“–#&¤grZJqØŽsÍ:Þ¼5"xˆ‚qŒ #ɃN@Σdˆ’ósÖºW„Œ¥ºBŸ‡t°“õœfR<Á:%šæl³2XZª1ƢŠ¡ÍU ¬þþ:(‰h•-Ù"[ÀÄš89€.áˆxHŸ`÷Š8!8L’Üì`È ¹™5À0¸²Ä—[?ÉóÏñ‰Wøâ‹|’ûc~Fc•q`<¢Ó¿Ìï7.ØYaŸ–›ÒL¡‡ãtd Ð?ÏáŠÐ‚§]ñôVOhŽiOèÎéaœbh"QKg91ÆæÕB^‡hÖKY*ëŸ×LY§®´2¸c.à|}x9HUŸ%H³ßÿn.>cíÄ­Ü"ך´–xBÔüÑJí½«Ùðª>¢u ¯êC¨Ÿá¯}’Ÿˆ¸†:²g™FžzLÈð09jlIìR¿__ØèÄp­ÑsBÈ>x'†Ñå顯iÍ&`Œïß(y7y …´(åΛ5}Á(ª$£ˆeœ¬Ëõ8KU!ZÔNfqM¶…Ák²-^4eB5eÃ´Þ 5`oÂXÂ*Çt§ã*ÛaWŒzF‘zŒÝÉO’%Ì B+ôÐ&gŒd7»€Uæ©íyÑð# ·[–ä&*£ÜnKökžG˶’µ|'#õêZHjÁzúˆ©P‹5˜˜ÁëSt…N‰5Ñfëh];â›±>‘ØNñV¨{˜£zD¡É½uV±…ALÕIƳ)#….SD®œQ$俘1è–DZÙ,Ög2I¬½ >Œå‰kíNÎÈÌÀë/©fBéÌLv6P¢cW ‘&½­-»ìEIBÑH¬b°˜š:m D‚"ðóñHa˜ªÁƒšï>©–›}‰®ftG›H ¯X¡}^‹x%Ö„–°ÄŒÀI¹pÓOÊqT &¨ >½S9åøj± IG@÷2aÊ'¾ÌÏÎöù¥SäŒëŽñ6)w–Ðb,‹)«ጃfÀÀ.áêâŠi ŽòÊA"2aÇ0½ƒBÏùgï°zŒ?Ç¢ÅؼhÏ£Á2.^ÒWè³€/ƒÇ¤Û¥”m‰X•ÎN½Äa˳ŒêÙ¾ý6vzéNå0ɯìÂé9^³±RzÆÞ|“ÿjB¼ªh]è«úÿº¾Â_~WF+*ÍÆ.0 ¬tÓ4n4÷æòмÚdð릤tÕPnÒ'ÐÂb°e¸öòºàUḸ~!lÊMÐMõòì6ï–©hAã Q7Ð^`\rqZ©JèsÚO6fa½¿©ƒÆy¶£€`^ëž^•žÂP€»ÀLP+LËÄ3nõ­¦p]*|Mm ö‹bépÊ«“å`Uà2îIóë§zþxd?dA/R(·|Ïf³ #EX<èM[³µÃó0ìý%uÙE°hâíÏ1ed d…Œ³"&›¹cHt…v˜g/sŸº£<¦Ã(.æ@N6ç¡I&$åÃÙ¡þ>BVY£RáÖÆ®h ‹èFÉYßé”j1;I¼ª5"“M3¤DPë‚kö¹“‚z×ÇF6–Ê ÔjI(´kÆZ²þFŠ:ý?£y%‚MÎcg‰WÀ• ø€OãTé¡+^Ì é!3Œ`ŒBlp¦9wpí¦k)¼AGH;Øða nòÊSC̲öJÿµ0¡„)æý¼@N ˜:Gë 8Öµ›ë)Aí,Jd{Oĵë¼4ûªç·žpÍ@ÅÿËÞ»þÚ’œç}¿·ªº{­½öåì}æ\æp.äEI )ATr,ÉqŒ$²ì/¶eARÀ#’1àü¹|JÀvD€ Ć>$¶_$±,Dt¿Q¼9Ãá̹ïûZ«»«ê͇ªêÕ½÷>„hˆ³^ ˆÅ}ÖêKuuÕSo=ïóœVˆRÜîãÓaÎÀéÕŠÝõHãæ% ì=}¤2¥«D¼‡ì‡ìí±<&¬YVÏY½Ëú$«¤›²èì´¤“‡l¾€¦| §f’©C3å¸2~^‡ÈWü ¹ö;egéÒ–·¶˜ˆT`Š#L›Å­À !YâõH¤néc©æÓMõߦ{݈¤gdc;—ïÅ͵f.ëódæ“îF‡Ø¬)’\Ÿ“aEÒª3ã="v“fεZB$#æ¡_'¿óP6ùËë')°'u¯„"i'†*ÒÙìP(¥ù%ÙhÈFµ#ßz@Ö°c³ µ!'ž{%(z-ˆ¸d»Å¬.›³vy§I%Àx|GDèvd­E0¤ž²uSHð5š<Ó¿.ˆ'›Ýô¿6`–SS‘¸‡¬0s´*ïl7‚6„ÇT ¿Þ²[Dè}ʼnÁ´ÔçxœE9C.39åø€¹Á\qšçc÷•HçO(sfDCïAp9Äî°÷fùuNqö DÃÂQ —Ý(Å»åeê]Êhè¸IÀgè¼bOȵ|ó0úz |°8Ý.‹›yì}‘Ê0‹¹¼ù›üü[üõ털?ѱÐÛøöÅŸæo?Õq¸Ä&DZ€jÍ-;J,›ÎæÅRšsEÍ)ä:ü\¦&dªC?Jßóª/š¾Œ`ô¸Ì\6GJùºl2½#S6ÅMRÅŠ8ƒ$6sÄÅœPËg®7þ-9[8Öó‚‘=³½ftF@Ö•|óPb5ü<àzæg:vª\F¤¸Á%¾mÁÒž¸G „>sÅ•‘ ­M±Uöþó=?Øq7¢†`YHµVÎ…!ÀZQánÍóËÜÒYºnÐIгÜÜ86É鱤I$ÚP¡{fQÔÔŠI¢%O˜=’Ü {ì¬JƒlÑÜHâ}™?¡ÙZ"AÝ^Y%í1DÙ \Î:˜Ê@É6#²DÔZ*EméS©%Œf9¹¤.ÇÀåB ×ŒôÒ^H_ºyRê°EÇB ”_õd¿Ã4Â'{B)Ú™l2†a)‡äÔPYê’ÄÔääRHÏQÊâ²\•Ììøërÿ+ a½1ÙhJU«ßT¹1´Æ²©B)8X#ÁbnNK³Nÿså妔¶¦utHÄ\¢»Äª”»Ü¬™‘USbž§{}~ïî=î 04³Ê¨6Y¼Ã»ûTpÿl£°Ìyªx6yÉ[å5€Î |Qù® Â_{ÄP½Æ­˜gtèŸÃ%»sÖƾ*—£6”imßÕäP¾|Eˆc#x>ú‰Lwä®ÔeêÒb ·$¼PÁÓPгRW,<À úÿqÏÏo'ÄmüI-€ÞÆ·)>ÅÏ~Š¿b9\eÚFÖvc_w#-Ĉõ#×­+Y¥!“dû}Á1›Z´ªÔ²$wÙajµ]þQ,Åb7RŸ§óAò52¡íNdU¥xu• ÜÆb ¶ÈrErr4Úˆ„͆3™Q¥ŽÞLë‡(í2ü¤,$P·ÌCf¬è f˜ä¶Ýƒâ%ㆠô!ö§„˜¥Ër +8ªšƒãÇ:>³ÆEž 6ÒŬ摗ÂNàKC¨Ù‰¨-Og %¤ï»,ƒ å·ƒG]n“ž…áÂÔÅ8èбȬ€­Â3ѽD{èQ%D¢'vÄb`‘¨ç1+…OŸn×&÷Da-Tš /Ô¿e@°‘ÃobOžÆœU· }A™&/²ìµ‚ ÙI[b1ø±ìä§‹e­!ye3`héì™BÌH7ûÔ™ü!Ùº’±NDžÔØ Öç´dÀæŽ\#xK ´…I’óÖIÆ0 wÌž¼k>õ i¢D f2`R1 ‰Xß–š³u&Ck²´,ÞZ¡J´ÄŠÐ¢J ݽƒ}œ;þ˜T3p¯âR‡®\Æ9²‡V¹K›ÂÕ±àýsZå„n—Øã &ÕžÒ+½AÀzgàò^ެÇÄ9]ͳGÜ>ƒeyÔá"ÓâV©"b13D15›ÚQºŽv…7Øöo#<á–ǃ÷N&qb!V]·é¾‚¡åÅ#ò@Ê2/þΕú3ú•»ÉŠutFÒ¡ÏÁÒvZzåÿà9?¹ ·ñ[½oG|‚Ÿù,³â°Í9­Á$D®là{ÎB¦¤nø“”ïø)CxÀЩo#ПtRM6é³I!ùÏ?™ºjM逈•’§_“ÊDÇ)©X‹“Â3-ð'ÖôRHÏã»’zÖé¤ÈH^C§ùò‘ÙŠi©<3¡Nô_ƒ â—4; ¬;¢Ï·$ó>¢£s²MZãÊL<ƒ†·ã–˜È Ze¬¨…KͽŒ`h…` 5saïŒå.½+2ØlN!"ê§ðzŒ¡mÑÈ3‡lD–¸Šª‹b‹®ñ3œhˆèZBŸ‰½!:4ŠEœD4¢]î28Û¥‹*®!¦äxkE+G%Ì#Ö#©ósØÊîïþ‹–ÚdTGhsOIè1iYP2Ä™|;øu`Ê÷uÒ#]1±NK 7 *™”—¦dó dÙÓ;­‡º)½ÌPÕ‚SLÀTì:!1—0ÆiŸ²é¯Î}ã=g{¹4A>bg£÷ç hAVY˜“¬t¦MÞ)R…–ØA#‰–VÁk¼Ð&ÄÛ¸ ªåÆñCFîn:>T…Nž.çÈ.Rmޡ̸¶Ø'¼{Á{<_poŽëó¶‚]#u<3ÔŸa*´BA–,ž`*f z¦< œöT=UÇ›BL¬°mVv•ºÃõÌ#&PGÔÃ%^émoY+Ö2¯8šñà î½ÄÁ3âðì=ü“’-Ø)NéLGOnÊCë‹e¤u´w¥‚¤íh¤ ÓM?Û9…†Y¤^s¬ï=âouüÂv6ÜÆwLlô6Þ÷¸Í§ÿä€öô!‹†™R)X_©&÷˜È­PªãüôP^è }ƒÙ– ê²·8Ö„n“±1H½ÙsL5_u£üMÈd²ÄGĘ,Àu=dTd–jÌíµ¬v„~F ’ÛLþq$¾¡S;_F:¬L3O µ§‰ˆä¬O”’’¬ê—ÈŒ¥Ùdæú"•¥†hË7ÍØdj‹XÁ6ðƒç˜õ¯Rª/(+²ïÉð÷DŸŽÂjÁ~uVÿÁ._þîþö¾o6‹t#ƪl;—H¾U.a1-Ǫa')-tt©ˆ0Énâ%qM¬‰ž8ÒGÓD’N鯡(Ï#mYbQ?Ùmª)[ŠzÎ+¢2÷ô† xa~ÁìKÿoO¿¦íéb~JV°ÅáÍV‚¶ˆøi$¤6“{Y^ o ¢Õâ®Rvk2ó8çý„J®Š)›Ñ2bw¤„qvE%¡°\,š"*öî’dæô°sPI¾ø¤."‚È…&—J1Ù¾¯iŽeÎñÊ«ðŽ «¾BŽPƒ^ŽDÝ{b•QO„hPÙ¨P:ÅVðñíM!„-Rã–à_3®wP¸€[D3U‰4¸Çÿ3Þý¡¿|d~ã3CzÄa¶E{¢ãÜ^ñ™S¨`[Y‚‹HÏÙŠÆóáëÄì~: j=D$@]â,5˜ÈiR¶N÷Óá,wjBÏþm>úýÜû^ž~ƒw?O@¡Š;ëUíkèù[pšu$ÜxåŸ5¢Ž†Ê{®×5¼B=n…©©;Xó_zÆ_ÞN…Ûø‹-€ÞÆû?ÌÿpÈg{ê<+¥ä´bqKÑ‘Ýo–›»ÉÍj°ì’Ñ(Ÿ$Æ2j²SÿÁÛÏÚ ,4â”ú‹é°£=P)ÙYÈVY¨ÁŒˆƒº©rÓR„eŠÚ‡–\fAAiµXS(½\hç2Ú$½2u JîY<Î3ó¸Ap@%X‚`CÎöû²dI"Im:Qs£]9‘N?˜ñ#=?¸¢t B­âa¥ôJ+xÍpAMNö–½»ûúù“ê¿æîö~`yTwÍF'B#Y![’4…ƒ{)÷›lW­]fœ«Æ]fÔ™~¦]çóFá!踹]±3–3g¡=³Qi–6PGlòóÜýÒïÕgç=ð‰‚±ÛÓùlš™²ÂI/9¥“ÑBcv‰aê “jF-Dƒ8ä¡%××E“iÊã*0;˜–³'¢ÈD#S8Êé´¨y$…ŒÔ³Š9œMë$SmFD”É{o×q·ÈJƒD‚Cï_Jgåd–½3ð<…“¤oG0CŸ%3¤‚}-QPŸe+|½Ù\ª#.cW™™?€:[vetô‡Žœkq•x†iˆõÆ=R@°ÿ;_ûsÿ΃û× ê‰‚T„ˆ¤R O0ü|̳ۖ²d tÆàÀðôž*rOº‘búðtmÙ† øˆÖa*-׃å­_òS·Øo¸\  ý«¼ká)| ŽG¹ü%íezÛ7Öüˆ=Xo¤‚ÄkcÔ•ïðÔÛ²jßã‡#ŸÛÎƒÛøÎ‹-€ÞÆû?Ä/ñÃëBeN’n’w´K]Òèé ê^@ß –fô¦„FXDf9 ²¡@ø:×Úò—ÉO!:b™?6e{Œ)åPfÂ;4#¸»•¯P&I¶T‚&Lg‘:ÃèJiá܃½!Û!u™ ¬…¤‘ú“ú‘à ¢“\.ËÝ 2g7¨2_óLO§´Â\‘Ž[ï>=xü,Ü¡¨¥ÚaÏR÷9¯J°˜bà•6Õð• nòA¿"H>¸|kY zÏaXJ¶÷lÄÃ¨Ó ®5"ظ°3¥½Kæxsê ¯ĵ˃ç<’.³›ªY§ÚÉÄ®ŽþÕ½x;¯Ù2±zM¨œ»ÿe}ÐÐ6¬´3¹mxà8¯8CNÑiÑ[ð] @ð9å¬k|¢S,8SäÞæ¾9I‰Æ‘ƒ©W¶múy©äqË-Ùw¸ø|ý3?²¿øW¬"e°cÄ¥þÕó‹‘ŸîÙiË㉄|ò hàV„ž'a”²´ÕQž`xB&£¬Ö6, ¡¹`Þ¼E¿@Ž_„¯ƒ+$òëÂö\­‡ÞôçõïBY>r 7ëh3jhë+ÊôŒÚׂ£?ãQûw–ügÛIpß©±ÐÛxc—zÀ%É„ËÂWV¬)X«/ˆw\Ž1J©å¦±ôIîjLSN¤ÃAmc]ª”ò\Uê¯ oxräPʯ¦{•™Ù\йˆ#} ÉNÎéÔ¦˜ç 3ŽW¢!BŸ$í"*ô¶ˆ<ðâÚ1õY®9è‚ñÔ=&l„|Ç<ш+h >m©Û"ù72m¹ÉMi*ÇÒ*o%m¢M‘̓.fO™„à³ÀŠÏ$·Ë­wâ»=¬]ï|õ—íÞËëW¾kyï²Ê²@°ÙA=k —[“âx¬ðìSÈõ¨%8¼!BÄ÷Ä€/¤]ÚŸMÆqƒê'¬’ñG™Š¿¨p•çà1/}å×-žUÒ¤³¸ö-5ÅAPKe±-¡Xé9ƒé³ Y¢›™Í†«aâ.ˆY4ßÖÀc¶×^_X=:“›eóCmÆoØØ»›D}ª¹›èÚœGÍf[%Á÷!·ýGׄYzgÕ_¯9úm sS‰v·yþ@¼8¼…6¼·¸ wˆß$&­˜‡ôgDGB›=kòÆŽ!Æ|nÝCjl7ÉÞÇi!î•=ù{€§ðRÙ½²Hù¾úg~pñ£_<äá.¦Â®‘ufæ8ϳ~Ê/)?›â8B¤ŽØ~E\£ê‘HÕæed‘†.¢“éå9‡å¨Ã„mK]¬§vh¤öÔpPóá=¾Öá+ø(\À ¼ =<‚‹bìtÅŒI®q-^¤÷§{w×—Ó±´ã8­nG Gσ“¸\ÿwÛpßÙ±ÐÛxãÓüWûÜ+–|tPÅ%F$»ŽÆm® àWpf’GK7ÂÐ)…Õ·NÕý>cd·èÆm~2>®¬±«M¦.'¥\Ö¬¼+#Šœ-¦ƒ§  ½lh½!Te†÷¥ÈLõ\u:ÛɆUd(¬fZ@ÒÎK¢x±ÜO"ö*t6“F$dêKb&¨^ËÝÜ"ÉØ˜i®Kª‚¦p5½² ˆÉUfMŸS.¼ò¬k._ù¸{÷]¥$â<•9¶øü¯Úî³g/W±Ù<èX,cÌP³²ÿÞ°SýM-·YòºuÒÙ°à‰q bþã2ô°˜o:ò˜¼ª>(½OY|í7 û]®T4õ‚G37ÃVX— ØeÆlÉn'»lSJ 55­d‚‡Ž²ÿ†¥†µf7¹Ä#¶B%ÅÎR‹£¹#´d‹ï0ئèFµÃÈ”']´5e‰3vkæ-]Üdöµ¸À îìésuñ™Ú¬5X¡B°–;{´t•÷Õ»‹æRwöVÏ?„·¸žjÍ˰Z$‘œxˆž×È]G&àΰã‘ IJcÒŠã>¼}¥"`Tï;e?Ý Sœ0ôm¤Þ@Øê1ñáwå§ì¿õühö«G<Þá|‡ÊÒ/-™­ñðy~TA{BOïQá’8GMvVêbAÏçe=¼.WyQždŠA®>£ØWjKè°1‘—-+ÃÛöà xŽJuæœÂyI˜kÃÈ•aT§Û,Wj…É‚+v™\iÄ¢º†Ë_ãøYÿ÷Û‰oØèm¼_ñ ýUþƒ8RÒð T5>à”~H÷v%çRÆp« mt¦RIæzŠ¡7@5RP‡öÔ"Öôó|)W3&¼^Lò.p¼ äÍ´ä&]O” Q9嘻àcáÕ+‚SãbA®Îm¢¸Hòþufšš\à›ùÙù¶’&Žr®·d,6r=#5Ö«Ja™ {ýÆ~aU²üéw‚ÒSÀ¹"01•gàùîA"ÖcЬŸ¸¯þÖþ³—Î?öÑv¿8õé¦öq`¤ˆb#µ°Öp¦™•‘¨Òù›¾äuÇ´úÂÖ¸ât˜Ô%Lٯljü¢"º!:ˆ0?G¿ðsÃGžpXÑÖÄä§î2ìéiɆ˜bpŠ1Ø"—§E CC^ÑØ’öIÏŒ’ª²Yc ¸È¡…–†µ²‚PlÀ{ÍNÉ)—œˆ%Iun°‚‘Ò£‡Ãϲ#Ü¢áÔr¾ ÙXÝA5ÌK[G¥:ÕÁÔ›ø”Ë`:´]W桌! 7Ÿÿ+Žöç¶SÞ6>h±ÐÛx¿âãüL…m§vWÎa >Ñ \6z’ Õ© Š”yÇ(Öâ)³Å𣪌ï9õ4µ€Å IDATG/ˆ 2߈,Œ"oi×p˜÷úsž›Ñ”3b]&N+aDÿH %qãøWEœâºÈÔðA§¶§¸ a£;æ¬É®{0Á‹ ²tCS„¬GçÕk¾¾Wjö-FX™â=šë\‘‡P*ÝX7ZŠÄ€gßÑ)«90£¸²NIè·’“§{¿wÞ|ø3g¯?B³Y£Ô‚Z¨#+›­é¤ÜlªqÓ)õ"·ºŒúÅõ œä¥ÔF>|JeÑR˜ ØŽù7þ± =­å{z`¹<£«qMà4—\xúd”±ËQ $²BES17Iß …#‘MŠV²‘{ˆlj(B%T†“óç-´BKX'<$’)$±ˇÚW6¤ô¯–ÒËbùoÁýÇ|>L.¸Õ‘Àà K/äðÖýöÃM#^»ŽuOŒX‹zöøô÷ñÛ—TŠi8»ØùÝÓ+yôo¨Årô„þŽK]H² ê‘°!ᨠò2|uãpo®Y!]!F3ª¹. K4 3ØÉÖ‰ú5x†| ¾—³O|œW>®P?¶ößF*Î^±'ìô,o£ <‚祟(ÒÁ%Z\ñ”Ѫõ.'¾ž™ù Y…Ÿ>bKO°T"µà”{–½o®hÓ­ÖP m ©tŽ+[{2êíñÚ¨}EÁ$ŒYp#‘í§ðîÏÐmeé¶ñÁ-€ÞÆû¯ó7îñgºk2J‰¹Ð ³l±‰)†¾Zä6& º’ÕN:0-ÄIqHù»Äs ³ؘEó{ ['¦§ q0xqLÄÄ zލő]àùêM»-‰Üñ½?¶P%PyìT¦C®V8žœÃã(Má*˜¸yš‘Ö·pñ°,[–ŽÇ·™µÌ/UæíÊȯX†¾Q-ã~á[³sïþêÑ©Ç&É“XŒ±G××_ú•=ùá‹×èçù±´X_*üœf”nU]±ú(.#q·Ï4$ 5›"–2&àë„ß¹aÖ(&²xç7õä÷”,_~@ØOMyÈˉòu[«=nWÌ.93XÁ8š:§ìÛòhfÈZÁ éÞ±êJz¶=í%Tì9ê$–g™šßª%œÈ" =ˆG*™4Gb}cž¿+02ÈÀõ0ãžÃ*u±õVÝ,+¤˜¶àÈj¯»Ó DqÔã¤cÙ?}ýµƒ:‚g‘˜>ëÏ={õá)µ†¹úùåþƒÓ½ïqg ß#‚{Bç‚YB2÷›e«ìƒ3ô#pûèÙã]aèè /ÖÕ"~?=’ÒVÄ¥Ùÿ¥tŸ<ùî{1/p²Ì›†’’MæÌcÚwÌ: ‚w'£¶œ“Üœ¾Ilào ?žâþJOSÀù¹çMÇ÷ÎM¥wtJtT{6ì(8ªˆVÔŽ Å—DŠ8t¹„L_WD3£ g ÄŽvE»âÜÑ4ìVÌ¡QÄà@…F3‰fè1›BÚ´caŠ;æ ÜhÕQ¶}p­±53h#!æRÈTwÒñˆÊî|¨ºí\EUëÈŽÅXŽ*œÒž™xðÉš?48‹©±–<íˆXö¬\Þ}t"{wLŒò!£ßx£¿„%ú:òutx=+üŒ5yß@qè=ä|³à e®­åÚ6ƒ)Iè´]#‚ú²™¢È }Ná=ä#èÇàÌ9ZôTOXÏ8­hÓ˜³*Ã\7ïBI3ûRêÇ _ŸmÊÙ¼‘^ÏWï`+è‘Ê`ÀgVKæŽû3¾’TÍüeÍk€üt†¬öhqe{fïü#Å™‹Ä>úò?çûæLÌ܉ª¦ÚåvBÏ‘(˜W1ÓMb$J¶ÍLàm C§ÕÙàx"ØÓi^XiǪg]QÍÑš]‹+ŒýÅÈã2“jËr€1XWj3â;èTè-õ‘94Üê9/Ý*Qî㨈Pâ}îï›ÝÊÆ¾ª]5Oj‡‡»œžÓò=;ü¾¥®1‹œÉ–¶A½AŒVKÛ\ºÅ¶%¤-ªgЀÉÊ|gÅݱË@SöÑ×ÑH…†}Þ¹Ñ߃k0ÚkÔ uÁб ñXF’5z à>Ü‚dqD–ý]°<^ÁEÉ /Z(ÕÓ-\BW«+·£ŸÄÑØ%°dÿ…‰cJPaW1žuÊ5K!o¤ÓÆÛ° £µBœ–¦1*^[cscõ¡Näênÿô9þ©í”·flô6þøcÆOžŽJïd4to§dcP½ ɵT:–õB'žýæ¹*O$®ì9ŽŽjkÄIЪšÑÉ&I©Ù+B­]E• tB€VFÙeJv9”œ¥Mì5W°ÑLÊØuCÌ41ËÀM¾[&³LfM)Êq¦ÙN0b½fd8\ÌPuÔþè¦_¨òOCMí©NiPmh‘A†õppWÜí"‡‘=%²ÅÛ\e•*!uƒ’ã¸Pj Á;úëÝ­Ï®÷‹‘Ê€¡+è© VÃ#¨‘0É4Ê(Á¼I¾|¯ª\A ‘•8fÇg²|'NT»U°ž7—Ô†W=°T Óâzz›å™1¸€/#‰)]aMV£“õ%fË’LáˆDO¯£Žà°c §õhŬfVËfGQ²kŠ[xzb¢¡à)Éj.Œm\â”la—»kz¼)¼çô°¤ü¯ÿ÷ô•;¶Q‘šµÃÚ/©±îâûï¶U]Uì.RaèQ¢ãÔT«.­½0Quï4ž¤¥ ÖHŽPGÄ ±#ïÁU¨‘CØKôȽGž@Dj¤Û(@ŽKg'L¥€9ÏÖBZC«Ñ†LÊÆÃòºGè]ØÅTÄçxy™e­ùõ¼ô,U.;–çW5–ôø ¦2€×8]¾8^{ƒé=ÖP)«.û@enÀ#‡‘ ¡ª,®ä¹¦+/£¤Å€¡•3^éú›fšoþ"eØ9úk`8þÉí¬·`lô6þøã÷ù/ßàïÎFû íT ½"PÝ$õ ºBÎK:bCQœ/ ÁP0O!]þE¶uCÙŸ<êÜ8ŒóÍc¹†±Ê×jÈ.3úçA®u |Økš|W,ý(“¨¿VËrýb,eƒÍ^ÔLiÐÁ‚[F_s}!2ºñdiFßdgG°"LÝË  ŠõÔç´v™uøˆYSˆ¬w Íæt‰Y±ç1³‚Ï%Oð˜P*ÛŠ”ˆè•¿<ÛùÒÚOw¨Ê³ðy§žQe¥ÁtýÉ3ÅŽ@s¢,¨lL"3zÖ‘FGÈK#Øhz䦠\OýðÿŒ™d¾y21ÓS¿¤¼ùŒ²¡ç¬£í8¯p»¨çÄ€°ÒMÑZ‰Í.·[.RŸfãpc¨*f‰ ÀtÈ;bÀfÌÍà{Ò²ìikæ6‹~ä>¢ÔžN‹À fWÊônHdVdK’Z£Dž±IsÅÂR ¶§-és˜Ö>©G¸FpHŸ E•Á 1"Ž*²Ç«ŽÇ‹¹‡œ“)Š%î¬Þ§•ªÁéì86¶G qŽi!ÑP,Zà À$ Ý•»@ƒ:Xe’G°À>†v¢§‰öQçC#Ò"O Z—uvÜ¥JÊF¿ »°Ù£Ÿ1?àö!mÏ2•`h=º† LÄ/c=}´Büh ¥Çö/±w‡óA™/è–¨â„Ú``î°Ê-†ÚÐ5ôMdüMî'Ã}2u<ºnŒŒþ]þ¡­Ò#Üþ«Èœçq;ñmãƒ[½?þXó÷¾Lø0ÿÓþ \¢ŸöЦ’d Û:õNH¼&ç[-d¿4€÷JéY£ °ˆÅõ4ʼð.úAQÁ8û«…ë[½€ ^è@-aL©¯9ßš›LŽ™ÖQšÑ|ìGêac˜F²Ä±\Ö˜“ fc22´ÌÕéᒘ梮p<ªki×+ÊÐÊÅŒƒžÛ—¼´¤:æyäÒflô¬öJÚ€ÁZåmè,Ö ¢œý»Çïyxl¾|!—Áв²Q#væÑ[‹‡=µ˜*¹Æ+¥|…*‚ÇLŸ[ÌF*ô¹@ÔBP6'usò=ŽÜ¹c–tÈ5¥!ûkGAÖ€!XÏüÙCVoé“ßÔÒeÉÃ%ükå#–ïl µ´ ³†&p¢Ì” %*šDîf,éYëFÞNöb>r\qÑÑÆlƒ‚À%Ï U騶æå>.XEW, :c¿b>ʼ'šuV'7™w“ gê@è1ÄHˆT2Í`*»,#& GÈOŠäáø¡»ý.nk¥Ù‚»ÔЂGׄ泻üóž&bö ê51 ‰èK³‡oÌN¶ÍÅEû;½^<ÿHè!v…ôˆC±Ej´Ã©D,Ú¡±l=)Ú”õó(aódó.ÇŠè‘øB£Üå¾r3˜ç¾­=$…™T øv`‡uÃÎÕ}ö„µ°Vê;ž™,<šÈ—#>´–AÀOËûí/òúgyóßqt‹ÖrÖ±ùÉ¥áÌ@í8ªXö<ÒâÀ:‡Ëi‘âu‘JFƒ€)˜>^£¶LŠsoâI·‚åßdý?nç¾m| b  ·ñ¾DÏÏ} ù0o$ß"@[ÓWP¬•e„’#t2Mè&z¥ì 0J£4 °ÎÉ.;c±Âj†¸Iµ#‰ô†’íFÙÒMÁõPõ“ÂA×¾ àæzÈM¢uã #rÚ}—ã"%ûaÛh K´!îb/@К賔ÇíøñÛ©U0Å¥Ëm êÀæ- P¬¹Í'5ጠ7gWq‘y!*a k/-ÊŸVõ)ô|è€þsÜÿ4_>¥¿àî7ãø’Ö`ÝÑ·H_*h{æ—áÝÔQöà|³Çx ×ÌStJÚ~QŒ7Юèö¸’Yå¿á­¯ÑÿÓíÜ·NØmlã}ŠÈoðÞœ¯G›„=¬á W½¨£ᤨ´Å3OËàœ"Þo$5ä*xzÄc•ØYvéæÌûÉÏÍ`5 ÔIJꢠZ)ÖB…7 `…VÉ™ï zŽË·´Â~QŒMUìˆÎ!Œ ÓŒ˜!2ýp…mo:Ñuÿf3"aWƒüÃM:£3zËùœ&àÎ1->`<VU‘4XÃNÄz&€§ù,ÛNj ³,œry§áCš§ksyFQá5ÇgáÁ~6Â7H:E .Ä•Ü<ɯN³†E¶›µƒNÅpeSY˜1“–'ë i³Áü˜?ü‡qý^Ԭˉžu,ª'ƒ_MÌnîÉgæyÍaæP‹—º±µè9‹Š¹ÁŽÔ6r}aÞWœv¬ FTXñNà\h„&=ªŽÇ–ÝŠÉߌ=ëÀJ°©º5––¼äÒÂTfÙÖФkØá®ÅWY•]§|D‰¾ð%i‰ÈËÿE¸}p{ÆÁ:ì©ß¡¸Û¤¢Òì,éªÛöí]…I ÅÓ` f…®QÁZ ÈCß|£_ܱË{¦?j?ÙDÖ\Ðy¼#ì,ë}.Ž8sz›ç¯pö «Wð/£·`ޱ‹éa ­KdŽð“E¥f°‹Z´9š’¸;¨q±1Ú"6SÅ£KI¬(kOÛ²hh˜ˆDú”玸HÛ£Zv̵pŠ"·nñgÿ»Ÿàë—œ^°øÀjI·¦_Wøu.ÛÐ:b k™{ö”¥Ç¯G«Ÿa ݘZc_îxmÔ’›†2FCÓ Tƒpr—ó­°Ý6>@±ÐÛxCùÍ3Þsüxâc´IjévÙø58‹uP-­-ó–ƒK2jB0øX z$1füä ÔL~]u`µ5Ìý.&x½vL*ÄèÌ´¶PeÛm5“ŒLW ²¤«{±Šóµ›¿ºg:„c7Êý]q–kºr¶¬®îë)5½f£ ÓoJѱcÀÑéÌTÛ‚åbÆN nq-Þb"¢ô‹\sYºkƒãsà`'hŸ©ÆÒ#â ‹=ü½#>¤³¥¹lñ¹öʶÇݽE;º­5Ò!Jp˜i&'˜žöô… t·†á¦Ü?Eò¯ g­€,Õ6?fçsÿ€þ¼Ï`)ƒfOÚ>âcqóÖl"h¡W;œå(²©’¿øÐUk¬¥²Y#Ixçÿ•¥YqVÐ3å¼™?½æ=¥¬„ZŠâLÇ3ËÂq ôdÁYÇŒwOï‰])¶jÁjÐG+#óx“•jÂ9oLI`§’Ç”ÞE^ý[ñ¥Ý—jö×qý,]©5ÔU¦`‰=/ÏNwxœ܃ MÆØ²"vPcÓ‚kmg—íì¨:qaOâ¢9?ØQšb ´Å¼4ùcWCë¸Xprȳ{œ¾Áò üÌ0¡èóX¤Al^OËz›¸‡:¨Ð­FŽ$’>ÔY!~ürgÇ1kiˆgï%œÃjƒ34†Æ°H¤2Kcñ’µ;òú>íÍíb>†~Š×¾Ý»<ìY¯Ð{–Ú±\³îéÖø%½ÇGG#•ÁyN.J}…Ïdqb!Ì f(öÚ"œk×0´y1ÔN¦þ(¼ÂòŸl'¾m|@b  ·ñþ†ò›¼gøq1\  ;ÈÈ`À •!Ú¡ø½è¤Ó¬Gv%{b µˆZ¨[ÜŒ³]ŒR+󒜮$[ÂfZ z’yÒQ6vˆ>2`â¦ô†ó7S>÷Õ¿éŒà/£CÉôï¢u£ioÔJ“̱L+ñ_äC¦#o—P˜\ã_™N¨ÁpV#sv—Ô+ú ·¢r´sfÈ4î˜J-uƒ®Ñ–˜Hœkü%Ý’vMßqÒpz$ÝþKñàÈÚ¥99 çg=èöRf²L¸µj½e&Y$mٻ“ÎJGÂl/Àù)%^GY© ‘Å£Õìó¿„¿Ð\6,ƒb¤O]²dŽU°&ÓŽ­‚Á@PŽ•3pÊ¡p{Jh"{žÝ–ªggk‘Ú!=¦C n ->ä;H:ŸHaÍCÅCah£ôÏ ÇÜ ë°É¼>ÒGú@h{úŽ.âm¤S¬§ ¬Ë@ëØ‹àшÕR° žå%ïJN–/Aô®|ø?ïfw*ö‚]ˆ4Ö`-U…³yÙ‘Ìwçwöøý@±@1Ù QaGrËדÁLg©ñ¿ÚzfJß–)§yl‹ð¢´ô˜mGdèáþuz…qt= ¡âá.ú:/½EÓjê§Ürœ/P—mhb̾hÕŒ°Ê´Ù”¿ ñ=9­¢åxŸ>,T>²3«ë·še?û¯÷û?²ÞÛ(i²ÉmŒÙ)-§YÔCF¼èH½Kr.üYq&bÏ$°xxV}áü’‡HyDª„!Ñ«¨`$‹g­Œ6?È•ðÃ#ËkŽÃHãY.S¢¼#Xvm ”"?VYïx^„¿Ý}­Ù)H•—´ˆ¢y:áTs-곞ç3héd!çiUˆWzá@¨H€¦á^ù-޽9¯(âhÛ{èA=—É«ErêRŠÕ#‚šÇR¯ 1õî芓™Ý’0´1œ0;bÇ#©p&ã‰A©æ„>‹;{ðñ–ïÂü\y²^É­·è_ÿPÏ38¾ -,‹¼L7²mFkÈAÌÛr¼àxŽÌ¹u‡KvNiÎ0kdö“œåÛœŠ–b{u“¬½R•K¾ éX?É æ¿+¼ SÄž¯|çJJI®¥ÆãH%z¸ªqŠz\Ô)yb>nh_æÕsvV¬fØ]hpG,Æâ‰I¶9†4ù\2¨ËÁ¬ÇÄÂzï0|ê;Þ©>÷ûõÝ7V‡¹iEƒ .v‹Çoï¿i¹ó¡å‡çË£ %æŠB@Þ WË+l˶¯ÄŽÙ“gúÅ_l7ôp…!%9Üe¼¤Vrf/Ÿ½(4'€4Èv[e­¼'<6Ü28ö=‹žsšЬ­æòE)Rà O' žØøÆ±XGÖŠ‚fÐköG4Ê D8iÛ$v¥'ÕP+ÖPV»i ÅŽ}hF+ž:jj v÷IÓ£] Å”¢aóˆó›7Â/"ɧÈÈÆARÃ"‡ïÒ IDAT0RCi£ZùÐÔçöè_ÇöÎëÝÎ&Ñ‹²mrZ”×#YÆáUM´é®ô𰶬÷y¼€Wعä^Ça ~}Hbƒ²Qe–—è=*+ÕÂÞËÔŽ¬În°&×Èšµ!V—KOkˆ3$2SL`Ñ€Vx%„‚tç ¬;8/kTƒî¢Ö¨=ÆgÕÃ:µsÒìp]Ùeè©V° eÎ ûy¼¿ÄMég}ýìÿo˜Ýí|·Blô6¾½¡§„¹q÷hËŸGÿ»ùîMü£FüßxüyðR=ÍE ÜàÕfÀ^ÝÁgÄ9zƒw¨»fÏ›Â\S²Ókùi7UÉàZšZ3£ŠÀ±yZ({SÁ½¹æŸ"/¨ ÒÑO=×þ&Dz²£=$›/>Êá[Ì.èvˆ¶Ø1**J ™ƒÜ$üWø uK$(5†Ù¼嵆êÍ_]}êOi•äêTc{ðøó«7ŸÀ'åÉÃ'mýÚ'Ö¯ˆ­ò6zÆmý\i"2¯€¢lè¡bGóÎÿÓ½÷ÏUÞàdd›R>ZVR8ÞƒyK¶ðS*Éâxêñ‚©¨ ÒM4“ 87TG3{Ô#]¤2t‘µf]¾$e1(¨Mú–¡ \¤ô0̯´EK{%ì§®Sü’yå`vˆe.`i Š%D‚bÃfëbí¨^²ƒdãqY=­#R!=ª †$¸žEQâÀ¥±F&z0dÁ¢ÓMæuÄ Ó÷¬{*±2ÖÛ ³óÖÕ'_·¼eÊBàV±êÂrºëâžm¯1z óÊqÿõ—Ÿbù}w o£ƒÚú%tÅøXÂ2Û¸§³HRؘ±#¸5zŽÌY¶œ¯9ksÛ€és'w†|—WIÁ–×|(éHïWËrF•òÇ”^q Ęw%¨'x|‹K:èÛeçsªÒVÅÿü…còM¾2”aW¯½Ëù8~;Ñmãƒ[½ïÜ8û ì/0ÿIÔbè…^X¥šK¥Ùd‚‘Àsïè+úÙHwÙNɉ=`Ä®ü‘§4å òbNá•ÏØîÜŒ$2äšbëˆëÑÁuÄtÔ‘#3#ê…NUý˜Ö#R>û)<Ž~ 2Ž$Dv89‚_fÞrº›óÁ*E¬L®20SÇL÷r U¤ê}A\ŠÆh«‹ûoTgÝ“çÝËG&võã?°o~Áp³Úá¬zûÍúzgq;ìíø]ú9Þb#Þl9É·cæyE#6d3póè—Ý£¢,JÍj’^Î^ƒ`³×@R76{uF+ 1Ùô0ouجt‘+W V²{¶ ÇÂEMí¹9˜ˆFºHÐ\~æªô‘ C¥øH«“‰Îå©6©š°Tµ­Á.åžKÞ×A-Y;q'*›ЫˆÚ$h]Ѷɤ} j© ®ì$1@ –I˜;äæM‹ #ÄrAk Q$õÁ$„Po S£è¡ÁÞrÕiëƒZ»ø\uöÆý¾ybXš2}-àl$džÊ’,ü’ì'N©/žZYÃwݧóvË3Çú·¹Ðcì)Õcì7‘ËÜ{õ-¯†‚®ó’ˆFfŠkØý^Ü—çÞšˆ‰¬W¸ž]Å)3C}‹nVF§ËÒ7¢äëqý_å‰ yÁÏêײÈ6¶ñ[½ïè8þiŽö¨þkš?Oý,Áþ?öÞ5F’,=Ï{¾sNDä­.]ÕÝsÝÙÛp½»"E‘âEÊ-A¢@À!Û/ò‚ lY’a[°,Û‚A %“¾€€mý° Á0 2hÈLHeé‡Ds%šÞ%¹ÜÝÙ™ž¾VUVfFĹ|þçdFVuÏ wùoòC£;+;*322"Î{Þó~ïëië¨À:zGt¨áæq\í¸Ão pÛ‘1ªÜJê²/hÓ¹ ²otä¤çt¿b÷{áíèû²Û/Xߨ½ñ((# ]ë¾±6Z^Ìd'°\¾Ì¦gò’“+Á$Ô설¢eY_FGRÑÁ–®ñ„AUC"¤;¾ŸÒZý™KqñÞÌWþaD WJ0g?•”XnªåÊá'DpŽ”fó0;‰FºaŠíûäÓ=–îY$ö~%~cü2èe íi…i"–à °WA“¢ŽW\í¨Yq뿱=u‡Æj{ôM6ŠÞ¶°Ùk¡¯X*ÇŽã!œO Œ¤Ø€V¢a¡DÍ„ÀUd½{¥œXŸãy¯Ì‹ô Maãx€à‚µÔ.k s¸¤A†>ÏE‰rE\ù2^ ¦ý壄„­1GI«I©,;ZÏ ~«1åP(Æp"5‡]’jŒbÎêêY«O[D–²øâìéw~BùEST “"çØf5¥âs—Êø–JSÝv]E>sJ˜ðó}™{®<+Çp¯0ù6¦³+ê§È5î1´ÈH–úUâ\ßÅžaàñCº¾¨yLvÍ@R.%±8—nÒ£("ûhÚQœøðLñ¬µÁJ¶š–†£{8‡_–¿äÇaI7î?ïÃ:ïkÛž¯ë¸q‹Ë÷syõQ¨€>ÔG¡–ø?‚>ýg˜ü!ÜoˆÓÊ=¦gÒ-›9³o‘‘v*ɽÜ8RÜÞh`—ýPÜíó7¶¡ìÆ–jßA‘õadŠçöeŽ[1´ÜÌCÙ½Ýø£U£mËn;ƒo¼È ¡6¨#Þaõé ÎÎNQ Q F–˜—ã,)#͹2XL)ƤBB‚†1Ø~úý¼ùiòvóî&ôiõÀ®1œæEá`Šw[õ×t½.žÕÙÍÍ€D4 $4Ð%¬¡v¡DÍl5Å¿bÀ”•-_¡P)ᆒˆ1çÆì¦VCv¸\HPÝCÏ»IL$&Ô"‘Þs-,-kÃܲP¦‰Tüòt@–JIt±,ÒWÜ1L+ˆRf{’ùÌu*Žešíß²ïŒåØ0P¬¥v,È 6C>‡ÔYåÞ[ªáp’-z»¼a1qMdÕU CÇâ¶3¡寸¡SLÚZà <ù¤È¦Þ­0m¨"rdd~Zß›ù”ؽõe™F¾íM•_Yå= ‹ÑÊÉ ÝîËu‘öÏÕ’I´°LNx7Œ®eE-aÛN0¡­iáÙKÀ¬¥ö¸ðÐVXC#45AÃ:Ð^ódE¦UÒpà "ym"–C`IJ+$ŧ91w,×{S.ĘFl‚Dëq ÐÖhÅLi,CCQW­„þ}oR'§ÏkÕ ÿ»ö¸‡úHÔ@ê#U_%þV`ùoÐý^f÷©Ï0 ª—sö´-CoU$‰7ØÍbW¨,¿OKë>-}ƒèå}»snãi½Õö—FïXíÈûv+á ©†9R›aHd%ô Œ–¢UI(9yDÊ6ƒÎxø5‘$ŸO¢æü”(, ¥ˆçD4DE 6ÑØ<ð ïp‚ó\iþDi‡>2-Âv¡WD±‚u›¼¨¡v,$#ZS”Й²´ $Kó ¥2ƒÜR½‚¬¾0ݤΙ5 ]–¦øoìþŽ;,×=uÌB—œÃ2ˆ>ʤep~O!¡†Úb¶­eÒtž.îü£ Þ¹ß|ǃîüšâd7… -ƒvD3-Ûd”¤­ô‘®£MŸ=^nÆÁ·E••¡­¨*DH:@YR\5"Æ£«¼ü"ÐK^ÑÒ¢”×Ñ$YÍhB›Šz›¢Þ¶£Á¹Gv‚­°‘Úc‡‰eCwLwÄñ Ý@âYÀƒ3ø!…rÜð\ˆük=s‹À¦ìóÞKèC}$ê õQ­ôS\üÏû/ù-È&ßNswÆâwS×Tw± ê—:ÐÃÕc ã•w$‰¾Ñ>ÈþÛQ憦ð¶9ÞrÛ PJ“²'Ã{…Q·ß8µÛì‡ º]<ãi*NjæcÄÒÐ)Q1Š3TŽÆá Iy¶âº'ŽÍ:¡ÁÞ…»)G?(ŠûâÙŠ´†ŠYâ¨G´ØÅf$É6;®0©t•l¼4bDèlíï}×Q½¨übë¿!L µ™þÆë“ïÆ‘*ªË/6O¿PÒ¶3é;8G²Ý” Á Ķ7ì¬8Í}b>¨ÓÝ×YäÎŒºëöç‚æ ©²z{ózòTH£ ´…ᾂ՜›™3Õ8@gƒx% µ`=Ë„×8$¹©±—’Gâ82Ô’=Qš{Nƒx…hqêm†xÅ”_mÈMÜ®"…ö‰¹j™Ï ɤòuoÕ5e•p‘˜Š‡t*-í\×´DXª!M²²‰OšâÖ„®—Jm&ºú¬œüýÙJ Ó’2ÄïÕ#¥¾ÛúVŽdU>™SÄ)}Ú×HÈHñ¿½N´D!ê„Ù ©èâKŸ_ZíìG”2;-²é½U Ùñàµ\/ÖææS£H…q8E,ý„~_jÖ«l“¯ƒ9Ë0—è÷Ó{‹`òAâ÷/ý Ã×ÃË¡> uЇ:Ô­Ò¿‡Âú—szË£»¹Áìqòûh>ƒ™S݇Qz5âÀØ?Ä}yôX¤q£n´ûØ[Réô%8}…‰ÝÓzŽW®©‹dÅÛ¡ïõ£¸²®ìmƒ­±Bc˜¦f޶ã2†P˜ïxŠ ÈšŠkÁ ßÁW«Îpg–˜'pÙ ÌDj»4w§Abª’iÖR](¡W™bæ]ºzÛT_»S9ª{Õ†à.«ß8ŸÍ¿ôåÍâw¦£Z+„dÁ2»¤zú·;ª@ˆYÖìÁ§œÛ“gd¢÷NÓÙQ<--t¢ZOÒlª&šùÒèµt×zym_%¿¶¿ÐVo-În»gû¹A`01Ó½ƒç]>-LVM$GUÚY‡ÞÁ:5[–UŠ#÷»‹S’à”F‰6Ð*KcYx.”F9J»¡“Â|Wœ[f’¥•e.YP1Ì¢¢B½=S,˜  ‹K¸¸;‰%Û¾%ÝÚéÌ­²9+s ÆLBûv8•†lï-÷œ-ïÈó“­¨c0éØèÛ• kÐާƘ…ÑkMG>¼9u¿r½Ÿ˜ãÊ-õW)chëP!ELz_å·¶Ë&‰œ-3X3¢®oôHÜx…´o"£…)O†Ep³JÓ" 6çøšd Qí³ôN¾¢£ÇÛ34~ˆl”q¥}ùÖÖÖy:ÓücÈ¡> uЇ:Ô¯½Ö‘õ_|þ}F3Æð ÐŒ|tj £Æ¦m™‘ߣÆA7Ú@ö-ó<{ÚÚÛ‘ÿoÃ@Ú·{Ù´…ÌN`‰(ôŽ¥€0wœÍ™÷¼½.j–â¸O¨kâŸÐT€áx,Ÿ'D8 LBNš”â•a´¦~jš6MŸ&»Át'G_¥z¦v£Õ“ºº7ùܼøú'Þþ¾fb&â¹êXò©Ù~Êv]b{fë¿Òò·“L”MTUiz&ó09ÖI“f„#bCªˆ5:ÅLÁ ÜÀh.¬$ƒ6'³®ûZõÅN¾øo&;¥ bîäÛÉçݳA ¦Âi¶¦«m¦QR$¨Å éÙ‰I ;xÆ(npÙ–j*CåY– œCR‚f‹á.e›·qŸ ¶¤I"XêA«,-i‹¶¤s–SÌfh"´8É"ò!$G-F¿ýÚF¬ˆìá{.M„ƒ ÚTñÅmÏT…¸Ë*Çd8®;DjÅîX©±]P#²pz…wѽjyÚð´Í‡üËU‘]õå 2ªJ†ä1¡lJL¹$uŸ$Ó´Í®+@_®ÊXb«Ñy>TÚŸÄŽÓ=o÷ûJ™Öš¬¼ñá™" &먥W²Ù·›[& ãz®Õ# ¼ IðÆ¬ø¹Ø:Az÷0Dê£P}¨CýúÕ+Ê(Òdö!ò`°¸ÿ»Ã XíÆø]ûôØxh4û®[•çVº=èz4N÷ïÆÂ…}ó»8zÇqWeÌÌä*ÑÁ]˱ã*îÒٌ̄Ó›%¦Å*•Ë[Pqgƽ š°³™Ä¿ïLX7ÕÉÛb׈j}ç¿:?úʼ FdÑØùä=cÿìãwÿÕ?ùô­O?{‡IÃå„§ŠŸâ,&¢=’psÚSž¹Oêí³,cÈ¿N1DÉ kvSƒX¤ÎÒâ@Õ㼉 R²f²°G÷íÇV“ß½–?˜ÜßÁÿÇ'P’HÄ04Z\6 A Î`5'šqÓû” s(„!ï0±ÅòBsJºQLÊÌ©TLãY " ?Ä’9ï&á•0>·¢g…D4˜ÁZ¨Ó‹Ï·ÁÿÎFu Õ‰¤˜/Æ{qѵ¯«ïLŒXŠƒÌ'w-/}Ç×›”­ëDw3ÁFK‰ÖAH 6B•E&rdíIezÑ—gñêÒ¬ÕUš>Ù˜§We۶ܹò@Џ»y´‰¸fy¶‹i@³ïÀÃ^ÒÔ¹±/G­Gi¿sñÆ`{;%t»Y5ÚI[&ºuhEŠåâÅ—C¡+Ú•±9½”›À ‘$ÕØÚt¾pßvwhh™´Ä¿~ õQ¨€>Ô¡~êî×9^à6{î³Ôå™íâï¸g]G ºŸÚ}›þ±ELéFHzœ«²¥ºý(Ppëàq7°ï.BÑ7×#^<åq=Dú³šeŸ­6¶*Ò㊉a2åä.'=¡Ûí¬&îÜãu}@t„!çC 5ËSd†w*©7:K§_ñf³QyiR-&fÒˆMœãïÿ§¯}ì»ßù‡ÿö׿D#Ü5Yw•Õ ="I6WSÝј*˜ ;€fÙ†äɦJþŒ6:©kª(1¨F$Ö‹N‚,go>:ûìÏ_½¹üwOëHnð€+¸ÓŒ¿ÕH4ÙŽc׆8ؾłž%û— ÄAô Q±%0%Z¬ænÂÁëÃ$ÌàîìÙ‚³L}¢t‚µØ-=œOZúãn¥#Zª[Aƒ)LG<úÐÿ–9X›_'«É¯žšuì§´_HÀ)®¤Îô-/}œOV?“ˆ“"цքD4æyBP¢Á4î.!CÒ³ºÞuâûV{ªÑ™ù§WÕ—‡ÄU¹Ê†¶Â4@§}¤HL¶—×£%sƒn8lƒ à°™n³Q(Âèô!ÄcCn ®ª²M?b”ÝHéAÁÐõhÉKö¯ëá°„çuþÝ®÷GÏ7ê¶?´@þýÃPp¨Hô¡õëQ‹ÿ…{¯g€±…Åú‚•Ðñ®»Ž·Üî'žÈ¾´ƒÑHi`²ß$Äh :f›ÛÌ4»ÑKõ£¡ æí‹‡Ñgb'6–ã/[ÞíË–Š -ÔÂõfÂñ Ú±\ÒõXËÇ_ç•{tƒn…̱Štp—Í=ŒåX¸L„©rþ%Ü“$ÎÈÑé¢;?5f°—,ºh¬ð2Õ—>ù]ÿÅìîþåÿgî—TU¦±ÛŠfA+r £¤V̇ˆ*7Ž6ùÖ˜¦@%uˆ£®D­ˆ»iÜÚ*!Íu*çÿÂ—Ž¾ïÞ{úõÏÄòbI¶/T¾Œb眥æ$‘aD³w^î&“Ý7µ$]+Iˆ’Í+†¿’Íáè ¨ÔRmØ„l ¡‚&Z`+_B~ i«å’wè°¶ÊúÛáÌøòr$²ÆÐ(¨ ¤áG¿äÉÚ¾bRHÄ@ëh×Üÿ8oTÿ',=UÊ¢èÄ.±<;– ´t–J44Ê$䃦`:R35.˜¤¦¯Ü²wŒÄ—]õ°ayZ–wºBån—z¾AÍèœXfƒ‡s˜z¹G„tÈ4OxÓkHJ%º]Gºø‘öîªß–îG#Ÿû·Ž­ ÌÖÎŽ„Xë[$ñö®âáÙ‡‹Py.z~ŸºýjüÚ? õ©ƒ_ã¡õ­_Fˆ7þÔÖ«uO‚lGë¿7´ƒ¶,¿Êózn$ Þ0†Ú¾¦52¢Ò(—!½¶j vËç®§êˆCÛ~ÈÍ\‘ˆhfj3þŽ#VÛb-çs3­÷¯½0±X0ÊÙœª¦vœŸñ¿û¯àÑ~Ù†ÅcºÀzJû*Þ ‹)zöów‘©Á}lêïLÂQíÂRÔ FTPÑT{yTÿÕjñÙ«Gç}O’£:AmCƒ s ¨ Éd»,3¬ÁZŒÃ˜œè&Øc0™`›ÞVÉTÊ$œ4ý‘™Ö¶¶ÍÊT_³Ö,^½<úžuw\µÿwÃΔC¶ÎÊeáÁ\£• "Ø!¢Î` Æbë)Ri›µƒæXL¹c—3È¿x%AHtC‹ddx–X*èjî˜bmbpC¡dKb[ÓXl­° ®Sš#!”µÍlOVAºßç&Ÿ¼3ãT¢Ó€z$qýŒjÆ·ÝýYÃÅ ™XêhaF .õ‘¥’•å,àÒˆ2¿NÂð-JŒòÞ:Ä”úzŽZÞ‰Åw"–Kr`Fë0–yÍ´a ¦„¶ LoîuJëëù2U¤#®wAHRâwæ,eeá9·s C?7¸ô†ÅÞø£Ùî0jeÞ…ÕóØâ¥·&çÛç?dExô?sýG£Á¡>B#ÿáêPßj½ñŽFš ¹¹^ü†)º‹©„æ–òRnÛÉ.ëagV=¼ãVÙ<ðIƒ#p¿ŸÎ]”Í&2]1»ÀCLˆÇªDp‘‰2꦳‘›+«V*Ì+lÅã¶|¡ÅÊÒBm°ž‰bûßû|ös,&,ù"Ax6åzAœIB´øZÓ›Nçßpê^jô´ñFª£jb0¢5‚Ô'úd„©™üŒß ú±å5Í9Ó)uÈ*PC^L‹˜âB8Ä‚ËfyCVÜ'sL8¨ŠiSÏj™T¦™öGǧ“0«tZ¹ÅÄÏêÞÙ‘ùËík¿í2™³«¿=Äe˜¢Ð0%{%k ·¸™¬ñÙ­S˜íóÃI y×*ƒš òļý‡œí|–$9ØÎ+±0ßV3®45÷K6±Å‘cXqC\…T8‡(] ŒÿÒμ{ s$8Ðý°«?y&á¸o%zú–˧¤†O~öïY¾ÚázèPŸ3‡“]ÚDžèËDT*á4QéNBa<ºöš¢¤(šÌ&òp¥I“ïïèzN¢Yc=O; Ì·È­?]©“ËÜR×\ôø4ò•V·£”ÂÍ\¡dŽàr$aZ!Ã4Èäu)W¥ÜÈÝN­oÛ%ë ì,·ÿ;Þ+½µâ´í*î-Wððôóxáe?}…”ðJ%š2ÖÏÝ>ÎiI† )ƒ Hè³jcØÞ¬­*«D£~Òš`LëL+²‘xdhÄ<Œá™™‡OÿÁËÇîµÒ#¨¨T:¤xYX%[Á*q«­-üôÀ‡fÏéXrê”hö¤Ö EŸb2ÛR+GåVh™ &r)Ø-DÚ‰oÏN“ÛuKc‰3) ÈwÞÖez¹vE â¯OÞ^Îbmä–Ô-Ÿú<¯¾ú%Þ^2©Ñ IÄ„ÊîL€HªÀÑ+×fkIU÷íT­ãlÛö¬}ò[¯|vyÙó0qe !K¾ërQÈÈ}{Y%TH5}[Lš=Ô%eŒ•‡ž~wzo¯Êâ²—Ÿ7[NžQ»¡EKR¹¸ò^µûæÓ¼Ø_Yoe”Ž[‡uŸŸÞæ(õðî~«ßí{/à˜åÖ}é}”Ð žþM.ÿùÃPp¨Zô¡õ­Õ鿾3Ä`¿ë(–rkA–ÑfŒžŸ ¯ñ:¯Ù&F÷V#a4…çÞåÖ3»¦ºF­8Ò[Û\A¡×Ý`=H òmCPK¦ÂÌröÛ%ÎŽøöÏpvÆõÛ,SËÅÏr%¨%MHS ‰-QéP Ó…‘EÝbTEÐe¬ZÅF“ŒQ×Þ«b¬M¯ONìäÿÃò1Õ1õá‚y *keà%b"¢Yê­ Re “•®ÅBA[D0Æ †VEK"‰F×2±¨ 3­Içªu0ïœ|öáÝ?yïá_˜ :Ê7Q“Rd„adl¶bÙ3XžOà¹sÊlël¡ÅÜcø6’ QlÄÉÛŽcåR¹V*ˉÒË®1mwêɾ 7«ÄH’zÞžšCÖ÷ «ð)÷ü%i'¯M$<àø.¯üÖ³ùÿÞ¢J­´Ò€C #¶$‹Ydß”¾båÃ<1 ÔmòȤ½òÑ'“Mª<^Å'^õIU ŸÜË›‘Ÿ“’¢²) »aÿB] Ñc…Ú`"ë!5p›c?TØ9æ9`Ý IDAT=F®Ò·æº‰«ˆ$ŽcLîÍfTšvòö\fä~³ÝátëÖ¡ûý‚cûjýàß~ã îB/js û~A·3_žýO~ð0ê#X}¨C} 5û¯¹°ÇèÝoÔeSF[Ú[ë¶Ïm32#ÿÇðþw|þGsŠÄö×o¨¨ÁDf×T+Ä#ò½”f$ÍTd^©–bp'#m¤ì‹L0Ûo·S«÷øÄ§¸s/C?cÐ5íWКÔk4”Þ€#(>‡¤ºîÿ²Qk‘ת$SQ£11iS$©JPÛÆÐz¹ì‰›³¿ìVÿjXá,õ‚°Ä*ðJ0l>í<¥Á8$fó ¶† ¦ÎF#­o&µ beD[©ŒÎ=Q.Ĉp§÷ÚO¼þÏ>~úŸÜÇ>e¤€ËqŠ´ †tšm1¶¬°ÑLŽ+HÊqߺµ~»UCh‹š²l`0 ŽYŸ§SÄp¤;/íÆÖºpˆz1JLÈÐígÐaW&‘Â"†û½?éÒQH'ýª¯Z—Öw›¤î²:yçW¬où¾ßÊýÏÿ¯?÷…hw×ÖЀêÂØÆ ʼn ØšÊQ%–Y isèLVŒt|ç1F£‰§«þ²K†ÊŠ´ÁÄÍIþ05œ$^5¼3l-ÞÌ(ë$6|db9¯é{6|a Í~¸w]³ì9oO~Ý=Þ%–ND™”æƒd†Ôú=МFŽ:ìhöãHáfó<Åöc¸ú éóê›uä§ù¢º~ÆÃï8Œ‡úhÖ@êPßBü›·)ÞçDsß–!¦‘­7'vý~Öoüå/_ýR§ÚTÌÓÅBŒªØ¦®©NÕŠZqžê„Qp9èÛhÁ™ËZ ))ªÕ'ë¨ ùàšÚWb{d¶Ѐ5¼y"tŒ®¸‘èygѨ*‹1e“,®a2RKë­î½íÝïö“Qs°m9 uÄí¢PÜ×d³½‡‚ûÍšs+%Ýò†ßJ«¯àÑ­Î8Ç”çݯn?¦Pòázïþða8ÔG¶úP‡új±Ï"ë-z<ø™Ñã±¾p4-ư†t]öCâ*ð¤5Æ/…„L!¡fòøò¥.¸¦YŠMj N•NÅ7¢Ï¬ë#Mš®^úá'üUÉ´±l=¦÷WÜ)RéY‡É :`•´ÅµžV˜¢¤„1oå-T²mó`·5 Üåà ¹*Âì–³ƒ$¢û.­ÎÛóï{¶xmiWîô)Ö+©O~Õ{õ‘“×M[÷ë)³ÝŒõ‰Þãì3Øû?ý¥åé‘ûx•ÒÕuõ@«¯§剒PY8¹Û„© µ1FuZ¥YŒÚQ•¹K‹ ˆ:‹Áê&…΋¨EEÖÇ'+|’an¡IÂêc$’¡L¤……ò‰À—B¹ôÂßB«à„Úr=ðÁ¼îG£/OÆ"¯Ë%9ÖW´ûõvA¦†yæ[x½½WÄýäQJyAðÛÖ=Ûðt+Í4ÁeAÏ¿V¬¬/H(4#óéqõðàOþîa8ÔG¶úP‡úfë•eÎj»Š:)Ýó|^o<Þ®Øê dÐÛ¥ç%¼oÿi–ÿÙÍm¶LUµ7èÚ G—4àªAX0²—Î-]ƒopÙ}-LíJ‰qôY̾ëÖøS àþ“\Ì84ƒD%í{ôˆ?dŠ@œ€C G*ZãÝbeÕÍì†O›º6)%º }pŠbBˆæ:DªÊ&ƶ?M훆 ÁPÏšwã䕸&yÖ *úDÈ ö4+NkÎ+žÏmË4`&˜!ö›ž.ίmÕO…bð]ôm"I—RÛkŠª‘õÊ]_ÝI÷5L5‹ã ᢯.6nÜ£•> !°¾/vY×ßp¦u¹cÓyÝŸ¸ÍÝYè£ûú2 µïfâÍkG’uVÃMkÁ¨¸êÒ£6 É8‰ø“®[ P6Zè¬å¥5ïD–ã+âj,èš01ÛÖí¤VÅÌnS\–ͨÃÁ.ÛI9ÌÝ­< .l-wƉöØŽ=èo Lçy–ð[­sÙóµpµÇ²«•FŸw\ÿ÷t?~õQ®€>Ô¡¾Ù:[ÐŒd‚Û?îÖ°·­-æo—wÍ~âÃv³Þ¯ýG\ÿÙçïÌ€t«}ž»ãdÑ£2Å‘˜2–—­’ èÈ!xƒœcGãUÅ\övæË°Âû2œ@M憹!A«¸‘ÕWé ½C# 5Ô©÷ bIn´ª“‰¡‰BïCëIQÝ ÚÛ|Œš¬ÙDåk Ç&ÔbŒˆ/™´œÍ_y  ÙÐxê)f…F4€âZœp¯¢ÞQ|G=|¸ Ú‚EjÄf5 bB²a$ÓD‹¯µêZLãO56ч~þô쇞>üñ× Á¼m˜SŠÆC„1£yIŠDݹvð J-^Õ€¤S–|2cnÙ%­Äí+‚âaˆè.ÁDªOÄÅï{úò÷<~å,žNMŠ)ƸºªS ³>DU6{¶n®[ÛMí}éfIgQ븀HûŒÖOãÉRü ÞœÛ׫´2^ð§ø³Ô|]«ÇJºðöiûʱNûîrS}9¤æD&o==~©ŽºèUUR¸ìÂWÖéõùdnm-v"¶¢~ÔV—ÞˆŠ÷N“¹|úù¸$¬ ‰ÔâRž4-Ÿˆü#qÏæ–€PÎ*‡Sl5Š˜ñFSÇ4Z2’GzPÂbwùo/0Š d$tf„ãÍ(%ÑpSòcÊEmG)¡zK^’Jq_~ñàçç¶5ëóîWìoáÑ_áêG#À¡>âuЇ:Ô7Uç¿È ,¹5kXÆP¬ Àïž åiò7+½†)Lƒ˜>‘®üä¨vT‰X©‘Ö%ø…¦ÍzsÔ|öã?Ú.r^<é«,Æ(«Ø¥`K<¡’twáé5;£¨âvG'’”iNéMð´…oO¶’ª8¬ˆ¥ ˜Ù\Ÿ~ß;ç¯=9i&†zÓ©=šœ:ö]ST|ðË>¾·ÔG/¢ÉhP×3_šÊ iJÜ`×Ô†jußm­ª5w—&Õ|v-Ó÷lªð'K?ýÅå£Ó—¿|n¦¿z1{âã±õ)O6ñW6qí¼u4æZ£¤ôôzñpuO$ úhãϹ^HÈ<·AW¤'˜ˆtÜKÜ…ÇÜb…÷ñâÄa ]I¦Ü !j‹Xy{EÇ‘^b  Ò"_9Üʽ†#hF—‚†·Réþ–ÊycÕ¥ïÐxÌaoƒB‡ÆÇnd‚ù!¹ç ²oàì¿p0­;Ô¡8èCꛬÅçò7f˜ü ¼êÆBF³ßct»q; v°„·þ*Oþ©Ø™WzŽ2joÇ-Çϰ=2¹䂌†x!Jæ*s·dªs”uKàÝp‰#ÙÉ)ÜýËÀϯ¹oPG6ñOÀa! Zgw‹(èœÚBÂ;4!ÔzŸ’¸Me‚HZÈ,®Kizµj½ö®GÆXÁ˜äÅØ?{=yùëS®XDò\Ìh`âq› XŽ h‡cÁäp_Lú†y]ÖèA\@:g²ÄùhâUº{£­®¥¯Lå;›æÑVi{Ou|ú{–ËŸ<*¹Ü»“@Š‹3; 9¢ëLIHWD‘â­z:ÅIÆ¢~H"ŒôŠÑTv{Ò$Ńœ¡6ô©¬.VKde<“ð[dòæÅÝÏù|ÑW¦#¢˜ 6¦””$¶±_ÇË\R]¥®™†YrÏ6SçÁ/Â4xS ¾Ü‹žfƒ­‘ DÔ¡´.X°³t3dÆ=‹"шŠU™cß½<ºðTšPRäá&|qU­E.ú°°UmÌe;{ØÖšœHRjµ}É{¸¡ÃAB­€gÂãq^ýøOYó9_Pj3²mf—8˜£ßWU¥2gιEó¬ÆéþŠÓ°˲*5n„`t0åe‡yZ&à ;Ž\ä¶è¹‡eAÞñÆþk¯÷‡Ý²ß€xñ‹ÛCŠr½êP‡úµWûÒw)mþCm­0̾ Çvø¬Êà·m*[Võ°‚¯þo<ý‘Þ“û?ËëUc8ºf¶¡n1@§˜òv9ôB!Ü2Øeo¶œÞá^rhG¶&в·i”ILa§£` jzCQE{V?—-rU‚Ú¼ÚæÐ`"•E_“ª•'DüÄ´8h<HTãÓ°ÐÄзŽsKeHB›ôÞZð ÏÇ€M9þÚÑ&ºÖâÖ¸5ó€)ž¾ÚÑ$–%ˆÇ¶™ù“AG¾,\ àÑG4bo‡;÷&~æÖÒ[ã¬hœ§©ef¤;ýüÅÓ럮÷¿§-½ûqx`pšu ¦(•‡d›µÒ(5ÙW.E‚Ò‚³ÙjCÍÎ…oÐéÄ­Ò ¿¢%éP?+ñÓ©{#Ä39=^ßYøãÊUFU"VMã+íCˆ1ø6¶Ëpteå® }Š>bÔªšW!¨:›„*²ˆTz´-ŠÞVHƒh 6u`ïÈVÑÒWWò{®‚Vf ÑËõW¯ú_íq:±xÁôš®}?5‹÷V“¤*ªªNL »§}³UÃHKj³ù[ê‰-½áiM©<~Ü÷¶ Nd1˜Õøê“[m 7:ƒ·š`[f¹WÏ{F¯¶†)ïñqÊÖ»Ý@–e6nGï‹Î¤+­¡| áÛ|Ìö{¯×¼÷÷þCj¨€>Ô¡¾©Zþ ÒwBÁŽ®üÊ0¹e¿dð–Ñêm¼µÊ¼†GðÞ_bùÇ>Ì^4Ó¿ytöÛg×Ô>'v D2ÍžØ6‹ Š&{ð0óK¡Zè4ïH–»Þ†„o÷ÓBÇåónƒ³e´è,øHh<í%Áf‚>šßçÄ&ë0 4kO *•˜Úz# Î*Qc’ª8ê$‚^·“£‚LÖæ÷¾'osèbêH¸¦‚Sbƒ14†Æ2 UÁЪم#ÿñµYû=‹ðÆUj’Ѝ¹h¬R¯±’8“Hô)õ$±QW)]¡ŠÆ.¥µ—G×Õ*ĘˆÝ‰¦…RÏ"Ó„ znËÇJÐ#(ºí | =† ÈäjúýC¸9‰G=N±³øh<¤ä®Þu XI¤‰"ÆÔfý2  •bÚÜq"t¬*®,àg÷ôÖâÏèLnª|˜*» ’Ù]¶ìãæ± †=]N~3ÊÐÞj¦Í¾aóYùÝí»ÄQÒ\Àrøú–/¯òi¼óŸ®¾¾ùýKn¹mnë:òÎnü‡:Ô¶úP‡ú¦jý'hÿx¶kØln?µKnV éhvßpchÔ»‚+xð§Xý¹|sáTö¼s<9:ÁŒ”Ë:(^«’½ÕáÞØ“7'’Ò ­à (’¡s…~¦hTÆ ÓL‹ó@¿/ŒnJ`IÂUädM ïÁñcΪɇÊWÈq+ï5©ˆõèuë½ ‰d„©ìè­¨M)¥Íµ•eo¿çIóO¾ÝÃhfâ¨î ÷N˜Ìq ˬ׼ý”î’´d¶„ì©]d&øj—‹.ZPxƒ > bÀÁ+¢ *!ºGݹˆ™›•¦:¨¬‰³äª;¿ãÉã;–i]ÎG„„:šD”Ìû›¢“þzE‡'5Ÿ1ƒ;ñ°S åÈ0‹„ÀZ°–™ãHh" œPC-T–F!¾\÷ož¶÷&áÞ…8µQ˜,ûù»óùÒ˜ˆIGN­ô}ôª1Q'º¨W‰(˜MŠ« ½\¥@< í¹gj̬ YC—È3˜dí8’Û©Š¥PðÈf ‹|>$%up =Ú“æs"u I™°9q S¡ƒ3·’6Ð#Cªa$n@ðÂFè§Ä˜£_Tiµq²ì+1"¯ÍY̹ \DV¦\’f´"DéÞ‹å²½‘>áŠ=õ3ì9¨§ý7hãiùß­iF E•¡Ï{—A€7üøäy-’Xú¼ÂÛ„®¬*¬á¿“ôw7þCj[}¨C}³õäÿC>GS("Íê…̃ê}ʨHG”aøñŠ·æ=üDeÿpc§Í”Å)¦Éªça©0fG¡©ì56å•hx6e­¤Áƒ9¡ïˆR†MIca´óõˆ;FÖf„3î¹Ê¿(Ê&Á1`,~€Î–Ú‘8ThCOt¢`:?lC+ÒÄ=r8ѱª*¢•tFî<«þ¥¯7÷VÕÎaáøä‚WŽ©§DËÓžjŠÔ8O2l,Ì™zÌáç°+tknVðêÒdYˆ7‰H•ŦZ— Ú##1Ùµ¼òÐ/^R{„WµÑVuýIýÆ£ú7'ÿŒŒÄ xÅ(Ztˆh$ÕL-Æãä*@6­¾•b‰´‘Ö01L…(„D¸Ÿ2u9Øj=¿ßúÍöþ«ºXKsUÛ ªw˜=`ú¦kœ±µ± +µU1ISŠhÔˆt*¡%,ÓÚk\+Wá8u ¤ ç©3zŽè6Vº‚Z¨AYÒƒÍAÞêòö²„HZ†í7åDòh‹x¤šè1؈$¢’*$a:ì€1$Koé”nJZg»³Éë0pª<4·üã $^»CU‘G #Æ,›¦œÿVåjÍe)/HZ¹m^¹Eպ߳hG]ÅéÖ¯èóž£öêÖ[lÿë}ê líÛ5<øèùP‡ºQ}¨C}³õöçyäÿ Ç¿ŸÅï`ó²À*…lfÔ?´mð·àáÃ[¿‹ø·>ðÝŒüùÆý{“ 29¹‘u¥¦àõñ(nöÂ6¶ì•W$1q`~ØRr|³†‘Þðj“2T÷0h…Ó¾wµ/n-Lp`”ÕÙ5P¡y‡Ó g‰© ÆÒü©~†ÖŠX‘ÓJj“tH™Ö¤ôbèí}cò_s`¹cx½¦:ÆZNgÔ Ñrèo- +¬ƒ ¡Í޹MÏd†ÞƒeÞÏ¡q0m¥á#AqvážhQ³k)3í)ÑÙc1ÑKzONgõ“c»VÕ:ÑÙÉYsú{ûGÿ`*#f/â5Ç j‰ýcðc¦Ø | +9B% D­:rG`T:ƒ+ÙÝ)p²“Y›ŽÎÃKßÖ½ñép2§¹’ÉF¬šÉ»zòw©®Ô X5Ue«…q‡u¨J Ò#!JÚH¸©ÄÏù:O{œ>Ùè˺9×*ëq%¡=Ø¢ßÓ qh,g‹Ejp9‘<µèò2º†‘Û:SƒÄ+&J3åî1UÍÑÖ kx™KxWHŽ.¯ñKäc‘) S²¡ 3 žIÇÔ°‘‘ÍsQÇÜ»“×&ÐèhNèGË$í­±ØãFÜÉÇc¼]t²û›¥}ÝÏK6¹e¢ûnÜr¼~Ÿå¯#<øOhìp¿?Ô¡nÔ@êPßZé»,Œe`æÿŸûÉ,v/›‘8Òà ~õâñ¿ø¡®Rû÷§î7×&û“™9nÁÂbˆöVÏ"»qèA3E!-D¡JTC ÷&o¬–dˆÊµ¥mF} ˜–öÁ ¬Šr#H>޾´UU„Š áhF·¡i x-1ehè VÃäI]]G­ûTáØIc²ó­(JHî{7Õ{¸/|fÆ|‚­è"¡U¾²äÉ%°1¨”œ•ãÓ‚pÜc*ü}ÌסF58‘¢•°»VKZ´A·ÖÚ:A+phLUOÓä6©ú²_|¢‰Ç¶ B²®jîØ7/Ó­&‘bi8Õl±¬B*¹'ƒ-tÙ6çl ¤„/Èa )j1ƒÜBä2q¥Ç¯Ä—¿§;ÿö09g¶fò”ÙJ«·Ñé{Ä;HMp6Ì6U¨·jŽMµÀ©&M}²No]õO’¯WýgϨ§j]w®íY>é¸ÊaSð‚vŸ¦$ÏOAr*$Ö¥ƒ.b5œM99åäˆ{SŽg4Sª)%U„D·¡ÜÌ1â[´æÂð5…)&!b*Îb<Y(Shã.CÌ×̧hÂ|`‘¨ÞR9Ðö˜·£kò¹Gµ)[(:Üö`øzÀy6–ÎkÔbAÛ¸2#@L…É#mÞ‹$ ÖæÄ˜c16©A§²q|Λ;}iÊÉõÔ?»®~î]ÞîYƇ7Û)¡3Y4'†)hiвíxÓ,Èù"5R“#r:P¤AI`I5j#&j˜ÐªN.Ä–ð•þøSõæÄ¶I¥ëîL¿ý¢úM1üü0Íz8méÒÄ`4cß± ki“è"ý›b¨,MÊZ˜ì|WîÞÉIÿê÷ö¯|.Îέ`4¤fÅdEs…éÔ$m–4°ÉȤң#/“°1}4ÉÃÔL¦¦® )i¢ )Ôñ:‰M°~mÎêd4­cRõµ4 i›gFL-HŸ}Å%°/‹c˜|]‚cvÆâ^}ƒWïSͨšìe½hˆÊuË:’„h["DÃÜæÓ&lÃÃ)]"yÌ Y¡JôˆGÓR ù—xýŒªbíYžz4`Ò>A›n5ºâSÉî"z¾Ƙ?~Qt¶¼8»äE¿ø\Ž™²«ö»ñþõ™…O™¿ëpS?Ô¡ž[}¨Cýz×õOòî?ÎëÿtþQ ’ûÆßá­ø€ß•ÿJêù¸ž7–Z˜ÿ(È &0A„ªŒ‘2V·K Î马÷ʈ+Ï xRÊÀ9°Æ蕨#ÐïJªb¿oG`öƒÖ¶´·/y‹ixkÍ‰çØ  R" š 3Ø»¯á¾X…ä$:wý’{tWÕÄ`i+óSŸ,>}ç˜fÂ&ðà_þrõÿ^æ€Àñß&p\МÀ|ŒZœËÞ|aŠkË‚ÂåW91w$! mò{IÄô$kT’H$4ÚÓ¬Œë[üjôi7;U íñôÎõñ¸|òó “ ¡U0š=ÕP|$’c­EdÅ2ñ1 ‰É`‡hƒ«hU®ÓäþæµïôçwmlÚ:í´ºVwMq©¾LÆ£1Iú³ÉªrÚŠKPM£Ðvº^Édb¦1BÛ‰D™„z²y#-_J‘§h+Ñ¥PiÚ§iÛ5J²´})©ò4O=DL‰¸`ò&ÇãÎ]êš*DÚ½Ç÷4 brxÌ`åÑõ«+øÄºÇGT˜ZE”ä0ÓÿŸ½7•-;Ïóžo­=UÕ™ï¹sßžØìƒL‰i*"e!![¶lY¢Y’E Hd)ñ âH¢ ëid%‚m$’óðC Ñe ;¨ 3ŽkȘÁü=„ŠLÉj²È` oð†Ì2ÞœIùâ\ïa£˜ŠÃÂøL£üÚhüµ¡|_°ä7_ãÙÛüžëí¤/ϰ†´ð,<ÛiMp»—yE›Dðµ¨Ag):÷’(íÞ ’u6زt+s˜ZBa‡¨¸JÝ„Qk ?ç³'¼îÊÌû‹»¸s˜Ü2Ö{žõ4¡k– 1XÆ¢ Ç,u{ªï5ËÏÐX*Ð@ÿ©œ´{Í/¾gQm!µ!P5T5Ù ã¯Õ±'j4 ™µ¥iœ l%¶ Á‰oµmC[ûfa6'¥oƾηšÊ_2.wABÈѱøÂññ.àmºKTPD-µiTеŠjâ¿~Äìl=B>"4´s2áТ–Öácm[QaîñQªÓ†:0ó´-Þa…Ì e€YÅ&.8Ÿa RQ92pÑÖ°áÛL¶hµs@ß°Ôý5ájÿÀŠôBÓ×ôª¿ô~{æ60n(”Ì'Ž’*œ§ß˜±_Тš¤~?A•ƒŒE†UüúÞ±Ivu!ñæ3ßýKÇeRBaÜÔŽßýË„¶¼SƒˆÅMp‚*¦ÀTPàw°±yxǞܷ2»°ñÜ¥É+ë­|xf¿éõ›<ÿ2Ÿ¿ 5LzRrà(%¤l'–œ: ;>íà p#qî{ ° -°¾—¤X¥R½¤ÞAM>ÖØÎZ“¬\dI4¶%HN‰ŠWÖêü©^ö­æ­Ëk—ÙåÍ>wÿãtЧa„¾ó{&tu{¿tÛ ¹Tkª4Æ‚© §8pÁŽçWß=Ýy+¨' >²&Sѹ¨b<Õ‰§* ˆˆ±…ï2§¦!?°NkQÄ£'‹mç6,Ûøm;•ÃéÞ|>ÖƒK¸RUäÖ“Ù¬rß@‹,í,/bùYÏÔMûÓ€Gèè`³Çl›OÙ`Zª ø€Ä&à -ä 9.˜*Ò ±m1ë:b£¢&.J:ñFÒ^GáQJáØð脯z„ª npï»,…R9¡ç<γI6½<Î àEÈ¿“=Œ½@ù^F_Ý ß èmÝ,rF˜¡½[¯¯‡—¥>cf§½LSé9CkÏO³O Cê^ —$*2\ÂÃÁ/“ô€o€@ðïû‹É‡©?u»3«»ÆñÖÔÕ/ZÁîeÜ+±†‰gä7dí© ™(e¼IÌ&á”áß/kŧ™ÞWøÒ3Ä÷â!d'Ù¼=ª¡XaÓå\Ù$äÜ œ’##êÑ+ÙÖ+›þ û³zÜ~c{ð]·oñâž?IeI²<PtñÜÃT,'»¸äxм1ðœÀ !¦¢hÊeLAŒ±‰A ¨ê´ Ñ‚BJ˜‹É~AÀ8¼Ñ:£Ôh`¢AX\£¼ã%éײ®·wße®ýäÉ«ß1ÉÑYÀ’ËÈcQ5ô˜Žâ“Õ]¼–.‹;€×¶>ã ’9A:¯·(7Yº¹ )p$ šø\r±´þñ%ÓÝ®;Óͨsv ¼#W2ß¹mH´ °„€x\Àe HøŽh ˜q ˆK¦‰-ªqPxhGx|Ä»¥Q7´Ú©DÄ ž¹§ÖtÓ…ÞRp½v»Ì4©à*äÊ ™ö'!½jÚ¿ÉoP}”ñD~ì2Õµ•;½~¬RèÝJíÚ9œq»;c*Òø÷=÷~$8«é§ý#K,<‹ÿy˜§ x zÀ€·å÷ríc§~KΚ䶧8³+§/ñÂQÆQ±Ý²YSÕˆ¦Â_L€³I ÍéABÔO/ËT¶Wý²çPöS« ·‡µ:±?ï9}«ÐSÅGÚô—æ–ù{˜Þ»ŽÿŸaž0à1èÞvh³’h¿Àò$œ«ßšëe0³Òx„û%÷ v+¶˜QŒ`”Þ|×Má ^zyÂë›ïýÍâ~†âÒNÏû^ïgÈ0µ%d¸Œ‹†P"ãŠÉƒÍŽžÇÌùÝ9º¯¥ÄuUI8e<8XÀvháì%JÅÃåª5ÕÞlÔL8t-ñ¦³ý-FHÙ•Ney‘+ÄvhKç ëÐ& •Ñ&´™ú´YuئR©çBYgÙæµ£?¹÷É2«kj ”¤ÓpÑ‹ÃÓêYs“®Â/Ýó¼ß¹¸Ø~T©!tQí¹’;-$PÌ­ùT<¶ÛŒo3Œ¥0 N]Óz^Ûæ²0Áúªl3œƒmŒ¸Ð*¼ÌÆÄ’m %!࣠%2æ¦Ûå5™‘Ñ ¤Åg,â:§Ä5¸šM!(âñ9!à h@S3Ÿ&wER,K š[EN×BÑ8ô}Ž©qŽlÁx«ÓO‡ÞXªóN»-ÕíªÊ¬"Š×Ná6ÑÑCŒ•wáàKÝù/±ø?Ã=à"åGÙù6ÞÕD+¡†=É•¶Ýåõ «w«¦z”Úõ8·ïy;ž‹3Ç9ø¥až0àa‡K0`ÀÛ y’ý?sÚ{[ñʤP<ÿ%=ri{¥ë^yl‘qXR—XKæ»"t—h×#”m̵î‡Õ®ÿõF¨~œ„í}µGô»¯R!¡ -pv‹½mò˜v1Ç!øZÇíóÃ^Koÿ ƒ{Ù¾—mëjEâ7Mª®Õ‰ß穻ѦnË‚K-ù`Ž64ñÊ2‹‘Î±ÎØÎ£ZlO¼j–ª*j‚·Á›àÐÃDã„6åL0FEÈF“,Ûzùà—J’L=¥¢t.)Vº|oX–ÖÖ§N߀`3J²bþð×¶ÅXÆ`D$r‡qjUm Ÿ‰mªFò–¬Åx²…àĨ梢NÛV%ˆfÛ’cÝ.ÛU ñÒLd¶E|.‡{´õøB;e4çBMÓÒ¸Ó_åT”¿2l+›VÝÇ,£Œ=‹uàÉÁ<Úâ\KÛÒjϬaÑ2÷hƒ£u·sbAZ4°8d,m@RcÏm±&ësížL”Q  dñÃà xïÿÂåÏý»ÍÏqðŸò¼ðìG¸ùkÌzkHÒ°oW{驵În„  µ—Âè^&èÞ † ô€o;бý­]#R¬T±30«±º–§½žÎ°šý«†:ªZ›ïèÑ_V+Êö<¯«ågÓ=›ÎÜ—Y*ÒÑ}1l‚u”Ò6´áöGmWÁ/³Ä^îâ>w—^噼 Ò3‹dÁ¡PÁ®À>ÔxÏÅšü¤‹t‰/7Ú,!%:JζŠ^ 𡆠‘f¼UC°ø€61ˆÉ01*¯a4bWØ4Tcåd’}mo·ŸÊÏ„ÝZ%€Œ§‰M„éÓŽªhŒ@sùÝ.«¢ÃI|Ë*@ÈÉMÀ”Ü#ADi……2ñ9ª¸ pEh2£YæJkia^f„¶Y‰ˆ Gwù¿•¿øÓñ…Ÿ#ÛAËì¸÷qšÇ0ïë6ŽX½…—"é¾×$«j+y°@ki¶3ý Gƒô€_ƒzÀ€·ùÕÓ¸l]•8Ÿ¡Ågv`×Þ0³WW¶¡%”)½/¤:tyÞ«Üjk)¯ô½oâ¬Gd›w·é¬dL ®ì2.¹¾Í¤D !çÖ ¯ÿ.Œ!§)YÁ§À,©8úé}×}@”q–æ3 {ð0ìB T„ç¸Ó2ºÃBð—±£XÅæH‰¨àm•-jq–Ö¤öAPUKÕvIª,ÒÂ!€–„ £HàÀa„ ‚4 ñäÕã—¾ãµW~65JˆAƒ¡«IÛ3sHÝ” ¯`‹MŸo`p6ÍÝŠ *¤s‰è‚Lj 9›QQtš©VlÄçŠzãG„Ò¶…•¬¸J[¥5¢èÜPçZ”hêùŽMøÀŸâð¿õãäOF“¼4Ê•™—aÏÂI‹Y`,aŒr!ÛRvJòŒ`Xhלwßq?0§³ô²Ú—]#2³¿KÞâ=(™vzŸXç6„̤‚¿‚âW¯òð#ˆEœcѲm¹·ôAã°LÚz›rãé¡z| •Nj%í2ÅA µà—>ëaüWxíyý™/ÃÔqðƒü å²ýQ6¿¹»y—ü8¬5>Ò»_Ö ÏÀ%µ~ô€o?|W+•µÊ9®3ãþ¯4ÑÖ3ÏÐ÷eÙËßö)a¡¿g½®'YJ-ÃÊ›è*Íëa„ #¸û{\»Æ8ãBÁ¤Äš1Ïÿ&í(aœ{;éUÆ)έà·ìþ#²*êXæ¹ìÃEØH¡6a›ûwÉpL[±Øa\b,"d6Eè$5´Bk;Ë Ó½ÁÚ¶¥&¿¼.¡z†h‹/0 #{Q¢ªk;¹þ®ñO|fúñÈx»#ZÊHAcúMF©Å(!›å{.vÊ,Ó¼»V1 A„”¤ed‰LÕ….À8†Â$Œ”¬E‘ Úni³e²`2¢b©-U´hSPc ¢Ô ¾ñóðSAßúŸ£ð{‰ìâêÔΙ/D]3¾Ë{6xè}„÷âJ¾ø<š#9SG¶£, Ý»½Ûà´[LtmËNÁùª¹‡ÀîeŒÃÔ<ŒEÓçN"ÍA!x®^çÏåø’ÓÛ¥IKÁ¥N‘øô²¥5õÖ•ξ:éN)¡‚Õ.l¥óõÃãO3Q^úaÚŸÿ2ÌõßäÖßäVÁöß`ã#Œ¯Ÿz<÷ÃDÏìk‘A¯ûuÄ7ÛÞ&èÞ =`ÀÛŽÝÿ‚Ñ~¥(6¯×"ä4By…M†ó4¤bUžÈA¿ÜwKëSö%{fµÇß­zÓ;`ôÓ͸]ð°gœ8œ4<ÿ\*Tß…<3ˆ eúsmâÐK3 íY×…µ]ì()Ù‡qJG=É5Ž_¥pŒ[ªÏa.²y•z_v1„HgÁ™S”תteéN¹¬"àCç+‘b xX „«˜ÀG…‘Ç(8´¦°û>øäüŸ ÿ¼3–$ƒ¦3¡Îñ:é É Ú»ñ… Ø&}žzÞ`)3ò#¨J#*‚: …‘Í`笄ڄ|¾Ùúk(­±-¢8Ã"£ˆ^Ù4„ŽÝäë>L–ä99|óp÷3Üþ»˜½­ ˜–p û!F_Íø)n\¢qoΫ3î/¸?ãN ZÄ£5!¤OÒÓÖ§i®Nuߥw[?-Ï1Þ¦,Á£mÑ@#°zúÅ|ÚÌBàÑGyÇSdù©Œ'(m 4옔ß¿ŠÄD튆8ª8œt6vÑsݤ0M5ë’b_®ÁæßâùoãðÏ|™æ‘†ÃáŠäÂÇäñSå•ïùr˜³‚®³3½¬Í xô€o/.~šMp«¹Ù¬Š}ÍZ͵Õ§Yó«Z×FkOÐ)=Ó7V="쪀²´3‰ßºªE&í³—L-/,Ø-!(.ãÙÅK¯tMx¯À{`Üó= W#/zg½ ͪÊe#ñïâü–¦×9lsW Û3ÊÙžé‡×±—°)Q‹Ñšô·\”pˆÅquhF°„Ø X!)WÐx˜%gh¸¯l'NóŸ·v.m}Ïíûÿünz…ôœ*ˆ¾ã;ý†hjhô妨ª5ÒóNP³lGô0ŠLFuôa UUoLš 5›Á`j¤&›#³<¯Ék(™GU‚ÐXrÄ**ð„À•k\~S(ÒŽE•ñõãW>ÙDÒ0Ž%ôààUÆ?ÆÎ·Ãµg˜µ¼zÌçîqذXPmÒ6€©ÁÓZˆÙàÑrÚ1¸Üâ«q?ŠX¶öÈ Ýcú ™Y!ñ³ðŽý}Þñ4YF\„@i;†]+›Y4ç éú_»¦Õ]Z¦æ J³\âI·w±ì¤5šn2Á-×™xê£Üü(/VPÙ¦”æxíÈþ(ÛÍ?Ò¥Ù›3Jûtïg«ÛMnÕíÑÃɯ“ô€oƒâiÀ€·ÕOñÐ÷vå©~Ý—Õžñµÿ缟׳ÎTžè•o—ÂþpÆ[ƒUƒ íQ}Àé™DÊ7’þxÌ¡r=c¿D2š’ùkÖ½s[À\†9Ã8N““¬ú@÷“Ø"µÊz¶ì%Ï»"yŒ-#dâ9¿ÈjC“±(x^½Ï•ç(>KxùöUÌMôÒ€â ÎvÍ— A\0ZCCð¨CR#-b ì M@¶0ka’syÜ…Œ(,æòÅã;/ëçº-$¹ QÑÒcÉÛýÇÛÑ–Ò¢ˆ˜Ô§ˆœvÈ âP•úZuüPux©8™Pg„€šS7Ãê‚EE[âLaul1!‰Úag¦¯ñ¡¿ÀÃßÀB‹“N¹³™é%ný¯ÈÆéjÊ× Lþ<ãoÁWL[î4¼Ür;pgÊíÏ¡ ƆIIpø€RwÁÝbÀ£óÎ({a’F¿?ö–7…ã›{Ø€ñHè:ãõÈÛù¹E{»<ýUTUW{^ê7M'ɨ_ôÉBÑ¥8×KçI‚+ÈB0³ÚÁ IDAT'Ò°¥QÚÙöBê íÔ,ýõg›°÷—8¬qÿôË9·„Ï1ÿÛ|œæqÌ:Lìß2ýI .qëžå|”<Ýýîaž0àÍ`¨@ð6âúO’§-é(^B×D½²¦èxP+¡®npŸÁ™#¸Tˆ¢W¦‚•lY#Êá7ã¶¢B¦ÌnÁ”*°µAUàž#Ô#¡³-W >ùö…±°loQ¦ug'Wò4[!KÞ³·Ë3_Ådï1‚ #x¥Ê˜î.RvcoÏfµoU Â"ݪÁw&ŽC#dÒ™3fË®ŸÜcâ 7áÿŸÏXü÷_þIæè8úìGØù¯¨§Ô^gböP¿Ñ€¿?LÒ¼I zÀ€· Wo3Nì9²ØâÁ•æžïìYóô¥g^»Þ0‰Ô²fz¢íi6üª¸YWEºúÃò4úyã/T`,[9’1süë_RŸÖ’ ¿ G)%[Õ›Ê*sÒž™]ßÏnYòLÐÞ~tHï:nÇïÃÝž7™h Ò2ñ4ÍðBkásó»ä#ÂÜ;i®ÑL&å*4È!RÁ ÒbGhž´¾['¨'XLÖ=žy&ï¼8ÿèëæ§tµiR½YÒæ?çSè·j3ÊQA B5©@J=mö¶î_¥.‘d~:6ÔbŒ¹GiºZi”¨c˜2¡Ôj2ÃΧÝ÷·§ïþ1;¾–Í!›8Ï¢e»DJ2ÃÞu>ðWøGŸ&8¤Æ<ÎöŸÃ¼“a¡œà=¾¡>dv“°¥ÊÉæ´ ‘w.긾òàBk Yµ‚ ½1ãØ½Â¨$8‚'@«ÜmÙJÛ%&zOnB`{Gž"«¨2éB%Iµá8@6r*›Öýû±ZõH–Õ|Ð@p`• ÉbQÈ œb…v)áÈ{‰'xæ§¹ýßðòä-™mü¯r÷W¹ ùÇØþÆßÐm×,oùEO0½Ün:ùõaž0àMb м-Øúöö¡·/¬P÷nÁ3ÁnÍçøAÖ­ËŸÝjG`¿:{F²¼dº¾÷O› «–ϲrv.Îü*€áH¹¿àú&£ÆÜá‹Ï'«æ~¬šïµ Fw¿e9Ь1xÖjêõ6©«ThÏQn’qÑ»\mçåç×+9,xÅÍÐбïZ›Ý­{Wh3Œƒ$éÃŽ ¡mñ½ôdɤÁÚ’yL²jrýû±_ÃóóyiGÛ-ËÂQ; † OóÄOñÙ!cÊïÁ¼‡¥ L[<õ·@„cÂ=hP‹*Zc…-¨¡õ„T?W‡8Ú í«tèmV((fÄöLÀy$îxœãŒ’K×û—7[[<þ eÅlNfPÛžãa5öw2ØÎy¸@rjÏ­4üF=în{ Øuý‚. «~{^ŒÌQ% ízBƒ'd½Ûª„«cXðrõÎ<íÏs'ZŒÉ¿êÙünìæÊ}­Ð´¸W8ú߆©zÀ€7‰@ðÖÃ~'W~€¼vÆÚ‚ó¸©yCÂÊZ}:$~•=ÏϬF¥hÏèàL±yÝïKžIª †W<¹-p·^H¼6¬i²`7ý³LÊõªÑ¾Gâûˉ2©;"!õâ$4hzíhôÜʾö ¾éƒüËÿ£(&ˆàÚ‚`Úí×;Çþ®<ÊÆ%(1%MÅËÇÂѶ,˜ UÌ+„cQC–3ʰ‚µˆÅJÇÆ f»f~þUÈ{ÅPÀ(>¬´¢©f…šÎyÀÅ‹F‰ŠÔ¡ÙÛºw¦HlGðˆmb,! Š4„B S%$™G÷tF{ƒ—6˜ý&ãÍÑæ6û{\.¹¼ÍxB,‚odOþI^ÿ§>{†…§Ü]ðÚŒÅ7p'è Í1(p¨˜@i)'Laƒ‚<=K=',sg—I†(­O=˜ 88jÙ2Tq¡ØÙæñg˜lt®¢½.D%(.t±mË… Ü›bÀà"õ*d=É,=˜Æptk9=ßÔšIWÎîòÐYÕAÙ4þ¯•˜–ó·~šÑþ,íÏrüCò8L°ïFPâi˜§ ø}a мõxø0I´ ÏIr¬Š,Ïf£œK£×ÓUúT˜ž/•YëM\<$žzuhó†bëþÏë'°œT\sÜÆjeÁWùÔ¿Hd—ÞnxÜV¾ W’¼ÄôJ»ê¯wÆ–+?Ž÷ýª]¯–ïàþÒì<ä|Ë·sé æ–ç?Åç‡[÷ÙÉÈ­%±5f\rý.?Äæ6ÇšT´Ë=Åã]ÆÂ¬áÞ=ž{žûsJƒ:ÄbÍ1yK&XC–£0.Qáð×›xWoOX²»ÐU6MbË h^hZ6„ŒG–W¡õõÖöý+4#$úë)6#«(·(wÈGØ#8OPÔ⎺K9¥ ]Ód¼záˆü“qp kØ®È 1³Ì reî8nq-•a¼Á;¾ŸOý2 ‹o¹ÓðÊœ:öÖè :…6•áM LèÚì,H”md]¥öü!§ d#6wÉ8®i…àc´…©’ FyâiFH,9žC–¡ŠøÐů,¡ºÀh›¢êþVPœ 9ZwM{Fhæ0âÅWh=—ÇXi‘¹c÷ûÙÿŽnò[3ž \­8¬™Ž®ä\©åE,YÃÞûÈ™yŽ^žãbÌN:¯6õH‹: 0Ç4¨¢Yœ#‹­~¬iî9'ìcsRˆråEàØ§Ý ßI)Jaîi…ë3£c0I{ÁwM„ÑzoáhC·´lG-ÍR°‘§XŸ°j׸ô³Kg~ØZnêÈé*5’òeSîʾÐrM—¸—/2ÿ?¸óG‡ùrÀ€ÿP0èÞbŒ¾¯Ûö8Ó#µó5ÿUY“^ô¿‰ûI¡GÀX}Ü¥?‘­™/õG齋u«éÞHt0Pp\ÝÆnrrÀþMÒWô³Q¤'¼nSµ8[;ó~‚Æ’‚ŒV³T–=REÒª†^þ3Ñã6Æûûjf õ”ÐÐ6Œ7yòñø3xÄ\Á)³OW?ž)¯.(-×®²ÖâÇ8¼ƒì„§ßÍïþVGÂt c¡ÖbJ2ŸâZ 62^ø&3ùìcÅ?þ¢b¢˜¬hc^Š/6D´è hã½Ù9¸È<£1º@y…|S@ÓxW,uámÀe0‚rE;ïáûþK¶,_ø ÿäp÷u¤ ­ðWÙx˜ÑWÓÔì匟mp-7&d#L6sœ Ì2oÑN^ãØãâÑL‰i4²€ÃÔ¿ƒEÚX³\ëɪƆó†¥Àˆñ¤kÔ#Åt»4æcöÍ4`—°[øUBO°|çõ Òm‹ó§·:l`ÑöXräÐíÚY-C³(Ni{k þªÒ‚•îviµ·¸]î¨,³®|Ç?Fýׇ)sÀ€ÿ 0èÞbl}ô|Ú/²ÒÓ.¯?ó^~&ߤ_Nް« …@“ž“­ñxýRºïìq†¦ËjyÔÝ^˜ %/ü&÷nÂxõ„IJÐdu’2÷5'fõ ÄsÖ{BD›ì8lϵ·IÔœT6Póõ_Å3˜™£™ZÚïÈ,*¨¡õH 08hÒÉÄ\ lT<ò0“=NÇs‚a¼†;/ÓÎÙšð®'øô籊ÎÐ1ÀÐZ†Òt}‚6cb©v8üðþÞs‡æ…ûÚÉâ{]º GÇa‘¸Ú‚%lHë¼lÜßÇm³½Ëø1ò­1rêóeÝTE@—¡Lq3n¼ŸGgùCØw2[ðzÍË-³«è1-l¾fÄgîó…9¯7lŒ°BnQe#c3gáX´¼zH=¢Íp lƒoaiR£'ݺH“O³ÑÔéh{áAgVЛ{ì•)¶Qð‚Ö¤nÃÔkhJ¸Ægv“óI"¯´(FÉ Mî'WsJxѤqg-Ü_í©eE‰äé<7ò$°:_ŽÎMŒô¤ûËLòîSxç'yVX|r˜5 øÊÇ@  x‹1yt…qFôëÍÑ&¢\³®;$Æš‡†öôÍÚŸØèÉE\é²Z6~“Ú¬ÖƒÃ_¨RˆI;ã G÷øÂ¿j-mQWn’äÔ÷ü¶ÌÚu³Iã±,Þ/¹$ɦM=‡Þ{<«åêïÿ#4#êc¬Ã-ð.ÙP(µ'(™à{^Ò ;ì\„‚£š:îþ m‹ŒÙ¸ÌÉMZÏöeU^™“°ä[ìL¨k1–2Ç·¸k\)ù·Op÷¿ãÂ'>- ÅëiD;¾Øž®âZB9a"mð6›î’?Äβ¡jPa#Q$½<ÀŠ€Qêµì<Íî7t=r·îqà¸øÀQàBM©d…P×”N¸{ÈICP6+šÀ"çVM.CÓðê ’‘ortŸàÒ÷PÃâÔíätÐFÑ€Jg˜}6Ñk¹9CoU²]Q´ÖÓ$º/ëŠåHu‰jƒyàÙš‡ ÏɄЮ%”¾ƒ¢GvúËÇ“|o`×É zyn¦,S{­ëqrI{?qÁ Ý[%‹hùî–’ý¥ jïøÏêP‡0à+0à­Äèã]¸ô£Mngj®¬rÜ%ïûÐÕ=ï%–[Ã~Õ[ãLV‹®Ê£×Ö²ÆÚ—gµä7²ªºnRo_Éh3âõOspwµ1¬ý 0íõüÙ^©»OÜm/˜ÄóžQtèùX7=–µÔc²À‡?ÂxŸ“#šêÈC'Mn=Á¡v–"Ô…'/ØÝ§Ø¡ Ìg, öͶ„[²ûí}fSö/áŽ9\`cI;§TréTÁ‚` yÁ¥13ÇÍw1ÿŽ÷þá§âœÜk4êÝG¼ma$®Ô@=1~DyâP¡ŒóNž¥˜Œ€Y’ËYwèEÃÎ×°±‡ƒcîžpë„OÕÜ‹–ËÊ(`32‡sŒ2®^sÌ<ÊßrÜp˰—q2å`N•,eE=Chº€ÆnøÕHŒVzm7UÉÂZž¼ž·Ù’ÄÍbÖù2áOÓ>òU´ÀMpk¨·öKrI é¥[£B*þF!} ^™:v SCë! 5:G] ÷o@9ól4yž(6­û)Š@¦‰©/Ó÷Å‹ÏÕOòÂ@  øJÇ@  x+±ýŸœFâ-q®y…OÇ}±Î#\Ú¯§.{ýš‘ó™ƒL˜/Žº¦NOÖ¸òÙ!·L.PnpRp©avL›AÀf–,æN[‚#(Öb…‘ac——¾‰+³÷—ÿø_…•3ïÇ{³M€0Æ„v *F¸žò7 ê’htÙ˜£mç j-³†‡ßÁå÷q¹DápÆë'üö”ÇÄsÜpxÂLxÔRXŒÇ‚1\hyÍAÆÌÓ8|ÆlW¨8œâÓlo2»…«ÓõoNÃ3íã?%–f£µ_t§–µèAzŸrA^™Na¥ÓEœî0„S1ñ¼æÖ160ÂÝ–›3,]®J&læ”`ô4'>òóR:F^‚/(=&48puVApK)Hoe+þVO]^–žãËeo¦XÅ/ß×2ŸÅõ6U€]8ú{Üÿžaú0à+0à-ƒý[O¬>Ö„ÎfU kB‹sD¢\¸Ÿš¶ôßhàÞÏ¡[l}ïi·çÕùÞÀ«NÖŠmgÒ ×ƒÙâF|ä²j0RñÜ¿æþ^æbߺOÄ¥·×ï×ìL:Óû[v­ ¾¼˜.-0BOij¹q Ïp\# ®Ecƒà4)\¢ñ³„V©”Âððeöö=ê>)Ó’§zj÷Úä¨b-›ß`vŸ½IÞ°0dŠ¥3VC!3Œ-b`‡ƒ'Ùû·ÉÍ…y¢[Ìöu¶S˜ø #dø1ª§ÓTŠ†à ¾»hâºýEËî&øf¶Øq<åî¯p8¥­9¬©çpL­¼`KeÙ(˦ryÁë9d8Ç«#dž¦ezLeÈ=–·ïipF“f“l•I¦ÈË%«iºjXW>9{cŠ DË9é90öGé”Û5À… Dp0õŒ”ÜÐ*ÓÀB(´Óþ,}Æ-L —-f†ãŒVhªÓ–ÁÅɯânÑ|àÊ?[7“nlñÁyêê3K/;¢_Ø6‰ÁŒaÌÆ„‗?Ÿ¤)Ùêóó^ápIdÜä(¦—ÝmÖ.`2A,¢Xƒ)PÅ;ü_“åŒáµÀæ,™é¼–ÙÙŠÁË‚€W|ÉÞ>[ð8¥õ¨CÆCFH2½[B…7H‹­ëlï3ý'¸9êBa,El¡3EÆPE¶:æèÓìŽ^ß¿F~ŸìÎͦog.§°­ÐFPAƒÞ‡½Ô¢çÑ):B2Äw’Ün\9‚‡Š}+®Ð9wçÜ9æ¥Û¼t ‹´ä8aQñœá*˜–Q…ÉÙÏxý&`ùBƒ.¸VñÚ7g;gLg«<ÙãðÍ1Ô½Áì1šºèV‡½‘NÇܳìKCNVÖQËEÓ"¢¶»ŸCDò@,»q»agÒåiÏc(,"˜§ŒcªŽÆv‘Ð*mÃDhÇdpÊtûÉ)ôägØùºß3gyÌ©¢šU"n“ùÆŒ`“bÌÝßfqòÞ{ =/»JÊʽnwz}ö̪¦9€A*¤ÂL0yg>-¢¶5AJØ€…ZxiÊEUÜÖ÷©@¨x¥]:Žª ]`«@Ú–z†wHô–ó´¾ÓÑÆ×ŠÁÔxƒxtãÊ_þ¶©½½õ®}wPO^ ‹ÖBkC•Se˜’r›ÙuÉý=ôüøÿgïÝcmËÒë®ßœs=ö>û¼î¹ç>«ªŸînÛmw'`ãG&¼•`‚d[ؼ"b@æX€„‚-¢à$†ø,B興‡q"PR‚ Ø’tP»ÝéGuu×­[÷}Þû±Öšóûøc­¹Î\{ŸSÝ&rçV×*•ÎÙgïµ×š{îuÇüæøÆ¸ë”B™ø(tÉ0m¿ƒÉ A»ªGìæèë‘ÖQΣžã%ÿÈpÿÈ¡,x~Î×Þä7~ƒß:êÔárºK:¡Î9U& ÆSN™M¹)¼8‡m|µÆÀ®gÕ0± ZÈÛ3ŽŸb4i ßEºI@´Â ¦÷]í—LN°‚µl8H€B”‹´±Û«n&<[à…ǜ앻8g ª]`«·à …Ò¬ºåJ eC9§Y¡t;N¼–O”«p9g׌%Û'wWí†÷ÙøNíçœÿyŽ|¼‰Žñrb$Ð#FüN¢úS œ`w( Ôa2DñM,ò…èåç¤mê2Ñ[¥Í=¡æö Ë9À!sÛvíñÚ îïP4h«|¸ h«]¸¨QK(°%&Ã4„™RM·­%s/Ž »|öŸ=<ü]ž»çÌ–°ZÑ4(,.dͲnNÜÁãó‡ï§ÞÃ;ÝA<œD]x†ѶϠè=˜EÛÿZk‹v ¨PCPž{~ð~äïc6CcX-øõ·y´¤‹øi™èyâ·Fä<ÉÈuÅŽeg—÷•¼q?Dád…õ°/L„Ì’gìÞeñe˜cZ¢sLCF·ÔYëˆÕhÝw9W5Yø)“œiŽ3Þ&ëë¸íFÝL&ðICå;éÈy`–ÌrœÅ+&Ï-<_^rT³c9„I ¯1y2C ¢Ì s‹¬U•×b>ãŠO†ë¾õÚúfÀçfåöÿ,'?5ÞDGŒx 1è#~çñøãlézŠuÏ_eèw¥ ðp2çù¿Gõ‹¿½wÔϳŒ¹€k¶ý»srC¯ÒI§.kO&ê7&° ÛÁ)¼™èžSà …ª±?ì’*õ´Þ& ÞtF f›bk‘@PÔ"½‚D—hBÒ 6¬íê²MC½âø-€;»l™®Úª‡A£ùö”{l4Ѽ,š¦iòyµ4Z‰®¨íÊ®Á’ÌÖö~vKøÂ#²·Íç¦|Þa§ÔabÂDÎvÐEc«ð|!æqò)ÕžÒæ¥ 5,º mCžCwÑ)M…äCkaÀ"†` †Óoøýá‡>ÉÎ+ßþ›9®)„ºIE!’é^”¼Â;¾&Ü(1sö 7JÞ4„ Z=tÃ\(= ߵǥÊmÊË:¶KÔbV[E:},—ÑŒƒRí°¡¶Ö˜(Ó®°•„îKÌ"‘(J·Mèšé" ƒR&¶S$+4­ ‹ÃyÃy[ÝŸPÖ8¡„sÁo~ƒÖ`®Vé_n5i—È3€M|cÒ~·r$Ð#F¼œ ôˆß<üC¼ö§ÅÔmÍ5ílÿ±]ÀÅ9ý3¬~áÿç;~ýgøà§;ùuÍ5bS’MsáÃ'ÈUïµ»‘=·M„_„9”‘"§©(Ñꮓµ”‰‰ài°³brò}òÚ4fEñQ¾œm؆E»1ˆe鬰¨”–vÓÁD1Ž»ng䞦él}U ñX‰#]áÒ•†i0ˆÍ|žàȶ c›Ø žâ ŠŒÌc+±›z7A2*0Æ]èä-¯ ™±°‡¶&ÙŠµ„Ù)@VÑ4„]L9Y ‚#Ž…ßsïÿ$÷îR‚PÔ Âƒ3¾tŠ=Oʤ­ÂÄŸ%vyz, akAwsj|r§ø ±xƒⱆýûÔA Á€ÉÉY°Í&Ù?Iåþ×”Ÿ±H? ñÔt˜Ý©Þ¦;”\cŒÓªÃ·~“ñ:bÄK‡‘@ñ­ÂâgXüÌÕšÿë¿#ï¸ú4_ü³Üù_¹ù»˜lDrM¤ ±¬k7þT$=[6†ÞM!ïœ7:ÑÅ£˜N‘†uOræ’-û–¦EÖÛË$¤#…YAfY Ò ê†â“<ñìéŽÄ"·¿<Ÿ§Ô`(2Jƒ+È<¾a'§h°-5ìc»9ÔеG$ê:ã<ŒCw G§HmEØ,UlF1ag‹yIÖ³îȬÒélf›ÔÞšøe¹l–)Ž…š»Œë8tëÂÑÆD·k y€sÔŽ á†ð©m¶¿2Í:©ƒD¥ Ôi´{ºí q±‘'Ñè(§”;X¥pÜUÞn u÷¾íõ8Ka(•È„Ý[ÌŸQ¿èD5V×í½M\Y!#IaÈSì à›ÙD-ÉÂ©ŠŸ~»˜œžpÚªí ~«áã%¯lu*¡VT}!(øÞ½9ïz1/JÄ©µ–ŽIrE抴BvýÄoW¡m? —x¹:uö„ÙGÇ{çˆ/!Ü8#F|[cÁü—9þ›ð÷“o¯ÇøõXK$É“Åu¯Îl¢ô"‹õéâ2؃m¨á+0í’ÿ.èˆÝié›ö!˸ï£ÁÇ)nS YŽ(¾u¢Ä–d-B%-“tgVñWXÍ8˜25d–2CÁ¦–™ÇÉÐÙOiÉ«Z*aéѶ”¨ûiå¼&…æqQ£%f«0†€9$P/9-qã#G•hÖ\##ÁÌõ䬙³X5{˜L]ƒ¿¡™%‡ ÉPoñ9’r‚cá™-øXÃ'+>²ûÛJßÃxT‚ §+^?ã­]]³õF I[ذW34ž9,aÛ1-x±@-x<4§ˆ‡v‚Ô4ë(óG„VÓ¢+È¥^ÉFŒ²2³aÿÜ[N¸1e;#(+A!/–IíÖ'’èÔ2‹Ú‰-Ž3eßá,µàG Ÿ_q^ób‘Xå,aÏX ­c䪎‚‰‘¹‘djP°){¶WJ2‡S!øÙÂ_ïe#F¼T+Ð#F¼à‡¿ÆÓŸd÷_dëS”“Ë$¿&R‡–ˆ„ ÿ„úkÔ¯S¿Iõ¿±ós¼ö{)"åmo­âyûqÓü+Qª‘%>ti¶œM¸Zin+üh‰Ë!|û‡L974æ¦.-ä:ªÒ'X˜ØUf¢ÃIÔ—?õÜ,Pð‚¸†© CḏÄ.Q‚\Fƒv"ì^­«@@W˜FÑ€® Âvé­¨„Öqt‡o+Â"ˆà›FOž‡å2«•YTSÌü/v.Y†¢LÔÚP¡±¬´+É7–ðAáNàPÙšb¶S ˜N;á¤æ+°ˆ£'Ãr¯¦åP£ø%g†SËá„»†·/:¶ZŒ°eJ­,-6Ç(µRl3»Íñ› ‘MFŸ5ÑGQ»%[•Öž®…B'X·† ê6;‹t¹I\8úFU—|:mVÊ–|bÆÄPÁ±pC^£Ko͆Ú'I\œÙ ÓºQ?îõ²Ü¯lÔ°uxþÛÑ~}¶>Eõ§ÇÛØˆ/F=bÄ{Ígxñ^ô¿ßî'ºH’mô7ßéåwÿ 6vµäµ×žÄé/àyba‡~fèPÐrš*(bCØ!æ“L)ã©ÄtüOe(uIÝ®?lV {Ô»5Ï9Ž=7',Z±¯wå;§a (M ´rA*6Z£ü ¿FcÐ6ÀeŽÎPÅz¸Àçø2Æ&ÂôŒ »8ßE>·LZë ÇϤZc‚QGþ\³=š[Š¢¢Ì"SBí‹@Öp^µ”í{€ó)¹A¯ 85ᆠ¬ÏúzmÿU‰"¢wäȇQí TœYöfìmñì˜Fº#T †|àLËD ` ¶ÞÇù1õITý¶^{)¿7`’>B’-…¸úrC¿ÛËÑþ#–ä%QoMÿqk/sÒ Nº…_Á™ò¤â•’…v¢öFqïc(ã*¾¼JA"±6ÃNÜC‰‰HmRŒ-u#de@§00ý»¿ó÷#F|+1è#Þ³x:øíãfÿebÊ•G t[ùk½ <„:q}öI­:õôÒ$Ü›HÂwRÞ ¨Ðš‹%µOÜ-Ò.7’ƒ›øò¾|îöœž‰ƒsŒç(P Ø kO ¦s{h´“;·~¨Cܽ.#W¬tü¾=%½ÀN1¬´Õy;!ËØE38‡‘NˆÁr~"ÁC¥Qc12y‹jÏ€š­Ž€i6^+^0–CØQ¶ò,ºR4˜Û”¦BmV¢±ç2µOò{½AK%©‘JRηñACG§Ü¸ÁÝ æ—‡ª…%lX¬¥tÔB9áàC<ú/KÝíµXíjùÒÌõCº)1‚ç¬æöc0†Üàú 5JœŠærÉÔÍ4‰f#»Pðå%«ç¸PhmNBdÏ>j9®dÏ ÷@ÞiO!ÉÊ3þu`ľéqÙGÌØéx·1âeÃH GŒñMàÎìh®‰5Ë<îòG»žÃ0°Šm…i‡Zp0lkwÛ_W˜LjŒç¢aYƒFés”-Ù¤ïM’ƒ÷,04¥ç.Ôd3ꊩà\çì&›ÑxªÐ™(›ÖpZh$úå‚Gëb&³t6Òf2ŒÅY˜Á­Îi®„œÆcfÜÊ9*1­–AbÖ"Û7¥ÜÑfeV+µ'µš=£X»É lnÔ¢A5Š•Ã[¶-‡0ÕØÕ,Æ¢vÈÖúhÕÂÂsZ%ƒ^]S'ƒÜŽpMT˜K§íJydÉ2ÊÛ îr;b¡l)“€…¹Ã®­ìÎØ¾Åùé¥òÁê@æ«}¬·:'›Kªú¼æ}ÒuÚÒ$ñ™:ÌÑì¥ý¶Ã&± ^Ãy×ðú ‰£ç¨ Ì#ón`‹¡ý¹O8®\ãç˜à•HËÌ&‘‚¬™¬§h…âãhĈ— #1bÄ7Âö/·IÑ+ÇYâñÜþà ¨b"÷2 I!Ÿˆ.ÒœEð ¹²Õ`ç¥\ï¾G’½²†¬ ·6I%²gž‹%~‹¹§ÈXÙn_”F¨¤³ÆPE,M›ŠbABã žÜa[O_ÅÚ.ß[[ †Ðª]Ï0)8cNYž1Ëy°Ë‹ ykÝÖ|¥v!7óSÿLB]ç_A ¹ »–&ˆ\€•Ë]e\ô×k©¼ñhdx®Ä6ݺ¥5¼ëb_”ZxRA PAKX #weÜ:ߎ ¬­eIIOZ×Þ›p€sæžU[ôìÙlølNUv˜ÂØçBç øcª)oÀEÉý=œél™½"­3ƒÐÔhÀ€ T¹µ°hɨ÷ÈsLŽ!zBW˜²Sôjƒ¼À %ëÉ~޽ b,¾í9¬§ R׫ðü¹q6OŒËðû^5˜¹¸Ì‹ ~§Q„»Ž©%@ã±MwE 6`W¨!LÐ-‚ï(¾Äõ@‹Z¸hË«/`[—üžSSo$IûóPQç¯p "ÍLeÑÀ®'8æ5YÍ–Çæl}ùoQ{*ƒ‹Þߢ±jl:)ùìÙ@À4ÔÒÕÔ}+/¶Ã,Léo–¼6$¬w ĉQGmtoùœÇ ¹JúSíFa>õÊØ||-¨|-–ð›‡nÔ8`ÎÇ[ш/F=bĈwÄô?äXäöƒ;QÅáá+‘©øÈ­[Eé$éîjï7>©W° w;½iN|o5]%F¹e»}¾C?;I5F`¸Äh"ò!÷„0ÃÜ¢)¨¥ëÌ¡jÁû.õƒ€TP‰Õ1h wä[8»w·`…1˜²Ó„A_ 7Ði{.Õ…£TBÝé3TEL]gG/šÓy³’€ä4»„ÜÛ“¶‚6^œ¯v\Åû…; N1Hk÷&C N1‚–䯀¥ø€…Êc´Kø^%\%ƒæ“al’…GmL4ÊÍC·½°È)ÖE†Ù¶Zz¬£†¹#«‘ çeb°»ä·‘‡d–-ƒÓn"8'•&Êœ¡a¹¬¨9¥Õ7÷NÏÂÓHšM¬hÑÛÁ$j ›8 ÂÍÈlÞA¬ŽL.M87C#†i/}t\ª“î·ø§× ž½É”·x ΢Šz–B¥ˆ)™Ò(r!W2“ä3[¬Ã‚§°h×¢gjT"Ú®5Èù+d”[L3öçÍJu`­­½—Ó£z±Ô¥ F ª¦ f¤ðöÌ«×ÕžX>1eû´»FÕ¶°ÝžÛ¨sUß&/AðÚ¹pH`U#Â\Xôƒéã0öýj½`fÍÆd™h9\ÂV·ñeé‚ÍC ²Ì§¡V¬ ugãí¼B~J¹ˆ¬²í)4‡ØaÙU‡É€çtÅí-2‹SÄI—ë~ùY»„¡¦„ ×°ˆm¯ËøävW±ÄÞ\u›¢g½æg®*E¯M`;¬aëp2¯Í7ã}hĈ— #1bÄ;b;!­~£Œ…ç–vÃë— ãŽ´ÁåeÁ²“«öBŽ7a/´²¾&ÙÄŠ`/ouñikõ¹ÞPÏÄ#ØÄ÷£ÏŸFý«§14g,ž0›¢r…@SóË™fŒ2å†Ã€U&†IF-Ÿp2‹ÙÁæØµ˜ &Ç:ê „ó9 ^m`ËûÉü‹Ç27å — ¥?¾¸³|áB^I¨‹­3=W‘•ȳÀvXÝÒêÀïšó²¨ÙýëÔí…zÔa SLÀØî*M€)eÛÙ:oDP†yàÙ‚EC uí®‰CÄк_Ÿ´Šá2qíˆ6‚’w…é& ÖWfŠkPA<ˆ‡€Ë(ÞGý•Žøz,ÚêÈû3¹RF,œ¬˜{n”d”½’¦@/,WH_DOÄkIsË‹$¥ï›XDEJ Î¦áÆ¦_õæŸH&'Éó7cŒ>¸Ví^}n¼ñ²a$Ð#FŒ¸û¿Úmß§Fν¯sëžñµóæ¸tê R¸(e6Q!ÝÒ‘;°—fÿ$ÑBb9Ðn”îBR«3IÓ—dë2 ®åñ}c¾·¾àíŒûSvfd`,2îrÚž¯éºìv• xƒW*Ç,ÇXȃqd»‹¹G˜v–Õ­áq0ˆ%”¬U®‹ðv#‹:«—ï{ûäùgW2•pãÅéÞéÑ}³ØµZiU¦©ïi´^6n¡õ­å­­ê.Ly+ãpŠIŽ&$×ÔO3&Æ9ÔÓÔ,´Ó7JmQ…šs¡*‘°Xŕ왮-V Ù(¦ÄèeÜ8&£šñÌsÒÔ^ë£Å«ójuò[§óåóúÂÙ¯™ì^fæ;o&䨘°µW•ßWoÝТ[ZØ’åâsHÞE^g`ëDÛ+–žz…yA.˜šÌbL§vXŒ!(Æ–tÕìÔ*n­#3…‰Í,‘³›Nus)‰±UE¶dæÁ ®cöBG²«[LÎ:[ C§I­µjà”¶zÎj¡´ÑdEYš¾C4$Û v¸;AâÓ’%Ù1½S¸ ,éPÈ5ä˜!u¾ò´×„$ÏgøF\óWóÿi¼ñ²a$Ð#FŒ¸³Ow-€&ù¿‹É)œÀÛIî`_”¤o¯‰ei"¡im’‰²:–¨ëÄÿÁ'EeI2܆ǚÓB¯{–ÈÝ5’E“(M[Â÷„Å9Ï•;ÀYœb•ÔDÓtšãª5)ÁñUOn¸cØ7ä†ÌÐ쳃”„žE Tœ´¬/|mlÉÌÌí|ìØÝ{‘=uG¯7óc(|uÇQy fwU|Ü›Y›Z×» kÁþ÷²óˆS`…yÀGpLÍÔã‘ÝîA›„e`5Mƒµœ-iZYsï>A2€$ÃÈUl }—Ûg…è?°F ¢YÄŒÆ4#ÚXÁŒå]¦:ÙF›óRmÖe7 ½‚4ÑuÃNÃPJáâað%åÐýÔ Ñ”£ÍU |sºAš¯ì)¼òß'Yþ‰ñn4bĈ‘@1âìÿ¡Ž.÷q­½Æ¦PÁ›C.%ÑâÀÆú±KêÇ­YÇÜ¢½Cû×&*7tHqúîôw­}‹4˜°IÊ–²b×½{‹µ¡%Ï“n}˜ÒvµËÚдþzÕÐ’¢•Ïh o)o))Øs¸©2;]L ­~ÚP &PÈŠHQ“5'MñU?™íìÝœÝ;Ï?ztú “9=EA³®!¯ìÁ„Ç _\ð½Ç~=Ú‚° *¸ÿ~Ü„à/©o­`h”‹†Ó%_8†Î’IS6ľk^­}²$k$E=6Ç€D° f‰z$0­Fl (êÈ,‹mا:·•iC°CÙÃæG© ,j*Ï´Àvr…Zeeñýí·#RÑ6Ôµ:w/¸_+9_iÞ¬¿ÍŸ¹þqsÕÌL¿S <ý…ñV4bÄKˆ‘@1âlO áš6ƹµZØGIjwJ>H\ 4aÕö`7QS¤•é&á:ubÓ¡‰…‚&š„³¬)wCâYÖïæ§q*)©r <ù"¦¡Ê —árû ×Ò›%óÅ‹­[‹‰6Ó$`Éöp€*P)…€ÎUlƒõØãÚ~µ±Øl¶]6½±{÷ÙÜ!Pšè×WôÏàœ©òö6ß}“Ãæá¯¢€(F îâ ó‚å«OmM-‚âmM¬{^8‡“dÖÒõ6«§é’c•ä´ËåÂIåR>!Klu9`m–¤³`°ŠxÔ’.iVѰe³áÏ ¥Àý ÍpÚ°[b”iÆ¡pâðY´çkgHgQH¬7 ›Íо£ÿY‡ÉóæMË•˜oâ›e¾QÞJÿ¦GŸcõKã­hĈ—#1bÄUØúcQnÙ°Šˆ–aÁ󸇞…Ä#ÝÚ>Œ¶h&á4!FWH°ö;éõ†oîZzsHÒÓÍzIê—’ìÑ»Dóª——æ+Þþ[¼úÔ­#¡7Ñ$„D:Ò§—CpJ!­„A…ú RVhäâÂOÍ*È›UÙÂzlµ4Ù=·ºåx¦Ñä¤IH?°èäû¬òdÅG?ÎÓ¿Áò­ŽúKƒ*bЊÙ?ÌüÇ2B#x³LB<Ö í€û¤0®ê \3TîÅ9«ÒîãøÛκ;ù°2Ô £h*ÞæŸ ”¿{ßiºáEÃ}×y¤”Ž©rP34›Ë¡ˆK 6CÞ':{—,ÞH&pÿ³ÖƒÍUç“>xe[ášz„kH³¹êJÛÎáÉO·¢#^NŒ ¡#FŒ¸ ÷ÿfÑpÃD÷º" ¿•dš!Ñ$–9}Â]ØßØoÕu¬a§?{³ ˆô·ÄkÙRM{µÉu彆»×s{˜Ã=§< Þå"H¬ôšøŽ.¹®¦;æ¹âÁ符¬$ºŸê󌅡QÊü Æ5+?]†ð –•R¢9îdI¸-¥ß¶ŒãÙ§Ìd‘¤æÐpwFa©+>¸G^ðäÿ¦ ˜EÇ ÍŠÙ nÿ< ä *T «%¡aU¡ +Ï…§9‡ cNËÿlDUˆɚDMRÝw ˜¦-‡œeÇt£çÚ–Gíâ[_Ž\1µbs=ORÌÆ0±LK\†ä ÊyMè[ûžÂTËÑË£-lEs›bM²ã¡‰'£IV '9Wùu¼ƒÇÚ¥éUOHËØsxý‡‘_oE#F¼œ+Ð#FŒØÀΧ¹‘ÔkMdmáñ0ü¹çi FîDÂá…„ÈDûwõvnÓ ¦£’4y6NàJI>ÇK˜G~ 'o2;L´Ô={nK˜ub,Ý’é)’qÜpì±5»à3ÔRœ`æ˜m¼aîÿë¾oQÏÔùœ€Edd®Ñê4èÍ‚ÜÒôþk!Ž].úÔPXð ϼò<úžýÊIw±þ„÷ÿö?otɈ"¸h®ùÄ—ÑaРŠË0©ðsŒ#+°Š¬#s¨’VûøÅFDÎÚÊP¬ì9^2Í(3Ôa ;†™¥&9g‰NϽ%‹FÝ/À$™Ø’hŠê$2¦Ùk8t:€æ›ûr¥_“5}HÿÃÞúw‘ßoE#F¼´ ôˆ#6pð“ä‰tØBx–ˆ"ZȽdënĪmÏÈ%±¨ëI³$„z™T×ÿjj”HŒØØ%—xðµù”X7QÀÐÂ]Ê<ê#&§Ø¤I.ÊF«þÌ{Ï^k¡HàN”mà ÇÅ,YžM¹?˜øO=\ ±¸-ÌÉ  LÑð\ì]ë_ͳ7@’vâPLÙ™`éFàá‚÷Ýâ£ÿ Mà«ÿ;Ö’[f¯pûã\4œ n`Öì!t>qâ±km‘kb_†â`¹&.d.JበØUâÞ=e÷wïc£˜äâmNŸ \Úx‹ B™ã·ád£Ôm†+™”^7œ5ÜJA´;¦Ë†Bü]º ¼‚³„àÚ¤jn74Ö&¶É61wÓÄ]‘T:o†uúwNQ1ï¸$H_ebí¼†·~žùïC#F¼Ì ôˆ#6°sëRQš%ÞÏ<‚*îKB+Í5YÇ[ðýž5qÞèùG9tˆÆ ÌÚ^´.+D#¿ia‡Ìo3£çè}TuϳI8}ëVñœì€Z’Ò¸N|>1©hk™s ê"º.„#ض<]¡5û“S).Ä€%Ì0’¡9ÍMœËü–Ñì ä~ ³8Ç]º+»ã;xT³¬¸õA~øŸãC?Àçÿ_ÿoø¾Ÿgÿ5.žQzlÔå!Öu•ÎÙ¯j­WÀ$VÊý#í“ë¡¥]Ó…`k+ÒØƒ}ì..‹3(ç·عÁé3ÎNÀ¤ëù›ÛhÍ¿7[ñ67<¾¡Ì2]@Lw}ù>ìFýc¸âÔ ÃP•^âŸ>âbe¨ŸÖawª—¡†á™C9ÇZfxÿç?7Þ„FŒxÉ1è#F ±ûó]q1DyhE¥ 8ŠÛô©?Ý•ž_Ü´À&{ý!©çõœÆ'’ Vø¢µÑÄÿ!§Ú«ìú@DŸÄ¬pU®rÎV/ÐUrušD½ä‰)G“Ûÿ& ")ŸÃþ}b_»Ô´ÐûŠÜ¢ Ø¬8jÛÞÄèij[PEOWœos³d2áC÷?ÂëßÏû>‰5œ/ãGa0ÐÄêr+¨¥Qò~]Ô7D¾CnÈf…µç‹«ä㫺á2˜Cl¨•¬ LÎî-fd_çÅ£n£J®äŽz–x3§ïŰö¬qZ– ûEGt'©IP¥"òÖ9d’˜†¡/GjÞê:Ò°ž|H¯×gÌUntl ÝÚƒz•ºG¿Æ³oB#F¼ü ôˆ#†8ø.sm‰¶ÿ=b†^¡Ã"eZ>´p¦QíÐwïù¡…soÁÚi,&9¦U‚\•è–ªAlb|e³ñô̺ÞCVðnÅzsÉwW©ÛCKž,akØ:wùeugý}CG^ó€ƒ&°¯xŽ­¶O˜ IDATŽ"C­.MHò‚ºfQá…Ó%UàÍîl3ÍÐÀd›ïþ\Fã9^Y€Ð ‚÷ø”°DVÔ–Ìú ‘ô‡ i¸ Bf¸˜I¶ŠN)`JÌMÌ ³B !à,.š:‹b ‡¯Q–½Í¢ÂX¬’-‘ _DÓt‘º"’ü5°ôx˜XlÄl_.oÖÄ*&1.ìõÐí²8Ú$ ¬Yb§è"oN׊×é[¾IlV£=<ú #{1âÝ‚‘@1bˆ{—†¸.*AsXÂI$k-Pk…´–ÜŠìY†¥Í”=K¤Ë~h~ܳO@0Í:C4cÑ´tj1ÆZÈ\¿ÏUÂhâi\3†\U{ðè/òìŸo?#F¼[0è#F$Øý˜&‰Y,¿Y8jàžs0´ >.p;I Då¬ {&ê}û2vX÷‹”îL ¨Mr›S—âú*C½$ïãÒ·35¶ëÙUíÃæñLÖÊ®$¯j}ý²„ý·nÖØJÊíiq7i:l©†U`©¬„=ËýŒÓ‹h•}•¸M,o+î”TÊ2°gX­˜Ÿñäm>ä¼âëgäJf£„(8 B#”BÕÊrZ;ƒxÕé'r?‹ášAck];Ô½™´‹ù‹ɱóΫN” ƒšŽa«a¶Ï+æõÏã+T í¢h+’òµÏˆágjÁ£­ÇˆäÊ»uCOC—t’Ì¢µyH>â,ÉYÌ`+îä #—¤ô¾JÂÀ¹ÞŽÃ\cÖÑÏÌ_åÙïo?#F¼‹0è#F$¸õïtbß´t—ÁŽ/‚ž¼^)“݆ÝX ¶CJ¢C%(ë†qÔH]!Ò=fIJÔƒŽÀ+-ízÈðÌÍPqAÒÀ÷ö"e†Æ —YbÜÄ×¶%äíhô¦Qøœ–´5 šñ¨!³HÍMGé¨N’¨íöÈç±°mØ.Y ËŠ‡Ç<|³s°”3‚%«±’ôêåAÔ ×u>”¯ñ‡<–Zû×®`‘œ³·¾$ÆÃ-Ì6Ð}¬2Áh w¨µØøŽÐ@1áæM½ŽF¥D:‘ØÈâá²Á—EýVÆAdù$Ÿ5Wí6¸ÄödíÚ5±Ç^ O¿u·ÔÕq³«•Hq¦<øðxï1âÝ…‘@1"Ávy©Í°I½ù8)î†õtåK´æ»³XÂÌ’ÇÓBlÏ&Ã'õ­ZI’³ädmÁo [o¼cê§¡ï¨FÕ$èûJ9G/~õ‰8; ±“XÁÍâ2 KÈV%Î'0ƒ)˜Sv›&5JœcÈiØ*ùhÎçNâÂCbaÛÑÚrßͨG/øÒW¨æL6#+(¦X°sLƒ±ƒ("—¾rjQÓ¡<#µCÖdI“Ƥû8,>Rçµ°‹5¨Fº‚)R Uƒ£³|V6BÅ` ! ÊÁ}.Î8:ï¤&"9$ÞÒ›!#ñMüÒØÅ¬긖¨1I=´©8ƒnŵD[mwL”]ƒWlkkpQ0dÏæT½‡VýMÒ¸i×?Ù¼d‚µCðJi)·Ø¹¦[åu+t[#Ýy(â'¸¹ô2±­0½æ¹°ŠG`è^ÂF;)éØÿPÛ¿Àâ÷ž#Þu+Ð#FŒˆ8üÔe`uO\–pvUåŒá#--ÞJ†ÞªÂ ³‘û.1Ÿf¿QnLµÂÒ sgᚸãϰ€m7N©‡^e'Ìõ*ÛTI@]„I¼eÊÐÁ#Öb/ÐS®U”D§™>6ºµ± Ž›5nÁΔ÷íñúñ°À_Ãf yÊóXÛI¢3Ç$Ç8Ôb“)xV BRmåã‘vÀ¬’`âyöì¿W<‡È§{#¿žXK°¥S›2Åø®0¯ ­ZX6LS×Å"¶~|Æ’9|ÀM1÷áÇ~ßn- '­([æ5A±€ÁZÊ[ûÜ8D„  œT¼½jñIŽ“.fÃ9i†K¬8×_Öò8Ó_íU_“ô<þ9ÿ#ãgĈw#F=bÄî¼Á —:Š7ÒZ²^S{Þ<àJRŽ]û5 ³'Ö„ÔéÁçðð¿â¹åÎOá Ê%YEÓGf¤MŠi ‡{Çhå^Œ±öœžÃNB”]äÁ2<Õ>ä9åO’HŠI¢¹{‘É*¶cÒ•0Ÿ-Ù[˜Œƒ]^?Þäg ŽV4eŽ1l•ä9ê˜N™ÌhrÍÉrL‰©’·ó˜@¨‘ç°‘¤».Mû CwBÖ¤Ã0w½¹|©ÈŽÑiì>ŒCr¬í> Ó r€¨èp1œ*Ü„{m(`>j¯é1XÖÔž"‹í‰€â&ÓÙçÍá´I>eÝpzYûôûIÅÆ¬è[-ëøÂ&é,” { ¹ÊÁzÄŸ+Ïþ©ñÆ3bÄ»#1b»ÜÚÂêùeL‘æoGYpËBz53Ù=×Ô¡6Ô$Õ¾– <;å­“å¯ÀwÑüs op ‘¼f×È66±&ä Ù$j+(‡jàU|²>3$²f’½û°Q—è\ѲÏ2܆³†SÃ^ÊÞ6ûN–Éqæ°Äî±ÚÆBCÈ( ¬%+Èsæ»îoóð5qÓ¶Ù hΔYL¢!©7§%|¦¬‡!·‰T=\Žª(2Ç,Ñ~Éá© ˮ⠀“€Ã+Þc-³¾ÿ./iötA5:©¾„¾o¢{òÀˆ°Ÿx +.l`ÐÂÖ-f÷º‘>W¾®Èj8z›Åöªw⇒ò³nô/j²"2ѯÚs™7”~#ؘç›ßšöåþ­ñ®3bÄ»#1bÜùâeH/9 ‰{ñÚV5CâµÅe(ÜZ!9eÏáªX;’ç÷:×<øoyòcño_ †¨32I:Û²Ä>Â]>h†ûòzÕÓ$‘q›Èb]b%·ÏÍh:¢Ùjª¡ ¢ÍòÈaE±dVr^ñJ9á÷ùëoF?»eWN^V1Q¯à¹aæ¹¹…Zóà‚ÙŒÝ ûN=¡ŠêñíªÂ4C=±Û(Ç¡NÝ'ŠgM²Ö‡"xñ¸c¬û,4`gv l´´³¦KÆÑ€s|×ßËÖ}ž6§ëbµ¬³oís¿{£z%{ðYügã0úäsé—. tUlÎÍ0½‰uxáHy¨Q¥ÍpÊõYž|š ׫øÁ5×gñZmßq¦éUý 9yÌòÓãgĈw/F=bÄØûè¥7E‹‘Õ†ô9Ýãî ®[1cb³,ò¥ÆY¯¡7…8¯ü+,þóÁ_Ïþ5{ú§8!ÜÂØXÍmy-L£¾b­–l6þo‡r”°Ñ Ö£—1´¶4ÎÅÞ»¾Ï²ÐËâ$Y´•æ§CÁ@¿lÈ`+*ÅMqŽSGYà2vKn*/VI®GßÎXBAáðpZájžŸsQs¶`>!+(2V Ò<@;ñ±örmIöR¶'‰ßˆ&h‰¹åý2)ñµ°ç˜ ä°{D=!ãLÑŠ½Øi'`„ƒ»|ÇßÃö-¹¨ü$d,¦¯‡°œÏmv–½ªE~Ó/àËqHSi²¹l4œ×4žý›L_ã8Ç(µðDx®±T®©›¡9tÚµÙʤºÜFK¸/’|ûM$šár±§??ÞuFŒxWc$Ð#F¼ç±õóœ •÷4±EÏ0ì°ïj–8=³áƒ›êž¯3o&a®žÃ—þ1ä/­Ÿ¤þ’râVÒ=¸ZÖnν“øÈšMu ‰oÑQù9á jŠ;Ì&”‰+p,ÒKº‹Ä…cÍ-ÀY ÔHÝ9²øÚž»äLÌ€.jÄ2µÜ©©Npç°ˆ7‹æÍ Ö+çKžxŒp±$S2ÃùkqPÄáûÁ÷1]Ɔq’$ ‘©3wÿ«OŠÁ å ŠxÜ)²×¦öœn4ʼaËv.Þsãý;·k¤P_ìå|XV¯ëÅJ1j4X?…óÄËO†RcÇÅœéúO”£ *å-‰†1æ×÷‰CÅ~hB×"5¬ª`ÅP‚ÏÆ7…ƒôSýè7YýÉñÆ3bÄ»#1â=ƒv î%16v¥S™\Ïž{™G_“f(nîɲAóY÷ä4pûÞøÙ+ØswÈÏx>“ñë¥üÌñ[„<²Þ2‰”ˇÖšD¢ôAz ÌYWÙjwÙÙ³ù,¡þåð‰W×—¹J§ÛF>Á&jc°R^(·*N8=cRPVT’œ[Ү؛Ñ,8«ÀR{.j¶ Î`=T„ux—Øði'†D¢Hb!’®7dCL¢„fXmM¶ÄàÎqg„ Á+Ê´@…ºa’£ÂÞ!ùAò-çKÝÈ-{eñšN¿$çF•à¸ð,éÕÓ¤—±Ý͘rÿ{™Ý@^p&¼­ÔÄÓk†Büdu‚f®±® ÉäÜ|a€¯Ãvã$ ×»ë°ïÖE«ìçÿÑx×1âÝŽ‘@ñžÇΫ×&NÍ5a-ZW ›l÷§Ñ*v£½&MÙS™9|õS}O\Ï ÿt¡ÿÝdJ|ŽÏ“ù¦Í ~AóÙ>å«lOȇ™‚}3Y{á¸.ð¹5½ÞNˆ¯6ÉËXÖáJ£¿Æ*¡ïDÒ™%Ñn8ý¨fó%¯³W“œã`Ê£e|N+>.±%[Âò¬[x45˜ Ú0…LAªÛ?(u½–È;%áskvÅi]ºð Ñ@³qò±/ü):EK˜ ¹áf†T>ø vP^.a± XN _³C{êªÃÞ¼ÏmP^³â¸öÚöÿÏàm¸¿&æªCqÕW¦ÇÑß ú¯Ç»ÎˆïvŒzĈ÷6fÿ~§^°I¹1í‘Ò«¼ÞÊKEÁ •*Õl¦s¯µ ß´M«~ð3ÿÙoæ”…ÿ~…!àæ>ŸÿXV:u¯²ús,~jýÙpëgÙÿGÙFf¬ <LAà ¶"#w /_Á$i>ëѧmYË“2mHH§ ÇA“´Ž¶°ú”* 9A ʤ¤Ôýø—°KÇÙß%³ð+\€ÀÂCζ#("dBå8­ØVrA«XEtè³f‡´¸W·ëPã‘®®‹§1ÈØsB†ŒÁgKÊ’‚ÚsãÌЀLÔd‡ ‚:xm2Eë¯{¯í‰Ý€[ð|hhûe8NÁáóλ#Ámtå•왘~²éÉXoˆ­×&ÿ|r(ÁGéÿš#Þ•Éá=–ðüǻΈß ôˆïm܈ú ˶æz+å¾RÛ DjU×\úÊ«M¨öž?äÑ!¿þÛ=÷À~œêŸáé‡ôÚçù¿Ì£¿Ì#Ëí_âæO°½¿~çÓ$âd ÇÏÙ=ì–iããò]l±uB¯ÓîÉ5ÔP¹€ 8êŠ÷Œ…€v ^øØÓ¶Žú r PNpfßM©„L¡ÁŒaêÏyÅŽ½Êâo­‚ÞC6–@i¬‰¾vã Þ?GfhÙéÑ%° 4ÜpûýÔ¡ÎCaMǰQÁh7œ5¯Mvªzù…Öq9‡ÛPÁi²ÝA"U7:ªÔÿÛÂÅP)´¦ÓÈ7¶DZyŒ>mmÈ–ð5ðð”ÐIJùF„)Žþ/ªÏŒw#¾ 0è#ÞÛØþðå>~ŸêLdx=la ñš0Ú$µÌ+ Öš[sˆm^þSÎÿ¿½køs/Þk!<ýižþ4[›€ÿÓÄü8>…è!GÙ~¥³eèO^`ÓäZzôœlm}¬V¦1+.a„1ZÅb Æ àA™•œµÇkßë9ËbÉÂFÁ®b &0c0` Ž/¼£æƒsHI¤$®#’<Á_cÒÇUܶ½Â^ Œ`,µÒx^ý(û‡ +eH–ÒèÅ24Šu‡µk¦ºÕ¥£ç°7¡‚åÆR-@Åó'x†Êì9œËÃ)² ÞocCçÌpoá 8W`{hDsåX{¤ÿùü/Ž·œ#¾=0è#ÞØüå°Š®Ùl'Ȇáq_{&y¡’Òèv“ýdÉÃù?¿ÕW½ø¿Âƒ‚ÛŸææO°}piá×Å\Ì8…2^»$B‚E´Ysrèä’ôGêp™Ñ[ÈõNÒ'ô›jŸ%ä–ÇQOs /¢ƒ„ÅehRûÔ‚Ìb$~( |`±B³a<Š&QÕ\ö„aƒ=3 ¾YƒéŽ”½@¶1q[C-57©Ã†B3„.˜0-1¢Z{C¤9|0o¾¾;•¾Ð»‚'ÑW1Õœªyw-a¦‰Ù™$É‘ Ûøª8þ6þ5\T_Þ·À>†Gpö‡—¬˜Wúrôëÿx¼ëŒñí7Áˆï]Üú/Ù»qAâ†VköÉå° Ýsn›p/³¡~ÞlCôpþžÿôÁß¹‹Ìÿgžÿ1NàùG™DmwK.åèkl'\Š8~#Å0EÏ¡m;ÂU6'1¾Ñ¨Ã9¬FbfÈ g+XÂF‘ ©ÛQq/£gJ&šÐzhÿ£`ˆîaæÀ]Ι:3icYÛÌdi’ªZ¬­ßr MÔV1ôgª³Ð‚ޝ\çƒ ñ çÅ\LšÏNœÆ_äD^›>QòÚÝÒ‚JR¥~æ@ü!΂œÐ:«ªâ%?+6>&:‚°gZö+Í7~SPµ ûûz«üײPȦd å]uS­KFpæwéþÚú‚س|û—xñ‡Yh»Ðl²¸@×1«ŠK‹0Fy_ŽÐýÍøq´ü™QàÒàªIã©÷pÐcknæUÔMÿñŸv„*P{ïêpíf±ž?ºð§³–xÓ@ÏÑ8DÏpb@»9ÓŠReŒÅÇemÞ6w8§”jÓ‚Ajm=¶þXò]ä>ÌxÑK9ò¨i’µÃÀÎ9õÕ¶:´ý¦Nw‹C•Q~øFþ ö¼{wÍ«qògÆU‡ì°¬CåÞÅú7É§Ž ìD-û•Æk9üŠÂÒKU5͹„Æ7ìí骱±«ÅeAéÐ…“ÿšþû/Œ5|—Ñï÷æëÞMóh¥$Ê ×$ó_I 3Èm†ª:¤Ÿ¯³Ód3dˤ‹ô™KI2Z óD†u_×ab0( šHQS(ŠòW•‚N“hœ üõÊ&kÈ[Ð ¢¼uÕ}™êް[BµÿÕÖˆJ£ŸA4‚¬ŽKš‹µÙ¸¥œÃ9ЉqÆ—Î)…ЉY/U'vjã“­3y ;~šp ŽÁóáv¬B†Ð‡ ‚óp"ÑfB‰n}µKŒÊtAql½D¯wc¢jKÞïžÿgY¿îÇåSGö Ò„ýÊâÏTÚ%»æŒP}‚òÓWYIm«7Á ¬ÿ-+?q-Ë™ßáàu{MQ-ÅšöEc5užÎk(Ço¦ÂhJÿ>¦¼äò1͸f`Acèô9ØââËHS~¤HT)šÁÊ ¢ ÊfBМûÜ9o&]‡mоɚׅMŸ[^f»L_-„×U¯ ˜£´`,õMôÎbzXD먛F‡ê(œ3™s6sÚZ8åœB9´R-ÕšReqe;9¯€Ÿ ù<åÅpfZDÒØFŸÅv§6¦ôáN%RxÔÊJ·¶Û!•å$bY¼fäÓ…JŒ¾Cã8óJùÈ„½„Ђ°oÿ÷?³¨}£)Cº] è ߺrΚ|E•Àê±ñO/È•ù»×â¾Èå¯+ö¨}„Á*öPq7ھ͊‚œÒN{bõÊ´ŽP2Ñö¦x5X†#ÂvDs†Æaê ÌE\|ˆc/æöÛ8> ܦó¾µEG“×/¡ÞZ…Žl±W`g~¾ð@PG6}ãÜV¯ÔnÙ×êügˆ!…Ö€ö;¥¬œãÊË3;j 3ÛŠP gLfM?ÕÆâ”µDZ(¥µc9e}ä«ÒrpV¡ Cè×h/£û~w¬o±‡.ÈQ­mpöæÇ«ÆØ‘fz:ÐMÝu1Sv7´`û:£Z>oa!´ ìÛÿý 0ˆ«ô„é„®Þ¼.} òneÔnFpê}ô~ã^œï¾ƒƒÇY^Ä€Ö´æ‹UÊί£ «ç²Jžh…:ß9Ž| tê_0† á,d°Dü\Z™©Óh³0;œK›³ŽŸ\bûœë{¹B>å6d”2ŠqþzJ£r+è ¥°©¯/ój:Ab/EB(pL°ýYÕÅŠZcü}Uâ4fDëvFGIt‡œÛŒgmÛþ|bj5VÔtÖªEƒT'f,*FY­UëÍZÝ.¶!eihfa³X±¹£´S0‡®amp0¡Õ6}Š©ši”ŠŽÔˆúT£ààæŸ/é°üsþ3ù¤„=‰h a¿rðÝ´ãñä»5ñjE™e|´,›ò"{gÞKï7/ðÕÐy˜¥Ÿ¦ÝÄ‚Š0ªãFÏØÀKؽgXk[ÿˆ®*Ë­·¢kBâcÀ•˜ƒœÙáá>u™­ÇÏ>äÚ³ª¹È‘E:ÐY†%X€%˜ó•_+¨ . VÏyËùX0¼XŽ.¸ Ê‹ÈÔ}ÓºŒŒ}3XOD„ø ÆÓÍÎ̽ÍÚe £a£OÃ,i÷”‰¬C+gµqŽšÆ¸8³Qyf†jÔGÎÅI¦üéç` ;ÃbÚ ÌÕ¨uÑ ½!éf@J”ÒHQ):CÈÇ+3TR5Þð$ðÌq¨‡Îç GAØä®a„¥ËÊÊ­ n‘OAØ“HZö+Qs܈T»e æ_I0;Uýƒtyök{a}6oᮘ—}ŒºÆª¢.®ãÀŠ®lDd¾ÞšÈŸ(³TÐøÏÓÑs§ç\/„9H¡3ŽFÿæ*—]ÖhÖj,Çü”ãØ‘£gè¦lØêc‡>m$ƒ>zC‰\Ÿ°ä;ß3ðÍ-Ú‹Üyˆ÷º¼ˆ U(šÊµþd ˜M,=ãêÅB¹­ßÝäªÃw¼©u„ÁA"Å÷Î1—|Îá3q:;4`#@c[±3ŽÔúméúbkÍî(Â9ÜâÜC×™kP ÊâF¤éؾZ[ê¼×¬QçÏ[§±Š$ª*—¨ë×-ö©™¦:}¨ª×a+Úyðµ?c]bSaÏ"´ ìWJåÁÝí’0}ÚÖ²Ú.Ïù7l~ŸÓ—ÊF[NÞIDATï©%Úø§ÞÀ%o¯~÷GÕ‹ [†’]ceÖÍùŽ~³ðL¸æ¼:‘¡ã„kU´úè±£Y§ K a©¡7`c›‡†0G|H$Z×¼ö wü2/¹…y°Rtü»ç]çrö®lQë@Û0}y5HÞºN·{¼¹÷W»’þOb›ÄŠûViD —ڎҙܣ˜oŒé;•§™qÖ:í”Ö…¦ÆÐÍûÐÔòóm&¦ž "Hú;¸´x_¥¨o7çp“ö@£"thfâª<ÉNÞYõðÙ݂͙ÒU`í½J>`ao#AØ·ÿû_ÌüóŠÖ#S¢ r4’jfþi¥{W ÞÊÊË÷à*­|†¥W2ɸ‘< LëT04–VËE®¾š ¢Ñs…@^à‚£¾qûŒÃQà’v›)Ç, 1t ;Ö’&¤I¡rŽcÖG˜–塯ؗÅu0зüé›Q×Q?F+”à ÇÄ•nL%éP•œø2úô«„¹™{_=‡Ñ3ÐÚÒߪG5»¸¸©©9«qùYX‹5Öa¥n}ˆ)¤ãœJL êdD è›0²Ög˜€‚ÊÒH ÍF¤P (ïâçéLé[—šæÄk£íT‰l§Ú̃¶ù…ÐÚ9Nþ:wʧ‹ ìy¤-û•Õë9à˜õ*‚UtA6[Y:”­»>¬ÞÂÆÞ–:ý'þ›‘òJ£þ¦UG3ª7úmõŽ¿ Ô 8Ë !ƒmÀ7wcÿø"ôÙþs¹’‹®¦iiXúClJ–`½E±REX”ƒ&Ð[ã·3wa»ÿK'®eðjï(,¥CWŠXSÝ—ó%ð)ÁúíùÃ)ïÚá-s·=³q É<ÆaŸ^ÐdGê.ÖNk—×è´kj‚sXWxŒ ´JZY¬ëQéÑź법Òëa츎WŠFŠöSƒÎ 4J¿çŒ?·U¸©¥TCO¹YS5W e9‡2—²l¹w3ø€|®Â>A:Ђ°éŸFÿCâ&õjÒEX˜lºáÖÀÀÚÝ<üúÿm/¯Òö_±xsÇÆÊï̯þ®}$–®Ø60~¶Sê KÞ £LTiµZZ0„¿„ãôOqé i×iBš°Õ'õ Þ€Ò kƒÀ89 ,>R?b؇/ý«÷ônÅ^ÅâóÇÉ…vj÷3Æ¡€®f·Þ3>xïÔ¯âîÍHx°µöóµÃŒ.%² h@g§mF.KÓ$uY'i=KqŽX©~âÖ‡Nçå®Ba´ÊZµ¬¦²Q–ÖãÆêC‡Kü:€ÆQ:ÞÔ”—ÛKçµøxŸ*Û\îc4ÕP·ÁС Žlþœ¶àÄûX»–ìëò‰"û) a“~‹­éF̼‚F5Ppˆ¦-‘K]pò?³ö&Ü©½¿Pý‡_O-Û Û .;Û-ÝZû.² Ôäew3†Y¨ƒƒ†wÃp‡þÇWá›pF윢=Ï¡KiB»ÖtGdÖ?]±mØø·®ùí¬yoÁ<ÚãlÊçß:ÞµÎgi¿žù£E6üPÀ•Íõ‰æ´=xÃÁæ:Ûïþíø™öwže¯ÆÎa ‡rô’ÈêÔjc‰3ê©a”‘dôF R[W*RÄššN•#ÍÔV¢G.é©–ÛRÓSpÔ!ÎøÔøTÕ~G”!7™v\pF—Îøz⾬ªî~؉ïÁ™Û8uéWå³Dö"á„}Oï=œ}Í·&¦ýì¬Ï˜Èå ëù_Ãܾ_–hóóœú—¾µ(§êÞ‹ƒàê"T—ëæª!Þe•<ëí/òv.¡I«nw)‡sþRhòµÏsôÒÏ]ri½fl]¡Ôáµ>…\8#ɽ&fïïš7`€¤?¹wß»Žƒ[Ô¼ª;4¡Ú{DØí<)kî'Ï2®m3j…í7âCÅX«:;-œ3 W‹hFÞÏéÚb5*uv˜°–2pcÛE­žÔ qô˜Ù™JéÉšl9W¶®ã©Är]½‡ ?6À£¿ˆýŠ|~ÂþD:Ђ @ïV⟣qdÜ1Íó5í=ƒ-lÜÅéOçßâNî¯%Zý#æ¯cîØØ¿/´VÕ Pv¬ªòèÐJYUËÓR…œÏ·mÖ/ì²ÂœŽ;>2ÌnxÆK/j¶NÕx½>ÛŒ ‹;V¡YK˜@¹;ò¹ß Â|ç6¾÷©Ê®¹;kÌþ,ä^a¶™m î ¸„èÃÊ«¦VÐX~¬}ú*õ<²,4,‘1Öé$¢H)]s Z‘d.M•s®—±–ºÕDg®u\ŒÑDÛt›ì×$õ„8o“oCV´]ØÎ·?B©ñ$¡+” n³(¯M*£‚²ª4<…µ³œúwlýî„|r¾E hAè|ˆí-–ÿɸù®[~g|ûnÎ~€Í_ÄüÝ>]¢K×RŸÿÙ9ªöe tÚ÷ìñ\™GMPŒ*ß×'¸Pq>ènàÁ6áþw‘ü‡{™}¶Zzõæ·š¹ïÏÖ·j*îe‹'eñÛÓ€6ÌA3¾NÉ"†ðåÿÄÚÝ“»Öÿ[ÖO2ûz~ƒ“ÀÓMUý7l5Ãoë±óß§×/ãsMû›3³ ž«YjÆX¥2§ûi]i¥5™C+†#Õ¨A­&ÑN‘¬˜+0Ví0l³ eˆR\†30¹ñ tYûF ÈçSå7ÜM%ÝÄç‘;Ø„“ïfãzÌ7äCö9R@ ‚àIÿ†ú›©b¶7Ø~ÓdíX{[ïeç&²;÷õú$÷Áa–®_þfAYZ^äEX­šïym@Hä%qõ‘‘Ÿ]Ë}è†pîûG ‹ÎñÃ_=5{IséŠùFÛfjÝ©‡æêÆzcµßìÙ6qÌ܈™˜Ô ;;X9»ÅÏã—’ÜEç ó¯£쎫öÈ] l˜ÎUÉ1púƒdµû)F³‘þTz1é<©BÙ(sÊ «·SjqDêØèóp¡ÅBªTpDaAÈZl€‚“`&"KÉ4¦F‘E8MlPÈ ×ªz‰”O›>L@N~˜sO&AÈ‘Z„€­ÿÉÆ#¬\Oç·èÞDöר{dUÆt¾Œ¾šÙ+ ]¬|KcìZàp—»^ÄUÇèÒ0ÔO—~Û©ÿê{wá¬ðÀöD¸!?¸íŒq›sGZs‡—FŽîdµû–Úß]kªÑòˆƒ=Z ×¢céwЧIk|ûO¹÷Ï»wÉ·èœaîuEºœ"uSîue£ÝÏä1ô–¿H¶_£\ì1M"GäÎ)tê$44õ˜í;­pŠTUµ.T‹Îùõ)üƒê±°¨"tÝ÷•=™"ÓÁ еj>ÂCÿ’þ{äô¡D hAªdß–5x,6>Mü2fžS´ŸKc =¥îÁ‰ðÈ,ÿ|aT7ò“‚eš7iàÈOþmCÁ©÷/íº!g¿ÑY?ùýg¾ Y_Œ{iM¹Z»ÖÛΚÇN‡ý Ò®VtÅ·kÜö~Î}ç±ö®¬¡ëÁäœÚm®Ž 5q¹>»ë7J 7g¼Q]tDÇ8EÝ(å4ù•…cÒˆ¥ô2”ÂiR]ÍúÉ`=Øž =ÉÇ#£bÛ´#ÒØ&.FPŒ7Û:Rüt`Îtñœ{­œø‚ „ˆ ‡ Âäø«Qÿ‹Ã¯.úšesÔøb®+¿ÏÊ/Ï_½C×0XÔô8MðHéÜœ7}7áüròkÙŸ«;¯zËÅË/=fjG2ÓÄÖÀ¡,ѺOÌé[‡¸û‹|÷“?|ïz7sŸã¹7Óö³x‚áa6`i`ó÷è;¸ã?ž]å¨C“¬IÍäJÔ±’âTZ„ž(ó5q±,;Þèz¬?A­¯!Âùûf€·Ç#…¿ø‰½ÜÜe¢ãÞų “HZታñIv:Ì^K#†Àg#s_çøèÞ<~r÷# 3óÒqNG¨#îŒ÷âH¡ ßÿ ½>ö†tNd~icæb³tY¬köÌàÙv·ƒÚ!ÚD¯0:ÁÙ¯óÛùâ'<ú¸ö.ùÝU–_Sl°ªã§ -ã}Çë7νêq½Eã°»è¥*Eijqñ’yè‰1Ø*Á¥¸”Ôk’ `† !¡0â(×0ïÙ7‹.²‚Øú•tØu4I~¼ÊÒÜ&ƒ*ðùîÁÉŸÅ­È)/Bˆ’%Ax’̾•ç}ŒÙz¡ZÞ¸—³ÿ•ÞÍ»?Ybá—9øvÚ—1ã›Íã¼ôyë.Ö>Lï÷žÐ¶¼ä=Ï{Ö«ß›¼äd )+[Ü{’;äŽãOfï.ù&G^R$Ú 5žVÞÃúz—Gæïë_óõ¼kk0?KÜ.BQ z`Š·êÕq¥x#ãeå›°T[è@fŠr_9š#Øöy+-ˆQŽšÃB7£2 §ï/Úþ]òý=s/>_ÎtA&Zá)0û6®øYSïeó½÷§æ~…¹W°üZP÷±v{Xû(½ßyÒÛrżlø²ëNž­qï)î|¯ßÿT÷îÊ -vò†®™êš—ߟº•ëï‹«%®ß`‘6ÌÏ’ÎâðUìÖa j€—•×aè^6 ã·'o)§Ü¿8ÉЛÐ÷ÛDÕŠ&7ªàª€@žÞ¦Ðçò•‡ÿ˜ÍŸ“Ó\„ ¤€Axjè˱=ÉŸU üm†ÇéþÖÓ²-«FßèchadD,mmodp(bTRCÄ gTRCÄ aabg Ð aagg Ð descDisplaymluc nlNLèdaDKèplPLèenUSènbNOèfrFRèptBRèptPTèzhCNèesESèjaJPèruRUèsvSEèzhTWèdeDEèfiFIèitITèkoKRèiMactextCopyright Apple, Inc., 2011XYZ óRÏXYZ uÁ<‹æXYZ \˜µ‘9XYZ $} äÂcurv #(-26;@EJOTY^chmrw|†‹•šŸ£¨­²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿparaffò§ YÐ ÀvcgtV.ëmI#   , E h“Áø$`£ß\¡â#[•Ñ!"7#a$‰%¨&À'Ó(Ý)ã*å+â,Ý-Ø.Ó/Ó0Õ1Ú2â3ç4ì5ò6ö7ø8ú9ù:ø;÷<ô=ò>ó?ô@ùBCDE$F,G4H9I?JDKGLJMMNQOVP`QmR}ST¢U³VÃWÑXÞYëZ÷\] ^_%`4aDbXcod…e›f°gÄhØiêjûl mn0oFp_qr¤sËtóvwBxfy‰z«{Ë|ë~!€5C‚KƒN„O…O†P‡OˆP‰PŠQ‹QŒQRŽSW^‘k’}“’”©•¿–֗뙚›&œ9Lž^Ÿq †¡œ¢³£Ì¤ä¥ý§¨.©Fª]«s¬‰­ž®±¯Â°Ð±Ü²æ³î´ôµû·¸¹º»¼½#¾(¿/À9ÁGÂXÃlĂŘƭÇÃÈÖÉêÊýÌÍ#Î4ÏAÐJÑLÒHÓ@Ô5Õ(Ö× ×þØïÙßÚÐÛÀܰݞތßwà^áAââúãÑä§å|æPç%çúèÐé¥ê{ëPì%ìúíÎî¡ïqð>ññÆòó:óñô¦õ\ööÉ÷ø5øëù¡úWû ûÂüwý,ýáþ–ÿJÿÿV.ë^%êÕ½­§ ª ´ Å Û ö"Cl›Æþ._“Éú2[ˆ³ Ø!ù#$)%8&@'?(<)2*$+,,ø-è.Ù/Ë0À1´2«3¡4—5Œ67s8d9T:E;6<%=>>ô?é@âAàBàCáDãEåFæGçHæIäJáKÞLÚMÖNÔOÒPÒQÒRÔS×TÚUÜVÞWßXàYàZß[Ý\Ü]Û^Û_Ý`ãaìb÷defg+h7iBjMkXlbmlnxo…p–qªrÁsÙtòv w$x?@@þAøBõCñDìEèFãGÜHÕIÍJÄKºL°M¦NœO”PŽQ‹R‹SŠT‹U‹VŠWˆX‡Y„Z[}\y]u^q_o`oasbyc‚dŠe“f›g¡h¨i¯j³k¸l½mÁnÅoÌpÖqárïsÿuvw+x8yEzO{Y|b}j~qv€vs‚mƒe„[…P†F‡;ˆ/‰$Š‹ŒŒùïŽæàÞ‘á’è“ñ”ú–—˜™ š(›/œ7>žFŸO Y¡e¢s£‚¤‘¥¡¦°§¿¨Î©Üªê«ù­®¯°,±7²B³M´Wµa¶j·r¸{¹ƒºŠ»‘¼™½ ¾¨¿±À¾ÁÍÂàÃôÅÆÇ0ÈEÉXÊlË̑ͣβϿÐÉÑÏÒÐÓÐÔÎÕÌÖÉ×ÅØÂپں۶ܲݬާߢà›á”âŒãƒäzåqægç]èSéIê>ë3ì(íîïïüððñãòÖóÈôºõ¬ö÷ø€ùpúaûQüAý1þ ÿÿÿndin6¥¼V1LÙ£9%2 ‘P T9îÇ®®  *5AN\k{‹¯Â×ì0Ic}™¶Ôô7Z¦Ïù&U‡»ó-jªì0v¼L–ã1Ô)€Ú 6 ” õ W º  „ é P ¹ $ ’uêaÛWÕUÖWØZÜ`çpûŠ­BÚs«Hæ„"Ãe ² [!!¶"g##Ï$„%:%ï&¢'T((¹)n*%*Þ+›,Z--à.¦/p0=11ç2Æ3¬4˜5ˆ6{7p8h9a:];[<\=_>d?j@pAtBuCtDrEqFrGuH{I„JKžL¯MÂN×OïQR!S;TVUqVW®XÐYô[\C]o^_Ïab;cwd¹fgLhœiñkHl¢mþo]p¾r#sŠtôvawÎy:z¤| }q~Ö€;£ƒ „z…ë‡^ˆÔŠL‹ÇEŽÊW‘ï“”•D–ÿ˜Àš…œOžŸì¡Á£™¥t§T©8«#­¯±+³Lµy·¯¹ë¼*¾mÀ²ÂûÅHÇšÉðÌKίÑ"Ó«ÖVÙÛøÞÜáÂä­çê“íð’ó›öªùÀüÝÿÿ "-9FTcs„–¨¼Ñæý-Gb~›¹Ùù@eŒµà =o¤ÝW™Ý#l·R¢ôGõP­ p Õ < ¦  ñ a Ñ A ³&›Œ‡ œ'³AÐaó‡¶Qï2Ø€*Öƒ0ÝŠ 8 ç!˜"L##º$u%2%ñ&³'w(<))Ç*Œ+Q,,Þ-§.r/?00á1¶2Ž3i4G5(6 6ö7ä8Õ9Ê:Â;½<º=º>½?Â@ÊAÔBàCîDýF GH+I9JGKWLhM|N“O¬PÇQåST(UMVtW›XÂYé[\8]a^_º`êbcRd‰eÃgh@i‚jÈlm^n¯pqYr²tumvÎx2y˜{|m}ÛI€¶‚!ƒ‰„ï†U‡»‰#ŠŽ‹ümŽàV‘Ï“K”Ê–N—Ø™j›œ§žPŸþ¡¯£c¥¦Õ¨’ªR¬­Ü¯¦±v³Oµ4·&¹%».½>¿SÁlÈũÇÎÉöÌ#ÎTЉÒÇÕ×lÙÝÜdÞúá›äCæñé¤ì]ïñÞô§÷uúIý!ÿÿ #/;HWfwˆ›®ÃØî8Rn‹©Èé .Sy¢Ìø&WŠÀø4q±ó8Ç_­ýP¤ûT¯ m Ð 6 ž u ä T Å 7 ª ˜‘ž(µDÔføŠµMè†&ÉnÁnÍ~.ß !D!ù"±#l$)$è%«&p'7((Í)š*g+4,,Ð- .r/G00ø1Õ2¶3™45h6T7E8<98::;Al?€@—A°BÌCêE F-GRHvI˜J·KÔLðN O,PMQqR˜SÂTîVWOXƒY¹Zï\&]]^•_ÍabDcƒdÄfgNh—iâk0lmÕo+p„qás@t¢vwoxÚzH{¹},~£€˜ƒ„•†‡‘‰ ЇŒ}Žúz‘ü“•–’˜™°›CœÜž{ !¡Ñ£‰¥G§ ¨Óªž¬l®=°±é³Ãµ¡·ƒ¹g»P½=¿.Á$ÃÅÇÉË&Í1Ï?ÑQÓeÕ}טٶÛ×Ýüà&âT䆿¼èöë4ítï¹òôMöøðûGý¡ÿÿsf32 BÞÿÿó&’ý‘ÿÿû¢ÿÿý£ÜÀlmmodœÝǵúÿÛC       ÿÛC ÿÀ#"ÿÄ ÿÄD!1A"Qa2q#B‘R¡±3bÁÑrá$4CS‚’%c¢ðDs³ÿÄÿÄ;!1A"Qaq2‘¡±ÁÑð#BáñR3Cbr‚¢ÿÚ ?ø"ÏXiHó„—¸?KÿÞŸi×Ë?y\uFª­Î—%³ã”Š3OžXŠ—$òô=Å=›Nl†ÄYË9$HÝÑ«]êZ^Bm¯íÄU [‡›|Å“9@r27¨«¶›=¦«‚v /åcÞ†Ô,å°Ç"’?¡d”ÏÛøvú YY%4›ÝØû!ô-`ëÚqÓîÏþvÙr¬Où‹ÿ4%Ý¯Š¾ ¹B²#äz«Ú¶ŽÆ?šKÝ=ü)ãn`;sf'µ”q ´Š?+T#~0×vò?±TõcŠ[³ wo#ûÊkM~;«1¥k–éwn>d§Øö¥×›pAÊÃuaÐûƒL&a2¥ÈêF_ZÖÖê;¨¾J÷éüÝüVdµ‘í¦ØZÑÑÌ×øÊ1Î.>?˜~! s&õí°š<¿­f)écG"¬‘àéÌcp² ¼ÄíÁSnáhœ†+mV­CL¯MûUræÕàrM/©…Ñ›öNé*Û ·t8=ÅJ“èj5•Í ×ÂhÉ<“6ôsxrn 9Enµ_ùNi²Íâ[aºö¦°Ô–³i^|¥®G$¨qGYÞ¬‘ønrG­(‘†k6òrH wuxF:Ò18uF;`T(ùNôTQ$‘õ­GíÚ—8îÂÒn!/¿³xÔ0ÅgqãÚœî´Lâ/+ŒÐбÍÓcFéÒˆ_´œe,¡žˆ±NVm½Z´ùy‘P¯ElS#ãFÆ@Ç¡­,v7Í.¯‹uÜ;.Á£èBöEYG”W=ÕµK»ÛƒçmΊ»q<¥­ím×é‚)/ é1Iqq©N’ÔyAéÌkEö¨<¹”tøcÔØOÞ±±†Aªd'÷°=/†u뵈ü5a‘Îø¢¯4]sJYàpŸÎ¾e4|ú”í)v“•A§z¸®~^YRhÛfFÜYJXÚÇ€×|Ò÷ÖT0õ AVýR=hîO„ã•ÏnÇíJøŸN{yËrõÞ®Úß E§òk:j‘i1ÝøÛÓíAêVˬiNq™íFOú“þÕô&ÆêÝ5ÐKÿ±¢ãÔ ýöÏÑ9¥®Žv‰òœB¹uâíÉÇ™65Z–/—FHz—Oh¥u+åa½'»Ñ|@Ü£í_;¨k¥7þá„âžv¸í%V£™ƒU“C¼*@'n”¡ô™U±Êhë;i­ÈÈ"„‚GÄû•ê‘ã9M/H‘Í,{|·(q”3yÆ E1 æŒ3nÊ_ ‹0—O(;P‡mˆ¦²âA‘½/š" Tùm™DûŒ¡Øož•´mÊ1^({ŠÂ¡¡º„”FS¡ÍG;é[Œ­E3S2ZÔ3ÆýÁ¡~~å:HzÔê—êÜUFV«Ûƒ‹)]»TLܽkF¾F݃íXA/Ôä{Ue÷8D5Ž…‚Ç5°bN+̰tŠÙí ýê9%K²ßÃ$ ÜŒVè"#üÀ+V)œ¬[³eFêNÕ;TŒsŠÛ™@Ï5zÀ®’T-j¤ä’?JõIÏžõêåš½w+×ÌÆëË8çC߸¨šÕWña!“ÚªÚvºÈW+ëO-ï?Ùáê´L5üGQô)q†Js´ÿ„|7N*ÏaªE©À,¯pdç½TÉKµñ ÙÇTÿŠÖ+™`` >SŸµQLÙÙŸ¡ ’Ó¶­–8pûÂg«E.)ê=­MÃü^º}×ËêQøÖw‡2žÃù‡¸©òvÈÙÎÀN£(ǽS¯ã’ÚgŠPACŠS$,™†™ ˜é™TÇA8Èþ@Ö4¨íž_’—>¥Êž„zŒUÃá®—£Xð½ïêÖQ\Ì·?+l’Œ¨p¹'ðýëiº“ê|;sŸ˜±>=ÌgqûWFá™!Õø+FÑ ¢›R˜»vær‹“ú(¬Þ¤é~ ÷pŽl.OÞP cØLRäƒoØýpSK[Î2â#$úB46±žV›˜A Nm†}†õµÆ•Ų§…&©¥êý¼“´ªþ†¬šìEä:|jÖún–žPDq¶pÜîIêMA? Ý ˆ¶¶Á„\˜bšO"åæçÜrŸ.øÉ>ÝkesŽæ0ì ú›ƒo½l#û2P;Ÿ2slX ã<ù.m}Ã\1«O&¨é’ðö¦»x‘¡çýqƒÝqö5BÖt]W…5V°¿dL’ç+½.ºHç˜ Ñ‘‰a9SéÚ…cëFËT{¡£ð›„¦âØÆNÛPÅpi´Ù9ha ÓjÈÂnÔâ ƒo_¦091ãP+lÅ©²Ãg†}IŽsŽp‹t¢ÂÈ)y‹l7­‘dR6­æ™ü1Š¥,w-^ÞÆžnŒäŒ+.œáâœQ '!Æi^ˆ|LŽRqíL&“à(~» ËHB9äHZ¤HHØŠ`‹ ¶((¥f9æ¢ÒLìW>ô@‰² ´®º2ႚÃm‘q³ "ÖYÓmóŠLæVÈÝOQM »BàŽ‡¥=Óes^Ö¿*†8—ÝÝ;Ö ¹Ò-n—v‹17÷/É宣§¶¼Œ·ï¿ç…7C,wFÞ3€´.ž’DêÜäß5bãK$f·×´öñ¬/4rÍÝO¡•V<›‘ÓÞ³ÒÅ 3²™æX×ß½×ZàÍj=jÚN¾ üÚâcÙÿ/õ¤Úu¼Ú}ä±]ŽWBÑ:ŸØŠM†B Ýó†íNxÏYŠM~]B-’èx„æïLbÕ(iŒØ´Û膤…ÑU¾ž1áx¿ÿ°ýÇä”êÖ–±ÊBãØûR -• ž_.i•Ä¢úä2ù…GnÛ¸˜}[Vv©†£ªß”ܦâÀí×㔚[h³Ì°¶±IÕ@¢.À%ONÔ2ÜzªGµà9½×&»Û¹… ¦äsGI®ãò±;U§ÅWŠ_}f’œ¨ëB¼[-UÓÔ²%´~õ¬‘ ”©ZÊE$^Xž?½u„¸YÁ2lå¥ñ`t¨À ×½9Æv ÷ªžæÇ”dwrËÚ´å€Þõºò“Ö£º®We,pŽZŠá06\xÅCr 9|`E…8Þw$ó“šè¹Ç3hb€ÖZ¨ä¢Þpµ'j&,ŒmP/_J™â„C<¢S “P£+'¥Z‚â²ï³C¹ë[4€÷ÅBÏ“·J7WÆË-XóQ‘[“ŠÓ>ÕÛð‰hQò’k"¥?¥kÊ pµZZàÖFFù­‚ŸzÛ”Ž•í«»JðrZÁs÷¬ˆ˜ýëtµf5í¼ªÎÖò´V$Ô¾fè1SÅg“ÒŒK0:Õ&_$,“´‰ñÐשºÚ «Õ©C|SRW„«`Tö·³Ú7•Ž=*Ya’0j ñM&§onyblƒ)ýާÌ[‘ý)À’;¸¹\~Äwª3£s!"šØk/VöÍWOS%!ÛË!*ž•Ñø£NüI¬¦ ‚)ìqëÖ¦æîøŠ?8õ5¼ÐêpMB«s¦Ý,бR§ ÑSÁÕhžœßùÁB¹Ûìæáãùeî»·¯k+rGr<3°ÝïWî¼’X.tÜ—1Hn­»`uøU#ìj™}coªBu G0ÿ:ØújŸNÔdºðÙ]âÔmˆ!Ã>:0?Ì?ïI«)ã¨iqÁïèGÛ±ôUºV¹ýv‹vpò>çØ¯¡4qõÕk«xÒ[–L^Ù˜žî£ùs¾GN‡ÞkË‹µ±kK[ÍR;fR­n œ¨<ÃÊrr6Ò¹V™Æ:n¢ÉüeÛN¿Œ‚.âSÈì;°]ѽÀ#íW{_ŠîPâØ®Ôþqqÿär°Õús©næx}ïo¼òàöZh5Éa†Í±ú={‚/ß$_(ˆg:\_Æ/£ùd…-"cæ–NØõÁ9' ¥Sx²êÞǃ¡±cË-õÚ°SÝc'÷aV gO±¶-®qO#L£,ž?4˜ìN?ZãÜWÄóñ6µó+†Ú[EŸ¡þ穨h›¥œ< ´“b/`/ÎMÉYýF³þFïw?Ï,vÂw¤„Ž™_é0´cܰÅSDkãò†V«…º°Òì¢|ƒ(iIýp?±ýê½iá<×J:½l ©;ÞîAý0³4Žï¿Ó ¯¹‘û8ïëH] g ÔSé³Ã9ïCÝX‹˜¼TaÖ˜µÁ£-tÀk¸CXß<å{ƒG3Ár9£![ùi1Gа ŠÙ õS‚(Ω,±W>ã¸r‹ò±æ„#5²N[iFkr‘¸Êš Æç À¡6'Ú¥Š#&¦TUõ5"L±l ºScO.DµÏ DҙNj4‰ C«1þÞµ£\i‡B× ?3ì?jòîk—ÌŽHì; ö¥Üêw+¬Lù;àt©:fÆ.>òi³wLëô ŤjÆ8Qlˆ§m…I&«‡ñ~¢¥“EkX–¥ Ê1Ê£&€—O‹¢ÌÀûŠ :¾£‹®„ˆÓÊòñݦÆmÂòŸU8þ•¹ŠXÇ“©L³iá s­Ô{U'Rº7*nmÆ?ùv>¿jÛ‡õ;‹[µðÉÆrTô¬Ìõb!±ùocééûvY™è ÝQ Úþã±÷~½×Jð­´ˆDHáŸ&–ëWbX£9É~”­õÕ¸—Cœ×žSràžƒµrŠfÚÍî©Ó©ßÒr§ÓÞC0åÎÔ]ìÍð˜y}k{!¼~+Kµ^BÀ ­=¨‰¬¢%Ç'+S5>øï·”OË›èJ©É"–½±Š¶ÄRh¸¢KY³LÔ·ÜF'"@“Ö±ÑÔÆ a+<(ªcya)ƒIÖ¢yœô4¡µ†ú«xõe~µi•¤Ú뎣{y Ä<­»×§†Üîf”É«,kµ&°ÄåGõ®>©± ª ¤”ºã»è±ôÒ¶‚BÄòš%/LýI¦0Aª2ª`5%0d¦ÙÉ Œ±[ÂïëMå´Ž3åÁ gíGG„\ò‰dÂN"\mZÊÁ”ÖR<š$Z†Z-’9âÊààÄŽeóf„Œâ›ÝZ…;PR[w¬ý[qíísnƒ"?záSZ…=©i6C¹·Sø§Ò°dÏZŒf³Ê;š›A+Ì„’¼Çœì+<˜Öy£NkGœ•pkZ.âŒl@ ¬”î@¬ ò%Éè*3»UoÀRðŽ…—¹¬‚¦‚Vw;ûÑPÛJûäâªø€½½­åJ=O¹n‚·‚ÉÀÎ(Ø­Ü(¨š¦œYQ%\c ³ "¥Ž nE8úͼL0Í^ pP¯{$)z§ µ4`“’(ãe >V¯ e|×¾ á+e¢«r«Õ±eÏÕý+Õ߇(N™I¥ˆ‰J°Çl‘—ldS;™•æa"æ44öÊĘý³L÷nZM™Ñ\Tl#Ÿ?j’HÝN9¨Œ}ÆÔ3Ýè­°=Ñv´–/.HÏz¹ÛÜÁ¬ZÖ¨>=iÞ‰,°:”?¥§¹ýMƒƒÙ+®¦hF`„ÌÇw¦\x‘±ÛmûCL"ÚŠøÈ s!É*wSEô<ØǘR{‹{>àOnO.vaýsP¡ væò–õ¹Ã¿4î9¢òjQ–=§Œy¿ûôÎ-gŒOg2Ìž¨wqÔU~Öí/6I»§cö£í.®¬ä[»FëÜR`×å­Áòì¨s$k —oç²=줷0ÈïšW£DÒ|ŶÛXcÕíõ¸Ì3*Åx«Ûa'ýéaÅ'+‡’G:"Å).š) ð|¼ÓM~$‹‡ôù`qÏ![׿½%Òã·Ôâ’ÎiÔ<ƒÊIêiê›ëöíæˆn§¸ªÝðÓçñ@oÊßI¢ô™c,1¸fçóºî›Lj#t`ø®Oãt}ö‘$24de84¸E, r6« ÞÅÄV?9c*­ìñ¢'Çó +…É[¨Ãv§]ìµ7„JZZîG#¿óÉ)–ÍnFë¿­ úMÌC! jµ%¥”Þh\c[xbXÈdo²!’½¸üÕ9lä$s)‰KP£z³2ÚÉ´ˆ¿qCɦÙɺÌV¥º(ùD|dlä$&Ÿø¡å…ݼ8”“íV?á¶«õNHö©í#´„“@,zÐòÔ xB©ú…²ÑtŸNáPÈ.õyþ^Ütw`)ÉÖ¢Ó-M®‡d¶êvæÆ]¾æµ-6©t–ð©yò¢öê;k !<;uYn¿<ÄgÑGo½)©ˆêdùvúÿ=‚]QT\G_Ä{7°÷ÿ?@ªMiÄ—âËÏmó#þ†¤FÖŽé$rŸ@àš³Ûé—:”ÜÑÃ=Ãu!±¦ñiÈÑnbÌÁ–½ÎÙ‹ùc÷º)º«Ø<-@¨‰Ôˆ®ížõÆ)ͦ„/O w :5_!Ó¬na\Góí°ætûÑ´A£H±‡ç¶—x_ÐúzÐÒÄ7ZÇùü²ƒµ±&6»ð?Ï%Áü% ¬¸ä`k]U­îOñ |r1óùMEÄ:ƒMy#Ù‡•ÿæ©Vž“ÝÎÂ…˜ž€Q¼AdaÁ£ôæM#NIÀÄ÷›=Âö¨ÁGĄӨí¢G žÉœ¼7zÖ€s"¶>žaš£kVW6’ŒZ.„˜r·•‡õ«[1á¸BÉJFNU….VtØïQIèÔ ñ}éƒ"äQQ½ÎB€"w¢…S|QоN”<@¡ÁJ°+éLà°‹ð‚ºAéAeЉ¤>µ¡žõ«Z#¶(B⬠RÎäÔ2?jÎä`V¦"Æ¡+ìßáPXàT‘ÂÌh˜­rsËGCj)sçÍ‚õ¼(ml³‚›[Û*Ž•´êæŠT@:׋’éª Ö›)Š—œXåAZ» ÷«š-”1ÊÇ9'­Oí“Aóe¶­šFUëW2@ÕÛÛ„A¼T|f¦¤ÛŠNìÜÙ5ºÜc¦hÈkKpäds‘`So?ù+Ô¼]6;רߊ‡É_Õg’‰u iåüDÁ'µzy-ÖsÉ>1XÉ:å0sÔRíFÖHçg•ÎÄP­¨»U­kël&?7cŒNÜÞâµit6é$€ûÒQ¾Æ£u ×])µì®øVÿ؄辚7Y¿za¦\éÈûËš¨70èh­=ÊÌ>õm Idí6Pš’ì7qW‘«[ÚËÏÈ=G­5H¦ŒÈ#æFú–«)* e¥gc×úÓÝ@wŽR‡RÄæn…ëöx¹¶£'b:©ô4E¦®÷éáÊܳ(ÿò<ò5¤¤ÍìÊzAÜÛ˜níXò}= –1&[ʱ‘5à5Üö?¢j—S[N³Ää20 Ó]Vy%"xÜòÈ¡…!­Ì"e'f†›X#ÝiíÜ÷þSÿzK¨F×´Hy(Zúvíl¤d`ÿ=Ñ\<.n܊ƒ,Nâ³Å\+$‘ë%I•‡âú¸ê+Ü?{kku=¿ÖóFBöÿÍ9±Ö¯’aJ ŒíéYi4ñð?°±ñ¸KÊåð›ÝñfdLàþÆžÊð_ ¼€ró}ièiβ÷7?2‘Ÿ4-Ë•Î1Ò«–ZœqIàÜ[¯+{`ŠÓQ×:Vø‚dÉßRÞ¦ßæÇ²ÐÏ$O±#JjLÕÏëR\YÛ\/<r“¸¥)–9`«¦L˜œ"XæN,yLMÎ7Îj3xÙ „猚Êc°¨¼Ü(º9E‹‡=êx Š+çÛ=Í sǺÆIíµ2²Ó.%as¨±H—p™Ý¿JYCJbÆ ÚßÎ!¥ØK©Kÿ¨œrEŸÊ½ÛõéVŽàèø’Iu­zy-´{Wåv_®wþDÿsÚ«ÒÇ5ó[Á s=Ä‚(w$à êZ¢­ˆ°àí$s%˜[e ÿ¹3;~¬lVkS¬‘¾ÍžûÜÿÕ£›~CêP”Ðõœé\.Oðî.-²ÓQtÎÓ Óí“Ê«yßÝ›©4ÊÓŒnm¼š²ËõŽâ U‡ØÓ $Óø<¦“¡[G>¦./І‘Ÿó'èQí‚z“SÞq.¬-Ùu¨¡Ô¬ÎÒÅ3,ʯ\¯ýCzÉÅ¿^×7öZx~ÎÍ4eÀrCIПNö½•[áþâ+Io¸J1c¨ª—{luðóѽ»×,}|ËšeÈ+Ìp3±GïWŽ%·9yi­h3È4ÛÖ& Í–‚EÁ1“Ün=ÇÚ¨-Ö-nb «•z AÃãîFZú‰©½í‡× ´žA´ÿŸ+\‹ ¥Ð[ =A‘ü!/åMOJsyÀ)*÷Ö¹¼ÂHfxØTàÕ‘õ 4í`äþ ÀWaÛ~¿×4·‰í>^ô\DsËÌ iiçûO|…u>ý?ívGê°Üøðª¹Ë&ÔÎÅÄ©Èz­Vm®0øéšoatc™X³ƒZ]>¨€äÒ'tßèB™% ØÑ6p¬±È½AÚ¢8V:6â±op^ö4ÍN& !ý”Ë»˜äÃXU1¯.ìNOÞ¦6Éu© ióàZEÍ ¨\ ~¤Š_yw›ÒÐS‹6G×.mÜóq´hNÛ˜0®1úÐ5Âô»âù¬OÑCá[ XÑê$ûO¸Öf·/§ÙÛ¥ ÈX•P®îßÖ«üGiÆš­”K Hü’Æ¿J¾2ôoju§êéÊ–ÒÚ{r|3ÍËó¸Æû“AqBÛEŠÚCø× ±Ü*‚3ú–þ†“iñ¼\kþ#<þwG½Œ0c›.mÄfüÉóT9ûž´S»ý©¾¼¸»‰;¬C?¹ nc e+ Î(YÛ°³nu¶‚«ÒÞ: Å o$=óZNO1£Ò„–¦G]:dm œ/-4_’ˆ|“K,T©/è*B3¶N1¹4µ.Ýž“Äë^ÁÖ¶—9ð›”ö…0ˆ›”œ¨‘žytÅo{ŸX~e‰¦ªí!z(}—ÁL¬®£R#˜‚;Ju L ¶5PCÓzu¦]­ö4â*›bÈz­8¼#$±Z žm…Gu†È­bŒƒVuÞãee†ŒCŸZÙMRùÓkû%Pê ,ZO²¦\£ÍPþÕ‹+i£s«Í ŸPÿz°Ïq¥õˆ ëP€',J§µ!‘æínSŽ´’ ò…};ø\¼Ìy¡u­$¼’Æå]4da€èÊkhurÆèá[d'òŸø Á)#XÝ0p„ö>”<´¦F—7H eÉüÇš.h&71Íbʰñ²ÕŠ^æ Kh>e† €tÿlÐ|/YÙÎ÷yxYùGr‡ÔQöúIÔ5mmdSÍþ`;ÔŸÚ°ú€kd-a½ÿŸ’CQ3£”À8üÑ\7SA8@ïø/…AßÕU—OÕs6Ÿ4ˆ:ƒ7ô®¦¶ë”ÚV’ª9— ùÀUîÎj³&€‰!ðõÄY³¶c!?|çúP4•á®q#ŸÓ…v“Hùw¾Ø'¸T¤ÄˆZÐa×s ïÿOüT&æ ´åsÊá=Yõ.âWsqǨB¾"ȘÅÂ}ÇS¶Ç¯cíRÔ î18<Àvq×÷ØÖ–ޝª1üýÇúL„;µßOÙL\åy€`qŠž,Æ9†ýèØwY‡æØýê8®f‡em½)ÃHxÊ0Âdm´éú¤/"Ç0zÚž\éw³F.KByÇOÞªZ¹ÕnŠ9bŒs9•r·âBʼ4±“˜ò* Ë+vozM^ÇK$sè?EŸ¯ñÈär<‡§¯¢eðÍ­.¸ÓKÓ®y–bñ1éâp?|UãN)ÇšT—‡•WR‡œžßˆ75˨ð¶·Å梶÷6²,±˜WÄ;ƒœû_AÞé\Å<7Ä.þê[ˆB‹›UGƒ7æÎ gqíY nh¡™²‚K$.6q½¾ûýá:ÓéË,öä`þE 9– ÝEQ­r3·äR[$ûl?jsÅúÃÓéÃMkà×2KÑݺ±/(ç(嘌ýÎõPPœL·kHì¡5`|Á»¿°'pzHûÏ/Ëó }2Òv¹š0™ä ¶ØêlR ¾0Y#l{úX[üEõ­2¦–:v<¸ ³s’q‚ 7±ò<á/×fVà^V9T‰bϲI“ûb¨ÿ$ ¦pü$Žp“¿èYG÷¬ºõÈÔ®,8kL"XìYšWC•yߨ=Â…QŸ^jçügªÇ¬ñÚÙ¸’ÞÅÒ&^©‡Ý‰­–‚Ç §Ës½+ ©Ì×’GaoÏò½¾Š­Ä­áMhs‚Ðÿò4S‘ªh(ýd·nSö4»Œ%Q­5°;[F‘¸?ÔÖ4;ß9 cåt­[\á$Œÿ>‰)ÔѼr3÷ßô)C+C1SØÑ°O¸$õ­µ{^Sã òš)0M>§—‡s$hrºiS‹»Fˆœ¼}>ÕžJêoÊhºä¹Sž»wsiò×~"쯸­³™éCÛÈÁýÛ.Hïdºîà›†`OZn%°¥Ô{’lŒ) ò””‘œX°Ô.,e扶?P= J7µ¶L&gZYÈW{=W@±4êåv ñ«0ýHÍmq ×…®ï%g=^F=©U–²’•?+1ï½3¾¼y­cŽƒaOhè£8Ø ù(E’FZì*&±'$ØÂ“…Ðt¨Øx–ŽŸÍSÝ@d™œô æ å¬N­2lg(«§Úë7²«ÞÙº18 Ln1Vû‹x¦\‘½,–ÁyŽ1I$ðòˆ†£rÂܼdcr*wµhÆTh«X¼ÐQ9Îê){É' ™$%Ä„ª%|NNæ¡ÕIUFáF)ë¿*O/Ú–\[‰›~µ8ÉcƒŠ²ps’ÈÈÎôd.Uƒ)­~Eƒm[‹yý&›Å#]Ý1kÛ Líïù‡,»â‡åŸ¤rót£í’Lçz5“ò…};A¸6Vk%“£Æ–Bç¯Ú“ÛÊbPy°~ôRjr§þé£R0B)]ò”ðÂWè?µC&ž«Ñ7¨¿ŽÝ(À“jøÝÑ=Wö¯=ÍpRøz…¤ºkÍËÒ†’ÁºoSˬ߃ÐP“jƒnd?µ#¬cFPSÒÎ ð¤K,uÍy¬sC­æ¡Œø¤ ójw*?ÍûÒ§LÛYY5­pµ–ÕcÎô;2'zëWŸ;•oÒ„“T'ëE¡º æÃ'÷#žäŒÔr]äaE/:Œgfˆ±¯ Ëc³3Ò¬4CY·Ši™†àV‹+gêÅEã@Ý&­eTÖE?c]ÞJéÛÜ-šG-×jÜHËÞ£1HyMdFǵ{s»•µÄoDµÀ#zTªYfdÕÍ´!‹ZJŸÅ_æ¯PÞ*ûWª_è»´+~±ðMV{ xq¶S=yã?¡“[ÝÄd žõ·y.©©M} ÃJÙÀèaûb €0¥Õ»kK¾`®h›óŽRýJÝìßšLgúPKtùÉ5e爯$±ó)ëšOy¦Zøù‚ã Çö¨²aò½Nð¼}TßH`ÓkMDœgsQ[pާvÁ¬Çݘ¤QbÒÏ„ùÇVa½q²Â^ Ê¢i)ßáa¹òýÑë¨Ìcåå`>Ô®ñÞCFC«N¤ÐÑâÊÓVOÂd‚àôì­ÿ¼Ó*#t!’H¦vç6ÃÍUù™3Zs±bH¦Ž‘{fån-Ùqê6 Ñq±ËáÇ-ÈZ i£•—èiTƒóFÌ¿=gâÿïB0ÇÕ}kÆ”cè –’‡åÈ0?˜zUf”·Ål}»ŽBk·‘NdÒnd ãŽTbvæíDiÆçEâVæG…C¸ü¡‡)?¦sUûËcm:ÍnÄ#yÐŽÕtÑn-xºÕ`¹eRyI?û£±÷5‰×ô³éZ<'ŸOP‘êløW|[EØyô>~Ý’²i¶ÍogªYË/㬉!ß%dú?zÙÿ†*¹UÄ <Æ\œ66=Nÿj¦_êzž™yÆh%¶òEtªYJôÀë¶ÝFÄTÏ­<Lšb†\1Y¼2þì…¿ íX#E ;‰çý<¸OtŠÈ#‹{ïåoóçŸàO5!i$ àLá» z€AæûT}cK “¬$K¢Ž˜óÚ´¿â2òi(–`‹ÃR±Ãž¤grÞçÛsŒPvÚ„£³WçT9sÛ>”JqºÿÏçꃮ%Òõ"8çéŸô…½¶+dI_¤ŠT1Á«–³lŸÂ¹ÀÝT|2ŸRMÔa>ªT5Vê®\9b–¼;s}'çp‚‹áxRIµ D¦ö°_bÍŒþÙýëI<4I’ÐJ€ô­8&öµK.éÂE©ÀmÕEÈOÜŒö¥S8½“;¸?€·étƒê%/çq€²èºTVZ¦f“Á šœ¼³F(ûvë×z?„8žóƒîõþoËà:ßY• Ü e‘Ômœd«}j§kqèÜÚV³k;C…¡–x°·}Ží‘¿zi«q.Äl¶BOô«É-ÍÀ Ó'ò€ Á>¹ßÚ²õ4eåѽ·ÇžÀ^þ|zZàäy­sÁGÇÊ{yºö'7¾gá3^¸þ?ð·‰E²ÿ'4Þ Õ±=W#êâ™Zp'ı¹¿âÞ)µÒ´ˆ#üy®.’%p.ÉôÞ¹ÚÝiòe•FV*Aõ¨õÉu ø}ãžid2\ ìNpÖ¥%Î-c%·09À{ÞÇÜUGÆma±°÷°qOèz}´š4“ŽãRuå,½Ö1ÔÝŽçÚ’ðÕ”v¶ók—‹ø6‰Ì þwü£÷ªæŸ§¢:½Ëàgéiré¶údXDcÍÊ+CI"gÂÃs¸øœy>¶0jЇT‘NÎüŸNê—{$—WRÜÌyžG,ÇÔ“RÚ·†Ûš!ôù(ØïSÅ¥H˜ƒŠÐ<µ­²c$Ì ÚJ’.¬ä‰ºã"“]‚:{SèíÚÝ9”TÚÏ!ÜWÒËpZ«†[\·!cMVŽEn˜5mÔd籊u=°j¹ 9`§¶±M,Mk*ùl} <¡sîAàªúá“6CÇdŠy™É Ù¡ä6çO¸‚Sƒ5ÛrÌ)€cšž7³mÚS'§÷O‹l{R}ÀWåÜÓ=Fá"ƒ{V²•Ö£/+­æÊ¹{!-Ê» Î*KÛÂXìh¸f=kS /)k‰'(®bGZ‚SÜTbà‰¯ 9Îæ|l”(tÁÊÔÈÝ3YY@>õ§”ã5¢œœÒ³k¬­l”cMŸZ…çȨò}zÖ¤‘ˆ¦aR,Ù94JÜ!\b€$ƒ[s`f¥VRd'Oå­þl®Ëµ.2޵ƒ+zÕ»l¬èéºb~ª–9É=i@œç§ŽãÁL2[¨š`þõº²Ž­Š)¹·ÏZ˜6Fæ˜4o±àŒñc⢖íq…P C‚FÕƒ#½Q5(xÊ¢JF¼ei5é %¹;’sR\`u4ºwåÎôŠ¢˜D—ÉJØø^šbÇcŠä'zÒIIÍBd÷¤ò ˜S'½crp*!=ët“|ÕJ£p¥å=ë •;Ô>k|] —óSÃysë!Ç¥kªq*JÁb·Œ¤Õ­{‚¥ñ5ü…bŽ[iÆAå5𞫽(IÊ ó‚ƈ`¡~³-*B§=ëÕ¡¹RI5êå‚–×y+ÝÆšAÉÈ5V8“u'5.ÆÖìÂjÉYzxˆ0GéW}&Õ ñ-n‘˜ØŠ…sd€£O¸ÈIëª'¢oõ˜mæ2<¿‰¢c”Â-ÂÆ‹’Í÷®‰Ä<>‰“vÅ#áÝ Ë¯B²/–2\ŒzRÃPÖD^{kã0:AØ,êúÛðÕ¬z=‹1PÓ8ë“Ú5ýΣ* V’G8 ’j>0fmjv`|͵]8#D¶ÐìW[ÔP©—0£~E=þõH|T4âb.÷~$þˆú úM’Þ#’}Жœ´+q¯^˜]ÆDQŒ°ûúW§áã­ø]¤c-1  ~oÒ˜MSŠ5) ÓP¶<òÈÇ ú³v-× ßi1µþ™«Ezm‡<©ed«.~ ;÷«©µ™)Þ¥ñݽ½>¥4:{¤fàÜ%jFæß‘ÛŸ”`ƒ½/»Óà¸Ì¶`²zý¨ýGN›UõÝ0²¦÷p'ÿôQéê( ‹^ÖÔE³øxËmúŠØÐ}£ŒZää|ü¿d¡´2@ûÄ—Æ_ 0s¸¢–Þ9|¹¢-Z3%³î”}$ìÿ÷¥ ªMo!IV]ˆ5®¤×i%^=ÂgO¾\ ÈNžÌ´MnwåË!þ⦰»KˆÉR§q[Ŭ«€XïYžx¦àêÊ¡KRÏÁW:صâà£u)Ö­Ëͼƒ™èÙëU-Sˆµ+©I–ìÐÓÜÅ*x,r:jk(O*)÷#¥cêthZw±ƒwš[<®¿L]WbmJíùcRªNü£÷«^“a ´A¦u-èiËpËÌy}©…›$â³õq½£iÂQtŒð¢°ê2¤¶Fã5Tž>GéµYcŒ¼Psie›¥ K(ŠíAÒHØ.ÒQ\+~lÝ£#1J¼®¾ Ðú¥˜µ¹v‡<¤ó!µ¥›Àã)…Ì"⧨›‹Y)wåAÒ69ÌïÊžÛŠ¬¯áXõø_ÆPæbçê__z2$á0ø±%Åãö¼‚©sÄð±Ú‡bJƒôø^0H@á5›.‰7p¾£.l&¶‘z<ÀÕKˆ8…u'Kk(Ú+h‰+ÍÕ‰îigš¶BIÅ  ;·2ÿSuCÀòL´þi&\ï½m¯kÔßé]¨Û2=Ä ÷›µ5Òâ ¸²–añ–KÖ×…@Y$w« BÑtÏž`;{Õ8KœbŽ‚ýþUíËy[¨Í?¬¦ÛÕ”f@Üg*+ûãrä.ËéKŠ0'¦s[à±DEOn0Œ‰¢µ¡ “OÙÈ#ÞŸiڵDŽ9œœR—ŒÔU„}V¯ŽÆï ]˜1í¹ ÕÄÿ?\HÇÖ”påoÞãålg,¶‚í¦pm#ãrcP6x‚ªÜÈI4!rÆŽ¿€£^†–>Ãô¬5`to ¥®pvBËIƒŒÔ‘ÉŽ¦†'s[ç© ›!FøSLàïZ+à «%y£ÏëPsoŠŒ¤‡]N9;"”æ¶éC¤„u©D™"¬Àá×-Õ9›¥fT+±b¤‹p*iPI½1Žö9D·'„±ÛÚ /¹ÁëSNާ¥'6w®rXmeǼ78Ån³ïÖ.ÀãzÇ3ަ†*ú᩼w@Î(Èï½WÕÛù€*N«õIEÅ^[ʱµÍV%ºLu¨ä¹\dRq}ôýÍy¯Éú@YÔXG*Ï„Tî_8Þ—N²ÐÖÍs#¨þ•«¼„u4¦®¥² 眹 `NjžlŠ.BÄo½Á[±™À€/¿+LY {W¹7ëYÅWb¹…º7›µ8bzPê1R¡åë­*A·Sƒ¶sYç¨Á&³¿mÿJ D§Ñ[‰sYY@qPï‘[gÐ茅 ŸÄ?Í^¨rÿí^®Ø¨tU–XdÁÍIc¨]iî%·‘CFÏl麨93ÊTíZ·Æ× çÈË8\+–™ÆÍ¹¡¤ºM2 ¾%bym£Ö„õ’b¸R>Û·ØRø³¹.¸õ¹À¹óý½Òkôè))Û#‰Ýqc|k› p1c|› ¶‰z4n –KcÍ sºÙ“˜Œ~Õbâ]^Òæpövë\£È:U_FÓÞUñ~ôV¨·†â;(YäÀDEæf' ½9’ßP|oö±-i<*æµ£Cl±¦«%»?,ññ1îòŸéIï­`Ôò&+ÀIGG³ÿ5ЇëZø·F“ømèðeeåb™éº“ÊÝÀltªV¿¢\h7¦ÆYDиÚNŸLÑ„î;Eh´ê¦Ëfî¹oqß>c¸ò±õUÏNø^Áþ~ ¯,r¦R¤tô5º\61“µ=·ŽB?•ºaô1ê(; âÖB9r½Ž+QK>ã±ÆÅ [vI‚— ·C¹4eµÓJØ;з6)æå5.˜§ÅUo±©TO%=Íתc‰ì$+6ŸÃßÄa3šg 5±Ã.)· ù#P£µ=¾·fŸ•Ô*Ý\Òæ`…ózêÙáœÆOøXŒu¡ÚËrÓ™T•&‚š8Ãsæ‘Æ÷•ئs¾d'ËFkÓ¤k”Ñ#ÃhÎâ–ÞMÊSLcax–ò•j(§p>ô¥¡bp3‘ÙÉæß5˜-ƒÅ;££tÇjnÇô™”¾+}Ùju²HÈ-L¥1B¸¾Y•ŽÍµ4’‚qnJÊùN8FYÌœ- ­Ânнj4,ÍE®dBf’NãíT€a”HqÊOOZN;Ö÷ö¾–:бÊOzwK;gnV¦ša;ãn¸5,nó ½j Ï¥léŠ40·!^èn˜%ªÌ¹·ô©"¶–ÝNôÔ;!?¥Mæ;ˆÉ÷­ q±Ò«%¥qDA¹­±ÍŸ_JȯºúÖë¹V0hýÑg¸Ç•w5ÒDcqVo´¼öGiº*IÌÞ?$}‡sG¬Hò‹NaêNõ¥åÆXF£ »Rñ*œá³âgz¤Hé2k!š÷Ì78ÛѨèPZêÇ`»²Ô‡—z¹Ù²¸hØu5TºŒG; 9NI»]›#)ç]Ž7²€`uÍz¶åê'j/j¾Ù\ xS®U¶Ï¥A¨Yˆ¤æQw´·¸‡61N#ù[¨ÄR?.~’}koUL71)œà1Ý! Á=¨+¸óœ °É§Iu>”¾êÎH²;R§°ðP±Ô7}ÁN¸2ê[.hÈ–ÑHë¿!ÝOèsý*ãÃڌړ>¹¤ˆÚò%ðõ+6Yw~QÕ€vÝO¦Äóî¹[-z8œâ;¥h=<Ão는Þj\;¬|þ›pö÷01Ã/õwÕˆÕ4¾¬Ïk0H¸¿üƒé¨6*ÏøZç–à:Ä~Gñü×V—ZÐãgá›Ö“©ˆ](RêåÏÿÇõ¤·“k\[{ÍlÛÀ ÛÚÀ¤G=Nù%ŽX’NÝ€{Œ¶s[„Öx2Òâàu– A½ÈéP^üa»XŒ\? ZiÙçÉwýÍ"§Ó+#>lîĸ=¿ÕÓÙ«L­ÚN=—HѸgFÑmÐëw±Âò¤ž”>µ£®ƒ«[ë2¤\Úȶ÷ vVÈS¿¯+~™®©ñ¹ªLn/o¤‘Øç%©¿|IÖt+s¦_¯ñ2Fçky«FØÇÙÔßø«µ/âç]Õ#Xþ›{¨Ì­ôVöjCÇsg®ÚÛhZ´i¶¬dÌŸ\²tæ>Ÿj/K¦¨5»HñIE½Iôü‚º²¡³Goö’Ø‘$Š2õ«Õ¾…uq`·ÕÇÕë\ÚÚRŒ<ØÅuo‡ÜHeµm6ì‡OËšÕU!Hóe”ÕŒÑF%ˆ^Üû*¦§¦,[Èö¤Oeá?: WSâ[Y2ñ&3TMJáÉˆŽ¹µMØà¡G©uÀÎÖÖÕ–9M_¡»‹Pµòcq\VâðÂùSŒzSÞâÉí°ŒÄëC3Gß)tg•F©£–õ£å[o´ùD¬÷Ú‚šÜò`sSŽ*‚è€Ì'ðn“$¬õ]ô²–¼$%³ÓØH,¡!ÙH .,W”îiô¶Ñ#’i=ÜaXºSŠšæ‚ód|‡;E°/¿ë^”-²sŠÖK¹p P—» 2©å=kKÑS²ÑòŒ {ÎxA^Þ’Ä yÝÎ$Ô“D®û1ëN,4ø­­~fA–mÆi\®|Ï$”[äd  [kÃ!#ÞœÃi8/ méC%ÈçòÓ#©,VùfíK§µÃAK*$‘ÄX*)8ö¥<ž˜oOî'µ¿Å÷ îôY–?Ø—ON⎅®‰ „Ê–£¢\lRä—>ÔLn¤P…%…Oj M²¿üÒ—{ z‡$]"’Œ•aŒTD“O5xXø±ã–´+Ö AV²Mͺ8ÙÛ”´QkkQ†Q,¾™ØT.æ1„ëëMtn¿Ô<9ÚÜ“.ñ«lþcžƒï·~•L’66îØ.I#c¤6šXÚ­ÉÈL(‚íZ‹ã0åš5>à`Õæ¸AȺµš°@b}ØÿozÂð=޲ZÒĶÕáceåY[ùO¡=zWÓßÓÍ %u<>'¶ÃÌ_øTR ©Ú¶`ŒT×ú|ÚmËÚ\)Y#$GCP«s¤t¦±8<\&,!ÍnB׶ݩ΂ч œIÎÇjÚ9¤ˆå )[Ôajô¬ê°´+4öR3d ç|ŠÖ;)³²O½{ÁÿzÚ]Q”røÜ£Ú†d a=@Æ,žÉq 3°ñÀÞ«ŽL®d'©Í@ÒÉ+sJ幩clìi3z`‚nJgMD›’¶c¥z¤ 1^¢2¯Ü¬`â‹ÉS;öö¬ËjÈzPÎZ&ØE}m†UZVa—sî¼Ä:ÿZ2]]>^öoCŽ”­ŒŠpã½x2Ëä— þ´–¡®ˆîo еÇ#ü"æ¶´wY %He#±§rÚÁÄ1­ÄS"Þı“Žcüª²x±yrkOž–ËèFÆ•ÖE@8#º¥Ôï’Îc²8?ÎÉܼ3¨DØ6®Jø ÂŒÌ1þ¦„‡\¼eå7óéÎkÍ"]Æ•›Üµ)dw÷¹^ÆÔðò>€©Î—ÓópéÎ*áScç^Åw§ðËé> ÷©#´¾´<ö7Äc°j—üt¶»]tKZ{?ïü’É,ÝqZx$†ž QóÉ«iêÿþâV£ ƒ‡.ðRøÄ–EÅRæËÎÓôÊ“ä’1ãiúd~ ½ 21–¯ as5ò²¡U±ga¢À9Öxå>Æ™Zñ-žŽŒ!äV1@ÍW$€ÇMÊIY[,ìtP°Üù«­½¥±fgŠå:¶¢²;}¨Ž"â›AÛ3½Tä•Ý‹&Óè]d9Vé:[éÛyNT·2säæ¶±''…PZ°5 £gõE–…ä1–[M<‘ᕎFôßFâ'ˆª;ŸMéèÀØPjΧ+µQ¨ÂÙÜZä<´ÑÕEµáuå[¸ŒÁó·jOq+ ÇCJô=^[X€™²®qL¯K4ÈÉÑðEfÝé_°ð²î¤u,¥§ŽÊ N§8u¢Kk«1áòˆ#x¬£=qS/ÌG"Hêy€8ïV¸»hÚU¬L¤ÐB^p 0Õ®ˆ oE¤‚Ђ[ ½e´ñ5Öepz›¤!†Ë’JÂðçvQhúl÷óªÄ¤’i®µk¤iò]]#Ìz¨9Å· ¥X7ÊSË»w®yªÏ-åÃÈìNOsCR†ºBò/d=;$¯›q;X>òœÇ«À0G_LS½‰,RAÍŠ2Ž+ž‘2‘ÓM½Äáüñ8>ª)³jKS69EÿŸz麞“¢jpøÖ)á–*£uÂ7Ɇ1 ÿNõ6¬Ë g~SØŠsqÏ 6Ó=v¢a˜n>‰S E¶cÕW­ôËCÌÖî¸õSCj‹*. йØëW*<9æ :oXÔÞ)`"}º§Ð¶aV3P•²ƒ+W7>)=N~õ=½ÄñAÎ*Ç.hìV[EÕ+OàVÌs ÅO£÷¢é©$&í)ÈÔ#'Äòê9‚?vÆÔ Ò¬­˜ß>Ô×PÑçŠÍ¦@?Rî)Õ¤‘0p¦Z™ŸûÅ‚íIŽYw0òY]Nwßjš(‹í½ÑYGëMìaŽA̬ l޳²ÊåeC‘ÓÞˆ„  6ÆŒœ1ŠYp¬­Ìct'pár9Üáb˜I`· ̸$R«Ûfä0ºî:S:üÄÀ>ãÐÓ©´Û}N,sã¥4n¥ t=²<×…I…þ.:hÊ1SÚ¤ zÓoKšÆnfBö¥kkU”S¤!>†q#C‚f‹âÄ@늃+‘S[LQ‡¡­îbÏâ¨Ø×ewV0áÈF=Û›%¬Y&‹ˆäP‘œ QQ{ЂT9•LS"£r18¢U Ñ“\.UïÁB\°|±$P T7SGÉe'Æ2zÕávÂÖ /(1þô`; ”ù:йàbº,BóÕeàþ³¾i5YKYÚùŒyÇŠÝ—íVKK£{Ïw3ŽK•ŠEQ€‘Œ tôÊ(M#ðxŠÔå•òàR»-Z}&YHD°Ë ÁÈèAìw?½g¦ŒÖ=û¹ÀzÔó÷$ðHÚª·É/ö›äî®·¶|–s=Ü B6hÜ P^R=N:uª­Ü“¶¯¦ˆ³ã´H Iç`¹÷Æ?LT°j_:Ü–:EÄÒŸ¥Yò û€2GíZË|œ9,š•äñ\ë,?!–Æ66ì¾Ã5T:{Øl2Oß¶É)sÙ+zQ ¸ŒßÈzŸð’üHžøºõ` òIÊÄt-ßúÕg‘ƒbMqpÓJYÝÉfcÔ“ÔÖM©æÀÓúzSNÆÇä,®‚„…^û@2ƒÓzÇ!4Áldmù0+ÆÑWbØ«‹l»Ô-1œÖâ";Q„ZG³>My$¶'s^»¼••Ý‚Gè+p¥{J-M¹Ù{ÖÆ$äWìr¥Ö>JV ר‘# WªÞ²÷]\§ 8ê~Õˆïgc•ö®ˆŒ\"$¦2¶Î]¢–Úãv†6ÏújáöMæ0òçùMTlõ›˜[<íV]?WŽt¢`}Å'„ž¢šX ÛǺ÷K𜵽ÇÿW²f’‡N_qÐÕ¢å#¸ˆ• UVþI-݆äzUßòO¦vÛ¢hät§k¹RahÉž›Ôq¥Ž«'–9»Çþ*(Òò'ŒŸ Ll{Rrn,®q( gfcF?]ùì‚,Bo< †–àÙI¤¼ ÈS•”ô5ë%‘"¬š|ÖºÄ otÜ +w¦6ÜtX˜À‘OqÖœQQG\ñ%9åkZѲ\Išît8늌j œ:šu­èsXÊVHÊŸµ"{bH#U\54’»².7FöÜ"ážÚLN\S½.å#aË&ÕZD çQÈèF ÅNŽ½Ð¼:ÊF,®Ú†•µbÙQÎAÍõ-"{™JœŽ•|Ðõ9c^V9”N©¦Ûj‘™æëu>½N'f$ïURV:û–®`™Q°¶W•·~¡£=£œ®7 V2‡q_1¨†J'–H’:–ÈÝÌ7 W·(y—$"Ýy°*hP·QDGh3Ο¨¥S¸ó¾ÙjšÚԾ§}iËý¨½7•H柲´‡,qKY#Mš.’ͨKìÁuI›I– ACûR™ìÙ3•5iÔø†ÝÜ… '›U´pCE²I\.æ¦tóÎæÝÍUùamò(7BJ°Ë%”ë…`  &±]ºŸÖ¦æŒdßöZiö¡£¹ùWêFSL%ãK§Þ=Nçþf‹ŸúŠ[ò±D9¤š5ç5§ÍØÁ´jdo^ÕQ§Šgn,¹þ{*d¦‚gïÙsõür’ë|Oª)€ßIM±ŠÇp€Þ Hl-7½¼çoä‹ÌSÒ…¸¾’qÊ_••v)+Øš2 bÍðDL1˜Å›f ?TðjÖ…<;{B‹ýOÜÖ îcGÞ‘ƒ èMH’H7-LbŒ8#˜ÈΚîæM¹€‚„“Äo©n’‚7;ÖØ Ó[cŒü¡ØYý¨6ŒšÐ©iˆ£ë‘Qt ];!ÖVSÖ‰K³ÜÔ¥hTZð]Aчr[W©~O¨¯U?ªèÐäµÁéP´\¢Œžò!Å÷qÔVÕaTRLçt,à¨ØÔ³`3–XÙN¥²¿+mCU²æè‰â¾@QKjJäŠáÓÛW °4&¡f§,‚¨–énj]»µÉf¶Œ‰­äˆ¡Þ°3Ò”Œ¦Ñ¿É1²p¦©žØ°"˜‡$ÒÓIv&Lp-Yš<Ã¥fÒök„¹÷ˆ­¢˜g•ÆEzæÉ£dóFÛƒ]‘¢@E®ïs]à“‚“ì"îÜc?Rúô*OzO§]=¤ÝÌm³ ±Ç|IæGÜêgÛ±KË +íÛ²Q¨ÂzŠV9–N›U¶}5¦BÍ&—I™$Ç!ëCÕ_¸!ª$a7SèÖþ<À‘œSKõäí°¢¸wK‘P¹OÞ‹½µôåÕ-¥ ¹Y©êšg°ì©—Wd‹°¡brf\ž¤U†÷DIhºÒ)-%¶¸PëŒ϶¥²»”Êžx¤m›Ês­Cç‰ÀÈ((¾‘´‹û}MO,ÀJ–[Sv.6 oCëÚ‘tkö½Æð$#%F){ƒí•zÔŶ³3K*JÛ´lq¿·­0ÑôöÑíÏfaÎ üµD–èÞCèÄ1Q’CEÝßß…ðMËý#nÔŽ¢6=¢œ~ÉK¨žæ¬ß#éê¬7A/®Y'DHG3¡GûšE|èÒŸ”ÕdOBÑáOìvþµ¶‹ ¸Óµ NbÓIwêTdêE4Ñlln,<[È|_¹01‚^›Òÿ„kvö°ÿ)Õ.cñsê«2Ep÷ êsºÈ:0õÏqýh‹ÛÔ²µ0óa†Äg¥X¯4¸âÐåº;ü³†F>‡b?·íT VSzâT'*yX{v5¡¦h‰›]쓾˜I-¿´ '¸wrrw4F¤^k—bÒÕ 1êqMÓƒ5 ìÖêÝ ägêóÀšš6‡6¤öä\Jy‘Ò¹];YN_]À÷TÖê±S@] Üt>¥’‰.Ü9ô4:„MÈ-Tþ•myLpæuËc'=è{&›ÆxçzÊ‹}Ì„•œ:L€™Oà—Úðų­bvjIZÎÀr[¬ }ÐQšŽ¦ò©DPtª½ý–«83Am#޹U&‰¦¦”ó:ÃÝzIRïê¹4ùûI[–òÉBŸÌƒRN#á³$&ûMoæ[bÄ`+Ö…i‘OJùÜðHÃÂsAœqb¤f ™«ú¦¡*’¤©Ä³+/”ÕsVÜÕh÷Üò‹¥§kßr2”Ítò9ÜÐÒJç©­œ21 dTM =©¶Í¸!< 1âËOAÑ`Ë!êíûÖIÏjÕ½j;ƒÊÑË1ëZá³Ö·\“ŒšÈU9ß5Y ·[½ëe‘Gj޽šåÕ. n|…a‰"£V9ë[ž•0W²Ù_±©Lµx§5kG–>Èô5HPA#÷(””â˜Å0vŒd€à¯<5 DE­Î+Í{Qñp¬-„¸Æ3º×¨Óµz©è(ìDÉ+“æ¬%Ëçrha·E'ô¯4SŠj¢:‚ÃrRØdlfÉœS–Û5‰Ô‘‘@Á)ïLâ+*`õ§PÊÚ†íîš6FJÛ ´ì†¤–à¹Á¬5¹RHéCI•;šóƒ£fÒ”Ï(n1Þ…+ƒŠ"F p(˜mr”wK >”\eŽÄTPDì2F)­›¹Æ(šwY*Cº€FÇr)ž—:06—hßo±­.,f@1’k6:]Ù0Œõ«„¦7!_TÉnV·ú|¶ò‘̺ŸQLt;¿ ÄS–ßÒ¬ék{§ø7)†Qå>•[º€YLÑôžµ9%ÛÆAUÓj©¦dåÕÒÆÉY 2ž„S)t[}™£>ÕQÐxåmîc=òÕæ ¤»ˆÜ2‘±¾fuÊI©Ç ½ð‹ƒH´KÐ*£ªóC1YPеÌÒAkÖ«w÷+;rJ¼Ù«k£’0Öœ‹,µ#^$qvBR%ŒÑ'A‹Y1( ;Ñö¼7ÈñUùG¡£!‹øk…ñ(;ïYš¹DFñ«fª wôˆ%òiOgf#eó 늪êÒ‹¯Ã}Mu™Í–§¤2BA™FÕȵÛY­®™™HÉÞ™ÓÕ:®1/+ôJ“Q#„˜p*}>9ZÛ•NëM/€{H¦9’h׎“ªu ±«6«o‹4Ç}ét¯-˜˜Ô¼Ç;C»”“L½¹µ¿YíO$Š~® ŽàŽàÕóN¼Ñ¦å’ö¶õäó!>ÝÅRtÛfis˱5dXC¤¶¦M¸Ø©TVÀp›q>«c&Šú~ž­ÊÄ3±êq\öÊ%7Yr¬pEZnáU…”ãqKô]%®¯–5Üó v"=§ïWC$oå˨izSÙi¶S(ÌS Ããúé64¼al HîÏ;>ßPÎÆ«Wž&¡iÖ¼ ³&JÆ)?qÂG-¤"¡†0¬ªiDÅ0awbMý¯Êù ðO[3vvsÇ¥ùBq’À@@>Ô|%ã>8—:}“Ãiùî%¨£õëG|.²´øÇz~ªKÉ`¤ÍpOò.æ¾…âŠZE¬ËÃ3h–öH<(’!‚GLœu õf:W¶Ž‰ ÈEÉ<4~§œ'Ôq9­ xT O…?8%Uõ¤“\¿_«')ûw§1qwÁÊYð¶™ #nQÎ+MOD¾Š?›Õ%Kq'˜,ŽÓ­U®ã†<˜¥'ÐV>¶’ŸUˆ™f/w;€=Ïè`áKÄ|ðû¡xäÒN¼ òOÀϸ®Æ? õ®ÍÅ·ÎXçÉsP=Èÿzïš«%”¦+*G‰öæ r+:»]ZÊÒB’4Rò¥L«L«K©ÕèuFŸvèü‰¿ÜrGâÈë!ª†À‹"¾dÓtGO–=OGž@èy¹FÎ?Nõ|“„´ŠÚCÏ ‰§q ‰Žãßï]ˆ>hüE¡.¡Â<–:Ú‚dÓ¹Rãÿõô·úNDZí\»†¸´]bk]cNd’6):2òH¬:†¿õ­Z¨Ôc5†Ò3¶/o"8sOnÞÅg§}SÁ©§6‘‡Zäw³õú._Ä<¬p–¡òšÝ‹Æ¬|’¨Ê°§:WüÜ ulD¨:•ê>õô™sÀ4ù´+­B2Äs@³7ÿK£Ú¹×p|?•îlObý;}è)+å•ý9×û £þrz£ðò’ù@>×ãï( Xô:Ùl%UYÎâL}&§ŽfŸ0” qaù‡¨ª¡ÄÓ;³+&wȦ¼)ÅsIw¸E“ã'ÒŠ¤|ÔN2r!rm>vFe9<¢ø»SŸN°.«ÕDÓøšàI—v>µzø¥wgıiA#š%Ì±Ž„×š[‹yŒ2¡V¾“öwVž’øÏÍ›z'š š—Ä,ãÛºì:7ÙÜ ·Ô º¶Ù=¨­BÍ-ˆ–6R=+YêBC?½]t)7¶í¦\>N<„×Ði>ÐÅV:ssæ»S@èöqÝ1¹º'*§”]JÙ<ÌqQÝ_´r4LpAïKîo9Á©UI Ûv«b‚Ü-o̯Šacô¾sÚ…žð«[cC‹™ù•ö5’ªc ·™GÚ0SVµ•W˜ Oª@̤â˜Ùêo0÷¢§KK¸ËDFýG¥.ì{¯>Húj—Fý²ª£0db¬¹ ~t§·ºYW,½=hF¶*0Ò$YËBÙ[#l“»Ö§cL'µ$ © ÐFèÎW²Ñpõ°eÎ+Å{Ö2GJ¥rëÕêט“ŠÚ¹e±œVAÅ`àŽµ®kÊ6[äæ¶íQ©È©J›T‚ÌdŒÑ šAý*uö¢£*ƾÊxÛ}è¨Ûš†E©ãeÈD²[)¹z½^£îÔFðžÅk­#Ï®+K‹„q´?JQáNæˆù¤#sXàÐy 1ðí½ÈYh­d9xTQµK œYü)qìhV¾…?.k ªÆþOÜš"Ÿ®Ë¢#Dn˦ß!#¦Í(¾±¸‰‰*iÆ›Ä1ÄG5”.=óÿ4ôêúMô<²é°ö¢eSªbËsôFuäxù.~‹H1Ù&‡Y•ý*õ,ZIc‹x—-§ÆÞKhÎ;òŠSS+†CJ¨ÔvØUnÞK‰Â1ý)啽û`¬/ûQɬ%¸ü(ãþ‘ZÍÅøÂ>°(«¨þÖÛꃒJ‰0ÈÀ÷?áL–×hyæûÑê‰f2Ñó}© ºÝÜßæ½ Ú”¼Ùzƒg¨.»Šáä{­ Võâ”#2)>¥sóldsJÓPÏŸ”ýÔTÍtì¸/éN!‘ò2ÎM))Y ÜдNpØÅYøsX¹°‘UÛš#ÔÒªfi9·;ÑÏ ]ØÕÑ·Í5?ÄFXåÜ¡–œ²­Êî7éÌ}»œ#;ç¥U8{Y¸‰qó°îj×¹¾@Ëhù÷«5(º‘µÛ»/›TR¾Šg5ÎÅý77Ó*+H/ofæ ¹&®À®%\JcˆV¡[I(y/U½…dk*a§Ã×!®¥€øÿt£@Õ¦‚@ '5aÕxHë‘‹ˆâÀo·z;Oá­&Ì«lÄzÕ›ø¥¥• Ž0» Y¸Ø¤ ¸)ef¥y„”m7\±8:M*R%Œó.à⊼¶’k`¡Pbº–×^€ª¨'õ¥ƒF*쎸ÍJ¦b$*CU‘æóUþáÇ”xÍåëRjÍ ™1F¹9Å]àŽ ?G~\sTW+y|U—>j4墳ÜÊJ·ÖNùò„ªáe”(U;ÓþÓ’B›³É°'4»S¿µ±sk–]«m{ífña‰·Q{šÔÄe›6Ü­4l’Zrmfùú/£'þ¨iÑê2ZÇ HÕ€$þõÈ8î.y˜iJ¯r~¦þÕ5Ç qªÉciÄÅv–A‘cÐõÑx+á—éß-g«Z¼ò\ec×”U0h±‰Œ¥Ç³G—®Ú6ŸbçÛù÷ª/ÀM'ˆ5M{T‡­fººù3ò³°Û;×AâbÏç‡Dáç‹Qâ&LÝjG ³d‚‘v$w^ž§ìÞø?ðã‚þ_iü –z&©Ä–e^úgüV'c‚z Ò¾n×?ÂêZÅ®‹ÃŒWä"Ç$ñ®X–;c$ׯÛöâƒ[Ô&šw¢nuÆí£%Äch7³o“Ï’g.ŠøXØáÏp¾3kÿ8TN²ÖøšæE´KÍjôï4ã,Š}2vï]Ò¸ÿƒìN¤Ü#â@›´ƒòp:WÖðËá]X\Ü!K['–iÐy¦›”’~à®QÀZçØqÌEhó+9 Ùd„ˆ¤£í£«%•ô­k¢gbHÜ=-`8âÆØº>?üwAPÀ+\çHîMð°çï\Ãt‰ÿÔ¶‘ N°.ÆnQcõ5fÔ~ü\Óm/4›»ˆ†âKwñ‚þ€æ¾€âˆ—úv–lõ^´µ¶lúsJ^àþÕÍtŸŠz· ñsÙêÓMl\c†Ç§\I&¹__#¥‚1½›—ýÿEïþ¦éMíÝø·ÜGê¾fÔ­5­]{[Ó2IÁ "¬Pð? üm·šÎ{ÅÒ8ÎÞÜ¥µî?ø…I½°ß5ôßÇ} ‚þ é1q%…’ZkRZ VuQ‰Òãï¶kãnâW±ã‚lE,nÊq¶?ö§ºf¡&§Lj!9X>ãú´ÛÈåfµ=6Z_l°b1|ŽÝŠùçZ‹_à®"¼Ñ5Hä´¿ÓçhfCUÁ®•ðëãhU?Ƽ×Z|ÓÄ;²÷«ßø¿øm{«XèŸtÛ"ÑjÊÖ—ÒF¹4`aŽ;•þÕòå¢0ú¶ ×ÒôꈾÐéí‘ÿ7ܵÃßU*Ý2øvJ=qê ïµë/Yd T‘ŠÒ¤AŠ˜ ŠEâˆ*Ç›4T@>ôL|®]KYÜ ™PVc·)Þ˜Æ@W1ÞkƒÖ½Zàתî°Võ±êü ¥Ó-޵m³šÕuù4‡-ò½ï1;` }§|3¿¼QsNM‰?‡t—,OœÒgøw\âkkBõ"ˆy[WzàÎÒ>ð¸ÖõΫ|1 ·Ó÷5Î~é©u­$²¦LXë÷®ÍÅ6˪4pFÞKx–0m©¦¹\ÁU ™Ë­ßÓÙnçkÆ@89>÷ԪÌGªê7‰²'7Ò›_K|à¹5ùa›X´’âNPëž\ üÎÇé^žç¶2+|àž&âø4¸-Ë$CÆ‘Û þ¤WÝòZð¯Á> }Jî4–hÐ?žâr:}‡Aè=ÉÏÊ?òÚ&R¶: &ÞWð9þ9LèèÍS‹É³[Ýsï‹PÒ45WR‚ÆO¬VñY«r@£,ÍÌF{‘ëD|½â8tÝ_‹¸ŸQŽ+y ÚÙN@X„@åÝFryW§åjâüyñž÷‰dŽS¤[>¥©Ê©9fäRp  ìÔߎõÎ Ô´=#‚­.? å å^ƒ$ýÏ1¯–ÿÂÔ8èç iy7q±! ÜÛ댜åi4šF:©­¸o¤à_ó_Qž7áMGCybÔ­µHˆä•QÀæõÈ4«JáÞâ´{‹ äæŽd8®/ð»á…í¶Ÿ%Þ¡~éoœ–$àŸEõ®ÉÁó[èüÐÚä"ùºÖ3P£‡O{㤔ºÇž?ÂÖSSNèÝ&ݶõî–Ý| ŠîÖm=¯åe“<‚Cœùëâ'Áþ Ñ®dŒÝF<&>o¸¯ª¬¸±u\Z"€¹TНüPЦž+‰ˆæB¼ÀÕºv©UI8kÏ>húM6šz¶Á¨³‰>¡|óÃüoyÃ:F“7iéwrmî!—óÂØ8÷ª/Ç?€5ÙñÏÂÍm$²âÅêi×,·Ô#ìzý-Œzšëspjqo ê|=(Yc/nØÝ$^˜ª³ms£ÏÂü!ªÌÆçE³_š\ç # ú//ïZê ³GV™Û]âo ¶×Þ‡Øå%ûKöV*%<‡|_Øxp'±úwàÙ6ø7c«ë<%¯|+ã:ÈØ¢5âG©1NS€<½óéŒçµs¿ÂÏÞj“‹k(cµKÉ,å ŒþTc“]ßã¯Â»>*ø_£kœ/s%µôJY[˜«Ýr;WÏ¿ x‰ì¦Õ5]f;€ö„$fF'.O]ú€)–—Xé š¶–£¤IËÇ‹‹óßðõ_8¬¢:k›‚âØ+™üø9¦|¾Òø“„u_â:mæÂOÃ(ìFOj¥éšÙÔØË,ªÌNpz×Ò¿â/†î¯ø)ø~Þ/˜ðñv®FqÐ~õñäv7Úi.9‘àûWÔþÉÖSOkª_ºV’.y#µÖ{Q‚ ]¹¦Îþat> á¸x£EymZxur+†Í¦Ée~ðºdb0EuÞâÓ Ò,²rIœg³QøNÇRŒq&”ÿò#¾µ´ÒäŽ)ØãßËëÙ¥Öü 憣åwÊ}|¾½—2YÙ8çôÀcHµÛ?”¸òH9†*É}dÂÝ /¼_K+8æðX`ž yÓu,ûÓ†Âêw‡³ƒ‚ƒá¢~e•z:‘ŠÚÆèÚ^ÉèXÑ<=kË|¦=ÔƒXšÓ’úRWcƒEË!Š0GšbÇÌð{€‹v3qÖ²é•Üþ”D ÈòÊ;5C::Ÿ5v'ºt¶ùÚ ‡11( ¨oàŽðs‘Ë ïÏßÖŽfU]†;P3ÉŸ)ß?Ò‹èÌ«#¹váʯÍFä~µˆ<ËGÝpYò¸È椲¼ö²ò¸#û©é›;„úœ¸ÚÇ++ÊO)©ãUPS‘(3[ìÃêZÒÞo+\.éºÇè|Ó¸Õ¨öF#¥=ºÈ» ë+}˱éDGqHííw…Ê{ÜÜ<$3Û:”>àÕ¢KHnb3Jî´·Œ“ŒÐ3Ò–x›Â©ÎhÈKÇJ‘3[|³©úMn±ž˜ªZÛ¨o N\ô­ÅH!oCYðHÁ«ºDªË¶:Tñ>;Ô2 H¹Z“ZZr¦(ød§ÎÛ_òÑ .Ýh–»^±ì¥5êÓŸÞ½]ºæUfùÛç—ÂÉ•ÉÜúšíÿøV×Gµ‹XÔmÁf^uæè­r;E[$}&þ`Ÿ+pbv9Ãc"º‡ÅŽ1*-t->ZÎÞÝcæ}º“_/Õ§’¥ÑÒÀ|.'Ó æºü“ÕM 8¾ãä«|cㄾ˜Ãhàªyv®3=ܳ1f&›q%ᙂd’Mi¥¼«âÎJ'õ5ªÒ І•¬ e¢QA¦Ñ60%Zð œÓ¥µ²e„·¹©U éòëûS?ˆ²iñ-‘yÁÈÍÜãÃ~ôØA®öà~• ±µfɉ‡¸© Ñ ¶æÆriã1·µCÍŠµÎæ>Q#=E?*’ÜW%š?™§^ú¸y ‘ºVêÌi“põÈúëP¶‹¨'H³ö5Sga8*¯ˆ…æÁÁme4‘8!ˆ?z³iÜc«éeZÖòEåì«IayïþÕç&ÌŒ)´f'ÄY 'ÒA; ^Ùv®ÿµ—-¾¢þ*t<ÕÕtŠº^²¡CÄ¥‡Ozøë™ƒdJo¤ëWv²C+]ö5œ©û+ETK¢n×z,•oØ]>¥Åð·k— éÞ#ÔyÝd;‚*ƒ¨_NÒçbMé>•ÇIªÙ'ÅŒzõ©!Õ,ç“‘›|íšEAHý>¥ÐÈTéiïs$nB¼ð-„> úÕÔ~#A€Šw§Ïy«ëwKoyöTSŒ{ú`V>X ý#V¶PXDã=À;â—è¤×2ë–¶*D‰nÌcçÃãúgÚ csª¦_m¹àk~i¦”|ŸÃo?Oò¦ºÔ.ôiLv—öRÎTJsŸNlrçõÅ(Öçµã æ6‹oª[©ç¸ñHõô=è}BÝL!Uˆ@¼žno\ýè~(‘øn[ ‘©ÜÛÆó'æL“ËÍèyyj/¡ •­oÎocÇ7ôÿ ®© SÒ†MÜmÞþÿrã“ݰ¿|ØÅ5¼Õ¤´Ó‘•ÈÏJ]ªÛÖ®ÖÛ|Ã…ÇýT§^¾3L¶072óSZCB_#9Sm(žVF;r»·øqâ”»âx´½JLE1Ç1è+¤ñ¤7Qq%ójJ <ç°Û—·ô¯˜¾êÇIÖ•ƒ•nSÊsÐ×Ò·š´œ{Â_Å@u=:!ÊŽ²Gù_ôèJÊ}¥dšUx’.>‡±ý>å…ûUHí/Vņ=¡§ÐßëÁú%Ü=¥X­Ô׺Lƒ's¥\8~;‹Û²³£n{Šâz³u¥k+”;`ï]ÂÚ{Í+M‡SYVOscÐVzªº¥• .uï€J¥ÖÌÆˆevãÚü¯¥¿Ãfem¬ÝßO—ßÙ?ØWAÿ|!®kÂÒâ)™´Ô%ålùcsŸÒ¾<øoñÞNã(¡¹¹ða¹ÄLÄà)æOî?bkî¾;â»Eø#ªqS۵ݫé¾$±ÆØnF!_à}ñX_µÔÚŽ›­SÕÈ/Õ³[õ°Ç¨¾óB©eD2DüXn¿²ø×àþ“uÇ¿âM‹O·æÒ´™LŒÌ<«§îkìðw„mïçâ «÷¸DbÒ5Ô™Vî+ÿ†Òˆ×x‹‚µkmOMÔ\:06rŒæc>aêCŠ3â÷j:‘¦ð\“ºÜê’™î†wóaWõ ŸÒ—ý¢ë:Óh¨^XÖ°‹YÖ±s‹È#?‡7oô¦ü5¼dß<› {‹[ëä¯#_Ò¯/Ú85k{ˆË @Tt QvM$Óɪ3´žUU$ž€W)²¸¶´¸FŒŒ;Wyø#ÔîµNEYb,ög'Íú?½.f[5C!c$ö$Ørl;•ôb›þˆÔØ¿óNø7á2h¬u^#Ôd[©O8µƒ†fcÔûÞ­ÚŽŸÃ·–§ÝÙø°L¥¼Ø=Áìj ÕŤfQ(-ÔŒîjžúÜ·’ Yö8ÍY©×PP<ÅINÌcs›¹ÇÜ“a@=ÏZ+57üL¯8âØ··ûU‹î ‹áμ·ÚI4Öü¬ßü‡¨Vô?Þ©ß à/Vn8GƒUº»ÎXåC ÷ý±WÏŠ··:çþ%ŽT!-¬ÖîÙ±ôÌ’(}òWõ®k} ivºÎ`B³Hïù0OüRªŠ2fÕBHkÿ¶ù¸¸É]ø¾V¶iõZ*&7•#µ›¸|e\ âÍâÍ XYÿ ¼¹ãÞF8Ü~Ùª‡ÇëK†ü?¥Ã^¾Gœθ«wü(¸³×muþ¾·»µŒ¬’A Ûúf¹øÂø§eq|3¾¶çÕ4Ë…¸ñ×°#aûQÓcêqÓÒx£Épî<Î|–R’*­4™6ñõ¿"߈\£Eø…,ò:ª‹›v<¬² òoJCñcàµ×\ñÏ(’¥î-Ôn‡¹›N¹V }³]£á†£.¯pä‹Ï¨UÁîï[×Í.“ ª¥6·#±×Ì«é™#CZ-žWçÜw’A~É‚0Ø*k¤p†³üIÿ^K‘wH˜÷8ØzãÇZð_Ä+ë[L^RÊmêµÃ2¶¿¦ALŸ5.=y…}.J–ê4ͨ‹˜ºÏkÂhÎÞ@&þV¿à‡m_å/®´[蔼²©>ƈéW:]ÈšØ+l ¾iÄIã_ˆšˆƒ ¸`qëšKdžÂìJ–Aúæ·ÔµGçäíϽ“ºWu¡‰ïÆàÓõ6º°h–vQ_«FÍÊEO6k5Ô¾ÀÝŽÍUÝRxÛÄ •µÕû»´±7~Æ>â-ðºãÉ[S¤I,¥ð»²° úÆÝ:ô¬¾‹q"òÈnÍØýê½kÄ—P8V‘ˆ½jÑ¥ñ w “”ûÒèÄÁÞcäRZªÈÎRét[Ør¯0”¾îËÃð¤$v+WË}[…•UÓÓ¸Õ,ðÛ]ÆyQOB1޲F‚ÉZ•üt°»ú\vê9D…Œm¹¡&E¸Â¸C·ÒÝÅt­KGˆdÆý6¤3è`“äK«™{ÁªF𠬹Ýͼön Sô°èhyWÅüH¶uµ~¸Ð¡FŒ”n ŠAÂw05©çúŠõ1»ÂxN©µ8Ÿk›]I|E(Û0¯ N3FM¥Ý“;Ö„’Úu8x˜×jp"àßÕ<Š¡“ _*HîåCÔlz‹8ÚS‡¦¶ƒÐŠë'-î¦C f×”T±MlÿTt¶9OCD"sn´Dm7j ¢‘¤]‰´IfýF(Èì´÷µ(€•ëF$£ÖœÁmâ 4Œp6¦G°q´ÀV­Ãö­ôÜ-³ZØMþ£E:šŽ@ÌÞV[‡òL¦´máGçí[dü’ÞµùËÈ·V$PQµ™³ñ¸(•v<3ûW¨¯ãw#bµê¦ÏTfú ®÷Ê×nàQ°¼äoºà7õ£ø‡«£ëžm—Ê?J÷ ™Q»×ï›,¾%ÄŽ˜äÿsT­fòm[Pšç$«1"¾sGHe¨ðÆõþ“dMªÔLŸÚÁo©7B=Ük1”Ä$“·7A^:­ëŸ¬èW—N”ùˆ©áÓ[«.Õ¡kZÎBеð´g+Xï¯o)÷ål3¯Ô‘þ«Q²Çn»c4 ÷r„«Ã)ÃT…¥ÃF³ªAþ,1äzmQŽ#Ó“g€±ªäÑ\Ê þô#Ø^uä'íVI@Ì¯ŽŠ|îüUÚ%ÐÃC¥0]WCºLÇ:çßjæmor‡Í~Õ´m4MœT£¢`ð’lŠf› 6÷]%dÓœàJ¿¡¢a°¶˜æ9Çï\à‡u0?z–Þúþ r\H¸÷¨»GpuØü*$ÐÝ{Ç"é/¥Þªþ )=íÜd™­Éý)¿ë6øå¹'»Ók_ˆWk…½´Šeï‘]’–ª!áß‚­ôU°ü KKiNê+É£\Z!â/·QOà×xoW\Ij`÷V¢ÒÞâ[Jì¾Ø5uTŒx{©CY+×´´úþê’Ò]i÷$D£)§šwYܰ[™>Zçü„ÿµª.Ÿ7þ§õdÁýéšUŒ›kèÁôcP¯c-Þ>k•}*±yZAó]Ÿá×ÜðÝÚ‹¿5¼ªWÄ_2•;E[ì ÒSY]oCÖ­£NVÆdÞ<Žày»úWÏZT­ƒåî2‡¨GÐÕŠ õQ‚Ö‘¿¹Lj¾ . Û½¯‘cÆG¨+úPÔ™édüþö<ݵ~,‚ÅL¶7ZoÍ…gÀr¶>°9Kž˜õ®3ÄÂÜÜ^£Ï=åÃ’þèãõ+“×ÜŸÓ4v–u7ÁÿÊÛŽäòÿ¾iïð®Ô/jªó $~æš³ìÜ4Œê0nó°¹ü;z^Þ…[6¡$_Ô—žöñ ?™\ž/ÃA§©gmšb:où¨/8u­-šè2k«ÏÁZ~3¦ÞÃàŽ€´ ÿ Ëü.ñ€‰'Æ:hŒWàö¿*º}r(ÜÓkž?UÇ,ï$´¾IÔã”×}øY¯ÜC,wÉ̲)ŽE'fSÔá:…·r¸éVNâ;Íe`ÙPà Öín˜ù¢7ù‚³í Ô鋆Jé\y£|ž´—öYHyÂú{WDàž/´Ô—øeÔ b5Œ¡í·Z[ÅZzk&™«Ù®Rx¹‰ù»Šåúòßhz¬7–ŽñPvõŒÑôæëmè8Ùͽ¨ÿK5¡U6fˆää=qþ•³âˆÓ5ÖLÙWÜàâ~ qÃ^.žIÓO…­Ï›Ïòò©Pz{šüú×xŽóW³‰¯@rÉŒú]Ÿü*qV«À¼?ÆöŸ+FÎ,íÆ^gb¢S϶¿g]_ödBüM˜XîáÁÀ`­^“Vi¥ê à‚<Áô—Ào‡º·Âÿ 8sYþ#¥Þ·§•Õw<³Gì@9é‘ÚºGÅká‡ÿñ GH׬Â5½ë&QÈc˜Øñ±ê;×3øqª\ñGøŒÑõ]vˆ=³\Ü*c È#Ó$U¿¸kPøƒÅ÷Zdx2Ûæ2ê˜çåêÆ¾;©Å¿XŽ®ºM¯4— 5Ã$YÝ{[Ôv_Aû?Tøh‡M›¼dþGî@XEkx¯7¶Òž¸YÈõÒº§À-qx{‹çáÝQü8µ¨|˜¼`rŸ¿˜}ȯ—¯¸k[àÝ_çÄO3Aä𢓘&v%±ß»}ö_üK¬iiÒ<Ò,à† OXŒzcýªuSôÏmøâø7PHúú/¹i’Õ}¦ÑäƒQnבœdÕÞ÷ vµì¾™â›½V×\¸²›˜Ü€ 5¦‹óZ•âCDä°ª&‰þ 5ÛŸ,áû dÁÓ”0Ü;]ÜŠ°h?â J,¶š ç;çš[©ügϰ ÎVSÑK#¦;|¶›ýÃý@KeÓµ"è6\®6ûæÇék®™Æz¾‘Úu®{m ò^¡`(ƒ*X{¿.>ƾ6øÓÄ’k—×"Ú4a²¢a|C+} àoú×tâûé.,/¸»V»’g³µšòw;’¡*¨þ¸Á> â‹mx§Z±{mÂãæ„mùØõ;° ©æ‚IdÔ#Œ.sŽ»¹6óô Šiéô )º¥í àºáÎôkAç’l=»7À½#ˆ8f⮞V„Hm 1;”§úŒ×Îÿãm:çâ¾§5¿!•|8äaüÊqý+ìN9â8xKGšÛ…í£¹Ô‘äüLÕœ’Ìçù·ØWÄ|e—šÎ³q{sv÷··Rñœ’w"®û,~'Pv¡7„o~÷ö÷_ Õ«â m$Y!×6àz{ª? éß:ÈÛ¢œ–m€®ÓÁ“i¼-f³Z°’æpOˆ{(늗á×Á»k2}C‰o¥°²µ Ò9œœ+œüRø›ÀºÝõ— ÜË”òÚþhpñ8eþ†«‘ý9û6Òé`<ŽÞS×Ú•O+íg}ë'©i¬x%¡Z¤š;¨ÄÉÐõ†‘W;Å ÷ËÉ̬ žÝˆ£ZheX[#¸î),²;vyX÷Óº[²ŠD‰— àGg”`Ñ "gê¨gu+€j$—k.0—ÝèÐI™mz”ÿŠW&“o(ådýqÒœ=ÃÄr PÓJ³’ñí'§­A¦@|)„2ÊÎÿT†ë†Ô…·Z].…Ëÿ´*Ⱥ˜Bb•~àö¬½Õ» € úÔÕ¶æ&ñUÎ0îUE´p2y­àÓNvÚ¬­%£u5èÒÑŽv©Á],/Ê ×ÊÁRoáNË·ZKˆIÊ“Wl­¤V­¤ÒцϚӳR`=ЮÔŽU–Eü¦µ2°ê [¦ÐÁÜ4+è!¶ÂÕgV 6rà­Œ|ʸ.H©ào/ ÈFTPrpõÜ{„'íWÇ«Æq¹^ʨÁCæ3¾+Õ“¤Þƒ ¿jõñðú"ºÌÿ²¶q£™aÿ‡,dV‘5Ü‹ëü”—MÓžéÆÊ:Ö4Í6kÇæs…ÎY?{‹[<lÜÖ.¶™(òãÉõH ÌèÅ—“ëüá@ö¶ðlØÚ‚¹™!ZKpò¶XгHTniÝ3_—« …×ñ<넆8¡ÚÅ[èzÞIW;וÆ2)‰¬)»74a%¥ÄcmÅÒ]Fç…7ñd½DÁ¤>h³úUS4”«ã˜j3®ßqRUù–±7ÜT¦ÉߤU©Ò¤m¹¨4H8D5ОTÖÚ½€?‰§!ðqGxü?8ÏËY¹süÕ»hvS§àÜ¡ôÞ’›i{©¬qÈ,>Õa ÿj±ÔÎvcþi™á˨›šÓ˜ØV×ÈèäU~-Bú&•‡ëM-5ëäÆ\Ÿ½q‘C.²‡F¤®³“Éf3ÃÉulO¾)å…©bRB¹ìEXl¸‰¥^YâFϨ¯^ü½ÀÈ·QŸACÍF× ²—I$¹¶ö*¯ Oe.Hû°éŠŸÀº¸r‡lç¥ðF¤þ‘cl$Y9ª`cà}î¡4?-oÉY˜ÝÚœÇÍ$Gpãz.Þc:‚ù¡4mRàF0ª9}E<]VÎïIks·: V·RŽ•—ý  Ò‚&oÝû!—W¸±`¹#Ò¬Z_ÙÅÙÞl.Æàú©ë1G§FeVæÏAéTÛ­JW›Ÿ˜ç4R¯’kž¯Mf¢z€X+éËå£ÃFÛ£„Pš-’\@ʘ+jmuoò× ÌŒ2¤ö5®Še¶ÔïÉš[¨BýJ!aw.í‘°‘ø®ÛðÓVÖgáöÓcå˜Ù6|7òÓŽ6°Óu]&ÚêïHX¦Œ•fˆãúUk†õIxZæ jÄ«‘F†»¾ø}ñ‡…´Z¬zªÝ#¹ÿ%ÛÙ‡OÖ‘A E Iñ³4Øž[{‹óp3o[ó}E΢«A‡a9-¾<îmëb¸$º-”ðˆ ¸eem•ǯ½} ð£áÍìî–ùü{èd˜®à(VåÏêMr>,ø}Åœ%/>£¦³[¾ñÜÂyâqêmŠé_áë㥷 j'…¸ 1Óï—ååp2BžŒ3ù”ൠö­Ó×i†m)Â@Ò@7¸Vïéæµ:ec'nøÞÓß•ÙÂìŸ/ç¸c·´šú†ÛÚ¾‡m^ëJ×xŽì"Ç%¤¥ØÉ!8÷¯–¯uýWáË­éŒ3ƒ#D|·¶ ¸õH ×JãN>˜[iœcg+\Øj‘¤ìGæ*pè}Áÿjø'Ú6mFµµ` ’ÆÖv䨿û}_ì–±2±µ÷w³ˆöÕO^ŸVžæÕæ&ò8r¥T!Á,Ø$}XæÆù8ªmïè¶w/udycl¹¬¼OÇ:>·o4ºdï匢³//†_Ö¸å·É©j;3âNíþ^w'ÛÖ‚§…µ¾²ì·oç}×QÿÈpè12ƒFŽ9]k—\‘ž8µÏ7Í€°çŽ·uñ3‡mþVínäŽ+ëq"1ˆã9*Ãî¾ü(áÛ=šMfÞæAmf9Ù¥<¥ÏeQÿ4¿á·øSÖ8›‡%±ø‹ è¶q·ar|Â;`2”;`܂۽vþà+?„vppÏiW:‚JA{¹Wvý¶•Õ+èac £y2^Ý­o~/l[Í+‡ín­¨ØNÑnÀî¿Ô‘>l–Ûð¾»Åú§ð‰­´‚†;Æß „`"þ„Ÿ¾=*Ó¬^éü ÚWÙD²4|¥a-ÿ*¨÷ªïÄß/]/è1 \¨ù¹cKLŽ™üÏíÐJäŸ~6Ù è:$E8ÖâöîFËÎqŸRiU.[¨º;ÇfcÿÜO?nrµõìlrÈ÷ø­oROÏ©]¦ ŽÑtü#ÅIô – nf‘é–pu˜GxI ãjI9 9¬Û_2°VjHØ×ÞØTÕé²;Æ]tѬâ óÒéÖ8_*¢ñ¨ õ¡ç‰ õªjƒ!fð‘mƒ•¥Ä¾&:µ4W[%ñAÉ4šFðö]«Æô˜Â¹¬eEL“LŽJàù¥ÝÙ2šñ¯Ñ•Žr*¯{nc•†:UƒO íå5³eážr:Ö‰‘‰ašW²ý$³G湕Zœ¯¸ïV¸lVÞ1$ƒU]67†í'!‚*ÿɳd&´ÇŒƒÏ½:ÑbÍså«#®i¿ RÚÝ4ìyù€ùO©_ÕtNâ;ÝoHƒ‚¸Æ#³¦©O¹o¦ò¾ ?Χ%}A#²ƒwàî)±Ð,.xWŒ ž~½“œÏóK§OÓÅQèvæ^øõÁîøTDñá庇!Œn<Ê}Aê 4Òþ4Gk0—XÞ,rÍ*§3ÿî§SÿXßÔ´£]Ð_WLçÂ˵Þ+4ÚÎæíî òù…¡Ðõz†J×I`F/Øúëþì»î¥ðNðéïÅ«YñŽu»±”7(ÿZ}HqÔDüàmWPø…e,Úi–(dñYˆÊ¨äžÕÏx ››³Äߏ߸uä»È–S†óùe¾œûŒ{QüEñ“ã§ ´Ú^¥g§éÒ¸ÃÏkh"2\®ÕñÚšZé„”Œ‘¥Ä[Åv8{Ž»mìÔ¨jèƒÛPK˜Fv¸ènúýëì/‰.ø%$ƒNK ™ s+¶¯›¸ãüqüJð¥Òô +F´ù~dBÎËî1ûŠàñv¥1—U½–rÛîûYÉy«LV¤»ð ¯ic¨hÀ5lkÈîxü×+µÊ™ž~ÛGÑYãâ^"â[©uNiî.nÉ)2™îIÁ«Ç‡ï‹î!Ž8IÐÃI,¤ª‘Ýw?ÿv¦?þËy«[I¨Æ$bÀ¬'¿¹ôÑ¿Äø¶áï…ö?ø‚î"¹¿·Â•à#ÃGÇAޏ£jµ:£RÝ?D‰¦CýÖ¸hó·Äý*ŠÖÒ7©Rââxh9qúñê{%_m¸Oü5pUüZðºâ ˆZÞÞBAdÈÁjù'EÒ¬¸ƒO‘8†.{û‚ÏorÝUóõ™¸£ˆ~$k­qN¡4êíÎC¶ÀzRÞ!ã29ZßIT•áò¡Søqã¦?˜ûôûÖÓDÑfÓáteÅó¼‡=þVà_ËœwXŠúÚÍIý&ÍÛÃ|…ÿ??%X¼¶°á½Míu½;ŸÃ“—Š´žê};æ—ÜYZK¨ÿÑgi-ÄŠ1üHŽz7¯ÞÕµøºÙooNu+O)oþHû~ÕY€ÍerfNeÉ9÷­”¾Aw›>Ö#±öýÓJq#ØKÜD€YÃûO·¿ ý›Ž4¦ã])5¥^kÝ5BÌ{ɯÿSýµS4Ív>g†2L¼Œ}*ï¢ñéì² ®9dSЩ؃\ûâ É¥Þÿ±ÌšuÙæ‰Æü„þSî)–›+)™ð.àü¿¨úrÚEœïøêŒ3û2ß¡Èôº ùf{†‘›*ûB /žÜEø ¨ê=*M7UX#·À´'`ÃêJ&êÖH ÜBâH_uuèi˜™ÔÇiçóZÁ3©?§÷yòŸh:”BÕmݰcš´Ô,å†lÈ VÝ[±¤–ìàóB9[ºö5`Òµh/Sä®Èôåoö­F¨6V†¸å ¯¥”Î̃ÈK&‰“Ì:Plq¸«UÖbã<ñÿQH5 S¿(ÚšUþ=ÌZ†Lʸđ›¥ÑKŒœýéµÂH¡IýéP2¢mÛZQ †7YTµÊKûUܨØúR;ˆLGjpùˆ‘J¥~m˜fÔØÒn•d„½Nýh»K™-Ÿ~Ǩ¨Z5æÈÚ‰··ñ ‘¿Òi\sÀ¥3‘”Å%‘œ©þ•*\`àèR[G%—n„*U¢XÎU·±oÌ;¶r(/;5 rŒwªúHÀìhëIFÕNdnÏ nŽ7 ð‹–Ôr•`OPi=î–‰™%G\ufH\Çä`ÞÆ e‰_ÆPûô5dnþÓ•Øžæ º©I`f\++Øê("¹³~Yö«…΃ÀñtùT7xØãö4²HïmI†î@ü²-^èâ“&í>|Ý0Чv¿§'Šu|]ëwŒÔæ‹kK[*­C‘Rÿ—12È=]<¯w·íÊ®YÚÇ_„¨‚;VC‘ö¢g²º‡êŒþ¢  Ê<ÈE æ:7X‚ñÌ×dá*czõ{Ã×ö¯Ww•~ñèžIáÇÛ5’» V®á€$Ô,þô#d!­yºÐÒ)S‘E¶Õ £j9¬Â%˜6X‚äý¨–„H<¸ ÒÃ×j&ÞåâÆh†cuñwj†kFBHÍCá°ïO£hn—ÐóiåX•éQ’“û˜¼É­‡`¥¨§½°èkajGQ[ˆÙEz8Ñm!ËpH5•9ÍhCt5ì”7 ªämÑbæHW+A\ê—O•ç VË)èÕ“h“nZënì5râk®àƒIdb›4ãJ½(ÁXìiyÓæ]ÔdVð¤‘0Ê‘GÀ_h!s@ð«T°$ñs.:R[¨šȦzeÖT+«]RÜ2–QMdh–=Áö‡µk~aš·ÂY9¥2Ã:¶@À÷8¯[²[Ê%™ù½–“Ô½®¦R švHå:¹À“ÖÚRTê­DÎp«Ú¶±æÜmY©¨‹³Ý(’˜†n<£´¹e‚p¬Z²ßÚ›Ë>aÈéAØ[B®²°L¢½X¦d/åþ”m=S©ÇMÁgæ•ÂPæ …\´¶äfce¤÷67h•ê(ÞæG‹Ú³òüÙ¯¡ið‰©šGt}CSnBe{¤ë²*_Æ ¹; WlŸzGÄú-Ö™vXyãêzV‰láÁBAÎã^hŒVº”~"ÆOP)^«I-$DÇ–ù~É(§–÷‡#»d»J¿0“¨î*Ãa}5¥ÂßYœ¡Ù‡¨î -ŠÓJšcòÒ€­¾=)¥¥œvÙå |ö¹±ÔÜÛÅåæ•Vº7m“Ø«e„ðß@d³›•Žå3ÐÒ}^Ò š2“/þâ ÷ìkH#Ž?ŵ”p{Ö—Z–¥A+(ÿP â¿DÆßÊME²]‡ïÂÃAWBçFÕŸ|Bó˜ƒô°úO±Úºƒñ»ãwÃð4mqí8“I?ÃõûTºP?Ðî9×î­\Äñ ÐÍø–1ûUŸFø¡%´ÆóN·»¶!¹Q"¶z~•ˆÔ¨Ÿ9-ž&Èß#kgr>ñN“ÇÜ<®/ô?¸ú®±gñÇá~¼«üká¥ær~¦°·Žê}ƒr=·«‡|'Uù•¸×Yg‘l£·©-\f÷Œx@ZüÂð“[·\Ú]3öpÕJÕøçF¸Ïþ_R :+L§ûHcû4ʳhØöÿ+þw?Š"ŸWÔë0ȶúØÉÖ]×ÄåÆŸ§Ï |7±]PÅ-ʹiÜwý¿Jùáæº¾š]cUºòæ{‰ÛûŸaH¯¸Òfþ¤!~ÏpÅñúl*»¨^kzì¢KÙä.ʧdQì´š^ƒž bh`<“—ç©ú# Óê&y’¡Ö'’lO°ùÊ´k\u=ägKÒ%’;N!Ù¥ÿ…öýë}1$ùnlÍH4­-V@glïÒ­_Cg¤j €V¯lpÂ"€”ÞH¡§„CLß_rµSm þp yÑè~ô]·=ä¢-cC´kvؘS•—ÜRY¯® °.Í^{ym%Q"ã5[©bx¼£Åî ¡$£§—ÿh»üîAÖ*ã6‹mt‚ãI¹ ƒ!XàjÞÊ2–òèÚݱ{Y¶!‡Cê*µg¬›)ü Ù¾¥ÏJ±®¡q=¿–A,$mȤÕ-‘žŽÇ¿ßæ“Ï Ñ ®7ygÍsž1áKžºçˆ™¬¥9Š@;zzU¦êÓØ7×n uƒ%½í«Ø_Æ$‚A‚§·¸®mÄü1>…rd‹2Z¹Ìn?±¦´:€ª ›óÿ+A§jB°|5WÍØùÿŸökXìu8¼k&ê?Ì*+½5ÜsÄyd^Œ?±ªÍ¤óÛJ³A##/B [ô­vÓQÄ¥a¸;ü¯÷£ž%¥ñ°Ü~!$rÒìËû…#ˆ.m$ºš¶É§×]¦§ÌXº’FJŠçEŽå|9Ó”ãÊâ•cUáéÇ)b™Øö5¦Ò5ÖÈÞœ¹EÑÔ²_Úï.ÅA¨iR@ä:410p¸«¦­a¬ †ú0Ž{Ön¸QàCyâE×#¨§*Ô„áÚ¶½Û%ð»ðU¼"š@y²}jÃ}hìÇm©\Ö¤Ò-J'nöUTÎl‚åÍ2Ò­ŒÎ}èe¶?H5bÑtæ‚#<‹ÛjK¤tõÈr”ÕÌÖGŽTz¢”WÌ—ÛFUŒ/ô?ô4ÊòRy‰;P ÎâÔ\Ã5ڈ˼šdˆHå"½oÊ9ÇzscqÜ"6ÝÐ~â·šÞ2¹ÆÔˆ8`ª¾!Àìro0<68¨~rP¼²î=Æhø‘|"Ø}>ô=ŶÄÖ„ tfÅ[ ÛN×!ÃÞ9ïƒÒ¶þ+<ƒsp;8È­S¸­ç³7–ÁFA©üAa±DºVg…°i7C˜+@çºtýªK].àom"L?ÒpjRÞ23ÒŠ±ºš'Q´²–HfŽM¾}ùS_4ð³\vaKãù‘síN®uyeÇ£ Ð&’G)…½TíûQuURZ÷÷P…Å¢îrÁŒöþ•ê7øDÿ’d+Øçêªÿú":ìÿ²[#ÎECãÒ£2—í^_µpGdè6Ã*`ý{TO–Ú¶Þ½Š¼6ÁGuŠÐ ïŠñJ-xúT‚èyºÂHb;6 ÞœØ4P2I­UðjæIµIÌOcð¦!µÛÊ)m¬ÅHÁ§Ì ÑMØár©q0ä $·*~š‚HÏ¥5”ƒ±#‘ž‚‡|aÇq²ºB„H2h„¶éƒR+/L •]FäQ0S´f锌•‰×¡­Õ\õ­ÌˆvQÖ6üþbY‚è³ `¹Ré¶E¼îVšK‹GŽS‘ÜšK†Aá À•gåÜõ£)ä.pºÿ˜Ø%WúPšLG&äì)eÍŠÛ·*¯1Iß4õÉŽQ#gê #¶”¶gôå9¤Z”{d³B_[#â~xJºËnîQU”d1Dé‘ÁjÚ@_0F„/ROSFZÃÈ£"€Œ—%“Nv{¢ä¹ðÓËéI¯on£”)å4Ñ¡2)íK®mÄ߅͸ª§{7„¦éß!4áÛ$õ«QÓò ªf•höòÏJ¼i×ÊȪì }ìåK =9²¦C »™ÂÖÞÀ´ òýéoiæ9W”~Z¹iË ’@Á¥\clc™F2¤lkG_JÊŠb½^¥@o¢ç+<öòV#iÑxdQ ÇÛ4ŠêÐsz+)TsÄÛ×ʵ=4FòJ…u4r¶Ïå_,äŒHÁ$q°­®-î{dƒ½Tà¼(V†óIâIíÜEpC§¾õ•©¦‘„¹¹YÙ¨äŽïfPš…”ÝJu¥­i2•aŠº^j¶7®<1IîõHˆå‹ŸÒ ,Z¥OS1v m‹ùðÝ( ‹•š)blÓÄV–ı’=+Ö8¯ž(‚ŽÛTà†[¸`˜S²}ĆØÒ¬ÔóQ÷­•ÛÆZQ%ÕÌ»—5¢¬¤åœÔE,Ž9r A!ËžŸÅmgÌg˜Ð÷S@‡t$×´Ò«g֘˟:ä°æô§Æ`ˆQ¬&Ý÷)m®©$˜"³Š•µ!*2ß ÷ÆàÔmkX8Ú‘j—ÊX¤coj ³¬x\dL¨„}P„²%ÑebTœ«âŸpÞ³Ô»œ«yz×\ã­+¨LÈ£˜wÅrÛ‹a!†ùÅj©§§w+k¥jB¶ç|ÝÓÞâÙì¹m¯—Ç·éƒÕ~ƯÐC¦ë6œð2ˤõZärãÒšéZíæ:Ël䝿SÐÕiû†øN×+ê´Î°êS®ü ´êœ-qc'o–‹9u6™¯^Øo&^.„i¦q]ޱm„!$#žô«S³’3@ž^¥}*Ú]n¦ôɳQÖÊA¦«õL5-R{¦¨xÿâª×:DªO—ÓKâ,f$.wbk×—æ¬@çêê;ý½é©—T!Œ"¼éå§ðËòö?¡UM‡ÞâPÒ …6Ô£KX¼ñRßÜ>™Ž0Cµ\¹Õe™Écšlâý.Ÿ ÏœòR×2yäÞ~T-Ì2» ¨V·uÜDC-†*]ÂýEeåšmĹ Œ aš{Y„‰œŽÔö ¥»AÊ@'¨¡pJ;W¾I”óBØ>Æ¡qŒÙÊ©^ÙyÁR\ÅäEÎLe®Ù©ÓÌzAš–ébÁÍJ†5À$rŒëßýêê¡Ó¹}+˜ƒÔ³Ék(™_;2ÿ½[4ž'kö éTæ9y cœŒVêX²rç$b“Í gäeWKHñ ù«ÝÆü@üŃÇ}º#LÕ.xzO ‚®:ƒK´=^M&19þ”ñïtír>[¨Á=¤]˜Sm?vŽC¤É?xYÙ,g§0Ýššöâˉ#2FV+¬nFªuý”Ö“4sDQ‡b)ìÚ%ÕŸã[IãD7 GÜV§U‚xþ[S‡ÄQ°Ì´Òy¨7ªÓŸç(¶D{©üMòî?žJ©,Y$Z"œúS»­5[/e(•};Ò¹#š&åxˆ?jG4*@ìèÙ”ìhØ®]Fô –ÇÒjXƒ³oµ iL˜ ©)¬3,»Í¶L3)Tô¥fh|¨7¬Itãy$þµgücX.yüN…ÄøM“VÒtÓÿ¸[ì(i4+Cº+ÔPk©" ßqSëèÕC©ä! Á• à”4Ú!MÖ7+A4$,«úÓµÔ™ŽVd’Þa†P R_Uñ7 öTJÜH.’$÷q¶VEp?™hÁrn#ÃZÆ[Ú¥›OF£ Ц âl¨4Ò‹S ;dD #—#•£3‚A°5êaòòeÞ½O(Ü/¼ýÁs­n߉\Ý®Kt¨Ìîw¨sކ¤·¶’àùF­'ê—-Ž.VêäïSÅÓ"SE½¿Ô9ÏÞˆ,“ =ªæ2«ëò…è4ÉHÌ®õ£bµµŒy›˜ÐFvêIýë>>:!®hì î£ù)¢ÉCÊF÷a¶RÖ¸vÛ;VÊÄt¯:kà äbd—=©•Ë æ–ÆÍšel¼ã};·(Æ+s’*6ì(ÁjHïXkfjaÓ(Ö½¡/–„}Îâ˜Ëm#v­#JìsŽŒÂè(Ô“µ1µ‰‰éRÁ¦*œ»¨ûšim œTÃô¦TT®'(¨¸á ±0Lb·ŽÕÜî(æžÁ2Ɖ¶¸²;iÏÇ].óÀA~Xð)¸™ŽÕe¸h@Û ¤LÙÚ‡©ÓÄ¢À¡ÙHdI#µ`~š+Â-1½3ðíÀÆÕ§‡)ºM3§ÁATÑžRQ†NFÑ6k<È7§öTw2 SjZ]¥¯ši=3  tq¿º ³ZA•HÉu¡eµxÎâ¬÷Æ<ÖðùOz Hƒ®$JzâAå;Š9ÈÂSjòC"°8Þº7 êΑ©'#¸ª"Ú9}†ÔÿJ¹ð7&ƒ‚CK0iCê¶Fú«žµ§E¬Û™m±â(Î=j‡um%¼¨CÆ®úsNJ„ƒCkV¶÷ÛÊdþaZºA+­å+ÿ íƒ-ü•6¤‰ÁF ûS8§Žëk¨A?Ì:зZlö­Î:ÿ0©l”“ÌÔ‚JpããDNÖLÝÁOu¦[´$Å&3ëIÑŽ\b™j÷°á ôª¤÷×<ÄsšI¨RNÉ.ôôsØ›á4ò(K»ß2‰@™¥Ìkh£sÚ‡†žçÅ”tTá†ïÊ^îüç$ç5<7/F¢eÓäææ kQdËùH« 3šH²›‹V÷…ð²ŒÔòG‘Ì£j^"’3Ò‚BW¢ªÌ7.™ ÍPº½h¸©îPœTpFÎÃ#j¦^lnŠ·!q¹¢ã™‡S‚(tC×Ò·þjÕÒÂYB>ħQß­Ü>€ù¥—V ’Ñl}=j5˜BÁ”î)‚È—Q󡻊¼Ž¦(` sxJl¯®të‘4.ѲžÕÖ4>+ƒ‰´Ña«D’È£‘¿ï\Êk1tqŒH:7­0Ф—J¹YÊwºY¬7#ºí^›ªÐ@³ÇºsÄœo½Í¤Ã-¸Fíú×=½±¿¶vIC…Ï®ÕÔ5}X][­Ìl ‘†_J«Ü\C1 ƒÚ’êufˆœ0:ZÉ©â? }ꡜ¬Œ q§À‰[$šlW2~ŸJØÛÉkˆðqTн’)ì½=cen ÌÓýv´nÜÑ9ƒ™ È¡üF;Ñ„™Žçw]daíÊ»i|DW*}E3¹±±ÕÄò«‘õ§¸ªŸ99ÓÛ+É ÁV¤§LØ®²Œ ÄlVš•…æ”ÜÛòöuèhXµäK˜CZ±&·o4f Ô §lŠ_y¶ºˆi´™Ð¿_8«69Ž´¢Þ½òBʦÛ×·øB­ÖŸ>è fˆHm™r^ºÓ/t·?5ÆG­u«ÎnQº ½ÑGw“Å”áß.`uÂ}xJdA†÷¤ÓÙ>h«[µœ¤†ô4iH™s*ŒÒùtÙêïÁ]4fÄ$‚)X÷©G4÷£f†A¼#"‚•Ülâ„~˜`F P‘o¥MÁ©“UfÙ¨ Æõ…uªþ¬}ÕN¥œG©öæ£!¼pHªß0gQöñ»Tš¾á®bj(Ⱥ¶D‘4jyFâ½ABò,J2vµê׳M¤Ú<)¦uù\²ÒÍæ<ò¨=hÇ”F<8ƨ¦¹¼a‡.{mQ«­½bš@ùVßk¤7r“œ“’k`ç8É­9³Ó­n‹ÍZÒU¡ªE8æ|ì*2}+hÔ“Ò¬Ý|SÝn±Œš&8Ëv¯AlHæ;S[K{ ¸å‹võ©Xe/{œóf ¢c€GæsŠ6 »x6õ\{©æ;±¦µVvÜæŒ¥'x+éé Ýã*ÂÚª – —U|l(oªÐò“Òœ?s[”ãá"`ÈS>©1;TGQ¸oýÃBµà2iyß ½‘´à#RæW>g&˜CrcLÒȈvÀÀ4Ê–·qE±á­º!®Û9­Öñ“4 œdÖKÀõ1;€º˜q"å<[ÖxrMBפ½ $¾AZ{’¨OJƒëÜ’ƒkÈ»‚5õWçÇ1©­õg®øÅÛ˜S-)‹Î ÒS[,’r“UJ÷ås³ÔšÊÜÊO˜ô¥Wš¼—NK¾sëQßÊUD@ô´FîÞUÍh_Y3ØšRúW9¤ÊyL Ôq˜¤ª)oP±@(y!h·Æ[½Fñ¿8~Z䕲2çrŠòèK!3¶’,cÖmã!É•B¸RÄ` ëPa°$R©_ÄðÉ]$ï .‘¤OâÃ…eÁé“Ö†»œHÄgU7FâáÄcr»ž”èܹQ#sZ­+UTå¯.SHàâׄÅ9Vw¤m6£2Û°FþZN'yNPšÌº”¶éõãUMt!Á¶DÊl§Êþ ˜åb]Ú mñVÕÒäòΡª;­&+¤2[Ƀèh‰›\W$#éõEá”YVF⌶ds•3iòÂpã¥E"rì´„SÙØ‰]¹0[‹~LrdŠç,‰ÃÊ_ÌÑíšÃ””yNv¦! ·<¥òS2\ƒ”Ç›K~ Š–M1˜ŸzGÌWfÚ¥ŽGFÕ.Š)EœÔ”帹 Ðt.!çŠ@Ø¥²iŸ.H˜èwœÃ”œQW(ŽIT<.y-Iú’ÆòÂnuÔ¦Õ “Ö˜]Å‚H¶PÃ9SWÍDmFÆw(Ë\Ô¶—E(+Ó½BªYð(¤Œ'Þ—ˆIuÇæAÔ<'‘FŒ‚Tß53¬SÅÊv -°»7#Ÿ+J"ë(yסéŠoÂ6aH4Ó£è—F[YÚÖrB±Á¨. £œt;ƒZê«ó)Î6‘zZÆ?ÏFmæÝãéŸJÁkTæZíî½(ÜÞµ½ÿtËOa~#g&˜Ú¤WnÇZQ$Á‡†Õ=•߇"h)b°1‡„žxKpå4â>x K»o2°éU!m/Š"*AÎ*ýu©Iq£­“öª‰ºL²òŽd9©éÓJæ¿6VéNc,“6Oôý-m´ÿÆ ‘Åi5‰xC2ÜŠƒçe„á?Þ­–ÜKi«GòZŒ@ž‚LoJõ^u&kSÏÜS·Lô¤Áü>‹ÃS wF¤Yßö@Aª£y]y o+E8ßµ)¸²ž!”ŒV#y£‘D‰ÄÃ9]|,“ĦžÊFÿ)ÏÚ…1\ÆwBh¸ï¤B iŒörŒIÞ‡t1“{Ù $’@8¸IáG$i„7MÀïEIko0ÌD [ãÝ0©ÆæÂ|! j6Ô”yÅz“òÎ6(ßµzŠøâ«éGè—S—ŽN`½A5 ÄJÈ%QƒœSKؾY ‘Že \Ò©–»þ•–…Û²(dÝâQ¢½nA;Y‚33nܪ:’jSui å@e#ö£›b»$¤¬X†ÒICŠ$›{A–<Íè(c{<£ „_A[¬<ÛÍ>ááTÞ¤·SÎyG•}F"=M° ÔÂßj%”eÜ©a˜£ì¡<Ãj’;_jaik†ÎOvðl®†K9zXqJ_4}jÁsn|>”²Ksž”Ò®ŒŒY5@ KFOjÊDsÒ6Äõ4V[ç­º{œîÍœ†Ž.Q“Z°'4ÁíŽ0alÉíEš'Ÿ ñ0vþCŒÔ–±ó9ôQF½¡åéRC  Ûw5 (ÞѺI@fÑÝ-ù+¼˜î Ñ÷dÇ‘JœÄšCXHðDްÚ‘sNtcÍtƒÞ” ©ß[;Ü EÜÕT™gkæ—UídN)ä–þ<Äc4\:` ž\VJÈQ¾i͵¬Òªùv;×Òh´¶ÈnÊÏ9î’è´Ž}ñGy­ùÖ>žÔòÓOUúØf¯|)¤ÚÞÂÑ7)Ú´,û%\F7‹\*¦Ôþ7ÃuM:[AÉËUÙí6û×}â^ †YSt®c¯ð­Í“·áz×Ï5ß±•ZmäknÕ-6¾@’ªv*Ì*ÐRFÂ’Ág$2àŒW®¯ØIá¡À¤/00Çk]5>)bbob€ò©PWw+8<§z[;¿76NõÌC êÐÀÓg#â„0åŒÈÙ¢¢ÔüÍP€¯{Ò»—q!ÎEZ\ú{9…]Ðî]H]yNÙïCˆ\läRØe#½4µ¸è M+*y9óQ|ffp¡•9¾ô¡æ<1ϺìÔÖrg•Õõ4¯å¹\d¡Üà VLŒ>õ4låNG¥xé³çé8¯-…ÊŸ š8çiËJ“ÞÇ 8«sjÓä¤ÓÙíc-˜¤ªzl3G:3FzÕ´ÙÎ_*­‚§TeÆ@ïéež­¦ —s(X4ó5Áñ2 ,Õ ð¬ŽQ`R¾…iîŸc0K†9”zßNÒ¦Ô¯ÖÆÝT»dó1ÀP:“DV鬕ÅÒøZ<שéºòþÊy¤ˆÜÀ _ÌUÿµ3é]çHf Ú~ «’È9WSÜRÛŽ…du‚e8;gÓµÍI[ý9ËCOLñàêšC1Ȧ“ #ùiý$Ó ­s‘Ÿµ+žÝÁò)‚—O–˜ã*ÙiImž žIy æ´Ù„Ñôméýª,ñøW#¥zòÁä ŠG¥'¨¡ ‘’—·ví„{ú d\F.#=z⢊R±mö3˜¥BPœ0¦ÂØáãFèk3-ÁÛl¤ú7·ÇdÃI¹æü)•† /ÔlÊí”+©ö£lì]$ ¾Õi‡I‡TµI"Œ«µJŽ‚H&Þñá<¡á¦u$ÝG åSlĪãâœüêAñN:TóiÙI# jWsi$ŒHV’Z6°´\”áÔm™ÁĦöW¸™˜t4!´$ù2’%ˆzŠWåjé„Ýü§ ‰­m”c„dcnô4³<§8ö¨®',Ägj+KDžPŒE(ª¨°!¼ êeØÓnlÖ^pUM]4Iæ1ˆä”ö4¬%­ªÆõ¨â¶8Œ G1uH³BÍÕV 1©®µ¥ÄêdTïUK‹V8§Åeäa‘Jo/K’ÉŠ¾ËÚå*&ÔB6=,z”1 Ç$·­œ0¡žè½1{œZ™÷ ¢â¸–?¥ÎÞôÆÛUuÀ‘CŠD.t5,WK^öœ f§ä+:êvDÖÃ5êB.:ת_ô¿àÛë÷•=ݬ"`m¶¤Ífò#w=Yõ ›(aåÈ‘‡aÒ«—-3e¶ ¨öÒDË­<ƒn³ËáÄ/sëQ¤Xè*bA­pAÚ¦"h7Ž“c D"‹Œö"…A¾h˜úÑôø(iZ,‰Eä &0½Å¯Ë^78¦l•±ä¡ú%å3ã”]½Ä@ƒµWÍÉëš–“ͦì+IµYno#1þ”®K¤$Ô1xúÐŒrsDUj/}¬«¨ˆ]1IК.'VéŠOE0µ$‘½Fš¥Ïuбɔ0x§¥˜[~Z+E±3ãËVXôÅ2F+qC¥ub¤ÂwYU_JÈéPjÖËj¸èµc½x¡!2j¹­MâHÀR§Š͹\¨¨©º†KW.ÛÓ«ØúÒ©$Šù^¡ÈçÞ.Tvñ^®¡Kvä_3ì)6—bÌ—s]†´ÀÄ6A½hþÊ騨n9Jµ|+ÚOôša·Z#TÖ-´å)S jõ,­Ìqœ+˜ê÷òM+e»×ÑõŠè~ÎStiÇŒòU}7Æ;tœ'3ñlÅÏ,˜«oñ•ÌE±!Üz×!2±mÚ¬œ3uáɂݫçûAY$ÁÛÊqW¦@èKv®ÅkÅMw9Žf'®jkø ºˆ‰•H#c\žãY’Òà•“4÷C㨧ÿÈÞ6Ç`Þ”ÓLûU,Ï4µGXÚ&XNøFZΟnfh¢\Þª:–,.Yrjÿ¨Z-Øñ­äõªõï4g–Aœu®êe+Ú|6¿pޤ¨s *¢èü¸eéCyðݪÉ$0Êz`Гi$yÐdV^§M‘£ty²ÐEXÙg`¥Í1Š2{Pž*MžlQ:»Ã)zO—S¶iSä|'k‚cLï Ñl…Ý*x&e8¡¡¸$rµLªKSµN'Xîb½ûd í‰ñHȧq f’EÝ#´"Æ+s|yºÖÖŠ­”±Ž¦IHg‚IáápÂ&åå Úb­²Š!'†æ<1ó _<ʇ—¦*UUxÚìÖDH³†S+{ãPjï¦j1ÜZG4‘®àšæÑÜ¿6õa³Õ¢ŠÉ¬ÃŒÌw«aÖ+:ÎvíÝ]FKA7VÈ59®ŒˆÄ£“=³Ú‡Þè—ÿ9dád@Fêz‚ Sžù2Jœ“ý)ÝŽ®5+t†iGŠƒ—$à°í@×ý¥Ž¤^P86B¸ºŒ‰IÿuíOY»Ôoíì¤8Q€è)n±¬\CiaóDǯO)å”–£¡žÙ×™ð #¶€“SJ—ÊM6жv7|¾/twüœ í9Lne¾‚¥Kmm*‹{²Kú}ê¶í2í“P¼’õÉ¥óê»rÔÂ:ÈÞ,à­wöQJž"à°w™y àM¼m±Ïå÷¤–:ÅűO—ˆöî>ÔUå»2 ëFçŒõÇj£ãc˜|¨Ø+™ÿ¦QƒÁV+R ž„S[eóƒ€*¤jü [Ü)è})ïñ?6ç½MÒD¹üeAm¶¹X.u]:í s ƒR[ËXÃx‘WÚªºž¡,31õ­lx–hX#·2ŸZÕ §aÁì¼Æt£þ‰Ç’;S²-Oéõ ª_\mÐU¾ãT‚xÁ€ŒŸ©j»®ieÓæ­TòÙGjEU©tßÑrþC¤î”˜H@Ç9ý(­6~I¼Á9r¡MghcÌw¥óT‹¹Q;Kºr× 6Pž´šë$ žôÆÙK8À'¿P÷ Êhx%ñm |YûV!“|sQ(ëÜÒÿ ”lkeçí½é %ÎÝÝq0ØPÂÀçÂ3Ž\Qi¦I'\UÌ•Ç ?"Ê­²05àÅz³7I/ӌгpµên©Ÿµ\3ÙtWÀì%æ@1šõt@xF½]é%>­?˜X¼•œ¹Å†=$E‰85 @¦™ü3™Êj h²…cîku@+Hµh²5àæ4Ùs%¨:Š•F*8[›Ñ!Å2‰ÂáTæ¨Û8¨"‰aµ )ªç Ø…”FSšÙ' ïQ20¨‹;Ò£RZì"0pš¬þ^µä—'s@,Þ]«hçÁh¬½®†¨ŠéÄG"™éÑ—‘@îi¼Ù=jËÃËâ\§¦kE¥¸M+B›®Ã:jÇl$aÚˆÕî’ Þ ¤¶±`US\Ô˳ÕöI§ŠŽ¬Ù ï%u|f»=è ï4ŒMAÀ{±¿zšéÔ“_>«ª¸’RJ·—J“ÝÃN)ZÀ\c½=˜SK¢P.zY-B&™VC! )Å”)kr7í÷«ï DÁ¥#r3\Ù¯ƒÌ±ƒåS]?„Ùn4ÒŠsµ}#ì;Xú’Öód²±®k7;º¬ñ%Á%×;šç÷Äóµ^8š7[¹ÅTîíüBH“ícdžw$ëL;EÉlše¥Ýx7€™LDäV–Ò‘09é_;ê_nëDë9¶óO5'i°=w¥©#DüÀFô`”L¸;вÆA8¡åyÞ\q`âÒ®|=­Ë,AKe—b=EªÜ+¶Y:ÕKB™­îÔçbpjéyiãÛ ÎÙ¯ iuÔ´òÌÔ’ª&C=Ç ‘°r½*kkX#ô¨¤”ã+0ÂCsŒRá#á~\E“V²Šá@@3U«&Dc„þ•`Id{Œv§QØ[Ï3œSi1ë .Å Y…€W<þ?åJ&ÇO¹i@ä8«EÝ ˆžHè/­²ÁpiqÐ⥖ò…y¬|­;FP—±Mn¡yH´Ü08 Ñ·w’ÎÇ™+’GVÜRýBfÞ"l¥¤ÎGZÞ2È+MfcFoQÙ7<€ÞŽâ+Qü> TwÁª$t£q…é'1ÈÐ{¤–×R<Ê Qæ;ö¢¬®¦I)cø@Ç H%“»yGëVžáéµÈ,£BB¯;ŸM³YÚ§Êö²rMÿ@«¬¨dl/yþ ­R"#PßQ5,w‰cÍZ®8vÖ 92KÜŠ=Ïjio¢iÂE½€I§Ÿ4,ò¾ÓçÉf¤ÕÑqÝVQ/® ( sŒJ&²–6/*è5sšÍ´ù¼à`ô#¡—XA,™Nâ˜ér—6òr¥IU¹Öo"Y·À4T_ˆ¸;Ò¹¤…NØ4ÏȮò­æÎM¥hkw,Kjp('·*wZ°‹`@ÍE=š‘°Þ»SHÜqU›_xÔv©ì¯d²“+æ¶t=­î!åb¡JoµÇX¦M™¯m Õ”|Ÿ=ds nGt>†¢µÔŽ|)ÛËù[Ò¶³»k\«a‘öuõ¥õ€ñ¡óE&ê}=[SU–oÕK9xéJ}ó¿æ·Ôí򾆼ýiÖ«ÁÊ“ž¢žY]•ü O°'ûPš½‡7+uÇcH ËŠæôŸôK¢º•%æW#â-\…Ę÷ªðb­’(„”:ã=(šqRá•]L ¨!”÷’ÊãÎ#¥-«ø¼Š§¢Óï7ä—Ö®:pÓ¯¢ó(È4‚mÔÇ T=ÔfäxYÏ «ëŠLðºÉ—;çz·jsˆÆ˜ÀO»½C÷«èº7²º‰ÒKsnQ±Ú,é¶3C=¬¾ãjÖÖú@À¥YL{½<êaWê§±RI0ÀVK!¦?ÔॶKœyiˆnNÔ%£Ç˜æi±{GŒFjbb©q.á ²HK Ò}AãÙ ¯M4;ªš YbÉÍq±¼ð½;ÍÜW¸Éògô¯P­qN¯UÛÿdáýP:òí@M)$àÖÙ…a×⨩tº×ô€mÔ^cR"‘צý…I{é|røò¨%J¶ÛPHôT!ŸaZ&m•d•!C'•wÍn,9G4”d1G|òè˲X…Î Y7PYª»¹çk8ZȰÊ,Ô 1y—¡©šRÆ¥tù«wó(Ȭûšæ;rk¢ öJÕ¼¸¬«oZªµn‰“Eç· ¢íd%€sáÆçµTl`,ãn•iµ–„•©ÐÜèÞ{)ˆ€eÕÂMH¼<¡»UWW»nfÞµœ¼y&–j±3d[šúÙ&§%Nˆ8”¶ÚåÎsLžRÛæ“ÛÆDý)ª.ÛžµêÈIºÏW3l‹Å—¤ú¥È³ò©óvò$Q–ÏJ¨êÒf,}h]IäD ò¹C’Lð‹µ¸.ÎõÒx'[[hŒr0Élæäl´iwØÇ#}4óì–´hj09ŠÚ. Ú®üJ‘ÊÆdܦªLÄsM­µu¸O—¸>Ù4¾ÿOuc,Y#®Õ¯Õ¦eqø¨EÁäwAR^3Óq²Q¨iFPLx¤ÿç‰ÎGJl÷sÛ¾àTÑÞ[ʤJ0kQKGW%þWz¦ÑÍ4n·!,´ˆ’"zŠ6 HžNhØšg“ãå¥nÑ¥'Á”ÕMk÷’Æñ”¨=k§é:{]éÊ äòÕ{N᩠ˈò3é]W‡ô…·³—µ}ìN‡0sÄ¢À„‹T®k¬Z¹ÅÆ ˜‚ê‘9To]S´Š&fåW¿¹DsÓjoU¡AÎô4U/–ɶ‡á)‘Æõ‡- áNÕ>¡«”ˆªUn}RVc¹¥5Utš}™!{ÍÕ‡–;”Á ëGB¬ZE“®©:0 škkyó1âFÜŠƒ5M@ls|IŒ q96I., Èù…¡&Ó’Qå3M/t÷f2 Ȩ-l$‘ðS?¥f* hyc£ÇÕXX7—!ôýå¥2¬Iìi¶©§\›…á'Æޔ}Žžöªg)‚£jÄzâ¥Ø†ár‡!óéTHÖˆvßêiêŸ4·fCUB;–{{U³s5v®ÐFƒÃwzÔ‘uX¢Ï«ª‰ÂÖpjÜNÃò-öÓx»PÚ^›kb?›Va츃4pµÏ¨i¸hÇÜ“ëU/–FS7¾Oçú*EÄ7:…ì°,þ0åLgÇrMJš\K ¸;}D¿ëÚ½i’ÞÝÙ¸Ëôg»Óö'ö¢Œ×rF–Ó@"XH/#€`m«5K`šÑÿ=}r‰§‰ŽÉuãÝÇ5Îy¡VeÏU+ÔR{YþníÔý* ©ZùDúŽ¢™â^Lÿ¯ z:v“6£&Í0ðâ¹=MBóÕ|‡ÓòÊë! .ÛÜ€=Ï)%Ðç¹r½3Môxwɪ\dަŸ[É…§;cœ…:¡€ÔKáã’™Ô’9DÜÎÛz^oC63Jo5&šBy¨t¸lïLf‘¿+Wb¢ÚÛ¹<ž4™9…-’"¤ž€QVw<ÔַÊr)VטK°¥Ï9çÀ=)¶tކÞ}ã~¾ÇÔU}ßH¢íæ#¥r’¨‰ (÷a¸EjF H:ƒê(«[ÈZÖ}Ûg½FnÌœ²ý&€·º0ËŒá”þÕ*¸á{­ÅøL ™µQì~šŽ˜ös•#ÊNA¡áˆ‚Hô«\é¯`Xâ¥VŸ09B0GZY±£ä+`”H ]ó<ôMÜöÒ«#ªh ‘ÈÛéÒGæDæ_QCTÑ íÈUU1–Ê:ei­^\䆪w+É1 ëVÝ1ÃÅ-“ìYrŸz®jÐüܽô .ØžXP/ ‘Ñ•¾–Š[˜¦qW *øIgq`ÊU]vÏ­Tt8®\°1G‹›«^bY—'¿J}@ŒaS]gqeò»÷žÂéâlö5¢ê³ŽcM¯| fÜ p³¯ÒØë÷ªÃÅ5µÁ†a‚§÷¡%;]Ž 6ì™–xñÙ­ %㓱¡L¤Ö…³^sü•€)ÍÄ„õ¯Tê…Ô¬ÐÎ*rêö4t*JäþÕÆÌK“…HJÛ¯ÙÅ/2䟕²!Í2³‹šy©’ì›6*‚ìÓgà-.ç r©éKòe•#Î(©“;ç­ÈÁ¹“b(‡‡„CaÚÛPHÙ¼/Bôé¸ýj;/óy>â½óW.9<£;sSÚAÊyØ`(ÍPÈ]´îP¸4î@ü°.ßzÞ;6æØTȤ±oSš2 â(ë]1u% B6¢¯* ÚÝÀ9ÇJîuypiÔGg KÊÄ¢¤ã¶ÁŒ`Ԝ̕£kÿ¤Jë8¨¥±1ÉΣjÃ1µ“’0Ã5é#ŽQ½-#$ËyCÔѶ£=Òk©ÌžAÒ“ÞÀ[|Uô°Ç+AÝéRò‘J«4ù]°:œàaU‚rd“Ò‹ÓnZÌIÁ¬ÞXËÇ'SRÛÙ•EȬ¼bZy±‹#™g´’šÅv­ƒœqg«rFl:šG¦P`Іeš=”šÔÓêõÔ3FÙZn¬sØÙêx=³BKÃÓ*)ý)M½ÝÔLIÚœÚñ<ñ“'0£bÔôÚò~%¥®=Ç y†¢ ôÍÂÛI¾†@c$Õ߇-.¥*“Å·®)}‡XÊa Ôî×[äÞµ¤Òè¨`"HåÜ<’ŠÙ&Yí±]FЭ#E‘ÊçÒš]OmkXÙr;W1“ŠoÐa\±¨“Š®±3“[X>ÐÑÀ:12ÇÍ!4ÈíÎ*Å­jÜÕJÔ/rI§3_Ewónj«©£«±"”êó½ìÞÃpšÑ@ÚPw“‰2.d ki¤`NkHåà×Î*I’K”Ðdz…*Z 1Lm,%,±D¤³l¬ÙHÌŒVþ ³‰­.õÛÀ<8G,cÕMaòU5&’"óÏd-Ÿ§(ùùqŸÊ*É¡pv‡9ç“ÄwÍ Yî¯oI&{U†;÷±€DŒyjJèåné›pZªš©Y°¼çÉGÄÜ5eÓo—›DƒÖ¹/ÚÝéìÂxYŽìGÞº>¯©<ñ–qŒU6k™¯î¼Ä„à ŒV?í ;!.•¸¿í&9bn÷x€çÍ>øEn’I=ÔßXŒ…&¬_m4Úb”$˜ØŸµ.áx-ç’;'åwJo©\É42®TúÐP?w÷ºOVòêã+}0ª¦â ZÙ%X¯#H„ã˜Ì( J]Zäˆnïfx“\„¯õ¢ï´}>ë2€ÈÙü´È,.fíÎI¬ÌÚYl»ƒ…»bööGBZÏý<¾·X´ÒÆ ñÄïáXÆÁåíÎG§µ.â^ËÁ GmäŠ?EîzÑæ±ü"ØÂî$º` èƒÞª–êÒIóLX“œôTP‡ÿJ.;Ÿ4Ö†œ¼õäàqúŸó÷&0ÝÁ*ì=+·®é‚M 3±sPÝ1`kEÚjrÖÎ8šçŒ!šR^¥ŠCBd÷¢¡ÁÐ1÷e1|v •”˜qG^’ÑmKí0fšK’ Šª¦'6öIçnÙUé6zš&±q +V#ÈëKâ»]”Qµ$*r  \Å×ó ÝEOç7VÚ®™†VíUµÆ3¸-4}PÛÌ Ÿ)Ù…1×ô¤” Û|aÆN)ݬ¶’ÝXøzú;ÛvÓîHgÒ—6R×ÚQè§PKmS÷öU†ImÛ¸¦Zv±qlÁƒg˜d#Q±{iZ6\Ò–Hˆ”cÞŒ|N„îaÂ-’‰™âÈ*ä©£êñG¨ZŸ”ºB9Ô}$ÿµE¬p·Ž©y©ŠS¾;5Vtû泓ÄÆû;U«IÖ>YµÑ/iq±#~_qKª£d¿ÕÎú¤•T²Àíð;ŽÞž_NÉBY‹9ŒJpsŠÒæo) £v¦¼E`ÐH%æÈ#+"ý.=iE¼ÑÞT÷Þ Ù.ÑnM/sD©\ðÉm+ 'ìjV²¥±bxÇ”úÔ×,&œºt4\¼JE ù§Læ´ê™,o ´r) §V€š¹k:Ô 7°G‰PyÀî=j®öÆ~Š®X_½z¶LßUO§ô¯TâÞ\n†½^Ø|‘^4¾ Œb™[©8º1M,ÔíCÆ<+Ck5OÉ€3^7\QqF1½L‘s®|7PábÜp…Šì*nWŹ—z›ä‹/LSm5Ç6DÃHy)+«ë@¹8¦sZ„Û:Àt«¤€Ær®|e™+[{Bç$T÷ˆ±F N§ê5:‚3#v¡¹üR\õ5(X$7P½G_°PG­K[”ÛjÓ=h樒,Š‚Ä[ÔRérÒÑ|ÄDÊ<@M2ÀU'6LmÃ,b²fj’"CíPHÀÓ·Œ)($8©ãŸ|0£`xÎ2E(,zŠÊÜ7­Eµ]>U†R°(ÔQ0Â’ Õv;§#·£ »–5/ž”tñ“â & ›\hÖ·ʵ­× YròœReÕçi>£¹¢[V¹'"CúÕMCS¹Û y£Ûg}·ÝH¤[²¿¶i}ÿß[9ñ­\\Qún¿y €†¦W\[tƒ$+{š!´štÔ÷q-#ê¹,´íÇU#§ª2`ýª 4õ'ËVI8³NœrÞi‘Ÿõ.ƵŽ^¼9‰Þ{éôO6‰í?Ttá ¸¶³!ȧKéí»©ìº|y¡ºFûÒù¬\“• =W%æ#eDÚ{\0¡M~N’ .ßQ‚R )lÚi]Õ¥Ó‰ 8Ú«em]3¿¨.y´áØY\ÈåÊ6j¸æÚAš­ZßN›?­3ŠðȾu­5.¢ÉÛn?æ:Ë×vÊä´tËJLÖhù‚ÑFÚ<‚2kÓ¡©%íì¦bw‰*YݤKu霿i`Y 1vŒ`ŸsU}/B’{ØÏ.9ɧ¾Ú¹HºçsBÇI+^\BY¨9µ°W J´•šRyQ:‘ÜúQÓÛBŽ|(ÓnädÐ|?ζqsg,Äš¶XZ)î¤ ’yG0éO[KºL•ªy‰Ä¸ÝQµ[(oPÄ𨫃UKût„fyÐìIê+®êVi4@I²¾Àºûæ¹þ¡¥³Ï%¼ÊXU«¬A4Æì‘}{Úplo5^Ó.L ‹v*ß~µa»ÖŒö¢Y`¾*©wo&tЂr‡¥4Ó.„žKž´5,Œcz\Á9¨d56—¿æµ}r×–Ìþ¦—j‰4ºLòÊn&&IägzÐØÏÄŠAþÕmY´Û-’A4Üôj÷P±¸ò(=MZ)"§mÜm莪W»p«Ò:D¸ô¥w7‰ÇOjk¨Zɉ¶¤²DêÄ0¥µuOqÚÞêPÆ‹ŽTA‹2Þ‡XÈíÖ¦D"«¦Ü Õ²:黀ÀÓÈ?, ­ÄÄy¦Î`šk(eÒz°H¸Q]ÛŽbq@ø8m©½Ê’đւaÊqФ×v\†CµD·¦jUˆ©²²íDÇ0èËD²ž7w]{ä·{$¿´8ÄŒgî)U´2Ù],ѤÓëâŠáX¯—;ý¨OE†Ä‘JrŽd&½>”Ú‘v€J¦:‘Œoàÿ Íõ§ÏY­Ì{²€O¸ªÅݬˆ~Šè|-mlÈÖ·R‚¦7È=kmo‡´û9K(fFÝvíDQèRÍ…îŽ.{*)µA)ßEÍbÒîîHDÌO`)½Ž—{hDŒf(›écùM^øQtߢHðÝ2FõgÔø^ÊóN•ähÓl‚ßWíYÚ¸aÓ§éÊðB£PÖÝ '¶ÃϺ¦i6Víð½ZQ-»ïŒe¨ 5߇éÒ‰ã ðɼr§Ò³e¨ÙYÜ6—u!pˆßÐúU¿H×®ôô6$ör}QË… z©4Î**8OZ!vžGíëù dª«¢~øò#€}G‘TKN#ÄÀõ™'.p7¥\uŽxN§¥skÕ¹1âEÿP½ém¼/Û²šÕéÔú]c-ãõPøöÔìwÓÉ!K$²“q•;UŽ#±}lj`Ã/™véíWûû2_›³´ª÷NKëI-$‘”>†­®ÐXvM<»xî¹ÏÍþ^£'ÓÚ)^7BN z³'Op6Z Ö‘{*¼V§­2´„ä Pipkp:Öm­Œ ­ÁØS%„ EE(ƒ†e;±Þ§v&‰‰ÑævG,±¨éÒ¼÷¨«KÞT=†’RN£…VÁ`ŠØa=Lj}ª4<ÄmC=3R<¢YûÒºª¢ó`¨œ»E¨Ý‚Þ ‡Z‚+ŽSÖ‚iر;“X`Ô¢L+cþ›@N#œ0ëS W¢)ˆ=hë{ÀÍ0ŠPîU¡×FÉCò~%Î ­jcàŠi›„-Hñ"q¡Ýrhå_Ã;šq,whI¶ä¡°Â¢ «{0¨ §7jF[…í«6ÊKäŠarV89}hcÊØ#T—w¼€ôªÉéDIU:ç% Þ‹l è_2 &Ip´í*·T:,4«zŠH£Ï¹õ­$xÛwXUrK ëTÖž3ŽlCLãÖĨšyß&¬©ec9ò·†ki,d€eeõŠ ]%a“Ê¥:³»˜aÙ²ŸzmM¨A'Ê>¡ú6¿%hä³ }è˜òˆegéÐT—ðJÇSíY·$g#Sj:®,ˆßÙ ¯§{‡åM´n%俆'.qVû1¥qÉŽíØW5²±ax'˜”OêjÍmª2J1¨è=jí7Z–)zR‹‹÷H*¢¿§Í—^Ñ,l"·ðœó/™}µk&¡5¤¯å”õ^˜ªV›­Ë…+! ëš±ÛëQ]·‘’ßνZÚÔt«"5žž‘ÁÅÎñ›[êÌ@XÝ›·61Rÿ[¬ÈØ%·$Pö¦fmýªËg5¬pdŽ­ÔÕ4ÚTS »I'Í-–ñërþ'áŒÝ5ÄqùWbÞµV{6’QHyA®Ù}g¦¦%­*<+¥é!ïï˜ãÛÐ5_aÛÕäß²eO[± ?žÁs­JÆâ+8àHÀËéUKQ²(eØuîÔÃâ?M«\µŽœ¾ ´^U©÷5Ï9ÝÌ…¹5ŒÖ5ˆà˜ÓÓ öÅÏ赚dN Ÿ$eÝÝÄÌR0Uj/ÔNMlñÈ9ó†‰ /më>i_Uý@nV¢-›˜±Jä€w©&²'2èÈ´ðŸU»•tzKÈñ¥²Ë±×j­ü©]ŠÖDXÛþêÎ09”P- c8¢AÓÂè¨Þ.—„#µOg1ŠNµ$‘C°*vëQ–ÖÙxÙâÅ>¬± ïCKÈz ‚Î}¹I©$lÒ9‰ûmacˆXä_jÁpµœu¡e, £] kwsI&ÅÝó½>¶‘u]-­‰üh7OqU$”Šq¢¼ñݤ±ì3æûWbª¾<ÐõLwFBͦ£q¦Ý,ŠH(|Ã=E^ÒUÖôÕx2üÃ(GP{ƒI5­/OH†§cÌ+ÿ™þ“ö¨¸kŠfÒoB5kiHP1î=ê‡ÕKI ‘Žãñ ]Iø¸ÄÐ7Äß§Ño ·º]ß‹.;ŒUÃJ×eÖâkN_•s#0úE¯é7ZŒ'TÒ\K¶]Ãß?ÝK‹t$LHÓ|ÀÅGQm©]ƒÇáC¾¦*¸…£p zt£XÑ‹ê>—4²·*•ݘý鯧ÆtCĽ¸fS„Obz‘ï‘LøN(šööëê)hÞz©$ý ¦ZE”-í¶Ââo ÇØ²‚y”{î§Õž¥ÔO-Œü¶¿­ÿ@ŸEKLMïîùMG‡®‘í‘í$aø~~hä™Éþõë‰â½·:­¤"7Få¹€¤úNgÇÉ&™tÌ× ` »ú‘ÿôR ÿMÔogŒ††W*HÝ[Ì7­‘+ªm+Ï·¨YíB‘°Ë˜~#ù÷)⊠NÝÑ[(æP{Ò È¥¶›Ï±Znì¶wQÉa]r¿½}4wÐ3§Ö+yxŽ!½×P¤sšñÿSø%²éÖW2ÝG3îkÔ¼Þ:¤î6¯U¶o`´!’‚¹ Ž·#hrhÈ|×Éåu–éïÂ3˜€0qYçeæëQ€Ì3éZ6O^•K^G Œ'²ŸÇ"¶YïAõå˜ä(ÎõwÄ–à«ú¤ Æ5æ`hMRã¢)¢D‚(·;â”ÜIâÈI5ÈÏUÛ‚®?ê?w`±ȯ6Ç5”Zô‹¶Ô]¼(²0²Ž1Öˆ†L8 Ð)œÑQ²æa5ñÈ ½Œ[“M/(]é…”ÂD4òŽp^c*š—XÝ:ˆsF7¨Þ5Öð>#Þ¢–LÖ¡ÎoL]*kÁqP¿ZÔòâ±#µ rZ\÷€U¤‹-åo sŽÔŸúÖo.ùc+ž´²;‚s¶i-}[A Mi)í²/89­®¶Âus½Ku"‘ŠŒO†ë±n-Êc“Þ‰µL0Ú†‰ÆqFÂê£"£Î ª[‘dk0Uô4Þc‘S<Üà 9;S)Žð—“±DcdÒû¹¹rÑ7SòR‹‰‰$Ò:Ƀ<-]Ž£®T3LÇ4 .í±©üÒ7(^´}½’@¾,´ 5òŸDÉ‘tÅ×´ûr¸iÞôÊ]MbO÷¤—z$¤#…K‡S¹&‰Ž¿ :l?U=æÖ)¡¾g“,pjßÃ, ‰'Ý{Þ©ö6~.'›eô¦Ñ_½³ áE1ÒõgSÎ|w)MlF˜Ú®7Эà Ç@(f·’<8›­[Î& sËH¤x€ƒï_@Ž m@uap¹Yéakpƒµ¾’7ïV'RY@ wéH^Â-ˆuÅmüKOÒy&ã¢æ‰†ªm5ö{…½Òù™¿ÂWCK¸mQXa¤#aéFÙ_\M(yX‘éØW1µã›Pûw=霼_*Û C€ïô'ûš×Ðëô,nýûèu ÆÉ]n bÆÆ0÷¨'¢çsU¾7Ö%Õlͼ€ªž¯q<Þ=ÄÌìORjÑ¥vÞ$ç#°£?ä¯x˜Ûa ðšA)É š^ðÔåÚiT…ÎwïIî4¹¹#Bt»>³¤Ç:ø@_J¬Ë¤ª·•?¥d5±9,0ÞS¨+I*…m¢˜¼Ò èØÀ€ù×j´Ë¥¢)$UoY+*´ Úkt¦nhácžlÕóÅËÍ(#xsµ({É"”ïjxÜKæ_Ö³êFwÜæ8(¿tÏæy×ÐrMÊØ5,Qdw¨®bÇÞªëIk•ÑJl°]ù¨%åêk’6¨šS’ MÕͳ” § h¥(àö¦‘•–<Ž´‘ÛaFé·ƒ›‘©%k;‚ªx‰nàŒå8Å r„g­2’<áס¡§PFqRÝh±Ù ót¹ 3V-.EKc¾:ÕvAÊÔÛI½q˜ÁÚ†ŠBÇØ©ÕÅÔŽá4ÓõilfefÌRlÊz6}*)aù­=€i <¿íK.,¼Þ${©ÜbˆÓuk‹yÁ1“GUªåqËÂS$_ýHyî<ÓžÕnôùVwIWé$ì}ˆ«ž•s£kbKk•[9®09ñ„çìO¥Rîíc¾„Kádl~aCZê3·ày„±œìXP‘‡@ûl{ù<’ŠŠAUyáwOÜ+EÔz¯ê4°áãÏ”Œ«¯qö"™ÚkÜ%©Æ%{ÉldêÐËp³/_ÖŠÑ5Í/‰ô¡ÃZû¹+Ëmp~¥ÿKJ­k Ü•¹«Œ*5Ldò<ñÜw_öôíYÐMS‡~Ô0ž]j¶­Š’³°å{™F?Ò;}ÍUx£X—@{k4ıI’@›Óô]´¦%!F1J¸‚ÈëŒÍ4>eõ#Ò™GP(bc ç¹LâéUN×Éòÿ2‹²×l5˜Ä1ËáËV>ƒzÒÎ9cœù¹•­sè%šÊä8ʲ6âžÿº…ÖHeoQÌ¿ñ^5òn´Ñòi}[ÁóV‰ôò³©šõ.‡Yº1)iq^¢ À:–ãp\z<³mL`«KíÆÔÍvM½+)+Ï u)Í—žB£Ôe»Ö$$TlO­v1ulMÂó½F²aòkßíCLO ÿ©¸ªf6 Òp™]NR9ëAÄüç&·¼ÿÓ%AQVP¿^£w†É„i‘^•1RAÐVòƒµ< »Sa/ ƒµÚ¡`Ô«ÐU-ÁP+7O°©ìny;Ðwdí¿jŽÝ0Þ¼'tsÜ êÍî¾ÖèH›šËNA¥š{7/Z.F9ë[(jL‚V|ÈZòÍÓ­ s2ƧzÞV`§zQvìNäе]6à"bqyÊŽy˱އv=EaÉÎ3X~µ•‘æG\£AÊg¦ÈI4ÖKrãš“Xìøa]íÁ4æŒnŒ´¢‡…¶!ÅM’v5™ë[[õýjècñÙ -Ú.ˆX®HÚ‚½º© µ1"3ŠIxIb £«Òeš…§€Jû¹,Ï#oš×À/Ö¦ ¸'´$—$öéYç4rì§Œ»¬¼‘Ádž$„g°¥÷—Ò\63…¬]Hï1ÄàÐþ”ºz‚ÿ p®yy¹YHÚBîMž°^¾•>™b>`£>µ-ÛÞ–ÊK¼!<„ø´Žf$.6©Èæ] ƒqD¯Ò)… nÔ3Ge y#o)#e¾¥wË)¨09µªƒ!$t£Q%?‰Ž!Aìkäú-Y㇙å<ç¢æ€ž9oØÈI,i+É#]1f'*ÁÃÄ›0Nä.ÔêŽsUà—$«XÆäd Lm`|IN1[ÛkRÏ?3“è ¬ê JÇÄóPöñFƒ* FA”óMØRv–â%\t›£,‹Ìp]¬54·EšåúLÒøßY« ¬ó4þi ¯¨èš‚æ·Ä{¬åu#ZòÑÙtû[¥¼‹ÃcœŠæÝbrP:$’y|Æšê¿J·r+djºôýG „€Ž›ö…^Õ[–&(;W=Ö%w‘¾õ~½%‘²sTm]TJØëç_ideû&Ô6Ýb«sF$Ö°HcldÑŠÀÆkæ’ ®¸Z¨8Âsgt¬B“S]Ç•æ]é=¹<Ãzw-oæßj* ƒ'ÊN—Åb”9ÃP—JG™hËœ ÷CëÈ-*ÛX¨a”8åc½dsDá”íC6Ò(è÷}èhÿ®ÒÇr%ˆ7Ž«Ÿõ4ˆt ´ :SDPIÚ†¡k„¦;¤Ò@ód¢x~šŽ+wVØNÞ(Îr¢±iü¢—N¯tÊžŸ{rTvò]DªUÏ/¡¢Öêl õ]«80p*3Ò£50Œr¼ý>HÊi¦ê–q¸…„¸?Nq±£.¥‚vÙÇáÎs¿5V:Šm0d`Np(-îÇžª‚;sB&41N'Û$óšë'¬ÁÅoð=eCL‹ˆen¿jåRíÊFÛÕ“„&–=JÜ£yÅ`. For a brief demonstration of a few of these callbacks in action together, see the cookbook recipe: :ref:`annotations-recipe`. Also note that new ``annotate_`` methods can be defined without modifying yt's source code, see :ref:`extend-annotations`. Coordinate Systems in Callbacks ------------------------------- Many of the callbacks (e.g. :class:`~yt.visualization.plot_modifications.TextLabelCallback`) are specified to occur at user-defined coordinate locations (like where to place a marker or text on the plot). There are several different coordinate systems used to identify these locations. These coordinate systems can be specified with the ``coord_system`` keyword in the relevant callback, which is by default set to ``data``. The valid coordinate systems are: ``data`` – the 3D dataset coordinates ``plot`` – the 2D coordinates defined by the actual plot limits ``axis`` – the MPL axis coordinates: (0,0) is lower left; (1,1) is upper right ``figure`` – the MPL figure coordinates: (0,0) is lower left, (1,1) is upper right Here we will demonstrate these different coordinate systems for an projection of the x-plane (i.e. with axes in the y and z directions): .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") s = yt.SlicePlot(ds, "x", ("gas", "density")) s.set_axes_unit("kpc") # Plot marker and text in data coords s.annotate_marker((0.2, 0.5, 0.9), coord_system="data") s.annotate_text((0.2, 0.5, 0.9), "data: (0.2, 0.5, 0.9)", coord_system="data") # Plot marker and text in plot coords s.annotate_marker((200, -300), coord_system="plot") s.annotate_text((200, -300), "plot: (200, -300)", coord_system="plot") # Plot marker and text in axis coords s.annotate_marker((0.1, 0.2), coord_system="axis") s.annotate_text((0.1, 0.2), "axis: (0.1, 0.2)", coord_system="axis") # Plot marker and text in figure coords # N.B. marker will not render outside of axis bounds s.annotate_marker((0.1, 0.2), coord_system="figure", color="black") s.annotate_text( (0.1, 0.2), "figure: (0.1, 0.2)", coord_system="figure", text_args={"color": "black"}, ) s.save() Note that for non-cartesian geometries and ``coord_system="data"``, the coordinates are still interpreted in the corresponding cartesian system. For instance using a polar dataset from AMRVAC : .. python-script:: import yt ds = yt.load("amrvac/bw_polar_2D0000.dat") s = yt.plot_2d(ds, ("gas", "density")) s.set_background_color("density", "black") # Plot marker and text in data coords s.annotate_marker((0.2, 0.5, 0.9), coord_system="data") s.annotate_text((0.2, 0.5, 0.9), "data: (0.2, 0.5, 0.9)", coord_system="data") # Plot marker and text in plot coords s.annotate_marker((0.4, -0.5), coord_system="plot") s.annotate_text((0.4, -0.5), "plot: (0.4, -0.5)", coord_system="plot") # Plot marker and text in axis coords s.annotate_marker((0.1, 0.2), coord_system="axis") s.annotate_text((0.1, 0.2), "axis: (0.1, 0.2)", coord_system="axis") # Plot marker and text in figure coords # N.B. marker will not render outside of axis bounds s.annotate_marker((0.6, 0.2), coord_system="figure") s.annotate_text((0.6, 0.2), "figure: (0.6, 0.2)", coord_system="figure") s.save() Available Callbacks ------------------- The underlying functions are more thoroughly documented in :ref:`callback-api`. .. _clear-annotations: Clear Callbacks (Some or All) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: clear_annotations(index=None) This function will clear previous annotations (callbacks) in the plot. If no index is provided, it will clear all annotations to the plot. If an index is provided, it will clear only the Nth annotation to the plot. Note that the index goes from 0..N, and you can specify the index of the last added annotation as -1. (This is a proxy for :func:`~yt.visualization.plot_window.clear_annotations`.) .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) p.annotate_scale() p.annotate_timestamp() # Oops, I didn't want any of that. p.clear_annotations() p.save() .. _annotate-list: List Currently Applied Callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: list_annotations() This function will print a list of each of the currently applied callbacks together with their index. The index can be used with :ref:`clear_annotations() function ` to remove a specific callback. (This is a proxy for :func:`~yt.visualization.plot_window.list_annotations`.) .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) p.annotate_scale() p.annotate_timestamp() p.list_annotations() .. _annotate-arrow: Overplot Arrow ~~~~~~~~~~~~~~ .. function:: annotate_arrow(self, pos, length=0.03, coord_system='data', **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.ArrowCallback`.) Overplot an arrow pointing at a position for highlighting a specific feature. Arrow points from lower left to the designated position with arrow length "length". .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc"), center="c") slc.annotate_arrow((0.5, 0.5, 0.5), length=0.06, color="blue") slc.save() .. _annotate-clumps: Clump Finder Callback ~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_clumps(self, clumps, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.ClumpContourCallback`.) Take a list of ``clumps`` and plot them as a set of contours. .. python-script:: import numpy as np import yt from yt.data_objects.level_sets.api import Clump, find_clumps ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") data_source = ds.disk([0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8.0, "kpc"), (1.0, "kpc")) c_min = 10 ** np.floor(np.log10(data_source["gas", "density"]).min()) c_max = 10 ** np.floor(np.log10(data_source["gas", "density"]).max() + 1) master_clump = Clump(data_source, ("gas", "density")) master_clump.add_validator("min_cells", 20) find_clumps(master_clump, c_min, c_max, 2.0) leaf_clumps = master_clump.leaves prj = yt.ProjectionPlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) prj.annotate_clumps(leaf_clumps) prj.save("clumps") .. _annotate-contours: Overplot Contours ~~~~~~~~~~~~~~~~~ .. function:: annotate_contour(self, field, levels=5, factor=4, take_log=False,\ clim=None, plot_args=None, label=False, \ text_args=None, data_source=None) (This is a proxy for :class:`~yt.visualization.plot_modifications.ContourCallback`.) Add contours in ``field`` to the plot. ``levels`` governs the number of contours generated, ``factor`` governs the number of points used in the interpolation, ``take_log`` governs how it is contoured and ``clim`` gives the (lower, upper) limits for contouring. .. python-script:: import yt ds = yt.load("Enzo_64/DD0043/data0043") s = yt.SlicePlot(ds, "x", ("gas", "density"), center="max") s.annotate_contour(("gas", "temperature")) s.save() .. _annotate-quivers: Overplot Quivers ~~~~~~~~~~~~~~~~ Axis-Aligned Data Sources ^^^^^^^^^^^^^^^^^^^^^^^^^ .. function:: annotate_quiver(self, field_x, field_y, field_c=None, *, factor=16, scale=None, \ scale_units=None, normalize=False, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.QuiverCallback`.) Adds a 'quiver' plot to any plot, using the ``field_x`` and ``field_y`` from the associated data, skipping every ``factor`` pixels in the discretization. A third field, ``field_c``, can be used as color; which is the counterpart of ``matplotlib.axes.Axes.quiver``'s final positional argument ``C``. ``scale`` is the data units per arrow length unit using ``scale_units``. If ``normalize`` is ``True``, the fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. All additional keyword arguments are passed down to ``matplotlib.Axes.axes.quiver``. Example using a constant color .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot( ds, "z", ("gas", "density"), center=[0.5, 0.5, 0.5], weight_field="density", width=(20, "kpc"), ) p.annotate_quiver( ("gas", "velocity_x"), ("gas", "velocity_y"), factor=16, color="purple", ) p.save() And now using a continuous colormap .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot( ds, "z", ("gas", "density"), center=[0.5, 0.5, 0.5], weight_field="density", width=(20, "kpc"), ) p.annotate_quiver( ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "vorticity_z"), factor=16, cmap="inferno_r", ) p.save() Off-Axis Data Sources ^^^^^^^^^^^^^^^^^^^^^ .. function:: annotate_cquiver(self, field_x, field_y, field_c=None, *, factor=16, scale=None, \ scale_units=None, normalize=False, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.CuttingQuiverCallback`.) Get a quiver plot on top of a cutting plane, using the ``field_x`` and ``field_y`` from the associated data, skipping every ``factor`` datapoints in the discretization. ``scale`` is the data units per arrow length unit using ``scale_units``. If ``normalize`` is ``True``, the fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. Additional arguments can be passed to the ``plot_args`` dictionary, see matplotlib.axes.Axes.quiver for more info. .. python-script:: import yt ds = yt.load("Enzo_64/DD0043/data0043") s = yt.OffAxisSlicePlot(ds, [1, 1, 0], [("gas", "density")], center="c") s.annotate_cquiver( ("gas", "cutting_plane_velocity_x"), ("gas", "cutting_plane_velocity_y"), factor=10, color="orange", ) s.zoom(1.5) s.save() .. _annotate-grids: Overplot Grids ~~~~~~~~~~~~~~ .. function:: annotate_grids(self, alpha=0.7, min_pix=1, min_pix_ids=20, \ draw_ids=False, id_loc="lower left", \ periodic=True, min_level=None, \ max_level=None, cmap='B-W Linear_r', \ edgecolors=None, linewidth=1.0) (This is a proxy for :class:`~yt.visualization.plot_modifications.GridBoundaryCallback`.) Adds grid boundaries to a plot, optionally with alpha-blending via the ``alpha`` keyword. Cuttoff for display is at ``min_pix`` wide. ``draw_ids`` puts the grid id in the ``id_loc`` corner of the grid. (``id_loc`` can be upper/lower left/right. ``draw_ids`` is not so great in projections...) .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc"), center="max") slc.annotate_grids() slc.save() .. _annotate-cell-edges: Overplot Cell Edges ~~~~~~~~~~~~~~~~~~~ .. function:: annotate_cell_edges(line_width=0.002, alpha=1.0, color='black') (This is a proxy for :class:`~yt.visualization.plot_modifications.CellEdgesCallback`.) Annotate the edges of cells, where the ``line_width`` relative to size of the longest plot axis is specified. The ``alpha`` of the overlaid image and the ``color`` of the lines are also specifiable. Note that because the lines are drawn from both sides of a cell, the image sometimes has the effect of doubling the line width. Color here is a matplotlib color name or a 3-tuple of RGB float values. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc"), center="max") slc.annotate_cell_edges() slc.save() .. _annotate-image-line: Overplot a Straight Line ~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_line(self, p1, p2, *, coord_system='data', **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.LinePlotCallback`.) Overplot a line with endpoints at p1 and p2. p1 and p2 should be 2D or 3D coordinates consistent with the coordinate system denoted in the "coord_system" keyword. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot(ds, "z", ("gas", "density"), center="m", width=(10, "kpc")) p.annotate_line((0.3, 0.4), (0.8, 0.9), coord_system="axis") p.save() .. _annotate-magnetic-field: Overplot Magnetic Field Quivers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_magnetic_field(self, factor=16, *, scale=None, \ scale_units=None, normalize=False, \ **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.MagFieldCallback`.) Adds a 'quiver' plot of magnetic field to the plot, skipping every ``factor`` datapoints in the discretization. ``scale`` is the data units per arrow length unit using ``scale_units``. If ``normalize`` is ``True``, the magnetic fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. Additional arguments can be passed to the ``plot_args`` dictionary, see matplotlib.axes.Axes.quiver for more info. .. python-script:: import yt ds = yt.load( "MHDSloshing/virgo_low_res.0054.vtk", units_override={ "time_unit": (1, "Myr"), "length_unit": (1, "Mpc"), "mass_unit": (1e17, "Msun"), }, ) p = yt.ProjectionPlot(ds, "z", ("gas", "density"), center="c", width=(300, "kpc")) p.annotate_magnetic_field(headlength=3) p.save() .. _annotate-marker: Annotate a Point With a Marker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_marker(self, pos, marker='x', *, coord_system='data', **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.MarkerAnnotateCallback`.) Overplot a marker on a position for highlighting specific features. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") s = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(10, "kpc")) s.annotate_marker((-2, -2), coord_system="plot", color="blue", s=500) s.save() .. _annotate-particles: Overplotting Particle Positions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_particles(self, width, p_size=1.0, col='k', marker='o',\ stride=1, ptype='all', alpha=1.0, data_source=None) (This is a proxy for :class:`~yt.visualization.plot_modifications.ParticleCallback`.) Adds particle positions, based on a thick slab along ``axis`` with a ``width`` along the line of sight. ``p_size`` controls the number of pixels per particle, and ``col`` governs the color. ``ptype`` will restrict plotted particles to only those that are of a given type. ``data_source`` will only plot particles contained within the data_source object. WARNING: if ``data_source`` is a :class:`yt.data_objects.selection_data_containers.YTCutRegion` then the ``width`` parameter is ignored. .. python-script:: import yt ds = yt.load("Enzo_64/DD0043/data0043") p = yt.ProjectionPlot(ds, "x", ("gas", "density"), center="m", width=(10, "Mpc")) p.annotate_particles((10, "Mpc")) p.save() To plot only the central particles .. python-script:: import yt ds = yt.load("Enzo_64/DD0043/data0043") p = yt.ProjectionPlot(ds, "x", ("gas", "density"), center="m", width=(10, "Mpc")) sp = ds.sphere(p.data_source.center, ds.quan(1, "Mpc")) p.annotate_particles((10, "Mpc"), data_source=sp) p.save() .. _annotate-sphere: Overplot a Circle on a Plot ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_sphere(self, center, radius, circle_args=None, \ coord_system='data', text=None, text_args=None) (This is a proxy for :class:`~yt.visualization.plot_modifications.SphereCallback`.) Overplot a circle with designated center and radius with optional text. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) p.annotate_sphere([0.5, 0.5, 0.5], radius=(2, "kpc"), circle_args={"color": "black"}) p.save() .. _annotate-streamlines: Overplot Streamlines ~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_streamlines(self, field_x, field_y, *, linewidth=1.0, linewidth_upscaling=1.0, \ color=None, color_threshold=float('-inf'), factor=16, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.StreamlineCallback`.) Add streamlines to any plot, using the ``field_x`` and ``field_y`` from the associated data, using ``nx`` and ``ny`` starting points that are bounded by ``xstart`` and ``ystart``. To begin streamlines from the left edge of the plot, set ``start_at_xedge`` to ``True``; for the bottom edge, use ``start_at_yedge``. A line with the qmean vector magnitude will cover 1.0/``factor`` of the image. Additional keyword arguments are passed down to `matplotlib.axes.Axes.streamplot `_ .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") s = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) s.annotate_streamlines(("gas", "velocity_x"), ("gas", "velocity_y")) s.save() .. _annotate-line-integral-convolution: Overplot Line Integral Convolution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_line_integral_convolution(self, field_x, field_y, \ texture=None, kernellen=50., \ lim=(0.5,0.6), cmap='binary', \ alpha=0.8, const_alpha=False) (This is a proxy for :class:`~yt.visualization.plot_modifications.LineIntegralConvolutionCallback`.) Add line integral convolution to any plot, using the ``field_x`` and ``field_y`` from the associated data. A white noise background will be used for ``texture`` as default. Adjust the bounds of ``lim`` in the range of ``[0, 1]`` which applies upper and lower bounds to the values of line integral convolution and enhance the visibility of plots. When ``const_alpha=False``, alpha will be weighted spatially by the values of line integral convolution; otherwise a constant value of the given alpha is used. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") s = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) s.annotate_line_integral_convolution(("gas", "velocity_x"), ("gas", "velocity_y"), lim=(0.5, 0.65)) s.save() .. _annotate-text: Overplot Text ~~~~~~~~~~~~~ .. function:: annotate_text(self, pos, text, coord_system='data', \ text_args=None, inset_box_args=None) (This is a proxy for :class:`~yt.visualization.plot_modifications.TextLabelCallback`.) Overplot text on the plot at a specified position. If you desire an inset box around your text, set one with the inset_box_args dictionary keyword. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") s = yt.SlicePlot(ds, "z", ("gas", "density"), center="max", width=(10, "kpc")) s.annotate_text((2, 2), "Galaxy!", coord_system="plot") s.save() .. _annotate-title: Add a Title ~~~~~~~~~~~ .. function:: annotate_title(self, title='Plot') (This is a proxy for :class:`~yt.visualization.plot_modifications.TitleCallback`.) Accepts a ``title`` and adds it to the plot. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) p.annotate_title("Density Plot") p.save() .. _annotate-velocity: Overplot Quivers for the Velocity Field ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_velocity(self, factor=16, *, scale=None, scale_units=None, \ normalize=False, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.VelocityCallback`.) Adds a 'quiver' plot of velocity to the plot, skipping every ``factor`` datapoints in the discretization. ``scale`` is the data units per arrow length unit using ``scale_units``. If ``normalize`` is ``True``, the velocity fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. Additional arguments can be passed to the ``plot_args`` dictionary, see matplotlib.axes.Axes.quiver for more info. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.SlicePlot(ds, "z", ("gas", "density"), center="m", width=(10, "kpc")) p.annotate_velocity(headwidth=4) p.save() .. _annotate-timestamp: Add the Current Time and/or Redshift ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_timestamp(x_pos=None, y_pos=None, corner='lower_left',\ time=True, redshift=False, \ time_format='t = {time:.1f} {units}', \ time_unit=None, time_offset=None, \ redshift_format='z = {redshift:.2f}', \ draw_inset_box=False, coord_system='axis', \ text_args=None, inset_box_args=None) (This is a proxy for :class:`~yt.visualization.plot_modifications.TimestampCallback`.) Annotates the timestamp and/or redshift of the data output at a specified location in the image (either in a present corner, or by specifying (x,y) image coordinates with the x_pos, y_pos arguments. If no time_units are specified, it will automatically choose appropriate units. It allows for custom formatting of the time and redshift information, the specification of an inset box around the text, and changing the value of the timestamp via a constant offset. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) p.annotate_timestamp() p.save() .. _annotate-scale: Add a Physical Scale Bar ~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_scale(corner='lower_right', coeff=None, \ unit=None, pos=None, \ scale_text_format="{scale} {units}", \ max_frac=0.16, min_frac=0.015, \ coord_system='axis', text_args=None, \ size_bar_args=None, draw_inset_box=False, \ inset_box_args=None) (This is a proxy for :class:`~yt.visualization.plot_modifications.ScaleCallback`.) Annotates the scale of the plot at a specified location in the image (either in a preset corner, or by specifying (x,y) image coordinates with the pos argument. Coeff and units (e.g. 1 Mpc or 100 kpc) refer to the distance scale you desire to show on the plot. If no coeff and units are specified, an appropriate pair will be determined such that your scale bar is never smaller than min_frac or greater than max_frac of your plottable axis length. Additional customization of the scale bar is possible by adjusting the text_args and size_bar_args dictionaries. The text_args dictionary accepts matplotlib's font_properties arguments to override the default font_properties for the current plot. The size_bar_args dictionary accepts keyword arguments for the AnchoredSizeBar class in matplotlib's axes_grid toolkit. Finally, the format of the scale bar text can be adjusted using the scale_text_format keyword argument. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.SlicePlot(ds, "z", ("gas", "density"), center="c", width=(20, "kpc")) p.annotate_scale() p.save() .. _annotate-triangle-facets: Annotate Triangle Facets Callback ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_triangle_facets(triangle_vertices, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.TriangleFacetsCallback`.) This add a line collection of a SlicePlot's plane-intersection with the triangles to the plot. This callback is ideal for a dataset representing a geometric model of triangular facets. .. python-script:: import os import h5py import yt # Load data file ds = yt.load("MoabTest/fng_usrbin22.h5m") # Create the desired slice plot s = yt.SlicePlot(ds, "z", ("moab", "TALLY_TAG")) # get triangle vertices from file (in this case hdf5) # setup file path for yt test directory filename = os.path.join( yt.config.ytcfg.get("yt", "test_data_dir"), "MoabTest/mcnp_n_impr_fluka.h5m" ) f = h5py.File(filename, mode="r") coords = f["/tstt/nodes/coordinates"][:] conn = f["/tstt/elements/Tri3/connectivity"][:] points = coords[conn - 1] # Annotate slice-triangle intersection contours to the plot s.annotate_triangle_facets(points, colors="black") s.save() .. _annotate-mesh-lines: Annotate Mesh Lines Callback ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_mesh_lines(**kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.MeshLinesCallback`.) This draws the mesh line boundaries over a plot using a Matplotlib line collection. This callback is only useful for unstructured or semi-structured mesh datasets. .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e") sl = yt.SlicePlot(ds, "z", ("connect1", "nodal_aux")) sl.annotate_mesh_lines(color="black") sl.save() .. _annotate-ray: Overplot the Path of a Ray ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: annotate_ray(ray, *, arrow=False, **kwargs) (This is a proxy for :class:`~yt.visualization.plot_modifications.RayCallback`.) Adds a line representing the projected path of a ray across the plot. The ray can be either a :class:`~yt.data_objects.selection_objects.ray.YTOrthoRay`, :class:`~yt.data_objects.selection_objects.ray.YTRay`, or a Trident :class:`~trident.light_ray.LightRay` object. annotate_ray() will properly account for periodic rays across the volume. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") oray = ds.ortho_ray(0, (0.3, 0.4)) ray = ds.ray((0.1, 0.2, 0.3), (0.6, 0.7, 0.8)) p = yt.ProjectionPlot(ds, "z", ("gas", "density")) p.annotate_ray(oray) p.annotate_ray(ray) p.save() Applying filters on the final image ----------------------------------- It is also possible to operate on the plotted image directly by using one of the fixed resolution buffer filter as described in :ref:`frb-filters`. Note that it is necessary to call the plot object's ``refresh`` method to apply filters. .. python-script:: import yt ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030') p = yt.SlicePlot(ds, 'z', 'density') p.frb.apply_gauss_beam(sigma=30) p.refresh() p.save() .. _extend-annotations: Extending annotations methods ----------------------------- New ``annotate_`` methods can be added to plot objects at runtime (i.e., without modifying yt's source code) by subclassing the base ``PlotCallback`` class. This is the recommended way to add custom and unique annotations to yt plots, as it can be done through local plugins, individual scripts, or even external packages. Here's a minimal example: .. python-script:: import yt from yt.visualization.api import PlotCallback class TextToPositionCallback(PlotCallback): # bind a new `annotate_text_to_position` plot method _type_name = "text_to_position" def __init__(self, text, x, y): # this method can have arbitrary arguments # and should store them without alteration, # but not run expensive computations self.text = text self.position = (x, y) def __call__(self, plot): # this method's signature is required # this is where we perform potentially expensive operations # the plot argument exposes matplotlib objects: # - plot._axes is a matplotlib.axes.Axes object # - plot._figure is a matplotlib.figure.Figure object plot._axes.annotate( self.text, xy=self.position, xycoords="data", xytext=(0.2, 0.6), textcoords="axes fraction", color="white", fontsize=30, arrowprops=dict(facecolor="black", shrink=0.05), ) ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.SlicePlot(ds, "z", "density") p.annotate_text_to_position("Galactic center !", x=0, y=0) p.save() yt-project-yt-f043ac8/doc/source/visualizing/colormaps/000077500000000000000000000000001510711153200232625ustar00rootroot00000000000000yt-project-yt-f043ac8/doc/source/visualizing/colormaps/cmap_images.py000066400000000000000000000006711510711153200261050ustar00rootroot00000000000000import matplotlib as mpl import yt # Load the dataset. ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Create projections using each colormap available. p = yt.ProjectionPlot(ds, "z", "density", weight_field="density", width=0.4) for cmap in mpl.colormaps: if cmap.startswith("idl"): continue p.set_cmap(field="density", cmap=cmap) p.annotate_title(cmap) p.save(f"Projection_{cmap.replace(' ', '_')}.png") yt-project-yt-f043ac8/doc/source/visualizing/colormaps/index.rst000066400000000000000000000160241510711153200251260ustar00rootroot00000000000000.. _colormaps: Colormaps ========= There are several colormaps available for yt. yt includes all of the matplotlib colormaps as well for nearly all functions. Individual visualization functions usually allow you to specify a colormap with the ``cmap`` flag. In yt 3.3, we changed the default yt colormap from ``algae`` to ``arbre``. This colormap was designed and voted on by the yt community and is designed to be easier for people with different color sensitivities as well as when printed in black and white. In 3.3, additional colormaps ``dusk``, ``kelp`` and ``octarine`` were also added, following the same guidelines. For a deeper dive into colormaps, see the SciPy 2015 talk by Stéfan van der Walt and Nathaniel Smith about the new matplotlib colormap ``viridis`` at https://www.youtube.com/watch?v=xAoljeRJ3lU . To specify a different default colormap (including ``viridis``), in your yt configuration file (see :ref:`configuration-file`) you can set the value ``default_colormap`` to the name of the colormap you would like. In contrast to previous versions of yt, starting in 3.3 yt no longer overrides any matplotlib defaults and instead only applies the colormap to yt-produced plots. .. _install-palettable: Palettable and ColorBrewer2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ While colormaps that employ a variety of colors often look attractive, they are not always the best choice to convey information to one's audience. There are numerous `articles `_ and `presentations `_ that discuss how rainbow-based colormaps fail with regard to black-and-white reproductions, colorblind audience members, and confusing in color ordering. Depending on the application, the consensus seems to be that gradients between one or two colors are the best way for the audience to extract information from one's figures. Many such colormaps are found in palettable. If you have installed `palettable `_ (formerly brewer2mpl), you can also access the discrete colormaps available to that package including those from `colorbrewer `_. Install `palettable `_ with ``pip install palettable``. To access these maps in yt, instead of supplying the colormap name, specify a tuple of the form (name, type, number), for example ``('RdBu', 'Diverging', 9)``. These discrete colormaps will not be interpolated, and can be useful for creating colorblind/printer/grayscale-friendly plots. For more information, visit `http://colorbrewer2.org `_. .. _custom-colormaps: Making and Viewing Custom Colormaps ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ yt can also accommodate custom colormaps using the :func:`~yt.visualization.color_maps.make_colormap` function These custom colormaps can be made to an arbitrary level of complexity. You can make these on the fly for each yt session, or you can store them in your :ref:`plugin-file` for access to them in every future yt session. The example below creates two custom colormaps, one that has three equally spaced bars of blue, white and red, and the other that interpolates in increasing lengthed intervals from black to red, to green, to blue. These will be accessible for the rest of the yt session as 'french_flag' and 'weird'. See :func:`~yt.visualization.color_maps.make_colormap` and :func:`~yt.visualization.color_maps.show_colormaps` for more details. .. code-block:: python yt.make_colormap( [("blue", 20), ("white", 20), ("red", 20)], name="french_flag", interpolate=False, ) yt.make_colormap( [("black", 5), ("red", 10), ("green", 20), ("blue", 0)], name="weird", interpolate=True, ) yt.show_colormaps(subset=["french_flag", "weird"], filename="cmaps.png") All Colormaps (including matplotlib) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a chart of all of the yt and matplotlib colormaps available. In addition to each colormap displayed here, you can access its "reverse" by simply appending a ``"_r"`` to the end of the colormap name. .. image:: ../_images/all_colormaps.png :width: 512 Native yt Colormaps ~~~~~~~~~~~~~~~~~~~ .. image:: ../_images/native_yt_colormaps.png :width: 512 Displaying Colormaps Locally ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To display the most up to date colormaps locally, you can use the :func:`~yt.visualization.color_maps.show_colormaps` function. By default, you'll see every colormap available to you, but you can specify subsets of colormaps to display, either as just the ``yt_native`` colormaps, or by specifying a list of colormap names. This will display all the colormaps available in a local window: .. code-block:: python import yt yt.show_colormaps() or to output the original yt colormaps to an image file, try: .. code-block:: python import yt yt.show_colormaps( subset=[ "cmyt.algae", "cmyt.arbre", "cmyt.dusk", "cmyt.kelp", "cmyt.octarine", "cmyt.pastel", ], filename="yt_native.png", ) .. note :: Since yt 4.1, yt native colormaps are shipped as a separate package `cmyt `_ that can be used outside yt itself. Within `yt` functions, these colormaps can still be referenced without the ``"cmyt."`` prefix. However, there is no guarantee that this will work in upcoming version of matplotlib, so our recommentation is to keep the prefix at all times to retain forward compatibility. yt also retains compatibility with names these colormaps were formerly known as (for instance ``cmyt.pastel`` used to be named ``kamae``). Applying a Colormap to your Rendering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ All of the visualization functions in yt have a keyword allowing you to manually specify a specific colormap. For example: .. code-block:: python yt.write_image(im, "output.png", cmap_name="jet") If you're using the Plot Window interface (e.g. SlicePlot, ProjectionPlot, etc.), it's even easier than that. Simply create your rendering, and you can quickly swap the colormap on the fly after the fact with the ``set_cmap`` callback: .. code-block:: python ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot(ds, "z", ("gas", "density")) p.set_cmap(field=("gas", "density"), cmap="turbo") p.save("proj_with_jet_cmap.png") p.set_cmap(field=("gas", "density"), cmap="hot") p.save("proj_with_hot_cmap.png") For more information about the callbacks available to Plot Window objects, see :ref:`callbacks`. Examples of Each Colormap ~~~~~~~~~~~~~~~~~~~~~~~~~ To give the reader a better feel for how a colormap appears once it is applied to a dataset, below we provide a library of identical projections of an isolated galaxy where only the colormap has changed. They use the sample dataset "IsolatedGalaxy" available at `https://yt-project.org/data `_. .. yt_colormaps:: cmap_images.py yt-project-yt-f043ac8/doc/source/visualizing/geographic_projections_and_transforms.rst000066400000000000000000000142551510711153200316530ustar00rootroot00000000000000.. _geographic_projections_and_transforms: Geographic Projections and Transforms ===================================== Geographic data that is on a sphere can be visualized by projecting that data onto a representation of that sphere flattened into 2d space. There exist a number of projection types, which can be found in the `the cartopy documentation `_. With support from `cartopy `_, ``yt`` now supports these projection types for geographically loaded data. Underlying data is assumed to have a transform of `PlateCarree `__, which is data on a flattened, rectangular, latitude/longitude grid. This is a a typical format for geographic data. The distinction between the data transform and projection is worth noting. The data transform is what system your data is defined with and the data projection is what the resulting plot will display. For more information on this difference, refer to `the cartopy documentation on these differences `_. If your data is not of this form, feel free to open an issue or file a pull request on the ``yt`` github page for this feature. It should be noted that these projections are not the same as yt's ProjectionPlot. For more information on yt's projection plots, see :ref:`projection-types`. .. _install-cartopy: Installing Cartopy ^^^^^^^^^^^^^^^^^^ In order to access the geographic projection functionality, you will need to have an installation of ``cartopy`` available on your machine. Please refer to `Cartopy's documentation for detailed instructions `_ Using Basic Transforms ^^^^^^^^^^^^^^^^^^^^^^^ As mentioned above, the default data transform is assumed to be of `PlateCarree `__, which is data on a flattened, rectangular, latitude/longitude grid. To set something other than ``PlateCarree``, the user can access the dictionary in the coordinate handler that defines the coordinate transform to change the default transform type. Because the transform describes the underlying data coordinate system, the loaded dataset will carry this newly set attribute and all future plots will have the user-defined data transform. Also note that the dictionary is ordered by axis type. Because slicing along the altitude may differ from, say, the latitude axis, we may choose to have different transforms for each axis. .. code-block:: python ds = yt.load_uniform_grid(data, sizes, 1.0, geometry=("geographic", dims), bbox=bbox) ds.coordinates.data_transform["altitude"] = "Miller" p = yt.SlicePlot(ds, "altitude", "AIRDENS") In this example, the ``data_transform`` kwarg has been changed from its default of ``PlateCarree`` to ``Miller``. You can check that you have successfully changed the defaults by inspecting the ``data_transform`` and ``data_projection`` dictionaries in the coordinate handler. For this dataset, that would be accessed by: .. code-block:: python print(ds.coordinates.data_transform["altitude"]) print(ds.coordinates.data_projection["altitude"]) Using Basic Projections ^^^^^^^^^^^^^^^^^^^^^^^ All of the transforms available in ``Cartopy`` v0.15 and above are accessible with this functionality. The next few examples will use a GEOS dataset accessible from the ``yt`` data downloads page. For details about loading this data, please see :doc:`../cookbook/geographic_xforms_and_projections`. If a geographic dataset is loaded without any defined projection the default option of ``Mollweide`` will be displayed. .. code-block:: python ds = yt.load_uniform_grid(data, sizes, 1.0, geometry=("geographic", dims), bbox=bbox) p = yt.SlicePlot(ds, "altitude", "AIRDENS") If an option other than ``Mollweide`` is desired, the plot projection type can be set with the ``set_mpl_projection`` function. The next code block illustrates how to set the projection to a ``Robinson`` projection from the default ``PlateCarree``. .. code-block:: python ds = yt.load_uniform_grid(data, sizes, 1.0, geometry=("geographic", dims), bbox=bbox) p = yt.SlicePlot(ds, "altitude", "AIRDENS") p.set_mpl_projection("Robinson") p.show() The axes attributes of the plot can be accessed to add in annotations, such as coastlines. The axes are matplotlib ``GeoAxes`` so any of the annotations available with matplotlib should be available for customization. Here a ``Robinson`` plot is made with coastline annotations. .. code-block:: python p.set_mpl_projection("Robinson") p.render() p.plots["AIRDENS"].axes.set_global() p.plots["AIRDENS"].axes.coastlines() p.show() ``p.render()`` is required here to access the plot axes. When a new projection is called the plot axes are reset and are not available unless set up again. Additional arguments can be passed to the projection function for further customization. If additional arguments are desired, then rather than passing a string of the projection name, one would pass a 2 or 3-item tuple, the first item of the tuple corresponding to a string of the transform name, and the second and third items corresponding to the args and kwargs of the transform, respectively. Alternatively, a user can pass a transform object rather than a string or tuple. This allows for users to create and define their own transforms, beyond what is available in cartopy. The type must be a cartopy GeoAxes object or a matplotlib transform object. For creating custom transforms, see `the matplotlib example `_. The function ``set_mpl_projection()`` accepts several input types for varying levels of customization: .. code-block:: python set_mpl_projection("ProjectionType") set_mpl_projection(("ProjectionType", (args))) set_mpl_projection(("ProjectionType", (args), {kwargs})) set_mpl_projection(cartopy.crs.PlateCarree()) Further examples of using the geographic transforms with this dataset can be found in :doc:`../cookbook/geographic_xforms_and_projections`. yt-project-yt-f043ac8/doc/source/visualizing/index.rst000066400000000000000000000011521510711153200231230ustar00rootroot00000000000000.. _visualizing: Visualizing Data ================ yt comes with a number of ways for visualizing one's data including slices, projections, line plots, profiles, phase plots, volume rendering, 3D surfaces, streamlines, and a google-maps-like interface for exploring one's dataset interactively. .. toctree:: :maxdepth: 2 plots callbacks manual_plotting volume_rendering unstructured_mesh_rendering interactive_data_visualization visualizing_particle_datasets_with_firefly sketchfab mapserver streamlines colormaps/index geographic_projections_and_transforms FITSImageData yt-project-yt-f043ac8/doc/source/visualizing/interactive_data_visualization.rst000066400000000000000000000007331510711153200303070ustar00rootroot00000000000000.. _interactive_data_visualization: Interactive Data Visualization ============================== The interactive, OpenGL-based volume rendering system for yt has been exported into its own package, called ``yt_idv``. Documentation, including installation instructions, can be found at `its website `_, and the source code is hosted under the yt-project organization on github at `yt_idv `_. yt-project-yt-f043ac8/doc/source/visualizing/manual_plotting.rst000066400000000000000000000135341510711153200252200ustar00rootroot00000000000000.. _manual-plotting: Using the Manual Plotting Interface =================================== Sometimes you need a lot of flexibility in creating plots. While the :class:`~yt.visualization.plot_window.PlotWindow` provides an easy to use object that can create nice looking, publication quality plots with a minimum of effort, there are often times when its ease of use conflicts with your need to change the font only on the x-axis, or whatever your need/desire/annoying coauthor requires. To that end, yt provides a number of ways of getting the raw data that goes into a plot to you in the form of a one or two dimensional dataset that you can plot using any plotting method you like. matplotlib or another python library are easiest, but these methods allow you to take your data and plot it in gnuplot, or any unnamed commercial plotting packages. Note that the index object associated with your snapshot file contains a list of plots you've made in ``ds.plots``. .. _fixed-resolution-buffers: Slice, Projections, and other Images: The Fixed Resolution Buffer ----------------------------------------------------------------- For slices and projects, yt provides a manual plotting interface based on the :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer` (hereafter referred to as FRB) object. Despite its somewhat unwieldy name, at its heart, an FRB is a very simple object: it's essentially a window into your data: you give it a center and a width or a left and right edge, and an image resolution, and the FRB returns a fully pixelized image. The simplest way to generate an FRB is to use the ``.to_frb(width, resolution, center=None)`` method of any data two-dimensional data object: .. python-script:: import matplotlib matplotlib.use("Agg") import numpy as np from matplotlib import pyplot as plt import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") _, c = ds.find_max(("gas", "density")) proj = ds.proj(("gas", "density"), 0) width = (10, "kpc") # we want a 1.5 mpc view res = [1000, 1000] # create an image with 1000x1000 pixels frb = proj.to_frb(width, res, center=c) plt.imshow(np.array(frb["gas", "density"])) plt.savefig("my_perfect_figure.png") Note that in the above example the axes tick marks indicate pixel indices. If you want to represent physical distances on your plot axes, you will need to use the ``extent`` keyword of the ``imshow`` function. The FRB is a very small object that can be deleted and recreated quickly (in fact, this is how ``PlotWindow`` plots work behind the scenes). Furthermore, you can add new fields in the same "window", and each of them can be plotted with their own zlimit. This is quite useful for creating a mosaic of the same region in space with Density, Temperature, and x-velocity, for example. Each of these quantities requires a substantially different set of limits. A more complex example, showing a few yt helper functions that can make setting up multiple axes with colorbars easier than it would be using only matplotlib can be found in the :ref:`advanced-multi-panel` cookbook recipe. .. _frb-filters: Fixed Resolution Buffer Filters ------------------------------- The FRB can be modified by using set of predefined filters in order to e.g. create realistically looking, mock observation images out of simulation data. Applying filter is an irreversible operation, hence the order in which you are using them matters. .. python-script:: import matplotlib matplotlib.use("Agg") from matplotlib import pyplot as plt import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = ds.slice("z", 0.5) frb = slc.to_frb((20, "kpc"), 512) frb.apply_gauss_beam(nbeam=30, sigma=2.0) frb.apply_white_noise(5e-23) plt.imshow(frb["gas", "density"].d) plt.savefig("frb_filters.png") Currently available filters: Gaussian Smoothing ~~~~~~~~~~~~~~~~~~ .. function:: apply_gauss_beam(self, nbeam=30, sigma=2.0) (This is a proxy for :class:`~yt.visualization.fixed_resolution_filters.FixedResolutionBufferGaussBeamFilter`.) This filter convolves the FRB with 2d Gaussian that is "nbeam" pixel wide and has standard deviation "sigma". White Noise ~~~~~~~~~~~ .. function:: apply_white_noise(self, bg_lvl=None) (This is a proxy for :class:`~yt.visualization.fixed_resolution_filters.FixedResolutionBufferWhiteNoiseFilter`.) This filter adds white noise with the amplitude "bg_lvl" to the FRB. If "bg_lvl" is not present, 10th percentile of the FRB's values is used instead. .. _manual-line-plots: Line Plots ---------- This is perhaps the simplest thing to do. yt provides a number of one dimensional objects, and these return a 1-D numpy array of their contents with direct dictionary access. As a simple example, take a :class:`~yt.data_objects.selection_data_containers.YTOrthoRay` object, which can be created from a index by calling ``ds.ortho_ray(axis, center)``. .. python-script:: import matplotlib matplotlib.use("Agg") import numpy as np from matplotlib import pyplot as plt import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") _, c = ds.find_max(("gas", "density")) ax = 0 # take a line cut along the x axis # cutting through the y0,z0 such that we hit the max density ray = ds.ortho_ray(ax, (c[1], c[2])) # Sort the ray values by 'x' so there are no discontinuities # in the line plot srt = np.argsort(ray["index", "x"]) plt.subplot(211) plt.semilogy(np.array(ray["index", "x"][srt]), np.array(ray["gas", "density"][srt])) plt.ylabel("density") plt.subplot(212) plt.semilogy(np.array(ray["index", "x"][srt]), np.array(ray["gas", "temperature"][srt])) plt.xlabel("x") plt.ylabel("temperature") plt.savefig("den_temp_xsweep.png") Of course, you'll likely want to do something more sophisticated than using the matplotlib defaults, but this gives the general idea. yt-project-yt-f043ac8/doc/source/visualizing/mapserver.rst000066400000000000000000000027111510711153200240220ustar00rootroot00000000000000.. _mapserver: Mapserver - A Google-Maps-like Interface to your Data ----------------------------------------------------- The mapserver is an experimental feature. It's based on `Leaflet `_, a library written to create zoomable, map-tile interfaces. (Similar to Google Maps.) yt provides everything you need to start up a web server that will interactively re-pixelize an adaptive image. This means you can explore your datasets in a fully pan-n-zoom interface. .. note:: Previous versions of yt bundled the necessary dependencies, but with more recent released you will need to install the package ``bottle`` via pip or conda. To start up the mapserver, you can use the command yt (see :ref:`command-line`) with the ``mapserver`` subcommand. It takes several of the same options and arguments as the ``plot`` subcommand. For instance: .. code-block:: bash yt mapserver DD0050/DD0050 That will take a slice along the x axis at the center of the domain. The field, projection, weight and axis can all be specified on the command line. When you do this, it will spawn a micro-webserver on your localhost, and output the URL to connect to standard output. You can connect to it (or create an SSH tunnel to connect to it) and explore your data. Double-clicking zooms, and dragging drags. .. image:: _images/mapserver.png :scale: 50% This is also functional on touch-capable devices such as Android Tablets and iPads/iPhones. yt-project-yt-f043ac8/doc/source/visualizing/plots.rst000066400000000000000000002552721510711153200231730ustar00rootroot00000000000000 .. _how-to-make-plots: How to Make Plots ================= .. note:: In this document, and the rest of the yt documentation, we use field tuples; for instance, we specify density as ``("gas", "density")`` whereas in previous versions of this document we typically just used ``"density"``. While the latter will still work in many or most cases, and may suffice for your purposes, for ensuring we explicitly avoid ambiguity we use field tuples here. In this section we explain how to use yt to create visualizations of simulation data, derived fields, and the data produced by yt analysis objects. For details about the data extraction and algorithms used to produce the image and analysis data, please see the yt `method paper `_. There are also many example scripts in :ref:`cookbook`. The :class:`~yt.visualization.plot_window.PlotWindow` interface is useful for taking a quick look at simulation outputs. Simple mechanisms exist for making plots of slices, projections, 1D spatial line plots, 1D profiles, and 2D profiles (phase plots), all of which are described below. .. _viewing-plots: Viewing Plots ------------- yt uses an environment neutral plotting mechanism that detects the appropriate matplotlib configuration for a given environment, however it defaults to a basic renderer. To utilize interactive plots in matplotlib supported environments (Qt, GTK, WX, etc.) simply call the ``toggle_interactivity()`` function. Below is an example in a jupyter notebook environment, but the same command should work in other environments as well: .. code-block:: IPython %matplotlib notebook import yt yt.toggle_interactivity() .. _simple-inspection: Slices & Projections -------------------- If you need to take a quick look at a single simulation output, yt provides the :class:`~yt.visualization.plot_window.PlotWindow` interface for generating annotated 2D visualizations of simulation data. You can create a :class:`~yt.visualization.plot_window.PlotWindow` plot by supplying a dataset, a list of fields to plot, and a plot center to create a :class:`~yt.visualization.plot_window.AxisAlignedSlicePlot`, :class:`~yt.visualization.plot_window.OffAxisSlicePlot`, :class:`~yt.visualization.plot_window.ProjectionPlot`, or :class:`~yt.visualization.plot_window.OffAxisProjectionPlot`. Plot objects use yt data objects to extract the maximum resolution data available to render a 2D image of a field. Whenever a two-dimensional image is created, the plotting object first obtains the necessary data at the *highest resolution*. Every time an image is requested of it -- for instance, when the width or field is changed -- this high-resolution data is then pixelized and placed in a buffer of fixed size. This is accomplished behind the scenes using :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer`. The :class:`~yt.visualization.plot_window.PlotWindow` class exposes the underlying matplotlib `figure `_ and `axes `_ objects, making it easy to customize your plots and add new annotations. See :ref:`matplotlib-customization` for more information. .. _slice-plots: Slice Plots ~~~~~~~~~~~ The quickest way to plot a slice of a field through your data is via :class:`~yt.visualization.plot_window.SlicePlot`. These plots are generally quicker than projections because they only need to read and process a slice through the dataset. The following script plots a slice through the density field along the z-axis centered on the center of the simulation box in a simulation dataset we've opened and stored in ``ds``: .. code-block:: python slc = yt.SlicePlot(ds, "z", ("gas", "density")) slc.save() These two commands will create a slice object and store it in a variable we've called ``slc``. Since this plot is aligned with the simulation coordinate system, ``slc`` is an instance of :class:`~yt.visualization.plot_window.AxisAlignedSlicePlot`. We then call the ``save()`` function, which automatically saves the plot in png image format with an automatically generated filename. If you don't want the slice object to stick around, you can accomplish the same thing in one line: .. code-block:: python yt.SlicePlot(ds, "z", ("gas", "density")).save() It's nice to keep the slice object around if you want to modify the plot. By default, the plot width will be set to the size of the simulation box. To zoom in by a factor of ten, you can call the zoom function attached to the slice object: .. code-block:: python slc = yt.SlicePlot(ds, "z", ("gas", "density")) slc.zoom(10) slc.save("zoom") This will save a new plot to disk with a different filename - prepended with 'zoom' instead of the name of the dataset. If you want to set the width manually, you can do that as well. For example, the following sequence of commands will create a slice, set the width of the plot to 10 kiloparsecs, and save it to disk, with the filename prefix being ``10kpc`` and the rest determined by the field, visualization method, etc. .. code-block:: python from yt.units import kpc slc = yt.SlicePlot(ds, "z", ("gas", "density")) slc.set_width(10 * kpc) slc.save("10kpc") The plot width can be specified independently along the x and y direction by passing a tuple of widths. An individual width can also be represented using a ``(value, unit)`` tuple. The following sequence of commands all equivalently set the width of the plot to 200 kiloparsecs in the ``x`` and ``y`` direction. .. code-block:: python from yt.units import kpc slc.set_width(200 * kpc) slc.set_width((200, "kpc")) slc.set_width((200 * kpc, 200 * kpc)) The ``SlicePlot`` also optionally accepts the coordinate to center the plot on and the width of the plot: .. code-block:: python yt.SlicePlot( ds, "z", ("gas", "density"), center=[0.2, 0.3, 0.8], width=(10, "kpc") ).save() Note that, by default, :class:`~yt.visualization.plot_window.SlicePlot` shifts the coordinates on the axes such that the origin is at the center of the slice. To instead use the coordinates as defined in the dataset, use the optional argument: ``origin="native"`` If supplied without units, the center is assumed by in code units. There are also the following alternative options for the ``center`` keyword: * ``"center"``, ``"c"``: the domain center * ``"left"``, ``"l"``, ``"right"`` ``"r"``: the domain's left/right edge along the normal direction (``SlicePlot``'s second argument). Remaining axes use their respective domain center values. * ``"min"``: the position of the minimum density * ``"max"``, ``"m"``: the position of the maximum density * ``"min/max_"``: the position of the minimum/maximum in the first field matching field name * ``("min", field)``: the position of the minimum of ``field`` * ``("max", field)``: the position of the maximum of ``field`` where for the last two objects any spatial field, such as ``"density"``, ``"velocity_z"``, etc., may be used, e.g. ``center=("min", ("gas", "temperature"))``. ``"left"`` and ``"right"`` are not allowed for off-axis slices. The effective resolution of the plot (i.e. the number of resolution elements in the image itself) can be controlled with the ``buff_size`` argument: .. code-block:: python yt.SlicePlot(ds, "z", ("gas", "density"), buff_size=(1000, 1000)) Here is an example that combines all of the options we just discussed. .. python-script:: import yt from yt.units import kpc ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot( ds, "z", ("gas", "density"), center=[0.5, 0.5, 0.5], width=(20, "kpc"), buff_size=(1000, 1000), ) slc.save() The above example will display an annotated plot of a slice of the Density field in a 20 kpc square window centered on the coordinate (0.5, 0.5, 0.5) in the x-y plane. The axis to slice along is keyed to the letter 'z', corresponding to the z-axis. Finally, the image is saved to a png file. Conceptually, you can think of the plot object as an adjustable window into the data. For example: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "pressure"), center="c") slc.save() slc.zoom(30) slc.save("zoom") will save a plot of the pressure field in a slice along the z axis across the entire simulation domain followed by another plot that is zoomed in by a factor of 30 with respect to the original image. Both plots will be centered on the center of the simulation box. With these sorts of manipulations, one can easily pan and zoom onto an interesting region in the simulation and adjust the boundaries of the region to visualize on the fly. If you want to slice through a subset of the full dataset volume, you can use the ``data_source`` keyword with a :ref:`data object ` or a :ref:`cut region `. See :class:`~yt.visualization.plot_window.AxisAlignedSlicePlot` for the full class description. .. _plot-2d: Plots of 2D Datasets ~~~~~~~~~~~~~~~~~~~~ If you have a two-dimensional cartesian, cylindrical, or polar dataset, :func:`~yt.visualization.plot_window.plot_2d` is a way to make a plot within the dataset's plane without having to specify the axis, which in this case is redundant. Otherwise, ``plot_2d`` accepts the same arguments as ``SlicePlot``. The one other difference is that the ``center`` keyword argument can be a two-dimensional coordinate instead of a three-dimensional one: .. python-script:: import yt ds = yt.load("WindTunnel/windtunnel_4lev_hdf5_plt_cnt_0030") p = yt.plot_2d(ds, ("gas", "density"), center=[1.0, 0.4]) p.set_log(("gas", "density"), False) p.save() See :func:`~yt.visualization.plot_window.plot_2d` for the full description of the function and its keywords. .. _off-axis-slices: Off Axis Slices ~~~~~~~~~~~~~~~ Off axis slice plots can be generated in much the same way as grid-aligned slices. Off axis slices use :class:`~yt.data_objects.selection_data_containers.YTCuttingPlane` to slice through simulation domains at an arbitrary oblique angle. A :class:`~yt.visualization.plot_window.OffAxisSlicePlot` can be instantiated by specifying a dataset, the normal to the cutting plane, and the name of the fields to plot. Just like an :class:`~yt.visualization.plot_window.AxisAlignedSlicePlot`, an :class:`~yt.visualization.plot_window.OffAxisSlicePlot` can be created via the :class:`~yt.visualization.plot_window.SlicePlot` class. For example: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") L = [1, 1, 0] # vector normal to cutting plane north_vector = [-1, 1, 0] cut = yt.SlicePlot(ds, L, ("gas", "density"), width=(25, "kpc"), north_vector=north_vector) cut.save() In this case, a normal vector for the cutting plane is supplied in the second argument. Optionally, a ``north_vector`` can be specified to fix the orientation of the image plane. .. note:: Not every data types have support for off-axis slices yet. Currently, this operation is supported for grid based and SPH data with cartesian geometry. In some cases an off-axis projection over a thin region might be used instead. .. _projection-plots: Projection Plots ~~~~~~~~~~~~~~~~ Using a fast adaptive projection, yt is able to quickly project simulation data along the coordinate axes. Projection plots are created by instantiating a :class:`~yt.visualization.plot_window.ProjectionPlot` object. For example: .. python-script:: import yt from yt.units import kpc ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") prj = yt.ProjectionPlot( ds, "z", ("gas", "temperature"), width=25 * kpc, weight_field=("gas", "density"), buff_size=(1000, 1000), ) prj.save() will create a density-weighted projection of the temperature field along the x axis with 1000 resolution elements per side, plot it, and then save the plot to a png image file. Like :ref:`slice-plots`, annotations and modifications can be applied after creating the ``ProjectionPlot`` object. Annotations are described in :ref:`callbacks`. See :class:`~yt.visualization.plot_window.ProjectionPlot` for the full class description. If you want to project through a subset of the full dataset volume, you can use the ``data_source`` keyword with a :ref:`data object `. The :ref:`thin-slice-projections` recipes demonstrates this functionality. .. note:: Not every data types have support for off-axis projections yet. Currently, this operation is supported for grid based data with cartesian geometry, as well as SPH particles data. .. _projection-types: Types of Projections """""""""""""""""""" There are several different methods of projections that can be made either when creating a projection with :meth:`~yt.static_output.Dataset.proj` or when making a :class:`~yt.visualization.plot_window.ProjectionPlot`. In either construction method, set the ``method`` keyword to be one of the following: ``integrate`` (unweighted): +++++++++++++++++++++++++++ This is the default projection method. It simply integrates the requested field :math:`f({\bf x})` along a line of sight :math:`\hat{\bf n}` , given by the axis parameter (e.g. :math:`\hat{\bf i},\hat{\bf j},` or :math:`\hat{\bf k}`). The units of the projected field :math:`g({\bf X})` will be the units of the unprojected field :math:`f({\bf x})` multiplied by the appropriate length unit, e.g., density in :math:`\mathrm{g\ cm^{-3}}` will be projected to :math:`\mathrm{g\ cm^{-2}}`. .. math:: g({\bf X}) = {\int\ {f({\bf x})\hat{\bf n}\cdot{d{\bf x}}}} ``integrate`` (weighted): +++++++++++++++++++++++++ When using the ``integrate`` method, a ``weight_field`` argument may also be specified, which will produce a weighted projection. :math:`w({\bf x})` is the field used as a weight. One common example would be to weight the "temperature" field by the "density" field. In this case, the units of the projected field are the same as the unprojected field. .. math:: g({\bf X}) = \int\ {f({\bf x})\tilde{w}({\bf x})\hat{\bf n}\cdot{d{\bf x}}} where the "~" over :math:`w({\bf x})` reflects the fact that it has been normalized like so: .. math:: \tilde{w}({\bf x}) = \frac{w({\bf x})}{\int\ {w({\bf x})\hat{\bf n}\cdot{d{\bf x}}}} For weighted projections using the ``integrate`` method, it is also possible to project the standard deviation of a field. In this case, the projected field is mathematically given by: .. math:: g({\bf X}) = \left[\int\ {f({\bf x})^2\tilde{w}({\bf x})\hat{\bf n}\cdot{d{\bf x}}} - \left(\int\ {f({\bf x})\tilde{w}({\bf x})\hat{\bf n}\cdot{d{\bf x}}}\right)^2\right]^{1/2} in order to make a weighted projection of the standard deviation of a field along a line of sight, the ``moment`` keyword argument should be set to 2. ``max``: ++++++++ This method picks out the maximum value of a field along the line of sight given by the axis parameter. ``min``: ++++++++ This method picks out the minimum value of a field along the line of sight given by the axis parameter. ``sum``: ++++++++ This method is the same as ``integrate``, except that it does not multiply by a path length when performing the integration, and is just a straight summation of the field along the given axis. The units of the projected field will be the same as those of the unprojected field. This method is typically only useful for datasets such as 3D FITS cubes where the third axis of the dataset is something like velocity or frequency, and should *only* be used with fixed-resolution grid-based datasets. .. _off-axis-projections: Off Axis Projection Plots ~~~~~~~~~~~~~~~~~~~~~~~~~ Internally, off axis projections are created using :ref:`camera` by applying the :class:`~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction`. In this use case, the volume renderer casts a set of plane parallel rays, one for each pixel in the image. The data values along each ray are summed, creating the final image buffer. For SPH datsets, the coordinates are instead simply rotated before the axis-aligned projection function is applied. .. _off-axis-projection-function: To avoid manually creating a camera and setting the transfer function, yt provides the :func:`~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection` function, which wraps the camera interface to create an off axis projection image buffer. These images can be saved to disk or used in custom plots. This snippet creates an off axis projection through a simulation. .. python-script:: import numpy as np import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") L = [1, 1, 0] # vector normal to cutting plane W = [0.02, 0.02, 0.02] c = [0.5, 0.5, 0.5] N = 512 image = yt.off_axis_projection(ds, c, L, W, N, ("gas", "density")) yt.write_image(np.log10(image), "%s_offaxis_projection.png" % ds) Here, ``W`` is the width of the projection in the x, y, *and* z directions. One can also generate annotated off axis projections using :class:`~yt.visualization.plot_window.ProjectionPlot`. These plots can be created in much the same way as an ``OffAxisSlicePlot``, requiring only an open dataset, a direction to project along, and a field to project. For example: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") L = [1, 1, 0] # vector normal to cutting plane north_vector = [-1, 1, 0] prj = yt.ProjectionPlot( ds, L, ("gas", "density"), width=(25, "kpc"), north_vector=north_vector ) prj.save() ``OffAxisProjectionPlot`` objects can also be created with a number of keyword arguments, as described in :class:`~yt.visualization.plot_window.OffAxisProjectionPlot`. Like on-axis projections, the projection of the standard deviation of a weighted field can be created by setting ``moment=2`` in the call to :class:`~yt.visualization.plot_window.ProjectionPlot`. .. _slices-and-projections-in-spherical-geometry: Slice Plots and Projection Plots in Spherical Geometry ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What to expect when plotting data in spherical geometry? Here we explain the notations and projections system yt uses for to render 2D images of spherical data. The native spherical coordinates are - the spherical radius :math:`r` - the colatitude :math:`\theta`, defined between :math:`0` and :math:`\pi` - the azimuth :math:`\varphi`, defined between :math:`0` and :math:`2\pi` :math:`\varphi`-normal slices are represented in the poloidal plane, with axes :math:`R, z`, where - :math:`R = r \sin \theta` is the cylindrical radius - :math:`z = r \cos \theta` is the elevation .. python-script:: import yt ds = yt.load_sample("KeplerianDisk", unit_system="cgs") slc = yt.SlicePlot(ds, "phi", ("gas", "density")) slc.save() :math:`\theta`-normal slices are represented in a :math:`x/\sin(\theta)` VS :math:`y/\sin(\theta)` plane, where - :math:`x = R \cos \varphi` - :math:`y = R \sin \varphi` are the cartesian plane coordinates .. python-script:: import yt ds = yt.load_sample("KeplerianDisk", unit_system="cgs") slc = yt.SlicePlot(ds, "theta", ("gas", "density")) slc.save() Finally, :math:`r`-normal slices are represented following a `Aitoff-Hammer projection `_ We denote - the latitude :math:`\bar\theta = \frac{\pi}{2} - \theta` - the longitude :math:`\lambda = \varphi - \pi` .. python-script:: import yt ds = yt.load_sample("KeplerianDisk", unit_system="cgs") slc = yt.SlicePlot(ds, "r", ("gas", "density")) slc.save() .. _unstructured-mesh-slices: Unstructured Mesh Slices ------------------------ Unstructured Mesh datasets can be sliced using the same syntax as above. Here is an example script using a publicly available MOOSE dataset: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") sl = yt.SlicePlot(ds, "x", ("connect1", "diffused")) sl.zoom(0.75) sl.save() Here, we plot the ``'diffused'`` variable, using a slice normal to the ``'x'`` direction, through the meshed labelled by ``'connect1'``. By default, the slice goes through the center of the domain. We have also zoomed out a bit to get a better view of the resulting structure. To instead plot the ``'convected'`` variable, using a slice normal to the ``'z'`` direction through the mesh labelled by ``'connect2'``, we do: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") sl = yt.SlicePlot(ds, "z", ("connect2", "convected")) sl.zoom(0.75) sl.save() These slices are made by sampling the finite element solution at the points corresponding to each pixel of the image. The ``'convected'`` and ``'diffused'`` variables are node-centered, so this interpolation is performed by converting the sample point the reference coordinate system of the element and evaluating the appropriate shape functions. You can also plot element-centered fields: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") sl = yt.SlicePlot(ds, "y", ("connect1", "conv_indicator")) sl.zoom(0.75) sl.save() We can also annotate the mesh lines, as follows: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") sl = yt.SlicePlot(ds, "z", ("connect1", "diffused")) sl.annotate_mesh_lines(color="black") sl.zoom(0.75) sl.save() The ``plot_args`` parameter is a dictionary of keyword arguments that will be passed to matplotlib. It can be used to control the mesh line color, thickness, etc... The above examples all involve 8-node hexahedral mesh elements. Here is another example from a dataset that uses 6-node wedge elements: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/wedge_out.e") sl = yt.SlicePlot(ds, "z", ("connect2", "diffused")) sl.save() Slices can also be used to examine 2D unstructured mesh datasets, but the slices must be taken to be normal to the ``'z'`` axis, or you'll get an error. Here is an example using another MOOSE dataset that uses triangular mesh elements: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e") sl = yt.SlicePlot(ds, "z", ("connect1", "nodal_aux")) sl.save() You may run into situations where you have a variable you want to visualize that exists on multiple mesh blocks. To view the variable on ``all`` mesh blocks, simply pass ``all`` as the first argument of the field tuple: .. python-script:: import yt ds = yt.load("MultiRegion/two_region_example_out.e", step=-1) sl = yt.SlicePlot(ds, "z", ("all", "diffused")) sl.save() .. _particle-plotting-workarounds: Additional Notes for Plotting Particle Data ------------------------------------------- Since version 4.2.0, off-axis projections ares supported for non-SPH particle data. Previous to that, this operation was only supported for SPH particles. Two historical workaround methods were available for plotting non-SPH particles with off-axis projections. 1. :ref:`smooth-non-sph` - this method involves extracting particle data to be reloaded with :class:`~yt.loaders.load_particles` and using the :class:`~yt.frontends.stream.data_structures.StreamParticlesDataset.add_sph_fields` function to create smoothing lengths. This works well for relatively small datasets, but is not parallelized and may take too long for larger data. 2. Plot from a saved :class:`~yt.data_objects.construction_data_containers.YTCoveringGrid`, :class:`~yt.data_objects.construction_data_containers.YTSmoothedCoveringGrid`, or :class:`~yt.data_objects.construction_data_containers.YTArbitraryGrid` dataset. This second method is illustrated below. First, construct one of the grid data objects listed above. Then, use the :class:`~yt.data_objects.data_containers.YTDataContainer.save_as_dataset` function (see :ref:`saving_data`) to save a deposited particle field (see :ref:`deposited-particle-fields`) as a reloadable dataset. This dataset can then be loaded and visualized using both off-axis projections and slices. Note, the change in the field name from ``("deposit", "nbody_mass")`` to ``("grid", "nbody_mass")`` after reloading. .. python-script:: import yt ds = yt.load("gizmo_cosmology_plus/snap_N128L16_132.hdf5") # create a 128^3 covering grid over the entire domain L = 7 cg = ds.covering_grid(level=L, left_edge=ds.domain_left_edge, dims=[2**L]*3) fn = cg.save_as_dataset(fields=[("deposit", "nbody_mass")]) ds_grid = yt.load(fn) p = yt.ProjectionPlot(ds_grid, [1, 1, 1], ("grid", "nbody_mass")) p.save() Plot Customization: Recentering, Resizing, Colormaps, and More -------------------------------------------------------------- You can customize each of the four plot types above in identical ways. We'll go over each of the customizations methods below. For each of the examples below we will modify the following plot. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.save() Panning and zooming ~~~~~~~~~~~~~~~~~~~ There are three methods to dynamically pan around the data. :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.pan` accepts x and y deltas. .. python-script:: import yt from yt.units import kpc ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.pan((2 * kpc, 2 * kpc)) slc.save() :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.pan_rel` accepts deltas in units relative to the field of view of the plot. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.pan_rel((0.1, -0.1)) slc.save() :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.zoom` accepts a factor to zoom in by. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.zoom(2) slc.save() Set axes units ~~~~~~~~~~~~~~ :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_axes_unit` allows the customization of the axes unit labels. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_axes_unit("Mpc") slc.save() The same result could have been accomplished by explicitly setting the ``width`` to ``(.01, 'Mpc')``. .. _set-image-units: Set image units ~~~~~~~~~~~~~~~ :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_axes_unit` allows the customization of the units used for the image and colorbar. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_unit(("gas", "density"), "Msun/pc**3") slc.save() If the unit you would like to convert to needs an equivalency, this can be specified via the ``equivalency`` keyword argument of ``set_unit``. For example, let's make a plot of the temperature field, but present it using an energy unit instead of a temperature unit: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "temperature"), width=(10, "kpc")) slc.set_unit(("gas", "temperature"), "keV", equivalency="thermal") slc.save() Set the plot center ~~~~~~~~~~~~~~~~~~~ The :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_center` function accepts a new center for the plot, in code units. New centers must be two element tuples. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_center((0.5, 0.503)) slc.save() Adjusting the plot view axes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are a number of ways in which the initial orientation of a :class:`~yt.visualization.plot_window.PlotWindow` object can be adjusted. The first two axis orientation modifications, :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.flip_horizontal` and :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.flip_vertical`, are equivalent to the ``invert_xaxis`` and ``invert_yaxis`` of matplotlib ``Axes`` objects. ``flip_horizontal`` will invert the plot's x-axis while the :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.flip_vertical` method will invert the plot's y-axis .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # slicing with standard view (right-handed) slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(20, 'kpc')) slc.annotate_title("Standard Horizontal (Right Handed)") slc.save("Standard.png") # flip the horizontal axis (not right handed) slc.flip_horizontal() slc.annotate_title("Horizontal Flipped (Not Right Handed)") slc.save("NotRightHanded.png") # flip the vertical axis slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(20, 'kpc')) slc.flip_vertical() slc.annotate_title("Flipped vertical") slc.save("FlippedVertical.png") In addition to inverting the direction of each axis, :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.swap_axes` will exchange the plot's vertical and horizontal axes: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # slicing with non right-handed coordinates slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(20, 'kpc')) slc.swap_axes() slc.annotate_title("Swapped axes") slc.save("SwappedAxes.png") # toggle swap_axes (return to standard view) slc.swap_axes() slc.annotate_title("Standard Axes") slc.save("StandardAxes.png") When using the ``flip_horizontal`` and ``flip_vertical`` with ``swap_axes``, it is important to remember that any ``flip_horizontal`` and ``flip_vertical`` operations are applied to the image axes (not underlying dataset coordinates) after any ``swap_axes`` calls, regardless of the order in which the callbacks are added. Also note that when using ``swap_axes``, any plot modifications relating to limits, image width or resolution should still be supplied in reference to the standard (unswapped) orientation rather than the swapped view. Finally, it's worth mentioning that these three methods can be used in combination to rotate the view: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # initial view slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(20, 'kpc')) slc.save("InitialOrientation.png") slc.annotate_title("Initial View") # swap + vertical flip = rotate 90 degree rotation (clockwise) slc.swap_axes() slc.flip_vertical() slc.annotate_title("90 Degree Clockwise Rotation") slc.save("SwappedAxes90CW.png") # vertical flip + horizontal flip = rotate 180 degree rotation slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(20, 'kpc')) slc.flip_horizontal() slc.flip_vertical() slc.annotate_title("180 Degree Rotation") slc.save("FlipAxes180.png") # swap + horizontal flip = rotate 90 degree rotation (counter clockwise) slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(20, 'kpc')) slc.swap_axes() slc.flip_horizontal() slc.annotate_title("90 Degree Counter Clockwise Rotation") slc.save("SwappedAxes90CCW.png") .. _hiding-colorbar-and-axes: Hiding the Colorbar and Axis Labels ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :class:`~yt.visualization.plot_window.PlotWindow` class has functions attached for hiding/showing the colorbar and axes. This allows for making minimal plots that focus on the data: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.hide_colorbar() slc.hide_axes() slc.save() See the cookbook recipe :ref:`show-hide-axes-colorbar` and the full function description :class:`~yt.visualization.plot_window.PlotWindow` for more information. Fonts ~~~~~ :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_font` allows font customization. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_font({"family": "sans-serif", "style": "italic", "weight": "bold", "size": 24}) slc.save() Colormaps ~~~~~~~~~ Each of these functions accepts at least two arguments. In all cases the first argument is a field name. This makes it possible to use different custom colormaps for different fields tracked by the plot object. To change the colormap for the plot, call the :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_cmap` function. Use any of the colormaps listed in the :ref:`colormaps` section. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_cmap(("gas", "density"), "RdBu_r") slc.save() Colorbar Normalization / Scaling """""""""""""""""""""""""""""""" For a general introduction to the topic of colorbar scaling, see ``_. Here we will focus on the defaults, and the ways to customize them, of yt plot classes. In this section, "norm" is used as short for "normalization", and is interchangeable with "scaling". Map-like plots e.g., ``SlicePlot``, ``ProjectionPlot`` and ``PhasePlot``, default to `logarithmic (log) `_ normalization when all values are strictly positive, and `symmetric log (symlog) `_ otherwise. yt supports two different interfaces to move away from the defaults. See **constrained norms** and **arbitrary norm** hereafter. .. note:: defaults can be configured on a per-field basis, see :ref:`per-field-plotconfig` **Constrained norms** The standard way to change colorbar scalings between linear, log, and symmetric log (symlog). Colorbar properties can be constrained via two methods: - :meth:`~yt.visualization.plot_container.PlotContainer.set_zlim` controls the limits of the colorbar range: ``zmin`` and ``zmax``. - :meth:`~yt.visualization.plot_container.ImagePlotContainer.set_log` allows switching to linear or symlog normalization. With symlog, the linear threshold can be set explicitly. Otherwise, yt will dynamically determine a reasonable value. Use the :meth:`~yt.visualization.plot_container.PlotContainer.set_zlim` method to set a custom colormap range. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_zlim(("gas", "density"), zmin=(1e-30, "g/cm**3"), zmax=(1e-25, "g/cm**3")) slc.save() Units can be left out, in which case they implicitly match the current display units of the colorbar (controlled with the ``set_unit`` method, see :ref:`set-image-units`). It is not required to specify both ``zmin`` and ``zmax``. Left unset, they will default to the extreme values in the current view. This default behavior can be enforced or restored by passing ``zmin="min"`` (reps. ``zmax="max"``) explicitly. :meth:`~yt.visualization.plot_container.ImagePlotContainer.set_log` takes a boolean argument to select log (``True``) or linear (``False``) scalings. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_log(("gas", "density"), False) # switch to linear scaling slc.save() One can switch to `symlog `_ by providing a "linear threshold" (``linthresh``) value. With ``linthresh="auto"`` yt will switch to symlog norm and guess an appropriate value automatically, with different behavior depending on the dynamic range of the data. When the dynamic range of the symlog scale is less than 15 orders of magnitude, the linthresh value will be the minimum absolute nonzero value, as in .. python-script:: import yt ds = yt.load_sample("IsolatedGalaxy") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_log(("gas", "density"), linthresh="auto") slc.save() When the dynamic range of the symlog scale exceeds 15 orders of magnitude, the linthresh value is calculated as 1/10\ :sup:`15` of the maximum nonzero value in order to avoid possible floating point precision issues. The following plot triggers the dynamic range cutoff .. python-script:: import yt ds = yt.load_sample("FIRE_M12i_ref11") p = yt.ProjectionPlot(ds, "x", ("gas", "density"), width=(30, "Mpc")) p.set_log(("gas", "density"), linthresh="auto") p.save() In the previous example, it is actually safe to expand the dynamic range and in other cases you may find that the selected linear threshold is not well suited to your dataset. To pass an explicit value instead .. python-script:: import yt ds = yt.load_sample("FIRE_M12i_ref11") p = yt.ProjectionPlot(ds, "x", ("gas", "density"), width=(30, "Mpc")) p.set_log(("gas", "density"), linthresh=(1e-22, "g/cm**2")) p.save() Similar to the ``zmin`` and ``zmax`` arguments of the ``set_zlim`` method, units can be left out in ``linthresh``. **Arbitrary norms** Alternatively, arbitrary `matplotlib norms `_ can be passed via the :meth:`~yt.visualization.plot_container.PlotContainer.set_norm` method. In that case, any numeric value is treated as having implicit units, matching the current display units. This alternative interface is more flexible, but considered experimental as of yt 4.1. Don't forget that with great power comes great responsibility. .. python-script:: import yt from matplotlib.colors import TwoSlopeNorm ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "velocity_x"), width=(30, "kpc")) slc.set_norm(("gas", "velocity_x"), TwoSlopeNorm(vcenter=0)) # using a diverging colormap to emphasize that vcenter corresponds to the # middle value in the color range slc.set_cmap(("gas", "velocity_x"), "RdBu") slc.save() .. note:: When calling :meth:`~yt.visualization.plot_container.PlotContainer.set_norm`, any constraints previously set with :meth:`~yt.visualization.plot_container.PlotContainer.set_log` or :meth:`~yt.visualization.plot_container.PlotContainer.set_zlim` will be dropped. Conversely, calling ``set_log`` or ``set_zlim`` will have the effect of dropping any norm previously set via ``set_norm``. The :meth:`~yt.visualization.plot_container.ImagePlotContainer.set_background_color` function accepts a field name and a color (optional). If color is given, the function will set the plot's background color to that. If not, it will set it to the bottom value of the color map. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(1.5, "Mpc")) slc.set_background_color(("gas", "density")) slc.save("bottom_colormap_background") slc.set_background_color(("gas", "density"), color="black") slc.save("black_background") Annotations ~~~~~~~~~~~ A slice object can also add annotations like a title, an overlying quiver plot, the location of grid boundaries, halo-finder annotations, and many other annotations, including user-customizable annotations. For example: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.annotate_grids() slc.save() will plot the density field in a 10 kiloparsec slice through the z-axis centered on the highest density point in the simulation domain. Before saving the plot, the script annotates it with the grid boundaries, which are drawn as lines in the plot, with colors going from black to white depending on the AMR level of the grid. Annotations are described in :ref:`callbacks`. Set the size and resolution of the plot ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To set the size of the plot, use the :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_figure_size` function. The argument is the size of the longest edge of the plot in inches. View the full resolution image to see the difference more clearly. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_figure_size(10) slc.save() To change the resolution of the image, call the :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_buff_size` function. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_buff_size(1600) slc.save() Also see cookbook recipe :ref:`image-resolution-primer` for more information about the parameters that determine the resolution of your images. Turning off minorticks ~~~~~~~~~~~~~~~~~~~~~~ By default minorticks for the x and y axes are turned on. The minorticks may be removed using the :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_minorticks` function, which either accepts a specific field name including the 'all' alias and the desired state for the plot as 'on' or 'off'. There is also an analogous :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_colorbar_minorticks` function for the colorbar axis. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") slc = yt.SlicePlot(ds, "z", ("gas", "density"), width=(10, "kpc")) slc.set_minorticks("all", False) slc.set_colorbar_minorticks("all", False) slc.save() .. _matplotlib-customization: Further customization via matplotlib ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each :class:`~yt.visualization.plot_window.PlotWindow` object is really a container for plots - one plot for each field specified in the list of fields supplied when the plot object is created. The individual plots can be accessed via the ``plots`` dictionary attached to each :class:`~yt.visualization.plot_window.PlotWindow` object: .. code-block:: python slc = SlicePlot(ds, 2, [("gas", "density"), ("gas", "temperature")]) dens_plot = slc.plots["gas", "density"] In this example ``dens_plot`` is an instance of :class:`~yt.visualization.plot_window.WindowPlotMPL`, an object that wraps the matplotlib `figure `_ and `axes `_ objects. We can access these matplotlib primitives via attributes of ``dens_plot``. .. code-block:: python figure = dens_plot.figure axes = dens_plot.axes colorbar_axes = dens_plot.cax These are the `figure `_ and `axes `_ objects that control the actual drawing of the plot. Arbitrary plot customizations are possible by manipulating these objects. See :ref:`matplotlib-primitives` for an example. .. _how-to-make-1d-profiles: 1D Profile Plots ---------------- 1D profiles are used to calculate the average or the sum of a given quantity with respect to a second quantity. Two common examples are the "average density as a function of radius" or "the total mass within a given set of density bins." When created, they default to the average: in fact, they default to the average as weighted by the total cell mass. However, this can be modified to take either the total value or the average with respect to a different quantity. Profiles operate on :ref:`data objects `; they will take the entire data contained in a sphere, a prism, an extracted region and so on, and they will calculate and use that as input to their calculation. To make a 1D profile plot, create a (:class:`~yt.visualization.profile_plotter.ProfilePlot`) object, supplying the data object, the field for binning, and a list of fields to be profiled. .. python-script:: import yt from yt.units import kpc ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") my_galaxy = ds.disk(ds.domain_center, [0.0, 0.0, 1.0], 10 * kpc, 3 * kpc) plot = yt.ProfilePlot(my_galaxy, ("gas", "density"), [("gas", "temperature")]) plot.save() This will create a :class:`~yt.data_objects.selection_data_containers.YTDisk` centered at [0.5, 0.5, 0.5], with a normal vector of [0.0, 0.0, 1.0], radius of 10 kiloparsecs and height of 3 kiloparsecs and will then make a plot of the mass-weighted average temperature as a function of density for all of the gas contained in the cylinder. We could also have made a profile considering only the gas in a sphere. For instance: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") my_sphere = ds.sphere([0.5, 0.5, 0.5], (100, "kpc")) plot = yt.ProfilePlot(my_sphere, ("gas", "temperature"), [("gas", "mass")], weight_field=None) plot.save() Note that because we have specified the weighting field to be ``None``, the profile plot will display the accumulated cell mass as a function of temperature rather than the average. Also note the use of a ``(value, unit)`` tuple. These can be used interchangeably with units explicitly imported from ``yt.units`` when creating yt plots. We can also accumulate along the bin field of a ``ProfilePlot`` (the bin field is the x-axis in a ``ProfilePlot``, in the last example the bin field is ``Temperature``) by setting the ``accumulation`` keyword argument to ``True``. The following example uses ``weight_field = None`` and ``accumulation = True`` to generate a plot of the enclosed mass in a sphere: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") my_sphere = ds.sphere([0.5, 0.5, 0.5], (100, "kpc")) plot = yt.ProfilePlot( my_sphere, "radius", [("gas", "mass")], weight_field=None, accumulation=True ) plot.save() Notably, above we have specified the field tuple for the mass, but not for the ``radius`` field. The ``radius`` field will not be ambiguous, but if you want to ensure that it refers to the radius of the cells on which the "gas" field type is defined, you can specify it using the field tuple ``("index", "radius")``. You can also access the data generated by profiles directly, which can be useful for overplotting average quantities on top of phase plots, or for exporting and plotting multiple profiles simultaneously from a time series. The ``profiles`` attribute contains a list of all profiles that have been made. For each item in the list, the x field data can be accessed with ``x``. The profiled fields can be accessed from the dictionary ``field_data``. .. code-block:: python plot = ProfilePlot( my_sphere, ("gas", "temperature"), [("gas", "mass")], weight_field=None ) profile = plot.profiles[0] # print the bin field, in this case temperature print(profile.x) # print the profiled mass field print(profile["gas", "mass"]) Other options, such as the number of bins, are also configurable. See the documentation for :class:`~yt.visualization.profile_plotter.ProfilePlot` for more information. Overplotting Multiple 1D Profiles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is often desirable to overplot multiple 1D profile to show evolution with time. This is supported with the ``from_profiles`` class method. 1D profiles are created with the :func:`~yt.data_objects.profiles.create_profile` method and then given to the ProfilePlot object. .. python-script:: import yt # Create a time-series object. es = yt.load_simulation("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo") es.get_time_series(redshifts=[5, 4, 3, 2, 1, 0]) # Lists to hold profiles, labels, and plot specifications. profiles = [] labels = [] # Loop over each dataset in the time-series. for ds in es: # Create a data container to hold the whole dataset. ad = ds.all_data() # Create a 1d profile of density vs. temperature. profiles.append( yt.create_profile( ad, [("gas", "temperature")], fields=[("gas", "mass")], weight_field=None, accumulation=True, ) ) # Add labels labels.append("z = %.2f" % ds.current_redshift) # Create the profile plot from the list of profiles. plot = yt.ProfilePlot.from_profiles(profiles, labels=labels) # Save the image. plot.save() Customizing axis limits ~~~~~~~~~~~~~~~~~~~~~~~ By default the x and y limits for ``ProfilePlot`` are determined using the :class:`~yt.data_objects.derived_quantities.Extrema` derived quantity. If you want to create a plot with custom axis limits, you have two options. First, you can create a custom profile object using :func:`~yt.data_objects.profiles.create_profile`. This function accepts a dictionary of ``(max, min)`` tuples keyed to field names. .. python-script:: import yt import yt.units as u ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sp = ds.sphere("m", 10 * u.kpc) profiles = yt.create_profile( sp, ("gas", "temperature"), ("gas", "density"), weight_field=None, extrema={("gas", "temperature"): (1e3, 1e7), ("gas", "density"): (1e-26, 1e-22)}, ) plot = yt.ProfilePlot.from_profiles(profiles) plot.save() You can also make use of the :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_xlim` and :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_ylim` functions to customize the axes limits of a plot that has already been created. Note that calling ``set_xlim`` is much slower than calling ``set_ylim``. This is because ``set_xlim`` must recreate the profile object using the specified extrema. Creating a profile directly via :func:`~yt.data_objects.profiles.create_profile` might be significantly faster. Note that since there is only one bin field, ``set_xlim`` does not accept a field name as the first argument. .. python-script:: import yt import yt.units as u ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sp = ds.sphere("m", 10 * u.kpc) plot = yt.ProfilePlot(sp, ("gas", "temperature"), ("gas", "density"), weight_field=None) plot.set_xlim(1e3, 1e7) plot.set_ylim(("gas", "density"), 1e-26, 1e-22) plot.save() Customizing Units ~~~~~~~~~~~~~~~~~ Units for both the x and y axis can be controlled via the :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_unit` method. Adjusting the plot units does not require recreating the histogram, so adjusting units will always be inexpensive, requiring only an in-place unit conversion. In the following example we create a plot of the average density in solar masses per cubic parsec as a function of radius in kiloparsecs. .. python-script:: import yt import yt.units as u ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sp = ds.sphere("m", 10 * u.kpc) plot = yt.ProfilePlot(sp, "radius", ("gas", "density"), weight_field=None) plot.set_unit(("gas", "density"), "msun/pc**3") plot.set_unit("radius", "kpc") plot.save() Linear and Logarithmic Scaling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The axis scaling can be manipulated via the :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_log` function. This function accepts a field name and a boolean. If the boolean is ``True``, the field is plotted in log scale. If ``False``, the field is plotted in linear scale. In the following example we create a plot of the average x velocity as a function of radius. Since the x component of the velocity vector can be negative, we set the scaling to be linear for this field. .. python-script:: import yt import yt.units as u ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sp = ds.sphere("m", 10 * u.kpc) plot = yt.ProfilePlot(sp, "radius", ("gas", "velocity_x"), weight_field=None) plot.set_log(("gas", "velocity_x"), False) plot.save() Setting axis labels ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The axis labels can be manipulated via the :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_ylabel` and :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_xlabel` functions. The :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_ylabel` function accepts a field name and a string with the desired label. The :meth:`~yt.visualization.profile_plotter.ProfilePlot.set_xlabel` function just accepts the desired label and applies this to all of the plots. In the following example we create a plot of the average x-velocity and density as a function of radius. The xlabel is set to "Radius", for all plots, and the ylabel is set to "velocity in x direction" for the x-velocity plot. .. python-script:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") ad = ds.all_data() plot = yt.ProfilePlot(ad, "radius", [("gas", "temperature"), ("gas", "velocity_x")], weight_field=None) plot.set_xlabel("Radius") plot.set_ylabel(("gas", "velocity_x"), "velocity in x direction") plot.save() Adding plot title ~~~~~~~~~~~~~~~~~ Plot title can be set via the :meth:`~yt.visualization.profile_plotter.ProfilePlot.annotate_title` function. It accepts a string argument which is the plot title and an optional ``field`` parameter which specifies the field for which plot title should be added. ``field`` could be a string or a list of string. If ``field`` is not passed, plot title will be added for the fields. In the following example we create a plot and set the plot title. .. python-script:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") ad = ds.all_data() plot = yt.ProfilePlot(ad, ("gas", "density"), [("gas", "temperature")], weight_field=None) plot.annotate_title("Temperature vs Density Plot") plot.save() Another example where we create plots from profile. By specifying the fields we can add plot title to a specific plot. .. python-script:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") sphere = ds.sphere("max", (1.0, "Mpc")) profiles = [] profiles.append(yt.create_profile(sphere, ["radius"], fields=[("gas", "density")], n_bins=64)) profiles.append( yt.create_profile(sphere, ["radius"], fields=["dark_matter_density"], n_bins=64) ) plot = yt.ProfilePlot.from_profiles(profiles) plot.annotate_title("Plot Title: Density", ("gas", "density")) plot.annotate_title("Plot Title: Dark Matter Density", "dark_matter_density") plot.save() Here, ``plot.annotate_title("Plot Title: Density", ("gas", "density"))`` will only set the plot title for the ``"density"`` field. Thus, allowing us the option to have different plot titles for different fields. Annotating plot with text ~~~~~~~~~~~~~~~~~~~~~~~~~ Plots can be annotated at a desired (x,y) coordinate using :meth:`~yt.visualization.profile_plotter.ProfilePlot.annotate_text` function. This function accepts the x-position, y-position, a text string to be annotated in the plot area, and an optional list of fields for annotating plots with the specified field. Furthermore, any keyword argument accepted by the matplotlib ``axes.text`` function could also be passed which will can be useful to change fontsize, text-alignment, text-color or other such properties of annotated text. In the following example we create a plot and add a simple annotation. .. python-script:: import yt ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") ad = ds.all_data() plot = yt.ProfilePlot(ad, ("gas", "density"), [("gas", "temperature")], weight_field=None) plot.annotate_text(1e-30, 1e7, "Annotated Text") plot.save() To add annotations to a particular set of fields we need to pass in the list of fields as follows, where ``"ftype1"`` and ``"ftype2"`` are the field types (and may be the same): .. code-block:: python plot.annotate_text( 1e-30, 1e7, "Annotation", [("ftype1", "field1"), ("ftype2", "field2")] ) To change the text annotated text properties, we need to pass the matplotlib ``axes.text`` arguments as follows: .. code-block:: python plot.annotate_text( 1e-30, 1e7, "Annotation", fontsize=20, bbox=dict(facecolor="red", alpha=0.5), horizontalalignment="center", verticalalignment="center", ) The above example will set the fontsize of annotation to 20, add a bounding box of red color and center align horizontally and vertically. The is just an example to modify the text properties, for further options please check `matplotlib.axes.Axes.text `_. Altering Line Properties ~~~~~~~~~~~~~~~~~~~~~~~~ Line properties for any and all of the profiles can be changed with the :func:`~yt.visualization.profile_plotter.set_line_property` function. The two arguments given are the line property and desired value. .. code-block:: python plot.set_line_property("linestyle", "--") With no additional arguments, all of the lines plotted will be altered. To change the property of a single line, give also the index of the profile. .. code-block:: python # change only the first line plot.set_line_property("linestyle", "--", 0) .. _how-to-1d-unstructured-mesh: 1D Line Sampling ---------------- YT has the ability to sample datasets along arbitrary lines and plot the result. You must supply five arguments to the ``LinePlot`` class. They are enumerated below: 1. Dataset 2. A list of fields or a single field you wish to plot 3. The starting point of the sampling line. This should be an n-element list, tuple, ndarray, or YTArray with the elements corresponding to the coordinates of the starting point. (n should equal the dimension of the dataset) 4. The ending point of the sampling line. This should also be an n-element list, tuple, ndarray, or YTArray with the elements corresponding to the coordinates of the ending point. 5. The number of sampling points along the line, e.g. if 1000 is specified, then data will be sampled at 1000 points evenly spaced between the starting and ending points. The below code snippet illustrates how this is done: .. code-block:: python ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1) plot = yt.LinePlot(ds, [("all", "v"), ("all", "u")], (0, 0, 0), (0, 1, 0), 1000) plot.save() If working in a Jupyter Notebook, ``LinePlot`` also has the ``show()`` method. You can add a legend to a 1D sampling plot. The legend process takes two steps: 1. When instantiating the ``LinePlot``, pass a dictionary of labels with keys corresponding to the field names 2. Call the ``LinePlot`` ``annotate_legend`` method X- and Y- axis units can be set with ``set_x_unit`` and ``set_unit`` methods respectively. The below code snippet combines all the features we've discussed: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") plot = yt.LinePlot(ds, ("gas", "density"), [0, 0, 0], [1, 1, 1], 512) plot.annotate_legend(("gas", "density")) plot.set_x_unit("cm") plot.set_unit(("gas", "density"), "kg/cm**3") plot.save() If a list of fields is passed to ``LinePlot``, yt will create a number of individual figures equal to the number of different dimensional quantities. E.g. if ``LinePlot`` receives two fields with units of "length/time" and a field with units of "temperature", two different figures will be created, one with plots of the "length/time" fields and another with the plot of the "temperature" field. It is only necessary to call ``annotate_legend`` for one field of a multi-field plot to produce a legend containing all the labels passed in the initial construction of the ``LinePlot`` instance. Example: .. python-script:: import yt ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1) plot = yt.LinePlot( ds, [("all", "v"), ("all", "u")], [0, 0, 0], [0, 1, 0], 100, field_labels={("all", "u"): r"v$_x$", ("all", "v"): r"v$_y$"}, ) plot.annotate_legend(("all", "u")) plot.save() ``LinePlot`` is a bit different from yt ray objects which are data containers. ``LinePlot`` is a plotting class that may use yt ray objects to supply field plotting information. However, perhaps the most important difference to highlight between rays and ``LinePlot`` is that rays return data elements that intersect with the ray and make no guarantee about the spacing between data elements. ``LinePlot`` sampling points are guaranteed to be evenly spaced. In the case of cell data where multiple points fall within the same cell, the ``LinePlot`` object will show the same field value for each sampling point that falls within the same cell. .. _how-to-make-2d-profiles: 2D Phase Plots -------------- 2D phase plots function in much the same was as 1D phase plots, but with a :class:`~yt.visualization.profile_plotter.PhasePlot` object. Much like 1D profiles, 2D profiles (phase plots) are best thought of as plotting a distribution of points, either taking the average or the accumulation in a bin. The default behavior is to average, using the cell mass as the weighting, but this behavior can be controlled through the ``weight_field`` parameter. For example, to generate a 2D distribution of mass enclosed in density and temperature bins, you can do: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") my_sphere = ds.sphere("c", (50, "kpc")) plot = yt.PhasePlot( my_sphere, ("gas", "density"), ("gas", "temperature"), [("gas", "mass")], weight_field=None ) plot.save() If you would rather see the average value of a field as a function of two other fields, leave off the ``weight_field`` argument, and it will average by the cell mass. This would look something like: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") my_sphere = ds.sphere("c", (50, "kpc")) plot = yt.PhasePlot(my_sphere, ("gas", "density"), ("gas", "temperature"), [("gas", "H_p0_fraction")]) plot.save() Customizing Phase Plots ~~~~~~~~~~~~~~~~~~~~~~~ Similarly to 1D profile plots, :class:`~yt.visualization.profile_plotter.PhasePlot` can be customized via ``set_unit``, ``set_xlim``, ``set_ylim``, and ``set_zlim``. The following example illustrates how to manipulate these functions. :class:`~yt.visualization.profile_plotter.PhasePlot` can also be customized in a similar manner as :class:`~yt.visualization.plot_window.SlicePlot`, such as with ``hide_colorbar`` and ``show_colorbar``. .. python-script:: import yt ds = yt.load("sizmbhloz-clref04SNth-rs9_a0.9011/sizmbhloz-clref04SNth-rs9_a0.9011.art") center = ds.arr([64.0, 64.0, 64.0], "code_length") rvir = ds.quan(1e-1, "Mpccm/h") sph = ds.sphere(center, rvir) plot = yt.PhasePlot(sph, ("gas", "density"), ("gas", "temperature"), ("gas", "mass"), weight_field=None) plot.set_unit(("gas", "density"), "Msun/pc**3") plot.set_unit(("gas", "mass"), "Msun") plot.set_xlim(1e-5, 1e1) plot.set_ylim(1, 1e7) plot.save() It is also possible to construct a custom 2D profile object and then use the :meth:`~yt.visualization.profile_plotter.PhasePlot.from_profile` function to create a ``PhasePlot`` using the profile object. This will sometimes be faster, especially if you need custom x and y axes limits. The following example illustrates this workflow: .. python-script:: import yt ds = yt.load("sizmbhloz-clref04SNth-rs9_a0.9011/sizmbhloz-clref04SNth-rs9_a0.9011.art") center = ds.arr([64.0, 64.0, 64.0], "code_length") rvir = ds.quan(1e-1, "Mpccm/h") sph = ds.sphere(center, rvir) units = {("gas", "density"): "Msun/pc**3", ("gas", "mass"): "Msun"} extrema = {("gas", "density"): (1e-5, 1e1), ("gas", "temperature"): (1, 1e7)} profile = yt.create_profile( sph, [("gas", "density"), ("gas", "temperature")], n_bins=[128, 128], fields=[("gas", "mass")], weight_field=None, units=units, extrema=extrema, ) plot = yt.PhasePlot.from_profile(profile) plot.save() Probability Distribution Functions and Accumulation --------------------------------------------------- Both 1D and 2D profiles which show the total of amount of some field, such as mass, in a bin (done by setting the ``weight_field`` keyword to ``None``) can be turned into probability distribution functions (PDFs) by setting the ``fractional`` keyword to ``True``. When set to ``True``, the value in each bin is divided by the sum total from all bins. These can be turned into cumulative distribution functions (CDFs) by setting the ``accumulation`` keyword to ``True``. This will make it so that the value in any bin N is the cumulative sum of all bins from 0 to N. The direction of the summation can be reversed by setting ``accumulation`` to ``-True``. For ``PhasePlot``, the accumulation can be set independently for each axis by setting ``accumulation`` to a list of ``True``/ ``-True`` /``False`` values. .. _particle-plots: Particle Plots -------------- Slice and projection plots both provide a callback for over-plotting particle positions onto gas fields. However, sometimes you want to plot the particle quantities by themselves, perhaps because the gas fields are not relevant to your use case, or perhaps because your dataset doesn't contain any gas fields in the first place. Additionally, you may want to plot your particles with a third field, such as particle mass or age, mapped to a colorbar. :class:`~yt.visualization.particle_plots.ParticlePlot` provides a convenient way to do this in yt. The easiest way to make a :class:`~yt.visualization.particle_plots.ParticlePlot` is to use the convenience routine. This has the syntax: .. code-block:: python p = yt.ParticlePlot(ds, ("all", "particle_position_x"), ("all", "particle_position_y")) p.save() Here, ``ds`` is a dataset we've previously opened. The commands create a particle plot that shows the x and y positions of all the particles in ``ds`` and save the result to a file on the disk. The type of plot returned depends on the fields you pass in; in this case, ``p`` will be an :class:`~yt.visualization.particle_plots.ParticleProjectionPlot`, because the fields are aligned to the coordinate system of the simulation. The above example is equivalent to the following: .. code-block:: python p = yt.ParticleProjectionPlot(ds, "z") p.save() Most of the callbacks the work for slice and projection plots also work for :class:`~yt.visualization.particle_plots.ParticleProjectionPlot`. For instance, we can zoom in: .. code-block:: python p = yt.ParticlePlot(ds, ("all", "particle_position_x"), ("all", "particle_position_y")) p.zoom(10) p.save("zoom") change the width: .. code-block:: python p.set_width((500, "kpc")) or change the axis units: .. code-block:: python p.set_unit(("all", "particle_position_x"), "Mpc") Here is a full example that shows the simplest way to use :class:`~yt.visualization.particle_plots.ParticlePlot`: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticlePlot(ds, ("all", "particle_position_x"), ("all", "particle_position_y")) p.save() In the above examples, we are simply splatting particle x and y positions onto a plot using some color. Colors can be applied to the plotted particles by providing a ``z_field``, which will be summed along the line of sight in a manner similar to a projection. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticlePlot(ds, ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_mass")) p.set_unit(("all", "particle_mass"), "Msun") p.zoom(32) p.save() Additionally, a ``weight_field`` can be given such that the value in each pixel is the weighted average along the line of sight. .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticlePlot( ds, ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_mass"), weight_field=("all", "particle_ones"), ) p.set_unit(("all", "particle_mass"), "Msun") p.zoom(32) p.save() Note the difference in the above two plots. The first shows the total mass along the line of sight. The density is higher in the inner regions, and hence there are more particles and more mass along the line of sight. The second plot shows the average mass per particle along the line of sight. The inner region is dominated by low mass star particles, whereas the outer region is comprised of higher mass dark matter particles. Both :class:`~yt.visualization.particle_plots.ParticleProjectionPlot` and :class:`~yt.visualization.particle_plots.ParticlePhasePlot` objects accept a ``deposition`` argument which controls the order of the "splatting" of the particles onto the pixels in the plot. The default option, ``"ngp"``, corresponds to the "Nearest-Grid-Point" (0th-order) method, which simply finds the pixel the particle is located in and deposits 100% of the particle or its plotted quantity into that pixel. The other option, ``"cic"``, corresponds to the "Cloud-In-Cell" (1st-order) method, which linearly interpolates the particle or its plotted quantity into the four nearest pixels in the plot. Here is a complete example that uses the ``particle_mass`` field to set the colorbar and shows off some of the modification functions for :class:`~yt.visualization.particle_plots.ParticleProjectionPlot`: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticlePlot( ds, ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_mass"), width=(0.5, 0.5), ) p.set_unit(("all", "particle_mass"), "Msun") p.zoom(32) p.annotate_title("Zoomed-in Particle Plot") p.save() If the fields passed in to :class:`~yt.visualization.particle_plots.ParticlePlot` do not correspond to a valid :class:`~yt.visualization.particle_plots.ParticleProjectionPlot`, a :class:`~yt.visualization.particle_plots.ParticlePhasePlot` will be returned instead. :class:`~yt.visualization.particle_plots.ParticlePhasePlot` is used to plot arbitrary particle fields against each other, and do not support some of the callbacks available in :class:`~yt.visualization.particle_plots.ParticleProjectionPlot` - for instance, :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.pan` and :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.zoom` don't make much sense when of your axes is a position and the other is a velocity. The modification functions defined for :class:`~yt.visualization.profile_plotter.PhasePlot` should all work, however. Here is an example of making a :class:`~yt.visualization.particle_plots.ParticlePhasePlot` of ``particle_position_x`` versus ``particle_velocity_z``, with the ``particle_mass`` on the colorbar: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticlePlot(ds, ("all", "particle_position_x"), ("all", "particle_velocity_z"), ("all", "particle_mass")) p.set_unit(("all", "particle_position_x"), "Mpc") p.set_unit(("all", "particle_velocity_z"), "km/s") p.set_unit(("all", "particle_mass"), "Msun") p.save() and here is one with the particle x and y velocities on the plot axes: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticlePlot(ds, ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_mass")) p.set_unit(("all", "particle_velocity_x"), "km/s") p.set_unit(("all", "particle_velocity_y"), "km/s") p.set_unit(("all", "particle_mass"), "Msun") p.set_ylim(-400, 400) p.set_xlim(-400, 400) p.save() If you want more control over the details of the :class:`~yt.visualization.particle_plots.ParticleProjectionPlot` or :class:`~yt.visualization.particle_plots.ParticlePhasePlot`, you can always use these classes directly. For instance, here is an example of using the ``depth`` argument to :class:`~yt.visualization.particle_plots.ParticleProjectionPlot` to only plot the particles that live in a thin slice around the center of the domain: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticleProjectionPlot(ds, 2, [("all", "particle_mass")], width=(0.5, 0.5), depth=0.01) p.set_unit(("all", "particle_mass"), "Msun") p.save() Using :class:`~yt.visualization.particle_plots.ParticleProjectionPlot`, you can also plot particles along an off-axis direction: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") L = [1, 1, 1] # normal or "line of sight" vector N = [0, 1, 0] # north or "up" vector p = yt.ParticleProjectionPlot( ds, L, [("all", "particle_mass")], width=(0.05, 0.05), depth=0.3, north_vector=N ) p.set_unit(("all", "particle_mass"), "Msun") p.save() Here is an example of using the ``data_source`` argument to :class:`~yt.visualization.particle_plots.ParticlePhasePlot` to only consider the particles that lie within a 50 kpc sphere around the domain center: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") my_sphere = ds.sphere("c", (50.0, "kpc")) p = yt.ParticlePhasePlot( my_sphere, ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_mass") ) p.set_unit(("all", "particle_velocity_x"), "km/s") p.set_unit(("all", "particle_velocity_y"), "km/s") p.set_unit(("all", "particle_mass"), "Msun") p.set_ylim(-400, 400) p.set_xlim(-400, 400) p.save() :class:`~yt.visualization.particle_plots.ParticleProjectionPlot` objects also admit a ``density`` flag, which allows one to plot the surface density of a projected quantity. This simply divides the quantity in each pixel of the plot by the area of that pixel. It also changes the label on the colorbar to reflect the new units and the fact that it is a density. This may make most sense in the case of plotting the projected particle mass, in which case you can plot the projected particle mass density: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ParticleProjectionPlot(ds, 2, [("all", "particle_mass")], width=(0.5, 0.5), density=True) p.set_unit(("all", "particle_mass"), "Msun/kpc**2") # Note that the dimensions reflect the density flag p.save() Finally, with 1D and 2D Profiles, you can create a :class:`~yt.data_objects.profiles.ParticleProfile` object separately using the :func:`~yt.data_objects.profiles.create_profile` function, and then use it create a :class:`~yt.visualization.particle_plots.ParticlePhasePlot` object using the :meth:`~yt.visualization.particle_plots.ParticlePhasePlot.from_profile` method. In this example, we have also used the ``weight_field`` argument to compute the average ``particle_mass`` in each pixel, instead of the total: .. python-script:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") ad = ds.all_data() profile = yt.create_profile( ad, [("all", "particle_velocity_x"), ("all", "particle_velocity_y")], [("all", "particle_mass")], n_bins=800, weight_field=("all", "particle_ones"), ) p = yt.ParticlePhasePlot.from_profile(profile) p.set_unit(("all", "particle_velocity_x"), "km/s") p.set_unit(("all", "particle_velocity_y"), "km/s") p.set_unit(("all", "particle_mass"), "Msun") p.set_ylim(-400, 400) p.set_xlim(-400, 400) p.save() Under the hood, the :class:`~yt.data_objects.profiles.ParticleProfile` class works a lot like a :class:`~yt.data_objects.profiles.Profile2D` object, except that instead of just binning the particle field, you can also use higher-order deposition functions like the cloud-in-cell interpolant to spread out the particle quantities over a few cells in the profile. The :func:`~yt.data_objects.profiles.create_profile` will automatically detect when all the fields you pass in are particle fields, and return a :class:`~yt.data_objects.profiles.ParticleProfile` if that is the case. For a complete description of the :class:`~yt.data_objects.profiles.ParticleProfile` class please consult the reference documentation. .. _interactive-plotting: Interactive Plotting -------------------- The best way to interactively plot data is through the IPython notebook. Many detailed tutorials on using the IPython notebook can be found at :ref:`notebook-tutorial`. The simplest way to launch the notebook it is to type: .. code-block:: bash jupyter lab at the command line. This will prompt you for a password (so that if you're on a shared user machine no one else can pretend to be you!) and then spawn an IPython notebook you can connect to. If you want to see yt plots inline inside your notebook, you need only create a plot and then call ``.show()`` and the image will appear inline: .. notebook-cell:: import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p = yt.ProjectionPlot(ds, "z", ("gas", "density"), center='m', width=(10,'kpc'), weight_field=("gas", "density")) p.set_figure_size(5) p.show() .. _saving_plots: Saving Plots ------------ If you want to save your yt plots, you have a couple of options for customizing the plot filenames. If you don't care what the filenames are, just calling the ``save`` method with no additional arguments usually suffices: .. code-block:: python import yt ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0100") slc = yt.SlicePlot(ds, "z", [("gas", "kT"), ("gas", "density")], width=(500.0, "kpc")) slc.save() which will yield PNG plots with the filenames .. code-block:: bash $ ls \*.png sloshing_nomag2_hdf5_plt_cnt_0100_Slice_z_density.png sloshing_nomag2_hdf5_plt_cnt_0100_Slice_z_kT.png which has a general form of .. code-block:: bash [dataset name]_[plot type]_[axis]_[field name].[suffix] Calling ``save`` with a single argument or the ``name`` keyword argument specifies an alternative name for the plot: .. code-block:: python slc.save("bananas") or .. code-block:: python slc.save(name="bananas") yields .. code-block:: bash $ ls \*.png bananas_Slice_z_kT.png bananas_Slice_z_density.png If you call ``save`` with a full filename with a file suffix, the plot will be saved with that filename: .. code-block:: python slc.save("sloshing.png") since this will take any field and plot it with this filename, it is typically only useful if you are plotting one field. If you want to simply change the image format of the plotted file, use the ``suffix`` keyword: .. code-block:: python slc.save(name="bananas", suffix="eps") yielding .. code-block:: bash $ ls *.eps bananas_Slice_z_kT.eps bananas_Slice_z_density.eps .. _remaking-plots: Remaking Figures from Plot Datasets ----------------------------------- When working with datasets that are too large to be stored locally, making figures just right can be cumbersome as it requires continuously moving images somewhere they can be viewed. However, image creation is actually a two step process of first creating the projection, slice, or profile object, and then converting that object into an actual image. Fortunately, the hard part (creating slices, projections, profiles) can be separated from the easy part (generating images). The intermediate slice, projection, and profile objects can be saved as reloadable datasets, then handed back to the plotting machinery discussed here. For slices and projections, the saveable object is associated with the plot object as ``data_source``. This can be saved with the :func:`~yt.data_objects.data_containers.YTDataContainer.save_as_dataset` function. For more information, see :ref:`saving_data`. .. code-block:: python p = yt.ProjectionPlot(ds, "x", ("gas", "density"), weight_field=("gas", "density")) fn = p.data_source.save_as_dataset() This function will optionally take a ``filename`` keyword that follows the same logic as discussed above in :ref:`saving_plots`. The filename to which the dataset was written will be returned. Once saved, this file can be reloaded completely independently of the original dataset and given back to the plot function with the same arguments. One can now continue to tweak the figure to one's liking. .. code-block:: python new_ds = yt.load(fn) new_p = yt.ProjectionPlot( new_ds, "x", ("gas", "density"), weight_field=("gas", "density") ) new_p.save() The same functionality is available for profile and phase plots. In each case, a special data container, ``data``, is given to the plotting functions. For ``ProfilePlot``: .. code-block:: python ad = ds.all_data() p1 = yt.ProfilePlot( ad, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "mass") ) # note that ProfilePlots can hold a list of profiles fn = p1.profiles[0].save_as_dataset() new_ds = yt.load(fn) p2 = yt.ProfilePlot( new_ds.data, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "mass"), ) p2.save() For ``PhasePlot``: .. code-block:: python ad = ds.all_data() p1 = yt.PhasePlot( ad, ("gas", "density"), ("gas", "temperature"), ("gas", "mass"), weight_field=None ) fn = p1.profile.save_as_dataset() new_ds = yt.load(fn) p2 = yt.PhasePlot( new_ds.data, ("gas", "density"), ("gas", "temperature"), ("gas", "mass"), weight_field=None, ) p2.save() .. _eps-writer: Publication-ready Figures ------------------------- While the routines above give a convenient method to inspect and visualize your data, publishers often require figures to be in PDF or EPS format. While the matplotlib supports vector graphics and image compression in PDF formats, it does not support compression in EPS formats. The :class:`~yt.visualization.eps_writer.DualEPS` module provides an interface with the `PyX `_, which is a Python abstraction of the PostScript drawing model with a LaTeX interface. It is optimal for publications to provide figures with vector graphics to avoid rasterization of the lines and text, along with compression to produce figures that do not have a large filesize. .. note:: PyX must be installed, which can be accomplished either manually with ``python -m pip install pyx``. This module can take any of the plots mentioned above and create an EPS or PDF figure. For example, .. code-block:: python import yt.visualization.eps_writer as eps slc = yt.SlicePlot(ds, "z", ("gas", "density")) slc.set_width(25, "kpc") eps_fig = eps.single_plot(slc) eps_fig.save_fig("zoom", format="eps") eps_fig.save_fig("zoom-pdf", format="pdf") The ``eps_fig`` object exposes all of the low-level functionality of ``PyX`` for further customization (see the `PyX documentation `_). There are a few convenience routines in ``eps_writer``, such as drawing a circle, .. code-block:: python eps_fig.circle(radius=0.2, loc=(0.5, 0.5)) eps_fig.sav_fig("zoom-circle", format="eps") with a radius of 0.2 at a center of (0.5, 0.5), both of which are in units of the figure's field of view. The :func:`~yt.visualization.eps_writer.multiplot_yt` routine also provides a convenient method to produce multi-panel figures from a PlotWindow. For example, .. code-block:: python import yt import yt.visualization.eps_writer as eps slc = yt.SlicePlot( ds, "z", [ ("gas", "density"), ("gas", "temperature"), ("gas", "pressure"), ("gas", "velocity_magnitude"), ], ) slc.set_width(25, "kpc") eps_fig = eps.multiplot_yt(2, 2, slc, bare_axes=True) eps_fig.scale_line(0.2, "5 kpc") eps_fig.save_fig("multi", format="eps") will produce a 2x2 panel figure with a scale bar indicating 5 kpc. The routine will try its best to place the colorbars in the optimal margin, but it can be overridden by providing the keyword ``cb_location`` with a dict of either ``right, left, top, bottom`` with the fields as the keys. You can also combine slices, projections, and phase plots. Here is an example that includes slices and phase plots: .. code-block:: python from yt import PhasePlot, SlicePlot from yt.visualization.eps_writer import multiplot_yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") p1 = SlicePlot(ds, "x", ("gas", "density")) p1.set_width(10, "kpc") p2 = SlicePlot(ds, "x", ("gas", "temperature")) p2.set_width(10, "kpc") p2.set_cmap(("gas", "temperature"), "hot") sph = ds.sphere(ds.domain_center, (10, "kpc")) p3 = PhasePlot( sph, "radius", ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "mass"), ) p4 = PhasePlot( sph, "radius", ("gas", "density"), ("gas", "pressure"), weight_field=("gas", "mass") ) mp = multiplot_yt( 2, 2, [p1, p2, p3, p4], savefig="yt", shrink_cb=0.9, bare_axes=False, yt_nocbar=False, margins=(0.5, 0.5), ) mp.save_fig("multi_slice_phase") Using yt's style with matplotlib -------------------------------- It is possible to use yt's plot style in outside of yt itself, with the :func:`~yt.funcs.matplotlib_style_context` context manager .. code-block:: python import matplotlib.pyplot as plt import numpy as np import yt plt.rcParams["font.size"] = 14 x = np.linspace(-np.pi, np.pi, 100) y = np.sin(x) with yt.funcs.matplotlib_style_context(): fig, ax = plt.subplots() ax.plot(x, y) ax.set( xlabel=r"$x$", ylabel=r"$y$", title="A yt-styled matplotlib figure", ) Note that :func:`~yt.funcs.matplotlib_style_context` doesn't control the font size, so we adjust it manually in the preamble. With matplotlib 3.7 and newer, you can avoid importing yt altogether .. code-block:: python # requires matplotlib>=3.7 import matplotlib.pyplot as plt import numpy as np plt.rcParams["font.size"] = 14 x = np.linspace(-np.pi, np.pi, 100) y = np.sin(x) with plt.style.context("yt.default"): fig, ax = plt.subplots() ax.plot(x, y) ax.set( xlabel=r"$x$", ylabel=r"$y$", title="A yt-styled matplotlib figure", ) and you can also enable yt's style without a context manager as .. code-block:: python # requires matplotlib>=3.7 import matplotlib.pyplot as plt import numpy as np plt.style.use("yt.default") plt.rcParams["font.size"] = 14 x = np.linspace(-np.pi, np.pi, 100) y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, y) ax.set( xlabel=r"$x$", ylabel=r"$y$", title="A yt-styled matplotlib figure", ) For more details, see `matplotlib's documentation _` yt-project-yt-f043ac8/doc/source/visualizing/sketchfab.rst000066400000000000000000000334511510711153200237550ustar00rootroot00000000000000.. _extracting-isocontour-information: .. _surfaces: 3D Surfaces and Sketchfab ========================= .. sectionauthor:: Jill Naiman and Matthew Turk Surface Objects and Extracting Isocontour Information ----------------------------------------------------- yt contains an implementation of the `Marching Cubes `__ algorithm, which can operate on 3D data objects. This provides two things. The first is to identify isocontours and return either the geometry of those isocontours or to return another field value sampled along that isocontour. The second piece of functionality is to calculate the flux of a field over an isocontour. Note that these isocontours are not guaranteed to be topologically connected. In fact, inside a given data object, the marching cubes algorithm will return all isocontours, not just a single connected one. This means if you encompass two clumps of a given density in your data object and extract an isocontour at that density, it will include both of the clumps. This means that with adaptive mesh refinement data, you *will* see cracks across refinement boundaries unless a "crack-fixing" step is applied to match up these boundaries. yt does not perform such an operation, and so there will be seams visible in 3D views of your isosurfaces. Surfaces can be exported in `OBJ format `_, values can be samples at the center of each face of the surface, and flux of a given field could be calculated over the surface. This means you can, for instance, extract an isocontour in density and calculate the mass flux over that isocontour. It also means you can export a surface from yt and view it in something like `Blender `__, `MeshLab `__, or even on your Android or iOS device in `MeshPad `__. To extract geometry or sample a field, call :meth:`~yt.data_objects.data_containers.YTSelectionContainer3D.extract_isocontours`. To calculate a flux, call :meth:`~yt.data_objects.data_containers.YTSelectionContainer3D.calculate_isocontour_flux`. both of these operations will run in parallel. For more information on enabling parallelism in yt, see :ref:`parallel-computation`. Alternatively, you can make an object called ``YTSurface`` that makes this process much easier. You can create one of these objects by specifying a source data object and a field over which to identify a surface at a given value. For example: .. code-block:: python import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sphere = ds.sphere("max", (1.0, "Mpc")) surface = ds.surface(sphere, ("gas", "density"), 1e-27) This object, ``surface``, can be queried for values on the surface. For instance: .. code-block:: python print(surface["gas", "temperature"].min(), surface["gas", "temperature"].max()) will return the values 11850.7476943 and 13641.0663899. These values are interpolated to the face centers of every triangle that constitutes a portion of the surface. Note that reading a new field requires re-calculating the entire surface, so it's not the fastest operation. You can get the vertices of the triangle by looking at the property ``.vertices``. Exporting to a File ------------------- If you want to export this to a `PLY file `_ you can call the routine ``export_ply``, which will write to a file and optionally sample a field at every face or vertex, outputting a color value to the file as well. This file can then be viewed in MeshLab, Blender or on the website `Sketchfab.com `__. But if you want to view it on Sketchfab, there's an even easier way! Exporting to Sketchfab ---------------------- `Sketchfab `__ is a website that uses WebGL, a relatively new technology for displaying 3D graphics in any browser. It's very fast and typically requires no plugins. Plus, it means that you can share data with anyone and they can view it immersively without having to download the data or any software packages! Sketchfab provides a free tier for up to 10 models, and these models can be embedded in websites. There are lots of reasons to want to export to Sketchfab. For instance, if you're looking at a galaxy formation simulation and you publish a paper, you can include a link to the model in that paper (or in the arXiv listing) so that people can explore and see what the data looks like. You can also embed a model in a website with other supplemental data, or you can use Sketchfab to discuss morphological properties of a dataset with collaborators. It's also just plain cool. The ``YTSurface`` object includes a method to upload directly to Sketchfab, but it requires that you get an API key first. You can get this API key by creating an account and then going to your "dashboard," where it will be listed on the right hand side. Once you've obtained it, put it into your ``~/.config/yt/yt.toml`` file under the heading ``[yt]`` as the variable ``sketchfab_api_key``. If you don't want to do this, you can also supply it as an argument to the function ``export_sketchfab``. Now you can run a script like this: .. code-block:: python import yt from yt.units import kpc ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") dd = ds.sphere(ds.domain_center, (500, "kpc")) rho = 1e-28 bounds = [[dd.center[i] - 250 * kpc, dd.center[i] + 250 * kpc] for i in range(3)] surf = ds.surface(dd, ("gas", "density"), rho) upload_id = surf.export_sketchfab( title="galaxy0030 - 1e-28", description="Extraction of Density (colored by temperature) at 1e-28 g/cc", color_field=("gas", "temperature"), color_map="hot", color_log=True, bounds=bounds, ) and yt will extract a surface, convert to a format that Sketchfab.com understands (PLY, in a zip file) and then upload it using your API key. For this demo, I've used data kindly provided by Ryan Joung from a simulation of galaxy formation. Here's what my newly-uploaded model looks like, using the embed code from Sketchfab: .. raw:: html As a note, Sketchfab has a maximum model size of 50MB for the free account. 50MB is pretty hefty, though, so it shouldn't be a problem for most needs. Additionally, if you have an eligible e-mail address associated with a school or university, you can request a free professional account, which allows models up to 200MB. See https://sketchfab.com/education for details. OBJ and MTL Files ----------------- If the ability to maneuver around an isosurface of your 3D simulation in `Sketchfab `__ cost you half a day of work (let's be honest, 2 days), prepare to be even less productive. With a new `OBJ file `__ exporter, you can now upload multiple surfaces of different transparencies in the same file. The following code snippet produces two files which contain the vertex info (surfaces.obj) and color/transparency info (surfaces.mtl) for a 3D galaxy simulation: .. code-block:: python import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") rho = [2e-27, 1e-27] trans = [1.0, 0.5] filename = "./surfaces" sphere = ds.sphere("max", (1.0, "Mpc")) for i, r in enumerate(rho): surf = ds.surface(sphere, ("gas", "density"), r) surf.export_obj( filename, transparency=trans[i], color_field=("gas", "temperature"), plot_index=i, ) The calling sequence is fairly similar to the ``export_ply`` function `previously used `__ to export 3D surfaces. However, one can now specify a transparency for each surface of interest, and each surface is enumerated in the OBJ files with ``plot_index``. This means one could potentially add surfaces to a previously created file by setting ``plot_index`` to the number of previously written surfaces. One tricky thing: the header of the OBJ file points to the MTL file (with the header command ``mtllib``). This means if you move one or both of the files you may have to change the header to reflect their new directory location. A Few More Options ------------------ There are a few extra inputs for formatting the surface files you may want to use. (1) Setting ``dist_fac`` will divide all the vertex coordinates by this factor. Default will scale the vertices by the physical bounds of your sphere. (2) Setting ``color_field_max`` and/or ``color_field_min`` will scale the colors of all surfaces between this min and max. Default is to scale the colors of each surface to their own min and max values. Uploading to SketchFab ---------------------- To upload to `Sketchfab `__ one only needs to zip the OBJ and MTL files together, and then upload via your dashboard prompts in the usual way. For example, the above script produces: .. raw:: html Importing to MeshLab and Blender -------------------------------- The new OBJ formatting will produce multi-colored surfaces in both `MeshLab `__ and `Blender `__, a feature not possible with the `previous PLY exporter `__. To see colors in MeshLab go to the "Render" tab and select "Color -> Per Face". Note in both MeshLab and Blender, unlike Sketchfab, you can't see transparencies until you render. ...One More Option ------------------ If you've started poking around the actual code instead of skipping off to lose a few days running around your own simulations you may have noticed there are a few more options then those listed above, specifically, a few related to something called "Emissivity." This allows you to output one more type of variable on your surfaces. For example: .. code-block:: python import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") rho = [2e-27, 1e-27] trans = [1.0, 0.5] filename = "./surfaces" def emissivity(field, data): return data["gas", "density"] ** 2 * np.sqrt(data["gas", "temperature"]) add_field("emissivity", function=_Emissivity, sampling_type="cell", units=r"g*K/cm**6") sphere = ds.sphere("max", (1.0, "Mpc")) for i, r in enumerate(rho): surf = ds.surface(sphere, ("gas", "density"), r) surf.export_obj( filename, transparency=trans[i], color_field=("gas", "temperature"), emit_field="emissivity", plot_index=i, ) will output the same OBJ and MTL as in our previous example, but it will scale an emissivity parameter by our new field. Technically, this makes our outputs not really OBJ files at all, but a new sort of hybrid file, however we needn't worry too much about that for now. This parameter is useful if you want to upload your files in Blender and have the embedded rendering engine do some approximate ray-tracing on your transparencies and emissivities. This does take some slight modifications to the OBJ importer scripts in Blender. For example, on a Mac, you would modify the file "/Applications/Blender/blender.app/Contents/MacOS/2.65/scripts/addons/io_scene_obj/import_obj.py", in the function "create_materials" with: .. code-block:: diff # ... elif line_lower.startswith(b'tr'): # translucency context_material.translucency = float_func(line_split[1]) elif line_lower.startswith(b'tf'): # rgb, filter color, blender has no support for this. pass elif line_lower.startswith(b'em'): # MODIFY: ADD THIS LINE context_material.emit = float_func(line_split[1]) # MODIFY: THIS LINE TOO elif line_lower.startswith(b'illum'): illum = int(line_split[1]) # ... To use this in Blender, you might create a `Blender script `__ like the following: .. code-block:: python from math import radians import bpy bpy.ops.import_scene.obj(filepath="./surfaces.obj") # will use new importer # set up lighting = indirect bpy.data.worlds["World"].light_settings.use_indirect_light = True bpy.data.worlds["World"].horizon_color = [0.0, 0.0, 0.0] # background = black # have to use approximate, not ray tracing for emitting objects ... # ... for now... bpy.data.worlds["World"].light_settings.gather_method = "APPROXIMATE" bpy.data.worlds["World"].light_settings.indirect_factor = 20.0 # turn up all emiss # set up camera to be on -x axis, facing toward your object scene = bpy.data.scenes["Scene"] scene.camera.location = [-0.12, 0.0, 0.0] # location scene.camera.rotation_euler = [ radians(90.0), 0.0, radians(-90.0), ] # face to (0,0,0) # render scene.render.filepath = "/Users/jillnaiman/surfaces_blender" # needs full path bpy.ops.render.render(write_still=True) This above bit of code would produce an image like so: .. image:: _images/surfaces_blender.png Note that the hottest stuff is brightly shining, while the cool stuff is less so (making the inner isodensity contour barely visible from the outside of the surfaces). If the Blender image caught your fancy, you'll be happy to know there is a greater integration of Blender and yt in the works, so stay tuned! yt-project-yt-f043ac8/doc/source/visualizing/streamlines.rst000066400000000000000000000117311510711153200243460ustar00rootroot00000000000000.. _streamlines: Streamlines: Tracking the Trajectories of Tracers in your Data ============================================================== Streamlines, as implemented in yt, are defined as being parallel to a vector field at all points. While commonly used to follow the velocity flow or magnetic field lines, they can be defined to follow any three-dimensional vector field. Once an initial condition and total length of the streamline are specified, the streamline is uniquely defined. Relatedly, yt also has the ability to follow :doc:`../analyzing/Particle_Trajectories`. Method ------ Streamlining through a volume is useful for a variety of analysis tasks. By specifying a set of starting positions, the user is returned a set of 3D positions that can, in turn, be used to visualize the 3D path of the streamlines. Additionally, individual streamlines can be converted into :class:`~yt.data_objects.construction_data_containers.YTStreamline` objects, and queried for all the available fields along the streamline. The implementation of streamlining in yt is described below. #. Decompose the volume into a set of non-overlapping, fully domain tiling bricks, using the :class:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree` homogenized volume. #. For every streamline starting position: #. While the length of the streamline is less than the requested length: #. Find the brick that contains the current position #. If not already present, generate vertex-centered data for the vector fields defining the streamline. #. While inside the brick #. Integrate the streamline path using a Runge-Kutta 4th order method and the vertex centered data. #. During the intermediate steps of each RK4 step, if the position is updated to outside the current brick, interrupt the integration and locate a new brick at the intermediate position. #. The set of streamline positions are stored in the :class:`~yt.visualization.streamlines.Streamlines` object. Example Script ++++++++++++++ .. python-script:: import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D import yt from yt.units import Mpc from yt.visualization.api import Streamlines # Load the dataset ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # Define c: the center of the box, N: the number of streamlines, # scale: the spatial scale of the streamlines relative to the boxsize, # and then pos: the random positions of the streamlines. c = ds.domain_center N = 100 scale = ds.domain_width[0] pos_dx = np.random.random((N, 3)) * scale - scale / 2.0 pos = c + pos_dx # Create streamlines of the 3D vector velocity and integrate them through # the box defined above streamlines = Streamlines( ds, pos, ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z"), length=1.0 * Mpc, get_magnitude=True, ) streamlines.integrate_through_volume() # Create a 3D plot, trace the streamlines through the 3D volume of the plot fig = plt.figure() ax = Axes3D(fig, auto_add_to_figure=False) fig.add_axes(ax) for stream in streamlines.streamlines: stream = stream[np.all(stream != 0.0, axis=1)] ax.plot3D(stream[:, 0], stream[:, 1], stream[:, 2], alpha=0.1) # Save the plot to disk. plt.savefig("streamlines.png") Data Access Along the Streamline -------------------------------- .. note:: This functionality has not been implemented yet in the 3.x series of yt. If you are interested in working on this and have questions, please let us know on the yt-dev mailing list. Once the streamlines are found, a :class:`~yt.data_objects.construction_data_containers.YTStreamline` object can be created using the :meth:`~yt.visualization.streamlines.Streamlines.path` function, which takes as input the index of the streamline requested. This conversion is done by creating a mask that defines where the streamline is, and creating 't' and 'dts' fields that define the dimensionless streamline integration coordinate and integration step size. Once defined, fields can be accessed in the standard manner. Example Script ++++++++++++++++ .. code-block:: python import matplotlib.pyplot as plt import yt from yt.visualization.api import Streamlines ds = yt.load("DD1701") # Load ds streamlines = Streamlines(ds, ds.domain_center) streamlines.integrate_through_volume() stream = streamlines.path(0) plt.semilogy(stream["t"], stream["gas", "density"], "-x") Running in Parallel -------------------- The integration of the streamline paths is "embarrassingly" parallelized by splitting the streamlines up between the processors. Upon completion, each processor has access to all of the streamlines through the use of a reduction operation. For more information on enabling parallelism in yt, see :ref:`parallel-computation`. yt-project-yt-f043ac8/doc/source/visualizing/unstructured_mesh_rendering.rst000066400000000000000000000367051510711153200276500ustar00rootroot00000000000000.. _unstructured_mesh_rendering: Unstructured Mesh Rendering =========================== Beginning with version 3.3, yt has the ability to volume render unstructured mesh data like that created by finite element calculations. No additional dependencies are required in order to use this feature. However, it is possible to speed up the rendering operation by installing with `Embree `_ support. Embree is a fast ray-tracing library from Intel that can substantially speed up the mesh rendering operation on large datasets. You can read about how to install yt with Embree support below, or you can skip to the examples. Optional Embree Installation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You'll need to `install Python bindings for netCDF4 `_. Then you'll need to get Embree itself and its corresponding Python bindings (pyembree). For conda-based systems, this is trivial, see `pyembree's doc `_ For systems other than conda, you will need to install Embree first, either by `compiling from source `_ or by using one of the pre-built binaries available at Embree's `releases `_ page. Then you'll want to install pyembree from source as follows. .. code-block:: bash git clone https://github.com/scopatz/pyembree To install, navigate to the root directory and run the setup script. If Embree was installed to some location that is not in your path by default, you will need to pass in CFLAGS and LDFLAGS to the setup.py script. For example, the Mac OS X package installer puts the installation at /opt/local/ instead of usr/local. To account for this, you would do: .. code-block:: bash CFLAGS='-I/opt/local/include' LDFLAGS='-L/opt/local/lib' python setup.py install Once Embree and pyembree are installed, a,d in order to use the unstructured mesh rendering capability, you must :ref:`rebuild yt from source `, . Once again, if embree is installed in a location that is not part of your default search path, you must tell yt where to find it. There are a number of ways to do this. One way is to again manually pass in the flags when running the setup script in the yt-git directory: .. code-block:: bash CFLAGS='-I/opt/local/include' LDFLAGS='-L/opt/local/lib' python setup.py develop You can also set EMBREE_DIR environment variable to '/opt/local', in which case you could just run .. code-block:: bash python setup.py develop as usual. Finally, if you create a file called embree.cfg in the yt-git directory with the location of the embree installation, the setup script will find this and use it, provided EMBREE_DIR is not set. An example embree.cfg file could like this: .. code-block:: bash /opt/local/ We recommend one of the later two methods, especially if you plan on re-compiling the cython extensions regularly. Note that none of this is necessary if you installed embree into a location that is in your default path, such as /usr/local. Examples ^^^^^^^^ First, here is an example of rendering an 8-node, hexahedral MOOSE dataset. .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") # create a default scene sc = yt.create_scene(ds) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # adjust the camera position and orientation cam = sc.camera cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-3.0, 3.0, -3.0], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) # increase the default resolution cam.resolution = (800, 800) # render and save sc.save() You can also overplot the mesh boundaries: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") # create a default scene sc = yt.create_scene(ds) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # adjust the camera position and orientation cam = sc.camera cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-3.0, 3.0, -3.0], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) # increase the default resolution cam.resolution = (800, 800) # render, draw the element boundaries, and save sc.render() sc.annotate_mesh_lines() sc.save() As with slices, you can visualize different meshes and different fields. For example, Here is a script similar to the above that plots the "diffused" variable using the mesh labelled by "connect2": .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") # create a default scene sc = yt.create_scene(ds, ("connect2", "diffused")) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # adjust the camera position and orientation cam = sc.camera cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-3.0, 3.0, -3.0], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) # increase the default resolution cam.resolution = (800, 800) # render and save sc.save() Next, here is an example of rendering a dataset with tetrahedral mesh elements. Note that in this dataset, there are multiple "steps" per file, so we specify that we want to look at the last one. .. python-script:: import yt filename = "MOOSE_sample_data/high_order_elems_tet4_refine_out.e" ds = yt.load(filename, step=-1) # we look at the last time frame # create a default scene sc = yt.create_scene(ds, ("connect1", "u")) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # adjust the camera position and orientation cam = sc.camera camera_position = ds.arr([3.0, 3.0, 3.0], "code_length") cam.set_width(ds.arr([2.0, 2.0, 2.0], "code_length")) north_vector = ds.arr([0.0, -1.0, 0.0], "dimensionless") cam.set_position(camera_position, north_vector) # increase the default resolution cam.resolution = (800, 800) # render and save sc.save() Here is an example using 6-node wedge elements: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/wedge_out.e") # create a default scene sc = yt.create_scene(ds, ("connect2", "diffused")) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # adjust the camera position and orientation cam = sc.camera cam.set_position(ds.arr([1.0, -1.0, 1.0], "code_length")) cam.width = ds.arr([1.5, 1.5, 1.5], "code_length") # render and save sc.save() Another example, this time plotting the temperature field from a 20-node hex MOOSE dataset: .. python-script:: import yt # We load the last time frame ds = yt.load("MOOSE_sample_data/mps_out.e", step=-1) # create a default scene sc = yt.create_scene(ds, ("connect2", "temp")) # override the default colormap. This time we also override # the default color bounds ms = sc.get_source() ms.cmap = "hot" ms.color_bounds = (500.0, 1700.0) # adjust the camera position and orientation cam = sc.camera camera_position = ds.arr([-1.0, 1.0, -0.5], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.width = ds.arr([0.04, 0.04, 0.04], "code_length") cam.set_position(camera_position, north_vector) # increase the default resolution cam.resolution = (800, 800) # render, draw the element boundaries, and save sc.render() sc.annotate_mesh_lines() sc.save() The dataset in the above example contains displacement fields, so this is a good opportunity to demonstrate their use. The following example is exactly like the above, except we scale the displacements by a factor of a 10.0, and additionally add an offset to the mesh by 1.0 unit in the x-direction: .. python-script:: import yt # We load the last time frame ds = yt.load( "MOOSE_sample_data/mps_out.e", step=-1, displacements={"connect2": (10.0, [0.01, 0.0, 0.0])}, ) # create a default scene sc = yt.create_scene(ds, ("connect2", "temp")) # override the default colormap. This time we also override # the default color bounds ms = sc.get_source() ms.cmap = "hot" ms.color_bounds = (500.0, 1700.0) # adjust the camera position and orientation cam = sc.camera camera_position = ds.arr([-1.0, 1.0, -0.5], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.width = ds.arr([0.05, 0.05, 0.05], "code_length") cam.set_position(camera_position, north_vector) # increase the default resolution cam.resolution = (800, 800) # render, draw the element boundaries, and save sc.render() sc.annotate_mesh_lines() sc.save() As with other volume renderings in yt, you can swap out different lenses. Here is an example that uses a "perspective" lens, for which the rays diverge from the camera position according to some opening angle: .. python-script:: import yt ds = yt.load("MOOSE_sample_data/out.e-s010") # create a default scene sc = yt.create_scene(ds, ("connect2", "diffused")) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # Create a perspective Camera cam = sc.add_camera(ds, lens_type="perspective") cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-4.5, 4.5, -4.5], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) # increase the default resolution cam.resolution = (800, 800) # render, draw the element boundaries, and save sc.render() sc.annotate_mesh_lines() sc.save() You can also create scenes that have multiple meshes. The ray-tracing infrastructure will keep track of the depth information for each source separately, and composite the final image accordingly. In the next example, we show how to render a scene with two meshes on it: .. python-script:: import yt from yt.visualization.volume_rendering.api import MeshSource, Scene ds = yt.load("MOOSE_sample_data/out.e-s010") # this time we create an empty scene and add sources to it one-by-one sc = Scene() # set up our Camera cam = sc.add_camera(ds) cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam.set_position( ds.arr([-3.0, 3.0, -3.0], "code_length"), ds.arr([0.0, -1.0, 0.0], "dimensionless"), ) cam.set_width = ds.arr([8.0, 8.0, 8.0], "code_length") cam.resolution = (800, 800) # create two distinct MeshSources from 'connect1' and 'connect2' ms1 = MeshSource(ds, ("connect1", "diffused")) ms2 = MeshSource(ds, ("connect2", "diffused")) sc.add_source(ms1) sc.add_source(ms2) # render and save im = sc.render() sc.save() However, in the rendered image above, we note that the color is discontinuous on in the middle and upper part of the cylinder's side. In the original data, there are two parts but the value of ``diffused`` is continuous at the interface. This discontinuous color is due to an independent colormap setting for the two mesh sources. To fix it, we can explicitly specify the colormap bound for each mesh source as follows: .. python-script:: import yt from yt.visualization.volume_rendering.api import MeshSource, Scene ds = yt.load("MOOSE_sample_data/out.e-s010") # this time we create an empty scene and add sources to it one-by-one sc = Scene() # set up our Camera cam = sc.add_camera(ds) cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam.set_position( ds.arr([-3.0, 3.0, -3.0], "code_length"), ds.arr([0.0, -1.0, 0.0], "dimensionless"), ) cam.set_width = ds.arr([8.0, 8.0, 8.0], "code_length") cam.resolution = (800, 800) # create two distinct MeshSources from 'connect1' and 'connect2' ms1 = MeshSource(ds, ("connect1", "diffused")) ms2 = MeshSource(ds, ("connect2", "diffused")) # add the following lines to set the range of the two mesh sources ms1.color_bounds = (0.0, 3.0) ms2.color_bounds = (0.0, 3.0) sc.add_source(ms1) sc.add_source(ms2) # render and save im = sc.render() sc.save() Making Movies ^^^^^^^^^^^^^ Here are a couple of example scripts that show how to create image frames that can later be stitched together into a movie. In the first example, we look at a single dataset at a fixed time, but we move the camera around to get a different vantage point. We call the rotate() method 300 times, saving a new image to the disk each time. .. code-block:: python import numpy as np import yt ds = yt.load("MOOSE_sample_data/out.e-s010") # create a default scene sc = yt.create_scene(ds) # override the default colormap ms = sc.get_source() ms.cmap = "Eos A" # adjust the camera position and orientation cam = sc.camera cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-3.0, 3.0, -3.0], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) # increase the default resolution cam.resolution = (800, 800) # set the camera to use "steady_north" cam.steady_north = True # make movie frames num_frames = 301 for i in range(num_frames): cam.rotate(2.0 * np.pi / num_frames) sc.render() sc.save("movie_frames/surface_render_%.4d.png" % i) Finally, this example demonstrates how to loop over the time steps in a single file with a fixed camera position: .. code-block:: python import matplotlib.pyplot as plt import yt from yt.visualization.volume_rendering.api import MeshSource NUM_STEPS = 127 CMAP = "hot" VMIN = 300.0 VMAX = 2000.0 for step in range(NUM_STEPS): ds = yt.load("MOOSE_sample_data/mps_out.e", step=step) time = ds._get_current_time() # the field name is a tuple of strings. The first string # specifies which mesh will be plotted, the second string # specifies the name of the field. field_name = ('connect2', 'temp') # this initializes the render source ms = MeshSource(ds, field_name) # set up the camera here. these values were arrived by # calling pitch, yaw, and roll in the notebook until I # got the angle I wanted. sc.add_camera(ds) camera_position = ds.arr([0.1, 0.0, 0.1], 'code_length') cam.focus = ds.domain_center north_vector = ds.arr([-0.3032476, -0.71782557, 0.62671153], 'dimensionless') cam.width = ds.arr([ 0.04, 0.04, 0.04], 'code_length') cam.resolution = (800, 800) cam.set_position(camera_position, north_vector) # actually make the image here im = ms.render(cam, cmap=CMAP, color_bounds=(VMIN, VMAX)) # Plot the result using matplotlib and save. # Note that we are setting the upper and lower # bounds of the colorbar to be the same for all # frames of the image. # must clear the image between frames plt.clf() fig = plt.gcf() ax = plt.gca() ax.imshow(im, interpolation='nearest', origin='lower') # Add the colorbar using a fake (not shown) image. p = ax.imshow(ms.data, visible=False, cmap=CMAP, vmin=VMIN, vmax=VMAX) cb = fig.colorbar(p) cb.set_label(field_name[1]) ax.text(25, 750, 'time = %.2e' % time, color='k') ax.axes.get_xaxis().set_visible(False) ax.axes.get_yaxis().set_visible(False) plt.savefig('movie_frames/test_%.3d' % step) yt-project-yt-f043ac8/doc/source/visualizing/visualizing_particle_datasets_with_firefly.rst000066400000000000000000000043411510711153200327110ustar00rootroot00000000000000.. _visualizing_particle_datasets_with_firefly: Visualizing Particle Datasets with Firefly ========================================== `Firefly `_ is an interactive, browser-based, particle visualization platform that allows you to filter, colormap, and fly through their data. The Python frontend allows users to both load in their own datasets and customize every aspect of the user interface. yt offers to ability to export your data to Firefly's ffly or JSON format through the :meth:`~yt.data_objects.data_containers.YTDataContainer.create_firefly_object` method. You can adjust the interface settings, particle colors, decimation factors, and other `Firefly settings `_ through the returned ``Firefly.reader`` object. Once the settings are tuned to your liking, calling the ``reader.writeToDisk()`` method will produce the final ffly files. Note that ``reader.clean_datadir`` defaults to true when using :meth:`~yt.data_objects.data_containers.YTDataContainer.create_firefly_object` so if you would like to manage multiple datasets make sure to pass different ``datadir`` keyword arguments. .. image:: _images/firefly_example.png :width: 85% :align: center :alt: Screenshot of a sample Firefly visualization Exporting an Example Dataset to Firefly ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here is an example of how to use yt to export data to Firefly using some `sample data `_. .. code-block:: python ramses_ds = yt.load("DICEGalaxyDisk_nonCosmological/output_00002/info_00002.txt") region = ramses_ds.sphere(ramses_ds.domain_center, (1000, "kpc")) reader = region.create_firefly_object( "IsoGalaxyRamses", fields_to_include=["particle_extra_field_1", "particle_extra_field_2"], fields_units=["dimensionless", "dimensionless"], ) ## adjust some of the options reader.settings["color"]["io"] = [1, 1, 0, 1] ## set default color reader.particleGroups[0].decimation_factor = 100 ## increase the decimation factor ## dump files to ## ~/IsoGalaxyRamses/Dataio000.ffly ## ~/IsoGalaxyRamses/filenames.json ## ~/IsoGalaxyRamses/DataSettings.json reader.writeToDisk() yt-project-yt-f043ac8/doc/source/visualizing/volume_rendering.rst000066400000000000000000001110771510711153200253700ustar00rootroot00000000000000.. _volume_rendering: 3D Visualization and Volume Rendering ===================================== yt has the ability to create 3D visualizations using a process known as *volume rendering* (oftentimes abbreviated VR). This volume rendering code differs from the standard yt infrastructure for generating :ref:`simple-inspection` in that it evaluates the radiative transfer equations through the volume with user-defined transfer functions for each ray. Thus it can accommodate both opaque and transparent structures appropriately. Currently all of the rendering capabilities are implemented in software, requiring no specialized hardware. Optimized versions implemented with OpenGL and utilizing graphics processors are being actively developed. .. note:: There is a Jupyter notebook containing a volume rendering tutorial: :doc:`Volume_Rendering_Tutorial`. Volume Rendering Introduction ----------------------------- Constructing a 3D visualization is a process of describing the "scene" that will be rendered. This includes the location of the viewing point (i.e., where the "camera" is placed), the method by which a system would be viewed (i.e., the "lens," which may be orthographic, perspective, fisheye, spherical, and so on) and the components that will be rendered (render "sources," such as volume elements, lines, annotations, and opaque surfaces). The 3D plotting infrastructure then develops a resultant image from this scene, which can be saved to a file or viewed inline. By constructing the scene in this programmatic way, full control can be had over each component in the scene as well as the method by which the scene is rendered; this can be used to prototype visualizations, inject annotation such as grid or continent lines, and then to render a production-quality visualization. By changing the "lens" used, a single camera path can output images suitable for planetarium domes, immersive and head tracking systems (such as the Oculus Rift or recent 360-degree/virtual reality movie viewers such as the mobile YouTube app), as well as standard screens. .. image:: _images/scene_diagram.svg :width: 50% :align: center :alt: Diagram of a 3D Scene .. _scene-description: Volume Rendering Components --------------------------- The Scene class and its subcomponents are organized as follows. Indented objects *hang* off of their parent object. * :ref:`Scene ` - container object describing a volume and its contents * :ref:`Sources ` - objects to be rendered * :ref:`VolumeSource ` - simulation volume tied to a dataset * :ref:`TransferFunction ` - mapping of simulation field values to color, brightness, and transparency * :ref:`OpaqueSource ` - Opaque structures like lines, dots, etc. * :ref:`Annotations ` - Annotated structures like grid cells, simulation boundaries, etc. * :ref:`Camera ` - object for rendering; consists of a location, focus, orientation, and resolution * :ref:`Lens ` - object describing method for distributing rays through Sources .. _scene: Scene ^^^^^ The :class:`~yt.visualization.volume_rendering.scene.Scene` is the container class which encompasses the whole of the volume rendering interface. At its base level, it describes an infinite volume, with a series of :class:`~yt.visualization.volume_rendering.render_source.RenderSource` objects hanging off of it that describe the contents of that volume. It also contains a :class:`~yt.visualization.volume_rendering.camera.Camera` for rendering that volume. All of its classes can be accessed and modified as properties hanging off of the scene. The scene's most important functions are :meth:`~yt.visualization.volume_rendering.scene.Scene.render` for casting rays through the scene and :meth:`~yt.visualization.volume_rendering.scene.Scene.save` for saving the resulting rendered image to disk (see note on :ref:`when_to_render`). The easiest way to create a scene with sensible defaults is to use the functions: :func:`~yt.visualization.volume_rendering.volume_rendering.create_scene` (creates the scene) or :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` (creates the scene and then triggers ray tracing to produce an image). See the :ref:`annotated-vr-example` for details. .. _render-sources: RenderSources ^^^^^^^^^^^^^ :class:`~yt.visualization.volume_rendering.render_source.RenderSource` objects comprise the contents of what is actually *rendered*. One can add several different RenderSources to a Scene and the ray-tracing step will pass rays through all of them to produce the final rendered image. .. _volume-sources: VolumeSources +++++++++++++ :class:`~yt.visualization.volume_rendering.render_source.VolumeSource` objects are 3D :ref:`geometric-objects` of individual datasets placed into the scene for rendering. Each VolumeSource requires a :ref:`TransferFunction ` to describe how the fields in the VolumeSource dataset produce different colors and brightnesses in the resulting image. .. _opaque-sources: OpaqueSources +++++++++++++ In addition to semi-transparent objects, fully opaque structures can be added to a scene as :class:`~yt.visualization.volume_rendering.render_source.OpaqueSource` objects including :class:`~yt.visualization.volume_rendering.render_source.LineSource` objects and :class:`~yt.visualization.volume_rendering.render_source.PointSource` objects. These are useful if you want to annotate locations or particles in an image, or if you want to draw lines connecting different regions or vertices. For instance, lines can be used to draw outlines of regions or continents. Worked examples of using the ``LineSource`` and ``PointSource`` are available at :ref:`cookbook-vol-points` and :ref:`cookbook-vol-lines`. .. _volume_rendering_annotations: Annotations +++++++++++ Similar to OpaqueSources, annotations enable the user to highlight certain information with opaque structures. Examples include :class:`~yt.visualization.volume_rendering.api.BoxSource`, :class:`~yt.visualization.volume_rendering.api.GridSource`, and :class:`~yt.visualization.volume_rendering.api.CoordinateVectorSource`. These annotations will operate in data space and can draw boxes, grid information, and also provide a vector orientation within the image. For example scripts using these features, see :ref:`cookbook-volume_rendering_annotations`. .. _transfer_functions: Transfer Functions ^^^^^^^^^^^^^^^^^^ A transfer function describes how rays that pass through the domain of a :class:`~yt.visualization.volume_rendering.render_source.VolumeSource` are mapped from simulation field values to color, brightness, and opacity in the resulting rendered image. A transfer function consists of an array over the x and y dimensions. The x dimension typically represents field values in your underlying dataset to which you want your rendering to be sensitive (e.g. density from 1e20 to 1e23). The y dimension consists of 4 channels for red, green, blue, and alpha (opacity). A transfer function starts with all zeros for its y dimension values, implying that rays traversing the VolumeSource will not show up at all in the final image. However, you can add features to the transfer function that will highlight certain field values in your rendering. .. _transfer-function-helper: TransferFunctionHelper ++++++++++++++++++++++ Because good transfer functions can be difficult to generate, the :class:`~yt.visualization.volume_rendering.transfer_function_helper.TransferFunctionHelper` exists in order to help create and modify transfer functions with smart defaults for your datasets. To ease constructing transfer functions, each ``VolumeSource`` instance has a ``TransferFunctionHelper`` instance associated with it. This is the easiest way to construct and customize a ``ColorTransferFunction`` for a volume rendering. In the following example, we make use of the ``TransferFunctionHelper`` associated with a scene's ``VolumeSource`` to create an appealing transfer function between a physically motivated range of densities in a cosmological simulation: .. python-script:: import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") # Get a reference to the VolumeSource associated with this scene # It is the first source associated with the scene, so we can refer to it # using index 0. source = sc[0] # Set the bounds of the transfer function source.tfh.set_bounds((3e-31, 5e-27)) # set that the transfer function should be evaluated in log space source.tfh.set_log(True) # Make underdense regions appear opaque source.tfh.grey_opacity = True # Plot the transfer function, along with the CDF of the density field to # see how the transfer function corresponds to structure in the CDF source.tfh.plot("transfer_function.png", profile_field=("gas", "density")) # save the image, flooring especially bright pixels for better contrast sc.save("rendering.png", sigma_clip=6.0) For fun, let's make the same volume_rendering, but this time setting ``grey_opacity=False``, which will make overdense regions stand out more: .. python-script:: import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") source = sc[0] # Set transfer function properties source.tfh.set_bounds((3e-31, 5e-27)) source.tfh.set_log(True) source.tfh.grey_opacity = False source.tfh.plot("transfer_function.png", profile_field=("gas", "density")) sc.save("rendering.png", sigma_clip=4.0) To see a full example on how to use the ``TransferFunctionHelper`` interface, follow the annotated :doc:`TransferFunctionHelper_Tutorial`. Color Transfer Functions ++++++++++++++++++++++++ A :class:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction` is the standard way to map dataset field values to colors, brightnesses, and opacities in the rendered rays. One can add discrete features to the transfer function, which will render isocontours in the field data and works well for visualizing nested structures in a simulation. Alternatively, one can also add continuous features to the transfer function. See :ref:`cookbook-custom-transfer-function` for an annotated, runnable tutorial explaining usage of the ColorTransferFunction. There are several methods to create a :class:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction` for a volume rendering. We will describe the low-level interface for constructing color transfer functions here, and provide examples for each option. add_layers """""""""" The easiest way to create a ColorTransferFunction is to use the :meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.add_layers` function, which will add evenly spaced isocontours along the transfer function, sampling a colormap to determine the colors of the layers. .. python-script:: import numpy as np import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") source = sc[0] source.set_field(("gas", "density")) source.set_log(True) bounds = (3e-31, 5e-27) # Since this rendering is done in log space, the transfer function needs # to be specified in log space. tf = yt.ColorTransferFunction(np.log10(bounds)) tf.add_layers(5, colormap="cmyt.arbre") source.tfh.tf = tf source.tfh.bounds = bounds source.tfh.plot("transfer_function.png", profile_field=("gas", "density")) sc.save("rendering.png", sigma_clip=6) sample_colormap """"""""""""""" To add a single gaussian layer with a color determined by a colormap value, use :meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.sample_colormap`. .. python-script:: import numpy as np import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") source = sc[0] source.set_field(("gas", "density")) source.set_log(True) bounds = (3e-31, 5e-27) # Since this rendering is done in log space, the transfer function needs # to be specified in log space. tf = yt.ColorTransferFunction(np.log10(bounds)) tf.sample_colormap(np.log10(1e-30), w=0.01, colormap="cmyt.arbre") source.tfh.tf = tf source.tfh.bounds = bounds source.tfh.plot("transfer_function.png", profile_field=("gas", "density")) sc.save("rendering.png", sigma_clip=6) add_gaussian """""""""""" If you would like to add a gaussian with a customized color or no color, use :meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.add_gaussian`. .. python-script:: import numpy as np import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") source = sc[0] source.set_field(("gas", "density")) source.set_log(True) bounds = (3e-31, 5e-27) # Since this rendering is done in log space, the transfer function needs # to be specified in log space. tf = yt.ColorTransferFunction(np.log10(bounds)) tf.add_gaussian(np.log10(1e-29), width=0.005, height=[0.753, 1.0, 0.933, 1.0]) source.tfh.tf = tf source.tfh.bounds = bounds source.tfh.plot("transfer_function.png", profile_field=("gas", "density")) sc.save("rendering.png", sigma_clip=6) map_to_colormap """"""""""""""" Finally, to map a colormap directly to a range in densities use :meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.map_to_colormap`. This makes it possible to map a segment of the transfer function space to a colormap at a single alpha value. Where the above options produced layered volume renderings, this allows all of the density values in a dataset to contribute to the volume rendering. .. python-script:: import numpy as np import yt ds = yt.load("Enzo_64/DD0043/data0043") sc = yt.create_scene(ds, lens_type="perspective") source = sc[0] source.set_field(("gas", "density")) source.set_log(True) bounds = (3e-31, 5e-27) # Since this rendering is done in log space, the transfer function needs # to be specified in log space. tf = yt.ColorTransferFunction(np.log10(bounds)) def linramp(vals, minval, maxval): return (vals - vals.min()) / (vals.max() - vals.min()) tf.map_to_colormap( np.log10(3e-31), np.log10(5e-27), colormap="cmyt.arbre", scale_func=linramp ) source.tfh.tf = tf source.tfh.bounds = bounds source.tfh.plot("transfer_function.png", profile_field=("gas", "density")) sc.save("rendering.png", sigma_clip=6) Projection Transfer Function ++++++++++++++++++++++++++++ This is designed to allow you to generate projections like what you obtain from the standard :ref:`projection-plots`, and it forms the basis of :ref:`off-axis-projections`. See :ref:`cookbook-offaxis_projection` for a simple example. Note that the integration here is scaled to a width of 1.0; this means that if you want to apply a colorbar, you will have to multiply by the integration width (specified when you initialize the volume renderer) in whatever units are appropriate. Planck Transfer Function ++++++++++++++++++++++++ This transfer function is designed to apply a semi-realistic color field based on temperature, emission weighted by density, and approximate scattering based on the density. This class is currently under-documented, and it may be best to examine the source code to use it. More Complicated Transfer Functions +++++++++++++++++++++++++++++++++++ For more complicated transfer functions, you can use the :class:`~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction` object. This allows for a set of weightings, linkages and so on. All of the information about how all transfer functions are used and values are extracted is contained in the sourcefile ``utilities/lib/grid_traversal.pyx``. For more information on how the transfer function is actually applied, look over the source code there. .. _camera: Camera ^^^^^^ The :class:`~yt.visualization.volume_rendering.camera.Camera` object is what it sounds like, a camera within the Scene. It possesses the quantities: * :meth:`~yt.visualization.volume_rendering.camera.Camera.position` - the position of the camera in scene-space * :meth:`~yt.visualization.volume_rendering.camera.Camera.width` - the width of the plane the camera can see * :meth:`~yt.visualization.volume_rendering.camera.Camera.focus` - the point in space the camera is looking at * :meth:`~yt.visualization.volume_rendering.camera.Camera.resolution` - the image resolution * ``north_vector`` - a vector defining the "up" direction in an image * :ref:`lens ` - an object controlling how rays traverse the Scene .. _camera_movement: Moving and Orienting the Camera +++++++++++++++++++++++++++++++ There are multiple ways to manipulate the camera viewpoint and orientation. One can set the properties listed above explicitly, or one can use the :class:`~yt.visualization.volume_rendering.camera.Camera` helper methods. In either case, any change triggers an update of all of the other properties. Note that the camera exists in a right-handed coordinate system centered on the camera. Rotation-related methods * :meth:`~yt.visualization.volume_rendering.camera.Camera.pitch` - rotate about the lateral axis * :meth:`~yt.visualization.volume_rendering.camera.Camera.yaw` - rotate about the vertical axis (i.e. ``north_vector``) * :meth:`~yt.visualization.volume_rendering.camera.Camera.roll` - rotate about the longitudinal axis (i.e. ``normal_vector``) * :meth:`~yt.visualization.volume_rendering.camera.Camera.rotate` - rotate about an arbitrary axis * :meth:`~yt.visualization.volume_rendering.camera.Camera.iter_rotate` - iteratively rotate about an arbitrary axis For the rotation methods, the camera pivots around the ``rot_center`` rotation center. By default, this is the camera position, which means that the camera doesn't change its position at all, it just changes its orientation. Zoom-related methods * :meth:`~yt.visualization.volume_rendering.camera.Camera.set_width` - change the width of the FOV * :meth:`~yt.visualization.volume_rendering.camera.Camera.zoom` - change the width of the FOV * :meth:`~yt.visualization.volume_rendering.camera.Camera.iter_zoom` - iteratively change the width of the FOV Perhaps counterintuitively, the camera does not get closer to the focus during a zoom; it simply reduces the width of the field of view. Translation-related methods * :meth:`~yt.visualization.volume_rendering.camera.Camera.set_position` - change the location of the camera keeping the focus fixed * :meth:`~yt.visualization.volume_rendering.camera.Camera.iter_move` - iteratively change the location of the camera keeping the focus fixed The iterative methods provide iteration over a series of changes in the position or orientation of the camera. These can be used within a loop. For an example on how to use all of these camera movement functions, see :ref:`cookbook-camera_movement`. .. _lenses: Camera Lenses ^^^^^^^^^^^^^ Cameras possess :class:`~yt.visualization.volume_rendering.lens.Lens` objects, which control the geometric path in which rays travel to the camera. These lenses can be swapped in and out of an existing camera to produce different views of the same Scene. For a full demonstration of a Scene object rendered with different lenses, see :ref:`cookbook-various_lens`. Plane Parallel ++++++++++++++ The :class:`~yt.visualization.volume_rendering.lens.PlaneParallelLens` is the standard lens type used for orthographic projections. All rays emerge parallel to each other, arranged along a plane. Perspective and Stereo Perspective ++++++++++++++++++++++++++++++++++ The :class:`~yt.visualization.volume_rendering.lens.PerspectiveLens` adjusts for an opening view angle, so that the scene will have an element of perspective to it. :class:`~yt.visualization.volume_rendering.lens.StereoPerspectiveLens` is identical to PerspectiveLens, but it produces two images from nearby camera positions for use in 3D viewing. How 3D the image appears at viewing will depend upon the value of :attr:`~yt.visualization.volume_rendering.lens.StereoPerspectiveLens.disparity`, which is half the maximum distance between two corresponding points in the left and right images. By default, it is set to 3 pixels. Fisheye or Dome +++++++++++++++ The :class:`~yt.visualization.volume_rendering.lens.FisheyeLens` is appropriate for viewing an arbitrary field of view. Fisheye images are typically used for dome-based presentations; the Hayden planetarium for instance has a field of view of 194.6. The images returned by this camera will be flat pixel images that can and should be reshaped to the resolution. Spherical and Stereo Spherical ++++++++++++++++++++++++++++++ The :class:`~yt.visualization.volume_rendering.lens.SphericalLens` produces a cylindrical-spherical projection. Movies rendered in this way can be displayed as YouTube 360-degree videos (for more information see `the YouTube help: Upload 360-degree videos `_). :class:`~yt.visualization.volume_rendering.lens.StereoSphericalLens` is identical to :class:`~yt.visualization.volume_rendering.lens.SphericalLens` but it produces two images from nearby camera positions for virtual reality movies, which can be displayed in head-tracking devices (e.g. Oculus Rift) or in mobile YouTube app with Google Cardboard (for more information see `the YouTube help: Upload virtual reality videos `_). `This virtual reality video `_ on YouTube is an example produced with :class:`~yt.visualization.volume_rendering.lens.StereoSphericalLens`. As in the case of :class:`~yt.visualization.volume_rendering.lens.StereoPerspectiveLens`, the difference between the two images can be controlled by changing the value of :attr:`~yt.visualization.volume_rendering.lens.StereoSphericalLens.disparity` (See above). .. _annotated-vr-example: Annotated Examples ------------------ .. warning:: 3D visualizations can be fun but frustrating! Tuning the parameters to both look nice and convey useful scientific information can be hard. We've provided information about best practices and tried to make the interface easy to develop nice visualizations, but getting them *just right* is often time-consuming. It's usually best to start out simple and expand and tweak as needed. The scene interface provides a modular interface for creating renderings of arbitrary data sources. As such, manual composition of a scene can require a bit more work, but we will also provide several helper functions that attempt to create satisfactory default volume renderings. When the :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` function is called, first an empty :class:`~yt.visualization.volume_rendering.scene.Scene` object is created. Next, a :class:`~yt.visualization.volume_rendering.api.VolumeSource` object is created, which decomposes the volume elements into a tree structure to provide back-to-front rendering of fixed-resolution blocks of data. (If the volume elements are grids, this uses a :class:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree` object.) When the :class:`~yt.visualization.volume_rendering.api.VolumeSource` object is created, by default it will create a transfer function based on the extrema of the field that you are rendering. The transfer function describes how rays that pass through the domain are "transferred" and thus how brightness and color correlates to the field values. Modifying and adjusting the transfer function is the primary way to modify the appearance of an image based on volumes. Once the basic set of objects to be rendered is constructed (e.g. :class:`~yt.visualization.volume_rendering.scene.Scene`, :class:`~yt.visualization.volume_rendering.render_source.RenderSource`, and :class:`~yt.visualization.volume_rendering.api.VolumeSource` objects) , a :class:`~yt.visualization.volume_rendering.camera.Camera` is created and added to the scene. By default the creation of a camera also creates a plane-parallel :class:`~yt.visualization.volume_rendering.lens.Lens` object. The analog to a real camera is intentional -- a camera can take a picture of a scene from a particular point in time and space, but different lenses can be swapped in and out. For example, this might include a fisheye lens, a spherical lens, or some other method of describing the direction and origin of rays for rendering. Once the camera is added to the scene object, we call the main methods of the :class:`~yt.visualization.volume_rendering.scene.Scene` class, :meth:`~yt.visualization.volume_rendering.scene.Scene.render` and :meth:`~yt.visualization.volume_rendering.scene.Scene.save`. When rendered, the scene will loop through all of the :class:`~yt.visualization.volume_rendering.render_source.RenderSource` objects that have been added and integrate the radiative transfer equations through the volume. Finally, the image and scene object is returned to the user. An example script the uses the high-level :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` function to quickly set up defaults is: .. python-script:: import yt # load the data ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") # volume render the ("gas", "density") field, and save the resulting image im, sc = yt.volume_render(ds, ("gas", "density"), fname="rendering.png") # im is the image array generated. it is also saved to 'rendering.png'. # sc is an instance of a Scene object, which allows you to further refine # your renderings and later save them. # Let's zoom in and take a closer look sc.camera.width = (300, "kpc") sc.camera.switch_orientation() # Save the zoomed in rendering sc.save("zoomed_rendering.png") Alternatively, if you don't want to immediately generate an image of your volume rendering, and you just want access to the default scene object, you can skip the expensive operation of rendering by just running the :func:`~yt.visualization.volume_rendering.volume_rendering.create_scene` function in lieu of the :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` function. Example: .. python-script:: import numpy as np import yt ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") sc = yt.create_scene(ds, ("gas", "density")) source = sc[0] source.transfer_function = yt.ColorTransferFunction( np.log10((1e-30, 1e-23)), grey_opacity=True ) def linramp(vals, minval, maxval): return (vals - vals.min()) / (vals.max() - vals.min()) source.transfer_function.map_to_colormap( np.log10(1e-25), np.log10(8e-24), colormap="cmyt.arbre", scale_func=linramp ) # For this low resolution dataset it's very important to use interpolated # vertex centered data to avoid artifacts. For high resolution data this # setting may cause a substantial slowdown for marginal visual improvement. source.set_use_ghost_zones(True) cam = sc.camera cam.width = 15 * yt.units.kpc cam.focus = ds.domain_center cam.normal_vector = [-0.3, -0.3, 1] cam.switch_orientation() sc.save("rendering.png") For an in-depth tutorial on how to create a Scene and modify its contents, see this annotated :doc:`Volume_Rendering_Tutorial`. .. _volume-rendering-method: Volume Rendering Method ----------------------- Direct ray casting through a volume enables the generation of new types of visualizations and images describing a simulation. yt has the facility to generate volume renderings by a direct ray casting method. However, the ability to create volume renderings informed by analysis by other mechanisms -- for instance, halo location, angular momentum, spectral energy distributions -- is useful. The volume rendering in yt follows a relatively straightforward approach. #. Create a set of transfer functions governing the emission and absorption as a function of one or more variables. (:math:`f(v) \rightarrow (r,g,b,a)`) These can be functions of any field variable, weighted by independent fields, and even weighted by other evaluated transfer functions. (See ref:`_transfer_functions`.) #. Partition all chunks into non-overlapping, fully domain-tiling "bricks." Each of these "bricks" contains the finest available data at any location. #. Generate vertex-centered data for all grids in the volume rendered domain. #. Order the bricks from front-to-back. #. Construct plane of rays parallel to the image plane, with initial values set to zero and located at the back of the region to be rendered. #. For every brick, identify which rays intersect. These are then each 'cast' through the brick. #. Every cell a ray intersects is sampled 5 times (adjustable by parameter), and data values at each sampling point are trilinearly interpolated from the vertex-centered data. #. Each transfer function is evaluated at each sample point. This gives us, for each channel, both emission (:math:`j`) and absorption (:math:`\alpha`) values. #. The value for the pixel corresponding to the current ray is updated with new values calculated by rectangular integration over the path length: :math:`v^{n+1}_{i} = j_{i}\Delta s + (1 - \alpha_{i}\Delta s )v^{n}_{i}` where :math:`n` and :math:`n+1` represent the pixel before and after passing through a sample, :math:`i` is the color (red, green, blue) and :math:`\Delta s` is the path length between samples. #. Determine if any addition integrate will change the sample value; if not, terminate integration. (This reduces integration time when rendering front-to-back.) #. The image is returned to the user: .. image:: _images/vr_sample.jpg :width: 512 Parallelism ----------- yt can utilize both MPI and OpenMP parallelism for volume rendering. Both, and their combination, are described below. MPI Parallelization ^^^^^^^^^^^^^^^^^^^ Currently the volume renderer is parallelized using MPI to decompose the volume by attempting to split up the :class:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree` in a balanced way. This has two advantages: #. The :class:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree` construction is parallelized since each MPI task only needs to know about the part of the tree it will traverse. #. Each MPI task will only read data for portion of the volume that it has assigned. Once the :class:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree` has been constructed, each MPI task begins the rendering phase until all of its bricks are completed. At that point, each MPI task has a full image plane which we then use a tree reduction to construct the final image, using alpha blending to add the images together at each reduction phase. Caveats: #. At this time, the :class:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree` can only be decomposed by a power of 2 MPI tasks. If a number of tasks not equal to a power of 2 are used, the largest power of 2 below that number is used, and the remaining cores will be idle. This issue is being actively addressed by current development. #. Each MPI task, currently, holds the entire image plane. Therefore when image plane sizes get large (>2048^2), the memory usage can also get large, limiting the number of MPI tasks you can use. This is also being addressed in current development by using image plane decomposition. For more information about enabling parallelism, see :ref:`parallel-computation`. OpenMP Parallelization ^^^^^^^^^^^^^^^^^^^^^^ The volume rendering also parallelized using the OpenMP interface in Cython. While the MPI parallelization is done using domain decomposition, the OpenMP threading parallelizes the rays intersecting a given brick of data. As the average brick size relative to the image plane increases, the parallel efficiency increases. By default, the volume renderer will use the total number of cores available on the symmetric multiprocessing (SMP) compute platform. For example, if you have a shiny new laptop with 8 cores, you'll by default launch 8 OpenMP threads. The number of threads can be controlled with the num_threads keyword in :meth:`~yt.visualization.volume_rendering.camera.Camera.snapshot`. You may also restrict the number of OpenMP threads used by default by modifying the environment variable OMP_NUM_THREADS. Running in Hybrid MPI + OpenMP ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The two methods for volume rendering parallelization can be used together to leverage large supercomputing resources. When choosing how to balance the number of MPI tasks vs OpenMP threads, there are a few things to keep in mind. For these examples, we will assume you are using Nmpi MPI tasks, and Nmp OpenMP tasks, on a total of P cores. We will assume that the machine has a Nnode SMP nodes, each with cores_per_node cores per node. #. For each MPI task, num_threads (or OMP_NUM_THREADS) OpenMP threads will be used. Therefore you should usually make sure that Nmpi*Nmp = P. #. For simulations with many grids/AMRKDTree bricks, you generally want to increase Nmpi. #. For simulations with large image planes (>2048^2), you generally want to decrease Nmpi and increase Nmp. This is because, currently, each MPI task stores the entire image plane, and doing so can approach the memory limits of a given SMP node. #. Please make sure you understand the (super)computer topology in terms of the numbers of cores per socket, node, etc when making these decisions. #. For many cases when rendering using your laptop/desktop, OpenMP will provide a good enough speedup by default that it is preferable to launching the MPI tasks. For more information about enabling parallelism, see :ref:`parallel-computation`. .. _vr-faq: Volume Rendering Frequently Asked Questions ------------------------------------------- .. _opaque_rendering: Opacity ^^^^^^^ There are currently two models for opacity when rendering a volume, which are controlled in the ``ColorTransferFunction`` with the keyword ``grey_opacity=False`` or ``True`` (the default). The first will act such for each of the red, green, and blue channels, each channel is only opaque to itself. This means that if a ray that has some amount of red then encounters material that emits blue, the red will still exist and in the end that pixel will be a combination of blue and red. However, if the ColorTransferFunction is set up with grey_opacity=True, then blue will be opaque to red, and only the blue emission will remain. For an in-depth example, please see the cookbook example on opaque renders here: :ref:`cookbook-opaque_rendering`. .. _sigma_clip: Improving Image Contrast with Sigma Clipping ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If your images appear to be too dark, you can try using the ``sigma_clip`` keyword in the :meth:`~yt.visualization.volume_rendering.scene.Scene.save` or :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` functions. Because the brightness range in an image is scaled to match the range of emissivity values of underlying rendering, if you have a few really high-emissivity points, they will scale the rest of your image to be quite dark. ``sigma_clip = N`` can address this by removing values that are more than ``N`` standard deviations brighter than the mean of your image. Typically, a choice of 4 to 6 will help dramatically with your resulting image. See the cookbook recipe :ref:`cookbook-sigma_clip` for a demonstration. .. _when_to_render: When to Render ^^^^^^^^^^^^^^ The rendering of a scene is the most computationally demanding step in creating a final image and there are a number of ways to control at which point a scene is actually rendered. The default behavior of the :meth:`~yt.visualization.volume_rendering.scene.Scene.save` function includes a call to :meth:`~yt.visualization.volume_rendering.scene.Scene.render`. This means that in most cases (including the above examples), after you set up your scene and volumes, you can simply call :meth:`~yt.visualization.volume_rendering.scene.Scene.save` without first calling :meth:`~yt.visualization.volume_rendering.scene.Scene.render`. If you wish to save the most recently rendered image without rendering again, set ``render=False`` in the call to :meth:`~yt.visualization.volume_rendering.scene.Scene.save`. Cases where you may wish to use ``render=False`` include saving images at different ``sigma_clip`` values (see :ref:`cookbook-sigma_clip`) or when saving an image that has already been rendered in a Jupyter notebook using :meth:`~yt.visualization.volume_rendering.scene.Scene.show`. Changes to the scene including adding sources, modifying transfer functions or adjusting camera settings generally require rendering again. yt-project-yt-f043ac8/doc/source/yt3differences.rst000066400000000000000000000335161510711153200223760ustar00rootroot00000000000000.. _yt3differences: What's New and Different in yt 3.0? =================================== If you are new to yt, welcome! If you're coming to yt 3.0 from an older version, however, there may be a few things in this version that are different than what you are used to. We have tried to build compatibility layers to minimize disruption to existing scripts, but necessarily things will be different in some ways. .. contents:: :depth: 2 :local: :backlinks: none Updating to yt 3.0 from Old Versions (and going back) ----------------------------------------------------- First off, you need to update your version of yt to yt 3.0. If you're installing yt for the first time, please visit :ref:`installing-yt`. If you already have a version of yt installed, you should just need one command: .. code-block:: bash $ yt update This will update yt to the most recent version and rebuild the source base. If you installed using the installer script, it will assure you have all of the latest dependencies as well. This step may take a few minutes. To test that yt is correctly installed, try: .. code-block:: bash $ python -c "import yt" .. _transitioning-to-3.0: Converting Old Scripts to Work with yt 3.0 ------------------------------------------ After installing yt-3.0, you'll want to change your old scripts in a few key ways. After accounting for the changes described in the list below, try running your script. If it still fails, the callback failures in python are fairly descriptive and it may be possible to deduce what remaining changes are necessary. If you continue to have trouble, please don't hesitate to :ref:`request help `. The list below is arranged in order of most important changes to least important changes. * **Replace** ``from yt.mods import *`` **with** ``import yt`` **and prepend yt classes and functions with** ``yt.`` We have reworked yt's import system so that most commonly-used yt functions and classes live in the top-level yt namespace. That means you can now import yt with ``import yt``, load a dataset with ``ds = yt.load(filename)`` and create a plot with ``yt.SlicePlot``. See :ref:`api-reference` for a full API listing. You can still import using ``from yt.mods import *`` to get a pylab-like experience. * **Unit conversions are different** Fields and metadata for data objects and datasets now have units. The unit system keeps you from making weird things like ``ergs`` + ``g`` and can handle things like ``g`` + ``kg`` or ``kg*m/s**2 == Newton``. See :ref:`units` and :ref:`conversion-factors` for more information. * **Change field names from CamelCase to lower_case_with_underscores** Previously, yt would use "Enzo-isms" for field names. We now very specifically define fields as lowercase with underscores. For instance, what used to be ``VelocityMagnitude`` would now be ``velocity_magnitude``. Axis names are now at the *end* of field names, not the beginning. ``x-velocity`` is now ``velocity_x``. For a full list of all of the fields, see :ref:`field-list`. * **Full field names have two parts now** Fields can be accessed by a single name, but they are named internally as ``(field_type, field_name)`` for more explicit designation which can address particles, deposited fluid quantities, and more. See :ref:`fields`. * **Code-specific field names can be accessed by the name defined by the external code** Mesh fields that exist on-disk in an output file can be read in using whatever name is used by the output file. On-disk fields are always returned in code units. The full field name will be ``(code_name, field_name)``. See :ref:`field-list`. * **Particle fields are now more obviously different than mesh fields** Particle fields on-disk will also be in code units, and will be named ``(particle_type, field_name)``. If there is only one particle type in the output file, all particles will use ``io`` as the particle type. See :ref:`fields`. * **Change** ``pf`` **to** ``ds`` The objects we used to refer to as "parameter files" we now refer to as datasets. Instead of ``pf``, we now suggest you use ``ds`` to refer to an object returned by ``yt.load``. * **Remove any references to** ``pf.h`` **with** ``ds`` You can now create data objects without referring to the hierarchy. Instead of ``pf.h.all_data()``, you can now say ``ds.all_data()``. The hierarchy is still there, but it is now called the index: ``ds.index``. * **Use** ``yt.enable_parallelism()`` **to make a script parallel-compatible** Command line arguments are only parsed when yt is imported using ``from yt.mods import *``. Since command line arguments are not parsed when using ``import yt``, it is no longer necessary to specify ``--parallel`` at the command line when running a parallel computation. Use ``yt.enable_parallelism()`` in your script instead. See :ref:`parallel-computation` for more details. * **Change your derived quantities to the new syntax** Derived quantities have been reworked. You can now do ``dd.quantities.total_mass()`` instead of ``dd.quantities['TotalMass']()``. See :ref:`derived-quantities`. * **Change your method of accessing the** ``grids`` **attribute** The ``grids`` attribute of data objects no longer exists. To get this information, you have to use spatial chunking and then access them. See :ref:`here ` for an example. For datasets that use grid hierarchies, you can also access the grids for the entire dataset via ``ds.index.grids``. This attribute is not defined for particle or octree datasets. Cool New Things --------------- Lots of new things have been added in yt 3.0! Below we summarize a handful of these. Lots of New Codes are Supported ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Because of the additions of **Octrees**, **Particle Deposition**, and **Irregular Grids**, we now support a bunch more codes. See :ref:`code-support` for more information. Octrees ^^^^^^^ Octree datasets such as RAMSES, ART and ARTIO are now supported -- without any regridding! We have a native, lightweight octree indexing system. Irregular Grids ^^^^^^^^^^^^^^^ MOAB Hex8 format is supported, and non-regular grids can be added relatively easily. Better Particle Support ^^^^^^^^^^^^^^^^^^^^^^^ Particle Codes and SPH """""""""""""""""""""" yt 3.0 features particle selection, smoothing, and deposition. This utilizes a combination of coarse-grained indexing and octree indexing for particles. Particle Deposition """"""""""""""""""" In yt-3.0, we provide mechanisms for describing and creating fields generated by depositing particles into one or a handful of zones. This could include deposited mass or density, average values, and the like. For instance, the total stellar mass in some region can be deposited and averaged. Particle Filters and Unions """"""""""""""""""""""""""" Throughout yt, the notion of "particle types" has been more deeply embedded. These particle types can be dynamically defined at runtime, for instance by taking a filter of a given type or the union of several different types. This might be, for instance, defining a new type called ``young_stars`` that is a filtering of ``star_age`` to be fewer than a given threshold, or ``fast`` that filters based on the velocity of a particle. Unions could be the joining of multiple types of particles -- the default union of which is ``all``, representing all particle types in the simulation. Units ^^^^^ yt now has a unit system. This is one of the bigger features, and in essence it means that you can convert units between anything. In practice, it makes it much easier to define fields and convert data between different unit systems. See :ref:`units` for more information. Non-Cartesian Coordinates ^^^^^^^^^^^^^^^^^^^^^^^^^ Preliminary support for non-cartesian coordinates has been added. We expect this to be considerably solidified and expanded in yt 3.1. Reworked Import System ^^^^^^^^^^^^^^^^^^^^^^ It's now possible to import all yt functionality using ``import yt``. Rather than using ``from yt.mods import *``, we suggest using ``import yt`` in new scripts. Most commonly used yt functionality is attached to the ``yt`` module. Load a dataset with ``yt.load()``, create a phase plot using ``yt.PhasePlot``, and much more, see :ref:`the api docs ` to learn more about what's in the ``yt`` namespace, or just use tab completion in IPython: ``yt.``. It's still possible to use ``from yt.mods import *`` to create an interactive pylab-like experience. Importing yt this way has several side effects, most notably the command line arguments parsing and other startup tasks will run. API Changes ----------- These are the items that have already changed in *user-facing* API: Field Naming ^^^^^^^^^^^^ .. warning:: Field naming is probably the single biggest change you will encounter in yt 3.0. Fields can be accessed by their short names, but yt now has an explicit mechanism of distinguishing between field types and particle types. This is expressed through a two-key description. For example:: my_object["gas", "density"] will return the gas field density. In this example "gas" is the field type and "density" is the field name. Field types are a bit like a namespace. This system extends to particle types as well. By default you do *not* need to use the field "type" key, but in case of ambiguity it will utilize the default value in its place. This should therefore be identical to:: my_object["density"] To enable a compatibility layer, on the dataset you simply need to call the method ``setup_deprecated_fields`` like so: .. code-block:: python ds = yt.load("MyData") ds.setup_deprecated_fields() This sets up aliases from the old names to the new. See :ref:`fields` and :ref:`field-list` for more information. Units of Fields ^^^^^^^^^^^^^^^ Fields now are all subclasses of NumPy arrays, the ``YTArray``, which carries along with it units. This means that if you want to manipulate fields, you have to modify them in a unitful way. See :ref:`units`. Parameter Files are Now Datasets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Wherever possible, we have attempted to replace the term "parameter file" (i.e., ``pf``) with the term "dataset." In yt-3.0, all of the ``pf`` attributes of objects are now ``ds`` or ``dataset`` attributes. Hierarchy is Now Index ^^^^^^^^^^^^^^^^^^^^^^ The hierarchy object (``pf.h``) is now referred to as an index (``ds.index``). It is no longer necessary to directly refer to the ``index`` as often, since data objects are now attached to the to the ``dataset`` object. Before, you would say ``pf.h.sphere()``, now you can say ``ds.sphere()``. New derived quantities interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Derived quantities can now be accessed via a function that hangs off of the ``quantities`` attribute of data objects. Instead of ``dd.quantities['TotalMass']()``, you can now use ``dd.quantities.total_mass()`` to do the same thing. All derived quantities can be accessed via a function that hangs off of the ``quantities`` attribute of data objects. Any derived quantities that *always* returned lists (like ``Extrema``, which would return a list even if you only ask for one field) now only returns a single result if you only ask for one field. Results for particle and mesh fields will also be returned separately. See :ref:`derived-quantities` for more information. Field Info ^^^^^^^^^^ In previous versions of yt, the ``dataset`` object (what we used to call a parameter file) had a ``field_info`` attribute which was a dictionary leading to derived field definitions. At the present time, because of the field naming changes (i.e., access-by-tuple) it is better to utilize the function ``_get_field_info`` than to directly access the ``field_info`` dictionary. For example:: finfo = ds._get_field_info("gas", "density") This function respects the special "field type" ``unknown`` and will search all field types for the field name. Projection Argument Order ^^^^^^^^^^^^^^^^^^^^^^^^^ Previously, projections were inconsistent with the other data objects. (The API for Plot Windows is the same.) The argument order is now ``field`` then ``axis`` as seen here: :class:`~yt.data_objects.construction_data_containers.YTQuadTreeProj`. Field Parameters ^^^^^^^^^^^^^^^^ All data objects now accept an explicit list of ``field_parameters`` rather than accepting ``kwargs`` and supplying them to field parameters. See :ref:`field_parameters`. Object Renaming ^^^^^^^^^^^^^^^ Nearly all internal objects have been renamed. Typically this means either removing ``AMR`` from the prefix or replacing it with ``YT``. All names of objects remain the same for the purposes of selecting data and creating them; i.e., ``sphere`` objects are still called ``sphere`` - you can access or create one via ``ds.sphere``. For a detailed description and index see :ref:`available-objects`. Boolean Regions ^^^^^^^^^^^^^^^ Boolean regions are not yet implemented in yt 3.0. .. _grid-chunking: Grids ^^^^^ It used to be that one could get access to the grids that belonged to a data object. Because we no longer have just grid-based data in yt, this attribute does not make sense. If you need to determine which grids contribute to a given object, you can either query the ``grid_indices`` field, or mandate spatial chunking like so: .. code-block:: python for chunk in obj.chunks([], "spatial"): for grid in chunk._current_chunk.objs: print(grid) This will "spatially" chunk the ``obj`` object and print out all the grids included. Halo Catalogs ^^^^^^^^^^^^^ The ``Halo Profiler`` infrastructure has been fundamentally rewritten and now exists using the ``Halo Catalog`` framework. See :ref:`halo-analysis`. Analysis Modules ^^^^^^^^^^^^^^^^ While we're trying to port over all of the old analysis modules, we have not gotten all of them working in 3.0 yet. The docs pages for those modules not-yet-functioning are clearly marked. yt-project-yt-f043ac8/doc/source/yt4differences.rst000066400000000000000000000535231510711153200223770ustar00rootroot00000000000000.. _yt4differences: What's New and Different in yt 4.0? =================================== If you are new to yt, welcome! If you're coming to yt 4.0 from an older version, however, there may be a few things in this version that are different than what you are used to. We have tried to build compatibility layers to minimize disruption to existing scripts, but necessarily things will be different in some ways. .. contents:: :depth: 2 :local: :backlinks: none Updating to yt 4.0 from Old Versions (and going back) ----------------------------------------------------- .. _transitioning-to-4.0: Converting Old Scripts to Work with yt 4.0 ------------------------------------------ After installing yt-4.0, you’ll want to change your old scripts in a few key ways. After accounting for the changes described in the list below, try running your script. If it still fails, the Python tracebacks should be fairly descriptive and it may be possible to deduce what remaining changes are necessary. If you continue to have trouble, please don’t hesitate to :ref:`request help `. The list below is arranged in order of most to least important changes. * **Fields should be specified as tuples not as strings** In the past, you could specify fields as strings like ``"density"``, but with the growth of yt and its many derived fields, there can be sometimes be overlapping field names (e.g., ``("gas", "density")`` and ``("PartType0", "density")``), where yt doesn't know which to use. To remove any ambiguity, it is now strongly recommended to explicitly specify the full tuple form of all fields. Just search for all field accesses in your scripts, and replace strings with tuples (e.g. replace ``"a"`` with ``("gas", "a" )``). There is a compatibility rule in yt-4.0 to allow strings to continue to work until yt-4.1, but you may get unexpected behavior. Any field specifications that are ambiguous will throw an error in future versions of yt. See our :ref:`fields`, and :ref:`available field list ` documentation for more information. * **Use Newer Versions of Python** The yt-4.0 release will be the final release of yt to support Python 3.6. Starting with yt-4.1, python 3.6 will no longer be supported, so please start using 3.7+ as soon as possible. * **Particle-based datasets no longer accept n_ref and over_refine_factor** One of the major upgrades in yt-4 is native treatment of particle-based datasets. This is in contrast to previous yt behavior which loaded particle-based datasets as octrees, which could then be treated like grid-based datasets. In order to define the octrees, users were required to specify ``n_ref`` and ``over_refine_factor`` values at load time. Please remove any reference to ``n_ref`` and ``over_refine_factor`` in your scripts. * **Neutral ion fields changing format** In previous versions, neutral ion fields were specified as ``ELEMENT_number_density`` (e.g., ``H_number_density`` to represent H I number density). This led to a lot of confusion, because some people assumed these fields were the total hydrogen density, not neutral hydrogen density. In yt-4.0, we have resolved this issue by explicitly calling total hydrogen number density ``H_nuclei_density`` and neutral hydrogen density ``H_p0_number_density`` (where ``p0`` refers to plus 0 charge). This syntax follows the rule for other ions: H II = ``H_p1`` = ionized hydrogen. Change your scripts accordingly. See :ref:`species-fields` for more information. * **Change in energy and momentum field names** Fields representing energy and momentum quantities are now given names which reflect their dimensionality. For example, the ``("gas", "kinetic_energy")`` field was actually a field for kinetic energy density, and so it has been renamed to ``("gas", "kinetic_energy_density")``. The old name still exists as an alias as of yt v4.0.0, but it will be removed in yt v4.1.0. See next item below for more information. Other examples include ``"gas", "specific_thermal_energy"`` for thermal energy per unit mass, and ``("gas", "momentum_density_x")`` for the x-axis component of momentum density. See :ref:`efields` for more information. * **Deprecated field names** Certain field names are deprecated within yt v4.0.x and removed in yt v4.1. For example, ``("gas", "kinetic_energy")`` has been renamed to ``("gas", "kinetic_energy_density")``, though the former name has been added as an alias. Other fields, such as ``("gas", "cylindrical_tangential_velocity_absolute")``, are being removed entirely. When the deprecated field names are used for the first time in a session, a warning will be logged, so it is advisable to set your logging level to ``WARNING`` (``yt.set_log_level("error")``) at a minimum to catch these. See :ref:`faq-log-level` for more information on setting your log level and :ref:`available-fields` to see all available fields. * ``cmocean`` **colormaps need prefixing** yt used to automatically load and register external colormaps from the ``cmocean`` package unprefixed (e.g., ``set_cmap(FIELD, "balance")``. This became unsustainable with the 3.4 release of Matplotlib, in which colormaps with colliding names raise errors. The fix is to explicitly import the ``cmocean`` module and prefix ``cmocean`` colormaps (like ``balance``) with ``cmo.`` (e.g., ``cmo.balance``). Note that this solution works with any yt-supported version of Matplotlib, but is not backward compatible with earlier versions of yt. * Position and velocity fields now default to using linear scaling in profiles and phase plots, whereas previously behavior was determined by whether the dataset was particle- or grid-based. Efforts have been made to standardize the treatment of other fields in profile and phase plots for particle and grid datasets. Important New Aliases ^^^^^^^^^^^^^^^^^^^^^ With the advent of supporting SPH data at the particle level instead of smoothing onto an octree (see below), a new alias for both gas particle masses and cell masses has been created: ``("gas", "mass")``, which aliases to ``("gas", "cell_mass")`` for grid-based frontends and to the gas particle mass for SPH frontends. In a number of places in yt, code that used ``("gas", "cell_mass")`` has been replaced by ``("gas", "mass")``. Since the latter is an alias for the former, old scripts which use ``("gas", "cell_mass")`` should not break. Deprecations ^^^^^^^^^^^^ The following methods and method arguments are deprecated as of yt 4.0 and will be removed in yt 4.1 * :meth:`~yt.visualization.plot_window.PlotWindow.set_window_size` is deprecated in favor to :meth:`~yt.visualization.plot_container.PlotContainer.set_figure_size` * :meth:`~yt.visualization.eps_writer.return_cmap` is deprecated in favor to :meth:`~yt.visualization.eps_writer.return_colormap` * :meth:`~yt.data_objects.derived_quantities.WeightedVariance` is deprecated in favor to :meth:`~yt.data_objects.derived_quantities.WeightedStandardDeviation` * :meth:`~yt.visualization.plot_window.PWViewerMPL.annotate_clear` is deprecated in favor to :meth:`~yt.visualization.plot_window.PWViewerMPL.clear_annotations` * :meth:`~yt.visualization.color_maps.add_cmap` is deprecated in favor to :meth:`~yt.visualization.color_maps.add_colormap` * :meth:`~yt.loaders.simulation` is deprecated in favor to :meth:`~yt.loaders.load_simulation` * :meth:`~yt.data_objects.index_subobjects.octree_subset.OctreeSubset.get_vertex_centered_data` now takes a list of fields as input, passing a single field is deprecated * manually updating the ``periodicity`` attributed of a :class:`~yt.data_objects.static_output.Dataset` object is deprecated. Use the :meth:`~yt.data_objects.static_output.Dataset.force_periodicity` if you need to force periodicity to ``True`` or ``False`` along all axes. * the :meth:`~yt.data_objects.static_output.Dataset.add_smoothed_particle_field` method is deprecated and already has no effect in yt 4.0 . See :ref:`sph-data` * the :meth:`~yt.data_objects.static_output.Dataset.add_gradient_fields` used to accept an ``input_field`` keyword argument, now deprecated in favor to ``fields`` * :meth:`~yt.data_objects.time_series.DatasetSeries.from_filenames` is deprecated because its functionality is now included in the basic ``__init__`` method. Use :class:`~yt.data_objects.time_series.DatasetSeries` directly. * the ``particle_type`` keyword argument from ``yt.add_field()`` (:meth:`~yt.fields.field_info_container.FieldInfoContainer.add_field`) and ``ds.add_field()`` (:meth:`~yt.data_objects.static_output.Dataset.add_field`) methods is now a deprecated in favor to the ``sampling_type`` keyword argument. * the :meth:`~yt.fields.particle_fields.add_volume_weighted_smoothed_field` is deprecated and already has no effect in yt 4.0 . See :ref:`sph-data` * the :meth:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree.locate_brick` method is deprecated in favor to, and is now an alias for :meth:`~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree.locate_node` * the :class:`~yt.utilities.exceptions.YTOutputNotIdentified` error is a deprecated alias for :class:`~yt.utilities.exceptions.YTUnidentifiedDataType` * the ``limits`` argument from :meth:`~yt.visualization.image_writer.write_projection` is deprecated in favor to ``vmin`` and ``vmax`` * :meth:`~yt.visualization.plot_container.ImagePlotContainer.set_cbar_minorticks` is a deprecated alias for :meth:`~yt.visualization.plot_container.ImagePlotContainer.set_colorbar_minorticks` * the ``axis`` argument from :meth:`yt.visualization.plot_window.SlicePlot` is a deprecated alias for the ``normal`` argument * the old configuration file ``ytrc`` is deprecated in favor of the new ``yt.toml`` format. In yt 4.0, you'll get a warning every time you import yt if you're still using the old configuration file, which will instruct you to invoke the yt command line interface to convert automatically to the new format. * the ``load_field_plugins`` parameter is deprecated from the configuration file (note that it is already not used as of yt 4.0) Cool New Things --------------- Changes for Working with SPH Data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In yt-3.0 most user-facing operations on SPH data are produced by interpolating SPH data onto a volume-filling octree mesh. Historically this was easier to implement When support for SPH data was added to yt as it allowed re-using a lot of the existing infrastructure. This had some downsides because the octree was a single, global object, the memory and CPU overhead of smoothing SPH data onto the octree can be prohibitive on particle datasets produced by large simulations. Constructing the octree during the initial indexing phase also required each particle (albeit, in a 64-bit integer) to be present in memory simultaneously for a sorting operation, which was memory prohibitive. Visualizations of slices and projections produced by yt using the default settings are somewhat blocky since by default we use a relatively coarse octree to preserve memory. In yt-4.0 this has all changed! Over the past two years, Nathan Goldbaum, Meagan Lang and Matt Turk implemented a new approach for handling I/O of particle data, based on storing compressed bitmaps containing Morton indices instead of an in-memory octree. This new capability means that the global octree index is now no longer necessary to enable I/O chunking and spatial indexing of particle data in yt. The new I/O method has opened up a new way of dealing with the particle data and in particular, SPH data. .. _sph-data: Scatter and Gather approach for SPH data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As mentioned, previously operations such as slice, projection and arbitrary grids would smooth the particle data onto the global octree. As this is no longer used, a different approach was required to visualize the SPH data. Using SPLASH as inspiration, SPH smoothing pixelization operations were created using smoothing operations via "scatter" and "gather" approaches. We estimate the contributions of a particle to a single pixel by considering the point at the centre of the pixel and using the standard SPH smoothing formula. The heavy lifting in these functions is undertaken by cython functions. It is now possible to generate slice plots, projection plots, covering grids and arbitrary grids of smoothed quantities using these operations. The following code demonstrates how this could be achieved. The following would use the scatter method: .. code-block:: python import yt ds = yt.load("snapshot_033/snap_033.0.hdf5") plot = yt.SlicePlot(ds, 2, ("gas", "density")) plot.save() plot = yt.ProjectionPlot(ds, 2, ("gas", "density")) plot.save() arbitrary_grid = ds.arbitrary_grid([0.0, 0.0, 0.0], [25, 25, 25], dims=[16, 16, 16]) ag_density = arbitrary_grid["gas", "density"] covering_grid = ds.covering_grid(4, 0, 16) cg_density = covering_grid["gas", "density"] In the above example the ``covering_grid`` and the ``arbitrary_grid`` will return the same data. In fact, these containers are very similar but provide a slightly different API. The above code can be modified to use the gather approach by changing a global setting for the dataset. This can be achieved with ``ds.sph_smoothing_style = "gather"``, so far, the gather approach is not supported for projections. The default behaviour for SPH interpolation is that the values are normalized inline with Eq. 9 in `SPLASH, Price (2009) `_. This can be disabled with ``ds.use_sph_normalization = False``. This will disable the normalization for all future interpolations. The gather approach requires finding nearest neighbors using the KDTree. The first call will generate a KDTree for the entire dataset which will be stored in a sidecar file. This will be loaded whenever necessary. Off-Axis Projection for SPH Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The current ``OffAxisProjectionPlot`` class will now support SPH projection plots. The following is a code example: .. code-block:: python import yt ds = yt.load("Data/GadgetDiskGalaxy/snapshot_200.hdf5") smoothing_field = ("gas", "density") _, center = ds.find_max(smoothing_field) sp = ds.sphere(center, (10, "kpc")) normal_vector = sp.quantities.angular_momentum_vector() prj = yt.OffAxisProjectionPlot(ds, normal_vector, smoothing_field, center, (20, "kpc")) prj.save() Smoothing Data onto an Octree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Whilst the move away from the global octree is a promising one in terms of performance and dealing with SPH data in a more intuitive manner, it does remove a useful feature. We are aware that many users will have older scripts which take advantage of the global octree. As such, we have added support to smooth SPH data onto an octree when desired by the users. The new octree is designed to give results consistent with those of the previous octree, but the new octree takes advantage of the scatter and gather machinery also added. .. code-block:: python import numpy as np import yt ds = yt.load("GadgetDiskGalaxy/snapshot_200.hdf5") left = np.array([0, 0, 0], dtype="float64") right = np.array([64000, 64000, 64000], dtype="float64") # generate an octree octree = ds.octree(left, right, n_ref=64) # Scatter deposition is the default now, and thus this will print scatter print(octree.sph_smoothing_style) # the density will be calculated using SPH scatter density = octree["PartType0", "density"] # this will return the x positions of the octs x = octree["index", "x"] The above code can be modified to use the gather approach by using ``ds.sph_smoothing_style = 'gather'`` before any field access. The octree just uses the smoothing style and number of neighbors defined by the dataset. The octree implementation is very simple. It uses a recursive algorithm to build a ``depth-first`` which is consistent with the results from yt-3. Depth-first search (DFS) means that tree starts refining at the root node (this is the largest node which contains every particles) and refines as far as possible along each branch before backtracking. .. _yt-units-is-now-unyt: ``yt.units`` Is Now a Wrapper for ``unyt`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We have extracted ``yt.units`` into ``unyt``, its own library that you can install separately from yt from ``pypi`` and ``conda-forge``. You can find out more about using ``unyt`` in `its documentation `_ and in `a paper in the Journal of Open Source Software `_. From the perspective of a user of yt, very little should change. While things in ``unyt`` have different names -- for example ``YTArray`` is now called ``unyt_array`` -- we have provided wrappers in ``yt.units`` so imports in your old scripts should continue to work without issue. If you have any old scripts that don't work due to issues with how yt is using ``unyt`` or units issues in general please let us know by `filing an issue on GitHub `_. Moving ``unyt`` into its own library has made it much easier to add some cool new features, which we detail below. ``ds.units`` ~~~~~~~~~~~~ Each dataset now has a set of unit symbols and physical constants associated with it, allowing easier customization and smoother interaction, especially in workflows that need to use code units or cosmological units. The ``ds.units`` object has a large number of attributes corresponding to the names of units and physical constants. All units known to the dataset will be available, including custom units. In situations where you might have used ``ds.arr`` or ``ds.quan`` before, you can now safely use ``ds.units``: >>> ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030') >>> u = ds.units >>> ad = ds.all_data() >>> data = ad['Enzo', 'Density'] >>> data + 12*u.code_mass/u.code_length**3 unyt_array([1.21784693e+01, 1.21789148e+01, 1.21788494e+01, ..., 4.08936836e+04, 5.78006836e+04, 3.97766906e+05], 'code_mass/code_length**3') >>> data + .0001*u.mh/u.cm**3 unyt_array([6.07964513e+01, 6.07968968e+01, 6.07968314e+01, ..., 4.09423016e+04, 5.78493016e+04, 3.97815524e+05], 'code_mass/code_length**3') Automatic Unit Simplification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Often the results of an operation will result in a unit expression that can be simplified by cancelling pairs of factors. Before yt 4.0, these pairs of factors were only cancelled if the same unit appeared in both the numerator and denominator of an expression. Now, all pairs of factors have have inverse dimensions are cancelled, and the appropriate scaling factor is incorporated into the result. For example, ``Hz`` and ``s`` will now appropriately be recognized as inverses: >>> from yt.units import Hz, s >>> frequency = 60*Hz >>> time = 60*s >>> frequency*time unyt_quantity(3600, '(dimensionless)') Similar simplifications will happen even if units aren't reciprocals of each other, for example here ``hour`` and ``minute`` automatically cancel each other: >>> from yt.units import erg, minute, hour >>> power = [20, 40, 80] * erg / minute >>> elapsed_time = 3*hour >>> print(power*elapsed_time) [ 3600. 7200. 14400.] erg Alternate Unit Name Resolution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It's now possible to use a number of common alternate spellings for unit names and if ``unyt`` knows about the alternate spelling it will automatically resolve alternate spellings to a canonical name. For example, it's now possible to do things like this: >>> import yt.units as u >>> d = 20*u.mile >>> d.to('km') unyt_quantity(32.18688, 'km') >>> d.to('kilometer') unyt_quantity(32.18688, 'km') >>> d.to('kilometre') unyt_quantity(32.18688, 'km') You can also use alternate unit names in more complex algebraic unit expressions: >>> v = d / (20*u.minute) >>> v.to('kilometre/hour') unyt_quantity(96.56064, 'km/hr') In this example the common british spelling ``"kilometre"`` is resolved to ``"km"`` and ``"hour"`` is resolved to ``"hr"``. Field-Specific Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can now set configuration values on a per-field basis. For instance, this means that if you always want a particular colormap associated with a particular field, you can do so! This is documented under :ref:`per-field-plotconfig`, and was added in `PR 1931 `_. New Method for Accessing Sample Datasets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There is now a function entitled ``load_sample()`` that allows the user to automatically load sample data from the yt hub in a local yt session. Previously, users would have to explicitly download these data directly from `https://yt-project.org/data `_, unpackage them, and load them into a yt session, but now this occurs from within a python session. For more information see: :ref:`Loading Sample Data ` Some Widgets ^^^^^^^^^^^^ In yt, we now have some simple display wrappers for objects if you are running in a Jupyter environment with the `ipywidgets `_ package installed. For instance, the ``ds.fields`` object will now display field information in an interactive widget, and three-element unyt arrays (such as ``ds.domain_left_edge``) will be displayed interactively as well. The package `widgyts `_ provides interactive, yt-specific visualization of slices, projections, and additional dataset display information. New External Packages ^^^^^^^^^^^^^^^^^^^^^ As noted above (:ref:`yt-units-is-now-unyt`), unyt has been extracted from yt, and we now use it as an external library. In addition, other parts of yt such as :ref:`interactive_data_visualization` have been extracted, and we are working toward a more modular approach for things such as Jupyter widgets and other "value-added" integrations. yt-project-yt-f043ac8/minimal_requirements.txt000066400000000000000000000026461510711153200216540ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile pyproject.toml --python=3.10 --python-platform=x86_64-unknown-linux-gnu --resolution=lowest-direct --no-build cmyt==1.1.2 # via yt (pyproject.toml) colorspacious==1.1.2 # via cmyt cycler==0.12.1 # via matplotlib ewah-bool-utils==1.2.0 # via yt (pyproject.toml) fonttools==4.53.1 # via matplotlib kiwisolver==1.4.7 # via matplotlib matplotlib==3.5.0 # via # yt (pyproject.toml) # cmyt more-itertools==8.4.0 # via # yt (pyproject.toml) # cmyt mpmath==1.3.0 # via sympy numpy==1.21.3 # via # yt (pyproject.toml) # cmyt # colorspacious # ewah-bool-utils # matplotlib # unyt packaging==20.9 # via # yt (pyproject.toml) # matplotlib # setuptools-scm pillow==8.3.2 # via # yt (pyproject.toml) # matplotlib pyparsing==3.1.4 # via # matplotlib # packaging python-dateutil==2.9.0.post0 # via matplotlib setuptools==74.1.2 # via setuptools-scm setuptools-scm==8.1.0 # via matplotlib six==1.16.0 # via python-dateutil sympy==1.13.2 # via unyt tomli==1.2.3 # via # yt (pyproject.toml) # setuptools-scm tomli-w==0.4.0 # via yt (pyproject.toml) tqdm==3.4.0 # via yt (pyproject.toml) typing-extensions==4.4.0 # via yt (pyproject.toml) unyt==2.9.2 # via yt (pyproject.toml) yt-project-yt-f043ac8/nose_answer.cfg000066400000000000000000000003721510711153200176600ustar00rootroot00000000000000[nosetests] attr=answer_test detailed-errors=1 local-dir=../answer-store local=1 nocapture=1 nologcapture=1 verbosity=2 where=yt with-answer-testing=1 with-timer=1 with-xunit=1 xunit-file=answer_nosetests.xml ignore-files=(test_raw_field_slices\.py) yt-project-yt-f043ac8/nose_ignores.txt000066400000000000000000000036261510711153200201140ustar00rootroot00000000000000--ignore-file=test_add_field\.py --ignore-file=test_ambiguous_fields\.py --ignore-file=test_callable_grids\.py --ignore-file=test_callbacks_geographic\.py --ignore-file=test_commons\.py --ignore-file=test_cython_fortran_utils\.py --ignore-file=test_data_reload\.py --ignore-file=test_eps_writer\.py --ignore-file=test_ewah_write_load\.py --ignore-file=test_external_frontends\.py --ignore-file=test_field_access_pytest\.py --ignore-file=test_file_sanitizer\.py --ignore-file=test_firefly\.py --ignore-file=test_geometries\.py --ignore-file=test_geographic_coordinates\.py --ignore-file=test_glue\.py --ignore-file=test_image_comp_2D_plots\.py --ignore-file=test_image_comp_geo\.py --ignore-file=test_invalid_origin\.py --ignore-file=test_line_annotation_unit\.py --ignore-file=test_line_plots\.py --ignore-file=test_load_archive\.py --ignore-file=test_load_errors\.py --ignore-file=test_load_sample\.py --ignore-file=test_mesh_render\.py --ignore-file=test_mesh_slices\.py --ignore-file=test_normal_plot_api\.py --ignore-file=test_on_demand_imports\.py --ignore-file=test_outputs_pytest\.py --ignore-file=test_profile_plots\.py --ignore-file=test_raw_field_slices\.py --ignore-file=test_registration\.py --ignore-file=test_sanitize_center\.py --ignore-file=test_save\.py --ignore-file=test_set_zlim\.py --ignore-file=test_stream_particles\.py --ignore-file=test_stream_stretched\.py --ignore-file=test_version\.py --ignore-file=test_gadget_pytest\.py --ignore-file=test_vr_orientation\.py --ignore-file=test_particle_trajectories_pytest\.py --ignore-file=test_image_array\.py --ignore-file=test_alt_ray_tracers\.py --ignore-file=test_minimal_representation\.py --ignore-file=test_set_log_level\.py --ignore-file=test_field_parsing\.py --ignore-file=test_disks\.py --ignore-file=test_offaxisprojection_pytestonly\.py --ignore-file=test_sph_pixelization_pytestonly\.py --ignore-file=test_time_series\.py --ignore-file=test_cf_radial_pytest\.py yt-project-yt-f043ac8/nose_requirements.txt000066400000000000000000000000531510711153200211600ustar00rootroot00000000000000nose~=1.3.7 nose-exclude nose-timer~=1.0.0 yt-project-yt-f043ac8/nose_unit.cfg000066400000000000000000000002461510711153200173400ustar00rootroot00000000000000[nosetests] attr=!answer_test detailed-errors=1 nocapture=1 nologcapture=1 verbosity=2 where=yt with-timer=1 exclude-test=yt.frontends.gdf.tests.test_outputs.TestGDF yt-project-yt-f043ac8/pyproject.toml000066400000000000000000000402061510711153200175700ustar00rootroot00000000000000[build-system] # keep in sync with .github/workflows/wheels.yaml requires = [ "setuptools>=77.0.0", "Cython>=3.0.3", "numpy>=2.0.0", "ewah-bool-utils>=1.2.0", ] build-backend = "setuptools.build_meta" [project] name = "yt" version = "4.4.2" description = "An analysis and visualization toolkit for volumetric data" authors = [ { name = "The yt project", email = "yt-dev@python.org" }, ] license = "BSD-3-Clause" license-files = [ "COPYING.txt", "yt/frontends/artio/artio_headers/LICENSE", ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Framework :: Matplotlib", "Intended Audience :: Science/Research", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: AIX", "Operating System :: POSIX :: Linux", "Programming Language :: C", "Programming Language :: C++", "Programming Language :: Cython", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering :: Astronomy", "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Visualization", ] keywords = [ "adaptivemeshrefinement", "amr", "astronomy", "astrophysics", "visualization", ] requires-python = ">=3.10.4" # keep in sync with minimal_requirements.txt dependencies = [ "cmyt>=1.1.2", "ewah-bool-utils>=1.2.0", "matplotlib>=3.5", "more-itertools>=8.4", "numpy>=1.21.3, <3", # keep minimal requirement in sync with NPY_TARGET_VERSION # https://github.com/numpy/numpy/issues/27037 "numpy!=2.0.1 ; platform_machine=='arm64' and platform_system=='Darwin'", "packaging>=20.9", "pillow>=8.3.2", "tomli-w>=0.4.0", "tqdm>=3.4.0", "unyt>=2.9.2", "tomli>=1.2.3;python_version < '3.11'", "typing-extensions>=4.4.0;python_version < '3.12'", ] [project.readme] file = "README.md" content-type = "text/markdown" [project.urls] Homepage = "https://yt-project.org/" Documentation = "https://yt-project.org/doc/" Source = "https://github.com/yt-project/yt/" Tracker = "https://github.com/yt-project/yt/issues" [project.entry-points."nose.plugins.0.10"] # this section can be cleaned when nose tests on GHA are removed answer-testing = "yt.utilities.answer_testing.framework:AnswerTesting" [project.optional-dependencies] # some generic, reusable constraints on optional-deps HDF5 = [ "h5py>=3.1.0", "h5py!=3.12.0 ; platform_system=='Windows'", # https://github.com/h5py/h5py/issues/2505 "h5py!=3.15.0 ; platform_system=='Darwin'", # https://github.com/yt-project/yt/issues/5301 ] netCDF4 = ["netCDF4!=1.6.1,>=1.5.3"] # see https://github.com/Unidata/netcdf4-python/issues/1192 Fortran = ["f90nml>=1.1"] # frontend-specific requirements # all frontends should have a target, even if no additional requirements are needed # note that, because pip normalizes underscores to hyphens, we need to apply this transformation # in target names here so that recursive dependencies link correctly. # This does *not* prevent end-users to write, say `pip install yt[enzo_e]`. # We also normalize all target names to lower case for consistency. adaptahop = [] ahf = [] amrex = [] amrvac = ["yt[Fortran]"] art = [] arepo = ["yt[HDF5]"] artio = [] athena = [] athena-pp = [] boxlib = [] cf-radial = ["xarray>=0.16.1", "arm-pyart>=1.19.2",] chimera = ["yt[HDF5]"] chombo = ["yt[HDF5]"] cholla = ["yt[HDF5]"] eagle = ["yt[HDF5]"] enzo-e = ["yt[HDF5]", "libconf>=1.0.1"] enzo = ["yt[HDF5]", "libconf>=1.0.1"] exodus-ii = ["yt[netCDF4]"] fits = ["astropy>=4.0.1", "regions>=0.7"] flash = ["yt[HDF5]"] gadget = ["yt[HDF5]"] gadget-fof = ["yt[HDF5]"] gamer = ["yt[HDF5]"] gdf = ["yt[HDF5]"] gizmo = ["yt[HDF5]"] halo-catalog = ["yt[HDF5]"] http-stream = ["requests>=2.20.0"] idefix = ["yt_idefix[HDF5]>=2.3.0"] # externally packaged frontend moab = ["yt[HDF5]"] nc4-cm1 = ["yt[netCDF4]"] open-pmd = ["yt[HDF5]"] owls = ["yt[HDF5]"] owls-subfind = ["yt[HDF5]"] parthenon = ["yt[HDF5]"] ramses = ["yt[Fortran]", "scipy"] rockstar = [] sdf = ["requests>=2.20.0"] stream = [] swift = ["yt[HDF5]"] tipsy = [] ytdata = ["yt[HDF5]"] # "full" should contain all optional dependencies intended for users (not devs) # in particular it should enable support for all frontends full = [ "cartopy>=0.22.0", "firefly>=3.2.0", "glueviz>=0.13.3", "ipython>=7.16.2", "ipywidgets>=8.0.0", "miniballcpp>=0.2.1", "mpi4py>=3.0.3", "pandas>=1.1.2", "pooch>=0.7.0", "pyaml>=17.10.0", "pykdtree>=1.3.1", "pyx>=0.15", "scipy>=1.5.0", "glue-core!=1.2.4;python_version >= '3.10'", # see https://github.com/glue-viz/glue/issues/2263 "ratarmount~=0.8.1;platform_system!='Windows' and platform_system!='Darwin'", "yt[adaptahop]", "yt[ahf]", "yt[amrex]", "yt[amrvac]", "yt[art]", "yt[arepo]", "yt[artio]", "yt[athena]", "yt[athena_pp]", "yt[boxlib]", "yt[cf_radial]", "yt[chimera]", "yt[chombo]", "yt[cholla]", "yt[eagle]", "yt[enzo_e]", "yt[enzo]", "yt[exodus_ii]", "yt[fits]", "yt[flash]", "yt[gadget]", "yt[gadget_fof]", "yt[gamer]", "yt[gdf]", "yt[gizmo]", "yt[halo_catalog]", "yt[http_stream]", "yt[idefix]", "yt[moab]", "yt[nc4_cm1]", "yt[open_pmd]", "yt[owls]", "yt[owls_subfind]", "yt[parthenon]", "yt[ramses]", "yt[rockstar]", "yt[sdf]", "yt[stream]", "yt[swift]", "yt[tipsy]", "yt[ytdata]", ] # dev-only extra targets mapserver = [ "bottle", ] test = [ "pyaml>=17.10.0", "pytest>=6.1", "pytest-mpl>=0.16.1", "sympy!=1.10,!=1.9", # see https://github.com/sympy/sympy/issues/22241 "imageio!=2.35.0", # see https://github.com/yt-project/yt/issues/4966 "contourpy", ] [project.scripts] yt = "yt.utilities.command_line:run_main" [tool.setuptools] include-package-data = true zip-safe = false [tool.setuptools.packages.find] namespaces = false [tool.black] # TODO: drop this section when ruff supports embedded python blocks # see https://github.com/astral-sh/ruff/issues/8237 include = '\.pyi?$' exclude = ''' /( \.eggs | \.git | \.hg | \.mypy_cache | \.tox | \.venv | _build | buck-out | build | dist | yt/frontends/stream/sample_data )/ | yt/visualization/_colormap_data.py ''' [tool.ruff] exclude = [ "doc", "benchmarks", "*/api.py", "*/__init__.py", "*/__config__.py", "yt/units", "yt/frontends/stream/sample_data", "yt/utilities/fits_image.py", "yt/utilities/lodgeit.py", "yt/mods.py", "yt/visualization/_colormap_data.py", "yt/exthook.py", ] [tool.ruff.lint] select = [ "E", "F", "W", "C4", # flake8-comprehensions "B", # flake8-bugbear "G", # flake8-logging-format "TCH", # flake8-type-checking "YTT", # flake8-2020 "UP", # pyupgrade "I", # isort "NPY", # numpy specific rules "RUF031"# incorrectly-parenthesized-tuple-in-subscript ] ignore = [ "E501", # line too long "E741", # Do not use variables named 'I', 'O', or 'l' "B018", # Found useless expression. # disabled because ds.index is idiomatic "UP038", # non-pep604-isinstance ] [tool.ruff.lint.per-file-ignores] "test_*" = ["NPY002"] [tool.ruff.lint.isort] combine-as-imports = true known-third-party = [ "IPython", "nose", "numpy", "sympy", "matplotlib", "unyt", "git", "yaml", "dateutil", "requests", "coverage", "pytest", "pyx", "glue", ] known-first-party = ["yt"] # The -s option prevents pytest from capturing output sent to stdout # -v runs pytest in verbose mode # -rsfE: The -r tells pytest to provide extra test summary info on the events # specified by the characters following the r. s: skipped, f: failed, E: error [tool.pytest.ini_options] addopts = ''' -s -v -rsfE --ignore-glob='/*_nose.py' --ignore-glob='/*/yt/data_objects/level_sets/tests/test_clump_finding.py' --ignore-glob='/*/yt/data_objects/tests/test_connected_sets.py' --ignore-glob='/*/yt/data_objects/tests/test_data_containers.py' --ignore-glob='/*/yt/data_objects/tests/test_dataset_access.py' --ignore-glob='/*/yt/data_objects/tests/test_particle_filter.py' --ignore-glob='/*/yt/data_objects/tests/test_particle_trajectories.py' --ignore-glob='/*/yt/data_objects/tests/test_pickling.py' --ignore-glob='/*/yt/data_objects/tests/test_regions.py' --ignore-glob='/*/yt/fields/tests/test_particle_fields.py' --ignore-glob='/*/yt/fields/tests/test_vector_fields.py' --ignore-glob='/*/yt/fields/tests/test_xray_fields.py' --ignore-glob='/*/yt/frontends/adaptahop/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/ahf/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/amrex/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/amrvac/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/amrvac/tests/test_units_override.py' --ignore-glob='/*/yt/frontends/arepo/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/art/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/artio/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/athena/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/athena_pp/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/boxlib/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/cf_radial/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/chimera/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/cholla/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/chombo/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/eagle/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/enzo/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/enzo_e/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/exodus_ii/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/fits/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/flash/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/gadget/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/gadget_fof/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/gamer/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/gdf/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/gdf/tests/test_outputs_nose.py' --ignore-glob='/*/yt/frontends/gizmo/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/halo_catalog/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/moab/tests/test_c5.py' --ignore-glob='/*/yt/frontends/nc4_cm1/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/open_pmd/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/owls/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/owls_subfind/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/parthenon/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/ramses/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/rockstar/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/tipsy/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/ytdata/tests/test_old_outputs.py' --ignore-glob='/*/yt/frontends/ytdata/tests/test_outputs.py' --ignore-glob='/*/yt/frontends/ytdata/tests/test_unit.py' --ignore-glob='/*/yt/geometry/coordinates/tests/test_axial_pixelization.py' --ignore-glob='/*/yt/geometry/coordinates/tests/test_cylindrical_coordinates.py' --ignore-glob='/*/yt/geometry/coordinates/tests/test_spherical_coordinates.py' --ignore-glob='/*/yt/tests/test_funcs.py' --ignore-glob='/*/yt/utilities/lib/cykdtree/tests/__init__.py' --ignore-glob='/*/yt/utilities/lib/cykdtree/tests/test_kdtree.py' --ignore-glob='/*/yt/utilities/lib/cykdtree/tests/test_plot.py' --ignore-glob='/*/yt/utilities/lib/cykdtree/tests/test_utils.py' --ignore-glob='/*/yt/utilities/tests/test_cosmology.py' --ignore-glob='/*/yt/visualization/tests/test_callbacks.py' --ignore-glob='/*/yt/visualization/tests/test_color_maps.py' --ignore-glob='/*/yt/visualization/tests/test_geo_projections.py' --ignore-glob='/*/yt/visualization/tests/test_image_writer.py' --ignore-glob='/*/yt/visualization/tests/test_line_plots.py' --ignore-glob='/*/yt/visualization/tests/test_mesh_slices.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_custom_norm.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_inf_zlim.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_lineplot.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_particleplot.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_phaseplot_set_colorbar_explicit.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_phaseplot_set_colorbar_implicit.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_profileplot.py' --ignore-glob='/*/yt/visualization/tests/test_norm_api_set_background_color.py' --ignore-glob='/*/yt/visualization/tests/test_particle_plot.py' --ignore-glob='/*/yt/visualization/tests/test_plot_modifications.py' --ignore-glob='/*/yt/visualization/tests/test_plotwindow.py' --ignore-glob='/*/yt/visualization/tests/test_raw_field_slices.py' --ignore-glob='/*/yt/visualization/volume_rendering/tests/test_mesh_render.py' --ignore-glob='/*/yt/visualization/volume_rendering/tests/test_vr_orientation.py' ''' [tool.check-manifest] # ignore generated C/C++ files, otherwise reported as "missing from VCS" (Version Control System) # Please resist the temptation to use patterns instead of exact file names here. ignore = [ "yt/frontends/artio/_artio_caller.c", "yt/frontends/gamer/cfields.c", "yt/frontends/ramses/io_utils.c", "yt/geometry/fake_octree.c", "yt/geometry/grid_container.c", "yt/geometry/grid_visitors.c", "yt/geometry/oct_container.c", "yt/geometry/oct_visitors.c", "yt/geometry/particle_deposit.c", "yt/geometry/particle_oct_container.cpp", "yt/geometry/particle_smooth.c", "yt/geometry/selection_routines.c", "yt/utilities/cython_fortran_utils.c", "yt/utilities/lib/_octree_raytracing.cpp", "yt/utilities/lib/allocation_container.c", "yt/utilities/lib/alt_ray_tracers.c", "yt/utilities/lib/amr_kdtools.c", "yt/utilities/lib/autogenerated_element_samplers.c", "yt/utilities/lib/basic_octree.c", "yt/utilities/lib/bitarray.c", "yt/utilities/lib/bounded_priority_queue.c", "yt/utilities/lib/bounding_volume_hierarchy.cpp", "yt/utilities/lib/contour_finding.c", "yt/utilities/lib/cosmology_time.c", "yt/utilities/lib/cykdtree/kdtree.cpp", "yt/utilities/lib/cykdtree/utils.cpp", "yt/utilities/lib/cyoctree.c", "yt/utilities/lib/cyoctree.cpp", "yt/utilities/lib/depth_first_octree.c", "yt/utilities/lib/distance_queue.c", "yt/utilities/lib/element_mappings.c", "yt/utilities/lib/ewah_bool_wrap.cpp", "yt/utilities/lib/fnv_hash.c", "yt/utilities/lib/fortran_reader.c", "yt/utilities/lib/geometry_utils.cpp", "yt/utilities/lib/grid_traversal.cpp", "yt/utilities/lib/image_samplers.cpp", "yt/utilities/lib/image_utilities.c", "yt/utilities/lib/interpolators.c", "yt/utilities/lib/lenses.c", "yt/utilities/lib/line_integral_convolution.c", "yt/utilities/lib/marching_cubes.cpp", "yt/utilities/lib/mesh_triangulation.c", "yt/utilities/lib/mesh_utilities.c", "yt/utilities/lib/misc_utilities.cpp", "yt/utilities/lib/origami.c", "yt/utilities/lib/particle_kdtree_tools.cpp", "yt/utilities/lib/particle_mesh_operations.c", "yt/utilities/lib/partitioned_grid.cpp", "yt/utilities/lib/pixelization_routines.cpp", "yt/utilities/lib/points_in_volume.c", "yt/utilities/lib/primitives.c", "yt/utilities/lib/quad_tree.c", "yt/utilities/lib/ragged_arrays.c", "yt/utilities/lib/write_array.c", ] [tool.mypy] python_version = '3.10' show_error_codes = true ignore_missing_imports = true warn_unused_configs = true warn_unused_ignores = true warn_unreachable = true show_error_context = true exclude = "(test_*|lodgeit)" [tool.cibuildwheel] build-verbosity = 1 skip = ["cp314t-*"] test-extras = "test" test-command = [ 'python -c "import yt"', "python -m pytest -c {project}/pyproject.toml --rootdir . --color=yes --pyargs yt -ra", ] environment = {"YT_LIMITED_API" = "1"} yt-project-yt-f043ac8/requirements/000077500000000000000000000000001510711153200173755ustar00rootroot00000000000000yt-project-yt-f043ac8/requirements/docs.txt000066400000000000000000000004471510711153200210730ustar00rootroot00000000000000alabaster>=0.7.13 bottle>=0.12.25 ipykernel>=6.29.4 jinja2<3.2.0 # see https://github.com/readthedocs/readthedocs.org/issues/9037 jupyter-client>=8.3.1 nbsphinx>=0.9.3 nose~=1.3.7; python_version < '3.10' pytest>=6.1 pyx>=0.15 sphinx>=7.2.5 sphinx-bootstrap-theme>=0.8.1 sphinx-rtd-theme>=1.3.0 yt-project-yt-f043ac8/requirements/typecheck.txt000066400000000000000000000002251510711153200221140ustar00rootroot00000000000000mypy==1.13.0 types-PyYAML==6.0.12.20240917 types-chardet==5.0.4.6 types-requests==2.32.0.20250328 typing-extensions==4.13.2; python_version < '3.12' yt-project-yt-f043ac8/setup.py000066400000000000000000000075111510711153200163700ustar00rootroot00000000000000import glob import os import sys from collections import defaultdict from distutils.ccompiler import get_default_compiler from importlib import resources as importlib_resources from setuptools import Distribution, setup # ensure enclosing directory is in PYTHON_PATH to allow importing from setupext.py if (script_dir := os.path.dirname(__file__)) not in sys.path: sys.path.insert(0, script_dir) from setupext import ( NUMPY_MACROS, check_CPP14_flags, check_for_openmp, check_for_pyembree, create_build_ext, get_python_include_dirs, get_setup_options, install_ccompiler, ) install_ccompiler() if os.path.exists("MANIFEST"): os.remove("MANIFEST") with open("README.md") as file: long_description = file.read() CPP14_CONFIG = defaultdict( lambda: check_CPP14_flags(["-std=c++14", "-std=c++1y", "-std=gnu++0x"]), {"msvc": ["/std:c++14"]}, ) CPP11_CONFIG = defaultdict(lambda: ["-std=c++11"], {"msvc": ["/std:c++11"]}) _COMPILER = get_default_compiler() omp_args, _ = check_for_openmp() if os.name == "nt": std_libs = [] else: std_libs = ["m"] CPP14_FLAG = CPP14_CONFIG[_COMPILER] CPP11_FLAG = CPP11_CONFIG[_COMPILER] FIXED_INTERP = "fixed_interpolator" cythonize_aliases = { "LIB_DIR": "yt/utilities/lib/", "LIB_DIR_GEOM": ["yt/utilities/lib/", "yt/geometry/"], "LIB_DIR_GEOM_ARTIO": [ "yt/utilities/lib/", "yt/geometry/", "yt/frontends/artio/artio_headers/", ], "STD_LIBS": std_libs, "EWAH_LIBS": std_libs + [os.path.abspath(importlib_resources.files("ewah_bool_utils"))], "OMP_ARGS": omp_args, "FIXED_INTERP": FIXED_INTERP, "ARTIO_SOURCE": sorted(glob.glob("yt/frontends/artio/artio_headers/*.c")), "CPP14_FLAG": CPP14_FLAG, "CPP11_FLAG": CPP11_FLAG, } lib_exts = [ "yt/geometry/*.pyx", "yt/utilities/cython_fortran_utils.pyx", "yt/frontends/ramses/io_utils.pyx", "yt/frontends/gamer/cfields.pyx", "yt/utilities/lib/cykdtree/kdtree.pyx", "yt/utilities/lib/cykdtree/utils.pyx", "yt/frontends/artio/_artio_caller.pyx", "yt/utilities/lib/*.pyx", ] embree_libs, embree_aliases = check_for_pyembree(std_libs) cythonize_aliases.update(embree_aliases) lib_exts += embree_libs # This overrides using lib_exts, so it has to happen after lib_exts is fully defined build_ext, sdist = create_build_ext(lib_exts, cythonize_aliases) # Force setuptools to consider that there are ext modules, even if empty. # See https://github.com/yt-project/yt/issues/2922 and # https://stackoverflow.com/a/62668026/2601223 for the fix. class BinaryDistribution(Distribution): """Distribution which always forces a binary package with platform name.""" def has_ext_modules(self): return True if __name__ == "__main__": # Avoid a race condition on fixed_interpolator.o during parallel builds by # building it only once and storing it in a static library. # See https://github.com/yt-project/yt/issues/4278 and # https://github.com/pypa/setuptools/issues/3119#issuecomment-2076922303 # for the inspiration for this fix. # build_clib doesn't add the Python include dirs (for Python.h) by default, # as opposed to build_ext, so we need to add them manually. clib_include_dirs = get_python_include_dirs() # fixed_interpolator.cpp uses Numpy types import numpy clib_include_dirs.append(numpy.get_include()) fixed_interp_lib = ( FIXED_INTERP, { "sources": ["yt/utilities/lib/fixed_interpolator.cpp"], "include_dirs": clib_include_dirs, "define_macros": NUMPY_MACROS, }, ) setup( cmdclass={"sdist": sdist, "build_ext": build_ext}, distclass=BinaryDistribution, libraries=[fixed_interp_lib], ext_modules=[], # !!! We override this inside build_ext above options=get_setup_options(), ) yt-project-yt-f043ac8/setupext.py000066400000000000000000000364341510711153200171170ustar00rootroot00000000000000import contextlib import glob import logging import os import platform import shutil import subprocess import sys import tempfile from textwrap import dedent from concurrent.futures import ThreadPoolExecutor from distutils import sysconfig from distutils.ccompiler import CCompiler, new_compiler from distutils.sysconfig import customize_compiler from subprocess import PIPE, Popen from sys import platform as _platform import ewah_bool_utils from setuptools.command.build_ext import build_ext as _build_ext from setuptools.command.sdist import sdist as _sdist from setuptools.errors import CompileError, LinkError import importlib.resources as importlib_resources log = logging.getLogger("setupext") USE_PY_LIMITED_API = ( os.getenv('YT_LIMITED_API', '0') == '1' and sys.version_info >= (3, 11) and not sysconfig.get_config_var("Py_GIL_DISABLED") ) ABI3_TARGET_VERSION = "".join(str(_) for _ in sys.version_info[:2]) ABI3_TARGET_HEX = hex(sys.hexversion & 0xFFFF00F0) @contextlib.contextmanager def stdchannel_redirected(stdchannel, dest_filename): """ A context manager to temporarily redirect stdout or stderr e.g.: with stdchannel_redirected(sys.stderr, os.devnull): if compiler.has_function('clock_gettime', libraries=['rt']): libraries.append('rt') Code adapted from https://stackoverflow.com/a/17752455/1382869 """ try: oldstdchannel = os.dup(stdchannel.fileno()) dest_file = open(dest_filename, "w") os.dup2(dest_file.fileno(), stdchannel.fileno()) yield finally: if oldstdchannel is not None: os.dup2(oldstdchannel, stdchannel.fileno()) if dest_file is not None: dest_file.close() def check_for_openmp(): """Returns OpenMP compiler and linker flags if local setup supports OpenMP or [], [] otherwise Code adapted from astropy_helpers, originally written by Tom Robitaille and Curtis McCully. """ # Create a temporary directory ccompiler = new_compiler() customize_compiler(ccompiler) tmp_dir = tempfile.mkdtemp() start_dir = os.path.abspath(".") CCODE = dedent("""\ #include #include int main() { omp_set_num_threads(2); #pragma omp parallel printf("nthreads=%d\\n", omp_get_num_threads()); return 0; }""" ) # TODO: test more known compilers: # MinGW, AppleClang with libomp, MSVC, ICC, XL, PGI, ... if os.name == "nt": # TODO: make this work with mingw # AFAICS there's no easy way to get the compiler distutils # will be using until compilation actually happens compile_flags = ["-openmp"] link_flags = [""] else: compile_flags = ["-fopenmp"] link_flags = ["-fopenmp"] try: os.chdir(tmp_dir) with open("test_openmp.c", "w") as f: f.write(CCODE) os.mkdir("objects") # Compile, link, and run test program with stdchannel_redirected(sys.stderr, os.devnull): ccompiler.compile( ["test_openmp.c"], output_dir="objects", extra_postargs=compile_flags ) ccompiler.link_executable( glob.glob(os.path.join("objects", "*")), "test_openmp", extra_postargs=link_flags, ) output = ( subprocess.check_output("./test_openmp") .decode(sys.stdout.encoding or "utf-8") .splitlines() ) if "nthreads=" in output[0]: nthreads = int(output[0].strip().split("=")[1]) if len(output) == nthreads: using_openmp = True else: log.warning( "Unexpected number of lines from output of test " "OpenMP program (output was %s)", output, ) using_openmp = False else: log.warning( "Unexpected output from test OpenMP program (output was %s)", output ) using_openmp = False except (CompileError, LinkError): using_openmp = False finally: os.chdir(start_dir) if using_openmp: log.warning("Using OpenMP to compile parallel extensions") else: log.warning( "Unable to compile OpenMP test program so Cython\n" "extensions will be compiled without parallel support" ) if using_openmp: return compile_flags, link_flags else: return [], [] def check_CPP14_flag(compile_flags): # Create a temporary directory ccompiler = new_compiler() customize_compiler(ccompiler) tmp_dir = tempfile.mkdtemp() start_dir = os.path.abspath(".") # Note: This code requires C++14 functionalities (also required to compile yt) # It compiles on gcc 4.7.4 (together with the entirety of yt) with the flag "-std=gnu++0x". # It does not compile on gcc 4.6.4 (neither does yt). CPPCODE = dedent("""\ #include struct node { std::vector vic; bool visited = false; }; int main() { return 0; }""" ) os.chdir(tmp_dir) try: with open("test_cpp14.cpp", "w") as f: f.write(CPPCODE) os.mkdir("objects") # Compile, link, and run test program with stdchannel_redirected(sys.stderr, os.devnull): ccompiler.compile( ["test_cpp14.cpp"], output_dir="objects", extra_postargs=compile_flags ) return True except CompileError: return False finally: os.chdir(start_dir) def check_CPP14_flags(possible_compile_flags): for flags in possible_compile_flags: if check_CPP14_flag([flags]): return flags log.warning( "Your compiler seems to be too old to support C++14. " "yt may not be able to compile. Please use a newer version." ) return [] def check_for_pyembree(std_libs): embree_libs = [] embree_aliases = {} try: importlib_resources.files("pyembree") except ImportError: return embree_libs, embree_aliases embree_prefix = os.path.abspath(read_embree_location()) embree_inc_dir = os.path.join(embree_prefix, "include") embree_lib_dir = os.path.join(embree_prefix, "lib") if _platform == "darwin": embree_lib_name = "embree.2" else: embree_lib_name = "embree" embree_aliases["EMBREE_INC_DIR"] = ["yt/utilities/lib/", embree_inc_dir] embree_aliases["EMBREE_LIB_DIR"] = [embree_lib_dir] embree_aliases["EMBREE_LIBS"] = std_libs + [embree_lib_name] embree_libs += ["yt/utilities/lib/embree_mesh/*.pyx"] if in_conda_env(): conda_basedir = os.path.dirname(os.path.dirname(sys.executable)) embree_aliases["EMBREE_INC_DIR"].append(os.path.join(conda_basedir, "include")) embree_aliases["EMBREE_LIB_DIR"].append(os.path.join(conda_basedir, "lib")) return embree_libs, embree_aliases def in_conda_env(): return any(s in sys.version for s in ("Anaconda", "Continuum", "conda-forge")) def read_embree_location(): """ Attempts to locate the embree installation. First, we check for an EMBREE_DIR environment variable. If one is not defined, we look for an embree.cfg file in the root yt source directory. Finally, if that is not present, we default to /usr/local. If embree is installed in a non-standard location and none of the above are set, the compile will not succeed. This only gets called if check_for_pyembree() returns something other than None. """ rd = os.environ.get("EMBREE_DIR") if rd is None: try: rd = open("embree.cfg").read().strip() except IOError: rd = "/usr/local" fail_msg = ( "I attempted to find Embree headers in %s. \n" "If this is not correct, please set your correct embree location \n" "using EMBREE_DIR environment variable or your embree.cfg file. \n" "Please see http://yt-project.org/docs/dev/visualizing/unstructured_mesh_rendering.html " "for more information. \n" % rd ) # Create a temporary directory tmpdir = tempfile.mkdtemp() curdir = os.getcwd() try: os.chdir(tmpdir) # Get compiler invocation compiler = os.getenv("CXX", "c++") compiler = compiler.split(" ") # Attempt to compile a test script. filename = r"test.cpp" file = open(filename, "wt", 1) CCODE = dedent("""\ #include "embree2/rtcore.h int main() { return 0; }""" ) file.write(CCODE) file.flush() p = Popen( compiler + ["-I%s/include/" % rd, filename], stdin=PIPE, stdout=PIPE, stderr=PIPE, ) output, err = p.communicate() exit_code = p.returncode if exit_code != 0: log.warning( "Pyembree is installed, but I could not compile Embree test code." ) log.warning("The error message was: ") log.warning(err) log.warning(fail_msg) # Clean up file.close() except OSError: log.warning( "read_embree_location() could not find your C compiler. " "Attempted to use '%s'.", compiler, ) return False finally: os.chdir(curdir) shutil.rmtree(tmpdir) return rd def get_cpu_count(): if platform.system() == "Windows": return 0 cpu_count = os.cpu_count() try: user_max_cores = int(os.getenv("MAX_BUILD_CORES", cpu_count)) except ValueError as e: raise ValueError( "MAX_BUILD_CORES must be set to an integer. " + "See above for original error." ) from e max_cores = min(cpu_count, user_max_cores) return max_cores def install_ccompiler(): def _compile( self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None, ): """Function to monkey-patch distutils.ccompiler.CCompiler""" macros, objects, extra_postargs, pp_opts, build = self._setup_compile( output_dir, macros, include_dirs, sources, depends, extra_postargs ) cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) for obj in objects: try: src, ext = build[obj] except KeyError: continue self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) # Return *all* object filenames, not just the ones we just built. return objects CCompiler.compile = _compile def get_python_include_dirs(): """Extracted from distutils.command.build_ext.build_ext.finalize_options(), https://github.com/python/cpython/blob/812245ecce2d8344c3748228047bab456816180a/Lib/distutils/command/build_ext.py#L148-L167 """ include_dirs = [] # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_python_inc() plat_py_include = sysconfig.get_python_inc(plat_specific=1) # If in a virtualenv, add its include directory # Issue 16116 if sys.exec_prefix != sys.base_exec_prefix: include_dirs.append(os.path.join(sys.exec_prefix, 'include')) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. include_dirs.extend(py_include.split(os.path.pathsep)) if plat_py_include != py_include: include_dirs.extend(plat_py_include.split(os.path.pathsep)) return include_dirs NUMPY_MACROS = [ ("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"), # keep in sync with runtime requirements (pyproject.toml) ("NPY_TARGET_VERSION", "NPY_1_21_API_VERSION"), ] def create_build_ext(lib_exts, cythonize_aliases): class build_ext(_build_ext): # subclass setuptools extension builder to avoid importing cython and numpy # at top level in setup.py. See http://stackoverflow.com/a/21621689/1382869 # NOTE: this is likely not necessary anymore since # pyproject.toml was introduced in the project def finalize_options(self): from Cython.Build import cythonize # Override the list of extension modules self.distribution.ext_modules[:] = cythonize( lib_exts, aliases=cythonize_aliases, compiler_directives={"language_level": 3}, nthreads=get_cpu_count(), ) _build_ext.finalize_options(self) # Prevent numpy from thinking it is still in its setup process # see http://stackoverflow.com/a/21621493/1382869 if isinstance(__builtins__, dict): # sometimes this is a dict so we need to check for that # https://docs.python.org/3/library/builtins.html __builtins__["__NUMPY_SETUP__"] = False else: __builtins__.__NUMPY_SETUP__ = False import numpy self.include_dirs.append(numpy.get_include()) self.include_dirs.append(ewah_bool_utils.get_include()) define_macros = NUMPY_MACROS if USE_PY_LIMITED_API: define_macros.append(("Py_LIMITED_API", ABI3_TARGET_HEX)) for ext in self.extensions: ext.py_limited_api = True if self.define is None: self.define = define_macros else: self.define.extend(define_macros) def build_extensions(self): self.check_extensions_list(self.extensions) ncpus = get_cpu_count() if ncpus > 0: with ThreadPoolExecutor(ncpus) as executor: results = { executor.submit(self.build_extension, extension): extension for extension in self.extensions } for result in results: result.result() else: super().build_extensions() def build_extension(self, extension): try: super().build_extension(extension) except CompileError as exc: print(f"While building '{extension.name}' following error was raised:\n {exc}") raise class sdist(_sdist): # subclass setuptools source distribution builder to ensure cython # generated C files are included in source distribution. # See http://stackoverflow.com/a/18418524/1382869 def run(self): # Make sure the compiled Cython files in the distribution are up-to-date from Cython.Build import cythonize cythonize( lib_exts, aliases=cythonize_aliases, compiler_directives={"language_level": 3}, nthreads=get_cpu_count(), ) _sdist.run(self) return build_ext, sdist def get_setup_options(): if USE_PY_LIMITED_API: return {"bdist_wheel": {"py_limited_api": f"cp{ABI3_TARGET_VERSION}"}} else: return {} yt-project-yt-f043ac8/tests/000077500000000000000000000000001510711153200160145ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/DD0000/000077500000000000000000000000001510711153200166035ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/DD0000/moving7_0000000066400000000000000000000153061510711153200205600ustar00rootroot00000000000000CurrentTimeIdentifier = 1188160080 MetaDataString = new_output CompilerPrecision = r8 InitialCycleNumber = 0 InitialTime = 0.81651319185139 InitialCPUTime = 0 StopTime = 20.097276379555 StopCycle = 10000 StopCPUTime = 360000 TimeLastRestartDump = 0 dtRestartDump = 18000 TimeLastDataDump = 0.81651309185139 dtDataDump = 0.01 dtDynamicalFrac = 0.000000 TimeLastHistoryDump = 0.81651319185139 dtHistoryDump = 0 TimeLastMovieDump = 0.81651319185139 dtMovieDump = 0 TracerParticleOn = 0 TimeLastTracerParticleDump = 0.81651319185139 dtTracerParticleDump = 0 MovieRegionLeftEdge = 0 0 0 MovieRegionRightEdge = 1 1 1 NewMovieLeftEdge = 0 0 0 NewMovieRightEdge = 1 1 1 MovieSkipTimestep = -99999 Movie2DTextures = 0 Movie3DVolumes = 0 NewMovieParticleOn = 0 MovieDataField = -99999 -99999 -99999 -99999 -99999 -99999 NewMovieDumpNumber = 0 NewMovieName = MoviePack CycleLastRestartDump = 0 CycleSkipRestartDump = 0 CycleLastDataDump = 0 CycleSkipDataDump = 0 CycleLastHistoryDump = 0 CycleSkipHistoryDump = 0 OutputFirstTimeAtLevel = 0 StopFirstTimeAtLevel = 0 RestartDumpNumber = 0 DataDumpNumber = 1 HistoryDumpNumber = 0 MovieDumpNumber = 0 TracerParticleDumpNumber = 0 RestartDumpName = restart DataDumpName = moving7_ HistoryDumpName = history MovieDumpName = MovieOutput TracerParticleDumpName = TracerOutput RedshiftDumpName = RedshiftOutput StaticHierarchy = 0 TopGridRank = 3 TopGridDimensions = 16 16 16 TopGridGravityBoundary = 0 ParticleBoundaryType = 3 NumberOfParticles = 0 (do not modify) CourantSafetyNumber = 0.5 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 ProblemType = 27 HydroMethod = 2 RateSolver = 0 huge_number = 1.000000e+20 tiny_number = 1.000000e-20 Gamma = 1.6667 PressureFree = 0 RefineBy = 2 MaximumRefinementLevel = 9 MaximumGravityRefinementLevel = 9 MaximumParticleRefinementLevel = -1 CellFlaggingMethod = 2 -99999 -99999 -99999 -99999 CellFlaggingSlopeFields = -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 CellFlaggingMagnitudeFields = -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 MinimumMagnitudeForRefinement = -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 -99999 FluxCorrection = 1 InterpolationMethod = 3 ConservativeInterpolation = 0 MinimumEfficiency = 0.3 MaximumSubgridSize = 10000 NumberOfBufferZones = 1 DomainLeftEdge = 0 0 0 DomainRightEdge = 1 1 1 GridVelocity = 0 0 0 RefineRegionLeftEdge = 0.2 0.2 0.2 RefineRegionRightEdge = 0.9 0.9 0.9 RefineRegionTimeType = 1 DataLabel[0] = Density #DataCGSConversionFactor[0] = 6.2557e-27 g/cm^3 DataLabel[1] = TotalEnergy DataLabel[2] = x-velocity #DataCGSConversionFactor[2] = 4.06287e+07 cm/s DataLabel[3] = y-velocity #DataCGSConversionFactor[3] = 4.06287e+07 cm/s DataLabel[4] = z-velocity #DataCGSConversionFactor[4] = 4.06287e+07 cm/s #CGSConversionFactorTime = 1.38147e+16 #CGSConversionFactorTemp = 2.068e+07 UniformGravity = 0 UniformGravityDirection = 0 UniformGravityConstant = 1 PointSourceGravity = 0 PointSourceGravityPosition = 0.5 0.5 0.5 PointSourceGravityConstant = 0.02 PointSourceGravityCoreRadius = 0 SelfGravity = 1 GravitationalConstant = 1.000000e+00 S2ParticleSize = 3 GravityResolution = 1 ComputePotential = 0 PotentialIterations = 1 BaryonSelfGravityApproximation = 0 GreensFunctionMaxNumber = 10 GreensFunctionMaxSize = 1 DualEnergyFormalism = 0 DualEnergyFormalismEta1 = 1.000000e-03 DualEnergyFormalismEta2 = 1.000000e-01 ParticleCourantSafetyNumber = 0.5 RadiativeCooling = 1 MultiSpecies = 0 CIECooling = 0 H2OpticalDepthApproximation = 0 MagneticField = 0 MagneticDebug = 0 MagneticFieldMode = 3 MagneticFieldAdd = 0 MagneticFieldUnits = 0 MagneticSmallNumber = 0.000001 TracerMagneticField = 0 MagneticFieldMethod = 0 MagneticBiermannSave = 0 MagneticLagrangeRho = 0 MagneticUpwind = 0 MagneticLimiter = 0 UniformBField = 0.000000 0.000000 0.000000 UniformAVector = 0.000000 0.000000 0.000000 GradientAVector = 0 0.000000 0.000000 0.000000 RadiationFieldType = 0 RadiationFieldLevelRecompute = 0 RadiationSpectrumNormalization = 1e-21 RadiationSpectrumSlope = 1.5 SterileNeutrinoMass = 0 SterileNeutrinoMixingAngle = 0 ZEUSLinearArtificialViscosity = 0 ZEUSQuadraticArtificialViscosity = 2 UseMinimumPressureSupport = 0 MinimumPressureSupportParameter = 100.000000 RefineByJeansLengthSafetyFactor = 4.000000 RefineByCoolingTimeSafetyFactor = 1.000000 MustRefineParticlesRefineToLevel = 0 ParticleTypeInFile = 1 OutputToScratchSpace = 0 DataDirectory = (null) ScratchDirectory = (null) ScratchDirectoryCleanupScript = procyon1 ParallelRootGridIO = 0 MinimumOverDensityForRefinement = 0.2 1.5 1.5 1.5 1.5 MinimumMassForRefinement = 4.88281257e-05 0.000366210938 0.000366210938 0.000366210938 0.000366210938 MinimumMassForRefinementLevelExponent = -0.100000 0.000000 0.000000 0.000000 0.000000 MinimumSlopeForRefinement = 3.000000e-01 MinimumPressureJumpForRefinement = 3.300000e-01 MinimumEnergyRatioForRefinement = 4.000000e-01 ComovingCoordinates = 1 StarParticleCreation = 0 StarParticleFeedback = 0 NumberOfParticleAttributes = 0 StarMakerOverDensityThreshold = 100 StarMakerMassEfficiency = 1 StarMakerMinimumMass = 1e+09 StarMakerMinimumDynamicalTime = 1e+06 StarMassEjectionFraction = 0.25 StarMetalYield = 0.02 StarEnergyToThermalFeedback = 1e-05 StarEnergyToStellarUV = 3e-06 StarEnergyToQuasarUV = 5e-06 LeftFaceBoundaryCondition = 3 3 3 RightFaceBoundaryCondition = 3 3 3 BoundaryConditionName = moving7_0000.boundary CosmologyHubbleConstantNow = 0.5 CosmologyOmegaMatterNow = 1 CosmologyOmegaLambdaNow = 0 CosmologyComovingBoxSize = 1 CosmologyMaxExpansionRate = 0.015 CosmologyInitialRedshift = 10 CosmologyFinalRedshift = 0.3 CosmologyCurrentRedshift = 10 VersionNumber = 1.300000 yt-project-yt-f043ac8/tests/DD0000/moving7_0000.boundary000066400000000000000000000003401510711153200223720ustar00rootroot00000000000000BoundaryRank = 3 BoundaryDimension = 22 22 22 NumberOfBaryonFields = 5 ParticleBoundaryType = 3 BoundaryFieldType = 0 1 4 5 6 BaryonFileName = moving7_0000.boundary.hdf BoundaryValuePresent = 0 0 0 0 0 0 yt-project-yt-f043ac8/tests/DD0000/moving7_0000.boundary.hdf000066400000000000000000001657431510711153200231550ustar00rootroot00000000000000¼Ê\¾&j¶½ºÆÐÐØ ¼Ø ¾äÆtÐ| ¼| ¾ˆÆÐ ¼ ¾,NCSA HDF Version 4.2 Release 1, February 17, 2005@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ jjjм¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?Æ ‚Ð Š ¼ Š ¾ –Æ(&Ð(. ¼(. ¾(:Æ/ÊÐ/Ò ¼/Ò ¾/ÞÆ7nÐ7v ¼7v ¾ 7‚м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^hÆ ?ØÐ ?à ¼ ?à ¾ ?ìÆ G|Ð G„ ¼ G„ ¾ GÆ O Ð O( ¼ O( ¾ O4Æ VÄÐ VÌ ¼ VÌ ¾ VØÐ ¼ ¾ ½Æ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Ð ¼ ¾ ½Æ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Ð ¼ ¾ ½Æ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Ð ¼ ¾ ½Æ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}¾Æ _.Ð _6 ¼ _6 ¾_BÆfÒÐfÚ ¼fÚ ¾fæÆnvÐn~ ¼n~ ¾nŠÆvÐv" ¼v" ¾v.Ð ¼ ¾ ½Æ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Æ~„Ð~Œ ¼~Œ ¾~˜Æ†(І0 ¼†0 ¾†<ÆÌÐÔ ¼Ô ¾àÆ•pЕx ¼•x ¾•„м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¼jÆÚÐâ ¼â ¾îÆ¥~Ð¥† ¼¥† ¾¥’Æ­"Э* ¼­* ¾­6Æ´ÆÐ´Î ¼´Î ¾´Úм¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ÛÀƽ0н8 ¼½8 ¾½DÆÄÔÐÄÜ ¼ÄÜ ¾ÄèÆÌxÐÌ€ ¼Ì€ ¾ÌŒÆÔÐÔ$ ¼Ô$ ¾Ô0м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Æ܆ÐÜŽ ¼ÜŽ ¾ÜšÆä*Ðä2 ¼ä2 ¾ä>ÆëÎÐëÖ ¼ëÖ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿм¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@м¾½Æyt-project-yt-f043ac8/tests/DD0000/moving7_0000.grid0001000066400000000000000000003143411510711153200220060ustar00rootroot00000000000000È j\¾ Æ@¾IÆ@¾‰Æ@¾ ÉÆ@¾  Æ@¾ IÆ@«‰Æª‰Ê<­Š!«Š'ªŠ+<­Šg!«ŠˆªŠŒ<­ŠÈ!«ŠéªŠí<­‹)!«‹Jª‹N<­‹Š!«‹«ª‹¯<­‹ë!«Œ ªŒ<­ŒL!«ŒmªŒq<­Œ­!«ŒÎªŒÒ<­!« /ª 3<­!o!«"ª"”=­#Ñ"«$óª$÷=­%Ž4"«&ŽVª&ŽZ=­'Ž—"«(޹ª(޽=­)Žú"«*ª* =­+]"«,ª,ƒ=­-À"«.âª.æ=­/#"«0Eª0I=­1†"«2¨ª2¯;«3êª3ï8«4‘' ª4‘0:j5‘j½5‘nБŒ­6‘œD«7‘à ª7‘ë;«8’&ª8’+8«9’c ª9’l:j:’¦½:’ªÐ’È­;’ØH«<“ ª<“*;«=“eª=“j8«>“¢ ª>“«:j?“å½?“éД­@”G«A”^ ªA”h;«B”£ªB”¨8«C”à ªC”é:jD•#½D•'ЕE­E•UG«F•œ ªF•¦;«G•áªG•æ8«H– ªH–':jI–a½I–eÐ –ƒ­J–“G«K–Ú ªK–å;«L— ªL—!7«M—XªM—]8«N—• ªN—ž:jO—ؽO—ÜÐ —ú­P˜ L­Q˜VŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNCSA HDF Version 4.2 Release 1, February 17, 2005?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€@ @ž@ @ @ž@ ?€?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?€@ @ž@ @ @ž@ ?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?°@ @ @ @ @ @ ?°?€?€?€?€?€?€?€?€?°@ @ @ @ @ @ ?°?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?°@ @ @ @ @ @ ?°?€?€?€?€?€?€?€?€?°@ @ @ @ @ @ ?°?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€@ @ž@ @ @ž@ ?€?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?€@|@ @ @ @ @|?€?€?€?€?€?€?€?€?€?€@ @ž@ @ @ž@ ?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€?°@|@ @ @|?°?€?€?€?€?€?€?€?€?€?€?€@ @|@|@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?°?°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE¢7 ´ÿ7 ´ÿ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l7 ´ÿ4ÌA4ÌA7 ´ÿ8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m7 ´ÿ4ÌA4ÌA7 ´ÿ8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7 ´ÿ7 ´ÿ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7 ´þ7 ´þ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£5õ4ÌA4ÌA5õ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´þ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´ý4ÌA4ÌA4ÌA4ÌA7 ´ý8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£5õ4ÌA4ÌA5õ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE¥7 ´ý7 ´ý7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l7 ´þ4ÌA4ÌA7 ´þ8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´þ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´ý8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l7 ´ý4ÌA4ÌA7 ´ý8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m7 ´þ4ÌA4ÌA7 ´þ8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´þ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´þ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m7 ´þ4ÌA4ÌA7 ´þ8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE¢7 ´þ7 ´þ7þE¢8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE¢5ô4ÌA4ÌA5õ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´þ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7 ´þ4ÌA4ÌA4ÌA4ÌA7 ´þ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£5ô4ÌA4ÌA5ô7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7 ´þ7 ´þ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7 ´ý7 ´ý7þE¢8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m7 ´ý4ÌA4ÌA7 ´þ8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m7 ´þ4ÌA4ÌA7 ´þ8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7 ´þ7 ´þ7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m8§,m8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,m8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8§,l8§,l8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ðDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ýBˆÉ%BˆÉ%Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡BˆÉ%?ÿý?ÿýBˆÉ%D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£BˆÉ%?ÿý?ÿýBˆÉ%D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBˆÉ%BˆÉ%Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBˆÉ$BˆÉ$Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ@…*!?ÿý?ÿý@…*!Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ$DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ#?ÿý?ÿý?ÿý?ÿýBˆÉ#DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ@…*!?ÿý?ÿý@…*!Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCzÀBˆÉ#BˆÉ#Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡BˆÉ$?ÿý?ÿýBˆÉ$D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ$DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýD$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýD$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ#DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡BˆÉ#?ÿý?ÿýBˆÉ#D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£BˆÉ$?ÿý?ÿýBˆÉ$D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ$DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýD$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýD$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ$DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£BˆÉ$?ÿý?ÿýBˆÉ$D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ýBˆÉ$BˆÉ$Cz¿ýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ý@…* ?ÿý?ÿý@…*!Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ$DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBˆÉ$?ÿý?ÿý?ÿý?ÿýBˆÉ$DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ@…* ?ÿý?ÿý@…* Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBˆÉ$BˆÉ$Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBˆÉ#BˆÉ#Cz¿ýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£BˆÉ#?ÿý?ÿýBˆÉ$D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£BˆÉ$?ÿý?ÿýBˆÉ$D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBˆÉ$BˆÉ$Cz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£D$Û£DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û£D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþD$Û¡D$Û¡DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþValuesfakeDim0 DimVal0.1ªfakeDim0Dim0.0ValuesfakeDim1 DimVal0.1ªfakeDim1Dim0.0ValuesfakeDim2 DimVal0.1ªfakeDim2Dim0.0ValuesfakeDim3 DimVal0.1ªfakeDim3Dim0.0ValuesfakeDim4 DimVal0.1ªfakeDim4Dim0.0ValuesfakeDim5 DimVal0.1ªfakeDim5Dim0.0ValuesfakeDim6 DimVal0.1ªfakeDim6Dim0.0ValuesfakeDim7 DimVal0.1ªfakeDim7Dim0.0ValuesfakeDim8 DimVal0.1ªfakeDim8Dim0.0ValuesfakeDim9 DimVal0.1ª fakeDim9Dim0.0Values fakeDim10 DimVal0.1ª" fakeDim10Dim0.0Values fakeDim11 DimVal0.1ª$ fakeDim11Dim0.0Values fakeDim12 DimVal0.1ª& fakeDim12Dim0.0Values fakeDim13 DimVal0.1ª( fakeDim13Dim0.0Values fakeDim14 DimVal0.1ª* fakeDim14Dim0.0Values fakeDim15 DimVal0.1ª, fakeDim15Dim0.0Values fakeDim16 DimVal0.1ª. fakeDim16Dim0.0Values fakeDim17 DimVal0.1ª0 fakeDim17Dim0.0DensityVALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j5j5j5j5¾j5½5Ñ5 ­­­ªªª¾j½Ð23455DensityVar0.0TotalEnergy  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j:j:j:j:¾j:½:Ñ: ­­­ªªª¾j½Ð789:: TotalEnergyVar0.0x-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j?j?j?j?¾j?½?Ñ? ­­­ªªª¾j½Ð<=>?? x-velocityVar0.0y-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jDjDjDjD¾ jD½DÑD ­­­ªªª¾j½Ð!#%ABC DD y-velocityVar0.0z-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jIjIjIjI¾ jI½IÑI ­­­ªªª¾j½Ð')+FGH II z-velocityVar0.0Temperature  VALUES long_nameAttr0.0KVALUESunitsAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jOjOjOjO¾ jO½OÑO ­­­ªªªª¾j½Ð-/1KLMN OO TemperatureVar0.0­­­­­­­­­­­­­­­­­­­­­­­­!#%')+-/16;@EJPmoving7_0000.grid0001CDF0.0yt-project-yt-f043ac8/tests/DD0000/moving7_0000.grid0002000066400000000000000000007774411510711153200220250ustar00rootroot00000000000000È j\¾ Ʀ`¾°&¦`¾V†¦`¾ üæ¦`¾ £F¦`¾ I¦¦`«ðªð <­ðF!«ðgªðk<­ð§!«ðȪðÌ<­ñ!«ñ)ªñ-<­ñi!«ñŠªñŽ<­ñÊ!«ñëªñï<­ò+!«òLªòP<­òŒ!«ò­ªò±<­òí!«óªó<­óN!« óoª ós<­!ó¯!«"óЪ"óÔ=­#ô"«$ô3ª$ô7=­%ôt"«&ô–ª&ôš=­'ô×"«(ôùª(ôý=­)õ:"«*õ\ª*õ`=­+õ"«,õ¿ª,õÃ=­-ö"«.ö"ª.ö&=­/öc"«0ö…ª0ö‰=­1öÆ"«2öèª2öï;«3÷*ª3÷/8«4÷g ª4÷p:j5÷ª½5÷®Ð÷Ì­6÷ÜD«7ø ª7ø+;«8øfª8øk8«9ø£ ª9ø¬:j:øæ½:øêÐù­;ùH«<ù` ª<ùj;«=ù¥ª=ùª8«>ùâ ª>ùë:j?ú%½?ú)ÐúG­@úWG«Aúž ªAú¨;«BúãªBúè8«Cû ªCû):jDûc½DûgÐû…­Eû•G«FûÜ ªFûæ;«Gü!ªGü&8«Hü^ ªHüg:jIü¡½Iü¥Ð üíJüÓG«Ký ªKý%;«Lý`ªLýa7«Mý˜ªMý8«NýÕ ªNýÞ:jOþ½OþÐ þ:­PþJL­Qþ–ŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNCSA HDF Version 4.2 Release 1, February 17, 2005?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@@@@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@@@@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@ @ @ @ @ @ @?À?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?À@@ @ @ @ @ @ @?À?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@`@ @ @ @ @ @ @ @ @ @ @`?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@ @ @ @ @ @ @?À?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?À@ @ @ @ @ @ @ @ @ @ ?À?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?À@@ @ @ @ @ @ @?À?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@@@@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€@@ @ @ @ @ @ @ @ @?€?€?€?€?€?€?€?€?€?€?€?€?€@€@ @ @ @ @ @ @€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@€@ @ @ @ @€?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@@@@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€?€@@@ @ @ @ @@?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@`@ @ @ @ @`?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @`@@@`@ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À@@@@?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À?À?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚK7©ÚK8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚK7©ÚK8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[;6i;6i;7[;7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[;4ÌA4ÌA4ÌA4ÌA7[;8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[;4ÌA4ÌA4ÌA4ÌA7[;8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[;6i;6i;7[;7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT7©ÚK7©ÚK8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨64ÌA4ÌA4ÌA4ÌA7¨68“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨78ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚK8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚK8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨78ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨74ÌA4ÌA4ÌA4ÌA7¨78“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT7©ÚK7©ÚK8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨64ÌA4ÌA4ÌA4ÌA7¨68“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨78ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨78ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨74ÌA4ÌA4ÌA4ÌA7¨78“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[:6i;6i;7[97þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[:8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[:4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[:8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[:6i;6i;7[:7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[:4ÌA4ÌA4ÌA4ÌA7[98“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[:8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[:4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[:8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[:4ÌA4ÌA4ÌA4ÌA7[:8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚJ7©ÚJ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚK8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚJ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚJ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚK8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚJ7©ÚJ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚJ7©ÚJ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚJ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚJ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚJ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚK4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚJ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚJ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚI8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚJ7©ÚJ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[:4ÌA4ÌA4ÌA4ÌA7[:8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[:4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[98ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[:4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[98ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[94ÌA4ÌA4ÌA4ÌA7[98“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[:6i;6i;7[:7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨74ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[:4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[98ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7[:4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7[98ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[96i;6i;7[97þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨64ÌA4ÌA4ÌA4ÌA7¨68“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ6i;4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA6i;8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨64ÌA4ÌA4ÌA4ÌA7¨68“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT7©ÚJ7©ÚJ8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨64ÌA4ÌA4ÌA4ÌA7¨68“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚJ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚJ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7©ÚJ4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7©ÚI8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA4ÌA8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7¨64ÌA4ÌA4ÌA4ÌA4ÌA4ÌA7¨68ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7¨64ÌA4ÌA4ÌA4ÌA7¨68“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT7©ÚJ7©ÚI8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[96i;6i;7[97þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[94ÌA4ÌA4ÌA4ÌA7[98“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8>sT6i;4ÌA4ÌA4ÌA4ÌA6i;8>sT8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7[94ÌA4ÌA4ÌA4ÌA7[98“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð7þE£7[96i;6i;7[97þE£8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8>sT8>sT8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚI7©ÚI8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ7©ÚI7©ÚI8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8“üÁ8“üÁ8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ðDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'€C'€DïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'€C'€DïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØJAåÿýAåÿýBØJCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØJ?ÿý?ÿý?ÿý?ÿýBØJDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿý?ÿý?ÿý?ÿý?ÿýAåÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿý?ÿý?ÿý?ÿý?ÿýAåÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØJ?ÿý?ÿý?ÿý?ÿýBØJDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØJAåÿýAåÿýBØJCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿC'€C'€C»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}¿þ?ÿý?ÿý?ÿý?ÿýB}¿þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}ÀDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'€DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'€DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}ÀDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}À?ÿý?ÿý?ÿý?ÿýB}ÀDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿC'€C'€C»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}¿þ?ÿý?ÿý?ÿý?ÿýB}¿þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}ÀDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}ÀDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}À?ÿý?ÿý?ÿý?ÿýB}ÀDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØGAåÿýAåÿýBØGCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØJ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØG?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØGAåÿýAåÿýBØGCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØG?ÿý?ÿý?ÿý?ÿýBØGDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØJ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØG?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØG?ÿý?ÿý?ÿý?ÿýBØGDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'ÿC'ÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿý?ÿý?ÿý?ÿý?ÿýAåÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'€DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'€DyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿý?ÿý?ÿý?ÿý?ÿýAåÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'ÿC'ÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'ÿC'ÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿý?ÿý?ÿý?ÿý?ÿýAåÿþC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'€?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿþ?ÿý?ÿý?ÿý?ÿýAåÿþC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'ÿC'ÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØG?ÿý?ÿý?ÿý?ÿýBØGDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØG?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØG?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØG?ÿý?ÿý?ÿý?ÿýBØGDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØGAåÿýAåÿþBØGCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}À?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØG?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþAåÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþBØG?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýBØGDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØGAåÿþAåÿþBØGCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}¿þ?ÿý?ÿý?ÿý?ÿýB}¿þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿAåÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿþDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿAåÿþ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýAåÿþDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}¿þ?ÿý?ÿý?ÿý?ÿýB}¿þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿC'ÿC'ÿC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}¿þ?ÿý?ÿý?ÿý?ÿýB}¿þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC'ÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC'þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»Ïÿ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþB}¿þ?ÿý?ÿý?ÿý?ÿý?ÿý?ÿýB}¿þDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿB}¿þ?ÿý?ÿý?ÿý?ÿýB}¿þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿC'ÿC'þC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØGAåÿýAåÿþBØGCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØG?ÿý?ÿý?ÿý?ÿýBØGDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿý?ÿý?ÿý?ÿý?ÿýAåÿþC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþC»ÏÿAåÿþ?ÿý?ÿý?ÿý?ÿýAåÿþC»ÏÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿBØG?ÿý?ÿý?ÿý?ÿýBØGDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþCz¿ÿBØGAåÿþAåÿþBØGCz¿ÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC»ÏÿC»ÏÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'þC'þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿC'þC'þDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDïÿDïÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþValuesfakeDim0 DimVal0.1ªfakeDim0Dim0.0ValuesfakeDim1 DimVal0.1ªfakeDim1Dim0.0ValuesfakeDim2 DimVal0.1ªfakeDim2Dim0.0ValuesfakeDim3 DimVal0.1ªfakeDim3Dim0.0ValuesfakeDim4 DimVal0.1ªfakeDim4Dim0.0ValuesfakeDim5 DimVal0.1ªfakeDim5Dim0.0ValuesfakeDim6 DimVal0.1ªfakeDim6Dim0.0ValuesfakeDim7 DimVal0.1ªfakeDim7Dim0.0ValuesfakeDim8 DimVal0.1ªfakeDim8Dim0.0ValuesfakeDim9 DimVal0.1ª fakeDim9Dim0.0Values fakeDim10 DimVal0.1ª" fakeDim10Dim0.0Values fakeDim11 DimVal0.1ª$ fakeDim11Dim0.0Values fakeDim12 DimVal0.1ª& fakeDim12Dim0.0Values fakeDim13 DimVal0.1ª( fakeDim13Dim0.0Values fakeDim14 DimVal0.1ª* fakeDim14Dim0.0Values fakeDim15 DimVal0.1ª, fakeDim15Dim0.0Values fakeDim16 DimVal0.1ª. fakeDim16Dim0.0Values fakeDim17 DimVal0.1ª0 fakeDim17Dim0.0DensityVALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j5j5j5j5¾j5½5Ñ5 ­­­ªªª¾j½Ð23455DensityVar0.0TotalEnergy  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j:j:j:j:¾j:½:Ñ: ­­­ªªª¾j½Ð789:: TotalEnergyVar0.0x-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j?j?j?j?¾j?½?Ñ? ­­­ªªª¾j½Ð<=>?? x-velocityVar0.0y-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jDjDjDjD¾ jD½DÑD ­­­ªªª¾j½Ð!#%ABC DD y-velocityVar0.0z-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jIjIjIjI¾ jI½IÑI ­­­ªªª¾j½Ð')+FGH II z-velocityVar0.0Temperature  VALUES long_nameAttr0.0KVALUESunitsAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jOjOjOjO¾ jO½OÑO ­­­ªªªª¾j½Ð-/1KLMN OO TemperatureVar0.0­­­­­­­­­­­­­­­­­­­­­­­­!#%')+-/16;@EJPmoving7_0000.grid0002CDF0.0yt-project-yt-f043ac8/tests/DD0000/moving7_0000.grid0003000066400000000000000000020213411510711153200220050ustar00rootroot00000000000000È j\¾ ÆW¾`ÆW¾·ÆW¾ ÆW¾ eÆW¾ ¼ÆW«ÆªÊ<­!«'ª+<­g!«ˆªŒ<­È!«éªí<­)!«JªN<­Š!««ª¯<­ë!« ª<­L!«mªq<­­!«ÎªÒ<­!« /ª 3<­!o!«"ª"”=­#Ñ"«$óª$÷=­%4"«&Vª&Z=­'—"«(¹ª(½=­)ú"«*ª* =­+]"«,ª,ƒ=­-À"«.âª.æ=­/#"«0Eª0I=­1†"«2¨ª2¯;«3êª3ï8«4' ª40:j5j½5nÐŒ­6œD«7à ª7ë;«8&ª8+8«9c ª9l:j:¦½:ªÐÈ­;ØH«< ª<*;«=eª=j8«>¢ ª>«:j?å½?éЭ@G«A^ ªAh;«B£ªB¨8«Cà ªCé:jD#½D'ÐE­EUG«Fœ ªF¦;«GáªGæ8«H  ªH ':jI a½I eÐ  ƒ­J “G«K Ú ªK å;«L! ªL!!7«M!XªM!]8«N!• ªN!ž:jO!ؽO!ÜÐ !ú­P" L­Q"VŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNCSA HDF Version 4.2 Release 1, February 17, 2005?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ @ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@ @ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð4ÌB4ÌB8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ð8ý‚ðDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþ?ÿÿ?ÿÿDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþDyÿþValuesfakeDim0 DimVal0.1ªfakeDim0Dim0.0ValuesfakeDim1 DimVal0.1ªfakeDim1Dim0.0ValuesfakeDim2 DimVal0.1ªfakeDim2Dim0.0ValuesfakeDim3 DimVal0.1ªfakeDim3Dim0.0ValuesfakeDim4 DimVal0.1ªfakeDim4Dim0.0ValuesfakeDim5 DimVal0.1ªfakeDim5Dim0.0ValuesfakeDim6 DimVal0.1ªfakeDim6Dim0.0ValuesfakeDim7 DimVal0.1ªfakeDim7Dim0.0ValuesfakeDim8 DimVal0.1ªfakeDim8Dim0.0ValuesfakeDim9 DimVal0.1ª fakeDim9Dim0.0Values fakeDim10 DimVal0.1ª" fakeDim10Dim0.0Values fakeDim11 DimVal0.1ª$ fakeDim11Dim0.0Values fakeDim12 DimVal0.1ª& fakeDim12Dim0.0Values fakeDim13 DimVal0.1ª( fakeDim13Dim0.0Values fakeDim14 DimVal0.1ª* fakeDim14Dim0.0Values fakeDim15 DimVal0.1ª, fakeDim15Dim0.0Values fakeDim16 DimVal0.1ª. fakeDim16Dim0.0Values fakeDim17 DimVal0.1ª0 fakeDim17Dim0.0DensityVALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j5j5j5j5¾j5½5Ñ5 ­­­ªªª¾j½Ð23455DensityVar0.0TotalEnergy  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j:j:j:j:¾j:½:Ñ: ­­­ªªª¾j½Ð789:: TotalEnergyVar0.0x-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 j?j?j?j?¾j?½?Ñ? ­­­ªªª¾j½Ð<=>?? x-velocityVar0.0y-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jDjDjDjD¾ jD½DÑD ­­­ªªª¾j½Ð!#%ABC DD y-velocityVar0.0z-velocity  VALUES long_nameAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jIjIjIjI¾ jI½IÑI ­­­ªªª¾j½Ð')+FGH II z-velocityVar0.0Temperature  VALUES long_nameAttr0.0KVALUESunitsAttr0.0e10.4VALUESformatAttr0.0Cartesian  VALUEScoordsysAttr0.0 jOjOjOjO¾ jO½OÑO ­­­ªªªª¾j½Ð-/1KLMN OO TemperatureVar0.0­­­­­­­­­­­­­­­­­­­­­­­­!#%')+-/16;@EJPmoving7_0000.grid0003CDF0.0yt-project-yt-f043ac8/tests/DD0000/moving7_0000.hierarchy000066400000000000000000000034051510711153200225320ustar00rootroot00000000000000 Grid = 1 GridRank = 3 GridDimension = 22 22 22 GridStartIndex = 3 3 3 GridEndIndex = 18 18 18 GridLeftEdge = 0 0 0 GridRightEdge = 1 1 1 Level = 0 Time = 0.81651319185139 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = moving7_0000.grid0001 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 0 GravityBoundaryType = 0 Pointer: Grid[1]->NextGridThisLevel = 0 Pointer: Grid[1]->NextGridNextLevel = 2 Grid = 2 GridRank = 3 GridDimension = 28 28 28 GridStartIndex = 3 3 3 GridEndIndex = 24 24 24 GridLeftEdge = 0.1875 0.1875 0.1875 GridRightEdge = 0.875 0.875 0.875 Level = 1 Time = 0.81651319185139 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = moving7_0000.grid0002 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 0 GravityBoundaryType = 2 Pointer: Grid[2]->NextGridThisLevel = 0 Pointer: Grid[2]->NextGridNextLevel = 3 Grid = 3 GridRank = 3 GridDimension = 34 34 34 GridStartIndex = 3 3 3 GridEndIndex = 30 30 30 GridLeftEdge = 0.28125 0.28125 0.28125 GridRightEdge = 0.71875 0.71875 0.71875 Level = 2 Time = 0.81651319185139 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = moving7_0000.grid0003 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 0 GravityBoundaryType = 2 Pointer: Grid[3]->NextGridThisLevel = 0 Pointer: Grid[3]->NextGridNextLevel = 0 yt-project-yt-f043ac8/tests/DD0010/000077500000000000000000000000001510711153200166045ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/DD0010/moving7_0010000066400000000000000000000154551510711153200205670ustar00rootroot00000000000000InitialCycleNumber = 10 InitialTime = 0.81751317119117 InitialCPUTime = 2.15207e+09 CurrentTimeIdentifier = 0 StopTime = 20.097275649537 StopCycle = 10000 StopCPUTime = 360000 TimeLastRestartDump = 0 dtRestartDump = 18000 TimeLastDataDump = 0.81751316119217 dtDataDump = 0.0001 TimeLastHistoryDump = 0.81651316219217 dtHistoryDump = 0 TimeLastMovieDump = 0.81651316219217 dtMovieDump = 0 TracerParticleOn = 0 TimeLastTracerParticleDump = 0.81651316219217 dtTracerParticleDump = 0 MovieRegionLeftEdge = 0 0 0 MovieRegionRightEdge = 1 1 1 NewMovieLeftEdge = 0 0 0 NewMovieRightEdge = 1 1 1 MovieSkipTimestep = -99999 NewMovieParticleOn = 0 MovieDataField = -99999 NewMovieDumpNumber = 0 NewMovieName = MoviePack CycleLastRestartDump = 0 CycleSkipRestartDump = 0 CycleLastDataDump = 0 CycleSkipDataDump = 0 CycleLastHistoryDump = 0 CycleSkipHistoryDump = 0 CycleSkipGlobalDataDump = 0 OutputFirstTimeAtLevel = 0 StopFirstTimeAtLevel = 0 RestartDumpNumber = 0 DataDumpNumber = 11 HistoryDumpNumber = 0 MovieDumpNumber = 0 TracerParticleDumpNumber = 0 RestartDumpName = restart DataDumpName = moving7_ HistoryDumpName = history MovieDumpName = MovieOutput TracerParticleDumpName = TracerOutput RedshiftDumpName = RedshiftOutput RestartDumpDir = RS DataDumpDir = DD HistoryDumpDir = HD MovieDumpDir = MD TracerParticleDumpDir = TD RedshiftDumpDir = RD GlobalDir = /rmount/users08/stanford/mturk/data StaticHierarchy = 0 TopGridRank = 3 TopGridDimensions = 16 16 16 TopGridGravityBoundary = 0 ParticleBoundaryType = 3 NumberOfParticles = 0 (do not modify) CourantSafetyNumber = 0.5 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 ProblemType = 27 HydroMethod = 2 huge_number = 1.000000e+20 tiny_number = 1.000000e-20 Gamma = 1.6667 PressureFree = 0 RefineBy = 2 MaximumRefinementLevel = 9 MaximumGravityRefinementLevel = 9 MaximumParticleRefinementLevel = -1 CellFlaggingMethod = 2 -99999 -99999 -99999 -99999 -99999 -99999 FluxCorrection = 1 InterpolationMethod = 3 ConservativeInterpolation = 0 MinimumEfficiency = 0.3 MinimumSubgridEdge = 4 MaximumSubgridSize = 2000 NumberOfBufferZones = 1 MustRefineRegionMinRefinementLevel = -1 MetallicityRefinementMinLevel = -1 MetallicityRefinementMinMetallicity = 1e-05 DomainLeftEdge = 0 0 0 DomainRightEdge = 1 1 1 GridVelocity = 0 0 0 RefineRegionLeftEdge = 0.2 0.2 0.2 RefineRegionRightEdge = 0.9 0.9 0.9 MustRefineRegionLeftEdge = 0 0 0 MustRefineRegionRightEdge = 1 1 1 DataLabel[0] = Density DataUnits[0] = none #DataCGSConversionFactor[0] = 6.24041e-27 DataLabel[1] = TotalEnergy DataUnits[1] = none DataLabel[2] = x-velocity DataUnits[2] = none #DataCGSConversionFactor[2] = 4.06287e+07 DataLabel[3] = y-velocity DataUnits[3] = none #DataCGSConversionFactor[3] = 4.06287e+07 DataLabel[4] = z-velocity DataUnits[4] = none #DataCGSConversionFactor[4] = 4.06287e+07 DataUnits[5] = none UniformGravity = 0 UniformGravityDirection = 0 UniformGravityConstant = 1 PointSourceGravity = 0 PointSourceGravityPosition = 0.5 0.5 0.5 PointSourceGravityConstant = 0.02 PointSourceGravityCoreRadius = 0 SelfGravity = 1 GravitationalConstant = 1.000000e+00 S2ParticleSize = 3 GravityResolution = 1 ComputePotential = 0 WritePotential = 0 BaryonSelfGravityApproximation = 0 GreensFunctionMaxNumber = 10 GreensFunctionMaxSize = 1 DualEnergyFormalism = 0 DualEnergyFormalismEta1 = 1.000000e-03 DualEnergyFormalismEta2 = 1.000000e-01 ParticleCourantSafetyNumber = 0.5 RandomForcing = 0 RandomForcingEdot = -1 RadiativeCooling = 1 GadgetEquilibriumCooling = 0 MultiSpecies = 0 RadiationFieldType = 0 GloverChemistryModel = 0 GloverRadiationBackground = 0 GloverOpticalDepth = 0 CloudyCooling = 0 CloudyCoolingGridRank = 0 CloudyCoolingGridRunFile = IncludeCloudyHeating = 0 CMBTemperatureFloor = 0 CloudyMetallicityNormalization = 0.018477 AdjustUVBackground = 1 SetUVBAmplitude = 1 SetHeIIHeatingScale = 1.8 RadiationFieldLevelRecompute = 0 RadiationSpectrumNormalization = 1e-21 RadiationSpectrumSlope = 1.5 ZEUSLinearArtificialViscosity = 0 ZEUSQuadraticArtificialViscosity = 2 UseMinimumPressureSupport = 0 MinimumPressureSupportParameter = 100.000000 RefineByJeansLengthSafetyFactor = 4.000000 MustRefineParticlesRefineToLevel = 0 ParticleTypeInFile = 1 ParallelRootGridIO = 0 ParallelParticleIO = 0 Unigrid = 0 PartitionNestedGrids = 0 ExtractFieldsOnly = 1 CubeDumpEnabled = 0 SRBprefix = /NONE MinimumOverDensityForRefinement = 0.2 1.5 1.5 1.5 1.5 1.5 1.5 MinimumMassForRefinement = 4.8828125e-05 0.000366210938 0.000366210938 0.000366210938 0.000366210938 0.000366210938 0.000366210938 MinimumMassForRefinementLevelExponent = -0.100000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 MinimumSlopeForRefinement = 3.000000e-01 MinimumShearForRefinement = 1.000000e+00 MinimumPressureJumpForRefinement = 3.300000e-01 MinimumEnergyRatioForRefinement = 4.000000e-01 ComovingCoordinates = 1 StarParticleCreation = 0 StarParticleFeedback = 0 NumberOfParticleAttributes = 0 StarMakerOverDensityThreshold = 100 StarMakerMassEfficiency = 1 StarMakerMinimumMass = 1e+09 StarMakerMinimumDynamicalTime = 1e+06 StarMassEjectionFraction = 0.25 StarMetalYield = 0.02 StarEnergyToThermalFeedback = 1e-05 StarEnergyToStellarUV = 3e-06 StarEnergyToQuasarUV = 5e-06 MultiMetals = 0 LeftFaceBoundaryCondition = 3 3 3 RightFaceBoundaryCondition = 3 3 3 BoundaryConditionName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.boundary CosmologyHubbleConstantNow = 0.5 CosmologyOmegaMatterNow = 1 CosmologyOmegaLambdaNow = 0 CosmologyComovingBoxSize = 1 CosmologyMaxExpansionRate = 0.015 CosmologyInitialRedshift = 10 CosmologyFinalRedshift = 0.3 CosmologyCurrentRedshift = 9.9910277956689 VersionNumber = 1.300000 yt-project-yt-f043ac8/tests/DD0010/moving7_0010.boundary000066400000000000000000000004131510711153200223750ustar00rootroot00000000000000BoundaryRank = 3 BoundaryDimension = 22 22 22 NumberOfBaryonFields = 5 ParticleBoundaryType = 3 BoundaryFieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.boundary.hdf BoundaryValuePresent = 0 0 0 0 0 0 yt-project-yt-f043ac8/tests/DD0010/moving7_0010.boundary.hdf000066400000000000000000001713401510711153200231450ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿàòÿÿÿÿÿÿÿÿ €`HEAP°€BoundaryDimensionType.0BoundaryDimensionValue.0BoundaryDimensionType.1BoundaryDimensionValue.1BoundaryDimensionType.2BoundaryDimensionValue.2PTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàØzÀ¸@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ €` ! èèÀ¶H H NumberOfBaryonFields  àW SNODÐ@ SxÀU (X°TÐV! èÿÿÿÿÿÿÿÿèÀ¶H€@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ! è [èÀ¶H H NumberOfBaryonFields  Y ! èÿÿÿÿÿÿÿÿèÀ¶H€ ! è@§èÀ¶H H NumberOfBaryonFields   Z ! èÿÿÿÿÿÿÿÿèÀ¶H€ @ BoundaryRank   8 Index   8 Size  ä P BoundaryDimension  ÿÿÿ @ BoundaryRank   8 Index   8 Size  ä P BoundaryDimension  ÿÿÿ @ BoundaryRank   8 Index   8 Size  ä P BoundaryDimension  ÿÿÿ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@yt-project-yt-f043ac8/tests/DD0010/moving7_0010.cpu0000000066400000000000000000052766141510711153200216700ustar00rootroot00000000000000‰HDF  ÿÿÿÿÿÿÿÿŒ}ÿÿÿÿÿÿÿÿ €`HEAP¨€Grid00000001Grid00000002Grid00000003Grid00000004Grid00000005Grid00000006Grid00000007Grid00000008Grid00000009Grid00000010XTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX8TP˜¸@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ €`HEAP€8@åTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°ànÀht@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ ÐSNOD(Ј˜h–0–(¢øŸÀŸ8\p?€:?€7?öK=Þ|Ð=̽O=Ì̱=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÐ?}Ê&?€ ð?€??€5?€2?ý÷=Þ{9=̽==Ì̱=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÌ?{¡š?€ á?€:?€.?þ?F÷€=Õ¥}=̽'=Ì̲=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÄ=Ýûj?{À?öQ?ýõ?F÷˜>`0/=ÌÌø=̽=Ì̳=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌç=Þ ==Þ|Ï=Þ{G=Õ£Ë=ÌÍ=ÌÌÔ=̽,=Ì̲=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÁ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌã=̽N=̽<=̽(=̽=̽(=Ì̤=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌ´=ÌÌ´=Ḭ̀=Ḭ̀=Ì̲=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÀ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÇ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÆ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÇ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÁ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÄ=ÌÌÄ=ÌÌÉ=ÌÌÍ=ÌÌÑ=ÌÌÍ=ÌÌÅ=ÌÌÆ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÁ=ÌÌÁ=ÌÌÂ=ÌÌÄ=ÌÌÍ?{–í?}Ê(?}Ê&?}Ê=Þ8¤=ÌÌê=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÍ?{—'?ÿñ?€ õ?€ ö?€ î?}è¨=ÞK=ÌÌå=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÅ=ÌÌÒ?}Ê%?€ ð?€B?€(?€:?ýð=Þ{Z=̽>=Ì̯=ÌÌÁ=ÌÌÁ=ÌÌÁ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÐ?}Ê%?€ ò?€7?€#?€2?€$=Þy=̽+=Ì̱=ÌÌÂ=ÌÌÁ=ÌÌÅ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌË?}Ê?€ ò?€d±l=ÌÌä=̽ =Ì̲=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌæ=ÞIJ=Þ{J=Þy =Õ¹(=ÌÌñ=ÌÌ¿=̽=Ì̲=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌç=̽?=̽,=̽=̽ =̽=ÌÌ =ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=Ì̵=ÌÌ´=Ì̳=Ì̱=Ḭ̀=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÁ=ÌÌÂ=ÌÌÆ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÇ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÁ=ÌÌÄ=ÌÌÇ=ÌÌÆ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÀ=ÌÌÈ=ÌÌÆ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÀ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÉ=ÌÌË=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÆ=Ýú•?{¡?}Ê=ï¦+=ÌâS=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÆ=ÝúŽ?{©Œ?€ ã?€ ó?}ó3=柳=Ìñü=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÐ?{¡Å?€ Ý?€5?€6?þ ?F÷—=Õ¤<=̽'=Ì̱=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÃ=ÌÌÎ?}Ê?€ ð?€0?€*?€0?Hš¦=Õ¹“=̽=Ḭ̀=ÌÌÁ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÅ=ÌÌÄ=說?}ó5?ýù?€)?€!>èÞ›=Ìä}=̽ =Ì̲=ÌÌÄ=ÌÌÇ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÄ=Ìã™=ï¦i?F÷‰?Hš™>èÞ§=ÕÄ=ÌÌÀ=̽ =Ì̲=ÌÌÆ=ÌÌÇ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=Ìó;=Õ¥u=ÕºÔ=ÌäG=ÌÌÉ=ÌÌÀ=̽=Ì̱=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÇ=ÌÌÇ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=̽)=̽=̽ =̽ =̽=ÌÌ =ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÄ=ÌÌÇ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÂ=Ì̳=Ì̳=Ì̲=Ì̳=Ì̲=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÁ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÆ=ÌÌÄ=ÌÌÁ=ÌÌÀ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÀ=ÌÌÀ=ÌÌÁ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÁ=ÌÌÃ=ÌÌÂ=ÌÌÀ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÇ=ÌÌÅ=ÌÌÆ=ÌÌÅ=ÌÌÆ=ÌÌÂ=ÌÌÅ=ÌÌè=Ýúø=Þ:E=Ìã)=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌê=ÝÐP?{¿ø?}è¨=ï¥Ù=ÌÝn=ÌÜg=ÌÌÔ=ÌÌÁ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÁ=ÌÌÆ=Ýø?{À0?öS?ýê?F÷Š>`0=ÌÍ=̽=Ì̳=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÅ=Þ9?}è¨?ýü?€*?Hš“>d±`=ÌÌæ=̽ =Ì̲=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÌÅ=Ìáç=ï§?F÷ ?Hš¶>èÞ“=ÕÃÁ=ÌÌË=̽=Ì̱=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÝo>`09>d±`=ÕÖ=ÌÌÇ=ÌÌÁ=̽=Ì̱=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÜf=ÌÍ =ÌÌæ=ÌÌÀ=ÌÌÀ=ÌÌÀ=̽=Ì̵=ÌÌÇ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÕ=̽ =̽ =̽ =̽=̽=ÌÌ¢=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÃ=Ì̳=Ì̳=Ì̳=Ì̵=ÌÌ´=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÁ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÆ=ÌÌÇ=ÌÌÄ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌÁ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÅ=ÌÌÇ=ÌÌÆ=ÌÌÄ=ÌÌÅ=ÌÌÇ=ÌÌÃ=ÌÌÁ=ÌÌÀ=ÌÌÃ=ÌÌÇ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÁ=ÌÌÁ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÁ=ÌÌÆ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÇ=ÌÌÅ=ÌÌÁ=ÌÌÃ=ÌÌÃ=ÌÌæ=ÌÌè=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÆ=ÌÌé=Þ Ó=ÞIT=ÌòÏ=ÌÜf=ÌÜg=ÌÌÖ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌë=Þ ë=Þ|ê=Þ{V=Õ¤û=ÌÌù=ÌÌÓ=̽*=Ì̲=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌë=ÞJ©=Þ{g=Þy$=Õºj=ÌÌå=ÌÌÀ=̽=ÌÌ´=ÌÌÇ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÄ=Ìñ=Õ£ê=Õ¹/=Ìä =ÌÌÂ=ÌÌÁ=̽=Ì̳=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÂ=ÌÌÂ=ÌÜh=ÌÍ =ÌÌè=ÌÌÀ=ÌÌÀ=ÌÌÂ=̽=Ì̲=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÜf=ÌÌÞ=Ì̼=ÌÌ»=ÌÌ»=Ì̺=̽=Ì̲=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌ×=̽,=̽=̽=̽=̽=ÌÌš=ÌÜO=ÌÜX=ÌÜU=ÌÜY=ÌÜX=ÌÌÕ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÃ=Ì̳=Ì̱=Ì̳=Ì̲=Ì̵=ÌÌÈ=ÌÜW=ÌÜf=ÌÜh=ÌÜh=ÌÜk=ÌÌÕ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÂ=ÌÌÆ=ÌÜX=ÌÜe=ÌÜ\=ÌÜ[=ÌÜh=ÌÌÕ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÀ=ÌÌÃ=ÌÌÈ=ÌÌÄ=ÌÌÄ=ÌÜX=ÌÜ_=ÌÜm=ÌÜk=ÌÜ\=ÌÌÕ=ÌÌÃ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÜX=ÌÜ^=ÌÜm=ÌÜo=ÌÜ`=ÌÌÖ=ÌÌÆ=ÌÌÅ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÆ=ÌÌÇ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÜX=ÌÜg=ÌÜ\=ÌÜ]=ÌÜj=ÌÌÓ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÖ=ÌÌÕ=ÌÌÓ=ÌÌÔ=ÌÌ×=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÆ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÆ=ÌÌÆ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌç=ÌÌç=ÌÌÂ=ÌÌÔ=ÌÌÕ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌæ=̽P=̽<=̽%=̽=̽+=ÌÌ£=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÂ=ÌÌÂ=ÌÌæ=̽>=̽*=̽=̽ =̽=ÌÌ£=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÇ=̽*=̽=̽ =̽ =̽=ÌÌ¡=ÌÌÅ=ÌÌÈ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÆ=ÌÌÁ=ÌÌÄ=ÌÌ×=̽!=̽ =̽ =̽=̽=ÌÌŸ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌ×=̽+=̽=̽=̽=̽=ÌÌš=ÌÜL=ÌÜT=ÌÜV=ÌÜY=ÌÜW=ÌÌÓ=ÌÌÂ=ÌÌÅ=ÌÌÆ=ÌÌÂ=ÌÌ¡=ÌÌ¢=ÌÌ¡=ÌÌŸ=ÌÌ¢=ÌÜ4=ÌÌü=ÌÌï=ÌÌì=ÌÌñ=ÌÌä=̽>=Ì̳=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÆ=Ìëè=ÌÔ¶=ÌÔ¨=ÌÔš=ÌÔ¤=ÌÔ’=̽8=Ì̵=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÇ=ÌÌÅ=ÌÌÆ=Ìëô=ÌÔÁ=ÌÕ=ÌýW=Ìÿ¿=ÌÔû=̽(=Ì̵=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÇ=Ìëð=ÌÔÄ=ÌýW=Íw=Íwk=Í=̾=Ì̳=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=Ìëó=ÌÔÁ=Ìÿ•=ÍwU=Íw¨=Í$=̾=ÌÌ´=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=Ìëñ=ÌÔ»=ÌÕ3=Í.=̓=ÌÕ=̽)=ÌÌ·=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌå=̽J=̽.=̾=̾=̽)=ÌÌ =ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=Ì̵=Ì̵=Ì̳=Ì̵=Ì̵=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÂ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÇ=Ì̵=Ì̱=Ì̱=Ì̳=Ì̱=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÆ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌ´=ÌÌ´=Ì̲=Ì̱=Ì̳=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=Ì̳=Ì̲=Ì̳=Ì̲=Ì̲=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=Ì̱=Ì̱=ÌÌ´=Ì̲=Ì̲=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌ´=Ì̲=Ì̳=Ì̳=Ì̳=ÌÌÆ=ÌÜT=ÌÜg=ÌÜh=ÌÜg=ÌÜk=ÌÌÔ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÈ=Ìëç=ÌÔ·=ÌÔ¦=ÌÔœ=ÌÔ =ÌÔ’=̽8=ÌÌ´=ÌÌÇ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÜK=ÌÍ=ÌÌî=Ìë><·×>@Ê¿=Î ‚=̽/=Ì̶=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÜ^=ÌÌò=ÌÖƒ>ñeV?qNí?s`¹>ù|G=Ð ¶=ÌÌÉ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÀ=ÌÜ]=ÌÌÿ>;y?pX´?Áñ«?ÂÇ+?v@|=×@=ÌÏ®=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÜ^=ÌÌû>=Z?qB­?ÂT'?Ã'á?w.w=×Io=Ìϵ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÜ^=ÌÌõ=ÍÄÚ>õ#?sÀ%?uÓÊ>ý=s=Ð*Â=ÌÌÏ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÖ=̽?=̽=Ïõ«=×l=×}=Ð%²=ÌÏh=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÇ=ÌÌÆ=ÌÌ´=Ì̳=ÌÌÇ=ÌÏÒ=ÌÏÜ=ÌÌÍ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÅ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÏ=ÌÌÐ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÇ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÁ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÁ=ÌÌÁ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÁ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÁ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÈ=ÌÌÆ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÁ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÜW=ÌÜj=ÌÜ_=ÌÜ\=ÌÜj=ÌÌÔ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÅ=Ìëî=ÌÔÇ=ÌÕ=ÌýW=Ìÿ´=ÌÕ=̽)=ÌÌ´=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÜ_=ÌÌï=ÌÖ>ñe5?qNÜ?s`à>ù|`=Ð Ä=ÌÌË=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÜo=ÌÌÈ>ð9j?À-L@ ñ¥@ ­É?Ã,q=àmÏ=ÌÒ½=ÌÌÏ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÜo=̤E?ocø@ ~@qè@t×@ ña?•„3=×{4=ÌÌÍ=ÌÌÁ=ÌÌÁ=ÌÌÄ=ÌÌÈ=ÌÌÆ=ÌÌÂ=ÌÜp=Ì¡ð?pN"@ íÒ@s.@u>;@ QÄ?•ë=×…T=ÌÌÃ=ÌÌÁ=ÌÌÁ=ÌÌÃ=ÌÌÅ=ÌÌÆ=ÌÌÃ=ÌÜo=ÌÌÈ>óãu?Á5P@ C@ Ãü?Ä7 =ô¶t=ÌÝÈ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÆ=ÌÌÃ=ÌÌÖ=̽/=Ïë¦=Þ¬û?“ou?• \=ô–¦=ÌÕÄ=ÌÌÇ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÆ=Ì̳=ÌÌÆ=ÌÍ=×@é=×zÝ=ÌÜÝ=ÌÌÆ=ÌÌÆ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÈ=ÌÌÈ=ÌÌÃ=ÌÌÌ=ÌÌÃ=ÌÌÆ=ÌÌÍ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÀ=ÌÌÀ=ÌÌ¿=ÌÌÂ=ÌÌÇ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÇ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÂ=Ì̾=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÁ=ÌÌÁ=ÌÌÇ=ÌÌÄ=ÌÌÉ=ÌÌÆ=ÌÌÁ=ÌÌÁ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌÁ=ÌÌÀ=ÌÌÀ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÈ=ÌÌÈ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÇ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÜX=ÌÜ]=ÌÜo=ÌÜk=ÌÜ]=ÌÌÖ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÅ=Ìëî=ÌÔÈ=ÌýQ=Íw=Íw\=Í=̾=Ì̲=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÜ`=ÌÌþ>;xò?pX¬?ÁñŸ?ÂÇ3?v@„=×?È=ÌÏÝ=ÌÌÌ=ÌÌÁ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÜp=̤D?od@ ‚@qçì@tà@ ñe?•„<=×{6=ÌÌÉ=ÌÌÃ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÀ=ÌÌÃ=ÌÜn=Ì'Ø?Àxá@p½ùA=X?qB·?ÂT)?Ã'à?w.M=×I/=ÌÏà=ÌÌÌ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÜr=Ì¡ð?pN&@ íÁ@s"@u>%@ Qß?•ë=×…k=ÌÌÉ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÜp=Ì'‚?ÀÙ›@qÕvA”CŸA¢A@x߈?Ä8d=×{b=ÌÌÉ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÜp=Ì'B?Á9×@rïvAšA±kÂ@z Ý?ÄH1=×vG=ÌÌÎ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÜn=ÌW?rÀD@ ©@v;@xËŸ@no?—&e=×—Ó=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÁ=ÌÌÁ=ÌÌÂ=ÌÌÂ=ÌÌÔ=̼×=Õ«j?“¥?²i?Â×L?—"w=ìµ =ÌÚö=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÀ=ÌÊ=×JÂ=×^@=×Tm=׌½=ÌÙÛ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÍ=ÌÌÍ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÂ=ÌÌÇ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÇ=ÌÌÃ=ÌÌÀ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÌÁ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÀ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÆ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÜX=ÌÜg=ÌÜ\=ÌÜ]=ÌÜg=ÌÌÔ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÃ=Ìëñ=ÌÔ¼=ÌÕ1=Í.=Í=ÌÕ =̽+=Ì̱=ÌÌÅ=ÌÌÂ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÜ\=ÌÌø=ÍÄ >õ?sÀ\?uÓâ>ý=§=Ð*z=ÌÌÐ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÀ=ÌÌÄ=ÌÜp=ÌÌÑ>óãA?Á5H@ <@ Ãý?Ä7=ô·&=ÌÝ4=ÌÌÑ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÁ=ÌÌÀ=ÌÜn=ÌŸŒ?qÔÎ@ ¡6@u\Ö@w¤@ x?–ÀØ=×þ=ÌÌÎ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÜn=ÌU?rÀA@ ®@v?@x˺@np?—&Q=×—û=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÁ=ÌÌÄ=ÌÌÄ=ÌÜm=ÌÌÉ>÷“Ç?Â?D@ …@ ß’?ÅB>¬ =Ìè§=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌ×=̽.=ÏÿQ=ò?”«¼?–ÖÈ>žã=Ì÷3=ÌÌÒ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÆ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌ´=ÌÌÅ=Ì×Ì=×So=׌ß=ÌèA=ÌÌÒ=ÌÌÃ=ÌÌÁ=ÌÌÀ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÅ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÅ=ÌÌÁ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÈ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÉ=ÌÌ×=ÌÌÓ=ÌÌÓ=ÌÌÔ=ÌÌÓ=ÌÌÃ=ÌÌÅ=ÌÌÇ=ÌÌÆ=ÌÌÂ=ÌÌÁ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÂ=ÌÌé=̽K=̽)=̾=̾=̽*=ÌÌ£=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÆ=ÌÌÅ=ÌÌÃ=ÌÌÔ=̽A=̽=Ïõ=×Û=×à=Ð%Ø=ÌÏe=ÌÌÄ=ÌÌÂ=ÌÌÁ=ÌÌÁ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÆ=ÌÌÕ=̽5=Ïë‚=Þ­.?“ol?• \=ô—Œ=ÌÕ°=ÌÌÆ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÕ=̼Û=Õ£?“9,? 8?ÂÆÊ?–½b=ìµ=ÌÚ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÖ=̼Õ=Õªý?“¨?²p?Â×L?—"‡=ìµ0=ÌÚ+=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌ×=̽-=ÏÿT=òL?”«¯?–Ö°>žŸ=ÌöÒ=ÌÌÏ=ÌÌÆ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÆ=ÌÌÃ=ÌÌ£=ÌÏ =ÌÆZ=ëç‹=ì¡©=Ìö\=ÌÌÇ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=Ì̾=ÌÚœ=ÌÚå=ÌÌÓ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÈ=ÌÌÃ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÁ=ÌÌÃ=ÌÌÈ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÇ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÂ=ÌÌÄ=ÌÌÇ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÁ=ÌÌÀ=ÌÌÄ=ÌÌÇ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÁ=ÌÌÃ=ÌÌÅ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÃ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÂ=ÌÌÁ=ÌÌÂ=ÌÌÄ=ÌÌÂ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÁ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÅ=ÌÌÄ=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÇ=ÌÌÃ=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÆ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÆ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÄ=Ì̳=ÌÌ´=Ì̳=Ì̳=ÌÌ´=ÌÌÇ=ÌÌÆ=ÌÌÄ=ÌÌÃ=ÌÌÂ=ÌÌÂ=ÌÌÃ=ÌÌÂ=ÌÌÄ=ÌÌÅ=Ì̲=ÌÌ´=ÌÌÇ=ÌÏ—=ÌÏ¢=ÌÌÍ=ÌÌÄ=ÌÌÆ=ÌÌÂ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÆ=ÌÌÃ=Ì̳=ÌÌÊ=Ì̲=×A‚=×z6=ÌÝ`=ÌÌÆ=ÌÌÄ=ÌÌÂ=ÌÌÃ=ÌÌÆ=ÌÌÅ=ÌÌÂ=ÌÌÂ=ÌÌÅ=ÌÌÆ=ÌÌ¿=ÌÉÝ=×AR=×c=×Y‘=ׂ+=ÌÚ“=ÌÌÃ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÁ=ÌÉÔ=×KR=×^=×Tj=׌2=ÌÚ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÄ=ÌÌÄ=ÌÌÃ=ÌÌÃ=Ì̳=ÌÌÁ=ÌÖ¤=×T?=׌}=Ìçû=ÌÌÓ=ÌÌÃ=ÌÌÅ=ÌÌÅ=ÌÌÃ=ÌÌÆ=ÌÌÃ=ÌÌÂ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÄ=Ì̾=ÌÙ˜=ÌÙÜ=ÌÌÏ=ÌÌÊ=ÌÌÆ=ÌÌÃ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÂ=ÌÌÄ=ÌÌÄ=ÌÌÅ=ÌÌÅ=ÌÌÄ=ÌÌÃ=ÌÌÄ=ÌÌÉ=ÌÌÄ=ÌÌÄ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýq8ý¯8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýo8ýi8ýi8ýª8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýl8ýf8ýj8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýk8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 4ÿ'e4¾è 8ç_¬8ý¯8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 4ÿ!_4~ê4~èÆ4½‰º8ç\½8ý¬8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý4¾ã{4~é 4~èY4~æ‹8çn8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ç_#4½ŠF4~æ¥8ÔJÐ8üõ;8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý°8ç`F8ç8ü÷;8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý©8ý¤8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 4ÿ!4¾á}8ç_£8ý®8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý!5 »4š4—~4¼~j8çò8ý³8ý8ý8ý8ý8ý8ý8ý8ý8ý4ÿ$#4š4–a4–d4—z4¼./8ç\º8ý©8ý8ý8ý8ý8ý8ý8ý8ý4¾æA4—€4–k4–e4–b4|2è8ç[8ý£8ý8ý8ý8ý8ý8ý8ý8ý8ç_4¼€f4—|4–g4yÑ8Ô_k8üõ;8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý¯8çé4¼+Ø4|-T8Ô^>8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý²8ç]D8çí8ü÷38ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý§8ý¡8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý4ÿ$4~ê4~è±4½‡ƒ8ç_¶8ý­8ý8ý8ý8ý8ý8ý8ý8ý8ý4ÿ|4š4–h4–i4—x4¼( 8ç\°8ý¨8ý8ý8ý8ý8ý8ý8ý8ý4~éª4–b4–(4–.4–o4Î8æÂ«8ý™8ý8ý8ý8ý8ý8ý8ý8ý4~èÐ4–c4–14–34–04p8æÄ·8ý˜8ý8ý8ý8ý8ý8ý8ý8ý4½ˆÌ4—y4–r4–,4¾6„þ­8ñp8ý§8ý8ý8ý8ý8ý8ý8ý8ý8ç\/4¼.Ã4΂4e6„þÄ8HïJ8ýÇ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý¬8ç]@8æÂ±8æÄ·8ñq»8ýÏ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý§8ý—8ý˜8ý¨8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý4¾çâ4~éL4~è­4~æ­8ç 8ý¦8ý8ý8ý8ý8ý8ý8ý8ý8ý4¾á|4—{4–i4–k4–a4|+¨8çÜ8ý¡8ý8ý8ý8ý8ý8ý8ý8ý4~è‹4–c4–64–'4–84b8æÄ 8ý™8ý8ý8ý8ý8ý8ý8ý8ý4~è_4–e4–44–,4–:4–,8æÇ_8ý—8ý8ý8ý8ý8ý8ý8ý8ý4~æ©4–f4–:4–(4–86€F>8ñTå8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8çé4|5‘4n4–76€F]8Cá©8ý·8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý£8çÙ8æÄ³8æÇt8ñV—8ýÁ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý¢8ýš8ý™8ý¦8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ç^—4½‹74~æÌ8ÔJo8üöº8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ç^˜4¼€¢4—{4–g4yX8Ô]Ä8üö´8ý8ý8ý8ý8ý8ý8ý8ý8ý4½ˆ4—u4–m4–54Ä6„þÐ8ñqP8ý§8ý8ý8ý8ý8ý8ý8ý8ý4~æn4–c4–-4–34–96€FY8ñV(8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8ÔIà4y#4¹4–14–*7‡Ëv8üï:8ý8ý8ý8ý8ý8ý8ý8ý8ý8üõ/8Ô^Ì6„þ°6€FL7‡Ëk8ñ@ã8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8üõ08ñp8ñTØ8üïk8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý©8ý©8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý®8ç\·8çö8üõ¶8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý°8çÜ4¼0K4|4¯8Ô_r8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ç_©4¼*s4΄4j6„þ§8HïT8ýÎ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ç{4|.­4g4–16€FC8Cáµ8ý·8ý8ý8ý8ý8ý8ý8ý8ý8ý8ü÷E8Ô]¡6„þ½6€FK7‡Ëz8ñAN8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8HïQ8Cáµ8ñAw8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ýØ8ý¸8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý«8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý³8ç]¶8ç×8üõ¾8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý¯8ç]°8æÂ¢8æÄ¡8ñpŠ8ýÇ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý§8çW8æÄ 8æÇc8ñUT8ý·8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ü÷?8ñqÃ8ñVŸ8üï·8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý×8ý¹8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý 8ý 8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý 8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýª8ý¢8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýª8ý™8ý—8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý¡8ý™8ý—8ý¥8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýª8ý§8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýŠ9”Ä9öÕ8ýÓ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý9‹é9À7E9ÀÔ9´Ã8ý!c8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý9ÃY9À¼Ò9ÁF 9"ã—8ý"{8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý9‹%9"ßã8ý£8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý!Y8ý"}8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý 8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý9;j59FÜí9Ë+N9>#8ýAŽ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý9+]R8¼s8êV8¼¤ý9¥ ¾9ì8ýì8ý8ý8ý8ý8ý8ý8ý8ý8ý9gl7ë:4Řv7µƒ9BÚÚ9¿aO8ÿÓÉ8ý8ý8ý8ý8ý8ý8ý8ý8ý8ÿU°7¼O4¡û7¤!9?z ?v¥?t+?sß?y?zö?|?{’?yË?wÎ?w?x‰?z/?{ž?{G?zÐ?yé?wé?vº?w9?{¢?~??}¸?y«?u?sæ?u…?wy?y?ya?yÂ?y¦?y?xõ?yü?}‚?ã??~G?yF?u?q°?s?sa?u~?wÄ?y"?{l?}•?`?€“?€ä?Ù?}?y¨?v ?s?rc?r`?s?u–?w¤?yg?{L?}¢?u?€¨?€¨?ä?|ß?yš?vŒ?sQ?r_?s?t?v2?wº?ya?{?} ?~M?Ì?þ??|‰?y ?v?só?s4?u™?vŒ?w’?x§?y™?zV?{k?|?}ë?~È?}_?{£?yž?vÒ?u>?tÒ?xm?yV?z?z.?y¦?y?xê?yø?{,?{ò?{÷?z¼?y=?wU?w?w_?}J?³?`?~V?y’?ub?rE?sk?u™?wö?yR?yM?y!?xÏ?yš?{]?‚x?‡?‰ã?ƒ)?y?pë?ið?k©?p˜?t¤?w^?x}?y?v#?x?yo?y¤?y¯?y†?z?{-?y‰?{?{¸?{&?y ?ww?w•?x?y¯?z’?{>?z?y}?xÖ?x&?xQ?v©?wí?xÑ?yÒ?yt?yR?z?{)?|u?|Â?|Ó?{c?y=?wˆ?vÇ?v=?tç?v ?v÷?x·?z?zª?{¤?}?}ë?~3?}Õ?{Ø?yK?vë?u‚?tØ?tQ?t»?vN?xS?yÝ?{L?|L?}ê?~à?~¿?~9?|p?y¯?v²?t§?t?t|?t¤?vA?x ?y’?{-?|?~&?,?~Ì?}Í?|?z!?vº?t?tI?ua?uÏ?v¿?x%?y¦?z¡?{û?}g?~O?~?}:?{¬?yƒ?w&?u–?u?w?w~?xV?yh?y“?yè?z‚?{P?|V?|ï?|>?{F?yh?w·?vŒ?vh?y‰?zˆ?{`?{?yè?xh?w›?xO?yM?zâ?zÿ?zW?yy?x?x ?xŽ?{•?}k?~À?}Ë?yØ?uA?t-?ua?w&?yT?z?z@?yF?xÑ?y&?z*?Ë?ƒm?†?ƒ]?y›?o³?mB?ow?rä?vk?wû?y?yn?yµ?{&?}?„¶?ŠM?‹â?…P?y¾?mj?fè?hî?n]?s¸?ve?x9?yY?zÜ?|Ÿ?¤?ˆž?,?Œ¤?ˆ#?|¤?oÅ?c„?_?jÊ?r ?uÅ?x?y¨?{*?}e?¼?ˆI?]?5?‡Y?|?|^?}Ê?~?}¸?{ó?yd?w ?u?tí?sX?tÄ?vL?wÖ?yé?{u?|Û?~o?a?¯?~ÿ?|q?y6?vU?tT?s&?r˜?s·?u\?wA?yá?|?}¬?^?€c?€?ä?|Þ?y?uÌ?s«?rX?rÍ?st?u\?w=?yÚ?{Ð?}¼?u?€£?€™?Ž?|Ç?y@?uð?sq?r†?sÅ?t¡?ué?w}?yé?{2?|·?~¯?Ê?½?~¹?|Œ?y_?vž?t?sU?uš?vs?w?xÿ?y†?z‰?{9?|R?}¶?~:?}[?{ú?y­?wA?u&?tí?w÷?y[?zl?z”?y—?x÷?x›?y{?zÐ?|?{Ÿ?{*?y‡?wÿ?w,?w?xž?zš?{¨?{p?yM?wŒ?wQ?xz?z}?{y?{Í?{]?yM?x?wU?wœ?|j?7?€€?q?yy?s ?r•?tL?vÝ?xq?yÊ?z ?yz?y?yK?z‚?€T?„"?†?‚ò?yT?nê?m?oã?s?v?x-?yM?y‘?z)?z®?|¼?‚ó?‡ç?‰²?…ï?{‘?pñ?g!?jF?pÑ?u%?wx?x›?y¯?zˆ?{‡?~‚?‚Ø?ˆ?‰°?‡?{f?n¶?g?hÕ?q?u5?wš?x}?y”?zw?{Ç?~´?€I?ƒí?†?‡ê?z‚?nl?iÝ?m´?s“?v/?xL?yT?y¯?yá?{6?}?|Þ?~ÿ??„?xÀ?r*?pP?qÌ?wE?x·?z?z ?y‡?yd?y{?z¹?y?z?|g?=?wW?vI?uõ?vÛ?zÀ?|?|?zÄ?y˜?xo?w‚?w ?u ?vã?xœ?y¡?xŸ?y(?z^?|8?}ò?~?~?|f?yÖ?vØ?u?t»?r?t?v?x?yG?{?}!?~ù?€‹??€/?}•?yœ?u”?r§?qÜ?p?rZ?t†?vÞ?y?|Y?~À?€÷?‚÷?ƒu?‚?~¾?yQ?t3?q ?oM?n?q?sš?v-?y³?|ã? ?‚.?„?„»?ƒ[?¤?y|?sN?oÕ?n'?o?pµ?s]?v:?yâ?}'?a?‚)?„?„½?ƒ$?Ÿ?y»?sŒ?ok?nC?p?qƒ?t ?vÔ?z'?|–?~=? ?ƒ?ƒ—?á?~½?yj?tl?p•?oµ?r°?sØ?uß?wà?yã?{Z?|Ç?~Ú?€Ä?K?€?}‘?yŠ?u¸?r„?qÑ?uy?vú?xX?yR?yY?yä?z¯?|?}‹?~}?}ù?|¬?yE?vñ?tõ?tv?të?vû?xÁ?yÈ?y]?y,?z’?|!?}Â?~¢?~;?|´?y??v»?uF?t/?x–?zÝ?{Ô?{¹?y?vÑ?w3?x­?z„?{??{Ð?{?yY?xK?wŸ?wD?{µ?~Q??}¶?yx?t:?s§?uš?w·?xò?yì?z(?y?yS?y?yÛ?}^?€°?‹?¤?yE?s?pÕ?rI?vF?wÝ?y(?y—?yª?yš?z?{o?}[?€Ï?Ò?‚q?x€?rÂ?p«?r?v?x@?yp?y]?yx?yw?z$?{l?{†?~)?À?‚Â?w½?s?rÂ?t$?wÞ?y™?zK?yé?y´?y?y9?z??x¬?zë?|â?€Ž?ví?uÇ?uç?w$?z¾?{Ï?{Ý?{ ?yì?x ?w—?w¼?uŒ?wc?yD?~³?wU?x;?y"?z˜?~y?~Þ?~C?|…?z?v?tÔ?tw?q”?sá?vJ?wú?x‰?zd?|á?ß?‚ ?‚b??~,?z ?tŒ?qˆ?pÑ?mÝ?pc?s·?vÀ?y?|c?ƒ?ƒ?…¢?†?„÷?€‚?yÀ?rl?m¤?l®?j{?mß?r?uÍ?yR?}¤?‡?„ü?ˆä?‰ÿ?ˆS?‚®?yt?p$?j\?há?h•?lV?q ?u?yY?~f?‚Ç?†V?Љ?ŒŠ?Šä?ƒ³?yP?n¿?hG?f…?h?l?pï?uC?yŸ?~??‚A?†‡?Ч?Œ?Šë?ƒÆ?yª?nÖ?h?f’?jä?ms?qy?uè?z&?}’??…8?ˆœ?ŠX?ˆœ?‚†?y•?pF?iñ?há?n?p5?sO?vŸ?yñ?|V??‚ª?…N?†¿?…(?€Q?y“?rª?m`?l?q¨?sE?uî?x ?yv?{?}K?—?b?‚†?™?~`?yL?tò?q‡?p„?q ?sb?uø?x?y€?zï?}@??¬?‚Å?o?~?yy?tò?qÝ?pž?u ?ví?xR?yx?yg?y"?z¹?|+?~ ?~Á?}¿?|5?y’?vä?u?tz?wó?y›?z?zƒ?y?wÍ?x°?yÉ?{?}Ë?‚‰?ˆ?Œ?’$?‘§?†?z ?h¶?e¹?dÚ?aò?gä?nw?t{?yƒ?=?„¢?‹'?‘V?™?]ë?gO?k{?p€?u*?y¨?}ë?‚ä?‡d?‹´?=?ŽP?…¢?yg?lv?dû?db?l„?o¦?s>?vn?y?|š?€?ƒS?†_?ˆF?†Í?P?y„?q~?l?k0?m’?p?sY?v’?y´?|k?Ã?‚w?…T?†â?…{?€“?yØ?r?mÏ?lr?rv?sÒ?uè?wì?yT?zÖ?}4?~ä?€é??€4?}²?yÒ?u=?rÿ?q•?u‡?v•?wÉ?x­?y?y¸?{«?|™?}—?~_?}?|6?y™?w.?uZ?tò?vã?wÔ?y6?y?x«?y;?z‚?{6?|&?|í?|¨?{n?y‡?x?v|?vŽ?vÏ?w”?xç?yi?y?y?z)?{…?|H?|Ï?|x?{’?yè?wÐ?v£?v|?uB?vw?wù?y ?y ?yÆ?{?|´?}å?~g?}œ?{ò?yÞ?vÕ?u ?tï?r`?tA?v8?x?y?zé?|ª??€Ú?{?€C?}i?y˜?u„?rÙ?qÙ?n?pÏ?s?vÈ?y?|?Z?‚Ó?… ?†?…V?€¶?y…?r‹?m?l?g{?l?pO?u?y(?}o?‚‚?ˆ/?‹É?’1?‘ ?‡#?y”?hÕ?e?dª?_??f ?m)?s¤?yO???…²?6?”É?Ÿ ?Ÿ>?´?"ä|?·±?Wì?Yj?V?a+?j›?r¸?yn?€l?ˆ^?’(?œ’?­Ó?üÇ?9Œ&?BïÝ?6?#?ã½?K°?P“?^)?i?q½?yI?R?‰í?•K?¢‡?·X?3,Å?LÄï?L™¢?IVâ?Ã$?A`?PY?^?i?q½?y‡?e?Š?•?¢?·…?32“?LÏ?Lœ2?IŽŸ?ÅN?A?V ?a?jw?rj?yÙ?€¸?ˆŸ?‘£?œH?­ö?!¸?:ë¾?DŸ—?7BY?ê??K?^ä?f ?m(?s?y¡?²?…ñ?Œ§?“â?šî?¤>?i/?n#?"(?X—?Xx?fÝ?k³?pl?tý?y°?~?‚þ?‡L?‹´?Žð?û?† ?y?l^?e:?d?jB?mž?q?uu?y«?}h?º?…_?ˆX?Š­?‰L?ƒ5?yÔ?o§?iä?h—?pN?q©?t&?vå?y¡?|6?~Ó?€ó?‚ç?ƒ¯?‚S?:?y¼?t(?p®?o?s°?t½?vX?w€?yu?{?|á?~@?C?é?$?}/?y_?vK?sü?sV?u?v ?wX?x?y;?zx?{è?|Ò?}§?~W?}í?{þ?y1?w??uâ?u%?tÅ?u“?w'?x—?yX?z?{Ê?}+?}¼?~Z?}›?|?yá?wM?v(?tÇ?sK?t˜?vh?wý?y#?z³?|Õ?~¥?Ÿ?à?~í?|Â?y·?vv?tR?s&?oï?r?tÅ?w7?yX?{Ž?~~?F?‚÷?ƒ¤?‚+?~·?y­?t8?pÒ?oŽ?j¢?n ?qÑ?uî?yd?|è?%?…V?ˆ§?Š^?ˆë?‚×?y,?pl?iò?hå?aÿ?h‰?n‘?sû?y7?~œ?„ ?‹Q?‘?šÇ?˜þ?Ž»?x»?d?]?]?VJ?am?j­?rN?y?€h?ˆ³?’ ?œ?®Z?û¦?9‰½?Bóâ?6>Ô?ãË?KÙ?II?Z©?g@?q?y=?µ?‹û?˜¦?¨Ô?ËÑ?I¬j?Lïý?L¦&?LV?æ‚?4,?@?V2?e#?p9?y_?‚€?ä?œÈ?±q?&œ8?Mð?M«?L£›?L*ð?K ?ºn??õ?Uý?dý?ph?yÿ?‚¥?‰?œ¢?±$?'²-?Mm?Më?L¤‰?L+—?KІ?»è?Hõ?Z;?fô?q-?yú?‚&?‹‹?˜)?¨?Ìß?J33?LîZ?L E?LQm?§Ö???V ?a ?j??ru?y—??ˆ8?‘½?œe?¨,?a;?L¡U?LKM?¶Y?F«?Jƒ?ay?gö?nB?t?y??… ?‹?Ñ?–?–Û?Hõ?lô?j?\„?\þ?h%?lA?pg?u?yš?~$?‚b?‡=?Š´?Œ°?‹3?„€?yý?nê?g‘?eõ?n¶?p ?sy?vh?y†?}??‚[?„Q?„µ?ƒy?Ð?y›?s´?oN?mÌ?r‹?sÑ?u?w?yN?{Á?}‘?B?€?€~?°?}$?yï?v‹?s?r5?t]?uf?v%?w’?yl?{2?|d?}ä??~¿?~e?|?yW?vµ?u™?t9?sÇ?u#?vS?x?y.?zå?|K?~/?~§?~á?~(?|D?z7?vè?u®?sá?r?s·?uË?wo?y]?{P?}‰?ƒ?€??€ï?ø?|À?y–?v?sÚ?rL?n¾?q?sü?v™?yª?|.?N?‚,?„ ?„Ý?ƒt?š?yî?s§?o:?mã?h©?lá?p´?u‚?yÝ?}}?‚ ?†–?Šf?Œ¤?Š÷?„7?y?o#?h?f&?^ð?f]?m ?sx?y•?F?…ä?L?”?r*?t>?u?v`?wý?y?{?|u?~?~ñ?~ñ?~¡?{y?y?wA?uW?t?sú?tÛ?v]?xc?yB?zÎ?|J?~?~³??~H?|$?z?vÞ?u5?t?r\?s°?u”?w¶?y¥?{G?}??d?€5?€â?€?|õ?y@?uÿ?s³?rr?nÎ?q?sé?vÉ?yÅ?|?w?‚)?ƒ¼?„«?ƒœ?ª?yã?ss?o@?n?hª?lá?pè?ue?yÛ?}¸?‚u?†ˆ?ŠD?ŒA?Šô?„?yf?o?gô?f?^ý?f@?lû?s½?y¹?~?†?Œ÷?”>?Ÿ^?y?“Q?x?^Ð?Wœ?XI?PE?^?i?qÊ?yŽ??Š/?•"?¡?¹=?31í?LË?L›Á?I¤?Åç?A??9?V?dü?pF?yq?‚Ä?ß??°š?'±á?Mª?M?L¥R?L0Ò?KÑ„?¼˜?2¼?PÆ?b9?o'?y›?ƒƒ?N?¡¿?»¸?Lû¿?MMë?M£p?L¼ð?K•9?KÆw?‡?3?PÒ?b4?o1?yq?ƒ‹?i?¡t?»‚?MÌ?MPg?M¬ç?L¼K?K‘?KÆÆ?†A??\?Uæ?dÅ?oÚ?yP?‚Ë?ê?œR?°Ì?)¯Õ?M3?Mß?L£™?L(â?Kõ¿?½w?O¥?]Ë?hã?q;?yc?j?Š!?”Û?¢©?ÖQ?LÎ4?LÑ?L¢o?L-ü?žx?J;?^?eî?lø?sD?yŸ?x?…Ñ?6?•?›Ä?\C?ˆV?rN?]k?Z±?Wr?j?m·?qì?uÇ?y¦?}k?+?…?ˆÊ?Šš?ˆè?ƒ ?yè?p%?i­?h9?oÑ?q?t®?vø?y‚?|A?~Å?b?‚÷?ƒh?‚?~ù?yì?t?pz?o§?s{?tD?vL?x/?y¬?{(?} ?~?W?k?~É?|‹?yÌ?vÑ?t ?s?tí?u§?wA?xØ?yh?z„?|?} ?}ª?~B?}ç?{9?y>?wÆ?uÃ?tÛ?tµ?uÊ?wS?x˜?y{?zŽ?{¼?}?}í?~?}?{¯?y|?w5?uÆ?u?sœ?tŠ?vZ?x?yu?{?|’?~L?f?×?U?|—?ym?vm?t ?sq?p?r ?t°?w@?yŽ?{á?~¼??‚~?ƒ1?‚f?~Æ?y¶?tH?p§?o’?j¥?nu?q¨?uà?yÖ?}?f?…?ˆž?‰ÿ?ˆ{?‚²?y–?p?j.?h±?bQ?hl?nC?t2?yœ??„{?ŠÏ?‘?™[?˜;?¶?x¦?bÖ?],?],?VL?a?j½?rT?yµ?€¹?ˆ?‘—??­[?!Ë?:í®?Dš»?7D?ê[?KF?I ?ZK?fï?põ?yŒ?å?ŒR?˜›?©f?Ë™?J3?Lð?L ©?LUa?¨4??v??Þ?U¨?dº?p?y}?‚–?ï?œð?±ö?(‹?Mé?M9?L¥m?L,?KÅ&?»Á??z?UÕ?dÍ?oü?yp?‚»?É?œ„?°Ê?)®?M|?M?L¤A?L%ˆ?Kõz?½j?Hß?Z-?fÿ?pÈ?y?‚V?‹Ñ?˜?¨À?Ïå?J È?Lí?L¢¡?LT–?iä?Jº?V'?aJ?j0?r>?yK?€·?ˆm?‘ä?œ»?¨ì?Ú?L²?€‡?X…?Jþ?a£?h ?n:?t?yÒ?~®?„Ž?‹?‘£?–†?™?P/?t$?n?\œ?\T?mQ?p7?sá?w?y}?|½?À?‚s?…ƒ?†Þ?…*?€ª?yæ?ry?m?kç?r*?sÀ?vd?xk?yd?{K?|ü?~ô??(?€/?}¡?yï?up?rt?qE?u~?vE?wË?y?y’?zˆ?{H?|(?}î?~?}X?{÷?y»?w?u?t›?w ?wW?xö?yŽ?y^?yÛ?zy?{?|?|Ç?|H?zè?y‹?x ?vÞ?vT?vÿ?w¶?y?y”?yj?y›?zs?{S?|?|¼?|=?{ ?ye?wþ?v¸?v5?u©?v»?w¾?y?yZ?yö?{a?|£?}J?~ ?}Ï?{±?yk?w‡?us?t¤?rc?t%?v=?xj?y&?zÖ?} ?%?€£?€Þ?€C?}j?yv?u£?rö?q¿?m³?pÛ?sn?v¶?y€?|T?p?‚«?†?†?„¥?€g?y‚?rX?n?lÄ?ge?k°?p.?u?yÐ?~?‚L?‡Z?Œ“?ŽÃ?C?†;?yQ?l¨?em?d[?^Ð?f0?mC?s¼?yø?g?…—?ŒÃ?”?š‰?£O?i…?n?"8?X}?X¸?V?a?jŽ?r¼?y„?€“?ˆý?‘¿?P?¨ ?`?L  ?LKû?¶›?Fµ?J°?P7?]Ñ?i?qÝ?yG?T?Š5?•?¢Ü?ÓL?L½'?LÑ)?L¢‰?LP?›¦?IK?P7?^?hØ?q¢?yI?Š?‰ù?•?¢a?Õï?LÎ,?LÑx?L¡Ù?L.E?Ÿ?Iõ?V"?a[?jc?r`?yR?€Ì?ˆ«?‘—?œj?¨Ì?-?L²?LTÐ?€²?Xw?Jõ?_?fQ?lÚ?sÊ?yk?ƒ?…ß?Œc?”L?š»?œÃ?Ã?!·?n?WE?X2?g?k…?py?u`?y‰?}à?‚?‡;?ŒG?1?ÿ?ˆ°?~’?le?do?cÀ?qd?s“?v0?xr?yI?{f?}¢? ?‚?‚¶?-?~8?z?u0?p´?oä?uk?vò?x»?yÔ?yP?yè?zÂ?{à?}ò?~È?}S?|?z?và?tŒ?t?xK?y–?zu?zo?y‚?xœ?x’?y‚?{i?|7?{*?zÝ?y™?wè?w ?wB?yÀ?zŸ?{Ÿ?zÿ?yq?x?w’?x"?y„?zÖ?z?z?yT?x©?xo?xÙ?yõ?z»?{«?{A?yz?wÊ?w³?x+?yD?z½?zÅ?z?y4?xÁ?xg?x†?x…?yu?z¼?zŽ?yY?x0?x×?yÄ?z?{ó?{ð?z™?yG?xF?wA?w?u–?vÓ?x¬?y?y;?yv?z¥?|G?}Ï?~L?}Ñ?|1?y6?v´?uW?tÁ?q_?s¦?v?x$?y?{E?|ó?n?ø?‚D?A?~1?yR?t¡?qê?pì?lX?o¦?sA?v]?yj?|¸?à?ƒx?† ?‡ó?†„?L?yz?q ?l²?k(?fØ?kj?pn?u'?y³?}ò?‚Ò?‡?‹ã??e?†€?y¸?lY?eA?dŠ?a‡?h>?nQ?tC?y¦? ?…?м?‘S?–2?•™?Hl?ms?j?\Å?]?^H?f?m‡?sˆ?y??–?†G?/?”n?›?XË?ˆM?s>?Vø?Z¶?W?^T?f?mN?sˆ?yD?›?…ô? ?”«?šÇ?\I?ˆÝ?re?]I?Z“?Wâ?aÒ?h?nG?t8?yT?~î?„º?ŠÓ?‘5?•ï?™+?Oè?tY?n‡?\Ò?\¬?fì?kŽ?pP?uz?y.?}¼?‚Á?‡k?‹ü?Ž×?Ž?ˆŸ?~?lb?e;?c°?lp?oÅ?sU?vª?yZ?|h?€%?ƒ]?†‹?‡ÿ?†¹?Í?yÿ?qº?k…?j?tþ?x¥?{s?}?}]?{ú?y?u\?q«?n?j¿?hg?hz?jw?m‹?q)?w?z?}è?§?Å?~#?zË?w>?sæ?p½?mÑ?lc?l-?mp?p?s[?xñ?|??B? ?~á?{±?x¬?u÷?s`?qz?pl?p)?qR?sŒ?vP?z:?|?~?~§?~B?}Ä?{?yG?wÅ?vY?u—?tû?tð?ua?v¼?xZ?ye?yô?z7?z@?yí?yj?y·?yŸ?y0?ya?yÅ?yÓ?y ?yt?yN?y?y»?wH?u1?t“?tÉ?u>?w5?ym?{?‚D?‚¾?t?{]?y2?wÓ?vÔ?ve?vp?v©?w¼?yI?yÊ?y’?z?z±?y~?y3?yÑ?y?y,?yW?yV?yO?yÍ?y£?y^?y„?wÓ?s¬?o’?o¢?pŠ?pI?s@?w,?y«?{?|+?|Œ?|Z?|/?{@?y÷?w?r?ly?hp?if?læ?qô?vÎ?zC?}(?~ˆ?H? ?~~?}‰?{ ?xC?sÝ?nÒ?jû?jø?nô?sÏ?x•?|[??€Þ?‚!?‚j?T??{á?yü?uù?rr?pp?p`?rï?vÁ?zŠ?~?€ƒ?‚Ò?„‡?„\?‚Å?€O?}d?{9?x=?v$?tÖ?tÕ?v?x™?{_?~u?V?ƒÏ?…7?„º?ƒe??~Å?{Æ?y¹?x?wŒ?wZ?x?yˆ?{¿?}ù?€?‚±?ƒÌ?ƒÏ?‚i?€b?~&?{?z?yC?xÏ?xÞ?yM?z?zÝ?{é?}¯?2?÷?€?/?}z?| ?y´?yŸ?y´?y•?yƒ?y¬?yÓ?y«?y¨?yk?y?yH?yW?y¿?yq?y?x)?ym?yÔ?z?z?zÏ?|u?}¼?~?~?}?{ì?zš?z?xÆ?x?w®?wµ?x/?x‹?yv?zj?{J?{Ù?|W?|'?|?{{?z¨?yü?yŠ?yŒ?y~?yu?yd?y„?y¯?yÀ?yª?y•?yD?yZ?y›?y‘?y£?yˆ?zÈ?{o?{^?{D?zø?z¶?y½?x¥?x?w3?v¡?w?wG?w³?xp?yã?{§?|è?~?~r?}Ÿ?{ã?yù?x?v¯?u»?uf?uF?u–?v§?x?z¼?}ß?€ñ?‚»?‚ë?\?~‡?{Û?xö?v¬?uD?t¡?tX?tß?vF?x>?}v?‚p?ˆ?‹Ç?‹í?ˆ_?‚·?}Í?yÉ?vË?u*?t^?sò?tß?v·?y?ã?‡Y??•”?”»?æ?‡ù?€?{?wÕ?u½?t›?t³?uÝ?x?{?´?Š?J?Ž+?އ?Œ¼?ˆ®? ?{8?x\?v«?v?vE?w;?xŸ?{ä? ?ƒ·?„:?†]?… ?ˆB?†-?2?z?xƒ?wÈ?wš?wÌ?x?y‘?{&?yx?y¢?z[?|j?}?|é?|?z¨?xÜ?yj?y˜?y?yþ?yû?yæ?y¾?t6?oú?oy?qT?o°?o[?p¼?v?wì?zC?{M?{¦?{5?zÍ?z?wö?q°?iî?eB?cë?fi?b²?gÈ?oË?wT?z?{×?|™?|œ?{?yÖ?w6?rÀ?jË?c"?Y˜?WR?^d?iš?që?x ?{??|Ù?}Ë?}ï?}?{?w×?u?p}?j¬?gH?g¶?kƒ?pø?v?y§?|2?~?~ê?~¸?~?|.?yR?wx?tK?qe?p?p€?rY?tÔ?wù?z½?|í?~‚?X??}Ë?|l?zY?xð?w ?u2?t?t±?v ?wŸ?y?{?|u?}w?}ì?}ú?}]?|?z³?z?x³?x ?w§?wv?x?x¿?y’?z‡?{?{w?|?|r?|9?{`?z˜?yÏ?y·?yª?ya?y?xø?y;?yx?yÄ?yÑ?yf?y ?y|?y„?yl?y¤?y•?zÁ?{??{Y?{m?zá?zM?y?x°?x?wo?w?vh?vÔ?wÛ?x°?yÝ?{Œ?}#?~g?~T?}›?|"?z ?x?vâ?uæ?u?uf?u÷?ví?x7?zü?~??‚¹?ƒ ?Ž?~§?{¦?x?v?uQ?ta?ts?tÓ?vR?xz?|/?€@?„”?‡Ì?ˆ ?„X?ç?{Î?x$?ub?sÜ?rò?r²?sƒ?u|?x‘?}’?ƒ”?Šï?l?+?ŠÕ?ƒ¾?}Ô?y‘?v¨?tŠ?sa?sŠ?tn?vn?y]?!?…ú?‹È?¹?×?‹—?…\?~Ì?zG?w”?uÿ?u?u ?v2?w«?z6?~Ÿ?ƒ”?„[?†i?‡?‰µ?‡¸?`?y¹?x?wg?w??w?wÜ?x¢?z¹?yr?y¦?z8?}M?|­?}8?{Ô?zÂ?y!?y-?yg?y—?y ?yÂ?y¶?yÃ?tÇ?p?n³?p·?oö?mÛ?pÏ?v¬?xÑ?z˜?{œ?|4?{º?{(?z|?xe?t*?m|?g½?f¦?eM?e†?jˆ?r?x\?{??}?}—?}›?}?{Y?x¶?u?nà?h$?^È?]q?cµ?lÿ?sÐ?y×?||?~N?~ô?@?~ž?|ç?y’?w?r£?mï?j’?kŽ?nó?sJ?w¶?{'?}«?8?€N?€A??}e?zŠ?xƒ?u¢?rç?qt?qÐ?sÍ?v·?y ?|[?~u?÷?€Ñ?€·?U?}`?{p?y¹?wþ?vN?tò?uT?vÓ?xp?z?|?}Æ? ?‹?]?~¨?}o?{¨?zZ?yM?x]?wÈ?wÀ?x\?y.?z?{?{ï?|Ô?}@?}?|’?{ê?{?y¶?yq?yq?yl?ye?y$?y4?yL?y?y§?yR?y;?yk?yŽ?yT?y?y!?zO?zÝ?{?{,?zŽ?yˆ?xÂ?wè?w ?v6?uû?v?vÈ?wX?x(?y?zæ?||?}þ?}›?|ò?{t?y?w?u?t#?s’?sà?t‚?u§?w??yØ?|¿?­?B?Q?À?}!?zI?wc?u??sƒ?r³?rŽ?sL?uR?w{?y*?|Ê?€?‚9?‚*?Õ?|u?xÙ?uÌ?rÈ?pœ?oL?oV?p?rß?v?zM?~ê?ƒÃ?‡Ò?‡ù?ƒp?~±?zƒ?vã?sÛ?qØ?p„?p²?q¤?sš?v?{þ?€é?†{?‰ú?‰Õ?…Ô?€,?{Ã?wü?u?t?sD?s\?tv?uÙ?x?|4?ò?ƒ‘?‡ ?†?ˆ?ƒ?6?xÓ?wâ?w-?v|?v™?vø?wÑ?y–?y†?yª?y§?|È?}>?| ?{¡?z™?yF?yT?yu?yV?y;?y*?yj?yž?wm?sT?o§?pQ?p÷?p´?sü?y/?z?{?{Ë?|£?|¾?|(?{–?yÿ?wM?ra?mt?i`?g¬?j?p?u«?zn?|ö?~Á?y?Q?~Õ?}?z¶?x\?sÝ?o0?hæ?g÷?lC?q¢?vÊ?|?h?`?‚?‚&?2?9?|?yú?v]?r‘?o¹?pÈ?t?w“?zì?~?€?‚„?„(?„d?‚Î?€Z?}u?zï?x?u™?t?t·?vª?ym?|,?~Å?\?ƒ‡?„µ?„z?ƒ8?€Ð?}ò?{*?yr?wá?v­?vç?x$?yÂ?{Á?~-?€Œ?‚l?ƒ–?ƒJ?ë?€N?}À?{/?yå?xù?xG?xz?y?yä?{?|9?}?~Ë?±?Ú?~õ?}¿?|™?yõ?yš?y]?yj?y™?yÝ?yž?y›?yš?y?yf?yb?y¡?yß?yt?y?x2?y?y®?zH?z?yü?xØ?wÍ?vÆ?uŸ?t[?sx?s)?t?u2?v¥?w?yj?{??|J?|?zÊ?yT?w?tÃ?rw?p©?o|?ož?pé?rØ?tô?w¬?zÍ?}F?~¸?~.?|¹?z¡?wÝ?t¥?qÖ?o¾?nÚ?n•?oÍ?r>?tã?u:?xï?{Ã?}‚?}h?{Ž?xÕ?u_?qÀ?mÛ?j}?hÑ?hÅ?j?nL?q÷?vá?zÃ?}Ö?È?ø?}¡?z??vÌ?s?p+?m¾?l?l:?mÉ?p"?s6?xó?|g?ª?³?œ??{r?x`?u“?sF?qb?pw?p»?qÂ?s?zî?yJ?y«?yó?y¹?y‡?yc?ym?yk?y†?vÝ?tÏ?u}?u!?uò?x.?z]?zî?|F?}"?}®?}ë?}x?|‹?{]?z(?vì?t?qÇ?q?rû?u÷?y¶?|ÿ?5?€Ç?ç?‚?&?„?}?| ?x‚?tå?p|?pe?s?vM?z"?™?‚«?…?†?†¼?…3?‚p?*?}Š?z?w?tœ?ur?x ?{?~€?‚?…–?ˆÔ?Š{?Ši?ˆu?…)?ƒ?~|?zþ?xk?w6?wÆ?yÅ?|f?›?ƒ3?†×?ŠN?Œ¥?ŒX?Š9?†V?‚2?}õ?{q?yÌ?xÙ?y?zS?|W?~=??…w?ˆÒ?Šÿ?б?ˆl?…#?W?|k?zã?yÞ?y#?y+?yì?{-?|†?~+?€¶?ƒF?„K?„&?‚Á?€­?~Q?yÉ?yw?yY?yˆ?yÏ?y¨?y#?yU?yt?yo?yy?yŒ?yç?yü?yÇ?yÑ?v¼?x*?x´?yZ?yá?y4?wó?v¢?t–?rk?p?nØ?nÄ?p3?r?te?tÓ?w–?ym?z$?yï?x³?wB?t´?qg?mÍ?jY?h#?h#?j…?mõ?q¤?t¤?w´?zR?{»?{??yñ?w¬?t™?p­?ls?hå?fõ?f¯?hô?m?q?q¨?ud?x?y³?y•?wì?u)?qU?l¢?g!?aÀ?^„?^}?aî?g³?m#?s¸?wB?y?{?zù?y-?v¦?sR?o¯?kª?gú?f?f5?h%?kØ?oµ?uì?xš?zÆ?{Ò?{ˆ?z5?x+?uÉ?rü?p?n?m ?m:?n¢?pZ?s?wÙ?yn?zÞ?{C?z¥?yê?xí?w²?vK?tî?t?s–?sr?sÝ?tæ?v‹?ya?yN?yz?y€?yl?yf?y?yN?yh?y²?yâ?y“?y–?yW?yV?yS?{ ?y›?x¿?xK?xQ?xß?z ?{!?|O?}Þ?&???~ä?~?|¤?|ú?zÑ?x¹?w¹?w•?x®?z§?|í?—?‚?ƒý?…G?…–?„“?‚L?^?²?|g?ya?x?xa?yé?|?ƒ?ƒ¸?†û?ŠQ?ŒÛ?Œã?ŠŒ?†«?ƒ?°?}?zN?xª?y$?zä?}×?‚.?†³?æ?•‹?˜¥?˜m?”?‹¾?†?‚n?}é?{j?z$?zA?| ??ƒ ?ˆ›?‘|?˜É?Ã?ž,?˜—?Žê?ˆ ? ?}‚?{‰?zÔ?{?|‹?~¤?Ë?‡?Ÿ?—‘?ž?ž›?—ÿ?•?†˜?~N?|+?{ ?zr?z1?zÙ?|O?~d?m?†y?Œñ?‘Â?’\?…?†g?]?y–?y´?yŠ?y8?y{?yO?y)?y+?y?w°?wå?x{?xÀ?xª?y·?yÌ?tÝ?vÞ?wÎ?xƒ?xÍ?wñ?vÕ?t¥?q)?j+?cH?^÷?_½?c÷?lÈ?qÄ?qk?u?wj?xN?x'?w6?uD?q›?l?eg?]œ?WÉ?WÄ?]¿?em?kâ?pÄ?tð?w­?xð?xä?w”?t²?p¡?kM?d]?\Þ?XŒ?X­?]§?dÃ?k’?n ?rt?u`?võ?vø?u\?r!?mK?fê?^Ú?V[?PP?P²?Vv?^ú?gV?p˜?tM?vÜ?x7?wÖ?vB?s³?p ?k¶?fr?a)?^F?^B?a`?f?k´?sw?v;?x?xÝ?xœ?w?uõ?sª?pX?m?js?h÷?h¾?j­?m ?pe?v‚?wÁ?y-?y“?y*?x­?w„?v!?t±?s??rZ?q’?q?rh?sŠ?u&?y?y@?xò?yI?y¡?yu?y?yj?y‹?yl?y;?y_?yÀ?y{?y£?y’?{ÿ?zâ?yò?y’?y™?zO?{9?|h?~?Ÿ?€Ä?$??€ ??}Ž?N?}?{W?zM?z`?{‚?}?~?‚s?…†?ˆ-?‰¤?‰]?ˆ ?…G?‚0?‚Ñ?_?|¦?{o?{U?|~?.?‚ê?‡¯?ŒQ?‘f?”²?”œ?‘¦?ŒO?‡?…‰?€Ð?}¡?{ð?|?}t?€›?…ù?Œ"?˜¥?¡Ž?¦˜?¦Ó?Ÿì?”$?Œ,?†ç?€ñ?}Ë?|X?|€?~N??†…?‘5?ö?«?¶?·m?«Ù?šn?#?„Ý?ç?}Y?|l?|Œ?}¹?€e?…W?Ž?æ?7Ä?'C?‰?$ç?’µ?†?€¢?}œ?|?{%?zÿ?{ä?}r?€•?‡?tw>ó4>>Í:>Í)Ò>ðW?´n?†‰?yš?y|?y\?y[?yš?y®?yÅ?y’?x[?ì¼>ßàÞ>Ì©³>̪$>Üa?}¦?yÈ?rn?uz?vò?w ?wò?wE?uà?rl?k~?5>ù9é>Òˆ—>ÑàS>ö¶%? Ä?lµ?m÷?rå?u–?vÉ?v?u‹?rÜ?má?e?W?ï?Œ.?‹O?ìJ?VÑ?e(?lN?qù?u=?v?v«?u?r?lµ?dˆ?X’?J¤?@Ñ?A?K?X¹?dZ?j´?p]?s¨?u?u6?s‹?p:?j‹?b?V¦?I0????X?I?VQ?aë?n?r?t§?v?v ?tÛ?qõ?m‰?h?a?Z?V3?V@?Z±?ay?gø?q˜?t«?vÝ?wx?wK?v,?t9?q†?mâ?j?fí?dà?d„?f¥?iô?n6?uª?vø?wø?x–?x\?w¾?vÅ?uV?sº?r)?pñ?oÀ?o”?p†?rk?t;?yw?xÿ?xÏ?yG?yd?y ?xí?yE?yZ?y[?y ?y(?yØ?yç?z.?yÑ?|²?{·?z÷?z^?z*?z½?{È?}C?'?€®?‚?‚¦?‚|?¥?Ï?~,??~…?|â?|?|Q?},?~Õ?v?„Î?ˆ‘?‹Ï?Ä?·?‹{?‡û?„b?…j?_?~j?}!?}.?~†?.?…>?Šî?‘¨?˜i?œ‹?œ?˜>?‘`?е?ˆ¢?ƒ?à?}ì?}À?s?‚î?ˆé?‘D?¡e?­º?· ?¶ì?­V?œ”?‘D?Š·?ƒš?¶?~ ?}ä?®?ƒÂ?Š~?—?«k?†? —? u"?‚,?¨?–w?ˆÚ?‚ ?~Ÿ?}U?}‚?~Ï?‚?ˆ®?–¹?;}>Óï±>Íœü>Í–>ÒÉg?£ò?•‡?ƒ0?~â?|Á?{÷?|?|ä?~Ì?‚í?ŒÁ>ó>£>Í4,>Í“¦>Í>Í:Å>ÍKD?ʉ?y^?yF?y?yL?yì?z?y¹?y—?xñ>ßìê>Ì£9>̤Ç>Ì£>Ì >̰ì?xZ?p!?tQ?vJ?w?w ?v‹?tŒ?p?e">ù(>Ì >Ë´€>˳â>Ì?eÕ?b°?jI?q?t_?u§?uB?t;?pè?j}?]i?îÇ?bÜ>ÌÎþ>ÌX?.¸?DŽ?]0?h‹?ov?sz?u?tþ?rö?oÈ?i?]ˆ?K[?4?|S?{|?.)?JÓ?]7?h¤?o*?r”?t3?t?r¸?o?i?_L?P€?>‘?1„?29?>Ù?P1?^¬?ld?pÃ?sÕ?u ?u ?sí?q?l7?eÓ?]Ö?V-?Q?Q`?VS?]þ?eÔ?pÔ?t?v ?v»?vƒ?u??sC?p+?lY?h"?dV?a¨?aÿ?dÎ?h–?lþ?uC?vØ?w~?w÷?x?w†?vY?tú?s~?q¿?pt?oX?n¿?nì?ql?sr?yR?y?y=?y¬?yo?y!?y?yQ?xö?y?xÙ?y?yõ?z'?z>?yç?}?{³?{+?z™?zÉ?{4?|s?}È?`?p?ƒB?ƒ¼?ƒ?‚9?€d?~Î?‚ ?s?}£?|Í?}?}ù?½?‚f?†4?Š"??y?Î?Žq?‰Ñ?…g?†¸?‚{?2?~?~O??‚Z?†Ä??”â?œg?¡? -?›¨?”‰?ŒÌ?ŠÎ?„J?€¢?~—?~P?€?ƒó?Š©?”€?¦«?·/?Âó?µ?¶­?¢O?”…?0?„û?€ç??~¨?€t?„Ó?Œâ?œ&?µ‘? ›µ>ÍñP>Íãå? Y ??=?›µ?‹#?ƒ5??~'?~3?­?ƒ[?Šô?›æ?,)>Í’·>Îê>Íýˆ>Í—ä>Í…ä?ÙN?„Ö?Ç?|ó?|?|?|â?m?„?Ð>Í? >Ͳ>ίì>ζ&>Í‘]>Í?T?y’?yÌ?ys?yE?y¬?yÎ?yœ?y€?xó>̲>Ì¢>̬d>Ìá>Ì ó>Ì ­?xN?nO?sr?v+?w ?vç?v?s«?n¡?ad>Ò‘&>˸F>Êšì>ÊŒ>˸y>Ìž?e±?gj?oQ?sN?tï?tá?s®?oó?h7?X?Ê>̼>ËFT>ËBD>ÌV?Z£?R‹?ff?n2?rU?t/?tS?rb?n_?f×?Y?AS?{`?y?z]?{>?;Š?X?hl?nõ?rÀ?tv?tV?r×?o%?hß?^Ç?Oý?>Š?2]?2?>÷?P?^j?lz?p›?sË?u ?tå?sæ?qd?lá?f ?]ø?VR?Q•?QK?UÐ?]§?f"?pß?s·?uX?ve?v‘?u‚?s€?p•?lÉ?hq?d}?a…?aÿ?e4?hê?lË?u?v©?wt?x*?x$?wQ?v*?t»?sb?qÂ?pá?oª?nÎ?oe?q}?sX?y‡?y`?y]?yÂ?yƒ?y?}Ú?}ó??‚?†ì?t?• ?œc? Û? [?›ö?”«?Œ—?Šþ?„\?€o?~Ç?~¦?€j?ƒü?Šk?”e?§?¶Ò?»?Âh?¶{?¢y?”©??„¾?€á?~í?~Ä?€J?„s?ŒŸ?œ!?¶°? §>Íåí>ÍÜ? p±?<:?›Œ?‹^?ƒ¨??}û?}Þ?›?ƒK?‹.?›î?%r>Í•³>Íù;>ÎÒ>͘K>ÍeÙ?ÖÛ?„ñ?Ò?} ?{ì?|?|×?F?ƒþ? >Í1{>ÍŒo>αÜ>δÝ>ÍŒD>Íú?Å?y‰?y±?y©?y²?yˆ?y‘?y®?yg?yý>Ì´>Ì¡‘>ÌÊà>ÌÚ”>̨Ç>Ì¡õ?y‡?n{?s®?v@?vê?vÈ?v?s†?n`?a„>Ò!«>˵A>Ê—x>ʈu>˵ç>Ì@›?_{?gƒ?oS?sp?tÑ?tã?sƒ?o»?hE?XÌWò>ËD¨>ËAF>˵Ù?Y"?RÒ?f?n8?rM?tV?t‘?r¡?nP?f¬?Y?A ?{F?z?{?{o?;à?W³?j[?oò?s£?uS?u&?sä?p?jå?b ?V%?HÝ?? ???I ?V?aø?n;?r?tÉ?uû?uÖ?t¬?rF?n'?hS?a"?ZO?V;?Vd?Z;?a?hq?qµ?tx?v?wM?ww?v_?t¤?qÃ?n#?j'?fÖ?de?dH?g?jŸ?n ?u†?vé?wÝ?x‡?xB?wŸ?v­?u:?s?rb?q€?p.?oŸ?p”?rL?t?yJ?yn?y?yA?yT?y\?xþ?xò?y4?ys?y?y?yÎ?y=?yA?y?}f?|%?{+?z§?z¡?{?{ò?}>?~¸?€?Ö?‚½?‚­?å?€)?~?€à?~v?|Ì?{ø?|?|ë??Í?„å?ˆ??‹P??È?‹¤?ˆ@?„a?…?j?~b?}?}J?~~?(?…A?‹`?’/?˜D?œ1?œ ?—æ?‘m?Š‹?‰?‚ü?—?}è?}¯?§?‚»?ˆn?‘_? —?­ã?·?·V?­T?œå?‘W?Š•?ƒ.?¨?}Ê?}å??ƒ“?ŠZ?—ë?¬õ?‚? œ? y‚?~%?§?–¥?‰&?‚g?~š?};?}?~>?‚?‰?—?59>ÒçÓ>Í•#>Í“p>Ñï0?ñÄ?“†?ƒ‚??|Ì?{ê?{Û?|Œ?~*?‚?Œ•>ð‹m>Í8>Í‘ >Í—>Í:t>Í'ˆ?Ã`?yg?y³?yÄ?y­?y¡?yÆ?y¡?y7?x€>Ü–$>ÌŸ8>Ì Ù>Ì©ö>̨õ>Ì£?qä?p?tc?v–?wc?w ?v¤?t«?p2?fœ>÷F>Ì x>Ë­ƒ>Ë«ž>Ì~?–í?^G?iç?pû?t+?u¡?uÔ?t6?pÑ?j¨?]Æ?í†?žÿ>Ì|´>Ë·y?i°?6?\’?h*?nþ?s ?tá?uh?s`?o?hº?]¦?KW?.J?{ä?zò?)B?K@?\¸?m6?qè?uP?v÷?vé?uÆ?ré?n!?g\?_ ?V&?OÚ?P$?VÍ‘à>Íd"?ñÚ?š‘?Ž? ?}¿?|F?{h?{*?{ê?}#?€{?†?¸ë>ÍYà>Í->Íœ>Í'Å?ù?…&?y¹?yÿ?yÀ?y„?y‡?y½?y‚?yr?xü?†…>Ì¿…>Ì£N>Ì¢*>Ì¡?¼ß?tð?rc?u¦?w1?wð?wø?w;?uí?r|?l}?£ñ?ÉØ>Ìý=>ÌBo?šæ?V•?lo?n?rö?ul?v×?vï?u§?rÚ?m¦?eˆ?VÆ?DÏ?Á¦?¿d?9‚?WÆ?e?kô?q'?tÆ?v]?vz?tÙ?q–?lp?dl?XÛ?K?;Y?;U?K4?Xp?cõ?q)?t÷?wÒ?yš?yˆ?x‡?v?q²?l©?g`?aÒ?^8?^P?a½?fì?l5?s®?w ?y˜?zú?{?yƒ?v£?sy?oé?kû?h ?f?eö?g¤?k|?o§?v?u?vø?yº?}u?ƒ*?ˆà?‹ï?‹f?ˆ+?‚w?}q?y·?vÈ?u?t2?tf?tý?v¾?y˜?{þ?€‡?…?ˆ?wº?ua?q¡?lÄ?g?a»?^Â?^Ð?aß?fÛ?l€?mè?r|?u‡?vÝ?v¤?u?rL?mÎ?g1?^å?VY?P³?Pp?VH?^ý?gy?jÓ?p)?sg?tæ?u ?sf?oþ?j—?aÍ?V"?IR??©??Y?I(?VR?b/?h¿?nÜ?rM?sÿ?t~?rÙ?o?hˆ?^`?P ???2X?2…??"?P5?^·?h²?në?ry?t%?tO?r§?o ?hž?^’?P~?>ü?22?2x??$?PN?^†?jÆ?p?sF?tì?tô?s¤?p@?j˜?b ?Vb?HÕ?????I5?V#?b?m?r?u)?v€?v©?u‘?re?mâ?g?^¬?Uß?Oú?P)?V ?^C?fÐ?qŸ?u›?x ?yŸ?y¯?x?uI?q¾?lm?fÌ?a–?^Z?^n?aR?fä?l‰?w}?zÚ?}Ï?U?r?}¢?zu?vö?s‘?pG?n?l·?lQ?m8?oó?sª?zÛ?3?ƒ¶?‡u?‡Ê?ƒ‹?~È?zœ?vÊ?s«?r>?q ?pû?qÍ?sÆ?vè?}Á?ƒ×?Š©?6?…?Šü?ƒÖ?~?y‰?vB?tî?t ?s¹?t±?v§?yh?S?‡ç?¾?”Ã?”‚?Ý?‡Æ?€?zö?w¼?v+?uw?u8?uø?w§?zW?¶?‡€?ž?•0?”?¢?‡â?ù?zð?w±?u»?tò?ud?v&?w¶?zÇ?}Ï?ƒè?‹?J?×?Šà?ƒç?}º?ya?v/?tr?s¯?sÌ?t«?v„?y?zu?O?„?‡g?‡ž?ƒz?~Ë?zu?v…?sÊ?qö?pµ?pØ?r?t"?vÇ?vå?zÓ?}ƒ?I?Ô?}¡?zR?w#?su?p6?m™?l'?l~?n?pY?s4?s§?v÷?xÏ?zX?zÛ?y‘?vê?sË?o×?kŽ?gû?f,?f4?h5?k“?og?p&?sÞ?vo?wÖ?wç?v¥?t ?pf?l?f ?a^?^r?^%?aR?f~?kR?mÏ?qâ?tÄ?vR?uË?tZ?q‹?mÉ?h`?a=?ZÐ?VÌ?Vm?Zt?aŽ?h??l‡?põ?sš?u?…¹?‹?M?Œ¶?Š­?…^?~Æ?z@?w¿?v?uà?u©?uõ?w´?z??‰?Œ8?g?Ž?Œ»?ˆê??{E?x¼?w’?vÑ?v–?w?eP?it?mˆ?q´?t`?vy?w/?w?uÌ?t ?q|?nO?j³?fü?dÜ?dÒ?fï?j{?n_?sw?v?wì?xÕ?x®?w?v?su?pu?mð?k?hö?h…?iú?lÿ?pb?uœ?x)?z?{V?{Z?z=?xŽ?v+?s´?q?nø?ml?lø?n?p>?s?y?zü?}™?~ˆ?~ˆ?~'?{ï?yË?wÜ?v»?uè?ug?u?u?vt?w?{¢?~Î?‚ÿ?‚Þ?‚‚?ƒ–?ö?{¹?y‹?wû?vÜ?v‚?v0?vŒ?wâ?y„?~2?‚Ä?„?ƒ“?ƒá?„›?‚Ö?}Æ?z?x‹?w†?wS?w†?wñ?xÈ?zÉ?6?‚³?ƒ?†£?‡?†›?…æ?€É?zš?x¦?w×?w‰?wÖ?x¸?y˜?{ ??ƒI?ƒE?†?†ñ?‡V?†Z?€ç?z=?x˜?wû?wœ?wÐ?xœ?yÄ?{l?}Á?‚ÿ?„¹?†¶?…Ö?ˆ2?‡q?X?yÅ?x•?wî?w[?wp?x?yO?z¼?{‡?„?ƒU?†K?…ò?‡Þ?ƒÈ?~Õ?xü?w¤?vÝ?v¬?vÙ?w?wÅ?yS?y,?{“?~'?x???ø?|Ø?w‚?vS?uŸ?uˆ?u}?uÂ?v,?w•?wZ?y`?z‹?{O?zæ?z%?y?w{?v?tá?t?s¼?sÀ?tj?u?v6?v_?wð?xÇ?yŽ?y]?x›?wª?v=?t¹?sy?r‹?q”?qŽ?rx?s¯?u?uW?vû?w¾?x?xX?wÙ?w?u¡?sï?ru?qK?pO?p?p£?qê?s±?tç?v[?wJ?w¥?x ?w³?v?u(?s«?qð?py?o®?o ?oª?q$?s?u%?vm?wF?w³?wÌ?w?vc?u?s?r?po?oŠ?o ?of?q?s?uÈ?vì?wÏ?x?x?wÀ?vù?uä?t(?r¬?q?oô?oÓ?pm?qã?sÌ?v×?w²?xw?y?y'?x³?wÎ?vž?tÛ?s4?rM?qº?q|?r#?s_?u?w‰?x¾?z ?zÛ?{!?zq?y:?wä?uû?tß?sù?sª?s„?t.?tý?v?yÇ?y—?y7?y«?z)?y ?yM?yE?xô?xá?xí?y+?y?yK?y–?y¡?yŒ?yq?y??zj?z?y¡?yœ?yw?y1?y1?y?y?xí?y?y×?yÈ?y{?y”?z?z;?z?y³?yÕ?y ?yV?yo?y-?xí?y?yi?yk?y’?yâ?yÞ?yç?}˜?{‰?~ú?|¾?{Ù?y2?y?xÍ?xü?y%?yA?ya?y‹?y¹?yK?y¿?} ?{=?}Ñ?{§?zÚ?y ?y'?y?y2?yˆ?yÇ?y»?yë?y–?yá?yç?~’?|*?}v?{}?z·?y9?y¨?yÆ?yª?yœ?yö?yå?y¼?y¡?z?yN?|Ø?}{?{·?{ž?{"?y?y_?yÙ?yu?y_?y¹?yÈ?y£?ym?y|?y?|?zm?{$?{x?z¿?y?yM?yµ?yP?y\?y˜?yÜ?y¦?y‰?y»?z?z ?yÀ?y†?y“?y ?xË?y)?y§?y_?y&?ys?y´?yd?yF?yq?yœ?yÍ?yÈ?yç?y¦?yB?y%?yC?yŠ?y‚?yu?y[?yN?y?y_?yT?y2?y_?yé?yù?y—?y‘?yY?yB?yE?yO?yf?y›?yy?y0?y\?yA?y#?yW?yª?yã?yË?y­?yv?y ?xâ?y?y²?y¼?y]?y?y.?yn?yA?y:?yK?yw?y‚?yv?yz?y4?x²?xÍ?yÔ?y¥?y7?y!?y;?yg?y?y5?yt?y`?y?yG?y†?y!?xî?y2?y6?y3?y`?ya?y6?yM?yM?y€?yW?y]?yX?yY?y‡?yT?y?yN?yW?y•?y§?yˆ?yv?yu?yn?y?y{?y‘?yz?y&?y/?y9?y'?y?yr?yŠ?y¶?yÅ?yí?wÔ?u”?t—?t^?tÇ?w?xÿ?zÝ?|O?} ?}À?}ú?}Å?}?{É?w›?t ?pX?ob?oÇ?o}?rÊ?vÉ?y[?{!?{Õ?|…?|Ð?|U?{]?z ?u?oí?nÔ?nö?o?nR?oa?t‹?xh?zY?{)?{¢?{•?{L?zi?x\?tš?oÍ?nú?n_?q?pD?qF?u)?w’?yÜ?zÈ?{G?{W?zš?yy?wð?t“?oÓ?nÖ?n²?qˆ?o—?p«?u8?wÙ?yŸ?z—?{B?zú?za?yJ?wõ?u$?oñ?n?nC?qú?n«?pP?u?x¾?yô?zæ?{Ï?{€?z¼?y¹?xd?wž?sv?p?qí?p­?q¸?tk?w¾?yÇ?{ ?|#?|Î?|?|C?{=?yæ?yù?w¢?uš?u?u"?uâ?wY?yþ?{?|]?}™?}÷?}Ñ?}Ý?}?{£?{†?yÉ?xÈ?xK?xo?xï?y°?{#?|‚?}×?~Ð?\?\?~ô?~O?|Û?|?{!?zL?y¬?yÀ?zc?{”?|Ê?~ ?`?€E?€Ö?€ü?€~?C?}ó?}Z?|?{h?z‡?zc?{.?|e?}¿?5?€²?Ü?‚u?‚`?Ê?€œ?~ê?}²?|­?{»?z¨?z­?{s?|Ô?~I?Í?Ž?ƒ:?ƒ¨?ƒ ?‚"?%?m?}˜?|g?{?z³?zü?{Ÿ?|Ì?~?•?c?ƒ%?ƒ×?‚÷?‚,? ?I?}#?{õ?{?zw?zº?{L?|?}Q?~Í?€m?í?‚Í?‚¨?‚ ?€‹?~Á?|Z?{O?zw?z?z?z£?{[?|^?}¿? ?€(?€Ý?"?€¬?D?}È?{5?z?y?x–?xD?xÓ?yÓ?{?|„?}¹?~Y?-?;?#?~B?|È?za?w‘?tx?r?q“?sÞ?w?z?}r?–?J?‚g?‚l?¸?ç?}X?w?rç?mr?ih?iE?l¥?q÷?vÌ?zÃ?|É?~˜?‰?”?~î?}?z}?sû?mñ?h?e¢?e>?f½?lÜ?sÃ?xy?zÑ?|™?}­?}å?}2?{B?x\?qŒ?j0?g=?fv?aô?c?f§?pÈ?w“?z­?|a?|ê?|ð?|O?zq?w7?q¢?j6?f®?eØ?d^?b(?gn?q9?w¸?zÔ?|o?|©?|Œ?{·?z%?w?tG?m{?g ?f²?eÿ?d?j?sÌ?y?{T?|ž?}?}?|·?{??x¬?wk?r_?mP?iÂ?gÅ?jV?pb?vm?{?} ?~K?~ú?E?~v?}?z¬?z®?wZ?tY?qô?q©?r‡?u9?yˆ?}L?÷?\?‚?‚+? ?R?}8?}?{?xø?wí?wa?x?zš?}_?€C?‚Û?„©?…ö?†N?„¹?‚F?€#?û?}?{©?z‰?zI?{n?}Q?ì?‚­?…Á?ˆ8?‰Ú?Š?ˆ?…·?‚¦?l??};?|D?{Û?|¾?~£?+?„Á?ˆˆ?‹˜?æ?ÿ?‹ß?ˆ—?„½?‚A?š?}Ø?|Ã?|–?}f?Z?‚*?…Ü?Š??W?À?ŽC?Šm?†=?‚€?Â?}Ã?|À?|Ê?~ ?Ä?‚x?…é?Š ?¢?|?ô?Ž’?ŠŒ?†I?’?~t?|•?{ì?|*?}??„?„È?ˆ¡?‹É?û?Ž?‹Ô?ˆh?„È?“?|÷?{X?z8?z”?{e?|ò?z?‚‡?…˜?ˆO?‰æ?‰Â?ˆ:?…˜?‚}?}g?{?xò?w®?wr?x_?zs?}?É?‚/?„‰?† ?…É?„»?‚„?ä?{é?xJ?ud?s»?sÍ?uk?x­?|T?˜?‚Á?…0?††?†w?…?ƒ?œ?x~?t$?ox?kÅ?kß?oœ?t?Œ?j?}Ë?{?y?y§?{9?}ñ?¬?†©?Œi?‘ž?”Ž?”Ä?‘R?‹ä?†?~é?{¢?y?w’?wÇ?yB?{Å?~½?‚d?†€?ŠŽ??y?Šš?†‹?‚^?{È?x­?vB?t¶?t¤?vW?xÒ?{Œ?~l?H?ƒ½?…;?…y?ƒÔ?C?~]?yS?vM?s˜?qÁ?q·?s¡?v?ym?{È?~C?€?%?€ý?ß?}ò?{¾?wü?t ?qæ?p?pA?rg?u7?wà?z-?|”?~`?!? ?~j?|–?zY?x?t“?q_?oâ?py?ry?uA?x?zQ?|N?}¹?~î?7?~?|ä?z™?y ?uÙ?r“?q(?qÀ?sè?vo?yG?|?~?W?€?€Á?Ë?}î?{‚?{`?x ?u1?s¼?t‹?v¼?xá?{×?~ã?x?ƒ…?„Þ?„‰?ƒD?€é?~T?~?{?xu?w#?w}?yO?| ?Íô>ÍÞ‡? YŸ???›?Œë?… ?€ß? ??€º?„Ì?Œ¥?›â?¶~? P>Íêp>ÍÚ)? r9?Óìh>Í“î>͘X>ÒÎó?¤H?•é?‹{?ƒ¶?´?~0?~G?u?ƒk?Šç?œ?, >͘c>ÍýO>ÍþÊ>̓>͆•?ÙT?‹^?ƒ?v?~!?~™?Ù?ƒ_?Šê?›ä?&S>Í›µ>Îo>Íÿ—>Í–:>ÍeÇ?Ö?‰4?‚Q?~Î?}?}²??‚ ?ˆ‘?•Ì?5¬>ÒãG>Í™>͘Œ>Ñì?ò(?“M?…~?€?}–?|n?|d?}Ï?€?…-?±?’›?­ò>Í”>Íe?ò'?š¿?ŽL?º?}ý?{Ò?zØ?z·?{Ä?}Ñ?’?†¯?Û?–g?à£?Ù ?” ?Ž@?‡?|D?zç?yÀ?yi?yX?z?z¼?|™?~õ??‚Ã?ƒä?„ ?ƒ6??~b?z¥?y›?xï?xr?xW?y%?yß?{F?|°?}¶?~ã?z?Y?~è?}†?|=?yá?xõ?x?w¶?w?x9?xé?yì?{'?{¼?|H?|>?|Î?} ?|'?{?yz?xÕ?w£?w?wH?x?x~?y?zë?{S?{Ì?{Ö?{ú?{r?{(?z=?yU?xæ?x?w|?wR?x?x˜?yœ?z†?{?{›?{Â?|?{³?{:?zS?yÃ?xâ?xF?w·?wñ?xŽ?yC?z ?zÁ?{Ï?|Û?|×?|¸?|H?{Ï?zõ?zÉ?yÏ?y?xh?x»?yZ?z*?{?|&?}`?~©??Í?ó7¥>Í9Á>Í%¥>ðW?´?†±?ƒ ?~?|¢?{Ü?{á?|»?~²?ƒ?Œp>ó>H>Í7 >Í­>Í…Õ>Í7å>ÍKe?Ë ?„°?‚?|Ó?{í?{å?|º?T?„?Þ>Í<>͉ >Σy>γÈ>Íœp>ÍF?Ì?„â?€?})?{æ?{¥?|v?6?„?Â>Í*ª>Í®>Ω}>ηã>Í—Ì>Í#?ß?ƒK?%?} ?{á?{‚?|?~U?‚”?Œ[>ð‡>Í4j>Í‹V>Í“v>Í9v>Í(K?Ã`?€}?}_?{ú?{*?{!?{z?}^?€N?†G?¸þ>Í[7>Í>Í>Í'•? ?…?~T?|?zÕ?zQ?zu?zå?{ö?~ ?¹?‡?Ѱ?™œ?•?ÆS?…†?§?yL?y€?yÛ?y¸?yŸ?yr?y0?xå?y?yl?yD?yG?y3?yY?y†?y]?y`?yW?yÑ?y¤?yx?yk?y|?y?yV?y‹?y†?y\?y:?xõ?y.?y©?yk?y ?y?yM?y…?y¬?y¸?yø?yÅ?yÙ?y¬?y6?y9?yY?y€?yu?yßßÎ>̧8>̧>Ü[4?}?y+?y]?yƒ?y5?yßè£>ÌŸ´>Ì£P>ÌŸì>Ì£à>̰±?x?yz?y?yM?y ?ye?y”?yg?yœ?x¥>Ì®Ü>̤B>Ì»4>ÌÔø>Ìž>Ì Z?xÐ?yq?yu?y‚?y?yB?yW?yy?yƒ?yq>̯‘>Ì›k>ÌÈ>ÌÍ–>Ì£9>Ì¡«?yþ?yœ?y±?y˜?ym?y"?yA?yT?yA?ye>Ü™:>Ì“[>̉>Ì œ>Ì ñ>Ì Á?q¹?yÓ?y±?y¹?yO?y$?y?y?yM?y§?‡±>̽“>ÌžÙ>Ì È>Ì ?¼ë?u?y†?y¶?y°?yv?yb?yT?y ?y!?y¤?yã?„ç?~,?€Â?v?tæ?y ?v_?w ?xÉ?y–?y»?y/?x:?vÊ?t§?rC?p ?nš?nš?oú?r?tl?x!?y ?yÈ?z„?z¬?yõ?y?x?vï?um?t?se?sØ?tm?u_?v¾?xø?yÎ?z†?{m?{“?zÒ?z@?y??xK?w$?vk?vG?v!?vH?w?wþ?yk?z?zï?{Ê?{°?{ ?zÄ?y½?xu?wÅ?w ?vË?vó?wx?wä?x»?y-?z?{?{H?{?{A?z²?y¡?xÛ?xF?w?w?vŽ?vå?wv?xq?y8?z?zÙ?{6?{?zÆ?z?y?x1?w%?vc?v?v1?vŸ?v÷?wö?x>?yI?z?z‡?zP?z?y\?x6?vø?u}?tE?s`?s4?t ?up?vï?vÞ?xt?y3?y®?y¨?y?wö?vŠ?tã?r ?p#?nµ?næ?oõ?r0?tâ?tä?w?xZ?xÏ?x±?xA?vµ?t‹?qÛ?iU?d?^Î?_o?c¶?l¤?qŽ?r€?u?w!?w«?wÑ?w…?uÔ?rF?lÆ?3B>ù3)>ÒŠ->ÑÝÁ>ö¹{? Z?ls?p%?tF?v:?vê?w?v&?t˜?p)?fÑ>ù&ß>Ì/>˵~>˱l>Ìä?dõ?b?n{?s{?uü?vè?vÉ?uÜ?s{?n¨?b§>Ò–>˲¦>Ê–>ʾ>Ë­»>Ì›–?dÖ?n[?s}?v?vã?vá?v?so?ny?aƒ>Ò$Ì>˶R>ʘˆ>ʉ>Ë´‘>Ì@-?_8?oó?t1?vm?w\?w#?v­?t“?pL?f‚>÷Ø>Ì >˰í>Ë«n>̵?–²?^?r\?uÛ?w(?x-?x?wa?u«?r²?l»?£ö?Éi>Ìý>ÌB{?›?U¾?kÎ?tn?v¢?wå?x¸?xë?x ?vÝ?tÂ?qa?l?a½?qš?e¶?]Ô?kù?pÜ?tE?vÆ?x¼?yý?yÓ?y%?w¹?t«?qc?m¨?iä?g€?g¡?ið?mM?pû?v¸?x÷?z¬?|?{ÿ?{ ?yB?vÓ?tà?r ?pµ?o[?o`?pÆ?rÁ?t^?xß?zß?|^?}D?}*?|‹?zÈ?xp?w?u›?t*?s–?s„?sÑ?uR?vÚ?z ?{¼?}r?~'?~?}u?{Ô?yÍ?x4?v¹?u?tÊ?u?u[?vu?wí?yú?{å?}’?~?~}?}‹?|C?z–?xª?vä?u’?tï?tÙ?u¢?v•?w¯?y(?{Y?|¼?}Á?}¦?|”?{c?yº?wž?uÓ?tM?sg?sg?t€?ué?w?w¢?y§?{4?|_?{÷?zì?yP?wq?u–?sC?q ?oÄ?o…?pß?s-?u[?u0?w–?y]?z3?yÝ?y?w%?t´?r?n6?j_?h?h?jÌÎ`>Ìb?/?D¨?\¬?g?o?sx?uC?u ?sÊ?p?h8?Wô?Š>Ì»¢>ËDê>ËB%>ÌJ?[!?R‡?g;?oi?st?u?tò?sÂ?oò?hu?X?Œ5>ÌWÏ>ËF >ËA>˵ƒ?X\?RN?i†?p¬?t?u§?uá?tX?q3?j¼?]‰?í+?Ÿ>Ì|¸>˶‹?iq?5¹?\9?m ?r¦?uQ?vÝ?vü?uÊ?s?r®?tY?tP?r“?n¥?f½?XN?@6?{j?y—?z%?{?;5?Wð?f,?n)?rž?t„?t~?r‘?nŒ?f?XØ?@G?zO?y?z¾?zÖ?;ž?X?h¼?ow?s\?u4?u4?sW?of?h¢?]p?J§?-Ç?{ ?zØ?)?K=?]?lT?q?u?v¶?vw?t±?q’?lœ?dN?Xb?J´?;K?;{?K?Y ?d?ps?t³?w?xØ?x¨?w0?t·?pÚ?k?cî?\?Wv?W×?]1?d@?kDy——Dy—™Dy——Dy—™Dy—™Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy—™Dy——Dy—šDy—šDy—™Dy——Dy——Dy—˜Dy—™Dy——Dy—˜Dy—›Dy—™Dy—™Dy—˜Dy—šDy—˜Dy—˜Dy—™Dy—™Dy——Dy—–Dy——Dy—˜Dy—šDy—Dy—Dy—›Dy—˜Dy—™Dy—™Dy—™Dy——Dy—–Dy——Dy—˜Dy——Dy—˜Dy—šDy—™Dy—Dy— Dy—ŸDy— Dy—˜Dy——Dy—šDy—˜Dy——Dy—–Dy——Dy——Dy—™Dy—›Dy—˜Dy—œDy— Dy—¢Dy—¡Dy—žDy—šDy—™Dy——Dy—˜Dy—™Dy—˜Dy—˜Dy—–Dy—˜Dy——Dy—™Dy—™Dy—œDy—ŸDy—ŸDy—šDy—˜Dy—˜Dy—˜Dy—™Dy—™Dy——Dy—™Dy—™Dy——Dy——Dy—šDy——Dy——Dy—™Dy—™Dy—›Dy—˜Dy——Dy—˜Dy—™Dy——Dy—˜Dy——Dy——Dy—™Dy—šDy—šDy——Dy—šDy—™Dy——Dy—˜Dy—˜Dy——Dy——Dy—šDy—™Dy—™Dy—˜Dy—˜Dy—™Dy—šDy——Dy——Dy—›Dy—™Dy—–Dy——Dy—˜Dy—˜Dy—™Dy—˜Dy—˜Dy—˜Dy—™Dy—˜Dy—˜Dy—˜Dy—™Dy—˜Dy—˜Dy——Dy——Dy—–Dy—˜Dy—™Dy——Dy—šDy——Dy—›Dy—šDy——Dy—˜Dy—œDy—™Dy—˜Dy—–Dy—–Dy—™Dy—˜Dy—™Dy—™Dy—˜Dy—™Dy—™Dy–ùDy–9Dy—˜Dy——Dy—™Dy—˜Dy——Dy—˜Dy——Dy—™Dy—™Dy—šDy—šDy——Dy——Dy–÷Dy–ñDy–ñDy–4Dy—•Dy—˜Dy—™Dy—™Dy—™Dy—™Dy—˜Dy—™Dy—™Dy—˜Dy—™Dy—™Dy–ôDy–îDy–òDy–/Dy—•Dy—˜Dy—›Dy—™Dy——Dy—˜Dy——Dy—™Dy—˜Dy—˜Dy—™Dy—˜Dy—–Dy–óDy–Dy—•Dy—˜Dy—šDy—˜Dy——Dy——Dy—˜Dy——Dy—˜Dy—˜Dy—™Dy—˜Dy——Dy—–Dy——Dy—•Dy—˜Dy—˜Dy—˜Dy—˜Dy—™Dy—™Dy—›Dy—šDy——Dy——Dy—šDy——Dy——Dy——Dy——Dy—™Dy—™Dy—˜Dy—˜Dy—™Dy—šDy—™Dy—˜Dy—™Dy—˜Dy—˜Dy——Dy——Dy——Dy—˜Dy—˜Dy—™Dy——Dy——Dy—˜Dy—˜Dy—™Dy—›Dy—¡Dy—¥Dy—œDy—˜Dy—™Dy—˜Dy——Dy—™Dy——Dy—šDy—™Dy—–Dy—–Dy——Dy—›Dy—¦@{ž @˜?ûa8?û`t?û^­Dcà Dy–0Dy—˜Dy—˜Dy——Dy—–Dy—šDy—˜Dy——Dy——Dy—šDy—œDd*³@:ê+?û^ÅDQZDyt@Dy—˜Dy—™Dy——Dy—™Dy——Dy—˜Dy—™Dy——Dy——Dy—˜Dy—™Dy–;Dd+ÒDcá¥Dyv9Dy—˜Dy—˜Dy——Dy—™Dy—™Dy——Dy—–Dy——Dy—šDy—™Dy—˜Dy—šDy—™Dy–4Dy–.Dy—˜Dy——Dy—™Dy——Dy—™Dy—˜Dy——Dy—˜Dy—™Dy—™Dy——Dy—™Dy—˜Dy—–Dy——Dy——Dy—™Dy—™Dy—šDy—šDy—˜Dy—–Dy—™Dy—™Dy—˜Dy——Dy—–Dy——Dy——Dy—˜Dy—˜Dy—˜Dy——Dy—™Dy—™Dy—šDy——Dy——Dy—™Dy—–Dy—–Dy—™Dy—˜Dy—˜Dy——Dy——Dy—™Dy—˜Dy—–Dy——Dy—šDy—šDy—˜Dy—˜Dy—™Dy—–Dy—–Dy—™Dy—šDy—˜Dy—˜Dy——Dy—™Dy—˜Dy——Dy—˜Dy—™Dy—˜Dy—™Dy—šDy—œDy—›Dy—–Dy——Dy——Dy—™Dy——Dy—˜Dy—™Dy—˜Dy——Dy—šDy—šDy—™Dy—˜Dy—šDy—›Dy—˜Dy—–Dy—•Dy——Dy—™Dy—™Dy——Dy—˜Dy——Dy—™Dy—™Dy—˜Dy——Dy——Dy—™Dy—˜Dy——Dy—™Dy—˜Dy—˜Dy——Dy—˜Dy——Dy—–Dy—˜Dy—™Dy——Dy——Dy—™Dy—˜Dy—–Dy——Dy—šDy—šDy—˜Dy——Dy—›Dy—™Dy—™Dy——Dy——Dy—™Dy——Dy—˜Dy——Dy—˜Dy—˜Dy—˜Dy—™Dy—–Dy——Dy——Dy—™Dy—šDy—›Dy—œDy—œDy—šDy—™Dy—˜Dy—šDy—™Dy—™Dy—˜Dy—šDy—˜Dy——Dy—šDy—˜Dy—œDy—¦@{˜_@<<¡Dd+1Dy–9Dy—šDy——Dy—˜Dy—šDy—˜Dy—›Dy—™Dy——Dy—–Dy—›Dy—¦@|‡l?€?€@9âDdZÕDy–>Dy—™Dy——Dy—™Dy—™Dy——Dy—–Dy—™Dy—˜Dy—œ@{›j?€?€?€?€@9’çDd(SDy–4Dy—™Dy—šDy—™Dy—˜Dy——Dy—˜Dy—–Dy—œ@Dd)KDcàrDytÁDy—“Dy—”Dy—™Dy——Dy——Dy——Dy——Dy—˜Dy—˜Dy——Dy—˜Dy–:Dd)EDc]Dc’UDngDy–QDy—–Dy—™Dy—˜Dy——Dy——Dy—˜Dy—˜Dy—˜Dy—™Dy—–Dy–2DcÞ÷Dc’TDc• Dmý‘Dy–ADy—•Dy—™Dy—™Dy—šDy—™Dy—˜Dy——Dy—˜Dy—˜Dy—˜Dy——Dyv(SD~sDyŸËDy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy——Dy—˜Dy—•Dy—šD›“íE>aE>˜²D ¡þDy ßDy—™Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy—–Dy——Dy—˜Dy—”Dy——Dy˜„DUhD žWDyš Dy—ŸDy—œDy—˜Dy——Dy—˜Dy——Dy——Dy—–Dy—™Dy—™Dy——Dy—™Dy—žDyŸÁDy áDy— Dy——Dy——Dy—˜Dy—˜Dy—™Dy—˜Dy—™Dy—™Dy—™Dy—˜Dy—™Dy—šDy—šDy—˜Dy—™Dy—šDy—˜Dy—™Dy—™Dy—˜Dy——Dy——Dy—˜Dy—˜Dy—™Dy——Dy——Dy—˜Dy—˜Dy—™Dy—›Dy——Dy—˜Dy—˜Dy—™Dy—˜Dy—˜Dy—™Dy—˜Dy——Dy—˜Dy—–Dy—˜Dy—™Dy——Dy——Dy—™Dy——Dy—™Dy—˜Dy—™Dy—™Dy—˜Dy—˜Dy—˜Dy—–Dy—™Dy—™Dy—˜Dy—™Dy—˜Dy—™Dy—˜Dy—˜Dy——Dy—˜Dy—˜Dy——Dy—›Dy—šDy——Dy——Dy—˜Dy——Dy—–Dy——Dy—˜Dy—˜Dy——Dy—šDy——Dy—˜Dy—™Dy——Dy—˜Dy—™Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy——Dy—–Dy—˜Dy—šDy—™Dy—˜Dy—˜Dy—˜Dy——Dy—–Dy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy—™Dy—™Dy—˜Dy—˜Dy—šDy——Dy—˜Dy—˜Dy——Dy—˜Dy——Dy——Dy——Dy—šDy—˜Dy—˜Dy—˜Dy—™Dy—˜Dy——Dy—–Dy——Dy—˜Dy—˜Dy—˜Dy—™Dy——Dy—™Dy——Dy—˜Dy—™Dy—˜Dy—šDy—’Dy—’Dy—“Dy—’Dy—•Dy——Dy——Dy—˜Dy—šDy—˜Dy—˜Dy—˜Dy——Dy——Dy—œDy—“Dy—œDy—£Dy—œDy— Dy—’Dy—™Dy—™Dy—›Dy—˜Dy——Dy——Dy——Dy—˜Dy——Dy—˜Dy—›Dy— D¸Ñ£DÄÃEHZàD»€ÐDy¿„Dy—›Dy—˜Dy——Dy——Dy—™Dy—™Dy—˜Dy——Dy—™Dy—”D¨ý«D9gÆC•ϦD:E"×5D}’Dy—sDy—šDy—˜Dy—™Dy—˜Dy——Dy——Dy—•Dy—˜Dy—™D‘\»Cgø@BÛËB‘©¾DÀ'çE<º®D|H¡Dy—šDy—˜Dy—˜Dy—˜Dy——Dy—˜Dy——Dy—™Dy—•D{ÌGC9³W@¼·B†¿šD¹·ôE=yD|M¸Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy—˜Dy—™Dy—™Dy—–D˜´zCû'êC0W`D›sEå»DœKDyšDy—šDy—˜Dy——Dy——Dy——Dy—˜Dy—™Dy—™Dy—˜DyØDwpE8œµE:³ÝDh©Dy—Dy—˜Dy——Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy—šDy—šDy—™Dy—™Dy—ŒD|;RD|DTDy™ÊDy—™Dy—™Dy—™Dy—™Dy—™Dy——Dy—˜Dy——Dy—™Dy—šDy——Dy—™Dy—œDy–úDy–:Dy—˜Dy—˜Dy—™Dy—šDy—˜Dy——Dy——Dy—˜Dy—™Dy—™Dy—˜Dy—˜Dy—˜Dy—–Dy—™Dy—™Dy—™Dy—šDy——Dy—˜Dy—˜Dy—šDy—˜Dy—™Dy—™Dy—™Dy—™Dy——Dy——Dy——Dy—šDy—˜Dy——Dy—–Dy——Dy—˜Dy—˜Dy—šDy—™Dy—™Dy—™Dy—šDy—˜Dy—–Dy——Dy—™Dy—™Dy—–Dy—–Dy——Dy—˜Dy—™Dy—–Dy—–Dy—˜Dy—™Dy——Dy—˜Dy—˜Dy—–Dy——Dy——Dy—˜Dy—™Dy—˜Dy—œDy—šDy——Dy——Dy——Dy—˜Dy—–Dy—•Dy——Dy—˜Dy—˜Dy——Dy——Dy——Dy——Dy—˜Dy—˜Dy——Dy——Dy——Dy—˜Dy—™Dy—™Dy——Dy——Dy—™Dy—˜Dy—™Dy——Dy——Dy—˜Dy—–Dy——Dy—˜Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy——Dy——Dy—˜Dy—˜Dy—“Dy—”Dy—”Dy—’Dy—”Dy——Dy—˜Dy——Dy—˜Dy—šDy—™Dy—˜Dy—˜Dy——Dy—™Dy—’Dy—¡Dy˜D˜s&D›ÅDy˜^Dy—˜Dy—™Dy—˜Dy—˜Dy—™Dy—™Dy——Dy——Dy—™Dy—šDy—‘D¨ö‰D9hÁC•ÐMD9ÿ×E"Ù¯D}’Dy—uDy—›Dy——Dy——Dy—˜Dy——Dy—–Dy——Dy—˜Dy—D DA¦F²?€?€D?>¢E‘8“D~¤¯Dy–EDy——Dy—™Dy—™Dy—™Dy——Dy——Dy—˜Dyw9C1‰ð?€?€?€BgˆýDöHÚD|uêDy–?Dy—•Dy—–Dy—˜Dy—›Dy—šDy——Dy—™Dyu\Cw?€?€?€B_†PD÷7D|uADy—˜Dy—–Dy—–Dy——Dy—™Dy—šDy—˜Dy—™Dy—C ‡–A#?€?€D9VüE#fD{àÄDy—™Dy—™Dy——Dy—˜Dy——Dy—šDy——Dy—™Dy—™Du’¨E*P¯@“à»DóQÁE%×°D~L§Dy™¯Dy—˜Dy—˜Dy—˜Dy—˜Dy——Dy——Dy—™Dy—šDy—˜Dy—„D|,DkJðD|:WD{ìŽDy™•Dy—šDy—˜Dy—™Dy——Dy——Dy——Dy—˜Dy—˜Dy—›Dy—œDy—™Dy–÷Dy–ïDy–òDy–2Dy—”Dy—™Dy—˜Dy——Dy——Dy——Dy——Dy—™Dy—™Dy—™Dy—šDy—˜Dy—˜Dy—šDy—™Dy—™Dy—™Dy——Dy—˜Dy—™Dy—šDy—˜Dy——Dy——Dy——Dy——Dy——Dy—•Dy—•Dy—•Dy——Dy—›Dy—˜Dy—˜Dy—˜Dy—›Dy—™Dy——Dy—˜Dy—™Dy—˜Dy——Dy—šDy——Dy—™Dy—™Dy——Dy—“Dy——Dy——Dy—˜Dy—™Dy—™Dy—™Dy—˜Dy—˜Dy—–Dy—˜Dy——Dy—–Dy—–Dy—–Dy—›Dy—™Dy—œDy—šDy—–Dy—–Dy—˜Dy—–Dy—–Dy—–Dy——Dy—˜Dy—–Dy—˜Dy—šDy—–Dy—•Dy—•Dy—˜Dy—˜Dy——Dy——Dy—™Dy——Dy—™Dy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy—›Dy—›Dy—˜Dy——Dy—˜Dy—šDy——Dy—™Dy—›Dy—™Dy—˜Dy—˜Dy—˜Dy—”Dy—’Dy—¤Dy—¡Dy—’Dy—™Dy—˜Dy—˜Dy—™Dy—˜Dy——Dy—˜Dy——Dy——Dy—™Dy—‘Dy—ŸD˜hÜE=ÊE>þD|CDyŸÌDy——Dy—›Dy—™Dy—™Dy—˜Dy——Dy—–Dy——Dy—›Dy—˜D‘^lCgõá@BáB‘ªŸDÀ#ŒE<¼D|H­Dy–DDy—•Dy—˜Dy—™Dy—˜Dy——Dy——Dy—™Dyw7C1€0?€?€?€Bg™~DöMHD|v Dy–BDy—–Dy——Dy—™Dy—˜Dy—•Dy—˜Dy—–Dyü@×f?€AÌÒmB˜µÝ?ŸñDøn¡D|„ÓDy–CDy—™Dy——Dy——Dy—™Dy——Dy——Dy——Dy¹?þ²C?€B0ç˜CYë?N¦Døn»D|âDy–?Dy——Dy—˜Dy—˜Dy—™Dy——Dy——Dy—™DyslBuai?€?€?€BZ²‡DöÎxD|véDy—“Dy——Dy——Dy—˜Dy—˜Dy—˜Dy—™Dy—˜Dy—gDl‡¼@Rãš?€@”’—Dñ„•DDyóDy—˜Dy——Dy——Dy—˜Dy—™Dy—˜Dy—™Dy—™Dy—˜Dy”ñDkEžDkvDk0ÚD|'Dy’$Dy—Dy——Dy—™Dy—™Dy—˜Dy—˜Dy—˜Dy——Dy——Dy—˜Dy—˜Dy–ôDy–ñDy–óDy–-Dy—•Dy—˜Dy—˜Dy—–Dy—šDy—˜Dy—–Dy—˜Dy—™Dy—šDy—˜Dy—˜Dy—˜Dy—šDy—˜Dy—•Dy—–Dy—šDy—™Dy——Dy—˜Dy—–Dy——Dy—™Dy—˜Dy—™Dy—˜Dy——Dy—–Dy—“Dy—–Dy—šDy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy—™Dy—˜Dy—Dy—™Dy—•Dy—–Dy—˜Dy——Dy—™Dy—˜Dy——Dy—šDy—˜Dy——Dy—–Dy——Dy—–Dy——Dy——Dy——Dy—›Dy—™Dy—™Dy—™Dy—˜Dy—˜Dy——Dy—˜Dy—™Dy—˜Dy——Dy—˜Dy——Dy——Dy—™Dy—˜Dy—•Dy—•Dy——Dy—˜Dy——Dy——Dy—˜Dy—˜Dy—™Dy——Dy—˜Dy—™Dy——Dy—–Dy——Dy—˜Dy—›Dy—™Dy——Dy—˜Dy—–Dy—˜Dy——Dy——Dy—˜Dy—˜Dy—šDy—šDy—šDy—’Dy—“Dy—¤Dy—£Dy—”Dy—™Dy—–Dy—™Dy—˜Dy—–Dy—˜Dy—˜Dy—˜Dy—™Dy—™Dy—‘Dy—¢D›”dE>®E>ÿD £.Dy âDy——Dy—šDy—–Dy—˜Dy—˜Dy—™Dy—–Dy——Dy—šDy—D{ÅöC9±|@´ƒB†ÜÃD¹µ3E=wÆD|M°Dy–>Dy—–Dy——Dy—˜Dy—˜Dy—˜Dy—–Dy—›DyuYCwÅ?€?€?€B_ÞOD÷„D|u`Dy–BDy——Dy—–Dy——Dy—™Dy——Dy——Dy—˜Dyµ?þ‹?€BmõDC:é?m­DøpƒD|üDy–BDy——Dy—–Dy—˜Dy—˜Dy——Dy—˜Dy—™Dy?ë4Å?€B·¯vD,R¤?šüDø}ôD|ªDy–:Dy——Dy—˜Dy—˜Dy——Dy——Dy——Dy——DyqªAéÉ?€?€?€BS5ÍD÷«tD|y'Dy—”Dy—–Dy—™Dy—–Dy—–Dy—–Dy——Dy——Dy—dDkþ!@ßÞ?€?€DòBºD"DyÚDy—˜Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy——Dy——Dy—™Dy”ëDk$uDk¤Dk hD|' Dy’ Dy—Dy——Dy—˜Dy—˜Dy—˜Dy—™Dy—™Dy——Dy—™Dy—˜Dy—™Dy——Dy–öDy–Dy—•Dy—˜Dy—˜Dy—šDy——Dy—™Dy——Dy——Dy—˜Dy—˜Dy—šDy—™Dy—˜Dy——Dy—˜Dy—šDy——Dy—–Dy—›Dy—™Dy——Dy——Dy—™Dy—šDy—˜Dy—šDy—šDy—™Dy——Dy—–Dy—–Dy——Dy—™Dy—˜Dy—˜Dy—˜Dy——Dy——Dy—šDy—™Dy——Dy—™Dy—™Dy——Dy——Dy—›Dy—˜Dy—•Dy—™Dy—™Dy—™Dy——Dy——Dy—˜Dy——Dy—˜Dy—˜Dy——Dy—–Dy——Dy—™Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy—šDy—™Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy—–Dy—–Dy——Dy—–Dy—™Dy—˜Dy—˜Dy—˜Dy—–Dy—˜Dy——Dy——Dy—˜Dy—˜Dy—˜Dy—˜Dy—™Dy—™Dy——Dy—•Dy—–Dy——Dy—˜Dy——Dy——Dy—˜Dy——Dy—šDy——Dy—˜Dy—šDy——Dy—™Dy—˜Dy—™Dy—”Dy—’Dy—’Dy—’Dy—’Dy——Dy——Dy—™Dy—™Dy—™Dy—™Dy—˜Dy—™Dy—˜Dy——Dy—”Dy—˜Dy˜‚DSÄD ¥Dyš Dy— Dy——Dy—™Dy——Dy—™Dy——Dy—˜Dy—™Dy—™Dy——Dy—™D˜µ2Cû¹C0GÐDšfEæ×D›üDy™ãDy—™Dy—˜Dy——Dy—–Dy——Dy—•Dy—˜Dy—™Dy—•C ƒVA# ¹?€?€D9?ZE#etD{ßDy–ADy——Dy——Dy——Dy—˜Dy—–Dy—•Dy——DysqBuiÚ?€?€?€BZØ£DödzD|v˜Dy–:Dy——Dy—˜Dy—˜Dy—˜Dy——Dy—–Dy——Dyq«AéÉì?€?€?€BSt‚D÷©ÉD|y Dy—–Dy——Dy—˜Dy—˜Dy—–Dy—™Dy—˜Dy——Dy—C#\@!Ó?€?€D3e[DyJADy’EDy—™Dy—šDy—˜Dy—˜Dy——Dy—˜Dy——Dy—šDy—™Dt~DM#É@UHDìÍÂDzDyp«Dy—ŒDy——Dy—˜Dy——Dy——Dy—šDy——Dy——Dy—˜Dy—šDy—€Dy{{Dk¼D{ÛÈDy“Dy—‘Dy——Dy—–Dy—•Dy——Dy—˜Dy——Dy—™Dy—šDy—˜Dy—˜Dy—šDy—˜Dy——Dy—”Dy—™Dy——Dy——Dy——Dy—–Dy—˜Dy—šDy—˜Dy—˜Dy——Dy——Dy—™Dy——Dy—˜Dy—˜Dy—™Dy—™Dy—™Dy—˜Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy—šDy—šDy—–Dy—™Dy——Dy——Dy—˜Dy—™Dy——Dy—™Dy——Dy—™Dy—–Dy—˜Dy—™Dy——Dy—˜Dy—™Dy——Dy——Dy——Dy——Dy—–Dy——Dy—˜Dy—˜Dy——Dy——Dy——Dy—˜Dy—™Dy—™Dy—˜Dy—™Dy—šDy—™Dy—™Dy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy—–Dy—˜Dy—šDy—˜Dy—™Dy—˜Dy——Dy—˜Dy—™Dy——Dy——Dy—™Dy—˜Dy——Dy—šDy—™Dy——Dy—˜Dy——Dy—˜Dy—›Dy—˜Dy——Dy—˜Dy—™Dy—™Dy——Dy—˜Dy——Dy——Dy—˜Dy—™Dy—™Dy——Dy—šDy—˜Dy—˜Dy—˜Dy—˜Dy—˜Dy——Dy—œDy—šDy——Dy—–Dy——Dy——Dy——Dy—™Dy—›Dy—šDy——Dy—–Dy—˜Dy—™Dy——Dy——Dy—šDy—›Dy—šDyŸÂDy áDy—¡Dy—šDy—™Dy—˜Dy——Dy——Dy——Dy—šDy—™Dy——Dy—˜Dy—™DyØ‘DwoÁE8ŸæE:µ—Dh½Dy—Dy—˜Dy——Dy—–Dy—–Dy—˜Dy——Dy—–Dy—šDy—˜Dy—Du’E*TÛ@“úDóUÄE%Õ‘D~L¾Dy™~Dy—“Dy—–Dy——Dy—™Dy——Dy——Dy——Dy—˜Dy—iDlˆ @R§ ?€@”o Dñ†ýDDyùDy—’Dy—˜Dy—˜Dy——Dy—–Dy—™Dy——Dy—™Dy—dDkþô@ÛÆ?€?€DòKüD#ëDyßDy——Dy—˜Dy——Dy—˜Dy——Dy—˜Dy—šDy—šDy—˜Dt~DM#ì@WSDìÉDz•DyqADy—‹Dy—šDy—šDy——Dy—˜Dy—˜Dy—™Dy—šDy——Dy—™Dy’øDy„¢DTšDá]Dy{ Dy—gDy——Dy—™Dy——Dy——Dy—˜Dy—˜Dy——Dy—˜Dy—–Dy—˜Dy—™Dy—˜Dy˜Dy“eDy—”Dy—˜Dy——Dy——Dy—–Dy——Dy—šDy——Dy—˜Dy—œDy—˜Dy——Dy—™Dy——Dy—–Dy—˜Dy—›Dy—˜Dy——Dy——Dy——Dy—˜Dy—›Dy—˜Dy—˜Dy—™Dy——Dy—˜Dy—›Dy——Dy——Dy—™Dy—™Dy—˜Dy——Dy—˜Dy—™Dy—™Dy——Dy——Dy——Dy—–Dy—•Dy—˜Dy—›Dy——Dy—˜Dy—˜Dy——Dy—–Dy——Dy—™Dy—˜Dy——Dy—–Dy—–Dy—–Dy—˜Dy——Dy——Dy—™Dy—˜Dy—˜Dy——Dy—–Dy——Dy—™Dy—™Dy——Dy—–Dy——Dy—˜Dy——Dy—™Dy—˜Dy——Dy—˜Dy—™Dy—˜Dy—–Dy——Dy—˜Dy——Dy—˜Dy——Dy—™Dy—˜Dy—˜Dy—–Dy—™Dy—šDy——Dy—˜Dy—šDy—˜Dy——Dy—™Dy—™Dy——Dy—™Dy——Dy—˜Dy—šDy—™Dy——Dy—˜Dy—™Dy—šDy—™Dy—˜Dy—šDy—˜Dy——Dy—˜Dy—˜Dy—˜Dy—˜Dy—™Dy—›Dy—˜Dy—šDy—˜Dy——Dy—šDy—˜Dy—™Dy—™Dy—™Dy——Dy—˜Dy—šDy——Dy—˜Dy—˜Dy—˜Dy——Dy—˜Dy——Dy—˜Dy—˜Dy—™Dy—šDy—˜Dy—˜Dy—™Dy—›Dy—šDy—˜Dy——Dy——Dy——Dy—˜Dy——Dy—˜Dy—™Dy——Dy—šDy—ŒD|;bD|D`Dy™ÿDy——Dy—šDy—–Dy—˜Dy——Dy—˜Dy—˜Dy—˜Dy—šDy——Dy—™Dy—‡D|:DkI›D|;sD{í]Dy™ÅDy—˜Dy——Dy——Dy—šDy—™Dy——Dy——Dy—™Dy—šDy—™Dy”ñDkDŠDk{Dk0ID|'§Dy’ Dy—Dy—˜Dy—˜Dy—™Dy—™Dy—˜Dy—™Dy—˜Dy—˜Dy—šDy”æDk$Dk×Dk lD|(§Dy’Dy—‘Dy—˜Dy—˜Dy—˜Dy—™Dy—˜Dy—˜Dy—˜Dy—˜Dy—™Dy—|Dy|¡DkD{ÛÏDy“æDy—Dy—˜Dy—™Dy—™Dy——Dy—šDy—˜Dy—–Dy—˜Dy—˜Dy—™Dy—™Dy—˜Dy€ÝDy“ÔDy—”Dy—Dy—šDy——Dy—˜Dy—™Dy—™Dy—˜Dy——Dy—˜Dy—˜Dy—™Dy—™Dy—˜Dy——Dy—•Dy—œDy—˜Dy—˜<îs{?X“¹>ïYY?Byd?9V>ïêq? ‘?U—s?<ÌÆ?–r?Š=>Üy…?–æ?yì;?ò•? &7>Ê=Ç?m˯?~õ?Üþ?=g?¬[?Xc£?ƒå6?`•x?Žó´?_j€?h_?–±\?{+Ç>èͬ>¿pI>ÄÑ3?/¦?i2{?·…Ð?¥?•RF?>Ê?ƒiø?QX4?U$"?1Ÿ?^_?üb?1ÑR?K4?›¢å?d¹š?7o?ŽN?^ß>?C¦?£–É?ãêd@v?í4¿?Ü,?&?|¾é?¤Ð?:s ?‹˜G?Ud ?OXl>õW ?A °?ìâî)?Hž`?–¼?ê ?'Ù?|«>ÉÀ\>¸xÆ?R™ ?w,µ?²YD?Gáô>ñ?[k›?ç®>ÒËM>ÿß»>ÎêB?\ö?„š?ƒ't?n¡û?ÖÆ?‰WH?opÅ?ì$?28?K€?âÌ?2³??x§g?b$’?r€ ?9Ð? ÉŠ?Ò½?v8±?åá>ï« ?—u5?„ú ?”{?°?R>p?÷w?‘Ûç?L?,p?Qˆt??¨? ce? ˜Ã>±e½?bÁs?Wèž?gì™?u¹?0c?> ­_?7,A?:BF?š–?¤Ö®>údL?i?)“t?(îä? U,?Š•É?Tÿ^??»É>ê’…>ß÷®?›”B?1Ì:?ˆ0?pøh?RÙ÷?ƒœ²?* ?MG¿?G³ä>ÿ… ?–O?QÔ$? í>Ýâ·?MˆT?J?”xˆ?›6H?¤o?˜˜? …h?‰g? Éð?sŒ…?E½8?cðU?<)%?ë?+Â&?8¬¬?bBk?Mî?^a_?{'+?‹úZ?Ns0?ÝC?oÿ#? n>¬5?0?‡B7?;oi?Þµ?CŸ´?#Û:>á(Î?Tû¯?ª*?p¹?Rd8?bÔ`?vêS?+Üü?%Â9>Þ¿?N?ë?’P.?#Ÿ×?3˜>ß’F>ñ"?–8?;*>·{?Uq—?.TÝ?ˆq¢?;Ê{?!±?&F?Pìí?fZE?a>ñ¹0>ó%M?î9?o)?xâ·?š;þ?†~>¦}³?&…?‰ÿ?a?ªp?߯?ú?|1Ã?>óh?/ô{>_^Ð>åË?~š?bäK?$£ó?a—?RO?%?287?%D>Þ•Ê?0™m?2J‰?•Œ?Dì;?BT=?)5‰?U!±?Œä=?·$??ê¿È@5ÆF?¸²Œ?8_6?i!Å?JL´?2ZÝ?–m?^È?µ¿?oí >Ððn>îQ.?#ÑÌ?a«¨? –A?6ýè@žê@ï!BA½m@Ü¿‚@Àèh@G?Œ6³?D}î?@·¹?BW>Œ-?¬kÈ?\E&?_€°?O?z±?Èýu@s0Ë@Ü“½@ÅíÁ@T_ƒ?ɾt?Œ}? Š?/(û?•RE?3Á*?…P?Œ8þ?&gƒ? au?.Æ?i̪?Ø¢?×ÙD@At?¦Šò?y®„?dùˆ?‘:?‘Í–?†Y?'(œ?Œ‡?,¬?A?ƒæq?9ÇH?žÿ?‡çY>à¥î?€!õ?\ƒÛ>ïãý?–‘P?-±€?œ'À?DA˜?*¡?‡*}?Ÿë;?Š;?\#?:²F?jQ>àƒñ?l?.Ã?‘3 ?Šq•?©·ê?¢4`?O¯?qï?‘0X?‰ê?jìx?(EÑ>¼iõ?Wò?áÁ?l:?hS$?‰/?=¯]?¢e5?x´õ?¢°P?69M?&Gù?Ÿ À>å*`?µ?¨‹?i4?^l?J {?J¹n?›P?B<1?î?dL?¥éÃ?¹ÿ±?{Åô?[(^?˜êV?&Ì>ëKw?‹¯ã?˜7^?lD«?{9?V&†?˜Ÿ?z¤?j?`Iü?r;ä?6—Ð?‘±?ŒZ?š??¡¡š?fÒ?`f\?DÒµT!?6~Ä?V%í?Ž’Þ?=ê²?^‚w?/n?E$? gÀ?;6œ?,÷?b=?¼ø?W‡€?iú&?†Ï?vùN?·?/Aé?jÈ,?éÄ>õeE?zGñ?ˆH??,ò?“û?NJþ?%v?F4§?Ë;Ï?ÂåM?}ç? |?¦w¹?Yï!?Š‹ì?T? ™]?šMî>膬?D‡O? Œé?e b?m\É?9ÚÈ?c¤É>l?17>¢ðº?4ù?”Nw?[{æ?‹¶;?¥ò?m§l?Sr‚?8ÇQ?‡îC?pï?xLŒ?•v?qð£?WÀ¤>ýA?ow]?&Øå?ňˆ@8#Î@×+t@Ï -@Èš?‡=û?­²f?Œ?FCÀ?½@™?_,?ÀÞ?ƒà­?!E~>žE:?^C¾@GŽ‹@ÃuA¨AmÞ@½>ì@ æB?¥<?šá±?;¦I?•wt?„ÂÌ?7Ýf>øbøI?ˆV“?‡{?*-L?AÞw?&84?FA?Ÿùº@&–h@µßH@Á?Ý?³gŸ?9Ò&?4_ü?.îM?hÊ\?ts±?Žyœ?`?0í¥?ˆ,x?.ñ?™q}?}j?WYÕ?½t?½šƒ?œ’“?);s??žŠ‡?MŒZ?*J¥?)*Ô?V(?-%?4?BÚ?¤CÂ>ð>?\”%?µè?€v0?7XÌ?"þo?„*Æ?Ã2C?\D?7d?bÏ?X™2?B?d`7?X??a|¯?CAP?|ÊW?;ƒ~?¡„?WŠÐ? ˜?GÏ?Š-Y?n,?:Ä‘?·?¬€?FÆK?<¸á?L9¬?-“Î?+а?FÈz?¤u?>¾—?oØ?Lõ?'¥_?ké^?/!?Fé¢?4¶²?€÷y?;çú?mߦ?gÿ?|Î ?v¥?S¤Ø?©u?;ü?V 5?LŽM?/©‘? æX?)ðô?ül?¤q–?‘ j?=b8?Šbû?‘rÊ?Kc>âŠö?Y |?s^²?ŠõŸ?@ø¥?R~ë?a¥9?ĵ?%’º?¤" ?†ù“?!C2?¦ê?‹¤‹?…?l“ ?‚¶Ô?ûX?‰ÜF?y™h?·?lIø>úîÆ?tù—?%JÎ?Z?ü?„r?r…À?„´¡?oz?Ih? M?kxÊ?‚ß?„VÈ?‰É??gƒ?w§?!¦?&m•?æËN?+?Ì­?€o8?O; ?b:é?3Ñê?D?Y<ˆVk?7ߟ?/Aô>×ßO? Ò?Òìö?¹??»wS?úQ£?Šn)?s\;?kÃ?u·æ?ºê¬?}-?Ùà?K–l? ýˆ?XÁ©?$™?áx#@áß•@õòÝ@Í)ó@â]]@3×À?‘Ú?¤˜¦?ˆXU?ŒY^?€—?š\»>‰Ïö??- ?@ò.@˘@éЭAµeA¿L@åd0@œò½?ÅÑ?+”=?>]?\ËŒ?j|?š.n?UkÒ?q'×?nçŸ@ †1@÷Ôm@ô'È@Ó?ï…@L$?©¥º?œç?j”ƒ?)|Ì?’[?RÁˆ?5)?²?“ê?£+6@>fAñ @í§Ï?×ÏŽ?gx?”ó]?'ª>}– ?ÑM?@I?‹xP?9#â?uù1?g]?ƒS‡?*E?ÕÆÃ@µ 5@üÒ¶@/c ?`Æ?Ä }>‹>(<=>ôF=?I¹?^6-?ER>?>Å?3y?¨Rð?rÛß?A=@ûÞ@À´@l(?Aá*=Ç_™=/YÑ=]-?ƒ”l?{<&?&ß?”Iµ?­«,?,”_?5Gõ?D^ƒ?Rçÿ?ޏ?¼§Á?Q³>H¢J=ö¥Ú>Z |>a‡ã?l˜?W9”?Ž? ¦æ?Š l?tÿñ>Üi¯>ªîp>ÐÛ?_„>ú_X?'¼K>öM?få?* 1?y~Á?¶Ô+?XJü?Dj?†«ÿ?¤Ši?§¤I?¢­?[Fc?N4X?|e?#•x?”ãA?¦XA>ú^½?-z*?FÑQ?wŠG?^ˆÆ?||q?„(?8hm?j_?¡ðÌ?Êe>ËÂ-?ˆÀÖ?mÓ?Mr?ø¹?Q ?–Ž\?yBà?…Š?f Ú?Ÿ¼«?o9„?[/>¾>Æñ­?Zwõ?`üƒ?+ä?õ×?„o?wT‹?Ã$?'>U?/€¹?J ?wi—?F+r?’–Ó?Šnø?£8ñ?„gÖ?…ðu?§xr? Úb?\8¸?i¹?lµ ?ZoD? èk?K6?i›L?>W ?na÷?]ç(?G=? {?t“Y?{AG?‹NÌ?`Çó?€?oÙƒ?MçÝ?@Ró?¥(Ÿ?˜Ÿ§?]HÔ?«"?—a ?Íï?‚<¢?Ö?,þ»>»«ø?Ï•ó?ˆ%Æ?0M? á`?…¶o?v=ª?yU?T0Ó>¸$o?/Áá?v?J?mk´?}–k??ÿ?5ö>j1H?H3ñ>ÙAO?CS?7F»?oÌì?Wl¬? _Õ?“0”>ç€8?U¶?7ýÞ?°Å?Pp~?28?0i_>‰,?aõ€?GÕØ?“Ë?­§@%6?çı?^?Œ$ÿ?>¹³¹?!X€?cû?/&?ÑU>ÂÁÅ?Â?*gc?ù®š@»MA/rA}õ@ÒÍY@Cc‘?³•I?ˆ~Ý?a9S?Ë8? 3;>Ö&-? á?¡[W?P…~?¸5Â@ÌÁ1AxóA 2_A ìÖ@×µ@§Ì)?Üëƒ?6^?€?R‘ ?0Ùz?=áð?]€>Ó›G?‡A£@ ÀAÔ@õ.Ú?Žœp?™\?׺i?…Œ¾?I!N?ˆžC?ô?(]?ÃÌ>¬Gâ?d?}[¤>ýÌ'?ÿXv@úÙ`@ý¹„?¨•…>€+?Ï5?7ËL?Qu¤>ócµ?‹çö?œYð?ª?N|\?ÚH@Ä@1@þ$m?`&r?$ö?Ðû#?²U?ˆ?_²œ?¶|?Û~˜?F™W?SÏ’@U9n@š´‚>$½º;¦Q,>ï4ß?0…?žoj?$ot?Hb¸?2µ½?aí`?K×Ó?¤??ÛJ?Ÿ®ã>÷@>g|Ž<<Ú¼÷µ?KH•?$Ö.?œ¼‘?¼YÙ?›×Ã?){ ?i ¨?=¥ï? G?~;Y>ç§?6°?®ö?|Ö?) .?_½ ?;ÛÉ?9‹G?‘Æý?|§â?aoS?Id]?9,è?@³?Dø§>çÊŠ>ß½û?•_?±\™?Ó×?{–0?VF‚?u©ð?¦€?€¢?ªàV?m:?W)?4ã?¥ç??(n?>d?F´²?iÔÝ?"HÛ?G„›?, ?LÙh?žòC?ŠšF?vÄÇ>îË?§ø¦?q®Ë>ÿ¥?Wo?‚Àò?‰î$?P·?Fë›?å.?K«?%Ô?=^?™_©?[jÍ?Oõî?L¼?éª>ŽÛ ?0Ñ™?@§ÒL?Qò?’;ã?4‡U?•9u?zÐy?“©ô?€X•>Ò$æ>¦Õ¿>ÿT?®Ë?”?½N?_f?E f?Wª?b?z_×?~?5Ð?…É ?"H’?,Îß?2š?}9a?A³?@o?•;_?Ö)£?õ?Fn™?ÆM>¬Þô?aFç?e©?gŽ…?giß?3}F?*dQ?fÓh??èa?;@D1Ù@Á»@Él,@:*ò?¢û?a5?$€f?¤5??Åš?j~[?“Ï^? æÜ?ƒËü?„ Ó?fá²@†@Ò$A‚}A-©@¡ó?žÜz?>rE?dÀå?ÃW?–À?xL®>å§ì?d‘R?ŠÒ%?C¸@Ò˜@Ç­P@Ôc ?òãŠ?‚4?Ög>X:e=¶ ê>þ„?RÑ?(k¿?UäK?u?63š?·s?ZlÙ@K@¿Ö§@ò²?›“¼=^òõ?;R>ä¾? ²–?^:í?UÇú?•–¥?€=?kß?A%É@%ZC@­ªé?D«>y3'?h–°?kP¥?c `?× ?Jí?.¦@?Mm©?!þÿ?c9?¹~¢@06I>8>d*?›?WöU?šYö?¿›?Cdr?’jÅ?6àw?F=ˆ?ZÑÚ?2VE?c>kˆµ>*é=EfW=ËvÒ>3‘?t ?`å?Ÿ¢L?€X[?j‡Ñ?Â-ª?½³?{Šs?ˆ'{?-f$?nãÂ?:Ë?ÊŠŠ?€¨½?a”>ñ×ú?+²C?c?Lc?>âÑ?€”?c ?È.³? òm? ~>ÜKó?q©?ƒú¾?ƒ?7?H:Ô?—„•?EÓ?¢Ùb?]ñ ?s3R?nî?_Mñ?)¡Õ?*ín?8S÷>ëMü?9^X?bع>Ó«‰?Ké?}àä?‡Ùâ?©­@?}1\?g)?jjÕ? †?{…?<¾g?q={?=åÔ?€é??ŒŒ:>ÿõK?,_-?–›?—Ò?‘Úá?ÚO?l?—§í?¢K?­«‹?cÁß?Œ¤¤?=$w?z:î>óU~?W]7?‚í?IAí?Ô?p?QÛ÷?hh?:ªÇ?Dp=?eÌK?’š™?eøÐ?0ªí?$" ?B¦Û?•ñP? N¹>Ìí?Š ù?ªÌ> ”†?K¨à?e™Ñ?AÎØ?I†Å?fjV?*Œ{? ÝG?bôp?"£6?[€ô?‘?)ÝÕ?¯c?N}·?Ÿîø?Ÿ6ƒ?EN?'3À?Läå?"ì?E™a?}X_?¦q—?&ñ6??ž…>”õp?.ÚB?€-¾?\k¬?.c+?¢Þã?„`|?‡yì?V ?—‘¶>¬{?1m~?)ðÑ?I–v?Wýw?UÅÁ?irß?rDr?KĹ?4I>µ•´?_¤‰?Are?RÀ?h(?tׄ?WÆÉ?¯¡˜?tjÕ?0?V¯¡?0ÿ1?y$?‰$?ƒÄ @ å'@ í?Ï@?Lº;?4?š›´?EDZ?=y?oü5?‚é+?CÛ#>ÿç…? Ý]?si?µuŽ@:;é@ªU’@¹´ñ?Ø—á?G×­?WPO?EgY>Û7>ôÍú?mû÷?Y| ?œ*W?'tq>™¹9?ˆ¸@ø @·Ò?­wâ?w1¼>Ïx…>Bc­=pÁ^?,{­?“lÞ?£¢·?t4¿?Wi?¤¢?=X?M·?Wœ8@ .@½çA?Hɶ<<B?!¥Î?\"u?–ì×>ÛÄŠ>çô¢?€ >ž©½>ðd?„W²?›3š?ÂeM>HG>†bÓ?Å̱?P“_?uÚ?2"n?ùó?³¬?“®m".?”Hº>úh“?Qûâ?Pÿ ?tJ?iÕ’?'Ù,?,©•?œÂ*?'½?+î=¥*z<ðP°=0ƒ=ƒòj=“çÿ?­I?´€¡?Ɉ.?V/Û?V5?zz?¾? ô?€q¸?™D—?e¤É?w¦Ù?5|g?’0Û?}1?̬?zg7?X²Æ?†æ?~Ó?«@Ž?s>8?P÷m?uáä?‹Š?z²!?LˆS?ª”?{Iµ?…Ìæ?¨m/?˜@o?0u¤?-Ú`?B.¸?PLŠ?]Õ?‚;R?88 ?/>ÁwŒ?ñ„?xm?Iý?(Ž?l.d?GÍ)?(È?aJ?t—?WüÞ?‰dI?Wv?Tj?Œ\Ý?"$?3w?i|¹?ƒÄ€?'x?\±Ø?/²‚?#Aw?oö?Z¬?W`õ?k0?…a?EÐ*?¡q?”–¤?|xt?‡j?›Þî?v׋?÷?AYš?Éü?‹t)?¢<?‚µ“?‹¯º?=¾o?B?Gê¹?—Í?wÂñ?b{?˜¥V?-F‡?M#>©’?6²R?¯—Y?UÁ?q°.?¦?¦º?RO¬?—9_?s?,¡Æ? Ôð?C?A¿Ö?Sb?R¯º?5eÓ?ŠÝÃ?ŽØe?( Q>òÂ?IÇ??D½ ?äë?"Æ?¯é?eô0?,²?’#?}Œ?xq±?_$`?3L>盪>à]?*Õ¿?7 ƒ?>ƒ?(‚‰>×(¥?”|´?SÆ?[4t?Õ?bZ?%?,?(³u?wt]?:3ƒ>â??gN¬?|¨?Gj?UT»>÷ß?z2?rÜT?µ?½¿~?PÀ£>¿?J,¨?±C?ê&Ú?‹~—?œŸU?>ôÔ?ð»"?´o„?5Ë ?8—‘?„€ã?OXø?'^¨?=Ñ%?îR?e,`?K?;7?)W?ÝY»?ÕÌ>%Î>Ä,>éOy>£q©>oú?wò—?L²h?F+à?8|m?póö?gE?„ge?ª&`>å$;?â-Î@ÿ%=Øoè;¼ït=pl'?ˆ›&?+å?³y˜?’ƒ?v¯J?SöŸ?n7?WF?0"z?db®?{/n>Ë_ <=Ø®=[>>5ç²–öt?ªç?…´ >dÂè=CÌU<¥J > Æ<÷i?%0 ?Y°M?9ôG?(”l?? xó?‚ó?„€‚?-¦'?MJè>ô,9=§‚>–¢ó=Ð %=Æ·=|üÁ?_à?O“1?‰ã?"¢ö?¶)?r"?{ô#?:š¨>çÿì?S`!?±?]ýÿ?Zâ?q×>áb?„ì?‰3?£?l[>ÏYu?‹ge?|¦•?„÷?ˆ×›?9Wô?,×?8bÑ?ŒY,?-Y?‘?IÌO?©Z?ßÌ*?‡tÆ?•š?]!à?i %?¬Û(?F)>ø°:?0U?C1*?˜#¿?œx¨?pä!?†8?«d›>ë‚Æ?•©„?’Gõ>üX„? wI>úZÚ?kì¡?}Šâ?IÊ?gWÐ?AQ?_7?-ü>ŽŠ˜?M˜§?Ù¨À?S2Æ?|¶6?ŠV…?•§?F¾§?Zm? ž?m¬Ç?qJ?‹Ú?Eè?Ž{?’÷ü?Y6?{ðô?fOƒ? }Ù?o?4Ðî?+<?'ߟ?©½'?«hÄ?µðL?’?dS??.+??J³]?²°Ä?½åï?q[K?‡ ½?M?[±C?ìø?=Ú?¨î?Ž?Y„>†ð¦?TOv?‰š`?fá=?.ù€?¢™€?*3?hU8?X9á?NFš?„j«??¡?0?Ry´?¸,†??¸u?GÝ?~Í?XÕ??j?9Hv?ƒS€?Dš?2É?yç?Mk?0ÁÊ?3z?H¨ó? ³8?}Íæ?ir? ¸þ>ø Ñ>òë@?^W?M®F?/x?/;‹?DP¡?Ky? lz?‡Æ?€½Ú?¦?ïù?VV?XpX?”8?XïH? p?WÔ£?Žö?——?3ã?ÏÔ?$Lœ?|[n?´@Ù?P-?càñ?n:À?i!?¨=ˆ?Å?$SÎ?^’?B±f?“U?L¼?Þü?{‰3?N«¯?n¦~?m°?8¿º?_(?† 8?ײ?ŒÍæ?vÀ>ÖÛ¼?i)v?‘Wõ?RC>Å=U>À}»?’Š)?\à)?~Ž??VÒ#?)•®?‹j-?V”2?SoÜ?a ¢?©+I>ô—Ä??c¿?Ž‚>´Öú?%ªB?0¢?‡û?‹Õ?”QZ?“ÊÆ?1u?eßÄ? ž¶?¤W?4Cø?–g?–"?´ÚY?GŽ*?dXæ?aÃ?u?‹šœ?jiä?‰|»?Û2o?›ý?¦y?2—ž?¢Æ?€ã…>4A5?}¥Ü?¬Ú?uÀ’?' ?”?žÍ¯?T'=>âp¦?u¯ž?¦!>ܨè?‘í¨?o®Ù?¢"ä?+Gœ?'?¬¾t?—¾?•(A?šÉ.?ê?6Æ?³r? I ? A>s¡?K)Ù?‘áÍ?k¢>ó°?¦ú?Œ~?¦½?P^?-<3?{#Á?ty?b?„ÅÕ?Yúû=êÓz>gVž>g>›P>3°™?ÄÀ?i¶‡?Œð ?Xßz?Q:!?^ý?Êx?&0á?*4â?‘A>?«p¨;,L¡<šöD>`ÿQ>G" ?‚B?eðg?%$ ?i€?šÞ?y ô?zB?Åó?ˆ?Š’H?¸^=œŠî=Ö6³=¨9î=ƒû÷=‰ p?5í?Š'Z?’Ð4?$7??»û?½åà?@2Ù?•4?‘ß?²°ä?r€=TÎÄ=ßíÂ=¾Z>\>G-v?7q?É[?Pv?m´?zZ8?Oe?Cä?q¸?0‘þ?OÔ?Â`£>Åç©>† &=“Æ4=ãz;=µ1Ä?ò„?@¨—U ?M>—=ðü>CÑ>ÌÐ ?®c¬?n€?Ę?[5|?9Àw?-ÔË>ý?„Ë?€ºß?å?8ûÔ?Å?o??óË?ˆ{C?1N>ǧ">‰!Ý?°£?‡ÁÒ?k s?/ÓÔ?n/d?„í9?Xi?FO±?kQ‘?šh’?…’É?V3O?R>?œâI>á³¥>h©y?VŸ?‰é>û,$? ùª?f?È?>Ž,?‚ÿU?ÕK?&Ó? jß?ar¤>¸.ê?–É?jê?C•â?$E?‡Gø?^%q?ƒä€?¢`Á?Ùs?Ü?Vÿ9>ó  ?kéÎ?–ž…?]ÍÙ?H™†?ŸÞ?ÁŸ?¥=Þ?í@?oÖ(?„‡Ã?S$â?OQ8?g?¸>½I}?h’?ŸBÆ?k›C?•ûn?=–ì?{{?f_¤?Š®?>çê?3Ë?c+¥?I1*?Àî5?¶µ­?,™£?#«3?„±a?#¾ž?¹í?Um.?†çâ?„3Í?Nù?®Øx?"Í–?Á¤?–¶s?`÷?7KS?‡Ôš?•æ¯?`·J?/Ê?¤Å?XJ?2ƒ?W°?zø?¯p—?Ÿ±©?`ü1?3Þ}?I_‹?:Ùå?Ù9?‡ØG?Hÿe?~G1?k>Ã?[R?„+î?™gŒ?‡Q3?V«D?Á(?(;?YÏ?=u?&RB?E—û?îM?Fs±?D×û?«ÎE?% `?aQº?[ ?’?jÜJ?5ª?Ê’?/aî?bd¡?om`?@rÎ?“ÿQ?+ê?˜šM?'›?zË·?‹Ð-?Hžà?·?¥>Ý+L?IÊ?_Ò?K“?™Ù'? p?C?4%?®Üõ?d–w?‰2?w%,?Õî? =¶?å ¬?r):>(ß=]ÿ<©Æe>&Pö>Á˜?Œž=?T+?ÆD?yÞ1?BëÒ?;{?6.!?y[ô??Q¼?_.Õ>Ñö6ŒŽ‰=² ?\¢n?š3?W®q?9/?&±®?™$F?›"¤?oÓé?1” ?ŽØþ>Lf9ÌA=,ül?s‘?XþF?13´?bfœ?®á?W"‚??_+½>µŽ!?I™m>œäÀ>B/ï?…ü¾?W§F?í?fTâ?‚p‘?tÅH?!ÝH?[†?Fü²?_=>„?=ÖWá?»b8?lšY?$ ?WËý?ux‡?%ë?ˆrî?^šL?£¿H?‡bè>x=Ž>{¯½>d\=³õ>-ò?j;å?…àõ?‘SÔ?Yèæ?In{?EÒ?'âC?`öª?‚‹Ð?r9r?üÜ?E¿à?‚³?̸½?ØY˜?H¿`?£0?,üÐ>»|?~Ív??ô?9â;?†VM?4ìK?Š[´?Ž?r®t?Vå?XT?R·œ?A–a?õÖ?–ýÐ?B­?3âl?[ö?v×?9x?]—;?Ç.?ˆÛ§?Ž4c?Î&?~!¿?†"?N#?Š­?$B?8‰?UDo?«LŒ?QL‹?,>„?3D?i4?‹Ly?¢Ó‹?S>?R;þ?me‰?p³?ˆð?¦µ|?–(ï?·‘¯?*?lúW?kJÁ?®Å?R^9?›£ ?—oÇ?”j?:…?>{?Mð?k_.?âL”?yÜ?˵>åÎ?A˜ò??0?€2]?¹ò«?“ïš?Ž" ?‰ms?¦Ü9?fà‹>ÿÏ?8k?Ó½?Ž3>ÝR?Ÿˆ?<?e@;?}¥>ö5]? úí?…Š•?·!‘?DTö?T°Ì?€Ä(>ø#>?hzF?Jaè?p׃?”hv?Vé¤?ì®Å?’P¾?EÇ:?iŠ?oç?g&j?†©>©öW?Q?XUü?|e?>ˆ÷?-n•?iI?pî?€)´?L@ì?&`-?²+?&Ò?mÓf?”{£?š†z?9Å‘?Su^?ô¯?c°Ã?‡¾d?N?6ïæ?oôr>ú?Y×;?bä$?ot?s¦2?0&:?ysë?kÄ+?.†4?B8±?Pf?AÎk?u~À?˜S…?‰‡æ? ›’?Š™›? r?^€>κË?Uû4?˜™×?‰Eç?nK?sPd?;þ?ƒ;¡>×½/>…¤)>z+i>•¿‚=aÓÃ?fE?›í?a¾|?S˜?a×?‰0ñ?Ž|?.•q?7Çá?ˆhó? Y=•i&<ªï?";¢?›«‡? c*?Wý8?G†8?Q*}?85ù?Ñî?&×>í*_<”dÏ>Ã;û@ ˜–?A«Ò?z^2?ŽÍö?  ‚?£Þ?’ÐW?AY?pO?2•†?†*Ý@ã§?Äè$?.#E?#D?!%r?z¯?Ý_?µf=?3nl?-Ü?¤ŠN@î&?†å ?I‹Ô?Ð~>í ‘?F€Æ?˜/U?§à#?[²P?b¬;>² a>U‚=²Ò?EL?0»S>øÖ@ @ç?p×¥?–H¹?¢e?>²?n¿#>þï?ÈR‹? ¡s?™ö?LCJ?Êš?æŠ@Å7¶@Æîc?ùµŠ?r¡?dr? = ?3öN?Š¿?HQñ?I˜/?6#?¸H?ˆÃ˜?.v\> 2D?†PŒ@ Y@K¬?zé?(`÷?u4®?Á‰?s1 >Î~6?Nj?3–?ƒÌT?0ìæ?¹š?ÎÃà?€Š•?3d`? ’=?rÕÇ>ù&?5Ås?Oè×?n°‘?+õ?VÂ?6ï?Ë?™eä?yÿÞ?‹X=?©¡?s—Á?!ã‘?!»Ð?Gâ??Ú(?—Û?J?(’²?…&©?œÎM?…Íœ?<þ¦?3ç-?97×?9"É?e £?%0?_t?‡J†?eÚÀ?µî9?7¥Ñ?9Ê0?P&«?Î ?wý÷?R·?A:[?šeÄ?bp«? c?¤É-? ñÿ?98ò>éóù?†g>ŠËÊ?_£Ÿ?C¤¹?9Vn?˜Ci?pïÜ?¤…#?D ß?‘ä¼?Oh?šÀh?n${?J­?|Êe?(ä+?Œ}r? ÙS?ëGª?Œ3r?Ê®>é€y?V8z>ô¥+?=9?ç?-Å?j„Æ>ãwÒ?1Þ&?‘ú?*ØÏ?®û7?ïE?\áÓ?‡ƒV?"å?6@Î?—õ ?:+Ô?…-‘?s¤f?láõ?!C?„F‘?ŠÏ¡?†Iÿ?3Å?nœû?Æ£~?zÐA>õ@â?;½?¸x?Aæ?‹±­?Ϻ‰?ìæ?|G×?a8/?KgI?u“&??]h‘>ÿÕ‹?+S?— i?A„|?\>?ŒZˆ?la?Ñ,?SÐ?-y6? ±?~Äþ?(†‚>C>EÉ„>9‚M>-R>?'?Adö>±kÓ?dÉq?žùÊ?‡1?tß}?.?'”?MUÒ?‰Sk<á™f>á#?ç¦i?Ìɳ?U¸?»F?Šá¸?˜â¸?˜J?lÞŸ?8l>õÚÒ> £ñ?u?q@Ö2Í?Ùÿ}?:M“?2QÖ?M5?”—ô?~½æ>è9—?q8?ý@ôƒAAVG@hi?„;?®±?T?ÝŸ?¦&??7œ?jit?*GÏ?ìiãAN—@Ã?P‚­?eÀ?•ã›?ƒ{W?°®?+µî?oç?jQª>™É~= TV?,'í?ª»®@õú@ ¥@Öÿõ?¾è~>ãâ+?@V†?JÌ?ný˜?gͰ?}†&?ˆb?Xï$?B>l?Žç-AldA2¯A—ú@å ß@2³?-ƒc?e>ÚÚâ?€È?Rbå?“Aœ??aK?m¬Ü?aAn?¯Å?zM±?÷_º@^Õ0@)Ì@þf?Ÿ¾¥>ð°ä?"©Ž?™ì"?a}Â?a2 ?@Ö?Yž5>È>^>ë½?/°õ?lÚÖ?!¢×?Oò3?Yÿç>ý%?HÔ¥?!üÉ?W V?r˜?²¨Â?R{>ã\´?ŒO?xî¥?•g?+p8?tºÓ?M'~?/©½>ìúê?BR?LåÃ?‘7ì?$ê?åk?Uÿ{? Ìv?<µ?’{"?g>G?œLÝ?\°?~.–?‘?#?Tª]?OÌí?ò?6p?ºh?^gH?qÙ?r8D?Bœã?[?aË-?Ü?:l?Mq?GÉ'?à©T?<µ?´?’?•Hè?5C ?‡IX?`^§?§¾?Æ¥²?tr.?D1ë?ç?o«‚?&Õª?Z׿?Œ,!?lH?¨Ð×??ï¹?xeÃ?v9?ªå?ƒ0“?Fg±?O"¿?‘å?ƒ”b?#×H?ygá?  ®? [”?MÄ?¢?Ç?ŽØ^?€/?†þË?|3J?'-ü?6n©?XŸ!?uÜÝ?ª+ß?!¾`?vQ+?”¢Ú?=};?;xq?~ñà?,Éa?["÷?K=B?hV?CÕ5>óL8?Eõ’?S”'?üG?vxè?I®Ì?µrH? ý?¡‹>ÿ.ù?;‹á?9‹?Jt¯?‡ýÎ?ˆÚ>ìÐ?…éÐ?uç÷>èt ?Z¡ò?uêà?„O?™?ƒ‹?"V¦>Rý˜>ˆb >¢ô=°«h>qàq?6âK?‰Á?!Œ>Û"¢?_ˆd?gÌ‹?™ì? ? ?o?<·Qå>%×?ž·?T”>áC©?}ÆÈ?4öj?‘Œ?V?I?—ŸÁ?#$?UNÍ>¸D?&9¦@ê³Ò@›¿?A#‹?†!¡?BVR? Ë?’læ?dü%?P•Á?<Ûš>)ÎÝ?œˆA"O@R˪?unÔ?ƒ¹K?R#a?‘FU?€œ3?E¸?‚ªþ?ofÌ>ö?À¾úA*7@&p“?aSå?„±â?sg ?Zª¿?j ¿?<`Ý?YÁ:?ÁÜ>G5Ô=I€è?;¬ô?±?Ö—?ì=Q@×uÏ?Ø>7?]æT?'­}?~V>ÆÂ>ÕV?$ü6?3À?E×4?-Í@R@ä6A FüA ·Â@ÆŸe@@?ØaÐ?4Ï?aÿM?m˜°?–Ú?@ýB?Uس?X?)sÊ?f?¸Ø”@ î_@¶®?âZï@/?© ”?^V?Dh:?Râ ?Z.ý?€bø?+5„?‹^=?Hð ?†üÇ?)Ù"?fa?@¦?> ?$9?ŠcN?C‘’?&`÷?¤7ž?QJ¼?—ð‹?U;ö?&¾ ?>˜ ?V“Æ? ùÁ?—C2?…?A O?a^G?ÃÍi?g‘ê?Í ?žNf?†˜ ?KÆŒ?RRÔ?‘‚J?¿»W?sˆ ?¢ÖÂ?µ‘Â?žûÿ?}Ý?mo?:~p?Zhr?™yd?`“?&9˜?[±?>¹¨?DΦ?À¢?–?E]2?”Æi?“¥Í??*Sk?Õ9ï?F¾k>æÁ?ž·c?‹Ö?TdÛ>Ý*Æ?w¯?B€Š?Cá6?_¨³?„Šã?Aã?þ?7T#?¢ ?~2|?ˆ€F?j”>î×?@žo?«ý¼?n³‹?G”?`Õg?dEê?2ÐJ?`ƒÞ?æš>îX?&ã? ´(?”?yœI?…_?‚‡œ>ðË?,€$?–K?6¹?g²´?V¦³?Sè?Xß?”…?š¤Ü?@=y>²Ûü?9Y?3ÛÞ?g£?Ož?.A‰?[*>þd[?­¹M?5Ït?Oâ?´^J?9+?‹?Pêá?gV_?`ñË??ܺ? 9Ù?5¦?%qS?!ñ ?%?w¸?¤ËF?è? Ó^?[r—?—Œä?B8p??)ì?’×í?Q‡Ý>*å>>¥r>¢z–?-n?I?0›?ž_ç?¤?•L¾?*q ?aþÑ?“®?›»?=>=*å…9zy>? >XàŽ>ª/œ>[Mð?;ß? ½?i?†óü?Iš‘?+>?Cd~>Œk)?Œ-Ð?>+›=ê³#=£ë@>£Jú?<Úª?—5?;Sú@p·?vh??¬ A?\Kº?Q©?[R?¶ƒ?Ud>Ýzœ?&°C>¡çu>Ã<ò?gh€?¢Bá?ÚXÇ@2Ù@¾å¡?†ÙÜ?U… ?q+ç?nå?W¦”?‹¿‰?a–?0‹?AÏ!>$)È>åXM?›‡˜?ÑÙ?¿€‡@Ïv@æ |?ï/'?s‰ƒ?'Ì?€7¾?‘ù>Ë9m?“t¹?v·?V=豌>Š&¿?<z@=@7?f£?ü4?¼ä·?fiÉ?¿öu?l …?wn?)àn?p/S?,žï?µ¯?Ó?Jƒ@&@Ó†R@â =@Ñ?<Žv?LE???²Ø?7ð·?Î?›Å¸?z¡?În?@r?o?2'Ô?£êÂ?ûw@–?‰ï,?–i>ã0Ó>‡ã>½R?‹¾?=-w>¡d,?}¦?—s2?º?cD?{XL?F÷ž? ?O¬?LA8?nO>äÛË?BÕn>Ò?[D¨?¹ÓÀ?€Å%?q/ï? ?¼²?‘˜?„†?…9í?„lµ?Í?€N|?¢•S?"™Q?lŠä?HVè?V*?;¾î?87?°ÝêÚŽ?ŒÝ?Hñç?0˜[?^X‚?Žyù?AjÖ?¥cŸ?ˆ.?¥¤? +Ø?y7?‚Só?¤X?S%Ä?ˆâ?2yL?% ?6®ñ?W<? k?JÙù?Rã?„=Ý?S¼?<È?1®'?ƒÓë?œÿµ?Š\7?h2µ?‰©?¬ï#?– +?•!?KÁº?ŽC?‹$û?‹¨Y?my8>´Ò?„¹‹?È­*?Kó÷?†¥w?tqñ?Wño??š ý?&¶¥?‘?’šå?O™^?0Yš?ºÒÖ?¢~%?•e?7”/?&™1?3½Õ?Úp??‚¢?4þâ?Uög?pYé?ŽJ?&Ú?dþ%?gÈû?c ã?ouu?¢rÓ?TTÞ>æáÆ?µãþ?r1C?i]Û?x­Å?dÐ?v]?.^?èQ?“o|?",4>ñLÓ?- Š? *Ô?Û±?jYã?”è.?”"‹?'áw>ÓÛ}?AÅG?tÉ?CÍk>ú;$?¨Ÿ€?¡¥?LVü?Šp?)ˆ·?]ìi?€,Õ?IÀ?Â¥?A’ ?>rú?9ó¶?ºâN?•Ò?'m?2ÆŠ? ¸¤?*{Ê?HgÙ?¾1?º¯I?q0Û?-òf?/…‹?%ùº?x„>ã‡×?no?:M>üa?ºzä?^Â?¹d ?i£m@Ø@¶‹ˆ@Ö•k@³/?£öV?&¡?^üš>åƒç?PzI?Œ€Í?T8T?uG?0,?D].?>»®@ 9-@â5¥AØ/A>¤@ô7}@5Y¹?7¾î?>ÎÖ?H/?J€2?]Œm>Þ ¡?•yI?#?œ»-?:R?®B@ö¿†A'×zA7w@ê4T?ž‡Š?‹’>ãŠ?b̉?8Cä?e–F?B»q?T+8?—¯ ?§­Ä>‡*ž?úÈ@8·‘@ÙT@Éqù?ú+Î?BÛ¼?1$,?ŸÐ×?¯}×>þe?fVQ?YÎÎ?Ä5?Æ^®?-¯Ë?[‘ð?gøg?„Q!@:/W@'J°?^Xê?sb™>ŽÔþ?hŠÒ? ØŽ?@Î?kŸ÷?C–? ,t?!¿>Î’K?IÅw?b*!?mÓ§?ª˜—?g+Ó?s89>èX¶>µÙñ?‚Ÿ>Þú>èz•?›øÿ>´Nz?dk:?°” ?+dD>ùÍG? ',>ïü¢>âG ?É?–¡Â?;­=>¤Ò?j\ï?2—É?%m®?¤Êy? Ùi??Z1?‡Å?-Ƀ?Y;þ?š^?b¤>QA?]ÔX?ƒ‰§?[(ü?x ?&?¤½s?uù@?P#¦?^"”? ›F>ÿf¼> y?FV§?«¨–?„Î?X ?+9Þ?Ð3>íœ?0ÿs?],“?2ÞŠ?‡ƒG?ÐS?PÙf? ÛÚ?3ï"?R.>ð|?ŠÊ…?M% ?? >Ï·?#Éq?$¤Ö?R G?B$ù>ß]!>óÜb?FÏ?x˜{?"~ ?u¤?'öæ?à6?Ýl?KßD? öh>î’H?Ù!?Jâ >à^?Y“¿?!Óü?m¶ ?B‹ª?ï³>ÓÀb?l»ò?À¤? £/?HéÚ?‚iá?AˆÝ?,¶?I?w?E?‰K?-§?2©?j—;?c?FÁÕ>´O?&Ϲ?Wº*?iH?Cþ¼?J}h?Šä6?-ðî?!—e?Tìˆ>¢â>ðÏö?€ñ?kØ?¨<À?>b½?²ŠK?\:ï?,È›?o?0mé?;ï?]º?C©P?AÉ×?_ž/?d*s? É•?\E^?F*? ©?B7i?‚xÅ?9ø ?^!?)ôW?Eäd?cûÝ>çH%? FÔ?Ž'}?µP“?RQ–?Bß>ñÏN?(?jÝ?4:?ˉ?WÃí?…*r>Çg?QÔD?+2Ã?[]„?ŽÔF?AB&?1K? td>Õx²?ršH?R—M?yÙ?€&Q?_:N?¢OH?!HÚ?[54?KQd>ÿ;?Äó—?ÜÎC?F4??¢Ð?`M0>êµY?0(œ?‡€ª?š¸(?'sð?6à?\B?†Çø?`W?¡U@ð@SZÂ@î?ÿUK?uï>}_?ÛH?8ë~?X#?Ö‰?aß·?…Wø?pût?JÄ?°Û?([Ã?ÙH@ãY@}œ@ õÝ?#ø¬?„~¸?GS?B‰1?tGQ?Eb ?IH ?XtÐ?M?¹ú?eþ>óèý?º‹?¡¿ð?ÿê?iš?€‡\?ß_?8±P?r½ñ?,XÀ?£È|?n>Ïý?Nñn?~s?|it?H o?T¶|?o£…?Š÷_?YÔM?Ô!J?Á >ãWÙ?Tô?XE?4[Þ?*¨>ýÑÒ?3>ø? ã5?“™?Z‰U?.Ó§>ÒϽ?0Ç\?­Ø >©óg?Ñ ?¸`CZi-?ÄZ]«Zâ?ͱÐW\µ`?ЉƒÅçI?Õ%´ÁX`û?Ú.Ø)ÞÞ?ß`¼±N6?àR±ô.—?ⶆ¸QD?äÒNíÅæ/?çáÝRî?é^Có…­?êÌ2ÁL?í¬L~3’Œ?ïúAR±Ì?®úR*qí?¼¢£ÓÔ¾?ÀM%¹–Û?ÊI y^?ÓÜÒ0›Gw?ÕÎMùE‰Ë?؃ƒv*C[?Þ–‹(ØÏ?àÔÒèP.N?â¼RÃ’Š¼?äW!ðET$?æ6®"?è‚B?÷ƒ§?êjp³³?ì­bÿö?ï_,VJ­?“qº[º?¼œ£ãÓ:Z?ÆB: 0³?É(Ç‹ÔPD?ÐÞÌ3k˜o?×?èÕi?Úæ˜ž@?Ý Üfç¢c?á½´«‡æ?âÏZÞG}?äï?™ÃÁ?çS'ãàûŠ?èíÏYh?êøÓ(ŽdY?íèÁa‚Ó?îùG:—–å?˜úÜ3€Wc?±¹ZÉ?Á97êÊ*?Ë¥\ZÏ ??ÓãÂG&«?ÖZ¾ìtÅH?ØÊ;â€H?ÞÆ?àÒ¢†?àÛ&éÊû?ã&‹ƒS L?äÙÒð$‹¾?æ5k“?èÆzÉ‚°ž?ë ~¼w|?ìøo2¯öŠ?î] þé5R?¬7ND a?¾{gǾ®æ?ÁlKСP?Ê/ù•TiZ?ÐÍÄ ­þ™?×e1Ëf»?Ø¿SÅÁ_Ù?ÞÆr Ã?áUB”?ã;ð…#O?äµ¥*–ô?æ’W |?é˜Øo/&À?ê'Ų&?íN³ÞÊU?îZ ´g?¡x7™ tš?°~+—Q_‹?Ä<­¦çw?ΩâLS†ß?щ­„ë?Ö‹ot>Û?ÚôÈ#½Í ?Þmo>Ñ?ážvY?â áUD<?ä¾F»Láñ?æ+õ•¦Ù½?阀nÓ¸?ë7›¨û*G?ìbÿ1Äó?ï:¿³;Ñ?ˆÔ³‘?-?²u/pŸã?ǽ´›ö«Ž?ÍÿA¯óç?П«µ!ŸN?Ô_Ã$i*?Û·‰ß‹ˆf?Ü¢S½½0K?áR;ïýWb?âVÕòNýD?ä…eÊŸ?æ1j;z‚?èàù U?ëÊ0ÈLt)?ìYÉêjò‹?ï¯?Ѫ/?OŒ1Û?¾+F(–·~?Çýôç¢L´?ÌËn}ÿR˜?Ó‘á˜/‚^?ÖZ*Ø#?ÙòfbÒ²ù?ß#pÖ¡?áØEÕs›?âß{ s•W?åê´-?çOÞU?è°ö˜e[ý?ëí) ù?íIƒÁÇvV?ï¸\£œÖª?–RÑTÿ,?¾ƒf‰‘‰?ÄLÉZ¶¬?ÉFùö‡&?Ñ}½w;?×ZEve?Ù®fUÊ3?ÞœÏä¶ÅÔ?àðÿDÇ'7?â«¶¨P_/?äâ¦D?ç+s”7ý?è³êžX‚»?ê&-æŠý?ìu¢ C‰–?ï‘P3K³ ?N´aÞ´¼Ÿ?±x«{&»?ÂãztÍÚ8?Ê‚‰Ú&9?Ñ‹}˜ cZ?Ô"Zß’'Ì?Ú’_áø J?Þpwªææ?á´LÚ²ÁT?ãDoóÑ¢K?ä2ÝÇãZ?ç s^b?è8yª×8?ëyÔÃ…?í‘5Ül§?ïF“™‹½?÷}R/ƒ?ºœÞ³' ?À’u©½J?ÏÜT¬d\t?ÑqEh1?×ÞzÔh?Úc7›] ?Ý6mM ö­?áÐñ&Ü?âõ‹uÄ%?äô‡O?ý;?çx 8!º?éu8ä1¼?êÑX+…ˆ?íýáz@±?îH”¶ü4?z¸²¦OË?²  Ð÷‡C?Ãê˜_In?Ï¢û¦|ñ?Óó:„”Õ?Ô#Üòn.?ÛƒéÞÊb>?Ý<Å/áÑ?àÌËÉ"?ã 畀Ƥ?å^L#‹N÷?ç˜pvsˆï?èÇ®½MXå?ëFÇ—gc¯?ì áî0?ïÚïzBÇ?8Ûl{ò[?¾@Etl9?ÇbÃc'#Õ?Αÿb?Óc_c;–?×Ö"hc‹?ÛuIÄ›îJ?Ý}˜Es?áD°— |?âiÊlë?äY~4Ô?ç̨ڦÞ?éô‘=?ë¡P:Ÿ§b?ì>ešÈÁ?ï!1ÙîB?ž¥ÞqUe?»Z9´ïN?ÆuÁ™ ½É?ÌS={¶¿)?Ð":¿+eL?Öå({;Nj?ÙV=d b²?ßТŒ ñM?à,iËöš=?â>ùÿ68Ž?å!ó¯=2'?çã©]ö?ég3éÞª?ëíæÐ!?ì€yëXœ#?ï‚|H_?•ÞÌÂD–?µ`ó„´Ü£?Â?™2ƒò¤?Ë!› £ç¡?Ó ÈYŸÿ?׌‘»W1?Øc»|jxƒ?Þ.þ8 P?àÕƒæÛ?ãDwù4áŽ?åÅ@è ò?çTkòDò¦?érh3¬¨?ë<ÃŽgßÍ?íq÷ä§+e?ïÄX‘¥®i?ªWÇaÞ¸?¶ßÖü{gÝ?¯Z®3+?ÏÓ+€oÅ?ÐQË..?Ôt›mR¶?Úx¯'ÉW?ÝÔ>R:ÿz?àMÁ÷V­4?ã Ëq¢ý?å#w—é"ƒ?ç„€Kò™?é¬M£-?ë5Ó?ퟄR5“?$–å÷®?­à²½m÷?»zÍÆãl?¯š•¹à"?É ç@ÏL¹?ÑHõ„€?Õ«•².a?Úïä—1?ßÑ—œs?àb °ÉZ?ã¯ØŸHq¡?åÎÐߤv?è AY°‰?éŸuš·±?êQEÕÛqw?ìžYä?ïŠ(O·”ƒ?¡O·ë˱?±êN$¬Ór?Å'O—…Xˆ?ÎßòÀšÿY?Ó›1}Q;?Õ%ι,Þ?Û:pÍÖ•?ÞOîä÷â?á{g/ 4?ãÔ|ç.@Ž?äø{,Ÿ¯€?æÆÒËß …?é)¯Žo²?êepy•X?í ŸR4^X?ïï­kŠ?¯ÚïÔ¶ä?¾'É ?Áù)ÇÀ£?̳þ–x1K?ÎÝrÈ‹x?Îzò©¹Z?Êf* ¢S?ȳ6ÀÖ#:?σ´lX?ËrŒ!W_•?ΔøjV?ÉÞy#Êø?ÊrZ@i[?Òßø^êñ?Ц+ºÿ÷>?Òõ0='tw?Ó˜Oœ5³?Ò¬Ÿ°s)?Ñ‚lr·§?Òé£Y¶?Ñ÷¦?Ÿs|?Ó2ð²½dC?ÑÊÅïLAý?׫éj+61?ÛàYÌùþ?Þ^W xî?à&™m‡x?â¤.ƒý?äâP^ÐØ?æÃ¢ÁêÚ?é^où7â™?ëot ²¼?îYV‚ Ð?ï^G¹?‚,zëËÿ?»^¢¦ï»ø?ÇðU@6Ü>?ÇXäFæ?ÂäÛ£ˆm~?ÄõŸ‘úÔ˜?ÆxRˆ¾½Þ?Á×ùdþ…>?áí ëa?À&"—iÓ?Ãè}Œ¿?Áh£_G?Ìžnl:™à?É7ÁâÒx?ÊpJ=ÍâE?Èx¦iðat?É,7Ãáò?Èì·P«R?È€Æ{¯?Îlâ}ä?ÌqÞ~[:?Ï`ûlíˆ?Ó,ð Áqn?Ó»ù»ô}?ÐFóIÀ\?ÑmvuKm?Ñ ÌwÝ2 ?Ñ„þbØ?Ð+Éû±ù?Ð ·´Ü?б«ÎtD?ÑtE ÆY?Ôyƒ)¡Ó`?×+¸]©?Ö.†|Fþ?Ô³¥ˆs?ÖK6»- P?×l¨ß\v™?Ô8R Ò>?Ô® †XnŽ?ÕÇ¥³Ñ°`?Õfdé`$?Ûqêtï³?àÙÖý?á>?+ìÉ?âkÉÿHn?å€ð6ðIö?æ»ú±ÏŠª?é‹a_J?ëú92^?ìYuŒÊº?îzÀ—?£¡=ÿž˜ð?¾Ç´&Aõ?Á$íDw?ÃPm.¬,?ÁRØsîˆ1?Æ"!ÃðfR?Ágðl‰ê?Çã•9ŠSŠ?Æš‚Ú-Â5?ÄF.Zè.?ÃFÜF²†³?ÆÁó ïÕ6?ÉðY=¼.?Ëļ´%e?Ìv> Ó?ÍUÊnØv?É9gà(é"?Ðr‚"0K?Í7?„¦?Ê+y­D·à?Ìm*ñê{?̵Η?Ç£?Ð’[¸Mt?Ñ päC?ÓPRÖRß?ÓÙ¡ðë_?Ñ~ýYâì?Ó Ð^FÁ;?Ð\;%á’ö?ҞNjÛðG?Ð[Ó$(ãí?Óh‘Å=8?ÖK¶©¼?×´€ã§?×dÐÜ#þY?ÖhæäÁ€]?Ôâ•s¸?×çÞñ¦?ÖûÀìÚ?ÕŽM?áew?Öê‡åÞÒK?ÖÈ7½…Òª?Ùk4í^±?Ý£=OñQ?áSL (Ô?ãsøG·?䓎Z}#¬?ç÷%*¼£?é“f¹^Î?êA­Ãîuª?앞r½§¦?ïL§ïÉ!Y?‘uÏYdK?¶ÁXÞHÅÁ?Â/Ùç­VÝ?ÌÝNÞÙ‘Ñ?ÏAÀ+Õe?ÊcÊ dÇ*?ÉYxÚ«.?Ì_£_ô?Ê©šŸÆò”?Î$ÁSP«?È‚y<Æ?ÉXóqrz?Îݹ—Ü{?Ð#’»v‚?ÑÚŽ[¢h?Ф»µºÿ•?Ñ5”Ììa‘?Ó…G}¡?Ђ#nÙ^¿?Ó½aÇ¡Jõ?Óª1"Yã?ÓO°ë|ÜŒ?ÑvEEæ?Õy1ŠI?Ø&"`>o©?߈¡HXù"?àmæ N€?⼬ÍUŠ?åCcÁ X•?æOó`¤?â@1è5Uã?ä5xW.F?çáÝn¹Q?è—cg„C?ë —5›ƒA?ìíŽéœöÂ?ï"CZ4|Ÿ?¦µ?Š?ê?½Þ¤áz7×?Åw™ÏáÚ?άbDS?г[ä­+ò?ÖVWUG±?ÙÒNR¦ˆs?ßÈá*Ôe?ಅúÉF?ãÌ<ý*<²?ä £Z R?æYò=?èl«@Ò?ë?5ûV?ì"Ñ_„ÎÐ?ï{\ mt(?Rr\Å€ÖT?¶ÿ6Øüæ·?Ácw{®ÂÃ?ÎÔ"•® U?ÑlUXàô?ÖÏ¿¸q?Û?¡?á·Î?Ü/«!^w?à/1Æ‚U?ãä…+ü¶Æ?åA/ØçÈ|?戚\{Ú?ée÷}‘n?ê›êZâˆe?ìÿG&<?ï8+è`‹?ª5‡Ù#Ä?µG“ã©EÔ?Â#ÉÜo1?Ïâ´¹À[e?ÓË2DV?ÕTu2A‡a?ÛHÁ3"Ïä?߸*2hæÉ?á+Ó²A~û?âÿ;SkÖÑ?å`øŸl?ç’Øfþ×s?éÌn¿Ú?êÂ( N‡?í $cŒ®¯?î3¥È†ÿ?¤Óü‡žI¡?´ôÓ2Z?Å/ tB?È‚æ“?ÐÎŒÓÀÅ?Öšò Û?Ø·åÔø?ÞoÇt¬]Â?ᤤ™ÜÛ?ãÅ€Ø2yë?ä…âTºä?ç8¯±ˆÒ]?èÉ ÈÍÄö?ê éB@9?í%Â#=?ïéi[ÿɺiš?ï;{œôí?®j›M?¸j›Ï×!?Á ¨Â>¬@?η²|øw?ÊÇâ™øm?Ìèÿ‘>V?Ⱦ6×çœ?Éèù²¨Å?ɨ¨¢.(œ?ÍáÞ wÊW~?×:à}EÅ?ÕÕe¯Ïúv?Ö­Ï_ëúÊ?×ÜÑ»Üd?ÔÏû¹©A?Õ/\lªá?Õ5,ŠóÀ?ÖTN­Ì`6?ØEb›36ï?ßö‰ÿ¡\ ?áû Móz?ãìðmE?å–ðepOK?æÏ*Ö°dá?èÐÆÞ8ë?ê†ÎM‚þ4?ìØ2þHêž?îe~½Ü??¢<íXf?»4¬žJ£?³ÂÓÛH5‚?¶VY[ü?¸!“fS?»m&ê5«?¼Xe3dîÉ?¸”=„Tij?·YûwQ?¾g‰†à?º?!ùNN?“2üï?½Û`êX]?ÇËÕ2e7?ÀoV×ö¿÷?ÃXÜ¢áÈÂ?Æuò›1Ù?ÄqÞºóŹ?Ãæ-¬‡rÍ?ÄzNÔ´Í?Ã Ì Ë]?Èc6RŸ»a?Êâ[&×y?Èðçg+‰t?ËBëàø×Ö?ËC;à*Ö€?ÉOH QV?Ì aFŒ¬?ϯSôC?ȃ¶”ºpA?ÏZó¶iæ?ÑÉåþò?ÑU­­Ôð?Ó,˜‘ÂÁš?Ñ“E}¸Tc?ÒŽ_bO:«?ÑEìéj˜°?Ó=x°e»?ÓÚIÛúæŽ?Óf‘8ãÝ?ÓAN2¹Ï?×wÐåObæ?Ö(¦M‰|©?×Ý™²¹úr?Õå5Î`¢?Ö’ŸeÇ?ÕÜM·ÉQ³?Ô%‚sžA§?ÕÅ­‹fK?ÔÊ«ªNÄ?×h/wÂj?ÛWˆ‡µí?ÜQßhîá?ÛØ!í`‚?ÛîUHž?Ûë©Iœk?Ø„Jçq«^?Úù÷Ú–›É?Ø“ëvKõ?Ø‚êåÃ2­?Ú¿÷cËñI?ß¿!~m/ð?áþuI"k?â˜BNîÁ?äß.ïo?æeÞŸg¤?èñÏ"PÏ`?ë¦tšSåS?ì qeæË!?îÍß™Æ?¢Y=JÓƒM?À–8Tô?ºŒ‹‰Ò8?»nC0ˆŒ˜?²â’oNŽ?¿3ЦcâÕ?¹ö W#Ge?¶‘¹¬ó £?´×¶?m°?º{¡”û4?³Ä³Æg®Ø?Çü¥†’s?ǤDà”–Â?ǃÂGÖ ?ÃåM¯Dn?ÇtÆ_\+?Ä€Žçî§?ƉÁÜÍû?Ç—„Ò¾M˜?ÆLrXE´å?ÂgêÈõ~?ÈqftÀ»+?É$WÄú?Ï+#SŠ?Ê8 Þ›?ÏÖäŒÓS?ÈõÏ0$H?ÍÐ9¶å?϶$\âè?ȵŖ~?ÍTïÍNÎ?Ò^Oÿ»C?Óró¦}?Ó/‘³„Â?Ðò¬•Ó°?ѹeÄÊ»?ÐñD8ó?Ói±RCè?ÒO~àÁ:N?ÒÛ—òðŸu?Ò1Þ¬¾µd?ÖÖצŽk¦?×@¸o0‡3?Õ  B†C?Ô€‹tÛ6?Ô;™"kÿ?Öo6݇Ê?ÖQþ ÇóÊ?×N*—è?×Û™½Ç€?Ôs h¶ç?Úʧ‰dœ?Ù¨ý ?Ú©/½¿?Ùel—E{—?Û5€Dø¡˜?Øh ³ùŠO?ÛèÃâ¬%?ضCTMñ?ÙšM !:_?ÚRÆc¦¡?ßdÁñš¨?á‡ì.>¾0?⦲nÆ?äô }ð0?æ15˜…°+?èt6&w{_?ê²Æ¬ÆûJ?í1ƒ»ô…?ï³$Õgf?£½[1éò?½af2·b.?Ç:¤h>¤?Å×1N«âÜ?Æ/"Z0„?Á¸iNVÝX?ÄßÏz”†Š?ÂqJÕ±$?Äòo¡R`_?Ä`îz£ -?Âcj¾õf?ÆÉó;¦®?Ϋc[Åm?Ï×$¡uÊ9?Ëèà~™?ÏlãÔg.?ÎaáÔÒ†‹?ÊÞK­%?Ì<}Áv?Îèò×ü| ?ÈåÆ58R?ÎD›Ñ+ ?Ñ g.i?ÒQ.åp,Õ?Ñ6DȰ׺?Ñ‘Eu²Ê?Ñœ™•3û?Óˆi;§é?ÑÐí­<,?Д;’Àò?ÑåS•‡?ÒÔßåˆå?ÕœM<¿?ךùFo)?×%ˆH=Ø?Õ[d¿c?Ö¸ïoÆÂp?Ö>öwZ³j?×Éq|êU~?Ô«Ûlâl]?ÔïëøÊ »?Ö;ÖqI0½?ÙÐu„ˆ¸h?ßøúéÚá?à—Fv¶?âö“ôi#?å{ˆ.&ù?æÑrÜAò?èåû ‹÷?ê9eµ‚¨?ì­ê§AýÙ?£ÑO?¢¹v¯Rù?ºkÀvÏ·Î?ÁG¶®?ÈZ#ï´&?ÍÚ É›á?Ëd\ѵS?Ì-͆¤?ÉÎ(éÃ$A?Ï£Ëíê?γ’s¦~?Ï1ÓbyÚK?ÎO²å !?É‹˜_ ™Ö?Óƒ±>„¾D?ÑWÅ M¬y?Ñ|iËW?ÐHKdé?Ó´¡±€©?Ó–iy—Ÿ¹?Óè}ï¾3?Ò’—qyF?ЬSÁ›Ç÷?ÑïŽ4Íš?Ô â\‚åÅ?Ú˜§Q¹sS?Ý“­D9Ð…?á9K˜™(h?âˆ^@ðÁ9?åvà)! ˜?æèS c£?èZþœ¨?ê+–Åi ?íµ”­ŽÚ?îPMë’Q ?¢Âz8Üê?ºÇ_Ç¢¾ ?ÇÆäàþ‘g?Ï0Ã\d¼:?ÑuþiJ?Õ$¼›”yH?Ú„7OÑɼ?ß*hÁ xsCl?ßÀš8Y­k?àqTž?Ò#VÇÁ·?׃Ѣ¤?Øqë‚Sü?Þ¯xÒï?á3ÁS¼?ã,—¯ ?åb~ P?ç%‹À$4?èB™ÀÐ\?êÊJÀZ‘?í¾šaÀ?î]Ûܶf?–ÚR ÜÇ ?¼rM_¥Á?Æxñ¼–G?Ë:kSèPT?ÒÒ&´Õ³?×6An¯‹?ÛP»½Ý€?à\VX?ág*þ”|?âµz¿/?äú@pot?ç¤D‡Îí?è7 ©¨kO?ë1s©ª7?ìáWé{?înÿ”°O?¥-=S)AP?³Y°r=s?ÂËzy)ÿ˜?ÉÎ(„O½?ÑxMlØ‚‰?Õµ%ï%>,?Û³B–dù?Ü:®1f^?áh˜²K??âÑŽò–rm?äTÁõŠ*m?æ©yZ¥á?é_Gò0gM?ëõõ,âc?íYÃäŠ?ïG3·¿šÔ?…Qz—M¹î?´.’9ÚLH?ÇC?|E?ÊVŸêÿ)?Ñ1¬ÝR±Ð?Ôî\XÃèk?ÚS'&° ?Þv¨Î¬Ÿ?á¿{@ÒÅ?ã‚|L$q¾?å \Ó’?æÙ¾òr†Ä?èlž¦)†?ê(!„Á¦¥?íŠXMê?ïÔläÔ ‰?¦ô‚˜ˆ?¶ —!ûË?Çqä'ÕÎ?Íz`ìú^?ÓGØóëöÐ?ÕU¤ù£CÙ?Û®q›#¾w?ß“AÂð?áÍœÉ|¬ö?âœÚm{tÚ?ääïçz?çt#ß–?èÇjÉáJ|?ê;m°»êÒ?í¯T§þm?ïNoçn ð?¥ŽB&§ v?¿œ Á`O?ÃE,_'wp?ÅNð($L?ÃdüpG?ÄÉFr;?Ç~ô™*âÃ?ÂIð¿¾?ÅÆñ;e1Ç?À!eðŽË?Äe^fe´­?ÃÌ8R!Õ?Ï;ãyu?É×£â²?Ì5½žË5?ÍâäÑ;ô?ÌD½ÈË=–?Ì—nj¡Ôv?Ê'¬¢ÇY?ÎSá¿ñÙ?Ë.{¨¸ãú?Î7‡ÖHO?Ð>²òÿ=Î?Ò0¶®5yÄ?ÓÅy¼r’Ÿ?Óÿ:1€?Ó€wÃF±?Ð8zç@A-?Ð3:ÜþŒ˜?ЃÃx"èØ?Ðдdßò?ÑÁÕáLŒ“?Ö<Ž‘Ìç?רÑS\®A?Ôv£²"·?Ô:š¹/¢?Õɽ¹œ±[?ÖŸ|CT?×"ˆM4êì?Ö¤oaïÑ?ÔDR´é¡§?Ö k‡Žx?Ø1"o° ô?ß`xûÓ€Ö?à´²2Ñ@?âJѽ-Ç™?åBϼ¶a?ç ë|ì)æ?èÍ2ÖVüX?ê.IžS2{?íªpªïâþ?6kšÆ±š"?”»X7™«?°s­}ÒÈ?´}U†ƒ'?¹C_)—è?¼Ä夕2¸?¼×e® ‹J?½âˆ}H/?µS·,¿™ô?³$2—îî?·z€×?¸óÞ8ø0¼?ÄsÊ6ªY?ÅÈA[·æP?ÅV ~ßõ­?ÁªEȘ?ÆÙ³jj(?Ù}7£(?Á h)šG?ÀOµ}@?ÃòýÁ›x¬?ÅV I_ ?ÎM¶^µz?ϱ„_šbâ?Êé„q„H?Ë‘Ì{\­?Ì”^kò0?ÊðKEwâ«?ÍTÂFQ?ΟÂTE@8?Î~¢»ß ?ÌxN6h¤„?ÒˆgU¯]?Ò*†žÜEÜ?ÑÐEò3Uö?Ñë–(66+?Ó·ñž–Ûj?ÒŸ¿RXJ?ÒßFF?Ó•X»ºY?ÒÍ—Ù™Û?ÐMƒKë?Ö<^~+g‚?Ö¶Ÿ^²„D?× I%CP¨?Õ!ÄSO ?Õ¨ÍSk‘ó?Õmáÿ>?Õ&TZØÜ°?Ôð³ù ¾9?ÔÒZ+b?ÖYÖ®Ñp}?Û{¨ß-§?ÛI¨^|ž0?ØuÈ^þN?Úп€v9—?Ù Óó‡Î–?Û)¨D­úa?ÛèAÌ=ÜÆ?ÛõÁÛW1Â?Ú]üů”?Ú+þ3,oÁ?ÜÞ«ßÌ—?á´P‚ª¸^?ã¥TyÒ´œ?ålx.ì„?çÛD¬.G?éW\Ï©?ëÅØ<3Ç?íÙ!:…î?ïïÅN Äe? S:‚^ ?²JÑU*t ?ºw¡† ]?±’Oø§E?´Ã¶1Ô€?»É$ Ãs?¹:ßGI©ï?¾î Lš‰?»7Búfï?¶½zå?µ+wÍo+?“[(§´?ÂI*›œ u?Áþ:ÿ‰9?ÀaFå”8 ?Ç|ä4Ýû?Á@¸—Û ?ÂBŠŒCø?űñ,jË?À6^ <?ÅÃAKñóž?àÔ©‚¯*?âdeâÆÚ®?äÜžè^ ?æÑ²Û6˹?è»®¶‡M?ê©þœ^"Y?íªL³0‘?j* ?©Ü\{ˆ?µ¥ø*B?¾ˆ¤» ?´Ä67n?±[ox–Zh?¹?í‡52?¶BRA?¸ëþ³h ?½õèqÓŸ?³©´)eÎ?±.툱ö?Á…ù#Hžs?ÁpHõO~?À”—HĶ„?Áây×£K?LjD´Ïf ?ÇÊ%2y:Ý?ÁÒùºl]U?ÁËy«¹ã°?Ç@´+tpð?¬k^cÿ¦?ààöÄÂ"Ô?ãŸäf'ô?çS|l&‰?èÝZôÕ*œ?êp²Úƒ6?ì”X¶ö2?î{ê,9Öé?d NWÞ;û?»§Ôß‹„?ÂüÊÈÒ˜5?Ìï^ÉNLÿ?Ðå”Feû¬?×ÛrEózÙ?ÛÁâ®ÀÍ?ÝŒ¹wi¢?àm)bR)?â(é¥ &?å›j°+¦?ç%‹cJ?è©¶‹·Ò ?êwf—Øx?íäðZ•¹?îÌÅÿý?œÕ^¯~šð?ºÏ_»ÔÊ?Æìb®Â³È?É®ˆ:eŽ ?ÑY1˜¡è?ÖF®uŠÈ?Ø“Îð_Þ?ÝñF…xC?à¡Ö•…B÷?ãe¤\Çv?åõé3S ?çIïÔ4õ>?èJÎŽ3?êj†]?ìÚÎÜb€ü?î™ÊZH¦^? ‚shI?±ËbîP?Åå0žÎÇ9?ÈçÖ­]/¯?ÐYû.ÚÔ0?Ôl›`¢;E?Ûfég›~¦?ÜrÍ÷ql?àå›äÛ?â: Çk“?äÍbé9¯ ?æDùÏö T?騠ä;€?ê—v^zç?í f›Õ?î'At¼Ú?¤)»øhµ?ºÑ_0ÃQê?ÅMoï!i?ÈWe¿¶ ?Ò˜“+?Õ›E¿°,3?ÛÜ*QÈ7ƒ?Ý{½öô?áþLm$!?ãË^l-©?åûeLô?çË8Óã˜?èæ×ø&œ?ênžQ¥Î?ì™b Œí?ïÁÀ­c ¿?sŠÀPe˜?²EBi C?ÀÅæa=Ö?Î%A:yÌ?Óµ7 ?ÖÐø ¯»”?ÚyÇ~ˆ./?Þy€ðij?à™qæ¶×?ä…Obs;?äbªƒz?æÌ"Ø0Xf?齯é'?êÃb·¸û?í06t?(Ó6ÚX c?¤Û=bw4ú?± ×Ów?Ç-ƒeì‡B?ÉåhÙ-•˜?ҠߺQs9?ÔG{=mZ?Úú0WÜrÕ?Þ:†ãcï5?àùG_bk?ãtÜ)ænA?åßdÿvª‰?ç²L eO ?èšjne)?ê !QsÈZ?ìBiµf?î áNç?¡?’¯Îo}Ê»?²‡‘§¤À?Ǥiý‚!?Í—ðG¨2±?Ò³ßÃa³?Õ¾¥ÍÆuê?ÚÕÇõêÔË?ß@ ÑZ%Y?àƒ¶-ØzH?ãöq$ê'?åKԦͯ?æ£*‚:Ò?èõo%T"O?ê¢B€eYª?í=k¶·É?ïÉåÁ…Ü?¦IÃ¥ÍK?µ€¶,ª’t?Æl‡R˜ò?ƨòä/0æ?ÀÓ7™ßèü?ÅøatLéÕ?Ç…ô…­‡§?ÇÃÅÆ„å?ÅÉ‘8ºïz?Â×›¨â ?Áj辆OÙ?Æý3’á; ?Êí{8?Í?Ì­ZÊÚ?Èï7COÉ0?ÈáWCn5?ÏŸô=­¥?ÏY›|·?Ѷ½Á(}?ÐÑT o¡2?×ÙbÏ¿?Ô 2<˜þX?Ö˜ß.µ ?Ô<2—0 Ð?ÕVäµ÷5?צÙ4îh4?ÔLú¸âÐ?Ö÷'ã÷ò½?ÕëÅß‹4?Ô.*{ãBC?ÛõÙ½ô?Û±Vá­¥?Û8ÐW7ë?ÚÅWqŠ‚m?Û60>x c?ÙA´\þ ?Ø}RÓ½@?غõÝ7?Øm²Æa(É?Û,5y¹?Ý^Ì–Üy?áp×þÀ’3?â y]Þt ?å/W‘Ÿ`w?çin«?èðë!° Ê?½|ç€+¤®?ÇBô/\ã»?Â5jxŸ?Áá™ÐÆ,ì?ÅŸß¼>?ĪÏ7è2?À¿—¡Óc?Ʊƒü2?Á†™$ºôì?Çùå#Š5?ÁðÉñ@¹Ï?à0VÈ™†?ãálë^ÑÌ?厠P7’B?ç_÷ûVî?éÚˆû8ñï?êbz ¬bÜ?í¯0¾ÎÚB?îÀÆðó½ ?Q{°Y ?°<Œ’¹e?¿jõ¹?µ®7íqRî?¾“ÉJ”×Î?³\Ó €«4?±îXÊ]±?´âÖ¤Þ×?¾s¨öë?¾rÉöÜ)?´ÒV Î ?À]æÍ”I?ÂáË»ßü2?ÄB>V<[?Â[©Ùq?¬ûPvñ–?À·§rFàº?ÇÌU4ˆr ?Ãýíà3üx?Æ ÑÙ\&?ÇG¤5>.Ÿ?âÝ!h 8?âî£Ç=?äIù¦?æ4Ñ høŸ?é¼¼£¦?ë´œ¶FF?ì“–z'&d?î©€×a)?¬osnÞ­?·—ZÖ\€'?ÃÞ]Llt?Á^èg7~Z?®+ 9A?Á ÈùÖ?Àœ—).Îm?³ë(‘“?™ùY^´?Àµ—NÏÏ?Â諊äµð?À‘‡Md¼?á^KÛ>¾?ãÃܽ!)?äûß0, ?æŠ6O§?èô?&iDíè>9?ì?½¸»Ï?î;ݱ¡*?©äFÄ÷­v?±«­Æ%ü?À†ÅüÞ„ò?̵®`‘?ÓØ‚-JÕ{?ÔéÔPºå?Úd_HhŽ™?ßêòh&T?á ÛcÊêª?ãØŒúïc?ä£zŽ’\­?æœZzŽ ?è€Â;¼Y?ëêYX*?ì£vzÙ¨"?îÕÖÙcwÐ?©T¬¬½1?» ߬í./?Áe×¢F‡?ÌÔž›î ½?ÑÓ.%<òK?ÖA— $²C?Øö{ߨ?ÞH/+>úí?áâa:ß?ãõM7n<·?äÇÂÛËån?æ- Ÿj ?響… k?ë¿Lkä.?í¼4¥.R?î( xZàê?¥zýËâ?²J ë¢ê?Ã-û ï?Ëk»ÀC?Ñ‹–JSÅ?Ô…£”ÿm•?Øà¤UíÔ!?Ý8}o?à°´2z?ãoà/s3?ädªòiZ?æ‰jX!W?è$É…•Íñ?êÝi„—S»0' ?å.£GmÅ?çÛò¶Ë?èÅÎÅû>"?ë`?õE~?í‹lYê?ïÀ„»(¥?«¨Œ™ìu?²Ô¯Ø'¿Å?Á’¨‹¦îÓ?Ì(½\VÚ?Òf–>ôë?ÖXoû?Úèp"³£Ÿ?Þ;V¿­v¶?áüQ1ÚïU?ã¬?Z&q?ä‰öU]ªˆ?æ6¡­¢ d?è]]?êlÂ¥Wç?ì[ï«5I?ï›ìxnÓ?~®å~n!a?´)¿ °?ÂË{hPÜ—?Ëk–uø­?ÈųQfð?Ìï *5!?É®¸Í)3ü?ÌH-Õ§û§?ÌÇN½7 D?ÊÞÊö[6›?Ì:½ $$?É¿ÈËÚ%3?Ï€#ü~¥?ÐB²ú§C?ÒÕô^gy?Ѧí¯F@?Óìr 7ýŸ?Òö˜+E’ë?Òãá-º?Ñ€]d®×l?Ó(^Œ1>?Ò+v£àÍ?Ñ\çæ?ÕD”¶o¥?ÚS̃™?ßZøšû?ឤ`¬!­?ãx"g³÷?åç[q?çC_üJ+?éHŸÍ¦õ?êab ŒF?íÇ(Ü>š€?ïÅ8ÜP%w?¥!BN@‘Ï?¼×¥¶ªJ?ÂKš‚N#O?Æ#Áé”3é?ÇÊ5*tÅ“?ÂÚ)ឬ?Çó±ëÒú?ÂåK•?Z?À+v@.«X?ÄóßžE×n?Å;€EŽ+?Æìó|˜¯A?ÊÐ+ Ub?ÉeM'K»?Îx2 \W}?ÊSêJÿð?Èg–]#6?Í VãÏ?Ê*’!Ã’?É=Gúγ?Ê‚pÌÐ2?Ðy{bÂe´?ÐjuI–?Ц›¸Šñ?ÑÉmã+A?ÓïzÄ…>?Ѩ¥Õ– ?ÒÔÿèÂç{?ÑÀíÕóÊ·?Ò¹7¯ºã’?ÒgÏ­ü(?Ô“[>V±K?Ö´jËog?ÔêV˜?Ô b:’s?Öó?íâ­?Ô~ ö a?׸±\Xo?×Íцóâ?Ö$–UÌF?Ûrò.ÅG?Þú;)ì?áh³ðÞà)?âý+'‚Þò?äÈBÃÔß§?æçó …??鎨^=I{?ësmíÔ?ìü[GÕ?îüW\m~?F–/û²{Ã?²ÓѾiQY?³´³Ê;Z?¶*¸¹ËQ ?·ÚÝ?MŒ?±ÎÂÎEø?½Æ;w?´5´àæ»3?±R®ôU9O?²:p°9Ÿ?°ÒÏ#üe?Æ9R!A%?ÃE\xc¢v?ÇdTnöþ¾?ÃÆýrûF³?ÔÍZ­·?š[ nÀ?ÇZ¤]/xî?ÄMéÄæg?ǧ„ãƒ7?ÅYᡲ?à¤JQRá?ãK“¿‡Ï,?äñ^èÉ?çz/ŸùZ?é¬c¤vq?êñg²=®?í5›ÃY ?ï'Û½¶™O?‰ê”|Dè?¶ãZ) Áv?¶Ó:#üÅ?¿¦ B!°?½‰_ü¿Ù?¿VÊ¢a´Í?°ú öŸP¤?¶º'Éž?°-nM"ó?¼G¤»4Òp?±ÄÏ·çáI?ÁêÙÙê?Ä[®™#-v?ÇcÔXŸ O?Á¸©xd ?ÀÕ7¨QË9?ô½>÷˜G?ÆdqGêÔ?Æ'B Û?À„–ýÉP²?Çmtr_ñ_?á1£2Èm?ã‡p;I¨è?åð1¡X?ç={¶`Œù?éeÐ Ý ¶?êš²}RÝ-?ì0‰³&K)?îƒÆnŒR?«£ãdu?·´;ú:¹?Â"ú%X&¿?Ã4¼Do M?ÂVZ‡šS›?Ä ½ûN(¤?Â! 5Ïy?Â>J3ɲ?ƽ'Ê\?Á7kfJ9?DZÄñ6NÅ?Æ7B¬¶?ài½ðHáS?âqÚ Œ‚a?値9 ‚?æ˜êlމH?èyIÛë{?êÐ&çIo.?ìäô~†?îŠVmë…‚?pZËZC"À?±šÎïÝS?Äÿ/†˜Ò?àé-­)I?ãÄ€½Ó†ñ?å‘@a[Wð?æ|3‚ƒ¦?èœJvF'ñ?êìß׺:?ì'阡s~?ï Û_2¼?¥˜YïN?¿:iRÑE³?Å/¯‚¿"T?àöÃ#HH?ã>+Á/F#?åh|ôQÓ?æIб@?èÜÎöŽgâ?ëõ!'É<ƒ?ì> ¶—?îL]Ûjß?¡¤÷ /³ª?ºj¾à¶–÷?ÅN¯ž§ø#?ÍZ¿°H=ç?Ñ´íÛ] Ô?Ô=jê`Òm?Ù„…PpÓ?Ü2²¤Ð?áz,<«P?äAFÿŽ?ä¯:¡ö?æh¶ùY?è'A‹|ZÆ?ë«”‹ŸÙ§?íq,in9?îo5~!?‡µz/Ë{?¸\]o¾¥?Æo!ǘ°z?Ím¿Ê¨éA?Ò5>ã²l?Ö Nûx?Ú…?š Î?Ý(DìŠËµ?á}uÊ?àÝ›¢)F?ãቧ­ÿ?äÒóÚÌé?çójÁ9?é'{‚iÎ:?ëßðé)ª“?ì3QŒúù?îû7?”´M q¹ë?°qŠ)«½?Ä <ØLQ?În!ÊShT?Ѷuî]Nœ?×aÑcäl'?ÚãÈsø~Ý?ÞE?F§í?àúP-7b?â,½¯³9X?åS_óÏþb?çe R â?èñóM‘×?ëš\¥&¯?ì0U ?ïV3Í oµ?‡¸x1ËöŠ?À $É3+?Á†ÚuÙ]?ÏGsRã}?ÒMé ô?Ö½ Kó?Û\d¨À?Þ“?Ñ—xr?à”‚z6?ãSÿyB(?ä-u°UAË?æäà @{â?èÅzÂaÀo?êÖÆÛ—í/?ì°R†Ê¿?î£^l zc?£xÐíq?µÕ•/ ®¬?Ä0mJÝ“o?ÌQ–ÔŽ?ÐM }r?Ö_·F›”ä?ÚX×8ÞÓG?Ü]KM”Iù?áýýGÜz?ãW¿÷‹V?äÅ]ä#&?çÝ÷£°²?éæí–û?ê𠃂?í±€Z•ê?ïÍXÊËfx? ­´a ›š?¹H¼‰KÍ|?à ›9J ?ÌJ}Šäwó?ÓÆÑþÒïH?Õo K7Í’?Ù%d¼†n?ÞSÿa%n?àŽŽb&.Å?â0ɨ蹢?æòÛ$g|"?éòT?꺹tXÊ?ìÑ×{La?ï)ߌ‚à?mŽ—@ƒ=?²°¯<ñûa?Áç Úá‡?ËÜ<°¬¾m?йè²$?ÖXg ´ª?Ù‹-j"û÷?Ü=ÂóÂV?áå] é!?âeTfÆ?ä¯ê¢«î?çfH …?èÍo÷ޝ?ê3¤?ì‹6GeM.?î¹²²¶¹”?Cµ?r ¢u?¼âä¡å?Ä(v2]?Μò8rW?ÑøbÐ?×N€¿”ƒ.?ØQšäUo?ßwA^·tó?á(g‡bZ«?ãw¸+Óäx?åרîáš?çRSã~´?ègê ©Dk?êíi‡%}?í{,-GE\?îh¢ ˆuâ?¤!¾oeߘ?·Kùƒ’XU?ÂQF§æ?ÐR†ƒÌÚ?ËOëÌýƒ4?Í”PMÆ5v?Ï|3ï:¾“?ÎåÂÌ`{·?É7vàÿ?ÏCƒ€:.Ú?Ìážìï=2?Ï´^S†?Ëôý,ÖÑ?ÒBnÕç?Ó68°Ž±‹?КӢÅß?Ð{‹dæ#?Ñü^L x°?ÓàSÈ ý?ÒO®æ¥'ö?ÓÇÙÕèÜ¿?Ð ˆÀ¹ƒ?Òþz^j?Ö9>q»~?Ú]Náüù?Þ©ÿ§£?à©0’Ь?ãÇTÈ4xm?åQŸÚ ™?çv€*…¨?é„DFf½?êéÏZÕà?ì§Š”¤7?ï® ®yÕâ?Mf¦P?µRv¡AÔo?ÁM¸=gm?Ç&sΑaP?À)† •Ï?ÁѹHÚ“?ăþ’áç£?ÅåQ&ð¿?ÂyÀæW?Ä5=ö ¢Ø?ÅÏ=Û5f?Å ŸÑ™6|?áƒð0¦qÔ?ã£`zù?ä^-ñ×€•?ç?çZ•?élü¦4Ù?뽄¿óª?ì)½šöZÿ?ïWtkJ2?˜„Þå;=n?±ªAλ5?ËlÙÇÁ"?ÅÜqi eÈ?Ã&Ì+r?ÁÉy0¹~¹?ÁlX†[Ð?ÇQ+uÕÅ?Âç«™*d?Å/ ¶Ë?À fåí´j?Â|D¡?àðw Œf0?ãëÍ þŸ?äTÛì#o?æ$U¦h‰?鸯JF§?êUIꯆÈ?í`rŒ|?ï/ÁÛ’?« 'Õæh?»‡¢;.Š?ÂÓë3Ž«?àuÞë–?âNµÑòQl?ä›>n2Qè?çïMäMŽ?égx=Áj?ëÆhÎÅ„#?íyü9Õt?îÐÒç‘æ‡?¡(w&gC?²aoräR+?áBcÄ“Î?âÖÒèÑíç?åw .­78?æf® o2?éAÓÀA8©?êÂÁ]Cˆ?ì6¨‹?ïCwЯqÏ?oÝy7GÆI?³b0Þ|î^?Ä]íÙÐæ»?áÇa•G…?ã _‰[±Í?åxx2ŠÕ¸?æHYÒ¿Ì^?éþ3e?ëæAƒ¼?ìr²J½³?î&éÛßh? ó0>—?´ù3…¶6¬?Äý<›D?ÌSÍ“±¤æ?Ò÷eµw`?×tij]mÏ?ØaSId9ì?ÞÜp`ºÔ×?áYWÿu?âîË4½•m?å4óºJ\×?眔yl€?éŸm¬á?êYáJþÝ?ì—:SPc€?ïÐ4Ì÷…M?—úÔ^*¹bÖ«Ï?ïD÷“-ñQ?¤Qù½Ü¡*?³÷•€Ø?Â®Ê gÁU?Ï£ô?ÖÇ?ÓÈ¢%Ù÷d?ÖÄ€.÷|Ÿ?ØâÐ*T?Ýð†¶j³?àê‹Aj¤y?ãCo¡ï°?åŒxn±0?ç3~Mã8?èEj „ž?ê\=ãmóì?ì#±VÕËZ?ï·¨…Ž&‰?z€æ€åU?»õ!^ÈÄÆ?ÂaY€Õ~?Ê $ D?ÓØB9^ ?Ô"¨òÝ,?ØDC"L˜?Ý ¬Ôt©U?àw&I8‚ ?â"ù ühu?å;³Æ¸ûX?ç´H§ÝžE?éè8úG·?ëp >øf?ìüW<ù±?îeAâ ÝH?®úл3A‹?¾/FG?Á#WÇO&?ÍÝà±÷?Ҏߟ °‘?Õ·=ä¿"Ñ?ÚV§A/I?Ü]óA J ?áÌ@è‘à?âîG*HÅ©?墎Ü&?ç-Ç Ÿø?èV ç2TP?ëq€-iV?ì5—Ú<­?ïÏlúZ?o-d–\©?¾ØÇƒÈö¼?Æ¡òÄà×?ʨJ7ÒfB?Ñœ£!å™?×3ßÏ?ÛìúvzÃw?ÜYcBåâA?á+ƒJ¸?ãE‹Ýl»-?äMïJ ¹?æçïúdè?è`áü|Ä?ê½ops?í­`ØÛ?îMTY£¹?¬…L¡ƒ5f?¿ˆFY “?À´æ½8æ?Íö€ã¼³+?Ñã¶3y›È?Ô"ʬdÓ?Ù… |Ì3ö?ÜaûF÷ƒÙ?àˆ:S¸È?â§.“ÎK?å ·V0L?æÎ ÜB9?éOËÖ‹SB?ëhì O¾?íS«Ônn¹?î ÉFKJ ?¤^<÷"Šw?¾-ƾ¥Ø¡?Á*¤Q?Ó>HÚŽ„?ÔËËæöz¾?ØúäMGV³?ßRÙ BÕ?átÀ(X;Á?ât–(“é?å#ˆñCì?ç–\j€&?éðÙ>ýR?ë´,¢2Ëœ?ìenC 1?î¦BŒŸÎc?¬›Lþé^?»o¡¹ci?Áùés"J?áµ›º:6?â2!ŸX•\?ä¥.†nFg?ç@ÍTƒ?éÙð&zˆ?êj÷Q[?쯞ðƒ!?‹6r?““Ñj€1?¸M;‰jM?­:Ω¦?àՊߣõ?ãÇ8ÊÄâñ?å˜xnÊç ?æ¡x_§™?褲…Õµ?êryÅÌÐ?íÌ,ÛY?C?î9ÄK?‡‚™q Ð?ºM¿[z Ð?ÅHßËvŸ¤?áYsåšÓù?ã¾8»À!Ý?åFÌ™+¨?ç€(=ž *?é6§§‚X$?ê‚Â?ÖÔ6?ìg ]ad?îÉÂ΢†?‚th)?½$“²´Yf?ã„QRŠ›?åu¤-ÌvD?ç‹ðV{åx?èù*]¦á?êw²&\ò«?íy0#òÁ?î3UŸ¼i?¦?g‰nÃ?´K²/©—?¸Jh LT?àë÷(ÎË?㪘ŸÂA?å­ð­¦ƒÝ?æ®>¤ÉÁ?é q®“?êßnçø)’?í‹E…¥?îý-âÜý?­L E³Ô­?½Ï¥)•?úʚª‹?˪ü3æNâ?Óh9O<­Z?ÕÐ7˜Dq?ÛLÉFÓù¹?àýcu|§?á;„ªW?â#¹¥³r´?ät¦DòW?çŸ\¸bB?éÚ ã1’?ê JÜ\t?íô!+c?îF¹®ðòË?l…Neaãù?³‚° ÕÞß?¨Ï[Tþ?Êu)¯ÒïÛ?ÐÂlWŠ?ו±Ìé1p?Û¸ +Œ‚þ?Þù(¸¦»?áS@ç?â†Zrã¡;?äŽz£ê?碈‡ªº?é°èƒC«U?ê’Â=éö?ìÍGôœ?ï•(q ä?‚×b:V?½dƒ}\¬›?Á÷tVÜÚ?Í.Ÿ:¡?ÑÊV4ð ?Ö–ß×L‡[?ÚFÿÞ³?ÜHËo~{?á¿a¸Y?âKÞ Ÿc?ä?ï9?æÆäF‰?è¥ò{ͼ·?ëc§Î¦K?ì´–jðÀ?ïmÕ‘Ô?¤±¹ze>?³‹¯LÞK^?ÊŸÔùú?ÉfÆ©x½?Óƒ)›çäb?×nÙ¦D%?Ù\ú¹’I?ÜqëÑ!K?àåëOôþØ?ã§›{ªñ?ä‰Jˆg?æ#©JÐQ?é†Ü&â”?ê1áf‹$Ú?íæ÷Ï=û?îd‘½ÛÀÉ?l'±æ ²?°'¨¸oÛK?Æ?Så1?Ë׬~R¦?Ó¾G¯p?Ô^Û\m^£?Ù½FNá×?Ük“®†»A?á 7§kx?âÔGòµ?änªpÊ–W?æš7fã?é'¯twfÃ?럜B—â?ì"5Apn÷?îkÉÌ‹·ö?¡x³g¯?±«*/fÓ?Äê^m˜4?Í)_3Š_?Óš ÊŸìï?ÕÎF>²öÄ?ÙïÞ¯‰ŽÁ?Þ"×# z?ájœE„; ?â8™à=¤Ä?ä/ýêkÇO?æ¦&©ŒrG?飄\¢TJ?ëð6rM?ì+ùN7‡Á?ï/#ÎO?¢®vÐN:"?»ò ÕÉüÙ?ÀuEyAw?Ï.“NË\í?Ò^™?ÖÊH1žT?Úó²4Ì?ÜúœËö™¥?ávd_ §P?âÊþKïS¾ã?Õ†5—dŒ ?Ûèºo>Š?ÞLD+¶æ?àé—kÚe?ãS ŸE²?æ9W¯^V?æQ½î˜,?é{ˆ5Ð?ëx´²e?ì\¥Ø¦ŽË?ïá|×Ô†±?™!U’¨À¿?¸Ù¬&ZÆ?ÇCƵ{?É{×Óýv ?Ò>¿ȉÜ?×ÛZI™:&?ØPË=¥õ?Ýæ&~·ì†?àïƒ5¶#K?ãQWûðØ?䉀.:z?çJÏ×ÓŒ?è°Ú—oµŠ?ëuÇNð?íSwËÞ,³?ïooój¿À?‰|ÉOäà?³ oÑ…:’?Ç_ãªîó½?̆JËo?ÑoS¹ñ¼?×Èò ¸Cg?Ú‰7Ÿ~t?Þç«2Ó?ᔄv¬úç?ã ól"O?äw–?¦èû?æü 9ú´à?èóƒêV?ê9E¥r˜Ø?íW‹ÜÝgù?îמÏéÑ*?«™Ê^Þ?½‚„í·Ò?ÀáfÃPIÑS?ì–VZsÒK?ïm âRÑ?¦¤÷Bo÷?¸W;¿?ÅjŸÞÔk?ÉÐØ™oŒX?Ð/ÊÔnÂ(?Ô(¢¼ÄA?Û8 à+Xi?ßèšNg5§?áöý. ,Ê?㉜XyÖ?åëÁ¤9?ç+'•* ã?è`üª…v?ëÓkŸvŒ?íÌLÐt%§?ï¸y稲?wšÌ ¡‘?¶y·kÔ÷8?ÄDM‹+D?ÉÛ˜²&/ˆ?Ót‰Xú¬Ä?Õ×^7°?ÛApö5¥?ÝsbîÆ?áö!+Å*¿?âihO š?äð?"=ïÄ?çÖŒê£V?é›(pÕ@k?ê¹n©g8h?í„àF[-¹?ï2§¦Q~?hYh# )÷?¾ñý,›;?Ç3SqiTÎ?ÈÏVª·ý?ÒK§¤±?Ô—«£!4?Ù>$ìk;?Þ¼_þq3±?àÅú×eùj?â?iÄ÷Zþ?ä²J¬4Ñ…?æ1l°Ýå?è…ÚF–ù?ê¡ÊxS¤ ?íZ‡é J?ïäY-æœ?¬È ·ëÕ?¾Ügw“þ?ÇxcÛÍêL?Ê?y`ô,?Ðb—ÐÀ?Ö¦×ÖmÔ!?ÛbÁIx:?ÜÕü8' ¡?àÚ}ó¶?ã§ø›3@Ÿ?äé—Î÷ ?æù_3®¯G?èúG. ?ê´–Åw®?í6³›ÿ·?ïômB§?¬—JþÿìI?»rÀki.c?ÂÐje¤[Ì?Íiï¸ iû?ÐsÛ\M?Õh5`3é?ÙÁy¦?ÞÉ€_‰ý?àøVõEv?ãt½ž?ç P‚¯€ú?è™zki?êƒJ-ø"é?ì‘ÚG¿°?ïºD™Ô ª?ªAE{i¸)?´q‡Ý?Àt%S7±Ñ?Î:ñXïºu?ÑéFM’n?Õäþ[Â=?ØÉIúb¼?Þç:ÊR?í³pk%\?î–r:ú&Ù?¤‚·ôd§D?³õpK+œM?ÆÛ¼¾?ÎI1vv¢=?Ð9²ï0ͯ?ÕL}DF¶©?ÛȪ’ŽQÍ?ß09ZÂh#?ìñæÉÓ–?îØú…(*?|H°Z6Zº?´Çñ"Ûc?Äî>lòT"?Íװ޼†W?Òúh“â H?Õt¢ïK?Ûk±Ü:~?Þ‡MPtÅ?íò¸¡„?ï¬àA;Ò?™ãMÿºŽð?À !“]?Æ¢!±ã[?ÌÕŽwN|&?Ðj—Ó´?ÕVÕghÃa?Ø<Wƒ÷?ÞpÐß§?ìêN“õ”Ù?ïX#zùz¬?§É¾ÄÞ?±‚êAÓ?Â\ùÝt0?ËÝÌ‚^­w?Ò'àµ^-?Õ–-õ`Hª?Ù!Å5 †«?ÜÏ×1g?íÜ(I‹íí?ï{ϯZQŠ?QÉáÛŸØá?¸á¹°?_å?Á×Ç:P*?Í7Dy R?ÓCI!ò­?Ôùl­ ?Ù⮟!Ø]?ÞkëÔ,é?íÚ°e‹¿&?ïSãy"K~?šZSZ?\?¼ €ºYQð?Åöð‡7_1?ÍÕÐO#(?Ó0hòªü‚?ÔíÄ„´q$?Ù¢¦%)ôè?Þ÷1~T?àòa™&Å?ãùÑtg¿Ü?äó›n3@O?çŸ(m¿õ?è:™©óü?êx²Í!Ô?ìG]”êo?ï÷hïªåŸ?`-, ŒOC?¸ýZÖÔ]?Ä“ÊJÏ?ÎW±œ¾ýž?Ò ~›¢ÛY?Ö†ª9ˆ ?Ùc¥ŠY|d?Ü`ƒ2–Ä?á4‡Í­oÌ?ãFÿþ_[?å/ÛŇœ?æ!é£X\ž?éžð\8H?ëI?­Ÿÿ?ì©:aþŠ?î˜VD1P? ÝsC‘M?·Àx{7ž?IJž ä?Ì,ïh[¡?Ò·Çõ\?Ô|ûŽqP?Ú…Ö'?Ü¡<ÛM?àéÏJO›?ãºÐÙ•˜ ?åWd9j?çÜ}üR—?éÃR™;?뺊£«3?ìüψ?îþ“9¿£?¦ù?¢“¦8?¼}â]¯™Ì?Ã蜥 Ã ?ÉÞh—&,’?ÑrU_%X?Ô°kð$FN?ÙFE5økb?Üð$ôâÃ?á»Ñ?ã6kÑÏD?åë…¿6–?ç^;ÿ ¯‰?è½&°;—Õ?êUdFfY?ìÑÞÁØ?î¡¢^ÿq?ŒƒõÿÔ?¹tÜ‹¾&?ÃF{hY¿¦?É}wä Žc?Ò>^úbsð?צ!ÓU^ô?ÛA¹wh‡?ÞèHb.¤8?â¡XÝwí?ãzDH,§£?åš${|Ø?çyP3áTÊ?èL1Ó¾^†?ë4+˜5¸‹?ì±®ŒÉ`‹?ï4ÿ—¼aš?pؼj©Ô›?°üK’Î=Û?ÄY íKq?Ì4]Píáz?ÓoY1;?׉Y–á‚›?ÙñÆ`Ja0?Ü¢Ä ‰L?àYÑþçŽ?âjÆ!×à¤?ä˾ßñu?ç_a‚=’?è4õ¦¹C~?êx¶&›XÞ?ìGéÄxÅ?î›ÞlHüÓ?•—ÑOû²£?·ƒ¸ÏÓtì?Æ)Kè?¯?Ëþè^?Ð%zÂ!$?Ô+ZÒ¥&x?Ù.\ã¢>Ž?ÞŽG²D­æ?à‹¦]¾¶?âOQç–{e?ä•rumh?æ¦aÎû?èó‡ EW ?êTß Ú?íNÀ?îOAÍ@Rx?©'¬Aª?µ‘ÔÖÞD]?Â;ih×î?ÌÝìû¹?ÒÐà•Ã$?׃‰ŽÀ•Ã?Ù­mÜäÉ‚?Ý@5 À8œ?áûqJ›È¨?âIyÞ‹=?äõŠÕ@#?çîq¥:Œ?èJýÑxõ?êcùn€î?í”tWNÀq?ïc3÷+«Ô?œ¼^rp g?¹ÅüÈê÷D?Á×ä¼­?Ì­ Cê{?Ó®YâFšb?Ö/çu-²?Û‚Ñ™}F†?Ü£{ésÓ?àC}èdI±?ãV–œ¾?åûx|ˆ0?æîû&1 ?è…fDu—w?ë· ‘Ü?쮦„á!Þ?îÆª©ÆDh?“È&ª•?º×^ªÏ ƒ?Á¦GÛÛmÁ?̯Ž:Ex–?ÓIU"é?×ñ:‡Á;?Ø sí¹‹{?ß!)!ÞÇ’?àu.k‚¡±?ãŒÛ¢‹?äÕ7X°K?ç^”Ýù†?é³Ü‰Ç%itú?Ôˆ —Iª(?ÙpÝ„¹z5?ÝŒ¼Cx›?àÀZÙ¤3?âa¶ITò?å'gžµ&ï?èI@ `ä?è*Å‘õÑ?ë4»•£+N?í`&'?îü'à;p?ƒ’l^w—?²c­š~ W?Ä`­|ì ?ÌúÞÙöÒ?Òº7üãN?Ô#¢Ø`DH?ÛPô®§ ?Þ&éÅjy?á~Le£æÕ?âà‹&áä»?䄞l<É?æãs²{?èÆÎÁ–´Ý?ëßÒ?8ª?íK»¯úÔ)?ï)ÿ`<ô?œ´Z,dÇÁ?»AßKçÿ?Á—äî?ËŠî¡€?Ð%‚Ä5 ?×™iðE-?Û¬r!ÁÒ?Þa§Â·à¾?áÑM±³?âM20Í ?ä¦ÒÅñA§?çWgüùÐÛ?é‡UÛ]\?ë _Æé¡?ì%eQ?ïTg¡:W~?…ûgÑŒ+?¾”Å•eôˆ?Ãì¬)>ºú?Ê“IÝ@m4?Òt·zÀƒ.?Ôàlmêw?ØC‹U‡Õ?Ýêÿ$·½?ì}ƒM?ï)Ï"«\q?vš•}Q?½3"J¦ÁÎ?Ç™µñc?ÌÜíž³’?Ñ9Dùr§j?Õ½öKòwi?Ú¥èhZΠ?Þ cb §?í¶ü ¶‘?ï VÐo+Y?ª¸€tÁ6?¸}÷¤YZ?ƵᶜK­?Í]Ÿ|i7=?Ò+î‹ø*?×Y8ñÝ?Ûb| ‘l?íkâù§å1?îÎîñÆÊ?ž/Ë“’è?¼à@€ÖE°?Æ=¸K?Ì&Üõ%%?Ñû¦”6x¥?Ô¤´!èž?Ø£„}äú?Ý(î QN?íxmœÛö?í{‘{ND?ìrߘm"3?ìO¾×(¬î?ìÚ*"?íŸ&÷Ü?í|Óâ¨?ìÔÌ\œó?ì‰cÃO„c?ìôÚÀl?íøåg"Ø?í¤ÖÊZz?ïQºÏÛc?•s8ÎÃÚB?°-¥gi^t?ÂÓ)wS™ñ?Í·ð=a—?Ñ“•¿+?ÔR{oi¯Å?ÛW!€;y?ÝïŒþÉ?ìº8?¼§?íqy¥Tcö?í ‹N„Î?ìíì?kÆ?íí#?ìëØç´µõ?ì4~Û'2`?ì9&ÇÞº?ì­­°Œ?ìV“6/ž?ìz;¾º°6?íOñIs•…?ïv{¯YU?¥½÷uä.ô?²IꡟÕ?Æ–pKæK?ÍSïnÊ|p?ÓpQœŒŠb?ÔfS”ñZã?Ûx‚Gi¿f?߯ƒL–Ê??íðà€7î?î_!ƒ4G?¦6¹ˆ'€±?²Ü ¦s+?¶9–¥Œ(?Ë¡ø¾IÕ?ÓÙJ`$ j?ÕÒ&x­r?ÛeJj]Ÿ?ݽÑû-j?àå—©Ñ[š?â¬ë‚gð ?娯4f?ç»CÖdǧ?æK…@(·?æJEQÃt?çã$èóS?æ»ÊKz©?çÆó¥?çIÊÃ1?çb[>Â}µ?ææ–…¬Pf?æ  õå+f?çÀ·ð7ë*?æ¸&u€ƒ?éþJdV²?èž…[!Sê?é6>·í?èÒñ¿?è00¤Þ5‚?èQðÞcØ?éK¾Ž*ü?éºyZD?é•‹.”¸?è|Å4ö´?èèiíº{Á?éÊqpÔ½?êÐ6Tüc?ì T¦uvŠ?ï1oþ½L?_Ê&ÊÈTÉ?± j6ò?ÄòNe‹?Ê•éç]«á?ÒÞ z=?ÔæïG?Ýʃ‡Ü_?àýCˆ ÏV?âúçƒÎl¯?äÅK1f?æ<…ßåm?è4M¡È.v?êÑ?k?íw#¿e?îO™*`¸?¡b4[<1Ù?ºs¾Øñ0?ÅÛqÒI‡?ͯº°?Ó‰I èõ=?Ö^ßhPºð?ÙüÆ«Òe?ßâ ” ?á[œ`þJ?ãO”ÎÀ§?ä…þsd¿·?æÖÖöDMõ?默£ME?êÚÖÍR·?ìZ Ê÷ ?ïÓ4Qo?…ÙrÅ?Éa— þ­Ä?Ñ3<âý³9?Õ¬¾- :?Ûz±“ì“?ßHÒÁ’?àåu“ñj?㢕Y_k?äõS6=ž?æuò2 0Æ?èßâø¾o?ê&‘~áF±?쾊§Œ0R?îðß‚1C?¡`4 c ?À”ÜÒîÅ?Ç$ÃÔ¬Å?ÎsÁÒ±g_?ÓȨòÿt?Õ¶ù^²õ?Ù„u¯ö?Þ º!s'?à)¹ª$œŽ?ãm´.?åGûßÙ\L?æEåÓm :?èÛ†í_O(?ë'Y6G¹?í¹˜•¢B1?I=¹˜?€BboC}R?¼ô"âÓI?ÃÆ¼@fYÌ?Ïq£Ó/—?ÒJÏðŒ6?ÖdgjXâ?Û÷Ò¬Šá?ßÕ €Áƒœ?â¥pñ;V?ã›x¤Šê??äÕw1Dg?æHáâ6+´?éGËÀ0™z?ê€ò$úçÿ?ìHµœQ²K?î@1•dR‚?¬>ȳ{ÁB?¸ 8ƒLï ?ÇÎÔUõ….?Ì뵯†?ÐR¬_Þ?Ôï4…k‘?Ùtù“ÛÚ?ݾf‰54é?à&éÌ1å?ãADÑØ“?å_ð@<2?ç2[±L6?è5± Ðfh?ëOË«¾6_?íTן˜–Ž?îrG?ªQóüƒ?ºß¢¡ø ?À‹•.n”Š?É+ÖðÂ!’?Ò¦wâæ0c?Õ ,ʾñ˜?Ø\;ªÈ c?߆¢UÊ¿?ì‰Õ±oÕ™?ïûX®é e?h´Šº}Æ?¶*SNマ?À—¥=çÀ{?Í;ß>Ð?ј%»I°?Öâø´=í‚?Ûé‹i,V?Ýí^“Åõ?훇¡ï–;?ï.îÞ®?…Lé~#×?¾¯¤dcÑ:?Ãä+Ùi¹ô?ÊBé ÞmÐ?ÑOm0Q"?Ô¸EŒ‘?ÛcºVál?ßP Í=,y?í£¦`I`A?íªjMºï¤?ìš#ê_=³?í5­ZSÃÃ?íÚ”1ð?ì½hi\?íÛnTZÓ?ì‹´÷ü?íy™‚¶D*?ì#Ò]£‡?ì úƒCÁ?î®i@¹ý?‹8CWÛl?¿¨&/“C?ÅxŽþ #¹?Ë›µt?Ñ´ö©µ1?×ÅJ²€ìÄ?Ú4îMÄÂ?ÜxœÞt[7?ìÏ›XÅ}?ì©Û2cŽÙ?íÈòF?ôv?ì©c1ÖÛ~?ì™ÿ¤cõš?í´O'íÈ?ì â¯!?ìþÌ"Zƒ?í—in·6?í©•´€{Z?ì£Ïl¢ï?ìµ›©g°§?íë¾D½î(?ìH^õå?ìöГß?îý…¸á¿?¢ê— Òœ?¶mRfØ ¯?ÃÚáG¥?Ïë¤ÇÐÇ?ÓPÝßu?Õ8ÅWÍmø?Ù=åÌNí°?Ý•ôÃá(?í£a‹ˆ?ì,ÖF,¹?ìÇçÓäù\?ìì“ÒÕ”#?îfuFß?íyq7˜°?ì1rƒÖOx?ìTbw¦U¿?ìÐ_º° ?ì–ç (ì?íÙêCx~?ì­?N¢·?ìsNáL˜?íJðÆ#÷·?ìv–¸sr ?îe<|'Ë¢?„ê<­¹¦?µ ¯ß؉Ó?Æ{á&íMQ¯?Ðü Ÿ´?×`‰¦ËªX?؈<‡y³?ßA¡­2Ã?áº^Pj‰?âí–“Õ÷?å¾õû$?çú±2þºå?è;=£Z@8?ë‹óêåkµ?ì>¡þ%[?î@¡SyÏI?¯oÏuö×?³[ÏH> ?Åx‰f¤²?Îq ©°?Ñ L–¨ù4?×0¹ø2t?Ø`óyÌÃ=?Þ¶ÈooÛ¬?àØ;7úÑ?ãQ$1ZÔ'?ä©®æ½5Ì?æâj46?èëŽùÃGw?êeÕ„|?í|kêYÀæ?î'¹L8ÆU?sH¬fy(?¾]…Â\?Ú°  Õ?Èduˆ ¾X?ЭsÙsØæ?Õj}spøp?Ù^5hJ>Y?݆Êêf}?à9mqôÏ?â9}Ôþ?äÇŽèËŠ?Þ"Æéç¬?ácœ­›¶?ã’¸u‡Ü†?䜎†';?çµèª£²†?è6!¨Ö‘M?ê‚R5Ù3Ž?í+Krµ?îAáªy•Ë?‘òÈŠÔõá?¸ ¹_—LG?Ç#c½ÖÊ?É©(/Ëw6?Òžžï ?ÖŒO«ÒïÙ?ÛR9Eé,v?ßHêÓ×µ?áÍtñJ5?ã° ¶5v?å:‡ÃÙ?çs„'–G8?é DrìÅÈ?êú_ _Å?ìðûÛgù?îv² ß¡?¬ £ß“?µŸ´Pg¶I?Ä]Ý\. ´?Î`Ñ­\~8?ÑhES1Ü?Ô8Ã5ý?Ûò«]6Š?Ü9›?s ?à©òÅ^ ê?âc‚-ÒoŠ?ä°j¼Ü¬^?ç:³º/³•?éIƒÃ8¨Ö?êgÅêKGÂ?íï(ô9ã?ïjoë¦ ç?¬CÉBØxº?·õXlm?Ä,\õœéK?ϬTL!Ý9?ÑèÎ^ æÓ?×°¢/$Îß?ÙF½S˜{Fm?ÚÖе*N?Ý %ŒBE?ì‚ÅÃ’—?îr͈¯}¢?¤Guxßþ„?´û0…•?ˆ?ŘÏt¶~z?Ê›)Éà ?ÒϹ&i?Õߎ‹\BZ?Úæ óÂbD?Ýä/EŠ.¢?ìª1u™ÞF?î@rà0I?š®A7õÒ?·|Õ>ë‰Ë?Æú?ÚŠº?Ë,ŠðµÂ?Ñu}NâJ?Ö…àÄ×E?ØŒ¼UÝa?ß–{xÞîÎ?í²~1)G,?íì œãË2?íIËQ'?ìo䉜¢?íi9~Y"c?í¼.T½Ûš?í\uRÖ™!?í#4ÆA—?í,Áù<ó?íXųäf ?í¾¢aÊã|?íþê‘r~)?íßš%Ik?íìAÚ£r?î$²÷­B?¥;3[„Å ?¹‰ùu¿UN?ÄXìÆÐ5?Îÿe²Ò?ÓÃÚKBnE?×S±¿” ?Û©œ”ïÊ?ß§ó“G]?íšR Î ”?ìÀûã¬?ì¥x‚€j?îGôæ‰?ìµÛåCa]?í*ñ5óâ¯?í‡ÍëX|?í|¹ù1Hß?ìó„Ê%Qh?íM5¹­9'´?ä’zDé…å?å ËQ¹Óú?å?—ßI\?ä·š¹¼D?ä%‘d¼ì^?å6‹‚]Ûœ?äaeÜ„øÎ?äÞ¾Xm?åxðC*?ä²ÚD­?ä9C¼›U?çØÐoa?ç$· YðP?æFI€~?æˆá6¾è?ç9"øu–0?çg;LàKz?æ¼2<¼zÅ?毆Lt?çÐL ý”?çÖñ¤º˜?ç‹'›µº^?ç±WÕ†Ô©?æ8?æd©œÑ?æ–æ#”?é,Š7™?é¥Fç*qt?è-tž¸”?è‘.bŠ¥?èPk׆/?éL&Yö[È?én2ìLI?èŠÁÛXM?é‹ÓœŒq?épV¯Z>?éö˜È8?èP¤Û4Ô(?è\táBâÎ?é52DcÆx?é,²,ô·?êû¸ß¡x?ëËG-Ãì?êSèdSs³?ëÙß?Œ¼«ŽÇ?´ °õ óW?Ä^-u2j?ÊbI™E;Î?пsü†må?×2a 8г?Û+)åI?Þ¬ØJ÷?á,›µ“‚R?ânM?ä&M®Î^?æ„¢X}É?èÏnÔ `Z?ê)%{Zƒ?ì.!pºY®?ïo“ñ•¢˜?«µ %ÏÖ±?³Ñ°ùÃò(?ijÞ0Mê?ÎðâÔ˜2}?ÑÔ(£Ú?Õ²=ïÞi?Ûöê‘sÏŒ?Þ¨Ðóâ?à°Ö¸Îšâ?â0U¶Dˆ?äzêDpj?ç¢4…g ?èo¶{Lÿ?ëÐäÌX?íH‡·T.Î?îê:ó=T?vÂÜ‚ ?»ÐA'³?q?È û-ü/?ÎS±˜Hš?ÐMƒï”ô?×°iî/+ ?Ûj!l æl?Üm³q›;O?á{ˆILas?ãÊÀâK?åîÉ$'ØH?æ(”©µ`?éݸïtÈ0?ëÀÀ®þ'ð?ì'ezÐV»?ïzˆÕ1Ü?§ãÂxÿ#I?»çuF@›?Ç·ÔW }?È{õáí”?ÒZ‡4['Ù?× HÊÚŸÙ?ÚýHŠÛùx?Þ¾º~zT?áÆÔÝXÀ?âÛÃH©Ÿ?åJëÞ‘b]?ç&3Œsc`?èúO,ÚÄÓ?ëið¶¹]?î…2¹B­?îèùû?D‡EòŽ ?·mx6é‹?Æî"°Kø5?É7'RP?Óoédª¥k?Ôs;nåMU?Û>ùŠ*Ô?ÜA Ô¹?àÿßVŠÏ¥?ãýÙJ—QÍ?äóW4З?æCEÍ›¥õ?èij S0 ?ê¥f¼ªA?ìÄ¢¦…3Ó?ïáX屯ï?¤uºö9[ð?¿3G™QX'?ÃÖ,nu§9?Í.oD:(?ÐÈ\ r+×?ռš4?ØS£PÃã?Ü©Äйš?àÉvìÞ?âq¡ˆ\f?å'¨%DÞ?æý+A ßã?éÑèÑåÂ?êH²s›?í7‹ª ô?ï<”og·?¨ë‚'ÜxØ?»€–*ˆ…?Àåbj?Îç2¼C±Ì?ÑäÎRlD?Õþ&«ºðè?Úuï´ÀÓB?Ü êã›Ç?ánÀlâ°?㪠ØÌ‚î?ä@ó¬\Ñ?çè½=vó?è­uiÖD?êºzs)T¦?íWû£™@Ã?ï4§o†1]?šùQ9$éo?¶”€ÅX?Ãïl@Ýun?ˬLpãS?Ñ äž~ߊ?ÖÞÔf• ?ÚvOÍiuî?ßDÉÿ%?싵Õ^ÏÉ?î®Î“J‘?—GF£î û?´%Oñ~ý"?ÃÚ2°­?Èþ6‘¸?ÓÊŠJk¼õ?Ô´L"ÓT†?Ú;G’æ ?Ü£ü·¹¾?íµ·Øðs?îÏ¢0ÆÓ?‹!Säp Y?´mÞ{]G?ÁLF{Ó†u?ÏB“lM`ô?и{ö³PF?Ô¥lÎãO?Úš‰y_m?Ý8¾‹4L?ìÐ!ŒÆ3?îjA4ß$&?¡ «2/@–?¾Acbï”­?ÂB_e÷!?Ê (•÷¯?ÑU==çѲ?Õãf°äF?ÛMú°Å?ß÷d²ÈL?ìâÔä&Ø?ívÏ{?ì>l-?ìRë;-?í¥%ôlñ?ìÆgç ïA?ì›Ø´Va?í²f,ÎäM?ìçx<‹œ?íœñ¡vI?ì}«T.N6?ì@“Kgi?ï-Â]”ö¾?¯‰ÈÄÕØK?°=f!³ƒ?ÀTÔ;Ò¸?ÏjÓ¼‹ª?ÐJ×?Ô™4[g¯?ÙE½Úg?ÝŸŽ÷Ï?íHUñôŠ?íW-Ês{ô?ì<â\ç?ìM)°7'?íÍ.‚“u\?íÇBWÉ‘]?í$®HÈõ?íÛ.¡4O»?ìêhht\?í¡F9´‚ ?íLßÌæ?ì.6Ƕα?î'ü¾jÖ?¢æ°4ɤy?¶²T !l?°iY}iË?ÈÇ•ü>*?Ðé$[`}R?Öy¯æÓ‡3?ØÂÜ¢qDF?ÜiÔ/²BF?í{_ŽI÷?ïR:þOÌ?Œ‘aðîŒl?»¿*,§Ë?À$d3U;?Ëb[mãt?Ò¡·ß«Ó=?ÖSǃ2ú?Ûÿ3Á±?ÞóYpzë?à`b{?„Á?ã¿1y*ƒo?äþÇÆe}?çP;¯B?æW5Z…¿?ç½ïäwqÔ?ç7ƒV9a?æP冉¯ ?è@U²É?æ4•&‡Ì?熣’îv?çú–Zuð?æˆùÀÁÁ?ç‘#˜hÕŠ?çøC÷¿œ?èQˆã^Ž?éƒâå>­Ã?éæË¼hTm?ètáúl?é jþžŸ?éŽ2ùb#ë?è‹?m™¦?éÄãfV?èülzÎþ?èØíϼuÛ?éÏËbÚñÑ?éâs¥sSÛ?ë¿LÁ:t?ìOMFâT?î«E˽—Ê?j×O,—ÿ?¾ë& ó¬¬?Ù»ƒº€?ÌšîJ&©?ÓO™9Ûðe?Ô\»g˜5H?ÛÙ<]Î?Þ—àŠ_º‹?á?Û€ ì?ã9°0î|?å}„ „×?ç‚ X§Ð?èV…Û)ÿã?ëlc•çá?í4O$ú?î=fÿ?‹jzß|`&?¿žè ΄,?ÈôЀm¿?ÊŸê#ü\?Ó)ɽ£M?×?iVŸÏ?ؾT% å?ßW™‰IïÆ?àã)t“ë?ã{¤@ö?äã·)ß:µ?æ.m«÷¨x?é&§{ñÅx?êÉ’±üp/?ì<ѵ¥º?îM­˜Ï-N?•ùP¸¡ï?½ #Ù&ñi?ÇG³d˜ÁÀ?ËOûƒ~œ?Ò™o¶¿âõ?Ö wÜš9Ñ?Û/©™Ì*?ßÏŠiüð·?á4ÛÏ+”_?âØëF’?ä§ú¥.µ?癬tâ÷#?ègÚ D9ñ?êHͽKÒ?í*csê?î!õ_ñ” ?˜ãÖ’Éx‘?³ìQcU7ã?ÄŠîjÙû?ÊsYÚ¿Å?ÓôÊg¢®J?×ÐŒAA?Ù`ÕSöúIt?íƒWQÙO?î@ ®K?¦òÀÖÞ@D?ºƒž–a®?À†ÅÜÁa?Í¿1¸¥?Ѥ´.Eå?ÕÄ£~è5?ÙéÖVXQÓ?ÝüÖ‹ä·?à¶^³ —¼?ãG‚•`?å¿PÊÛo?çÔ`æ|Ï?éõ!î_ì?ënPc+­?ì‚f/ÓÕ?î=[0á½?v”ÀÔ´§e?°Ä*ëÎdd?Ç×’(z¥?ÌüžßŸ%c?ÐOÛ,.%?Öm_jk\?غÍwP?Ü´ÔU#³?á…¸iÒx?â‡kß1?媰£'c?æ±â¨‘ w?è˜Bh7oH?겎/­?ìPéÄ óŸ?îM=·¦vû?|(¼"ˆ¸?¸Lœañ?Èû³™Æ1?ÌñnÉ„nÆ?ÑL…ÎÐ?ÔÒÃTòÓ?Ù㦅úH{?Þ݃ÍÏ\?á…ˆzª ?â ¥… ýõ?ädÖ;¡ò?çèÍô ¼?êM&båÇ?êS|zz?íä”Ë ¾ù?ï+ci˜ñÂ?£€6÷çž5?·h·šk’ ?ÆÎLËnD?ÎWq•ñŒ?Ó§áåzô¬?ÖÓZ‹*?Úê ²÷¾Ë?ß5 K0^÷?á&³×ìQ?âë[p€BÕ?äyúiD”?æª%:ž?è‘âLŒÕ?êîâÇ5¬­?íã/Iœ;?ïõøÔT0 ?§j=6Ùÿ?¾_Å&WÖ?Ãúwâîï?Ï»jè¹?Ñ\5C7Eì?ÕgÝ•cß”?Ú§PeÞX?ݪ†ƒÏ¼Ï?áÚšÔ-?âFVMó²?åÞéi­±?æ¨ÀiI?éò°©­~Ï?ê ÐùñBŸ?ìYþé ¿?î'UöVK?pÈp0+oô?±nÉx¯EÅ?À±yv…}?ÉÄX(A?ÓIÙ9Ôq?×MAqØŸh?ÚÇBì#?ÝÙ/{ÅÏ?á¬'Âî?â[Îæ}¹ù?åsTÎ ¾?æVA¡{£Ë?çhïNÍ+b?çS5mqŒ?çØ(aZþ?æ²v@Š?çŠß”CUy?æ[R~¥?æ$ñ zö?çž±˜m?æÝ"UFcÀ?狯ˆÞ›?èÖ©«—Ø?èÆñ³¿È??é)æU)P„?éË›uS‚ö?éC²|çÁk?é$‚9¢ý?ꈸro?èÆ)­ûRl?鿃0>f#i?æÛjZ6?ç?ç«+ÀÏæÜ?æÃ=HŸI?æVån‰##?èä[Ÿ„C?çžû¹b¾?æÌ*GSZ?æøîвú ?çÙHšn ?æñaF¶³?çOÛEÒõ?è XmòÂ?æbÑéÜ)ß?çW¿J3B?æ)1vøtX?é>B%òË?é =øò-?è;€³e;•?釛-,?é ñø§=à?èiüöb?è™]3-,?éžòù1©?è~Á%Ôc?é—Õ­ x?èm E”?è¶µn'8•?è,]îCT?é½s=äÅü?è7Xª~±y?êT0&à?ê}ÈH«“?뿊8?ë„ât!Ïú?ëpštœPÜ?êðEo ëZ?ë”Yùã?ë¯n¤‰?ëkN=È6 ?ê×pìj’É?ëÚ› ò?ëê›U}Ï-?ì—íŸ'ƒ}?ï=¾ådùC?¦ysÎìÏ?µN‘j ?ÀøeÆD>?ÉÃ8±í?Р‘K?å?Öò˜Ö|A?Ø^“¸L?Ýkv@B~?á€|îƒ;ù?ãAè廫^?åeì)â®»?å²D—H?䘞¢ó¾ò?ä)Íe÷<^?åÚäà10†?äø¿ è"J?å#N¦QÆ?äÿïIGué?åGbpZå?åiöÜ?ä0‚Ç6s?ådOÅÆÖa?ç*“{¾Õ?çÓÄ ÖW?æe•žÏò?æ›…§÷?æý*«‰Ûç?æ‰uËíä?æu&;?æ2Á˜Ç{?ç¬Åß0 ?æZIsO}Ú?æNݲ ?惩ÍÑø÷?æØæšc—?æ™-ã.—á?æ–z‰Ê!?è {Î< ?è®uw£œá?è±ñzhÂ5?éçˉÇç¼?èx…6Ó?éo¾È£Ì?èâQ¶¦‡C?é9vbš •?èÀ™ŒBÜ?èÍ¥¬×C+?è¦Az»?騞áÙ?éÈI..F?èSØÝu?èeÈûüΜ?êÿ%‚wÏ+?êJ‹äóëd?ë<ޤÌ?êùyPð£[?êi Н4ü?ë™r’?êûY¤ÿ?ꃉÄV?ê(³Á‘Ûº?ꀻÿUÎ?êÏÂíO?ëxö*mZ?ì”)lCu?îáÚWŒß ?ªŠÃjÔ7*?´×pö<§Ï?ÇPs ’-=?ÊÈJ;‡?Ñ„U•3ë?×–j ø•?Ø~+î”Ìr?Üï]Ns§x?à (Òà?ããv<¨?å\¨‰öQÊ?æV•˜Í¯M?çN§0ÆU?ç "©­-B?æ.…s~M?ç¬[Æ™6Q?çð€6F±÷?æ¶N7…ðI?çlÓ`OA?æh½­Æšû?çâ0!Œ•Á?æ ŒÛ ~?èýí{•Ã?èŒk¬›Q?èÏ¿¢ºÚ?è€lóg]?èØA˜<°?é“ûÕÆ˜?é9þR÷^*?èo…û.?éGVœõìè?éþÒo?èY”åÛø?è¼z öp?ë ?í_;z(?îEiNW,’?†žfù'?·u·³Á?Æ[ñOêìE?ÏFs{‹x ?ÑàNJ« ?×f!„teÿ?Ú«`"Éʰ?Þ%÷DõÅW?àÀ{YQw?ã¿(ó]9?ä4ÙÛ´";?æz¶i];?éP/¿L?ê¶|¢n?ì$Ý?µV¬?ïrÓá÷ý?¡%ôéA?´©Ò„‚I?ÇfSœÎwb?ËSÛ’–k‚?ÐS!‡.?×éjn)›U?ÚƸ4®&?ÝPUZJD?à[²ÌÀ?ãVxo¶!?ä a™±¤ ?æuÆ:¡.¥?éÉ\¹zpw?êTÝÒ>3Ù?ìÿÓ"óü?î…*+óH/?žõcËçkE?³þQ¶ã®?ÇÛžÆá?Í:?h«Œ?ÓzéwúÑž?Õr msxS?ÙÍþ'9HK?ÞéÈf†‚?ìuÚ%|=H?ï³Ô¢ÈÞè?ª&aò`?µ/t<¯A?Å'¿V#÷%?Ê*YM§ÞJ?щˆôõ ?ÖÈ(0%[?Û°!Õp™?ݺ-ö­w(?àY™öƒ£–?ã¯ð¥Ö®?ä@¥Ç«B7?æ€2@ ˆm?è¼"²ó·?ë2ÿ—_´Ç?í[›é½äË?îqá?™Oص¼²·?µ$“îcYD?Æ·B\ —?ÈГýÝ?Ñ_õ6gƒ?×dI:–áë?ÚÓ`9@1?ßôz[K?á`Ü*Še?ãv ?ï‰ø6Ä—?xÃk/y?·A×·DH~?ÂîZŸ¤ýŽ?Ê3©GŠ‘ò?Ó ®Ê Ÿ?ÖdÇZ÷(@?ÚÄ -uÙ¦?Ý…=ÎÒË?áƒm^ß?ãEèçaj?äÒvüÿ÷?ç +W‹¹}?é kGbS?êÉB¶¯§¾?ìÜž×­,ˆ?ï¡xWÎèº?|æÂœÎc?¶áÖŒêÂñ?ļå*Ìï?ÊùT—Æ?Óg bCÅP?Ö_Ç_ Ÿ?Ù ”¼qa?ÞfûpÖÈ?àC’»t9?â:)ÝVñ`?åËhô5Òg?æÉfš®!?éŒèCÚ?ëC?Á]?í+#$Ýþ?ï ÕØÓ?R f3ÿ»?±êG 2?Èä¯ÖÑî?Î;Ñ^V®?Òª7ìFÈn?Öùx¶Ž?Ú Bâ?ÝÛÞ° ˆn?áò“61?ãK¸)PÆ×?ä¼ ƒ(?èÅ@Û?è™K5“ë?êŒ6[~-?íÆè›¥L?ï ®à­?«¤Ç!Õ±?¾¬%ÔRõþ?ÁV…+æ}?ËYûp¹»Ò?ÑI5”;?×cùYÑ—?ÙËÞˆ‹,y?ÜE‹®²…Ø?áÿÆ^&?ↆ˙Â?ä$ýÖeã¹?ç}PUÛ|*?è"ù}FrÄ?êØÆŸ£³d?ì×~ièÃÞ?îþ ÌŠm¡?”¤FNñB¾?¾#Ä›Ðr¹?ÇJø?Ê)Hø„‡é?ÐD‹+K?Ôßtt28Œ?ÛÕBÏ ¢?ÞMWåõl?àÃc;,÷?âNrz§ùP?äIÆ/ðÅ?柚Î=¶ ?è\Åå,×?ëzÚÿýà?ì3AQŽ_?ïÙt‘œA?©ÁB„æ»)?½¸ƒîð¸ƒ?Á†[ß:ò?Ì\€Ä·?ѲÅó¿M?Õ:0X»Y?Ø[{žì?Ý%MÓC…?á?éƒî?âžJáϹç?å2Ä4vEà?ælÞo›Œ?èÝÂÑ›ñ??ëÑì77F°?ìÀøÚ4ì?ï#6ô¨²]?¢Éõ;fó>?µ\òÝ‚&Í?ÄYm@ ñ?É¢—÷î«U?Ñ@elÝ{?ÕPaZù?ØfÛ ‰”Â?ÜHã»J¯Ô?àÍ›9SM-?âGêcÓY³?äêÏx“!!?æMŠÙd?鹸s³s?ë*¯?Þ?ík¿»Ù-­?îXIšoƒƒ?­ôÌ —ïL?°»éy56?ÇÑäC¦kò?̦>"½) ?ЯûÞïU0?׋iÐwGl?Úe¯ºÚ’~?ÜÐt€=š?á¸xúx^?â4ý×ri?å¿ÙG=X?çô!'CÂ:?é/go¡?ë_SÄTÕ?íÆ hÍB?î$¡40÷«?å~Úöàn?¹7û-nf?ÃŽ{¹Xe?Ð*‡„nÁ?ÐZ/Ø9+?ÖdghéÇ?Ú¬8 ,s³?ßÁáƒÛ?á×§=Èr?⢾µ-—ˆ?ä‚¶hjÉ?çQî˜òŠ?éè”ðæÿ?êgmä_û?íèŒã4ô?ï£4NÖoÆ?àIަ­/?຿ ˆí¡?àÝÕ\ðsþ?à’ÊÇû¿¯?àóKS=“?à} o”‹?àmÜ`û›ô?áA«47I?à”Äó,ÑÁ?àL&R"¤…?àÿ³Æîû†?àÿ“v_€?áèåZðL?à£ä¢LŒ?጖“g,?áÄm%u?êcÖšì?ìK^*FI?éý`*›h?ì/‡'?ã=ë>m¤/?êK3(õm?æKQy^SNOD ã°ux ‘0’¨(p(0äÀ8q!?@4 4ÿbðbÁ¶H€! b­bÁ¶H€! bˆúbÁ¶H€! bHbÁ¶H€SNODØHrðXs Ћ0àŒ@ð! b˜•bÁ¶H€?‡¸}²ÿN?®¡‘Äq±¯?Ž5!–DZ™~?Ò ¦”€T?ÕŸ%šÆ;}?Ô|Óz'4Ø?×…a¡i¹S?Öÿ œ” x?ÔÚœYÇÆ?Ô€#¥´[?Ô3#Ô?Õ¦öÖ;$?Ö~ß§ º^?×ådÁ ?Ù=<úÉ?Ø‚0'È?ÚŠ¿BâðÞ?Ø1⪒?Øvc: ÖÐ?Ø%J|«?Û«™Ç;?ع»÷ñjð?Úü`­ûÙ®?Øj „Óú ?Ûpç­¥?ÚŸð#hÔÎ?ÙFýlU?Ûàí} [?ÛèFx?Û‰ùµ³æ4?Ü>«þ/.?Þ îðQ?ÞuB;+?ߞѠ¯DB?Üx£=r„?Üú„c) ?ÜÏdLÍ?Ýtµnä?Þ¸à,Ÿ¦å?Ü; ³s¯?Þ ·c{@?ÜŒÜfÌ.?ÞÚhЭc¨?ß°ït4?Ý4Çt €?Üüä–›?àŒ6ÿ`?à€ö€)?åg¬/Ý)ð?åúÑX>6h?äÆ/²º?äÅŸwqŠ?䳺Á-“¹?äÈè}[ü?çˆ4QVl?繈±…Aá?ç #Xnöe?ç³ì¥;XÐ?çô)%hàÔ?æxh-?ç|ì8—&H?ç1ï¦"W?æCAÑóÜ?æŸÞŒwzb?ç—0qܱ?æÀÖ¼¾ ?ç“4kûF?æƒn[™(ý?æô·9ÙŠ…?æ«®¢ÚР?èÍ.Ôe&¡?éG”º?èfn Ìû?齸 p‹?éàûî¼?è²^ ’Þ%?èÞõ€³?è9phk”?èÝ>ïOæº?é'O|'¦?é"«r˨±?é±<€óŠI?é‰6¶_?é·¼ŒØÄ¥?è`!öÈ?éx\ g.?êí ævh?ê"¥|#c¼?ê¡Þ€´· ?ëÖHél/?ë€h<ÈÍX?ë<Û²ÚQ'?ëH?ÈíÂ?ë€Ð(Ú>?ël˜m¾?êYQÓí^6?ê‰:&|¹€?ë"£R¡·0?ëÔ\:ü?ë@•Œ³ì?ê é1w?ëCV7\?ì¯æB•Ü?톔;Bkp?í¯ž5x7?í“´m€–8?í Èß³²?íÛ^d_–?ìŠÖL·?ìwZo¦j?í¥ˆlÖ Ý?ìš >ã‡?í=gì¼?í7ÏTDù?íâx¾c@?ì4¶?íì!ņæ?ì¡Mi&?ïF3À”6?ïQ××VÐ>?_í§€?îø78Ã|°?S­¦ ?ïù­:ׯ>?ï?iÈPb?ïN“Ê¥£?îÇZ±QaV?4ž ©?ïã4È‹&?ï#+@™)?îcÂZÍ?ï…¼5õå?ïK›/FÀ?îÕ¦Ój4›?‘£ËÚ#é?‘`˵}I?ªŠhSXÄ?«\Ž„Ù?£j}rµ®›?¯š•‚nâ¼?tv ›§@?šÅÝ1ÈŒ;²D?¶'¢‡?³jÒjç=ˆ?½Qft–Uþ?´Q¤¯¾?´_ó*žó¶?¼ç$g ?¿ÍiP. ?²0-“VøT?¸Ñš×ìÙ‰?°Š*-ö?¶÷V¼øÂß?¹-ûxgu?°éêú4Ì?± ‹Ä˜5?ÅÛ±ª½?Å‘ð³»Ó?Æî£n¨K?Ç»„üƒ‡ü?ÄfþŽ»ú?Çm´aÃ9?Ç£¡q/F?ÄÁoé:ò?ÁþÙוhè?ÃØíWXSW?ÄÑ?aÿeš?ÈÕ®:?Ãe<™°š?ÁÙ h…·?ÇðeTD·]?À^–¼y½?Á©I-¦ÿƒ?ÃSŒ»?À²vJ€ƒ?Åö°´Ø?ÅŒÏË·?Àv…s­MØ?Ä¥NôHk?Ľ¾[Œ’?ÇN3œuç¡?ÊÇ*‡ã ?Ëê] ¥i?ÍÈ௞!?ÊS åéæ?Ë˼ér2Œ?;£Ö6?Ì÷)€FV?Ê£º‘á[?ÉmèMòã.?Ï#N_{ ?ɼâ¦g‡?Ìyž-Õ\]?ɲøÓŠz?Ïf£Ò„·ú ¸?È£6ÇA´Á?È#uÄ4RV?Éørz§¢?Ê9Y§ù?È`¦"K§w?ÍÑ0À¥y?ÏD®”½?ʹЄafù?ÉÓ©vn?Ê~*L©È?ÉÚ ó[Ñ?ÉpøE¯Ë$?Î.ñbï<ž?Íç Äp1?Ë8û_W}K?Èø†Õ`}³?Ìm ÈHÕ?ÉÇJYì?ÏtsÙZ:£?ÈP•o•ùü?Éw+îå?ÏŽdy%T?ÓŠi€Å¢?ÑVUò>?ÒÖ ¤?Ò}¿HÔª?Сc´‡“?Ó3±´äÐ?ÓHbéÂ×?Óá¹ý³ì=?Ò ÞgiÃÔ?ѧe¨B²ë?Ó P^  œ?ÑJ$ù¯ ?ÓÆÑÂ6¥o?ÒPÖú($G?Ñ'tµ›g?ÒqO.Ó8ô?ѼÍÓcf'?Ñæ>#+à ?Òeç ð³ ?Ò­_£Ë [?ÒEâ“‚õ?Ò¬ lù?ÓÇ1Ä›óG?ÒÆÇÓÑ?ÐgƒBë8?Ò•}^Óµ?ÑŽu~•àL?Ñ n=?ÓñB?Ñ´ÉŸñè?Ѭ•´GÀ?ÓI°×úû¢?Ó~©;^¶þ?Ó—¡yɶ?ÐrCV—•È?Ð0Õ93à?Òž?“Z2>?Ð4‚ßB"”?Ш;½¹ÅÏ?Óì¢åÊ?Ð&2¨d ?Óø\s™P?Ò-.½?h-?Ñq…R§ê?ÒâH;Œ™0?ÐãŒCnpV?Ð:íà/Œ?Ð ò¼£¤?ж«êëqI?ÓuÉxm’?Òl?\ [?ÓuñiQJ?ÖYGÍe!?Ö{g9Çè…?Õµm¤üÚ?ÔJòÄ1¸?×ÀÉ{ÝCÅ?Õê-òöI?ÔRÓEí7?Õ^|ÞÃÈ?×i¸Ñ¾ù?ÕKŒÂÍ…Z?ÕåÙ)?Öµ¿Ž°E?Ô‘«TÿrB?× 0êÄ?ÖÚWØ+Yþ?Õ}=&?Öˆo&j?ÔM‚Ï©0ó?Ô.Ú‰m‹w?Øj*§ÿç?Õ6쫦Ü?Ø¢Ò@à?Ö¾€_8?׉1>¬š,?Ôü¤29J?ոܯ¡?×8hñ|«O?×4Àû?Öv£ô^?ÖÜ8Z…Á?ÕÙS¼A?ÖRB1˜?Õ#ôÔ0Å?ר«÷T:?Û €’Ú¥7?Øõ”DzR?ÚŽ‡<4f?ÚEžx!íõ?Ø ";Ñõ?Øô´2{Î?Ø6Z®J²å?ØÔd:ü(?صKß´y™?Ú¸˜ƒW’?ØAà w@Ç?ØñL^‹?Ù›âÖÇé?ÛŽ©Ì•ž?ÙŠ-ºU?Ù]5:éÒ®?ßtij)†??Ý@?á©„|O\Â?áåŒï¤w?à‰GøÝg?ád¤ÐÍd?à 1[áR$?à~ÖG¨ú%?áo¼88<:?à6•ÖlÙp?á8Ø÷v¬?àÐO ²Ì?à6¹ÏG?á–ä‡ß“·?á‘pm§ ?ãØŒó·Ã?ãp¸#^…>?âxº%q»ú?ãà òÄ—?㎼M;Õ?â÷Ñ Š?âˆÑYiÐ?ã³(²+ì?⌞l6ÆD?ânâ1•Ì"?ãTh©H?ãímKÄë¯?⛦š2û?âÅæ®aX?ãRÛýIRŠ?ãäyéÝ?ä´ú²! ?åù©2O¢Å?æ=ˆ£?çÆÌË1”;?æ>À)õ?æ&Å–÷ù?ç?ïź2?ç¼È¹Súz?æà“ ìI?çÜ4÷û%ˆ?昢z_r?ç ^üN'?éÒpÞ/Éê?éÈlÎNák?é(—ŽŽÝ?èÈbÏ@Gh?éL;Öïa†?ép”U?éux(*´?é!c|V^­?é+ßO?è!j2£Œ?éÇpºi*?è5^|›ú?è±HA!?éŒ8A‘Ê¢?è1¹ŸM?è–‚gHÑ?êhzÿÔ?ë ëRc9Ó?ë6;®wF•?ê&~ËÈ?ëäq?ë›C\VV?ê`Ó$t?êJË«æ?êeùýŒÔä?ëk;óÈ(z?êÐîÄ'Dl?ê~©Ê?ëqC÷sÃ?ëϤ·…Ã?ë'DÍ$Ú?êߪì7ˆ?íÓÈáÎ%?ìtî)A‚ ?í·´ºá\€?ì»ÒÅj¼«?íV¯ü`óŒ?í»°Âˆ¥?ìŸ{"»±?í ÓE®‡`?í÷4 }?íh[ôP÷[?ìÕB³?ìþ‡s•?í”AŽp ?쫊m9¢¨?íu4=ç?í(pë6Ó?î‚R=×Fè?î¹F·:±?ï ße·µA?îöŸ;AÖ\?ïq@7V?î¿jÓEJ?ïÏPï´øö?îhš ëŠÕ?î\Uâ³ë!?ï ã2áDÊ?Xé|-?ïŠü*Óâà?î+Ñ_Vâ?î+ý_¶/é?î ‚Y†r!?ïúõ&ÆÐ•?~’æ9“Oü?®œÒå$†?`©‚UaM?«ì+èÓÌ?‹‚”—çü?š‹â½Ã•ù?§„ËfI?¬U9&¹?}LÕÚk ?†nóœ?z0É_Ep?¥Ö~-Ží?–‡ óm“?ªAFéæ4Ò?Ÿ·å/“úv?ž_dXsÏ?³BR@¡(?µjuã_?ð?³g²í;?»#"¿:þÎ?¹Žß£‡?T?½HæÜ .?µv_Ô ?±ðTÉ$?¹J~´?º"à¦w?»~£6š]»?±h¯ONe{?¼ó&6•ž ?·nûp^¿(?³fÓ+ÇÊ5?¿ÌKÚ«½Ë?²Pƒè??¼ D…=§?¶5¢G„?º¶B9ßJ?¼c¤æ|e?¸™Ý–ªÞ?°’ÍÓè=\?¾Ü‰ë¼ 3?µguÁËJ©?½7ä÷ÕÚj?°‰ªÍˆªÞ?»…ÀŒ‘Ag?µI³î74?½S¤Lb?´ñ=Ü?´8q«ºHÞ?¹3ž•n?¿Ý)ëâ?Ä]AtÆ?Å`º-/?ÁâI… ׎?Á=(fãʨ?Á&x?2ì?ÃW¼œÖjn?ÆÑ5×?Ç3Ø8—?Àï×êË«?Æx"‚\R?Ä^‚V?šFªÉ?Ã;Þ…ì?ÃŒ §:?Ã!¼,/¥.?ÀfQ Åê?Ârêæýb‡?Ãæ=©=ŸH?Áoøá1L?ÅŸÞ(·×?Çy’R3I?ÃP\¶ñ?ǰ4ø´æ?ÇÆ•,¿àé?ÆÙÓfžñ…?ŦÁ4?­ F äX?Á9,Tt–?íëU†?ÅpéLëo?Åãa†(tÏ?Ä›/¾á?Â׫bf9?ÁhùV?ÅÿáOz?ÆÊs@æÓg?Æë“m· ›?À¯ç<¨ä?Ç^T[hâ?ƽC,Çëp?À¨G>búå?Ä® *ãi?ÁPP_+?Çó< Oª?Ä_íÀÕ²´?Ƽ2R¸rÿ?ÁŠwÏ]£u?ÆÓ2s ͳ?Â8Ú?»[?À2ÅÝeß?ÄÛ^”È„†?ÂX ¦óý?Ít¯÷Kñž?ÌŠ>Lw/s?Ìà^æ @$?Ëò­(a; ?ÍŒà9Qpö?ͧ°s[Ý?Ï*“^Ž:?Ϩ?”?Èî‡A ¬ò?ÏäÁ’œ?Ï/f-+|?Ȇ¦—ýG?Î 1<ÇÕr?Îÿ3Ø1 ?ÉCø3 s?ͯ[ò>:?ÌVíüR–?̯.¦P÷Ò?ÈäwU?Uò?Î8!”Þwâ?ÎE¬w¯Ñ?Î ¡>–g$?Èf­ŽÔ?ÐꉢiØ?È„› ߥ?Éç²­b¼?ÍU?áJÑ«?ÈÕW7±4?ΗDÑ9M?Ï—T3ý#k?Ìœ^y×°?Êì«9,ˆ?É ×ÃÊkN?ȪfÛ$1N?ÎÁ(†ƒw?È:Õþø­’?ÎQ šû?Î A8í?ÏõÔánÌú?Ê»šÖ7…?Ï5p E?Θ’@Ë'?ÈíaV@?É:‡Þ´ƒÔ?Ψra=œ?Ë”·h?ËÚWÑ7?ËŒlf¬º½?έjï r?ÎÃò—K|?ÉÇùÄyç?Í„@"†*|?Ëâ¬ðÃõ®?Èhf#úu¶?Ï ÃD‰E?ËäÌõÓÚä?Îâ"Ï+ù|?ÍôÐúÅŒ?ÎR‘»–F?ÊŠw/ª?Í;ß•Ù?Ï*SH½+ý?Ï›„&çù?Ï$#7²Öö?Î"!.OŠ?ÈÿVÓØ€e?ÍÐ$pp°?Ïy£åÏÑà?Êÿ:çQòÛ?ÎÚ⫾u?ÓÓª?ÓuÑ#ko?ÐÚ°bg?Ñu½J? é?Ñ&ì¸É¤?ÑÌgÃ!?Ó,pšàc)?Ñme;8,?Ò•Ç{6Y®?ÑÃÜÐL[?ÓùJ7¢f?ÑTƒ¿™a?Óóbô[ ?Ñ.ÀDpR?Ò.Ö©ä‡ð?ÓAK6L?ÒcG v‚3?Ѩ5©-‡ ?Ñ×F¦?Ò=.Äþ?щ}mLŸ¿?ÓaË+?Ò Že?œF?Ñá&s?Óí ¾¯ã?ÑËÅíØ6?Ð×¼ž¡…?ÑEŒî#éä?Ðí¤Cg;µ?Рû±e?Ñó&8?ÓÅ·¯ç/?ÑxÕN½9?ÐB²io8?Ñ©=¨78?Òä@÷µ+?Ò9®¾bx?Ð`“2˜~w?Óï d£x?Ó ή:?Ðs{Wp œ?ÑÄÍåËÇŸ?Ðô¬OU­û?ÐúÜ_@¡?Ð[(Ÿ’˜?ÒbŸ#¢Gî?Ñ:Ô×°¨×?Ó Ðƒ‹i?Ñ÷þG“)h?Ó‚ig&’ó?ÓÀøC_?ÐH3rKû?Ñï†[×ü?Ñ,ÜÖ ‚`?ÐÁÓÿýø?Ò>¦¥F˜?Ñ<üôÐ a?ÒÙ %N:Ç?Ö´?¶ƒÐa?×ë©÷%Eà?Õo¼ðÀ¹_?ÖÛϾí?׎© Ù.¤?Ö &&{pg?Ög$Ô@À?Ô,Ê‚Ýê’?ÖŽ×"W e?×ìaÒ«$Z?ÕÅÕ”Ù”#?Ô4‘n—?Ô’[@O =?ÖØ7¡æ¥?Õï…áöz?Ô1B†„¥v?×%4Þò³?× ¢Ï‹?Õ1\p?"N?×Ùñ¡$Ÿß?Ôh‚ñg]w?ÔEò±8?Ôµ‚@óÃ?ÔãßÁV­?×ꉻó@?Ô""n%÷L?Õ«-Z‰Øµ?Ôþl ä2„?Öšï-+DÉ?×%Ð<1‡Ð?Ô³û{Ç?ÕùeùÍ¿ç?ÕðUßMÉž?ÖáÛR.?Öɇ–‚#f?Õš-:»ñ?ÔeÊò¦Ì?ÕèÏFE%?ÔÜÔFÞ?ÔÙûȲùí?×màÏâë§?Ô8ò“…˜‚?Õ$nå‘ ?ÔúŸÛgÂ?Ôû¼fœF?Ô®ó݇Ùe?×eÙ[íE?Ô ª¤ÇR?ÔÂd AO?Ö$~ØJÛø?×7¸ùåP?Ô§£Ãàb¯?ÛIÀìJ…¿?Øâ{Ø'y?Ùm„ééÐ ?Ú4: f±?ÚùŸ½=TÀ?Úþ'Å~èy?ÛÊñ_rIÛ?Ùµ­Sª!?Ù;FϤ@?ØD’gùŽð?ت¤^ä?ØwÊÊ6ì*?Ûωp/ï5?ØTJ•+Yô?ÙÑ}h‹£?Ø[‚–¡{ô?Ø}šäË6üï·?àÒÚ÷G¬?à1-xS¡?à˜šE67?à&)V}ß?áS³»Ûöv?à:U;l{?àô«}±À?á]›î®ä?àa*ã(,?ॺ™=]?à\Š ©âï?ávC8›Ù?àÂÊäÖÉ?ᵘÀ}ph?à§Úš3=?á?[¿!T¿?âXµïß$¦?ã]ûíâ:?ãO7Íewñ?㔤S/]µ?â£òg#Äç?ãíþr?ã,ß“×R}?âAÙÁÃb?ä…H©€a?ãB«Ï†ªÙ?ãX$ IÌ/?ã³YíX³?ã.³£•—?ãf %›à?â,¥ÛÐ?âÜzþˆ²?å]œ«?äLÎw…K?äˆþDl…t?åL×ÌØ2·?åòªê6?åšPlñuˆ?äa ø XÚ?åW—ñìz?ä,nÞÀ?ä;¥Ã^²E?䟶‹ËÑÌ?ä •f»}Ú?åJÓâr ·?䑾w¢Ýg?å||CÖE²?ä–rrƒ(ó?æÎ²ÜŠ"p?ç£jž_?çHWË›ÆH?çotqz$?æŽÆVÒ"€?æ=²›Ôâ?æÆVÉL?æ¡F‚8®‡?çË,Ô ?ç_ÿ?PÓ?ç'f™^?æÈÞÙnA…?ç‡Liƒ?ç ^Ÿë ?æ²Aâƒx?æï;ð÷?é&‡Ì?è)]É—?ér &˜¢´?èœ6w2qç?é’ègò£.?éWKî Ž?êåI”…?èp^F?饀‚í.?èT ãðÑÇ?èÅ^™L?è•ÂdNç?é3c›Ö|Ë?èÛšï‘î?è¯^˜Dwè?éW¿çX´?ê¢Ú~ƒnà?ëóEú¼œ?ê9bw<È?ë1Ë©ˆ‹Ò?ê$) eD?ê2=¥˜ç¬?ëwØ2æÓ?ê¥R„‹ðe?ê‚nrà?ëÖ ÛÅÆ8?êÄ:·¾×i?ë&wuÏ¡¦?ëÚ(Ü.??ê[ùçA''?ëX'ÛPîM?ëoü7PÎ?ìŒjW¦ e?ìlFñu:?팔jrWÎ?ìêmoé?í²à½ãná?í󋛀ÿ?ìmV¦]Á?íï‘'1Õ?íðAù0¨?ìù Çís?íШÃõu¸?íÐ0½\gë?ìf•êÍÄ?ì›ÂY„Œ¡è ®?°1Ÿ#"?µˆjlTç?¸‚=b&„?»ÿ+H(}?µÊ7øŸ¯²?¼À…ù&ë?°ø.>ÞÏT?·8šŒY?±À­úþ³”?²î¯óxæõ?º‡þæ?ÁÑy~Æ!Š?ÀËçvWvj?Å Ïà¡J$?Æô£q†ûj?ÄÕÏ?º+Š?Æ!qå­vD?ÃÀ=BT¬÷?Çm„ƒß#?ÄÏx8ö?ÄøOÆÝ§?ÁX ?ù?ÄÛ—¦¸?À·§x7ÈM?ä">?Ç“DÀ±1?À¯Çkp–?Å)ð&‚ð?ÇÜeW‹u0?ÃÎ}ŒÒr?ÄNžŒs?œk8¦{-?ÆP²^%á?ÆB²CÞ €?ÁÞ)Êû¦?ÂéûÐÓëu?Ãü8 u?ÂÙ+²ëP?Çòuárã?Äf>´³]?Å–`û éÕ?ÁNȽ|?j?ÁSؽ¨­?ĽÏYÃZ½?ÀÜ·Ú6äg?ÇãÑCTn?Æ#2f± ?Áâ9×*Åè?ÀàÅkâî?ÆÌ“CýÍñ?ǘÄ,©?ÃB¼y 0 ?ƧÓjz?ÅÚñzPïè?ÜÍ(vÜæ?Á +PI0?ÄùïÌ%‚?Ãí½/¿?Á…x§A˜$?ÇM¢Üà?ÅXPdÄÒ?Å»=.F?Å#ös£6?ÀjFytä ?ÀyÖ}¨âO?ˆj“àü?ÁrÈ©#ê‡?Ã/lÛŒÖ?Áøvï’ñ?Â39a·!?À)e#îKé?À®6!#Ú?Ç'“ œŽø?Á 'þiŒ¤?ÇZs›dk?dzP¼ð?Â6i”?Ì*Ýqv`F?Ê{*D¹×|?ˣ̗5Ô?Ìdmú…—È?Èì×F É=?Ð2~(¾&?Ïò$ÝwÔ?Ïãº1ï?È“¦¬${7?Èã'7ñÀ@?ÎeÁßZVÙ?Ì“žlÎEW?Ïc#Ï•¿?Ë${©9?ÊyÚ];è?̺n¶öEÕ?Ë£¬ž)vB?ÌÔ>è W’?É©˜Ñî%?Í‚3bØA?̼n½Sj?Ìž3´Âi?É.—LµãØ?ÊÙÿ´?ÏÁ”sÙJ?ȨF(àmg?ÊhõÏ)à?Ê»ªl‘8?Ë\"õÕ?Ðâì4+é­?Òìà!Ýi?Ó´¹_9‚?Ó좱å?Ópvÿ§Ï?ÐO ùn?ÒXßVT?ÑU›U ?Õ‹mvg?×äÈ×?Õf4×)-?Ô¬rGò—?×…!~úbf?Ö*ÎÕGƒ'?Õ-táh'O?×È""ñIY?Ö8¦ÿšþl?ÔÚt7/¹F?ÔÖT+[ü?ÖÁ/ë'ï?ÛçR%ï†_?Û… UÚI?ØþkÏA„j?Øj°âÜ?ÚèÏX•?٠ćÖ^?ÛŸù˜}“?Ùð±è,Ñ?Û¶1W¢é?ܱ×ÑÖ¢?Ù¦Õ*ÂÁ±?ÙC4XžØˆ?Ùƒ­„–o?ÙXÕ:/o|?Ù¡-؇Qî?Ùý´º< ?ÛÒ’AÄ‹I?Ü"Ì?Ø®ƒß±YŸ?ÙL„ü¨ý”?ÜÇÃñÐR?ÞÆÏœ&_k?ÜH’[N‡6?ß¿1ÿ¤þ?ÞJï¥Ó?ÜêÄp<Ÿ?ß9¹˨J?܈û´Íïù?Üå¬cjæO?ܽ«ý «>?ÜŽ[}¼Ñ?ᓈ^û?áÏ,û©?ࡊFzŒÅ?á©c«ªi?áñ€ô¬q«?àÎÒ¯ù7?á½(ŸlƒM?àÓ¢ßÝJ?áV{ëÊë??àed96?Წ¶³Ql?ákbKóY?á8×ÅòÝP?à¶ÕÞ?á×ø#O?áSwäéÓy?ãKOÓøàÌ?ãaOöj™{?ã ×gqq?ã,y“±?âÝþÜIj¼?ã›_ôV±?ä‰6Ô·Ò?âJáÊoO?ãrˆ)ä;U?ã²";?ãÔTö‡õÏ?ãã=¢?âx ?â&½œ‡Ãh?ã£W¿Ï;?â- œœÛ?åüe8¥é¨?å¥4¤'Æ?åç™hnV?å ÀtÁ‹÷?ä*á~Zø?å‘PY*{„?åQ«Ý½e?åfwu–?äZÁõß_Ú?åÊ4×ÃöË?å)?Fß4?åsûžÎ?å óaOÇz?åü@,è%?åÇ$Ñ„UT?ä$Ý‹²g÷?çCe8G´?æ{Ö1^Ž„?æ^)ó÷Ù¢?çDCÿ?æ]mð_É?æÚÂîcÁ?çk¸²¿Í?çå‰6Fº?æQ-á»­n?æeh¸ÜÒ?çCSÆóL?æVes‚§?çƒ@F éJ?ç«Sd'å?çÅ@ȲmÆ?æºæ¶T?é7{¬!Õ?éE‹Ê%½?éO÷áÖ€1?éTÏ럗F?èEéÉŠ?è§riP¶?éüÍ9ÜÒ?èXÕ?éòÑÀ7ç?éæuÛ¼R?é7s¥¼9?赺£ù%E?èIµÎÁ”?è¶®¦Ë°ì?èÀ–»ežô?èÞö_`ˆ?ꋆUdsŒ?ëY§ù9Ï?ë+cŸ;(Õ?ëOèÖÌ#?ë?“Ê<ñO?ênr"Ä¥¬?êæ ÖÂ?ë^gü‘×;?êwª&¶FU?êÑ.× Ev?ëÝ„éÃú?ëžÜhЮ‰?ë×$ÙN¶œ?êY1ä`Ã?êËÎʨð?ê,a“WÚ­?ììïK§ú?íFwÕT®?ìAÍÎué¿?ì.Õ¯pq©?íp´?WÕ?í-Û°ÿRà?ì{¦?¡9!?ìã«Z|Q?íòÁ%8ºí?í4k–{ê›?ìeOwð?í~ä'ÅëÎ?ìBIåòV?ìiÅýGÙ0?쌎HŒ·p?ì2Á àT>?î45®?îUn.V?ïŸ~7×?﮸ÖzâR?î …{ù!±?À¸ïó?ïÄ´ãwª{?ïe .ñó?ï’føÀ?ï‰Bôj?îíÿ.d?ïè ø(sg?ïß&Žæ?ïÈü»Lê§?ï{t$ad¢?ï­$ŽèE?8Œç„Z0?¥’ÞC©?…‹†º‰x?¨‡J¿[/ì?¨•ŠƒÑ©?–%\õ|•?—k[}Õ³G?¢âúžÜª?­ë@{cê?©ìFèã#¤?g5Y¾4•Ž? ¸t?ñ?rÚ¸sÒ§î?ŸÍe|¿Õ?r¼éœê@?¬h iqôr?±ëŽÀ.kw?±LmðH5U?½F’÷xÐ?µ¥×úEÁi?¿£kO-ô?²¡ñ± ¹×?´f\WÜ?¼Ê†Éѵ?·äüRvhÖ?¾ä îÖU?¿‹ê ?·Ì5˜?¾È[i?¶òZ"íð?´s4gÝ¡?²‡ÿQµ­?¸sRBn_?¿Æi:¬6’?ºSž/O?±×,ñ0ÕP?¶eÖ)dãù?¶w–Ÿ á?¹:ü,•Àm?¶‡vó æ?ų0Î?Š£?ÄDO_‡?À‡VìO·­?ÁB¨W]*?ÇìÕg·mŒ?‹úµñ®?À«×7žå–?Á×]8ÖÞ?Çwtº5?Â{ŠÊm‰A?Â+ -8W?æ­1ãÛü?ˆK ÅÄ?Çà™ÂV?Â÷æ’s?Æ©ã >ò?Ŷ!6 ß?ÄÅŸh ß7?Çv„Žƒ!7?Æâƒw…d"?‰›*}ã?ÄDln! ?Â’K(ªÕ?ÅoÀ«™`?Å ,Ÿ"?Á(HoP¼m?Äp>Âçª?Á<¸åä-?Âó‹ä݆Ç?ü¶0Œ?À6fTUE?ÄŒ.÷5«q?ÁI4o­l?ÄÛ?Ã-?ÄKnzËx±?ÀŸ§esÄ?ÆâÔš]>?Á¨1¿T›?Ǹ• ¡ß?ĺPzõô?Ưãz©“?Äð¶üOö?ÁÁy{”@Ù?Ç6ÔqñÃ?Æ!übŸ?Æ5‚\Á¹?ǵµ ¤á¦?Ãc즙ƒ?ÀyGÖb’?Å‚ÀÐEì"?ÆL¸^ä?À•Èk^E?ÁÝiþ5?Àö'Ÿ}í?Åàa>kŠW?Ãó¦õåá?Çttx{û?Çíd¨Ò ?ÂÉä;³…?Þ~ *?Æl­ñ?Á gç©?ÂìúÌÑm ?„ùÔ/¶Ù?ïü?Ådï™5!Ö?ÆõRÎ,f‰?À¤ö?ÐU££C÷?Ò«¿×Ôâ?Ó@«ºË_?ÓñŠm6Î?ÒSO(˜/é?ÓÜZ?¤e?Ó¸Ñê˜Ê?Ó@P÷y?×Îyñ¦‹Ì?×¥ùp° ?×EX‰JX‡?ÕïMù\z?×È(^±Y?ÖæÏêt*V?ÔÜsô‡¹v?×8Àˆ'²›?×”I%«ûY?Ö!Op6e?Ö±ß|†ô ?ÖaÞÇ;¦×?ÕØu´ÑÚÁ?×àÙ§·¿ä?Õd¼×¢f?ÕíÍßÅŸÎ?×  ‰†.?Ô/Rà°þ?Ö?¦zÜœ¼?ÖB} Ÿ?Ô0ªƒóÈ£?Õ¡¸Ùr?ÖgP ¶?ÕõŽq$OX?ÕËÖ$ A?Ô+zÞTà°?ÖEÇÁ„_?Õ‚å„Úñ’?×mYN0÷Ö?Û;8¡Éë?ÙÏ•¬½1?Û )ðBj?Ú¢'1:Ø:?Úh^²çCÅ?ÚôË¿ã÷?Ø*!Œ‘?Ù¯9,ß¶?؈{}k?Û(`FW¦º?Ù”• ¯¦s?Ùsë™íà?ØJcÌu?Úvµ}÷Ù?ڈǣn¥z?ÙEý$¤Ù²?Û_AZH±?ÙFÕ£†ç?ØèÔGO¬Ó?Ø„s|J(~?ßéZ/°·©?ÜŸkjÔÅ¿?߀0퉌'?ÝM°°åù?Ý@¥¦ˆ?Ý0|ôNâ„?ÝÅF)“¢¿?Ýt½‰hWP?Ý|m•0W?ÜÀœ[Ï?ÞÙ8*=HÛ?ácIË¢à?á-û€@Ô?à³Js2?àûþªÂ?àc¡Âb Ã?ásþj^ ?á93”r$n?àÅn¼xÈŸ?àÔÆæ«>ø?à\ý'¾Â?á+ã¦äøs?áÜå S­3?á‹DiNC(?ám@)gßã?àA¥Ï¥Ph?àþÊL_V?ãÐlÙ(?ã9¥²Þ1?â’ÖJ€œ™?â|vµ_Ö?ãGwµ5#?ã{+Ô?âŠRBó£8?ã_üêº+?ãÙ˜õ\‘‰?âºjÂ!öy?âñ+0©ö=?ãób@Jf?âåÇâz?âUõéGt?â%“hz?ã§”‘i«?ä?E¶é#?䲎˜ÃT?äŠDsw¥?äqIüa0?䙲_g’`?äx®!é«G?åFÿÄÍ?äËÚÕ>¦?åÚ öÖ¹8?äg›Ûê?å]r–?åy:C¤Š?äõ“6†E¸?åÁ`Ê˨M?ä*E™r¶?äçÆõÓ?çÞ|úæ•›?çЈޟw?çg| z&ä?ç¯f–KŒ?ç£cÏXq?æÃ.¾,VM?ç°\œP·#?çˆøN„$%?çn`Ü?æDíÌá‡Ç?ç§HŒÒÖ?ç$SˆóÇø?畈jq‰?çä¡§$Ü?æÃÖÈœc?è1>.¯?éë‰q@?ê¹C;W?éÜ\û“ V?ègÖ ½]?èA…ÀrÜ?è8•­¹jÀ?èz†1©µ?édLÃCQ?è ]P`?éËuMþN?é ßMÝ—½?è u¬û‘?航JŠp?èmm*Ì:?è÷k'tG?è_Ùüã?ëžø|fëQ?ë+w›G?ê½&¿Ú ¤?럨Žwä…?ꮢ¦'ˆÒ?ë}øFi}£?ë «^ŒíP?êºJ¯-h€?ê9ªÌ?êr~g?ë¯;°Ñ?ëÕ`× Ò?ë½Ð¨^¸?êalœX©?ê¦pÇÊ'?ê¢ |ÇÑn?ìlF½?ìê(©?íA7Ö¼Û?ìWÎz]Ò?ì99Ä··?í?cÏ‹ Ó?íÀèѳœÅ?í¶l¬låm?íæ¡Nò?ìòÛÐôi?혠ZÙ~K?ì^…è¾Ïy?펰FœˆR?í[áÚ?(?ì¢.vUáê?íÑ´Ø‚‡î?îAýÃÜ-ð?ïÐé &4?ïB÷çlÎ?ïiJp!?îH‘ùÂni?ïoôPÓq«?î$a›ÆR·?ï“qä’z?î•êa åZ?ïå¼ýA'Û?:R"?屮vãö…?ï%b«þ?î kØû†?ïC§·¯?î¨ÆÝ-?¬šN‰Nr!?•HT ²Ú?§ü‡…tæ?u~çà†Éê?]Û61?š1!Úó?Á:8Š­¬ì?Á~8ûèÃ[?ÁÇ™z §Ÿ?®‹VUaÇ?ÁJH¡JæP?ÆrRœµæµ?Âûÿ?Æ%"²Ð&?Ã=ìkå×?ÃM\ZÚ4?ÁZx¶tº?Ç.Óóûƒh?Ʊ±VÉ¡?Â#j ‘ô?À•»ÑÜ?ÂåÊðJÏ|?Å耽²V×?ÀúÈË‚x?ºz9¢çÞ?Ãð,œÆGÓ?Ç´3á=1?à<¶?Â…Iä¼®¾?Á°˜g¼Pó?εòi^‚¶?ÈŸö“ ¬ú?Î)agœ73?άòh  ?Ës 2w6¼?Ì/½‘ÏRÍ?Ïù4ér±?ÌV½äM¦?Ëð}nå?˱ –=hE?Éèy§I?È‘F§ìó?Íéø„š?Í ÿN#ì?ÌípŒ;ý?È2Uúüi5?Ï/óh´Í'?Ë&K©Y‘@?Ð~>¸G?Í¿€£–á?Ì~þC!­¶?Ï/“NH–N?ÈpÕÀMÙÆ?ÉȈlp"?ʤ $W$ñ?ÏŽD WÚ?̧>4‡›Ã?Ê]9©e{à?Ê·Zqéò ?Ñí–Eÿu?Ð;t³úZ?ÑàN¢Z?Ó¬q•(æ ?Ó¡y{›  ?ÒÂÇЭ`2?Ò}W&?ÐÉ;þ§Íy?ÐÝä*/?ÑŒíx/[?Ó£’Ôôÿ?ÐßÌ)Zí6?Ñ {_ŒW?Ôr;êw?ÒfvïA‚?ÓpbÅ’~?ÓŒOÔ?Ô‚6`£?Ò¿`†¤ç?Ð…s|Fÿ?ÐóÚ?Ó à¡Tè°?Óöšz{#?Ó”y°éó…?Ðóäeï\;?ÑJö¾?Ñãþ@0áf?Ô[ëÏB?ÔÊ£Íx< ?Öñßæžbš?Öo<ÖÁõ?×€\ò®?Ô3Jޤù?Ô,²„›©Ã?×ͧ!l)?ÔQºÅÇj×?Õ²õvPu×?×00S³¨Ø?Ô‡C/ü.8?Ö°—ÏÚ™Ç?Ôþtq€?ÖÌ+ 2É?Ö’—½°ªÍ?Ö?Gÿ(V?Ôj¸Á¾q?×™B]Ç?ÖÑpî\?ÚÊoùvTŠ?ØYÜPe?Úv¯9®?ØÀ³í‘ì9?ØX;?àqy?ÛÎrHíÓ?Ù¡½áŽ‚?Ù›MÓc0A?Û¹rÓƒ!?Øz{€?Û Y¼e»Á?Ü"Ú›˜þ”?Üš CÚ^¾?ß©Ùi‰ck?Ý>ÄüUžˆ?ÞóŒÁo?Ý…m¼ga?Ü–;ÑCÅ?Ý—mÜej‚?ÝYíQ¿ïB?ßÉb-Zt?ݬ%Û½Û„?àWùæ8$S?ànR•ýƒ?á|$A¹í?à>e{2TÝ?àھóä?૆péª ?áܘ줥?àAÙ·lG?áð±(¦‡?àw¦D=‡=?àvâ>è;í?à‚š_m$¬?àq¢;‰œ?àO!õZAã?àåŸCy?à(ù•™™??☪r4ã?â+%‡]Ö)?ãT×Ðh¹Ã?âÜRÝêæ|?â¯"~VíÂ?â3á‹âAN?ãTƒß©Û˜?ãbÀI¡Ö?âÊÂßpB?ãáå“v?ã&¢ÍW?ã8ÅÄñ ?ã.¯±­Â8?â^R¥ò?â•*x¼¥º?ãkX3F ?åß„ÿ3µ}?åò]€2Š?åjì ‡ÅN?ä!Q 6?äúk$Â7?åUkàƒm?䌺Vt¸[?ä­î›LÉú?ä_"&äì?䪂Ÿüÿ¡?åÕÄø‡µø?ä«¥·s?ä=!Òª4S?äü—JáÍä?å»|¿Fnƒ?䎖a"nõ?ç‹eÐ6?æ ÁM•R?æÍBÕ3¦@?æÝò8ð&?æ´V ‘8u?ç-¯–˜E?æéxLz÷?ç4_¥Âåš?æÚRõJ#‰?ç:ã·(€?æ®B œß?æÚnùL`?æâ; òÂ?ækë r?æÓ²éþÌë?ç3f '?é6èkØ?éÎ0ÚѤï?éÍÄÝ…ŸÝ?èRXÁÎ\?éÿ±CÛo’?èAI¿Zž.?èw²+Véí?è6¨ /A?é£Ô~в ?è³R¡0@ð?é:ûª•óö?èÃÖ¾ˆ}(?è®>”j0¥Ô?îC}©ýØ?ïˆð/iˆ=?ï¿`¨âº?ï‰@÷Ã?—ë÷ã?ǶØÝŽï?Åɱ‰ñë?Å‹ È»l­?ē?Ǹ`ë?Ãf¬|·)?Ç3”Y`’?ÆS1×*„Z?ÂgIÄmo?Äô®Úª­?Á¯H `ž!?Ç‚3Τ{C?ÅÎðIº?Ø?ÀBÕ"_¦÷?ÅÔ eO¯®?ÆÍ2dá“?Å¿ào–Aš?Ë@Û‚>`?ÍÓ@¹¯â®?ÊÉz¢6P?È…6±’è?ÈY jl¡?È›†Jº£?ÎáOuS#?Èšf ø¨?Ï·ha±?ÏkcÖ G&?ËUûúá1«?Êü›STv?Éâø©ÍìÔ?Í/$jºÂ?Ëá<¡õä=?Èúº}_?ËDÛ^á?É7 £  ?ÉÎøw¬©?̬øÃŸ§?ÓK¹¢â=?Ð RÆh?Ñ,¤¼ˆLs?ÐBÂûõ?Ð¥¸CÚë?ÐÊ™£eø?ÓT°ù ??ÒpÇ-s?Ðas6EW~?Ò'öŸ‹[º?Ñ&·¯z?Ò·¿Ç_±ñ?Ós™eþÂ%?ѬÙ€¡?Ñ'l̯ȥ?ÐÓ¶Á?Ð#ò <œ?Ó^KnüY?Ó{™zp¿5?Ñ\ŠˆÀ?ÖOúž†9?Õt[ë‚Í?×LPÀn é?פ‰ÂYÙ?Ö&–Ûwò?Ôø ` ?Ös¿ˆnŠƒ?Ö1ÇÜçÞ?Ö–?Ç]ˆÃ?Õÿî’ÑÐ?×rir^5Þ?Û oÓ“Ð?Ú2.³Z”}?Û˜×ϼw?ÚåØd<9ÿ?Û»ò-;ìt?Û“©Û>À?Û  ÌïJ?ÚÛÈbC[Á?Ûœ‘á g4?Ø)¢Ù¨â?Þ|O}ÿ6á?Ý}¥]ÚÖ?Ý—åiGÿ„?ܺ¸"ö?ÞZ—hug?Ý6 #Òi†?ßrI£E¯á?Ýæ®¡¢lõ?ß÷B6sÀ?Ü}³³R½?Þ¬?þÃU¯?á`3üy~î?á[7ítSt?àòÿ›Ã?àã‚åRvB?á”0Iaã ?á‡ä>oCY?ád?ü®pf?áö 6C•?á3u„^Š?àë÷=Ã|?áHÜ<¨8?áDÃò.4?á¦ü¹»Nf?á)«½²’›?á¶Êêh¨?àç×ú+@?âˆ6Vv ö?âãRýî÷r?â€Â,“§?ã^Wí¢Þ’?â:…™” %?âÁU |?â¸Þ² œÑ?â«&¢'=•?⫞³©?Ž?㿈â=ÑÊ?ãPýä£?ãØ­ºàq?â%E±!̉?âäW+ F‰?ãûYR“à?â3¡¸5sø?ä¢v…½…`?åS?ã3Ú“?å_cùözl?ä N@—é?äa`“ëä?äi B¦?ä˜^lÉq?åÒØè²]Œ?å<‡Áœ?ä_’2ã?å,c¬¬Ï?åOÇ÷çiÜ?ål¸3{>­?äõ†/zÙ?åy>Có?åâAlŸó?ç&;ŒÓT‡?çî í]?æ«þ’ÊÁŠ?æz-zEm?ç¾Ìº$&¬?æ!•|.¬?æV™ì–&8?ç-K›2ô,?çÚ´ó xE?æj¦"ç?æ¬Z¤ai ?ç4­Ä ?ç6Ó²Ô,D?çÃàÇä°¾?ç|Œ9ÃÜH?ç®'­?éU¿æwWß?è}Î8ó?èöÃ)áËÁ?é~Ü;úƃ?韼}î‘ñ?èª2’Ta¿?èJyÑy¬7?é™ÀkÌÖ?è…âD­®G?é¦\y£ _?é1«•3°Z?éþ}%™—?è8ªóÇ?è”f_I¼°?èvB$#Ç>?èÅ®ÃbÐÁ?ê~J1DKX?ꓚ_./ñ?ê‚ÆF…Ý?ëƒäJûéÍ?êD=ÈFËr?ꇶLw?®?ê áY£O)?êV]à!yÔ?êhÞ#ú4?ë?G¥u9´?ëðôõ›§6?êVùÍåE?ê`Ú‘8ž?ëÈà±ÛþÑ?ê9‘—ñ«4?êÆê¾·7”?ì8%ž2?ìdžÓ$SÂ?í†-œ?¶Žuœ8p›?²S,Ǧ®‹?¹m›È@ާ?±Kï* S?¸8êºÀš?ÃË\zÜã?Âk©þ„//?ÂÇêݾj½?ÂÊÞ!?ÇSm±"y?ÇD"Rk’?ÆÍ¢x! ì?í–ÀTF+#?în† «›Ø?gÝõw?ïDÏÔ–8ò?ïØéï€æ?ïÌXá§É?ïˆXS•ׂ?ï™0gN²à?î§ê~;3}?îÞbÕF„â?ïKk”ÑüN?ïúpàÖq¡?î±d”à‰?î|}ÖÖæ?î(=Jß2?îûKòÌþIzë?ž}ÛžÊuŸ?ªô¯ x¦?‹q{ _d?¹áÕœŸr?ºÈ<¥¿?»Ç!ŠÜ¸–?¾XæãÁª?½Æ³àÊ?¸¾{fãí?¾ŸfÇÏ`?¸:î~?¼CA`}ÒŒ?¼‹A† !#?´ä…À§?¾ó¦ã@?¿F8’ó?·A‘%—!?¿4ç%o0?³.»ëÓò?ÀŽÕØbþ ?Æá ?ÇÓ(8ef?ÁjWàyw>?ÀëËf9½?Å’@&ÐlÛ?Á츧鈗?Äè^‹,›?Æ;Q2€¦„?ÀXe¡0?ÇBžú>?ÀC$¶¨¬?Ãÿ\YˆSÖ?Ä@œçÛi‚?ÁOî»?Á~Çœ9žû?ÎÁ#Y:]?Í\°?Z?Ì}a/?ÈݺôÕ?È.åZ:Ù?È­¶YÉ^.?ÍŠ`-A?˰¼<,õ¥?Ï㔸nÌ™?Î_žÒ›i?Ìê¨ÿçÑ?Ê̺Pàrn?Ì¢¾“E?ʹZ" =ä?Ê0y"À €?ÉψuÓ?ÑaCSlû?Њñèi?Ó({ó£¯?ÐE3z?ÒE7$ÇI?ÔŠ~=?ÐQû{ÿÍ?ÑâFCç"?Óœ¹Én¨¸?ÑĆ`?ÒGï$sÀý?ÓM)@”Å>?Ó21á{?Ðøuå5?Ò¬?'â?ÐÜT6Nž{?Õë–l3$-?× ’ÏÚ“?Ö£×®¡6?ÔÚ=W§?×"زGæ ?Õ+¼Ð2?×`¡8{Fº?ÔÞLKçíÙ?փﰿí?Õ^5_5R“?Öõ€°ãûœ?ÕõÞ«·ï?Õ‘é_ì?×Ó sö Œ?ÕEí4Œ»?Ö8o’-i?ÛìòƒBâq?Ú»oùÉ$4?Øã>Ç?Øl6½¦?ÛŠa}ÓÿV?ÛÔ*,¬•q?غÓÿ¬­«?ØVÓB®y:?Ûã–›å?Ù)õ»¸Š?Ø Ù3°?Ø^[”]¸?ÙSEˆ¤Ëœ?ؼLi »?ض|/9?ÛˆØ:ý'?ß¼2'gôz?ÜãÈüš‹?Þ"6Å„Á?ÝX"m·?݋݆s?ß3pí/sf?ܰsðaÚ?ß”Yñ½ú\?ÞK~Ðær?Ý0-GÀy ?Þâ™L%?ÝÓžÆÀ?Üð}„áŽ?Ü$“3’¸æ?Ü‹Ѱ??á¢èž;\ñ?àiR"€vÊ?áÔÀõ~‡‰?àLÙ_“ƒ?àvª#§«ç?ànÞ~÷?áËœãoþ?ᓆþë?áŒ|˜[”¶?à—"¢|3¯?ãF3îŠÌÀ?âîV« ?â™gAÂF?â#å^z·?ãÕlï`°n?ãé¡_b?ã%o¨mõ½?ãß—Óa_?âñ3zОÁ?ãÉa%b ?ä9‡¢Îó?åtØ6ó°U?äÍpÑúË?å §Z¿œ§?å;+¹˜4?äò‡+C2?åV£ûÑÀÞ?å°<µoÅ‚?å Û“>N`?ädê,‚+ù?æäC ù“?çe$ B‘?ç^ƒûän-?æô;&Ðe°?æ›övŸZ3?ç¨0œ¨g?æjÜÉn?ænÞ)¿Û³?ç†ÔV¼b÷?çqt(j à?èXëêG?èüã.ñig?è×Úé]æ˜?è^F[Ï?éÆÐÈÈj?èXiì6S?è­_2Bv?èùgzZhª}Ü?ÎÓ¢†ð˜[?ÍûPÊž„ñ?Ì{ÍÀªP‰?Ì1}5p¾?Ѥ½Ì§SÉ?Ò ¶™Ps?Ñ œ¹"?Ò¼Oô-H=?Ѿ¥÷¯%ô?Ñp\/gÝ?ÑØŽ/rRk?Ó-hêŒÖ™?Òv‹Ú<(?Ó;±&.•?Ôj¼Ýi ?Ó¸Ðvéþ?Ó¨Ê v?ÒÞ¸ßê‹?Ñ2¼înÚ?Ò6­%î?ÔT7çˆ?ÖT5zk‡?ÕÍ^Ý ?ÖO¿ ¢¡q?ÖÞ°9â£d?Öqïn—Ú%?Ö&fÔ[%l?Õ‚—ýã~?Ö¾8Dq s?×ñÚµ8ì?ÕêNš­6Ñ?Ôp3Ÿ«`?×`óü%”?Ôà4†L˜B?Ö¹§Ô?Ô¿Zµ3?Ø3+—úº?Û5á.Ö?Û @¨ýx¨?ؾËüïá¬?ÙÙ?IÍ“?ÙÞþHvGå?ØŒS±È_“?ÙêÖ`îó?Û[aöéé?ØÿÔêgÌù?Úï°ôŸÚ?ÛY Á—?ØÇ¤•Ú?Ù]9 þ?؈‹ïx²¡?ÚÏ|ê0?Þø°ÈˆÈú?ÞŸÏ這Œ?ߟyÛ÷«·?ÞF_Šç?ÜÃW?ÝÍ^7¿o´?ݺ&Q'?ߎû.²â?ßÊb¢zi½?Ü ÃO-_?Þ8Ïû°µ§?Þ ¿ú7??ß§ªØr¶ ?ÞÓñ"ŽŸ‘?ÜiíÈ¡ã?Ý*µH‹?áøUàõ?àÇFç3ÿ?á—¨b/Ã?àà«Å+|?ài²íÌÏ?ágK帗?á2ÏÆÒ±é?àéÿDz‰?àÆ3>ù¡?áÔõ.õ|?â?yçÝ(×?ãF#ðy88?ãeBÜ?ã”Xo=6í?⪢šH(1?ã5sº›Õì?ãåA™6?ãoà`ËÅ_?âP¦_ʶ¯?âe¢Ó×®?äMb ”5?ä‰ËRº?ä²²­ÿr?å ŸY{ùP?äÚÖû•ñ?äÕ®ö]s ?äMŠ›„?å uŽØ?åt${·‚?åÉñ-ôÚ?ç€ÐF^p9?æÛBü7 ?æå» °°>?çþI8Þ N?çÐYˆSw?椪Œv¶·?çÕ<è?=?ç©À•EÑd?ç/KÕ“t?çW€FR ?膲AÇš{?è”Þ`äü¨?èš®o"P…?éàüög™?é‘ôZúÕq?èÊ>ͧ£j?é²´átÇ?é¹dš®†?éK³Š2ÎW?è]Iïuõ?ë˸¦uÅ ?ê8 )Eð?êc‘÷ç²ï?ë"ßz»ª?ëfËýœg«?êäZõqÓî?ëjðCµ“?ë o4÷lU?ê2%RDg?êîúãŸcÇ?ìÆv”Ÿ2?ìNŽeɼ?ívÐI´ö?ì¡q/¬Ó?ì½V!¾É?íUGÒé(¢?ívœÓT£?íõ õò€Â?íoÃΞ+?íÊöT?ìB êF¯î?í{“[)·?ík79 `??ì] +hã?í(žýÁ¸?í|{âôú?ïʤ®Tew?îÆ^©zè?ï_/æßð?î‘>V·6 ?î_Aîãæ”?î]åá% ?"_Ь?¼¦âÒQb?´5ñ’R N?°ñk2ì?¾Wæ]äy&?¾À'¿çi?¼¡™1v“?»¾ ¿Þ’ ?°IµÝ‡a?½[ ÓXE?¶“¥ðõð?µºò?±¯ô?¹Çyèpßv?°…F_'o\?À C«§œ3?µ¡R6«û?·UösÕ?ǵT*|X?ÂÌ*AVŠë?ÆÏâyÊÿ†?Æo!·bn?Æ ‘"HŒü?·š6èf¥?Áê(%¸û?ÅyO˜WØ`?Âx¹NPt?ƨ¡¹À¶?Äß½ÿ\¹o?ÃfZ¾Õ ?ÅzO f¤?ÀÓãwC?À—5 ò£h?ÅŸ_¶,rú?ÎuAϪFø?ÉðhªÁÛº?ʎڣУ?ËNˈðö?Ê4)IªÒw?ÌÊžyâØx?ʉ‰àí?ËÞ|(°?É?çH<Ÿ?Ͻ¤h~S@?̸Ž#·b?É"F±·™-?ξbY‘?Ï3Ð~i?Ï T/Æ…ù?Ímÿ´êl?Ò–÷»ÙÿQ?ÓÞrLw?Ð|ûr–Æú?ÐëÌQN¦à?Ñ\¶ñ»?зëéiä?ÐhCI ?Òrçp©Ì¢?Ó}ù™Èo]?ÒúؘU\Å?Ó ÍOðâ?Òõ˜Å)q?Ò×ha`v–?ÓÕ2fÚÂë?ÒŽ¢ ¸ ?Ò£wÚÔ?Õ;%Zü–?ÖÉ8'Jª?Õ¸Æ"l?Öñj0*ï?Ô ú˜q~±?׹ɗ¸?×—AÐÃ×é?Øjº— ø?׸èȘ?×T —ÙË?שjU‚UÞ?Ôî,¸÷2/?ÔÓ¬„B!Â?ÕD-^ 3?Ôí|—pþ}?×ðÄ¥s¿?Úð˜¼K™?Û)¾Èÿ˜?Ú8÷ ½ˆ?Ú ^¦Nü&?ÚïèxHø ?Û–¹ÉlJ?Û7Q-Pþ¯?ÙÚ†6Å~?ÚP§§ú ß?ÚëØóg ?Û&yª«?Û1Ñû7Cÿ?Ú_ð+t??Û¢²™m\?Øñœæ)í?Ü+\î·?Þ~Ÿå2\ˆ?Ý\µvxö ?ÞI·EÎ ?ÝmoÒ¥E?Ýtõ~›Õ?ÞØðf½£w?Ý\%}‘œ™?ßDñ_é?Üt$‹,?ߙҴ®]x?ÞÇiKE#î?.+?°¡JmWÂ?¸95Ä‘?·–f²ë=?µñÓŠ¼!S?¶ãÔª²??³Ù–Ã9?¿ÉÆmàd€?¼–_ºküN?³ù­§“‹;?ºyùb•n?°(gÀnÜ?À€EYƒ5?Æ\Áz2NŠ?ÄßΉüËn?ÄÕn|ÇÀà?÷Ì>N¢?ÈäÐ)ð?Ág÷\¯ƒî?ÂÔÊ%ø’?Çõto5¹¼?ÀÏ‚/"&?ÆÈáË: ç?ÂÙ™„ñÅŽ?Å,nPÍúH?ÂçÍ{p?Ä ü'¸ÁØ?Å®¦ÈÂj?Ëf»šøôÓ?˪Þã‰?È3•;„½È?ÏòׂIÛ?ÎHA{…8?È+õ'U¥£?ϸ]>åz?Î[јyx ?Í.?,Hñ?ËÏÌOóh?É™'§ºCÔ?Î8<,?Í*? @(?ÏùÔæîí?ωd ?ÊÖêk¢÷½?Ó è¬‡†U?ÑfÍL?,?Ð$ZÀfA?Ñò¾dŸS ?Ð ’’Ûãm?Òv…R ?Ó¿b!0?Òƒ¿˜oÌ?ÑI….În?ÒálI+U?Ñ'|Üã?Ó.ÁÅÂ?Ѽ»_~?ÓgÙOGÉ?Ñõnƒ•°í?Ñ̽ì ?×c±>ÖÐ?ÔjËcyë?Ô¯CçF?Ô²¸Ë3k?×w¹ƒÅe??Ô=sJ?×í"€Z?Õ$ªmŒ9?ÔçäCµw?Ôïì¬~?ƒ?Ö[×¶ÜÅ’?ÕÕ  Ž?Ö‰x#/?×ÑBÂÐd™?Õ=}?»¢¢?Ôw£¡"µ?ÛìšÏÕž?ÛWidØ(›?Ù,®DLü?Ûµ²„²ø?ÜêªòÍè?ÚwÂÛ&?ÙÂ~IEHä?ÚéxÁÒ—ì?Ù¦\޳?Û'±¬^%v?Ú¾8î÷ƒB?Úš"&"?Ø_´_0š?Ù]¶ úØ'?ÛPúàØ2?Ù?=wy÷ ?ÞzÝ{Í?Þª˜ ,Ó~?Ü*²ø´‘?ÜbÆ&Å?Þ•‡Ì]%?Þ’ÿÐÍD2?Þꘙ²ht?ß™ò0¤›?Ý–CÀæ¸?Þ‘PÓº†?ß‘[bö ?ݪ?f)Ÿâ?߅㸦(?ÜG´0EØh?ÝqþVÚ­(?ܘäv0Ç?áÿA}j°?áŒ4~ ®“?á L¡.Y“?á„Ø]gyu?àa†zßÀ?à!Í™çOÕ?áïQ>® £?áë´÷V?áxeYÔI?áÈu6%x~?ヨ•çéõ?ãsu‡¾X?â±€M$*?â·ªÀÆËö?ã´l·¯?ã5oÄÂa?ã2×Õa‚?âÒ?*úg?âƒç¤’ô?ã›Ý*8¤?ä ¼gQ×?富aú?äq’Èý?äžrŠœr6?åiHP”š?ä¼ÞÉGSÖ?å^ô–øe?ä¼³‡œ?ä Ç}h´?ä7øû‡?ä×@õ6ô?ä´èhçœ?äû™  ›?än` ux½?æÚ°¥Œô?äŸhr)ê?åÄrW~ˆõ?åÚþáZš¢?æ— ;R‚?äQÒsvÇ`?çj“7â?çË˜Ö ºû?æV½ôä‡ý?æ®&Ÿ}±d?çv@,Ÿ<ã?æ%y—¡êÛ?çhœ¥‚ ?磃üÁŒ?æÜ|]UXÍ?çTm$^p‰?æ’·Ñ,‘?çê ”Ó«?æF Wâ8?æ™×vÍ:?ç-ðùËÀ?æ—£Û#_?猕iú¾?çƒqWuz;?æîð|Æ`ë?曀¿?æÒÐ%-.Q?ç2éPï?æÚÔO?e?çüY:ž¡?é=T?é"Ãsé@m?éçpþ¶/t?è61¨„?èã:þþÜÔ?éi¬/(ã?ét࢓}?隈U†¤?è6ú…D'?éŠly–ˆ?èm¢ÚzÀG?èH*œJTX?èrrìëJ_?éAÐD³„Ó?è«#, ¶Ò?èc’¼z  ?è*7%®×?é WÁ{ò5?èi¦Ü!z®?èTö­Pzº?èf7S®;?è8Ò‡#i?èbþ½Õ0?é·ÐNã?”?ê/¹bpy(?êIq³]®š?êÏÊË„Hh?ë—K0g ?ê±B‘ÜŒ²?êPAÏÍ¡?ê+Q{=—ö?ë— : ~C?ëµÀ±˜ x?êWj!TS!?êÓ¶ý`‹C?êBÞq?ë„T÷Äq?êÀþ{Cèò?êÁVÐd@á?ëT{½¾Í?ê/qòÖ ì?êY‚,²œ)?ë}¸¤Õ¡?ëB+×_£?ënþ­h?ì­žVS?í:o„‚$?í¦4oñ:¼?ì©¶|ò%*?íÇH¶$WB?ìqŽ =Ê;?íŽ"º ?íéÕræ?ì41GèΆ?íA>ˆÉ÷?í¾ž¯ZÏ?ìž½´¼{a?íU/Ù6Ð?í bžµè?ì›Õ_w/?íIßaé$?í(îüv”?ìÒ‘ã½;?íµ_î)na?ìf JØ ?í»_Íôb?ímX„E€?ì7dX¼Ÿ?î$wx?íÊÉÃö?ì#`M8]?í zklË?í]úÌ(L*?í} B«!ì?ìÎe  ?íd¶Ó¿-?íh®ó™ïÝ?ìÿ¸²@7?íâ¸1ý±Š?íÊ2¹¤?ííü4o†t?í=ªýo?í¶ÿ•ùÜ?ì²V™?ìBtiÚÑ?ì ’߸8?íJ"¤+w?ì(³öVË3?í>ö®C¨d?ìÛõ¶Ñ™y?íåØ;zk?í°‹½Üth?íZÒ¶ýxÄ?ìZØxŒd³?íæ,á`Û?ì$P:æJÏ?ìµ9PIŒû?îÛ }?íøL’l?ì¼É Ø ?í݈°¢v?ì1eA$N?í J¨5?ìl“Bn|?í*ŠÕZs±?í¿à]‘s…?ìÖ r?í¾eý3ƒ?íp×jý—?íÃOœ¯s?íŠ‡Ñ 3Y?ïÐ( "?ïéÀàšEŸ?îsº lk?îÜNßçC?îXEØ<x?î >Yß?îc-Ý5?ïâô²’J½?îTÅÔï]?ïb/K)»?îûAåc“Ñ?îÁi*ôÏ–?î8Ã꨷[?ï£÷̬?îs ŸÌ?ïç|…#œf?¤g¹‰üÒæ?ª¹F´ô? ’ó‚E 1?‚n¾•é?’µÊ}â|3?¥Ì½°ÜŸ¿?¯Å•ŸB~?›)Õ}”É? QÁ¶?—-B•i€?nÀšŠoœ?”ž±„ì©?Œ…Dø¨UØ?™ðBSIÄ?¡Pîf£·?¬‰†£ÔJu?´Ð6_…]?²µnvÀ€:?º{¾FL¯©?µ1s¶¥@?²Ž®~êù?¶dA5}?¾t¦j²:?¼ó‚¡¿‹?¼‹Ásiò?¾þÑ &?¾C. «o?µ Ï…îÚw?µ[P‹Ñþ?¿Õfß/?¸ºøsí,·?·6¶.ÀÐ?Áâ¨8ò€?Æñ²—uZL?ÇÛԉ̴?ÀpUž_U?Äh¤( ?ÄÅK¢‘ã?Átw~·&R?ÅkdFPD?Âxi—¾?ÂgØÙ†ì2?ÇRG…I?ÄÔ½©éõ>?Á>&ú¥À?ÆH ÃÕG»?Ǻ³ä­±ú?ĨÈhðs?Íb/žáñ?É”WóÈ¥³?ÌLxƒŸý?Ï?£pÓ»ô?Ë ÿ?ÉŒ‡ísm?ÈxU¶%Ý?È4×C'T?Ì lÓfÕ3?̬ÌÁwc?ÍÞ°‡"+Œ?Éõa¼ƒ#?ÈÐå÷MÏÂ?ʤ©ÔÞJ?ÌŠ]ÔŽî­?Ϧ9á¹?Ñ™M·Øw?Ñ  À "?д3ãî€g?ÒL§Å!Ú?Óu)nÇ¡Œ?Ñd13ã?Óu9uWªT?ÓKù-÷Í?Ñ™å¿Iv?Ñ ÕÔ†ô…?Ð "ŒŠ ×?ÒëðްË?Ñ´nÓí?Ðu[läà?Ñ|Á¾”?ÒU·C†\‹?ÔtóŠÍe¹?Õgfpb$?ÖË (áý?Ö&˜2œ?ÖHïÂO†?Öc[ÆT»?Ô›Õµ"Ñ?×!y<-?ÔûL²Óé&?ÕœøêŸ?Õ]%»f?Ötç÷‹‚±?ÕZŸaEn?ÔÐÛÙ·±?äc>U˜?å%s•2+¢?å4‹²fGe?åHOà‹q?åi@'.› ?åOÄ#(²ù?å]‘®‚¼?å˜ÁðþG ?ä]GÒù·ï?äû};†à?åd}¯„x ?å0a< û¬?凙ñzìÖ?å¨52ÚV?äå ã>È?ä.#gÈp ?åié™ÙÐ6?å’Ú—?æÎªïÕË?çxïñ?ç.ë£&\ó?çþ 9iú?ç[çø¹?ç7iHm¥?æ”*rª:b?çè5aèh?ç5€Øúy?çX‘èÔ?æS?tÚ.U?æÆÍÀ%|?çÌÆÝ!?æ=Ÿ&ÀL?æìð iF:?æù(š[ÉT?æÃÄ bÖÒ?ç¶±]ë‹?æVoyÔsÜ?çÌõ W?ç¼M¶¦Ž'?ç×îá?æPÿJE û?æØ#"ûTT?éû]wUP?éiÏþmÄ]?é½$ªgïˆ?èì×´–†?鬘f¢9?é2/™Œò„?èµÊœÚ[?èÑcÛ´?è"FcÄX˜?è”û ¥-?èHŸ§ör?èãoÀõ™?é çäˆV?é­hµÙðu?é½&8÷?é? ;”ç/?è·;\/à³?é*#óçç«?èBþŠÏÖ?é\T6\Ö?éV\|Ñ?Ï?謟M÷þï?è¡.Ž›?é{ð»?ë#K ?ê¥jýcv?ëÍÀšXÞ?êzn%³t?ëkZkßÅ?ê%Mx²7?êWÝÎ+·?ë+«@k¦2?êÿKb¤â?ê‡âS sÕ?ë:+‡o ˜?ëd;P¼?êpN*¿¹?ë‹ Vˆ}^?ëo¶³h?ë¾Í` 1?뮌ހÃ?ê)Åá+ Z?ëݨǗ2?ëàt8?íÝ@º®n?íè<èЉ?ìˆ23úçl?ìáiK1­?ì”êQÌÙ•?íø) è#?ì1B¨w?캎yrüâ?í©ý–•?íÌÏÒ{Øy?í`˜-ûâ?íkæÅÇ£?íÒ¬ØO?ìÝeÿ8t?ì§Vmÿ?í£¸Ý(?ìTP?‘+%?ì—Ut€†?ì}™@åù?ìÌ‘hh!†?í“oƒ’ô?í@Úª‰R?ì}%÷`,?ìOÏ/š?ìyˆÛ j?íÙ÷ì3öN?ìQÄ‘–‚×?ì}9 eé;?ìê-Ä?íŸ b#§?ìJlG,Ì”?í°/оøn?ìì©ÝË3?í!B+`ý)?í£G¢Œe?ì´M%¯7Ê?ìªérš•¹?ì¾9KÒ;ì?ìÎqËÂI"?íøâï?íØL:Üv?ì‚uäÆ]?ìcá[aV¥?í©4C?찥łٷ?í‡÷s„?ì,vãg³?í¥ì/1¶Z?ì¿u«ÝvÃ?íÚ;á­ì?í›÷Âì›Ä?í¢‹ú\×f?î Ý «ñ†?ïÏaC—?ïú9îtï?ï ;©c«?ï™L\üÇ?î/~GŽ?ï­äu´Æ+?ïAË}=Á±?ïl¿•x}?îD®µtr?îÎ!×v¥?ïÀ_…bÈn?ï¤on>ùv?ïÏSûsc?îiÙY Ë?îžú.ðÃ?ª½jÆq?p–ªyÃä?™ × ÷u?¢ÊxT«a—?Uª‚-!¢?£¤9¸ûä?š-WÓ“±x?¡C²Ñãëç?^¹ÒXN º?O’äÑ~Ю?®»Éf0Ü…?ªX~VœÖ?zªc°b5?—¢ÃÈ$¬=?”a?2@(æ?­ … Äf?¸ñš]sö?³C0 ­‹?¿9HN¶å?¶±v¤³>?½ ãd‡åÛ?¹m›fÒ´·?½tH8÷?²×ŒÜŠû¯?µ¢QìºtJ?²Zj½à]Ü?¸¿˜ñ€Ð?º¸¼D$¼g?µW±su2?µÔSÈ’<4?Æ¡µm×?à zÄW¼?ÂbɈµ_Ì?ļTØÇ?Æ7AH¢Öo?Å/Ò—?Á£‡âU‘?ÀRµƒí?ÁU×I„8?Ã*…l?Æâ2Šé5?ÀÃÅf)?ÀÝ5R½—t?Æ Ð\¼¹r?ÇÐdC$ã?ÁÚ§ÿ8—Q?˾ÜK~Ã?ÊïÚ¹‘º?Ì0íI<3¿?Êu Ϭay?ɇ þK3?ȇÅäPí?ÌFÝaˆ;?Ì*Ý'r“ç?ÉVÌGÀÌ?Ïdç„2?È ¤¡T†¶?΃Üqj³?Èô# î?Ê]YI—ÅU?Èÿv~Ô ]?ÏòôÖÆk?ÑFõ.Ùœ?ÑðbXy©?Ñ <–~™Ç?ÑuÍhCÙ‰?Òöðo.õñ?Ñ$$ÄàÍø?ÐÿzòNd?И›­—I±?Òvg‚q|w?ÒÎð=ØhÔ?Ðã¼O…Ÿ6?ÑßÞ[5P?Ñw5„űD?ÓAÁ/± |?ѽ~ Ì™?нsûŽ@?ÖÐTqâ#?Ö²ÿÙ—å?Ö\'F#‰W?Õump5üa?Ø Bžw8?×'Ûx’Î?ׄ±®C„m?Ö¡÷ætŸL?×GIVví[?ÔÜ${“‘‹?Õ†½äæZ«?Õ}Þ´ 6?×3i{û£?×:a€,ï ?×àZ¼î3ƒ?ÖfÜ)Ð4?Ø‚S¼ ª5?Ú‡¿¸|B?Ù•´‰VÂ?ÚtïuX?Ú¸ßþd½Ì?Øln6ó&?Ú²¯é€?Úvó޽S?ÛÎj‡ÊÅ?Ûlú¡?Ú·rš‡?Øq4Ÿ­T?ØM»¾ÙÇù?Ûzâ_Óñ?ÛEA¦sxÝ?Ø BçùÖ¯?ß“‚ 2 ?ßÀº=\É?ßïò•$?ÝÌ£htë?ÝÊö/‘ê\?߸2ê6ä?Üã,‰œ`˜?ÜQ3n»?ßHàüÆÜ?ßÁ‹wÿÂ?ÝŸÞ'½?Þ§AŒüÅù?ÞâaÎæh?ßKŠ¥V=š?Þç6>f¦?ÜkÞj)2?ás˜mçìã?àYjq­?áÔXSoü?àÇÊÞ»A3?á)W¡Å0?à{rK&}Ù?à(ñµ¦çÀ?áQ˜/ãý{?áoXÚfí4?á—|éJY~?âLJ÷ Œ?âñŸ)ÈU?㇌c:ÂB?âg꣈ ?ã“‚I2?âÜ·M›ì?âËû`º—?ãi¸Im¶ ?ã‹ìõɸû?ã>>¬?妈¿ Ì?åj|1]û?äœnŒ?ä:µÀÃ;@?ä d\¡¢?ä19±’ ?å n‡Jž?äŒ~‰ çŸ?äïËñ[!,?ä«Óòé?æ›¶Éä?ç)—˜•6Õ?çä5á`œ?çƒDFKß?çŽô] F˜?ç“y¥íI?æ’âsÖ?çŸ ƒJˆ?æ)¢æ ôU?çYÁ–ö‹?æu{ÃKþÎ?ævCªYKy?æ‹Û£Zh5?稶ÊÚ¢?ç÷Ú¹“»?æY£D;,?ç¡ÑŽÉJ¿?æ’Û­"»?çµy´ 4 ?æußœjËÇ?çÕ€ï’àE?è›.j%a??éR¿Ñ ?饸{î_?èBÀ!™?è a}.Kë?èý{2ÄÀ©?é9G£Ó(H?éjÓûýn¨?èŽY~).?êeÀ}Ù?èÝïƒhŠS?è/Æu³À?èÛ{¬¶µ?éçáƒÊh?èü{Ô—ù­?é_Œ™zß½?è˜S'– N?鱡+ÃÂ?é#È!®+Ä?é–¸®Ö‡?éÈÈB?ëU/³Á—Ê?ëü•§?êRÓXÔç?ê°b‘-Š‹?ê2}˜™2é?ëghÃσ?ë`<ªÄŸ?ë´„|Nå?êâdà¡L?ëæå/à?ì|¥æe2?ìíõû?ì¨"nö^k?ìnB÷ÜP?í³‘Ïã§?ì‘zI‡%?캄hõ?í†û†Ù‚?í¤,9:7?í+º£iíX?íͯÍÄþ?í´FÝ_?í~áôjY?ì4-&Óº8?ì~Ez’±ø?쥹¬È”Þ?ìˆAµãú?ìñRÄAY ?íö¢LøG?íÍ8'M<"?í±×øûô?ì8ù@(¯˜?íŠï‹Ò)?í׌•º\í?ìØ¢;à%?íU¨eO??ìÝVu:B?í™O¦”Ñ€?ìþrUäé?í‚«w¶¯!?ìýî`é?í6u¼æ?íÄÔKÈ=ç?즱æË¨ã?í²¬ Æ?íË3Ê9we?퇻ˆÂA¼?ìÆ<‘²?îcÃéÎe?î|‚*l'?îN™¿œ7Þ?îÉ>¿?¡?ïðà0Á?î£m5ÑÝ?ï $‰›*?î3‘X ¦?ïqOµª—Ÿ?îuu|#ÛŠ?ïKÎã-??îíº("?ãr@2Ø„?ãɬî¥Ý?ãA·íÅÜ ?â$)ßžNê?ã§øïÊ­¸?ã(Œ¦U&™?âdbìk •?ã«9±Md?ã>,¡c…ç?â1FÀ&?â.zÂGÜ?åN ©ªÒ?äõFàÄ?åûY>40?ääc‹~w?äÇžÔ‘ÏB?äG%Ù¡[?ä² ¸a¯?ä¸îÓS) ?äø“uå! ?åòYs¬ß?åàUŒ5_…?åÊC–5ß?åDÙyÇ[?åUk•Þ:?äTã÷ÑýÅ?ä‹/ÂàZ?娒¤M2Š?äóFµœ?åôz’LQ’?åÛ–oRY?ä‹”!}’µ?åñ ñФ?ä²¼å÷úF?ä„wòÁ`-?åT=J’&?åí|CÉ?å"­HìûÈ?䉌o1[3?æ[,F?ä¥tAÀÙP?åÕÆ¡ßOü?å"1k?äGQ;?厒ù¨¸?ååq‡35ç™L?ç–ýw*+Z?çFDõöOp?çQþvè?ç#µðK=?æ=»Z7î ?çßeó[Ÿ?æ~ó0‹?çd¡þ?æ‹ûè#–¡?æ[œ¬ˆ?ç›eŠÖî?çS].¨tE?æwO¹§ã?ç„ánÇ0Ú?çpçÇb%?çÖuîPƒK?çgA/ÔòW?æ¶Û]??æMëxÓ,ó?æê¸f•V’?æãœŠ)Ü?æäèNBé«?çï²dqŠ?æ¤Ì­ûe?æñÀc'ð?ç=xý2ø?çF„ëóÝ/?çS Ç·x?æñzG¹?çÆÉË ‹?ç†ÑjgÍe?ç]Y›BÃ?æÅØþW?èj¶Ûå±S?見 Íá?è=Öiƒœ?é^t‰†ûâ?蛫Ï?é÷|dïi?ék8LÉ;q?é —÷;…?è¥ó2¯Ë™?è:Ú‡9.Ä?éðUx0Ó¬?閨آí?è Æ;[*?騥Í]^?èv"ò‹—?é|ð‹4º_?éÐù>Èv£?èÁ»eüä?èF^ Îç?é£Å÷¥ñ?é¢ ­ßš?éž`Ú7{ˆ?éÔ%‹¹?é˜ —ƨ?éZ4ƒ•,4?è‰û§Ór?é'$5kk?é®5Fо?è›w7o$|?踫a??èžó'§ñ?èë¨Jð?è󷦚…œ?èúÞö…Ô?éì "&Å?éÃ&…¹P?è'a¼„p?éëWó¢'?빤ŒÈ?뚈UðÆ?ë&÷}øIÉ?ëÕPÜw¬?ëgìiˆÄ?ì¡/〵?ëWçÛ:ùË?ê:•ïîü?ëã4½S·ï?êçRè@G?ê êÇA¼?ê#ñ©9û®?ê™âkÃÈ?ê`‚LpˆC?ëÛ?Oý?êÈÚÉ¡¬c?ë+oJS1?êžêiªŠ?êHUï¢e§?ë—œ¢¥?êF©Ý/?ë7“ív ?ëlë½Ýî3?êG•©.n×?ëA“íÆæ–?ënÇäØ_z?êÁ^¬A>?ê ¥ÅXžŠ?ë&«@t+7?ëF›ç߯Í?ìE\)ýŽ?ê[2KÈ·?댸þ^Ù?ëc(7¸´“?ê éÄPÑ?ë–H ›?ê%ÍWöF?í]¿¼ACB?ì½Ú”ÙY#?íîM ‹D½?íO]:BE?ì:Ù§Ÿ–-?íb»í›{»?ì²&ˆ¹Ý?ìAUÒ/À?íF»b7|T?í¤<¤Þ»?îíW?íhgʵ?íh¯Ë{?íâÄc%‚?ìáF’÷â;?ì_Q¨Ì¹?îU}¦r0a?î ‰8‚ܹ?îVÙÜ3ñQ?ïIÅ¿‡ï?ïçLÿ  ñ?ïx çÑ?ï€AÿR?ïòÔìÐï«?îìJÒ_Ñ?ïÕü (¹?ï¶8¯½Ù?î` žŠ??KöÇ*?ïá”}=Rs?ïR`Р?ï–À(ùÇø?®u^4Qú?¥|:¥º?g}j·g°®?©T·¯\?¤Zý:åÙÜ?’RËmoÉ?šõ[9"¢è?ŠÅ(ß+?¬j‰ï÷ž?¯†Od†a¾?ŒyÍð?yÊŸ¢Ѐ?€ S/‚0?™JO™j!ü?ªÇF4˜œÓ?¯Ï?)F?¼Ã"Ö©„?¿!‡éí×?³^P­7Ì‘?¾[&»ÝÛ/?´§ó5kÒ?³n&ß?µw´Ž‚ç”?ºÞDò~?½qVÍ?¿¶Mni?¼RÁbiƒ?¶ 5Yx |?·ÉëQý?»(~cž§P?²Z Àÿ-À?º¯ö¬‡­?Ç3³>%Ýê?ÃF«t€ó¸?ÁÙH¥ã(?À8…r 4¶?à ›3μ?Á_'¯ùq=?ÁÒøˆ,¥?Æß‚”l9ñ?Ç.#q˜q?Â(¸WJ'?Ç’‚cˆ?–leÓm?Ã{P³Áð?ÅS.¹Úß?ÁWâ–ê?ÆÁ‚BfN{?Ì )ÜP6?Ì2}cîœ[?Ê© xBA?ÌL­’Ôeþ?Ê´Šro•Œ?ÏdS½N¶ ?ÏùTç]==?Ìþ®ì;£?É1×#~,?Í­P3z#Ÿ?ʸª-Å¥—?ÊüꪢÕÚ?Ðjˆ8%…?Í]ÿj2à?ÌÑþt>Òã?̹NVG†?ÒXo5|>q?Óg™Gý˜æ?ÒvÏ`(?ÐÆ,Ó|†?нÃõH©F?Ñü£“)?ѰÜÓ P?Óž Äz%?Ò]ÿA&õ@ìåÒ?Þ7ö·Ç?à~R_ÅEš?àÓ÷œæ¶?àêËa‘n?à¹d?8ì?áO›âi@?àÝX(?à[µÿðþ'?áô‰B‘eý?á(/ÅòÓ?à±ÊÛÎè?áYð[QÈg?àéÞƒO?à òá%™M?á^Àhé|[?áJ¨%·*?áÐE‘q?âìÓE¾ö?ãÅààTUë?ã#Oóç_?â6Ñ©8:Ó?ãˆ|U¬–?âжíÍÔ¥?âƒþX†@ò?ãAgÞ‚' ?ãáAKõ±z?âM6?p¥ ?âÕwr…Ÿ™?ãoàª÷¦?ãBèBG ?âÑÓige?⽯º1?ⵉ õb?ä\f }@M?åd|iº3?äfZêÿž?å%«I??ä«ÖšÀÃb?åtÈ-ÍšØ?åI_Ý>L“?å!+—L¶k?å!'²5GŠ?ä¸öûÛ_]?äßÃ?¡¥?åGk>˜?äGêd@—?å~~À1k?ä+¹÷îŠ ?åãtÊ?çHGÔÌÔ?æú¯7}çæ?ç^îu4?ç/矃S?æ¥^–3F?æ5™¬¡k£?ç9+µ)p?ç03¦B?çê‘T¹?çOs#¼?æEåô¯Dä?çK€Àú½?æôX¥¯›?çùY1°ð]?æ~j]z¨?çû¥59äE?銬H@?éÿA5Hƒl?éj°PYX?év,'>2?è4Ù¦’‚û?鎰ViÜ?è—¶i„uº?émè –=í?é?3¦È‰ù?鱈€(z»?éÛü˜Qñà?èuuTv?è\ åAóë?è4Í¥"a?éj«çÄ+ß?è¸Þ¡Ñ»?ëœÀ_&„Ñ?êSéÚœl?ê…îAWK?ê,’Ø~?êa]jt?êvŠ"qþ?ê Vt£¨|?ê|¦ ç‹„?ë_ÃÐò6Õ?ê‘ÚXÂ_?ë½?‡l?ë˜ ü€-³?ê¡”ò,?ë¼p09£?ëßH¿æd?ëÉ| or?íÖ¸ÈÍá?ì…v.ˆµ?ìX]å×%?ìØ^ê²(\?ìF9Çݦ*?ìž²qc?ìÅb®¦ í?íáØäkµ?í ÷å¸M?íi“½žÇÈ?ì‹=¼¯¡&?ìýb«©(ç?ì4ù U2¼?í÷@ɦ·a?ì¢4}ÍB?ì¼ZvÐ{s?ï¯ÐpA?î ©?• 1?îŽ2´¼¤?î”–dðís?îdR¾V?ŸéZ?ïð- >di?îEݧüïp?îÂ~„T”?ïýô중?'V?ïJ£6¹=y?ï>õv^Ö?îg=ŸmÝ?ïì$»È-?îcaØE ?ã}q¦ ô?äPî ÿR'?çýO¸‚i?è¹j¥¶4i?êpÁúCž(?àˆ€ˆ¾×?âgþC &;?äïõJ˜Zæ?æƒ|^‚E?èýg'žf»?êYÓÄòºZ?࣋5ë/?à 3¹¾s?à$kíˆL¿?àÌ/SÑ?à†°·¢å¡?ì4Ÿ—Ë?äÐZ]«Y…?ìL¤Î‹?ç ;Í?WDÎÒ74?¯íÓE°t?¨uæ?¡*÷G#åV?–ÕY«`¢1?§HÎw?zÚúû=?§þÉ-ûý^?©R 5•\ì?’¶Ó"ü—?¢f9°se?b6µªX?£¸:¨òð?þÇG{ÿ?ÄÔLÖ˜§?— Ó3VSÐ?¢f·•]ÀØ?£þ{$¬…8?“PÍù:7?«X p]íª?¡šú¾5YÊ?¦gDÜQÒÈ?ªbŽNÌ+?—!à»·à¯?‘lÑë•?˜ñà3oK”?—"ØQ~]Ä? îµÝ×5Ä?¢ wXVÏ?£7ù*Geí?¡ù¶|óÒ?¬ÉŒ3ƒ½B?­WÍg+,•?“æNdCÀý?«@ Oôm ?‹¿Š¡×6?¤1½ík1!?„å…¿g¹?b1¥Æ+ï×?^³,ëÆ?˜™Þ2yÓA?¯½Uy„±Æ?—iÎÆç?§îC –?›c\Öô‹à?ˆ«{\/?…˜t‰ùE6?©›…À•Gç?Ž‹†Ï‡c?•ÚÐè‘`Ý?˜ÏXlP¹?pR½…N3N?¬ŸNyÝe?®ã;!½²?—ÜØ•V„?Šî’Óm?®’~Bò‘?¬Ì),ç§?sêÉÍÀ¸é?|¬Ì³ ?y?€Îl,?®î­2m?~Å Õí;?\Úd˜,õ? œ²Ìݦg?¬~Šîbv_?§ÒWÇ•r?£®ºŠ°ï?¦>åIC? 0µ •fà?kÁ}ÅØ©¿? ów`–A?™bÜì+? 5@t ø?¦¢À&/ƒ?¦ä?PyÃT?Œ|3@Æ?Šsï2­?‘‘øqË?hm"M€2?¬IIA^0î?¤¦úì.‚?§`Ár¦™?Ÿ§cÞ¸“í?¡/õÑðbå?–ÀTàv?€oíï,$?œ…რ[?ªˆƒÏãZ?§(}Ý?®Ï­q°È?—’ÐÓõ{?¯œ13Jå?… faråó?”¸D¼ë*?œÖ#o&?§\¾¦`?›±ÖéXŒ†?LAî.4 ?–ÁÎq?‘FÅœÛÄF?¬‹ Ø$}?¯âE臲?ªÒûŸL?І€( Ó?l‡Æ¬Ðb?«.‡{×V!?Œ‘vbáÈ?¢‚2ó¾b?§z<–U]?¬“„¸7ʽ?¡á®~nýÓ?«;„!ç?ªõÍo±?œÊXû‘‡? -°Gú?‚;kì$+?«‰I>Ž¡|?®ðÐÀ+?¤Ó¼p؃?œu\ª¯Q1?š·Øx0?¥úÌ0Î?™ˆÍŸ[À?jReáë?°eÁ–âß?¥´ó3€?xa!ƒ´ð?¡ùîW¥á;?«LÄë`˜(?©’öOh?¡ñ3Šb¼?®ÒŽß%ñá?€´iP^Å?¥(¼é1]™? k32/}?¡?4Ö4Ñ?¨«BPâ08?xܱ3áa±?£hõLv?­]G'ì˜?­ñ…Ž z?¢7*Ë\M ?¨E÷Äì})?ª[þÎõ>˜?BÍnä9?޼Rêô*?­’ {™Â[?‹ø~1d$?ˆyRŠÑ$?ªŽ‡Ïó Ñ?—XS«Èšª?¦K¾rõ®??¥A»xÙçv?™ÜT)ŠÀÄ?¨À¿†âAd?šäÎɽ?¯QŠ ®/ß?¨À9u»PG?¬5Ášè?—«ÀµŠJ?’H5üEä?˜¾Mf™Ý?¦ˆ}’ÝÌ·?‰IxÖo•¦?ª†‡›K~F?n4†0?tÊÀ Ú?XßHÀ…??­ Pÿe?ž÷Ýw$ä?¥k:Ž´ô? 2--OÕ?¥Õõz>u?®&†sìGq?dÔdó#?ªŒA3dvœ?Ùc£uá?²Yß߬Ù?g­6«1«e?}üÌŠÖ?¨†D÷8c?¨ ƒ{éè?y4ÌY˜ø?ª]‡|ñúi?˜´V j¸q?[ÚLÔ1^2? ÷ò¨ÈÀð? ä¯1ÇGL?•(ÁOVâÇ?’ߺ֤ F?¥ø¸Ý1ôh?«SBêD°€?uH›Âwg?£ç·±;Q?¬ñŒ?jù?—‹ %æ?©GÞ„Ý—?ža䈲…Û?cih·;.?˜X’ª”‚?¥þþíJI‡?¤a{ Lõ?¥åýìð?Z¦Ô?¢ó“"¿?§Òþ0?¨’~äÿž»?ŽºxPËð4?€÷ašÇ†Ì?®ìÏŽ.š?º1^1 ¯?°Š«´Ø?»×B_+ò®?½Î&•q5?µNul¼Ãþ?¶ˆ×õ«(?»˜R¡?¿>èa*?²Ò(y?¿ÚܧD¼?Àôˆ½°±?°Zè·öJ&?µ²óÇf83?°éª3tÇH?½ã)1?»=_éÊJÙ?ºpŒ~\?³§Q”[Ýû?¶à¹BÏ?µ¦6¾ØòŒ?½ŽfÛ?°¾¬1ÏÌ?µËUôÁ?¼oºØ£?¶I6UŠ:|?»hŸü—$d?¾G…Ò…‹£?¼×‚ŸKÉÞ?´WQðì?¼R¡È_³Ø?µ`Ó׆¹¬?´)ñýµwì?¾áˆÁwѶ?Àuì¿‹x?¿•K`jl?¶7ù*?ZE?¸46Uè?¶XÁ¯Ÿ?°¦-М…•?³.R³J+–?ºÿâHFç?²ñUm¢?ºä¢Hz?½BFã7>s?»KâèûéŒ?µÜWó4‰¬?´qÕ™±“ú?¶µY€Û¹¶?½¨GZ]!/?±§O ú+.?°ûn$‹º?¸²}¥Å‹í?¿Êj jƒ?¹¾3—(?¶mY€»?»âÎŒ¾[?À Õ‘ÿ„?±OŒýùxÎ?» bJ¿ì?°–*ž$nÔ?½ž$ÆÑ|8?·¨ø¬µFx?³ææ5uK?¶ðwLàŠ?º ýåî£?¾Ã'·+Æ?°Ò °ë ?º\À¢áÐ?°òŽ‘|?¿õ  d?¾)]®‚?¶Ø¥Ô{…?¸"<ó”&¬?µ8v­Ú[?»ÇcÊ.–õ?ºæÁç{Õq?¸SÝ_ ¾?¸2|¹h¸?·–{ÿ’åL?²„‘Ëyë×?À¦AÎz¡?ºÔ"2”äs?¹ŸÕ '¥?¾–)w…4^?»•£·Ê^?¶Y NRo?·ê<Ä­I?½òhjÒy%?³5-Èr³?³7’ñ†iß?»zc¢'æ?·^»šÌ(í?¼j¥d³q5?¼KE8Á‡Ã?·“œ€7ó?²²²ÇQZ?¾j©<ÕK?¸|ê\'m?´bµI`]Ç?°Õm¾3aÜ?µµw°L$x?¾)«m…Â?²APçŸät?»‚NìâÙ?¸Õþ@ë3?·±;û CŽ?´Ub­•?ºA`ãÞÁ?¸ØÌH?¹<¬¦Ï°?º%þVôô?¾T&wy5—?¿÷ †]Üä?¹Ž\úAi?³2Ê4X~?¶ª6àÒ=c?¹Û b™º?´NÒ„Ó /?¸ÇVziœ?³És•„¯×?²!me¿?¸á~/‡ ?¶ØÙþO³+?²éÒN÷?´‰Õ0ÙTù?¶*xájêÓ?½ÎGúØØÛ?»ÙäÖÏÀ?¶=¸Ø î?µ/÷ f‘?ºœaÆö¨Y?±»0Wb¾f?¼*dÿ18É?¿òLFfh>?»Ø$FzìS?¿J¦IÕ?³8sTÐYA?¸Ü~~'XU?²AQiN~G?½ËHPhá?¸e}“”µR?¿Mk'å!?³EQïY=?³M<‰?³2sHn},?µ·X=ËŠ›?»ø}“–?±ÇÐ\µÆa?¼Ê¦5ÊhÀ?µÙYì?» MYÈ?¶$øî&?¶aŸ¼í?·Ž[”ê¤?°+Ìã‚Æ†?¶p¹ ÅšÞ?¹H¾ãÊ®a?¶·ÙÎ0%U?·{;UO*‚?°NÌ¿ùïØ?³AFÈ :?¼¼¤Lù*´?¿Sè²Þjv?¶¦öûq/?¹~<”‹:ï?º!½ºÂ•?¿&‡Ö[ô?¸a3ö&$?´æStJ|?´»•K1?½Â¥ã-Ðr?¹}^'1ŽM?²ïé*í;?¶ô§ßî´?½™'B ?´T¡‰ ?·LÛ3«åC?´¯õûì{?½vFûßíé?²GÑ!9SÒ?»ÖÃî’±q?²B²Ül(?´4õjÕ†?¹O^ùÓÅÌ?±§¯šn×?±þð†kb¦?µèxF¹?¶¬9ç’Í?½tG[ñæ?´ä5±ävÃ?¸ÿ^p²X?·zšý“KJ?¾±ÉŒ~ ?²Ùqr$?¹Þ€“@í?º±Ÿ¬'À?²]Ω†C{?·w“ÍE?³q0,=À›?¶·öªIf“?²üÏ4Q2ô?´ÎíKÖ?»7à¢NG?²Ð¨Ýy?°Oª}òûZ?±ÂN)E\¬?·F¹¾­vo?¼Þåhûz¿?±CcRe?¹Y=ÞNrÇ?¼ÁäG@Œ?¼bVÖɱ?º•_óé?¶šöf“šÃ?½Oãïh¤Ê?¸:=-T?¼µ‚ŠðÉ ?¸t™î&á?²TäÈæ?¹Õ}u—?û?³YP2+¦?½ ¤™a(?º?¾ÇQ;?²F/{6Vå?¹L2z_Y?¹'|³ˆÍ?½DP:?ºt>e—Š?ºöŸKÃS?¶w•ljÌÒ?³}oSÒ#?³dîãïå??ºü£ÔXI?À DyЭ?ºV]¹Ì_7?°¾ ¦#Õ·?½FÄ#¢G?¶ér¢´?±ó øRÜX?³IЂ=*_?½’%á?´HŸ…ï?ºoÞ[œ¼Ï?´SQþ—X?¼óc¹à7?µts[%rÀ?²Þ­ÖœŒ?º[œ¡M å?·çº¨ÔÑ?¸°¶>‹?¹ò|;ÿ†?·¥x8kX?¼Åbx‰’[?±^ Çýz?¶¡vÒ‡ 3?»¿áœ9w?»OO®?¾Ç‡BÐø?·?WÕP¥ó?´Љ{/?¶õT=/\?½¿ãî|¦„?½B¸(Ò6?±1Q<-?¾W¤iqÐÉ?»^]S?¶RÔœöÍ`?»¾ßð¿?²zÍr,øï?³o4³ù?²ÃN×fJ?¶‚˜Á '?°6ÉÒo] ?²Á®ËI‡J?²•-î†8H?²¬µü¡?¶]´ËƒÛ?¶ßÕ,ëù¡?´æÍÌw"?¾:£Ã޹j?»ýÏfD?½ò‚òÕT×?¾ª4‘¯?¹U:Åé×Ù?»ÞÀ3>‘?¹ {EQ¶?½ cu€z£?µàõ+Bqõ?´urK7YL?°ZÉù2ms?²@-qð·°?·V¡pÜÅ?¹†»3h|?·²v× ¨*?³nlü³ñÕ?¼e?.3%@?»U¼ö¯}i?·Ëuçê®Æ?µâr²’ñ.?½ËãиÜÒ?¸ù¡´?¸„Z'âIL?²\-ò…€?²]채v ?¹N›Ö"ŠW?¾Ú§êÖ(2?´0@ØÄ?¶nVº=¿?ÅÄ vUQˆ?Á¦¸tUÕ?Àþˆð??ÁŠh™ÝÞñ?ÃE Äl¥&?Åâ0é:s?Ç‘ÄG7Xé?ÃiëÌ3±?ÃÔÌb±pê?ÆÛBfŒ{?ÅC#€‘$?Ȕ܄z¶?ÁíHDtŠ?Ãv{“i?ÇËP :Ü?Ä.M%O[ö?ÄÍ^GpH?ÀØ÷ ü³?Âì;L1©?ü}BÌßX?ÄR~bæ?d?ÂÈ»iWÑK?À ¤9a?Åyàuãö×?Àwf»J86?Âúû[F?ÁÝ »_?ÃæaNöO?Å·Ñ7;?ÃbL0#Ä?ÄË#Âf?Ä:îCvhP?Ù|ÅÍT?ÄFîMš1Ñ?Âfªwm‡r?ÇŽDª6.[?À4L9„š?Áƒ8Ñjlv?Çü5Y­º“?Æ`’`´Ðâ?ÂÃÚÈsÑ?Ä }:î—m?ÂQùrÂvˆ?Â×:r[òj?ÅÙÐn*½L?Çjƒ˜u¥ü?à ŸÌ‘?Áâ˜`¸¡Ä?ÆfŸœ[?ÇÉÄ”1Ô?Ç¥´v3°²?ÇT“Ú„?Àßg‚,æ?Á©ÜÈ?ÆÍC,ïËS?ÆÉ“: úÛ?ÁÇ kæ3Í?Á{Èó†M?Ç_4T-~«?À½WR¨í?ÂÆûaÙm0?Æ12kW?ÀB&…Mé?Ä6ÎN™ß?Á­ùZõfS?Ãfl§ˆ;2?Åý¡µˆR*?Á(E«Ó ?†j÷ ‘,?Ç#ËÇ]÷?º1Âx?ÀlfèHÈ? ª5?Çëpµq(?ÇéõnyÄ×?Ä»/L3q?Á¯IVÕðÑ?ÇDt'­?Æ?Ò8öût?ÃÕ=“«\o?˜k+¹;j?ÄN}£?ÀÞ·‚¢íÒ?ÅckÉ6?À–ç y”Ê?Ã>¬bÇ'×?ÁWØ™‡âØ?Ç Ãš…Ìð?›‰õ^?Æ~R¯?ÂPzph  ?Â?*_“Û?Á~x§a—+?Ä=bx×Á?ćN r‚þ?ÃçL¦-Þ>?ÃHOí×Ç?ÅÝ ‡õ¾#?ÀÕfZ‘ÍÕ?ÂQÙXJý4?ººQ­?Åù`âÂ?Æl*—ð?ÁzÉÍD‚?ÁÜ©f¼o?Ç“˜Oýû?ÀFëÊeÖ?ÂéÚÈʯ?ÇÜe@'K¼?Á$ȸ:?ÃÛ¶¦˜§?Ã"¬mm?ÇÛÕ<÷EB?žK–Ø?À‰ç8KÛ?ê­;¿÷?Á×™Šéz?Æ|B£J­E?Šk ;)œ?ÂÛ¾}³?Â꫺ҥ¬?Çó´Î€/?Å6 /'?à ¡?Æ,Âg?ƪ9±?Çtô‹Ï1B?ÄÈïgÍ:£?ÃnLÈa(‰?ÅE WÄÎ+?ÁÀ‰’ú'þ?Ãú­×¨î}?Åoò/.ì?ÄonÁp϶?ÃF|çßF?Â5ÚkÐ0Í?Â3Zs¢U?Àªgmw…?À~·'EÒq?õý\# ?ÆrÖ34z?Áìà†£?ÂËö@Ú?Á§‰;úð¥?Åoù/­?Á³AøÏº?ÂYº®Ñt‹?ÅšPõº€?ÆÀ1“7P?Àæ×âÍsh?ÇtÁ‡|ø?Æ_‚yK<Ä?««8¢6o?ÃÄ- ôiP?Å3ÿá*ÒÍ?”j¿ì9y?Àà7nKS?Æ›Œð?Ä¥¯ “ø»?ÁÞŒá?Äçˆ_Mu?Åõa‘[:o?ÁQ˜Vá¾Â?î8îZ?ÁƒýP¾­?ÇbÓ²,r?İž8Ûp?Ä läljÄ?ÂÄJRŒ1Q?ÆÙ2—×óu?ÅÀ f*ô`?Ç•ä6Þû\?Ç·¬},“?Å:ð+æ‰J?Ã9ü=¾oI?Â9Õ}¥o?Àîç‰Î¦x?ÃDì^28|?ÃÀ?–7¶?Ã&¼ åHQ?Å.÷1™?ÆÏ#6Áàb?ÃQ\;à  ?ÀH®g$Û?ÁjÖÁÌ©?À†OrD?Á+Y•ü?Äðß±9(?Åg’òäà?Á_¸ÈcLH?Åt­vè?ÃÑm†1+3?Â8ê_ãÒ?Çã´Ç‘€?ÁÛ ¾ˆ™G?ÃÀ½q +?ÃÔt]t?ƒû ^¿?Ätîüõ§?Á*8qa2 ?Ãü.?Æ]bnOJ ?À†R ü'?À'örŒXü?Á¨ yO?Åq°²¸O7?Ádøä@µ|?ljTµó??ÁËÉ«Q6”?ÃÌ=‰EÐm?Ãñ-Íš›5?Á¨' JÖ?ÆÁ³7n(’?ÃElfWÂ÷?Æzœð¾ó?ÆÖC\†l]?Æ|¢­Ç¢?Â.šb ÞŒ?Á¾YydÌÖ?à Ëþí?ÃQ€eä_?ÆJ²>Ƨë?ÀúWý&ÉÓ?À±W {?Ū`ö=…}?ÂRSt|?ÇodeÀcA?ÃG *›õj?ÀüÑ‘_?ÀCv¥®?Âê‘Ñ+?Ä{¾¤£yº?Â.Ê=z?Æ¿b ÊF¬?Àÿæø.¸Š?İîM¤ý?ÃÝÌ‘‡¬t?ðÜ1Ñ_?ÆýRÏËÌ×?Æ_1›qK ?ÅÓ€š³+s?š,…`;?ÃÝÜþZ³?À¨vë¾ L?À×篲ØÓ?ÀF" ˜?Çv#Ù?ÁVHƒ¦!?ÀùwÛÿ=}?ÂC¬z?ÁPxˆ&©³?ƒÊÐVð?ÅëˆD.A?ÁV8¬E)?Ä> [²Ç?Ä4^BŽñ?ÁAèl4?Äo®¾uêË?ÇáQB§þ?Æ‘ÞNœ$?ÃÈMyâB;?ÁÑÙ5u²?Âö+Úî+²?Á­yf"B{?Ç5tw-?ÂGÊŽTˆh?ÃÂMY4ñ>?Á5~¶n?Åzº×yÛ?ÇÙICj ?ým\c??Ã^lóãs?Âì{ÐØo?Á]h¼€Õ¯?Æ9"%Ìß9?ÀF¶M_sz?ÄîÒv;?ÅÞAw¢c?Âmº½Cô¼?ÇÔå>g]°?Ç·d÷­ìi?Æ‘¾m7?ÄÍÎϾb?ÅÃaD|8ˆ?ÄE)êÀÿ?Åâ4°ò?ÁY7½¤GŸ?Öü î2`?ÆÆXRqm?Ä·þ:âì©?ŠF)?ÄÀ®K§ðô?èŒ'JF?Ç“A7>? Šód?Å+ÿÇuõL?ÀÐ'6Üñ?Åÿ!’°Ø?Å_@T’º¡?Æy"vã?ÆÄÂûÃZ-?ÀІâZÂÅ?ÅïôÖ“Ï?Ä#ŽcÄÏ?ƽcäçÏ?ÅOì“_S?Çu”O3€`?ÂÓësOÙ?ÆÈ!A¼P?Ãὒݾh?ÁLX}$Ø?Ã[ÇG-÷?ÁL¨0ª9$?ÁÏyrnýÚ?ǺäÝ”ÅÏ?Ãìý¡t?Á,ˆ(Ö8’?Å‘Zïc?Á&·¬UÊ÷?ÄÕ¾Ñ1(Ç?ǽD`;¦S?ÅCVZÖÝ?ÁfglòC3?Äœýôµ5Ó?Åà0~üÓ]?ÆÝƒ}O?Àðþý?Ä#­ùì?Åu¯×ŠMŸ?Á­ÈfáÒ?Ä™­¯‘¼?Âi‘Hä›?Äl~TêjQ?Ã& —Áw?Æ_!Îlw?Ç ìãWß?Ƭ’¿G%?Å*¯ ‚Ü«?ÅÅ"Š"Ò?È„¶“Øï?ÀĪm?°?À5Tò›î*?ü|&ØMl?Ä¿n=d¶i?Äüæ8& ?Ç$Dt?Ä-?äæ?Àåû½ðê?Ã0;jmÒi?Á‚8HÝì?Á–XEè°?Æ­'Ô¡÷?ÅnˆÍå?ÂêÊ]wK?Æß2Síû?Ç¿þ݈?Àí¦.l>?Æzál«yû?ÃÂÜŠUT?İ5œ?Ã\k[ʸ?ÇÜtŒîŸ"?Áòx¹ 3™?Æ‘ ¬"?Æß³Ýp4?ÃÌ ndã–?Àu¥’Såh?Ã1[\×?Çà¤ZMß?ÃÍ ~@¶?Á¦1­”¶?ÄLüËNËÃ?Äò® e9R?Ç©“Â~à?Ç(‚ÞKÌK?Çr¥“ƒ?ÆÐÖýE?Âþʺš9Ó?Â~9Ìh =?ÅPOv¨ÿ#?Äž¾Ÿa?Á–åvÙ?Ƃʙ^?À™uŒ ‡?ÀÞæ#W N?ÅFnáï¨?Çÿdl9T?ÄûCY?Ç¢\k  ?Â)x?ƒ%T?Á@¶‹xȦ?ÄðÎU8?ÀzÕl–u?ÅÓ`OÇ…à?Ç­ä,õŒª?Á°è#úðü?ÂM \À…n?Å\ÿ=Iž?ÂØ!ê?ÀsBÚDï?Ç@ÖÂ?Å÷ð,;[ ?Ã*z>{mÑ?ÀáåJ°ª?ÅbNÄáú/?ÅÎϽ@&A?ƺñây@3?ÄùÞI=Ï?Å:1ä•?Âñ*€ÍLö?Æéâ5?Æ“økŒ?Æ{Ù³?ÂŒ9È‚Ã?Á…·´\‚?Â׊ OªÊ?ÂÖi÷ús?Æ®Á²o]*?ÃYZ˜šÃX?Çåô·Gr?ÆD€ nçh?ÄÙm¼þ¹a?¢YiÞ­…?Äß.¤P¼?‡yå*÷?Û;ïsƒ[?Á͸]YVú?À¡º?Æ¢â"û×?ÁyW¤´›?ÇÒÄi­Ï?Çl£wý†?Áq'>?ÃcÛÑæ¢?ÇdcÞŸÑ?÷»fÙ–ö?ÅKþ¿@I‘?Äõž NÞ¨?Ã^ZÝä;?ÂlY%é8û?Dzڢ÷?ÀÀ–*tQ?îœ?)?ÆÑÒˆÔ»á?Áµø_™êf?Âæzª%:?ÁúxžÖ6‚?ÆcÁ—¾m?ÃÛ AÐ}=?Ä<\ÞÝE?Á?¶·–E?ÀÓå½Ü»??ÀJ¤cÈ7Ó?À;4zDC?Dz3ÿü?ÄxÝveÎ~?ÆøâÁÜ5 ?Ãü|Ð{`?Æ¡F™Ÿh?Ãg›Ì¹4?Ç´¿O™?À¥þ­gç?ÃΧ?̻ΈÃ ?ÍåÓu†Å?ÌûNôέ1?Ìú~ÜháÎ?Ì3-Ja÷z?ÍǰtŒ’R?ËÄüYD·?ÌÙž„üÝÂ?Ê Ê«1c?Ì¿^QožÜ?Íú°âIE1?̪.> )†?Ë{¨tœ?Ìž*Ÿ([?ÌýIJj?ÏHãŽâ¡Ú?Ì‚*YŸ\?É–xŒ›¤M?Î 3mô+?ȹ6×Û$“?Ï£4@äŸ?Ì ®[‚€?Ìã~èrÚR?˸, è„—?ÍÇતS ?ÉI茷/?ÌAí½`T?κ2…§Ã?Ïå„Ä‹çØ?ÉY(•Z·?É_ø-Xg-?Ì•~h41ø?Íæ`ë”g"?Î1/ÄŒž?Íb¯ñÜYr?ÉW©û¸?ËåmI¨?ÍéPó]4?λ¢ˆ6Žj?ÍËp¸DB?Î…b wf·?É–H‘}Є?ÈÆFïØôr?ÏÓF«Ó¶?Ë£¼óf?Ímþ¯k4?ÌáNø?в;÷—?˰ì®ÌeL?Í¢°\@,æ?ÍÍÀ²€:?Ì‹Îvt?È×™ëâï?ÌØ~œ‚ë?Ï#â;a?Èî–Ÿºk-?ÈÕi›ò²?̲îFONú?Ë÷LÝ÷ªõ?Î#aBüÄÙ?ÊD9>š?ÌŽIMu ?ËËк–·?ÍùÑ<Á?ʸJ¼y£­?Éù¹CÿB!?϶$eþ]?Ê=éËáGe?ÈöÇB€¡ì?Ì°Ž’al?ɧ¢¶ö1?ÎF!¥Ê„g?ÍŠP?ÌV#?É͉©ð ?ˬ,ž*Y?Ï-ÓcŸ?Ã?Êñk8ú´w?È'UÑ´æ?Éò‰Kd,*?ÊûKM<å†?ʪz¸Ùå+?ÌGÒñcD?È-ï€@¬?ÊòKDL”Û?ÈZfC­cT?Î*¡rSNø?Ì'=•³‚à?ÉQøŽ(n?ÈP0®|À?Ï #BÖ4?ͦPtã'F?οR ‰Ú?Ê£Ú®>?É~¨y`«€?Î"3óMj?Ï›t5¸¼?È25ùSyÞ?ÊYׯ×?Î.FÈ ?Í…6i^Š?ʳªÉn7*?ʾªÚ÷­Ê?ÍÑ0Æ“l0?ÍI¯ÀzÆ?Í/’Ï[ú?ÏiSÔ ò?Í(„²(˜?Íz€"ƒIu?Î a6¡—#?ͤ€r¥›í?ÈR60¼Â%?Í÷ þ9Y4?ÏötÞ™šg?Î,aõ,?È4Êbêø?Ì >xtéÚ?Êð[õŸk?ÉPÇ×óP?ÉÇxñžä?ÏpÓÞ7ż?Ê­*¦eàN?ÏŒ¤¦?Ì,ë’?ÈMÅu„£?Ë“ÌôÂF?Ë’ìÕ?ÉÉ(eË?ʹšZÉËÁ?Ë™¼)µ½??ÉÇ ûþœ?ÊÀº¯äWg?϶ägÄe¢?È:eòÇé³?Ï1e$†‰?Ëel °›?Èõ±-e{?ÎÉòž’Ë?ÎÉñë?Ï×d¥eøè?ÉsèHÆñÜ?ÈdÖ9Amû?Í0ŒÓ¶?Î]AÎ24?ÎÑG«³{?λ2ƒaÝå?Ë‹êš=?ËÕ<ñ$ÑÖ?Ì WN×(?Ê÷‹Iì©ñ?ɇ¬8\?ÉbH;’ã ?ÐÒ‚º ?̸Þ_‘Ç?ÌxÝÓ}Yn?È膪±]?Ë_›òúÒ?ÊÄÚn¦H?ÈÕ7¶ ô?Ì€¾ ²ø?Ïxƒî=7?ʵúº¬é$?ËšŒ€ÃdÊ?Ë?+ÂkV¼?ÏPs #?Ì´ŽœïŽð?Ê9˜^8à?ÍïÀú¨2Î?Ì -T­ßù?Íãß?y7?ÎoÑì®þ¶?Í£jƒ›B?Ï“D"± D?Ë Ì9¼”?ÎG!£ÚÉ\?ÊïK9M t?ʰú¿íª?ÎÑ‚¬âËç?ÎB™r½¶?ÌÕŽÞ¬N?Ï–4&-†?Σ"6eûó?Éçh³õ&1?ÌIÝv5œB?ÈØ†‰œ7ì?Î Àþ·SQ?ͳ Y:‡œ?Î(!E)7?θâpÒÆ«?Ð:†˜?Êj &ïAq?ÎÈ¢œWu³?Í`ìï´¦?ÊÚelWü?ÊbšáU’?Ì!†ò-˜?Ë>[ÆZÈ@?É)— ¶ä8?ȼÖÍtì?ТˆîêQ?ÉlMl*ã?Ê¿:Þe»t?Í ßKðKj?γvš’?Ðr~ÔÍ?ÊõÛJþwû?Êã &јB?Ê®º»†×¤?ÏÃ/ô8‡?Êú€Ž»Q?Ï_ã¯-h´?Ï@ósH #?Îøòáß„K?ÏiÓÅ`«¹?Ï-cK©?ÎW±¢ˆÅö?ÎÌrŽ Õ ?ÈëfÙØŠi?Ê–Š!²Ô¬?Éx((·>º?Ì]ÝöVãm?ÊdJ%¿±æ?ËÑLä‡h±?Ê1)³,÷?Î!‘R~sê?Ê4iÁv\~?Ì,í~¦?Êø‹'öð&?Í{@|º?͇Ð4yKE?ÊË:|fí?É7wGÂç†?ÉzÄä$Æ?Ë9‹Kr«±?Í à$‡(B?Éþ‹ñ?ÊS¹\Ð?Í;méÆ?É%'Q¤å+?ÌÎN *+5?Ï<v”B?Ékw¢>Žw?ÉŒÇå³?Ͷ€Pi¯?ËáÜŽã?Èæ‰µ5Ü?ËÒ\|1\Q?Î7á\—å]?ÉI§€#!?É«8'Ÿê?ÏdÓ¿ óq?Î|Ñ÷Âä?Èyó}E?Íwÿê{„ ?ÍRÿœØ(¹?Îèâáµ'?Ênù°™¯Ö?ɱˆ'*Çl?ËÛ<…W?Èäeµ«ó?ÎübÞ¾§?Ê=9#Nùð?Ë›ëýž÷T?Èø–DH6?ÎrñÍxƒ?ȳæ'?ÉÏxt hm?ÊkYÂâϰ?Ïÿ$ô,ÎM?É-wM¸©R?ÉÔè’¬Ò?Ë‘ÛÿTð?Ìî^½›™6?ÉôH—]{?ÌCÝHm}(?Î#1â³z?Í!@ÙI?ÏD#n¹ßx?ÍÌÐn´æ?ÊÅ:: —É?ÌêÞ«¡ÎÅ?Èqe†]ë?Ì%]'ßÂA?Ë Eæ³?Ș†—9?Ϩ$D…ã?Ë@»b“>?Ép÷©ÿ€ ?ÍêÀºŸ^S?Ɇw¾ é?ɶ¸Kû™?ɰàïûŒ?ϵäVÎEÂ?ÍÔ€pí?Ê%Ê&ä_?Ê)ð-Æ>?Ëðü¥_Ci?Η2~ݨ?Í)3´¥¼?È'%"ØÕ?Èoõµ¼1¨?οÂol­8?Ïì„Ï‹åA?σ0<°?Ή¡úÓ͹?ͶðG°¬?ËÃü5»ºÐ?Í㌣\:?ÉêCÒbN?É6_ %o?ËÐÌ@2Rç?É †‘Ì,]?ÊÐúLòÁ?ÉQYU4´?ËK e‘ ¤?Íå±u\„?Ïüæóàp?ÌôJQ»?ÎA =ñ?ͯ±DH?È|…±:ÀE?ÎЂ†»'Z?ÉùˆÖE?È0T¸×§ä?ɺ‡àc.Î?Î%1Ü+è?Êh O2 Ç?ɯ×îuç?ÉâX\ê¸?ÌGýdœX(?Í¥0uö²?Ê1ùBYÂq?Êÿàãdý?ÉÑ„A¡=?Ìø×?& ?Ê›ê’?Ï2í/…?ÍïŽ"}?Î…‘æ„Ìè?Î(!™l?ÈïöJ`¿?ÉÅ€-±?ÊîÚkÎßó?˵\·Èß?ˈ›Ò‰':?Í °"l­¶?Ìþ^áÚù?ÉŒGõÁg?ɇ÷÷õq?Ì&]=é¹Â?ÍŸð.KíN?Êèj¬¾š\?Ïá²ø ñ?ÍÖ Žª~ƒ?Êep‹"3?Êw™ŠÏðƒ?ÎáèFë?Ì­¾Ûf?ÈÕV&á™?ÉFÈw P?Í3_AiÜB?ÊûúÔrØû?ÌÊN{L ?ÌM˜ÕO?Èñó! ¬?ÍÏà•LßÙ?Ìý*;°?ʘ÷¿¯?ÌqɤŽ?ÈCÅY³|â?ÍÁ0]¾c?ÉæukÅw?ͧà'R“b?Èåd¦M?Î÷‚Öž#‹?Ë- HÚ{?È^5{'c·?Ó;`øWÝW?ÒæŸÕ;‰?Ñ7$ßÝ´;?Òp‡=Ü‚·?Ò¨¯¬Ÿo÷?Ð)"ÊøŒ¬?ÐædB8¨!?ÑJE S{9?ÑNåBJ?Ðwshßx¬?Ð,йo¥?Ñ[­:Â2Ú?ÓVaH9—J?ÐN»(u˜?ÓÔòA/‰Î?ÑÈV å,M?ÑÔÞ«mÛ?ÓqGŠ ?ÑEZã+i?Ð%*Á¨sj?ÐõTRöü÷?Ó–yuã }?Ó…™OëBG?ÒÛ·ü«v?ÓY¿¡?Ò·§¸Yqú?Òç þO’?Ð# ½£€?ÐÁK·?Ñ=œÝÂ0T?ÒΗì?º?ѯ%·‹Yî?ÒTÞú_FY?Ó7ð®ÖÂ5?ÓVÀêv…?Ó5Яí¸Þ?ÐKÓ ™-¤?ѸíÊ~k ?Ó-—…íò?Óéâ‹~?Ðw a’ í?ÓwÁ3áã?ѲM¼Œð?Ñ D€³S[?ÒOw5?ÑØv …êF?Ñ9Õ¤eÙ?ѳULjoZ?ÓƒÙK§¼—?ÑzETW*¸?Ó°KQ¶È?Ñç¶(«ž?Јí=w?Ók¡ a;?Й{¢£·‰?Ñ7„Õåð?Тû³x²?Ò$6 ýˆ?Ð)ÂÊïï?Òߨ)Õ9P?Ñ>4ñnVÅ?ÓeIL¬«U?Óx!wJQà?Óª±è¼,½?ÐhJ`†Ù?ÒŽÄ®"¬?Ó@² ?Ó¦!Ï‹ú±?ÑcÝ1¹,!?Ñ•[ÍJ„?ÑuLÿxì?ÓÍÜÔ¯?Ò&¾¸‡'?Ð?¢õ~ßõ?Ów¹4 y?ÑËu÷ZëÓ?Òcï!ýK?Ñ` ó±?ÒN,?Ñðö4_WÉ?ÒÆ/Ï4.?ÑeE'W–U?Òò˜$%ÏŽ?Ñ‚]_CVf?Ð÷ä^?ÑT•×YK?Ð3‰ky?ÒÎjø?Ðê|C˪?Ó™Ù«ág¦?ѰÍÚÝË3?Ðï”Z`ù?Ð’cžpšÆ?Ñ Ì¾ ºÙ?Ñ9äðc´E?Ó‰Á‘Õ6”?Ò7^Ý¢«?ÒÅîU¼õ?ÐþŒgfx@?ÒµW¯Üç?ЪÂô?Ò[<¾8?Óx±êƒ?ÒQæôžÁ+?ÑQUèí?Óüâ6ÛK ?Ñ)$µD¢±?Ó(0›Ä?Òn$¾Ü?Ó´¹˜Šbô?ÒOè Iª?Ò?†Ê¶mð?Ð%rÁÊt°?Ñ%”¬¯¥‘?Ð ²‘_ Ô?ÒAÎÏSÜ?Ñqu?LÎ:?ÐÑü Ÿ,)?Ñ쓬±?Ò@žûFÞu?ÐÞ,6 } ?ÓÇ-;×?ÐÄ„;ÏA?ÐnÃW1ç|?Ñ8,é{n?Òþ i=jÍ?ÓÒ™Çä?Ñ×. Mú]?Ñ($²´[\?ЫƒÄ[r¤?ÓÂ)¸Q÷=?Ð/"Ô`œ›?Ѐ+s)»?ÒKNæ¦Ú"?Ð%ÀÊàL?Òâp ت?Ò%6˜W;?ÒïHE>£?Ñ׆Õ©?ÓcüWû?Ð5‚ß ´?Ð rŽƒŒ®?ÐôM‹ï§?ÒçC†é˜?Ñm6_r(?ÓÈoK$?Òsè‡?Ñ^¥+“…O?ÒGŸ ¤J×?Р£» ©Ô?Әɸš ?Ðþly`~?Ó›y»>’?Ðÿ4xV_Ã?Ñ-y‹ °?ѦEÀL+Þ?ÐaC8ŸÏ?ÑàN[]Ï?Ñ{eVîÃ)?Ð ²Õ Ï?ÐÝ´)1o§?ÓóŠ/ ýÌ?ё݆‡a©?мãçÎÊ?ÓZ(ñBáÂ?а[Ð[v%?ѽ½Ñ±pœ?ѸÄ£ª?Ð)êÊÞl2?ÑuýEéâ’?ÒÂןŸk?Ð;¢íh&p?Ð¥c´Îå–?Ò˜ÏsZ?~?ÐQ»£0?Ò¶jæƒL?ÓP«³ƒå?Óf!J÷[Ë?ÑýŸ«Ùo?Ñ‚í† Õ?Ò‹7˜–?ÑR]"‘±u?ÑW%*pJ!?Ð)òδ ?ÐÓ—"ÖO?Ó¿ÑÔ©8î?Ò’'|)šH?Ó¾ÑÈÎ]¡?Ó›‘~|ïa?ÑñÞBÙA¯?ÑŠ-rIŃ?ÒŒ/g óî?Ótq)œ?ÓèÒ!œÈ?ÓœyÃm,?ÐCÊýê›×?Г’Ø52?Ó™‰»¼½4?ÑÓî-A­‚?ÓÔR7Æ^—?Ð(âÌ}\:?Ó0¨êë³T?Ò/¶ÜûX?ÓX ,6?Ó‘)¥ l>?ÓWQ ´4?ÐÓì+zc?Ó?Ñ&¢?ÑÉfÌ’?ÒvÃyø°?Ñ&Ï­Ïe?ѦuÓ÷æ?Ó®éôwÞ%?ÓßJPWã•?ÐëdUê™?Ò¯ ³À8?Ñ>LøÎ!Y?ÒíÈP¨C­?ÓT‰8|?ÑÈfòW]?ÓÔ*žÞ?ÑueòN›?ÓŒq›=†~?Ò¦ÇÓ9Ô?Ð]Û5„1©?Ò2æ÷Då*?ÑXe;©aØ?ÓeÉpd¿?СÓİîN?Óïz|ˆô?ÓèÏÖ»?Ó®éíñÞ/?Ð j”•a?Ò¿ÏþЧï?ÓDéëK?ÒñÐbSvÒ?Ñ)TÏåÎ8?ÒŸ¾wX?ÑÄ‚}¾ì?Ñ)ÜØO2Ó?ÒÓˆCÓT?ÑT‡g%ñ?Òè+I?Ò±À”Å/?Ò_Uˆ”z?Ñä­o:z?ÒXW@gpœ?ÑÅ6¸ì?Ѽ5ùgt?Ð-2Óÿ¬T?ÐØ”, Õ?ÒPŸæIÉ?ÒÀ€œi½?Òö¿šÍ ?Ò˜/ÀÊô?Ò®ú†<&?Ò¼àï£Ç?Ó­Ûù?Ñ& Ù¯ ,?ÑóN‚lb¦?Ósé ÇÃ?Ò.Fð/ÿ?Óƒq¬|Q?ÒgZvG?И;­+¯?Óç2YQœ¤?Ò°×âeR?Ð̬Ù0Á?Òð°eñæ?Ñ\M;V—P?Ò±oôõø”?ÓÏŽhv?ÐBk±;ô?к ù›¥'?Ò#öë¶ìW?Ó1a _Ñ?Ñ!Ð}ú7?ÐÎ< µ&?Бû¡vš¶?Ðf»Hó™d?ÓÄúã:=?ÑìÎZ«` ?Ñ”í¥ö4>?Ð+êÑÚH?ÑìŒv'?Ó²Q÷‡If?Ðñ ¸Šó?ÐãœC¡@s?Ò€æf¼?Ó¸Ùܸ?вSç*N?ÐÃÌ ìK?Ó@¯ašÓ?ÐÔ”/¸_»?ÓÜ|^†½?ÑC_‰?Ò>Ÿ6>N?Ðÿ|?_‰?ÒÌÐ5JC?Ð]Ã4éЯ?Ó[Ñ5ñíY?Ô²ƒ|å?ÓçºR„Žž?Ó¢ñÈ=Ó`?ÓÕâ; 0€?Ðö”mÏÕ?Ñ[½>¥ÔP?ÒéØsºQä?Ð'*ȸâ?Ñ݆NÅkÊ?Ñ{͆ —?Ò>ÏëHß?Ð^C9á™?ÑóVkŒdJ?Ñœí¯¢û?ÓÍr>zË?Ó †½?ÒÙø*{œ?КK¬Ïsl?ÐÚ›¥w?Ò¢7ÂAïÔ?Ó.íÙ ñ?Ðz·*Þ?ÑvÝs¯G?Óu¹Š…/2?Ó*˜ôŽZN?ÑØ~AáÎâ?Ó‰!¬0ý™?Òd‡Q?Õú¶bKe-?×§ñÆÒ½v?Ô‘[zëý¾?Ö_oçÙû?ÔY>w†?Ø`§âû?Ö<Þ¾+??ÔäL:ÄåL?×pÑ„ÚQ?Õ”ÊJ Ï?Ö¹!·Ê¾?×!ÛŸxi?ÖX¯[nä™?×B!=K™?ÕùÖž‡È’?Öá Snnb?מ ³lû?׿QÂsBª?ԮË’7¶?Ô]úã®ú?ÔÒmèa™?Õ½’S­?Ô¡;g|¯!?Ôâids?Õ<`ÖSS?׿)àªp?Ô zDßJ^?×ûª˜²í?×¢¹C0Ã?Õ¾½Œ†®ù?×”!-tü‰?Ôiãv÷?Õ£­ZH|$?ÔŠ›8‰šë?ÔxÓŸ?Õ2–4JI?Õ™MAÕœ?ÕYLÉVk?Ö«y¤½n?ÖÆSTS-?ÖÿØ@r¿…?×X!¦L^?Ô[c2vŸï?Õ©Ýú÷?Ôa‹VÌÚË?׊aÂ6j ?Ö©û¡8S?Ö–ÍcH?Õ¦_-p?ÕdŒ3û?Õ¤n'þ+?Ö¬ÿmƒ«š?Ö¬G^qk?ÖÞׯT ?×2YWþþ?Õf¼ã°RN?Ö›§2¿òæ?Ö—–[ê?ÖïPýó?×mPܨ˜F?ÕŒ(%Ûó?Õ—A¦“?Ô9Ò–²ôm?Ö%¦IÕÈ?Õ $$j·¶?ÖågÅ—Âá?ÔÆ«$M?Ö ®¿¥?×J¨“ÁÍe?Öüÿð·?Õ×´—Ö“?ÔA ©N²_?ÔNH‚?ÔZ2Ó³È?Ö€þö—Ê?Ö7^pÚâ?Ô ÂCQ¯y?Ör–ä YŽ?Øñð¾±?Õä½Ë¸©‚?Õ¹m{… §?Ô4B‰Ép®?Öf?oRÔ?ÕÅu‹ôŠ?ÕÌ2$O¬?×@€„Ý_i?Õ(,'Ó?ÔÐû¾µt(?Ô$br v[?×ð p„?Ö&iDm¥?ÖÉ—í:±ª?×/€ÓÓ‰?ÕŒºÜÐ?Õ‹Í£”g?־ù?׈a¦>Kî?Ômsdés¶?ÕÑv)V?Ô—S¤YÕý?Ô:ŸÀ?ÖØçÙ‚î?ÖdNçÇ ?Ö”>ÔR"?Ô²qV3ù?Ôc:¥°?×ùaô!°Z?Õáããe~?Öz7 Ép?Öê_÷µ}¨?Ö—GZ^d?×y¨æ' ©?Ö:Þw’£?Õ(tb\Ò b?ÔwÓƒ$í2?ׂá¯rÚë?ÔoÃh©K˜?ÖŽ¯¥Žõr?ÖåH;ÇÚ?ÕLÜîÀJ?ÖfW-0?ÕíÞg¨??×횎‘ ñ?׿ƒ2Ù•?Õ™õØ š?ÖLgHo¾¬?Ö®ÖŒŽ?ÔÕ¤NÌ?Õœ¸ø/?×C!…|?×J¡•,è?Ôd‹A`Çó?Ô&R´ü6!?Õàî,*'?×®Ç~¶l?×ÑÊ-¯6?×¢¡É1—?ÔÛœYðõ?× ðÖ×íY?ÔjÙÙÿ¯?Öµà1—'t?Õµ¸wEû?׳b*™‡?ÖqG“9ª6?Õum‡jÈ?×áÉJ²{?צñÝ_ò?Ô%R̬æ?×éG?Ô¦³È˜®m?Õ<Ü÷j¾?Õºž 47?ÖÔLê” ?Õä>{ü*+?Ö8_5¸OÆ?Ö±°AæÙ?Ôþ,È(º?ÔA³C[œ?ÖŽïî_Pg?Õ³>&áÆ+?Öºø0ÿüì?Öp÷–°©?ÕòV~Ònô?Ö¸ØçÊ?Ô0Êâw”?Ô¾\!o?Õ@啸”?ÖÓpKRÞQ?ÕÞædÙB$?Õ¢&ßžâ?ÖûhÎÄs?×0lï0ô?Ôb;‘…Ê3?× BO¢ä?×árÌC ‰?ÖúHÜÖ¸T?×1!51í?×Èß]ò¹?ÖÓhCü~Å?Õ½>2ñ*?ÔkS\¡B?Ô”Ã²Ìø?ÕœÉFÂl?ÕáÖ^å¨?ÕâÎr€Ðë?Öm¿¥7ão?Ö7—[ÄóÑ?Ôë„°ešl?ÕwMáƒÝú?ÖG 6üm?ÔUãqgô?ÕüùÄu!?ÔêDŒÎ?ף IS?Ô†“¦+ÌÓ?ÕÞ®MZÙH?×GÉ JmG?Õ4ÜòWÁ#?×à`ʇû?×Y‰_žD?تÏùÍm?ÖÝhˆóã?Ô¤lš'|?Õ‡mù/4?Õø®òIB·?ÕÀ¦{¾-6?Ô[ê+í`?׸}š1°?Ö?ŸC$†É?×ÀB;àªw?×^yUDYž?ÕWU>òîP?ÕD]sÎB?ÔÇŒ_ÿæ?ÖÝPLXå'?×Ü*g9§?Õó.”ê²?ÔE“.Π?×O“æ"½?×táéÊÆh?ÕG•kÚ+Ç?×tYöOÏ?ÕžÖzO§?Ö&ýXHD?ÖO¿`»©È?×°ÄÞ—]?ÖC—‹Èq?ÖoW^e…?ÔÅlefÌ?Õ´¤‹Åê?ÖN±]²:?Ö¹Wÿ'ã2?Ö]'SyÅü?Õ8¥rYq?ÔBÝòŸ'?Ôrû º #?Ôë\§/«˜?Ô¨4ÿŠ?Õ—fXG?Õ7Ý Ü¡8?Õ… rh?×°z9OT?×4pç:°ª?ÕüÞb¯Î?Ö•?ŸZ¿?ÕþFcsëz?Ô½#èDð.?ÔnÃXŽÉ”?Õ{]~{ÁV?Ø ±(<;?ÖDW5‚# ?ÔòÂ`}?ÕDÑËŒ†?Ö@/Má?×ú Ä`Å|?Ö­ð´fÀ?Õù¦Žña#?ØÅ]ºy?ÛÅše€=?Ùä-|w?Úd ÃÞ?Ø "c5u{?ÙÅÕë~‘f?Ù(¤Ïf3?Ù‹¥˜œ¹Ð?Ù?&VcF?Ú×0^™P?Ú#ïA?Ù Œï}1¥?Úþ0ÙÓ;?Ú?ÇG‰º?Úæø•ïô“?Ø.úõð̽?Øé¼W> Á?Ù¡…y–kß?Û—l?q?ÛH0ŸºÏ?ÚØ7œ4oo?ØRb»=iÆ?Û?¦ Î?ÛQ¹$2Ó?Ú-Vçv'w?Ù:]ªç¤?Ù[ n‚MÎ?ÛÖš{f?Ú³NNè?ØW£` %X?Ûê3¯TÈ?ÛVAFx¥?غ ǽ¨?ÛØHû‰Ï?Ú–ú„?Ø=Újîz?Û§a7æÊ?Ø<ÚZª4?Ù»BìøT?Ú—_ Ë þ?Û\“æ ê?Û©i1«œº?Ù«}3Éñ?ØŽ‹%ƒ˜?ÙI¼iQÿ?Ù&¼:·ÛÉ?ÛŸø6÷?Ùì=¸³ê?ÙOT| Àñ?ÛRlâ3l?Úï?²U+Ö?اs*RÝ?ÛÔ™Œz`Þ?Ú•Nó" ?ÚÒyù­ù?Ú¬g=NÌ ?ÛÀGçÖp?ÛµÙÍÍK?ÚÚ`¢F?ÚùÈŽ#Í¿?Ø1*õõu?Û³ý†m?Ù|Çq‹?Ú3Çw÷-?ÚÁI1Ó?Ûi™?€Äó?Û¾¹Æý…ž?ØÍ#«æ??ÛZ¸§êà?Ú!¶á1D?Úg–ɺÖ?Úæ—¾lI?ÛÚÁOì?ÚÇ7jçòH?ÚúçÚ {í?Ü‘â__c?Ùò5·l ?ØLŠÎ2n?Úto`”¦?Úé0^ð„K?ÚŒ_©œpc?Ú¹ËÒš?Ú^_W€ôg?Úsÿ{H±ª?Ù‘­°Ðâ?Ûå"4ªãž?صör;º?اãn´Þ'?Ùíݳ£Ùé?Ø."Añ¾|?Ø×Ë•Os•?ÛÂénä7?Û8Ñ¢?Ùì׎%6?Ù¤­(%ú‹Ëg_?Úrwùp?Ú»v9å?ژܙv?Ú>ß/Dç?Ú˜oì@ÕJ?ÚÁxIñï?Ù ä™¶Û ?ÛVQ6ÚÁ—?ØØü+ï¾?Ú‡¿+¸?Û2 Àf?Û ˆÕ g?ÛôBÛ;Z&?ÛŽJLË G?Øo3ä¯v?Úé9-_Ë?Ù>+B¡ƒ?ÚZïÍl?ÛBÑ¢µLå?Úï° "š?ÚY·oЉ-?Ù¥]÷9Û?ÚûМ߮Â?ÚH—%Æf[?Ù?¥ ªÞ‡?ÛÉ.¿FÖ?Øç„qëÁ1?Ø8³<׉1?ÚÖæT¨Ó?ÛB_U¡t?Ø 4__9™?Ù.}¦»èë?ÚŸÒ" ?ÛÓ«­‚Þ?Ù­2Éë¢?Ú2_r’Aü?ØOcoƒçî?Ùûæ«6a^?Ú Öº +™?ÚÉX-”d?Ùøf‹; ·?ØN 3 _?Û[‰s½iì?Ûn Š Ê?ÙíÐð ?ÛZ¢\¡?Ú ÷©ì}6?Ø¿ÜÉš¸ò?Øs /üÄ?ÚÀ¸ëC`?ØìTóKœm?Ùˆ.¨°?ØWkqÎ"¡?Ú É*ð9?Úè°w´dX?ÛµJØþÄ?Ûüššû$ ?ØÍ|:asÎ?Û|1®±:?Úk¯¥EÓ?ÚõÙ "u?Û6yµ˜A?ÚRI€d?ÙMèµµ1?ÚÐéB°¿@?Ú¯»VÑ?Û`70s?ÛÁ2^cè?Û­ª/ò*å?Û‹Ö|PU?Ø®ëø{éR?ÙåÄ¿?Ú,âGx?Øi3¢% F?Ú†Oäã€L?Ú»Ð+˜mÙ?ÞÞ¸g”Ÿš?Üvs]8¡{?ß‹†Yw´?ßg3ŒH”?ݶ]¾fÃ?Þ×° Ä?ÞÂH+—?ÝU•7ßiV?ß0ÐR?Þøàŧæ ?Þ0ÃÀi?Þfo܉[ ?Üdõ¼tY?ÞûÝb}?݉]àÁ~ ?Ý4çe%¸?ޜ籨2N?ßȑا…¿?Ü-’^=t?ÞìèçÃ,?Ý .„˜±?Þîÿüúfê?ß y—k£?Þ‘G›\fÕ?ÝU@„:s?ß_ic‹\û?Üÿô¹¢?Ý}ÅÄËÚ›?ßá ÆÇ¬?Ü4s²éƒ?Þù8®V¸­?ßšÉÜì)0?ßÖÚØ”f?Þž‡mÇÿˆ?Ý:ìÅ¥d?ÝÙ­ª+?ÞÖ®0?ßf8ù/©Ó?Þjˆ ?Þ—ÿÀQU°?ßò’x]…?ßët?Ý­¦ ‡ŒÉ?ßRáNó±ç?ÝŠíÂ{}Å?ÞÎð;ާ”?ÞÕÈ1L¸?Ü|k,bµ1?Ý÷eýô´;?Ü㣴 ïi?ÜéÄQ5wÿ?ßœi¿}JÎ?Þ?cêI?ÝOÝBŠ6?ÝpUˆ?ßJ:•ÌT?ÞQ¿4r®?ßuj™!?Ý[•w…~?ÝÈ5©'¦?ÝFdv"c&?ÞwOdhù?Þ|ï¼îT?Þë¸y=Ùé?ÞD7,›þ??߈µ‡$`?Ý<Â×}?Þ–¿³%5á?ÞÏ@ ¿d§?Ü 3§ik?Ý{õ¹:?ÞïßßïÞ?Ü z–Ñ!_?Þj/s»?ßV©W0ç?ÞpŽL½?ßñ™?‚±?Þ^iÖâ:?ß(¤Ü÷÷?ß^IF^¬?ÜT3è¬*?Üs»+Cþê?ßZ°ö·?ßIÁÿö?ßp1“GŒã?Ýá~]î?Ü@kFk?ß¿¢`3”?Þø8­Q9Ô?ßÂ.¥YC?ÝCÝ#VÈþ?Üß„LÓªé?Þ†‡Ïz?ßo7CT’?Þ‰Âψ?ÜPûi€1C?ß›òM;Å?ß%ÙTd;×?ÝF …ýû?ß…ûeP©?ÜX«k?ÞÇP\¯“O?߈ÂÎ7¤?ÜS`Á%?ÞöŒ™5 ?Ý›E­åŸÔ?ߤqºÓr[?Þç \ jƒ?ßd±h|Õ?Ýx]Â>4l?ÜŸ<]à?ß êX?ÞØÁ08?Üsl Ë?Þ”È?ln&?Ýþ/"²|Ð?ÞzN±?ÜÕÜ€t|L?Üñ”½G<Ì?ܧCþ¤%t?Þ_¿c\á ?ßÞZS@XÓ?ÝÔ¿ÆW,?ÜJÑ*P?ÜSÓaƒ÷?Üü\Èå ã?Þ72ñ€K?Ý9‚ W?ÝD-ëì8?Þ'È¢¨q?ßúC€eÍ?Þ3éÿ€m?Þ‰NvgR?ß‚Ú m«“?ßž*K±àä?ß1Q‡Ù;?Þ²ˆƒ ?ÞU—D¹Š?Ý(læ0Á_?ÝHÍ7 D?Þ3ß,`NT?Þ¾¨PÎuö?ÞF_×€w?ÜÒtÛ«¦Ö?ß7RScs??Þ¢©}yn“?Þ‚Ðð* ?ÞIP5èèÀ?Þ(/·T&Ç?Þ‡ˆ)?™6?Þm§Èlt?ÞΈuð?ÞïîWSF?Ü÷ĔТÊ?Ü1“æ"O?ßs©¡~¡?ÞD×Kvt!?Þ{‡ÝÇ;?Þ7§ž’X"?Üj¼A3ß?Ýd¦‘MGˆ?ÞGÀ·èÝ]?Þxé:ýUl?ßwéÎl?ßv!‚bxw?Ý‘=À6D?Ü®ƒþÚÁ+?ß@i?Ò¯3?ÝT-ŠQ%?Üf3ݯ_W?ÜŽ,A‘'`?ßò›¾Üç?ß Ò‡îw?ß RUJ”'?ÜÕE#úö1?Þœ€ëc n?܃FSÝ ?Þn§Ÿ>?ˆ?ÞYO]ø‚?Þ,~ûõ?ÝÝÁÕÐj?Ý”µÜ ¨?Ý-”ñÀx?ßqQŸ£"K?ßæ‚–W¿?ß7It!æÒ?Þ[gü@ª¸?ßȃ»?ÝUn&”?Þöqp+¼ß?Þ 'Âç%å?Þ‹H/Ê×Ê?Ü2Ã;»?Ý`ÕxþO?ßÑj/?ãO?ÜØü'Û,£?݇¥tˇ#?Þê?œkM?Þ‡‡ó,?Ý_ÅYS¬m?Ü`{u;9•?ßî®·Ç®?ß«ª^ñÈ??ßÃB¦{. ?Üg“ôâ¿v?ßâ³0Æ¡ï?Þ’dg”°?Ý@ý„³6³?ß{)Åè`?áëÁ(GýÁ?áën…õ9?á³gBÝT?àD)¿ESø?áqœMûZ?àÑgÔw?á QÓ’ê?à¾BÍ»}?àÂw MU?á‘ÃPŠ?àÈ? Ó?à6-ì2ö?áéESš—?àj.Mž~Ç?à=iß)¯L?áos(Wu?á³<­dÊÍ?à¶Ê¤ q-?áx0r»ì?àÝNÓsȤ?àçªèâÌ?àŒÞE!Ü?áÎÒ%ñï?àmfÕô¼?àÿCN¬?á•´ƒþÁ…?áEïô1±?á!3¦hbç?á‚(hXÇõ?à³bÏ’b?á!ó˜G0Ú?ᛤK K?á¿Ü¹²t?á¶Ø“Å}J?áYKÑ#¥z?á?«–еP?áýõ ÙÝ?à8튒Ž7?áÐ<Ò…®Š?áÀÔ¼ëì ?àmú¹×[?à&y¤s˜f?ᇑ`âþ?àξÿÝÞ?á:;Îú%?áds˜9?ẰÊ'˜™?àëÏ3È?áù%O?àH™©åÔ,?áEWš¾X^?á~¤”Tð?áÚ$ÈdDË?àñJðú=,?àçzö7­•?á.§–:ÑP?àÓbêEO?áêÝ :j?àpÚ2Å©á?à›¶‹óü¬?á.³°ÎKI?áÓð÷ôF?á7|í¬Ã?à€–<’v?á·H§ÙeL?à‘F`µ²?ỨŽX±?áP“«d7?áàð×M\O?á;Jo9?á2ëAX?á` òsñí?à5í°õ1ÿ?à¢Ò‰\š0?àì»(W²´?ᨤÒÔ?à[ ³I^?âÑSì‡?à>¤/Ó?à4¡¥Y]Z?àþÿ:²š³?à†F-ÇÃ?àrôï$Æ?àNé£ Kg?àö¶yN?áßÛ†§?àX¡Øô/–?áq$âVÈ?áë5¡ü?àéc#J6?áÿ’g1 ?á{…}ˆ?áÜ¡¢q-?à„Âfª?áDçÕ¯žN?àuŽ*œ³Ç?áŨÏ/Ór?àø;!0l?á—dPÞ o?à6}iƒÌ?à¼2›þ)_?á{Ø-;%ó?àŲÂYñâ?á1žA¬?á×=úv’?àtšD^eH?á¢|£Ê¦î?àM ü!Âu?à+y¼ßÅ?áó•FÜo°?à¸:Åq?à„Rýå?á/3¯:Ò?áóJ`QG?á{Upa?á%Kw‚ ñ?àÕ×Ãêö?át%*tc?àî/FEï?áaüªß?á­è½Þ³Ÿ?àUrŠL&?á4›ûôÐÆ?᜸“5Ìê?á/Y€|?àÙzµ§Õ?áœÄŽ˜ž©?à‰Ek¤?àÝß8+K”?ጌöW-?á0%QÎ?à½ò<öÓ?ᤠœyi?àØÆÿHÜ]?á—ˆsÃï7?àLußÉ­†?á§ðšœ¤{?áÞ=‡-Í?á±cÜv?àbrE¥+c?à”Êës?àÓw ¾žÎ?àEÚ^ù*?áXœÇ=ð›?ண)Pܘ?à`ΈË?÷?à)Ṉ̃Zø?àÔëK¸?à¨þ£MÕ?áE³Ð´Ñû?à)k¥’?áÀt¿˜£ ?༖¼“®“?áüùGÚ2y?á _p¦é?àQ^ ÿG"?à¯^óP×?à0">!²?á]XgõF?á[”z´”Ê?à_’P¤°`?àÉ˸;?à`á?ãTô"Èi?ãaÈAdÕ?âvÖ,LJ?ãf|Å{?âIuÆý´?ãAÿ½ûv$?ãfØlu?ã´l±;øb?ãß Ê?â÷»\å?âQŰǟ?ãœÔ°]•2?ã[”=ΞF?㬠ÝÎ?â’®3?ãšøx‰?ãøu6ׄ?ãx|)Ï?â6œu:®?ã9Û¤¡CT?â?=¦Å%?ãjp U4?ã•ðk %?ã˜ÜzO¼‰?ãNg땱Ÿ?ãÌèøº·ã?â²ÒÖ(¬?ãË4õÈ9>?ãgÐ;~¥¤?ãÖ½€dì?ã»<ÎxS¾?â"•ò¿Œ?ãh¼|¶ë?㑜Uq4º?ãÆ¸Ð??ât² » ?㸀 zÔ?ãs$ÞRU?ãËLÏéø?âkÒ¦öi?âþgKÂ?ã¨ä£Ø?ã+{©ù#«?ãò]?™€@?ãî}1Ì7?â§~£»É?â•Ö¼F?âÉjÖñ’b?â}R6ê“?㌴OÝ?ãÇ`®ç ?ãZcÖ³¼z?ãÊè¾HÖ°?ãäd÷ÎÁ÷?âO ɳùæ?ãÓtÞPÿH?ãWhâ?ã`´ºæ?ãpŸ¯?ãŸ0į$?âüËJ¥ÖG?ãxŸqŠ?â)yžˆ?âGÐS®õ?ã}85mˆ?âÄ>´×Èô?â-r®Ú?ãÑTÈsJn?ãË,¼ñ¤?ãÑtÌ>& ?ã°<‘OÈ7?ã¨äõÀ²?↦K¥)‹?âdZè~?ㆌZë??â!•ǹ?ã~ôOÝãî?âP1ðÝú ?ãའð®œ?ã¦T‘OH¤?ã^Sõ2EŠ?âMÅÌgK@?âW?TË?ã¢äm¿ ?âŒþ=¯ìÖ?ã—` Ë?âí8››;?â8A©rÈ?ãšÐ€™/G?âþãMø­#?â…ž\_Ð}?âÌfíÖ"k?â€"\r܃?ãt¬;zrŽ?âM)åâ š?ãúÉ>d?â=QÆ^V±?ãÖÀù`n8?âv~ESý¿?ãœ8™¨y?㥘Å_Å2?â×sgr³à?ã*ì–§J?âßïÍúØr?â—IÒâŽ?ã”Ý:HÙ·?ã‡í¤˜h?ã‚ †vm»?âE½ás‚?ãÉàºS?âÄѵm?âOQát•?ãWñ¯(7?ã£ø’ªm?ãÉ Û‚è4?ㄸdÍkî?⺲ø@)ž?⨚ڨ“?â*ÖGЬ?â?rW6Z\?â¶‘@É?ã"€¦kõ?âÈß%YëŽ?ãÊù ŽíŠ?å ‹Y®Ç5?äG'Ÿ$?å{QŽîÑ?äžB{ï½x?ä½V´»_é?åêÕŸé ?åZý%o›?åØLõ×â?åÒ”ö©5¾?åV°f£µ?åCì É??å +‡8ãø?张ݳ+_?ä}>‚}?äZÚ*t´?å&‹¥È7?å3K¨’39?åð]nuA?äݺöÐç?äPñÒ!¦Á?å˜hfYnS?媎=Áã?åmH$5î?å€F;Óž?ähZ‚¡õ?åb ?äx2N¦AV?åŸè•ïwÙ?äÅ®÷E‹?åûMD-ª?äêC.¬J?åw^!?ä/¡˜´Êî?äVÍè0t ?ä®N‰$?凴B†äZ?äs®dçÆ?äß:óÓ;P?ä“Z`{ñ?älô³ ?åê-²0?äQ¼o´?ä$­—¸Oº?ä±~µ.ué?ä Én|d,?å‘àtCЧ?å×XÃýX?åÏLàÇ¿a?å~86VëÝ?åclÎÙ3?ä[µä•ÒÅ?äYÙÜõý?å}˜-ô¡?åÇ4ÃT’ë?åç¥ê-?äÓRà­ª;?åù-4˜ ?äÝÏĦ?åAgËËd?äŠÆc’À?ä,եʅ}?ä%t†q?äã_ Ò@F?åZ—õüò'?äñ«!21…?åt$";â?åƒ|9—+¬?äµSW?äÇ~»ë["?ä™fl^ÿ*?䩚šÐ3ã?äô÷+‡ P?äï£Ãw0?äÕîâï?å¾ ²÷C?䀢0"ÜÕ?ä…Î8\Ìê?ä]%ìªÄ?å¾Ð¹MÓ?åÓ4æL@¡?äÂFÊ,¨O?åË`Ý´ñu?ä2ºÆ²?å2³²\W5?åžL‡)ƒ?å ¯gNï?ä¦R–ßÈ?ä>õĬTñ?ä°Æª?jÆ?å»KLbË?äß"ö\:Ý?åïcÛÉ?åOçØý”Z?äÿ9²ÜÄ?åo< |±\?å|ð=¹Šý?åý­D úK?ä=YÔ8Ûy?å>×"·å?åÊîÓ<;?å/½QÁ{?äÆVì¯9?å“Hrˆ„˜?ä€2RM?䀲P ÉÙ?åë…úœÒ?å5ª)÷¬?äKñЭrO?ä`)ùÜr]?彄½$$Í?ä(éš,F‚?åDKØ"Lj?åHÏæ+$U?åÊló[¦|?ä§ÊÈDF]?å:Kýí±ü?ä•.™v›Î?är‚`‘\?äÕ?!õ¡²?åç9n€R?åþJ'­v?å$7b¦?åÉlÖ¶Å7?äÉúÔºäß?åÔçRG/?äκá`s?å~Ac‹z?åƒ`^™¢ƒ?ä»ÿ.÷]?äÉžþÑ??äZ† ›l«?å7Ǽ7“Ð?ä ²ˆ&¦?ä²bªhñ÷?äëÏXBG?ä)á ±,Ó?å kNo}?ä[>§k?ä•ÜA¿t?ä7Ùîü–M?åÓí§”?äþsW~[?伦ÇZ´3?å’œl|ºë?ä+i VM?åÿG¡wÐ?å"ÿ¢@¥?åOìfDV ?ä^\°&?åcœv§?åqØ9f=?äq~3i>À?åo¬(d¯í?å™°{æÒ?å_ô õXÜ?ääS!Ž5ä?äpvUDƒ?å_¦S¦?åé6˜bfû?ån}ðE™#?åëNìd¶#?åĦ|³L?ä­RÙý·?ä¯LzFú?äá¬ñ„"y?å^5Ò;¨~?äCOß?äÕP²Ž‹ò?å Á,G0_?å;H å?夹°5ê?åD¿ñT2?åßÉ òð»?äMÉéˆÁ?åÈgZa?ä±Z°ë%ƒ?äížû?å¥Ì³ç^Y?å5#Ÿ~Å?å Ê>í?æ’Í7F½?åáÖ£ÞŽý?äÀB0´?ä(CdöD ?娊wúè?å šw÷;?å׎ªµ¿„?åÒ¢¤3H‚?äßÿ%'?å?ívbÿ ?äÊ÷?\½‹?äUâ’lç?æ¡[îÏR?äàPN?åf¤;8?åp )˜N?ä•Bx2–À?ä…«ó?åA/ù¹ýÀ?ä@Z0ü6?åXÐN©?äÉþóûòû?ä§Âªý$K?äu:84±)?åX—õ^«g?ä|Š?Üj>?åb| «k©?åɬæl…§?äA ék¢I?åÕÍ!”?åbðo]ÏÉ?å±<}ÔM?äŽÏý¬§Ò?䳓X?å'3Š`?å4 Có?å 5_=L?å-iûýX?äP7ð*œß?äÝ°í¹ ?å_-à礩?åCIF²ºv?å¯N‹½ ´?å0¥pÉkZ?ä2«täYí?åÐöv¢¸?ä_Çn?äw؇Pr?åîÞŠ†8?å²R*df×?å¥Þ7h°?åHIjðð?äàøGE?åÍÊÈŸ@]?äxW߸R?äf{êbŽ?äñK·\l9?åJÄI¶?äkêQðu*?ä%!¨ÇÞ?å"o‘€ÊÜ?å|49D¹q?äàÓ×Äô?äGÎ\~?ä)锈Ûï?äel^qŠ?äK-ï¿$)?䌺~¼Ù?ä~tº_´?äç—WÚ~?äJ¶|±Y?äÛÿWS»ò?äÀ1âµö?äc–\@­•?äy¾VâÔ"?ç+Ÿ™gɘ?çŸ {×s¦?çpèïL?湆¯°ÑÎ?æ7¡«/•g?çB/Á³r?æ ¹† Ñn?æÍ~Ýš‘?çûi*ÛS?爔U•i?æ:×Êå6?æ”RÍÕ?昈ñ”\?çqô/.¿?æ_êNïÙ?ç7¿³úx9?æò#"¥Ü?æý5ì¯Æ?çYïï8~ò?æ‡ÒHÆvö?æ IMYËX?çÍÀØI±§?æí_ù‚c?æÿC@+WÎ?ç,—›¦)?çéa…çø?çͼڤª£?æ“No¥#g?æÌÒá×¹C?æÚÿÿdÅ?çKCØ´b?çºD³Fz©?æˆ PH?æŽUÚß?æ+‘ŽMš?æïËólé?æq*m@´?æ0 ˜Å°í?ç`ýH7ª?æUP1G¾?ç¸l¯DýÄ?æižߤT?æ9oî0?æ#U«*?ç¾ ¹Ë0§?ç>'¾Ã³í?ç¬ —E?æ •ZV›T?çŸÈ{Zêý?æâ¦ÿíå?æe«q?ç܈õ#Õ>?çQgÝ€?çÛŒó°Æß?ç* Žùú¸?秈ŒKðN?æPaà€Æi?籌¡¬8ò?æ*Uš¿Y8?æ I^wÚÞ?çWF"Ü ?æZ\Ã6?æÄȯêù?æq £?æÎ®ÚO_¤?ç §PR5ø?æPÁØÃÐ>?çÀ»Ø?è)C9 R?摲[\j?æeú—.?çr¬"£'^?ç“\dBõ?çˆLO·UÙ?æÝtÑ=?ç»À¶Üä?çÃXÄjY?ç©”‘sBª?æÏ~ßAbF?æ¡€Ð!«?çÍ4× f®?çÔ¼å»Ïù?æCÕ¾\Ô?æÓ’ßžÝÎ?ç‘_³8?ç¢ô‚’+Z?æ%Ùˆ5z?æ•kH3?çc<k|ˆ?æmtiú?çbÝ­>?æEUsl?æý¯?ˆÖÀ?碼„ðÊG?ç>ÃÆøõ?æ?y犽ƒ?æAáâ!þ?çoü.k9ü?æÖºû!©}?æùg<«??çl@¯˜R?ç'“x`?惂Kɰ?çƒ(DšZ¥?æQEâ±§N?æ•i3ª=?ç¾Ü»²+#?æIS÷Ž?æpF/Ô.p?çŒDek¾ê?çwr7iY?æÙ…êæ·?æ”ör μ?æ„~LI=ö?ç«4“çÝ7?æÁ‚Ä©yî?æM à!Z?çfPש?æÃ–ÔÆC?çëiœ¹7?æ"_vU?æ ÅzRu+?ç ï^ÃY_ñ?ç\¨5”?æ—Ÿ»O>Ÿ?è*ØÌi?æGŸ8;?ç°‚^¥•?çcy<ÙŸ?æ-oMÝå?çA‡ÞáÆÝ?润бÎ?æa2E»â?çÌTÖ ?æçgab°?æCQ˨µ?æb"Z²o?çe\´>?æÎ^îcˆÅ?æúTŽ0à?ç`¦©\?æpëpd’H?ç‘…€6u?æK¸Éô?æ–ÓÒè¦Ï?æM“£•u?瘕x²L?çîš Ó;?æÏ ‚y?æY7ˆÈ6(?焱]øì?çm .ðò³?æ-ÿGË… ?æØ„K6±‡?ç›°ˆÊaá?çÈÒ\Ïó?ç‚ôIÕ¡N?çý%7•÷J?èñ=ÿ:z?æµR9ï²?æù}…%P?æ=oýò?æÃžÙÁp?çƒQXé¬q?ç(@ì£b4?æ†ÿ£_±?ç‰ém–î›?çljÈÇ¢ƒ?ç(¢o~-?æ ¯.5V?æØÀs¡w?æˆ_ÍŽõ?è.*½ó®?çODþEz?æÍ0JÖ¯â?挿í®c"?æÊ (R>Î?çèrë;'»†c›?æÈÊÑB€?æ¥kÄH]?æm*$ÑÐŒ?æ×Jõ:­¢?æ/‰Âªº‡?æÀo“Æ?æXGV9ÈÊ?æ7.Ê?ç¼â%?æ]Z +œ?ç¼iÇ_?çÍUд?çkÝ)ó6ú?çž­ˆâ ª?æ«“Ø¢°¿?çð²C{´?çÃ5À^¼¦?çq-4VÔ?稣Éo“?ç€JÅÀ…?æïÔYÀ?æ¿\M§?æõLm_BÎ?ç†)],?æ /ÐÌéß?ç}åZí0å?ç]÷Dë?æî¬^Áo?ç_yEr•°?çÈŽÔdŽ?抸BW?çm}+­»y?çGÔ÷rÃÁ?ç'ü»xn?磥÷Z?æbW®µ™ƒ?ç#Ôõšj?æøüt BØ?ææ@Ž„SÐ?ç:\ÞMxÏ?æÎ¨9 þ¡?æ+Zø"Ú¯?æ„㚨š(?ç,‡Û½³?æ£t°Ö^?çBDäRë±?æcAˆ5Ó?æÆì7–q9?æË£ÿÓÁÅ?æ½îá:?æþ<†#÷!?ç,`«ä½L?æqœ?é”$_Å=?鯸›,.T?齈·w–»?èJÑ [@?èh MÏã?éxD+ïz?è"¹€…Ÿ­?è*MxGõ?èuÙQ¼?éEƒ¦£Ö¶?éC;eÿ?èÖâÑxå_?éÌt°¯ÙŒ?èÀгWw·?èZíôz?è§ŠˆfU@?é¶Ð¨äøÕ?龄»Ì?éseIñ£?é'׎:ö?è"€×£·?èÑâàýÓ ?é{cô¶?èúvîKp?è²bÕ÷œ?èÅqhÑÍ?é×0hdh?éØ˜×§Èƒ?è½I¢¬4?è&I‡Nãh?èêg C€õ?铌a ùØ?è¿nºöý?é¬Ìš–´‰?è >~ŸHq?éÃÌËZ´:?èØ^ðZh?éÿ9;Í‚[?é¸<¬\??èq|=B?èæ7Ýì ?èós†`º?èc~úsT?è U³}?éÆL¿g‚z?è{ƒ ?év¡t?é¹|¯N'h?èQÁßL?ü?è4¤=B1?èI‘Ñ/Û?éêÍ%Y»?飜‰(GG?èE™Èó«?éÂdÁšDŠ?è™bnÆfA?éÀ:ÛÈå?éÔ˜ÝãgD?逤6?ÞŠ?è0•œ’û¨?éy(Ã:Ã?ée(²Ã?éS‹à¯5?èë‡n™Ù?è<‘µª€Ï?èu(3óœ?èÈ Ïé/ú?è˜ÎpûÔË?é×<òF?è¡^ ¢?éJSÑb1 ?èhåž?èAE¿Aþ*?èÊJÎã"\?é’`Y!uð?èÎÊÕð…Ò?èè+• @?èéI‚7‘?èk–×?éšäq1=¶?蹞°ÿ2Q?é7K®ßÈ ?éçU~yõ?é8ϳ%?éA¿Ãó[e?éÛI™Ñƒ?é oO´ó?éÇ0Æ0ùç?èl úܽ?é'£…“› ?é×@÷°“?é‚71•?éw„#¶lŠ?è«®޶?è Pqø¦?èÉl$¯ü?è­®˜Ðm?è)I7_È?èÆ Ê„…„?èä»Õ¨.?èl®0÷ó?é\t;Ç(?è |â9?éf·èå?éW]œ€T?èÆ®ÂÔ+-?é»8¢`‘‹?è3Ý¢,Þô?èf*í‹®?éÑDԵˤ?èÖå„ì?éÑpØá µ?éè…½¨?é §~#¿k?èÕhWkè?饈‡_Q¢?èÜŽô ?è 9P06¢?èËÂÐ…—P?èhþ ?éWkÙ¢¼?éHƒ®ÓT?éE›£DPå?è\¥ïˆµ?é'F¥«m?é ƒFþJ?è¹B¦·ð?è•þaaÙ‰?édçþŽ[…?éYûì$LÕ?è[Yò»Wg?é³( d/¢?éPã܆½Ä?è¡Ê}c?é°Ä’”x?èI9ÇT‘½?é £cú³à?éÒ´Î'Û?鈔>Mßò?è¸ÒªO?è.=—Î^??è´Ž¤G?ê1>°í²?é¶`œ€÷?éXñ‚?è»ú‰Åc?è4©ž\ê?èHÆøãà?éq4¬çY?éx$­>å?éªPˆÚ6±?éØx岸?ènª K?è¦Ö„‹xƒ?ém#ò›Ç?éT\|µ3p?é+04øM"?èo‰eù?è7²…–›?éHu|?ébðš&Ñ4?蟟7·Ô}?èº`·xv?èM¬F˜ï?èóÏÖQ¥«?éØ]mjÆ?è 9QÑÄò?éÉ€±>¢•?è õT9œ¶?è»Î­ª?é°<“R*¿?èÅÒÃÀUâ?èšnmYœ3?èêwÇ?éŸP¤ªÌ?èRN±=qñ?é?à3Ìk?éÒ ´ŠØ?èÍo‹0ðª?è9ê‚8Àä?è¼ãgì6n?è z9 MÊ?è¦k#º®?èþL?xq?éUhXP$Y?èÅ“bTØ?è ê9=r”?é #ð[?èÉJ¸Ñ?éæ`×ö?è€J%þï-?éºTüÙ?è5 ¤wìå?è EWRüA?èLáÔÏÛ¹?èM Ôpuþ?éP'Ôo@?éa‹ê'Ùw?é,gs"Ì…?é{L³²?é,ÄþH1?éG0Dò É?èâN“Pµ?èÍ×±™I?é)/åaøõ?é»éfÔ?é ä,òX?é²­(Jm?è¢Oà4[?èU.¢ËÑ?èµÃ5‘£?éX¬€:©>?è®YÕå?éú,!l?è~rse?é+ ?èsFÿN?è*­‘»Pà?éCÿ¾½Óí?èÆâÅÉ„?éǽYa?éK'ÂmX°?è$=+Íú?é:¤NdªÜ?èù?¾.¢Ù?èRê°ôVà?è¢+Igß–?é&p pŽ?脯 gމ?éYÀ…—Äò?è½÷vâç(?è(ên"2ˆ?题÷ÜŒ/?èL¨Æ²®?étpN?é´ü–N—Ÿ?è…îF&l ?è€*;ýQ?éôiT?èwf) ›a?è»ö°-ÁÅ?éã_Ét?é¿l§Íȼ?è¸N›Hß ?èÞÎÓÄ’B?è#zfùÈ´?èî[ÉÑd7?ènL­ì?èÝ/ŒOü?éÏéDþI?éU¼Ž'“š?èÇû~ƒÁo?èINª}™z?é/”CÈÒk?èÂKí©??écTyÔï:?èÜë±eU ?éE ’„ý?éEPÓv°Ðç?è¢W!IG?é ŸíA1_?éÊ”ï6#p?éþAS¶1D?é\PIlCõ?è î8©,?éJ|‚‡¢?é¦i9.?é) Õäˇi?èîX]ƒ’Ï?ëâTÒgXœ?ëa“ÝѵŒ?ëûL7°O?êš’Y&Ne?ë'RßA?ë’˜cÓno?ë‰ÈRäwï?êsF)‰?ë9+¼Q[?ê;M¼§Y›?ë0FóÚ?ëƒBvis?ë«kÊ:?ë· ¡ö ?ë%³|C@l?ê8¢6?ê*uhÈ?êh…ùä_·?ëóg>ÞÊ?ê%9K,ò?ê²Æ˜”/*?ë¹$®<íA?êÈöÔ¨@z?ê]Ê}Z?ëX¥>?ëÛx>j5?ëup23 ?ê-¥›ó .?ë “Y—€1?êl^ì¾ô?ë'buY¨?ë;“¢ûn(?êj¢•ý?꯮œh£?ê‚26¾|ý?ë&ß}{Ѧ?ê±›8Þ?êAdmâ?ëÐXèï·—?ë¯à­ç.¶?ëÆÛ!ž)?êjUþ?êêCª“¿?ë{ <2?ëë0›~?ëNoÓúÂ|?êàúö¯{ƒ?ê‚N6¾ç8?ë¨È}Ò®?꜒h]Ù?ëü$âIÝ?ê8á¨/ÛÂ?êñ“CE¿?êÏ2×UÞ?ëEûÍC#?ê…¦PbÊÃ?ëË”ä04?ëÏÈè[x0?ëgx7Zø?ëÌ^bÜ»?ëÑ\áǦ?ëÉ”ÆFl?롼qfï?ëyìÞH¨?êÙÞß`Ó?ê¶GùËl?ê·Ò˜²½?ê­_Yê?ê%gTö‚?ë&?…°Í?ê–úk­Â ?ë®H š¨Í?ëü1?ØK…?ëþ-B¼ùà?êâ^/b?êÐÝ bú?ê9¥¬trò?ët”?êâþí ßD?êcë¥ÈÝ?ê!Ýl€™g?ë$Ÿk¾?ë™ÜF õ}?ëâ¤äºX]?êÃvºF¥Í?뜤ds„?ë7k£ ØY?êëß_½?ë GVgîþ?ë7J•í?ëU[ã=X?ê%I‚¢‰ß?ë=#­IN ?êQ1Ä¡-?ëÔ\¦*Ì?ê‘f9â>+?ê±3Þ¸?ëqËÕÀZ?ê}uÿf-Û?ë\#Èãõ?ê×2ØÄkü?êA©©F?ëâ\ò¼Û"?ë3—š›x?ê»R¯¢V‰?ê“ê`Oâÿ?ëL¯ÏÏu/?ë;k¥ê[ù?êÔ Ä‰£±?ë)—8:ä?ê²nÕ:ž?êï–ó€x{?ëÙä×EËÛ?êíG;ó?ëÇn‘â ?ê.I‘¸]M?ëÙ¼ãU­ž?êÁN«Jµ¥?ê‰NæpN?ëJ¯\6ib?êl!ÕSÚ?êÿ2õÕAL?êféíןk?ë/HR9?êx&#T˜?êMC‡ÎQ?ê AsCÁ?êýº­Üö?ënk}èùD?ë¯|¾ó˜N?ëŽÄ€–½ ?ë°l\Ðz?ëS-øt?ê1}ÉC ?ëu+ókЭ?ë·´‘4?ë,˘ssÙ?êenXLÉ}?êDª““Ð?ëí(ÃJôø?êZâ9†A?ë³lε“Ÿ?êËÊáZ“?ëã•$cùL?ëÈ\¹ýï¿?ê@V ׊?êuÊY5y?êW’2bzÿ?ëìõãôÈ?ê£.Ÿ…rj?ë<#àÒ÷´?êϺÞ6Z2?êaYš'g?êšýýË’?ëäÐ#Ã?ê/q]^Ä?ëÀ4š«?ê‹–H3#?ê“:ZAXÔ?ë™?ìoJ^Æp?íÏ€Ù#"h?ì¥b·ûÇ?ìUÏy7?ìF‘ªB7×?íj÷è8œ?í1›k4–3?ìP±½Yrõ?íQÿµkîÕ?ìêú÷ ¡ç?ì1Í‹7ß?ì O{½?ìS™ëÍ£?íôÍ>«ç?í¥\§.¢e?í^в9?ì§:šä÷5?ì Q[\6ý?ìHåÏÓ•ò?ì›NjiHŽ?ígÏý-Æ>?ì°¾ˆ/L?ì‡r2°6?íS^ kÿ?쓪G£û?ìÓÍ<â-?íü=2ÁH?ìÞJý(ÿŠ?íÿÙV£vT?í“°JC"?ìKqéFMd?ìkö*/£n?ìêg!«!„?íyð>=@?í¢…ÙÓð?ìil¾Ç?íûÁ'Xžû?ì ±nñè¼?ì,Õ„GX?í»0šÊ´7?ì†ö:dUÄ?ínä ^ßÏ?ìÕºÝy%?í“@hº?ìÙ$?ìk)Fc)?왈.2ó?ìwi׊?ìgZ¡|ã?ì¹þ¾/™?ìÁ>ÂÊdÜ?ìÛöï„FM?í¬¼‰Ãü¸?ìØ:ÛRÏf?íY×{á?ì¶’•Ò˜ ?ì…ú721Z?ì•mkˆ\?ì'½„r/@?ìé颇?ìݱ´?ì¡ÖŒè-Ë?í}lOôŽ?í¡ œU£(?ìs²6ÇèR?íý%Gz¬?í/Cžc,?íäqs÷?íœfu–x?í·O&+Ç?í,Æõa?쮺ƒFdŠ?í±ÌŠ?Fo?íõU Øð1?ìê¿¡f/?í»©]ìÕ?í ×€_þÛ?í¡ä“R²?ìFµÔ¨óf?í“„tJÌx?íËüÝU¢Ì?íHÖÒšs?줃Ì?ísX Ëjt?í¢_(Ñ ?삎 a²í?í"_]܆W?ì‹ò+Àš?ì2qƒ‚k?íù= Iÿ?ìæ*ò 6a?í<÷—Ÿ“º?íVÛ?ìXí ™?íâ% ‡?íÏLÝ‚½%?íÓÐêÍþ ?ì²:—Âf?ìF¹º¨Ç ?íC;›~”?íŽ(+.ïI?ì_!¹÷ÿ?íé̜Ӿ?íÁ|iU†?ìw×ÐR*?ícoÎoë—?íð|þLâ³?í?3šO$?í­„‰q…?íth#Dñ?ìås \~?íå%Š»?íÕ¼Û‡?ì¦vu·Ñ?ííõ^¨+?ìÉšœ”3?ìf»6°l?íÖØˆá4P?í ¶ÌLž€?앺¤äÂ?ìuµ«0Åâ?íà®Ú+v?í/#dô.ú?ìlÑîl /?ìÓ&ÌV–?í©¤;\?ìß6ïk‚C?íPÓÒ“ˆ?ìCY³17"?ì"Qcî”Ü?í\#Âar?ív÷Õèù?í¨  8½Î?ì6ÅQ:?ìý.e¾øe?ì9„ŸpÁ?ìër"m#+?íÎX`C»y?ìV‘”º­ö?ì‹Íü¶1÷?ìq½ñNœÃ?íí¸÷Ëûz?ívHdLU?í¹œR‰?íõ= ý4?ìö{,æý?í‡Ô Ù|:?ìöÖ²ý0?숱‘ÌZç?í,:$Õ ?ì-|Á¼?íWó4T) ?ìš¹©H¿v?ì~©¶`À‘?ì螉AZÑ?컦´N*?íÏ€¨€×Z?í·ä ñíÊ?íȨ öà?í.m|‰?íiSmE³A?ìèœN‚l?íÑ|z¯Å?íni¡ÙvÅ?ìc O@?í·ÝãÛT?ìí†abg ?ì å¹n¡?í"ö´3(?íÙDª"¢é?íçXšÆ?í$—ª7?ía›¨™Ã?íFkÔc‹?íý¨öÒLŠ?íÌn¦´2?ìßš‡ J©?í}g¬eÙ?íè8vo[?íàsùVª6?íbVÝ%D]?íÝd—°?í°«† ôÚ?ì=<æ]?홳^y a?íï @ï??íßköïÆ!?ììZ!;ð?ìfT²ÜI ?ìÞŠ\|Ï?ìoê?D?ì¥!7ýƒ?ìNø•½-\®®à?íVŠÜN{?íG4ómÕ?ìo@ßEm¬?휷Z$Ñ-?ì³5"brö?íþPAI ä?í"¦h§xA?ìßM“$s?ìÚýø!C¸?ì]¬øÿ·§?í³ôI#6 ?í 2ÓÇ{ý?í¿ÀXûž6?ígÜŽ—Ž?ì5>êÁq?ì«öúë™?íRÐÚ(y?ì€h(¬Õ?í[Š`Ö2?톳ú»=Á?íÌ{Î÷÷?ì«B€²D?í†Ü­??ìɺ¡Y÷T?ìC¦8G?íj'ûåXÕ?í'5•ÀØ?ìù'x“³?íóþ{X?íL[“–¿n?íäÜk«‡å?ìj±뎼?ìdÙ[*Ô?ìZûã?ì† ¯¡Y0?í°¤]¶”Â?ìVÀøŠ»v?íu³¡ª‘‚?ít˜J?ì§ùùB[@?ì¹Eórã˜?ì©ù¯âq‚?í•xÑC?í—ÇÌ{w#?ì~¼¥èì«?쎹9F?í9J—ER?í¦-ïrS?ìðùáà®ç?íÃçÕÑ1?í2jŸÊÄ?ìH©Ø¶?íg’Õ½)?íË0è™?ì[8 †è?í†T>º]?ì-0%5Ï?íé€+P$Ž?ìÐ…’U5Š?ìÚE€Ù€?ícëÓ‡Ã?íSšÐõŠ?í3>œÈ*Ý?í¤Ã•±»J?í¸‡´{nö?ìµ¥Uý¿ã?íAZÅCâ?í*vA-3ø?í´Ûk¶ ?ì%³Úg©5?ì˜6 ÿ?ìÖ5ê°ªŸ?팃c?í‘oAÛ=?í^cd·R?ì6DŒ†uø?íãø•XÜ ?ì¶v ŠÙ?íÛ„Feý@?ì°f„žD?ìRQÄW?ì¶!½„ñ\?ìs (.?íøà¨ëä?ìÝ2/òÐ?ìSñor ?ì.ÄLÜêû?íÞ8nÞKû?íÐà©ñ›‚?ìðÖõ{~?ìhaúÁ„ƒ?ìï³ ÞPt?íÊ ÂÕ?íIµKÛr?í47ºØ¨?ì§>Q+±?ì-ÑLüLò?ìä{ô†?í¨cDW˦?ì„M]Ê›•?ì¦%¡¶ ?í·ƒò+©d?íÏ%¡?ìÊÿLeú?í©ýFk?ìs)KZ‡–?ìÐQÞgO>?ìNÙ8¼X”?ì¤|…¯š?íI¿[ðgl?ì+ÔôEó>?ì\5ÅÔ?íÊôYý‹ì?ìé¥U„?ì 8xj?í÷Œ™Ô?ì—?í•w½Ñy@?íVŠ\yÜ?ìßîPé-?íž‹àn˜Œ?íùD­U\?íå0–H³)?ì¯Vl4¡Ò?í+ƒz˜7÷?íkP ~G²?í_#øÕõ=?ì„Cëß.?ícc~N{?í™]SƒÉ?ì12 ¡?íÑ8¨P“K?íä¼¶¢Ìâ?î¨èÞq?ìcíb³!Ù?ì–Ýü}|?íÉøqì”…?íR¿†a+ß?íŽÔ,9?ïVGÞ4g?îe2èŠ?ï8ß±¢)m?ï—kÀñ®?ïá¡%˜3?ï8 ª¸Uà?î2E™ûÏWxEµ?îDM×7€o?îF±×©Úg?ï S\q–ƒ?ï+/˜M‡?îÖVÖøÁ*?ï°†ž"ð?îËF³¡os?1¨¤:l,µ?îµvy`Å?îØŽÇAÎ?ï.KnLÙ0?î9Tµ ä?î™Öy¹ú?îÄ×·j°?î¡æ™†q?îbî!ó¿Ö?ïTøª×x?·Ž?îèû#¾Iì?ïõ .;`?î$Q|ˆ™?ïVwב+?îZÁÚ1I:?IfJ?ï~è‡OŽ?ï¾(£y9 ?ïÃܳ丅?î¥&„öIÿ?ïm¼"ú¹@?ïßI%]?ïzhdÎòÒ?î ~²ýñ?ï„vlÒê?îtÎPöݯ?ï5ÈÊÜô?ï b·åO?ïg$®ÙÇ?îEµ»(È?ï5•(®Ü?î±Ê‰>pÏ?îUEÒd°4?î7Ý›±£®?îSQÔÑ?ïä©3•ö?îÝ}Õ$Ò?ïéÙ-¯¶?w%~»?î Æ±‰:e?î2ÑÏ„$‘?î#©«IÅ"?îH9ä5Ó?îiRt }?ï˜lp#H/?ïn´ ©ßæ?îæžôÏ2ç?ï Hg£©c?ïÀü¦‘ìÈ?î$mssuÌ?ïô1õùþ?îžÒ|œºî?ï‚lP%9+?îD=Ö§U ?ïoÃý?î\’Ÿ?î Å¥vo??î`V»ý?ï¥X•͸K?ïuì+láW?ï„È@‘Á?ïñiDZD?î7—4òœ?ïaWâÀža?ï­lyÉ(°?îaãLç}?î?ý®±–¶?y|x?îl‚$w?ï÷Š?îùEVGz?ïÓ¼ýâ\?ï~H\×3>Ò«Ç‘Ð?î:!µ±ð?î;é§Úk?î á9øãÌ?î\½ÚiAŽ?ïhÃà?¡?ïìLæ=Öó?ï¨øc]Nñ?î Ù*—`d?îµ3˜ÛÈ?î y@Cï?ïÜLázøI?îµr «j?î€NBUGÆ?ï1©‚ëŠ?îC9Ìë%m?î¶î¯ ?îŸ^t–†ô?î­æBªA?î`YÜ¢•¾?ïî¸Ý'±À?îy]ùs R?îouÝZ&¯?ï%1İ5?ï¿ q3Ų?ï"gQA¥Ì?îqÿÓóÚ?ïEã£hG ?î˜"YÞ @?ïßXö´Ö§?ïvh-ö‘?îj~ į?îX=é6äf?ïXÓãQ R?ïåtà‚Â?îGÙšV?ïÔ@¦”ÙE?ïaWˆ* ç?î1ý½Ü¯?ïµüCÿv?îÖ vTã¹?îÞN£Wœž?î<µtª¢?îÐr™L?îâ~Ör•?îd!öu?8¯*p™?î1ù‘³"?î1T”aÁ?ï?KX8/?ïc#ÐõA?î[yº)?îiI™˜Å?îT1>Mõ¹?ïªËÌZ?ïS7Ó~?îªMâiRT?ïØ´›Çy?ïÙd°eÉ?ï–7Èv¶?ïømTº?î·²‹à?îsr§ñè?îZ‘á•?ïÁø¢éa¨?îîjò/ZÌ?îN‰£}à=?îEuWcŠü?îxÍ“oåF?#Àø€?î—툃E?î—!OI¿‘?ïÔ¤(ñf?î8Õ YŽ`?îú.Æ}s:?ïä¸è7?ïkíšö?ï _+B?î¡Êim3_?îh)ù¯ Œ?ï Ã;£S?î©Uè:j?î¶>\,Á?ïÞÓ£dl?î|½}¿ž?ïϨêà?îQð3»ú¢?îÿ!ÂùFæ?ïl· )x?ïñ[k×°?ïÙ0‘ã7Ò?ïzOôÑL»?ïíóõœO?ï¶4ˆuM?ï%wsI”w?îá¦ë4?îÂʨ±a•?îõœ£Q?îÏÒ›‡9Å?ï-×ÉûÃ?ïÞ^Ô#?î&@>ìSƒ?îj$v½ÈC?ï+’'LG?îI<¨æÇº?îþrM*ŽJ?îwy¯@¢Ä?ïÌ$ž 2?ïÞ¤ÎÊ—v?îº&—å?ïóMl‡w?ïìKpþW?îAO”Òà?ï­ u×Ñ?î7Ùtó¯¯?ï" ã\?î4µ—|¯?î£Õ£ù3?ïŽnaÉg?ïâ]÷=#?î?ðÕ$ß?ï©p ß^?ïÝ$į?î?—´Œî?îçßýLÄ?î¾ö©yÄ?ïWÞÒ‚á?ïÂÜ´ï‡ñ?ïÐ`Æ×¢?îÕIX–c?ï1ë{pî?ïZ•îV?æËÍ?îbÝ)>Ià?îýzkÛNu?î‚i@¹[¶?ïeƒŠL=?îÅ:TïF?ïY?‹±Ç?îméõÚÕ—?î¼þŸX#ä?îô›«¾µ?ï3· ÿ†L?î‡ZFƒ|í?ï Tt®Œ©?ï#7t£´$?ïè˜âŽn?î ªA{‰Ù?îŒÎ¬Hñ?î8á;&­?î<Ü׬¶?îÜ–‚˜Sv?ïM§X´ û?ïðãFCR?ïÁäŒ(Ë?ൄ젡‡?જëÏ?à,$£å¹?àT NÀú?àì!‚Å“0?á½Læ‹Ax?á-¿Ú™ó˜?áýä嬚?áÕëZaJ¡?áZ,]P0X?áË}H8Øp?âLLÔÒÐ?ãÝ©+fs_?ç˜nse¾!?ꇢ$°?ëçÕ¨¹…?çoÏ[cí?è9åwÕ¨s?êÄ1jLÈ]?ëÛúw7?ìø}qÿ?ìȺX`?ì-ÇK´?v|?y¸?y‹?y¦?y?yÏ?{?}ç?} ?|ˆ?yÀ?x?t¨?sÚ?rí?{?~¥?|4?zi?y0?uù?x?x?zP?†í?ˆ?‡S?}!?{ ?{2?~ ?Ð?zK?}ú?{?o§?pÖ?sß?v?y ?yó?}z??‚Ä?ƒq?„?…?y¼?w¯?q!?n6?oö?pé?s•?u?y?z ?8?é?‚L?ƒ·?ƒ@?‚á?}Ž?wö?n¢?pý?o?wB?uš?w›?p?q!?ou?wL?w”?x¼?y?yF?y;?|4?‚h?ˆx?Œ£?†1?‚ë?‡º?†Á?ˆ?„?ƒ¾?ˆD?ˆL?‡¾?”?zO?|ò?}b?z2?€/?}Ì?|'?z??r7?rà?yy?p„?sæ?u‡?x·?w•?y:?y/?l>?k?k–?kF?k2?jç?mÍ?l^?n¤?kN?l«?lï?pœ?uâ?x;?xm?y?zE?}¯?€$??‹ö?Œ‰?@?Œd?ŒC?ç?Œ¸?Œs?‹ó?ŽÚ?ˆ”?ˆI?ƒ½?‰ƒ?‰?„ÿ?‡n?‡û?‡ù?‰S?‚Ú?€H?‚o?Û?Ä?Å?~ß?y ?‚ë?zM?tÙ?uð?q’?uO?s)?v?q'?oy?q%?p|?ih?j6?i¡?k%?iw?j?oŸ?j«?m3?iÅ?eÉ?e¢?hA?gÄ?e1?fž?g“?fx?f¿?g=?j°?r?r?vŒ?x?yú?}’?}ö?…I?Œ>?‹t?r?Œ|?‹Ì?‹Û?&?Ž!?ŽN?Ž»?‹É?‚ê?ƒ“?„?ˆ=?„d?ˆa?…?„I?…ú?‰?ƒ4?Ó?z??yM?ƒ¦?|?yZ?ƒ•?|u?s?q^?pú?x«?t­?v¢?pE?r±?qö?s”?iË?hp?jö?m¤?nó?jŽ?jF?hÕ?g3?n»?i7?d“?fa?d]?f?g;?i0?g«?eÙ?fA?iì?r?rÄ?v»?wà?yÑ?|?€+?…Í?†í?……?ƒÃ?„?… ?‡Ñ?…³?‰#?…Ë?…‘?‰q?„ˆ?{?y‚?~Ý?y÷?{U?€?~º?zª?ƒz?{q?vF?rÛ?uê?tÑ?uñ?oå?sÿ?w¤?tä?q÷?k?l®?jT?k­?ji?iÞ?gü?m@?mµ?iä?g2?kà?p|?t¡?x?x¬?yó?zõ?}?ƒ+??†÷?ƒu??{˜?~?|á??z‚?{?z8?{?~­?qe?uŸ?v|?x˜?s|?t ?r~?sñ?wz?u ?s?n ?n1?sò?vp?y+?y+?y?zm?zÃ?|ø?} ?|?^?yú?w”?t°?sb?wá?y?xï?z-?yØ?yK?yL?zR?z»?xê?|^?{#?yã?yc?w³?y€?zq?zL?{R?z³?z:?x%?wí?wé?wÌ?v5?wH?w­?xÔ?y?{ ?}È?|á?~)?~"?|ñ?z_?wæ?vº?v¾?uØ?tS?v%?wé?xé?zH?|ß?|Ï??€?~H?}|?za?wŒ?uÞ?rY?sÛ?r?t"?w£?wñ?{‡?||??m?€ ?B?~ì?{.?y?uR?sv?rÖ?s?sß?vç?y?zl?}‚?~?€ ?€ì?Ÿ?~?zµ?y)?uZ?qþ?rå?s$?u²?w ?xH?z™?}U?}?~ý??4?}I?z?y6?u?tG?s?vF?uC?xy?xÓ?yÕ?zâ?}™?zþ?}?~?};?{Ä?wG?t?v¢?tÃ?z@?y8?{Ê?y‹?yŽ?yÏ?zW?{A?{5?| ?{î?z ?xî?w|?xo?yP?|ù?‚?~r?{†?vç?t:?rD?wF?u÷?wO?yT?yF?y?xÍ?|[?|ß?†,?ˆ€?‡H?‚»?„p?…?O?Z?Ž/?ް?¡?Š ?Š}?Š?Œ?„D?‹J?Š\?†ˆ?Œ7?†X?jû?q?v?xL?z?|W?Ä?‡“?R?ê?D?A?Õ?,?9?š?Ž?t?M?‹X?‹4?‹Ù?‹?„R?ƒó?‹,?‹0?„¬?ŠC?k\?t?v:?yŠ?z*?~¨?a?Š&?„?‰?‹S?Œs?Ž€?Œ0?g?Н?6?‹T?>?„þ?„ú?‰?Š?„L?…“?…õ?ƒS?‡R?…@?rq?t?vê?xñ?z:?}ª? ?ƒé?€j?ƒû?ƒ¸?ƒq?„§?‡«?ƒî?…q?„G?€b?‡?…1?t4?v‘?x¸?y“?y¿?z?}¥?î?€û?i??xÙ?xe?y`?y±?yš?yÿ?{ý?~Ò?x£?{=?{X?yu?y]?w5?xñ?y?z½?y´?{?z?x÷?x_?w8?x¯?v ?w,?y?yŽ?yt?zE?|/?zÐ?}K?|r?{Í?z=?xÜ?w÷?v?wu?t?v˜?wQ?xü?y¶?|!?}”?}ƒ?~?~ü?}?z\?xj?vW?tƒ?u›?tx?v$?wº?x-?z?{¿?|µ?~?~§?~8?|?z`?xô?vh?t"?t?sÿ?tÿ?w¢?x3?y«?{3?}q?~“?~¨?~è?}Ó?{Ã?w?v1?t ?t?un?vÐ?w¶?xG?zW?{i?}f?}?sr?tb?y€?w©?sí?q'?n!?pÃ?mº?m=?na?l3?mÊ?k9?le?m?oï?p>?u–?wK?yB?y:?{‚?}?~?Œÿ?Ž?‹l?Š ?ŽŠ?‹¿?‰Š?‹”?ŽÓ?Œõ?Œh?Š»?†õ?ˆä?„ß?‡ì?‰?‰?Šs?„û?…þ?zâ?€?{%?€?T?Œò??{?|?ŠB?†?‰&?‰~?‹;?‰Š?†6?ŠW?„¾?Šª?gÖ?s\?w/?xR?{¥?~}??ˆo?ŠL?‡?‘ ?×?@?Þ?}??ð?r?Ž?„¢?Šº?Š¿?‡‚?ˆ?Œ?…F?‹[?ƒ¦?Šà?h\?t?w?y,?{Ì?{ï?‚6?…Ý?…Ü?Š´?Šˆ?Ž[?Šü?Œ|?Œ“?ŒF?Še?‹M??Šø?‰t?‡?‰Ü?‰X?Š?ƒÎ?ˆ?|z?|(?z¿?xâ?wG?wX?uÓ?vu?w?wÃ?y{?z(?{?|.?}“?~?}n?}K?{™?wb?u£?u™?t~?t’?u&?v£?xÜ?zR?{{?|Ð?~u?9?~!?}Ÿ?{—?yR?vÓ?t€?tc?tð?u*?vº?xè?{?{³?}ö?~ª?~Ý?~•?|õ?y¬?x”?v:?tk?tl?v?vT?w?y ?z-?{>?|O?~j?~ñ?}ú?{ß?{+?xž?vh?u(?tÿ?v?wÎ?xù?y ?yÑ?zž?z(?{“?{©?|¨?{?z7?y?wA?v??w€?x ?} ?{M?zì?yQ?w?y[?w¾?{>?yï?zK?yp?xë?w ?x6?xÛ?P?{¸?€‹?zð?xF?vç?t¨?s”?x¤?zK?z¦?zc?yi?xž?x·?zo?~C?ƒý?‡?Ä?›?}(??~…?}#?}Â?~¬?~ó?z?xæ?s»?vŽ?p°?r??r²?vÞ?r?t?w3?p1?o[?s?sN?v³?xà?y?yk?z›?}²?~¨?Š?Š$?ˆ¥?„/?ƒs?‰r?„«?†«?ˆˆ?…o?†ç?„:?€Ï?‚K?{Ñ?€œ?‚ö?}ó?§?‚?€í?xP?yd?w³?tx?oÅ?tþ?rT?tî?r&?s?m?jû?nª?o?kw?n­?h?h ?l ?lf?n·?rY?u³?w2?x¼?z…?|?}Î?ð?Š?Šž?Œà?Œü?Žë?ŽJ?Š%?‚?ŒU?‹»?‹4?„ó?ˆÍ?„r?ˆF?ˆß?ˆ“?„‹?ˆ)?ƒ[?†ê?lÍ?t†?v&?y2?zÛ?{9?€0?…‡?Š?Œí?Â?‰?ŒL?‰“?‰ÿ?c?ŒM?‹B?‹U?Š.?ˆ?ƒ$?ŠU?‰Ê?‡T?„?…¹?‰9?ƒ‰?qù?uƒ?x(?y,?z×?|;?~µ?ƒµ?Œ?Ф?‡î?‡©?‡¨?‡ô?‰(?…z?„æ?‰Á?‚ð?…?q³?tâ?xH?y?yŠ?|?|Ç?€u?}?‡Ä?‚š?sl?w‘?z?y2?y»?y–?|r?|²?~×??|s?wË?{z?z‰?y­?yŒ?y°?xÏ?zÞ?yQ?z¢?z_?yù?x†?xI?{?yG?|T?{á?zð?z­?yŽ?wz?w#?xÖ?u.?w…?xè?yD?yw?y|?|3?}·?} ?~¼?}g?zx?xz?v4?v?t,?s%?uu?vÊ?y{?z@?|/?~S?~?~Ù?€?}Ø?y¨?wD?s?rˆ?s ?r¬?u;?u¡?yÉ?{Ž?}d?}õ?£?€À?‘?~$?zÔ?w;?t¿?t?rñ?qÄ?t¢?v?y2?zx?~;?·?£?$?¸?}|?zT?wµ?t“?r?r¢?s7?uW?v?yv?zž?|%?¹?Ž?~r?€v?‡?zö?x4?ué?sÊ?sÅ?tÉ?v?xó?y?y¯?{+?{,?|?}2?|B?}O?y¶?w–?v?u¾?vi?vy?z ?|!?y­?x{?wü?x?xà?{†?|?{o?yú?wÕ?w]?w²?x‡?z ?y.?zÉ?yÑ?y!?xT?tå?yX?zÔ?{ª?zÙ?z ?yh?wä?v ?z ?{Ã?€ü?|œ?z-?vy?nä?rž?x]?wþ?yi?z0?z?y…?y=?x?yb?‚Ç?}?„U?yL?} ?|?y»?zX?3?yà?|Ö?y±?~`?t?rü?wç?x?tÖ?s8?s_?s=?xå?tº?nU?oy?tÏ?sÃ?x¸?x ?y?yÆ?zy?{„?~È?‚2?‰Ï?„(?‚4?…w?„d?‚Ä?‚‰?…?‚Î?ƒý?…Ê?tŸ?wQ?wº?yD?z7?{E?|?N?…¼?‰]?†3?… ?‡?ƒì?„z?‚®?†ž?…?„_?ƒd?u?w´?wJ?x”?z?zR?~C?‚Í?‚ˆ?ƒr?‚Ó?t¹?w?xE?y¢?y«?zÅ?{º?{½?}?ª?z??y?zf?y÷?yw?yŸ?yJ?|¬?x³?{?|?zM?|f?z°?zÌ?wì?w‚?w•?vá?vò?vù?xy?y?yF?z{?{?)?}p?ô?~L?zC?wÌ?v,?sª?u‹?sF?u±?x$?x—?yb?zÓ?~³?€±?‚³?‚Á?}‡?|@?xŒ?sm?oc?rt?rO?s«?t‚?wž?{ ?~?~u?n?‚î?„?ã?z?xÈ?q!?pƒ?o?pÐ?r~?u0?x‘?{½?|Õ?î?…D?„-?…ë?i?}¼?xé?o¦?p0?o?ql?r1?tc?w—?}Y?}Ô??‚É?†?…—?‚‘?}š?vZ?rä?lé?lë?n÷?p‘?uV?yÁ?|Î?~î?~¼?„·?…?ã?œ?|:?y3?u'?np?p?px?v ?vE?x“?{W?{Z?}q?ÿ?€Þ?€æ?~ ?z ?tØ?s½?r_?p?u~?y“?wn?y¤?z†?y?|¸?zì?~?}¦?|¾?z©?y?vë?uÔ?u¾?t?xå?wÒ?xÈ?yN?y‡?}Æ?|C?}á?~Å?p?{Œ?y?vÞ?t6?uY?x°?zñ?a?yº?wÛ?w7?x«?z©?{?{n?zñ?zV?x?x£?u­?v?{0?}R?.?v$?tZ?uY?w)?z?z&?zÅ?yÀ?y5?xË?y,?zR?‚p?€ó?‚?vÃ?y?yV?yt?yÊ?yA?zw?z·?~¶?’?c?w?yf?yÅ?y—?ym?z?{?{?}•?€&?}Ð?w ?z ?zi?y£?xá?xè?x§?y?{¦?}Ð?{m?|K?|L?zé?yô?xe?x?vŠ?xš?wa?w ?yÅ?~k?|à?~£?|E?x?t³?v=?rñ?q8?v³?vÒ?x6?z%?}+?Ò?‚y?/?¯?€Ø?zŠ?uâ?u?qÎ?r×?oà?r¬?s“?v¸?yü?~c?Ž?‚â?†)?ƒŸ?ƒB?zï?rÖ?pž?né?j?gH?p*?qÄ?x%?zø?]?ƒú?…´?Ч?†Ñ?‹Ñ?€9?v^?lt?jß?i5?k?lK?r(?v?|í?ƒ'?ƒ“?‡¤?Šð?ˆ€?‡Z?‚ß?pÐ?kX?eÆ?gè?fh?mß?t?wP?}ý?~ ?„—?„ö?Ž?‘®?ޝ?¥?s¼?ka?iñ?gø?m?kø?s÷?xb?} ?~v?„ ?…°?†?…È?Œz?s?oñ?nj?hI?kþ?pU?r?s\?yj?{!?}×?ƒ–?„â?†‘?‡È?†‰?{?sß?n»?m?q?r=?tÄ?x@?yr?z&?{¢?}ç?¬?ƒö?ƒ‘?~?|Þ?ty?pq?rS?oÂ?q"?ué?w¾?x?zÑ?|™?}¸?€s?Õ?€u?€m?zÕ?wÊ?sŸ?s¯?o¯?tø?x?y?y¢?y?yÉ?{Ô?|,?} ?~B?~R?zÂ?xx?w?uØ?r¥?w ?y•?zS?y:?yg?v¿?{?{s?z)?{³?{W?z¾?x?wD?uØ?w?y?}‹?zÕ?yÊ?xî?wÊ?x6?yó?zO?{–?z1?z?yr?wÙ?xN?yë?x¿?}ø?zr?yü?xT?wÉ?x¿?y¬?z-?z+?z¹?y²?xõ?wü?yw?z‹?x4?{?zò?{?x#?xÍ?x[?zý?}?{Ì?|(?zÜ?y ?w‹?w\?x®?t]?xL?y?yÿ?x¶?{\?zÇ?{È?2?} ?}#?z†?x³?v?v ?u‚?qÀ?v?v­?x™?xî?|C?|õ?ƒ¤?ƒÃ?€?zÍ?xt?tI?sw?sÅ?nu?rD?q0?x†?y¯?|v??‡Ç?k±?n?f~?m™?u?x–?yÇ?”?Š?‰:?d"?_¶?f¥?gU?s{?xL?~?{?Š|?’€?Yñ?d¬?_^?kš?qU?w ?y®?W?…â?‹?Zß?]¦?g€?d¬?o ?vÌ?|’?ƒ5?ˆ&?á?Oœ?Y?b£?j?n’?w°?}¢?e?…@?Œv?W)?_D?h?o‡?t?xp?|x?Q?…ÿ?Æ?‰?Š?‹¢?{?wŽ?no?gu?jß?mŠ?q[?tž?y'?{Y?}Ú?‚z?ƒ˜?ƒ;?†ó?ƒm?ÿ?r?q+?oÐ?of?p©?rM?té?xH?z÷?|Ë?ƒr?‚?‡¥?ƒ?‚7?zÙ?u?p)?qB?mˆ?qC?ux?w¢?wþ?yž?|)?Y?Ò??ƒQ?€"?zì?wÖ?uQ?q×?qp?ub?wg?x?xî?z?{(?}?|r???| ?zš?y)?v\?tÈ?uÕ?w·?w0?y?x×?y°?zÎ?yå?|?|Ù?}L?|%?z{?y„?w§?w‡?w?v¢?x˜?yé?y?y6?yÌ?{"?|ò?{Þ?|{?|Þ?zº?x£?x ?v?v?vi?wò?ye?xç?yk?{;?z‹?{Í???|ö?yÀ?y1?vú?uÿ?v“?tb?tV?v‡?xÂ?zù?{q?|¸?~?‚t?I?~Ò?|C?x¿?sf?tT?q›?nŠ?r[?si?x ?zÌ?~Å?Ä?…ö?Š5?ƒþ?„É?{ð?rü?nq?oÁ?må?iË?o>?pS?wÈ?yø?}Å?†à?‰¹?eà?ej?g]?d’?q,?tÊ?{ ?ƒh?†ì?—–?^_?`?^¥?c#?k…?v¾?{>?…I???–Ê?Oo?O‡?Wã?dÛ?n?tÉ?{o?ƒñ?‰2?šË?-?:(?Uò?`«?oy?qê?|Õ?‡³?–&?”'?1>?JŽ?^?i?lY?yx?€?ˆP?’­?‘?C“?W'?`A?fô?o­?vq?{9?ƒ?Yf?mŠ?vŠ? ?†Ó?—?›Û?LL?L0?L;?L7=?LD.?L01?L=—?LBd?L7J?L3®?LD|?LE?7Ü?X#?c±?ol?u ?~?ƒ_?‘œ?‘“?§Š?±=?LÈ ?LÔø?LË@?LÐ ?LÕ‹?LÙŠ?LÚ ?LÓ?LÃî?Lì|?LÆâ?LÏ;?LÖX?L½?L¤?LÈë?LÄí?L¹Æ?L¼?L³?L·Ø?L»2?L®Ê?L¿>?L¾-?L¹–?LɃ?L–§?LŠd?LÞ?L‚³?L‡²?Lzà?Ll?Lì?L¡=?LŠ?LšÚ?L™G?Lš2?L”Š?L‚Ç?LvÊ?Ld?LPc?Lrw?LZ?Lr(?LR ?LmG?LvŒ?LjÜ?Lpò?LrÏ?D?Ni?du?jj?pÌ?y˜?{5?†µ?ˆ”??£Ü?˜q?Ã?y¸?u£?_±?Q;?_~?lô?o?t?xi?zÉ?€Õ??Š$?‹Š?–?ަ?€¶?s#?m}?g!?iâ?m„?r²?u@?v^?z?~?¦?ò?‚Ö?‚?€‘?~À?s ?r·?oh?n?rB?t}?w ?w6?yµ?|É?W?]?N?€â?}5?|¡?xÆ?uj?s”?rÕ?tÎ?uD?v°?yk?yë?|=?}P?~Ì?~ê?}É?|s?{È?wc?uö?t§?tT?tÖ?v;?wò?y?y°?{Ž?|Æ?~»?~Í?~9?}q?z?yv?v®?t¬?t?s½?tÍ?wL?x/?z€?|£?~a?€ë?Â?i?}Õ?zo?vù?uR?sn?r™?pã?rÿ?tu?y?zž?|s?ƒ?ƒ?†`?ƒÅ?$?{w?vp?q2?oú?p:?m…?nŸ?t=?y‡?{Ï?ƒÍ?‚Ú?ˆ±?Ž™?"?ŠÚ?‚I?qa?o‘?hš?h7?^?hð?nA?wU?zN?€Ã?Š×?‹?Z?S÷?^š?_g?nÁ?s«?}g?ƒ™?Žž?™2?47?@»?Cø?]'?oJ?së?|Å?ŠI?‘I?²m?LC?LAE?LIè?L:?L;1?LG?L6Ç?L0g?L> ?LPG?LI?#7?M÷?P‘?a^?ob?|?†l?’B?ª»?L ›?L??L(ô?L7ê?L †?L.†?Kÿ~?L¶?L*f?L ?L?L %?L'è?LO?L,«?˜?E?WA?fý?t´?}ª?‡?¡´?»[?L-à?L?L¡?Ls?L µ?LL?L(?L\?L(ð?L “?Kþ‰?L ”?L5C?L(?LŽ?|?Nè?^?kå?w?‚‰?ŒÝ?šé?±b?LEþ?L/?L>=?LOÎ?L.ß?LGê?LFÞ?LO¥?LH ?L8Å?LL?:˜?`Ä?j?l¢?t4?€Œ?ˆ`?˜g?–{?¦x?«û?LØh?LÔŽ?LÙí?Lä)?LÞÉ?LÒü?LÑê?LÓ?LÍB?Là‰?Lé’?LÖV?Lª?L½?Lɨ?LÁ ?L®ü?L­?L¾+?L½/?L¨®?Lº¤?L®É?LªA?LÃv?LÁÐ?LÆó?Lz?Luø?LžÇ?L‘t?L •?L ?LŒM?L¿?Lˆ›?L‚6?LŠò?Lœ\?L˜?L„ú?Lƒh?Lu?Ltu?L}¼?L[¦?L`?LqÊ?LeV?Lpê?LR…?Lr[?Lq²?L~y?KV?Mç?Y¨?gœ?q?y?{i?„¡?„ó?Žý?•?¦É?™¸?|6?k|?`²?Q ?[E?n!?l—?t«?y_?}‹?_?ƒz?‡J?Ò?Y??}R?o¢?iZ?jJ?lt?p'?qn?uó?w¦?z?~¾?}?‚‹?7?…?‚¤?}ë?w§?t²?o¬?o,?uX?u?vè?y}?zW?{–?~¤?€o?ð?„?}Ü?zt?xØ?u?t?r/?u¿?v›?xg?y_?y¬?{ì?}3?}?~¿?}j?{ç?{a?w^?uÇ?uk?tÞ?u0?w–?xÊ?xå?z5?{Œ?|H?|?~ ?~d?|m?zO?xf?vY?tÙ?tç?s2?uI?w›?x’?zâ?{R?~e?}ö?Ý?}î?}Ž?|?xµ?uÃ?qÎ?s6?r¥?t?vƒ?xÏ?yä?|P?\?¨?„d?…3?€§?{ï?uë?s?o3?qš?kå?o=?pk?y/?{S?€?ƒ1?ƒm?®?‹?†ß?z?xÙ?j9?g¥?l/?an?mf?p¥?vÑ?{?€÷?†?‘õ?Z‡?YÌ?[1?gñ?l­?s¼?€Í?f?a?—y?Fã?Jã?I?[+?hè?xÅ?zý?ƒ?”W?ò?'^?C®?O¿?aà?hm?r”?|©?‡?u?|Ô?„®?ޤ?  ?º9?©ï?LÒ£?Lê?LÒì?LÐÜ?LÏ$?LÙ ?LÑU?LÌ_?Lͦ?LÑ-?LÛX?LÐã?L¶"?L¨8?Lºý?L½J?L¡·?L¬Z?L¸ù?L³?L¤ð?LÖØ?L¶²?L¡Ì?LÕ–?L³ý?LÕ©?Ly!?L|Í?Lœ÷?L‹ƒ?L†%?L˜©?LŽL?L„e?L˜?LxŸ?L™¾?LŽh?L¡§?LT?LœÓ?Lpæ?Lb‡?LpÆ?Li°?Ls·?Lr‚?L[æ?L^ì?Lhì?L^·?Lnô?Lo?GX?F?\U?aK?h?s˜?y‡?ˆ?‰M?–¿?¤¦?Á?Lãª?LÙ?Lèƒ?LÑ8?L×?LÒ¸?LÓ¥?LÞ­?Lâ§?LÜŽ?LÕ-?LÌ?L¼4?L¦ß?LÁë?L¿§?L¹‚?L»h?LÆð?LÙ?L©?L¼Í?LѲ?L¾´?LÇ ?L¹i?LÃÙ?LŸr?L”¾?L“¸?L},?LŸÓ?Lˆ¦?LŠÏ?LŠA?Lë?L’€?L‹‰?Ls ?L|¬?L›?0?‹…?z?nÄ?l?b?e‡?m©?o¦?t6?yu?yÈ?~5?€]?…?‰P?…ï?ˆï?}!?rl?oõ?o?l»?‹??’ ?’S?”?‡[?‹6?“Þ?›é?˜ ?›d?‡È?Œ)?‘Z?”??LV?L)s?LkN?L3V?Lùq?LMj?LËT?x—?|D?}ë?}J?©?zÊ?w?s„?q?nO?mª?l?i9?o­?rb?y”?~·?~&?}¬?}+? ?{{?wy?uµ?p`?q;?ož?lB?p?pè?w,?yÿ?}¨?~"?£??}å?|ó?zg?wº?t ?sÄ?rŸ?s?q0?wA?x?y¹?z:?zì?zÞ?|?{a?zß?y7?x9?wí?uê?yŠ?wæ?vÿ?w1?x?xl?wÞ?vê?y5?wä?y?xI?y­?y’?{À?|‚?|q?|Ó?|k?yû?yê?z/?x?uÁ?v?uN?uq?uô?z[?}?}ÿ?~¬?~Ý?}??Y?}t?}:?væ?p5?sP?t?pÄ?xR?z×?€Ê?€??g?†r?…É?ƒL?e?~j?|s?w!?uh?vÃ?sì?uô?x¥?|û?z?ƒ¹?‰é?ˆ¨?R?„E?‚?‹w?¶?~r?x*?vl?uA?ui?sà?tG?w¦?{É?~y?ƒ¨?† ?ˆ½?‰?ˆÉ?„Ÿ?„j?Š™?ƒ0?†ê?‰?‡»?ˆë?ˆ?‹g?‰þ?‡Æ?Š?‡j?„?ˆ?ƒ—?„ ?…8?†‰?ˆ³?‹?‰ ?†§?…ú?‡Ù?…:?‡Ý?„a?„Ë?ƒ{?‡½?ƒü?…-?ˆÑ?†ù?… ?{®?z[?wÿ?v ?w)?u?u?x9?y¯?{%?}¶?|”?~d?|8?|}?zÞ?z&?€g?yä?zÈ?‚Ã?|„?{k?A?~??~¯?‚Ý?|†?|c?|²?ƒA?z?ƒE?‚¦?}£?‚ç?{®?zž?~E?€$?‚=?‚?|0?‚­?|?|:?y«?€?z¨?{@?‚Œ?€¾?{P?ñ?0? ?{R?{?ií?kÉ?o?n ?hå?jè?o?gó?hi?lP?ha?nž?o4?mœ?mÈ?g{?oÇ?k-?l¿?i›?hÈ?n?k…?j?m¿?k?k[?o\?j?n?m2?i7?nÆ?oi?vÝ?x?yf?xR?yH?xN?xˆ?wŸ?xN?x¶?xM?x‰?zÍ?zÉ?{¿?{r?z‘?zâ?xÎ?w9?vE?v6?t¦?t\?v7?u+?vr?z?z%?~„?~€?€0?~"?zJ?|C?w?Žf?Žð?ŒÊ?å??‹??Џ?ŠÚ?—?о? ?‹’?Œ?Z?Ž_?‹a?’?Œ²?‹V?‚¶?{Ø?y“?w?tþ?rö?s¨?u½?xp?{˜?/?‚Í?ƒÀ?ˆÞ?…%?…¸?ƒÔ?‡¹?†?‡‰?ˆœ?Š$?‡·?…?‰ ?ˆë?Šk?ˆŠ?‰ñ?‡U?‰„?Œ9?‰B?‡¢?„°?‰N?‹G?‰ã?ˆ¢?‰ƒ?г?…ˆ?‡ä?…Ü?ˆï?†?Š?ƒx?„ª?‡~?ç?|Š?y?w¥?v¬?v+?w?wW?wü?z±?z$?_?{‘?{(?~ö?}^?y¿?}¤?}`?}{?~\?‚o?|£?}™??ƒx?zÕ?ô?yÕ?|†?~ƒ?yD?xB?x?x?yz?x¶?yA?z~?wµ?x?ui?r?q–?s×?v ?wà?x ?v/?t?w”?w ?pi?tt?r·?qz?p,?s?x‚?w5?yp?yg?{8?{í?{D?z?z?yX?s›?q‡?k?jé?jõ?on?oñ?l&?nÛ?lª?i?nÁ?x.?z?|Ÿ?}J?}(?{C?{ä?yT?vó?oÔ?hÐ?wß?|-?¬?}÷?}Ï?K?{Æ?yÅ?u'?l—?mV?z&?~ï?€M?~Á?Ü?}ò?}Š?z·?wë?s°?pG?jH?n#?pü?w?wL?|?i?~{?€³?€?ï?},?{/?z‚?v¦?t?sÅ?r€?s|?x?yÿ?|?|í?Ï?™?@? ?}Ï?{â?zU?xÆ?wÆ?t ?vR?wÅ?y?z(?|8?|ù?}w?~?€@?~ž?|R?{C?yû?xÙ?xþ?x[?xP?xû?xñ?y?zs?z¡?{J?{`?{l?{¼?zí?z5?y ?yé?zÇ?yÕ?{c?y?yM?yS?x"?xö?wƒ?x?xR?xÐ?y?x¼?wë?z®?zå?{‰?{Ö?|?y;?xÎ?x-?vs?u8?t?rò?u?v?w1?y?{D?a?€Ø?€B?}¸?|x?zž?v—?u½?u­?tA?t?tº?v`?xœ?yi?~‘?„?‚%?„Þ?„a?€8?{ˆ?w‰?v?tø?sX?rø?qà?tó?x‹?yÁ?zÉ?æ?‚M?†Í?€u?û?{X?uy?t?r&?ps?nŒ?q?q­?wW?yy?‚?i?…è?…ú?„î?æ?x_?w?t~?r(?o?rû?s?sa?uS?}^?~ñ?h?ˆÔ?„.?†?„?…û?‚p?„?‡0?ˆ?†;?ˆ?Ÿ?‡.?‡0?€í?‚©?…0?þ?‚|?„ö?€Â?}E?y­?yf?u?w?u(?t?xX?w‡?xU?zm?{?z>?y’?yÆ?w7?uþ?t*?ox?ru?q?r?s¦?v™?uö?{J?{v?}È?|Š?| ?zt?ví?v[?s'?rq?q!?lC?q¹?sñ?r?xç?~?|?‚v?}R?€Û?yÆ?{?s?s|?nN?oñ?o?r&?qH?uO?s?|d?{?|D?#?{ˆ?vÐ?wï?r/?m›?k*?h§?l?jË?p„?qé?xJ?{m?€?~×?‚™?~?yë?wå?u9?rh?pª?n?k¯?qç?p-?qÑ?x?|°? ?¢?}+?zg?x'?tó?u?qž?rl?t^?r¾?tª?vÊ?z{?yø?yá?xU?yY?xà?yu?y[?u?x_?wõ?y¦?yK?ww?y»?{±?|?|™?z ?zc?zp?yþ?yç?vU?v7?{S?~!?k?~Ö?‚A?€L?«?}ˆ?z¡?t¸?vQ?€c?‚ ?‚-?ƒG?ˆ³?ƒF?·?~´?{5?z´?wV?€Ä?‚Õ?ŒS?‹_?‡ÿ?‰¦??ƒš?~?x??x?v?w÷?yò?}?€I?O?…M?Š?ˆ?Ь?Š/?ƒ¨?»?~Û?{?z?y™?x?y»?|?~ö?‚é?„U?†è?‹¨?‘H?‰Ý?ˆ¢?ú?~P?{1?zK?xþ?x†?z ?{d?}…?€9?Z?ƒí?„Ö?ŠÉ?†¤?„“?Þ?{õ?zg?yÊ?yH?y>?z'?z‰?zî?}Û?|­?}Ø?ë?zO???z~?xê?x%?y3?yS?yO?yº?y?x¼?uƒ?s?r&?tÃ?s?tÇ?u¢?wm?wP?v¿?y#?yª?yº?y€?w ?u¸?s?q~?i?k˜?lÉ?nÊ?p[?uç?t²?wù?xÄ?{?{ ?y®?uÞ?sÙ?na?m¶?eX?in?dø?j¡?mµ?t!?u?wø?|ø?}ü?|Y?{?w—?uÿ?p¦?j,?eÅ?k@?bR?i7?mp?qV?q?vó?yp?zÎ?yD?xÍ?vá?rÆ?pe?jB?gÐ?f?`?h?m³?n¸?s?w³?z1?{Ñ?{|?yž?v¤?u?qR?n?iÃ?l7?kf?k ?pe?nü?v?y?y¸?{?y£?zo?wE?v ?v#?pÙ?s}?mŸ?p6?pï?o—?tZ?xñ?yÅ?z?zX?z?z1?y ?x?yf?xe?w¨?uÝ?vò?ua?v€?wº?zS?yW?yF?y„?yH?y?yb?z3?|??z]?|Ï?~ø?}Ç?zë?{Ã?z?|Ý?xæ?w=?wA?wî?ye?y9?|*?I?~‹?‚Õ?‚?‚ü?ƒÏ?D?}?Y?z§?yM?v7?w?zÓ?{…?|Ý?„?ƒn?†™?„÷?†G?ˆ ?‚]?Ð?€ ?{è?zo?x’?x"?{?|&?ƒ6?†ô?†ë?“R?¿?è?‡K?•?€B?~+?}9?zb?wî?z±?}+?„‚?‰Ç?‡ ?‚¢?~ã?zO?xº?{f?{L?‚?‚?æ?ˆÌ?é?}ˆ?z—?z(?{?{¿?}×?h?‰Q?‚Ã?|ž?zI?z?yä?yù?yž?{p?}:?|?{¢?y?w¥?x¾?yY?y?y*?yG?y3?qk?rÂ?pÜ?v?v¦?x?xí?wÐ?v²?r ?l¡?o&?qð?tã?wü?xC?y#?w?t?pÉ?m‡?cf?`*?^¾?S¤?_&?f4?pM?oX?uÍ?wŸ?{]?xf?x4?t ?rƒ?n1?eþ?bÁ?Y*?Y ?e?eõ?mo?p^?s†?v¥?w¦?xX?v`?q¤?p¨?h˜?cw?\¡?Z:?U»?`Ã?hË?h×?pä?uÓ?x?wî?ym?vµ?t?p¯?n ?e?bh?eU?`?cž?h€?m?sÿ?w?xa?y‰?x5?wÜ?u’?u#?oc?l´?q3?g€?k²?nõ?n6?sú?y ?w?y?yU?y?y@?x¹?w‚?uÐ?u?w"?v ?xS?wœ?vñ?w„?z¯?z ?yZ?yÀ?yœ?y®?z?|7?{c?}‘?€å?~Ò??|á?{d?|?|?l?lŠ?rp?tÖ?v,?w…?t„?s?mG?fŠ?[;?DÏ?D•?DA?G´?VE?e ?pÇ?q®?t]?wk?vz?sÄ?rQ?lô?c×?`E?X`?OU?Pþ?QX?]&?d†?o)?pÍ?rÑ?tÿ?uì?rí?n4?mõ?d‹?Yq?C??OÖ?M?M¢?]±?d@?p¾?s§?té?wË?vô?uY?tŒ?no?m:?f|?a4?]Ç?UÐ?dd?aÆ?hµ?va?tü?wú?wï?xi?vÜ?u8?tÉ?pW?q ?n’?iÈ?mS?e*?iÙ?qõ?x¨?vÿ?x…?y ?xm?x ?w/?w€?t ?y'?u“?qU?x&?x?yD?wÛ?{ÿ?|%?ye?y‡?y—?y‚?ya?{Ÿ?}ð?~‡?€Œ?ÿ?€y?¯?|í?|º?}š?|à?|3?{I?zZ?{ó?~z??‚í?†ï?ŠA?…›?…¥?„Â?®?‚5?…l?‚*?}’?|¯?}H?}¶?ò?ƒ˜?н?C?•M?œq?—?”2?ŠJ?‰r?†ý?À?€r?|î?|Ò?€¿?‚À?ˆì?4?™­? ñ?³ó?Ö? Ý? 3?Ž2?Œ”?ï?ô?~?~+?~å?…?Š?¥ª?’­?ŒÂ?ê?~]?}ƒ?}·?~?„"?ˆ’?]?’P?…Ú?~Ë?|·?|x?|È??„?¥œ?’¹?}æ?{/?{y?z¦?{Ù?zG?{ô?~D>̵%>ÌÇ>ÌôQ>̪î>ÌÅ#>̨>̲›>Ì£å>Ììª>ÌÕ >ÌÚM>̳_?‡\?o?x?yV?xr?y˜?wL?xŠ?ví>Ìd>Ì¡š>Ìgÿ>ÌrZ>ÌqŽ>Ìp¬>ÌYV>Ìos>Ìt>ÌJˆ>Ìf>Ì~î?we?m¤?nã?v¡?v¼?vÄ?vD?qð?g?\ô?Wj?k?nò?sõ?u?tÉ?s•?nz?k¿?\Æ?J†?& >Ìí>Ì=>ËìÃ>Ëæ¸>Ëð8>ËÍÏ>Ëïš>ËêŽ>ËÂø>Ëüh>˨;>ËÓ­>ËüÄ>˺‰>Ë÷£>Ëî™>ËÓÅ>ËÑ‹>ËÓ>Ëâ:>ÌŒ>Ëžd>ËýÔ>Ëý£?2É?æ?]J?a^?p'?så?uÛ?vN?uö?sÉ?r ?kà?gö?aû?Z0?aµ?_^?X»?c?fC?p?vn?w?w{?wI?w ?sC?rC?r¿?h÷?n.?gÙ?j.?g?ká?qÖ?w7?wh?w¡?y†?y?wT?y$?xe?w[?up?q×?w'?uÑ?yy?y'?vA?|•?z!?yH?z0?y‡?z?|Š?|]?zì?v?{ç?¨?|>?‚?}Q?{??{þ?{U?zô?|Æ?{ˆ?~ê?~µ?€?ƒ¨?‰Ó?†B?‹ç?ß?‚±?€`?ˆÈ?€ä?}ó?}›?~?+?‚¡?‡Õ?‹ì?•F?—é?–à?’:?“Å?–?‰?†þ?ƒs?~›?}â?~`?…?‚¨?‰„?ŒÎ?Ÿ‹?°ê?«V?¾J?”?—ö?’H?‰©?ƒ}?‚@??~?½?‚??´Q?“?|?„³?Ø?~Ž?}ê?€s?…ï?‹€?­?™?j?é?&?}¤?|¬?~?‚?Œá>Í A>Í¢>Í-Á>Í ×>Íø>Í!>Ìã§>Í$Q>Ìó>Í b>ͯ?j?|Ô?z?|?{?zR?}c?{_?}d>ÌÞ{>ÌÊ‚>Ìà™>̧Ú>Ìñ»>ÌÞ>ÌÛU>Ìâ>̵8>̵’>ÌÛÔ>Ìÿ>Ì̇>ÌÏ\>ÌÙ6?x?qÀ?uý?vX?yÌ™™>ÌB1>Ì×>Ì”>Ì’">Ìo>Ìy¢>̉>Ì¡,>Ìc³>Ì’‡>Ìà>Ì¡>̘¯>̉Ö?b§?kb?r—?ud?uæ?v9?v6?sš?mŒ>Ì3l>ÌI>Ì;>ÌU®>ÌÄ>Ì å>Ì7+>Ìe>ÌYr>ÌL™>Ì!õ>Ì9?_^?ib?n|?sW?tÍ?tT?t8?lH?kc?b½?-Š>ËÇŠ>ËÎe>ËÂ]>ËÕ˜>˧¥>Ëør>Ëç‡>˲¾>ËÔX>ËÑÌ>ËÃ=>ËÑâ>ˉm>ËÌD>Ë­Š>ËqÀ>˨¶>Ëœ>Ë·–>Ëyr>Ë™6>ˤØ>ËV,>ËÉ9>Ë¥K>Ë¿c>Ë«i>˧C>ËT°>ˆŸ>ËR>Ë›ë>Ëgº>Ë«×>Ë“¤>ËÊÅ>ËÁ>Ë”Ú>ËvC>˰Å>Ëg>ˇj>Ëÿ>Ëã>Ëá>Ì ">ÌÀ>ËÆç>ËË>ËÒØ>Ì>ËÂ>Ìc>Ë×??7æ?Z ?fÕ?mÝ?rõ?tB?t\?q¢?p??eó?R?M?(?º? 9?=ô?9•?X£?mƒ?o–?sã?të?t“?r×?r©?i ?c.?R¿?Bè?<\?7Ñ?He?Z"?a(?k?rØ?tã?u ?uo?t¯?sµ?p_?mx?i\?bÝ?V›?Y¿?dt?bË?hÁ?qº?u©?w?v;?w›?v{?sÐ?sW?nü?kF?nÌö>Ìë·>Í5>Í,¶>ÌþÊ>ÌùJ>Ìü»>Ìþ >Í‘>Íå>Ìîê?–÷?~Ï?}e?{–?y¤?zX?zý?|Ð?yË>ÌÃu>̹é>Ìòû>Ìà^>ÌÔ>Ìáþ>Ìï8>ÌàM>ÌÏœ>̯ÿ>ÌòI>ÌÔñ>̬„>Ì­Õ>Ìàh?†®?sÒ?u?vj?xX?wj?w¦?v±?x¯>Ìv>̉\>Ì•Í>Ìl{>Ìw$>Ì=¯>Ì^ð>Ìm/>Ì…I>Ì_5>Ì6>ÌN°>Ìv×>̈µ>Ì„³?g?kú?r®?t?vŽ?uO?u²?r?e>ÌB >Ì2>Ì—>ÌIo>Ì5q>Ì0¨>Ì@ê>Ì8í>Ì(•>ÌVY>Ì:?X ?jŽ?p›?sZ?uL?tm?pê?m.?kº?V?:>Ëý³>ËõÏ>ËÖ;>ËÆ>ËóL>Ì x>ËÛL>Ì…>Ëù>˹õ>ËèÏ>ÌT>ËoÄ>Ës‘>Ë¢w>ËϤ>ËËé>Ë®”>ËŽÎ>Ëœ>Ë`å>ËŠð>Ëš >Ë|q>ËÁw>˨>Ë—ê>Ë^¿>ˈh>˯«>Ë‹V>Ë›÷>ˈ>Ë¥Ä>Ën*>ËË>Ë–d>ˈ¾>ËÁ>Ësd>Ë¢C>Ë|j>Ë«>Ì X>Ë×L>ËÑ>ËÜD>Ëìä>ËÅ>Ëìê>ËÆJ>Ì9>˪>ËÖ…?N?Xk?b—?l?r™?tX?tÎ?r?pÿ?i ?Z?I?:¥?-T?5N?BÔ?9?_|?nW?o5?tU?uh?uB?t3?qs?kÎ?]š?U‹?ZÊ?QÝ?BÐ?T:?Wþ?h&?nd?q«?u€?va?wD?t}?s‹?nï?mœ?d@?`.?[T?_ ?az?a?k¤?uÄ?u?…§?œ?€Ê?}3?}l?~ÿ?‚‚?ƒœ?“¯?œ¤?v?°Ñ?ªl?ªð?–£?Šî?‹?‚i?V?}þ?}ú?=?ƒ??¨"?˜*?‹œ?ƒe?5?~'?~o?}ì?ƒ`?‡w? Ü?™Æ?†}?9?~þ?}?}K?}û?€Â?†?­…?”û??{œ?yë?zÌÕä>Ì·G>Ì÷â>Ìêï>ÌÒm>ÌÍl>Ì£¶>ÌÕ>̲è>ÌÐ^>̱ç>Ìä‚?{f?wN?uk?vL?yR?yk?xJ?v²?t>Ìž>ÌmÕ>Ìr¤>̘ý>̈ç>Ìi>Ì|x>Ì{Û>̉Ì>Ìs>Ì{½>ÌP ?k?lô?pE?v-?v|?wE?té?ró?op?S`?c©?f ?q6?r(?u…?u]?t?n ?fd?cØ?>»?8s>Ì >Ì[>Ëñ6>ËѼ>Ëð>Ëäë>ÌS>Ëܪ>ËØô>ËÜæ>Ëøé>ËÍÊ>ÌÎ>Ë×Ü>Ìl>Ëô;>ËÌÚ>ËÍð>ËÌ>Ìš>ËÆƒ>Ëòñ>Ëß>Ëéü?8Ñ?IÈ?W?j?q ?s?u˜?tÜ?t5?n/?fü?_²?O£?>»?1%?(ð?<÷?P8?Zÿ?m€?se?vƒ?xø?v‹?v¶?u!?l•?mS?b‰?\?`I?YŠ?`_?dÍ?du?qÂ?w9?w-?wã?wÅ?uó?t‚?m…?k?fT?eâ?a?fl?dæ?k?kÑ?sI?w4?yA?yœ?y ?x?v³?u ?rî?qb?o?lÃ?lb?i¼?nŠ?v ?w¶?y.?y|?y ?xã?xC?x ?y?x¡?xH?uÞ?xÜ?rV?w*?sç?t„?z?z»?yØ?yô?yÚ?z.?y·?z‡?|›?|„?|G??zL?€ ?~Æ?z4?}Ï?|z?{ô?yÑ?yt?z“?{?}N?}©??‡Á?œ?ƒY?‚5?„Ï? ??~=?zO?{‹?z?{‹?|%?ˆ?†ó?‰j?ŒÇ?й?„?‹£?‰Ó?†Ñ?†À?•?}º?yÚ?z¯?{?€4?„¨?‹/?ŒB?šî?”?™ ?¡7?Ž@?†ú?„â?v?|?|—?{¥?}U?‚;?…?³?šd?˜¡?¨õ?³¿?©ç?’?‰z?…•??}+?|t?|?}›?€ô?„Ÿ?š?‘®?½ò?±Ý?¿W?²0?—b?–T?†&?V?|?{ò?{˜?}???‚u?‹?Ž?œU>ÍÎ>Ío>Í&ñ>Í1W>ÍÂ>Íë>ÌüL>Ìàë>Ìè!>ÍS>ÍË>ÍJ>Íp>Ìýï>Í->Í ô>Í83>Í Ì>ÍÏ>Í|>ͤ>ÍY>Í??˜¢?Ÿ?‘Ü?~³?yü?yþ?zÒ?z•?yÙ?|u?zw?z|?„·>ÌÝÚ>Ìï*>̯_>̽£>̨Ã>ÌÈa>Ì×®>Ì×¹>ÌÀÒ>̶Ù>ÌÕ½>ÌÎþ>̶V>̱õ>ÌÁµ>̺Ÿ>ÌÄÒ>Ìúû>̧ã>ÌÔ7>ÌÄQ>Ìóç>Ìþv>Ì·@>ÌЙ>ÌñZ>Ì¿‹>Ìà¯>Ì®%>̽À>ÌÚ^>Ìú>ÌÕ#>Ìíz>ÌÎí>Ì¥,>ÌëŒ>ÌÌ—>ÌÍÒ>̼’>Ì¿o>ÌØƒ>̬F>Ì»„>̳Æ>ÌÉ÷>ÌÅ}>̾1>Ìè>Ì©>ÌÃ>ÌÀU>Ì˾>ÌÂM?ƒÎ?€/?xt?vƒ?y?xx?yŒ?y?xÔ?t4?v;?fp>̈™>Ìk,>Ìša>Ì|¡>ÌT¶>Ìu³>Ì‹h>Ì`6>ÌŽ>Ìr?>ÌžA>Ìw>Ìxý>ÌFh>Ì}Ù>̉w>Ìad>Ì|½>Ì‘_>Ìx•>̘X>Ìu">Ì~T>ÌV¦>ÌK>Ì}§>Ìé>Ì–Æ>Ì[.>Ìd™>Ì Ž>Ìgä>Ì‘ >ÌX1>Ì]Ì>ÌÐ>Ì—«>ÌTõ>ÌDâ>Ì^!>Ìx¼>Ìv>Ìv`>̃F>Ì‚>Ìc.>ÌŽv>̆G>̃X>Ìw­>Ìn>̃Ú>Ìt€>Ì_…?vš?s{?q²?r±?ve?vÕ?w¿?u?uE?rç?ht?L˜?UÍ>Ì@¼>Ì,B>ÌUK>Ì8R>Ì,†>ÌB†>Ì&“>Ì?œ>ÌQ>Ì8)>ÌAÎ>̃>Ìâ>ÌEE>ÌË>Ì(0>Ìg.>Ì ø>Ì=z>Ì<ú>ÌZ%>Ì >ÌH™?L?a†?m­?jÚ?pÍ?vž?v ?vÛ?tx?s?qV?`?]K?MY??x?;Ý?R¦?_ ?eT?i?qÛ?v?w@?v£?t˜?rº?lj?i(?_¶?^?>[?JÎ?Yš?Z-?i?tA?t¯?yA?z‰?|S?wê?u ?r¡?ný?l?i:?`î?^?dÙ?m4?o?tï?w“?yÓ?zw?y|?xÇ?wW?ty?qG?q?m‚?kp?h*?k=?k?oR?wÇ?xÇ?y…?z[?{ì?yk?xÎ?w²?t±?r]?rK?n—?p2?s?sY?vZ?xà?z;?zË?yå?zÞ?yš?y›?y>?vZ?wè?v,?uw?y}?w“?wb?x‘?zÜ?yj?x£?y€?yˆ?yr?yã?{F?zí?}&?~ì?|Ë?~Ð?~?|ž?z)?zÏ?zä?xp?xí?xý?y‡?zn?|I?~?¡?€e?‚?‚ ?„P?~Ç?|?~I?{À?x­?vŽ?vk?wµ?|¼?B?…¯?…˜?ˆ£?ˆ?‡Æ?‹?…?ƒ$?€#?}~?{?xo?yé?zÅ?}Ñ?"?…ã?ˆÇ?@?q?‘q?.?‰??…?‚ß?~?yƒ?x‰?{?{×?|_?€„?‡*?‡Ö?“Œ?•Î?‘?”ƒ?ì?‰‘?„å?P?{®?y7?{q?}P?~T? ?Œ7?’ó?›d?•œ?˜­?šà?ˆW?„ó?~?{Ý?|‹?{0?{ ?{S?|Ú?h?…ú?ŒU?‰ð? »?ž„?Š“?‘Ù?„"?{S?zÏ?z?z>?zœ?z¤?zû?{­?yÅ?Ç?„D?„¶?…ê?z ?‚B?yÉ?w?x ?x‘?y ?y‡?x„?xÙ?vU?t†?qÅ?bñ?wï?u­?w\?o*?vf?sÛ?w?w¹?xü?y?wš?w[?tV?nì?i\?\^?^?b9?Xï?jÅ?mô?q(?s;?w¢?xÑ?y-?wK?sm?r©?jl?el?TÁ?W]?SV?a«?fØ?j3?n²?t?wC?yB?x]?x{?uc?q’?jƒ?fó?[?Sš?\D?_.?kÝ?nð?…3?„?|«?w?p´?‡¹?†Â?„·?ð?uš?nâ?‰è?’?’à?˜=?”<>Ëdï>Íe%>ËÁ>ÌÒ>̧ß>̲P>ËüE?x±?~d?}#?|n?}H?z·?vØ?sý?q1?n%?mñ?lt?i»?o–?r€?|k?i?å?‚ž?€ñ?„Ô?~ù?xê?wt?r…?rV?p½?n£?p¯?r^?x?}?ƒÓ?…Ó?Œ³?ˆV?„Ç?€Ì?~‰?xÖ?u°?tW?s?s9?rº?x?yj?}ù?‚?Š’?Œ?Ž?Œ#?…p?}Æ?zv?w=?u?tÎ?t¨?u‰?vþ?y?"?ˆ?Š!?î?ŽÀ?ˆ?† ?~K?zM?v¿?uS?t?tí?uH?w‹?y?}[?€N?…ø?ŠB?‡Í?„ ?‚?|y?w?vƒ?tt?sÌ?t”?t)?tÒ?wô?xÄ??ƒå?‡I?…ô?‡d?zÞ?{A?t?tÞ?qÃ?nø?oÕ?pÒ?rA?u9?w?{!?|ì?{Ò?€ ?~~?zÐ?uè?p¦?pÀ?jl?kÍ?g?mU?pý?u,?s“?x ?w†?z5?{4?x'?u…?r³?mó?nx?eè?`+?dÝ?gà?iº?mE?k×?p¤?uõ?x%?w·?uƒ?sc?o ?g·?b˜?ak?YZ?V?a?g]?i}?k6?qò?s'?uà?uŽ?qþ?oØ?m?` ?ZQ?X¡?J•?D?T¿?d?dž?g„?ou?rc?tÌ?sá?sÒ?n~?jk?dº?\·?Qò?E?Ha?S“?Pk?aM?l?pÆ?sˆ?u6?te?qí?nj?kˆ?bñ?[„?X»?KC?P ?N?N1?b¯?l¦?pÈ?t¦?uâ?u£?s¢?rœ?iÀ?i’?^?RÓ?SÕ?E1?[u?Rž?g³?lt?rÂ?v?wæ?wp?uq?s½?m??m~?`?Y?U`?^¥?\?cp?j5?u?vU?wt?yØ?z?{?v"?s?oÈ?jÁ?hw?dž?aã?e¦?jY?qT?xÇ?zu?°?æ?€¤?A?yÁ?w[?vƒ?s¹?pó?j½?m¦?mš?qÊ?uß?|?|l?„E?…š?‡ê?‚˜?}&?|?xp?sÏ?t‹?r?q”?sŽ?s&?wª?‚?†¼?ˆá?œ?ŽÃ?}?ö?Œ‘?Œ!?s?È?ŒÏ?Œf?‹¶?{?Šš?Š8?Œp?‹R?‹å?Œ?ŠT?Žú?ˆK?â?}1?{œ?uô?vµ?u@?sG?uÀ?xÍ?{É?}m?‰4?+?‹r?‹?Ž‘?B?‹Ê?‹¼?ж?D?Œ{?[?Û?Œ&?Ž$?w?Œ7?Ž4?‘?‘>?Ž?å?ç?6?°?ŽK?Žß?‘$?^??ó?ô?Šƒ?Œ ?Œ¥??‹A?Žp?Ž—?Œû?Œâ?…b?}Ç?z¡?xÎ?w!?uð?u™?vÄ?w$?y‰?€h?Š?Šª?;?ŒD?¯?ŠÑ?Ž7?ŒË??ŒÁ?>?ŽB?Ê??Œç?Ž?“?à?ï?÷?ø? ?*??ŽÞ?à?@?Ž=?/?ŽÑ??‹¸?Š?‹ì?˜?Ž1?‹(?Œm?ŒÐ?ŒÒ?Šø?ˆ?€Á?z¢?wÇ?w?v?v¬?uô?w˜?{??}(?ƒv?ˆ¯?Ç?Šf?\?Ž¡?ޝ?Šc?Ž^?‹è?‹Y?1?Œ²?Œ7?Ž??ŽG?i?‰?‚Á?ƒˆ?‡4?€ ?†t?†û?€Æ?€?|˜?x°?wS?tq?v?vN?v¹?uµ?w—?y?zü?Ó?‚ý?Û?~ß?{H?wÇ?v?uÉ?sé?uC?qQ?r÷?uH?vL?vÄ?z‰?{=?}R?zè?{S?y+?w9?u6?rð?sÌ?rÉ?n¡?qí?sÁ?uI?t?wœ?w?xž?y•?wÀ?tÝ?t™?t?pû?jT?m®?nF?qw?sK?t?}u?É?{ó?~X?{Ä?{?|t?z? ?{?Õ?€?zå?{š?}h?yð?yS?x§?x~?wã?y?yp?yž?z?uê?x±?tM?p?q9?w?rn?s]?sð?w¦?x¼?x>?x?xz?xÇ?wC?y?w*?x?wg?q,?rE?sY?tí?vx?xh?x§?x+?xÕ?yd?y9?x?y`?x}?tÓ?t,?wŸ?v ?r?~“?Ä?~À?€?ƒ·?„?€?}«?{®?y–?vú?u}?tN?v]?tþ?zt?0?€?ƒ?„×?…³?ƒ?ƒ?}9?zÁ?qS?r7?l‹?h3?mi?oŒ?zb?{Ú?~?.?ƒÌ?€?€??µ?}w?tá?qk?i?hž?gÇ?fá?df?f†?f??fŸ?f]?g¾?e}?h`?e'?fµ?fo?cð?eH?f-?g*?cã?e…?gQ?n ?v¤?w ?}‰?|ª?~®?~ò?}´?|H?wä?s¥?lÑ?hè?e{?iì?há?f?fV?gH?f/?e"?gÓ?yH?{Ô?|Z?}é?}?|¦?{ð?w¨?sÑ?nq?eÄ?gt?gu?gÞ?hÛ?eB?fœ?e¨?h²?gó?xÓ?|N?|7?}"?}°?}—?zd?w@?rß?m>?kc?xþ?{¢?}7?~ ??}~?{Ð?zÝ?xe?tê?}Z?}¨??€Ê?€y?j?x?z%?|ì?xƒ?t~?}y?æ?Ï?ƒö?…B?‚E?N?€n?}ì?|¤?y’?wÕ?xÔ?z`?zÜ? ?[?‡?‹“?‰Ø?Œc?‰ú?‡Ð?€X?Œ?}d?{?{‹?y?z`?~I?‚l?ˆM?Ð?Н?• ?‹ú?ŒÄ?{?„O?£?»?~×?|Ï?{Î?}Í?~Ž?ƒ?†•?Žk?µ?’î?–7?•U?‰Ü?‹ž?„(?€“?~~?}”?}†?}?³?†Î?ˆË?“W?–¢?’ò?’ó?–Ú?.?‡ñ?‚y?€Â?~ù?}ý?~k?~!?€×?ƒÍ?¿?’Ê?’??“?•?–®?•1?ŠÅ?„¡?‚'?}Œ?|¹?}í? ??…„?‰Á?‹d?”N?™?žG?9?”å?†•?„z?|ð?|2?z²?{²?{«?}‰?‚¦?ƒâ?‡’?‹?Ô?“T?‹½?ˆ`?ˆ?~?xÖ?{?vÓ?yô?x&?|$?|-?‚¸?ƒ?ˆÉ?ˆ›?ˆK?„?…?€¤?p?xa?xb?w ?t£?u»?|‹?{Ø?ž?„‰?‰;?‹Õ?‡?†æ?ƒÒ?½?yL?sf?oU?qe?m¥?ns?u9?yj?}?~Ç?W?ƒ?u’?m¯?gå?y?|÷?~l?~¶?~Ü?}«?zü?z‘?w??oH?j±?zF?~?s?Ó??Ý?~„?{~?y#?rò?rž?~?€‚?¢?‚À?†/?º?|?}«?{û?zÁ?uq?€?ƒ?Œ„?Šý?‰.?ˆ!?Y?ƒÔ?€€?{›?zŸ?y?~R?€ ?„»?‡ ?‰á?Š.?ˆE?ƒ+?‚ª?{ü?wö?s¸?p»?pç?uC?wÕ?zÊ?~?ƒs?‰q?¦ ?”“?ŽR?ƒR?€–?~z?~r?o?‚e?‰ ? ³?’?‡ü?~ý?}¦?{q?{ø?|é?€h?‡Š?Š?š?§¨?ž@?®‹?œ~?›N?ˆ:?„>?~†?{—?x ?yù?{?}¯?€?„œ?Žð?–›?š°?šg?Ž2?Œ'?…9?|û?zó?z?wf?wa?xÒ?|é?|V?‚æ?….?–?‡¥?‹Š?†?ƒ‹?€Å?{½?x]?v@?vœ?t€?wá?z?|c?}æ?Ö?†"?ó?„ÿ?‚[?€'?€X?zL?vî?u“?s@?uk?vI?xŸ?yÏ?}È?~¡?~î?€Ú?`?[?1?{L?y*?wÂ?s»?ró?tŒ?v%?uR?x1?zW?|?}©?~ ?~v?}Ö?|þ?zÎ?y?u?p¯?sä?sÝ?tÂ?v—?y\?yã?|?}«?~Û?~®?~Q?|\?{n?xy?v™?s7?tm?sc?v@?v?x‘?|ù?~@?~z?n?-?~¸?}?z£?z¥?yN?wf?t&?w?w?y?zÌ?~”?€˜?„±?†l?‚ö?€‰?å?~Q?ï?{7?z¶?xb?w??z‡?zù?G?‚Z?ƒ?ˆÇ?‹?H?Œ?„?Ç?‚]?}{?}5?zE?y™?{Q?û?d? ?Š?ƒ3?Â?}A?}_?|?~J?€Á?Š#?—&?‰ˆ?ˆI?ƒä?â?}´?}¡?€?ƒ¸?‡£?«¨?—‹?Žø?ƒ5?~ï?~R?~Ä?r?‚{?Žx?Â? ê?‘B?„³?€?~t?}ï?®?† ?ˆ€?»?”J?‰Ý?‚?€Ž?}Ù?}²?€C?ƒ7?†Q?§3?”ª?††?€Ö?~?|a?}(?}1?€;?‚e?ˆ}?—¸?¸ô?¬ž?¯ï?­5?œ ?Šv?Œ?}Ö?|w?zû?{#?|é?|N?€ø?Šœ?Œ8?˜ô?Ÿz?ž6?™²?޵?†å?{ç?{€?zí?z&?xØ?zH?|v?{’?~?ƒ‘?‹,?†9?ƒ0?‹ß?„,?€Q?zÇ?y}?y)?vr?w½?xà?y6?{Ì?}-?~(?­?±?…?}?€­?}Ð?x±?xø?v?w&?uª?w²?xÆ?yŒ?zÝ?}?}Í?~»?~é?~R?}ù?zÓ?xÎ?xÒ?u²?t½?w?w–?x¼?xÖ?z×?{Ë?||?}·?}A?|Ë?|?z}?yÅ?xÈ?w?tY?u£?u8?x(?yF?z™?{ü?|J?}±?|I?}?|'?z¯?y1?wé?x?v?u˜?v÷?yÌÿh>Ìîä>Íž>ÍØ>Í>Í9>ÍÌ>Í!Â>ÍØ>Í(Ä>Í#>Íe?‘^?†_?ƒ÷?}P?}—?|ò?}Ã?„Ò?„ >Ím>Í !>Ìïð>Ìû£>Ív>Í0>ͺ>Í%ø>Í >Í °>Í*€>Í ?”?„þ?W?}Æ?|Y?|“?}¢?ƒº?‡Ì?Ÿé?×?‚??}¦?{P?{ó?|,?~ò?ƒä?„l?”?—X>Ìû¤>Í h>Ìþ>Ìù>Ì÷­>Í+6>Í2#>Ͷ>Í>Ìì>Í!;>ÍÂ>ÍÕ>Ìö>ÍÕ>Í}>Ìâ™>ÌîÊ>Ìò>Ìøv>Ìö×>Í!=>Íý>Íi?žŸ?–x?ˆ|?’?}`?z?zW?zW?zw?~?€}?ƒd?ˆ"?Œ?£?‘‘?–??‚ð?zÑ?y´?yÕ?yy?yL?y¤?{j?zî?| ?{^?‚÷?„P?€`?} ?~?{b?yå?yA?y(?xž?xˆ?y‚?yÕ?z±?zä?y¿?z?|?|J?|ú?zÒ?yü?zH?x¹?x#?xn?x?x‚?y?z-?yè?{é?|?|}?yº?zä?z?z”?yD?xû?x?yU?xæ?yu?y ?yž?z:?yñ?{Æ?{»?z¦?zË?zr?yÉ?yŒ?y?x(?y;?y‰?xU?y:?y¢?y±?z?{‰?yó?y¼?yà?z[?yø?y?ya?x?xu?yu?yz?yq?yÕ?yË?{‘?{5?|`?{O?z)?z?z?yÆ?y¢?y?xû?yh?y›?yÉ?{7?zÄ?{Ã?zV?}Ó?z?|)?{þ?{ö?z7?zP?yZ?ym?yƒ?yÒ?z/?zz?y±?|˜?„3?´?}Ð?}¡?|³?zÓ?{‘?{H?yà?zT?z?yÌ?|?~?}o?} ?¬?|G?{?y®?z±?{Å?{ ?}œ?‚‰?‚Q?ƒF?{|?z?zŒ?z–?y™?~»?yÞ>Ì»ˆ>̾Š>̪š>ÌÔá>̺å>ÌÒŸ>Ì¢>Ìà­>ÌÇZ>ÌÇÊ>Í…??‚Å?}2?y§?z­?{„?|Á?{C?~™>ÌÙ1>ÌÜú>ÌÓÙ>Ì»²>Ìù´>ÌÞñ>ÍY>̵i>̤ü>ÌÖz>Ìöâ>ÌÑ¿>Ì·§>ÍU>ÌÙ~?~ˆ?zÑ?{??y¡?yX?{ô?|$?~?~Ï>̶f>ÌÚt>ÌüÕ>̺>̪e>ÌÈà>Ír>Ìí>Ìê,>Ì¡î>̼ø>Ìäd>Ìõ×>ÌÖ>̨?‹*?‚Ï?~[?{?y÷?z¬?{Ñ?{0?|ì>Ì¢å>Ì«N>ÌÌ>ÌÆ‡>̹(>̵%>Ìàh>ÌÑE≯‚>̦Ž>ÌÉ >Ì®ª?‰m?z?|»?{I?z?z7?{b?|Ú?{é?‚ù?ŠÐ>Ìß">ÌìI>ÌÈ >ÌÜR>ÌÏK>̨R>̶_>̳>ÌÍv>ÌÌè>̪„>̶@>Ì·>̲!>Ìɉ>ÌóP>ÌÎ>̵³>ÌÔÅ>̼¨>̼˜>ÌÍZ>ÌÏâ>ÌÁŸ>ÌÞ“>̵ >ÌÀæ≯>̼÷>Í>Ìã.>ÌÍÂ>Ììþ>ÌÁÇ>ÌÚD>ÌÝ>ÌÔú>ÌÌ~>Ìó·>̼ÿ>Íp>Ìá>ÌÁH>ÌÇ0>ÌÑ{>̶Ñ>Ìã>ÌÊ>ÌÙ×>ÌÅ >ÌÑm>̯f>̼F>Ì´Ý?yv?{?|?{5?y™?yÌ?y×?zæ?zY?zý?|Þ?€Ë?æ?†‰?‹§?{Q?€o?|F?xÏ?x7?y@?yj?y’?yc?y,?y?y ?wŽ?qh?rŠ?s`?py?vï?x‘?x©?y#?yî?yæ?z?y…?y?y?y?x™?yH?v?t?yZ?y?x5?xÖ?yW?z‰?yÖ?{5?z#?y¦?yJ?y¡?xr?x??xÓ?y=?w?yj?xì?yf?yC?yQ?yÑ?{¦?zÄ?y¾?y„?yQ?x.?wˆ?w™?y+?wþ?xˆ?xº?yh?yr?yÎ?z@?z$?{,?z?y?y•?y}?xç?wt?xm?xf?y=?xþ?y,?y¿?z\?{:?zª?zg?yÇ?y?xK?y ?wí?w­?w7?w‡?x…?y^?yt?y?yy?yé?yå?yÃ?xµ?yG?w|?w—?w¸?ud?xï?wþ?vê?x®?wÙ?xD?y…?y{?yd?y?y½?x•?x›?tû?qÁ?p‡?wÈ?u?uª?wh?w³?w‹?xa?yS?xØ?y?x³?v¤?w’?s"?tœ?u°?x€?y&?y?wí?vR?uâ?nÏ?w›?x?u¸?w}?wk?wf?xÁ?x?p›>Ìx½>Ì}ì>Ì>̘Ó>Ì{$>Ìxá>ÌŠ‰>Ì„N>Ì—>Ì…Ã>Ìm3?y&?r ?yc?w¹?w?x]?xu?wB?tI>Ì”a>Ìi#>ÌJ¬>̆>Ì•@>Ì…~>ÌžÚ>Ìz%>Ìžò>Ìeï>ÌzK>ÌŸ>Ìy˜>ÌkO>Ìyk?sÈ?p£?xë?y?y?xê?wd?t4?q>Ìm>Ìp©>Ìj\>ÌœH>Ì€Š>ÌY >ÌVá>Ìsê>Ì`²>Ì|I>ÌŠ_>Ìs¤>Ìv>>Ìž>Ìn?qD?t ?xr?yh?x?xW?v9?up?x€>Ì{<>Ìt­>Ì“¥>̱>ÌmS>Ì/>ÌwÏ>Ì‹À>Ìœ >Ìhs>Ì–b?l®?u}?xæ?xþ?wÆ?xã?xf?v4?tª?u?p>ÌÛ>Ì„ý>Ìžu>Ìw¼>Ìaà>Ìé>Ì…´>Ì™á>Ì€Þ>Ì>Ìh¹>ÌŠ>ÌRj>ÌUW>ÌfU>Ì‹Ÿ>Ìi/>Ì’r>̈Þ>ÌŸ}>Ìg>ÌO{>Ì=>Ìh->Ì{->Ì—C>ÌR3>ÌŠ>Ì|ð>Ìz´>Ì=->Ì;Í>ÌWö>Ì 0>ÌPú>ÌkD>Ìl>Ì‚l>Ì`á>Ì€ >ÌIÿ>Ìa>Ìo>ÌšB>Ì >Ìtµ>Ì[š>Ì”:>Ìœ>Ì|Š>Ìg~>Ì}@>Ì“œ>̆æ?m·?vÍ?v ?w1?xÕ?yi?y%?y?xÕ?y?r ?sC?kþ?mÂ?j(?qÿ?ro?sl?v‰?w?y?yÚ?xë?yA?w«?uš?q?mN?m›?l¬?f?mF?kA?sb?w¡?x?zD?zè?{æ?yØ?y6?wø?v?s5?s8?n?q-?r‹?su?uŒ?yö?z!?zä?|»?{Å?|c?yn?w÷?w ?v™?vÔ?u ?t£?u½?w’?w ?yN?zõ?{à?|á?|Ý?| ?zt?zf?x$?vé?u^?vµ?uõ?vö?vS?x ?y°?|N?}¿?}ü?|"?| ?{t?zM?x?w‰?vè?uy?v2?u—?wô?x?x‘?zœ?{‰?}y?|p?|?yÿ?z?w%?uá?u,?uR?t?t°?vb?x—?x¡?y¼?{G?|?{Ú?zâ?y‹?xŸ?u¢?t2?sÑ?tž?s ?o#?rV?w4?u4?wi?wí?zA?y×?xw?xZ?w?s?j ?pM?m³?gF?k¾?n·?u?sã?uæ?w9?x”?xu?wõ?v)?qœ?d?mE?oŸ?sT?u±?w?w¤?vZ?t˜?sj?\²?iÝ?h?rz?t]?w ?w—?w?qÅ?pÈ?O??[?jR?si?sŒ?u_?vä?tZ?ov?b…>Ì/j>Ì9 >Ì+>Ì>Ì]´>Ì>¤>ÌŠ>ÌY~>Ì?P>Ì25>Ì-º>̾?b^?oñ?mC?uE?uº?v‹?u?s»?jý>ÌSS>Ì?†>Ìå>Ì é>ÌCg>Ìd¸>Ì,­>ÌJG>Ì1N>ÌEË>Ì-1>Ì/±?UÁ?m«?tË?t&?uÏ?v7?u™?t?o?Oâ?bÒ?np?tb?uQ?w?w¨?uñ?pÈ?oŸ?k¡?W¶?S>Ì@>ÌA“>Ì>Ìx>ÌC'>Ì­>Ì3b>Ì/ë>ÌV@>ÌZ>Ìÿ>ÌQˆ>ÌCì>Ì5>Ì>»>Ì%a>ÌV%>ÌFÄ>ÌQE>Ì¿>Ì<¶>ÌDA>Ì/Ù>Ì[-?\ï?Xô?jJ?pì?v¢?wP?xj?xÁ?wþ?u€?sž?pP?bñ?a™?O’?^}?^Ú?ie?p;?tä?w?qS?pÌ?oƒ?r$?p+?t?qé?wÝ?y‹?|N?z?{?w?u´?o¯?nK?ia?iW?df?e3?l‚?qÓ?qz?uÿ?xð?yF?xI?vK?t ?qM?ló?fL?a®?XN?S?Zõ?fm?lJ?o¼?só?uâ?v ?v?uˆ?rN?m#?d»?Y“?Aé?FZ?6Ü?>?[ç?d²?d?n ?q–?tá?tò?s?p“?j;?\?K„?ý>ËÆX>ËÔè>ËÌÊ>ËîŽ>Ëî>Ë«t>ÌÕ>Ëáj>ËÐ9>Ëï>Ëß½>ËÛ®>Ëý€>ËÙI>Ëêt>Ë×>Ëçø>˼ê>Ëò™>ËÀi>ËãQ>ÌÎ>ËËÜ?%.?D ?Z;?ds?kƒ?s?t‚?uO?sX?n‚?i-?M›?G}>ËÕÂ>Ëä–>ËÊ>Ì >Ë÷‹>Ëíø>Ìø>ËÙ >Ì>Ëüú>ËõÁ>Ëêí>˶_>˯Ë>Ë !>ËÄs>˦&>Ë•>Ë©0>ËÃØ>˯¢>Ë¡ø>ˇ­>Ë{×>Ëzˆ>Ë‹U>Ë1>Ëd2>Ë`Ï>Ë›>ËÛ >Ë‘>ˆÊ>Ëb¶>Ë«?>ËŸ>Ë”>ˤ>Ëræ>˵¿>Ë¥>Ë~ä>Ë´x>˲P>Ì1>Ëî³>Ëþa>Ëã.>Ëûš>Ëî>ËæY>ËÆõ>ËûÑ>Ì?9Z?YË?hs?n?r.?tU?t¿?rF?p¡?im?Sµ?:8>˼>˳å>Ëä >Ìy>˸Ò>Ëí:>ËÞÜ>Ëæà>ËÓ>ËÁ>Ìz>ËàÙ>Ëi„>Ë”–>˦%>Ë—[>Ë”>˹ì>˰–>ˆû>Ë—k>Ëʳ>ˉi>˺Ö>Ëz6>Ë¿N>ËŒê>Ëzå>˸C>˨‚>Ë®´>˹a>˵>ˉ¨>Ë´º>ËŠd>˦>Ë\â>Ë™Å>˲™>Ëœ>Ë£J>Ëæ1>Ë£È>Ëø€>ËáÚ>ËÚ,>Ëêm>ËÌÜ>Ë»Ì>Ë¢Q>ËáC>ËÍ#>Ëõž?(?W»?lª?o€?të?uÅ?už?s˽Ã>˽ò>ËÔL>Ì #>ËÈw>ËçÐ>ËÅ}>˲c>ËßÜ>Ë­Ð>Ëö>Ëà”>Ë >Ëóæ>Ëã>Ë•r>Ëá_>ËÚ >Ëí$>Ì ¢>Ëçj>Ëç\>Ëü-?*?P—?[T?kë?q~?vÙ?w1?v?u[?sF?n\?`½?]T?P ?A™?A‚?T ?YÅ?]ò?mø?sZ?w?x÷?yI?w ?u5?s`?l?f?d7?Qú?^¤?b]?c?n9?vÄ?wè?zD?}]?}î?xü?w·?t•?s?o1?j?co?c?h ?oQ?r?x?{Í?}Ó?€á?}Ü?|Î?{9?x'?tÿ?t?qÄ?n›?n?p?nÿ?t?{Ÿ?~?™?'?…8?Ó?~¢?{O?wU?u?sŸ?qû?r+?t:?ut?xý?| ?ƒ³?†?ƒ?‡Ò?ƒç?Ì?}?wµ?vS?u1?sÝ?te?uf?uí?yÇ?{K?€£?‡y?†?„…?ƒ??{õ?z?uö?tS?t5?sš?u?ué?z ?} ?}Á?ƒ?ƒ?ƒ?€¿?}?z1?x?s‘?t¹?r~?r8?rŒ?v?vÇ?y?{??°?‚-?Ÿ?z™?vd?s?rµ?nB?mþ?nì?oF?s?u1?t¦?w¦?z¶?|?{ß?z“?wÑ?sÐ?q}?j™?lN?jH?d»?gy?m´?qå?po?tÒ?xW?z“?wÌ?x„?vË?oð?mC?j3?\`?b3?`è?]‡?d|?i?iÛ?nì?uÙ?wØ?uÖ?t{?r°?mG?[ó?\Á?PÄ?I?H?NÅ?ea?j?m|?q`?rÂ?tÝ?ub?r;?oâ?l=?^?S®?NÚ?—?47?B–?Q/?_v?fù?pJ?s;?sè?tÃ?s|?k“?f©?\ü?Lz?E9?Ñ?±?5œ?IÍ?b»?j¶?põ?q„?t…?ti?s>?pg?i«?[Ï?Sæ?(ä?X? x?1¡??Ä?[?l?nñ?sF?td?tÓ?s¸?qr?kô?b?QÒ?B¹?=–?3‹?@ü?Z×?`_?p¤?nÿ?uL?vŠ?v2?sø?qm?po?c?`É?D|?G¾?:Ð?ZÏ?]?a2?q?sÉ?vý?x¹?xK?w_?uó?né?jµ?iô?]Y?[#?]W?Zj?kj?l°?ŠO?o?”B?“0?”K?…?‰³?“k?•G?“ë?•î?„Ú?†?zé?o¿?l†>Ìë/>ÌÊ>̵>Ëò%>Ì„>Ëxö>Ëúš?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff?fff  b ëbÁ¶Hˆ  b¨8bÁ¶HˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàá    !"#$%&'QRSTUVWXYZ[\]^_`abcdefghijklm—˜™š›œžŸ ¡¢£¤¥¦§¨©ªËÌÍÎÏÐÑÒÓÔÕÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ+,-./0123456789:;<=>?@ABCDEFGqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹Œ·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊëìíîïðñòóôõûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüý ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C m n o p q r s t u v w x y z { | } ~  € ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « Ã Ä Å Æ Ç È É Ê Ë Ì Í Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü ý þ ÿ         ! " # $ % & ' ( / 0 1 2 3 4 5 6 7 8 9 ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ÿ                     ! " # $ % & ' ( ) / 0 1 2 3 4 5 6 7 8 9 ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ B C D E F G H I J K L M N U V W X Y Z [ \ ] ^ e f g h i j k l m n u v w x y z { | } ~ … † ‡ ˆ ‰ Š ‹ Œ Ž • – — ˜ ™ š › œ ž ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNUVWXYZ[\]^efghijklmn‹ŒŽ‘’“”ÍÎÏÐÑÒÓÔÕÖ456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝäåæçèéêëìí     _`abcdefghëìíîïðñòóôõö÷øùúûüýþÿ‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–âãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¨©ª«¬­®¯°±éêëìíîïðñòvwxyz{|}~€‚ƒ„…†‡ˆ‰ö÷øùúûüýþÿ     z{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùú  CDEFGHIJKLÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâOPQRSTUVWXYZ[\]^_`abcdefÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéênopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSZ[\]^_`abc€‚ƒ„…†‡ˆÕÖרÙÚÛÜÝÞabcdefghijklmnopqrstuøùúûüýþÿ    XYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ!!!!!!!!!! ! ! ! ! !!!!!!!!!!!!!!!!!!! !!!"!#!$!%!&!'!(!)!*!+!,!-!.!/!0!1!2!3!4!5!6!7!8!9!:!;!<!=!>!?!@!A!B!C!D!E!F!G!H!I!J!K!L!M!N!O!P!Q!R!S!T!U!V!W!X!Y!Z![!\!]!^!_!`!a!b!c!d!e!f!g!h!i!j!k!l!m!n!o!p!q!r!s!t!u!v!w!x!y!z!{!|!}!~!!€!!‚!ƒ!„!…!†!‡!ˆ!‰!Š!‹!Œ!!Ž!!!‘!’!“!”!•!–!—!˜!™!š!›!œ!!ž!Ÿ! !¡!¢!£!¤!¥!¦!§!¨!©!ª!«!¬!­!®!¯!°!±!²!³!´!µ!¶!·!¸!¹!º!»!¼!½!¾!¿!À!Á!Â!Ã!Ä!Å!Æ!Ç!È!É!Ê!Ë!Ì!Í!Î!Ï!Ð!Ñ!Ò!Ó!Ô!Õ!Ö!×!Ø!Ù!Ú!Û!Ü!Ý!Þ!ß!à!á!â!ã!ä!å!æ!ç!è!é!ê!ë!ì!í!î!ï!ð!ñ!ò!ó!ô!õ!ö!÷!ø!ù!ú!û!ü!ý!þ!ÿ"""""""""" " " " " """"""""""""""""" "#"&")","/"2"5"8";">"A"~&j-Õ.é79>Ç>õ?O?T?x pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8ÌTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ™pÓÀà;@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ h–0– !    0ž Á¶HLèPSNOD`@и˜P0Ï› !    Э Á¶H4ƒèP !    p½ Á¶HÀèP?€n?€B?€E?€>?€G?€U?ëa?ë,=ïøÿ=Íé?€U?€+?€A?€H?€?€,?€;?€8?û²?û™=ð=Ìèç?€L?€N?€0?€)?û¿?û™?{µ›?{³þ=ï{V=ÌÎb?€^?€'?€*?€E?ûá?û¨?{´“>ä =Í)_=ÌÍ?ë‡?öÓ?û¤?û ?{µ¹?{´>¾T=ÌÕ¨=ÌÌ÷=ÌÌä?ë:?öÆ?û¤?û¡?{´¢>ãè=ÌÕ¹=ÌÌô=ÌÌð=ÌÌß=ïøå=ðþ=ð®=ð…=ï{B=Í"è=ÌÍ)=ÌÌû=ÌÌì=ÌÌæ=Íê=Ìë=ÌëG=Ìèê=ÌÎ`=ÌÍ!=ÌÌ÷=ÌÌÞ=ÌÌà=ÌÌÚ?€??€)?€?€%?€$?€6?€0?€0?€'?€#=ð$-=ÌÍ÷?€6?€)?€8?€8?€$?€0?€-?€3=ð$G=ÌÍõ?€*?€.?€-?€/?€?€.?€?€=ð%=ÌÍõ?€0?€&?€?€1?€?€5?€ > ‘„=Í+Ù=ÌÌ×?ö×?€/?€"?€#?€?€?{ç¿=ïSQ=ÌÍÜ=ÌÌ»?öÈ?€?€?€1?€> ”[=ïV¦=Í%þ=ÌÌÇ=ÌÌ»=ð.=ð$<=ð$H=ð$Œ=ð$¥=Í%*=ÌÍÞ=ÌÌÈ=ÌÌÌ=ÌÌÒ=Ìë.=ÌÍø=ÌÍø=ÌÎ =ÌÎ=ÌÌÚ=ÌÌ¿=ÌÌ»=Ì̾=Ì̹?€=?€I?€'?€2?€J?€??û®?û¥=ð+=ÌëK?€>?€&?€!?€"?€G?€=?€&?€=ð$R=ÌÍ÷?€8?€*?€"?€?€/?€ †=Í+×=ÌÌÖ?ûª?€;?€L?€9?€?€ ?€=ðO¹=ÌÍä=Ì̺?û|?€"?€(?€ ?€> “²=ðNF=Í)ð=ÌÌÈ=Ì̺=ð=ð$&=ð#ù=ð$+=ð$•=Í%+=ÌÍå=ÌÌÈ=ÌÌÆ=ÌÌÓ=Ìë`=ÌÍ÷=ÌÍ÷=ÌÍö=ÌÍõ=ÌÌ×=ÌÌ»=ÌÌ»=ÌÌ»=Ì̲?€\?€?ûŒ?û—=ðË=Ìèð?€^?€.?€*?€?€0?€"?€2?€=ð$]=ÌÍ÷?€H?€4?€$?€&?€6?€-?€1?€=ð$E=ÌÍö?€@?€(?€&?€?€4?€8?€$?€'=ð$[=ÌÍô?€V?€-?€$?€?€6?€.?€"?€)=ð%=ÌÍ÷?€A?€#?€"?€?€ »h=Í+ê=ÌÌØ?ûž?€?€?€0?€4?€> æ=Í1@=ÌÌÊ=Ì̹?ûŒ?€5?€!?€1?€> »e=Í6=ÌÍi=ÌÌÆ=Ì̺=ð›=ð$J=ð$;=ð$1=ð$Ÿ=Í%D=ÌÌÿ=ÌÌÌ=ÌÌÆ=Ì̼=Ìèè=ÌÍ÷=ÌÍö=ÌÍö=ÌÍô=ÌÌÚ=ÌÌ×=ÌÌÃ=Ì̺=Ì̱?€R?€/?€%?€??ûÜ?û¹?{µ”?{´ð=ï{‡=ÌÎf?€D?€$?€?€/?€?€)?€?€=ð$=ÌÍõ?€=?€1?€?€:?€%?€2?€ ?€-=ð$Œ=ÌÍõ?€-?€-?€?€4?€/?€)?€!?€=ð$È=ÌÍô?û¹?€$?€#?€A?€$?€?€> A=͆ä=ÌÌß?û¾?€?€$?€0?€#?€?€=ðçD=ÌΟ=Ì̾?{µí?€?€&?€.?€0?€(> ×=Ìó·=ÌÌÇ=Ì̹?{´H?€ ?€?€> =ðà¡=ÌîÊ=ÌÌÇ=ÌÌÆ=Ì̺=ï{=ð$¬=ð$“=ð$¨=Í…I=ÌÎ}=ÌÌé=ÌÌË=ÌÌÈ=Ì̼=ÌÎd=ÌÍö=ÌÍö=ÌÍö=ÌÌÝ=ÌÌ¿=ÌÌÉ=Ì̼=Ì̽=Ì̲?€E?€;?€@?€Q?ûò?û¡?{³ñ>ãt=Í$«=ÌÍ?€#?€?€A?€:?€1?€(?€> “Ï=Í&Ö=ÌÌ×?€A?€!?€1?€+?€0?€$?€%> ’Ý=Í&Ô=ÌÌ×?€-?€&?€#?€)?€N?€1?€!> »O=Í&Ø=ÌÌ×?û·?€,?€(?€?€"?€*?€=ðß,=ÌΆ=Ì̺?û}?€*?€/?€?€?€(>^›=Í2¯=ÌÌÎ=Ì̹?{´Ï?€ ?€^ˆ=Í\ó=ÌÌî=ÌÌÆ=Ì̹>âž> ‘~> ›> »y=ðår=Í1%=ÌÌÜ=ÌÌÆ=ÌÌÆ=Ì̺=Í)z=Í+ê=Í+á=Í+â=ÌÎ’=ÌÌË=ÌÌÍ=ÌÌÆ=ÌÌÏ=ÌÌÁ=ÌÍ:=ÌÌñ=ÌÌÚ=ÌÌØ=Ì̼=Ì̾=ÌÌÁ=Ì̺=ÌÌÈ=Ì̹?ëŽ?öÎ?û”?ûˆ?{µÀ?{´Ä>¾ü=ÌÕŸ=ÌÍ=ÌÌâ?öå?€)?€*?€#?€?€?{ç¢=ïW=ÌÍá=ÌÌ»?û°?€'?€2?€+?€0?€'?€ =ðN–=ÌÍæ=Ì̺?ûª?€?€.?€,?€?€$> ÿ=Í6c=ÌÌÉ=Ì̹?{µ¨?€?€#?€A?€?€> ×=Ìïy=ÌÌÿ=Ì̹?{´/?€?€*?€(?€%>^š=Í[%=ÌÌß=ÌÌÏ=Ì̺>À ?{ç¯?€> d> ×=ÍYÀ=ÌÌÊ=ÌÌÆ=ÌÌÆ=Ì̺=ÌÕ©=ïQØ=ðOœ=Í/²=Ìó=ÌÌë=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌ»=ÌÌ÷=ÌÍë=ÌÍì=ÌÌÊ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=Ì̺=ÌÌñ=ÌÌÖ=Ì̼=Ì̺=Ì̺=Ì̺=Ì̺=ÌÌ»=Ì̾=ÌÌÇ?ëq?ö³?û˜?û?{´>ã=ÌÕ¨=ÌÌó=ÌÍ=ÌÌÞ?ö®?€?€!?€?€> ‘Ð=ïQ1=Í&=ÌÌÓ=Ì̺?û‡?€"?€(?€%?€> ‘=ðOZ=Í)ù=ÌÌÊ=Ì̺?û¦?€F?€(?€!?€> »\=Í/¡=ÌÍi=ÌÌÍ=Ì̺?{´Ñ?€ ?€ ?€!> ¯=ðæ]=Ìò=ÌÌÊ=ÌÌÞ=Ì̺>âx> ”Ë> “Ç> »I=ðÞ&=Í0"=ÌÌé=ÌÌÆ=ÌÌÇ=ÌÌ»=ÌÕª=ïUÛ=ðNv=Í4Å=Ìî’=ÌÌÚ=ÌÌÆ=ÌÌÆ=ÌÌÆ=Ì̺=ÌÌô=Í& =Í)ü=ÌÍd=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÇ=ÌÌ¿=ÌÍ=ÌÌû=ÌÌÈ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÇ=ÌÌÄ=ÌÌè=ÌÌÚ=Ì̹=Ì̺=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=Ì̼=Ì̳=ïøº=ða=ðñ=ð¼=ï{=Í'À=ÌÌ÷=ÌÌð=ÌÌé=ÌÌß=ð=ð$Û=ð$j=ð$f=ð$Œ=Í*'=ÌÍÛ=ÌÌÈ=ÌÌÔ=ÌÌÂ=ðö=ð$´=ð$:=ð$/=ð$Š=Í*$=ÌÍä=ÌÌÈ=ÌÌÛ=ÌÌÃ=ð°=ð$¡=ð$]=ð$p=ð$Ð=Í*9=ÌÌÍ=ÌÌÇ=ÌÌË=ÌÌ»=ï{=ð%,=ð$Â=ð$Ë=̓Š=ÌΕ=ÌÌÈ=ÌÌÉ=ÌÌ×=Ì̺=Í"Ò=Í%8=Í%=Í%$=ÌÎq=ÌÌÇ=ÌÌÆ=ÌÌÈ=ÌÌÍ=Ì̺=ÌÌø=ÌÎ =ÌÍé=ÌÌÉ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÓ=ÌÌÔ=ÌÌð=ÌÌì=ÌÌÑ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÈ=ÌÌÅ=ÌÌï=ÌÌç=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÈ=ÌÌÏ=ÌÌé=ÌÌÖ=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=Ì̲=Íé=Ìë8=ÌëX=Ìèç=ÌÎa=ÌÍ=ÌÌå=ÌÌß=ÌÌß=ÌÌå=Ìë=ÌÎ =ÌÍû=ÌÍ÷=ÌÍö=ÌÌØ=Ì̼=Ì̼=Ì̼=ÌÌ·=ÌëI=ÌÎ =ÌÍÿ=ÌÍö=ÌÍõ=ÌÌ×=ÌÌ»=Ì̺=Ì̼=Ì̲=Ìèæ=ÌÎ=ÌÎ=ÌÍõ=ÌÍõ=ÌÌÖ=Ì̹=Ì̺=ÌÌ»=Ì̲=ÌÎh=ÌÎ#=ÌÍü=ÌÍõ=ÌÌÜ=ÌÌ»=Ì̺=Ì̽=ÌÌÄ=Ì̱=ÌÍ=ÌÌö=ÌÌä=ÌÌ×=ÌÌ»=Ì̺=Ì̺=ÌÌ¿=ÌÌË=Ḭ̀=ÌÌä=ÌÌÜ=Ì̾=Ì̺=Ì̹=Ì̹=Ì̺=ÌÌ»=Ì̼=Ì̲=ÌÌà=ÌÌì=ÌÌÁ=Ì̺=Ì̺=Ì̺=ÌÌ»=ÌÌ»=ÌÌ»=Ì̱=ÌÌì=ÌÌÎ=ÌÌ»=Ì̼=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=ÌÌ»=Ì̲=ÌÌÙ=ÌÌÉ=Ì̲=Ì̲=Ì̱=Ì̱=Ì̱=Ì̱=Ì̲=Ì̪4–74–"4–%4–4—24—@4:4BÏ8Óïþ8üÇ4–24– 4–24–:4–14–/4°ß4´³8ÓÙ18üçB4–D4–+4–*4–.4–*4–*4¤4¦8ÓØd8üç4–74–.4–14–!4–-4–,4£\4¥88ÓÚÔ8üêX4—54–C4–%4–4m4Ÿ>4»v4»§8Ôp‘8ýP4—H4– 4– 4–74Ÿ`4¡*4»– 8¶{j8ü}Ý8ýÕ4:(4°Û4¤4£G4»v”4»§²8¶Í8üÑ×8ý8ý 4B×4´§4¥ñ4¥14»•ƒ8¶{™8üÑò8ý8ý8ý 8Óð(8ÓÙ78ÓØ~8ÓÚÚ8Ôpˆ8ü…8ý;8ý8ý8ý8üÇ€8üçD8üç8üê[8ýV8ýâ8ý8ý 8ý 8ý4– 4–4–/4–,4–/4–"4°Í4´©8ÓÙ8üçF4–-4–'4–*4–94–)4–+4–04–D8ÓÇ”8ý}4–04–-4–,4–>4–74–74–24–/8ÓÇ—8ý}4–*4–04–@4–A4–,4–74–74–>8ÓÇ•8ý|4–"4–74–44–74–*4–84—ò4˜8ÓÇ'8ý{4–&4–/4–"4–84–&4–?4—ï8µa±8üyš8ýú4°Û4–B4–.4–.4—ú4—é4²¦Æ8Ô“Å8ýÐ8ý 4´¨4–.4–(4–;4—ð8µ^_8Ôö8ü™÷8ý8ý 8ÓÙ8ÓÇ|8ÓLj8ÓÇz8ÓÇ8ü€ç8ýÎ8ý8ý8ý8üçN8ý~8ý~8ý‹8ý„8ýÿ8ý8ý 8ý8ý 4–4–<4–4–&4–=4–24¤ 4¥ï8ÓØG8üç 4–04–.4–*4–+4–R4–F4–14–&8ÓÇt8ý}4–*4–14–*4–$4–64–E4–24–8ÓÇ‚8ý}4–44–84–A4–%4–'4–>4–94–$8ÓÇ‚8ý|4–:4–L4–,4–-4–84–44–&4–18ÓÇ-8ýz4–34–:4–/4–4–/4–44–&8µbÂ8üyš8ýù4¤#4–F4–Z4–B4–"4–4—ë8Ó˜t8ýd8ý 4¥ú4–-4–04–(4–'8µ_#8Ó™8ü”—8ý8ý 8ÓØB8ÓÇŸ8ÓÇÄ8ÓÇ’8ÓÇ&8ü€é8ýe8ý8ý8ý8üç8ý~8ý~8ý}8ý{8ýý8ý 8ý 8ý 8ý4–;4–04–*4–"4–(4–04£?4¥+8ÓÚª8üêa4–U4–64–14–!4–84–+4–<4–#8ÓÇ\8ý}4–;4–;4–,4–/4–>4–44–84–"8ÓÇx8ý|4–34–/4–.4–%4–<4–?4–,4–/8ÓÇm8ý{4–J4–44–,4–#4–>4–64–,4–18ÓÆù8ý|4–34–+4–*4–"4–E4–<4–8µ3À8üyj8ýû4£R4–+4–'4–84–<4–(8žÕ8ü88ý8ý 4¥24–?4–*4–84–(8µ3¸8ü2f8ý+8ý8ý 8ÓÚÇ8ÓÇ•8ÓÇ8ÓÇ‘8ÓÇ8ü€¾8ý-8ý8ý8ý 8üêX8ý}8ý}8ý|8ýz8ýÿ8ý#8ý8ý 8ý4—;4–&4–4–144ŸM4»uÈ4»É8Ôp|8ýX4–84–,4–4–74–&4–34—õ4—ò8ÓÇ.8ý{4–/4–94–(4–B4–-4–:4–)4–48ÓÇI8ýz4–"4–54–(4–<4–74–14–*4–(8ÓÇ8ýy4i4–/4–,4–K4–,4–$4–$8÷Ò8ûãI8ýì4ŸM4–"4–,4–84–,4–&4–8ÒøP8ý 8ý 4»w44—ì4–.4–54–74–08µx8üÀ¾8ý8ý 4»¡L4—î4–"4–!8÷W8Òý–8üÆ[8ý8ý8ý 8Ôp½8ÓÇ8ÓÇ)8ÓÇ8ûäà8ý 68ý08ý8ý8ý 8ýQ8ý{8ý|8ý{8ýë8ý 8ý8ý 8ý 8ý4—24–04–44–E4Ÿo4¡(4»§L8¶|†8üƒM8ý×4–4–&4–J4–C4–;4–34—ù8µ_8ü8ýû4–44–*4–84–34–84–,4–,8µ`38ü8ýû4–"4–.4–+4–14–]4–84–*8µ3Õ8ü~Ì8ýû4ŸH4–64–/4–%4–+4–24–&8Òÿ8ý >8ý 4¡4–44–74–&4–"4–08À$8üO¢8ý8ý 4»Y4—ì4–E4–,4–8À78ûß²8ýÊ8ý8ý 8¶}À8µa®8µbÚ8µ3±8ÒùÞ8üQI8ýÞ8ý8ý8ý 8ü}³8üyY8üyT8üy8ý 8ý8ý8ý8ý8ý8ýî8ý8ýü8ýû8ý 8ý8ý8ý 8ý8ý 4:)4°Ô4¤"4£I4»w4»k8¶Ëþ8üÑå8ý+8ý 4°ç4–<4–44–.4—ø4—ù4²§8ÔÇ8ýÑ8ý 4¤4–24–94–24–84–.4—Ý8Ó™e8ýf8ý 4£L4–*4–54–34–$4–,8ÿ¾8ü2‘8ý8ý 4»vC4—ø4–+4–J4–$4–8µ†8üÅr8ýB8ý 4»¡V4˜4–14–04–,8À,8ûâ8ýÞ8ý8ý 8¶Ê4²´Ý4—æ8ÿt8µ†8ûã«8ý8ý8ý8ý 8üÑá8Ô•G8Ó˜„8ü:8üÁ8ýÎ8ý8ý8ý8ý 8ý8ýÝ8ýj8ý8ý8ý8ý8ý8ý8ý 8ý8ý!8ý 8ý 8ý 8ý 8ý 8ý 8ý8ý4Bö4´4¦ 4¥-4»§8¶|ô8üÑÑ8ý8ý18ý 4´ 4–04–,4–*4—ù8µa@8Ô•8üš8ý8ý 4¥Ý4–-4–/4–-4–'8µb,8Ó˜ž8ü”8ý8ý 4¥54–T4–/4–*4–8µ3Ö8ü:<8ý-8ý8ý 4»'4—ð4–)4–+8öw8Òù68ü¾8ý8ý(8ý 8¶}Õ8µ]Í8µ^õ8µ3Û8ÒÿÓ8üR¥8ýÐ8ý8ý8ý 8üÑö8Ô‘,8Ó™r8ü4h8üÆv8ýà8ý8ý8ý8ý 8ý8üš8ü” 8ý28ý8ý8ý8ý8ý8ý8ý$8ý<8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý%8ý 8ý 8ý 8ý 8ý 8ý 8ý 8ý8ÓðQ8ÓØï8ÓØE8ÓÚ«8Ôp®8ü¹8ý8ý8ý8ý 8ÓÙ 8ÓÇ/8ÓÇj8ÓÇf8ÓÇ@8ü{v8ýÑ8ý8ý 8ý8ÓØ;8ÓÇP8ÓÇ’8ÓÇŽ8ÓÇ:8ü{u8ýc8ý8ý&8ý8ÓÚº8ÓÇq8ÓÇv8ÓÇ`8ÓÆí8ü{F8ý8ý8ý8ý 8Ôp\8ÓÆß8ÓÇ8ÓÇ 8ûç98ý 8ý8ý8ý#8ý 8ü…P8ü<8ü(8ü€ó8ý >8ý8ý8ý8ý8ý 8ý8ýó8ýh8ý8ý8ý8ý8ý8ý8ý 8ý8ý08ý8ý8ý8ý8ý8ý8ý8ý8ý8ý08ý8ý8ý8ý8ý8ý8ý8ý8ý8ý!8ý 8ý 8ý 8ý 8ý 8ý 8ý 8ý8üÇ„8üçY8üç8üêY8ýR8ý×8ý 8ý 8ý 8ý8üçG8ý8ý8ý}8ý{8ýü8ý 8ý 8ý 8ý8üç8ý8ý„8ý}8ý{8ýû8ý 8ý 8ý 8ý8üêX8ý‰8ýˆ8ý|8ýz8ýû8ý 8ý 8ý 8ý8ý]8ý 8ý€8ýz8ýë8ý 8ý 8ý 8ý8ý8ýÝ8ý8ý8ýü8ý 8ý 8ý 8ý8ý8ý8ý 8ý%8ý 8ý 8ý 8ý 8ý 8ý 8ý 8ý8ý 8ý48ý8ý 8ý 8ý 8ý 8ý 8ý 8ý8ý8ý8ý 8ý 8ý 8ý 8ý 8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýþ?‡t?Š?|?ud?p×?jÖ?b²?``?\Ý?aQ?‡ƒ?H?}1?vÐ?oE?i”?c6?`.?YÂ?_~?‰ ?A?{?uß?o†?i™?c?_ñ?Z?^Þ?ˆ™?€á?z¾?s¹?pI?iö?d£?_ž?Z‘?_‘?ˆ_?‚­?xç?t§?pK?h¼?e?aX?^®?a?ˆ|??{?w¾?pH?gÆ?dQ?aó?bò?et?‡î?€{?{¿?uÆ?pI?h¾?fT?e‘?g6?iÕ?…&? ?|6?uþ?o~?j€?l?j+?k?qü?r¤?si?r›?‚?¡?vÑ?u ?tb?sä?sÔ?t]?u•?u§?€Î?€‚?w?uƒ?u—?uŒ?u±?v+?w?w£?€?~æ?vÊ?vÓ?w?w4?w‚?x ?xì?z?~È?}?w9?wà?x_?xÆ?y:?yÜ?z—?{%?€D?‚?yx?u¥?tR?sS?s?so?sý?tï?€ƒ?^?xÜ?uÒ?tA?r÷?r¦?rë?st?sÊ?€¢?s?xÛ?ux?t*?rõ?r‹?rÌ?sZ?sO?€é?Æ?xô?u-?t ?sW?râ?s&?sš?su?õ?€¼?wU?u_?tt?sú?sÑ?tS?tË?s‚?€ø?€d?wö?u}?u5?tò?u?u«?v(?t…?€ ?€+?w9?v'?v?v?vY?vÁ?wA?w?I?€?v™?v~?w ?wZ?w¿?x2?x¶?y?~Å?}f?w‚?wÃ?x6?x¨?y'?yÐ?z€?{ ?}"?|ï?wÍ?x•?yU?yü?z§?{†?|\?|ö !    Õ Á¶H¨‘èP !    °ä Á¶H“èP !    Pô Á¶Hx•èP !    ð Á¶H`—èP!?@4 4ÿAAÁ¶H€!?@4 4ÿA˜%AÁ¶H€SNOD8>(=xPÑ`Ò¨ 7(¼~À°8?†°?„[?„9?…?†¢?‡+?†~?„¦?µ?~û?‚?Û?‚þ?„g?‚Ö?“?‚?é??}?~h?~`?|R?{e?{ ?{¢?{r?{4?{|?z×?u¿?we?w?tà?u¥?v?wœ?wI?w¿?x€?nÝ?p©?nÑ?o½?o½?n…?nô?pµ?t¢?vG?j®?g?iN?lg?l_?kp?j®?kI?o}?sç?cÅ?e$?eÄ?cÌ?cÎ?dÊ?fÏ?jv?n²?s?^Å?_q?_æ?`?_F?a?fÚ?kƒ?o‰?sX?[Ë?Yí?Y@?Z[?]r?c5?iT?l§?p•?t@?`=?^h?^ ?_¥?b±?g?hý?m ?pÒ?s•?„Í?ƒù?„„?…µ?‡?…à?†?…¢?‚Î?A?ƒ…?‚¥?‚ ?ƒ®?‚Ñ?ƒp?‚U?€é?}‹?|Š?}k?}ý?}\?}?}[?}Ó?}M?zæ?z¥?z–?v?w§?xu?vA?té?v?wY?x£?xó?x½?oH?p€?nÏ?o?o&?o?o ?qâ?vj?v£?j6?h‘?gþ?iÉ?iÒ?j/?jW?k?m²?sk?dË?dÕ?eP?d?dÐ?b ?fµ?hµ?m?rl?_?\|?]?_b?^Ì?_ƒ?aì?g!?n?rÒ?Zü?XZ?X=?YF?[Š?aT?fc?jÿ?oé?t?^þ?]×?]%?]ç?a°?e²?i?lµ?oö?rH?…b?†R?„Q?…›?‰ ?ˆY?‡f?…b?ƒ.?R?ƒô?·?ƒ?ƒ?„u?„q?‚ ?~‰?{‡?{ä?|p?}C?}C?|>?{?|ë?|Ï?zô?z­?zk?wh?wë?x¤?vJ?tl?u†?vå?x?|1?}?|?zS?}~?~®?}]?z`?{0?{?}†?}Y?|9?zc?}?~?|ç?yù?z2?zó?d?~g?|U?{¥?~k?}}?|j?zÿ?y‘?zô?~:?~C?}û?z²?}{?|v?|?z{?{"?{6?|Ü?~?Ï?}ñ?{’?{°?|]?zÈ?{8?{?{O?}%?}$?{?{¤?{ö?{ü?{X?{/?{?|?|8?{?yv?{?{Ç?{¹?{]?zÚ?zß?|?{¿?{/?yý?z~?{8?{l?{\?z×?zk?y?v›?wu?v?t¾?u²?tê?u¶?uö?xr?y@?w´?w'?uš?td?tc?vÛ?vN?vÞ?x\?wÊ?wm?v¡?wh?vð?ub?vÈ?w2?w??xq?uõ?u6?t§?v»?x–?v?uˆ?wB?wù?x¯?vÓ?tB?u˜?v-?w°?vº?vj?vj?x…?y?v2?tt?u’?w ?xÕ?w`?up?uâ?x^?yR?v+?t?s-?v_?y€?xä?t]?v ?xq?yl?ws?x?vX?wÐ?wþ?y?yî?xí?yG?y–?v×?x¨?x?wÃ?xV?z,?|x?z€?yÕ?xÔ?wˆ?xð?xç?x¤?y?zL?|@?{-?zˆ?zO?n×?n™?oc?r¢?r?oË?p8?rT?ti?uÈ?m[?n¯?om?râ?o8?oþ?o–?rø?tp?v?nc?og?pS?r´?oj?oÂ?oC?s?t®?v+?m‚?o~?p?s?pl?nÃ?oŠ?pÂ?sÎ?v;?l–?n‹?pœ?s¤?o?n:?oo?o`?rl?vf?o?nì?q?r ?n¤?n½?pÍ?q¦?tF?w'?s‚?ph?qL?p«?p{?r2?r¹?t-?vA?x?s7?p’?pô?oË?q ?s(?tž?v'?wµ?xñ?tx?sá?s»?s}?tL?u?u½?w”?y?yÇ?w ?w?v½?v“?vÜ?w^?wl?x©?z6?zù?iV?k ?m?lƒ?kÐ?iÊ?jq?kî?q?sÀ?g?iò?nd?lx?lL?jR?jÚ?k´?p£?sú?j?iS?ll?j3?k%?i??j˜?jH?o¿?sú?j$?iò?k)?iö?kç?j»?jØ?jÒ?oÝ?t:?j(?kN?k9?hM?i¸?ko?kÁ?mB?qr?tÅ?h¿?lÔ?lo?iV?i'?k›?m ?oð?rò?uÏ?h›?lf?m­?j7?h?j»?n.?r ?tä?w;?lä?m¢?n*?l–?kç?n:?o¢?sâ?v›?x’?rð?rœ?r?qQ?qœ?rR?rL?uª?x„?z)?wU?w"?uf?tÏ?tü?ub?uŽ?w¿?z_?{ß?g¤?dÖ?c?`~?b8?c¹?e?he?n?s?g“?eÌ?bÕ?aj?b­?c×?ds?hµ?n¯?râ?e?e„?d–?c=?bÇ?c¤?e?i.?n¿?rø?fÀ?e~?e¯?cô?_©?bŽ?eP?j8?o´?sz?f?eË?eY?f×?cj?c-?e–?k¦?r?tŒ?gu?gk?e£?f)?f1?en?h¢?n-?rœ?u‹?g-?h±?eó?e!?fá?hø?m ?pú?tG?vú?jµ?lv?j?iï?k©?mÕ?p3?sS?v ?xl?o”?p§?oã?oÄ?px?qf?r¸?u`?w`?y¥?sS?tA?s?sn?sâ?t[?uJ?w?y?|?a¸?`;?_ÿ?^ù?]ø?`S?eh?jˆ?o?ró?_d?^?]|?];?]Õ?]£?b?x;?k?ks?jÓ?k¨?m3?o1?q‰?t?vš?y?oR?n€?ný?oµ?p×?rS?t?v!?xH?z²?rÙ?r?rs?rî?s¾?tå?v[?x?yÜ?{¤?`g?_®?_Y?_Ú?b2?fJ?j??m|?pu?tj?_›?]œ?]˜?^R?`h?dÄ?i?lp?o;?s ?^›?]?]o?^F?`?d§?h»?lN?nð?r?_\?]??^P?^û?aÑ?e1?iM?m?p ?sP?b?a^?`Ô?aJ?d?g?k.?n|?q?t?fR?e#?eV?eÔ?gÕ?jŠ?m—?pž?s_?uI?i‘?h?h?iš?kS?m‹?p?ru?t#?u–?m;?l¹?lD?m:?n­?p{?rŠ?t“?vF?w¤?pk?nã?o?p?qÀ?s4?tÙ?v?x3?y=?r§?r?r¥?sZ?tP?u‡?vô?xž?zc?|3?€?€?€?€?€?€?„^?„f¹DQwDyG(?€?€?€?€?€?€?Ýæ?á­DPéüDyfx?€?€?€?€?€?€?€Ô×?€Ö¾DPé1Dyf=?€?€?€?€?€?€?€Ô?€ÕõDPë™Dyi„?€?€?€?€?€ÎE?€Ð@8ÝH@9 œDQCDyŒü?€?€?€?€?€Ð1?€Ñõ@8üÝD3ôVDxþ‰Dy—\?„^1?Ýâ?€ÔÇ?€Ô @8Ý×@9GD4DçDyQYDy——Dy—‘?„fÁ?á¡?€Ö«?€Õí@8üXD3ô…DyQtDy—–Dy—šDy—’DQ¡DPêDPéKDPëŸDQ:Dy–Dy—ÀDy—£Dy—œDy—˜DyG&DyfzDyf=Dyi†DyDy—iDy—¡Dy—’Dy—“Dy—Ž?€?€?€?€?€?€?ÝÔ?á£DPéÜDyf|?€?€?€?€?€?€?€?€DPØDy)?€?€?€?€?€?€?€?€DPØ Dy(?€?€?€?€?€?€?€?€DPØžDy'?€?€?€?€?€?€?€?€DPØ1Dy&?€?€?€?€?€?€?€D2Þ…DxúVDy—€?Ýã?€?€?€?€?€@0-HDQ¡úDyzDy—?á¡?€?€?€?€D2Û>DQž9Dy?Dy——Dy—‘DPéèDP؆DPØ‘DPØ„DPØ$DyˆDyxDy——Dy—ŸDy—¤Dyf…Dy)Dy)Dy6Dy/Dy—…Dy—”Dy—‘Dy—“Dy—?€?€?€?€?€?€?€ÔÊ?€Ö¨DPéDyfB?€?€?€?€?€?€?€?€DPØ~Dy)?€?€?€?€?€?€?€?€DPØŒDy(?€?€?€?€?€?€?€?€DPØ‹Dy'?€?€?€?€?€?€?€?€DPØ8Dy%?€?€?€?€?€?€?€D2ß’DxúUDy—?€Ôã?€?€?€?€?€?€DPª$DyDy—?€Ö³?€?€?€?€D2ÛÿDP«@Þ&ÚA€Œ>ÔËAKøÙ@ð ç@àèÈ@ÿå£@æg¹@Ýy@ŒœÞ@Ã¥ù?ËëBA"„A{ÀA ÙB@•-@û©1@öXAÚ@âä?>îñ@ô¸ÜAE_T@²â®@…Ê@õÊ4@wQ@Ëèö@­cN@>à> &¿A7î‡@lé³@‹ÐøA"ÛÅAGþd@Üå@¦úž?ˆÜ?‰Ñî=“‡öA»)@܇%@ú0G@àpA&r@Ñ’ì@/~Ô=ú B@§ ¿@æÀA ÷B@û*Ú@ÍB#?tëR@cŸç?¹…ò?³$ @ .@[™?»ñß?$h@ÝT?Ç›˜>¯Yµ?£O<­­?X“>«›?»²@>Á,>ë>ÿ®²@†^]@‹ ×A±Ð@ð*=@ø¼©@’YH@›Ë[A~š@ŠEƒ?ÍË@þ‚M@Ó˜…@ê¹5A1ëG@ßš¥@íXà@ÂÌÌA/ÀP?òÝÈA tpA$W@ùc`A=õA'´A#Ê5Aû@õ®Ý?^J@à)A J·ABôµAD\@ôµA&ü+AµˆA67Š@%‚@z}A"¶AÜlA'ð@ÍËA" @ÕÁA'OùA‰\>"º@­êøArw@ GIA+p@«nA<*@»¹AV'@ 0 =•çC@èÎìA#ú‹@ç1@ï”nA¾ð@‰ît@òºÚ@ºŠü@ñ¹Ê@±²¾@¾åQA(ûè@Üèû?¡ã*>62;lŒÏ@~A‡?ÚxF@º''>ÉÃÛ>¯{<:E±?R—'@^ í@.J½=´àà@/¦¾?Ó7Ä>£¯?&Z~>»n9?Ÿ]@lÅ‘A1dÔ@i&0@»FöA7ã1AG“AYA .@ùÝÐ?Ú A NAR@éZ@å WAw¦”ARA/ó@­xÑ?wx@ë‡A Ü#@êÏm@´i„A&hçAS<A‡@wÇ´?ý“Aà\A)¼AH!T@·VË@ÈOîA@ËÓA.\H@¸+=F#A.žßAeMÁ@û•HAiA)‡>A @ÂHA f(@07]AæA*ÙªAòv@j¿TAðA p@·^Ÿ@zŠ­@^»ABYANS©Aƒ&½AGåÎ@œHÈ?úb=AJL@;T@Š%ü@ìVAB @Ý—ò@ÎÄ?‘à©@±è–?N >ƒ/<>©ß;>+„<>ü1><–x@]:|@xp=[›Ö=ŽÓ©>PLVA,qÓAw÷A±@ ?Ë@ÒÅ(A ê@áÄ@Ýà!@>KD?àÿeA{£A\ÓAZ¢@’„¤A%~@ì?oA-¨@‘¡;]‘ÚA/3îA4"º@öÓ5Aâ’A<€dAÈÏA/lå@˜z>°ž‹Aä#A ‘¡AV#@ºöA8 ’A=!<@÷òA Á?|ÓßAb‹žATë@øš1@¦qÜA<}nA e^@ìñ1A Ù@ÚBZ>ñÓlAĉ@èví@ôv @—r”AT0\A6¥2@s@!#½@S·u>Âí@Ýâ•@Óöë@Ë–A$ÊëA6ög@Ìëu?#×ì@±;A6r@â,qA*±u@Ö—‰@‚@žû³?Ýu@0^J?©uÒ?€#<Í9¯@ @å«E?;èY?NQ‡@¡ ?»hëAZÆ@Í×÷=A»A)¤°@Íx2AM¸¡@ÿþA2lC@ÚA¯µ?û¦Ó@žÁ Aì @Ó}A3Û9A#Ù>A @ä¸È@àA±?ëŠø@çq™@ùo„@÷ AdåY@üXø@³2Â@µ”!@i–7@!^?¬(A–‡@ƒ3@ôÞBA)Ö(@ô¿"@ÄRŸ@jÅ ? ?q?Öp`>üoA† þ@´xAÏA"eæA(ìVA “ö@ä!@þ»@¿è@ž Ý@—Êç@˜ãÁ?Ïáå@¡ÙÙ?†ä1?¶|Þ>}¬µ<ÖÊæ?çd@•;Ö?&;>UD®>-U>°þ1?Zœ8@=Ï>ô$3>ÐCx>©CÄ@»Ú™@þÉvARêAMÛ«AnŠ·@ÅGI@”á@ÄJO@»*C@%@ÉãAb$AM†?A($AklAú^@–¸¥?Ÿ A @ÝŸTA+îÌAE†A).]@ïÜ¥A6@ °Ä?H ì@›C:AÉ—@ó’KA ú_AŠA(Æ@èU?·$Y=¯ÃÉ@øWÓAÖyA …@´á¬@ìö$A<"@Á«Ö@²_?â8Ÿ@?ßAdBA$ë@Á6^@¡iÖA ~þ@ƒEÉ?uÀs?q~@ÆÙ @·þtAP'÷@ö„@UVÐ@t Ã>Eb@•Ì@†Íu@·6'@_03@Q˜Œ@éÈ?«³?N6?Uêé?‘€u?l‚Õ@ Ü@€2Ê>¨÷?S‰c?°W¬@[?æõþA(p;@Éë¯@Â"J@ƒÎA>(I@¡«ê? >Ž>¾z°@…§áA eÃAYßAóé@î¼AªiAq@€~‰@8A?ŽÁ@þå@Í<A!3ÄA@§âºAã @®ñk?”J AùA Úþ@ìý©AcûL@±I @~µÉ?›£@özÅ@³–‹A'X:ASyAK@@ý¸¾@‘ó>©Ž3?¥Eä@9"@¸Ä@ÿúä@ÛM?Çè?ÖÌð?5´ö@ßÿÑ@e‚?‡¹¾=±|@·Þ?¥¿@ 9%@‘ñ{>¶Èª>ßII@~ A:e•@¨r—@íDd@ŒÓ<@Í>?`—@?¥A§@ ^@œ&r@¶Êá@äh(@Ï¢A Ÿ@!‡@èü?Ìdœ@®† @èDAh†Aã>@ÍÆ?Î/Ã=Êá?>³ ;AE‰AwæAÑ@áL@RŸ@*Åd?G ù=Þ/?hÅÿ@ÂÛ{@ÉS<@Þ@ä©¿@ ò:?ÝŽ€>ˆ¥Œ>Çü$@R7Q?ëúÖ@ÁVË@/R-?|ã5?»Œ:Ü<ñÌ?ÃÈ?Ü|ø@®L}=BW…>Ýa@.´@¸|$@M@ü@JÎ?ƒ•W?Þ)>½ˆz@Äîë@Ù—?À€?¬Xï? Gt?ëZF?dUØ@4ˆ @´.e?¸D’>Qüç?]î>+¤@4›ý?¯9i@ƒÝ@Ò†§?ñ!!?Óâ>çW,?Ì—$>ùÃ`=]?óH@ Ü@È‹à?Ú¾(@¤>>_rÊ?H–>0$ >Á‰@éÎ>/ @tÑÁ>Gº€>|w??^€\>€¸À@Æ+Â?8ÙG?Ý.@Λ=Mn@žì¢?¦p=>•ÄÜ?ÙZâ?ZƒÀ@”†:r´d>ŒzK@Pqi?ÙQ©@‡èg> ßÝ>”ô¯@nì@+2N?óÅ^?`§@HTÎ?Ç“L@=µ6?«%=èy#=aа> Ý?@0Úò@M?Ó˃@ïæe?¢é>äè‘?ɽr>¤Äº@¢B¼@ ´ç?@%Û@)Wª@ Jê?¢ê>ZmÉ>ÿô|>^šÑ@þ1è?¢õ^<¼h=\Q’?í¿×@F=d<…$c?rv@‰ ¶>§&?ÞˆGJú" ?вëÕŽv2?ßÁq¶ë?Í+ßœ>?ÕÜ.†>?Ý厃VÀ?È¥”À#?ÞÍP–Ê9?ËëlÿP©â?ßüêMž2ã?ݧ=}µ";?ÜŽÛmB ¼?Þfß%Óa$?ÞØ8.Ïé?Ý ¿ééæ.?ÝYÀNë?ܸ—´D0?ÝQ9¿Òú?ÜK288ÿy?Ýéõ€‹³x?Ü¿¯*Ëj?ÜÈóCõ?Ý‹$Ø™e£?ÜUÞ~]H¸?܉CRgS?Üä3Æp>?ÜÌ/“ìº?Ü„?|ëT?ÜÉWµäzè?É®@üÏHZ?Ì7}ÒxÌî?Ðn·\<]¼?ÒOšö &I?ÕùÝî[e,?Õ‘#æRó?Ö{’åóä?ØGvX†I?ÚÓÏ>ôÎ>?Éöq‰ d?Ë]<4Ù…þ?ÎöCUA†?ÑEàÿT*?ҮǫWÞ?Öñ÷Âäû?Õ)¨`.X?×£Õdeê?ÙWÇc½?Ú/ :²?Ê §ètÝ?Ë#³ôØ}?Ì]ò€?ÑRM0?ÒÅ/Ó8/?ÔQnÅ,- ?ÕÚ1¯£f?׆hßéiõ?ÙI(@*Ö-?Û¼¡øOR?È™o+œ÷?Ì %Š^?ÎN͉Þ]?ÑXsá-‚?ÒëKoß?Îék›½b?Ñ"t©Ò9¸?Òx¥e,?ÕD“eÏ?Ô5¦Š4LBT?Ѻ=‘?ÓÙµæƆ?Ò-γ$ú’?Õ .ž&ë?Ö[b¦i;¿?Ù/àéO»?Úêiðu›?Û¥æB?ÉyÀ§§i×?Ìl†Bg›/?ÑpÑR£?ÒiO*˜2ö?Óyñ-¿ø?ÕÕ=§øxq?×ó=°Ï!?ØõöLsë?Û«Äá8aÄ?Û¯’ 3?ËML(i¬¼?ÌlNDÌ'M?ЖëÝÝê?Ó†åI:åÐ?Ó™i«)÷?ÕV€¸«F?×W<ƒ* ð?تOÌäž?Ú{v…œ(?ÛjDY爎?ËÑ,3?ÎÄÊÇ7 Í?Ðè”G \s?Ò„ÃQÝ3?Ò™ {¤E3?Öf ƒ-\?ÖÇ#wŸÒH?Ø®gä1?Ûõpk“¡?Û 3 Ãm‚?Ê‘ÚËpMµ?Ï6«’ÈÊÓ?ÐB>[0?Ó(>ÿŠ®?Ò*·îÊ?ÕÍõœlø?Öç‚8/?ÙÏðÚ?Ûq¬†Ò ½?Úr“,®—?Èù¶Áã?Í—ˆ}(?ô?Ñ(й£0²?ÓDÆAà-?Ò£³–(Ê’?ÕJˆžiÁ?× ;Q?ÙÊ?Úí—˜]w²?ËèD?ÍUà &¹w?Ðb7E6¾„?Ò©#šã¾?ÒI²àk¿Š?ÕnLãÏÀˆ?ל°¹?Ù—Ñ(î%?ÈU†D¶–T?ÊÀS\Z?Ï(ÛzŒ!/?ÑóGx[(?Ó…‘G6MÐ?Ô;ښآM?ÔNj»m–?Ò|ÛYy@?ÓÌ)ú.!?Éè§Î§?ÎÁjgf«?ÍV×ÿ×Ò¼?ÐK€¿#?Óé6DÈ=?ÔÔËÊ^?×ÿ•Ì%?×,X1Γ?Ù<´%Ð"h?Û¹I„¶a?É,ø«W?Ð’fý?ÌIÅÿ4ñc?Ñ¥á²Ð k?ÔDmä}?Õ 1IKt?Ö}¦äâ<È?ØÉÇèà?Ø”šÕRóR?ÛÇŒŸk™?ʰ[CsO?Ήc‰æ?϶ô‰Hú\?ÐGs¿9?ÔúF›‘÷?ÔQêÖÆ_–?Öäg¥‰x?×J”g h?ØØûQÓ¨?Ûã@¾>ü?ɤ‘ €Fc?Í-·¹XÕ0?ÏÓTº{K?Ñ%¼³Sf`?Ò$žšnj'?ÕêYÖ ³n?×Ûõ€’²!?ÖÓl¹Hr?Ù¦ßuÁ¼?ڢȅ¤Ï?É”èð¨‚M?ÎiIÿAв?Î,q ,?Ñ\õ0’!?Ó&hв2'?ÔËç¸9ñ­?Ö»'Àˆ>?×% "Ÿ_?ÙǼJL?Ûµ^H;?Ê<ª#CX­?Îæjý‹Ë¢?Ï«%Ž*ø?ÐBW¨?Òm#'ºp•?ÕQ³s¬O?Ö²ò×%Ç?Ö®ËCtÓ‡?ÙˆLÖ‡s(?Éû±²:I?ÍÌØöþö¤?Ðí¬O'Ç»?Ñ׆°Å?ÒPîì¢Æv?Õ.b²?ÖŽX±?Ù‡ Û;GÄ?Ù7?»ùE?Èø©…¯?Ì^>$ 7û?Ð2­…s?И‡¯`[c?Òj3!€‚}?ÔÄ·¡½L.?ÉôS/s?ÉÝñßt‡?Êzš˜H‚q?ɸ¹'c²z?ÏÃŒ˜Hâæ?Ðì2?ÖV¾—ñÀ?ØÄ;E0ŒÔ?ÈE†bíH6?Èö÷´ÁÇN?ÏÈŒÁà§5?Сê¸þ?ÓÜaä¿B?ÔeÂëM©:?Ô Š?KP ?×OÙÍ®A?Øs®£‚ ?Êpz4?ÊÉc1cº8?ÏCEuc?С{Ƹ֣?ÒÚ¯ÿ¾”ð?Էljû¹§?Ô¤ód£j!?Ö¦\û˜?ÙY¬_‚ù=?ÈÞÚÔh?Ïf,%„/?Ï%«‡'¤*?К?²ÒóA?Ó‰mBèM1?Ô*Ú{ãò?Ö5Ú]€5 ?ÖÄãox]/?Ù¾™CdΉ?ȹE¸8A?Î8éΧ8Š?ÏnŒƒRV?ÑØ""‰?ÒÌ'×dÆ?ÕW´º^Š;?ÖÌû•s.Ø?×[ìŸ|?ÊgRoª.?ΟB|¬TI?ÑO… —œû?Ñ÷BB[ ç?Òôl"w”Å?Õ ÅS‡ú†?ɹÙ¨A?Éß©#µw¶?ËŽäPþ?É Ç6êu?È+n"ÒZ\?ͨ_•Þ¿?Ð|«}¯ÿ?ÓÕÑÙm“h?Ôá£Ó*ñ?Ô¤÷aøw?Ö™[¢Bì?Ézà©Þùœ?Í7Í.a˜?Ì%œ§§?Ð¥Sʼna?ÓÐ{þK?Õtëÿyô?Öv¢ÝN'?×÷YÂÂr?ÙÆ}S5F?Ê·ìˆ?ÎãË Ü5´?Ï ¤gp2?уákædý?ÓÂÙ´ò¢±?Ô¢Û\Øc?Ö•»?HÌ?ÖÞ&^ß?؃2× Ö`?ÈçǦ0Ñ?ÍõÙ8JDé?Ñ9`ãT†?Ñ<à볤’?ÒÜKöùÈ?ÕéYË`îï?Ö€Îï31ð?Ù}Àºµ?ɸ±:{ï?ÍïÙ7›ñ?Зǯšå?ÒúÌ.ðLe?Ò?ÑÈqö?Ї?ÐBÓÆãË?Ðи'²ïY?ÐjÇ{õ?Ð{Cw'*s?ÐÓ° Ž,?ÐD{ }BZ?Ñ$€¹à ž?ÑÀiè×B?Ñ~%jPó2?Ò‡ë^eþf?ÒÙýè„B?Óhy@Y?Ò›k†cØ?ÓbDüÚØ$?Ò>ó +?Ó aröÌ¥?Ò̯ےpÉ?Ó<”¸IdÝ?Ó0|œÁË?Ô.G1ËK?Ôƒó#ESC?ÔÊ¥VU8?Ô–]Á“^?ÕÀÝ‚ønr?Ô@>¨1=€?Õ?p“ƒÊ ?ÕO̬rJ?ÕB¤Ai?Õ ¤4δî?×é! Škà?֨ߕߴ?ÖA6r#º;?×ÒMyǼž?Öð³Ì“V^?×Çè?×=Ô\Áô?× ™¹)?Ö!Dæ± ?Ø¥òÊí?ØÉí5ÙO?Ùvš'Ê?Ùz§öM“?Ø¥ G!??Ø6î8êgÀ?Ù)|ÿE%?Øb¦ˆ0?Û…¬ìF?Û½å b^v?Ûê5eëÕ?ÛHhu‘^?Û?˜FB?Ûdèiý«Ñ?ÛÿÑý/8H?Ü„žúäE?ß~Ô÷ h?ËëeY,@Ú?È–ÖçñÛ?ÈŒÖÖ ’?Ì 5šƒ©?ÈtV°ý‚m?Ê=j2e‡?ÈÇoV×4G?ÈŽæxñ?˰¼Ñ2ý?ÈËÇ/$r?Ϭ îǰ?Ϙ mO™?ÌÊ7þ›?Íiè=Y3¸?Ím€D—Ø?ÌŒŽšÓ&×?ÍûùOÉür?Ï W0Å4?Ín¤‹5?Ï=룵 R?Ѻ ß¿³¿?Òlbñ?Ñ«™Èú°?ÐÚX2]í?ÑA˜óЧ>?Ñ‘é‰õØ?ѢݯG%?ÑþÆ__d‘?ÑÝ*#ƒˆ“?Ð’?¦‚ò?Ó@ˆÅ:ÑM?Ô"7„è¾?ÒKó@Ð?Ó P[Ÿ?ÒC2Ü-¯ä?Òät ;zq?Ó¬±‡‡…1?ÒZ•Ö´ó?Ó^Mìh?ÒÈé‹+?ÔÙƒÇqË)?Ôõ·øef?ÔoFüiŒ.?Ôƒ?!k;?ÔjºñîÀ,?ÕGô—Óm–?Õ² búGX?ÔI‚¶¿E?ÕÌ•°C’Õ?Õ–Ñ=õú™?×Ä9[lC$?Öʃz XE?Ö§W;:Q?×P Ü?×Ghlèç!?ײi:ŸÌ?ÖﺂŸ3?דåLZô?×ò™ÖÊnÿ?Ù§ØõP"¹?Øê‹„=Ý9?Ù[Ò%.„?Ùâed^ò¢?ØÔ“b±þ?ـ̱,'¤?Ù´{ü+?ØøgÆ¿?Ü ©õÒ?Ú9Û‡÷?ÚGº$k‡ž?Ûº[?ÛŠ¬žÆú?Ú€N›Çï?Ú)éò$‡ ?ݼÜ4Ò?ßõò_T?ËpœuŽƒV?Ééqxúˆ ?Ê_J[ƒJ?Ë´üê2“?Éjp—Š×÷?Ëoü0b¿?ÉC8KBp—?ʵ£½¶V?ËIÇ‘?Éî±MaÍ?Ï\SõY ?Í­ð¼ømµ?ÌôWµ%?Î࣠6º?Μjê»?ÍsèYˆG?ÏÝlé#ÂY?ΙjÄ­Š?Ï}¼2" ÷?Ì„¦Q ¼›?ÑFPþÜê?ÐO³.Wäc?ј©¥›WE?ѽÝëÁÇ??Ð,våùZ?Ð’ó¢£Uá?ÐEóÿ´/?Ðïì\¨¬?ÐøXkhm ?ÐZ—5Z ?Ó¨-‹ùòÄ?Ó³=¢1Ë!?Ò§w£9Ü ?Ò™[‡‹Á?Ó¼!ªF”S?ÓæMõ:ùc?Òß„JÄ ?Ò¯+§*ªv?Ò ^|›°0?Ò²ÿÀQ¬;?ÕG<šøë±?ÔôKûêËx?ÕÈySº ?ÔA.¥û?Ô†(,{&?ÔWFÏ‘–€?Ôj9?ЭëàÛ–¤?ÑpÙNŠ>?Ñ2ˆ×(tÔ?ьņvøŸ?Ò vr~øR?Ò ›¬9~?ÓιÏHH!?Ó# „Æìš?ÒŒ×k“†‘?Óߥæ»ö ?Ó*LŽŒ¢³?ÒE®á[Ìù?Ò{OWЙM?Ò•ã|³m5?Õ«ébzE±?ÔÕÂY-±?Ô4IW?Ôß“ÓpKd?Ô–×Fª”w?Ô=ÎNuä?Ô3fˆ²€ë?ÊI©Í Ê?ÊF*/ÖåÛ?Ë€L™S0\?ÊêF¹¬?˃ܕ­?Éhà¬9Ió?Èð§» ”Ù?Ë S½Äì?ËÜRIÖ?ÌHN4¢L?Í'¸ÑlÑ?Ο‚‰Ö?Í‹Px4´Ú?ÎÑâæÕ?Í—Ø‘ —‰?Íãù(xí]?Ï$[†Äâ®?ÐÓ,"€^¶?ÐáD>Þ¦Ø?Ñé>4U?Ñ%¨½N G?Ð,.寉6?Ñ~íjàzV?ÐKŸ# ù?Ñï¶AÃFE?дŸéÐI?ÓÉ9ÅÃYÜ?ÒT[ Á ?Ò—€Uc,?ÓÑ©ÖB!?Ò§CœiJ?Ó&t…%©œ?ÒxëAÕh;?Ó9h­ôE?Ò×sù­YË?Õ°AÖ9?Ô”Iž[?Ôn3Ì‚?Õ߯H\w?ÔÌG­ðt??Ô‚×Váf?ÔXæÏqZ?ÔäïÛ<Ö.?ÕˆÅ.^)?×ùùÒ¼%×?ÖÒ+–)ºJ?׆¼í›ë«?׿եG ‚?Öw"ß›÷Í?Öêß»}­¬?×¹Ù^*ëÊ?×`©³óÍ?Ùš}V]K?ÙÃQ@r)z?ØÆ»KdÅ?ÙéÍ„_Äs?ÙÆ5I´¡Â?ØÁU.æ?ÚoŠ•!iÍ?ÝøõÆ£[HO¥?Ý8,Ïý“"?Ü\c2¸™$?Ìf¦ ?Ðj”Žî˜?͘€x³Ë–?Щ¿ÕJÄœ?З­æN?ÓÞ†í?ÓdE¡*ð?ÕÖIá@á_?Ô ú•Â?×l$!]Ô?Ø :çf”?Ùzý0Ö1?Ù„\—!—?Úų¢¿³?ÛÈ™¸õJ‡?ÊYÉ=­U?Ê39úÀÅè?ÈÒ×gärê?Ìm| ZÒ?ËcìW~4?ȘnúXnE?Êѱ<ÖI?È5Ž;~?ÌõSn?Ë˹U#?É[0f¶i=?˞λO?Ë‹¢¸´?ʡçX+?Ë>¤ ò|?Ⱥ·E :?È`ŒU\ñ?ËfìYDµ“?ÊU2EE˜?ËY”.ÜeV?ÊŠò¨B¡W?ÊópG„†?Ê“Ãû/÷?ÊP2Rž?Ë›D·É?Èi^®)2??É@ðKÆ{Ù?Ë,Kô³¿A?È"-ë®Þ,?ËÃSs¹?Ë}y™“ ?È“¾ñ³þ?È«ç+ž0?Ëg4hÓLÊ?ÊÙ‹_ª¨?Ëëµh"-Ü?ÈiµT›?Ë˹»ÿK?ɸÑ*Ûù?ÈU¦t»²0?ɦ`õT!?ÊFÒ3V>¥?ËË®b"Z?Ë"»Ùœñ{?ʲ[¢¹É?ÈGÞoƒ?Êû¦ “|?ËÜP¬¼?ËÐÕ1ñ6?ɲâ”?È[~ƒ-äo?Ì¥Ž@?Èܧunž?Év£ªâé?È%^,#½?È©W'~^E?Ë7L@Æh?ȺHa'?Êõ[}Âi?Éñ‰XMŒ?ʦrç¼gì?É —ÎH0?ÉúyQ‰?Ê»cxT¾?É¿±=ò=?Éû‘­‚á?ËË…©z ?ÈxŒ×Á?È‚œºÑ¾?É3Wú†|ƒ?ÉQÐF[‡â?ÉTpJ'IØ?ɽØÖÙK(?ÊBYØ V?Ê·J¡>Àe?ÌÞ$Žè?ÍéÙ$¿_Æ?Í#g£ÌÝq?Î˲Û`%9?Í<Ù;—¬?жœ(çÓ?Ì­¾Í;á?Ï­ ˆÛ÷œ?Íç¡™Ç?Ï3ÓŸÿ¸?Тrx»?ÎY\˜<Ž?̆>x÷!T?͇øeùJ'?ÌT~#¥Ñ¯?ÏíE ¯f#?Ìv¦l`y?Îæ$‡A?ÍrÐ3ÇE@?ÌLÍÿ¥Ã?ÏÒ\ÈÞßQ?ÌTæ% ÷?Ïh¤c££?ÎYê>¾?Í[à ¶S4?ΊBqé[Å?Ï[‘Ö>V?ÎÕû.·(?͇P.÷?ÌAâA‚?ÏÛœÞd?®?Σò“~«Ý?Í w‰é?ÏKB_\b?Ìwfm4xÜ?Ìiî` Ò?ÏÕ èsWÅ?Îó7Â]@?Í‚8M’Ð?Ì¿àfCD?Ì‘¶–@gc?Ì[¾7 ï°?ÏgUû?Τ2”#Î?Îù34KÅr?ÏtŒ/KÉ?͸°â€Ô`?ÏÕôØù¥?Ï;K¤öbË?Í$µr¬C?ÎòÆÙ-j?ÎRªª«?Ðv—Óœ·?ÍJ×ë9Ý?̆–yÆh9?̾èR m?έB«ø?Í<·àØŠÙ?Î0¥âÍU?Í#Ϭ…]?ÏÖäÚ™‰î?ÏÓ]†TÃ?ÏBÓ¼ÀQì?ÏëýÑÚT?Ì^ö0å@?Ì.ÝÎˈÜ?Ï€4) ¥v?Îo £¸?Í?_Á–š?Ìc3h^õ?Ì·¶É ý?ÎæÊø2ë ?Ì >¦j=«?Îÿ£392¾?ͰmÔ5§?ÍSÎ+b*?Ѻ9áöÓ?Ñâ"(uaÍ?Ñc<°“?ОS¿>ø?ÑŽ¡†Žïà?ÐÂ.ï?Ðu·s•á³?Ðú si‰?Ðë¨Tñ_?Ѱ]ÊÏ c?ÐÆÜèî-?Ð{WìG|?Ðq?qDIè?Ñšm°HŠ?ДӬFN?Ñÿª[ësg?ЊֳêŠ?ÐåhLÃ?Ò&i3­‡?ÑFXú#\½?ÑðvCKþ_?ÑÁqí*¡?ÐlûaÒlZ?Ð*áN=ì?Р?Ñšéš$Tì?Ñ Œ§H·?ÐTg09®L?ÐhëXc~?ÑvådåR?Ð:ÊVmk?ÒjgÞn’?ѧyµ=È??Ñh ?ÐŽ¥¢½‡?б£äbÔ.?ЀÈN“„?ÑxÉ]8¾?Ð@ ›yH?Ñ9LñM+æ?Ð+Þì-?Ѷ‰×ƒi?УoÂKÒ™?Ñ0¥'¦±?ÐÆD ¯¶E?Ñd16õ´?Ðæ4Jj¥?ÑC8ûf¶8?ÐV¼gŠv?Òv‹Kr?Ð!rÌø=?ÐŒ›—Sßb?ÑÉûJPô?ÐK!jUÓ?Ñ(HÈùÛ?Ð 6Ͻ,­?Ð:Ky~ ?Ѥ­¶ê|?ÐŒ7šÐ“?Ð,æãÛ’?ÓØÉá›d?Óaqoy ?Òd[ÿ Ò?Ó`ÅÐŒ=?Ò4VƬ0?Ó¾ °éÐ?Ògk"\Hã?Óˆjܾj?ÒLJÚp¾'?Ò'ª6ó÷?ÓèÅúL ?ÓÌI΋¨?ÒŒGlh i?ÓÄK*5'?ÒHzí ‚?Ò*^«+t?Òkc(lì?ÒQÚöùa˜?Óf} ‰Ó?Ô¦F×=?Ó—a²*Ô?Ów]&>ð;?Ò¨ƒ¡`àº?Ó„ÅD™÷0?Óà±ùòþ?Ów}*KºØ?Ò#6¦?uŒ?ÒÓëëP»?Óür§Üî?ÓýN2Eê?ÒvC8r?Ó·q˜vFB?Ó‹õFñ}?Òw£>k_ø?ÒëX%E£'?ÓEdÒ¹óì?Ò§G¥w}É?ÓœõlU+»?Òh;"Gîü?ÒÛÕhm?ÓÛåÛ¶˜â?Ò¥÷’ ý‚?ÒÃëȃIÈ?Ò3obˆ(?ÒÊ‹çØWE?Ò«uȼ?Óa&ÂŽ?ÒÓëð¶U?ÒËcßtÜë?Ò´¼Î ˜?Ò\¯ ¡?ÒÔ›íßÌA?Òà€œdâ?Ó ÀS²Ý×?Ó¥‡ÂØ ?Ò¶oÛî?Ò#‚§,N?Ó[Dóœï•?ÒBÚÛ„l?Ò|/MFå?Ó¾¸ä:;?ÓÄaºÈÉ?Ó¥i„ê !?Ò¦ƒ?Ò}WFnï?ÓÇ)ÆRð?ÓÏMáÞy°?ÓRä÷6¹Ì?Ólpñ@Œ?ÒG¶ôD?Ó¨} —¡?Ów‰.=ÖM?ÓøX>b@?Ó¿Û¨q#?Òà4u<ñ?Óa!8¨0X?ÕƒÉ A›?ÔôÛøi¢K?Õ©Í_à³2?ÕíuÝþÁ?Õùa÷9õl?ÕM0åP?ÔBêªú¯‚?Õ…É¿Ë2?Õ›­NÄgÒ?ÔÏÓïë?Ôwc˜©Ê?Ô˜;D¿ª?Õð¡èv8-?Õ¸~>Ûq?ÔëõuôÜ?ÕÐ!£îÖ|?Õ^ÀưVS?Õ ”%TYƒ?Ô¥¿r“Š ?ÕÝ)Ä®%?ÕØ¹®Âw?ÔMš½À?ÕâYÎ/»?Õ™á={ˆ?Ô³ã´Z6?ÔJÁˆ¦?Ô°Óxß~d?Ô`ââ<Ü?չᑾ)"?Ô+þŒ³¶=?ÔûXfòI?Õa8É{‹?Ô…#+Q„?Õ¹ù{°$×?Ô~G'(„?ÕÁ™‚Åß?Ô8z”“Ü$?ÕÜQ¹$âq?ÔCNÂ3¯\?Ôb¾ìcÃ7?Ô¡·U ­ï?ÕÍ9œ+:e?Ô/v‹çÌ2?ÕÐ:äzN?Ô×ʵMZ?Ô?†¨Wf?Ô4.nf3?Õó…ê6Ó?Ô`êÎqº?Ô̯úl?Õ³msÅ,£?Õ ”<>Ã?Ôø V?Ôøœ¯ƒ ?Ô¦›eut]?ÔšÿTá.p?ÕÿÎ +:0?ÕlXÀ•Í?ÕØED@¿?ÔRhµv?Õ¡ÁTæ­?Ô–cMYÑ?Ôƒß.{¨?Ôíõ{ñ¤?Ô˳¿=¤¨?ÕVœÎŸ¤Ê?Õ -eÊk·?Ô öXV8?ÕûÚd\‚?Ô}³!^s?Ô7úºUx?Ô$*«Ü*9?ÕûÊJ†Æ?×õ‘ÇÔ1±?×xøä½ ?×vÈ̯eâ?Ö–'4Ѽ?Ö“CfŒ>?Öã§­{ö?Ö{äGÐ?ÖW.£óS|?ÖüÃÿïÐY?Öº‹c|â)?×Ï?×h8°Õ¹Ô?Ö¥Ç<õ?ÖN6—…Úy?×FÜk/^\?Ö–KÁº‰?×3˜JJò>?×èÝ©R!?Ö¾{kOº?Ö“·5¿o?×—Ý X;v?×—ÈÿÛGZ?ÖÑ_‡‹[Ö?Ö„¢ù?µž?×2h=üõQ?ÖÓˆÿÌ?×ô1Ĭ.?׿‘§jý?ÖJ‹¼„A?Ö«IÜþ‰?×ò¥®×…?Öâ÷£j£b?Ö,zOƒÂ?ׇß/•¶?ÖÃB]¶?ר t?ÖDâ…[0?ÖoŠÖï<Õ?×k<»òx¬?Ø 9ÝБ¯?Özâ} ¨?×W@†•ïä?Öæ³³¯~?ÖâËÈ! ?Ö"ÂC,vú?×»Q_,k7?ÖþõÖò©?×Hxz#mÍ?×â)˜‘+ø?ÖhúÄ•?׿õi`ã?Ö–ç)Âø$?׺ eÒf?×4L*›?ÖJª›Š^–?×  >0?×¥6â±û?׌ã’?ÖæB‘„›?ÖËËßä ®?Øc~:¨?ØÖ³€„Ø´?ÙfЖÏLE?ÙÀq4ùÆG?ØE’T N?Ø«¿ suè?Ø•^ãi+â?ØŠJË„ÒÛ?ØÖ e!•½?Øýÿ¾ÐÙ?Ù8È)'SC?ÙkLŒGhê?ÙÛÖ¢5?ØÕ«TÈm?ØÄ£9›ð?ÙSpQ uÆ?Ø-*'•§?ØþÜG³½?Ù‘XÔEÊx?Øyýl©?Ùv¸œ®Ÿ?Ù‘XÆz$‡?Ø]ã÷p?ÙâmZÜàÃ?Ùv”“´=œ?ØJ:QÏB?Ù£Êbí]?Øò­–ú«?ÙöŸît…?Øïo•‹×­?Ø€¶ÅŒØU?Ù®¤ù|µ#?ØjJ‰¿K?ؤúû·9?؉Fî‚k?Ø-F11ü5?ÙƒÍxm?Ø\F…ˆÑX?Ù+Dc‘‚?Ù-¨¿§ó?Ù¸YÂf?Ùe(~ög(?ØÈOm“en?Ù¢‰[Çü?Ù‰ìÔ9ù%?Ø1ê:”?Øvþ´íŠì?ØÕCxï!m?Ùr¤¡\5.?ØÞg­Z+õ?ÙA4OIHr?Ø4nJ§úP?Ù,è\î_¨?Ø MÆr“?Ø^pöªß?ÚÝgXÈ“?Û7 ¥«?Û•Ä,©?Ûß×Zc?ÛðÑ•[¨?Ú]P¬¦’?ÛÐ%@Ф ?Ú©Fóà  ?ÛÂ9lS?ÚaÍ ¦Ò?Ú° å¶)¸?ÛÒ±/Z‰c?ÚòŸuÖšm?ÚÞ9+È?Û“<¼ ‚&?Ûib/1Í?Û† “Q6Ö?ÚMf&¸ ¸?Û+Ó‘µ"?Ûnlis4q?Û¤3ì®?ۋصò„?Úálœó?Û0gë•Ør?Û³dõ&ÖV?ÚÆ®5?Üy¥„ªA?Û0[ï»?Ú+Eþ†+õ?ÛŽ”¬7}?Úõõîu´?ÛU ˆÏ?Ûõ‚wö6?Û#tÆÑ?Úöh,rX"?ÞŬ¯9?ܲÞïORK?Ý|0‰¬ß÷?Üiªd÷é0?Ý©ý<\­?ÜBK½î°?Ü+¦Eke,?Þ ž&ù`?Ü2NˆÄ>c?Ý€½;¥U]?ßÕUi/h?ÞB’*J`Y?ßæáƒ´±û?Þ†± ;5?Þï¿ ɵ?ßûa)?ß¹A`Ü0O?ÞägÒ@ ¢?ß“}LKª…?Þhóllç?qÌ?x‰?p÷??kû?t=?‚T?wØ?}Š?tã?pl?t_?uj?wÒ?_û?^J?]©?]ë?_+?`y?`N?a?c"?e?hp?jj?j?l=?nõ?„?€a?z%?u??iþ?k¹?ht?d?]â?ƒÕ?Ä?|ú?xÆ?t#?ik?l7?e?a¬?_?„‹?‚8?€E?xD?s‹?nØ?j?e/?`£?^?‡??œ?}O?wD?sh?j?m?g¿?bã?_°?„÷?‚?}µ?vÿ?u9?l?np?h'?b¢?_æ?„ï?„,?~ù?y¹?pì?n ?l?f?cc?…[?‚??wÙ?u2?k?nz?f'?‚l?{ü?{í?z‹?r2?o?m?]²?^t?…P?€¿?xc?ud?q·?jU?d?bã?\6?\?‚÷?€é?z?qé?qT?l?e_?aI?\Ô?[³?‚ª?~?y?s?s¤?j?gå?c?[Î?[Ó?„Š?|—?z?r?tº?j¸?hŒ?a0?_a?`Æ?†N??w`?rE?tI?kÏ?g8?ct?b„?T?€??ze?s‰?t?k?h²?e|?ƒ¬?‚?|Ë?vŽ?r?nô?nf?v\?vœ?†q?}þ?b?z?q*?n™?dh?eÉ?`n?]µ?…ø?{‘?€ç?w;?q„?k¶?gè?b?`e?[á?„?}Þ?{ú?zÆ?q?pà?fF?e?^å?Zb?†I?€ ?{†?w\?t•?jÔ?cç?gm?]_?[?†n?} ?}¯?vŽ?r7?n?h@?e¢?`~?] ?„¬?}G?|…?zi?t?lÅ?h£?gv?c”?…±?þ?xÐ?u>?sÍ?kx?i”?dR?dã?…š?€ ?{¨?z?t?m ?€Ò?~¿?„n?…–?{`?xU?sÅ?jÅ?jo?eµ?_ç?_b?ƒ.?„Š?€M?y/?tÕ?m?n¼?hR?_„?]Ü?…µ?„õ?|?zÇ?qé?kó?n9?d©?a?sÉ?r#?t^?q‰?uU?pÉ?r¾?r]?s?wÆ?w§?|Œ?{1?wC?t?qJ?r ?u4?uÿ?u¸?pà?r0?t?sƒ?m®?m?n]?iý?l%?iÛ?iâ?mÎ?lÞ?nÛ?i?gŸ?d£?hØ?eN?cç?g¨?fÄ?iÔ?ci?b?b?^?_k?cy?b?bq?\Þ?[Ð?Z½?[?]L?]?hµ?fû?j–?…”?‚A?†c?ˆ·?„æ?†.?…Œ?ˆ+?‚)?†?€ ?ì?|¤?Ç?}=?‚Q?}Í?}m?Œ?|Æ?x?y?zï?wB?{¯?v¼?zÜ?wí?x—?wô?uË?uÈ?p·?rD?tŸ?ot?q·?u%?v‡?tˆ?k÷?m–?ol?m?m²?nÁ?n»?mÆ?m?f¥?i¶?ež?h»?i&?eÚ?iÓ?i¾?bý?e:?dq?c½?`²?`3?` ?dœ?_|?^Š?]?^Q?^D?h?jX?jþ?„:?„>?ƒ•?…Í?ƒÞ?‡—?‡”?„{?Œ?{?€²?~Ð?€0?~K?€+?€?~?yË?yÏ?v‰?x‹?{ß?wí?{«?v¥?zW?pë?uL?t?pÛ?t?q€?td?qÄ?sÇ?m?n›?o?k¢?mB?m(?nG?lÃ?n&?fž?hí?fÄ?e¸?hÛ?go?h?gé?e8?b?b˜?`ý?bƒ?e ?a¡?e^?h†?„ÿ?‡?†m?ƒÈ?‡y?ˆ ?…;?…t?|q?|ã?~¬?‚6?v?|@?Z?Š?|Î?z©?{I?x–?w?wU?y“?{?z>?w]?p’?s ?v ?tz?v}?sÿ?rá?tÇ?lÍ?mˆ?l¾?nÚ?l!?m?oR?hë?ky?j·?fè?h@?ib?j?e?f÷?cS?e?eE?gT?‚û?‚÷?ƒ¬?„?„?‚?€}?W?€?€6?|ÿ?Ó?”?{v?yÒ?y?wÑ?{™?{ã?z™?t?rY?s¬?v?s?tÍ?u&?mÁ?o›?q.?l1?pU?k›?k…?kÑ?kß?kZ?ƒ?}?y•?u?rû?nY?m ?p¥?mJ?o¼?~A?~˜?z3?w?u?sü?t?q?r¹?pœ?{î?rÂ?zZ?og?ln?z?k‡?{˜?b“?të?p?qì?vW?yŽ?€?|9?~¯?z†?z'?u?tÑ?q?tß?n»?p¡?nÖ?p?ƒF?„‚?ƒÁ?…X?„”?„L?…î?…j?à?ƒÜ?ƒœ?ƒS?„?|ê?G?}.?€I?}À?|?Z?€þ?œ?Õ?}y?‚?G??€‹?|‰??}V??€¿?€ ?f?E?€?€?|¶?~ï??~?‚ ?‚ë?~ ?M?~Æ?€P?S?‚?}¡?~ó?~Y?~—?;?}?}"?€ù?~/??|à?Œ?€ó?€è?>?€ž?~‘?€q?} ?~ï?}«?};?K?4?|Ñ?}³?€ ?€ú?É?|é?€‡?}z?}a?|¼?x?w?y5?zÇ?w¥?zi?{V?z ?yí?w¢?zú?{‡?|2?w‰?z­?v ?}8?zz?v…?x ?v³?w½?{K?{ñ?{ö?y¤?y™?}"?vŸ?yG?yÜ?xü?v"?vò?zù?{M?{?v‚?z™?{o?ze?vñ?y%?{;?{?y%?}z?vG?w-?x?{z?zr?{$?wÕ?{]?z'?|Ê?w?y¿?yr?z?x0?z?xö?|:?wë?{ˆ?z?w‰?{®?y`?|-?{â?x?z˜?{o?p?rY?u?rk?v(?q?v?xà?y ?kE?lc?l?já?k&?l„?o??k¬?m†?o?m4?m"?k†?l[?n€?k?kË?lå?os?kî?jZ?nÝ?kÉ?kÛ?o¬?p=?mƒ?n¤?nV?q?lY?k‰?n·?k?oá?jŸ?o ?j¼?qó?ol?lÓ?jâ?p-?mÃ?n%?o®?o5?k?oâ?m~?lB?n¯?n?mŠ?m ?nr?l?mÚ?m?oþ?l±?n©?o[?n?oŠ?nÈ?oF?rÌ?m³?oV?sÁ?vÒ?tÌ?fU?fh?f•?h|?hü?gJ?hx?ic?kq?hv?gÇ?fÅ?h»?j ?fe?hx?g"?e¶?h†?i?f6?dÑ?gV?h±?eí?gç?f>?f?i¥?i,?cê?fr?it?fK?i‰?g?j"?i­?go?d?]?j’?]À?]?[ì?\z?Z¶?\‘?^z?^¡?a ?\[?\Ê?\?c?`ð?_‚?\Ê?_ë?\Ü?aâ?f—?j?p_?ro?`?\1?]%?]1?aj?cË?hz?j&?o?qÁ?b–?_„?aš?_Ž?bé?dë?h?kÌ?o3?r¨@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æffÚÏúÂÛ Ò ) > ê þ   . >/ /¡/¢/£/¤/¥/¦/§/¨/©/ª/«/¬/­/®/¯/°/±/²/³/´/µ/¶/·/¸/¹/º/»/¼/½/¾/¿/À/Á/Â/Ã/Ä/Å/Æ/Ç/È/É/Ê/Ë/Ì/Í/Î/Ï/Ð/Ñ/Ò/Ó/Ô/Õ/Ö/×/Ø/Ù/Ú/Û/Ü/Ý/Þ/ß/à/á/â/ã/ä/å/æ/ç/è/é/ê/ë/ì/í/î/ï/ð/ñ/ò/ó/ô/õ/ö/÷/ø/ù/ú/û/ü/ý/þ/ÿ0000000000 0 0 0 0 0000000000000000000 0!0"0#0$0%0&0'0(0)0*0+0,0-0.0/000102030405060708090:0;0<0=0>0?0@0A0B0C0D0E0F0G0H0I0J0K0L0M0N0O0P0Q0R0S0T0U0V0W0X0Y0Z0[0\0]0^0_0`0a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p0q0r0s0t0u0v0w0x0y0z0{0|0}0~00€00‚0ƒ0„0…0†0‡0ˆ0‰0Š0‹0Œ00Ž000‘0’0“0”0•0–0—0˜0™0š0›0œ00ž0Ÿ0 0¡0¢0£0¤0¥0¦0§0¨0©0ª0«0¬0­0®0¯0°0±0²0³0´0µ0¶0·0¸0¹0º0»0¼0½0¾0¿0À0Á0Â0Ã0Ä0Å0Æ0Ç0È0É0Ê0Ë0Ì0Í0Î0Ï0Ð0Ñ0Ò0Ó0Ô0Õ0Ö0×0Ø0Ù0Ú0Û0Ü0Ý0Þ0ß0à0á0â0ã0ä0å0æ0ç0è0é0ê0ë0ì0í0î0ï0ð0ñ0ò0ó0ô0õ0ö0÷0ø0ù0ú0û0ü0ý0þ0ÿ1111111111 1 1 1 1 1111111111111111111 1!1"1#1$1%1&1'1(1)1*1+1,1-1.1/101112131415161718191:1;1<1=1>1?1@1A1B1C1D1E1F1G1H1I1J1K1L1M1N1O1P1Q1R1S1T1U1V1W1X1Y1Z1[1\1]1^1_1`1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1r1s1t1u1v1w1x1y1z1{1|1}1~11€11‚1ƒ1„1…1†1‡1ˆ1‰1Š1‹1Œ11Ž111‘1’1“1”1•1–1—1˜1™1š1›1œ11ž1Ÿ1 1¡1¢1£1¤1¥1¦1§1¨1©1ª1«1¬1­1®1¯1°1±1²1³1´1µ1¶1·1¸1¹1º1»1¼1½1¾1¿1À1Á1Â1Ã1Ä1Å1Æ1Ç1È1É1Ê1Ë1Ì1Í1Î1Ï1Ð1Ñ1Ò  A¼†AÁ¶HˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8ø]TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX£ ñÀ€Y@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ øŸÀŸ !    À§ Á¶Hx_èPSNOD`pîH¢P`í ¤ !    @¾ Á¶H`aèP !    ÀÔ Á¶HHcèP?z?xØ?…“?v?ŠY?mé?˜‚?v?—ß?p1?“)?q=?ð?w¿??rŒ?’8?w1?¢õ?lï?¦Ð?n‘?¦4?i´?•Ì?vî?’_?m¡?‹Ñ?”?g{?‹7?…Ì?YN?„#?¢Û?SŒ?u=?¡Ú?—î?Z¾?Šû?zó?¥:?QK?hg?“I?k?Œn=ÌÍ,=ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ=ÌÍ =ÌÍ =ÌÍ =ÌÍ=ÌÍ =ÌÌè=ÌÌö=ÌÌö=ÌÌé=ÌÌê=ÌÌô=ÌÌé=ÌÌé=ÌÌß=ÌÍ-=ÌÌé=ÌÌú=ÌÌö=ÌÌé=ÌÌê=ÌÍ=ÌÌí=ÌÌé=ÌÌß=ÌÍ-=ÌÌè=ÌÌü=ÌÌ÷=ÌÌê=ÌÌë=ÌÌó=ÌÌí=ÌÌé=ÌÌÞ=ÌÍ-=ÌÌé=ÌÌê=ÌÌë=ÌÌê=ÌÌï=ÌÌÿ=ÌÌõ=ÌÌê=ÌÌÞ=ÌÍ-=ÌÌê=ÌÌÿ=ÌÌë=ÌÍ=ÌÎ6=ÌÎS=ÌÍ!=ÌÌê=ÌÌÝ=ÌÍ-=ÌÌë=ÌÌõ=ÌÍ=ÌÏ.=ÌÏB=ÌÏM=ÌÏ8=ÌÍ,=ÌÌß=ÌÍ-=ÌÌî=ÌÍ =ÌÎ4=ÌÏ==ÌÏ>=ÌÏO=ÌÏA=ÌÎQ=ÌÌã=ÌÍ-=ÌÌì=ÌÌþ=ÌÎD=ÌÏ==ÌÏ>=ÌÏV=ÌÏB=ÌÎ`=ÌÌã=ÌÍ-=ÌÌê=ÌÍ=ÌÍ=ÌÏ6=ÌÏ>=ÌÏ?=ÌÏ>=ÌÍ7=ÌÌß=ÌÍ-=ÌÌé=ÌÌê=ÌÌê=ÌÍ,=ÌÎQ=ÌÎ}=ÌÍD=ÌÌé=ÌÌÞ=ÌÍ%=ÌÌß=ÌÍ=ÌÌá=ÌÌß=ÌÌã=ÌÌä=ÌÌà=ÌÌß=ÌÌ×=ÌÍ =ÌÌè=ÌÌè=ÌÌé=ÌÌé=ÌÌò=ÌÍ =ÌÌé=ÌÌé=ÌÌß=ÌÌè=ÌÌÙ=ÌÌß=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÇ=ÌÌÆ=ÌÌÆ=Ì̼=ÌÜ_=ÌÜ==ÌÜ==ÌÜ==ÌÜ==ÌÜ==ÌÜI=ÌÜA=ÌÜ<=ÌÜ2=ÌÜ=ÌÜe=ÌÜw=ÌÜm=ÌÜ`=ÌÜP=ÌÜ[=ÌÜb=ÌÜ_=ÌÜ7=ÌÜ„=ÌÜe=ÌÜr=ÌÜh=Ìݾ=ÌÞå=ÌÞô=ÌÝÔ=ÌÜc=ÌÛý=Ì܃=ÌÜp=Ì܆=Ìß­=Í-=ÍËd=ÍÓ=Í8)=Ìà#=ÌÛÄ=ÌÜŸ=ÌÜc=ÌÝÓ=Í-‚=Κ=ÎÍ=Îø=Î=Í?¡=ÌÝV=ÌÜ =ÌÜN=ÌÞé=ÍË-=ÎÈ=Î )=Î &=Î =Í×a=Ìæ”=ÌÜ—=ÌÜM=ÌÞØ=ÍÒé=ÎÝ=Î =Î -=Î =ÍÝå=Ìæõ=ÌÜ—=ÌÜ`=ÌÝà=Í7œ=ÎÉ=Îÿ=Î =Îæ=ÍIf=ÌÝŠ=Ì܃=ÌÜ`=ÌÜe=Ìà=Í>²=ÍÖÜ=ÍÝß=ÍIP=ÌàÒ=ÌÛÇ=ÌÜy=ÌÜd=ÌÜŸ=Ìܹ=ÌÞ»=Ìçê=Ìèp=ÌÞñ=Ìܸ=ÌÜ#=ÌÍ-=ÌÌè=ÌÌì=ÌÌð=ÌÌé=ÌÌí=ÌÍ=ÌÌé=ÌÌé=ÌÌß=ÌÜ^=ÌÜK=ÌÜ[=ÌÜE=ÌÜ==ÌÜ==ÌÜ==ÌÜ==ÌÜ<=ÌÜ2=ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ=ÌÍ=ÌÍ=ÌÍ =ÌÌí=ÌÌã=ÌÌì=ÌÌõ=ÌÌí=ÌÐ=Í`=Í(Õ=ÌÐ==ÌÌô=ÌÌt=ÌÌã=ÌÌì=ÌÍ÷=ͺÕ>Q·>¨Tˆ>¨wr>]ç =ÍÏø=ÌÔq=ÌÌä=ÌÌê=Íñ>Ù—@?4wB?yÅ4?}D?8:>ä>=ÕŒ‚=ÌÌê=ÌÏ >QK(?3ƒý?“r~?ØÒ?ž ï?•þ?:ÌÀ>b–®=ÌÌå=Íð>¤Š ?xµ¸?º?¨3Å?¨qŠ?žsD?`Ì>´H=ÌÌâ=Í'b>¤•:?z="?Ôá?¨RŸ?¨Ú?žŽª?€má>´V£Î?5CP?”¼?ž4é?žk?•ì?<œå>hm =ÌÌã=ÌÌþ=Í>Ýœ·?7Îr?||€?²‹?;²B>èJ™=Õï¦=ÌÌë=ÌÍ=ÌÖô=ÔÔ >[©a>°R”>°±Ñ>hl=ÕÒ€=ÌÛ—=ÌÍ.=ÌÌè=ÌÌí=ÌÌó=ÌÌê=ÌÌê=ÌÌï=ÌÌþ=ÌÌé=ÌÌÞ=ÌÜ=ÌÜd=ÌÜm=ÌÜl=ÌÜm=ÌÜL=ÌÜO=ÌÜe=ÌÜ_=ÌÜ7=ÌÌä=ÌÌé=ÌÌé=ÌÌí=ÌÐ=ÍT=Í(ý=ÌÐa=ÌÌô=ÌÌu=ÌÌÒ=ÌÌð=ÌÎÃ=ͼ[>¥OÚ? ç?#Å>©€I=Ö£=ÌÜ4=ÌÌÆ=ÌÍÍ>R‡&?MÖÚ?“_?ž2Ï?žr@?•ç?TÖ>h&¿=ÌÌÆ=Í!?L J?ž¨ê?µ:e?ÃÇ?ÃU2?¶1ê?Ÿç ?Wî=ÌÍÖ>¢ÚÙ?’ÏÚ?µ\?Òi ?åLt?å¾£?Ó¹–?¶¯é?– ;=Ì€ã? ê ?tU?¤ƒ?å4?û—Š?ü!7?æ˜0?ć? ÂØ=Ìv’?œ«?šð?ÂÌî?å@«?ûÜj?üff?æÒ?Ä©b? Þk=ÌÍÕ>£Îé?“[Ò?µ?Óà?æ ?æ‡j?Ôc›?·.?—*Þ=ÌÌË=ÔŠ?N…Ô?ŸFZ?¶Ÿ?Ãñµ?ÄD?·X? …þ?Z\Õ=ÌÌö=ÌΚ>_¾Y?R¦Ý?”_¾?ž÷8?Ÿ.?•ú¯?Xµü>vw÷=ÌÍS=ÌÌé=ÌÌé=ÌÌê=ÌÌê=ÌÌí=ÌÌò=ÌÌ÷=ÌÍ=ÌÌÞ=Ì܆=ÌÜ_=ÌÜ`=ÌÜc=ÌÝÖ=ÌÞÜ=ÌÞÝ=ÌÝÓ=ÌÜe=ÌÛÿ=ÌÌä=ÌÌé=ÌÍì=ͺº>QÃw>¨U2>¨w>]ç~=ÍÏÀ=ÌÔŽ=ÌÌÆ=ÌÍÈ>R‡U?MÖù?“^å?ž2?žrZ?•ë?TÃ>h'=ÌÌÕ>Rsê?v’`?©F‹?›2?Ò¦´?Óú?ú?ª©?€Ió=Ì~?Këœ?©)?Ñù|?ú¤@ õù@ JE?ü„?Ô)@?«ì\>P‚Ï?’M»?Â@ò?úUŒ@.…@0¥ê@1. @Ÿê?ýr`?Åe¸>¤6×?œö•?Ò‚@ ““@0TV@L"@MFÿ@22@ |Œ?Õp>¤G“??Ò1L@ ½e@0˜@LëÐ@M¢ò@2wŠ@ §?Õ ß>U×~?’ÞÞ?ÂÏÂ?ûD<@æF@1”@2 @[M?þg ?ÅöÝ=Ì~?N]?©×?Ó?ü0k@ êM@ ? ?þñ?ÕE3?¬Ÿ3=ÌÅ’>_¨¨?zN?© *?Ãm&?ÓÛ™?Ô=v?Ä’j?«á?ÞÉ=ÌÍ.=ÌÌé=ÌÌê=ÌÌë=ÌÍ9=ÌÎ6=ÌÎE=ÌÍ=ÌÌò=ÌÌâ=ÌÜ=ÌÜ`=ÌÜd=Ì߸=Í-€=ÍËC=ÍÓ…=Í8$=Ìà6=ÌÛÑ=ÌÌë=ÌÌê=Í>Ù— ?4w…?yÅ?}›?8:k>ä?~=ÕŠ7=ÌÌÆ=Ít?L ì?ž¨Ñ?µ:^?ß?ÃUA?¶2!?Ÿç?WîC=Ì~i?Këë?©(Ë?Ñù–?ú¤]@ õè@ Jƒ?ü„?Ô)O?«ìP>×.Ž?žY?Ñă@ hž@0<@LBë@Mâ@1ô@ Mê?Õ©?1r-?´Òô?úY@/Ì1@qx@”Ü>@•¬@töS@2ÜÀ?þ±”?v¢k?ÂPB@ d²@KoK@”jÁ@Á@è@ŸÈ@—Ñ@O@ %Ì?x)c?Âxó@ Ž_@Kͳ@”Ò@ÁïY@ÃQÙ@—{c@Oð½@ PÃ?34C?µN?úö‡@0·.@s3¸@–.@—Á@v¿õ@3Ò®?ÿ¦>Û)?ž³?ÒÙð@ Y\@1Ÿé@NQ»@Oy@3„›@ GQI¬?3„C?“rv?ذ?ž ÿ?•?:Ì–>b–Š=ÌÍÚ>¢ÚÐ?’Ϫ?µk?Òhæ?åL1?å¾þ?Ó¹U?¶° ?– O>P„?’Mê?Â@ì?úUw@.…@0¥ò@1-ó@ŸÎ?ýr’?Åeg?1r??´Ó ?ú.@/̶@qx @”ÛÇ@•¬!@tö@2Üú?þ±¦?‘ãè?ÑÊ@¸/@päÐ@À.¼A k~A ÜÛ@Ä Z@v± @!?›¥Ý?äKK@/¼@“úzA­™Anj4ArµxA 5¬@˜aã@4{?›À4?ä„N@/ÿÄ@”`½A c Ap‹AtèSA ÷î@˜Î‚@4Sç?’kÿ?Òq@nä@rŒ@Âa‚A ¬›A )ñ@Æía@x÷@ßà?4Ó?µ¡6?û“@@1PŸ@tY@— @—ãÃ@wíb@4p›@$—>ZY?“Tv?Ã#?ü3Ó@Èé@2Æû@3U¼@C/?ÿ`…?Æ@Ô=ÌÍ-=ÌÌê=ÌÌî=ÌÎ4=ÌÏ==ÌÏF=ÌÏQ=ÌÏ?=ÌÎR=ÌÌñ=ÌÜ =ÌÜM=ÌÞð=ÍË/=Îà=Î =Î !=Î =Í×O=Ìæ»=ÌÌå=Íè>¤‰Ü?xµ=?º??¨3 ?¨q§?žsX?`É>´”=Ì€Ü? éí?tî?¤B?åw?û—e?ü!.?æ˜?Äð? Â >¤7K?œö>?ÒV@ “ª@0TL@L1@MG‚@22@ |?ÕpN?v¢W?ÂP‚@ d°@Ko@”j @Á@Ë@ŸÈ@—ê@O޳@ &?›¥Û?äKÕ@/¼@“úËA­ŒAnj3Arµ3A 5ó@˜aˆ@4•?¥Ó«?úb‡@KY@¿ËAl7‹B…ûB—‘ÄAyÔÁ@Ç'„@Q +?¥ò&?ú¦ð@K³\@ÀxAnNEBÍ©B¥A|)9@ÇáØ@Qiœ?œn?å¦@0¨¶@•H¾A è‹Au'€Ay°QA—Þ@™Â”@4ùé?yOR?Ã;­@ VÀ@Mz@–™@Äê³@ÆWû@™S@Q¯&@!n>¬7?Ç~?Ó3C@ ã&@2s‹@OfÑ@P"œ@4Z«@ Ö'?Ö®‰=ÌÍ-=ÌÍ=ÌÌñ=ÌÎD=ÌÏ==ÌÏB=ÌÏF=ÌÏ>=ÌÎ`=ÌÌý=Ìܘ=ÌÜT=ÌÞ×=ÍÒð=Îõ=Î =Î =Î =ÍÝê=Ìç=ÌÌâ=Í'>¤”Å?z= ?ÔÎ?¨R–?¨Ò?žŽÂ?€mí>´;^=ÌvT?œ›?›c?ÂÍ?å@‹?ûÜ?üfs?æÑË?Ä©? Þ;>¤G½?û?Ò1@ ½b@0˜@Lë›@M£,@2wŸ@ §¢?Õ Á?x)?ÂxÉ@ ŽC@KÍZ@”Ñâ@Áï¨@ÃQ†@—{ƒ@Oñ1@ Q?›À,?ä„y@/ÿ°@”a\A bÎAp‹ÌAté3A ÷å@˜Íõ@4SÈ?¥ò)?ú¦Î@K³b@ÀwìAnN—BáéB¥cËA|)4@ÇáB@Qi±?¦·?úëS@L î@Á%*Apn‹B—]kBÁgÒA~‡@È@QÉü?œ8?åJÆ@0ìë@•°·A ¤Awf«A|ÚA_ý@š1¾@5F0?zÍ¥?ÃdÎ@ €ò@MÙU@—‡@Å Í@Ç—@™Àÿ@RE@Lß>¬=?â„?Ód@ @2¼@OIJ@P@4¤;@w?Öàa=ÌÍ-=ÌÌõ=ÌÌë=ÌÍ=ÌÏ6=ÌÏV=ÌÏH=ÌÏG=ÌÍ8=ÌÌä=ÌÜœ=ÌÜ…=ÌÝÎ=Í7=ÎÒ=Î =Îý=ÎÞ=ÍI{=ÌÝ =ÌÍ=ÌÏ:>V¢6?5CG?”w?ž5 ?žjþ?•‘'?<œz>hm=ÌÍ×>£Ï?“[”?µ>?Óè?æQ?æ‡?ÔcŒ?·-ô?—*Ð>UØW?’ß$?ÂÏÓ?ûD^@æ;@1”-@2@[G?þf›?Å÷?34Ž?µM­?úö»@0·@s3ê@–.@—Ä@v¿`@3Ó?ÿ¥‘?’l?Òq@nÌ@rž\@ÂahA ¬ÝA )™@Æíe@x€C@ßí?œN?åê@0¨n@•H;A èÃAu'Ay°ËA—Ÿ@™ÃE@4ùž?œO?åJ†@0ì§@•°ÑA £¢AwfçA|ÓA_õ@š2 @5Fw?’í·?Ó"@'S@t_w@Ä ÌAÈAН@ÉHŠ@zU×@Ÿ½?6¢C?¶ù?ü„k@2C„@vJ@˜iÇ@™CT@yÁ¤@5h[@ @>_…?“äÓ?ã ?ý((@…@3¸ @4H§@@-t?ÆÕ==ÌÍ-=ÌÌé=ÌÌê=ÌÌë=ÌÍ,=ÌÎQ=ÌÎ`=ÌÍX=ÌÌî=ÌÌà=Ì܆=ÌÜa=ÌÜf=Ìà6=Í>¿=ÍÖÚ=ÍÝÍ=ÍIP=ÌàÑ=ÌÛÊ=ÌÌä=ÌÍ=Í€>Ý›œ?7Ï?||œ?±ê?;²®>èKñ=ÕëÁ=ÌÌÆ=Ô‰R?N…¾?ŸF=?¶—?ÃñÐ?ÄDg?·? …ã?Z\È=Ì}ù?N\ä?©×·?Óú?ü0¬@ êf@ ?a?þ?ÕEj?¬Ÿ?>Ûq?ž³j?ÒÙê@ Y@1Ÿ¸@NQ—@OÑ@3„t@ GP?ÖÅ|?4Òè?µ¡?û“U@1P›@tY@— ¾@—ä-@wì¬@4pÖ@$”?yO¸?Ã<@ Vâ@Myt@–™¸@ÄêL@ÆX*@™RÇ@Q®ñ@!T?zÎ?Ãdí@ €Î@MÙ€@—0@Å ß@Ç«@™ÁJ@RP@Lâ?6¡J?¶+?ü„a@2C—@v£@˜iÏ@™C @yÀÕ@5hÈ@ >Þþ?ŸWí?Óò¹@ Oû@3-‡@PkW@Q1i@5 @A”?×äü=Ó¼Š?S"á?ª4{?Ô.?þ@ A@ ˜@Ý?Ön—?­ö=ÌÍ%=ÌÌà=ÌÌß=ÌÌê=ÌÌæ=ÌÌä=ÌÌç=ÌÌõ=ÌÌß=ÌÌØ=ÌÜ{=ÌÜe=ÌÜ…=ÌÜÆ=ÌÞÂ=ÌçÎ=ÌèX=ÌÞý=Ìܺ=ÌÜ&=ÌÌë=ÌÍ=ÌÖ€=ÔØ>[ª>°Tc>°³_>h"=ÕÔñ=ÌÛS=ÌÍ=ÌÍÜ>_¾?R§=?”_k?ž÷g?Ÿ-ë?•ú“?X¶c>vxÉ=ÌÅQ>_¨[?zQ?© @?Ãm?ÓÛµ?Ô=‡?Ä’I?«ñ?Þ¶=Óoò?P¾?©‚›?Óñ?üƒÐ@ G…@ `?þn×?ÕL–?¬L‚>Zª?“TÀ?ÃE?ü4@Èú@2ÆÌ@3UÖ@C?ÿ`À?Æ@¸>¬e?Çš?Ó2ï@ âõ@2s•@OfÌ@P"|@4Zt@ Ö3?Ö®E>¬:ô?âè?Óc™@ ý@2¼@OĽ@P†@4¤e@>?Öà=>_ƒˆ?“å(?â©?ý(s@„÷@3¸Ï@4Há@@-S?ÆÕt=Ó¶Q?S"Ï?ª4Œ?Ô. ?þ½@ @ì@ ˜&@ì?Ön{?­=̾o>mQg?}Fj?©ÿ ?ÄG:?Õ?Õ}—?År–?«lW?ƒ\Ö8ý8ý8ý8ý8ý8ý 8ý&8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý"8ý 8ý8ý8ý.8ý8ý8ý 8ý8ý8ý$8ý 8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý!8ý8ý8ý 8ý8ý8ý%8ý8ý38ý 8ý88ý@8ý8ý 8ý8ý8ý8ý38ýý8ý 8ý8ý8ýA8ý 8ý8ý8ý.8ý8ý 8ý 8ý8ý 8ý48ý8ý8ý8ý 8ý,8ý8ý 8ý8ý 8ýA8ý8ý8ý8ý,8ý=8ý8ý 8ý 8ý 8ýJ8ý 8ý8ý8ý8ý8ýA8ý38ýY8ýU8ý8ý 8ý 8ý 8ý,8ý8ý 8ý8ý8ý 8ý 8ý8ý8ý8ý8ý8ý8ý8ý/8ý8ý8ý 8ý8ý$8ý)8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý'8ý8ý8ý8ý&8ý8ý8ýó8ý8ý8ý#8ý48ýV8ýk8ýw8ýj8ý08ýÂ8ý8ý!8ýI8ýM9R9ª–K9µ†Å9 c8ýÁ8ý¨8ý8ý8ýh9Sü:Ýò: Àü: âV:¥É93R8ý;8ý8ý8ýl9ªr&: Êû:!A»:!Nº:!E<9¸ýc8ÿh 8ý8ý8ý^9´Äð: ̦:!Dm:!C!:!:v9Â|º8ÿ°8ý8ý8ýs9 M':xº:!5£:!?³:!$‹9gÉ8ý":8ý8ý8ý/8ýº9òþ9¸x¨9Âjß9n8ý-é8ýª8ý 8ý8ýH8ýu8ýR8ÿd¹8ÿ°¸8ý"è8ýr8ýá8ý8ý8ý8ý8ý8ý8ý'8ý8ý8ý 8ý8ý 8ý-8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý!8ý8ýü8ý8ý8ý8ý8ý19t3R9zëk8ý®8ý8ý°8ý8ý8ý9ô"9Ñ>ó9•6‘9–Qc:^·Î9û5$9 ÷8ý8ý9¶U{9(ü¡8Ä0§7ì…v9¶¯9v×ï:¡9¾ø8ý8ý'9±uú8 ¨Ù5Ëol5nZ5Zü8¼P9¾é:J4;8ý9ugâ9927êhø54þ¾4ýÆà5PÒ9s¼:Lk‘8ý9{¸‡99mª7` Î5a44ýå4ý 56>9aZ§:L8ý8ýr9ÀË8k: 5[ ¶5f5?8­f«9¯f$:>8«8ý8ý$9º‰98'8_²)7UòŸ8ÝCˆ9E‘ž:¯ç9¿[l8ý8ý:9B=9°×9ˆ“°9"߀9)¿ƒ:1é¸9¿XŽ9Õ;8ý8ý8ý8ý8ý8ý8ý8ý%8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýó8ý8ý8ý8ý8ý+9t8¹9zðq8ý×8ý8ý±8ý8ý78ý:âi9ªÝß9+9þe]:%% :j 9%8ý8ý9Ì;ù8ŒÓ|6=5Š5á|8ÀlÈ9¡îÐ:UúC8ý9òNê8Yš24ìá4]w4˜â4¸þ4 ïk6ND:î8ý98ã5ü‰4­4–Ý4–Å4–‡4´”5sÒ9nÜÿ8üÝ«8ýs05‘w4™4–e4—4–â4–Ù4×{96­8üÕ~8“UG5[g&4˜4–Î4—!4–Ú4–ô4Ö 96>ç8ý"98£™5º¾D4é]4–Í4–‹4–±4²Â5‚g9l¥Ñ8ý9«•ª8 Š54¢x‹4šÿ4—Û4¶4½+6ñ™9÷5§8ý<97×9Hš:8ƒ 5ˆ[×5+¯5 Ã8¯5ç9…‡Ú: ^8ý38ý8ý8ý8ý8ý8ý8ý8ý*8ý 8ý8ý8ý8ý/8ýj8ýb8ýb8ýi8ý28ýÄ8ý8ý8ý9ô$9Ñ?†9•?q9–YÛ:^š9û Ï9 ñ8ý8ý9Ì7ú8ŒÔÛ6óö5­á5àØo8Àw†9¡éÆ:V+8ý 9ÌI7û3g4·4–«4–›4–¢4í'5ž€9å>›9r• 8W4#ù4–*4–Í4–„4—4–\4–o9]\»9j/6%L4—64–t4—4—¸4—¢4–Ó4–“8^Rh9Ew5•$4–4–Ä4—…4˜Œ4™J4—A4–ø7‚Oë99´5ƒù4–t4–Õ4—4š(4™F4—®4–Ñ7€Xý9"ÎD5¬èÁ4–~4–¸4—4˜4—~4–þ4–¬8V‚œ9y¾j8 Ãt4Ùž4–w4–K4—4—#4–r4–79X68ýã9H‰e7h—w4W4XÅ4o²4oÅ4iÓ5‚9ÚÙ§8ý8ý8ý8ý8ýQ8ý!8ý-8ý;8ý8ý8ý*8ý8ý,8ýU9Q³9ª ¼9µ€9 cÌ8ýÒ8ý³8ý8ý9¶Y©9)–8Ä"H7ìØ9ÆÄ9v©N:™V9¾I8ý9ò1È8Yšl4ì{54‹]4˜¼4¹4 Ñê6N:–^9rÀç8WÕ4#¸4–;4—4–L4—j4–`4–u9]E,8Y55F«y4–‘4–84–‚4—#4˜b4–ô4–o8—{¥8tÅÕ4`â4–e4–é4—±4›4™é4™…4–¿6çu27ç†É4©.4–ü4—C4™I4£J4žƒ4š4˜4£Ck7X=54¢û4–Ñ4˜à4™ð4ü4¥4›4–þ4›ü>8%44–£4—/4—B4š4›!4™94—6×U…838»4ýãf4–P4–”4—J4—b4—Í4—U4–V8’*,8÷g7^4d‚4Q±4y´4W4â4{j4Sq9XüY8ý8ý8ý8ý38ý8ý8ý 8ý8ýC8ý 8ý48ý8ýb9Q]:Ú1: Èg: Ð@:k93Ç8ýH8ý8ý&9±æ8 ¢K5Ë|ö5qÙ5Z–8¼“9¾3:J4(8ý"98Û`5ûäß4›¬4–¹4––4–ë4´=5;è9nÚw9j •6–4—(4–s4–ë4—ˆ4—\4–ß4–À8^Xš8tŸF4ì4–W4˜×4—44˜ž4œl4—¿4—D6ç’45'4–ý4–Ý4™Ô4š”4 “4§—4™ƒ4™u5aR4È2˜4–…4—‘4™w4¦v4ü4ý\474›g4†ß4ȉ4–¾4—]4˜_4 ò4÷â4Î4£™4›x4EO5sô4–™4–³4—q4›b4¢$4¥U4œŸ4–þ4ú6’7·<<4ª4–c4–ƒ4˜4œG4˜ü4™¥4—U6Û=š8½¦S5JŒ4Y#4y4Œç4–ˆ4–a4‡4zë8To8ý8ý8ý8ý8ý8ý8ý8ý 8ý48ý8ý8ý8ýr9ªwa: ÍÊ:!CÑ:!:Â:!1¸9¸ô…8ÿg_8ý9ug-99 -7êaX5}À4þÇ4ý¾X5L<9s˜ñ:LgB8üÝ£8ý…{5‘d¼4˜Õ4–¶4–É4–â4–²4×ï96½9EU5•%4–t4—4—†4˜ä4›`4—#4–Å7‚l&7燙4©g4—4—4˜Ô4¢á4 g4›Þ4–õ4¢•|4È2h4— 4—ò4œ 4¢@4ÿY4÷£4¡4œX4Œ4¿ëÚ4–Ü4™4ža4 6Ùm87œ4ðv4Ÿ¸4›4¿Ô~4–û4˜Õ4¢Q4ä»79Lõ8X0½4Ù4¤4šs4Çå4–À4˜P4œ·4¡z4êù4ƒ4£!4˜4p€78$è4¢ 4—4š 4™Ô4ž{4¢¥4œy4—¤4™ó×7äo›4yÞà4o‰4¤4–h4™P4™4–î4X7p¾98ý8ý*8ý8ý,8ý8ý 8ý8ý 8ýA8ý#8ý8ý 8ý\9´Ç: â7:!A$:!5Ü:!19‚Â8ÿ°U8ý9{·:99e‰7` 5cª4ýâï4ý™g55W9a;ñ:L8üÕy8“Qh5[Ûa4˜4–²4—64–î4–¹4Ö$96Nh9Eh5‚î­4–T4–—4—³4™k4š 4˜<4—57€¥ã7X4¢Û4–¤4™p4™€4 œ4 44šx4˜54œðæ4ȼ4–ï4—“4š 4¡54ù 44¥%4™4\Ê4¿Ôž4–â4™)4 è4õ—7yñg89tC4´4 [4›E4¿½n4—4˜¡4Ÿ€4õ>7¼—k9 1÷4ù©4¤24›4ÇÎ>4–”4˜C4™%4£¼4 U4’4¥84™G4-5ø¥à4™—4–›4˜4šC4 54£ô4›4—å4“xä7ß/‡4yÍ\4on4™4–Œ4™4™_4–<4‘$7mu%8ý8ý8ý8ý;8ý8ý8ý8ý8ýK8ý8ý8ý28ýe9 L:‰v:!,:!6q:!;9e‘8ý!ò8ý=8ýž9¿È8kE+5[vø5c-5=/8­ 9¯S:>,h8ý"98˜ƒ5º†µ4¸4–É4–œ4— 4²Ô5ò¯9l½P9"ÉŸ5­!74–4– 4–ú4— 4˜ 4— 4–M8Vµd8%14cñ4–»4—(4˜4›`4™×4–Û4˜M6ا5 4–“4–°4™—4œ©4£Ø4¥D4š{4˜ 4û†4Çå 4–í4—i4˜S4¤04åÚ4ï4¡}4›4S4ÇÏ 4–R4—k4šâ4Ÿü4ò434¤4œà4¯4ÏdA4–¥4–º4—¿4›¥4£Ã4£4›4–á4ëÆ-6ÚÚé4Ÿ²4–‹4–ñ4—:4›þ4—ë4š4–Ä6Ë‹x8G¢d4¡e24Y¤4zl4Œö4–Q4–n4Ž€4{Ä8LÁG8ý8ý8ý8ý8ýA8ý48ýA8ýe8ý8ý 8ý8ý8ý/8ýÔ9÷y9¸o¨9Âi«9ju8ý-ß8ý®8ý8ý79º¨99Û8_²ž7UÃa8ÝCì9Ež1:·‰9¿a‚8ý9«˜„8 tr4¡—X4šå4—ÿ4¶{4½6ù39÷;·9yÁÅ8 ¼<4Ùz4–P4–~4—c4–³4–…4–i9X&W834ÿ>4–=4–A4—4—4˜¥4–×4–v8‘ß7·.84©ÿ4–b4–­4š4š½4šn4—¤4˜_6Ùü788q4¢{4—>4—W4544£è4™&4—4˜Ÿà5ø Þ4™¢4–q4—’4˜'4¡4§ç4›#4˜÷4“4¨6ÚÞ¼4ŸÝ4–„4—i4—Ó4j4™O4—œ4˜g6Ì€ô7’O49‡N4–€4–p4—,4—¾4—û4—f4–.8ŒÀ8ò,‚6‚@´48®4RÕ4z4‚4Ì4{¡4TÇ9SMª8ý8ý8ý 8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý28ý€8ý\8ÿe78ÿ±A8ý#M8ýt8ýã8ý8ý;9B{9°É†9ˆ…-9"Ñ;9)ÀM:1än9¿R[9Óß8ýQ98Ë9H˜ž8n÷5‡÷ñ5n=5O8¯%«9…¢d: S8ýí9H€k7h‡@4W4X¿4oä4oÙ4i”5¶…9Ú³8÷Ì7iª4bÎ4QŠ4yÉ444{O4S 9Xêo8½´$5K(W4YC4y§4,4•ê4–é4W4{8Tl7äu34yšA4o74 4—4™4˜…4–/4f7q, 7ß7@4y‹d4o/4O4–Œ4™k4˜æ4–ê4Ð7m…48G£Ô4¡V¬4Yy4z«4ŒÙ4–©4—B4Ž4{‹8Lðô8ò4ü6‚@548¯4RÌ4zS44·4{´4Tž9ST18ý„89bÃ5äÕ&4Ñ÷4w4Jö4KŠ4 …4Á9ÑÆ7?‡û?‡¢?†}?ƒ©?€l?~I?z?s(?pH?m¿?‹?Šò?‰ö?…º?H?~L?z?r“?nq?k>?Ü?Ž?G?ˆf?ƒ-?L?z¾?pè?l?h_?‘ ?‘}?¹?‹K?…q?€[?yË?pu?iÈ?eC?”?”Â?“[?Žp?ˆ§?î?yÙ?o1?fð?aö?–à?˜?—"?k?‹?ƒ:?yœ?mÓ?d±?^î?™7?ši?™|?’ì?Œá?„s?y»?lp?c?\?šj?›à?›r?“?©?„Ù?yò?l!?b1?[E?š1?›œ?šê?”.?Þ?„›?zq?l?b2?[„?™?šV?š,?’p?Œ…?ƒÙ?yš?mp?c¨?]?— ?—ã?–¡?ö?Š×?ƒ ?z¥?mõ?e ?_j?”{?•-?•%?Œ‘?ˆ?Y?y??oö?hJ?b­?Šâ?ж?‰ ?…ê?‚n?$?z?qš?nB?k&?ŽÌ?¨?L?‡Ô?„,?‚?y…?q”?kÜ?gÒ?’•?“3?‘x?Œ¨?‡.??z?oÀ?h\?c¾?—?˜L?—#?ò?Š9?ƒ?yä?m¤?d²?_?›?>?œ??•á?ޏ?…‰?yÝ?k+?`k?Yâ?Ÿ ?¢T?¡?™Ð?“q?‡o?xò?i ?\2?T‡?¢+?¥P?¥‹?ž?– ?‰é?yû?gf?Y[?P?£õ?§?§©? Î?—t?‹‘?xê?go?X?M‘?£Ä?¦´?¦Ä?¡y?˜Î?‰¶?zR?fá?WŽ?N­?¢?¤é?¥2?žÕ?•å?ˆº?yŽ?h&?Ze?QJ?Ÿ ?¡? «?šÀ?’º?‡r?z3?ig?]C?U”?›¥?M?7?•?ŽE?„Ì?x¼?l?b ?Zø?Ù?Û?Œn?ˆæ?„?£?z=?q*?l@?hK?’§?“Ö?‘â?‹ì?†ó?€É?yZ?pN?hº?cÇ?˜>?™v?—k?’‘?‹þ?ƒr?yò?nx?c(?]ž?Ÿx?3:À??êµ?L?¾??w¥?2“?¸„?®­?´?µë?3,›?LŽ’?L²,?L”­?Lƒ?@}?2¸?Ü'?¥o?¨×?©®?¤~?óf?)¦m?,å/?)A?lw?Sm?‘Y?Ô?º?ŒD?†X?€(?yy?qï?i™?dâ?–ì?—À?–t?‘Æ?‹1?ò?y@?ný?dÀ?^ú?? ?žM?š ?‘Ì?ˆ‚?|$?oŸ?\X?V„?§ª?«Ñ?¨?£¼?&Ö?&D ??á?&$ê?Áö?Lß?°&?µt?µ[??ƒ~?L‚?L¥û?L˜é?Lxa?@¨?(OÈ?·É?¿h?2æ˜?LÛ£?LÕï?L¾i?L¡½?L‚´?Loå?KuÎ?½”?Ǫ?L‚ ?Lí1?Lâ]?LÂ?LŸ2?L€s?L`^?L)®?ÀI?&Q¢?LÁÐ?Lù?Là°?LÌ`?L ¬?L*?L[ ?LOh?Àè?(t?LÆH?Lô¾?Lç:?LÆû?LŸS?L~x?L[ù?LLR?½§?É?L–9?Líw?Läs?L¿Þ?L ÿ?L~!?Lbù?L) ?¸Z?Âü?5.?LÚk?L×T?L¼½?L£n?L…¦?Lií?K§j?°2?µ¶?·a?A–t?Lšù?L²Ö?Lœ‘?LÑ?Az?)ÕG?•û?’­?’?q?‰©??xè?pÎ?hÛ?`²?œ&?›Õ?›$?—ƒ?I?ƒ‘?xå?ly?a›?Yî?¥?¦Þ?¦Á?¤µ?›}?&TŸ?&EØ?%é?]?OA?°?´°?´ú??‚+?L{j?Lª¢?LŸo?Lr†?@7?(Rß?»§?Å*??žÐ?Lã¬?LÜi?LÁÄ?L¢4?L~Y?Lgp?KÁ{?Ç?2ñÆ?M?Lò@?Lîþ?LÉ?L¥;?Lrê?LXö?LE?×]?Ly ?M ?MN?Lýþ?LÖ¿?L£^?Lji?LJm?L:?é—?LÃ3?M˜?M1?M?LàD?L¤‘?Lb?L@‡?L)?êú?LÅG?MÔ?Mˆ?MÝ?Lâœ?L  ?Ld§?L?a?L)å?Ùv?L”:?M å?Mf?Lø©?L×?Lÿ?Lj?LI^?L4è?Ƚ?5/?Lý?Lø³?Lç?LÐ?L¢&?Ls=?LU£?LK‘?»q?¸S?A½ò?Læ¯?LÚG?LÅ^?LŸá?L…´?Lcý?Ký?˜?—?–?’Å?œ?€»?x ?n¶?f¾?_ ? ö?Ÿ? /?œð?“`?…²?y?i›?_?UE?¬?­`?¯*?&A ?37Ï??äa?LEm??s?2Ÿq?¹4?¸9?¾7?2ã´?LÚ©?LØ>?Lº·?L¤Ô?LÝ?Lo?Kvï?È??2ðb?Lû¼?Lõ?LîŠ?LÁè?L©Ö?Lr?LZë?LC ?ä??Lð¿?M?M Ð?M?Lài?L§R?L`{?L=?L,*?&¦Y?M_?M#?M)E?M'?MÌ?L¦Z?LHk?L2?L?@ ’?M‘?MŒ?L #?L/³?L›?Kø°?B2{?M}?M2Š?M>—?M4 ?M º?Lªô?L6ú?L ?L&?(ÑÉ?M¿?M#o?M.S?M!?Lý~?L©&?LKG?L þ?Ln?ç‚?L÷;?Mè?Mî?M ÿ?LÞ]?L¤„?Lb*?L8?L3Ü?ÇÊ?7Z?Lÿd?Lý!?Lë?LÍ®?LšŠ?L~ª?LT?LR;?™ÿ?™©?˜?”Í??‚“?w™?ma?d¬?]a?¤??£?¤à?ŸÖ?–G?ˆ?wß?hU?\=?Q¡?¯·?³ˆ?µµ?31À?L‰Š?L¬½?L›>?Lj?@n?'×?¾d?ÆÌ?Lá?Lí?Lâ*?LÀŒ?L¥Û?L{¡?MV?LÂr?M7?MÁ?M?LàB?L¨Ó?L^Y?LC&?L1A?@â?M ›?M3p?M:¤?M8{?M ?L¡’?L=„?Kÿº?LO?MÕ?M8?ML«?Mna?M?MŠ8?L©ç?K³?K­U?KÍ!?M Ü?M>o?McÐ?MŸ³?N_?N©'?LÃ…?J¤‡?KF?K™?M µ?MAé?McG?Mžï?N;?N¦t?L÷I?JŽP?KF×?K›«?My?M5s?MS²?Mt>?M–!?M’e?L½;?Kµ?KŒŒ?Kθ?BY?M"I?M7å?MDk?M:î?Mþ?L¬Ê?L7à?Ký?LR?õ\?LÚã?M¦?MÈ?MB?Lã?L¤a?LdÐ?L9ü?L1²?› ?œ›?˜@?•3?ŽÈ?„¢?x¢?lœ?cb?]À?¤ü?§I?¥^?¡»?™º?ˆÉ?w©?gi?Xà?PI?²y?¶ö?¸‰?B ñ?LÅù?L±Ž?L˜§?L€ˆ?L%-?3½ ?Á‹?(s´?LÉœ?Lôó?LãÊ?LÊL?L¡B?L{?La¸?LHo?éü?LÊm?MÚ?Mé?MÊ?LâÕ?L¨G?LfA?LDÏ?L+Ä?B1°?M!0?M3¶?M@¤?M4?M?L¦„?L3•?L%?L&?Mß?M8?MJ‰?M{2?M3?M¥»?L°œ?K·÷?K§q?KÎÑ?M n?M@&?Md?K—ù?M¿?M3‚?MQ?MnS?M¡ö?M™?Lµ¬?K§ð?K ?KÍŠ?Dƒ?M$m?M0?M<(?M?Ž?Mó?L¥t?L-š?L…?Kÿ´? ?LÚ?M'?M K?M ?Lâú?L¤ª?Lb­?L@&?L,Ù?™º?š¥?— ?“æ?·?… ?x"?m¿?cÙ?]ñ?£¹?¥ñ?¡??Ÿ?— ?ˆÞ?w¢?hÞ?[#?Rþ?±ä?´{?±q?31 ?L¤?L´I?L“/?Lƒ˜?AM—?'Õ~?¿ ?Èá?L”?LíÑ?LÞ]?LÄZ?L¡ì?L~ ?Lb?L+?ØV?L“?M ^?M ?L÷Î?LÓé?L¦¤?Lkx?LE)?L:Ç?(Ó?M?M(¹?M+i?M$¹?Lõ(?LŸÙ?LBÐ?L++?Lÿ?Lß„?M,d?M<Ÿ?M[á?MYS?M0"?LÛ?L p?KßÑ?Ké…?M*?M7 ?MLH?Mvâ?M¢)?Mˆ·?L¸q?K¡À?K™¦?KÅ?MM?M3!?MQ‰?Mt%?Mš.?MŸ?L±ë?K¨ã?K˜?K̲?Lò ?M,^?M;?MUe?MhR?M=!?L¤~?L‰?KÑ?L­ˆ?L,½?Lr?Ký¡?D¥?M%i?M'ý?MD ?M;š?M_?L­q?L2k?Kú?Kü}?,ÁÄ?M—?M?M-¼?M*³?Lü²?Lž“?LNÑ?L?LX?óœ?Lö„?MÝ?My?M £?Lâ¯?L¤×?La¾?L7?L2;?É ?8ËÉ?LúŸ?Lûý?LçÓ?LÓ„?L¢8?LwS?LZg?LHµ?“ê?“ê?’V?«?ˆÜ?#?y,?qd?g?b|?›?›¼?šv?—å??„¿?xg?lì?aG?ZŸ?£Ü?¦Þ?¦?¥Ç?ô®?)¬?,â¨?)@Ê?mˆ?Oþ?¯ ?²z?²®?A—E?Lœ¤?L¹Ü?L—a?L}²?A|I?)Íd?ºV?·?A¼è?Lég?LÚÍ?LÈ?L(?L~?Lg"?Kô?Ç1?7s?Lþ„?Lõå?Líœ?LÊö?LžÈ?Lye?LYM?LJÄ?Ü—?L§ç?M?M.?Lü»?LÒÙ?L¤ê?LiR?LKí?L8s?õ^?LÛ7?MŽ?Mà?Mu?LÚ??L s?LcÇ?L>‰?L+ß?â?Láê?M À?MS?Mz?LÞ'?Lžr?Le?L:v?L+??ÞÑ?LÀý?M?M ð?LöR?LÛ£?L¦‹?Lm?LB?L9î?È?8˯?Lû?LýÔ?Lêt?LÌ?L¤r?Lv‚?LWy?LJÑ?ºÏ?fy?Cz‰?Lî9?LØK?LÄ?LŸ?L„Š?Liw?L  !    @ó Á¶HfèP !    À  Á¶HìgèP !    @  Á¶HÔièP !    À6 Á¶H¼kèP!?@4 4ÿ1@M1Á¶H€!?@4 4ÿ1ÈN1Á¶H€SNODØ[ÈZx€ïð¨@U(è\ÀPV?Œm?G?‘ð?“à?•?–d?–‚?”X?’—?o?o?À?”6?–C?—?—?—?–Ž?”¼?’C??±?”?–®?˜–?™—?šS?˜ª?–9?“Y?ŒÔ?C?“´?–™?˜Ý?™ë?˜ö?˜ƒ?–K?“0?‹?Ž_?‘?”&?—[?˜î?˜÷?—¶?”á?‘“?ˆ?‹k?3?‘-?“ ?”í?”±?“T?‘?Ž ?ƒ·?† ?ˆh?Šç?Œ®?è?Ž?Œ¦?‹?ˆ¥?~}?ñ?è?‚‹?ƒU?ƒ¼?ƒó?ƒ£?‚Á?z?x‘?xM?wÇ?xf?xœ?x_?xÝ?xÍ?xÍ?y ?r½?qq?pµ?nõ?mš?lØ?l?m_?nØ?p\?mV?jà?gM?eÚ?d-?ca?dm?dñ?fQ?h²?i ?fL?dÙ?`o?]™?[å?Z}?\?_Â?c?&?“¿?–þ?™^?ší?œ@?œ‹?šM?˜ ?•O?‘Æ?–¸?šâ?œÅ?ž‚?žÜ?â?Û?›}?˜1?’?•¡?™Í?ž¥?¡¨?£6?£Â?¡ñ?žo?šY?‘Ã?–Ý?œŠ? Ó?£Þ?¥Ñ?¥?£‰?Ÿà?›7? ?”û?š?Ÿ?£˜?¦$?¦?£§?ŸV?š1?Œ?‘­?–ø?šþ?ŸÃ?¡©? ½?Ÿ1?šý?•¯?†?Š ?Ž?’b?”ý?—w?˜~?–H?“o?Žø?~W?þ?„?†Ñ?ˆ6?‰á?ˆÎ?ˆÔ?‡‘?…?u¦?x=?w\?x ?yf?w‰?xó?xe?wÜ?xõ?ov?o"?m–?jí?h?g?fA?g‡?j`?m?iê?fŒ?b?]ô?ZÊ?Y„?Z,?[l?^G?b’?d;?`?\¨?Vÿ?R‘?Oï?Nz?Q*?Uò?[J?”1?˜†?œ­? ?¡í?£D?£?¡¡?ža?šÍ?–`?œ[?¡Ï?¤Í?§®?¨Ò?§í?§?£–?Ÿ?—Æ?œë?¢(?¨Ÿ?­€?°?°¹?®}?©8?£P?—î?Ÿ•?¦§?­N?²þ?µÄ?µâ?²½?¬ÿ?¥Î?•î?+?¥½?®S?kö?!^? æ?f¡?®þ?§”?’?˜ù?žþ? Ü×>ÿèa>æŽ>â >ÿÆ@? ¯?™??‹¶?‘J?L^>ÿ‰Á>Í–„>Í ã>Íý>ÍR>û,N?åÍ?‚Ã?ƒÄ? È(>åëà>ÌÚ >Ìáe>Ìà®>Ìߌ>Þî,? Eù?x?vc? ³>ÌÕ >Ì·[>Ì©Å>̳±>̲>̵??¹5?n8?j;? §Ö>æ-a>̃Í>Ì}4>Ì|>Ì€›>߀×? X?e¦?aB?W>ÿ–9>å—k>ÌMö>Ì\â>âá½>ÿaþH>>þþ? Ð?‰å?R"?˜~?_?¢w?§?©Ë?«b?«Ã?ªZ?¥Ÿ? å?šõ?¡á?¨˜?®?²ï?´¸?´}?±”?¬¼?¦ß?Ð?¥U?¬Ý?´·?»?¿r?À‡?½ö?µ‡?­T?Ÿ4?ªÐ?²Ü?½6?0¨? ¾6? …Ü?+í?¹ö?²m?œ»?§Ä?mÞ?!>ÎX+>ÍÇ@>Í»ü>Î:>ûÀÆ?\§?™?£?>çøÙ>Í9F>Í5–>ÍJ>ÍD)>Í3 >Í:>ã`?’? "Í>ͧÁ>Í»>ÍH>Í%e>Í!p>Í!Ù>ÍÙ>Í`µ?…m? IÇ>Í>Ìä•>ÌÞ>Ìæ{>Ììõ>Ìå~>ÌÛ=>Ìâ=?v:>æœ>Ì­>Ìši>Ì¥î>ÌŸ¾>Ì¡_>Ì¡r>̨f>Ì´B?i9? º™>̯l>Ìe˜>Ì[Ï>ÌNö>ÌT‡>ÌQÇ>Ìfæ>Ì¢?`K?®Ð>æg>Ì<0>Ì$I>̲>Ì.>Ì$>Ì1¦>ã/?U ?IC? ¨)>Í?Î>Ìc8>Ëþ®>Ìv>ÌT¿>̉_? UÊ?–?¢ ?§×?­~?±Ô?³Ô?´0?²+?­)?§&?ži?§“?°?¸/?¾è?Á{?Áu?½,?µÚ?¯?£ ?­?¸¯?Â=?w[?"?$ ?sž?Áè?¸5?¥…?³?z5?/H>άÝ>α>η>Îk>ûéÓ?r©?¦ ?p[>çŒè>ÍWi>Í{Ñ>ÍŠQ>Íï>ÍrÁ>Í[¤>âõµ?ŸA>çó¡>Í1w>ÍJÜ>Íj½>Í}|>Í„>ÍlJ>ÍPê>Í1º?N9>ͼ¹>Íü>Í6Œ>ÍT}>ÍjÛ>Íf)>ÍT¹>Í1>Íô? Ëò>ÍH>Ìá˜>ÌúU>ÍÎ>Íò>Íô>Íô>ÌöK>Ìá$? ¶ê>̾è>Ìž‚>Ì£Î>Ì¢Y>Ì©Ï>̤ý>Ì¥Ï>Ì¡W>Ì£Ÿ? £Û>̪5>ÌbÀ>ÌP‘>Ì9ô>Ì(f>Ì*w>Ì9Q>ÌLI>Ì_Ý?U›>æ½>Ì.>Ì#>Ëë>ËÚ¾>ËÝÃ>Ëêè>Ì #>Ì3•?L†? ¦€>Í ­>Ëï¤>ËÏ4>˾h>˺">ËÕ`>Ëò >Ìje?Ÿ»?¦:?­R?´??ºc?¼?¼)?¹-?³î?­?¥\?­Þ?¸ ?ÂZ?½T?s…?nh?»O?ÁR?·Ê?§£?µ]?™?Üx? ;Æ>ç D>â‹á? öØ?Ð?½÷?¬J?½!?7ê>ͳÂ>͈7>Í¡>ͪß>Í“µ>ͱª>÷¿ã?¯¶?'Ù>ÍZX>Í€>Í®>Í¿Ç>ÍÃ!>Í¢>Í}¯>ÍV“? Ùp>ÍDà>ÍP‡>Í{>ͦ>ÍÔ>ÍÓó>ÍÁà>Í…“>ÍPü>ÿ :>Í%>Í(^>ÍkË>ͯG>Íßø>ÍÕ>Í£¬>Íh>Í9>åû>ÌØ3>Ìý>Í ¥>ÍN³>Í>Ͳ>ÍK™>Í&W>Ìö‹>Ìà³>̦8>Ì >̪>Ì­Ä>Ì™1>Ì®M>Ì•%>Ì•æ>Ì¥˜>æ2¿>Ìcd>ÌOO>Ì%Ø>Ë÷Ç>ËÆk>ËÄ2>Ëü->Ì'Y>ÌW˜>ÿžÜ>Ì98>Ìä>ËÝQ>ˤó>Ëg:>Ëa7>Ë¥¦>ËÒX>Ì9?—>ÍC>>Ëæ+>Ë·ƒ>Ë|,>Ëg…>Ë_Æ>ËŒ->˾J>Ëò;?¢0?©®?²?¹²?¿ú?­?‹?¿?¹%?±„?©]?²Ä?¿%?¿l?3(?55?5o?1ì?¹]?¾?«±?»Ÿ?xƒ? >±>ÎA>ÍÓþ>ÍÕ³>ÍýØ?ÛM?k§?²z?3ò>ξÌ>Í‚:>ͰJ>ÍÄÈ>ÍÊú>ͱ£>Í>Î6°?n:>Î^>ÍoH>ͧ>ÍÛ>Îú>Íø >Í׈>Í©é>Íp>ÿþ >Í8B>Íkë>͸¥>Íþ½>ÎBy>ÎDß>Î>Í·á>Ís'>Í¡>ÍÚ>ÍS?>Í¢Ÿ>Î%3>Ο¨>Θx>Î4/>Í¥J>ÍW¤>Ìó>Ìã¿>Í >Í@f>ÍÂ>ÎF>Îzš>ÍÉ×>ÍS >Í[>ÌÁŒ>Ì¥Œ>ÌžÚ>Ì _>Ì¥ª>Ì«…>Ì®Ù>̯Ô>Ìš›>̦>Ì›>ÌW>Ì:…>ÌÔ>˃ß>ÊÉ|>ÊÊ‚>Ë{´>Ëâ]>Ì3Í>å™ð>Ì+ù>Ëïà>Ë¢P>Ë*ü>ʽ'>Ê—R>Ë/]>Ë ,>Ëñß? ûc≯B>ËÑõ>Ë…ß>Ë7>Êô>Êô‹>Ë0<>ˈ~>ËÐä?£8?«–?´z?¼‡?ÂÊ?ÆY?ÆÑ?Âd?»Ë?´A?©Â?µ?Âò?uC?6ô?:Ç?:™?5\?k¥?¿=?¯¬?¿H?$P>ç&£>ÍÔš>ÍÛ>Íà¢>ÍÛe>â7V?•?µŽ? À¿>Î)y>Í—–>ÍÁ2>ÍÚ³>ÍáZ>ÍÁÌ>Í£ˆ>Íà)?#|>ÍßÄ>ÍwR>ͽ>>ÍûÍ>Î+Ð>Î2>Íô >Í»ª>͉€>æžÕ>ÍF>͆>ÍÔ¬>Î;¦>ΡX>Î’>ÎRˆ>ÍÖñ>Í‚_>Í!>Í(¿>Íf–>ÍæU>ΛŒ>Ï_K>Ïj·>Ρ;>ÍÙà>Ín§>ÌóE>Ìäš>Í„>Íl<>Ît¬>Дê>Ч>ΗÀ>Í„>Íx>̾Y>Ì¢ñ>ÌŸ–>Ì©j>Ì ¨>Ì¿¸>ÍkÛ>̹•>Ì£ô>ÌŸ.>̉>ÌS>Ì0~>ËÍ>Ê×¼>È¢#>Èor>ʺ">Ë¢>Ì'3>Ì`{>Ì5>ËÜÆ>Ër">Ê >Éã>ÉÊè>Ê n>Ët™>ËÛ@>þ³J>Ëý&>ËÁõ>Ëh`>Ë£>Ê>>Ê‹ >Êóå>Ëb@>˵S?£P?¬q?´:?¼0?®?Æ/?Æ? ?»u?´m?©›?´F?Á…?nµ?7>?;?:'?5?eÅ?¾\?°I?¿P?%>âóö>ÍЂ>ÍÝ¢>Íâ9>ÍÚë>Þöö?â^?´È? ¨T>Î)`>Í—Ž>ÍÅ >Íâž>Íã>ÍÁâ>ͧ>Íå?#C>Íàð>Í|Ø>͹ò>Íõs>Î"k>Î,>Íþë>ÍÁ5>Í„">âdo>Í?>̓«>ÍÙí>Î@;>μ>Ι9>ÎEZ>ÍÝ0>Í‹5>Í>Í*½>Íbx>Íà–>Î’>ÏpS>Ïu;>Δ˜>ÍÓA>Íhº>Ìú@>Ìæ>Í>Íx>Îu&>Ð{É>ж3>Θå>Í‘©>ÍN>ÌÆÕ>Ì¢á>Ì ì>Ì£G>̧Ì>Í6G>ÍhF>Ì“>̳w>ÌŸt>ÌŠÓ>ÌT¢>Ì0L>ËÓ#>ÊÈ^>ÈwW>Èoû>ʼî>Ëœ_>ÌÝ>Ìc]>Ì’>ËØ_>Ëlì>Ê«Y>ÉÚ³>ÉÉ>Ê¥g>Ëp¯>ËÚü>þ•«>Ëõá>ËŸ>Ëct>Êø–>Ê”·>Ê•>Êò>Ëk>˸Ê?¡ù?ªc?±·?¹&?¿;?ÂÕ?ÂV?¿)?¸ª?±a?¨}?³Æ?¾?»¯?2}?4x?3ð?/4?´ã?½?®è?¼Î d>ÍÜ,>ÍÖ1>ÍÝ'?ÿ×?e?®G?-=>Γ*>Í’µ>Í·N>ÍÁz>ÍÆE>Í©a>Í.>Î2?f:>Î7¨>Íu>ͪì>ÍÔ>Íøô>Î.>Íß>Í¥Œ>ÍvJ>ÿï‚>Í2w>Íi>ͱ >Î Î>ÎMÜ>ÎMü>Î >ÍÃÎ>ÍmQ>Í^â>Í:>ÍHM>ͦb>Î.Q>Ζ¬>Η}>Î2&>Íšó>ÍZ >Ìø¯>Ìéx>Í V>Í:¤>ÍÔé>Îp”>Κ$>Íá¸>ÍWð>Í Ÿ>ÌÎH>̘o>Ìž&>Ì£ê>Ì•Š>Ì·â>̳;>̱?>̧Ï>̨a>Ì‘Â>ÌbÖ>Ì8Ž>Ëûe>Ë…O>ʸÙ>Ê´!>Ëp,>ËÚw>Ì(á>ãåI>Ì v>Ëò§>Ë>Ë%>ʲº>ʘv>Ëã>Ë™Õ>Ëò? (l>̲A>ËÓð>ˈâ>Ë3ú>Êí>Êñ¤>Ë(Ÿ>ˆ>ËÈ?Ÿ$?¦>?­P?´4?¹ƒ?¼:?¼?º_?³ë?¬ö?¤Ð?®G?·×?ÂU?º"?lw?f?³¦?À‚?·r?ª?·&?ÂS?ÏÞ?/>ℜ>ßÂ? t?ÂK?¾´?¬#?º‘>üIî>ͯ™>͘i>Íš‰>ͨ">Í‘¬>ÍŸõ>ô^(?¬;>ü>Í_5>͆é>Í¥a>ÍÅ >͹>ͧu>͇>Í`µ? Ów>Í?->ÍE >Í}U>͹ >ÍÙ_>ÍâÑ>Í·‰>̓ê>ÍK¬>ûvÞ>Íû>Í0[>Íi—>Í®:>ÍÜÐ>ÍÑ‹>Í®ß>Íl>Í6=>ßRí>ÌãÖ>ÌûŽ>Í~>ÍS¦>͇„>Í>ÍU>Í9>Ìð6>ÌÆq>ÌT>̘2>Ì¢>Ì’>̲>Ì¢µ>̳>̱´>̧Å>ßõÏ>ÌcÇ>ÌOÑ>Ì4Ž>Ëþ“>ËÃã>Ë»õ>Ëê§>Ì(ò>ÌD¶>ÿº>Ì8Æ>̺>ËÜñ>Ë’t>Ëa>Ë_É>˘Ò>ËÌd>Ì Ç?‰z>Í8>ËìX>ËÄW>Ë‚>Ëc7>ËWp>Ë…ë>ËÁ>Ëé2?›s?¡‰?§Ð?®8?²d?´P?´ø3>ÎR…>Íâ>ÍäŒ>Îü>ôx9?»²?£¸?vc>ãZö>Í^7>ÍpÄ>ÍxÆ>Í~š>Ín¡>Í\>ßx~?›&>ãÃ=>Í.¥>ÍG÷>Ís[>Í{”>Í…>>Ío0>ÍN>Í13?k>Í>ÍI>Í2þ>ÍRº>Íf^>Ík”>ÍJ_>Í0N>Í2? |$>Ìî>ÌÝÅ>Ìöñ>ÍØ>Í$F>Íh>Í ­>Ìï²>Ì×?ƒ>ÌÃÅ>Ìš>̨’>Ìž.>Ì¡+>̨€>Ì©ƒ>̤.>̦-? Q>Ì>Ìa¨>ÌOÒ>Ì/,>Ì(>Ì)´>Ì4Î>ÌEÝ>Ìeo?OÛ>äâ>Ì-?>Ìø>Ëïù>ËÒ>ËÍY>Ëè†>Ì >Ì*L?Pä? Ä>̹>Ëô…>ËÌÜ>˾¢>˶ï>ËÌb>ËòÅ>Ì?‹Ü?Žš?‘I?“o?•?–^?–A?”e?’?H?š?“2?–Ó?™V?›?œ0?œ?šP?˜?•/?“‰?—ô?œé? ,?¢?£~?¤6?¡Ð?ža?šº?—¨??£^?§ˆ?ª?«â?¬[?©Ï?¥? à?›ª?¢?¨ÿ?®Y?²U?´ù?µÀ?²½?­4?§A?Ÿ)?¦¡?¯>?´å?º?½H?½ð?º^?³û?¬Ø?¡¬?©Ä?²É?¹‘?¿®?Ø?Äe?À(?¹n?±H?£?«§?µ•?¼e?ÂÆ?ÆÆ?Ç¡?ÃN?¼E?³?£?«?´×?¼J?ÂÍ?Æz?ÇM?»?»•?³?¡¢?©“?²s?¹z?¿m?´?¬?¾ð?¸x?°›?žã?¦?­ì?´G?¹o?¼u?½d?¹«?³T?¬r?›Q?¡é?ª.?®0?±ÿ?´??´;?±x?¬“?¦ä??(?’Ä?•?—?—Ü?˜]?–Ø?”Õ?’E?‘i?– ?™n?›„?žk?ŸŸ?Ÿ!?ž&?›–?˜3?•è?› ?Ÿ3?£u?§x?©:?¨Œ?§g?£È?Ÿ/?šÔ?¡ ?¨?­ª?²s?µ?µ2?²k?­W?§.?ŸÃ?¨c>ç5>â‰Ý? øÁ?Ò7?¾ç?­i?»™?w…? ;Þ>ÎB/>ÍÚó>ÍÐW>Íü¹?Ü?le?°2?¿E?#~>ç'^>ÍÔe>Íàµ>ÍØD>ÍÔÉ>â7y???°C?¿]?$$>âôA>ÍÚÖ>ÍÝ<>ÍÞ>ÍÙ»>Þü?ã?­ð?¼g?r ? $]>λ>ÍÚó>ÍÕX>ÍÛ?L?g~?©«?·@?Ä&?Ò®?0Ñ>â>ßT? †?ÁE?À?£z?®_?¸å?¿u?ox?'Y?óX?fž?¿é?»?(?$?“p?–Î?™?™›?˜×?™™?–ø?“?‘Á?•°?šA? ?¤]?¥„?¥i?¤? #?›[?˜8?žµ?¥Z?¬®?²~?µž?µO?²Ø?¬å?¥’?Ÿ¡?ªQ?±¶?¼œ?0€? ¾O? …š?-z?¹Ó?±è?¥¥?²«?yP?-Æ>δt>ÎO>ÎT>Îkl>ûè;?n†?¬?½s?9 >ͺa>Í4>ÍŸ†>Í¢x>Í…ï>ͯx>÷º”?²ž?3ì>ÎÂ>Í@>ͳ5>ÍÅ2>ÍÞ>Í«c>ÍŒ)>Î94?¶»? Á¶>Î'å>Íç>ÍÇd>ÍÛÉ>ÍÜ>ÍÇÉ>͘J>Íä ?¶? ©>Î#¾>Í™>ÍÇÙ>ÍÝý>Íܨ>ÍÎX>Í2>Íæ‚?³I?1¼>ΑÕ>ÍÛ>ͯ[>ÍÀt>ÍÀ->Í´!>Í”8>Îõ?­’?¾*>üM§>ͯ%>Í‘>Í›ç>Í“Á>Í™ >Í©Ó>ô]µ?¥f?²Ô?‹>ø6Ó>ÎKá>Íßu>Íå¦>Îr>ôy?½?Œ¾?Ž›?‘~?”ª?—û?˜Ò?˜A?—!?–\?’6?8?”O?˜þ?ž½?£å?¥„?¥(?¢©?Ÿ{?šw?• ?œå?¥j?®T?jä?"?""?bá?­¡?¥¥?œ°?§9?m0?‚>ÎM”>ÍÆ*>ÍÄ«>Î î>û¾/?]?¥Ü?o¤>ç‹ >ÍSg>Ípõ>Í„Z>Í‚à>Írf>Í\µ>âù9?­Ë?%`>ÍY>>ÍyY>Í£>ͶÙ>ÍÀ>ͬ>Í‚·>Íc(?l >Î[1>Ír˜>ͧ/>ÍÑF>Íú¤>Î>ÍÚð>Í®ÿ>Íz1?#¯>Íå¶>͈ß>;å>Ι>Î&¸>Î&ê>Íù<>ÍÊ>Í‚¾?$>Íݹ>͉y>ÍÀs>ÍúØ>Î,Z>Î%U>Íûç>ÍÄ>ÍÕ?h*>Î;Ž>Íw—>Í­R>Íܸ>Î Ô>Î>ÍÙ>ͤL>Ín´?®4>ü>ÍVÑ>Í„>Í¢+>ÍÊŒ>ÍÇæ>ͤ>Í‚¾>ÍZ£?¤v?uÈ>ãY½>ÍXˆ>Ír¿>͆†>̓µ>Íry>Í[->ßw…?†|?ŠÚ?ŽD?‘˜?•?•@?”f?’M?2?ކ?Œæ?‘)?–1?›·?žÓ? ü?¡)?žQ?›¹?—!?“?™f?ŸÀ? ß?>ÿê¼>æ>â z>ÿÈv? ¯Ž?œ_?™s?£>çõB>Í7>Í>i>Í>=>ÍCF>Í;9>Í?½>ãb? Ë>çù >Í'b>ÍH•>Ím_>ÍwÄ>͈m>Ío>ÍV >Í-4? Ý>ÍM>ÍM_>ÍyÖ>ͧç>ÍÙ:>Íâ >ÍÂÆ>̓Ó>ÍP2?Ü>Í7>Íkÿ>ͪ>Íý>ÎI³>ÎPÎ>Η>ͺ>Ín¶>æ£>Í@®>Í€¬>ÍÙ>Î>S>Πl>Ψ”>ÎV>Íä;>Í„¶>âiÚ>ÍI>>Í…]>Íâ*>ÎEÉ>Ι¸>Ρ>ÎKî>ÍÜ)>͆Ž>ÿîÉ>Í;q>Ín§>͹3>Î ¥>ÎGˆ>ÎC\>Μ>͸‹>Íx ? Ó)>ÍA5>ÍQ™>͉>Íá>ÍÚ7>ÍÜ0>;>Í…@>ÍSö?›;>ã¿Ú>Í-—>ÍQ>Íqÿ>͆>Í„J>Ío¶>ÍR¢>Í7é?ƒ€?†?ˆ±?й?Œ'?Ž?Œ?Œ8?Š(?ˆ}?†€?‹?¼?’¤?•Œ?—Ø?– ?•W?’‘?Žó?‹?‘½?M~>ÿÆ>Í—>Íó>ÍÎ>ÍMK>û,×?æu?‘ó? $”>Í­>ÍÎ>ÍJ>Í!¿>Í&Y>Í#>Íý>Í^F?L§>;!>Í 9>Í1ï>ÍJÅ>Íi>Í[{>ÍL>Í-È>Í®>ÿ™«>Í>Í,b>Íqž>Í®>ÍÛÒ>ÍÏË>ÍŸ÷>ÍjÒ>Í6O>Í›Ø>Í—>ÍMÅ>Í¥>Î%Q>Έ¨>Τ>Î$>ͳ/>ÍU=>Í´>Í#4>Í_E>ÍÒ>ΆÇ>ÏY&>Ït6>Î’Î>ÍóJ>Ív >Í">ͤ>Í[ù>Í»ç>΋Þ>Ïf>Ïi—>ίe>ÍéA>Ívº>Í^–>ÍM>ÍL¢>Íš5>Î.õ>Î… >Î¥Õ>Î º>ͨÊ>ÍVj>ûr»>ÍÖ>Í1>Í^>ͤ>ÍÛÀ>ÍÝ>ͱE>ÍjØ>Í2?³>Í >Íò>Í+Š>ÍJò>Íb>Ín>ÍQx>Í9s>͉?~ ?ë??æ?Æ?ƒ’?„À?ƒo?‚"?×?|–?‚@?„ë?†t?‰?‰¡?ŠN?ˆµ?…?ƒÌ?‚?„? ˳>åá>>Ìß>Ìçz>Ìߢ>ÌßÐ>Þç»? DÙ?ƒ“? Iˆ>Í É>ÌÝØ>Ìââ>Ìí~>ÌêÅ>Ìéd>ÌÜå>ÌáU? Æ>Ìÿ”>Ìä->ÌôÑ>Í N>ÍÒ>Í+Ì>Í Á>Ìò×>Ìè>åë|>ÌÙ’>Ìû2>Í X>ÍOw>Í’õ>ÍQ>ÍUP>Íb>Ìøµ>ÌïŠ>Ìîš>Í Ü>ÍDÆ>ÍÖ>>Î~1>Îo³>ÍÑÇ>ÍGL>Í W>Ì÷w>Ìð(>Í>Í„g>Îim>Т>К»>Α¯>Ít’>Í>Ìõf>ÌìR>Íh>Í™Ö>Îu+>ÐŽû>Ðïp>Ε#>Í{¦>Í'>Ìô·>Ìà^>Í)>Í\B>ÍÔ>ÎuV>Δ>ÍÚI>ÍQ3>ÍØ>ßJ>ÌÑ„>Ìùâ>Í!¢>ÍKþ>Ítl>Í”Æ>ÍSÀ>Í>Ìõî? z›>ÌïÃ>Ìé®>ͧ>ÍÇ>Íð>Í>Íó>Ì÷Q>ÌÜ ?xÞ?yþ?xq?x?wð?x1?x?xœ?xž?yE?uÊ?y6?w ?wh?xR?xy?w˜?xE?wþ?xv?x‰?w? ³>ÌÕ•>̬\>Ì­w>Ì®Ï>̬Í>̯?¸‚?vò>æ¢C>Ì©¢>Ì™Å>Ì ,>Ì¢ã>Ì¡.>ÌŸ›>ÌœÉ>̯ô? ´m>ÌÄå>Ì Ô>ÌšC>Ìžò>Ì¢ì>Ìz>̦>̤×>Ì›0>ÌÙ>Ì›È>Ì›>̦>Ì¥ô>Ì¥n>Ì­>Ìžt>̨h>̧>Ì¿‚>Ì™J>Ìžl>Ì£a>Ì­R>̺¹>Ì¥•>̨­>Ì” >ÌŸ„>ÌÀ¸>Ì¡í>ÌžÔ>̰e>Ì™@>Ìë„>ÍI³>Ì»ê>Ì¡˜>Ì ½>ÌÀ,>Ìž]>Ì¡'>Ì›¤>̦3>Í ^>Í?/>ÌÜ>Ì­Õ>Ì “>ÌÁí>ÌÌ>Ì£E>Ìœ>̲ž>Ì©>Ì©@>̰ >̧€>Ì–ü>ÌÌÞ>̨>Ì—¶>ÌŠ¨>ÌŸt>Ì à>Ì¡>Ì¢>Ìže>Ìž?‡>̾à>Ì—0>Ì“Ý>̘î>Ìœâ>Ì¡r>Ì×>Ì©>Ìž@?rç?pë?o¶?ni?mX?mÃ?mB?nd?nÞ?o?pG?ph?lQ?i¬?fô?g?g?hz?jÁ?my?qÊ?l? ¥€>æ%7>Ì~>Ì€Ã>Ìza>Ì‚Î>߇‡? ]?js? »>̬ª>Ì_6>Ì^Ã>ÌN£>ÌTï>ÌY>Ìcø>Ì}}? ¥7>̧D>Ìkµ>ÌS»>Ì4ž>̶>Ì![>Ì3>ÌD/>ÌdÂ>æ8(>Ì`ù>ÌT¥>Ì#>Ëó§>ËÅ>ËÂ>Ëï½>Ì)>ÌC>Ì•&>ÌRD>Ì3‚>Ëí>Ë|A>ÊÈÐ>ʸ>Ëxð>Ëû“>Ì1j>Ìö>ÌQ›>Ì! >˼)>ÊÌ>È”>ÈŒ®>ÊÀQ>ËÑâ>Ì ß>Ì”µ>ÌM%>Ì%¢>Ëà >ʾ`>Èx`>È{>Ê´ð>ËÂ>Ì&{>Ì‘”>ÌZB>Ì/å>Ëë>Ëp0>ʱ>Ê¥‘>Ëm">Ëê•>Ì2ˆ>ßþø>Ìci>ÌNm>Ì$K>Ëè˜>Ë·ˆ>Ë«È>Ëþ >Ì>ÌHÙ? Q>ÌÅ>Ì[Æ>ÌNð>Ì,F>Ì;>Ì"E>Ì=$>ÌE6>ÌX?mU?i÷?h:?fW?d&?aÿ?b×?e‰?fu?gô?i˜?dp?b ?_r?[Ê?YK?Y¹?[ ?^¸?a¶?c¼?^M?T³>ÿ’>åŸ >ÌO~>ÌS¶>âÞ™>ÿgV?…?_¯?«O>æ`ú>Ì06>Ì!9>Ìò>Ì!:>Ì#z>Ì7k>ã !?T¯>æ»>Ì1Ë>Ì ã>Ëðw>Ëãy>ËÖ>Ëé—>ÌØ>Ì0E>ÿ™Q>Ì;>Ì Ã>ËÕI>Ëœ>Ëeî>ËhE>ËÀ>ËÑ®>Ì Å>åŸì>Ì->ËñË>Ë>ËÃ>ʵ>ʪò>Ë«>Ë£m>Ëçù>Ìoo>Ì>Ëâz>Ëg<>ʽ×>ÉÖ>ÉÙª>ʯ>ËVR>ËÕ5>ÌhZ>Ì&ä>ËØ/>ËoË>ʲ­>Éâ!>ÉÒŽ>Êš9>Ëm_>ËÒý>ãݵ>Ì&;>Ëñæ>ËŸf>ËZ>ʸø>ʦß>ËÈ>ˤ>ËíÓ>ÿ‹s>Ì7Ÿ>̼>ËÕî>Ëš>Ëa•>ËlY>ˆK>ËËX>Ìœ?N>ä=>Ì'>Ì\>ËîÞ>ËÙ0>ËÔE>ËæÏ>Ì –>Ì.L?i1?eË?bØ?`?]Ö?\?\G?]7?_?b?d=?_B?Zë?V[?R?Px?PÕ?Rü?V?Z›?^%?U¹?OÜ?šØ? Û>þD°>þ#=? ÐÉ?„Ì?M÷?Xm?KÍ?ª>Ì]´>Ìu>Ë÷>ÌZ{>̈í? R?Q%? «>ÍÑ>Ëóv>ËÎ.>Ë»û>˾ >Ëσ>Ëòt>ÌaU?›Â>ÍVw>Ëô¨>Ë¿<>Ë„à>Ëeü>Ë]…>Ë‹)>Ë¿›>Ëð™? ý<>̼ù>ËÚ´>ˉj>Ë3>Êô8>Êð$>Ë3Ý>Ëò>ËÉc>þª±>Ëõ•>˾z>Ëi_>Êòò>Êž>ʉ…>Êîh>ËYU>˰þ>þ“c>Ëýj>Ë¿y>ËlŒ>Ëš>Ê›á>Ê‘˜>Êè >ËT&>˲X? ,C≯)>ËÑÙ>ËŠ>Ë/¿>Ê÷>Êú`>Ë,`>Ë}ð>ËÐC?ŒE>Ìü,>Ëí>˼j>ˆ¬>Ëd™>Ë`ú>ˇ!>ËÁÜ>Ëñÿ?P'? _>Ì·l>Ë÷N>ËÒS>˹>˹>ËѼ>Ëîó>ÌQDy—šDy—šDy—›Dy—›Dy—šDy—¥Dy—«Dy—šDy—šDy—“Dy—šDy—šDy—¥Dy—¥Dy—šDy—›Dy—£Dy—šDy—šDy—’Dy—šDy—šDy—¨Dy—¥Dy—šDy—›Dy—³Dy—žDy—šDy—’Dy—šDy—šDy—ªDy—¦Dy—šDy—›Dy—¡Dy—œDy—šDy—’Dy—šDy—šDy—›Dy—›Dy—šDy—œDy—¦Dy—¢Dy—šDy—‘Dy—šDy—›Dy—«Dy—šDy—¸Dy˜¢Dy˜ºDy—ÅDy—šDy—‘Dy—šDy—›Dy—£Dy—¸Dy™{Dy™ŒDy™•Dy™„Dy—ÆDy—‘Dy—šDy—Dy—³Dy˜¡Dy™‡Dy™ˆDy™–Dy™‹Dy˜µDy—”Dy—šDy—›Dy—¦Dy˜®Dy™‡Dy™ˆDy™›Dy™ŒDy˜ÂDy—”Dy—šDy—šDy—²Dy—ÂDy™‚Dy™‰Dy™‰Dy™‰Dy—ÏDy—’Dy—šDy—šDy—šDy—šDy—ÆDy˜µDy˜ÚDy—ÚDy—šDy—’Dy—“Dy—’Dy—²Dy—”Dy—’Dy—“Dy—•Dy—’Dy—’Dy—ŒDy—šDy—šDy—šDy—šDy—šDy—¢Dy—´Dy—šDy—šDy—’Dy—šDy—©Dy—®Dy—›Dy—šDy—šDy—›Dy—šDy—šDy—’Dy—™Dy—šDy—šDy—™Dy—™Dy—™Dy—£Dy—Dy—™Dy—‘Dy—™Dy—žDy—¬Dy—¤Dy—™Dy—¢Dy—«Dy—›Dy—™Dy—yDy—›Dy—žDy—¨Dy—¹Dy˜×Dy™èDy™ôDy˜êDy—µDy—IDy—›Dy—¦Dy—ÍDyšÇD…rDE(9eE3DŠqGDyž/Dy—/Dy—œDy—›Dy˜éD…tE›®'Ež†õEž§ØEo®D,åDyšµDy—œDy— Dy™êE(ÁEžÑEŸëEŸ¼EŸ `E6mkD{ÞcDy—•Dy— Dy™ÛE2CïEž’uEŸ”EŸLEžþÀE?ËD|%âDy—•Dy—šDy˜ôDŠ[¦EC?EžùþEŸëEžé#D–K\Dy  Dy—™Dy—™Dy—µDyž(DívE5ê‡E?¹yD–QDy¬$Dy—1Dy—’Dy—Dy—ÌDy—úDy›ÈD{ÛD|& Dy¡KDy—÷Dy—gDy—šDy—šDy—œDy— Dy—šDy—Dy—­Dy—šDy—šDy—’Dy—™Dy—¦Dy—²Dy— Dy—™Dy—™Dy—™Dy—™Dy—™Dy—‘Dy—šDy—šDy—šDy—šDy—šDy—šDy—¢Dy—¦Dy—šDy—‚Dy—šDy—Dy—¤Dy—šDyš¬DðÑdD÷qªDy›'Dy— Dy—7Dy—šDy—Dy˜œEp¼ENXøE%vE<]EÛ¢Ewº]D|‰ÛDy—šDy—šE3ÎîD¦¥hDAxøCi>ÃD„ØþDól¢E›rE;d'Dy—šDy™¦E/´Do'AHž @ƒ•6@ƒ‚D9z`E;}¼EÇg9Dy—›Dò¯D¶€ôCg)Á@ƒ¥¯@z‚ö@zBý@ƒxDð\HEÉ–³Dy—™Dø;ïD¶Ü$BÜñX@ƒˆ>@z`Æ@zÀ@ƒ]àDÞ;EÉ0¨Dy—™Dy™ïE Ê$Cç÷ð@Ø•ë@ƒŒü@ƒgD*ÿËE,ø.E»–&Dy—šDy—ªE7nóDƒ_ÂCܘñBÒûøDZ2ðDÂÕ E«ME<´àDy—›Dy—¿D~ïÈE.d E¯dD õD§e—E¯rØE<² D‚þ5Dy—›Dy—šDy—Dy—¢Dy—šDy—šDy—žDy—ªDy—šDy—‘Dy—™Dy—Dy—¥Dy—£Dy—¤Dy—ŸDy—¢Dy—žDy—™Dy—yDy—šDy—šDy—šDy—šDyš¦DðÖ¹D÷vžDy›PDy— Dy—8Dy—£Dy—¼Dy™E–ÄIE(ûD•ùEzßIE¢ÛrE–M”D‚/Dy—šDy˜™EIgÃD àA˜à‡A‰|A]õ_D=ÂsE°›EÓDy—šEnó²CÖ––@i™?€2?€?€?‰ÂAËh­E…Dy˜¡D¶SfAxƒî?‚/Ž?€?€?€?€A ~DŽEâA g?€?€?€?€?€?€?€CÖD31A>>?€?€?€?€?€?€?€Bý#¾D ŒöA*ƒ ?€?€?€?€?€?€?€CÓ‰öDöHÔCˆ×f?€ ,?€?€?€?€?€?€DÕïDy“wDÅÂLBå^³?€?€?€?€?€A ŒPEWÑžDy—›Dy—šDy—šDy—šDy—ÖDy˜£Dy˜®Dy—ÀDy— Dy—”Dy—¯Dy—šDy—±DyšÏD…qÜE(C±E2ü|DŠqúDyž?Dy—:Dy— Dy—šE3Ó D¦®=DAjÌCi99D„èÚDó>§E›j~E;b~Dy—šEnÖ÷CÖ–Î@i4¦?€¼t?€?€?‰âAË7XE•µDïdCÔp?€V??€?€?€?€?€?€DÚ4ŽD+A@Ãêÿ?€?€?€?€?€?€?€Db~Cñaß?…€Ö?€?€?€?€?€?€?€Bd@tCdQÍ?€?€?€?€?€?€?€?€@!~BÕ>n?€?€?€?€?€?€?€?€@Ó C¢Ëº?ƒ>”?€?€?€?€?€?€?€BTYôC°½8@z_?€?€?€?€?€?€?€D#àDsª=Bÿ&¦?€?€?€?€?€?€?€DÕúìDy—›Dy—šDy—›Dy—¸Dy™‘Dy™Dy™‡Dy™ƒDy—ÈDy—’Dy—¹Dy—šDy˜ãD…q‡E›ªtEžŽFEž–EgnD-YDyšÂDy—›Dy™¤E/ wDh°AH«f@ƒ˜¨@ƒ·D9{žE;yEÇg%Dy˜¤D¶KáAxg©?Èþ?€?€?€?€A GDë‹€Dæâ_A| È?€?€?€?€?€?€?€CÛD+Cñ;Ù?†7P?€?€?€?€?€?€?€Bd]A +q?€?€?€?€?€?€?€?€@ƒˆ\@El²?€?€?€?€?€/*?€0l?€?€?´{@ETú?€?€?€?€?€+?€A ?€?€?sÓ@™L¹?€?€?€?€?€?€?€?€@v¿RC4²}?€?€?€?€?€?€?€?€BX4.D;Õ@ǽß?€?€?€?€?€?€?€CÑ*RDy—šDy—šDy—›Dy˜ Dy™‡Dy™ŽDy™—Dy™‰Dy˜¶Dy—ŸDy—œDy—ŸDy™ïE(éEž“•EŸúEžÿ Ežö!E6d¬D{ݸDy—šDòüD¶y Cg"=@ƒ¤e@z@z:“@ƒs‘Dð9'EÉ’sDy\ûDzAa,?€?€?€?€?€?€ „D´5 DŽEÁA d?€?€?€?€?€?€?€C¬CdRš?€?€?€?€?€?€?€?€@ Tø@Elƒ?€?€?€?€?€2a?€*Ç?€?€?º @=CN?€?€?€?€5 BVj:CV™?€#³?€?€@=,E?€?€?€?€"B¶»ãCÕ2"?€D ?€?€@E /?€?€?€?€?€J?€H=?€?€?žkBµ—ð?€?€?€?€?€?€?€?€@ÑîCaE”?öh×?€?€?€?€?€?€?€BíhŽDy—šDy—°Dy—›Dy˜®Dy™‡Dy™‹Dy™Dy™‰Dy˜ÂDy—©Dy—•Dy—¥Dy™ÚE2FEž§ºEŸVEžú7EžõŠE?ÑD|%«Dy—™Dø:¦D¶Ô BÜð™@ƒŠ«@z^¨@z%@ƒ\ýDÞDEÉ2™DyTîDG@ØÏÆ?€?€?€?€?€?€ ¾D³ÇôD>»Ad?€?€?€?€?€?€?€Bý»iBÕ ö?€?€?€?€?€?€?€?€@Äd@EU+?€?€?€?€?€,(?€BN?€?€?Šû@=,d?€?€?€?€(ÂBö{C¶â¥?€:Ÿ?€?€@=‡?€?€?€?€(jC9ú­Dùë?€,Å?€?€@E ¼?€?€?€?€?€=6?€L>?€?€?@jAu4-?€?€?€?€?€?€?€?€@mõC\?öW‘?€?€?€?€?€?€?€Bê+!Dy—šDy—£Dy—šDy—ÀDy™‚Dy™œDy™‘Dy™‘Dy—ÐDy—–Dy—šDy—·Dy˜æDŠ[ESÀEžðŽEžúÊEžÝúD–I,Dy XDy—ÂDyšE É$Cèè@ØlÁ@ƒŠ0@ƒd¸D+%¢E,åiE»ŠDy˜¤D¶ ñA7ñJ?€è˜?€?€?€?€A%äDéu×D ˆbA*»M?€?€?€?€?€?€?€CÓ¼ C¢ÎÅ?ƒŠñ?€?€?€?€?€?€?€BU L@˜å@?€?€?€?€?€?€?€?€@w‡w@E 8?€?€?€?€?€=?€F¯?€?€?Ñ@E ‡?€?€?€?€?€D¹?€Dù?€?€?=õ@L„Ù?€?€?€?€?€?€?€?€@h‚!BWÒÜ?€?€?€?€?€?€?€?€BH¹´CÄÞ~@(ä?€?€?€?€?€?€?€CÉë9Dy—šDy—šDy—šDy—šDy—ÆDy˜µDy˜ÂDy—éDy—žDy—“Dy—œDy—šDy—´DyžADñáE5á¦E?¸JD–MÿDy¬Dy—5Dy—šDy—¼E7q DƒaqCÜ™dBÒÍaDZ3RDÂárE²ÔE<ºàDy—šE)8 CŠ‚f@ZY?€?€?€?€A•ÞNEsÏ DöL#CˆÐH?€ ?€?€?€?€?€?€DÕ'áC°¢ß@{yý?€?€?€?€?€?€?€DÙÛC4¤«?€?€?€?€?€?€?€?€BV÷Bµ«3?€?€?€?€?€?€?€?€@‚­Au/=?€?€?€?€?€?€?€?€@*«BWÖ¡?€?€?€?€?€?€?€?€BI«ÊCF?¶õm?€?€?€?€?€?€?€D ÌûDnÑÄBrÕ?€?€?€?€?€?€?€DÐ`cDy—”Dy—“Dy—“Dy—›Dy—˜Dy—”Dy——Dy—¤Dy—’Dy—ŒDy—“Dy—žDy—¸Dy˜Dy›ÓD{Û—D|&”Dy¡®Dy—øDy—jDy—›Dy—ÀD~ðCE.V¤E¡D ãD§f^E¯m¡E<«ïD‚üÞDy—ÕD~Ý&DÅÑOCGÿAÍ@}M§@} D,¸šEȇEæŒDy“DŹrBåN¶?€?€?€?€?€A ÀEW¬ Ds®’Bÿ=Š?€?€?€?€?€?€?€DÕéBD;u@ÈWó?€?€?€?€?€?€?€CÑzÆCaK?ö%,?€?€?€?€?€?€?€BíÔÚC\»?ö„?€?€?€?€?€?€?€Bê:÷CÄßé@’?€?€?€?€?€?€?€CÊ=DnÚ BrW?€?€?€?€?€?€?€DÐfÓDy(C¶ÑcAa©¶?€?€?€?€?€?€ENÞ]?ÉXÝ?öP@?úm@DÜœ?é…??íâÆ=Åü?¿Ðr@Ž ?ø|ç>(Ê‚@ŒU?@*}ü@­¢=È I?ŽH¬>¸Z>?(j>.ù_>¥­œ@}U?·j>.þ@6«ä>©aS?ì—­?p ÿ=¤Tû?Îß?A’Î@ÿ_=÷Î>þy'@v—@ °>­¾2>ec@ &ñ¯ºï@€ã€?ð@@»† >}¥r>N*=ÀX?¡²¾@ç@ˆú@YJÝ=èž=JŠ ?Ú:?*‚þ?÷ÿ‚?ã‹>ðD¹?ùó‰>Ð8?¯ƒ(? §4?Æþ @rû”?Dëá?þ@•ba>“=r@õn?_‡>kcß@>§k>íl?(E\@®£A?«JL@½?”@%¦s>éIZ=ÜË =šv@‹§ü?M;—@V§?Ðòn?¾k?i@‘@ Dù?â ´@k-w?ˆt#>ÃSÇ?¤9?€Ÿ@WçÉ?ÿt+@„¥â?ŽñI>;!5>»µ¨?¾K?oÕ/=dU=?Nåu>;áG?ix>nÉ=¢[«=c)Ò?Û¡?™¥¬>g­ö>:¬>à„Š>´ç<> v%?Ýx?ºúÔ?ÓÞ@,+r?&®\?ùUm?àÈp?ØŒë>b=Š?‡iE=žÓJ>!vÎ=6Ì?Áÿ@¯†}>©vx?í«@rM=˜æT>o|á@N®Ü>H??‹Ù>c ï>Zc'=®w½?–‘À<»Ç@¦ûb>°½é<`ç?‡Ô?W4Õ?˜@@Ém@°ª><,ê=Üf‘<È;@`XŸ?¶}>~¢>íÔ@ •Ò@3e>÷BÚ@'òÀ@+d>3 ’="†<?o„@"îš=t×ð@ÍÅ@]K)?HÞº>â×™?…lN??!}p?$Íc>à„? @mØX>]£r>õ¦O?’O…@‚¦ö=ÑDã?S,ò?†6D?Ö^>ú+a<.µ=|¤?„Ö5?½kô?Åi=·õv@UØ{?³à?‹–t>/ñ×>ý¸4? ý@¢tø>ØÍ<¤í^@!%Y=\LÆ>ÞN<§.îÀný@-ï?å Ûuù# ?èys"çŠ?å/p‰¸Â?èr&B ?äí¹]+x÷³?æ2ûtÍd?çψÞp½??è›S¿g?éw-ù\Ñ/?êŒ/îÆih?ëž•É=?àE2[ßzè?წȣÜÑ?âá™  ?ãê›Ðñ?äD¤k˜>ë?å–Ôβ•D?æF¦/矴?çE‰‡y?è+ñ„ÑÏâ?ét±Í£€å?ê(Þÿ\9?ëÓ¤W \h?àÈ éf?á›?‚­Ô?àÈAÁ9ßÅ?á& Ò¨œ?à|Ĺjï?àªAƒ¹é?áÄYòtzÄ?àGzè¬?á=žÔ–¹}?à-¬Ì9º ?ávôè²§Ò?ái×_šÙi?àÎcBR??ás­p¿ X?àEÜ¢§éë?àj"ï6?áÿW@¬úh?à,BÓ&þ?á”+c¸.?áUæóÉñ?àšÙŸÿ•Ä?á~…~$9?àê$gÅ?áFJi]?àuyÛœÙ?à4Ø‘â?à5N.^^l?à#ð*¤ï?à{âÌdA?àÙlL?Å?àÙ/ò«jU?à÷ ¥ª @?à}©?sûœ?àïÜ´?jf?à'ülæX?à\ƒÂåØ?àq´îðÀj?áŸ6ô|:>?á­Kä?á¼ þ8?áaRä7E«?áSåƒfÑ?áºË‡â?ájŲ²9G?áȪ,D3&?á4 WXÖ?áìzA©ý?᪮.D4«?áz[Åü}?âJ‚EAëJ?âΕ^ ¹Š?ãÞç8z§?ã ñž¤?äyâoÓ<˜?äŒL±ø‰Ð?ä'þDƒÓ ?åˆì¦ðëŠ?åk|˜§;?æx6a³õ?æc¶,‹Ž£?汜ÛM)3?çÍäÞËÈ?çwŸÑµ]?è˹±Ó:?è&Q>ê?è3õŽÊcÜ?éz³ñ³ö?é\7½ M?é­P Ô?êMÛycö«?êŸ=R)?ëÅPaƒ?ë«æzA?î?•2?“?–‘?”?˜+?‹=?›?z ?wz?mV?`€?a?Ô?™×?Ÿ3?›7?¡ï?‘ù?’]?„¬?už?cÞ?Vƒ?W?¢}?¢Ô?µ?¨Ò?œž?¯?Ã?¾î?¿?ÄÁ?§é?Ë_?ÙÔ?Ê»?®Æ?¸‘?÷Ô?Ç!?·„?º²?Ã'?Ȱ?¼Å?¸Ã?Žƒ?–Ò?š{?¢?¤r?Ãs?¸â?Ö ?¹Ï?Ý?µA?ÁÇ?«ü?—?˜¦?­z?±b?ÃÁ?»Ø?ß9?Ï?ã¢?ÑÉ?Þ)?²´?•³?™Á?šd?™å?ˆó??¦c??“K?†Q?ƒW?‡ø?{?‚Ê?sT?w?t!?i¾?kë?h.?bA?W•?Uð?VÌ@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff@æff A"""!"""$"%"'"("*"+"-"."0"1"3"4"6"7"9":"<"="?"@"B"C""€"ø"ù"ú#¯#±$Ã$Ä$Å&k&l)L)M)N,.,/,0-Ö-×.ê.ë!?@4 4ÿ1PP1Á¶H€! 1À¦1Á¶H€! 1ØQ1Á¶H€! 1œR1Á¶H€SNODØ`WðpX °¥0@ë@Pì! 1`S1Á¶H€  1$T1Á¶Hˆ  1@e1Á¶HˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8¤ TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœqdŒÀDš@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ 8ý8ü P8ý8ü T8ý8üÀç8ý8ýê8ý8ý8ý8ý8ý&8ýú8ý8ýŽ8ý$8üÉ38ý8üIP8ý8üCÇ8ý8üÀì8ý8ý8ý8ý<8ý8ý8ý8ý8ý&8ý38ý8ý<8ý68ý8ý8ýM8ý8ý8ý8ýê8ý8ýA8ý8ýb?‘T?‘?”>?–,?˜O?š}?›”?ž‚?ž÷?¡?Ÿ6?¤?Ÿ?¤Ø?©?£N?›@? ð?—ˆ?›?”î?—è?˜£?œg??¢ ?¡R?§Ÿ?¤û?¬¢?¦õ?°º?¦Ä?¯â?¤Û?­»?¡f?©0?œð?¢È?—’?›u?œß?¢+?¢Ë?©»?¨4?±?¬m?·É?®ù?¼y?®Í?»s?¬A?·l?§ï?±ú?¢?ªE?š?ŸW?¡ ?§~?¨¯?°t?¯£?¹?³½?ÂÃ?¶¤?É(?¶Z?È}?³&?Š?®?»)?§¶?±j?œÜ?¡™?¤a?«L?¬•?¶T?³¾?Á?¹?Ë?¼?Њ?»ý?д?¸›?Ë ?²î?Ã?«Å?·H?žT?¢ä?§œ?¬Ç?¯™?¸o?¶?Å ?¼‹?Î?¿.?Ôu?¾û?Õ:?»u?Ï~?µ\?ÅÑ?® ?º²?žK?£Ó?¦[?­ú?®?¹B?µ?Äl?¼K?Í?¾Ô?Ó„?¾Ü?Ö?»Z?Ϭ?µƒ?Æ?®L?»?7?£?¤U?¬=?«Ä?¶0?²ï?¿ë?¸o?ÈÆ?»Q?Î?»|?ÐP?¸U?ÊÂ?³?«?¬N?¸-?›€?¡Z?¡Ç?¨^?¨â?¯æ?®Û?¸Ö?´?À?µÛ?Ž?µ«?ÆQ?²ñ? ?®l?»Š?¨p?²…?˜ˆ??ž2?£?¤?©³?©M?°r?®Æ?µH?¯?ºi?®n?ºá?¬?·º?¨s?²y?£²?¬??‘M?ŠÏ?†?Šï???‡´?‹6?„š?†2?}l?~ê?xý?va?sš?p?n¬?lC?j?d³?Ù?–Ÿ?Ò?’X?Q?’ö?Š‘?*?†J?‹S?€G?„d?yp?xž?rB?p?kÉ?g@?gQ?`ë?’I?˜ó?‘Ù?˜–?º?˜E?¤?”ò?ˆH??·?†q?y´?x¸?pÁ?l¹?i!?c±?d}?\Ì?”ø?œ-?•&?Z?”z?j?‘"?™À?ˆæ?’À?‚s?ˆé?xû?x[?oA?iC?gŒ?`*?añ?XI?—Š?žÍ?—™?¡[?–?¡?’I??‹?•??‚Õ?ˆX?xÎ?y?nƒ?hÒ?f??^Z?_î?T0?™?¡$?šY?¤N?–e?¢?‘Ü?ž—?Œî?•û?‚Œ?‰?x•?yf?n ?hI?eJ?[¬?_+?T(?˜^?¡,?™=?£s?–í?¢E?“¬?žù?Œ¬?•m?‚B?ˆ¸?xÔ?{k?n?g?eS?[Å?_M?TW?—?Ÿê?—{? Â?–B? C?’æ?œÂ?Š’?“€?‚E?ˆ?xø?zÀ?nµ?i$?fƒ?^t?`„?V?”ü?ž?•?‘ÿ?š ?’?¸?“Y? .?’Ÿ?ŸÎ?‘ ?œ[?‰?˜Ê?Œþ?”9?„]?…¤?†Ñ?Ф?ˆ[?ŽD?‰…?‘?‹¶?”?Œ?“?‹ê?”7?‹+?“¾?‰Þ?‘è?‡Û?Ì?1?~ˆ?í?ƒp?‚M?„p?á?…ñ?ƒ¶?†¬?ƒj?‡\?ƒ3?‡Á?‚º?‡8?Ç?„†??„{?x>?vC?w!?xS?w«?xƒ?y~?xè?yDy—šDyHñDy—šDy—†Dy—£Dy—ˆDy—¼Dy—’Dy—²Dy—MDy—šDxÔ`Dy—©DxްDy—DxŽƒDy—šDxކDy—šDxŽƒDy—šDxʶDy—šDyéDy—šDy—Dy—ŸDy—‡Dy—›Dy—KDy—£DxÍêDy—¬DxŽªDy—DxŽvDy—šDxŽšDy—šDxŽ„Dy—šDxÅ=Dy—šDyDy—šDy—Dy—šDy—„Dy—›Dy—jDy—©DyNÆDy—›Dx2Dy—šDxŽ}Dy—šDxŽŽDy—šDxŽ“Dy—šDy@¦Dy—šDy—pDy—šDy—£Dy—šDy—†Dy—¬Dy—€Dy—¤Dy—Dy—©DyHÔDy—›DxÊ·Dy—šDxÅADy—šDy@ªDy—šDy–£Dy—™Dy—ÁDy—˜Dy—›Dy—™Dy—Dy—¬Dy—¸Dy—ŸDy—ÁDy—»Dy—Dy—DyëDy—™Dy Dy—šDy—pDy—šDy—ÆDy—›Dy—ç@)ˆ?È¢î?çr?ªb¾?ž+«?Z‹2@‰uó?9 ¾=Òî)?§Óu=ýß,?Éþ=·¼Ç?ƒIŒ>É”O@ 9?Û·¯@Ýzc?lÓ> 7I@—.]?3;^@[ï?,7@:éÕ?Ì'?dk?' ˆ@'üC?³’2?ÁNÊ=1D¹<†{?çÙ"@¹­»>Me±@€*ù@"œe=3?/Øå?‹ ?,„,K ?Ä…–@dº>¨o-@¥ss?ÃKî@²‘›@-ñ8@ƒ‡>Õ³B@±¾?,Ö>×N@"·†?Swe?D™=­5^= p2?Àþj@Dï>­Œ?û«=ktÙ@H\?ž=‡=ªaò@(R4?žgŠ?Aâ“@‹Þh=m™2@@¡ú?À’>)èŠ@¦>¯òØ<¨p;¢zv@[2¢>87u?ôÕ@[מ@¬Ê?—Tf>ü³@ÖÕ?¶VÜ?BÌ>;ç¸>kT°?ƒ£?SÍæ@l8?åp¼dmˆ?âw‚¼Ša?äâÍAr¤?æzO³4Ò?êP} †å'?â~Ȭ¶ ?çCÁåOe³?è ÓRK{?éÆf€Gt`?é·(Ô6‚?å2ù’.?çp DýP©?è>ï¦ÔJÕ?êp¯–V?âÎAI=Ýó?ãŠðåö4?ä%½þ_ ?ç\ª$qáÖ?楽²^?èá0Át„?åx&{Ö•ž?âlä¸6C‚?ä”êÁ¨ ?æ'ÅÞ ˜?âÑ9n›?ä[XÞf?æ¢|«Öc?ëHYc‡Wî?åïÅSÕN³?âä©Wz×Î?â5–4›_?â6¤&+:Î?âU²yµl>?㡎ØÁÚ³?ãåóµ§n¼?ã4ûž8?ーúªQ|?ä9Fµ?å·êõ¦>2?åTê‹ð® ?åó]˜ßÒã?æaúS›¾?çƒzda?ç»4ºk?çjÈ#Z¯?ç@eï[Öý?è yS” Ú?èá@Áy‘?é"·^^{ÿ?êƒ;ü§Ì0?êð¬Ò²zn?êðÞ¼]õÜ?ëvÛÊý?ëþ"Èiëx?ëyC¸Âñ€?ë²à)Ý!?@4 4ÿ$•Á¶H€! žÁ¶H€! tžÁ¶H€! äžÁ¶H€SNODØ$˜ð4™ ôs0†@‡! TŸÁ¶H€  ÄŸÁ¶Hˆ?²E?“ pÄ=ÑÞO=Ï…'=ÏD=Í®=ÌÖ0=ÌÊW=ÌÍ=ÌÌü=ÌÍ =ÌÍ=ÌÌŒ=ÌÕ=Ï3:=ÏWï=ÏG?nÙ"?€ˆâ?…iJ?…„?k?~Gf> ¦=ÏŸ¡=ÏE¨=Í•=ÌÉÅ=ÌÍ=ÌÍ =ÌÍ =ÌÍ0=ÌÕ =ÌÕ=ÏB¶=ÏF‹?qŸ¦?‡G?ŠÔ2?ŒÅ?ŒÊ ?=ϤÊ=Íu=ÌÑÆ=ÌÍ=ÌÍ =ÌÍ=ÌÍ4=ÌÔö=Ï3R=ÌèI?n¯Q?‡„?Œ¢Ì?¨ø?’×Õ?’á±?üN?H?‡­¨?DÂ>Šþ=ÏV=Í =ÌÍ=ÌÍ =ÌÍ=ÌÍ=ÌÔu=Ï?P=Ï7•?zÁ¨?ŠÃ_?ŸÀ?”çd?—:®?—Dµ?•@?‘ ß?‹U–?…á©> s=Ï …=Í x=ÌÍ=ÌÍ =ÌÍ=ÌÕƒ=ÌÔÍ=ÌÎ9?höÜ?…Aº?Œ¡T?’¹û?—)º?™Ž¼?™™&?—„«?“Eí?K?†}?yPã>Jñ=Í¿=ÌÍ=ÌÍ =ÌÍ=ÌÕÇ=ÌÔÌ=Ì¿†?k…ø?…O€?Œ¤“?’¿5?—.i?™“h?™ž]?—Š?“K?O(?† Y?{ïk>ð!=ÍŽ=ÌÍ=ÌÍ =ÌÍ=ÌÍ[=ÌÔs=ÎïÖ>™?{¤ð?ŠæÆ?Éì?•á?—gÛ?—r~?•mY?‘Ki?‹{\?…è;>, =ÐÅ@=Í X=ÌÍ=ÌÍ =ÌÍ=ÌÌÿ=ÌÔO=Ï?f=ÌØô?qåþ?‡B!?ŒÝ`?ê˜?“k?“'g?‘>‚?W?‡×w?A€> ¡n=Ïeù=Í µ=ÌÍ=ÌÍ =ÌÍ =ÌÌ÷=ÌÔx=Í8=Îõó>\•?v'?‡^š?‹ý?”?"|?‹aZ?‡Ì?‚È>+Üu=Ð7K=ÍLá=ÌÓ‡=ÌÍ=ÌÌü=ÌÍ =ÌÍ=ÌÌÓ=ÌÔí=Ï?»=Ï=>—?sZÖ?‚Ѝ?…Æm?…Ò†?‚èì?€ý|>,@ý=Ï‘Ø=ÏS5=Í”=ÌÉÇ=ÌÍ=ÌÌû=ÌÍ =ÌÍ%=ÌÌó=ÌÔù=Íá=Ï@ø=Ï*x>±s>©£?m¯¶?{uÒ>*Î(> =Ð!³=ÏQò=ÍSù=Ì×Ê=ÌÊ=ÌÍ=ÌÍ =ÌÍ =ÌÍ=ÌÌú=ÌÍC=ÌÕÜ=Í$=ÍÈ=ÏRV=Ï+ƒ>†ä>jd=Ш=Ïdè=ÍM•=͇=ÌØO=Ìͽ=ÌÊó=ÌÌû=ÌÍ =ÌÍ.=ÌÍm=Ìͱ=ÌÎ=ÌÎP=ÌΚ=ÌÖÎ=ͺ=Í=ÍÆ=ÍË=Íc=Íè=ÌÙÿ=ÌÏ=ÌÎy=ÌÎ/=ÌÌA=ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ=ÌÍ=ÌÍ=ÌÍ=ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÌ×=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÍ=ÌÍ>=ÌÌÉ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌQ=ÌÌÇ=ÌÌÆ=ÌÌÅ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÔö=ÌÕ;=ÌÍb=ÌÍ>=ÌÌÈ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌËå=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÇ=ÌÌÈ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÕ=ÌÝp=Ìݨ=Ï6æ=ÏE«=Í!=ÌÝä=ÌÕ|=ÌÍ=ÌÌÆ=ÌÌÆ=ÌËH=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÇ=ÌÌÈ=ÌÌÅ=ÌÔì=ÌÝ]=Ï7q=ѧ>=ѯí=ÑÉ–=ÑÊ.=Ѳª=ѯß=ÏŒÜ=ÍQ=ÌÕ¢=ÌÍW=ÌÊŽ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÔÀ=Ï6}=ѧ@=ÏR±?nÈ ?€Š)?‚=?‚BQ?€ÅÈ?~;O> ¤M=ÑÄ=ÏL=Í,=ÌÇb=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÔö=Ï3N=ÏCá?k£ü?‚ý?Šœõ?Žã?”=T?”i´?,€?‹-?‚Ö?{‹> HD=ψ°=Ìÿ˜=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÕ9=ÌÖf?kŒ}?†øº?Žy¨?˜=½? ¨?Ÿ£)?Ÿ¯(?j??˜õ€?b7?‡½?|9×>ªÐ=ÍE=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÔù=Ï3‡=Ï.þ?}Ñ.?Žv!?šq¢?¡å¢?§.?ª ý?ªÞ?§›}?¢‰*?›h÷?u¯?‡´Â> ýÂ=Ï]f=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÕP=ÌÎ’?nyÑ?Š|e?˜&?¡Õb?©ÎÛ?¯¤·?²Ï¬?²Ý¨?°?ª…š?¢¹ø?™Nd?‹Ä+?žM>mˆ=Ì̱=ÌÌÆ=ÌÌÆ=ÌÕC=ÌÀ ?zu?Ž{è?œÐÇ?§ ­?¯^?µÏ ?¹5?¹DY?¶PU?°T ?§ÿ ?ž ç?©*?†·> ¤j=Ì̱=ÌÌÆ=ÌÔ÷=Ï3Ä=ÌÏÇ?}¯å?”-?Ÿi?©Ð¿?²£Ý?¹W?¼£(?¼²™?¹¢q?³nï?ªÐ? ~]?•2¼?ˆ!›> «9=Ì̯=ÌÌÅ=ÌÕ9=ÏA=ÌÐ1?}µš?”Â?Ÿos?©×?²ªÙ?¹#®?¼«?¼ºJ?¹ª?³uô?ªÖy? „/?•>•?ˆ%‘> «$=Ì̯=ÌÌÆ=ÌÍ=Íf=ÌÂ-?zÁñ?ŽÐ?ª?§CŠ?¯Í+?¶Ï?¹xî?¹ˆG?¶‘ñ?°‘W?¨6Ÿ?ž=‘?ç?†=I> £i=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÕj=ÌÀe?q·(?ŠÈZ?˜xµ?¢'Ï?ª)ó?°¡?³4Ü?³CÒ?°5?ªáÛ?£ -?™£™?‹ôÔ?z>.G=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÕ:=Îòâ>g{?~ñ&?Žø?šå¹?¢W´?§§?ª‰©?ª–Ñ?¨?¢û?›Ü,?þó?‡çƒ>+ÖI=Е÷=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÍ=͆=ÌÉC?o&³?‡Sƒ?t?˜È ?¡ñ? /±? Å©=Í5;=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÍ =ÌÖu=Îô‡>~?pFË?„>±?‹E¡?@?”Â$?”î?ÏH?‹Ã*?„æ¢?l>)X=Ð3~=Íå=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌË=ÌÌî=ͼ=Ï¿>äz>”â?sŠÞ?‚Â?„¼o?„Å?ƒ+F>5é=Ð =ÍC=ÌÈ\=Ì̹=ÌÌå=ÌÍ=ÌÍf=ÌÎ=ÌÐù=Íœ=Í(Ç=Ï%«>Ò>óæ> â>ô>û°>Ì#=пÏ=ÍZõ=Íç=ÌÒ¿=ÌË=ÌÍ=ÌÌü=ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ=ÌÍ=ÌÍ=ÌÍ=ÌÍ=ÌÍ*=ÌÍ=ÌÍ =ÌÍ =ÌÍ =ÌÌ=ÌÌÇ=ÌÌÆ=ÌÌÅ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÌÅ=ÌÔö=ÌÕ:=ÌÍ=ÌÌñ=ÌÌÊ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌËæ=ÌÌÅ=ÌÍ =ÌÍ´=ÌÌÍ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÝ=ÌÝ¥=ÌÝ«=Ñ’;=ѯ)=Íg(=ÌÝô=ÌÝ¥=ÌÍV=ÌÌÆ=ÌÌÆ=ÌËQ=ÌÌÆ=ÌÌØ=ÌÍ=ÌÌÈ=ÌÌÆ=ÌÌÅ=ÌÝ=Ìݨ=Ñ“C=Ѱ`=Ï@y?i4{?wœW> ]‰=Ѹì=ѯÝ=ÍfÇ=ÌÞG=ÌÌû=ÌÊ€=ÌÌÆ=ÌÌÇ=ÌÌÇ=ÌÌÆ=ÌÌÄ=ÌÜ­=Ñ“9=Ï?Ö?n¢?‚;?…“#?ŠÆÆ?Šî¾?†½?‚xn?~l > ¤=Ñ˧=ÍOŒ=ÌÉ×=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÔß=Ï_?h¨ä?‚?Œc¢?–"¥?šÎ„?o?"—?›'U?–×?p?‚݉?x¨)>d=Í>Ì=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÕ=ÌÉn?h‘@?†Þ¬?“ÞÒ?œÏ ?¤Xð?©Ð²?¬•¬?¬œ0?ª=ã?¥Ø?Ÿ»?”ô?ˆ ?yK°>¨Ä=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÕ8=Ϲ?}ˆ’?“å+?Ÿ7H?©–€?²/ª?¸™¬?¼À?¼(S?¹û?²òD?ª’L? L'?•Ü?ˆ> ð=ÌÌÇ=ÌÌÆ=ÌÕ=ÌÁ:?nT ?ŒK=?œ£r?©‡¿?µ/ò?¿;B?ƪ•?Ê»À?ÊÎG?ÇE7?À#m?¶K¢?ªÆ±?žž?ʘ?•S=ÌÌÇ=ÌÌÆ=ÌÕF=ÌÈ3?}Q?–b?¤/R?²¾?¿#Õ?ÊfÓ?ÒÀf?×TÝ?×iU?Ónc?Ëkz?Àg÷?³im?¥—–?—]œ?ˆË=ÌÌÈ=ÌÌÆ=ÌÕ7=ÏR?‚º?šÃ?©‘±?¸\‡?Æx,?Ò£?Û³?à­—?àÄK?Üo>?Ó¾7?ÇÖ$?¹×}?«,?›ÿù?‹²=ÌÌÈ=ÌÌÌ=ÌÀÒ?h°ñ?Š~Å?œ³ÿ?¬87?»¼³?Êj ?×n?àŠ?å¿?åÖŸ?áO³?Ø=j?ËÖ>?½Mw?­Çö?žhà?¢6=ÌÌÈ=ÌÌÌ=Ì¿ˆ?k>³?Š’?œ·7?¬<Ñ?»Ãá?Ês?×?à”?åËz?åâb?áZÕ?ØG„?ËßX?½U«?­Í?žne?¦í=ÌÌÈ=ÌÌÇ=ÌÖ´>}ë?‚‘ê?š¼"?©ÇŽ?¸žÔ?ÆÆÉ?Òúê?ÜÇ?á]?á(•?ÜÏG?Ô~?È&!?ºÕ?«HS?œ-Ý?‹Öÿ=ÌÌÈ=ÌÌÆ=ÌÕN=ÌÇ¢?}Ïã?–VC?¤…{?²ji?¿˜T?Êèÿ?ÓLQ?×ç”?×ý/?ÓüP?Ëî?Àݳ?³Í“?¥î³?—²å?ˆ?u=ÌÌÇ=ÌÌÅ=ÌÕW=ÌÀ?qÆ?ŒåË? o?ª ?µ¾T?¿Ü“?ÇYP?Ëqu?˃ä?Çôá?ÀÇ?¶Üž?«F6?ž…c?ŽB?–ˆ=ÌÌÆ=ÌÌÆ=ÌÍu=Ì׬>è?~Âè?”eŠ?ŸÂ¬?ª5Ý?²á’?¹Y?¼à³?¼ðª?¹ßƒ?³¦x?«4t? Øù?•‰ê?ˆU’>*Zr=ÌÌÆ=ÌÌÆ=ÌÍ=ÌÕ§=ÌÇó?lOê?‡}R?”`š?’?¥×?ª‘À?­^X?­eÙ?«?¥¼V?žX?•xó?ˆ™æ?}#P>ÊŠ=ÌÌÆ=ÌÌÆ=ÌÌÇ=ÌÌò=ÌØ>F?mc ?„„+?*Š?–À°?›w?íâ?ùh?›Óa?—vÎ?Ž·?…LW?|·–>'¡Í=Íÿ`=ÌÌï=ÌÍ=ÌÍ_=ÌÍ»=ÌÎË=ÌèQ>z>Ï„?sà­?„ð»?ˆ–â?ŠŽÖ?Š—ú?ˆÞj?…X¿?—>)Ô±>^ =Î=ÌÌ=ÌÍ=ÌÌü=ÌÍ =ÌÍ =ÌÍ =ÌÍ =ÌÍ=ÌÍ=ÌÌý=ÌÍ=ÌÍ=ÌÕv=ÌÕ½=ÌÍ\=ÌÍ =ÌÍ==ÌÍ=ÌÍ=ÌÍ=ÌÌ7=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÌÅ=ÌÔé=ÌÝn=ÌÝ©=Ï6†=ÏD´=Í#š=ÌÞ£=ÌÕ‘=ÌÍ=ÌÌÆ=ÌÌÆ=ÌËL=ÌÌÆ=ÌÌÌ=ÌÌÚ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÝ=Ìݨ=Ñ’ü=Ѱ_=Ï@y?i5Ô?w±> ]¿=ѸÉ=ѯå=ÍfÏ=ÌÞG=ÌÌû=ÌÊ‚=ÌÌÆ=ÌÌÈ=ÌÌË=ÌÌÆ=ÌÌÅ=ÌÜ®=Ñ“¼=Ï@ ?q“*?ƒÒ?‡Zm?ú?P°?ˆ?„,¨?€Ë> ”=ÑÌ<=ÍP’=ÌÊ=ÌÌÆ=ÌÌÆ=ÌÌØ=ÌÌÏ=ÌÜÁ=ÏF?n\»?…lä?“Ðb?š´?Ÿd?¢?¢?Ÿã¾?›EV?”×!?†“?~Ï> 3‡=ÍNÀ=ÌÌÆ=ÌÌÆ=ÌÌÈ=ÌÔà=Ï—?pû?Òu?šŠû?¤7Ô?¬G?²D&?µƒ?µ‘>?²À?¬ýŽ?¥?›‹µ?þÄ?žè>Sª=ÌÌÆ=ÌÌÆ=ÌÕ=ÌÉL?ny?È¿?œ»¿?©Gü?µ`?¿#?Æ‘?Êô?ʰ\?Ç+‚?À Ö?¶7*?ªˆ?ú©?‘!í?€[=ÌÌÆ=ÌÌÆ=ÌÕ6=Ͻ?‚?šu?©?¡?¸*?ÆEÇ?Òf·?Ûo3?àhE?àë?Ü,f?ÓË?Ç¢_?¹­_?ª¸¢?›Üƒ?‹¸;=ÌÌá=ÌÌÍ=ÌÀ÷?q½?“²M?¤ Ô?´îg?Æ,ø?Ö—Õ?äã×?ïŸg?õ?õ¬C?ðA?æ1?Ø0ö?Çí?¶®v?¥¶?–ÛO=ÌÌá=ÌÕ=ÌÈ4?€]…?šyE?¬?¾Þã?Ò._?äÂÝ?õþ@¶i@%Ú@5x@8·?ö“±?æ“ù?Ô'.?ÀÚ°?­Øy?–L=ÌÌÈ=ÌÕ?=Ï>?ƒÍ?Ÿ5x”?„qg?Ÿfÿ?²,@?Æ{,?ÛrÒ?ïÈ5@âÂ@»Ü@ b@ ¡f@M@·?ñÈZ?Ý™ ?È¡w?´n?¢™¹=ÌÌ«=ÌÕ%=ÌÇ…?€„¦?šÀq?¬d?¿R}?Òº‰?åh2?õÒÌ@—@”ç@¤þ@£Y?÷Sð?ç<Ò?Ô¶y?ÁQx?®6?áœ=Ì̬=ÌÍ=Ì¿Á?t›#?”1?¤ð?µ~º?ÆÚê?×dª?åË?ð?ö›Í?ö·?ñV?çÔ?ÙU?ÈžÄ?·Bc?¦.$?—FI=ÌÌÅ=ÌÌë=ÌÖ¶>L¬?‚äš?šô?©ßÑ?¸ëW?Ç%û?Óaü?Ü€Ñ?ᆄ?áž?Ý?ò?Ô~^?È…y?ºq7?«\8?œbÈ?Œ==ÌÌÆ=ÌÌÈ=ÌÕ‰=ÌÇ?qú?N:?Zx?ª?µõâ?À!F?Ç¢?˹î?ËÌœ?È?G?Á s?·/?«J ?ž¢µ?‘˜m?‚/=ÌÌÆ=ÌÌÆ=ÌÌò=ÌÖá>ÿ‡?v;?o¿?›/¹?¥ …?­2ÿ?³A?¶Žj?¶}?³¿u?­ëa?¥ú6?œ5¨?‘‰®?ƒßÈ>(¼Ô=ÌÍ(=ÌÍi=ÌÍÀ=ÌÎi=Ìà¦>ð]?t›?ˆa~?“†!?šm?ŸZ(?¢ï?¢!?Ÿ¾ÿ?›H?”jF?‰U?ô’>)=¶=Íy»=ÌÍ=ÌÌü=ÌÍ =ÌÍ =ÌÍ =ÌÍ3=ÌÍ[=ÌÍ–=ÌÕã=ÌÖ4=ÌÖ¹=Ìß±=Ìßü=Ì×=ÌÖà=ÌÖŸ=ÌÍð=ÌÍb=ÌÍQ=ÌË®=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÔë=ÌÝZ=Ï6º=Ѧ«=Ѱ&=ÑËl=ÑÌ=ѳk=ѯì=ψ­=ÍÌ=ÌÕ¥=ÌÌã=ÌÊz=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÜ­=Ñ“H=Ï@?n£¯?‚N?…’æ?ŠÆ?Šîå?†u?‚wØ?~mX> t=ÑË­=ÍO—=ÌÉØ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=Ìܰ=Ï 3Ñ=ÍNÔ=ÌÌÅ=ÌÌÅ=ÌÍ—=ÌÝ&=Ïš?t ¾?‘Ò€?œÚà?¦­?¯"˜?µZ)?¸µ?¸Ãé?µÙý?¯åÁ?§˜?àã?“†?ƒIµ>§¼=ÌÌà=ÌÌÆ=ÌÔú=ÏW?söÈ?“×[?¡j¦?®è?»IÕ?Ñ÷0?é—7@a¦@ Àe@¸0@3t@Hš@b\@ ¶¹@‡ý?ì•?Ô]õ?½Œ$?©›D=ÌÎ*=̲t?}WË?šn?®Ð@?ÅùÀ?ßq?úy‘@ ¨]@Ô\@ K@%¨¿@%Á@!·@÷¬@ ÿ?ýH|?â;y?Èe?²`=ÌÎV=Ì¿v?‚ô?Ÿ)[?´ìŒ?ÍÙ(?é˜À@œ@‚‹@ +:@*áy@0ý @1O@+É*@!s°@G@(:?쓘?Г½?¸BK=ÌÖÚ=Ì«¿?‡f?¡¦;?¸&ï?Òx?ï@Å@Ù @%^-@0ÐË@7]@7|:@1Ǧ@&½9@oW@»“?ò4r?Ôï?»‘“=Ì×$=Ì«&?‡)I?¡ª¨?¸.Y?Ò!?ï s@"@âÐ@%iÃ@0Þ%@7kÂ@7ŠÎ@1Õ¦@&Ê@yå@Ä?ò@Þ?Ôù^?»˜´=Ìή=̽í?‚T?ŸZÌ?µ,ÿ?Î,¢?êß@à@×.@ J@+VA@1y¦@1—@,=Ä@!ÚÙ@X&@n‡?í+?ÐéÇ?¸ƒN=ÌÍŒ=Ì¿f?}©ž?š´æ?¯1Ž?Æv??à f?ûA @ "ñ@eÙ@ ï—@&X’@&pÑ@!¼s@Šû@ {Â?þ€?âÞ@?Èþr?²u=ÌÍn=Êš?qšk?”&ã?¦è?»ä?Ò¹¾?ê‰o@ôM@ jÙ@w’@þ°@š@#y@ cä@'?ìþ?Õ%v?¾*ˆ?ª =ÌÍZ=Ì“¥>áh?‚Ù­?;?¯{£?Ã#»?×cˆ?êïá?ü-Ý@£@J@Zð@.¿?ýÄA?ìÚŠ?Ùtf?Å6â?±`ø? ‹w=ÌÍ=ÌÌÎ=ÌÆˆ?qæ”?’H3?¢¬?²•?Ãj™?Ó`=?á;ä?ëžã?ñ\1?ñv¸?ìy7?â?Ôíw?Ŭ?´ZB?£ž¸?•wî=ÌÌå=ÌÌÆ=ÌÕÞ>ý³?y”Å?”}œ?¢3?¯ÛH?¼¬?Çž ?ϼ(?Ô.W?ÔBÞ?ÐdÑ?È›­?½ç?±<?£Œ¢?•ÖQ?…Ì·=ÌÍ}=ÌÍÍ=ÌÎZ=Ìß¡>+o?z~?‘H“?œvÈ?¦q’?¯>3?µ´’?¹7?¹FL?¶8N?°¾?§d0?†?’eÕ?…¨ç>(Ûë=ÌÍ=ÌÌü=ÌÍ =ÌÍ =ÌÌö=ÌÍ=ÌÕ²=ÌÕÿ=ÌÖ6=Ï6ð=ÏK=Ñ =Ñ®Å=Ï…<=ÏG8=Í.=ÌÖ|=ÌÖ=ÌÍ=ÌË=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÔ¾=Ï66=Ѧ²=ÏTs?nÉ‹?€‹.?‚=d?‚BF?€ÆK?~<$> ¦ =ÑÅ%=Ï|†=Í=ÌÇN=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÔÙ=ÏZ?hªu?‚C?Œbz?–#_?šÎT?K?"!?›( ?–Ö¼?pf?‚Ý+?x¨ù>ep=Í>ä=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÔÙ=Ïk?pûê?Ò&?šŠ?¤7œ?¬Fì?²D×?µ‚‘?µ‘J?²Àf?¬ýÏ?¥À?›‹8?þÓ?žü>T=ÌÌà=ÌÌÆ=ÌÔà=ÏJ?sö‡?“×Ü?¡jÅ?®è0?»‹?Æ[j?Î_.?ÒÂ?ÒÖ?Ïé?ÇUe?¼Â`?°D"?¢¹#?•BŸ?ƒgÂ=ÌÌü=ÌÌÒ=ÏI?pÖ#?“Ìž?£àÚ?´Ñ4?Æî?Öz?äÂ?ï{U?õiî?õ…v?ð])?æÞ?س?ÇÏÍ?¶—ñ?¥~'?—/=ÌÍ;=Êdƒ?hU?´†?¡Kµ?´¼B?ÉÆÀ?ß?ô¢n@°@ Ù)@Ú­@í@ q@ŽI?öµi?á»a?Ì=?¶×?¤ìð=ÌÍ…=̵ƒ?}N*?š^S?®½Ò?Åߊ?ßae?úQ&@ ›@¶û@ )ì@%„Æ@%ë@ ôù@ÙË@ å–?ýþ?â?ÈeW?²¡==ÌÎA=Ï ?ˆÔp?¤Å?»=ƒ?Ö$?ôWQ@ wb@ï@*qC@6Ÿé@=žº@=¿¤@7¨a@+å @¼@ /?÷˜»?Ù2?¾ú=Êf—?n(y?•Ö0?«å€?Åà´?ä@;@p&@~I@*LT@=ß@L%4@Të…@Uß@Mo»@>Õ2@,S@†R@S ?çš·?ÉÅ“=ÊXÚ?z7ú?šo«?±Çë?;Ô?îÈâ@ x­@ɶ@6LQ@Kîö@]¡Æ@hT@h6‹@_)@N&@8Ÿ@">@ ‘@„?‰Š5?¤} ?»Øn?Öï?õ^×@ "Q@ä@+rß@7É@>Þ©@?U@8ÔÞ@,í/@–Ì@ Üü?ø©ø?Ùéž?¿””=ÌÍ=ÌÏÃ?~5?šÞ¾?¯i¿?ƾ?à}h?û¶¤@ i«@¹@!O@&½-@&ÖÅ@"”@àÇ@ ÄÚ?þŠ´?ã=¹?ÉJ?³O"=ÌÌñ=ÊH?l`?:‡?¡òñ?µž¥?Êá?àÛ=?ö?Ô@¡©@ å9@õË@ò@ @‚V?ø\R?ã&?ÍÐ?·¾ò?¥ž^=ÌÌç=Ì4>Aè?vYÜ?”rC?¤®û?µÜ—?ÇRÕ?×ó°?æn°?ñQ†?÷W—?÷s?ò5¦?çÂñ?Ù’k?ɶ?·©ç?¦Yæ?—»7=ÌÍÊ=Ìϲ=Ìßk>ì?yír?“G?¡?®ô#?»â¿?Ç*%?Ïc·?ÓùÉ?Ô U?Ðß?È,X?½#u?°Xä?¢oø?”™Ï?…ßñ=ÌÍ*=ÌÍ=ÌÍ =ÌÍ=Ì̼=ÌÕD=ÌÖ=Ï3Î=ÏLÝ=ѽŠ=Ï\i?iG?w±O> rZ=Ñài=Ï€j=ÏC&=Í0=ÌÖ1=ÌÊW=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÔõ=Ï2y=ÏD»?k¥á?‚}?Šœs?Ž}?”=É?”iÌ?,[?‹-¥?‚Ö?{‹k> Jž=σt=͉=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÕ =ÌÉ’?h޶?†ÞÌ?“ßn?œÎò?¤Xÿ?©Ð?¬•0?¬œ"?ª?u?¥.?Ÿo?”óN?ˆ ø?yN\>»=ÌÌÆ=ÌÌÆ=ÌÕ =ÌÉp?n&?ȧ?œ»V?©H ?µ‰?¿#œ?Æ‘?Ê&?ʯÂ?Ç+„?À €?¶6­?ªˆQ?úÕ?‘!ü?€=ÌÍ=ÌÌÌ=ÌÉ?n?‘¾\?¡[ª?±Ã?Âd.?Ò.D?ßßð?ê!¥?ïÍv?ïèV?êú^?á?Ó¶I?Ä+?³zü?¢ëÿ?”ö„=ÌÍ8=Êd‚?hVÆ?µ?¡Kt?´¼?ÉÆÃ?߀?ô¡@±@ Øë@Úœ@íÀ@ q”@Žh?öµx?á»i?Ëÿö?¶×}?¤ìá=ÌÍ¢=ÎçÀ?ƒ€?œ¨F?±¥h?É­{?ä>%@8t@R@,@%6Š@*éŠ@+´@& ç@aÓ@¼1@°¶?çš?ÌN+?µ¼×=Ê]©?k-Ì?“©ß?© ¸? ;?ßBã@#›@7V@$Ö§@6Sµ@DW[@Lr’@L™6@E‰D@7ÿë@&¼@"x@ïJ?âx­?Æœ=ÊV»?}iÉ?œ…Ü?´ºu?ÑÀ£?ô2Á@ ?@$´†@<¸Ñ@TÂ@g0…@ró@r¹<@hÚL@VDŽ@?6Ö@'#É@UÂ?ø E?ÕÛß=Ì >?†þ?¤?¾¦j?ßC¼@Z³@ÓV@6þ@SÉ@q¤@…c@Œü@!d@†€@t€@VîP@8û@sm@”J?ら=Î÷¦?ŠÑ[?©m²?Åðk?éUl@ a’@$®@CÆ„@f©7@…6¼@”¸h@ž9U@žhÌ@–ó@‡¬@jgw@G9@'¦ƒ@ Ù(?íÚÊ?hЦ?“Ë?«øÉ?Éͪ?îÎg@>Ê@*/@Kšš@qš²@ŒÐ@ñ@¨µ @¨ê¾@Ÿƒþ@Ž’C@uµä@OT·@-[¿@ÚK?ókˆ?k?“á#?«ý?ÉÖ5?îÚÜ@H@*<@K¬ï@qµÌ@Œ¢§@žh@¨Ïe@©<@Ÿš²@ޤœ@uÑ?@Og®@-hÔ@ä1?ó{Ô>g\?‹P?©¤ù?Æ<ï?éÀ½@ ¬ó@%ÿ@D_—@g~–@…ÄÆ@•jE@Ÿö@Ÿ2"@–ÐR@‡”½@kB¡@G×@(A@ '«?îG(=̹†?‡=…?¤e†?¿?ßâ@É@lV@6Õ-@Tèª@sC@†H?@ü @Ž!M@‡hr@vÛ@XJ@9Öc@u@,?ä#Ú=Ê?~7?œíä?µJo?Òƒx?õ9ç@Ó/@%¦¨@=õ@U‘Ä@i ú@tA@tÆQ@j½¤@WÞH@@{Ü@(Ž@?ùL?Ös=ÊNo?nÇn?”80?©¬)?ÂôÈ?à^;@ß @+i@& Ä@7Ï @Fy@NO @NvF@GFl@9‹@'úÄ@Ò@¯š?ãœ>?Çs=Ì‹*>„?„>Ž?G`?²œ?ÊÈ/?å¨;@—@l@{d@&²4@,l@,šò@'\@µ@Û@š!?舙?ÍnŸ?¶T=ÌÌî=ÊŒ?l¬Þ?R”?¢?µÈ?Ë®?áè?ö‹@Ì@ Þ@(C@;ò@ ¯@®A?ø©O?ã^"?ÍU?·êl?¥Á=ÌÎ7=Ì >±?sÏ(?‘5 ?¡O?±Í0?Âïi?Òñ%?á+²?ëîT?ñ×?ñò—?ì˲?âwÇ?ÔÒ?Ĩà?³‘0?¢£w?”[Á=ÌÍ=ÌÌþ=ÌÍ =ÌÍ=ÌÌ‹=ÌÕ"=Ï2á=ÏX =ÏFä?nÚï?€ˆæ?…h³?…„O?Ó?~Gî> ¨=Ïœu=ÏE*=Íï=ÌÉÆ=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÌÅ=ÌÕ:=Ìׯ?k‰Ä?†ø¦?Žz!?˜=x? ¤?Ÿ£=?Ÿ¯R?ià?˜õ?aÕ?‡¾?|;®> =Íx=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÕ6=Ï*?}ˆì?“äÒ?Ÿ6±?©–Æ?²/p?¸™N?¼—?¼'è?¹?²òn?ª“D? Lu?•\?ˆw> ð®=ÌÌÆ=ÌÌÅ=ÌÕ5=Ï$?‚??šu‰?©?Y?¸)ú?ÆE¨?Òfû?ÛoM?àhQ?à~ù?Ü-D?Ó€?Ç¢Š?¹­H?ª¹ ?›Ü¯?‹·Þ=ÌÍI=ÌÕg=Ïü?‚Ž?œ¸{?®ÐH?ÂMX?Ö]?é¹?úÊg@Ý@x@‰ë@f=?üYÂ?ëŸ ?Øj?Ä[®?°¯? =Ì͆=Ì·v?}M¬?š^‰?®¼„?Åß„?ßb?úQž@ :@·Ú@ *@%„†@%žV@ ôí@Ú^@ åy?ý?â˜?Èeš?²¡u=Ê]¢?k/u?“ª²?© Ê?‘?ßB£@#«@7°@$Ö£@6T7@DV[@Lqó@L—ú@E‰²@7ÿç@&¼Û@"m@ïˆ?âxý?Æœ{=ÊRÒ?ƒ‚C?Ÿ ?·àZ?Öz?úg@$ß@*=@Cà¤@]Z@r²@~\X@~çx@sÈÛ@_˜@Fˆe@,²Æ@\Ï?þÕ?Û )=Îå2?ŠÁÓ?©Xˆ?ÅÖ°?é1à@ Hª@$œE@C¨…@fœ£@….C@”©!@žâ@ž‰±@•úý@†ðš@jN @G'«@'që@ ¾J?íù¢?n0?—Ý?±°Ã?ÑÂÌ?úÒ@Fž@5ã@\—@…@Y™@³vŸ@Ály@Â<@µb€@ŸÓ @‡y†@aX@9\ý@/?ÿ&û?z9f?œ˜š?·ü¢?Úž\@\d@‹?@C¢Ð@q"ï@”:Î@³$C@Ð]Þ@ãr@äP<@Òùœ@¶X@—MŠ@vxi@G«”@"Ò+@ ?Ô•?Ÿ.?»Y?ßii@×@$´‹@Ks>@}‹@`Œ@À¬í@âëæ@ù³1@ú¿@æÙ@Ädõ@ Ø^@yÃ@Oж@(2$@ ³X?Û`?Ÿ4@?»`í?ßtX@Þ­@$À€@K„Ô@}HÁ@–'@ÀþS@ãX@ú9â@ûJª@æt°@ĹN@¡§@÷@Oäv@(?E@ ºÖ?zÇÔ?œÇà?¸@?Úý @ .@ðÔ@D:ß@r†@”á@´±@ѧî@äúÑ@åâD@ÔKÆ@·Ut@—úf@w]ç@HIÔ@#;”@c?qdú?˜=?²›?ÒMs?úÌ‘@×…@6¶r@]ÄB@…ár@ž’á@µ’@ÃFM@Ãí÷@·N@¡ç@ˆ`÷@bLÊ@:8½@Åä?ÿò¢>>Z?‹l?©Õ?Æ„?ê"Ë@ ò@%Ï@Dõ”@hpÃ@†g@–1@Ÿ×›@ Gj@—ˆœ@ˆ2˜@l;r@H‘b@(k @ nƒ?îëž=Ê‹C?ƒÔ?Ÿ”­?¸ Y?× i?ûgs@@+\0@EŸ>@_M¬@t±ë@€©{@€ñž@vy @aÌ@H]@.Â@WË?ÿx¤?܆=ÊL0?olõ?”3Ò?©ÈÙ?Ã$?àœÀ@†@Qý@&@@8@FY@N R@NÈ@@G’C@9Â'@(-@D@Ùh?ãÝ?Ç©Ï=ÌŠå>t+?Sà?› ?¯ª¬?Ç ì?àû¾?üR£@ Éd@*}@!Ìü@'DW@'\Ó@"›¶@S8@ %À?ÿ*‰?ã¾7?ɯÚ?³Ÿ&=ÌÍp=̬>Ua?…'œ?œ`ó?®Õ8?ÂÖÖ?×~µ?ë%J?üÐL@&@æ@õý@±ˆ?þpG?íÿ?Ù˜´?Äôz?°Í¸?Ÿži=ÌÍ=ÌÍ =ÌÍ =ÌÍ0=ÌÕ =ÌÕ‹=ÏB—=ÏG?qŸX?‡D?ŠÔ/?ŒÅ`?ŒÊ??‹Ñ?‡â?!ž> ;”=Ï õ=ͼ=ÌÑÈ=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÔù=Ï2µ=Ï0r?}Ðñ?Žv-?šqŠ?¡å}?§.&?ª ì?ªN?§›H?¢‰0?›h%?uî?‡µE> ø”=ÏXÚ=ÌÌÇ=ÌÌÅ=ÌÕ =ÌÂ?nQx?ŒL?œ¤q?©†Ê?µ0?¿; ?ƪ$?Ê»g?ÊÎú?ÇE°?À#“?¶JÌ?ªÇ\?ž¾?Ë?˜o=ÌÌà=ÌÌè=ÌÁ?qø?“²m?¤?´î¤?Æ,ä?Ö—Á?äãÐ?ïŸc?õ?õ¬a?ð‚ ?æ15?Ø0£?Çí­?¶®x?¥¶F?–Úö=ÌÎ=Êe?n)2?“§¡?¦qH?»Jc?Ñö¾?é—§@aù@ Àr@¹%@3@H@bg@ ¶ž@‡B?ìd?Ô]Ì?½‹¥?©š¿=ÌÎA=Ï?ˆÓþ?¤ ?»=]?Ö$p?ôW@ w{@œ@*pü@6Ÿö@=žQ@=¾ú@7¨Š@+åê@»Á@ /"?÷™?Ù?¾ú =ÊWa?}j?œ„?´ºG?ÑÀk?ô2¯@ (@$µº@<¹4@TÄ@g/\@rƒ4@r·ê@hÙ‡@VDO@?6ü@'$@Uö?ø ï?Õ܃=ÎäŽ?ŠÁ£?©Y°?Å×?é2@@ H-@$œ…@C§N@fœ?@….k@”¨g@žå@žŠ+@•ùý@†ñ @jNî@G'Ô@'r@ ¾©?íù’?q„?šø?ïŒ@ms@6UÒ@gÑ@”›u@ÁÌ@ù5A´A0©éA1½dA˜é@ÿW=@ÆYU@˜ºø@l¯@:œ@°d?ƒ´k?¡ß’?¿?äÆ@@ Ã…@*šG@T…K@…­o@¨–&@Ѽá@úëA ˆVA 1@þ·½@Ö'i@¬Ò@ˆ÷v@YKÂ@.Y@ Ì´?ub?ššq?µNC?ÖÕT@¢u@Š÷@=§²@h@„ @©j©@ÃN@Óïo@Ô¶8@Å—Å@¬EÛ@N@mX@Aq @¨³@]Ð>æ³?‹rö?©õ9?ƶ?êg®@ !ì@%Ò@E`ñ@i Å@†Ì@@–°j@ g¿@ ×€@˜ }@ˆ™÷@lÐQ@Hú>@(±¹@ Ÿä?ï7¥=Ê€L?€Êñ?E?µ™5?ÒñK?õÏá@9?@&l@>“r@VY¦@iû)@u•K@uÎ@k®h@Xª?@AS@(–›@v4?ùµ?×/=̇Ž>‚Ú?‰¸J?¤Û?¼] ?ת?ö?·@ ± @—’@,I÷@8¿ƒ@?ç]@@N@9ÎQ@-ÇD@N@ p5?ù?Úœh?Àm=ÌÍý=ʧü?s‰?“Z…?¦3[?»Ÿà?Ò¸_?ë(@jÜ@ @Aì@áü@ý=@ö@ A@˜?í€?Õ-?½íÖ?©eõ=ÌÍ=ÌÍ =ÌÍ=ÌÍ4=ÌÔó=Ï2ô=ÌèK?n­?‡‘?Œ£Q?¨×?’×»?’á¬?ü2??‡® ?Eò>€#=ÏUD=Í ®=ÌÌÇ=ÌÌÆ=ÌÌÆ=ÌÕN=ÌÏÁ?nv¬?Š}‘?˜'*?¡Õv?©Îa?¯£å?²Ïj?²Þ´?°Ã?ª…s?¢º&?™MÅ?‹Äá?¡Ù>c0=ÌÌÇ=ÌÌÆ=ÌÕM=ÌÊ(?}€m?–±?¤/?²8?¿$€?Êf?Ò¿G?×Tö?×j.?Ón(?Ëk“?ÀhX?³iW?¥—¶?—\»?ˆš=ÌÌà=ÌÕ=ÌÊ?€]?šyC?¬ï?¾Þç?Ò.c?äÂË?õ@¶@&@5ž@8¤?ö“?æ“p?Ô&Ó?ÀÛ??­Ø0?–{=ÌÎ1=̳°?}W•?šn?®Ð8?Åù*?ß^?úyE@ ¨@ÔV@ K‰@%¨r@%ÁP@!³@÷¢@ þÖ?ýHý?â;ú?È~ï?²=Êfý?n*x?•×?«å??Åà¬?ä?Ù@p@~N@*L‹@= -@L$û@Tì@Uþ@Moª@>ÕŸ@,RÑ@†+@Sg?çšg?ÉÅÈ=Ì ä?†þ*?¤Ø?¾¦?ßC®@Zi@Ó¦@6@SÈð@qr@…co@Œûû@ g@†€$@t‚ @Víó@8û<@s @”?ã‚æ?n1°?—Ý?±±?ѯ?úå@F«@5ã@\“¦@…@Xâ@³t@Ál7@ÂÞ@µa¾@ŸÐk@‡yÇ@aÈ@9]i@/ ?ÿ&«?ƒ„¥?¡•?¾¦Ð?ä ©@ J#@)á×@Sgv@„Ï`@§3@ÏšY@÷ÝàA ¦ÔA Ká@û6@Óí@«k@ˆ ­@XÉ@-˜ð@ M¹?ˆß?©n­?ÉÀM?ô5j@HÄ@Jè?‡ÅW?¤À›?¿¡b?àž @J`@ ˜@7º…@Vº@t›m@‡?ª@@5ø@ˆcL@w â@YXÕ@:Âí@ÊÆ@Œ?ää˜=ÊT©?r”i?–|[?¬Ì{?Ç"B?åêÚ@Š®@ð½@,$ø@?Nã@NÈt@Wʯ@Wô=@PŽ@A$­@.7ó@ð@vc?éS‡?Ë Í=Ì…[>bŠ?®"?š3?®ç?ÆÆý?àÍy?üô@ ûË@w7@"*g@'¯‰@'Ñ7@#ì@¢t@ Z¨?ÿ_ñ?ã•Ý?É[]?²:K=ÌÍ=ÌÍ =ÌÍ=ÌÍ=ÌÔn=Ï?==Ï8W?zÀ¥?ŠÃ~?ŸÂ?”çI?—9ò?—Dæ?•@"?‘ é?‹Uh?…á,> ©=ÏœÅ=ÍÂ=Ì̱=ÌÌÆ=ÌÌÅ=ÌÕF=Ì ?zŽF?Ž{´?œÑŒ?§ ’?¯©?µÏý?¹5?¹DP?¶PV?°T ?§ÿç?ž º?©c?†1> Á=ÌÌÈ=ÌÌÆ=ÌÕ7=Ï¡?‚©?šÿ?©‘T?¸\i?Æx?Ò£¢?Û²Y?à­Õ?àÄÍ?Üo¬?Ó¾E?ÇÖl?¹×˜?«¦?›ÿV?‹±Õ=ÌÌÇ=ÌÕ?=Ïœ?ƒÍœ?Ÿ5M?±îê?Æ.€?ÛS?ïVÕ@¡&@s!@ B_@ T>@@tð?ñU%?Ý8÷?ÈR?³ß?¢gÑ=ÌÎ]=ÌÁ‡?‚O?Ÿ)?´ìÝ?ÍÙq?阼@›o@î@ +b@*áÊ@0ý…@1è@+Ɉ@!ss@4@(°?ì“I?Гk?¸BS=ÊY?z9¾?šoª?±Çõ?;¹?îÈ@@ x‘@Ê+@6LŒ@Kï†@]¡[@hE@h7¥@_)X@N?@8Ÿ@"…@ ‘B?òu#?Ñ„Í=Îù?ŠÐã?©nK?ÅðT?éUÚ@ aA@$­ê@CÇ2@f©ê@…5·@”¸Ù@ž9Ù@žh @–«@‡@jg@G8Ä@'¦%@ Ù‘?íÚE?z9S?œ—ª?·ý?Úži@\@‹³@C¢o@q#E@”:@³#ý@Ð`=@ão­@äRA@ÒøÄ@¶Z3@—Li@vw@G«f@"Òj@ì?‡o?¦Á…?Åò?î¦c@é@5Ó@f59@“ö«@À…@÷GxAL1A.ÔA/á A"¨@ýjØ@Å2!@˜ l@kÑ0@:$@O¾?Œ½;?¯)(?Ñð@@'I@|@K& @„½U@²sü@ö°WA-]AmLÉA’›A”¡At83A3@ÿHŠ@¸e@ˆy@PpÆ@#aý?Ød?µK.?ÚÐ)@ÕW@* °@\uŽ@“ê@Ï AcäAlC‹A»¨OBZ°B !¨AÄnqAw—ÁAø×@×Y@˜‹´@b­²@.OÖ?“]?¸”ì?ßH@ @/êÒ@foG@ z@á^ÍA- ÅAôÏBâÿBgµõBoùB ßéA™‡A5@ê½Ô@¢>/@m91@4|Ý?“ G?¸šß?ߨ†@ ˆv@/øZ@fˆE@ q@áÍA-4A‘¬ZB;MBkÆ÷BtXèBjZAšRŸA6#@ë5²@¢VÆ@mQ³@4Œg?‘Þ?µ‹Ö?Û.­@Þ@*}¤@]7;@”ši@ÐSAÉŸAo£A¿ì€B ȉB ÆØAÉ-OA{IqA |Â@Øb$@™FH@c{Ø@.ÉB?Œý?¯Š ?Ò{ @@ 0E@L.È@… k@´@ù·A0²AráNA–ÚUA˜k¼Az"A6KNABÖ@º‡@‰h¹@Q‡@$õ?‡Y ?§7?Æžû?@Öâ@6ø©@hZ@•yÍ@‹@û„øAšA3“A4*HAƒÆAð@Çå0@™£²@mÁè@;Fî@c?~P?:ì?¸¸ö?Û­Í@ ì@ ­c@EW:@sÄi@–@p@¶ô@ÔSŠ@èÅ@éP@ס@¹fú@™gž@y:!@Ir˜@$%@ìÝ>?‹™Î?ª+F?Æÿ¯?êÐf@ k.@&'’@EÆË@iq @‡ì@—F@ ÒX@¡R@˜s÷@ˆêÃ@mEï@IJø@),P@ ìÄ?ïdÔ=Ê€Ÿ?~‘@?›{?²¾ó?ÏÈ?ð›B@ ²‡@!ký@8i‘@N’É@`ºã@khO@k›@bLØ@P¸X@:ÊÉ@#ÀÃ@ Ôé?ôV…?Òç¿=̃>rG?…1>?Ÿ ²?µ@÷?ÎÛè?ëaˆ@ãw@ 1@" !@,ÿ±@3DÃ@3d-@-ìã@#Të@…@yj?îpû?Ѧ»?¸¦é=ÌÍ=ÌÍ =ÌÍ=ÌÕƒ=ÌÔÊ=ÌÏ‚?hôQ?…BT?Œ¡·?’ºŽ?—)A?™Ž)?™™u?—…?“EØ?J½?†¿?ySÏ>@7=Í=Ì̱=ÌÌÆ=ÌÔ÷=Ï3~=ÌÐo?}¯Ü?”ª?Ÿh·?©Ð¹?²¤{?¹|?¼¢Ü?¼²}?¹¢‘?³nü?ªÐÖ? ~e?•2?ˆ!¸> «&=ÌÌÈ=ÌÌÌ=ÌÁs?h®.?й?œµI?¬8?»»­?ÊiË?×n?à‰¹?å¿O?åÖŸ?áOá?Ø<™?ËÕÆ?½N?­È?žhu?¡Ò=ÌÌÈ=Êdð?h˜°?ºÈ?¡›å?µª?ÊÇ?ßäù?õœ@ö€@ &È@,²@?^@ ¿@Õ`?÷/ê?â"Í?ÌSi?·_?¤øE=ÌÖæ=̬ö?‡e?¡¥Ù?¸&Œ?Òp?ï"@%@ØÚ@%^@0ÐX@7\¹@7|È@1Ç¿@&¼à@oB@»T?ò3,?Ôð?»‘³=Ì´‰?}_4?œ¢?´ç—?Ñüh?ôƒ@WR@$÷<@=¢@Tqn@g¶Á@sÅ@sO‰@ib”@V¸$@?Ì@'h)@ŽÈ?ø]²?ÕÓB?hŒ#?“Ì?«ù?ÉÍÙ?îÏt@>…@*.É@Kš @qšh@ŒÞ@ñÜ@¨¶$@¨êÃ@Ÿ‚š@Ž’t@u¶2@OTf@-[m@Û ?ók/?ÓÓ?Ÿ.–?»Y³?ßif@ÖŠ@$´Ê@Kr6@}§@`á@À­M@âè@ù³@úÀˆ@æœ@Ädß@ ×^@y³@OÐú@(3@ ³V?ˆêm?©ƒ$?ÉÞÔ?ô`Î@i—@<Žƒ@që@@Ï ÛAå¦A.4åAJÔAKõ÷A1åäA ¬À@Õ°þ@¡ª@wCf@A Œ@ÉK?ŽÊ¬?²9B?Ö[ƒ@v@$¶µ@S–£@Œ I@¿æåA‰ÓAH(öA‘º›A¾:4AÀµìA–þ_APP­A ÎÞ@ƽŸ@1´@YT%@(É?“M?¸“Ò?ßb@ @/ê¿@fow@ <@á\PA-EAô{BâœBg·hBoûµB ÞA™ìA5@ê½Á@¢>¹@m8w@4|¡?•G?»úÁ?ä¡™@_p@66L@q]þ@§\¿@÷8zAH1A»{Bc‚QCo³sC’qPB~†2AÉ´RAS‹ÞA;·@­?+@xÉT@;U?•Ló?¼‹?ä­„@h@6D§@qw8@§u @÷¿AHà›A¼®ñBgd¸CƒOõC©Í¨B³èAËåATM¢A…~@­[ó@xäš@;/#?“3Æ?¸×?àR@ ËÑ@0e´@gCÓ@Ð@âá$A.ÜèA“€’B :ÎBtúUB~4'B×ÒAœ]øA7‡Ý@ìa¿@£.@nº@4ý8??²ž?Öív@ä6@%cæ@Tµm@P@ÁºúA d´AL&çA•ï;AÅE0AÇñ>A›ƒAT¦½AÎo@È®+@‘‘?\?.?µïH?Ófv?önD@¢Ö@&³@?Vº@WLC@ká@vÍÛ@w@lÑd@Y¢±@Aç4@)0¼@ä?úWñ?×D=Ìœ»> ?‡û?¡¡?¸¦¢?ÓKÔ?ñ¡@„H@…D@'ck@3'@9Ð@9ð¬@4¹@(Ãþ@æ@ .«?ôK?Ö4@?¼=ÌÍ =ÌÍ =ÌÍ=ÌÕÇ=ÌÔÈ=ÌÁh?kƒ‘?…O¡?Œ¥?’¾ü?—.Z?™“€?™ži?—Š8?“Jy?Nú?† ‚?{ð„>æ•=Íé=Ì̯=ÌÌÆ=ÌÕ9=ÏA¡=ÌÐØ?}µ˜?”ì?Ÿnš?©Öü?²«?¹$?¼ª#?¼ºF?¹ª2?³v?ªÖ÷? ƒj?•>?ˆ%«> «=ÌÌÇ=ÌÌÌ=ÌÁ›?k<?Š“?œ·ø?¬<½?»ÃN?ÊrÔ?× _?à”Ô?åÊ·?åã,?áZÎ?ØF²?Ëß8?½U´?­Í%?žmÕ?¦I=ÌÌÈ=ÊW?k& ?×b?¡ŸÜ?µ?Ê!n?ßï¹?õ&­@þ‡@ /_@6@Hˆ@ Çô@Ýj?÷=?â,þ?Ì\¸?·!æ?¤þ%=Ì×0=̬?‡(¸?¡«'?¸.`?Ò!?ï Š@!¨@â®@%iË@0Þ@7kd@7Šß@1Õù@&ÉÑ@yo@ÃÀ?òA5?Ôù?»˜•=ÌÂV?}dì?œ¦F?´îB?Ò1?ô%@`j@%¸@=Ü@T†X@gÐ3@s2n@sj@i{.@VÍP@?¡@'tp@—÷?øjâ?ÕÜj?k?“àÝ?«ý\?ÉÖT?îÛ«@Gp@*;Ô@K¬€@q´T@Œ¢¥@žè@¨Ï7@©=@Ÿ™Œ@ޤG@uЊ@OgŠ@-h¿@ä8?ó|?Û˜?Ÿ3à?»`¿?ßt}@ß @$À@K„n@}H’@–@Àþ@ãYŽ@ú9Î@ûH±@æsÙ@Ĺ9@¡@Ÿ@Oä®@(?m@ »9?ˆî ?©‰v?Éç?ôm‚@s@<Ç@q5–@J<@Ð A 6™A.¼mAKC AL¬A2oA >@ÖD@¡äA@w`1@A0´@ß`?ŽÏP?²@?ÖeY@}™@$ÂÌ@Sª©@Œ†@À6/AÛÍAHÖªA’w A¿v_AÁü¡A—Æ#AQžA'@@Çý@DS@YhN@(ã«?“ 0?¸›È?ߨ¬@ ˆu@/øÕ@f‡’@ þ@áÍÃA-•A‘¬ŸB:çBkÈ‹BtW¶BjAšRA6@ë5ù@¢W @mPÉ@4?•L™?¼•?ä­T@hO@6D¹@qvÓ@§uÚ@÷¼xAHÝõA¼°«BgdaCƒxuC©uDB¶'AË`ATPCA… @­[œ@xä<@;0?•Qü?¼ -?丫@qC@6T3@q‘J@§Ži@ø@žAIAA½ç®BkqlC’0CܧÉB„:ÿAÌjAU/AÍú@­x0@y@@;Aµ?“9[?¸Ýì?à @ Ôi@0sØ@g[Z@æý@ãR=A/`A”A¹B «By‚3B„“B{ A4~A8—@ìÞb@£+b@n2@5®?Ã?²¤ê?Öö³@ëw@%pz@TÊw@e@ HA ·³ALÞ}A–µAÆšÏAÉOÃAœT%AUjhA+@É-@‘PL@Z–ë@)‹t?‰A?ª?Ê›[?õv@<ñ@=Ûj@s<(@Ÿ/@Ó)A ÓúA2ò€APôbARq²A6×ËAÅ7@ÙBÞ@£²û@y‚[@B}@£b?‚1Ê?Ÿ½W?¼(û?àÞ@­m@%ö @M]@€N@ŸÜÖ@Äct@è*@ÿÛ¶A|J@ëB@È?·@£oÄ@ƒ)!@QÒx@)I@ ‘þ?o8?”j/?¬Áƒ?Êñ›?ðfÞ@`ƒ@+ι@MÖj@tÛ@޳ @  À@«À¿@«ù¬@¢>á@é@xùä@Q¦¥@/ @p?õz>³K?`:?††?µö½?ÓoÝ?özý@¬@&¾¡@?fh@W`Ÿ@k1¯@vèã@w ¨@léÖ@Y¶Ã@A÷v@)=ò@î¹?úe*?×M’=Ìœ§>Y?‡7?¡¦ß?¸­°?ÓUn?ñ'ö@Œ,@•j@'w$@3&[@9ßw@:É@4$³@(Øô@.Ò@ 7?ôYi?Ö>”?¼T=ÌÍ =ÌÍ =ÌÍ=ÌÍ[=ÌÔk=Îîš>—ï?{¦W?Šæ|?Éã?•ß?—hJ?—rÊ?•l¼?‘Kz?‹{?…è->,=г=ͨ=Ì̯=ÌÌÆ=ÌÍ=Íé=ÌÂ?z¿Ë?ŽÐƒ?‡?§C?¯Ìñ?¶?¹x…?¹ˆ.?¶‘Ç?°‘p?¨7?ž<Ø?™?†=µ> =ÌÌÇ=ÌÌÆ=ÌÐÊ>}à?‚“}?š¼#?©Çã?¸Ÿs?ÆÅ±?ÒúZ?Ü?áM?á(>?ÜÐ?Ô«?È$Õ?ºâ?«H‡?œ-?‹×=ÌÌÇ=ÌŽá>zU?„qÑ?Ÿf”?²,?Æ|"?Ûs€?ïÇå@â¿@¼X@ H@  ×@MQ@¶ö?ñÇ*?Ý™‹?È¡?´÷?¢š=Ìζ=ÌÀ ?‚Si?ŸZÚ?µ,‘?Î,(?êØ@à'@×"@ ˆ@+U½@1y}@1–®@,>B@!Ûœ@W@ná?í-?Ðè¾?¸ƒ*=Ê›w?zl?šší?²B?Îí?ï8{@ Å@ .é@6Ð2@L•®@^f@hÛ @i¬@_ï©@N±§@9&m@"|Ü@ ß{?òèÃ?ÑØs>h÷?‹Q?©¤g?Æ<ü?éÀ§@ ¬ð@%Ó@D_Ú@g}™@…ÄÙ@•jþ@ŸÐ@Ÿ2û@–ÐÙ@‡•?@kB@GÖò@(è@ 'Ø?îGâ?zÇž?œÇ:?¸??Úüã@ U@ð@D;@rÏ@”á£@´ç@Ѩß@äû@åà9@ÔLÑ@·Uî@—û"@w_¾@HIù@#;Ô@cª?‡7á?¦÷n?Æ? ?ïÝ@mM@6Uy@g b@”œ½@Á@ù„A¶MA0©ÝA1»{Aš[@ÿUï@ÆXŒ@˜»@l°ˆ@:œ¯@°í?ŒåÖ?¯eZ?ÒFŠ@hš@òT@KÉ@…J›@³gœ@ø€A.Ó”Ap¶|A•6¶A–¿ÖAwÓfA5‹Aœ¡@¹f@‰ „@Q~@#×?‘?µŒ\?Û.`@°@*}Ç@]7T@”š(@ÐRäAÉlAo¢åA¿îÖB ÈgB Ä´AÉ,A{HæA ~@Ø`û@™E(@czÇ@.ÉÁ?“3°?¸ÖŒ?àF@ Ëp@0e2@gDÌ@Ðu@âàÈA.Û‘A“ÝB = Btö;B~5KBÓ²Aœ]2A7‰@ì`–@£~@nœ@4ýA?“8ÿ?¸Ýø?à %@ Ôs@0s‹@g[™@ç@ãRiA/bA”@yB «„ByþBƒ‹BzjA3*A8œ@ìÜ&@£*"@n2Q@5e?‘.î?µÌg?Û%@e$@*ð±@]úð@•L@Ñ›™A5cAsàAÄvÒB’B¶AÎFAÎA"Ð@ÙÆ@š@dI@/Cj?%¦?¯Æç?ÒÒà@Ñ@ •™@LÔ]@†.ñ@´ÿ\@û’nA1àYAvqCA™:A›=ÂA}ê½A8KgA@d@»_@‰ÿ@@R6@$x3?‡|a?§o†?Æí^?ðŸ@+«@7~l@häî@–$@ñ¶@ýf¸AÑA4ûA6eA! ßAîÈ@Éh@šXA@n¢ß@;Ó@ml?~x?i¦?¸üÉ?Ü ÷@e~@!š@Eñ@t§¿@–ëš@·¯@Õ©à@麟@ê¦Ë@ØgÂ@ºj @š£@z'û@J@$k@3>$ ?Œt?ªb¢?ÇM?ë;ß@ ¸!@&”u@Faž@jKš@‡¢ý@—½@¡¢q@¡Ôµ@™.R@‰€ @n&\@IëØ@)É@lU?…TÐ?Ÿ>?µƒœ?Ï2c?ëÑ@(i@g&@"wÛ@-v|@3ÄÁ@3ãø@.e¨@#Æ)@ëâ@Á‰?îâ½?ÑþÞ?¸êE=ÌÍ =ÌÍ=ÌÍ=ÌÍ=ÌÔI=Ï?ì=ÌÙ“?qäI?‡B?ŒÝD?ê”?“Â?“'Œ?‘>t?V¼?‡×½?AÃ> •À=ÏeC=Í ê=ÌÍ2=ÌÍ€=ÌÍ(=ÌÕ…=Ì ?q´ ?ŠÈu?˜y\?¢&}?ª)ž?°D?³4©?³C?°?ªáá?£ ê?™£§?‹ôç?z™>%o=ÌÌË=ÌÌË=ÌÕQ=Ìɯ?}Ïö?–V??¤…D?²jˆ?¿—t?Êèì?ÓMZ?×çˆ?×üÇ?Óü`?ËîŠ?ÀÝw?³Íb?¥î“?—²o?ˆ?W=ÌÌ©=ÌÕ,=ÌÉ?€„v?šÀq?¬cö?¿R¾?ÒºÎ?åhX?õÓc@¹@”‘@¥¶@£+?÷Ta?ç<„?Ô¶?ÁQ½?®5É?áo=ÌÍŽ=ÌÁ{?}©?šµE?¯1w?ÆuS?à ¦?ûAÃ@ "g@e @ ïG@&W@&qé@!¼?@‹d@ {Ä?þ­?âÞŸ?Èÿ ?²uX=ÊXh?qe¬?–1ž?¬@¨?Æ]ž?äå´@Þ@ë@+D@=íá@M/ƒ@VÇ@V8Ø@N~5@?¾@-¿@@ÄD?èF?ÊBV=Ì»“?‡=9?¤dž?¿6?ßã0@Ȳ@kô@6Õ_@Tè@sÉ@†Hw@üq@Ž!v@‡iÉ@v?@X@9Õñ@]@²?ä$!?qfS?˜;ö?²ç?ÒMe?úÌÜ@×6@6¶I@]ì@…áT@ž’&@µ C@ÃHÒ@ÃìÅ@·@@¡e@ˆa&@bP‹@:8¬@Å?ÿòß?ƒ´]?¡ßû?¿À?äÆ^@ ÃG@*šA@T…¹@…¬»@¨•d@Ѽœ@úëãA ‰LA 1#@þµÄ@Ö'@¬¶@ˆ÷…@YKÓ@.Y@ Ìž?‰I?©È°?ÊAy?ôó}@؆@==@r;Z@ž/@ÑOÒA ^ŒA0œ²AMÔNAOF$A4g‘A:m@×{f@¢±6@xr·@A×’@YY?Œýx?¯Š'?Ò{;@j@ 0[@L,û@… E@´õ@ùµÄA0…Arß«A–Ú%A˜lAz"”A6L^AB@º:@‰hž@Q†è@$Ý??²´?ÖìE@ä@%d@Tµ@K@Á¸³A d4AL)¯A•î‹AÅD*AÇò4A›€žAT£¥AÑ@ȯ2@‘<’@Z‚@)o´?(?²¥º?Öö@ën@%pî@TÈÝ@@@ –A ·ÁALÞ¸A–´}AÆš—AÉP/AœT AUjHA*Î@É@‘Q@Z—Å@)‹ˆ?&F?¯ÆÉ?ÒÒ¡@й@ –p@LÓž@†/ @´þ†@û’7A1ßdAvr£A™žÈA›;óA}ìA8IRAAÞ@»°@‰ÿ¦@R6c@$x?‰Tº?ª$[?ÊÄ?õ±Â@i§@>"Ù@s³o@Ÿ@Õ@Ó~JA FA3·ARASYA7¥õA>Ÿ@ÙÇ@£þ’@yþÇ@BÈ>@ð?ƒù½?¢OA?¿»Ò?å®×@ n¹@+@Vn@†èü@ªä@ÔÈ¡@ÿI»A;ÕAëhA›$@ÙT¯@® \@ŠCm@Zð!@/gt@|ž?uN?˜Ö?²Âc?ÓH?ü2©@Úá@83Ÿ@`v@‡‚Ë@ ãÁ@¸e@Æ¿¨@Çk:@º¡@£yª@ŠJ@dœò@;â@ÒÐ@²i>G%?ˆ®?¥?Àß?á>]@¹õ@¼S@8’A@WFT@vÉ@ˆ*ö@à@=@‰Ré@y,X@Z†¯@;¢š@kû@ÿU?删=ÊFß?u·®?–Ø?­'í?ÇŸÏ?æ“z@ú€@ƒÍ@,âæ@@8@OÚM@Xô3@Yh@Q1\@BÛ@.øð@š@éÆ?éÿ?ËŒI=Ì‚U>#?ÝÎ?š|·?¯K¹?ÇG!?ás ?ýNÐ@ z@ h@"Í @(\#@(~}@#§ @:@ Ûc@R?ä>ø?Éß?²  =ÌÍ =ÌÍ =ÌÍ =ÌÌù=ÌÔv=ÍÉ=Îô_>[ì?vê?‡^ã?‹¡?²?"”?‹aV?‡Ì?‚ȳ>+àë=Ð(=ÍL¯=ÌÓŽ=ÌÌï=ÌÍ =ÌÍœ=ÌÕp=Îñ>f.?~òY?Ž÷œ?šæ>?¢W.?§§N?ªŠ‡?ª–ý?¨ç?¢ûA?›Üä?þ±?‡ç‘>+æº=Ð……=ÌÌÈ=ÌÌÇ=ÌÕd=ÌÁö?qÃÖ?Œå*? Æ?ª?µ¾¸?¿Ü?ÇYh?ËqÁ?˃ä?ÇôÞ?ÀÆã?¶Ü?«F(?ž…?ŽAÈ?–í=ÌÌ«=ÌÍ=ÌÁž?t›?”1%?¤É?µ~Ø?ÆÛ^?×d©?åËC?ðû?ö›ì?ö¶þ?ñQ?ç?Ùz?È÷?·B*?¦.K?—Fƒ=ÌÍa=Ê—œ?qšÌ?”'?¦è“?»äI?Òº™?ê‰.@óã@ j÷@w‹@þ/@Á@# @ cû@Ø?ìýŠ?Õ%l?¾*t?ª/=̇ï>AÛ?‰‹X?¤}F?»×ø?Öï«?õ_L@ !±@ä‡@+sK@7Ș@>Þr@?@8Ô©@,ì1@—K@ Ý ?ø¨Ñ?Ùé™?¿”º=ÊŒ´?~7?œí%?µJ?ÒƒÐ?õ:2@Ó@%¦[@=ö@U’F@i ·@t×@tÆ,@j½»@WÞL@@{î@( @?ù ?Ög>A ?‹m«?©ÕA?ƃU?ê"×@ ò‘@%Ö@Dõˆ@hr @†h @–/•@ŸØX@ G×@—‰@ˆ2c@l:š@H‘t@(k+@ n9?îì-?uq?š™Ô?µM¤?ÖÔ¬@¢D@Šš@=¨P@hf@ƒE@©ld@ÃMC@ÓïÝ@Ô´@Åš@¬EÐ@Mþ@mù@ApY@¨¢@^^?ƒÇ9?¡ÿ?¿HR?åH@ ó}@*âô@Tõy@†g@©"¦@Òš @ü •A GÐA ñ¥@ÿô¼@× û@­%`@‰U@YÀ§@.¤Ù@ ý?‡XÊ?§7“?ÆŸË?ï¤O@ÖZ@6ø³@hÃ@•xŒ@‘Ò@û„YAA3¯A4'A†Aï¥@ÇâÒ@™¤f@mô@;Gc@ñ?‰=X?ªK?Ê’S?õjk@2¶@=Ë/@s"ý@žÊ5@ÒÆA ~ÙA2kLAP5‡AQ±ÚA6FéAj@ØÛ]@£z_@yeÚ@Bkü@¨?‰B#?ª–?Ê›?õv)@=@=ÚÉ@s?¿¼„?å®î@ nv@+,@V @†è@ª4@Ô˱@ÿJŽA:°AìœA›&@ÙSŽ@®¡ò@ŠC`@Zïv@/g @|À?x“I?›ú?µÛó?ס%@5@aª@>çÝ@j M@Žåä@«eÈ@Åñp@×l@ר]@ÈKQ@®T¤@‘ÀT@o@B½@‡ë@õA>$s3?Œ!?ªr?Çbõ?ëZ@ ͤ@&Ƶ@FÉæ@jçÃ@ˆ 0@˜C @¢0©@¢¡›@™¤Á@‰ä @n¿L@J^Ø@)®Ý@R±?ð,4=˱? ?®n?¶*ý?Ó¶Š?öÜK@î»@'$@?× @Wòð@kä@w¯3@wéÉ@mžü@ZM«@Bk#@)“Y@2 ?úÉ«?×Öü=ÌK”>$sñ?Šh·?¥Qé?¼ùs?Øk?÷Lg@ ^¨@qp@-Qä@9ï‘@A.ß@AQ{@;­@.Ôw@+æ@!ß?ú£Õ?ÛnÓ?À¼"=ÌÊ7=ËŸ€?vƒm?“Ͼ?¦®Y?¼?G?Ó€ï?ëø&@Ü@ “@Y@¬‰@Æ!@¶£@ Ã@1K?î}ž?Õø¹?¾‘{?©â|=ÌÍ=ÌÌü=ÌÍ =ÌÍ=ÌÌÑ=ÌÔí=Ï@J=Ï+>¨c?sZ÷?‚о?…Æt?…Ò†?‚èk?€ýâ>,E–=Ï’o=ÏR›=Íõ=ÌÉÌ=ÌÌÈ=ÌÌÇ=ÌÍ=ÌÍ}=Í*=Ìʦ?o$²?‡S??˜È©?¢? 0 ? ;,?ž‰?™…5?ð²?ˆ r?ßô>ºo=Í4!=ÌÌÆ=ÌÌÆ=ÌÍ7=ÌÑ >çb?~Åv?”e3?ŸÂL?ª6?²á³?¹X´?¼ày?¼ðí?¹à=?³¥ß?«4/? ØÕ?•Š?ˆUx>*oª=ÌÌÅ=ÌÌÇ=ÌÐH>L’?‚äý?šô‰?©ß¬?¸ë?Ç%?ÓaŽ?Ü>?á†Â?áu?Ý@y?Ô~b?È…³?ºpÃ?«[ä?œbŠ?Œ<=ÌÍU=̤>âo?‚ÚZ?;ž?¯{±?Ã#|?×cx?êð?ü.@¤@Iö@Zk@-¹?ýÅ,?ìÛd?Ùt¥?Å73?±`°? Œ=ÌÍ=ÌÒC?~2T?šÞ§?¯j?ƾ†?à}d?û¶@ j$@º @!Né@&¼ô@&Ö€@"Î@à®@ Ĉ?þ‹Y?ã=ç?ÉI¹?³O‡=ÊO ?nÈ?”8 ?©¬E?Âô??à^Ô@Þ±@+?@& w@7Ï¡@F+@NNž@Nv¦@GFG@9ˆ@'ù£@@¯ø?ãœA?Çs=ʆþ?ƒÔÉ?Ÿ”Š?¸ ?× R?ûgr@¨@+\@E¾@_NB@t±–@€©t@€ò}@vya@aËÕ@H\¢@.x@XÔ?ÿy?Üò>áa?‹só?©õ??Ƶä?êg†@ !ê@%Ñâ@Ea<@iÅ@†Ëè@–°›@ hª@ ×@˜ @ˆ™¡@lÐN@Hû@(±÷@  7?ï6Õ?rH^?˜v+?²`?Ò»Ð?ûjU@HV@7[¶@^Å@†ž%@Ÿ¡>@¶gã@ÄÖî@Å}É@¸gå@¢*@‰%´@cXÐ@:å@;E@K9?~k?;;?¸¹Ÿ?Û® @ á@ ®Ž@EVÙ@sÄQ@–?š@¶ì@ÔS8@è˜@éÂ@×»@¹eÿ@™fü@y:S@Iqí@$v@ì¶?‚*?Ÿ·ã?¼!³?à…²@¥r@%é÷@MK“@ò›@Ÿ¥@Ä@çn@ÿL]A5M@êÊr@Çç’@£4À@ƒ@Q½÷@)tå@ ‰¶?‚2?Ÿ½?¼(x?àæ@­i@%õ°@M^@€p@ŸÝ@Äb¬@è±@ÿÛA}u@ë@@È=×@£pã@ƒ*O@QÒÑ@)r@ ‘Ï?~x ?i ?¸üá?Üc@en@!¥@Eò@t¥@@–ë @·Ä@Õªæ@é¹@ê§©@Øg@ºj`@š‹@z%@Jl@$jÚ@3$ot?Œ!H?ªr©?Çbò?ë[@ Î>@&Åq@FÉf@jê“@ˆ p@˜Bf@¢0@¢¢ @™¥`@‰ãô@n¾,@J^@)®@QÑ?ð-R=Ê}ƒ?„*Õ? Á?¹b?Ø[?üÏ£@º@,ÄŠ@GhÏ@a“ô@wj0@‚.ÿ@‚x>@y=¼@d@J*Š@/tý@V@uÑ?Ý\=Ê@?rØ?”Â?ªhø?Ãü?á½@ʼn@Hû@'z¾@9ï@H @P‡u@P±a@IX3@;L>@)os@A@œN?åN?Èé=̇q>zë?²"ê?…Ð×?œç?¯‡ ?óØ?ØŠÒ?ìcÂ?þ>h@ñ1@ ºº@ Ëš@à?ÿã!?îZ ?Úªè?ÅÕ?±ƒ=? )Þ=ÌÍ=ÌÌû=ÌÍ =ÌÍ=ÌÌ´=ÌÔ÷=Í =ÏAO=Ï((>ÅJ>²?m¬î?{w>*Í> ¸=Ð0=ÏRY=ÍSk=Ì×Ì=ÌÊ!=ÌÌÇ=ÌÌÆ=ÌÌæ=ÌÌû=ÌÕ‰=Îó?>Ú?pF ?„>Ÿ?‹Dn??Á?”µ?”í~?ÎT?‹Â§?„æt?m’>)_ú=Ð&g=Íð=ÌÌÆ=ÌÌÆ=ÌÌÙ=ÌÕ‚=Ìɬ?lM"?‡|’?”aE?€?¥š?ª‘á?­^m?­eÁ?«Ä?¥¼.?žW•?•x?ˆ™Ã?}$p>ÀY=ÌÌÆ=ÌÌÆ=ÌÕœ=ÌÊW?qøØ?N3?Zž?ª2?µõN?À!`?Ç¢ì?˺?ËÌÈ?È?Ð?Á à?·#?«Iµ?ž£?‘—Î?‚=ÌÍ=ÌÌÃ=ÌÉL?qæ??’H'?¢B?²ñ?Ãj¶?Ó` ?á;?ëŸÃ?ñ\Ý?ñv³?ìy¨?â~ÿ?Ôí?Å3?´Zõ?£žJ?•x=ÌÌí=Ê‹¡?l!?:T?¡ó´?µž¥?Êáf?àÛS?öA5@¡<@ å@ö@‚@ ~ã@‚Õ?ø\6?ã+?Íå?·¿Œ?¥ž=̈¹>@?„?¸?G)?²?ÊÈ ?å§Í@€@kÎ@{†@&±»@,F@,šŸ@'@µb@Ú¸@š?èˆË?Ínæ?¶œù=ÊLœ?om0?”2Á?©ÈH?Ã#Û?ài@8@Qu@&?R@8ÿ@FYÚ@NŸ–@NÉ]@G’=@9ÂÈ@(-@CM@Ùo?ãÜð?Ǩ­=Ê|?€ÊÄ?Ec?µ™S?Òñd?õÐ(@8Û@&Þ@>“r@VY~@iû¦@u”Á@uÎÎ@k®Ÿ@XªP@A±@(–À@v¤?ùµÏ?××>=~?‡Æò?¤ÀÖ?¿¡˜?àž„@J@ ,@7»@V î@t›Ð@‡?’@@5Ì@ˆd"@w¡@YXE@:Ã@ËK@Œ?ääE>†l?‹›9?ª,^?Æÿ½?êÏŽ@ k|@&&ü@EÅÐ@iqÔ@‡I@—V@ Ò{@¡ï@˜s°@ˆéü@mF@IJH@),Q@ ìÉ?ïc€?l£’?”V'?¬½?Êè4?ðZ£@W¥@+Áb@MÄ@t§õ@Ž í@ ˆ8@«¥¿@«ÞC@¢&å@±@xݘ@Q’U@.ü,@þ1?õD?o$ª?ŒØ?ªbE?ÇMÔ?ë<@ ·å@&•@Fa}@jJä@‡¢i@—½@¡£@¡ÔO@™-¿@‰€b@n'Ò@IëP@)ž3@<„?ïÒg>9:?ˆb?¥7?À?á>@¹z@¼g@8’³@WG@v?@ˆ*ƒ@@=Ù@‰Rß@y+-@Z‡¦@;¢¦@k°@ÿ?刼=Êò˜?¾?®F?¶*ø?Ó¶v?öܪ@îÑ@'Â@?×Ä@Wóë@kâ«@w¯í@wèú@mžÓ@ZM@Bjà@)“v@2±?úÉ^?××u=Ê>É?r׸?”Ç?ªi&?Ãûù?Ỽ@ÆF@I@'z`@9‘@H @P‡"@P¯ž@IX+@;Kº@)p¤@A$@›ü?åk?È‚:=ÌIê>"(¼?„ü£?éÀ?³[?ËåÞ?ç@ª@‰%@Ï&@(2’@.µ@.8æ@)Ë@*@ý•@‡?ê?ΓŠ?·{=ÌË,=Êü9?pN?ÙL?¢¿P?¶­H?Ì4x?âz]?ø1<@Áu@ %¢@IM@\–@ Áÿ@¦K?úV¨Ì?wî?‘¼W?¡¯â?²®€?ÃüÌ?Ô*õ?âH?ív ?óp›?ó‰Ú?îV?ãÞ³?ÕÀ?ż8?´w'?£^l?”Û~=ÌÍ=ÌÍ =ÌÍ =ÌÍ=ÌÌô=ÌÍB=ÌÕÙ=Í =Í_=ÏS<=Ï(•>š¶>q=кá=ÏeÞ=ÍKð=Í"=ÌØ7=ÌÍ~=ÌÊù=ÌÌÇ=ÌÌÆ=ÌÌÇ=ÌÌÆ=ÌÌÝ=Í“=Ï0>ør>œà?s†‰?‚Š?„¼Î?„Åw?ƒ[?1ü>+>=Z=Ð-_=Í@ü=ÌÈj=ÌÌÆ=ÌÌÆ=ÌÌÆ=ÌÌô=ÌÑÁ>G\?maù?„„$?)¿?–ÁQ?›wT?íî?ùT?›Óx?—v¨?Ž=?…Ll?|¸->'¦¼=Íò=ÌÌÆ=ÌÌÅ=ÌÍ =ÌÐâ>?vO?o½?›/Í?¥ ?­3?³B ?¶Ž?¶c?³¾í?­ë?¥û?œ5ð?‘‰ô?ƒß†>(ƶ=ÌÌæ=ÌÌÆ=Ìϼ>ÿ)?y“Õ?”}/?¢3u?¯Û)?¼«Ï?Çž?ϼ?Ô.C?ÔB¤?Ðe?È›¸?½çm?±;¨?£{?•Ö?…̹=ÌÌæ=ÌŠÊ>DÂ?vY[?”r?¤®Ø?µÝ—?ÇRH?×óö?æn‡?ñQø?÷WŸ?÷rë?ò6Ù?çÂ!?Ù‘×?ɵ?·ª?¦Y­?—»,=ÌÌê=ʈ"?l­‹?R²?¢@?µÈ?ËK?áœ?öŒt@Ì·@ è@(~@;@ ¯m@®d?ø¨Ø?ã]?ÍTå?·ê—?¥Àð=̈˜>gS?S‡?› 4?¯ªß?Ç!¥?àû?üR @ É´@*J@!̆@'D«@'\ø@"œA@SP@ %Ø?ÿ,?ã¾8?ɯo?³Ÿ!=Ì„ö>|]?‰º ?¤Û%?¼\Ì?×÷?ö@†@ °ç@—9@,J©@8¾z@?ç‘@@ @9Í–@-Ç1@M!@ pU?ùr?Ú›^?ÀÔ=ÊU%?r–ç?–{Í?¬Ëÿ?Ç"u?åëœ@ŠÎ@ð>@,%5@?O @NÉ@WÉx@Wôþ@Pª@A%z@.7@´@vr?éR?Ë ´=Ê|d?~ò?›ž?²¾Þ?Ï?ðœf@ ²d@!jb@8i3@N’›@`»¥@kh@k›1@bLÏ@P¸¬@:Êg@#Á @ ÔŠ?ôUá?Òç×>+?[–?J?µïy?Ófi?öm2@¢ó@&³ @?Uƒ@WLã@kê@vÎ<@wê@lÑ,@Y¢@Aåí@)0§@äÓ?úW?×D?>§}?_·?†±?µ÷ ?ÓoÛ?özH@¬b@&¿º@?fz@W`»@k1T@vé-@w D@lê‰@Y·L@Aöò@)=D@î?úe9?×Mà=ËxÕ?~ΰ?›L@?²þ‡?Ïn·?ñ r@ ÿØ@!Ò@8ðJ@O<–@a„Ö@lEÒ@lx.@c³@Qeå@;Tõ@$*§@$Ì?ôÌ^?Ó<}=ÊFá?u·Ö?–ØB?­(Ä?ÇŸ»?æ’±@û@ƒæ@,â?@@8G@OÚ„@Xõr@Y !@Q1Â@BÈ@.ùÓ@™@éƒ?éÿ¯?ËŒN=ÌK7>$Z?Šhâ?¥R"?¼ùa?ØjQ?÷L¸@ ^©@pú@-R@9ï@A/"@ARm@; @.ÔŠ@,?@!Ä?ú¤@?Ûo”?À¼o=Ì…>n?²?›Ô?°Za?È0?â•?ý¼Ð@ §÷@21@"÷Å@(‚”@(œ @#É@^¹@ÿ@O?ää÷?Ê–H?´Oƒ=ÌË<=Êéû?pN?Ù?¢¿J?¶­9?Ì4l?âz0?ø1t@Áå@ %@I@\Š@ ‘@¦|?úVE?äÄ»?Îy—?¸ÔÆ?¦wn=ÌÌç=ÌQ> zj?{g`?•o?¥‚f?¶í?È™Q?Ùrk?è!ÿ?ó. ?ùLú?ùhö?ôd?ézs?Û?Êd?¸¾ð?§:Ù?˜Y^=ÌÍÃ=ÌÎ=Í>!P;?~ï"?“é$?¡àË?¯ñÙ?½ 0?Èu?ÐÉø?Õo‹?Õƒ(?Ñwb?Éz?¾NÇ?±[o?£LÌ?•+…?‡ø‹=ÌÌú=ÌÍ =ÌÍ/=ÌÍo=Ìͳ=ÌÎ=ÌÎP=ÌΘ=ÌÖÐ=Í =ÍG=Íò=Íy=Í=Íä=ÌÙô=ÌÎþ=ÌÎ{=ÌÎ2=ÌÌI=Ì̹=ÌÌå=ÌÍ=ÌÍi=ÌÍÎ=ÌÐÕ=Í=Í$`=Ï#†>åÓ>ý‹> L> ü>>ÒZ=ÐÐÉ=ÍYõ=Í­=ÌÒ³=ÌËŒ=ÌÌð=ÌÍ=ÌÍa=ÌÍÀ=ÌÎc=Ìá]>$i>Ð?sÜ_?„ðÃ?ˆ—)Ëß>e=Î:=ÌÌ#=ÌÍ<=ÌÎ$=ÌÍÅ=ÌÎR=ÌÙÿ>ôÙ?tç?ˆaÆ?“†›?šl£?ŸYÙ?¢7?¢)9=Íx¡=Ì͉=ÌÎ=ÌÎS=ÌØØ>0‡?zµ?‘HÃ?œv¦?¦q¸?¯>D?µ´§?¹7{?¹FŸ?¶8?°ä?§do?…ë?’eë?…¨¯>(ß¶=ÌÍÐ=ÌÏ¿=ÌØ¢>ñÂ?yí?“G?¡r?®ó©?»âQ?Ç*!?Ïcå?Óù¨?Ô ,?Ðß?È,"?½#·?°XÐ?¢oú?”™‘?…ߨ=ÌÎ<=ÌŽ›>¥ ?sЦ?‘5k?¡Y?±Í?Âï*?Òñ5?á+Y?ëíÂ?ñÖP?ñó^?ìÌ+?âvµ?ÔÖ?Ĩ÷?³‘X?¢£ ?”[Š=ÌÍy=̨ò>Z´?…'×?œa-?®Õ?ÂÖÄ?×€?ë$~?üÏ—@%Ö@å¶@ö@±‹?þp­?í’?Ù˜¿?Äôã?°Í?ŸžJ=ÌÍû=ʤ2?sÈ?“ZE?¦3¦?»Ÿ³?Ò¸Ê?ë@jÂ@ 3@Aï@áÔ@ýW@õæ@ @˜+?퀢?Õ-?½îB?©f:=̃ƒ>Uq?®?š3i¾?…1u?Ÿ “?µ@i?ÎÛÝ?ëbÉ@ãF@ ä@" ž@,þù@3E-@3c·@-íh@#U@c@yD?îpÅ?ѦC?¸¦å=Ì™]>¥?‡[?¡¡*?¸¥æ?ÓK;?ñ@ƒ°@…}@'dx@3¥@9Ю@9ð@4›@(ÄŸ@ˆ@ /%?ôK}?Ö3â?¼=Ì™w> ?‡¦?¡§?¸­(?ÓTÏ?ñ(š@Œ:@•²@'wÍ@3%ì@9à?@:ó@4%d@(Ù<@.B@ 7X?ôX²?Ö>%?¼I=̆<>dÿ?…UG?Ÿ>µ?µ‚Ú?Ï1©?ëÑ}@( @g@"x@-vG@3ÄP@3ã“@.e”@#Æ’@ì@Á‡?îãè?Ñþ½?¸éÌ=Ì€g> x?Ý}?š}–?¯K7?ÇG,?ás?ýOÂ@ z8@ )@"ÍŽ@([ô@(~H@#¦—@9p@ Ûó@[?ä?&?ÉÞë?² G=ÌÊl=ˉ??v„!?“Ð?¦­}?¼?K?Ó€ä?ë÷Ù@ @ Ã+@Ø@«ÿ@ÆÉ@¶@ Ã-@1?î}X?Õù?¾‘Ð?©â =ÌÍF=Ìs!>"ÿ‚?…ÏÈ?œçã?¯†ó?ôø@ñ @ ºÀ@ Ëß@°?ÿã-?îYÚ?Úªƒ?ÅÕQ?±ƒW? *=ÌÎ=ÌÙ>œ°?wÿ?‘¼­?¡¯É?²®b?Ãý?Ô*ÿ?â0?íu¹?óp€?óŠ ?îVÁ?ãÞý?ÕÀ?Å»z?´w5?£^m?”Û!=ÌÍÄ=ÌÎH=Í …>!W‹?~ïÁ?“éM?¡àS?¯ñL?½ ?Èu?ÐÉÇ?Õo(?Õ„?Ñwq?Éz0?¾Nj?±[P?£M ?•+`?‡øt=ÌÎ=ÌÏ|=ÌÐ-=Ì R>!A?9?»R?œõ?¦>G?¯h²?¶?¹ÄG?¹ÒR?¶¥?°6²?§;?+ ?‘Ãs?‡§¥>K†8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý 8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýè8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý­8ý8ý8ý8ý+8ý]8ý8ý8ý8ýü8ýú8ý8ý!_8ý!¯8ýT8ý)8ý*8ý8ý8ý8ýS8ý8ý8ý8ý)8ýU8ý-8ý\8ý8ý"$8ý"Ù8ý8ý% 8ý)…8ý$8ý#I8ý"Ý8ýë8ýd8ýT8ýì8ý8ý8ý8ý8ý8ý8ý!¤8ý!ÿ8ý"j:„S:‡´„:þ½;Yj:‹Õ:‡óÖ98ý$Æ8ý"8ý•8ýa8ý8ý8ý8ý8ýÏ8ý!*8ý!Ñ:…J}:‡õv;ƒ”;»[73Ì]:T¶:ÆŽ;;6:‹EI:‰=ñ99²8ý$L8ýÕ8ý8ý8ý8ý8ýž8ý Ò:…LB:ŒWå;â67,ž°6€5e5Aî6¾™ð6“7ã:T_:ÆðÂ:Žtó:‰m@9(Ø8ý8ý8ý8ý8ý)8ý Ø8ý!j:‡d‡;Ì‹74$ 5‡;v5ƒÚ‹5|™5Å$5ƒŽE6Å É:Q…S:Æ¥:bd9 ÿí8ý«8ý8ý8ý8ý)8ý ¬:…-Ï:‹\u6õÂ(5‡G5€6±5|Ï5xéŠ5xÓ{5{§25€ƒÊ6ä.á:Qbð:b=^:ˆÜä9388ý8ý8ý8ý8ý :‡Nó;Î-6çê5„ ò5| 5v!­5rÞÍ5rÔ?5u©—5{´û5ƒëû:R½©:·Ý :Ží¤9…î8ý8ý8ý8ý!_8ý÷:‡ÞŸ7,A5LÏ*5`›5y*>5rôŸ5o¯è5o¦J5r|M5x5w`7T9¦:Q)`:cõè9àß8ý8ý8ý8ý!³8ý Ä:‡á6’¯§5@;ï5?5y%ƒ5rí¦5o¨ 5o˜J5rp5x‡f5„y5Fe}:S=ß:b ¤9Ý8ý8ý8ý8ýS8ý D:ˆ–b:Y6b>5G§J5A‹ 5D×­5Jñ“:A?ª::ôQ:Šß4:ŒîÓ9§8ýÃ8ý8ý8ý8ý$8ýú8ý ì9.¯:ˆûj:Œ=µ:?<:8&C6ƒ×:A†˜:|Ÿ:_¶é:‹C:ŒË90ö§8ý„v8ý¨8ý8ý8ý8ý8ý8ý28ý#ä9Æ9?ò:ˆŽ:ŒH¤:B%,:b§³:‹E:ŒY90ÎÎ9„À8ýwh8ý~8ý48ý8ý8ý08ý^8ý˜8ýÔ8ý58ý%E8ý% 9õæ9é:9ðW9´9Î×9Ý¿8ý¿Á8ý8ýK8ýé8ý`8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýé8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ýW8ýv8ý8ý8ý8ý8ý³8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý!C8ý!›8ý—8ýu8ý8ý8ý8ý8ýZ8ý8ý8ý8ý8ý8ý8ý8ý8ý!b8ý)à8ý*:„E§:ˆ6¿9fú8ý,™8ý!Í8ýD8ý8ý8ýØ8ý8ý8ý8ý8ý8ý8ý!58ý)Ï:ƒ(;ux;Ûé;%Í;3ô;óë;Ï(:Œ×’9 «E8ý"Ñ8ý‰8ý@8ý8ý8ý8ý8ý8ý ø:ƒ;jk;}è7CѾ6£ú»6Ðáw6ÐÁö6¥SÓ:V Ú:Èi;Á¦:ŒÃ#9 E8ý´8ý8ý8ý8ý8ý! :„A6;7F16¡2Ê6K@„6BC4:bR7EŸ$6lè7º‚6µ—·:VÂ:ÊY:ŒŠ9 ¥Ù8ý8ý8ý8ý8ý!y:ˆzÜ7ª+5êZ‘6?ÝÍ4C®74Þ4«4mo@4 )7EÀ¾6mÃ6áÐå:Rª„:eo9’8ý8ý8ý8ý! :„R";—Š7&D6By4*–€4¢)4i4œ÷4ž'4¿4lNà7CW6lk:SÍ :¹Å>:Œ«8ý8ý8ý8ý!w:ˆ‰Œ7’ù5ëT4$ù“4 £4m4÷44œ”44žo4lF¤7B¸U7nCÂ:R’‹:e²¼8ý8ý8ý8ý!†:ˆœŠ6­ Ò6@áþ4#½ 4ž4œ§4œ¹4œ»4œW4œ]4œ—4ž¿7F#F6cg:U»¯:bís8ý8ý8ý!:…x—:Œ`@6­ËÂ4/4ã¬4œì4œ4œ~4†4œ^4œH4œº44q›`7?û‡:R—Œ:bÂ8ý8ý8ý!m:‰Ô:Œqƒ6­Ð64ÎB44œã4œ„4œJ4±4œ?4œV4œ±4œÿ4ž 7B¶ö:R†É:b¹8ý8ý8ýS9U±:ˆÂ6­ö»5èÎx4 ß24á4œæ4œd424œÀ4œ›4œÁ47:Ð[6$Ð:UŽ·:bñã8ý8ý8ý8ý#Ô:ˆˆ¡6³—5ãY4 uü4Ÿ‹4a4œ°4œÎ4p4œí4{4b7E¹06>y:TÖ:cÒ\8ý8ý8ý8ý!t:‰N:>w?6™ž„5è£+4Âc4ž 4¥44 44ç78Ò6$©:Dú:<Úû:‹ZÖ8ý8ý8ý8ýO9F:ˆyÌ6´Â5é‘ 5å¼Å4"4œ 4›·434š>78­¡6%âÞ5èÊà:Tœè:b\69-8ý8ý8ý8ýL8ý$ç:‰Ó:@¸›6¡±5ìÛó5ìòü5åñä4!×79.R6[6 `–5íƒÙ:C)*:>#¥:‹».9?¸8ý8ý8ý8ý8ý09³:ˆøÖ:BÛ•::¤6¦×þ5ï³p5ôöm6Cè5ñš·:CM: Pä:bË&:‹ÚË90Z>8ýzê8ý8ý-8ýX8ý•8ý8ý@F9>Ö9?ï:‰ž:Cm@:=¯:=Ÿ>:=›¿:=Žt:d\x:‹ÿí9-+ß968ývÚ8ý78ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý'8ý8ý8ý8ý8ý®8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý8ý!E8ý!œ8ýV8ý88ý8ý8ý8ý8ý[8ý8ýL8ýØ8ý8ý8ý8ý8ý8ý)q8ý*8ý*:û“;º9*ëÇ8ý.õ8ý*,8ýŒ8ý8ý8ýß8ý8ý$8ýI8ý8ý8ý8ý)8ý*(:ûä;à;Mú7D\:Uª :Åmá;¶†;×09*ê+8ý/†8ý;8ý/8ý8ý8ý8ý8ý8ý(÷:ûÀa;C97AÉ6Ò=6˰ç6Pj¨7›¼6ÞÃG6Е:V:ż#;–q9*µ8ýú8ý8ý8ý8ý8ý!:úš-7EW‡6Ñlˆ6J©W4 1ù4]4šÎ4„z4&»7¿°7L6á³ :Uñ:Èo9"Šx8ý8ý8ý8ý!*:ƒÅ|7Êæ6P?·4ö4‘4³D4«Ñ4—Í4©í4´˜4Q º4{¢7î7'Ù:RÒ9:_†Õ8ý8ý8ý8ý!Œ:ÿ}ë7ð4 j34¤4ªì4—h4–k4–94›z4–o4ª÷4´4P*7Sî:Q*:»í¯8ý8ý8ý!):…`7ªO6JÅØ4³å4­4— 4–¨4–f4–y4–l4–=4™ 4¦-4µM7qÄ7},Á:%Ê­8ý8ý8ý!x:ˆ—é6¬„W4 ‰r4Ïè4–§4–J4— 4—l4–è4–E4–ï4–ª4š(4¡4Oë7‰}:®8ý8ý8ý!:þSŽ7íþ4ç 4¬-4–Á4–-4–>4˜04–O4–<4–/4–­4–<4©ª4´ü7Ía:²º8ý8ý:„Æ]7{u5ì³!4÷t4—4—4–C4–(4—!4–Ê4–Y4–Ž4—y4–¥4™<4œ7&­Ä:ö~8ý8ý:ˆQÔ6·ã@5ä=4 ²Ä4–{4–ß4–¸4–,4–Z4˜.4–k4–w4— 4–Š4–Ÿ4 e7'‡ :ôH8ý8ý9þ{:75:6­4ت4¤f4–A4—4–£4–>4˜^4–ó4–Q4–m4˜4Ÿˆ4¯ì7]×:^ 8ý8ý8ý#Ä:ˆtæ6¬\·4§p4¢º4–?4—4—X4–04–g4–í4–þ4–S4–`4©A4«q7ƒº:68ý8ý8ý!Œ:ˆˆ6³5ëÖ 4 ¹n4¤Ù4–h4–I4–u4–{4–24–%4–,4ž$4¯Þ7—æ6 Bá:&¸€8ý8ý8ý£9:8Fº6±m4æê4£L4¤4–ô4–K4–O4–@4–4ž=4°¾4£7Ç:B :8‡8ý8ý8ýF8ý$F:ˆ[Á6·%5óçƒ4Yº4¡È4¤h4¤a4–Ã4ŸÂ4¬4¨_4¢7)¼6$[m:T¤:d…8ý8ý8ý8ý%9ä÷:;Å6º’Y5ö›ƒ5é?_4!4£Ÿ4£34§Ž4¢Z7ff6 ='6Üû:Cq:?Žu9)>¹8ý68ýY8ý‘8ýÜ8ý? 9Ó:>û†:<Í=6©;î5öŒÁ5ñeJ5îz¨5îjg5ñ‚6‡ú:D¸™:# ©:džÉ9(·V8ý2r8ý8ý8ý8ý8ý8ý8ý8ý8ýü8ýú8ý8ý!_8ý!¯8ýS8ý8ý78ý8ý8ý8ýV8ý8ý8ý8ý8ý8ý8ý8ý8ý!48ý)Ú8ý*:„K/:ˆ<¹9cö8ý-Š8ý!è8ýD8ý8ý8ýÜ8ý8ý8ý%8ý8ý8ý8ý)~8ý*:ûÏ»;ÝÅ;MN7DT:U¼—:Åc\;®=;Õý9*êf8ý/‡8ý;8ý08ý8ý8ý8ý8ý8ý(÷:ûÚÕ;GÎ7Y6ÐO6ÇV4ne7˜Å6ðþP6ÏãÀ:Wo):ÅvÂ;ž 9*)ë8ý#8ý8ý8ý#8ý8ý):ú͈7ZýÃ6ËÐ04<·4'4ü4¯ü4‚d4: 4Z7j Æ6ðë_:WNÁ:ÇsU9"÷!8ý8ý8ý8ý!:úÊ 7X¡4Þµ4±4­Y4–¥4–4–²4–?4–34<4¶4:Œ7fãõ:Q—:ÈK88ý8ý8ý!4:ƒØl7üû4Ö4­½4–F4–+4–W4–$4—4–ö4–44˜)4–y4;e4¬ç7oWg:>Ë8ý8ý8ý! :ÿ¸Ù7y4xš4–I4–4–:4–÷4–%4–o4˜4–04–4–)4˜Ô4387%݇:Ä8ý+8ý:…[7Ç4[•&4±í4–D4–-4–64—K4–W4–?4—4–\4–q4–|4–q40Q4>j9Þh48ý+8ý!9:ˆªû6ªtý4¢±4–74–4–!4— 4—04–î4—\4–n4–t4—;4–§4–{4—¹4© 9¦{r8ý8ý!Œ:þC7”ü4eÊ4–S4–Ž4–14–¸4—4–Ò4–Y4—ò4–÷4–ß4–Ê4– 4–-4}Ô9†»ï8ý8û056ÅVÏ4]U4ªÞ4–>4–‘4–ì4–D4–64˜]4–Ê4–ã4—‚4—p4–†4–†4–T4„9pÒÖ8ý8û#ô6*êê4È…4¢Î4–/4–94—¹4— 4–w4–Ý4–Å4–Ä4—4–•4–‡4–Ú4–m4ƒú9pwp8ý8üìC:7%6­Ä.4Áö4–v4–'4– 4–ï4–ÿ4–ã4—u4˜4–‰4–ï4——4–@4–É4xb9…¸á8ýý8ý!m:ˆh×6ª<4¡Ž4–Ã4–54–4–=4—D4–e4—44— 4—k4–14–}4–Ô4–±4‡9¤Áv8ýþ8ýW:ˆ|ç6±?‘4ª+4¤†4–L4–q4—v4–C4–74–Û4–O4–Q4–"4–è4–Ç4&=4Cç9ÛMZ8ý8ý29÷.:7®6¯³¼4É 4–84–¿4–ü4–d4–-4–y4—4–C4–Q4–›4–Z41z7hZ:@–8ý8ý8ý$):ˆP6³<û4±¶4¥64–34–Œ4–‡4–-4–$4–+4–²4– 4–.4%4­ç7?>:>²b8ý8ý8ý$9ãF::…E6¶K:4Ô 4£D4¤´4–Ì4–k4–4–4–¹4%+4ª 4¦{7¦-:@ým:<™°8ýd8ý˜8ýß8ý‡9ë.:<ôI6¸ T5ñ¥\4 =4ää4ñ@4÷F4÷o4ñ§4è7Ëð6á':CÙ):?ï9%8ý8ý8ý8ý8ý8ý+8ý\8ý8ý"-8ý"Ù8ý8ý%$8ý)ƒ8ý8ý#I8ý"Û8ýë8ýe8ý+8ýç8ý8ý8ý8ý8ý8ý8ý!48ý)¾:ƒ"µ;tæ;é…;8f;GA; 0;Ö:ŒØ›9 §B8ý"Ë8ý*8ý/8ý8ý8ý8ý8ý8ý(ö:ûÅ1;Bä7Aƒ6Ò/ï6ËÊ 6PÁ7ü[6Ýmv6Ðq¥:V}:Å¿[;–E9*É8ýù8ý8ý8ý8ý8ý(þ:úÁÂ7Zú6ËÒ{4<l4à4Ö4¯ä4³ƒ4;Û4Û7i‡Ï6ð¤¡:WJô:Çvv9"ø\8ý8ý8ý¿8ý)³:ûQ7V»4…€4ì4—µ4–¢4–Ö4–>4–+4–U4–4Ár4L7&†:Q› :Çôî8ý*8ý8ý!=:úÐÙ7Vƒ4{*4–+4–4–+4—4–m4–04–4–¿4–•4–B4–L4 7@(É:RP/8ý^8ý:ƒÂž7×4ËN4—@4–D4–4–"4—y4–W4–N4–;4—À4–'4–w4–-4–¬4í:(t~8ýŸ8ý!E:ÿqð7t4_Ž4–[4–/4–4–4— 4–34–^4˜L4–4–4—4–„4–4î|9Ý?k8ýÒ8û/h6¿r4]gå4ž'4–+4–w4–a4–)4—I4–T4–ÿ4—É4–±4–¶4—24–¢4–44ÆD9e8ýè:… (6ªí»4¤4–%4—-4–34–<4—4–¥4—¢4˜K4–ƒ4—£4–^4–­4—4–T4¦ü8ñÀ.8ýZ:ˆ6ÕÐ4r4–14–‹4–¾4—4—¢4˜ 4–é4–¦4˜"4˜Ú4–³4–Ø4–74–‘4½8ac8ý$ø:ˆ¦«6]"§4:4–è4–@4–]4—/4—4—:4˜µ4˜G4™k4–Í4—’4–Ò4–ž4—²4˜¡8V¬Ð8ý%a:ˆR6YÊ4ú#4—4–4–J4˜4–œ4–q4—t4˜14˜4–ö4™W4—Ž4—-4–ç4™í8U·õ8ý¹:ˆ“M6ªó¥4±Ã4–œ4–x4–74–[4—4–Þ4™N4—4˜4–é4–S4—L4–v4–Î4ž®8¥“8ý`:ˆ‡à6¬nS4£4–Q4—Œ4–‰4–64—A4—Æ4–ü4š²4–Õ4—Ç4–U4–•4—4–t4£ 8ëÔ8ýÖ9w¬6+0­4°4–C4–4–"4–ö4–Ü4–54–¥4—„4–º4—;4–®4–R4–€4–(4ÁÊ9]í»8ýÈ8üùô:8 „6¯ºø4Öá4–4–u4—G4–F4–4—44–å4–Õ4™/4–+4–*4–4–h4í§9Ø0{8ýh8ý®:ˆ;&6³966.é²4ºÜ4–4–n4–*4–4–4–·4—à4–U4–Ï4–Œ4–W4–¼4–4–4ia9öfC8ý28üòQ:;yµ6¶L 4ÞÅ4–?4–4–‡4–54–04—G4–n4–£4–4—œ4–4–4–h4>’:0½Â8ýí8ý¾9À‘:<ìó6´ (4ç4¸g4ß4¬48÷4Sc4a¤4a4TÝ4;ó4³4âz4c7%à:Rc98ý38ý8ý8ý8ýÏ8ý!.8ý!Ó:…XÖ:‡Òû;ŒE;·ï73Èä:T<´:Æ’=;9,:‹2¢:‰9£9:‘8ý$A8ýÖ8ý8ý8ý8ý8ý!&:„E:;à7FD6¡R16KY6BCÎ45¬57Ean6nŒ+7Þ¾6´³:UÈ/:ÊKq:Œ{9 œÆ8ý8ý8ý8ý!*:ƒÉæ7ôÍ6Oñ=4ò‹4 +4¿Õ4« 4–ú4©o4¶Ž4X‘¦4Ç7ÉZ7â4:S:_³(8ý8ý8ý!.:ƒÐ½7ò4Ù4­k4–;4–x4–]4–$4–>4–4–V4˜Ö4–84: 4Îè7o={:>È8ý^8ý:ƒ¼š7 4Î4–Ó4–)4–4–‰4— 4–(4–`4—C4—o4–34–=4–½4–Ž4Òô:(ry8ýŠ8û/6´d4ï¢4–H4–4–4–4–p4—j4–G4–…4—Ò4—”4–o4–W4–4–¾4y’9üYž8ýÃ:ú¿Ý6Ç©´4L‹4–É4–4–4–‘4–†4–A4—Â4–V4—34—4–»4—š4–_4–e4ú–9 î8û(ú6Ã?n4A$‘4™.4˜\4–54–4–4–¥4–Z4—ý4—x4˜Ì4—ù4—F4–-4–å4–54©O9ט8û&96~k45¯ý4˜4–H4—4–ã4–E4— 4—4šn4–ï4š64™Ž4–Á4–®4–24–b4˜ñ8&NÄ:ƒ}D6WN4?=4–±4–4–L4–r4–M4— 4—E4œ”4™y4J4™4—l4—54–4— 4–Ù7H:þX 6½òÔ4<Õ4–O4–4—4—J4–þ4–æ4š¹4œö4œT4ŸK4žC4˜"4š 4™’4–É4–‘5Éc 6Æ,º4?õ34™¶4–R4–¸4–š4–ê4˜"4—Ù4™“4žœ4è4žö4£¹4› 4˜4—‰4—ú4–O4ÓÄJ6,c24ÄÛ4–„4–(4–‹4—F4—:4—ê4š·4›4ža4¢a4¡n4¤4œ-4™&4—.4—%4—…4ÎH|:5½Û6] :44–‡4–n4–b4—/4˜«4˜4™ù4š¡4£K4žr4˜ì4—´4˜>4—i4–Û4–[5»:‡ñÜ6YŒj4úÝ4–C4–#4—°4–À4–4—G4˜¶4˜64š 4›N4—•4—À4–Ù4—4–ª4——7:‹9iþ6*â»4! g4—á4–W4–:4–V4—H4–y4—z4˜h4š4˜ 4˜=4—µ4–F4–¬4— 4šF8qÍ8ûo6)²_4:4– 4–a4–/4–§4–{4—4–z4—=4—d4—Ñ4—Å4–d4š’4–H4–N4¨)95?8üå}::7â6h’_4 ß4–ü4–Z4–J4–94—c4–a4™@4–\4–Þ4—€4–$4—÷4–!4–4ßa9™_8ý9 ë«65]4Þ¡4–4–"4–J4–s4–&4–N4—i4–i4—«4–04–ƒ4—4–y4–+4£9÷PÅ8ý=8ýe8¿’Ç6'N4Ò4¸r4Öè4e44j4[ä4vŽ4„·4ƒè4w¦4_§48‹4_4Û#4?Ö:&R8ý$8ý8ý8ý8ý8ý Ö:…Hz:Œfe;Òœ7,™Ÿ6€6ƒ5B©U6¿C56‘¶:T%y:ÆÝº:ŽeÎ:‰[¤9e8ý8ý8ý8ý8ý8ý!‡:ˆ~é7Ï25êgÞ6?›—4@—?4 ô4¹Å4wÚ 4|7ER²6n¹`6áÅ®:RÒ§:e•ä9 8ý8ý8ý8ý!’:ÿr´7‡4 J4£‰4«4—34–R4—!4›4–­4«04µ4WÚä7Cý:Q7C:»ù¸8ý8ý8ý!•:ÿ„o7c²4{ƒ4–A4–4–/4—«4–84–„4–l4—.4–24–84˜É43ƒ7%·ç:¯.8ýŸ8ý!:ÿp½7kÝ4\54—j4–}4–4–44š 4–44–14—³4–¼4–(4–L4–]4–T4î-9Ý Ó8ýÖ:„!6Øí†4%á4–4–'4–p4–Y4–?4™Y4–F4–V4—×4–á4–ÿ4–14–4–Ô4Ñ¥9‹£c8û(ø6ÃRX4:,c4˜§4–(4–4–!4–l4–p4—v4–`4—“4–°4š4—4–Ü4–Å4–h4¨9á 8û%¸5?$s4µè4–€4–•4–p4–?4–4–%4–H4–z4—94—ß4š´4—r4–p4–>4–-4˜¡8oèÉ:úC6»]â4;½4–4–*4–ë4–U4™ 4—S4˜4›¢4–Ø4—Œ4ê4–ú4–\4–C4–Ž4–b7W¤Ì6Àõa4NÕ4š 4–Ã4–4–$4–ò4–:4—4™¯4¤Ñ4˜k4—W4™l4¦4—Ö4—[4—Z4–5îʱ6‚Dr47n4™{4–44–é4–—4—P4–±4™[4›—4)4£:4œm4ží4–M4›4˜Å4˜›4–ð4Núæ5GùÖ4ù}4–(4–R4˜4—T4—ü4–è4™°4—á4¸®4£Î4›4Ÿ¯4›e4á4—C4—‚4–j4Ð×5BÕ³4µ4–>4–74–à4–Ù4—4—™4˜d4šá4›ø4£˜4°d4£*4©w4–˜4–\4–û4—Ô4Àq6)¿¼4 ñx4˜ã4–À4–…4—m4—¬4  4–Ì4œ®4—.4›4·¹4–à4—4˜,4–ö4—;4–y4CTø6(áH4 ÅŒ4˜d4–#4–*4—Z4—4™­4–ò4—ø4žq4—M4¡ 4œ€4£Â4—4–,4—%4˜\5×Ê:6Ìa6]³44–¥4–E4–•4— 4–!4–\4–4žö4–^4š¸4š4–æ4—š4˜Ú4–[4—%7F’¦9!«5Mæ4¶<4–a4–)4–,4—"4–04˜û4–†4˜44–J4™ 4—Q4—}4—‡4–w4–4š 8cº8û6.ªð4"ˆ>4˜t4–K4– 4–Æ4–ä4˜œ4–÷4—&4—w4—4˜”4–_4–V4–Ó4–f4¦9T8üæ8¾ž‰53˃4°]4–4–#4–·4–n4–“4—4–¼4˜’4–D4–Ž4—4–a4–!4–=4ãâ9ˆ/ 8ýU8üÏŒ: ö6Qc„40ã4ß94¾4'Í4S»4tg4Š34—4•‰4‹E4wH4Vþ4+´4b4©W9Ù[Í8ý8ý8ý8ý)8ý Ü8ý!l:‡RX;Å174:­5‡Jƒ5ƒãÙ5‚15Ë5ƒ’36Å ;:Q†e:Æó:Jm9 ô—8ý¥8ý8ý8ý8ý!:„V¿;¢ò7`6A¢Ý4)Âö4¡‡4F4œë4žñ4z4vS7C H6m¹A:Tt:¹î:Œ#Ý8ý8ý8ý!&:… ²7Ép6JÕ4[4«à4—84–|4– 4–74—b4–¥4™-4¤…4µû7®7}<:%Âè8ý*8ý0:„ÿ67À/4YW§4²J4–@4–'4–74—A4–k4–14–ý4—S4–-4–4–‡4/|4Fèˆ9ÞTÇ8ýÒ8û/ç6¿‡Ÿ4[Dæ4ž%4–˜4–24–Þ4–‡4–ø4˜34–N4–ƒ4—E4– 4–4–³4–4À9eÔ8ýÅ:ÿˆJ6Âéõ4F>4–$4–p4–ª4–“4–†4—Ø4–×4—94–†4—Ü4—C4–*4–ð4–A4B8Ñ2]8û&;64<3}4˜î4–.4—<4–à4—Ì4—…4˜Þ4–›4›i4—>4˜X4–Ó4–·4–å4–¯4—é8&€Õ:ùÙ¥6»?q4= 4–X4–\4–I4–†4–E4—'4—Ö4—´4—4™°4—`4˜4—U4–©4–¿4–á7Wß6ñ54s,Z4œ 4–%4–Æ4–T4– 4–M4—À4—Ü4™4š"4—e4 o4˜j4—o4–˜4–.4–66D?â5@³_4¹Â4–14—4–`4–M4—Ê4–4™454¡+4›Ù4š™4¥–4—û4›³4žl4™K4–Ñ5Aÿ5H4³ 4–44–4–g4–æ4–.4—Y4—4—î4˜”4˜³4–þ4–ÿ4—4–W4–'4—ñ8„Ö8üî”: ˆ6Lú–4ç4–f4—4–24–;4—4–Z4˜"4˜4–a4™c4–d4˜4–`4–‹4¤‚8È&Ñ8ý9 AN6Ÿ64 d4îm444µ4SE4nÑ4œ4Žð4˜44–/4ž4ƒŠ4oÖ4Ué48Š4E9] 8ý8ý8ý8ý)8ý µ:…55:‹d¹6ö _5‡0X5€65|â5xìã5xÈþ5{›¼5€‚6ãúy:Q+õ:b{:ˆÖ¡9*Ù8ý8ý8ý8ý!n:ˆŽ,7®ß5ê‚4&I4¡E4œØ4œ§4œÉ4žT4œ¿4ž4vN7B/w7nDž:R†ö:eÈå8ý8ý8ý!w:ˆŒ6¬q÷4æ84Âî4—4—4–.4–!4—4—R4–u4–•4š·4 W4V·&7L’:·B8ý+8ý!7:ˆ¡t6ªfß4 ð4–24–4–)4—4–L4–64˜4–ç4–a4–™4–84–24˜?4—®9¦€E8ýè:…ˆ6ªÇª4¢4–4–p4–'4–4–­4–“4—4—‘4–ñ4—¨4–f4–n4—–4–Õ4¡w8ñ׈8û4è6Á-444˜V4–4–b4–£4–Y4—"4–æ4˜Œ4˜)4˜>4—“4˜Ë4–Š4–±4–Â4—&82‚:ƒÂ6Vúÿ4f”4–q4–4–4—-4–Ù4–à4–«4™74›”4š4˜è4š_4–ˆ4–›4–‚4–|7¨T6Àî4M'4šÖ4– 4–04–A4—ž4¢·4–æ4–I4—}4˜z4±þ4–g4—64˜p4™^4—í4–M5î|)5@´T4¹ê4–&4–.4—A4–Ú4—ë4¢Û4—J4›4›W4¢´4­ï4™h4R4™Ñ4—*4—Æ4—€5gw5:@/4²í4–A4–‚4–¹4˜4˜*4t4¡4˜û4œ¾4¯84« 4ÁÓ4š34–œ4œ4š4˜™43ºž56ø4±H4–34—_4–„4—á4—°4š“4› 4¢"4¢4Û4ʤ4¶‘4›„4š14™²4›*4šÅ4”¨53÷š4±O4–Y4–Ø4™¬4—L4›¥4˜È4œd4Ìì4ô4¡i4æ\4׎4¤†4 4Ÿ!4šq4—»4™/53òÄ4°Ë4–p4—/4—G4˜À4˜Z4˜ª4ŸI4¯ˆ4ð;4¸a4õ4çö4ºM4žA4¢¤44š”4—;55í4±Y4—>4–¸4–`4—{4˜4šQ4›ê4\4׃4Åk4ܹ4Ùj4Ÿ4ž}4Ÿ%4™4™Ô4E#5:Þ4²Z4–<4–V4–•4–^4—®4–£4™G4¦&4™D4±‚4ºS4v4¢4¢Ä4–Ð4—(4—<4.Æ’5@0%4´V4–ˆ4–P4––4— 4–¿4—[4—x4˜Ó4—K4¶³4§P4ø4˜`4›É4–×4—®4–¥4õ±76,Öè4!74˜Š4—à4–h4–„4–ß4–-4–ê4–u4ªý4›ê4—Œ4—14—ö4£Q4™Â4™ 4—C5ÞžÝ8½— 5/Š·4²°4–44–G4–v4–d4–l4–84˜¿4›â4›Ô4œ4—H4—‹4™ü4–ë4–Ë4–¸7d¤8úÝZ6mK4}I4˜˜4–(4–64–,4–™4–[4˜Œ4—S4™Ø4—4˜“4–U4˜­4–ƒ4–g4šM8›[8üò8¾Ž&53óá4óÞ4 4934[à4t4‚4ŒL4’”4–Ø4–ü4”4Œ‡4ƒ4vR4^¡4Lq8äÌ«8ý8ý8ý8ý8ý:‡9Ô;ÉP6çë_5ƒþº5|!¯5v 5rÜT5rÉÆ5u  5{«5ƒæÂ:R„4:·µ:Žä…9‚¶8ý8ý8ý8ý!ƒ:ˆ¡·6­36@ŽŠ4P4žD4œñ4ž4œ4œ_4œe4œ§4ŸD7Eñè6dŒ :U¯ƒ:bÿ8ý8ý8ý!‘:þD7ûá4æý4«ß4–4–!4–É4–æ4–ƒ4–4–‚4–›4–y4©­4µw7”½:ÀH8ý8ý!‡:þ^a7š4gM4–ç4–¯4–=4–e4–®4–}4–©4—@4—A4–æ4— 4–…4–44ey9†õL8ýZ:ˆŒÇ6ÕLä4S4–u4–î4–¨4–,4–™4˜´4—X4— 4šd4šO4–”4–ã4–ü4–l4œ8•8û,w6">ý4¡Ç4–?4–n4–³4–f4—Z4˜4—?4˜ 4˜”4Ÿ14œ84–·4—!4—Ž4–V4—{7Wì!:þWµ6¾$4=g4–F4–°4–Ï4–ƒ4˜S4˜4—4¡n4žƒ4’44šE4™a4—,4–D4–Ø5ÈG°6‚›4;ä{4š*4–:4–‡4—^4—X4—44–’4™-4¯å4›C4¦}4™†4¤Y4˜?4–Q4–è4–œ4N?254–54–u4—×4—„4—³4—ø4ž'4žC4—_4˜¶4—4—4–æ4–Ö6áM›6Æ,_4;<Þ4™Ž4–Š4—4–4–š4—!4˜04›Ž4œ#4ª\4¤ 4™ð4›º4™)4—,4——4—Y4Ò4…5HÁº4o4–¸4–S4–Ã4— 4–¹4–í4™¢4™²4›}4¨…4  4§ 4œb4š]4–©4—Â4—È4Î5:D4³4—u4—4–â4—ø4—ÿ4›ˆ4›œ4¡.4¡q4Ñž4Äe4»n4¤¸4˜Y4—+4˜¦4˜ 4œ"53ø4±4—á4—4–þ4˜{4—Ÿ4y4ý4¶ç4¿×4üÔ4µ$4æ4¯ò4&4¦4ŸÝ4›j4™)5/¾Ü4°4–z4–k4—Û4—ù4Ÿ¤4™Î4³4ô‚4w4 …S4'ç48ÿ4ן4­64œ¹4£d4™é4˜B5-–Ð4¯74–E4—å4˜}4žÛ4Ÿ…4›{4Ë@4ß³4 Æ£7pq¨8!l•4 Q4¥m4¾o4¦Ð4ç4šA4˜5-’\4¯R4–š4–æ4–þ4™ˆ4›!4ª•4ÙO4ng4Ïõ7Æä8Ò‰Ô4 ’474½4·˜4¥4š×4˜V5/“ñ4°¬4–ü4—R4˜4˜X4›34›ï4¾4Ç=4«Ó4 aâ4 PÈ4vÆ4ðc4ÁA4›e4žA4˜ 4˜£53³Á4±O4˜Œ4—r4–¯4—ç4™Z4¡n4£E4¤-4ç 4e4î4ú4µw4™‰4–ö4—ù4™ä4™ï59ïÙ4³4–:4–4–¡4˜œ4›½4£î4™94¦Ö4²Ê4Ìf4ÛF4·Ê4œ%4¢²4—f4—Y4–­4šü5BSí4´…4– 4—z4—F4—¯4ž4¢h4—&4Ÿ4¢(4¡Ô4 Þ4¡ã4šø4—J4™a4—û4–Æ4¥w6éJ4^%4˜94–84–4–à4—‹4˜U4™¹4›Ì4Ÿb4Ÿº4«4 !4™4™;4–»4–ñ4—4ÀÃ8¾¦‰53 ñ4°64–(4–›4—ƒ4–¿4—@4˜ì4—ê4žž4›4šz4›_4™#4šI4–”4–W4—n6ÌH¿8üº·8º‰¹5-*y4?4,B4a 4ƒ›4–”4–4–o4˜f4—r4™µ4–å4–L4—84•î4…|4gž8EË¡8ý8ý8ý8ý!±8ý ¾:‡Ý¡6’ÇÀ5@B—5I05y/¾5rê55o¥ø5o‘—5riÒ5x€n5€;5F_:S=):bÖ9×í8ý8ý8ý!t:‰À:Œ_Ç6­Ç`4Ó¤4›Ý4œç4œÌ4œ£4œA4œM4œ~4œ¡4q4a7CF:Rˆ$:bΈ8ý8ý:ˆc¿6¸ Ä5ãú°4 JÇ4–k4–$4–94—"4–¢4–»4—q4–f4–R4–ˆ4–p4¡7'yj:ãx8ý8û#æ6*´o4ºà4£Ž4–24–4–&4–­4—»4—4—‚4—:4–•4˜Y4–b4–(4—94{9p†s8ý%t:ˆµË6YÑÒ4ú„4—74–'4–84–À4–N4–¾4™4—O4™=4— 4˜¬4–Ä4–v4—“4˜±8VÎ:ˆNƒ6¬ÏŠ4"¾E4——4–N4–,4–q4–€4–i4™î4ž 4™€4ž74—ß4šˆ4–ô4–ß4–Õ4–d6ßšƒ6,-4µµ4–­4–>4—4–\4–¾4–õ4—Ö4›z4¤È4Ÿ54ª¶4š4™“4—Ñ4—`4—r4—34Ï Ô5BÔÙ4´œ4–!4–Z4—¼4–V4–u4Ÿ 4š 4™ê4£*4§)4£¸4œà4¢94—¥4š.4—U4—®4Âi5:<å4³-4–]4–R4–à4—)4˜Ü4—ä4Ÿ4 4ÅS4Ó4ÈÄ4¬â4£Ñ4˜ƒ4Ÿ4˜4—œ4™“53ó¹4°ù4—|4–¨4—B4™º4›Ö4™k4¦4ª4Ê4®i4ð‘4G4«Ÿ4Ÿ[4–Ù4˜+4˜4˜5/ºu4°ò4—4—º4™4š4©×4­m4¦=4Ù‘4t¯4–b4 ;m4ÍÊ454¬Ü4Ÿ¹4 b4˜@4˜P5-‘Ô4¯L4–s4—Œ4—G4˜ð4ž 4›ã4³4·Á4z8D!8´¿[4†4ž¯4ßæ4®ù4¢ó4™ò4›s5-4¯24–P4—’4™"4™Õ4ši4š«4¸ò4z«4“8BèÂ9Œ\¶4 Ìì4½^4ÂY4¤Ä4¥U4 4—ý5/“4¯Ô4–¶4—Ÿ4˜ù4—š4žâ4¦Å4¬_44¨4 Åä4 ,f4Îü4ãL4£Î4¨,4®44šÄ4˜053­Ô4°û4–²4–^4˜4š¾4šÿ4—z4žô4±þ4 à4á™4ï(4 Z4«ö4¶%4œè4›§4—Ë4–Ì59é+4²º4–l4–I4—4˜Y4˜4–á4¤ÿ4¡¼4Ÿq4Ûõ4Û*4­v4§‹4¢84–å4™–4—º4—?5BTš4´‡4—4–4–g4–Æ4–¼4—74šÉ4š4›ÿ4¦4®Ÿ4¥R4£{4œ*4–´4—â4–ý4™­5Lܦ4°4–ˆ4–Ð4–e4–<4–ë4–Ç4˜á4™É4¡e4¢s4¨4 W4™n4˜È4—ˆ4—¡4–Ã4¾ä8»k526j4°$4–W4–54–p4–³4–Y4—´4—w4›b4›Ÿ4œx4™ª4—4™I4—¿4˜X4–Ò6ÊÐP8üµ¢8ºƒÒ5-%—404+©4`{4ƒ‹4•Ò4•ñ4–4—¼4—R4š„4–¢4—4—€4–t4†ù4fï8EÀ8ý8ý8ý8ýS8ý C:ˆ”ý:4–44–44–r4—4—®4˜„4–ò4—¼4˜l4˜G4–f4—4–È4š?8ú˜9©Ÿ6&³½4ì4–4–4–Z4—F4–Ç4—­4˜×4›4—Ž4´4š4š(4—4˜È4–›4–Û7Pkr:5Š 6^)Ã4é4–d4–24–|4—x4™=4—L4š;4¢C4˜›4£*4œ 4› 4—Š4˜4–´4–º5¼¸’6(¨Å4#±%4˜!4–‚4–†4–…4˜Ç4œ4—ë4šX4;4œ§4 á4›æ4—é4›B4™j4˜4—E4EcÎ54œÚ4˜ê4–¼4–Ñ4‰“51¼À4± 4–“4–i4˜ˆ4™ 4›z4¢54¡¤4ª‚4\F44@Ž4 ¿4Ôn4¥ú4Ÿù4™’4˜4¬,5/“ª4¯Ù4–Ö4–`4–ó4œ4ž14š4³Þ4‹4&š4°4 Þo4}·4æ™4Ɇ4˜#4š4š=4˜à5/4¯Ö4–×4—¯4˜4˜j4 Õ4¨ì4½²4 º4 ,4 FR4Ћ4ò¢4Ô]4¬ò4¢À4 X4š4—f51Þ4°.4–¤4–Õ4—4™Ž4ƒ4˜4¦µ4¤š4¹y4Ññ4§´4c¢4â—4¥34ŸÂ4Î4˜,4§ë55°è4±R4–b4–ø4–4™L4™ñ4š¥4›¦4¨4³4ô4*†4É4¥4˜g4˜(4—4——4Ø5;ês4´µ4–ß4–¡4–t4—Õ4—w4›«4˜4ž4Ð¥4k4Ã4©Æ4ç4™4˜Ñ4—|4—ì4$H5DDÃ4±Ã4–¥4–54–™4—x4–^4Ÿ"4— 4žý4™–4¥b4 Ö4ž…4—o4œ 4š˜4—ã4–§4<Ü8’Ãz5 Q4²b4–54–-4–Œ4–`4—F4˜ü4™Q4œ4ž4£ 4œ4—ë4—-4—”4–Ý4–w5¬U8ø³5AZv4±4–#4–‚4–”4–H4–U4šE4˜4˜°4™|4™³4š;4–Ü4–Z4—g4–Æ4—7@®/8üî8¹æ5.ÌÅ4ÿ€4!Ü4Tb4vÖ4Š¿44“ó4•ï4˜|4—4–‘4“†484Œj4yB4\P8„*#8ý8ý8ý8ýû8ýo:ˆ¶}:‹@ô6‡Î*5‡Q³5€Ÿ}5{ù‡5x½|5x¶¯5{…w5€Ó5‡y:SÜ:^út:ŒFD9ë‘8ýl8ý­8ýc8ý#ñ:ˆ{a6³+w5âÚô4 4¬4 4¬4œª4œ–4œÂ4k4ž7E¸ 6ÞØ:TÂs:cÛÁ8ý8ý8ý#Æ:ˆn×6¬L 4¦4¢´4–?4–4—A4—^4–=4–ƒ4–÷4–U4–*4©24ª÷7i°:&ƒ8ýû8ý!g:ˆ^Q6ª,"4ŸÝ4–¿4–T4–<4–,4—ý4–ˆ4–44šˆ4–Þ4–4–B4–î4— 4„ì9¤Âê8ý`:ˆŽ¤6¬i4¡p4–J4–†4–œ4–°4–W4—n4–Ý4–_4›ˆ4—4–ç4–i4–D4–Á4¦Q8ë‚ß8û* 6&ž"4ªK4–}4–ð4–È4–¶4–£4–]4—Â4—|4˜4˜4˜r4–õ4–˜4–.4–[4™á8 …¾:‡èŽ6Ygç4ù²4–S4—4–È4–b4–É4–—4™…4˜.4œ<4™14Û4—M4˜O4–Ñ4–š4–Ÿ76(¦i4#HÒ4—ü4–4–E4–Ø4–³4™è4–Q4–Y4—¡4©4™k4˜+4–{4—4ž¥4—-4–ž5Ú.V5@Jª4´¯4–ª4–A4–04–Þ4–å4˜4—Ü4—°4›J4ªz4™^4Ÿâ4œc4—Ä4–½4—ä4–˜4ò#Ì5:T4²R4–-4–ã4–M4–Å4˜î4¡F4–ï4žå4 4¯4½4§ñ4™4 [4œ—4™4–Õ4/yÅ55Ýk4±N4–04–;4—d4—34š±4—Ð4ž4«â4¤c4Æ”4ùl4Åç4¦ä4žË4›J4˜ø4—.4u˜53³Ÿ4°Ç4–Œ4–Û4–½4˜ 4˜B4—C4Ÿß4²>4ç½4|Þ44öâ4ª±4¨¢4š¢4™G4˜Ý4—q53¯:4²}4–94–V4˜24— 4š4 j4¢Î4¯ 4þº4Ðÿ4ùÔ4ê¹4°}4§A4¦ˆ4 `4šÒ4—S55²l4±N4–44–L4˜4˜4™4š¥4b4ž§4«!4f$4ÀÈ4«4ší4ŸM4›‹4™e4˜¿4“59Ã4²Ä4—4–b4–I4—P4–ñ4–g4¤ç4Ÿ94˜Þ4À44²4¢4Ÿ*4—„4–J4˜†4—W4*¬\5?÷«4³ø4–%4–ß4—-4–4—|4œ4™®4–Î4 $4¡4œX4œ-4N4˜,4–‚4—'4––4â–k5Hÿº4¯4–$4–94–B4—4—}4–…4–P4˜¿4¤÷4˜é4—®4—44 ¿4—Ö4–?4–N4—5ɦr8¹15/_„4³"4—64–a4–·4— 4–l4–î4–ú4G4˜Ä4˜;4—Ô4¸4—4—4–ì4–À6øQ—8úÇ5F~l4®n4–#4–/4–¡4–F4–ˆ4—‚4–Œ4˜4—I4—±4—4›4–²4—È4–Ç4—8ÏQ8üïÇ8»&o52Q4ó¾4 4:m4]™4uJ4‚•4Œâ4’´4–ó4—§4”W4ŽÆ4ƒÇ4vþ4`%4MÀ8ÞÇ:8ý8ý8ý8ýü8ý F9G:ˆÌ:<¢Ø6‚ùx5‡D¶5ƒ¸ø5±5£>5ƒj›5‡œ:Bˆ::n;:‹4‚90×S8ý¿68ý58ýM8ýÂ8ý!·:‰Ÿ:>zz6™]l5çƒ4 î4­4L4ž`4(44w79 6#«ÿ:DÔ:<Ë´:‹Z8ý8ý8ý!ˆ:ˆmZ6³+5ê>4 ð>4¥ž4–™4–14–’4–ß4–'4–,4– 4ž4¯œ7oó6ûÐ:&Ç:8ýý8ýX:ˆ€¾6±;Ñ4¨g4¤L4–@4–ü4—c4–Y4–ˆ4–¯4–S4–:4–E4—s4–+4%Ë4F¤9Ûj]8ýÕ9|Ï6+ 74£4–:4–=4–¶4–Ž4–O4–ƒ4–Ö4–f4—k4–z4–4—þ4–(4–4½9^B¢8üäà:7-N6`ˆà4Ò4–X4–4–{4–š4–ÿ4—·4–ª4—4—\4——4–O4˜¸4–J4–U4 F8Ç8O9qè6)ðN4#׊4˜<4–‘4–i4–\4–á4—l4™'4—)4™]4—÷4˜à4—<4–R4–ò4–ò4—\8àä:6Ú]6^m\44–#4–K4—†4—<4–+4™-4šE4—4™‚4›#4šM4–24–'4˜¯4–N4–7GH66)˜;4#³b4—¶4–84–€4–v4—¯4–T4˜:4£©4–Á4š4˜a4¬ó4–A4–(4–-4–c4—w65»'5@0‡4´i4–»4–&4–¢4—4—4šð4—’4©©4™4³x4›Û4œ„44šœ4–|4—K4—4ôp5<4³4–ø4—þ4–‹4–Ñ4—í4–£4œ[4N4¤±4¹-4£Q4°­4™î4š4™u4œÙ4—â4Fa59îs4²\4–x4—ý4—24˜Ø4š˜4˜h4™ 4£Ð4ÇC4°G4ãj4±±4› 4˜q4Ÿç4™l4–«4˜õ59és4²a4–4–44—d4—^4˜34Ÿ¢4˜â4šB4¹4³ò4b4¯Ë4±.4¡ 4— 4˜04˜U4—e5;ék4²æ4–ß4–X4–U4–Ý4—54–k4—™4›µ4¢£4±U4¸Š4±Ì4—4õ4Ÿg4˜4™4è5?÷&4´4–¶4–Þ4––4–À4–­4–}4˜84q4œÎ4œ,4¬4™4™µ4œ]4–4–j4–C4ãá35F6G4®"4–…4–›4– 4–z4—4˜œ4˜U4–C4–á4¤p4¡4—¨4—M4Ð4—U4–{4–³6(£8‘ Ë5H4±ø4–+4–4–-4˜x4–×4–c4—õ4˜€4žÕ4™N4–…4˜g4—E4—4–á4—Ø7:¦8ùR=5?’‡4°„4–+4–D4–¢4–S4–4—Z4–4šŒ4—è4™D4—¬4˜½4˜4–v4–º4˜a8µ 8üÀ.8’¯E5!»4² 4–4– 4–94–Ð4–á4–T4š 4–×4˜[4—4˜4–o4–g4–U4œê8¾¦.8ý»8øø†5E`ž4Þs4íZ4H474UA4o#4‚E4”4—'4–x4‘d4„I4pö4Wò49Ð4=Ì9V6M8ý8ý8ý8ý8ýÊ8ý"Þ:ˆÌì:Œ=j:>*’6aíå5Gx¥5A…[5DÔ5J¿.:AO<::û·:ŠäÉ:ŒÊn9”®8ýÄ8ý8ý8ýO8ý©9Gô:ˆxY6´å%5é•…5ädÜ4÷ø4›Í4œ34›Ç4š878Î6%U§5èØ:T˜1:bzÛ9-¦8ý8ý8ýr9ü§:8#"6² 84ß]4£ 4¤4–è4–44–,4–D4–þ4ž!4°B4£7¿:B¥P:8z›8ý8ý9ûX:7›6¯á¨4ÇÎ4–!4–o4–74–+4–®4–Ÿ4–74–³4–W4—4–41<7zä:Hª8ýÉ8üùi:8Æ6¯ó=4Ö’4– 4–74–ú4–d4–F4˜û4–Á4–'4–w4–¿4–­4–A4–±4Îê9Øw]8ýˆ:ˆG]6¯ø4º‚4–±4–d4–34–]4–˜4–ï4—¢4–s4–™4˜4–:4–f4–o4–‘4Ðf9‡â8ûq6)v44–4–4–s4–B4–T4–‚4—¶4—ô4–‚4™-4—Ò4–r4–º4–Y4–æ4¥Æ9 9)_5MÿZ4µô4–£4–4–#4–Ž4– 4–½4—©4—Ì4–B4›¨4–þ4–§4˜4–@4—^4šÏ8bC9:7Yž6]34 4—I4–X4–C4—4–j44¡X4–°4›œ4—ƒ4™ 4—}4™v4˜‘4–Î4–ì7Id®6+”Å4#¼4˜q4–$4–—4–s4–:4–´4–¹4™è4–Œ4§©4˜ï4û4–×4˜ 4žH4—g4—Þ5Ü…l5DH4²4–s4–ƒ4—y4˜é4–Ä4–_4–Â4¤ 4¢q4š±4¡<4š¼4œÓ4–4–n4–ã4–¥4Dü‰5BR­4´4—·4–ˆ4–«4—P4˜ß4˜64˜Š4Ÿ)4™©4#4±¥4¤K4™ã4–©4–ø4—Ð4–ì4¡Å5BT$4´¶4–]4–m4–`4–‹4—¢4˜§4™«4—Þ4¨¢4¢S4Ä¢4œu4™µ4¡r4Ÿ%4™ü4—©4˜q5DQ¸4±[4–4—l4–o4–N4— 4–)4–<4˜44ž»4®54›•4˜54œ¥4–L4—4–Á44—¹4–R4˜ã4—$4š}4—:4—04–B4–4–T4§9 xb8üå³8º8¤51ØŠ4°‡4–+4–34–'4–½4–½4–B4˜m4–…4™34–º4–M4–©4––4–4ÈP9ƒ‹/8ý28ü¦L8”?N5"ü§4à84áa4þ4*4TŠ4tÈ4‹`4•Ó4•ö4Œ…4wL4X4.·4ø4nM9ÓŸ18ý8ý8ý8ý8ýÇ8ý î91¢:ˆý:Œ9Ó:>î:7ö½6ƒê…:AŠ :b3:_«š:‹C0:ŒÊè90ðÃ8ýz]8ýª8ý8ý8ý.8ý>8ý$:‰ ž:@¬¡6 Ð,5쾓5ë–¹5äYí4½79>%6 óJ6 5ít:CJ•:>C~:‹Ã«96õ8ý8ý8ý$8ý$:ˆ>6·.Ø5òz4 }V4¡ø4¤ï4¤o4–¯4Ÿà4­4¨04¡‘7816$*ã:T—÷:d<Þ8ý8ý8ý$O:ˆhY6³=B4°4¥)4–14–#4–ž4–¸4–94–54—ã4–-4–?4$Ü4®Ó7%:>Á¨8ýh8ý£:ˆ@x6³C«4­,4–K4–S4–.4–4–4˜¶4–ˆ4–4—n4–X4–4–4—4¥~:%n>8ý=94–U4–S4–Ž4–´4—¹4™å4™À4š 4žp4¡d4¯û4¡Ž4šs4›Q4™Þ4—V4—l4»™‚8’µ\5 \”4±î4— 4–J4–M4–õ4–W4–¨4–ä4œ@4£&4Ÿ,4—é4˜Ý4šÍ4–¦4—£4–¿5¬ þ8¹R£5.õc4´_4–C4– 4–"4–à4–Ç4—å4–f4˜X4¤¦4œ34—š4—™4˜—4˜V4–‰4–a6ù3ì8ùg€5?ªí4°‡4–4–F4—^4–N4—U4™E4˜4–d4œ 4˜L4—â4—é4—I4–{4–î4—^85²8û¸5H¬W4¯ƒ4–=4–74–*4—'4–I4— 4–£4–a4˜ƒ4— 4˜>4–[4˜<4–"4–4¦Ê9 ¤€8üº`8’ý±5&T|4´4–4–…4–)4–4–Ñ4–k4–ˆ4—Q4˜s4—?4–.4–t4–W4–4ÖÎ9”908ý¸8ùZT5If‘4¯—4–4–<4–¼4–94–.4–(4—f4—4—4–<4–@4–‰4–ß4–4G]9ð‹u8ý8üø|8»ï57i„4¿ 4¹4Ùu4 46Z4]C4wŽ4…È4…4xö4_ö4:Š474Ý4p:#º8ý8ý8ý8ý8ýü8ý18ý#ã9D9FE:ˆ‡Û:Œ"ì:B#:b $:‹@ :ŒWÀ90ÇL9ô8ýD8ýI8ý98ý8ý8ý8ý8ý#9!:ˆò:B¤k:9Ǧ6¦èD5ïv`5óVî6ÙÃ5ñ’E:CJ : :Ò:b¹k:‹Ë±90Oì8ýp¨8ý8ý8ý8ý$9âð:;¢36»5ôÆ;5ç•,4Q¯4£O4¢ú4§>4¢k7X6 cÅ6Bq:C8<:?šR9),u8ý8ý8ý59ë_::‘6¶¥k4Ó‡4£4¤4–²4˜€4–B4–4–a4$×4ª—4¦U7êv:@Šx:<„8ý/8ý9Õ::tº6´’C4Üì4–X4–4–64–:4—ç4–-4–4–¨4–Y4–54–4![7%çš:Q_G8ý28üòJ:;b¦6¶“h4Þ4–$4–æ4–4–ˆ4–4—ý4–„4–4—34–§4–!4–4–45E:0£Ü8ý9 è…64p4Ï4–¯4–4–4–94—K4–T4—4–Ý4–*4–æ4–¯4–z4–)4–4ˆ’9÷ $8üæ*8¾ºå53Œû4°m4–4–4–:4–4—4–´4–-4™34–x4—u4—:4–M4—"4–N4×@9ˆ({8üî¥:)6Nâ4-4–:4—J4–Í4–=4–o4—Ü4–V4—÷4—ß4——4–q4–34–s4–,4þ8ȉ~8úÝw6 ™4"Ž4˜r4–W4–š4–>4–W4–õ4™[4˜ï4–_4˜Ä4™š4—›4—i4—Î4–Å4—A8æ½9 ìY5Q:4³’4–#4–[4—)4–Z4–p4—ƒ4™¸4™ƒ4˜·4šß4—Ü4˜‘4—4—54–.4–_7HJý8¾Âf53cs4°Y4–*4–‰4–P4–4—+4–¯4š$4™I4o4™94š¬4—u4—4–Œ4—4–®6ÌÐÏ8»‹µ51÷®4°L4–U4–54–>4—Z4˜à4˜¶4—‡4™@4A4šÃ4œà4˜©4˜¡4—(4–³4–ÿ6Ë¢8øÌK5Av4±B4–¨4–|4–F4–v4–œ4—4˜;4˜†4›”4˜4˜š4˜ 4—¸4–‘4—u4–Ö7@œ8úÇ~5F}S4®™4–Â4–4–@4—4–©4–‡4–¨4—x4›i4™*4—U4–ª4˜µ4–‚4–Z4—©8ÔÎ8üÀ8’œ\5!·V4²¼4–d4–?4–O4–ø4–j4––4—c4—H4›_4—B4—œ4–Ô4–S4–«4žò8¾»ó8üåØ8ºZ51š 4°Ÿ4–X4–4–4–d4–y4–´4—X4–¼4—s4—34–b4–É4–Ÿ4–-4È9ƒ¥«8ýÌ8ùo5IoÀ4¯r4–4–64–·4–$4–E4–¶4–[4–Ë4—-4–ù4–`4––4–P4–64Of9ð£h8ý8üÉ$8”k.5&Lœ4¬À4– 4—•4–ˆ4–,4–.4–^4–û4–¥4–!4–¡4–L4–B4–=4*:-é8ýÞ8ý-8û6 8“Ï5$…W4·Ô4¹Ê4àÿ4 4;4UX4cÿ4bÿ4VÜ4>4R4å]4¼u4ãZ:Rå<8ý8ý8ý18ý_8ýš8ýÓ8ý58ý%J8ý%9þ9í‹9÷c9ºã9Ö9å(8ýÌz8ý8ýO8ýë8ýe8ý8ý-8ýY8ý˜8ýï8ý@(9F#9Bê:‰>:C6:=†‡:=©<:=¨g:=|:d=¶:‹þ/9-&¿9<·8ý8ýA8ý78ýX8ý’8ýß8ý>Ë9þ‹:>¹h:<­ô6©p:5ôÆd5ïªA5ìÉu5ì¹À5ïAÇ6ç):D¨°:"ï‹:dx9(Ì8ý2k8ýt8ý08ýä8ý‡9ï :<øY6¸Ž65ï‡Ë4 bæ4ä‡4ðö4÷j4÷_4ñc4çÌ7Ë6ãŠ:D #:?Ü©9% |8ý¶8ý-8ý-þ9ÓF:<-D6´P{4ïu4и4ëü4 ~4!v4,†4-4"Ÿ4 Ž4~4Ù7­–:AC:>úÚ8ýó8ýÇ9º :<æ 6´]ˆ4æ4¸§4ÞR4’48ó4S—4ao4`þ4Tö4;·4ã4â‰4c:7%aˆ:RNI8ýB8ýx8¿ªÆ6%Ú¤4ó4¸t4ÖÒ4 44ò4[³4v(4ƒÑ4„r4x4^48ƒ4‚4Û44µ:&BÏ8ýY8üÏ¡:!£6R̈424ß$4µ4([4ST4sæ4‰å4–¥4•¡4‹]4x4V4+Ö4²4v9Ùe8ý9 Bk6zM4!¿?4îè4Ð44Ø4S²4nG4ð4ŽÕ4—|4– 4H4ƒC4oÃ4V484@}9]A”8üò8¾©œ53¯,4óÈ4 T494[É4tc4ï4Œ4“u4–4–U4–4!4ƒl4v4^¶4H~8å4Ì8ýr8¹ú«5.—‘4þô4 ˜4S04w"4ŠL434”À4•¼4—c4—%4—,4“64 4‹…4xt4Yñ8†¶8üºš8ºŒõ5,Ûx44+°4`Q4ƒy4•84–D4š4–Í4š4—G4–m4—r4–)4–³4…¢4er8F*8üµ¹8º†Ë5,×Z4+4+‚4`'4„4–x4–£4˜'4–¤4šé4—b4™4˜ 4–H4–t4…À4e 8ESº8üît8¹ú+5.84ÿÆ4!{4SÏ4w4Šø4Ç4”z4•a4—Ê4–—4–S4”Ä4¤4Œ]4z¡4Z®8„ž8üïá8»F.52Œ4ôl4 f4:‰4]±4v4‚½4ŒR4“©4—4—¹4““4Œì4…¢4w4`4GÕ8ß5è8ýÐ8ù¶5Emø4Þ–4ìù4L474U4nÒ4ƒI4`4•û4™”444„‘4pÔ4W¼49û4>ç9V]½8ý68ü¦:8”$_5#†4àJ4áF4e4)ê4Ti4uL4ŠÏ4•³4– 4Œ4wb4Wî4.M44q§9Ó¯¸8ý!8üøÎ8»­E574¿ 4¹4Ùc4.46Ô4]C4wB4…}4…<4y4`4:Œ4¬4Ý4':"ëÀ8ýÞ8ýI8ûJ§8“Æå5$ƒ@4·Ú4¹~4à–4(4;*4U84c54cÙ4Vå4=Ñ44åQ4¼£4ìÁ:RÐz8ý!8ýl8ýÊ8ûp*8“êb5$D¦3ÿ@3ÿÞn4DŸ4…ô4±Ó4ȉ4ÈÍ4´v4Š€4K–3ÿìY4~:3Îû9 zã?•v?–3?–n?•·?”j?’&?d?Œ©?‰}?…ý?‚'?~?yŸ?tª?o>?j ?f?c?`¿?_?˜T?™+?™]?˜¯?—]?•?’f?e?‹Ö?‡Ç?ƒf?~Â?yí?t¢?n&?g?co?`M?]È?[æ?›´?œ†?œÝ?œE?›?—à?•N?’I?Ža?‰£?„¬?h?yô?t?lÀ?eq?`Ù?]_?Z¡?X‘?ž¢?Ÿµ? |?¡? 8?™"?˜0?•Œ?‘w?;?u?‡•?Q?zw?rÍ?gª?^Y?ZQ?Wy?TÓ?¡ ?¢¡?£Ä?¤±?£â??›÷?™@?”ñ?™?žk?—³??‰Z?€ò?m4?[‡?Và?U&?P?£¡?¥z?¦Ã?§+?¦>?¦¯?§Ê?¨ƒ?§á?­??¶|?°E?¬Å?¤_?–Ó?}á?g…?Yø?Qr?Lb?¦>?¨G?©Î?ªH?©Ÿ?²U?»0?–?Í¥?ËÏ?Ö?K¯¿?K8À?Js?¸µ?œ?‚[?eò?LÊ?JH?¨‹?ªÒ?¬š?­Z?¬ô?½_?Ì^?Ý–?ër?KÇÉ?L~;?L”n?LŠ5?Kß…?K1ì?5Y?›é?qØ?R3?K[?ªƒ?¬ø?®î?°?¯ì?È ?Þo?ôÍ?KÚÎ?L²?L¤‚?L”X?L‡?Lˆ?L~Q?J²X?Õ?†¶?]¦?P$?¬ ?®¦?°É?²2?²‹?ÐÚ?ð·?KÜ?L¸%?L´Æ?L©x?Lš?LŒ¤?L…Ÿ?L|T?Lp­?J±?ä?o˜?Ur?­?¯Á?±õ?³o?´?Ú³?n?L3´?L·ˆ?L³ñ?L¬Z?L¢?L‰Z?L‚û?L|†?Lt>?KQ‰?5ê?{>?[/?­”?°M?²v?³Î?´e?ß?Kà­?L½ô?L¸`?L³ö?L®Ø?LŸß?LŠ?L„¯?L±?LqR?L]>?Jšõ??h%?­ˆ?°?L‹b?Ly?Lj§?Kc?!‡û?d?]P?¬ ?®Œ?°‰?±·?±ì?ÐW?÷?L1K?L½Ä?L°?L¢©?Lšì?L‘0?L‡?L}°?Li?KQ??É?tv?VÃ?ªp?¬ñ?®Ü?¯¶?¯N?Çš?ß"?"9ù?L8?¬ ?®¡?°?±˜?±Z?®y?¬6?K½?L„t?L¡¼?LœC?L¥?L®?KìA?Lf ?KÁ“?K…?ø:?O¡?A“?®Ü?±¼?´?µ­?µÖ?²C?KÆ?L·6?L­w?LÅ&?L¹Õ?L§”?LžU?L’¹?Lë?KÓÕ?LHƒ?J—b?óq?IÜ?±+?´c?·)?¹#?¸{?µß?L%\?LºË?LÓã?LÅD?Lº6?Lª;?Lž2?L˜:?L‰i?LzÞ?KÆ?K( ?`?G?²ø?¶s?¹Ž?¼/?¼»?KÓ$?L½é?L×#?LÓt?LÍÛ?LÀ€?Lª?LšX?L–~?Lˆo?L|´?Lj?K·E?JyA?Ùò?´ ?·È?ºö?½^?¾?L.³?LÈ?L×"?L΂?LË?LÂ+?L¯’?L›?L‘¬?Lˆn?L~,?Lm?LÀ?LŒH?L‚Ë?Ly?L^¿?LZU?LS†?Jë¼?º_?¿^?Ãs?Ér?L=Ÿ?Læ·?Léu?Lç7?Lä ?LÏ5?L¹Š?L´Ò?L£Ì?L*?LÅ?Luú?Leâ?LaÃ?LTÒ?JàI?¸s?½™?Â)?Áe?L+ë?LÈ“?Lè ?Lâ;?LÚm?LÌ"?L¾²?L°Á?LŸ]?L‘k?L†?L~?Lcà?L_¹?K¾o?KAœ?¶6?»X?Á?La®?J D?»g?¹^?½i?Á³?Kï·?Íò?Ôf?LR?Mž?Mè?M s?Mµ?Mþ?Lü0?Lè¡?LÑ?LºÓ?L¦B?LˆG?Lhî?L[2?LH?LB³?L8?Ký?Ï;?ÖŠ?L£Î?M `?M T?M£?M ª?M \?Lú›?Lé¦?LÙ†?L¼œ?L£Ã?L€5?Loi?LW,?LHE?LC?L-"?L&?Ïá?Ök?L£Ÿ?Mñ?M[?MÌ?M ·?M?L÷#?Lè?LÛe?L¿?L¡ì?L€‡?Ls ?LX ?LHW?L??L0Œ?LÖ?ε?ÔÞ?LWŒ?M?M2?MÌ?M˜?M;?LüÕ?Lèr?LÚì?L¹É?L¡‰?L€þ?Llx?La?LJŽ?LBs?L6Û?Kúc?ÌÓ?Ó2?LL×?M%?M {?M O?Lý—?Lÿs?Lú…?Lå¨?LÑñ?LÁ=?L™p?L‡Æ?Lk*?Lb?LSF?LB?L9&?KðO?Ê"?Ò2?LFí?Lú¸?MJ?Lþö?Lÿ%?M?Lí ?LÚš?LÐö?L¼Æ?LŸ?L‹¸?Lp›?Lb®?LVå?LHÆ?LAù?KÓo?ÆË?з?!ð¸?LQ ?LúL?Lùƒ?Lúû?L÷Â?Láë?L×/?LϦ?L¶_?L¢,?L’}?LmÌ?Lgä?L[?LU¡?LK¥?K›1?ÁT?ÇÕ?Î%?L5G?L÷?L÷Û?Lé:?Lê#?Lâ ?L×ê?LÅÏ?L­q?L¡Ò?L”½?L{[?Lo8?Laî?LZI?LW ?Kj?»x?À?Ã@?!º?LK¬?Lï¡?Là?Lãª?LÝt?LÎ?LÅw?Lª?L¡8?L“«?Lƒ÷?Lt¨?LhK?LaV?Lcr?J°B?µº?¹&?»ú?Áû?!n;?L<¨?Là ?Lßø?LÖ?LƬ?L¾Ì?L¬Ã?L¢£?L”P?L…L?L|?Lk7?Lhë?JÚÓ?!/ ?¤q?¤²?¥R?¥µ?¥t?§ô?©‚?©’?¦E?­?µ‚?°þ?­?¢”?”-?D?gþ?[È?P¹?N*?©Q?ª½?«Ø?¬n?¬g?«?¨?££?œ?Kª?Lq£?Li¼?L]-?LVJ?K,/?†?\?M–?J±?Eò?¯$?±^?³ ?³ô?´t?±°?KÀÑ?L¨?L¤ ?LÀ_?L¹Ã?L¯4?L¦)?L¡?L‚>?K×Ý?K¡v?K ?÷\?J*?µ?M½?M$?MÜ?MÇ?Mo?M?Lü2?Lå?LÇ€?LK?L|”?LZ¯?LN[?L6ú?L+Ø?L!w?L?ï—?LJ¤?MÚ?M x?M ?Mg?Mà?M.?M?L÷b?LÜÂ?LÆã?Lž*?L‹?La3?LTq?L:ï?L1F?L(K?L,?ßN?" Î?L¬I?M p?MÄ?M æ?Mú?M•?Lõ?Lî^?LÜV?L¾ÿ?L¡\?L…Ð?Lk]?LT?L@_?LB~?L05?Kíñ?ÒŒ?Û’?LS?Mf?MD?M1?M®?Mã?Líc?LäË?LÚ?Ka@?»(?¾ù?ÆJ?!aj?LF ?LìÈ?Lê‰?Lì?LÕÄ?LÌÙ?LÄÌ?Lµ?L›k?LŽØ?L‡?Lv÷?La¢?Laæ?L_{?Jµõ?¨ñ?¦G?§Ô?¨ý?©3?³Œ?½µ?Æ'?Æ?ÍP?Ðä?K± ?L?´?šð?ƒu?iy?Mü?JÜ?¬á?® ?¯†?°«?±%?¯Ö?®;?K½•?L„Í?L??L‘ù?L«Ô?L£!?Kîá?Lb$?K¿Ë?K »?ýn?Q…?AÅ?²í?µ?·?¹&?¹?KËÞ?L¾K?L×Í?LÏä?LÁ5?L¸Ç?L±ì?L¥í?LŽ?L;?Ly‹?Lq#?KÃ8?J‡³?Û‚?¹¬?½}?À1?Á?KÚÚ?Lï…?Lá·?LÞv?LÛC?LÍQ?L¼ß?L²é?L¦N?L“ž?L‚Z?Lqe?Lp£?L[¿?LWQ?J$?ÀÓ?ÆM?Èz?KâD?Lõ¯?Lú¥?Lèr?Læ?Lçm?LÙ)?L¿?Lµ„?L©Q?L?Lt™?Ll?Lf–?LX?LK÷?KgL?ÇÎ?Îø?Kçb?Lÿœ?Lû+?Lúþ?L÷I?Ló"?Lñ?Lâ?LÅî?L¹r?Lª?L!?Lp+?Laè?LVz?LU2?LCT?K‚?Íê?Òÿ?L›?Mñ?M ?LüÓ?Mn?Mã?Løw?L&–?L?L'?L ö?L?"´Ï?L²¼?M#?M0?M%ê?M/„?M3;?M4V?M#ˆ?M™?Løù?L×µ?Lž?Lk?LH¦?L1o?L ?L–?L O?L #?4œ?L²8?Mi?MÙ?Mê?M/»?M&N?M'w?M“?M ä?Lî6?Lͯ?L¡­?Lqª?LS÷?L7Ý?L(ñ?L®?L?L?^?L^Ù?MË?M?Mx?M0?M!)?MŸ?M D?M‘?Lè;?LÇ›?Lžq?L}?L\*?L@Þ?L4X?L-?L%O?Køé?âm?LJš?M 6?M?Mq?M?M«?MŽ?M¾?Lî@?LÝ ?LÀ‰?L£z?Lƒ\?Lc£?L[>?L2¤?L6@?L0¸?Kß ?Î?!ÍÈ?L¢]?M é?M ÿ?M=?M0?M¥?Lüâ?LãÄ?LÙF?L±á?L¢?L‰?Lkm?Lcó?LC°?LAí?LA?K½‚?Ç?Ñ“?L@á?Lÿ[?M4?Lÿ?Lþ³?L÷§?Lèè?Lß?LÑ,?L³Ç?L¢•?L…I?L|:?Li×?LS`?LMî?LPÄ?KŽÐ?Àä?Ä«?!wt?LL4?LöÆ?Lò#?Lói?Lñ"?LÞK?LÖH?LÉ?LµÄ?L™?LŒ®?L‡E?Ll ?L\2?L[Ë?L]‡?Km?ª¼?©™?ªí?¬C?¬½?¾Ú?Îö?ß?åÀ?Kİ?Lyƒ?L—Ä?L‘?KÞ¯?K+j?6¯?Ÿ®?u?SD?KV?¯®?±l?³7?´Å?µ“?´ª?KÉK?L³O?L®­?L¼?Lµ"?L®Í?L£Y?L“°?LŒ1?KÐI?LMå?J›Ì?ò ?GØ?¶[?¹Š?»ÿ?¾;?½?L/ú?Læ…?LÚ3?LÕw?LÅ\?LºE?L·¹?L¢_?L“0?L„Q?Lzè?Loç?L_y?J¯?ÜN?½ð?»?Å?ÅA?L=Û?Lù•?Lä?Lâ©?LàY?LØ?Lºñ?L´Ø?L¤¸?L“z?Ly?Lpñ?Ljµ?L]?LM¸?JÖ‘?ÆO?ν?ʵ?LCg?Lüq?M½?Lï¼?Lëk?Lëâ?Lçn?L»þ?L´}?L©¦?LŠú?LsŒ?Lh1?L^Ò?LQ?LDù?Kš+?Ò ?Ù?LFõ?Me?M¤?M?M“?Lü?LõÉ?LîÂ?LÆœ?Lº{?L©Ò?L‰;?Loð?LW¯?LOÉ?LJP?L7í?K¼x?ßÔ?KñO?M V?M¦?M Ä?MÑ?M™?MÞ?M7?LõD?L×V?LÅ6?L¥ˆ?LŽu?Lc˜?LN?L>Ã?L3´?L,%?KÚÚ?³?L¢„?M¡?M¹?Mf?MÅ?M6?M?M¦?M”?Lí'?LÍ0?L¦Ð?L„‚?LK|?L81?L,P?L ,?L„?Kô0???L©†?MŽ?M ê?M' ?M.C?M(þ?M*˜?M"ø?MC?MÞ?LÊÓ?L¦º?Lƒ?L?ö?L%x?L?L?L Ô?L?L5R?M??M3?M,Ô?M+^?M5?M?z?M5?M@4?M8Æ?MŽ?Lʃ?LŸ©?Ls?L>‰?LÒ?Lˆ?L{?Kø?Ln?L \?M¤?M?M.u?M=?M9…?MI ?MC@?MR?MDç?M.Œ?LçW?L—Õ?Lgƒ?L#?L?Kô ?KðÑ?Këî?KþE?LñŸ?Mª?Mñ?M1'?MF-?M=W?MM?MO?MX?MJ?M>Ã?LÛR?L›ƒ?L\Ä?LÖ?L ?Kè‚?Kì?Kê?Kú?Lð}?MG?M æ?M,x?M@¾?MCß?MKá?MX×?MM(?MFô?M(`?LòC?L«?LG*?L)X?Kù?Kì)?Kïþ?Kô¢?KôT?L¡Ò?M ¶?M(…?M%F?M5,?MFZ?MG€?MW=?M1Ý?MAÔ?MY?Lî2?L¿û?LDÅ?L+s?L'?K÷?Kùè?Kø³?Kù@?L†ñ?M!š?M!û?M??M.?L‚½?Ly#?J¶å?n?‰ã?^÷?PP?²?´y?¶ª?¸µ?¸]?¹?L.,?L²æ?LÎÐ?L¿é?L·?L°7?L¦`?L‘4?L‡„?Lz(?KÊ­?K-?ÿ?DŸ?¹7?½$?Àd?Âî?KÛa?LÇ2?Lè™?Lâ•?LÚÇ?LǪ?Lº„?Lµ8?L©Ž?L.?L~^?Lwd?Ln>?L]Â?K­ú?Jœ&?Á~?Ç¢?É¢?Kç’?LõÑ?Lö§?Lì·?Lê…?Lå7?LÚ1?LÀ‡?L´?L§u?Lö?LuA?Ll/?Lf?LTh?LLœ?K¨?Ê”?ÓÎ?Kí ?Lùq?M?Mý?Løµ?Lùu?LïÐ?LåE?LÍÿ?L°´?L¢Æ?L£?Lpÿ?L_L?L[L?LF™?L@?KÓ«?Þ?Ú1?L¢Ú?Mc?M E?M.?M;?MÛ?Lþ!?LòR?LÓE?L½A?L è?LŠÇ?Lií?LN€?LKþ?L8ä?L12?KõË?ýã?LS?M ?M‰?MÇ?M"&?MJ?Mý?M «?LÿÈ?Là3?LÎ?LžX?LT?L\?LC¨?L4ä?L'Ä?L!¡?L?®?L< ?KòR?Ìo?ÓK?LDí?Mn?M…?M5?LúE?LúÚ?Lõ¸?LÛ?LÏ?LÀŒ?L›R?LŒ?Lup?Lb?LS?LSÔ?LD&?KÓ ?­5?®Ý?°?Me?M ?Mµ?M¦?MÍ?MÃ?M ?M'M?M;?Lål?Lº†?LUö?L@?LÖ?KÿÐ?L_?Kü?LJ?Lôp?MS?M#ï?M0k?MBŽ?MGz?MUã?M^4?MI?M\Ÿ?Mgä?MxN?Mˆ ?M…H?Mk5?M+6?L§Y?L:&?K¼¼?K¾Ÿ?KÓ?KÄ\?KΫ?Kâ$?M/?M)O?M5?MJ“?MPg?Me.?MxJ?M“§?M¥«?MIJ?M¤…?MW6?L›G?L)?K…Ä?KŽÒ?K«”?K©|?K¼N?KÒô?M??M/d?M5ƒ?MK-?M_?Mbº?Mˆ%?Mš“?M»+?Mú?MÇÓ?M~Ç?L¯?KÊ ?KXâ?Kk{?K•›?K•v?K²Û?KÑß?M'?M,‘?M;s?MI£?MX:?Ml¶?MC?Mž?M¼6?Mî¤?MÕ–?M‘?L¶?KºØ?Kr'?K[ø?K?K˜"?K¹f?KÉ(?MF?M&i?M<ö?M@N?MP?Mi?Mzc?M’ƒ?M¨Œ?M¿”?M©·?MCï?L¯à?L ˆ?KŒƒ?KO?K -?K¦L?Kȼ?KÒ?Lÿ ?M#L?M4ï?M;•?MK`?MZ4?Mm?MvÞ?M‰O?Mœ€?M^;?M, ?L±Â?L$Í?KÙ=?Kº!?K­¡?KÇ?KÙe?Kãž?Lïô?M Ä?M0?M4?M?ö?MJÇ?MSï?MXý?MbÒ?MV?M/ ?M '?L §?LGÇ?L ?Kõ˜?KÔM?KéJ?Kê?KùŸ?L‹½?Mq?M(?M1ê?M+X?M6À?MA˜?M8s?ME?M-?M"M?LÎò?L›?Li¨?LC{?L*I?Kû®?La?Kþq?LO?"_Ã?L§Ò?M?M"4?M%?M'„?M)¾?M$?M4?Mx?Lø?LËþ?L¡d?LpË?LWD?L>?Lø?Lk?Lö?Lø?ôƒ?LLƒ?M?M|?M?M0?M¨?Mº?M?Lü??LÝ?Lľ?Lœ?L„Š?L`ù?LXX?L5»?L4a?L4å?L?Ò'?!¤·?L¡I?M —?M K?M Ê?LÿÙ?Lÿë?Løe?Lä|?LÑ?L¾U?L¢?Lˆ?Lm·?LdM?LOS?LEæ?L@?Kò!?®?°3?²?³¨?´Â?ÜU?^?L8Í?L·ª?L°“?L¤c?L›+?L”E?Lƒ,?Lz7?Loü?KM?9?€f?]e?´Â?¸+?»3?½ó?¿;?L7?L¿t?LØ??LÏp?LËÎ?LÀÉ?Lª?LŸd?Lè?L‡w?L ?LjÊ?K¸&?K}?ò´?»×?Á?ŧ?ÇV?L@?Løx?LëÑ?Lß/?LÜ"?LÕã?LÃD?L²0?L¡¼?L&?L€D?LqÄ?Ld–?L]G?LOœ?JëÙ?Ä~?Ë?Îõ?LOE?Lý­?MU?Lô§?LíÛ?LëŽ?LÝ}?LÉÇ?L¹½?L¥?LŒQ?Lv¨?LfÒ?LYy?LMó?LEº?KÍæ?ÎÈ?Ôk?LO7?L9)?L–?Ž?LQç?M ?M¦?M?MÎ?M?M??M?LùÜ?LèÊ?LÉt?Lªñ?Lxß?LS¿?LD¤?L´?ML°?M[?MuW?M±?M«‡?MÔâ?N(©?N Ò?Mߌ?L£-?K”?KDâ?K(³?K|>?K†®?K¡´?KÆN?M e?M;M?M8?MO^?MfÜ?My{?M¥õ?M»@?Mì³?Na6?N‘?N^†?L¹Æ?K¹?J½“?Jò'?KHÎ?Ktý?K»?K¿F?M š?M2Ø?MB?MQ?Mc?M€+?M¡/?MÃM?Móû?NR#?N‡ü?N¥Ã?Lóÿ?JÕ)?J¸ö?JØœ?KKM?Kv{?KžÝ?K¹’?M¡?M-`?M@D?MI ?M_Þ?Mv~?M—?M¸U?M×"?Nˆ?N5?MØ©?LÕ??K\+?K~?K'ò?Kl?K†±?K«s?KÂR?M$?M):?M:f?MBQ?MXà?Mpc?M{‹?MŒ{?M¯4?M¶|?M ;?MT4?LÀj?L ?K–Ù?K“Ë?Kz3?K­?KÆÀ?KÙè?LúB?M#è?M.?M;»?MO=?MT¸?Md?Mlâ?Mm"?M|Õ?MJî?MÊ?L½‘?L+“?Kù4?Kãª?K¨À?KÓì?KÜÎ?K÷c?Lå ?Mº?M&ƒ?M4Q?M?h?M?Lø?LôÃ?Lïc?Lä?LË`?L¶Â?L Ó?LŠ.?L{:?L]?LZ&?LUb?L??KÏ©?ÏŒ?×>?L¤,?M k?M ?M‰?M A?M ¢?Lø:?LðÍ?LÝ]?L»?L§\?L€|?LnX?LO®?LJÆ?LEz?L0ê?Lá?Â?LRÀ?MZ?Ml?MR?M}?M—?Mß?ML?M 6?Lî±?LÂê?L§?LrR?L_?L8 ?L8j?L*µ?L"Ó?L?L€"?MÇ?MÒ?M$Y?M/?M%–?M4n?M6h?M*›?M(É?M Ù?LÑ?L¦{?L`Ò?LEu?L$Â?LŽ?Là?LH?L f?Lð?M}?M%ï?M0 ?M@é?M:‘?MLK?MWa?MH~?MGm?M:c?Lìû?L¡?LMî?Lë?KíY?Kø?Kîw?KùÂ?Kù¿?Lþo?M,®?Kpí?K™e?KÉ’?MF?M5d?MCî?MXH?Mfq?M†ý?M«¥?MÐC?N1 ?N·Ä?OK“?PÌî?M$B?H¢—?I¿?Jž0?K º?KOf?KŒÊ?KÀ?ML?M/W?MCà?MYï?Mi?M”?M¦?M×f?N3µ?N¥°?OX¬?Q,¯?MÍì?H•ß?I±?JˆŸ?K¹?KT?KŠ¿?K³ø?M æ?M,U?MAã?MQ¶?Me ?Mwk?M š?M½Ö?Nµ?Nc?N ?N¶4?L©»?JÖë?J™%?JèO?KMh?KoÚ?K’}?KÀ?Mw?M1?M;ì?MCn?M]+?MuF?M€“?MœC?MÊ?MçÉ?Må?M‰ ?L¶?KËË?KX9?Kkö?KoC?K•f?K¸y?KÑ'?Lþ¸?M-»?M0?M9‚?MPZ?MaØ?Me?M{î?M–?M‚?Mk?M2Õ?L°Ü?L&°?KÕÌ?K¼.?K¬l?KÈû?KÖv?KçÛ?LïI?M"J?M-B?M.?M:”?MI ?MM?MX‰?MYG?MJ ?M*h?Lû~?L§V?L[¾?Lñ?Kåá?Kà[?KñÕ?Kï?L9?LÒx?MÐ?M"?M%5?M(ñ?M/¼?M6n?M2²?M57?M€?M ?LØ®?L©Ž?LiA?L:&?L%æ?L¹?L?L ¦?Lï?"N~?L£¸?M ?M^?MQ?M ?M?MÅ?M†?Lþ‡?Lð¼?LÈh?L£2?LyÑ?LVÏ?LLƒ?L0É?L+ ?LÕ?L?à/?" ¥?L§L?M ‘?Mò?Mr?Mç?M¹?Lùh?Lí?LÜ_?L¿³?L§Ø?L}²?Lnv?LZç?LE…?LBž?L5-?Lá?®Ë?°ã?²?²q?²Q?Ùû?"NÖ?L@??Lº?Lµp?L¤ ?LŸ›?LŠ?L‚ ?L}5?Ln ?K]­?!Šé?¡?_3?¶P?¹@?ºÐ?¼ ?¼-?L+v?LÇ„?LÚ?LÍM?LË·?LÀX?L¯??L g?L‘?L‡Û?L{„?LlX?K¸L?Kª?ô÷?½O?Áñ?Åw?"õ?LKW?LìV?Lñm?LãÍ?LØû?LÖ'?LË/?L¬Æ?L z?L“€?L|í?Ln\?Lg‘?L]x?LQÕ?Jð?Å„?Ì?" r?LZ“?Lÿ”?Lût?Lüç?Lð„?Lè/?LáÞ?LÎO?L±S?L¡—?LŽŸ?LuÄ?La´?L_£?LT ?LAë?KÊù?ÎË?×!?L\¢?M ?M Ã?Mï?M*?M?LùÑ?Lî^?L×:?L·Ì?L§?Lˆ?LmN?L(ð?L'ç?LÐ?"µ·?L¯?MH?M ·?M&Í?M*²?M2?M3/?M>?M#&?MÑ?LÈJ?LªÈ?Lm¯?LGr?L(^?L!??LC?Lõ?Lr?L ä?M­?M$¶?M-«?M7–?M>%?MO:?MUD?M??M>:?M&3?Lå~?L§°?LSn?L!€?L u?LÁ?Kó†?Kù™?L{?LøË?M&9?M2Á?M:©?M?‡?MVD?Mp ?Mp²?Mj?Mq\?Mr?LðD?L¨?LF|?Kê¨?KÉØ?KÑ?KÖ{?KÛ?KóV?Mø?M,}?M;ä?MGž?MK{?MlÎ?M„»?M‘ä?M™Ê?MÆ$?M®G?MI®?L¿8?Kù)?K­3?K„C?K‹?K§d?KÆŸ?Kàñ?M m?M4[?M;`?MId?Ma“?Mv"?M—¤?M°^?MÑñ?Nb?N'¦?Mä?LÙH?Ky+?K#ÿ?K@ì?KTI?K|q?K®?KÑ‘?M ™?M3‘?MAÝ?MLø?Md?Mƒ8?Mq?M¿j?N ?NQÍ?Nš/?Nw?LíÜ?J¯/?J¥G?K?K1B?KhÕ?K£»?KÃú?M ¸?M.Ž?M@Å?MQŠ?McŠ?My¹?M¢^?MÃ?Ni?NA?N›O?N1?L”à?JÎÙ?JŸ?Mv“?M—€?M°Æ?MÇB?M©S?Ma^?L·~?L²?K„l?Ky=?K ?K¥0?Kň?KÙ,?Lû®?M1?M*?M5??MER?M]®?M]?M}ä?Mvy?Mv3?MjÕ?Løj?L¸E?LCœ?KíN?K¼Ê?KÈá?KË ?Ká°?Kêë?LéQ?M%Ž?M#û?M'À?M8g?MHî?M@Â?MS¤?M@'?MA$?M Ç?Lô8?L¦?LaJ?L&î?Kü?Kù?KðO?KöY?L.?)bù?L°–?M\?MA?M&?M0¤?M,`?M0K?M/”?M$?Mƒ?LÕ{?L§F?LmË?LDb?L)?Læ?L¾?LC?L»?K’?L¥¤?M ?Mê?My?M|?MÀ?MS?M3?L÷’?Lè?LÇn?L¤.?LÅ?LYï?LF?L>?L*Ò?L#Ù?L?Ùî?""ä?L¤?Mz?Má?M ö?Lýê?M?Lþ,?LêÂ?LÖ»?LÂß?L£p?L†?Lk?LX@?LL8?L?@?L;ˆ?L ?®y?°F?°‘?¯ø?¯t?Ï‹?öâ?L/Z?L·Í?L²§?L¨-?Lž±?LŒ`?Lë?L}D?Lo®?KN4?=Â?uö?Y?¸9?º)?·£?·;?·ë?L"'?L¿°?LÙ³?LÐ,?LÌz?L¤?L«’?L¿?L’¼?L‹ ?L}?Lkå?K·?K$X?õj?¼Ê?Àà?á?È£?L;Ù?Læä?Léj?Lä¢?LÚŠ?LØ?LÄ“?Lªî?L¡ï?MCó?M2ê?M+Ê?M¢?Læe?L“°?LeÅ?L0ä?L¤?L 5?Kü\?L2?L~?Lñ(?M#`?M+1?M0?M;?MO«?M[L?M]?MS”?MYz?Mc?L> ?L5ý?Kó€?#?L[?M ?Mù?M›?MÂ?MA?MZ?Mñ?Mµ?LàŽ?LÈ?L Í?L~ä?LX?LA³?L8Î?L*ü?LÅ?L¨?"±?L®M?MA?M ˜?M)M?M4ï?M0 ?M&i?M2L?Mê?Lñ°?LÔæ?L©®?Ls1?L8Ï?L/?L,E?LÒ?L·?L?L™Â?M¿?M#¸?M)°?M3ù?M>»?MJ;?M=v?MFì?MK?M¶?Lèÿ?L¦û?Lz?Lé?L ‹?LM?Lû?L?Kÿ?LñR?M"1?M/5?M/y?M>‚?MM§?MU#?M^£?MVk?MuÁ?M!.?MF?L’Y?LVõ?L?Ké’?KÝ\?Kë„?Kêš?Kõ±?Lùø?M'?M4é?M;6?M??MUf?Md?L™Ä?Lе?L€Ö?Kx?!Áø?¾%?sŸ?S¾?L“?¯"?²ò?¶®?´?®Ÿ?³œ?LÅ?L³?L®c?LÄà?L¼¶?L¯ê?L›°?L’º?Lˆ»?KØ{?LKe?K:t?ƒ?J'?¶r?º?¾é?¾–?!×y?L3’?Lá¯?Làà?L×j?LÉp?L½6?L®•?L¡S?L–Ý?L?L{¼?Lkë?Lh«?JÂm?!$V?½i?—?Çâ?!ÿ?LFï?Lëö?LìO?Lê?LÜ.?LÑB?LÆN?L°§?L©?L”?L~¿?LuÜ?L_?L_ ?LTä?Jåf?Äx?ÊÁ?!îÇ?L`]?Lù»?Lô‰?L÷ ?Lõ•?Lã°?LÚ×?LÒÙ?L®?L›3?Lç?L|n?LiÆ?LWy?LX?LH6?K¡ä?Ïþ?Ö?LP|?M ˆ?M 3?M ?Lÿ?Lÿ±?L÷Ë?Lçk?LÒ ?L³t?L¡2?L[?Lkë?L_’?LO/?LIC?L<|?KÂ?à–?LD?M {?M#?M&?MÌ?M ð?MA?Mˆ?Løý?LÚ•?L¹·?L¨o?L\?L`¿?LQš?L@2?L<?L*…?KÜ? l?L¥h?MÖ?Mƒ?MI?M!ó?M#{?MA?MÇ?M T?Lã2?LÃ'?L¯¾?Lq‘?LSé?L@®?L,?L.–?LÐ?Kï ?"x)?L®^?M ü?M"¶?M!‡?M,?M3ð?M*f?M8e?MÈ?Lë?Là"?Lžì?Lvü?LBK?L0?L?Lº?L¥?Kþ&?LŽ ?M¢?M"q?M&‚?M2¿?M6@?M8-?M@\?M?Z?M9?M?LæÇ?L•\?Lt%?L)v?L?L†?L'?Lm?Kýí?LæÊ?M"»?M(5?M*H?M9?MB‰?M<Ú?MJ?MNœ?MS ?MÍ?LáU?L¥?Laõ?L$Ü?Kù‚?Kù„?Kù³?Kõ¡?Kü¨?Lïu?M"ç?M/b?M)¢?M7¶?ME?MMe?MRÍ?MQü?MR'?MU?Lòâ?L¶1?LT4?L1?Kôh?Kñý?Kí¬?Këœ?Kö‰?LïØ?M¬?M*M?M.ë?M4‰?MBu?MR/?MO ?MRÆ?MD:?M;ö?Lín?L½??LBƒ?L?L W?KÞ±?Kæ?Kê1?KùA?Lç ?MŽ?M+¤?M0e?M)Å?M?L£?KéÔ?Kñ¢?Kòƒ?L&?LÕÍ?M ?M&´?M,w?M$N?M7Ñ?M@?MBR?M:.?M0ò?MB?LñÒ?L—é?LTV?L>/?L¬?LO?Kôþ?KûK?L Š?)@¯?L´ ?ME?M…?M&?M/Š?M,®?M0Å?M:?Mr?Lý¥?Lì?L¤ ?Lha?LI?L*ó?Lr?L)?L?L V?/¿?L¤K?MŠ?M:?M??M*?M r?M ¡?M!B?LøÏ?LïØ?LÏØ?L A?L‡?LYf?L@Û?L#2?L%y?L# ?Kù?ë¯?LŽ–?Md?Mß?M/?Mä?M Ó?M 3?M ©?LíÊ?Lè(?L£?L©l?L|Ü?Lc?LH„?L:á?L6¿?L1C?Káç?Óa?"?L â?M¦?M?M@?M?MW?Ló?Lá˜?LÝ?L¹º?L¨°?L’?Lmæ?L_$?LM¦?L<Í?L=Ã?KÉü?Çù?Îì?(4?L¥ß?LùÊ?Lû?Lú¢?Lìl?LäD?LØU?LÐ×?L¶“?L¢'?LÕ?Lxo?Lj-?L^?LG£?LJˆ?K¨?¥\?§Ò?©?¨¢?§?²K?½?Än?Ñ\?!âc?'Qn?Lé?K{ï?!ÃÇ?%ÿ?¿?‰m?kq?L¹?K8?«I?®®?±?°¡?¬³?«´?!£V?Lð?L©N?L£„?Lœß?L©V?LŸš?KêÏ?L`?L\*?K_"?!‚‡?°ð?A…?²}?¶?¹?¹­?¸™?L¼?L»Î?LÙþ?LÕº?LËv?LºŽ?L¨|?L¢þ?Lšÿ?L{Ò?L{e?Ls/?KÓ ?Kò?úú?¹$?½Ô?ÃF?É?L'?Lé?Lä¿?LÞ‡?LÙÖ?LÒ®?LÂH?L«1?LŸ?L™±?L{7?LvI?Lj?Lk?LUÖ?J¼]?¿‚?Å?Íö?L9”?LôŠ?Lóª?LîL?LæË?LàN?LÖ¨?LÐm?L«?Lš©?L–´?LzŸ?Ll'?Lc?Ldž?LMz?KjI?Æ?Ì'?L9??Mß?M7?Lú»?LôÊ?Lóu?LòU?LÙ‘?LÑ ?L±=?L›º?LÛ?L{Ô?Laò?LU?LT8?LKÙ?KŠ0?Ì·?!Éf?L¡º?M1?M ›?MÃ?M£?M)?Lÿ;?Lçö?LÕE?L²?L¢¬?L‹A?Ln¤?LZÂ?LHŸ?LG ?L>Û?Kµ ?à½?LLŸ?M?M ‰?Mg?MV?Mó?M Ý?M >?Lõ?LÛÝ?L»m?L­3?Lé?L`Û?LHZ?L@$?L?L7{?L-Ý?L&”?Kû ?"b„?L«œ?M?M–?M#|?M"3?M#…?M,¾?M$'?M]?Lí?LÅ?L­?Lz²?LJN?L5M?L%Ã?L!?Lƒ?L ^?# k?L²Ž?M‡?M5?M(B?M,É?M(W?M/†?M1ï?M+r?Lðû?LÎ}?L®T?Llá?LC)?L)Ó?L«?LE?L `?L Ÿ?L"?M?MW?Mx?M*È?M,"?M3â?M8Q?M.*?M(d?LüÚ?LÝ»?L¬ ?Llb?L<½?L"A?Lš?L?Lè?L ?LÓ–?MK?M;?M!?M&?M,ä?M:*?M6à?M*É?Ms?M?Lß¿?L­R?Lf‘?L>8?L+þ?LØ?L 9?L È?L w?)d?L±ˆ?M x?M"G?M¥?M)?M7 ?M*%?M&ò?M#?M ?LàW?L¡?LgÚ?LMª?L5á?L ?L=?L v?L ?"¼æ?L«d?M!1?M½?Mþ?M%K?M.Ù?M$Ó?MÕ?M[?LüÍ?LàY?LÁ?LnG?LU¥?L>O?L ¨?Lt?L¹?LÆ?3­?L£ß?M1?Mn?M÷?M"®?Mj?M?M«?LøÀ?Lä?LÖ?L ™?L|”?L^^?LCÔ?L*¢?L'û?L!Ü?LÑ?ãØ?L—?M|?M G?Må?Ms?M¥?M S?M`?Lï?LÛÆ?LÉv?L¢Ç?L†}?Lbm?LTÒ?L3Í?L33?L4Þ?KæC?Ï*?(X’?L©£?Mà?M ?M‘?Mù?Lý?Lû??Lå–?LÖö?LÃ:?L¦ç?Lƒ?Lh?L\[?LJ'?L>y?L?½?KÁÈ?Çc?ö²?LŒ”?Lþ?Lû”?M?Lÿn?Lï=?Lçÿ?LÜú?LÕÙ?LºX?L¡þ?Lƒä?Lv¸?Lj:?LY°?LEo?LGÿ?K—?Ás?Å7?!Í?L“ò?Lïƒ?LñÙ?Lòâ?Lçç?Là?LÓS?LÉÃ?L¸o?Lœ ?L)?L€>?Ltd?Ld3?LO³?LRŠ?Ky?¢¨?¤‘?¥{?¥G?¤;?§?©5?«3?«²?¸/?Ê?!’Þ?&€}? ®?£n?„@?mø?^Ì?P?NŠ?§ö?ª‹?¬?«û?ª¨?©-?©Õ?!x"?&á{?L?L—ç?LŠï?L‚?LiT?Kf ?!…q?%|¸?µ¢?I®?F?®C?±N?³?³Ú?³?!ƒm?Lû?Lµ_?L²n?LÂ?L¼Ý?LªÁ?L¢Ã?L–‘?Ló?Kãt?LR~?KIê?!o˜?«²?´{?¸¼?ºê?½.?!¯Ô?L8à?LÛ?LÙK?LÕ?LË?LÇÒ?L¥~?LœÐ?L•m?L„?Lr?Lmú?Ljà?JÎú?!JJ?º ?¿?ÂC?!¹R?LEx?Lîê?LæÇ?LÝQ?LÛ?LÐ|?LÉ·?L¨}?L3?L•×?L ?Lsä?LiÞ?Ll(?LTË?J¯T?¿«?Åf?!›·?LLä?Lùì?Ló?Lô]?LäB?Lå?LÒã?LÎÚ?L­ç?L›y?L–?L|¤?Liq?L`$?L`?LS ?K]G?Å×?ÐX?L@³?MÆ?M ?L÷H?L÷Y?Lõð?LòŽ?Lس?LÎD?L´ë?LœÞ?LÜ?Lx7?Ld?LU›?LP¶?LN1?KŠw?Ð?!Àæ?L¢?M–?M?M 3?LýÔ?Lüc?Lûš?Lãè?Lθ?LÞ?L?LŒz?Lpt?LX ?LS?LB?L>³?KÀ¡?Þi?"`.?L­>?MÃ?M+?MŠ?Mª?M˜?My?Lòç?LÒø?LÜ?L£½?L„‚?Lea?LR ?LI†?L9Ô?L5Ÿ?Køº?õu?LO†?M û?M ?M^?Mq?M …?M\?M?Lÿd?LÛq?Lº)?L©¬?L…J?L`…?LL=?L?£?L,ª?L)+?L ¬? þ?L£?M)?Mh?M?Mc?MU?Mš?MÄ?M¥?Lâ¤?LÂ?L¥S?L|¡?L_†?LE"?L5M?L%?L$?L?"?L¥Š?M3?MH?M?MÑ?Mˆ?Mf?M‡?M T?LçŸ?LËÍ?Lž=?L~š?LVð?LAÚ?L/x?L*;?L ?LZ?"Mh?L¤?M›?MW?Mï?MR?M%ð?M?M®?Mh?Lì:?LÎú?LŸ?LÔ?LVî?LC«?L+Ú?L)Î?L"Š?L–?Gº?L¢>?Mt?M+?Mº?M•?M ?MÁ?M i?M ?LéW?LÍf?Lž?LE?L^Ç?LH?L-ø?L11?L#]?L‹?ø©?L›¨?MF?M-?ML?M+?M=?MÝ?Mü?L÷U?Lç?LÍÚ?L¡?L{ö?Lbì?LRÊ?L2¤?L3?L-k?Lû?àå?(©~?L´½?MÖ?M Š?M g?Më?M ?LùÕ?Lïj?Lß?LĬ?Lª–?L}ô?LiÎ?LS,?L??L?KÌ^?ÆÊ?ô??L‹x?LýF?LýZ?Lÿç?Lÿó?Lï0?Lë-?LßT?LËË?L¼–?L¥_?LЍ?Ls»?LgB?LUy?LK[?LKh?K˜?À‰?Ǩ?(¶?L˜Ù?Lôž?L÷¬?Lü?L$?Lz@?Lh’?KW÷?!V?%*X?ªÔ?Dh?±?¶å?¯8?´U?¹ý?!Qe?L)Í?L´ï?LÏ’?LÄU?LÁ{?L¬i?LœÍ?L‘o?Lˆ*?L„Ñ?KÚ˜?KG?!L+?e«?µu?º¦?¸g?ÀQ?!m,?L=?LàI?LÚo?LÓï?LÆ%?LÁE?L¯D?L R?L(?L…–?L~?Ln—?LlŠ?JÍá?!-Ù?º.?¾¯?Ŷ?!c!?LG?Lè?Lê-?LáD?L×?LÌ?LÆ0?L³z?L›¤?L¨?L‚¯?Lz„?Lcî?Lb­?LZa?J³\?ÀU?ÄŽ?!up?LM-?L÷?LëÖ?LíÒ?Líy?Lâ4?LÎ[?LÆ8?L¶{?L¢·?LŽ+?L|±?Lpn?La?LZK?LU*?KkC?Æ?Κ?!ý™?L¡Ï?L÷Ì?Lõ:?Lô—?L÷k?Læ?LÔ¥?LȦ?L½¨?Lžv?LÃ?L€6?LcZ?LZû?LTã?LI?Kž.?ÌT?Ô?LEº?M+?M”?LþF?Lú†?Lû®?Lî4?LÜ£?LË2?LÀH?L¡?LŠü?Ls¥?L`Š?LVm?LNG?LDj?K×]?Ò)?!¢?LŸª?MÓ?M ñ?Mº?M?Lÿè?Lô!?Læ]?LÑÊ?L¸£?L¥7?L‘?Lk¾?L] ?LP?LB ?L>˜?Kø?Øe?"¤?L£ö?M?M N?M }?M C?Lý?LùÏ?Lñ ?LÐ~?L½m?L¡Ì?LŠè?LiÏ?LY@?LJë?L;Ì?L7%?L•?ßÿ?" ?L¨Š?MQ?MY?M {?M ^?Mú?Lþ»?Lö?LÎ:?LÄi?Lœp?L…?Ln×?LRx?LJr?L9u?L4$?L ‡?àª?" z?L¬ ?M2?Mº?M ?M×?MÏ?Lú¿?Lñ„?LÐÎ?LÇ?Lœb?LŠ8?Lm•?LO?LH‘?L;Í?L3©?L ø?Ù»?" ?L®z?Mò?MŸ?MT?M ?Md?Lø?Lï?LÒ Á¶H} èP !   » Á¶H èP !   8 Á¶Hì€ èP !   µ Á¶HÔ‚ èP!?@4 4ÿ†2 †Á¶H€!?@4 4ÿ†Df †Á¶H€SNOD ¡ üŸ xT: d; ¨tš (Y À„› ?•?—”?™¼?œ;?žy?¡5?¤–?§4?©„?«'?¬C?¬ç?­%?¬é?«é?©û?§¢?¥?¢b?Ÿ‘?”ö?—ô?šß?Ö? Ç?£¯?¦¶?©r?«Ì?­”?®Ô?¯?¯Ý?¯Ñ?®»?¬>?©‘?¦Á?£Ê? ??””?—ò?›s?Ÿ ?¢¯?¥?¨f?«H?­Ó?¯°?°÷?±Ÿ?±£?±?¯Ÿ?­†?ªï?¨?¤Û? ™?“ô?—)?šÆ?Ÿ”?¤Ò?¦ ?¨è?¬?¯?°Þ?² ?²?²W?±L?¯Ü?®3?«Ì?¨Ï?¥§? ?’ù?•è?™0?œÎ? y?¤h?¨1?«Æ?¯ ?±E?²¢?³?²¿?²O?±6?®ü?¬;?¨÷?¦¥? ?‘&?”4?—1?™¬?œ ?ž4?›?—ó?”7?Žž?‰ ?\?QÀ?}'???”¡?˜V?š?žz?›²?ŽÕ?’?•?˜+?›–?˜?Q?¥?zÏ}”>Î….?À˜?*é?nè?~ª?Š?•?–ÏX³>ÎqŸ>Í3B>Í.™>ÎFÎ>ÎrY?³‹?[Ø?xd?Œû??ˆ°?‹—?ì?w?“K?‡l?lg?/ª>Ï:Æ>Í>Í+>Íb>ÍX>Í$>Í >Î*'?Åì?`O?ƒ¼?‰?„«?‡y?‰Z?‡±?‚°?qé?Wv>Ï>>Ìÿs>Ìúº>ÍÅ>Í ¿>Í”>Ìÿá>Ìù“>Í >Î,6?Û†?iÍ?x:?€:?‚Ò?„?}®?oÆ?\:?!‡>Í~r>Ìì[>Ìæ´>Ìê±>Ìù<>Ìñ­>Ìë¿>Ìì.>Ìó>Ì÷Ò? p½?Fý?e‡?{a?}É?~O?w?gÞ?Nì>Ï%>Ìß*>ÌÚ›>ÌØ>ÌÛ¬>ÌÖA>Ì×>ÌÚc>Ìá>>ÌÜ0>ÌÓ>Íçä??W??vb?xŽ?xt?p¨?`¶?B¼>ÍYË>ÌÆ¢>ÌÈæ>ÌÌ >Ì·¢>̲Å>̾¡>ÌÈ\>ÌÇ]>̼â>ÌÂ)>ÌÉN? ?Es?rf?sr?rÔ?j”?ZC???š¢>ω>̼ª>̱’>̧Æ>̦ >̪µ>̰ >Ì¥Ø>̧Y>̲°?Á¯?âi?KÆ?n¢?nž?m‚?d¹?SÛ?;^?>ÍHË>Ì–>ÌÍ>Ì•f>Ì’!>Ìý>̉™>Ì”Ó>Ì–>Ì™Ê? !È?*m?G3?k%?j?hl?b’?X ?CÍ?!œ?Š>ÏX>Ì}Œ>̉š>̃>Ì}a>Ì}’>Ì{Ë>Ì…©?¥?ÏÇ??×?L«?gí?eÍ?cÂ?aˆ?^Í?L=?*‹?¿?—:>ÏOô>Ìe>Ìhò>Ìs¹>Ì|Õ>Ìr#?¡…?–?&-?FÇ?SA?d?a¶?_Q?]œ?\#?O2?<>?(e?þ?ˆ?kí>ÏVU>Ìfà?Ÿf?c?÷Ü?%Ô?6'?Jü?R&?`Ð?^2?[ ?W,?S ?Q?M8?C?3?!ö??…ž?i?θ?À?.›?Až?LÔ?S*?R?^Ý?[à?X?TÈ?Q>?NϹ">Î϶>ÎÊr>ÎÇ>ÎËâ>ÎÏH?+}?~†?©ú?ª?¥?’?•m?™3?w?¢#?¦'?Ž&>Ϭƒ>ιn>Íiì>Ínð>Í)J>Í!e>ÍX³>ÍV->Î4>λ5?V?™c? M?V?’t?–?š?žª?¢†>Ï¡¿>ÍMÓ>ÍMŽ>Í>͹>ÍÔ>Í^>͹>Í>ÍJg>Í?Ó>Îuä?a?™?‹ô?Žœ?‘¦?•?—é?~Ö>ͽ5>Í1¬>Í&>Í>ÍÚ>Í&>Íï>Í Š>Í>ÌûÅ>Í3@>Í1ƒ? r?†ò?‡è?‰ü?Œ`?q?“×>Ï}â>Í(Ë>Ìß“>ÌÜà>ÌîL>Ìû >Ìú‹>Ìó>Ìï¬>Ìí¦>Ìí<>Ìì×>Í( >ÎI#?oZ?ƒQ?„­?†?‡M?ˆ­>Í—è>ͼ>ÌØD>ÌÍY>ÌÇË>ÌË^>ÌÖH>Ì×t>ÌÍ×>ÌÎ>ÌÐñ>ÌÑÓ>Í ’>Í k? ä©?~?~Þ?…?~S?~Þ>Íy>̲5>Ìį>̼&>̶î≯>ÌÃÎ>ÌÀé>̾Y>Ì¿î≯¦>̼Û>Ì»>Ìö4? ™½?x–?x?x`?x?w‡>Íf>Ì”,>ÌŸ™>Ì¥G>Ì£4>Ì›m>Ì¡>>Ì¢Ó>̤ô>Ì¢¾>Ì¡?>̨s>Ì¡>Ìã(? K?sQ?rš?qÃ?ri?q>ÍS=>Íç_>Ì…i>Ìï>Ì>Ì„J>Ì©>ÌŒ>ÌŠj>̆R>Ì…Š>̈è>ÍáÙ>ÌÞÏ? ¶?nn?m2?kê?jÈ?jÔ>ÍJš>Ì¿Ù>ÌnÐ>Ìw->Ìq'>Ìkè>Ìac>Ìk>ÌjÏ>Ìo7>ÌhF>ÌaŒ>ÌÄv>ÌÀ(? £ª?iê?h!?fN?cy?]é?¥)>Ïr¨>Í¿€>ÌE<>ÌJz>ÌT™>ÌI>ÌCg>ÌH²>ÌN.>ÌSã>ͯÓ>Ì©?ØÑ??e ?c=?a ?^?\g?Tæ>ÍAª>Ìš¥>Í ß>Ì>e>Ì1˜>Ì7±>ÌG>Ì;ø>Ì4ä>Íœà>Ì¥a>Ì? †ß?N ?a@?^:?[;?ZK?[?R5?c>Ï\c>̃ >Í”£>Í›x>Ì2£>Ì)”>͉µ>Íœi>Ì’´>Ì{}?È?ú~?T?] ?Z,?UÁ?Oñ?I?JV?C@?˜h?|ž>Ïk>Ìq >Í‚‘>Í…q>Ì~ò>Ìq­?¼E?u±?#p?Rx?PË?[•?WÙ?S‡?NÒ?KV?Gë?CÂ??·5?º­?½j?¿}?ÀÙ?Áq?Ár?À/?»×?· ?³x?¯7?ªâ?šv?¡s?©²?©n?­?±«?¶[?» ?¿b?Â-?Ãù?Ÿ?–³?º5?ÁÏál>ÎïX?A6?“Ä?’æ?³?¸#?³*?­­?˜­?œ(?Ÿá?§c?­Ë?³¾?“Š?‘•>ÏÐx>ÎÛä>ÎÔ>Í->ÍŠ…>α>ÎØ>ÎÚ ?Bþ?}?¯x?­Í?—M?›H?ŸÆ?¥Ë?¬_?”<>ÏÁä>ÎÊT>Í{ >Í;">ÍDØ>ÍMÛ>ÍAú>Í8b>Í7>Ísž>Ψö>ÎÏ?‚’?£¨?•4?™8?ß?£‰?¨Ð>Ï»\>Íe>Í*Ã>Í$á>Í3_>ÍAŠ>Í?N>Í9Ý>Í-¼>Í#{>Í*S>Í*>ÍX½>ά?«P?’_?–+?šµ? |?‰Ô>ÍÔ&>͵>Í >Íy>Í(\>Í µ>Í Í>Í0î>Í'Õ>Í!l>Íð>ÍŒ>Í l>Í=? t?ŽÄ?’?•æ?˜æ>ψ·>Í3?>ÌøW>Íå>͸>Í_>Íb>ÍH>Íš>ÍN>ÍV>Ía>Ìý†>Ìù„>Í.º>ÎX|?ŠU?ŒÓ?Ì?“}>Ͱ>Ìç+>Ììô>Ìáý>ÌîF>Ìý¿>Í _>͉>Ìþ>ÍW>Íà>Ìï.>Ìî0>Ìña>Ìï.>͘?…I?†í?ˆ¨?l">͈P>ÌÒ§>Ìâ‡>Ì߯>ÌÖÉ>ÌÒ>Ìà$>ÌÝ>ÌáK>ÌÛó>Ìà>ÌÜ>ÌØš>ÌÕ’>ÌÕå>ÍØ?2?€€?€Û>Ï\Ú>Ìï3>̯>̵›>ÌÉ,>ÌÁw>̹°>̹ >ÌÈ >ÌÇ>ÌÈæ>ÌÌ5>ÌÉB>ÌÄÍ>̼ð>̶‘>ÌöO?xt?xQ?wÎ>Íw²>ÌÜÒ>Ìšd>Ì’O>Ì™â>Ì©%>Ì¢a>̘Â>̪>̦>>Ì¢M>Ì A>Ì¥ß>̪H>Ì¥:>Ì¥™>Ìâ#?r_?q.?pÃ?´«>Ï¢A>ÌŠ£>Ì€ý>ÌvK>̉>ÌŠ{>Ì|J>̪>Ì…x>Ì~Ä>Ìz">ÌŠ>̃{>ÌŒ>̈µ>̹¢?lô?kf?iô?f£>Íjx>ÌvP>ÌmØ>Ìe†>Ìaó>ÌfG>Ì[ò>ÌLœ>ÌY>Ì`ç>ÌZÏ>ÌT†>Ìbí>ÌrF>Ìt8>Ì­Ã?gÖ?f ?dµ?a7>Í%'>͵™>Ìe3>ÌQÖ>Ì=>Ì7 >ÌA>>Ì9<>Ì0X>Ì2ù>Ì?¥>ÌP>ÌLu>ÌHH>Í™>̵?bÖ?`?_¢?]1?¢Î>Ï‚ú>ÌI»>Ì@®>Ì0ò>Ì) >Ì%.>Ì"î>Ì'–>Ì(©>Ì/*>Ì,g>Ì7±>Ì>Å>ÌŒ'?òu?]ò?YÍ?T³?Qþ?S>Í5Ã>ÍŽ>Ì.ð>Ìö>Ìh>Ìó>ÌE>ÌÐ>Ìí>ÌÐ>Ìx>Ì*k>Íë>Ì—Ö? Ú?Z;?Ui?N†?Hû?C†?”a>ÏWæ>͈ >Í(>̽>Ì t>Ì *>̳>Ì>Ì«>ÍT¥>Í}à>Ì‹?Ék?ø´?X ?Sj?MÜ?H[?CÓ?>#?›A?ƒo>Ï@è>ÍhÙ>Íc7>ÍcL>Í^u>Íf2>ÍlÛ>Ìo-?Öc?|q?2?D?N?¡%?¥G?©z?­…?±Ô?¶?º?½?­½??¢v?§?¬?°Æ?µ´?º¢?¿R?ÃS?ÆÀ?É¢?¢?š©?Á?ʺ?Äü?¿¶?º¬?µ®?°˜?a?£…?©Ù?®F?³‰?¹?¾Ó?Ä ?¢Ï?žB?¡ >Ïé|>Îù^?IÛ?—V?˜Ô?¹-?¾?¸Â?²ô??¢J?§ä?®Ý?µy?»Ý?›¼?šÞ>ÏÕ?>Îñ}>Îè>Í_–>ÍZj>δ«>ÎõJ>ÎæÍ?6Ù?ˆØ?¶=?´„?œl?¡­?¨X?¯s?· ?žå>ÏÆ¨>ÎØ>ÍS²>Í`œ>Í]Ë>Í]ñ>Í[ß>ÍXo>ÍG,>ÍKœ>΢Õ>Î×L?iu?«á?šÙ?Ÿ¨?¤¨?¬??–v>ϺÞ>Í8Œ>ÍC˜>ÍIs>ÍEr>ÍQò>Íc>Í[h>ÍP©>ÍIÊ>ÍL‡>ÍN7>Í;~>ÎŒ’?­Ã?˜ž?W?¢¸?¨Ü>ϰÎ>Í%™>Í*ê>Í*g>Í9I>ÍDÐ>ÍNø>ÍX >Í\î>ÍL>Í=ø>ÍBº>Í7>>Í-¬>Í >Î}Þ?•”?š?? ?Š >Íߎ>Í Û>Ít>Í$K>Í3>Í@Ã>Í?#>Í=2>ÍHt>Í?/>Í8Q>Í-_>Í%F>ÍU>Í Ÿ>Í>g?‘¤?•Ò?š;>Ï„^>Ìëß>Ì÷0>Í'>͆>Í##>Í-é>Í2Ô>Í->Í(¥>Í11>Í2 >Í&¤>Í;>Í >Íö>Ìø¨?‰?ì?“°>Í©°>ÌÜ>Ìëç>ÌöŠ>Ìþê>Í >ÍÙ>ÍE>Íf>ÍÄ>Í£>Í>Í >Ìþ~>Ìùó>ÌúÍ>Ìé¬?Â?‰»?n¥>͇[>ÌÍÈ>Ì×z>Ìæ}>Ìé¢>Ìæì>Ìè©>Ìïš>Ìí.>Ìüý>Ìù›>Ìïc>Ìéû>ÌÞ’>Ìà8>ÌØF>ÌÑò?xÙ?ƒÓ>Ïp0>Ì»^>̹Ç≯ >Ì¿ù>ÌÎ>ÌÁŒ>̾>ÌÏn>ÌÏ0>ÌÈ»>ÌÎÕ>ÌÍw>ÌÇ…>ÌÌÅ>ÌÇ]>̺m>Ì»º?pÕ?wN>ÍoV>Ì›Í>ÌŸ@>̈>Ì™>̤Z>̨ô>Ì£=>Ì™â>Ì¢J>Ì u>Ìœ|>Ì›E>̤d>Ì©>̨‡>Ì¥a>Ì£é?i‘?n¿?®8>иˆ>Ì’ê>ÌŽY>Ì~>Ìl¶>ÌzA>Ì€l>Ìx?>Ì}>Ì~“>ÌsÎ>Ìw°>Ì‚Ó>Ì|ò>̉@>ÌŒ%>̉4?ct?i?dè>ÍiË>Ìm/>Ìo¹>Ìfã>Ì\>ÌS>ÌT>ÌIŠ>ÌIÁ>ÌFê>ÌQ7>ÌH›>ÌO*>Ìc˜>ÌbÏ>ÌeU>Ìw9?až?cž?bù>Í:S>ÌX÷>ÌHt>ÌLl>ÌL|>ÌDˆ>Ì(ø>Ì&¢>Ì!s>Ì@>Ì _>Ì/4>Ì:Ô>Ì>>Ì>)>ÌL!>Ì[š?_ê?]Ý?\?¢Q>Ðé>ÌBÊ>Ì7x>Ì.ß>ÌÊ>Ì_>ÌW>Ì S>Ìë>Ì>Ìä>̇>Ìw>Ì0«>Ì@è>͆Å?ZD?Sù?L£?G>Í?”>Ì9v>Ìô>Ì >ÌÄ>Ì.>Ëý{>Ëñ€>ËðU>Ì>Ì 7>ÌŸ>Ì@>Ì#>Ì?>̈‡?V‡?PV?Hé?D¡?”ä>Ðg°>Ì>Ì ->ËÿŠ>Ëóq>ËõÄ>ËîW>Ëì¼>Ëðè>Ëó>Ëüš>Ìs>Ì$>Íg4?áo?Ts?NË?HB?Au?7n?­b>ÐTw>Ío&>Ë÷,>Ëí_>Ëî`>ËèU>ËåÞ>Ëàè>Ëë³>Ì‹>ÍLš>Íq5?È?'ü?Ÿ|?¤?©?® ?³>?¸e?½W?Áº?Äå?È ?Ë_?ÍA?Íå?Í4?ÊÈ?Æ­? ?½(?¸?²ò? H?¥¡?«Q?±?¶â?¼Ù?ÂÌ?È}?£Œ?¡Ó?¦\?œÅ?žO?©Ö?¤²?¡|?¾P?Âw?¼Ø?¶¼? ¼?¦ß?­d?³Ç?ºp?Ás?¢?¢=>ÏáŽ>ÎøÛ>Îò<>ÎdG>Îká>ÎïÏ>ÎúR>Îò€?Qø?Ž÷?¼c?º!? ï?§c?®ù?¶9?½ø?¦Ç>ÏÔç>ÎêŠ>Íd >Ín>ÍtÂ>Ís¡>Íp%>ÍfV>Íh»>Í_T>αï>Îæ ?pß?³ ? u?§l?³4?¹î?£+>ÏÍ0>ÍIm>ÍS­>Ícé>Íyn>Í€0>Íw>ÍgÓ>ÍpC>Ím¨>Íd¢>ÍX&>ÍO§>Ξ ?¥?ž­?¤?§M?’³>϶>Í8„>ÍD®>ÍJP>Í`M>Ík~>Íj\>Ír9>Írý>Ítê>Ík„>Íd>Í^ù>ÍXþ>Í@‹>Έ{?œG?¡›?¥Ú>Ϭµ>Í!!>Í6¯>Í=ƒ>ÍEe>ÍT^>Í`¨>ÍfS>Íq,>Ítã>Íoº>ÍZ€>Í\'>ÍLC>Í:ô>Í1>Í&¡?˜÷?ž‘?‡>ÍЛ>Í ½>͈>Í)+>Í9>ÍEµ>ÍM¼>ÍY">Íf1>Ír=>ÍWt>ÍTt>ÍRU>ÍB„>͇>ÍØ>Í·?”£?™Î>ω0>ÌõÜ>ÌøÄ>ÍS>Í •>Í1E>ÍÍGâ>ÍQµ>ÍWÓ>ÍOÇ>ÍO7>ÍM'>Í<ª>Í-Ë>Íû>ÍE>ÍÕ?ƒò?‘Š>ͬ>Ìå7>Ìì×>ͪ>ÍÃ>ÍI>Í*õ>Í)#>Í:_>Í8ä>Í*„>Í2‡>Í%G>Í•>Ín>Í =>Ìþ±>Ìêƒ?r?Œ£>͘>ÌÖ->ÌÚŽ>ÌÚ>>ÌíÁ>ÌÿX>Í·>Í L>Íœ>Íp>;>Í z>ÍÔ>Ìø˜>Ìé->ÌðT>Ìã>ÌÔ¸?j?ƒy>ÍAo>ÌÀ>ÌÈœ≯Þ>ÌÀº>ÌÏ´>ÌÌ#>ÌÎd>Ìß2>Ìâ³>ÌÝØ>ÌÍù>ÌÚÆ>ÌÒ¶>ÌÒN>ÌÓ >̼í>Ì»•?aù?xü>Í•>Ì—©>Ì¡}>ÌÁ>Ìž>Ì£z>Ìœ‹>ÌšC>Ìž >Ì£>ÌŸQ>ÌŸä>̧+>̨¹>̨Î>Ì Ú>̧Ð>Ì¢j?X¹?n,>ÎnÞ>Ì… >Ì‚t>ÌŠš>Ì|x>Ìj[>Ìv!>ÌuØ>ÌtÝ>Ìj+>Ìiz>Ìj!>Ì]">Ìo>ÌsŠ>Ìz7>̇ >̆Í?Qo?fÝ>ÍT/>Ìrª>Ìd®>Ìl->Ì`O>ÌP>ÌL=>ÌF~>Ì4v>ÌC—>Ì3c>ÌA>Ì>^>ÌAb>ÌRÌ>ÌQó>ÌV‰>ÌjŸ?VÍ?a¡>ÍÌa<>ÌN£>Ì5{>Ì9>Ì;Z>Ì!€>Ì ->Ì s>ÌV>Ì v>Ì_>Ìe>̘>Ì%Ô>Ì3J>ÌDù>ÌT?\Û?[s?Ÿã>Ћb>Ì<Ö>Ì0ž>Ì(v>ÌÁ>Ëÿw>Ëø¼>Ëö4>Ëéh>Ëï¶>ËýO>Ë÷Ò>Ì>Ì ‹>Ì$>Ì7h>Ì@‡?V{?M=?CB>Í.¡>Ì0ˆ>Ì- >Ì ³>Ëí÷>Ëîš>ËïÚ>ËÜ=>ËÊG>ËÉÆ>ËÎ/>Ëé>Ë÷ë>Ì´>Ìà>Ì& >Ì2Ú?R³?Jè?D{?”{>Ðh¯>Ìê>ËÿÑ>Ëó>Ëéá>ËÕÿ>ËÕ¦>ËÎÚ>ËÍ–>ËËn>ËÜ->Ëç >Ëó¹>ÌÝ>Ì>Íkˆ?P®?J?B²?9Ù?¢Ã>ÐEV>Ëù»>ËðÑ>Ëá—>ËÒ«>ËÄÌ>ËÊ=>ËÍ%>ËÎu>ËÑ0>ËßÐ>Ëæ >ËõY>ÍFþ?ã¦?¡Ç?¦ê?¬±?²š?¸Ÿ?¾{?Ä?ÅÇ?Â'?ŽW?zh?wk?vâ?~‡?…?·û?Äç?Ã?¾ ?·»?¢¹?¨È?¯L?¶?¼ö?ÄB?¢R?¢Ò?›T>ÏñŽ>Ï Ê>Ïp>Ïð>Ï}>Ï i?;Ë?žI?žt?ºµ?½?£é?ª?±Â?¹J?Áw?«|>Ïì@>ÏV>Îex>Ís“>Íuý>ÍzF>Í…‰>Í¢>Í}ÿ>ÎCe>Îí…>Î÷ê?š¿?¹c?¤?«Á?³»?¼7?¦ê>ÏåE>ÍU¸>ÍZò>ÍpÈ>Í‚ý>Í“&>Í+>ÍŒl>Íy?>Í‚;>ÍpY>ÍiM>ÍZ >ΪS?½?¤a?«ê?µ?ŸG>ÏÌ6>ÍSã>Í[«>Ídà>Í|+>Í•Á>Í>Í”õ>Í>͘%>Í.>Íw>Íg{>Í_H>ÍO3>Îè?žæ?©è?“L>ϸe>Í5>Í=³>Í\t>ÍhÅ>Í">Í„>Í„¦>Í“}>Í—Ÿ>ÍŽü>͇¸>Í|ª>Ílø>Íh >ÍI>Í0?˜g?¦º>϶X>ÍÁ>Í5'>Í8ç>ÍC1>ÍeW>Í|">ÍyÍ>͇¢>Í–Ð>Í“Œ>Í…é>Í€>Ízu>Íh>ÍTZ>Í@>>Í.ì?‘T?¢Ã>ÍØÐ>;>Í*¸>Í.¡>Í?¶>ÍL >Í[Õ>ÍuÝ>Í3>ÍŒµ>Í’>͇W>Ív‘>Íi}>Í^­>Í2 >Í)I>Í!h?‰\?>Íx­>Ìû+>Ìø>ÍJ>Í@>ÍH;>ÍXk>Íud>Í0>Í‚µ>Í€…>Íy">ÍnÇ>ÍZL>ÍEq>Í2á>Í*ì>ÍÈ?sŒ>Ïqî>ÌÛG>Ììä>Ìóü>Í>ÍŽ>Í(>Í7k>Í@?>Í`a>ÍXð>Í[D>ÍX#>ÍM|>Í<è>Í.¬>;>Íy>Ìò?_£>Í¡ì>ÌÏÁ>ÌÓ>ÌäÕ>Ìì½>Ìòß>Í ]>ÍA>Íö>Í$'>Í0–>Í1†>Í.>ÍÌ>Í™>Í¿>Ìúì>ÌìQ>Ìàx?NO>Í|©>̺ƒ>̼¤>Ìú>Ì»>ÌÈf>ÌÜ›>Ìã¶>Ìé¾>Ìè¯>ÌíC>Ìå5>Ìã>Ìì=>ÌÛr>ÌÓš>ÌÚ>ÌÃk>̽¯?Fh>Ítš>̦i>ÌŸù>Ì‚>ÌI>̧\>̦^>̨s>̪#>̲_>̳µ>Ì®z>̦F>Ì¢'>Ì¥2>ÌŸ>Ìœ”>̦3>Ìž ?ÍRM>Ìy>ÌŠL>̇n>Ì€:>Ìw«>Ìq”>Ìbƒ>Ì`a>ÌKí>ÌN±>ÌJT>ÌL¶>ÌQs>Ìj;>ÌpÚ>Ìs²>Ìy0>Ì€‰?6œ>ÍA >Ìx—>Ìie>Ì]q>Ì\³>ÌQ >Ì@N>Ì/V>Ì%¿>Ì:>ÌS>ÌÊ>̬>Ì,¸>Ì8Ë>Ì@Ë>ÌK›>ÌYO>Ìdý??â?¢é>Ïb>ÌS™>ÌN=>Ì5F>Ì'Q>Ì$Y>Ì>Ëñ!>ËðI>ËàK>ËæÚ>Ëï]>Ì‘>̲>Ì Ó>Ì0b>Ì?´>ÌLa?J'?S÷>ÎaH>ÌF…>Ì+ò>Ì>Ì„>ËòÑ>Ëã³>ËÐp>Ę̈>˾¶>ËÀð>ËÉ>ËË¥>Ëæ>Ëôg>̃>Ì O>Ì6?Km?L>Íõ>Ì(²>Ì>>ÌM>Ëõì>Ë×U>ËÓ>ËÎ >˺¸>˪P>Ë©ð>ˬ½>ËÁá>ËÏö>Ëå>ËòÍ>Ì )>Ì)D?Nª?EÔ?’_>Ð]X>Ì •>Ëù†>Ëêz>Ëá1>ËÊ’>˲_>˪">˪ÿ>˧(>˨]>ËÆx>ËËÐ>ËÝE>Ëõ¥>Ì>Ì ?Lç?Ec?<#?¯>>ÐRÏ>Ëð†>ËãŠ>ËÜa>ËÈA>˵+>Ë¥ª>˯6>Ë¥‹>Ë«ˆ>ˬý>ËÄ>ËÒ >ËÝ^>Ëìy>ÍK‰?£§?©G?°.?¶è?½¾?ÄP?Êr?™¤?€³?\ ?3¡>Ï}ì>Ήv?³¿?O.?€S?Œá?¿$?Ã(?» ?£½?«…?³:?ºç?Âë?¢j?™ >ÏõG>Ï Ç>Îw>ÎrÍ>͇Æ>Í…u>Îhi>Îz$>Ïf>Ï Ë?qí?•|?¾f?¦Ç?®B?¶K?¾Ö?¦„>ÏêÔ>Îk>Íqb>ÍzC>Í>Íõ>͘>Í›>ͨE>Í’#>Í€¿>ÍyÓ>ÎFŒ>Îï?Áw?¨:?°ÏØ!>Íah>Í`b>Íl”>̓b>Í›J>Í£>Í¥À>ͦz>Í’I>͘C>Í…ÿ>Í„µ>Ínl>ÍSó>Χ¸?¨j?±V?™•>ÏÈê>ÍJ·>Í^´>Ílv>Í{Ô>Í”k>ͬB>ͰŽ>͵À>Í»Ü>͵¯>Íšp>Í”>Í{S>Ísµ>ÍZ >ÍE®?›s?¯Ö>ÏÉ >Í>>ÍF³>ÍH>Íex>Íé>Í“>ͦ >ͳ¾>Í»™>ͽï>Í·>Í®7>Í™ø>ÍyË>Ítl>Íbü>ÍEj??‹)>͘Ì>Í.ê>ÍIÓ>ÍM³>Í_ä>Íx>Íœ>Í—j>ͼ7>Í»>Ͷ>Ͳm>ͱ¾>ͨ’>͉b>Ídº>ÍT­>Í<À?~—>Ï¢>Í ´>Íš>Í:§>ÍGà>ÍXì>ÍeÞ>Í…u>Í S>͹E>ÍÏO>ÍÑU>Í‹>ͨ~>Í€\>ÍwŒ>ÍQè>Í5²>Í,ù?nå>ÍÉ–>Ìþ%>Ìý$>Í>Í9(>ÍV>Íbâ>Í€Œ>ÍŸû>Ͷ >ͳ™>Í·5>ͬf>ÍÑ>Í€Ó>Í\ >ÍNè>Í=¡>Í'q?UŠ>ÍX#>ÌâO>Ìù>Ìûä>Í ´>Í)í>ÍCš>ÍZs>Íwe>Í>ÍŸ>Í™Ø>͆j>Íx[>Ígn>ÍPØ>Í8Í>Í>Í?C>ÍW…>ÌÔ:>ÌÖÁ>Ìîþ>Ìüñ>Í a>Í#E>Í+%>ÍH¹>ÍP>Í^>Í\#>ÍZÒ>ÍBÐ>Í9­>Í)„>Í~>ÌóF>Ìë™>ÏO>̱á>Ì»>̾³>ÌÊ}>ÌÌ>Ì×9>Ìðk>Ì÷k>Ì÷]>Í¡>ÍÛ>ÍÔ>ÍÑ>Ìþy>ÌåH>ÌÖD>ÌÛü>ÌËW>ÌÁÙ>ÍJy>Ì«Ž>̪>Ì¡>ÌŸ>̦·>̧Ð>̬”>Ì´&>̪ñ>Ì£ù>̪ó>̤‡>Ìm>Ì #>Ì>Ì™y>Ìœ>Ì«ç>Ì?‰a>ÏSF>Ì Ã>ÌŠ¡>ÌzP>Ìrá>Ìqù>Ìl>ÌWÁ>ÌNÅ>Ì>>Ì>{>Ì5 >Ì&I>Ì:‰>ÌP™>Ìb·>Ìi‘>ÌpJ>Ì{?É>Íß>Ìk>ÌbE>ÌVT>ÌV»>Ì=<>Ì"ú>ÌU>ÌÚ>Ëì]>Ë×ñ>Ëߤ>Ëì»>Ì/>Ì >Ì)r>Ì=Õ>ÌYˆ>Ì_?¼>Î@¨>ÌH·>ÌG)>Ì?ù>Ìå>Ì{>Ìú>Ëâa>Ëɱ>˽Î>˱©>Ë«@>ËÁ¹>ËÒN>Ëä8>Ëúï>Ìž>Ì+í>Ì@5?'¶>Í/<>ÌB]>Ì6>Ì&>Ì >Ëû>ËØ¤>ËÉÔ>Ë£>ËÙ>˲>ˉø>Ë–•>Ë¥Ë>ËÚ©>Ë×7>ËéW>Ì>Ì(p?8?˜2>Îø$>Ì&%>̉>Ëý>ËÜ>ËÂ5>˲¤>˰>Ë”ø>Ëw¬>Ëu>Ë >˦>Ë¢ >ËÄ@>ËÛÊ>ËüÐ>Ì"Þ?J™??Ò>Î4î>Ì B>Ëð>>Ëä>ËÑÄ>˽½>Ë•e>ËŒ8>Ë{Ð>˳>Ë€«>Ëx¼>Ë™È>Ë¥—>ËÄ„>ËÜã>Ëül>ÌÚ?I>?A#?—*>ÐQÃ>Ëýe>Ëá<>Ëʬ>˽Ï>˨)>Ë–‘>ˇ>Ë‹3>Ëw÷>ˆZ>ËœË>Ë¡>˲‘>ËÎ>ËçŽ>Ëö«?¥¼?«ã?³?ºÝ?ÂG?Æ ?˜è?tà?Oÿ>Ï„³>Îvv>ÍÙl>ÍØ5>ÎfZ>Îu?Ã¥?j­?‹Â?ºx?¾›?§?®Ü?·'?¿©?Ȥ?¨š>Ïû¯>Îsê>Î{ü>͸>Í…>Í”E>Í”>Í“ˆ>͘¾>Îm¾>ÎzB>Îÿh?xL?¾s?©þ?²!?ºç?Ä{?£0>ÏA>Íu«>Í|÷>Í‹{>Íœ>ͦ>͸ >͵>Ͱ>Í >Í›í>Í\>Íví>Î9k?*?«Ä?µ ?¾A?œ{>Ï*>Íva>Íow>Í€e>Í– >ͼ >ͼð>Í·a>ͼE>ͼÌ>ͧ´>Í“5>͈•>ÍL>Íh6>Î)(?¬p?¸¦?•Ž>Ï–>ÍT{>Ík{>̓6>Í”æ>ͬ>ÍËU>ÍÌË>ÍÌ>ÍÕû>ÍÄþ>͸Ï>ͧj>͸>Íwü>ÍeÀ>ÍT=?˜9?”_>Ï9>ÍEh>ÍN†>ÍR×>Í|>Ͳ>ͱg>ÍÀ >ÍÕi>Íá¦>ÍáÙ>ÍÞs>Í×3>Ͷ>͘,>ÍŠË>Íp„>ÍZD?€J>Ïš>͹>Í5>ÍMñ>Í\§>Ír‘>͘g>ͳ(>ÍÀ>Íás>Í÷:>ÍîO>Íù >Íàk>ÍÌÄ>ͪÕ>Í}Z>ÍfN>ÍL5?kÈ>ÍŠ˜>Íj>Í&Ô>ÍDU>Í\8>Íqy>Í…‹>ͦ!>ÍÆÌ>ÍðÖ>Îj>Î ý>Îä>ÍØö>ͬÈ>ÍŠ>Ín>ÍO(>Í6”?0¯>Íx+>Í“>Í·>Í'>ÍF>Íc¤>Í’—>ͺ@>ÍâU>Α>Ϊ>Íþ>Íôš>Íа>ͬ[>͈f>Ím>ÍI¤>Í5L>Ï&>Ìã7>Ìí¼>Í®>Í />Í>ÍGÕ>ÍX×>Í‚R>Í»Ÿ>Íè§>ÍÿÅ>ÍáH>ÍÂç>ÍÚÞ>ͧR>ÍÜ>ÍVï>Í1„>Í™>Íy©>ÌÜ_>ÌØö>ÌÜÚ>ÌÿÖ>Í >Í>Í5ç>ÍL·>Íz6>Í”V>ÍÁh>Í´->Í›¦>ÍRH>Í_ž>ÍFñ>Í h>Í·>Ìó—>Ìÿ“>Ì´»>̾I>ÌÃÆ>ÌÕÚ>ÌÝ>Ìåe>Ìø¸>Ìüv>ÍS>ÍAO>Í/ >Í3î>Í8!>Í+€>Ìóº>ÌÛx>ÌÕL>ÌÒT>ÌÈÁ>Ìâ»>̪ >Ì«€>Ì¢Z>Ì—†>̤±>Ì¢v>̱l>̧”>̰d>Ì€@>Ì•–>ÌÅy>̱Ç>ÌœY>ÌzŠ>ÌŠ@>Ì—Ù>̬y>Ì ÷>Î?2>Ìf>Ì–u>̇ú>Ìq>ÌuÛ>ÌlÚ>Ìo¼>Ì@å>Ì5X>Ì k>Ëúú>Ì ø>ÌF>Ì Ä>Ì<ñ>ÌRV>Ìbå>Ìjá>Ìuq>Íú>Ìt“>Ìf>ÌZ>ÌL;>ÌCñ>Ì/%>Ì>ËøÐ>ËÏ>ËÈÊ>ËŒ•>˃Æ>ËÀ¢>ËíŸ>ËïØ>Ì Ø>Ì,D>ÌO£>ÌV!?}>Ï´>ÌD@>ÌJk>Ì3û>Ìu>Ì>ËÏN>˸Z>ËŒÉ>ËvÕ>ËVs>ËPÛ>ËTº>Ëb®>˼p>Ëö>Ë÷d>Ìá>Ì2}?á>ÌÝ>ÌA^>Ì+">Ì\>Ëô >Ëá|>ËÀ>Ë®ž>Ëx[>ËPV>Ë? >Ë(}>Ë;Ñ>Ës>Ë£ >˱¼>Ë×>Ëø >Ì$O?%Ž>Î/->Ì,Œ>̶>Ì>Ëåâ>ËÅi>Ë©ò>Ë…˜>Ëg´>ËPó>ËFë>Ë6ò>ËJ¼>Ë\º>ËyÝ>ˬ4>ËÎ>Ëêá>Ì??ò?‘ù>ÎÔÒ>Ëùö>ËëQ>ËØ>˶Û>Ë—¯>Ëmc>Ëa²>ËRü>ËO•>Ë?J>ËFg>Ëm¸>˃Ÿ>Ë——>ËÀ,>Ëáº>Ëçê?B^?;\?—ù>ÎÙe>Ëñ>ËÒ÷>Ë®>Ë›¢>ˉÂ>Ëo*>Ë^â>Ë\7>ËXÀ>Ë]r>Ëw7>ËŒ·>ËžR>˺ð>ËÕ>Ëæ}?§b?®?¶l?¾@?Åô?Ã4?ê?Pð>Ïp0>Íß>ÍÔ“>ÍÔ>ÍÔp>ÍÔk>ÍØ”>Îj??ÙÓ?e]?ºq?À¼?ªS?±ü?ºÃ?Ä@?¤Ï?t>Ï[í>Î|Ç>Í™€>Í‘Ë>Í™´>Í >Ͱ'>Í©j>Í¡>Í—Ï>Îm>ÎvM?ê?›!?¬ú?µ«?¿{?©c>Ïåâ>Îeí>Íps>Í‹ˆ>Í¢g>ͱÊ>Í·±>ÍÁŸ>ÍÈ(>ÍÃÐ>ͱ„>ͧd>Í›>ÍÊ>Î?,>Îè®?®µ?¹?¢Œ>ÏÕŸ>Íb>Íwa>Í>Í)>ͤ4>ÍÉ|>ÍÕ|>ÍÓ,>ÍÏ>ÍÐŽ>;Á>ͨ%>Í—l>Í„ƒ>Íq(>Í]Y?¯H?¼>ÏÑÃ>ÍGN>ÍX5>ÍzÌ>Í“>ͱ>ÍÆ >ÍÜH>Í÷{>ÍðJ>Íæå>ÍâO>ÍÙ¥>ÍÀ+>ͪ¥>Í">Íu¤>ÍaÛ?“®?а>͸§>ÍJ’>ÍZ>>ÍpÔ>Í›Ö>Í´•>ÍÑl>Íë’>Íö>Îf>Î z>Îü>Í÷Ñ>ÍÖp>Í·¯>Íœ=>Í~|>Íjb?t‡>Îñ¸>Í M>Í;9>ÍX>Ív|>Í”ª>Íà >Íâ>Íüî>Μ>Î9¸>Î3ñ>Î! >Îm>ÍïŠ>ÍÁc>ÍŸ2>ÍV>ÍZÔ?2l>ÍŠ“>Í%ÿ>Í4&>ÍOu>Í^â>Í„ø>Í£¨>ÍÐL>Íýß>Î1é>ÎEÉ>Î\c>ÎC®>Îá>Íæ•>ͳE>Í‘Q>Íiª>ÍEW>Ï90>ÍU>ÌÿC>Í,>Í:Ú>ÍT>Ízd>ͪÙ>Íè6>Î Ï>ÎV´>Îrâ>ÎZx>Îp3>Î'œ>Íéš>ͳ•>Í{Ø>ÍVx>Í?±>Í$¡>Ìåô>Ìññ>Ín>Í>Í4a>Í_«>̓>ÍÄe>Î >Î@²>Îg¨>ÎpÏ>Î@×>Íþá>ÍϹ>ͺQ>̓¾>ÍIh>Í!é>Í (>ÌÐ>ÌÙ >Ìã¹>Íi>ÍM>Í&R>ÍIx>ÍjÛ>ÍÃj>ÎD>Î+í>ÎCµ>Íû]>ͬ/>͇v>ÍU">Í:˜>Íë>Í{>Ìôo>̹ò>ÌÀË>Ìħ>ÌÖd>Ìà>Ììñ>Í È>Í&Ž>Í.÷>ÍdÏ>͘T>Íž¶>ÍgÅ>ÍS >Í%T>Í>ÌÚñ>ÌÜ?>ÌÔh>Ìàï>̧x>Ì«É>Ì¡9>Ì’E>Ì«}>̪ø>̘ð>Ì9>Ì»z>Ìœg>Ì­”>Ìš¶>Ìš¼>Ì©N>Ì¥þ>Ì„›>Ì—>̧^>̪º>ÌÌÆ>Ì‚>̆ô>̃R>Ìpß>ÌbÜ>ÌP>ÌK>Ì%Ü>Ëì™>ËØ>Ë’Z>Ë­”>ËÕá>Ëúû>Ì! >Ì:a>ÌJ’>Ì^Y>Ìfd>̼>Ìl»>Ìdë>ÌSF>ÌI >Ì6å>ÌÄ>Ì ~>Ë×Ì>Ë|Ä>Ë=Ð>Ëü>Ë ¢>ËT‚>Ë–½>˰D>ËØ`>Ì>Ì4>ÌH†>Î!S>Ì\i>ÌM¨>ÌG'>Ì.“>ÌŒ>Ëí\>Ë«Ò>Ë‘~>ËJ>ËÑ>ÊÎJ>Êì>ÊýÄ>ËÅ>ËwÇ>˼U>Ëæ>Ì>Ì ö?€>Ï>Ì3¤>Ì)›>Ì–>Ëä|>ËÌu>Ë›È>Ëp>Ë6Ä>Êû<>ÊèÅ>Ê·(>Êî›>Ë:ò>ËpD>Ë–X>˼ö>Ëßb>̃?¯>ÌÊÈ>Ì/>Ëöç>Ëê^>ËÕÀ>Ë­º>Ë·>ËF¸>Ë#‹>Ë?>Êõ>Êå1>Ê÷ï>Ë >ËL:>Ë…1>˧>ËÙ>Ëþ™?.p?‚u>ÎÝó>Ëõz>Ëçí>ËÏb>Ëš,>Ë‚!>ËX*>Ë2À>Ë3>Ë Ë>˵>Ë' >Ë5!>Ë`L>ËY>˯P>ËÑù>Ëܰ?8=?8º>Î#>Ëý^>ËÛŒ>ËÀ”>Ë¢ñ>ˆl>Ëk>ËH>>Ë*ð>Ë1[>Ë1î>Ë1Ò>ËO‹>ËZ!>Ë‚›>Ë«F>˺„>ËÓ(?©:?°(?¸Ø?Àø?É ?Ø?_'>Ï‘‡>ÍÞd>ÍÖÖ>Íѳ>ÍÕ2>ÍÙê>ÍÔ>ÍÔR>Íà=>Î~P?îÛ?v^?½?¬?´k?½„?Ç|?¨T>Ï÷ >În>Í•F>Í›U>Í›>ͯ>Í»•>ÍÅP>ͶÈ>Í©³>Í¡ª>Í—Õ>Îl >Îýâ?˜Ê?¯0?¸?ÂY?¤>ÏÍrò>Í|†>Íg>ͱ>Í»g>ÍÅb>ÍÔo>ÍϨ>ÍÐH>ÍÁ¾>Í«ä>Íœ•>͈Ý>Íwà>Î;?°®?»\?]>Ï0Ì>Íg>Íy_>͉Z>Í›>͹ >ÍÔL>Íãš>Íì>Íäf>Íà¤>ÍÑ>ͺ;>Í«×>Í–h>Í„ü>Íi§?±·?›Ê>Ïæ>ÍFa>Íc>͇>ÍžE>͹—>ÍÓ²>Íö;>ÎÆ>Î _>Î >Î ë>Íì±>Íן>ÍÊF>Í£—>Í€>Íj`?‘À>ϰ>Í9Ê>ÍKì>Ícã>Í€ƒ>Í®$>ÍÑõ>Íòv>Î>Î*m>Î7n>Î:ß>Î+¦>Îë>ÍïÒ>ÍÃÍ>ͦ>͇×>Ío•?>Å>ÍŸÚ>Í8>ÍMo>Íc}>Í}>Í­­>ÍÝ0>ÍúÞ>Î!)>ÎM%>Îlk>Îh‰>ÎUø>Î9>Î |>ÍÔÓ>ͪ>ÍŠ}>Ígô>ÏAó>Í >Í ä>ÍÍZp>Í{`>Í¥×>ÍßÍ>Î->Î?»>Îsp>Î¥µ>ÎÈ’>ÎyŽ>ÎEb>ά>Íß7>Ͷ >Í||>ÍT*>ÍD{>ÍJ>Í S>Í ¥>ÍNr>Ío>Í”¨>ÍЗ>Îö>ÎsÑ>γ¹>ÏQ>Î×Ù>ο[>Î’w>Î->ÍÎl>Í—N>Íwâ>ÍP†>Íl>Ìé>Ìú>Íà>Í#U>ÍMV>Íqm>Í®>>Îd>Îc<>ξ(>Ï>Ï ñ>ÎÿÀ>Î\>Íý%>ÍÜI>Í–>Í[\>Í6e>ÍÚ>ÌËù>Ì܃>Ìõ>Í–>ÍÞ>Í5«>Í\#>Í–¬>ÎC>εJ>Ï›>Ïç>Ηp>Î,Ò>ÍÆä>Íp6>ÍL7>Í(!>Íã>Ìú=>ÌÂk>Ìà >ÌÄå>ÌâÊ>ÌÝ–>Ìý>Ír>Í:©>ͯ;>Î#Ó>Îe¹>Îi`>ÍúÍ>ÍžO>ÍY>Í,7>Ìðf>ÌÝ_>ÌÚú>Ìãa>Ìši>̦D>Ì¢é>Ì”À>̨ç>Ìší>Ì¢¦>̪¿>Ì“4>̶[>Ì­¿>ÌÈ4>̽½>ÌÆ›>Ì©k>Ì®ç>ÌŸæ>Ì«¦>Ìš|>ÌÕ >Ì… >̇ú>Ìu|>Ìe9>Ì]å>ÌP">Ì9´>Ì .>Ë¢”>ËU>ʽ¶>Êêz>ËkY>Ë®Ã>Ëÿ>Ì!è>Ì>Š>Ì]e>ÌoT>̲¢>Ìg>ÌV¶>ÌMs>ÌCæ>Ì&>>Ì Œ>ËÏ€>ËŒ>Ë.j>ʈ;>ÊAê>ÊU_>Êt†>Ë <>ˇ“>ËŸà>Ëö>̹>Ì:Û>Ì¢>ÌPß>ÌFå>Ì7ñ>Ì!>Ì=>ËÏì>Ë—Þ>Ë:Ì>ʾ$>Ê…{>Ê:‘>Ê'>Êyo>ʽ‚>Ë.s>Ë‹k>ËÐS>Ëñµ>Ìx>ÎÃ>ÌDÍ>Ì+€>Ì'%>ËþS>ËÖ>˱s>Ëo>Ë(Ð>ÊÛÙ>ÊÈu>ÊH)>Ê>”>Êxó>Êâ/>Ë0>Ëz)>˳;>ËÛ»>ËûÚ?v>ÏÞ>Ì>Ëï>Ëãu>ËÅ>Ë•H>Ëk>Ë&>Êù\>Ê´“>Ê¢>ʤç>ʲa>ÊØ‡>Ë >Ë]>ˆà>ËÁî>Ëæ‚?u>Î>a>Ì3>Ëög>ËØŽ>Ë·ö>Ë‹Þ>ËoQ>ËBò>Ëz>ÊÙr>ÊËŠ>ÊÀ½>Êç°>Êûe>Ë0 >Ëa±>ËŸ>ËÁ®>ËÓ8?.›?–¯>ÎÕ|>Ëîò>ËÔ–>˽ç>Ë•ø>Ëi×>ËN~>ËÜ>˵>Êù>ËÓ>Ë >Ë&->Ë>N>ËpÑ>Ë”2>˪7>ËÉc?«ž?²¸?º½?Ââ?Ë/?|ã?7>ÎÎu>Í×Þ>ÍÒÄ>ÍØ‚>ÍÛ‰>Íà>ÍÚ’>Í×)>ÍÛŽ>Íàê?Sÿ?`?¼u?®T?¶S?¿|?Éì?¬>Ï]Ã>΄¡>Í–Æ>ÍžÚ>ͳ >ÍÊž>ÍÇ¡>ͽà>͹>Ͳ¬>ͯ&>Íž…>Îml>În ?&0?°º?¹¥?Ä-?¤²>Ï8ó>Íw>ÍŒv>Í™Ý>ͳ>ÍÇu>ÍÌŸ>ÍÜ®>Íá)>ÍÝ‘>ÍÏ>ͳ3>ͦæ>Í—‹>ÍÉ>Î=±?±Å?¼d?œ¸>Ï->ÍqW>Íì>Í‘è>ͯ4>ÍÎl>ÍÝc>Íêu>Íù¾>ÎS>Íú >Íç“>Íѳ>Í»œ>Íœy>͈ >Íyú?²|?”¡>ÏX>ÍNc>ÍlÑ>͆<>Í£ÿ>ÍÈî>Íè©>Îë>Î"Þ>Î.¼>Î7¯>Î%Â>Íúã>Íâ+>ÍÍ…>Í®g>Í•s>ÍmÎ?‹g>ÏU>Í?s>ÍT>Íi>Í…>Í®M>Íæy>Î Ž>Î>ÎH>Î[£>Î^n>ÎIç>Î.Â>Î>ÍÝ9>Ͳ>Í•g>Íqb?'ù>Í Á>Í>~>ÍO»>ÍqÀ>ÍO>͸î>Íì>Α>ÎG|>Ί(>Ξ‡>Ή¹>Îx¯>Îfm>Î1Ö>Íå_>͹I>Í“d>Íkj>Ι>ÍQ>Í-¬>Í?w>Í\†>ÍŽI>ͼä>ÍîY>Î1d>΄‰>ÎÐ>Îí>Ï>ν>΄ >Î+à>Íí´>ÍÄ>͈!>Íb¼>Í=÷>Í>Í/>Í/>ÍM›>Í}5>Í­ >Î g>ÎSQ>ÎÉŸ>Ï É>Ï[¥>Ï]«>ÏÍ>γÖ>Îh™>Íø|>ͬä>Í}2>ÍZÅ>Í!>ÌèÞ>ÍB>ͨ>Í0R>Í\>Í\>ͽÒ>ÎB>θ£>ÏO[>Ïþ0>Ïãˆ>ψo>ÎÑ8>ÎIï>Î>Í©O>Íc¾>Í>š>Í&>ÌÖ¡>ÌÛ>>Ìõ>Í A>Í·>ÍI›>Ít;>Íݤ>λF>Ï–¹>Ðz³>Ђu>ψÀ>ÎìX>Íû7>Í–™>Íg>Í,ö>ÍÒ>Ìý¿>ÌË>̽>ÌÃ)>ÌÚj>Ìâ£>Íg>Í;T>Íoû>Î K>Îó>Ð-Å>Ðdü>ÎÃ>ÍÕ>Í„*>Í,Ä>Ìü2>Ìó™>Ìå¨>Ìás>Ì>Ì£#>̦>ÌžÅ>̬(>Ì£>̰>̲i>̉>>̬6>ÌÜw>Ìþ*>Ìó>ÌÚ¥>Ì–†>ÌŸ4>Ì />Ì¢N>Ì—<>ÌÓ>̆">Ì‚³>Ìr¡>Ìl7>ÌXÌ>ÌD­>Ì >Ëîl>ËHÏ>Ê9h>ÈÎY>É%Ë>Ê‚©>ËO'>ËÒŸ>Ì}>ÌD¨>Ì^R>Ìo]>Ì®K>Ìb>ÌVž>ÌI,>Ì;)>Ì.Ã>Ë÷ô>Ë›>ËU¶>Ê©À>ÉÆ>ÈâÛ>Ȧn>É’½>ʃd>ËN0>Ë¥>ËØ‚>Ì~>Ì>7>Ì „>ÌNq>Ì6y>Ì)™>Ì>ËÞÆ>˯‘>Ë}Í>Êò{>Êi¼>Éצ>ÉHÎ>ÉN‰>É¥>Ên¾>Ëù>Ë`­>˯>ËÛ>̱>Ì“%>Ì5ö>Ì%ë>Ì:>Ë÷›>ËÆõ>Ë—R>ËHÂ>Êú">Êo1>Ê<æ>ÉæÔ>ɾu>Ê1<>Ê >ÊÉ >ËOk>Ëž >ËÊ\>Ëì’?bõ>ÏÔ>Ìù>Ëú>Ëà>˹>ˉ­>ËT›>˶>ʾ„>Êtê>ÊQ >ÊKê>ÊoQ>Ê®…>ÊûB>ËNå>Ë>Ë»5>ËãA?ï>ÌÁþ>Ì ÿ>Ëòf>ËÒ,>Ë®m>Ë}->Ëd'>Ë1n>Êï>ʱL>ÊŸá>Ê>Ê¥>ÊÐ>Ë7>ËL4>Ë}P>Ë®>˼?%p?‹W>Î×$>ËîÞ>ËÌ>Ë´˜>Ë…/>ËO>Ë/:>ËŽ>Êó®>ÊÖ%>ÊÝG>Êç/>Êý“>Ë&>Ë\å>Ë„Ð>˨Ÿ>ËÃ,?¬˜?³Å?»©?ÃÚ?Ì?}*>Ï—>ÍÜ[>ÍÒ*>ÍÕ›>ÍÚñ>Íß:>Íä>ÍãÉ>ÍÞt>ÍÜg>ÍÙç>Îz‰?2g?·[?¯1?·K?À ?¢Q?ž>ÏV|>ÍÓ>͘Å>ÍŸ‚>Ͳë>ÍÇC>ÍÎb>ÍÀ²>͸¸>ͳü>ͲÖ>Í£Ä>ÍŽÒ>ÎVÜ?,º?±[?º{?£÷>ÏìN>Îlý>͈Þ>Í”œ>Í£h>ͺQ>ÍÏì>ÍÜl>Íå>Íå_>ÍáO>ÍÐj>Í´s>ͯÍ>ÍŸã>Í„µ>Î>t?²?¼Ù>Ïä+>Íb”>Í}ì>͆2>͘à>ͽ’>ÍÝÐ>Íæ>Íù¤>Î>Î ¡>ÎÕ>Íõ]>ÍØô>ÍÁT>ͬ«>Íš>Íuâ?²*?”©>ÍÇø>ÍW]>Ígí>Í‚P>ͪo>ÍÝš>Íì‹>Î >Î%=>Î8k>ÎHe>Î$,>ÎD>Íìß>ÍÆc>ͬW>Í£«>Í?]f>Ï }>Í@^>ÍZÀ>ÍhÈ>Í‹¸>͵}>ÍÝÓ>Îz>Î7>Î\Ì>Îz>ÎlÎ>ÎYÙ>Î?¶>Î>ÍìL>ͽC>Í’¨>ͺ>ÏcØ>Í&Ë>Í:Á>ÍO>Í€>͘ð>Í¿§>Íî>Î(t>Îj>Πÿ>λ²>ίX>Ζ²>Îq>Î@¿>Íô˜>Íȃ>Í¡³>Íhü>Í_>Í#c>Í6w>Í@P>Í]>͘x>ÍÁ>Îw>Î]‹>Μ>Îä¬>Ï>Ï" >Îñÿ>Ο¯>ÎNÆ>Î >ÍÆ‡>Í”C>ÍlR>Í9R>Í #>Í$>Í8¥>ÍLÄ>Í‚Ä>ÍÂ>ÎÆ>Îcu>ÎíË>ÏWÒ>Ï»ì>Ï¿2>Ïq1>Îø{>Îp>Î1>͹î>ÍD>Íca>Í!ú>Ìé¬>ÍÈ>Ín>Í3q>Í^–>Í“$>ÍÐ(>ÎdE>ÎÿÌ>ÏËÂ>Ð’ >Щ;>Ðá>Ï'Æ>Îqê>Î'z>ͳ4>Ík:>Í>b>ÍQ>ÌÕü>ÌÒ×>ÌïA>Í >Í"v>Ífž>ÍŒ}>Î:>Ï ‹>Ðck>ÑÞÐ>ÒŒ>Ð|£>Ï5®>ÎAÃ>ͧ>Ík¯>Í()>Í í>Ìþ(>̶†>̹ÿ>ÌÒ2>ÌÙ >Ìô>Ìý÷>Í'ð>Í—_>ÎXÈ>Ðpò>Ôs>Ô›ƒ>Ð*–>ÎU#>Í¢.>ÍL…>Ìÿ­>ÌïË>ÌÛ³>Ìèˆ>Ì f>̤š>Ìú>Ì›z>Ì—ò>Ì•°>̼ä>̹‹>Ìp>Ì|c>ÌùÑ>ν‘>Í‘>ÌÖY>ÌžÏ>Ì¥>Ì«V>̤Å>Ìžh>ÌÈ…>ÌŽv>̇D>Ìw‰>Ìo®>ÌVˆ>ÌFW>Ì £>ËÖƒ>Êóý>É +>Ä“¾>ĺ¸>É*ª>Êܦ>˾>ËåŒ>Ì2>ÌP$>Ìly>Ì­H>Ìf@>Ìb>ÌJ>Ì2´>Ì%X>Ëóð>Ë­#>Ë!Š>Ê=ñ>ÈÛ:>Çd>Æä®>ÈŸï>ÊO>Êü¥>Ëh¬>ËÇ)>Ì5>Ì9á>Ì¡J>ÌMâ>Ì)ç>Ì!¨>Ì>ËìØ>Ë»ì>ËZ5>Êëà>ÊPô>ÉeF>È·˜>È2>É4W>ÊÊ>Êèj>ËOb>ˤÐ>ËÑ>ÌJ>ÌŒê>Ì+é>Ì">Ì”>ËöŒ>˽ü>Ë–0>ËRC>ÊÎ >ÊHØ>Éø`>Éqá>Égù>ÉÓø>Ê[U>ʽ;>ËMT>Ë—›>ËÆ…>Ëï‹>Î E>Ì.N>Ìî>Ëîø>ËÔÄ>˨>Ëg`>Ë1±>Êò>Êš >ÊIã>Êœ>ÊV>ÊCP>ÊŠ\>ÊìÅ>Ë5>ËwE>˸>ËБ?xÍ>Îù²>Ìî>ËìG>ËÓ*>Ë´>Ëtã>ËKH>Ë…>ÊΑ>ʤL>Ê€>Êh>ÊÒ>ÊÍ>ËÜ>ËI„>Ëuù>Ë©y>ËÁ·? R?šÜ>ÎÔÛ>Ëí¥>ËÔã>˯­>˃T>ËWÜ>ËP>Ê÷9>ÊÏ>Ê¿®>ÊÊ%>ÊËÓ>Êí>Ë0>ËN$>ËzŒ>Ëœ‘>ËÁ*?¬Ê?³õ?»˜?ÃÀ?̆?|Ÿ>Îâ>ÍØ”>ÍÑ>ÍÓ&>ÍÚ>Íßý>ÍâÒ>Íåé>ÍÛè>ÍØ>Í×'>Íîd?òm?·?¯S?·…?ÀÈ?št?ž>ÏR´>Í(>Í™ã>ÍA>ͱm>ÍÅè>ÍÇù>ÍÅ.>ͺ,>Í· >ͲZ>ÍžP>ÍŠ4>ÎUƒ?-&?±?º??œ(>ÏHo>ÎnÒ>Í‚Ù>ÍÉ>Í¢i>Í»…>ÍÕ>Íá7>ÍêL>ÍíS>Íßß>ÍÐ>ͺD>ͳ >Í¢|>͆v>ÎA«?±È?¼Å>Ï?û>Í_ƒ>Ívw>͇Ã>Í">ͼ >ÍÝÊ>Íð>Íþ<>ο>Î>Î>ÍûV>ÍÔ">ͼô>Ͳ³>Í‘>Ís ?±ê?•S>ÍË»>Í[>ÍqN>ÍŠH>ͬ–>ÍÖÏ>Íè">ÎÃ>Î%Û>Î.I>Î;`>Î(>Î>Íòo>ÍÓ¸>ͳÂ>Í—ø>Íy'?Sì>Ï%>Í< >ÍS >Ícù>Í™>ͼ™>ÍÞ´>Î >ÎF?>Îh>Îx7>Îwi>Î[…>Î<Ë>Îâ>Íê>Í´T>͘¢>Í‚M>ÎÀa>Í$>Í5>ÍM2>Í{©>Í—è>ÍÁi>ÍøÇ>Î/A>ÎfW>Μç>ι>η‡>Ε>Îaµ>Î9Š>Íú(>Íʧ>ͧ©>Íqë>Ícs>ÍP>Í+7>ÍB¨>Íe>Í >Í¿>ÎA>ÎXÅ>Ε²>Îì>>Ï*>ÏÃ>Îò{>Χ…>ÎR]>ÎÕ>ÍÆ¿>Í—£>Ír4>Í3>Ía>Í"ö>Í4d>ÍF>Í…Ï>ÍË>Îd>Îl>Îá9>ÏkS>Ï·Š>ÏÃü>Ïz’>Îìç>Îgv>Î>͸ó>ÍzÒ>Í^&>Í!V>Ìêð>Í œ>Í >Í5•>Ígû>Í›3>Íél>ÎYO>Ϧ>Ͻ˜>ЀÏ>У>Ð->Ï!>Ή>ÍíI>Í—Í>Í_1>Í6s>Ͱ>ÌßC>ÌÛÊ>Ìö´>Í >Í&C>Íeè>ͨ1>Î >Ï Ô>ÐBJ>ÑÜ…>ÑúÑ>Фµ>Ï8>ÎKl>ÍÈ>Ísu>Í)³>͈>Ìûÿ>̯u≯€>Ì˵>ÌÎÎ>Ìä >Ìê>Ìú¹>Í>Îeð>Ð]!>Ô{R>Ô•@>Ðe>Î\å>ͺk>ÍI¾>Í®>Ìõ‚>Ìâ™>Ìëò>Ìžú>ÌŸ;>Ì¢{>̨¡>ÌË>Ì’k>Ì Ò>Ì®[>Ì›=>Ìáñ>Î&Î>ÏJ=>Ìðñ>̲µ>Ì‘t>̦(>̯¯>Ì«{>Ì“Š>ÌЩ>̉v>Ì…[>Ìtï>Ìl/>ÌWÀ>ÌLÌ>Ì->ËÈh>Êþ>ÈÙ‰>Ä`7>ÄAN>ÈÞ>Êä>Ë ¡>Ì¡>Ì@Ž>ÌOÔ>ÌgÂ>̤>Ìl>Ì_x>ÌC>Ì3P>Ì.Â>Ëî÷>Ë£—>ËÕ>ʱ>ÈÑL>Æð!>Æêg>È‹)>Êî>Ë)>Ëdÿ>Ë»ó>Ì >Ì*é>Ìžu>ÌR¯>Ì:º>Ì'G>Ì ”>Ëæ‹>˯œ>Ëlt>Êù»>Ê5B>É=J>ÈŽÕ>È‹×>É1…>Ê&Ô>Êο>Ë9â>Ë£i>ËÞû>Ì ž>ÌÕ>Ì0Œ>Ì*ç>Ì>Ëé‘>˹'>ˉ.>ËGW>ÊÍ>ÊTò>Éèi>Éz>Étß>ÉÇû>ÊS;>ÊÞ^>ËD8>Ë>ËÁ>Ëö$>Ì•¾>Ì$m>Ì>Ëûr>ËØ >ˤŒ>Ëw2>Ë/Q>ÊæØ>Ê‘*>ÊLq>Ê×>Ê0>Ê:Ç>ÊzË>Êåž>ËË}>˳W>ËÚM?k >Îýà>Ëÿ>Ëç>>ËÒF>˳ >ËyŽ>ËE‚>ËO>ÊÌx>Ê™š>Êy>ÊlŸ>Ê‚>ÊÆ¦>Ël>ËOG>Ë“>Ë©ú>Ë»|? S?šv>ÎÒT>ËèÌ>ËÕ6>˱ö>Ë„>Ë^]>Ëå>Êû5>ÊÒB>ÊÁ >ÊÊ^>ÊÒg>ËÄ>Ëß>ËG<>Ëu>Ë¡D>ËÃ@?¬?³É?» ?Âß?Ë?[?.ž>θ>ÍÏŠ>ÍÎ>>ÍØ>Íäf>Íãê>ÍÜ?>ÍÜ >ÍÛµ>ÍÞ? ?#V?¼ž?¯D?·«?Àx?À)?«U>ÏLH>Îu¤>Íž>Í£>ͧÞ>Í¿?>ͽß>Í¿­>ͽ9>ͱº>ͪG>ÍŸ>ÎXÁ>Îkì?'G?¯Š?¸N?¹f?§‹>Ï6o>Í}f>Íá>Í %>Í·l>ÍÌ+>Íâ>ÍÜã>ÍÝ!>ÍáÀ>ÍËâ>ͱE>ͧ¼>Ížö>Í…»>ÎF2?°‚?»=?¡H>ÎüÛ>Íjì>͆Õ>ÍžB>Í·»>ÍÖ:>ÍíÎ>Íóþ>ÍýL>Íÿ\>ÍõÓ>Íç”>ÍÎ}>;®>Í«t>ÍŠœ>Ím?± ?”Ä>Ï+>Í[M>Íp->͇>Íž4>ÍÆ.>Íé{>Îk>ÎQ>Î!8>Î->Îè>ÎÂ>Íìe>ÍÐe>ͬõ>ÍM>Ío?{Á>Ï->Í7¢>ÍXt>Íl1>Í‘J>;l>ÍÛÍ>ÎÜ>Î1ß>ÎSt>Îaf>ÎhÉ>ÎN>Î/þ>Ϋ>ÍáÍ>ͯ>͘W>Íw”?›>ͨ >Í3,>ÍG >ÍlQ>ÍŽÈ>Ͷ†>Íöþ>ÎÖ>ÎS~>· >Ι÷>ΘÆ>΄>ÎQ†>Î(£>Íî>ÍÁb>Í™|>ÍyÃ>ÎzN>ÍÍ>Í#h>Í=—>Í_>Í…ä>Í¿%>ÍüÐ>Î4é>Îq>αó>Îå¥>ÎãÌ>ξ>ÎyË>Î=U>Î>ÍÇ­>Í’ë>Ín,>Í8v>Íx>ͪ>Í6ñ>ÍFÝ>Íw2>͸v>Í÷ð>ÎH9>γ>ÏHÉ>ÏU§>ÏWˆ>Ï>Õ>ÎÂN>Î;´>Íâ>Ͱe>ÍtY>ÍU—>Í'>ÌîZ>Í >Íì>Í1¯>Í^¦>͈‹>ÍÐ2>Î>->Îà6>ÏK•>Ïç5>Ða>ÏŠë>Îëý>ÎP>ÍÚZ>ÍxW>ÍN±>Í).>ÍØ>Ìâ5>Ìàþ>Ìäñ>Í>Í#¶>ÍIj>͈Œ>Î />Ψ‹>Ï…Ê>ÐJ‹>Ðc>ÏÖ1>βt>Î-¦>Í®r>Í\¨>Í+ù>Í >Ìøi>̵4>ÌÁí>ÌÉ>ÌÎo>Ìèƒ>Ìô\>Ìûµ>Ív2>Íþc>Ï!¬>Ј.>З">Îý›>ÍøÁ>Íž÷>Í ¸>Í ©>Ìõ1>ÌÚ3>Ìî>Ì¢M>Ì \>Ì©t>̨>Ì•4>ÌŸ>>̺ ≯>Ì€>ÌÀ¸>Í¥>̳M>̲œ>Ìšj>Ì—¸>ÌÂÅ>̵A>Ì¥á>̘è>ÌÌ>̃">Ì•>ÌsŽ>ÌhD>Ìc >ÌCá>ÌR>Ëѹ>Ë2‚>Ê÷>É>ÈÝÉ>Ê.%>Ëpc>Ë·œ>Ì4>Ì=g>ÌQÇ>Ìp¹>Ì«c>ÌoK>Ì`“>ÌOh>Ì5ù>Ì)$>ËñŸ>ËÈ^>ËF>ʱÓ>ÉœÞ>È>ÈÂ>ɈI>ÊŒ>Ë >Ë}d>ËÓ&>Ìþ>Ì)‡>̤ë>ÌbØ>ÌF—>Ì+Ü>Ìù>Ëì²>˹©>ËŠ >Ë“>Ês7>ÉêÕ>É4z>É+3>É´ñ>Êjw>ÊýÖ>Ëd±>˲>Ëê£>Ì>̘ƒ>Ì4>Ì( >Ì  >Ëóþ>ËÊÅ>Ë’4>ËPù>Êóü>Ê–ö>Êv>ÉóÕ>ÉÌü>Ê‚>Êhß>Êû$>ËSB>Ëœ%>ËÅ>Ëïª?Ïñ>Îñ¤>̹>Ëÿ¾>ËÛV>˪Â>Ë‚>Ë<´>Ë>ʲç>ÊiG>Ê0T>Ê<(>Êh|>ʤü>Êõ¡>ËAB>ˆ&>˵ë>ËàU?ÑÑ>ÌÞé>Ì®>Ëî >ËÜ¿>Ë´Ð>Ët>ËML>Ë$e>Êã.>Ê®>Ê’">ÊŒW>ʯÛ>ÊÛV>Ë >ËUŠ>Ë…³>˰µ>ËÌÿ?$r?‰>ÎÖ >Ëïd>ËÛ5>Ë·ù>ËŽ>Ëa!>Ë&Ü>Ë >Êê6>ÊáX>ÊßÛ>Êì>>Ë>Ë3Ž>ËOù>Ëx³>˧ì>ËÌ?¬5?³y?¹­?Àª?É|?…,?RØ>Îå>ÍÔ(>ÍÊW>ÍÖ>Íà+>ÍÛ3>ÍÓ¾>ÍÓ>Íݸ>Íøp?“÷?k&?¼?°½?º¶?ÁÍ?É?©¥>ÏQï>ÎwÏ>Ím>͘©>Í©.>Í»æ>Í´">͵÷>Ͷ;>ͯâ>ͦï>Í–t>Îdã>Îm!?Y›?«0?±æ?½ø?žº>ÏH>Íxˆ>͈[>Í%>ͱó>ÍÉ5>ÍÍ>ÍË·>ÍÒ„>ÍÒC>͸Ž>Í¢–>Íš\>Í•>Í}à>Î?Ó?®?·˜?•×>Ï&ç>Í`j>͆<>Íœ>ͱœ>ÍÈ4>ÍÚØ>ÍÚ´>Íé†>ÍþÀ>ÍçŽ>ÍÚ²>ÍÆ±>ͺ7>Í¢í>̓Ç>Íl•?¯ ?ó>ÏV>ÍXQ>Íkû>̓ >Í£Ü>ÍÁ_>Íב>Íéß>Δ>Î ·>ÎÓ>Î >ÍøB>ÍÛÜ>Ͷ|>ÍŸ>Í>Ík?ŒZ>Ïw>Í3w>ÍN>Ípä>Í‹>Í©p>ÍÈØ>Íï>Îç>Î/ù>ÎB‡>Î-ä>Î-Z>Î>Íñ¯>ÍË€>Í£9>ÍŒQ>ÍkÄ?-¯>Í¡v>Í(É>Í>¸>Í_º>Í€A>Í¡º>Í×È>μ>Î6›>ÎYÀ>Îu|>Îo{>Îbâ>Î5|>ÎÆ>Íè>͵Ì>ÍË>ÍiC>Î¥>ÍÅ>Í">Í1>ÍFÊ>ÍyD>Í®›>Íß+>Î ”>Î1>Îne>ΠÅ>Μd>Îo>ÎBÌ>Î(>Îb>ͲN>̓?>Í_ì>Í5Ñ>ÌýC>ÍË>Í+‡>Í=>Íb>Íš…>ÍÑ>Îû>Îyw>ÎÔ”>ÎÙV>ÎÔP>ÎÆè>Η­>Î:>Í®>Í×>ÍkÛ>ÍNF>Í*/>Ìæ >ÌöD>ÍT>Í(¸>ÍEå>Íx÷>ÍÇ>Íü³>ÎxÓ>Έ>Ïr>Ï%>Îè&>ÎxJ>Δ>ÍÇó>Íwú>ÍHÆ>Í&y>Ík>ÌØ—>Ìß~>Ìé >Í ß>Í>Í9k>ÍR>ÍÍø>ÎDI>Η9>Î÷˜>Ï&Ì>Îß>ÎMÆ>Íå>Ít >ÍC<>Í¥>Ík>Ìôe>̾^>ÌÄÁ>ÌÌO>ÌÑ­>Ìá*>Ìäï>ͨ>ÍEÈ>Í“>ÎÛ>Îj}>ÎeË>Í÷>ÍxW>ÍTÛ>Í >Ìú%>ÌíÛ>ÌÐ>Ìòe>̯Þ>ÌÎ>Ì›Ö>Ì©ª>Ìš>ÌŸã>̶ú>Ì¢Ô>ÌŽ¾≯…>ÌÖ@>ÌÂ>Ì s>ÌŸ>ÌØ>Ì»Á>̯‘>Ì«V>Ì£+>Ìλ>Ìz6>Ì‚‹>Ìxï>Ìl~>Ìgò>ÌJ~>Ì(>>Ëî½>Ë©p>ËCR>˺>ÊÔ²>ËN>Ë¢;>ËæŠ>Ì#Û>Ì;ù>ÌVä>Ìs½>̪à>ÌqP>Ìkç>ÌXC>Ì5T>Ì+v>̱>ËÓÆ>ˉÉ>Ë(±>Ê}œ>Ê>Ê"Ñ>Êr>Ë,>ËYÜ>˧„>ËñZ>Ì×>Ì8>Ì«j>ÌSÏ>Ì>>Ì7À>Ì#%>Ëúå>ËÛÚ>Ë´œ>Ë5¬>ÊÀi>Êu_>Ê>ʧ>ÊSû>ÊÆƒ>Ë?ß>ËŽG>ËÁä>Ëñ!>Ì ¯>Ì«ð>ÌJò>Ì,¥>ÌÁ>ËöE>Ëä“>˲‡>Ë`¢>ËG>Êêd>ʤ«>ÊG6>ÊG/>Ê„>ÊÑ >Ë"x>Ën°>Ë¥å>ËÝB>Ë÷R?dí>Ïe>Ì>ÌV>ËëÃ>˾Å>Ë>Ë\_>Ë-Ü>Êæ¬>Ê©„>ʆv>Êþ>ʳÁ>ÊÞr>Ëø>Ën…>Ë¢‰>˾õ>Ëëa?>̼:>Ìh>Ëí<>ËÝû>˺³>ËŠd>Ëj¢>Ë>ä>Ë>ÊÔQ>ÊÈU>ÊÏ‘>Êà>ÊþË>Ë-‘>ËhÙ>ËK>˰>ËÔt?-9?9>ÎäÒ>Ëôã>ËÞ<>˸;>Ëû>Ël¸>Ë?U>Ë*”>Ëš>Ën>Ë â>ËI>Ë"é>ËFô>ËZª>ˈˆ>˼‹>ËÑÉ?ª|?±w?¸?¾Œ?Æo?·s?€O?L>ξ½>ÍØU>ÍÚo>Íà>Í×>ÍÒ³>Í×Ì>Íëý? 4›?:’?«ÿ?À‰?­‹?¶Á?ÁÒ?Ç]?¢Þ?¤u>Ï?¼>Îi€>ÍÊ>Í–I>ͦˆ>Í®c>ͪã>Í¥[>ÍŸ{>Íž²>ÎVr>Îlú? ¶è?R?ª?±v?¹N?žô>ÏAâ>ÎJ%>̓|>Í¡Ú>Í­f>Í´«>͹L>ͳà>͸ÿ>Í»L>ͬö>Íi>Í‹>ÍÀ>Î0d>Î^¯?¬?´ß?–>Ï<¼>Ía$>Íww>Í >Í¡Ä>Í»P>ÍÆÄ>ÍÌ„>ÍÑÏ>ÍØÍ>ÍÒq>ÍÉJ>͸˜>Í¢œ>ÍŽD>Íy>Íj—?¬–?¬U>Ï#®>ÍDI>Íed>Íï>;>ͨ·>͸2>ÍÖÃ>Íè“>Ííó>Íú2>Íï«>Íà?>ÍÇñ>Í«>Í•$>Í|æ>Íb"?‘–?—>Í£ý>Í>>Íaà>Í€¼>Í’î>ͲY>ÍÜ™>Íû»>Î>ÎÈ>Îd>Î í>Íë%>ÍÕ>ͺ >ͨ>Íb>ÍY$?qV>ÎÞ‰>Í'\>Í:ý>ÍSÀ>ÍpW>Í´>ͺø>Íã•>ÎL>Î)>Î?0>Î:>Î+|>Î }>ÍÖ´>ÍËŸ>Í¥¯>Íu&>ÍU? >Í”>ÍÃ>Í+§>Í?T>Ío=>Í›>ͯÜ>ÍÝØ>Ο>Î/\>ÎIÅ>ÎZŠ>Î?÷>Î ¤>Íæa>Íͯ>ÍŽÜ>Íte>ÍX÷>ÎV†>Ìñí>Íì>Í!ª>Í7>ÍJú>Í~þ>Í®ì>ÍæÓ>Î?ÿ>ÎMK>Î_|>ÎXW>Îjg>Î->Íæ¯>Í2>Í‚A>Íg2>ÍG.>Í+S>Ìè>Ìþ>Í Õ>Íü>Í<Â>ÍR×>Íš—>;õ>Îä>ÎEO>΄N>Îf)>Î2Ý>Î70>Íè¶>Í6>ÍkÕ>Í:Ð>Íë>Í">Ìט>ÌâÂ>Ìú€>Ìû`>Í >Í)9>Í5Ú>Í~Ù>͉>Îä>Î0›>Î>×>Î#æ>ÍÊõ>͉>ÍiU>Í?$>ͪ>ÌõÄ>Í">̹a>Ì»d>ÌÇ>ÌÒ@>ÌÞ:>Ìì²>Í0>ÍŽ>ÍDH>Í‹G>Í•e>ͯð>Ínû>ÍKý>ÍË>ÍY>Ìã˜>ÌÕŸ>Ìë>Ìô6>Ì¢¦>̘w>Ì¡>ÌŸ+>Ì•A>Ì”»>̤>Ì•È>ÌŽÏ>Ì£>Ì­ª>̾Ä>Ì«&>ÌÇV>ÌÀÉ>Ì>Ì–Ò>̪º>̨H>ÌÉI>̆A>ÌŒ§>Ì~’>Ìi<>ÌbW>ÌO>Ì o>ÌÎ>Ì0>Ë·Ê>˵é>Ë’Ÿ>ËÐ~>Ë×é>Ì(£>ÌS>ÌMY>Ìdy>Ìv«>Ì»N>Ìo‹>Ìg9>Ì_ô>ÌJx>Ì6õ>Ì>Ëþ>ËÔZ>ËŽh>Ë3ò>ËÑ>Ë Ü>Ë1À>Ë“I>˧y>ËËQ>Ì®>Ìj>Ì=<≯7>Ì[ã>ÌFO>Ì6>Ì(ã>Ì>Ëôù>ËÐÈ>ËŒ}>ˤ>Êã]>ÊàO>ÊÎË>ÊëÚ>Ë/`>Ëri>˪'>Ëà¼>Ìæ>Ìý?Æþ>ÏF>Ì.^>Ì>Ì>Ëì§>ËÑŒ>Ë™>ËTå>Ë=Õ>Ë8>ÊÖÌ>ʰ“>Êå÷>Ë(ª>ËOH>Ë >ËÅ>Ëðö>Ìè?ö!>ÌðI>ÌD>Ì ‡>ËøÞ>ËÞ>˃>Ë{>ËW!>Ë"ì>Ë>ÊØF>Êë<>Ë*>Ë"Ê>ËR¡>˃é>˱">ËËÉ>ËðV?)³?Å>ÎÉ>>Ì">Ëíâ>ËÉ0>Ë¥>ˉm>ËY">Ë1Ï>Ë>Ë•>Ë >Ëß>Ë*§>ËKw>ËA>˸>˺M>ËâÞ?6˜?û^>ÌÍ2>Ëú>Ëç,>ËÃ_>Ë®9>Ën>Ë_>ËEÄ>Ë!ù>Ë+>Ë#Ç>Ë:&>ËEh>ËcÈ>Ë‚•>Ëž¼>ËÃ[>Ëßœ?¨R?®´?µ¼?¼z?ÂÐ?Ä¡?Œ]?iÊ?r‹>ÎÕŽ>Íçy>ÍÞÐ>Í×ú>Íß(>Íô? %†?M?‚ð?º¡?¿F?©å?±¾?»è?Ä”?¿Ñ?£>ÏPÎ>ÎpÒ>Îa/>͈>Í”§>Íš†>͘G>Í•Œ>ÍŽ‚>ÎTµ>Îu[>ÎqL?#×?¶q?©}?±?¹h?º_?À»>Ï3k>Ís>ÍŠ>Í›ö>Í¢ô>ͤF>Í¡Ü>ͬ²>͵>Í¡ó>Í•C>Íy€>Ísÿ>Î<¹? ¾?ª?²4?±«?¬4>Ï>ÍkF>Í >Í’º>Íœl>ͬ>;r>ÍÂS>Í»:>͵‹>Í´¹>ͨÐ>ÍŽÇ>Ízå>Ígv>Î9?©Ê?²,?°x>Îð)>ÍaL>Ípè>͇ß>Íž>Í­>ÍÀW>ÍÙ´>ÍÓî>ÍÏ#>ÍÆÆ>ÍÂ>ͪ>Í›>ÍQ>ÍrÍ>Íaû?•?~.>Îä >Í0#>ÍS>Ípª>ÍÅ>Í“Ü>ͼ9>ÍÕ…>ÍÓr>ÍÞ¡>Íê¾>Íå„>ÍÄÂ>Ͱ">ÍŸW>Í«>Íj¢>ÍOŽ?~>ϲ>ÍX>Í(Ñ>Í8ß>Í^ >Í|G>Í™S>ͺÃ>ÍÞ >Íï>ÍûŸ>Î &>Íñë>ÍÛ§>Í¿¼>Í¡©>͇e>Íc@>ÍAÅ?]>ÍŒ¤>Í>Í/,>Í7µ>ÍOÑ>Í{ >Í‘->͹D>ÍÛ;>Íê³>ÎÍ>έ>Íÿp>ÍÛ{>͵6>͘É>ͱ>Íh8>ÍC§?Ï>Ív>>Í Ç>Íb>Í/>ÍCq>Íd>ÍŽ¥>ÍÂ#>Íã³>Íðÿ>ÎU>ÍïÙ>Íø³>Íæ`>;·>ÍŒú>ÍcÒ>ÍJ$>Í1£>Î^Á>Ìëh>Ìì¯>Ìô->ͧ>Í+>Í3ž>Íaƒ>̓v>Ͳä>ÍÓ->ÍéN>Í÷ >Íæ±>Í¿¯>Í›™>Í|–>ÍK³>Í0ü>Í€>Í é>ÌØT>Ìãy>Ìë>Ì÷½>Í">Ít>Í(+>ÍH{>͈o>͹>͘í>ͦx>ÍŽ‚>Í’¢>ÍJà>Í/D>Í>ÌøK>Ìîø>ÍÁ>Ì·þ>ÌÆ>ÌÄÆ>ÌÂM>ÌÈ“>Ìéi>Ìþý>ÍÍ>ÍŸ>Í$b>ÍE½>ÍIb>ÍE–>ÍÙ>Í0>Ìÿl>Ìè+>ÌÔ>ÌÆÁ>Ìî®>Ìô>Ì“¦>Ìž‚>Ì—÷>Ì‘ß>Ì›>>Ìža>Ìšþ>Ìn>̽>Ì¿Ì>Ì­E>Ì”ê>̨Ö>ÌÍ>ÌÉ>̨i>̤ì>̪,>ÌÓþ>Ì…>̇¤>ÌŠŠ>Ìu >Ìhƒ>Ì]±>ÌEI>Ì#€>Ì#D>Ìp>Ì ¦>ËêÚ>ÌÔ>Ì&„>Ì5>Ì;>ÌR¤>Ìc“>ÌvB>Ìʳ>Ìm¨>ÌcØ>Ì\>ÌQ#>ÌHQ>Ì.Á>Ì0z>ÌI>Ëϱ>ËŸ°>Ë¡>Ë­>ËŸ±>ËÉK>ËÕ|>Ì"Ì>Ì!8>Ì2^>ÌG‰?É->Ï)î>ÌO->Ì1ì>Ì6Y>Ì%¢>Ëþó>ËåÓ>ËÚ>Ë‹Ò>ËZÂ>Ë8¹>ËL`>ËsÍ>˘Î>Ë›3>Ë¡„>ËìO>ÌÚ>Ì3Í? @>Ìúð>Ì6Ö>Ì*4>Ìn>Ëûà>Ëãc>˼Û>ˆ>Ël\>ËW×>Ë#g>Ë#û>ËMÅ>Ëq*>Ëž“>ˬý>ËÔ>Ëûì>Ìã?.;>Ìþ>Ì"õ>ÌF>ËúI>ËñÂ>ËÄÙ>Ë  >Ë!>Ëi*>ËWx>Ë6?>ËE>ËA¬>ËZ”>Ëx>>Ë¥³>ËÈO>Ëä­>Ëýø?8Â?‚">Îî®>Ìy>ËôT>ËÏ$>ËÁ>˪>Ëz>Ëb^>ËMD>Ë;>>Ë9ù>ËC}>ËY@>ˆ•>ˬ>˸a>ËÑL>Ëö?@í?<?'É>κ£>Ëñ>ËÕ~>ËÅ!>ËšÀ>Ë}C>Ëj}>ËPÊ>ËJ£>Ë=”>ËY‰>Ën²>Ë„Ñ>Ë¥>Ë»>ËÔ`>Ëêè?¥Ú?«•?²?¸a?¾:?ß?¾g?Šê?eB?yÈ?¸>ÎÛí>Í÷Š? 4-?г?9X?ƒš?µƒ?Äu?¼t?§ ?­ð?¶U?¾T?Ãä?Ÿp?Øõ>ÏF>Îr´>Îfj>Îo>͆;>Í„ì>ÎWX>Îdö>ÎrT>ÎrÑ? þ´?^Ï?¾¤?§D?®¯?¶¸?¿Æ?–{>ÏEà>ÎI•>Íwµ>͉©>Í™>Í“¦>Í]>Í—@>ͧ>Í2>Í€G>Íj¨>Î'¬>Î[.?bŸ?§‹?¯V?¹?’Î>Ï8->Í`4>Ípš>Íyr>Í¿>Í%>ÍŸX>ͤ²>Íž>Í›ƒ>Í™Á>Ís>Í}>Íw=>Í]>Î*á?¦Ä?®1?ƒ¹>Ïë>ÍKl>Í_5>Írƒ>̓#>Í >͘>Í´â>ͳû>ͪ<>ͤ>Í¢½>ÍŽë>Í€ì>Írù>Ía™>ÍLt?˜Ÿ?¨2>Ïy>Í)>ÍK >ÍY•>ÍfÙ>Í€$>ͤÊ>ͪ>Í«_>Í·¹>͹Ê>ͬ>Í®¹>Íš–>Ím>Í^1>Í\7>ÍH1?‰Ó?£>Í‘¾>Í#Î>Í2D>ÍJj>Íb+>Í}>Í—8>Í´%>ͼl>;_>ÍÌ8>ÍÂü>ͨœ>Íš&>Í€x>Íf$>ÍKÝ>Í=Ï?yí>ÎÑå>Íì>ÍW>Í&Q>ÍEY>ÍaÚ>Íuh>Ív>Í©‚>Ͷ…>ÍÉb>ÍÞ¡>ÍËø>Ͱ^>Í‹›>Íz·>ÍeR>ÍH˜>Í-?_>Íz{>Í'>Íœ>Íy>Í1|>ÍJo>Íi8>Í|û>Í–>ͯD>ͽ¨>Ͷø>ͦ©>Í–N>Í€>Ín6>ÍQN>Í=»>Ír?+>Ígþ>Ìå±>Ìö˜>Í >ÍÎ>Íÿ>ÍDû>Í`ÿ>͈ë>Í‘­>Í–²>Íu>ÍžÖ>̓>ÍrÎ>ÍOö>Í6 >Íß>Íf? ýŸ>ÍW9>Ìáï>ÌÞá>Ìè¢>Ìý¸>ÍW>Í >Í(€>ÍU.>Í\Ä>ÍoF>Íq)>ÍVP>ÍG´>Í0Î>ÍŒ>Í>Ìðå>Ìè¯>Î20>Ì·h>̵L>Ì¿Ÿ>ÌÄÁ>Ìñ>ÌÚœ>Ììl>ÌäÇ>ÌÞò>Ìö†>ÍF>ÍÌ>Í4>Íœ>ÌóU>Ìå%>ÌÞÑ>ÌÓØ>ÌÇÄ>Ìò>Ì›>Ìžï>̤;>Ìš·>Ìœ=>̨‹>Ì¥ì>Ìžÿ>ÌŒ³>Ì£>̧ª>Ì©ÿ>Ìž¨>Ì¡œ>Ì´ì>̱§>Ì£º>̧¦>̨V?Ú>ÏH>̉c>Ì‹G>Ìv­>Ìnú>ÌhÂ>ÌNþ>ÌG5>Ì6€>Ì;c>Ì;Ò>Ì#[>Ì&³>ÌE7>ÌXó>ÌXü>Ìjê>Ìn>>̃j? È‘>Í Á>Ìs•>Ì[›>ÌV®>ÌO">Ì>Ð>Ì4,>Ì%ì>Ìd>Ëç$>Ëç>Ëàê>ËíŽ>Ëýc>Ìp>Ì/§>Ì0>ÌB„>ÌVk?Ô…>Ìù¼>ÌDÅ>Ì@’>Ì@Í>Ì;t>ÌÆ>Ì ’>ËöÏ>ËÝ >ˬ.>Ë—>Ë@>˹d>ËË'>ËÕÆ>Ëéu>Ìî>Ì)9>Ì8¥?(N>ÌØÜ>ÌL]>Ì5³>Ì#Ÿ>Ìü>Ëÿ€>ËÔ>˵->Ë¡·>Ë‘0>ËwÕ>Ë|ï>Ët>˘Ì>˺¾>ËÌý>Ëã>Ì a>Ìë?:Ú?ú~>ÎÔ¯>Ìh>Ì ö>Ì•>ËÙ>ËÀÉ>˨ù>Ë•ð>ËŒ=>ËŽ>ˇÛ>Ë^>ËŒž>Ë›ê>ËÈX>Ëß‚>Ëõ >Ì?K²?S>Ìéº>Ìþ>Ìy>Ëém>ËàÄ>ËÄù>Ëž>Ë>Ë‹>Ëy?>ËoG>Ëq>Ëì>Ë©ê>ËÈå>ËÖá>ËçJ>Ëö%?Iø?C4?z>Îîê>ÌÛ>ËâN>ËÉ2>˺f>˪.>Ë–Á>Ë~#>Ëzƒ>Ën¤>Ë€®>Ë“Å>˧ù>˼ë>ËÑW>ËæZ>Ëý£?£-?¨G?­Ö?³„?¹ ?¾A?Âm?¹Ä?ºp?y?cÒ?¼$?1)?#Ž?k?«Õ?ºó?ħ?¾ú?¸%?¤F?ªD?°É?·{?½ÿ?ºÁ?•~?ç?1A>ÏK>Îy>Îb^>ÎgÃ>Îu)>Îl? Â'?K?^?·x?¼ÿ?¤Ó?«›?²«?º›?¾?>Ï9(>ÎI«>ÎNw>Í|>Í~:>Í{Ê>Íz×>͆½>Íy>Î/b>ÎAa>ÎYÔ? 'ˆ?†á?¥?¬v?´^?¹g?àA>Ï >Í[->Íbª>Ín>Íi>Í•‚>ÍŠÍ>Í~©>Í|³>ÍL>Í‚É>Ípj>Íe¸>ÎE? !?£p?©Ñ?­$?Ó@>Îë˜>ÍMn>Íbç>Íkb>Íwc>Íe>Í…->Í‹D>ÍŒ>ÍŒè>͆Î>ÍoE>Ífþ>Íi#>ÍXŒ>ÎN??§ü?êý>ÎÜ>Í9_>Í@³>Í\[>ÍjN>Í~|>̓>Íc>͘>Í”®>Í•->ÍŒT>Íw’>Íd>ÍLr>ÍHö>Í8\?–k?žI>Îç@>Í´>Í2>Í:¶>Í@Ÿ>Íc³>Í~é>ÍŠ˜>͉@>Í—_>Íš#>ͳ>͆ã>Í|>Íg>ÍGp>Í>>Í3B?Õ?ÏO>Í„‘>Íä>ÍÖ>Í:À>ÍGî>ÍTÔ>Íg>Í}r>Í~>>Í›¶>Í›Å>Í“>̓—>ÍkÔ>Íg>>ÍJ:>Í*¼>ÍØ?„? ·]>ÍnY>Ìþ¡>Í3>Í(ù>Í8—>ÍDø>ÍR¡>ÍpÎ>Íy">Íy>>͆‚>Ívõ>Í`É>ÍU½>ÍAõ>Í1”>Í>>͉?iú>ΜÃ>ÌâŠ>Ìêï>Ìù²>Ìÿ˜>Í L>Í&¼>Í=ó>ÍW>Ígì>ÍU >Ía*>Íb7>ÍX¦>ÍK´>Í7ý>ÍÌ>Íï>Ìî€?G0>ÍV>ÌÑÃ>ÌÖ*>Ì݉>ÌéF>Ìôç>Íw>ÍÀ>Í@>Í-¡>Í=M>Í4z>Í#P>Í!]>ÍN>Ìþ>Ìïc>Ìå9>ÌÚ‰?Xƒ>Í9”≯þ>̾•>Ì¿§≯©>ÌÐÉ>ÌÞ¹>ÌÕÿ>ÌÝÊ>Ìçž>Ìû?>Ìí>ÌðØ>ÌßÕ>ÌÙ >ÌÍ~>Ì×|>ÌÏ£>ÌÃÆ? -N>ÍC>̤E>̨/>Ìšá>Ìš >̨‰>Ì«Ó>̨>Ì”>Ì>Ì£è>̦€>Ì©>̨Õ>̬i>Ì¥H>̤y>Ì©/>̧1?êT>Í)ß>ÌŽh>ÌÞ>̃³>Ìz%>ÌoJ>Ì`>Ì\5>Ì[f>ÌU>ÌR€>ÌO\>ÌN?>Ì]ð>Ìg>Ìk]>Ìz:>Ì{‹>Ì‚‡?(Ù>Ìýâ>Ìoe>Ìkœ>Ì^Ò>ÌZ >ÌQ>Ì?Ú>Ì,á>ÌE>ÌÖ>ÌÉ>Ìà>ÌX>̈>Ì4í>Ì>_>ÌB>ÌV8>Ìgß?=? š>Îù\>ÌIT>ÌK >Ì<>Ì#;>̾>Ì }>Ëù©>Ëé¦>Ë×>Ëêf>ËîU>Ëü>ËüÍ>Ì]>Ì(>Ì9½>ÌIZ?G¬? W >Ì×½>Ì:ï>Ì/I>Ìb>Ì >ËôŸ>Ëéõ>ËÚh>ËÈ>˶¦>Ë­m>ËÁÀ>ËÉ5>ËâM>Ëûu>Ì¥>̦>Ì3n?LW?Ò>ÌÚÖ>Ì#ø>Ì9>Ìq>Ì9>Ëã¼>ËÑ'>ËÀ‹>ˬF>Ë­Ô>ˬp>˱V>˽ñ>ËÌå>Ëài>Ëø >Ì>Ì÷?Qá?M?ýq>ÎÉ*>Ì >Ëÿò>ËøÆ>Ëåa>ËÇ >˱>Ë­¡>˨F>Ë >Ëœf>˶>ËÄ>ËÚí>Ëï0>Ëþ>Ìú?N?H3?Ž?ö>κ>Ëöœ>ËÚ>ËÕÁ>ËÉ?>˺S>Ë®¤>Ë«;>ËŸÀ>˨ò>Ë´í>ËÊÕ>ËÙ[>Ëãè>Ëñü>Ìž? G?¤Õ?©ª?®¬?³¯?¸?»F?¾D?ÁK?¿?Ám?¾ï?º·?¾?½"?À­?¿x?¼¬?¸:?³B? õ?¦Â?¬!?±Ô?·¬?½A?¾)?¾¾?œ˜? Ó?vÅ?}õ??v?‚~?P1?¶Q?¾Ó?½?·?¡p?¨Á?®!?´|?»À?º?/Ó?tÊ>Ï9±>ÎEÑ>ÎJÊ>ÎC€>ÎD>ÎL†>ÎDÇ>ÎZ°? –?ŠA?†¡?¹¿?¡È?¬ü?¯|?¶$?µ³?;³>Ï»>Î5,>Íb›>ÍeA>Íjm>Ínð>Ím>Ía£>Íe÷>Íl>Î"c>Î4u? œ?޳?ž?£Š?¬·?¬q? S>Îò,>ÍOM>Í[E>ÍcN>Ífú>Ífv>Íl2>Ís&>ÍnQ>Íi>Í[X>ÍP”>ÍQF>Îõ? A“?š6?¡¯?£œ?*¡>Îã‰>Í1>ÍCw>ÍQ<>ÍWn>Ídk>ÍnB>ÍvÍ>Ím>Íp‡>ÍjÎ>Íc§>ÍNB>Í9n>Í5Û>Íìr?–y?¡i?Ñ>ÎÙ_>Í$>Í)Ø>Í1z>ÍJ>Í`K>Íc¡>ÍcÏ>Íiä>Íx>Ís>Íg>ÍRÀ>ÍM>Í<>Í1½>Í!L?\?šz? 5,>͆˜>Í‚>Í!õ>Í/»>ÍCÁ>ÍK >ÍXI>Í]8>ÍkÎ>Íc>ÍbS>Íhþ>ÍO`>ÍBø>Í8#>Í“>Í$?‰?‰@>Ϊ‰>Ìì’>Íá>Í>Í"þ>Í+l>Í;°>ÍKÈ>ÍPÔ>ÍVC>Í[@>ÍU˜>ÍÍ6ž>Í-|>Í 2>Í>Í þ?w±?Úð>ÍW>ÌÛþ>Ìê">Ìùo>Ím>Í >Í„>Í,#>Í9N>Í)É>Í2v>Í>„>Í.æ>Í'à>Í7>ͽ>Ì÷S>Ìî+?e>? ˜á>ÍB¦>ÌÍ¿>ÌÐT>Ìßð>Ìô«>Ìî¹>ÌúA>Í!>ÍK>Íß>Í>ÍÄ>Íâ>Ìù(>Ìî¸>Ìâ>ÌÓ#>ÌÉ?W)? ^ >Í/÷>Ì»9>̽b>Ì»«>ÌÀ>ÌÉâ>ÌÓØ>ÌÜã>ÌØ“>Ìæ“>ÌÛ¨>Ìϼ>ÌÙÒ>ÌÌh>ÌËs>ÌÆ >ÌÀ>̺­?Ej? _±>Í&>̦}>Ì› >̘e>Ì£(>̪Â>Ì¢ã>Ì™©>Ìžä>̤C>̤!>Ì®^>Ì«Ï>̤ý>Ì¡->Ì¥Ë>̤Î>Ì£†?K? D=>͹>Ì•V>̈«>ÌA>Ìw_>ÌpB>Ìjê>Ìeä>Ìh‚>Ì]Í>Ìdï>Ìd¿>Ìk>Ìyü>Ìx>̈c>̇>̉]?Eä? V«>Ìãï>Ìup>Ìh>Ìe¢>Ì[1>ÌR¢>ÌB1>Ì2[>Ì<3>Ì5‡>Ì>c>Ì34>Ì3->ÌK•>ÌH’>ÌN…>Ìbç>Ìt¾?KÆ?É>Ìùz>ÌNl>ÌRŒ>ÌFS>Ì6ƒ>Ì$>Ìk>ÌÉ>Ì>ÌS>Ì”>Ì í>ÌH>ÌB>Ì+>Ì6>ÌHD>ÌT–?SR?Mb?&K>ÎìE>Ì<.>Ì+">Ì(Q>ÌÞ>Ì™>Ëôî>Ëç>ËäŒ>ËÚÙ>Ëë_>Ëñ^>Ì7>ÌÓ>Ì>Ì'ˆ>Ì8’?R‡?S=? ¯5>ÌÉ(>Ì,X>Ì„>Ì(>Ì >Ëþó>Ëãm>Ë×…>Ëݰ>ËÔ€>ËÙÀ>Ëä>Ëõb>ËýT>Ì>ÌÇ>Ì%Þ?R˜?R?Ž?~>ÎÂ=>Ì,>Ì>Ëñê>Ëá>ËÙá>ËÔ >ËД>ËÔ3>ËÍ‚>ËÕ*>ËâD>Ë÷j>Ì Œ>Ì¢>Ìç?P¦?Lž?E3?/h?'(>θJ>Ëú_>Ëïs>Ëà*>ËÖ†>ËÔD>Ëʹ>ËÂÑ>ËÍS>Ë× >ËèÎ>Ëöú>Ëük>ÌÛ?cx?•?—¥?šP?œý?Ÿ¯?¢b?¥ ?§{?©?«'?¬/?¬¡?¬T?¬?«s?©¼?§ ?¥$?¢r?Ÿ¡?—”?š»?à? ã?£Ú?¦ö?ª(?­*?¯Ó?±Ï?³?³±?³„?³’?²î?°i?­­?ª¨?§s?¤?š!?í?¡Œ?¤å?§å?«y?¯„?³H?¶ä?¹D?º«?»d?»Q?»U?º…?·‘?´H?°·?¬÷?¨û?œ¸?¡?¥,?©L?­?±?µá?ºf?¾À?Á>?¢?Ã`?Ã,?Âà?ÁÐ?¾é?»d?·]?³.?®Y?Ÿ[?¤?¨Ð?­µ?²m?·G?¼à?Â?Ç1?ÉÂ?Ë ?˘?Ë%?Êö?ÊV?Ç'?ÃE?¾ª?ºð?´p?¡û?§1?¬e?±å?·D?½?Â?Åú?ÄT?%?zÉ?vv?tÓ?}â?‡K?¹æ?Ǧ?Åt?Àç?¹ÏƒÍ>·a?³Ó?O?‚7??Àú?Ę?¼°?¦ó?­ ?³?¹É?Àa?Ãï?—ý?u8?R·>ÏŒs>΃÷>Íá‘>ÍÕ\>Îc>Δ?ÇL?kÀ?Œ?ºú?¿Z?©)?¯¯?¶”?½Ì?Å?Á¢?ž?P>Ïr¹>Íæþ>Íá>ÍÕ>ÍÎF>ÍÓ[>ÍÞ‘>Îlr?Úf?dç?º¿?Á{?ªû?±Ñ?¹.?ÀÕ?È£?ŽÏ?\Å>Ï…ó>ÍÚí>ÍÔ>ÍÛŠ>ÍÙ²>ÍÑc>ÍÎW>ÍÌ‹>ÍÛT>΃q?ïl?wO?¾ ?¬Q?³S?ºò?´?ʳ?{b?7˜>ÎÉ4>ÍÕJ>ÍÓÑ>ÍÛ³>Íã†>ÍÙµ>ÍÓ>ÍÏn>ÍØl>Íã?T?`Þ?½T?­?´ ?»Ö?×?Ëc?|>Ï„>Íàþ>ÍÓÒ>ÍÔ¦>ÍÞó>Íä§>ÍÜŒ>Í×Å>ÍÖã>ÍÔf>Í×§>Îz“?25?¸–?­?´#?»Ù?Ã?˨?{Á>Îá¸>Íß/>ÍÕD>ÍÖl>ÍÛ©>ÍàÄ>ÍÜÔ>ÍÞP>Íßš>Í×8>ÍÙÌ>Íñ?ò^?¸®?¬l?³z?»?Âä?Êæ?~A?+>κ>ÍâH>ÍÝ`>ÍÚI>ÍÞ8>ÍÜâ>Íáø>ÍÚg>ÍÕ´>ÍäO? Ž?#?¾&?«N?²8?¹®?ÁG?É?„J?R#>Îç>ÍãÂ>Í×a>Í×{>ÍØâ>Í×Ü>ÍÕð>ÍÖ\>Í×Q>ÍøÈ?”š?mÊ?¾¸?©Æ?°s?·­?¾ú?Æ?·?€Ÿ?Jè>Ϊ>ÍÝ>Íáy>ÍÙö>ÍÓp>ÍÔ$>ÍØç>Íå²? 2z?=º?®î?‰?§ß?®*?µ&?¼q?÷?ÅÑ?ŽÙ?l‰?s]>ÎÔc>ÍæÍ>ÍØˆ>ÍØÝ>ÍæŽ>Íõ¢? $8?Iº?…r?¼P?Àž?¥b?«A?±â?¹‡?Á„?ÅŒ?ÁÎÔx>Íô5? 4×?Ð’?:~??´[?Åž?½Õ?¢j?§Ð?­¦?³á?¹—?¾¨?Äl?¼Ž?¾ ?|¸?fÚ?½¸?/Š?%ã?lÛ?¬ü?»4?Æ$?§?º6?Ÿn?¤??©W?®ž?³p?·å?¼¡?À8?Ã?¿ý?¾ô?¹Þ?¹j?¾‰?¾}?Â&?À^?½í?º??´ò?•A?—ö?šÂ?Œ? s?£|?¦ˆ?©T?«²?­~?®«?¯K?¯r?¯v?®½?¬P?©›?¦Á?£Ä? >?˜?›w?žì?¡ö?¥0?¨Å?¬i?¯Ó?²É?µ?¶„?·d?·ï?¹Ó?¹—?´0?°`?¬Ù?©@?¥Ÿ?šå?ŸN?£º?¦ƒ?©Õ?®U?²ã?·2?»?½¥?¿X?Àr?Á?Âí?ÂB?¼5?·Â?³‘?¯N?ªó?™?¢k?§?ª?­ ?´E?º,?¿¶?ÅÏ?ǽ?Ée?¡›?™&?À}?É9?Ä»?¿õ?ºø?¶?°Í? \?¥œ?ªÎ?¯?³:?»Q?ÂD?Èÿ?§Ë?£€?¥%?™’?™Š?¥‘?¤B?¡ž?¿ ?Ã'?¾¡?·r?£6?©?¯?µ)?»–?Ã4?¡ã?£ ?>Ïô»>Ï Ÿ>Ï>Ï è>Ï Œ>Ï C?;C? t?ŸD?»Ý?½]?¦?¬n?³,?ºx?ÂH?¡¥?˜Û>Ïûn>Ï¥>Îxt>Î}>͇_>Í…R>Îc<>Îtü>Ïà>Ï?r“?”¹?½û?¨Ì?¯¼?·=?¿?È£?§º>Ïø_>Î{î>Îu’>Í’ò>Í”¥>Í‘z>Í•]>Ͳ>ÍŒ'>Îj:>Ît“>Î÷?s"?½ÿ?«O?²À?ºú?Ä4?¥?œZ>ÏMÓ>Î>ÍžÙ>͘©>ÍŸ>ͧJ>ͦ&>Í¢ç>Í“ý>Í”Ò>ÎcÙ>Îi¾?ä¤?šÓ?­k?µ??¾?È$?ª>Ïù>θ>Í”>Í a>ͯQ>Í»_>͹Ï>Í´4>Í´œ>ͪæ>Í~>͉Z>Î\]>Îûà?–÷?¯?·?À?Ê'?«è>ÏOñ>Ά>Í >ͦ€>Ͳn>ͼï>Í·u>ͺ >Í·°>Í® >ÍŸÖ>ÍŠñ>Î\\>ÎkÎ?%?¯Ý?¸?Á^?¢?œ­>ÏQÑ>Í“>Í«}>Í«›>Ͳ±>͹½>ÍÄÖ>ÍÄ<>ͼ >Í®Ÿ>ÍŸŒ>ÍšÝ>Í‹D>ÎW!?-Á?¯Ò?¸?ÁA?™ñ?œ>ÏO‹>͉>ͨÍ>ͪy>Í«A>ͽ>ÍÒì>ÍÈ>͸É>ͪú>ͦF>Í¥X>ÍŒq>ÎX÷?/8?¯?·/?À2?¿Ò?ªŸ>ÏI†>ÎmÃ>͵>Í£#>Í©ô>ͺ…>ÍÎu>ÍÉ(>ͳ~>ͬ©>ͧ¿>Í¡v>ÎZ™>Înæ?&˜?­Ì?µ·?¾?ÈQ?ªû>ÏQ¥>Îy4>Í•˜>ͨÑ>ͬ>Ͳ->;_>Íý>͵µ>ͨó>Ížy>Í’•>ÎhÈ>Îsˆ?X4?«ÿ?³À?¼d?Å{?£?¥ >ÏBO>Îlm>Í•ã>Ížþ>Í :>ͦ>>Í«ä>Í£á>Í ˆ>Í•E>ÎR0>Îc ? ¹@?PV?©ª?±/?¹Á?Âv?Àò?¤Ñ>ÏWå>Îz«>Îi‘>Í>Í’>͘y>Í¢l>͈X>ÍŒ„>ÎS&>Îp¹>ÎdÌ?%?¶¨?¦Í?­µ?µ¬?¿?ÉÃ?¢Œ?Üb>ÏM›>Îuç>Îe´>Î_Ð>͆T>ÍÈ>ÎT+>ÎdÕ>Îsž>Înu? ûù?] ?¿ž?£ ?©­?°c?·Ø?¿û?¼?—?îÿ?5Œ>ÏOD>Îl>Î^ˆ>Îg¬>Îu£>În? ÃÄ?LÛ?`ú?½??¿X?Ÿõ?¥¶?«m?±Ò?¹É?¾ù?¿G?À¤?á?“?v£?|å?~Û?yh?…,?S{?· ?À?¿ˆ?¸ð?•&?—Õ?š¼?©? É?¤&?§Ÿ?ªÇ?­t?¯x?°Ä?±w?±ƒ?°ê?°‡?­þ?ªø?§Ý?¤¡? t?˜&?›À?Ÿ¼?¢²?¦@?ª;?®F?² ?µ@?·Ë?¹•?ºª?º?¸0?·#?µ¸?²C?®{?ª‘?¦£?›7?¡h?©)?¨Š?¬?°Õ?µÀ?ºl?¾{?Á£?ÃÃ?Ÿ?–?¸_?À)?¾w?ºn?µÐ?±?¬]?ž ?£Ý?ªK?­H?±ë?¸?¾(?Ä?£^?ž„?¡U>Ïê0>Îù??JÙ?œá?šg?¹Q?½½?·Õ?²[? û?¦ê?­7?²å?¹?Àr?¡R? >Ïçx>ÎýÃ>Î÷Z>Îhð>Îgó>Îð >ÏÍ>Îô5?R?Ž?¸ ?·Í?¤?ª—?±™?¸÷?Á?ª>Ïëo>Îÿ>Îr">Ízr>Í~ >ÍŠè>Í…ö>Í{_>ÍuÐ>ÎAß>Îë&>Îõb?”‰?µµ?§>?®c?¶D?¿?§L>Ïë@>ÎoU>Í{>Í‚H>͆ð>Í”>Í¡ë>Íœã>ͱ>Í„,>ÍyÇ>Íwk>ÎEæ>ÎåÈ?¼[?ªH?²?º÷?Åw?¦r>ÏAŠ>Íw>͈>Í‘E>ÍšR>ÍžÇ>Í­Ð>͸>ͤ>>Í‘k>͈_>͉ô>Ív›>Î9O?)B?­?µƒ?¿M?¨½>Ïè€>Îi>Í€¥>Íœ>Í¢9>ͳ–>͹u>ÍÂw>ÍÃÖ>Ͳ4>Í¥Ä>ͤ¥>Í“}>̓>ÎAï>ÎéÐ?¯€?¸[?»?¤ê>ÏC>ÍsÙ>Í…c>Ížã>ͳ·>ÍÃM>ÍÊ7>ÍΦ>ÍÏE>ÍÉ*>ͼŸ>ͦƒ>Í• >͈™>Í|­>ÎÏ;|>Í}X>Í>ͧ=>ͽ>ÍÍ‘>ÍßW>ÍÕ\>ÍÙ=>Í×D>ÍÏH>ͱÃ>Í›®>Í}>Í…Õ>Î??²„?¼Z?¦®>Ïëñ>Îf]>Í€c>Í”†>Í«Ö>͇>ÍÐZ>Íà‘>ÍÙß>Íß>Íáô>ÍÛ¦>ÍÂs>ͤ>Í‘÷>͆©>ÎD ?²Y?¼?žE>ÏFŒ>Îb`>ͧ>Í’o>ͦ(>ÍÆk>ÍÏ^>ÍÝþ>Íâû>Íã >ÍßJ>ÍÙ>ÍÇ&>ͦ>Í•ì>͉>ÎJA?±_?º¤?»/?§»>Ï1 >Í{æ>Í‹W>Í ->;«>ÍÊò>ÍÖ>Íàì>ÍÜ’>ÍÓº>ÍÍ>ÍÌ$>Í Á>͘>Í…2>ÎAz?¯Ú?¸Î?Âü? M>ÏDâ>ÍvÚ>Í…>Í—”>Í©­>ÍÀo>ÍÆ>ÍÌu>ÍÊã>ÍÉß>͸È>͸È>Í¡×>Í’–>Í{ÿ>Î>2?­Ã?¶·?Ás?¢4>ÏB!>ÎIw>Í…">Í•¢>ͦ+>Í­&>ͲÍ>ͼn>Í·³>Í´>>ͪr>Í´€>Íš>Í‚\>Î.>Î[Š?ªú?´?¿ô?¿?Á>Ï6 >ÍvÜ>͇–>͘“>Í¡B>ͦS>ͧ”>͘Ž>Í ­>ͤl>Í¡‡>ÍŠ§>ÍsÔ>Î: ? ?§ª?¯?¹]?ÀT?—9>ÏNè>ÎIŽ>Ít•>Í€>ÍŠ>Í‘›>Í>Í…»>Í>Í–>Íž>Í{>Î-S>Î\M?`–?¤F?ªÚ?²?¹Œ?¾*?´>Ï8…>ÎK¨>ÎB¢>Íp^>Íz¡>Í{ñ>Í}N>Í…¤>Í}>Î2>>ÎD^>ÎWÉ? (?†Þ?Ÿþ?¦z?¬„?³ ?»0?¹?4T?w<>Ï9l>ÎC>ÎD >ÎC4>ÎF>ÎM¢>ÎG}>Î_? ä?‹8?‰Ô?»?“ð?–ã?™ç?B? Æ?¤u?¨0?«‘?®G?°a?±Ñ?²Ÿ?²µ?²,?²Š?®Ç?«ƒ?¨V?¤ò? )?–»?™ú?`?¢?¦?«?¯–?³¿?·&?¹ÿ?¼W?¼’?¼í?½?½}?¸0?³§?¯Œ?«j?§/?™§?œ ?Ÿ?¦Ð?¬õ?²?¸ ?½K?š˜?•÷?™P>Ïâ>ÎòV?A¬?“ ?’‹?²k?·™?²À?­…?@?¡˜?¦ ?­Å?´{?»)?›)?™á>ÏÌ>ÎíE>Îæ¬>Í`P>Í]>εQ>Îó»>Îæ?6B?ˆM?µÔ?´`? ü?§(?®¾?µÜ?½Â?¦C>ÏÖ2>ÎèÄ>ÍY§>ÍvS>Íu®>Íqì>ÍoÇ>ÍlÌ>ÍgW>Í^¤>α]>ÎåE?oâ?²Y?¤‰?«ˆ?³–?¼I?§°>Ï߉>Í]ä>ÍlK>Íi1>Í€®>Í‚Ä>͇å>͉ò>Í„ø>ÍzJ>ÍqÃ>Íl¶>Í]Á>ή9?½•?§õ?¯¨?¸|?¡>ÏÛÿ>Íbø>Íg÷>Ílo>Í~>Í’|>ÍÐ>Í Ï>Í­ >Í£Ì>Í•>ÍŠ¡>Íy<>Ío%>Í]m>έü?«-?³¼?½Ð?Ÿ)>Ï78>ÍmÞ>Íuv>ÍwÄ>Í’Î>ͪÁ>ͯ>ͽ\>ÍÄF>Í»>Í«é>Íž@>ÍŽC>Í~J>Íj>Î-}?®?·§?¡—>ÏÙÍ>ÍcE>Írl>Í‚ÿ>͇–>Í£->Í¿>ÍÆq>ÍÐ>ÍÛ\>ÍÑï>ÍÆ >Í­>>Í£B>Í‹+>Ív$>Í`ó?°l?º³?‚>Ï,>Í]5>ÍwÔ>ÍŒŒ>ͤ¶>ÍÅõ>ÍÓ>Í×l>Íçì>Íîß>Íã>ÍÛ¨>ÍÉ>ͳç>Í“Ö>Í‚ >Íhä?²e?½z?ž–>Ï'O>Ígø>Í}m>Í’>Í®>ÍÖ >ÍéÏ>ÍæÒ>Í÷Ù>Î ×>ÎÆ>Íêz>ÍÖþ>͹ç>Íž¡>Í„Ý>ÍkA?³«?ÀÆ>Ïùk>Íd>Ít >͆Ù>Íšâ>ͳƒ>ÍÕú>ÍíÚ>Íüô>Íÿ%>Î õ>Ît>Íë´>ÍÕ,>ÍÁÊ>Í©*>͈Õ>Íh”?³•?¿Á>ÏMÊ>Í^ >ÍuY>ÍŠš>Í¡Œ>ͽä>ÍÕæ>Íî²>Íýí>Í÷v>ÎB>α>ÍæÂ>ÍÓÂ>ÍÀá>ͬm>͈½>Íb?²d?½`?£Ø>Îþt>ÍwJ>ÍŽ¡>ÍžV>Í´>ÍÇC>Íäv>Íùô>Íôæ>ÍÿV>ÍõË>Íä(>ÍËí>ͺQ>Í©¸>Í‹þ>Íi¢?°’?»?˜Ï>Ï'[>Í` >͇Í>Í—=>ͪþ>͸C>ÍÒ>Íç|>Íñ¹>Íí >Íæ >ÍÒ>ÍÆ\>ͺû>ÍšÅ>Í|ð>Íl±?®P?¸¾?›^>Ï6J>Í]b>Ísü>͈>Í¥­>ÍÀ>ÍÃí>ÍÌž>ÍÚ'>ÍÙÈ>ÍÓr>ÍÁt>ͱ>ͬž>Í•>ÍvW>ÍfM?«?µø?¶&?ªO>Îû>Íd>ÍvO>Í“Þ>Í­ò>ͳK>Í·º>ÍÁO>ÍÆ>Í¿W>Í®%>ÍŸj>Í”„>Í‚Y>Ínë>ο?§ú?°?· ?Œû>Ï+|>ÍP“>Íd²>Íwÿ>Í“>Íž$>Íœ`>Í¡>ÍŸt>ͧ$>ÍžŒ>Í…Š>Í{_>Íp[>ÍjŽ>Î0®?¤E?ªÿ?²?µ4?Ük>Ï÷>ÍS—>Í[î>ÍxÉ>Í€a>Í…›>Í…Æ>Í}ë>Í‚î>Í‚á>Íw¬>Íjn>Í\Ë>Α? $'?Ÿo?¦q?¬?²³?®Ñ?6U>Îú¡>Î,<>Íb¹>ÍiÈ>ÍvÅ>Íp >Ík>Íc¿>Íi>Ím‚>ÎÞ>Î.A? Š?‘8?’?•Ð?˜õ?œo? #?¤?¨"?«Ö?®ž?°Ô?²I?³?²ë?±u?¬¢?¬£?ªå?¨ ?¤š?Ÿ@?•6?˜û?œÇ?¡g?¦;?«6?°N?µ ?·$?”ø?ñ?’Á?“~?¨?‡D?ªü?³¡?¯ó?«¨?§?˜‘?œZ? a?¦é?­"?³?“¶?’Ò>ÏÇc>ÎÕS>ÎÑX>ÍŽ>Í•›>ι>ÎÑ>ÎÖƒ?AZ?€½?¯}?®2?œk?¡k?§[?®??µš?ž>ÏÄ >ÎÚ_>ÍLR>ÍMG>ÍX+>Ía >ÍjQ>ÍY«>Í@M>ÍGÏ>Ο>ÎÕt?iî?¬Ý? u?§(?²-?¸¸?¡¶>ÏÌG>ÍGe>ÍU¾>Í\¾>Ífy>Ízô>Íyú>Ívi>Íqq>Ífí>Í`×>ÍT<>ÍMß>Ο£?¦«?¤@?«”?´Æ?žä>ÏÍ|>ÍQR>ÍSú>Í\€>Ísý>ÍŒ >Í“1>͉I>Íö>Í•°>͈Ì>Íy>Íe5>Íkƒ>ÍX6>Ο´?§Þ?¯î?—¾>ÏÅâ>ÍPÆ>Íb¬>Ífè>ÍwŠ>ÍŽÜ>ͨ>ͪB>Í¡è>Í¥é>͹I>Í¥+>Í’X>Íyû>Íy)>Íi!>ÍRh?«7?´&?’‡>Ï–>ÍIX>Íh->Íz˜>ÍŒ\>Í¡s>͵c>ÍÄl>ÍÊÎ>ÍÏÈ>ÍÑw>ÍÁ5>Í·E>Í—J>Í€o>Ípb>Í`¶?® ?¸>ÏÆï>ÍEM>ÍV>Íy³>Í–>ͧ>ͺ>ÍÏl>Íã@>ÍõÉ>ÍöÃ>Íï’>ÍÜ@>ÍÍJ>ͲÁ>͘5>Í„>Ín?°˜?™\>Ï€>ÍL>Ígˆ>Í’—>Í£!>ͼt>ÍÔw>Íæ¦>ÎV>Î1>Έ>Î a>ÍèN>ÍÙé>ÍÇ>Í©x>͇¦>Ío?²¨?”†>Ï >Í\Æ>Ítá>Í”Ï>ͪ™>ÍÒ2>Íðe>ÎW>ÎI>Î%u>Î%D>ÎW>ÍýÆ>Íè}>ÍÒ>͹ƒ>Í’‰>ÍqÚ?³??”O>ÍÁO>Íc¼>Í„ >Í•¿>Í«L>ÍÓ{>Íúó>η>Î& >Î9>Î:d>Ω>Î å>ÍóÊ>Í×”>ÍÅB>Í•>Íq‘?³ó?•ø>ÍÄb>Í_V>͆+>Í–>Ͱ*>ÍÒÁ>Íîq>Ξ>Î*K>Î9r>Î9>Î ç>ÎÉ>Íø>ÍÖç>ͽx>Í—X>Íp ?²Ò?•>>Î÷Â>Í[i>ÍwŒ>Í’.>Í®’>ÍÐO>Íê7>Î>Î'F>Î&e>Î$>ÎI>ÍüX>Íä\>ÍÍ›>Í®U>Í’>Ímß?°Œ?“>Ï6>ÍUB>Ío>ÍŽ”>ͨ>ÍÄé>Íãi>Íö™>Î>Î ¦>ί>Îæ>Íò>ÍÖ”>ͼ·>Íc>̓X>Ía€?­Ê?¯L>Ï0v>ÍIK>Ídó>Í{Ô>Í”Š>ͳÔ>ÍÀ>ÍØÚ>Íôò>Íÿ¤>ÍþÅ>Íùí>Íá#>ͺ€>ͤ>Í’•>Í|>ÍYÛ?«?¶€?´8>ÎãB>ÍTV>Íj‘>Í…M>Í›ž>Í ¡>͹>ÍÛ>ÍÞr>ÍÚ§>Íâ–>ͽF>Í¢>Ík>Í…õ>Íre>ÍVÜ?§V?¯õ?…9>ÏØ>Í=å>Í^v>ÍnÄ>Í€i>Í‹6>Í£Ç>͹u>Í·H>Í´1>Í®*>Íœâ>Í”1>̓³>Íqä>ÍY:>ÍH?£„?ªg?­G?Ó¡>Îê~>ÍRx>ÍYã>Íj$>Íu”>͆P>Í™+>Í—–>Í”²>͈?>͆>Í~ì>Íp>Í^n>ÍOx>ÎÔ?ž3?¥¹?«ý?ª;?>Îëõ>ÍEb>ÍXµ>Íc•>Ík„>Ío³>Íy¤>Í|>ÍuÇ>Íkq>ÍY„>ÍT >ÍJ$>ÎÐ? D}?‘€?”ž?— ?›?žå?Ÿ0?œ?™V?”š?ŽN?‡?Z”?P¾?zÌ?ŠŸ?‘Å?–1?šC?žô?œH?”#?—Å?›? ?¤õ?ªm?®Ð?Ž?‹>ϸâ>ÎË®>ÎÈ>ÎÀ×>ÎÄ>ÎÅ?(„?zõ?©–?«?¦?—M?›u?Ÿó?¥’?«û?”>ÏÂá>ÎÏÇ>Íyø>Í9<>Í=ó>Í?&>Í6õ>ÍDF>ÍÍs>Σa>ÎÎÓ?†?¦?šú?ŸÓ?¤¾?«•?• >ÏÀO>Í2¼>Í5¿>ÍE¿>ÍMè>Í\“>ÍZJ>ÍRÓ>ÍVó>ÍUŸ>ÍIÕ>ÍDZ>Í7(>ÎŽO?¯U?žï?¤T?¦™?‘F>Ï®²>Í?n>ÍE#>ÍG>Í]>Íj>Íyl>Íx.>Íz>Í~Ô>Íi&>Í\:>ÍSµ>ÍFÚ>Í:¾>΋Ÿ?Ÿ%?ª#?’É>ϵh>Í.">ÍCä>Í]>ÍeÀ>Í~m>Í‚š>Í“€>Í™ž>ÍŸI>͘ó>Í…I>Ít³>Ía§>ÍUó>ÍC>Í3ƒ?›s?¯>Ï >Í4 >ÍBž>ÍFW>Íb_>Í…¼>ͦ6>Í›Í>Í´é>Í¿F>ͽÛ>ͯ.>ͪ¶>Í“P>Í}‰>ÍoK>ÍRZ>Í<?˜ ?’=>Ï÷>ÍCû>ÍaG>ÍaA>ÍxÊ>͘«>ͼ]>ÍÃó>ÍÛ>Íå²>ÍÞ.>ÍÞì>ÍÍ$>ͪ->Í™í>ÍÞ>Íg–>ÍJø?”?ô>;í>ÍJ>Í`>Íw|>͘k>ͯ°>ÍØz>Íñ¶>ÎÆ>Λ>Îí>Îà>Íî¯>ÍÇå>ͱˆ>Í–º>Í|/>Íb=?>ϹØ>Í8>ÍQ€>ÍgÅ>ÍÝ>Í«3>ÍÃË>ÍäÎ>Îy>Î.Ê>Î0>Î?£>Î.™>Îé>ÍíQ>ÍÌÍ>Í©;>Í„â>ÍiÈ?‹L>Ï>Í6R>ÍN³>Ír>Í” >ͯü>ÍÍã>Í÷X>Î?>ÎN=>Î^è>Î^e>ÎO9>Î(Ø>Î:>Íèb>ͳÜ>ÍŽ*>Íu—?_9>Ï p>Í26>ÍKž>Ílö>Í—(>Í·Ô>ÍÚú>Îá>Î4‰>ÎW>ÎiY>ÎeÒ>Îd>Î:À>Î Ñ>Íé‘>ͺÍ>Í”¦>ÍwÞ?Y2>ÏŸ>Í9y>ÍSè>Íi!>Í–t>ÍÁ@>ÍÝå>ÎÂ>ÎDR>Îkú>Îz>Îtû>Îjx>Î5•>Î F>Íßð>Ͷ>Í“ >Ís?v>Ï•>ÍCî>ÍYP>Ímî>ÍŒ>ͼc>Íäü>Î[>Î.ï>ÎBé>Î^»>ÎV>ÎM6>Î)ä>Îk>ÍÝ1>Ͱ>Í…M>Ím(?Ò>Ïß>Í?·>ÍPˆ>Íf>Í|™>Í­>ÍÔÎ>Íê`>Î >Î#´>Î.þ>Î8W>Î+ý>Î>Íü>ÍËX>Í©˜>͈>Íg%?‘?— >ÍžÖ>ÍC>Íez>Í{Ï>Í›>ͽÇ>ÍÌ’>ÍóÞ>Î ˜>Îß>Η>ÎÜ>ÍýF>ÍàJ>ͳ2>ͦ­>Í‚â>Í_Z?”ô?}´>ÎâÈ>Í@'>ÍVo>Íj8>Í„>Í™Ð>Ͱµ>ÍËà>Íá>ÍäE>Íä»>ÍÓ›>ÍÑ>ÍÄ>Í<>Í„*>Ík¼>ÍUU?˜í?©Î>Ï…>Í2V>Í>±>ÍP/>Ím >̓ >Í•9>Í©ó>Í¿†>ÍÄZ>ÍÄé>Ͳ?>Í©Á>Íœ$>Í|Ÿ>Íg×>ÍQ‹>ÍL?r?¨ç?ìq>ÎÚ3>Í'€>Í7>ÍWì>Íq:>Íu¾>͆(>Í” >Í —>ÍžŠ>Í >Í”>Í€í>Íf >Í[ >ÍD4>Í:ó?šþ?¤>?£/?(z>ÎÖæ>Í/à>ÍC8>Í\º>Í\>ÍhK>Ítr>Í…\>Ívµ>Ík£>Íoq>ÍeS>ÍR>ÍC`>Í.È>Íék?‘?“ ?•?˜Õ?œ?™??…ê?x±?DÒ?+…>ÏtÇ>Άœ?Àå?,?m¹?~2?‹J?˜$?˜?’}?•Î?™|?¸?¢v?¦Ø?É>ϰ[>ÎÃË>Íl>ÍXÇ>Ím>ÍÒ>Í\¯>Í^5>Î>κ®?We?žj?¢Ô?•[?™€?ž?£4?§ò>Ϻ’>Íc1>ÍJ>Í3Ñ>Í4 >Í29>Í6î>Í6Y>Í6Ý>Í1>Í/œ>Í*¯>Í[d>Î’p?®?˜ö?ü?£"?¨>Ï­6>Í]>Í(ñ>Í0>ÍEÙ>ÍKñ>ÍB2>ÍMš>ÍP‰>ÍO[>ÍG>ÍD·>ÍEÿ>Í2ä>Í>Îz°? ?£Q?§ä>Ï­r>ÍE>Í(‹>Í9y>ÍG–>Í^>Íbt>ÍUÒ>ÍnH>Í|¡>Ím>Í`n>Í\Ò>ÍYõ>ÍF¯>Í*‹>ÍÏÀ'>Í' >Í)å>Í2Ó>ÍB«>Í^›>Íp;>Í„²>Í„Ë>Í–+>Í¢ü>Í•>͆é>Íz>Ífñ>ÍRê>ÍDˆ>Í-€?Ž?¨>Í¢¬>Í5ç>Í>m>ÍGÅ>Í_s>Íz¿>̓Ö>Í¡ô>͹J>ͺÁ>ÍÅ\>ÍÁ˜>͸¾>ͨ5>͈=>Íc:>ÍXú>Í;p?‚p>Ϭl>Í!;>Í:¨>ÍNd>Í`æ>Íx¼>Í”k>Í­i>ÍÌg>Íóh>Íýv>Îa>Íü×>Íê¸>Í¿l>Í ì>Í‚é>Íe>ÍFÎ?w`>ϧ>Í'>Í=>ÍXç>Í{Ê>ÍŽÕ>ͯ>ÍÕ¿>Í÷>Î*?>Î.1>Î;>Î0>Î ²>Íåß>Í·†>Ížñ>ÍxA>ÍQl?AR>Í£Ö>Í.ë>ÍI >Í`ä>Ízd>Í¢#>ÍÊ >Íøë>Î.^>ÎY­>Ît®>Îy¨>Î^o>Î7‚>ÎG>ÍÙ·>ͺ>ÍŠÕ>ÍfT?+]>ͦ²>Í3x>ÍL}>Ík>ÍŠ–>ͼØ>ÍãÛ>θ>Î]d>Ά>Ρù>Τ >΋J>Îc>Î:>ÍþÃ>ÍÇ•>Í‘Á>Íqr>Ïg£>Í''>Í4>ÍN½>ÍqÛ>Í›>Í˲>Í÷º>Î"`>ÎjÐ>Ξ(>λy>ÎÂà>ΰ>Îu»>ÎD0>Î,>ÍÉj>Í™±>Íuö>ÎÃQ>Í'ø>Í8¨>ÍO½>ÍsG>ͤž>ÍÌ>ÍýÜ>Î-þ>Îk·>ε>β³>θÂ>Φü>Îs{>Î<>Íû£>ÍÈÒ>ÍŸp>Ítï?Ì>Í®ˆ>ÍL+>ÍPj>Íf+>Í—a>ÍÆs>ÍøB>Î#@>ÎZ->Îß>΢™>Σ->Î>Î\ª>Î"õ>Íí`>;Í>Í–œ>ÍoÍ«">Í>Ý>ÍI>Í_>Í‘Š>Ͳ”>ÍÛ>Î F>Î6I>ÎY&>Îfu>Îm^>ÎMÿ>Î3>Íú >ÍÖ >Ͱò>Í–¶>ÍiK?n8>ÎØÒ>Í)(>Í<¹>ÍPÁ>Íy;>Í¡>͹„>Íè6>ÎÎ>Î&…>Î7ÿ>Î1É>Î K>Îç>ÍÛg>Íà >͘ >Í‚Þ>Í]Q?}ˆ>Ïo>Í>>Í-`>ÍJV>Íf">ÍŠ8>ÍŸc>ÍÎh>Íá=>ÍçÕ>Íøø>ÍúÌ>Ííí>ÍÜ>Íϰ>ͦJ>Í€>Íb¸>ÍP?‰„?Âã>Íé>Í+L>ÍF >ÍOà>ÍpT>ÍŠ>Í®2>Ͱ«>ÍÂ>ÍÅó>ÍÅÚ>ÍÂ,>ͪÌ>Í¡ü>Í…†>Íi>ÍS9>ÍIÙ?–h?ž>Îå>Íb>Í+î>Í:’>ÍW¡>Íf¨>Íxò>ÍŠn>Í“Ó>Íš¶>ÍŸe>Í’š>Í}È>Í{“>Ílµ>ÍPF>ÍA‡>Í7¨?–Õ?¡›?>ÎÕó>Í#A>Í,›>Í>q>ÍG¸>ÍXÜ>ÍkÁ>Íu3>Ívº>ÍqÜ>ÍmL>Ímš>ÍWâ>ÍLÛ>Í@²>ÍÍ*7?‹H?/?’E?•{?˜í?‘%?}6?iZ?4«>ÏP/>Îl\>Í1Ñ>Í0 >ÎF@>Îk‹?°&?]ä?z?Žš?‘ ?!?’ƒ?–3?šO?žÎ?£±>Ϧ“>ÍIU>ÍK.>Í >Í·>ÍÞ>Í1>Íô>Í>ÍG›>ÍM³>΂3?e­?š?’T?–a?šÏ?Ÿü?‡¶>ÍÖ®>Í/>ÍN>͇>Í&S>Í)b>Í4ÿ>Í-É>ÍÒ>Í"Â>Í#»>Í!>ÍV>Í;l? s–?•Ü?›2? 5?ˆ >ÍÑ™>Í—>Íi>Í&Ñ>Í1>ÍFâ>ÍBõ>ÍFÍ>ÍC™>ÍCH>Í2>Í0Å>Í$Û>Í&ù>Íu>Í>?™é?¢?ˆ†>ÍΠ>Í>Í&F>Í5J>Í@>ÍHò>ÍeÒ>Í_V>Í]#>Í]ó>ÍR¥>ÍPq>ÍHÆ>Í9/>Í+v>ÍY>Í_?’]?¨t>Í×î>Í †>Í7>Í1^>ÍL>Í]½>Íg#>Íñ>Í€î>̓>Íß>Íyû>Íya>ÍeR>ÍW”>ÍAÈ>Í*ù>Í&à?~¡>Ï¢H>Í ä>ͯ>Í\>Í@&>Í^«>Ít¨>ÍŠº>ͨ¬>ͦx>;ø>ͺ!>ÍÀU>ͨ>͉A>Íy>ÍZ{>Í<>Í6s?kÖ>ÍŠM>Íž>Íè>Í&>ÍTp>Íw4>Í•“>Í®ë>ÍÐ?>Íà,>Íðw>Íök>Íÿ>Íá>Í¿†>Í>Ívß>ÍV>Í@?5>Í‚>ÍZ>Í*w>ÍB$>ÍaT>Í„¹>͹w>ÍÜU>Î?>Î=¬>ÎB>ÎD•>Î@§>Îî>Íæï>Í»®>Í”ÿ>Íaö>ÍMø>ÏH±>ͦ>ͯ>Í76>ÍS™>Ín‹>Í¢£>ÍÓ>΃>ÎP¿>Îë>Λí>Ή>΄y>Îos>Î&>ÍèQ>ͳ >Íy“>Í\‚>Ψq>Íø>Í$E>Í=N>Ídb>ÍzJ>ͱ->Íé„>Î3©>Îp±>ÎÖW>ÏÈ>Îß0>θÆ>Î~Ù>ÎL'>Î >ÍÊã>ÍŽ>Ídy>Ídf>Íí>Í'‘>ÍB^>Íoª>Í‹Õ>Í¿`>ÍóH>ÎDŸ>Α>ÏÛ>Ï1ÿ>Ïý>ÎÜž>Π°>Î\8>ÎÓ>ÍÃû>Í•->ÍkÁ>ÍSþ>Í}>Í(ÿ>ÍC‘>Íh,>͉£>͹ë>Íö¿>Î:|>Î’ÿ>ÎãÐ>ÏP>Ï5¥>Îî=>Ο>ÎG[>Íý˜>ÍÀ>Í•˜>ÍoÂ>Îr>Í?>Í >Í@^>Í_ >ÍŒ`>Í´ñ>Îõ>Î(<>Îzp>δ >Îäl>ϸ>ÎÇN>·¢>Î3{>Íë’>͹O>Í6>Íi¾>Π9>Í>ÍM>Í8>ÍTæ>Íy£>ͦù>Íæ†>Î Ò>ÎK[>ΉÑ>Ι4>ÎÁ>ΣÚ>Îm{>Î?>ÍЮ>ͧù>͆>>Í`Ù?È>ÍŒ>Íe>Í;J>ÍHÎ>Ío>Í–¿>Í©>Íå>Λ>Î>Ù>Î:¥>ÎQ—>ÎG >ξ>Íê§>Í·¨>͇]>Ík­>ÍSw?[>ÍY>Í5>Í0Ž>Í8>ÍW]>Í}'>Í’Ó>ÍÉó>Íßh>Íöˆ>ÍúÀ>Î>Íô$>ÍÚ‹>Í´>Íž#>Íuo>ÍcB>ÍO=?xp>Îȯ>ÍI>ÍÎ>Í%U>Í@>Íf|>Íœ>ͤ>ͯê>Ͱ>ÍÉ>ÍÅm>;d>ͪ>͆>Í‚>Íg°>ÍQ\>Í>˜?Œù?Ë>Í€™>Íh>Ͷ>Í4)>ÍKž>ÍYÍ>Íu‘>Í…w>͇6>͘Û>Í„>ÍŒE>Í~0>Íd<>ÍU>ÍJÿ>Í6µ>Í#Â?,?™ ? /þ>Í}ž>Ͳ>Í%—>Í.Ì>Í7s>ÍW->Í]Ð>Í^Í>Íiß>Íe>Íc#>ÍX¤>ÍOê>ÍA&>Í7É>Í$¤>Í_?‡@?‹A?Ž7?‘(?”G?ˆ­?n?/ä>Ï@O>Í„>ÍF>Íg>ͼ>Íž>Í¡>Î)?Ä?_Í?ƒv?‰?‹T?Ža?‘Ò?•Ã?˜º?€J>ÍË’>Í7>Ìü@>Í >Íž>Í‚>Í£>Í:>ÌûL>Ìú<>Í5Ï>Í4¸? Š?†Í?ŽO?‘ê?–M?›A>ÏŽ­>Í3Ê>Ì÷'>Í«>Í÷>ͦ>Í>ÍD>Í §>Í—>Í º>͇>Íž>ÌüŽ>Í,d>ÎWE?‘E?–N?›>Ï‹t>ÌüP>ÌõE>Í3>Í2>Í >Í$>Í/¡>Í-ü>Í3¢>Í,…>Í#é>Íb>Í>Ìÿµ>Ìý«>Ì÷Å?”8?™å>Ïk>Ì÷Þ>ÌÿW>ÍD>Í>>Í2c>Í9ù>Í5ì>ÍYœ>ÍRA>ÍDo>ÍIò>ÍD¶>Í3%>Í,">Í>Í U>Íì?ˆb?|>Íu>Ìü+>Í ð>Í"W>Í5°>ÍIl>ÍZ>Ía>Ízk>Íù>Ín>Íqy>Íeh>ÍS>ÍKp>Í(Š>Íj>Í ?m)>;ë>Ìþv>Íã>ÍÂ>Í<2>ÍW²>Ísä>͈¬>ÍÍ>ͯ”>ÍÂW>ͳÔ>Í™q>ÍŠo>ÍuÚ>Íb >ÍJ<>Í4u>Í#¤?+Ý>Íu³>Í™>Í%>Í Õ>Í>¿>ÍkG>Íœ>ͳä>Íß×>Íøh>Îó>Î>ÍßÎ>Í̵>Ͳà>Í‘s>Ím>ÍNÊ>Í2X>Ï.>ÌÿZ>ÍŒ>ͯ>Í<„>ÍLá>Í‚>Í¡Ó>Íßé>α>ÎC>>ÎmD>ÎgJ>Îañ>Î$‹>ÍìL>Í·M>Í|P>Í[¬>Í<¯>Í?y>Ío>ÍÇ>Í*[>ÍG[>ÍfQ>Í™ë>ÍÖµ>Î#Œ>Îz>Ζ >ÎÛ}>Îù/>ÎÍó>ÎVý>Î(B>Íð+>ͨó>Ís—>ÍI>Í5‰>Ì÷½>Íb>Í1U>ÍLü>Íoê>ͤ>Íð»>Î92>ζh>Ï),>ÏE3>Ïz^>Ï)ÿ>β>ÎSï>ÎO>ÍÃ>Í… >Í[Î>Í0k>Ìý >Íè>Í2/>ÍM>Ír>Í«³>·>ÎNì>ÎÜ—>ÏS >Ï£/>ÏÎ1>Ï^K>ÎÕ÷>În³>Î.º>ÍÈ>Í\>ÍgY>Í2D>Ìú’>Í&>Í1>ÍH^>Íy+>Í´{>Íà/>ÎP¬>ÎëN>ÏiÓ>ϬÆ>Ï¿C>ÏW8>Îæß>ÎŽ>Î"µ>ÍÇ‚>ÍŠ>Íq>Í6»>Ìôø>ÍØ>Í,{>ÍGó>Ífj>Í—å>Íà >Î>¥>δØ>Ï*D>ÏW,>Ï^ü>Ï0å>ÎÜz>Îl>Î ^>ͯo>Í}·>Íb >Í8>Ìîs>ͱ>Í%®>ÍD·>ÍYF>͇p>ÍÅ¿>Î I>Îo1>Ζf>Îç!>Îâ>ÎÎ>Îp×>ÎH>Í×h>Íž2>Íb>ÍT>Î]p>Ìör>ÍM>Í"‡>Í??>ÍUL>ÍrÎ>ͨ×>Î0>Î&)>Î1g>Îe“>΄H>Îbñ>ÎÒ>ÍØõ>Í Ý>ÍŒ³>Í`?>ÍB³?´>Íp|>Í¡>Í {>Í0g>ÍE¬>Í]È>Í€¨>ͱ°>Íãœ>ÍñK>ÎB>Îz>Î@>Íå/>ͳÍ>͈Ö>ÍrŠ>ÍGè>Í.í?]ä>ÍqA>Ìóò>ÍI>ÍÚ>Í5‡>ÍF>ÍSÁ>ÍlV>ÍÑ>ͬð>ͼ…>ͼ>ͤÄ>Í ¾>Í‹[>ÍcÃ>ÍL=>Í5Ü>ÍY?‚´? ®Œ>ÍdF>Í7>Í->Í,q>Í)y>Í=Ô>ÍW>ÍaÙ>Íy >Íz~>Íû>Í…‚>Íh >Íh>ÍM›>Í;8>Í&>Í?ˆÄ?ˆÆ>Ϋê>ÌóÁ>̓>ͧ>Í8>Í(£>Í8>Í:Â>ÍDê>ÍOª>ÍYV>ÍYŒ>Í@•>Í5.>Í1Å>Í+3>Í >Ìüô?ƒÖ?‡?‰^?ˆ ?ƒ)?rW?X$>ÏJ>Í %>ÍÁ>ÍT>ÌþÝ>ÍÓ>Íæ>Í Í>Í >Î"?Õè?g]?w?‡5?‰s?Œ??’–>Ïzz>Íü>ÌîÍ>Ìæt>Ìî¤>Ìõ]>Ìøf>Ìúk>Ìð >Ìði>Ìô >Ìæò>Í"3>Î@}?nc?‰p?‹Û?Žø?’>Í©³>Ìå>Ìê,>ÌñÑ>Ìõ²>Ìø>>Í“>Í ¤>ÍÒ>ÌüW>ÍE>Ìø˜>Ìç»>Ìëd>Ìãµ>Íõ?‡¹?±?‘Ý>ͯB>Ìá0>Ìæå>ÌôL>Ìý¬>Í N>Í>ÍÍ>Í#œ>Í>Í H>ÍS>Í ¯>Ìü>Ìý>Ìö¤>ÌêÈ?‚—?£>Í¡u>Ìâü>Ìì>Ìòï>Ìÿ>Íe>Í>Í>Í)\>Í?Ú>Í=&>Í4 >Í&7>Í"$>Í >ÍQ>Í1>Ìó?uE>Ïz>ÌÙ*>Ìä«>Ìõº>ͪ>ÍÆ>Í#>Í9õ>Í?Ž>Í]€>Ígß>Íl>ÍY°>ÍNO>Í;h>Í'Ü>Í8>Í •>Ìøa?Wy>Íl#>Ìïð>ÌöÚ>Í>Í k>Í*ê>Í=Æ>ÍS>Íd >Í”/>Íšž>Í¡>ÍŽ{>ÍÈ>ÍXq>Í;e>Í&D>ÍÂ>Í}>Ï+>Ìé®>Ìëž>Ìþå>Í ™>Í%Ø>ÍK›>ÍH>ÍŠÃ>Í O>ÍÎâ>Î>Î>ÍÆ >ͨÄ>Í‘ñ>Ík;>ÍK%>Í(œ>ÍU>Í#¬>Ìí`>Ì÷Á>Í]>Í7>ÍAf>ÍeÛ>ÍÆ‹>ÍÒ->Íø">Î7¬>ΈY>Î~÷>Î%A>ÎC>ÍÒV>Í‘¹>Ífâ>ÍIÛ>Í%$>ÍK>ÌðÄ>Ìÿë>Í ë>Í*L>ÍZo>Íw¿>ÍÛá>Î/‹>ÎIñ>Ωè>Ï"&>Ï>Îåb>Îlë>ÍôŠ>Ͱ'>ÍyJ>Í[Ã>Í9–>Íê>Ìì1>ÍÆ>Íe>Í(˜>Í\§>͇>ÍÞ5>ÎN>>ÎÔ>ÏV÷>Ïã.>ÏÚ>Ϧ>ÎÙo>Î5d>Í×>Í‹/>Íj>Í<>Íl>Ìô¦>Í >Í`>Í4×>ÍZL>Í—|>ÍÓå>Î`>ÏA>Ïþ>ÐG>Њd>ÐG>Ï"”>ÎaM>Íø—>Í ó>ÍkY>ÍBº>ÍÛ>Ìçy>Ìÿ\>Í4>Í7)>ÍWÜ>͇R>Íë¦>Îb >Ï"M>Ð~>ОŒ>Ð¥H>Ð ×>Ï>à>ÎW”>Î Ž>Í©2>ÍoÕ>Í2m>Í->Ìê|>Íh>ÍN>Í+®>ÍRÔ>͇>ÍÖµ>Î> >ÎÒØ>Ïv¾>ÏöH>ÏâG>ÏÄ>Î׃>ÎU>Îr>ÍQ>Ín•>Í;q>Í+e>Ìä”>Ìòr>Í `>Í&•>ÍBB>Ít>ÍË>ÍîÀ>ΆS>ÎÎR>Ïi>Ï1L>ÎÈç>Îk@>Î'‰>ÍÄ„>Íxh>ÍR¢>Í.>Í+‚>Ìá×>Ìë¼>Ìü>Í*>Í5ð>ÍV>ÍË>Í›¶>Íóß>ÎF9>΋e>Îlß>ÎD‡>Î>Íëö>Í–Y>Íb»>Í5>Í,ï>Î[/>Ìàv>ÌäX>Ìù±>ÍÐ>Í'Œ>Í;q>ÍX³>ÍV\>Íž!>δ>Î>ÍÞÔ>ÍÇl>ÍÆÅ>ͧ=>͇‹>ÍY6>Í1E>Íð?)q>Í]$>ÌÞÑ>Ìò?>Í Ë>ÍÀ>Í#>ÍA.>ÍH´>Íwœ>͘Ñ>ͤJ>Í¥ª>Í“Ú>̓Ð>Ípÿ>ÍO$>Í8F>Í>Ìÿ˜?i/>ΘÚ>Ìäõ>Ìó¾>ÌúI>ÌÿX>Í›>Í.~>Í3Å>ÍS‰>ÍS>Ía¸>Ící>ÍTÀ>ÍJ>ÍA>Í/*>Íw>Í ,>Ìöf?wë?Ù1>Í\a>Ìëê>Ìó%>Ìüž>Í£>ͦ>Íí>Í*>Í.>Í)™>Í7ö>Í0>Í ¦>Í%3>Í^>Í]>ÌùÁ>Ìì¾?Y?‚.?„?~;?pÕ?],?>Íw8>ÌîM>ÌæX>Ìî²>Ìír>Ìñ*>Ìñz>Ìëx>Ìì>Ìï‘? mZ?D§?d?‚™?„?…ö?ˆE?‹Ô>Í©ï>Í >ÌÆ~>Ì̲>ÌÜÛ>Ìèˆ>ÌÙ:>ÌÈî>ÌÒø>ÌÒ|>ÌÕH>ÌÏ>Í™>Íÿ? æ6?„)?…©?‡Ñ?m>Í’H>ÌÝÝ>ÌÝÐ>Ìω>ÌΗ>ÌèG>Ìï4>Ìá`>ÌÚ‡>Ìà>Ìß»>ÌÜQ>ÌÙ`>ÌÕ‰>ÌÐF>ÍG?}Æ?†¶?kí>ÍŠ•>Ì×4>ÌãÌ>Ìæ0>ÌãÕ>Ìã>ÌñŠ>ÌöT>Ìï>Ìöt>Ìú>ÌõÅ>Ìó¾>ÌéD>ÌÛd>ÌÓ–>ÌÜ0?oK?‡P>ÍŠ­>ÌÑ>Ìà>Ìã?>Ìì>Ìó->Ì÷ÿ>Í >Í ƒ>Í>Í! >Í—>Í÷>Ìþ\>Ìì¿>Ìæb>Ìè¿>ÌßÉ?Y«>Í;>ÌÊ>ÌØ~>Ìå„>ÌçÚ>Ììˆ>Í~>Íã>Í@>Í'_>Í5M>ÍD½>Í3Ï>ÍË>Íç>Í>Ìò7>ÌöP>Ìá¤?È>ÍDª>ÌÐd>ÌÚÝ>Ìóâ>Ìø >Ì÷Ã>Í\>Í*’>Í@”>Íd;>Íg>Íeœ>ÍVÉ>Í@M>Í6±>Í­>ÍH>Ìÿ*>Ìå,>Íc>ÌÔ—>ÌÜï>ÌÞ>Ìõ½>Í >Í´>Í‹>ÍNÌ>̓ë>Íˈ>Í´>Í¡ü>Ížâ>ÍŠÃ>ÍS€>Íi>Í)>Í Ý>Ìó£>Í u>ÌÒð>ÌèÙ>ÌìL>Ìõ;>Í¥>Í'%>ÍJ>͉B>ÍÞ~>Î>Λ>Î)Ž>Íý_>Í­Ö>ÍŽÍ>ÍE&>Í$²>Íd>Ìýê>Í O>ÌÒt>Ìñ,>Ìô>Í>Í'v>ÍDÿ>Í\ã>Í»f>Î5Ó>άc>ÏÂ>Ï –>ΊJ>Î#Ç>Íæ³>Í{L>Í8 >Í×>Í8>Í z>ÌÝ>Ìï¥>Ìô]>Í ´>Í*>ÍXã>͇>Íé>α;>Ïœ>Жa>ÐU'>ÏŠk>Îåš>Î ª>Í{>ÍR”>Í»>Í>Íì>Ìåw>Ìéô>Ìòª>͈>Í.ü>Íiö>ÍÇ¿>θ>Îð¼>Ðqi>Ñæg>ÒÃ>Њ—>Ï&Í>Î,¬>Í‘™>Í^ >Í3Ú>ÍÁ>Íá>ÌÚ1>ÌæÈ>Ìõß>Í †>Í2S>ÍqÊ>ÍÆÁ>Π>Ï!>ÐjÛ>Ò¦>Ò1 >Ð¥ >Ï8ã>Î1z>Í’v>Í_`>Í*Y>Í Y>ÍU>ÌÛ0>Ìá¡>Ìó>Íf>Í-^>Íf`>ÍÈk>Î ï>Ωþ>ÏkY>ÐW>ÐáÔ>ÐÃ>Îã'>Î>Í—!>Íe£>Í+p>Í z>ÍÑ>ÌÖ·>Ìá0>ÌîÁ>Í ¥>Í5·>ÍRÆ>͈>ÍÐ:>Î"x>ζ‚>Îÿ;>Ï>­>ÎßA>Î0ò>ÍÏ^>Ía>ÍHQ>Í"ï>ÍÓ>ÍM>ÌÔ>ÌÑ>Ìàj>Ìþ¯>Íš>Í2¶>Íbý>Í€<>ÍÝ>μ>Îá>ÎRñ>Îr>ÍÏŠ>Í™H>ÍT#>Í&ø>Í O>ÍO>ÍÔ>ÌÎi>ÌËW>ÌÐY>Ìø!>Í.>Í›>Í;m>ÍN©>ÍvZ>Í€º>ͨ™>ÍÅ_>Ͷ¹>Í•h>ÍÍ*Y>ÍÚ>Ìúí>Ìö? ÷Û>ÍKb>ÌÏ]>ÌÛ>Ìôð>Ì÷H>Í Ü>Í%¤>Í.”>ÍÍP{>ÍWã>ÍkŸ>Íkb>ÍU >Í(N>Í5>Í ù>Ìóÿ>Ìò·?GX>ÍRí>ÌÎH>Ì×%>ÌîŠ>Ìí¿>ÌþK>͈>Í×>Í>Í(->Í,$>Í9ñ>Í)h>Í)¿>ÍZ>͵>Ì÷¡>Ìà_>ÌÛ«?fy? “">ÍBj>Ì×6>Ìæ>Ìäb>Ìíx>ÍS>Ìý‘>ͬ>Í2>Í >>Í>>Ͷ>Í/>Ìü„>Ìô¼>Ìè1>ÌÞç>ÌÕ7?{?}¤?~†?w•?hC?Mó>Ï"Ÿ>ÌÒˆ>ÌØ%>Ìà >ÌØ>Ì×°>ÌØ#>ÌÞª>ÌÜl>ÌÚI>ÌÛß>ÍíP??V?}Ä?~§?˜?L?€ƒ>ÍnŸ>Ì­>Ì®³>̵>̾”>Ì´”>̾ƒ>̺ê>̽ê>̼ƒ>̺Â>̺l>̲{>ÌòY? ˜×?~j?c?s>Ï_¬>Ìü>̳ã>Ì´ ≯+>̼O>ÌÇ>ÌÄa>ÌÆ£>Ì¿&>ÌÁ¬>̽ß>̼>ÌÃ$>̺'>̶É>Ììá?w,?Û>Ï\1>̶ >ÌÆ•>̹¡>Ì»>ÌÆƒ>Ìˈ>ÌÏÖ>ÌÓœ>ÌÐ#>ÌËw>ÌÉï>ÌË>ÌÃb>ÌÈú>ÌÊ?>̾7>̰÷?gy?Û>Í7É>̽>ÌÁŽ>̵Æ>ÌÁg>ÌÚð>ÌÏp>ÌÌw>ÌÖ>ÌÙí>ÌÜA>ÌÈj>Ì×(>ÌÎ >ÌÁ—>ÌÈX>ÌÌh>ÌÁÿ?IÈ>ÍrÏ>̶9>Ìç>ÌÁQ>̼y>ÌÈz>ÌÒy>ÌÓÏ>Ìå>Ìê›>Ìø >Ìê¶>ÌØW>Ìçý>ÌÙ>ÌЙ>ÌÓy>ÌÉà>ÌÐ>Ï 3>Ì­>̲š>Ìœ>ÌÏ >ÌÈ>ÌÏ8>ÌÔ—>Ìç†>ÍÏ>Í®>Í>Í#>ÌöA>Ìòâ>Ìèa>ÌßÝ>Ìâ¨>ÌØ!>ÌÍ‚>ÍŽ>̹/>Ì»n>ÌÃ>ÌÐ!>ÌÒ<>ÌÔ >Ìê³>Ít>Íê>Í µ>Í@ò>Í8>Í-;>͸>Ìý$>ÌïÛ>Ìå>Ìä@>ÌÖÞ>Ìø‘>̾Ÿ>ÌÈB>ÌÍ>ÌÏ>Ì×Ý>ÌéB>Ìò‰>Í+^>ÍT>ÍRÝ>Í­5>Íž>ÍŠ=>Í_‚>Í ~>Íè>Ìò>ÌæE>Ìß[>Ì÷ð>Ì¿j>ÌÊÊ>ÌÉÕ>ÌÒ@>ÌÚY>Ììô>Í9>ÍIË>Í¡>Íö#>Îi#>ÎmC>ÎÞ>Í•e>Í,Í>Í!H>Ìü3>ÌíÓ>Ìß,>Ì÷1>̾«>ÌÂ\>ÌÄ%>Ìך>Ìå>Í Ü>Í)Z>Í‹ô>Ξ>ÎÛ0>Ð(N>Ð}>Ï ³>Íçï>ÍÜ>Í#>Íh>Ìé >Ìâ.>Ì÷¤>̪G>̾Ó>ÌÓP>ÌÖK>Ìö§>Ìù0>Í(é>Í­…>ÎMè>ÐU‰>Ôzã>ÔÂ>ÐPÖ>Îd©>Í™Ó>ÍEÌ>Íõ>ÌåH>ÌØF>Ìÿ >Ì»¤>ÌÀv>ÌÊð>ÌÒþ>ÌâO>Ìëî>Í4>ͨx>ÎPõ>Ð)ô>Ô’‰>ÔâZ>ÐGQ>Î`û>Í¡V>ÍKÌ>Í&>Ìçv>Ì߯>Ìöâ>ÌÃð>ÌÅ>ÌÏî>ÌÖ‡>Ìà>Ìíz>Í,>Í’·>ÍçÍ>Ï+X>ИK>Ð`é>Ï1>ÎB>Í~L>ÍÉ>Ìü·>ÌÙ`>ÌÜ>ÌøÖ>ÌÇÖ>ÌÐþ>ÌД>ÌÎ/>ÌÖ¿>Ìén>Í)¸>ÍM¡>Íz$>Íö\>Îj­>ÎVy>Θ>Í£Í>Í)Ý>Íz>Ìë{>Ìß>Ì×®>Í,>ÌÇ>Ìľ>ÌȾ>ÌÉ4>ÌäW>ÌõY>Íü>Í)n>ÍR‰>Í|M>ÍÀG>͘â>ÍŒê>Í>Ý>Í>Ìût>ÌáI>ÌÕ<>ÌÆ‘>Íë>̽3>ÌÀÐ>ÌË>ÌÇü>ÌÛ‚>Ìô>Í3>Ìú·>Í#>Í5É>ÍK!>Í5²>Í% >ÍZ>Ìó>Ìü½>ÌÛ@>ÌÑK>ÌÉ.>Î/À>Ì»£>̽o>Ì¿S>̼È>ÌË*>ÌÒ6>ÌÚ>Ìõœ>ÌüÒ>Í@>Í…>Íò>Í P>ÌéG>Ìç>ÌÓ®>ÌÒï>ÌÎ >̶T?Z>Í>Å≯ù>̹2>̽µ>ÌÐã>ÌÉ¡>ÌÁ>ÌÛù>Ìܧ>ÌøÛ>Ìô,>ÌñM>Ìós>Ìåí>Ìàœ>ÌЩ>ÌËv>ÌÉz>̼?Xö? ]ê>Í+h≯U>ÌÅú>ÌËÈ>ÌÇÈ>ÌÉE>ÌÆß>ÌÎ9>ÌÔá>ÌÝÞ>ÌÞ'>Ì×ï>ÌÕ6>ÌÏH>ÌÁœ>ÌÁ">̽„>̺?v¦?y?y?q!?`­?B+>ÍZý>ÌÁ,>ÌÁµ>̺â>̾¿>ÌÀ >̺á>Ì¿›>̽>̼ú>ÌÁ >ÌÈë? ŸŒ?D¡?y?y^?yO?xÓ?wô>Íc[>Ìœ3>Ìö>ÌœÅ>̘>Ì >Ì™ò>ÌŸÕ>Ì ë>ÌŸ >Ìš>Ì—8>Ì› >ÌÞÇ? Žy?x»?y$?y>>ÍzË>ÌãQ>Ì’l>Ì•>Ì>Ìž>Ì=>Ì¡Ô>ÌŸ[>̧œ>ÌŸI>ÌžØ>ÌŸÔ>Ìš¦>Ìž>Ì i>ÌÛÅ?p×?y>ÍyŸ>Ì¢'>Ì>Ì“¾>Ì™e>Ì›>Ìž7>̧6>̦p>Ì£ê>Ì 1>Ì¢>̤à>Ìšâ>Ì–Z>̤>Ì¢->Ì¡u?a ?yá>Í(a>̦M>̦N>Ì›Ä>Ì›š>Ì•>Ì™·>̲>̦ö>Ì™Î>Ì“,>Ì¡‡>̬É>Ì i>Ì£Ì>Ì«m>Ì–‘>Ì›”?Aö>Íi¥>̦h>Ìž}>Ì¡”>ÌŸª>Ì¢‰>Ì•Ð>Ì—|>̬á>̲>̘\>Ìž >Ì£+>Ì©ã>Ì¡9>Ì¡>Ì¢…>Ìœ_>Ìž<>ÍB>>ÌK>ÌŸ)>ÌœÜ>ÌB>Ìž¶>̤n>Ì o>Ìž:>Ì©[>Ì®Ÿ>Ì•Ý>̦¬>Ì¡…>ÌšR>ÌšO>̧>̤ß>Ì¢p>̦ì>Ìïc>Ì•(>Ì“é>ÌŸ2>Ì¥7>Ì•>>Ì¡Ý>̱Â>̨v>̧Ä>̲Ç>Ìžt>Ì>Ì‘d>Ì©å>Ì›I>̰å>̤Æ>Ì¥õ>̬ð>ÌåP>Ìšã>Ì’Î>Ìšÿ>Ìžr>̘>Ì«I>Ì’Ø>Ì´J>Ì«a>ÌÂ>̰½>̤ë>̆/>Ì««>Ì£Õ>Ì©ø>Ì£Ë>Ì¡r>̧¨>ÌáÕ>Ì›ÿ>Ì™¸>̤>Ì  >Ì¢>̳v>Ì›ì>Ì­p>̨,>ÌÒÄ>̤È>Ì­Á>̽y>Ì«Û>̧[>Ì{õ>̈Ç>Ì•6>ÌŸ¿>ÌáG>Ì¥E>Ì¢°>Ì¥">Ì¥>Ì¥µ>̲è>Ìʈ>Ìs>Ì¡A>Ì­>ÌÊs>Ì͉>Í#ä>ÌÑ^>̪ >̘+>ÌÍ>Ì–ê>̤W>ÌÞ>Ìž4>Ì¡I>Ìž—>ÌšÚ>Ì•Á>ÌŸM>̤×>̇k>Ìžß>Ì|">ͺk>Î>Í2Q>Ì·E>̼ý>̼¯>Ì »>Ìœ¤>Ì«>ÌáŠ>ÌœÇ>Ì›é>Ì£>̧ò>Ì›…>Ìœ8>̱>̈¡>̲¹>ÌÇ>Íó¶>Îà >ÌÆü>Ì C>Ì 9>Ì¡ß>̤)>Ì£x>Ì @>Ìê’>Ì—Î>Ìš>Ì w>̤[>ÌœË>Ì¢¾>Ìžæ>ÌŠ@>ÌÓ1>Íj>ÌÂC>ÌÖ>Ì›>Ì¥9>Ì‘º>Ì¿J>̲>Ì£>Ì›>ÌãÉ>̦>Ì–æ>Ì™¨>Ì¥>̦P>Ì®>̃N>Ì£>Ì´º>̨©>ÌÇÒ>Ì­>Ì´L>ÌœS>ÌÃF>Ì´>̦ã>Ì—>ÌŒ†>ÌãÒ>Ì¢u>̧m>ÌŸ5>Ì¡“>Ì ,>Ì‘…>̃ï>ÌÀæ>̱J>Ì…›>Ì® >ÌŸ^>̤Q>Ì´Æ>Ì«Ö>ÌÀ>Ì¥K>̤¥>Ì“½>Ìêƒ>̧>̰?>ÌšÉ>Ì”œ>Ì›:>Ì„a>Ìl>̬O>Ì—ô>Ìô>Ì©¯>̯ç>Ì–™>̦è>Ì«f>Ì•í>Ì>ÌŸp>ÌšA>Ìûd>̦z>Ì®a>̧ >Ì—´>Ì—N>Ì”>ÌË>Ìž…>Ì› >Ì¢‡>̦U>ÌŸ\>Ìž„>Ì›_>Ìš>Ì£·>Ì¢è>ÌšÓ>Ì£ò? *Û>Í&g>Ì¥|>Ì¡þ>Ì–Ý>Ì–R>̘‘>Ì‹=>Ì•J>̘å>̘">ÌŸÞ>Ì£d>ÌœN>Ì•;>̘S>Ì©¨>Ì®‡>Ì›X>ÌÛ?E.? ^Þ>͸>ÌP>Ì—>̘‚>Ì™Ú>Ì‘Ì>̘È>ÌœÁ>Ìš>Ì¡ >Ì£v>Ì¢¡>̨÷>Ì Ø>Ì¢É>Ì©>ÌŸÖ>ÌŸö?sT?tº?sò?jé?Y£?>b?œÀ>Ï‹>̧~>̪{>̪Ê>̱>̬D>Ì ,>Ì­«>̱>̱?À'?æM?Ké?tï?tÝ?sž?r”?o2>ÍQƒ>ÍíE>Ì–Š>Ì„>ÌQ>̃ˆ>̇)>̆á>Ì‚ö>̆û>Ì…™>Ì‹ë>Íâ,>Ìß*? „&?s}?s?rÓ?´A>Ï–ý>̆ó>ÌŽ>Ì‹3>Ì€>Ìvk>Ì„X>Ìy¸>Ìv}>Ì„ò>Ì‚>Ì||>Ì€v>̆„>ÌŒ+>ÌÊ$?j–?qq?µ:>гj>Ìz.>Ì…Ê>̈>̆ã>Ìxd>ÌtA>Ìz%>ÌoS>ÌpÚ>Ìy©>Ìn›>Ìw>Ì‚_>Ì,>Ì…%>ÌŒ´?Z?q >Î~G>̃Í>Ìx%>̃/>Ì€û>Ìvj>ÌyÐ>Ìtá>Ìh¦>Ìb(>ÌbŽ>Ìq>Ìoo>Ìq>Ì>Ìxd>Ì}•>Ì‚‘??E>ÍV>ÌŒö>̈Ê>Ì€>Ì„Î>Ì‚è>ÌsŸ>Ìpƒ>Ì`°>ÌSs>ÌK@>ÌS¸>Ìd>Ìb–>Ìjª>ÌvÄ>Ìs:>Ì}J>Ìx?‹>ÏT®>ÌŠ­>Ì‚Ã>Ìq->Ì{¹>Ìx>Ìq¬>ÌXö>ÌM•>Ì=\>Ì+ì>Ì4¸>ÌNT>ÌRM>ÌYÊ>Ìk0>ÌiÊ>Ìo>Ìõ>Î=L>ÌŽ¬>Ì‚y>Ì}¾>Ìk)>Ìro>Ìx>Ìf£>ÌE:>Ì)¾>̬>Ìp>Ì>Ì}>Ì>ÌF´>ÌVÂ>Ìj¬>Ìj|>Ì|3>ÌËÉ>ÌvÝ>Ì|,>Ì€J>Ìl5>Ìcƒ>Ìe>ÌPÙ>Ì>Ë÷½>ËÐ?>Ë”R>ËšT>ËÄÍ>Ëö÷>Ì,x>ÌAN>Ìb6>ÌcÐ>ÌsÍ>ÌÌJ>ÌzÍ>ÌpF>Ì} >ÌhL>ÌZ;>ÌRL>Ì7z>Ëév>Ëø>ËVK>ÊÏç>ÊÔà>ËRr>ËÍ">Ìÿ>Ì6‰>ÌC>ÌS7>Ìfµ>ÌÍŽ>Ì}Ê>Ìu®>Ìk>Ìdm>ÌWÛ>Ì5<>ÌY>ËÙH>ËXg>Êc¦>Èîe>Èî>ÊO,>ËJ¡>Ëݰ>Ìô>Ì,>ÌUv>Ìei>ÌÈ>̃v>Ì%>Ìhy>Ìd>Ì\@>Ì?Á>Ì >ËÐæ>Êú[>É< >ÄŽ>Ä”>È©*>ÊÑ>Ë©ß>Ëô>Ì' >ÌUj>Ì]>ÌÉQ>̆µ>Ì€;>Ìo„>ÌcŽ>ÌSÕ>ÌLf>Ì&)>ËÄc>ÊÿŽ>ÈÓ>Ä2>Ä,q>ÈŸŠ>ÊÉG>ˬv>Ìq>Ì,]>ÌO¤>Ìb”>ÌÄÂ>Ì€g>Ìz{>Ìn>Ì_„>Ì_µ>ÌC†>Ì W>ËÏ9>Ë.>ÊBÞ>Èè¤>È«W>Ê(h>ËL”>ËÒô>Ìï>Ì)O>ÌQœ>Ìk»>ÌÌ>̃®>Ì{ˆ>ÌzN>Ìbf>ÌZ>ÌF6>Ì8Q>Ëö/>˲v>Ë:e>ÊÖä>ÊÂ}>Ë:\>Ë®s>ËÒÛ>Ì>Ì@>ÌZˆ>ÌjŠ>ÌÔÜ>Ì“á>̇=>Ì~ø>Ìh¹>Ì`?>ÌP>ÌSÖ>Ì ½>Ëý$>Ëè˜>ËŽœ>Ë›3>˼«>Ëô>̽>ÌK3>ÌO>Ìf>Ìln>Ìà@>Ì—å>Ì…>Ìw‰>ÌsE>Ìq¾>ÌYC>ÌVÝ>Ì1>Ì.>Ì^>ÌÃ>̨>Ëû‚>Ì>Ì5e>ÌaÙ>Ì\H>Ìj>Ìf“?à%>ÏU1>ÌŒ¤>Ì{×>Ìv„>Ìx>Ìd#>Ì_f>ÌTV>ÌE¯>Ì6<>Ì/ž>Ì+_>Ì40>ÌH1>ÌN½>ÌdÎ>ÌeA>Ìl¡>ÌlÅ?çî>Í8E>̾>Ì‚—>Ì >Ìyà>Ìl>Ìg!>Ìj„>Ì\¡>ÌJÖ>ÌI5>ÌK>ÌW>Ì]1>ÌP‰>Ìjõ>Ìic>ÌsZ>Ì~H?M4? G*>Í¥>̉&>̆ý>ÌÑ>Ìu‡>ÌiÊ>Ìs>Ìn|>Ìc>Ìiž>Ì`¶>Ìq?>Ìk~>Ìf>Ìs–>Ìm>Ìyú>̆ÿ?p?pŸ?o&?e?R¤?9œ?>ÍFG>Ì”ƒ>ÌŒÌ>Ì•>Ì“»>ÌŽw>̇>Ì‘’>Ì™Ð>Ì¢q? $´?*$?GD?rÂ?s¦?p‰?k›?gd>ÍD´>̰Ø>Ìe‚>Ìjš>Ìl(>ÌpË>Ìij>Ìd>Ìg>Ìmà>Ìqö>ÌpÏ>ÌÁt>̼? ¦?n|?mc?kr?f>ÍaŸ>Ìpi>Ì`>Ìb>Ìe>Ìhå>Ì[ª>ÌX>ÌYz>ÌWâ>ÌZ0>Ìc>Ìcr>Ìjø>Ìq3>̪?dB?i«?e:>ÍjŽ>ÌpÂ>Ìoß>Ì^Ì>Ì[ö>ÌW&>ÌUä>ÌBF>ÌD—>ÌX¾>ÌI¡>ÌM>Ì[h>Ì_>Ìbt>Ìj¥>Ìoÿ?RŠ?e¡>ÍK >Ìvx>Ìl†>Ìkc>Ìdi>ÌW >ÌE">Ì=B>Ì.Ž>Ì0 >Ì=¯>Ì2Á>Ì6Ó>ÌJå>ÌE>ÌT€>Ìi|>ÌkÝ?9ê>ÍEŠ>Ìp>Ìku>ÌqY>Ìe’>ÌOk>ÌD >Ì0È>ÌK>Ì >Ì">̤>ÌY>Ì…>Ì3Ø>Ì.§>ÌH…>Ì]…>Ì`+?á>Í [>Ìe…>ÌcO>Ìd»>ÌW>Ì:>Ì$°>̬>Ì=>ËÝÌ>Ëì¯>ËÙ^>Ëõç>ËúH>ÌÓ>Ì(e>Ì=>ÌEÆ>ÌOg>Í&>Ìuv>ÌdV>ÌV»>ÌLk>ÌJZ>Ì* >ÌÝ>Ëëi>ËÅI>Ë ì>Ë©>Ë–—>Ë¢…>ËÆÍ>Ëâ >̲>Ì'>Ì6>ÌA>ÌÀÛ>Ìoã>Ì`|>ÌK7>ÌB°>Ì5A>Ì Ù>ËÞ®>ËÄo>ˇj>ËW>Ë.>ËŽ>Ë)E>Ë›µ>˼ >Ëè>Ì>Ì-l>Ì9È>̼5>Ìg£>ÌV©>ÌF¶>Ì9¨>Ìq>ËüŠ>ËÍD>ˈi>ËÂ>Ê–_>Ê@y>Ê;>Ê•h>ËM>Ëv>ËÜñ>ÌK>Ì's>Ì7>̼ï>Ì]·>ÌUK>ÌJç>Ì2f>̺>Ëì’>Ë’«>ËL«>Ê©Q>É´¯>Èì§>ÈÎÇ>É¢F>Ê‘>Ë6ô>˵C>Ëûs>Ìú>Ì/Y>̶>Ìg>ÌX >ÌN,>Ì5Ä>Ì M>ËÜù>Ë”p>ËÛ>Ê8 >È©É>Ç/>Æú>ÈÇ>Ê$Ã>Ëq>Ë«>Ëëµ>̹>Ì%p≯x>Ìt¥>ÌUÃ>Ì@N>Ì7.>ÌÍ>ËÝÈ>ËšË>Êö6>Ê46>ÈÂÀ>Æøþ>Æò>Ⱦ{>Ê+œ>˲>Ë¥>Ëæ >Ì>>Ì/¬>̺ >Ìk­>ÌX¹>ÌEË>Ì;p>Ì>ËäR>˯š>Ë54>Ê™á>É¿á>Èeà>È |>É3>Ê_;>Ë-7>Ë™>Ëä>̇>Ì2z>̹•>Ìm¤>Ìd&>ÌN¢>Ì:õ>Ì9>Ëôz>˳$>Ë‚=>Ë ý>Êx>Ê>Ê@>Êu >Êúá>Ë|¥>˾Ñ>̤>Ì!g>ÌA>ÌÀ>Ì^{>Ì[Ï>ÌZr>ÌJ>Ì$P>̪>Ëî>˽,>ËtZ>Ë ]>Ë.>Êøà>Ë$„>Ë‚É>˺:>ËØ½>Ì$>Ì'î>Ì?·>ÌÕÈ>ÌtÌ>Ì_›>Ì\Æ>ÌLn>Ì>?>Ì6Ø>ËðÝ>ËÚø>ËÇ2>˶3>Ël‰>Ëo>>ËšÊ>Ëê>Ëã;>Ëò+>ÌÅ>Ì@C>ÌGT? Ë>Í>Ìe>Ìl=>Ì\²>ÌRT>ÌD>Ì%t>Ì {>Ëõ½>Ëë>ËÐO>ËÄc>Ëà´>̆>ÌZ>Ì'Ç>Ì7`>ÌI7>ÌR”?*Ú>Ìúâ>ÌpŒ>Ìkm>Ì]â>ÌX/>ÌLü>ÌC?>Ì%>̲>Ì|>Ìi>Ìÿ>̬>Ì5¿>Ì1!>Ì;7>ÌB>ÌI*>ÌYï?GÃ? Sþ>Ìõï>Ìq,>Ì`¥>ÌX`>Ì\à>ÌN§>Ì=N>Ì6>Ì,»>Ì,L>Ì3æ>Ì<„>ÌI+>ÌD„>ÌEÐ>ÌQÏ>Ì]•>Ì_›?k%?iÅ?h‚?b*?W?Bb? ?#>Ïg;>̇®>Ì‚Ñ>ÌK>Ì} >Ì}þ>Ì>̇,?¨I?Ð`??g?L™?hƒ?eÍ?h‘?d>?^\?£´>Ïnf>ÍÂy>Ì[Ó>ÌN›>ÌJi>ÌY°>ÌPa>ÌJ>ÌIn>ÌV>Ͷ©>̰ª?ÚÄ?ƒ?gÙ?eN?cÁ?`>Í">Ͱì>Ì[5>Ì\>ÌR¶>Ì:7>Ì6Ì>ÌA™>Ì6Ÿ>Ì3>Ì=á>ÌQe>ÌLW>ÌLƒ>ÍŸß>Ì·ø?aù?b„?_Ï>Í>ì>ÌY”>ÌGÓ>ÌD÷>ÌK¦>ÌE«>Ì&Z>Ì(Ä>Ì)Ò>Ì’>Ìú>Ì,Œ>ÌCF>Ì4d>Ì7Ð>ÌNÔ>Ìb¨?Wv?_>Í)@>Ì]}>ÌS>ÌE >Ì>Å>Ì37>Ì&t>Ìa>Ì•>Ì 3>Ëøê>Ì·>Ì¢>Ì/p>Ì)§>Ì-Ü>ÌÌY…?B?¤Ô>Ïd>ÌLƒ>ÌDx>Ì:h>Ì(f>ÌÊ>ÌŒ>ËûF>Ëå²>ËÜ_>ËÚŽ>Ëâ\>Ëïí>Ì»>Ì?>Ì(a>Ì/Å>ÌG?#«>ÎR>>Ì],>ÌH>Ì8 >Ì*€>Ìc>Ì>Ëôi>ËÓ>Ë´þ>Ë¥ñ>Ë¥4>˪ë>ËÇÛ>ËØÈ>Ëû>ÌS>Ì"ÿ>Ì3%?‚z>Ï3(>ÌP[>ÌC,>Ì1 >Ì ‡>Ìä>ËÓ‚>ËÑ_>˦>ËlÃ>ËE½>Ë^l>Ë%>Ë‹ç>Ë¢U>Ë· >Ëôw>Ì&>Ì,1>Î&5>Ì^>ÌEò>Ì9}>Ì+0>Ì _>Ëçr>˨æ>Ë— >Ën>Êî>ʹ¶>Êã‰>Ë Õ>Ë#ö>Ëft>˪m>ËÙE>ÌÓ>Ì%>̨«>ÌVë>ÌMë>Ì.>Ì!‚>ÌG>ËÁ >Ën¦>ËNš>Ë '>ÊcÕ>Ê,m>ÊÓ>ÊYä>Êçó>Ë#÷>Ë~}>ËÍO>ÌÖ>Ìd>Ì›ú>ÌUÁ>ÌQ@>Ì?\>Ì>ËòO>˰×>ËbÃ>Ë<>ÊmÑ>ÉÛÊ>Éu>É,/>ɲ>ÊRg>ÊØ2>ËcÚ>ËÈÍ>ËñØ>Ìä>Ì¡`>ÌPß>ÌGÏ>Ì9ì>ÌÄ>Ëï˜>˸»>Ë_ >ÊÈy>Ê$p>É|«>ȵ½>Èí>É<>Ê&Ú>Ê¥Þ>ËZ†>˱Ÿ>ËÙp>ÌÆ>Ì T>ÌCq>ÌBW>Ì.2>Ì >ËêA>Ë­J>Ëj>ÊÑ >Ê>É_…>Ȉ>È£’>É>>Ê5j>Ê¿G>ËB>˘k>ËÙ6>Ì>Ì™,>ÌO›>ÌLs>Ì3¾>ÌÝ>Ëä>˲e>Ë\>Êú'>Êg>ÉÈo>É'2>É=è>É­>ÊpC>Êþi>Ëol>˪>Ëæ×>Ì g>̨Ù>ÌMÚ>ÌGÁ>Ì;\>Ìß>Ëð>Ëʦ>ËŒþ>Ë"¸>ÊÓ>Ê{4>Ê l>Êc>Ê[ >Ê»;>ËV>Ë—8>˾k>Ëëm>̨>̵Á>ÌV>ÌOã>Ì9Ð>Ì ¢>Ì>Ëã³>˶g>Ëxq>Ë2ì>Êâ1>Êæ[>ÊâE>Ê÷Y>Ë>ËŽ§>ËÂå>ËÝ»>Ì>Ìÿ?Æ4>Ï>ÌQé>Ì@ö>Ì-z>Ì >Ìã>ËÝë>˺~>˃%>ËB¼>Ën>Ëm>ËjD>Ëos>Ë©>Ëà„>Ëý>̺>Ì(¡?Ñ>Ìû>ÌS<>Ì?!>Ì:®>Ì*k>ÌJ>ÌY>Ëé0>ËÈ’>˳ñ>Ë®Æ>˶ì>˹Y>ËÁí>Ëå>Ëí¿>Ì C>Ì#›>Ì-{?>?S>Ï5>ÌV>ÌJ²>Ì9 >Ì,>Ì >Ì{>Ëô@>Ëõÿ>Ëè>Ëëð>Ëêþ>Ëíè>Ìf>Ìù>ÌË>Ì-O>Ì;ß?L>?>ÌùÕ>Ì[?>ÌS™>ÌD(>Ì;ú>Ì4ª>Ì"ß>Ì>ÌÐ>Ì £>̆>Ì ­>Ì*>Ì >Ì-ò>Ì.¯>Ì4>ÌI…?g{?dM?aƒ?_~?\µ?Jº?)?I?šj>ÏY->Ìn±>ÌgÖ>Ìkò>Ìq#>ÌqÕ?Ÿž??)?G¶?R÷?cá?_G?[v?\½?Y\?R>Í;«>Ì’ø>ͦ;>ÌGï>Ì7 >Ì0h>Ì5È>Ì;û>Ì7æ>Í‘o>Ì©ì>Ì™? Q?N?bÐ?^ü?[Ì?Yò?Ÿ{>Ïyù>Ì@Ø>Ì/(>Ì2Z>Ì-!>Ì">Ì…>Ì)¢>Ì5¿>Ì->Ì,?>Ì9º>ÌH’>Ì’-?ð{?`‹?\g?Y?žd>ÐŒn>Ì>>Ì06>Ì ¤>Ìè>Ì>Ì>Ì">Ì >Ì>Ì”>Ìþ>Ì%ø>Ì5j>Ì<þ>Í€c?]~?X¬? •>Гˆ>ÌEˆ>Ì.>Ì…>Ì>ÌS>Ëü>Ìî>ËõC>Ëá*>Ëð >Ëú>ËþH>Ìe>Ì-¸>Ì3#>Ì<µ?JÆ?QË>Î]·>ÌD„>Ì5>Ìs>Ìx>Ë÷Š>Ëçá>ËÕ¤>ËÓy>ËÀ<>˼”>Ëʈ>ËÔ)>ËÙX>Ëú°>Ì•>Ìq>Ì2É?&V>Í%„>ÌC4>Ì0í>Ìr>Ì ´>Ëô!>ËÕÎ>˾÷>Ë¥o>Ë™>Ë|µ>ËŒ€>Ë‘">Ë¢U>ËÂ:>ËÑ>Ëó7>Ì y>Ì¿? V>ÌØg>ÌC`>Ì4Ë>Ì>Ëìæ>ËÔ>Ë·>ËšÃ>ËqC>ËMŽ>Ë3æ>Ë=4>ËF?>Ës¸>Ëž¸>Ëž—>ËãÓ>Ìd>Ì y?w>Ïà>Ì>r>Ì/›>Ì õ>Ëåµ>˹ >Ë‘?>ËW7>Ëâ>Êù[>Êë°>ÊØ4>ÊÛd>Ë,e>Ëp>Ë¡\>ËÆ>Ëã>Ëô×>ÎŽ>ÌOU>Ì+å>ÌY>Ì–>ËÓí>Ë™ê>ËX>Ë.>ʸÃ>ʉI>Êg®>Êd}>Ê“´>ÊÀ;>Ë >Ë}>˦N>ËÙp>Ëîˆ>Ì›ú>ÌFl>Ì,ç>Ì â>Ëþä>ËØü>ËŠI>ËI>Êô˜>ÊŸ#>Ê-`>ÉÒ’>ÉáË>ʪ>Ê‘t>ÊÔí>Ë/è>Ë|8>ËÂ>ËçX>Ì–{>ÌDÚ>Ì7e>Ìì>Ëîç>ËÃ!>ˇ2>ËBl>ÊçL>Ê|V>ÉÍç>ÉŠ>É”†>ÉÛ®>ÊY>Ê·Y>Ë*ø>Ë‚/>ËÀ©>Ëê¹>ÌŒó>Ì@B>Ì2³>Ì–>ËçÉ>Ë¿”>Ë¿>Ë@g>Êñ¨>Êf*>ÉÜ>É>É{5>ÉÒd>Ê7«>ÊÇK>ËK`>Ë“©>˺‹>Ëì‘>Ì•/>Ì?>Ì->Ì#ª>Ëó£>ËŽ>Ëœ>ËTë>Êø5>Êz«>Ê(2>ÉóÀ>ÉÝc>Ê<>ÊŒô>ÊÜù>Ë=;>Ë™;>˹X>Ëë€>Ì¢“>Ì=J>Ì*>ÌÍ>Ëú<>Ë×>˱>Ë…W>ËN>ʯ>Ê€,>Ê“J>Ê[­>Ê|Ó>ÊÈþ>˾>ËŽ>Ë´€>ËÒ~>ËõÂ?Ä>ÏÛ>Ì2ö>Ì>ÌŒ>Ëî³>Ë»>ËŒ,>Ëe>Ë y>Êð=>Êà+>ÊÍß>Êõ>Ë á>ËF…>ËŒý>ËÀÇ>Ëß>Ì~?Q>Ìú >Ì;o>Ì/>Ì É>Ëþ>Ë̘>Ë»P>Ë >Ë\ÿ>ËE >Ë&.>Ë/z>Ë\C>Ë^>Ë|6>˨û>ËÐM>Ëê€>Ì~?+‚>ÌÐ>ÌB>Ì1ã>Ì"[>̽>Ëô>ËÛÂ>ËÄö>Ë¥Å>Ë:>ËŠß>Ë“K>Ë’Y>Ë•r>Ë©h>ËÌš>Ëç|>Ìk>Ì! ?GÔ? OÝ>ÌÛL>Ì@>Ì(™>̶>Ìv>Ëüb>ËæI>ËÖG>ËÇ>ËÄs>ËÈS>ËÃê>ËÄ>ËäØ>Ë÷œ>Ì:>Ì…>Ì4?S8?M8?)>Îóc>Ì1ß>Ì* >Ì'>Ì€>Ì?>Ëú«>Ëõ~>Ëã¿>ËáÔ>Ëã>Ëìç>Ìf>Ì7>ÌJ>Ì+_>Ì?3?d*?`Ò?]e?ZŒ?X ?M^?;Š?'Æ??ƒŽ?hj>ÏXc>Ìnþ?¦†?e?õ?%?;-?Kk?Qn?aR?]?Wß?TZ?Rf?NÝ?Z>Ï]ß>Ìè>Í”ß>Í È>Ì13>Ì/ƒ>ÍŽ&>Íž(>ÌŽç>Ìyã?ÆS?üu?Rt?_?Z’?U:?P7?J…>Í)ì>Í“>Ì0>Ì#®>Ì'ü>Ì"í>Ì•>Ìê>Ì"g>Ì >Ì>Ì(½>ÍR>Ì€? Ùˆ?\”?WÄ?S?IÇ>Í3W>Ì5/>Ì%²>Ì x>Ì 6>Ì â>Ì”>ËøÒ>Ëüë>Ì!>ÌØ>Ìž>Ì6>Ì11>Ì1×>Ì| ?Yq?S¤?JÀ>Í(>Ì$¸>Ì#>ÌŽ>Ëòy>Ëì>Ëã´>ËÞû>ËØý>ËØ >Ëå’>Ëá¬>Ëæm>ËýC>Ìr>Ì É>Ì º?N?P<>Íú>Ì(Ò>̹>Ì 4>Ë÷>ËÞï>ËÔ{>˸j>˶à>˲>˧ >ˬ%>ËËá>Ëл>ËÝW>Ëó6>Ìp>Ì=?9Ý?œ=>ÎþN>Ì#ª>Ì>Ëø >ËÝq>ËÃ?>˰û>ËŽ”>Ë„8>Ëtý>Ëi3>Ë}#>˘þ>Ë«=>˼4>ËÑò>Ëúý>ÌÓ?&è>Î36>Ì4>Ì>ÌÑ>Ëíº>ËÄZ>Ë¥ñ>ˈ1>Ëbæ>ËOï>Ë;9>Ë:ª>ËH<>Ëf~>Ë|°>Ëœj>Ë·™>ËÛ>Ëþ5?G>ÌÓÞ>Ì&0>Ì>Ëó8>ËÛ¡>˪¨>ˆG>ËX½>Ë)Â>Ë “>Êä^>Êòæ>Ê÷[>ËF>ËL>Ë~õ>Ë¥ >ËÏ>ËìÏ Ï>Ìs>ÌÆ>Ëèƒ>ËÀ~>Ë—5>Ëo7>Ë:L>Êþ˜>ÊÌ@>ÊŒ>ÊŸ¸>ʳà>Êç>Ë$>Ë\O>˶>˱©>Ë×Ò?hÃ>Ïs>̾>Ëú–>ËÙJ>Ë­ç>Ë…>ËPÓ>Ë>ÊÂ@>Êk>ÊN÷>Ê^v>Êl'>ʪ->ËÎ>ËA>Ëzù>˪Y>ËѾ>Îz>Ì:4>Ì ¼>Ëõå>ËÛÝ>Ë©!>Ëv#>ËD:>Êù>Ê¢À>ÊN >Ê']>Ê>ÊF>Ê›™>Êçq>Ë&¦>Ëq‡>˪$>ËÒÆ>Ì”>>Ì-o>Ì,>Ëûž>ËÞ˜>˳u>˃n>ËO6>ÊùF>ʤí>ÊCÄ>Êž>ÊR>ÊRT>Ê™H>Ê×H>Ë!>Ëmo>ˬà>ËÑ ?Ïÿ>Ï5>Ì">Ëü >Ëà2>˹à>ËŽ>ËX­>Ë,>ʸƒ>Êpn>ÊIë>Ê>•>ÊsE>Ê»õ>Ê÷Ô>Ë>c>Ë…˜>Ë·>ËÞZ?e¡>Ïu>Ì(è>Ëû‘>Ëå§>ËÀ¦>ËžJ>Ëlì>Ë6¤>Êê€>ʺ>Ê•o>Êä>Ê·>Êê¢>Ë(Á>ËS>Ë”‘>ËÇ•>ËíA?õ¸>Ìäñ>ÌŠ>Ì >Ëð!>ËÖ“>Ë´?>Ë >ËRº>Ë4“>ËÚ>ÊáÀ>Êßï>Êÿž>Ë't>Ë[‚>ˆí>˶e>ËÛ>Ëò0?#Ç>ÌÀ >Ì4>̱>Ë÷L>ËáÐ>ËÐb>ˤ»>Ë}ý>Ëh¦>Ë>[>Ë+*>Ë,è>Ë<:>ËTC>ˉË>Ë©¢>ËÄÓ>ËèŸ>Ì 9?0Ÿ?ø¿>Îåö>Ì!å>Ìé>Ëðy>Ëä—>Ëĵ>Ë¥Î>Ë•N>Ër‘>ËtQ>Ëo>Ë{¸>ˆ*>Ë­ä>ËÌ[>ËÛì>Ëøà>Ì {?J?I>Ìáw>Ì-Y>Ì(>Ì Û>Ìâ>Ë߉>ËÉs>ËÀ1>˰œ>˲>ˤs>Ë©:>˺ô>ËÑ >Ëé>Ëú\>ÌË>Ì  ?Qô?S? ¬•>ÌÐF>Ì)ä>Ìd>ÌÆ>Ëþà>Ëö@>Ëì*>ËÝ¥>Ëá’>ËÕR>ËÏÿ>Ëßs>Ëóð>ÌG>Ìu>Ì >Ì3?ae?^?Z|?W"?TR?Pä?Lù?Bë?0‘?‹?$?ƒe?kf?ÏF?8?.Ç??]?J?O¿?P¸?^Ÿ?Zr?Tõ?PP?NG?Kò?Dg?—?x>Ï`˜>Ìiü>̓°>Í•·>Ìã>Ìr?¼Ý?p@?v?IG?N?\-?W_?Pè?K?IÕ?›'>Ï\ƒ>̓,>Íy8>Ì D>ÌP>Ì ¦>Ì <>Ì>Ì d>Í_<>Í‚B>Ì‚L?Á7?øÌ?Y¤?T‚?L ?Dà?™>Ðnß>Ì1>Ì È>Ìq>Ëñã>Ëý:>Ëñ¥>ËëÉ>Ëã2>Ëõ >ÌE>Ì)>Ìý>Í_U?ÞÎ?VN?P:?I?•%>Ðc>̤>Ì´>Ëò>Ëç§>ËÛR>ËÍõ>ËÈà>ËÍÜ>ËÌ3>ËÙ$>Ëè…>Ëö>ÌM>Ìe>ÍbÐhÁ>Ì Œ>Ëû>Ëúu>ËÜ“>ËÁp>Ë´Q>˯û>Ë©>˨ß>˵8>˶Â>ËÅ¡>ËÚï>Ëó{>ÌU>Ì ]?Mä?F8>ÎD_>Ì>Ì z>ËëÉ>ËÙ>Ë«>Ë©<>ËŠÃ>Ë|»>Ë‚<>ËvÏ>Ëô>Ë”ý>˧¤>ËÁ…>ËÔû>Ëóû>ÌØ?Bœ?˜Q>Îæu>̈>Ëû>ËáI>Ë¿>Ë¢>Ë€Ñ>Ë^°>ËDg>ËT’>Ë4H>ËI >Ëh3>ˇÑ>Ë®2>˹»>ËÖT>ËõÎçL>Ì6>Ëî>ËØž>Ë­_>Ë}í>ËVä>Ë9”>Ë ý>Ë(>˵>Ë>Ë2Ä>Ë] >Ëõ>Ë™ð>Ë·Í>ËíŸ?U>Î=¤>ÌÂ>Ëó¢>ËÛÃ>ËÈl>Ëš¦>Ë_·>Ë3Ü>˹>Êäb>ÊÎW>ÊÇ^>ÊÜf>Ë„>Ë6‹>ËjC>ËŸ>˨ô>Ë×+? Ö>̹@>Ëþc>Ëë>ËÐÒ>Ë´,>ˈ;>ËVC>Ëé>Êѵ>ʵg>ÊœK>ÊŠ)>Ê©‡>Êâ6>Ëï>ËR,>Ëp>Ë->ËÈê?sÌ>ÎðA>Ì>Ëéå>Ë˵>Ë©3>Ë~\>ËH¢>Ë 8>ÊÐð>ʦJ>Ê‚x>Êg>Ê‹Q>Ê¿»>ÊþÃ>Ë:_>Ëhº>˘ú>ËÄå?ip>ÎøÏ>Ì!>Ëíá>ËËý>˦ä>Ë~>ËN›>ËF>ÊØ+>Ê >Ê~û>ÊcÓ>Ê•¹>ÊÇj>Êö€>Ë.B>Ëi„>Ëš®>ËÃÓ?ÕÀ>ÌÖ\>Ì >Ëò‡>ËØû>˲>Ë„ö>Ë`>Ë+ >Êüê>ʺÛ>Ê• >Ê“>ʽC>Êßñ>˧>Ë@[>Ëxv>˨ >ËÂÖ?Ð>ÌÄ>Ì 6>Ìn>Ëáa>˽>Ë—Â>Ël>Ë2>Ë>ÊêŒ>ÊÅ$>ÊÌi>Êç@>Ë >Ë*æ>ËOŒ>ËŠ9>Ë»¬>ËÔ*?.-?>ÎÉ)>Ì ×>Ëë¤>˽[>˦K>Ë~7>ËD¤>Ë27>Ë'¸>˲>Ë›>Ë;>Ë2Ó>ËQ½>Ëz¯>Ë£K>ËÌ4>Ëå?@ ?„0>ÎáK>Ì÷>Ë÷‚>ËÔš>˲Þ>Ëžð>Ëy8>Ëj’>Ë^ˆ>ËCD>ËJ>ËMš>Ëci>Ëy¥>Ë©!>ËÁî>ËØ–>Ëùx?JÐ?" >Ìæ>Ìk>Ì®>Ëåm>ËÝÉ>ËÂ>Ë¡•>Ë•O>Ë~ú>Ëq>Ën¿>Ë}t>Ë”Ï>˪¨>ËÁL>ËÛ>Ëòv>Ìw?Q?LM?ûÃ>ÎÃÉ>ÌM>ËûÓ>ËõÞ>Ëâs>Ëʈ>˹9>Ë¡>Ë Â>Ëœú>˨è>˼ñ>ËÄÕ>ËÓF>ËóT>Ì ²>ÌE?R?Q ?ûõ?á>ÎÂÅ>ÌË>Ì>Ëýf>Ëé>ËÜ{>ËÑD>ËÑ7>ËÍ­>ËÐÐ>ËÝ´>Ëçv>Ëï}>̵>ÌE>Ì?_Q?\&?X¢?U?Q²?N%?J_?Bü?8P?/º?( ?$(? ·?%’?.ç?7h?Ay?Hì?M|?Oá?\g?X”?Sö?Oy?KŸ?Gˆ?DD?=T?7‚?–—?$?‡€?‚f?}Ë?ö?óL?;‘?AÚ?F€?K™?YÙ?U¨?Oa?IX?DH?=ê?™ß?ƒB>Ï=(>ÍiŸ>Ío&>Ím`>ÍcQ>Ídù>Ín¶>Ìlü?Ö÷?{#?t?D ?Wß?V ?JŒ?A“?:Ë?¯‘>ÐV >Íp©>Ëý>Ëê¥>Ëݶ>Ëë¹>Ëëä>Ëä$>Ëëú>Ëöƒ>ÍL¢>ÍmK?Ë[?(?T?OÌ?Eä?;Ñ?¨Í>ÐTC>Ëû,>Ëóî>ËåG>ËÐg>Ë¿">ËÌ­>ËÒ×>ËÅO>ËÎP>ËâÈ>ËìŒ>ËíŒ>ÍAä?á¼?OÊ?Iq?@*?³B>ÐXð>Ë÷¥>ËàÛ>ËÚÍ>˾É>˲>Ë ^>Ë«Ñ>Ë«ë>Ë¡>Ë­9>ËÏ>ËÖŽ>ËÚï>Ëê>ÍKo?KÁ?DÉ?œÿ>ÐY´>Ëý÷>Ëå@>ËÒT>ËÁl>Ë #>Ë‹y>Ë{!>Ë~|>ˉË>Ë„‘>Ë›>˦">˼ë>ËÏþ>ËÝŸ>Ëò_?D2?=×?">Îê8>Ëö">ËÌY>˺I>˰,>Ë„ý>Ëf}>ËZ3>ËNT>ËPø>ËSù>Ës>ˉÂ>˘¨>˾>ËÒn>ËçÖ?9 ?8 >Îý>ÌÍ>Ëê+>Ë»1>ˤÍ>Ë–>ËmÝ>ËA{>Ë1¿>Ë+`>Ë%Ò>Ë2ƒ>ËN³>Ëj}>Ë„J>ˬ¥>ËÊ >ËÝh?.d?•Ð>ÎÏ@>Ëó >ËÚä>˵>Ë›ñ>Ë|§>ËH>Ë}>Ë>>Ë+>Ê÷8>ËW>Ë%>ËF’>Ëgx>Ë‘È>˼p>Ëܘ?$-?ˆž>Î͹>Ëê¯>ËÍ >Ë«È>Ë›7>Ëhf>Ë2%>Ëî>ÊãÔ>ÊÚÖ>ÊÓ´>Êï½>Ë>Ë,Š>ËW±>Ë„“>˪”>ËËÛ?,?˜)>ÎÐn>Ëç>ËË{>Ë®ô>ËŠ«>ËS•>Ë0>Ë]>ÊÔ×>ÊÆ(>ÊÂB>ÊÎü>Êþf>Ë÷>ËUo>ËsÂ>Ë›¨>Ë ? +?›¸>ÎÕ8>Ëäí>ËËá>Ë­k>Ë…O>ËRÁ>Ë*Ï>Ë ë>ÊÖG>ÊÆ«>ÊÀ>ÊÔ‹>Ë>Ë•>ËQ#>ËuK>Ë—g>˾ê?%5?l>ÎÚÚ>Ëê>ËÑ >Ë´•>Ë`>Ëb`>Ë9œ>Ë>Êé7>ÊѰ>ÊÕ>Êê÷>Ë>Ë+M>Ë]º>˃Œ>Ë£f>ËÅÎ?.1?k>ÎÙ>Ëó¢>ËÚ«>ËÀ?>Ë—>ËuL>ËNÔ>Ë.Í>Ë×>Êïy>ÊöG>Ë;>Ë"G>ËBG>Ëh7>ËH>˰$>ËÒØ?8'?ÿŽ>ÌÉ*>ËøÍ>Ëá¹>ËÊŒ>˪w>Ë}ì>Ëbõ>ËR÷>Ë7­>ËÞ>˨>Ë'$>Ë@æ>ËX=>Ë~Î>Ëžw>˾Ì>ËÞ?B¯?<8?$#>η`>ËîÈ>Ë×>ËÆÝ>Ëœ'>Ë€*>Ën)>ËW>ËM)>ËL>ËQ>ËiS>Ë{X>Ëœm>˹ >ËÐ×>Ëã`?J?C?Ž•>Îî.>Ì>ËâÌ>ËÇC>˽·>Ë®P>Ë‘?>Ë‚b>Ë}`>Ët^>ËyØ>Ë<>ˤ>˹”>ËÎÐ>ËÞ¶>Ëëj?NM?H+?£?ï>ιÑ>Ëôa>ËÐ,>ËÊŠ>ËÉ4>˺j>Ë©%>˦#>Ë«G>Ë«g>˯¨>Ë€>ËÖd>Ëéh>Ëô >ÌM?Pt?L–?DÌ?.å?&\>εæ>Ëð>Ëä'>Ëà>ËÖz>ËÎß>ËÅ>ËÎ>ËϪ>ËÑØ>Ëàm>Ëóð>Ìñ>Ì ý?aGDy—šDy—šDy—šDy—šDy—›Dy—›Dy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—Dy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—oDy—šDy—šDy—šDy—šDy—šDy—›Dy—šDy—›Dy—›Dy—›Dy—œDy—œDy—œDy—œDy—›Dy—›Dy—šDy—šDy—™Dy—4Dy—šDy—šDy—šDy—°Dy—áDy—Dy—šDy—šDy—Dy—€Dy—˜DyŸÇDy Dy—ÙDy—®Dy—°Dy—šDy—šDy—™Dy–ÜDy—›Dy—šDy—šDy—®Dy—ÚDy—³Dy—áDy˜!Dy ‰Dy¡F~2bBª:•AüÝŠ@¿>nB;öB-ÛEÑn~FD/RF {ÍF…ñD’yDy—šDy—œDy—šDy—šDy—®DyŸBDyŸÒF„nF~§B±¥×A[îAÿ@ÿbâ@ÿñöA»ÇBBeÇEΞ^FC¿ÆF eôDŒfDyžDy—œDy—šDy—šDy—®DyŸFUF nIBrZ´Agi@üà@ø’ý@õvç@õa&@ø*Ø@ýx(BaÀEÎ|uEß!F÷”D’%µDy—œDy—šDy—šDy—˜Dy¯Fo'F~ ÞBdh›A5½@ø @ò¸ç@ï—@ïw.@òBz@ø8oA0EÏÒaF5QF òÒDŒŒŒDy—œDy—šDy—šDyŸÈDyšFüÕB€^ª@Éøë@ÿ+­@õ¶µ@ï—@ì]û@ìT€@ï t@õ5@ÿX•BÑIEÎC±EàÍDÛ—Dy——Dy—šDy—šDy DyŒrFÿ2B§‚@½’G@þê@õ² @ï;@ìVÍ@ìF±@ïS@õ@ÿrk@Ã¥ûEÐPÑEÞêD×âDy—–Dy—šDy—šDy—ØDy‹ôF² E¹ÐDAë^A÷·@øjÏ@ò{Ý@ïCH@ï4@ò­@÷ý‡A¾ÀEÁŸE¸YáFÌÂD’ÜãDy—–Dy—šDy—šDy—€DyØFÎÄF MÌB¾A„Z@ý¦Ú@øƒ:@õP£@õJS@ø¥@þlA³2EÐ%EÛÖ”F RÍD’ã.Dy—–Dy—šDy—šDy—€Dyž±D’6zFâºE¹ë´BE¡A_¥Aê_@ÿÍ‘@ÿ²‚A›dAÃE¿<¬E·Î-F gD®_‚DzHDy—šDy—šDy—šDy—™Dy—ODy¡>Fã°F CÂE»rUAÞì¨@ÄãS@¾Ü¿@®@È!ñE¾’iE¸]aFòÄF ûD“”VDy—JDy—šDy—šDy—šDy—©Dy—€DyŸVD’!?ŒÈ+?€?€?€?€?€?€B¶B½A¡ÖºEÂ?¢Eº=NF l°Dy—šDy—šDy—šDy—ÔD’8HF•ÛB2AAfTÒAbŽ!?ŸØC?€?€?€?€B¶ÄA£–£Ae‘fEѪýEß9ŒDª¢Dy—šDy—šDy—šDy—ÐDy£CFöE¾ 9B˳Ai”AiªÅAbƒ?ŸPúB¶­AŒˆA‹kDAj9 EÀu!E»jF ˳D“.}Dy—šDy—šDy—šDy—žDy—µD’yF#EÀ(ŸE·p B$ˆ^AlawAq‘ËA=AAnAþEÀ˜EžjEߦòF êàD­èçDyøDy—Dy—²Dy—ÝDy˜Dy˜ŸDy¾@D’1)DŽ@nF"³EÀ¸FEºýPEºþÙEºûgEºîKEá2µF }DªÅÏD“%DyôDy–ÀDy—›Dy—šDy—šDy—šDy—šDy—šDy—šDy—›Dy—›Dy—›Dy—œDy—œDy—œDy—›Dy—­Dy—Dy—šDy—šDy—™Dy—5Dy—›Dy—šDy—™Dy—šDy—šDy—šDy—šDy—šDy—™Dy—™Dy—™DyŸ­Dy Dy—ÛDy—½Dy—žDy—šDy—šDy—šDy–ãDy—™Dy—ÑDy˜ZDy— Dy—šDy—šDy—šDy—™Dy§½Dy¨hDy¨gFxˆFÜ%D¨³Dy­-Dy¨uDy˜Dy—šDy—šDy–iDy—™Dy—©Dy—ÎDy—›Dy—šDy—™Dy§ÌDy¨qFxfÜF€ŠFîBÁ£»EÒ´eFB±ÍFÕ!F€ ÆD¨ŒDy­¼Dy—ÀDy•»Dy—šDy—šDy—›Dy—šDy—™Dy§DFxC­F~ñ¹B¾âhBOSBHÞŸA͇žB„¾kB[­]BM±bEÓŸFBþùF•ÛD§º—DyšvDy—šDy—šDy—šDy—šDyŸkFw!ŒB›ÃBN…ìAÇÚµ?ùì?€L ?†¶h?€µ«?€Y8B–¢ BOåB^’ºEÒúfFEYÎD JDy—šDy—šDy—šDyŸ“Fò:BÅìAÍ]E?—äÉ?†¬×?€?€?€?€Ú™?€?Î%y?©fB•Ó_BŽ(­EÏæ¨EÜn6Dy—šDy—šDy—šDyŸóF{óôBƒ)?ˆb?€?€?€?€?€?€?€?€Ûž?€?Í©¥BMEÎD]F9SKDy—šDy—šDyŸ’F*ºB¬àAÇöÑ?•™ö?€?€?€?€?€?€?€?€?€Öæ?€BnBù«E£~ÈDy—›Dy—šDyŸàF³ŽB* š?Š—?€™?€?€?€?€?€?€?€?€?€?€ÑÒ?Í ËBßE¥ðDy—œDy—šDyŸõFz͸B~IŸ?Ý?€?€?€?€?€?€?€?€?€?€?€ÚW?€BÈ^E„ÕDy—œDy—ŸFïŒB‘p~AikÍ?‡Æ?€?€?€?€?€?€?€?€?€?€?€?€ÌëB¤^ºE~ZcDy—œDy—ŸFnrB5W1AaÆ?Š¿Û?€?€?€?€?€?€?€?€?€?€?€?€Ñ2B¥5E~VDy—›Dy—šD‘ñ³E´«”B*œ?¯?€?€?€?€?€?€?€?€?€?€?€ÐY?€B’O¼E„”Dy—›Dy—šDy¢#F‘B)ù†?€Ø$?€?€?€?€?€?€?€?€?€?€?€Ùï?€B|0E CDy—›Dy—šDyŸôF£ãB0ˆZAh‘Æ?‹Âá?€?€?€?€?€?€?€?€?€Îù?€Bƒ¾.Až ™E¤iPDy—šDy—šDy˜'D‘õCEµ¹IB.øg?½?€?€?€?€?€?€?€?€Ï?€?€BÜE¿îZEµø½Dy—šDy—šDy—ËDy¢¤Fx;B4›¤Ap†£?Œ`õ?€?€?€?€?€Ð’?€?€?€BX¢A¢žEѲEàò¨Dy—šDy—šDy—›Dy—ªD‘؉E¹+4B7üÅAs1 AfH?žá¹?€?€?€?€B”uA‰OjAŽÛMEÀTÝE¼ç4D¦æ–Dy—»Dy—ÝDy˜Dy˜_Dy½ D¶E¼VNEº/ÀB&ãÔAs"An NAk-AkýAm©ìAއyEÁÿE Ë{EátD¦aDy°žDy—›Dy—šDy—šDy—šDy—šDy—šDy—šDy—šDy—‚Dy—€Dy—˜DyŸÇDy Dy—ØDy˜Dy—¼Dy—šDy—šDy—™Dy–ÞDy—›Dy—šDy—šDy—šDy—šDy—šDy—šDy—™DyŸœDy¨$Dy¨fFvFY¡D’UÆDy«ÇDy NDy—ÉDy—šDy—šDy–fDy—šDy—ŸDy—«Dy—šDy—šDy—™Dy§ÉDy¨eFxRÐF€EF›BÁ›âEÒÆ°FB§mFÄÉF€ ˜D¨ŒVDy­½Dy—ÀDy•¼Dy—šDy—›Dy—žDy—šDy—™Dy§DFx]ÃF~úÂBÖ&BMlqBD“A?ë?CûwBm§ÂBM”EÔsDy—„Dy—ÜF˜ëB.Ë ?€ÚÖ?€?€?€?€?€?€?€?€?€?€?€?€?€X»?€uýEXC·Dy—™Dy—¸D‘ê€Eµ"³B-D³?€ù]?€?€?€?€?€?€?€?€?€?€?€?€cÐB“VE•(?Dy—šDy—œDy¢‡FlÁB0Áh?€âF?€?€?€?€?€?€?€?€?€?€?€W‡?€B‘5E¼.Dy—šDy—šDy—©D‘ÖÞE·ïßB3ÄÑ?´?€?€?€?€?€?€?€?€W­?€?€B}»ûE¾QE¹üëDy—èDy˜Dy˜bDy›DŒðeEºVBB5}ºAnL~?‹HI?€?€?€?€?€?€Bø˜AŒæ‚EÁ"°E½F~D¢Ä×Dy—›Dy—šDy—šDy—šDy—šDy—°Dy—áDy˜!Dy ’Dy¡Dy˜nDy—éDy—±Dy–qDy—šDy—šDy—šDy—šDy—šDy—™DyŸDy¨ FQµF}ZÊF€ÛF‚cF‚r2F€<F€ œF å+Dˆ»™Dy¡/Dy—°Dy•¼Dy—šDy—šDy—šDy—šDy—™Dy§CFxHkF~ñB¾ÔëBOFžBH÷kAÍÜÕB…³BZ\IBMŽ‚EÓWFC&F•„D§º«DyštDy—šDy—šDy—šDy—™Dy§LFwH•B×ñŸBHÿ»?¹i¸?€O?€F–?€?‚Üõ?€n ?€K‰BæK·BmOPEÔOˆFD³,D ¶yDy—™Dy—™Dy˜BDy§ýFw”BÓÅ?ƒ¬ ?€J?€?€?€?€?€?€?€?‚ê³?€}þB¤N¶EγÉFE/äDy—¯Dy—šDyŸ¦FwWwBÓ!V?ƒ¡Ø?€?€?€?€?€?€?€?€?€?€?€?‚7ÉB½eEÏfkDy—âDy—žFïfB °?‚ôl?€?€?€?€?€?€?€?€?€?€?€?€?€?„5E¦'Dy˜#DyŸ­F{è#B–Wz?¶?€?€?€?€?€?€?€?€?€?€?€?€?€?4EZ.âDy™QDw´¶B<Ë$?ÚVË?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Dáì´Dy™gF4_B( ?€ÔÜ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DnfðDy™ØF¬@BRŠÀ?J‹?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DakDy£SFÂAÚ„?€3??€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÓ³•Dy£»F¸äAÖÅ¿?€->?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÒÂDyš6F¯B(•t?€âS?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D ¯LDy˜áF£½B* ä?€ÓÜ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DhJDy˜XD‘lÂA¨Ñ¤?€à´?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DÚÚÇDy˜KDyxèEµ“œB-KÖ?í?€?€?€?€?€?€?€?€?€?€?€?€?€?bEU1áDy—ìDy—5FXB0À‘?€ß“?€?€?€?€?€?€?€?€?€?€?€?€?€?‚ 2E£ Dy—´Dy—šD‘Ä—E·ïOB1Ãí? Õ?€?€?€?€?€?€?€?€?€?€?€?€R}B¤EùEÎ~ÖDy˜0Dy˜rDy¬8DŒÙE¹¶NB1†™?€"ú?€?€?€?€?€?€?€?€?€?€B’b E¾é¥E¼e)Dy—›Dy—šDy—šDy—šDy—…Dy—™Dy  Dy dDy ÎF1¶FÎF{6FõF BFD’üDy£ Dy zDy—ñDy•çDy—šDy—šDy—šDy—šDy—™DyŸbF=‘F}6ùF±BÁ ³B!²íBNóBMÆwB"ö°EÒãŠFEœmFÏÆF ÅÿDˆŒgDy•1Dy—šDy—šDy—šDy—šDyŸoFwz?¹¯n?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C@Dw±­A ,Û?€àÁ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BÕtF×B'øû?Êd?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B]d¥F{“B*x4?õf?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B[ºD‘—A¤uÇ?ß?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BÍw° A¤…Š?€èp?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Cˆ êDyd,E´½jAÜT$?€9y?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DD-zDy˜ FcGB-F?€ì£?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ëE¿Dy—ÃD‘,A¬}v?€ëK?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€šòErü‰Dy—·Dyq`E¸àìB3Å¢?¶?€?€?€?€?€?€?€?€?€?€?€?€?€?‚i£E®K Dy˜pDyš:DŒÆ_EºOB1Œº?€q?€?€?€?€?€?€?€?€?€?€?€?€B£“îEÏy1Dy—¸Dy—žDy—šDy—šDy—UDyŸ—Dy 9FýFñZFËF€èhB±KaEÑLFCÒFgÛF E FS D’,ôDy¢ŸDy•cDy—šDy—šDy—šDy—šDyŸFp3F€OBÃ…B'AȈA¿’ò?³(BÂ¥‡Aë>IB„ B1„cEÒÒFG~F ‰xDŠª*Dy—šDy—šDy—šDyŸ“Fö”Bï>AÍâ?“ÞÕ?ˆA$?€?€?€?€Ú?€?Õ‘³?óÀB”²«BŽàtEÐ$"EÜ™íDy—šDy—šDyŸ—FýSB Î?ƒl?€?€?€?€?€?€?€?€?€?€?€lE?û„Bëí$E¼#„Dy—âDy—žFéxB×?‚÷0?€?€?€?€?€?€?€?€?€?€?€?€?€?ƒøkE¦*Dy˜ Dw´ÛB@ú?…(?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ªéExÚÊDy˜FFwF·BDå´?€~‚?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€-¯EÝDw®_B@‹?¾w°?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D’ÇÎDw«¨Aûôr?³+»?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C¤ F«AÔ$}?€qc?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BŒO‹FzÒ%B;QF?€o?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€AF˜íBCn?½L…?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@PÕ^A©ÿê?€õ'?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@KmE³9hAÚ?€:?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A8FÎAÖˆÿ?€-õ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B‚eªD‘_DA¨„Æ?žÐW?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C™J™Dw£ùA§X¢?4¢?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DŒ<ûDyd¹E·£AåY­?€?¸?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ÛEýrDy—D‹ôlA³ ‹?‘?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€²ÝEsãÌDy˜¾Dyœ×D<ëvA¤Ì¸?€8Ä?€?€?€?€?€?€?€?€?€?€?€?€?€?nnE¤PDy—ªDy—œDy—šDy—šDy—%DyŸ@FoÛF tŠF~žBª5–Aüß¿@¿÷B<œÿB±dEÑ55FDŽF lÞFt“D’LDy—˜Dy—šDy—šDy—šDy—šDyŸïFšæBÆAg(¬A¼ô'?½ìS?’‘ë?€?ôk+?šÔ¿B–þAëjÞB^¥EÏçEâgÉDªDy—šDy—šDy—šDyŸúF{èåBƒ/Ï?Š/g?€?€?€?€?€?€?€?€Û×?€?ÔÝyB=VEÎQcF9_*Dy—šDy—šDyŸüF{ú`B–GS?©G?€?€?€?€?€?€?€?€?€?€?€?€eÒB£lEE—Ž8Dy˜"Dy F{æôB–Ob?Šh?€?€?€?€?€?€?€?€?€?€?€?€?€?çEYüDy˜YF/BUìN?TÕ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€OE ´I(?Ë;?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Ak{òBv†?´ã«?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?Ìô@Å4»?€,š?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€…@À"Ò?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A§eÏ?ž¶Å?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?À SA¦Šp?ž‹u?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ATÌÓE´D.AÚ?€8?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BÃÒ„DŒ)¬@Ë þ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CßñzDw¡¯A¬?’? Gè?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D DyeVD;ú›@±M÷?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€LEL·Dy˜ÖDyOEž»YAÎ}?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€EVYDy—Dy—›Dy—šDy—®DyŸFDyŸÔFr€F}ù'B±»—AjÆA+@ÿmê@ÿý¡A¿§BBP…EΟmFCÀF NRD‹ý9DyžDy—šDy—šDy—šDyŸ‡FzF®„B‚Š A¾ôEÞìäFñgD’sDy—šDy—šDy—šDyŸÖF©óBªIAgBu?£û_?€?€?€?€?€?€?€?òåB¿~äBê÷¹EÏœpEâšDy—›Dy—šDyŸßF¨ZB*{?Œë€?€?€?€?€?€?€?€?€?€?€?€Ñ%?Ó½ÆBEÌE¯Dy—°DyŸ F¼÷B( ¡?€Ñ¼?€?€?€?€?€?€?€?€?€?€?€?€?€?€ÈšE$1ÜDy™gF,ÛB(j?€ÒÉ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Dn}øDwº#B>h‚?±šc?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C/¤F­wAÔ®?€˜/?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BŒ®xB>Aù?ÊO?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Ak.€@¾ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@94@·«¾?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?±=N@³’E?€?€?€?€?€?€?€?€?€?€?€O{?€?€?€?€?€?€?€?Â@±yq?€?€?€?€?€?€?€?€?€§?€'P?€Ò3?1?€ #?€?€?€?€?€?€@±t¬?€?€?€?€?€?€?€?€?€?€#y?€èÙ?@3?€Q?€?€?€?€?€?€@³h?€?€?€?€?€?€?€?€?€?€ ?€?€?”l“?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B^.°BCm¨?¸¤î?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@OK#@ÅùÚ?€O?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€E@·°?€?€?€?€?€?€?€?€?€?€?€H?€?€?€?€?€?€?€?€@±yå?€?€?€?€?€?€?€?€?€?€?,Y?€å¨?€i?€?€?€?€?€?€@­O¬?€?€?€?€?€?€?€?€?€'±?5Û?‰–—?†E?€k;?€ 4?€?€?€?€?€@«/F?€?€?€?€?€?€?€?€? ?‡ÞBí CŸ0-?‡j0?€Ö)?€?€?€?€?€@«*á?€?€?€?€?€?€?€?€ Þ?€Ÿå?…î_CD"ÃDOŸD?ˆ'ü?3¢?€?€?€?€?€@­%Y?€?€?€?€?€?€?€?€?€?€Üx?‡z»?Š_:?‚¡?€#¡?€?€?€?€?€@±6‰?€?€?€?€?€?€?€?€?€?€l?€² ?Ï?€8ë?€?€?€?€?€?€@·\…?€?€?€?€?€?€?€?€?€?€?€#?€Î?€?€?€?€?€?€?€@¿¢Ø?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A”Ò*?)?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@=g´D<~@±#ü?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BIt\Dy:ŒD7ôC@ªÄn?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÃADy—–Dy—šDy—šDy DyŒlFûÚB¿E@½˜Ø@þý|@õ¼"@ïŒÖ@ìT/@ì@@ï:@õ@@ÿj @ß´EÐPEÞúDÒÄDy—™Dy—šDyŸÜFæF nB+_)?¼?€?€?€?€?€?€?€?€?€?€BÀT²EÏšEߪGDy—›Dy—ŸF€B5%A`ÒG?‰\Ú?€?€?€?€?€?€?€?€?€?€?€?€ÑáB¥'ŽE~4ÞDy—›Dw©\A¨W?€ëO?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€¬qDí1ŽDy£ÎFÑAÖÍp?€-ž?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÓ! Fk,B*jÂ? }0?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B\žA©¹á?€æ6?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@LÀ–@À!û?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@·¨€?€?€?€?€?€?€?€?€?€?€?€¿?€?€?€?€?€?€?€?€@±už?€?€?€?€?€?€?€?€?€?€7¿?€ß? B?€Né?€?€?€?€?€?€@­KT?€?€?€?€?€?€?€?€?€ ?€¦?†² ?‰M·?‚öß?€g[?€?€?€?€?€@«*[?€?€?€?€?€?€?€?€?€è;?„³C„hD2>n?Œ¹?€Ï‚?€^?€?€?€?€@«%­?€?€?€?€?€?€?€?€?€«þ?†MCÀ5žE jþ?‰Ý1?€íÄ?€?€?€?€?€@­! ?€?€?€?€?€?€?€?€?€G¿?=ï?‰ÖC?ˆBp?€ÿ$?€·?€?€?€?€?€@±0±?€?€?€?€?€?€?€?€?€?€>¼??Þ?€?4?€?€?€?€?€?€@·Uï?€?€?€?€?€?€?€?€?€?€?€{?€³?€?€?€?€?€?€?€@¿£ƒ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@Ê7?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@;í&D8Ò{@¯¾z?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BH$Dy5ˆD7îr@ª¿ž?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÂSýDy—–Dy—šDy—šDy—ØDy‹óF°¬E¹Û™Aê×–Aï.@øsr@òxÆ@ïBŸ@ï-#@ñøÃ@÷÷ÐAÆ EÀÓ]E¸OdFÒ°D’ÕÜDy—™Dy—šDy—×D’JFÕšB+§áAcüÑ?Š.?€?€?€?€?€?€?€?€B¸»A¡aEEÒ™”EßɇDy—›Dy—šD‘øNE´¨LB+D3?q?€?€?€?€?€?€?€?€?€?€?€ÐD?€B’EûE„—]Dy—›DykUE´V­B+ƒ?€ðd?€?€?€?€?€?€?€?€?€?€?€?€?€?€™³E oDyš6FÉÒB(-{?€àÍ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D $D‘žA¤dž?O?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B͈eE³dAÛú?€9×?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A:_A¦Rµ?¡l³?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?§Þ@¹¤?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?Žƒ@³hD?€?€?€?€?€?€?€?€?€?€?€?€;£?€?€?€?€?€?€?·%@¯F€?€?€?€?€?€?€?€?€?€?€Ž?€IÇ?o#?€@•?€?€?€?€?€?€@­%?€?€?€?€?€?€?€?€?€7?U‹?„B?Šêê?€®ÿ?€ú?€?€?€?€?€@­ }?€?€?€?€?€?€?€?€?€=š?;{?‹Q]?†ëg?"L?€þ?€?€?€?€?€@¯9?€?€?€?€?€?€?€?€?€?€?‚úø?€Øg?€•G?€?€?€?€?€?€@³,£?€?€?€?€?€?€?€?€?€?€?€'N?€\õ?€?€?€?€?€?€?Må@¹P?€?€?€?€?€?€?€?€?€?€S?€?€?€?€?€?€?€?€?Œ,@@ÁŒÌ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?¹uûD»@ž¢?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A)¬:DuA©@¾¬Ö?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B¾òDym9D7S]@¬`ï?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DU|Dy—˜Dy—Dy—›Dy—DyÞFѶF S*BìšAqÝ@ý®Ë@ø|@õKu@õDÁ@ø ”@þˆA¼–EБEÛãÇF TÛD’Û€Dy—ðDy˜0Dy—èDy¢PF—kB0°"A_¶ˆ?Š b?€?€?€?€?€?€?€?€BÂúðA‘ÒEÑÐEà³ÆDy—žDy—žDy¢%F‹ B)é?€Ö»?€?€?€?€?€?€?€?€?€?€?€Ùà?€BbƒE EDy—DyŸÏFzÂB'е?€Ð¬?€?€?€?€?€?€?€?€?€?€?€?€?€?€¶E"z­Dy˜áFªjB*¾?€Ò:?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Dh?ÂDw¯ÿA¤OP?€Úõ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Cˆš‹F AÖdý?€,Î?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Bƒ0IA¦Pb?¡Ò?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€AW(¬@½ Í?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@nÉ-@·n¿?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?­ Š@³Xˆ?€?€?€?€?€?€?€?€?€?€?€?€,Š?€?€?€?€?€?€?£r@±6g?€?€?€?€?€?€?€?€?€?€?€®*??\?€* ?€?€?€?€?€?€@±2?€?€?€?€?€?€?€?€?€?€1Å? ?)d?€ ?€?€?€?€?€?€@³."?€?€?€?€?€?€?€?€?€?€?€—À?€?€:–?€?€?€?€?€?I¯@·0Q?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?¨O(@½Nõ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@_rñ@Æ6þ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€AFÛgD6 p@¬ñ¥?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BtáDwMÚ@þ‘?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C€ÿxDynßD8ŽÎ@¯Ø·?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D[±BDy——Dy—›Dy—šDy—Dyž²D’9SFç EºòB) Ae Aåâ@ÿÊ>@ÿ¯A˜›A»$E¿fME·Ù(F FäD®dADz;nDy—ºDy—ÒDy˜EDy F3wE»× B=˜AcÕF?ŠúG?€?€?€?€?€?€B¶{ØA¡gžEÂ%Eº.=F l]Dy—œDy—›DyŸðF‰–B0•7AfÛ"?Š?€?€?€?€?€?€?€?€?€Îç?€Bƒ–ÈAÄ„E¤wÖDy—ƒDy—ÜFœµB.ÇZ?€Ù?€?€?€?€?€?€?€?€?€?€?€?€?€XK?€x°EX`SDy˜XD‘qÓA¨­°?€Ô_?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DÛ.DydE´£ÃAÝl®?€:½?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DDuáD‘gA§•µ?¡’?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C™¸&E´QøAÛX£?€9õ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BÄ…A§>Ú?¡nè?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A³6½@½‡?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@q D@¹sC?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?ŽFÉ@·[$?€?€?€?€?€?€?€?€?€?€?€?€Ö?€?€?€?€?€?€?€@·V6?€?€?€?€?€?€?€?€?€?€?€?€6\?€?€?€?€?€?€?€@¹O?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?ŒE@½Nr?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@`¹%@Ãwl?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A¥»³D{@½?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B·túDuÞ'@¼ë8?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C‘©HDy?ïD§!@Ÿ…"?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D<$Dy—BDu…®@¤¹?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DÓ>¶Dy—šDy—šDy—šDy—™Dy—PDy¡AFçÕF L!E»ˆ?AÞÌÂ@ĵS@¾×#@Â(@Çð>E¾¡ÄE¸d®FøFF ×0D“‚FDy—KDy—›Dy—›Dy—ÔDy˜-D’:'F”nB2c³AfY=Aa:û?œÄE?€?€?€?€B¶>ÌA£ aAežnEѦVEßWÃDª¬ïDy—šDy—šDy—öD‘ïåEµ–0B/”Þ?K?€?€?€?€?€?€?€?€Îö?€?€B§E¿óEµìrDy—™Dy—›D‘î›Eµ»B-qý?€ø?€?€?€?€?€?€?€?€?€?€?€?€c“B“h×E•07Dy˜LDyx_EµuJB-ƒS? ?€?€?€?€?€?€?€?€?€?€?€?€?€?€ÿEUwÈDy˜ FdB,•e?€êò?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€E>ÃDw£ûA§7?5„?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DŒ(%DŒ1E@Ë$ä?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Cß çE´ÏvAÚ"»?€7ý?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BÆš‹A©4Y?¡w~?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€AYwu@ÁÈ{?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?ÂB@¿¡?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@¿£?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@Á™”?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?¹Ñï@Æ4ù?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€AH²•D d@/?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B¹ VDv)ò@»6ß?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÓÍDwT@Åàœ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D‡ëDydîD7¤N@¯aç?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€E¸»Dy˜´Dy&hD1Ÿ@ ºµ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€EP°ÉDy—šDy—šDy—šDy—šDy—NDyŸXD’$$FÌF H—E¼HýEµjiBÀE¾ÛÀEœ0“EÜ’yF U^F שD®}VDy÷‰Dy•8Dy—šDy—šDy—³Dy—ÃDy¢qF#°E¾iB•ïAiwAhSUAa03?œŠ9B¶­GA‹ûðAŠpAj*EÀ–E» ÒF ÔD“%ØDy—šDy—šDy—ªDy¢pFZèB4¥HAn¿?Š‹*?€?€?€?€?€Ð°?€?€?€BfäA¡äÁEѦEáŠDy—šDy—šDy¢­F„¦B0Á®?€à›?€?€?€?€?€?€?€?€?€?€?€W`?€B‘yE¼=Dy—ìDy—+F]SB0È?€ÝÌ?€?€?€?€?€?€?€?€?€?€?€?€?€?Ò®E£#¢Dy—ÂD‘2SA¬_Ÿ?€Ýþ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€±Er¿±Dyd«E·ŽAæx›?€@³?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€E½0Dw¡µA«â?£ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D&XD‹ßŒ@̓¼?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C›ojD;%@¬°7?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B‚טE™÷üAÇÆà?€4w?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A4‘EA”†6?Ÿ­B?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@;é“@Êr?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€@9ID­"@ž#ñ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A)¾ D6Á}@¬ˆý?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BuÀADuó@½G?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C’(&DwŽ@ÅäÂ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D‡¼lDy:5Dôx@¤¯?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ fE+˜Dy–CDuæ!@Æœh?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€yfEm6~Dy˜¡DywvD8ôÞ@´ß$?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€5mE À²Dy—šDy—šDy—šDy—šDy—‚Dy—·Dy¢CD’ D“4òF£¹F 2E¿WEß|ˆF RDF fD®TrD“}œDyþYDy—ÎDy•ÅDy—šDy—šDy—šDy—šDy—©D’ÚF mE¿ò9E·4àB$˜kAl%?AoøAŽØ An9ªEÀ•ŒEž¦Eß•vF ÛûD­Þ¹Dyí÷Dy—šDy—šDy—šDy—ªD‘Ö‰E¹ÛB8è/AqbDAd_ý?œ J?€?€?€?€B†IA‰uAFrEÀƒýE¼òçD¦Ô“Dy—šDy—šDy—ºD‘ÞÛE·ûŽB4Â?Ÿ?€?€?€?€?€?€?€?€WZ?€?€B~B©E½ß¹E¹çDy—´Dy—™D‘ÈÓE·ßB2ö? ã?€?€?€?€?€?€?€?€?€?€?€?€SêB£›OEÎxÚDy—·DyqYE¸Ê0B4 ??€?€?€?€?€?€?€?€?€?€?€?€?€?‚`wE®1€Dy—D‹ñQA±ð¬?€ÿ7?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€¹´Es³×DyedD<’@±M?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ×EE«DymÁE›ø1AË@ ?€5+?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DEÂeDwcçA’X?ŸÏè?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C‡JD‰ü/@ÎT&?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BÅ„ÁD<ù@°çX?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BIúŠD8ò¬@¯€œ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BH5¹DuZ@¾È?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€B½ñ DwN<@ý}?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CâDy?ÐD”|@Ÿyå?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D<œDyeD7ÅJ@¯$F?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€EÒÚDy–VDuú—@Æ¥w?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€REmNDy—™DyHÆD\ä@£þë?€?€?€?€?€?€?€?€?€?€?€?€?€?€?YpE«(wDy˜`Dyš§Dw»BDÂõ@¢=ô?€?€?€?€?€?€?€?€?€?€?€?€?€?:EÏùhDy—‹Dy—˜Dy—¶Dy—äDy˜Dy˜VDy˜·Dy£¥Dy£kD‘ñXD’ÝsDøãD¶"D’Æ×D’Õ.DzHƒDy›~Dy˜ÐDy˜mDy–íDy—œDy—²Dy—ÝDy˜Dy˜rDy¾#D’8]DŽC^FfEÀÎEºæyE»´E»âEºÜ1Eá`F ÆDªÀÁD“+†DyþDy–ÉDy—¼Dy—ÝDy˜Dy˜bDy¼ËD}E¼EºçB'gAqbmAlXhAiÒAirTAkñaAŒìnEÁïWE ­ÇEáMúD¦uwDy°—Dy—øDy˜²Dy˜gDy›DŒô2EºZDB5ÿÈAl6l?‰t¤?€?€?€?€?€?€B­€AŒèÜEÁUòE½4RD¢Â>Dy˜9Dy˜¯Dy¬:DŒØÒE¹‘ÿB1Ñ?€"¶?€?€?€?€?€?€?€?€?€?€B’ž`E¾–@E¼U¥Dy˜vDyšCDŒ¿ðEºH9B1Ýö?€?€?€?€?€?€?€?€?€?€?€?€?€B£EÏdŒDy˜ÃDyœêD=!A£Ž‡?€&³?€?€?€?€?€?€?€?€?€?€?€?€?€?GØE£õADy˜ÚDyO+EžÌ¡AÏá ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€EVb4Dy˜žDŠQA—Z?Ÿ²?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DÚ1Dyq(D<†@±2?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Db Dy–úD7g1@¬,x?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DØcDy:nD7÷t@ªv†?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€CÃ^©Dy5ŸD7ñ`@ªrv?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€C˜Dym‘D7f²@¬u?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DD6DynùD8®@¯—*?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€D\hDy—VDuž…@±ä?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€DÓešDy˜¸Dy&WD@ Õ4?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€EPÁDy˜£DywÆD9Å@´Î?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€MÍE ª Dy˜aDyšÃDwÏ•Dºâ@¢;å?€?€?€?€?€?€?€?€?€?€?€?€?€?EÏäïDy˜£Dy™éDyšFDwô’DÝá@¡þ)?€?€?€?€?€?€?€?€?€?€?€?€E±QcEAÔ@f#ANï @[º¡AE5Ç>±eá?&©µ@Å(ý?ÛJ»@ÝãgAPv@8›l@d†±@ÎÌ»?æ|AìŠÈA ÒA¨A'·?"eÅ@À ¥@ÍÁtASÏ@É`ÍAŠêÖA•‹Ò?é—°Aoéº@Ì^]AÏ·ÌAÈfA®-B4@‡é<A bATïÀ@ü;§@4)ë@ÿY@:äÔA*ÔA–H„@øä#@üû@”§@(…‰@¤Ï¯B í@ÝÜ>.1@Am’AåŒAK@xô’?¾ò;P´AÁÕšAåöS?æïçAŒx…AÑùÝ@Á û<áHÚA½_È@ <þAH˜BìøA>úæAåh‡Aû}?!w¯?½H@ 2¬AûÜ!A{A~Å1>7¨5AuÏAöÜ@r…ð@#Ô˜@a™@ (nAÙÿÁ@?•¿= Ã@´ ÌBàA„Ó?£õZ@ܽYAÞš@FŽâ@€.BËA¸i1@$~@›²A O>—±+>ô·çAš@æ@nøN@Ø•@· >4<#?.-ç@œây@ýjAÑe@¢2ú?´Ò >; «@Ï)A @=òI>ª UA(TâA‚ákA5úA}Ì@À¥>ƒ)A`Cv?&Ì? ×?‘ªßA}\¢@Œæ·>¾,^@L AAïA¶31A(bJAZdÙA„ßé@“ÊL@lçþ? [°@Ù¯eB…'?€ É@ lyAš7A©dAM«@’jÐAuoÕÿ@U Å@è.˜B ôAOzs@1¤OA!ßAZæ%@œZ¦>HŠ•>(±vAž"’A9Î@vO@Fj>ðÔBAfåB(N>ýÀ@í?nAþsA¿@®‹@ígAŒžÈAKJ…B;ȱ>[#p?‰¾B îx@½>F@%-@‡%„@DÊìB($@ÇÿP?úÀ¸@S‘û?‰Zã=&†A)LAVÁ÷AaúKA8Ž'AÞ9bAªo­A¡×@ò‹;m¸B&Ü2>¸«F?â2?[-ADš‚>ãÌðA,ZAZ6{BHØrA¬Üv@¾WEA‡kB5AFv«AÝk@À¤A\C¥AJóØA|‰ÎAÂ9@Ô3j@µ½@ì3AÔ?ú;%?ç´‘@º_"Akà @ !=@€3÷BEµ,A ûZB>¾@Ri(Aø?u¶h@9‚@´?0hfBgÖ@’C$?;`€@5Ö?årßA‚ñËAؤŸ@Ãæ{@}éA®š@GÙA .ÁB 0YA¨œ@·Ô?Ž/»>™ý;?^@<´X>ÏÜ?´ž @T©>¯ŽZ>ŸÂ@§Õ?+ ªA’êB#Y>°$—?A!ò@ŠAØA(ù>æÉ1@rÌ A-’AÅMÎ?†½˜A:ÔA“*nAŸø{Aþ§â@bw{@ßå=Aâ,A§WAëA´”g@ÖiAƒªAŽø;AÈe‚AV³@”Ý+Bˆ©B rð@Ø_ðAâFAñlƒ@WVJA%{g@TpAŠþB A+L¦AU¢@؉Õ@²z“B ÄAÌùY@š) ACÃ@GÖB1€ŸAÚÈiA¼ó™AiBõA–Z$A9£Ö@¥<]BdÆ@Ðm=@],AÜFG@Õ@@íº„@Œì A¼B„BÛAÑÞc@šî|AÃKA•šØ@‘:Ø?GðßAV!®A~~Ã?Ù‰"AX§*A€ áBñ«A†6@KÅP@¦ÆB;ä@ªlâ?¹•AƒF¬@LþçA8AÒb@I®è@\IZA³—B.C@ÝßÎ>þï5AÙþ˜A†ƒ9@uÂxA9iòB›A±§@š¢S@_ŒðA(!GAh[@ ƒIA¹sAÇéË? áA RB.*úAìCA2ZÁAýFA²\‹ATÃ?Ûí²Aq IBïÑ@Y ¦=û~Ê@Ö{@#oBÐuA–„ÂB0Ë’AªyÒ@º™-A6ÍwBV5»@®¹™A´AAO@Ò®ÜBW?³ýA@é@&;Aø¬B,v@ág(A¸KDAÁQ$A#¡öA/hû?îÈóAä–«A§ö @™k@wNÏABÀÄ@þG@ÇUÄ@ýâêAZcAY¯R=D¬¥@DÕ.B5Ŭ@IWÇ@vx?ÒúÅATåFAr4*?Gßã?„Ì{A†OAœË7@÷B‘Av¤A fA &>Ö¹=AŽr"AÆØË@Ú=A¯ËAó#AõU‘Aô$Á@&cw@Ò{¬@~æA…^»?žƒH@æ& ?Ôó@»àeAñ™ApýÓ@ÐT’?¨÷mAÑŠÇ@âï›@‰Î AµJ@†SÆAØ=šAé› @îžûA”ë ?ö µA€É¥N@»6@m‚>Ä3ýAk@ô Aäõ@€Ž¤?1ïc@1ZÆ>Ž@œ€ö>EI?D¤<@cÖŠ>6.IAk3.AE! @¼f¢BkV'?ß Ð?„í}@ sè?–‡@$A ñ(>Bø@B-+iAB@ËjAd…TA‘ï4>i×Ü> \Ã@ªT@:ÖA[xÖB€'@ã0hA¢>Q¼9A½5A Àÿ@«YçAh @޼¼AÿJA‹$A–Õ?= '@"| @Ù[W@¬êBw\BE!@}+>ºˆŸA“æAÁ±A5b@’ùA:7jB7óâAd…®AÌ”>¼{µA åÆAשu@^ÂAr~B+úÚ@5Ûm<³8@N“AÀ»çAaÛ°=œ€Ž@ÂfVAw6`A–¦Ž@/¸•@!µ”@ÊëAþC A­¼?Þó„A¯Â©AÝAr­_A@“"A…WAI Aš…³;Ú%>­b>ãÿXAïŽ8B¶A±³AÎMRA ² AŒÞBŸA¦†A‰ÙAÎÅ4@†@ |P@@8?44¦@œÝqAn)1=¾„®A¢³AÑv°A8Ì@„*B 0AâA½.ÚAÑ[µ?ð~A“§P@JB@¯_ÕB8 $@Ÿ…«@y–—@BQA…ãÑA²\â@l3>dbµB B£A8pAqfÏB ´¶AêªAMSArŒ¡A ÆA3z@‰,Õ?€œ§AÍ×?ÿ­>@ŠÀIB÷`AÜÝAA#>þAq.¶@‹ÕºA0å‰AÇžÕ@ÿ A3V¨A»àìAfõALb@'î3A»7gA‰F?Û'Aª°…A³…ßAw A®[LBF¬A=FA»Q3BF÷@¼,Aß‚¤A²¦@–2AÓT@Œ¦>‰Z®AÇów@œ°ÖA˜©¿AªñäAù½v;I$QA„+AùvAàïÆ@òsÓA™ù@ôßåA]«@¨¿A‚óFB&j:@úS?¬ãçAx Õ?ùp@&P>ĈAõ ¹B #]AcOA²Ù @†ÖAšúA¤Öü=©µm=áƒ1@¨)qAÚgBž,Ašg?–ܱA@ï¯Aæ¨@UŸXAHÞÑAÊÎXAžwÉA ¹@7gI>Ãat@”{‹@ƒ½þA«‡QA-F'A«¹õA³zø?4F¡?‰Ä@§¸A¹û´Aáç£@­~Í@ä'»@«ìB}e@Û+‹AjDU@ÂhôAÀÏÐAõ[A( Õ?ï’:> ÈAËLËAu÷§@Ÿuo?‹™”Aþµ@~º¢AC¾@@…rAÕ˜b>› >>ÀºL@K!B,çwAÇ›?’½@§@Sî@ÁßvBnn@¶W‡?õmr@Ý’AñUwAÓ–AV!´A_Gâ>µAÇh? A¿9NAô<¦A5Òc@(.@ÌàóAƒ¡A! m@9Êÿ@ÏÚ[@î™@}#@ÉìA¶Ý„Aj!?j!ž@ YÍ@®¥B ucA€Ua@SFqA²’Aм¤AFØUAí[B'hA˜¹A¡!GBB«PAPÕ@§B, õAVÄ@Ÿ£Ä@?ÄÿB72!@†úpAšºê@û…pAöþáA÷]vA©ÝÃ?“,%@X( AŠ µAª™<ƒ ÃA¤oõ@gSRAÎæB-¨AbU?ë=BÍ+A¬D`?Ö±@´ÄAÃ'?¡´n@Aîï?çE@äó0AœNˆAu­´?Ÿ ÄAÁ&Ÿ@¤BûA»£¶BVŸAp}A§§B)«ÉAÓ¹@ô$üA’HA²¶‡?⺄@€ˆ¹>­)QBF­@† A ,÷BåÁAlX AÍMfB%<Ç@Gà,Aå£K@ §(A¦ñ|B…xASj,A° øAíA8ôâA†µ¢@¤Æ|A§/8A¤ÈBzfB&#ëB(|UAx@¬,$AÍóqBLAï˜AºBh@‡&AÓi-AïÊ×>Õ’R?2ˆaA®™gA݈oBï@ıPAßëB ïAëVTAÍ—úBWAåÁ´B1Æ'?L :AÃû@A³'‰Ahs²BM1–@ÔÄ—>í›A§*H@ú7B/­)?¢N‘?ŽlRB:­à@û§¹@ŽXAYoA¯ªêAáï§?;aMB7õ…Bi‘AóÓpBlæA»ïLAÙO*A¨ý2@›¡A9ßOAƽAIT•BNïBA=,B¨úA7$‡?èФAܳéAyaAá~³B&%,A«@òA^`BDÃÎA`r@ÔºŒBÆDBU¨AÚ¾ýB»·A[­Aÿâ@^32A2$OAùÃA|ò{A®y?yÏŸB|AŠBA#H?ŽÛ¼@ÇYA÷KäAÚgP@aëAˆÇ+B2-AžÒ‚AóŽÎA…õª@³ A¢¾3@"¦7@Éa¦@(SzA•'¬AÇ@'?E-I@ˆt>íÃZA—R7B*-JA¤ä?ÕhB4BCRAÖúB‹‘~A§nšAŸŒ±@Y¶õ?Ø ó@|d>éòœ@ˆl B÷2@.DÖ@€aŸ@Ë`¶AÒóôA¶"?¤ë®?¹˜ùAÁºACA‰,ªA—4'Ab)A±ëz?““œAªGjB2bÔ@`ŠïA•Œ½Aö6B¢@É`ì@úõ#APšAEAp‡7A‘•¼@†W}B>»}¤A=€A¬ê@È„è@ýZ™A‹.ãA­ò@<ª"A3òsAïC—Am‚>AhžA®—c@Fy@ËP‚>ønAW~?wa<@8£0B zY?‘×ÙEóADÈ>zA£@Ô59@¾™A-9ŠBù)@°&@ ºAáã‹BikïA5Þ8@^†’B)¼ËACÓd@,>@SvAb÷Aƒ B@7W A‰ùBEé?=,AÁußA«”—AÒº;A1b£AkΟB5KA†…˜@µ¤_Be„@°}nA“bÜ@ìR¶A‹õBr®@Û”àA¤WHAçWîA„KiAE2ÚA(”ÀA?ÚB*³ÚAÅŠ@5n?h‚ÑAeA  šB]ŒÊ@^¨½@\‚AôàþAI޽@„¡ðAq6A†¥A“Ö>&çñAÆAÔ”?ƒíŸ>l~Q@¢ÊAëA´•A×@Ê$p@aGpBËvA-îAÿ‚CBTyYA‚ÚAþZUAÜ8>Aµ¬ØA‘øFA9AÅH(@e¶÷A¯¼B®A̓?ŠÃ@AÛgóAënA êèAȘ8AkÖ2B7­ã@ºlýA¿v¤A×ÅtAú9íAfBXc@ÿ³ÓAGØ\@—ù{AD§Aª8@ÊGGAe;ÏB%ÙAO¢`AJjwB 5Ý>Æ B£GAÊ×Bø§B;ÍQ?ªpËA+sêAõ1Â@ÑçýA¡BËA5K!?„Íö@*éA@”ÌA”3QA<7k?¾Z A«–âB27›A‹x;AÖéâA–l+AHë"AɵƒAú§½@³¬WAÇÆB$†›@]Í@@þOµAb®KA—‰9@ƒñÉ@^ŽQ@Üðö?CÒ4A:ÍA‡Ý¾B@ÿºAÙ«óB’FB`üŽBD7°B;¢´Aç{+ArÖAƒzdA£²A‹,b@i@ÍAbg@ èMA8Ã0Aåý,@°I’AJ§éAײ¯AÕ«¾Aã#AŸ_âB“A€y)AëQQ@)cAõºAϰÕA®Ú@ß%-A©_š@™;A©:A šrA–’A¿÷}AŽ£NAô§ÈA,rÿB ¶;A³QÙB‚»A‰8ìBMµ¹AáZ±A‰¦a@ÛpÏ@XÖ$?V áB ¡¦@ËvÎ@ösæBY%@øƒA—‰fAüƒ¶A¯¡¯Aê.UAò¯¬B:VB ?QôÖB>ÃfAŽ0½@ºA áEAÝ@A­mmA¼€@ÐùA‘ÀBÜ=Eà]=ȇÄBµçA©&A§n7Aß6ˆ@ê»Aô½A;A¬* @šA@¢F]A@cAMÄñAY5@МX>œ¡€AϱôBù»A"?A¯ò„AWA"s A¥ÓB?($@1|AT¼¤A&f@U•KA¹FAȳ@©þAåž½AzaîA™…ò?_ÔoBE ÖA´"øA¤ý4BÿôA GåB*TA(±o@¦«tAù'"@óÿÕA'ìAõbSAxb?éWuA›LA ¯ÉA¤Ü @¾…ý@Ô.”@8^AדA¡ÁjAÁð°?\Z>0ôôA¤•µ?½E=AjqAeíäA¦ëã@®^JA×ÏB`?mA llA>Ñ'@ôÐ>: ½A™zéA çAqÑu?ï+KAi?ÙA$•@ºÄ@ç§ÐA\b@zž>AÑ}i@“²¤B,@¤@KA?ËAòóÑAÉ©Õ@¢›A{³ÂAÇJV@Ñ|ó?u@?ÏÖ?M¸¿Aœ'B=Ò@û~ÚA¶ÅíB?XB0ö1A„Œ¿AR»ÙBÙ¿AÙ¾,Aõ¡B=?ýiÏAÞ€çA‹ÜU@~×B$pL?áÑŽA™ÉžAWAÆŽBcÚÐA…¸ AžÁBÜ÷@SKé@ŠwA¹×B#@°Îg?ÑEõA®&ACtBžB!Ç´B=FiBcHB=e>’’|B „GA£µ@”âB°µAt­=@‚B"{A††õB.ŽÓB(¡VAŽÜ–AÆ?àWÛA—Ÿ.B2âA:«Aëã©?ãŸ)AuŠBA«…ó?¸0Aâl*AuŒMAøêœBáªB|ÄB@A(zAŠ¥=@ábB ÒôAÅÓÖA ï@ÌU³B ÃÞ?Ç÷)A8é7A€CïAòèBœA‡—qA!„BMwÊAåsAmÅÛBIjBlŽRA¼›ðAüÿÙB.SRA–´‘ADa6Aœà®?[á‡@î)J>ÜùÇAȇA³bA¡ì}AË‘!B%ò„AØùAÜȵA†MRA›Y:Bî­BŠ€=AšŠAÆ5ˆAÓ¹òB6£ã@T±AA\ÒAý½Aʽ·@(ì“?³ÏÓAˆ&¯BjšAî­B sÅAÔxZAðýBÂÛB.ÛŽA§ºfBnú•BÿB* AA¶ EBµBùD@§IA±n¥B^mA(kÍA5wŽAMsXAô Bï^Aói BcçBÕµBÝÝAºqœAý A°ÊÝB¤‡BVAê"ñAbW—A¤˜BFÌ5@À^âBD(ÓA±‚TA9‚AAÜšA«c @–3àAó¦”A×&AA˜ý0BЭAÂó B&æ„?!¶@· @Œ¹>Ò«>A_.6A $iA ñBçÒAk°™Aôü¸AÜP_Bk€Açq½Aý0/BØ@çÅ+B lB3Å›?#,æA“ªx@É1áAžóä@Ù¾A¸PÿA„†Aã µA[—<@JAßÀB›$B õCAhv…B—-z?P€A„ªê@åŽA*a@g¿àA‡¸wB!r7A‚£JAs¦®A*ä¸B-éÉAŽÍ BfŠÏ@éNßA‡ÜºB u?PlB*Q?…—£=Îõm> ,ªAî>Ä?­Ö?7jï@•×óAwônA¡qÿAqÇA稌A4?ëB<á @a¢AvABY*AÍÊG@®¡ÜAÕ`üA¤îA"A—±@ïÂB6Í@‹ÿAcŸ~A¶B—@_‹BÝÚA`óâ>Ò™}@&RIBÈÍA¦ãGAúŒ?md¦A#G9@{?˜ë?¨.ê@"<9AB…wAX¢g@‰„Ñ@ï#Aº%Ž?²ð²A©® @NP@׉~A@Õú@Ô$Ö?®CAØlNA9ôA‰ë¼@•BêAŠÏê@Û¦œA}®×AåŸÿB§ìAØ‚@Hr2= ±6B+Ô€@>:Ð@¡)Bxp@äîA‰m{AB @½˜'@½z AšÙOBÈuA+çAEíg?²Té?-Øþ?ž”aBMîîA°½{?!žÛ@ÿ³¨BŽ d@¬ç@ŠRB}[AT\#?\rA?ðAz\AA¥[?ë£ðB—%Aœ¾²Aƒþb>˜JË@}ÔAÚ@A„]ÛA,Ð(BU›Ã@íÇì@Ã̵B 8ÀA`kAâ W@†&À?‰êjBsúA&fA>W=€"ŠA™¶ý@Èí>¢|@S¸KA—à*AލAä V>ÅÿAˆC¿@¹:ýBS—\A #ÎA© AÈê2A¥²A]kAgÝá@’ò?а·A³Æ\A°™pA¨cTAAAó=½s©Aj@@0ÍÛA`QA7@„,A§¸a@ößÖ?…X¸AÌŸU@ße‚=P…@s=.BãŠA-Q<“ʳA †ê@³ð>K·×<àØ÷A@¥AOÅÖ@Æ!mAßòAÆüœA÷?,ŠAž‚”?–bíAÑa\?ý‚Û?®OAêâADÆ@¹¬‹@=ÞÄ@¢n@×y„@c“JA ‹AÍ6Bág@„äA»õB:†êA²º4?ÝUí@#ÙB GAëAB.ÓAyï÷@úBÕ?!Õí<›»%@umA§½{A!…†A)A#A1@Ò@yÔ·B#u¡AkûAG"óB¼y@Í¡>ô—Z@4µBcöïA¨·³B #A*p˜@àæA3³5Agª@üÝèAŽiy@èA"ª@kóÜ@zuHAá¨]@g„û@åÇ Au 4AÏù@ç{S?éËAñA¯*ÂAü…@Ñ®Î@ŒŠè@áðA‚ ¬@¾ÁÇ@·Ì@HÿAo+ù@xóA'€AlFJB7-?Ù«)ArŒ¼B1TAb°A>?nB#pÒA1Ïr@¬W`AÔòAVOA%Â…@§ìŸ@Sª®@ ¹Î?åë@ÕT@è°A°y²AŸèB AòmŸ?77jB.´WA¦Î‘?–èÕ@Ÿ¯=?H¢?ñ˜?NÖ¬AÊ ›Bxd‹?•ÊRA¼É@‚¾:AÍô@ŒxA°µ,B Ÿ«@Ûý›@Ó ?%åk@uÇ¥AÏ4?½GÆAú‚ A•¨y@hö—@€ ÜBçPA2ð¯A¹AZ—1@´¨@i‚þAaÑÎ@}ª÷@Oqá=gªÛ:]+A©¾ÀAÖ©AëÿYAÑžRAV›j@:ÓB E‡AøbB6YÖBvA¾£wAÒ’ØA-Ã"BIï@í5Ü@›D‡B ³ÏA%3AhÚªBR ¬?Ï‹†?\iá@‘ÌAþxnA’ƒÛAoAëíAœ·àB1‹×A[Aã»*Ayð@5¿A/RA½ŽcAYz¥A=¸ A›I`A@¿A*2~Bø`AÁ'dAjÕB/†?ö·¹BY…B@%<–GA/JÔBɼAŒw?A–ŒŠAHÆŠAºÝÂ?BWxB&(Ü@¾ÝA™s?-çÇ<–ò? óNAsu@Ë4@÷­Ù?–l+@ôš2A‡õ—A~™AÜ(A<žU>û 2@Š}«Axœ\B3iAy2@ÆÖB:]AÆÝG@º¾ýAj+¾AÿqËAGªû@–³)@Vª@¾ÿ¾A5’àA‚Aj’?8ʪ@9•Bå~A›Û@â˜A½‚CBdŠ@6¢®??1¶A˜ßÇ?ÒãÒ@€qAx¿ÁAÀ•K??UÒAå×@§¡rAäF5A¨Ï®AË…BPÄÆ@º¼@•;úA™¯ªAƒõ9A¥žË@\ÿ?›é@¶ìDAÌ‘•A„d?.?9A†óAå¨cA‡æA¡B£AMÆA"ØAé˜ïAøÄÖAô¯0A<†?°÷A¢/?$@š—Ý@Yf.B0üEAâWB_íûBhgB i?çé BP–CA)ZAФ"AJìLAœÖAÎ…‰AðA°ÙA "D@Q.W?Š„êB{­A?»éAi¯A <A°ã³@P)AÒÃe@Ûýµ@ïI@š$(@žr@4lAü£A±ÔÀ@oõÒBY'A ð(@Ú€„?do­Aô^¡@Á»A ù>wPN@aO?Ìö/AITA3”dAЃ?½»B ~ŸA6ýj>âÉB8Õ @OÆm@NåÆB.ÎA=zB2FQA/??B'A žAì¢Ø@Ìç–AŠ·Bf‘#AS¢³A»èÌAy•>:ÌÎ@³ÿôBEØãAŠ ³A^¾A†ÊÞ@°—Au@#B#AGLA­p·Aø>TSF>žTÛB =>Ah7›A¹ñÔ@ËÔAöøAͶËAßöûAÑmAÈŸ’AF½c?]•B “OB>5>òJAÐO BOG"AŠ{ë@øáB+7qA¯ÔAxw0?¬½À>å@>ÀV«A0È”@—ŽlAĘö@Âô–AŠø‹A€·¼@þƒ<ÓAeÜ"B&µ#?ÕUåAþešA·™MAîé:A¸²ØA¶!@¾ž€AF½XA©‘PBcAƒñw=Í´·AÊM@/C@î'JBs> 2¼B+o£Aq/A±;ÖA®sj@…„c>äÃeB*eAW R@¥A­Ö¥@Ä’vB B°?s¼K?¢ôî@ò\A‹ ÍA¡Já?Ky>ˆÓšA—„BË$A¯Ç?£-@fŒA;âB?–Ø@ƒú¯A7»UAÓí,A òAÁ?ÝNS@Ht@MòÚ@–¤FA¥žçB 7i@^.¥?/BAàü]BƒA‹(PAš_¨A¡¦@ˆ‹±AŽ^?HŒÐ@^ßAÞcˆ@Yõs?Ô¢>±ßìAܤ†@§Ím>ÑðƒBe¾A‡äg@‘ŒÉA†q¢@œÙ@SßàA¤AeAË7½?Šp°@Hú¬@º´d@?ÞâA¯g;@M³A`–AviÊA½_ÍAÚ±~AL¡fAÝ·J?ð?AB>›OAâ¡×@»“<>bÉWAV´4AÐ-ïA’D¯A»PóA?nÄB¹ÆAÑXBfAã8uAߎA»}B5w@×3uA®8_AÞû@bòA ‰—Aë A® /A·ÀüAŽ\B ¾ÙAÑ_ÈA§oÐAžAñ-ºAÿy9AŠ}B’•Bf}â@_´A‡ÀAi“àARûAA¦à€@‰›'@të³A?vAË}Ñ@sÄP@°Z“Añ¾AšÔˆ> ¹g@Æ–?”3Ö?¥AC¶yBˈ?Ü Z@`úA”°SA²Y@/O(>Ó8B,A® #Aƒ:@¤ÚàAbFBÞtA®fv@·vgB,E/B¯½AÛÙ}Bm3A6 BôA”Êf@?NAfâŽAóü°BD@íAÂМAŸ-r@³‰bAu*RBvËþ@¤£ºBA´QbB @—!Ï?&¶1?[ÜeAá)Bg‡dÔ‚Bl:A×I A cé@”ŒÅACçCA˜0ÄAšÍ7A‡ûÿ>¸S2B0s¿A³¢BFÕBqEÃAQñ¾A›*ÀB´ANømAåËA‚NVAYM÷@“?eA“FÁ@¨…¿AF@!8ÒAÕ5]AEEB>¡AžíÂB1­O@´©BÈ|??.zB;»ÿA93ÛA<ÿ_B5XA/Š@1gFA1p#=s_AµÔ@†›'=Š]AwæAB±A>’ÉA³†0AÎ÷õ@ÄsA}X#A{ÕAp<ÃA‘wš=É1>»EgA{£A/sH?ÉgÐAG@˼@©s›A?“ååB/ †AÃùÚA޳‡@šÜáAhFüB8ÛA<ØG@ Âh?9[`@t©-AýUTA%|IA>Q@HðAá’AýFD@$?‚t@Aº5ÂA·˜­A`àAÅíA¿AŒË”A[ O@ÔÌRAÁa(@ÙùÝ?édèA±Œ?@Êü°AÞ»¡A“k«@)ö”A7(JA(F@dˆüA¤¨A{ÓjA¼Y¶AËdxAÞUoAŠy>@ÑÊeAöLž>\:@'ˆAŒ+CAÿª:Aœ'q?šhAGfEBE§AËžßA»ÕàBDÚÜBZ‘A‹æAîËøAˆ±5A‡»@ɼüA3m,A²¬WAжAgA—£Ù@¹¥lB £ÚB-‡A—ø|AÕØ@A¼³JBŒsñBlÙ@ŸeEA'<âB' ]@Õ‚¯Aóº=¿c@Ã3¤A„1"A§!@à–ÐAâîºAЦÃ@üÖ«B:Ç—Aôõ;?WB_ûGB çAž¿éB‰B ÞB,ÙA¸­~@ BŸ‰>°n³@°AAcÏ_AÛÈy?ûlAyªA÷~¿A¯¢@å í>:ç@fF€?m(7?b©Ð@ÑŸô@–)@ M€?›ù—A÷Ê?S¼ÉA ºA¼U5BøüA¥ƒmAÿ…BóŒ?4–°ACÄBYÑAq+–?šC@ˆÅBz°A ¬I@“DA÷ÿ9@Òë@B–D¾@,ÐB ´`B(ô&BN_@PúÎA“ª½Aá—yA²=èB36™BIn‘=×§š?á6ŽB3tBa @¥¯¸A£¶BAWNAÌF’AT)ÎAïÁ¡A¶2%BOBJAuM§A;;áBvÞJBY\A…@Aˆ›Akç@Ö5A½dãB¸$B†ò;?þ’Q@3&¾B™jBrÏAÝ9sAÁ>@ø£<ɹ?+‹2B å A«AèͽAú³BÊ·@§öBPÌ@·Ýº@š¨9@|^BÓAd^A±ªLA<òXAv@:óü@Gû@&ÿúA³¤?¢@Ù‰aB:L:@màñA¤]Î@âm‘B4¤A`§B²B'í¯@,­Aé#áBA–ø1BVMsB:mpBQ†A›KBsÐAàIÃA1ÜABhx®@lî@ª<.@%‹A%“XB Ú@–ÀVAU›ðB£xEB)jrB/gæAYÞäBH Bä™AŒÌæAß%’B<íA¢aóA–ø^A‹T @±?@J…M@‘ƒ ?Çr&AǾA¤éÖAŽø@ZAå5oA¯˜üAD:©B5ñ@& kB~VAÊ.Aw0%A¡ÍTAžŠ•A×'Ð=JïAE#Añ¶A•rA³rZA†j,AvP ?iÁ€?ÕÕäAbvA%sªA¾ 2AFIAµƒe@¯Ú @§ñ4A‹BÇêA¬è‡@ˆ?·Õ@‹ŠÉAÊ!×A¾ÝS@@¤Ã>;TB è¤A‚¾1AÌw@Ž¿ìAŽP&A ‚<@e‹AˉA‘³¡@HVÿ@à7B.ÝúAÆUAR‚A õAèÆ‚A™ èA”.LAƒ7NAG™ÅAżAìyiA|ËB X¸BtX@Î$:A±¯@@‘x@êBeSÍA]rRA­ô±A‰èzA™ƒ*B„±8@¨ð×ANAA‘&x? ÐH‡A\qBAÏKAàJkB ¼AF[BLê"A®ÉAøšB0"@…RU@üŽŸBb¨BEzöBÂó=_ùA–w»Ah(þ@ðAÝÈsASÿB<$hA2œÙ@ä 9B„M-A©„AîîÙA@-bÆ?”ˆþB=XAÃ2Bzú½BQåAžA?ÆœøAGô Aß§AÀf"A±3M@ŒD"A\YƒA«@âd$B CÂ@^Çd@‹˜B8è AâÜ‹AËu@™3A苹AÍFÇAÄ\žAð8>A¨J¹AŸ%ÃAáA‰ NB`AŽÃB£ä? õ‘AœÖ©BQµ§AÊîý@Ç'âB£Aí!.@bÖÆ@ÔP€A/çB5ö’B:¶!A-ŠžB¬?uìÿ@·_oA4@óB7PB:@Ÿd—@é"sA˜ßü?ˆÄAÀ%Þ@ÈÂ@¨SdB! >B·âA¥««B¡È@Dâ=I: =ÁÓîAϹ>–™¡> coB ±}AÁB0@¢jA’A+,A×ÊV@³í=Aë|BUðB:…òBVB‰(€B!$˜A†D!Bu·?âÜAÉQVAèzA@åƒÈA¨DAOߥ?ç8 AžáB°Ay%Aâ²BbeœA¥³âB‰…zB%ÝKA§s}Bmé4@Fy®@GÃ?ƒ ÁA×FdAÀÑAˆÙBaüAÖ‘@}jîA†Å“A…ƒÀAnzè?Bû.@ݱ/A^ì^BË1?ÃuT?Æ(A½’¸B%ªeAªU£AÔô®A'€A:UŒB@\;@kš@s†Bí@æÃ@œÐ @pÌ]Asž¡A‚¦¬B0îs@e¾_Aãm @L@b=ÀAï„L@÷îAù„\AZ^®B|NAQ¹%A*øUB/&žA „¡>ˆ(¢=ȹdA(èuAOß@y2>Ž•A}ð)A]ñ•?çJºBCšQBvš(AìgBU÷A‰æŽB[‡Ô@DÙAɱA“.Ã@ Ÿ=?‚öØ@8É–Av^@Ó×B'J@¿Ú&A”|¶AàÇ,AÓ_B)À1Bn„ÂB=6Bˆ`d@/…€AnA¾—¿A£ÊGAîpvB`‰?Í}ÉBæT?óŠt?ï¯Ç@ù¢BJ6?& A÷FAÇS?·š=ÁPó>[ZE@€@Ï}§@“µB-£\A¢ëG@¾ ´Aúž€ALÁÌ@Ó~}AzJJAînBâ AJÏ@¨ ]AçáúAÇŽ^BȬA$ÞB/®ÞAT×A9àÕB95ˆB5k@Û§dA½0B‡AõHªBYýA¾aAÊn,B'þA½ËqB‚{’A ŽA€®‰B[Q7@½VZBûG@œG@¾²ÛA •²Aø’–A>°ÏAÅ_ B˜ÕA97@A…çAɃA‡ëçBlamAŽ…å?4Ü@psNAïdóB!ÙAú¹@Þ æ>qîZ>ßͪB÷#B-™vA€õ4BIJ@cÈ A‹Œ¸AßQ\B0Xq@ÇÀµB"PDAh]¦Ax*qB–ˆ AÜXA¥û¾>7Ä AaâÐAÁÅA?ºö¨AÄ8’Bo¯B%bA!’ÎA±ßA¿ê{@_ ÷>‡¢A¤ê…A[I ?™eìAׯ®BEeA¥¿k?1Œý@ðA Bœ AÁ‡ÊA>ˆ@(À{BüMAŠÖÓA"òAξ¼Akíw>–ê>@ÕÎú@¬¦A<Œ ?¢|AJ1@eˆA à-B'¬³A§At¹AÄÑ2@k¤zA¤µò@iEAHñãAÁnA|’bBE@W¦ Aì B“dA, _B®A¯3›?Äãê@oóQ@¨P@†*ÍA¾[^Aª‘ A—˜6A£[ÝAl€B¢RAþãâ?ìíýAÆšASÑýA‘9 @Ov A2U8A¥ðuA‘9–Ae B?bÖAÚ’“Aš—R>û¥BåA:ÁÜA(×B\‡Až‰–@y¡çAGÜA‘õ'@Õ‹A»¼L@¤¤öA2àBòA«vA>BÁA³Ì$BR¾A“»@ßxqAΑ=Az„»A˜©AûÞD@£1¢A„ˬ@ë‹¢AîN%?‡'A8u@–˜n>É}ÊAbôÎA¹…íAÈ£ˆAËo[@ðý›A†hAú—‡Bpà?›8ÁBM×AöT=@/h’>ˆG,>DXtAUR"AÍf?{fNA'ÛâB¬ AT?äAÞÐßB3p`B=¢tA…²;B5ÉBä/BZ@ÌD›BMΟA4\ÐAŠ)?éÞ6AiÇÃA©›AcTAú}AYþ$A¾ßWBegž@[ûB.]BŒoaA|@B!MBýDB"c¬A¥dBÛöA„MÛA †êAÈ$@ÛÄA8AžA7¶»AŸ”h@é÷¿AÇËl=°£ÇA,(AKe]?ȲA‚ @ ƒ»A…AÝwÂAò·BÑ>âJ‰A‘ÀgBŸ_@ÌG@ýÏB ìA^øA†JÇBfþAœ"aB1xÜ@.È¡AiÓBJ¼@>^ŒB @‚B=@÷|ÝA)èªAÁ A®gAìáÄA‰À@pÚëB™þAÞB*Y@-Ó/@ÉbA7îA‘ÍÂB£+;½DçAüàAË5T@i_‹A¶úB`¾@B1‡mAc“@šÖ@èþ–BK€BîâA†è«@£M·A¬AAó¶¬A÷ùVAB!öBnμ@­;AVØ*Bƒ†AüæÏA+÷ßAoÛO;R~eA’`«A‘iÖA„Ö-B’äBpÚÓ@øJïA¥û‚B'TAlNõB.e@ѧ²AD=8AÃ](@ÐB_9A™Þ»@eQ7>×Õ’@ø+ŸAÈÛìAó˜BWŽ7AÉ—vA† I@¤Ì=B5æ@–ÿß@ƒ…Ag6BJA–Û¸AÚ×êA¼Ÿ%@ˆ£Aa~BD{Ž@M·q@ÈF@ÀPÏ@Ó°;A¾A½²A4e@p6c@ûåØAZGBLA€&qA: ÷Aéõ¼@Ôáê?6%A‚€A)¯©?Ÿ„‘B DÎAÞy Bù™A“r~BT†aB%±±AXñ\A‡(A£Ë¥AÊ—®A+w£AheAî6@ãsÞA‹hcAÁHÖ@gÙÆ?Ï õBƒÄAÍÞsAØ6AÊ4CA»ÚB"јA)ˆ @¤Ñ»B/¶tBôAÀ]ŽAAÕ AdïÙAF÷AëðAûAºt]Aâ#A£2ÏBbPAÎú§AÓdbA~š AOfÄA¸Ûg@Áì0A}àXA°”@ù•@=ú>ç'@@#û@† gA¦z¼A<7A"ñýA-ÊKAfA A×’ˆA<ï@>Ï@ÙdõA‚È@ã¶BÉBA»ì@±ê®@ûAÔ “B>ÍAvúp=¯8I>$ ?’àŒ>• ÈB@@W?Š0×@ñ`>’[ÓB ÇB)t@LNA7UAæ[@ÙAv?}I/?ï¤AœC>A¥#?Ÿvo@»÷ÀAÞ?jAR{VAÞU@[Õ§B3*A—?½*«Bq¥#A’™A€SÒ@úÆSBöûB ö@ÝQAþØAŒdüAˆu A#ŸA€ÊNA©„AÐ%@÷ƒ´AÆ"èAU­ñBwU/A°ÜAÄ¿APì‚@ÄŠ AÞ_ÅAò` >ߢžA]§ A¡”³BêøAÅmÖAß“Aø%A"…úAç[A´ÕAßM(A…4Aá ÖA•~^Ayµî>ÐfA\©B£D@?ä+@M ¾@¡X\?ûí]A8¼Bâ´A¿­ @³…vAÓ¡ AV¤ÓBo)BAÈ‘BÙAÈ%B3!AÔ¡B(ƒÜA{­A¶ÌAG)A;àA$VA“@uì@².ƒAåi¡AƒëÇ@üŠ[@ªJ AƒóýA'°¾A –u@‹áD@‰ÄAóyA¢¹A'Ô°A~|-AÍkk@òdÿ>YWfA²ä A’r?Ù©A8¾BA †A6 A’êA6’û@ =SAêâÇAFràAÆRa@×AþBa=AN¡ÇA1²B Œð?•†?ýµ9AÈPA+BIE¢@ÇfxAJª8AÛÂíAJeoA°  > ˜ñ@’FÿAD­yA‘ßtAZÕ,ÄB/Ð@§ðbAÅ©ÐA6üU?c=ÕB^uBR$…AßïB É?[¥¶@|b,?7‘wB,êæAÙ&§A©jmA1cç>—;Bç7BÇmAÙæ…A®„|Bî‡A B…¨A$çs@°8ã@“ÌBŸâA¯ [Aèsˆ@Ž7Û?¸ÈAÑŒAãê`AEÞ‡AšÂ_A ùqA2àìAͲ}Az²jA1ÈA^á®B)LÎ@1Lô@*ó @ –HB;“*B/ö%@Ãh@\ˆ?‰þŸ@‹bP@þæ~@">“@ìW¸A˜ó@ݸ@Œ4£Bì@”ÐJA*‚ÔA¬¶FB'±mA&ÿVAÑ”-AþµW@¸³AËØýA=°eB9cäAzè-@š|YA°/B`ý ALÄ_BôAû íA¤XHA¸>ðÏy?å!O?÷º&@ñÐAê_-A¡~¼B-'òA‹AªäÎA¹AjÁA,3lB]¬2A0˜BVѯAójí?üéAGANº]B ”úA.FAÔ;A£ÎÝ@ÁwÆAÄGF?U»AÇîB¾KBÉzB#ŠùAYsA%(BXr@Ú¥¡AA)A.KAŦÑ@Ë@ƒ@lñÊAöô@φK?+Ðä@±`SA&åAð׫@ôgÁ=k)ACbB/ŒRAny@â3B€eANa?ƒ =û=|¿@@<"@?>êºB(ÈA•²p@¾ÈæA&¡A¶Ôµ=î ?†pAžÇAÂ%A¥ÐA£+"Aú 4B¿“A4KƒAzºA¢£AÇv&@ˆËTBe n@“êœAe^íAˆe×A§æAA„ã‹ANþÂAæEhAzPO@áŠAý/áBÅBWÄSAˆhLBNA™*µAâEB hÏAÓu´B-lÊ@«ÂQA¨J°B`?™—\@Aw@ÅC'B?téB¿tAN9M@•¹@-7@›àúA¦ö>1¨§A´’dA©/Þ@[nA)KµA%ø¹A­õ@Ÿ}í@Õ(¢@ÚÏA„Ü‚A/aÅB?±}A¤ƒò@$²‹A3ߦB)ÍB y@?„9Aðtw=µ‰ARkBùVA›ª@ Q™AÖË¥AÛñB¬ª@õéCAÿ´"B1‚ð@¾ccA‘ÍB¶°AÏrA%Ÿ¨BŒA«AÙ| Aë#ç@)ɃA\œÆB?A“þµBã´BHæÛA'§šB}yA X£Aß½?”Í@#1VAù(¿AÁAÌA·NAH¤æB«AXÀ ?ꤰ@T!AþúÑA!U@¹­ÒA€)A¸£œA:v?BLr†AÁÂ@™°Aî°AÔ HAa´A¡_&A¡Æ¢@ÎÖ@Y/å@PV³A„U²Ar´ûA®ÉKA­ A GAe¨ÂBîºAx-¦@¿íîAÀ AƱ?Õj1@Ù@‚LÂAmÍ@¢œê>»¸`@C]Bj¬AqmuAœ¢@ÙÒðA•º@§t >®ò4Aí);n%A£âÜBFCð?ö»AE‹ AÁ·ó@œª!AGAöR@³/ù@÷“%BúŒAgžB6¹™A1È8A¯t×B,BBHΕB8ñÀA{ÂLAþY¤AÜ6>¼M—?BÒ(?Ü+ãA4 QA¦Y"AêС@0PHBËÞAñÖy@»fBsAARŠ#B ânA‹ŒBfÄA•9AŽ&A‚6Ú?ZMEAß’úA*\"A†;4AInA‚@?@ñ$BVmAŽKŸB¦8AÞkA>9¬A•#Ú>µ>ìí\A#‹PA]4@7pùA!Ò—>Ü1>Abé?»Ý¹A=Am‘U@ÀAyAªìq?, ;ZÈÝ@ª&A­"@Óg{@‹G@¬/‡Aà A>A¢õÆA¶>ÔOAóÞB~?QEãAaÉóAcSþAb³Ai}0@7\‚> (]@Å(Amd@ÃÚô@;øêAcÅ-A¤ðE@µ^UA¥n½A6FçB :@)„à?€Q•BxHAÞ?ųøAALB$š¡AGAÀ\BaE§AÕƒe=ü @û.ÜAÝÍmAÑÇù@ëš›BO.A¦‘AöRÅAnñ B WàAž€³@÷üAƒv‹AÅä(Aí AÞȾA¾bA?8B-]8?îAA9øèAŒí5AÈ ï@ñ˜ãAÊmãAÁ¼ A,'‚AˆÓIA§ÌvB#¬¬B+ö²AJê}BTF"AøgvACKJApA>ëBô@G¶©A‘’QAÙ$Ô?&² @81ÑAÕͳ@áÖ@¢1@U]µA‰1e@Õ!Y@G\ÒA F@ë¡@Äð)BDeB@oIA½òØB,¦\AG$á@ïwJB•@ÔU[AºÄ?A4XÓ@CU@1%NAzpB?dæA­FAÃ$%@mØýA*´@Ç4v>ÎK÷A¾F|AœÀ@F´ò?aa4@*½>@éàÌ@‚©>Òż=xFÈ?f×4@£,¶@‰ü—@X‚‹A—)$BGçA> à2AšbœA©LWA-¨@B÷¡B{…§Aã5?¹ML@ÙÉOB¹ÙA'×_@—õA º@_9ÍA ï A:ÜSA‘ A›ŒA–‚i>yüeAGaBTwïA=n˜Aq[AóÉôBÞâ@Žv›?*3»@ì ˆ?'5Ê@pYI?÷‚RAjºäAP2“AÆgVA£ÇœA_QA/FuA!ÚEAa4A‹ýÿ@ÅQÌAÝ8B".@ÿB>°¥Œ@2´@ýÕý@±¿W?¾;A¾ŽAA²;Aò˜HAºjA¥/Š>‰;I@é+öAµî›BG4O@ë“ @]®¨BÉ,?€MAAÜñA ó@Žqi?µ¯Ô?r7@{,³A÷ý@åƒRA~@±,‘B4»@%öA”4éAù%x@é>‚½&A®ÌA94®@Ÿ@ÏA+Ýù@ã ¿?ܦrAð–M@ŒºŽA! A¤å@†÷ANë°B–zAA³AíË@¶cÿ> îh@mAù A?ó@£ËKAÊ>ºµ#B¥5@—^Í=¼]·A(›®ASƒBBeA‚`’@iw”?‡j@š€e?ûò¹AnŒAÁAH Þ?>ÏóA:ÂøA;r>@i BK$D?Ù”;@áKIBQm5?®4²@E½+B,ïšAQFúB`ï!A“PâBz\ÉAéŽrBÛA‹ú›A†›@àœFAh¼@Ø3‚@⛊@žIñBð‹AãQŠAaB1;¾AzÖñBY‹¤Aš¸Õ@ÎvgAËçßAÐa›A}ið@‘PSAˆƒ›@„YrAëâByAO̓@Ë/uA´/@… ÁA«AKÜ´A\ž|B%„_@ìfüA†’ßA‡•Ž@œyú@¼7J@‡›@ò1<AÉÑ@Ú§ç@É0îA’ÊcAÃãm@@/‰š@œÁM@£´TA¦ßìBë7AÉðŒ>ÌyWA&f?BX’]>¾@e×@ûu¥@úœ„@ó'Aº¼fA)aaA`~9?b*/@†“®A̓]ApÄî@ÛÀ@ßPB6ð@’ tAàæBåA@Èk@*qAÚë@_ò5Ay/Aº@èEM?GÝÄ?\yBDI‘A¯•4B{LA8‘‘?œÇv@“ÌB)ÆöA2B×@!Ì>|AÿXPA­t?†*@Ç2B %G@d.ªAÅÿëA²?Ó•|B˺AŠ™A?F/š@^\¦AÑ»§A)æqAÛöAd>?{ˆü@˜HýAæ%0AµpB#y‚<~ @~–RA ÐcAgïAÖ™L@Â&NAάA‘·¨AQw½@•ä@B*ÙA²ä¼AˆÓò@_%õA÷ùµB9Bž˜@ ²µBJ3OB IêB~â@ªã£A›'ûA=Òª@á½ã?Ƶ£Aö¨ñ@Ú@1WéA²•_A¢éA`HWA“n®A•§BB?ê«AúoB/h!AC^A®lÔA¥ÖA¼HpB+õÄAO×@éÞ«A2F7AÆF?·W B ø$Aô{™B)'ðBJ˜Z@uòB/¥B98ýAïxB ö%AºAØB/~ãA§?×yàA#­A²9¿B'aª@Ï ÒAŽ4Aå¼ACã@ð_yAúPB¸Y%BWûŒBf0B¬ÐqAé¾A³ìVBgìA†#sAâINA€›ìA©)A’É AšàAA[é*@±ZéAÛ{6AiYAmšuBäA[«èBGºA¨ˆhBP•B;Ù±B%²BH° Aó”(AI8B'÷ÕAËV‰AAKpÕA‚º?¯ÑQAߥZBzºyB زA¼)rAõ½ªBF>ÆAÆ3A„^@¢Ü1AáhC=à A@¬\B;§ãA.‘BêB+r|B,Ê5?øÃšBøtAiHAïúB'‡B§/A£VÚB#AiÚAàB*jóAí@@Ð’ì@ÂúXB;««AW¬A¦u1A¯?LA ä@hW¤AõÉ@@vu8B%d|=Î(oA˜:þA™´h?PŽ=ü˜‘AÊå@©_>VÞAá¡øA„ÌêACtáAõÿÙB6%#BV¢?Hº¥AF3#AmQv@.1x@›µB6 q@óDšAK´¸?SÛÅA<¶¬BÐßA `@Dé7@‘:øBŽBAñeBì<@U/Ê@èI»A°þB_ø@3„ð@­mP@bjB Ú´A+§‡>ŠfùA9£@ãlßAt¿A-¡A6ªAáãÁ>ñ®º@š”öA€$A»R AŸ^V@ƒjH@„få?¸j?<‚dAM>@›ùöAèý~@õ@0×1@T¿’@žç›A9|A³ãÖ@µÐ?ð^%A‡‰ AÐþè@8¾A [µA6“oAˆ^W@‚¸ADß@ÕÜBfµAŽ™= ¡@ñÇŸ@¸+ZB µQAOÛðAW’ ?O-Ý?ÿ@F,¤A™ì€AŠ“ ?nJôA=ˆABÁb?m Aš˜A|ËWA¸Ä@Z+¢BBÂA³¯¡?}ÓA Á¶@3Ó B0!Ä?`DA˜©0>/rB"“qA€P‹BýAÌ·˜?¾V§AÝ\{AëR!@@ºB>‰ˆ–@³­YB d‡?ê+A'¶ÏB)80Al"âAõê’A¸A‘AŸO\AºÄA®‘Þ@ ?@.§vBz“A‹ù.?Íšm@ÄÄŽ@+5ÉAêý³A&AðF‘A´½ãB[ãçA+dfBÔýB ¹-AY,BB2eyAIÆA0ŽA1Sµ@å³A¤ØÝ@ä BH¨RAÝ®>…×ÛAA¯×B®@\ë„B?HAìUüA΀Ï@ºÑK@¸N”A_§@= ‘>‹úB<@¡ÒËAhE!A@=˜BŒ\@Û—ã@?•A¿ŸsBO÷lB/‹!ByB&äÎAøMxAêÈ/B>Ê6A†Ê%?Ÿ¦!?ì¦AUÍ!AIfáA-]B!w±?ДþA—ÍAï5:BÎðB ¤ËAÔÒÆBôA|ÍB"çSAüÊÓBÁ AGožA% @B¹÷AŒR8@ãÅAN¼ñAõÞAÚBÜ©BºBRŠA†XÿB hAd6ÁA«ÊA ÇlAå ÂAh…Î@álAP:€AƒG7@UYAíqˆBDH{B$žA•/BŠBA+>AÌïW²Am¹ÒB ä@]HdB A¸'óA@ª‡A˜»øAÑ'BQYÇBÿÿA×ÔÉAgB7&¨@¼ùAZ”AŸ@YAS$™@)j³A§!A‚˜s?ÙkAH@½Bè @÷¤7ADÅAA÷ÓAÐÜÀB‰ÅgB , B­ÍAÜcåA9ƒ2AÒƒˆAÅ7ŸAjb@öŽgA Ö-A‰£ïAšãA«Ç{A×ÑB$.WAšgqAÛµ5AøWA_ŸQAÛÓAöc¶@Þý˜@à@Bàz=ýâë<™À Aš7Bt@q)õA ÑAÝLMA>?ÒA‰ÅüAíã6B %A‚*AÄÇAf½ÖA >A„hB˜AWH?ùÌC@ˆ[BZç®AéDÜ@·1<@ˆzóAíÝA¦¥žAM~LAì,A—;A×øAFLAš0óAƒðA,/@JGA³AýMðA3Áq@ÿ ÑAÓ{B ´4?LijAq5A´=)Ag’@ÃqA²'A™eýAMA‘‡ @¶ »?ñuO@4Y;Aê’AY@œ ²A׌/@áêAÞžØ@Ï6ÑAÅi?Ä~ AXp™?BÿA¶R“A½¢AEÉŒ? ²ÉAê3øA‡Á§@ïS ?g ÖA2¿rA)3AN&(Av-AÍm­?Ð~6@ôQAgAÀfïAG9K@˜õAÝï'@°l©@V.èAc§î?=Û}AŽ?Aˆö.ASaEAéeB §Ú@ u@3Ã`@£2?¸fÄA/,AÏÛAˆßrAÓ„=@ ¸@‘hA!BâAÌY4A€$ó>‚ð1A@ó×A7·@¶KDAE2?Ò‡…A ÕqB+$AUŽ>š0ë>8"\BoA¥²@Mì‹?E2`AÑ`«@.,(@¤êB=ûRA6¬A ~ÑA»ÎÎ@¶§ä@Ê%ACY³AËãêA%A%áBãDAù‘ùAuC$?:ŒþB ÷#Aš|¶A|ôÇAWKg@¾­÷ApdA¸‹õA¢ü®A¥X0A™ð“ANRf@T AÀRQAXÚ@Ò×=AžÜì@Šå«B9MMA‡ÐÖA¬¶1A¾%A>n“AëÿAàTü@Ûp-@X,Ä@²dA.B(Ã^A;@Ë@ÙWBOÊAz]“A¶ATB n@ÊH^AqAƒ¯ÝA“5;AÀã¢?ñÆ @0@|Î@꾈AŽgç@Âo@Y<[Aø¥‹AñÖ†AKŽB5 ¤?±«jBPi¥Au•'B”¨B?œ’? >A’f€A(`ý@Æ BOMA¼`AA2öƒ@œ3A­3yAŸšA9+CBÖo@Ô!8A¸ûôAŒõAÓÍWBl±A§Û"AŠOaB,(¶A"AwçðB-˜NA[µAДûAû™AR¨ÂAáOAÕƒA¯wAõ}ïA‹ë‰@ûçaBDðä?eE$A‹° @äÍàA´ø†?ž˜PAQ´ƒA¥¿ANjzA‡‚;@6³—?ƒØpB ^_B ÀÌAÚB\TüA ð|AûAÔ?×'æ@Ø¡¤AVõAsZAÊ’`?™°A‘`«A!ï’B8(Ð@GïVA¢…ALz¥@–NØAŒ°¸A7EAhP A:<AÛOí@üj‡@]2A—ë¬A–üÌA‚Ò@‚é‚A…/BXC@þíA©6D@÷¹b@õd•A€ÐrAÔ­åAKù<@…™G@Qp¢@žWAK™á@+m,?28>ÿL@,‰?î’A|jA&{ˆA޲B »@á}@ä+»A4[’AmNl@Ži.?'Al×a@Gið@ñB5AW¾A0'GA£hAô2'@åÊ<?âK¼‰.?ä$Hh¢?æ\Àp²!?ç‡y[ï?æþÌ£¡d=?é,4IxÐÌ?ç ßÐÂï­?êeÏ>ÊD—?çÎ>õx»?ê}̓¿>µ?ço–hã ?韤ÿ›‡?æÌ>N2®‡?ébÙ•ÁÊ?æwýÒä ª?èÌ@¡Ò?æÍÚ´´ï?é8D ý?çoW]~¨?êÌ× Ü¥?賎xÚˬ?çTÄ Èh?é ç2Äß?ë[Ù4¿Áº?éŠ(ôæâ?ç2¦?êͼmï—?ç±¼£ý?釫×Rÿ¦?æ(ºa:.:?èêÍÂÃS?åg?Þ?çi [h*?é¦Èø@äÍ?åYïôp6R?çá¯öF?éßð^$Ý?å¬ ‘½õ§?çÞûpš›?êu´68“?åÀ”±ÈËJ?çÏR»Ã»?ê?÷Ôwrñ?凓_!›Ý?è ¢dÎ¥þ?êAlùáôÏ?æWz­ZW?è#“‡žðº?êýŠ2gÈ´?æÿ2°™›@?é+ô.B9‡?国T², ?è;s®ü)…?ä%±aâDi?èN§Ïö2?æÜzu?ë#fƨÿN?ã&U@Ã…?æE=X§°ê?èŠD6Ö¯¸?æL†v[è?é8FFš2?å‚1?&îõ?ç–ï¥y£µ?ê V®}ê?åÐNÂôWM?è/ñ£&ë?êKõØDî?åÔYõ@~o?ç˜7¤@Ô?éÒËv¬á?ä1¹îqŠ(?æ«bNóÜq?èúzÎ{Žj?ëƒ.Oü:?冱®‘,?çUlPÅ’ò?éUb4z#?ëT¿™ eî?å—À‰·ïà?ç=F7Ûd_?éz኎?ëý-ìÇ.è?æžM’÷Ù?矞ÅIÿ?étj}p®~?ë_*Ï 6?æ]àÞF_Õ?çèÊ0M:™?é¾ð,Ä?äDy-…f?æžÑ0¡ƒa?èç?©“C9?êð ì­Çÿ?årˆDd¹5?çðˆ>-?é¶âùÿB¡?ãÒ_ùÖW?懴 š7C?éjh†_ˆ(?ä‰ \@2?ç'bëÑ£y?éíf!Ìg?åÛE²Çƒd?è-2Ÿþ^?äsÍç|lå?誀hºƒ³?éOÁ†— ?æþ §pÏL?é<_V…D?æÇš`/?é-Ï?åš„e˜Û?çØH›ph?ê0IÎ*E?å €e¯?èOœr/?éâT3w€0?ä}àÉFÄ?æ‰vó+D?èNkL½4?êzj.ßßÿ?äTÉ0Z?æè>¶'‰"?ègkÞU+å?êmW´ˆ ?ã˜Óï €§?ælç_±/?çž7ËÞð?é5bÈl©?ëTzq²:I?å JyI&?æ— Y!¤?èl5àíu?é¾µ© Õñ?ã/Á%ê #?厶Ò<‚?çRXp"r?èå5©âJ?êyÏäÕ ?ãLHs@N±?åð_¦?çÌÕÙ˜?è”D‰Á?êÍ0g§>?ä`œzE?æ^‘ûD³?çîØ9t¥?étãh…þŸ?êñ¹µ±:?äÒÆ^ÃêŠ?æÍ0´ÿÚ?è…!ýHî?ê{Û×7?ã’tµ?æÑW¨xE?èj²Ùo?éÌçÙ'çð?ëºkL Æ?åxS®º )?æ·XÈkŸô?è@|'œó?é–?q‚«q?넘¸“¥]?ä¿"b«ò½?æ-qô+3y?ç†ßº$he?è»$ ¸ÂH?ê‚Âw=Ð?ㆥüF^?åðë­”Mâ?æ¾Å-a9?è ò¶GÆ?ép&°X?ë\«*c?䂇?åö«±Ì??ç"MVŠÇ¨?è ½_!F•?él4â#?êÛ›.gÈi?äC—…¯è?å¼ù`A]??çv¯ÈªKu?èÒhûß>?é[õò»0]?ë£G—¦?äbÖØEÀ?æ%Ý D–Ë?ç4Qe`À¼?è-J}µ“?éß•Ý[`?ë$1à¿·§?äÕ–UÏ?æmÎTš™¨?ç¢ÙÞ%½‡?è§E¨3¥?ê& m-?ã[Œ5r?åØà5óc?ç-¯X??èŽXëZŸ?ê*xi@›?㚬8Vã?åŒ?®£›3?ç{Ñ«’ Ù?èªL,„Šæ?êß’Ïs?ä¹Iö…ª?æúÚÚE?è½4NÏê?êVûú~¤ž?䊚–Eì?çS\B¯]‘?é]rKš3?ë?A˜Mý?æ_Á¹’ž­?è†ï!¦§ß?ëju¦¶c?çŒÒ“RBH?å¶wƒ–ä?èÖµ¦é—x?äÅ>÷‡˜•?ç\sSý¦Ì?éAø3hÿ?ä”&¢ŒÊÜ?æÿ€Ø{Üþ?è·³V.Õ?ë„Û1`w?å!'ÀØâë?ç!‡ÿ»9K?èï'˜$›4?êÃÿ­…—?äX£Œ…*?æJáï.’Ú?çžQÍ6I?é„P Àò?êÒÒ…r¯ô?ä1·Zfy„?æ-añŠÏÁ?çíIò¡Ã?è¹e&µý½?êDôp’r‰?ãdÌÃE4?å< ‚¾`?æŸ=´šà?çõ:K@j0?èå™W>ÖR?ê$${?ë¯Öß?äèfýý{?æ:„«ÿ?çEf–©§Ü?èK¿˜ã/5?é q:è?ê IÛ+Ö5?âœ/=ÅÝS?äá`Ý9??æ:¢UÆp+?çQÃ÷Ñ?è êcÓiž?èžƺè?ê'=ݺŽ?ë'e…­T¿?äq D’Š.?æ¨kQÃ?çkq’™ ?ççÄ7—j_?èÈ_èt§?é:Ímf/?ê¹^åMþ?âÖáÆJä•?ås—] -#?æ5XUðM—?ç:J¸Èc?è0Yf ’?èšù¹sÝ}?éóߢÚ2W?ëCg?äjæû?å¦pK*`?棃óú¶_?ç•õ^~z?è¡î× „?ééÓ“·Iä?ꇑx!?ãW‡Í l6?å|‹ôiçÕ?æê\:?çàS+ ì¾?èÚy4®;Õ?é¸ÖNËÆ²?êàzDˆœ?äLšŠ˜Š!?åÄð]ÜÐ?ç'zÚô'?èZƯKþ3?éIŒÚkU?êÕYQ3ŒK?ãzùh7?å¥d /­?çUlyቡ?èmÒ ¶?é°uf[ð?ëZOfY¯@?äß~áß?æþyþ¢]‡?è £Yj l?é¾mÔ܇?ëùîÂÃ^I?åáËÂ7?çe4{rãl?éO?71˜€?ë j"öÌ¡?åÔÚç8¸Ã?çÞßÎàÍ?é¥jضùž?åÖÀw&^O?æ©j]×?éÈ•?¿‰0?æBK?èÐ'˜g·?ëfP]½K?ætóÍ/?è ”p4`J?êCZèò*Ž?äß_R´.Ó?æoP³u?èÊæ3y?ê€ Y%º?ã‡ýÝñK?æz^0³Øü?çÓ(hXg?è P"ˆKÞ?ëšÜ_ëv?ä ‘û€»?åÚÒc©·÷?çCÌkògi?è>É®˜ÿ¶?é´ÜÕeY?ë)mæ]Ÿ?ä6Ùd ?æ€%|ÿÃM?çFÝæžÿ?è3“Ò ?é%ó~:O?êWí;ü›Ô?â×ðµqp?äæ4 mÆü?ækN“wä?çO„¼(]?è{Ö¸ã&?蜻ÓéèM?éŒÀCÆ•?ë1l˜7?}?ãÇGКÞZ?å AQ¡9¡?æuÝu ç?çgUæ¢3??çþVän8?芔Ÿ|¿:?éš§’:VJ?겕KÅ?âÉU¾ù²a?äõs‰mV?åõ)¤(gó?æÃ¼›vx3?èZœa€S'?茈| ò?ét„BVÜA?ê)ÃvÖw!?ëËÁpz™?äðè?å¨êõ†çB?怾eæ‡?ç|.E<³?çùFTQÔ²?è ]o¶?ȩ̀.0?ê-w0?êÇvR€ýº?âœ!¿þ?ä ¶ë=<­?åÌ=ˆv?æ‹Y%Ö?ç[;upÚ?ç­¼'=Û?辂¯~ýw?é0=ÂÊÙF?éÙ˜Ö*z(?êŒ0ÙjNx?ãQ¶í¬¨û?å&/äø ?æ7uhjP?æÙHµ3\?çœ%»“Â?è –L[".?èú;кœv?éíåú*|Ð?êë;¹šö?âþºE<*k?ää÷F°OÔ?æHF‹‹°†?ç¯/ ˜­}?èá_Ýll?ézú­O̲?êYóì«ÀÓ?ë„jו?äe9ë–ƒL?嶯(qÂ?æËlUË?çД!5B?èËx~rá?é=——@õ?ë^?æ«Ù?âà…«Í¦>?åNŸÌiÆo?æb>`$^?çújKc{?蟮ÿKý™?é¾÷i<áž?êÏ`/Sk?äW¯°öi?æM7;¿ñl?çD×hF“T?èÃ!½‡?éÄj|eàû?ëïE}ö¸w?åƒéÍD²R?ç÷ÎV?èJ:§}Q¢?éû?²Îž?ë¡]«V?å¨BÒs1Ñ?犞ëGå?éAx,ë#y?ë0Õ\¶þÈ?èƒq,ªy·?æ"@oD?éPßh§ãZ?åKûÛ–XB?çÌñöåÍ?éçîV9G?äÆEúU?æØä 0… ?èÔêzJå¬?êÜ\Æ…È?ä»r:Ô˜?æÃ@žÆ$Z?èZ°Ñk‚Á?éú÷HÃÄJ?âÈ]L+ð ?å,8Î €?æØ÷ÎÂS@?èK¾ôõ?éUèêG,Þ?êÕ tB ?ãÛÄзÂ?åÕ‡²O™?æ“à¥+Êh?çÈ…9´h?è´$Æ{Ú?êaaYŠ?ëÿ[‘-<®?ä A ¼?â_Ö¾½Š?ä¯4º*?Œ?ë¼Eá8†?ãÉ6Sl©?åi:µ'ã?ê¢ÌŠ·‰?âA ±·E?ä;ÝGÑ#f?å:ßDù7å?êò92»¢N?âAzLJ?ä;*bZ×K?åÝÿ…ç?êµ×¸"=?â­r¡LêX?äpù¶bT?ë?Hâ8?ãDAÚ;.?äÛÏž°ïp?ëC̈m[?âÕ€úi ?äêkœÙ©?ë™î“Í>?ãÑ`vCƒ?匵ÅBÏ?ëHlRUñ?ãIH¬º?ëzeVìo0?ãç Ô90g?åïe×™õñ?æçõ…%Ä?ç­¸ñ6f¶?èž•éZœ?êk6},r1?ëáW*BÍ?åV›ª[4 ?æÎæÐZ?äHw9ß½?ëùÂæ;©é?ãúçp“Ÿ?åB8ž©ëè?êáð(«?â;~¸Æx?ä]TÉç”õ?åUÉNƒp‘?êàè¥-ã?ë½í)Ë`?ä9~¯uÈœ?åóÁIJ±?ëh¦ßÚµª?ãà‰-âñ?ä»9È&YB?åVÛ9–Ðí?ê™äøj;@?ë¿–á8 %?ãž•í%(L?ä‹áDÂç¸?ëkýʉÃ?ã#ûÆgõî?äôÔÍ1_?êÓ65#í„?ëêäÈ–£?äx¶9?å}Û;W?ë%%±"}?ã«>Êç'“?å)þ¿¡'¦?êÈ‚m —m?ãn;rûp?å±’Oqð?æ:êXc Þ?çSŸüw(?èZ^Ѿ?éfg§hû?ê ã¨Çó?ë–Ú&K?ä9z´ñ?æœÒ§ÔÖ?çh<´Ïß??èÛKÁ±¿?éÌaòâN?ëpúv¼Fò?äÌ”y×øâ?æ1þÞ&×?çÕ Õe_?é§»\š—?ê×ÀˆäP?æÃ\:ßX0?éȰÀ?å¢bf—ê?è-•¸1G?ê§>‘ÔÄí?åëÒ U¯ý?çëz;æÆ3?éŠ?¥™÷f?ã¶H6áí?æTrÚÖÛB?çà‹-ðïô?é\Ë9«{?ë ÷W)?äÊù™ðÂ?æ{í|ûçÀ?çêÍc/7Üx?ã7Bù‹³í?äÜã¸ÐX?ê‘á]B#d?ë¨Qï0\?ãÒ¼2ÆE?å5ÍôËÛ?êÿcÃ9·×?ëÒzÁ䵆?äšÚϧ³Œ?嘶ŒÄ3?æôdz„²?è¿IvzR?èz?ŒÑ…=?éHÛ*óë?éÍfT± ?ë Ñw1!·?ã¦gl\$€?åç֛܀?æ¾V÷“ZA?ç°þ5^f?èÃå±?é͡­|?ëe2†þ?äÔ†´¸£?æX,é_’?èjR³á?èšéÌj‰?ê}3Äã À?åöOíYîp?èaÛð‘ü ?䨥¨Å?çQJ=p¼9?éÞ'M«ßÎ?å΂]Þ¦?æÿÑgPÏ?è‡Þ2?êà:à z?äô2ž^êï?æšwˆ5›f?èmXÖÌà?éè8î\ãí?ëê­¥ŽŽ?äòßÁv€Í?æéÙi°0?çèHK¶­?访×¥.?ê~ÝÚØÄ?â•¡0Φ?ä–›‰"„c?æ3™h]Ô•?ç ©S×P?èY¨¡ºï[?趆ݯ¨‚?ê.ÃÙ±<8?ë.üÔ¯Ç?ä0dúÀ*±?åØ_Š\?æ-ž„>ÒO?çVìæN,?çïùLyh?èÌ­¬½ñ|?émOA°ñä?ê 1McÜ?ëAS˜bdÒ?ã9óã2_?äùãgJ³?ë ¦«Âp?ã#Kï·È¬?ä˜o¬¨P?ë¶ržƒ?ã<ŸXô®?äöc¢É~,?ådû¬¡¡?êÁ¢®¼Ø’?볨!‡J?ãæÖþM»ò?å/¤ÚG5³?꘱"úÈ”?ë ùB:·ê?â³÷!Üi±?ä=ŠC“?å;Éøè€O?êà‚ÑÿA?ëTÍ®­?ãŽI©”Á?ä×¼ïh€r?å4Àç$b?뜗B{‡?â ßlnƒo?ä?ÛŒZÄ?å`ð7b?긥‹—çX?ë`‚qg>?ãlι?äêK£ ©[?å aü¿@?êí”DxÉ?âzzT³Õ?äapùZJ¯?å*Eß“-Æ?ê®ÇµÝ?ë»äêå(?ä3¢M»Á†?åoS¸5·?êÑmo ñ=?ëýùþ.…?ä*æWL?å‡xxXÌJ?æÝˆÈ˜ƒÔ?çBÝ ß?è-²vO6?èãÓ&có?éG£d°??ê_Â7˜¡?ë´…†¸7?äJÝØP®°?åõ?ü~'Â?æ¡PÔy‹?çÜF;ºC?è‡þ®Üt ?éÛt©c?êŒ¯Žž¾a?äÛ& %)?åÝÓ¡{37?çC±~fé?ènnÀJâ”?ë16ÿ)@™?æ«éëu?éz´uÀ°?åvî,ÿßd?çØõ ‹ˆà?éöÀkíJu?äú[z£¹Ç?æÅÙ€Ñóv?躥OF–??ê_[飬¸?ä`©†Å»—?浪ë£?瘌Ã'PX?édíB²R?êŠ È ?äP1•Tå?å´D»?ç"F?FË?è+,x.™5?éÀCßš?ë) çÃñN?ãÃÓÕI„?å±~³/K?æÑ/ˆàý?祿à'+?è—cW+?é•CìÐç?êv­T™Ý?ëã˜.Á„!?äN¥+nR?勨—Gë?æñUÏâ´ø?ç„Í)¤w¬?çªl*O7¼?èÙBùŒX?é7²,Òθ?éºx°‚­?êܲ–wî´?⣃­Ï?ä´cixi{?ëDzLûùÁ?ãa3ŠI¾?äŸMvÿ D?ê’a?ÊV?ëŒJƒ,ÝD?ã‹Çð[Çè?ä« Sƒ?ê«FÙk?ë+E^Ñv?âï]´Ð<²?ä]qMK6?åYò‰*Dª?êÛ ŠÐh¥?ë¶qhèP·?ãä]@‰Ëq?䳕œÓ³µ?åv'8†B?ë«Ù Â?â*5ÖO0?ãªJá+?䟢=Ì`r?êµ§â•W?ë{¢mê¦Ö?âß6‹W^?ä›—q?ål$›…{?ê‹f®tÕô?ëÉ*¡ˆ¥‡?ãnhÈWï?äñ%Î^ç ?ëe¹àÒd?â¼ ‹ ó?ä¯Ù„nø?åQø+uáR?ê•sÙ‚ñH?ëõ>iŽ`£?ãÇÕW)g?å*BŒÂ:h?êØ‘!øç?ëøË¼z×í?æg–±Ó?çp¬Údäb?ç•îði»]?é > |ð?çàŸ €)1?é:öîÉ+J?êÓ¼†˜Ž?ä5DdŽdç?å®r]õ½z?çfÞ«ó†T?èH;œh>U?éüš(?ê×RCCg?ã‡cP­¶?åD°ýÙÔ•?æûf‚7§&?眫ñ´F?èBÂh« ã?é)£]-?êiˆoÊ?äPZŸd?å­ìç|˜ˆ?æ‚¡ ’ºZ?çR«õRëÇ?èKŽÆ?è>€dE³Ù?éwêm<€?ê'§V?êûç÷‰î?âPl.?ä>‘^Ï–ô?ë9}%ú?âÂ3£‹?äòP—²›?å|šÔ ¼?êên´¤Nì?ëØß¥?ãIƒl—ði?ä¬q,Nñ ?ë^¯Í—}i?ãCê]†?äC¸Ïß1?åF„j?ê¦zäq-?ë¸STÓ?ãã0à™E?äØ5 ~úï?åcrïQz“?낎PÚÝ?ë÷úÿ²Åô?ãËfð äL?ä‘ýf\?êÅ÷^ôl?ë SJÉ?âx£ï?ä­Á »?åb«š#ê?êË+läú\?ëýÝ$¾¢7?ã½´Ùß?ä—nÙ•k/?å*ÙW˧q?ëP~[¬ûì?ãt™¨l£?ä}”h,÷Y?å)Zñ?ꪞ³¾G?ëh25ÑøÁ?ãZ¤7 PÑ?ä,Z´Ñ°?ê¯ÖÅYW|?ëÆ(pàÍ?ãí0G˜·š?åU¾#ÐùÙ?æVµ:P;W?ç1T —-J?ç¶ä?Œ?èXÀl†)%?è)’ ð?é©=ËŽ|—?êŸv[G„i?ë²¾jìã?ä¹0”¾Ò?æ]á©«ø?ç5ý¸bÑ?èVˆìk?è¸?ƃ ?éÏuHMé?ꇡcLõ?ähÆÉݦî?嬕 3U ?çMjÏñ?è8 Gkë?éo)k´é?ê³óÿ93º?æ¬ÓƒÎ?éu¾­½&Ò?劰A‚Ñ·?è p\oÓ ?꬘„Ž?åv×F÷÷Ç?çSSšÇÓ?é­É–?ë³RVºŸ€?å]V¤® ?çYƒh¢žÄ?èÚ {Ô#É?ê Yåý?ã&£B9.#?æ_›8ÍI?æ“Þ´jNY?èmsÌ5{Î?é_r ýq?êŠÛÕz±~?ãŸLzQØ?åfcéu‘Þ?æ‹ß䋨ï?çX`¯*öO?è?}Q³?éª[ÙU©?ê.âÿ_ië?âÿ}R^º?äìd8âû]?åÊú©àÏ?æ¿h«ä‹ƒ?ç<Ôâ^?è^™ƒ4fC?èÏîÚk ±?é@Nû¦¯g?êi®´?ë–#6,†:?ä<ì‹N?å#OðÔ”?êÚ»)´Ó?ëÓ¡tðù?ãÔáuÓ?åaB˜u â?êæ……›?ëÓ2-Czb?ã”n3Úœ?äé ‘Ñ"‰?ëz1û¯¼?âß}ŠìúDü?ãw–÷¯XÒ?äíìÛàEá?å!Å–™î¯?ê‹`Šo?ë%7Éå?ã·OL3’Œ?ä僓[/º?ëc¬õs}7?âݼJ[±´?äÐ8á—¤?ër³oгþ?âÐs†Hm?äœn4Ù:?ê‹d‡®ÔÊ?ãMëùq› ?äÑÍM`3Ø?æWzÞäˆ?ç·öÔä?çÿTÒÍØ?è)q[³".?è—€qÖ'Ì?é–¬o‚´)?ê‘ \ðÕJ?â×鯘/?äŠ]@Ë÷W?æ^©W,â?ç~Ýõ–ñ?耱Ð2Ê2?èò’!Þ?êqÚ*.% ?ë/‹²&?ä’ü”¨&?æiƒy¢HH?çÖ0†¶8?èì‹rÔš?êIßak?ëŠw›Áîd?çðF.¨gX?â“pfá¹?ç(U‚æ?ét£« v‰?äJw%Áa?æíî¿s¬?è—‚1ªk3?êç¤âfž)?äÃÿO‚Yô?æºò™:°?èJJ¹+uÀ?ês:d?ãþ¬ ,?å~Ò¯Þô‘?æó½ùœŽ»?èPQ»Écå?é PÇ&—w?ëÆ å?ä@«qu&?åïý› Éì?æ³×{${?ç®ý¨%à?èòöo¼?é¡MbË?ëLIlÇl?äg×# ‹f?åèî."^7?æ³amiZ¹?ç/©²?èe_–?èÄJ¸ío}?éÕÿ*Z?ê¥,_ gk?âãAÖT?ä¥*…ä?ëj£´½q?ã 7…§é?äö¡*U?ꤻþÂB?ëÇþGaÓ?㸚^ùÃ?åa¶½ÇÖ?êÛŠÁDI™?â>!æéq¤?äM„ ±þÍ?å+û*͘É?ê›TÏϱ?ëçùö¸Ä?ãŒu/%?å/×XXÉ?ëÖùÛïä?â¼Kæ¦?äMÉèQ,ñ?åj®gdx?êþ¾‹#œ^?âáC,Çü?ä³"”h³?æK‡1Q #?ç5ˆ‡Ô<Æ?èG3˜`¢?ép]ïPZ?êþžl å?ãŠ|Ü9›?å¬ß½¹e?ç¥Oû?èX(³±?éÏéÒ§?ëú¹Éó‹ý?å'Ûæ®ó?çSö„˹Š?èïȈ5¹Î?ê'Fc1Ô†?è=H#‘ý?çBð»’ ?ê…ä¦?š[?æ—Åý?è½ nLiþ?ã ­V;Œ?æìX¨ Ç?è*>_ýÅ?êÌqÕ'Áa?åô‡$YÛ?ç]ºg"An?èÕ†b,?êã$ÝÚ¥‘?äq&¬ÇõQ?æAÝ÷A_?çŠÛ§dÔM?éH©ð?ë^Økr?äsªÀñ¹]?æ…‘ƒ¸l¾?ç3pHÝÊÃ?èªh8ô4?é¤9l ~?ëàf.]¦?䨥hÂ?æqTn‘‡l?ço^ÅŦ?èS_µÀcz?éÅujÔ´?êñã‹ÃB ?ãS©Ø> ÷?åŸVN^b?æ þ?è¸Hp~ã?䌬\‡O?ç9àŽ?éÀ¹%—]ÿ?äùíWx¯S?ç#ã'¿?èù™Éyý?ë —7Do?å>µ÷~³2?ç[?gµbþ?èÏiz¸Lð?êèÂóËYÇ?ä/Ìw"?æMº÷7ù?ç˜H»³üû?é{½Aû²÷?ëKUzèŽó?äïÚÇܨÁ?æxü>¯ò ?çÓ_!<ô?èèÖxD¯]?êGJtô›S?ãîBì‚æ?åÅîœ?æÍ3'àò?çÔÖ*Mæo?è“¥õ»Àv?ê þÛþê?ëÂ$(?ä™QS¯ÄN?æv¡Š·5?ç £L‰!'?ègã¼úR?éVÛ讥?ê-úKB?âê=Öw|ã?ä¬Q…€Õø?æsWØ:¦£?çhýÊJÀ:?èI_‡ó¤o?é*&JäµU?ê _˜Í-ì?ä_Æã»W³?åÕÕÊrê?æÈh_WÊ?ç Ô:XÔ?èÅÖ:$N?é´Ræâþõª?åcr ýÛ}?æ ­&ß­?çmå'?èu`…Ä´?èñ¯=9?éÎÛòºÀ?ënª4'9?ä 'eh[?åšo9§Q ?æÍÏÞ¥H?ç˜ K­ž?èÅM‹Ê?é³ÅFÕ—n?êðÍKÐEî?ãªÔVsõ:?åjz ‹ÍŒ?æ«ÝÞǦe?çÄ!hK¶?è’l×Nqº?éÅÃHl”Ì?ëš.Hæ?ä6WIï2?åŸ>=×L?ç€bÌ»]ê?èEGœŸa‰?éloÔ>c€?ê˜uýê¾.?ã·¾Köãh?åþ)®Ÿ˜?çG7}7¤Ò?èèÖóع?éýuùƒ%?ëÏ"f{7?åD$'°ð?æéïî œÖ?èrGMb?éˆ|qº2Ë?ë­UB"¿?å’]‹R&O?èƒUT¯Z?éQSEt>)?ë t!)Ûe?劤wV‚O?çÄ.ó¿™÷?éõhe Éè?癎ˆwƒ´?çlÖ]/ªX?â¶n–˜h­?燥‰ÕAš?ꥮÔÉ?æ2ônÕÌ?è"Yz1Ur?ê½\΄‹ë?å¢ey2?'?瘱ÊO‡?éÆå_—Æ?ä&TÎó‘ ?æ Oc#‡=?èpHïšuô?éþn1R~?ã¥}L•rý?é;½õÚPI?éOüHém¼?éú¶\&ü?é˜3«mp°?ên/0cÇ?ê2Ù·TO¸?êÉÑ3…ý˜?êסÏçP¼?ë8´¡òº5?ëDýkc?ã[DZ"?äŸþP?äj©P‰g?ää f«Ì?ä—ÛŽ¶p¡?åj”éQ6ã?åy’ô™?å4]n?å§…S½Õ>?å¼£´É––?å‰$˯=?æLYkŠ×‚?æÓêÇ«?æM´†Íˆ%?æê2I8Â?æ¡GþÆð3?æÂðE!Ú ?æñQw _?çZÕµ1?çÈdNò?çjNV÷?çr1EØöQ?ç…Wkrä™?çƒ^dv©?çç$A¨M?çÿ•,_‡Ó?èšV)+?èÙI ­Í?è±S“†?èZT»B6+?èú/ž‡ÿ?èÃç>ò,Œ?èÑI[¢c?é5¸4Ö˜¶?é&DݺÈò?é'©Ü¨ŸH?é Ñ[?ê]³Ôæ?éöG">õ·?éÔ÷*£$?êwŠ ?êÞ37à ?ê]<ÉØ^±?껇Õßø?뜘‘?ê«IgÁ7Ì?ëpøï ?ë"×;oÄ?ëäÓ$åF?뙋¯…Ø?â”–¦m?ãùñ ‡“†?ã´Iù t?ä8¾¡Cª?äïƒPv?䯸ãyó?äÙäB€Å?äÆ ³„?åfbð©?åOWáÖL?åN'³§s?åÐ}'$?åóšÜÛÊ?å‰xßì›?åh:ˆs?æ@ÑÅ4m?æg•Ø^Ù ?æe¯Óµc…?æ‚óôœŒG?抶®ÁfÒ?æç·“&ÏJ?æÇÌ”Üi)?暆;Ïð¬?æÎ—RM'Ò?çh C±Çs?çêÆôÝp?çZœUÂÿz?ç Õ“Ç”?çÓÔÞHèn?çæàý7S¯?ç«n¸0ÀÜ?çç *Ét?çû¥+ðwi?èuPÞ3?è.ºdÚÒÁ?è~̱â??èÉ<¦ÓS?èQVíŒÀ?èÌAln®?èŸk'?è¨O à¦ù?踶#¤›’?è×k•lë?éß j?é 6£b®æ?éð Ùæ¨?éo‡J扳?êà¢J*Ì?éÇY°'Õ?éƒÃó?é†c#l?é„ȦJK?ê/ h;?e?êJS[<ý)?ê"µ_Cö?êõm0Þy?ꕉ b<?êÚä‚ö3?êÕUçv±t?ëö"N?ë4 9Óxµ?ëUFÒwÏ?ëò°‡êûì?ëÂsSvvÌ?âõVË,: ?ãf™s,z?ä+’ ¥?ãÊowY?äNÔ¶§{õ?äUU˜&™ð?ä€HÄ}ì0?ä›3v f?ä×,R ¨ç?ä£fš-?äÙÿñGÅ?å=x¨}!Þ?åGW(š\ä?åG@:&J?åD°À°9?å…?'Âl?åêb9\[è?åâ½NB¦z?åÖô'2Ã?å„i°¼[?æV˜Í7Pß?怌UÆòý?æ‹i—»?æ9Éh\?æ]IŸ‘¶«?æ¯â,YEˆ?æìWÊ´£v?æ«’‡æ?æþäûÒ?æ‘õ*æŠ?ç|T¤Í8?çprv:1(?çeâiƒº^?ç-$%úGV?烋^+›?çAâ—P&?ç¬m© gÎ?ç™ö¦‹Šé?ç£ûÁàkj?ç¥Ä þî?çð¼‰Å?çÝÉþ5>À?è&qch¢æ?èOb†<ÈÊ?è|:¦%âÔ?èE0öî?èEéoaKÊ?è?ŠÙb–?én¬£”?èà•= @œ?èà0·ª/?è»» X)l?è´™qc?èËTOrÙ?é(¡ß/S¦?é3­ÁØ[?éCªÞÁØ?éF†ÎŒC?éE Ã47î?餮ðª6÷?éí¤øõÊt?ë,=x-S?ë›íßt3¯?ë˜:¦©V?ë¾×nà a?âÉMaq-õ?ãVˆTœº=?ã8o 2?ãøì„ Äª?ãë]õ¶ž:?ãÇœš ‹Y?äZnñèÒ?äYKºéžP?äVÙÙ®rÖ?ä"¦¤³®?ä±BéN’D?ä‹”þÀõ?ä…,òˆ"?ä±ÂúÅŽ?å|ì9^š?å"ÆéŠ?åÐE6´¡?å&bWIJs?å;½ìKmÓ?åÎɨŸÛ¤?å„ÝFzb?æÀ"æ ³?厔D'þ>?åÝÖ*¥¡)?ætv?ægS¸¢”?æ62?æ=v&ôù}?æXy:[Þ“?æ%-+Ñ…?æL²•mÚH?æïdª«f?æŠÁkÉ«Y?æ™÷È·?æ¡v)ý?æ¦Àª+´?æé'Êš)?çNF§KH?çW{+¼Ôä?ç}5žï/?ç4:w»ÚÒ?çLð“òsî?ç.=‡†UÇ?çf‘æ3?ç ë–?爱}#åƒ?ç–’½l³?çük/¦:¬?çù+Pè×?çƒWÅXfk?çøuŽ}-?縌Ñ&i?èkîÞÉ\?èT±–-ñ?èM…7å?è/ÿRöŒú?è[€võ ã?è?EH«½Ø?ètO´í š?è[ì Žž?è¢`o?èæÉ=›·ó?èàNþ²o?è—Lž}kÊ?èš”ª$?èüNQÜ«?芭هï?éI|*æUª?éhBcÖ3?ét;¾¡a•?éGÒy\§m?éLˆ’«p?éƒ é+,ð?鯘Ý?é圞ór?éÃL€Ú ‰?éÂ=ä¾ò?镆Úôd?é¶f8É“?éÓA·wn¿?êo©%c<ù?ê6iQ ?êlÑ5Q>G?êvôææ?ê%KÑ´Œ¹?êäfÁÝ?êìEžÔž ?êÖäçùqÌ?êù¶øký?êøŽ~s·?êò3ù8ÇX?ëok¯£ô?ë?.ˆQ¯a?ëDÈÉ \?ë<:Ô<ô?ëÙ=:s^\äf?ä*uëù/?ämѰm?ä(/¿Ç?·?ä;s[d Ñ?ä·÷µMeF?äªG^XåQ?䋎Õ*&?äÉaÞ›:Â?ä·~)1?åÇì>÷›?åµ'­ñA?å³à8s!?æ4~$ù–j?æOVã=‚õ?ærëȧbº?æ_vkŒ%?æOŽâ÷ï?没€¨´?æ>‰‚WÂá?æp ÿ%?æÐ+L¡yz?扫6mF?æ…ª®ãП?æ¬,óæÒ?æÏq霟?æÌŠH?æðºaâ µ?æ—-{åI`?ç[µ Áâý?çH‚M1¥-?çj£ÂqŸ×?çHø³Yã?çi±í¥?ç]Xî?ç]¡/?ç6Éc Tÿ?çtÄX V?ç‡t¿Lþ?ç®ðí± 0?çú¤6¯=w?ç×|4*?çŒo«¸Ÿ?èÖ+–]?ç¤"YR?çÎÓëš“‹?çªúć¨?èZ0»ÑÔ§?è(I]û’?èvý¨\„š?è0ñ\Á‚?èk‹2çy?èZ]¿#¤?è!²¹°?èe(;ø?èYgAío?èh8ˆ]EÔ?èÊ‚t§æg?èDۭЛ?è¹± ly?è½ìâJ?è²Vq¬2ø?èÙ!vÚ¦?è’ª_5} ?è©õ¯Ö¡?èˆÉ”è\?éµÈô¤?é@šÉT]6?ém0g-?éy$T{W?é|ÿ9Kc±?éÕ&Ožp?éúa<Á?éoOâô?éùËO h?é­C—že?éŠ×fò©B?éÕ Æ%¢½?éÃÌß }¡?éãwcéµ?é›5?w©s?ênÇ=ö?êaJþß=?êllÏ„?ê$¶3Gà¶?êM¤¥à_?ê^è:iÆ?êDâ¨ÊØ?ê¦. æ?ê´f–Ml?êÃÀhpýµ?êª)þäÛÂ?ê‡åCå¾?êÉh”/U?뽄¬R?ëöݧG±?ë:YTˆ““?ëB¸#í?ë´Ž?묎¢FÉ?ëÿ,Mß?ëàùÃLh+?ë„Ø¡©Fÿ?â &$?âöxðö¢ˆ?âõæ‚“Ñ?ã.j#@Pþ?ã`³/¸‰W?ã@Ф‘g?㻬LùÒo?ãædO/‹k?ã·a쥮/?ãËEspðY?ä<51°ý?äJNºT?äk4p„}­?ä K(M?ä—c{e#?䆘ôxW'?äÂè™î?äžóØœž?äôQp vª?ä¶sQµ?åhÈ£Y?å@¯Ë©?å§ÿÆÅ?å{^8 k¡?åJYtì†å?å_l·ßp?åKwÎðq?åÀw©;¨k?æ'Ä$ëï_?æTu«ˆÂ?æ|ß‚º?æÆ_ë£?æÞn Γm?æßܯ‡”?ç]kªÝb?çv¦|ƒ;?çkZŒaÜÄ?ç ûCºRz?çð¾ýRW‘?çä6ý_¨_?çÅ}îÀ*&?ç„ÑNæ?èl,Ú1|?èv½˜”@?è7på‡l?èþ=µ±¢û?èá >iáª?èý`TÍëì?éZ9IE ?é/ušùµå?é7)šñbí?éëñ ‹œ?éÇi\ìÒ?é‡t+˹ã?ê{BÞˆW?êh©j•?êÈÈ~hÚ?êœKmO±?êŠ!Å­H©?êéP#IBC?êŸÏ™Ÿ%¿?êÇãÃÉlÎ?ê S+Jä:?ë 6LILF?ëaª•ü?ëm \/ ?ët?·Ö¿Ú?ëƒXžªÒ?뾫KãM?ëÛ¡lsË?ëâü<#?ë·f4€?ë¶ýµ«?âï;ÏKÝ?âÌwãd…?âú:uiBá?âéŸå>~?ã ›M $"?ãG«%§[?ã‹&èè?ãŠ&« Vö?ã•7«ÁeÉ?ãÖ~j¤?ãj8iQ?ä&q™uÂÝ?äOB WEÁ?ä-1<"jö?äx;â ‚?äoUE™«ä?äÀÙÐt¢T?åo=ûÈ?äÚ&ÓŒ?å‘“]¹?ä´´Rú“-?äûXD¥?äé¿j̬E?å*¢mM}?å:ÈæüÝ?å/Å5/<Ó?åqy|AAá?åpõÿÌÞ8?åYÏF›‹?åGQq¸Ž¾?åÒ ±·LŠ?åⲕF8?åÈìn•±ë?æP¯JbÇ?æ.ƒöœ5?æ"”“_^ñ?æý±Î@Tà?æžÂÄ÷ñ?æêº0k¥?ç-{Ç£Eï?ça©ni7?ç2ç†H±¥?çB«y9?çnÔt¡Ë?絳ɟZ¡?è20W9Œ?çãA 1É?èQ™¯ŒLè?è.¯`ÆÚ?èLÝj2ÃÀ?èq|S?è:Qm¤äÊ?èºO:'-?èîKCÑtì?è”Ĭ6o?é€Ï~‰ 6?ézù岃?ém‹£bçÄ?é!œ˜E?éø0ø qH?éó]Ћ€?éÀG<8†C?ê.iQ…u?êl~:·u?ê(VŒ\Ê%?ꈎâ?ê®ç˜ÝòÀ?êÚ"HE™ö?êú5ª\?êÈ:Ù,?ê£|wA ?êïlz{mØ?ê¯ag?ë@Äùx 6?ëh$™^†•?ë(0>ׯ+?ë0Z£š”?ëJà)üé4?ë7òîëk ?ë‘̈9ê?ë­‡$À7­?ëÁäáØ· ?ëéÿX@?ëüm_œ?âDu§AµZ?âlr&ìB?â•Â1i‰w?âªbϼã3?âöSû<2Ï?ã(v²‘}?ã-hÙ?ã-¡Í0ÈÕ?ãÍ'Ôï?ã–Õˆò?ãÓÔ x!¨?ãžoêb_4?ã×ÿ¸Ï4Ã?ä}Kì Â?äfi½E?äh4š8F?äZ»æ55?äSøE£ö?äò»þGö¯?å~`œï¾?äáŠÆ„=‘?ä§â›0*õ?ä¨8ù´G?äøFPj=•?äülVÖþ?åVAåæÛ*?å‚^U¢¿?åÎÍ”ö¥?åVÙ²¡Ì9?åIìJ~?å24`·?å j=Øæ?årYX©·C?å,ø¨¥?åŠ_i ’?åèà9È.m?å±ê¹(+u?æ'óù ?æaA!Š~·?æ|t·õ[?æ˜Úã9 I?æÜ€ÿïU?æÝdp>÷°?æó ö-íš?ç">ùÜ??ç,IS"|?ç*gX)j2?çɃÐg ?çÞ¬ø†vÉ?ç¿2 _%>?ç‘¡äD*U?ç­ÌÁÓ…?ènyÁ^û)?è 7GžU?èpeÂÆc?è8Ñ_A x?èÞ©hÙY?èˆä¨}ŸJ?èÇ{ïÒ¢?éšÒí³ï?é‚|9Æâ?éX ™Øø?é$›:ÜåÈ?éó¢(ùït?éȺ:]†s?éžN~SÉâ?é¿t^¼å}?êI™|sÛê?êR±ÑDê·?ê0«°(»?êpا‰ð?æe‰¶)y•?æU§Bdx;?æU–‰0hR?æþzŸÒ}?æ´Gú¨õƒ?æ´V“?çÚªÐQ?æŸ%õ ?ç Òè:í%?ç_üÐhVA?ç&ÔãÔçg?çhÀÂ4Y?ç«( ª²ª?çè` ǽ?ç§úœ²÷?çÇò‡?çÂäîÖ‡?è*c.cq?èca{sQò?èQÚXy©?èU8lw?èÕ󆙞ò?èïxCù'#?è¾°ˆÍ(a?è¨ÿo´jõ?è²`ÑÁ\ð?é]˜ ¬p?é=ÖF$ù?é7W%)?é:¬ så?éñENþ?é¥:-ø–G?éÞx·Ê?ê@‹Û¬?醭¹l?ê$¯Å|Ð?êsV¨x'?êiMÕ·oŠ?êuíŸ°Òø?ê©Âò|;X?êö››)o?ê“ì‰y=“?ê¿\€ ¬æ?êÝ‚…e?êâ}ä@!?êÿtte¨¾?ê¶òõòÂ2?êµ"ä­Îá?ëOËŽM¡?ë9Ð0ÝF?ëVx¥ €D?ëÿQ±¹—?ë9ŽI?ëk=4?ëTŒYå;{?ëÝ-F ?늠“¿Ž?ë©9øP;?ëžaÔqW5?ë°W‰"'‚?â `=žd©?âik!§%G?âÜÛÖÕä{?âó_Ÿáª¦?âºÏà Ä?ãPŠ«O3{?ã kª¼ÇÈ?ãþ\V?ãd½&³äð?ã±PA®ªµ?ãçíˆÚUw?ãŽoÄià?ã–gô¿U×?ã¼±×5ø£?äúnU÷?äB¿+:̸?ääVÙ§?äoª­Ä%?ä#qH:¥¤?äKÆ3*a?äë›eþg??äç‚ný?åËè9?äì 5Oœ»?䘕$[ @?äÅöGí(A?äÎEKÔÕù?䥘YÉ?åUnx««3?åøÝ±–8?å8Ë¡gK?å;â&…s³?å5ŠgIÎ?åzXJɺ-?å)Ðï?å:Jì]Úê?ånÏCYp1?åÚô±Gþ"?åÞw«üŒ?åƒÔýIÿ?ætšèÿ5i?æ ^øœ?æÄÞ+ ö?æ(D{®h?惫 ’ÊÇ?æºkÝKï?æØŽ¸ÙJÿ?æÅLF·U?ç,·éö ?çXs9P¼?çDÝž†?ç]$Û"ô'?ç QNWï?ç´ÃÈô}Ø?çÑQýÏ?ç¤ðï(?çáS =D¶?èŽ\÷k?è}ÓÉe?è±¥¯!?èdA}4?èCÖd7ªo?认62?è³5ý,«Ï?è㧉Îý?賋0\6ˆ?èô9Hþâµ?éCaå[ù>?é7Ë=æE©?é3hPïºW?éŒN(—_?鯩ý?’v?éæ,ÅJ8¶?é‘sRÁÖ?éŽi,é+ù?ê2!M>Hÿ?ê-KÆÜ?ê1pcœžú?êÔlγ?ꇌŠxëÍ?êˆãS ¯?êãZô›?êã>ÂäCZ?êÝ:;Q?ê§Oiш?êïo˜E?ëXÓ¿?êÁÿ€¸Æ4?ëzýO-?ët¿ Va ?ë;H~½?ëGœÃ?ë~,Uâëz?ë;3Ñ2H·?ëì•ë?ë* SF+?ë¤i¦]q?ëlºc»"?ë„Mš·!c?ëÙ6pb¢Û?ë¡ r?ë½^€*Zø?â:M¥á?â`%g ,?âÔ‚Æ.5?âê(sˆ…|?âõ¿^(m²?ãªÙ¸¸å?ãs8ºÏ´×?ãZ©Ê’$?ãN¯ñ‹­v?ãýC©šÜ?ãºWDwp?ã V®¡?ãÁÚ‚sB?ãÇÛlà$?äF1NÅß?äwŒš{€¤?ä]ÖV¢Ö??ä:ŠõE??ä^~#¦ ø?äY&ŽEb?äë›FÓ$?å q»Ì?äÌ@qwêr?äîmâÌN?äôâTàÖ?佋þƒ±?䄽űi?å:IL™ð?å:°Mæ)€?åzÜâ’ù?å2r•-7?å b¨C¼?åÆA5¸?å2|6&g?å^oöIÚq?å>þ×Íå¿?åÓ©y¬«¨?åÝAGdÄ?å§¶]_¦?æQÈ|å8Ô?æqsÂÙ¾¢?æ^Ì3ÃìJ?æuuäVw?ætd9±–‹?泦a–I¹?ë/9’¥Ô?ëYRÙ"8f?ë  )^Ò?ë?„Œõ–½?ë*>ù û³?ëãû7Qíñ?ëüu¸ó0?ë´©8kŸ·?ëÓúP†%s?ë»*†Ti?ëšÚú,ù•?âä Æ’Ã?âb‚ þ‡7?â ¾Pì.?â‹S”ð8?âÍÐÓ%«?ãG]›E|?ã\„È‘X?ãuD²XŸÊ?ãu¼lL?ã·4¨¼ÇÖ?ã Ë;*V?ã†bëÔlP?ãÈkî«D¯?äC?éÈ«û?äǺ㹶?äs¿Í>9?äqXaÜsÙ?änó=¥ãU?äMl Ë^a?äîKäÛ¸?ä¾x黦Ý?ä¿Ó<ÈÛ?äª>#ÕÁÿ?äÀ׃Ãn?䈆»ÿ"?ä§ Ô?årØ \bp?å )Ï Y?å5}E´pˆ?åYÎyÔdi?å`:(v¢P?å1/РÜX?å%ο¹?åvĉîW?å$³ øˆT?å\´• -a?åß5€Í ?åÑÙž¬?åÿ÷4Ä?å·² ¢z?æKê¶k ?æp45û'?æ!£Ls?æ]‘ í H?æ¢Þ8 j?æ¹ô<º?æ˜B’Ýÿ9?çL1€?çIÞjT„?çZ§¶D¸í?çw~ç j?ç~T¦ìz?è $ä¯B?ç¼gàqö\?ç´ÇüÞ¨ð?çœÆÜs`?è|Zõ!*1?èkˆ¦®£Æ?èHžMD7S?è_õKX{?è%êKˆŸ?èØcXLà£?è¯úÂsš?è§í–˜£?è©â:Ìëê?è¿ëï(±d?éx¢6ˆ ?é?SRW¯ˆ?él:«–!f?é¹}ä¼`:?é›"Ôéûª?éÿ.Ž&7)?ê0£ØµÇ?ê"Õ¼ñ½)?ê/çjŽ2?ê‘® Ìɳ?ê‹Ý\9a?êôÇÕ'ê?êžS%¶uô?ê“í¤Ü$2?ê”7Yo%?ê“©¥gŽ?ê—PëãB™?êð3¨ž0è?ëe›ÙIÏ?ëZU¾¢…?ë<‚§¹w?ë_8?%?ë&ˆ?ùŽ?ë{ÈrÑ?ën¨Ñ1^?ë¨K¾™?ëþ¿]2?ëï?1~V?ëÈkæôZ?ëá2߈?ë’r–;ÙI?ëš/ˆûGû?âUxÚžm?âÀÁ©#”?âÚÛ9?)‘?➇„i7Z?ãRùeó \?ã>>j.Ó?ãöãµ\7?ãcØçÙÝV?ãî¥ÛôRb?ãÄOTCÛ?ãËžÉÐô?ãÊ?ÛžáD?äo©Mì-?äè“ i@?äf®×?ä\abe?äÈu8*?äM)´AÒM?äšðí©?äˆfôo?äùu ü9Ô?äø1Úß“Þ?åñ’ˆã?ä‰çñ?䯹en?åJç‡nî?åleX·w?åeå;gŸ"?å åÔ®J?å3ÄHp ?å[«® Å„?åKý®?‡?årà5èÑ??å¡0é?åij/…×å?埆1AQ?æ,vCv4å?æ,DÓ¤†?æ;.а}ß?ægá w6ƒ?æÁ7g4QÁ?æýoãõ×?æÁg¨à?ç ¸Õ'?çm~ $?çCð‚9f?çi9Ï'î ?çhÃl4©?çµ µy…´?çÆ3é˜/?çö´&w;³?è5~2¯‰?èa-˜ßý8?è,ÿ3ê.€?èR–e-·á?è‘ ¤˜X?èúÐçi?èêzF@?èƒOo þ?è¬'ÛlKà?é?èQú?é"R?,†?é^?Ã1v?énWÒsQ;?éÇš—Ve¶?éôX?éšµr4Uë?ê^]?ê!å¹ËŸ?êTŽ+nni?껯Fþò‚?êššFÃôÒ?êë…J y?êÁ߯cä?ê•U‘÷¿?êš³{ðì?êœNR?êõ¢Ž„óš?êî|ƒ‡?ë2Än”‡?ë0•уäã?ë'ÇL®[?ësñ’ôš?ë'pUðž³?ë1xª ?ë¹.m_òË?ëÔÞ>Œç?ë¡¶u•y?ëéð/oh?럈ye˜?ëÞ4ùXkÝ?â‚BR4:æ?âËbõ_o ?â™À} K–?â“ÓU‚š«?ãjàmó?ãb;Póx?ã8D?Ž?ã×z-,ƒï?ã·ß…ÓW?ãô¦šI ?ãÒN$y–?㯈)¯?äk™ë@•O?ä‚v Œó€?äK­à_U?äP¶,01H?äa ]üFÝ?äâm¸Ê—¡?äÒ¿j xŸ?äät<`)?䯠U;=?ä´`ü#_“?äßɧkì?å>øô`>7?å¿nåt¾?åF–v«Ä£?ådh2ðÜú?åy$a¯ðõ?åp´¨j§?åA ÖØIÌ?呉°¢?å¡÷&G?åúwk‚Ï?æƒ+!¾WA?æ/)SB—Ý?æYª6Q^Ñ?æôa”­õ?æàóâCSè?æÖé‹r?æê(«s´=?çr–c¤‰‚?ç˜P [?ç4 oÓÁ!?çW ¯Gö?ç…¥zøù?çÊníx–?çf¹°5õ?èC—’]?è¿1†—?è º!­à?èS“‡l²L?èéèŠõ¶@?èÚ…ÿzã ?èĘè0Áœ?é ©Åxz‘?é‚;á] ?éðœPk°?é…«G?>µ?ê%PÁ×?éýΩªÝ|?ê ÏM«*B?êKiÔRæ*?êaÞ Ÿb(?êÈëÏ.À ?êê,Q÷MÎ?ê—c„'?ê‡Ü1A]Õ?êïùâõû¾?êÕ´ÊB?êÀóϪ¡?êÅOÉ~ŠÇ?ë1ý-?ë ýÁDr?ë,‹dBR_?ë9ž˜†D?ël2tƒO?ëèbwÓ?ëÛ$L³¨V?ëŒbâTˆñ?ëŹÉ9õ¿?ë»EÏe^Å?ë‹íçTtä?â(Î<#Â?âòKAô8V?â–¨]¯/?âQù„b ?ã^Ùôï!&?ãO±ææ/¶?ã!çþê¾m?ãøt6ã1?ãþLy ?ãœkt±©?ä/‹Ù›?ä[µÆd\?ä?bä ?äc˜TÛv?ä`Öº2£×?ä—ÇŸ\y?äA‘00”?äùZ–ê†?äü‘ÞÎPû?äó™ÿ±óp?ä†1¥¢M?åÒÛjë}?åM«<Œ˜/?å*ð&,¾Õ?åE‘žá5?åtºLäÇ¢?å}ˆ×Zñ‰?åûÓü¤Ïâ?åË}…o„?åצ_V…?æE=* ô?æAë |R„?æ \®Þ0 ?ç%•MÍ?ç/éã2?渆㴞?çi?½?ç; +$Ó?ç*í I‹?çÄuÄÜó^?ç§o®ïÀZ?çËéçç½?è€Æé#d»?èº4Yq?è81ZmÒF?èKúœz;³?è¾i@¢ÞP?èŽäÀç÷H?è®-ÂfÑ?èÞáaŒS®?éCfóÓr?éX鯄È{?éf{¬•²X?éÀ©ôÝßá?éáëÃ,¹ ?éÊ3³Ç—?ê;E{Dxê?êµðý?ê¯m~š?êð­¹hgÕ?êéLØ2¡?ê ,’à?êÀUân?êŒÇë“P?ërGð7 •?ëd$݇³ ?ë ôÇ=`?ë$ÿ…£ùå?ëX÷Àµ¹Å?ëh¶¨ä—?ëÝàU³2?ë €?ë™XÆîi’?ë 7bû*?âb¢°g)†?âó‡óv»a?â¥õ ¶?ãWÀËG/ï?ão7ïe'?ãá8Œ?›?ãˆy6™˜Â?ã“¢re{l?ãàX‰ýûë?ä­Í˜mÒ?äRÙ…ž4?ä øˆ¡‰z?ä „ ¥Ó§?ä­àSÛF?äÅ>¢ò1??ä¬ÇÕwƒ?俳«6ýä?äÛ‡5û85?å $|P¹“?åtûÄ{"?å?ëÄ­|Ã?årx…¾Åä?åÚI; ¾?ågzF*?åúw,cÉð?å®ßáT?çèF^?èv,Þ ·÷?èu>uXì?è1ÑN]2?è% v?è@¿;Ól?èã*Z+d?èU«7Eè?è5+R º?è!«NÖuÅ?èWè“ÚÉé?èàÝ… ?èÚù$¨Ø–?è„°æ<@?èª=e>ñ{?è÷Òp, ?蘹L{—Ù?è×@ÕdÞ?èèJÝþ ?èÉñ±î?é;"b?émÓÚ;âñ?é8‡qÎB?é47ùÈ?éOø ?çõ?é(÷³Ý‹ò?éx C”n3?éÂA‹CÖ?é9œÛ"¡?éÎ/¹=&?é»Z *~?éÅüÞPó|?é–Á1´tA?éÈwÞ•°j?é¡6®1]?é¯Ã# Âi?êBfÒ\¸‰?êD©2…{ ?êÚqÏý?êk`·%2?ê$cÂȱ?ê]ä¥Gÿ?ê ,ÝaÒ?êè¡”)I\?êžÎ?Æ<?êË€8ù Í?ê»:l\rG?ëÉ‚ÁæÝ?ê¶V%`ë?ëLv)tÊ?ëMä‡>­þ?ëÄ»|Â?ë~¤9Æ6?ë¶ÕX@ç?ëÛ6ùÍ?ë—A3Ü ö?ëìξk¡B?â호Œ.l?ãRÙzê?ãB[ÒR’?㣯`Þ¯?ãÆœŒùM ?ãØ;|gç?äG‘ñP?ä/¤U³&?ä¿Jº°­?äv7jŽšF?ä»äù¤,n?äÓ°±Â£?äݸÑÏ(?ä…]–›le?å)Eƒ¾DF?å6#I> ?å$§Cw²è?åq€Âjv?åQÆè”Iw?æ9=Þï„?åŠïÖÖ"?åÈÚ‹m­?å¡þ3 ßÙ?åØ7D=F?ætç›þ9¸?æe Ó UÌ?æUrI¢d?æGÿâ0M?æs1xœ¼.?æÖ;xd:?æ1ÀUõ¤’?暀mv?æõãúF¸ ?æÕwŒ÷U?æ„æËÒÂD?æçÅ)ð„?æÖr½Ò~¼?ç~õQÊ‘?ç;¾X%b?çÏ6; ?ç|K»‘8Š?ç^ Êí˜?çC¤‹¬½Ð?ç?tH´Q?ç¨×šq—5?ç’Rº»•?çÌ­å£äß?çò˜ ʽ?çÓY*ò?çúà0B¡I?çþx+>û×?è(mÞ Á?è™Oxü™?è3†_{‡Š?èr[„6÷Å?è1’S+úç?èUª{9MÎ?è0'dµ•Õ?èËI6ef?è¢3'SB)?è¶R ¹b…?èµÙ Z}?è¡0§R¬µ?èˆÌ¦/—Š?èÓ…#ŠR³?èÉt7=°6?ébÇSs¦J?é?§À„ư?é •GŽÒú?écH› ?é&ŸÕ]Ñ?éw&ÄF¶?é|+5l4¨?ê®MŬÈ?éµ \W×?éÿ/fv”Ì?éÕò*–d?éˆÂ¼c$?éÞOÅvº?êKZ¹I?ê"ëê2C?ê $’^Žü?êwèZæ­?ê-m3}?êMuÐ}a?êﵫ¡‰ ?ê¯W‘ø?ê¤1ˆ}·w?ꇩ´K2?êîÖaÏÊ?åþ9xƒŸU?å ÅK;?å¦×ƒŸ´6?æç'u?æ??æ‘1é?æ9ËkQQ?æ{’UxTð?æXæ¥ZÀÆ?æ—³éú|S?戅\¢?æòº ³ª?æà­Ã×±¡?ç<ñ«K?æ§ ofÿ?çEžšK/?çi0ƒˆe?çbŸlåÄ?ç-U#çè?ç0È¿¢‡?ç‹ý|Òê??ç¦õºP[¯?çÚô/p ?çÜÇVtß?èm‘áX¿?è:°v·ïÐ?èôD%i>?èsuê?è>unwI?èíוXh?èÕ\ƒR;—?èþŽƒX+?è˜[Òö‹3?èð"#b¼©?èÖ)£x‚?èî4y ¶?é(sx“?éJ¦Ú»?é&Žp÷´+?éoeÈËpó?é?`´ tO?éknbÝP?é‹ o‰3p?éÆâkÐÞû?éžÅ9?éÑtk?êp‰º39Â?êÃÈÅòJÎ?êÌtS«pÞ?êñh…½/”?êåáTñ÷?ë„Ô€šÔ?늞7È?ë_3¾º>?ëoÂÁvè?ëþË‹56d?ëæÐ\xf?æ8Áç©?ꀒû̧±?ç•ÕÇE?èðå{ê'Å?æ?.?çöV—í?åéßöŠ„?ç±ç™4?çPgβ¿Ã?éÖÚ¥F”!?@4 4ÿ†t¢ †Á¶H€! †¤Ö †Á¶H€! †¼ð †Á¶H€! †Ô †Á¶H€SNODØ”œ 𤠽06 @$7 ! †ì$ †Á¶H€  †? †Á¶Hˆ?åk(_Žš?âÄ'vŒu?â:®WH1 ?âK&—/«î?âD¨¼?â(SÛ!€?â€]¨¢÷?ârºÚÂò ?âd…ßåý?âkìÈ3j?âë~]Å/?â€!ÑÁÕ?âÒ=¥t•?â?ú=ÿÊ?âsÃ+ŒÔV?âUÚþ=Ö?âNÛÕ’ç&?âiAƾ¶?âVßr'?â=ª=öÅ|?â'ªCéí?â8“M—+e?âïÚ$'?âœ%” îp?â‘kçV,U?âòkêJ^x?â—Òî’N»?âáàõr?â£s@út¥?âéCû д?⩌—ò‘¦?âíŠÇç· ?âéQ’«È?â :l-=?⟠OÓºT?â……s9?â‡c6*Ž?âà6(âÆ?âξ;;?âÛ™áy¼?â±lŸ©Àê?âðô˜Dy5?âîhÁ?âÔu½ž`;?â¯~æ$©Í?â•}Ox4ˆ?âÀ‹Éñfˆ?âÿº–²U‘?âýíµ£–?âž\~3¾B?⥟ŠÒ¾?âø…Òéeg?âÁ±’&—?â§/ųZú?â¬ÌTl¥?â¡Ïªü…?⧜Ա3-?ãIÖ{†½?ã²L=?ãrxÁ‰?ãS=€Z\ç?ãr½sn¢?ãDÁ\gé¤?ãE¦l¹À?ã‹ê5!Ê?ãµ Cý ?ãeÄ=§à)?ã_¬´„Z>?ãGc«çPó?ã4söŒÈ•?ãQ˜êÅAÉ?ã&»ûz?ãqpl?ãÃÛå.?ã)âN6¬?ã0œgpݽ?ãQºZA;Þ?ã€[Ã-«?ãxíèõÞt?ã,(“•í?ã"týÖœf?ã$ß‹í ?ãV|ZžFM?ãO`çá–?ã1½-º?ã6' Ò?ã1E;_¦?ãAµö3¼e?ãêýÖ…õ?ãDO0Ïþ?ã!;øg¬?ã6'è-W•?ãºÄ<—ù?ãevà, ?ãf’Ñ8V“?ã)¶3ƒd?ãN3ïRõD?ãjŸ4Mä?ãy|Kë#?ãuÄ&i>•?ãLH·?ã¢:&‹ë?ãN³2ŠQ?ã7Íd1Ð?ã=ÎeŒ?ãäm•á?ã4¾8µY?ã­¿uö?ãÚÊh\$?ãº@ü އ?ãíñ¡ئ?ã…η{§í?ãÈ:t?ãÄ+µÍ/?ãë3˜Pò$?ãê½’†²?ã§î’íæ?ä¥÷„?ãË‚#ŠU?ã‹Û^ŠFá?ãØ­‰?ã«"Gúì?ã„6°†Ê¨?ãD¤»².?ã‹1Í-˜?ãÛZ!»Ø?ãÖ #‹?ã²âˆ?ãö×÷t™?ãÂw6ø" ?ãòÓÙ,¨)?ãÿó3~ôš?ãªZ°?ãØªæf}þ?ãøª)`–?ãÎsª ?ã•–ƒkT¼?ãÒÚÉïÞò?ã¢:CDç\?ãö"n•‘4?äùwvL*?ã¹.E°?ãÜžÖ‚áû?ã“U0åƒ|?äm³Í?K?ãòßË9ßy?ã’LÊ÷ù{?ãÈSì§?ãñó_¬¥?ãÜîç‹?ããŠÖNn|?ãíë½rˆp?ã¹aoH?ã½¹r€)?㤮Œ b?ãôø_¬¡[?äí ˜ö ?ã§£¶³»Ç?ãÆ-4ìÓ²?ãÔÆâ¢Ý0?ãã=]´?㪌€”Â3?ã–O“Éqb?ãÒaÙ2Z?ãÁ߯5p?ãÒqD-…G?ãëÅËPÊu?ã†U ²&?ã‰Cpß?ãÝF{·4û?ã±Þ™­?ä[q½gœ?äJeNŸ}¢2?äÖIƒ8?ä F 8ØE?ä7¢¾vå?ä0›*bCž?äEN5b@?äPí;6|?ä-Æáßû?ä!Mw–•?änŠ<$©?äBˆeuôÚ?äI8˜±?ä|e³h@l?äNM¥Š?äPÜ!KÌ?ä tŽjÞ?äv¿Ã¨ÍX?äsIËäÀ ?ä? Ì®Â>?äžtf'Q?äæÃœïd?ä.trdžR?äP*«p²³?äIÞq‹(?ä,ŸŽç»?äjZe§§?äâœ6ï¦?äF2é‚)?äqvÒ~â?ä4Ä>ø|×?ä5ABW¨¨?侃ö×ä?ä(g³û¨?äIž¹æU?ärf¨úm{?ä8ë+Ñ{=?ä­J A?ä2¼ò½òy?äZ®…Z×?ä[640íc?ä$|ýÄ•?ä9¬ÙÍMþ?ä"¨eXTw?ä‚¶Žº?äP#Üuf?ä §ÜJDf?ä=ÏeáÓ[?ä$[›²b^?ä—#f"/?ä¤yŠqE€?äÝötÕ©?äìùeôÔ?äÉ£0ÁÄ?äþÖxB­?äÄH6AÉN?ä†ý…é`Ô?äñõ#%Å?äþû¡™žµ?䪯.ú„?äÎ%žŠ ?ä®…k€ß]?äé»è9?ä– Q:?äû³1… ù?ääÔx6l?äíÆ‘B¶Ò?äÔŠž?äÊj—Uü?为PI{´?äºOZ V1?ä•Ë´÷2G?å`Ñg½?äž¼¢§f?ä« }ë±¼?ä‘M“ó‘V?䦘ŸvWé?äÓÃz?™N?ä‡,Flp-?äœTàÍØ¦?ä«ôÀ¬‰?åN4–œB?ä« ¡ì>²?ä•!O:O?丌v(:?ä»›·…S?äå]î­O`?ä¬Öý¡"Y?äñJ> Ø?äâ}TêÂÎ?ä‹ Ûýó¦?äç½W\§0?äÔ±øòÞŽ?äõÝA¹ Ä?ä…à|kw?äç1)üqb?å}VXã"2?å'¦¦ hz?åt'¶ûž?åde{F«á?åb$hõK?åYfü'?åjÁØ-8?å‹ß /?å[Cš¬Úç?å#±Y ´??åPvÔ??åEyÂyAt?å uwyJ?å& ™+G?åYX¨Ø?åBú„ Ç?åJw ^4?å*qÄ;¢?å›kŠØd?å,ï0š\Ñ?å$j:î$ì?åL”R¸K‹?ådÄ×ñÚ?å$b^}ÌÝ?åcîœæås?åpû×ó]?å@¼­W5×?åVd:ô€Z?åeTƒ?åbE±ýà ?å?Šb¼û?å…š^üê?åíAÊ<?å(JúªÛ?å(äÁ``?å;T“Äo«?åZ ËuÝ?åk»L¯Å;?åL_질?åƒ?éë?åY‰Ðv‡¿?å~¿ ”uQ?åAŽ_˜(?å{ûÈ$J?åWJÜ.7?åh¦¨ùó>?å<€t²?åëŸÂ?å¿Ò¶r??åPq8èç?åYçzÓt‹?å}$²@jh?åf7á¯N?åS¶œ«Ï?å ×¹åL®?åw\Æ&ðá?å—¿tÍô?å,:ÖâD?åp8d•_?åH-ðá‡?倽ªÕÐ?å`‡ "1~?å+q·òu?å|gH½?åeÞÖ¢Y?åCw bF?åOè;jˆ?åM•ÜÁ§?åY{æ•úž?åToûXvø?åhôqû9?å?F?å#!8ñô?åÍ£±"?å9´8i“µ?å%­¨\S?å?Ô:*Ç?å:žÕÈ?å°¹Aàc?åSg’Ž‹?å[™ +u•?åÄŠuÞ?å S¯Ö?å%Ô:Nßä?åS%¤²C?å©+„Kä?å~Öœ¡;?åML›j?å1‚iFä¼?åÐæŒ÷ ?å<Óÿš?å Kf_¼?åO`ýŠÖ?år¨nd”?åF[í²1“?år‹_›è?åHOËΈ1?åe©˜¿?åi“x0U?åkÍ©ªÊ…?å&i`ÒO?åB†Cz–0?åm@ ]à?åVzb?å ÁÅn(?å^!UŽüò?åebTvd?å•ìÉG?åÌF“ð€?åâ8À€1?匱£é¹?åÔ‹àò¯s?åëc׺û[?åá䮲Α?å˪ùôB’?åéœø¦S?å¶'~(ó?åð)à9 ?åÐI%Ü0=?å–Ðç]y?å‡ p½ö?åÒ–~U›|?åÚømÁµ?å‘§o&?åŒ_øT"?å—Â$Ø‹ò?å¡Èšm?åŒq9-œÂ?åã`uŒß÷?åÈ#–’_G?åö––Þ*z?å³6ÂJoþ?åŠ'íqvæ?åËâ¼1[?å… Fç^”?åóÊUÉ?å§nßí¸¤?å–y»RÊ?åþO8Ì1?åÑ'÷Š2ó?åÛò"&`?å¿açE?åÁŸ$:?å†_cº¬Û?åý•%Wû?åø£¶„Õ?åÙpLwõç?åŸëv;‹‚?åM¬ž„o?勌5Œu?å¤f aµ¬?å›2M<(?å‹|Aæo?å±;UŒ¨?å¸Ê†ø4$?å¡<êŽk?å¤y8V‹?åÞ„¥ÛÇg?åÜ3õµhå?å­>!jü?å™rúx?åÍU•µˆš?åÅ^ÿ´ª?å…Á2Coj?å®óQÀ Ä?åŸ-´?åà°CÓé?åÄ~Š“J?åÁ›Aþ.?åÜüï$ê_?åÊSdÄ)?åÞ)ª.E©?åÉoT±$?åµÇ¿Þ×?åûh+í‘’?å†7GKé?å¥vÈìîÔ?å‹D´è޶?å˜Ñ·à.ñ?å‹v=4?æ3h"3#¡?æY»kŽÓ]?æmF¼óQ?æ ßáš|{?æZÕŠ .?æ&0]&ÿ?æ ê$¶y?æV2]vPž?æ1+rv„Õ?æV\Ûi‡Ÿ?æ€Ïô?æX?æÀ"·ÁÇä?æà0¢ÞÏ?æýK/ñV?æÅâô&ƒ ?çä…Ðe?æ£7i| ?æý‹žT§Ú?ækt”§?æÊü²?15?æ¼d¤P?æõñ¡¢é?æùuޱ„€?æ™ÔäŽ×Ô?æài~òÀ?æÐ§¯p§?æÝP.—?æçÙP¤5P?æ›cC|ÖÝ?æ¾¹MkB?æõ+ ÉÛ©?æ™ØäCÂ?æltÉÍZ?æ¸Á êæ!?æóãÜîש?æ¼5RL†?æ Ÿˆ®ê?æ·-­ô³g?æ“´!zߌ?æôÃú(§?æùãÿán´?æ±Þ ôñ?æ¿®mXsQ?æèÊɈ\é?æúŽa¦œ?æÑgþè£Ê?æ²þd†õ?æîs%l‚?æúüìØv?æë%þ~ª?æ«æëà<?æˆX¦ †Ë?æÒ±Â )u?æêé‹—iS?æõ¾ÕÔ’?æø¾¿Ò*ž?æ «bo´Ÿ?æÓ…‰t?çÓÃkZ?ç=‘ýœ3?ç¤V¢ô?çDæé„\‰?ç5õùh­?祫*âK?ç/‡ÛË[?çc]B€˜J?çi¨²SA?çhM-FO?çg=<îÞ?ç|¶}5޶?çyéW“M?çU…>qÞU?ç;§ìÕˆ?ç6’ø–'¼?ç^«m ´?çq·ä¨dþ?çRÝ èÄ?ç}VÀ·Nn?çGðêx$?ç}Íø®?ç'¸¡º?ç#c£ûœ?ç[êß&´?çg^TrÔ?çwz±B§ˆ?ç@^ÁSê†?ç Ôx”#?ç Pˆhç?ç`o➃?çÒ¿ZXï?ç&:Äßù?ç.îVZý+?ç{«mت±?çKÙbJ³?çN™®?Þ?ç/¬|ÚÙ?炵^ȹÅ?ç.W( î?ç*5uÄè??ç8ùc;?ç30‘%?çx\lIÏ?çI~{ì4?çIUÃ?çKC$›ˆ?ç/;þ»Ò;?çA)MiéÙ?çŸ2X?ç€áŸÖ?ç@ו½“?çUKØ5U'?ç\Ë¡õKK?çE‘]pð?ç<”FÇ‹?ç^E¾?è 8þAÌ?èwíK´Ó?èvzÀ´Å¶?èUÑ”zÊ?èu–®µü?è€3ºÆþ?èÛe’ì4?è Ð&Ít ?èUÂNôð?è‚OKAþ?ènˆ‘-Í6?èQ¨ˆl¶Å?èHݘݕ›?è:ñl_?1?èU»ŒcA¬?èC4k© ?è` ƒ¢?èl¦uáb?è?7TÃ!?èU’7À?èÎ:¨•+?èUèv „é?èv+1â3?èv¢Ïo?èa}”øˆÂ?è\?èp‹¢ŸžŠ?èEÄ’&?èXYˆ;R{?èf~zhŒ?è†>’g?è+ácéê?èXn~ª©?è}èvp?èUà>»¯;?è#s4%Å/?è\™/~Ø?èþ+?‘?è05?S°?èNW€ï6?èd×£êc?èCD{*¬?è˜-VN?èOÚRáË.?èN~_ý2?è|sÔce?èš({êš?è^0™Ö.ê?è‚qêºjà?è&7I®5?ètßžõ`—?è2[rå?èTŠÝ–‰?èTñ”p$Ô?èÌqpàÊ?è¥H2î›–?èî¢Ü¶Ž?蟸#d§?èúÒº"Ú?èÂW‡ü?èà¯eª ã?èá}a'‰¥?è¢Ó4 Æ ?èÒˆhÜŠü?è¦éK?èáf\<†ƒ?èì&€dŠ?è”´$\@‡?è°AÔ?èàO-¥&0?é´0¦7é?èÊeuº?èÔ¾D[°?èïœC«h?èÿ®}‰Ò?è¶ã¡›À?èª µ‹[a?è±y©÷¼Å?èÅdñŸt?è§`ï¼’Q?èªá4ûcÿ?èò)xo‰ ?èÞÚ1·%?èÐmªŸr?è³ Z æ‘?èªùuJ|^?èúÞÑxp?èꈧ&.ò?èªBç=ïù?èæXXŽ'²?èœ1ò š?èí/4ùj‡?èú=&”À?臒áÙøß?èÝd^5]?è›Kš„-œ?èÏKô¸È?è·{uÒÖ?è¡9þÆ»©?èëûnXî?èƒä®Ê./?è„ßæt«?è˜ê¹œÈ³?è’”qæQ?éý‡ì'¯?è·ñØÞݦ?è¬÷ ½ü?èÃHÊ›|?è¸YhÙÔ?éNºJ±?è§¹8ð0#?è®Æë‚Q?讆†`vÑ?èʨUý(?èÔéëx/?è˜Åòæ ù?èêìßKY?èÀc—\ ?è²S9ª)ä?èÛWeêU?èå¶jïÏ!?è£S©Iè?èÍÒþÂ;¿?èÅpYv?è±Ë”3?èž)Ûs•o?èö°·8jL?è¿y ²C0?è¤]°]ý0?è¯J6á0?èü½kÕ†?èŒY˜/Ä?èŽ[Ð:ú?èá:F®Mˆ?è‘4%E/;?è§]7âE#?èî¸ÚsLÄ?èø¸DÙ6‰?è²ÁZû2D?è˦L²™Ö?èȃñXK+?èÎãÖÄ_ ?è¢äÃeµö?èìœìÃ&?蓾Ïöb ?è„#ÊÐ?èÃÂMœÖW?è¬Íèý%?èîh3„rß?é k°¬?è©Fòâ…?è·©0÷ª?éÍÍ$Õ‚?éc—Æ{¤„?é$(éë ?é0 ‘Ô?éåþ*R#?éQn~]?é’ÁîŠ ?éWÙ,A?é"`ét•¼?éZ ôÒ?éB›ÇRbq?éQÛ¶G‰?éEy5îî?éR׋™Ü?é{~\‚?é+)Œ ¥?élú:÷+?éLÙþó?éz}u(X?éÞ"Áw²?énsšüŠ"?é!™»iÙ?é<±-œ±œ?éoÉî~· ?鈮·"?éJ¸ Ôª?évh¨Ì8©?é<þr“!)Ý?éQ…³f£R?éO|òÐÅ»?ég¿‚3×*?él±S"´(?éRUIhÍ?é:C“}_-?éeoðÔþ–?é7ƒûr#n?é5B¥„–?é|ÂüŪ?é íÏ|ƒ3?éGÌ‚?éE¨ñ ‘?é€êM÷îÞ?éLë–š?é(Í+S?étú@û‘õ?é:7œé£?éy‚s ?é!0]©ð?éi EBÕ?é"¥dÓ÷?é/# |c?éx?V´¼x?ék½àR?é5 Nç?é1œ/J?éAÃùj±6?镲²‡¨6?éœd3?éÈþšMŸ?éY¤¼’?麎×tS?é²m{?éÊ_ô¾‚?éÞ„Qî#?é¹ß©²>?éúæãà²:?éªV”þÿe?é§ÞÚñig?é¸Õ¦]£?é™ì I´¤?é’ôN¾ï¡·?éÐ1wdè‹?éÑñyì•Ã?é¾6—FB¥?é±¶7ÆH`?é¹0!Lê?éÍ-s'‘N?éÕµ¤0¾?éô7ÛÚñ?é¼ÉׯõN?é«‹*ÒQ?éîäpŽS?éÄ,!ëÑ ?éÿùOàæ?éûÃV°—?éǶ®¥}Ù?éÓ!š¡?é¸ýºlˆ?éÚJ‡©?éöævÇÚ?éù’ýy6Õ?éòeéOÚ?éä­JvÉ?éÜ']¤2?éÎ~ËÿÏÒ?éœÃâ’Î!?éö¿%sŸç?é¶$Ë\jº?é™ ÜþV ?馰1Þ;?é®Xùáx?é‹w0ù#7?éÀ™Û—ÿ?éÇ.ýí4?é©‘-:47?éõ9&9Ì?é¢tƒ‰¬?é©q,Ð?éÈâRê?éóîï×nD?éª.´¾?éïI ÿ1—?éæÛË®?éØòà1;§?é¾0ñºêü?é‹v'/ûš?éàý å»÷?éñ‹žü‰°?齪`?éÿáe"]J?é‹qèsf1?釒 ÑM?éñ­Íl°?éãé³A‚¹?鳯:#Øz?é°ê;ÚK3?éÍŸ”Ê?ê ·IK?êk7€þ†&?êÁ€_¬«?êÕ­Þ»O?êmP•?‹?êZG“¢\„?êS¢VÛ?êàÛˆ?ê…Z¬ù³5?êòÒ¥¶_:?ê‡Õò™ÿ":?ë:“p#7?ë;G<íñè?ë^¤RS?ëk&Áf?ëiK¾Ú?ë7ùd¹¶k?ëoï„ 3?ëB-è*ù¢?ëZ©–hÌ?ëLœ¢ÚØ?ëG™,þ³À?ëI§¶‚ä?ë)=Z¿k0?ëi°e(??ëbC±?ëH¤ÀkéŸ?ëhtÒÀd?ë€L°Û¨i?ëcçm·å"?ë:yæPS«?ëbüæ¡óC?ëC‘J\Ë?ëŽønÌ!?ë %ö].Ø?ëHÅ&]²å?ëlÂ×N½ô?ëX?ô,K?ë"D?”‘‡?ë@ÜÍ%Ä?ë ð»Å?ëd©Ì€€?ë,)ª‚Û?뀲eó\{?ëšpJ~?ëlv˜bQM?ë] õÔêT?ëo3˜Óè?ëIì]¤?ëZTT9Œ?ëÈä?ëKŸX-|ÿ?ëU/êëÙ?ë?x¿ú&F?ëp áàw?ëh’XZç?ëâiè?ë6ó™š?ëw.n ?ëv\¡s?ëVð¸TE?ë0¡Žœç?ëoJc‡âR?ë|/Â9ù?ëVN¢çu?ë]†bvË?ëx¯ëü?ëk÷é¼?ëx ¸èå„?ë(´&-µ??ë’‰nz?ë0ö™ûz2?ëoh”2«ü?ë!1+ó“?ëK‡à., ?ë/÷tfÿì?ë eEo¥?ëx_³r˜?ëQáUI?ë-&??ë¡óAÕþ?ëÖÊ5 {É?ë‡=ÚF°y?ëêtv ™?ëêŽ 1P©?ëà:6šÃ?ëñeÅ©…8?ëÄb°""|?ëáøæs?ë“ÃPYñX?ë«âNpãd?ëÁŽTüJ?ëæšL^Zk?ë„LD`ú?ë÷[Q5Ê-?ëÌpY*I*?ë‘Õ´ç¶?ëüÆd±Í ?ë˜Ñs˜¨D?ëÒvèÁåÈ?ëßÏ>=$â?ë¾—O=Ñ?ëŽEó¯¤?ëæ¢ë¯ã?ëŸï‡rNè?ë¸è¼à?ëâµZÂÚ?ë©æAñš?ë˜Ó/?ëå–Ä<Åœ?ëá%”RÙf?ë²4qÕwa?ëë[ Í x?ë·½÷PÒw?ëöóCó?ëéB¼ •?ë¹=.·#\?ëÉvªà…?뺯 ãê÷?ëº Iï$n?ëÖibEwµ?ë˜2°û?ë•É<­ê?ëÒoÒz N?놟º n$?ë¥åKÆÞÈ?ë»ÞnÊtÖ?ë³þ"¼ì?ë§½Æ|}?ë•¶±‚?ë‹j <É«?ë§š³SjØ?똱¯A5±?뱟µK³?ë½#ë¨Øü?ë³é{BÈ?ë’ð’™æ?ë‹‘—…7A?ëÒ8÷4`C?ëƒS¥´]š?놖[ëA)?ë˜bö«?ëïUËC&³?ë­àœ¡7Õ?ëýF ?ë„N7S¹d?ëŠ ˆEï?ëšÉx²Ï?ëÇËÎÚ }?ëí„«¼î?ëíÏk-³ˆ?ëúˆ­pŽÍ?å¿î*RÁ?ætNhÚ?è5ô‘Þ=q?è"žL¯$™?è¨9(k…•?ê`Ö,?ê€öÄCØ´?ê§XvH¶?ê‚ 0¶*a?ê€Áæ ûû?¥¾?{?—k?L¨l?L¶¿?LŠ–?Lºæ?LkÛ?L£ö?Lpø?L¯Õ?L€|?Lº™?L‚Œ?LÅž?Lf?L»?Lƒy?L­1?J ?L’?„q?k?J¬?LŽ­?L´‰?Lzg?L¸‡?L€Œ?LИ?LŒ³?Lä5?L¯¿?Lya?LÜ?L ü?Lv]?LÚÍ?L£[?Laó?LØ?L§å?LdP?LàG?L «?LlÈ?LÌR?Lú?L]>?L¹¿?L„?LÓß?L›ê?®¯?Lš˜?L¼À?Ka?¦?LÀŒ?L–Ÿ?LÈl?L„?LÛ?L«d?LuE?LÕ?L™d?Lfç?Lßh?Lª?Ll@?Lî¨?LÊØ?L„Ù?LXW?Lí#?L¶½?Lq[?LK¾?Lâ½?L¼x?Lsù?LLg?LÝâ?L®ð?Lt ?LR2?LÕ*?L¥Y?Lkñ?Lë?LÉŽ?L…4?LRÏ?Lä?L¯y?Lqö?À·?LËQ?L{?LÞÙ?L´?Lqä?LËÌ?LŸ=?©(?L“?d•?L·Å?L…Ú?LÁ4?L‰?LØ“?L¥p?Ljr?Lç0?L %?LjÂ?Lô9?LÏ-?Lžþ?L]è?Lí ?LÆF?L’?LW??Lúû?LÝw?L±?Lu?LBC?M6?L×ó?Lm?Mþ?M¹?Löù?L¿ß?Ls£?L@$?L1K?M™?M ?LÞ)?L¨X?Lnò?LG3?L1\?M§?Mw?L׳?LŒ¾?La/?L9ã?M?MÜ?LÀÆ?LŒ??LQ ?L<¯?Lû?LÍ ?Lp?Le1?LC»?LàÝ?L¹{?Ltþ?LS­?LÛÁ?L£?LrV?›è?L¸€?LzT?LË?LŒR?Lb´?LÔ?L£z?Lg³?LðÒ?LØÞ?LÈ?LW\?LþÇ?LÛ\?L­ù?L†Û?LDÎ?MÞ?L÷)?LÅõ?Lš9?LU?L5-?M ?Lë/?LÃÊ?L™e?L[õ?L3s?M ~?M»?M{?LÓÊ?L~«?Lui?L:L?LA?M^?M%?M)?LÒ’?L¤p?Lq{?L'î?L¸?M?M1ð?M8?M?LyÈ?LjÙ?L' ?LÙ?LÐ?M%á?M3=?M#š?M Š?L¦?L˜ ?LSv?Lr?L…?M å?M4´?M3?M ?LíE?L¿ï?L\?L)?LÄ?Lê?Mj?M0L?M/¸?M Õ?LÈ?Lœ,?LFü?L£?L 7?M¹?M)¶?M ì?L¸É?L–‡?L>?L?Lâ?MT?M¢?Lû_?L­v?LnÜ?LV?L?LN?L—q?LËÔ?L€E?LÝ7?L«‚?Lp€?LîÛ?LÈo?L‚¡?LS?Lü’?LÒ¬?L•?Lcñ?Öì?Lûi?LÓÇ?L˜8?L^þ?LB£?M_?MÏ?LëZ?L·f?L{‹?L8?L4õ?M²?çn?M!£?L0?M3Y?MB‘?KóÇ?M±?M> ?M=Þ?Kåß?MÞ?MDã?MET?Kø?Mm?M??Kú?M-?MCª?L«?Mƒ?M<?Kÿ„?M"f?M. ?Lu?Mö?L)3?M ¼?M ?LÞì?L²Ó?Ly¾?L9ñ?L*Ž?M ‹?LÞì?L¬?Lj?M{®?KÐ?M*?MC»?MsÔ?KÓö?M-8?MIƒ?KàÈ?Kù}?M-¼?M7 ?L¹?LJ?M0Ù?M!?LðT?LŸL?Lté?L6¨?L1(?L(?Mù?M T?LêÆ?L´›?LtŽ?LJß?L*Æ?M?LåÑ?L¡½?L‚=?LB¹?LÌâ?L™.?Læ¥?L´ ?Ls:?Lê°?LÁÃ?LŽà?LWq?Lù.?Lá?L?LW?LCé?Ma?Lݽ?L«Œ?L{û?LG?M ?M!©?M €?LÛ‚?L‰û?Lk!?L/}?L.&?M0Ù?M n?M9?Lâ­?L¨¸?LTg?L*i?Lž?L|?M&\?MMP?K÷n?M4?MYD?Kã"?MAÙ?Mv2?M|´?KžÑ?Kâà?MU¥?M¦=?K²?Kž?M8Ÿ?Mkv?M§æ?K˜d?K¾ç?MTæ?M—Þ?M¯6?KªÄ?M ä?Mi¨?M¥?Kš©?K¿B?MGn?M|,?Mæ?Kâ?M"”?MW?Mk¸?Kè$?Kîá?MCƒ?M@S?LÌ?L¿?M.ç?M$þ?M~?LøÌ?L”Ñ?LRå?LEX?LB?L‰?M?M¥?Lð`?L®E?Lv?LB>?L-é?M M?MØ?LÊh?L‡>?L7u?L¼$?L~i?LÜ,?L¤?Ln”?Líq?LÊ ?L„ò?LZ.?Lüè?LíÈ?L±á?Lm?LMf?Mo?Mž?LÍ?LÑ ?L”Ò?Lp™?L=™?Mª?Lú?Lô ?Lº?L]…?LRq?L+‰?M‘?M!À?M ñ?LÞÞ?LžŒ?L[ˆ?L+}?L6?MW?M7¼?L H?M'a?MO?Kîü?Kÿ{?MEI?M†ç?KÕ;?M%C?MeÆ?M}>?K¹¡?Kâœ?MEm?M|Å?KÂ+?M0(?M\„?M«û?K¶Ë?M%Ñ?M_+?M– ?K°ì?KÞÈ?M3ï?MjH?KÓë?Kñª?M=5?MIU?Kã‰?M?MCÓ?L /?M!(?M,e?M @?Lïµ?LÌ?LPQ?LCù?L+Ï?L#Ï?MÑ?M'?LØ:?Lž×?LTõ?L1?M?Lù÷?Läñ?L8?LY±?LAÂ?L­A?·o?LŸ1?L}Ò?Lâ?L®ƒ?Ltà?L÷?LÖ0?L±w?Leé?LMÈ?Mô?LÓV?L’¨?L_O?L?­?Må?Lðƒ?L¢ ?L{´?LN»?L/?MØ?M f?Lüi?LÁí?Ld…?LD7?L(Í?M§?M* ?L?M8²?MBE?Küà?M1Æ?MT²?Kö_?L_?M>ð?M^ê?KØÓ?Kë?M<²?M_L?KÜå?M=?MS¥?Mh—?KÛj?M1{?M_~?Káè?M26?MMŽ?Küà?M5ª?M9 ?L?MG?M1ç?L{?M ×?M‡?M [?Lã-?LŠ·?LcŠ?L1°?LÎ?Më?LþF?LÔº?L¡˜?LdÍ?L*¨?MQ?LíO?L¨¿?Lx\?LL8?L¾a?µ?L©Q?Ls ?LÖÙ?L?Lcæ?Lë(?L®?Lo!?LûÃ?Lï©?L»Ô?LnW?LPÓ?Mz?Lâz?LÏ?L‚€?L@ ?L@Õ?M Æ?LúÉ?LÓÎ?L™©?LOU?L,Ü?M_?M& ?Ly?M Ì?M5Ý?Lý?M/?M:ø?L?M.?M;$?L8?M*è?MNi?Lí?M0”?MEÂ?Kç×?L W?M({?M?°?Kë÷?L±?M*Ç?M=L?L ?Mí?M)?Lò?M|?ÙP?M}?Lòc?LÓÒ?Lª?L^?L,X?Lý]?Lù.?LÒ™?LŽÙ?L\?LE(?Lñ˜?LÄ ?LxÊ?LTÞ?LŸº?L²?Lul?LÈ—?L‹T?Û?LÀ×?L1?L^n?Lé`?L¸&?L…~?LUw?Lþ+?Lã+?L±É?Lo(?LD³?Mß?Lê2?LÅk?LzD?LXb?L9Á?MÝ?Lîæ?LÙä?L‘ß?Lb°?L:2?M.?MA?M?LÓW?LÜ?LCí?L(N?L n?M$Q?M"œ?Lòó?LãÔ?L´?L_[?L-‡?L";?Më?M2Ã?M)¥?Lÿ?L¨'?Lvß?Lr÷?L°?LS?Mp?M+5?M(£?M¬?LÕ3?L»,?LrÎ?L6š?L è?L ?M" ?M;?M#Z?M^?Læo?L†å?L\Å?L>R?L{?L ã?MÍ?M9b?M?Lë&?L¡£?Lñ?LBp?L'+?L®?M?M&‰?MF?LÕÑ?L¨ ?Lld?L>æ?L#¯?LÊ?M?Mƒ?Lø~?L¾ê?Lcx?L)x?L"?M f?Mß?LíÒ?L·I?Lud?LI6?L-?M4?Lõ!?L¤^?Lzo?LMu?L<>?Lòp?L¼y?L^?LQÌ?Lö?Lб?L´ ?LV?L›Ô?LÏÍ>Ìê|>Ìë1>ÌÓ,>ÌÒ≯ï>̼>̨Û>̪Ô>Ì•l>Ìš…>Ì>Ì„Á>Ìpþ>Ìjó>ÌQÚ?WÚ>ÌFˆ?CF?¹>?¦}>Íø>Í(>Í Î>Í >ÍÜ>Ìõ>Ìï>ÌÔp>Ììô>Ìä>̺?>ÌÔ}>ÌÈ6>̳6>̬,>Ì©>Ì‘>ÌŠ >ÌŽº>Ìmá>Ìh)>Ìté>Ì\w>ÌNâ>Ìc·>ÌLô>ÌM<>Ì:?>Ì9?S2>Ì!#>Ì(ì?I ?¢×>Í0©>ÍGj>Í-„>Í9“>Í>Í74>ÍØ>Í u>Í(G>ÍQ>Ìõ;>Í>Í->ÌÇg>Ìê>Ìë§>ÌÖ³>ÌÀ>ÌÞp>ÌÍÌ>̽à>̽‚≯Ú>̪“>̤K>Ì™l>Ì>Ìœ >Ì‘u>ÌdS>Ìj·>ÌkD>ÌsŒ>ÌX>ÌV8>Ìh–>Ì>í>Ì+m>Ì7É?RT>Ì#Š>Ì»>Ì?U>Ì>Ìž>Ìž>Ì 4?Bp>Ìc?Ç»>Í_„>ÍQß>Í^Ü>ÍG}>Í7“>ÍPÜ>ÍI>;>ÍFö>ÍC>Ìú>Í!>ÍHÉ>ÍÂ>Ìï>ÍÃ>ÍN>ÍË>ÌÙ>ÌíÅ>Í Å>͆>Ìæ´>ÌÆ‘>ÌàÄ>Ìܨ>ÌìÉ>̨á>Ì¥p>̽Æ>̨(>̧ð>Ì–—>Ì‘Ù>Ìw>Ìœq>Ìœ>Ìx‰>ÌvÝ>Ìm >ÌhG>Ìm´>Ìb¬>ÌA‹>ÌBz>ÌEè>Ì]¥>Ì!‰>Ìö>Ì;²>Ì]Ø>Ì%>Ëõ4>ÌÌ>Ì3f>ÌÃ>Ëøg>Ì.Ì>Ëñú>Ëú¡>Ì$»>Ëã‘>Ëù?à>Ío„>ÍPw>Íy¾?¯>>Í>>Íjø>Í*P>Í[1>ÍsW>Í?t>Í!v>ÍVX>Í_>Í&\>Í|>Í<_>ÍMþ>ÍF|>̓>Í×>Í4ˆ>ÍIX>Í5É>Ìþ >Ìò>Íl>Í"Ÿ>Í ë>Í·>Ì»h>Ì×>ÌÛ/>Ìñº>ÌÏ7>Ì×X>̵t>ÌÆ/>ÌÉÙ>ÌÉÀ>Ì©>Ì®Å>Ì™>Ì‚I>Ìf>ÌŸ>Ì€m>Ì”…>Ìd!>ÌM³>ÌH >ÌX^>ÌfG>Ìkc>ÌV>Ì4 >Ì#1>Ì9m>Ì- >Ìbæ>̬>Ëñ+>Ëø>Ì+¿?P>Ì5>ËÖ>Ëí_>Ì¢>ÌK>ËÔú>ËÐ>Ëû–>Ì,>ËÕ÷>ËÏŒ>ÌÂ>Ëæ>Ëͪ>Ì á>ÍpB>Í] >͇ö>ÍRÝ>Í—W>Í€>Í>>ÍŒS>ÍÌ>Í;>ÍM>Í|R>͘º>ÍU>Í*0>Íp{>ÍŠó>Ío>ÍD`>Í“>Íj>͘‘>Í›^>ÍAm>Ìô¤>Í%+>ÍWq>Í_K>ÍNQ>Í"5>Í F>ÌñÂ>Í5o>ÍSŠ>Íh>ÍD>Í4>Ì¿>ÌË2>ÌÞŸ>ÍÕ>Íb>Í P>ÌÓ§>Ìé…>ÌÂÒ>ÌÔŽ>Ì©>̪#>ÌëY>Ì¥>̽1>Ì‘4>Ì„Ó>Ì€w>̇Ê>Ìnù>Ìsî>Ìtù>Ì‚æ>ÌzS>ÌH>Ì2'>ÌGÜ>ÌJR>ÌÌpý>Ì[}>Ì1i>ËüÙ>ÌN>Ì >Ì!>ÌMJ>Ì6ÿ>Ì^>ËÃ=>Ëê>Ë÷l>Ì~>Ì3o>Ëæà>ËÀú>Ëžf>ËÖ2>Ì+>Ëõ>˰Ÿ>Ë–·>ËÙ<>ÌQ>ËØÔ>Ë™!>˳/>Ëó=>ËÖ¥>ˤ>ËÇ…?×^>Íw.>ÍuŒ>Íu >Í™Ë>ÍH—>ÍŸ0>Í´>ÍzÛ>Í`>>ͦô>;¯>ÍzÞ>Í->Í>ÍÃA>Í­O>Íf?>Í8e>Ín>Ͱ>ͪö>Í™}>ÍI…>Í>Í>Í´T>Ͷ$>ͨæ>Íq>Ìé÷>Í,¶>Íz>Í«,>Í©_>ͺ*>ÍŠæ>Í ¬>ÌâJ>ÍZ>Í[>Ís.>Írz>Í|˜>ÍIí>Í1(>ÌÁŸ>Ìê >Íÿ>ÍY>Í,@>Í$_>Í>Í0>ÌÄ6>ÌÀS>ÌÔ>Ì®'>ÌÃù>Í>>Ì¡Ê>Ìè§>Ì´2>ÌÄ+>Ì‘Ô>Ì”Ã>Ìl5>Ì‹b>Ì>%>Ìd8>̆>Ì…4>Ìiµ>Ìk>Ì~P>Ìo>Ì/m>̾>Ìo>ËóÓ>Ì-z>Ì>?>ÌkÎ>ÌkŸ>Ì4e>Ëól>ËÄ>˳.>ÌÄ>Ì%;>Ì-+>Ì%>Ëð>Ë»û>Ë¢ >Ë¿>Ë»o>Ì>Ì?Ú>Ë×>˪$>ˉj>Ë™w>˸>ËüÅ>ÌÝ>Ë“w>Ë…>Ëjp>Ë­Œ>ÌÎ>˼Ü>ËŽ>Ë„„>Ë­©>Ëí>Ë·è>Ëžl>Ë”à>ËÞ]>Í}F>ÍŠ;>Í¡Ž>Íl*>;$>Í™>Ís>ÍÇÆ>ÍŲ>Íw&>Íg‰>ÍÉÅ>ÍÓ~>Í”z?²÷>Í|2>͹>ÍÓÖ>ÍÊ^>Ízª>Í(»>Í£P>;î>Î%>ÍõÍ>Í„j>Í?¬>Íé?ž·>Í6«>Í{>ÌõŽ>Í1Æ>Í9›>̽ï>ÌÔ,>ÌæÝ>Í />Ì›°>̧ü>̾å>ÌÖQ>Ì>Ìw>Ì·>Ìk¨>ÌMÙ>Ì=->ÌYà>Ì.ƒ>Ì' >Ì->Ëøà>Ì4>Ì#l>Ëý·>Ìá>ËŠ>ËmÓ>Ë>¨>ËC>Ë­›>Ì»>˰~>ËY™>ËV¾>Ë|x>˼l>Ëù>ËŠ>ËxU>Ë3>ËÚË>Í>Íx>ÍÇ3>Ío»>Í >ÍÈ+>͉ó>ÍTÏ>ÍÇz>Íäž>Í–»>Í0›>;Z>Íöy>ÍþG>ͺ…>Í1¡>Í’2>Íæ">ÎK›>ÍúO>Í®n>ÍU7>Íj|>Í¿>Íð•>ÎP>ÎÜ>ÍÏþ>Íz/>Íâ>Íg>Íw">Ìà²>Í7ô>Í1•>Ìõ8>ÍMS>Í7·>ÌÃ:>Ìáº>Í>Íz>ÌØ>̼:>Ì¥˜>̰·>Ì”@>Ìí>Ìw>Ì„,>ÌŠ¹>ÌqÝ>ÌC‘>Ì]œ>ÌN;>Ìö>Ëõñ>ÌE§>Ìî>˯>Ëï¿>̰>ËÃø>˱A>ÌŽ>˱>Ë_k>Ë+g>ÊÜ>ËCÈ>Ëc'>Ëí„>ËÎÍ>Ëye>˱>Ë7Ü>Ë_›>ËÒ%>ËÅN>Ëy¦>ËE>ËW±>ˬ%>Í—>Í£=>Í‹*>ÍÄb>Íe>Í›>ÍÝB>Í¿ã>ÍO÷>ÍÅ>ÎÔ>ÍÝ>ÍŒ–>Í9>Íýœ>Î&">Î0ÿ>ÍÕM>ÍF>͵ê>Î>Î*>ÎWø>Î4>Í­›>Í'@>ͯÔ>Íå>΄º>ή<>ÎU$>Î8_>ÍÀþ>Íj¤>Í4[>Í»>ͱï>Ìðt>Í9§>Íh“>ÌäÔ>Í-Ü>Ícá>Í'>Ìæ¸>ÍÏ>Í ó>̬'>Ì«>ÌÒœ>Ììu>Ì¿Õ>Ì€¹>Ì‘>ÌRv>Ì…Q>Ìd™>ÌZË>Ì@‰>ÌqÀ>Ì*ý>Ëʵ>Ì .>Ì1H>Ëæ³>Ë´^>Ìu>Ì>Ë©«>˺ž>Ëô¾>ËÉ>Ëpx>Êøc>ʸ:>Ê”;>ÊÓL>Ë-:>Ë«ú>Ëõ >ËX>ËÐ>Êè>Ëð>ËAÄ>˹D>˵Y>ËVÔ>Ë2¦>Ë"¿>Ë}(>Í”>ͯß>Í~â>ÍÍæ>ͧé>Í„¨>ÍÒ¹>Íã,>Í“Í>Í->Î<>Î-R>ÍÝ“>Íj´>Í’">Î*à>ÎY‚>Î*>ͺ£>ÍÎ>Í“>ÎX>ÎÎgÆ>Î^>ÍèŒ>Íšf>Í]l>Íóµ>Î>Î¥*>Εv>Ϋ'>Îiß>Î G>Í‘±>Í(|>Í)>Íš >Íò>ÍU>Íw>ÌÝO>ÍR¢>ÍaŸ>Í]Ë>Í1î>Ìö>Í=>ÍT¦>ÍÍ>̲{>̪ >Ìå>ÌÆ;>ÌÁ„>Ì• >Ìib>Ìi>ÌoØ>Ì{ÿ>ÌB’>̺>ÌÎ>ÌFy>ÌSE>Ë÷>Ëía>ËÜF>ÌAœ>Ì1>˽>Ë‘Ä>ÌS>ËñÂ>Ëu¥>Ë•¨>Ëëó>ËáÍ>Ël@>ÊÉê>ÊšÉ>ʺ>Ê•Ÿ>ÊÓ¤>ËY‹>ËÏì>ËÍV>ËE0>Ëg>ʧÃ>Ê’º>Ë2W>Ëm>ËØƒ>ËT¡>Êý#>ÊäM>˧ã>Íœ?>Í®É>Í–>Íâ®>Í®>ÍF>ÍÏ}>Îy>͵¿>Íx$>Íâ–>β>Íéå>Ͷ>Ío>ÍÙ>>Î'>Îj7>Îå>ÍŸÈ>ÍPð>Íæ5>Îsß>ίî>Î|>Îa&>ÍÝå>Ín\>Ío¬>ÍÜ>Ι‚>Î×>ÎÀá>Ï)g>Î}ã>Î?Ú>ͺ >Íä>ÍÈ>ÍÏ>Í >Ík >ÍÖÃ>Ícd>Í È>Í>!>Í‹ð>ÍQ×>ÌÐ]>Ìíy>Í4>Ír>Í #>̼Ê>̺X>Ìñ‡>̲œ>Ì ¼>Ì{{>Ì`v>ÌLŒ>Ì|ç>Ì{*>ÌZØ>ËòÍ>Ëöp>Ì@O>Ì?f>Ëøê>Ìz>ÌJ0>Ëá?>Ë¡>Ë>Ìý>Ìê>Ë`>Ë”k>Ëõ¢>ËÐf>Ë~E>Êââ>ʘ?>Ê[È>Ê*›>Êw™>Ë">Ë’ž>Ì%g>Ë£¶>Ë"î>ʳ>ÊÅê>ÊÆ>˹>˨Ÿ>ËÁî>ËUw>Êár>Êöá>Ë.Œ>Ë–Ä>Í–Š>͹Ë>Í–>ÍÖ >ÍÀ?>Ísù>Íð>ÍÿÒ>ͱS>Í`t>Íí¸>ÎI>Σ>Í­k>Íj >ÍÛü>ÎPå>ÎV¯>Î7i>Ͱ>ÍA½>Í¿5>ÎoL>Î…;>ζµ>ÎNõ>Íׯ>ÍsS>Íò>Îx²>ίù>ÎÈW>Îɶ>ÎYU>Î!D>Í´L>ÌôÞ>ÍOõ>Í#>ÌøQ>Íx’>ÍÁÕ>Í….>Í2ú>Ìöi>Í4J>ÍMù>ÌÃå>Ìô5>ÍMg>ÍGY>ÌÛ¦>ÌÊ>ÌÐ>ÌÛ=>̪ö>ÌÂå>Ì‘Ò>Ìv×>ÌQ<>Ì^ü>Ì|^>Ìfh>Ì Ï>Ëü>ÌTø>Ì[n>Ì>ËÓ>Ì ,>Ì8¨>Ëõ†>Ë¡î>Ë’K>ËÜO>ÌE>ËËB>˃F>Ëݾ>Ëå0>Ës5>ÊñQ>ÊlÝ>Ê_]>ÊDX>Êo(>Êü+>Ëp÷>ËΔ>˲º>Ë P>ʳ¨>Ê­ >Ê Ü>Ëð>Ëg–>Ëʘ>Ëtú>ÊÿI>Êìs>ËÚ>Ëw >Í“~>Í«h>ÍÆ>ÍÅß>Í£`>ÍœF>Íë¼>ÍéÜ>Íqá>ͤÌ>ÎL>Íõ">Ͷ>Í;©>ÍÛí>Θ>ÎCo>Î4 >;v>ÍAL>ͽr>Î.e>Îj>΃˜>Î>Ä>Íìø>ÍŽ>Í—)>Íó>Îeö>εV>θ>Îvl>Înë>Íæ\>Ív¨>ÍE}>Í£&>Í–Þ>Í`W>Íÿ>Í£ƒ>ÍŒ¨>Í3Ù>͈>ÍJm>Í;b>ÌÇœ>Ìöî>Í ¹>Íw>Ìàô>Ì (>ÌÀ±>ÌÖB>ÌÝÓ>ÌŒr>Ìv>Ìm>ÌU?>Ìã>Ìc>ÌA/>Ì3¹>Ìî>ÌG˜>Ì3 >Ëûs>Ëû?>ÌH¼>Ëá©>ËÚk>Ì2U>ËÎj>Ës«>Ìô>˵™>ËJ>ÊŽÿ>ÊZ6>Ê4¤>ÊZ>Êö•>Ëxô>Ìî>ËÆ†>ËÔ>ÊÕÆ>ÊÛ>Ê×>Ëa`>˦>ËÂê>Ë&>Ë>Ë%„>ËN>˽n>Í´«?²Ò>ÍȈ>Í«$>Íi>ÍÏþ>ÍÜ >Í—é>͇Ž>Íé‘>Î <>ͪð>ÍA5>Ͱá>Î<>Î>Íö>Í”ò>Íd³>Í̲>ÎÌ>ÎwÕ>ÎKØ>Íð@>Í‹(>Íd>ÍÙŸ>Î;Å>Î`>Îs>Î}Ï>Îø>ͯS>ÍW>Í\B>Íus>͵>Íh§>Í’/>Í)]>ÌýÛ>ÍDH>ÍQÃ>ÌÉ>ÌÞA>̓>Í £>ÌÙÁ>̲û>Ì»³>̪>Ì—ò>Ìyÿ>Ì{¦>Ìnó>Ì…´>Ìfª>Ëö>ÌDO>ÌQJ>Ì[J>Ì ±>ËÔ>Ì ;>Ì9>Ë©Ê>˱Þ>Ì<½>ËÍÕ>ËÓË>Ëù’>ËœÀ>ËO^>ʺ·>ÊÌ>ÊØJ>Ë >Ëfã>Ëâ/>ËÃL>ËN >Êôd>Êí±>Ë”>Ëkù>ÌÐ>Ë® >Ë/½>Ë%Ñ>Ë3°>˱è>Í®7?ÀB>ͬ/>Íž¶>Í¥;>ÍÌp>Í£>ÍHÑ>Í¿¦>ÍÞ®>ÍÆ¨>Íi>Í’ >Î |>Îþ>ÍËò>ÍVË>Í‚Y>Íè$>Î=ž>Îo>ÍÐ,>ÍÔ>ÍDó>Í©>ÍúC>Î9">Î;Ð>Îô>ͱŸ>Í‘>Íqg>ÍE>͈>Í‚å>ÍK/>ÌáÈ>Í>Í)>Ì÷L>ÌË÷>Í#>Í>ÌôÈ>ÌĽ>̹€>ÌÛõ>Ì–Â>Ì“Ò>Ì“9>Ì~Â>Ìj«>Ì5>Ì?¼>ÌfP>Ì>Ì?P>Ì.>˹s>ËéB>Ì,•>˲G>ËÁm>Ì/6>Ëш>Ë@Ë>Ë'¥>˲>Ë& >ËXP>˲Ò>Ë÷p>Ë >Ëy>Ë)V>Ë.>Ë•Ù>Ëݤ>Ëy—>Ë5[>ËB>Ë|U>Í}Æ?¿ô>ÍŸå>Íx>Í£6>Í«8>ÍfŒ>Ížk>ÍÅ>Í¡i>ÍFß>͇>ÍÑ >Ͷç>Í„“>ÍAª>ÍÂ9>ÍØ>Íøa>Í”0>ÍU¾>Ídû>ÍÖû>Ííû>Îë>͸Æ>Ív¬>Í “>Í‘>Ía'>ÍD>ÍN^>ÍU‡>ÌÕå>Ìüá>Í>ÌÖ>Ìæ >ÌæT>Ì«_>̽>Ì·m>Ì‚a>̘4>̃Ÿ>̺>ÌpÊ>Ì`Â>ÌBâ>Ìac>ÌF>ÌC>Ì…>ÌI¥>Ìó>Ì ˆ>ÌŽ?M>Ëã >Ë€­>ËS!>ËI >ËF¾>ËÔ¹>Ìñ>Ëž†>Ëm>Ë4]>Ëš›>Ëüë>ËÃc>ËM@>Ë_e>ˤh>Í~>ÍŽX>Í^³>Í•Ö>Í¥×?½)>Íš«>ͬð>Ímz>Íh$>͵ >ͪ²>Ín1>ÍL·>Í”'>ÍÄ¿>̓>ÍI>Í< >Í”ä>Í©ã>ÍÖÁ>ͤG>Í'>Í&F>Í€i>Í«ß>ÍÇŠ>Í›h>ÍI>Íl>Í<Þ>Ítà>Í›ö>ÍŸ>Í¡>ÍD`>Í'u>Ìé‡>Í2Q>Í:#>ÍP>ÍRH>ÍdJ>Í1~>ÍÁ>ÌÄÔ>Ìä§>Í&R>Í8ê>ÍGà>Í'«>ÍGc>Í(]>ÌÛ¬>̲ >ÌŸŠ>Ì£É>Ì›¥>Ìâä>ÌÞN>Ì¡¨>̰ñ>̹´>Ì®þ>̇™>Ì”Ü>̃Ù>Ì‚>Ìl)>̈Æ>ÌUx>Ìž6>Ì“ó>Ì€¿>Ìpô>ÌG4>ÌY·>Ìf>Ì©>Ì\>Ì$->ÌÌ>ÌMÞ>Ì^ƒ>Ì~>Ì|>Ëâƒ>ËÒÔ>ËÃÿ>˼h>Ì>ÌXñ>ÌJ>ËÜ>˼f>Ë–>˺>ËÈÌ>Ëó¥>Ì7'>ËæÄ>˼*>ËZš>Ë¡>˨H>Ìè>ËóK>Ë•$>ËfI>ËŽÿ>ËÐÌ>Ìß>ËÚ­>ËŠ>Ëj$>˰¹>ÌÂ>Ë´¨>Ë–Ã>ÌØ>ÍwN>Írõ>Ít&>Í>Ž>͈+>Ín­>ÍJp>̓‘>ͺ>ÍHÉ>ÍJ›>ÍV>Í’¼>ÍOˆ>Í#0>Íw>Í…c>Íx>>Í,Ö>Í(->ÍiU>Í…>Íù>ÍLÿ>Ìå>ÍÖ>ÍlÆ>Íp•>ÍR¥>Í&@>Í>Ìû:>ÍR>Í8í>Íiè>Í7K>ÍK>̶×>ÌÐà>Ín>Ìíg>Íÿ>Í I>Ìðy>̱¯>Ì×v>̳Ž>Ì«Z>ÌÂ>̪î>Ì¥>ÌœÕ>Ì™>ÌŠ|>Ì_>̆i>ÌvÃ>Ì>Ì¡>Ì€4>Ì^Ð>ÌFÊ>ÌBx>ÌYÙ>ÌN6>Ìa©>ÌV¬>Ì1>Ì(°>Ëûå>Ëá>Ëü¿>Ì/)>Ì5 >Ì Ö>ËÆð>Ë¿d>Ëç¥>Ì€>Ì=Ž>Ëåö>ËÎÐ>ËÈ\>Ëì¤>Ì+Q>Ë÷©>ˬ¥>˳f>ËÙ²>ÌR>ËÕ:>Ë” >˱>ËüT>ËØ°>ËžÅ>ËØ?ÕŒ>Íeã?¥>Ív>ÍcE>Íd>ÍlA>ÍQ,>ÍF,>Íd^>ÍU>Íî>ÍJs>ÍcQ>ÍQè>Ìþþ>Í1£>ÍGÉ>ÍO`>Í%b>Ì÷ý>Í>>Í6Ó>Í93>Íê>Ìé'>ÍN>Í>Í j>ÍÎ>ÌæJ>ÌÖ”>Ìä²>Ìø¾>Ìïÿ>Ìçy>Ì¿o>ÌÃ*>̶l>̱Ó>Ì’:>ÌŽß>ÌJ>Ìj>Ì‹>Ìyh>Ì{>ÌX¹>Ì`_>Ì1ô>ÌD">Ì^G>ÌgÇ>Ì2®>Ì"ª>Ìd>Ì+Q>ÌUO>Ì<Ú>Ì>Ìh>Ëõú>Ì6)>Ì->Ëåy>ËÒ¿>Ëþ>Ì/¨>Ëîö>Ëãš>ËÖò?I=>ËØ¡>ËÍn>Ìe>ËÙH>Ëó->Íqy>Ëd>ÍG>Ë¥w>ÍÀL>Ìéc>ÍÖ[>ÍY„>Í#|>˯­?ŒÍˆ±>Í—æ>ÍŠ.>͵é>̓>Í­Þ>Íw«>ͬ­>Í”h>ÍÈ>ͤï>Í©y>ͱñ>Í£Ž>Í©¨>Í¡&?ؾ>Í‚·?ß?½k?¿¡>ÍkB>ÍŠï>Íh„>Í—R>Í…e>Íœ>Í©ä>Í~ç>ÍÆx>Í¥>ÍÞ>Í»>Í›ˆ>ͧì>ÍÕS>Íœõ>ͦ>ÍáN>Í©>Í’d>ÍÊQ>Íš>Í«á>ÍÒ$>Í‚L>ͨB>ͦë>ÍyŽ>ÍŸ‚?ÆG>Í…É>Íh’?ÉÉ?£0>ÍQ >ÍvÅ>Íu¢>Íu]>Ír7>Í– >Íoi>Íe>ÍÀ>Í‚ö>Í–Œ>ÍÍt>Í«4>Í`>ͽÝ>Íˈ>Í…•>Í‹K>Íá†>Íç>̓ú>ͤí>Íâ˜>ÍÒ8>Í`>ÍÃ>Íê¼>ÍÖ°>Íu>ÍÀY>Íæ>ÍË>Ícö>Íĺ>ÍÎ >Í’«>ÍŠF>͹$>ͰÏ?ÀY>ͳ®>Í¥ >ÍLI>Í…Ë>͈0>Í\8>Í?º3>Íp}?¶#>ÍbŠ>ÍMh>Ík|>Í_0>Í^~>ÍŽ7>Íiê>Íg‘>Í© >ÍŽ >ÍOç>ͪß>ͼH>ÍT>Í]R>Í˺>Íá†>ͼ”>ÍE‘>ÍØ<>Î Ý>Íß+>Í‚=>Í¡p>ÍãÕ>Î2>Íã>ÍBÄ>ͳT>Î>γ>ÍÇ÷>ÍCª>ÍÐú>Τ>Î M>Íݹ>Í{E>ÍÙŒ>ÎË>Íáå>Íže>ÍŸ>Íöd>Íý'>ͧ1>Í=;>ÍÁÓ>Î >ͺÌ>Í]©>ͤ">ÍÑÊ>Í’>Í=°>Í¡á>Í>ÍY>Í‚N>͆Ö>ÍHR>Íiž>Íb¬?Â>ÍKW>ÍG¦>Í\Ž?°5>Íyƒ>ÍqŒ>Í;N>͉ï>ÍŠM>ÍX®>ÍN¨>ͦ>Í´è>Íi÷>ÍeA>ÍÃ1>Íß[>ͳì>ÍMí>Í£¿>Íöü>Íì>ÍÔR>Ír¥>͇Á>ÍßÀ>Î8>Î2`>ͳ¶>ÍJ”>Íã·>Î,>Î%}>ο>Í¢>Ím€>Íè#>Î=->ÎFà>Î h>Ͷµ>ÍxÐ>ÍÙj>Î`c>Î[¶>Î"¦>Í¥I>Ísâ>Íø9>Î+¥>Î-ñ>Íâ >Í’Ö>ÍÁ>Íô™>΄>Ψ>ÍÔ´>Í8r>Í®L>Íð„>Íñ±>Í·h?¸}>ÍŽx>Íè >Íã*>Íx#>ÍP2>ͬõ>ÍÅÅ>ÍrÔ>Í=G>Í—â>Í”K>ÍK©>ÍiŽ>Í}ª>Í3Ò>Í&ƒ>Í+5>Í5ñ>Í(4>ÍX'>ÍG>Í1Æ>Ím‚>Íu¾>Í'ß>ÍHû>Í€z>Í|R>ÍWÔ>ÍFk>ÍW>Ͱí>Íœï>ÍdD>ÍO*>Í¡‡>ÎÙ>ÍÞý>Í +>Í.O>Í–H>ÍéŒ>Îí>Î*>ÍÈ™>Íh­>Í>ÍÜ…>Î;r>Î`>Î+Â>Íè”>Í >Í›Ï>Α>ÎuÛ>Έ>΃,>ÍýI>Í– >Í{Ù>Íûä>ÎiÞ>΃ >Ή“>ÎZ*>ÍÂæ>Í)ÿ>ÍØ->Î¥>΃<>ΉB>Τc>Íÿo>ͤ¬>Í`}>Íàþ>Î7†>΃£>Îr>ÍøZ>ÍË^>Í7>͹µ>Χ>Î<¤>Î-b>Íï>>Í£¼>Í]U>Í»ƒ>κ>Î*P>Íú>Í–(>Í*¨>Í™Á>ÍØQ>ÍÝ>ÍΟ>Ík÷>ÍM >ͯ>ͲI>Í>Í7þ>Íeš>Í”ž>̓©>ÍJˆ>ÍPµ>ÍqP>Í_Ù?¡Û>Í è>Í©>ÍŸ>Í) >Í >ÍGt>Í9/>Í5¤>Í!â>Íbj>Í[™>Í<*>Í ‹>Ít²>Íz£>Í‘9>Í<‡>Í&P>Í~1>Í­Ú>Ͱ4>Í“®>ÍO8>ÍAŠ>Í´Z>Íñ>Íþ{>Íâå>ÍU>ÍÙ>ÍsU>Íÿû>λ>ÎJ¶>Î.ò>Íü*>Ízj>ÍGc>ÍŠ0>ι>Îgã>Îx>μ>Î>†>Ͱ÷>ÍX>Í–±>Î ¥>Îp‰>Ïô>ÎÎæ>În¡>Î r>Ím¨>Í]í>ÍîA>Îfn>ÎÜ>Ï ¼>Ïp>ÎÅC>Î)±>ÍÃÄ>Íl>ÍŠ„>Íÿ¯>ÎVx>Îëy>ÎÆÓ>ÎÀ>έ>Î4z>ÍÚö>Í/Û>Í·ª>Î,>·r>ÎÃ>ÎÎ\>΢_>Î>Ͱ˜>Íý>Íû>ÍþÍ>ÎS9>Îeš>Î2Ÿ>ÍÒ>>Í)>ÍVn>Ͷm>Î-·>Î*Ô>Î d>ÍüÓ>͇“>Í b>Íuæ>ÍÂË>ÎS>Íét>͵>Í…R>Í9>ÍŸG>Í·™>ÍÃÑ>Í¥'>Í3 >Í[ì>ÍqÉ>Í– >Íh;>Í"®>Í?ú>Íb.>ÍV+>Í2 >Ìù>Íø>Í M>Í _>Í&…>Í ´>Íõ>Í;e>Í+×>Íé>Í >ÍLM>Íeî>Í;d?Ÿ¨>Í,>Íy¡>Íxd>̓>Í5‹>Í>Í`’>Í‹\>ÍÚù>͵>ÍuN>Íÿ>Í(L?¢X>ÍR>ÍPå>Í(ª>ͱÊ>ͼ>Ìý¦>ÍYÛ>Ͳ­>Íœõ>Ìî;>ÍV6>Í¢È>ÍÌ«>Í Á>ÍiÏ>Í“Z>ÍÒ>Íô>Í’·>ͦ>Í€ >Í|è>Í)>ÍzŒ>Íp…>;>ÍDg>Í)>Í‚a>ÍÁ­>ͨ}>ÍÆr>Íhe>Í%O>ÍG5>Í]>ÍvÌ>Íaá>Í%/>Í £>ÍHÓ>ÍX¤>ÍLÏ>Íç>Ìé¶>Ìè>ÌüO>ÌïÊ>ÌõÝ>Ím>Ìüš>Ìä¢>Í>Í">Í =>ÌÙ>>Í%Þ>Í;`>ÍFÿ>ÍU>Ìïð>Í81>ÍH>Í>Íbã>ÍÌþP>ͤ>Í]>ÍxË>ÍÍý>ͯú>ÍxI>Í:ê>ÌüÄ>Í]Â>ÍLî>Ìá¾>Í!C>Í®>Í!>Í…Ã>ÍŽÃ>ÌÞª>ÍHÅ>Í“a>ͯg>ÍW>Í,—>Íý>ÍZ¨>Í>Íj¯>Í®>ÍÍé>ÍNí>Ío>ÍFl>ÍUÇ>Ìûr>Íe>Í}³>Í7*>Ím>Íeh>ÍTß>ÍU>ÍNN>ÍO;>Ìþ‡>Í-µ>Í`š>Í÷>Í™Ø>ͦ`>ÍK>Í!^>Í ö>ÍB >Íh‹>Íuf>ÍMa>Í{>Í>Í3ò>ÍD8>Í,>Íý>ÌÞ„>ÌÚt>ÌÓ¾>Ìãä>Ì×z>ÌêF>Ììö>Ìàt>ÌÊK>ÌîL>Í [>Ììì>Ìì¥>Ì÷>Í ³>Í5A>Íð>Ìýk>ÌÊI>Í q>Í8Ó>ÍH3>Í4 >Í<À>Í’>ÌÒÇ>Í9@>Í?¯>Íkn>Ígã>Ím>>ÍSë>Í4Ô>͹>ÌðT>Í-!>Í>ÌËÅ>Í`>Í< >Ìé‘>Í7_>Íp.>Í&F>Ìü`>Í%8>Í>>ÌÛÎ>ÍL>Í[>ͤm>Í >ͪ>Í >ÍWV>Ía>Ìêü>ÍDz>Í.­>ÌÍ%>Ír>ÍpÝ>Í%Â>Ìä<>Í7?>Í`ò>Íû>Ìêˆ>Í. >Í*R>Íc>Ìù‡>Í'>Í]û>Í‚>Í„.>ÍV€>Í88>Í J>Ì×î>Í>ÍÙ>ÍSx>ÍT&>Í;>Ìæð>Ìå[>Ìòø>Íq>Í/>Ìì>ÌÄ«>̶ï>̼ï>ÌÒ>ÌÏœ>ÌË>ÌÊœ>ÌÝF>Ì»b>Ìï>ÌÎ>ÌÒS>ÌÖ/>ÌÇ„>ÌÒR>Ìä>ÌéÐ>Ìî>Ìã>̾T>ÌÖ˜>ÌÖ7>Ìÿ°>ÍÏ>Í£>Ìõ|>ÌÓ©>ÌÒµ>Í8>Ììê>Í.¯>Í<ù>Í­>Í\>ÌâÀ>ÌÜÓ≯Í>ÍX>Ìæ¦>ÌÉ0>Ìû(>Í+>ÌÕ}>ÍÊ>ÍV>Ìÿo>Ìâ·>Ìßþ>Í ¥>Í')>ÌîØ>ÌÅÛ>ÌüV>Í?)>Í8Ã>Ìïé>ÌÏw>Ìí#>Í*¼>Í%­>Ìdz>Ìä>ÍP>ÍF>Í£>ÌÈ>Í >Í l>Í·>̹Ø>Ìñ~>Í|>Íì>Ìñ>ÌÊ{>Ìÿs>Í7>ÌÓŽ>ÌÜ|>Ìñi>ÍJH>Ìü>Í5q>Í!i>Ìÿª>Ìô¥>Ì×u>ÌÌ£>Ìç‚>͹>ÍW>Ìø˜>Ìçß>ÌÓ‚>ÌÀs>Ìéž>Ìù7>Ìçæ>ÌÜ#>̬±>̯Ô>̰Ý>Ì´˜>̬ >Ì© >̺i>Ì¥ë>̵¸>̪é>̼v>ÌÃó>ÌÀ->̳™>ÌÀz>̬™>Ì¥Ö>ÌËÿ>Ì£ÿ>̳z>Ì·w>ÌÐ>Ì©P>Ì©>ÌÑÐ>̧R>̶¿>̳Ð>Ì´ž>ÌÊU>Ì»é>̳1>̱!>ÌÞ¸>Ìϵ>ÌÜÑ>ÌÂt>̱?>ÌÊP>Ì´§>̤B>Ì´(>ÌÆw>ÌÃ3≯@>Ì·Ž>ÌÆc>ÌÓv>̧w>ÌΡ>̯!>Ì£ô>ÌÂx>Ì´M>ÌÁæ>Ìé+>ÌÜ&>̤l>ÌÄc>ÌØ&>Ìã*>Ì®f>̶¿>Ì }>ÌòÖ>Íh>̹`>Ì´¿>Ìß>̽@>̬.>̺~≯z>Ìé»>̼Ð>̪2>Ì·¡>̲K>Ì®û>̼}>̼ç>̹_>Ìä>ÌÂ%>ÌÌ >Ì®>ÌÅÉ>ÌÀÝ>̱>̵>̼>ÌÈ,>Ì¢Î>ÌÎ->ÌÛ>̬s>Ì®|>̱ >̾Ì>ÌÒ>Ì£«>̶>Ì—®>Ì›n>̉ß>Ì™Ž>ÌŒ>̪>Ì“>̈Š>Ì…I>Ì›¾>Ì“>Ì}1>Ì‘>Ì‹L>̉W>Ì›°>Ìg>Ì“u>Ì„>ÌŽÞ>ÌŠ>Ìxr>Ì“>Ìw'>ÌŠÀ>ÌeÞ>Ìx˜>̃>Ìp™>Ì•_>Ì@ý>Ì•®>Ì`œ>ÌSî>Ìi!>Ì>Ì‘ª>̉‘>Ì~é>Ì•è>ÌoÖ>Ì]¥>ÌyP>Ì‚I>Ì“‘>ÌsØ>Ìšñ>Ì—>Ìw³>ÌdV>Ìxå>̃<>̃2>Ìš>Ì‚?>ÌÔ>Ìsí>Ìnƒ>Ì}¼>Ì…ç>Ìk•>Ì™C>̦>ÌgH>ÌbB>ÌuÕ>Ì”Ž>Ìx/>Ì}F>ÌŽ™>̉Ú>Ìô>̘_>ÌhÎ>ÌoŠ>Ì–ï>Ìz|>Ìg±>ÌŽê>Ì“ª>Ìu¨>ÌM>ÌXî>Ì…>ÌK>ÌŽÎ>Ì}Ð>Ì€Ž>Ì{C>Ì•ÿ>Ì•>Ìh“>ÌpL>ÌMt>ÌŽÂ>Ì}8>ÌZ>ÌŒÊ>Ìi’>̉Ä>ÌÂ>̃œ>Ìz§>Ì‚±>Ìr<>Ì€‡>Ìq(>Ìw>Ìak>Ì^h>Ì…¥>ÌpÈ>Ìjá>Ì\>Ìc>Ì‹­>Ìk…>ÌFs>Ì'E>ÌF >Ì\Ü>Ìmþ>ÌNÞ>ÌK>Ì@Ë>Ì3¿>ÌCa>Ì_Ë>Ì~#>ÌYr>Ì=Å>ÌÔ>Ì W>Ì •>ÌÂ>Ëú²>ÌXÔ>ÌU›>ÌkÂ>Ì=h>Ì)Š>Ìx>Ì`ì>ÌAï>Ì;)>Ì[>Ìm+>Ì#\>Ìb>Ì|ñ>ÌSh>Ì6¿>ËÔˆ>ÌD.>Ìr4>Ì2S>Ìç>Ì‚>̃">ÌVw>Ì%n>Ëò>Ì0Õ>Ìo>Ì­>Ì&>Ëü >Ì>X>Ì_ >ÌA7>ÌA}>Ìv>ÌD’>Ì^À>Ìs¶>ÌVV>ÌCœ>Ìzv>ÌHÝ>ÌR>Ì02>ËÔ¶>Ìq>ÌÖ>ÌŸ>Ì9Ã>̉ >Ìiµ>Ì@H>Ì,þ>ÌNi>Ì >ÌcN>Ìo¾>ÌqÓ>ÌW*>Ì;b>ÌDB>Ì^þ>Ìo¦>Ìq¨?ju>Ì`>Ìdˆ>Ìn)>ÌJª>ÌUñ>Ìdx>Ìaš>ÌJï>Ì1w>ÌFw>Ìd­>ÌHb>Ì<¯>Ì+r>Ì!S>ÌG >ÌOÚ>Ì"L>Ëðô>Ëñ4>Ëì>̇>ÌWp>ÌK>Ëð>Ëß‘>Ëï’>˺Ó>ËÏ >Ëü>Ì ¨>Ìb¼>Ì [>Ì@æ>ÌV0>Ì¥>Ëå¼>Ì™>ÌM”>ËÐþ>ËíÓ>Ìl¬>Ì)ê>ËÈP>Ë¥Î>Ì7<>Ì=‹>ËÔs>ËÝ£>Ì]²>Ì N>ËÄÒ>ËÙ™>ÌeX>Ì >ËÑ·>ËÀÞ>Ëù0>Ì]ñ>Ì>Ëó>Ì">Ì:>ËëI>ËÏŒ>Ìg2>Ì 6>Ì#£>ÌU¥>Ì-ì>Ë÷>Ëïº>Ë—/>ËÅÓ>˹8>Ì2>ÌN>Ì;—>ÌQ>ËÚ•>Ìñ>Ëø­>Ì-]>ÌqO>ÌC>Ì-8>Ì+>Ì-5>ÌJ>ÌS8?dB>ÌG$>ÌTL>ÌA0>Ì,á>Ì?´>ÌYn>Ì+€>Ìm>Ì0¯>ÌU->Ì*»>ËýÜ>ËüZ>Ì@>Ì?>Ì0ð>ËéP>ËÆþ>ËÍù>Ëöˆ>Ì(Š>Ì:N>Ëæñ>ËÁ¦>Ë€%>˼>Ë¡>ËûÙ>ÌXP>Ëþ>Ì x>Ì ¨>ËÂ>Ëû„>Ì>œ>Ëß>Ë»>Ë÷M>Ì A>ËŽŠ>Ë·3>Ëþ>ÌC>Ë™5>Ë–>ÌP2>Ëäå>Ë« >Ëæ>Ì,h>Ë‘>Ëç|>ÌAï>Ëàâ>ÌÀ>Ì>ËÊ‘>Ëþ—>Ì7¸>Ë÷d>Ëþ>Ìdz>Ìù>ËÂÃ>Ëuí>Ë®Å>˪©>ËÍ5>Ì>Ì63>ËïÄ>Ë×B>ËÅŸ>ËúC>ÌÛ>ÌHÅ>Ìò>Ëúõ>Ëþ]>Ì 9>ÌQ–?UÌ>Ì;•>ÌNÏ>Ì0 >Ì>Ì?ã>Ìâ>Ëò€>Ìç>ÌAt>Ëÿ1>Ëìq>ËöÅ>Ìb>Ì1S>Ëà—>Ë«¥>Ëž¾>Ëìó>Ì%»>ÌX>˯Z>Ëž>Ëz,>Ë‘¼>ËÛj>Ì&«>ËÐÉ>Ëäp>ÌÆ>ËÀ»>ËÎ*>Ì,B>ËÖ$>Ë»÷>Ì6>ËÐ>ËÀº>Ì"#>Ëш>˸ï>Ìg>Ëm½>Ëœª>Ëê >Ëÿ?>ˈP>Ëšo>Ëïi>Ì>˽y>Ë¥Ê>Ì;ë>Ëì¯>Ìù>ÌR?R¤>Ìb>˾>Ë–W>Ëjd>Ë‘¼>ËæÌ>Ì9>ÌZ>Ë£z>˳Ô>ËÜÀ>Ì1W>Ì+>ËÞ¹>Ë×È>Ëýí>Ì2Ü>ÌM>Ì9^>Ì>Ì 7?Fi>Ì >Ì >Ì}>Ìw>ËѰ>Ëâû>Ì>Ì>ËÆ>˹Ä>˼ƒ>ÌN>Ìk>ˉe>Ë€q>Ëh¤>Ë·ê>Ìå>ËÞØ>Ë{î>Ë@¹>Ë8ô>Ë]Ã>ËÊ’>Ì"K>ËÄb>Ësó>ÊôË>ÊêH>ËB>Ë€¡>˼>Ëæì>ËW§>Êç­>Ê >Ê¢÷>ÊâÂ>Ë.•>ËŒ[>Ì è>ˤá>Ë Ù>ÊÉÓ>Êp3>ÊQ¹>ʳu>Ë&h>Ëx`>Ì0f>ËØ}>Ë=>ʾ+>ʉ>Êaº>Êj¢>ʵ–>Êé/>Ët˜>Ì&Ï>Ë•ˆ>Ë5³>ÊÄJ>ÊKº>ÊA)>Êw—>Ê®>Ë7%>Ë”|>Ëý¢>Ë™‘>Ë/‡>Êy>ʦ™>ÊPô>Êì.>ËF>ˈ÷>ÌL>Ë‹<>Ë2ä>ÊÐç>ÊÊ>ʧ>Ë\>Ë<Ì>ËÕµ>ËÏi>Ëi½>Ë=O>ÊÜù>Ë&>ËWI>Ëž>Ì s>ËÀ>Ë]…>ËJ>Ë=º>ˉ•>Ëé >Ëî×>Ëœ¬>Ë{ >ËzK>ËÓ>Ì <>Ëò@>ˬõ>Ë >Ëó >Ì0í>Ëì¡>ËÅG>Ì.¤>Ì‹>ÌË>Ëýt>Ì%È>Ëéî>Ëõ‹>Ì+>ËÎ>Ë×O>Ëø2>Ëëí>Ë¡ä>Ë¢ý>Ëõ>Ì·>˱>ËŠ/>ËŽª>Ëä]>ËÄî>Ë…(>ËM>Ëd;>Ëž¶>Ì>˱}>Ë.ò>Ë.#>Ë&>Ëm3>ËåM>ËË}>Ë'$>Êù’>ÊôÅ>Ë~>Ë_¸>Ì>˵>˦>Ê¿>ÊŒè>ÊÚÿ>Ë>þ>ËÅ >ËFu>Ê÷Å>Ê·X>Ê›>Êün>ËdŒ>Ëû]>ËuU>Ë6°>ʺ>ʳá>ÊÜ>Ë4>Ë©V>ËÛ¬>Ëox>ËW>ʰN>ÊÛ>Ë)U>Ël>Ëûà>ËŠÅ>Ë'I>ÊüQ>Ë>ËCÚ>˯}>Ëì¿>ˉ%>Ë:Ò>Ë!2>Ë-ò>Ëžc>ÌÝ>Ë…n>ËKÓ>Ënó>ˈì>Ëî>Ë×Z>ˇ¼>Ëmí>ËŽ >Ëü)>Ëë>Ë”$>ËÇ/>Ëí9>Ëìw>ËÜ">Ëè7?9¥>Ìc?NÖ>Ëâž>Ëú‰>ËêÀ>ËÖl>Ë÷¶>Ëâê>Ë­ª>ËÍ›>Ìó>˧j>ËŽÔ>˨>Ì £>Ëš1>ËUV>Ëxu>ËÄK>Ëò>Ë^">ËK>Ë7]>˵^>ËÑþ>Ë}°>Ëß>Ë%ð>Ë: >ËÏd>ËŒï>Ër>ÊØ)>ËÂ>Ë} >Ëš¿>Ë1”>ÊÜ–>Ë(V>Ìô>Ës>Ë"ã>Êã“>ËŒ>Ëoœ>Ëý>Ëœ…>Ë-o>Êî’>Ë7>ËØ>Ëï8>Ëp->Ë Þ>Ë‘>ËH>ËÑ—>˲0>ËNž>Ë Ä>ËLK>ËÒÖ>˼—>ËdÍ>Ë=¤>Ë­ >Ì·>˳º>Ë™]>Ë«?Hˆ>Ë´>˳>Ëç8>ËÚ7>Ëî’>Íœì>Í•:>Ìc/>ÌE>Ì=T>Ëa>Ë6Ý>ÊÁÕ>ʯ>ÊŒ“BfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfffBfff#°1Ó1Ô1Õ1Ö1×1Ø1Ù1Ú1Û1Ü1Ý1Þ1ß1à1á1â1ã1ä1å1æ1ç1è1é1ê1ë1ì1í1î1ï1ð1ñ1ò1ó1ô1õ1ö1÷1ø1ù1ú1û1ü1ý1þ1ÿ2222222222 2 2 2 2 2222222222222222222 2!2"2#2$2%2&2'2(2)2*2+2,2-2.2/202122232425262728292:2;2<2=2>2?2@2A2B2C2D2E2F2G2H2I2J2K2L2M2N2O2P2Q2R2S2T2U2V2W2X2Y2Z2[2\2]2^2_2`2a2b2c2d2e2f2g2h2i2j2k2l2m2n2o2p2q2r2s2t2u2v2w2x2y2z2{2|2}2~22€22‚2ƒ2„2…2†2‡2ˆ2‰2Š2‹2Œ22Ž222‘2’2“2”2•2–2—2˜2™2š2›2œ22ž2Ÿ2 2¡2¢2£2¤2¥2¦2§2¨2©2ª2«2¬2­2®2¯2°2±2²2³2´2µ2¶2·2¸2¹2º2»2¼2½2¾2¿2À2Á2Â2Ã2Ä2Å2Æ2Ç2È2É2Ê2Ë2Ì2Í2Î2Ï2Ð2Ñ2Ò2Ó2Ô2Õ2Ö2×2Ø2Ù2Ú2Û2Ü2Ý2Þ2ß2à2á2â2ã2ä2å2æ2ç2è2é2ê2ë2ì2í2î2ï2ð2ñ2ò2ó2ô2õ2ö2÷2ø2ù2ú2û2ü2ý2þ2ÿ3333333333 3 3 3 3 3333333333333333333 3!3"3#3$3%3&3'3(3)3*3+3,3-3.3/303132333435363738393:3;3<3=3>3?3@3A3B3C3D3E3F3G3H3I3J3K3L3M3N3O3P3Q3R3S3T3U3V3W3X3Y3Z3[3\3]3^3_3`3b3c3d3e3f3g3h3i3j3k3l3m3n3o3p3q3r3s3t3u3v3w3x3y3z3{3|3}3~33€33‚3ƒ3„3…3‡3ˆ3‰3Š3‹3Œ33Ž333‘3’3“3”3•3–3—3˜3™3š3›3œ33ž3Ÿ3 3¡3¢3£3¤3¥3¦3§3¨3©3ª3«3¬3­3®3¯3°3±3²3³3´3µ3¶3·3¸3¹3º3»3¼3½3¾3¿3À3Á3Â3Ã3Ä3Ë3Ì3Ô3Õ3Ö3Þ3ß3à3á3ê3ë3ì3í3÷3ø3ù4444444444$4%4,4-4.4/404142434445464748494:4;4<4=4>4?4@4A4B4C4D4E4F4G4H4I4J4K4L4M4N4O4P4Q4R4S4T4U4V4W4X4Y4Z4[4\4]4d4e4f4p4q4r4}4~44€44Ž444Ÿ4 4¡4¢4°4±4²4³4Á4Â4Ã4Î4Ï4Ð4Ñ4Ú4Û4Ü4ã4ä4å4æ4ç4è4é4ê4ë4ì4í4î4ï4ð4ñ4ò4ó4ô4õ4ö4÷4ø4ù4ú4û4ü4ý4þ4ÿ5555555555 5 5 5 5 55555555555555"5#5$5051525A5B5C5D5X5Y5Z5[5r5s5t5u5Œ55Ž55£5¤5¥5¦5µ5¶5·5Â5Ã5Ä5Å5Í5Î5Ï5Ð5Ñ5Ò5Ó5Ô5Õ5Ö5×5Ø5Ù5Ú5Û5Ü5Ý5Þ5ß5à5á5â5ã5ä5å5æ5ç5è5é5ê5ë5ì5í5î5ï5ð5ñ5ò5ó5ô5õ5ö5÷5ø5ù5ú5û5ü5ý5þ5ÿ6666666666666"6#6$6%68696:6;6X6Y6Z6[6\6†6‡6ˆ6‰6Š6´6µ6¶6·6Ô6Õ6Ö6×6Ø6ë6ì6í6î6û6ü6ý6þ777 7 7 7 7 777777777777777777 7!7"7#7$7%7&7'7(7)7*7+7,7-7.7/707172737475767778797:7;7<7=7>7?7@7A7B7C7D7E7P7Q7R7a7b7c7d7{7|7}7~77©7ª7«7¬7­88888|8}8~88€8ª8«8¬8­8Å8Æ8Ç8È8Ö8×8Ø8Ù8ã8ä8å8æ8ç8è8é8ê8ë8ì8í8î8ï8ð8ñ8ò8ó8ô8õ8ö8÷8ø8ù8ú8û8ü8ý8þ8ÿ9999999999 9 9 9 9 999999999999999999 9!9"9-9.9/909>9?9@9A9Y9Z9[9\9†9‡9ˆ9‰9Š9ð9ñ9ò9ó:Y:Z:[:\:]:‡:ˆ:‰:Š:‹:¢:£:¤:¥:³:´:µ:¶:À:Á:Â:Ã:Ä:Å:Æ:Ç:È:É:Ê:Ë:Ì:Í:Î:Ï:Ð:Ñ:Ò:Ó:Ô:Õ:Ö:×:Ø:Ù:Ú:Û:Ü:Ý:Þ:ß:à:á:â:ã:ä:å:æ:ç:è:é:ê:ë:ì:í:î:ï:ð:ñ:ò:ó:ô:õ:ö:÷:ø:ù:ú:û:ü:ý:þ;;; ; ;;;;;.;/;0;1;N;O;P;Q;R;|;};~;;©;ª;«;¬;­;Ê;Ë;Ì;Í;á;â;ã;ñ;ò;ó;ý;þ;ÿ<<<<<<<<<< < < < < <<<<<<<<<<<<<<<<<<< > > >>>>>!>">#>$>,>->.>6>7>>>?>@>A>B>C>D>E>F>G>H>I>J>K>L>M>N>O>P>Q>R>S>T>U>V>W>X>Y>Z>[>\>]>^>_>`>a>b>c>d>e>f>g>h>i>j>k>l>m>n>o>p>q>r>s>t>u>v>w>x>y>z>{>|>}>~>>€>>‚>ƒ>„>…>†>‡>ˆ>‰>Š>‹>Œ>>Ž>>>‘>’>“>”>•>–>—>˜>™>š>›>œ>>ž>Ÿ> >¡>¢>£>¤>¥>¦>§>¨>©>ª>«>¬>­>®>¯>°>±>²>³>´>µ>¶>·>¸>¹>º>»>¼>½>¾>¿>À>Á>Â>Ã>Ä>Å>Æ>È>É>Ê>Ë>Ì>Í>Î>Ï>Ð>Ñ>Ò>Ó>Ô>Õ>Ö>×>Ø>Ù>Ú>Û>Ü>Ý>Þ>ß>à>á>â>ã>ä>å>æ>ç>è>é>ê>ë>ì>í>î>ï>ð>ñ>ò>ó>ô>ö>÷>ø>ù>ú>û>ü>ý>þ>ÿ?????????? ? ? ? ? ??????????????????? ?!?"?#?$?%?&?'?(?)?*?+?,?-?.?/?0?1?2?3?4?5?6?7?8?9?:?;?<?=?>???@?A?B?C?D?E?F?G?H?I?J?K?L?M?N?P?Q?R?S?U?V?W?X?Y?Z?[?\?]?^?_?`?a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t?u?v?w?y?z?~@+CrD DÊF{FÀFÈFÊFÙ  †a †Á¶HˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8àØTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌŽ ”ÀÄS@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ l‹ 4‹  !   4“ Á¶H`ÚèPSNOD`d ¼ PT   !   4 Á¶HHÜèP !   4 Á¶H$òèP@çº@#q·@.°K@9wn@DEI@Mçm@VÅË@] Ø@b¢x@ea¾@e}?@cf@^Õ:@Xað@OÓÙ@F`!@;½|@0í´@%Åt@ä’@#X‹@/Â#@<´z@ImÑ@V!'@a¾C@lVK@tÅ@zúÙ@~Z›@~x¥@{ñÏ@vG‘@nG²@dí@X›f@L @?N²@2Xi@%ßô@.šû@<œé@K *@Z‹²@i‘@w{3@‚²@‡9l@‹Z@c@1š@‹@ˆ(À@ƒ8z@z?L@l‹@]®y@N´d@?®€@1E@9,N@I;¸@Zl@kžÉ@}5ˆ@†Ìè@Ž_]@”9@™1É@›·’@›Î@™ç@•¬ø@¾k@ˆjN@€U6@o4@]çô@L¥ª@@a?ï@w@@†žÄ@’-Ù@;ã@§¬×@°YÛ@¶ç@º‚+@º£`@·åo@±åQ@©“@Ÿs@”wC@ˆöO@{‡Å@e¬@QY1@V/¼@k§œ@¶ë@Ž™@› º@§‰@³|²@½q~@Å\@ÉEm@ÉnÍ@ÆA @¿L@µ»Ú@ª!Ü@®@¿D@„?@phÛ@ZLŸ@\Æ€@sæ@†ÇÝ@”"r@¢OÒ@°â@½?ò@ÈTÞ@Ðäo@Õ“‰@Õ¼›@Ò+î@ÊX€@¿²,@²Ðý@¥!)@–ü‘@‰r@xð@ae~@aš@yé@Šv+@˜ 4@§µú@¶i¸@IJ@в\@Ú_@ß^@ßAˆ@Ûfù@ÒàK@ÇdJ@¹r]@ªÎ»@›°†@TF@>³@fy}@d,ˆ@}M@Œl@šþ|@ª•Ç@¹Ðé@Ȥ4@Õ4@ÞËh@äà@äO&@àHL@×bJ@Ëjp@¼í¶@­À¤@ž¨@V÷@A@i$â@d;k@} ß@Œu @› ÿ@ª§à@¹áL@ȸ£@Õ+@Þàß@ä5~@äeÙ@à`í@×} @Ë…d@¼ýF@­Õ‘@ž)_@fž@I;@i=€@aû•@zb @ŠÁ†@˜û‘@¨$F@¶çA@ÅGÑ@ÑLä@Ú°ð@ßÒþ@àã@Ü*@Ó‡Ú@È @¹ð(@«>5@œ¾@¡Û@´™@fà @]]®@t¬@‡@k@”®š@£,@°Ñ:@¾3v@ÉTÕ@Ñüµ@Ö¶~@Öçr@ÓKà@Ë^A@À«x@³ @¥áR@——@‰ô×@yµ²@b‚@W7@l¡_@‚L@޽Á@›àU@¨uP@´™A@¾Ÿ2@ÆkŽ@ʤ‘@ÊÚ?@ÇŸ @À¥@¶à?@«0@ž‰«@‘iÐ@„ÔŸ@qbH@[ö@NZo@beæ@xjâ@‡l‚@“24@žV%@¨þ@±¸«@¸n§@¼7@¼7@¹rï@³N±@ªê¤@ •Â@•@‰Ì @|ù›@f¼ü@RjÔ@DþI@WC@jÆ5@~Ç@‰¬i@“b€@œ™Â@¤Ä@©ÎY@¬â”@­ â@ª°@¥~x@žRE@•gî@‹¼<@~º@náÊ@[@H@:Wî@J„0@\=@mh@T€@‡õã@¼}@•û÷@š»@G_@^@›m/@—¶@‘{@‰œS@hx@qœ@__@Nr@=´È@/¯­@=â€@M%?@\>@k•2@y°>@ƒLl@ˆ…?@ŒuL@ŽT@Ž©‚@|@‰„ž@„~@|„Í@n›Ö@_˜@PcÁ@A@2”[@$â@1Ç@>?@Kv@X<@cáj@n¶@wNW@}¨ý@€‰@€˜“@~— @xÏÄ@p«‹@f7Þ@Zª@MÒ;@@ù@3¹ @'/@Ùž@$¬³@0©@;×@Eö>@OÞ’@XÒ“@_î+@e…@gÙj@gýÿ@eÚx@a+´@Zrï@QàË@H-‘@=nQ@2qÌ@'t@ @#X‘@/Â.@<´}@ImÓ@V!5@a¾A@lVW@tÄð@zúÂ@~Z‚@~x€@{ë@vAô@nK÷@d‡@X›~@L 6@?OŠ@2Y“@%ßè@/w@>‹ñ@M•@@]@l%§@z™”@ƒº¶@‰s@Œù@ Î@2²@’P@Š Q@„ïð@}fƒ@o2ì@`*H@P½'@AŽ_@2ëô@à@fb @k`¸@ƒP@’ð@¢-‰@³ @Ä1g@Ôu3@â¹@íƒ@ó®á@óæ5@ï8ô@åP@×›@ÇœÙ@¶“—@¥•=@•O@†9Õ@q @sn@ˆ’ @˜ ?@ª8á@¼çT@к@âx­@òÄô@ÿ0{A+íAMãA–þ@õ¼û@æD@Óôÿ@ÀÙ@­ý@œm@‹²Ê@yÛì@yw@ŒM@Mè@°+@Ä%·@Øá%@ìó'@þâeAH$A ?ƒA e%AbµA @ðéG@Ýž@Èc@´i@¡ª@œ@€ Ê@|—ª@ŽS…@ŸÜ6@³E@ÈÔ@ݽY@òÆøAÏdA åA>5AaA 2öA…ñ@öè8@â,»@̈ß@·xI@£±)@‘ºÍ@»b@|¨S@Ž]%@ŸãH@³Ss@È.@ÝÔj@òááAÝA !AN°AuwA I@Ľy@Ùú@íÑ@ÿÛwAÙA Ù‹A üÉAõLA“J@ñÆ@Ýéß@É@´›¸@¡g‰@í@€J¬@t)0@‰ -@™.|@ªð„@½È•@Ñ'¯@ã»ç@ô;uAeÌA¸A(üAkE@÷;@ç`‚@Õ«@ÁÁx@®¼ý@œ²‘@Œ,‹@z¡þ@lZÈ@ƒé@@’Ù]@£²@´*@ņ@Öð@ä„d@ïv@õÅ¡@öå@ñ:"@çÿ@Ù6…@É3@·»{@¦Ž±@–@†×ü@r¿@að›@{i>@‹'¤@™©.@¨·1@·Ø²@Æp@Òq™@Û°f@àö @á*é@Ý*N@Ô¥Õ@ÈÑ4@ºàÿ@«á @œ»T@Ž >@€W6@gE@VÃV@mCþ@‚“@A”@œF]@©)‚@µ#ç@¿ià@ÇË@ËWH@Ë}3@È9W@Á8÷@·h^@«¸S@žùÚ@‘ê@…œ@qýS@[rv@J${@^\Ê@s$Ä@„€W@ŒI@šZè@¤Cù@¬¯¥@²ÙJ@¶T±@¶uá@³Ó±@®'1@¦#¬@œ|¤@‘Ï}@†Æ#@w“I@b€M@N°G@=®q@O›@`þu@s–“@ƒ£@Œ·@”Ò@šìý@Ÿàw@¢§L@¢Ä•@ ©a@œ@•¤@ÁÂ@„ñ¾@wh8@d¾t@R¢B@AT(@0»•@@*@OW›@_ ñ@nw@}0¤@…, @Цâ@Žœ@е@å(@;@‹œ‰@†gŠ@€Ž@q•è@b=³@R‡7@C<@4:3@$ƒ%@1~¡@>y×@K­w@Xm @d‡&@o'ò@wûL@~Kö@€æŽ@€ù@Fp@y‚÷@q#O@f×v@Z÷©@NS3@A%%@4¦@'CF@.› @<œþ@K 4@Z‹À@i‘@w€€@‚ @‡8š@‹R@X@1Ï@‹`@ˆ(²@ƒ8Ì@z@@l‹.@]®‡@N¶a@?±@1Da@Ž@°Âi@žU @t@|  @ˆ@’N@¤’ü@¹7@@Ï–°@æðG@ýáÁA >*A<îAàÀA àAÒA #ƒA0™@ëº@ÔW\@½°È@¨¦ˆ@•¡H@„Ê„@†Œq@˜~-@¬Ú4@ÃÅ@Üïý@÷‘7A ÞA/–A§°A$5A$j·A 5ÄAjA ³l@ýƒ@âT~@Èă@±Z,@œsW@ŠÞ@ŠF@.V@²ç'@Ë‘ô@æìŸAAÒ°AjA)(?A/|æA/¹æA*ì A ô:AÇÅA%$@ìÑÒ@ÐúF@·¸s@¡co@õP@Œ1×@Ÿ¶Š@¶1ì@Ï× @ìpA–íA1$A#¨jA/$2A5ñgA63vA1 KA&[˜AR¾AÒ¹@òŸ.@Õ}ú@»4x@¤ó@f@Œ>Ê@ŸÁÖ@¶@¯@ÏëH@ì‰A¨5AGîA#¿A/@)A6ÂA6RNA1%ÉA&rÑAg´Aä@òºˆ@Õ“X@»Aâ@¤9@_@ŠŒ@ŽË@³dŸ@Ì4¢@çÀ¦AždAz\A-A* HA0r A0µØA+ÕA!¾ArÂA³„@íµm@Ѧ_@¸< @¡É@ŽAG@‡ ¤@™#@­—…@Ĺ!@Þ'i@ù·A þNACÕAé+A%ŸA%ËšA!~KAŒ)A «@þ±@ãŸÅ@Éʲ@²®@ }@Šž¯@‚e@’½ä@¥ƒ¸@ºh#@Ñ¿@èË›A ”A GAµ}ArGA {A PA |^AYj@í­p@Õêz@¾ò•@©Ÿ@–g@…dØ@x…@‹~@›ÍÌ@®-¸@ÁÒ—@Ö@é‘c@úí’AAïÓA‡A-ø@þå@ídæ@Ú'T@Å÷á@² 2@Ÿo†@ŽWV@~ V@j™×@‚y@‘2@ ñx@±˜c@Â`ã@Ò^Ç@àV½@êé@ðò8@ñ*8@ì˜q@âà+@Õx @ÅÅ´@µ ‹@¤NÆ@”Fâ@…ak@o•°@[½Ì@rü@†w@“{¯@¡Sˆ@¯ì@»ñ@ÇN@ÏHÚ@Ó÷‘@Ô#|@Мê@É@¾fœ@±Ðw@¤-.@–L @ˆÆ<@x5@`pº@MN@`Úk@vN@†[e@‘½²@œÝP@§Ó@¯Ñu@¶>­@¹ÚÍ@¹þ @·I>@±_@©E@Ÿæ@”c@ˆ®`@zΰ@e)Ì@Pш@> 2@O87@aF @sàc@ƒ?ó@Œ4O@”T@›#N@ X@¢é@£`@ èœ@œXY@•ÛN@ô@…"Y@w³d@e @RÙr@A”j@/æN@>]Ç@Ms¾@\ߟ@lï@zn…@ƒ©U@‰9@ŒßS@@¹@y¯@‰ó@„ÝM@}>_@o8@` ,@P |@Aw@2¼B@9,Z@I;è@Zl @k£»@}9@†Ìû@Ž\½@”Œ@™1Ê@›·ƒ@›Îd@™å6@•ª­@¿Ç@ˆlÊ@€U{@o4“@]è@L¥Ü@ë@¤i¸@šô @y4@…¢@u¨D@`Û¤@MZ@ZÖ@q$·@„óC@’_@Ÿ®·@­#@¹¹;@ć[@̘õ@Ñ'K@ÑP @ÍÜý@ÆtV@¼ è@¯Ôô@¢zÉ@”à£@‡š¸@v@^̇@jÝz@ƒ>>@’@¢¿@²ãº@ÃóŒ@Ô8L@ât:@í=@óa;@ó™…@îò@åm@×cÚ@Çk#@¶hÊ@¥nÏ@•2ã@†k@põ¼@|r @öé@Ÿoø@²¼ @ÇŸ@Üðl@ñÕÈA>A m€A ’ÁA ¹oA —`Aö>@õò—@áTÞ@ËÞ‘@¶ä¿@£@œ@‘cÔ@o„@†%[@˜k–@¬µ”@Ó#@ܳ¨@÷?AÞôAñeAféA#ï«A$#^Að¾A/tA M@üÄç@âb@ÈÄ@±0 @œRQ@ŠÑ@š´@¡ð@¹S@Ó.@ñE8A¯ùA CA(O A4€ÕA;¶£A;ûúA6„~A+,™A\GA Ý@÷µt@Ùi˜@¾8@¦]™@‘ÙS@“‘š@©ö-@×à@árA¾÷A„óA(A:•mAI±ARÂAS½AL4…A>5A,*A€7AzÀ@è¤@ÉYw@®Óé@˜T~@˜¦@¯Ðg@ËaV@ëÚæA±DA©^A3Ù!AIJùAZû¸Ae´{Af vA]õAMiNA8p^A".ÛA Ù<@ó(Æ@Ñ›`@µ@&@šl@²þ¶@ϧ%@ñ¡nA “`A"ÙaA:¡AQГAe%Apá%AqZ~Ahi)AVMÓA?’pA'¬éAø‡@ùE‡@Ö#|@¸e@Ÿ‡)@šv–@³ö@ϸX@ñ¸´A ¨kA"ò·A:Á=AQöÚAeU†AqdAqŠAh“¼AVwDA?³©A'ÆœA­@ùh#@Ö/eA>v‚A8Õ½A-7oAÜA pJ@ù×ú@Û ÿ@¿tˆ@§Rc@’™@†ôQ@™vz@®@ÅH¹@ÞÝý@ùùùA ŠæAëÂA ¥rA&[ËA&‘ÎA"= A7¯A Bm@ÿ§/@äZè@Ê[@²”R@g<@ŠïÚ@~³@@ Ô¬@´yž@ɧ_@ß—µ@õA¥A |ÅAÇáAìnA ®`AÚÍ@ù@@äg@Î)S@¸·!@¤²º@’…$@‚U¶@l¡k@„Y@“a†@£¶\@´òü@ÆoY@×!—@å¾Ý@ðÙ@÷1Ü@÷lõ@ò¡â@èjø@Ú[@Éõ @¸Œ´@§1@–ŸÊ@‡F^@rÕ1@[¼ @sPŸ@†Cí@“³—@¡”ð@¯\@¼F÷@ÇbV@ϱŽ@Ôe@Ôã@Ñ [@Ébµ@¾¿t@²ø@¤ož@–ƒœ@ˆô@xJ÷@`¡š@J‹N@^Í9@sÁ@„Ó@ñ½@šÈ @¤Â @­3´@³pZ@¶ñ‡@·È@´k½@®º¹@¦§@œíŒ@’7*@‡¯@x%*@bï@O"@:¯M@Kz4@\Äï@n§K@€6@ˆÄ@z£@–ðý@›¥¨@žG–@ž^ç@œ`1@˜Š@‘ê@Šnq@ÿe@rZ*@`f®@Nòæ@>4™@CøT@UÌâ@iL„@}¼@ˆ¦Î@’I²@›G=@¢´3@¨D¨@«T°@«xñ@©!Ž@¤Ž@œö'@”E¢@бy@€¡@m[ò@Y¹p@G`J@U’¤@kÊe@§z@Ž'~@šöÏ@§ž @³^@½nÈ@Äç¢@É"£@ÉM¶@Æ3@¿5ü@µ›<@ªF@˜˜@ÆE@„-@pkç@Z-@i&5@•ê@þê@Ÿ@¯ã×@ÀZÒ@ÐÒ@ݨ|@çýñ@íäÅ@îÛ@éž*@à¨@Ój@ð”@³Lø@¢ß‹@“ @„nò@n ‡@|t@ø@Ÿq>@²¼I@Ç{Ò@Üíê@ñÖ°AAçA o$A “âA ¹žA •QAòÊ@õôç@áU~@Ëßü@¶äÓ@£AS@‘`@oW@ˆD@šžW@¯Ÿg@ÇL@áp@ý6¨A ‡“AIA#YžA);IA)qXA$ú½A©;AUßA‚v@ç E@Ìy%@´:€@ž²@‹Ûu@‘¤é@§ª@¿ØR@ÜyÆ@üç°A1øA"ˆÝA3Ö AA×âAJ2åAJ‚XAD&çA7&A&JLAø¾Aþ~@âÏê@Å\/@«ÕK@–ø@šŒÏ@²–ó@Ï-v@ðý€A &•A"MËA9æÏAPÜæAcù£Ao¥ÔApÖAg2¸AUVŸA>ÐAA'9A‚U@ø˜@Õ¦@¸Å@Ÿ/ð@¡·Õ@¼d_@Üz±A–„Aš±A3:ªAPtMAmÅAƒoaA‹?öA‹‹\A…–hAs˜êAV¢•A9‡AÈ=A%@ãÑ•@Âp¹@¦í¥@§%À@Øk@æjdA‚ÞA"PþA@¼mAb÷HAƒ€A’…TAœI„Aœ®šA•4(A†£oAjZÄAGˆA(,TA jï@îqc@Ê&Y@¬H@©ûä@ÇŠ @ëé™A cÚA'ÍûAH“´AmߦAŠoUA›Á6A¦Ò‚A§DkAžÈAŽa)AuÿéAOé¼A.ÄAÕ@ôQ;@ÎYª@¯œ@ª@Ç™/@ì6A tßA'çñAH·1AndAŠ‘>A›ð2A§IA§{‘Ažû™AކAv5AP£A./áA£È@ôm=@Îk@¯²å@§’Ò@Ä/å@ç;ñA ›A#¦AAé\Ad‘[A„,ÄA“áAÒ«Až6ìA–š5A‡ÂòAltAHÈæA) ‘A@ïOª@ÊÁ³@­“@¢z=@½G@ݰÚAl;AÃrA4ÔÙAR¥õAp§A…/sA5½A‡wA‡eqAv—£AX÷ßA:ÍA©Aâã@å,@Ãgá@§§œ@›au@³±/@Ъ;@óÉA ƒüA$#³A@ܹ›@÷ADAÜŸAíºAfA#ï4A$)ìAñÏA*úA ~ò@üÅ÷@âR@È‘Ê@±.®@œQ+@Š'@‘¥Z@§O@¿Ó¯@Üvð@üîëA2šA"ˆ¼A3Ö,AAØ„AJ4 AJƒ{AD(uA7 !A&IëAúqAþå@âЊ@Å\E@«ÐÚ@•þŸ@œZn@µÅ@Ó`?@ö©œAoA'vÕA@½!AY‡ÊAnUÊA{ZA{œqAqÞÃA^\AFŸA,–Aš–@þ¡(@Ú(@»Qº@¡¼!@¦¬è@Ãv,@æ)ARÃA"hA@^YAb€ŒA‚Ã*A’0A›ÔØAœ3ÌA”Ê{A†JHAiÓYAG"ºA'ãA 3Á@î&z@Éì|@¬wÃ@®ú@ÏBH@ö³¹AGA2çAXŸ°A‚r²Aš=RA° µA¾_wA¾é­A³õþAŸ-mA‡V A—!Aɾ@Ü:r@¼àW@¢ã¤@’©ê@¨¡<@ÁÔ¿@ßA9EAyVA%m;A7`HAEôCAN­—ANþÀAH]·A:Ì”A)N´A`hAØI@åŸØ@Ç{^@­k™@—/&@‡K´@™íH@® ê@Æ Ž@ßÕ@û0WA GCAɉA!§°A'g–A'&A#>ÖA AéAs²@å^+@Ë' @³3g@ã@‹Po@xÌT@‹£ @œ…_@¯l@ÂêJ@×U¹@ë$@ü¹íA!"A [A $‚A2b@ÿðŒ@ïa@Û„ƒ@Ç@³w@ 0M@Žï@~ðÔ@bîä@|—@‹ë*@š\@©Ò@¹<@Ç”@Ô Ð@Ý…W@âß›@ã§@ß¹@ÖX@ÊZ¾@¼3ð@¬ÿ$@©ø@ŽÖ#@€õ™@h˜Ê@OCx@d›@yù«@ˆ“ì@”Xp@Ÿé0@ª”Ø@³¨o@ºX_@¾|@¾BÑ@»d¡@µ@ÿ@¬‘h@¢3J@–½s@‹œ@~¢I@hh4@Sõ@V/¶@k§¤@¶ü@ޤ@› ®@§‰3@³{ï@½wú@ž@ÉB@És-@ÆBÜ@¿Jî@µ»˜@ª!¬@®Y@ÁA@„;%@pcÛ@ZL£@k`º@ƒP0@’@¢-|@³ @Ä*æ@Ôr†@â½@í€@ó®"@óí“@ï;@åI*@ךO@Ç¥@¶”q@¥•ç@•Nv@†8º@q•@‡ù@’à@¤’2@¹6÷@ÏŸ¬@æì£@ýÙëA =ÁA<¼AáÂAOA‡±A "'A/¼@ë¼®@ÔY@½²¨@¨¢ö@• õ@„ÅÍ@š¨@¡ñ@¹œ@Ó'@ñIÕA±uA ãA(LA4}A;¶èA<zA6‚¶A++=AZ+A »@÷»R@Ùm¸@¾3µ@¦] @‘Ø"@šŒ›@²–z@Ï.’@ñ8A ) A"I A9áÃAPÞÖAdÇAo¥Ap@Ag8©AUR“A>ɹA'ÛA‚ý@ø°@Õ¨Ö@¸ý@Ÿ-f@¦©h@Ãr…@æ)ASæA" _A@]þAb}ªA‚Ç?A’ÃA›ÏÊAœ3eA”Ç¡A†HüAiÖÛAG"¿A'à”A 5Ç@î,@Éók@¬uÓ@²†@Ó\"@ümA`8A9 ŠA,ÝA€sAzN@è ?@ÉZ}@®Ó”@˜T9@¡·Ï@¼e@Üz(A–‰A›NA38APs/AmÆ_A° 5A¾V{A¾çÌA³ö~AŸ.¹A‡T¨Aa:³A: OA×y@ÿäÖ@Ö@µšù@¼À@á@qAUþA'6ZAO¦XA‚#ïA£ZÙAÊÎTAò 5Bå B+èAùc9AÓ5eA«6êAˆR?AYBëA.•HA øÇ@éíÉ@Ã9¸@Æ¥@ñ[ABA9?”Al0pA™aˆAÉáàBèÔB(ÛÔBDþ BG§³B/ë€B :}AÖ iA¢˜AxªÔABNÒA¾l@úü @Ï €@Îúù@ý A5‰AG¾ÃA‚!tA®UÃAïšßB'ÇŽBe€YBŽg‹B‘0BrÝ[B3’ÁBŠøA¹zýA‰¸™ARDÙA$¶ Aøœ@×¾˜@ÓIÿAÔ•A"^AP&„A‰Y`A»™B¨qBAò÷BŒµ„B¸™.B½NÛB–Þ0BQë>B[QAÈqA‘Î@ä¸ÔAë/AZ;A+©A?ANé;AXsBAXÓAQ–"ABÄÕA/ÓA…AÇÇ@ëœë@Ëöï@°ÊÂ@™Ñy@‡ÅÝ@š=Ö@¯{@Æœ‘@à‘1@ü#ÞA Ø(As&A"`ùA(2FA(l/A$AÌLA–ÂAì—@æ!â@ËÉ@³¯‰@žA@‹„ä@ué<@ŠÝ@š‘Ä@¬ž@¿äL@Ó¨¸@æÄš@÷¨ŸAMLA¨A)·AYB@úÇî@ê{í@׳÷@Ãò\@°×@ž#T@M]@|x—@_ º@wé@ˆø@–m@¤æÿ@³SK@ÀÑY@̃‚@Õ<”@Ú5@ÚdÌ@Ö¤@Îf@Ãeœ@¶6Î@§ßË@™nÖ@‹U@|Ax@cÓâ@ašK@yèÃ@ŠwÛ@˜¡Ô@§¶@¶k»@İ“@Ч›@Ùøã@ßž@ßFw@Ûh@Òàå@Çd…@¹sZ@ªÌM@›­Ý@T @>Ù@fy˜@yyÀ@ŒN`@Ož@°@Ä%²@ØäÛ@ìòÝ@þÚ‚AI¦A @WA d:Ac\Aâ@ðá°@Ý!4@Èe-@´F@¡ý@›@€ ñ@ŠB«@/@²ç]@Ë‘Ê@æòLA†AÒZAg£A)*šA/„$A/ºA*ëjA ñ¬AÆKA(ˆ@ìÙg@ÐûÔ@··é@¡aÎ@ò3@˜A@¯Ìk@Ë`Î@ëá¬A´AªJA3Ù^AIGuA[AeµàAf²A]øAMk×A8p A"/A Ö0@ó'@Ñp@µ@œü±@§%»@Ù°@æm–A…SA"O¤A@¼GAb÷EAƒëA’Š@Å´¸@éQ2A ŒoA%-áADÎ Ah A†ÖA—)FA¡æA¡ö#AšŒAŠ™ ApQÝAKçôA+DA™ð@ñ‹u@Ì[ @®(€@™¢@±Ç@Î K@ïr¹A ÂA ß)A8AN€{Aa(5Alx{AlåãAdBNARÚ[A<Ñ4A%“ÅAe#@öúy@Ôvn@·! @ž’•@‹ªà@Ÿò@µFû@Î¥a@êåXAêAïùA"àA-eÒA4 A4O­A/@ßA$ÈAŠAÆ;@ðü@Ô@r@º9Î@£O´@p3@|&h@í/@Ÿ`×@²§y@Ça#@ÜΘ@ñ°ªA$ÅA N A pA —uA t®AØ@õä@á0@@ËÂ@¶Ï?@£/@‘R7@m¯@d‡@}6U@ŒKo@› ›@ªcƒ@¹Ô‚@ÈXs@ÕÁ@Þy·@ãéJ@ä•@ßþ@×KP@Ë%ß@¼ï&@­“¾@ž,Q@=C@P[@i _@d,d@}@Œj"@›‚@ª•‘@¹ÎU@È¢|@Õ­@ÞÌy@ä&©@äP;@àLé@×`B@Ëiä@¼ê„@­½è@žÝ@\H@B1@i$à@|—š@ŽRO@Ÿ×Ž@³E‡@Èì@ݽ?@òÈêAÌ®A QA:ÖAa1A 3AŠ/@öç@â.®@̉Ó@·sY@£®L@‘¹Ð@»€@Œ2Î@Ÿ¸¸@¶1á@Ïמ@ìq0A˜KA0ùA#¥ÀA/(WA5ð´A62'A1 A&^åATÊAÕ)@ò ™@Õ{„@»/ÿ@¤@@šp@²ý@Ϥö@ñœÂA ’­A"ÚA:¦…AQÏAe ApåDAqY-Ah_'AVO‘A?˜ A'°(Aõm@ùA„@Ö"£@¸c‰@Ÿ…À@©ûû@ÇÝ@ëì¶A bîA'ÎAH‹(AmäAŠlxA›»æA¦ÕÐA§AèAžÍAŽ`ÄAuû5AOñºA.+A@ôR\@ÎX?@¯š@¸³­@ÜÙšA|€A'›A3µùA:ÕLA;¸A5©·A*s+AÇÇA šâ@öþJ@ØÝF@½Éi@¦ u@‘‹h@T6@@¡ý$@µïþ@Ëy‚@áÓï@÷¸3A¨AA 9ŒA—ÉAÁOAnÖArp@ü»@æq@Ð >@º>£@¥ío@“|D@ƒ#„@f“X@€@b@ŽO«@…{@­a¿@½iª@Ì}²@Ù³‰@ãš¾@éHL@é}Û@å1Ž@Ü®@Ïg@Àž–@°­Š@ ½Ã@‘W$@ƒƒ@kͦ@d;Q@} t@Œt¶@› N@ª§´@¹ÝÎ@ȼÉ@Õ-+@ÞáD@ä7‡@äf€@àd^@×tò@˃ï@¼ú²@­ÕC@ž)U@hC@Il@i=€@|¥•@Žbr@ŸãC@³TÌ@È. @ÝÓ‘@òâÆAÜ(A ­AO AsÝA DËA•6@÷ø@âCÒ@Ì¢d@·ƒ,@£·ã@‘ÃD@ÂJ@Œ>¦@ŸÃ @¶@¶@ÏíÒ@ìŽA§îAEA#½}A/B·A6DA6PˆA1%ÇA&sgAgµAçþ@ò½Ý@ÕE@»?Ä@¤7@n@šw @³ :@Ϻ¤@ñ¾bA ¦ÛA"ñðA:ÆAR†AeMAqšAqŽÖAh”kAVwÁA?³òA'ÈùA @ù]ø@Ö7N@¸s@Ÿ‘@ª/@Ç™é@ì¸A wA'êAH°LAn«AŠ”,A›è”A§ ÒA§wQAžøsAŽmAv2rAPóA./øA¥%@ônV@Îj(@¯¬y@¸Âð@ÜéçA&qA"trAHP³AxÏ9AšˆFA½1AÞõKAö]A÷ZAåJHAÄ•ËA¡lqA‚1AQJªA)d}A ƒØ@å25@¿û³@ÇŒ[@ñt0Aj€A9ÅuAmžAš¤AËdB®B*ÞDBGºëBJuäB2ÈB ƒA×£àA¢Ì×Ayš¬ABߦAO@ûsw@ÏC@Óf˜AäA"vgAPMA‰}ÏA»ÎÿB;,BC?gBŽýBºóqB¿ÖÁB˜yÄBS]nBsAÈÍ)A‘ôˆA[À$A*zVA¦‡@ܤÀ@ÜÚEAÓ}A-—AbжAšMAÜ2üB(z“BŒ“3BÿùÓCaáÇCq—óCúßBÊ¿B9ÚÎAíïéA¤±mApd­A6£A>1@æ†a@á´A¤‰JAò/(BDRB·âoC`ÒKDˆ"™EåCŽböBÕ‰ÝB[ôB¸èA°’QA|ÑúA=¼éA‘õ@ì*y@Ý’ÇA géA.nBAdhçA›˜šAßKÐB+ì¼B‘yCs C|—cCˆ=CiB£ù¼B=ÿAñIA¦e/Ar(ÒA7¨èAߢ@çW@ÔºA¼_A#ÉuAR~A‹l‚A¿iÄBÏDBKÅB–_•BÉ$ÚBήÜB¢àB\{èB7&AÌã¯A”óA^/A+ëêA”@Ýݸ@Èß4@ó|^AóÝA@œ¸@¡ô@µn@fä¡@yì}@ŒšÒ@®A@°Ž@Ä¿X@Ùž²@íÐÀ@ÿÜAÕÿA ÕßA üàAõ¾A”y@ñÆÔ@ÝÞÞ@Éý@´›ü@¡gÍ@ïË@€My@Š‹d@Ž;@³d{@Ì4µ@çÀZA›AyA1ßA* 4A0quA0®@A+×¢A!ÚArûA°~@í®ó@Ñ¥Ù@¸;»@¡ÏY@ŽA¯@˜n°@°F@Ì]@ìº1A DAm²A4ßAJSA\yAg^áAgÆ7A_{@ANº}A9€ŒA#}A uw@ô ý@ÒGA@µ‡¿@[@§–S@Ä/s@ç>°A þA#AAâËAd•HA„*ÐA“ÛåAÕ¦Až7½A–›øA‡ÃWAl²AHÆlA) fA @ïPÈ@ÊÁ«@¬þÚ@µ×Ý@غèA WAõäAAˆ›Ans+A’¡zA±`{AÎÞ÷AâܨA㳨AÔI‡A·óA˜ËƒAxè÷AIí/A$ˆ¥A=H@à°ý@¼Éá@Ä&¼@ìu¤A­¡A3íûAc“àA’=]A½vŒAô‰ŒB™B.Ÿ|B0²±BÞ9BpƒAÈ¿AšAo9$A<{AÆ@ö µ@Ë‹ @Ϙ©@þÔAûAHúŠAƒ/»A°C0Aó ’B+6rBl 4B“eB–aÇBz3…B7ƒ}Bˆ9A»ª'AŠæÏAS¤A%‹ÜA‚@Ønž@ØÏ›AóA(bñAZ(ÎA’:FAÌ9B1@Bið}B»ÌCÇ”Cf€BÍ¥fB€¬ÐB$JAÛÕA›ËeAf¾¾A0ÿ,A ¾Ù@áí±@Ý}fA W A.WîAd9%A›hPAÞü0B*ï B 5CÞ~Cs’±Cƒ C”ZB¢)äB<É¡Añ2A¦1Aqô@A7޳AÊÚ@ç<@Ý’¸A h%A.pAdlA›•¢AßQ­B+øµB‘~úCnDC|©'CˆsCüYB£ó~B> üAñ„8A¦`«Ar(™A7®àAß;@çU@ÙƒpA,&A)D™A[«A“IAÏ)ÿBêcBp´BÄ"CuåCÏôBØl{B„ÄB':÷AÞÜèAPîAh_šA1ùÉA X@â·(@жÍ@ÿ£6A;HAK A„í£A³m%AùýB1ABwmpBœP«BŸ£BƒxÅB>^ìB÷ãA¿>{AŒÚÀAUåkA&ê"A`Â@Ù Ö@Å|@îeÊA€A61aAgFA•AAœ_AýJ„BæŠB7zÉB9ͬB%§BH“AÍèrAc;As5@A>ñïA™0@ø+g@ÌüG@·\@ÚÏÌA¡‡A 1ADëîAs•A–léA·8AÖfñAëãXAìÉXAÜ:A½ô´Aœö=A~AM•¤A&øüAäã@âõ~@¾e‘@©s@ÆF²@ê&ÝA ']A& &AFAAjFÙA‡ýA˜œÚA£1A£¢ÉA›ƒúA‹Ç°Ar&ÜAM0ŽA,*ïA;@òoÊ@Í@®¡@™ð€@²DÊ@α¯@ðUœA ´áA!ªÛA9AOÐAb±ŒAn0`Anª“Aeä´AT7fA=íxA&qÊA‰@÷ç“@Õ X@·¤@žô´@‹öÌ@Ÿeþ@µÌ:@ÏN@뽜A"EAœîA"îA.W A5 ‰A5M A02(A%žfA¼±AXš@ñàs@ÔéZ@ºÀ@£¸!@Á@|[@Ž?o@ŸÈê@³%p@Çýû@ÝŽ @ò’7A§‡A äoAðA5CA hA`§@ö·Ž@á÷ª@Ìn@·RÌ@£—m@‘¤°@­5@dd]@}­†@Œ˜ @›jx@ªÕL@º[€@ÈøÐ@Õ¶©@ß@L@ä³›@äån@àÃ@×ÿZ@ËËR@½z[@® F@žŽ^@ž@‘™@iqo@]]Æ@t¢@‡?¸@”®]@£L@°Ó @¾7º@ÉU%@Ñü£@Öµ@ÖãV@ÓMe@Ë^‹@À°ô@³¡@¥áˆ@—™@‰ôÞ@y¶ë@bú@t+•@‰—@™/ò@ªò÷@½È}@Ñ$ˆ@ã¼@ô>¤AeÒA A)‘Aks@÷<‰@ça¤@ÕÀ@ÁÁŽ@®½h@œ³,@Œ0´@z¢Ï@‡ ã@™9@­˜ú@Ä»J@Þ( @ù%A ý“AIÞAéÃA%ŽÎA%Å9A!}¹A‘&A «q@þ±5@㚤@ÉÅ@²@ =@Š¡­@”Ú@ª­Õ@Ä‹œ@â¸ÌA—7A™&A)u.AãAŠ1YA[¤RA0ÑAþ¨@ëF,@Ä2®@Ç£ƒ@ò“5A4%A:ù|AoA›Æ÷AÍïNBsB.¤ìBLÞÜBOÊ$B6DB"AÚœ¼A¤²rA{ÌýAD2VAðw@ü’‰@ÐÚ@Ðì@þ˜½AsJAIÃAƒÚ^A±mÒAõbB-ƒÁBp[¥B–£'B™ÝQB~öFB:BãA½‡A‹£4AT†jA&WAÕ]@Øäš@ÔiA¬bA#²$ARYA‹DçA¿,B6ÑBI lB”ÙÃBÆg¶BËèLB <ñBZÕeB{ŠAÌŒA“÷èA^ÒA+ЛA}c@ÝÃ@Ô€(A¿ A#Ê3AR‚¢A‹i@A¿p BÜ…BJôB–XáBÉ.yBΧ¢B¢+B\y¡B6˜AÌã‹A”.A^/2A+ìfA|@ÝÝŠ@жì@ÿ›FA9‚AK A„î1A³o*AøþèB1&øBwc§BœS§BŸ«±BƒxRB>c;BøiA¿<AŒÙAUæƒA&îjAaT@Ù›f@È£U@ô¿AOªA<ÃSAqáòAž=îAÒ)šB *eB4ÚBUk™BXøB<ï›B\åAßk©A§s¤A~ÿ¤AF"©A(à@þ(O@Ñ/ß@¾1Ó@äKwA “´A*›ATè/A†NAª#ŒAÕ%B¿B+ÍBšÙB’¨AÞmA²œ0AŒãYA_ÞA2GOAg¸@í4ã@Å„@±!4@ÒBš@úõA*äA7sA_T)A‡CA¡©A¸ì[AÈ»AÉe-A½8A¦lÚAŒŠtAhxA>úƒA.³A=@Ù¿î@·èF@£Ï”@¿:?@à]zAK¼A]‹A8g×AW|8AvþA‰ åA‘ŠA‘ã&A‹c$A}6…A^ tA> DA!ÌžAà½@çöì@Åv@©'@•‹Û@¬Ž@ÇW@æ.AËKA~òA-*KA@æWAQ³AZÍÅA[3kASÆAD² A1dfA¸)AµY@ìøp@Ìý¼@±’®@šhA@ˆYÔ@šÍü@¯ÖU@Ç™@áÕÆ@ý°–A ÑëA~A#³BA):A)ÛÅA%Z†AÿMA›AÁõ@çx´@ÌÊn@´yC@žá@Œ[@vª@Š”@@›(¢@­[ò@ÀÏP@ÔÌ{@è`@ù/ìA&ÚAîÀAúA4@üW@ëÚ@Ø×H@ÄæÊ@±Dc@žÁ@ÌÉ@}C@_§÷@wÝM@‰@—@¥˜@´!!@Áª@͸@Öbh@Ûg @Û˜f@×Åž@Ï«Ê@Ä\@·ù@¨–@šX@‹Ñ÷@}{@dy„@Výô@lžÉ@‚Kç@޽®@›à&@¨tÞ@´™W@¾Ÿ/@Ækq@Ê¢Ç@ÊÔþ@Ç¡@À‚ @¶àt@«W@ž‰Ã@‘iâ@„Ôš@qd&@[V@l\@ƒéR@’Ùõ@£‘@´){@ŃX@Ö˜@ä„;@ï{0@õÇc@õùù@ñ6ÿ@çÒ@Ù6Œ@Éa@·»@¦Žp@–@†Ú@rO@‚ Ò@’¾5@¥‚g@ºhO@Ñc@èÅèAˆA ÚAµòAs'AžšAtA gAZl@í¨³@Õç®@¾îo@©Ÿ&@–g0@…b„@ŽBM@¢ÚÍ@º>_@ÕB@óJèA ûƒA³âA*K²A6ÄÌA>0ÕA>wJA8ÖWA-9¦A,A n@ùØ@@Û â@¿y$@§TY@’Å@›b)@³±G@Ъ@óxA ˆA$!„AÈA›bAýR BàŽB7äB9ÐøB% æBH’AÍß…A`eAs4IA>÷‰A›@ø%’@Ìú³@¾1Î@äK-A –YA*œ‚ATà[A†MkAª„AÕžB‘*B6"BŸ*B•AÞn¼A²ŒeAŒàuA_ zA2LAer@í0@Å @´À.@Ö{DA‹ðAª‚A>áAiTÓAŽÑµA«ê´AÇv¢AÙÔ¤AÛh@AÌ= A±ì^A”´ As2’AF*ýA"÷A‘m@Þ]B@»F@¨äõ@Æ~À@êeíA RôA&LšAFc>AjÅ¥AˆRPA™A£ÆA¤4ëA›ÿAŒ'+Ar©+AM“\A,söAj8@ò²ž@Í-ú@®Ó^@œ³¬@µuß@Ó ç@ö;ÛAµA'=A@1ÌAXÛaAm}ÁAz$¸Az­ÒApÿ'A]¥ÌAEsA,'áALˆ@þ-¹@ÙÁª@»©@¡r@›o@¤Í@¼|Y@Ø 1@÷kA poAÈ&A.|A;ZABÖRAC!RA=:qA1ÙA!IAu@ý߉@Þ$š@ÁÐo@©!7@“öB@ƒ\F@”VÍ@§Š~@¼û;@ÔTx@ìÊ+A|A j]Aæ°AØ‹AZAHQAl#Aß@ñÛ›@ÙRÍ@Á¢¸@«Á¡@˜ @†´=@n®Þ@…a„@”ªV@¥L @¶ãA@ÈÀ—@ÙÜ”@èßà@ôG @úÔ7@û E@ö@ëšê@Ý-Ä@Ì`X@ºö@¨ØÌ@—þ@ˆ^(@t}·@Y@op+@ƒë‡@àÔ@ž2º@«iä@·µ‹@ÂFR@Ê)­@ΚÏ@ÎÆ˜@Ëbe@Ä-C@º¦@®¢@ òV@“œW@††+@tW@]I@NWY@b_A@xjN@‡l@“2:@žV.@¨þ“@±¸Ê@¸n•@¼@¼3@¹l¤@³Nq@ªés@ •w@•â@‰Ëÿ@|ùÚ@fÀ@RkR@aö•@{h@‹'™@™©.@¨¶ý@·×ñ@ÆU@Òl®@Û®9@àý.@á,;@Ý)Â@Ô¤…@ÈÐx@ºàý@«Úé@œ¹Ã@Ž´@€[*@gŽ%@xi@‹ ÷@›Ë‚@®-@ÁÑÔ@Ö¦@é“i@úïÞAŸAðhAA,š@þŒ@íc@Ú&q@Å÷g@² é@Ÿo·@ŽW…@~ 6@†ñ1@™t&@® P@ÅL_@ÞáÏ@ùú)A ŠAê”A ¨~A&_A&ŽøA";oA7A =@ÿ¦‰@ä^9@Ê[Œ@²–Ð@hf@Šïã@’¸@¨Lú@Áu±@Þ¢‚@ÿÎýA‘A$çÉA6¾bAE8ÝAMÞ)AN0PAG›¬A:"LA(ÃyAñÀA‚Ù@åÒ@Çý@­$„@—ñ@t@·7Ô@ÕZë@ù`AÛA)üXAD#A]ÈóAsvA€f÷A€¬’Aw#7AbÎÝAIŠßA/AzA•CAà@Ü60@¼àv@¢é@¨ Õ@Åó@èˆrA übA$aæAC¶ÆAg XA…ÌÆA•æ-A %tA #A˜»A‰€ƒAn©€AJ°«A*lFAþa@ð¯@˽V@­Î©@°X2@Ñ+ú@ùq A ;A5ÇÜA\à#A…y Ažz·AµAÄÂÊAÅd¼A¹·ºA£³žAŠ–àAeÊãA=*×AùyAgæ@Øš4@·Ã@¶ìZ@ÚAAhhAC¾ßAq¾ÝA••A´é¦AÓ­Aèq´AéYMAÙ:ÍA»ºûA›qA|~ùÎA/}A<ì@Ù¾e@·æÝ@¨â@Æ~%@êo÷A PæA&F—AF^ÅAjÐFAˆU-A™ºA£ÃÏA¤0|AœŠAŒ,Ar¦AM×A,o°AkS@ò³§@Í-û@®Ó]@ž‹Ï@¸¶@×f@ü(öAÅ’A,”»AGÒAb5£AxÓAƒ]SAƒ¡ðA|¤ÁAgrhAM)™A2˜AŸ¬AA-@Þ`¾@¾vÿ@¤Ä@“Á§@©ÝÒ@Àé@áZA®§Ad¢A'á›A:h?AI}¿ARARÙ•A^ÌA>™A!ËœAßå@çÿ†@Å{ @©%°@œ°¼@µs!@Ó C@ö;‡@çwL@Ðâ3@ºé@¦uÍ@“ç @ƒo@l b@ƒ»€@’®e@¢Üò@³éÛ@Å- @Õ¨k@ä@ïi@õFS@õ€@ðÁ@æ®Ð@ØØ`@È®@·{ø@¦O@•è@†²@qµ›@XÚô@o–X@„K@ö£@žQ‹@«Œa@·åÇ@Âw@ÊY«@ÎÎy@Î÷›@Ë—r@ÄWÕ@º:v@®0K@¡9@“°‘@†œE@to@]‚8@FØÕ@YPÊ@m>Ï@€@‹yè@•¡×@žóF@¦Æ|@¬Œ›@¯Ç}@¯æ@­tˆ@¨-@ °Ò@—¨2@›}@ƒ.U@qw @]^Q@JJ†@:Wì@J„3@\5@mfl@Rj@‡õî@¸1@•ýZ@š»Ö@Km@ag@›m]@—š@‘è@‰œU@h€@qµ@_~@N…@=³ò@J$g@^\š@s,ß@„‘@‹ß@šZŸ@¤@<@¬ª@²Øì@¶VO@¶uc@³Óz@®*v@¦&w@œ|ª@‘Ï)@†Æ6@wƒ@bz¥@N°=@[½Æ@rü¦@†,@“{ã@¡V_@¯:@»ð@Ç{@ÏHŸ@Óø@Ô#7@И–@É=@¾há@±Ð@¤-#@–L&@ˆÅ˜@wþø@`pÑ@l¢ã@„Y‹@“`ª@£µÀ@´ú:@ÆmØ@×¾@å¾h@ðØÿ@÷1ž@÷m@ò£€@ènß@Ú[¥@Éõ”@¸Œc@§1@–ŸÀ@‡F\@rÒÀ@~Û@A%@¡ @´Êe@ʦ@à·@õšðAm½A áA(¿APÀA ZA3˜@ùÕø@äžC@Γ+@¹ ø@¤ö7@’º‡@‚…”@‡L™@™ê,@® ¢@Æ v@ßÜ­@û3…A G&A̶A!£\A'iöA'œÁA#=AÂAAqù@åcw@Ë)º@³3”@ã@‹Pi@@£š‰@»A}@Öua@õ ¿A A A+ò¥A8´A@JâA@‰A:ËGA.ôAƒ‡Aš•@û­÷@ÜvW@À‰w@¨$¨@“8Ï@”øo@«Ñs@Æ!@ä¼9AìoAW)A+¨_A?YANêÄAXu5AXÎîAQ¸ABÉ÷A/ÖïA AÇS@ë“Ü@ËùÀ@°Ë?@™Ñ”@™¢"@±ÆP@Î Û@ïsA mA äxA8ÃAN‚ˆAaAlx^Alë@AdAˆARÔ@A<ÕŸA%–Ae@öòÔ@Ôp¤@·!@ž™y@›õç@µ ­@ÒkÀ@õ`4A tA&EÅA?#•AW}ËAkÔ…AxM¾Ax˧AoEA\3JADSïA+N“A«S@ýCC@Ùð@º“„@¡(<@œÄ@µ@ÒÉ@õ{-A1]A&]¸A?CmAW£ÖAl BAx‚ÍAy}Ao~øA\a*ADvOA+m˜AÀÍ@ýb˜@Ù,‡@º£1@¡3È@™ó„@²KÖ@βÞ@ðTæA ³8A!ªÇA9ŸAOÔ¶Ab³•An0{An¤£AeåÿAT6èA=ëA&leA–@÷åh@Õ v@·¤,@žôˆ@•‡;@¬j@Ç'@æ ›AËtA|šA-"×A@ã¸AQÖAZÒrA[./ASÄAD´•A1a²AµÙA²F@ìó&@ÍÑ@±˜u@šgÒ@š™@¤Œc@¼yX@Ø @÷žA r/AÊüA.bA;’AB×AC!îA=>‹A1TA!HIAV@ýàW@Þ+…@ÁÔJ@© ž@“ôî@ˆ÷@š÷]@¯ü@ÇÉ!@âí@þ A ÖAÚwA#ù,A)éðA*'.A% þA?~AÑ Aí.@縗@Í?@´¢@žþ±@Œ-@€L@`Å@¢|Q@¶“-@ÌF¼@âÒØ@øí^AX¯A ÿnAp#A™A;ŠA*”@ýAb@çvé@Ðßn@ºêo@¦t‹@“æ%@ƒok@n}@…|k@”È|@¥o,@·+@Èô@Ú–@é%5@ô—O@û*Ë@ûa"@öjC@ëà2@ÝmR@Ì–@º¾@¨ýƒ@˜x@ˆsò@t¶!@]ŠH@u-ë@‡j@•C@£@@±V@¾ŽL@Éò@Ò‚@×S'@×zß@ÓÖ(@Ëüý@Áò@´(Ü@¦/Õ@—ûý@Š(·@zEg@bI\@KíK@`e@uŸl@…ó@‘H@œRI@¦<@¯!è@µ‰A@¹î@¹8E@¶ƒ@°ª@¨n½@ž‚*@“•$@ˆE|@z5@d–@P}P@<d@LÆø@^W‰@pz]@D®@‰õ®@‘Õ±@˜g®@4 @Ÿâ @Ÿü]@÷g@™1@“Kg@‹©m@ƒ„@t7í@bv@PQç@?bD@/¯§@=âx@M%>@\>‹@k•¦@y¹õ@ƒMî@ˆ„ê@Œu8@Ž`@Ž©¬@¦@‰N@„}É@|„Ú@n›z@_t@PdA@A6@2”ä@=®m@O@`þÈ@s—@ƒç@Œ@”Þ@šì@Ÿàe@¢§B@¢Å&@ ©ž@œ•@•¥{@ÁÚ@„ðn@wfÂ@d¾<@R¤-@ATü@MT@`Úš@vFU@†[ö@‘Â@œÝW@§…@¯ÐË@¶>Š@¹ÚÉ@¹ýw@·An@±`s@©Õ@Ÿý@”C@ˆ®R@zή@e)â@PÑŽ@[¼,@sP´@†B™@“¯ø@¡”8@¯_à@¼Hb@ÇbÀ@Ï´â@Ôel@Ôæ@Ñù@Éb‡@¾Â<@²@¤o„@–ƒ£@ˆô@xK@`¢@k"6@‚Í@‘‡¶@¡rW@²5|@Ã1@Ó:¤@áMÊ@ëúÐ@òï@òGµ@í¯L@ãÛp@ÖYd@ƈr@µ¯:@¤Ôõ@”µQ@…º@p&>@xÌ@‹§¸@œ„÷@¯@Âêè@×V,@ë$–@ü¹ƒA*A JA $MA3r@ÿñ@ï=@Ûƒr@Çâ@³0@ /@Žî÷@~ñu@‚Å@“•ë@¦”i@»Âv@ÒÌq@êä­AQGA ÁA^ÙA7ðAc[AºDAVAª€@ïÚV@×±c@À\}@ª¾Ý@—I{@†¸@‡Åâ@š<˜@¯´@ÆœÀ@à‘<@üpA ÖŸAz€A"c­A(4A(iÔA$PAÐîA˜öAìŠ@æ!«@ËÁ¹@³¬@žA@‹…­@‹­x@Ÿ‘@µH2@Î¥6@êâ£A—¿AîÃA"#(A-eiA4 jA4K‘A/=HA$ÉlAúAÇ@ðýó@Ô7ô@º8@£Oº@qR@›š@¡@¸¨¤@Ó :@ð— A=A}A'Ÿ=A3­›A:Ô+A;ÎA5®A*sAðA ™ÿ@öþ@Øß_@½ÍÍ@¦@‘‹›@¯j@¡¦'@¸·Â@Ó ~@ð±JANHA– A'¸’A3ËA:ó*A;;KA5ËÝA*ûAÜ*A °,@÷@Øûö@½Üô@¦H@‘•Ø@‹öó@Ÿe*@µÇæ@ÏLÑ@ë»vA$Až~A"ñ A.QÊA5 }A5TMA03·A%¢A¸YAY@ñæG@Ôì^@ºÁ6@£¸,@ÀÛ@ˆW`@šÍ¨@¯Ô@Ç—ù@áј@ý³õA ÒPAœêA#³A)^A)Ú9A%[AÿQAœAÁm@çt@ÌÎ@´~\@žá½@Œ¨@ƒ[ô@”V<@§‰÷@¼û%@ÔV¬@ìÏÕA~ A f2Aæ`AØ~AÏAKÝAiÝAß @ñÚ:@ÙM½@Á¢¬@«Ãg@˜¼@†³=@zHï@Œ~c@˜r@°lö@ÄŸ’@Ùk»@íž@ÿšÚA²ªA ­ÌA ÖHAÐAo'@ñ‘d@Ý­³@ÈßÎ@´}@¡RX@׋@€3ò@l f@ƒ»Ž@’¯t@¢à@³ð5@Å.x@Õ§„@äž@ï®@õB @õ|7@ðÆ’@æ²|@ØØš@ȬH@·y[@¦PD@•é§@†³m@qµ“@]Š:@u.Æ@‡lh@•0@£@D@±Uý@¾ŽO@Éñi@ÒxÎ@×M+@×{=@ÓÕ\@Ëü½@Áö@´(~@¦,Ÿ@—øA@Š)9@zDò@bEÀ@NÆO@b´÷@x‡Û@‡®š@“Oò@ž¯Á@©0¸@²â@¸µ¸@¼jð@¼Ž#@¹ÇÁ@³²æ@«2~@ ñh@•±Y@ŠÞ@}#7@g @RnM@?kL@PÐ @c!Ú@vë@„†q@¥Ä@•ôÜ@œíz@¢‰@¤ßª@¤øŠ@¢ÑÖ@ž&0@—…ý@q­@†rM@yû.@fùÄ@T}¿@BóA@1E›@?¬e@O @^¬ß@n7@|¼ï@„ò*@Šcg@ŽXá@Šè@±@Žõª@‹YH@†,(@›Ó@q.`@aä^@R;ƒ@BÖ™@3ç¡@$×@1Ã@>?@K!Ä@X<@cã@n³º@wH @}¨{@€‰@€˜Ï@~™l@xÍÉ@p«P@f7Ú@Z©D@MÒ0@@ü@3¸G@'//@0»‰@@<@OUT@_l@ny:@}/@…,‡@Ц¿@Žœ @Р@ç·@?(@‹œ@†g¨@€O@qš@b:€@R‡@@CU@4:_@> -@O8`@a@Ò@sÛ^@ƒ=N@Œ8è@”T@›#W@ ›@¢ç@£@ æÛ@œVÀ@•Ûz@ôT@… $@wºö@e ©@RÙd@A”^@J‹F@^Ít@s¸F@„Ц@ð¶@šÈf@¤Â!@­48@³t@¶ó4@·p@´l @®´‚@¦§×@œí@’5Î@‡Ý@x$æ@bïQ@Oò@W‘9@n e@ƒ¨@×@œúÊ@©ö @¶@ÀoC@È+‡@Ì`@̯a@Ée8@ÂJÅ@¸bˆ@¬@Ÿ®[@’†@…¦ @rÖÈ@\>Ú@bî³@|˜u@‹é¯@šŒM@©Ñï@¹X@Çü@Ô¶@Ý„@âç•@ãv@ß2@ÖYO@Ê[|@¼3]@¬ü¶@¦§@ŽØ@€õÐ@h™@m·V@„¿Ì@“ç@¤\®@µ¿Ÿ@Çb¿@Ø=“@ç @ò>8@ø© @øäš@ô@é±|@Ûƒ¿@Êø×@¹a™@§ßK@—-^@‡¸Ð@sy@ué@ŠŽ@šñ@¬ž_@¿åÞ@Ó¬<@æÁ?@÷©óAN…A$A)"A[Ä@ú¿@ê{^@×°5@Ãò‘@°~V@ž"*@MV@|yv@|)N@ïU@ŸbV@²§£@Ça@@ÜÎ @ñªˆA"A O"A pßA —A uWAØg@õÄQ@á3L@ËÆp@¶Ï@£.ó@‘R@lb@I<@@¡ü…@µï†@Ëyg@áÛŠ@÷¶´A¥LA 6©A—œAÃÎAt]Aq½@ü\@æm™@Ð ·@º>«@¥êÙ@“|$@ƒ$û@U<@ K@¢ @µÿÀ@ËŒj@áë-@÷×A¹,A HþA«+AÕºA‚¾A‚ì@ü#s@æ†%@Ð(@ºO@¥øì@“…õ@ƒ.F@|ž@Ž=‡@ŸÅ2@³'@ÇýQ@Ýq@ò’±A¨;A á‡AA:µA &A_w@ö°2@áøL@Ìfd@·S«@£Í@‘¥#@­>@vªE@Š”)@›&¾@­[Ú@ÀΟ@ÔÆ<@è@ù5A&eAëGAkA4)@üN·@ëàî@ØÚ•@Äæ©@±Eƒ@žÂO@Ëû@}Bª@n¯^@…[°@”¨©@¥L@¶æ8@ÈÁM@ÙÜt@èâî@ôH*@úΧ@û@ö%œ@ë—Ù@Ý.;@Ì`p@ºú@¨Øë@—÷›@ˆ[­@t}i@d&l@~Ó@ŒÇö@›œ*@«ƒ@º¤G@ÉWc@Ö-@ß´È@å4@å_!@á=L@Øe@Ì,@½ÈH@®S@žÀ×@¾6@µ}@iÌ@XÛ^@o–§@„â@÷Ÿ@žRÊ@«ˆF@·Þß@Âts@Ê\U@ÎØÙ@ÎúV@Ë™‰@ÄXö@º:n@®,‹@¡«@“° @†Ò@tx@]ƒ@Kí@`e@u¡‰@…óq@‘Eb@œQë@¦€¯@¯!ž@µƒƒ@¹è@¹=o@¶„á@°ª@¨o¸@ž„.@“•@ˆE@z+@d•´@Py@?jû@PÐ@c%4@vç@„ƒ¢@¥’@•ôô@œìr@¢@¤Þ`@¤ù@¢Òí@ž&n@—ˆí@qZ@†nö@y÷D@fùŸ@T}†@BñÉ@2ÿ@A‹þ@Q#Ü@aª@pÕ±@Ò§@†¦V@ŒA*@Ml@’?@’¡ã@íõ@9(@‡éÖ@a@tm@dTÜ@Ta@D¦ž@5–@%æ•@2Åy@@Ÿ@MdA@ZjÉ@f´ @q“@z @€­@‚Kl@‚Z®@@|" @s™u@i@]¢@PØ@B¿c@5ws@({ê@Ùz@$¬¥@0Ï@; 7@Eö @OÞ†@XÒ¥@_î+@ez@gÙQ@gþ?@eÜô@a.D@Zs@QàÆ@H-®@=n^@2ns@'@ .@$‚û@1~‚@>wæ@K«j@Xm8@d†»@o(8@xV@~LA@€æ®@€õõ@Gó@y…2@q$@fÙï@Z÷í@NS2@A%@4{@'C\@/æA@>]û@M}/@\ßà@lã@zn@ƒ©Y@‰Õ@Œá]@õ@}@y™@‰ó¤@„ÝØ@}B‹@o@` ã@PŸë@Aw@2¼R@:¯F@K|@\ÄÄ@n¦“@€4ß@ˆÂ@zu@–ðõ@›¨d@žG•@ž^R@œ`É@˜@‘êS@ŠnG@ÿ@rTu@`bî@NòÕ@>4ô@E¤ç@XS@k¿Ã@€ J@Šot@”s­@•¼@¥M<@ªõ¸@®#@®AØ@«Ú#@¦²¢@ŸOñ@–o³@Œˆ†@‚A@@oä¡@\q@I3@OCa@dµ@yùï@ˆ”b@”\·@Ÿéþ@ª­@³¤e@ºWŒ@¾"ª@¾Cy@»d˜@µDŠ@¬•÷@¢3š@–½.@Šþ @~¡z@hh@Sµ@X6]@nqÕ@ƒR¬@,N@XÙ@ªo}@¶Ú@Áá@Ƚ×@Í#L@ÍMé@Éø@Âßõ@¸à,@­ K@ @’ß@…ç«@sL@\rM@_ @w³@ˆŒ”@–+@¤éf@³Y0@ÀÑÕ@̃]@Õ>@Ú5@Úc1@Öžp@Μf@Ãi¤@¶76@§à+@™nÚ@‹U @|Ae@cÔ @d¶@}1v@ŒK@›v@ªfA@¹ØS@ÈX @Õd@Þ|‡@ãæ¦@ä»@ßý@×O“@Ë*A@¼ï]@­“Á@ž,b@=T@PH@iÏ@f’k@€?n@ŽOÉ@ˆí@­aÒ@½d@Ì}@Ù³O@ã›@éH@éç@å5ú@Ü•@Ïh0@À›Â@°«¦@ ½Ï@‘W@ƒi@kÎG@f°í@€G„@Ž[€@’@­o}@½sÏ@Ì’Ú@ÙÌG@ã³ @éfQ@é¡}@åJ×@Ü)Ç@Ï|d@À¬§@°ºð@ É˜@‘c@ƒ@kâ=@dd¥@}­ì@Œ˜Ó@›lÄ@ªÕ‡@ºZ8@Èõõ@Õ³Ú@ß>’@ä¹N@äé @àÃ9@×þœ@Ëǵ@½zH@® Ù@ž_@‘¡@’è@iqv@_¨;@w×#@‰#@—@¥—ú@´¿@ÁÁ@ÍŽñ@ÖeÈ@Ûfà@Û“h@×ÄÑ@ϪÑ@Ä[ì@·ü@¨––@š [@‹ÔS@} &@dxÇ@YD@om @ƒëC@à®@ž2@«gj@·´Y@ÂHb@Ê(«@Ιm@ÎÂB@Ëað@Ä- @º†@®P@ òx@“Ÿÿ@††U@tQ@]H”@P]É@e)*@{`@‰h@•VA@¡ @«ÝÈ@µð@»è7@¿¼W@¿ß2@¼ö®@¶·d@­é @£^ï@—Ä@‹Ú"@€ e@i Š@Tƒˆ@FØÇ@YPØ@m>é@„@‹yÛ@•¡ì@žï@¦Æ"@¬F@¯Ér@¯é@­t½@¨,ñ@ °È@—¦Ñ@šý@ƒ+È@qt0@]c~@JKð@<i@LÆù@^W‡@pè@D¡@‰õí@‘Òì@˜gJ@6$@Ÿã6@Ÿü­@óÀ@™Å@“L¥@‹¬5@ƒÃ@t6_@bJ@PQð@?bD@1B}@?¬@O $@^±|@nÕ@|½@„ò@Šc.@Ž[@‰{@C@Žôö@‹Y9@†,,@šÅ@q+„@aã@R;Ž@BÖœ@3çŸ@%ã8@2Å$@@ @Me[@ZjÖ@f´2@q’è@zŽÎ@€€ô@‚J@‚Z„@@|!»@s˜·@iÿ@]3@P#5@BÀ²@5wu@({ã@!ù@&ñ@1l¡@<Ì÷@G½À@Qû•@Zï @bO¬@g’É@jwŠ@j’‰@hc @c™Ý@\˜@Só\@Ié4@?p@3ÃT@(g¾@Ð4–4–4–"4–)4–34–M4–r4–^4–f4–a4–P4–¨4–T4Ô4–š4Ÿ­4˜:4–%4–4–(4–4–4–4–)4–&4–<4—!4–u4–r4–b4–94¡$4ž4—u4–+4–‰4–34–4–4–14–)4–]4–=4–C4–%4–4ª'4—p4–D4–Y4–V4—÷4™ˆ4–Ä4–24–K4–)4–4–4–Œ4–)4–14–24–74–B4˜b4¢˜4—+4–\4–R4–K4šÏ4Ÿ4–¤4–)4— 4–/4–4–4–a4–/4–!4–)4–B4–±4«è4˜|4˜Ñ4—U4–ƒ4–š4˜Ó4š4–†4–ì4³á4—Â4–4–4–74–64–24–X4–`4–Y4–È4˜Ç4´4—Ô4–œ4—4—`4™"4–Ê4–þ4¡R4—,4˜Í4št4–Ø4–J4–.4–(4–V4–·4—Œ4šî4Ì4§¶4œI4— 4—Û4™4—Ì4–h4–B4—74§œ4Ÿz4—«4–r4—L4œ^4— 4–¾4–Ø4–Ó4œ4´©4É4š4™4™¾4—‘4–y4–+4—(4¡Ù4—4–»4–‡4–N4–ˆ4–h4–ƒ4–m4› 4÷:4´y4˜˜4˜î4˜ü4™¶4™j4—4šÉ4ší4–~4–P4–Š4–…4—£4›j4–Ö4˜4œR4 k4­=4˜°4š]4™u4˜…4Ÿž4˜÷4Ÿ44›Ü4—«4–Î4–”4–d4–F4—"4š4™ü4¡Â4˜œ4˜½4˜”4šÐ4šL4Ÿ£4Ò¦4Ÿƒ4œù4—¯4–‰4–ˆ4–k4–c4–G4–Ú4—¨4œ¨4—S4—Å4—œ4—Ï4šL4šÀ4š4Ýa4šS4˜F4›ü4—ˆ4–—4–x4–_4–W4–I4¹Ò4˜O4–`4˜4¡S4—«4—I4—ò4™°4ÄÜ4›A4–°4–ª4–á4–ˆ4–4–s4–i4–h4ªþ4š÷4–v4–K4–‡4˜¦4–Þ4–ÿ4—”4 4ÝM4™4–y4–‚4–z4–Š4–Ù4–ƒ4–h4–4œÕ4¦¸4—>4–"4–N4–€4—s4—a4— 4–˜4«c4´4˜4˜/4–²4šÒ4–à4–E4–04–84–;4–p4–<4šH4e4—]4¦g4—}4–j4–q4˜Å4šÀ4–±4¼o4(4™s4–k4–+4–4– 4–&4–4–4—4—‘4–²4°v4˜ 4–V4–u4—+4—f4—%4—ª4–h4–{4– 4–G4—é4—i4–)4–4–$4–I4–C4–+4˜D4˜4–Q4–X4–ò4–é4¦R4–¾4–%4–“4–=4–4—4—4–&4–4–F4–4–'4–4˜B4¡©4–‰4–?4–³4–4šF4–M4–4–ó4–¥4˜\4—4–4–@4–<4™Ý4™/4–G4–04–j4–ò4–K4–U4—4–„4–¿4–c4–,4–,4–$4›4—ò4–4–4–4–4–(4–34–œ4–a4–B4–”4–ª4–e4–~4–34¤è4–¾4–u4–14–4–ç4–(4–4–-4–4–34–&4–d4–4–.4˜Î4™4–V4—u4—W4š14–P4–+4– 4–"4– 4–)4–H4¬4˜Ø4›P4–n4–p4˜A4–p4—Ó4—á4–44—A4©g4˜4– 4–D4–'4–4–4–4–,4™ú4–¤4–ª4–=4¡%4žQ4–ø4—4–Š4–.4™O4¬É4— 4–4›R4—ª4–%4–4–`4–-4–$4–4–&4–ò4š^4—M4¬4œr4–v4–Œ4˜4œ<4–¹4˜@4ä 4™î4–4–4–;4–A4–|4›ù4–÷4–Æ4–Ù4–J4Æó4œŒ4–â4—V4—4žê4—œ4œn4™ 4—Œ4˜ë4™4—T4–?4–)4–³4–f4—)4¶K4¢Ü4›½4žö4˜¿4—b4˜½4¸U4™<4–M4–&4–4™U4˜4—é4–|4˜24¥¯4—è4–ô4™4˜ 4¥N4£N4©C4®è4È4£˜4—e4–A4–e4–£4œµ4™á4–¾4–þ4–U4–Ü4–4–Û4–™4˜_4âø4Ÿî4›4É4›4Ÿm4¸¯4¢4˜º4–f4–×4–±4—4–>4˜w4§É4™4—í4˜V4¢w4Á­4I4Ÿ4œ¸44žQ4˜È4—ò4™¢4®94«J4šÆ4–Ö4˜‡4—Û4–Ð4—Ù4˜X4™4ª¾4Ÿ44˜T4©^4£\4Я4¥B4˜ß4—Ò4–Õ4—4–®4–Q4–`4™Ÿ4˜œ4–Ú4˜'4™z4 4š¶4—å4´¤4¶Ò4¦²4µ@4›ø4œ™44¢4–…4–W4–`4–L4–N4™<4–ì4–Û4˜ž4ªH4—¸4–ô4—Å4›4¶ý4­:4˜4–õ4™_4–¸4–o4–{4–4–i4¥44—v4–¬4–•4— 4ŸB4–ß4—4—4§®4ák4 D4–Å4–£4–’4–”4¡Ä4—X4–œ4šx4˜ 4˜ 4–Ù4–P4–N4—4˜y4£Ñ4›Š4–Ó4©¨4ša4˜—4—Ì4–Ó4²€4˜Ø4–E4–#4–;4–$4–&4–+4–Œ4šE4—}4¬>4Ñ4–[4–4™-4—Ý4š%4š¯4ž‹4͈4˜j4–74–34–$4–#4–4–%4–74–ª4–ª4 ±4ªÌ4–à4–/4™=4™m4˜U4–v4–d4˜]4–94 4¤4— 4–%4–4–S4–”4–:4–:4–ù4™¼4–C4– 4¨N4y4Ÿæ4–y4–4˜ª4–ß4–Z4–i4–$4–(4–4—ü4ž®4—d4—4–U4—(4–;4–O4˜x4—Ž4™l4–74–"4Ÿ4˜î4–84–4–4–E4–;4—Æ4˜á4–c4–m4–M4–p4–E4– 4¦>4–â4–€4–M4–-4–”4–D4–Y4–24–"4–(4–"4–#4–24–&4ž'4—¸4–34–¤4–Ì4—)4—¨4–\4˜4–÷4–%4–4—p4™™4–\4–4–4–4–L4–/4–é4–¥4–^4—ò4——4™j4 4–¾4˜G4–Ì4– 4–4–K4–24–-4–24–Ç4–T4¢ 4–æ4˜Å4—‘4–³4˜T4—þ4–‘4–©4 4ªí4–æ4– 4–!4–/4–4–*4–,4–<4– 4—P4–_4žT4˜ð4š}4™>4–ó4–q4—4¢à4£Ã4—4˜“4—[4–&4–4–-4–@4–4–4–A4­™4—¶4–P4£Y4˜…4™A4˜Ù4—Z4¤Ê434—~4› 4–º4–4–,4–:4–W4—+4§È4˜R4¤14–é4— 4±c4˜=4Ÿ=4žÝ4™&4œa4—¼4œ~4—e4·A4œ¢4—Ï4¡a4–-4–<4–ö4–‡4–n4«–4µû4£\4šª4—Å4™^4ë;4K4šN4–4–^4—Ø4b4—u4°½4—4–š4–ì4–ú4—O4 q4™‡4-´4¥ð4 –4¡L4±4®Ð4ŸÆ4—³4–î4–r4š4™“4—P4±Ä4šŒ4–è4—4—c4–Ò4˜r4߈4 ƒ4Ÿ4¤T4ªt4°m4¢"4œ4™Z4–Ê4–á4˜64¢\4–£4— 4™Ü4™²4˜|4˜4Ÿf4Ýu4ª¯4¯a4¶p4ã´4§˜4ši4˜’4™G4Ð4£ê4žl4— 4—4—4—[4˜¶4™±4šq4·°4±44§!4°84º 4·®4œ`4™¼4—›4–ü4¹4™i4–^4–Y4—4—_4—4˜Ð4¡ü4»Q4¡H4˜‘4¦Ù4²|4 4»y4£­4ž–4¼ª4¬«4–Ë4–Â4—·4–o4–14–p4–L4–ž4–´4¤B4œ#4—Í4™4P4m4®|4™v4˜#4—#4œv4£Þ4˜}4˜4–”4—S4–¥4˜x4—d4˜4¬B4š-4™í4™©4›.4±õ4«¶4˜V4—õ4f4˜J4žd4— 4–‘4 H4õ4™{4š(4—4–{4™4—ç4¢ò4®á4˜ñ4›®4žç4šÝ4›(4¢‚4š•4–Ê4–@4–&4–`4–H4–ƒ4–¡4–44¥ø4—š4—š4™=4—4˜¬4˜©4™4Ÿ 4˜”4˜4§ž4–õ4–&4–"4–14–(4–24›Ð4˜U4—Ð4˜ò4™04Ÿ·4–„4–‡4–®4¯©4œ÷4–A4–B4—h4–$4—4—|4–j4–.4–^4©4/4–ô4–¦4–©4˜4–[4–34—ß4Ö*4›ç4–(4–"4–ñ4–D4–"4– 4–+4–44–L4Ÿ4œ¦4œÿ4œ/4–Q4–n4–-4™“4—a4ž4šð4–O4—4¡k4–¿4– 4–4–"4–<4–+4–Í4–Û4–Ü4—*4–J4–^4–74™ß44–Æ4–4–24–s4—Ì4–ý4–ˆ4–*4–'4–34–C4–X4e4™ž4—¤4–¼4–4–´4—=4˜Ò4—34–54˜“4œÝ4–t4–,4–W4–24–/4– 4–ó4–&4–k4–^4–P4˜#4œj4–¿4—'4šÔ4™§4–A4˜¨4œ 4–m4—‘4œL4–‹4–!4–L4–~4–"4–]4–%4–‘4—¢4š¸4›4™•4—å4—R4—-4š„4˜È4–14–â4U4—Å4–64–(4–4–4–&4–54—e4–‡4Ÿç4¢(4˜f4–“4š84™Þ4¨!4Ÿk4–¬4–&4—„4—‘4–f4–\4–:4–„4–¸4§ 4˜4–T4Ÿ%4 ü4™v4–‹4¡Œ4¬Q4Ÿõ4–4–S4–*4–ú4§K4—|4–l4–ò4˜A4—o4˜•4–‹4œ 4°ž4[4œy4˜~4›š4Ç´4š%4–³4–•4—“4—M4˜4µÎ4–C4–<4–W4–‘4— 4£4›Œ4¨14©v4›Œ4™"4Ñz4£4£X4—Ï4–…4–¶4¡ 4—4˜%4–‹4–&4–„4—-4˜ë4ßü4¢54¯4­N4»þ4­z4¶Ž4¤R4®)4š¤4—¾4˜×4— 4–V4–Y4›¢4¢’4—Ü4–»4™é4˜T4›Ì4׋4¹˜4­Å4Û4ÆI4©c4¦³4¬4¥Ÿ4¡4˜A4œ÷4«4–î4¢p4¢o4ªg4›Q4žØ4¢­4º14MJ4²H4þ¤4• 4±4¡ 4œB4¢Ð4™ò4—Æ4˜¹4—Ü4–Á4–f4—¡4šü4±C4¨¡4ª4ª«4RÀ4ïÝ4·4 4 ¸4œ·4šR4˜²4¾ê4¨C4—›4—m4–Ù4–Ä4—4œL4ª44™­4ž*4¯4 ‘4Ïý4ÖO4ÍS4¡Ù4¨…44—þ4˜Ø4™4–¬4–Q4—)4–n4–n4–ƒ4¡¯4ËÄ4 Š4¡“4¡ê4*t4°š4¡í4¢O4£;4›É4™“4˜¸4š±4–º4–Õ4–]4˜4—ˆ4–û4œ}4°%4ž¥4¡`4£w4­ø4¨.4™`4£v4™x4–Œ4–Õ4–4–‹4—{4¤ 4Â4¡4˜4–œ4–»4›4Ý4˜‚4¬4¯ 4žü4—4¶C4¢£4–œ4–†4–\4–'4–34–ˆ4™4™¦4–S4—Š4—<4—¨4—¤4–w4´›4›`4œ;4š§4™b4šF4Àæ4˜e4–54–/4—T4–,4–74—$4–™4–-4ŸR4šœ4–Å4—]4˜á4–â4© 4§Â4–µ4–B4˜4–;4– 4–34—û4–/4–34—Ò4 S4™w4—4–ó4–ù4—„4–ò4–(4а44–84–&4–’4–Ò4–4–4–D4–84–q4® 4œ~4–ô4–o4–P4—\4–4™J4–Ì4˜Ž4Á,4˜¿4–”4™ƒ4—Â4–R4– 4–4–;4–<4›f4—u4—ö4™Ÿ4–h4–}4—¢4™ 4—y4–²4˜L4–X4–W4–†4 ò4š‹4–E4–4–Ç4œø4–ä4—4–¤4–94–x4–í4–Š4™·4³à4—è4–M4–ˆ4— 4–Y4–E4–d4–84–S4˜"4Õ|4™}4–N4–24–04–å4˜¹4–õ4œ4È–4˜‰4–64–4—b4–_4™X4¢F4—I4—³4¨ý4Û54š 4–'4–=4–44–¡4—ò4¼4¹^4ª4–á4–g4–°4™64™w4š–4¬­4™á4–ª4›¼4 4š.4–H4–t4–74–´4µB4£Ý4ì4š®4›Õ4–Ù4«p4—-4—Z4–J4˜’4—ö4–½4›ñ4›ž4dž4˜Ù4—4–J4–{4›G4§4Ÿâ4˜(4£p4˜è4ñ¯4™Ö4–+4–(4–i4›h4—4—4 U4§4 Y4–”4–“4™.4™T4° 4©î4¡º4˜ë4Àè4Æ»4˜Ð4–`4–Y4–Ç4¤E4¥4–´4–“4–ê4–é4˜c4¿€4­4™&4ž«4ݺ4©x44¸ƒ4˘4š4–ï4–-4–š4–y4™”4–g4–B4˜¾4š¶4šB4¯á4ª*4š4·.4=4Âô4®4Ë”4§r4™Å4™à4§ü4œ=4–Q4–H4—¬4™4—4˜{4µ=4™ 4¦É4@{4Ú(4ïÀ4i¬4Êó4¿í4æ³4£o4¦h4§|454©4¢˜4—ð4™ö4ž=4¨Ÿ4¢˜4å†4ªƒ4×´4y34r4ެ4ú4¸¿4ÌB4ž4Ÿ“4™ 4—É4™Ò4›|4–ø4–ž4šÖ4 Á4¦˜4½æ4ò4ÀÉ4èÈ4T4À‘4ï¡4ʯ4ÀŒ4Às4ž94 ·4`4š<4»¸4—4˜k4—­4˜D4¡%4» 4œ[4À^4Z±4õá4é4ýŠ4°²4œY4±}4¯4›4˜N4–S4š¹4—q4­³4œ4—m4–î4›4±¯4Ï$4©`4°Ë4 ü4¸º4ã–4³š4 I4šÅ4˜4™÷4­[4˜Õ4–¢4–‰4—4——4–b4ªX4©i4š=4¢¾4éê4°>4 [4¤O4ÕL4™Q4–,4—4—Ö4¥N4©¹4—>4–Á4—B4—ý4Ÿå4˜Ú4¢@4™4ŸÍ4ªÕ4Ìñ4 æ4š4ý4–Þ4šž4šM4–è4–£4—4–H4–[4–[4–‘4¨ã4šŸ4˜4–í4›4œ4²Í4µt4˜Ó4—\4—!4ñ–4šî4–44–R4˜„4–04–14–"4–!4–ƒ4£Ò4˜s4–€4¦ï4—½4˜a4Þx4š4¤4–q4™j4–ü4–94–U4™¯4–+4–4–H4–„4–@4—4–¶4—z4¦‚4˜4–4«[4˜?4—¯4–84—Š4³#4—=4–P4–Ì4–,4–4—S4–r4–'4–(4— 4´Z4˜À4—4–Ô4˜Ú4™ 4–q4–4™î4Åê4˜W4—K4™64–64–'4–c4–24–¹4—94— 4—ð4³g4—p4–½4—54–à4–X4–I4–Æ4œÇ4–„4šÐ4«®4–ƒ4–4–?4–)4–24–64–}4™…4–¹4—¿4ÊÙ4š4–Š4–p4–ã4–¼4˜G4–U4–W4–Ý4šè4¦4–÷4–E4–e4–G4–€4˜4˜c4™D4Ü|4š˜4–l4–L4™U4—4–\4–N4–ˆ4«,4— 4™Ð4–f4–n4›¡4–q4–F4–«4¦H4Ÿ4ɵ4šI4–€4–_4žD4«”4–õ4–4–>4—O4–j4–H4–P4—4¶Ä4˜Ð4–E4šº4¡˜4š4çÄ4Ÿã4–ü4˜24—þ4¨4—í4–24–.4–u4—?4–§4—‚4˜f4¨‚4—.4–®4˜=4¾t4¨þ4®Y4¦4—–4Àþ4œï4—Î4–}4–s4–f4–r4š€4°Z4ŸA4˜µ4—¿4—4³ß4›4«4­È4Ö%4¨4—84®4£R4—)4–4—š4›Œ4šñ4¤h4¥²4š+4—Á4—s4 4©4˜·4¤t4;c4Í44܆4 \4±44œ[4y4—P4–²4–=4˜Û4—Ó4˜4¢$4¤ü4™4Þ4®›4š`4ãœ4Yb4û¤4Ó—4ª´4Çë4¡¦4™{4˜ž4™&4Ÿ4¢4—‘4™14—²4˜ò4ªT4á4ì"4g34 4¢‡4¹&4ßE4Ô:4þU4§84§c4¥`4M4§=4£`4›b4—c4š4 4Ã-4ì^4±04¸4#4´!4A©4QÙ4 ý4¤Ñ4žI4°c4« 4˜×4–ö4–²4—Ô434¨y4²4¤C4°Ï4’Ö4=]4J=4ËT4Eû4É`4n4Ùï4íÍ4·ò4œf4¤4«m4œD4—44š°4˜4—ì4œÅ4Á4«Þ4Æ`43å4¼4m!4^ö4é‰4îF4¸X4 o4š»4¥¬4¬4™„4—ˆ4—ª4—4™¹4¯4¥ö4©Ò4”Ð4¶4Ô4(¹44L4§h4­34® 4ç4™Ý4˜84–Ô4—K4˜I4¥:4¦v4š¦4—4—ß4Óç4¥ë4 a4·_4¼ž4í¬4«€4—k4—¹4¸ê4“4ša4›Õ4–n4–H4—4¥.4´4—k4–ê4›K4È”4ÆF4¯p4ð÷4©B4˜"4–E4š%4Ò¨4¤ô4–á4–ƒ4–s4—G4–n4˜Z4×l4˜é4–™4–Ò4­E4¼4¢Þ4­à4ž*4 44˜ 4™4–É4–q4–O4–¶4»4–Ç4–“4—ý4˜V4—"4–˜4Ô¯4¤64™Ò4°Ÿ4—…4¸Å4›ª4–Ã4–•4–d4–l4–Q4–C4–V424˜Š4–=4—4–ð4˜°4½­4üÀ4™~4œ 4—ˆ4œ™4™4™=4 ÿ4š4–¥4–g4–54–)4™4—>4–B4–E4œž4ÔÌ4›4œø4—x4˜A4–Ê4˜4™–4™µ4§l4—¸4–i4–V4–:4–*4–24–84–U4–¬4´4®?4—ÿ4–ö4—)4—¤4–¶4–y4—É4–é4¬(4˜4–Y4–4–A4–74–14–;4–a4–G4—•4ú.4Ÿ+4–º4´ô4™U4–Î4–Í4˜54—è4 N4—.4–X4–A4–E4–z4–#4–b4—k4–u4—U4²‹4™4—i4Ð4š’4–‡4˜c4Ç4šP4™I4–‚4–@4–‡4–ƒ4—‡4–`4—t4Ⱦ44˜4¡04™c4šC4áI4›ï4—4–÷4šÝ4˜z4Ÿ4–Ë4–O4–H4–54–Ô4–“4—E4¢´4¢Í4™µ4œ}4¢X4›µ4Çþ4¶›4™f4–Í4–Ž4žd4ž™4—4–m4–h4–p4–34˜O4ž‹4 ð4œ4—ð4¢y4Þ4¨#4³4Î4œI4Ì4–¦4™V4¡4g4–ï4–d4–á4™Ê4™ñ4œ³4©4¢[4šw4É4>¼4©{4îk4Ñ^4˜ƒ4Ï¢4œã4›Ó4£84©m4² 4™³4–±4–†4–ê4—“4R4¨ 4™œ43Ç4Sg4Ãü4º%4²4š4—Û4â/4»j4šË4˜·4œ–4—¨4ŸD4ž 4™ 4šá4œ4È4 -4d 4ã4jO4F¿4¦64kª4°4ß4½ä4š¼4˜•4™84™D4§4áS4 ’4Ÿb4³Õ4DÚ4„4د4Bê4ŒÜ4ª'4΋44±F4Ÿ54³‰4ªà4•4œó4›§4¬4© 4›#4¢ø4ºÀ4Øl4Çp4]D4z¬4Ýç4 dz4v£4Ϫ4¬°4½G4­ 4«Ÿ4œ34–è4–ï4Ö4¢¸4œØ4¢•4®Ÿ4ª…44k4ˆ;4í04þ¸4Ëæ4ïÇ4Î4ßz4y4¤k4šÉ4˜14—¹4œ4«º4£®4œµ4¨w4yó4JÖ4¸"4ãÒ4+4±p4<4´¢4Â4ÞQ4¸þ4³À4¨]4¨K4™´4±Î4§ð4¡—4š4œ\4Õ®4Ä+4²”4üý4¿Ü4øI4­V4ŠË4Ê/4æ|4WÚ4«Ö4˜z4–Þ4—Ò4¯b4Ô®4¬±4¬¥4¨Á4šµ4—é4œh4¡§4¦‰4ÄM4'™4©]4d+4¤k4 ›4Ÿ¬4¡ˆ4›d4˜Ó4š4¡4›î4±Ñ4š4˜4–½4©h4±w4«4÷#4=Ñ4*‘4¦W4˜Ü4—74šÀ4ª¬4Ÿ4—[4—O4˜×4˜]4™È4¡Ä4¦ö4žO4šM4˜l4A4³‘4²©4áY4›Ú4—u4˜#4–µ4Ÿ×4˜Ò4–Û4—J4¨4˜R4— 4—+4ž*4¨€4˜Ô4šà4£t4ÅÞ4¬û4Ÿû4—R4œX4£ 4—Ý4–ú4—4–•4–ˆ4˜ï4—4–”4–Ÿ4¸4ú4˜ª4šå4™½4²y4¯À4¢4˜Õ4œ 4—¦4—l4¥À4—Ä4–€4–A4–J4–Ä4–_4–|4—´4—û4«?4¤'4—ã4™â4› 4™÷4šŠ4Êœ4˜ê4–É4—L4–ˆ4–J4–Q4–>4–|4–L4–v4–¥4žN4¤4œ§4—4—™4šx4—†4™¬4÷ä4œè4™á4–Õ4–i4–H4–›4–¤4–¾4–¬4—"4œ©4±T4Ù4˜b4›Ä4 £4š!4š 4›p4ÁM4™ 4–€4–T4–M4–F4™4™£4—/4–ë4—e4¡ð4´Š4¤Y4Ÿì4ž»4}4¡'4 \4˜"4›‡4–É4—e4–‘4–Z4–:4«94ÎR4œ4—4—84˜Ú4¤4ýp4¢Y4¥r4¦Ä4¡€4­Ñ4˜Ý4—4–ù4œ4˜ 4–¶4–f4—4¤{4š4–Â4—4ŸÛ4ª@4ùÀ4±¤4¡Å4©®4Ë4®ì4™e4™ 4—4°ÿ4œ)4—a4–¨4–ó4˜=4—‰4˜.4™64Ÿ4ª(4¢³4#4¾Â4¯84©4£<4 P4¤£4›-4¨34›«4˜q4—4˜f4ÌÊ4¢ 4™Ú4—ú4–4¡4›×4"·4îV4X4M4²|4±Œ4=4›”4šŽ4 Ï4º¯4›4–õ4˜µ4˜¾44Ô|4šQ4—E4¢Â4Ð4Ë64€Ü4çÚ4Ë4˜¨4íÃ4¤J4Ÿ24Ÿ4§®4˜ø4—e4—O4™J4Ÿ64²±4¢4 ¯4äõ4Ö³4 nª4h4ªo46‚4÷4qÉ4³–4§¶4 4œW4Q4˜¥4›Û4Ú4«‚4Ðo4 4ÞÝ4Úc4k‘4Ð4׺4¬[4K4®4¸4ÃÃ4 h4©—4¦Í4­4˜=4˜l4š"4µ°4Л4V41Ü4Š}44 rô4¯¸4yE4jä4°°4Ë34»É4»4½è4ŸÂ4˜!4™Ä4›Y4À)4¦°4±U4ÂW4#õ4 ¬4SÂ45Ë4Ê4kÊ4‹ë44þ4 4¬—4šž4˜%4—b4²Ç4¡o4ž 4È4§l4o‡4În4Ê64˜M4ˆr4ÃÇ4f:4Ù(4Ø\4ä4Å*4£4­ø4²ƒ4™ê4š¥4š?4›Ä4©Ï4½4¦’4Ê4!^4?"4FÔ4ÈH4îì4í4§84Æ4².4[4›Å4ºŽ4ªÃ4˜¤4˜P4—m4—Þ4«ý4¡È4¥A4œ“4àü4¶44_4à¹4¢y4¢õ4™W4›ê4šƒ4»4­ 4—C4–§4™¯4™Ç4™d4­¶4£ñ4¹4 4à4J&4n4X4²C4 4œN4ª“4œD4™Â4˜¶4–Æ4–;4š4 ´4šX4¶ 4³?4ú4­Þ4ÆÝ40+4Á:4Ðz4}4©o4žZ4©Ó4£¬4™4—c4–¡4™Ð4—Â4—‹4—ª4°@4œÁ4Õ[4›^4 14Ɔ4F4 )4—ý4›¯4œÓ4ç¶4ž54—÷4—4–‰4™E4—/4–Ç4˜·4à 4ˆ4›4šH4˜ö4±4½(4œì4—c4˜(4™4º'4Â4—}4–§4–C4—4¦4—a4—h4ž#4 4˜\4˜ž4˜24¢G4·–4ÒÖ4™õ4¢K4—®4šÔ4˜Á4–´4–+4–]4—4¦‡4—Ô4–»4—4™)4˜À4—N4—Õ4œ4Ë/4£ô4˜4›£4–Ý4–„4–‹4–q4–+4–4–Ö4œI4™œ4˜4›Î4—ö4—l4˜À4¥t4§¦4™›4™G4—±4›#4—^4–€4–>4–;4–R4˜r4˜<4?4œ94š24¡œ4—´4—•4«®4¯#4C4Ÿ•4Ÿ4™i4«u4Ñ4—#4–X4–b4–`4˜m4˜§4—Å4šÃ4¬4§Y4—Á4›¨4°4üÈ4£Û4§‰4¢Ù4š‹4²?4°~4™™4˜s4–ÿ4–§4˜»4– 4˜#4±á4µ 4›Ÿ4—?4œ[4%¿4Ȭ4º?4^4·ß4¤X4…4š+4šH4›Ù4™S4—Ä4–Ü4˜æ4Þ4¥ 4¬84¢g4žT4µý4Ä–4à¨4Lm4UÖ4Ët4"34§ƒ4šL4—$4˜4½–4¥4–Â4—Ó4—4›¥4¬Œ4 4é4û4c+4Ä4z§4(¾4ôð4Ë4Ÿ™4œq4™i4—4°¤4¦‰4—C4—X4›¿4ªH4\j4cD4¡·4M4O4Hf4ÿ&4öS4ô®4‘ì4´4£4¤ì4¢%4˜I4™–4˜_4œž4ã4ê4ï 4êA4Oµ4ÌÕ4µ44#.4 yh4g4òÈ4Q'4:!4»ý4»–4§Û4Þ4Ÿï4˜Ê4 '4Þ>4¼Ë4¶b47:4ª40z4b±4#)D4,4 :4Š4¯R4¶é4â°4¿Q4²g4°Ð4â4˜¥4›Ý4¥Ÿ4ÀJ4˜R4åÅ4HÏ4è`4&|5ò§4O Q4)¤Þ4 ø„4nk4J¦4¨4×G4ŸÞ4š¨4˜Ý4ªp4±ù4Ä"4±4")4km4¡|4£Z4å¡4Á† 4¥[4b4ÅT4u«4Ú:4S<4¼Õ4©4š<4—‹4¦[4Û(4¼ž4¸W4¸64vO4â«4œ4—½4–4(Ié4Ñ|4A4å4 ê4Þk4¯È4´44É‹4¯L4—ô4¥Ë4È¿4Ý&4¾4¸4³é4o74„ò4eî4YY4z 4º”4YÙ4Ì4°]4ìV4¢´4šQ4™$4—N4š4¯…4¤Ì4œY4¨e4Å4Ù>4Ý4'14 ¤4•4áª4±Á4É]4¢4¥î4˜˜4šU4™:4˜,4ž 4Ç%4±Û4žD4ž‚4Áv4µE4‹É4Íñ4Æû4òÿ4eX4±Á4°Z4ƨ4œØ4–ê4–Û4–Ñ4šì4·#4œ 4˜Œ4š”4›!4Ô%4£4Ãâ4) 42Ç4°4nì4­Ý4¢D4¡4¢ö4™s4—£4–ö4–ï4—64–­4š¦4ÁN4 æ4²ó4¨é4U4ÍØ4×Þ4¥4ñj4žÄ4˜4™%4©´4¶h4š4—4–³4–;4–%4—64ŸÒ4Ô‹4¤Ç4¡À4­·4Ÿc4äS4¸ö4¦º4™æ4–ã4˜4¿ 4›*4—4—C4–Y4–U4–C4–4˜V4¤=4«þ4¢ß4¢4œG4¦×4Ÿ74¢ç4š4–À4–D4˜O4–h4—4§4—˜4œ 4–²4–Ç4–Ž4—‡4™–4™o4˜°4¦Â4²%4™ð4žG4— 4–Ò4–e4–z4–‡4—Q4¥ƒ4–K4–N4–h4™˜4—›4–×4œ&4—ý4ž4Âß4š§4«+4›ª4—Ò4—–4–ö4—°4²F4˜r4–\4–04–n4–^4—Y4—Z4—4¬ 4›ž4ŸO4©’44 È4ëþ4¡:4\4—ò4—4šn4–È4–W4—à4˜ç4—J4–®4—*4š4¤}4¤Œ4¸€4¦u4¨ú4³.4èP4¹4¢Ü4—{4–ª4–¾4–e4–r4ª4 J4˜4–â4—|4žÀ4¾]4›È4¨ñ4ß4Þe4ÆÈ4º.4ßÃ4·4˜4–x4–¬4–²4–Ã4˜³4¨ 4¦V4™¶4š64£±4ÁÖ4¤)4ÐR4œw4ïy4ÈÍ4­g4ºÉ4â4¡ 4—4™m4—Ë4—04˜4°E4£ 4˜ì4w4¸;4³4¾a4ÕÛ4[ý4$n4ÿ4óZ4Æ4Îd4¤4›y4½Æ4ªa4™b4™ 4™ü4œD4¢—4 Z4®c4Á,4Â4b!4‡Ê4Çô4$4‘4Ÿˆ4Ó£4©³4œ½4¢I4¦f4™ï4›Ì4›&4££4´Ô4Óï4ô%4l744‘^4tQ4 û÷4 êì4ñç4Y4ñw4Ì’4¬#4Ñ­4œ,4˜K4˜À4žÃ4áj4á_4éw4S4ß4 œ>4 Ú4¦îD4¨¯U4 ùƒ4`4•²4II4è¹4Çm4Ée4 4šË4˜Í4œ4¬¼4âÃ4A4'¸4 ¦4õl4’CI8ñ8¬Z444ût4Ò4÷ê4üÆ4¿¹4®Ó4Ÿ]4œ4˜þ4›94©4ˆØ44* 4½Þ4yL4-Üe8a~;9LΩ4½54e¡4‹4nz4wz4ÑN4µÇ4 T4œï4˜Û4£â4¦³4Ûé44Ò4MN4Ðø4‡4ª5ÿ4É _4 É>4í“4é4ú 4íá4¼F4Ö¯4ß4±÷4¸4žš4­ 4Ö‘4Î4õ4É4Ü¢4 —4·$4!e·4Ïœ4 ˜¾4W²4S(4ÞÝ4þ–4³{4¢+4™04—à4™v4¡†4ÁÉ4±&4ï#4 4ýÊ4pf4›ß4‰54Œs4ü]4½‰4ݼ4ª¼4©ÿ4Å74žr4˜D4˜v4™4˜Q4¡t4¢Ä4Ä4‚Š4²4tö4c¿4*ð4\4+¤4Ñÿ4Ð+4žú4–Õ4—å4—4–4§24©¼4™g4—4™•4£‰4ûo4»74ÅN4v4R4%4®4¾4Ðý4¤¹4–r4–l4–ã4–Ç4ž&4âu4›³4–Æ4—Y4š?4²B4áé4«Ö4¾4i4Ú³4¾…4»'4®<4™Ç4–²4–©4–²4–Æ4›[4ž:4Ÿ64™Ô4—4™í4œ–4³Ý4'4®!4´·4¢S4¦Ÿ4¼/4œN4—È4— 4–O4–º4–ª4áM4›v4˜34—‘4—4˜m4žÛ4´Ð4¿ù4ñ4£4šÛ4¤Ì4ž4£¤4›»4–È4™ú4— 4–³4›ó4š4—14—Ó4 €4¾Á4›w4™ñ4šO4™4›c4›C4½w4š¥4¢C4šˆ4–ª4–ä4–‘4–Ë4–o4—„4—ƒ4§ 4™á4—Ï4«Á4žÅ4œ®4 Ð4šŒ4·â4™94–²4–÷4™Ô4—h4˜o4–k4–+4—y4Ò^4›#4œ¤4˜a4—…4³ˆ4ŸÕ4Ïi4¼@4œq4Ÿo4œ²4˜4™æ4Ñg4›84–ì4–!4–*4–Ü44™ï4£¹4¤þ4šÍ4¤N4¨¥4®4çœ4¬14«T4¡&4™Ù4¸^4©Ù4™54–Â4– 4–:4—_4˜t4ž’4¶Ò4±Ü4£L4µg44¹ 42ù4×é4òI4¯4šÓ4¤%4¨©4šC4—@4–C4–4–v4–ú4˜‡4¶®4¿Y4ª94Ò¬4&Ä4ã<4˜4&4 [44Ïô4¥“44£4š]4–|4–4—ü4˜D4š4¸æ4±<4¦#4ô4[]4¨m4Z4™˜4P4£4Sz4Ót4¡Ç4œg4™$4—§4—4µK4šÎ4˜`4¢Þ4ªs4Øô4ª£4*4~O4¹v4OŽ4ô˜4*¹4Ÿb4$ 4²E4¡74š4™°4˜^4æ4¡4›P4¥+4Ã4ûÐ4ø4¬Z4 7œ4¿4 d4PG4v4 $Ë4 4ü4²Í4±©4´84Ÿƒ4œk4š«4 ³4Æ[4j\4z4ŸN4Ú[4gÆ4›÷ä4‹· 4&4'é4¶F4nŒ4K”4‹4­Z4²Ä4§ö4˜Æ4š˜4 14«Ì4B4g¬4]4â©4;Ë8˜@M90%à4Éå4!9Q4 ×÷4Ûö4 4Ô¹4ï4³n4ñˆ4˜A4šw4¢ 4ÄÈ4U46-4Ê‘4 £A4 Ü·8ЄG9è÷i4î`e4-k4 b,4ð4ç€4æx4 Ñ4žR4¼£4˜4šb4ŸØ4¥ì4¤4<.4;h4D4&Ý4v(:4rj-4#C¢4 åÚ4ZÛ4W4K*4¸Ö4¡Û4©4Ÿõ4»ƒ4£'4¡Ô4©Œ4"<4$¦4+4¥J4í4ª4AÊ4‰ 4Ç4ß4å4¾y4­74ž¤4¦o4«Ø4šI4˜æ4š(4§Ä4M 4'>4tB4‘£4“ù4ÙC4Di4¦ì4ô4‹Ø4"%4Ì#4¤Õ4£W4£4µÿ4—}4—¾4˜Õ4™ë4ªÛ4Ë4‡4+õ4–u4ÏÌ4˜a4Ïi4ð§4âõ4æ4«×4­4Ÿû4™â4œ<4˜­4˜—4œî4§#4›H4¢æ4¢¯4¹4›4¥Æ4a€4é­4ïõ4¯14"4Ÿt4Ê­4÷4—¶4—4–¦4—š4ª 4É 4³4œû4˜‚4¡¾4³4="4É&4ÂÒ4±œ4«l4œ#4ž4ÑP4›‚4—F4—4–„4—J4›-4¨€4½4¦4˜k4™Ë4¨Ð4õö4¿u4§=4¡@4¬A4Ȫ4¡ 44˜¬4—4–»4˜„4—Í4˜F4¡4¥·4—Ó4–Ø4˜ê4¼4Ÿ4?4¡¦4˜ë4Ÿq4Ã4™Æ4™4±¾4˜å4–d4–N4–94–¿4—ï4—…4—á4–¬4Ÿt4¡'4™o4¨N4™Ó4˜ö4›o4˜+4¬æ44˜ê4–¡4–L4–-4–;4–f4–|4—#4–ÿ4£Û4ž4˜x4˜‘4š4U4™4¡–4©4˜t4–É4–K4—v4Ÿ84–$4—4–B4–V4œá4¡ƒ4œ:4ž4›D4š4 ,4·ˆ4©¢4œ34™d4™44–ß4–¾4Š4¤¬4–14–?4–Ž4–´4™l4žf4¢I4Íæ4¤(4¤û4¤Á4à¢4Ù_4ž³4—ä4˜¨4—Ø4™ 4ϼ4™®4—¶4–Ó4›4›²4—†4›¡4¸04Ý„4½4ï 4Ñ14½64¸Ñ4®4žG4 j4˜þ4˜94§4—‚4 4›¾4¡ñ4—·4—4Ÿ_4Í®4«Ó4Ø/4>U4r464«#4Ÿ°4›z4«ü4¢Ã4™G4–ú4–¿4–Ó4›Y4¬,4¢“4ž4¸4Q4äÙ4H4/í4X4¤×4Ö§4Þ4˜Ã4¼=4¯Î4—É4–}4–½4˜¿4´4Åœ4ª 4ž©4Õ4Ó!4ç"4R749v4§¥44 ‰14š¦4£°4n4ÐŒ4™4–“4—W4ŸÙ4À 4¦ 4£¡4½À4%[4Ã…4Æ|4×ð4­@4vÃ4  4Á#4¦Ý4,›4Ü34±I4£4¨û4›;4˜â4›ö4œÆ4±›4 R4O¬4ê‚42é4{ 4 a4%m&4ÆL4ï<4œ•4ñ14l¾4:æ4®&4£¼4›4™l4¤×4Þ¿4µ44‘v4%ë4Ï24RKà4AAº4‡‚¢4=MB4 ìÆ4{ä4dç4Ã4·Ú4À¥4 A4› 4˜ÿ4›å4­$4¼14ý34û 4ë4 ‡”4&3ù4¼TÔ4­´24 â4 ø4ä4M4Ïã4°ñ4ÖÔ4¬4›4˜Â4 þ4¬b4öC4é³4²Ã4ÓÚ4 ”‰4”,4&3û4õè4ï¾4ÞY4Ò4Ž®4`ç4Þ4¿®4ŸÉ4™:4œ"4Ïe4ºã4ʤ4µ´4'Ÿ4÷4²©4–¯4h24o”4Ç54g}4E4ÿ(4Ö‚4©;4Ç4š×4³¿4—Þ4›4 4ª»4$¤4æŒ4À®4Ä4ü±4< 4K84¼4š!4õâ4¯È4§¶4¢Ï4›P4®‡4œ 4–¸4—r4™Ä4¥+4Ë„4¢ 4Ôù4Ðn4•14G4íW4—i4ð4ìk4²*4¦ô4ÖV4àÇ4¬4œº4–ˆ4–‡4™#4¦Ö4Æp4¥k4§$4Q4+—4Øö4Žx4j4¾4ù]4±V44œî4è4œS4šú4–o4–84—4š°4§ý4šä4¤h4©g4±Ã4«4€+4Ó#4¥ï4¬È4Ä24š€4f4˜G4— 4—4–;4—ñ4Ÿå4˜¢4˜¶4—ª4 34à4áj4©ê4Á,4®p4£¢4Ï)4"4—~4˜E4–Ü4–\4–„4–34™¥4¥c4˜¾4—Ê4š·4žŒ4›_4½4´*4ž>4›44Õ”4p4Ìh4›4–›4–)4–c4–84–B4—04—K4—)4™-4 Ð4 ¹4¥ã4ž64™ò4˜á4š`4°}4™34œ˜4—`4–d4–-4–b4–Q4–Q4–«4–Ã4—Ñ4¬œ4¨e4™4—s4—U4 Æ4¡f4™Ê4«ÿ4ž44—>4–›4–m4—l4–ž4˜4–Z4™ø4œ4˜°4š¾4š4ª@4˜4—´4¦¼4­o4Ø4œZ4˜ª4–‡4–P4–‘4¤4—¶4š4–è4™¤4›?4˜J4˜04˜Ø4ì¿4š$4š{4£ö4¥»4É<4šª4—4˜T4—Q4–¸4—t4£ß4˜4— 4–Ë4˜Ú4šÒ4™Ù4Ãþ4¿§4›†4¢i4àŠ4Ÿû4±ê4¢^4œº4à®4 ]4–e4–[4—¢4œ`4˜v4—#4ž½4œg4™s4ÂÉ4(o4¤ô4´d4Ó4®y4¬Û4©Ó4ÎÇ4«!4™x4–Ù4–04–4¥C4›\4—¯4›4£€4˜¢4Ùƒ4¾â4êÑ4¿,4…y4ç`4¸€4ij4Ö4­4ŸQ4¬>4™T4–¥4— 4™4›§4¨S4ʦ4¡4V¸4å†4øü4ïL4ëá4Á4ë42X4ßE4¢Ú4¢A4Î94 Û4˜24™£4¶4ª¨4¢¨4°4¤×4¥Â4ÙQ4‘4¯4³X4ãç4®]4š4}’4ÈJ4™Š4œ64´„4Ÿ 4˜–4šã4žž4¸(4äþ4ÀZ4Ý4Û–4øæ4rÀ4Ĩ4-4ðÒ4Öã4/T4à¦4ë4¦£4™ 4—4›04œc4¥¶4ÛÑ4í14öì4¿4Zv4Hû4Ù4!â(4ys4ºé4.4ä4M4´%4œÚ4™#4˜Ù4±å4ÄÅ4«&4×°4ÿ4ýÑ4Ku4—M4‹ç4'mø4‘-4 g{4„4Ýê4$/4ë'4ªÎ4ž!4ªÊ4±Ç4™t4™o4œ›4¤Ö4Äd4 ·4Ð4rß4 º4©â4ê4QE4¢4P®4ã04­™4¯¦4Ân4 Ï4˜¹4˜ 4š4£Æ4ÓÈ4§4£4›¡4°y4TY4¹Ó4,;4¬…42³4úo4 ¦4¹°4Ÿ4¡“4¬s4š4—v4—À4˜´4›74ÅM4¦õ4Ré4Ë4á)4Š4¼d4›?4¯54Žs4úw4±4í4¤&4¥á4¤Ü4˜£4—­4—Ó4˜4šÎ4ºX4«4«ö4ð4M4Íd4ªš4ÕŒ4#µ4¸ê4¬4š#4˜#4™O4šf4˜¨4—4—@4¤Ü4£v4¤G4¥E4ýÑ4¶˜4­`4Ï´4«¶4§ú4ê¡4ª£4¡Õ4šD4—æ4™‰4š4«‘4—ú4–{4™-4¢4­Ó4Îð4ª4œ˜4ž<4$½4­ø4º¼4´c4 i4ªa4¢q4˜4˜B4—Ä4w4–½4˜]4™×4¢j4Ó4Ÿï4k4›×4šÏ4õy4ªŸ4Š4™q4™ƒ4¡ö4˜4–˜4—u4—74–}4–S4˜m4—L4š4ó4›4—Í4š¼4´%4¡æ4¡¨4¹¯4˜¦4–‰4˜¯4–W4– 4–é4–¯4–¨4£¯4˜4—$4—q4›_4š4–à4žS4¢o4µ4œJ4œ˜4—Ç4–Œ4–Ç4–K4–c4š¤4–ì4¤=4˜þ4–š4–i4–q4—84˜44—Ý4—Í4˜Ù4ž4¶4—>4—•4–œ4–š4—4–û4—Á4–u4°S4™W4–ù4–ë4–]4–¡4˜ð4˜ä4£a4°Y4™`4˜é4™34—„4–•4–¿4¡t4° 4š™4–‘4ªÇ4˜M4–â4–Ê4–Œ4˜ß4²ó4®4š4£–4šÿ4˜4¶K4›L4–R4–=4— 4˜ó4–¥4–Ý4Ÿ,4—d4–r4–U4–‹4™Ç4³±4ºâ4˜"4­Ñ4¤ñ4¨Ë4°f4™{4–g4—ì4–°4¢‹4›„4—4–Ë4–“4–™4—M4£Œ4¡24šˆ4 ¶4ž74Û¯4¦4º4ߦ4™34˜4–€4–÷4œô4£Q4—m4›q4—P4–Ã4–Æ4˜4Þž4¡¸4—4¸4¹u4544Ÿx4˜4·À4™¿4™º4œv4˜Ò4–4œK4¤#4—o4—Œ4˜S4¢Ë4v4–Ê4ž4©Á4A¾4°}4”4¤f4šÔ4šf4®„4©*4™4–ñ4›U4¯4­4ö4¦h4²ß4¤x4˜ç4¢^4Ì4Þ!4‚Ü4·ä4¼Å4¹–4/4 ”4˜;4¢ 4š‚4˜ 4š$4Ÿ4Àà4´m4÷48°4jl4O‘4¹4~4ÎÍ4æõ4Éý4³4¼D49b4¦š4—þ4—Z4˜ƒ4š*4ºO4ŸK4Á‚4(34°4Ìæ4¢4HC4Æ¡4?34î÷4%K4ðk4¸º4·k4š<4—)4—{4ž‡4Æ4d4±A4©l4öË4ÖŽ4E4ÉÜ4nT4:³4 4öâ4­â4Óå4º\4 z4¨ý4©Š4 Å4˜04™l4¥4¿°4¾P4¼h4Ì>4Q64t4¬.4B4 ¥4rv4±¥4§4ž*4ºõ4¨ 4šû4—ú4—i4˜34¥o4›Ä4—˜4—Y4—’44ê4©p4U4çŸ4Ù’4‘4´Ž4Âå4»R4˜4—´4—³4—p4—‰4—4¥ñ4¨é4š4žî4™Î4{4œ¥4,Æ4˜Æ4ªO4¦¹4¤u4.4›4¹$4žä4šë4žK4˜Ý4—`4¦°4ă4¨14—X4—Ó4ü…4E³4b;4|4£ž4¦§4šõ4¢õ4˜ë4—†4–{4–k4ŸÒ4Ÿ¾4šb4–Á4—X4˜4¤z4Í4¦4B4›:4£W4¢U4Â4˜h4—ù4–Ñ4–‹4–p4–Z4—º4ŸÄ4›ž4–I4–D4˜-4¢Ø4Œ?4¨Ï4™r4¶y4š4½`4šú4–Œ4˜k4–„4–4™Þ4˜ª4–˜4—54–‚4–D4–_4—4Æ4ê³4›ª4šÑ4žÚ4(4¬e4—4—24£$4—4˜á4œ14™4–à4à 4™©4–4–V4—Í4™x4žH4˜š4¬ 4™¡4› 4¡ž4–É4—€4—4–ú4Â4¢¯4—u4–ƒ4y4—_4–»4–¥4›y4˜4—c4³Ý4¢<4¥ž4˜t4—à4–­4˜ø4—Ž4–}4˜„4£14—‡4˜4–Œ4–;4–34–T4–Š4–À4—4–ß4Á–4šø4–¾4—4–þ4–i4–c4–r4–€4š4–4¯j4—Ÿ4–4– 4–64–\4—w4˜Ñ4— 4½è4£l4–¡4—·4–ä4–Ï4–‘4–_4˜@4¥Á4—‹4¡o4–ý4–-4–74–I4–E4šý4¨“4 4¡Á4šé4—Ì4šÊ4–½4 4˜ñ4—´4–Þ4—4–i4–ž4–@4––4 q4œ@4–p4˜u4™ñ4¤Ë4ÒÂ4šX4›A44–Ú4¥³4›e4—$4™*4˜:4–À4–K4–H4–¡4™š4Ã4 $4–g4™H4µ…4§4¥94¡ 4œA4¤N4¡«4˜º4–Q4˜4žµ4˜4¢B4—f4–¡4– 4—t4a4 >4—!4£ã4¯·4>ú4©Ö4—Ò4¼Œ4›4–l4–Õ4–g4—^4´N4–ó4—ª4œí4—§4—R4½Â4®Ú4–ñ4¢ý4ïµ4Ê24#÷4Î4 t4™„4©¸4+4—Ù4–4—õ4—4š@4»<4›È4˜34ÉO4Íž4Ÿª4¶_4ê}4·4E§4à¢4ÒÈ4¦944ž\4š`4®!4š~4–Î4–½4—/4¨ú4çV4žð4®4ð”4ðu4Hm4í45)4¸&4¶ó4£^4›4²ß4¯Û4¥4˜ú4—B4™‹4˜Ä4š|4¯T4°Œ4º®4@a4Wo4Ù4AÐ4ŒÀ4ob4>@4¼”4Ò4´é4™Ì4šµ4—©4šz4Êå4±T4œ“4—ô4ºd4˜4Á14’|4ª4àq4 40m4áj4× 4Ÿ¹4™'4·4äâ4¦p4—D4—i4˜­4À¦4Øó4¸®4Êù44üŒ4bð4ÿã4þ¿4-¶4¸Ù4«=4¡®4­W4­74›4—ø4˜4–õ4–ß4™:4 4˜›4—n4©I4øÁ4É 4ØT4Ìæ4ÕV4¸í4ž+4ª}4š½4–‘4—74—s4–™4˜)4Åz4šk4—£4›@4ßß4¢Å4è¾4_4Ä­4¨Ñ4E4›L4—c4–˜4™ò4›å4˜4—D4–^4–ý4ž~4—r4Ÿ4"4—Ç4—£4 ™4¿¯4ÈÖ4¤Ÿ4©‘4˜˜4–R4šÔ4šï4–ï4–G4–i4–c4–r4–Ý4žæ4¡x4–Ö4–4Ÿn4ÞÍ4Ë4¼4˜•4¥4±Å4™‹4—74–·4–24–*4–O4²x4—4–°4™Ö4˜e4–`4–4®æ4ªÜ4›ƒ4 ¦4–î4¯424–¶4›U4–ž4–(4–?4–E4˜,4¢l4œ`4–Ç4–b4–h4ž<4¡4—`4ª´4$4—54™ì4™4™­4­4—õ4–54–84–;4–_4˜e4–û4–O4–=4–e4»Ž4˜ê4–¹4žm4˜ü4—/4–í4›4²¢4™E4–K4—O4–e4–*4–@4–<4–:4–T4–s4žW4/4–¿4˜D4˜4˜^4–å4–¥4—4¦54œ.4–?4–+4–`4–A4–'4–$4–+4–,4–P4–u4–D4š¶4—¾4¡=4˜74–b4˜ 4—¥4–[4–O4–`4–é4±4ž³4–™4–"4—<4–T4–64–b4–£4—04–Ê4ÅØ4™4–B4œ 4˜.4–i4–4–q4™o4Ÿ4–ñ4–Q4–(4–J4–)4–d4–é4˜_4—4¥©4Ÿ¤4—24—ð4¢í4–»4˜4œ®4›(4–Æ4–i4–T4˜Ü4–ä4–B4–õ4—‚4˜ü4/4—4°j4›T4–y4œ4Åî4—¼4™04˜±4—$4–+4–"4–H4Å#4›µ4–‘4–•4šØ4–þ4–q4ÆÜ4ÖZ4™p4—¯4›Z4Ë4˜ 4—(4—4˜ú4–e4–P4–J4±z4™t4–f4–=4—î4—f4—û4§ù4°z4­s4›ç4ÀÒ4°¨4˜®4–Y4–A4×¥4œ†4–â4ž«4™z4–ô4–¦4—v4¦®4³$4œ×4–¬4Å´4·À4žÍ4áø4 $4¥ 4›ã4©Ê4¨4§14—È4–‰4–~4—^4ž¯4—É4—¸4©Þ4›;4œ!4¼ò4·›4Õ4õw4±o4ä4©É4ÆY4›4Ÿê4š4–ß4–”4—@4Ÿb4²¸4³74§h4¬4äŽ4´Ï4Åà4æÏ4´4Ýñ4Ö4žÛ4—I4—[4ž^4™Ç4™U4–x4–þ4˜W4Ÿ4±œ4¥q4²˜42'4Õ›4ß4ëi4Û>4Ê(4ñu4¤Ÿ4¤ 4§$4²‰4˜Œ4–ð4™_4š¿4™w4›b4Ÿ4š½4«4Óž4#š4'T47l4/Í4ÃÝ4àŒ4Ç•4¢à4› 4¿F4¢4™”4˜N4›`4™Ž4šÃ4›„4µg4ÞC4¬ç4ߟ4ðÓ4Ù;4,”4ÜH4¤`4§î4ž¾4š]4žæ4—94—=4³à4˜l4–Ë4—ù4—£4™ý4¬G4ª"4½Ò4)<4Ê4·‘4JH4¥14˜ 4™4—C4­ö4±44š4—?4–u4˜4–²4œ!4¾%4¢4Ÿc4¦\4üq4£ 4¾‚4ê4š¦4–84–ñ4ž4·/4šÂ4–þ4–14–;4–`4–B4—~4¸“4˜/4—s4 ê4­õ4Øñ4 á4¦²4—ª4š°4–z4˜Q4—c4–O4–]4–,4–J4—¸4ú.4œ—4–¨4˜4™å4 ‘4´4¸£4—4›ê4š<4Æ*4¥ç4–t4–(4–/4–M4—þ4–Ù4—O4¤¯4—4–?4–Ö4›4› 4§%4¨¯4—34˜à4–¨4ž-4œÆ4–™4£B4˜B4–F4–C4–{4–G4–Y4–K4—?4—ü4°â4™*4¢Û4Ÿ4–è4–°4–?4›…4¯4–f4–½4–?4–(4–*4–4–4–4–4¡4³#4žÍ4˜4™Á4˜4–ÿ4–e4–94¦w4™§4–ö4–4–!4–4–54–%4–14–64–04–{4µÚ4˜4–ë4—€4—R4—R4–n4–M4›4—*4·4šC4–[4–14–,4–04–€4–J4–N4–Ü4–)4™q4—04¥\4œz4–S4™54—î4–F4–-4–4–4™_4–Ë4–#4–I4©b4˜p4–y4–o4–]4–½4–€4ž04˜D4–14£æ4›é4–I4– 4–4–/4–Ð4–44–)4–54˜4˜24¥ 4–Ï4–4–4˜¿4™T4–Á4–ô4Òx4™½4–*4–N4–@4–4–4–,4–ß4–ª4–V4–•4È–4™”4—j4–”4ž34˜m4–8474Ù{4˜74–<4–”4–+4–4–4–+4ž 4™=4–|4–ì4ßÒ4™c4–J4˜€4Ñ_4™ð4–‹4›Š4¯˜4–ë4œ4¯Á4—ÿ4–-4–24–H4—â4— 4–y4–‘4»À4š4–×4£ü4Ÿ4à 4˜L4¢;4 Y4–ä4–I4¡C4Î4–ì4–y4–˜4–†4–M4–14›Š4¨’4š²4—ž4—Ã4®F4=4ž4®x4°¹4K434›¶4™D4˜¨4–õ4–æ4–c4—j4²?4ï4™H4—¹4—+4¡4¦74´¤4¤ê4¤©4×ú4©4š}4›Z4œ­4£,4˜+4–˜4–ñ4—q4š 4œ€44Ï4©à4Øÿ4¡…4¼µ4Ký4¦ë4 §4¾·4¢p4˜ 4–4—£4˜ù4ÏÏ4–O4–\4–Ç4˜á4¢p4Š4¾÷4ü‘4´4ÑN4V?4°.4ªN4ë¸4œ¦4˜Ù4˜“4˜Ÿ4—›4—O4§4—Û4—14—(4—‚4™14·‚4´4à²4ºº4)4ëq4¸u4ât4à:4«ï4¡„4›W4—©4–Ä4¡²4à 4žX4š¥4šX4œ¥4¡Ö4Òš4¬4©p4áÓ4ú34¦34™4™b4žÀ4›H4–À4–24–‘4–ñ4— 4–Ì4Õ4)4—m4ši4™g4ã4Ûè4±84¤64­4¢Ô4—Œ4–z4–64Ÿu4Á¾4šÇ4–/4–(4–G4–U4—†4£E4Ç„4¡)4›ë4§4£;4ɰ4µ4—,4–J4˜¦4¨|4ŸŽ4˜o4–Á4–44–"4–&4–,4˜G4¤E4œª4˜4—ß4œ²4Ù¸4Î4œ24—ä4šD4˜”4¬4—f4–!4–44–64–è4–é4;4™4˜F4¦}4˜24— 4¬[4¦64˜4šÒ4™n4›…4–Ù4˜4ž}4–{4–+4–4 4O4–ø4–44–C4—@4—4—À4ÄA4šÿ4˜P4—4–™4—N4–º4—(4¿¾4™24–K4–34–S4–O4–'4–94–24–U4—4­°4¬J4—D4¡4–¯4–O4–}4›4Ÿ·4˜€4— 4šv4–14–4–4–”4›4–Ÿ4—£4–4¶¸4¡/4–¦4˜+4–Z4–K4—4—T4—'4–X4–¼4›”4–B4–*4–74–T4–Í4–+4¢4–Á4˜–4—W4˜í4¤4–É4–G4–õ4–p4—Ç4–Ü4–F4–m4–-4–"4–%4–f4–‰4´4™Ÿ4–?4–O4—84˜)4—Ú4–ì4–§4–24–$4– 4–_4¡=4˜«4–&4–4–{4–•4—E4—ã4–k4–S4–_4–)4­©4š 4˜W4™4–;4–4–4–4˜4–¼4–-4–4–G4¢&4¬–4—4–'4–K4—n4–¿4™a4–ª4©E4š‹4–,4–4–94–4–%4–+4–;4– 4–4–¯4œ?4Ÿ:4—Ô4–y4¡U4—4–M4› 4 k4›4–<4–.4–,4–4–4–s4–w4–$4–$4–«4³$4Ü4—4–Š4¡¢4œ4–P4¡Ô4š4˜­4š~4š‰4–m4–Õ4–n4–ì4–×4­4ŸO4–4šu4—Á4–D4—ˆ4˜(4óo4šh4ŸÉ4š4—#4–i4šƒ4©84™¿4–}4–’4–>4–Ë4–c4—#454¥È4˜ª4¢U4œ4´n4˜°4°¯4ž¹4˜4˜ž4˜V4™K4–½4–þ4­4–<4—4˜è4˜^4™4œ4™p4ë 4¥4¡X4™þ4 T4ËŠ4¥4—ì4–™4–|4–á4–k4—54ù4šü4š&4˜Ã4˜S4›Ò4™ˆ4Ïs4 .4¤?4¶4œ·4©g4¨4®4š4–e4–(4–<4™D4™§4–À4–Ï4—Þ4™4šä4ŸØ4Úì4¢–4©S4ÂE4à 4¡Ø4®4—c4–É4—ö4 i4¢ 4—„4–¢4—4—X4—’4—Ø4˜[4À4Í¿4§ö4 k4Ù÷4ËW4«x4¼Î4Ï®4®ƒ4Çñ4ž¡4˜Ô4—œ4—94˜ì4˜-4˜~4˜Œ4¡34¥4±š4 ÷4¹Ê4XÚ4¼P4›»4˜ö4šm4¤Â4šf4—B4–44–w4–K4–K4–‡4—4–Ã4›Þ4›×4™Ë4ŸÃ44Ò4°[4ž'4ž+4—Ð4–{4›ù4¦S4šÃ4–ù4–+4–%4–:4–^4˜Â4¢-4ªÞ4š¼4˜œ4™f4¢â4è4›G4–Þ4–D4–H4–Ä4™4 4— 4—`4–ª4–(4–d4œ4œá4—ê4©‚4—Ô4š 4¶Š4§ó4—<4–ž4žˆ4šñ4Ÿ34–—4–Ÿ4–@4–/4–A4—H4x4­4˜j4–½4šÙ4—W4Ÿ 4™Ù4©4›4–y4—ƒ4–Œ4—:4˜A4—Ò4–Q4–84–°4šb4˜P4–³4–14–R4—&4—ú4œV4š24¤A4–Æ4–4–4–?4–54˜Í4–F4–"4–N4K4™#4–14–/4–!4–14—ˆ4™ë4˜¯4–È4Ô%4™%4M4–j4—;4˜4–C4–54–ã4–s4—J4–4–¹4L4–h4–4š4›Í4™—4–g4™a4–\4—b4–Û4Ÿa4™¯4–34–84—4šD4–s4–64–T4—4–44–¦4–‘4˜þ4œ4–¾4˜4–Z4–^4—Œ4š<4—°4–:4–)4–34– 4–4–4˜34™V4˜‡4–Ž4–M4–=4–;4— 4œ=4–˜4–J4–04–4–“4¥#4–þ4–+4–4–4–4˜4™Ï4—›4–-4–v4–N4–4œA4©n4–Ë4–p4—4–*4–T4–“4–+4–.4–/4–*4–œ4–Š4–Ô4ãE4˜À4–&4—4–Ï4—…4˜a4—¯4–ò4—¿4—‘4©Ì4—4–$4–h4–=4–+4–(4–%4–4š34–S4–„4¡k4;4–_4š?4˜Â4›4–W4–[4™Z4–t4–¯4§¢4–94–,4–-4–<4—Ä4—e4–Z4–e4˜¨4¸4—’4žÍ4™4šN4–m4–94–4š 4˜ý4щ4–U4—þ4—C4–Š4—Ÿ4—n4–Q4—,4–t4×Ñ4›4—r4˜ë4›“4˜á4–´4˜­4œZ4–®4—d4–04–,4–44–°4™ 4£Ž4›ê4Ï4˜ø4j4—„4›p4—¸4™Í4®ý4š„4–Á4–<4–Q4—64–%4—f4—š4—t4šƒ4²m4™M4Ÿ®4Ÿ4™û4™n4ÒV4·4™44˜T4–¸4–d4–G4–@4–}4šÈ4›˜4š4—·4—B4Ÿl4—î4š&4¨ 4ž*4š4¢Ò4©ƒ4¥'4Ÿ>4¡4—I4–G4–D4–¾4˜}4–Ä4–Ÿ4—M4˜Ý4´“4›i4žì4™Ë4›"4µ”4ùC4Ÿ4—¥4—œ4—r4–Í4–Ò4—¦4™4–…4™è4˜²4—ð4—C4–þ4¶n4ÏÏ4 @4žâ4ºÐ4©Z4¡A4¬ô4ž¦4šÎ4šX4™I4˜ú4§ 4–n4–k4˜+4²4˜4›’4žÀ4(4šì4¥W4"l4¡®4˜¿4˜l4˜A4›˜4˜4Ä4™¿4–ƒ4–04–'4–64–G4–74šH4™+4¦Í4›4™.4À4 4˜44°¾4[4—p4™Ô4ša4–—4–>4–/4–4–)4–Y4›]4˜ð4˜õ4§=4˜x4—å4žô4ú4›n4—@4–‡4–M4–½4–m4–€4–44±†4žv4–]4–U4«4—$4–æ4¢š4–ç4¿4™·4¢l4–Ç4–@4œ¾4˜¼4–²4–‘4Ÿ4—74–È4–M4–˜4—k4˜¡4–u4–H4—Š4œf4ßu4›|4š=4—’4–-4—š4–p4–M4š/4´ö4˜e4–4–4—œ4˜+4–64–"4–)4–i4˜=4ž£4§`4™+4–.4—4š4–J4– 4–H4–Ó4–'4–"4—·4œ˜4˜ê4–,4–4–4—Ù4™æ4—t4—ß4› 4–u4¢¦4–î4–'4–L4– 4–4–4–;4–U4˜¯4—@4–T4–4–4¨j4˜4–Ï4–y4–w4˜Q4˜=4™ª4–›4–ß4–$4–4–4›§4–b4–?4–I4–-4–'4–D4—4—Á4™Y4–y4–D4š64–ï4œl4–w4–A4–!4–4–4–&4–$4–14–S4–y4–u4–c4–Ý4–=4–/4–e4šV4˜þ4–\4–H4–24–"4–}4–4–"4–(4–#4–n4–A4–_4–]4–P4¡94–—4–*4–†4šÁ4˜4–4˜Â4–H4–4–4–4–-4–G4–±4Í4˜C4–,4™64–]4–î4š²4—4–t4—h4—4–©4 4–¤4— 4–'4–#4–04–T4—B4™À4–N4–G4–½4–T4–74£4™Ã4–G4—4 ±4–¹4–x4–<4–_4–#4–04–œ4–Q4—ê4—®4–N4–ù4–Î4–t4–s4—4œo4–Ž4—ë4ªx4—-4–94–A4–O4–‹4–•4˜24–E4–?4–D4—e4­#4—4–q4–´4–d4ª4˜þ4–y4ŸK4©c4—G4–H4–š4–×4–h4–I4–34–*4–I4–4—ü4«¡4™ã4ž,4—B4˜à4—Ü4—ñ4—å4˜4¢ª4š\4–Û4–j4–S4–M4–:4–P4–‡4–“4šÊ4¹z4™ò4˜74›õ4˜Ç4˜_4›y4™Z4¤64˜“4—F4–ž4–j4–L4–G4–ò4––4–É4—L4š“4¢±4—ç4—£4¢Ö4šÉ4˜y4˜4·"4©Ê4˜{4—’4–µ4–`4–E4–<4–ð4—-4—%4Ÿ\4šû4˜Þ4—¸4—ú4—ÿ4—g4 ð4Ű4¥¿4˜R4—s4—!4–4–64–D4—>4—+4£ˆ4› 4—H4–†4–ó4ž+4ªP4™¤4§4‚4¡‹4˜S4™ 4˜/4—’4–¿4š;4š¹4¾ò4–4–f4—y4œ¼4—q4—‰4˜g4˜i4™<4º4³N4™4˜4—ì4—­4—˜4˜o4®Ü4›»4—4–74–&4–+4–K4–\4—4—4š_4°ø4Ü4g4˜á4—i4˜ä4—ù4˜š4®&4œ"4–N4–,4–(4–#4–,4–34–«4–Ö4—f4›µ4¬/4›4˜­4O4—K4–²4–—4˜$4£D4–°4–4–)4—84–¢4–H4–E4–­4–€4–Å4›4£!4œi4˜†4˜Y4–¦4–i4–À4–i4–‰4–]4¤“4˜É4–:4–14–=4–¢4–A4–B4–V4–¦4™%4¢/4£4—M4–w4–R4–¶4–L4–H4–Ã4¥ó4˜§4–64–94–=4£“4–ö4–G4–24–#4ž­4š4Ÿ4–Á4–>4˜04 …4–±4–<4–84–”4–24–:4–4`4ž4– 4–<4–+4–#4§½4—Î4–ø4–’4–i4—:4—J4–>4–¼4–>4–%4–&4–#4–N4°)4—\4–&4–*4–'4–ø4˜4–ö4–…4–64šÌ4–‹4–M4–B4²34—ú4– 4–4–54–4—!4–b4–/4–34–P4–’4–ß4–ì4–Ž4–=4Î4–Š4–f4–04— 4–@4–4–?Mr?M(?M ø?M!?Lý>?Lòò?Lå€?LÕX?LƵ?L·™?L¨í?L›à?L‰:?L}"?LTA?L\\?L:Í?L1z?L2ý?L-ü?M–?M‘?MF?M?M ?Lýu?Lñm?LÙÀ?LÉ{?L¹Z?Lª¦?L¨ø?L‚û?L_•?LLo?LG„?L8Û?L0½?L,´?L(?M ¨?L'¸?L¡?Lc?LN?M*É?M0H?M1$?M- ?M,º?M4ò?M§?Lè—?LÈË?L±â?L£?L“ ?Lv?LRk?LGÌ?LLÎ?Lp?LL?Lñ?L J?M1 ?M7I?M:T?M3w?M2B?M-Ö?M±?Mù?LÃ-?L®'?L?L‰d?Lq(?LP8?LA?L@Ñ?L†?LØ?L Þ?K÷?M7a?M=Ò?M@"?M8f?M8Ø?M5@?M(?M?LÞh?L«?L‘´?Lƒ??Ln™?LF¹?L3µ?L,¥?L$Ñ?L)¿?Kþû?Kë ?M>$?MFÆ?MK¼?M33?M:?M6ô?M)!?M?Lí?L©K?L‘à?L~d?Leê?LD_?L.?L$Á?L!¶?L*?KðQ?Kñ?L(?Lu?M‰?MŸ?M–?Me?Lëj?LêN?LãW?LÔš?L¿…?L³]?L¦?LŽÿ?L~“?Lk?L^M?LX?LP=?LO?L-ç?L"x?ML?M0?M?Ma?M?Lû?Lè]?LØï?LÌG?Lº?L¦ª?L—2?L…ý?L}‰?LIT?LFü?L;u?L6š?L.ž?L ]?M"ª?M%`?MG?Mn?M ‡?Mæ?LõS?LâR?LØJ?L¾?L¡?L˜…?LÖ?Lg]?LCê?L;N?L1¥?L*0?L"ò?L(?M.•?MBò?MO?M($?M7?M ?M(?Läž?LÔõ?L¼e?L£E?Lœ‘?LŠ6?LO&?L8“?L3?L&2?L?Ló?L¬?M5?MBR?M,q?M1S?M)?M1?M ?LãÆ?LÎà?L·À?L¦S?Lë?Lƒ¶?LCî?L7¹?L82?L¼?Lã?L æ?L ƒ?M8~?M@¿?M@ ?M< ?M>?M6z?My?M?LÎ1?L«‡?L Ý?L³?Ltñ?LJM?L<Ý?LB-?Kõµ?KÿF?Là?LH?M?Â?MIû?MU:?M;v?MF?M=7?M(g?M+?LÏã?L­g?L–?L†Í?Lp ?LAa?L4k?L[?L?L™?Küp?Kò?MGQ?MP_?MVþ?MH ?MOÿ?M]J?M,§?M W?Läù?L·=?Lœ?L„Å?Ln%?L)ü?Lq?LM?L À?L B?KôÈ?Kè®?MP+?MZ ?Me??M@Þ?MPµ?MW?Mx?MDJ?MJÜ?ME?Mç?M¡?LðE?Lãù?LÀ ?Lg~?L?»?L4°?L&ç?LO?L¦?KàD?Kî}?Kþ?MOÓ?MAû?M8´?M< ?M=å?M4M?M ó?M|?LÚ?LÅB?L·ª?LzÕ?LX¶?LAÑ?L0c?L/ï?Kò¼?KëŽ?Kö¯?KýÁ?M?`?M>Æ?M<?M:„?M9·?M"†?M$¦?LíŸ?LÅû?L¾?L¬x?LŠp?Ln.?LNæ?L<ò?L8Ï?Kð-?Kþ–?Lõ?Lq?M2r?M7”?M9>?M1h?M'÷?M+?M»?Lî\?L·˜?L¹n?L°?L–?Ll–?LMÌ?L>¾?L3­?L?L!ò?L ?Kú™?M(Ï?M0+?M4ã?M&µ?Mõ?Me?Lý?LçÇ?LÅ?L½¾?L¿?L?Lp?LEF?LDŽ?LBÙ?L(v?L…?Lm?L?Mç?M(¾?M4\?M#v?LþL?LùA?LêF?LÝ÷?LÇ ?L¼?L®M?LK?Lwv?LUÁ?LR?LW?L1Å?L!?LÉ?L?M?Mc?M(E?MS?Löc?Lð0?Lãš?L×s?LDz?L¼)?Lµ?L§?Lt¤?Lde?LYÎ?LS7?LB ?L6¤?L*?L#ï?M"!?M!R?M?M¢?M•?M Ç?LèD?LØH?LÏ{?L½ ?L¨?L’m?LyJ?LkÈ?LPˆ?L<Þ?L6Í?L5Á?L)N?L?M+5?M,ê?M%ö?M#Ÿ?M?Mä?Lü°?Léê?LÚÐ?LÁÿ?L¬¤?L•Ÿ?Lk`?L^û?LAî?L/«?L*g?L#ß?Lö?L?M5É?M;g?M2|?M>¾?M ?M÷?M?LðÒ?LÝ6?LÀ?L¤ ?L”6?L… ?LZ4?L"c?L!i?Lm?L?L ¥?LÜ?M>5?MD–?M@ï?ME?M7Ÿ?M8[?MŸ?Lý)?LÚý?Lº’?L¤ï?L”Í?LÅ?LNƒ?L?L–?L 2?Kþ‹?Kþ³?Kü«?MD€?MMñ?MR‰?MOž?M`Í?M-Ò?ML?M[?Lß?Lž?L¥?Lˆõ?Lt×?L? ?LQ?LË?Kû[?Kõk?K÷?Kô©?MN?M\?Mno?MKÓ?MiÝ?MA?M6Ø?M#~?Lߨ?LËQ?L¦?L|Z?L\?L0Ý?LÓ?KýN?L ?Kë?KäÑ?Küƒ?MX¼?Ma¡?Mkk?M_Æ?Ml?MsÊ?MK£?MU?Lè?LÅ—?L¬?L•\?L;¸?LD?Kûé?KõI?KýÏ?Kø-?KÚ?KõÒ?Mhs?Mc`?Ml?MiÕ?MsB?Mzâ?MTÄ?MO ?Làò?LÈx?Lª?Lƒñ?LCe?L …?Kéâ?Kêt?Kí›?KðÒ?KÚg?KÓ)?M~C?M_Ž?Mid?MpÇ?Mw˜?Mt—?MfK?MRã?LòZ?LÐI?L«I?Lyd?L=?LF?Kç¦?Kãå?Kã`?Kà?K×%?Kפ?Mo7?Mrñ?MtÚ?Mu ?My±?Mv|?Mo?MPœ?Lú-?LÚÂ?L­ñ?Lvñ?L"ê?Kú¤?KëÊ?KåË?Kç„?KÚo?KÆ×?KÁÈ?MkÉ?Mqe?Ms?Mz?M€?Mz…?Mj÷?M7ß?M©?LäÔ?L°µ?Lgß?L¶?L:?KõY?Kä±?Ká´?KÌ?K¿i?KÆË?MjI?Mmò?Mm?Mwë?M„Q?MG?MI?M#Õ?Ms?Lî°?LÄ$?LZÏ?LÌ?LÐ?L D?Kç­?KÅl?KÉn?KÌ¿?KȆ?Mgš?MiÈ?MhA?Mkö?MtO?MuT?M@É?MP?M?Lë³?LÍ ?L^}?L%M?LÄ?L ?K÷)?KÞ?KÀ>?KÒ¯?KÒø?MfÂ?M]“?Mea?MZ‹?Me^?Mh¼?M0À?M?Lûº?LÛË?Lº+?Lu^?L8&?L%ó?L ?Kóé?Kè$?KÆ·?KÙà?Kîw?Mf@?MSÜ?M[a?MK?MSµ?ML?M-?M™?Lô¼?LÂâ?L«Ò?L‚¬?LTÂ?L5œ?L"Õ?KýÙ?Kàò?KÙF?Kãû?Kíö?MPÇ?MSg?MRØ?MGê?MT‡?M*Ü?MŒ?M–?Láé?LÉö?LªÐ?LŠŸ?Li,?L:ð?L'Q?L·?Kæô?Kì?Kñ?Kö2?MAw?MJ?MV~?M>?M2£?M(ö?MD?M?LÛ?LžQ?Ld?LSÑ?LDª?L13?L(?L…?LÁ?KÝð?Kæð?M\Ó?M]Ÿ?MU÷?MN£?MU‹?MF ?M=?M:?Lû¾?LÉü?L ?Lys?LQ?L@\?L)?KøÍ?Kç‹?KíÝ?Ká?KÚ~?Mhê?Mlé?M„v?MUÊ?Mgl?MYq?MN¤?MAµ?Ms?LÍ.?L›?Lê?LM=?L4!?KÐd?KÒ?KÖr?KÝ«?Kç„?Kˆ?Mw?MŒÞ?M‰?Mxo?My•?MzÌ?Mi{?MGç?M&9?Lâw?L£Ö?Lp¥?LS?L í?Kµì?K·ï?KÃ?KÍž?KàŠ?KÎ@?M‚š?MŽ?M’©?M˜û?M¦Õ?M³e?MyÑ?MI\?M.å?Lÿ3?LŸ#?Lb7?L8g?KðP?Kœˆ?K¦T?K°?K·]?K¾¬?KÉ_?M‰F?M–­?M¥Á?M°Ï?MÂ?MÆ&?M“z?MhF?MI¶?M ?L-?LO¿?L½?KÉ÷?K>?KÀ?K¬f?K—H?K§Y?K²Ã?MŽ¡?M¢+?M§è?MÁ/?Mã?MÁš?M¬J?M£à?MLl?M.?L°9?L5¿?Kù?KÆ$?Ktð?K•Ã?K—?K†,?K¬E?K§ ?M‘F?M¨r?M´Ì?MÍ"?MÚ¿?Måß?M¢É?M§ý?Mq?Lø¨?LµR?L3è?KÙN?K»?KtÑ?K‡²?K‚ô?KƒŸ?K ¿?K£`?M“…?M§î?M·Š?MÉÝ?M܉?Må¿?M³½?M”õ?M?Lõ1?L¹J?LBà?KÄ—?Kµd?KŽƒ?Ks»?K€c?K…?K™”?K­‘?M’y?M«™?M¦ð?M¹î?MÖ?MÛ.?M  ?M™¨?Mi,?Lö?L®c?LHŽ?Kæv?K·0?K£Ê?Kƒ…?Kxø?K‹K?K*?K¡?MŽv?M®H?M•&?M¤ ?MºA?MÅ2?M¥ü?Mzâ?M6‰?Lüe?L¹‡?LNF?L£?KÌå?Kž”?K‘â?KŽy?Kš?K´~?K’Ÿ?M„K?M’Z?M–[?Mš—?M¢é?M¬ÿ?M„¤?MOÚ?M1‚?M ?L ?L[Î?L)?Kø¶?K§î?K¬Q?Kªõ?K¥?K¼ˆ?K¯~?Mx©?M&?Mˆ˜?MŒD?M˜>?MrÙ?MhT?M:(?M!‰?Lòð?LµF?L]V?L+9?L¬?KÙÀ?K××?Kº ?K©?K·2?KÁ«?MhÂ?MtI?Mv?Mx¶?M‡?MU•?M<“?M$X?M”?Lç¼?L½³?LyG?L0ÿ?LJ?KüH?L?Kµ€?K¹¿?KÇä?KÛ]?MZ1?Meo?Md>?M`Â?M^}?MW˜?M'Î?MÎ?M‹?LÌd?L³>?Lš¼?L=ä?L)”?L¢?L:?KæŒ?KÖ?KÛŸ?Kî“?MJö?MTì?MYK?MM=?M?k?M5õ?M ñ?Mq?LøŒ?L¾ò?L£Æ?L“?LT$?L9l?L ü?LÐ?LL?KØo?Kå&?Kóç?M;?MDÉ?ML?M8ô?M+õ?M$N?Mž?ME?LÍ?LµÕ?LŸ?LŠ0?LiÄ?LIò?L;º?L:x?L,,?Kã“?K÷?Lù?M-Å?M47?M7#?M+/?M%A?M&?M ç?Lö•?Lß›?L£4?L˜P?L…Ç?Ln?LY?LL7?LB?L5?L Ø?LÉ?LI?M;˜?M:™?M*Ÿ?M)/?M#©?M9?M©?M4?LÞr?LÍ?L½¤?Lj]?LWA?LO•?LB´?L-ß?L é?L¼?Lû?L K?MU±?MSÿ?M%å?M4|?M7Ç?M+™?M F?Mi?Lò ?LÖÑ?L½ô?L`#?LI?LC?L:?L?L¼?Kû¤?K÷>?L Â?M]Û?M^ß?MA±?MMs?MZÎ?M5Õ?M-–?M"?M?LÚ?L¯n?L[?LAF?L4Â?L0?Ld?KÞ¶?Kâï?Kà@?Ké?Mff?MkD?Mfœ?Mk¹?M?MF¥?M<ã?M7?M?LÞ?L½Ÿ?L_‰?L3q?L N?LŸ?Kúå?KËæ?KÊ ?KÏÉ?KÓî?Mui?M}W?M…Ã?Mƒ­?Mé?M`V?M_ƒ?MOó?M2Ø?Lä©?L§á?Lg¨?L-J?LL?KÌ«?K»m?K¿S?K¿Û?Kž?KÂ?M‹Ø?M¢ã?MŒæ?M”³?M£4?M—?M•4?MZ´?M7U?L÷+?L´?LW?L÷?L ?KŸô?K†?K¡¢?K¯h?K¾:?K·¹?M¡ ?M¢±?Mž?M³ø?MÇç?MÑ{?M°?Muã?MZa?M!ë?L¢?LK?Kñ»?KÏX?K‹M?K{¶?Kyƒ?K“-?K©?K¯à?M¡±?M¬Î?M¿´?MÐŽ?Máï?Múá?MÔ°?Mœu?M‰Ü?M:»?LŽ>?L) ?KÔÄ?Kª?K^ý?Ke?KeY?Kyð?KŸD?K›Ï?M£Ø?M¶Ü?MÁÌ?Mã®?Ng?N)Ê?Máh?MÓ?MŽ)?MFÀ?L™J?Lž?Kºù?K‚ç?K:S?K_L?KR¥?K^R?K‘L?KŒƒ?M¦?M¶$?MÎ,?Mïu?N4?N0¬?MîÒ?Mê·?M¯u?MJa?L½~?KìÙ?KšÙ?KM=?K?s?KZV?KA2?KNm?Kx?K„\?Mª?MÃô?MЪ?Mêü?N–?N-;?N[?Mܱ?Mµ°?M0Û?L«?L^?K‰Ž?KHw?KJ ?KC¤?K2Ù?K_#?K}t?KvH?M§ù?M¦?MÁ³?MÜ ?Nm?N¾?Mí1?Mß—?M¨‰?M! ?L­å?L1'?K¶Æ?KhÎ?KLE?KK0?KC¬?Kj:?K‚ ?Kq(?M¡h?M´å?M»ä?MÑP?Måj?Mï ?MÐë?MÕ?MYP?M7?L¿B?LJ?Kê-?Kt?Kl£?Kl ?K^ú?Kj?K…s?KŒ?M•ã?M¨?MºÌ?MÃQ?M»½?M±ë?M«R?M£¯?MN_?MP?L¦§?L\g?L À?K§y?K~è?KŒY?KœÔ?Ku?KŠ:?K•ß?M„˜?M–°?M¢?M»?M¬Ð?My?M~ç?Mq¦?MMí?Lÿß?L­w?LnÖ?L‡?KÍ?K² ?K¹G?K¼À?K‰"?KŒH?K¥(?Mus?M‰‘?M„?M–\?M¥c?MYI?MU?MD¥?M1$?Lõz?L¦—?Lq¤?L.u?L†?KÜà?Kʵ?K¼8?K«j?K«%?K½0?Mh¦?M€w?Mdg?MmÞ?Mp¡?M^À?MBe?M+¹?M#ž?LÓR?L¤?Ls?L;ñ?L-D?Kðó?Kã>?KÖp?KÆ\?KÄD?KÒ?MV&?McÍ?MiZ?MQ(?MIâ?MIŽ?M3\?Mé?M è?L׿?L‡?L{v?LQ%?L8ä?L‹?L ä?L?K×ç?KÌ*?KàÖ?MCÈ?MMw?MVT?M>Û?M65?M6¨?M4°?M¿?LË?L¸­?L™?L‚?Lc½?LO@?L: ?L¡?L”?Ká‚?Kç+?Kõ±?M5A?M:ˆ?M:Ö?M1?M,ð?M(µ?M-ÿ?Lún?LÄ ?L­—?L›À?L…­?Lk?LWð?LK\?L0w?L0\?Kø¯?KþÄ?LÀ?MA™?M?J?M6©?M2;?M-õ?M)M?M$-?M[?LÈÜ?L¿‘?Lµ®?Lo'?LVˆ?LK-?L@L?L"?L?K÷É?KýC?L‚?MT«?MRR?MB¦?MD®?MGR?M:j?M/³?M ?LäÒ?LÎ?LºÏ?Ld?LGï?L=1?L@k?KûÉ?Kûè?Kèb?Kì!?Kõâ?MfÁ?Mhg?MUG?M_C?Mz¬?M=?M7c?M/E?Lýï?LÜz?L¹Ý?LZ9?L:R?L$n?LÐ?Küû?Ké–?KÆô?KÒË?KÝ«?MrL?M}`?MsŒ?M}°?MÕ?Mg]?MJª?MAy?MÞ?Láþ?L»‡?LhÏ?L Ë?L?Kðé?Kë?KÐ^?K®?K¼?KÆ ?M}I?MŽ×?M˜5?M¡£?M£Š?M€%?MtN?Mné?MM?Lá?L©]?Leš?L ?KêÉ?K¾?K°«?K»\?K§ƒ?K©r?Kªý?MŽÚ?M¨²?M«I?Mº«?M˨?M³r?M¨ç?M›Ä?Md9?LëŽ?L±â?LKB?Kï¶?KÛ…?K‰ñ?KtÅ?Kˆ?Ku?K¨?K†Ï?MŸÅ?M²k?M¾¥?MÙñ?Mþß?MñÚ?MÐã?Møå?M¾¦?M?Lšp?L(Á?K¿f?Kß?K…?K@q?KHä?Kw>?K–’?K€~?M³y?MÀ?M˽?MóÔ?N ¬?N7î?N\?N' ?Mùõ?M¦?L‰±?Káf?K°Á?K(Õ?KGý?K?K!0?KS5?K}ô?KzÙ?M¹~?MÚñ?MÇð?N£?ND?NsÕ?N4”?N$?N#Û?MOY?L±(?K¤=?K[?J×e?K>?K ?K\?K2r?Keæ?Kq?M¸í?MÍ?MÜf?Nª?NP¬?N|?N`?Naí?N¡?M‹:?Lç²?KV§?K>‰?JÉ·?Jê‚?K ß?Jþ±?K E?KPF?KiÎ?M¹£?MÖã?Má$?N Ý?NG?Nw…?Nr$?NG ?NQ–?M{Ö?LÙm?K‹?K9þ?JÁp?JåÌ?K þ?Jä]?K#ù?KQ×?Kbë?M»‚?MÕ›?MÙZ?Möµ?N3?Nz†?N8r?N0Ö?N49?M:t?LÑr?KÖ(?KE=?K 1?JóÖ?K ?Kå?K0¹?K\ô?K\–?M¿3?MÁY?MÐÎ?Mèý?N¿?N6ä?N¿?Mýv?MÍ?MAû?LÒ®?L^d?Ko?K*?K#H?K7÷?K&?K÷?L=?Kö?Kþ?Kë§?KÝñ?Kêì?M=à?MB€?M@Ô?M3?M3 ?M/È?M*½?Mz?LÑ?L²µ?L¤H?L‹Å?Lf8?LPñ?LYÿ?L@?L-?KùW?Kù2?L?MH²?MF_?MA{?M9Ÿ?M4@?M:3?M+¸?Lÿè?LÚø?LÉÒ?L§?Lu—?LZ ?LF?LF?L—?Ly?Küv?Kù=?KýL?Mc‘?MYø?MH§?MK“?MMd?MSY?M:4?M.?Lð;?LË×?L¢å?Lw°?LN8?L)1?L%®?Kÿœ?Kñ­?KâÓ?Käˆ?Kë ?M‚ö?My@?MIœ?M`ß?MoÐ?M`W?MMN?MH?L÷.?LÙl?L£è?LjŸ?LAÿ?MÚ?Lç¢?LŽ’?Ln?L<›?L 1?L×?KÝð?K¼ª?KÁF?KÄ?KÏ-?M‰¡?M…õ?M‡G?M¢L?M—•?Mw'?Mrÿ?Mc¹?MG?Lå?L¨t?Lp÷?Lø?Kã\?KÅÔ?K¬ ?K´¾?K©&?Kª ?K³z?M•Ó?M¬Í?M¬ú?Mº?MÀ?M¨“?M¨Ô?M“Í?M€ ?Lûv?L¶?LWú?Kßž?KÑW?KŸ?Kfƒ?K…¯?K”O?K²?K?M¤?MÀÚ?MÆÁ?MÝå?Mø_?N …?M×p?MÓ ?M°U?M<ð?L£u?L(u?K¶Â?K}—?K:Q?K7ø?KW&?Kz?K™Õ?Kp¾?M¬v?MÌ{?Mìj?NÎ?NRÞ?N-’?N#¯?N,?Mü‰?Mæ?L†y?K°×?KOú?K8U?Jì ?Jæî?Ku?KQN?K_+?KaÑ?M¹¹?Mâ5?N+?N7h?Niœ?Nq¾?NÈÍ?N€?Nwš?NS?L™?KI·?J³v?J­ ?J™½?J™X?Jéã?K%Ý?K9?KZW?MÆK?Mô?N7Û?ND‚?N‡²?NÕò?O4?OFX?OEé?OVY?L¥8?Jˆ?I“*?IêØ?JA'?J„›?Jº|?Kâ?K%Ì?K[q?Mϳ?Mù?N/ç?NeÒ?N´x?NòA?Oo³?OÀ?P:+?P¼Ÿ?L—?IXk?HÚ?I/?IÚ+?JYÑ?Jœ§?Jß‹?K4?KCŒ?MãÇ?Mü/?N,?NT7?N¥§?Nñf?Oe…?Oɤ?PŒì?PgY?Ló=?I>?HÃÃ?I>U?IØ0?JH?Jƒs?Jç7?Kk?K?T?Mâ!?N÷?NŒ?NEø?Nw”?NÙa?O¾?OmT?O¾¢?O6ü?M_?Joð?Ia?IÊK?J/r?JH@?J¦ ?Jýµ?K5h?K<`?MË™?MòM?Né?N4}?NC_?Nt£?Nµ?O "?Ns_?Nü?LöÈ?KAÎ?Jz?J½þ?J{é?J™ˆ?Jëa?Jüì?K@?KO`?M·›?MØ7?N?N Æ?N?N5ü?Nmx?N>ì?N<Ä?MÌ?LÌÛ?Kæ’?K@Ë?Jí?Jû,?K ?Jý°?K(Q?KV>?Kdô?M§?MÆ;?Måœ?MÞÒ?MÜl?M÷Y?NX?M̓?M¿Ÿ?MJC?LÃ#?Lº?KÄŽ?KE´?KTÛ?KL?K>?KMr?Kf ?K}ƒ?M›™?M»%?Mší?M´c?M¼Ò?MÄM?MË?M}Ð?MXe?M%?L¶þ?L<¿?L?K”!?K›~?K~“?K†Š?Kr¨?Kx?K“A?M€L?M–¦?M‡e?M˜é?MªÏ?M…Ç?M…²?M^&?M69?LìI?L²ú?LY?L(P?KÐí?KÇÁ?K¸\?KÉ+?Kž ?K„D?K¨ ?MhÐ?Mv,?Ms7?MwQ?Mƒã?Mƒ?M;ˆ?M/ä?MP?LØ?LÀ?Ll_?L1?L"?KëÎ?KàÏ?Kñr?K±>?K®«?KÆ?MUÁ?MZ ?MYÙ?MV?M[/?M^»?M=d?M“?Lí?LÉS?L°ó?L~—?LW?L$à?L D?Kø–?KóM?Kßg?KØò?Kí+?MF‰?MRÔ?M:?M:>?M>?M8>?MÜ?M?Lå´?LÔ?L± ?L|0?La²?L5s?L&‰?L=?Lv?Kúu?Kø»?L ¡?MO ?MN·?MKr?MNö?M4Ù?M3q?M*'?M}?Lñ7?Lز?L’d?L„;?LLã?L6‰?L*Ä?L¾?Lm?L ?Käg?KòÛ?Mdž?Me:?MW®?M]…?MQó?MO§?MJ?M?LÿÉ?LÒÕ?L—È?L}B?L`Ç?Lä?LR?Kö>?KÝr?Kè°?KÚ—?KâÛ?MzÒ?Mx-?Mf ?MuÎ?Mxé?Mm!?M_ó?M>¨?MN?L×Ù?L ?LtÌ?LF|?Kÿb?KóÁ?KÊ?K»,?KÃu?KÇ9?KÎ?M‘¡?M‡9?Mƒd?M˜‡?M˜ã?MŽX?Mü?MWK?M6å?MB?L«Í?L^©?Lä?Këg?K¼³?K–¦?Kœ7?K¥ª?K¬Ã?K´ ?M›Ó?M¶|?M¬á?M´~?MÃh?Mº·?MÅ?MÆ?Mr?Kݪ?Kê?K"¤?JÂH?JÜ¥?Kg?K-}?KMN?KWŠ?MÍ?MÈÈ?NQ?NAß?N}«?N ÿ?NÇ?O0-?Nýl?N‡ñ?Lϳ?KÎ?JEÒ?Jj–?JKÓ?Js€?J›?Kó?K.k?K;?MΣ?Mòv?N+ý?Nk?Nºp?NÜf?ONC?OÙ4?Pté?P›s?L¿¤?I ?HèÝ?Iu§?IÅL?JCI?J¢t?JÞ¶?K?K6¤?MÕM?NY?NL?K5{?K#ú?KGr?KI1?K[í?K{À?Mš?M¬ù?M¹?MÓ?M»®?M½¶?MÊ!?M½R?Mc?M%?L²;?L1B?Kï¬?K£?KzÊ?KlÒ?K‰³?K^&?Kyý?K”^?M†¸?Mš'?M§Q?M©‚?Mƒ…?M†?M…y?Mq2?MKé?MP?L¦¢?LVF?L ?Kå×?K¹÷?K m?Kºˆ?K}?K–ü?K­¸?Mrð?MV?Mг?M}×?M{?M\š?MFÄ?M?&?M)I?Lò?L s?L_ß?L8C?L!e?Kî½?K½E?KÃ)?Kµ?K·÷?KÆ9?Mfq?MdH?Mf+?M]â?MX³?M@t?M3e?M!?M(?L×»?Lºÿ?L\?LOq?L8?L )?Kö?KÛ~?Kìv?KÅh?KÙ?MM¤?MOM?MI²?MD‘?MA¡?M5ã?MÕ?Mœ?Lì´?LÁ?L«?Lx?LaÞ?LE?L"½?L"r?KòÆ?Kð¶?KìË?Kô)?MK?MGÜ?MC‹?MFg?M>è?M8ö?M-Ù?Lû¢?LÙµ?LÄ¥?L§è?L…l?LX÷?LM?L-4?L¢?LK?LS?L‚?Lé?M]o?M\?MTM?MZ“?M[Ä?MO†?M8ß?MÒ?Lï¥?LË­?L«:?L…ò?LIÍ?L¬?L]?L,?Kå‡?KïŽ?L)?Ké§?Mj/?Mj†?Mmõ?Mv?MtL?MjÎ?McÆ?MFg?Lõ£?LÕ˜?L¦¬?L†?M¯I?M¶¹?M¾?MÍm?M„’?MQ ?MŽ?L¯×?LM?Kç‚?Kµµ?K›K?K‰ø?Kw?K{ð?K‰Ð?K™s?M¡â?M´á?MÍ)?MØP?MåP?Nh?Nì?M³ð?M±?MË?LÉB?L'þ?K§?KkŸ?KUH?KR²?KN€?KOÙ?Kd~?K|4?M®§?MÌŽ?Mð“?Mú=?No?NQ?N?Š?N<Ú?Môš?M‰j?LÂ?L,|?Ke??Jô7?Jü ?K\?K?K!¼?KFË?K`?M¿ö?MìŠ?M÷&?N%?Nk!?N8?N—y?OË?N«.?Mþæ?L¹í?KĪ?J£?Jm?J¯?J¡×?JÖÓ?K ,?K84?K?r?MÊ@?Mô]?N[?NVã?N£?NÀÞ?O ¬?O—•?O£]?NÇä?Løœ?J‡·?I”c?Ió?J-?Jk?J»ï?JÚp?K_?K5Œ?MÒÝ?NÉ?N=´?NYÙ?N²Ä?OŸ?O2?Oè+?PÞ%?Oà(?M?Hþ|?HÑH?I{?IÅ?J:??J˜Á?Jí?KN?K6 ?MØV?Na?N8+?Nmo?N¥u?O ?OQ_?PX?Q S?P¨‰?Lç¸?Iÿ?MÃá?Mú9?N U?NQ ?NRu?Nz«?N¹«?Nù}?N÷>?N §?LkÏ?K6?J¬±?J0Š?J€?J™?JÓ?Kb?KBÒ?K`t?M¯£?MÓå?M÷÷?N.Œ?NQÙ?N´?NPÌ?NSÖ?N5?M…Õ?L¸Õ?KÓ“?KvK?KÞ?J×€?Jåù?K…?K:­?Kb®?KaÊ?MŸ½?M¹?M؉?MÿÊ?N e?Mß'?N·?N?M¥Ð?MB½?L¨R?L|?K°Œ?KsÃ?K6Ï?K7a?KZ2?KZA?K_Æ?Kxç?M‘%?M¢¼?M»?MÚv?MÆž?MŸ?Mº5?Mº?M[Â?MQ?LÏë?L(.?Kï·?KË?KyÑ?KmÔ?K„€?Ks”?K‡[?Kšƒ?Mƒ·?Mƒ?MW?M¦&?M—ä?My?M‰6?M`ô?M/š?Lö!?LÌÁ?LNÍ?L²?Kò¾?KÌ?K™Ã?K®þ?KŒ¯?K¡õ?K²¼?Mr[?Ma?M‰ ?Mmx?Mq›?M\£?MVæ?M7R?M&Š?LÚ¾?L¯™?LfW?L8*?L&Ó?Kât?K΂?KÄ ?K¹?KÀÈ?KÊ‹?Ma?Mp,?Mk½?MHã?MS?MO?M2M?M)?M 1?LÕ¡?LšO?Lpä?LY¨?LA?Kû‘?L Ø?KÏà?KÚb?KÙp?KßA?MM?MS¬?MIƒ?M>d?M=?M:Å?M&?MX?Lïí?LÁ¿?Lœ÷?Lym?Ld?LN¦?LM?LD?Kù_?Kõ¶?KõÈ?KôÑ?MFA?M<$?M;€?M=?M<‚?MAî?M!?Lð?LÙô?LÆ?L°–?L„??LWL?LSÉ?L's?L u?L Å?L D?LÅ?Kþ€?M[¢?MEM?MSÝ?MS¯?MIt?MH´?M3ÿ?Mò?LäF?LËŸ?L°ä?L€Â?LI©?L*P?L¹?LY?Kúo?Küx?LV?Kßí?MjÖ?MQ‰?Mh?Mm1?McH?McD?M]æ?MEy?Láš?LÏÆ?L¬?L~-?LO@?Ký?Kó~?KêA?KØ-?KÛ¬?KäÁ?KÝ?Mw¤?Mgr?M}¦?M„?M‡Ù?M‚o?M•?MM{?LÿÙ?Lé,?L¶?LZ?L3?Kóµ?KÓK?KÝ’?K2?K¯^?K¼?K¾p?MŽ?M~d?M•ˆ?M³Ÿ?M²·?M§V?M¶Ü?M­?M#„?MÇ?L¼K?LJ?L ?KÎé?K¼S?KŽÐ?K€®?K¬?K—?KŸŸ?M§ˆ?M˜í?M¯"?Mβ?MâÏ?MâŒ?MëC?M™Å?Mw„?MÓ?LÉ_?L9?KÔù?Kœè?K‰î?KLÒ?Kaã?Kv_?Kd„?KY?M®?MÃÞ?MÓ ?Mð,?NM?N/?N/?MÞ¸?M¿…?MU#?LÉc?L+Š?Kªô?K6/?K'é?K«?K;p?KX™?KE?K`|?M»‡?Mêö?Méú?N $?NFÒ?NIT?NfO?NoÒ?NÅ?M³™?Lð_?Kþþ?Jûà?JÜ ?K#?JÏ ?K?K'ï?KN>?KHI?MÂ?Mëw?N9?N=Z?Nkœ?Nv²?NÁ0?Né?N¡,?MÄì?M"®?KL?Jr$?JŠ?J¡»?J²â?Jöú?Jî.?K)r?KE?MË™?Mö†?N!™?NN!?Nuý?N¿4?Nìë?O,X?O 7?N[s?LúV?JÂú?J7B?Jƒ?JQ¨?J¦U?JÐô?Jñ?K#C?KF?MÕ?Mü;?NÏ?NSQ?Nm‘?NÀ ?Nû’?O=?O2Ü?Nε?L†C?K¢?J-ò?Iñj?JUî?J˜?JÌ:?K ?K.?KHF?Më?Mî?NÃ?NMà?NUZ?N‹??N²^?Nýø?NÀ?NZ­?L‹p?Kl‰?J¡˜?J ?JvÈ?J¦?Jéì?K"?K+?KEí?Mµ?MÞ¨?N ?N=Y?N"P?N>8?Ntu?N‘ ?N‡«?Mª|?LžÃ?K?KU‘?J¥~?JØ2?J¿m?Jÿ2?K1©?KR?KU4?M¥Å?MÅJ?Mêý?N³?N%ã?Mùü?NA…?Mÿ)?NŒ?MD?Lš?KÚŠ?K¢­?Kvà?K?K ‰?K'Ã?KUº?Ke›?KoÃ?Mžw?M¨³?MÉz?Mì€?Mò5?MÛF?MÊô?MÊ?Mœ¡?M,ê?L©á?L Ï?Kè2?K§–?KQ™?KXz?KSp?Kgh?K{?KŠg?M”T?M‘b?Mª*?MËÅ?Mµ)?M™D?M§?MŸ/?M:ö?Lû\?LË?L.)?L Q?Kè&?KŒ¸?K…†?K†—?K‚Ö?K˜é?K¥?M‘Ô?Mt*?Mˆò?M–¦?M’k?M.?M†î?MJŸ?M‚?Lçü?LÉl?LW°?L2ü?Kùì?KÌE?K½•?K­?K˜]?K²ô?K¸ ?Mz?Mj ?Mw?Mi÷?Moœ?MX>?MSL?M.m?MU?LÜs?LÀ†?Len?L<^?L"?KüE?Kê¯?KÁ^?Kı?KÏë?KÍ¿?M_É?Mg?M]¹?MFä?MR?M\ ?M Ì?M ¾?Lü^?LÜô?Lšõ?Lx‡?LaX?L"]?Lc?LÇ?Kè¤?Kê#?Kè;?KÞG?MN?M_D?M5ð?M5Ø?M9J?M;b?MÛ?LýF?Lõo?LË…?L¥&?Lk‹?L`—?LA:?L)…?LW?Lw?L^?Là?KìÙ?MMå?M*E?M-,?M3›?M2¤?M+Í?Mü?Lú¢?Lá$?LÊf?L©Õ?L?LWÚ?LH7?L0?L1?Lt?L=?L>?Kýé?MeG?M,±?M?‰?MF»?M@L?M=c?M/°?M4?Löš?LÒÞ?L“s?LtÚ?LTé?L4?L¹?Lƒ?L+?L j?Kò©?Käg?MmF?M:°?MU:?M_î?MZß?M\i?M]M?M(K?Lçƒ?LÎC?L›o?Lv“?L[r?L›?Kþ®?Kô>?KìS?Kê#?KáŠ?KÞ7?Ms?MU¬?Mm?M€Ô?Mp?Mw?MyÄ?M?&?Lø?LäÊ?L¤)?Lnx?L<$?Ký‹?Kå)?KØŒ?KÅ®?KÕØ?KÁ¿?K¾u?M}ò?Mu ?M†•?M ¿?M³€?M•u?Mzƒ?M\3?M,¹?M[?L«|?L`?L%*?KÑÖ?KÅ?Kª?K¥°?K³‹?K°?Kv?M“x?Mˆ.?M›ç?M·/?Mω?MÞ¬?Mœz?Mn!?M^ù?M`?L˹?LPM?Kßr?K¼Û?K©s?Koš?KˆË?KŒñ?KÝ?KR?M¦–?M´v?M²(?MΔ?Mñö?Nu?Mé:?M“4?M‚l?M9N?LÙû?L,©?KÞU?K›?KRê?KO*?Ku…?Keˆ?K_?Ku?M±]?M×?Mؘ?Mä£?Nt?N!-?N0à?MÐ?MºÛ?MlV?LÑb?L:í?K_‚?K[/?K?K$?KQ ?K>%?K^W?K`?M´Œ?MÙÃ?Mú?N}?N( ?N/Ü?N\Ì?NB£?MõK?MšÓ?Lˬ?KÃå?Kï?KÀ?Jå2?K¼?KQ?K ø?K>ì?KYo?M»o?Må¸?NJ?N C?N=Œ?N[ ?N]Ô?Nµ?N8˜?MÐ|?Lο?K¹Ø?Jùe?Jöu?J¼‰?Jø?K/o?KN?K9g?KW5?M¿Õ?Mó.?N°?N?N)½?N\›?Ni–?N¡â?N+H?MÇ?L¦q?KÙµ?Kô?J±?JËý?Jùa?K÷?K3 ?K??KRÈ?M³W?MÛ\?Nb?N!¯?Na?N0š?NU¶?NwF?N?M¹Ø?L›_?K¸$?Kbè?JÇ6?Jä??K Æ?K:®?K6#?K@¤?KYÞ?M¦q?MÉ ?Mø?NV?Mý ?N9?N%f?N*º?N)M?MT?Lm1?KÃt?K™›?K"Z?K ±?K-¯?KA×?K?*?KWÂ?Kli?MšG?M´Ü?MÚ?Mø?Mñ?MÏ—?Mï ?MÚr?Mæ?Kß.?KÓU?K¸Á?K¶Â?KÅ`?KÀÛ?MkÍ?L)@?Lç?Læ?L?LA?Kåc?Mdí?M8V?MK?MUµ?MPÉ?MH?MDÌ?M$¦?LïÌ?LÆÞ?L– ?Lr–?LVö?L/õ?L"F?Kþ)?Kób?KíÐ?Kìé?Kä¶?MeŠ?MV_?Md ?M~»?Mn0?MTº?MNS?M1&?Mh?Lݨ?LŒ?Lo5?LGñ?L1?L J?KâÜ?KϦ?KÝ…?KѰ?KÍ—?Mqý?Mg·?Mwj?M’ˆ?M¥ ?MpO?MS=?MH ?M'ü?LÞÒ?L£>?LiK?L5è?L ¿?Ká?K¿À?K·À?KÁè?KĤ?K³š?Mƒ|?Mq?MŠÿ?MŸ’?M²?Mª0?M?MR*?M?d?M;?LÅì?LIŽ?LØ?Kóü?K±Ï?Kž¶?K¦?Kœö?K¢ ?K½*?M޼?M—Þ?M±™?M¬?MËù?MÞ‹?Mž!?Mc‘?MZò?M&?L¾°?Li?Kö?K®'?K‚ñ?K“?Kˆ‰?K|o?Kƒd?K•ô?M˜ó?M´Z?M×à?M»S?MåÙ?Mö3?MÆt?MŽˆ?Mvg?M1÷?LÌ??LT“?K׈?KŽº?KOX?Kl»?K~?Kj…?K}g?Kqø?M¢¤?M¿]?MÚu?Më‡?N ?MÛ€?Mïð?MØ„?M’Ã?MJâ?LÖ?L¿?K­z?Kq ?K2(?KSb?K{ÿ?K[ÿ?K_r?Kgæ?M©ì?MÍ–?MÚ™?Míê?Nÿ?Mó-?Nm?Nõ?M®?Mj:?LÅ0?Lµ?K ¡?KU\?K%?KIw?Ka‚?KQ¿?KWì?KgÓ?M­f?MÝž?MÒ?MçÐ?Mûn?Nj?N&?NÅ?M¹Ê?MS?Lº®?L¬?KÃ?KCS?K$Ð?K3½?K\?Kw¤?Kc¿?KXž?M¤?MÀh?MÖ¥?Mý?Mõæ?Mâø?Mþ;?Móþ?M¨V?MU6?L®‹?L)þ?K¨ú?KCö?K7?KLo?Km%?Kr?KZ*?Kk>?M™|?M­@?MÍÈ?Máª?Mãá?MÍõ?MÝ?MÏ?M±?MCT?LˆŽ?L8?Kì¾?KX¨?KT÷?Krê?Kq°?Krü?KrT?K~c?Mc?M¤D?MÑú?M¶e?MÇ‚?Mºµ?MÆ;?M?Mx1?Mˆ?L‘†?L?þ?Lˆ?K?K‡#?K‹ ?K“n?Kˆ?KC?Kô?M…?M’R?M°Ö?M«Ô?M³I?M“,?M€¤?Msv?Ma?Lðƒ?L ¼?LIÜ?L?KÔ5?K¶‹?K±â?K¡ú?K’s?K—!?K¦?Mzå?M~J?Mƒ?M À?M…º?Me(?Mj?M_F?M1?LÒ0?L§d?LY„?L?Ø?L ­?KÎX?KÂÖ?K±£?K­ã?K²?K»š?Mt‹?M\Ü?Mo+?MxD?M_ð?MUî?MV‚?MFu?MŸ?LÈŽ?L Z?LlŠ?LYr?L?K÷*?Kë8?KÇx?KÉD?KÉ?KÍ?M]?Mh„?MSÃ?MHJ?ML(?MK!?MH‚?M‹?Lì2?LÕR?L™A?Ln·?LX±?L6ú?L$?Lv?KÕ?KÜr?KÞ/?KßK?MHE?MUñ?M@‰?M8§?M=A?MAk?M<?Lô?LÛi?LÈ7?Lš[?LzU?Lcö?LQ$?LBÃ?L?K÷‘?Kü#?Kï¦?Kí×?M6Î?M=ù?M3m?M-Û?M.B?M6è?M%?LèV?LÙ9?L½J?LžP?L¨?Ljù?LZm?LTØ?L%‡?L ”?L –?L?Kÿ?M1S?M+Â?M+#?M&c?M ¤?Mœ?Mþ?LûÖ?LÛ?LÊ ?L—Ä?L{~?Ln?LQg?L9·?L0Í?L+È?L,Z?L6§?Lò?M?j?M5×?M>¼?M1˜?M,K?M!ù?MI?Mú?Lê™?LÞn?LŽ»?Lv†?Ll?LD7?L)â?L!\?L?L¶?L?Kñm?MK»?MAÝ?MJ9?MJW?ME[?M6?M&>?M !?Lùb?LÃ÷?L–µ?L¦?LgË?L1c?L&%?Lø?L?Kòò?K÷¶?Kñé?M]Ý?MK/?MTô?Me¼?Ma“?ML\?M8(?M2?M9?LÀN?L›R?L‹"?Le¡?LÛ?L?Kú'?Kä?M&?M(>?M)û?M ¿?Mœ?M o?L÷h?Lðc?LÔ?LÍD?L?Lz@?Ls´?LVZ?L>A?L7U?L2;?L0Ÿ?L0—?LŒ?M/ì?M5o?MI’?M$@?M?M)?M?LòS?Lß?LÎ{?L¡ì?L„Ò?L{R?LI ?L'?L&þ?L"ô?L ??LC?L :?M;t?M?)?MJä?MG¯?MB‰?M??Mg?Lø{?Læú?LÇ?L¤ã?L‘H?Lƒ³?L.…?Lî?L¢?LM?LV?Læ?Lÿ?MN#?MGC?ML´?MY»?Mj?M ý?MŸ?M?Lò ?LÃ#?L¤H?Lš?Lv?LÁ?L ?L O?Kù„?Kòe?KóI?KöK?Mg?MNê?MR?Mh^?M„i?M- ?M!à?M#%?ME?L¹?L™ð?LˆÝ?LaH?L?LD?L –?KÙJ?KÖ‘?KÞ?Kè0?MmE?Ma\?Mfð?Mu•?MŒ«?MKB?M5?M;M?M š?Là?L%?L|d?LHX?Lñ?Lv?L$?KØa?K¿.?KÇè?KÚd?Mnk?Mrç?MÛ?MŒ[?M’G?M_?MHD?MB&?M*¢?Lõu?L†i?L{?LB–?L?KçŒ?Kß?KÈË?K½×?K³Ä?KÌ?Mrå?Mƒd?M£?M†Á?MJ?Mz?Maf?MUb?M$à?Lï?L¦›?Ln¿?LB>?Köì?KÈz?KÅ-?K¾?K¾ÿ?K¤…?K¿ ?MwÑ?M‡’?M S?M˜¤?Mž8?MžŠ?MrM?MeW?MG?Lý>?LÁ¸?LGç?L(»?L?K¸)?K¨?K±¼?K¹?K«)?KͲ?M|Œ?M‡5?M—q?M¡’?Mª÷?M™"?M™€?Moà?Ms?LýH?LÄÚ?LDŸ?L×?LE?K¥Ç?K£@?K¯ò?K°Ý?K¤"?K°j?M‹ú?M‚U?MŠŸ?M?M¤?MœJ?M¡F?Mk†?M0t?Lðÿ?LΟ?LQ…?L ³?Kö?K¸?KŸH?K¯?K«?KŸ†?K§ ?MŒM?Mx?My?M˜ž?MœA?M•?MŽ?Mm?M&Þ?Lñ?LÀ:?LgY?L N?KÛ?KÃ;?K½I?K»\?K²@?K¦¥?K¥2?Mxa?M|?M„â?Mœÿ?M•ð?M…7?Mvé?MRZ?M8m?Lîo?L¦ñ?Li?L5£?KÐ&?KÇ·?KËx?KÐ?KÏ?K¼¡?K“C?Mké?Mt´?MÊ?MЇ?M?M‰ ?Mit?M, ?Mº?Lëk?L°‰?LzF?L,¼?Kç?Káì?Kã¿?Kì6?KÇ’?K²²?K©ö?Md9?Mm?Mu?M{k?M}0?Mr’?MEW?Mù?M~?Lè§?L½?Lb‰?L9b?L%?LV?Këõ?Kõ—?KÁí?KÀJ?KÃ?M[M?MhÈ?Mip?Mkó?MXZ?MMh?M?/?M·?Lû©?L/¨?Lj?L?L(á?L(è?LS?M/¨?M6â?M>í?MJb?M4è?M ?LòÙ?Lê#?LÝ?LÃB?L¯4?L??LG?LE2?L%0?L#z?L?L²?L-?L˜?M<—?MA1?ME,?MK ?MJó?M$v?Lüû?Lò?LíÛ?L¼†?L£Œ?L–?Lpó?L@x?Lç?L1?LH?L•?LÙ?L )?MM1?MO?MMD?MT?Me‹?M(Ï?M ø?Mç?Løð?LƤ?L›´?LÙ?L^º?L;Ú?L"3?Ll?KøB?Kô~?Kñr?Le?MY?Mm?MRÚ?MQþ?M`d?M=y?M"Ó?Mc?MI?Lèf?L†›?L‚º?LSz?L.ç?Ly?Ll?Kü\?KØW?KÞ?KòŽ?M\L?MhH?Mf?MhE?Mqy?MUú?M*X?M/ä?Lþ?LÛË?L”Ä?LŠ?LL(?L?L=?K÷%?Kéô?KÓG?KÚ??KøÉ?Mb´?Mo3?MrÝ?Mm?Mt¼?Md‹?MBM?MN?Lô(?Lѵ?L¡á?L‚±?LX•?L„?Kêb?Kâ‰?KÙÅ?KÔS?KÒ*?Ká"?Mo?Mi”?Mr‘?Mrr?Mxk?MqØ?MW?MO?Lî?LÖ]?L½S?LnB?L;ÿ?L<ž?Lý?L!?L O?Kø±?K׬?KÚ ?ML?ML9?MN±?MSû?MZw?M=ô?M-²?M ?Lè‘?LÒk?Lµú?LŒ?L+–?L0`?L b?L;?LW?KüÌ?Kí§?KßX?MZ ?M=Ô?M?L—>?Ly[?LE$?L9X?L7¡?Lß?L)?L®?LD?KÝõ?MBÎ?MA?MDJ?M?9?M1j?MV?M?M'?Lù‹?Lå^?LU?Ls^?LR?L@u?L9ä?L"%?L?LX?LÂ?KßÃ?M9é?M?h?MBy?M.9?M?LS-?L7¼?L0?Lè?L?Lþ?Lë?MDe?MI?MIj?MJq?MPØ?M;?L÷À?Lø‘?Læ$?LÃ?L¨G?Li?Lm??LOv?L´?L ?Lk?Lo?L¹?L?MFù?MK»?MJÄ?MP?MM?M6J?M{?Lÿ9?Lñº?LÇ?L«w?LŽÏ?Ltƒ?L;D?L®?L n?L?Kÿ?MBÑ?MCü?MAì?M8y?M2r?M&5?Mÿ?MV?LÅJ?Lœ6?Lpb?LR ?LBþ?L0?L*Õ?L3É?L9?Kå·?Kîß?M:j?M<`?M<Ç?M:÷?M5q?M)h?MÖ?MÂ?Løí?L½?L˜ó?Lz¦?LS4?LE?L8€?L2·?L0?LM?KüE?Kõo?M:?M6"?M4÷?M2¢?M*²?M?Mn?Mû?LòÅ?LÁ³?L˜?Lxs?LZž?LK}?L@?L1H?L'q?L?L,?Kñç?M2\?M3Ž?M30?M+ø?M­?M’?M‚?MÉ?Lð!?LÐK?LŸþ?Lo6?L_‘?LSX?LF¦?L3G?L*†?L$²?L) ?KùJ?M.³?M1;?M3A?M-z?LüÉ?LþÆ?Lû¸?Löd?Lðì?LÄ„?L£,?Lv ?Lin?Ld-?LSú?L+?L,b?L% ?L•?L-?M(`?M-œ?M8?Mç?Lîy?Lô‘?Lò¤?Lîù?Lë²?L³J?L?Lˆ"?Lv’?LgÑ?LR;?L<Š?L8?L%÷?LM?Lã?M”?M"Í?M3o?Lø,?Lð“?Lð‚?Lì¯?Lç,?LÓ[?L·?L¡‚?L‘?L‡Ç?LcÜ?LVn?LJ¢?LSø?Lp?Lz?L"±?Mþ?M{?Mõ?LÿÃ?LóÓ?LíÆ?Lç~?LÝR?LÌ ?L¸Íw>Í„;>Í”¨>ͤr>Ͳ)>ÍÄ'>ÍÛf>ÍèÏ>Íï>Íô>Íù->ÍüI>Íï >Íó->ÍÔ>ÍÄØ>ͬ+>Í–=>͈‘>Íy¦>Íx[>͉>Í™µ>Í«s>ͼï>ÍÑÙ>Íê>Íò´>ÍøP>ÍûÆ>Íÿ¹>ÎY>Î #>ÍÐÓ>ÍÁ >Í–>Í’¶>Íõ>Í„º>Íy>Ít>͉æ>Í™ˆ>Í®>ͧ>Íà\>Îr>Íÿ˜>ÍòÇ>Íï(>Íë>ÍÑŒ>ÍÍ>ÍÇÏ>ÍÁ¤>Ͳ>ͤÃ>Í•Û>Í„^>ÍyE>Íb~>Ío>>̓6>͘h>Ͷ>>Íá+>ÍÝì>Íèj>Íéì>Íæt>Íä{>Íèt>ÍâŸ>ÍÉÚ>ÍÂ\>ÍÀ^>Í«Õ>Í–>Í‚ù>Íp•>ÍQ>Í[¦>Ío·>̓M>ÍÉ>ÍÅÍ>͵«>ÍÚ–>Íä>ÍÝ/>ÍÒì>ͽÐ>Í®>ͲÓ>ͶÔ>ÍÓ‹>Í¥ö>Í’>Í>Íhû>Í>÷>ÍM>Í_S>Íjü>ÍvW>Íh >Í 6>ÍÚ“>ÍÎn>ÍÆñ>Í» >ͦ¡>ÍH>͘—>Í‹«>Íst>Í}!>Í’ñ>͇ù>Í\µ>Í*Š>Í7’>ÍCH>ÍM3>ÍZN>Íiy>Í}à>Í„I>ͺ>ͳË>ÍÆ>Í‘>ÍŒf>Íy‡>Í^z>Í4±>ÍTM>Íiä>ÍMû>Í5 >Í >Í$ >Í;s>Í0Ô>Í3N>Í7>>Í8ç>ÍRç>Íp²>Íl¤>ÍlH>ÍbÇ>ÍQ9>ÍL€>ÍAO>Í1£>Í+I>Í$>Ìóµ>Ìÿ&>Ìò§>Ìöë>Ìò)>Í`>Í è>Í!>Í)b>ÍS_>Í&>Í€>Í&X>Í)Ë>Í((>Í'b>Í#>>Í"n>ÍŸ>ÌÈÑ>ÌÒ¡>ÌØ¡>ÌÑ,>ÌÞg>ÌìW>ÌæÀ>Ìêƒ>Ìö9>Ìá¶>̯Ü>Ì­>ÌÞ>Ìíç>Ìö¤>Ìþ >Ìë+>ÌöG>ÌÞy>ÌÉÂ>̼{>Ì·ª>̶_>̰>Ì­y>̧ >ÌÀ/>̳p>̪À>Ì„‘>Ìfë>Ì…ò>Ì Î>̵p>ÌÍL>ÌÏ«>̬Õ>ÌŸ¡>ÌŠ©>Ì} >̇k>ÌÂ>Ì“%>Ì’®>Ì—|>Ì‚q>Ì~º>Ìlâ>ÌR->ÌO>ÌHÝ>ÌW;>Ìd >Ìt_>Ì€C>Ì4é>ÌNK>ÌWZ>Ì\7>Ì_–>Ìe¼>Ìn0>ÌvW>ÌzØ>Ì>ÌTi>Ì/S>Ì=Ç>Ì?Õ>Ì%‚>Ì>Ìø>Ì#I>Ì>>Ëð >ÌH>Ì >ÌÝ>Ì+>Ì<®>ÌEÀ>ÌPP>Ì[÷>ÌsÁ>Ì($>Ì#‰>Ìý>Ì>Ëò?>ËõP>ËîÂ>Ëå >Ëçœ>Ëï]>Ëß—>Ëå>ËóÛ>Ëþ)>Ì>Ìé>Ì%Ò>Ì1>Ì?Z>Ì H>ÌÅ>Ìg>Ëý>Ëìî>ËÖ>ËØì>ËËæ>˳>Ë”>Ë/>ËÌá>ËÉ\>Ëâà>Ëê}>Ì'>Ëú >Ìœ>Ìø>ÌØ>ËÎ>˼>ËÝh>Ëò>Ëë'>ËÉ»>ËÖÒ>ˬ°>Ë>ËuÂ>ËN²>Ë_Ï>˘>ËѨ>ËË>ËÈÛ>ËÓ¹>Ëê>ËþX>Ì >ËÝ4>ËÍ–>ËÈ+>˳O>ËŸ,>Ë›l>ËšÝ>ˉ>ËvÅ>Ëjc>Ë]‰>Ë_Ÿ>Ëu(>ËU—>Ëx>Ë >˶`>ËØ>Ëø >ÌN>ËØ,>ËÉ­>˼Ì>Ë *>Ë„Õ>ËvÀ>ËH>Ë_–>Ë`f>Ë^v>Ëa+>Ëit>ˈV>Ënü>Ëzy>Ë’ý>ˬ©>˽Ž>ËÊ™>ËÙe>ËÍü>ËÁ%>˹Ö>˧É>Ë‹B>Ëvœ>ËlA>Ërc>ËYW>ËQ>ËK‘>ËI“>ËC+>Ë]‹>Ër^>Ë‹æ>Ë¥Æ>ËÃ5>˹à>˹>>ËÅ >˺>˾_>˪2>Ë…ç>Ëmó>ËLÛ>Ë4«>ËK™>ËO>ËO>ËHL>ËAu>ËY:>Ëgì>Ëoð>ˇ?>Ë©­>Ë´y>˼©>Í…>Í•e>ͦÑ>͸š>ÍÇË>ÍÝ>Í÷ˆ>Κ>Îô>Ή>ÎŒ>Îó>Î ¤>β>Íòñ>Í×ö>ÍÁ¹>ͯ>Í'>͈­>͉>Í¡×>ͱþ>ÍÅú>ÍÕÃ>Íé]>΂>Π>Î.ï>Î6l>Î+w>Î./>Î!>ÍòÞ>Íè$>ÍÑ>ͽ©>Í¥>Íž>͇f>͈•>ÍÃÏ>ÍÁ/>ÍàD>Íæ>>Î>Îñ>ÎI>Îã>ÎØ>Îz>Î!Œ>Î6>Î>ÍïÀ>ÍßV>ÍÈl>Í®d>͘u>Í‹>Ío>ÍnÃ>ÍÒ>Í£—>ÍÕ >Η>Î#Î>Î!á>Î6>Î ¶>Λ>ÎŽ>Îá>Íòp>Íð€>ÍûL>ÍÚ]>ͱi>Í™>Í„ß>Í\ã>ÍYÏ>Íö>Í–L>ÍÀ>ÍÊç>Íãò>Î.k>Î*z>Îí>η>Íì¬>ÍΘ>ÍÛ¢>Íæ‹>Íú>ͽ!>ͨ">Í–>Ír>ÍKš>Í^£>͈Ø>͇0>Í—>Í£8>ÍÇ>Íô>Íö>Íù>Íòi>ÍÞÜ>ÍÕÔ>ÍÃÓ>Ͳ->Íhl>ÍŒ‘>ÍŸf>Í‘ó>Ít>Í4U>ÍA*>ÍD·>Í]©>ÍrF>ͲÌ>͹ >Í–$>ÍÁ/>ÍÖ#>ÍÒÙ>ÍΕ>ÍÊì>Í¿>Ífz>ÍOz>Í[">Íe&>ÍV >ÍB´>Í>Í2•>ÍOW>Í@o>ÍAó>Í6í>ÍU’>͇«>Í—>Í®„>͵o>Í•ã>Ín¶>ÍgÚ>ÍWt>ÍDa>Í8>Í0o>ÍÜ>Í×>ÌüÃ>ÌüŒ>ÌïX>Í ö>Í9>Í >Í.ñ>ÍYˆ>Í>>Í=ƒ>Í=]>ÍB7>Í< >ÍTí>ÍBn>Í%ç>Í•>Ìêå>ÌßÀ>Ìâß>ÌÏ«>Ìãö>Ìù>Ìé¿>Ìê>Ìî»>Ìúb>Ìß¹>ÌçŒ>Í>Í8>Í®>Ìí->ÌÍ>ÌÛU>Ìæ>Ìøb>ÌæÍ>ÌÅO>̵±>̺>Ì®>Ì0>Ì©ç>̱G>̲>Ì >Ìuk>Ì‘«>Ìï>̾L>ÌÔÝ>Ì®§>Ì›ö>̨>Ìž‹>Ìy>Ìl³>Ì|(>Ì‹ð>Ìp>̇¼>Ìq>Ìr6>Ìt>Ìpj>Ì:«>Ì,P>ÌZ¤>ÌYS>Ì^G>ÌLj>ÌEô>ÌZM>Ì›î>Ìw­>ÌZ >ÌVü>ÌcÚ>ÌoÃ>ÌYï>ÌZ$>ÌI#>Ì6¶>Ì/–>Ì(/>Ì[>ËôÈ>ËÕý>ËÛª>Ì>Ëö§>Ë÷†>Ì,>ËÛ)>Ìý>Ì7>Ì:A>ÌEV>ÌW»>Ìcþ>Ì+^>ÌÓ>Ì Q>ËôÃ>ËÙŒ>ËÖ>ËÏ’>˽>ËÁÖ>ËÈ$>˯þ>ËÃr>Ë×õ>ËâI>ÌË>Ì%P>ÌW>Ì"X>ÌA½>Ëü >Ì>Ëø1>ËçŽ>ËÍð>Ë«|>˾Ä>Ë¿”>Ë”È>ËUq>ËEÉ>Ël@>Ëè>˸d>ËÌ(>Ëÿ®>ËÖ6>Ëën>Ëüm>Ëü!>ËÎÀ>ËÈ]>ËÎ#>ËË>Ëà >ËŸæ>˨>Ës¸>ËJ´>Ë<–>Ë%¾>ËD_>Ëoº>ËŠ>Ë¢B>Ë´ƒ>Ë®Ç>ËÓ™>ËìÈ>Ëô^>ËÐ>ËÁß>˼3>Ë©¹>Ë…n>Ës>ËZñ>Ë\v>ËAA>Ë7`>Ë>>ËH@>ËDÕ>ËCU>ËFÁ>Ë;Õ>Ë+>ËÖ>Ëõn>Ëèˆ>ËÊ>Ë»&>˲>Ë`>Ëu}>ËS>Ë‘>Ë|>Ë)ä>Ë3‰>ËS]>ËQ >ËXC>ËH8>ËS'>Ëqö>ËÙ>ˆÞ>Ë” >ËÀî>ËÀ>˱>˯è>˨ð>Ë>Ë`t>Ë:ö>Ë'¾>Ë*²>Ë'>ËA>Ë>Ë×>Ë7¶>ËRþ>Ë{)>ËŠ>Ë•ž>Ë b>˳r>˵–>Ë¢Ô>ËŠç>Ëj>ËZ%>ËGþ>Ë:}>Ë-T>Ë.©>Ë.í>Ë=¥>Ëî>Ëš>Ë2·>ËC >Ë:ý>Ë\Y>ˇ›>ËžA>˰->ÍŽ>Í .>͵>ÍËU>ÍÞ>Íýg>Î8>Î'á>Î,Ç>Î6->ÎG˜>ÎH:>Î5š>Î,€>Î7>Íùë>Íàw>ÍÍ–>Ͷc>Í›°>͘:>ͯx>ÍÃ>ÍØÞ>Íé_>Íïn>Ρ>Î=#>ÎT'>Î]Ø>Îf>Îl>ÎPØ>Î6>ÎB>Íþå>Íæ>ÍÁq>Íœc>͘Ÿ>Í”¹>ͱq>ÍË>ÎR>Î>Î%Î>Î<>ÎIÕ>ÎNE>ÎJx>Î:Ø>Î8›>Îk~>Îa:>Î$Ù>Îú>ÍèØ>ÍÉ›>ͯ;>Íšl>Í€ç>Í’Ú>Ͱ>ͼ†>Íù~>Î*ÿ>Î?Ê>ÎYX>ÎT >ÎIT>ÎIû>ÎK:>ÎE>Î-Ñ>Î…>μ>ÍðŒ>ÍÅv>ͯ5>Íšþ>Ío >Í‚">Í¥E>ͺ|>Î _>ÍõÜ>Îñ>Î?€>ÎFö>ÎW&>ÎVî>Î?³>Î%>ÎP>Î[>Íí,>ÍÑ}>;µ>ͯ–>ÍŸÔ>ÍYÃ>Ív(>Í® >Í¥î>͸G>ÍÞt>Î>Î+ð>Î-B>Î>b>Î?h>Î&ò>Íô¦>ÍØ¬>ÍÙŠ>ͯÃ>ÍÖ>ÍÁ–>ÍœZ>ͬ–>Í?'>ÍJS>ÍEm>Ím,>Ís6>Í×>Î>Íñ®>Ε>ÍøE>θ>Î7™>Íø4>ÍËÞ>Í‘l>Í+>Í`‡>Ízz>Íe>ÍWì>Í47>Í6Ù>Í8•>ÍL>ÍYþ>Íi>Í|ö>Íã¼>ÍÒg>ÍØ>ÍÒÅ>Ͱ!>;K>ÍÄ>Íz>ÍdG>ÍDI>Í59>Í5Z>Ìï©>ÍÚ>ÍI>Íþ>Í>Í( >ͽ>Í=³>ÍFS>Íi,>Íz>Í{ä>ÍgŽ>Í[->ÍJw>ÍEs>Í8–>Í$3>Í >Ìüš>ÍÝ>Ì©>ÌÏ_>Ìæð>ÌðÐ>Ìõ>Ìøµ>Í ˜>ͺ>ÍW>Í)ï>Í/>Í)>ÌöÀ>Ìæí>Ìîô>Ìó>Ìú‰>Ìîò>ÌÎÇ>Ì¢„>Ì¥>Ì¢0>Ìžú>̨š>̶\>̾w>̶S>Ì„>Ì›>̯Q>Ì´>Ì–q>Ìð>Ìœ½>̰ >Ì­¢>Ì¢ª>̆u>Ìvë>Ì>Ìo>ÌxZ>Ìlá>Ìgå>Ìo>Ìxq>Ì4ð>Ì >Ì)>Ì<>Ìcš>ÌI‘>ÌH{>ÌU{>̃>̆n>ÌO†>ÌK >Ìe#>Ìh<>ÌQS>ÌIa>Ì>©>Ì&i>ÌÓ>ËòR>ËáË>ËÒÞ>Ë»¡>˶¥>ËÈÃ>ËÎß>ËÛÂ>ËõR>Ëí!>Ì >Ì=Ã>Ì,.>Ì7ÿ>ÌM«>ÌAÉ>Ì)ì>Ì$ >̾>ËãÒ>ËÊ­>˪°>ˤ >Ë‹²>Ëkž>ËDe>Ërù>Ë™k>Ë»¸>ËÚï>ËÓ¢>ËèJ>Ëôn>ÌU>Ì9‘>ÌÅ>Ì“>ËñG>ËÒd>Ë®î>Ët`>Ëou>Ër¬>Ëda>Ë3>Ëœ>Ë1>Ëi½>ˉ²>Ë¡>Ë´V>Ë¥ä>ËÊX>Ëßo>ËÞW>ËÃ…>Ë‹>˱–>Ë«,>ˬ—>Ëk;>ËG>ËØ>Êññ>Ë>Êþ>Ë3>Ë6Ô>ËAg>ËJP>Ë‚W>ËŽ>˳&>ËÎ[>Ëß)>ËÂK>Ë®ò>˶>ËŸ>Ë[*>ËSI>Ë<,>Ë(ò>Ë>Êó¹>˘>Ë16>Ë>Ë9>Ëñ>Ë+÷>Ëo>Ë¥:>ËÃj>ËÏD>Ë»>Ë©î>˯>Ë—<>ËZð>Ë)>Ë<>Êïû>Êø¶>ÊûŽ>Ë ‚>Ë!Â>Ë÷>Ëc>Ë*l>ËJ[>ËnV>Ë>Ë•Í>˱H>˯Ö>Ë™z>Ë€Ã>Ëq>Ëb¨>ËI >˽>ËÓ>ËË>Ëþ>Êç•>ʼ‘>Êêˆ>Ë«>Ë8^>Ëk>ËqÖ>ËZ>Ë’á>˧ >Ë¢¼>Ë„÷>ËHò>Ë0ð>Ë'Ë>Ë:>Ëu>Ë•>Ë 0>ËÃ>ˆ>Ê×>ÊÛ¾>Ëj>Ë>Ë&³>ËZs>Ë{7>ËŽ7>Ë¡K>Í£Ò>ͼ>ÍÏ´>Íñ#>Î>Îæ>Î8£>ÎMé>ÎSr>Î`¾>Ît°>Îm>ÎX6>ÎTh>ÎI/>Î4>Îa>Íêæ>ÍÊr>Ͱ>Ͱr>ÍΪ>ÍÙ,>ÍÖÞ>Íõ >Î>ÎP‰>Î}ƒ>Î>Î…y>ÎX>Ή©>Îr.>Î[*>Î@I>Î'À>ÎC>Î &>ÍÔ…>͵¤>ͨž>ͼž>ÍÚ*>ÍùS>Îã>Î>‹>Î[ê>Îy>Μ>Ζ±>Î}Ö>Îx˜>΃j>Îp¨>ÎG >Î*þ>Îp>Íî†>ÍÕë>Í®Ã>ÍŽ >Í¢€>ÍÎ4>Íñz>Î!u>ÎG>Î\ö>Î…û>Δö>ΈJ>·¨>Λ†>Μ[>΋{>ÎW >Î G>Íí|>ÍÁÊ>ÍÃ)>Í®Û>ÍË>ÍšÈ>ÍÆù>ÍêÜ>Î3ú>Î:L>ÎO+>Îd+>Îv½>ΉÆ>ÎÛ>Λ¾>Î>>ÎHº>Îw>Íþˆ>Íã×>ÍÊÛ>ÍÞ>Í»Á>Íc{>̓(>ͤ7>ÍÃI>ÍØI>Î=>ÎX¢>Îx>Îsæ>΂N>β>Îf>ÎX,>ÎV>γ>ÍãÊ>ÍÔt>;R>Í|>ÍÀ>ÍEy>ÍVì>Íi‘>Í@>͵Â>Î Ÿ>Î>Î0:>ÎXm>Î\o>ÎdÞ>Îl£>Îà>Î >ÍÞ>Í¿˜>ͦ^>Íœx>Í_ð>Í7 >Í/k>Í?ÿ>ÍNÁ>Ík¥>ÍŽí>ÍË>>Íч>Íù>Îè>Î>À>Î9{>Î,>Íãà>ÍÎP>ͬs>Í1>Í„ç>ÍC«>Í:°>Í >ÍJ>Í7µ>Í,›>Í<>ÍNù>Í*>Íi >ͦ)>Í¿>Í¿ >ÍÙ…>ͽÞ>Í>Íj\>Íb©>Íks>ÍNŒ>Í ó>Í"C>Í$¡>ÌÅ}>ÌÜ$>Ìùn>Ír>Í X>ÍÌ>Í>ͳ>Í_Ü>ÍI‚>ÍS->Íe:>Í *>ͨ>ÍX>Ìý×>Ìïº>Ìçc>ÌÍE>̪ý>Ì›Þ>Ì€–>Ì‘µ>Ì›œ>ÌÃg>̵®>̪™>ÌŒ§>Ì¥,>ÌÆÏ>Ì•}>ÌŠÒ>Ìö>Ì N>̰ó>Ì–—>̺w>Ì´a>Ì’˜>̇Ù>ÌuŽ>Ìlq>Ì^Þ>ÌR:>ÌE$>Ì<>Ì+O>Ì >ËØI>ÌN>Ì >Ì…>ÌG >ÌJT>Ìe¤>ÌW>Ì,…>ÌB¹>Ìeb>ÌYÀ>ÌLÒ>ÌDÚ>Ì0U>Ì>ËÏÄ>Ëê{>Ëõƒ>˶+>Ë|>Ë[>Ë—#>ˇ‹>Ë£>Ëã^>Ëò‘>Ëÿ%>Ìâ>Ìb>Ì,>Ì7Ó>Ì1Ï>Ìs>Ìž>Ëç>Ë·•>ËÉ>Ëjw>ËW>Ë6ÿ>Ëú>Êñ>Ë 2>ËG÷>Ë‚U>Ë’²>Ë®k>ËÄß>ËÖ>Ëä˜>Ì„>̈>Ì>Ëäï>˶—>Ë‹Í>ËC÷>Ët>˪>ÊêÄ>ÊõÃ>Êò>ÊôE>Ë>ËK}>Ësò>Ë‘”>ËžJ>˲‰>ËÃ’>Ëà'>˲=>˱Ú>Ë”™>Ë‚F>Ëh>Ë5Ù>Ê÷>Ê˽>Ê»ë>ʺê>ʨJ>ÊÉ%>Êð„>Êç>>Ëî>Ë}Ü>Ë| >Ë•/>˳ >ËÛC>˱2>ËŒÚ>Ë~µ>Ër>ËB{>Ë7#>Ë >ÊÝ>ʼØ>ʇƒ>Ê¡“>ÊÛÝ>ÊîÜ>ÊØè>ÊãÚ>ÊöQ>ËK™>Ë4>Ë p>˾4>Ë©£>Ëì>Ë‹>Ëë>ËEÒ>Ê÷ >ÊÚŸ>ÊÒ>ÊÂÁ>ʱF>ʳz>ÊÓ†>ÊÁÝ>ÊØó>Êù>Ë Ü>ËW">Ëtd>ËŠ“>Ë”Ì>ËŸ>ˆº>Ë„ó>ËK1>Ë%¶>Ë >ÊðB>Êã>ÊÏ>ÊË.>ʳ…>Ê„ò>ÊÖ<>Êèþ>Ë =>Ë=$>Ë[‚>Ër_>Ë…»>Ë™·>Ë‘þ>Ëp˜>Ë3Ž>Ë‚>Ë"t>Ë4>Êÿ$>Êèy>Êæo>Êט>ÊÇ>ʨë>Ê‘¥>ÊÎU>Ê÷>ËU>ËSÀ>Ëut>ËH>Ë–n>ÍÅ>Íñ>Íòþ>Î Ú>Î$>Î;ð>ÎSÿ>Îm@>Îa>΢>ÎÅò>Λè>ÎuO>Î_>ÎFØ>Î+H>Îý>Î>Íã#>ÍË">ÍÙ>ÎÔ>Î|>Î >Î&×>ÎMà>ÎvÄ>Μ >ΰù>ÎÀ‰>Îŧ>ΰ£>ΖÏ>Îù>ÎiC>ÎMã>Î@ù>Î4Í>Íõ§>ÍÛ>ÍÑÒ>Í×È>Íös>Î>ÎBæ>ÎfÂ>Ή$>β>Îå7>Îìb>η”>δ >ΰå>Ο™>ΈB>Îhá>Î.%>ÎJ>ÍíÎ>Ͳh>ÍŠ>͈m>Íî¢>Î{>ÎK>ÎqÇ>Ι­>ÎÛË>ÎÎ6>ά¹>α>ÎÚQ>ÎÕ>ÎÍ >Îoù>Î5f>Íÿ>ÍÌ>ÍÒÛ>ͺ>Í’û>ͱÑ>Îy>Îp>Î>w>Îe›>Ή(>Η>ÎÚ>ÎßS>ÎÛe>Îì&>Îä[>ÎËw>ÎjÙ>Îê>Íú>ÍÙ|>ÍæŠ>ÍÉz>Íe4>Í¢Ñ>Í®>Íói>Îz>Î[›>Α‹>Î>ΰ´>ÎÛ‘>Îç…>ÎÅ—>ÎÑd>Î|á>ÎG*>Î÷>Íì^>ÍÌ >;n>ÍÂô>ÍIê>ÍLŠ>Í^‚>Íž¸>ÍúÀ>ÎlÎ>΋m>Î >Π­>ÎÙó>ÎÐ>γÉ>Î~Y>ÎKÏ>Î>Íö_>ÍÐ5>Í£þ>Í^Ú>ÍW>Í):>Í:Õ>Í`x>Í+>ÍÆ~>Íí >Î ¬>Î^->Γâ>Χº>΢À>Î~Õ>Î@Ö>Íô¨>ÍÛY>Íʇ>ÍÃ>̓ñ>ÍU–>Í&¡>Í >Í#\>Í(L>ÍI >Í‹D>Í>ͱt>Î*¨>Î,ƒ>Î"T>ÎGý>η>ÍÒ¢>ÍÊÑ>Í”ò>̓¾>ÍZ“>Í6³>Í@>Í)Ñ>Ì×>Ìãú>Ìýk>ÍØ>Í^>ÍP>Í<Ñ>ÍFý>Í`>Ízl>͆>͆>ÍB&>Í,=>Í*>>Íê>Ìåx>Ìàå>ÌÊ,>ÌÂQ>ÌžK>ÌœÍ>Ì¢¤>Ìœº>ÌŸž>Ì §>Ì¿ø>Ì™Š>̹>̰>Ì·›>ÌÔæ>Ì­‘>Ì¡£>ÌÒë>Ì­k>̧’>̪V>Ì>̰ÿ>Ìrv>Ìx°>ÌWÊ>Ì7‚>Ì*_>Ì:>Ëî%>Ëø>ËÉe>ËÌ>˵Ð>ËÂ'>Ì{>Ì >Ì2À>ÌR½>ÌC•>ÌP>ÌVu>Ì>(>ÌE—>ÌJ®>Ì(ñ>ËüŠ>ËÄÙ>Ë–ç>Ë£>ËeÀ>ÊþÖ>Ë&>Ë%s>Ë2>Ë‹Ú>˸›>˯->Ëʾ>Ë÷0>Ì{>ÌCè>Ì&À>Ìj>ËÙ0>ËÜg>ËÉ`>Ë›z>Ëj¢>Ë'=>ÊÏã>ÊÇû>ÊÏû>Êœ>Ê·œ>Êá¥>Ë9°>ËLi>ËŒ>˹‹>ËΞ>Ëß>Ì>Ëî1>ËÌÉ>˰›>Ë“Ú>Ë~>Ë ù>ÊÓ>ʦì>ʇ>Êj‚>Ê•×>Ê—Â>Ê›g>ʶ>˳>Ës–>Ë+>˘X>ËŒÑ>˰K>ËÀ_>˦^>Ë>ËZ>>Ë9í>Êÿ¡>ʦô>Ê|V>ÊX¡>ÊBt>ÊS÷>Ê‹G>ʘ/>ʶ >ÊøÃ>ËX•>ËNQ>Ënu>Ë‘>ËÄ_>Ë©c>Ë‹‹>ËgÚ>Ë7²>Êã >Êéô>ÊÀ5>ʘ¬>ʇ8>ÊP4>Ê;F>Ê} >Ê–ì>ʾð>ÊËù>ÊÁ2>Ë!â>Ë^ >Ë… >Ë«¡>Ë—¶>ËvÊ>Ë`É>Ë:>Êü\>ʶ°>ʪ±>Ê¥>ʈ²>Êmð>ÊO>Ê?¸>Êk>ÊŒ>Êȱ>˰>Ëbû>ËeÞ>Ëxã>˃Ò>ËŒ‰>Ëln>ËR§>Ë)Ô>ËÝ>Êß…>Ê׸>ÊÛ >Ê…“>Ês->Êi >ÊWQ>Êø>Ê£î>ÊØO>ËN>Ë=5>ËS“>Ëw>Ë™‘>˃5>Ëaá>Ë7±>ËL>Ë>Êú»>ÊÔ”>ÊŸu>ÊÅ >Ê Õ>Ê“7>ÊŠ5>Ê“>ʱ<>ÊÒp>Êá«>Êó>ËA^>Ëw$>˦g>ÍÔº>ÍôY>Î->ΰ>Î@w>Î^„>Îw>Εt>Ϊê>ÎÈq>Îøþ>ÎÇo>ΗD>Îz•>Î`8>ÎC/>Î/1>Îe>Íðá>Íݶ>ÍñM>Î%´>Îj>Î1R>ÎZB>Îz·>ΗÎ>δ>ÎÛ>ÎîÐ>Îîò>ÎÜ>ÎÀ>Χ?>Εê>Îl!>Î9>Î!>ÍÿH>ÎP>ÍÁr>Í×P>ÎÕ>ÎAõ>Έã>Μ¸>η)>ÎÜ>ÏÀ>Ïì>Îþ´>Îõ>Îä«>ÎÈB>εD>Σt>ÎS¤>Î ->Íòˆ>ͦð>ͦ&>ͽù>Î#>ÎC÷>Α)>β5>ÎЩ>Îû¾>ÏJ>ÏŠ>Ï%l>Ï-<>Ï©>Îé!>ÎJ>ÎeÒ>Î?>Îi>Íî >ÍÄ:>ͧû>ÍÌ0>Î >Î:$>ÎZ½>ÎŽ>ÎÔ0>Ï0>ÏGÁ>ÏRß>Ï'Ã>Ï8v>Ï,À>Ï"n>ζ{>Î35>Î2>Îà>Íñ¾>ÍÏa>͸>ÍØT>ÍæÉ>Î~>Î,&>Θî>Ï>>Ï©>Ï##>ÏH—>Ï`Å>Ï?Î>Ïj>Îý%>ΤÆ>ÎH>Î ²>Íëü>ÍÜ„>ÍÆ³>Íl>ÍmŠ>ÍŽÕ>ÍΠ>ÎÞ>ÎŒò>ÎÀí>ÎùÃ>Ï@•>Ï—6>Ïqö>ÏYÆ>Ïn>΋)>Îcë>Î?è>Íï§>Í··>ÍŒé>Í~>Í(>Í;·>Í}P>Ͷ>Íêh>Î<å>Î…À>ÎÓ>Ï1.>Ïa0>Ï:=>Îö1>μ>Îq>Î0>Íìº>Íþ>Íú>Í}­>ÍVo>ÍÜ>Í%>Í)©>ÍO§>Í©Ä>ÎÝ>Î"º>Îw>Η8>ΰÔ>ÎÅ>΂­>ÎMë>Íÿ×>͵o>Í«>͆­>ÍNH>Í2¿>Í:>ÌåV>Ìä>Ìý>Í{>Í,¾>Í;Q>ÍV™>Í…Ü>ÍÈt>ÍÎÜ>κ>Íä(>Í£ë>Í8“>ÍAp>Í8n>Í\>Ìõ>Ì‘>̱¡>Ìšl>̵>̺ >̱>ÌŠ >Ì‚‚>ÌÎ3>̱·>ÌÁ>ÌŒ¼>Ìš#>Ìú,>ÌØÌ>ÌÎö>Ìãx>̶>̘ƒ>Ì»B>ÌÄá>̨¤>Ìh´>ÌeI>ÌGH>Ì)>ÌE>Ëüñ>Ë«U>˲é>Ë—_>ËwÈ>ËLÞ>Ës>ËÍ4>Ì>Ì|>Ì#R>ÌDÙ>Ì[<>Ìe–>ÌZ§>Ì8Î>̽>Ì¥>Ëü™>ËÆ">Ëho>Ë0[>ËÚ>Ê{ >ÊnÏ>Ê>ÊÂf>Ë(é>Ë@>Ë~æ>ËÂ=>Ëù¹>Ë÷>Ëý­>ÌK>ÌA>ËëX>Ëè²>ËÌ·>Ëyü>Êÿ>ʳæ>ÊM>Ê:¨>Ê6ð>Ê6>Ê']>ÊnÎ>ÊÕg>Êû >Ë:é>˸¦>ËÍ«>ËØ!>Ì©>ËÓX>˯®>Ë~Æ>ËnÊ>ËP®>Êàç>ʆ˜>Ê f>Ê#@>ÉòÝ>Ê›>Ê&J>Ê">Êd€>Ê̲>Ë&õ>Ëf>Ë<>Ë’ >˰Z>˵¦>Ëšà>Ë^Î>Ëÿ>Ë}>ÊÇÑ>ÊsŒ>Ê >Éß>ÉÊp>ɾ>ÉÅ;>Ê>ÊxÕ>ÊÍð>ÊñY>Êî#>Ë1ß>ËrÊ>Ë£T>Ëž`>ËŒ(>ËNƒ>Êý!>Ên>Êœ&>ÊwÉ>ÊEB>Ê,>Éæ»>ÉâŒ>Êþ>Ê’>ÊsN>Ê›ë>ÊÌò>Ë >Ë7Ô>ËiA>Ë“¼>Ëv>ËFÇ>ËPö>Ëâ>ÊÊã>Ê¡p>Ê‚u>Êfì>Ê:0>ÊCù>Ê ¹>Ê>Ê)o>ÊAÂ>ÊŽ±>ÊÜw>Ë6=>ËWH>Ëge>˃å>Ëxž>ËL«>Ë%Å>Ë>ÊÙÂ>ʳB>Ê·{>Ê·’>Ê4S>Éó‘>ÊŽ>Ê#e>ÊH—>ÊuQ>ʺ®>Êät>Ë ä>Ë!Œ>ËW>Ë€æ>Ëtz>ËNY>˦>Êý{>Êêj>ÊÎh>ÊÁ >ÊvÂ>ÊhÉ>ÊRß>ÊY/>Ê`w>Êod>Ê€’>Ê¡†>ÊÆN>ÊøG>Ë' >ËUº>Ë{®>ÍÙ>Íõ>Î>Î52>Î_Œ>Î…´>Σ¾>ÎÚ¬>Î×e>Îãâ>Ï Ó>Îä>θ>Λr>Άd>Î]í>Î:É>ÎÌ>Íö‹>ÍÞ©>ÍìÞ>Î`>Î+ >ÎOb>΂>Τ>λ>ξl>Îö>Ïb>Ϫ>Ï “>Îí>ÎÑ >ÎÒî>·=>ÎDØ>Î&a>Î~>Íèû>Íݨ>Î µ>Î/X>ÎdØ>ÎÍr>ÎÐÿ>Îßw>Îý5>Ï.²>ÏKä>ÏHÔ>Ï5Û>Ï~>Îèþ>Ξ>Πª>Î}R>Î: >Î]>ÍÖ¥>ͼB>ÍåK>Î!Ü>Î_$>΋B>Îàr>ÏÔ>Ï,>Ï`õ>ÏkÈ>Ïo|>Ï‹›>ÏZ«>Ïõ>οW>Μu>Îil>Î8¥>Î z>ÍÜd>Í­»>ÍÐÍ>ÎØ>ÎaË>··>θÉ>Ï l>Ïg>ÏÔJ>ÏÎ>Ï©¾>Ϥ!>Ï}“>ÏLS>Îç{>Îpû>ÎI'>Î:>Îv>ÍÞÀ>Í–Þ>ÍÉÁ>ÍðB>Î#3>Îr>ÎÙP>ÏB6>Ïœ‰>ÏÊ>Ïßn>Ïðo>ϼ>Ï€ø>ÏaÞ>Îû¯>΂M>Î)Î>Î>Î>ÍÌú>Íq8>Í…Î>ͬ>Íêß>ÎPF>ÎÍ>ÏÌ>Ï·E>Ð>Ð?>Ðó>ÏÚ´>Ï|`>ÎöÀ>Îü@>Η=>ÎÒ>ͺ´>Í–ë>ÍŠ;>Í[Ð>Íw‰>Í‘ >Íij>Î ]>ΕG>Îô¼>ÏGO>ÏÝf>Ðc~>Ð.V>Ϻt>σÂ>ÎíK>Îr1>Î-[>ÍÝP>Í–>>Íc«>ÍYÄ>Í'ß>Í\Ñ>ÍT$>Ívs>ͽ3>Î3i>ΫP>ÎÃ5>Ï*^>Ï·>ÏÔm>Ïeù>ÎÐø>ÎQ·>ͼÛ>;9>ÍœÉ>ÍZÛ>Í++>Í>Ìì>ÌÓ1>Ìü>Í>Í.Þ>ÍA4>ÍrÔ>Íì…>Íâ.>ΈY>Ï,Š>Îá>ͯ|>Í¢½>Ín >ÍFƒ>Í#Í>ÌÿV>ÌÔŽ>ÌÅ{>Ìšç>Ì­,>̶>̧e>Ì”>>ÌŠP>ÌÁ±>Ì>Ìýé>Ì’B>Ìj¸>Í x>Í,²>ÌÞY>ÌÖ>ÌØE>Ì£X>Ì« >̬¥>Ìžü>Ìs8>Ìpp>Ì`±>Ì:S>Ìç>Ìm>ËxØ>Ë<>Ë&>Ê”\>Ê–æ>Êýe>Ë13>Ëò«>ÌË>Ì .>ÌE5>Ì`:>Ìv…>ÌiŸ>Ì>{>Ì]>Ìß>Ëêÿ>Ë¢F>Ëõ>ʨò>ÊVö>Éc$>Én#>ɘl>Êž>Êá^>Êå>ËOç>˵(>ËÒâ>ËèÙ>Ëü>Ì$E>Ëðð>Ëñ¶>ËÉò>˰î>ËVE>Ê™Ë>ÊÒ>É­£>ÉP&>É0>ÉÉWd>ÉŽ—>ÊrX>Ê–¿>ÊÑî>Ëz²>ËÄ >ËØ >Ëìæ>˧u>Ë~>Ëh>ËOî>Êò->Êœ>Ê2+>ÉÑë>Ézp>É=4>É*£>É;+>ɪo>Éñß>Êj}>ÊÑÌ>Ë/{>Ë€·>Ëz>Ë«C>Ëžƒ>Ëv>Ë>‚>Êé">ÊÙÏ>ʧ>ÊMP>ÉÄm>Én±>Év|>ɤ>É>Éj>Éô(>Êq!>ÊÃ>>Êí¾>Ë+’>ËTÚ>ËK>ËŽ’>˺>Ë./>ÊÒÛ>Ê„Ã>Ê^‹>Ê/Ã>Éå>ɧ:>ÉO>Éw¾>ɇ>>É—ê>Ê>ÊiÛ>ÊÀ>Êøù>Ë>ËM„>ËÄ>Ëq>Ë0$>Ë!Î>Êê>ʪ:>ÊŽ²>Ê:,>Ê æ>Éßß>ɳ¤>ɯ¿>ÉË >Éó>>Ê'>Ê_‡>ÊÇ>Êåy>Ë=>>ËQÞ>ËvÌ>Ëgã>Ë.l>Ëè>ÊæÞ>Ê®>ÊW>Ê\>Êdq>Ê*h>Éç…>ÉÁÜ>ÉÖl>Ê!>Ê\>ʹ >ʹ>ÊÒ>Êì->Ë:³>Ënê>Ëhé>ËA >Ë}>Êîå>ÊÇ¥>Ê—>ʆò>ÊR©>Ê3M>Ê*x>Ê%o>Ê0È>Ê>>ÊaL>Ê›>Ê¿ð>Êí»>Ë ›>Ë>µ>Ëhñ>Íå®>Î#>Î,f>ÎOó>Îy~>γe>ÎÛœ>Îæ6>Îñà>Ï >Ï“>Îÿ»>Îàã>ο¢>ζ>ÎuU>ÎEk>Î>Íþ¤>Íâù>Î ö>Î1Ë>Î@¨>Îd>Ζƒ>ÎÈò>Îä¨>Ï >Ï8ƒ>Ï;ž>Ï3è>Ï7Ô>Ï m>ÎæØ>΢e>Δ=>Îir>Î6Ê>Î d>ÍêV>Îü>ÎGE>ÎG¥>Îqn>ηf>ÎÐ¥>Î÷Ú>Ïbœ>Ïl$>χÛ>ψ_>Ïož>ÏU9>Ï>ÎØ>θ>Ζç>ÎS‹>Κ>ÍêR>Í«‰>ÍÙÏ>Î1º>Î}1>ÎÁL>ÏZ>Ï7 >Ïk–>ϯé>Ï·q>ÏÎ@>ÏÞm>Ï›­>ÏTÓ>Îÿ·>ί¨>Ζ->Î[>Î$n>Íï|>Í«þ>ÍÌÃ>Îð>Îlt>ΰÔ>Îñ6>ÏKÎ>Ï>ÐD>Ð;³>Ð,f>ÐÝ>ÏÀ>Ï—Ì>Ï7:>ε0>Î`ø>ÎC>Î6¸>Íõ…>ÍŸI>Íï>Î –>Î@ï>ΔŸ>Ï„>Ïz|>Ïà‡>Ð4e>Єò>Ж¤>ÐV„>Ðæ>Ï›¥>Ï>Ϋ>Î:ó>Î/þ>Î%‹>Í×V>Íqº>Íh,>Í»>Î$„>ÎÆ>Ï °>ÏlÍ>Ð>Е>ѳ>Ñ1º>Ðår>Ð%r>ÏŠp>Ïaæ>λü>Î@+>Íë+>ͧ·>Í‘…>ÍS/>Ínp>Í¥²>ÍéÖ>Î3º>ÎÌN>σ>Ð]>ÐÚ˜>Ñ™x>ÑZ >Љ°>ÐpZ>ÏD>ÎáŽ>Îi+>Σ>ͱ,>Íi±>Ír>Í%Á>ÍBÞ>Íbü>Í’Y>ÍÐ*>Îlh>Îøœ>Ïe7>Ðg>Ñ—ù>шÍ>Ðм>ÏT`>Εi>ÍåÉ>Íó:>ͦ¦>Ír]>Í?$>Í0¾>Ìì™>Ìòë>Íú>Í ¬>Í8ž>ÍŠ·>Î;„>μ->ÏQõ>Ð0£>ÐL>ÏU>ÎîÎ>Î<>ÍŽl>Í^)>ÍXè>Í â>ÌÙÁ>̱Ó>Ì»4>̼Ë>ÌÐ>Ì›>̆D>Ì€(>̈l>ÌÖ>Í¢K>Ì?e>Ìúá>Ír–>Ìñ0>ÌÐ@>Ìé\>ÌÒï>Ì€ >Ì‹”>Ì™>̘>̈á>Ìo}>Ì?:>Ì,ä>Ëý¤>ËäÅ>Ë^i>Êù>ɧt>ÉÂm>Éæ#>Êkø>ÊTæ>Ëk€>Ìþ>Ëí~>Ì®>ÌUž>Ì‹p>ÌtÈ>ÌÚ>ÌÏ>Ëî<>ËÙ–>Ë”O>ÊÒ±>ÊqV>Étc>ÈpG>Ç÷×>ÇÀ§>È >ÉÔ3>Êoù>ÊÔ'>Ëce>˸C>ËÖ}>̧>Ì4ï>Ëà2>ËÅ/>ËÞ>Ë`I>ËÓ>Ê‚Ì>ɱ´>ȺO>È3s>Çí}>È×>ÈÅ©>É"E>ÉÊ>ÊM>ÊÜu>Ë^»>˘w>Ë·;>ËÜÉ>Ë´D>Ë’“>Ëo«>Ë/$>ʰk>Êhh>ÉÓ,>ÉA>È „>È3E>ȵ>È07>Éõ>Éš°>Ê#M>Ê­û>Ë'a>Ëa,>ËP >Ë}Í>ËŸ›>Ëy>Ë2`>Êðy>ʧÔ>Ê^ð>Éþ­>É‚ç>È´‹>È‚>È~š>È”„>Èëì>É´>Ê2O>ÊP>Êæ—>Ë70>ËO >Ë{>ËÊ>Ëe>Ë>ʸÝ>ÊŠ >Ê9—>É˨>É~/>É:>Èòc>ÈöV>ÉDÔ>ÉK->ɱý>ÊŸ>Ê‚i>Êý'>˯>ËBæ>Ër…>Ëjƒ>Ë6>Ë Ý>ÊËQ>Ê>Êgø>Ê >É’ñ>É|A>ÉU->ÉQ5>É`>É¥Û>Éñt>Ê-Y>ÊnC>Ê®>>Ë{>Ë:à>Ëk>Ëa“>Ë+'>˽>ÊÔ>ʆÍ>Ê0ã>Ê1I>ÊÍ>ÉÕÈ>ɳ>ÉŸ>ÉÂw>ÊD>Ê7n>Ê€B>ÊŽ>ÊŽÜ>Ê× >Ë,a>Ëg¯>Ë_`>Ë=_>˼>Êá>ʨ_>Ê_‰>Ê7·>Ê&ù>Ê·>Ê …>Ê™>Ê»>ÊM>ÊBx>Êh9>Ê H>Ê»~>ÊìÓ>Ë/>Ë_ì>ÍðÁ>Î|>ÎK >Înæ>΃x>ζ„>ÎÒb>Îê>ϵ>Ï$n>Ï,D>Ïv>ÎýB>ÎØx>ζ°>΃[>ÎRÓ>Î-P>Î !>Íég>Î>Î8¿>ÎKO>Îqf>Χ >ÎÝ÷>Ïc>Ï->Ï`°>ÏV¢>ÏA&>ÏP(>ÏF>Ï„>Îõ‰>ÎÁÛ>Îx3>Î<‘>Ϊ>Íîü>ÎB>Î)Ø>Î=ˆ>Îy>ÎÝH>Îúo>Ï(‘>Ïr»>Ïž >ÏÌu>Ϲ>Ï£ý>Ï{o>ÏHD>Ï ^>ÎÜ>Ι>ÎY+>Î!s>Íò´>ÍÙ%>Íþî>ÎGð>Ω›>ÎÙƒ>ÏT>ÏWô>Ï¡‘>Ïû >Ïô>Ð'‹>Ð-o>ÏÝ">ÏŽ>Ïž>Φ>Ε‰>Îf)>Î2+>Íú¶>Í©B>Íó£>Î?ÿ>Îp€>ÎÁ:>Ï$;>Ïk>Ïï>Ðd >а>Щ¨>Ðvœ>Ј>Ïî5>Ïaý>ÎÓy>Îu)>ÎKÆ>Î[Ú>ÎZ>Í¡>ÍÅÕ>Íù‰>Î@Ð>α>ÏyU>ÏÖt>ÐTý>в">Ñ7“>Ñ>г>Ð^ô>ϸ%>Ï.Ò>ÎЦ>Ît¡>Î@ë>ÎG>Í×S>Ͷ>͘)>ÍâÏ>ÎR >Ï÷>ÏIð>ÏÖq>Їj>Ñ66>Ò8 >Ò.—>Ñe¶>Т>Ð.>Ï€M>ÎÛ{>Îmv>ÎA>ͤ÷>͈>ÍV>Í’>ÍÝÒ>Î">ÎQû>Î×&>Ð M>л®>Ѩ#>ÒÐæ>Òù!>Ò¹>Ðáž>Ï¿)>Ï3)>ΚÎ>Î05>ÍÙY>ÍÀ>Í€g>̓>ÍAÔ>Ízæ>Í”Ù>ÍÒ–>Ι#>Ï]o>п>ÒmO>Ô &>Ô,Þ>Òñ>О¾>Îé]>ÎDÆ>Î>ͼ¿>ÍxD>ÍPd>ÍR(>Ìèm>Ìñ>ÌãØ>Í%>Í}\>ͶÓ>Ξñ>Ïj>Ñ—ñ>Ôó‡>Óúù>Ñw›>М>Ί>ÍÑA>Í},>Í@ç>ÌøÕ>ÌØ>̧›>ÌÉ|>ÌÎã>ÌÇç>Ì¥X>̈Û>̇š>Ì}›>Ì®€>Í>Ì>Î:.>Ìú0>Ìd>Í*~>Ìîì>ÌÇÜ>Ì•\>̤¤>̤ >ÌœÃ>Ìoë>Ì{>ÌHš>Ì+/>Ëα>Ëœ>Ë-C>ÊTR>È å>ÅÄW>Åú„>È”ò>Ê >Ë3>ËÔ´>ËÑ&>Ìq>ÌF¸>̆o>Ì€>Ì ï>Ëë_>Ëð~>ËÌÓ>Ëdú>ÊšÐ>ɵ)>É¡>ƽ>ÄÓÖ>Åaò>Æ—÷>Ȥi>Ê`Å>ʘ¬>Ë:×>ËÆ2>ËÂ^>ËÚ¶>ÌP>ËÞ8>Ë©«>Ë‚â>Ë1ü>Ê×>Ê\->ÉJ>Ç»ô>Ç1z>Æ3>ÅÄŒ>Æñ>È;¶>Èè>Êð>Êõ±>Ë7>ËpÐ>ËœC>ËÆ¼>ËÁS>Ëj>ËdÆ>Ë y>Ê—y>Êö>Ém>Èc>DZm>ÇV>Çâ>Çx×>ȆZ>É0$>ÉáB>Êk_>ÊîW>Ë5æ>ËV)>ˇ,>˯w>Ë—õ>Ë>ʯË>Êq’>Ê µ>ɘ^>Èøë>È*A>Çã>ÇÊþ>È"ð>Ⱦt>ÉZh>ÉçØ>ÊL>ÊÜ7>Ë&0>ËIf>ËuI>Ëp°>Ë'{>ˈ>ÊÉ‘>ʤW>Ê)Ñ>Éœ >É^J>É*>È¥ >È €>ÈÖ5>ÉŸ>É~>ÉØh>ÊKâ>ÊÒ}>Ë(ƒ>Ë@ì>Ëjð>Ëc]>Ë#H>Êú½>Ê·f>Êg`>ÊbÕ>ÉÑ$>Ét>É,º>É.>É!Y>ÉF:>ÉXQ>ÉÃq>Ê â>Ê_>ʰ¤>ÊÖm>Ë)ª>Ëf2>Ëeñ>Ë.%>Êû™>ʹ]>Êhq>ʦ>Ê >Éä©>É–˜>Éz¨>É[€>É€_>ÉÌ[>ÊÇ>ÊD€>Êu;>Êxƒ>ÊÓ’>Ë*>Ëx£>ËTM>ËE¢>Ë‹>ÊÎï>Ê•v>ÊHê>Ê ò>Éí0>ÉÞk>Éóš>Éó”>Éð6>Êj>Ê&Í>Ê]>Ê->ʲŠ>Êè >Ë(2>Ëg€>Íò®>Îñ>ÎB“>Îyª>ΈÁ>έ{>Îà >Îö'>ÏI>Ï;à>Ï$H>Ï'€>Ï Å>Îä|>θê>΋È>Î_J>Î[ª>Îu>Íð)>Î Z>Î7°>ÎO¶>Îl>Φ>ÎÞÆ>Ï!>ÏAs>Ï^Ã>ÏRF>Ï\«>ÏN‘>Ïw>Ï.X>ϳ>ÎÉò>Îy>Î#ú>Κ>Íñ÷>Î\>Î6¡>ÎS>΃Ì>ÎÒ&>Ï M>Ï7V>ÏŒ¥>Ͼv>ÏÆ¶>ÏØ>ÏÇI>Ï’=>ÏnÒ>Ï(>ÎÐ^>΋>ÎA§>Î">Íñz>Îè>ÎÅ>ÎY~>Ο%>ÎßR>Ï!ñ>Ï…¿>ϵ\>Ïò+>ÐI>Ð^¦>Ð3>ÏÑ×>Ï©P>Ï<™>ÎÕ‚>Ηá>Î\d>Î'~>ÍóC>ͤX>Íÿb>ÎTC>ÎŒT>Îàz>Ï9B>Ï ×>Ш>І!>ÐÐH>ÐÃ>ÐÁ–>Ð?>ÏØ¿>Ïys>Îð1>Μ.>Îj‹>Î;–>Íö>ͤ2>ÍÖŠ>Î @>ÎZn>ÎÇ >ÏE>ÏäÐ>Ѓ>ÑL«>Ñ­û>Ñ ?>Ñ5ô>Р•>Ð9>ÏS>Îê¾>Î’>Îl€>Î.D>ÍÙ½>ÍN>͉Æ>ÍÙh>ÎV>ÎÜ)>ÏV>ÐL¢>Ðã0>јÃ>Òt9>Òµ¨>Ò<>Ñþ>Ðl>Ï‹a>ÎïŒ>Ît/>Î '>ÍÌê>Í¢R>ÍW>Í{ú>ÍЩ>Î#h>ΜQ>Ï'<>Ð3F>ÑwÃ>Óœ>Ô¥÷>Ô¦‡>ÓL>ÑhR>Ðe†>Ï_0>εt>Î?>ÎÖ>ÍC>ÍvÑ>Ía>Í:>Í>ÍÀ=>Î >ÎëK>Ïý>Ñ©=>Ô>×B¾>×]ƒ>Óò™>Ñx)>Ϫ>ÎÉ>Î(>ÍÔ>Íœ>ÍP >Í?>ÌØö>Ììð>Ìì:>Í1´>ÍgÐ>ÍÃf>ÎeÕ>ÏÀ>ÔÇ#>Ûõ>Üý>ÔÂ>мð>În >ÍÅ->ÍzÜ>Í4Ï>ÌþÕ>Ìõ>>Ìõt>̪">̱ >Ì©¬>ÌÏ@>̦Ó>Ì~x>Ì?‘>Ì…á>Ì}>Ξ®>Ò,>Í•Ó>Íì>Í+u>ÌÔQ>ÌÎL>̦P>Ìšë>Ì© >Ì©ð>ÌmK>Ìk²>ÌE×>ÌØ>Ëä·>Ëx>ÊðÎ>Éä>Æ“ß>¼«„>¼ÁW>Å>É:¢>Ê©Ç>˘g>ËÉÛ>Ì!>ÌB˜>Ì’É>ÌoÓ>Ì/–>̪>Ëë>˱´>ËJ_>Ê…Ê>ÉtŒ>ÇêV>Å>Á¤ >ÁÍ$>Å#Ó>Çó–>ɱã>Êw >Ë'v>˸_>Ë·J>ËÄk>Ëõ¿>ËØî>Ë®Ú>Ë}Ô>Ë1f>ÊÏ>ÊQ>És”>ÇÑE>Åè£>Ä>ÄaŽ>ÅßÁ>Çd<>ÈÆ >É÷k>ʳž>ËŽ>Ë|µ>ËŽe>˺Õ>ËÀ8>Ë•š>Ë@i>ÊÕ>Êb9>Éä>ÉD±>È-¹>Çp>ÆB[>ÆBª>ÆÇT>ÇãÇ>Èåu>É¿Ý>Ê\¾>ÊÌ”>ËÃ>ËD+>Ë2>ËÁI>ˤ!>Ë*F>ʱò>ÊH >ÉÆd>É7Û>È¥–>Çàw>Ç1>Ç‚£>Çæœ>È„+>É.±>ÉÈp>ÊO‰>ʼ >Êÿ>>Ë79>Ëj>ËpS>Ë`W>ËÓ>ÊÊ”>Êu>ʘ>Élˆ>É-§>ÈÇA>ÈXÔ>Èd3>È™ß>Èß0>Énu>ÉÇ[>Ê7ƒ>Ê«Ù>ÊúÎ>Ë0\>ËaP>Ë_–>ÊüÐ>Ëé>ÊÀ™>Êz#>Ê7'>ɯã>ÉW>ÉUU>Èûž>Èů>Èõ>ÉAk>ɸÉ>ÉÿÙ>ÊY4>ÊŸø>Êéj>Ë(ô>Ë]É>Ëž>Ë%W>ÊÜô>ʦL>Êl>Ê1Ì>Êt>ÉÎU>ÉgÂ>ÉN€>ÉS@>ÉpÂ>É«ï>ÉÑÒ>Ê2p>Ê+>Ê©[>Êÿ~>Ë.á>Ëc¬>Ë¥>Ë&Ž>Êêg>ʺ¼>Êž˜>Ê~ >ÊÊ>ÉÙÊ>ɵ>ÉÁU>ÉÄ>Éâ<>Êë>Êò>ÊKæ>ÊŠè>ʹq>ÊÙú>Ël>ËW>ÍóA>ÎŽ>ÎCô>·S>΋_>Ϋ/>Îë->Îü´>Ïú>Ï) >ψ>Ï!_>ÏÅ>Îã§>μª>Θ>Îe†>ÎKû>Π>Í÷N>Î ´>ÎcÀ>ÎW¾>Îfà>Î¥–>ÎÛ >Ï;>Ï:Õ>Ï€ >Ï|ã>Ïgÿ>ÏD„>ÏK>ÏÔ>Îõò>Îé>΄÷>Î:Ž>ÎO>ÍöÅ>Íÿœ>ι>ÎNã>Ζ>Îܰ>Ï>Ï8E>Ï’Þ>Ï¡u>ÏÓ¶>ÏÚ+>ÏÉ•>Ïž§>ÏbE>Ï5•>θ&>ΆP>ÎGÐ>ÎT>Íë_>Íï>Δ>ÎaÔ>ΰw>Îàâ>Ï(A>Ï•z>Ïï0>ÐN>ÐPò>Ðc¢>Ð?m>Ïëþ>ÏŸd>Ï1.>Îë‚>Ο>ÎW >Î+>Íã>ÍÆ!>Íøq>Î8i>Î=>ÎàÙ>Ï;È>Ϧ­>Ðf>Ð{l>и¤>ÐÇ{>Ы;>ЊT>ÏöÐ>Ïh>>Îß>δ˜>Îjv>΋>ÍÞ“>ͯ‚>ÍÞZ>ÎB>ÎgÏ>ÎÁ‰>Ïhs>Ïøp>Ð|>Ñi">уö>Ѹé>ÑP€>І™>Ð-:>Ï~Í>Îõk>Îu>ÎMµ>Γ>ÍÏ#>ͳ„>Ͱ1>Íå­>Î6 >ÎÀO>Ïq¦>ÏÝ÷>ÐÙ>Ѻƒ>Ò€V>Òw>Ñä3>ÑØ>Ð>ψ>Îüf>Îz >Îü>Íè>Í®ª>Íf >Í€¶>͸>Î ä>ΠK>Ï#>ÐAÅ>ÑI>Ód>Ôéd>Ôþ>Órè>Ñ©*>Ðh>ÏŠ¾>λA>Î:^>Î…>ÍÃP>Í‹ >ÌÐg>Í">ÍmŒ>ÍÀ>Î9>ήØ>Ϻ>Ñ3©>Ôï>×a3>×jŸ>ÔY >ÑÇò>ÏY2>ÎŒ#>Î.¿>Íì >͉L>ÍT‘>ÍKq>ÌÉr>ÌéØ>Í 9>Í+">ÍV>Íî>Ζl>Ð:k>Ô‡>Ü^ð>Ý>Õ:Ž>к8>Îã0>ÍÀE>Í]:>ÍM>Í(>Ì÷k>ÍO>Ìža>Ì¥)>̨#>̼œ>̤Þ>Ìš>Ì«Ñ>̬ð>Ír–>Ï?“>Ö+ý>Íf¾>Íš>Íï>ÌÈ=>ÌÍ">Ì›h>Ìyx>Ìšl>̘Ä>Ìkî>Ì]Ý>ÌA(>Ì>ËóÌ>ËiZ>ÊÌÔ>É’A>Æ-Î>¼T>»@ã>ÄæC>ÉP€>Ê=/>Ësâ>ËÏJ>Ì®>Ì(Í>ÌdD>ÌL >ÌLs>Ìî>Ëãè>Ë®1>ËWR>ʦ]>ɽ >Çá*>Äxø>Áv >Áx>Ĺ²>ÇlÉ>Ɇ>Ê >Ër>˲>ËÂø>ËðÕ>Ì!.>ËÈ>Ë«>Ë|¬>Ë9>Êò¿>ÊU§>ɨŸ>Ç>Å©I>Ä;g>Än=>ÅÅþ>Çg©>É:c>ÊØ>ʾJ>Ë"¢>Ë€ß>Ë›>ËÅ.>˶«>ËŒ>ËFÖ>Ê×”>Ê0û>ÉËì>É ë>È.T>Æþ>ÆCì>Æ'Æ>ÆÔ>Ç÷?>ÈÔ‡>ɯÅ>Ê^Ó>Êû£>Ë-E>ËG>Ël×>ˬÙ>ˆ^>Ë@ >ÊéQ>ÊD©>ɸË>É19>ȦÔ>Çî>ÇvX>ǃã>DzŸ>ÈQ~>É^>É«>ÊÊÁ±>ÊéV>Ë%j>ËVÈ>Ëy >ËS>Ë.¿>ÊÜá>Êlç>Éý3>Étæ>ÈÚ>È®>Èdá>ÈO`>È— >Èâ->Éeh>Éã>ÊKy>Ê­?>Êë >Ë$v>ËW >ËeT>Ë3b>ÊúÝ>Ê¡¾>Êï>Ê?Ä>ÉÁb>É]>ÉN>Èú*>ÈíÚ>É>ÉGÝ>É¿G>Ê(>Êc¥>ÊtN>Êã{>Ë&.>ËW€>Ëd½>Ë+¼>Êâó>Ê¢ö>Ê]¡>Ê%ž>Éýè>ÉÁõ>Éw]>ÉCU>Éz«>Ér‹>Éžµ>ÉÌÓ>Ê>ÊxÆ>ʪ>˃>Ë2ó>ËX>Ë7<>Ë >ÊÓG>Ê Ï>Êr¤>Ê\É>Ê2°>Ê>Éä„>É·Æ>É—G>ÉÎw>Éõ>ÊÌ>Ê<>Ê•(>ÊÃŒ>ÊËÑ>Ë—>ËQd>Íî>δ>Î7*>Î`¥>Î{³>Τˆ>ÎÚ‹>Îî‹>Îÿ¿>Ï ä>ϳ>Ïr>Î÷K>Îé3>ο®>Ή1>Î\>>Î;³>Î$ï>Îu>Î>Î*î>ÎL>Îsì>ήf>ÎÚÛ>Îð†>Ï)w>ÏUÅ>Ïc>Ïi¼>Ïj)>ÏKé>Îð®>εé>ΰø>Îyˆ>ÎK3>Î<¹>ÎB>Íü¿>Î}>ÎLµ>΃à>δª>Îëo>ÏA¯>Ï z>Ϫa>Ͼ>ϵv>϶¹>Ï¡n>ÏGî>Ï‘>ÎÁO>Î… >ÎJª>Î+Ë>ÍÒµ>Íö]>ÎÏ>ÎZÿ>Π1>ÎÅþ>Ï ·>χ·>ÏÁÇ>ÏøÂ>ÐCÛ>Ð?ç>Ïë/>Ϫ®>Ï•D>ÏD&>Ïþ>Π÷>ÎIH>ÍÝ>ÍÅP>ÍÝE>Î Ü>Î?ª>Îmë>ÎÌã>Ï->χµ>ÏÇš>ÐG¥>Ї†>КÕ>Ð~É>п>ϪÐ>ÏUº>Îé->έ‘>ÎWØ>ÍôN>ÍÆµ>Í•ä>ÍÝ–>Î">Îs‹>ÎÐD>Ï]O>Ïí!>ÐMr>ѹ>ÑÉ>ÑGF>Ðþp>Ð|ô>Ïèò>Ï\¢>Îî >Μ¬>ÎD¤>Íú>Í¿Ï>͇>͵ü>Íü>Î@J>βž>ÏJ¾>Ï Ô>б]>ÑJ >Ñí/>ÑÖ“>ѹw>Ðè->Ð Ì>Ïg>Îì7>Îwª>Îi>Íá>ͨ|>ÍR€>Í‘*>ÍŸ>ÍõQ>ÎÌ>Ï>ϽÝ>Ñ>Ò8°>ÓP'>Ó_ >Ò‘‡>Ðö>Ïüð>Ïgá>΄Ä>Î 0>Íâ#>ͱ>Í‚È>Ìô>Íú>ÍRP>Í´b>Î#ú>΋ä>Ï…!>Ñ=>Ò?¬>Ô.y>ÔHX>ÒHi>ÐÕ6>ÏÀ>Γ”>Î%>Íì+>͈+>ÍMB>Í9³>ÌÒ>ÌôŸ>Í2Ï>Í8Ž>ÍZæ>ÍÿÔ>Γ>ÏT²>Òlû>Ôm,>ÔgÌ>ÑÆ~>Ї>ÎGJ>Í¢î>ÍC¶>Ìýž>Í!æ>ÌöU>ÌøY>ÌŸH>Ì w>Ì—>̽>Ì¡Ò>ÌŒ->Í;>Í-Æ>Í}O>ÎkÁ>ÍO÷>ÌFA>̯>Ìü>Ì¡’>Ì“ä>Ì‹˜>Ì¥X>̵†>̯è>Ì`¿>ÌV˜>Ì8ˆ>Ì=Ç>ËïÉ>Ë}«>ÊÅÓ>Ê4>È?J>Äú>Å\¸>ÈÊm>ÉÌ>ÊÒ&>˘>ÌŽ>Ì!s>Ì+Ø>ÌJ§>Ìaú>Ì0ƒ>Ì!”>Ëè>˧w>ËPÀ>Ê©Ÿ>Éò¸>ÇÕ6>Æ–}>Äû^>ÄÍV>Æ¡>È‚©>ÊÄ>ÊÈû>Ë%>˃3>Ë«©>Ëøt>Ì<>ËàÆ>Ë“a>ËkL>Ë'9>Ë%Ô>ÊgD>ɦ>ÈB>Æ‚‚>Å·â>Æ‹>Æ–¯>Çø3>Éuå>Ê!ª>ʼ©>Ë2ë>ËË>ËÀ>ËÃ*>Ë¿ñ>Ë‹2>ËK¥>ÊíE>Êc”>ÉîJ>ÉTØ>ȶ>Ç”­>Æéå>Ç%>Ç©>ÈÉ$˜>Éåü>ʇ#>Ë>Ë]†>ËXÞ>ˆµ>˧B>Ëzç>Ë@]>Êõ•>ÊiN>Ééc>ÉZ€>ÈËš>È77>ǸÔ>È è>È(W>Ȥ >ÉU«>ÉÛç>ÊHu>Êžm>ÊÏÃ>Ë >Ëbå>ˇÇ>Ë]h>Ë!r>ʹÛ>Ê[²>Ê>>É­à>ÈøD>È¢´>È…!>È >ÈÉã>ɨ>Éf×>Ê >Êc¡>ʸE>Êêl>Ë>ËMë>Ëi\>ËMJ>Ë$¦>Ê´2>Êg+>Ê-l>ÉÈê>Étª>Ée+>É.4>Èñø>É à>Éf >ÉÏ@>Éüº>Êe£>ÊÐ>ÊèÁ>Ë&Ì>ËV‘>ËV>Ë6â>ÊüÌ>ʳE>Êui>Ê[>ʲ>ÉÐ>Éœ>É‘¡>ÉhF>É~>É¿ä>Éô€>Ê'ã>ʤ.>Ê»>ÊûÔ>Ë-÷>ËWh>ËE>Ë s>ÊÇ>ʲR>ʆ®>Ê]e>ÊDÔ>Ê(²>ÉíÈ>ÉÆ`>ÉË“>ÉÜ>Éñ`>Ê B>ÊG¬>Êgo>ʺC>Êò*>Ë*›>ËU²>ÍìD>Î[>Î,¶>ÎPN>ÎhÊ>Πß>ÎÁ¦>ÎÎn>Îê†>Îøæ>Îø}>Îï,>ÎØð>Î×>Ψo>Îoè>ÎP7>Î4 >Î>ÍûD>Îd>Î >ÎK•>Îx¹>Î’i>Ο >ÎÊ>Ï!ÿ>Ï6r>ÏAð>ÏB¾>Ï;Ê>ϵ>ÎËg>Ρ>Î>Înq>ÎL^>Î;>Íÿà>Íø4>Î>Î9>Îké>Οñ>ÎÔ©>Ï)?>Ï€‚>ÏyB>Ï>ÏvÎ>Ïr_>Ï}^>Ï%·>Îæ³>θó>ÎtÑ>Î2Ö>Ííx>Íé¸>ÍÖ+>Îg>Î.%>Îsä>νT>Îû+>Ïkï>Ïwå>ϵM>Ïê‡>Ïþ>϶»>Ï|->ÏhV>Ï*>ÏŸ>ΛŠ>ÎAr>Íðf>ͯš>ÍË~>ÍùU>Î)\>Înr>ÎÄÔ>Ïä>ÏR">Ï·W>Ïþ6>Ð1Â>Ð<>Ð>ÏÍH>σÀ>ÏTi>εÍ>Î}ˆ>ÎA.>Í÷„>ͺ>Í©O>ÍÝÕ>Î>ÎH]>Φý>Ï->ψ’>ϼD>Ðd >Љ.>Ф÷>Ђ°>Ð>ϳb>ÏNG>ÎÌ>ÎŽZ>ÎXÜ>ÍóÌ>ͳ>ÍZ¸>Í®x>Íö±>Î9Þ>Ρí>ÏM>Ïe¨>Ð&/>Щo>Ðíw>Ðå§>ÐË>ÐkŠ>ÏÇ?>Ï>>Ρ[>ÎU÷>Îú>ÍÜ,>Íž`>Í>Ô>ÍŠ#>͹Š>ÍìA>ÎJT>ζt>Ï6&>ÐOó>ÑÚ>Ñ×€>Ò=•>ѪI>ÏÜk>Ï>ü>Ï >Îl>ÍöA>Í“ê>ͦë>Ív…>Ìý”>Í™>ÍIv>ͱù>Íü,>ÎUÒ>Ï>Ïåh>ÐöÂ>Ñfó>ÑÚF>ÐÚe>ÏÔq>Îø÷>Î.>Íö>ÍÞ“>ÍnX>Í$ï>Í !>ÌÝJ>Ìóp>Í>Í9H>ÍP‚>ÍÞÚ>Î"§>Îtd>Ï`¾>ÐmÏ>Ð$q>Ïy">ÎÍJ>ÍÝ1>Í_`>ÍJî>Ìÿ¼>Í[>Ìô>ÌöI>̵ª>ÌÀÇ>Ì©é>Ì´>Ì¢•>̨Ô>̲ñ>ÌŽp>Í>Íox>Ì‚ÿ>Ìá¸>Ìá>Ì£>Ì©î>Ìû>̃s>Ì­?>ÌÄÊ>ÌÈö>ÌD>Ì&£>Ì+%>ÌK>Ëî>˃>ÊÒy>Ëô>ÊN >ÉV‡>É¢‘>Ê*>Ë÷>ËŽ>ËÇd>Ëáå>̨>ÌVm>ÌNé>ÌJ¢>Ì ¦>Ìc>Ëæ®>ËÅó>Ëa·>ÊÇ>ÊA¸>É)G>Èà>ÇÆÙ>Çä†>È3•>ÉYL>Êv³>Ë?í>Ëp>ËœS>˼H>Ì >Ì(¾>ËðÜ>˽ó>˨>Ë/p>Ë6_>ʯí>Ê^g>ÉMÍ>ÈÊ>ÇyG>Ç€Ž>È >ÈÅi>ÉýÙ>Ê_P>Êõ*>ËEg>ËŽ>˵&>Ëê÷>ËÙ–>Ë¡>Ë^>Ë €>Ê¢Ð>Êe>É“O>É >È<’>Çöø>È/)>È‚y>Èü)>Éz>Ê[>ʳ¹>Ë Ù>Ë-R>ËX>Ëo>Ë·²>ˉè>ËK->Ë >Ê‘†>Ê ~>ÉÆš>Ép >È­K>ÈJZ>Ƚ>ÈÐÄ>É‹>Éšê>Êk>Êj$>ÊÐÉ>Ëm>ËBÓ>Ëx$>˧>Ënõ>Ë+,>ÊÁG>Ê{Ô>ÊGÄ>Ê>É[à>É>Èߊ>ÈÞK>É-Æ>É}Ÿ>É í>Êí>Êà>ÊÝ€>Ë>Ë;ú>ËcV>Ë\Ð>ËG’>Ë$x>ÊÌØ>ʆˆ>Ê9>ÉÑ>ɘ>>É™>Éh²>ÉTù>Épx>É„b>ÉÂ>Ê!Ñ>Ê|X>Ê¥ì>Êý»>Ë7“>Ëa•>ËAg>Ë;›>ˆ>ÊÁÃ>Ê…ƒ>Êþ>Ê)Ô>ÉçÀ>ÉÛ>ÉÛ—>É>ɶ(>Êá>Êê>Ê:È>Êos>ʾX>Ë k>Ë9Ñ>ËZ¨>ËN¥>Ë>>Êñ >ÊÍí>Êœ<>ÊY¯>ÊOe>Ê/>Ê!m>Éý>Ê!>Éø´>Éø">Ê3q>ÊgD>Ê‘ò>ÊÍ(>Êÿþ>ËAþ>Ë^™>Îs>Î0>ÎÃ>Î9Ý>ÎLh>ÎnL>Î&>Ϊ“>ÎÏù>Îã.>ÎÜ>ÎËp>ΰ >Θ>ÎwÛ>ÎVø>ÎBü>Î->Îï>ÍíÆ>Î>Î W>Î0Ø>ÎWþ>ÎrR>Ε¥>ÎÆï>Îø6>Ï#…>Ï3¡>ÏÓ>Îò>Îë¾>ÎÄT>ÎA>Î}ë>Îs>Î_s>Î%>Íëp>ÍÝ>Íù`>Î$Õ>ÎW >ÎŒ¢>ÎÊÏ>Ï(J>ÏQ>Ï2p>Ï)Ó>Ï8Ä>ÏÏIÿ>Ï?>ÎÐT>Ι?>ÎG$>Î>ÍÞ¯>ÍÒ›>Í´.>Íé8>ÎO>Î[Z>Î¥>ÎÚ\>Ϙ>Ï?Î>ÏaA>ÏŽÒ>ÏŸû>Ϩ>ÏP®>Ï R>Îí¹>η5>ÎjR>ÎAß>Îó>ÍÁ">ÍŸ]>Íܘ>ÎW>ÎP>ηì>Îõ4>Îôà>Ï/ú>Ϙ¡>ÏÔ=>Ïâ>ÏÆL>Ϙ>ÏB>Ï>Τ->Îct>Î$>ÍüS>Í»>ͨ>ÍÏ>Íù¯>Î&À>Î^¹>Îò1>Ï6r>Ï[C>ÏÀ¹>Ïæ¦>Ð):>Ð_>Ï~f>ÏSƒ>ÏE>Ψ>În>Î!ü>;î>ͦŠ>Ía>Í·c>Íä<>Îó>ÎXÉ>Σ*>Ïç>Ïmk>Ï«e>Ïçà>Ïûz>Ïß>ÏÔª>ÏQP>αÁ>Îpu>ÎJT>ÍüR>ͺÃ>Í„z>Í,ù>ÍoN>ͽ>ÍèŽ>Î/¬>Îi)>ÏÌ>Ïm;>ÏäÖ>ÐB\>ÐA¼>Ð"\>ÏRç>Ï(€>Î}>Î%Œ>ÍÝ>Í6>ÍŒS>Íaõ>Ìô±>Ìûõ>Í=[>ÍŸÿ>ÍÅ >Íâ…>Îa}>Ï-<>Ï«>Ðs>ÐS¤>ÏT:>ÎÕÅ>ÎS?>Íò±>Í˯>ÍÉ<>Íe·>Í%Y>Íw>ÌØ >ÌðÎ>Í>Í ®>Í4ñ>Í>Í~>ÍÆÙ>Γ™>Îãd>έÎ>ÎL>Íÿ>ÍÚÇ>ÍdÓ>Í0‘>ÌìÎ>Ìù‰>Ìîg>Ìéí>̬À>ÌÇN>ÌÇt>̺÷>Ì’ß>Ì—¹≯õ>ÍŒ>Ìݼ>ÌëM>̉/>ÌßK>ÌÇ}>Ì~µ>Ì™u>̘J>ÌuŸ≯Ž>ÌÂÇ>̺Ž>ÌR÷>Ì"Æ>Ìí>ÌAo>Ì’>ËÂ÷>Ë›’>Ëaö>Êø->ËŽ>ËKë>Ë>ËlA>˲2>Ëݾ>Ìû>Ì<&>ÌBN>ÌF'>ÌT\>Ì$ñ>ÌO>Ëéš>˰«>Ë{´>Ë%|>Ê«™>Ê-C>ÊNv>ɰä>ɬ¹>ɲO>Êú>Êãš>Ëe>ˬ„>˺¼>ËË>ËúK>Ì¿>Ëûk>ËÓ+>Ë—Í>Ë|»>Ëdè>Êúÿ>Ê‹Þ>Éí¯>Ég\>É>Èée>É+Ë>ɶ–>Êk„>Ê×£>ËÅ>ËG*>˳ý>ËÉÃ>Ëìõ>ËïÂ>Ë·>Ëtñ>Ë9ñ>Ëa>ʺ>Ê"¦>ɲn>Èø}>ÈÐ;>ÉÒ>ÉDÚ>Éš¥>ÊP>Êx<>Êîì>Ë¢>Ë+²>Ën›>Ë¡w>ËÆ>˧>Ëbù>Êö6>ʆJ>ÊLR>Ê8¨>Éõñ>É9…>ÈÜ5>É.R>ÉoN>ÉÀì>Ê%D>Ê]„>Êšò>Êé­>Ë"_>Ë`>ËŽé>Ë“Ž>Ë~…>ËB0>Êç«>ÊœD>Êlÿ>Ê9Ù>Ê •>ɘý>ÉSA>É!Ø>É~á>ÉÑÙ>Éã^>ÊOQ>Ê«¿>Êë!>Ë&6>ËgZ>ËŒ>Ëiò>ËH>>ËÒ>ÊÞÓ>Ê¥×>Êk’>Ê"¼>ÉÇö>ɳa>ɶ>ɤÙ>ÉÇL>É×,>Ê >Êi¡>Ê¿‚>Êåð>Ë't>ËSI>Ëu(>Ë]‡>ËiY>Ë" >Êãœ>Ê´Ö>Ê“>ÊII>Éöü>Êè>Ê»>Éó%>Éú‚>Ê >ÊA3>ÊoA>ʉþ>ÊÝÔ>ËFø>ËTÉ>ËcÅ>ËT‹>˶>Ë1>Êêþ>ÊÆ>ʧ‘>ÊoG>Ê@“>Ê^>>Ê27>ÊB«>Ê'X>Ê*&>Ê]»>Êö>ʺu>Êâ7>Êèþ>ËB¤>ËfÍ>ÍêE>ÍñÛ>ε>Î 0>Î0e>ÎLN>Îp>ÎŽ >Î¥ð>ÎÊs>μq>Σ1>Άì>Înœ>Î\¦>ÎBž>Î'Ý>Î~>ÎW>Íé>β>Íýs>ÎR>Î6´>ÎT…>Îw¤>΢Þ>ÎŨ>ÎÜ>Îæ>Îèà>ÎÐ >ξA>ΣX>Έa>ÎfÖ>ÎGx>Î.Ã>Î ”>Í×>ÍÂË>ÍçË>Î4>ÎB>Îrþ>΢A>Îä´>Ï^>Ï>Îà—>Îä¢>Îø >Îò>ÎÕ>ÎÆ¹>Α”>ÎOz>Î>͸)>Í·È>Í >Íׯ>Î â>Î\`>ΗÅ>αM>ÎÄ<>ÎÝÌ>Ïå>ÏL>Ï7o>Ï1'>Ïâ>Îé€>ο*>Έ¦>ÎAy>Î!ž>Íë—>ͼG>ͬÝ>ÍÏR>Íök>Îã>΋Ï>ÎÂx>ÎÌ >Îúb>Ï+µ>Ï,?>ÏZz>ÏO>Ï,*>Ïx>ζè>Îpó>Î@{>ÎÙ>Íîd>Í»­>Í¢ü>Ͷ¾>ÍÝ©>Í÷™>ÎM>Ά8>Îã·>Î÷*>Ï&I>Ïz>Ï¿’>Ï€>Ï3»>Ïh>ΣÉ>Î`·>Î=.>Íó¢>ͬò>ÍÀ>Í>M>͉$>ÍÖÂ>ÍïH>Î#U>ÎŒ–>ÎÄg>Îç†>Ïg>ÏZ>ÏIÿ>ωÊ>ÏV2>ÎË|>Îmƒ>ÎV>Î\>ÍÛ)>Íž¤>ÍN>Í>ÍV€>Í£ò>ÍÂ>Íõ>Î%6>Îv˜>ÎÊž>ÏÂ>Ï?>Ï5P>Ï ÷>ÎЗ>Αë>Î*Í>Íݾ>ÍÈ«>ͤ>Í–ù>ÍPí>Ìø>Í ‡>Í_>Ís>ÍÀD>͉à>Íݵ>Îrw>Ωp>ÎÛé>ÎðB>΂‘>Î-%>ÍòÆ>ͶÎ>Í„ >Í€â>Íd‘>Í(´>ÍX>Ì×>ÌóB>Ìóy>Ìø·>ÍŒ>Í1Ø>Í]h>͹7>Íïs>Î.î>Î »>ÍÖ'>ͺ÷>Í…œ>ÍI>Í4ä>Í¥>Ìë>ÌÛ>Ìܹ>̬J>ÌË>̹ä>Ì­A>Ì„>Ì¥ú>ÌÂ4>ÌÓ˜>Ìê•>̽û>Ì©ò>ÌÖt>̱L>ÌŽÝ>Ì­ >̳>Ìwr>ÌÀõ>ÌØ‹>̹à>Ì`u>Ì&:>Ì0F>Ì[N>ÌOÞ>Ì ÷>Ëá¬>Ë«—>ˆÒ>ËŒö>Ëu¼>Ë•¬>ËÊF>ËÌF>Ëïœ>Ì"Ï>ÌDÀ>Ì;‘>Ì(•>ÌM¦>Ì1J>Ì H>Ëù¯>ËÁÜ>ËžV>ËsÖ>Ë0ë>Êè6>ÊÔ˜>ʰÄ>ÊgE>ʃÅ>Ë ý>Ë6P>Ëo¾>˼þ>ËÅÝ>ËÆì>Ë÷ë>ÌN>Ëù>Ëç!>Ëîß>Ëœ[>Ëaß>Ë;U>Ë9>ÊŒí>ÊB>Éêê>Éýu>Ê >Ê>0>ʪ.>Ë3>Ë@“>Ë„K>Ë®§>ËÈB>ËëÆ>ËáÁ>˽»>Ëz^>Ëd[>Ë<è>Êí@>Ê_ž>Ê*M>Éý“>ɪ«>ÉÉ–>ÉÜŒ>É÷Ÿ>Ê}Ê>ÊßÐ>Ë-Ï>ËIX>Ë^Å>ËŽ >Ë»8>Ëσ>˨>Ëf×>ËO>Êó¸>ʘi>Êlk>ÊCÌ>Éâ·>ɧ©>ɵ¥>ÉÔÝ>Ê-¦>Ê™Ó>ÊÉÊ>Êëx>Ë à>ËA>Ëwù>Ë¢ >ËÕ >Ë•õ>ËSþ>Ëñ>ʶñ>ʘ±>ÊqÇ>Ê?2>ÉèÉ>ÉÕq>ÉDz>Éí>Ê!>Ê?>Ê>ÊáÒ>Ë {>ËCL>Ëq&>Ë“õ>Ëe>Ë{/>ËE>ÊåC>ʾc>ʨA>ÊŒ’>Ê<>Êž>Ê#>Ê©>Ê[>Êè>ÊRã>Ê­$>Êó0>ËR>Ë4r>Ë^è>˃m>Ëd³>Ë=ß>ËF>Ê÷=>ÊÙŽ>ÊÆ>Ê­b>ÊZ3>Ê8ð>ÊÖ>Ê ¢>Ê7P>ÊM®>Ê–>ÊÀ4>ʲó>ÊõÏ>Ë7Z>ËZ]>Ëw\>Ëbá>Ë2ù>ËP>ËÉ>Êå>ÊÑ?>Ê}‡>Êf>Êa>ÊJ›>ÊS>Ê]Û>Êk¨>Ê~Ñ>Ê¢Š>ÊÝ•>Ëî>Ë!ö>ËPu>Ës¯>ÍÇ‚>ÍÙ>Íô&>Î D>ÎÚ>Î/î>ÎO¯>ÎkÎ>Î|;>Ζw>Α>΀l>Îo>ÎWx>ÎB{>Î)q>Î5>Î>Î+>Íëþ>ÍÐw>ÍäÇ>Î ý>Î"“>Î9g>ÎSì>Îp•>Îz=>ΘZ>ÎÂ@>ΰ>Î¥¶>Ρ£>·™>Îh>ÎI>Î'{>Î(>ÍÐa>Í©2>ÍÀ®>ÍÙ$>Íïº>Î!n>ÎT>Î|€>΢·>ζ‰>ÎÔ\>ÎO>ή”>ÎÅk>μX>Θ+>ÎÄ>Îu£>Î?V>Íåù>Í£ù>ͦ>ÍÃ’>ÍÖ>Íì>Î!o>ÎZ1>Άf>Ϊ/>ÎÀ>ÎÖ™>ÎÎÆ>Î×»>Îë>Îåÿ>ΩJ>΃[>ÎNî>Îç>ÍóÈ>ÍÈd>ͬr>ÍÒü>ÍÍ>ÍÝ>Îi>Î:½>ÎZH>Î|D>Îëg>Ï >ÎðÊ>Îð>ÎÚò>ÎÁû>Σú>În©>ÎJ¥>Î0->ÍøÏ>ÍË >ͪ>ÍnD>Í“q>;>Íá[>ι>ÎM¶>΃>Α9>εQ>Ï{>Ï â>Ïo>ÎÂs>Μ9>Î\°>Î7_>ÎBê>Í÷…>͵h>ͨ±>Íi>Íat>ͤ->Íͨ>Îh>ÎPÒ>ÎZs>ÎV…>ÎÁ>ÎÈ >Îáƒ>Îåã>ÎÑë>Έ~>ÎGi>Î,>ÍÎÖ>ÍÎ>Í‘ó>ÍQ{>Í ”>Í>>̓›>Í› >ͧÌ>ÍÙç>Î Õ>ÎI>ÎZæ>Î|>ΤŽ>În=>Î)’>Î f>Í÷>ÍÀÝ>Íz<>Í{>Íi¢>Í>~>Ìõ!>Í Ý>Í)>Ífº>Í…½>Í|g>;‹>ε>΂>Î >ÎAˆ>ÍâÐ>ÍåÒ>ÍÖ‘>Í|>Í*|>Í7à>Í6>Í%‹>Í›>ÌÓo>ÌÛx>ÌÏ>Ìà>Ìþ>ͱ>Í=>Ís@>Íd>Í–’>Í„ >Írw>ÍEÃ>Í>+>Í E>Í4>ÍÞ>Ív>Ìâó>ÌÐk>̵3>̯U>Ì—Ð>ÌŒ[>Ì`>Ì”>Ì®ÿ>ÌJ>ÌëU>ÌÅ>̾î>ÌÊá>̪]>Ì—E>̳?>Ìš>ÌŒ>Ì®k>̵Ó>̦Ç>ÌuÃ>ÌeS>ÌQx>Ì?â>Ì(š>ÌGà>Ì7m>Ëì.>ËÀµ>ËÚ>Ë«˜>Ëí†>Ì ¼>Ëãs>Ìï>Ì,š>Ì>¸>Ì7w>ÌQ’>Ìa>ÌL3>Ì2>Ì Ç>Ëô£>ËÛ|>˸>ˈ >Ël>ËJ¶>Ër×>Ë4î>Ë2Ð>Ë>Ëx³>Ë—>Ë̘>ËòF>Ì>ÌC‰>Ì5¬>ËÙÝ>Ëè>ËâR>˽M>˨Â>Ë’>Ë-ƒ>Êôf>ʽJ>ʃq>Ê¡>ÊÉÅ>Êã >ËR>ËV4>ˉà>ËÁý>ËÈ&>ËÄX>Ëñ>ËàÚ>ËÎÃ>˱>Ë—i>Ë[¬>Ëï>ÊÏÔ>Êœ²>Êtñ>ÊRë>ʃï>Êa3>ÊmÑ>Êå'>ËCÑ>Ë^Ù>Ëhœ>ËV¶>ËŒ©>ËÈ#>ËÕw>Ë»n>Ëž¿>Ë®^>Ë2[>ʶË>Ê´Ô>Ê¡Q>Ê~>Ê`>Ê<>Ê=Q>Êqþ>ÊË>Ë$ã>ËJ'>Ë< >Ëbð>ËŽ>˳n>ËÆ:>Ë¡5>Ën³>Ë >ÊìC>ÊŽ>Ê w>Ê„ >ÊXj>Ê=r>Ê7Þ>ÊN>Êe >ÊŠÛ>Ê®^>ÊúÔ>Ë2A>Ë|s>ËŽÏ>Ë¥2>Ë’¿>ËvÔ>ËB%>Êó3>Êíc>Êá·>ÊÍr>ʽÛ>ʆ>Êi >ÊZ>Êh{>Êw">Êžk>ÊѼ>Ë >Ë'Â>Ë)ˆ>Ëa¤>Ë”N>Ë‚>Ë]Û>Ë8Û>˼>Ë/>ËÒ>Êõ$>Ê•ó>Êt\>ÊQ·>ÊRf>Êrd>ÊŽ|>ʲ¾>Êä>ÊíU>Ë>Ë?5>Ëc:>Ë…Í>Ëy‡>ËX>Ë;°>Ëo>Êüñ>ÊÊ®>Ê´¾>ʉB>Êz >Êu?>Ê‚l>ʘk>Ê©>Ê»k>ÊÆc>Êð¿>Ë3Ú>ËS›>ËdH>Ë|Ï>Ͱ—>Ͷ>ÍÞÚ>ÍóQ>Î>ÎÓ>Î2>ÎFK>ÎRð>ÎuO>Îw‰>ÎdË>Î^e>ÎG¥>Î&Ø>Î œ>ÍùÜ>Íéz>ÍâQ>ÍÂO>͵v>ÍÐ~>ά>Î>Î#>Î,g>Î?•>ÎCJ>ÎY6>Î['>Îeø>Î{ä>΋ß>Îr¸>ÎDb>Î# >ÎH>Íã\>͹’>Í£A>ͯ@>ÍÂí>ÍÅL>Î >ÎVI>ÎS&>Îap>Îmz>Î}`>În>Ît€>ÎŒÊ>Ι>Îh~>ÎPV>Î4I>Î >ÍÛN>Í®>ÍžV>ͯ+>Í¿ë>Íä>Íæ>Î66>ÎW9>Îk¿>΀þ>Ηž>ÎŽÿ>ÎŽ`>ΠT>΄“>ÎZ‡>ÎMh>Î2Ö>Î`>ÍÝq>͸ >Í “>Í¥j>Í´r>Í¿¾>ÍÝ'>Ι>Î*a>ÎHe>Έž>ÎÀ–>Ϋt>ΕÆ>ÎrW>ÎH'>ÎP>ÎW„>ÎT¤>ÎT>ÍÝ>ͳˆ>Í—k>ÍV„>ÍzÉ>ͤW>;§>ÍÏÀ>Î Ã>Î2Â>Îdd>ÎaQ>ίb>ΗI>Îc>ÎM9>Î>>Î|>Íû >Í÷>Í˲>Íž->Í‚p>Í.¨>ÍPW>Í‚±>ͰC>Í»>Íâ¸>Î ú>Î~>ÎRI>Î}t>Îp >Îl<>Î_µ>Î<à>Î C>ÍÆŠ>ͨP>ͤO>Í|>Í_ô>ÍL>Í6à>Í|ü>Ír0>Ín7>Í¡³>ÍÔÕ>Íü@>Íïô>Íã¦>Î"Ÿ>Î Ó>Î Ý>Íòþ>͵Ó>Í€§>Ím>ÍoÊ>ÍQÂ>Í6s>Ìû>ÍÁ>ÌÿG>Í!Í>Í>A>͇0>ͨÎ>ÍÉq>ͪ·>ÍÆ°>Íû >ͯà>Íz>Í>Ín>Í4ú>Í£>ÍJ>ÍP>Í7²>ÌБ>ÌÏ_>̾+>ÌÏ>ÌéC>ÌÞ×>Í-J>Í@2>ÍIi>ÍJ>ÍQ[>ÍHÜ>Í-…>Í,l>Í’>Ìû´>Ì÷Ï>Ìèi>ÌÚ>ÌŸš>Ì»×>̲>̘Ú>ÌÌ>̈>̇Ä>̘t>Ì•>̼¾>̤ ≯x>ÌÑ >̲ê>ÌÔ>ÌÀs>̺n>̹·>Ì®=>Ì¥>Ì’«>ÌuÛ>ÌŒd>Ìj·>ÌaÖ>ÌRÒ>ÌCª>Ì7>Ì v>Ì }>Ì Ì>Ëð>Ì3>Ì>Ëæ·>Ëýã>Ì: >ÌR&>Ì_÷>Ìn>ÌjK>Ì&>ËùÂ>Ìñ>ÌÜ>Ì >Ëít>˾ü>ˈ‘>Ë®u>˦Ë>Ët™>Ëyâ>ËÎV>˹X>ËÁ%>ËÜd>Ìn>Ì@>ÌjÖ>ÌGi>ÌN>ËöO>Ëë±>ËÆE>˺Ž>ËÂ>>˦X>Ë[û>Ë :>ËÏ>ËF>Ë:¦>Ë(’>ËV>ËŒ1>˼Û>Ëõï>ËåP>ËÓ„>ËÿK>Ëöó>Ëç’>ËÒ >Ë´Y>ËŽ{>Ë`æ>Ë>ÊþG>Êæ>Êá>>Êúd>ÊË>Êä'>Ë4@>ËtG>Ë…>˘>Ë‹ý>Ë¥ò>ËØ­>Ëã>ËÚ=>ËÃ>>Ë£Ê>ËYõ>˼>Ëy>Êè–>ÊÕ£>ÊÏŒ>Ê">Ê­Ó>ÊÖ×>Ë ‹>Ë5Z>ËLŒ>ËJ`>Ëš¸>˪^>ËÅ÷>ËÏ>ËÈ{>Ë¡Ï>ËU>˼>Êé²>ʾa>Êı>ÊÈó>ÊÆT>Ê£»>Ê­Ñ>Ê´ª>ÊÓí>ÊúQ>Ë0‚>ËLî>Ë‘9>ËžÈ>˸/>Ë«û>Ëu§>ËO”>Ë5¼>Ë!Y>Ëb>Êí>Êä´>Êê¹>ʱÔ>ʧï>ÊÊ7>ÊË>Êå>Ë5>Ë?ã>Ë\f>Ë/]>Ëtè>Ë»K>Ë›G>ËqÝ>ËRB>ËB<>Ë?7>Ë8>Ë>ÊÛ>ÊÃ>ʼ>Ê×>Êž{>ÊÃ>Êâf>Ë}>Ë Ï>Ë"Ù>ËI>Ëqù>˘~>Ë»>Ëoˆ>ËRå>Ë4>Ë 6>Ë>Ël>ÊÏ>Ê@>Ê›i>ÊÆ¨>ÊåÐ>Êßj>Êì‹>Ëm>ËÑ>Ë<>Ë\Á>Ëkä>Ëoô>ÍžA>Í­à>ÍÄø>Íà<>Íöš>Î)ø>Î$\>Î ¬>Î' >Î77>ÎGÍ>ÎF>Î6ò>Î"ß>Î 9>Íñâ>Íá×>ÍÔY>ÍÚ’>Í·‰>Í ±>Ͷ›>ÍÖ‘>Íð>Ζ>Íæ>Îò>Î&,>Î7¸>ÎDí>Îp·>Îeu>Î\§>ÎJo>Î!¤>Î>Íå>;ì>͘%>ÍS>Íš>ͯl>ÍÇ÷>Îê>Î1™>Î$õ>Î+t>Î:Î>ÎKç>ÎF—>Î' >Î9>Î_y>ÎD >Î$à>Î Œ>Íî>ÍÄQ>͘p>Í‚>ÍŒ>Í¢h>ͯ3>ͱr>ÍÝ€>Î!»>Î6å>ÎGç>Îjr>Î[Ì>ÎDë>ÎRw>Î4»>Î+ª>Î=>ζ>Íã>ÍÄÆ>ͧÕ>Í>ÍzJ>Í–5>ͦB>͸«>Íí|>Íö>ÎŽ>Î2«>ÎHs>Î\½>ÎM„>ÎC>Î]>Îá>ÎŒ>Î>Íç„>ÍÆc>ÍŸŸ>ÍŠE>ÍZU>Í•Ò>Í¡>Í›Ì>Í‘Œ>ͺ;>Íêš>Î6>Î&@>Îj >ÎA >Î#ô>Î Ö>Íòµ>ÍÝI>Í×Ö>Íݧ>ͨB>Í€Z>Ícå>Í0·>Í'²>ÍM.>Í{A>ÍÉ>ÍΆ>ÍÞ%>Î ò>ÎÁ>Íìí>Îö>Î >έ>ÍêU>ÍÅœ>Í–j>Íq>Ífˆ>Í^¸>Íj>ÍQ>Í*u>ÍCu>ÍMÊ>ÍTÊ>Ír>Í¡´>ÍßQ>ÍËQ>Í·]>ÍÐ>ÍÁ™>ÍÛ°>;?>͈ê>Í_µ>Í?z>ÍB>Í1ÿ>Í(>ÍG>Í™>Í >Í>ÍA>Í3n>Í\‹>Í^S>Íh5>Í{ú>ÍœÎ>Íx>ÍWS>Í[w>ÍPo>ÍÍ#N>Íò>ÍÞ>Í„>ÌÂè>̽>Ì¿Ñ>ÌÐ>ÌÛq>Ìëö>Í">Í™>ÍA>Íw>Í8–>ÍE¡>Í»>Ìö>Ì÷Ú>Ìô(>ÍÁ>Íü>Ìþè>ÌÍ>̉Î>̘…>Ì—(>Ì—P>Ì”¢>ÌM>̾>̦W>Ì©ô>Ì«è>ÌÀ|>̱>̧+>Ì´°>ÌÑS>ÌÑ">Ìáí>̹A>ÌŸ¦>Ìœû>ÌjC>Ìk_>Ìd¾>Ìaë>Ì\;>ÌbO>Ì5Æ>Ì#Â>Ì2L>ÌK>Ì\>Ì4>Ì'Ï>ÌB>Ì ³>ÌNÄ>ÌI>ÌcŒ>Ìeï>Ìft>ÌÌ-m>Ì))>Ì#A>Ì)>Ì>ËÜ >Ë»’>Ë̘>˵Î>Ëœ­>Ë¿S>ËØ9>Ëâ>Ëß >Ëã>Ìg>ÌG™>ÌTf>ÌE‘>Ì#†>Ì•>Ìw>ËöŸ>Ë÷~>ËÝ8>ËÀC>ËšE>Ëy>ËjM>ËX,>ËŠà>Ë„½>ËŒF>ˬ>ËÈ/>ËÐ>ËáQ>Ì–>ÌÆ>Ì#>ÌÇ>ËïÈ>ËÛ<>ËÂÒ>ˇn>ËTI>Ëe>ËI>ËF>ËBŠ>Ëf>ËEJ>ËiM>Ë ø>˲ß>ËÆ`>˲ö>ËÀq>Ëî4>ËçÒ>Ëâ>Ëà>ËË8>Ë“>ËG¸>Ë+1>˽>Ëu>˹>Êò(>ËC>Ë5i>ËE×>ËQ9>Ëi8>Ëx>ˬÅ>ËÂV>Ë×m>ËÙr>ËÒ>˾H>Ë|Ù>Ë+‘>Ë>Ëð>Êúq>Êþ>ÊóÜ>Êôâ>ÊôÀ>Êþ<>Ë(—>Ë;Ù>ËVÓ>Ëf±>Ë„Ì>Ë” >˽>ËÆ>˺€>ˈ—>ËYö>ËEl>Ë/>Ë>Ë7>˹>ÊéE>Êå¢>ˬ>Ë`>Ë;³>Ë?ô>ËaÀ>ËuÊ>Ëi>ËŒ>Ë»ò>˳®>Ë}‘>Ë`‰>Ë^Œ>Ëc@>Ë:¦>Ë$þ>ËÏ>Ë >ÊðÝ>ÊÕ>ʰê>Êî>ËZ>Ë8>Ë]>>ËcË>Ëmx>ˆ»>Ë¥O>˸Õ>Ë}I>Ë]1>ËH0>Ë(>Ë*þ>ˆ>Êö#>Êì;>Êõ_>ÊõÌ>Êøò>ËC>Ë á>Ë)‡>Ë1>ËHÙ>ËjX>Ë~Ì>ËŠ/>Í¡>ÍŸÍ>͵7>ÍØE>Íë™>Í÷S>Í÷:>Íüp>Î P>Îí>Îþ>Î'G>κ>Ρ>Íñ>ÍÜá>ÍÈ>;?>Í­>Íšy>ÍÑ>Í£™>Í»[>ÍϦ>Íà¯>Íé.>Íö¯>Î ò>Ε>Î"“>Î;^>Î< >Î"i>Σ>Î>Íè>ÍÉÝ>Í‹q>Í“Ù>ÍÄ>͈@>Ížw>͸u>ͽ:>ÍÕñ>Î&Ì>Îè>ζ>Î&>Î$>Î¥>Íñê>Îá>Α>Îí>Íñ¿>Íò¼>Ͳ¼>͘Ã>Íð>ÍvÄ>ÍŠ×>Í”á>Í¥'>͸í>Í¥´>Íé_>Î Ú>Î:>Î=>Î;>Η>Îè>Î L>Íà`>ÍÍô>ͨê>Í©…>ÍŸJ>Í´ž>Í`e>ÍwL>͆±>Í›”>Í·>ÍÁ>ÍÞ/>Íóâ>Íú°>Î'E>Î5>Îg>Íýö>Ííƒ>Íßl>ÍÇæ>ͨµ>Ͱ»>Í/>ÍŒ¦>ÍHØ>Íd >Ís¼>Í…>Í«>͵û>ÍÌR>Íâg>Íܯ>Íù›>Íõ‚>ÍÔÓ>Í×\>ÍÖª>ÍÔ%>ͳ•>Íš]>Í >ÍZH>Ͱ>Í+«>Í2H>ÍG>Íb>Í…->Í·\>Í¿?>ÍðÉ>ÍÅj>Íí>Ͷ8>ÍÇT>͵Ö>ͪÊ>ͳñ>ÍŽ®>Í_ô>Í8‡>ÍÍ/È>Í>Í*„>Í4Ä>Í;">ÍL>ÍnR>Íp–>ÍgN>Í“S>Í‹^>Íž >ÍÄ™>ÍŸ>Ív>ÍF]>ÍHŒ>Í9F>Í*ü>Í •>Í ö>Í –>ÍU>Í;>Ͷ>Íp>Í m>Í1 >Í3$>ÍM‡>ÍW>Í^>ÍA‰>ÍUÝ>ÍF{>Í3>Í4>Í'>ÍÇ>Í>Ìÿ>ÌÂ>Ì»n>ÌÀˆ>ÌÐí>ÌÒw>ÌïO>Í«>Í4>ÌíÛ>Í•>Í0>Í/²>ÌîÄ>ÌÙs>Ì×V>ÌÖ¨>ÌîQ>Ìö°>ÌïM>Ìî>Ì‹>̺>ÌŸ(>ÌžÐ>̵>Ìy>̼>ÌË^>̱Y≯U>̵³>̉c>Ìš€>̲p>Ì«B>̰ª>ÌÆ‚>ÌÒÆ>Ì»g>̽>Ìj>ÌYö>Ìb‡>Ìlµ>Ì_>Ì]ï>Ì=Õ>Ì/î>ÌNÐ>Ì\ú>ÌoD>Ì?#>Ì6E>Ì7ó>ÌOž>Ìeù>Ì·>Ì¡ˆ>Ìyì>ÌJñ>ÌJó>Ì;«>Ì+>ÌÍ>Ì%1>Ì•>Ëÿx>Ì º>Ìm>Ëêå>ËÐ{>ËñG>Ëøú>ÌN>ÌW>Ì)>Ì'S>Ì>Ì:`>Ì<\>Ì9£>Ì*,>Ìl>Ì Ú>Ìš>ËãA>ËËh>Ëż>˺>˱å>Ë£È>Ëß>˾->ˤk>ËÅ>ËâÞ>Ëæi>ËïC>Ì—>Ì%¹>ÌF­>Ì+n>Ì>Ëï‹>ËÖÃ>˪>ËŽ;>Ë…Š>ËŠk>˯#>Ëp>ËJ|>Ë|Œ>˦>Ë¿b>ËÉy>ËÊÁ>ËâÅ>Ì >ÌË>ËÔÇ>ËÛ}>Ëë5>ËØ>Ë÷>Ë€%>Ëeù>ËJg>ËhÞ>Ëjö>Ë]g>ËQÄ>Ëj>Ëxó>Ë}‘>Ë>Ë¥Î>Ëʬ>Ëè6>ËêÔ>ËÝ>>ËØã>ËÕ>˳%>ËxŒ>Ëc>ËN}>Ë2¶>Ë!#>Êÿ©>Ë4Û>Ë7O>ËEd>ËnI>Ët>Ë>Ë‹¢>ˇ~>ËxD>Ë·˜>ËÕƒ>ËΛ>˾S>Ë–À>Ës>Ë\Û>ËL>ËC(>Ë&Å>Êú¥>Êò´>Ë!¯>ËB­>Ël:>Ë\¿>Ëuj>ˇÛ>ËŠÒ>Ë– >Ë·Ç>ËÆÝ>Ë e>ËB>Ëm>ËhY>ËWô>ËJê>ËSz>Ë'|>ËN>Ë!>Ë k>Ë6>Ë0¿>Ë`w>Ëp>Ë‚J>ËŠv>Ë™H>˰>ËÎ]>Ë”Ú>ËeD>ËYl>ËVÃ>ËO¾>Ë?à>Ë·>Ë›>Ë+A>Ë#2>Ë%©>Ë47>Ë2x>ËN>ËXõ>Ëd”>Ë}>Ë“ >˦_>̓>Í‘B>Í¢ò>Í´£>Í¿ >ÍÊ`>ÍÔz>Íâ¨>Íîœ>Íõ\>Í÷¡>ÎÎ>Ϋ>Íç»>ÍÙê>ÍÅ£>Í®ú>ÍŸ>Í“–>͇—>Í}>Í•¬>ͯ)>ͶÔ>ÍÁ¨>ÍÐ>Íà@>Î(>Íþ>Íýº>Íýê>Íø±>Íïù>Íî8>Íí¥>ÍÌa>Ͳ&>Í– >ÍŒh>Í‚>ÍzÐ>Í™ >ÍÜÆ>Ͷ>Í»´>ÍÖ\>Í×>ÍÔß>Î2>Î>Íîù>ÍÚÝ>ÍÝ)>Íâ1>ÍÞP>Íǃ>͹Á>Í›£>Í‹>Í>Íf´>Í|Å>Í\¸>ÍŽ:>ͧÒ>ͯ®>ÍÅÄ>ÍÒ¯>Íôê>Íüë>Íî9>Íï,>Íû“>ÍÕ‘>ͧ‘>Í«~>Íœ>Í‘3>Í„»>Í}÷>ÍN>Í`?>Íf§>Í„ë>ͤ)>Í®º>ͺ>ÍÀŽ>ͰD>ÍÒþ>ÍÛÅ>ÍÜ >ÍàÛ>ÍÎq>Í´¶>Í¢µ>Ív>Í…}>Ír>Ídu>Í4Ã>Í5¨>ÍFH>Ís>ͯœ>Í©F>ͪ¼>Ͳ>Ͱë>ÍÈ&>ÍÃ>Í®>ͪ>ÍÊä>ͯœ>Í‘–>ÍyO>ÍeU>ÍNA>Í1×>Í>Í$¢>Í37>ÍE›>ÍO}>Í•¶>Í’±>Í¡§>Í•>>Íx>ÍŒ>ÍC>Ío'>ÍjC>ÍŽÉ>Íy>ÍUP>Í=³>Í0A>Í ¾>Í p>ͪ>ÍŽ>Í(*>Í=7>ÍVh>ÍR;>ÍS¾>Íoú>Íj¯>Íp¨>Ízg>ÍuØ>Ín>Í:M>Í3">Í+ð>Í ‚>Íç>Í ,>Ìó>Ìøz>Ìû#>Í!>ÌþÚ>ÌôÅ>Í£>Ͷ>Í5`>Í<ý>ÍC->ÍBA>ÍP¢>Í)`>Í ž>Í «>Í”>Í>>Ìýî>Ìø¶>ÌÈè>ÌÕÄ>ÌÓ>Ìݤ>ÌÁÀ>̶>ÌÚš>Ìê>Ìà•>ÌûN>Í Â>ÍÍ>ÌÜù>̽%>ÌÏ>Ì׿>Ìâÿ>Ìê>Ìè>Ìñ¶>Ìž>̲´>̧^>̆¤>Ìy>̃Þ>Ì©á>Ì»>̹Z>ÌÜ)>Ìä­>Ì‘R>ÌzI>ÌŒm>Ìœ¶>̪¶>Ì¿³>ÌÛq>ÌÎâ>ÌÜÒ>ÌnÈ>ÌL3>Ì\.>Ìh¶>Ì[{>ÌYh>ÌSq>ÌS’>Ìt€>Ìr>Ì9Ì>Ì:P>ÌCO>ÌP>Ìau>Ìuè>̘->Ì¥c>Ì„ä>ÌB>ÌP>Ì>|>Ì/>>Ìé>Ì*>Ì&C>Ì ™>Ì,õ>ÌËüÛ>Ëèò>̵>Ì 1>ÌÙ>Ì+’>Ì?þ>Ìc>Ì7>Ì8>Ì=b>Ì=)>Ì0D>Ì!ù>Ì:>Ì>Ëû¤>Ëî¿>Ëí >ËÝë>ËÍ>ËÇE>ËÚá>ËÝÚ>Ëá¹>Ëò>>Ì|>Ëû'>ËøA>Ì$ >Ì1ê>Ì.>Ì!º>Ì>Ì€>ËëÎ>ËÒ1>˽R>Ë­«>Ë þ>Ë©Ú>˧Ï>Ë£Û>˲3>Ë¿^>ËϦ>ËÌ«>Ë»X>Ëí>Ì4Ô>Ì,‡>Ì>Ëþ©>Ëût>Ëî >ËÌ>˰G>Ë–o>Ëvñ>Ërº>ËQ>ËŸÀ>ˈ>Ë–ß>˦h>˲…>˶µ>˼ü>ËÓr>Ëèn>Ë÷Y>Ëòò>Ëî8>Ëîs>ËñØ>˺+>˘õ>ËN>Ëj‡>Ëmf>ËT³>Ë\!>Ëi8>Ë}>Ëž>˹$>Ë©¡>Ë­>ˬ›>Ë•~>ËÀµ>ËãŸ>ËÜ>Ëç|>˵ˆ>˘ž>Ë„[>ËrJ>Ëcc>ËYR>Ë6í>Ë*>ËMã>Ëi:>Ëpê>ËnL>Ën>˨°>˪ >Ë«ã>ËÂ`>ËÐÏ>Ë»Z>˱í>ËyŒ>Ë|…>Ëtž>Ëhy>ËX>Ë&>Ë.»>Ë7¶>ËI«>Ëlµ>Ëcš>ËlH>ˇg>Ë¿Û>Ë­®>Ë­½>˼ß>ËÂð>Ë Á>Ë^>Ëmf>Ësð>Ënš>Ëdf>ËP£>ËCy>ËF0>ËJ>ËQ‡>Ë_¦>Ë]ÿ>Ëg2>Ën±>ËW½>Ë„å>Ë£»>Ë·‡>͆>Í“W>Í+>Í«§>ͽs>Íʈ>Í˨>ÍÑ•>ÍÛ‰>Íà>ÍÝg>ÍàT>Íæ>Íö >ÍØß>Íà’>ÍÉe>ͪs>Í’*>ÍS>Í•§>ͧK>ͱ|>ÍÁ]>ÍÔ>Íäè>Íî>>Íõ4>Î>ÎÀ>β>Î?>Îh>Î >Íì÷>Íݰ>ÍÏÂ>ͺ>Í ¶>Íê>Í¥>ÍÀf>ÍÈí>ÍÛŽ>Íïˆ>ÎÆ>Î.«>Î#Ñ>Î+÷>Î/ô>Î*%>Î0ü>Î33>Î ó>Î>Íúö>Íä>ÍÌ>͹}>Í©>Ͳp>ÍÍó>ÍÛB>Íñ˜>Ω>Î:>ÎUä>ÎNP>ÎVA>ÎX>ÎST>ÎcÞ>ÎgÆ>ÎF¯>Î4V>Î&Q>ÎU>Íå»>ÍÐ>ͼ>Í¿÷>ÍÙÆ>Ííø>΃>Î,Õ>Îg‘>Îo…>Î~­>Î…A>΀_>ÎxJ>Î~S>ÎR>Îj>Î]º>În€>Î+ú>ÎÕ>Íç>ÍÌU>ÍÌ>Íè~>ÎP>ÎA>ÎB|>Îl>Î’û>ÎÂ>κ#>ίð>΢s>Î>κ>ΈÀ>Îx>Îyx>ÎAô>Î(“>Î ¿>Íá,>ÍÓ>Íó>Î]>Î4ò>ÎQí>΀?>ΰ>Îá'>Ï>Îñ>ÎÒ½>ÎÆ0>ο‘>Ο3>΄§>ÎjÁ>ÎJ,>ÎAç>ÎN>Íç¨>ÍÜV>Ξ>Î9¨>ÎG¬>Îa,>Îm>ÎÅD>Ïá>Ï1Î>Ï8>Îûº>Îè>ÎÒœ>α¹>Εi>Îv’>ÎYõ>ÎJ”>Î>Íë„>ÍÞâ>Î>Î07>ÎM3>Îp&>΢¿>Îë$>ÏK>ÏJW>Ï&µ>Ï>Ïá>ÎäÅ>ÎÃö>ΧN>ÎG>Îqè>ÎBn>ÎB>Íò)>ÍáË>Î r>Î@5>ÎTß>Î{p>εï>Îöš>Ï.•>Ï*i>Ï,‰>Ï#ó>Ï>Îüq>ÎÍL>δ>Η>ÎvÌ>ÎE¿>Îm>Íòt>ÍãK>În>Î*È>ÎP>Î{ä>ζJ>ÎÞÿ>ϸ>Ï?>Ï%›>Ï-÷>Ï3>Ï >ÎÖN>ε¥>΋v>Îaí>Î8>Îw>Íï>Íä>Î <>Î#¸>ÎJÓ>Îså>Î¥ê>ÎÉ?>ÎæJ>Ïä>Ï>Ï07>ÏET>Îû'>ÎÈà>ζ3>Άæ>ÎVñ>Î,>Î §>Íé;>ÍåÔ>Î+µ>Î"¿>Î=">Îl">Î¥¶>εƒ>ÎÌ >Îá>Ïî>Ï7>Ïä>Îãñ>οÏ>΢Š>Î{ß>ÎNc>Î">Íÿx>Íá|>Íú$>Î'>Ά>Î-ˆ>ÎU'>΂¿>ΛÞ>βÇ>ÎÃÜ>Îéá>Ï&>ÎóT>ÎËj>ή>Î!>Îoþ>ÎEÛ>Î>Íó—>ÍÙ¼>Íëï>β>Îö>Î>Î?¨>Îc>·í>Ξm>Ψ.>κ~>Îêú>ÎçA>ζŸ>Î¥¨>΂Ù>Îlü>Î:n>Î õ>Íå>ÍÊ1>ÍÁ»>ÍÚ½>Íð<>Î >Î<©>ÎN>΀È>΀$>Î}t>΄±>Εà>Μ2>Ά>Σ>Îs—>ÎR‚>Î!>Íú>Í×…>Í»ó>ͧ€>ͼ6>ÍÝ“>΂>ÎŽ>Î.,>Î^>ÎW6>ÎL³>ÎQ?>Îa$>Îd>Î\q>ÎMh>Î3U>Ϋ>Î >Íëü>ÍÐ >ͱ¤>Í—†>ͪÇ>ÍÌq>Íçæ>Íø>΢>Îå>Î'É>Î#>Î)š>Î=>ÎC¥>ÎO>Î#<>Î ž>ÍüZ>Íè§>ÍÑí>ͺÕ>Í¢>ÍŽI>Í¡l>Í¿>Í׉>Íå×>Íò_>Îí>ÎS>Îï>Î ¦>Î>Î4>Îs>Íû¸>Íî¿>Íæ“>ÍÅð>͹+>ͯU>Í–ó>Í„>Í—y>Í¿À>ÍÑ~>ÍÏ¿>ÍÕ«>ÍÛÓ>Íä >Íè,>ÍïA>Íõ>Íî>Íå™>ÍØÎ>ÍÐÀ>ÍÄ>ͦ”>ͪž>Í¥‹>ÍŒ¹>Í„¿>Í”Ú>Í¢û>ͳq>ÍÃ÷>ÍÔQ>ÍÙ²>Íàù>ÍìE>Íñ >Íì~>Íã>ÍÜt>Íݶ>ÍÈW>Í›>ÍY>ͦì>Íž$>̓”>Í•Ú>Í­´>Í»i>ÍÎ(>ÍÜø>ÍëÌ>Íó†>θ>Î"º>Î+Š>ÎA>Íö)>Íñb>Íÿï>ÍïD>ÍÕý>ÍÈ,>Í»[>ͧÐ>Í‘æ>ͪq>Íçè>Íã>Îé>Î^>Î Œ>ÍûØ>Î#>ÎH*>ÎQ©>ÎEî>ÎFº>ÎZ_>Î?‰>Îj>ÎV>Íì;>ÍÓÖ>Í»Ý>ͤ7>Í·;>Íæ[>Íñ*>Î È>ÎÂ>Î?<>Î@¸>Î\ƒ>ÎsÂ>ÎwË>Ît>Ît4>΀L>ÎhU>ÎKa>Î? >ÎÀ>Íï>ÍÒ>͸€>ÍÃ>Íä'>Î>ÎÈ>ÎB >ÎDC>Înª>α >ιæ>έ³>Ψî>Σ>Ρ8>Αg>Î{§>Îf >Î1A>Îã>Íææ>ÍÎÞ>ÍÏÉ>Íõ‹>Î,>Î;O>ÎbZ>΀6>Εè>ÎÈY>Îâ>Îߎ>ÎàI>ÎÛJ>ÎÙ·>μò>΢‰>Î[>Î@‡>Î[>Íð>ÍæE>ÍÙ‰>Íþì>Î,×>ÎL3>Îz">ÎÌC>ÎÒd>ÎÛ_>Îè®>ÏM>Ï>Ï„>Ï>Îá>Ϊ >Îu1>ÎFö>Î +>Íøk>Íóh>Íåd>Î H>Î@>Î[Ô>Έ»>ÎÉb>Îïë>ÏG>ÏÕ>ÏB©>Ï`<>ÏK„>Ï6h>Îû¸>ÎÀc>ΈX>ÎS>Î,à>Î#>Íþ<>Íï6>ÎÈ>Î=>Îc>Î’ >ÎÇe>Îö*>Ï >Ï(/>Ïf«>ÏuÕ>Ïcï>ÏJ>Ï)Ï>ÎàS>΋C>Î]ð>ÎP >Î1ÿ>Î>ÍíÌ>Î>ÎIS>ÎeÛ>ΑÞ>οÏ>Ï>ÏE¯>Ïc¸>ÏŸú>Ï‹>Ïp«>Ï8í>Ï Ù>ÎÃÕ>Î’Ã>Δ>Î{E>Î@l>Îæ>Í÷Å>γ>Î/ê>ÎO?>Ζ>ÎÇ]>Ï+B>ÏT>Ïnó>Ï›Í>Ï”d>Ïxô>Ïl>Îúª>ÎÍ£>ΧU>Έ2>Î_·>Î.…>ÎÕ>Íò:>΢>ÎØ>ÎBT>Ζ>Îå°>ÏØ>Ï7§>Ïr¯>Ï‘Q>Ï~¢>ÏD‰>Ï'v>Ïz>ϸ>ÎĬ>΂ô>ÎP&>Î$)>Íú8>Íѽ>ÍÑ’>Îé>ÎI5>ΉJ>ÎÍî>Îò­>Ï>Ï2]>ÏHÁ>ÏB=>ÏJ>ÏÒ>ÎõË>ÎÙð>έ¢>Î}0>ÎDÞ>Î.>Íõà>ÍÀÀ>ÍÔ>ÎC>Î>ß>Îuì>ε~>ÎÓ]>Îòâ>Ï \>Ï"©>Ï?>Ï>Îû >ÎÙ»>ν<>Ξj>΃®>Î8&>Î S>Í÷í>ͱb>ÍÁh>Î >Î)>ÎV±>ΊÅ>ι'>Îèè>Îén>Îã>ÎÑ >ι>ÎÎ>ήæ>Ξ]>Ξ>Îa>Îf>Íô‘>Í×i>Ͳ”>ÍÍ’>Íò>Î>Î-’>Î`€>Έí>ί>ΧÇ>Ω¯>Χ\>ΞÉ>Τâ>ÎfA>Îtú>Ή>Î;->Î 5>Íâ˜>ÍÁ >ͦO>ÍÂ&>Íåü>ÍøÜ>Î+>Î8>ÎIr>ÎÄ>Îq9>Îv€>ΈQ>·J>Îu>ÎTB>ÎCU>Î5>Î>Îî>ÍàA>ͦM>͘,>ͳª>ÍÛ¬>ÍõÚ>Κ>ÎH>Ι>Î1Ç>Î;>ÎK•>ÎzY>Îo8>ÎDQ>Î-}>ÎÉ>Δ>Íõ9>ÍÑA>ͪd>ÍŽ->ÍŠ[>Í£É>ÍÑÁ>Í÷Ñ>Íø0>Íþ >Íë>Íæ >Î Ã>Î!±>Î2ô>Î-ô>Îk>Î>Íü¥>Îû>ÍÝ;>Í¢M>ÍB>͉J>ÍzÖ>ÍŒY>͘>Í·™>ÍÌ÷>ÍØÁ>ÍÙ%>Íß2>Íïj>Îe>Î!f>ÎÕ>Íî:>ÍàÎ>Í×Ñ>ÍÎo>ͲÂ>Í€>Íz!>Í}>Í€K>Í­>Í Ž>͵2>ÍÇ€>Íë´>ÍéÚ>Íè>ÍîÐ>ÍôÖ>Íø¡>Íï>ÍÕ³>Ͱ¨>; >Í«ß>Í¢†>Í¡!>Í–g>Í|S>Í!>ÍžY>ͱ¼>ÍÈ>ÍÛ>Íñ>Íÿö>Î5>Î>Î>Î3y>Î-+>Î>ÍóÀ>Íïd>ÍØð>ÍÊJ>ͽ>Í¥•>Í{>Í›^>ÍY>ͺ­>ÍéI>Íú›>Ζ>Î"{>Î:?>ÎJÇ>ÎR>ÎT|>ÎCð>Î9Ë>ÎN6>Î$¬>Î6>Íê¢>ÍØÇ>ͼ=>ÍœC>Í­>ͱ>ÍÙ™>Î>În>Î.&>Î>°>ÎqŽ>Ήu>ÎŽ¾>΋º>Îy>Îvq>ΉU>ÎSn>ÎÞ>Î+>Íìš>ÍÒK>Ͳg>Í¿Ð>ÍÚ¿>Íÿ$>Î%ì>Îlæ>ÎYp>ÎjÇ>΂Ì>Τç>ÎÓ¢>ÎÛp>ÎËô>Î̺>εX>Îl­>Î ì>Îø>γ>Íì>ÍÒÆ>ÍÍü>ÍòI>Î$X>ÎDÈ>Μ>Δ‡>αM>λ >ÎÜ">ÏH>Ï)*>Ï,>Îë…>ÎÊ4>Γ¯>ÎQ®>Î_Õ>Î&ˆ>Íö>Î Ç>ÍØÎ>ÍûŠ>Î$>ÎSî>Îb>ιÎ>Îø>Ïï>Ϥ>Ï9Ø>Ï[µ>Ï}ð>Ï2>Îù@>ο$>΄›>Îdr>Î;3>ÎA>Î @>Íî›>Íÿû>Î>Î^Ò>Î¥>ÎíY>Ïœ>Ïu˜>ÏY(>Ïd*>ÏxG>Ï—Ž>Ïg>Ï.ü>Îâ&>Τ>ÎpY>Î?¿>ÎU>Îh>Î1>ή>Î0s>Îrë>θ>Îöù>Ï,6>Ï_%>Ï„¹>Ï y>Ï·Ð>Ï»Û>φ÷>Ï#Á>Îä¸>ζé>Îù>ÎV>Î8ã>Î)³>Íê§>Ε>ÎÅ>Îy>ί>Ï›>ÏÏ„q>ϳJ>Ï´?>Ïã\>Ïá½>Ï®>Ï=Ç>ÎøT>ÎÃç>΄u>Î^X>ÎA°>Π>ÍÔÅ>Íúf>Î0A>Îx€>ÎÇ«>Ï Ð>ÏS>Ï’í>Ï»¯>ÏŃ>ÏØ€>Ϥ‚>Ïp~>Ï>Œ>Îÿˆ>ÎÓh>ήr>Îx|>Î<Õ>Î ¯>ÍÆŸ>Íêð>Î+=>În°>ÎÂë>ÏÑ>ÏL’>Ït¯>ωð>ϧ÷>ÏÞ¬>Ï¡]>Ïs¶>Ï:!>Îå>Îãx>Î¥1>Îl[>Î=î>Îb>ͽœ>ÍÚ¶>Î! >Î^l>Ι¢>ÎàM>Ï-ˆ>ÏRk>Ïo >Ï‚Ô>ϤŒ>Ïyª>ÏX]>Ï,Ÿ>Îøx>ÎÚè>Ϊ¾>Î^\>Î0‹>Íú>Í›V>ÍØ‹>Î'c>ÎS¦>Έƒ>ÎÐï>Ï+>Ï&ñ>Ï=Ò>Ï,þ>Ï!>Ï<¤>Ï.>ω>Îñ>έ>Îmv>Î9ã>Î>Íñ¿>͸>ÍÜ>Î \>Î>d>Îi¿>Κ¶>βp>ÎØT>Ïõ>Ï $>ÎñÔ>Ï¡>ÎúK>Îáì>ÎÆm>Î_û>Î=c>Î>Íô>ÍÔŒ>ͬû>ÍÕã>Îí>Îw>ÎT)>Îd(>ÎQ„>Î}P>ζ>ÎÎi>ÎÊ1>ÎÊ´>ÎÁ`>Κx>ÎnŽ>Î.h>Îw>ÍøÆ>ÍÕ¬>Í¿W>Íž˜>ÍÃ+>ÍýÑ>Îj>Î*Z>ÎDÂ>Î2d>ÎIù>ÎuŠ>Ίy>Îü>δI>Ι®>Îhá>ÎDc>Î>Íþ€>ÍÆÈ>Í¢E>ͦC>͉>Ͳ>Íú_>Î &>ÎD>ÎD>Î.>Î+>ÎD>ÎR7>ÎHî>΃ì>ÎSC>Î5ñ>Î>Í÷­>ÍÝM>ÍÁA>ͤ•>Í–W>Í/>Íšm>ÍË®>ÍÕ >Íòó>Îä>Íü >δ>Î/>Î3š>Î+U>Î8ý>Î']>ξ>Îm>Íç#>ÍÁ¦>Í­U>Í—o>͉Ê>ÍlI>Í|»>ÍŠ>Íœ>ÍÁ…>Í×>ÍÜÜ>Íç|>ÍöÙ>Î f>Íò">Íót>ÍëŒ>ÍßZ>ÍÙ­>ÍÒ~>Í¿S>Í æ>͇4>Ízë>Ísó>͉#>Í•¶>͹E>ͼù>Í®y>ÍÅJ>ÍÞµ>Íè¨>ÍñÉ>Íò»>ÍßÒ>ÍÏÑ>ÍÉ­>ÍËu>Í®ù>ÍœL>͸>Íaõ>Íe>ÍŒD>ͨÐ>Í«°>ͶÎ>ÍÉ­>ÍÙn>Íü‘>Î<>Î>μ>ÎÛ>ÍüÎ>ÍñT>Íî|>Íò½>Íצ>ÍÒ‹>ÍÊš>Í™X>Í‚>Í P>ͳ>Í»>Í®ö>ÍÜ!>Íñ³>ÎE>ÎM >ÎaS>ÎeO>Î]>Î=:>ÎÎ>ÍþU>Î'>Íü>Íï>ÍïÃ>ÍÀì>Í–>Í­c>;>ÍÙ`>ÍãX>Îú>Î r>Î9>Îz>΢ç>ΤW>Κ¼>ÎP>Î`d>Î[¼>ÎNH>Î2>Íì/>Íí>ÍÔ >Í©–>ͽe>ÍÛ.>Íûa>ÎË>Î+J>ÎQj>Îx‚>ΟŸ>ÎÒJ>ÎÖà>ÎÔL>Îê>ÎË.>Μò>Î]Ý>Î>Íþ‡>Íù´>ÎÅ>ÍÅÿ>ÍÇ >ÍèL>Íññ>Î1_>ÎJÍ>·f>Îã>Îë€>ÏÚ>ÏÙ>Ï“>Ï)5>Ï.½>ÎäW>Î…¡>ÎL›>Íü >Í÷'>Íõe>ÍÙ>ÍÍ^>Íô°>Î>ÎS›>ÎŽ>μá>ÎÜv>Ï)1>ÏnŠ>ϸ>ÏuÇ>Ï^T>ÏQÍ>Ï$Ð>ÎÑê>΋>ÎCf>ΰ>Íðª>͸F>ÍÉ%>Íý>Î->Îs.>ÎÃÉ>Ïx>Ï1c>Ï@U>ϳ.>Ïç>ÏÜÂ>ϱè>Ïxœ>ÏU,>Ï€>ÎÄL>Î…Ô>Î' >Íõ>ÍÞ>;m>Îv>ÎGN>Τ>Îê÷>Ï,ê>Ï^$>Ï œ>Ð ú>Ð+c>Ð:Í>Ð >Ϭ >ÏtÇ>Ï ë>Î÷ò>ΰ+>ÎFi>Î>Íþä>ÍÚ8>Î):>ÎZD>ε4>ÏÐ>ÏEH>Ïj¢>ϵ®>ÐW—>ÐIÓ>Ð^”>ÐD¡>ÏÖi>Ï’Z>Ï9É>Ïè>Τ`>Î>>Î1>λ>ÍÑy>ÎÉ>ÎLï>Χ³>ÏÛ>Ï;Ì>Ï_¦>Ͼ>Ð`á>Ða…>Ð@>Ð64>Ïä¼>Ï•>Ï:™>Îê&>λc>Îm>Î4z>Î !>ÍÈ >Íü‡>Î>×>Αü>ÎåÃ>ÎþÐ>ÏV>Ͼ>к>Ð7Š>ÏüŒ>О>Ïãï>Ï‹¯>Ï|>ιT>ΙE>ÎeV>Î4g>Íþ¹>ÍÃf>Í÷Ï>Î0d>Îmô>ίË>Îö>Ïkg>Ï ¯>Ïϸ>ÏÜW>ÏÊú>ϺF>Ï«”>Ïyv>Ï#ú>Ϋc>Îb+>ÎKÎ>Î(Ó>Íé>ͳt>ÍåQ>ÎÖ>ÎR¨>΃H>ι³>Ï+W>ÏSÁ>Ïz÷>ÏQ>Ïx>Ïg‚>ÏYÏ>Ï?f>ÎÝ>Ά¯>Î?!>Î..>Î ©>ͺ>Ͳ–>Íß·>Α>Î6Ã>Î` >Έ*>ÎÊø>Îç®>Ï>>ÏQÒ>ÏSC>Ï*Ç>ÏÉ>ϱ>ΰˆ>Îd%>Î8N>ξ>ÍîÄ>ÍÅ€>Í¡Î>ÍÏ>Íõn>Î ü>Î7>ÎS>ÎzÝ>Λå>ÎÊq>Ï >Îýw>ÎåŸ>μÄ>Χ >΂›>Î^ó>Î&s>Íø>ÍÓy>ÍÁ6>ÍŠ>Í­ó>ͽê>ÍàÍ>Î1>Î8O>ÎKã>ÎP~>Ά>Οˆ>ΚØ>Îj>ÎŽN>În3>ÎI©>Î(Ì>Îü>ÍÒ§>Ͱ¢>Ͱ>Í>Í”ò>Í™$>ÍÊ>Íí>Î_>Îá>Î1é>ÎPê>ÎTò>ÎAŒ>ÎB,>Î>N>Î1Ç>ο>Íþ¿>Íë©>ÍÅÿ>Í¥š>Í“«>Ín]>Í‚9>Ížw>Í£¶>ͯó>ÍÅÌ>Íös>Î_>ÎI>Î&>Î Ô>Î >Î)?>Î ­>Íëš>ÍÐk>ÍÔÂ>Í·C>͘Š>͆f>Í]>ÍmÎ>͈E>ÍQ>ͪ>ÍÈ>ÍÙó>Íì²>Íú‰>Íçë>ÍÏÂ>ÍÛ}>Íàõ>ÍÖ+>ÍÈ$>ͼ>ÍÎE>ͱ†>Í‹¯>Í{½>ÍbE>Í‚>Íy‰>ÍoÈ>Í >ÍP>Í®Ñ>ÍËí>ÍÝ­>Íø1>κ>ÍäC>ÍÃÊ>Í£>ÍÏ>Í™&>Í‹”>ÍrE>Í]J>ÍVS>ÍŠ®>ÍÎ*>Í¢­>ÍŸ>Ͱm>ÍÂp>ÍÕ>Íë¬>Î>Î3#>ÎJÊ>Î B>ÍèÁ>ÍÈ>͹Œ>Í¿>Ͱ9>Í™²>Í}Š>Í{Ä>ͳ°>Íèð>Í¿ >Í¿C>ÍÚæ>Íêé>ÎX>Î$h>ÎW¥>Ήý>΄>ÎEï>Î.>Í÷$>Íû>Íý>ÍÙW>ͽv>Í•ã>ÍÁ>Ͱ>Í͈>Íàä>Íé…>Îà>ÎØ>Î>Ã>Îy+>Αs>ήN>έ->΄½>ÎN¦>Î9 >Î…>Î`>Íêù>ÍÇ‘>Í¥Ì>Í‹s>ÍÃ>Íãð>Î(K>Îù>Îi>Î?Ý>Îx¢>ά>Îõ—>σ>Îø>ÎÔ{>Σd>μ¤>În¡>Î0q>Îj>ÍÕþ>Í®6>Í™M>ÍÄT>Î;>Î0­>ÎK;>ÎP“>Άê>αX>Îч>Ï1W>Ï^'>Ï]M>Ï4©>Ïœ>Ïã>ε>Î_…>Ή>Íã8>ÍÜ“>Íœ…>ÍÈz>Íù¡>Î&h>ÎZì>Δî>ÎæÐ>Ï$'>Ï0 >Ïy#>ÏÛÑ>ÏÈÂ>Ïy¯>Ï€”>ÏXÈ>Îï>Ζ9>ÎA­>Íê,>ÍÝ>ͺú>ÍÂe>ÍôS>Î8->Îv>ÎÌ>Ï£>Ïr >Ï”ö>Ïó‡>ÐD;>Ð1â>Ïõê>ÏÝS>Ïè>Ï>ÎÃj>Ο>Î*>Í÷…>ÍÌþ>ͺÒ>ÍàL>Î35>ÎŒE>Ïû>Ï^w>Ϻ>Ð?>Ðb­>Ч”>Рd>ÐNn>Ð!f>Ïà>>ÏOÓ>ÎÚÿ>΋D>Î?¨>ÎÊ>ÍÑy>ÍÌ>Íè`>Î7G>΋B>ÎÿÄ>ÏŸÇ>Ïàj>ÐD>>Ð’g>ÐØu>ÐÒJ>Ðhs>ÐCX>Ð>Ïsa>Îì>Î=>Î9 >΃>Íè–>ÍÎw>Îá>ÎH…>ÎŒQ>ÎÜÐ>ψ^>Ïö>ÐQ1>Цƒ>ÐÀx>ÐôÞ>г[>ÐRL>Ðï>Ï•¦>Ïï>Îmª>Î/>ÎÈ>Îj>ÍË^>Îà>ÎA+>Îr2>ÎÁþ>Ïb>Ϲd>Ð=O>З7>Ð{ý>Њô>Ðaó>Ð>ÏÆ>Ïc>ÎþÙ>΄µ>Î<í>Î$>Íõã>ÍÄ>Î.>Î9>Îl&>ζ5>Ïò>Ïe>ÏíT>Ð{>Ð/9>Ð>Ð ³>Ï÷ >Ï›Ý>ÏÚ>ÎËb>ÎtÞ>Î>ñ>Î%þ>Íå>ͤÎ>ÍÚÇ>Îi>ÎOŒ>ΚM>Îëê>Ï!Û>Ïf¶>Ï«>Ïãk>ÏÕ>Ï¡Ê>Ï‘º>ÏRZ>ÎØ>ΜÀ>Îi0>Î7 >ÎÚ>ÍêÒ>ÍzQ>Í¥ï>ÍÎ]>Î,ž>ÎÑ>α>>Îåo>ÏÓ>ÏG\>ÏWO>Ïeå>ÏFY>Ï‚>ν>Î}S>Î~S>Î[ß>Îs>Íê“>Í»j>͇i>Í›„>Í»y>Î>Î_›>Îz£>Εñ>η>Îä >ÎÞî>Ï>Ïä>ÎÁà>΋)>ÎY@>ÎX>Î..>Íö¢>ÍÇð>ͤ">Íí>Í™s>Í¥#>ÍÊo>Î >Î->ÎHL>Îrº>ί(>Φ€>ά3>Ωè>Îie>Îq9>ÎB©>Î#s>δ>ÍÕK>ͬE>Í‘’>Ín/>ÍŸ>Í{ˆ>̓¿>͵->Íí@>ÎD>ÎEœ>Ît»>Îc1>ÎHU>Î >ÎX>Î$&>În>Îô>ÎE>ÍÈ•>Í›:>Í€S>Í[«>ÍdR>ÍGc>ÍbÉ>Í“F>ÍÄ,>Íÿ>Î?G>Î)>Î ¤>Íþ&>Íè·>ͽä>ÍÞe>ÍÝù>ÍÙÙ>Íï©>Í©‡>ÍŒÐ>Í€>ÍLß>ÍUñ>ÍL\>Íbæ>Í€>Í õ>ÍÕ¡>Íög>ÎË>ÍÔŽ>ÍÊA>ÍÊ3>ÍÀž>ÍÀË>͹5>ͪ>ÍX>Ítk>Í‚z>ÍŽÍ>Í:Î>Í8¿>ÍQu>ÍYx>Ío3>ÍŠö>ͧ >ÍÄZ>ÍÅé>ÍÃ(>ÍÒ¹>ÍÄ>Í®¤>ÍœN>Í‘C>͈>Í|°>ÍW>ÍFE>Í>H>ÍSl>ÍC“>ÍiÕ>Í„t>Í¡Ö>Í·‹>ÍȲ>ÍÛî>Íð¶>Íñ)>ÍüU>Íï°>ÍØs>ÍÔ>;>ͦ†>Íu¡>ÍAö>ÍPß>Ídõ>ÍBŸ>ÍCA>ÍØ>Í­¬>ÍçÊ>ÍíC>Íö“>Î>Î# >Î Ê>ÎC >Î0í>Î >Íëå>ÍêÞ>ÍàA>ÍšŸ>ÍOÜ>ÍV >ÍY¶>Í^ê>ÍxÆ>Íšß>ÍÔò>Î-ñ>Î-{>Î-p>Î'O>ÎSx>Î{ç>ηÞ>΄R>ÎHØ>ÎJ>Î •>Î>ÍÛ:>Í’`>Íp¢>ÍcÙ>Ít4>Í”“>Í—J>Íò >ÎI8>ÎV>Îx>ΓŸ>ÎÀ›>Îï¸>Ïž>ÎÐë>Α>Î^d>ÎU>Î)M>Íû >Í¿ƒ>Í{(>Íj?>Íœ×>ÍÉ”>ÍÐA>ÎŒ>Î\4>Δ´>Îíþ>Ï»>Ï4,>Ï\|>Ï|Þ>ÏJ>ÎØB>Îáò>β>Î]Å>Î>Íßi>Í™w>Ílv>;ï>Íé¡>Î9>Î7>Îyd>ε2>Ï!½>Ï|>ÏÓÏ>С>Ðø>Ïñû>ÏcG>Ï!M>Îüž>Ψ®>Î:£>Íó>ÍÇr>Í”\>ͽV>Íê>Î4>Î]Ù>Î>Ï¡>ψ >ÏñG>Ðqæ>ÐÕ”>еð>ÐhÐ>ÏÞ.>Ïž>Ï<;>ÎÉá>Î:[>ÎF>ÍïÕ>ÍÃB>ÍÁÀ>ÍëÛ>Îè>Îd!>νf>Ï\>ÏùZ>ÐW/>ÐÛ£>Ñe7>Ñ2‚>ÐÕ>ÐY!>ÏÑö>Ï]7>Îñ>Îg >Î>Íâ+>ͬô>ÍÐ>ÍìŠ>ÎÏ>Îc­>ÎãÈ>Ïx¤>К>Ж¬>Ñã>ѹG>ÑÃ]>Ñ;l>кÉ>ÏßÕ>Ïs‹>Ïç>Η?>Î,¿>ÍÛ÷>Íœ¯>ÍË£>Î×>Î64>Îxv>ÎÎû>ÏZö>Ѐ>Я6>Ñ!>Ñ”>Ѥ2>Ñ_*>Ðçb>Ð7>σŒ>Ï"ã>Έ>Î=Í>Ω>Í“è>ÍÂ>>Íð˜>Î&>ÎjN>οÝ>ÏB>Ïݦ>ÐiË>Ðé‹>Ñ?ý>Ñ= >ÑÁ>бv>ÐP>Ïem>Îóã>Ή†>ÎGÈ>ÎQ>ͱ>͵â>ÍÃ2>Î=>ÎjY>ÎÀ’>ÏF>Ïko>Ð#Z>Ðe?>Пx>Шô>Р‘>ÐM->Ïã>Ï5û>Îêæ>΋…>Î+9>ÍËá>ͪs>ͧC>ÍÔ>Îõ>ÎmT>ΟÂ>Ω>ÎöÍ>Ï Ù>Ï×—>Ðø>ÐK>Ðý>ÏÚ„>Ï8Ý>Îå>Φ>ΉŒ>Î+§>ÍÅh>ÍË>ÍŒÑ>Í·Ñ>ÍëÛ>ÎHž>Îq7>ÎL>Ϊü>Ï¥>Ït3>ÏŒ>Ï} >ÏŸL>Ï[>Îè6>Η^>ÎXØ>Î_»>Î!…>Íɼ>ÍÜ>ÍÑ>ͬk>ÍÈU>Îò>Î?f>ÎG>ÎvÙ>ηh>ÎúI>ÏÉ>Îæó>Î×°>ÎÑŠ>Ϊü>Îk×>Íð¿>ÎÇ>Íæ>ͬd>Íoï>ÍvÅ>Í­ú>Í®>ÍǺ>Íìâ>Íùˆ>Î.¾>Îd„>Ο(>ΪÈ>Î÷>ÎY€>ÎW?>Î^¹>Î;’>Íÿ{>Íߊ>Í¿3>ÍP>ÍW>Í`©>Í~´>ÍžÝ>ͦ¬>Í»¤>Íã9>ÎŒ>Î&ƒ>ÎPø>β>ÎK>Îä>ÎÖ>ί>λ>ÍÚ>Ͳ>Ͱ>Í,>ÍX”>ÍL³>Í^?>Ír­>Í~M>Í’Š>Í·7>Íç)>ÎÏ>ÍÿØ>Ît>Íø9>Íè>ÍÜ‹>ÍæŸ>ÍãŠ>Í­T>̓ >Í~Y>Í_>Í?ê>Í=ÿ>ÍJ®>ÍRK>Í^É>Ín>Í‹P>ÍÔ=>ÍÙb>Í£%>Í·>;m>;>͸>͵Ì>ͳª>Í‘K>͇ò>Íca>Í<€>Ío>Íy>Í"Æ>Í:x>ÍLâ>Ía>Í}Æ>Í¢‚>ÍÞ>Ͳ8>Í•‘>Í…·>Í>ÍŒ>Í„ì>Í€>>ÍiÜ>Í^ê>Í?>Í)z>Í?>Í>ÍQ>ÍI@>Íj–>͉v>ͦá>ÍÅ>Íè >ÍÎ=>ͼ;>͵Ë>ͽa>ͺ,>Í®ä>ͼu>Í„~>Ík@>ÍDÆ>Í.R>Ìü˜>Í1>ÍDj>Íj5>Í}>ÍÍr>ÍÚã>Ííç>Î >Íæ >Íë¯>ΰ>Íýá>Íð2>Íϳ>ͧ•>Í€¤>Í”#>Í_3>ÍCN>Í+ï>ÍG>ÍkC>Í„Â>ͨ•>ÍÈ->Î v>Î%é>Î; >ÎF1>ÎNƒ>ÎKl>Î\8>Î8:>Íý^>Í×Ö>Í·J>;Œ>͉S>Í`¦>ÍD´>ÍL¸>ÍoE>Í•s>ÍÐ>ÍôÒ>Î3Í>Î]û>Μa>ÎÑX>ÎÉé>ÎÍ_>ÎÇå>ΈÒ>Î%ç>Î >Íôù>Íì¤>ͼÉ>Í„9>ÍT@>ÍUå>Íaª>Í¿>ÍêK>Î;>Îs–>Κ˜>Ï—>Ïw÷>Ï_Ï>Ï\Ú>ÏD:>Îï®>ΊÁ>Î`»>Î/[>Î ’>ÍæÎ>Í»ð>ÍXÆ>Í\>ÍyÖ>ͺ€>Îj>ÎsÉ>γh>Îû¬>ÏÍj>ÐB„>Ð %>ÐÓ>ÏÇ>ÏsG>Ï<>ÎùM>Î…ú>Î")>Íé¼>ÍÄT>Íe->ÍÍ>ͶS>ÍÎn>λ>Î’=>Ï |>Ïk²>Ð;?>Ðñ>Ѿ>Ðù->ÐŽ>ÐAß>ÏsI>ÏQ[>ÎÅÈ>Î;®>Íï>Í«ø>Íht>ͦº>Íî3>ÍùÑ>Î<>Î¥Ù>Ï8>ÐÛ>Д3>Ñ¡r>Ѩ >Ñìj>Ñf>Ñ>ÏÎò>ÏYw>ÎÖÔ>ÎQx>Íûc>ͤ >Íl>Í­û>ÍÜ>ÍøN>Î<Â>Ψ>Ï<ç>Ð e>Ñ0>Ñî^>Òa¿>Ò½à>Ñò¨>Ñt>Ð.æ>Ï…>Îåi>Î`S>΂>͵¹>Í€q>Í®v>ÍÔì>Íõ%>Î3>α«>ÏE>Ð>Ðô÷>Òù>Ò•4>ÒÞ¿>Ò¬>Ñ=®>ÐÏvÄ>ÏÄ>Îk>ÍôÈ>Í>Íw(>͸>Íß>ÎT>ÎCç>Ω>Ï`©>Ѐ>Јñ>Ѳ=>Ññ->Ò+A>Ñ¥y>ÐáC>Ð9G>Ïvs>Îî>ÎkÇ>Íú<>ͯ4>͆w>;Ã>ÍØB>Îu>Î2´>Î…£>Ïd>Ï”>ÏË“>Ц>ÐþÜ>Ñ8H>Ñ^Y>Яž>Ï÷C>ÏJY>Îà’>ÎF}>Íë >ͬ¨>ÍŠû>Í«Æ>ÍÞá>Íã×>ν>Îaº>ή6>Ï>ÏF1>Ï»>Ïü5>ÐnJ>Ђd>Ïçƒ>ψª>Îæþ>Îzµ>Î}>Íá…>Í­œ>ÍwE>Í€ð>Í®[>ÍÌr>Íù6>Îz>Îh“>έç>Îü±>Ï›>ÏFL>Ϙ}>Ï‘>Ïm&>Îæ>΂>Î$®>ÍС>ÍÓ>Í©õ>Ír;>ÍiÍ>͉#>ͬµ>ÍÎŒ>ÍÕ¿>ÎH>·C>Ψ,>Ϊ±>ÎóR>Îô>Îܤ>ÎÉ>Î`³>Î%»>Íöß>ÍД>ÍÉA>ÍŒ²>Í\ >ÍYü>Í·>Í‹á>Í£Þ>ÍÇÊ>Î u>Î@D>ÎFi>Î%ž>Î\t>΋>ÎdO>ÎDÇ>Íí>ÍôÔ>ÍéÅ>ÍÇd>Í ·>Íp„>ÍH¡>ÍI{>ÍhÆ>Í\6>Ítú>ͧ!>Íú>Íöj>ÍéÙ>ÍÉ->ÍÌE>Î.o>Î-u>Îä>ÍÔ>ÍÃÖ>Í›º>Íw>>Í…È>Í[à>Í;…>Í8Î>ÍJ>ÍJ?>ÍZ¬>Í{U>Íž©>Í›*>Í•Â>ÍÀ¾>Í¿£>ÍÚ>Íá±>ÍÚl>Í»>ÍÀ¦>Íq%>Í:ó>ÍPÍ>Í?Q>Í)î>Í-.>Í=)>ÍI>ÍL»>ÍX“>Íf×>Í`×>Íq$>Í‹þ>Í–>ͦº>Ͳó>Í©Š>Í›ì>͸Ì>Ík >Í1Ÿ>Í3Ÿ>Í(‹>Íš>Í|>ÍÝ>Í(h>Í79>ÍCä>Íen>Ít4>Í7²>Í[Ê>Íq&>ÍRø>Í`ý>Íh@>Í^ë>ÍkÂ>Í5™>Í ñ>Í=>Í D>Ìÿý>Í!¢>Í1k>Í>>ÍL>ÍXU>ÍŠŽ>Íž±>Í…¾>Íœ^>Í—>Íg*>ÍŽ£>Í›>Ítè>Í;Æ>ÍBä>Í/b>Í$>ÍF>ÍB>ÍJ¯>Ímr>ÍUã>ÍY>ÍD>͉h>Ͷü>Íée>ÍÚ&>ÍÓÙ>ͦ×>ÍÈ>ÍÕ©>ͦ)>Íu£>ÍZ¨>ÍIˆ>Í=E>Í'c>Í6>Í9ý>ÍbË>Íf>Íw“>Íyo>ͪš>Íå?>Î(È>Î(b>Î >Îô>ÎN>ÎX>Íãì>͵¾>Ímã>Ívƒ>Í`9>Í@L>Í't>Í8W>Í^n>Ílv>Í‚3>Í¢}>ÍãÜ>Î%¨>Î@Ÿ>ÎqQ>Î?>Έ6>Ί>ÎZ»>Î.Ÿ>ÎE>ͱË>ÍŽ±>Íi.>Íb2>Í>&>Í@³>Í„&>Í…:>Í >ÍÅ}>Î ¦>Î]†>Î…:>ÎÛ‚>Ï2ø>Ï.>Ï#¶>ÎÚc>Îh&>Îï>ÍÛE>Í D>Í‚A>Íto>ÍH8>Í@Ú>Íei>Í•–>ÍÙç>Î<,>ÎHu>ί>ÎÙê>Ï#µ>Ð)>Ц×>Йó>ϳU>ÏÔ>Ή>Î">ÍÚ>Ͳ‘>Í…´>ÍOE>Í8V>Í[ž>ͪG>Íþö>Îe >΀ä>Ï24>ÏÈ«>Ð )>Ñp4>ÑÎ;>Ñp>ÐT“>σ«>ÎïD>Î< >Χ>ÍÍx>Í‹M>Íg >Í6ª>ÍCF>Í·ß>ÎL>Îv«>θÍ>Ï¥Ž>С>Ñve>Ótö>Ócˆ>Ò4 >з(>Ïá™>Ï>Îqø>Î ¦>Íäþ>ÍŸ´>Í€œ>Í7>ÍbQ>ÍÂä>Î%c>ÎÓ>Ï­>Ð`Ö>Ñ$q>Ò©¹>Ôªw>Óí3>Óm>Ñùµ>ÐŽ±>Ï;ê>Έç>ÎCO>Íð>ͤ!>Ím)>ÍW´>Írä>ÍÝ6>ÎD>Îqä>Îù®>Ïà´>ÑTÌ>Ó<¡>Ô —>ÔRg>Ó›à>Ñ´à>ÐUo>ÏMz>΃Á>Î Ã>ÍÑ?>Í‘<>ÍfA>Ín•>ÍqÒ>Í»Õ>Î (>Î]*>ÎÆ§>Ï™­>ÑMo>ÒT->Ó5ô>Ó¡`>Òæ½>Ñ Q>ϰ§>ÏO7>Îi>Íù†>ÍÆŽ>Í¥Å>Íq¨>Í?ñ>Íc>Í£¸>ÍúÇ>ÎOT>Î>ÏX€>Ðfš>Ñ6ë>Ñâê>Ѳ>Ðê:>Ð;>Ï7¬>ÎÓ‘>΢>ÍÞÌ>ͳ]>ͱŽ>Í‚=>Í.7>Í6b>Íw)>Í©Î>Î T>Îfô>Î÷à>Ïy>й>Ð…>ÐŒX>Ð9ê>ω,>ÎÔ’>΋T>ÎÆ>ÍÁ´>͇«>Í‘(>ÍqŽ>Í:6>ÍJ>Í€I>͈l>ÍÇô>Î3˜>ÎŽ°>ÎÔR>ÏD0>Ïr$>ÏM>Îø9>ÎÍ/>΂M>ÎCÁ>Íûz>͹H>Íhl>ÍT<>ÍE!>Í:Ó>ÍKø>Ís¤>Í>Í–Î>Íë>Î?û>Î’É>Ή»>Î}>ΰ>Ît>ÎL>Π>Ή>ÍÆK>Ͳ>Íu>ÍM¬>Í3/>Í.H>Í/Š>ÍU >ÍuÑ>Í›º>ÍÆú>ÍÜ}>Î,`>Î=>Î!S>Î-ü>Î:>ÎÞ>ÍÍj>Í©~>͈K>Í´Ï>Ívq>ÍB’>Í%Þ>Í'i>Í5 >Í>õ>ÍW>Ͳ>Í à>ͦò>Ͷ»>Í»>ͼ>ÍÏo>ÍÏÄ>ͳƒ>Í—Œ>Íw¡>ÍhÅ>Í~F>Í?q>Í+›>Í>Í ø>Í1£>ÍE->ÍCæ>ÍY¸>Íz}>Í{l>ÍSO>Íf3>ÍŽ%>ͬ&>ͼû>͹ø>Íså>Í>j>Í>>Í:¹>Í+B>Í¢>Í™>Íq>Í-¿>ÍIV>Í5´>Í:ì>ÍBQ>Í,‹>Í'Ï>ÍCk>Ín=>ÍŠŸ>Í¢|>͆ >ÍTå>Í Ä>ÍÃ>ÍP>Íò>Í ä>Í ˆ>Ìà>ÌíR>Í P>Íx>Í>ÍÞ>Ì÷~>Í¡>Í$†>ÍÍ2”>Í*›>Í!×>Í>Ìë>ÌýR>Ìõ‹>Ìú[>Ìñz>Ìè³>ÌèË>Ìô0>Í<>Í$Í>Í%¢>Í) >ÍÓ>Í ã>ÍL}>ÍV}>Í?ï>ÍBý>ÍGÄ>Í;@>ÍÍ*%>Íõ>͵>Ì÷¥>Ìí›>Ìãí>ÌØ>Í ’>Í+ç>ÍK2>ÍS >ÍF;>Í3Ô>Í}{>Í™­>ÍpŠ>ÍwÇ>Ín>Íin>Ípª>ÍN.>Í}>Íc>ÌÿŸ>ÌõÒ>Í»>Ìük>ÍÑ>ÍX/>Íb_>ÍQh>Ígc>Íj>ÍÇ„>ÍÖ)>ÍÉ©>ÍÆ]>Í®ä>Í¢”>Íza>ÍDÐ>Í>Í>Í !>Í>ͪ>Í%3>Í@V>Í[E>ÍsÐ>̓õ>Í«Æ>ÍÙ_>ε>Î(è>ÎF=>Î(±>Î>Íø]>Í¥ð>Íbå>Íè>Í´>Í21>Íw>Í Š>Ìû{>Í/>Í\>Í>Íü6>Î9>ÎL0>ÎŽà>ÎÛ>οê>Ά5>Î^>Îã>ÍÄj>͈T>ÍS>Í+ >ÍÇ>ͱ>ÍÌ>Í+Í>ÍVg>͉J>Íϱ>Î:Ð>Îq>Îê>>Ï`>Ïö>Ïj >ÏÓ>Îó&>Îʃ>ÍãÒ>ͪ.>Í€¿>ÍZõ>ÍÙ>Í>>Í•>Í?:>ÍŽ¡>ÍÁ|>Íî>ÎEå>Îûm>Ïv>Ð,F>ÑÝ>ÑF>ÐŽ€>Ï[º>Ïi>Î6„>ÍÛ7>ͨ^>̓^>Í=ø>Í!F>Í‹>Í>>ͦ<>ÍÂè>Íð®>Îs>Ïd">Љ¡>Ò Ç>Óâ±>Ô$Ô>Ò«L>Іú>Ïl)>Î’>Î…>Í¿>͉]>ÍO?>ÍBÅ>Íy>Í<ƒ>̓¨>ÍÉI>Î4ý>Ξô>Ïäð>уr>Óü>Ø>ׇK>Ô±>Ñwã>ÏØS>ÎÖÒ>Î$2>ͦ™>ÍxW>ÍMy>Í7³>Í >ÍN—>Í{>ÍÒý>Î6‘>ΤÖ>ÏÆ>Ñ8Å>Ó€v>×¹÷>×Ða>Ô‚Z>ÐÖL>Ïðº>ÎÙÖ>ÎÓ>ͼ:>Í¢>ÍXg>Í;û>ÍM>ÍZm>Í‚z>ÍÑT>Î Q>Î_>Ïh$>Ð@×>Ò9ä>ÔÕÒ>Ô‡ >Ò¯ >ÐÐ>Ï…º>Γ®>Íý—>ͶÞ>Íž>ÍSP>ÍG|>ÍÂ>Í=ô>Í„’>ÍIJ>ÍÙ…>Î%†>ÎÀ„>Ð!’>ÐÌ->ÑØI>ÑôÚ>ÐåV>ϸ÷>ÏFÈ>ÎW >ÍÖ$>ÍÃ>Ío…>Í%u>Í4>Íe>Í#“>Íjs>Í—#>Í¡¼>Íþk>ε%>Ï#>ÏÏ¢>Ïí>Ï—>Ï÷>ÎÏ•>Îun>Î"X>Íí€>Í~`>ÍH->Í Ê>Ìþ±>Í Ü>Í.2>Í_E>Ír„>͇d>ͱè>Î"µ>ÎC€>Κa>ÎÑÄ>ÎÏ>Îoô>Î5ê>ÍýÅ>ÍàH>Í­0>ÍDh>Í">Í9>ÍG>Í>ÍNÃ>Í2F>Í2s>ÍQ2>Í^>ͺI>͸ò>Î ñ>Î1>ξ>Íþ>Îû>Íʶ>Í–*>Ík’>Í:>Í é>Í >ÍR>Í V>Í7>Í"Ò>ÍAò>Íl´>ÍNP>Í‹\>͘¦>Íë>ÍãÄ>ÍÆr>Í|>ÍÂS>Í• >Í_Š>Í4Ù>Í >Í$ñ>Íü>ÌøÈ>ÍÚ>Ìý‰>ͱ>Í.D>ÍF>ÍI>ÍVH>Ívì>Í>Í”À>Í–L>ÍvO>Ít@>Íh>Í@Ó>Í&@>Í>ÌõÍ>ÌøV>Ìó·>ÍÜ>Í»>Ìô«>Í“>Í'>Í51>ÍE2>ÍXç>Í_>Í[ë>ÍS€>Í45>Í#>Í5ù>ÍR>Í >Ìû&>Ìò>Ìõ´>ͳ>Ìþ>Í &>Ìà¾>Íá>ÍH>ÍÃ>Í®>Í[>Í,R>ÍE2>ÍBÒ>Í @>Í!N>ÍÎ>Ìû >Ìü€>ÌñP>Ìí³>Ìï>Í ˆ>̾‡>ÌÂŒ>̺ã>ÌØ>ÌÚó>ÌÎÓ>Ìè*>Ìå[>Ìî_>Ìôï>ÌÖ˜>Ìûc>Ìé>ÌÛV>ÌÇÝ>ÌÏ£>ÌÔŽ>Ìû>ÌÛm>Ìη>̵æ>ÌÁ >̾È>ÌÒ'>ÌÚ|>ÌÐ^>Í>Ìøê>ÌìÐ>Ìð‰>Ìñ…>ÌùÚ>ÍÄ>Ìöí>ÌØ$>Ì׸>ÌÕh>Ìã>ÌØí>ÌÑn>ÌÄ->ÌÎ}>ÌÔÊ>ÌÜâ>ÌÑ$>Ìâ >Í>ÍÂ>Í Þ>Ìí‡>Í•>Í >Í1>Í0>ÌóY>ÌÌ$>Ìǯ>ÌÌ6>ÌÒ«>ÌÐ->Ìëœ>Ìî >Ìæg>ÌÒ¦>ÌÖ¹>Í„>ÍB>Í&ï>Í">Í@¢>ÍF9>Í">Í(„>Í8Š>Í&S>Ìúà>ÌÉc>ÌÃ#>ÌÈ>ÌÈÙ>ÌåÉ>Ìú‹>Ìúq>Ìï1>Ìõo>ͤ>ÍS>ÍF0>Í)¹>Íc>Ía>ÍnB>ÍS>Í$>Í>›>ÍÐ>Ìð&>Ìàä>̦É>̰>ÌìE>Í ª>Í ½>Í A>Í œ>Í!°>ÍbÀ>Íuë>ÍÀÁ>Íø”>Íñ–>Íî—>Í”í>͆4>Íc>Í1^>Í z>Í æ>ÌÆQ>̲ð>Ìü÷>Ìüo>ÍÆ>Í î>Ìâ†>Í-e>Íúª>Íâ>Î:>Î@‰>Î`>Ρ~>ÎÂ>ÍXÐ>Ím„>ÍF>Í{>Ìý >ÌìÖ>ÌÌ©>Ìý™>ÌöÖ>Ìöî>͸>Í-'>Í|‚>ÍÚµ>Î…x>Ïo€>Ð à>Ð*A>ÏÙ5>Οt>Îj>Í™á>Í`">Í#q>Í'>Ìîµ>ÌÂ>>Ìæd>Ìð>Í é>Í;">Íg;>ÍÐ >Î/B>ÏCæ>ÑXw>Ô‰>ÓâÂ>ÑB®>Ï‚?>ÎÏP>Î8>ÍjQ>Í<¡>Í-–>ÌîD>̯M>ÌÖÅ>Ìê¤>ͨ>ÍH>ÍRÉ>ÍÛ>ÎB1>Ï´>ÔS°>Ú²h>Üzf>Ô}ð>Ð<÷>Ï->Î[>Ígû>Í/q>Í4">Í >Ìþ:>̶û>Ì̇>ÌòP>Ís?>ÍoÏ>ÍÑš>ÎFé>Ïɾ>Óÿ>ÜR>ÜU/>Óþñ>Т$>ÏA{>Íû©>Ín!>Í@>Í*õ>Í)>Í >Ì´Ô>̽Ï>Ìï«>ÍH¿>Í…¥>Í­>Î^>ÏE]>ѯ >Õ õ>ÓÛ}>ÐM.>ϺS>έ†>ͽÜ>Íg@>Í7‚>Í&¿>Íâ>Ìüv>ÌÙ>ÌÔÎ>ÌêÔ>Í- >Ík[>Í—H>ÍéÝ>Î)}>϶ >ÐÖ>ÐO±>ÏQÌ>Ï –>ÍÜí>Í›q>ÍRõ>Í*>Í>Í>Ìñ>ÌÓÔ>ÌÙ±>ÌåÎ>Í+t>Íbh>ÍŒ@>ÍßF>κ>Î=$>ÎÍ)>λ>Ρö>Î1Ï>ͱ­>Íx)>ÍŠ>͹>Í%H>ÌùÇ>ÌåÀ>ÌÓ@>ÌÑø>ÌÁY>Ìö>Í. >ÍW>͘i>ͱÀ>Í»:>Íâè>Íæ>ÍÄB>Í„¥>Íxç>ÍW\>ÍÖ>Ìóg>Ìó'>Ìç—>ÌßX>Ìå>Ìݲ>ÌäF>Ìø>ÍŠ>Íø>Í6w>Í_ë>ÍrS>ÍX>Í>͆U>ÍK >ÍNb>Í7È>Ͱ>ÌÑ‚>ÌË–>ÌÕ>ÌÓø>Ìã×>Í™>Ìþl>Ìøð>ÌÔ>Ìé¡>ÍÉ>Í/Ž>Í à>ÍC >Ía·>ÍIÜ>Í$å>Í=ã>Í&I>Ìñj>Ì«Ÿ>ÌœÙ>Ìñ>ÌÊ >Ìà3>Ìðx>͈>Í3>Ìåñ>Ì»Ü>ÌçX>Í>ÍH >Í7~>Í<>ÌúM>Í(>Í1>Í#>Ìël>Ì›c>̰D>ÌÂþ>ÌÁÌ>Í €>Ìåv>ÌéR>Ìîü>ÌéÈ>ÌГ>ÌÖ°>ÌþÃ>͹>Í >Ìý·>ÌêŽ>Ìî€>Ìýè>Í>Ì÷1>Ìͽ>ÌÜ>ÌÈ>Ì­€>ÌÏô>ÌÆx>ÌËf>ÌÚ¥>Ìò»>Í>ÌåB>Ìë7>ÌðŽ>ÌÙ->ÌÍ>ÌÜ´>Ì÷x>Ìì>Ìõ¦>Ìñ°>ÌÔÎ>Ì̈>̾L>ÌY>̤>̧À>Ì >̯m>Ì©C>̨h>̽8>̳B>Ì™]>Ì}æ>Ì”½>̤Ü>Ìœ¬>̤O>Ì£>̨(>Ìú>̈Ò>̦å>̲Š>̨.>ÌÒ›>̧ß>̪~>Ì©>̤X>Ì­™>Ì­ >̹`>Ì©.>Ì 1>Ìš±>Ìq>Ì“‡>Ì”d>ÌÇw>̧Ã>Ìž–>̲Ì>Ì·Z>Ì–Ï>Ì |>̤í>̶Á>̳>ÌŸn>Ì®š>̲¿>Ìã>̵7>Ì£!>Ì >Ì}F>̇ >Ì¢>̯»>Ì¢b>̤`>̳ê>̱f>Ì„Ž>̆>Ì­W>ÌÈ#>̵>̦>̾W>ÌêÅ>̰ú>̾Þ>̨¶>̪4>Ì—¤>Ì|É>Ì—>ÌÅc>Ì©¡>Ìžñ>Ì¡0>Ì¡_>̧>̆^>Ì‘®>Ì»>̵r>̨¸>ÌÃÎ>ÌÛ¦>̦>̨À>̬ï>Ì“ÿ>Ìãn>Ì™1>̇Ë>Ì´ô>ÌÁ3>ÌŸS>Ì,>ÌŠ:>̳ >Ì,>Ì¡2>ÌÇÍ>̰h>Ì’>ÌÖŠ>ÌÕ>ÌÂN>Ì‚>ÌÆä>̯1>Ìʈ>Ì®¾>̲e>ÌÀI>̼\>Ì€ˆ>Ìe>ÌQ>ÌÖi>̵4>̬b>̧'>Ì•>Ì­m>Ì’§>̶/>Ìò½>̇W>Ì¡J>Ì›m>Ìë >Í=>̺$>ÌÍë>ÌÃO>Ì”ƒ>Ì€R>Ì‹—>Ìä¿>̹Ä>Ì’Á>Ì‚>Ì™í>̨‹>Ìš,>ÌÑ>>ÌÕ¯>ÌÚ>ÍI¦>̯Ç>Ìñ>ÍuÌ>Ìéµ>ÌÓ´>̾÷>Ì‘Z>Ì´„>Ì ?>̰1>Ì¢ã>Ìr«>Ì>ÌÆÄ>Ìj¡>ÌE1>ÌV°>Í7u>Í:s>ÍI|>Íë>ÍŸ>ÌÛ“>̨%>ÌØÐ>ÌÖ#>Ì›Õ≯Å>̬>Ì &>Ì ">Ì“E>̈2>Ì´">Ì•ö>̤p>̰4>Ìîß>ψæ>ÏTž>Íì;>ÍY>Ìõ‡>Ì >>Ì»ß>̽Ï>ÌÄ,>̺ð>ÌÒò>Ì”S>Ì”˜>Ì‘”>Ìu©>̳š>̲f>Ìè">Ì–ƒ>Î@>Ïy>Ôx>ÍèÜ>Ìç©>Ìø±>Ì—:>Ì»c>̲>Ì¢>Ì«C>ÌÂ)>Ì“>̆J>ÌË>Ì}>ÌÂ>Ì¢ä>Ì¡µ>Ìûó>ÍÃ>̬@>ÌØ>Íkù>Í V>Ì2A>ÌmÚ>ÌÀ•>Ì£Š>̈:>Ì{ù>Ìž>̯ð>̘!>̆5>ÌŠB>ÌÏj>ÌÃ+>Ìâé>ÍM’>ÌÁ'>Ìœˆ>̈ú>ÌÿL>ÌWŸ>Ì\>̘‘>Ì>Ì}Î>Ì“Z>̨Ü>ÌÉŸ>ÌŸN>Ì”;>Ì…x>Ì“>ÌóH>ÌÆæ>Ìôs>Ìì»>ÌÚC>ÌÑ>ÌæT>ÌéÒ>Ìh>Ìâœ>Ì·=>̨>Ìœá>̘ß>Ì·–>ÌÓ>Ì•>ÌŠñ>ÌŒO>Ì”>ÌÂî>̰¢>̃À>ÌÓ>ÌÆ¸>ÌÎæ>Ì T>ÌÕË>ÌÆZ>ÌÂÜ>̦ã>Ì©ý>Ì˶>ÌÂå>̺ª>Ì¿^>Ì€A>Ìlì>Ì¡m>ÌËó>̽]>Ì¡û>Ìz:>ÌÑ–>ÌѺ>Ì·ï>Ìž›>ÌŸU>Ì—%>̰Ô>̈÷>Ì—t>ÌÐG>Ì®5>̪>̬4>̈ã>Ìa¿>̶K>ÌÝ”>̵`>Ìžj>Ìe>Ì€Ð>̹Â>ÌÀ9>ÌŠU>ÌœÃ>Ì™ˆ>̨’>Ì¥„>̪ù>ÌШ>Ìžá>Ìž?>Ì¡Þ>Ì>Ì–>Ì®©>ÌÁ>ÌË%>̦:>Ì‘^>Ì„Ù>Ì}Ú>ÌÀ­>̰D>̦>ÌŸø>̯2>ÌͰ>Ì´ê>Ì¥¡>Ìš/>Ì›'>Ì›¼>Ìcš>ÌA>̳à>̽H>̼›>Ì™þ>Ì€ç>Ìx†>Ì‹k>̵r>Ìׯ>̧¦>Ì“F>Ì©Ê>Ì£>Ì­>̦Í>̲~>ÌŸº>Ìí>Ìc>̃s>Ìe>Ì¢°>Ì‘>Ìx&>̘>̲Y>̹½>̬!>Ì«Æ>Ì”‚>ÌyŒ>Ì£É>Ì“¹>Ì·T>̰ >̤C>Ì›^>Ì‹®>Ì„™>Ìs7>Ìk`>ÌIÀ>Ìcñ>Ìp©>Ìc5>Ì\Ü>ÌHS>Ì;>ÌN>Ì@›>Ìa>̇ÿ>Ì‹Ó>ÌgÖ>ÌkY>Ìhy>ÌŽu>̪>Ìx†>Ì:q>Ì_X>ÌW >Ìl%>Ìoµ>ÌBÏ>ÌK`>Ì}>Ì ð>ÌOÖ>Ìg;>Ìb>Ì\M>ÌN>Ì/m>Ì_j>Ìx>Ì®>̶‚>Ìv >ÌP.>Ì[ï>ÌJE>ÌE9>ÌSä>ÌM&>ÌWÌ>Ì'×>Ì >Ì*~>ÌSp>Ìd >ÌAõ>Ì>Ì8V>Ì_>Ì{S>̾%>ÌŸ/>Ì}K>ÌY;>ÌU>Ì:Ñ>Ì)9>Ì7ß>Ì>>Ì-n>̱>Ëü¬>Ì7>Ëþ >Ì# >Ì7V>Ì/>ÌRW>Ì`©>Ìm›>̉ >Ì€ï>Ì“¬>Ìk(>Ì\>Ì( >̃>Ì)>̬>ËòÚ>ËæÄ>Ë˧>ËÙà>ËÑ\>˨y>Ëë>ÌE>Ì]>ÌYã>ÌZ°>ÌaŒ>Ìi >Ìyî>Ìj²>Ìd£>Ì4û>Ì$ >Ìá>ÌÞ>Ëĸ>ËŽö>ËXá>ËfÙ>Ë~ô>ËŸ|>˦Ê>Ëù<>ÌU˜>Ìh¿>ÌQÊ>ÌI§>ÌY>ÌMÑ>ÌpÂ>Ìyº>Ì>ë>Ì>ËöÀ>ËÔ*>Ë£ò>Ë 9>Êã>ʹ>Ë.>Ë­î>Ë$U>ËÍø>ÌEØ>ÌeË>ÌN¼>ÌB§>ÌS‚>Ì;*>Ì>Ì`é>Ì(¹>Ì&>ËÚ°>Ëq{>Ë?ç>ÊDè>ÉA>Èÿ>Êt­>Ëd>ʯý>Ë©¾>Ì õ>Ì7U>Ì/}>ÌB^>ÌKW>Ì]à>Ìax>ÌG„>Ì>Ëî >Ë·S>Ë`‘>Ê®h>Èz¿>ÄÊ(>ƾ>Èc5>Éä¦>Êȇ>Ë´‡>Ì_>Ì8½>Ì.Á>Ì;S>Ì:&>Ìg0>ÌlJ>Ìm>Ì+m>Ëòþ>ËÇ >Êí­>ɘì>ÆôK>»Ä@>¼]>Åa<>Èð >Ê"y>Ë–Â>Ëê9>ÌF>Ì)X>Ì:›>Ì >ÌgÚ>Ìf÷>Ì[Ã>Ì+ò>ËïÎ>˸¿>Ë=å>Éì>Æ=>¼,’>ºð>ŵ>ÈÙ3>Êd‘>Ët…>˯‰>Ì +>ÌV,>ÌT°>Ì+>Ì\y>Ì`>ÌSÅ>ÌNh>Ëêl>ËÍ+>Ëi‰>Êk>È*>ÅÎ>Å T>È ~>Éî>ÊÄ>Ë•>Ëâ˜>Ì)…>ÌX.>Ì<Û>ÌA4>Ì?Ä>Ìlé>ÌW4>ÌGÔ>Ëä/>ËÑ8>Ëœú>ÊÛJ>ʦ>Èù´>ÈÀb>ÉÞ€>Êž~>Ë?&>˽Ù>Ì>Ìï>ÌA>ÌE4>Ì[I>ÌX>ÌU>ÌF_>Ì7>̰>Ëà7>Ëš½>ËfÒ>Ë >ʵ7>ÊxÄ>Ê[>Ë~E>Ëžk>Ë˨>Ìi>Ì,>ÌB>>Ìg->ÌYr>Ìaí>ÌSl>ÌJÌ>ÌMv>ÌJ>Ìk>Ëè¸>Ëé[>˶>ËxÚ>Ënÿ>Ëp]>Ë”)>Ëþ]>ÌK>Ì(þ>ÌO,>Ìq{>ÌxC>ÌsN>Ì\:>ÌG°>ÌKÇ>ÌZO>Ìob>Ì4>>ÌÃ>Ì1>Ëÿ>˼{>ËûH>Ëç >Ëç^>Ì6î>Ì4">Ì5æ>Ì->ÌV >Ìrû>Ì{>Ìjƒ>ÌRœ>Ì>F>Ì7ö>Ì^ª>ÌEž>ÌMM>Ì,V>Ëó#>ËÝŽ>Ì*ì>ÌÀ>Ìs>Ì6y>ÌZý>ÌN>Ì7C>ÌVI>Ìll>Ìvv>ÌnÀ>ÌkR>Ìh7>ÌHO>Ì4”>Ì5¹>ÌTŒ>ÌD;>Ì>ê>Ì?>Ì/ž>Ì<>Ì;Ð>ÌFÂ>Ì/†>ÌPo>ÌR³>ÌZn>ÌlÐ>ÌuR>Ì`º>Ìvµ>Ìü>ÌXò>ÌMo>ÌjÌ>Ìgù>ÌRö>Ìe–>Ì_Á>̲>ÌGn>Ì] >Ìo\>ÌO|>Ì{y>ÌcÚ>Ì=ò>Ìfü>Ìs½>Ìn->Ìv•>Ìv?>Ìj:>Ì`Ì>ÌjÍ>Ìz•>Ìgá>Ìo>Ìn»>ÌQú>ÌU>ÌYR>Ìl>ÌYË>ÌM‹>Ì[7>Ìe,>ÌwC>Ìx>ÌsÐ>Ì]Å>ÌLì>Ì;)>Ì6Æ>ÌHO>Ì(‹>ÌL>Ì>ÌŽ>Ì%Þ>Ì#$>Ì ±>Ì.q>Ì"Z>Ì.¾>ÌB2>ÌP4>Ì_>>ÌP >ÌzÛ>ÌN>ÌM>Ì?¸>ÌH>Ì ›>Ì>Ì º>Ëó>Ëó>Ëþñ>Ëô‚>ËõN>Ì ‚>Ì ->ÌÓ>Ì7%>ÌQF>Ì_>ÌL¤>Ìuí>ÌEÂ>Ì9`>Ì(6>Ìœ>Ëöæ>Ëêq>Ëé>ËÁã>˵¡>Ë¿ô>˯Ô>ËÐð>Ëæ¼>Ëîý>Ì+>Ì'>Ì9(>Ì!Î>ÌbR>ÌV7>Ì1|>Ì 1>Ëû‚>Ëý>Ëåf>ËÎA>ËŸA>Ë~>Ë[¤>Ë}ä>Ë{¦>Ë¢>˹…>ËÕ>̲>Ì ^>Ì1;>Ì2â>ÌN6>ÌD©>Ì Ô>Ëô©>Ëüö>Ëôò>ËÇ:>˘î>ËD>Ë5À>Ë_>ˉ>Ëq>Ë_¯>Ë’Ñ>ËÔ¾>ËÝÆ>Ëïð>Ìp>Ì5>ÌA«>ÌWU>Ì!>Ëãƒ>Ëж>ËÊÄ>ˇï>Ë2ò>Êþ >ʇÊ>Ê|´>Ê|â>ʦ>ÊùP>Ë]ú>ËÇ>˺ï>ËáJ>Ì0P>Ì-ñ>Ì5g>Ì,>̃>ËÞ¿>ËËz>˺">ËAF>Ëë>ÊcÚ>Éä(>Épï>É|Ð>Éz">Êré>Êú¿>ËeÛ>Ëo7>˽ý>Ì-d>Ì(Š>Ì+c>Ì.>Ì–>ËùÔ>ËÁ>Ëw>Êï$>Ê‚–>É¢?>ÈœB>ÇÈ:>ÈAÆ>È·>Éh>ÊOŸ>Êþ—>ËVÙ>˪b>Ëàý>Ì ?>̤>Ì!>Ì…>Ëðë>˾N>ËO¥>ʺW>Éñ5>ÈØd>Ç! >ÄÛS>Å3>ÆÖž>ÈTk>ÉÓ¸>ÊšÔ>Ë&k>Ë}>ËÅ{>Ëå0>ÌÖ>Ì,³>Ìy>ËÕó>˾n>ËEZ>Êš>ÊÀ>È!Ñ>Ä’™>Á©>ÁÂþ>ă)>Ç%ú>ÉVŒ>ÊV¥>Ë.>ËŸ—>ˤî>Ëâµ>ÌÂ>ÌC1>Ì.á>Ëê >˵‚>ËE¼>Ê·1>Éö‹>ÇjÉ>ÃõÜ>Án>Á[>Ä®p>ÇT>ÉÐ>ÊbH>Ë(F>Ë–Ù>Ë­ø>Ëñô>Ì¢>Ì&Ä>ËÿÎ>ËÜ>Ë•þ>ËDž>ʼ­>ÉÝ]>È ¯>Æfò>Äý^>ÄÊh>ÅÝÞ>È}á>ÉÙ$>Ê’*>Ëb>Ë“Ñ>ËÙ6>Ëö¸>Ìw>ÌÇ>ËÞŒ>ËÛf>˵B>ËU\>ÊÚ0>Ê6a>É^>ȃ>ÇÆ=>Ç˳>Çú—>ÉO“>Ê?’>Ë7>ËSb>ˬÛ>Ëér>ÌÜ>Ëñ°>Ì&Ÿ>Ì k>ËïE>˼ù>Ëdš>Ë$9>Êø~>Ê~}>Éçw>ÉqÅ>É[Ž>É‹ƒ>É÷½>Ë¿>ËI¤>Ë>Ë¿ >Ëõå>Ëý>Ì>Ì@>Ì S>Ì>ËÚ>Ë¢ª>Ë›W>Ë/Ö>ʼ>Ê«q>Ê~Y>Ê„u>Êkô>Ê×{>ËA˜>Ëw|>˺9>Ë«3>ËÅw>Ëý>Ì>ÌQ'>Ì/J>Ì Ì>ËïQ>ËËå>Ë¿ó>Ëšó>Ëb´>Ë!æ>Ëá>Ëd>Ë(>ËKª>Ë„³>Ë«õ>ËÜ#>ËÞ‡>Ëü<>ÌL>Ì4œ>Ìp >Ì@(>̺>Ëö¶>Ëì>ËøY>Ëô¼>ËÇÓ>ˇR>Ëk<>ËeÔ>Ëw>˱í>Ë¿ß>ËÂ>Ì{>ÌP≯>Ì>%>ÌJ=>Ìd>Ì:>Ìë>Ì›>Ì >Ì>Ëü+>Ëé¤>Ë¡(>˦¹>ËÉ\>ËÆ›>ËÎ>˲Æ>Ëæ>ÌÖ>ÌJ>Ì0¤>ÌO(>ÌUB>ÌPÇ>Ì>¹>Ì!K>Ìæ>Ì>Ì6`>Ì(>Ìq>ËßÂ>ËðD>Ëð©>Ëÿ%>̧>ËÐî>ËùÔ>Ëê">Ìu>Ì>C>Ì[U>ÌXb>ÌZº>Ìq9>ÌK¯>Ì9…>Ì2 >Ì3U>Ìš>ÌE>Ìã>Ì'%>Ì7>ÌÐ>ÌÒ>Ëù¡>Ìj>Ì>Ì/M>ÌK>Ìr>>Ìao>ÌeÌ>ÌC¨>Ì&]>̉>Ëþš>ËП>ËÂ>ËÙ>Ëè >ËðÉ>Ëã­>ËÙœ>ËÙ–>ËÁ>ËÓÁ>ËÿY>Ì,>Ì15>Ì9ð>Ì9.>ÌdŒ>Ì5¿>Ì —>Ëñ<>ËÞÄ>ËÈ>ËÀS>˯Ã>ËÓÀ>ËáÜ>˧Õ>ËÅ>Ë¥ì>˺N>ËУ>Ëít>Ì"7>ÌD(>ÌZ>Ì% >ÌI¾>Ìð>Ë÷B>ËÜD>ËË”>˽³>˼Ì>Ë>ËŽK>Ë‹a>ËiÙ>ËZW>Ër¤>Ë¢y>˹¦>Ë͆>Ëõ“>Ì»>Ì U>Ì _>Ì'w>Ìü>Ëã’>ËÃ>˳9>Ë;>Ëf†>ËIj>Ë9W>Ë0á>Ë T>Ë40>ËF¬>ËZæ>Ë}f>Ërh>ËÃ>Ì—>Ì>Ì>Ëú»>Ëè^>ËÊÎ>ˤö>˦v>Ë‚Æ>Ë€>ÊÀŽ>ÊÔF>ÊÒÞ>ʱ²>ÊÝœ>ËF>Ë>Ë(Æ>Ë`>Ë«.>ËðÜ>Ì$>̱>Ëúù>ËÙƒ>˳†>Ë„=>Ë]ß>ËUô>ʸ->Êa@>Ê0¯>Ê1>Ê5~>Êb·>Êz*>Ê·{>Ê÷°>Ë=.>ËŠï>˸Ó>Ëð >̉>ÌÓ>Ëâ#>Ë¡>Ë]z>˾>ÊõÙ>Êž>ɹj>É@[>É>É@ì>Él4>É’D>Ê%Z>Ê¡ñ>Ë z>Ë€Õ>ËšE>ËÍÎ>Ë÷Á>Ëï >ËÇ>ˤ‘>Ë[>Êù`>Ê©T>Ê\È>ÈÔù>Ç÷X>Çn¬>Çf3>ÈÅ>ÉØ>Ê>Ê·>ÊÂg>Ëg¡>Ë’>˲Ý>ËâÕ>ËæB>ËÂø>˘l>ËHo>ÊÁð>Ê5*>Éq >È!>Æ—S>ÆTè>Åhê>Ư>ÈRÎ>ÉUö>Éß2>Êœf>ËRy>ˉ0>˲ñ>ËÛp>Ëá–>ËÀR>Ëž9>Ëz>ʧë>Éá>È¿Â>Çc¯>Å–>ÅŽ>Ä<†>ÅÊ•>ǃ?>É¿>Éߎ>Ê‚s>Ë?À>Ëö>ˬè>ËÏR>ËØ[>ËÆÂ>˺²>Ë ÿ>ʘŸ>ÉÏî>È·R>Ççš>Åâ¢>Ãõ'>Äl >ųs>ÇTs>Èë¼>ÉÍ›>Ê >Ë1ˆ>Ë‹À>Ë«>ËÀ²>ËçH>ËÂ9>Ë™>ËBÑ>ÊÆt>Ê|>ÉdU>ÈEÿ>ÆŒÛ>Åy>Ƨ>Æ‚¶>ÇÙW>Èò€>Éâ>Ê®²>ËNê>ËvË>Ë¢H>ËÊt>Ëë–>ËÁ?>Ë›Ù>Ë->ÊáA>Êp>ÉÇ\>Èõú>È >Çâù>ÇÁ·>È>>È«Y>É_ù>Ê¿>Êé9>Ëm4>ˆ$>Ë’È>ËÁ,>Ëö7>ËÕ²>˳‘>Ëxr>Ëh>Ê»%>ÉÝ">É™t>ÉyV>É©¬>É*à>ÉOÜ>É”¡>É®>Êzþ>Ë»>Ëmõ>Ë­‹>˧¥>Ëø>Ì R>ËëÅ>ËÊ¢>˧–>Ëq†>ËA>Ê´$>Ê[Ú>Ê1*>ʼ>Éþl>ÊE>Ê4W>Êm½>Êâ‰>ËBS>Ëõ>˪ >Ë¿_>Ëר>̰>Ì ;>Ëâ5>Ë’8>Ëff>ËO >Ë)u>Êáh>ʼ^>Ê¡>Ê>ʯÉ>ÊÞ>>Êû >Ë0c>Ës >˘Ž>ËÄÀ>ËÜ>Ëîî>Ì+>Ìœ>Ëù >˹ >ˆ>ËsÜ>Ë[7>Ë’‡>ËN˜>Ë÷>ÊßF>Êÿ¿>Ë2^>Ë<>ËaÔ>Ë„>˦>Ëâ=>Ì[>Ì„>Ì^>Ìø>Ëí/>ËËÔ>Ë¥Í>˧ƒ>Ë­7>ËÁ—>Ë‚$>Ëca>Ë.ö>ËV´>ˆ.>ˉ>Ë©Û>ËÑ9>Ëæi>Ì Æ>Ì"å>Ì*ü>Ì/>ÌGe>Ì6>Ëî,>ËÕ<>Ë«>ËÍ>ËÑY>Ë«œ>ËšÒ>Ë“é>Ë›>Ëš1>˶þ>ËÞà>ËëÔ>Ì >ÌI>Ì>>Ì4 >Ì5 >Ì å>ÌÚ>Ì #>Ëÿ>ËùÍ>ËíW>Ëä>Ëæè>ËÏí>ËÄ.>ËÉš>ËÌ~>Ëà¿>ÌÔ>Ìw>Ìz>Ì0—>Ì@ >Ì<_>Ì >̉>Ì6>Ëó>Ëàá>Ëǯ>˶U>Ë·>˽->ËÝ¡>˱ >Ëœe>˱Î>˺’>ËÇ•>ËÜG>Ëï>Ì>Ì"0>Ì$É>Ì'>Ë÷¸>Ëæè>ËÒo>ËÃ4>Ë©a>Ë‘™>Ë„©>Ëv[>Ë’ä>Ë’Ã>Ëz*>Ë„A>Ë™s>˲E>ËÅ>ËÂq>ËÒ¬>Ìo>Ìa>ËëÓ>ËæM>ËÒ|>˽¿>Ë©e>Ë€w>ËM0>ËH >ËW>ËH›>Ë>¢>Ë6*>Ë,Ï>Ë`>˨º>˼>ËÊÁ>ËÒz>Ëég>Ëõø>ËÓh>ËÕ.>ËÃ>˾n>ËœO>ËW~>ÊþÅ>Êå^>Ë>Ëu>ÊÕí>Ê×~>Êîd>Ë)Ê>Ë{>ËŽö>Ë¢Þ>˲f>ËÑû>Ëðà>Ëáê>ËÇg>Ë©>Ëe>Ëp>Ë%ë>Ê×+>ʰâ>ʦD>Ê]€>ÊNH>Ê`>Êã>Êö­>Ë0<>Ë[>Ëu>Ëœ3>ËÄy>ËñJ>ËÜx>˯H>Ë>Ë^>Ë$%>ʸå>Ê„B>ÊM/>Ê š>Éñ†>ÉåE>ÉÙE>Ê5ª>ʧe>ʶ©>˳>ËNó>Ëmß>˱³>Ì L>˸Ù>˱>Ë~î>Ë:ä>ÊïD>Ê£—>Ê.ö>ÉÈ>É}a>É^x>É0Ý>Ét¸>É»:>Ê9>Êrg>Êûf>Ë>ËM>Ë•Ú>Ë×>Ë«A>Ëfâ>Ëe>ËÍ>ʳ¦>Ê^Ã>ɹ¾>É!õ>Èž>È8­>È*¬>ÈjÛ>És>É2>Ê"ì>ʳ´>Ë G>ËdU>Ë¢|>Ëŧ>Ë®“>Ëz>Ë:›>Êæ|>Ê®Ñ>Êx>É*E>È`h>Çžð>Ç ó>ÇF>ǃË>ÈB>ÉI=>Éç§>Êk>ÊÁ)>Ëd >ˤX>ËÁ<>ˬü>Ë|/>Ë«>ÊÖª>Ê} >É®,>ÈþÁ>È´>Ç—>ÆTC>Æbì>Æýx>È9U>Éd>ÉÏ/>Êq&>ÊÕ¡>ËVð>Ë’È>Ë´>ˬ‹>Ë€E>Ëð>ÊÉ>Êl>É¿K>É ë>È+/>Ç2Û>ÆRÞ>Æmp>Ç,>È©>É2$>Éä†>ÊX¯>ÊÖŒ>Ë` >Ë©G>˳L>˹â>Ë„{>Ë*>Êâe>ÊŸZ>Ê4>ÉSØ>È >ÇŠÓ>ÆÓk>ÆÝV>Ç}Q>ÈZD>ÉPÚ>É÷B>Êyù>Êßz>ËXµ>ËŠ>Ë­(>ËÆh>Ëò>Ë;Á>Êó¤>ÊÁÂ>ÊS·>ÉÍW>É${>È>ÇÝ>ÇÕÉ>Èj±>É*>ÉšZ>ʘ>ÊŠu>ÊêË>ËSa>ˈx>Ë«’>ËĆ>Ë£‰>Ë’u>Ëõ>ʽŒ>Ê”>ÊL§>ÉÁ >Èê!>ȽF>Èßì>Éa‰>ÉÑ >ÉßW>Ê8ê>ʶô>Ë(Í>ËMâ>Ë…â>˪W>˵Ì>Ë«/>˘F>Ë<ô>Êõh>ÊÇ5>Ê‹P>Ê8>ÉïS>É’Ò>É—w>Éú->Ê-¶>ÊMŽ>Ê ‘>Êö6>ËW>Ë„>ˤ+>ËÊ>ËÀ >Ë«‹>Ëœý>Ë–¼>Ëc«>Ë«>ʽã>Ê„Ž>ʘ´>Êa1>Ê>Ê_H>Ê™º>ÊÁ>Ë ×>ËFè>Ëwð>Ë¢!>˼>ËÖò>Ì—>ËÂE>˯*>˱u>ËR>Ë?U>Êõ®>ʶZ>ÊéÎ>ÊáZ>Êà>ÊÎd>Êé{>ËQ>ËM$>Ë}p>Ëš™>Ë¿Z>ËÆ >ËÞS>Ëþ#>Ëõ>ËÛ:>˸Ö>Ë–ÿ>Ë{Ä>Ë_è>Ë)ƒ>Ë>Ÿ>ËNð>Ë35>ËI>Ë)>Ëa·>Ëÿ>˰’>ËÂÍ>ËÌ&>ËÑò>Ëì¤>Ìn>ËÈ>ËÑê>ËÌ'>Ë»j>Ë®à>ËÅ'>ËŽ!>ËrÆ>ËXG>Ë_T>Ë[¬>Ëbv>Ë¡·>ËàÌ>ËÙ>ËÚ–>˾I>ËÖ³>ÌÅ>ÌÂ>Ëã´>Ëè´>Ëå>ËÚÌ>Ë×±>ËÒä>˲ >Ëu<>ËqÜ>ËpG>Ë‘¯>ˤ*>ËÂÓ>Ëñº>ËüŸ>ËüÑ>Ë÷<>Ëä>Ì >Ëå¿>Ëén>Ëê|>ËÞž>ËÌ“>Ë·ÿ>Ë¥>˧>Ë•Ö>Ës›>Ë|ï>ËŠ€>Ë¡!>Ë«>˱7>Ë¿ >ËÏ>Ëãý>Ì “>Ì9>˯j>ËÏõ>Ëà>ËÊr>˱e>Ë”Q>Ës>Ë\I>ËS>ËO¡>ËDA>Ë_Ü>Ë„Ÿ>ËŽÑ>ËÐ>Ë¡ >˯Ú>ËÄ@>ËÓÜ>Ëëô>Ë®S>ËÁ‰>Ë¿P>Ë©|>Ë’~>ËnÔ>Ë1Ò>Êýâ>ˆ>Ë à>Ë>Ë,Œ>ËO¢>ËR‰>ËZÉ>Ë…¢>ËŸ>Ë©·>Ë¿>ËÙ§>ËÕ>˾Ï>Ë N>ËmÏ>ËUr>ËCÚ>˲>Êá¹>ÊË0>Ê~>Ê®b>ÊçU>Ë#e>Ë=>Ëô>ËL`>Ëv>>Ë{è>Ë›ì>ËÉŽ>ËúD>˽W>ˇ*>ËOª>Ëh>ÊÛƒ>ÊÇo>ÊÑ!>Ê¢E>ÊT†>ÊE^>Êrb>ʺu>ʦ˜>ÊÖF>Ë&)>ËfY>Ëk<>ËxÄ>Ë·p>ËÄì>Ëš{>Ëgí>Ë9S>Ë'>Ê£3>Êf>Êl>Ê1ê>Éßl>É“>ʽ>ÊGP>ÊAy>ÊŽ=>ÊüÍ>ËlÀ>ËmÍ>Ë~>Ë •>˧p>Ër²>Ë4¼>˨>Êí[>Êfê>Éü7>ÉÛ>ɱ">ÉMb>É*‚>É\×>ÉÂê>Éþ9>ÊiZ>ÊÓ­>ËË>Ëa9>Ësé>Ë¢A>Ë’p>ËZ?>˲>ÊðÇ>Ê Ù>Êé>É“9>ÉY†>É Ã>ȯæ>È™‘>È¿t>ÉÂ>ÉŽ>Ê50>ʶ8>ÊÑ >Ë7m>ËDÖ>Ë>Ë>ËX}>Ë*y>ÊäF>Êe*>Ê û>Ét}>Èö¾>ÈeÆ>Çóx>Çìü>È î>ÈÓE>ÉrZ>Éú>Ê^Q>ʈV>Ë>ËB”>Ë’0>Ë‹½>Ë@Ë>Ë>>ÊË@>Ê` >Éá½>ÉT¶>ȯu>ÈÓ>ÇYô>ÇfÔ>Ǽ>È]‘>É,¼>ÉÑL>Ê0ˆ>Ê=>Ë ï>ËH8>ˆ:>Ë”›>Ë$à>ÊäÏ>ʪx>Ê\æ>Éв>ÉAà>ÈyŸ>È×>Çf’>Ç|_>DzÒ>ÈWé>É6E>É×µ>ÊIy>ʨ¿>Ë ’>Ë%“>Ërk>Ë©â>Ëcö>ËÒ>ÊŽ¼>Ê4Î>Ê 5>É–ä>Ⱥ@>È>T>dzŠ>dz‚>Ȭ>È—þ>ÉL}>Éï >ÊRõ>Ê¢Ú>Ë à>ËOç>ˆ>Ëı>Ëp>ËS>ÊÁb>Êq±>ÊOË>Éî$>É=>ȱµ>Èu>È}Ó>ȶg>É “>ÉŽO>Ê,>Êc¶>ÊÎä>Ë]À>Ëœ”>Ëî>˦â>Ëo—>Ëû>Êãä>ʺm>ʦ >Ê )>É¥>É.>ÉY>É#Ö>ÉtÃ>ɰ¹>É÷Œ>ÊTC>ʬ?>Ë ¿>Ëxz>Ë–>ˤ6>Ë¥Ì>Ë€X>Ë?¸>ËJ>Ê×ñ>ÊÖl>Ê|~>ʘ>É¥b>É·>ÉÞº>Éú~>Ê'£>Ê_@>Ê·¦>ÊÞ2>Ë*b>Ëp6>Ë–>˱->Ë«>Ë”`>Ëy(>Ëv>Ë>Ê÷L>ÊĘ>Êtâ>ÊAf>Ênn>Êeß>Êbò>Êk©>Ê‘>Ë`>Ë;‘>Ë[™>Ëï>ˬ>ËÂŽ>Ë›Q>Ë Ã>Ë”?>Ë„‰>ËJö>Ë$9>Êó>ʹ†>ʱj>ÊÖ>ÊÔ©>ÊÃ->ʯ‹>ÊÜö>Ë0[>ËUÜ>Ë€’>ËË>ËÊ2>ËÓß>ËÉ>Ë›W>Ë‘Å>Ë’Ž>ËyX>Ë]s>Ë2À>Ë;q>Ë/x>Ë]>˽>Ë’>Ë>Ë(9>ËTñ>ËhN>˘¸>˾­>ËÎd>ËäË>ËçJ>Ë»Ö>˳>˱0>Ë£È>Ë>ËzÑ>Ëv#>ËjÁ>ËPÁ>ËLà>ËN†>ËP)>ËY½>Ëy…>Ë£>ËÀi>ËÆ»>Ë×1>Ëó>Ëýñ>Ëá¢>ËÖ >ËÌ>Ë»>Ë•Ú>˲r>Ë™M>˃š>Ë{t>Ë|þ>ˉ\>Ëf>Ë–h>ËœÇ>˶b>Ëð²>ËöÔ>ËêÙ>Ëü)>Ëâá>ËÛ{>ËÙ>ËÍç>˽Ã>Ë­]>Ë’G>Ë{>Ël5>Ër[>Ë|%>Ëz>Ë…>ËŽÒ>Ë—ä>ˤÎ>˱›>˺1>Ë®7>ËÎ_>ËÇõ>ËÇ´>ËÜ>ËÂl>˧[>Ë…â>Ëax>ËD0>Ë,Ñ>Ë “>Ë3.>ËPB>ËoJ>Ëyg>Ët.>Ë~¾>ËŽƒ>Ë>Ë—^>ËÈ6>˶p>˰Ç>˲¢>Ë­>˧g>Ë]×>Ë >Êûó>ÊÕƒ>ÊÜá>Êøá>Ë.>ËP8>ËHê>Ë3ò>Ë=T>Ë[J>Ë„À>Ë¡2>ËÁv>Ë¢>ËšJ>ˉÇ>ËrÂ>˃­>Ë/#>ÊáÊ>ʼ>Ê–®>ÊÆ>ʯY>Êͽ>Êî”>Êûž>Êþ1>Ë a>ËJö>ËnÍ>Ë‘K>˵Â>Ët&>Ëu>Ëlr>ËO]>ËZ–>ÊÿM>Ê®G>ÊWv>ÊV½>ÊWÊ>ÊTç>Êe€>Êu>ʧ¢>ÊçM>Ë,Í>Ë1>ËS>ËyL>Ë£,>Ë\6>ËhÇ>ËR>Ë,>ËÅ>Ê»ó>Ê^w>Ê+ >Ê—>Ê•>ÉÞŠ>ÉÖZ>Êë>ÊIâ>Êše>ÊñM>ÊäÇ>Ë&ó>Ëa">Ë|X>Ël->ËW>Ë1E>˯>ÊÆ">ÊM÷>Ê Ö>Éìn>É´H>É·>Éms>É]q>ÉÔ>Éþ(>Ê[¿>ʉe>ʾ:>ÊþŒ>ËL#>ËŠð>ËxŸ>ËO>>Ë*h>Êæ•>ÊŒ >Ê>ÉÖ >ÉžØ>ÉIî>É>ÈëE>Èú•>Én(>ÉÐ>Êî>ÊJ‹>ʯÆ>Êô:>Ë4Ž>˃Æ>Ë~Œ>ËFÇ>ËT>Ê¡w>ÊD>Ê#f>ÉÁ´>ÉXÊ>Èï >ÈÅ•>È¥>ÈÅQ>É÷>ɇf>Ê >ÊS >Ê“Ã>ÊÌ–>Ë*L>˧ü>Ë{Ã>Ë;i>Êï>Ê‘>Ê0>Éú7>ɰò>É';>Ȫò>Èwe>Èr‚>ÈšÒ>Èì©>Év3>ÉôÊ>ÊF€>Ê…>ʲ>Ë!ž>Ëv2>Ë‚‘>Ë>ž>Êð3>Ê”W>Ê3>Éí^>É™ë>Éi>ÈzÔ>ÈV¤>Èlô>È¢>Èõ>É|>Ê>Êg>ʯ>ʱò>ËW>Ëa‡>Ë‚¡>Ëdu>Ë †>ʶ>Ê]>Éê•>É~s>ÉLN>ÈÇ|>ȇK>ȯq>ÈÏÞ>ÉÐ>ÉÑ>É÷Ï>Êt»>ÊÂ1>ÊÙâ>Ë,&>Ëjp>ËSË>ËHC>ËÑ>ÊÛ$>ÊŽŒ>Êõ>ɵš>Ék >É+B>ȼ>ÈðŠ>É>ÉU>É¿ÿ>Ê'­>Ê„¢>ÊÕ”>Êÿ>ËT½>Ë{Ò>Ë}Ÿ>ËVg>Ëè>Êä½>ÊŸÝ>ÊN§>Ê/>ÉÑú>Éxx>É= >Éf >ɉ >Éœ#>Ê>Ê]æ>ʶØ>Ë P>Ë«>Ëc;>ˉæ>Ë”¹>Ëo>Ë8ü>Ëæ>ÊÒŸ>Ê’Ë>Êw#>Ê3?>Éè^>ÉËß>ÉÝ\>Éò±>Ê—>ÊQÝ>Ê>ÊÝ_>Ë8×>ËQ,>Ë|;>Ëš€>Ë¢¨>ˇ>Ë\k>Êô’>Êü¬>Êê->ÊÇØ>Êz˜>ÊA!>ÊÊ>C>Êc=>Êv”>Ê’>Ê º>ÊßÍ>ËFz>ˉ·>Ë™>Ë­>Ë«‡>˵ƒ>ËÜ>Ë8[>Ë+z>Ëk>Êæ‡>ʺ|>ʱõ>ÊÄŒ>ʪr>Ê©>ÊÆ">ÊÛF>Êì>ËÎ>Ë`¸>ËŸD>˨ä>ËÀ$>ËÄ…>Ë´ >ËK>Ë|A>Ë_¶>Ë2>ËÉ>Êáø>ËU>Ë ”>Ê÷…>Ë$t>ËŸ>Ë"^>Ë#a>ËP>Ë—>Ë£>˼>Ëæq>Ë݃>ËÇ%>˶>˨Æ>Ëš–>ËF³>Ë->Ë'>Ëe›>ËXT>ËB©>ËOK>ËNÕ>ËQ³>ËA>ËnC>Ë›l>Ë­¯>ËË >Ëøß>Ëô®>ËÞU>ËÍ>˾>Ë«h>Ëå>Ëc=>Ël€>Ë|'>Ë}ö>ˆ¨>Ë›£>Ë…„>˃>Ë{ >Ë“L>Ë£ø>Ë·1>ËÒÄ>Ëëc>ËÕ2>ËÇR>˸º>˶z>˰þ>ËÀ°>Ë€>ËUØ>ËF>Ë)ä>ËB€>ËfT>Ë`‘>Ël›>Ë‚ >Ë”>˧O>˽Â>ËÓ>ËÛî>ËÀ<>Ë­V>Ë~Î>Ë;>ËŽ9>Ë|L>ËYS>Ë8ü>Ë$z>ËG>ËL9>ËN¯>Ë1u>ËD«>Ë_¾>Ës>ˈG>Ë™Ð>˳5>ËÉ>Ë«3>Ëš&>Ë >ˇ>Ëk>ËMŸ>Ë"ï>ËP>Êò·>Êë†>Êÿü>Êûä>ÊóÕ>Ë ]>Ë4Ï>ËJJ>ËiÙ>Ë‚g>Ëœ®>˹Í>ˉB>Ë|Ì>Ëmæ>ËR>Ëë>Ë >Êï >ÊÍ:>ÊÅo>Ê·Ø>ʶC>Ê­\>Ê£ß>Êðí>Ë7>Ë >ËEf>Ëi©>ËŒ–>Ë·)>Ë^ >Ë^´>ËT8>Ë-3>Êí¬>Êãï>ʸc>Êx>ÊM·>Êir>Êmá>Ê~>Êym>ʵÙ>ÊÉD>ÊØ4>Ë.>ËR¥>Ëx°>˪>Ëc>Ëym>ËR>Ë ù>ʰ >Ê”{>ÊoÇ>Ê+ø>Ê–>Ê$>ÊJ>Ê"±>Ê8E>Êjá>Ê—š>ʳü>ÊýG>Ë/>Ë[ >Ë‹“>Ëeš>ËP.>Ë¥>ÊÕ6>Ê’•>Ê>ÊDx>Êú>ÉÅí>É’n>É®õ>ÉÍ>ÉàŠ>Êú>ÊTc>Ê„G>ÊÆ•>ÊñÁ>Ë=[>Ë—R>ËkÒ>ËC>Êâí>ʰ‡>Êz>ÊNT>Ê´>ÊÇ>É¢<>Éeê>É\Ê>Ƀ1>ɰ¨>ÉäF>Ê'Á>Êbñ>ʘ˜>ÊÂÛ>˵>Ëbä>Ë‚‡>ËGÑ>ÊìŽ>ÊŸÀ>ÊSö>Éùƒ>ÉÃd>ɘ>É_K>É'>Èý´>ÉKƒ>ÉG>ɱ>Ê í>Êj‘>Ê¡w>ÊÏ÷>Ë=>Ë,£>Ët¹>Ë5R>Êî>Ê †>ÊF¢>Ê;>É­$>Éký>É%Ö>Èð¾>Èæv>ÉIØ>Éd¿>ÉzÃ>Éø£>Êc>ʲ„>Ê÷6>Ë:h>ËZ@>ËB­>Ë>Êìc>ʧ÷>ÊY>Êu>ÉÒÅ>ɃÖ>Éb>Èú >ÈîN>É(Í>ÉYÖ>É‘Þ>Ê >Êy:>ÊÛ›>ËL>Ë4Z>Ëdý>Ë7Ø>ÊùN>Êæœ>ʨã>Êb·>ÊÉì*>ɇf>É7>É8Ó>ÉY¹>ÉB:>Ée>ÉÃÇ>Ê-r>ÊÙ>ÊÒ>Ëã>Ë,ü>Ëa>ËV2>Ë,~>Êþ}>ʲ(>Êsx>ÊTA>Ê o>ɹ£>ÉWF>ÉF¢>É‹>ɇ¢>Éq/>ÉêÉ>ÊK¥>Ê“ð>Êáÿ>Ë þ>Ë”>Ë^o>Ëtâ>ËK·>Ëg>Êâ>ʰž>Ênè>Ê# >Éù>ÉÅî>É©¢>ÉÁN>ÉØÔ>ÉÌ>Ê t>Êl€>ÊŸz>ʽß>Ë¡>ËTö>Ëx>Ë“=>Ëi<>Ë7,>Ë o>Êä®>Ê•R>Ê^°>Ê_¥>Ê*É>Ê>Ê `>Ê->Ê-Å>ÊV>Ê™u>ÊÍ=>Êëª>Ë.J>Ëo_>Ëó>Ë—å>Ëq>ËT?>Ë0à>Ëx>ÊÍŸ>Ê…'>ʉ¸>Êo·>ÊUž>Ê^Š>Ê“º>Ê‹°>Ê‘&>ʵ>>Êêf>˘>ËA>ˇ>ËŸ&>Ë¢!>Ëz`>ËqV>ËX#>Ë.©>˧>Ê× >Ê¿©>Ê¥®>ʃw>ʯd>ÊÛ[>ÊÕ¢>ÊäØ>Êö->ËM>Ë3Û>Ë4¦>Ëzê>ˤH>Ë»î>Ë¿¶>Ë©·>Ë|D>ËR’>Ë*M>Ëv>Êí{>ʾ‡>Ê»í>Êì >Ë&©>ËÆ>Ë;Ô>Ë-ß>Ë2O>ËL‡>Ën˜>Ë’°>˦B>ËÔÊ>ËÌ >˸}>˘>ËvÐ>ËJþ>Ë(B>Ë/$>Ëd>Ë ¨>Ë$q>Ë8Ô>ËHÙ>Ë]×>ËeH>˃4>Ëù>Ë” >˧É>˳E>ËýO>ËÙš>Ë»>ˬ>Ë’#>Ëpä>ËB^>ËVâ>Ë`>Ëh\>ËS±>ËLÚ>Ëm >Ë}Ç>ËŽb>Ëžî>Ë›4>Ë¡¦>˺ª>ËÍ¡>ËÆë>˹6>Ë«D>˧¬>Ë–i>ËZ6>ËZá>ËVù>ËJ>>Ë>§>ËH9>Ëa˜>Ë^j>ËeÎ>Ëx~>ˉ>ËŸ×>Ëô>Ë“g>Ë­{>˶>˦E>ËÏ>ˇN>Ëv†>Ë]ç>ËHŠ>Ë;Õ>Ë*§>Ë >ˆ>Ë8w>Ë$G>Ë0|>Ë]³>Ëo/>Ë„>Ë’Õ>ËŽ†>˪ò>ˤ>Ë•t>ËF>Ë@=>Ëa>Ë]û>Ë&·>Ëj>ËØ>ÊùÙ>Êî>>Êñ">Ê×÷>Êü“>Ë:À>ËV¨>ËŠ7>˽>ËŽ:>ˬÖ>Ëg>Ë~>ËfÑ>Ë4Š>Êö?>Êãì>ÊÙÖ>ÊÕu>ÊÔ…>ÊÛ‹>ÊĦ>ʼÊ>Ê»m>ÊÝ™>Êÿç>Ë E>ËQj>Ëd >Ë…S>ËÅ >ËsJ>Ë_">ËG>ËÄ>Ê¿5>Ê®®>ʦ{>Ê–˜>ʆy>ʦ%>ÊŒn>ʉš>Ê™Á>ʶÕ>ÊÀ.>ÊÖ¦>ˆ>ËO*>Ët¢>ËÆÚ>Ëaå>Ë, >ËÅ>Ë!>ÊÌW>ʨÓ>ʇµ>Ê`\>ÊÊ5°>ÊA:>Ê:³>Êc¡>Êš™>Ê·¼>ʲ^>ÊÎ>Ë(h>ËNº>Ës½>Ë\Ä>Ë7i>Ë>ÊçÒ>ʳ]>Ê‘­>Êi>ÊFÑ>Éû¤>ÉÚl>ÉïÅ>Ééâ>ʬ>ÊZÕ>ʦx>ʨº>ʽ.>Êû‰>Ë,†>Ë6·>ËX†>Ë7n>˲>ÊÔÔ>ʪÀ>ÊŒÁ>Ê8>ÉÍ>ÉÄu>ɯÅ>ɾ>Éîö>ÉÔÑ>ÊT>Êd›>Ê’>ʶ>Êäç>Ë>ËAN>ËOö>Ë6d>Ë>ÊËK>Ê–&>Êdá>Ê Æ>É¡ž>É©²>ÉŠQ>É€s>É·Œ>ÉÒá>Éþ¯>ÊG>ÊŠ;>Ê­¥>ÊÚ³>Ë!>Ë=[>ËN÷>Ë4^>Ë>ÊÌ=>ÊŒñ>Êi`>Éø >É‘/>Ɇó>Éo?>Éwö>É¢">É»É>Éá`>Ê&)>Êl‚>Ê“!>ÊÁí>Ëp>Ë]ô>ËO÷>Ë9\>Ë />ÊÓø>ʈ`>ÊE>Éö¼>É·f>ÉŽ+>É{ö>ÉlY>Éz®>É®»>Éà>Êá>ÊK->Êw¼>ÊÛò>ËJ>Ë">ËO·>Ë(’>ËÔ>ÊÞ¥>Ê‹>>Ê?ñ>Éþ]>ÉÃ’>ɪ>É>Éñ>É…Ý>ɼÓ>Éýê>Ê/¡>Êbÿ>Ê®>Ëb>Ë/[>Ëb·>Ë]Ð>Ë6<>Ë >ÊÓ»>Ê“>ÊR>Ê!c>ʉ>Éà>É¿>ɳµ>ɶò>ÉÝk>Ê85>Êjë>Ê•]>ÊÀ>Êç§>Ë>ËY­>Ësä>ËK½>ËÄ>Êæ>>Ê­ >ÊXo>Ê,ž>Ê?<>Ê!X>Ê É>Éÿ’>Ê S>Êe>ÊOë>ʃ[>ʰ/>ÊÖc>ÊõÅ>Ëè>ËfR>Ë¥&>Ës>Ë29>Êúš>ÊÁ<>Ê{ >Êf}>Ê_ó>ÊaÜ>Ês2>Ê,î>Ê6¯>ÊNm>Êw!>Ê¡“>ÊÆÅ>ÊÛ]>Ë$’>Ët°>ËŠ÷>Ë’<>Ëjá>Ë=%>ËA>ÊÌ„>ʳy>Ê¡£>ʘž>Ê®ª>ÊÉo>ʘS>ÊrÅ>ʃ‘>ʪ->ÊÎ(>Êíy>Ëì>ËC>Ëšv>Ëž>Ë•ß>ËqD>ËSâ>Ë=“>Ëö>Êù¸>Êßã>ÊÐ>ÊÑÄ>ÊÕ >Ê׆>ʶÕ>ÊÊÄ>Êïâ>Ëø>˵>Ë-h>ËFh>Ë}T>Ë™ñ>Ë¥5>Ë„W>ËŠY>Ëzh>ËG•>Ë'£>ËÇ>ËÌ>ÊóL>Êæ~>Êå(>ÊÃø>ÊùÀ>Ë >Ë.L>Ë+‹>Ë?>>Ëg>ˆ»>Ë•ó>˸|>˧1>˪P>ˉó>ËK->ËE‹>Ë4Î>Ë8>Ë>ËÇ>Ë@>Ëþ>ËA©>ËL>ËVU>Ë?®>Ë]>Ë„Q>Ë™>Ë ×>ËÎQ>Ë¿A>˳>Ëší>Ëy<>Ëj >ËV&>ËQ!>ËB7>Ë<²>ËBÛ>ËIÐ>Ër>Ëq[>Ë{/>ËgF>Ëxy>Ë–î>Ë­F>˼·>˾œ>˰×>Ë >Ë}>Ëb¹>ËV;>Ë[>Ë\>ËP8>ËEØ>ËB>ËMÜ>Ëg¸>ËhÓ>Ët‹>Ë>ËL>Ëm7>Ë‘#>Ë­#>˰A>Ë¢Ó>Ë“{>Ëd)>ËCt>Ë<š>ËD2>ËU_>Ë6>Ë!>˺>Ë 5>Ë5Æ>ËB®>Ë\>ËbÒ>Ëi¨>Ëvk>ËŠ->Ë¡>Ë¢¼>Ëž>˶Ó>Ë^Ø>Ë)Z>Êê×>Ë¿>Ë‘>Ë>Ë<>Êða>ÊóŠ>Êÿ©>Ëî>Ë8Û>Ë7Ô>Ë/é>Ë_Â>Ë|[>Ë>Ë’Ÿ>Ë9>Ë€˜>ËG‘>Ë>Êô¸>Êî!>Êà­>ÊÒ/>ÊÀ>ÊÄ>ÊÄ–>Êä&>ÊÜw>Ë>Ë“>Ë!·>ËGÊ>Ëb1>ËXA>Ë€}>Ëx>Ë`X>Ë0–>Ë7>Êáì>Êɘ>Ê­°>ʈ>Êkñ>ʉê>Ê‘>ÊÇ•>ʼ±>ÊÕ”>Êð5>Ë3>˺>ËE>Ë/f>Ël@>ËJ¥>Ë3w>ËŸ>Ë>ÊÖÅ>ʨ4>Ê}g>Ê\´>Ê:¦>ÊW>Êj2>Êš6>ʲ>>ʱ0>ÊÎ>Êæð>Ë>Ë8¸>ËPÑ>Ë^Å>Ë=>ˈ>˃>Êàƒ>Ê´~>Ê|`>Ê,0>Ê-!>Ê!>Ê,¿>Ê3­>ÊSÒ>Êq@>Ê‹b>ʹ¸>ÊÜm>Ë(>Ë->ËBÇ>ËQ >Ë%ê>ËJ>Êïu>Êѱ>ÊœØ>Ê]Ï>Ê8>Ê :>Ê/>Éþr>Éè^>Ê)>Êiæ>Ê~D>Ê£ú>ÊÏ>ÊúË>Ë",>Ë@>Ë;>ËÕ>Êü4>Êî=>ÊÌà>ʆ>Ê<Å>ÊÇ>Éð½>Éæ2>Éâú>ÉéG>Ê%É>ÊK“>ÊVd>Êxø>ʽ>ÊîE>Ë>Ë9Í>ËB+>Ë2*>Ëõ>Êû>ʼŸ>ÊJý>Ê`>Éêq>ÉãØ>ÉÝ™>ÉÓo>ÉÙN>Ê×>Ê/>ÊNj>Êzñ>ʱ©>Êáƒ>Ëó>Ë>>ËUs>ËG•>ËH>Êã >Ê¥}>ÊW6>ÊŒ>Éâ>Éëµ>ÊB>ÊÌ>Éà‰>Éó·>Êý>Ê? >ÊtÎ>ʪó>Êê>Ë$>Ë^Ö>ËZi>Ë:A>Ë„>ÊÞÁ>Ê¡ô>ÊQó>ÊY>Ê?>ÊÕ>ÊJ>ÉÍW>ÉÛº>Ê>Ê!d>ÊQ>Ê„¢>Ê >Êî$>Ë.˜>ËU¬>Ë_x>Ë<ä>˲>Êââ>ʦz>Ê`´>Ê:›>Ê&>ÊJü>Ê(ù>ÉäÛ>Éël>Ê>Ê"õ>Êc>Ê®Â>ÊþÀ>Ë*>Ë+§>ËUù>Ëc>ËC>Ë9>Êí4>Ê¢Ä>Êy¨>Ê]ä>ÊM•>ÊnR>ÊD•>Ê ¯>Éçî>Ê4I>Ê_[>ÊT>ÊÎ÷>Ë’>Ë#>Ë6>Ëb>ËJP>Ë:1>Ë+,>Êÿa>Ê·Œ>Ê¡þ>ÊŒð>Êu4>Ê„æ>Ê>ê>ÊBC>Ê@ >Êi¸>Ê>Ê¡&>ÊÑÉ>Ë>Ë$c>ËSð>Ë|>Ëy>Ë]:>Ë;Ä>Ë@>ÊåÒ>ÊÒ%>ʽ’>Ê¢¨>Êô>Êcœ>Ê„9>Ê|­>Ê–¤>ʼ“>ÊÕœ>Êø>Ë >Ë-n>ËT>ˈd>ËÞ>Ëss>ËT=>ËT>Ë&Ÿ>Ëo>ÊîJ>ÊÚ=>ÊÚç>ʹ>Ê« >Ê®Î>ÊÎI>Êí>Ës>Ë!9>Ë:þ>ËRÉ>Ëq>Ë‘!>Ëð>Ë€ó>Ë~>Ërc>ËK¡>Ë/>Ë¢>Êú >Ë+>Êö{>ÊãÃ>ÊÛ>ÊõB>Êò >ËÕ>Ë;q>Ë[À>Ër˜>ˇ§>Ë›ÿ>˧Œ>Ëœ >Ë­Û>ËE>Ë_P>ËKÂ>Ë2>Êÿ¡>ˤ>ËØ>Ëf>Ë·>Ë+3>Ë$^>Ë+Ï>ËR>Ë™ô>Ë•É>Ë›¹>˨÷>Ë›O>Ë©&>˧>Ë’>Ë|q>Ëj«>ËVa>ËA,>Ë4&>Ë.Y>Ë=]>ËFY>ËV©>ËP›>ËDß>ËgÞ>Ë‹~>Ëž>ˬt>˺?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€*_?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€M?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€â?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Î?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€e?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€]?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€8?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Þ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Š?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€`?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€õ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€KO?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€K?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ Ä?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€%?€?€?€?€?€?€?€?€?€?€?€?€?€?€t?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€>?€?€1¯?€Æ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€„ ?€#?€?€=ú?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€=q?€­?€ é?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€\ã?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€^?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€½?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€$è?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€9?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€A?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€rœ?€ ´?€#?€›;?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ê?€?€ I?€ª‹?€4s?€¿¸?€3ý?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€%N?€?•?€L?€ðì?V?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Œv?€) ?€9×?€0™?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€×?€?€?€>Ø?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€>?€?€?€?€ê?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€­?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€$Ï?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€õ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ À?€?€?€Fn?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€m—?€î?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?‡Ÿ?€.º?€:?€?€?€?€?€?€?€?€?€?€?€?€?€?€F¡?€n?€˜Ë?€;ù?ÏÁ?€éœ?€¿?€Û?€1a?€?€?€?€?€?€?€?€?€?€?€?€©?€?€5´?€À-?á?„e—?€ƒ½?€=Ü?€?€?€?€?€?€?€?€?€?€?€?€?€?€ÃÔ?€o‰?x°?€û‰?‚pñ?‚ò„?€ ?€ |?€!?€?€?€?€?€?€?€?€?€?€?€?€?€?b¨?€ìz?€ž¤?€«?€ß?€!Š?€?€?€?€?€?€?€?€?€?€?€?€?€?€ÅÆ?€?€ª?€[.?€4‘?€}ø?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€‰?€?€×å@"¾+?’SÝ?ƒêû?‚Ÿø? :?€…?€?€?€?€?€?€±?€?€?€?€§±??€Ìõ?ƒ¾?“ƒ˜?¥õ*?–³˜?„¿ë?€?:>?€è?€?€?€?€?€?€?€?€¨?€?€?€?„’„?€¶!?„‰[?ŽY~?‚¤I?€?„}r?€;³?€?€¢?€?€?€?€?€?€?€?€?€?€õM?€ Í?ƒ?ë?V ?‚6l?€Æu?€?€?€?€JT?€?€?€?€?€?€?€?€?€?€?€?€?€¼ß?€þ?ó³?€&3?€–ö?€?€?€?€?€?€?€?€?€?€?€?€?€?€Æ?€?€?€[‚?€e?€?€ i?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€;=?€?€ s?€?€$¤?€?€?€?€?€?€?€?€?€?€?€?€?€+?€?€?€?€?€»?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€K?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ª?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€|?€â?€?€?€;?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ÍS?€"º?€ù ?€?€?€ˆ?€?€?€?€?€?€?€?€?€?€?€?€?€?ë?ƒƒ?Sg?‚+€?€&?€Ni?€?€?€?€?€?€?€?€?€?€?€?€?íù?€òä?€“Ë?ƒ®K?„ê?ƒKó?€IG?€?€F?€?€?€?€?€?€?€?€?€?€’?€'U?€½?ƒ=?†­?–W¸?Œ?ˆþS?!“?€3^?€$±?€N?€?€W?€?€?€?€?€Ý?€Ò?€Í?‚}å?‚@ƒ?ˆ°¼?‰(©@$žV@&Y.?ˆB?Ž@?†±_?‚t3?€?€?€?€?€?€?€?€?€1?€6|?½„?€/.?€?€?€?€?€?€?€?€?€›é?€K)?€Ð?‚Ï?“Uü@ÎÕ@ È1?£Í?“?‚ß®?€  ?€}Ž?€FL?€?€?€?€?€?€?€?€E?•·?‚ˆ??är@ FûD$lD­µC@G”?žýŸ?‡ï-? ð?€>å?€Y?€"P?€?€$Á?€?€?€?€?€H?dç?‚ó±?аŽ@¢MDM âEe½Q@k?›ü†?‰sì?‚C?Q?€Ø?€?€?€?€?€?€?€?€R:?€n^?€m›?„gö?¤]?ò¿\?ï”?¡³?Šò‘?€”?€?€ l?€?€?€?€?€?€)k?€?€ãO?ñ?Š¢ ?Œš˜?£æ¡?Œúù?™ÆÌ?K?€¼?€¿º?€’–?€?€?€?€?€?€?€?€?€?€Z?&œ?€ã5?„¹p?Œo:?ƒ–k?‚ða?•‰?€w/?€21?€ ?€?€?€?€?€?€?€?€?€W(?€ì?€?€?‚(«?‚g#?‚v?€F|?Çy?€) ?€?€?€?€?€?€?€?€?€?€?€?€?€˜?‘?€Æ&?u‚??€ÈV?€#M?€¶?€?€?€ ð?€?€`?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€+á?õ½?€ ç?€¡?h?€?€?€?€?€?€?€?€?€?€?€?€?€?€W?€?€?€Å?€?€?€Jõ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€>v?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€I?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ô?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ :?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€i?€< ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ -?€(¢?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ö?€?€?£?€?€p?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€d|?€ 8?€I”?,?€Ç?€?€$¯?€?€?€?€?€?€?€?€?€?€?€?€?€?€B?R–?€YÏ?f"?€b*?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Á?€?€?€$?€ Ê?€^ü?€Í?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?X$?€?€?€|G?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€/„?€?€?€B¸?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ‚?€?€?€?€?€?€?€?€?€?€?€?€?€-H?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ ?€?€?€?€?€?€?€?€?€?€?€?€J?€?€?€?€ ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€OÛ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ Ž?€?€?€?€?€?€?€?€?€?€?€?€?€O?€?€ ?€?€?€}õ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€/£?€?€ù?€ˆ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€'?€?€°l?€À?€?€ã?€°?€?€?€?€?€?€?€?€?€?€?€?€?€A?€?€?€D?€-N?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€‰?€n?€?€?€Eq?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ F?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€&¢?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Z?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€&?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€v?€?€?€?€—?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€w?€?€?€ „?€?€?€?€_?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Š¥?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€¿?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€q?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Ç?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€±?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€4?€?€?€?€?€?€?€?€?€?€ f?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Î?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€þ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€,a?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Tø?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€-¦?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ï?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€Õ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€9q?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€BþÉAo„•C5ôâBºsC4=½C>cçB%==@›ÃjCTö¼B=·ž@™ã@¡­6B{ùBC ÅÃA÷ã[Bž¤»Bêê±CpmIB„þ@Â4™@øwkBZéB[®’CWXIA”ÞwB†“vCb™ä@Í AIaŒ@No C2ôAv}B¸­C$C:F:C..§C6˜åBÂ%mBRÙA >ðAÃG>Cj•ÆC'îŽA‚,»BäìµA„ŠGB^ö Cr TBœ\3B-Á·CQ·™@)A}@¥B‹Þ9C·õYC…‘B¾ÝŒCt Aº€IBÏgˆA§­+BéÏ…BÊ C$öA+>rBƒ^A)+íBéËÒBÞ]BÎVÿ@lB ^÷BÇqAìçC%+™@@CMB¢¡¿C–¶qBB”fBÖ@5B zêAïg B?ˆCz AR|2C² ³A§9n@C™ÙAÄׯAÅîûBíRÅAŒY.Cg¾Â>t‘ï<Ý”CiΦBÊ­3@ÐâAúsœBIEÎC’ÜÛ@ÞóÅC ÓC€e}BòKÛBØñúC5ïÍC‰3žBANìA]9ÌB[ÌúA4B×rCõÁ@âŠCÿ¿A„=A›gUBŠž·C–þPB¸iÏB·ü@K™BR¼ B—Φ?£ß}Cls‰AìÌ>˜mÙ?‘ÅBXz B“H®BN‚@û5ÇAœið@—YC=-AÞE@ÆKfBjÿÊBŸâÒBÜ™ÛB·^GCdXl?è”4B‘rA½¦[AË­B¨¥âBZ[yBÑÖ±BÑL5C%ÛBÖ½çC8HA­zA"0Ô@¥ÞŸAͺBO]B›AþBÔn#>bòÏBqn@á¢,Cu8qBxGæBî²½AjT½B'°Bep’BñÔCE›…BW¯BéAëêrBë{@…nÞC×mC¬WB™+CNõÊBÁ!B›™@¬¶ BU B€OƒCH£PC÷HA°ºBÙ0@çG‹BIgÆC½ iB9mª@P·ÌBüÔ @¼³~?R\2C|þ£C-MB™ˆÛBÎóC F`B{͘@¾©BTXÔB—WU?.Ø`AÓÌC^mkB÷ÈKA9•B•‹Aó#oCn]gBtXu>êýåB;´ÒB‡HúBtÜCoøÀBHŠAüàBG·¹BÉÂôC™‡C7ŸBýyBÄB´”¯B½`Â@–õ/B¼AÉ3”C[/ŒB þB&v]Bì7A–Ë B´ñcCM‰UBòSžA¸¯sAãð±BTóïClÒ'BÏ‚ÿC86ÃA‚^3CVè”B>¾AàBBR†‚AçËMAˆ¤eC) \CE@¼BhDCB›³†B›·rAÆ Y?7þAUïÀCÏ@BœdÝBöýB¾´CV– BfÊ–?³??—9AT´LACérBÔÕBÕaº?!™,A´†BÑbBH;QC3¨eCWB¤¼BÖ™äAæÎ¼CÈ_BߘB¢T£@ϲLB¨ŒC7 Ç>½øA¿GQB¸ ‰C î€@ýµ?¤Œ@÷¿¦CTíYAŠÔÓA¯ÕBÜ@ôDA}IC'{¥Cž«BÑ ü@¿eC86B¬Â¸B…ú,BF†Ü@ÃnñB*ÙBgC5©üB„dBèõ˜AÏaCB VA·wÔ>¡4åC‚glB®-ÊBZÙpAºŠ&CxsJC¼y?¬P@™´oBŠ|9B?["B®§fCÂŽ2A3Æ@>‹Aù>»ÇÿA|’ A*½ûC02BCh*mAШSA2q@Œ [A…RvC$óUC^m2? æ‹BC¢‡C_‘VBÏ—C HÁB…ÍB62±B‡?hC2U^B nQB;»]A€9BXS,C.ALBî„ÜBl|–BŸÝá@ð>gA"æøC0jA§öÏC6 B‹3¬A;Ó A¼A;úA3æB‰‡4C siBBÜ”B…OBÈiôCH®/?ã™A2±A‚}4@‚¹?ƒ¼lCH „AÄw,BŒÓßB/ÐBAÒväBû®@ðâ&C*a­CˆÍ@Ãz?÷C¹>úYµBcéÆ@ß¾JC¼µB¼/QBŠØ7B>üAÂjCF=C)®pAŸ¦ÔB¶z›B’4èCnãåB>%Û=nwAB§âJAåvAB€ØñB¥TA—­gC9CeB^B®ãAéŽ@ÊC¡Cu´C^DBà4AÉŠAC °‹AEo‘CgC(eŸAHÇ@„s_A²éÎCÍ£AUC C®ˆCBŸ`|C‚ ³BN½(B§ðCPDBÖö;@Û~@çB‘y^C-š‘CDãAÒ@A¸Ö¹A«šß>µÃ&@Eè­B[ÇC]D¸C4íBDޱBñ"BaXÖ@ì¶¾@õiÈANêèBÎÆ•B¼|×Aü©0Aê’>?G àB„^C“”ÇC —ÏB ¿B‰-4C¢…ÌCëA૳CjCCSÿAžÙxBoYBÄ3BZÔ7=©0AÏÕSC—Æ BõQ}@uþhCî£Cf0A¡BzBrú³Av:A³Ð@¤6ÇBŒ` A¨kBDCiG3B;5šBTþ Aø¹ûAó“B¨j=C:0Aù<8A­¯:C•øB›ðÜC5ΘC+ï#Bí+ØB(IQBË8dAŠ”A˜>IBǯ‹BÇqCŽ8Czn–@mM>î¯\B™c B¿uB‡¹C-#xB·±[B"A¢AÜ#CZagB‡/4@€7A3ëÊ@l›í?S¡þBï<‘B–ÑAçTŽC9=Pùé=ŒåxBÝŽÑB—žB|ƒB°é¸BdYC? A 9ºC(ø{BÆ@€>¬¾µ?ÚòA÷ˆ/B&ä:CT­¾Cr;AôâêAK'åA(­ZAóLB—Q1C¦ÊyBñ¹Œ>ȇßC$}"C+a$C YøC+RB™{ÐB_ŽBÆÝ B™²‘Aë®B%àC/Ôr@s??Þ$@ë~æA›B;AÚÙ)BeB×bC1¨AŠÙApÒ˜BFlAÝßA¦{…@/Û'>;œCë˜ÀBYï¿Bs¨AÎk@%ÁDCÜÃC_SpB©T*?c >AqvNCvÕˆB€Y‘AÖï}CáC’ýB{XºB8½á@© ÅBÅÑC| O@ƒ^'?Ž BI.!BY¹éC‰ÐtA’ÝTBÖÀéB »#C ÒC*eß@ûµÓC{,|BΛÍBÂ) Aiý@¤•ICY½xBïœAM@®=>¦yæA²þ1C©B@`›BΖ®BýzC/B¸ëàBûµBN÷C$•æAÉZ6AôîB“ÙûAn‚ñBr̤C,FA“^Ù?¿]@Byª CŸ­C M>B‘[‡C1sC= ºB5ìGB€z*C‹C(¢õ@þwB_RCxÁcC3cÂB›°C,ßêBVZB@òBI™”C°¶A‚¢CI5CÜÈ®Až=KAç+©C´˜Bèï[A’ˆB­]BŸ1^A(E(B†y\Cr!B• ƒ@y“C£IC`²JAÜ CPVKAdlCŠ«ãC9dAÁ‚|Bl™?¿¡? T=B›‹Cku(@2¿hBü–CšÍxBÒC1vÁCl†{AêèBÍ¿9Bg¶OB†õXBßµ„@›Kv?àÎùBíÌåCƒ)WBZÖÂBÊ$BC*dCœBýrxBЦPBÓ¡¡C/¹B3 Bj£B¹"BãtB2A@ÂB¼aLAá‹B€N÷CL/ãBîaBº¶HBcK=B*NœB¬"™B+ÜBùÑEB¦`÷@–¸¾?Ê›CDɦBâECI‰ËBˆïBt‰´BÜ@ C£C=®DB¯®ÿB¬´CŠ¿[CU 4@³×‰B¸÷BùúºCmhBTA“á¨Blº¥B©¼,Bë/ C‹V}@HQBX‹wB*´A̺CÏ·BëðNBµrCQÿo?'„»@“ ?>L"Bš©qB‹›UC=gBäÙ´AØD÷A­È*C…ð6B—äŸ@1çuCwˆC®B(1ýBfüAÄøsB»ëC£v5BˆžA¸hìBÃzAe…AWû#BÇÈ$BÛ¦A§íÛB£‘B 45AݪB æICVý C!çØB«H½@€ËˆBFªsB ¨¸BvûaC;ï±B0·AŽþ—C¾ò´A€ œA¶fdBOà@¿}çB—IAC‚Q{BJ¯Þ@…ka@x:QB¬-ÿCOí¨Bl'_C2çsC·ÈjA‘bòB%±-C oÎC‡úIC3WB§‡Bê<“B±/³C‹dÑB¼æ?V…B h\BOö)B6g‡A—ŸèClùBåØŒBûÄA„§úBQó)AB˜íC,7CA UB`šôBj†ÑB=MõC&ÇZAë }CµÂA™ÂÒ@È%Bº ¶Cf@BédÙAƒÉJCæYBŠÜ0CÁ²B¢»@Õ,CXàAÅAÓ\,C¡SC<€,C¼mCÚNBñ_uB—þ?B„ƒC“Í‘C(+AòXl?¼žCBå™C8è¾AºàAŠA5A64˜A4ÝýB$ÌCŽ›C;€6C#b(BŒSCQŠ9C—OEA2pBØ©îBNË,Bi˜B­ 1CDöÇC*ÇPB¡GhC;Ÿ‹B¢c1B™)4C†Î3BÂ9]AÒIPCQÒB£*†B nÔ@}¦7@ëyêCš³AŸjB,»ÃC§„xC%±BùÅTC‹ŒFBÜoEC)´¾C¨nëB¥ßÒC@¸iB.:C0¤-C6kÙBa±üAÓCËÆTB’ØB›C9&%B°ßë@ÛvECt`BJ¼YC£užB¼"C ’:?$=A††s?ð7‘BÝA Ä2C#µ~B¤>¥AŒºéByp>ÞAÝ6„C‚*žAņ@Ú‘.B*C„ZóA,¦@gpBÅuÃC«‡AR³îBGð­A @R,A"³=AÂÌ~CgvKA9‚^A¬GêBnÌçBzÈC•o@µ>×@qAüþaCv¸«B€ì@@áSúBŽn^BÉ{¡Cgª5AœàmBJq/CdèsBu³«>Ú¨œA¿®¦@ãĺCszB ¬GB§¥^BŸ¦ˆ@ç™ÿC9ùMC#gVC,é4BŸ=ƾC"CŠû~@\¤V@ÿ³º@Á»€@‚Ï·@´)™C{E¿Bª%@3š3B'…€A»,¦AªŠCŒhBΦ_A×DAqîsCq<BP£)B…cÖ@(‘qBGgB$™ÝCr1c@á?m¢A‰¼¡C{4C*ZB¼NBÇA·B<µKCu¡JBí÷Ì@ú@ãA½ßAxA3[@útB\-ªCyáBèNB`JBZù’B*}êCw-B˜@Ñ®AφvC>HBºœ1@*¿oCÈØVC5£A.®ÍA!kõBXºBÃÝC!±âC;oÕAËt±AÙgˆA‡¡BùŽ%CLl?@”ÈCmðsCPùÙCz\BÆÝC0žçB­~_Bí§íBÅÐ Aü­ÄAéžBø‘Cµ:@UTBÁ‚C>&wBd|C¶ÙwCM)¿C^C>B\«CCo‹B"ªBÛîC±'C‘TAÂó{Añð~Bjç0CÜÚmC"2¡C£µœBSÜÜCÑy$C`/ßCªA©¤@C¦|QAâ•ÖC nâBijYC5rCôB‘D@WÉ¡Aà2YB'¿›C ±C½5ïB›:CDŸCncÇC§[vC÷”öCéfCP¢~Ado@J;1CcóYC!YCAÄïB¯ÕBî LC!qwB‘NC3žaCÅ ÒCIÅ´C–VòCú2C‹]0C·À CF…Bé÷zCL•¤CBÌÌBµ®ÆCU8B'2½BŸÙ4A&ô5AåÔêC;×”BkBÌCN>ÐC„©JC†cûBÝ CN-eCs´,Ct BÔB×2€AT=BäÑÓC{ vAw)®??¹B=.BÙ¹C;¼B?a…Cû¤)BôBÈýCTZÕCn•C¦íRB·äéC-±CB¶DBB†îŽ>ŘC>çÕ0@ ìC’­C@<1Bí½/Ai7NA¼bìC˜‡wBÞÞC8ÏAňAÅq¢C‡ÏŸB¼ÊA¦xC”zÅB¿ó9B‚£‰B¯vÔ>/\?Ã6!@…6˜C-|YCˆ´ËAS¿YBµWB»³C‘cCˆj{BëH÷C B}lqAÜçYB¯ÕJC¬»ÛCö­B]º>´&BaúC¾XýBÞ@º\BÌyC&5|Aò6BJ®ŽB‹ŽüC±üCP‹Bû…B[¢ÞA®§pC5|:?“ÌÖAÊÐ+AA¿Ú@XF4C{†wB€i/Ad†BúV4BÖÄøB¿ùîBaàB°WJC±’èAUãB ™B€CB÷ÔÓB±ÞßC^qûAi`ÿ?=¸@iÝCXƼC:wP@ÑÉñ@{A ÕC8°¨A«úÙ@e³AY]TAÎ/ÒC¸˜B!ßC.ZB[ÌoA‚êÎC%ËA͸C ûBÎFA!§óCE7BJݤA‘C®è±A9g|B\ËAÛ >A KsC¢o½B€…IA¹ã¾B†ánA[œB#»/C'äA[›I@İBžqX]C®ÝC2ÞB–ÉoBXMAGÙŒA5SØC{-C+©'B8>;C^‰C(¹@ Éò>Àç“BNWÿC\C/D&B`çäB¨gC¡ŸLCuƒBèBÌCWBÕ/6B¾œA¬–CfYBàÛAÃêÙA)ç¯B=n…BÁ|êC2†»C ÿ»Bª†!Cd½QC¯*@Š! C ¾B¢@"xmC‰;B´±BÝbéC#Ò¥C…¦ C VMC^›–@µ1ì?Ù} B®…ÃC1æÌC"2B8dfB ÀÏC¿ICIÀ*AmÐöCAáBÐ×BÀfBpOöCüCgDT?§kñAŠ»>Bsìô@å3ÔB’C³§&Cr Û@IÅB·Ã;AŠîJB® ‘C¾^B’wCàeìC-±XB«Â¶AË%4B«FBwñTCb^õB⿦BŠÖËA•LYC!ÚœC*rBµÏ{CPÊ{BŸ:Î>‰ºýAfyjB„µ¤C»CYT½Bl{B„°Bû „Aƒ B¨šˆC¦@^i=£FBp±A³|dCAQoB.±B¹=Cá(ø@5ýÌA3ä]A—#B”IOC2þBÖ žAíþCàéÍC@A{=sŸñAÇU&AoÞÛBÇAÉúzBÉ9B²CJHÙC„ðBŒ,!CüdC4•ðBÞ‹TBØ“A¡8NB;z™C½7A¥¯ÔB§_ÃA>ù%AìßEB—ÛiC!lâD´wB–OzC'îCŸz"Bà@!CP,C6bBt.?Ù fBH­“?£YNA×öA&~TB(EaB%B §ÓCÿIBǃ±B™ÀB¶áC„sºBœBO6DBƒÞŠ@cAÞC,t¿@)âC} eA¨åëB‰hC9oAÅ$xA5 B;»øC3¢B°&BIÿ<@ôp¥Bïä¸CYíBe·÷B—÷%BAÂAwÞC…ƒB».AÅÅAXÐCI³ÃCIAMC A‚oÝAü²AõŸÀC~¨Û?bëAÙlA<?wA–kQA†ÉgBžÚªCDàêB†’£AQ×Bg>îBºN„B QBB#¸æA˜D±C™.BƒÛÉBŽÚAž A (C .tCGBªx—B°_RB{]aAÞûÅB´!BÕ6¹=V·@ðÜA·¶£CL&¤C¸r•Ba™AòZB&COC£šBáBÖBÂg„AæL{C^ÿ?§ÅÇBäHCÚUA²zxC)CBTÝAheÝBuôC‰àBÎ`ËC¨÷ÝB™A¬‚fB–C9ŒC+»@ÜÞ%BtCrƒSB¡^¹AE¦AëìmB{¡/BʱBÒ…#B¿2;AÜgfCƒg½Ch€Bl“ÿCudBBo–Bâ¤ËCRvC@7BD¢‰B²ÿzAAù¸C­”jB×W®BG CǶB.Ø{C"æñB·ÐÕB6Ý»B_/B= C\7‚BÆ {C«$ÑA—iAÃ!æC§äðA“™ÑB eAÌhjAÕ<ÏA{ÞBBúCÞ€ŒAŸÝßB6lÅB|FCdpHAþŒPCk­éBxãÒCD®DB§ÔB¯T˜BE«ÍB²IQBÈ]ÕBxTpCNèÓBª-vAwUBä+‡CL9ÂBÓV£BÑ}WC9DB¼IÂBáÒ-@‰x›Bj@ÊC ©=C© ´Bçù`BŸ¢ïB!Ä”CC!²A)ÀAî0‡A†¿6=hsF?ÊnBÒø]C‘ÖnAŠŒ7?¢¼?õ×Clñ¤A¹ B‡ÞR@ÕË@ÖëòC°XIB;C +(D (B©­±B”y.CxìWBbƒB=äBiÜB¨ýgCW{óBõ@¶ífA«uC{qBC/CXŒC ›‘B ýÇ@à0ì>5µË=¼ê5CEòBe›F?ɪ«BÜ2EB„ ÍB@8AcøÊB Ñ©Bÿ ˜A¸umB¥™LA@BÓCvU¯C+þBkÊBŸ]AÙ ŠCi#B^ÂCÁCªšÊC>rC/Bp@úC¢ï!BóPBŽ“ÃB~YCbBštPATm"B…ocC3YBA C}{OCc!«CÄÔ > ÃÀB´“„C€ßMB1¡BêµAÿÓÍC;¡B€&VBá­oC7ça@0BÞ-žAÕ*AÊSzC±A¾½¯Cš£†Aq2DBöCfZC´ïAjã—@ZŒjBŒ BKö¾CÕïB9BÊAÁì9Bt8Cš\BtÝY@fÀg?*X·B¬²áBéÐCii¾C×@A‡©üC7”B–ÓNC¤MBNhgC°„C•­aA¢½C*DªA.#BuÎ=BtO!Aü6ÄC}>A•ã‘BW9ÿC—x°B£_…@°ç@BÃ¥@ÇèBÇi)B²¯ÓB¡%®@ƒBß/ðC3ÐoA¿´gAâêK@7A=B8ZB_5B£|ÖBчRB»ÀÌC )b@ù—w@fóªBþó‚BÈT÷@ï°Bz¯B 2CœB¼ŠB®X&B­‘¯C!žËC {?x MBåBÖf C£€MAÃe B€úBWeÑCWiC‰vIBïB+§B¼ô|@ˆ!ã@Ìe CCT£Ce²nAïëû?ÚPNA¸a‹C‹æB¿áèBʾC|3C2ƒïB3&Aç²´BÓ¹,BÛÖ5B›¾BéÖ–Bø%àCE¶iBE#B`ÓâCÝÙ˜BÏ#‚C˜æC³§BÕãCÕK^CLÕ|C†=–BîW`C©ÓÑCg/CPkC†ºC%šB¦â²BîU‘CmBFC™C„êšC ƒBHxCì¨"CºÁA’B‘}lAçŽA²æCa³AÏq4BŠÏBçfaBF¡ACC9AC<úeC›ãDBìAU |B·(C`M@BG_öC Ë C BˆgBÌŸ&APônCßIB•ÁBCÖ Bõ}@“ÜäB?šYC?Ø®B™;)CFÕB­úCÆÅíAƒCˆB¿Þ|C£3 C&rêAÀÆ€BÕsÐCR&¹C 1üAÖÂA“ÊùBýf¶BæeC/´ŠB‰LCXŠÄC íDA„«@àíDCÅC”SqA.çNB¾™{?L ÅBÒiC€íBÝXªBíe8A– ÉChˆB±`C“CûKªB–ûA˜¤¯BÓ©‹CO©£CÉÑBºÂC3zdC„–áC~ù?þw©>3J@ËüwC$רAm èCœ)çBE,óAÒpBþ³ÍBÒ‹âB#xLAäœB¢øLAä-nB *ECÒ^C½™xA¤NkBRt‡Aõæ]@eõAæ¿}C„¶ÒC^ñBp¾B E)BägŒ@¨xlC-íÎC€¡«Cª„•B`¡=C‚éBòQCFçCpdB`W:BÙAC j@xM¶C‚ƒ(A“Õ6C †áCoÉAž7C«ÿBu C@ˆB™FZC ?.B8`Í;6yëA.ŒB×ÍC’FJASóöC$!oBý‚ÆCžÞ:C(¶ECsB!‚qCtiAÅä A½òÙ@þùúC@U’CÍ9Ø’?©€ðA 9–B§[¦C±²´AÕ‰BCMB´øÖAVÉœCm³C9PB#ÒBžHC™ë B•óPB÷}CQ<%C @?ByãA±AïT»B—¶}BÒ’AN¦AMd Bó´@AuCBÌ@ôw™@ã Bî-”C>~@®M+BkºCz"Bµ]·A~çB22@‘ž3B z”Clϲ@§A¾C&÷ Cw4óAÖÀAðdÊ@¨îÒC*’AñËBÅDB‚T.@žJLC¢t<B¿ÖB_¦B|iÍ@ÛFjBP^ïBÌ)BÁöBoùÝBžÃNBî@ CM’¶A5cBVuõB¨ÍC†-BªLBõkgB;<CmôuCõ@þ4AtÍÞB/¢ BGùŠCW¸êA:¥ÊA@ C…C]ÍÆB¾ÎÃBóÉ•CRÙ­C“¿@‰­ÃCûSC0®.BDu»BÒ‘˜C&ñMAÖ7÷B<ÌXCoºBKGIC±†rA¶»B÷ÈC°IüA\?íB 1)B=a0@çýCSN­BÑ¢OCb6ÒBÀü'AØ+ÏC§A¿CÙ$GCýC¹JBƒ;C¥h#Bßô0A‰?ïCjïcC`£½@¦²šBÉBˆµ8B’ ÄBCÿ»AŒm‡A‡µ®B|­\?}‚“?‘ý CL|OBH FBNù*C9‰åB’:>Š{BŠóÔB×TþCLTˆB ·*Aˆž@¼u¶CC¦BÃVA(íC‹ðA­Þ•>µgâC}ŒC8‰öB¥ÙÁC·>ÔCšu‚A°AjCš‹¥BB…´Cƒ`A]«@[B8>SC5ÀBz#C–"?Ch°Bi¨äC\ÎWBkÒ0BJ›CqÞAÈÕBy«@ª½¨@›ŒCÚ /B ý‚CL%UADæÅAXCWšVCI#šB xsCìBª_Ö@‘B•àWBeC8²CÂKC§bBù7AÎéC¹ÁÇBtèA„£#C)dCº3)C "B]6–B"~OB¶ÏyCy¸tC5Ab,T@€¼ÔCk1aB‰EC–{ªB€BݱìAŒ{‡@£´×BOíC`SC+QöBƒRæB©‚ŽA‰nwA†ËìCXLB©Û—B|ü’C`ƒnBŸî? eœ@¡‘ªBû‡*B²œøB¤ÐÖC²®B¾CPJCEô§C£ÅYBÙgCåVB XCPHðB'9#CùûCnˆÀ>_ª×>Ш‹?ƒëÎCˆÔ¿B¸ÙÀCKKIC˜YCõòC|B©œ CxñB铪AnÏCÏ—AD5ÀAÔÇ·Bú^@JU>C †OC€=éBßBQß´C¾®#C ¯CÂ%Bù…öBùÕK<“úrBh9°BüÊfB¦BexB¦Qk>^ B…²ÆC«kuB¿_õB^¿¢B1B>—Cg%½B€A Û£C¹ òAf¼B)41AÛ A-c¥BàÑèCQ–äC6Ks@2·A¶P>‘ø»B›ýgB“à‹B圎BÃquBâ,|AtîbAÈö8Aô€!BðtœB³2cC‹ŽÞ@öðl@åN¯@WtÒA­ì¿Cj¤¨Bú›Bßþñ@ÌKu?]<×BN›¬CKµIB'CôŒBŠ˜øACµbBf*@á¯ßCaK/=Ô×|A~­ÚB§Û¯AÒ?¶nuCÅ–nB‡îA=¾LC9ìuBž²C[v:Cè«@T"C·wÀA«d·@äVA‹€BçZB˜9­C·rABŒQÚB–‰fBœŽõCBašAèo†B„{æB ø"@¿N÷C^¬`BàmMA­„B†×6B–Ï£CcŸCÅŸAäuCA>ÚC¨ôAêô@C™ÃC#_Cså·A½*AaÆ´@+ §@Dý@žÀ%Bæ¥ýBÒ¾¸B|îªA¶¦GB£»áC¶:ßBFÚ»C*ÏÁB0îB\1KC€²ÌCËäB/zC&C29B:-B”WÜC¼fêB$` A¢@ˆiuB¦‚˜CKûCy-ÁB÷VC9Bã_A‚²C Ý C6(ƒA؉@“¨A…,¿BÌnyB zÏC„›BZøC¨ŠéBä9WC²ÐC&ð'=ŽìB¯6ÍC¶îÛB¨ÉˆA­Ij@íëCq:ó@–«˜@È(AÐÂçC€yšBîH.AäƒBÓNgC›AÇ@Íì¯BúA…B‘ÕC]u»A¢ê¿A„¿B}b9C Z|B—ˆBÁëæC€@AÏh¹B©$Ú@ëæ`C : C+lA£ã’B•JKCùrÐC;>ÐBðX?ìIAô\@^Ö_A,XiA–û¹A¥…fC‚SCË:BBǪ–B̺?AŒ¹é@˜}–BÌáfCåBV)…C Þ,CVëC£ABÁ;xCv† C´OB!uèC>VEB¤$ÊC®¼BÉ.{AÊì¿@ÑBþB*Z±Bz±…AúÐC$í‡A%´AÐÊCš¢ÔBÓ˜eCOÌ“CHtüBD°KCNÖBéTAB¢iCˆ|A@¢'ó@î¡CNWC¨gA“ÍÛAfbj?c•TA a†CeÉsC€CšBŒó–C¦BÆ BÉbNBY§²C¨Ý=AÑmA¾ÍÔBÕ{{BÙÈóC‚N—CBôaB[WcC}¶Bó¾BY¢ÎB.H.CJóäC†;Bí(BGoT?ÀƒBD©QBiªkB§ŽBÏC —Æ@yf›?›Ä‚C¶0´@·Æ @eéqB‚=B€V½Br#ï@µ%RC‡ŠåBÈš@Èø=é˜â@öì;AÀM‹BΧƒBß hB‡ Å?ˆÎò@™½œBxO@¿+ŽCIR>B¡Â4AtÜ@•œBøuÃB ?ÚCñ_CR´v@Gs¿A¬‚HBþIH„OB„/C´BÍåêA1*@Ò½4B®ÔC(«C*a6:ýúÔ? Š@@j÷B[ÈÌB‚6B“éÐCb£vB›Bâ‰Aˆ[Cez}CZf?§”@wInAl8¬C¤ïÞAfè,B‡},BšïŽC)£@Ü~W?ÿìC^ã¢CœpiB½asCtš{C=U BŽØ Bï·WC&¤\Bbô¢C?xýB¦£@iI}C+ç&B4ÆKBÿxJ@‰ ?% C/t³B5B†’ÊCƒC&dCú™AVtB"£B>“C!-nBÀf!BP¬Bj1àC"›oB׳C›ZABÑ=B™Á@îVB[0BžÍyA /ŽB¨XC)|®Bÿ2ÆBeö@p¡ÿ@÷záC¡ucC*8üB¯ÕIBwÒ^C„ËC$* @“<ÕB4a¢C&ÊæAý~ÃBa8@B‹N¬AæRCuÔB äãBÒ+•B×xœB$ë C| ªB.hAðÛ$?l8BÓæ¢Cd†ÙAã"B Ê$B»%ÑB›ÏŸCÍ C/ºXA.H*Bk“C’và@9C¤éBB2î¢>„ÔÃB[ñC )BçeCÚwB²»?ì$®A²/C6­B•£vAµ±àCC”ûCF<™B¤2B¬Z8CJÛ¾CawByëA†g_B¡ÖB=BÞÑBQ€BF@ŠBÖÈ’BîÁØCÝBñnAò~A†)@A´F@ÂÏŠCs$"AäyCëBkvÃBµžA–øAÝBTA…eCª (>ƒ>ÀA言BÁGŒCƒÈ™@#ÓT>üL>„½NA´³µ?|ú:B«´·BkK1BŒt†>üC|›¡C"çAªîBY~Cƒ@ÚA CîC÷³BüTôA*ÇC³ôB•Íá@¶‹>•ÉCÀAпBæÓÀC Éö?ySôCHIXCµØB?1B«üvCEÛBcßíBÐjÀB¼¢C‹•ÕBí0@§HØCA-~B‰AòmBÊäÃB#C–"ÒB¶+@JIA3×Cz„ŸCÔBö•¯Cn,CÊ61B½ÙéB½œCL£ÀA‘uA#”¼BL÷€BTöºBÀÛÑCˆOòBÑ8û@éìAƒB«BœNC3¦sC]éBy¿?Bµ¤:C¤[wB„vÿAƒç|@¼9žCPFuBônA¹±óC)ó´B6‘/Cú¬^C õCF«NB‹ÐßAÃã0Bî–šCA?ÀAyõ CøÍC#iC{fWAoO¡B„ ÖCM¾²B ÈnB–}Cv«oB¢bBP7›CŸý˜B7‚A’!éDK˜Blr?B-ÑïBsÌšCSÓòA»üB9›/C¢¸AA‰Iu@Ä]BbAL.¾@Õ«€B©0BAEaC8? BãæB ']CARBþ±€C pXBlÕóAµœƒB ÷-C‡BKCCMãCA*C¤ ÷BR_C"ÅBªKABÃ'!CI,JB/<ŒA?;*BB]B”j0B› Å@oìü@QyšCLÆßC6Õ B úiC¬H¬Bà_£AöRC£k§BK7ÕB:¤ÌC™‡7Bë²BZUB_e´B5’!BºÍCa§zA¾Ñ@²îBBùp”BÖ ¢C¨+óB–œ|?¥C~CV ²BÙù°CgL,C8kBÝyÃCc´CGÍÜB÷±A÷„³C^}A'ÎÈB»L¿Beå^C&ÌR?¬sBT¾¢B p,=êéÊCŒ.'Bûº”?OåBkDçC0üªB Ã~BB½æ@Á @ÿC¨èB@&ø@Ž7BÿpC.ÍAV•±Bw'Cˆœ*B~(?Ã÷„C.Õ+A~ß6?š³‚B–ѯB©#GBË«SBôW C^ÄHA'WB9ŠC [c@Òå CHB$Pr>Áè>ÄŠOB²ª BÖ`(BJwüAb†C}¨iB MAÝOáB*³?Äk@€³êCŠC]Gi@ƒ‡aC½ŒC¿B›`MA­ÖC}¢ƒ›S¸B’œ–CPKBóÇA–ä¶>ãKÏA–Ï©C®§•C0ðA¡¾xC0±C@C=¯ëC­}IA] AÝøÖC†hùB :ÌB‹ÚSBÿ&A£ B¡øGC7§?‹ âAÆ ÿBöNAŠ©A+T0A‚e6BÊ÷cC< BŒÀÊBÁŠ@€${Cd˜ÏCJ!ÁBD-`A­B'¯úCLJñC',gBrC¹B[¥A(&ÄAt|B^)§CÀKC7ÒbB ·Ñ@Å.†Cx¨@ûlÔBÕÿºB“¾A@)6BtC`VBúôg>§bºB_ñòBÿG¨Aç BB<Âé@¾Û‰B<'¤A‹8B;¦ôD\â@ƨ@ôQ¹?Áa@6žPAYn1Cq’qAB­Bû•ˆCˆ¢I@[òAÄÝZBY.Aú QAÿA”ðCÝ,ÝBž sC‡@>#/Bp@ùC¹ô?tü›B$¾.CE¨Q@ðáåAj’}B¼¨ªB’+KC™úžB @ËM’AÛÅèBJŒCH™A­ãTBkò2CX»@'ø@Êz*Cœ ‡C¢™@¢BÖº§A”ŽBØDgB`ütCzQCg¡CAŠB?qB1ojCY°òA$¸wB>ÁAUúëC\©`B~­;@ CB¾üAÌšpCA@å@¤‚AXô?ö2HCMÐüC3RVBdºCClëTB¨CNZ!?ß)@PÖBæˆRCx†fBö:©B¤B–¥?Þ|@©4|CãüBxi0B€;CÐñBÿCS@:ÐZA¡ÒAC´#C­1C’ׄBXˆiAÏÆˆ>¬&Ÿ@„ nC6ÖŸBë²AÔB¼ ŸB^˲Be2ÎC§€$CCW¼Ab£ÊC–¿AªAOÏtAÝJYC(êC*§£BžÑ1BY’ˆCCÑþC3š“A¯ÿ%B±2C %>ÃV6B€L°C¯ØCÚ¶ÀA†?_C…€ÁBíW7?NÚA‚:†@ödCxºkA©¼¤C¿?ìÙ:@»ÑC‡ÀICdF`>æöÏBŠ&•CеåC¯C¯‹kC BþÛ&BñïC)>½BÑ5¥Akp…A‰àPC‘©@…û|AÛ5†C—t.CŒB)KBÁ»BÉåÍCš|CXs—CŒ×øC‘_¸Ax§B (Cß½B}èLCYq£B+ÉÝBÎ%C¶é”Ä?Ú±ëCf–¿C[ÅÑC‹$A굡BwÙéAcV@CgBCoB ¹˜C€GA8®rCFVCÜ#»ChÁC¯·ÿC¡ùÓAޏC,ï…CfpBi¨SAÍüÔ@õ·&CŽ OBõ ÿBfkOAìJÉB±¹C ŠƒC–.ÄC”ÓCêlC”¿ÇCžñ»C–¦CKÁ˜C$H@ƒ EAØíÛCmÏŒC¢>cB¡Šà=@ ?ÐF¹@³™'Cm!4C¯]PBX+^B¨j±CWC^NC$—CBC““øC•¸C^”B”á¸Bq}C1ƒ ClàBmOm@Ǹ¼BCAæÅ@c.N@§ÄBC %Ú@¬ïFCpAÈAþúvCjD/œC;CòBŸËÌC‘ñNBR`dBD´çA¾ KC¡FC^ŠBµ“»C=?ïAç{øBÑO¢C£B¸ÏPB,@DBÝ÷B O¦C"{BÆMZA7\ËA‰º±C¦,¹B«›“B—“BÒ‘pBaÏ+BÛÖ{CÖ´AìIDA•/CÈCBkB¥ë}CŠHüB=A3*”CÁkCq¥ÃB–yMB?9AíïPCŒÄAZABó¢'Bn˜eBÕqÔCm@OŸ÷?DÐÖC7çBÐbQAݯB¤zÞA…`ýAð¾C7q4CZ8BŸiAÓ*i@$¶ÔCèB´u%AEÞ(AÚŸB •LBª]iC{Ÿ"BL ?Ëê>ÝtÖBpö B0vÄA'}«C‚ŽFBQ5¿ÆµC2+ãB¹ZÁC ÀÎBóìFB²<ŽB.¹êBIi¿A§ÉNAÆÈ©A G @{CyJOC=RB?zžAg?ðCoýBV1B‡ÝÇB[pBÖ¹CBœA ÒA0ƒÒ@U-yAäMBZmQ@ã›C1.BŠëÀAwùÖA¾iÃBpf@9dáB¶hÕCð}=C v¬C¡èÓA~¬˜A–nCwm>$mAɲfB=€vBT›‡@°¶Co~ÏB,ž2@Å)@†Æ\B—íJCQ’†C:þ"CáAW½ÝC]VéC(àXBªA+ Á?_ µC»B—ññC žEB­‰ËAz(gA'»ž>IC7f·Crò7B¹9’>‚\ïCdú;B£.†B/ C•ƒ\C¼sÀC ÐBPTŽCmuÖC=·|Cb–¹A­ØB£FöAJ¹‡BZ,ÿCNÀHAœþAoÑÎ@î 0C);ïB%ä€B‡³BמíCi‘ZBWI±BNËBC}ÛB·HBPBBCG .B}­BŸmC}© A*» B hB¾RDC™ e@¢Š?Á^'BZG@­$?Œ+µB{ÅÕC/sBœ«B ÔYCÎ<—B·ô@C”Ô6CrÿB„nCaJÎC Lx?Mžß@›6éC¦Ö‘BWA~A B„’B>Au@y »@”þ5C-o#C§#C×CB”FÃ?þF‘CÒûC0ÐÙBŽV¼CãíC!÷AœB.C•,]@ø*»A¸°BÖC‰¢ÛBÜupBÁ8¬CßÈQCBÆÎ»C¡Ì.B^ Ck‘ÜC^¾AZg÷A„ãó?®ÌñB²j&C›9MBµŽBÁùBžÃ>C[õÔBŠ÷CCø+C½(DB¼uC“ÖÊB£5T> OÊ@Ê$?íwB-0C–¯%BSÿ˜@K×@»óBŠ€qC{ÉäBõ•ºBòéB8W‚BýÿÛB€ÿAAðvOAykÎCÒ«ÙBšÆXBUejB’ªÁC/CG‹îCJ<@B‰Ú±B¡òC£3CRáAËÏGAß%ºCQyÄAŽJ´A,5ŽAÃÄÑB„ª»BïðAj Ö@ØêB¢Î BãyC9ô­BfE"@.Ž A„ø8B Ò0CnèWB°öZBýf­C É»B³ÈCãÉAδ@¯™×C0¯£C”¢eCìmBI³ÑA¡ÓùA; ÄC2"Bµ´ÑAˆÝAo²XCW©¥AœÎMBŽšâB| C JC \‡BÀé@B= CF.8CåÔB-ä³B 5eB?CAÝHhCoÆ£BÊBP¾jA:N2C ÇC 6%A/ñ‰@¹•‘B] ™B•ÇC9‘A}”×A‚ߣAq«3@W°óAåðÅC‰t/B à$BÓ_AÖ¸ø@»hhCïB°&BUŸgC úC§sAðëCBµ?è?˜´C­nB…zA÷GÊCƒPAöì…Aô$ÕC6`=yBèíâC‹tBTc>G`1Bý¾.?-C2âfC…|Bbf~Ao4\C›ž#AA’1AÇàLBÝQ¦B„ûtCQ®hCŠúùBfùtC¾×C”²ï?ª©Bˆ?Cž‡BGÂM>MU@T ?U—SASPLC q·BG¬Aµ¸@£ãÎAdjýC(Ó§Bá_CèÜBóC™£èC4_%B„¸øCš &CMCÊB…â'BXjÁ@WS&C5:C”»xB€8ƒB†_UBœ2A3"CrÞ@BGSA4‚éBÏP`C`,‰BH4æ@„`Bo:÷BšLCfÃC:ÂC†„C TBt¶B8ìC¬oéBuäB؈¾C–”†A´»¥Aì@ÉBçX®Cz …BƹÅA4B¬AéX)C(òC f—B3öC€QPBþ„þBŒµ>„¨ÉB:?¤CX[BÃ~A³¼œA±„åC]ÁB¤?B^ì&BeúqB¹ïÙBÁ@Cˆ)åB •3AüÊIBIa‡B)vBÄrCd{ÊBu>›BÜ• BŒÂyBÜ[BÈGLBÀyŽA3‚RBa®=C€U?°èC' ŠC\‚@ã ‹?Q*šALaîCz ”B(å¯AQ  A˜ÉCªV¦B/HM>›]G>ÐY+@Þ÷3Cyþ`CZ²“B „BöYhA‡4iA‡DÇCôC?F ANWÈA-² C€Aºé~Cu\@NqA—q@³•¼CŠPCÎ=@~*m? ~£Aü‰AÀ ‡@¸€¨A °C(@¢ 8Aù¢úC. ‰A}¤A’X±A¥ŠøByS´C?l+Bv‡BXòA*¨‡AwÓèCƒŒ”BÍŽ@9±A¨‘Ú>J_A8³aCA×4BÆAÑABåÛAƒ™@ÿWÉ@Fü`Ca=Bë&IB9.„B®ÄB™ÙBZoC<åfC‡×J@â9ÈB/©ÖA2*ýC2áCíhA˜û±B¸ïrCK­B›0®Aõ²Bì¯Bp¬B˜à¬BÕ/,Ajzü@¸C‹ÝåCóB•Ój@ Â}BŽvBàËtB{gB¬ßB½pªBà·ÍA&ÚÃB°iA‹‘êC^,MCml>4ã‹A®féASö0CšÚ¡B“DAÇ¡Ã@™ð°B°e¹CsBKB9è–A#~öA©`—A\6š=…½B™k«CŠBÆ cBYöðBÛªÐC8ßhBŸgAº…yBjB݈C^ AýÍAÈ-¨<¶ 9BØ B†—FC·SaBûyB¤âH?ˆúLBÏZCe®ECVïC çBh'×B7ÐCdÏÆB†B:áB¼6¡A~ßBÎÊBÓ[(AóæBcÇ–A?õBäyB~¨vCZmuAê}¨BHp(BôÏCAÉBRÑA¡owA!üÓA‰B‹©lC¦dC&J>νŒAV›=CA+éCXÄ}A±õ¶@¶ ÙBü¸mC8â^BUòCÊ*CjûBCmbC¥JCBÿICBó1CnªCÅC$BÜ2CÅû™C~A‚kAûÄBƒºC8¸BÚèZAÂÊaB½^œBúªC+n CBzB13eA² 1CMOCX2›B‹uNB¡¨_C(!C9 —B lB1«?CbC¥@ãàí@ÊE|@Kåä? ,@U§©Bµ:ÄCZ®®A“ýøA+ÔjA zmC ÏËCÚºB(+OC8'r?í{“@ÜÁðC8ÁÓBÚc=AV!BßóöB‹š C)Â@´ßA>²?xHÈ@êBIA±ChjCŒQ¦B™U/B˜h?M^@Häõ>Å áC,¼Bó±ñBlVA¬tåB…X¤BˆBPÙŒB1‹BþÐB¿Öÿ>—Z@¨?âB xYBÑT"Bt9?tWC†²BܘfA?Å0@Kò@“…B×´C¸èB×ECìfB2´ÃB¢Ëù@F `BqÁ#?›üWBE ®BQ`Aí$+C6 B¨¸¶B ©B‡~A õœ?¹OøB5νB:²C!C]Bòo‰@xNzB—ŽÒC'›@ÎTB\gBg)BéÛAI(Baò@û!ŠBŠ•”BÄ[BŽ'øBš BßÐBjò*A·:A2*^B¡¼a@LtVC‚ÛlB°Bÿš0?äBÚ­¥AL˶BÌúa?ØÿfCYÍŸ@2þ?µ&ð@ÌœÌB´ÁñCne#Aìš™C´jB8rB¼÷øA¿²C$w@…eÔCÐëÅB*AAªˆ?@0|tC|§ÍBi"SAÕa¸Bš²ÓB¨"AòókC,ŠÆ?ª ê@Gýá@B";zCšÜA€m?Cž7@>ÿ‰JA©­5@= ?@\BŒCnXBAQAæækBmK:B;(GB1G+C\f@úhBEñBg9vAÁ¡ÎA†C 7øA8ƒA=»BúTAÇuBe¶Bk˜ BñL-CÀÍBÍî¶B—›,BVÂB_‘A•“B¿ˆPCœÕ^AΘÌByÉfBu. Aö”øB×=CcÏØBpuBºüBÁùaBH8Bþ B·÷ÂBWØBJ@ÈxA·m_B‰¦Ñ?©_ICV¶A•–V?ÅÏñA…ðB•»dB€ ÁCoéŒBjd3BxB”~¬B@ê¿À@©ó´CSåœBÓvB¡öÔBΜ¿B5JC2Z@’£@TJ®B…SB«\uBRÄÆB‡¯öAnr¼BÅ^CÇaè@¯¹rAâ®i?è`3BÞÈŒBIz²AâúèC;Ц@†òKB BSí,B¦ä>£6‚>»sn<À(îC8ºíBÑ`/@'ø©A¢Cúà?Ô¶˜ATç=²ô©@Î)jBŒYšBŸº‘BIç@ÈbÀAûå~CdjØ>\A}yA…âC_ŽC«B5²âCŒCtA'AÀŸ?;vbAÏ5‚B °b@L©AB|"@í÷A@f£@AÒ›CºçBŸnuBÞPCP™þ>þñ A ÂB_o˜A»B!f^C9~nBb;YB$‡BBÞc/CŒÿ?è¬BÏ…&B-ÈCF\šCD®iBG„A¨H»C…BÓâlA›B ý™AŒïPCîîBŒz¿C(ÜA']»Bð1*BÆ´Bê=9B‚˜GC>ÚiCñèC$Õ,@áMCTóBQY™AIlB.2CíbB3¦B·²ôA3FÑCƒgsB:²ÿCá{A–-?4&€=ÿõ@?ç1•*ÿxf?æþ;È×Y?å€OÛ+3?冊U{= ?å…«oñf?éu§L3Dê?è€àRÄcÎ?ç+¨C¦ü¹?è†3£ íH?æí…ëë_?ée.Sþ½?çQ¸…-é?êÖÂù’?ç¿ö#MÑc?ê´óÐjˆ?ç’ÔZ#?éÕª-Ù?çƒøÂE×?é…n€uU‘?çA’£hÂÔ?é/¡¤+Ø?æÖE%0š­?è¨ä®n4?æSeÜ¡LØ?è]¸Gž4º?åä½)J%?èÃä’,d?å£ÿ# ­ ?è#ýe¹µ-?å×üÆßù³?èQýOÚ!?æ…ÇJœÕ?èî<ÂJF‘?ç(LNvZí?êDãË?]À?èžpxÿÝ?çMª4‘™æ?æ?õmP_"?éù&|„S?èæ¿ EÒ?çÄùɹÙ{?åìío0}X?蔤/F?æþ zÇ­?é|w÷«î?çƒì$½g;?éc(Z§D®?çg§å)ñO?é÷m,Ã?æöå,Q‘F?è­>£©?æÃ~às?èŒÂ@û×?êÂ˱n?çj­•?è÷¼W¶?²?æ@Öj‡?çèÁyñ¯Û?龯Wýdm?ç:F† U?èmÒ„ÊŠ?å¾ÏÚUyB?çÁmÊ Å?é&‚·¶¼?æÔõ;¥5?èS™ã—â+?å†×®r^?çÿH˜=J?éýŽ©ûè?çMï¸{r5?éiŒÌ–!r?çž4?æø˜ªÀù,?è×»œg?æ§ÓÖ¢ª•?è˜?fI‰?æcõ †:?èòÝà  ?é°‡x™ów?碆#,?èVø¨™Ì{?êeN‡‡z¶?çgJk1?èœP¶ýÔ?å©ît k„?çXÀŒf.¼?è–*¢÷Îî?ê]ö¹#Â?çWãÝE?èMõ3ÕJ\?êM„‚R¸?磿k?è4[²N+?龃+Ä.8?æÓ¥ƒÐ?è4ƄƌW?éH,Ûƒá¥?æöáÆÉê?èŸd¥Ü?éˆu‘kË-?æÕÁ/vZ?èrÁàAc?êxèJÇ¥Ê?ç‘oƒó{?é7Ñ¡ùxÅ?æ¯ :„Ý?è·ÉËÂI;?æ9ñPmAR?èwö`‘Ì?æ35!W2m?èo€øUï¯?æÄÖAÓ1?éSÉP+9¦?çó.aÍç•?æz‡˜îì?é ‘æ<½¹?çL{܈Ù?é΄­Ó ?ç· zͼg?éü•í1,?çzþ+úÀ?é ,?"˜?èe—Lsž?é¶:û–b?æƒü½?çÚê^éª?è‰ó­Áá?ê Ë/Ï9?ç-äì§Ã$?è5™çýnè?éÍhTô¤?æ.¾Rfü?ç¶³ 0”?èøÿ©‹ù?å«å_§Œú?çI'h¨°?èüauÀî?æÛîÂç?çë²L u?éUtÁ1Z?æüƒ©i%F?èĉØ}™?æ{‹ÜÊ;?èkJn5Òä?æ1  ©Jø?è«Bklºl?é»~73KÐ?è ö5¦f?åâßl(ÀR?è.É·nx+?åßr.¿?çÿœFÌ%‹?éËÒX_&?ç[û Iè?èá{CÊþ7?æöǪí?ç´%I"¦ý?èݬ¿n ??æA e± ¿?繩¾LJy?è·ÚÒÔŸ‰?ê:D×q1ö?æÓÃbÛI"?çó:GEÏ?èÇê°þ©?êq±õ­Ôc?çT¢‹^í?çÈìz*E?è•?ÔœA¯?é´í«e?枀½4 $?çTÞÀ?è2C¿y@?èÌÿ8ÊQ?ê@ƒÞ­„º?æÙÍÀ!ª?çœÃ^ š?è‹“×ùì?èÚ UíÅ?êE6×]Ë?ç+‹Ý9?穱4RÏÊ?è43‹ãs„?é"áKÖ?êp%…侪?æÔ«×ýç?ç:…Üó?èl ³Š?éCÃ)B~ç?åñÍDxí?çIfÙá†\?è¦Àb?èÈ¢$m~)?êtmìÈ?æód‰äöj?çèìÇŸ!p?èÚД×þÍ©Ô?æ²YÏyŠ?è,V™÷c?é[06æ ‘?æ—Š£Ñå?çÊ‘_ýÏÏ?é3o…­‘?åï{‰‚œ{?ç.Ŧ‘ô?è6;ò”ä‹?èé´«ˆ?åÁ}ÞIå,?ç ÔAƒg?è¬db(?èt©î6›?éKk»y1?æC-ýÐÕe?çSÕˆh>»?ç` B¦ñ¨?çȳ>ò×@?èšäL'Ñ?é²ÛX?æˆsašT«?ç‰w®è:å?èP<óLç?é¹ÇŽø÷ ?æ¤5A™?çǨýué?é­Y‚'?åàg•H“l?ç°ON±*”?èàJUq™ô?æ7§wEŒ?çÜ´NNFè?é‡ô`ƒn>?çPÅvó:¬?é ,p¥Cˆ?ç;W¡’bÒ?é§…Þ{€í?çiˆÄTdY?éš=È<Ø?çl%vU72?é]é¤ú¼?æ¥RS@b?è¬BÌBé?éGÓëhr?æùŸ,Ç8?è6|Îö?ékCwñ?æKÀÆÕÇ?瑳®k—?è}aÖÝN¦?é¾¼È4yn?æ¼6”[W?ç„ê­ ?è7Ãa˜*—?èù ÇROa?ê_"KW»?æÈ´Ój,?éݽ#Ù9?å¾¾åñ+Ò?æþŽ~Žò‰?éLÊ1LH?åÞoêò/?æØ‚¨mt ?éz\är?åŠ÷àÓþ?æÿÒE j?éGË«r­?åçd\&v?æÈ$Ù2tÕ?é\쳪Õ?æ¨ýûf?æû·šPŸ?éx?J?媬¿´öì?æäpÏì&ø?éX5,¦ô?åÙ‰þOãŸ?é“ýÔñ[?æ[b¯w?çvª2gÿù?èCHÓ¾°q?è–úëé¼?éûÿÿ¾Ø?æÉôë©?çâûFƒß?è°ý oß«?éàù‹`–h?ç p‹¾ ?è)Ò³?éJFŽc¸-?æð™Ý°ý?è}‹Ê ) ?éÙ2v°:?ç†?Âé‘??é#ØXøêË?ç8ñ®¶/è?é$ÓMv?ç#Œ«wš?éA¢Ÿâ÷?æãeðƒ?肱 n&V?åÄ3›È¢?çjÒ ›–?è”ÆÙw!Ð?åïE„Ù?çWriÝV?èJr<ìé?é¥G±žÏð?æÁVYˈ?ç¿•ìÅ ›?èoâs¯wÜ?éÒBÕò’?å실ÏK?ç-œî5`.?ç°ØPÎL?èÞ¶ñ½a?ê ² c–-?椆TÝUÀ?é»jR$p?ê÷ÃŽ?ù?æ’»˜œ|©?é¶4JVX?êV±ÖDJ?æ¸w$¡?éCÐ Q, ?éúÉ–ÈÝ?æÉY]µ?é™~~kèc?æ#KñĤ?éQW`ðÇ?å’”Ã#Ãm?æå,ÏÝr?é*9> ?êL?m¹N?æù!9~?é5²z_6?ê%[ Yè?æ®c9óÊ?éÀÇô ÿ?êM­sì~?æò·Ÿ,È?çµ² ‰ ?è1ÛÚOã?è¯G®õ¶¾?é•‹¬ÓÒ ?æN ÕEš©?çHúq¬?è†â<»;?é0º±?åìa˜Ûb®?ç`ƒ‹î?è[öŠ1kÝ?é›aÌrå?æÖ©Á<Î?èDÊ1Üo?é¿æü„ ñ?ç ݳÉèU?褅®q×?æê!«¬?è[Tä€Ýk?æ>>üœ6?è}Í¡’k?åÀ‡ò¬]÷?ç¼3• S?é7¡ï"]?æ¿ãøÌ?çÞJ†+€ä?éE$8 Б?æ|hQ‚׃?çœS;‚²?èwtÒâÓ¾?éÀšÞ?æ¾3M¾ð?禠(ÅöK?è9ŧhc??èç0Ü8Ì?ê.gÐD¢»?ææ)Wá……?ç}CÊLÒ@?çû\zd?è†>oƒT¸?èÍ ‡Š•W?ê;ÖØ5Gp?æ½’Ýþ“x?é2ªw¯¤?éó.O‹þ?æ‚Êsó.s?鎅‚ZŸ?æ·öüáU?æÿôÒâº?éQÅH$GC?êV`(*r?æ·µô,œž?é~vƒv߯?å¾téçÄÄ?æ·!,?éO]œV?åí’‘õ@?æù3Ž:3l?éO•Q­Ü?êE©»Ý?æK[§é©E?韖±…?æ0ÂQ{“M?éTy`äP¢?æ5ãÏiɼ?çj ú~?çÅ·JÝ?è.A u#?è¨c#õ?éH!TÇ?åØïMÐol?çãn«Êª?çù°§ªè?è¬Üj ??é°ÑX-°?æ˜ô_²4½?ç©ø¨éß?袤χÄ?éήÂýxÇ?æà[‰ˆ?è9Re`µ?é¥û9Bß‹?ç>ж`D\?è¿%IŒ?åÙËut?èazp?êaOàG@?çÖºhiëN?éæDÛ]º?ç%ïBA]?èŸ MVͼ?åÐs{ѧÔ?爅 -¶†?艺òûï?匟þêÜ?ç½=µ­º?è­·¢§?èõhjfb?æj,î@?ç'kðé?çÚ fŠ?è_ó1v›Ç?é×Xê)6?åõ8læÌ!?æó‡±úï‹?ç¨ý6°%?çÒs™3$º?èb¯f™?èêÈEv;s?éùWü^Ñÿ?æ€.L´X®?é,“Û í?ê 1xI{?æ«I$5V?éAËÞå7?éåtˆ4R?æ1Ï"ã%?æýw'V[|?é_(TTŒ?éÐXÇñNÌ?æRž‹B¡?æýhyåÂ?éœZèÿ?éêg¥HŽ?æ`5„Q?æÖX3ïië?é9üõª+?éÙfC:·A?æs Íí§x?é „»Ž?êÕ}bfÔ?æy‹Š0ó?é$[¦og3?éûÓáú:?æ€ý8%\?éðÆˆ¸Wå?æz“Wû.?ç1ˆÑ#?ç¿°µdíú?èÿƈ?è«P#Áæ?é(T˜°nô?åÆèé?ç3æý]ŠŠ?è6~*?èió;ŽI?é9§÷ Áº?åßúçaÛ°?çii.æ:?脱ƒ|ü?é½ÒOÐÃ?æ9/?ç®Xn€®?èÞ$Z~Òõ?å¢ô‘×—?ç‡ÂµÇµü?é>>GÒh~?æÐr§_Ôß?èš•ÉÕ›í?æiFþ%`?è^¬¼?åßÅèÆ?ç¹*Ïšø?é_•¸˜Ùò?æ‰ð÷ß’ò?çݵtžÛ?é3sòŒÇõ?æJ(ÒÀˆ?çZ­«EÒ¬?èG¥¥ÒE?éR¹ŠËÄ?æ…däy’?çO vÿ=?çöØÍªË6?裠ÅÁN?é¶€ÞßÙ?ærê#1ÐL?ç"V|Ã4›?ç”5ó•À?èBA¯á„=?è¾92¹«¨?é,T#/À?å‰ÿ‚"êw?æòÿœ(v‚?é|3ðÈÿ?å–À«5?é öoþGB?ê.Û[£Û?æ“Z7íñE?ésèí‚?æÀhÍ?é|%P}ñ?êuH·9Ò?æ¹gnÏ'|?é]ãmar?êaÜžB?æ¯Ùe$ª?éN€‰u´†?å‰þ– ‰?æœmÓÂÏ?éPõÝ«=?ê9_¸•¼u?æ©>ã•?é?åvs?æï¬aªø?ç„÷Äg:Í?ççIhº\Ó?èTçÌÞû?è£ ™¶¤à?é³,î-JŠ?æV)%¯»?çZ&S©Œò?çü Ý~?è¼£Á¥ƒ?é är‚Oo?æIªè#¨V?çºeg£Ê[?èzzbs‚d?顿ü1Wö?æÂ&hïJÿ?çõWùŠC(?é,wp¡,V?懿`öCZ?è þ8¡ûu?éç¡k†g¨?ç9ù· í?érßÉž¹ñ?ç9ñ¸$f?é`‚P È?æÆ"5ë1ª?è„ÞÖW?êrqPGA‡?çt›œ ?èyV+ÓQ?ê"®½š”?ç<í+¶?çÈ#¥á??è×EGˆ{?匛#Jäœ?çîXI.?çìêSD?è_µ’ð‘\?éMEÁ†?æÞq¹?ç–/ŒŠ?ç¦h»Ôç?è"ŠÌç4£?è”å"úÈT?é€kÃ.ä?æ3kP‘3“?æá#d†°?é}~DšXõ?åäàh!5M?æÇoYªO?é,´Ç€?ê ?éáNŒr+?êK’$/­?ç2·0by?è/Ê^bÂë?élî‹Ï ?æ#Öº ,?ç¯Ðg[»R?èàõ6bÔ?åÿí¤¼?çÍ¡üE_?ég¤¼ÕÀ?ç§o{_A?èÛf-ü~?ç79x ?èöÉ^Ž6æ?æFÔI2m#?è@Â@×$o?ê"Ö¨|E?ç^ŒPA’Z?è Îæš¹{?ç9W°ÂÇT?èÍ%BÀ2?ékÆeK(¯?æ‚ÓëõÅ’?çb`pnkA?è%Cçm8Ù?é .Må?å°Âš=šš?ç&\Ü“UÍ?èA[xþŠZ?é Þ±×¢?é룦÷‚?æ‘G–å©c?éØKÌ^Œ?æ­Œ‰óì?é)ŠÉ-u?ê10p{p?æÞÍ‘»=?é<\öãÊ?éžczÇ.?æq¨ ^ã?æïÕI±Y?é]3ýÄ(?åÈ)f=*ý?æä"â]ð?és»­Õ9Ç?êG’jC×?æ’BIîà?é >Òus?éÒBk^?æeŒ‘&?éÐâ²k#?ê§ßk ?æŸYШü?é¯÷È :?êC­ýbsU?æÞT¾#ņ?ç‘Kâ9Ca?è ä‘ ¦?è›P;;Šm?ép‰z£§A?æ~0Aè:N?ç]¡Žô¿?è)†7ü?èÓ•E,è?夒?ç 5&ŒB?è8å„ ‹C?é…¦I-?ææØ÷ãj?è0ý z¢€?éS†^ÞX?æÞs…ð{ò?è”3œ/Û?ååX#fa?èV+h—Ø?å®%À©õ?èBèRÒŒ?åœñâÜ °?çÇ´ñ3÷y?éÊ¿b¼êñ?çäÅ‚?è´¹ ã?啞™•-Ê?çXþLV¨?èNPÆ&´?éó·<¬‰³?æÏ ¦wå?çï±QÄR?è¢Q¤¼œ?êÁ3‡Ì¸?æèÔ›ò¹þ?ç¸Ø’ø Ÿ?èNò5÷fü?é(1\o?åÍÑÿ b?é­Þæš#3?泚gè?飰C,?æUæ¶öž"?é–”º†ñº?æ")}að÷?æ×ëhå©ê?éŸÜf„?æñS?éôĽÈY?ækÜ{&8?é¤ì(ºÜ–?æ;Ÿ$[±)?éŠLª<å?ætض‚?é߀Ì"C?æ”cÚ°"?çÃ(?褨$û?èG³Õþó?èÇ|’s?éÇ¿a‹žû?æ ”ÊíË?çX/JÏ]/?çê¿r;7?è1p²ñ[?蛼ي Ö?é‚4…Z>?åëó6½?çA8ý?çŽè–½èŸ?èE©Iâ[×?èz “œ2?èøo;aê?ê.³›Ažú?æŠ+Œõÿ?çpÖ_ØÒ?è6þÍ3Õ?èb@FàKX?èÌ–ÿCæ?éìkÔ<{J?æ*ü8,?çbmÌnÞ?çç~ìÝS‘?è{Äøô?éu—û<"?æ$ÜŸpa?ç(@•ÊEˆ?çËì/kr?èóªu´?é´X.“ŸÄ?æ¹Fü¬hs?çœÇß?èL»…¥8À?éxm½B?æŠÐ½ÏB?çŒs#&Éy?èñ¨ê«?å òïÎ?焬p"¹³?诲Ⱥ€?åü¢bÄ?ç÷pÃTV ?éjÉ¢í=‰?ç)¹ÿ–t½?é N„]*N?æõæ+å³?é>ÈñÎ\±?çu '@]¸?ê9 ÷? ?çë¬Ó”Ü?ê2ø94?犿w?适¡‰fž?ç ~Ñkl?èÔB…rW?å‘5í>Þ?çnt<ø&?è  W…Â3?å©Å{¤H?çn8ÏgØ?è{²ESó?é¦÷"ÙŠ?æÂ`5Àò?碸û”!&?è°E9ñ?éÈKr¦†?æÒ.Ob“^?ç“âB‘ ?è[U¬½í?éR`¸övæ?æH’p(ôû?çWuùø,.?è ;™_LŽ?èÀb™®•?éí_fwu?測4±°?çk7¦S«?èÒM-$˜?èÑr@‹ßœ?éÖ1T‚1¤?æ$;V)?ç¦Ê47- ?è5á­Ýl?èÌùäݸb?éáŠgÒ?çc¸;AM?ç´Eäüñ?è4;Þ…?éîlŸóX?功%6„J?çÛ‰N‹½?çöÛT Ö?è’À~Ç~?éè½°‰Æg?æÇ|§ÃŒ¤?çÏ[o¹?è’”Ñ=A?éõ;_ÛÉ?æèwš_?èË®b+?éÇ2ó)?æf›å†h)?çòA2¿e?é@dÂ:ø?æVuÅÇ,?è )/QÑ?éï!‡qÊ?çk7oFg‰?éF1ø ¦Å?ç©_7v?é5ž[þã¯?ç%U/ìâ?é½á™}£?çüzÈÑ4?æ.×»‰4?輑z£µ?æ£E4̹?è¨Yó¤¿?æ­»Cj†¼?èƒz»dŽ#?å»w§á/¤?ç¦ìŽ#Ï?éA-ÛKÉ¢?æµµ0 îL?çÞ9…"ŠÈ?éq´8è±´?æ‹ã}?çγ¿ßU?éC_ ó9w?çhÜrNö?èma²ãfÀ?ê’U¶s?ç/•´NÎ ?èA΋?é¬à?¡i™?æü0/Ã(K?è ”‘?vª?éHh„Ç>1?æûÀ_Ðîè?è<;—ß¾'?éÅÜÆ A8?æö1rèÓO?èbfà9(‹?ê ?úÀ¦?ç`¥¬Ñ?é5ð’ w§?æŽqaQ¬?èz‰ waÔ?æ?³Ê£÷ö?èPÏö°‹?åûaTâ?èg¸Ê;„N?æ— ^zJç?ézà^÷ß·?çËÚ6óDÇ?æ…æÛqr?éß:kÛ9™?èXAÑÐW?æãÚ„ŒØË?é˜äþ¯Aè?ç›Ås ]?êNQê™À?ççF½œK?ê_$Á®FV?çï"\î§c?éÖkߌ3û?ç\èîÌh7?é ·Ý„`r?æÖíFþˆg?è^;˳Õ?åü8Z·8H?ç²ã{GÁ”?éI±#f#?æðµŒÇ?èIÖ·ÈÂ?êpóÊ80"?çPÜ¥ÌÉ?èÑÆ5¦ Š?æT0¶žü?è#¹ë%x?éØ-†Qd_?ç`ù¯Ïn?èõrˆ°Ð?æ¡yY¥?èM€›î¼?å·ç&Ö?çæýr\š:?ê,(­K?çÊ}§?ê uÂú0?èHÄw‚?æ@l§ú"8?èߘ<ô(9?çl23¤Ö?å•rŒ.?é},ÜS?脾¼^µ?æ Þ î?é— MÅ>?ç³í³ÝæT?æË‘n?è˜?óÐ~?æÂA_[?èÇÁU —?é:òjð‘“?èÔÈ{â??æÉ; éJA?è­Ž¶q%(?æy¼E"2—?èR­Q“¥?åòüßð?èÔÙÌ(B?ên%PÔàQ?çä0¥ši?é÷­ì?çµî‹Øy?é¿}å5¤×?çµmÕÐü?é–af)û?ê4AKö¿[?è8¼@¬?æc»½=yN?é¯lž ?笠Vׯ_?æ#xt‰ ?驦õ–#N?è¦õb< J?éÊ)‚>ˆ?éƒèÏM…?éïÃW Ð?éŒé‡}c?è‘gÛ÷¥?èŒF(d‹²?èØÌYCÃ(?蔕z@š?ç& åÏ?é |½nón?ê  ¥:&ƒ?æ4;’Ìö?æ§ö%„‡$?åˆRrl`?åá§‹Ų?æ9J-ÀÆä?ætcÿ7‚?æ“ÖU£+?æ¼öi:¯?æò€ðP=´?æþ5Ûþ?ç<ŒÞJOS?çÚ}1Ì?çI~\ ‰9?çHù•¹w?çž:••Ä?ç»Éƒƒ8?çÅÓ»,/?çþš¢ëâ{?èy´‡‹Ñ?è’¯vŽE?èFFßäÀÈ?èT$ºj’?èšSÊþþ?è¹T¨†?¥?èöˆ’EGÚ?éVKºöè?ékh‡zˆ?é--¯Á-ß?éu ÀZš?éW+ÄŒuØ?é‰ e;°?éÁPÉ?éñ¾Èäÿï?êóÉà°?êv å£?i?êJmµx˜¬?寴5û}s?åöqHÏQ¤?æ4œòCþ?æ;™–Ÿ?æsyªRw©?æpö˜;ûÈ?æ¼ðRlÈö?æ¨[’¡¿?æÈ›ó7é?æÏ.øËEL?ç+Ũde?çhÌâàˆ?çgÆf¸Ù.?çr†º=?çfÖOütð?ç¶z¦!·—?ç§èF¤ïf?çæ³òö?çõÎÎ'?ç䪎ñ4Ù?è% @ù?è5U–¾¬4?èDõðôŽw?è`!ÄgI„?è`¹®V ?è­\9óã?èº?ÜìŸ?èî-…ë·?èØ«Â•l£?èëÅe/í?é>.Û†¯€?é,¬G˜° ?é~m§êj?éc ¹öM?é«"ë<®Ö?éŸå`z\?éÙPGѲJ?ê*-³™e?êOÔŽÄ?êwµp2 ½?å‰ç¨@¼à?凊îx;È?åÙIê½b?æ..ï‘?æ,AÀV?æKŸaãV%?æ}[®“Žs?æÁjp¶èÈ?憵›¬%_?æëŠo,?æÊñíE ?æëd &Ì?ç!¡¨7Á˜?ç+“¢æùÃ?ç­~L??çV¨öÒ¼œ?çhr’?ç”×yþçÄ?ç†yÜø‘?çºF_KŽ2?ç§ÅâÆC=á&?æ:ÿçl?æsû®ÇÔ ?æQªôSÒ?æ¯þI-é?æ÷| ÓÊ?æ«3-è ?æßÚ9l÷å?æÐ%ìuH±?æçŠþßÚ?ç2,øë`“?çwaNîß?ç¤úâmŸ?ç]ÚÞC‹?çsËÑîµ ?çH‰Mü·¶?çÁ¾%Q??ç \P¿®?ç‰b¾úÔU?ç%”B6k?çéìÆ¼§ˆ?çä‰?çé@uÏ^?çÊÚ—¶«?è :.iý1?èýE#õ?è Ï™;ªÞ?è/íó'%?èKðb*Á’?èRÅ©Øtþ?èh¶ßÓã?è€=®mñ?踬1;&v?èŒCÝL!ù?èˆ^äA$?éV[O?èÅÏGœëã?éQáÄ?éÍg.gÉ?é"´D~î?é6RrÊC?éDÇ?ä´C?éHìI rE?é`Ö èCÈ?é©óÈÎú‡?éãóž±Z?éñ2ÓRè[?éçhÈ%·=?ê4ÎJ"\ï?ê 3.?ê[áü?åÂO,õ®3?åÓ%ÏÆp?寸¨Ê?æ'‰‹”?æ%láWS£?æ Íšð?æoàQ,G*?æƒ%K¯—v?昂cœ(Á?æ“Ô…‡Õ?æ´¬ðîÀÆ?æØøWz?æÎLé¢ ?æó›P’ŠŒ?æÕ×ôj{?ç4¼¯s]?çTÂ…x?ç,0»ÃLd?çÈJñx?çJ‹eA?çvÊÙïõ%?çpÁ¡‰F?çvDŸŸ?甑=>‘Ð?ç½»Mod3?ç†øçDk´?ç³ÛYD?ç œœ\ÈU?çëœoVoU?çØ4) `‰?çÄÉCñ?çÇ?ð!Ô?çù@‰Šb?è¤<×$?è ÀýÅ8?è(Zn?Í?è&u‡?è1E2E ?èS3íXé#?èaÎéš?èD-e±Òµ?èm¯÷Ì›u?èñ@´-|?è¢ýÀ€˜?艩AjÉD?è¦áci3Ø?è¯c&6?èÎzñÇðW?èêÒÀ\L¨?èåÍEýž?è߸>[ž=?é  ¯ÉF?é–·Û,O?é0ÙªZ¢?éE!'Ïïb?énhÄÔ2¤?ébË6a&’?é‹3uÞ(?é—u„Zaá?é…¥P„X?éý/Ro?éõ!¡š?ê$5þºY?ê/¯¶’Êv?ê{.AÀ â?êVÔtþÅ?åŸï¿˜à?åé¤]‚C?åëž‹ØÊ%?åàäÀw¯?æå6#ã?æÔ¹ iÕ?ædçT׈¨?æJ¤j8ïI?æD]ÂP’?æ–õÔ­-?曄[€â:?æº$RL.Ó?æã ^ÅÍ?æãüQÅ B?æÒÑ”Õæ?æþÁúGÔè?çíÏ{a”?ç#Ý" €¶?çû¼_Q?ç(›X÷ÒS?ç6.~= ?çVFûHÉ?çrq„}?çfä*»äø?çM߻а?çv&äš â?ç€èu ¾?ç·) <öÒ?ç§³ZdÓ?çŸ 5ÜÜ?纆 VÐ?çn¹u’Q?烷6Ò>Ï?çÖnrÛ×?çûäÒÞN>?çÍ)Ïs?çßâ3f™á?çòöZ!•L?çÖ Åtþ?èÍœ¬vŒ?è+£x>ç?è;]Ý@Ê?è`D.Ô€?è=↷?è1rßî‹?è7z,ûˆd?èc‘h”Ñö?èF¦ëpž>?èhž «?èLíŠÀs?èLø,Δ?èY_Þ9?èÁ¤g,š§?è¡çŸêÉ?èŠ|ÅèÍ2?è¡!)›Ñ?èž¼¬Drd?èߣРe^?èÌ«xÏÜ”?èÿNd~Œ?èÊëѼhÁ?èÎËP¢’u?é/•htØà?éÜ"&G?é!`´³&á?é'Ðcoª?éW'˜sÏé?éFMá ?éw7H¿ïJ?é‘̺•7?éºå‹#«?é¬m¼T£?éù‘~=`?éäÕÉ¢½ð?êöfëÓ‡?ê&0¶f;÷?ê$eͦ?êyW ,©?êxŒ¶ ?å‹J5µ“à?å„!b2•?åÄnóWƪ?åëÑE_?Î?æ"²ËÃŽ?æT‚;:?æ83póg?æPìÝšŸ?æ9 }þ?ægð÷šZ?æ½Ã­ísu?æ¶ÆôšŒÀ?挖$é¸?æÀ¡FJù?ææ`IŸ†1?æØÜQ4º—?æ÷šyAüÜ?æäD˜õc—?æäéð¢ÈR?ç.—-=V.?ç: µ[å ?ç{¾Â ]j?çvò’ e?çGïdX‰™?ç³'J!¼3?ç˜JŸEôd?ç¡õ‡Bû8?çð0)Ð'æ?çÄH"ßâb?çúŸvEß?è>‹±ž'?è …îU0?è"åúëXÈ?è‚v|r¼¹?è_LhCÇy?èwbÚ˜L?è¸z²%V?è¤×ß{Æ?è™<‘G?èñ­ßƒ?èú‡²(ÄI?éðçˆ?éPÊǪ·?é3±¢Ù?é4w­Šc?éfÝÙÍu?éTOæ?éfÊ:Ü|ã?éC…lÇG?éMɱ§?é…Ø[¦iö?é Þ};?éíŸê’Q?éßjÅr§T?éÏc¤ñ] ?éç§Qåt?ê7+JaOµ?ê*pÈ20?ê|­eT??êpJWé¥u?å¿êHÉß÷?å“þÛŒ?åȈ< ]?åÎ>«„hŠ?æ$@‘Ì?æ5+¹Gf?æCkÄÊS?æFIwl¤Â?æHÐn•ê?æytçZD?æ 5…« ?æŠ<Þ«q?æ²ÑÉí·?æ—QBÓ?æ—iº±fS?æÝΜ5‚Z?æÆpâzÏ?æû —,?æÐÄœJ´?æÌvyz?ç#Ïç3½z?ç5C‚àà@?ç-‡ÄÆS‡?çD:eâÈ-?çL$D 'Ã?ç‚VÀ]Ä?ç…CECÅ1?ç¹*º2Öþ?çÁ}T %4²?èôàäŠ^@?èÛî˜%?éC F^mo?é ç2Cv?麘/ÚR?é/E¦L± ?éØgc›ñ?éOÆešch?ébæ×óÊú?éui_ø)¸?éqd}ìÑÓ?é¾|»Û?éŒõÖN[?é˜ýì:Fp?鄯mV?éÆ*€Z‰0?éõÉSÛöe?éô˜8 P?êE þ:?ê-¸[›Wg?êIŠAsæ?êQi• ĸ?åÀÊ®ç/b?å¢2"Ÿ(?åú™­hÜ?åðLúþ?åê1Âa?W?æ*¶˜÷|?æ,¡°Ÿü*?æGz›4Ù?æq÷œUU?æ€^0ÿL×?æYF©¯¶?æO‹wÞwÚ?æŒcõÅFÎ?æ_bê ?æ `MR n?æ™ÌA}Ȉ?æºÒtܬ?æû5œÜp?æ× ¥´©À?æÈ­ÂC–A?æÒµYS¯Û?æ×A–uË—?æÇÑCi%?ç&í¸Ý‡?çB[‹Ûe»?ç@SÕF.M?çX5@ÛÞ?çJNTé?ç„V>=]I?癸¦#Ùå?籊ÇbZ?眻©»%?çÝjЋ r?çÒ‘ãЖ1?è=¿sõ2?èò¬P—”?è1Ü÷Í-?èwNà†‰?èUÏeL“µ?èY<Àh¢ò?è`‚ fQ€?èˆ/ÙëL\?èíf¢s?èÅQøÎ?èæ 5˜d?é°Eùgâ?éMçͧÝ?é] â&ú?é%¢.B9?é#³ùQ¼?é6rP ¶?éZµd9:Ø?éx»çaA$?ézk¦ýî?éo]AùHu?ét$QŽŒ?é­ð³”…‚?é¬e Z±?é‰:\Šèt?é¤nô¢ºñ?éëv\À?éØsÚ¿%?éÙ–AH¨Ö?ê$D­?ê   Åê=?êv‡“öQ’?ên<ÑÛk½?êKè{ÿ?å­ÏÁÕŠ?å­Ç]Îë?åÒ¹s+†Ç?åñQŠÔ£?æCŒFš?æÆ2:‰?æ9–ðÒ?æZP75G4?æG•m»#‹?æSŠW=¤?æLôE0EÉ?术Løø/?抧Ü1B?æ„xi8r?毨>Œ?æ˜-ð÷ë?æÑD¿ªR?æà oÿ+Æ?æÙL̽?æë±r9L¿?æÇRX¤‘ÿ?æÖ€Ðzw?æ×CyD ?ç>É&ºf?ç*çnב¹?çJéÊu?çzHÝ«”±?çp2s×Ó?çcˆ¦}$A?ç‘lÛ £?ç±|gžÅ?ç’€*išk?ç—UWtî ?çÇÞ°®é ?çÄ»F/ƒÛ?çé8ƹZ?çÊ¥g{Åê?èû«Ìà?è hÓ­½?è&#î0å?è<:Ó$¸W?èDy~v8?èh¥Ã™;¥?ènÜÅ D‹?è¸Å ã¦?è°¬ëY”?è± þîØ?èǶÏ7®I?èÜX9Ǥ?éòRõ4ò?é-8áK?éÖL%ÿ_?é#ÒM½»`?é@¸Ü›¥?éÃ’áÆ†?éfÎ&èGÒ?éyï_Y‡/?é]Z¬Aªo?é‚“àÆH?é[úÑÆ?é¨?÷lzZ?é« :òã:?é›’-%?éˆCûu`?éð S²þ?éâ¢owº?éÔ#Üž¢?ê"í8ú[õ?ê†8ày*?êB} §ú÷?êhaL\!?êsu–œ?å¯íÖ •5?å˜/—s­?åèl#ãs_?åêT«¹·ú?åÝVÒ1_'?æ?ÛùU?æBƒ§¢Ì#?æ(Î…±ú¸?æaÈ/‘èø?æa¯ßo­?æU"bwé?æZ0²ß#?æÐ/Ÿ‘?æ±™â ~?æÂé˜Z;­?æ Ó®ð?æ² Ý5ëÆ?é2àÐŹß?é:™¶¶?éRÄ l?é—_ë1?é%Øò<p?érbvêÛË?éb²Ð½C?én”7öó£?éo·`•?érûæOÐ?éžáHUÉ)?é—4ÐJ è?麻Œ›?é«rS”˜?éÛ™"Ò?éÔ¤}ªrp?éë}õL²¯?êü^èr?ê* ÀSñ ?ê#k„6¦ž?ê`ǰÊ?êqeìx’?嬘ï;E?åˆá ÛÂó?åöðŠL N?åýÈ÷Ž?åÄIÚb¡ñ?æ4’Æ‚[?æ3¶…õ¹-?æ,çÄ c?æH«ØÁ7[?æ‚3úd?æqx-‘€4?æè‚ú,?æŠ{°DÝ\?æ‡f%¾y×?æº|ŠDÍs?æ’_«¬ñÕ?æÄ”“Ö?æôøð7?ææª—ë+?æÐ‹}Ì»?æùVÙÆ6æ?æø ‹."“?ç-¶öÄšÇ?ç RaFX$?ç÷2àft?çUéN £ý?çcz`«?çxŸ¬.v?ç|E(¿À?笸çâEu?çŽõäÚV?çž7F¥?çò-Gànä?çÛ†Ô ×ä?çàô—ê¦?ççcO ñ6?èB– ƒsf?è >G?è;œã¾¾W?è28e~jë?è}»¿–32?èU‚nŽÆH?èz,´ˆ'?è“ûCÛ/?è¿;üâ¦%?è™i÷ãD©?èœ-©8‚?èà:|tDÇ?èþ¸ì¡8?èñX)ý?é«¢ˆ©7?é)®üÿ?éfX#ðÑ?é5™œ.?éB"Cë¶?é’¢`S´?éxÇ¢=­˜?éP¦YÚß?épKˆ?éQùq?éCªmÙ?鶨'ÕÖ?é…wœwÏ=?é¨X"Y{¥?ê&È:3“?éñ¨¹Ô˜€?éø¯qX?ê ä}Mèñ?ê3Å]sD?ê%e0åàk?êI=yá×B?êDžw-ô?å–ÍŽ;{ ?å¨ìéWõP?åüÃ$‡?åë=TY(.?å׸뉇—?æ.ÑN?æ5r¤èÝq?æm}2ÿz]?æ_@‘e?æd,ã5áo?æŠdª†÷?æ©´ÊLø?æÁ¾½[/¢?æ·Ãš;×c?æÎZM¥ý?æÝ|’!Á?æË¡äBŠ?çÂÔîQ™?çŒÍ;¿S?ç ní‡ D?ç ä|Ç?çWøz?J??çkæ_HØ?çR³h¡/d?ç³;£•’B?ç…7¦AÏ?縎ºpbX?çúÏ?*$?çÖBŠø77?çëïUñT?èØ]?è?w›Ö1?è¹Kj¸k?蟺f—?èCr×±[Î?èIr †?èYž–Ð#Å?è§®p(޵?è¥25 ©²?èÅCÈh?èÖ?Õm?èÓYQmî‹?è×Ûîvi1?é-¶7Î3?é@÷ ^?é/@„©€?é>#›¥?écÄi—?é]»t‰v}?ébû¹ ã?éSÃo¤?éùó¨¯8>…´?æ9¡ ¦‰2?æH2úkLò?æKŒ›–;?æ 4Ø?æn˜ù×ì?沇¬}à?æ°ù÷•Ñ?梀ÿ?æ›CèB®É?æémäø`s?æÎm¯þ?æè-OÎ?ææÆ™À?ç7_žRH³?ç,µ o ?çfòt™î?ç|3ük"\?ç—àç³$!?ç£"p­ýù?ç÷ÞQk‘K?ææÃ'>?笡»îZ?æâ¢ÆÇ‡;?æù Åh/y?æä%±(ãÐ?ç1Õp·"Ï?ç ÿÅx˜’?ç*Žkž­F?ç²&™Ù¥?çLbxÏ )?çWýXk“?ç_»/ü?çD…ú2Ýå?ç]¡„ ¨?çyùÈî¢?ç£ë½:‰¢?ç˜Ì”Ç„?纱ÇR÷\?ç´è¦ Kí?çˆæE­ºv?ç’¦ç*°?çÄ¥ ú2?çô¯wS?çôEîîîZ?ç÷gxL?çóá ø½?çá|¸aÕ]?èë UQ?è76Ic¤ž?èÂ7ì_?è.4sbœ?èC)F!4…?è=ʬû?èq§f_?èZb‰Î‚û?èJå ”µ?è[5(—F„?è~ ‚B{?耽J„d?èFŒã˜*á?èƒÔÛfin?è¼IÇš°>?虳IS+?è—ë¿s!?èŽ=¸åž?èñ¡{þI?èâ+{C2?èó÷ ¬2¨?èÌ`G©™q?èú žTar?éÑÏ Âõ?é2Up¦_?é³!ÑÔ?é3b‹åö?éo’9»?éißTD~?élj^½ÛD?é¦0h@©k?é–€Óe@¼?éƒö· éT?éî!1õX=?éîŽyßú?éÐ<"õ„,?êÊÑæÚ?êÕ+M¹?ê\rÍiËf?êrR¼F ?å†hLˆs?å£]ú‰?åî]¡è8£?åÝkï‡,?æ4©Sø†Ä?æ(ia?æPþ—Ä×?æFKÕÛO?æ˜'Ri¦c?æ¬ÐI¬¨+?æ£b:b?æñG}}?æÕñu(5T?æçø(Ô?æ×¸ÓDD?çfŠP.{?ç ðrnü?ç ¾æR?çÔñzD?çTµ®ÉÛ&?çslkY²?çD½¬+Ð"?çbñ?-Öa?ç°ø¯Ñ–$?ç¤eVéÐç?療ØÊM?çœb_ƒ+®?ç…Ic¢À?çÉŠÃN+ ?çÜ“1`K?çü QG`…?çäÚ³¼¹?çä6ÍF Ò?èÍò¢ì?胉œßÇ?èñöxU?è@éJg?è0.‚Mø?èq:LrE²?èC ‚ 0†?èp‰Ö ¥º?èDèØõñö?蟅×^%L?è…žØ\Ý?裯)w¤?è´ôlœL?轇÷ïgÿ?èôñlû®*?èǦUc?èøëpWÈ?èÅýøÞx?é3zž…‰?é96Œ]¼?é?J‰Þ~A?égçÉA—›?éW¯hÛT4?édR"*Åt?é‰+ñÍ(?é½”QVD?饑Ü:Ôæ?éöPa:Ö?éÄÀ;ÌM?ê?Þå…?ê2Kãߤ×?ê[ žE_?êYÓò`«M?唼„ðõ ?åëü®¢©?åÎÜ'”Gí?æ6j!rÃ?æ1é)D”r?æzü˜#ð?æo[ Á??æ‘`R,bT?æ£ñàL:?æ£ãìÃ?æÕ@׊[“?æòû—Z›?æÄTçѼU?ç7ëU`öª?ç î\Š ?ç5žÎè)û?çl-0j?çF»§ú×½?çCܾÖY?çQ-=bF?ç ù2·E?ç· Š‘ïa?籸!˜?碚ñÏ–?çóÒF u?çÈ Öˆð¹?çþ•‚)?èAòI6N?è1Ź?è!jãÅí?裷—‡?èX·§»Ö?è[ è1Å»?èj''ÞA?èIÝjö?芫sõ:[?è•ÉAÚú?èœ/0Ú#?è ¹Æu±?èâêSy?èåbÞ¦!ë?èõ—†~’?é@‚ÒI .?éqó¾Fh?é2:´bŠÈ?éQ¥ª¡?éI|¹?镹½1T?齿5r#c?é…ZÖŽI?éø^·f/?éåW¥cíM?ê'è«46?êšÁ<?êu¦|a@6?å¡âGݾ2?å².®%é?åêÉÄçÊÄ?æ%â(å3?æ¤-¥Ö?æoílŸÏ?æ}ªõ±h ?椪×ã/¦?批¹C ÷?æå‘{îP?æéÎáug±?æôÇ‚’çš?ç3F8ûŠ?ç Å*çz—?ç/ Ïe>?çNíƒöÍÁ?ç`[|hž}?缆óçJK?çLÂåVM?ç™|r²»;?çû1‡\?çñ—\/ o?çó Í þê?è+jY÷?è?ÎåT$?èHW+Ì;?èZEp§ŸÓ?è\Ú©0#•?èD™~׈?è´ï³ø?è›r]À¦Q?蘣*J8ë?è÷µ8ß(•?èñ)ø›õ?èІØù%³?é3Qº?é.„Ǩ?éZO‡©Ï?éZÍMâ0p?錾¡îê?é¿WSœf;?éðO~n ?éáÿ¾ô…?ê+o(Q«?ê8šÔùåü?êyÐ\bZ?åÂ3nvò?å†joÄÈC?åÏäi!~?æ ÐþÄÄ?æ2\P?æx1.{?æ‚ÈLaŒ?æ.î#'?æTû®+?æãŸ Ê?æÃÅý6ûS?ç(Ÿmvê6?ç ü§¬S·?çN¶–FûÍ?çlص½?ç‰agè°#?牚c~ÉÕ?ç®®>'cÓ?çÒõ´\?çäöj;?çÿÄ#I?è*õø‹?è-ÔqÞ¦u?èO$ ?èpÏFÇ©?èU^zì:?èÁ}‚,ý]?è§ý×ϲ?é ÷äá?èåö'±'?é#0/QÎ?é4›+õ?é@H“?égÚŠ_á?éT ï?éŒÄ5ïH?éúCà¥QG?éí ˜µŽ?ê Îi³?ê_‰_Œµá?êFJ\«êÎ?å¼H:”Þj?æ#‚èø%š?æ õÂM\¹?æGìÛ[¾‘?æ„w¦×[?æ†ñ$ËM0?æåO5)u?æáÀé“ +?ç5È}Û?ç|Ú2ñ×Ä?焪SÍw†?ç»–!ƒ8?çÓ[óQI?çð ÅNP«?è ¡m†?è"×>³·ê?è#Q6-Y ?èJœ/áÏ>?èHPþ µ?è“§«bñÚ?èŽOöÛ¥?èízIõDÍ?èË—kÐ!?é@>ovÆ?éz¬³ Z?é¹ÃËuy+?éªX+En[?éÉ?lϤÖ?ê ?/n¦É?ê +ÀóIP?êHõs ²?çcìT`«?è_t©+5^?ç c¹ÅÂ?蓃 ?ç(f‚óD…?çPIÞî'0?çÐhl2—?èA˜›Là²!?@4 4ÿ•„W•Á¶H€! •,|•Á¶H€! •€Ž•Á¶H€! •Ô •Á¶H€SNODؤQð´R $‘ 04 @D ! •(³•Á¶H€  •|Å•Á¶Hˆ?åšzd¢&?å€"î ¿(?åŒ/%ÚE‹?æ¥Ëê R?æ²HPð¨À?å£`˜£G?å”×¶ñ ?å›Ë -¯ô?å·ãîó*ñ?å““NÏéº?夥’üh?å˜p±Ÿ¶?åÁªêz?å¯B‡ÌdŽ?刀7ûz??å´ã–H?å–ëª?å¡_1¸Uª?å˜m©.Îï?å® !ÁG«?å‰+qQð¿?åŽ3{úT ?å›0Y8'½?åŠíè9?åžþÑ7]–?å­[Ãñ4Å?å©)¾ZW?åòY&¤ ?åÀŸÿýö|?媢‘:&?å¤%؈p8?å–QàYëÈ?å¡lÉBo3?å‘Gëp]Q?å…êäÌ=?庩hGr@?åAWû?å³›(f?¹?å™=ÛÜÇî?åÅܼ§ê”?åø­I•ûu?åá ;„Ñe?åê[…Æëy?åÝ1?íè^?åÿžM¦Ð?åÜ~,ŽyÐ?åé*07GK?åö”`Éè]?åð.¦§(ø?å÷av§hè?åØUü^°K?åô…6¤?æ28Êí­?æ”\¬.?æ1P‚ó÷?æN:¢š?æäwçå?æ.ˆ’Ò?æ0¤l[k?æ6y%%Ì?æ<¶ÜÎ#?æ 7·‘G?æ!òT8ÙÚ?怜_Š?æ'‡Š˜N«?æ)tMË?_?æ&'§0?æ)£¤J[?æ ê qH”?æ,4»Þ?æÏõÿá±?æ öT´?æ@Áumf ?æh+}?æ9 sJ t?攟zk?æ2)êr ?æ—Í”Ýh?æ@…}yR??æ?—XÂ~?æ#ΉŒÝß?æZzÜ|?æB ·u?æ5ªYodŒ?æ,ù«É5?æssPJ‹?æB+©Ik?æ ™1Zî?æž°ÂÊ?æ#½ ­:U?æ-’è^„?æ‰9~¨?æ02û»š?æ*ÚsIç?æ/½ ß™?æðÌÓ©È?æ7Õ%3&?æ6Ý:.¸?æ;käÓäÅ?æp+Y,6ˆ?æXþ¯ŽÌ—?ær©mX‡?æQ-í4Ô?æ‚b]~;?æ‚ãD«r?æHb7!}Í?æZ` mއ?æR™™<ó©ë?æK‹ü¬ía?ærNÿYru?æR~ ±èŽ?æo©Æ¦?æe™ó6r?æXüuK?æXµô‹Uî?æµ­=åî‚?枬Ç.á?æ­úKÇös?æ¦ÉZAP?æ¨þ}ð†?æaÌ{,‡?æ¨AÄ7D?æ–Oh T’?æ¥CäÊ¢l?æ³û‚áÎ?æ¿’80¿?æ°ñ\xù?æ‹ÜjC+?æËo†Ï ?æ¯h„ÿ/?æŒEÄP’y?æ¹Êc@Ú?æ§r(j ?澋iÝkš?æ¦Å;Œ¯?æÂ,B;”ƒ?æ”L¦ïKb?æ–Ì6…ú?æ•1d?¥s?æ¼|K \é?æ…2 óLx?æš aé?æ’Yá­÷•?æ Æ8É\ ?湪rÉ«?敯W”x?æ£B?ÑRó?æ‹§èý§?æÂúæ??æ‹úKB<õ?桎Éíyí?æ¤EáªÒ~?檢AY\A?æ¡ E~vL?æžz“€?æ½þÆ[Â{?æ®0ßöà?æžÌ´WøH?æ°þ#+6?æ°'ikVß?æ’W¤‡¬{?æ­¬S20?æšîŸ.K®?潘Óp!.?毟ìÒcu?æ²aURªU?æ©üù€òŽ?æ†ìK*?æºMfg%?æ§«-Ý?槈EÁR?æ›xõll6?æ§µ5š?æ³Tê½x?æ…¯‡%g¢?æµ 0Þèß?æ²çQ’ê}?æ³T]U-«?æ±÷Ž¡æ…?æš­±\—µ?æ¢È\1¾?æÿáSD?æÖ[ÂÛó?ç uWþ?æÓ‚„íÎ?æÑ 8«BŠ?æÉm!zØ[?æó‹¹+€¨?æåWøý:f?æÑ(Ëk®ù?æöO{î?çÈîåÜJ?æÒÊúŒò?æÆK3Ëv?æðj÷vcM?æÎú2•|ð?æÐ@÷L.]?æÜcgë‚,?æÈNš?æùü/ý_é?æöé6Bó;?æúaщ`%?æÍ2ßCìÁ?æù§e àŒ?æÝ/!Ã?æì0ÚU¦õ?æÓ´-–?æçÃ3Qh?æÝÊè¸áÅ?æËˆæbf3?æï°¥‰Ž@?æÏYá½æ ?æò&A­~&?æñµË) ?æôÍ÷Zž‚?æÇò¦-ò—?æÝsâÄ­ ?æÜ>ºå%®?æï„펭·?æÞB8Ñ?æÏ¢ðÑE?æÎÑ™?æè ¾a?æÜ¨9;d?æü—´Ç?æØtÒß‹…?æâ~ ùB*?ç…ÁS×?æÐýiøi?æÓŽ\Õ?æìAe4Åü?æÝ>VÚ÷Ó?çu!f[â?æòò­Òjw?æêÌ‹*êÛ?æökN»°?æÎR±î¤Ã?æþ“¶Ñö?æü^^çýÏ?æéHžÊ?æÚ- ·(Š?æÚpßj¡r?æÝ*bÀ?æä+}½¬?æË§0Q™Ì?æçilFà?æâYÂc…?æîùÒDÃÞ?æá¿{2 ¥?ææÙÉÎÂÎ?æþèã,)f?æã¤@a•?æÇ©m¿›Y?æë%KXŸ?æàûôé… ?æÊ}E#€X?æñ²Ý<î?æþ©Õî*ð?æÜíÌèhæ?æöÊÙ¸\Æ?çk§Ñ?ç ¿ º?ç6H]ÉÛ²?ç&/Á_?ç`P­¢Æ?ç4Bãe?ç9…ƒ??ç=È_SžÜ?ç §’.¤ì?çøéK¦P?ç5½|]ñ?ç·ÁÛ‡ò?ç ñýêà?çAAM ?çû@’ê?ç!Ì.„ÿ?ç/÷\<øJ?çæ ‡Ë9?ç:‰[u{?ç7ô´?ç8PÚú›Å?ç«Y¦?牒ZýÉ?ç!€å™$?çee^“ ?çB!j¯‹?çolT€?ç é´†?ç:Èh¢e?çŵs€à?ç´ñÐØ?ç )Y€Ø?ç–bmP?ç0ë·aêß?ç!© ª‡?ç| Ü„?ç/@›ŒS?ç ÙÝG?ç)†OzP}?ç$5·eÏ?ç ‚–£O?çUHæBO?ç?®²[ãÝ?çŽw]Î?ç"£™Î¢?ç&ádaá?ç6òö.³?ç%-×Ë?ç -I5À?ç=õN®¾Z?ç%JúóLf?ç> óè£?çµI/ ï?ç-rǘnI?ç‰8˜6G?ç fÑv?çÊž5{õ?ç>ƒAhŠ?çÛ²³~(?ç”ãÏI?çXpÁ”ÆÚ?çh^uáæ4?ç€:³†éC?çz2oð¾?çgÚäGæ?çO'”s–B?çm®.¤;?çS‘†(#Œ?çbÇŒ­ö?çL-pU?çXtEY r?çJ‡Üg#?çgV”òIq?çPm@š8?çwDâi?ç^¶ÁÌE?çuÅMœ-?瀩td?çHöÐ^?çP È™ÿ??çyuxÙ;?çN˺_ã?çE¯lbŽy?çzVÿòÙ?çFx™ýžÈ?ç~CA¤¥C?çUá…ý³?çEœuééù?çlbWD?çr V{?çVù<‡Üˆ?ç~RÜ%Ÿ ?çuŽúxìR?çP¿QBl?çaæa³4?çn³w„›R?çrÅ7þò?çu ¢ïs?çROp ?çl`Ú™Ûå?çFea¼F?çJ…S?çij9›ØÚ?çZEÿ .z?çaIå†ú^?çDü¾-ë?çQk•6F‚?çl¸N¾ë?çM»{_! ?ç\ c¸?ç_¥4ÕO[?çp4ýD’.?çDˆcI:?ç[>¤áÑ?çcâfÅ?çW{!Þç?çdZ™$ä?çu;ÉŒêž?çkvž±?çz_ö@I?çX'ä9?ç\ÓX7|?çZÈïfRf?ç•Ø…_9 ?çŠ(ÄïÅ?ç…NÒ:Z?çˆe$á`?ç¹6 ?çªçPÒôy?瓯b¹­R?çŸáÚÔ1?çšôêéÊD?çš®ÁXšó?çªÁîX^?çˆWƒe¼?ç·mk[IO?ç‹“ÄWz?çœ!Þ8?ç >WZŒ?ç§«y°£m?ç¢4vºÛ#?ç¨ y$ò0?ç¶oR ·?ç“uÚõŹ?ç¾f_9Tb?ç–=?ç ó´áÂe?çŒ/$ò‹]?çŽ^rày?ç§»‡Š?熉'Å<>?ç¡°ÁšcÛ?çº$ØI—N?ç„©4í G?ç“E¸Æ?窆_.ݨ?ç«ÛÅ_?碖êÆ.?ç—á2Íɺ?çK-¿wš?ç³r¹yð?ç»C*Ð$´?çŒÌ…w×i?ç½Bd©0?ç´=É`™?ç²{cøW?ç¶üù Ãš?ç§[­…ù?ç˜Mh¤÷?ç½íûÁy;?燠úÄy?ç¹°·ë?ç‹í–Äü+?çš.3X!?穈ÀýÏŒ?ç­nÑøg?çžA³Á×b?畇óTÏì?ç…\=z7B?ç¥þàëEŒ?çœú:'_k?ç¹ýjY?ç–FvPvo?ç¨`§Ø?ç­=¯ö!ÿ?玈n]Š?çÀ)5†û½?çÀqÞvI?ç¼UlCý?ç—L½2%?çªðR†ûÍ?çîô¯i"?çð8¨;Y?çÿ$=s—ñ?çÞÏ<û?çö“]„~?çÑôç1³¯?çì˜ËP×Â?çËX©\?çùAÕiX?çËø<+o?çä1MÀ¶»?çÃwÁ×á?ç÷ÿ}Õû›?çö:^ÙÆ§?çú%B*'?çØN„Ad?çæ÷<9RT?çÊJÎyP?çõn Nÿ?çÜêZ €7?çèUA?çù5GauH?çæ9ˆL.v?çüß)\?çõdú,]Ú?çó˜p€?çþ¾5P›?è9l¢«6?ç÷Ï0t‹?çÖ}Ñ"|¹?çà“¸ã?çê`%ÕÙ>?çÆN® Ð÷?çÏ0š=ªÜ?çî6)“àÊ?çÍO/7‡ð?çþÀËÚ3?çðšXXð?çòI(ºËþ?çòWˆ Ú?çÅ©…„K?çÓeMÑ»Â?çãzRTö^?çäáÀÒ,³?çÊpʧhÜ?çÄXX7T–?çô뫦 ?çõr€H…?çó.I‹ä£?çÆF‘çä?çܦTótü?çÊ:dd!j?çĨ阗?çèã”lÁ?çéìöК?çð[öƒLû?çÓ"é6?çí^)ƒ5ý?çñSg¨æû?çËpÌ r?çÞ’žÂƒ)?çÉYºþÙ?çÚÕ*–`ÿ?çók[F»×?çù^OûÔ²?çéa†é´?çþdÏç>±?ç瀎y^H?çÏÛ•à?çâëCðÄ?çå!*ë›?çÓ'ç?è I$çù?èÂ#Žš??è ‘®Zºr?è%êJ­‘?è*z’3î½?è"ïœV ?è+@ ˜}?è¶~zÑ£?è@nƒšær?è5 r:F?è!ŠÑZ?è<€ ˜ ?蟕|?è)9GUåÝ?è UbÝš]?èÏ/[F7?è<ÀÀS?è8fc}½?è:4ŽIš?è/¨ä]•8?è²YͶá?è%[zñ*ê?è ´Ÿ0i”?èõÕ'Ò?èÔßL0!?èA¸Ç»VB?è'+¾'p?èBý"^fv?è*ç À?è$0BL ?è>í z¨?è;I˜qvÄ?èþs í?èé˜Y¾‹?è,±r M¦?èæ&Æc?èL*“G?èÔ< Íñ–YÌ?è6ÏQ?è+âC¼ O?è, ˆ*´F?è ÑýÌ?è;KÀn'w?è";«9?è …Ÿ?è-æzd¥?è+ ¸Àà?è4§¬ü h?è,A¬›Ãu?èATõóô?è;˜í¬?è6þ§[T?è ò.Èñ?è; ™|Ÿ?è,Q¥ ©?è1á•’?è!Äiï£?èA#§Wé?è8޲ÉÜ?è~ؽF?è‰u\s«?èu)án?è,b£–ð?èùg?è )ª˜?èAÉ-¬®?è,¦Ö©~‘–Ú?è}ʱ)›N?èJ`|1ói?è[ôÀâ ?èwÊžðŠ?èVmÉ•Þ?èbm§õ¶ó?è^‹2ž?èI‰N‹¤?è+ÔXH“?èêngðz?èlúK[5?ès‰Æ¬—?èf2Þè?èxKµG?èZû´[¹M?è€'³G`?ès¼—!?èk«DxÆ?èrÐkõ?èCM¹!!:?èdãç4™?èv¦a´ÔU?è]ÐÃ`?èDí» ª?è‚ AÑ?èz:U?ÄZ?èt³µ/;/?è^ùÃZûK?èXˆÜ0ù?ènؾ‚ñW?èjFÏaëÿ?è[;ê„ì?èP½À“Ȱ?èyÅìuÿ?èL¯vY~?èF„ Ç–°?èC󲊺?èO¦!Í?è[Äþ1!n?èZ¬äÐï?èhú•hÛÔ?è`¤VÞÛ[?ès#Þò?èWµU ¿?è]za³ê°?è]°32Ë"?èYˆÖ>ºŒ?èƒ@ó„?è‚—©™€•?èy`¸Éâ¢?èo(@I»?èrQ‹'?è{Î.K@?è^s±õú?èY&>Ÿø?èIÜDeF£?èwì>2´?èXÏi3?èwú+Øtn?èOÇ}‰É?è ª2Ù$?èŽäÇ?è±¾P´÷&?è‰3³âöÛ?èˆ$ìL?èlÒÎÿ?èŠWä·?謟|=Ô½?è‡ÎÆ$ê?裂§:Æ??èªèä´‡?è§¡°·Oó?èfüÜ ?èžµjõû?è­ÏÙ½§â?è±»[¼ê?èºñ ¹}?è“»?ÅÉ?èÁÒÕ K?è ìŽ»+?è§ÚS Œã?è½ÂÃÿmÞ?è§ šÍä°?è¶÷ýã)€?è²r \ÍK?苯(ß·*?èšo%?è]®ó.d?èˆk®ÏÜî?èˆèTVa?è©„oH»?è®}TÏd?è½ozò Ù?è­#åj†ý?è‰ÔuÍ”5?èŠ ±·Ãv?è‹pn|kÑ?è` #?èªÜ P8?è’³bq?è®ÈvÎ+,?èÁrž!´O?è²4\Ä—&?è·•LL$?èˆ_Ôºa?è›6j)´\?è–ìc!Þ?è†-¯»?è—Ðõ?è²öÌôo?賨Y]’?è™3^èÒ?è©Ú;×?诉Öô&?è‹ÈpÊ[Ì?裤2 õT?è”ýE’P?è÷Æu?è¾pwÒ_ú?è”°“`-ö?è–gR€®Ê?è®Ö³Õº?è½*üvk~?èï·ýÖIq?èÃz+³Í?èÆmúãU?èàê\ø7x?èÞ®!ßT?èæÇ_i?èà,&•I|?èÏl3ÐY¢?èØiƒAÃÚ?èê\£ÂI?è÷õÜv¾`?éôG|?èÔËðLç?èÑ|5$?èÿ e“†?èæxîØ6e?èèÍ÷Êâ?è㤋þ7?èÊao´þ3?èØ ~Ÿ)?èìEÜœ?èÈy¶·û?èþ%Ñ næ?èɺþ{Ö?èÌÍÛqð?èÖêN “ ?èæ€Ç1 ?è㛟š§N?èìä>C?èöô¹\?èøŸï‚Ùi?èùTº”Ðñ?èÏv©Õ?è׳°T-ä?èëš•?èËôèÁw?èÐÊöý¿Y?èÓ$±ñc8?èÕ{ìâ“?èÅdñÌø?è̳qÔ`8?è×A‘³ò?èè¬âe”M?èßœhðVH?èÝ[ÏZÜ;?èÝžñ4?èÃåξ?èÌK v¢?èÜ“«²”W?èêígãÚÔ?èá²"[ez?èÇ”8J·?èæ@$šn ?èÏx‰ Rv?èÇFjqŒ?é)[þ4õ?éZF¿?éßwi?é 2 D?éOsy–˜?é7Vêšð§?é)ܘáñ?é6š ¸?é7ê7–µó?é ç{îsg?é:v¡^dø?éwšfë?éI±cÎ?é7,ÆC½?é"r ýã?é*ÅE;ö?é6çH9?éÕÓøã?é–o‡‚?é1Õ“3f-?éA4…Û’?é$Ñ¢á w?é$zÌZ*¬?é>æµä?é2ëšßõ?éµ&Û¾?éß W¸>?é<èÀpr?éCXju?é.Ûe’¹è?éNâòú;?é< DŠùô?é#tWÉ?éMqd›G?éSB,éÍ?é¼J/´@?é!l<û£ý?é þg¬®?é*t¡ùé?é!–e#?é 7>ºø¦?é4ÐÄyÃ?éƒ=~…ð?é(JqÀó?é1§b2?é*…xœø?éÊœód?év€@¬,?éLkÈÖ?é8Å6`ž?é-£¦‘U$?é4rŽ } ?é 8 ÐK?é<„•6»þ?é5’ ‰?é×â?Ã??é ‡9zƲ?é#r6C+?é*ÈUøß?é(UïÐE¸?é4a8“ü‘?é@GÁl{ ?é ¦°PÃÐ?éQGÌ?é@´¡&#y?鉿Z“F?é+@՗̼?é?º+v¤?éþ)ÿz?é%USãÃ?镞#²?é$»ÿGs??é#šÀ.?é4»«L?éÅ%LŽˆ?é)¢ÌOš?éMµ6Ñ‹?éÓû)s¿?é ÄbM?éCæ*?ée©èØ b?éknaø½q?éI¿u.%«?éEÜΈÀQ?éKõê[œg?énÀÞ^Ï?éƒR»£?éfF ÿÚ"?é` CœÙ?ér í¾?éj…2†êŒ?é‚ãEµŒY?ébpÕ½à?éb/ò|^i?éküKÙš?éPÁ\¶Ž?ébCë>?éhQ cY?éJ™s9í4?éHp(ãÍ?éh›Ä…»?éY†[%­?éqOU¹¬?é‚dø>h.?éu¡ÑÄ4?éZµ_—D?épõ3oâ®?邞!Àcý?éz`+atË?éÁØâ‰[?éFWn'ïÆ?éU¾I1VQ?édÐÒç>?éxâæT;?éb,‹•W ?ézÎ Z?éR…aœ8 ?é[¾eß?éLÊdظ•?éjw):Q?éIoŸ²Œ?é^°F§Yô?é_Þ—ÿMG?éqj'íÈP?éQœo‰l?éra%i,Š?éoêßÌ^ß?éi´+@äº?éF…v¬—=?éuÝÄ)6+?é_½5y‘Þ?ékS·­â?é{ °Uì?éhåƒYx¯?éw¶:›¤e?éb3ï?éHƒÃâ,Ÿ?ép´nž)?ézôú›å?é\Þa =?éj$šÖxÒ?éa%À­§€?éTO†Ù”à?é|•)C˜¢?éqÉBZþ?éœ/ʯNr?é•Yc”wœ?鈹Ac Á?é¿|óž|„?é¹Ì²ÒNß?é¡v#„?釤^Fl“?é†z?ü?éŠ9’m(¦?é¼´ƒKÄó?éžÚÆ0à?é†%˜ÊCG?é¤tU³*?é vO‰?霔ÉÏä?éŠý2Ó)o?é“ýr…å?é¿ùe%T„?é¬ÀKOÌÇ?éù&¿U_?é”­_uAO?é¬ÝZ+Ò3?é›@ý±²Ú?顿Ã?é´]WLh?é¾&Ïœ]?饮ˆdT,?éÁ̤É?é„®!¶†R?é¨s¶³¿a?é¾eoMa?é˜|WÂOT?鏯»Ö?é¯ïÁ ??éÂð’1kS?霋3Kp‹?é•#iHC?雿S^'¼?é‡å ô7¬?é²gbý„Ò?éŽgÅç.S?é Òc§mQ?é¼EmnÆ?éÀäÄm©€?éªXïc‹?é“âžFÜ?é…- Uÿ?éªo°¡À?é¡r”ÉBI?鮪o9áZ?é—œ(j?é³îmæ?é´+™>%­?é³ãBé}Y?骽"¯ug?éë®)ïh¾?éØÖt_V?éò£ùC=À?éÐþ¸‘Ð’?éËn°ñ„f?éá/ÉYÁå?êldÒè?éÉÚŦì?êãŠe[|?éè;*nÔ?éëÜ]Mh?éÝŠ¨2˜?éñiZËL?éÐ8/ y?éç]Y×Ç?éÛ\`Ê´ˆ?éǬք×Ì?éúBþ½=ø?éÚ ÁÒ?éÿëë8?éìH5ì!7?éñ5ü-?é䨣=©-?éýɆl“*?éò½–ª¸ïØd?ê$š®Þ-Æ?ê-s¢Eº¬?ê<’t«PÔ?ê;Ê#‰cµ?ê?X?êB¡$[?ê64·@š?ê œú\uÎ?ê3¯)=Ä|?êl÷›Rp?êtJd¤—?ê\órX?ê¹é)?êoXÉo?ê:’–H—n?ê(­] 8£?êø@cið?ê)2";?ê5;B¯+ç?ês—Òí?êY¥:9Ò?êÈx®üC?ꪊϗ?êzË熮?êd˜âS¥?êM½[U¯î?ê[¹¿«ºp?ê{m”ÏL×?êd¬›‡ôJ?ê|ÿRòé‹?êUò+–pN?êGÝbG?êfâ@ÔªÌ?êeÿhvë‡?êlÒ‰ÝÖ|?êrfàþƒÁ?ê}ÔÞO«^?êP½Hd¶È?ê^õk €?êO…àz?êmÜ=tý«?ê^÷ùÁ•?êd9wz(ÿ?êd’f¡‰?ê}L¢õ)„?êkÝ}Ûÿw?êrq؆0?ê| ä9?êvOÖ€?êJoso~ê?êY+YbOq?êjú÷?êtæ±ð~L?êFJëaÙ«?çuwñr?\?ç„ùh¦uZ?è¹”²Lž??è»Ü^ˆ??é‡+=?éá?¤bÒ?éÉ1gs˜?馛ãx?Lÿ@?Lþ¤?M€?M\X?Mqf?LH?L‚²?Lç&?Ltf?Mh?L)“?Lð‘?KÿQ?LµH?L }?L×k?Ll?LÚ¦?LÅ?Mà?L""?M(Ó?LG6?M7¾?LqÜ?MAò?L˜á?M@Â?L•¨?M/ú?Lys?M¡?LIe?LëŒ?L!6?LgM?LÒú?M?L3 ?L_N?L¼ð?M/£?Lpf?M³?LÒ?LÓf?L ?Lñ ?L ?M:K?LHð?MN¦?L™ê?Kå ?Lû>?L w?MU ?L·7?Kèð?M.×?LM¦?M`ƒ?LÞ;?L ?M.æ?L^+?MQ—?L³¾?KæÖ?Lòñ?LÊ?M ?L¼?LÙÄ?L Ú?Lž?Mœ?L7æ?L”í?Lîª?L*=?L‰3?M(¹?LOa?M=?L3‘?MKE?L\ñ?MW?L¢ü?K÷¸?MG?Lm¥?Ká°?M?L?NH@?JîF?N œ?K¬?Mß*?N$?M9¬?LIi?K‰2?K:?M®O?M”w?Lº'?KÓá?KŸ?M“º?M ?KèÌ?K7?M[‘?LvA?KÎ=?M+?L#?MA…?L¨ ?Kõ%?L»Ë?Kõ?M(?L4?M$?M 2?L("?M‰P?Mpq?L!?KÒ‡?M¯c?M—_?LçH?L z?K€ï?MÇn?Mø>?Mw?M(?Kì«?KQÇ?KwC?NVu?JÇ ?KXý?N´~?Jr2?K!?NSg?O‡a?JÝ?KY?N€ï?O¢?I­€?JüR?Nžä?O^Š?Iè?JüS?N›Ã?Iñ³?K,8?NI¥?J‘s?Jäà?NQ ?K?{?Mþ]?MáÅ?MVÅ?LŒ?K’æ?K ª?Mª¶?Mw?L©þ?L?Kuå?M‘ï?M@§?Lž?K¤?MsŽ?Lðe?KùP?M\B?Lõ4?L]?M,H?LTÌ?MF¾?LkE?Ma?LÔ*?Kð>?Mc?LËÍ?KÕ—?M—‹?MG ?LU*?K¬Õ?M½µ?MÂ?LÅ?KÂÑ?Kq0?Môÿ?Móß?M«.?LÄ?K‚4?KH»?MÂl?NPà?Jö+?MÆ@?JK?K5~?NÄÆ?J›…?O#j?JpÙ?KMf?Nþj?JH×?KHˆ?N¿ñ?JVÆ?MÒ?Ni?JR9?KGa?N(±?JõÃ?N.Ÿ?M–?Lò.?Kñ¯?Kj"?KW?M½?MS?L·h?KÄñ?K€Í?M(?M?L,(?K¡ô?Mb`?L±c?KØ€?M]Ð?L‹ì?KÙ¥?LéÓ?L i?M ?L)ˆ?MSø?LC"?Kìü?MÚ?L>Q?KÐ?MUÌ?LñS?K×Å?Mг?M”S?LË?Lì?K“Ë?MÀ?Nv?MdÇ?Lqv?K’?KF¹?N ?N,í?K)P?Míl?N”}?J°Ý?K,á?N©Z?J†‘?JÁ1?NEI?O À?Iâ€?Jþ ?Nz?O#?J?Ø?K??NdÈ?Nðè?J¹ø?N‘?Nn›?J¥o?Kgû?N>ö?K@?Kgš?N?Mã±?LÜÓ?L?Kƒø?KY?M¦µ?Md%?L„?K…?K“??MSê?LnÙ?KÀ¦?MeÎ?Líá?L;?Mm5?LÅ(?Kío?MO?L.?M?L,À?MXS?L~w?KõÐ?Mã?L7?MXÑ?L‰Â?K¶©?M¡A?MTm?LvH?K”©?M–J?M«S?LM„?KL ?Ki ?N§?K\?N/Ú?Jån?KY*?Ng@?J¹*?JáÈ?Nr>?Nú(?J™?MûJ?N§™?Jé­?KLÖ?N‹?JK‘?K}?N5Ö?Já!?KE€?N ³?K-?Krª?MÆ›?MY#?L‹þ?K´Ö?K<‘?M¸y?Mcˆ?Lh|?KÙê?M{¢?MpN?Ls?K£m?Mvš?L~S?KàL?M<„?LLË?M@?LŠþ?M<†?LŒ?MSÃ?LÅN?LŠ?MO…?LŒ?Mdµ?M1?L\ù?KÈ?MŠe?LÉ6?Kôº?K´M?M¹°?M)?L6™?Kx ?M¿û?Kwo?Mè¸?KT¹?N'q?Kl?N?NQœ?K(p?N©?KM?NX?KM?N ?K;æ?MÙ”?Ki¶?MÕ¬?M»?L‡«?K^š?M”>?Me"?Lqï?KÙ€?M„Œ?M 8?L·?KÊj?Mú?LO?MO?L¯ï?Kñ5?LÕƒ?Kö|?LªL?MH?L®?Kü?M:?L Q?MtÌ?L±?Kߎ?MŒ8?L÷{?KØ}?M‡é?MlÏ?L‰ë?K¹÷?K¹?MÌN?Lãn?LPÅ?K¡ö?M¿m?M±G?L¹#?L£?KXø?KpÃ?MÝV?MèÆ?Lµ?L@?K”Ç?Ki?NÄ?Mì?Lüz?LÞ?K}%?Ka?M C?L,Ê?Lú?L[?L¡Å?M6ã?LOú?M*E?LRS?MC¯?L\‡?MU·?LÝ?L~?MZ~?L¿8?Kç³?M‚÷?LÕ;?KÉ?Mš×?LÖõ?L†?K¯‘?M‡"?L­A?KÁî?K¿ ?Mc;?L ?KÜ?MŽð?MŠ-?Lf¬?KÄû?Mˆr?M;À?LZ/?KÈ—?M—½?M R?Kôë?Kš—?M3:?L,ì?KÝ+?MG?L?Kîâ?M(\?LÁ?MW…?L¶W?Ké‹?LÝ&?Lù?L¹x?L?Lƒþ?LîŠ?L z?Lœ?M5c?LE?M ?L;X?MM†?Lv!?MYÞ?LºH?KëM?MJì?L[;?Kìe?M^?L•?Me{?My?L 3?M?LF+?KÆ7?M???Ln£?KËõ?MWL?L«H?Kï ?MCï?LvÏ?Kàë?MK?LVŸ?KÊñ?MÀ?L•?M>©?LU?M<Ò?Lz•?M3þ?LmÅ?M?L*ó?Lµ$?Mr?L;€?Lƒ??Lú“?L#þ?LÛ©?L(?L¬?LU?L­‡?Kþ!?Lú?L!A?M0a?LrÆ?Ma|?LÒ?Kíh?M?[?Lzy?Kì ?M d?L¦?MM)?L‹C?Kçš?M®?L?MO ?LoU?MGÛ?L¶Ü?Kü?LÌ?KüÓ?L—Å?MÇ?LOe?Lâd?MÜ?LMð?L|‚?Mû?LFo?LÀ•?M+$?Li¬?M!Õ?LMX?Ls?L81?M(,?LFÑ?M> ?LyŸ?MQ3?L—o?Kï?LÄš?Kú,?LÓñ?L É?LÈR?LF?L G?L”»?MÇ?LC4?LÌÍSî>ËÑ >ËÙr>Íž>Ís>Í·Þ>ÍéÐ>Íäe>ÍÑÀ>ÍŸx>Í€F>Í{>ÍY>Ízç>ÍA >Ílà>Í 3>Ìö¥>ÌÒ†>ÌÌÉ>Ì œ>Ì¢ >Ì‘‚>Ìx>>ÌM´>ÌYØ>Ëìà>Ì#X>Ëz>Ëöó>˾>˾b>ËŸ>Ë’ê>ËÜ‘>ËHÚ>Ë_>Ë‘i>ˤ+>Íñ$>Î2_>ÍŒà>Î¥>Íè>>Íàð>Íò >Í© >Í÷W>Í´ª>Ípû>͹>Í@>Íf‹>Í l>ÌçÀ>Íç>ÌÀ">̾Í>̰^>ÌŽq>Ìe+>Ì‚>Ìã>Ì:ö>Ìð>Ë¢>Ì%‘>ËœÂ>ËÙ >Ë„>ËÊA>Ëj¬>Ëwf>Ë!r>Ë·,>Ë!‘>ËvO>Ëq>˯>Î[>Ͷ…>Î_Q>ÍÐF>ÎE£>Î**>Îv>;">ÎK.>Íiî>Î02>ÍN>͹A>ÍëÑ>Í;x>ͬ]>Ípx>Ìó|>ÍK€>ÍŒ>ÌÖ6>Ì™>ÌÚ2≯l>ÌHT>ÌPì>Ì‚a>Ì>ô>ËìD>ÌGB>Ë®¬>ËY2>Ëý“>Ë t>Ë 5>ÌŸ>ËGË>Ë›>Ëy >ËN>Ë–>Ë ½>Ës>ÊêS>Ë?¢>Ë03>Êëö>ÍûÓ>ÎHŒ>κ>Î`>γr>ÍØf>Îf™>ÎH>Íú>αe>͵^>Î@Î>Îi5>Íq˜>ÎÛ>Îà>ÍqÏ>Ít½>Íúk>ͦ>ÌÜ›>Í–>Íž;>ÍFÉ>ÌÆ>ÌñÝ>Ìݤ>ÌïW>Ì™ß>ÌVw>ÌŠ>Ì`Œ>Ì5U>ËÔ†>ËÙå>Ì'Š>Ëtµ>Ëw_>ËØ>ËÚb>Êö'>ËXW>Ëïë>Ë,Î>Ëd>Ë®Ž>ʵJ>Ë:„>Ë©>ÊÁà>ËMú>Ê—¢>ËFn>ÊÂE>ËIî>ÎÔ˜>ÍúÂ>ÎÈì>Í®è>ÎÌ>ÎÈ>ιÕ>Τ>Íßh>Îï >ÎÄr>Í»5>Χé>Îv®>ÍŸ'>ÍèÇ>Ï >Î5ö>Íf÷>ÎS>Îx >Íåº>ÍW¡>ÍS >Íqb>Íò6>ÍBå>Ìùt>ÌØ˜>ÍFç>ÍiÁ>Í"æ>Ì™X>ÌP/>Ì> >Ì€>Ìuá>Ìhh>Ëôš>Ët¾>ËÁ›>Ëá>Ì ,>Ë©>Êîç>Ë`Ž>Ëõ!>Ë@·>ÊQ„>ÊÕ•>ËÒv>ÊÔý>ʘS>Ë:>Ë%p>ÊOô>Ëv>ÊÈ¡>ÊFÁ>˶>Ênæ>Ë2~>Ê´Ê>Ëe>Ê¿?>Ëx>ÎþX>Íë>Ï>ÍÆ >ÏC>΂Ï>Έ+>Ï(á>Î_>ÎVØ>ÏŽx>ίÿ>Í“N>Ïð>Ï6>΀ú>Í]>ÎoÀ>Ï»Ã>ÏA–>Î%a>ÍoÅ>ÎI«>Îí˜>ÏT>ÎB>Í+>ÍW>ÍËå>·>ÍÒ‚>ÍÔQ>Í"E>Ìù!>̺§>Í/>ÍD\>Í|>ÌúX>̉Ÿ>Ì!>Ë£>Ìü>Ì"¤>ÌN>ÌnE>ˤ>ËÂ>Êö>ËŒÓ>ËÜ>Ì:>ˆ>ʈN>Ê4Q>ÊÈÉ>ËLå>Ëœ>ÊK«>Êh>Êx >ˬï>Ëò>Ê4¤>É×o>Ë7f>ÊÎ>Éç(>ʇ¸>Ëj†>Ê'³>Êv/>˨>Ê>Ë2>Êa>Ê‘i>Ê¥{>Êú9>ÎϦ>ÎBL>Îõ!>ηÁ>Ωë>Ïjš>Άë>Îù•>Ï‚¥>Î_T>έ>Ï¿M>ÏwM>Î:>΀‚>ÏÊx>Ð<Ì>Îè >ÍÙû>Îjä>΢>Í/b>ÎU„>Î.ç>ÌüÁ>;Œ>ÍjŒ>̱Ó>ÍV>ÌÒ >Ìhç>ÌxD>Ìi˜>Ì >Ëi >ËÉ->ËòY>ÊãÞ>ËD_>Ë©Ø>Ë5>Ë>}>Ég_>É@>Ƀ>Ë>ʈÑ>ÉXd>ÉÒ&>Ë.‡>ÊDC>É®€>Ê™Q>Ê_+>Éá>ËŽ>ÊJF>Ê“š>Ê´>Ê–M>Îä >α¯>ÎÕû>ÏE2>Íü>Ïd>Ï®7>Íúâ>Ïk/>Ïóò>Î],>ÎÅ>Ðm€>ÐHÛ>ÏC×>Í·>>Ϧk>ÑÀ>Ïy>Íÿæ>ΑY>Î̼>Í|î>Î2>ΗR>Í(›>Íä#>Ím>Ìô`>Í2>Ìçä>Ìue>ÌX7>ÌZ >Ë1¸>Ëí>Ì7c>ËZ}>Ë->Ë®M>ÊÛ”>Ê×>ËÆÕ>ÊÈuà>ÈŸ>ÉF>Êü±>ÊúÀ>É©R>ÈÞð>Ê*‚>Ë9K>ÉŽS>É?ö>ÊÓ8>ÊV>É”À>ÊÁ*>Ê,>Ên>Ëý>Êz>ÎZ>Ï4³>Î@>ω>Îö7>Îð‚>Ïåš>Îßn>Îú>ÐP4>Ð-»>Îo>ίÈ>ÐZÉ>к˜>ÏÁÐ>δ>ÏP(>Яß>ч¥>Њ>Ð,>ÍçÕ>Îг>ϳ>ͱ>Î ø>Î!™>ÍcŸ>κÕ>Íœd>Íb>Í>Í)è>Ìq€>ÌRá>Ì>Ì #>ÊôQ>Ë\ë>Ì4s>Ë·ë>Ëk >Ëx©>Ê„'>Ëw>É‘‚>Çä®>Ç_¯>ÈÛ>ÊFD>Ëlÿ>Éšâ>È€Œ>Éî>Êìä>ʆg>É(ò>É >Êî:>Ê==>É7&>Ê¡)>Éý¥>ÉÚ¬>Ë*—>Ê>ËU.>ÏW‹>Î:¢>Ï%b>Ï–>Î Û>ϺA>Ïø>ÍÈ>Ïl >Ðkz>Ï‘q>ÎÎ>ÏÄÄ>Ðä>Ñ"`>Ï{H>ÍÔÑ>Ï“Ã>Ñ•›>Ò:ß>ÑUì>Ð “>Î)³>ÎO>ÏJ>ÍÛ­>Î8Æ>ÎŒ>Ͷç>ÍeY>ÎX9>΄L>Í]Ð>ÍÎ>Íãö>ÍBð>Ìõ0>Ìo†>ÌfÓ>Ì!¤>Ì=q>Ì 7>ÊÔR>ÌŽ>Ë$>Ê:ã>Ë%F>Êø>ËUÚ>ʱ >É>Ç;ì>Ç0Ø>È”q>Éã1>Ë{>ÉWà>Çäv>È”µ>ÉÿÝ>Ë)ñ>ÉTŒ>È‘Ý>ɹˆ>ÊÒ„>ÉQ´>ɺ1>ËO“>ɨ>Êi’>Ê€‚>Ê)À>Î}i>Ï1G>Î-¸>Ϧ¹>ÎÌe>ÎÈÍ>ÐI>ÏÏ>Îx>ÏâT>Ðn>ÎþÁ>Ϋ1>Ð6Ô>Ñ·Æ>Ðñ>Î(>Îm¨>Ïí°>Ñ›>Ñé>Ðs>Ï[4>ÍFç>Ï…‘>ÎsŠ>Í8K>ÏKâ>ÍY¿>ÍŸ`>ͳå>Ìø†>̤ÿ>ÌÝÄ>ËÈÊ>Ëâs>Ì’¬>ËOœ>Ë…²>Ì‘>Êö¥>Ê%1>Ëü;>Êp>>Ê$¸>ɼÜ>ÈÁ>ÆàÖ>Ç\>È;¦>ÊëI>Êáê>ÈÜ >È‚>ÉÎ>ʬ&>ÊÝÖ>ȶý>É(®>ʘ®>ÊQ¥>É0>Ê+ö>ʃF>É–>ÊöÍ>Êà>ÊŸN>Ï >ξŒ>ÎÎ>ÏŽ>ÍíH>ÏÏL>ÏÊ6>Íú¹>ÏŸ;>Ðpô>Ï¡>͹º>ÏŠ*>пî>ÐÿN>Ï#>Î +>ÏÄÑ>ÑVË>Ñß>Шú>Î’>ÍÔ>Ï".>ÎŒã>ÍZw>Κd>Î`¼>Ía>Í >Î(ä>Í…í>̾ï>ÍY>Í6>ÌÍ>Ìs>Ì>>ËÉÇ>ÌPÛ>˵ >Ë8\>ËqP>ËàR>ÊZò>Ê»Ÿ>Ì „>ʸh>ɾC>Ëx>Ê£>ÈÔg>Çk>ÈÊ>Épª>Ë#>Êrù>È‚³>È\»>ÉË(>Ë]¯>ɼé>È®ø>Êz>Êü>É‹ÿ>Éí>Ë s>ÉÚ>ÊE>ÊP >Ê>α5>ÎÌU>ÎUï>Ï{2>Î!>Ïp“>Ϙ½>ÏhM>Ð9(>ÎÎ>ÎŒß>϶2>ÐЄ>ÏRô>Í >Ïh’>Ð]V>Ïf…>Íêm>ÎlL>ÍþÞ>Î)‡>ÎXä>Íjw>Íg—>Î`>Í.¿>̺µ>Íx…>ÌË»>Ìk>Ë¢µ>Ì`k>Ì•,>Ëú;>Ë…{>Ì«>ËeÅ>ÊÎQ>ËçÉ>Ë à>Êp%>˲Ó>ÊZØ>È¡Œ>ÈXH>Èå>Ê´>ʶ>>Éš>ÈÙw>É—Þ>Ëlô>Ê~>É8u>Ê®">Ê8>ÉQ¬>Ê€r>Êq2>É÷¨>Ë%„>Ê-M>Íüg>Îó€>Íä?>Ï52>Î>x>Ïþ>ÏIo>Í»m>Ï@Ø>ϾY>Î*>ΤÂ>Ð)>Ï Y>Íßj>ÎÈ÷>Ï´©>Ïøù>Îè>Ía >Î@>Í\Þ>ÍŠŸ>ÍdÙ>Í^ô>̾>ÌÔ>Ìðf>Ìbæ>ÌL‡>Ì'%>Ì %>Ë»÷>Ëdß>Ë1j>Ëc>Êä>ÈѼ>ÈâÇ>Ê’ß>Ë¿4>Ê >ÉK”>Éðó>ËYÆ>Éà$>É­[>Ë]Ã>Éÿv>Ê4>Ë3Z>Êd>Ë%²>ÊE€>Ë7ù>ÎÊ_>ÍÍ>Îë¹>Íó»>ÎÿQ>Ϊu>ÎL†>Ï[9>ÎvR>ÎuR>Ï>Îפ>ÍbP>Îh>Ï|÷>Ϋ°>Í}>ÎJc>ÏR˜>Ï â>ÍùR>Íx¶>Î0>ιÃ>ιÎ>Îw>Í4~>ÍE6>Î6”>ÍîÌ>Î"b>Íàa>ÍC`>Í C>Ìï¨>Íí>Ìã‚>Ìé>Ìdž>ÌŠ¾>Ëÿ€>ÌO,>ÌV=>˳€>ÌW>̈n>Ëü¾>ËŽO>ÊÑ9>Ê^ >Ë:³>Ì;¹>˼æ>ÊÀÕ>ÊšÐ>Ê¡ƒ>Ë¡ç>Ë s>Ê´à>ÉïS>Êqœ>Ëjn>Êëò>Éåw>Ê)D>Êô.>Êò>Ê=j>Ê^ø>Ë¡à>Éñ!>Ê4a>ËA'>Ê5û>ÊÚ->Ê—û>Ê ä>ʤO>ÊÅ>ÎbI>Íßë>Τ>Íà>ÎõL>ÎOÜ>Î>ΦV>͘t>θp>Τ­>Í=Q>Îg¿>ÎÙJ>Î ú>Íì(>ί‚>ÎnF>ͪ¢>ÍÉ>ÎFa>ÎU%>Í„½>Ìó >͇>ÍÚ>Í›{>Í)Ø>Ìäq>Í`>̹z>Ìíÿ>ÌÏ‹>Ì„_>Ì}z>Ìx>Ìsì>Ì`7>ËÈ•>ËÐ_>Ën>Ëù•>Ì>Ëžq>Ëu>ÊÚd>ËÑH>ËW’>ʼ>ÊZb>ËÉš>˳>Êe¸>Ë—>ËR¬>ÊwÇ>Ë«>Ë$ >Êh¾>Ë^C>Ê•F>Êñ¹>ÊÞŠ>ÊØ†>ÊÌï>Ë5Ö>Îp>ÍðÚ>Άë>Îe>Îe÷>΢>Θú>Íží>Ϊ>γ>ÍÝ>Λ >Íõj>Í{+>Î0‹>͸Ò>Í4‘>ÍÓ€>ÍáÀ>ÍW>ÍB¶>ÍWM>Í5>Ìâï>̼n>Í-¸>ÌšÌ>Ìqø>Ì–>Ìc¯>Ìo…>ÌKB>Ëêñ>˽>ÌF«>Ì>Ë`ˆ>Ë€f>Ì8>Ë\ >Êâb>˦1>Ë?)>Êúë>Ë|°>Êܳ>ÊÇ4>Ëœ>ʶ„>ËzÖ>ʼ½>ËCª>ʽ>Ë—x>Êàþ>ÎÑ>ͬI>Î;>Íóì>ÎU">Íü3>Î>ÍÍ+>Ζ>Ía]>Î P>Í”d>Íxý>Î 7>Í0a>ͺ2>Í¥l>ÌÊb>Í]d>Í*Œ>Ì««>ÌÉK>ÌÁM>Ìf>ÌB¢>̦b>Ì.š>Ì >ÌJx>ËÕß>˧0>Ì ó>Ëyô>Ë`Z>Ëî=>Ë@>ˉ—>Ë~7>Ë"õ>˧.>ËX>ËŒE>ÊÖ¢>Ë7Ê>ËH >Ë æ>ÍÝ\>͸a>Î(ì>Íä=>ͼ>Î$ÿ>Íkí>Îá>Íe°>Í´0>Í‚ø>Í–>Íð>ÍHd>ÍvÒ>Ìö„>Í2ã>Ìü>ÌËO>ÌÜ=>̲>ÌsÐ>ÌZ´>ÌR™>Ìé>Ìj>ËÑ›>ËÖ>ËÍ>>Ë‚²>Ì)>Ën»>Ëá >Ë< >Ë·A>Ë%?>ËŠ>Ë4å>Ë&C>ˤS>Ë>”>ÍæÒ>͘p>ÍÝT>Íì >ÍWë>ÍÒ>Íp>Íš×>ÍR^>Í'>Ìö->Ìá²>̵>Ì¿Œ>Ì‹ó>Ì‹P>̇®>Ì\ç>Ì…¦>Ì›>Ì<×>ËÉY>ËüE>Ëñ\>Ëtl>Ë¿N>Ë€>ËD8>Ëžp>Ë”m>ËHÍ>ÎÅX>ËŒ>Ï!>Ì’9>Ïj>Ï:>ÍG_>Ë Q>νÚ>Îyd>ÍŽÇ>Ín|>ÍM>Ͱ+>Î J>Î-O>΂>ÎVò>Îf€>ÎÀ>Î ·>Ï/>Î[>ÏQ>Î;­>Ï5@>Î~¡>Ï!J>ΰ›>ΰÙ>Ïj>ÎF>ÏGM>Íú,>Ï4‚>Íáº>Îþã>Íä>ÎÈØ>Îg>Î…¨>Îgd>Í·´>Î=¢>΢>ͨ%>ÍœÁ>ÍÞ|>Î08>ÍÄ>Î[z>ÎF>ÎC>ιö>Î3ý>ιý>Î;>ÎÖ­>Îüƒ>Î*”>Ïy¥>Î& >ÏE\>ÏV>Î1I>Ï¿r>Îz_>Ï\ö>Ï»>Íìü>Ïœ]>Î÷[>κí>Ï^Í>ͪ†>Ï'>Îbÿ>αi>Îr$>ÎZV>Î^é>ÎT}>ÍÍ‹>Îf`>ÍÊF>ÍëÃ>Íì†>ͺ$>Í€¶>Κ>Íå>Î'®>Î6>Έ>Î`Î>ΠÉ>Î>Ï >Î,Z>ÎÜÄ>Ïyî>ÍçÇ>ÏZD>ÏvÅ>Íæ>Ïb[>Ïš²>Íÿí>Ï”‚>Ð7Ò>Î@:>ÏCc>ÏÜ[>΀€>ÎûÜ>ÏÚÖ>ÎáÆ>Ï Ñ>ϲI>Θ >Ϊg>ÏCF>ÍÔ¶>Ï.>έî>Î:y>ÎÃ{>ÍÔ>΄é>Ͷ>ÎMR>ÍÞ2>ÍÖ">Íü‚>Í„¥>ÍŸ>Îï>ÍŽ>Îq@>ͧ8>ΔÏ>Î0>Î=ß>θÒ>ÍÉ]>ÎÑÖ>Ͼ>ÍÂØ>ÏhÍ>Ï` >ο>Ï®>ÐL¦>Ï@’>Î>Ð*ó>Ð >ΩÖ>Î|„>Ðn·>ÐjL>ÎÍ}>Ϧ>а‰>СC>ÎuŽ>ÎÉc>Ðnä>Ïý\>Î3F>ÏaÊ>ÐX>ÏH>Î>ÏãE>ÎñU>Í«K>Ï ¦>Ϋ—>ÍØc>Ïí>ÎXD>Î6²>Î,>ͺ§>ά>Íq>Íü->Í©>Íîé>ÍU>Íü^>ÍŽ>ÎXÇ>Íž²>Î^£>ÎaW>ÍÓM>ÎÊ >Î)>Îp>Ï&û>Ïð>Íš>ÎÕ†>Ïð”>Ï_“>Í >Ï4‰>ÐÒì>Ïøa>ÎS{>ÎÜk>Ð`>Ðvê>ÏñÂ>Íäl>Ïx£>ÑI>Ñ í>Ïõ¾>Íî>ÏÀa>ÐÍù>Ñ4x>ÏÇ>ͼØ>Ï>Ì>О¥>Ña>Ï>Κ>ÏÉš>й™>Ϻ©>Íî–>Îóø>ÏåÒ>ÏD‘>Í¿0>ÎÒè>Ïlw>Î<>Î_>ÎÝ>Î#•>Î7>΄‘>͆E>Îeâ>ͼÎ>ÎÙ>Í;>ÍÄë>ÍTË>ÍŒ»>Í<‰>ÍÀ§>Í9(>Î8>ÍÎ>Íîu>ÎYÊ>ÍÚÞ>Íöµ>ζt>ÎmO>Í¿>ÎËg>ÏG->Îê­>Íž‹>Îþô>ÐK>ÏÝÛ>ί>Î&­>ÏtÆ>Ðíu>Ðw>ϼ£>Í‚£>ÏSÚ>Ð^N>ÑÐ>ÑR>Ϫ>>ͺ½>Ï»>ÐþC>Òa>Ò-V>Ñ9å>ÎÄ>ÎEï>ÐO>ш{>Ò+Ä>Ò<ë>ÏNÎ>Íèi>Ïã>Й>ÑK7>Ñ8]>ÐEŸ>ÍÖ“>ÎfÂ>и>ÑoÛ>ÐX>ÎÜÝ>Íø“>Ï1>Ϧœ>Ð>ÎÎ/>ÏIî>ÏŠ¹>Î>Íÿž>Ï®>ÎAî>Í{2>Î@ >Î3Œ>Íp‰>Κ>ÍŸ–>ͯÙ>ͳ±>Í‘ÿ>Í@%>Í|á>ÍBX>Í€å>Í‚ð>Íœv>Í´`>ÍS>͸>΀Ì>Í­ >Í“à>ÎÄÙ>Î}Â>Í»Ñ>Î3ÿ>Îô˜>Ïz">΄…>Í{Ž>΂É>ζÃ>ÍŒM>Ï+Ö>Î’±>Í›ž>Îÿö>ΉÛ>̓@>ϘÒ>Îû->ÍÇþ>Îû>ÎÝu>Ͳ>Ïa4>ÎfÙ>Ígh>Îâ">μ>Í„>Σ>ÍÕÖ>ÎÙÐ>Ï,ù>Îô£>Í’Ÿ>ÍÝ>Τ¬>ÎËê>Íqõ>Î~>Î-f>ÍÐT>ͽY>Î>>Ío>Í| >ÍLë>ÍZ*>Í[‘>ÍQr>Í5>Í#Ô>ÍG>Í>>ÍÁœ>Ͱ¤>Í8¾>ͪ>Î0>ÍS|>Í«>ÎeÒ>ÍúX>Ͳ©>ÍF1>Íö~>ÎÇ >ÎGÈ>Í]?>ÍÉ7>Îx\>Íx£>ÍË¡>ÎÇŽ>Í,;>Î^+>ζ0>ÍŒ`>Íþ5>Î >Íx >ÎJ=>ÍF{>Α7>Îͦ>ÍpÂ>Íâ<>Ζÿ>ÍJ>Î ï>Î{;>Í<¸>Íë„>ÏN¡>ÏVÃ>Î{\>Í‚_>Íxe>Î?>Î?˜>Í–>Í7 >ÍüË>Íé >ÍoW>ÍtL>Íx>Í»>Í@>Íq¢>Í>Íeì>Ìø>Í"„>ÌîÒ>ÍKI>Ìî6>Íq>ÍfÙ>Íâ>Í$‹>ÍÕ>ÍU¢>Ím>Í ,>ÍÅk>ÍÙ>͆í>ÌàÎ>ÍŸá>ÍÚ[>ÍØl>Ή>Íyq>Ì÷„>Í‚v>Î >ÍHË>Íd_>Í•>ÍHž>Íöú>ÎÖ>Ío>ÍËq>Í~Ÿ>Ìô>Îó>Î >Ìôò>Íàª>Îwõ>ÌÿÁ>Í:_>ÍHí>Íò>ÍwI>Í&©>͈>ζ >ÍÝ7>Îd>͇u>Ín>ÍE”>Í®>Í«o>Í#ž>Í·>͈Z>ÍDþ>ÌðR>Ì÷B>Í5*>Ìô¼>ÌæË>Ìú€>ÌÆ‘>Íw>ÌÊó>̨h>̰ç>̬†>ÌÌ>Ì¢¾>ÌíF>ÌÅË>ÌÍ>̾Z>ÍÉ>ÌÏg>Ìè´>ÌÒÏ>ÌÅÝ>Ì´H>Ìòk>Ìè>Í'&>ÌÜZ>ÍXú>Í9>Ì·»>̪Ë>Ìœ\>ÌÓâ>Ìš“>Ì©>ÌÁ}>Ì­>ÌÝÜ>Í3–>Í1ó>ÍG>ÍÌ>Ìò>ÍÐ8>̳Ç>ÌÈD>Ìî >ÌÝÐ>ͦ>Í>Í$>ÌÇÌ>Í%q>Íp›>Ì­œ>ÌÑ(>̾/>Í Ÿ>Ͷ>ͯ>Í·—>Ìíi>Ìæ”>̘—>Ìùž>Ìó>Ìη>ÍÑ>̽å>ÍÜ>ÍÜ>ÌÆò>Ì»ž>ÌÔ8>Ì·2>Ì­[>Ìêž>Ìˤ≯»>ÌÇl>Ì“Œ>Ìšÿ>Ì„t>Ì_O>Ìm'>ÌuI>ÌDŒ>Ì|>ÌP©>ÌT>ÌVN>ÌX³>ÌsÒ>ÌJ:>ÌuÄ>ÌG>Ì^V>ÌHÃ>Ì$>ËÔþ>Ìm1>Ì(x>Ì“Q>̧Æ>Ìgñ>Ì25>Ì…Æ>Ë¡ð>Ìk×>ÌLÁ>Ì|>ËãT>Ìt&>Ì>̨>Ìq#>Ì“‚>̤y>ËÌ}>ÌŠj>ÌF4>Ëês>Ì‚Ø>Ì4ó>ÌK¡>Ì‚(>Ìä>Ëæ‡>˵>Ëü»>ÌI?>Ì_x>Ì-O>Ì€6>Ì9>ÌiÁ>ÌŒ^>ÌPà>Ì#“>Ì^¨>Ì‚:>Ìl*>ÌŠ•>Ìt>ÌØ>Ì;>ÌdP>Ìv>Ì]w>ÌJ¸>Ì!>Ì­>ÌxW>Ì%$>ËÛ†>Ìg>̦>Ët(>˶U>Ì]ò>Ë­à>ËÆœ>ËXñ>Ëé‡>Ì9Ç>Ë…Ï>Êþ¬>Ër?>ÊÂS>ËÊ >Ì Ÿ>Ë„'>ËïE>Ì&“>Ë“>ÊðW>ËüÕ>Ë‹>Ë à>Ì*~>Ì~>ÊÒ8>ÊÊ©>Ì)7>Ë•·>ÊÈ`>Ê¿Í>ÌÒ>Ì!>Ë>Ë‘”>Ì*Š>Ë£€>ËB>ÌJ©>ÌÖ>Ëçu>ÌNŸ>Ëõ>Ël¶>Ê}ã>Ë >Ëc>Ì#Ñ>Ì$>Ë>ˤÇ>ËŠ >Ì#®>Ëô¹>Ë„„>Ëë1>Ì+ä>Ëý>Ì‘>Ìgw>Ì)>Ì?1>Ì.e>ÌCO>Ì>ÌM>ÌÇ>ËâÙ>ÌXñ>ËÃW>Ë»>Ëf’>Ët”>ËÃ$>ËÉ”>ËÓ>ÊÉU>Ël">Ì>ÊÕ>Êp?>Ë#>Ë»ð>Ëzú>ËÔ…>Êûð>Ê‚ >ËÚ¯>Êë—>Êà{>ËŒØ>ËOZ>Êi@>ËB>Ëó~>Ê->ÊØe>ËØG>ËG)>Ê+©>˾ì>Ë>Êv8>ËÚ >Ë">Êhì>ËÏë>Ë*­>Êù>Êtß>Êu>ˤÈ>˼W>Ë ">Ê“b>Ëjë>Ì2¼>Ëb>Ë€ï>ËÇè>Ëߘ>ËŒ—>Ëй>Ìf>ËåL>Ì;>ËÓ‰>Ì÷>Ëä¢>Ì@ñ>Ë¡)>Ì›>Ë^M>ËH‘>̈>Ë>>ÊÀ½>ËÉx>Ë >Ê}¦>Êá)>ËÆw>Êè…>Ê>>Éÿ[>ËÏ>˾f>ËWj>˦>ËU>ËK=>Ë >Ëew>ÉÜ>Ë>Ë[Š>Ëlú>Ê×¹>Êû§>ËZ>˰>Ë3X>Ë—Ë>Ë@Ã>Éä->É¿x>Ë Ï>Ëýì>Ë©>Êdå>Ê­§>ËÜ—>Ë1>Ë4µ>Ì @>Ë>Ë^>Ì>˧ý>Ëý³>ËÇë>Ì,>˨ñ>ËÖf>Ëe‘>Ì>Ë=Ô>ËSŸ>Ëw>>ÊÔ„>Ë:D>ËHT>ʯß>ÊÇ>Ëܳ>Ê:Î>ɶ™>ÊSË>ËÀL>Êr@>ÉzÝ>É9ø>ÊÙ¢>ËBE>Éß>È›‹>É&–>ɯ>Ë~ >Ê}‡>È͵>ÇqQ>ÇO7>ÉH >ËË>Ê9>Èmƒ>Ƈ>Æíq>È:¾>Ê€æ>ËMì>Étµ>Çÿ$>Æú >Çú¨>É}S>ËfŠ>ÊŸ‹>ÈK€>ÆÞö>ÈÞ>ÉGÓ>Ë)4>Ê•ö>Éo>È:d>Èþ`>ʨÇ>ËM’>Êú>É2">Ɉ¯>Ë6Ó>Êå>ÊU>ɯq>Ë2Ê>ËTu>Êp‘>ÊÞu>ËÖ>Êܾ>Ê´&>ËÅ>Ë0­>Ë>Ë}5>˨>ËȦ>Ëæ:>ˆ>Ëáƒ>Ë,>ËÄ>Êù>Ë~>Ë>Êýð>Ë´7>Êv>Ê«ï>˃g>Ê;Õ>Éô>Ë-ã>Ê“#>ÉÏ>ɽZ>Ëþ>ÊJo>ÈïÏ>Ɉ>Êo«>ÊÒf>É[1>Èó>É7E>Ë>Ê*±>Èþ™>ÈR†>ÉR>Êç>Ê_H>Èoè>È ¯>ÉGá>Êöf>ÉË>Èå>ÈÎý>ÉÍ»>˘Í>ÉÊÎ>ÈÈ{>ÉGÊ>Ë6»>Ê™Ë>Éø>ÉÓi>ËBn>Ê”T>Ê G>Ê­N>Ë0>ÊA²>Êö >Ëns>ÊÞÒ>ËœH>Ë*I>Ë9@>ËT>˃J>Ëm>ËÂ\>Ë^“>˯÷>Ëm'>ËOÐ>Ê÷Ÿ>Ë0„>ÊÀf>Ë‹v>ÊZ >ÊÝ’>ÊÜ5>Éå0>Êí®>ʳ{>ɨ&>Êil>Êü²>É’¨>É×ÿ>ÊÚæ>Éøa>ÈäŽ>Ɉ>Ëvù>Éšr>Èøl>É~ý>Ëk{>Él·>È×G>Ê6>Ë)ÿ>É…E>É*õ>Ê &>Ê—*>ÉÍ>ÉnÊ>ËS5>Êû>ɳ[>Êé>Ê¿z>ÉÿC>ËH>Ê•">Êlì>Ë‚v>ÊÂ>Ë^…>Ë.>ËY·>Ë.×>ËÀ–>Ë{.>Ë}©>˵ >Ë Î>Ë`A>Ë$>ÊÚô>ÊÊ0>ÊïÚ>Êœ©>ËBê>ÊÈ>ÊÏp>Ê’®>Éê™>ËpÇ>Éò\>ÉÂù>ËZÍ>ɦR>Éí>É>ÉŠ1>Ëq>Éã«>Énu>ʶF>Ê$‰>Éq€>ÊR’>ÊE6>ɪ>ÊëH>Ê^÷>Ê+>Ë%Ë>Êsü>Ê·Ä>Ê÷ã>Ê~{>ËGµ>ÊÓ[>Ëža>ËÏ>Ë|Û>ËsÍ>ËZ'>Ëy>ˤj>Ë2N>Ë" >ËLï>ʱ®>Ë–ù>Ê›Ä>Ë}4>ÊCd>Ë í>Ê8Ö>Ê—X>Ê£·>Éß•>ËP>Éóò>ÊgÄ>ÊMß>ÉÃ?>ËSê>Éô>ÉÙ)>Êç>ÉÅQ>Êõ >ÊÊB–>Ê·(>Ê1>Ëmú>Êh–>Ëej>ÊŸ>Ëbß>ʲü>Ë€½>Ë!º>ËB>ËË‚>Ë\¢>ËB)>Ëx%>Ë6¯>ÊÜ>Ëdˆ>ÊÂg>Ë(>ÊšF>Êœ#>ÊUP>ʦž>Ê4Õ>ÊÖò>ÊÅ>Ë*^>ÉôÏ>ËIœ>Êw>Êõ¡>Ê4>Êæ¢>Êoi>Êë\>ËOè>Ê«9>ËJ>ËË>ÊëÉ>Ëxœ>ËxÐ>ËEf>ÎøÛ>ÎÕ_>ʺj>É”‹>ɧ>ɯ>É >ÇþqCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæffCæff3a3†4&5%53?{?|?}??€??‚?ƒ?„?…?†?‡?ˆ?‰?Š?‹?Œ??Ž???‘?’?“?”?•?–?—?˜?™?š?›?œ??ž?Ÿ? ?¡?¢?£?¤?¥?¦?§?¨?©?ª?«?¬?­?®?¯?°?±?²?³?´?µ?¶?·?¸?¹?º?»?¼?½?¾?¿?À?Á?Â?Ã?Ä?Å?Æ?Ç?È?É?Ê?Ë?Ì?Í?Î?Ï?Ð?Ñ?Ò?Ó?Ô?Õ?Ö?×?Ø?Ù?Ú?Û?Ü?Ý?Þ?ß?à?á?â?ã?ä?å?æ?ç?è?é?ê?ë?ì?í?î?ï?ð?ñ?ò?ó?ô?õ?ö?÷?ø?ù?ú?û?ü?ý?þ?ÿ@@@@@@@@@@ @ @ @ @ @@@@@@@@@@@@@@@@@@@ @!@"@#@$@%@&@'@(@)@*@,@-@.@/@0@1@2@3@4@5@6@7@8@9@:@;@<@=@>@?@@@A@B@C@D@E@F@G@H@I@J@K@L@M@N@O@P@Q@R@S@T@U@V@W@X@Y@Z@[@\@]@^@_@`@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z@{@|@}@~@@€@@‚@ƒ@„@…@†@‡@ˆ@‰@Š@‹@Œ@@Ž@@@‘@’@“@”@•@–@—@˜@™@š@›@œ@@ž@Ÿ@ @¡@¢@£@¤@¥@¦@§@¨@©@ª@«@¬@­@®@¯@°@±@²@³@´@µ@¶@·@¸@¹@º@»@¼@½@¾@¿@À@Á@Â@Ã@Ä@Å@Æ@Ç@È@É@Ê@Ë@Ì@Í@Î@Ï@Ð@Ñ@Õ@Ö@×@Ü@Ý@Þ@ä@å@æ@í@î@ï@ö@÷@ø@þ@ÿAAAA A A AAAAAAAAAAAAAAAAAAA A!A"A#A$A%A&A'A(A)A*A+A,A-A.A/A0A2A3A4A9A:A;ABACADAMANAOA[A\AhAiAjAsAtAuA|A}A~AƒA„A…A†A‡AˆA‰AŠA‹AŒAAŽAAA‘A’A“A”A•A–A—A˜A™AšA›AœAAžAŸA A¡A¢A£A¤A¥A¦A§A¨A©AªA«A¬A­A®A¯A°A¶A·A¸AÂAÃAÄAÒAÓAÔAéAêAëBBBBBBBBB$B%B&B'B(B)B*B+B,B-B.B/B0B1B2B3B4B5B6B7B8B9B:B;B<B=B>B?B@BABBBCBDBEBFBGBHBIBJBKBLBMBNBOBPBQBRBSBZB[B\BgBhBiBjB~BB€BB³B´BµB¶BèBéBêBÿCCC C CCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCMCNCZC[C\CqCsC¦C§C¨CÛCÜCÝCòCóCôCÿDDDD D D D DDDDDDDDDDDDDDDDDDD D!D"D#D$D%D&D'D(D)D*D+D,D-D.D/D0D1D2D3D4D5D6D7D8D>D?D@DIDJDKDYDZD[D\DpDqDrDsD‡DˆD‰DŠD˜D™DšD£D¤D¥D«D¬D­D®D¯D°D±D²D³D´DµD¶D·D¸D¹DºD»D¼D½D¾D¿DÀDÁDÂDÃDÄDÅDÆDÇDÈDÉDËDÌDÍDÎDÏDÐDÑDÒDÓDÕDÖD×DØDÞDßDæDçDèDñDòDóDôDÿEEE E EEEEE E!E"E'E(E)E*E+E,E-E.E/E0E1E2E3E4E5E6E7E8E9E:E;E<E=E>E?E@EAEBECEDEEEFEGEHEIEJEKELEMENEOEPEUEVE\E]EdEeEfEmEnEvEwE~EE…E†E‹EŒEEŽEEE‘E’E“E”E•E–E—E˜E™EšE›EœEEžEŸE E¡E¢E£E¤E¥E¦E§E¨E©EªE«E¬E­E®E¯E°E±E²E³E´EµE¶E·E¸E¹EºE»E¼E½E¾E¿EÀEÁEÂEÃEÄEÅEÆEÇEÈEÉEÊEËEÌEÍEÎEÏEÐEÑEÒEÓEÔEÕEÖE×EØEÙEÚEÛEÜEÝEÞEßEàEáEâEãEäEåEæEçEèEéEêEëEìEíEîEïEðEñEòEóEôEõEöE÷EøEùEúEûEüEýEþEÿFFFFFFFFFF F F F F FFFFFFFFFFFFFFFFFFF F!F"F#F$F%F&F'F(F)F*F+F,F-F.F/F0F1F2F3F4F5F6F7F8F9F:F;F<F=F>F?F@FAFBFCFDFEFFFGFHFIFJFKFLFMFNFOFPFQFRFSFTFUFVFWFXFYFZF[F\F]F^F_F`FaFbFcFdFeFfFgFhFiFjFkFlFmFnFoFpFqFrFsFtFuFvFwFxFyFzF|F}F~FF€FF‚FƒF„F…F†F‡FˆF‰FŠF‹FŒFFŽFFF‘F’F“F”F•F–F—F˜F™FšF›FœFFžFŸF F¡F¢F£F¤F¥F¦F§F¨F©FªF«F¬F­F®F¯F°F±F²F³F´FµF¶F·F¸F¹FºF»F¼F½F¾F¿FÁFÂFÃFÄFÅFÆFÇFÉFËFÌFÍFÎFÏFÐFÑFÒFÓFÔFÕFÖF×FØFÚFÛFÜFÝFÞFßFàFáG(GbIIœIÀIÁIÆIÊ  •Ðß•Á¶HˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8\.TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼„ÐÀõ@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ \$ !   $ ¶H$1èPSNOD`TͬPDÌ !   $J¶H 3èP !   $жHô4èPAp—¸A‚hgAÖãA˜›A¢srAª‰áA°ÕØA´ƒÿA´ÒóA²¦õA­o A¦*jAœj¸A’;ÀA‡)AxnFA‚(½AŽÈ£AœSŒA©#AµÒ(AÀ-FAÈkAAÍ1’AÍz+AʸÝAÃÞAºhúA®MœA¡§JA”&óA‡QbAÕAœAA¬`KA¼ ŠAË¥AØó—A㪃AéèAêKŒAæ§ AÝç-AуA²RA³ A¢«íA“/ÒA—WA¨‡½A»¿aAÎwAálLAñ÷èAÿ%$BuB½=B€‰A÷Æ&AèulAÖE‚AÕTA°åAžT!A¡ *A´ØõAÊÉäAàîËAøÑBðoB4—B_BƒœB¸KB ¿B[³Aê­AÔ ÑA½œùA©ÿA©"A¾ÓA׌eAð hB™¯B‚BÙB ¡B!zB±©BvµB „Aû§AáÒçAÈn™A±×CA®ô3AÆqeAá†ÍAý?ÇB gúBd&B%§B,1ÝB,¶ÊB(ýÔBv B'éBªùAíýAÑÛA¸w=A²/~AÊ¢·AçBÜBõBžþB+…ÃB2…`B3ÉB/àB$ÞZB¿B_ÂAòòñAÕ±®A¼ àA²bqAÊ܆AçFñB2HBùzB˧B+»ñB2À|B3CöB/zBªB/ABD'BU±BanBb&ÓB[…ÎBL|B7óUB"_¼B ZAôyzAÓ5ÒAɃ'AìŽB 19BÓZB5âmBL½=B_üHBlŠ{Bm’üBf]ÌBUrˆB?„B(/¦B¸ Aû AØ tAɬAìVÎB F[BÿµB68sBMÄB`S2BlåBmëÔBfŸzBUÆ(B?ÄÆB(ƒÍBè AûFÁAØJXAÆÑAçó…Ažú´A²Î6AÇö›AÝü†Aó©>B£„B \›BGBu?B ÂvBb°Aü;?Aç!ËAÑAA»SeA§"AB„AŸò‡A°¢¾AÁ¹ AÒCAàsTAë¨Aò[UAòÐ3AïîAåÏ1AØL„AÈûA·¦A¦Ï³A–gA—V\A¨ˆPA»¿cAÎw`Aáf`Añ¿4Aÿ^BpŸB»£B“´A÷ÓzAè‚ßAÖCÊAÈhA°¹AžSÞA§ŸLA¿;ÇA×€1AñRïB’RByGBñB îÀB!P¼BïáBÕbB ´ÆAü_AáþšAÈíA±öSAºàVA×Aö\ÞB kB"B/ ZB<õóBEü$BFBA^B5c¢B%X~BÓB(AãkˆAÆW¿AÌ_sAð[íB  B"¬ŽB:ã½BRõõBg„KBu.“Bv!ŠBn)vB\AžBE qB,yMBöLAÿ³iAÛÈÐAÞÆîBŒ¸B3¥B:@âBZàB|«åBc’B—£FB˜Z—B’b³B…/mBhÿBGOŠB(ª©BšAð-aAíêbBÙ©B-QcBQMB{t†B”ŽÄB©êB¸±ÑB¹ßB±Bž'OB‡$ BaóB;8©BþBT…Aú—`B¥rB:IuBd¨B‹ÝSB¨®nBÄ­ûBØûýBÚBÎx4BµB—¬ìBxˆ\BJh{B%_PBWrB”¿BÉñBAú*Bp8@B”É®BµÊ"BÖ¡BîùáBñ!Bâ€YBÄ,,B¢fBƒVBS›tB+hýB oàB·:B BBF®Bp®B•<’B¶ZâB×_ÜBï¿pBò pBãq?BÄåùB¢ÑBƒ– BT B+£ëB –CAüIqBÿBXB¯óùB¿½¬BÁ!©B·ãMB£[,BŠé«Bg-¼B>Ê7B‚çB 4Aáé½BhB ÁíB?HêBa·æB‚ìBB“%PBžBžÿ¾B˜¬°BŠF„BpÉ]BLò¿B,ŽBäEAôAÐ+Aõ‰jBœ–B'X³BA=ÕB[ ÄBqh¡BöòB€BxÇjBe=ËBL(AB1Õ•BïÈBÌåAàHA¾sŸAÜ%Aý)B­ÄB#½B5óVBDó§BN‡ÉBOEBIÁ®B<ÂuB+m_B}èBÚ|Aé+SAʉ‰A«J[AÃéøAÝŠÂAøÖ™B >ÃB{B!5™B'“}B(ÜB$BÁïB´B&hAè®ØAÎ$1A¶ ‹Aš”ðA­ —AÁ`›AÕþïAêAü†BôB ‡œB ½'B=bBP^AñÇøAÞxDAÉÃ?Aµ›2A¢hA¡ŸhA´Ñ½AÊÈAàû’A÷ëèBåBMüB)AΓøAëHáBVÁB1ôB$ÙB0N$B7ð¡B8~jB4 B)¥éB†>B ÈÄA÷÷ÖAÚ«A¿ðAÉã·Aê¹ BMæB©B4rBJÐíB]¿ÉBj BjýœBcÏ BSdsB=è†B'ËBÒ…Aù¢LAÖøzAÞÈÞB¨PB4B:6ÊBZÝøB|«ÛB^ýB—›£B˜ôB’d’B…²BhölBGP½B(”B¯Að[¼AõhEBÛÂB33BYôýB„ bBwzBµšNBƹïBÈ1êB½ïAB¨NúBŽ|ÐBlÀBB7ÿBÑ8BfµBàsB"|BHšBzRãBœ‡—BÁ—ÅBçUKCç£C”Bõ%“BÒ\®B«j®B‰7B[MB0xðB»PB ×`B-~fBZ3ûB‹$òB³ Bå8ýC9 C$QC&,ïC¬ÖBüËoBÇÑBš?ÏBpˆÛB=êBdBB4BdÍ÷B“íìBÁõþBþ;@C!Ñ”C>éØCAž½C/½ÈC Ñ¡BÙAyB¥ôB}…BEè|BŠnBTíB4XdBe!eB”R…BªBÿVC"}ÔC?åxCBÑÉC0¿CwÊBÙèB¥võB}çWBF3)BæjB XB/(§B\ú BŽsB·5”Bì<5CKÒC+ûC-ZCâ…C¤0BËÃB*ËBtzB@pBÓìBèòB$º¶BLšeB€ZnB¡ ÇBÉ‹"BòþvC C wÂC§BÛ~ëB±ƒB;B`33B3Ÿ\BçÊAù§ûB÷ÝB7È4B`¾øB‰÷B¤¡ñB¿;eBÒ,PBÓ­BȈ@B°slB”_@BtiBGyƒB#höBáqAã×vB°B!½B@ÀBc³B„6ÈB”ÃÔBŸýtB á‡Bš~B‹ÂsBsMBN“HB-¹ÑBÄãAõwÆAÎ`–AðÍB ±B#Bz?iBœŸÄBÁ¿BçS‡CûC&;Bõ/BÒkB«™ªB‰>åB[LïB0y¯B¼‰BnPB3d BcÙëB’õ§BÀOvÖBO2BY³ÕBZ“™BTOBFEB3 ŽB|óB ljAð¤AÐ|A­?(AÅÒAÞÿuAúÙ7B ‚OBs‘B"ÏB)WÉB)ÖPB&>B0BìpB?ˆAê40AÏzA¶ÏA¯$:AÆ{Aá‚6Aý:šB fßBdŸB%¦šB,2žB,¬*B(ïÃB{ÐBI;B®£AíœAÑ´A¸x`AŃ1AæHFB:ìB:B/ JBDüBUÌlBa #Ba÷œB[QÎBL!B7ÿ_B"ZóB QšAôz,AÓ7øAà”˜Bä„B¨B:çtB[Ø*B}ûüBŽFÙB˜—¿B™U®B“KãB…ÚbBj%•BH 4B)ñB‰ùAð£œAúžyBªÎB:DBds¡B‹×sB¨®1Bĵ BØÑ2BÚq£BΈ“Bµ3B—»mBx„ BJhB%gœB_LBô¦ŸB¨û_Bv.pB:¾ŽB(&0B[–¨B”YdBÒMPCÚbC‚ Cêã\DO\ûDi09DÔ¦C¤±OC?,üBõbaB©}›BvÂEB; &B$`BTB›ÃBÅÔCacC\êgC²VCD«ÈDq«C×6C†ØÕC*¯öBãýßB êiBmq¤B5~lB@úBDùFB€mB¬cqBî}”C'bCkPèC•ÄCœ(ÙC„CAèC ZBÃÿ¨B,BZEB*„ŸB©yB1kB`Ú}BÌB¼q¦BôÆcC¥C3ÃPC6/ãC&d6C¾’BÒDB¡ rBxƺBCB¨°BEìBÜqB@™0Bn¶B“B³e˜BÓæBê9Bì(BÞh·BÁ+­B GBïòBQþIB*„µB ºðAåù¶BÅyB#/—BB¾pBf­˜B†JWB—\}B£ÇB£åyBñBÛBvu‰BQ2[B/ƒXBü—Aöê=AʸöAí7B á`B´lB7#‹BN,XBa½Bn‘Boˆ®Bh™BW%B@ñjB)C`BŽ_Aü:ËAÙ€A³©£AÍ2ÞAég—B B™2B"J]B. eB5– B6&@B1ÄJB'·@BB ‡òAõÀGAØu A½Ç¬A²3AÊ¥_AçÈBBºcB…B+‚B2”™B3<B/ B$ÝFBÎlBb2AòôAÕ²²A¼ ¸AɃâAìB bBß|B5â4BL©ÒB_þBl…äBm† Bf!BUiìB?¦B(9oBº Aû °AØ ®AåÔB½ÝB#!­BBÑŸBf´%B†w+B—‚ÂB£)…B¤£B4BŽ LBvßBQñB/sùBð_AößB“BÉ5BBCBp[ÿB”¾ÍBµÏ½BÖÍÇBï-BñÜBâƒ\BÄL*B¢AWBƒ6ÊBS‡êB+hGB tB ¶B3ÿþBdÑ×B“ÿzBÁåÑBþ5»C!ÓvC>õuCAž§C/°]C ΉBÙVWB¥ ÌB}`†BEä‘BŠbBŒBIŸB„e B³rkBüwC4z1C‚ÅêC­hECµÖ~C•²éCSÕÐCö~BÌÚLB”í÷B`Z*B.­B'ùlB[M1B” ·BÑ–7C WC€²Cä”DD]ÆD[ϰDÜÃC ý&C=ËËBôŠÜB©_Bv9gB:¸®B-ÊúBfëBž³Bæ0C6áC£ëÖD9šsE=1Eo§ÒD‹ØCÛümC`£C+ØB¶5BÏ)BB†ØB-óbBfp²÷BW©gBm©B{ÄB|5¹BtÙBahFBI0ƒB/¼¶BM:B˜ÂAÞ/µA¶ÛÚAÑÏÏAï^äBç—BodB(ïB4ÌQB<ä±B=ª£B8Õ†B-î BëBÍYAü_RAݦpAÁ»×A²Z AÊÉäAçBéB0ùB_BÌB+»B2ÀÒB3FB/;ŸB%IBÀB…õAóiÄAÖÍA¼W`AɦÖAìHžB CÔBô'B6„BLÿfB`MÜBlï°Bn„Bf*BUÃDB?ÏB(kàBáAû^·AØR”Aæ"7BäûB#u®BBü…BgÜB†•ÙB—ÌàB£–ÒB¤¤ÜB£BŽb[BvôZBQd‚B/¯äB"±A÷JGB±ÕB?BBkòBp·(B•‹B¶avB×–˜BïçdBò ¯BãvqBÄäB(#ðB[ªB”ÄBÒRxCÇhC‚EëCê’&DOƒ)Di…eD«ˆC¤ÏµC?>œBõ“èB©‚)Bv¼ B:ý†B-ÿABfÔBžŠ!Bç¿C7ÂôC§©îDC)E8áRE®ìêD–-XCâÙCbXàCµ B¶v¾B‚§BBÏ€B.I0Bfê[BžÕ¦BçîC8úÕC«¬ÃDN'¨ElÎF(ƒvD£TJC鱈Cd3àC :fB¶óB‚c0BCQuB)Ë4B^œÌB—#QB×ô#C%‘4CŠëøDID>‡D“•³D0fˆC²ì"CH»Bý ’B­ÕBzÎB=.KB?„BN‰Bˆ3éBº6Cd•CEè³C•qÔCÍqCÙ&jC®ðCk¦ÈCõwBÖ~zB™çNBe“,B1-¬B·B8Ð-Bl“"Bš¢øBͶíC 6ÄC2þCTížCXM°CBÏËCYrBèHB­wÈBƒ¤BKÉDB!6mB»‰B"‹(BIV»B{\BQB›KBèp1Ch‘C‚:Bö@¾BÓfB¬ZßB‰Ù¬B\36B1mB)AëÞoB B)V•BK¤=Bs+‡BŽ­õB¢ÆB¯d\B°#B¨¦rB—ltB‚LB[3,B6”BäØAýÙ÷AÏ6uAó¦ÂB9lB%ÁB>âBuiBºDAÞa3A·"AÑóÇAïœÉB5B³BB(X²B5ùB=+íB=ë¤B9) B.LNBGBãHAü[AÝé0AÁìA¯ØÄAÇ™;AãAÿMB©‹BÎÃB'L*B-üB.|:B*¤dB! BHB§êAAÒjìA¹…AƉ¯AçäBPZBzB0ö"BF[BX›Bd:«Be(wB^N¡BN³ªB:qB$1BŒJAömAÔ™Aâ|Bó6B1B=9B^×CBBÇB›:BœJB•ÿÛBˆ_BmþBJÀhB*ýÍB¾ÞAòd—AüMRB•BÍB–ÉÔB×A‰C$–¸Cˆ‘ÉCÿˆDnšÏDˆœD)PzC®f¿CFìËBü B¬ªlByà÷B<ÛŠB)Ò_B^œB— B×èóC%ydCŠîRD¼VD~NÉD“u¾D1L!C²xŽCHb0BüÿB­6òBz_¿B='B%£BVæëBäBÊhCÄ3CiºžCÃO6D(D&2¦CïˆæCP¿C2;©Bê€5B¤ÿBpÏØB7…çBxžBGeŠB‚zIB° Bõï"C.’/CxúÀC¡PžC¨¥ÅC4óCKCäBÈ×gB’²`B]t+B,EêBÔîB3ƒÛBd!üB“`yBÁ»BüNÆC , C<'¡C>µÃC-lrC ‡·B×ùoB¤síB|BEXÅB$ÄByBg BBø´Bq˜¦B•ÕB·UXBاÖBñ-›Bó%BäuèBÅãYB£P^B„ÜBTÖ;B,'ëB äñAç•®B ëXB$ÓBE.Bj -Bˆ››Bš\‹B¦8aB§KB BbÈBznXBSÜÏB1l©BK$AøÉÄAËÍ^AîàCB œB!DB91BPÕŒBetBqðBrø³BkYBYèFBCDB+ Bá7AþW‡AÚ}KA´}ÎAÎ}•AëÞBBë;B#âB/í·B7”yB8,­B3½B)ŠØBO¹B ­UA÷› AÙóCA¾å1Aª€íAÀˆ~AÚ Aó‘èBy‡BCkBf*B#TÓB#ÎsB MÍBÈXB œJAþ×Aä“AÊx A³v&A¿pùAÞ“ýB¹Bå«B&McB9<ÄBHâBBRÓBS˜ÏBMÃB@NèB.]QB RB”½AëΣAÌÀnAÙuAÿp B‹­B0ûÕBN0•Bk÷¡BƒÎB‹¥BŒN˜B‡>Bw¸¬BZ² B<ÿB útB˜íAèLôAð¾ïBî B0g÷BUºõB·B™GBB¯è,B¿ÊlBÁhB·¶IB£\!BŠó®Bg5B>Ç B‚ÇBBéQB$® BLÒ¹B€^ƒB¡œ³BɯBò¢wC ÓC zŠCýŒBÛ€B±˜šB0B`3/B3ª\BéwBš­B6­ÃBhæÓB—]BÈÕCÊVC* ÍCIÀCO¬ÁC9(rC¼ÓBàÁB©U£B4%BIWBÃ=B> BE¬B€mºB¬SøBîgC'ñÎCk‹C•ºþCœ*6C„béCAùÚC ºBÃýøB'nBZŠBB*…·B8BM¥ÚB‡áJBº&XCÆ’CCC C’EaCÇÆ CÓ0•CªChJÞC4BÕ³—B™‚ŸBe(B0óQB?>BN2MBˆ>æBºŸ2CH«CEÃ0C•À¨CÍmPCÙqC®ëCk™–CìñBÖG¶B™á@Be«NB1<‰BxÚBGr|B‚€ÌB°%ÆBõýC.”‡CyüC¡GŒC¨§‰CŒù CK¨ÔC Û›BÈÙïB’Ì/B]†NB,G”B©ƒB9òrBnaBœ*BЖþC ·¬C6ÀsC\%{CbÖµCH*ÕCJBë™B¯RaB„¡¯BM ÃB"BgB/1B( ÉBRƱB… °B©<^BÕICðÂCGóCÌ-C ¯B鬴BºÎ©B’èËBg_)B8.–BË|Aö BèþB6"B^¬B‡%jB¡ÜOB»G BÍ›yBÎî1BÄtB­"ÍB’­Bq-BEsÇB".B~AÝÔ,B[NBˆ×B7å!BW¾BxšoBŠÁyB”`OB•$üBsšB‚¾lBeŽ{BD·oB&¬›B ¹®AîAÄ>AäôB`‘BZåB-ŠÆBB4žBSŒòB^uyB_gfBXÞBIø`B6V=B!MB ]xAó'AÒ8 A®ÈÙAÇAá¢VAþ B ^;BîB%‚ÑB,8EB,À„B(Ó–B°ÞB¨BöAí yAÑ®SA¸!A£n´A· AͨãAäY˜AüFÝBLüB BîB‡Bš³B cqB±@Aî')A×*AÀ5A«ÊA¶q®AÑ«ÿAï8ðBÁBC¼B'ñˆB4·±B<ÂçB=UMB8¦LB-ÕàBö_B“ûAü'AÝRnAÁ—GAÌÅ@AB :B!/ B9BP­,BdÜ—BqÜÇBrØâ»C-q—C ‰B×ìýB¤x¤B|‘\BEY¿B&µB.B(Ž8BRŒÜB….ÀB©WLBÕM­CïCCÏ.C ±ÅBéœôBºÑ°B’Ú«BgvNB81ßBÊôAþ ÊB%wB<š7Bh_BކžB¬³BÉÈ]Bß=qBàáÃBÔ!B¹|BšÐnB|ÁfBM;ÖB'."B f…Aæð©B WeB%bÆBEοBk ÞB‰TB›=¶B§¶B¨kúB¡)ÇB‘D.B{œtBTÊÁB2CB³£Aù”nAÑSÓAôá×B2àB&çßB@²BZ¨­BpÐB~óqB€ kBw£RBdfBK=B1aÄB²±B©˜Aß]…AºÈÝA×JBAö®5B ƒPBX»B/XÌB=G§BF`BFòˆBA¬B5ªxB%–MBÞÖBD!Aã¾ÏAƇ%A§" A¼“»AÔ=!Aí=ØB ÎB”¯B‘°B%BŸéBU@Bª¯B÷¡A÷¹mAÞeåAÆA¯_áA™YA«ÝA¿åAÒe7AæhüA÷B”ÛB˜¼BæÝBAýY™Aí‹ûAÚeòAÇ’A²ôÿA ÞAª-šA­;AÛ±QAöžæBÌBOTBWDB%FB&$B"fBåvB*„B!AæšÁAÌ A´ÇòA¾8ÝAÛ+NAûØÃBðüB"«eB4¡GBC‘^BLøÒBMÛ%BHHXB;‹‘B*U•B¤IB"»AèÕAÉÞSAÐ$AõlÌBŽ_B'rëBAUðB[4nBqhäBøB€ŸBx»˜BeXaBL'ÿB1ÔiBï~BÍMAàMAãØ.B®oB!²MB@“]BcÓûB„KzB”ǪB B æ»BšSíB‹ÊSBs SBNžþB-¸]B´ÅAõuAòü2B˜…B2ÉBY|Bƒ¥ØBœÓÌB´}eBÅ:åBÆ­B¼ÖÔB§Q¾Bä Bk` BAœ-BLBgØBPµBåâB@¡ÒBn4°B“"5B³SŒBÒî/Bê †BëînBÞjBÁStB ÕBî6BRßB*eàB ¹óB©EB"_äBH÷jBzѪBœì‚BœBç²:Câ§CƒBõq BÒ„ˆB«ÓKB‰’‘B[¾0B0À3BËBÌB" BI/}B{NEBQyB›ýBèœßCjSC{áBöv-BÓ=zB¬L`B‰àÖB\B0ýNB*ÄB)\B•ABBó*Bq”öB•ÉÕB·IàBØÏHBñBòøBäx…BÆB£[cB„ dBTž²B,ùB åÿAö3BÒ©B6 0B^JB‡,`B¡Ö;B»\UBÍqêBÎû#BÄ8äB­$—B’ žBqžBE˜ÿB" tB"AæïåB M]B%ZlBEóÂBk5B‰OŽB›M²B§maB¨d9B¡+;B‘=ÔB{qèBTлB2(ÀB¹lAù”'AÔAúÞNB@êB,i¬BH1Bd¯B|€gB† ÆB†¦”BýBnÖ³BSÏñB7ºB_¼Bí’Aä‰çAÁëXAàxhBkB~ B(’B<\BL8BVU0BWNÀBQ2¯BC;ÏB0Æ B¶FB ÂAî AÎKÆA®kAÇo‘AáÿÐAþ×B ¸sBB%îB,®0B-KEB)PœB £BosB6Aí{çAÑëA¹†A!þA°aËAÄÏWAÚ yAîÿ5Bò!BL@B ÑnB *B ŠABPˆA÷Aâß^AÍqÞA¸¨ýA¤üYA²›Až©\A¯¤“A¿èãAÐ+ AÝÔ Aé;qAï¢MAðK8Aì†Aã&ÌAÖK]AƸ\A¶˜©A¥¤äA•ñAž!ïA²@*AÇ)\AÝÄAò‡hBòB Ÿ†BSçB©šB óhB~&AúêFAæ+¾AÐ>wAº¼aA¦|A¯:§AÆ¿-AáFPAý®B /ÚBŒaB%YäB+þ©B,‚ B(³B‘3Bî¡BЭA»ý´A¡çðA¶mAÌ_aAãvðAúaAB‰×B°ÏBµHBJB,«B ;¹B–3AìïAÕÙ¾A¿<%Aª,“A“vA¢Å4A´~AÅÒOAÖßáAåõÄAñºAøÂ™AùL5Aõ4ôAëdûAÝnfAÍ(A»^[A©ñûA˜ñãA„‘óA‘fŠAŸ“A¬ÐAAº¦AÄòMAͶ@AÒ¦RAÓ AÐ0AÈåÞA¾÷¯A²S¶A¥K²A—SAŠ=AͲA¢A³&UAĨäAÕp AäeAïâ©Aöå A÷l Aód"Aé´AÛê AË¿5AºRÞA¨û²A˜lPAŸ3¥A²×nAÇõ"AÝüSAóª^B¤žB ]êBmBtLB ¸B6‹Aü+WAçiAÐõLA»~mA§0ÕA«U;AÃëòAÝ‹.Aø×«B 4øBöqB!6dB'¡UB(JB$]šB½ABÁ“B'4Aèš6AÎÄA¶ pA¸‹_AÔ%×AòˆŸB ÖNBõMB+*7B8qôB@óØBA‰B<„B1;ØB!æ>Bæ’AÿâÔAà*oAÃÛEAÂ\JAâ„ùBÆB.ùB*¡7B>pBO ÇBYµ®BZsØBTGBE÷B3ÁBÆB mMAð*)AÐ ÖAʸVAí|B åöB¶B7#/BN+´BaûÌBn…xBo€ïBh†BW†B@íäB)J B Aü\0AÙ¯AÎÚ™AóF‡BEB%U’B>BW¢gBm)hB{&@B|k«Bt™BaaVBI' B/–ˆBHñBšáAÞ7ÆAÏ‹Aó~¡BRHB% `B>ÚBWÿBmŠ#B{˜ÅB|²lBtGBaà¶BIyŒB/Ò3Bu(Bº+AÞhKAËÁ»AîÖ%B SB!E>B99KBPú›BdéBqåðBrêzBkRiBYæ±BC;ÖB+ùBâ?Aþ,²AÚyAÄ<´Aäý¨Bk B[öB-‹IBB%ûBSi6B^Œ°B_t BXá4BIø”B6U{B!öB Y±AòúAÒ4¦AºÇ®A×UúAöÒ¡B …¡BXOB/RäB=VBF$ÞBFÕ}BAªB5«‡B%™•Bñ¬B@ÇA㯫AÆ…oA® ®AÇa}Aáù6AþqB «éBSB%ïB,°B-7ìB)wDB \BpwB<}Aí|µAÑéEA¹ÈA¡ÒXA¶P¨AÌ[,Aãi_AúBB’€B´2BÙcB£B.éB <+B– AìïËAÕÌæA¿1ýAª+šA“˜ÊA¥naA·O÷AÉÎAÛ^–Aë`|A÷€ZAþùëAÿ^·Aû Að¾ÊAâ0ïAÑ­A¾áOA¬¦ûA›m:A‡P-A•6EA£œNA²A¿ÁOAËÃúAÕAÚaAÚÃJA×™äAÏúKAÄõÂA·é9A©•=A›mAŒë¯AtH°A„àSAŠA›x×A¦)óA®ÚêAµ„A¹c A¹ÒœA·œ^A±øèAª-A )ÛA•utA‰àêA}‚‚A„ A’~nA B fÖB ºB6ÅBKAñìŽAÞnôAÉÁjAµóA¢jƒA¥=‚AºZAÑ#AéyèBºB ¹BnÁBÑ\BNbB8BÎ-BŽåAó•³AÛ ÇAÃmÌA­FõA­<•AÅfAß-AúÛqB gŸBrÐB"Ú¸B)oB)ÖåB& óB1BîýB>êAê3-Aω³A¶Ò A³ˆ×AÍ*UAéfB íB¡›B"^B.ÔB5’„B6!wB1ÃzB'¶ËBØB ƒ AõÂHAاÝA½ÓA¶Ë2AÑÊfAï_YBá÷Bb%B(RB4ÎÒB<åÐB=…B8Ï÷B. üB#SB»ŠAü]&AÝ™ÈAÁµTA·ÜAÑïjAï«-B «B•B(T4B55 B=<ëB=Í B9>B.DªBO;BâAüž0AÝÆuAÁÛ´A´|*AÎ|£Aë3mBBêƒB#ãKB/ïÞB7–`B8-6B3¹¨B)qŒBL‰B ¼aA÷ŸˆAÙäÌA¾ã«A®È2AÇžAá§7Aþ _B ^ÈBÂcB%B,>#B,Ä;B(øB¸öB»B}Aí:AѤ³A¸€ŽA§!A¼‡®AÔ5½Aí<B ¨By'BŽ)BC B›‚B[B­3BúA÷ÐëAÞgÈAÆàA¯]AA!¼A°JŸAÄÀFAÚ¢Aîþ Bù“Bd7B ÛêB (ÐB –ÞBTA÷¦Aâé²AÍsBA¸éA¤ÍìA’ö§A¢·ñA´§AÅÐŽAÖøAæ5GAñÁÂAøÅ¢AùK(Aõ4AëeOAÝoDAÍA»^yA©Ú$A˜ß‹A‡PA•6’A£œ¸A²êA¿Ê(AËð>AÔáYAÚ["AÚÂÿAטÀAÏú¡AÄölA·éÇA©•œA›×AŒë-Ay}ŽA‡º§A“–AŸZMAª#ŒA³’ÀAº¥ÏA¾ÖvA¿P A¼ÍAA¶ÝÈA®:aA£ý¬A˜unAŒ²¶A€Í@4—¾4˜[4Rì4¦ý4²y4²^4¬¥4´4«÷4«®4¨4ªÐ4™¤4–û4™4™Ä4—É4—\4Ñw4ž²4É4Ù³4Øb4*“4¥ã4©(4¦74¤‡4›"4˜ˆ4—4—ò4™g4–Ú4™F4—d4#l4Ù²4'¢4P¹4ȵ4´94¢ö4œÈ4žŠ4¨ò4™‰4›K4›4˜4·˜4¡{4©U4 Ö(4Ù4ôB4±­4έ4´Z4žƒ4©„4`Î4µ4¡÷4¯4°ß4±Û4Â=4`4j™4¹»4[4à4Û(4ì4¬4g\4 '4§+4 74ïZ4L’4í64¶-4Z4Vì4÷ß4.‡4\ˆ4àŸ4»>4ÅY4:É44ÇÌ4¤Æ4°#4ªÿ4¥Ü4ªœ4½ô4ëI4‡4{…4²s4ã4]¢4´Ä4ÏÒ4·˜4¡ù4Ÿ14œ04™4›?4Á4n48I4Ã4þ´44 î<4Çr4«„4¹34¨4¥¸4 l4Ù+4A§4˘4Ý44Ä?4ðz4CÐ4"!4¼=4ì4lx4×y4¨¶4,4¶­4¤º4Ôô4ˆ³4á_4á_4'ù4:4³j4"4ît4 ›J4jå4(4†l4Üß4´<4¯º4¨«4s4§¶4ÀÎ4°Q4!®4&â4ýî4xà4ý4þ%4Ê4¨[4›4šÕ4ž%40}4ä4©ß4»[48O4PŸ48é4Á:4Ñq4/4Þ/4ê‰4“4„4)ö4K 4ñ­4¤4¦4¨n4Ä4K4Àœ4ªí4°Ó4¼z4Õ¶4H(4ëÚ4(Ü4ýQ4Ź4™‡4i4«Y4§4¢æ4¯$4®Ó4¤Œ4žÞ4)Ç4áå4á$4À4k4®4›Ô4–}4–«4œ¡4ŸÓ4ž†4¢A4©Ì4¦64›F4×õ4‚ú4­ä4§ø4¼…4Ù4õ$4–y4–s4šû4ž74œê4žë4¥4¥«4 4°4ï4»‘4œg4¥M4†í4ªˆ4˜w4Ÿ<4õÝ4ì4ß|4Û4ž4«ƒ4³ü4¹…4©+4ù4Œ4–n4—t4˜d4˜œ4œZ4‚4è¯4­4›°4 ë4Ï4Ç»4è=4¦Ñ4¯Ü4žD4˜4–F4—›4™û4™¨4ÁŒ4©54F4^€4èY4Kõ4×4ó64¡B4™4¬ß4½ÿ4™Ù4œŒ4ž 4Äz4 Ä´4|Ô4Ç4ɸ4»4NÂ4úë4ÛN4¼Í4¡—45V4464§¢4¨I4¼ó4Ø4»X4´40„4¸4Ø|4®Í4á4²4¤†4Ém4ê4Ÿ[4œ¦4Ûx4´O4ËG4£§4#Ð4À‹4å4ÌÐ4ñâ4ƒ4È\4Ñb4;W4ã4ít4Ÿ(4§4Âì4§Ü4¶4£x4“›4Kæ4‚D4*Ê4ú4ý4ÛÀ4Ñ4g4¿'4§Ô4£j4¡*4Kß4%á4úä4(O4”Í4«ô4ÖÂ4×€4Ï4V4ü@4¯í4Ãô4¨ý4¾ü4Œ4Ô4`4º!4Y424c¡4jÚ4 J{4.4_48O4Ñ„4¼'4—ú4º4ü4ô’4ã¾4Ö£4K&4è¼4 \ó4Œù4Âo42º4­ò4õ¥4Ôë4ì>4g‹4¡†4œ'4°Ÿ4Çl4Á4[b4M“4%Ê4]+4…æ4‘/4¿Ü4¾\4Ÿ!4©4ZÆ4…ñ4Àm4®à4Ä[4uì4C4z4®Ó4Ùö4ã 4µ4›]4¥a4²’4ßë4lZ4¨¹4˜Ñ4™4Ÿ„4ö±424$Ä4¥4ªC4æv4¼4²4çú4¶4À4:4ž4kâ4$Ü4ô4¦!4Í84Å4¡c4—Ð4j%4 P4*è4¿ê4Žl4ŸÆ4ŸÃ4–ø4Ÿµ4Ÿ‰4™ö4 ±4¬v4¬h4¡4–¸4se4 4E[4²‡4©ª4ä£4žš4–y4–4˜ï4›4žE4¢&4£Ö4¨4™D4¢4ç 4C 4ªl4›54§c4˜è4–ù4— 4¯-4šØ4Òð4¾"4¨©4Ç·4´¹4Ê4ÚS4!›¿4{õ4œô4–ú4˜4–»4–É4 •4™_4‹4Û‘4—¯4™«44µ4 òD4<Ÿ4Î4ä4–ñ4–n4—¥4—f4–Š4™ž4œ!4:•4&T4Ÿ˜4îì4=§4VÜ4¾m4¨­4ê¼4­¯4šš4¥&4šà4¼ã4Æê4Š{4¨Ç4¯“4«à4?ê4ÝŽ4H.4›Ã4®Ù4 yê4©æ4²¥4½œ4›¹4¡ß4Íñ4ï4¥Á4¦G4¶–4!b‡4ü¡4bW4õ÷4 û4ô 4µ4š‹4š«4E4]4° 4§]4èì4e4ÖQ4 ¹?4UÚ4à4?4á?4Ã24÷4 »4›04…ó4-š4O™43S4N4³4÷´4 '4 e4Äý4Ea4Ö4®Î4â(4á 4în4­m4Í"4%64â4ø¯4ô4Ô4(Öµ4Çü4=4NÞ4h‚44-»4€l4 ¢4¿g4øš4~4•4”/4±¨4ü4)ú4 ?F4Sü4 (·4Èî4|Ó4öK4ÁÏ4Ÿ24¡4Þ4 é4Ù±4[÷4 4·è44îî4ÉD4]4Ÿ‡4"ÿ4~Ÿ4©4@r4¯4¨·4Ù4:N4¡¹4Zè4—Ó4t4 ñÓ4=bš4jÐ4Âé4¨4¡ý4À4Z~4Ð-4¸P4è´4Å@44Ý4ÏÜ4“ 4¼k4A74¹…4"”4 ƒ4m 4¿4¨4¼ÿ4ŸQ4™#4ž 4?4öV4÷4 ‹™4"æ4±Ÿ4W4æL4 ±ü4/K4—4ž†4¤¸4¡u4¸æ4½ì4þ4©ª4î#4]4°ß4f4æ04tA4\4¢ï4wö4±û4š44—á4¯M4¦@4˜n4¦-4Ć4¹è4²g4˜4ñ84¿A4Iu4\4ñ®4 –4—Ë4–õ4–Ú4˜4™’4¡„4¢*4¤c4n–4¬n4Ëè4 D[4RV4£·4¢4—‚4˜;4—4—°4—34Ÿµ4«´4¥Õ4Ô®4!O4»-4 õX4F>4âù4¸¯4›”4™s4™_4—4˜Œ4˜4¿f4­4›£4œñ4òA4r[4;4ÏV4¶á4Ϙ4 œ4›G4™4—È4´4Ì.4¡M4Ó£4˜B4—Q494½w4óí4 ”4¯.4+t4Âþ4¨Ð4,F4¬4b 4ò4 ýô4<4–ø4—ž4ö4 Àô4×4¸74›Š4©M4)24Õ-4S~4œÍ4®4³x4q4§È4œY4éˆ4%F4 ³Î4ñ–4 SN4,Ð4¡ý4ÂA4V4·X4x44Ó«4¾ð4É4ÑÊ4p 4 . 4i­4 ž·4¿Ð4JU4Ž54—¬4°-4È4}4@4o°4í×4 jD4¡ö4ôX4ðê4NY†4"OÎ4ê¯46'4®è4<ã4±V4âw4°4¼¸4»Ý4#·4 è44úê4 ŠÆ42È4?º64 •[4‚X4ÄÝ4©04¡.4º4Ôž4>¶4›4Ë4y4|í4Ìô4 Å4 hà4;×È4 ¾£4ïœ4§p4 Þ4wA4‡ü4(“4¦4°4ÿ|4×4}Õ4³í4Ô¹4ý4oö4—²4 ºd4Ó 4%n4bã4l4×14¹Œ4œ4®ê4t4£4 4â%4ºé4 ·4Bô4”4ôY4®›4zÃ4Ê?4#ê4Ñ4c§4xø4æ4òÃ4nw4À,4ON4ü~4Wˆ4h4;4±Û4—µ4!4É»4«Q4»4í54 Î4 ¨4 l4%¦4óõ4¥X4Ñ45 4þÅ4›b4–”4›.4œ¡4œ 4° 4Ý4š4gB4(ß4Öâ4É•4®ë4µ4mü4±M4žý4ðë4Ï_4¡p4šE4›14œ84<4R4¥]4º‹4Ö4¡³4‹:44áH4œ4ú¤4Ç4žæ4är4ž:4œq4¤4¯¡4±æ4ìl4é4(Ü4¢4Lu4¶Œ44´4 „4œ4 Ÿ4ë4«4aS4J4Ò/4ë4/®4Ï48Ì4Ï34¤š4¡ñ4eÔ43Ï4¥¨4¡X4¡-4º@4I~4YZ4·•4œp4üƒ4I14º‰4¾‚4žÊ4°‹4Žu4,*4°#4šX4§64¦x4+#4¸_4È4Õd4§g4¸å4ýÎ4®å4˜Î4T[44ËÖ4¹È4Ù-4š4)Ÿ4á4›”4š—4­þ4rÇ4(ãv44áû4¨Ñ4£ï4ÃÜ4Ñu4kp4­4á¯4¢y4œx4ž`4˜¯4t4"2¼4#9t4k4uä4½Ñ4ª:4Ã4ÎF4×m4Ñ–4£4’4õ4!×4Î÷4 ày4;¿‹44XG4 O¾4þk4·q4¶:4¥Y4žì4X‡4 Ù4 LN4L‚4.‰4 ’J4R,n4£Ð4Z‚4@4‚ª4!s4T|4«4,É4¬‡4çP4½N4ÆŒ4|ñ4º¶4k–4N2b4³ ò4‚ÐÚ4(w)4Rù4 1Ú4ó*4£z4™4ù¢4~y4 Œ—4I4 {£4æç4£c4.4J’4´¬54LU4é4F¦4; 4±”4$Ö4 ´¯4¾(4è4f4[74Dü4 E4Í"4¨‹4e [4#ÔO4Ÿº4ê?47>4‰|4Ð;4Ñ4Ñ£4p¹4æ4ò=4„P4m749í€4ŸÜ4jK44tN4 #4Þ™4Ç4‹4Š4 4Øz4„94â4Ùæ4¶}4µ:4 Šå4Tr4þ³4¼44YÀ4îì4Ƶ4œ-4›½4è¸4§4¾~4À4 äç4]„4ÊX4T 4_ð4òí4$XB4 4'4¡R4¡4ÂW4ÉB4¨24Ã4 ‡«4î4ßD4e4ð4•4Y`4žˆ4 3h4Ú4šl4Û~4`¬4ÖÅ4Æ4Å•4:Ô4áê4ä|4ï44Ï4ãŒ4Â4]4çÜ4¡'4áÍ4íŒ4®˜4 1 4ÍÂ4¼m4§Ü4´ö4ü\4¤4e–4ç=44 À4¿04?&4ÓÐ4§ 4 y4¡L4žÝ4ª 4¯#4³ô4Ö˜404s 4ù·44.4ªK4š4¢Ù4É%4l´4¢t4¨œ4¦¦4Ü4¬ 4ö_4Jæ4Ö¡4 4ý\49t4½G4Ÿ44§34-Ä47ð4¹4ËŽ4©4œU4§˜4¼A4€à4$“4á 4L¨4áj4‚i4§Ã4¥h4ÅÞ4äâ4‹4Â4²‡4ž4΃4Û$4p4H‡+4â×4D¨4äý4¨¥4a4¤>4«ü4Ük4.Y4§ì4ð4ì@4—I4\¹4{}4Ni4%Ê84u±4\H4é„4¡õ4©Z4¤l4œÓ4`4ã}4+Ð4(Ò4Z4Éy4ܦ45Q§4 è™4B ó4€4V-4ýN4Ç;4¨Ê4­”4¾h4í4M4d¯4Ôì4o½Ÿ4?v5Sj4OC4Ê)4d4%×4·4æs4ÁX4¼4#4ß@4â¼4í^4* 4 =4a!5®´n4›º|4”4‰ P43b£44W4 ¢A4“ã4ê»4ÕO4 ú4þì4ƒÜ4™Y4 ÌŠ4ˆ#Þ5K·Œ5 g4›»4à4s¦4ëõ4 ”4î4x54ˆc4—•4y±4dg4.Ç4’í4 óP5,V,4ŸvÕ4ò¿4$Ý4˜Â4”õ4Èþ4Žƒ4ú4Gì4884«4$²4 Ñù4.Ý4 )+4# "4fYÃ4%|42ÞA4Eã¯4À4ôM4ò4Öñ4A€4!Z4´4§ù4 rŽ4S 4 ²`44è?4 D4Ç4Ì«4¶¯4×€4ž4×÷4¶Š4¾÷4Éo4 Á4öH4Úó4¸¼4 KP4èv4 æƒ4®j4¤_4Í 4¸4¿¢4ûñ4§z4ªh4Õæ4Na4\45(4E 4W,4 µ4}º4й4«‘4–ò4µS48ÿ4:<4šË4¢}4±É4þÎ4øY4‘´4úô4[ã4 (Á4*Y4tè4´4›¦4›¬4µð4¬ý4X4£g4ªÁ4ÜÑ4 ;Ö424í24L»4¶ó4"…4ÁÜ4ñœ4Ê64žw4Ž4ž{4 Ý4@4ºò4£à4·r4àl4÷E4ã!4\r4³è4?­4 .X4‡4£44›4¢4ýc4Ò4ºI4¡54¦<4Ü4 Ä4.—4E‘4šD4^J4ê|4ò'4§¿4šc4²M4«Ÿ4¥4¾²4šñ4¿†4¥/4 l4¹4K¶4h¾4úà4Ñ4 +4Ðq4È4¥j4ä†4î4¼Z4Ÿû4[W4ž|4ä4ñ 4 ¯¢4 Â4º4š4n4ïø44¿k4»94'â4i¡4go4ÒZ4ß4óu4EOi49<`4°/4®Ã4 y¥4“¤4äD4ÈN4ÅÔ4G¶4oÒ4¡@4Ae4‹™4Ƹ4‡‚4(î5ï™43À 4'±4¾Â4^â4øˆ4<"4544¢Á4X×4*54ûE4ƒŽ4Xã4 r}4 O4&4{O42¯”42N´4*Í+4Ïl4I4Á´4Ã(4èZ4ôv4Ø4ÃN4´ 4X•4nY4C4-/4ÿÊ4+}v4OU4{4är4¯4ø4 fÉ4}Ÿ4¹¶4­°4ž@4§49¡4Â-4 ©4!Ý4Cª4 ˆ4Æð44vH4v4&¹4Ó¸4¸Á4«à4¬“4°84àN4:4±"4ír4u¬4d;4Á4z4.4².4¦i4ª«4¥I4û4³­4 ƒ4ºS4ß¹42ú4ª4<Á4#<4ü?4ë4ÌŠ4¦]4—44˜¥4ôÅ4ªò4½¾4ë¥4Ðg4Ÿ4œñ4‹I4*ø4ýÙ4154£ 4â4ž‚4©4›j4·ü4²}4üo4oû4Åò4Í 4N{4Æ4 ¹4õ4›,4 rŽ43Ü4½]4—à4—Ï4·ð4ª*4ʦ4 Ú,4 Ó4#4¡e4 tº4)4ׂ4 Ž^4 ­ð4{54Ü=4 Ù4¤À4¡x4¿¾4’4 ÐÛ4\Ï4 :94"â”4*D"4$Ÿ³4 ,j4 H4Îþ4„ã4F‰4©Ö4ìù4z-4öœ4ªä4›Õ4-4 h©4J ¸4’[e4ŠÎ^4$)ÿ4Aì4†€4ì—4Ç4­44 [4&T4 ¯i44³€4»ø4$p58Çä5¬å4U²ú4ýW4O4»>4F24 š4*4 *47µ4ïÇ4 mF4-u™4»4–5“ý05¥YÇ63Å5¡ü4A4 ˜4 Od4ÿ²4æ%4aU4’}4UQ4 3Q4XÌ›4Œ‰™4ç>n6%CR8»¹È9@Ô·6¦ýE4å›4£o|4EóÌ4±™4ò 4~ 4(4Ñ;4°=4:­_4Qîó4]v6µ19 îü9É>Ž6È ¡5t*v4­D24=î4\©4ˆ4¨4074 Ð4(Ë4"~(4(Šº4+ô5Ÿ~•6-Àæ5÷t5Oè4{@4N_–4+ ê4 Ù 4)×4Vj4,‘4¡½4 &v4dY4 e™4 —4‰ö®55ºF5[³'4ä Þ4#qÕ4 }P4¨4Y94èC4!v4øq4$^4,4 Q4Ý4PI4* =4l›4ñP¸4…•44Éù4Óp4 ™Z4 µÆ4ØT4À 4Ôk4ë¿4À\4~4[4*†4ŠU49Ù{4;!£4Ãç4»44 ZÁ4Æ_4¾?4¶¿4Â4Éà4 04g4 ”L4cŠ4 ˜4»4™84 4Š×4 ¥4?64$ï4áÆ4›í4›K4Ù•42‹4þä4é‘4¡Ÿ4Ôß4¢y4ÚB4$94ª(4º4´4Ôì4¿‹4 4· 4Éé4!l4K‘4624-²4Kœ4 ü³4à#4ÆA4J4-¤4Œ 4üt4Ö04ž¥4«O4ÌÉ404'Ü4ê¾4WÜ4›ó424ƒ›4$ä4îS4ê 4Ýu4ëk4jx4¡ 4º,4ïx4àÄ4zŸ4ƒ]4­n4¯4Uà4{p45¼4û4Ït4+84Ù4šý4¢4@æ4)4}y4»€4Lï4?æ4 ÆŸ42Óy4æ4Ys4ë¸4³‹4É04L]4¤ë4®Y4o±4kå4xL4(4Âb43t4*,k4E+j4/µ¨4¨^4)Ï4ã4ßß4â•4&È4º¬4Ê‹4cD4æ}4´i4Ä*4}Ø'4N§4 éÄ4õu44Û+4 F;4 ‘O4æw4ûP4OT4B4:·4ø4Ïþ4ñ4 t»4¥Ò¤4•‘g4òϸ4¤dý4sYê40®›4g6c—9mƒ=:7',6â5ö5 c4Rvl4 óª4 ‹#4ã¶4Æ4[J4Ø4þŒ4Lœ44Wî04Êñ#6_w:6œ…ú57õ4ø/4„æY4Ü»4 öu4œ›4³Ð4 š4O4XU4‰ö4 KE4_| 4[êU4¹nú4¬#4mM4œ74à‡4š4ì4Iæ4w•4Ô±4èÞ4Ù4Ôa4R³4sœ49:•4„¸¶4˜=4Êá4+-42»4 744´4»4à£4’Z4d4ÊÆ4:+4Ó54 ÉÇ4-Î4—“4Ðd47›!4ßM4þÒ4½ã4åÒ4Ðà4,¹4Á£46©4 ÏL4 4[4 ,<4Åœ4ÝP4 .4å—4¼4y4é½4½Ö4À4 #P4ÊF4ö)4Ï_4ÊÂ4%¼4VÐ4.@4óª4ù4yr4Í4(Æ4Ï4¡{4¾¸4ÿ4Øï4â4RŒ4 „b4߀4t4“i4 >b4 È4 ­4£Â4׫4×B4ã†4Ç4¦¬4¬44Àì4ßR4µú4šã4…54’é44mº4Ê4D~4š}4v4&L4qì4 ÿb4ÀÜ4î>4!ôÊ4JÙ44÷T4 ™4ªý4 4\V4<4Üú4Éî4)‘4i4šh5Ic5ƒœ.4ì\°4o)<4g24=ýî4l4 ÅŸ4<4Ä4ˆL4+4Lç4zÌ46‘4ÃÞƒ5 À6BÌ6b 15ˆÊ>4I;4+ÆÖ4éZ4V54 ö[4Ãê4<@4ÈX4“'4 °Ä41}º4aN´5nè“5ge26ÆÃ 5¿fÕ46‚a4< 4ت429D4 ìå4½Y4é4M4Ó 4áœ41/4[ÕÜ4³½M4§Õp5gëŽ4u_b4v„4t 4?Ù4 Ú4fÙ4é4í4ðŒ4€4ä4Ǩ4!>4nï4.“ö45MÐ4[¹È4<ª4F34,½4¼>4 “4ø™4î’4¨u4 o4 Qå4c™48>4Wmï4RL)4(4 9p4CH4L 4 ¿4dB4&4Û 4È¥4ÊQ4”=4…4ú54 U4 ßx4'îL4"Ã?4w~4d|4õ4L%4 zb4 !4Ðë4Ç4Î4òJ4ZŠ4.]4—”4;ÿÏ4¸µ4"*Ç4dê4Ù#4‰R4ãj44G4 4Â{4…4Ëá4Ü+4ßK4 f4q4ôÞ4â»4A&4H¯4º?4ƒ4_u4 404û%4«Ó4ÀÏ4Ç›4¶14ú€4d4V14£è4 4P/4äS4ÒÓ4¬/4´ý4)â4°o4ªÐ4¬Ò4»Š4Ë44Ìÿ4 4Oá4ç÷4{4Sd4Ý­4¼4‚<4ƺ4—õ4—§4Ÿè4—œ4Ö}4—Ë4ñ=4Ê¡4uƒ4£˜4Âò4Ê4‘\4ýñ4/}ü4ê§4§ô4žt4­Î4­b4>'4ô¼4Þ‰4ÿU4 ˜¹44¥)4UR4L×4¹¶4Wú4âí4¾14žÙ4Â?4æØ4ç$4,¯4¨d4 ©×4Ø4Ô54€4ðî4õ'4É4ä&4H4·P4¯#4 K4·˜4ó-4?04Ye4  4’m4 ³ì4^á4ÉÔ4 h¼4 4ÜÌ4Ïá4DÄ4Yš4p¶4rº4Ø4E'4:V4Ñ_4$û4)z4bãÉ4©-4æ4ÛO4×¥4̽47í4m‚4‰ô4§À4´Ì4FE4-$Y4é_d4kÿŸ4d 4’€4k44– 4 ”·4Ö4Á4 ˆ4L4Ó4²I4 üR4AX4Vã!4Õ‰4‚Lf4æ,Þ4]³4œ´P4TÄo4@:4! 4=F4`4þð4×Þ4 hY4 Î4 €¯4F«¼5Rù54Ó`™5?œK4ÁxÍ4å:4gw4ž 4à44hJ4nD4Fû4Ú4R>4 43Ó›4 éï4±©45ve4BC¿4A4%à_4½î4p—4'.4†4MÌ4 4>à4¹Æ4‘Þ4 ÿ¨42¹Ì4 ò¢4;ñ*4aÙ4ÀÄ4O¦Ú49(4 —4a4Ô·4àg4Â4Ë4¥s4çy4o4V4Š4t˜ 4®q4ʶ4R4ì4¸4å-4´‹4Ç34lÿ4rô4 €4]M4^Ö4í4Û43ð14"m4¯?4Ø`4“÷4‡T4l.4­4µ$4Ññ4> 4l4Íæ4që4 ¶¾4 [“4Qæ4.4/Í4»4 )y4ì!4©q4™¾4¦ø4Õ4¡Ÿ4»X4¼c4Ýê4É4 ãs4üO4?f4O40p4‡±4–44Â4ÆÖ4©ƒ4Wþ4LJ4Æ24°Z4zä4µ4/64´4Ú'4èß4Y4 Õ‘4ØÌ4äù4›4¯·4Á 4Æ4ÚH4"À4ÍM4Û‚4ìG4n4Ô4 ãÙ4³Œ4¬Ø4™Û4–‹4–Ê4ÄP4½T4OP4òC4ɪ4¹>4°,4?t4æ4ê(4 œä4~p4•è4Í%4—‰4— 4®†4ì4œ¨49ô4ôR4î4j4®g4ïª4ãï4™(4Ø 4ä4«ü4™J4š4±‰4¿Ÿ4)¼4Ä¿4¹ 4204IÄ4x 4ž\4<4ýD4P;4Ò~4¬G4%4šê4ºÇ4Êð4Òô4"D4 4¯c4Eò4^^4ÝÒ4²Æ4¡E4.Ê4ìæ4+"4¶%4¾Ý4¢e4—L4 —4y~4¬84€°4 Ò4 m4;­4SF4âÂ4‘4±]4o4M¹4©U4K4d4lY4 ëC4ÑÅ4&}4„ 4ÿ4 46ÜN4Š]4¢]4»W4 ø·4›>4Ê44ÄÝ4ð)4­K4F–4ñ94$Þ4ižœ4€éK4dæ4334%"s4ȱ4J=4Þ4à´4A˜4Õ4²j4±4 &4Dû4À±–4Yê_4¢:E4G^4  ‰4ð¦4î¥4«4Û™4ã4¤­4 ( 4 ž46¾4Ý 4 Q4ˆ†4 ß4pìú4u4 âÌ4ÕP4»z4 744ç4ðw4[‰4®±4$ý4 ,<4F4 ÷w4±Ö4$Ò.4öÓ47Š4 ‚Î4‹ 4ƒ(4ìÍ4̬4ñÀ4G…47Ö46Ò74l4ÿ«4š\4/K44 ó4½K4 â4 <·4€4ÜÀ4ܹ4Å¥4Ðç4":4h4M%4òŸ4Å4)ù4VÁ4Ýc4«ä4e4le4zi4Óµ4ÍÁ4½K4²‘4µ­4¼4®;4Ÿ†4Ýu4òe4Ðó4_14Þ­4Ä74J4ë4¾}444#«4žY4¥²4«A4­4žk4m4ùÎ4¿J4 €±4?Þ4¨Q4®V4Ò’43 4‹4§ª4¢4ÜE4­ñ4¯4Ç34 œ54Ó\4Ñ¡4Bo4äU4ªF4¥Ï4Mp4²34¢µ4­4Ÿe4º<4»†4ðR4¡Ö4ÎP4·ì4½x4ÛÞ4ÖÂ4“F4¸4«“4›ö4—³4™4ÑÄ4!4ÿC4ïŒ4€4¬­4Íé4òA4ýô4ø}4K¢4°74t 4ë4Ÿ64—¿4šÂ4×74È&4ÇÕ4ü24ªë4~÷4Ï¢4/x4y4 û4R/4í“4Ÿ4—Ü4›4™W4¤÷4 R4š4o¹4‹ô4úv4Ð4/42¾4²v4S 4›Y4šT4˜Ð4œ˜4ì4¦²4ú‡4§4hŒ4÷ 4À:4×V41é4ûW4¬¢4]Þ4Æ4§ß4ž&4œæ4¬J4À4ß¼4 èƒ44ó4J94Þ¤4 Š–4÷Ú4nî4·Ã4`4¿\4à*4’84šX4tí44ÁÙ4†4 è4Öl4ºÒ4;å4/̇4¥=4>è4RK4 Àh4v¯4ü¡4F344y4Œ4íf4Z`45Q4¼À4( »4'"4-4 $n4ˆ4÷_47¨4ºÃ4µR4jé44Ý04^4Ïï4 w4;¬Ø40>-4Ç;4BÓ 4 ßw4s²4£‹4*Ý4+©4d4½Å4•é4ºÉ4ØQ44Ÿü4\4u4 §‚4ŸÇ4 е4r"4où4¦4³4·÷4äì4mù4†4 b4Jd4çc4$w4 |4v¤4 ^4ó34# 4ÉH4p4gd4»Ô4«z4 õ40¨4 Öd4†?4VÙ4O4ŽW4?”4?14ë4qÆ4€º4^G4—04Çù4›Þ4—©4›`4ì4¥„4g¡4'ª#4ÌQ4IÍ4ýÜ4Ÿ“4ð74“+4B4Ä4Ƹ4œ4¢4¢Q4±4­ö4 šï4‰¿4±ì4ˆ24Oì4™ü4 h4º{4ŠÔ4Îl4¯ò4Ÿ 4Ϊ4Α4Õ34!–4‘k46J4¦¸4S4Š4›4›Ô4œŠ4ªI4¨Ü4Ò¿4ŸB4µÎ4 N4±4ªò4€ò4-×4¼f4~ 4Ï4Ÿí4š4›W4ž¬4æ4 }á4šu4›y4˜×4§î4¼—4§“4¬ˆ4®ä4Ìf4ÿº4Ò‡4 ¿4Ÿž4q,4_é4î±4¤34ÂÉ4™14žO4¥ø4¥†4ð}4À©4Ïð4é4&v4ž?4D94Ù14<>4áƒ4°U4›4— 4¸Y4œE4©64½©4½‚4â14}f4Eµ4þ.4Èu4À4¢4š£4—÷4—Ö4ž4»4­–4ìÍ4ÙÖ4Y4g14ªÒ4f44—a4–Ò4–t4™ü4ñ¨4ÖÀ4›Æ4ÌÐ4ù³4åp4Ýa4:4ø²4`•4ßç4Ë4ž‡4š”4–¤4˜W4ýS4v™4:ê4­Ö4m~4½ø4üp4L4RÔ4,T>4}q4q¶4S4í"4­Ã4H4ýß4ùÁ4/Ž4Òæ4<ù4É¥4b‘4wÆ4eå4)¨4xë4ï4Ïš4ƒ¨4C…4¾É4,Š4‡Á4•o4Ük4Ûý4Iß41±4t4E;Á4 5444T½4²K4+w4Æõ4óë4`è4Ù³4Òí4$4F4¦C4 «Ë4;"4 ø4 Hò4·¨4Ç'4Žè4DÕ4Ôû4 æ4°S4*84Û4K4ÖŽ4 •4,¿+4¡4Ié4Õá4; 4Ç4½º4ä¯4°„4¦4ªG4¹Ë4ï4œH4¼46û4!¦4‹n4á4ð4‡©4aÎ4 ³4à 4Ô4¢B4¢”4¶¡48404µñ4|D4B4Ýx4*}4æ4àj4P4%4ù4« 4ø4™4—¡4™ø4™ü4¦04Ð4 å4é4ײ4£84¼I4œ“4Ík4ý/4%4º4¡Y4Ÿ…4š4¬4Œy4 .4[á4Ò-4Òa4'A4›E4™l4™]4Ø4½C4§á4NK4b@4ÂÎ4îî4zl4v—4/4ÆÙ4ƒ4É÷4›4˜$4—4%'4 _4¶f44u4žá4Ÿ²4¼94û 4’s4ÊÄ4á(4»Z4œ-4˜'4˜F4«@4 S4éÀ4®Ñ4™T4–p4—_4žA4¢#4¥/4¡4²¥4ó(4·w4 ‘4 ß4 Ä4œ}4ͨ4bA4ù†4–£4—Z4œD4¤4·£4µ¼4Ã24 ä4Ú 4¢4ÑW4ά4´}4¡v4-ù4 ¼4—å4˜W4›,4¦Ï4ð÷4Ó4̤4ì4L4Ö¿4žÕ4§“4ƒ½4ÁD4æÇ4¶@4–£4l4Ê4­54#4ñ4Ü4°³4ÂŽ4¤Å4«U4—4Ë”4¡~4ÎÃ4¶}4˜34¤n4§ª4¬š4£Ò4Ž•4 i4D4Vó4>”4 4˜¯4—(4–Á4î‰4ï¨4 3â4 œª4‰Á4gR4E4”â4J4Ìü4 ¨4Q4Ù4í¨48²4ÏÝ4©µ4´í4å54–4[4.u4÷'4vH4(4‚4H¨4XÎ4·4#14þ4$í4½º4´'4ν4ªV4Ìö4ÿN4Cz4œ>4wÌ4¦34–Ì4!y4ä’4ζ4°z4Á’4Óó4é%44à4äø43Ì4y4 ö4ûÞ4„4ÞY4 hN4‰4…4Æ·4®î4Ç­4ש4¿ 4ÒU4Ó=4‘Ì4EÙ4b 4ô84ºF4kÖ4Fc4`©4@4\/4µF4¢Ñ4¬ñ4ÊÊ4(4Úæ4×U4ò4fù4ùf4 ®ß4…Z44ó4¢³4õ4Èn4©ý4¡44¦¾4I404ïH4£'4v4N—4w4À4bÅ4³F4Ī4¥¨44ïà4ª‚4—4œu4«4™¶4 24Ép4e¯45>4ã¡41p4 ™4¢x4#û4´b4Ê4žs4–¦4–4–o4ž¶4Ó4 T¿48Ñ4Ê`4I4TT4¬‡4˜4›©4Ê»4¤v4™64¬P4¬º4šÊ4¢4å?4ms4uâ4|@4á¿4¡ä4š]4—è4–U4ýÖ4ÓÔ4™ 4›Ô4™¡4–y4–¶4›Y4@o4RŽ4°4Ó4ª*4˜ù4—|4—¿4žÇ4µo4¡È494—–4–k4–£4šº4¼4¡l4›<4£¸4™X4Ã4œ·4 44Ÿ’4–Û4™14Uº4¤4––4–±4š4Ÿ"4ª4¬¯4³“4€4«ÿ4›Ž4F£4ÖE4™B4—v4U4˜4˜X4—4›54¢¯4³‘4À14Ç"4Ï4Äz4Û»4óy4­M4Ê64â4³À4™ä4˜04™ä4žR4¦,4µ\4ò4ïW4ÝÎ4Ö‹4«Å4h4™ó4£™4›4™Õ4›4›ã4 Õ4£~4¨X4®D4ÿ4³j4 ?p4p4™¸4ñ/4™Á4š4—ú4¥[4íy4q34be4NÞ4@•4c4Ôp4˜š4ï4Ìï4& 4¼æ4°“474®®4«g4ª 4´±4æÈ4´4 ¤4úw4mq4ýc4S¶4ü“4ئ4¾ž4[4òž4~4±4§ 4©]4±!4ÀF4ác4Îl4£04ÑÉ4·w4P/4ÌÞ4ÿ¢4½Í4àw4­õ4·M4¬¡4n°4°+4 4#44‹‘4}‘4&ü4éT4ú‚4®f4¬4 {4¥Ö4Æå41K44½4246;4a"4‰K4|4R³4-O4ê¤4d4•4°‰4¥÷4°4ë‘4>ê4à‡4¼B4éÁ4?b4ö—4p\4 ÷É4€Å4 Î4âß4ë™4­64¡æ4 ß4¤¡4Ì}4¦4"4ÂÕ49Á4¼"4hN4Èy4ÄE4âz4€24#\4®D4 ì4—4–ž4˜4˜_4–Õ4éµ4- 4Š{4¨Í4Á­4l4¤4i•4°þ4žŸ4!4–“4–ˆ4—l4¦4Pù4¹N4Ù%4F4¥¯4Ýé4£f4še4­Ë4¬4›ž4™Î4™x4™Ö4˜Æ4›Õ4Â14¿²4\4ÙM4É‚4§4š‰4™Y454«á4œO4—Ü4™4˜Ü4–Ù4–4ž4q94y`4¦Z44÷Ü4—ƒ4—}4šó4žì4™´4˜Å?M»Þ?MÀ?MÕP?Mx?Mww?MCë?M ?LÙ6?L Ì?LnW?L<?L -?KÛ$?KÂ,?K³Ë?KŸ‘?MÏ‚?MØÀ?Mær?M¥ ?MÄ0?MWq?M:?LÜ6?Lyã?LTŒ?L$?Kö?KÉË?K«.?K“4?K†¦?Mâ‘?Mì|?Më6?MÒ?Mà?M‰Æ?M=Š?Lå>?LX®?L@t?L R?Kß?K¾¸?K›?Kj?Kdò?Mú/?N w?N/?MÞ?MîV?MÓð?Lü?LÛŒ?Lá?L\?KáW?KÄ^?Kº+?K’T?K.,?K:?N p?N*)?Np?Mõf?N\?Mˆç?M*7?Løa?L¥‚?Lf?KÀ«?K¬•?K¸K?K`Y?K €?KÅ?N&J?NU?MñÓ?Mÿ~?Nu?M¢-?MGÑ?M À?L²D?LG*?KÅ(?Kž?K¯¤?K@y?Jãí?Jü ?N5Ò?N?Mçð?Mà?M¿?M˜?Mm’?M6â?Mœ?LÔX?L¸[?LhÞ?Kóß?KÅÑ?Kw‡?K/¹?K*•?MÓÚ?MÓ9?MÇß?M§Ä?M¯?M\…?M1è?M®?LÓ3?Lµ?Lm•?Kø»?K˵?K¬8?K”D?Kt?M»å?M¸¼?M¯a?M“,?Moó?MMÁ?M'í?Lûß?LÏ?L¨ª?LrC?LZ?K×?K¹r?Kž?KB ?MÏí?Má-?N \?M‹À?M¤–?MKë?M?Là_?L¬ö?LsÀ?L7F?Lž?K±y?K˜2?K¬?K†µ?Mè@?MüW?Nx?MÆ÷?N\?MT\?Léå?LåÍ?L¢?L\Ÿ?L ‹?KÞG?K§.?K‚¿?KkÒ?Kk{?N…?ND?N&ž?Mù?N(÷?M—.?Mæ?Löx?L™\?L;ý?Kç ?KÀ??K¥í?Kr™?K2ì?KBy?N#-?NJG?N„)?MÆì?Mðo?M½¯?MC?Mý?L¦Ú?LP¥?K³á?Kø?K¤3?Krx?JØ?K í?N=S?Nhœ?NV”?Nu?NC?MÓã?Mo3?M;‡?L°?LH#?K‚Ý?Kp@?Kp?K2c?JÚ?Jên?NbB?N‹™?N ¹?N0C?NAZ?N='?M\)?M0\?L¼Ä?Lo?KˆB?KYw?Kiw?Kù?J•?J¼¹?N‡Ã?N\ ?Nb?N[°?N8u?NU?MµŽ?Me?Lü…?L)z?K&Ø?K.N?K.ß?JöC?J¢q?Jª‰?N€æ?Ny4?N¡x?N€¶?NFP?Nœ?M±Ú?Mjª?LàC?LJ•?K??K#¯?Kî?JÈô?J ?J£Ÿ?Nç?NŒ‘?N|€?N¦y?N^?NW?M|˜?MD÷?Là;?LVÐ?KeÑ?K?Ò?K/?J›J?Jˆ?J¬Ç?Nh?N…R?Nm~?N¦y?N„/?M×ø?M¼Z?MM´?LÀÞ?LtW?K[ø?K:*?K4È?J«ã?J¡ý?Jä?NK?NY3?NgŽ?N‡?NYÒ?N÷?Mx&?M! ?Lç?L¢‡?KtF?KK{?K–?Jã#?Já`?K?NAØ?N 1?NCÅ?NR?NG?Nû?M0Ä?Líi?Lʾ?L‚~?Kíí?K›È?K"Ÿ?KØ?Jþ??K"H?N( ?N"k?N$?Nþ?N[?MÊ5?MM›?LóG?LÌ6?L™§?LH\?L÷?K?Kš?K>?K3?N ´?N2*?Mÿú?MÌ?MÅ(?M˜¬?MPÙ?M ›?Lã]?Lå?Lu[?K¹à?Kx"?K0·?JòÝ?K ô?Móñ?K:©?KO?KhU?Kg^?N ˜?N?NÊ?Mú¡?Mëö?MŠŽ?MDh?M5?LÆf?L?KÂ8?KÙ?Ki?KI?KE*?KK‘?N/[?N>¡?NAî?N*??N ´?M·K?MaØ?Me±?LÅá?L?K¤?Kœ?K”œ?Kn?K Ë?K) ?NYã?NsZ?N}ž?NDX?Mòà?MÙ¡?M¤4?Mr‰?L²Â?Kþˆ?K˜š?K{œ?K– ?JÑÉ?JÊ`?J÷?Nyp?N…m?NŒh?Nh?N"Ï?NG?Më¶?MàÎ?L£—?K¡6?Kn?K)‘?K¶?JßÊ?J©ë?J¿€?N±¬?Nu?Nƒu?N‹ë?Nh°?Nxu?N‰?Mª?L¼ª?K¡m?KVc?Jòq?Jë±?JÃ?Jw?J‡Å?Ov?NBÃ?N—R?NÃf?N|Ì?Nu¥?NG½?Mç?L—'?K¥s?K ó?Jé‹?J͹?J P?Jx(?J:d?NÑ?N˜ú?NÉ?Nß/?NÀ}?Nƒß?N='?Mäì?L—×?KÀÿ?Kg?Jß%?KÒ?JP\?JFA?J ?NÅ?N¹«?NÖ"?O 4?Oé?N“Z?M²A?MƒC?Læ…?Lˆ?K]—?JÆ‚?JÅY?J6?J' ?JOê?N¯U?NÀÓ?NßH?O/Ø?O ?N?MÂ_?Ml?L÷é?Lq[?KM ?J±×?J•Í?Jg“?JNC?J™?N„é?N§\?NÏ,?O™?N‘”?N:?M¹)?M;?MÈ?Lµœ?K'&?Jé?J@?J÷?JŽå?JÉ?Nkÿ?N‹¨?N¬Û?N“?NfÑ?N@7?M‰à?Lü‚?Lßå?L‰Ÿ?K°ä?KxD?JŒw?JD?J³ý?JÖ ?N_)?N| ?Nu?N[†?NCd?N {?M©?LÌi?LÈ}?L”c?LÜ?K»²?JÉŸ?JÌ?JÍ ?JÝ„?ND‹?Nè?N7?Mïî?Mök?MÈ{?Mu®?Mˆ?Lá•?L¾V?LB¸?K˜s?KFÙ?K&?JÆ?Já?NÃ?N%?N S?Mç3?MÒ?M£°?M`?M"H?Lçä?Lèe?L¤÷?KQe?KYì?K7„?Jô§?Jþ#?Mö?Më8?MÝ?MÉ*?M° ?M‡†?M]&?M4´?LÅ’?LÞG?L~h?KŒá?Ky¡?KZ,?K4í?K-?Mü»?Mý˜?Múl?MëÇ?MÄÄ?M‘í?Me?MQ?L¹”?L¬M?KßF?KÍ­?Kp•?Kb?KU'?KDn?N"Ñ?N+Œ?N-“?N&‡?Mçá?M±z?M?Mo©?L£ã?LNX?K¸1?Kš–?Kq•?K?ç?K) ?K%Q?NV?Nh[?Ni±?NwÖ?Mße?M×R?MÃ?Mà ?Lš?Káo?K‘õ?K{?KžI?JÄ3?Jà=?K&«?N’¶?NÍK?N‚°?N¢,?Mí¾?N ©?N?Mýî?L­?K”þ?KiÀ?K/[?K2:?JÑa?JžC?Jù#?N³q?NÉÒ?N¾j?N´0?NVw?NcC?N[@?Nk?L•?K Ü?Kv?JšM?J©1?JÂ?JK?J‹?Né3?NÄ[?Nå8?Nçì?N§Ó?N¾X?N¿??Mò‰?L™Ë?K˜ ?KK™?JE0?JJˆ?JT?J5Ó?JR«?O¬?Nåü?OX?OFC?NÇ ?Nî°?NüÅ?NH?L‘Í?K2’?JÏ—?J”Í?Jî?IÿÜ?Jê?JÑ?Ol?OH?O.é?O_ç?OS¼?Nð?NÅd?Nü ?O)­?Ozz?O“?N½?N7?MM*?M.d?L˜{?Jê¹?J$?JC?J2Ú?J"£?JaT?N©í?Nûë?O?O ?Np?Ngb?NK?MX8?Lü?L¬·?K[?Jî?JD_?JYÓ?Jo&?J‘5?N•Ä?Nãí?NÚ-?N†E?NAý?N c?MÇ·?M?y?Lï?LÁK?KŒ”?J÷Û?J¤ï?J§©?J§§?J¡>?NlC?N’×?N‹Ë?NG´?N@®?MõW?Ms=?M/£?Lê]?L´›?Ká}?K7Q?K?Ko?JÎ?J«î?N@Ñ?N>Í?K7Ô?Jó?JÒ9?N1Â?Mï:?Mì“?Mé ?MÚ™?M«H?Mx:?MVï?L³?L?L7+?K©1?KŠé?K3Þ?Ki?K?Mûì?N'?N Ý?NÄ?MØî?M¬?M½o?M&?L€Ÿ?Lr?K×>?K£¯?Kw”?Kw™?KLÏ?KF?N*ã?N8¸?NFý?N}{?Mö*?M¾>?MÑV?MGü?L“í?L/ê?K´™?K{í?KW¯?KT´?K$Ÿ?JðÙ?Ny!?NE?NqI?N}õ?N-ý?N(?N?M»z?LžF?KÑ?K}I?KI¤?K=¸?JËb?JÙÚ?K?NÌû?Ný3?Nr{?N™Ì?Njç?Nfë?NW?N'¯?Lâ¦?KP ?K,Z?Jç ?J¶¥?JžŒ?J‹?JéY?NõÇ?O ƒ?N߬?NÚÛ?NÑ?NÍ?NÐ#?NHù?LÒÖ?KIÍ?K3_?J2Š?J ?JWÌ?JË?Jbû?O"ð?OSƒ?O\v?O}?O¦?OE&?OMœ?Nj?L#?KD?JêÖ?I¬p?I¦]?IÜè?Iå­?Jø?OS"?OÎ8?O`?O7™?Oï?OÓ×?P:?NÈ€?Lib?JŽ?JÙ?J ˆ?I3¼?I†Ý?I| ?IΊ?OQÌ?O³±?O¦‹?OŸe?P?Oü-?P.?OÚ?L£Ò?J—â?IÇ}?I`€?IP°?IQœ?IJ$?I¾_?O_­?O®§?Ou?PS?P>B?OÆl?OŒA?NêN?MNÈ?JØq?I­Î?Hþ ?I;º?I]@?Iqô?Jr?O3n?Og€?O’É?Oøl?P)=?O¾ ?O –?Nz™?MUd?KZÎ?Jò?I Þ?IQ?I”@?I„7?IÙ5?O ˆ?O[$?Obv?O¢e?O§??OsÏ?O,É?M«ò?M-?L‡?J`¯?IŽÕ?I…´?IµW?IÏ}?JÔ?Nä?O;ô?O6Ñ?OCQ?On?NÞW?Nª#?M²Æ?LÞå?LE?Jùå?J/Y?IÒ'?J®?J/3?JBˆ?N¿Ì?Nü?O;Â?OŽ?NT¹?NY¬?NÝ?M‹r?LùÔ?L©'?K@?JqÆ?JOr?Jxq?J¢?JA ?Nè?N´3?O ?N‹?N8 ?N"§?M¹í?M`;?Lém?L}|?K´ ?JÔ¡?JÌ??Jå½?JØI?J`ó?Nfµ?NX3?Nh²?NLE?N8Œ?N<Â?Mr¼?M1?Lη?L?KþŽ?K/.?K!†?Kä?Jç8?J³)?Nˆ=?MÞ­?MïM?Nó?Nf?MÑ>?Mj?M#”?L» ?Lmï?Lp?K¶?K‰=?KR?KE?K?N.?N ?N^?Nh?M÷…?MÓà?MÅ?M ??L–ù?LžG?K¢~?K‚¹?KT‹?KKÛ?KG?K/?NM¿?N=Ó?NJ?Na˜?N+‰?Nò?NEé?M)6?Ln`?L8ë?K•?KX~?K?Kÿ?K(X?Kb?Nµ@?Nxá?NyB?NÎ?Nu ?Na¡?NO›?MÛ4?L`?KÏß?Kj?K'?Jº'?J²›?JÚ£?JÛ¦?O'Ù?NÄ™?N ü?NÇ?NÐx?N¾i?N¥)?NUŠ?Lƒƒ?Kp¬?K«?J×P?JU?JAˆ?Jt(?Jš?OS´?O%§?O.?O§?OjÈ?O?O4š?NœÓ?L«Ü?K"ƒ?JŒ=?JaÂ?Iaô?IÃé?JX?J8?OeÐ?OÍG?OÌ?O0í?OÃ&?OÓc?P ?Núþ?Ly ?K8?I—¯?Id’?I%¶?Ij·?I£‘?IåJ?O’1?P;-?OÔ?O”–?Pd_?PÔ¶?PÞ“?Oåd?KÄ2?J Ï?HÓº?HÛ¼?H¥ˆ?I"?Iw?IƒZ?O‡¬?Oýc?P$Ò?P-4?PüY?QKÂ?Qà?Qlä?Kh÷?Hïm?H¦œ?H.?H>i?Hº`?HÛl?I`®?O|z?OËÚ?P#?P¦c?Q‰?QÓ?R½?P³q?L–‡?Hlî?Hî?H/?H}?H·Ú?I h?I‡±?O†€?Oy´?Oï3?Px¨?P˪?P¯Ê?PðÑ?P³?L’??IxÙ?I‡?HJß?HnÐ?Iº?HöÂ?Ia°?O\4?OmÑ?O©ã?P%L?Pt˜?Oõ)?OîÏ?NÌ?M3ó?JÊ ?IÖ¶?IO%?HÎT?I(¨?IC_?Iš–?O¹?OIw?Or%?OÆ?OÐE?OYö?O2?N§?LÉ—?K|›?JŽu?JW?I^?I°0?Iç?I×ÿ?NÞm?O ž?OE¼?OŒ“?NÓÐ?NÝ ?Nx?MÐ?M >?KÜ ?JÕ;?JU÷?J ÿ?JR?J²_?I¿ü?N¥æ?NÅ(?Nõ?Oú?NlÓ?Nq7?N ?MÛ2?Lê£?KÉZ?K=å?JËO?J«†?J­¿?JË;?J,F?Nq ?NqE?NÈ?N„$?N[?N$m?M¾3?MÖ?Lá[?L[?K¥y?K7d?KÖ?Jäú?JÒà?J«à?NUá?N’?N!ê?N4à?NY ?M¡ ?Ml)?MF?LÈN?L8?KæÐ?K§Ò?KT”?K'?Jú?Jö?N?MàR?N?N ã?NÀ?Må?M±$?M5”?L¶Ñ?LL1?Kä?KÅQ?K©?Kå?K)õ?K{?N~?N0t?NN\?Nn?NJV?N5?N&É?MQ/?L’±?L”?K¦„?Ki_?JÖó?JÝë?J÷À?Jê„?Néœ?N~2?N’ä?N¸Å?N 5?N  ?Nƒ)?Mű?Lƒ?KÉò?KTt?J÷¬?JeM?JG?JÌ:?Jµ°?O%÷?Nßz?Nì²?O?OÜ?OU?NüT?NK?LžX?K¤º?JîÒ?J g?Iª7?IúL?J\å?Jm ?OS†?Oaq?O[u?Ok€?O¿l?O—ù?O½°?Nžý?L»E?K?J‘?Iúú?II?Ix??IßÂ?JV?O‚_?OöÓ?O÷ê?O»?P€ë?PrE?Pøß?OÊ;?Lú$?I&ô?H»î?HÆ?H’©?I?Iƒ?IÑN?O©G?P&?P,Ç?Pu?Q¤o?Q¯…?RÔ¬?Q+¿?LÒX?GÓ?GV?Gj&?GÄ‹?H{r?Iò?IT¹?O²ß?PY?P\i?Q «?Rg"?Rõ?TvÖ?S ?NB?Eyƒ?F”©?F[Ñ?G€?H?HÀä?I?O˜ü?Oú\?Pz&?Q9{?R/‡?R€¹?Tìõ?SŸ¹?MvÛ?FÓ?E:ó?F0¼?G%Ñ?HL”?HÒl?HÕ·?O"?OÄŠ?PVb?PùV?Qþv?Qš¨?SR9?RŒl?KŸ ?H½7?FŠî?G#€?Gíg?H²©?HåT?HÒ8?OhG?O— ?Pº?P”ô?Q^?Pü'?Q{à?O\?L»£?Ió?H‰?H‰¢?HѰ?Hû‚?I?I0›?O6?O:ý?O½O?P5‘?P ?P3Ê?Oàð?N&ø?L½ï?KÓ?I@?I|?Iu0?I­©?I…°?IŠ€?Nó5?O{?O`ô?O›þ?O]P?O˜¾?NÝm?M¦^?M6?K¡Ì?J?NDs?NV÷?MÂø?Mu?M;$?LÙâ?L.€?Kåã?KŠ?K9ô?KJø?K`Ã?J­?Nhê?NzŠ?N’÷?N’‘?NdR?N0Ê?Mâ¡?Mu¤?Lê?Këm?K™C?KM?JØŒ?JÙõ?Jþe?J·‡?N¬È?NÖ?OÓ?N»©?N´?NÒ8?NbÑ?M¾A?L÷â?K·°?KI?J‰Ô?J[U?Jn5?J¥R?J½Ÿ?NïÐ?O/n?O‡?O0?O3í?OŠÀ?O«?MÍ«?Lßt?K¢«?JF?Iì¼?IÎ?J?JQö?JÑù?O7Í?OP?O¿‚?OËR?OýÎ?Pø?P Å?N{+?Lá8?Kà?Iœ6?I »?I&^?I§†?IŠ5?J ?Ozœ?Oø?P$·?PN‚?Pþ?QT?QÈV?Oó?L×]?I}?GÚ¤?G£?Hè?Hü?Høx?IA?O£Ž?P;Æ?P§*?Pµ{?R×?Syí?Så%?Sï?Mm‚?F W?Eÿ?F!°?GW§?H/À?HÇè?H°Í?OÈ5?P8?PÌ?Q?¶?R°?Tu?Vð½?Z‘—?Oæm?=Û»?B%Ö?Du³?F·?G…_?HR?H¯ö?OÛÛ?Oûø?P¹“?Q?S?Ttæ?WT#?[¤?YR?9¨b?@Ï?Dy@?FG?Gze?Hpk?Hܨ?Oœë?P"?PµÊ?QdT?R´ã?S½?T7–?Tt?N™Ù?D+?Ee?F €?G?H½?H¸•?H Æ?Or~?Oô{?Paý?PØŠ?QßÖ?QÓÖ?QWÿ?P u?LºQ?Iœ/?G·‚?Gó‰?H§?Hmn?HÊF?HýC?O=r?O¢É?Pž?PO?P³Q?P=Œ?O÷@?N¼\?LØÄ?JÑe?IK/?Iiw?I(±?IC'?I?IP?Oï?O^’?OÁ?OsË?O“5?Oj;?O!?NÝ?L®‰?K‡…?JÃÇ?Ié?IÜÚ?Iäf?I™?Iºt?NÍ?N÷á?O/ø?O?ì?Næê?N®ô?NZÔ?MuŠ?LÔ ?KÔ]?Kbµ?JœB?JgÞ?J9‘?J%Ú?J3Ê?N̘?Nƒ?N}Q?N»ÿ?N’ô?N'™?Mö?M6¹?LØó?LBà?K¨Í?Jøs?JÝ?J—g?J¡:?Jr?Nc??NKë?NN$?N{??NNï?M“?M†?M6ä?M U?LC)?Kól?K3¶?K/‘?Jü·?K),?JËŠ?N4&?N8ß?N9w?N;‰?N+Å?MÇ€?Mu ?M/\?LÏ›?LG¯?L2s?K\í?K<2?K/¸?K<Â?JåÑ?N_A?NqÁ?NŒ?N¨6?N=³?N/g?MÖ®?M8¹?L½t?LÖ?KÅ?KO?Lh]?I|?FÑ…?Fòí?G¹s?Hž;?I]?H·5?O™ì?PY?PÔ?Q6$?Rd{?S× ?S¹ß?Rí,?M¢k?Få?DbÔ?Eʱ?GP?HY¶?He‘?H¶:?O¦ä?Oÿê?PŒ2?QRÑ?R¥â?S©â?T¿£?RyÅ?OÓ?Eí?I·Ù?I~É?I!?I?Iž?Nþ•?OXÜ?Oœ!?OaÇ?O•ú?OB[?Nä$?MçÊ?Lƒå?K0b?Já?Jg×?JA?JX?IŠ?İ?N¾µ?Nìa?O ?O„?Om?Nõ\?NhŸ?MY?L·?Kr?KX?K5/?J]°?JFW?J,‰?J9°?N•á?NŽs?N‹ª?Nš™?N‹t?NhR?NÔ?LãŸ?L©z?L ü?K¯:?Kf9?JÛƒ?J®›?J×)?Jn=?NP@?NkE?NKD?N7?N"8?MÞè?M˜?M ã?L¿\?LNÒ?L8?KZ"?K>ÿ?KM?K:?Jȃ?N.?N0·?N(«?N%¯?Mù±?M¸p?MpH?M6Ã?LÙ@?L6‰?L ?K]?K_-?Kû?K?KÊ?NV‡?Nnz?N€Þ?NŸ¸?N;?N n?M¾?M ?L±¯?LAÄ?Kø ?K†¿?K“ ?Jp?J¯u?JÎr?N‰ ?N¨h?Ní?N®L?Nuì?NmÂ?NB?M/)?L¦Á?L.??KÃm?K Þ?K&·?J;œ?J]¹?J£÷?N¾ü?Nó¥?O>3?Nû)?O/Ð?O?Ný?MjÛ?LÆ ?L!¡?KfB?JO£?J)€?JÔ?JFÜ?J}?O¢?OIÊ?OÊÒ?O&ë?Oˆ?OŒä?N·æ?Mï?Lø?K‘ ?JÁÀ?Iê?Il?I¼ô?J ˜?J*Ý?OH±?Ot˜?O£Ê?Oݽ?Pjñ?PCœ?Oð?N †?LòÆ?KÖ?I¦I?I=Z?Iâ?IO¸?I»?IÄx?OFs?O¬?OôÑ?Pwž?Qjþ?Q—Ô?P,Ö?NSF?LÝ?K½?Hz7?H/r?H²?HÞ§?I?IT?OTë?OË=?P0ä?PáM?Qà?RFÐ?P¾?Of÷?M:Z?IÑY?GöÙ?G¾?HD?H…?HÖÑ?I0è?Oq+?P$?P ?P¯ï?Q£??RVþ?QÊÓ?NuÒ?M1Î?I†Ò?G±?GàË?HEi?H¯?Hñ ?I¸?O_Û?OïH?P/R?P¼‰?Q‘¦?QX?Q%i?N%|?L û?J8?I{û?H4V?H»…?HÏ?HÔy?I%½?O=!?O°€?P Ö?Pz¿?PÕØ?P¦è?OE˜?NIh?L<¨?JÀï?J˜I?IY[?HÊc?H÷^?I#i?Iy?O+?Oªª?OéÙ?O¡‘?OÀ¹?Ofs?O•?Ni–?LAz?Jùï?K!?J¿?IŸµ?IR­?Io9?I¿k?Nð?On¦?O@d?Oß?OMa?O‘?Nr?Mí‘?LT~?KL–?KMå?JǦ?Jm`?Ißs?IÔP?Jÿ?N§„?NßV?Nݹ?NÝ?Nö€?NÕi?Mù6?Mk?L€?KÃV?K™=?K],?J“r?Jo@?J^±?JVQ?NoQ?NŽ?Nƒá?Nyš?NwŒ?Ni½?MÝ+?LÔ ?L­ì?Kîê?K¿·?KŽX?K(f?JÇè?Jè8?J‚Î?ND·?N}!?N?A?N¾?N x?Mï?MÝ®?M…÷?MTu?M¶?Lɳ?Loé?LZÌ?KŠ ?K^­?K9Î?K)n?K%Ç?NUg?N}s?N> ?N5,?MúÌ?MÌá?M¢Ô?M¬?L³Û?Lh´?L>?Kb"?KC€?JÝ?JßÜ?Jði?NuÓ?N (?N×÷?N\?N:Õ?N­?Mà?M0ã?L»/?Lg6?L,9?JÜ/?JÝM?J•“?J¡5?Jì?N  ?NÐÀ?Oé?NÙÌ?NÌ(?N}„?MáB?M† ?L¿·?LOÙ?K¼À?Jh½?J`¹?JR>?JnL?J”ÿ?NÖ÷?O ¤?OuO?O9–?O %?Ņ?NP?Mö4?Lº?KØ?K,Ä?JRI?J+-?IÚ›?J(p?JUä?Oc?O„?Ozg?Oèf?OÑ?OAi?N—¥?M÷é?M=ˆ?KŽ3?JŠ?JÞ?I?I‡7?IÞ$?J Ð?Og?OPS?O­Ü?PMC?OþD?Oݶ?O2)?N[?Lñµ?K£û?Jí?INú?Ign?I@?IM©?I«A?O¯?Ou`?OÍ?P:Õ?Ps?P_r?O“â?Ne…?Lû=?K:9?I‚ó?If?I`?Hã±?I7õ?I˜‹?O;z?O®A?Oßi?P6x?P›2?P|?Oð¬?MÌ:?Lö?JËC?IƒÉ?I‘Ñ?I1?Hæj?Ib»?IÖ?O8ý?OÑ·?OÞž?O؆?P]7?Oþš?OÁ?O—?Oâ¨?N×ê?NÔ?N‰Ò?Mó ?LXÆ?KLò?KZc?JÒ?JI?Iâ5?I•W?Iæó?NÄÈ?Où?OŽ?ON?Nâ¥?Næ£?N-ð?MjØ?LR¿?K?K€Ë?KP¬?J«„?JQr?Iðâ?J$E?NŠQ?N·¡?N¶K?Nµ–?N¶r?N¿0?Mo?Më?L‡p?KÞÆ?Kœê?Kgï?Kp?Jòý?JžE?JMÇ?NZÆ?NÇ?Nf ?NXJ?NU?N9²?M|‹?Lù-?LÑ»?KÙÏ?Kµ ?KÑ?K^Â?K'A?Jý‚?J±f?N4d?Ncí?N&-?MýŒ?N0?N ?M;U?LÝÑ?L³Q?L_?Kåñ?K»ÿ?K¥»?KIw?K?_?Kr?Nú?N œ?MëT?N÷?MÃ×?MTÿ?M8E?MU?LÇÁ?L|R?L3Œ?KÆ?K•Á?Kf±?KHÀ?KD{?NK,?Nk ?Mì?N Ý?Mà ?Mœ ?M„ª?M ?Lš?Ls=?L!©?K—?K‘?K V?K7?K î?Nd?NwE?Nlz?NZ½?N?Mß•?M¸0?M&d?M¾?Ljo?L ý?K 2?KC?JÐÒ?JÔÚ?Jè·?N‡?O#?O_Ž?O\:?Osˆ?OK2?NçÑ?Mòú?LÎ2?Ká:?JkÊ?Ií?Iá??I¯´?IÃÖ?Iþ?O$¢?O' ?OE5?Ov—?O'?Ok3?O-/?M¶?L‰v?Kä,?Jn‰?J¥?Iãw?IŽ ?IÁ>?Iü}?Oþ?OnÙ?Oi?OPR?OqÖ?OM¡?Oÿ?MDh?L~Ï?KžÄ?Ke?JA9?IöK?I²Ü?I¿.?IüÆ?NãÈ?O0%?O6¸?O“2?OR?NÜO?N˜Ú?Mhi?Lžæ?Kœ?L¶?Kâ…?K‘é?KxP?KQF?K"³?JÄÓ?J£·?NU ?NpÑ?N:¾?NDÅ?N<°?Mäw?MR¹?Líž?L¿¤?Kû?K¶Á?K ?K}ñ?K\-?K,ý?Jü$?N+Ü?NZK?Mûø?M߈?Mè?MÉ”?M=ò?LÛK?L}?L-v?Kê±?KÆî?K­&?K°?K¡?K€?Möó?Mß/?MĤ?M¿?Mž?Mh'?M5õ?Mø?LÍ?Lˆb?L0;?Kçn?KÀ#?K©?Kjä?Kms?N!£?NÅ?MÞÍ?MÜp?M¼€?M(?Ma©?M˜?LÍ¡?L…æ?L(º?Kß?KÄÑ?Ksª?K$à?K K?NFÿ?N x?N½?N—?Má›?MÉ%?MŸì?Mº?LÞ0?L¡Œ?L#²?KK6?KO?K‰?Kÿ?KØ?Nfë?NYƒ?Nd«?Nc„?NC?Mý»?MÑz?MD?Lݨ?Lr‘?Kú…?K"¦?Jä¨?JÜ?Jä?JéL?N¦q?N™?NŒ¿?N–?NV?N -?M½?M~±?M>Þ?L1?K“?Já7?Jª’?J¤ë?J·:?J¾w?NÉÓ?NÅW?NÑÚ?O¿?Nl?N ¯?M²ž?Mx?M&½?L”k?K›?J‡?J’È?Jt¹?J ø?J‰ù?NÇÑ?NÒ¤?Nø?O_?NÂI?NO?MçZ?M‚å?M]?Lw"?Jõ¶?J…û?Jwk?J\?Jg¬?Jf»?NÔ??NÝð?O Á?N¾f?NÆÂ?Nƒ?NJî?MÌ?Mž?Kºÿ?JîÒ?J–š?J@?JZ?J8¹?Jj?NÚ?NÎ.?Në?NàÕ?NÝ1?NÌ×?N{¢?M›¯?Lò?Kå—?K/?J·/?J“Ÿ?Iñ(?J"~?Jf?NÑ?NÔ`?NÐ6?Nݵ?NìB?Nýý?Nu?Lú>?LrÔ?Kë˜?Kyl?KQü?JJf?I÷?J"¿?JXü?N¦¹?NÑN?NÙ"?NÖd?N¿?NÎ?NVŸ?LäO?L©?Kò?K›L?Ka`?J^b?JM}?J9Ç?JW¦?N†w?N¸f?NÄí?N°@?N…r?Nƒ?NÍ?Lè?L•ï?Kùj?K‡|?KU”?KN?JÛ%?JtF?JIÿ?NoK?N‰F?N`?Nˆê?Nr ?NEâ?M«?LôÜ?LÁ&?L Z?KqŽ?Kfþ?KM?K ?J±é?JŒÁ?NUÇ?N\â?NW ?Njè?Nyâ?NÈ?M:¨?Lâ’?L›—?LN?K©Ö?KŽZ?Kt$?KZ4?KŠ?J× ?N\ä?N>?MõÙ?N0Ï?N;è?MÑB?M+(?LÌÑ?L‰1?LF?KÓ^?K´ä?KžQ?KžŽ?KS$?K&?N(H?NÑ?MÐ?Màs?MÝ–?M«¼?MTg?L½À?Lî?L3=?KõZ?KÕ\?KÁL?K³¢?KºS?K_U?MÖ°?M«š?Mœ¾?M•?M‚G?M]à?M2“?MÂ?LÜ(?L§G?L"ª?Kô?KÎÎë_>Ï ½>ψ­>Ï‚>ϯ>ÏÖ{>Ð ß>Ð_>ÏñÑ>ÏÐ?>϶o>Ï–W>Ïpº>Ï;´>Îëg>ÎÃM>Î×ê>ÏÆ>Ï$*>Ï{Å>ÐÎ>ÐBø>Ðc€>ÐfT>Ð[>Ïäñ>Ï´>Ï…·>Ïb‡>Ï5§>Îõ>ÎÂ>ÎÀG>Îçì>Îð£>ÏG>Ï€¸>Ïã >Ð1/>Ð?+>Ð[>ÏØï>Ï£J>ÏtÙ>Ï^•>Ï=Ô>Îîe>ΩF>Οw>ÎÙð>Ï»>Ï)>>ÏAæ>Ï×%>ÏT>Ï@î>Ï¡¿>ÏÛT>ÏÊ>ÏVØ>ÏO.>Ï:„>ÎÇì>ÎpU>În’>δÇ>έà>ÎØA>ÏΤK>Îþã>Ï=Ñ>Ïl:>Ï>ÏE…>Ïâ>Ïu>έ+>ÎaÏ>ο>Î Ý>Îh>ÎBð>ÎXB>ÎTª>΀þ>μö>Îã¥>Îúm>ιJ>ÎÃ=>ΩÁ>ΗÌ>ÎY >Î «>ÍÓ_>Ím=>Í9Û>ͰŠ>Íå>Í¡Ô>ÍáŽ>Î.>ÎdÚ>κz>ÎÚ >Ît6>Î#”>͉Å>Íh>͇_>Í{_>ÍN>Í >ÍgH>ͺ>Í™ç>ÍÆ€>Í›^>Í™ò>ͬÓ>ÍÍ5>ͼ’>Ͱ{>Íxá>ÍB@>Í6í>Í.å>ÌÉY>Ìèô>Ì„—>̉K>ÌÊ >Ì¿±>Ì®i>ÌšQ>Ìy>ÌjU>Ìá>Ísx>ÍLÜ>Ìór>ÌÙê>Ìß>Ì;!>Ì+*>Ì­>ËõÇ>Ëæ”>ËÁG>Ëî½>ËöÀ>Ì'6>ÌI'>Ì…>ËêÎ>Ì >>Ì>w>Ìc;>̆J>˰->ËKP>Ëh>Ë[|>ËqŠ>ËvC>Ë>ÊÕ¢>Êž >ʪr>Ë ¦>Ë—>Ë; >Ë¥²>Ëïo>ÌÁ>ËÚµ>ËK >Ë %>ÊØä>ʹ[>Ê£ã>Êc4>Ê5ì>Ê Þ>Ê'>ÊŠ>ÊæU>Ë+>Ë”™>˾Û>ËÕ¥>ÊœÈ>ÊÀr>ʨ0>Ê\ë>ÉÂ¥>Éj >ɯá>ÉÆî>ÉÖˆ>ʦ>Êv9>Ë>Êù¹>Ë >Ë H>Ë >ÊeÝ>ÊvX>ÊUÃ>Ê þ>ɯ¹>ÉuÝ>É}Þ>É´>ɬÀ>Éúu>Ê%³>Éñ«>Ê‚„>ʃ>ʃÛ>ʱ>Ê^>Ê1³>Êã>ÉÓ3>ɧ<>É„Õ>É{2>É{E>Éxö>Ét6>É{/>Ƀæ>É:>Éø¸>ÊɃ>Ê“>Ê_‹>Ê*ó>Éú¶>ÉÑ‚>É­·>ÉC>É|?>ÉrO>Éi>ÉO >ÉYó>ÉÄ>ɧº>ɾ¿>Éѵ>Ê9>>Ïò>ÏZ#>ÏòK>Ïà>Ð5ä>ÐL¡>Ðe[>Ðt>Ð]3>Ð@‰>Њ>Ïþ>ÏÄ>Ï >Ï+E>Ï>Ïý>Ï?:>ÏhA>ÏÜR>Ðׯ>Ð݉>Яñ>л@>С_>Ðo[>Ð>Ï¿É>Ï«è>σ >Ï:«>Ï>Îù >Ï1¶>ÏB‘>Ï£>ÏÆú>ÐJH>Ð}Æ>ПØ>ÐŒÍ>ÐP@>Ð>Ïʧ>ÏÉ£>Ϩþ>Ï8©>ÎäÚ>ÎÝÍ>ÏC<>Ј>ÏŠ>Ï >Ïq>Ïòm>Ð#;>ÐD›>Ðn¨>ÐP>ÏÁË>ÏÂ9>Ï¿>Ïž>΢È>Φ”>ÎãT>Ή>Îý5>ÏO˜>Ïy>Ïϯ>Ð*x>Ð8Ó>Ð+”>ÏÐ`>Ïr>Ï/>΀>΂Ø>ÎCu>ÎTœ>Π_>Î:O>Α>Îä>ÏÁ>Ï>Ïz}>Ï>Ï4ù>Ï>F>Ï_>ÏÉ>Îãs>Î[W>Íþ•>ÍÁP>Íbê>Íß•>ÎÎ&R>Íß>ÎG>ÎþD>ω>Ï””>ÎäQ>΃j>Îr>ͽ¢>Í×=>Ͱû>ÌÖ¾>Í ”>ÍŠU>ÍÔ˜>Íðù>Íöî>Íóª>ÎÌ>ÍÑ·>Î3>Îp>Íç„>Í»™>ÍaÍ>Í\Ÿ>ÍN›>̘v>̼>Ìu×>̾»>ÍUN>͆>Ì©l>ÌVâ>ÌX(>Ìo`>Ìù>Í47>ÍcÉ>ͯ>ÌÕ >Ìë\>Ì>Ì ‘>ËéO>Ëî >ËÛÀ>Ël>Ì `>Ì>>ËÙ½>ÌØ>Ìm>Ë÷N>Ì{>Ì$š>ÌW/>ÌÉE>ËŸÑ>ËH>Ë?%>Êí¥>Ê“Ê>Êå>Ê^v>Ê G>ÊYž>ʹg>ÊàN>Ë>ʹQ>Ë*Á>˳Š>Ì ô>ˤ>Ë/>ÊÑO>Êué>ÊU¦>Êx•>Éôé>ɉ˜>ÉK[>É >Éó>Ê©>ÊÆõ>Ë#÷>Ë]ò>Ë…!>Ê£µ>Ê¢×>Êf9>Éð…>É|>ÈÝÏ>Éì>É&Ï>É*ÿ>ÉYƒ>Êó>Ê÷Ì>ʽz>ÊÅ:>ÊÈ»>ʵH>Êa‹>Ê*>Ê8¦>É£ö>É °>ȯ’>ȸr>ÈèF>É >ɳ<>Ê>É\„>Ê6T>Êk>ÊR¢>Ê1º>Ê,>É·‡>É}Ï>Éd¨>É&\>Èî->ÈÞy>ÈëM>È÷Æ>É a>ÉMˆ>Éô>É$‘>ÉÚ>Ê2<>Ê:>Ê1U>ÉàÐ>É¥Î>Éxã>ÉE÷>É¥>ÉB>Èýð>ÈçÁ>È•¨>ȶH>É>—>ÉAÉ>Ém>ɶ:>Ê X>Ï5y>Ïx:>ÏÝC>ÐÍ>Ð[Ì>Љb>н4>Ðëù>ÐìS>Ðå>ÐÒq>Ðò">Ð?Å>ÏË*>ÏxË>ÏJ>>Ï9L>Ï‚>>ÏÓû>Ð8à>ЮG>Ðã]>Ñ =>Ñy•>Ñ‚r>ÑwÅ>Є>Ïkø>ÏÑ>ÏÈ&>φ$>ÏLÐ>Ï2>φ§>ÏØt>Ð6³>Ðpü>ÐÊ:>ÑÊ>ÑkË>Ñ_!>Ђä>ÐIÐ>Ïü~>ÐS·>ÏëD>ÏtÂ>Ï0a>Ï>ω>Ïù´>Ð/õ>ÐY>ÐCU>ÐÈ >ÐÜ¥>н‡>Д×>ÐxÅ>Ð9>ÐB>ÏÇ>Ï;>Î×r>ÎÞè>Ïë>Ï#~>Ïk>ÏÉÐ>ÐFÇ>ÐÓí>ÑÀq>ÑŽº>Ðá§>Ð…4>Ïæ.>ÎæA>ÏÝ>ÎÊ>ÎYü>ÎØ>θ±>Φ\>Îí>Ïv>Ð:‹>ÐkÅ>Ïø{>Ð;6>Ð2¢>Ð>Ï›‹>Ï3`>ÎõÙ>ΓP>Î"›>Î|¿>ÎS>Î;p>ΩË>ÎÕÂ>ÎÐ\>ÏOð>ÏÎX>σ >Ï‚Å>ϯ>Ï>ÎÌM>Ίs>Έ3>Íõó>Ì[Œ>Íz>Íiu>Í¡ >Î)\>Î@ƒ>Î0Á>Î7>>Î1>Îf=>ÎY,>ÎI“>αµ>ÎÐ>Í­Ä>Ír–>ÌQ>̈õ>Ì¥3>Ìõ)>Íšs>ͧÔ>Ì¡ò>ËÈß>ÌlO>Ít>Íš,>Ílå>Ìíå>Ì´Å>Ìs>>ÌØ¢>ËÊ‚>ËØí>ËêG>Ì7>Ì,Ù>Ë;R>ËWH>Ë'¦>ËMå>Ì R>Ëéþ>˼>ËXå>ËÀŒ>Ì»>Ì™R>ˇ>ËTS>Ë4>Ë ]>Ê·>ÊW>ÊB>ÉçA>Ê.o>ʵ¸>Ên»>ÊÑÌ>Ê­t>ÊÝB>Ë`>ËÒ¤>Ë(]>ÊóK>Ê­ö>Éÿ>ɯŒ>ÉÁe>ÉbP>Èâ{>È`¾>È Œ>Éï>ÊSâ>Êa>Ê•ò>Êâ>Êù¤>Êï>Ê{A>ÊÊ>É9“>Èüÿ>Ȉâ>ÈÊu>ÈpØ>È@Q>ÈV>Èð©>Éw‡>ÉòÒ>ÊOÛ>Ê{>Ê’ÿ>ÊU>Ê[‡>Éää>É|>ÈtI>ÇìZ>ǃO>È ÿ>ÈDi>È¡>ɯ>ÈÓ">ɽ?>Ê@>Ê5÷>Ê?ú>Ê Z>ÉUI>Èúv>Èã>È›¡>ÈQÊ>È(ö>ÈI`>È[„>È«>Ét©>ÈïÀ>É >ÉqW>ÉÏW>Ê">Ê“>ɤf>ÉLÛ>É*>ÈÌ”>È“<>È’>È´s>Èo7>ÈY±>Èy>È~z>È¿>É,>É>Éßí>Ï_v>Ϧ >е>ÐaX>ИB>ÐÖ¨>Ñ3€>Ñv >ÑnT>ч=>ÑB>пñ>Ðhê>Ð!î>ÏÔ7>Ï’>Ïj¯>ÏÅÒ>Ð-í>Ð¥\>ÐèP>Ñ&©>Ñ|³>Ò0i>Ññ^>Ñ à>ÐÙ•>Ðo}>Ðv>Ð7>ÏàK>Ï£>Ïm>ÏæI>Ð]®>Ñ¢>Ðõ>Ñ:%>Ñ¡¾>ÒP>Ò->ÑY`>Ñm>ÐÒ>Ñ6ø>ÐKw>Ϫˆ>Ï­g>Ï]e>Ð)œ>ÐY>б)>ÐË>Ñ-U>ѳF>Ò,>ÒCb>Ñ»¡>Ñdè>ÐЋ>Ïâú>Ïú>Ïza>Ï Þ>Îýò>Îü­>Ïq2>Ï•>Ði›>Ñ!í>ÑÖ>Ñõ‚>ÑÒ@>Ñà’>ÑøÞ>ÐÞ±>Ϻ5>ϰ,>Ïv>Î!²>Îâ>ÎÜš>Ï%N>Ï >Ð,O>Ðâ>ѳÕ>ÑTE>Ñn×>Ñ_Ô>Ñ 0>Ðe„>Ï£î>ν,>Η™>Î*/>Î N>ÎQ²>Ω+>ÏbN>Ï¡'>Ð>Ð|A>Ñ(>ÑE>ÐL>ÏIÃ>Ïå>φÆ>Îݲ>ÎrŸ>Íÿ¶>Ìß>Í/8>Ío>ͦ->ÎÄR>έ>ÎrF>Δá>Ï=É>ÏDX>ÎÍM>ÎK’>Îñh>Îz=>ÍÞ>Í–ý>̈>>Ì¿_>Ì­8>Íé>Íãy>Ícl>ÌŽv>ÌO>ÌÃb>Í«_>ͨO>Íú>Ìï<>Í2Û>Ìó§>Íw>Ë·}>Ë}˜>˱©>Ëíc>ÌlÉ>ËÄ5>ÊýÖ>Êf'>ÊiX>Ë8|>Ël>Ë·>ËP¢>Ìs>̃>Ì:$>Ëgž>Ë6®>Ë©>Êï>ȯí>É%>ÉÙš>ÉÃ>Èã*>ÉÑ;>Ê&Ž>ÊHÊ>Éþ×>ÉÊ >ÊÜ4>ËŸ>Ê÷®>Êó">ÊÄB>ÉðÅ>È·>ÇùA>ÇŠ>Ç›>Ç58>Çi¨>ÈFG>ÉJh>É¥÷>É÷o>ʉß>ÊåŠ>ÊpT>Ê;Ó>Éθ>Ⱥu>ÈaŽ>ÇÙ¬>Çm>Ç7>Ç"s>ÇIÄ>Ǧ=>Èqì>É9þ>ÉÔ!>ÊF>ÊdÎ>ʳ>É{û>È݃>Èe¡>Èc>È A>Ç`õ>ÇCn>Ç7L>Ç>Ǿ>ÈIö>É->É€>Êí>ÊŽ>Ê/>Éaþ>Ȱ@>È[o>È Ž>ÇÒÑ>Ç©Å>Ç™Ø>Ç„ô>Ç×Þ>ÈLß>Èe]>ÈËy>É9:>É‚‹>ÉÏ >Ê/ø>É ‘>ÉN>Èœ“>ȧ>ÇÇO>Çú >È[ >ÇÇh>Ç4>Çqé>È;>Èßb>ÈÑÉ>Éî>É¡K>Ï‘Ó>ÏÛz>Ð3¸>л‘>Ðö6>Ñ;•>Òˆ>Ò*Ž>Ñâ#>ÑÑÉ>Ñ]œ>Ðóo>П>І8>ÐKØ>Ïå>>Ϙä>ÐÃ>Ðro>Ñ=†>Ñrp>Ñ”x>ÑÀD>Ò€>Ò4E>Ñ­C>Ñl›>Ñ¿>ÐÝ>Иþ>ÐHF>Ïøé>Ï¢>Ð.f>Є^>ЉÝ>Ðù8>Ñ«œ>Ò Š>ÓM>Ó#>ÒRN>ÑÞç>Ñ|¼>ÑCß>Ð5P>Ïš4>ÏÖ°>Ï¡«>Ъo>С‡>ÐÖ >ÑR±>Ñ÷®>Ò M>Ó~>Ô>Óõ>Òi¶>Ñ¡€>Њö>Ð7$>Ïš>>ÏRh>Ï<>Ï7>З>Ж‹>Ñ`T>Ò01>Ó1º>ÓXž>ÓD>Ó2‡>Ó@r>Ñæ›>РF>Ð>ÏC’>Î&>ηý>ÏEÅ>Ð;D>Ћú>Ñn>Ò¾>Ó_>ÓB^>ÒÜr>ÒÌ­>ÒE6>ÑCK>ÐLx>ÏQõ>ÎâJ>ÎP>Î.Ê>Ï!>Ï'–>φx>ÐYÒ>Ñu >Ó2>Ó¸°>ÓUJ>Ñüñ>ÐU¾>ÑÒ>Ð>!>ω:>ζ÷>Î(G>ÍÆ>Ìã>Íi…>Î;0>ÏHâ>Ï¢I>Ï­W>Ð&Â>М¹>Бô>ÏëJ>Ηõ>ÎÑV>Îj€>Íû[>ÍÚg>̬>̤B>ÌVì>Í ²>Í—ó>Í*'>Ëó>Ëà<>Í+>Í«4>Íz¾>Ì´>ÌŽ;>ÌŸë>Ìð>>Ívz>Ë­ø>ËP>ËCë>Ë*ø>ËU>Êí&>Éø>É .>É/ö>Ê'->Ë Ð>Ë%>ËOó>Ë»r>ËÞþ>Ë>ËDè>Ë)>ÊÈ£>Êu>É •>Ènš>Ȳ >Ç›>ÆÑÇ>È®>Èšæ>Éž€>ÊÅ>ÊQ>>Êç›>Ë`>ʺ>Ê<>Ê,À>És«>ÈB—>ÇM>Æj>Æ>Åsœ>Åþ»>Ç-ò>Èk©>É ü>ɬ >Êjó>ÊÆ¿>Ê(ô>É« >ɯj>ÉVƒ>Çña>Æåô>ž¼>Å{'>űG>Æ€J>ÇŒ>Ç>È„á>Émž>ÊY3>ÊKº>ÉóÆ>Ée|>É >ÇÞ=>Ǥ‡>Ç9î>ÆuÖ>Æ"_>ÆB>ŦÚ>ÆŒ">Ça4>ÈU)>É/>ÉœÂ>ÉÜV>Éú>ÉIY>ÇýÑ>ljµ>ÇŽ°>Ç¿Ü>Ç5à>Æ‘l>Æ]á>ÆbÖ>Æî«>Ç¡¤>ÈPõ>Ⱥ³>ÈÓ>Év>Ê…H>ɧ>È® >È&ß>ÇÀ>ÆÕð>ÆÔá>Æø>ÆÏk>ƪ¤>ÆÑ>ÇÐC>È®ð>È–©>È΀>Éf>Ï×>Ðb>ÐaO>ÐÍ>Ñ,>чâ>ÒHÁ>Òg\>ÒPF>Ò¶+>ÑïZ>ÑVr>ÐÜ?>ІÝ>ÐX£>Ð.d>Ï¿T>Ð/È>СI>Ñ4P>Ѽ>Ò}>Ó á>ÓL>ÒÌ>Ññ>ÑÜZ>Ñd>ÑÜ>в³>Ðv&>ÐLd>ÏöG>Ðjw>ÐÅ_>Ñ>Ñ«U>ÒPe>Òd>ÓÕº>Ó¯6>Òàß>Ò‚>Ò-Á>Ñn >ПA>ÐJ>ωu>Ïü]>Зê>Ðé>Ñ[k>Òq>ÒÍ|>Óva>Ôz!>Ô¨³>Óü™>ÓFŽ>Ò§Ã>ÑpÓ>ÐŽ•>ÏÀª>Ï:p>Ï5­>Ïð6>Ð×=>Ñ~Õ>Ò³>Óq¡>Ô•ˆ>Õ¸>Õe@>Ôס>ÓÄà>ÒØa>Ñ^ö>Ðe¹>Ï‚Ø>ι >Ί¡>ÏÁ…>ÑGš>Ña1>Ò Y>Ó‹P>Õ%Æ>Õ®<>Õps>ÔÐ3>Ó4.>Ñ4P>ÐÀ½>ÐŒ>ÏR´>έ5>Î+s>Ïg>ÏÈ>ÏÜ„>Ñ*¶>Óf3>Õzi>×”Ñ>Ö€>Ô>ÒÊD>Ѫš>Р¤>Ðr>Ïÿ>ÎE>Íw>Ì›Á>Í6e>Îe¬>Ï—‹>Ð+²>ÑŒ>Õz>Óñî>Òå>ÑVÊ>Ð9Œ>Ït»>Ϊ£>Î'–>ÍÛ¹>Ì€>Ì&>Ì(>Ía>ÍÅ>Ì‚#>̵€>Ë¿>Ï` >β\>ÌÇ¡>ÌñÕ>Ì¥Ù>Ì¡’>Ìþ>ÍE,>Ì0Ñ>Ë–ê>Ë.$>ÊÔA>Êb‘>ɇs>Çëà>Ç@d>Æm+>ÇÜ­>ÊmÉ>Ê–P>ËMú>Ì“>Ëð‚>˯X>Ë/g>Ëž>ÊÕ>ÉåY>É6í>ÇÑy>Å„q>Á½F>Þ:>Åv`>Æåx>É4è>Ê>Ê>ʬn>Ë$½>Êi¨>Ê"­>ɲ˜>Èð´>Ç­X>ÆLê>Ä«+>Ãoñ>ÂÈ5>Ä]>ÅJ<>Ʋ0>ÇÒë>Èè>Ê4£>ʤs>É¿Î>É@×>Èïl>È}+>ƉZ>Å“¤>ÄKQ>ÃÊ%>ÄJ¾>Å z>Å™>Æ0<>Ç® >Èð„>Êr>ÊPµ>ÉÀ>É16>ÈŽÆ>Ç“œ>ÆÁi>ÅèÙ>Å( >ÅM+>Äÿ?>Äéi>Å­b>Æœ>dz¬>Èk:>È¢>Év‹>Éή>É*’>ÈCM>Ç$ý>Ç>Æs\>Ÿù>Äø‡>Äô >Å]o>Æ->Çë>ǹr>È)½>È1Ï>É%p>Éïf>ÉS«>È>Çç€>Çïá>Æô>ÆE>ÅÇ>Å™6>Åþ¢>Ɔè>Çc->ÈQ>È_>È­¨>ÉA“>Їî>Ðh›>О>Ñ">ÑKŒ>Ñ´Ù>Ò8Ó>ÒyV>ÒY>ÒoW>Ò }>Òò>Ñ@®>Ь>Ð=Œ>ÏìÅ>ÏžE>Ð=É>ÐÕ3>Ño>Ñã’>ÒƒP>Ó€ü>Ó£t>Óa>Òä~>ÒgW>ѧ®>ÑQì>ÐØ>Ðp¼>Ђ>Ïå>Ð}Ö>Ñ>Ñ›D>ÒHp>ÓT>ÓÎá>ÔÉ>Ô >ÔC>Ót>Ò’‘>ѯ>ÐõÝ>Рþ>Ð8‚>Ϩª>Ðj‰>Ñ!¥>ÑËî>Ò¨[>Ó‰Ì>Ô«>ÔúA>Õ+;>Ôã2>Ô]>Óo’>Ñ÷Ü>Ðç;>Ð>Ïtö>Ïg‚>Ðeê>ÑHî>Òæ>ÓdÏ>Ôt‹>Õû >×3¹>×Yê>Ö*+>ÔR>Òÿ˜>ÑÇg>ÐÅÌ>ÏÏX>Ï#½>ÎÛS>ÏôJ>Ñ/>ÑΩ>ÓG‹>ÕÄ>×§­>Ù}->Ú3z>×CH>Ô’Ì>ÒP>ÑbÜ>ІŒ>ψ>Ï;p>Î%Ï>Î…y>ÎóC>Ðfˆ>ÒŸ>Õw²>×ý>ÛPš>Ú" >ØHß>Õ•.>Ò¶x>Ñ ù>ÐÊ>Ï>Îv>ÍFâ>Í:">Í”ú>μç>Ð=H>Ñú«>Óà>Ù<(>ÚУ>Õl0>Óï>ÑQ•>ÏúÔ>ÏÀ>΄«>ÍßÑ>ÌQÃ>̃>ÌNF>̰×>ÌXè>˾V>Í?E>Íä0>Îìø>Ïy->Í0z>ÌÔ›>ÌÃæ>ÍJ>Ík”>Íù>Ëù >Ë”:>ËM>ÊÆê>ÊX >ÈRÂ>Äìo>Ã@è>Á%¦>úû>Ç>>É@¿>ÊÞè>˦±>ËÍ´>˾=>Ë)M>ÊÏ6>Ê>…>ÉtÔ>ǪÀ>Ƨž>Âp >»;{>½°Ì>ÀÀ >ÄÀÓ>ÇwÕ>É;÷>ÉÆD>Ê!>Êæ6>ʲÄ>Éþ¥>É[Ó>È•Ô>ÆC>ÄÍý>Áÿ]>¿žæ>¿àþ>Â!¢>ÄC2>Æ"˜>Ç`ë>ÈÑ<>ɺM>Êe•>Éz >É7>È|>ÇLI>ÅõŽ>ÄÅß>¬c>Á¿\>ÂU>ÃU“>Ĭ >ÆL>Çï>È{ã>Éç5>Ê >É¡O>ÉO>ÈIê>ÇN¨>Æ7L>Äȃ>ÃÊþ>ÄŠ>Ã\°>Ãô>Åå>Æ{:>Ç“˜>Ç»š>È >É1¦>ÉÌ*>Éh>ÈDa>ÇRS>Ʀ|>ÅGÛ>Äae>ÄBŒ>Ä\°>Ŷ>ŧ¢>Ʀš>Ç>ÇË9>ÈO,>É$>Ê*ü>ÉL>Èu*>Ǹž>Ç8Ã>Æ]>ÅT>Å >Å'«>ŤÖ>ÆTØ>Ç}>Ǩp>È!Ž>È®k>É><>Ïý>>ÐD¿>гa>Ñ78>Ñ~«>Ñãú>ÒVÑ>ÒÇê>ÓÒšz>Ò>Ñóo>ÑWN>ÐÏß>Ð[>ÏðD>ÏØi>Ð_Ö>Ñ«>Ò ¹>Ò:ð>ÒØÄ>Ó¬³>Ó¸S>ÓTo>ÓI>Òæˆ>Òx >ѯ^>Ðï¹>Ða^>Ð5>ÏãŒ>І¶>ÑBp>Ñû;>Ò¹C>ÔYi>ÔÍ>Õ2ç>Õ™>ÔÊ0>ÔF¦>Òâw>Ñá?>Ñ*>Ћ>Ð1Œ>ÏÄS>Ð{è>Ñ^Ú>Ò>ÒÞm>Ó“¡>ÕZ¸>Öl>ÖW>Õyˆ>Ô_Þ>Ó‚±>Ò!w>Ñ'|>Ðk>ÏÿØ>Ï«>Ð}¶>Ñ[|>Ñû¥>ÓaÏ>Õî>×»>Ø¿w>جÁ>×E6>Õ'Y>ÓQl>Ò"">Ñ1P>ÐL™>Ïh¦>Ï ä>Ð">ÐÚ^>Ñ»À>ÓÐ]>Õâ…>Ø©º>Ü!I>ÛÄ>ÙS>Õ‹>Ó)Ä>ÑýU>Ñ‘>ÐÄŒ>Ï£>ÎC¨>Ψd>Ï&Ò>Ðc>Òþ‹>ØK>ÜáG>ߥG>á(ä>ÜÔ)>Öây>ÓHë>Ñ…y>ÐDƒ>ÎÐq>ÍÚ~>Íu>ÍŠ‡>Î"w>ϵÛ>ÐÔ>Ó=º>Ø¡™>ç¡>é&æ>Üë>Ö4ç>Ò!b>ÐZ‘>Ï7>ÍÇ•>Í…›>ÌgE>Ì4¹>Ì]2>Ì»2>ÌÌþ>Ë‘¹>Ìw<>Òà>Ù ó>Ñ@6>Îïü>Íb:>ÍU>ÍÙ>Í#ˆ>ÍÀ>˯ã>Ë¥>ËË,>Ê’ü>É¿{>Æé[>ÁŠ>°ï2>®òŸ>¼g>>ÄŠó>È‚^>Êe>ËD_>̈>ËÖ>Ë Œ>ÊÁ'>ɺÌ>È™i>Ç,Ã>ÄÐ[>¿/ñ>µÒ›>·u>½q>Â/­>ÅÐ>È*ÿ>ɽL>ʪ >ÊõG>ÊkH>ÉŒX>È£>Ç»â>Å´ª>ôÌ>¿Ãp>¼˜>½Q>¿b>Ãå>ÅêÄ>Çk]>É©>ÉZÊ>Ê*ð>ÉÕ\>ÉKÄ>È–Ç>ÇJJ>Å>Ä“>Áö >À.`>¿ÞQ>Á«”>Ä‘>ÅÃf>ÇËÉ>Ƚ>È÷S>ÉÍù>ɹl>É ­>ÈD>ÇHE>Æ>Ä{ª>Ãhô>ÂXq>ÁÙÝ>Âiø>ľ>Æ !>ÆÕ€>ÇJ^>ÈÉ>É-­>Éϳ>É >È%D>lj>ÆÐ#>Ää>ÃËé>ÃÞ™>Äq>Äl_>ÅE>Æ‚>Æýè>dzÃ>Èk¼>É9=>Éά>ÉÈ>ÈQþ>ÇŽå>ÆÌ[>Åüp>ÅSÃ>Å<‘>ż>Ū>Åês>ƲR>Ç-g>Çþu>Èél>Éo¿>ÏÀ°>Ðs>О>Ñ%Ÿ>Ñ¢Ì>Ñø€>Òh•>Ò»…>ÒÇ>Ònò>Ò>Ѹs>ÑJÛ>Ñ >ÐòŒ>Ð5Î>ÏÞE>Ð^}>Ñf>Ѫf>Ñ»>Ò•W>Ó}>Óò>Ô A>ÓŽË>Òí>Òuê>Ѻ.>ÐÐ7>Ïû>Ð#>Ïê¬>РÁ>Ñ«Ü>Ñ÷–>Ò•§>ÓÞG>ÔÖr>ÕkÇ>Õ">Õ 5>ÔŸ>Òú­>ÑöŠ>Ñ$§>Ðp\>ЂÍ>ϳ§>Ðe>Ñ<˜>Ò&õ>Ó)y>Ô’ø>Ö M>Ö>Õî>Õ¼¦>ÔY¼>Ó¿>Ò_>Ñ-°>Ѓ¥>а[>Ïy{>Ъ>И>ÑÆ >Ó^+>Õi”>×TÐ>ز­>Ø¥>×€0>Õ“Ö>ÓÎq>Ò}w>ÑÁ†>Ð41>ÎŒo>ÏB>Ïý>ÐÍI>ÑÏ>ÓËÖ>Õui>Ù)>Û÷(>Û§>Ù[Ü>ÖU’>Óåó>Ò­U>ÑUÎ>Ð1Ä>Î¥×>Îhý>Îö®>Ïò’>мy>ÒÇQ>×>Ûw©>á½J>â;¼>ÛÊç>×2L>ÓÁ&>ÑiF>ÏÐÓ>Ïl‰>ÎJH>ÍÄ>ÍÅ$>Î >Ï*ˆ>Єo>Óy1>Ûbd>êó#>ë¦ >Ý>ÖTò>Ñ;>ÏÌ#>Îy^>Í•>Íž>ÌÞâ>Ìc§>ÌIÐ>Ìä>;`>Íé§>Î×w>С*>é£j>ЉÐ>Î-±>ͯd>ÍY>Ìí>Í!X>Í{Ð>Ë-Ï>Ë]>ËLÐ>Ê÷à>ʽ>Ål>¿¨»>±¥>©û<>¹¬Ä>ÄÊi>Èuè>Ê%H>Ë?û>ËÑ/>ËU>Êá¶>ʦ•>Éýð>Èâ¡>Ç4>Å!>¾Æô>µþr>³cÞ>¼áœ>Ámß>ÅéÃ>ÈÎP>É›€>Éʺ>ÊŠ¥>Êi°>ÉÂ>ÉÄ>Çúó>ŸT>ž¬>¿±>¼ú‰>½ i>¿‹>Ü>Æ~>ÇSÞ>É_>É„›>Ê,q>Ê/>É{ò>Èíe>Ç@ê>ÄÿÊ>ÃZ>ÁaÆ>À/þ>¿èn>Á=Ö>Ä.Æ>Å=ø>Æž”>ÇçA>Èû+>ÉÊY>ÉØÓ>É Z>Çå¼>Ç}ç>Æ(>įÑ>Ãgm>Â>Âgö>Âþ!>Ä9ú>Å›)>ƹh>Ç_>Ès?>Égù>Ê7ª>É]E>ÇõV>Æçª>Æ@‡>Å30>Äl5>ÃëÐ>ÃûÐ>ÄÈ«>ÅS>ƶ>Ç>ǾÐ>È´\>Éii>É?>È›³>È;P>Ç»ç>Ç3Í>Æ•>Å)%>Å1ä>Å–Î>ÅšF>Æ)S>Æ©©>ÇL°>Èß>É#:>É¡>ϧ>ÏùŸ>Јx>Ñç>Ñu]>ÑÙc>ÒRý>ÒŠ@>Òsg>Ò2¨>Ò(>Ѩ>ÑHŒ>Ðàì>Ы¯>Ð'þ>ÏÖê>ÐJ%>Ðï>ÑÐa>Ñë>Ò¹J>Ó†>ÓŒ>Ói¥>ÓÕ>Ò+€>Ò€>Ñè$>Ñ)>Ѓ÷>Ð.›>Ð ™>Ðr>Ñ#~>Ñv3>ÒW^>ÓRD>Ô,’>Õ6Æ>ÔãÏ>Ôs@>Ó¨a>Ò¦ó>ÒÝ>Ñl™>Ð]q>ÐO¯>Ïxé>Ð7>>Ð÷n>Ñ»Y>Óí>Ôý)>ÕŽ>Ôå¹>ÕR¼>Õ„î>ÔpÑ>Òá@>Ñ8ù>б>Ð@>ÐBÃ>ÏIÛ>Ð û>ÐÄÃ>Ñ«Ä>Òæ)>ÔÉÕ>Ö`Í>׃>×zõ>Ö D>Ô­”>ÓV£>Ñâ?>ÐþŠ>Ðì>Ο9>Ïù>Ðâ>Ð,>Ñv>Ó4á>Ô€Ó>رË>Ùøñ>؇¨>×(ÿ>ÔíÓ>Ó8ü>Ñèµ>Ðä>ÐNª>Îäq>ÎL >έþ>Ïš>РŠ>Ò–E>Õ£Y>Øû¹>ÛK>Üò>Ùû«>Ö½ >Ó9º>ÑYç>ÐTÞ>ϸ>ÎT7>Í”õ>Íš‹>Ή>Ï'|>ÐCK>Ò€o>Óúž>Ü»w>ݤ†>Øšc>ÒÌ#>Ð0>ÏV>ÎÂ]>ÍVœ>Íex>ÌÅ'>̧“>Ì£É>Ìâ >Íd¶>̽ž>ÏX´>Î,ø>Лæ>ÍæÔ>̦’>ÍQ>Ìïm>Í¢>ÌÚû>Ìã›>ˇ6>Ëwê>Ëbw>Ënf>ʯ\>È02>õü>¿éÿ>¼@>Àǹ¬>ɾP>ʨg>ÊÐ×>Ëv#>ËŸ°>ÊùÛ>ʱ+>Ê>r>É*>Ǿ¿>ĵJ>¿àw>¼ª^>»“•>Á*~>Ä/Í>È>Éo¦>ÉhŠ>Éå>ʹÞ>ʇ>Ê8>Éð>È`l>Æ-ï>Ã|>ÁM½>¿å>¿‚Ã>Á6>ÃŒ¥>Å”w>Çð>É7#>ɹj>ÊO(>ÊP>ÉhÀ>ÈLM>Ç >ÅGï>ÄA‹>Â…'>ÁÁ:> ì>²>ÄÍ>År;>Æ¡§>È‹ª>É^÷>Éó›>ÉÊ“>Èò¿>ÇÕ">Ç?Ë>Æ/>Å›&>Ä¡€>Âó€>Ãh >Äù>Å›>Æ¢f>Ç'Ã>Ç|ã>ÈÀ>Éžñ>Éà›>É,ó>È+p>Ç3ý>Æ)Ù>Å•>Äxä>Ä´>Ä _>Äë>ŦÌ>Æ d>ÇC>ÇÜ>É4Œ>É—9>ÉYê>É>È]>ÇÆ†>Æò'>Åä>ÄÐ>Å)…>ÅTª>ÅÚó>ƶ»>Æ—P>ÇP>>È>È¡|>ÉYm>ϵ>ÏÕ§>ÐxX>Ñ Æ>Ñ=¾>Ñ•ô>Ò™>ÒG“>Ò2Ÿ>ÑíÓ>Ñë >Ñ–±>ÑQ·>Ðɶ>Ð]w>Ïö >Ï·Ù>Ð5u>ÐæÎ>ÑâÚ>Ñê >Ò‰Ü>Ó+•>ÒÊ­>Òv6>ÒŠ>Ò;–>Ò9>Ò¡:>ÑP>Ðn=>Ïý[>Ϻ>Ð.ñ>Ñ >ÑU>Ò6>ÒÖ˜>Ó‘š>ÓÜ">ÓÀ5>Ó™>Ó2ñ>Ò=J>Ñ[>ÐòM>ÐN2>Ïõ¨>Ïr‡>н>Ы}>ÑHI>Ò²,>Ôý>ÓøÙ>Ôr²>Ô¶>Ô£I>Óéú>ÒsÍ>Ð]÷>Ð9³>Ð>Ïû>ÏR[>ÏýW>Ñš>Ñn>Òh>Óì>ÔLö>ÕPð>Õ®Z>Ô“–>Ó=Æ>Òkç>ÑAY>Ѓ_>Ð v>Ïi‡>ÏXB>Ϻ)>Ït¶>ÐÕç>Ò§U>Ô:¸>ÕË>Ö >Öü>ÕCš>Óc>ÒDÓ>Ñ8§>ÐcÐ>ÏÁÖ>Î÷x>Íã >ÎK<>Îïç>Ð>ÒÔ>Õ™>ÕÈ>Ö?]>ÖN„>Öq†>Ó÷h>ÑŽY>Ðáæ>Ïþj>ÎÄé>ÍÜp>ÍY¨>Í“*>Î ”>ÎÜ]>ÏÐi›>Ñ$Þ>Ô j>Ô^>ÒŒE>Ò'î>Ð;>ÎëD>Î=²>ͳy>Íjò>ÌÀ>Íbþ>ÍC&>Ìë®>Íu>ͬ¶>ÏdA>Î{ú>Îd•>ÍÚ>ËÕû>Ì;>Ìø>Ìâ->Íq>Ìô>ËÆ>ËdL>Ë‚~>Ë>Ë_G>Ép,>ÆÐ§>ÆvL>ÅnÎ>Æuí>Ê4²>Ê–§>Ë|d>˾^>ˆ>Ë’>Ëñ>Ê—6>ÊK“>ÉÆý>ÈUC>Ç \>Ø->Ã@u>ÃÖ>Ľö>ÇUw>ÉE>ÉM >É›×>Ê@w>ÊêÅ>ʪ±>ʈ>ÊW,>È·C>ƈÆ>Ä#>ÃÈ„>Ã˺>ÃE0>Ä2>Åtw>ÆúŸ>È­~>Ée_>Éø>Êå>Ê$×>ɬv>È^'>ÇA¯>Æ_v>Ũð>ĉR>Â<>ìÔ>ÄPz>ĸv>Æ>dz…>ÈП>É–8>Ê$¿>ɸ>È™j>ÇÚæ>Ç_d>Æ®ó>ÆQµ>Å_R>Ã¥·>ÄY>Äðg>Å„.>Ƹ†>Ç ì>È>É>:>ÉÚ]>ɾ>ÉÏ>Èhb>Ç£'>Æ¡í>ÅÆ.>Å’j>Å«>Åf‡>źi>Ʋ>Æjª>Ç”>ÈN>É_5>ɶ(>É¢T>É€þ>Èú–>Èï>Ç8>Æ?>Å‹õ>Åɘ>ÅÑk>ÆN‚>ƽ¸>Æõb>Ç÷Ž>È^>ȹ™>Élþ>Ïi:>ϼ>ÐTò>Ðìd>Ñ•>Ñ:ã>Ñœo>Ñ¿ >Ñ£1>Ñ–¨>Ñö`>Ñmª>Ðþ>ЇA>Ð >ÏÃ’>Ï«j>Ð`n>ÐÅj>Ñ …>ÑE¸>ÑÑ>Òy¾>Òk_>Ò?}>Ò-|>ÑõØ>Ñ­%>Ñxh>ÐÅW>Ð* >Ï¿6>Ïbù>Ï«v>ÐáŒ>Ñ1#>ѱ¶>ÒT<>ÒÌ >ÓÕ>Ó&>Ó•>ÒìÊ>Ñé>Ñ >Ð}­>ÐÄ>Ï«!>Ï@Ü>Ï£ >Ð {>Ñx>Ò0Ý>Òñ£>Òíõ>Ó ¶>Ó¥G>Ów,>Ò”r>Ñ´Æ>мþ>Ð5M>ÏÓ[>Ïum>Ï&»>Ïž>Ðcƒ>ÑY>Ñ–=>Ò9>ÓO>Ô®>Óâa>Òý>ÑÑK>Ѧ«>Ñ@8>ÐOê>ÏÁí>ÏB>Ϩ>Ï8‘>ÏXG>Ð¥>ѵw>Ò¨Ž>Ó0§>Ó4“>Ô<Ü>Ó³>Òtb>ÑТ>Ц©>Ïý²>Ïi‘>ÎÒÍ>Í÷å>ÎX >ξ÷>Ï„Ê>Ðf>Ѭ>ÒÌ¿>Òàv>Ò[>Ò„>Ñ…>Ð'>Ï¿®>ϲ >ΞÏ>ÍÝ>Í`þ>Í¥ê>Íæ>ÍÈ5>ÎÀæ>ÏÊ>ÐŽí>ÑAò>Ñ>…>Ð}>Ï’»>Ïš¿>Îöw>ͱ>ÍÊ>Í^ >̾>Í#¹>ÍE>ÍB¶>ÍxL>ͼÅ>Î{q>ÍSW>ÍP¤>ÌkË>ÌTØ>̳,>̧¿>̤“>Ìú>ÌìÞ>Ëõz>Ì6 >Ì7‚>Ë|‘>Êûâ>É÷>É>ÉS€>É7>É—>Êp™>Êo>Ë{>ËÎ>ËŠ>Ëœs>Ë ü>Êd)>Ê2>ʧ|>É’–>È ô>Æ(®>Æç>Æxý>Ç)>Ƚâ>ÉÑA>Éšn>ʬî>Êí>Ë/Ì>ʦ->ÊZh>Ê >ɤ'>ÇÄr>Æüß>ÆQ™>Æ>P>Ŧy>Æ8>ÆÆŸ>È:±>ÉeÔ>Éü¤>Êjƒ>ÊǤ>Ê8Î>ÉÀÄ>ȶ >Æþó>ÇL>Ç=A>Æ…´>Å\7>Å]z>ÅÚ>Æ$¦>Ç!O>ÈT’>É1î>Éë¼>Êd]>Éëe>É'R>Èu€>Ǻ>Çb1>Çj>Åâ>Ž>Å} >Æ5>ÆOM>ƆH>Ç;œ>È| >É×>Ê Á>Éè>É|°>ÈÛ6>È!œ>Ç_ç>ÆB>Æ(÷>Æ{>Æ®ï>Æ¢M>ÆË>ÇK>ÇÊü>È7­>Èùð>ÉÈH>ÉÍI>ÉJ>É;>È`Z>Dzî>ÇXf>Æ×j>Æžd>Æ`Ò>Æß{>ÇG•>Ç¡K>È2$>Èwt>Èú²>ÉÆC>ÏU{>Ï©¯>Д>кl>Ñ)>Ððœ>Ñ&->Ñ>µ>Ñ,>Ñ!Í>Ñ.Ú>ÐîŽ>Ю>ÐU>Ïí >Ï‘¶>Ï“…>ÐX±>ÐX>ÐdÖ>Мj>Ñ+?>ÑÐÙ>ÑÚé>Ñϯ>ѼE>Ñ™>Ñ?Û>Ñ9>Їí>Ïé\>Ït…>Ï!w>Ï9J>Ð7¼>Ðá>Ñ$š>Ѩ>Ò h>ÒD4>ÒÈ€>Ò®ø>Ò…»>Ñs^>ÐbÐ>Ïò(>ÏÓ>ÏX¹>Ïÿ>ÏZM>Ð×>Ñ >ѧ>Ò>Ñæs>Ò:v>Ò8ª>Ò;>ÑÅ•>Ñ >Ð@þ>ÏÍÑ>Ït>Ï#°>Îë¾>ÏO²>ÏÔŸ>Ð=Õ>Ñ3e>Ñž>ÑΓ>Ò7ë>Òø>ÑÕU>ч]>Ñ é>ЀÄ>ÏÒŒ>Ïd‚>Îô->Ω‰>Îó¸>Ï:¾>ÐP)>ТÙ>Ñ-V>ÑŠe>Ñ»¾>ÒG>Ò|>С>ÐN$>ÏãÇ>ÏŒ>ÏmÇ>Îä>ÎCâ>ÎpG>Σš>θ>Ï2>м>ÐèÍ>Ññ>пl>з>ÐYÅ>Ïy3>ÏC>Ï%ã>Î)8>ͧO>Í‚4>ͪQ>Í̶>ÍRs>ÍÊ8>ÎfÐ>Ï”>ÏúE>Ï P>Îá>ÎLˆ>Îk5>Î@V>ÍVQ>Í Ç>Í9>̽g>ÌÃŽ>Ì”3>ÌÄ­>Ìýá>Í=o>ÍÑ'>ÍP >ÌÃÆ>ÌÉ>ÌÆ|>Ìê»>ÌíÍ>̯ú>Ì«T>̧>Ëýf>Ì‚>ÌZ>ËÃq>ˇ">ËLâ>Ë8'>ÊX>Ê<‰>ɸ;>ÊõÓ>Ë}¼>˦…>Ëóº>Ëë¯>ËðÓ>ÊÖ>ÊV>ʬþ>Ë;Ã>ʉ«>ÉŸ9>È{>ÈÓ¸>É>Éu>ÉG–>Éýó>Ê”—>˹“>Ë~L>Ë}˜>Ê£>Ê;½>Ê]>ÉÀ1>ÈÚC>ȵ]>È?G>ÇŽb>Ç!›>Ç_®>Ç×>ÈÑ>Ê'L>ÊÄ>Êé~>ËË>ÊZ>Éð)>ÉL{>È#³>È9”>È>ÇÆŽ>Ç >Ç »>ÇEE>LJs>È8>ÈÌx>É¡>Ê0½>ÊŸŠ>Ê7‹>ÉÈ >É8 >È¥2>Èb >Ç·C>ÆDó>ƧÂ>ÇT8>ÇVÞ>ÇhØ>ÇÉ>ǯÄ>È0Ž>É‚m>Ê@Ñ>Ê:ô>Êþ>Éj]>ÈÄO>È0>Ç05>ÆèZ>Æü^>Ç»>ÇDQ>Ç">ÇÚ4>È4S>Ȧ˜>ÉtÓ>Ê+h>Éø`>ÉËC>ÉPC>Ȉ‰>ÇÚŸ>Ç¥º>Ç{Ð>ÇU>Ç 5>Çl´>ÇØ©>È,>ȉm>ÈíV>ÉÍö>ÊÖŽ>Ï0e>Ïg>Ϲ>з>ÐF—>Ðja>Г>гf>Цø>С¼>К>Ðvü>Ð]'>ÐM[>Ïõ >Ï©>ÏaC>ϬU>Ïß×>п>ÐX >Ш>ÑN>Ñ]>Ñ«>ÑŸ>Ñ.>ÐÁ;>ÐÊþ>ÐZ¶>ÏÎ'>Îü>Ï¢>Ï^4>ÏÖ:>ÐP¹>К@>Ñ ˆ>ÑŽ>Ñ~]>Ñ£4>ÑÛ§>ÑÊŸ>Ðåm>Ïè|>Ï48>Ï [>Îù >οI>Ï.ñ>ÏÀñ>ÐWà>зÏ>Ñd>Ñ~˜>Ñš>Ñ–.>Ñ5D>Ðä>ÐŽ;>ÏÃ!>ÏJ§>ÎñÚ>΃>ÎÒ½>Ï< >Ïó>Ïõ9>Ð{>Ðñ>Ðeå>Ñ/i>Ñýê>Ñ{â>Ы°>Ð*ž>϶Æ>ÏG¯>ÎðE>Κé>Îb >ÎÏË>ÏWï>ÐQÿ>ÐxZ>Ð|ß>Ðjp>Ðb >Ð2Ú>ÐöE>Ïë[>Ï '>Ïk¾>Ï 6>Îß„>Îzç>ÍÝ&>ÍùM>ÎG¸>Î.@>ÎÖ®>Ïgx>ÏÓø>ÏÞ‡>Ïšã>Ïhð>ÏAX>ÎÐ7>Θ¯>ÎhQ>Í÷,>Í­>ÍXæ>Ío>Íš>Íy>Í i>Í´ë>Î>Ï >Ï;‘>ÍÇV>ÍÃ+>Íâ>Î ä>͉L>Í%ò>Í,b>Ì´0>Ì– >ÌP`>Ì>̽þ>Í>Í~q>Í<>ÌY>Ìi×>ÌÊã>Í È>ÍL>ÌÎh>Ì >Ì™‚>Ëûd>Ì">ËÑ)>Ë͇>Ëë;>ÌS^>Ì>>ËX>ʬG>ʸ¥>Ëf!>̤>ÌÃ>Ì>Ì º>ËôÖ>Ë´>Ë ˆ>Ë!®>Ëa>ʶé>ÊX´>Êá>Éïo>Ê$¨>Ê1 >Êog>Ê—º>ÊÂR>Ëo`>ËÁ>Ëžf>ÊÖÖ>Ê£{>Ê{>Ê-w>ɾ›>É"6>È®>ȶì>ÈÇO>ÈÒì>Ȭ->È•Ì>ÉÔÿ>˦>Ëtc>ËRº>ÊZ>Ê/W>ÉÁ.>ÉsÀ>ÉK„>ÈÓÛ>È«>È7>È”ç>È®3>ÈŠh>È«A>É43>ÉÃw>ÊA[>ÊÉ*>Ê€ä>Ê+¶>ɸî>É}±>Ér>É/Ã>È%‹>ÈÉ>Ç´.>Ç·Š>È"=>Èv;>ÈÁ®>É3ü>ÉÚ>Êq,>Ê»q>Ê~9>É¿©>ÉHÝ>ÈÍï>È8h>Èt>Çëî>ÇÆR>ÇæÚ>È6u>È„6>ÈÙ˜>Éa">ÉùÃ>Ê]g>Ê_>ɵï>Éj®>ÈÊë>ÈÕ>ÇÆù>ÈË>Çü°>Çî§>È%V>Èj>È«K>ÈðÝ>É:>Éè)>Ê›>Ï)˜>ÏF‡>Ïxc>ϪÖ>ÏÈa>Ïí6>Ðä>Ð' >Ð"T>Ð/B>ÐF>Ï÷û>Ïüb>ÏÚÌ>Ï™>Ï^8>Ͼ2>Ï~Ž>Ï‹>Ϻ®>Ïër>Ð E>ÐQÂ>Ðc¶>ÐPÒ>Ð!k>ÐDY>Ð/m>Ð>ÏÌE>ÏŽ/>Ïm>ÏPn>ÏD‡>ÏxV>ÏÄ9>Е>ÐMÍ>Гa>Ъ®>Шå>С‰>Яx>ÐR>Ï·r>Ï]v>ÏŠz>Ïô>ÍÑ9>ηb>ÏRT>϶Ú>Ð`>ÐG“>БY>Ðí’>Ð×à>ГÖ>ÐŒ¶>Ð=Œ>Ï…>Ïj>Î=ð>ÎH>ÎEÌ>Îݤ>ÏBH>Ï–Ÿ>ÏØØ>Ð>Ð*D>СZ>оH>Ðf™>Ï““>ÏPO>Ï.ã>Îàµ>Î…Ï>ÎM˜>ά>έÔ>Ïbb>ϰI>Ï®~>Ͻ>Ïð:>ÏÀF>Ï™–>Ï£N>Ï;B>Ï+Œ>Ï™>ήÏ>Σ>ÎAþ>Í|r>Ík¸>͇1>ÍÓS>Îfþ>Îá‚>Ï&>Ï3û>ÏH„>Îâz>ΣÍ>ÎI¢>Î ¾>Î •>Í÷ð>͸>Í$4>Í(s>ÍMW>ÍPg>Ías>͘#>Ísì>ÍÉ£>Î8W>Íæ>Íí >ÍšÚ>Íq@>ÍI¡>ÌϨ>ÌûÙ>Ì©Ž>Ì·û>Íi>Íð>ÌÛR>Ìùm>ÌÖ>ÌŸ#>̉>Ì  >ÍV>Ìì­>Ìè*>̽Å>Ì…¦>̇Ø≯>Ì>Ë«>Ë«±>Ì¢>Ì’H>Ì {>Ë™»>Ëc;>ËF>ËU”>Ì%î>Ì}!>Ì9Å>Ì>ÌE>˃>Ë’ >Ë—>Ë/å>Êê>Ê{½>ÊÔ>Ë >Ë'>ÊÜb>ÊÎY>ÊôS>ËT>Ëx¦>Ë£­>˰l>Ë3>Ë Ú>Êîí>Êšª>ÊZ>Ê>ʆ>ÉÀÖ>É—c>ÉÎ?>ÉÃ>ÉÍ¿>ÊŒè>Êú>Ë>>Ë\>ÊÀA>ÊE>>ÉΕ>Ê >ÉÿÒ>ÉËŒ>ÉA½>É'm>É0œ>ÉÙ>Ɇê>Ée—>Ƀ¿>Ê&>Ê É>Êø>ʦj>ÊSy>ÉüR>Éæ'>Éüá>Ê!l>Év†>ÉtŠ>ÈÔ>È&÷>ȧÏ>É->ÉBt>Éæ¥>Êb:>Ê­”>Ê>ÊU8>ÉðL>Éžy>ÉR>ÉT³>É >ȦŒ>È’m>Ȇ„>Èį>É >ÉS/>ÉÐj>Ê<‰>Ê}ø>ÊQÖ>Ê€>É·Ÿ>ÉM1>È·>ÇæK>ȹ>ÈD¢>È• >ÈÌ>ÈïÏ>Éë>ÉP¤>Ég&>ÉÒ@>ÊUñ>Îöv>Ï>Ï7r>Ï[>Ïmb>ÏŠ›>Ï©±>Ï»á>ϼX>Ïßø>ϼÙ>Ï·&>Ïâ3>Ï™H>ÏHÐ>Ï#>Ï4]>Ï% >Ï8Ÿ>Ï^ö>σ’>Ϩ¬>ÏÈA>ÏÒq>ϰT>ÏUà>Ï® >ÏÇ>ÏÙ>>Ï€>Ï=¹>Îæh>ÎÎk>ÎïÑ>Ï ß>ÏW€>ψÀ>϶6>ÏÚ¿>Ïð¡>Ïëf>ÏÑ>ÏÔö>ϳ>Ï>ϲ>Ï*M>ÎÖE>Î ™>·¤>Îò;>Ï<˜>Ïw7>Ï©?>ÏÕÞ>Л>Ð ­>Ï÷>ÏÕÍ>Ïî>Ï Š>ξe>ÎM<>Î?ƒ>Íý?>ÎeC>ÎÈB>ÏÉ>ÏQ0>Ï‚.>Ï®‡>Ïã©>Ð >У>ÏgM>Ï»>ο >Έs>ÎT!>Î%r>ÍÕ>Î+ª>Î">ÎÑø>Ï$½>Ïa2>ÏŠM>Ï« >Ï×>Λ >Η>Î\>ÎSo>ÎPa>Îh£>Î >Í|a>Í“’>ÍÃ0>Íý°>Î`>Î¥å>ά>Î9Ã>Î[>Î1">Î;v>Î*>Íî€>Íá¦>Î/>͸Þ>ͼ>Í0®>ÍVØ>Í\ˆ>Í)6>Í)Ê>Íy®>Í~>Í¢>Ͷ*>Îþ>Íê¢>Íw?>Í,Ó>Ìæ>Ìʃ>̱å>ÌÐe>Í’>ÌûB>ÌÊÃ>Ì×k>Í?6>ÌøÖ>̦¥>ÌË>ÌÔ>̶>ÌÊÝ>Ì­)>Ìqß>Ì{x>Ì<É>Ìf6>ÌdÎ>Ì Ž>̃>Ì£>Ë“>ˬ/>ËÑ%>ËÀ9>Ër>ËÎ>Ìgô>ÌBÅ>Ì">Ì >˶°>Ë¡ò>Ëwè>Ë`t>ËMŽ>Ë,Û>Ë+î>Ë\·>ËuÐ>˶b>ËIL>ËFC>Ë€Y>Ë¡±>˳>ËÁ§>ËIô>Êþ§>ʧ/>Êϯ>ÊÒø>ÊÏ>ÊÜË>Êî >Êj>ÊQ>ÊD >ÊŽf>Ë·>Ë&>ËI >Ëlˆ>Êù¼>ʯ)>Êj>>Êz1>Ê•½>ÊØ¿>ÊŸs>ɺñ>ɶm>ÉòÇ>Éë >Éñ>Éïä>ÊzA>ÊÛý>Ëš>Ê×+>ʘQ>Ê[º>ÊN…>Ê›Æ>ʯ)>ɲØ>ÉlÝ>ÉZ>É »>ÉT‰>ɘ>ɰ&>Ê-h>Ê>ÊÒÅ>ʽá>Ê^>Ê6>ÉñÙ>Év>Éf*>ÉX>É*>ÉNÆ>É=Ý>ÉY=>É„%>ɲD>Ê{>ÊWÛ>Ê›´>ʉš>ÊHµ>Ê>É®Œ>É’>ÈI>Èäg>É÷>ɺ>Ɉ>ÉnÛ>Ƀ/>ɪf>ÉÛÓ>Ê%e>Ês[>ÎÂ\>Ï">ϧi>Ï´C>ÏæÃ>Ïôê>ÏÕ²>ÏÏ@>ÏÒ3>ÏÊá>ϰõ>Ïò>Ïj‹>Ï2ð>ÎôÕ>ÎÒV>Îð7>Ï7ü>ÏÒ>Ðü>Зž>ЖÇ>ÐoÒ>ÐkÎ>ÐI–>Ð0¢>ÐY>Ï×»>Ï´1>Ï{o>Ï3 >Ï ¸>Ï& >Ïn½>Ïê\>ÐA3>лn>Ðá~>Ðò­>Ñt>ÐÇl>ФÛ>Ðn>>Ð9>Њ>Ïãø>Ïs{>Ï5í>Ï_á>Ϻ>Ð=Â>Ѓª>Ðâ>ÑEs>Ñ Ú>Ñû>Ñ0.>ÑH>>Ðíò>У->З*>ÐqÐ>ÏÂ3>Ïa‡>Ïš>Ð&>ÐTQ>Ð¥ž>ÑX>цÁ>ÑcÃ>ÑŒ>Ñ­Ö>ÑÜh>Ñj>ÑU>Ñj>е:>Ïît>Ï>ÏÓ^>ЉÔ>ЄÆ>ÐÒ>Ñ¢í>ÑÚ¿>Ñΰ>Ñî5>Ò>Ò U>Ѻ>Ñeî>Ñy¦>Ñ ì>Ð'?>ϧ>Ïé >ÐZ.>е>Ñ_>Ñ¡>Ñð>Ò!‰>Ò\ >Ò›ÿ>Òœí>Ò\>ÑœÙ>ÑHÎ>ÐÙ0>ÐY >Ïé7>Ïäâ>ÐYe>ÐôÒ>ÑsŒ>ÑÛ>Ò`Ï>Òk´>Ò²>ÒØ“>Òæ>ÒK—>ÑÌÔ>Ña >ÐâÊ>Ðs >к>Ïá»>Ðf&>ÐÄ–>ÑJq>Ñá>Ò^ñ>Ò‚Ð>Ò±t>ÒËo>Ò¸t>Ò8¡>Ò>ѦÓ>ÐöÛ>Ðr©>Ð >ÏÛ<>ÐXí>С>Ñ#Ô>Ѳ)>ÒÇ>Òq.>Ò¯`>Òþ>Òù>Ò4í>ÑÕ>Ñvæ>Ðâ%>Ðjý>Ðu>ÏÚT>Ð v>Ðmê>ÐéH>Ñ“O>Ò9>Ò>Ò:°>ÒyB>Ò}d>Ò†>Ñß>Ñ \>вJ>ÐPô>Ð>Ð2|>Ð »>Ð1º>К×>ÑPÝ>ÑÕ>ѵR>Ñ»3>ÑÚ>ÑÜL>ѧÀ>Ñ_À>Ðñ >ÐÁ\>Ðkà>Ð"->ϼ]>ÏÃØ>ÏÏ‹>Ðe>иS>Ñ 1>Ñ:6>ÑH¼>ÑW'>ÑkÓ>Ñi>щx>ÐëA>РB>ÐPZ>Ïù¤>ÏN1>ÏÝ>χˆ>ϺÓ>Ð7/>Ј¬>Юe>ÐÊ>Ðé•>Ñ4’>ÑVÁ>ÑÃ>ÐÌM>Ðf§>Ïú¥>Ϙg>Ï >Ï:(>ÏL­>Ï{e>Ï̓>Ð<>Ð!Z>ÐC¯>Ѓé>ÐÏ>Ðæ>ЖÖ>Ð3y>к>и>Ïft>ÎØ°>ÏM>Ïk>ÏÏv>Ï ©>Ï´h>Ï×e>Ѐ>ÐL->Ð_D>Ð-d>ÏÎß>Ï™ø>Ï’€>Ïþ>έ$>ÎèŒ>ÏPü>Ïx(>ÏÓ>Ïà>ÏÐ…>ÏÞÞ>Ïø<>Ð&>Ïùä>Ïí§>Ïž§>ÏHû>Ï&>ÎÐ6>ÎÞW>Ï&7>Ï•ë>ÏÙì>Ð…>Ðw >ÐK>Ð2 >Ð~Å>Ћo>Ð]Ê>Ð+É>Ïç`>Ï–²>ÏH,>ÏÐ>Ï>ÏoÈ>ÏæG>Ð3¦>ÐÇ>ÐØ7>Ðj>Ы>Ñ g>Ñ¡>ÐÁW>Ђ<>ÐQ–>Ðë>Ï‘>ÏL9>ÏUe>ÏÙ3>ÐÜŒ>Уþ>Ч¿>Ðxø>ÑÝ>Ñv­>Ñ«^>Ñ•>Ñ>1>Ðî>ÐÆ{>Є²>ÏÜå>Ï~>Ïr>Ðý>а¾>ÐÉ.>м·>ÑJÊ>ÑÞ>Òjé>Òyi>ÒB‘>ÑÉ>ÑO*>и>Ð^G>ÏüÍ>Ï N>ÏÔ·>ÐDf>ФŒ>ÑÉ>ÑO¬>Ò¢\>Òµ@>Òý¬>Óü>ÒÀ<>ÒD->Ѻý>Ñ%>Ðã_>ÐJÄ>ÏÊ¡>Ð* >Ðl>ÐÒI>ÑY:>Ñ×õ>ÒŽv>Ó–>ÓœL>Óã/>Ó€>Ò´F>Ò>Ѫ>Ñ??>Кi>Ðo>ÏûÂ>ÐaÍ>ÐôK>Ñ›>Ò+Á>Ò¼Z>Ów>Ô Æ>ÓúT>ÓÅ>Òúu>ÒN±>Ñí%>Ñ]n>оµ>Ð >ÏÆ@>Ð1¼>Ðã¾>ÑÖ—>ÒÃ>Óà>ÓqÃ>ÓÇ>ÓÙU>ÓÄñ>Ó,>Ò {>ÒÑ>Ñ~%>лr>Ð,£>Ï¢¿>ÐÒ>ÐËc>ÑÌ]>Ò®Ó>ÒäÒ>Óû>Óé+>Ó‡Â>Ó‘->Òû´>Ò®>Ò;>ÑcÚ>Эê>Ðn3>Ï’>Ð T>Ф->Ña%>ÑÏí>ÒMª>Òà->ÓÖ>ÓBc>Ó›A>ÒÑ^>Òä>Ñ¡>Ðåà>Ðn¸>Ð[€>Ï3Z>ÏÔé>Ðjö>Ðüµ>Ñi‡>Ò,Š>Òi5>Òk*>Ò„¼>Ò–‡>Ò@O>Ñ×Ì>Ñ'U>ÐoE>Ð P>ÐS>Ï9h>Ͻl>Ð& >ÐŽ>Ñ"×>Ñ¡l>ÑÑý>Ñʼ>ÑÔè>Ñä,>ÑÏ >ÑÌ>Ðñ¢>Ð*³>ÏÈt>ϵG>Ï1e>ÏÊÙ>Г>Ð&Æ>ЂÜ>ÐØ×>Ñ ó>Ñ$C>ÑDu>Ñžà>Ѭ€>ÑAW>Оª>и>Ï—;>ÏZN>Ï->ÏQâ>φ7>ϸL>К>ÐA€>Ðm.>Ð’>Њ>Ñ4(>Ñ~k>Ðͪ>Ð45>ϧ>Îôß>Ϲ>ÎÒŸ>ÏV>Ï/¤>Ï` >Ï™ >ÏÌ>Ïõ">Ðò>Ð@Ú>Ð3>ÐáŸ>Ðx¿>ÏÏÀ>ÏV=>ÎßE>ÎÓ>΃ß>Ο›>În6>Îäã>Ïë>ÏP>ϲ7>ÏöŒ>Ð>Ð>Â>ÐW´>ÐÎ/>Ïçt>ÏK>ÎúN>μ¾>δ>ÎäÅ>Î×­>Ï<ã>Îåî>ÏSá>Ð>дË>Ðù`>ÑK">п¬>Ђ<>Ðä>Ï“ä>ÏEa>Ïä>ÎìÏ>Ï: >Ïsô>ÏÕJ>Ïô>ÏÕ >ИU>Ñ„Å>ÑÐõ>Ñ›>Ðý„>бp>Ðj>ÏØº>σ±>ÏR§>Ï'e>Ïz‘>Ïoì>Ðiƒ>ÐoÇ>ЫÛ>Ñi >Ò:¬>ÒBó>Ñ¥h>ÑjÀ>Ñ( >ÑÊ>ÏÙ_>ϰ¤>χ³>Ïa‰>Ïœ0>Ð!>ÐÙW>Ðð >Ñuå>Òn¢>ÓÑ>Ó˜¢>ÒUu>Ò$§>Ñâ>Ðù‘>ÐVñ>ÏôB>ÏŸy>ÏâJ>϶3>ÐrQ>Ñ4œ>ÑÉ>ÑñØ>ÓE>ÔO2>ÔD¥>Ó[b>Òë?>Ò"]>Ñ#c>Ðh¹>Ð.>ÏÄ>Ðî>Ði>ÐЕ>Ñ£/>Ò-Ã>Òää>Ô ­>Õ¼>ÔCä>Ó9Ö>Ó'x>Ò§Á>ÑÞ<>Ñ1½>ÐÞG>ÐÖ>Ї>Ð^Ô>а‘>Ñmn>ÒcÕ>Ó0k>ÔF(>ÕBK>Ô¨V>ÓŽ»>Ó‚d>Ó>Òßü>Ñöà>Ñ/¾>Ð&ê>ÏÒ’>Ð.ß>Ðá >Ѥ×>Ò¦Ê>ÓÑŒ>Ô7 >Ô¹¶>Ô·Á>Ô8?>Ôº>ÓB >ÒaX>ÑÝ>ÐÍ'>Ðo>Ï„‡>ÏüÚ>Ðßc>ÑíÖ>Ó>Óz>ÓÕ>Ó¿Ã>Ô3†>Ô&>Ô…>Ó >ѱ7>Ñ1>ÐŽž>ÏäŠ>ÏjÊ>Р>ÐÕ‹>Òî>Òx)>Òä>Óeþ>Ó±>Ô >Ô&,>Ó™>Òѽ>Ѩè>ÐÞÝ>ÐÏ©+>ϵ>Ïß“>г)>Ñž>ÑÎ>Ò&X>ÒÚ¼>Òùf>Ó$ø>Ó0>>Ò½ü>Òr >ÑMÃ>ÐSº>ÏÀF>ÏÝ>Ï ç>Ϻc>Ð]r>Ðåú>ÑH”>ÑÍh>ÒŠŠ>Ò@Œ>Ò6<>Ò&(>Ñ£¶>Ðð>Ðy›>ÏßÒ>Ïy>ÎüP>Ï­>Ϙ >ÐH>ÐQ´>вÝ>ÑÕ>Ñ]>Ñc“>Ñ\‚>ÑN>Щ2>Ðk~>Ðõ>Ï´ˆ>Ï]r>Ï Ô>Îãõ>Ï7ì>σH>ÏÏ>Ð( >Ðu5>Фá>ÐÃ2>ÐÀo>жG>ÐØ´>ÐO–>Ïñ¨>Ï’J>Ϙ>ÎÚx>ÎÈ1>Îûl>Ï+K>Ïfs>Ï®”>Ïï>Ð0³>Ð{®>ÐGH>Ðsa>Ð0 >ÏT>ÏtË>Ï+ò>ÎËh>ΫÑ>Îjc>·>Μ>Îâ›>Ï‘>ÏIl>ϱ“>Ïä>Ïâ>Ð/b>Ïsâ>Îté>Îåu>Ï Û>Îåc>Ο >Λÿ>ÎÊÊ>Îô}>ÏP>ÏA*>Ï#>Ð §>Зz>ÐT >Ïà#>ÏË[>Ïv¿>ωÍ>Ïv¶>ÏA­>Îõ©>ÎÖ~>Ï* >Ïvk>ÐÍ>Ïœ>Ïÿ|>з}>Ñwô>ÑÑ>жú>Ð’—>ÐSz>ÐmÞ>ÏÕ#>Ïuw>Ïp:>Ï>ÏØµ>ÏÛâ>Д¤>І™>Ðá¯>Ѥ‘>Òѹ>Ò¬‹>Ñæ>Ñþ>Ðê>Ðk>ÐÇ>ϳø>Ï·a>Ï:A>ÏÅ0>Ð)w>ÐÁÍ>Ñ+o>Ñ¿å>Ò¤í>ÓÒ>Òê4>Òäø>Òö>Ñå¿>Ðãë>д>ÏöÚ>ÏŠ«>ÏG¸>Ïå>ШR>Ña[>ÑàÝ>Òi>Óêt>Ô_Û>Ô v>Ô>Ô™>Ò¯ï>Ñ[>Ð\>Ï÷4>Ïé>ÎÜ>Ð?>ÑF>ÒE>Ò®Š>Ó˜>Õm>ÕÌ]>Õ­i>Ô×>Ô,—>ÓŠz>Ò>ÐÑÀ>ÏÞ>Ï A>Ï[>ÐLþ>ÑS>ÒD>ÓI[>Ô7>ÕM¯>ÖÈ>Ö¼Í>Õ¿t>ÔœÚ>Ó†>ÒSc>Ñ1ô>Ð9>ÏÄ>Ï“þ>Ð…9>Ñ#æ>Ò4m>Ó”>ÓÖ'>Õ;i>ÖL®>×Ö>ÖQn>Ôµ³>Ó/³>ÒPÍ>ѯÍ>Жí>ÏêM>Ï®>Ð+Ì>Ðëg>Ñê©>ÓÐm>ÔVÛ>ÔéÎ>Õ‘ñ>Ö/ô>Õ€S>Ô[`>Òô­>ÒAx>Ò\>ТR>ÏŠM>Ï_š>Ð _>пÌ>Ñå>Ò„~>Ó“>Ô»>Ô°ï>ÔÔ%>Ô˜¤>Ô»>Òp>Ñ™b>Ðý<>Ð(>ÏZ:>Ï/é>Ð á>ÐÖk>Ñm#>у>ÑÍô>Ò¼È>Óp±>Ó¥ô>Óös>ÓD•>Ñee>ÐÚ >ÐW”>ÏÎn>ÏG>Ï>>ÏÈD>И(>Ðî{>ÐèÝ>Ñ®>ÑPc>Ò2ª>Ò‡W>Òí…>Ñþî>Ðby>Ð">ÏÜ.>Ï’W>Ï´>ÎÉí>Îéº>Ï•U>ÐC½>Ð×o>Ñ0Æ>Ñ.÷>Ñgú>Ñy´>Ñfà>Ь©>Ïà¿>Ï]í>Ï\P>Ïb >Îú¾>νˆ>ÎôY>ÏN|>Ϻ¹>ÐL)>з5>ЯÀ>иK>ФN>й>ÏoŽ>ÏV¹>Ï W>Ï$G>Ï€>ÎÀÄ>ÎÝ>ÎèÔ>Ϻ>ÏHF>Ï­>ÏäZ>Ðb>Ð_b>Ïü >ÏoÉ>ÎÖ÷>Ï.>Ï9×>Îò,>γ×>Î’>ÎN>Î`[>Ά.>ÎàÊ>Ï¿>Ï=v>Ïïè>ÏãX>Ïv¯>Îùv>ÎçT>Μ>Ϋ;>ÎÕÚ>ÎÂ>Îr’>Î|ó>Κ”>ÎËŸ>Ïvb>Ï‘\>Ϫ¦>Ð2n>ÏÒj>ϺÐ>Ï>Ïx[>Ï?‹>ÏÏ>Ï;µ>Ï+ñ>ÎÉ…>β:>Îã&>ÎòÂ>Ïì>Ï—Ý>и>е>ЀÆ>Ð’s>Ðu<>ÐIB>Ïé#>Ï»>Ï->Ïá>Îó|>Îø›>Ï_º>Ï80>Ï)û>ÐÃ>Ðá¿>шt>ÑÔƒ>ÒDî>ÑËi>ÑMg>к6>ÏÁ>Ïf>Ï@,>Ï%>ϲ>Ϙ>ÏÜ/>Ð&q>Ñ'>Ññ}>Òâ/>Ó@å>Ó¿g>ÓH>Ò¨>Ñ¿\>Ð¥Æ>ÏË]>Ïx>Ï( >Îì”>ÏúB>Ð÷O>Ñ,•>ÒÈ>Ó­>Ô9x>Õ.;>Õ&§>Ôù>ÓÄW>Ò˜Ö>ÑKÃ>Ð_>ÏÂ%>ÏNm>ÏO>ÐÆô>ÑkÙ>ÑP¤>Ò¸î>ÔÚ>ÖÜp>×Çf>×yÇ>Ö?‡>ÔÝT>ÓÇÁ>Òé>Ñ´>Ðy>Ïx8>ÏF‘>Ðpì>Ñgƒ>ÑéÈ>ÓCã>ÕeŸ>ØN>Ùaé>ØÒÛ>ב>Öv>Ô†>ÑÍÇ>Ðü'>Ð%Í>ϺÍ>Ïh¼>ÐWÔ>Ñ>Ñô·>Òû\>Õ$z>×jÊ>Ù4”>Ù&^>×”>ÕÄI>Ó°¡>ÑtÑ>Ði->Ð$t>Ð+{>Ï]>Ïú>Ф>Ñ:¯>Ñ•¡>ÔLb>Öat>×ç >×ôû>Ö’(>Õ<¬>ÓWÑ>Ñ} >Ïþ*>Ïç,>Ï‹Î>Ï:×>Ïî>Ð`E>Э–>ÑÛ‰>ÓA>ÕBÃ>Õóo>ÕçÛ>ÔÁb>Óª9>Ò­ó>Ñ„ì>Љë>Ïäx>ÏFÝ>Îü>Ïzþ>ÏÄ>Ð0ô>ÑnE>Ò\Ã>Ó»h>Ôe®>Ô%Æ>ÓO~>Ò?>ÑÓÊ>Ðî¦>ÐDˆ>ÏÄ\>Ï'8>ζN>ÎíÒ>Ϩ>ÐÓs>Ðù@>Ñi8>Ò>Ò¥>Ò´O>Ò‡˜>Ñëé>Ðì{>Ð7í>Ïܘ>ÏÐô>Ï·>΋>ÎÒñ>ÏÁ >ÐIŸ>Ð9™>Д¡>Ñ#à>чª>Ñ€?>ÑA>йÇ>Ð*>Ï…I>ÏJ >Ïbl>ÎÓ >Î…K>θ\>Ïa>Ïv×>ϸ”>Јï>Ф >Ð}…>ÐY>Ï“$>Ïh->ÏB„>Ïþ>ÎËü>ζZ>ÎzÅ>ÎÛh>λ„>ÎÄÝ>Ïy>ÏW‰>Ï¥{>σq>ÏQ>ÏdE>Ï ö>ÎÃ>ÎÙ«>ÎÖã>Γy>Îež>Î>ú>ÎW>Î>Î"Ö>Î$Ñ>Î`‘>βÚ>ÎÊ3>ÎÒè>ÎùØ>ÏW">Îò%>Ϊî>Î`æ>Ε>Î9>Î#9>Î>“>ÎHJ>ÎPª>ÎÛ>Ή~>Ï5]>Ð S>ÏÔŸ>Ïj>ÏŸ–>Ï`c>Ï…>΢>ÎN>Î/¦>Îyu>Îb>΄|>Δ3>ίw>Ï8Ö>ÏÖú>Ðt>Ю¬>Ð;6>Ð4Ð>Ðf>ÏÆ¹>ÎÆ|>Î>Î’*>Î, >Îç)>Îz…>Î×[>Ï9ü>Ïüz>Ьy>ÑKG>Ñ¥7>ÐïX>Ñš>Ðí%>ÐÈ>Ϧ¡>Îå)>λ>ÎÜ>Ïî>Ï3ð>Ï•<>Ð>ÑI†>Ñè6>Ò²C>ÓX¹>ÓL>Ò²¢>Ñgd>ÑË>Ðk'>Ï2Ð>Îÿ’>Î¥8>ÎÐm>Ï»”>Р>Ðò)>Ò9>ÓoS>Ô܃>ÕÊ >Õ‰">ÕÆ>ÒVÀ>ÑÌË>ÐÕ·>Ïð>Ïm\>Ïö>ÎÑÀ>Ϧ@>ÐÏ>ÑIk>Ó •>Õ¸ë>×WÐ>Ù0Á>Ømf>×/n>Ô¢¨>ÒQ>Ñ:‘>Ðxz>Ï·0>Ï"{>ÎÎ6>Ïc–>Ð[³>ÑtŽ>ÓXI>ÖCl>Ùd)>ÞÛŠ†>Ø\ã>Ö~>Óû¡>Ñã>иÏ>ÏâÇ>Ï%T>Τä>Îåh>Ð'Š>Ñg´>Òê2>ÕŠe>ÙÃ*>Þa>Ý•>Ù>Õém>ÔA2>Ñùð>й‡>Ïí©>Îñ>Ï-.>ÏX>Е>Ñ©>ÒO¿>Ô9D>×ËØ>ÛŒŽ>ÚòÔ>×?d>Õ2>Ó¹0>ÑùÖ>Ñ%>д>ÏH>Ïæ>ÏG>ÏÖU>ÐÞ±>Òym>Ó1>Ôéæ>Öߦ>Ö¬Ž>Ô¶E>Óô³>ÓRâ>Ñò >ÐÛS>ÏÈ_>ÎÕC>ÎÉo>ÎíP>Ï^ë>Ð^Ì>Ñã™>Ò×þ>Ó³å>Ô}k>ÔB‘>ÒÇd>Ñ÷ø>Ñ¥>ÐÉ+>Ðb>ÏŽò>ηò>Î`ú>΂þ>Ξ·>σ>Ѓý>Ñ‹n>ÒC >ÒÌv>Òæì>ÑSË>ІÀ>ÐFé>Ïî/>Ï >>ϼ•>Îǘ>Î+€>ÎOØ>Î,‰>Ï6g>Ï¡¦>Ð<>Ðü@>Ñø!>Ñâ«>Е±>Ï£#>ω{>ÏMŸ>ÎÚÑ>ÎÍÄ>Îd¡>Íÿz>Î0y>Îf`>ÎÙ`>ÏJ>ÎàÇ>Ï~>Ði¤>ÐyA>Ï—ê>ÎÞÂ>ÎëÆ>ζÖ>ÎGÇ>Î"Ù>Î¥>Íh>ÍÚ²>ÎE>Ξe>ÏJC>Îò»>Î÷Ù>Ï<Ý>ÏOS>Îì>Ι×>Îy¢>Î *>ÎÓ>ν>Íî >Î%ô>ÍÖ³>Í¿9>ÍÍU>Íü'>Î÷>Íî'>Î>ÎRÈ>Í«ö>Î0&>ο¹>Î$Z>; >Í€>ÍIÒ>Îõ>Íè >ÍñS>Î />ÎDn>Ή×>ÎQå>Τ‡>Îê}>ΖÜ>μë>ÎÙ.>ÎXg>Íã©>Í~>ÍP>Í÷Ó>Íþ4>Î0x>Î…·>ÎÝ\>ÏZŽ>ÏÃÔ>ÏÝ>ÏÕn>ÏÊU>Ï¢>ÏCÑ>Θ·>Î6ã>Î>ÍÍ“>Í¢I>ÍÒ„>Îi¡>Îö>Ïq³>Ðb>ÐÈ>>Ð>ÐSg>еÛ>Ðt…>Ð o>Ï>Î2>Î]¶>Î L>Í×>ÎE>Îݦ>Ï{â>Ð#ÿ>ц>Ò8,>Ò) >ÒM‚>Ò<>Ñà>Ð4d>ψ7>Îï‡>Ϋ>Îm¢>Î'Ä>Îx³>ÎÔƒ>Ïá‹>Ñ[ž>Òɹ>ÕRã>Ö® >ÖÈÿ>ÔnJ>Òƒå>ÑMŽ>Ð*>Ïg(>Ï( >Îüƒ>Î! >Íû°>Τ6>Ði >ÒË‚>Ôª>ØeÌ>ÚJ>Û—[>Ø».>Õ¤>Òcu>Щ>ÏX>Ï,B>ÎÃø>ÎSÃ>ΘF>Ïr>н’>Óm >Ök¦>ÚŸÈ>Ýz>áÒK>ÛÄÅ>ÖŸ«>ÓPŒ>Ñ6>ϼ¥>χc>ÎÉ>Î'Ž>΃`>Ï/>ÐZÕ>Ò¦k>ÕõV>Ûê£>ßZ“>à;>ÜÆœ>ÖÜÑ>Ó#8>ÑKq>Ðm;>ÏðÕ>Ι‰>ÍñÝ>Î~Ù>ÏLÙ>ÐP3>Ò—ã>ÔÊ_>Ùºë>ÜH.>Û8­>ØÔ7>Ô!ê>ÑÙÑ>ÐÚò>Ïôñ>Ï͉>ή>ÍêÙ>Î7š>Ï>Ïã,>Ñ^>ÓæÍ>Ö‹H>×>ÕhF>Ô;œ>Ñ2Â>Ïù>Ðø>ϱè>ÏG³>Îo^>Î1>Îu>Ω6>χ>Ïÿz>ѶÙ>ÓI@>ÓYÀ>ÓBh>Ò‡«>Л@>Ïm¥>Ï›³>ÏŠ·>ÎÒ>Î,…>ÍëÙ>΃>Î<;>Î\K>Ï^r>ÐàÉ>Ѧ€>ÑYk>ÑRr>ÐÖo>ϲ÷>ÏVê>Ï_•>ÏŽ>Î)5>Íøþ>ÍËí>Íé)>Íù0>Î<>ÎÖ>ÏÂî>ÐKõ>Ð%>Ï®!>ÏgÂ>Ï è>Ï2g>Ï43>Îw>Í›£>͵Ý>ÍÊk>ÍÛV>Íú)>Î7ã>΀^>ΣR>Îú#>ÏOf>ÏÅ>ÎÇ6>Άw>Έ>Θ>Ξ>;W>Í©a>Î>Íϼ>ÍàF>Ε>ÍÃO>Î >ÎW‘>Γ‹>Ξ>ÎjØ>ÎX$>Îݵ>Î?º>ÍÙÈ>ͼ+>Í¢Ñ>Ìj>Ìív>ÍB]>ÍwŽ>Í›Ñ>Í–3>Íz¤>ÍÁn>ÎJù>Íœ²>Í{I>ÍN>Íh³>Íi >Íaè>Í,#>Ì¡ü>Í ý>ÍsÑ>Î ;>ÍðÙ>Íð>Í«ê>ÍÜË>ÎE>ÍþÞ>Íôú>Îõ>ÍÄu>Í‚¸>ÍS >Í_>Ì‚>ͧ>ͯå>Îo>Îb%>Ï>ΠS>ÎNq>ÎÀX>ΰX>ο}>Îe©>Íô >ͧ>Í<¹>Í,≯ï>Í#Ô>Íë¯>έ >Ι8>Ï#>Ï«>ÏdÒ>Ï”“>ÏI’>Ïà>Î}Þ>Î Y>Íç>Í£;>Í¡L>Ìþ·>Í>½>Îü>Φî>ΤÍ>ϲ¤>о>Ðã!>Ðäî>ÐaÖ>ÏÙG>ÎÍ>Î}>ÎS>Î ÷>ÍéË>Í/+>ÍHm>ͪ{>Î’Å>Ï(>ÐÁ>>Ò¦>Ô7«>ÑÿÊ>Òdo>ÐûØ>Ï¢!>Ïj>ÎÕÞ>Ïd>ÎB>ÍN>ÍkS>Íã'>ί¸>Ï–S>ÓJ>Õêð>Øå>Ùœ„>×\³>Ò?">Ð:Y>ÏyÜ>Ïi>Îݸ>Î Þ>Í|@>͹œ>În×>ϧ®>Ð7Ã>Ô¥>Úl>æ?è>é˜#>ݼ–>ÔŒ„>Ñ~>ÏÛq>ÏË>Î)Ð>ͬø>ÍŽH>ÍÞ[>Î~¥>Ï¡>РV>Ó®u>Úñ>éþ§>íÀQ>ÝÇÑ>Öcì>Ѭ->Ð/­>ÎÓø>Íâ»>Í“Õ>ÍJ>Íêu>ÏM>Ïkü>Ð ‹>Ò/Ž>Ôûy>ÛzÙ>Ü|+>Ø>Óã>Ðï>Ï·ò>În>Î#>ͯ*>Í*‹>ÍÜÙ>ÎG>ΛÆ>Ï|‹>ÐmÕ>Ñ!>Ô&ü>ÕÍ|>ÔgP>Ñž>ÏV¯>ί[>Îer>Îö>ÍÁ >Ìàƒ>Íqä>ÍÃ|>;ª>Χá>ÏXÛ>Ï•l>ÐÁè>Ñ«™>ÐÊ¢>Ï” >Ïp>θ¤>Î¥>Î<>͈A>Í9*>Í„¯>ÍÞ¨>ͶO>Î>v>δ>Îà>ÏE>Ï&ß>Ï$ñ>Îï>ξž>Îø9>Î7Ù>ÍTS>Í8Ü>ÍR>Í|Ë>Ͳ$>Í®Û>Î[>ÎeÏ>ÎkB>Í¡E>Í«ç>ή>Τj>ÎP>ÎI>ÍÀÿ>Í@>Í0>ÍRè>Írà>Í‘1>ÍáÖ>Î6·>Íúö>ÍÖD>Í<>ÍV¸>ÍÈÇ>Ζ>ÍË2>Î*g>ͦß>Í`®>ÍBê>Ìø{>Í<Æ>Ímq>ͺ$>ÍÒ=>Íå>Í‘g>Íer>Íç>ÍÊQ>Í­ >ÍÂ>Í´P>Í~Y>Í›_>Ív~>Ì~Ý>Ì¢Ö>ÌÂë>ÌÕú>ÍBú>Í>Ì£ž>Ìgà>Ì'>Ìž$>̺B>Ìr6>Ì¿‹>Í Â>Íœ>Í >Ì…>Ì£ç>ÌÅå>Ìyô>ÌÇ,>ÌÀÊ>Ìx\>Ì™‚>Ìáj>ÌÞ…>ÌÌ©>Ìt’>ÌÌC>Íó>Í=£>ÍÑ>ÌŒ¦>̼÷>Í'w>Ìyü>Ì«ÿ>ÌZ¦>̇>ÌÐ>ÍHr>Í1¥>Í ­>ÌŒÊ>Ìá>Í L>Í%»>Í]t>Ìz >Ì¥ >ÍP>Ì›5>Ìñþ>Í^>Í0×>Ìà<>Í0K>Í|6>Í]>Ì$I>ÌÃl>ͽ>ÍD >Îõ>ÌD>Ì> >ÌA>>Ìe~>Ìèì>;•>ÍlS>ÌáÉ>Í3>ÍŃ>Íl_>Ìže>Í >Í¢‰>Í*“>ÍE >ÌR‹>Ì?>Ì:Ç>Ìy?>Ìè>ÍN7>Íþˆ>Ì'>ÌzG>Ͳ¦>ÎŒ>Ípä>ÍÔµ>Íè>Ìž©>Ì`>Ìxˆ>ÌŒ?>Íp>̪ð>̰P>Í ¯>Ì:ð>Íõu>ÎBÇ>ÌGb>΂E>ÍÓË>Í·>͆–>Í9Ÿ>ÌËc>ÌÄo>Ìȉ>Ìï£>Ì#Ï>Ìb‹>Íð>;>Ö5>Ögµ>Ì`š>Ρ>Í<=>Í*“>Ìü>Ìü%>ÌÔô>ÍÌõ›>ÌÙþ>Ì_œ>ÍF*>ÏN >ÎÈû>ÒYÅ>åPË>Ï ô>Î=»>Í™µ>Í!Š>ÌÐT>ÌúD>ÍP¼>ÌÀ>Ì®j>ÌZº>ÌÅ·>Í£ >ÎÉ>ÌóÃ>Îg`>Ï Þ>ÌÑC>ÎMˆ>Í~y>ÌáÐ>ÌÊ÷>ÌËz>ÌÌJ>Ì–è>Ì•L>ÌŸ>ÍŒ>ͨ§>ά‹>ÌÚè>Ëß.>ËLz>Ì)!>ÍL>Í‚'>Írw>̲W>Ëït>ÌbÜ>Ì•4>ÌÅn>Í1>ÍEý>Í£M>ͽ>Í&>ÌôÅ>ÌUë>̧Ù>ÍCC>Ížj>Í]S>Ì£7>Ì™>Ì`>̾>Ì÷%>Í`F>Í9ì>ͺ>ÌA.>Ìr=>Ìï;>ÌPg>Ì;„>Í\ >Í)>Ì>>Ìy>Ì 4>ÌWC>ÌÜØ>Ìù>Í*ù>Íq>Í7(>Ìr>Ìk>Ì¥&>ÌØ6>Ì¿Å>ÌÝŸ>̧]>Ìj>ÌŠù>Ì~´>Ì5>ÍH€>Í:ž>Ìøî>ÌÕè>̬ >ÌÌ&>ͨ>Ì·Ï>ÌË >Íy>Ít>̦->Ì"ï>Ì”õ>ÌËç>ÌÁ,>̃‡>ÌË'>Ìá>͇>͉>ÌàT>ÌÙí>Ì® >̪>ͼ>ÍPé>Ì›¿>ÌAk>̧>Í È>Ìó”>ÌCœ>Ì?Ó>Ì7 >Ì%P>ËËZ>ËØŸ>ËÖ)>˾ê>ˬš>ËÞŒ>Ì{Ú>Ì#>Ì#‘>ËïÉ>Ë£è>ÌŒ>Ì;ô>Ì)¦>Ì:>ÌGJ>Ëúu>Ë÷¢>ËÙ>Ë]>Ë >Ëd½>ËÄ'>ËÇý>ÌN!>Ì>f>Ì$>Ì)±>ÌZC>Ëû¾>Ë”>ËÆ >˺Œ>ËlÝ>Ë-“>Ë·>ÊaÆ>ʹÐ>ÊâG>Ëoý>Ì~F>̃_>Ìô>Ë÷>Ì V>ËÌ®>ËJ‹>ËR¡>Ë­“>˸Æ>ÊJ¥>Éå­>ÉÆñ>Ê}J>ÊüÅ>Ë6%>˶“>ËÛŒ>ËÊÍ>ˈK>Ëð*>˽³>Ë|Ê>Ë3S>Ë4™>Ë!I>ÉL9>È>Ÿ>ȈG>É>Ê{>ʼE>ËN>Ëb>Ë™l>ËŽˆ>Ëïò>ËÀŸ>Ë2i>ÊØQ>Ê“Ñ>Ê->Éu^>Ç9…>Åa7>ÆK·>Èz¼>ÊÛ>ÊEq>Êž‘>˼Õ>ËÅ5>ËÓt>Ëw­>ÊØ>Ê÷>ÊdV>ÈeI>Ç"û>¿Ûý>¾ö>ÄI>Çê¦>ɆÓ>ÊAô>Ë#C>ËÔÖ>ËÒð>Ë¥ü>ËJø>ÊÌ*>ʼv>Ê!í>ÈL×>À8>¯Æª>­º4>¿£À>Ä#ˆ>Èó>ÉÇ>Ëq>Ë•1>Ë©Î>ˉb>Ë‹y>Ë.#>Ê»[>ÉËE>Ç–#>À|>®„>¨G?>½þ>ÃÚo>Ç¢>É©*>ˤ>ËL >Ë ¬>Ëß½>Ë©Õ>Ë@|>Ë.X>Ê\¾>Ɇ9>ÅäŽ>½©>»Ë7>Â÷Å>ÇB‘>É6>Ê&„>Ë4K>Ê÷¨>ËX>Ëù;>˵ò>ˆ,>Ëw/>Êë®>ÉF>Ç÷w>Åw[>Åu”>Ç`>ÊÞ>Ë?q>ÊÒA>ÊýŸ>ˉ>ˈï>Ìä>Ì+x>Ìpæ>ËÞ>ËgE>Êef>ÉûÓ>É1>Èð>ÈE½>Ê ¼>Ê»•>Ëw¤>Ë3>ËM>ˬö>Ì%Ý>Ì3>ËÌÛ>˧˜>Ë­¸>Ë]C>Ë#ä>ʵp>Êb‹>É·W>Éòõ>ÊåÖ>Ëp">Ëܘ>˵>ËØ>ÌÃ>ËÿX>˺î>Ëeo>˶š>ÌM9>ÌZ»>Ë¢k>ËgM>ÊÛÊ>ÊÀT>Ëï÷>ËÝf>Ëõw>Ìr>Ì >˼\>ËÕæ>ËïÕ>Ë­>Ë¢¼>Ìè>Ìi>ËÓ#>Ëm[>ÊüW>Ë ö>Ëä>Ëä.>Ì >Ì€p>Ì?›>Ì >ÌH³>ÌBÞ>˺A>Ë_R>Ëïš>Ì>ËÉ>Ë)>ËEF>Ël>ËÕ'>Ëð’>ÌM>Ëþ~>ËøD>Ëê>ËØâ>˶ã>Ë‹Z>Ë#q>Ë ¿>Ë>ËË>Ë$Ÿ>Ë¿>ÊÚÖ>Ë;ª>ËŠ«>Ëe>Ë,>ËXÉ>ËÊ >ËÃ!>Ë®>ËŸ‹>Ë'˜>ÊÚ>ʪ>ÊO‡>Ê<æ>Ê|»>ÊäÆ>ËYq>ÌA5>ˆY>Êû\>Ë'>Ëyó>˃->Ë’Ó>ËM>ÊÚ5>Ê_>Ê õ>Èî?>É/>ɦ»>Êv“>Êñm>Ë…_>Ë µ>Êê·>ÊÐÅ>ËuÂ>ËS >ËEƒ>ÊÚA>Êr1>Éh>Èë›>ÈŽ5>È>Ƚ>ÉêL>Ê€g>ʵ¼>ʼÀ>ʶA>ÊN7>ˇ>ËE>Ë·>ÊŸ—>É¥1>ÇŸ;>ÆÓ¥>ÆÈ¬>ƶ>ÇN>È„">Éœ0>Ê@>ÊG¼>ʽ$>Ëg>˾_>ÊÿF>ʈ·>Êœ>Éq>ÇŒ>Ã3Ø>ÂXu>ÄŒ¤>Ų9>ÆË¬>È Ô>ÉkÛ>ÉÆÁ>Ê0£>Ë#í>ËY>ÊŸÿ>ÉÕÚ>É~o>ÈŒÛ>Ç?>¿{q>¼`“>½£î>Ái$>Ä1 >Æîø>Èôx>Ée:>Égð>ʧ[>Ëá>Êž¼>Éà>É/e>Ç…á>Ä!2>½s‡>µÁu>´ïØ>¼lŠ>Ãd¼>Ç>Ȉ>ÈÞk>ɽÐ>ʼÊ>Ë@>ËR>Ê€Ù>É;ü>Ç8£>ÄAI>¾ ´>¶^>³³ã>º4Ù>Âd2>Æ9>È+º>È¥3>Éùª>ÊÏ>ËQ÷>Ë;¿>Êšn>ÉY2>Çë™>ÅŽ•>ÀË>»«¶>»>0>¿HÓ>ħ‡>Æçã>Èÿ‰>É”T>Ê >ʽ”>ËjÐ>Ë%Þ>Ê©W>ÉõK>Èl>Ç2¡>Ø'>ÃR >Âÿ«>ÈV>ÆZÙ>Çâ >Èã“>ÉÍ>ÊcH>Êì>Ëò>ˇ‡>Ë>>ÊKË>ÈÙE>È">Æ~s>Æö“>Ʊ >Æô->È¡œ>ÉWv>É—B>É÷á>Ê¢>Ëö>ˤ>ËÇœ>Ë?>ʈ½>Éðn>É€þ>È>ȬÐ>ÈGI>È€Ó>É]7>ʽ>ʸ>Ê@¸>ÊÔ¿>ËO@>Ë“(>Ët•>ËE>ʨè>ÊtÃ>ÊÉR >ÉWä>Èé?>ÉP/>ÉÝê>Ê+$>Ê™§>ÊçŠ>ËNÑ>ˉÈ>Ën­>Ëlu>ËYœ>ËÎ>Êï >Êùœ>Êkû>ÊO’>ÊU‹>Ê"`>Ê@¢>ÊŠè>Ë'ï>Ë\á>Ë´>˧î>˶ >ËÙÈ>ËÁí>Ëgñ>Ë7×>ËV>Ë'>ÊòD>ÊÕG>Ê•Ý>ÊJ¾>Êç&>ËÏ7>Ë¥>Ë–;>Ë»Ð>Ë‹s>Ët>Ë:„>Ë9>Ê×ú>Êr^>Ê9å>ÉðZ>ÉÔ€>ÊB<>Ê×>ʲr>ʰ%>Êå>Ë Ü>ËEˆ>Ë€Œ>Ë›¢>Ëe>Ê36>Ê/Ç>ɵb>É…Y>É“€>É™°>ÉåE>Êx·>Ê+h>É€Z>ÊX[>ÊÄY>˲>Ë*Û>Ë>Êõ>ÊU®>ÉÖ>É14>ȼü>Èȉ>Èî>É\]>Ê2>ÉÒG>É-ì>Éâ™>Êzu>Ê¿c>Ê÷î>ʲ´>ÊZL>Êÿ>ÉSà>È#§>DZÕ>Çî[>ÇÞÆ>È6~>Èä:>É¥>É€9>Éî>Ê>û>Êqå>ÊË\>ÊR,>ÉŸ>ÉÁ>ÈÕý>ÇP•>ƃr>Æœ[>Æ>Æ .>Çx >ÈLX>É‚•>ɸ>Éô=>ÊI9>Ê >ÉÊ[>É€Þ>É…m>ÇëÆ>źÄ>ÃñÞ>õ>Ä. >Å>ÆQÊ>ÇÙ'>ÈïC>ÉQ­>Éž>Ê Ä>Ê“>ÉÜÍ>ÉOØ>Èë5>Æ2%>à >Àæ‚>ÀŽ>Àe>>ÁÁ6>Ä Þ>Æf™>ÇÂÍ>ɼ>Év>Ê®>Êš>Éîü>É)>Çݪ>ÅÃi>Â`Œ>¿Ìy>½q6>½OÐ>¿•‹>›‚>Åä½>ÇÒü>ÈŠ>ÉHç>ÊÎ>Ê™­>ɳM>É2o>È:>Æ- >Â>¿([>¼Ä–>¼›ç>¿ w>Âêp>ÆVì>Çè'>ÈLê>É*„>Ê>ÊÆ¡>ʆ®>ÉêÍ>È&K>žû>Ã&G>ÁK >¿ec>¿u>Á">Ã^À>Æ.È>Çñ>ÈY‰>É1¹>Ê´>ÊÄ >ÊYÚ>ɳ\>Èû¬>Æø×>ÄÑ>èÚ>ÂÅL>Ãh>Ãò7>Å)>ÆÅŒ>È3€>ÉlR>ÉÜè>Ê^M>Ê¿õ>Ê>Él6>Éò¾>È? >Ç>Æ0Ý>Å+J>ÅbK>Æ >ƈ)>Çý>Èê¤>Ê ç>ÊRm>ʤ¿>ÊÓÙ>Ê*[>ÉÊ >ɺB>Èæ<>ȯ >È)j>ÇÛ>Ç(>ÇÈ>Ç÷Í>É >ɇÝ>Ên>ʪ>Êäõ>Ëd>ʶ[>ÊhŒ>Ê%>É“‘>Ét®>È£à>Èx’>Ȇ >ȵs>ȼŠ>ÈÓÕ>ɲÒ>ÊÆ¿>Ë>¼>Ë-5>Ë2K>ËY>ÊÜñ>ʘá>ÊS=>Éð>É*ú>Év,>ÉÆ‚>ÉœH>É‚œ>É…7>Éìù>ʰX>Ê×ì>Ë<%>Ë\z>Ë'r>ÊõÝ>Êâô>ÊЩ>ËN>Êz >ÊJf>ÊRa>Ê,Œ>ÊŽ>Ê2%>Ê+Ó>ÊÁr>Ë€>Ë—l>Ë%">ÊöÎ>ʳ¢>ʘD>ÊdÍ>Ê>ÉΔ>ɰ7>É­ë>ɾF>Épk>Éç[>Êbö>ʾ >ÊýÏ>Ë;¹>˘>Êí#>ÊK«>É÷Ø>ÉÁ·>Éc>É%>Éð>É9µ>ÉMY>ɽ>É~>Ê)Á>Ê€ß>ʾ~>Êòö>ÊÍ >Ê{î>Éš¶>É¢>É5@>ȶ#>Èf >È`g>ÈæŸ>ÈèÝ>È­>ɰ>É‚ª>Éõ8>ÊW£>Ê n>ÊÞ>Ê1k>ɰ|>É›¸>Ȭø>ÇØ®>Çc~>Æöb>Ç„ø>Dz¤>ÇÜz>Èl¥>ÈýÓ>ɇî>É÷—>ÊNÔ>ÊTå>Éä->É%Æ>ÈÂØ>ÈA½>Ç=*>ÆÏ>Å1Ì>Å»Ù>Æ“m>ÇŒZ>ÇÕ[>È:ó>É >É™ø>Éþ¸>Éí‘>Éž2>Éû>Èhõ>Ç6œ>ÅΤ>Äp«>õ>Ü–>Äâ*>Å´n>ÆDT>Çs>Èš§>É¥Ø>ÊX>Ê?Y>ɸ˜>Èó¬>Ç”Ï>Æ£>ÄJî>ˆù>Áèk>Áþu>Ã*¥>Ä’>Å«Õ>ÆÈ­>È º>É1R>ÉØ!>Ê_‹>É»ƒ>ÈÛé>Ç"á>Å J>ÂäŠ>Á’Ý>À >ÀUY>Á†>ÃVî>ÄwÒ>Æ>Ç­s>ÈÂ>ÉŽ+>Ê]ã>ÉdW>È%E>Æž—>ÄŽƒ>Âq—>ÀàÐ>À¯…>¿Ùç>Áù8>ÃÓa>ÄÁg>Æ`Q>Ǻb>Èo>ÉD>Êfô>ɰý>Çý†>ÆéÁ>Å>úS>ÂyÇ>Á®$>Àö×>¢@>ÄXi>ÅÌŽ>Æ…>ÇÙ>ÈÕ>É›>Ê4>É™/>È”,>Ç{>Æ Þ>Å[†>ÄÊé>ÃŽ$>Ã’‘>Ä’•>Äíf>Åü>ÇuÄ>ÈÚý>ÉeÐ>Éç^>Ê/«>Éx€>ÈN>Ç•Æ>Ç&Á>Ç ¡>Ʊ§>ÄêÅ>Å>Åèw>ÅüÕ>Æ‘=>È9>ÉmÁ>Éäè>Ê2{>ÊOÅ>É©>É&ë>ȹ2>ÈOÇ>Çê%>ÇðÙ>Æ–`>ÆÃ>ÇNC>ÇYÕ>Çj>ȯ¸>Éܺ>Ê'#>ÊmC>Ê›>ÊE«>Éëð>ɬ^>ÉR>È€[>Èdå>È,Ô>ÈŽÇ>ÈŽª>ÈwÑ>È—M>É%î>É‹í>ÉÞÑ>Ê‹>Êï‹>ÊÐ<>Êy¤>ÊQ¿>Ê#->Ét²>É(|>ÉC>ÈìH>É/|>É7ä>ÉRÌ>É‘F>ÉôÄ>ÊUX>Êê`>ËÀ>Ër>Ê·5>Êw]>ÊGØ>ɾ&>ÉÊ=>ÉÅ>É–>ɼU>ÉÇV>ÉÜ>Éíz>Êkq>Ë/‹>Ëûº>ÊÞ€>Ê‚>Ê7>É©T>É^ÿ>É„>É‹>É}†>Ɉõ>É¡²>Éœ >É×—>Ê+(>Ê®Ÿ>Êñö>ËEŠ>Ê©>ÉÎÙ>ɼ_>Éaÿ>Éé>Èþp>ȳ±>ȸÂ>ÈÏ>Èôœ>ÉÞ>ÉWÈ>ɾç>Ê…a>Ê×8>Ê×¹>Ê¥ƒ>Éý¹>ÉXø>ÈÎ`>ÈŽ >ÈbË>È2>Çôe>Ç«A>È>ÈI›>ÈÊ>ÉB°>ÉÒÈ>ÊJ¹>Êx3>Êb—>ÉÖ1>ÈúÜ>È.>Çœô>ÇuI>ÇÇc>ÇWâ>Æÿ’>ÇX>Çg­>È@ÿ>ÈÄb>ÉMy>ÉÆ$>ÊÒ>ÊH¹>ÉÃ|>È´¤>ÇÐò>Æà/>Æb®>Æg >ÆV!>ÆÚ¿>Ƶd>Æ´>ÇY>Çþë>ÈÃy>ÉQ9>ɽ0>Ê>ÉœÍ>ÈØD>ÇÕâ>ƾ/>Ų>ÅCŠ>Äóö>ÄúÛ>Å£>ÅÙa>ƳÍ>Ç‹)>ÈD]>Ⱦž>ÉQ>É›¿>É$Û>Èz>ÇP>Æ]|>Å'>Ä.>û×>ÃÓd>ÄDÏ>ÄÂY>Æ >Ç$>Ç>ÈÓ>ÉZ\>Ésý>Èèº>ÈFÙ>ǯ>Ų>ÄR¿>Ö>ÂÙü>Ã~„>Ã0>Ä&Þ>Å€ä>Æâº>ǵO>È›µ>ÉsÍ>Él>>ȶ2>ȯ>ÆÝò>Åsm>Ä56>ÂÁù>¬ÿ>ÂË^>Â×G>Ä-f>ŧè>Ç Ç>ÇÒ#>È’Ë>ÉdI>ÉlZ>È0å>ÇÈd>Æép>ÅÑF>Å7¼>ÃЈ>ÃJÎ>Ã8J>ÃÈ >Ä£ >Æ¿Ü>Çd™>ÇèF>ȯ>Éh)>ɧN>ÈêC>È4g>Ƹ¹>Æ3>ÅòÑ>Åk >Äd<>ÄI2>Äå–>ÅÉ„>ÇW‰>Ç’\>Ǩ >ÈÆê>ɉ­>ÉÝú>É\‰>È¢V>Ç'0>Æé­>Æh7>ÅãŸ>Å`>ÅßB>Æ\>Æ¡O>Çz>ÇT >È$>ÉZÛ>ÉÈÓ>Ê$>Ém>Éë>Èi5>Çú>ÇË>Æ:ý>ƹ\>Çvm>Ç̶>Ç¢y>Ç«>ǽ[>È6µ>Ég5>Éó,>ÊY·>Éþ%>É–à>ÉAÂ>É)W>Șe>Èû>Èk>ÇÞþ>È%]>È^I>ÈŠt>ÈÎ>É=|>É¿[>Ê#Å>ÊØœ>Êy>Éìp>ÉÅÅ>ÉÆ|>É >ÉJ>ÉÑ>È5>ÈÆ2>Ɇ>É3³>Éu\>Éü;>ÊA¯>ÊY%>Êæš>Êb>Êð>Ê^>Éô2>ɹ8>Éê}>ɯ¦>Ét“>É€>É–>ɳ&>ÉÛí>Ê&ó>Êaø>Ê%½>Êİ>ÊaØ>Ê>ɽC>ɇŸ>ÉsR>Él>Ép­>ÉŒÁ>ÉÌT>ɲæ>ÉÊ<>Éæ>Éãf>ÊÞ>ÊKØ>ËÎ>Ê;Ü>É»œ>É]+>Éf>Èë>ȼ‘>È¿ >ÈÊ$>Èàv>È÷É>É;æ>É+³>ÉSÞ>Éßa>ÊMP>ËB>Ê#Ï>É]>ÈÒ›>Ȉs>È,I>Ǻ>Çêá>ÇÔþ>Ç¥2>Ç×;>ȧ´>Èù»>Év“>Êb»>Ê`ú>Ê]¸>ɯˆ>ÈðÝ>È3T>ÇÒ >ÇV›>ÆÓF>Ç9Ñ>Ç>Ç }>Ç€.>ÈWù>Ƚ>É1ö>ɵü>ÉìÄ>ÉËó>ÉL>È£t>ÇÕõ>Ç2t>ÆÆ9>Æ– >Æ >ÅÒ]>ÅøÁ>Æu>Ç~>È4Ë>ÈÉÛ>ÉHÚ>ÉœË>É|l>É/Ä>ȯ>Ç/þ>Æam>Æ4>Æø>Æ–>Å;¿>ÄœË>ź·>Lj>ÇÞï>Èm¢>É¡>Éc>É=>Ȥ>Çöu>ÆÝƒ>ÅóL>Å|>ÅÝÍ>Å\à>Äè|>Äï>Å»>Ɔ>Çf+>È´>Éô>ÉlÕ>Èåï>È[À>Ǥ]>Çf>Æ4">Åao>ÄÕ8>ÄR>ÃÎl>Ä)A>ÅN4>Æ>^>ÆÇƒ>ÇК>ȹ³>É;€>ÈÚ¦>È}>È_¡>ǘ‰>ÆS>Å<5>Ä$¸>ÖV>ÃÓb>Ä`Ž>ʼnh>Æ >Æš>ÇÂ$>ÈÐ>É)ö>Èù€>È‹”>È:y>ÇkÇ>Æ{>Ŧ>Ãë…>Ä. >ćë>ÄôY>Åu`>Å®ã>ÇU>ÇðG>Ȫ,>É<þ>Éf…>É ¡>È—r>LJ>Æ­Í>Ū'>ĨÆ>ÅE¹>ÅŒŠ>Å >ÅÑ{>ÆH>Ç]—>Ç÷Ž>È­>ÉOº>É­ä>ÉpK>Éù>Çñ>ÇH">Æ“W>Åþó>ÆPr>ÆZê>Æ—&>Æä²>ÇFe>ÈB>Ç÷õ>Èu>>ÉX>Éá.>ɆÝ>ÉB>È~¿>ÈŒ>ÇŽp>Ç,j>Ç? >Æû,>ÇÄ>Çå•>ÈŠ>Èbs>È`>ÈÔÌ>Ɇ—>Êö>É®p>ÉZ»>Èæ->Èkm>È„Ì>ÈvË>È©>È>È6•>Èm>>È >ÈÞr>É a>É`h>ÉÄ>Ééä>É„É>É‹Ù>É=n>Èón>ɘ>Éx\>Éd|>ÈÚÛ>ÈÕ >ÈûS>É(Á>ÉW7>É}¸>É¥¾>ÉåÚ>Ê2Q>ÉÓŒ>ÉÛc>ÉÁ'>É¡?>ɸ>ÊU>ɱ…>ɃÍ>ÉwÍ>É=>Éš>É·>ÉÆ—>ɉF>É£‹>Êo°>Ê9>ÊR>ÉÊ„>É 0>Ƀž>ÉvF>Ér³>Ƀ%>ɼˆ>É©g>ÉËä>ÊÝ>Ê #>Ê&>Ê)æ>Éß÷>ÉÞä>ɰé>Én>É7Ä>É H>Èëè>Èàô>Èãû>Èù9>Éê>Ée&>Éáã>ɼ½>ÉÍ<>Ê1>É[–>ÉŠA>ÉW}>ÉÍ>ȺQ>ÈtQ>È4¯>È&€>È')>È(:>È;¾>ÈÅå>ÉJå>Ét¯>Éi>ÉÄ>>É—z>ÉU;>È÷>ȇm>È0>ÇÓ¤>Çy‹>Ç>¨>ÇY>Lj/>Ç…í>È{Õ>ÈÞg>É/l>Ét‡>ɾ¿>ÉQ}>ÈÞµ>È}>È`>ǨÓ>ÇUÒ>Çh>Æ}¡>Æ­´>ÇE›>ÇL9>È.u>Èn>È×,>ÉBw>ɘ>É6>ÈU>Çžœ>Ç"ü>Çå>Æù˜>Æ›¨>ÆoÇ>Æ"D>Æ?·>Ƨ†>Çå>Ç«Ì>Èwí>ÉV>Ém9>Éè>Èu>ÇÞ >ÇQ >Ç[>Æ»–>Åœ>Åy+>Å:*>ʼnœ>ÆA>ÆÜm>Ç}#>ÈM«>Éß>Ém>É >Ȇ¶>ÇåK>Ç`ñ>ÆàT>ÆMÒ>ÅžÇ>Å(->Ä£m>Å]Ü>Æwc>Ç2©>Ç~Ä>È/>ÈÞv>ÉA¹>É3>ÈŸu>Çé´>ÇTÇ>ÆÏþ>Æ%(>Æ ™>Å‚Ò>Å;>ʼn|>Æ>`>Æô'>Çb>È >ÈLJ>É5}>É3˜>ÈòC>È¢>Ǻ%>ÆÑ¾>ŧ‡>Å->Å“ç>Å«>Æf>Æ`ñ>Æ›‚>ÇIR>È,€>ÈÏÊ>ÉJ®>Ég2>É>È›>Çëö>Ç1`>ÆZ»>Åßr>Åéã>ÅúA>ÆæÆ>ÆÔ¬>Æê·>ǯÜ>ÈVn>ÈÞ¹>É\A>É’ù>ÈúÑ>È\#>È v>Ç­O>Ç~>ƵÙ>Ç">ÆÅ>Ǻ>Ç[O>Ǧ´>È# >Èxë>ÈèË>ÉlÙ>ÉË|>Éa&>Èôî>È—Ñ>È<…>È(c>È&>Dz>ÇK >Ç70>ǽ´>È4%>Ȥ>È̓>É!Ó>ÉŽz>Éý">ɪ“>ÉXÔ>ÈþÙ>ÈÝ>È´$>ÈIß>ǦÅ>ÇØ>È,>Èn5>Ƚ%>Èÿ±>É0>ÉR>ɳ.>Ê…>ÉÓ×>Éž>ÉT >ÉÅ>ÈÌ÷>ÈRµ>È-”>È$>ÈÑ>ɬ>É4Ã>É_Ž>ÉL6>ÉmÊ>ÉБ>ÊT=>Êb>ÉéI>É´Ð>É|b>É/I>È€#>Èü«>ɺ™>É“Ý>É‚{>Éšp>ɹz>ÉÁO>ÉÀ„>Éí?€?€?€„Ì?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€"?€?0A?€ A?€ ô?€]?€?€?€?€?€?€?€?€?€?€?€?€?€Uõ?€ @?V?‚{Š?€?€?€?€?€?€?€?€?€?€?€?€?€?‡íd?€Qr?€'r?€?€þ×?€?€?€?€’}?€?€?€?€?€?€?„=Ý?€œ%?€?€:G?€?ƒ?€O?€?€˜ó?€?þ?€?€?€"›?‚wq?€ ~?€?ˆÖ?€ˆ½?€+?€`è?€ŽF?‰?€?€?‚eç?6€?€?€?€?€?€?€?€?€™?€EK?€¬Ö?‚Ûé?†D?€\?€?€ƒ?€?€?€?€?€?€Ì?15?€Ÿ–?‚cp?€óà?.3?ƒ*b?‰—?€÷µ?€?€?€?€?€?€ »?€sÄ?€?€¶?€ô?‚ ?€uæ?€T®?€ì§?‚P?€ý?…õÈ?‚ÒN?€Dò?€?€?€“?€¹Ô?€Ñ?€Ñ?€Zq?€B ?€ãô?®Ó?† q?‰¬@?˜ä?7†?€·•?€a?€?€?€?€?€?€?€àæ?‚M%?€Y^?€0û?€ª9?‚)†?€12?€?€?€?€?€?„T¨?€8Õ?€?€?g?„tX?€k%?€?€?€OÎ?€­?€Û?€?€N(?€\g?€}?€$æ?€?€?€?€?€H?€?€?€?€?€ R?ƒo‹?€'?€[Q?€0`?€?€?€?€?€?€?€?€?€?€?€\9?Ê?€˜?Gã?€K?€?€?€?€?€?€?€?€?€?€?€?€ ‰?€´/?€?€?€?„7m?€(P?€?€?€?€?€?€?€?€?€?€?€F¯?€?€?€?€¸?€?€?€?‚!ï?€a?j?€F›?€?€?€?€?€?€,$?€?€?€?€?€?€?€³J?€? Š?‚Åv?€?€º?€?€˜?€?€?€?€?€?€?€?€?€?€?…@"?ƒ…”?€³?€}î?€J‰?€&j?€?€?€?€?€?€?€?€?‹Íÿ?€®?€?€ùò?€?€€±?€.?‚¿?€?€?€gŸ?„2ª?€7-?€?€?€?€ÁÝ?€?€5±?€bÞ?€?‚ö?€ßh?…Ò?€Fs?€?€?€8Û?€?€?€?‚ݾ?€?€?€VW?¸*?¾^?€üÿ?‚?°Ç?€?€?iÿ?ƒ8 ?€ ¼?€?€×Ò?€?€?€?€?€Ä•?€}ß?¯ñ?/?Œÿ?,ž?€G?€Ä?€8Z?€?€?€?€?zL?TÕ?€-ý?W:?‚¾¬?†ÇR?…õ?’Ç·?û¸?€4W?€/S?€?€?€?€?€:x?€»?€’/?…ØØ?…?8?€dY?€•F?˜Ù?‡c§?\ë?‚0>?…XÒ?£?€?€?€?€Nž?€'Á??„ø€?€}"?‡B?‹g®?„¯Ü?½“?‚]ô?€Þ?„¡?þ?€Š?€™"?€?€?€?€?€Hz?‰˜?€†?€XJ?‹[?”p&?¾§?€?€?€?€?€ŒŠ?€·?€?€?€?€§O?Œi?4á?€?€ ƒ?‚`?€6®?€Ì~?€?€+º?€BN?€N9?„¶ù?–”5?ƒ$r?ƒQÈ?‡X™?„w©?ˆ>Ï?…çq?†˜Ø?%è?€?€?€?€?€<Ì?‚'?”FË?‚@Ã?äÖ?ƒ8B?ƒ?‘½8?†yŒ?€ÐW?€U‰?€¯ä?€Fi?€r“?€?€?€?ƒaâ?€Ò?‰ ?Å3?6Ô?ˆ­?ºÃ ?…Š ?€óÄ?N(?ƒ#_?‰›ö?°N%?½Z?Š¢Ú?°?ñ?«Å?‚Êá?€Á¿?€>?€pÞ?‚-„?€û;?¼ö?Ž|”?…ëh?…*î?‰z‰?¹=²?ž„¤?ŒôÃ?„Éõ?‡1½?„šq?€¹ ?€[ ?€?€?€2„?ƒ8þ?ù‡”?‘¨-?‚A?„!å?•Vø?œeU?‡Ò?ÿ˜?ƒML?œ1@?ƒ³Ù?€ È?€?€?€?½î?ƒÉ>?‡$?”Ë?ƒàµ?‡Î¿?À?Ý?#ü?€?€¬?€?€Vq?€È?€•L?‚£9?† ?€%ø?€Ÿö?‚éq?€;?€/‘?š-&?†%?€ýê?€?€?€?€?€?1/?‚f?€@¤?€?€?F?T›?€'&?€?“½Ø?‚`??€1Ð?€?€?€?€?€?€?€ ?€?€˜Ú?WÉ?€ z?€?€?†,@?‚˜e?€?€?€$&?€?€?€?€?€?€?€D?€Ö?€?€ ©?€?†§ ?†+ž?€»?€?€-½?€?€?€Ú?€?€?€?€?€?€¸?‡?€[Q?€ÒÊ?zà?€?€KÅ?€?€?€?€?€?€?€“?€:6?€×?…*?ƒWh?€A?‚cñ?€æ?€?€?€—q?€f?€?€?€?€?…iÄ?‡˜?€?É¿?ƒ$ò?€{4?€êú?€?€?€?»ö?[?€?€?€?€?€]?€?€?€ ?ަw?„Û,?€0Ü?€?€?‚ª?€6?€?€ê?€ Ó?€?ˆG?€ýî?€?€?€?+ä?€Av?€—3?€›?€L²?€<£?€?mÁ?€s?€?€?€?€?€?€?€?€ 1?ƒ¶“? í?€,Ó?’æ?\Ý?€?€?€?€?€ž9?€?€?€?€–?€?€)ˆ?—+T?…ôó?€?ï?„"A,Hî@’F?c@'T?°æ‹?›æ?Н’?ÁQ?€ ?€ì?€?Ò?‚*ß?Þ?™q™?ŠÙE@A@Èå,@›qv@“T?Ž|?‘hÊ?„?€@j?D?„›b?ƒ®â?€È‚?€«?’~?‚Z?À_?‰™@©ó@A`?»“?¢”z?›‘?„·»?ŒÎ®?‚¸x?€-?‚rÜ?‚c_?IÇ?‚P?ˆå¸?‚Z&?‡BÍ? Ý²?ã(ñ?£1F?°cþ?Ã%ù?–pÌ?„M?€%Æ?€ ˆ?€s?P^?€À¼?€Ø«?‹|ü?‚}Ô?Š¿x?–ãˆ?—Æ?ˆY¸?†#r?Ç«?‚à?€ ?€R4?€ ‹?€?€?€?‡%Ë?‚"Y? ñ?‚â?ˆ`í?—Ƶ?Šòâ?Ûz?€Õ?€Ú?€3»?•¥‰?‚'î?€?€?€ ?„r"?€Õ?€gq?€w¯?’I)?œ×½?„ Ó?Û?€?€?€?„] ?€lt?€?€?€?€1Ø?€+z?€Â¶?€. ?„t?Š7Á?Y=?€¦O?€?€?€?€?€?€4Y?€?€?€S?‰N?/¬?€ {?{%?ãå?€U?€?€$Õ?€?€?€?€?‹¦?4§?€?€?€?€â?&Þ??€Ž0?€äp?€qÒ?ˆD\?€¸=?€?€?€?„"C?€³ ?€?€?€?€£?‡3œ?„RÉ?t?€Ë'?€?‚·?€%_?€?€?€?ƒÑ¡?€ÕÖ?€?€?€?€?†)?•Ÿ ?…kõ?‚“9?*l?‚IV?€= ?€ ?€øO?€Ö&?b?€9Ü?€?€?€?€ÏP?Œéf?šÄ‹?‰ÀP?‡8‰?ƒßî?ŒˆH?œ ?€#7?€G¿?€?€ë§?VÏ?€›1?€™?ƒ÷Ó?ƒö?•ؤ?“Á?¶«ˆ?ޝ ?ÛÒ?Ї†?Á?€¬?€?€?v2?…‘?œÙ?†^=?Ž—?‚ïæ@¡@û¥@’ßy?±Bª?VŸ?…Ýi? ?€+©?€nS?‚«Y?jç?ƒ#Û?ƒ½e?Œ*?»ò?ÀØ@A…U@chš@ê:J?¸æ?¨ ~?‘½?ƒëÀ?-?€?€T?¶•?‚€ž?‚ؤ?Œr¡?ök ?éÈ@?¯ÎAg6OB9Wï?ójÍ@6“d@ëÃ?¬W?†¦o?…:¹?‚!ï?€/ì?€Qä?}H?ˆø¤?—hŒ?–€¡@|¹Aa€)B¹%@Û?>?òÉ”?¸æ0?Š;Ó?‹.ÿ?C?„®À?€èï?€O?R ?ƒª0?±]A?‰ól?èûŽAŠÂß@m—@-o?ŠP?ˆÕ?‡M´?Œ9ë?‹ó‰?‚'Ô?€y™?€”?€ÖJ?ƒ8Á?‡Öû?±‹3@!§‡?Ó—?Ãh?&M?‚ ¬?„/É?ŠG?ƒÈã?€Š¢?€\¥?„ -?€´Â?‡"?ˆ‡?ƒ2?’°?÷‹2?°5ö?¯Ön?¨o‚?‚ø{?€D?€ò ?€óz?€´?€'¥?€Aª?€?€?€Ša?œL?’8?ªÈì?Ž,?©]?l?D©?€Ù?€??9?Ští?«\?€?€?€?€8™?€kÛ?„äT?ŠŸ?ÕËØ@ —A@d rA¢ùMD9 D¾(òB$­!@aׇ@!+ò?Ã5Ý?’¢V?‚,?€¯S?€[?[?…Ï?¸k?ψ?Ú dA}Ù™D‰UEFtòBEZü@ðȨ@*ÝÌ?»L¢?Uª?‚ÇH?ÕC?€b’?ƒ4 ?¦Gã? =ö?¦5?•çAIA«XÆAt @Ì7–?÷ŸÙ?˃Ë?¨­c?‰é;?…JŽ?„²?€^ù?„ÄV?Š5~?…„A?‹p5?ƒ2Ô@ w@³5ß@ب@`÷î?¡.C?‡•Ç?ƒ?·?†u¼?˜Â÷?„EÖ?€+’?€Vã?€^q?€@(?Jô?M?§¯H?éT@mù@;³??ƒï?‚üp?‹£??‹¿F?‚Ð?€?€ ?€ ?€?¯-?˜8&?›ù«?–mm?·Fw?¸Š?†Þï?…ÙÐ?›ãy?‡s³?ƒì?€î¢?€?€?€?€?€˜µ?‹žB?“QÏ?„1B?†ÖC?„»ð?Šv?…ª6?Ž ä?‚jE?€Wr?€7?€?€?€ $?aR?†§?‚Ï?ÎÜ?‚g?ƒÈœ? C?‚O§?×G?¼k?á?€Š?€?€<ñ?€?€?€Sû?‚vt?€hx?€`?€}–?Œ8??€?€|?…NM?€½5?€/‡?€ Ê?€?€?€„?€42?†E?‚ø?€‰ª?€ÌÐ?ƒ/{?€´Ï?€Wg?€!˜?€a?‚ Þ?í?˜x?€?€?€"¹?€9?€«ò?€´‘?€Þ ?ƒÔý?‘Kn?„ž‘?dw?G!?€&?€]¥?‚y?€ËÞ?€?€s?…JH?€®Â?€ëí?‚wÍ?…`O?ŠÓo?°Y]?” —?V ?‚î?€?€?zÈ?ƒË?€?€¡+?†ˆ&?‚¢?‚Sr?†Ýp?°ˆ­?§Ðý?ÂpB?­F˜?Œ®‚?Xµ?Lô?‚ @?„Õ?¤x›?€?€?€”ê?‚Æ?…Ó4?—¢é?úT@ R~@¯,@óp?²YÜ?ˆ[ê?ŠžÜ?”Ï`?…¬?†kú?€G?ia?‚$?†êÜ?ëƒ?‹!@#†£@ @or¸@" ?ïû?®<?¹Îa?­œí?ƒ…Ä?€sŸ?€‡«?ƒx8?”í?Œû‡?-t?ß Í@B™+AîõûBH½@d{ô?ócw?íù?·'L?”j?‹:X?£ ?‚ŠM?‡ƒ=?“ Ù?“™Â?ªb@†îA§é+D¸jE+EÄA¸s®@ñß@71E?Æ:?‰;B?ƒƒë?¾|?‹¥?„š?†µl?Žü÷?ñx”@Á†‡AàpvDê9E´·B_]ñA ó@ˆxQ?ÏŒ ?ˆ }?‰œR?š·b?€öI?‰?„ùí?øÛ?ÉÆª?Ôð@H!‚AÜ^ÓBZó@´z@t¯Z@?›¬ô?Š(?˜E?„Ö*?€<~?}á?‚ƒ ?…©X?žß?Üc“?ØÞ…@6Ýp@) 3?êDë?f>?âX?™é,?‡~?xZ?€¨ó?€Q?€6?€ «?ƒùÓ?Od?™Lb?¶©Ä@â@ç8?ÿýG?¨£È?œþ8?‰I^?…4 ?€å?€Jm?€?€ÃY?†4Ð?„ìÎ?ƒa¿?ÿÂ?‰Ú?ª± ?™oÙ?˜«l?µ?á"?.P?€îG?€4?€?€_!?€ñú?ƒ^J?‹Ør?„.¿?€ŒÇ?‡EÓ?ƒëB?ß-?‰@~?à>??û?€8l?€?€?€?ˆ9{?ƒïÛ?%Ç?ûú?÷m?€X?€?€?€?€Ì?€æ{?€ËÄ?€¶c?€Ãæ?jü?€±ì?‹›?L3?€³?€r?€Å9?€I?€?€?€?5v?€˜ú?‚š?„"¦?‚h­?ƒÆ?;ƒ?ªt?€ :?€2A?€T'?¸?€õ"?€´?€?€?€G?€¥?‚ˆ?ƒFÝ?š“ð?‡Ž?ƒ#è?ü-?€›?‚ú…?„ÞÛ?ÀC?Ÿ?€7?€?€?€?‚7«? ©}?‰ -?ˆ\ ?ˆí¹?˜Ÿˆ?†>r?€L–?L?ˆ?Wâ?ˆuû?€?€?€?€?„‰Ä?Ó(1?¤?áø?À?\?—)‹?Š ?¼5?§?€ãÉ?€Ÿ;?€G‡?€v‘?ƒÀ¼?6Ö?U??„•/?ˆ @=k¿@jñÀ?Ÿ¶?•2_?„2½?„J?‚L?Ø?…*A?€Ž?€nC?‚ e?…èm?†F½?—âE?™r¤@ƘìAÉ@iŽ?ëÙ-?ãâÅ?»\9?Žkò?‹Îç?œ v?ƒ´0?€¹o?Yý?„p®?“h¿?´ ¯@A'÷@”Š¡A¿`¦AÞëŸAå0@ Tò?‚I´?€.?™5?€:Ð? K?€ ;?€y?f¡?‚—ì?€»?€Øs?€åQ?‚q:?ª¾d@f#Ü?èºÆ?š:?œ`5?çÍ~?‘uõ?ƒ¼[?‹ž­?†ñF?íé?€?a?€Pç?€9Á?€â×?ŒØ?“0?Óé%?ŒÛ @~]@bü¬@]È@ˆ¤?ÑÑø?9 ?ƒHø?€or?€J?.n?Ï*?‰z?„2t?‡™?ÃëA@Ð @Ps@¼ôÙ@>ÊÂ?ƒ û?†ƒÈ?ËS?ƒ ?…ˆ$?ƒ• ?€y?1N?‚}?‰?±Uò?ž¯V@.‘ò?²òï?¿’ã?–%?£”.?…ܘ?Žpi?+?ƒ+Ü?€¿?€=ò?€q?€ê9?‚»Ç?Œ"?°@ ?‰î?¹Vº?“P$?„âï?ÌÆ†?–`?;ä?€:M?€W?€Ý?€²ü?„å¬? þ?„ §?K…?ƒ.¢?„>?ñ4¹?—~?€úí?›$4?†#—?äî?€’?€?€?€žƒ?†?ƒ4¶?€??‚Ǭ?‚‡?±r#?&à?€ßØ?„ú7?Ád?ƒ­×?€´?€?€?€š?€pJ?€;T?€?€£]?‡Îj?‡t‚?ƒy&?„R;?€b)?½7?‡C?á?€?€?€?€?€?€?€?€i?ƒDÆ?‹ìQ?+×?„c\?€C?€bË?µK?‚¿í?€HÏ?4,?€?€?€‰Ì?€~A?€?€?€¬7?â?€a•?MÂ?€ ³?€7?€F?ˆéD?Ñ?€_?€?€?€?€?€ Ô?€UK?€?€ ?€“?€R?€µ?ˆ÷Y?€ä?€?€?€?€?€?€íº?€=?€%z?€?€?Ý5?€q™?€c?€|?‰­Ó?€¯µ?ÃO?€à?€?€?€?€Ì?„¿T?€l-?€'‚?€![?ƒDh?€ß?€"ê?€Y?œfÆ?‚‰?€Áè?€?€?€?€?€?€\.?€?€é‚?‚]m?€{Å?…—®?€Ï0?‚g+?–ÞÅ?~™?€%?€?€?€?€?€?ÿƒ?QD?€Bí?€ßû?€x?–B?†ø?‚Ü:?…ÀT?€a*?ƒ?€]?€?€?Ï ?€È9?€@m?„œ¦?ƒÒ8?„£¿?‚8“?„/?¸yY?ŽS€?ƒ Œ?Œ ³?„Ó¿?€L?€¬?€Ú?€4L?€9S?€ß?‰û?…ð(?‘²?œQö?’î±?—?´Sã?_?€Ó#?è9?ŒI?È’?€?€9?€?€#g?€Ýë?ƒmþ?ë·?š÷¤?æb4?þ@[?ÿ4$?°·¡?¢Øã?”²?‚u$?€;Å?€)?€sµ?3A?‚Ûß?Œ?–ír?Â@•@>M?Öå¦@û?Ä›?ˆ´ø?“Ü÷?‡ ?€Û©?„ñ?ƒ Ú?€Õk?ˆ>&?“Ñ?ƒ^_?…ûE?Š)„@"5?šÞý?í–©?ŽtÒ?‰òÄ?b?ŒÁZ?‚K³?€9ô?€m?€#´?€K?Û¿?†B:?‰>»?ƒ(¨?ˆ=?œ?¢‰»?ƒV?“&k?‡›1?€¼%?€´]?€ ?€h?€$ø?€y?„[ç?´Ið?„8û?€2²?dz?¬Ýž?‹æ?€?ƒ5?ˆRˆ?‚H ? ·?€ ?„+H?ƒà?€ié?€?€?˜è?€HF?€±?Jw?ƒõq?‰ˆ‡?¹Z?­Í9?–©|?À 5?‰ï{?ƒšz?‚Í6?€]K?€^?€J?‚ç?‘Š“?ƒà–?€ ä?2Š?…¿?Ù¨?Œ¢?Š´Á?ƒÅó?‹”Î?„•e?æ?€?€?€?€R?€Ÿy?€N+?†'?…j§?5?’(?‡§³?“d¦?‡(_?…¬?‚N?€?†#?€˜û?€?€?€?€c?‰æˆ?€·i?…?”îÇ?ƒ´Á?ƒg?€qW?€Û?€£8?„£É?†z¸?€È?€?€?€?€?€5ç?€?‚’ ?¥Wš?ø÷?xA?€0ê?€?€#v?‚½? ???€?€?€?€?€?€?‡²ý?‚³Å?€?†¤?€Ö?€?€?€?€»í?€"?€?€?€_?€F?€Ñ?€T%?¾â?€h?€?„¿ü?€L5?€?€?€?€?€?€e?€?€æO?€<3?€?€?®£?€`:?€?€¯R?€Ê?€?€?€?€?€óö?‹ˆ'?€?€?€?€?€?€?€?€?€#?€2Á?€.?€?€?€¢¡?€‘›?‚Ý?€?€?€?€?€?€?€#º?€?€¡?€D°?€Xó?€?rÁ?ƒþ’?jã?€õ?€?€?€?€?€?€?êƒ?€?€¡?«#?†b}?€1:?€?€?€?€?€?€?€?€N^?€?€ ?…ø?€I?€˜É?€Ûz?……æ?€N¸?€?€?€?€?€$á?€ Y?€?€Œ?€,Ï?€Õ?€â?€²u?–ÚD?ƒ‡¢?„0?€Lu?€?€?€?€?€0b?€§ú?i“?‘¢+?†‰¹?êÐ?€/‚?€Kù?‚}œ?©ñ+?‚§£?€£)?€„è?€ k?€Þa?€z?€0í?€,Ý?€aë?ÿt?†Yà?…è&?ƒ‰–?¥˜?“ö?§Nj?†”ý?€¾ý?€K?€´Û?€uœ?€?€^ò?€¸æ?‚¿L?€ï?€‚?€{ß?‚\ï?Œz÷?€_?‹*?bÏ?€†—?ßM?€]ã?€?€'?€’—?€ A?€“?€V–?€x%?€Öû?ˆÀ?”&k?ˆÅ?‹Sô?ƒÝ?€?ƒµP?€vç?€™?€=Å?€àè?Y?€7Ð?€I?‚?‹ ”?ªZ?„ã?x]?ð?i²?™Xb?ƒã|?€?€?€?€?€?€"Þ?€Í$?€ìo?ƒ^›?ŸhÏ?ƒ±â?…<Ê?ƒBó?†£‡?•I?Íó?€?€?€?€?€?€Eú?€LÙ?€?€­‘?ˆŒ? l?‚UÕ?€Àî?€à?€‚…?€Wž?ƒ!•?‚Ô›?€+%?€?€?€?€?€?B?…+ ?€_?„ùŒ?‚Ìä?€?€?€%?,´?€Bò?€?€?€?€?€?º?‡G¨?€¡?€Õ?þñ?€Y¼?€?€?€?€9Æ?€?€?|¯?^?€?€"1?¨4?‚ á?€a{?€?€´M?€?€?€?€?€W©?€Rò?€?€IÂ?€6o?€?€?€?€.µ?¿æ?€?€›?€?€?€?€?€?8¬?‰?€?€?€?€?€?€?€?€?€?"Ð?€?€?€?€?€?€`?…‚/?€,£?€?€?€?€?€?€?€?€@¹?€ š?€?€?€a?€?€?”p?9?€?€?€?€?€$3?€»?€`?€è?4³?€ W?€?€?…£6?€ñ?›?€?€?€?€?€?€9? Ê?€Mƒ?€?ƒè>?Ñ÷?€?€?€?€?€x?€?€?€?€?€?€?ƒ´þ?„.?3¯?€ˆÅ?€p½?€?€?€?€?€!Í?ƒE?‰FF?ˆ±&?‚³Ç?€˜ê?€A?‚¾À?/Ã?ƒòˆ?€S;?u? ?€ ð?€jï?€Ž?€?€?€š?€;}?C?‚Y¾?‘êy?„™{?…IH?ƒ¨®?€z¬?€Š™?€C‚?€Uº?‚-å?€Wp?€?€?€q?€?€±?€2W?‚ny?‚Æ?˜T?ƒÌI?Ä0?€T?€ú?€j?€?€?€•?€|?„Xû?ƒ º?€f?€AL?‚ʪ?ƒ$O?‚0b?„§?ˆ}„?€À’?€4…?€?€?€?€ >?€?€ý?€â?¿B??+?„…›?#Ü?€ê·?€]?€xp?ƒ‡µ?…&y?€ï?€?€?€?€?€Z’?€o?€ ë?E?ƒî?„U?‡Æ§?²ü?c±?‚Ì`?%!?€?€?€?€?€{?‚[c?€"‰?€?€D>?„rW?6×?KÕ?€”l?€?€?ƒË¿?€°ß?€#?€?€?€?€?€?€?€ù¬?“Á?€g‡?€ ?Ž2"?O ?€?€V‚?€?€?€?€?€?€?€?€E—?ˆj;?€k?…èÝ?€B?€†/?€?€?€?€?€?€?€?€?€?€?€¤?pÜ?‚ /?ƒ¢ê?€1?€?€?€?€?€0ä?€w?€?€?€?€?€?€?€r?VV?€à˜?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?‚Ã'?€?€?€@ ?€?€?€?€‡?€?€?€?€?€?€?€?€?€5}?€?€?‚q—?€ ß?€?€?ƒe?€?€?€?€?€?€?€?€?€º?€?€B?€&«?€?€úo?€~?€?€?€?€?€?€?€?€%H?€"˜?€N?€ $?…ʯ?€3m?€?€?€?€?€?€?€?€?€?€?€:é?àh?‡XÃ?€Áu?„¼n?€$i?€?€?€?€?€ Á?€¢¨?€”?€€Ì?€r¶?5Ç?†ï>?€Éƒ?0f?€«?€XŸ?€?€?€Là?€?€?€?€?€(?‚1Ž?„2J?‚&z?›g?,ç?€…“?€/¥?€ 8?€?€A.?‡ ?L?€?€?€?€?€?€Ö?€þ–?€Óô?þ[?€çó?…p^?‚õö?€2ª?€?€í?€?€?€?€ -?€àÀ?€>è?€V%?Œ ?ƒ²?€®Ú?Uì?†d?*?€?€?€?€?€?€c¢?‚- ?€D„?€dï?dõ?D?€ºj?€BL?€„”?€_´?€ö?‚ޤ?€HO?€?€?€?€ß?m†?€ý?€?€?€q‡?&3?žH?‹ë?®w?€?§?‚4?€è?€?€?€?€?€:?€?€?€?€kú?†×F?€™â?€ø¹?€?€é?„£C?€Uä?€?€?€?€?€?€?€?~?„QE?€»–?€?‚êí?€5j?€?€›%?€?€?€?€?€?€?€?T?»ª? )?€B?€?€h?€?€?€?€?€?€?€?€?€?€?€?ŒÅƒ?€Ô?€ Ü?€?€?€?€?€?€?€?€?€?€?€?€?€?€¢®?€ª¸?€?ƒµ}?€*ÿ?€?€?€?€?€?€D'!DU•Dš áBnhDs#7CHaDŒÚD‚|B¡WeB×ÈC‚ˆÒDiºþD‚$D0•]Cp¼‰DW¥þC®ÄD›Ë»Cg3úD.‡CAÄdD-’îDœRVD[/´Cœ7‚DkØ™B^$BqR`DF9QD¯ü•D#\‡Cå¥C騦D[z@t2@É6ÌC/›ÐC›èDØËÂD-,D¢>–D !ÛD«ˆCБDfëB÷“÷D‡1KB‰ºSD'yD%s|C’IžCˆãxDÐ0C¸DHT¨D>´èBîojCoNÐC4ÃqDÝ‚6B·+ÃD`utD¢øµBèmC«ÉåCfDiaB¦ŽEçàDº—@ÃéZA]0à@ÿ´'D S@DÉc¥A…„SBÿA£›Bç4Cx,ÏE!J—BMƒ=C%ñCtašD/uíBD:C§üøE´ÑC­ óDnr.A•A,C§ÔBà DBï6OBÎhÌD¬¡mC ô©B`tgBQ1sDÑÚkB¡Æ²C{ÊÙE ?C• ÒA¶;bA>(Dß[D¸5ÇCŸ%@¹E-CØ`CïÐsCÔšdD±kóD^=Cw,D[Dp¡ðCõ¢CV§ZD" Dû?D· `A,ÊÙD˜ù§Dü7hD´—ôDÑòéDZvWC.#æBŸE ¶µ!D¢HHD©€^D—­BÝŽ©CéøEDlB'·}DÂÂaDŒXÇC‰R¿B7âæD|ÚxD£«¼C¶@Ý¡CR‰D*²GCìÁ$Do)CÔ D#+0Am`ËDç÷$C2,SCsg‚D«ònE"d4CXBTq>C¯,SAÊBžD•í?AÆôC›w&AÝØÍCþ|èDžU‰E4ÛCšñºE yB¯ŽØD‚Ô¥BšAH<©C"ÞÏDafíCó¦ŽC²ÄD`åCÄÎ E5š;CÒ’B£1E} D¤mBäŽôEV/E6²•E0mE41MDÒlD@²àCéCDiCD›¢4C!Ç{D(eDä9kD¸.cE nåD½µ.D¦kÂDi„ªDU–jD€a™B°Ä‡C;"ÿCåJE2BçôDl÷,DË _CÞ“`D²-|D×:B£é·B @Oº_Dñ_A’ÆB–§…BA¶C¦è{D58¯E wnDœ D×'D<šB³4CA…â¿D0šC‰ÞÄA‘ÅëC¦×DׇB9¶5BÓµD"•D„ÞE‚½Cù¨C$2œD-bCˆ«–CDùEE/ýtDYÜ¢A½÷"D00TC!aCú¢NESÎCÊ“7CÔêDñéjBå´·AÊ\ÛCÈ9C\¥ôCâ.oD«LfD@"E‰DR-‹AãÿB†ÇD‚|BBÕ1C³QD’ÔgD7~†Aí¸uDˆ+BùíC¿ô¤E FŠDÙÅGDi.pC.õåERÑA³¸’DRþ‡E,qOD D3ó¤B¥+@C†×D7œÀC²„@&§¢CL0?B ¹’Cx±E.gÝE D‚C ‚ŽD_éùD0«FD.¦D\o&C)BMDIêCX—«CÐÝD³‚AÄ+D˜ãC`†ŒCGZDZVÆDº×úD—:«DË?¦CYBÇ•pC{µóD¿R¬CêÃàC|ÃhB6CéMDÕÜšB°æcD€¶D¡DDBÒ=§DÍTCûYC'D}X¹D—óWC¶‰DÓ.ûC/ÿD‰HDMC©-¬CåŽTE|¬CBCõöˆDDøCƒ"íDSÈEQoD×Ý›DsãBË@²CäÏD,3€CαD "XE½°D™!D—=ÐDáYyCãðmD,¢E ´±C3×–AµäB¬‹DÊ#³CacŠCÁ7ºCÒ GCû?D®,äD‘0DJU¢CÎPÈA(ìŒEY™zCþ¬D³w»C<¯$CƒËC’ˆúE¼E­àDLvQCñ¯Aö‚DÚ†¼C=³4CFD D…ãCÃUD?qD·pC°,5A‘eC«?mC¨¬'D÷g,C¬ëšB¨¬=Bƒ†ùDêrÛD„ùëC9â¤D>T9CÆ&÷BþNAÙ²øAžcDõÒD‘yPCИDo¦£DåiDˆ-yCÊhçCËùDƒÚD9©D’ÃZDT¤ÒC˜ÈD+V\C‚ëD&®0D>ž©EmCA ÛûC³>¾D ±&C¹€?D3UÈC?@¼Bä¢Daˆ-BÏ;D`˜ÒD˜õDE»¾D¶íaA£²±EʯB?‘êA§„•C™ÏD ñ†EåSCf® D¿®Bˆä`B±Á0C;€V@Ó¶@„!ÚC‘CüD5<4DLìlCJ‹D/9D*VþCˆûAE …/B&YCåélD£FAC‚[cC· FDR?wAø BuhkE!l#CukõC•“ûD±èbD£­ùCš*èDGVRCñ,“?ŒØáCïD kD·>C­©DJ`BÉ’Dv>ÖDæROD/¥ODÏŸE*oD1¦C %sC¾ÅCáÎDžJØDçoËCöÊB“ ·D7þD„ÅCÉ £D-Œ¾DAGCÀ#ÌBJñAnÆ"CHðD®*D½ÑC²QDénÝC˜ îE Ï^CÍæoD£ DEiD¤YD^U|Dâ(ÒDÐB×ÉŽCеCÚ“¸BŒ DÏ8®E½”C[æDßšE¥pAa¸Aé¦yD>ÞDÄû C¤Ë–Cþ8ïB€˜bDQæDœÞ>EOAD©h×D'æCØËD¥¦ïC(ÝBtAšÓüBhYŒBÀ™;DÕ¯D­”C¯tbC¸šqBcÉDÞÀ?AFäêCc ®B(¼yDMD<E%HCÂy­Bw>ÁA,ÏZCº žE%PA´½Dc¯Bä2@¹ÄGD•zC°†§D#–C§ÙCöK@ÒqóD¨Å¯C^¸¯D¾r¥Cás_E•HDÀ1`D€8JD¼ùèCùBôD¾DÀ,CRpC3:­D½É C0w D¤WDp‹{DB@D¦.DsßkEq@¾¨hC Ö)C£Ï7DïDuD±Ñ‡C¹ëÅEEoEDDDj¨D/B×~„DÍ[4Aã»BãíC‡›DyDÁcEDm€¹DxìuB°éñC:ICÆYUCŒbIBë­>C“CE,Ì-D½7oCC±"D;©~CÓ[9B{—ÖC´k@™@ºC:€¶Dü°CÇA·DâD³;âBgœèC°ÛDK"ÄCÔH®E/«mCœ xAâ/D`ÈëDòuADäàìD¯‚ùC›+úB¦Š[BLÕØE£lDÕ,/AµÅ7D8}nD¯Þ^DÇJD†#ÐD%õîBò”¶CUDðâÏD2$DÑ߃CÍ=DÖ¹D‹DúRõDÄDÇGlDE þD¥X¨C">íDÁ ’DÁ;D‡UÃDZ5äD äD9EbEœBâ=!Cž¼åD´fC™rDŽÓyEbä"E K‹B€Ä~D/ÁDØzGDIJ¼Bvì(A×âyCä„Aü(ËDø÷¦DÃk"D5ÞYD¿[³E4šµBÆ_C-ö}E'¶îDqÃD°JÍC÷\A:h CELÊD¦¼dEî@D—Ú$D&AExWCÒýB2DàDËÞQB)–8Có‘\BÞT×DÛI×@~‚TDpƒuC¶;|Dˆe DËâqC'*dB¸ C¸DÁVØ@[\rD[kCæ^5D¤Z#DÔÄB¶ƒCË~CðnE*;CDä,B`âA…†C¥õmD’²±CQSCô¡òBÈæ CéÖ C¯èbBÚ]ÔD·k¨BšêA©ÿ§CÜ.aD5ìêDz”jE'xƒDˆ;™E ç]Cæ± B½s)CÑD…TDBóðžD×ËBD8ŒµA‰©WD ”üEn8D/lµD¹™jD¹0^DcCCŒ}“Eo¹ÜB Á‚Cª9Ý@êKDÈ|E3yEqîD÷žzE¿~DÑC¶{ÛC=DÒ¬AþUùD’™ñCîõnDAH[CÈÒßD¿O´A»†AñøEC…a*C<ÍTC›#~C5VDök‹E<Œ7BÞÎAö­D )õEy«B”—CÉD{Ñ¿DÐü©D¿M1D\@Dݾë@×/ÉDà{DjOD¸ÚB¤z D\?¢EîçCøÑ`D‘ÑD÷×CDÂD ›DœpVCëy\C¤ßE³öDø` BQ D>ˆÒE BèæmCØžÇCµö…EúCõJ¯DµDë¢D›jwD,nE!ôuC—ÄþC×_9DuBáJ{DÓ?ÚCð‹FD8.D‘oÍCž®ECµÁC ï<@i™–D è DߦCCÇDŒ¾—D÷’æEìE>ŠuC©4„EGÔD„õD¶²>CÅ~_?þôæBé'ËDöÇ)CQ ãC/3(C¬>ÎDÐ'ÛDDžSLE*D‡ˆÖC=¦ËDÏC§OBIRHDUDÊ´‡D„r2CQ 1BW¢~D5¬¤CÑäVD‰ð1C8‹…BXk¦Bá&ÆAσD¿ÌhD”¢¼BÀ¬`CˆšADzõC2:CÍED—nŽD·JïB…¿CbæØ?ÃC X>B×øôDSVEüÁDÐY@DZÓDç uCCý>ãºpD£RD‰õóCó$bCTÈŽBáË&C†´DÙÎC¨ëcA¹=DûõC¹RDE¶C-ªÿDYàDFC ìrB|žX@¥ÀBb6³Af11D\MàD%ºeD¶ÂÄA6}ÇCI]C%îôB /@Ó&ØD1E"7Dˆ©¸CçÃÊC¯+6?p-bD¢…D¿nŒCItDU7‚CôâpAKÝC+=C\J‰E6±CÏ•B´k–CADÏ^ùD l’C˜VEnA±tóE?Ì—D þDÛRæD}IOD…ÎD'T¶>Ëõ[CuˆÔ@\D_{|CÒo´C¢\£D/ ‹C’ÅCó ÌC¿ŠH@½‘MCsLD¸·iCq”DoRD•ìþDilC-öD®È EäRC¶|{D}.?EACC<~îCí¸C6èüCûCøTiD,BLC“oFC÷;BDoE"ÊD6–\D¶ÁB…ß[C7ÃB¢ñBâNCªCD€x…D"a8E'ùÀD@<ØCÞDhœsDï‹?D _‹Ds@DCºŠFD£‹ DÂUA’°ÌC“é^E6C ÿ¢Da_úD(íøCÍŠ–B„ \DjkEC÷D¥@ C(BBYÞ/Dð˜eD"Ó÷E'(C WE}DÿϺD,B°BÁÍ D9‡DdýÁDü•ØDOA¸¨ODKë Bž0D “:Cè3"DŒí[DÆú%D×rÙDñ!ŒCÞ=KCÁ§ÎD@pDÙú›EPŒD "àE@CÝ\æB¤E†DóECDJÐ.C»0ñB¬†ˆC¸ÌyC6CKa¿EÖÆCÊåàCØ|íCzßBçÚDhpWCÉ~°D“sBÊzzBB`C›½aC‘^$AçÀÂBœÆgBDÐèDB9iD"¸ØDÁ*TCÀK™B¤z[B’DØ»ßC»}xD4/JD°oB“7ôCªK¸Ef\ÉC2ÒCuXD-»¢D4ÍBøBú§ÔDÀX?€€ñC·ÊíC™‚ÄE+£9DGv«B¤-rC¾YŒA–9çDc7fE-ŽD—MSC”ÀCÓè½Do,%Dô×Du•ãE8?ËCiYCpÃÃB9²CèïýD'3¦C,ûêDEÿÂD}…hC’ÆÚDD±¢ÍDgÇCº…ŠCÀuÏC$?AM«àB4r‰CŠxÑD3¡ÏDâ*D‘½DŽssCGôŠCÐÉC(ÛD—a9C™ hC×ËûC›#ëBm¡ÓC§`CD1¥Dž3GDWDOxzDêCÇ2”CªøBC‚òDÚ=CÎÇ D·©²DOÊ5Cá“BI‹ÂBYPúDÏ™¼Di¤RC!ÑC<Ü‘D%C ª0Dò(CA†B~b›A»,DC:öDg¾D9kñCÑ{GC”ÓáD‰YE [°C#B¦ÎC¯2ÐD»)öC(„aDºÔnDç•dD0¢eDš˜D¬ÈzBµ»BC/ÍDÔXD•SiB/¶}CçNÖDÓÀ‡CŠADY×mEŒ@Còl³D4çèB%ÐCMÊãDããWD“‰˜E Û×D¨ÓDzóDÞLD{ƒLBŠ}Dk²D!)þC@3!Eš0D²^eDR€=C°DÝ‚iDsDÔ åDŸ³¶B€ŒBš”ßCµí0Dæí‘BFD'«]DââD>?qBN²|C0ˆDD‘*Cÿ†ÉBÈ›•C5·t@]ÑCÉ`wCC DGqÑCT‡A±È•DV#ÄC¼“—B ÈûB€né?º D“[…DT6rC¹Y™Bä’`D"M9EO˾D¯Bo˜JDŒbDNCD‰Ð¢DÀ¨ÇDž×§A¥ÕBC@¼*BÞƒHDJMBú‡Dr|­DнŽBfCÞEBÁ<$Bt´xD3nC™+•E  C:ĬC+ $C—À´D\gC²å8CäKØCà'ñCÞ”E8ÇDÐ Dn‘}CÛ*sCø¥D˜*C!'DÞÏãD¬%õD*h DˆEEKEdDhC§3DCš·Cµ  CŸƒC¯«„C”ާD ' DQÊ+Dä2øD‡ó?Ö©B·«JE~;D‹†CÜCB´BñþÛBÕ7ŒD)_CëêÚDÒÃtAƒuÎDeŸ™E58£D9m¸C'D·D…¡€BA¦?DÜCn;·BêD_%|DÇÖÞDwz„D9ò÷CìÈ DÞ‰¨B¡>jD “C…ãB’>bD©x‰E,——C½l›A¦cA6ù´E"#Dhú/AÞ¦¡B¡˜v@x$£DŽ8ÞE5ÚéC°=8D8xDP6DNË6E@eCÊKB“dqCUòæC\ïìD¸ºWCš&C‘'®AƒB/€”Di‰Dµ0ÿD¬ÎµDŸ(ÐDÑ#D?6pC(Ù¡Dƒß Dª+XB1µC›ýïC ^ÅCr“[C„ŽëD`(D¦¦CÞªVD¶YKDO2ùBþ,k@W< Cf=ØBÊÁ—D#íbD3(òD€ŽDXXÓCé:1B >¥DlK–D“µ@B)TD aÔ@¥“¨@¡¥D‰LBÖeJAEK¤BŒbwAݦDE?®XBƒ±Â@óMXB\ã CzóCS9RDÒ=xDHSñCø'ËA§{C D ÒC„ÌcDšéD‚×¥CÙe—CÕtJD£{DE,ÚB²\Cì?pD‡»4EóÜDŠ0òCÔ—dCÕÅDSB2­ A%Ó³D<íADm‰CðËUC)ºjBÊXJC®SãBY¦­A§jiDû)œDŠD›šÎCî´²D DYKyC™*BK”IB>ˆ¼CøÕ¸C…DE,¦B²^RC—«xD£¡wD™%<CÏÏ;D°´<C(YnE;C¾ú4D­cCg÷D'µ}AÓdC$?LC’Y*DÐD«££C=_DDD ¶D÷éDÞ õC„àD{³{CYÞÛDoGBbc@IVÝAK˜DÈBù¢C497D÷æDDÓÙC×:aAWÖCslD“,|Cú+žD·€jCÁò€B±zíAÏšCi“C~¹çB !÷Ea1D“í DŸÿßC¦’†D€¯D,#lE&TÃCE/4BíCªDà5D •BÛHAÊTãA@ÊÏDBªWAÄbóD6ÞHC®?BÆhD»V¢Cð>Câ@NDÌ DÜ÷’C0Ú‚C{ugCøæCÛâDâíbBÔY"C¿{—CõzC,ÅeD+¤D©å BÍ®GCÈ D]0ÄC›èüA¢ó}A˜‚h@ã³?‘°íBk*xD‹1HD“`ƒDùŸCõ**D¤úŽD&¾ÄCˆý«D³z)D"–kC’^DžU-BŠá@B ]ëCˆÓD“.*C`¨‡CýÿvD±…ÀC¼4BCKDµI¬C6BcC‡ÛÃB]DµC.B5‚D0²—DÏ‘{Cß»¢Da™ÚCƒëÚD`–A‡E3DPð¹E%DED<ÞBÉNæAÔÄyB0DñAN&C…çØE*tA= þB¬äòA¶¼tD¤"Cþôx?èynÙÝTV?çÖÔEèR?ç¥àÏæ ?çÈqïò?ç²ûâ?}Á?ç $:É?çãݾD]?篌ŒmÊ?ç—4Eö;o?ç1xŽùmÜ?èŸoÙ $?è)Ë‚Ëq?ç©”ÃJ?ç( ë»Ê&?è}08²æÍ?è$xšwýÌ?ç¤Ýe2Ž?ç 5Úž?ètÄ.bX?çÌïêBÆ_?ç?ÆÆÏ,Ò?è¡ó&fê=?èWve30“?èC©‡…?è5¢l]`º?çúAõç¾Ø?çnûz?p?è“£`ht:?çÿ](Ü ?çîûäz€9?è²1äŒÔ?çÙ ØL5?èK‡;ôµ-?çn¯ õú?çøx‘á·?èxm/æ¶?çŠcü¶hä?褛+·á?èš c\¯º?çŒóN°O?èQM_:?èÆZ,(?ç“mJrÀ?è ¨)»K?è´ÜX¿È?çÄ:€.±@?èGx®™u?çntyX?èBW9Û"&?çDŸS3ƒt?è7´Ã¶£t?ç£i}Òú?èÀ7%Χ¿?è>W£UìQ?çíèHnÈ?çàa2qw?ç}fê|v?èÁÞ ¼&?çý«2©8?ç2³7 ¥e?è˜ð¨§?èý_rMž2?çâΊE?èP?oI?ç[:-Q=?èá–¿r?èTž úµw?ç:å((?çÓîXH4Ä?èí&ÚÂb?èq';¬-?çP™™:5Í?çÚÒ|°Íˆ?è%ŸÒg ×?èJeÿÔ?ç*VJ -¯?èH`mо?èóÏgø'?ç­‰"“™²?è!¡PØ(a?ç;Ñ#Ó?çϱˆ†{?è8®Ñ£Y?çâfE.ëj?èa‚iæ?çtˆ…ˆ/¶?è=i—Ìp?çUÀ€.lq?èL;-¥ò~?ç¼òC85?ç~JÛWü?è‹s«-NÑ?è5ôG­‰?çÔ–š•ÒJ?èéßÈ8ˆ–?çñÊî@œ_?ç5dÊk¬?çöã P’?ènEùü™?ç“K% n?èª÷b*?èn»BR›?çGË=©€?ç-·sOƒ§?èµ Zùb\?è²rYå?çk{_á?èq’±DÇ?çà²à?è³Þì•?çŠî½¥¸?臘Éswx?ç “@ˆ‘?çþ é/+¹?èlü§†˜Ù?çs—§ý·­?èÞ \Ç?èÑ®ÆÙ‰V?çôÈ7f^o?èÏr]å;?è«eÏ3?ç•Òoá˜?çƒÛ'ã?èo9áýTU?çÔHFSOÖ?çmF fZ?è¨j¡Ý­?èãšæüÊ?çØØR@g?è?>þZb5?ç$îÿª q?ç嵡Ìvã?è$ÅÛ“kz?èpͼc¿?çHQ-ˆð?èþYp`?çŒ[Á–?èfØ;=nŸ?ç&Ku²†?ç’Ç/‡LÀ?èƒâ«€å?èÜÂzí?ç›2À©Ç?è¾4‚?ç“5n§G?èÌĹ=Ñ?çšN^U4Î?èžÍyÍÌ?çl+«ä6?çË?>ú?è/€¦!P?è¶JB\Û?çÅRYl?èT“ €?父JœÖ?è–É S?èw$3ç?白~½Åÿ?èÊÅì8Ê?èšôÁ5?çMȪt³³?èÙ<*;?èèàDŒ× ?ç͵ýv?èCÕ7ÛÅ?èòà?(‘±?ç —Ų0?çñ5&Dì\?è)­.°Ñ?è°¡ l?ç‰Yô†È?è®1‰J?çL9I›7K?è‘ÿâ4‚?çGt¾c Æ?è~Åök Ä?ç †¢ª?瘿ÓV"I?è€# rI?èéïpd$3?ç‹Ày)…?èÉ$Ý5—?癌½ÿ=?èŽ Ä –Ñ?ç„øèï^?çá¢í'Ü»?è²ü€?èn~"ç"ø?çN7<0-é?çø8êT§?èVФ Ôd?ç|¥VÄò|?è·®}?çø­¶?è@¸ »;Ý?燨hÇÿæ?èÞ§9©·!?èU2cSÓ?çÅ.çð³>?èݼësd?çõªJ¸h?è¡[8R¦s?ç® kó_s?è;¸k¨A?èòeÚYK?çµ(@p-Œ?èÂB¦y?è]íô¼?ç9.xU’¢?èÓŸî[?ç–u}i¼Ò?èËaÔÔûÖ?è¬8&â Y?è´³ÍoÓm?çKM]1?èÂÕ*É=v?ç“ EœN?èy¬´ )?çOà0 ?çÝå¨Tð&?èÌì¨?èœM^Hò?狊£R?è ÇçöÚÊ?èµþhÄ?çÈuÔV?è{’\¯'?çÖ}ü4ä?èÒ‡‚e‘Ð?èOOºtf©?è„%ÈU”?çuÃ#‡ý?茩ŒjC?çÐ[[Ï?è•Ûmkë?ç§biÔ¼­?è\ëHÛ„?çQèH(?èÜŒxA?èbëÁF¶?çGÛ¢é“?è塌 Ú?è5%•Ãý?炘‘k ?èpAÕ²R?ç;fŽy“?èàš7v•ƒ?ç“6o€†?èŸ<Ò¯V'?ç~½öKøv?ès%ì¬.?çQåh?çô5”Ò?èWy9„éµ?ç0‡ÆÙŸ…?çÿÇ{°Ê?èŽ]@¡{Ø?ç¾;)gW?èjýD5…?çÇ]xRMt?ç@%qYw?è•1}ï‘A?èVê··Ú(?çÛZV©• ?çX”1"…?èTÇ$¬.ç?ç£g)öåL?èEÖçɼw?çb«k¡(?çì^¡Kx9?耦l”?ç øèf?è „‚"ñû?ègát)ã?çv!²¶i?çÜÁé/ê+?è9«95Fª?è³]e­®?ç¢æM±ÜQ?è0Ø”Ê?è)zûƒ?è–AsvŽD?çƒ'ãÞ# ?çÖÔ<¨?è+Ù•ù?èpˆL9?çL˜ÌÆýQ?çÒ1/¤‰?è2îÜÝ?è‘§it³?çˆkwYä÷?èN¯Êa`?èZ;ä??çrûÎeNc?çÿ5ã}4?è¦1FSï|?ç½v­Öd…?èm¾ù^?ç³?èÄžku(“?èåãaZ?ç·Šml,?çƒåYøl?ç-Ú&ÛÔ–?è•¶–~{?è6''q‰?çlä}<˜(?èHÕªpf?ç 2qÿ¶'?èZwŸt94?çu²ª È?è"¡èÒ†?èÞý þ!6?ç®îÝÐŒs?èF.õÓvÈ?ç—л@‡?çä…×¼¥?è41–r?çj]°·E?çàÉQ1e?è=5N¿‚8?ç5¨Ó ?çÛ_3hÓ?èXHm–¶?çrº 6GÐ?è"o0¿õ?è±,\T-?çÆÑEþ-5?èŽàæÝƒ©?çÐ'àï>?è¶6M£Ÿ‡?èÈWѸ³?ç†ÕêPJû?ç=i ¬E»?èìJ]-1"?è¿ÎÇújÝ?èDccÅXŠ?çÌ –¥þ?çÚ„ÏŠU?èÄxƒ–?çxãy€ç?è2`ÿwrd?ç†F¸Êç ?è7¤o#ù¢?çN ½h°L?èùb`Z?çû*y?¡™?è‘bè)HÙ?çËk4¢Í ?èr‚gL]Z?çÄ”Óã­Z?ègF§o»r?çËÖÔG?èéP"­¯?èWšFà(?祛W©¼?ç#!ïìñA?èÀµÛ’.?èáÇnÀwÃ?ççΕ€?ç (àöÁÛ?èÚSw®ö~?è^§¯¨2?çúq=çîq?çHÍÁ9Ù?èÒÝàî?çÚk8²_Œ?èØVÅÛMD?èœøMì¿?ç_xl83²?èHº¸ ¹7?ç­ÊñÄ>?花wéN?è0 *sÃ?çGÍ*‰aý?èø \°|?è7=Ktˆ!?çö°+ëâ?ç½h}½Îµ?çÒ5+p?è.mS ?èrékÀ?è) Ò½Ð?çéĽ:bä?ç¥B°5rÔ?èb’'’?çÚ¾Ç@?çIþ+Ñ.Ú?è6´Sý?çîf,Yüp?çÆYœ˜4^?ç¦5 r€ƒ?ç°-g–‰?èói[Y?è`Œéæ>(?çÚÅ9ÎÌ?ècä©ÜL?è`†[¶s]?èÈuuN?è(ʸ¹]?èdj:@ ?è`«ŸB ?èbt›èáú?è`¸È‘9?çÛ«ï”ÙÆ?èEÀ¦§è?èg»(B˜?çüv´N?èRåéG?èr/2Üiû?çX“©s?çpßUð?ç;ûöP1™?çGio&™?çpNh¶V?ç·@ØDî?窇ß~«?ç¤tC¦5k?çâHÓé´’?çþE:6zM?è"d {ƒ?èûo°˜’?è5Í^o?èc:äå?èx3V–_Ð?耱ÿ¾kÖ?è–0ë+c?è­wµÍ‰µ?èÚÂÎXò?çZ$/âï?ç*êØä¸?çTúE9o8?çx†JbUÝ?çl¬sÅ,­?ç…7¦Á¹u?ç°ü5Më?ç¹DÒ†¢´?çÓØT x?çëÅážõ¦?çüA'_½?è!ªÀÄ?è"Èl…?è38ÔK?èAã=žh%?èDÞÜS?èjØlÑ“?è}6?[?蘈ÀÂÁú?è¹µF à?èàKÁ‹?èýJ5”hÜ?ç1ds ¦?ç8XÈM Á?çFüN#>Œ?çÆ“#°?çi­±aeÒ?ç•‘ÐÔ=?ç¼þ6ÚO?ç¥? ¢?çÓ€¹lBP?çÞZƒg²¨?çáÌ!‡R‰?çëh zÇ?è ™µì?èïn®?èÅ®tÜ?è45'ÀÚ8?è?Šég?èJ“úþÞ?èSqWÐ&á?肈ûèQþ?ègèÌØŒ??èŠÐéêÁ?è°rbÅñ?è¿€}3?èÐ$Hó–?èî ›×?ç˜wÔq?çA¤bÔG?ça(•6NÁ?çQ­CË?çpPt·?ç˜ÏÝl?爣(òÌ?ç¾=šKî?çªyÀÒ.?çË=¯‡?çÕQ½ã?çÕ!WÄUg?è7 § I?çæ¬€ð¾z?çò6 •Ʀ?èSËïCF?è DwÝ|6?è°ß…)m?è1ºÞüë?è>×0?è*ÍN¼œ?èQ¢–Úш?èI›z¦„?èu¤RYoû?èg Eè­§?脯³äL?è”œÝ Ï„?è½i Mü?èÃ8|ˆ?èËPˆ]©?èóa«ž=Œ?çÿ¸6u?ç+Z•‡ÜB?ç%àÂ3Ôk?ç`ûÂϤ?çt{íŠ&Ï?çà†2¶„?çd¹%¿t?ç4<ñA?ç–Æø´?çµ–&'Ãö?çà}ÉE?ç«4ÅXÐj?çÚ»(¯?ç×H"ÖÉ?çÛæ2{Ã?çÙT¦Œ?çã÷^Õ@?çæÏ–éô?çýÜYTIÇ?çø¢¿Þû?語ë5?èYÒÐ?è ,:Ñ¿?è4o¿_[?è==M¾?è`Ö2-^?èQ0ºH˜¬?èa³35é?èqîPdc?èAq8ˆÔ?è˜"¨°¯Ú?è”9ó¶x§?è¦ÈЉ?èÁÏ86þ?èÓñüûÕ?èñû/œ?èþ éâo˜?çÎqaå•?ç/QN Å?ç(²’Nš?çJ„}~8Æ?瀶ʫ$?çyÅ^p[?çfÈïR:ì?眼ëT½A?ç‰GÇjÃÏ?çC˜¨??çÂtLå?ç˾¡ƒþ?çÜÖ¯}ý[?çìŒ3_ß?èmPùG ?è]ä@è?è>³={´?è;¶ÁŠí?èZÒ½U|?èJjm?Ñ?èk¦!ùjA?èdP¦G?çë_4k_?çéÒ“  ?èùKR%c?è*6Ð$i?è²b‡?è5Ø"©t?è5¾&´_?èC=Jv..?èG }!=”?èiÄíš³s?è|ÚņÇX?è~Wgpà?蔋A¸¸?èœï×ÐÒ~?è–ɹۉC?è“V‚I?轑ó¼º?è¯OYÆ›ª?èà1 9œd?èò*$Å µ?èëìão î?ç ¾I×%?çAò†sÌ¥?ç?‡Ý¾È?çQÀ¶óg?çQy䊔µ?çzŸa'š?çlRª÷ ?çvi“=™‡?矶\G2Ý?çŽwx¸æ¾?ç™ÕÖmÊ?ç¼@ûkm?ç´äd^y€?çÔýBzÙ®?çóq¸×:V?çøì5#Õ,?è¹â¦u:?è"fXÄ—B?è4àÂál?爉9 ?矜ӯ)?çº/ ƒ0ê?ç°ÚŽã/Õ?ç´ʈ•C?çÄFÐâ?çß\*\Â?çÃñˆ-Ð ?çÒ<·­…?èbäP?çè¸óQÀ?çõò~µ¶?çéÿ:¦úï?è Áð†Í?èèï?¸§?è„W,Ô?è ´í|Ù?è)í—°Õd?è0ø•Û=U?è6êaŽ3É?è%QÊh ?èKhºGt?èX—|qŠó?è^&pµk?èrbG’¡€?èEñU…6?è‚uhö­?è• ¼‘¼º?è„GbИ?èµÁýë8‚?è¸[ ÝÎ?èØÄŒ§‘(?èý§ª"Ÿ ?çÒõ@p:?ç3‰s@?ç91îCb¥?ç\ˆµ ö ?çgæ·}?ç€UÐ8#(?çB™ä^G?ç•j@5-?ç¼]~WÀÔ?ç¶Øl¼[?ç¸Ûë•é?çëàìÆ'?çØ~OŸ“²?çãV‘æ†?çêBÕÅcn?èqôËÚá?è ï vª?è zCL|?èN‡*Ô?è4íaßæ?è?lNkU?è9ê$/f™?è_Ϧ ¬?èO‘ ™Ü`?èUŒ>¹ŠØ?èt×!?熇?玛.;O?çQZWúä?ç–ÚÒÀ?絞`«O?çÇkñH!´?çû`M ^?è™v ŒÜ?è?jz÷yç?èU# …½?èwá:ÿY+?èšD"}7?è·ÄÇSr?èÊ«I÷1á?èü/Þ²¨?è=hW"‡Á?èb#tÚf?çôŸ*]è?è wé² ?è`m?è;^‘¦®z!?@4 4ÿêÄøê¶H€! êê¶H€! ê¼ê¶H€! êdê¶H€SNODØäòðôó 0$Ê@4Ë! ê ê¶H€  ê´&ê¶Hˆ?çVO¦`Rr?çUŠ„Æ#p?çÉ·Õ.P?è4—ðê™°?èšÚažÊî?çItŽå?ç…°Áµ?ç×äšÔ?町!ZÛ?ç"l›ìÁ5?ç›¶6Â?çÇšøÂ?ç¶®¡$Y?ç&ÁÇé?ç ‚ Ñ2?çª0ºâ‚?ç ‘%|?çìÇ!?çõå~tê?ç#öݯ?ç!Afù:¦?çu¨Ü!Ý?ç%'F‡8‰?ç5ïµ!Á?ç9&­ÓÏ?ç0ÛüŽ™?ç'úº¯ôk?ç#íÏ¡D?ç:ãðÅB?ç8Ÿàz=?ç2«Ïùã?ç#¢Z<ˆ„?ç0&Cˆ?ç0²XÓÌ?ç1xåªc?ç/+,y?ç>4Jý_?ç2i•öú?ç>`á$v?ç'Kt£y)?çBÀdÄ?ç%½¤‹_h?ç;å%1X—?ç4½o<‹?çRå‘’?çW E O+?çO¢ûTº?çaþDësÖ?çUÖd‘<•?çM²‘ž€?ça üInë?çZ;'šQ®?çLØyLÕ?çR2J¦õ•?çQáyÊt™?çC‡R&«?çY Û£ +?çL¿,Àgd?çT»ÂøŸù?ç^›ÿRµÅ?çX5”A¶«?ç_èíÒÒ?çWRÊêË?çD£Àîm_?çTÑ®-Î?çOÐÆ*°‰?çEÙ 2Q?çL+þ“2Ã?çĶl?çW”++?çwhG!®?çnh´“Ï?çtð¨ÄU?çz©cãfõ?çwÀŸt†—?çe3‹¬j?ç|ô¤£_³?çiÕk?Ôë?çm$¦ý* ?çiÆÑþ}?ç‚]R6›?çåL­ŸÚ?ç~_{¢F4?ç{*½WYÜ?çe7;‚…Ï?ç| jÌ`”?ço†ãØí?çrsg÷ú?çq£Zà?¦?ç}G@€æ?çq$FfÒy?çm`:€ýU?ç~7ÿ¿a?çgBFäË?çq¾ƒ)±,?çqö¼â_~?ç~v´?çÂHuàz?烹·ÄÃv?ç¡d½;ÿ¥?çŸ+RŒ¯(?ç’o³šñ?ç›^A‡ Æ?ç™Im×¾º?çŠ?ÿðí?ç›UDy|Y?ç¢1α7?ç—?ç¬ÙÌ™?ç¹?R»Ý?ç¶Š‚Ę́?ç¬%ß…?ç¹U†vˆ?礻ó]E?çÀµœZØ?糫n9r8?çÐø¼hf?çÚq!F›x?çÞÃu^Mp?çǼ‘)åš?çÞæñST?çÈ«XcM?çãî}š?çÞ;çq´%?çߥ\‡ø?çßöJ)ä|?çßβ»?çÛèH¿4´?çÃòâëž?çâ÷„å^¸?çÑðt„5N?çÙ;ÞlU?çß¹‡­‘?çÓO„Ñç?çÝ"oƒ¯P?çÔ¶=9Á?çÅâ~¦Þó?çܼ[¡;?çÜ#ûéF?çÞ8p’¨?çâÃj 5N?çÜW˽;?çËdÞe?çÕ7qûŸŽ?çÓñæâï"?çáÛ»À±?çÄÅw–’N?çÊ îvD?çÞÏ-›s?çÉ÷@„çÙ?çàh²½Yª?çÄV;MF?çÛ8ǸS?çÒö"(w ?çòÜ÷ù$ ?çñp±KŒ?çùòE±°?çôoÜe?è:hÇñê?çúwõ®D?èKâŠ9t?çëó‹ò‚ä?çë ¥ØöÞ?è¦xy¦?çüÝË1 ¥?çÿ—@«?çða ¯d?èœ!õÐ?çïS¬žz?çéÏÏHí'?çíxÇ:A?çøgû, ?çô“v6tŸ?èËô÷ÿ?çûNÍ E?çñ*s°?çåªìü‚Þ?çìI1ý?è1¦h_“?è-ß¡?çê€Uõ?çó¯ÁÄ›?çò"™‚̆?çèq­{í?çÿ;V-ÂA?çîå_°!/?çó¬ô6Á?è,[•Ä?è£ÇÜØ?èyV;Y¿?èv,1›?èÓ¥³Ä?èš…d¢?è³ÅÃ?è k2[]?èݨ¸ñ1?è â4nÿ?è,ü²Éç?èÑ£w C?èþˆ>?èI` F?èÜÇÑL!?è‹G?èÜ¥#‡ß?è sjæ?èuû.ç?èq*(l??èG+ßð?è†õäk9?èÑßKù?èÒ¬ÐSa?èî–“?èvyÁ.?èÏ6E)?è€/Øó·?è ü&„y?èjÛ¬BŽ?è*jo„Y?è+[1tx?è ló>¿?è66÷/?èªd2?èbŒ°¯Ç?è“bT“+?è4“òÏ›R?èB‘Oª£i?è$ç‚?è*/;¿”›?è7B|ÙÑÓ?è-B¯8}&?è>óIèÃ?è%ûÉ<}?è3/‡Œ*?è%xäð8?è$µO€‰²?è@^Š$9?è/75Q?èA[s©æ?è$ù1k¥?è-K²k7?è/ïy,šp?è=&éŒZ¿?è=èiHY?è7iÏqík?è4Åy—Hï?è7‰Êü>?è*†€*—Ï?èC,žþÂç?è.`NŠt?èAl÷ÏÄ©?è0]Õ[©?è(š)}ª?è4ßé8:?è1cÆ­?è'0LHø=?è?Ê…‡žƒ?è#„Õ>s×?èKE?]º?èEÞëØ‘¢?èO}Ñct­?èG×Hv|?èS){•ŽN?èNû‰“²?èU‡^úuV?èQ® _s ?è_èžE#û?èQÚ_J’Q?è\¨‚D&r?èY€`DŸà?èI×Éð¥?èDæÙ/Ñ?èb–GþTn?èT$É,?èb®…y@?è\ŒÇÍ=ÿ?èVÚ4¹´>?ècEŒß?è`µ‚öÙ?è[.6ìD?èZÏÔýß?èQã“V?èX̵-¨Ã?èG«JX?èW.> ?èDÝ&âÚ”?èJPƘ?èH±ù¸Ó&?èGAþöv´?èVÞs–?èn%«6¯ö?è…ÉÒ¶Î?èfèçW?è}gò ¿l?è~:Wž´å?èvÛd´“?è}‘ŠÚžã?èqíAB ??èylïò?èݳ?èm ´Ÿ>?èpB†p¨?èsÅi´@Õ?èdÃG¿e?è~øó)ó>?è€qõx??ètá®C?èm%Þ©¤?èk‹ìÑ4Ü?èl^ÑJ=?èuæ-sC?èh¸+þ<-?èjo4bF?èr˜4,?èo'àÜ0?詈iB–?èfä;1M?èsÜ"TU?è aºX?è~‡ô7ý*?èfvÃq‘ó?èv’&²á?èoÄ~L?èwò¸å?è~š ºÏï?èpMÏt¯ ?è{SE2v?èsP/m:‡?èfH†ÇÎ?èwWôkÛ¥?èˆíaÌ?è…uÛ®š?è Y_µ ?è’ÀdR& ?è—RQê?èˆG÷Ïu?è‹ïã‹ç?èžVßÃ%?è‹4TtÙã?èƒOQK^B?è—ã\‘ž¾?èŸã5— Ú?è–¤¢ªù?èfˆ.=?è…ÃÜepò?è†dÕ­˜?èží§Šú?è“Î*¥èë?èš„ K†Ñ?èˆ>sþ?è¡Ý*f8â?èˆoÀû?舡}Ê‚?蜸Öð;÷?è‡MHl™?èŽ_‰ºéÓ?èŽåYµ^Ò?èŒ 6¯»?èýLÁhÔ?è–ÇHQ“?袼†/á?è‡!INÖ?èÚ<™"s?è°Là–©?è«Î—ÈÐþ?è² E ?è¿›Õ º?è²(¥¦p?è½ñ&<-?è¯ç¸-Ñ?è¯ø•°%?讆(¥—a?è£é³Ó>[?è«ïPù”õ?è®x2ùÊœ?è©Ú ðÇÅ?諈9mX?è±[àÕ™á?è©·1óŒ?èµ#|‡?è¼÷‚±Å?è®øí"?賰LJ?èµø9ÑÞ?è¯ë•™•?è¿®!ŠÐ?è©39ú?èØ°ª…C?èÕU ±—?èÙ²ç¨~6?èÇÙ**yJ?èÇÞ”¥¹Ñ?èËèmiÇ?èÈ)Ò…?èÐ& &pÞ?èà$cò®¸?èÍÍ-‰ Œ?èÙ%xÊ’j?èܶ¸ÕF)?èÆOPe ?èÄ÷ˆ)à-?èØ¤BR®"?èÌd ?èáÓþUÿ?èÚž7Õ‡?èÒ ¤‡sÌ?èÍc^[é?èË:K â?èç“ÿ ± ?èðÏÁw?èù¨r%r?è缎|'|?è÷¼îøýƒ?èóк{?èýØ’¢sF?èõánÏ3?èýk»Ï¥?èÿe2¯?èòKtÌÈò?èð²Y ?èæ4ǯ?èö«Œ,ˆ?ç°£ðð¹ß?çÄ÷ŒÄ¥n?çèÛ#ï?çåIœâ(’?çî*?:`9?èÓQh#h?èb„:«´?è EºòEc?è)ƒ}ù ?è1}¹¼é¿?è54w(oÛ?èa¥ý^?èa#a… 2?K«w?NÓ?O2O?N¹e?MGv?MI?LÊ-?Md/?MËá?N?M?KL¨?L6?MÆd?NY?KU2?LDr?Mžw?N?K,›?K¶[?KR3?K\!?MÉÝ?MyÌ?M˜§?N&A?KDÏ?Kô?Máå?JÜ?LX?NUÂ?Kìo?NWb?JÌs?M4?JÀ-?L„?MŸk?K›?KYŸ?LLG?LKO?L‡?KÆõ?KõÆ?L©2?N^û?K.h?L¼©?NÍÈ?L:Ò?N¤¢?LäB?JC7?MeG?JYz?N3ò?JÇB?NÝ€?K|?O?L.Å?Mõ›?JÉc?ML]?N&x?KÑ?KÒ§?L?‰?L1Ü?LÜæ?NB‹?K'?LÃÕ?ML}?Ju„?N¼?Jñ:?Në9?Me?IÕé?O•µ?LQ«?J(Û?OŠ=?KÇË?Jº?O€?Kñí?Iس?NÞ?J‡:?N¦Ê?KCß?NãX?Kׯ?Mß?Jáa?Lî?LþÙ?Mß?N°?K.-?LØv?Nµµ?KÝí?J¸:?N"q?JÀÙ?O[™?L‡Î?J5•?O¶Â?O£Õ?Lë?I1÷?OÇV?O<¨?JËØ?I$$?Owh?Iõ?IË¡?O u?K¨?Oqí?NqÙ?KAp?MÅ—?JN?N©€?K]G?NÓ®?Ky?Mša?N§?KyG?KøÉ?M’ã?Jö?LÿH?O ~?M&Ž?Jt»?O¹ø?L¶¶?Iç:?Oã±?OÒ­?I/ü?HìÁ?Pƒ“?Gܹ?O} ?IÀ?Pª?Hâ¶?Oª¼?LÕ¶?I’ð?Nÿ?L:?J4T?M8ˆ?Jyr?LÝ?MÑÓ?N;m?KV\?MÍE?NÆŸ?L–§?JšŒ?Ny?K÷?OªÂ?NYž?K ð?I}X?OÁG?I·Û?QPÛ?GÆ£?P3m?R›œ?Go?Hèÿ?R,?H“]?Qÿä?HÍ2?QØ?Lš}?O!?Lß?N¨?Kµ²?NK?JçY?KÁ‚?Mž?JÕ?M ˆ?J‡³?O@?K''?J[&?OÞÏ?K§?I!%?OÜV?I!ƒ?Q‚(?Hµc?H]­?Ho3?Q¡¬?H¾v?PŽ(?H—H?OÕa?N]?L«?I(?NòÎ?L5]?IØ5?MðÍ?JÁK?Mnl?JÀ„?Kº—?L³?NZC?K?M„?J¶é?N¹@?JÂt?Ohæ?L¼ž?I®s?O@ð?Iÿ?H¯¡?Qñ?H±$?P/?Io|?P¹)?Hè×?O¸/?I—×?OŒ?M?J›?Oh•?L}Ü?Jº?N3?K ƒ?M‘¯?N1g?K;C?KÅ?MA?NË?Kdë?N,Ó?Kz„?O‘?M[ì?Ií…?OÒ?Lˆ–?IÙ}?Oá?Nð?JÚu?I¯t?P ?L‘£?J9?IÅ?PP?NÕO?K~–?IZH?OæÁ?NV˜?KhÃ?IB?Of÷?L‰[?Jb¸?O8p?Lx?J)¿?N+?JÓJ?Mí¶?J²}?LP[?Mhg?M½Ö?NFO?KOh?LˆÙ?NkÇ?KK?NOv?JâM?OZ(?KØö?JOÿ?NBç?J²?Ož?MÖk?J·–?O p?M¹T?Jì†?O^™?Mv¯?Jr“?OG/?KÍg?IÛ§?Mó?JÜH?Mwò?JS¬?L)ª?N,?N(ã?K(P?KFÕ?Kçr?MwW?N”ƒ?L“¥?Nd?K¹’?NŽó?Kul?O?L"¦?LëR?J+?Mûí?JÉ ?N?KÕ?M•ô?Jº?Ls?Mʰ?Np?KL»?K`?MØ)?NK?K8¼?K“G?Lû«?N°?JÜÞ?My!?JÝ?Lr×?Nn„?K›à?NJÉ?J 5?LŒ$?NyÄ?K?ô?KèÖ?L}$?MZ•?LøÖ?Lq¿?KŠÐ?LM»?MO?MÀ,?Ku&?M2?N:©?L»?LÏo?M=}?M­D?M{??L\Ý?HÝ?N²??E*¼?E®ú?JYj?Iô¬?Fá)?F ?F³×?I`,?N§?I[™?L,>Ðpö>Ìjo>Ê:´>ÏpÏ>Ðá³>Ï÷Í>Ïé3>Ï!d>Άl>Î s>Î@Ù>ͯi>ÌêÔ>ÌÞ>ÌLE>Ë0š>Êu4>Ë ß>Êø+>Ê;>Ê >Ê 2>ÏrQ>Ð~»>ÏËí>Ï 5>Ï*â>Ï©Ü>Ï ß>ÎQ›>Î>Í0>ÌÓ>ËÓû>Ëò}>˨>Ëíõ>Êb$>ÉŽ>Êûx>Él!>Éw$>É„>ÈÅ>Ð/Ç>Ðà1>Ñ2T>Ϥà>ÏÆè>Шß>Î >Б™>ÍCa>Î)c>Í–>ÍaD>ÌØV>Ìs£>Ì •>Ê·º>ÊNJ>Ên×>Èÿ¦>Èþ€>Éûä>ÈQô>É¿»>Ék@>ÈÉ@>ÇéÁ>Ñdh>Ò7š>ÐK>К >Òe!>Ñ=Ï>Ï…©>ÐE >Ðjß>ÎŽ>Л|>Ï3ß>Ípà>ÎÛ=>ÍÀ>>Ì>Ì=R>ˇä>Ê[O>ȘN>ËO>ÈÀÓ>ÈÕ;>ÉT#>È>ÉÒ§>ÆÎ&>ÇÝ×>É>ǧ=>È >Ò8>ÑJ>Ð2á>Ó²>Ͼ>Ódž>Îì\>ÔO›>Ò´>Ï×ç>Ô ç>ц—>Í‘l>ÑK‰>Ћ“>Ï‹>ÍSM>Ïeê>Í“ª>ÎA >ËõC>Ë¥m>Ì™ß>ɪƒ>Çòv>Ê&k>Ç<>ÇBœ>Äãâ>ÆÉù>È&M>Åüù>È Ò>ÆV>ÇÍ>ɼ >È~š>ÒLÈ>ÒÁT>Ð ^>ÔR½>σà>ÕÈ>Òýø>Ò‡>Ô—x>Òa>Îãb>Î q>Î}Y>Íò]>Ë…¤>ÌÙ>ËM½>Ê>È-Õ>ÈѾ>ÆZ>Áp=>Ç{>Çr‚>Äšê>ɱ,>ĪÃ>Èòc>ÄÔà>Ç©å>Ð>Òb>Ôg¿>Ïèb>Õ!Ò>ÏÄh>Öä`>Ôè>ÏÄ>Ù*Ü>Ö…g>Òï<>Ïï\>Îká>Ñ´>Ó.0>Í2²>ÎÚb>Ï@>Í È>Ê:#>Ëú>È=z>ÊÓC>ÆÒí>É>ÈVõ>ÂÐE>Âß>ÈMt>Ĭb>ÃŒ)>ÈÞ•>Ä‹¢>ÆíE>Å`Þ>É¡î>Çê>Ó> >Ò.y>Ò¹¬>ÔGÄ>Ô–>Ñâ>×2Ü>Ó˜¬>Ñ>×W>ÚÃ3>Ï^ü>Ïx7>ÎŒû>ÍÒ¤>Í©p>Ët >Ê*q>ÊÓ>ÉL>Éèþ>ɺj>ÄPÛ>¿Tç>Ád >ÈÕî>ÃÔ=>Áxi>Ç)¼>ÃÖ#>Å9k>Çs>Ç'5>ż>ÑÎÇ>Лb>Õ1õ>ÑÄ>ÕM4>Ðï>Ö;:>Õê>Ï5)>Ö´>Ú,È>×å^>Ñ–>Ò»=>ÑoÏ>Îô:>Ï{¤>ÌÎz>Ìë‰>ÌS!>É«a>Êk‰>ÌA>ÈLb>ÊÙN>Å@S>Çžû>Æ=…>ÀY˜>½–>ÅÅ>ÈOû>À5–>ĈË>ÆËÃ>Ä*÷>ÉPà>źù>ÇG>ÈîÀ>Ò>ÓRì>ÐRh>Õ5‚>Ñ$R>ÔÄQ>ÔÝq>ÏŸU>ÔûÛ>Ø[O>Ô©>Îöû>Ïr>ÑÆQ>Î'>Î?ë>˘«>Éy6>ÊûÛ>ÆEè>Lj>ÈÕì>ÂÐ>Â<>ÈW>Æ>ÃE‹>È>>ÄÜ1>Æ >ĈÑ>Èí÷>ÆØ>Òaª>Ñ™•>Ñã=>Ô9ø>Ñ>ÓAx>Òè©>ÐU5>Ô°2>Ò–‹>ÎhE>ÎãÒ>Ï >Íçj>Ϋ>Ì$i>ÌTÀ>É_>Ê¥b>ÈXr>ÈBn>Èó²>ÄD >Åm>É|>Ãfã>Ǽ,>Ås>Ætï>Å‘r>Èë>Çñˆ>Ò…>Òk)>Ðǯ>ÒX>ÒÊ>ÒiW>ÐH:>Óy>ÐcŽ>ÐcŠ>Ó{š>ÑC>Ï<ñ>Ñe>Ñj>ÎM >Í7;>Ð ü>Î Ó>Íù>Ì]½>ËÊtƒ>Ì$>Ëvá>ÈÀí>ÆxÁ>Ë"ü>É¢C>Æô‘>Ç,>È?]>Åvä>È÷™>Çâ>ǯ>Æß>ÈõÓ>Æb2>Ç…Ó>ÐÖè>ÏÌZ>ÐTs>ÒI*>Ћq>Ñ´‡>ÑOu>ÐkÊ>Ϭ>Ñͽ>Î6‘>Ïi¿>ÎÏ‹>Íer>Ï>í>ÌKø>Ëä >Ìf™>Ë«á>Ë:>ɪ»>ɵV>É”N>È»x>ÊÅw>ÇðN>ÈÓ>Æî>ÉiÞ>ÇSÛ>È>ÉW>ÉÏ$>ϾÊ>Ñl^>Ñ >Î[—>ÐÍà>ψ>Ðc>Îcf>ÎÆò>ÍD{>Íe¬>Ì¥§>Ìã)>Ëvm>Ë)Z>Ég>ɨI>É%ž>Ê`ÿ>È^K>ÈÃÌ>Éþ(>ÉÂ>ÉÀ–>Ïh>ÎcW>ÎÍà>Ð,ã>в>ÎGM>ÎsA>Î<Ö>Í¢U>ÍÅV>Ìv>Ì_‡>Ëcö>ËKÃ>ÊL}>Ê¡ >Ê>É).>ÈÆa>Èð >È_N>Ïà>ÎñZ>Ïú>Îð">ÎÝ>Ìö¸>ÌZS>Ë’®>ÊÕA>Êa>Ê$ò>ÉÍ_>Èûi>É^>ÈÏí>Âo&>ϵÒ>Ç1>½»ó>¾ñ6>Ó½É>Çè¾>Ò9W>Å>Á|#>ÐÍð>ÅÔè>Ï<ú>Ïkë>ͨ…>ËØ©>ÉòR>ÐYO>Ð|>Ðÿd>ÑX‹>Ð$N>Ðúâ>Ó]0>ÒÃv>Ðø>Ñä>Óel>ÑäH>Ïxô>Ð/i>Ñj;>ÐJ>Ï[>Ï;Ö>Ï ¡>Ð5t>Ð2">ÐLŽ>Ñðç>Òbú>Ðé–>Ó¸ó>Ñ<Á>Óã/>Òˆz>ÑÌ>Ó©é>ÏÛa>Óæn>Ò]@>Ï >и˜>Ñ?ï>ÐìA>ÏÛ¨>ϲ€>Ѐy>Ñ?ÿ>Ð::>Ðq>Ó~>Ï7ˆ>ÓûÜ>ÏÌë>Õ£á>РY>Õ·>Òt>ÓªS>ÓÎw>Òæ¥>Ô‚>ѧ >ÔgÉ>Ñt>Ñãi>Ò'>Ï£ÿ>Ï4>Ð.ö>ÏÙ>ÏP‰>Пc>Ïžä>ÏtI>ÒA@>Ó‰A>ÏÏŽ>Õy>Ó¯>>Ѧ‘>Ø-z>Ó –>ÓÕ>ØN8>ÑèX>ÒÛÊ>׋V>Ñu>ÒÄ+>ÖS÷>Ðå>Ô6«>ÔDd>Ðñ>Óu?>Ïçâ>Ò€>ÐjB>ÎçŒ>Ïöž>ÏÏå>Ïå>ÎÀø>ΡŒ>ÑT¯>Ï9>Òkœ>ÎQ?>Óé>Ò~÷>ÐrÅ>×ÑV>Ò«Q>Ïü¨>Ø’>ؼ>Ó|>Ð;ž>Ùk >ØÚÚ>ÖÙY>Ϻ%>Õúí>Ï5,>Ô»>ÖÐF>Ï"ß>ÕÁ>Ó•>ÓàN>Ðþ‡>Ïë>ч>Ï)N>Ï}[>Ï®>ÎE¿>΄†>ΗÉ>ÏMP>ÍÓ†>Ï™x>Îf©>Ñ`.>Ï>ПZ>Ò—,>Ðîê>Îå>ÏI=>Ð6ø>Ñ>Ðé‘>Ó[ç>Î8æ>Ð#*>ÑQ«>ÐæÊ>ÑY¶>Ôê>Ñ2 >Γó>ÐÙ¤>σ>Ðð>ζ>Ï+R>ÎGn>ÍF>Í»¶>ÎÆ>Í‹Œ>Îsø>ÍÆÒ>Ï^º>Îô:>Í>Œ>Ð$>ÑÍÕ>ÎÂO>ί†>ͧ>М>ÐåÆ>͉þ>Ñš¦>Ðh>Îp²>Òă>ΓÞ>Ðk,>ÎE&>Ïí«>ÎÊ>Îà_>Ð\r>Ж«>Íÿb>Ð0}>ÐR>Í&$>Îö>ÍÿÀ>ΜX>Í7â>Íã>Í'>ÍPÍ>Ìó£>Ízã>̾,>Í >ÌÛ)>Í»†>ÍpŠ>Ìûß>ÍpC>Ìt~>ÍÆF>Í9Æ>Íë;>ÎÐ>Ξé>ÍÂ9>Ív>ÌÿÊ>Ìõe>Í#Ô>Γg>Ït>ÌÒ>Ì|É>ÍÛö>Íw>ÍdÞ>Íó³>ÌØ'>Ílý>Ìó‰>Ìš?>Ì;Ô>̆I>Ì}â>Ì+r>ËIõ>Ì-ˆ>Ëþ>Ë+»>Ì»c>Ê·ª>Ìm>Ë,é>Ì 7>Ì‹Â>Ì$>Ël4>Ì÷>Ë—ò>Ê›>Ì š>Ëß>É®@>Ìu>ÊÚ‚>̯>ÌàU>̳>ËiÜ>Ê[|>ËË&>ÌŸm>Ì·§>Ì6>ËÖ©>Ì'·>Ë08>ÌSö>Ì5©>ÌC≯<>Ë–¿>Ê Q>ËëÚ>ÊN¨>Ë–g>ʪ}>É(¹>ËŒä>ÈÀò>ÈÖ>Êv>Êžû>Êßó>Èœ>Ë6B>Ê;Õ>ÊFµ>Èù8>Éñ„>ÉSs>Éf^>Ë^Œ>Éw,>Ń“>Ê–I>Ê6l>ÉØo>Ëb>Ê®Ó>Ë,û>Ë”i>˘>Ë™×>ʽ½>Ë'}>ÊÏÞ>É<>Êf{>É-x>É”Ð>ÊP>ÆÞ£>ȹ>ÊÛJ>ÊO#>ÈÛ>ÈG>Ź\>Ê8Ì>É™|>Æ=>È„s>Èd;>ÇQÈ>ÉÜ1>ÆaÙ>È&>Êœ¢>ÈD“>É•>ÉèÈ>ÉÝ>Êl³>Ë«]>Ê»Ñ>Ê!>È©Ó>Ê}^>É>È4 >ÈD>É *>ÆCE>ÈPá>Æ2Á>ÃÍí>Æ É>È!½>¿ü>Ä:5>È=õ>úï>¿>¿Ç*>Çgw>ÆÓ>¿rM>½€¦>ÅsÌ>É'>ŠX>À°>Ç-Ú>Æ„@>Ãÿb>ÆOÂ>Èwo>Åê>Éo>Çòã>ÇßÄ>ÈëQ>Ê^Ã>É»j>Ê^R>Ê £>ʬ€>É©ð>ÇéO>ÈÖ¹>Ça9>ÇTM>ÆÜò>ÇÇT>ÄHp>ÉPˆ>ÅS±>Ä:·>Ég¡>ÁxS>Â$æ>ÉUa>Áòa>Ãn•>ÉI>Ä+>Å ç>Çv=>ĬC>È )>ÆŠ>È¡>Ç2 >É>ÈoÃ>É|&>ʤ&>Ëê>Ê7±>Çç">Ç}µ>Ékä>Æ>Çù>ÅcK>ÆÚ>Äh<>È?„>ÃhŽ>Ã4>ÆÕ[>ÃÞv>ÆlÚ>ÅJè>Æx×>Å÷À>Ȫ¼>Ç?’>Èì>Ê5ž>É›>Ê8î>Ê8w>Ê.Ã>ÉØ >ÇõÈ>Ƴ>ÈR>ÇKv>Åz£>É%®>Ä„‡>ÇÏ>Åôƒ>Å›C>Ç$2>ÆQ>ÈËÏ>ÈCÆ>Çy>ÈgÍ>Èéu>É\>ɘq>Èì>Ç.k>ÆS >Ç>ÆÚ¶>Æ_ð>Ȉï>Ç·>ÇH|>ÇÓF>È>I>ȇK>ÉOã>Òén>Óëf>ЬŒ>Ñ>ÏÏ>ɲÜ>ÉÝM>ÇyO>ÇYÊ>ÉÝ>ÆYl>ÂËÏ>Á‚EfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffEfffA1A]BëCODÔFâFãFäFåFæFçFèFéFêFëFìFíFîFïFðFñFòFóFôFõFöF÷FøFùFúFûFüFýFþFÿGGGGGGGGGG G G G G GGGGGGGGGGGGGGGGGGG G!G"G#G$G%G&G'G)G*G+G,G-G.G/G0G1G2G3G4G5G6G7G8G9G:G;G<G=G>G?G@GAGBGCGDGEGFGGGHGIGJGKGLGMGNGOGPGQGRGSGTGUGVGWGZG[G\G]G_G`GaGcGdGeGfGgGhGiGjGkGlGmGnGoGpGqGrGsGtGuGvGzGG…G†G‹GŒG‘G’G•G–G—G˜G™GšG›GœGGžGŸG G¡G¢G£G¤G¥G¦G§G¨G©GªG«G¬G±G²G¸G¹GºGÃGÄGÅGÏGÐG×GØGÜGÝGÞGßGàGáGâGãGäGåGæGçGèGéGêGëGìGíGîGïGðGñGòGóGõGûHHH H!H:H;HEHFHLHMHNHOHPHQHRHSHTHUHVHWHXHYHZH[H\H]H^H_H`HaHbHcHdHeHfHkHlHvHwHH‘H’HªH«H¬H¶H·H¼H½H¾H¿HÀHÁHÂHÃHÄHÅHÆHÇHÈHÉHÊHËHÌHÍHÎHÏHÐHÑHÒHÓHÔHÕHÚHÛHâHíHøHùIIIIIII I I I I IIIIIIIIIIIIIIII I%I&I+I,I2I3I7I8I;I<I=I>I?I@IAIBICIDIEIFIGIHIIIJIKILIMINIOIPIQIRISITIUIVIWIXIYIZI[I\I]I^I_I`IaIbIcIdIeIfIgIhIiIjIkIlImInIoIpIqIrIsItIuIvIwIxIyIzI{I|I}I~II€II‚IƒI„I…I†I‡IˆI‰IŠI‹IŒIIŽII‘I’I“I”I•I–I—I˜I™IšI›IIžIŸI I¡I¢I£I¤I¥I¦I§I¨I©IªI«I¬I­I®I¯I°I±I²I³I´IµI¶I·I¸I¹IºI»I¼I½I¾I¿IÂIÃIÄIÅIÇIÈIÉIËIÌIÍIÎIÏIÐIØIôJJ"J(JJ‘J¨J±JÁJÐJßJäDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH  ê8ê¶Hˆ pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8Ì=TREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTK§Àü(@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ ôG¼G !    ¼O ¶HL?èPSNOD`ì£DJPÜ¢œL !    ¼j ¶H4AèP !    ¼… ¶HCèPBÓË{Bê 9Cå°C6ÿC33C ÜCÍ›CpICŸÇC ! C8BæŸBè˜#C‡çCÅÓC$ÀC1Ø„C:ËÄC<36C9BC0F¸C"vÑC@¹CïC ÍCòC)œÀC>aCQSýC^@†C`xC[áÀCNí C;—ËC%®C DC ±úC"å’C=„CU¥ÏCmöÔCC€ÇæC{™HCiÒCQ³ÊC6ÑËCuÖCuÄC.uCMÚCkßòC†$ÿCl€C‘ÕîCŽR¨C‚å£ChJ§CGOC*-žC€îC5PªCWü³CyT¦CŒ8CšážC›î÷C—æžCŠð8Ctà×CPd›C0¼CšC5Ù©CY1ÑC$ ±CìPC{ÆC2BCG±C]¶BCkê†Co Cj3«CZAåCEjMC-“C¼xBóö‹C cCMdC.IC>*CHˆCIÖœCFNŽC;' C, ÇCC·CXÅBÜ’sBõöcC ’C3HC"©ÆC)ïëC+’DC)H¡C! |C®;C{*Bóa:Bè›ÙC‰kCÉ’C#åzC1¶QC:ÎC<4âC9C/dœC"ÃC5CãC¤lC C/ãÁCFó¹CZìÈChæxCk-œCfB²CXiCBâ*C+ãGCÃC˜C.zäCL“ŠClÅ&C„ßC¥ÅCéC ãC‚qúCfÕoCG0&C)¯ÊCInCCá8CjWICŠCžC®×ÇC°n€Cª¯XC›7*C†hôCbÒC=Ñ™C+› CTõºCͶCœ^C·4CÍdåCÏ;éCÇNCC²‡ãC—ŠíCz¾´CNmC29äCQ0¬C]äC`•C\CN\ßC;SC%¨MC OCŸóC.}ÞCL„îCmº¥C…DC8mC‘¹CQC‚gVCf¾ôCG+FC)°(C'3ƒCJàÿCtÂŒC‘ÎèC©?œCº!eC¼ÀœC¶wßC¤ä¹CâRCmZÓCD70C8»‹Cg~#C«§C² ˜CÔNCñ4CöµrC뀪CοDC¬?ãC‹“C_mtCJ…¼C€lCC£ùNCÐiªDêÿD£ÃD¥FD»"CúröCÈ}­Cœñ}Cuû¨CSìòCˆüC²ŸTCç{qD¶”D/ËD6p D*?D# CÝP5C©¨ŸC‚ÖšCUQªC‰M}C³ècCê7ÉD_¾D2ÿD8uÂD-¸õD(Cß"„Cªr’CƒXóCOS0C„|CªË@CÝFD ±@D"ÖaD'¥•D‘6Do˜CÓ%C£ 1C}Š×CAGCràC˜ßJC¿jóCèkaDÛOD»ÏDnCáùÑC¸'nC’ÄŸCj¤C/rHCVóZCƒT+CŸ¤CºÂRCÏX‹CÒÌ®CÊôACµ„C™®"CùrD"–DH7ûDMšD?;…D5CîJHC²³4Cˆ¯ƒCAt/CwS“CCÅG>Cð-/D [D úãD 1Cê0³C½|‹C–îCmµéC(¹CQ½¹C€D”C™OC±©uCÅBVCÈ£ˆCÁÓÊC®02C”s½CvxÀCKcðCþVC0\$CO„wCpfùC†cEC‘&!C“¡C%C„7§Ci~˜CIvÙC+UC~æC.-uCMªÕCkàYC…×JC$KC‘éŸCŽeYC‚Þ–Ch<ºCGLåC*,”C+wtCT§×C¼¦CœKC·ã¯C˱uCÎä]CÇš$C²È¬C—LCz¹MCN CJ/aCH„C¤GCЛ—D"DòºDÍDF:CûøãCÈ‚QCœÙ€Cv ßCcåÅC˜ßìCÎbD ³D<“ÝDo3/DzƒäDe§›D5g5D´ÜCÁÞ¦C‘œC€àÞC¯A†CøŠõD7ÈÙD‰htDÃ:EDÚ–D¹÷HD']D*¼kCçÇC¥³Cˆ>šC¾Ú8D)¯D_&D·DE„PE?Z:E!ÞD¦Ü/DI1åDŽ)C³rMCˆx.C¿ñBDHôDb*˜D¾n­E+xÎETI^E D­`D’¸±Dœ©D‹AÇDT%DT½CÒZÌCš]iCVK1CŠ1§C´hHCëIwDnD6R¼D9ˬD.EnDѵCâÎÁC«ö]Cƒõ¶C7qfCf·C îC¯\ÀCгïCì?&CñG Cè§!CÌ™CªeTC‰¢fC^ÀC^©C>KeCb¯ˆC…7åC—ÊC¤ßôC§[²C¢ýðC”þsCxÈC[ù$C8R°C€ƒC5S¹CWízCyR¶CŽeZCšÀC›ÓÂC˜lXC‹TJCtã¥CPc¼C0ÍìC28BC_ùbCŠIÛC¨xCÈ6CáCæÐØCÜÕ‡CÃ3C£HŸC…F.CX²˜CSÙPC‰C±ÄÔCéSDô>D/õVD5è“C|°C¯©¨Cû5uD;¯ÐD‹B×DÅà’DÖãUD»sCDƒ»¦D,ÊšCèúˆC¥úéC`<…C“Ü•CÅD÷D/.êDVþ[D^'ZDOqD(z(CüU)Cºâ¯CŒŸ©C>‹[Cs6C™b2C¿V=Cè5Dã™D¸šDèMCâ1C¸­ûC“5Cj{jC#¡©CGRšCp²2C޼C¢¤ÐC²ê´Cµ¾C°9ÜC -SC‰ñkChIC@¼ÒC¶C5àñCXþÀC{M}CTCšÖÕCœ£IC˜ÜsC‹Á†Cw~€CQƒ[C1À;C2¶-C`ÞC‹ pCªk”CÉ\§Câ”éCçö€CÞDLCÄ“äC¤RôC†.ÉCZ\èCUµ0C‰qC²óCê‹ÕD ëD28ÀD8è:D,Ð{D§ÒCßUCª»îCƒäSCr ÜC¥FGCå(=D#uDi,çDž”ÅD¨}ŸD–æØD_tºDn³CÖðìCœ¶%Cˆf—CÁŽêD°ÞDc{D¿JeE.œERˆ¾E°oD­’dDL.”Dö-C³­JC“CÓpeD$_‚D`BEŽÍEÿìWF¢¶SE»v>Dúû‘Dv7^D™‚CÄÆ@C‘C:CÕGFD' D”#OE&mF4oÆGT¹LEìë¼EN@Dz©×D™)CÆWCŒ¼qCÊ«XDV&DZ¸Dñ E€mE«¬þEj)DÓÊDb™D ©¿C¼"C}±ÈC±ÅCüÏÎD<®äDDÏX„DàVZDÃ|D† D.ÎCëò]C§$óCbZNC”ÊNCÆ~ÔD{D0¨‰DZÓeDa¶DQÍ>D*$Cþd—C½[C®ÝC?ž8CuKSCšN CÀ”:Cé,õDµFD°µDi‘C㵪C¹þÓC“íþCk–C$Ë™CH CpúCŽ–!C£‚ C³ÿìC¶äC±MC¡XCŠÎkCi7CAoèCù«C1±êCRÜúCs 2CŠJsC•9C–ãC“/÷C†ÕWCp<÷CL*üC-¥C.ÁÌCZ^«C…ÿ±C¢‡éC¾å3CÕ®ÿCÙÉ CÒa×C»EC‡…CbŒCSekCOˆ[C„ŸCªÊ´CÛ™VD ÓMD#Ÿ×D'¸MD‰øD»BCÒùC£4C~cDCj;Cž;CØ)D9fDO˜ÞDˆî˜DìDª´DHÿDACË{C–s{C„LC·.D-ËDKDœ­5DôV‚E ¯çDäF¦D’5”D:”CöýC«¡×CŒdCÇú=DˆúD{¤—Dá#Ee\âE”EKöÿDÉbD^ÎD êÓCºÁCŒ­ùCÉ­9Dí\D€"´Dí«ŸEÛgE±ÄdEh}íDÐèzD`¿¢D ç7C»í$CˆSÁCÀÄßD¾D`û¢DºvE!REIÇEb!D©hDK¿D¶C²ê7CvïC©PôCìéZD,7SDx‡€D¨³>D¸Å¥D ßÏDjh8D ;½D=ãCîJÎC³YÍCˆ_®C;åCme:C”NC·{öCÜ8YCú=\CÿiVCôTmCÖp2C°ã›CŽ=ãCdIWC!ïCC 6ChÓ„C‰›”C"ÐC«Ö¹C®W¨C¨þöCš6 C…¢bCaÿjC<”CÔìC'Q[CEÌC_±FC{ “C†påC‡ïàC„ÄrCu‚ƒC[ÞSC=ûmC#ßC$rÙCK4ŠCv_PC’šÃCª:çCºÿ´C½—AC·mÕC¥®zCŽßCm¢1CE?"CA/CrÞíC™œC¿‡JCèI^DwCDD—DõLCἸC¸¡„C“ CjpCVó CŽ¡uC½VoCù·àD!™ÏDFÓÑDMLÅD>ÙñDwlCîjùC²â¿C‰DsCp"€C¢>CßëDñìD]zÁD“^ÔD›íŒDŠêDU'D8êCÒž…Cš#îC}ÄJC°‰Cúì¨D;TDŠòTDÅH2DØœDº˜ŠD„Ì;D-‡Cé,C¥ï2C~MžC± }Cý€#D>DŽç*DÌýDàâDÃ8­D‡€¾D/O`CêÍ"C¦çkCvCgC©)£Cí&D+øÎDwÄÇD¨”ªD¸løD ¯fDkØ.D J‰CÝ>C ^C`ö8C—ílCÌRTD ÿÚD:¯›Dj²DväDaÒ‡D2ž1DxÖCÀt[CÈ\CJ·ÒC‚°C§“ëCÖp­DœDÍ0D »~DÆ(DêÆCÌøÞC š.Cz¸ C.ÎðC[ PC†~ÌC£¶C¿ŠùCÖ!,CÚCÒVC»Ü·CüÖC‚ñCTY*C)MC6ÑžCW÷!C{ÄÉCŽ€Cš(›Cœ)C—Þ§C‹¯×CuYCQúC1¹˜CîÑCzÂC2A&CGÒ½C]ÚyCkäæCoþCj)´CZA2CE_ãC-WÆCNC•oC7ACY[C~hÉCcþC›~!CunC™ÒCŒŠ–CvuWCR»¢C1öºC/gÎCVæŽCƒy¦C 1lCº CÎݳCÒ¢ƒCÊîvCµ–1Cš{‚C~TÀCOšàCAT(CwõÍCãÁCÅ1uCï–®D ÔAD{Dõ=Cé‚ÇC¾C˜C–[©CmìECV…ÆCŠ_ÁC´–„CëCWD0ØD5 éD9œZD. DfMCâ7'C«úÇCƒ÷+CaÛC”á·CÅx¯DÆãD.däDV…\D_ÅuDOKD(XÜCüS8CºÛ`CŒšØCbZC”ʱCÇŽDl-D0±)DYÚDbD[DR–‘D*óUCþ´~C¼oCgáC[óCC—C½s*Cû3óD"RmDDáDJÿ.D>±åD­Cï hC³ÄCˆHoCJ¡C‚@“C§7CÕ¤ŸD8RDØéD "DŒ4D2ÆCÍùCŸ­hCyíüC8!Ce‡@CŽÌC®{JCÎò Cé»ÓCîgéC䎭CÉȇC¨¨CˆŒ‡C\C¤C!e(CD¼QCkáCŠÝÛCžç8C®UûC°øúC«¶ìCœ«ðC‡2¼Cc×6C> ’CQ#C&J²CAY}C]æVCvjëC„-C…µC‚‰wCrÛCXˆ¡C<–âC!Ý BóíCC HCD•C-ývC> QCH.nCIìÞCGijC; mC,C7HCCýC!ÈwC;†ØCU¶ÙCl² C}œ$C€7\CzÕ%Ci¯ÔCQ#ôC6àCôCCwC:‚bC\ÒC€ú®C’CŸxC¡HWCœïoC C|­CVfZC4§íC(ü£CRJïC€âC˜ìÆC±›CÅ5‘CÈœ#CÁ¥ÑC®DêC”ñ”Cv¨fCKWsC8(†Cf¥CŽÿ9C¯@lCЧçCëöCð¬[Cæ×ŽCËÉêCª­cC‰ñýC^”C?\²Csð|C™KžC¾þ‰Cç@D‡D«rD³‚CáÓC¸“EC“4CjžãC?;¾CtTbCš?jCÀ›/CèýD!`D &AD¯CãÙUC¹‘C“ÃØCk‡ÔC; Cl¢îC•¥C¸HWCÛóÝCú0[CÿWÃCô’¸C×SC°ù)CŽ.¸CdJaC/BmC[óC†ÑýC£ÑC¿dCÖ>\CÚ>CÑÌyC»; CÕ÷Có CT=FC!}PCDNðCkoICгªCžÖÛC®ZïC°õÕC«vâC›ï‡C‡ZCdX4C>PdC”[C,9^CI'^Cf×üCˆC‹2†CŒÜöC‰Y¾C~n:CaÕêCC„VC'lÊC¢¡C¬5C)½HC>ûþCQA¥C_5C`ŸC[ì CN­•C;HÓC&úCÍB܈@Bõè;C \C%‹C"§EC)ñéC+›¥C)C ý›C„!C|Bó1Bón¼C ™–C mÞC2ãéCBº»CN(˜COþ CL"0C@·7C/ÝCÝC µtC ?`C…xC7å*CP—Cfq÷CvpMCy#˜CtªCd~CLƒ‚C3?HCrmC ÏC0¯[CO…ÚCoa†C†BKC‘,dC“nC5gC„VzCiï×CIøEC+´CkC>cŸCbŠyC„åÃC–ý§C¤íüC§SC¢YðC” ´CŽ¿C\| C9s"C#¸_CGv|CoaNCÅ’C¢š‚C²æÖCµ¥YC¯ðwCŸÝ™C‰ÑÌCh!ôC@ùÛC% ¸CH÷ICpÂXCމDC£‰ C´=C¶á©C±C ÂCŠ}7CiÓCA|GC!†5CCR!Cj¤5C‰ž Cœ®C«¼,C®]¸C¨õíCš¹C…‹CaøóC<—JC%VC6ÕECYsçC{óMCè¿CšFCœ2’C—ßC‹Cu¹CRpC1hdCWeC&?—CAlùC]9wCvÀC„^DC†8pC‚³ºCrÃKCX‚ C=LC!‡ãC×IC¾ÈC)µC>âþCQ.×C^^GC`¤äC[ô_CNª3C;@9C&â7C¦ÙB軸C¬áCyFC"9•C/oC9$hC:*/C6ù²C-§4CŠC# Bÿ0–4Ín4¢•4`,4ÿé4&G4kD4 8<4›h4 ¥Ç4IN4Û/4ïÊ4É„4› 4þ–49K44ò4 e&4“Òï4!g4©F4±æ4}4Ì'4'ò4"?¿43Ég6 4úû4aÐ4ì-¹4†14 Zf4 Ê@4u345Ý5‰WÂ4Í~)4ci³5 !4Âê*4Ÿ)'5|L‹5g =4yTÕ4 Ë(4˜¶4v½$5€Z5 W†5)Mz4ÄN5Ò6D©¥5_þ/4ªZÍ4x4 ·j4yU5NÎû4Øeí4µŠg4ÊDÇ6ÄUp6Ç`6pÀ36ˆ›6 5eJ\4óqê4ØD§53…B5ë56¶¹6(Ê>7#73p8‡c7hA°5óF4ÀÍ 4Ï)ø5TÈ4I•¡4lÙ¬5s5w?:5“*…7Ù¨é8—[r7¦œ5˜¿5À&,5­êU5?:j4R4'cl48 ­5„3w7ka÷7d±]6ÍÂÕ5¼Þ5Ú5`.³5UaÎ55õö4³E4-4 6g4@Q5Í×6i·“6 »T5¿ˆ4>í÷5LRÎ4Á†å4µ›4 4a4 Pã4¬4 ”4äéÈ7¦ 5Ø?j4­Ó¡5³5»–‘4Ëç!4!ù×4•Ð4BG4(‹B4(¬+4\ 5‹75Ø=5üï-53½µ5X|Ñ4‰³h4 Ù÷4Ü4w?4"‡|4jA41Mò4MW-4'f5ŒÅ4çPl4Sǧ4 >4€Ã4—?4 C€4¡4 è4ÙÁ4‘&4‚gH5(á”4­Æ:4 6Ú4—d4”~4 ƒ°4Ö\4" æ4?‹5Qš4Õ°ð5ºåñ5U¸¬4‰á©4'ÁÜ4 J4-?4¹ô4Ž:64ˆBê6,,ö6"±Ó5*Yó5‹W/6mè5Zï©4˜C{4¬ó4˜64i(34DÆE4}Ã46> ¹6Iº5Ãf"6€ˆà6°>é6Tyª6Í54ß{D4 æè4†ð4ln 4ÈþÉ5ø²t6?›47mvA8Iš6å5÷5êÕˆ670H5I4BΪ4{ªÀ4ÓyØ6=Ê6²97xÚ‚9‡kŽ9ØLó8¿ 7ö66Wó4¿ë±4’\j4=7ö4«ëç6 IÇ6Fjæ7”M-9²@ª:D•9)7:‰b7 Øõ5û• 4¾Åx4-ñ4o!h5»sÞ6h÷7S«x8YV`8Úï74f”7@6Ÿ£5? —4«Á4¹ÿ49.5yés6•ñ¡5}*’6C‡6„¡6n™•5Òͦ5Yã4EaD4&6A4 A4(74†é®5¥åI5´ŸR6©T5†Îp6Çã5C2œ4]ù4~‚4 ¦´4qû4…u47›=4¹ª|5ÇÖS5‘<5$ï5šXÃ4íOC4Tߢ4ãé4 Lá4#4 œV5$4sÖ24ª04£XŸ4™ ¸5:F4±¯»4DX4M…4ä4 .4³Ÿ4 D(4¾£]4Êü4aw×4v<Þ4¦Îp4d~4›'4N¨`4(}24Ž-4š‰4AM5_"~4ÇP4Ù:5x 94ݲq4> 84Uø>4— ì4äç§4>Š4ŒÜ4Nâê5£Q5—Ù5Tôi6vî5ˆ”34lÃl4R?N4–&c4Ô+Å4ö®C4…"q4wáN53P5¶ é6ýª97ZƒÅ6š&5-4Ž‹q4`Áå4@SÒ4Pþ6?A5¿¯-6²Ÿ6¦Ù8&ÿ8B¢|6æ4¾™§4èWœ5Wô4‡Ò™4(S4ênë5+\6Ã7d7ÿŸ§9ê¦e:-[g8è×¹6†[À5ZW¸4Ä7a5„ñ4lö?5—5ÖÛµ7%u<7¾ƒ4:K7:ƒ½@9­˜ú7[‚6ŒI:5ã Ù5§4¨^}5²›£5ŒhÛ6@%¹7ÐŒs8 p¿8 þ€8¦§Ê7ƒyƒ6»]—6+ðË59¢ƒ4>GO4‹·Æ4”l]5P6“ã7$Û6ÿš6ÀÑ6 95†ºN5—ô‡4·<‹4,F¤4<º4M’J4ÛŒÉ5C!Ò6[lÉ62]5—õò5´c4 ?5OÖ4ªJ£4CWD4˜yå4PES4_ª®4§Ã5šœ5ŽÏ4Œ¥4±l¿4“¦.4pº}4&:4$4º‰4l\ú4š420˜4[]s4h¡4?½T4Ÿ×?4‰vª4(G4 ‚4 $v4 K;4 ]ì4ˆ=©4–ZÒ4@pn4=ÅŒ4Qu§4E6²5?d4yõ4îˆ4 0å4 °ä4­4†·è4š£Ó4„´4_&35j_˜4þžê4‘\ä45€È4ö_4G4 %‚4[4/i 4¢mñ6(®5kTí5t³5 Ç 48‚Ñ4#054]`p4+Ú4(Á³4 ÙY47È‚5÷ì7ö—6¬Ù5Xÿf5A±4Î¥ä4‹Ú4<]l4àK4¹ÓÈ4º:ï5ø“°5‰h'7k7 :5íbE4ÚŸÅ5ÎS6WòÙ5aÈV4,Éo4V ñ5SÒ‚6|Ž7¢"8v¯Á8áO7kEú76§6 Ø5aŠ™4ïQ–4Qà 4¤­€6QÙ6`¢Ê6¬‰(8H±W9•«ä9Ôê6!û@5K@24Ë _4µÆ‚4†ÕÞ6Õœ5¶/5üƒ6è›6M'ß8fcq6Kó5&þ³6N58¨Œ4¢f]4,Þõ4¨£¦4„g©5PÒB5<…4òý66áÎ|6d4_ƒ4«Ï~4w'4—yE4&M¾4Jç4T4URÙ4|ök5|aö6Â\5¨ 4X÷¥4(ª?44a™44<*É4c´·45î4‡84ÔFµ4Ç^y5)zÜ4‹ÞŒ4%q‹4 ÓR4ø4j_4¸,4L›84Ð#4¢ Í4ð¬À4i­œ4G_A4+Ò-4'ËÛ4Í"4=I4h^4 Èr4ïÙ4™ft4et24q¤4(×P4êÄ4ãy4 éõ46L4 …M4à4®U4©›4¶ã®4ƒ«¾5Õ4‚4åW4ú 4aÎ?4'Û4¨ô4Ó‰4l¾4Š04 "ˆ5k«4víœ4§Ùë5HO4ƒÆ+4[ì4 0Ø4Nyâ4*È4tË4vˆ4!V5¿4Jo§4w%6jÿ5½&¤4,jÙ4T<þ4H„D41| 5 !¿4þ,4 4í²4Qÿc4^í7 {ü6$t4dŒ"5¨ÿÑ5âþ5ÙT4~ïž5û4²ìl4É 6GÛ5šÞA6pO*7 Ư6'r>6®5° Å4£c·4%Á½4£ö4{q­5RÈó6S4ž–'4Èf4–Ì5 87Ž5”%h5†zÐ5ŸÅ4W|4+É–44Î4¦Ë4]54Lz4<=X4HD†6 æ»5Aø4}ã4±Œ4ð 4?y4ý)4|\?4Xª«4Áô–4Ÿ41”_5Î%t5 x4?Ä 4#° 5K4¿»Ž4Є4 É4Ο4‚À4h*È4,äH4‰¬Z4_ö`4™cè4,Á‡4Žp®4oÿê45õ&4 ˜Ú4æ.4tF4M¬‘4x“4óg4\ 4%¦p4 ^4$4Oh"4FR4 Í#4+Ö4>ÜØ4à4314 Wª4,Ïü4¨D4Òž4ܰ4Á†4‡Í4‹À44[5—ŽM4ÏÝë4ÏB4K4l,4á4ª˜4……4{±4û_4ÄI4/0i6S¶Ñ5o74.0³4)ˆ4ñÞ4Âz4ë‰j4by4ïk4‚4k^Ë5¨ü4õt¤4¦§5Zcà5œ@4¢Ó4˜¹4ø® 4P¤J4L“®46ž4ï)4Z(“4@œ4 4¥6i”5—*d4?xm4‚œÔ4ì‹4 œ45þf56Z5m`?4g¦ 4àÍò4מx5\hw6ÙÆ¼5ÜðÍ5 *4z¥ã40`˜4f»444HÝÛ5<)5V9™5Ì»5+å6¶6,ûÚ5iˆè4”Á¬4+>4§4 Áù4ãâ5С5fþF5•Â5ö5]È55ÎDI6ƒCæ5€Û42þq4 åu4‹4Úô46f4<ÄU4¸«!5‰N84ÊÈò4›Ç 5½Þ4R í4 X74 Oh4ï4¯FŒ5¨<Ç4Ý}ˆ4.k¦4]Ty4ƒ÷4M”e4#PÖ4_Á43 C4–Ü41¨4†üu4Õò“4cUv4š¸4Wß4ç|4\e4Ñ4*Í>4154U 464 g4uQ4šŸ4&þ4½d4 ´ 4 œg4!¡â4Vø4#49±i4®`44²4&f4  ¸4o"4qO4û52Û<4j]q4¢Ð404>]4%ÿ4º$4äÍ4oš040^4*÷4ì4WJ›4"m4r4 N4¶H6´Œ4Â,C4?4É›t40Fv4 ²é4B4tÑ4H~õ4rH4%4É4žh¤4IT 4Åtˆ4A¨[4 ¼ˆ4Uû×4Dƒu4éÃ4 aÿ4ÆÎ4,ü|4Px4¤ëý48_u4~°4˲49°Õ4×–4£È*4*X4 üî5Oè)4ãyó4¥¶4«ÏZ4tW4 ùÇ4ež4XŠÃ4Æ@M5†Û5EÔS4«Çv4D]¼41Ñ4=ä4 /)4äŽ43IÉ4i4“î5ƒc–5èÍ/5ÁVS5‰4/Ä4 —4´Ç47k49µ—5Øû‘5×½C5}î4‹¥b4¯Í4Ûê5[ÉW4¡"Š4íŠ4f4SŸ4ë 4¹fF4ˆm4P¯F4‰Š¤4OíÞ44˜.4^×´4]4(Ø4ão4$”4…a4„‘4%ÌX4 »=4%Zª47ã44w4 ¼4ò4>fI4òµ4S?%4bõö4#Oq4‹u4©ô4"w44 d4:Ù4˜4 C4±Ï4· 4â+4ãx4N`4ê14±{4$'F4Z§4~¦464þG4¿;4¤W4’e4 ®4Õ‘4#/4—!4ªü4T¼4U÷4±K46±4HÜ4ƒ4cU¤4'AO4¯¯4e4°°4t«4$Š‚4 Ô4ŸÈ4é§4Ï»4·Æ4`s^4&sö4 .4úÐ4E4ô(4K[Û4Å> 4$ž4L#4¶:4a!4"KŽ4w©48/4 Ú74jý4nñ4Æ»4§¥4LL46)4A{4y¼4 54¾4 Wä4ˆ 44ž946T4 É,4ë–4f*4”¤5cèª4 „Ñ4eg4d²4 Ü4'í-4d‹4‹Vú4š¤4\©>4}›4xÏ4,ì–4 ¨q4ÀY½4'ÌÈ4b4;þý4¢›ü4¼<Ñ4ÞU4‹…e4g[4ø³4Èn4É4†Íî5éàã56.f4vá48ž4O“)4WŠ 4FQ³4oA4‰Å4°ê4H“~4ц5‹º™4·à4'Çs4ñÅ4+¤)4{ø4 ää4¦V41w^4 :€4 Kd42…4N0`4Øb484#äF4«©Ì4-164 -4ý4QA 4e4i6—4‹c4õW4¤I4ÍC49J43Åà4 ¸ž4\y474»ó+4ßlZ4"Õ 48þ4Ö54p}4»š4œ\44i4Š#4£Ã4í4Ü'4Éæ?OzŽ?O‘‰?O’’?ON?N¦Î?M¡1?L ¤?KØ3?K8Ý?J”?J&?Iéë?OÞe?PÈ?Pï?OØ~?Në·?M½?Ln??K«U?K¯?IÎü?IV?Ip¯?PY¶?P›?Pz\?P@a?OO5?MÜ*?L"?K,+?Jûƒ?Iâ?I?I1 ?PÕ–?QÚF?P„/?Oï?O !?N{Ä?Kâô?J¤`?J{?Ih‚?IÝ?IC?Pñ¸?Qum?PüÐ?P–Ø?P×?Nà?KcÌ?JMÌ?Iší?I.Æ?H÷ ?HÜá?Pí ?Q;â?Qz~?QÛ?Q¾î?N‰n?K"P?JVè?Id?Hñ1?HÛ¦?HÂJ?PÎË?Q&?Qd,?P÷‚?P±j?Ožˆ?L= ?JÞ9?Há{?HœK?H¡ ?H—ª?P¥"?PÔb?PþÓ?PÅ?P\·?P#Ð?LÍÕ?KlU?H¹ý?H›?H¬‹?Hžo?Pˆ9?P—?P–o?PzL?P'D?OÁ?Lê£?KœŠ?I%®?I­?IQú?Hø"?PN™?PNn?P9~?P û?O•µ?Noò?Lö?KÏO?J_x?IÏ?Iõ?Ià%?Oçœ?Oû"?OÞK?OxG?N͸?MîX?M(`?LG[?K Ã?J#G?J"?J,N?O’?O¥õ?O‚a?O»?NLŸ?M­}?M2³?Lï?K×#?IÙ$?Iú0?JOR?O¾—?Oà–?Oõ?OÁ®?Nè!?M¥b?L˜Ý?Kª›?JÏ?JÍ?I¾ ?I™ˆ?P6?Pg?P˜?P¸ö?OqQ?Mf?L`?I K?Hƒ?HNö?HGŽ?H0B?Q¦Ô?R(Ä?S%…?Qà§?QP]?Ou¼?Kß_?I£Ø?H;©?Göî?H€4?Hs)?QŒé?R%Ž?Sr?QúÇ?P ?P?L·?J¦¢?G°d?G]³?G¿d?Gï¶?QVt?QËœ?RyÝ?Ro?Q‰?P‰Ï?L˜5?Jï„?HgÛ?G/?G^¿?GÒ\?Q¿?Qf†?Q¾?QèÊ?Q¹®?Oœ?L0’?Jé”?H¬]?Gúˆ?H7?HDh?PÌ—?Q8?Q0š?Qnç?QU§?Mªá?L7«?K6Ž?I±á?HøZ?Ié?I`Š?P]š?P¼?PsÏ?P&–?O’ª?MÛ&?M2?L<$?I¾?Ih\?I§?J”?OýY?PSë?OæÁ?Og¸?N…3?M¯:?MB£?Lš?J‰$?IÉE?IÞÞ?Jë?Pg?P6I?Plü?Px?O?M–Þ?LÆ?K¤W?J&s?I—Ú?IQË?IBæ?P…Þ?PÑC?Q1?R¶?Oïo?Lö¤?Lf?KBº?I{…?Hó³?H¯?H ö?Qï?Qz?Qæž?QïV?Q3f?Mò©?KñÉ?J]x?IF?H¾H?H¬?GÍ ?Q¼q?R!°?S#p?R0e?PœF?O¨?LõÖ?I?Gá?Hl‘?Gw_?Fë ?R)ò?RZ8?S3r?Fë£?Rü?RÄ`?Sýr?Tt ?QÚÐ?P=?Lïµ?J…‹?H)Ø?E«i?E¿v?F»/?Q‹¿?R.d?SÏ?S¬Á?RV?OÌ?Lã?IÎØ?H€è?Fæˆ?FÄ_?G=G?Q&3?Q¾6?RX(?Rܾ?Q’?NŒ?KàÉ?Jo]?I?ÿ?H•?GÖ©?H?PýK?Që™?Q'??P ¾?O¸ñ?Mûâ?L°ƒ?Kz½?IÀ°?Hì?H«?Hݼ?P§F?Q‹«?PXF?O ô?NuÄ?MœÊ?Läu?Kðg?Jp¥?I´?IN ?Iuö?PTA?PŽØ?P»+?P¡?O‰*?Mê?M\š?Ll?I‡?Hì?Há{?Hé¾?PìŽ?QJa?QŠ5?QÐ?P–S?MÏþ?M_Þ?KÐg?GäP?Gæ9?H a?H-U?Q™0?RC™?Rb?R;N?Q]V?O3«?L—7?JxI?H ,?GP¨?G3Í?GIA?RW†?S“œ?Sæ¡?RÍ$?Qù?P–ñ?MÊŠ?HÌ?F`?F§@?Fž7?FqÁ?RÌ?S¡ë?T¶z?TÍ?S'ƒ?R"=?Ov?F¯9?DòI?EQ]?FŠ0?FUj?S‹?TA&?UµS?Tv¥?U.w?SG¬?N¢?F[$?CŸÆ?D$¯?EŠÖ?Fhž?S ?T$Ø?U³É?U‚»?UŠÞ?Ràñ?N:?GŠT?C½?CÛº?DÀŽ?FQ¤?R´Ý?S¯+?U Ö?V3È?Ub?QÙr?LQT?HDN?FVv?Dê]?D-V?E·ù?RÛ?R÷\?T0l?UÙ?T^?QÌÌ?K.W?H‰4?H”?E~°?E(6?F9™?QšÓ?R[£?Sª?Sxv?Qëe?PM‹?K^(?I¼l?Ib?FÒw?Fh{?Fíž?QY—?Qø¡?RÕ?Q[?Oªc?N¡Y?L‰?KBÝ?IfZ?HD?G–ø?GÉ¢?Pòs?Qd1?Qšë?P§M?N4V?M¢R?LÐ|?KÐ??J4Â?I+'?H”r?H™S?P­Ê?PêÜ?Q)?PÏn?P<û?Míu?L³ ?K]?I¢=?HÆ!?H•X?HŸì?Qb/?QÞº?Qà?R(?Qà,?M&?L•¢?K<Ó?H¹?Gia?G‡;?GÊÚ?ReP?SÕF?Raé?Rì8?Q±4?Oá?Lí>?K ˜?GØÁ?Eй?F"»?FÉÌ?SRP?T>?T<?T‰>?R–¥?PÍÆ?N 9?Hß5?Eç¡?D£¨?Dù}?EµY?TNÆ?Sóº?U¾ñ?VH?V ö?S«Ö?Ov?Fo?B•ˆ?BwÀ?C¬y?E9ö?Tr˜?T°å?XN?Vîý?YMR?V« ?R§]?C8º?=?H±?F4I?FÐb?Q!?Qká?QÍM?Pà6?N×c?N#?M?L[!?I°¼?HuÉ?GÎî?HŠ?Pç>?Q €?Q4ú?Pû«?P<™?Mþ‹?L¿&?Kæ?I«e?Hz™?HMÈ?HpË€?>ÔÆ?@(î?BÒ?S4Î?Tu‡?V‘?X™?UÚ?T‰y?MA—?HZ?BW>?Bf¤?CÔ?Deõ?RxP?SAá?T@¾?U!x?S•P?QQ?L„^?IÅî?F&|?E––?EM?Eª¾?QÝ ?RXÆ?RÔO?R„F?Q¬ù?O}?L…#?KV:?H6,?GGG?Fq‰?FÇ‘?Q>´?Q•n?R\??P š?OCÀ?N/á?Lê¼?Kþã?IñŽ?Hf,?G˜U?GÖT?Qä?Q.Œ?QM+?QEZ?OÅß?Mž?L¾¶?K½b?IþÁ?Il%?Gù_?H#R?Q±«?QüV?Rt’?RÀÛ?PT¯?NNê?Miì?J¡ô?H ?Gñ?FT?Eª?R;f?R™D?Qæ?R¦?Q¤?O(?LÎ+?J…¶?Hå ?GŽ·?Fæ±?Fë+?QUV?Qb¹?Q™§?P@v?OGR?N»?Lĵ?K~î?Jcy?HÍi?G¸?Gù|?QÅ?Q?Q)‡?Pòb?O_s?Mk/?L”s?K¬0?Jdá?JA|?H?H½?Q›?QÙt?R6?R^?PR?N0ž?L ]?Kƒ‚?I?HB~?G¯ó?GÐè?R²y?Rœî?SB$?Sn?QÜÜ?Pk@?LJ”?J Ý?G›?F®J?F¼Ý?G6Í?Sݰ?Sgý?TV?T˜Æ?Sôž?S|°?Mg?G;­?FÓ?Dìo?Dè–?E’Š?T%²?U!á?UÏH?WØœ?V¢¡?Xü?Mó?BÛ?Bìð?Bk?Cʾ?CR4?T…?U“û?Wzß?YÐ?[õ?\ü?N–Æ?@Pø??'\?>‹?AXš?C}X?T«?V?XQ?Xªû?Y‡°?\OÇ?V¡½?>4 ?:d?=¥º?A®?C·Ð?TC~?V¯€?Uµå?V€‚?X=?V\ß?T=M?B•Û??wÚ?@]É?A»á?DÖ?S^D?TÈ5?Tn?U¸ž?UY?RGŒ?Oì?G¦?C7¦?C`‹?Dd8?E‹??RšØ?SU?Rþô?SK@?RL??Pç?N†¹?HbÎ?F9?FS?F«û?Fx?R#8?RB?QQý?Q¶:?Pxù?NŒø?Læc?Jnõ?Hï”?Gá!?G¶>?Gˆ?Q›b?P^f?PŽ>?P¾a?O`ä?MV¾?Lp?KK¼?J8?I…?H‰?Hˆ?Pð³?Pø=?Q0ó?P;?N½?MCÍ?LyY?K­n?J›?Ií?HÜ1?H P?Qq?Q°.?Rsæ?Q`Ê?P?LêØ?KËó?K4&?Iß&?Hÿ'?H3›?H?R#þ?Rs?Sƒ_?Qú¦?PfK?Næ‡?KñÑ?JQN?HûW?Hk÷?G2d?G; ?Rã?SK?TlÒ?Rtë?QÍó?QŸ´?L§j?H„?H â?G;?EÊX?F‡?S…š?Sá?T™ñ?Tž”?Sìm?T…}?Ká?F÷w?Fÿ(?D{©?DJk?D»¡?T…?SäÚ?U>?V¦á?TW#?T¥ä?O–?FN×?ECÕ?Aà‡?BƒV?D7‘?TZ·?T2•?UÅÌ?VYŠ?T)ð?S÷y?QMS?FÈ?C™ÿ?A;J?BW®?Dsä?SÉ1?TZÒ?TÕK?Tæ?S®M?R›÷?Q»?G'ø?Dï?Bvê?CPY?E<Ø?S Ë?T Í?Sk½?SVü?RTÞ?P¬‘?NÉâ?IÎ?F'·?D¿P?EnU?Fˆ?RRÄ?SS¤?Rrò?Qúû?P‰Þ?O {?Mݾ?I܇?GŒò?Gô?Güf?G î?Qšý?QËÐ?QE’?Póë?O~Ð?MëR?LÎ,?K ?IâÌ?Ha?H©??H?Q%ý?P,\?PM©?P?[?NÒÇ?MWn?Lds?Kej?Jsw?I]3?IRj?I†I?P©¬?P‹?P~ù?O˜õ?N=2?MH?L¬u?L'?JÆi?I÷s?Icz?Iõ?Q#Y?Q)2?Q£?Q7¬?ND‚?L¸?L+÷?KŠÚ?Jz?IŸÃ?HºÆ?HpÕ?QÊ¡?Q¹É?RVß?R:ê?N÷?MkY?LNª?K/?Jª?IÑ]?G¨P?G]H?R]?RÚ¿?S^f?P²ï?O¨Ä?O§‹?L¹Ž?Iͧ?IW ?Ik?QÕ_?PïR?Ow>?NW?MŠ'?L‰d?K‘?JÄ¡?H…Ç?Hé?HÆ6?P†?PP¼?P{?O´?N=?MQ¬?LKØ?KsÊ?J»Ì?I½k?IÄ‚?J?P4ÿ?Oô)?O ?Núý?N$¼?MtP?M>?Mi?J¨?IÐt?I­È?I|?P̶?PL?P#?O™£?S3E?QèÃ?Qó?PÔ>?P£?O~á?MÒ7?JýH?H¾_?GÁ®?Fè?G‚?Rµ?Rc¬?R)?Q…?O‘?O€Ð?MÕ®?JùY?H‹ù?F®ß?F½?Gm?RÔ?R.ö?SÑ?Pîc?N@ø?N???M?K9‰?Iƒ?G-?Gù?GS”?R!?Qií?Qòâ?P4Ù?N¸×?Mù$?L…¿?K/?I¼?HH»?HcN?GÂü?Q?é?Qdá?Pïj?OaÄ?Ns÷?M­¿?L‹?K+y?JÇ?IK?ItR?Hh?Q37?P°¦?O¶r?NÕ†?NH’?M–m?Lo?Kh,?J£r?IÈx?IÕw?IVŸ?P‚ý?O°–?OZâ?Oj?Na_?Mó?Kî÷?K1ä?Jƃ?JV+?JK—?J†?OÂd?OŒ±?O?Nwi?Mü2?M~ã?M ,?L—E?K8 ?Jo4?JS?IÝX?PJX?P\â?O8|?N6À?MáÜ?Mv?LüÚ?Lu??KV=?J+ø?I¨å?Im9?QG?P?é?Ocu?NO¶?Môl?Mˆ©?Lõ”?L«w?K¨®?I›Š?I Ö?Hâˆ?Qî?PÖ”?PÚ?N…ç?N=?MÈ2?LìÁ?L ?K#š?J;Æ?I$Ø?Hm5?Qh]?PéÂ?P'ê?OSÉ?NÈT?NÅ?L÷e?K¸d?J§ò?J?I)?IMy?QÄ»?QA?P3?OŠ?Nè ?N?§?M=?K¯?J$Ç?I%B?H”™?H¦¬?R(Â?QúL?P9´?O?Ÿ?Nƒ´?NO?M;?KœG?Iê{?H®·?HZ¨?HV?QþT?Q³Ì?QÀ ?N•?M‹?Ms«?LÈ2?K~å?J!Ó?Hö‹?H£¤?HiZ?Qz.?PÁã?Qü?N}´?My ?MS$?L ?K`Ã?JEù?I‰b?I¢¨?H˜v?P¥?PPË?P3^?NÚ”?Mð?M¡T?LÒÿ?K?JZ ?Jë?Ju£?I?P¼??Oqü?O$‰?N±¶?N&?M›s?L’`?KTÄ?J´i?J›Ù?KG`?J ¡?P).?O¿?Nàƒ?N­>?NU¡?Mû?LB?K;í?Jå]?JÌÁ?Jø¶?JgŽ !    ¼¨ ¶HEèP !    ¼Ã ¶HìFèP !    ¼Þ ¶HÔHèP !    ¼ù ¶H¼JèP!?@4 4ÿ©¼©¶H€!?@4 4ÿ©©¶H€SNODT+D*xü¤ ¦¨¼$(¼<ÀÌ%>ÒÊž>Ó‚W>Ô-“>ÔÔC>Ôàê>ÔÑ‘>Ô·e>Ô”Q>ÔŸ¨>Ô!>ÓVÌ>Ò„ý>Ò£M>ÓŠä>Ôq>ÕžŒ>Ö­>Õâ,>Õ>Õ?j>ÖH™>Ôþ8>ÓAª>ÒPÛ>Ò\>Ó>Ôi>Õ9Â>Õ÷ž>Ö¢>Õô‚>Ôèì>Ó\E>Òêž>Òl>Ñàt>Ѩã>Ô/Æ>Ô"^>Óv>Ô"k>Õkz>Õœ>Óð÷>ÑïY>ÑÄ'>ѵ:>Ñ_>ÏéÅ>ÏPì>Ð2â>Ñ•O>Ô[Ž>Ôý>Ó8B>Ò‡Ú>ѦÏ>Ñ0ý>Ðöl>Чˆ>Î0>ÍÔ¡>Ϊ¹>Ï›è>Ñ>ÑFM>ÐËÉ>ÐÉn>Ðhö>Ïçu>ϰÙ>Ïz›>Ìǵ>Ìwó>Ì[@>Ì0>ɹ¿>Ì^î>Λx>Ï®»>Î̃>Î S>͇³>Í2d>Ë]è>ÊÜv>ʸ>ɦÄ>É!c>Ê>V>Ë->ÌEJ>ËøT>ËÝŸ>Ëæ">Ëî‘>Ê|>Éž>>ÈÓ7>È?Ô>ÇÙ”>Æê§>Ç,ù>Ç”Ã>ÈrK>Éh«>ÊÊ$>Ë…Ÿ>É"ú>È’L>ÇÕÿ>Æö>>ÅËY>ÂØŸ>Ã0>Þ&>ÅßÁ>Ç’€>ÈÎó>ʾ“>ÈO>ǯÎ>Æø >Åм>Ä7.>ÃDD>î>Ä•w>ÅäÐ>Æ?>ƘT>Ç4k>Ç•ü>ÆíÕ>Æ5›>ÅP’>ÄNÝ>ÃÒ„>Ã÷Q>Å.‡>Æ´>Æ7â>Æ5R>Æff>ÓÂ>Ô^‰>ÕA%>ÖT>ÖŽ‹>Öwé>Ö^º>Ö]>Õ¤Ü>ÔÛ2>Ó÷q>Òý€>Ópà>Ô†m>Õ½}>×ʾ>ØÄ¾>Ø8k>×{‡>ÖÒ]>ÖgŒ>Õ^å>Ô#™>Ó–>Ó9®>Ôuü>Õ‡C>Ö¿Œ>Øõ>Ù¢ª>Øg'>Ö½«>Õk>Ô²i>Óâ‰>Òùò>Òª¯>Ôoð>Õé>Ô`ò>Õ>ØŸÏ>Ø(ž>ÖWÕ>Ôzm>ÓÆ:>Ó:»>Ò‡¨>ÐÂ>а–>Ñ—Ú>ÒÐ@>Õ˜–>ÕôÆ>Õˆ³>Ô~ê>Ó@>ÒCÈ>Ñú)>Ñà >ÍíÉ>ÎŽ >ÑI>Ñ–â>Ðåc>ÏXý>Щ>Ñí >Ñwu>б§>Ñ.Ð>Ѹô>ÌaK>Ìg›>Í6>Í1>˵)>Í.`>ΞÎ>З*>Ïœ£>ÎUø≯š>Ë«Ò>Êà>Êv>ÇÐ8>È>Ô>ÉGs>ÊyI>ÊI‰>Êèj>̆>ËÍû>Ë p>ÊÕÃ>É–*>ÈÍç>Çn<>Ç.:>ÈA…>ÅûÎ>ÅyD>Åy®>Æ6>Ç‘ˆ>É2=>Ê5%>Ȩ >Çäƒ>ÆÉ>ÅÍž>Åï>Á"—>Á2»>ÁÏ>ÃØj>ÅÛ.>Ç–>Éóp>ÇëQ>Ç[´>Åðó>ÃWt>¿b¯>À÷>¶>Ĩ>ÄV>Å\>ÆÉ>DzY>ǽ>ÆRš>Å3|>Ù>Á£»>Ám]>ÁŸž>ÂTS>Ãß¶>į5>Åe0>Æe>ÔDë>Õ;Ô>ÖP+>Øh¿>ØÏ³>Ø¡¥>ØÈ>Ø|æ>×(1>ÕÍ;>ÔÅ1>Ó±•>ÔL™>ÕŽ\>×{>ÚD`>Ûî¥>ÚÞ>Ú&>Ù!‡>×Åì>Öx5>Õ6ù>ÓóT>Ô14>ÕƒT>Öö@>Ö»E>ÙG3>Ûƒ>Ú5©>Øh>׌Û>×2K>ÖÚ>ÔÓ>Ôø¥>×c>בÆ>Ö©¦>Ú"A>ÜÌ>ÙÎý>×<{>ÖÀÛ>Õ¦þ>Óó7>ÑŽ >Òj>ÒGÏ>Ó2î>ÕÓL>Ø’Ê>Ù/ƒ>×=5>ÕDØ>Ò•Ž>ÒDÿ>ÒŒl>Î!>ÏÆ<>Ò%Š>Ñçþ>Ò\×>ÑêK>Ó h>Óã´>Òã>Ñ#H>б >Ðâ>Ëöï>ËàÊ>Í#›>ξº>Íå,>ÍA.>Íp>ЗG>ÐOk>ÎÐ">ÍD>̨>Êj$>ÉoŸ>Ç\í>ÉùA>ÉÇ >ÈÑÌ>Æœ<>Ç€â>Ëmì>˹7>ÊÀl>Ê_†>É.Ù>ÈÃ>Æe>ÄŒ>Åà~>Å@G>ÃÁû>ÂA%>ÂÍ>ÅIö>Lj>È©_>È%>Ç;Ú>ÅÏ>Ä2{>­®>¾ç>¾¬>¿×X>Áuþ>ò >ÅÔÖ>Ç >Ç<õ>ÇY>Å7%>À—‚>¼°Ì>¼ýY>¾—š>À¥P>Á‘:>ÃÈ>ÄÆö>Æ\‚>Æk^>ű´>ĉ>Âx>¿ >¾éÁ>¿UÆ>À@)>Áš‡>Ã.>ÄD>År)>Ôà´>Õõœ>׬>ØÍ >Ú1>Úk,>ÛŸ{>Ûéi>Ù5Î>Öáç>Õ†à>ÔM‰>ÕÍ>Ö•‘>Ø7]>Ú·a>ÜïÜ>Ü >ܰ>Ü#)>Ùô¢>×Ô(>Ö+c>Ô³'>Õ]>×ä>ØÌF>Ùv§>Û‚>ÝÜ0>ÜAð>Ù#>Ù²G>ز>Öò˜>Õ#ÿ>Ô1s>Ö¡’>ÙèH>Ú¾ª>Û+>Ýï>àn¶>Ýh>Ù²ž>ØB!>×/6>ÕšH>ÑñÒ>ÒMg>Ó§t>Ö^8>Ù·ƒ>ÜÓ>ß.û>Û¦Ø>ØQ¼>Õ„Ÿ>Õ%>ÕZí>ÎÀ.>Ïå[>Ñò¶>Ò¿#>Ö¡P>ØÃX>Ø+«>×%«>ÔÈÌ>Òo%>Ð]^>Л|>Ë´¼>Ëlã>Ë”‹>͆5>Ζª>ÍÓ¼>ÍÚÏ>ÏÑ»>ϰ>Ï>Í„$>Íi>Ê„>É>ÇÂ*>ÉÛ>ȉÄ>ƤÕ>Á·8>ÃjÛ>É.½>Ës;>ÊWû>É!=>È®ò>ÇYx>Åš«>Ã%ø>Àá¶>Á$>¿ï>¿~Š>ÂÛz>Ãøî>ÅÛ$>ÆãF>Çt¨>Æ?þ>Å ý>Â9A>¼Ýé>¹ò>ºié>¼žC>¾Šé>Á\”>Ãýq>ÅPg>Æg…>Å Ý>Ä >¿Øu>»Q4>·î“>º/(>½W >½ß>À} >Ã@>ÄÉ•>ű>Ä{v>ÁöP>À–\>½×P>»¦Ú>¼Oc>½u=>¿.>ÀÚ%>ÂÚƒ>ÄmU>ÕÖjÑ>×—>ÙW >Ûà>ÛÛæ>Û¸¨>ÛA„>Ù©T>׸ÿ>Ö>Ô®Y>Õ³z>×™>ÙG~>Ûû6>ß…>Þ‘ó>Þ:>݉Ì>Û¹J>Ùƒ>Öåb>Õ:w>Ö+Õ>Ú5º>Û=>Ü‹Ï>Ü>ß%>ßúC>߯c>ÞCt>Ú‹å>ׯè>Õ«&>Õo¨>Ö;«>ÙÀÁ>Ýa>ß>ây½>ä¸9>à}%>ÜG >ÚV>>×ýE>ÕãÙ>ÓX©>Ó‚>Õ¡½>Ù&a>áõ >æÐY>çLâ>ãšU>ÞÎï>Ù >×±>Õî>Îè>>Ð&>Ô”>Õú>Ü'9>ã.+>é¥Ä>ãH>×[•>ÓÀÀ>ÒIK>Ò<û>Êoˆ>ËQì>Ê—x>ÌèY>Íq>Ѹ¤>Òà0>Ò²Þ>ÐH{>Ðn>ÏP>Ͱ>É~4>ȇ–>Æha>Ƶˆ>ɳ0>¾%%>±Iá>¹qµ>Å?S>Èé:>É'Â>É{¢>Èo>Æcÿ>ÃÜ·>¿²ž>¶ÏÁ>³6§>³™k>·^>½„Ë>Â$T>ÃÖp>ůŒ>ÆËË>Åø>‰I>¾MM>¹>¶Lº>´Ú>·¿o>»Â->À/[>ÂŒ>ÈÓ>ÅÈÀ>ÄIÚ>Â"7>¾/Q>¸–>²ÖÖ>¶Xµ>¼.j>¼š€>¾‘Ê>Á>Ä%>ÅC>ó2>ÁÇ>¿lò>»k >¸V\>¹1¢>ºEd>½–>½óW>Á">ÃÏ>ÕW4>Ö‹”>×ã]>ÙÕb>ÜV˜>Ür>ÜA*>ÛÒ»>Ú\%>ØZÕ>Ö’Í>Õ ÷>Õß/>ײ*>Ùµ?>Ü”5>ÞÇç>ß|Š>á>Þ;p>Û0Î>Ùö>×Rï>Õ¿,>ÖY>Ùb‰>Û\ƒ>à‰Ž>áUE>áü°>â->â;>ß?Ù>ÛAo>Ø6F>Õø>ÕÆ‚>Ö¡Ö>Ù Þ>ÝŠh>á¥A>ç.>è,÷>äô¸>á†a>Þ^‹>Ù~>Õ£B>Òjì>Ô.Ñ>Ø n>Û²Ø>äü >ðÄ:>ñf>èNE>à>ÚŸŒ>×—>ÔØ6>Ï»>ÐÚÍ>Õº²>Ø3‹>ã´O>ô¯‚>ÿN>ðS>ÝÕ?>Ôí>Ò×>ÑðI>Ëù›>Ì‹×>Ìó >Íi>Ñê>>Ú½Ä>êG>ÔM¯>Ò* >Ò >>ÐA\>Î>D>Éæù>ÈÞ,>ÆjC>ÅÊ>À¢p>¥×>’Ã9>± ”>Åô>Ưp>ÈŠ>É­/>Ç·7>ÅrÇ>Ân>¾–d>²ÂÝ>¦ó…>£€L>­ i>µ >¼êÅ>ÂÇ€>ÅÞN>ÆZN>Ãû>¿•³>¹Ô,>µE×>®Þ>­å3>°3a>¶›W>½0>ÁrÂ>ÃÞ >Åz´>ô >ÀÞ÷>»G>¸‰>³ðy>´;i>·Í>ºÝ>¾t_>ÀÂs>Âöÿ>ĪÔ>Ã!‡>Â4>½³û>¸˜>¶ï >·³A>¸õ1>¼Z–>¾¿L>Á‡>Ã,:>ÕÇ>Ö7Y>×Õ>ÚÁÔ>Ün¹>Ü(Ä>ÛÛô>Û5Æ>ÙØI>Ù&d>×: >Õ’T>Õµa>×[:>ÙŸ×>ܱg>ÝÍ·>ß;Ë>à­n>ÞKb>Û%d>×î¢>×”‹>×E»>Ö§Â>Ø^ï>Ú~›>Þ)F>à€b>ãåf>äj$>â ·>Þ1…>Úsx>Ø ê>ÖUù>ÕТ>×pá>Ù{>Ü7¡>âz>êrÿ>ê¤Ö>çÚ>á À>Ü¥‡>Ø4ù>Ô >Ñtµ>Ö7>Ù}Õ>Þ‰%>è`g>ô™>ó8^>èf>ßÞz>Ú¸>׬\>Ô›>ÏYW>Ïé©>Ó¬ >Ù >æ>üFU?,^>óÌ“>à1p>Ö‡>Ò#ž>ÒÏ>Í\Z>ÍÝæ>ÎÓµ>Îè>ͳ%>Û?—?|Ÿ>àÚ >Óøs>Ñ· >ÏçA>Κ´>Ê» >Êžþ>ÇK\>´Z>½­‘>›>w˜>«G†>Ã^‰>ÇÓR>Éáï>É{H>ÇMp>Ãkæ>ÀÞ¯>¼° >²`ý>¨C >¢Zz>§H*>³ü>»óu>« >Å›p>Æ\á>ÃÐ>ÀZ>»(@>³5>­'ˆ>ªÜž>¬Å>>´ú0>¼Šã>ÁœY>Ä$>Å×¶>ÄQ4>ÀÙ>»à*>·>±ý>±kl>´ç>ºh2>¾Àõ>¿b>Â>>ÄK>Á(>ÀúŒ>½ô>ºº>·Å²>·ðk>¹©‘>¼ŸE>¿- >ÀÐÀ>ÂóK>Ô¸¾>ÕÐÅ>×£3>Úkð>ÛÕD>ÛoÞ>ÚÛÎ>Ú5>Ù#‚>ÙM@>×:Ê>ÕO >ÕMe>ÖÍ">Øæ >Úð>Ü×ã>Þª>Þ„‰>ÞA°>Û°Ë>׃>Ö·:>ÖD’>Õö>×oŽ>Ùˆz>Û®I>Þâv>ã¬ê>âÅ>ß²û>ÜÍ~>Ù¹ >×Û>Ö¢²>Õ±>Ö¸ª>ØÓž>ÛÜ >à >çL_>ç¼~>âÏÁ>ߢý>Ü#a>Ø`Ù>ÔüÌ>Ñœt>Ô·c>×Ãî>ݾ¨>ãùÚ>êØ¼>ìùù>æMÃ>ÞÿŸ>Ú¶.>ØŸ¡>Ôl:>Ð.ñ>Ñ:s>ÔqI>×üé>â“‹>ì6>ígÿ>ègÉ>àI>Ôí2>Ïîµ>Ðeã>Íõ>Ψ¥>Ϥ>Íýû>Ìÿ>Íqë>ÝÏk>Úêö>Î}>Íú(>Ìê£>ÍJ/>Êíƒ>Ëý”>ÇÞž>Ã{ >À©>³MP>­s¶>µ.º>ÁÌq>Ç×å>ÉÉõÜ>Ç™Þ>ò|>ÁQ—>¿Â˜>¹^¥>°Ñ>«¦>²¤4>¹=5>¾dk>Ãà1>Çå>ÆzŠ>Ãù!>ÀÌA>»êô>µ¼³>²¸V>¯µe>²Hï>¸ª>¾,T>“±>Ä>­>Æ=>Ä^>À¶,>½¬c>¹Ò0>µ „>±ÿ5>µ¢î>»@>¿F>Á`>Ã3½>ÅsS>Â`>À€>¾t>¼P >¹Õÿ>¹ ó>º¯Ó>½\A>¿ÑB>Á‡>Ãù>Ô%½>ÕFw>Ø` >Ú3D>ÛÃ>Ú¨G>Ù§€>ØÛœ>ØT>×o>ÖI>Ô³&>ÔÐå>ÖI >ØÄ>Ûe„>ÞM>Ý•\>Ü'Ù>ÛN­>ÙÒ.>×Ú‰>Ö‡œ>Õc#>Ôþ«>Ö¯>ؼñ>Ùý>Ú|>Þ•Þ>Þ“’>Üé >Û&H>Ú >Ø Ó>ÖRQ>Óñ÷>Õ¥>×s.>ØŠK>ÛL>ámž>áÿ">ÞÚÁ>ÜÁæ>ÙÁ>ÖØÛ>ÕbY>Ò>R>Ósy>Ó§q>×Á->Ü‹>ãq>â¥i>ßKä>݆í>؉¼>ÕI}>Ѥ^>Òð>Òò>Ó;Ë>×*Á>Ø€ >Ø]ž>Ü®Ê>Üø>Øé">Ó´´>ÐA¢>Ï$X>ÍJm>ÍÈý>Ï2>Îß>Î*O>ÍÆŽ>Ñ ±>ÒÆ(>ÍÆ’>Ìöš>Ìô>Ìã>ÊWh>Êu`>È»M>Åyî>Ãû^>Á—•>À†|>Áä>Ã*ð>ÆðÝ>É3æ>Êá˜>ȼ>Ç€s>Å->ÁÂZ>¾áò>ºØ‡>·k‹>º5M>¼»m>Á}Å>Åè)>È—€>Æ}×>Å$û>Ã*¬>Àv¾>»ð‚>¸Ä>· Ÿ>¸|¨>¼4>À±µ>Åç'>Æ« >Űœ>µó>À@Ê>¾4]>¼1>¹Õ¢>·†Í>¹è>¾¢à>Àê‘>ÂO3>Ä5Õ>Å•‚>¸>À¶º>¿RH>½ï>¼À%>»Õø>¼|1>½­Ë>ÀYU>Á¸¸>Ä{á>ÓÓ>ÔÃÿ>×KÄ>Øè>Ù:¿>Øêû>ذ>׃Õ>Öõc>Ö%A>Õ >Óú>>Ô›>ÕÝK>Øu>ÛÀú>Û§ß>Ú–”>Ùt,>Øaü>×ÌÆ>×&c>Õáe>ÔŒt>ÔíÏ>Ö:>×rû>ÙÁÛ>Ùnæ>Ú«v>Ú»å>ÙÖ¯>Ù*Ž>Ù†Î>×€k>Õ§>ÓF>Õ7‰>×N˜>Óñ˜>×'n>Üv_>ܵï>Ú–>ØÛ–>×W)>ÕHx>Óü>ÒË>Ò2&>Ѩ>Óz0>וö>Û[Ð>ÚùH>Ù§‚>×Ä|>Ô”h>Ò×>Ñk >Ñ]þ>ÒeX>ÑÍ×>Óþa>ÓËW>ÓŠ >×ßN>×:ï>Ô¦&>ЋÈ>Ï.>έ=>ËÈ|>Ì‘>Ï­Ü>Ï·²>ÎÌw>Íö‹>Ρ°>Ïý>Ï€¿>Í]f>̆r>ÌŸÈ>ʯ >Ê?…>Ê‹®>ɱW>É J>Æåä>Ãëp>Æ£ˆ>ÉÃ9>É‚…>Éïà>ÊÐq>É >È >Å¿>Âõ«>ÃC0>ªU>¿äf>¾»Â>¿\•>Ã\>ÇJ¬>É\>Ç[³>Ç}`>Ƽž>Ãil>¿TK>¼u0>½ e>¾ó>¿‘*>¸K>Æ>Ç >Æ%+>ÄIo>ÁI>¿øk>¾Þy>½Ú,>½Ã·>¿Ñ>Âæ>ÂüŸ>Ä/Ù>Æ U>Å d>ÁùŸ>À}±>Á >Àkê>¿ô5>¿mñ>¿A>¿U™>Á¢^>Ãtš>Æ&ª>Ôµ>ÔÏ·>ÕùV>ÖÙ2>Öø>Öâÿ>ÖžQ>× µ>Öb¯>ÕD>Ô,ø>ÓEH>Ôö >ÕÄö>Ö¿q>Ø¡>×ß×>ׇÇ>Öè>Ôæ]>Õšð>Õµ¼>ÔÌ0>Ó¥Ô>×=>Ö85>Öo>×?>×/â>×£>×}n>× .>×U–>×KH>ÕN>Ó¼>Ð>Ó#r>ÕÛ>Ôe}>Õ•]>×hg>×o‰>Ö*>ÕÃ>Õ~ >Ô^B>Ó >Ѫ>Ñ$w>Ðã>Òbß>ÔjF>Öî>Ö8 >ÕuÉ>Óø©>ÓYù>Òœ)>Ñú¼>Ï~¾>Ð-ù>Ð~B>Ñ‚¥>Ñöö>ÒÅò>ÔS2>Ô+Î>ÒtØ>ÎFð>Íæ†>Í»¸>Ìõ>Ìç¥>Ï<è>ÏØ>ι[>ÎÝ">Îó‘>Îð>>ÎiÄ>Ìi>Ì [>Ì>ËÐÿ>Ë€F>Í—³>Í,µ>ÊÃB>Çõ8>Æx¶>Ç$±>É{>ÉëK>ʃ>Êðh>Ê:o>ÈË">Æ;”>ÄÛ >ŇS>Äÿn>ÃûÐ>Ãã>Ãé&>ÆAˆ>É]|>ʸ>Çl>ǵ>ʼn>ÃuÒ>ÂÔ@>Â(4>ÂH>ˆ>Ã;œ>Å Á>ÇJê>Èš=>Ço•>Åúþ>Ã|/>„•>Â5¹> p>Áà9>ÂMr>ÃeÓ>Äs­>Å)ã>ÆòÃ>Å‘>à ô>Â[e> >Â÷¥>ÃÆé>ÂÛ>ÂW·>“ß>×€>Ä„Ÿ>Åýw>Ó¥õ>Ôp>Ôþ>Õ= >Õ3Ž>Õ5“>Õ6ó>ÕZ÷>Õ>ÔVœ>ÓmÐ>Ò«û>Ô>Õî>Ö  >Õ¹U>Õ{0>Õe>Õ<¨>Ôë>Õ!>Ôž™>Ó·G>ÒÕJ>Ô˜I>ÔV›>Ô´Ø>ÔèJ>Õ >Õ0í>Õ%]>ÕŠ>Ö·>Õ„>ÓÓŸ>Ò½¦>ÐÞ>щ’>Òõ;>ÓkÚ>Óú:>Ôz*>ÔWô>Ó+>Ò"v>Ói¥>Ó_>Ò®6>ÏÌÃ>ϰ‰>ÏáÖ>Ñk»>Òì>Ó1‰>Ó3Û>Ò–Æ>ѤZ>Ñ:>ÑVˆ>Ò²M>Ï)'>Ïê3>Ð;>Ðf>бÿ>Ñâ>ÑMæ>Ñ2ì>ÐEÞ>Îu5>Íbù>Ì>Îx>ϯÅ>Ï£>Ï#C>ÎOf>Î >Íìk>ÍÂ>ÍH>Ì`€>ËëV>Ë\ª>Ìž>ËßÌ>ÎÝ1>Ípo>Ënr>ÊÈ>ɬ>ÉB<>ɵp>ÊQ§>Êâ\>Ë@>ÊVb>Èd¿>È)x>ȇ>ÇÒ¦>ÇÉ>Ç2P>Æü6>ÇF³>ÈfR>Ê7>ÊÉ[>ÇWª>Æj‡>Ã|>Ä5>ÅS >Æ‚>Æ~>Åí,>Æp>Ç*>Èɶ>É·!>È,.>ÆŽ±>ÄA™>ÄW>ÄfÞ>ÄWÍ>ÃÙ•>ÄD>Ž>Æ ™>ÇŸ6>Éš>ŵƒ>Äã§>ÄJ+>ÄNz>ĪO>ņÒ>Äz¾>Ä@o>ăr>Äñ.>Älà>Å?>ÒÂ>ÓG„>ÓÕÐ>Ô~×>ÔãC>ÔõÆ>Ôæþ>Ô²—>Ôdì>Ó¤Þ>ÒTÎ>Ñ‘Œ>Ó~.>Ô5†>Ôód>Ö*>Öµ">ÖœV>ÖeÈ>Ö%*>Ö­ü>ÕcS>ÓM±>Ò&õ>ÔJ^>Õ^µ>ÕïQ>×1ª>Ø*q>؃®>×õq>×:v>×xI>Öc½>Ô­>Ó„·>Õ/£>Ø ÷>×þ>Ø@0>Ùh™>Ú¬K>Ùáê>Ø£Ç>×óg>ÖîI>Õ½ï>Ô…K>Õf¹>×gB>ØX}>Ù‡N>ÜÈ>Ý_>ÛÔé>Ú>Ø®¸>×f–>ÖCî>Õ*$>ÕG¨>Öø>Øáà>ÚÎù>ßVX>ßt<>Ý'>Û)è>Ùum>×èÑ>ÖÍÚ>ÕÖ >Ôñ>Öªò>Ù¨>Û š>Ýo>ßêÊ>Þa.>ܬ†>Ú7ü>؆>Ö©>Õ“È>Ôwš>Ö >ØZj>Úvú>ÜeŽ>ß´¯>Þ_™>Üç>Ú+>×ß.>ÖB¿>Õ&>ÓÏ“>Õ4»>×Sý>ÙsÛ>Ûw…>ÞJÍ>Ý)">Ü Ñ>Ù+Õ>Öô‚>Õ¡>ÔÙê>Ó^À>Ô‚>ÖGÅ>ØÄ>Ùæ–>Ú‰R>ÙÎú>ØÉd>×)S>Õ³ç>Ôðš>Õ\å>ÓNÊ>Ô'Ù>Ôî>Öq>׈¤>×ó²>×¢Æ>×›>Õªâ>ÔaQ>Ô`>Ô¶Å>Ó>Ó´ƒ>Ô‡>Ô¬>ÕÌ#>Ö6!>Ö n>Õýh>Õ9Þ>Ó“>ÓB&>ÓÁƒ>Òw‹>Ób>ÓÞ>ÔÐæ>ÕNí>ÕO0>Õ7È>Ôà!>Ô ,>Ó?>ÒsÊ>ÑÏ€>ÓE½>Ô>Õ'>Öû>×ü­>ר€>×3Ñ>Öq0>ÓüZ>Ó«Ñ>ÓM¾>Ò’(>Ô"ù>Õ&>Ö>j>ØcÄ>Ú'§>Ú‡ý>Ù¤>ØG¶>Ö >Õ~ý>ÔÓ®>Ó·>Õ2z>ÕUå>Öø³>Ù9º>Ûÿ>ÝÔn>ÜÞŽ>Ú·›>ØÐÖ>×¥×>Ö^`>ÔßS>Õú0>Ö­>Ø€x>Útß>ÜAì>ßÀ“>ß$ó>ܵó>Úq&>زv>×Xd>Ö§>Õª5>×v>ÛP>Ü€ >ÛˆH>Ý“9>ß Ë>ÝÐÄ>ÛŽ>Ùp«>Øì“>ØZ0>ÕHý>×v}>ÛÀ>ÝÃS>݈Ù>Þcp>ß)#>Þ¥5>Üz‚>ÙÆò>Ø p>Ö½Â>ÔÌÇ>ÖÅ·>ÙÆü>Übg>ÝÚ­>Þí>Þ.Ô>Ý8x>ÜS£>ÙÈÕ>×O>ÕpÎ>ÔEø>Õëð>ØHé>Û17>Þ•P>ݰI>ÜfX>Úø>Ùñ=>×ùv>Õa½>Óîá>ÓÊ>Õ;C>×5N>ÚD>ÝÐF>Ü>Úrz>Ù5Š>×Éo>Ö$)>Ô©>Ó å>ÓiK>ÔôÃ>ÖŽ>×z3>Ù z>ØØð>ØÝ>Ø¡¤>ÖRY>Ô¨ß>Óš>>ÓŠÔ>Òî >Ô_T>ÕJ>Õå>Ö\Ö>Öa>Ö~z>ÕÆm>ÓR>Óí>Òá^>Ós´>Ñøü>Ò«>Ó”>Õ$!>ÕNa>Õ@€>Õ¢¤>Õ†(>Ô\n>Ó;r>ÒaN>ѱ >ÒÓà>Ó®í>Ôæû>׫ï>Øbî>ׯg>Ø%G>×Õé>Õºµ>Ôf‘>Ótœ>Òˆ>ÓÊ0>ÔĦ>ÖK>×™H>Ù»ß>Ù8È>Ùôî>Ùš>×ÂA>ÖÏë>Õ|T>ÓÃ>Ô»Ý>Õ_‚>بŠ>Û O>Û-A>Û8>ÝÇå>Ý>Úxæ>Ùá¥>Ø>Õ=ß>Õ‰+>Ö©*>Ù/Œ>ÛaÄ>Û’Æ>Ý_b>ᦿ>ß²z>Üwž>Ú&g>Øû>Õû™>Õe>×îß>ÚJø>Û¢Ù>Ýt>ßây>äe>áÔ>ÝÞÆ>Ú–I>×zŽ>Õ‰>Õ*Ç>×S>Ú>>Ý>ßJs>àø>â¥@>áÀX>ÞÇK>Ûo>Ø é>Öo>Ô´£>Ö¢ç>Ù–>ÞÔä>ߘÓ>Þ‚>ßÄ>Þ€’>Þ (>Û4>×Å:>Õ¯>ÔI>>Õæs>Ø€/>ÜIß>Ý.»>ݾ>Ýe²>ÛB£>Úûþ>ÙF>Ö>?>Ô+ª>ÓÀu>Õ9T>׊4>Ú—i>Ú´ë>Û~ù>Úá\>ÙY>Øs>ÖÊ–>Ô|>Ñ¡¬>ÓÆ>ÕT²>ÖÃÍ>׿>Ø >Ø;n>×Lš>Õ¹#>Õ¢õ>ÔÍû>Ó! >ÐKH>Òlø>Ô»Å>Õ–›>Ö6>Õü¶>ÕË>Õ®>Ó™¬>Óg&>Ó[>ÒA>Ï^t>Ñù>ÑÎ>ÒxÑ>Òq>Ó$>ÔN›>Õb=>ÕîÍ>Ô8‚>ÒÁ>ÑÚG>Ñ*>Ñßg>ÒØ”>Ó•1>Óž>Ôi>Ö(p>Ø%9>ÙÄ>Öh¡>Ô>ÒÐö>Ñìœ>ÒÍ>Ô`q>ÕlÇ>ÕØ·>ÖDã>ØÐù>Ú>Ú2j>؇x>Õ‰>ÓÙk>ÒÛÀ>Ó]>Ö-#>רé>Ù >ÚÖñ>ܽû>Þ[×>Ý©o>Úæš>Öì>Õoš>Ôkò>Ó°¤>Ö=À>Ù5í>Ü]·>ÞáÖ>áD>ä»,>â]ì>Þ•>Ùù!>ØÐá>×YÉ>Ôtí>Öq]>Ùä>ÜîÇ>ã‰y>èŠn>é’—>å»ÿ>ßìÐ>ÛiI>Ød±>ÖÄ>Ô4”>Öž>×o">ÛÀ>äX>èsk>é~6>䨿>ß=¤>Ûµâ>Ø©í>×d>ÓË=>Õ¢Ö>×Ðk>Û•>âä¤>æzF>å<…>àÑš>ÜêÓ>Û\F>ØT>Ökƒ>ÓM?>Ôê0>×#µ>Ù«>Ýü*>â5K>àmã>ÝÈù>Üð>Ú£>Ö¦²>Ô«ý>Ò¥Õ>Óíé>Öv%>×™¶>Ø;>ÝR>Ûþº>Ú}E>Ú:z>××£>ÔÉd>Òx%>ÑÕš>Ò)Æ>ÕUÑ>Ö´ø>Öªs>Ø>×¹;>×J>ÖÆ>Õ;Ú>Ó,>Ðól>Ñ,>Ðé>ÓbŒ>ÕVª>Ôà‹>Ôð>Ô¿>ÔnÊ>ÓüÒ>Óì>ѯ:>Ïóß>Ð|>вK>ÑBØ>ѹ,>Ó*ù>Óü>Ñ¿T>Ð×k>Ñ¿Â>ÑŠ>Ðö?>Ð]—>П:>Ѱø>ÒO>Òû~>Õ²¹>Ôùþ>Ó/N>Ò9>Óƒè>ÒÄ>ѱè>Ðé¡>Ñ¢®>Ô¬1>Ô“á>Õïa>Ö>Ö||>×->Øf%>×Å9>Ô‘å>Òr>Ñt+>ÒÒ}>Ô>ÔÁ>Øgx>Úk—>ÚËt>Û‡ƒ>Ûm:>Ú[>ÖÀ>Ó;7>ÑÛ>ÔÔÅä>ÖÁ¸>ÛX>àÛò>ãq>äIÀ>âÕ}>ß3ô>Ú`í>ÔÉŒ>Ò˜'>Ô/o>Õð>Ùcd>Þw¬>æÆ >íÍŸ>òÀ>>ì>áóÛ>Ûiž>Öžw>Ô1ç>Ó^>ÔÙ»>Øx‡>݃å>æ Ñ>ñ·>öÁa>ïbi>áç½>Üå\>Ø­•>Ôò>Òzä>Ô_Ø>×1>ÛJR>åy>ê¿‘>êêú>åò>Þ È>ÚFd>׊•>Ôו>Ñám>Ólc>Õk¥>××>Û¡H>߇q>àþ>Þè>Ùhë>Øsé>Õ¥â>Ó·µ>Ñ8#>Ò8E>ÒÜà>ÓÍÞ>×]Š>Ûe#>ÚmÅ>ÙF>×,>×>Ô=o>ÑñS>Р>Ðå!>Ð×ß>Òç>Ô±0>Ölu>ÖÁƒ>×ç>Õë4>ÕE>Ò¤->Тç>Ðï>Ð#,>Ь>Ñ ä>ÒP>Ó.C>Ó­8>Ôà->Óèô>Òƒˆ>ÐøR>ÏÃ>ΰx>Ï¡>Ï… >Ïá/>Ïõ>Ðè>Ð Ñ>ÐÓÙ>Ðß‘>ÐPP>ϹÜ>Ï[x>Îâå>ÏE)>Ïù'>Ð~>Ï í>Ðì–>ÒÂ[>ÑŽ³>ÐP>Ðvý>Ð0<>ÏÙJ>ÏÄ>ÎÖl>ÐGB>ÔP>Ôi >Ó²E>ÔÊÇ>Ô€ô>Ñ£D>Ñ`Q>пÓ>Ð+z>φJ>ϼ>ÐF>Ô_«>×^>Ø {>Øtë>ØÈ+>Ö¯Î>Õ!0>ÑÆ9>ÏèÓ>ξ‘>Ð=„>ÒGT>ÖÍc>Úçh>ã¢V>â¢>Þ}¤>Ø¢>Ö0>ѽ«>ÎŽ>Îö×>Ñ>Ó¤,>Øé>äZ­>õo>ýð>êïé>ß>2>פ^>Ò5¡>ÎFÝ>ÐRY>ÒWñ>Ö>Ù£è>å¾+>ûë³?ù´>ï~2>ጙ>Ùke>ÒñÙ>ÎÓä>в²>Òœã>ÕÐ¥>ØÐ>Ü76>èÖ½>í`>çÒ™>á~ä>×xº>ÒQ>Ï6>Ð[P>Ѽx>Ô#,>×°ú>×LŽ>ØZ­>Ú¬d>Ûû>×ôJ>ÒPE>ÑBÈ>Ïe>Ïì«>в¤>ÑcP>Ó–™>ÓU>ÐéI>Óÿ>Õ&¯>Ò#1>Ï…>ÐÜ>Ϧð>ÏœØ>Ј>Ð3ø>Ñ>ÒÆg>Òä>Ѭ>Ð’$>Ð]>ÎòH>ÏTó>Ï$">Ï(€>ω°>Ь>ÏUQ>Ð)/>ÐV2>Ð^>Ï[Ò>ÏÀ>ψé>Ï>ιb>ÍX¤>Í|»>Í«8>Î]£>Í=z>Ì€v>Ì™ù>ÌWp>Ìÿ>Îþx>Îng>ÍÖw>Í:Æ>Í(¶>Í’B>Î_>ÌK¿>Ìjì>ÌŸ>Ìh>ÌÝú>Íï>Î’>ÏM>Íu‰>Ìb>ÌÁ²>Ì…Ø>˺Š>ÎÂ>γ´>Ëv">̼>Í/S>Îvù>Ïž•>Íš·>Ìü>Ì‘š>Ëœ>ÌŸ>Ñbé>Ñ'>ͺ@>ÌM_>Ëž>ÍO>ÎV7>Ì£B>ϵ>Íõ>ÎÕ>Ð){>ÕÛ³>Ô€>Ít@>Ìü>ËÊW>Î[§>Î>Ìé@>ÍÛ‹>Ìä>Îê>>ÔDì>ÞŠ@>ç?Y>Ðk'>Ì”>Í÷>ÍÙ >Î*Á>Î>Ï*W>Î >ÐÊ>ÐP}>ÚDƒ?¸Ë>àTÚ>ÐK©>ÌÊ>Í–µ>Îp>Îê›>Ñ >Π >ͬ.>βÈ>ÐJ>Õ´`>ÙÈ\>Ðæ6>Î+n>ί>ÎA>Δ®>Ï!î>͘9>ˬs>Î…ˆ>Ñ›å>Ñ6ñ>Ï¢â>Íèƒ>Ìú$>Îz>ÎJ>Ζ§>ÎÖ¾>ÍÔœ>̵Ä>Ì+½>ÍPT>Î¥»>Ì·ê>Ì3>Ì/P>΋Á>Î0>Îðþ>Ï^O>Ͳ|>ÌÎ>Ë>e>Ëh»>Ëч>ÊK >˪>ÌQw>ÍNæ>Í€R>Î\î>Îò>Ìf¬>Ì„_>Ì{³>ÌYã>Ì>ËÇ>Ëçp>ÌÁœ>Íû>ÍR>ËÙ¯>˹>Ëo˜>Ê¿v>ÉõÏ>É‘|>É@µ>É>ÉËš>Ë®k>Ë‹ >ËT¬>Ësu>Ë*[>Ê¥>ÈØG>ÈóC>ȰÒ>Ç¢>É>Ê1>Êˬ>ʯ3>Êab>ÊÃ_>Ê:ø>É >ÆL>ÇfÕ>ÈpÁ>Åg>Æä\>Èê¾>ʾ>Êhö>ʸø>Éô®>É„J>Èø>ÆI¦>Ä´˜>Å]>ÂŽ>±ä>Ƨ+>Épã>ʉT>Ëa—>ÊBÖ>Çæ$>ÇG>Ũ½>¿šF>µx >¹£à>À>Åë¥>ÉwÄ>Ë¡k>Ë5U>˪>Ék^>ÇäÆ>Äzû>»í>¡#É>•«ô>±¯·>ÅAd>Ç¡>ɉÛ>É™/>Ëv•>ÊP8>ÈÁ->ä,>·1y>™²>…™–>©]Ê>½–Ñ>ÃdY>Æ9»>È9˜>˽¹>˶>É‹³>ÄWa>¸Ms>²Ž>µÒ>±²>¼SA>õ=>Åç>ȯz>Ì›>Ë×Þ>ÉöÆ>ÇwM>Áiã>½&q>¾ß¹>¿Iƒ>Âa>ÆÑ>Ç%>Êü>Ì/'>ËÿÆ>Ê·Ò>È$@>Åøæ>ÄÆ$>Ç)Þ>ÆZš>ÆÆ >Çìu>È%>Ê`>ÌfZ>ËÒ‚>ʈ;>Éîn>Èú\>Ç—ç>ÇÖ;>Ç‘#>Ç—«>È@À>ÉÙ >Ë,>>ͱÔ>Ì.ç>Éõ >Ë1>Ë%Ã>ÉÁÒ>ÉÊ>È©>ÈZ³>Èç¸>Êí>Ëɧ>Ê}Ê>Ê.w>ÊCÿ>È…Q>ÇSo>Çb>Ƽp>Æ·>Æûk>Æ7¯>Çñ|>É[Ô>Ê]>ɰ>Ê'o>Èäº>ÈpÞ>Æ e>Äh¾>è™>Å<>Æã‡>È>Èœ>É ¶>Èà >ÉT¿>Ç[É>ÄIö>ÀÛÍ>ÀPò>ÀØ>Ãw@>ÇL+>È0>È->ÇÙƒ>ÇÐß>Çèˆ>ÃòÜ>¿fj>ºãÇ>ºpã>¼Þ>ÀµÂ>Äûö>Ƴj>Èš>ȼÁ>Æ>ÃÓ<>½_t>¸þ>´†ì>±º>¶"i>À^L>Âpð>ÂÜÄ>Å¿æ>Êo>Lj}>»[>¼ò<>°óÆ>¡—ø>¢í’>¬Ä>ºZi>ÁeY>Ã;Ú>ĵ›>ÉÓ >Æ–1>ÁÑö>½¢K>²¿ò>¢<$>›5k>¨=B>¹Å‚>ÀO¯>Âç>Äež>ÉLg>Ä¢Ñ>»•>¿ÀF>·&\>®Sž>«†>²£Ç>¼rÇ>¿°>Âé‰>Å3~>ÉØí>È€Ð>Æj~>Á´ª>»â|>¸ú>¶sH>º)#>À)…>µç>Äßå>ƶ¶>É׿>Éçf>ɘ>Ænz>ÂïÝ>¾ÙÎ>½ð¡>ÀŸÏ>ú>Åqí>È87>É;p>Ɇ¿>È÷>ÈmÛ>Æñ†>Äûq>ïô>é€>Äô°>ÇO>Ç[Q>Éý>ÊO­>É» >ÉH²>ȸì>ÇÅ>Ƙe>Æ™¡>ÆÝ>ƶA>Ç5D>ÇØX>ÉE >ËS­>É4¬>ÈZó>Æá¸>ÅÏ&>ÄÙ„>ÄîÔ>ÅX$>ÆB>Æià>ƈù>Çhì>ÈY1>Èþ>Çï–>Æ1ÿ>Æ >ÂK·>ÁñM>Ÿ¿>Ãi>ÄŠ>ÅÀ›>ÆÁÕ>Ç»>Èä@>Çr?>ÄÞŸ>Å–È>ÁW9>¾ h>¾Ðì>ÀR2>Âwë>ÅL‘>Æ÷>ÆDÄ>Çö>ÇA>Äù]>Á}‡>½dË>¹"W>¹½>¼¬>¾§­>Ãö>Äd>ͱ>ǹá>Åïê>†ô>½A>¸ˆÍ>²)>²‰>¶oÈ>¸á>¿A>ÂL»>ÄmÇ>ÇE/>Æ4a>Àþ>¹Á >³ð>­¨B>­{Î>°“ >´ZÍ>»¿Ÿ>Á Î>Ã÷#>ÅÜÌ>Äo>Á€;>ºá[>´w>®»C>ªâT>¯V>µðp>¼yF>Ák>î>Æ->Ä9ô>ÃDÒ>¿»>¸”=>²â>®Nq>³o>»b$>¿ _>ÁÜÞ>è£>Ç [>Ħ>ÃkÛ>Àså>¼Î+>ºR1>µ’â>·õ¬>½xp>Áí>Ã8ü>Ä:`>È5>Ç>ÆÏ >Ã.x>À(L>¾2>»–§>½E*>À­!>ÃÓ>ä >ÅQ>Èi€>ȇ{>ÇÊ >Äņ>Âß0>Â"…>Á‘n>Âed>Ä4@>Å.„>Å(>ÆÉâ>ǽœ>Çøö>džÎ>Æp¸>ÅV>ÅL£>ÅO>Å/Ü>żÇ>Æ€â>Çeú>ÈU>È>Æëî>Å>Ãæ‰>ÃÑË>ÄO>ÄØ1>ǯ>Æ®,>Æi·>ÆçÍ>dz°>ÈaY>ÆÏ>þr>ÀP>À-Æ>ÁBõ>Â^]>ÃìR>Ä«þ>Å>ÅÊ}>ÆÈ´>Êzæ>Ç-±>¼e>½MS>½òX>¾+á>¿->Á Ü>Âìë>²Q>ÃÀ×>ÅuD>Ç®F>Å>ÀÖ>½Ñ7>¼(Ô>¹;=>¹ÓÊ>¼©æ>¿#Ê>Àáˆ>Âå¹>Ĩ¬>Æzö>Äh>À R>¼Ä{>¸îÆ>³ñ>´ïC>¸h¯>»`µ>¿«,>¨Q>ÅC>ă>Á„\>¾Êu>º1Û>¶úÍ>³ŒP>±5">µ2K>¹¥>½ >ÁfÓ>Ä@¸>ÄÇ5>ƒm>¾‹«>ºU >¶üQ>´j3>±«Ù>´D >·âí>¼¡­>Àß >Ô„>Åà>ÃÚl>ÁŒ’>½½ˆ>¸ˆ•>µ‰0>³û`>´ä4>·jÚ>¼ãð>Ábè>ú>>Æè[>Ä€O>¨n>¿xá>ºã‚>·íu>¸'>¹]·>»¿\>¿–¼>Ãlµ>Ľ>ÆÈ~>Ä ,>ÀÙâ>¿{ü>¾D#>½™¾>½Xi>¾‰>¿¶Ü>ÂX>ĵb>žæ>Èë>ÅÓk>ÃJ“>Áñw>Áª…>ÁÂà>Á’>>Á9(>Á>Ãe3>Å›;>Æ®>ÈM>ÆÞß>Å]Ñ>ÃÆ¿>Ä(¹>Ų>ÄñÚ>ÄAn>ÄK~>Å] >Æ“>Æ>ÇBX>Æu>ÅT>ÄP·>ļ>Ä&m>Ä(~>ÃÓ>ÄA¤>Ūf>Ƥ>ÇSB>ÆY3>Ær°>ÄÚ>Âu>Á·ƒ>ÁþÆ>Â|µ>Âß>ú >Ä{6>Å€‘>Æ„ï>ÃŽ`>ĉ->Ãl>¿ý_>¿œ>¿¦G>À>Áe>›n>ÂFk>ÃÉ>Å}k>ÄØ>ÂïU>ÁD>¿"Î>½ß>¼šæ>¼åW>¾VY>¿­V>À'7>Ãù>ÅÂ>ÂÞ0>Ávâ>À>>¾#Ê>»î‰>¹¸Õ>¹ô›>»w >½U¤>¾ ¡>Á„‚>Æ_>Â>ÁHx>¿œN>½£>ºÀ2>¸.u>¶Ø:>¸P2>»—>¾’>ÁQA>ÄlÂ>Ħ5>ÃÐc>À;ø>¼t¦>ºUÿ>·X>¶ …>·RB>¹ý}>½ê9>ÁB–>ì‡>Äú´>Ãÿ®>Á~A>¼ÓÃ>ºøb>¹X˜>¸ìx>¹Q>ºšx>¾;M>ÁžÉ>ÃΊ>Å9ÿ>ÃÒ>Ã,>¿í>½4t>»Ø=>¼3>¼È>½Í>À7Ø>ÂÎH>Äv>ÅLþ>Ã0ï>Áãe>ÀÉ\>¿Á&>¿ºÆ>Àm>À>(>À·Ä>Â<@>Ä>Â>Å4>ÆA>ô©>ªý>ÂN¢> ">ÂH>ÂX¤>Â:J>Âl·>ÃÐî>ÆÃ¬>È#>Ƽœ>Ås‚>ĉx>ÃЂ>ÃÂÓ>Ãݶ>Ãû|>ÄÅ>Ä^—>Å1ï>Æ“)>Ç+Æ?€'?€?€‘Ý?†©?˜«?–NË?‹Cy?…ºŒ?‡½¯?wÅ? ,?~?€?€?€1¡?•!?² ´?žC?ˆ£|?:„@n?— ‹@Úd‘@?q@‚íœ?Ë@&?¿c?“ˆ|?†ò ?ÑI?‚°?¶'?…Ç„?¨ ?Ï„?«ôË?´ ?ŠÕ?ƒ×X?—C@J‰?g?Ÿ?ƒ“Ä?‡À/?Þ¨?ˆÉå?ìR?¥k®?Yâ?ñí?¸m?¢Cï?[²?Ñ?„´??‰§u?„üÉ?Ýq?>ò?¯×@À?—„!?A?ˆpí?€4¤?€¤?€/v?Ž3a?§^h?˜QC?àZ?Œü?†Bß?€ú?€?€?€"—?€?€?ÃSd@ Ü-?¦7j?³?†Ó‰?ªèâ?‰ ?€?€?€Ð?€?€E?ÍZ%@$}æ@gÀ÷?°Ú@?ƒ” ?® ?…eE?€˜?€Ø?‘ý“@hy?íi‘?`?‘øNAo’<@d/ó?“µo?…ÝF?‰ö?„÷º?€àY?¬Îò?œŒ@?‡k›?†Ì´@¤$]AÉþ@Å¿¢?­ÚÊ?ñƒ?‰p…?€J?€?²?‹Ë7?šâ0@:îø@2(?@ª2¼A U@Wm?å©?„”?«°?‡Òˆ@¬x?’°A?­ŽT@w8Ò?ª\K?€; ?ZŠ@p?íC?„N½?‚³l?“<ë?‹Ÿ9?û5?“ÓÅ@là? ŽÌ?å?‚{g@+ëå?‚õ??„t&@;?Ô­á?Žz>?¢úŽ?½µx?ˆÄj?€ñ‚?€Aë?€?¤?‚³ë?C_?†‰Ç@ÙÑÈ@È«?µIj?©@?ßšä?‹°×?€Œ­?€—ž?€n°?†F,?€sº?‚fC?½®@﹕@#F&?ÛÆð?¯nV?‚¡ð?–§¹?-'?‚9v?žyš?¾·§@,T@eíç?ÄŸy@•nBA&-f@!Pø?–eN@aO¿@ê?Š›Ò?ä³@?š¸?¢Oª?™ñò@ •¶A€‡¡AÏ›o@–Î?¢é¸?˜ Å?>Û?Œ³E?¤¦u@õ@’~?ûI½@NÑ@ÞÏAó …@¸Œ?­Ê%?¢Jâ?íŽ3@'Š"?þ?­n¼@Ã^G@ggˆ@(G/@±Ë£AxSAÖWQ@³Žû?Èö¥?©8%?¢§,?‡w?ˆ‘?²~¯AbÊ@VÜ@ÜíÏ@yþÕ@”'ArP^@Í?ÏŽ½?‹ŸÕ?ˆsé?„5?ˆÃ1@ 2@‰jAd ·@»;*?ÍŒ®@—ê?·B#?Žãõ?ƒm?‡ÿâ?†”¼?…•@>R›A+@x?Ótì?¡lü?›@Ô?ƒ'y?‚Ü?„éÃ?‡Í¶@•q?—Jx?¢ ­@ `?®³S?¨åÎ?ãD¶?“R?€QY?€9?‰‡º?‡¸x@#¦Y?§ò?¶?M?”ÑÂ?‡ûœ?’n%?Às?ŽÝL?€™´?€ ?s?ƒ Ü?ƒ=;?„¶Ô?¢$?Ò?’-«@.xò@Ý2º?êÖ?…ü?Ÿ?,å?…|Í?ƒu]?„ZÛ?Ù>ò@‰í?° G@åGAYm&@"Bm?†H°?ƒ9 ?‚Q?‰„ö?€O7?ÓH?’¥J@5ˆz@†…ø@Z@TÖÌ?¢?„%Y?††&?†jž?¢œ@NÉÃ@ãr@S©?¨^ü@ºA¯ˆ`@¡ßz?Òë?¼xn?°‘w?¬7i?Ïÿ?³ÉE?×Ï @ ?ò9@ïåBÆs6A_~D?ü,F?Ï«@@½@°é¡?Ëf¨@6¾ @Ò\Û@8AiBnB$Ó«BÙ¨+A­G@aÅã?Öã ?ì0Å@©]?”?î?­*Y@ß@9 A¹óA¤e~B¶ØðBI“¸@î`‡?öË?Ï$Ø@O·á?‹ür?± ?œìw@ I@A‰¹Aì]¿AzîŠ@¶¥Æ@á]KA d~@F/?·Üº?Ž;m?‹H1?Sñ?²±®@ÐABOÛ´AÓÍ?ÂÃþ@È,)@Éx?ž@Ë?‹Ø+?Œ¹ó?Ľ@ÈÕ@(íê@²A¬ìÝ@nò5?“H @M›ð? ž‡?‚²+?€»y?…¥§?/>@‡{g@Y¥?³±‘@-nf?º­>?ÿôµ?¦Û?…»?€ŠW?€Þ?ƒ˜y?‹Ó}?ù8¶@‘â?Ð%…?˜3—?—?ÐØ?™¨/?ƒîç?€Í€?€?†jû?‰ã7?ŽãÒ?˜Õ@€à¬@²}?»Â@ÆÁ?ŸDµ?…È9?„ÔF?ƒFt?Î@?¤Çp? j?±KãA”ó{@w?Þ¯`@hè>@'ý?ž!Ê?‡á§?‚Ÿ‚?³X2Ap¿@J¥}?àCM@‡:Í@@7@ôÆ@øÍæ@ã×÷?õà¶?‹Ô\?„»p?óR6@® @Šeà@¦õ"@A•ê@’ÂhAÁðI@Üãê@'þ»?›I³?‹Àä?‘ne@Ëñ¥@Uf•@3ª@GwŠBA?A–© AíjB¶²A‘þ@âM@pª@UEÆ@±¯@š¾ÇAÿÕ‡A¦s¸B€U™B°–xD‚îBå @–Ôê@>!b@LK_@ÑÕû?ÆÊÑ?é‘Ï@}W@óÒA ¬CV¥DB¼C$MIAsÁA=|ÑA+¢@¼”S?˜.É?¥Þ?µA^¯BèNBá†lBJé6A:@Å@„=Y@ÝÁ@Òm(@³p¼?®Ÿ?’ ÿ?‹A«?½o(@˜¨éAæzÒA‹ÄÀ@–¡ä?¼Hï@É~G@>ا@2X?‰FÃ?‰bà?ŽÙ?‡¬-@a¾B–ˆÖAU@›@+k?@€ã§A8ýc@I?Ÿ»{?ƒ¼ ?pÖ?¦5›?¦V?ØõûA Ir@’ÈqAynH@±@Z@Õ})@Ë ?ˆíš?‚·Ó?€¨ž? G)@j)?®Ù:?Ê ?þºÙA E@d0?ÐØ°?‘üP?‚ªé?†²æ?ŠR!?‘t›?”óP@V¼ß@#`@˜ß@¦Šº@+^?ŠE¨?…¶—?†°/?‹á?•»ó?ŸËP?¼ãä@Î|@R»1A8O5@ÒÂÒ@ø½?¥nÿ?ˆ3‹?€_¥?ޏÂ@ Aà@_½A©ÊoA pê@§ýäA iAé¼q@×çR@'?šb?€É ?åín? ƒ?ú?^A»jÏAÆîÌA@±AAý‚1B-ÍòAш;A—«Õ@\bÎ?ŠóE@7„?é'ª@F6Au@•A¼óÆBê,8C‘?SBb 0Ag”ÑA´¦´@–-?Àâ?ø.X@P‹õA»)’B/Ÿ¸BõhE‹\EUMôD©@ Û%@ôœ§@Z O?»iT?Óƒ@òX@a»÷?»æm?˜hÕ?ÌMA!;@€ÈÄ@ÒGAò³;A¯å?é{Ý?ÏUÆ@õ@Q;k@sCŠ@JY?ôrU@‘)XA3„hBz&»B×|ìBj@ª¢Œ@ ‘û?ݤé?½©Õ?ÎA†\ A=xAµB#ÅõD'ÖC¿ðQA’Ö!@;õÊ@e»@›*@ðù?¥þ®@g/Ÿ@¨ütB@ƒ)C|8EgfUEªô¯DežBR@×Q|@Aš@‚¯ ?é­ý@K¬ASáÔB£*‡C;ß§EHFêE+1hBØ’B WÆA`y¯A °Ì@& uA0"LA vøA½|`CM¨ñCŠ~ÀDßD$XÕC§NB8Å3A©@·B?»¤–@ ÈV@^@~‰_B×B¢’rB|³B>%IA~TAÜAÙ³@4²Ë?©ãÁ?¹k?ʹW@X‚D@ÀmäAØb¶A¯ ØAÛ@}Ø@Üf@Ìõ@'îÊ?À¢˜@]8?ÍbÍ?Ü‘@%p/@e@€¿Û@ &â@.÷›@šŸ?ídß?£ÊF?†3•?• ƒ?é×?d'?¯¸½?ØS–?ådÈ?½m@ t@9?¥°~?‡šz?‡>(?‰]L?ˆsG@ZŽ@E«?½Æ ?»$Ÿ?ÎŽê?Â{b@¼v·@ „I?”×U?ˆFß?‡È¤?…6¬@Ú2@z@݃?Üë@ç „@{ @Yp?²ý.?¿&?‘<Ê?‡?0?…{?¬û @ -ùA¥²Ý@èr@ñ¶@‡Þ}?µôŒ? íŒ?ÚOr?þà?¦kK?‹â[?µ<Ò@‚#÷B—Ô¤B)²§@Õýî@¾c@KÉ@ /?¹Á|?–Â3@7@Ø@7¦‘Au"=A€êBÿ@5B|ˆhAjƒ@W˜‰AKBsAÔõ@Þ§¸?ªd¼?Ó)¦@ÐãeAø„ðCã*CóEC‘ÔîBèµB:ÒA€Ñ£@ÞjÖ@lø?ÎÛ;@"eAKA݆=B*%ZCÅé±E™)DÖäAŸ¼Þ@Èoy@H;a@3Að@÷¿A“ÂMA4Ò@“èªAü])AÊPeCã2~A›.@¤®ŠA~¦ð@¶Á@ &€?ªyö@&M©@’(@ÍíÉ@þä€@oŸ”B^­ÈA2‰?Ûû@)nB?ó›3@`&?¤ ?™$=??Ò^i?ùul@øãB?VZAxý?ÕöH?¦T*?±áù?˜Û\?¹Œ?àG?›w@ ŠF@QUû@D›„@§!ã@ î“?£&ã?Šßõ?‡_?ƒ‘I@5Ÿ?Éű?†ë@Ë8@mWS?æpþ?ÄœI?©pè?¥xÛ?Ï8?†Z/?€™ò?‡ßÞ?‘ãE@F?âF?í¿¿?¦€›?’Úµ?›³›?ŠöG?³ˆ¼?‹y?ƒGÒ?‚×Ú?¬.@4[)@Ø×AR@}@aä?‘íQ?Þ­Œ?¤ËK?•,?†î[?ƒ“Ÿ?„­?‹.@A?ó‚@%†¹@ÅKà@òç?×þB?øÐ?˺?§´À?œBé?yÐ?œí@‚èg?Ç¡Ñ?—VöAæÑA:‡ë?ªv?ÑLg?Ž>?¯±@ˆ7ð?‡g?‡8d?™ÄÇ?Ͼ?ÛjóBžBéA¢,ò?áa¶A&¨@”Ëô@Л?ûgŸ@€.@0q÷@FB'A‚rËA¹Aìû BˆÚ—A¥ |A•ùhA-šˆ@! W?£uø@ Ô¦?÷ö@Ï݃A“+#@îZ>Aj•„Bº/QB‘޳A Šš@ÊùÍ?ïw?’Ë ?ÎtŒ@-@cÏ@EŸä@µ]@‰JdB}Œ{AAó@¨A?Ô ¸?©ho?²Læ@#¶?Ú?ÉJ­?¹¡Ú?Å~bA‡ý½@¾hN@Om?™‰v?ê?¼Ò?’ìÙ?øÝb?Õª_@?DÓ?ÿ¦Ü?¯®AKJw@ˆ?½?¡l/@™èÞ@=­?…îì?‰2t?†é‚@C‹?äó†?ª7@Ä*?ÜÜ6@Cý?ª\ñ@ w—?ì¬á?³oî?‹¢À?…Õ?ð¡?ÊÓA?˜TÓ?™Êh?™59?£[ ?Š-‚?‘„ã?̈¬?†c?‰Ýg?¨¥l?¼8 ?™·a?†P;?‡p§?ªk3?Œ®h?ƒø? §?€ñÞ?€¸ò?ƒ²4?±ÛAtã@LüÔ?•´ò?†,Õ?™E ?Œ%?Ž©œ?ƒ¬?€­?ò.?‚í?¬Ã2AÐÈ@'?«Ç?¦±%?Žïå?ïB@hF5?Þä?† e?„&S?è.A%Áõ@rD?”r@×]y@Œ¢Ž?”Œ¬?†´[@u<;?ÍÀt?ɾB?Œ>U?™Æ9?×"þ?†]v@üAæWËA\?¼Ñz@Í®@iDM@!Š?³y@³­@ê…?äpµ@]°Ë@T¡ä@ÙZçBVÂAYáZ@„-à?÷-?­ï+?“Tõ?±œ^?Æ—@’.†@ÓA÷@’½A,ªB@¡Aª–u@æLÌ@²/?¨¶–?Œ­,?‹ËN?™»@”¹Ø@ãË.@_à@‚<©@ÚµÆAKhßBro@þ$N?°ƒ»?‹îL?ŽŠŠ?Œàe?³‘µ?º&ø@6MAgW@Gùà@Ÿ<@€îC?Ï#?‡q2?ˆdö?‡@,ÙA%è6@Zl"?¬(?ÚC¥@A1?Ê»k?¡ ¹?„ƒE?°’Z?–yÉ?†N·@Í@Rûì?à/W?”„®?”BÂ?’×y?–@!@N!à?¨o–?Œ9?†q±?‚aE?‰b?šJ…@zÒ?¤­÷?•£T?ŠÁ?‰­Y?Ÿd½?†sƒ?‚Nˆ?·ó?Ûp?cq?…G*?‹ªƒ?†‹X?Ÿ7?‚'@°a?çc?Ð ?€NÒ?€p†?“?ç ?„ ?ìH‘?”Í?ƒRÁ?‚Ki?ÔO0? -–?†g?€>,?€A}ØS@?{º?Œ&M@FÐ?­Õd?‹¼s?…bü?œBï?Ÿ?îìó?“o?€± @6î?ÆŠ$@B¸\?¾ù¦?‰Í?Ó?ÁÊ ?Žçè?Šp4@D '?ª—?ƒw¾@"£.?µÑ­?’p!?„í·?·a@T™›@!ƒf?§ü ?Œq@Íí@`SS@#jy@)n?ÿR˜?‰ ù?œ3ò?ÕŠé@COAüÝ@ÃÔ@)fV?Á¥m?œýQ?“,¯?‹:†?æQ?°Î?åÊJ@þA‘¯Ae“­A>¨Â@›Zs?­TÀ?ˆ5Ï?‚Þ5?4|?·#AUú'ATÀA@€¯5@ ¶4@,²Ñ@XÞ?@ؽû@ç(?† ‹?J?Ð#4?‘ÞŠ@6ÔÚ@‰È?ÍËH@¢ì?Í Ž?²Ì?ÛÁƒ?•D\?¦*Z?ŒèÂ?!è?šZ\@0o?£€m?‰ËÁ?£S?µW%?™ØF?‡Ó¬?!¬?»Ã#?í.?ÐR?ßÑ*?¡ Y?†§F?†ÅY?ŸÉæ?•éÿ?‰i?– ?ƒ¾b?ˆY:?„Ô0?™Žã?Œç‚?ƒ @?’@}?„V@/Á?¡á1?ˆà?‚¨Ô?€Cÿ?€1S?†ÚT?€Õ?ƒ¸À?ƒÆ×?‚?‚N¡?Ä„?‚Ô?ÑÅ?’Gù?€áÝ?€hõ?€zà?°½?à/…?¤ð:?‚Ù/?“.?Ý·?‚žû?¢C ?œnV?ƒÅô?€ý?€l@4Žˆ?ÝWy?¤%¹?‰@Š?„¹?„hê?‚ @?ÈŠÁ@B‚£?¢VM?€~?€?”Kã?  ?ô;M?˜S?ˆíÚ?‘`@?ŽnÉ?“³ ?•á?ÉAŸ?³£&?†^R?rV?‹@ä?ÀØ?‡pà?•nÌ?²Á?³“œ?ž?í@?âì3@• @àÀ‚?Š’‹?–I?† ?Š.û?¥™·?áa@ hâ@ª?ÙšÉ?›O&?|?ª‡g?žnÁ@=¯«?¥yÅ?–E§?¹d\@ [`@9¡U@[Ê@ –©?–Jï?‚$¼?€?•ú@ïëAf£@³¨c?óv!?µz?̳?ÔÔ?Ã’w?“]]?€ºâ?€?ÅÌA?›¡èA Ë@4ž?¥t‚?ŽïÍ?©C†?œIý?‹í½?†ÁÇ?¯?‰LÌ?ŠYê?€dØ?ËU‘E¸œÿF=‡€CxQõCVê?éÉF›#D®-1C´jUC^×É>xŒÇD¤‹´Dú}èF8tEVÖD‡PC³À~DÀGF K)F,v ED‡p£Cž_VD'_ØFTFyFHŽ4E ¯±D‹·UE¨^FpgúE¶œxDÜüzDïäE€n9E¼?F9PºE,cƒFbYERÝF !hFÜ-F&ɵD#ûåD¡n:DŒÐFJ‡F—w$C^ÏDÊIOF(D8sD”Ú FE éÝD•)˜E£LòCFï€F28•F#¾nEÛÛ—Fb)`E£³ÙCjtD*%ŠDŠüÌFs8ëE‚%ßEòÃZF„ŒF¹E‡‹„E§²qEÂÞôEĶ_DlØõDdÇF³F ÐbDõFþD¾(EξE\v>ÊCÓEçmCÒÇ4Eã†F&ÃÇEÍûnC¡Ë DíÈ÷Cý{™C‚zE@ɇFPy<ENkƒE¥6nFIÁFÞfºBç] @U1D­Eî1C‰ºÝD„°[Dý®D!YE|T¨F9<ŒEó³KD/\F@¡#F)ˆÄF4‘éE5OjEq’BD‘2cDoE†¯˜Eø0.D3ƒ°#DKÑ'D=æVE§FßàùE)ÜCÚKEbíLF’ÐE*LE«6E+¨‚D\çFË#-CöêÓF2¼D´ESEþSEA¡³úEt·E:iKEá{gEäØEÚZE¾‘˜E‘• F £E3ì EËSKDäÐF9tFyðzE4ÒøF©‚éE%rFoD¿–CÖOóF‘”ÇFvFÿD<¯E”’5E’e"F’,éEΦfA±B|A soF]‰jD¸Ö«E®_²F…KEV¦A݉°Fv€|F€URDá0…E©øÂFGG‡EWƆCç|F—•ûEÀÔ<EÌÅïF€ûµCÛ ?CúIÅFÕEc—°E3Å•Gþ½D;TyD?D'àD×R@E¤ƒ‡EÚ~E¨ ¾DY%cF¥ÛCØ¢QDØ'DR;EaÊFGf.F@„ûD¸•üF~jæD¡¿E.)F`+E@rDÉ(æE.UoEX/TE³ŽEgäØEkZEÐ(ÐDÌ÷ÓEƒHjF-åE: íFÿªE>xC®©ZD[µfFãŸE1 F4çDé E%¼ûE|ÞEÞdFÚFÆÕE¿ù¤DǵcCC$Ch¡F4(ÚFo)E«ž+D-soC¸­bC ( E…w=E78 CäOE»@÷Eˆ¼ÐEŽcDÁœèD¼DÉú+DØE[!ÅE½è¿F3¢DækÂE‰$¨EíµYE<ÇEÖ­NFwDÄEszIDû#°EElWEñW,Eµû®DOz$C¶‡*Dì¥÷E”C®'C ¾¯CïDðþFzY7>C/>“FVìˆE«©±E†êE­ EÞ;aEƒƒEý­õEÄ7EÓGÇFgÑ EÄkHDÈó†EÂwïDÓÅMD)E?ç§É~àÍ•?è\²h›Ó?èPáz,&ó?ç¤Cʉ/?è\µXxa]?ç¤âò&;?禜Rãaª?ç£!—¤ç?ç£ÄÅíH-?è-Ä{‚î/?ç¾ØŸ.?çðbO ʘ?çï“RT7;?çý¯Ëšñ­?çýMÉO?èä%ÖU€?è$›êk£W?çá±a{­?çúæ“ÃC"?çÿ}ý§Ù?çó+-¸e?çÑ#Ï9ƒ£?èU‰ùõq?èfÐÊEG?çý ¤b?çé÷~{ò=?ççÿ>°WÅ?謊ÏÂA?è_do:f/?çÝoæ"Šk?çìgr®x?çÒœXP/?è@RÖrÑA?è=­ Q/?çÅè.H”!?è ¼¹ªc¬?çÐ']l(³?èÅO+Å6?çÛ–Xþ±ˆ?è.§f³?èvD“[‹?çØõcQ ÿ?ç¾Ó´œÏ?èÍqÌ?è܃쇖?ç÷8xÇŸÿ?çÂ×1(e?è¢+=b?çÕ&r·|?è ‰°;?èQá(fм?çõšÐª,U?èV¨¢?çê'©®?è^ˆý§_Ç?çøOÓvM,?è4Cú3¯ ?çñ"T8šŸ?è(cÙHff?çú§a›ú?çÏ·›F¨?çÕ˜òÑ?çîíëYl¾?ççÝNCê?绀êªÒD?è$´õŽb?çÚÎÒš­\?ç¨/=V„é?çÐ-™å?èD£âÅ£ô?è4Nú@{'?çóØ–Âä?è7øƭ?è#`|TM?è 'ËŸ ?è WË«?çáÖ‡FtŸ?èÊ£@:?çá¢g·X:?è6*´Éub?ç×ú6Íè??è4âº5?çÊÝ+}3?çËE…/ù7?è:Á[ÙX?çØ¤ÞÙ•?çÕüÈæ=?çÞ¡¾ú‰?è«kaÁ„?çð³²æp.?çÊÓÁÏCë?çáüÊBýC?çØe¸¯?èV¿iK"?è 8B±À¸?çª84øtº?èÜýÃß?èo .?ç¾Ç H,?èNÀ­Ö‚u?è\eöbÀ?çÙtH‡?è5†Šî?ç¸å¿y¶ú?èNÖ³Ìt?èLælàd?èòúžz?çµÂm.&ÿ?è2%pŸ?è;)ƒ¨?èàÕ U‘?çö@TŒá@?çüfº›Ï¯?è0áËQŽ?çÚw ·”Ž?èDÖ\/«¦?çØ]<Ÿuã?çΊx,S3?翈imö?è3|ÏîÔ?èym¯ô?çâpv0N?ç§z’@é?çÏ€ñÈÌ?çì4S ûÆ?çËÄ•=Ôæ?è?žÞ·p³?è<©È¿¨?çÙ™Ù_»?è ”„Ìñg?ççdiß>%?ç¿Ê 0?èε`“?çßQ+Ý2?èOcÀÖ9?è',¤XE?çÙ‡†@!?çÛW…C?çÇ&æÕs/?è9 Ò@x?çöÕ½à»Px%?èBZV?èE›Ñ‰?èW6(²P?ç­ Vvä?ç›çcÛ?çÁcÑû‚S?çÑ×niîð?çÔÚ#‰0W?çÕ÷S§þy?çÝ7¡odÞ?çêxPJ±¥?çç~䥮ã?èúœ?¹G?è wN h?èOîâîÛ?è9M# ?èsßך?è2ãN5¯×?è9ÈLt±¼?èFÕý†?èPäíÃõ?ç¨Ì•v?çÀ ü’!?çÉÁ7­âè?çÛvYˆkå?çß[î>`?çèBˆ—¨?çí„'§h?çü•}+Ú¨?èFá7¢?è&”y?èhi?³›?è3`˜D¦Ð?èR>€Õðü?è\´ÑÔäK?ç±Û~}¤Æ?ç¸>Gø¨¿?çÑט¡òp?çÎx I½G?çÛ_N¿1f?çýã$„È–?çöK0û”?è"Mâ)!D?è0¯5ÉkE?è0øöyÏ?è<éÎcu?è> Ða®?èI\ëZ?çÁÛÑM/Â?çÄÈ+‘´ý?çÚA¦£¸å?çÓ|„ÓY·?çêñ½úÖC?çøÿi<ú?è«ë§}?è(šëQ?è$…£?èÆ¤«5?èñÃŒ²?è%Ø%µ°g?è;€}ÜJ?èCcïÃñ?ç¤ úp²?çË|2”^ô?çÔì5uïú?çðÝœ·Í,?çëâ/_Ç?è]Ñ-ý?èÜsdÉ?袧 ¹W?è#eö ?è1ôµ÷.A?èNûuZÙÆ?è_ù3J`#?ç¾EÖì׿?çÇÐ'æ?çêÇêàO3?è€Ý`âÛ?èý"ŽÍÿ?è&—Ö2·??èJQ%28Ü?ç õkG5?ç¡’†³H?ç¢xÃ]ƒ[?çÂv´­?çþ+í1?çñ}N£Ý?è( ®w ó?è*†ª•øD?èJ‡;Hª×?ç¦ÏS”’4?ç­ráõ Î?ç«‹‰ à@?ç©TyŽÅC?ç°¢gªƒú?ç©èºqáÎ?ç¯Ðg>ëË?礷ž,M?ç´ÌègT8?çµb¡fç?ç¾=Ø%?çºçsÇ )?ç·ð#²èü?ç»ï ¸x„?ç´¦'ø?ç·Àr­ÿ?ç»ü›tTñ?çÁé#§¦¼?çºÇåÃ)?綘ñç™§?çËθÇ?çÇä4_·Î?çÍ=¡¾ó^?çËî†kµ?çË\ÅAw?çÐvž6T÷?çÍ #uy1?çÄcÁ±2?çÈvæ‡R?çÍ¢1ŒÍ?çÄ­\Ny?çÑ„ ôŒ»?çÏîî²N?çÇ,'gÔ?çØ¶d7u$?çÕ“§Nrk?çܽS ˽?çßGô¬Dþ?çÔbl3Ø=?çáÖš4 ?çÜŠSÅ}%?çÛÓtŽºÎ?çÛÍW7'¤?çâùx0Wê?çß´Ó%z¨?ç߯8j|±?çÝò¥uß?çÞZ2üD?çÔÿ¯ø@Ï?çÙáýš?çâ‚e ?çÛµÄaKN?çßC?Íù?çñ’…˜?çååu3û?çí”ä+)}?çç2Äàñ?çïµä–šm?çêXR=Ml?çéŠ;BZö?çëÀQ×!6?çê9 |bR?çêJö"b?çèxš*zÝ?ççØT?çýnùBÕé?çÿ3q ¯f?çùäp¢UÀ?çõÎ$ý7d?çü Î ?çùB<ï?çù²r±E ?çôk$ÜZ?çó¶B¯J?çý—éô‡?çõ´réx?çúºy5Ã?çøÅl$N[?çÿñ¡>–S?ç÷MlÈݸ?çøâoãÎ?çýýÚ ÚÀ?è¶w“ÝÐ?è.ç ›+?è ²‚“ñû?èqþïcE?èë¯mµH?è n‡{?èâ“ lÝ?è *‡«"?è1,eò¢?è 6ª?è /â¾+§?èž«4¼Í?è í6}d¥?è CFB˜?èˆñ•Y?è§™3?è-k&äã?èµìö ?èB¤ð“r?è57& ?èUŸYhÑ?è¸ú“·0?èT×Ó?è„7%™?èÝØ›ž[?èù#:=¶?èX×%?ègªË)l?èßÙHB“?è‡l3‘?è±*El?èÛ3çŒK?è-·ÿ@Ý{?è+@Ó?è'½9µ?è1ÇÎÔ¦Ë?è2¯++F`?è/”g¨‡“?è1BdÕz¼?è27óCš…?è&Ç+S6i?è%N¢6Y@?è/‡Töì?è)\«¡"ì?è2'|Â?è4¯U·Ã4?è9äj Èâ?è8S*  •?èA|ôŸ­?è5©*FŽ?è7|‡©SÁ?è<öiÝ\?è6r5­Ò½?è6Æ‘ï›Ö?èA¼‰áà½?è<›íqîä?è6d«‘>¬?è7ÀH´O?è?_V:)?èI_òŒÑâ?èLŒŒù?èR1¼ á?èF€^ë5Ê?èD²ª%?èKŸ“ ?èC}I?yñ?èL%}œ?èQ™Öð´v?èQÞ§CÝê?èQÐU$—?èPîÏ#B?è_âj¼M?èSÕ²±ùi?èVX¬“ ;?è_(KøBß?è_¹±¹,…?è_OlV®º?èZLsþvg!?@4 4ÿ©L©¶H€! ©¼,©¶H€! ©`/©¶H€! ©2©¶H€SNODØÜ&ðì' ¬M0¼ @Ì¡! ©¨4©¶H€  ©L7©¶Hˆ?P¢$?JÅk?IDñ?R??H¯A?SüE?TA?S€ª?Q$U?J.‘?Q•?N†C?N®Ý?MZM?Mªx?J)q?KYå?OoÐ?LÍç?LXy?Nî?Qüµ?G¶§?IyV?MY/?OÉ"?Oo?Ke5?Iä?P6e?Nw ?R ?Gqö?K z?ThP?J5ù?S&º?G¦œ?S–›?F  ?K’›?R­+?PÚ5?Jo¹?Jçt?NЩ?T4\?I.?TÈÞ?I´Ì?Eý›?QØ?E<»?U½˜?EOC?NñL?DR?P(ú?G6?Mzƒ?Qñ¦?O§?N…!?O¨?Sdk?Gdß?SÙÃ?Tƒ=?XN=?B«e?E(f?P»?F´ˆ?J'Ë?J&©?K\?Qî¼?H-ò?S¡&?C¹?ZBv?@y»?YjÅ?Yê‘??qÄ?X…$?W‰$?R‘?IRY?O75?Qu?Oq?Rjn?G ?J†G?S§n?L¢ü?Cl†?VÞ?CÕd?D Õ?\¡ô?=àg?VÏr?B?Ez?Mù?R,Ö?Hã?H¼Š?H×ã?N‚‰?O ’?DËh?WµÏ?C0Ž?WêÙ?X.?VŸ?@’Ñ?K«?OAˆ?P·Í?P­}?M¶Ì1>ʈ{>ÉV)>Ð(r>Õ9>Ò®Ê>Í»ë>ÉD6>ÒÓJ>Ó¨<>Ñ’>ÑD?>ÍT™>ËŸ™>Æ Á>Å7>ĵ‚>Ög >׌Æ>ÕO«>Ñ‹+>Í.>ÎTš>Êü|>Å’u>ÿ>ÀAÔ>Â/_>Åñ9>Ù}>ÙMT>ÕàÒ>Õó>×Ý^>Îx¼>Í«K>ÊZŸ>ÉÄ.>ÆÐ‚>ÇÒs>¾ ò>ÁÒZ>Äã¼>Ûš¸>Û6¤>ߦ>Õ†×>ÝMÉ>Öü5>Ýöá>ÓrJ>ÖbJ>ÐÉp>޵>ËÉz>½y>Å‹~>½Î+>¿h«>µãÀ>Á¡t>À€>ÞáÒ>ÝnØ>ØŠ>ÝnY>ݬ>Ιn>΂°>ÉGŸ>¿ >µ+<>¾¿—>»kè>Û‚3>âÅ>àñð>ã½>àÚ >ÜV>×F“>×8Ä>Ïéo>ÇG>ÊW•>¾$f>½–Æ>¹n—>¶ª¥>¶<¯>À->ܶ²>ÝfR>Ö# >ëÕö>ÔGv>íŽ÷>åzæ>ÓÜh>ÓÃ8>͆W>Åê>Æe˜>ÈÛ>ȧ>ÁÚ>°AL>Ã]¶>¾p¾>×ÿ>Þvú>ãí>êÇØ>Û‚7>Ûøí>Ò‘>Ð$>Ëχ>ÆŽd>Áj>²*h>½OË>Ãçü>Ù—Å>Ü4é>ÖõÊ>×+Z>ݦR>ÏöE>Ö7>>Á£\>ÃÑ”>·QÝ>¿>ÄEo>¿À0>ؾ/>ׂ">Ó²Ý>Õ†…>Ö¹@>ÎÑ>Íbn>ÊX>ƨ>Èá‹>ÄÁ4>¿°F>Ã>Ç>Ãt>ÖDÍ>Ò»¢>Ò>Ê>ÏW >Ïö5>Í#Œ>Ë€)>ÈNx>ɈÝ>ÈŸ#>ů >Ä >ÓÅ2>Ô­‹>ÎÑ3>ÍÁ©>ÊÅá>ÆÆ >ƱZ>ÕÏ>ܦ>ÔÝv>Ôf‹>Í_q>Ï!;>Èj(>ȯ>Ƹü>Õ·>׈>Ýèñ>Ýë>ß—>Þ_)>Û””>Ô’h>سy>Û§>ßE>Ýß»>ÛüÖ>ÖÄd>Þ‡Ü>Ýïž>ÜÌm>Ú6>ÖQ>Ñr>Õ È>×òR>ÙRß>Ö© >áÁ<>Ù´>é8u>Û>>à¸>Ýq>܈Ã>ßR>Ø7í>ÓÕï>Ô¢O>Õ¢+>Ùˆ´>Ô>Ý„ý>ØËM>åˆÝ>Ô{>éÑÛ>ÒÊk>åW>Ó/`>ã³>ÚQŽ>Ý•õ>Ùçç>×çJ>Ót=>ÑZ9>ÏÏŒ>ÒìÁ>ÐZC>Ôß«>ÓÛ±>Ðè8>ØÿR>Õ7ö>ÓÎz>Õãb>Ò6V>Ô$Ó>Í‘‘>Í)×>Ï—>пg>ÎǶ>Ð {>Òj!>Ó¼x>Ò?&>ÐØ>Óe>вù>ÑJz>ÌëË>ÏR>ÎP>Í82>ÊÅ>Ê$+>ÌWK>Éä_>ÊC>Æ«Æ>ÊÎ’>Ê-Z>ÉÉF>É¢¦>Ç B>Êžç>Ì5ù>Éõþ>ËoV>Ç&”>ËëY>ËV­>Ê`Å>Ũ»>Â…x>¾ìà>ò¸>Áöå>ÅZ}>½ž>Â|§>Æý>Â>>¿LR>È€>Ê–á>Ƹ*>ÃøÄ>Æ,¤>ÄÊë>·æ»>¹ñ²>®–*>» ì>ÇQÏ>ºçñ>ÂwÎ>Æá>ÃÒÀ>Âÿu>Á…>Âh>Á¹>²Á­>Ä^I>°q«>À»¥>¶ú>½ „>»|>¸•1>ÄBÙ>ÄŸÁ>ÅQg>ÅÅn>Á¢µ>ÃW!>¿üÓ>¸Üú>³mô>À`§>ÄÔ3>Ä/>ÅÄl>Ä·X>ÿp>ÁÝ>Äc>ÂF>ÀT>ÀÇ>Å ¤FæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffFæffGXGYG^G€GôGüHãHîI!IÑIÒIÓIÔIÕIÖI×IÙIÚIÛIÜIÝIÞIßIàIáIâIãIäIåIæIçIèIéIêIëIìIíIîIïIðIñIòIóIõIöI÷IøIùIúIûIüIýIÿJJJJJJJJJ J J J J JJJJJ$J%J&J'J)J*J+J,J-J/J0J4J5JBJNJOJTJWJXJYJZJ[J\J]J^J_J`JaJbJgJtJuJ€JJ†J‰JŠJ‹JŒJŽJJJ’J“J”J—J˜JJ¢J¥J©JªJ«J¬J­J®J¯J°J´JµJºJ¼J½J¾J¿JÀJÂJÃJÄJÅJÆJÇJÈJÉJÊJËJÌJÍJÎJÏJÑJÒJÓJÔJÕJÖJ×JØJÙJÚJÛJÜJÝJÞJàJáJâJãJå  ©ð9©¶HˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8hàTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬R,ÓÀ´Ø@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ NÌMSNODHl¹L··XŒ l‹ 4‹ h|\$xJôG¼Gˆ$PNÌM˜¤÷„õLõ !    ÌU ¶HLåèPSNOD`LyœQPDŒk‹D¾6D^¨'D?:DЍ‘DªñDÓa\DöªEÔ…EG DíŠDÆZœD .$D…5BD¡0 Dà®àE%òE>ÍEMŠfEH×E2WïE ^¢DÄ­Dš³EDÄÁ,EW­EO2DE‹ E£¥ EžWEö©E7KDõklDµ·ÆDÛðÈE+”E€MØEÆFœF &ñE³5/Eb#E þˆDÆÕDånE0–íE‡¾!Eä F-[`FÇJE½ÚžEj®ÆE ·qDÆôfDÚ²àE#`EvÏEÖQòF}§EéœðE¥»¨EXB$E {DòUDÀ7E îÌEK)}EŽ3…E cÂE™ôEtº¬E1™kDïîD¯D¢j5DØuðE …E/º¶E?@xE<:«E%ÜEÉDÂf D˜}×D†0D ™®DÆöSDáïYDî(4Dî@îDÕ4D¹:CD— UD}ÞDac«D„n:D›pÂD­ÐED°D®RzD£<ìD“ÞoD}œBDXæˆD”ƒD¹UpDëÌEŽßE%ËEÁ Eô‡DÛ«ËD¬piD^D¯ÑDü™ŽE1¤iEtGE‹ µE„øEZ™9E ]9DÙ?^D§õDÙßèE):4E„é×EÌÐ\F jPEúVE°BpEf“E DÆI{DõLßENšEºíùFX'QFÊg*F¬ážF¾ E—¨ëE …³DÙ«NDý9ÆEWÔ×EÒ¤`FŠ9GM<©G#çaF.¼RE•¶yE!š DÚÎDñåEGªWE¶iXF7AjFž§©FˆÓåF sÂE‹ÈnEãQDÔ2GDÏcÕE#VE€ÙšE³Eæ‰xEÝ EªˆGE^íEáãDÂhPD®HDó$E&ÕOEX„ÉEl°Ef$ëEHÇE> DÙKD«£‚D„D¬JÐDÛyDû ÇEþ—E)³DëQ#DЪqD¢}D„„¨DmËïD“D¨¯¿D½«=D½‘D¸ð×D²èåD§,D‡iÓD\AWD–ÿaD¾_DôJºEàNEÉ#EºêEÊûDáîÊD­ƒ DŒ *D²+iEbÜE9\0E¨iE‘;›E‰a#Eaý¥E%H?DÛÞ×D¨ŠD㕌E.DÐDÍ^E ©ÿEt¼•E³VsEÙ&õEÒE£BàET®ãEþDD½@kD³:ÉD÷ó¨E#C³EPoEcùE]·…ECËE¿»DÔ´#D¤2ÎDŒ­ìD¬ž.DÚp€Dùà:Eè‘E;)DéR=DË»qD \Dƒ¾Dh›^DaEaEmôƒE¸rEä§©EÚyE¡ÈžEP”#E úD»ÒD³±—E¯-E:g™EFEšxjE’"¤EhÎjE'¶æDÞîD§ŒhDžVrDÓ©áE.VE)PÌE8ÐãE3è”EeMDû°éDºÀ;D“îñD€_KD™zëDºQŠDÔK|DÞÙšDܬòDÈâQD²'”DC€DqL1DXˆD|¨ZD”.D¨T:D¬ÿïDª°^Dž£D‚ñDr…DM DuŽ'D‹ßD¬üÝDÂø¬DΨ¨DÍ5dD»;5D¢=D‰±?DiØ#DŠQÏD±dDßö,E`ùE›ÃE ÓíDû«^DÏÿ¦D¤^D†<D¢7vD×FEØE3mÒE>ý}E9ˆXE#-ÂE\ÐDÃ[þD˜£dD­þ@Dòo¢E'ÞEXaEnÄEkÃEJxeE2$DܹôDª}?D³µDøq`E*±E]/–EtÁuEr1EW¬»E|LDÝZDª+»D­ìDíMîE %wERï‡EgFE`û ECÐEíDÒõŽD£K7DœPÑDÏðTEÌE*ÌûE>.IE6¹©EتDúkÃDº•ñD“ŠbDŠ*|D¬§ÏDÔvDüÊ}E´¢E*DïÍ(DƱ«D¡ þD„œpDn;|DŒ·D£ä2D¹FªDÀáÛD¿)ÄD°4MDç†Dƒ¯„D^úDJí-Dj!D…œƒD—D"Dš3.D˜·ƒDŽm¿DXkD^ND=¥ÜDTTVDqW¸D‘ÌD÷Dž°D›²ÔD‘qJD†—DgDI!ADi•Dˆ~ýD¤u|D¹-„D¼Y^Dº§{D¬?D9=Dƒ^šD`aðD…­ÞD $üDÇÉDâäxDîšèDívADÓÿ‚D¸È‚D—ÑD€WnD9hD¬àDÙ­ËDø‹¨EëmE–ND믟DÍÓhD£°ƒD†^D–'D­eDÜ+ÝDünÕE÷¢EwRDð° DÑ\3D¤ ¹D…ÃþD‹{^DªuÅDØR6DøpÀE…²EåÓDè¬{DɧgD ›D„`D%cD˜±eD¹îäDÔW—DàóDÜñçDÈG9D²Dà1Dr=DDmG1D‹ÇD£Â*D¹>!DÁÂD¾5òD¯c"DžVD„@D_ÎìDFI.DcbyD„/D޲äD’UËD‘ÙjD‰eDDYó‡D=6D.NëDD¤NDa¨DoÛ—Dsñ·DtôvDhºDX@D=zD%¡²D5BDKódDjêD€ UD€iúD}±pDo±\D`NsDFD-NDG`¨DbË D†[D•Á\D˜wèD—_hDŒ1,DŸ D_ ÕD?gÇD`ÞÏD„„D›0D­UyD°†?D¯XD£[·D“nêD~Œ¦D[ÒDmEóDG›D¨ØD¼í!D½^€Dº+D²[!D¥S€D‡»²D_L5Dlr¼DލCD§È_D»S~D½° D»²îD³¬7D¤àÏD‡oMD_*ÂDiƒüDŒ÷D¤Ž¨D¶ä·DºvhD¹ÁD®øUDßëD„ÜèDb²æDXSºD|qzD”yD¨UD¬g>DªfŠDžbÔDÃôDs9|DN™ÄDJ8˜DiÉòD…Œ¹D—GaD™D–D ™D‚jDaÄÓD>ܨD.;^DD ÿDa´öDoô}DsÃ×Drø”DgE»DXÄcD>5pD%íDknD+–/DB„ÒDH÷®DKÄ£DLñwDDDäD7«LD$ЧDK÷4oˆ•56,5W©¨4‘Öj6F37É5îîf4Šº†4^±›4¢K°4@¼5aÖç5d=Â4ö,*6_°®6Ø6¢04¨è4w©—4K‹5|÷¬6Zæ6Uõ5‚Ìâ7Êš7ƒ(÷7¶6 n4ôd4mg'6Mo8Ó7dc97†1W8E”7Üyé6ŠEô5J "5›¯4‘‡z51}É6~¿þ6&ÙN6ê/@7³šV7Õ98Š7ÈÁm6SÆ5û÷4¨*·55Š-Ê6ÆÛ7;:š7ä´7­¨Í6Ö[v5”þ5ù 6»Òþ7…±{7"Ö¸6> ”696__i5È©s4§2µ4ÐÛÍ4ßøå5»ŽH7Na×6ìºY5›.5.G5¼¿5HÖ4E ˆ4|Ï_4œáÔ4©t5Õc63©f6(j^5o’y5T­–4 ¹×4Ï4CQ_4`2–4f{4ͧ‰67(Î72!06 4Ò­$4<¹ë4³º4HÊ54iä56:Õ7ð˜7/¿¸5üô5£56_3Â6ï%5j!>6$7:è)4Þ¸27n6÷57ñã5÷^z6#hü7jÕ6’ã#5³R¼60äk5{6‰3ñ6C\k5Æ×74š7 fc8>>Ú7[ ž5÷t4´Rv6#Y7 î`6ï˜Ü7óºí9UËz8¸©7‰)Ç6{À`5–jÆ4±>V5dG5ínX61CÇ7Á¿91(æ8¢5Y7ÛÞÐ7`6'%55=á4´ë04Q [6Çp8ãì48øyŠ8Û³7 Éõ6+èg5‡Jæ5/m7ÚÑî7ûGc8àÐ7ã*N7WÇ6ž‹\5PþF5.ÈU5?J5?#6Ÿ6ØR·7 Ÿ§61û½5š 7 å6ÆÒš4ê9u4¨€‹4µ 24ºÊ5Z¥6¬6ÍÆ5zT7  46('™4G4³4,í4Š2\4ѵð5Û¯6‡=F5š)Q5s'‰4ÃBk4#õ4&Øc4Fñ¥5…ð6« —6v£5·ˆu6šÆ6Àj6™ê67_5ðËŽ6q5¨ïÚ7ˆ¡8*Á§7íb6–ï61ň7©#Ö6íÂ/5û¼L6{m6L‡c8P’37‡q>67$7œ6à…Â6™à¦6е5· s6œ‰î7Ü–7ðQ7f,y9Ä\:--p8› 7J]6 û/5‰l7wxa7¸qš75i«8"@Ã:-n9£ v7°÷7N63Ä5Ç~¥6‘ |6Äô$799L:É÷9×ÁÈ6ü56+"T5¡t 5 ÑA5 J7%&Ê8>”:8›vh9s8}Å7Âmt6*e7T<-7úGU7ÿÊ6;óÇ7Í‹7„ 7 ŽŒ6Ê7k°6jpd6:žò6˜—g5Ø–5ª£5Á‚6!æ=6(G{5éæô6—ÖÎ6¬d5| ™52w[4å¤5ˆmz5 ¼¶5§$K5í%¹5…p5Š…Ï5rj}5]ëx5 =4¹Ë5‡:(5(5†³797¥£07 ˜Ë6;œ”5üW•5›üf5,Tµ5™®Ý5ð8 6ã"7èO¶9à‰8<ö¥6Ô¥ß6f?6Ì95y½63 ‰79R19e8ß|9¨ÔÚ8.q×7:a€7ñ†7Z—Ô6Aòµ6½_®7‚£9­0n:-îï:Ê:=m9AuÈ9ƒ…18˜©¯7|b7Jk¹85UÖ9¡:"úÂ:‰H¼:†ëÈ9øl8¥¥t8 I7Í"´6šž7‰g»9’ñb9†½Û9ï|9²Þó9Mû÷8žù6ÀS‘6×6E&´7g“¦8}Çd8"o…88ño8Lï7çÿt7Yž7QáÒ7Qˆœ5­Z6!V7Ó7 ` 7 N(7Å6‰òw6Š«e6’Hä7Eù–5° Ñ5Ozo6ÁË6—"q6ç(6‘k6Q'j7H²6›6+$$5¦>5·È6Œ5íÛ5Žï5…‘86'nd7U65†5'€²5¢u6C¹6›ç27ûM;8 âï7Ud6!õ;5 5ºù4\X6g%6&Hr6Ì18›Š¦9#81zœ6¡Ëw5Ûiñ5JŠí4އ³7H7)ê{8’Ñ9?9ÉÀ!8ÄÄ6ñ`6Ãî6%N5PÖÃ84kt8£†w9°K:…È£:‹$:S޹9—!8thF7jõó6VÉ7jš{7åó9ˆ•:z–:Ž™:‚j:>ù¥8À,7c^6¨ë6Χº7<“9^-9ís‰:qû?:KƒÓ9â¹{9#×98>Ã6úûC6\Â7 ±8…"8˜U28ÓjÛ9C6¼9E™ã8í„<8:¬û79>›5^!Ç5ž î70Ô 7D‡i7TÑÃ7«¢ 89.ý8¢9Ò7ôN~6ë,"5B6Ùã7¿Ü¯7L>þ6—üó6‡f6Îʹ7UV96³mž5Øa54–ø6$fÎ7’w46å¯6Çj5ðîA5óVC6Go5¢BØ5vO5Ôa&7 µ†7®–æ7•ßS7F‘¿6—‚×5±QI5q)Û4Ç×í4;Ý660H5O)4‡Çx44«f7,3 7£É"9 =9,jŠ84ò%9"Y76øAY5Y%,5„ƒæ5)7” Á7í½Ê8L%39ÖRQ:² 9?α93Ì8Dff6—-Ë5–¬7,’78yÈ'8Ʋ":kË.:zì:ŠÍâ9¿$o8.\6œ¼Q5ÖíL6äI‹7¡8nÀ9 ¨:— : _8˜Ä7è`þ7EP6F9Ž7³š8=ŒÁ7õ½¼7mw8448Рy8k•À7Ÿ¼‚7dÜ6_Ñ×8\Lì8Rv7¾¡ 6Ç“N7 ÈØ7T »7µo17†œv6¸~g5åš72rp7f»56èá”6™7ž›†7]Ãr7­æ7'„6I&Ã5N`^59›á5¼Ÿw6gVZ6F·Ï6öSõ6§Òˆ6 "5ìE[5~Þ4ø >5B±5¥^­6ͤø6ì96Ñ•H7†ZJ6–¤»584Â94„Mz5ÑG*6)„8tú7õ3F74·7ÕÍ6(&ú5Ä]5{ž 4©%÷8 8p¦8ØŠ8=È7•uÆ7ãÜd6À‡7ø7»Jÿ6° ü7 US7B…7Ƕ8ôús8dó8¡L8ZŸ7SÛ}6£u¤5Ú/76/²7¤,¢7íÜó9(Õ96ÑP:fè†9ÜÕŽ80Ì%6F‘5+)5Ô7r6¿†6î>Ò8‡ø½9d“‘9—fb8¸§<7 ×Ï6È5H`€5¬Šœ6Ë V6) 7™8úˆ«8›?6î4ù5JKz5wFó5¿Þ79*70ß6FY45 ’þ7˜Ñ7K‰Á6L¹~5óF5c±5:À6!Í_6¦l5F—Ï57jœ6É 7_#6A™15zïG4µF‹4c…ß5â4ÀlÂ4Ô%)5­â61*+6¸² 6ˆ5Ѝ4‘8­4F›4ó³ 4a>A6å¸X6þw“6‹ÇÚ7.÷J6'å4Æw4¥ý4™5£J4PÚÛ7'BZ7A’•6dN'6y5~ª÷4êç4Á œ4‹&¾7 ^7Žu\8¦°7ò±·6g H6•y5Š+ì6‡[6“s)5–ä6`36† —7†¿ 7o¶‡7À8]õl7„_$6he,7†Ïœ6ÿè&5žÆ³6!ê>6*G6~7£‡Ç8â¤}99‹ó8x†7 5ÿC5`oº5G\k6³Q7Ä¿28@ë78[Ô<8IÑ7º}5YIL4±g 4ŠñI4¬ÄŒ5"~[6˜có8–8ƒ™5þçƒ4„ö>4sÉ4ðÜ„4¡ÉL4¡Sª49ÏÙ4ã \7Σ{76ë5jkú48yü4ˆÝR4º3t5  m4xa4iFÚ4«&ƒ6WÑÿ71}—6BWÌ4±Ðb4-j4¯ø5¢U4€ä×4M“4¦éÖ5¿%e7‘vÓ6¨Í4æJè4+ˆg4ˆ46÷x4L”d66DÍ8T 6ðêE5±4—e4$³+4÷¬w6@r/4WÌe4‰º36<¨V7d/…6^Žg5³’û4¯YC4#.5 !È6”-f5³Q5ü< 7$ì6Õ˜l6Y¥56Œ6Á5¾Ä!5~R¶5CÎ#5ý)Ó5¿Rs5Ò’'6bVò6Gx6Éc²7ý-7AR5ð6T6†Ó5x¿5‰¬5Øf6"È´7A¼7¡}Ž7ßCþ7`HÊ6P-5|d½5{=]5h2^5½DÓ6~457>ËÑ7¦´7ÎÕ6O¯S5…Ã5Rþv4£|4¸–k5 -¨5šsÑ7†sŽ7N|©5üÇ4ö4°jw4½-O4lTw4w,¢4q4§7s#6±%D5AÈm4õ$Ó4÷Å24µš‚4JŠç4:-4?M‡4V“5ô¾Ç6ˆ(5¤$!4¤*4PFî4$÷ñ4[Ÿ¥46|ë42 ¾44O›5 #=6Ê!t6ÂÃ4ˆ”4œÁ4 xé4)?4[Ïw5º§7/67Þ5FË4H 4[Ó4dMñ5 S64?-ç4“65͇O6´Î6÷¶5eÜT4Œ Ê4%Úg4†ìÿ5O`û51NÅ5L5ŒÝ‡5Õùa6Nô6„¾µ5âqÍ58?¦57k64a5L]752 Ï5[Ç 5GŠ6F€y6ùa¨69I[5’óy5˜¬…6-å»5.°Í5!¹E5] ¼5 Ï|6_zK6Ó'6c„Ê5Üü´5\¿¸5¡Ë„5Ä{5G5š5]Û5§Àz6›1Ã7NUo6Ž ¾5¨¾€5“¹ª7FN 4¹võ4²©±4éj5ŒÙ6=c›6çoÛ6ÔK4ú–”4ÃQ15»ÛŒ4kà04yF4£¦Ô4½HÀ5Ãì5ë56¡§6¢ë6ŒóB5‚×ü4/Œ{47¼4R©œ4kþê5:_š5Ž 4ãŠ<5.¬­5ŠÛ4£K-4,—´45e4?í4BD™4džD5‘ŒŸ5BÎ4†l®4Lž649“ !     } ¶HéèP !    ¬Œ ¶HëèP !    Lœ ¶HììèP !    ì« ¶HÔîèP !    Œ» ¶H¼ðèP!?@4 4ÿC,ËC¶H€!?@4 4ÿCDÍC¶H€?Tv-?TQh?Rƒç?PO°?O›#?L_?Eª4?D,ó?D ¶?D*l?Uì(?Uìp?SðL?QJ?O|?K–?F?Bã?BUè?BËÙ?W¸?X&7?TÈÂ?Rbî?Q ?Kå?D¤å?@ì?@!?A' ?Y)?[¹?TÁD?Sü ?RgL?JŽ×?BĹ?>¾?>Ó¾?@)™?YÅ?Y>f?V´M?Tô&?QáÂ?Kzñ?EiÈ?<é=?=?Î??Úu?XûV?Xr(?V“¾?T!¯?P.û?K3?E7‚?>  ?>ä ?@­Å?YÆÉ?X±¹?TŠU?PZ_?M/}?IÒû?E¤?B~›?B½?BR—?Xi?X(¥?S œ?N‰?L ¸?IÝ‚?Fª¼?D«é?Dê?CÜÊ?Vrý?Uº(?SD{?O–£?KÉG?I»á?GIÖ?F ¢?E£ô?E$–?Uµ?THí?Sã?P°i?JU?I ‡?HO?GIW?Fè?FAl?V”?WÖÀ?RÜ5?O@W?Nk©?K¸ž?GL?C~™?C£t?DÀ|?W¿ð?Y{9?U^ð?Pãš?NÚø?KQB?Gtª?@F¯?@S?B?b?YsH?Zõí?WÂ5?T$Ì?Q:¨?KD ?EÄâ?;à¼?™ë?]®?Zމ?X[#?SÅî?M €?G+ ?Bnk?@L»?@?@A%?Z!–?XÀÓ?UN…?Pkj?Ml?KÙ?DÖÚ?Bà?Bv?B%×?Wh?V3ò?Sûo?Pæ?Lßú?Kb²?FAs?D&{T?>YÙ?@³a?[‚^?`ÉI?[o?VVl?R”È?IT‰?@)R?:$?:?<Èý?]£±?a~'?^ZH?]°$?[µ®?I:?4N?4Øî?6ÕÆ?:™ó?_ä2?a‹?`°¢?bÆp?^Ì?@8?3‹?4Äb?7~¬?:õÿ?^:ñ?`.?acO?bÖß?S??¤ö?9„ ?8_8?:]?<W?\ýå?_~?]>s?Wf ?NLô?Fù2?>”]?>Âr??^€?<²{?Y×(?Z­…?Wµm?RKÈ?N ý?Jâ?D*?Ba?A<º??­ç?VÆM?Vv~?Tä°?QS?M¦Z?K?€?FÅ?DWu?Bwôê?\Ì?a‚s?b³?[ÙÞ?UGf?BE?:ì?8¸?7’ù?:u>?_K£?d$?hõ3?j›ê?Y–ô?F1h?-« ?.ø¶?0QÀ?7w?`®Ž?eRA?kÎ+?lØë?f#„?CTu?#¶?'äT?2Bt?8ˆÆ?_†?c•z?iáÈ?b¨?Z…Q?Bj¡?4xa?/_€?4Ån?9ŠÌ?\§Ó?`ùã?`rß?X·8?Qé>?IuM?>Š0?:Èþ?;‘¹?;ÿ˜?Y¤™?Z×Ù?YI?T ??OBÐ?Jú?Ee‘?Bh?? ??Çë?Vî"?Vû}?V…^?QÈÇ?MqT?JÃ0?Gÿk?E–´?@9;?@´t?Tãó?T‰Ý?S¥å?P@(?Mz?JÞY?I>ø?GUƒ?B`>?Bw‘?W£?XŸ>?Wáo?Tä®?NÔ ?F“°?Bß?A†í?At?AK¥?Zb?]9ƒ?]F«?Yží?PW>?B)?>¤?=Yø?=›”?>¯º?^|Ç?a?dÝ[?j ø?uåt?_=*?6x¬?*ÑŽ?*†Ž?0ÜÇ?7·ê?`WB?dÿ¦?lŽÝ?zˆµ?z›ï?2 ‚?TÄ?Š?/K¬?7B×?^X„?cf—?iø?f® ?g*Ý?>^?1ΰ?*£á?2Fï?71?\£Ž?a ?`Úp?Y§ ?Tg\?LR¾?@ÝÁ?7éj?8Ö?9œÕ?Zj»?[ ?Y1µ?T%\?Pv?Ká?G}ä?Ck?MÅ÷?JÉh?GÆŒ?E¸??’Â?@Ù?UÄç?U¶?UGè?OEu?Mç?JËK?Hc—?F/n?BËÅ?B—%?WŠ?Y&Ä?X8›?SA?ME¢?G€…?C.?AÏT?A†É?A´®?Z(ž?]ÀP?^wò?WK?KIV?Cy¢??8p?>IŸ?>'??Eï?^M¥?`Â{?d/?]‚s?L¾Þ?FÄ]?7©Ú?8_"?:.œ?;ë„?_þ?bñ^?fW|?i¶?Wr„?@’1?5ÍÛ?.ßr?3Š›?9‚?^4?e7Z?f±Ð?t.?a"?DgŸ?'!Õ?(Ñ?1Ö?8Kˆ?\Ú\?aÿ™?dÚ'?c;?^ó]?GÝq?0_?/@‡?4ä?8ªá?\YŸ?_ü/?\Þq?X6Ô?Uel?L²%?@ y?8s?9¬Z?:ûÊ?\¸!?Z¼Å?THÁ?RŒ|?PÏh?L3X?Fåk?A“c?<ë?=ò?YÁ?W$?T«W?Q€:?P›2?Kg?BÉU?Bå0?X¨ì?ZŸ?Z‡Ÿ?T½c?L³ý?G;0?AaZ?@¹ž?@á?@±²?]mÀ?^ ?]ȶ?VE˜?>Å-??+:?<›^?\òÇ?^ñ/?`)#?\á,?PŸs?GØæ?<Ý"?8~@?:<Ï?;2a?[»›?`•]?`è?_$ÿ?Wîw?M÷ï?9I ?2Xâ?6œ?:Ê?ZqÉ?]Íõ?^DZ?^Rü?Y"ê?KYH?964?6ä?8À ?;¾Å?ZŠ?[[µ?Y¨ž?Y?Vç)?K?>¢r?;¹?<~Û?>"=?Y­õ?WÿÜ?T}å?S“‚?R[?K¢?DýO?A9&??¡C?@H+?WÓ?U?SEG?Qؽ?Pf›?L”?F ?Cº•?A¨^?AÏV?T¼p?S;?R8 ?P½D?OM?LK+?Gy¿Ê?@Ã5?=÷)?XV•?YÕð?Yå:?Wïh?TJÕ?N•C?Dv%?:…h?<·9?=J8?WšÉ?X¦?YŸ?Yu®?U*¾?L0v?AÝ ?Ÿ?W]?W4M?W U?W`í?U;?J©‰?A×I??¯²??ÕÅ?@‚ü?V*E?U`?T?¥?Sàƒ?R¾ ?K5K?Dàb?B˜Å?Aé‡?B†?TôÐ?SS?Rhƒ?Qž5?Pm`?Mú?Fã?DÕ?C5²?C#R?S–P?Qœ«?Q8[?Pt÷?O??NgN?G~-?DŽa?DX?Db?Tr?TY?Utá?SÚz?Já?H/?F@8?F#ƒ?FåF?G~?UÅ?U[½?Vä?S`?Kõä?Hìè?FÎ?Et?F$Á?FN"?UvS?Uø?VU?RØe?Má6?JHy?EÄf?DˆU?DÓn?Dd¡?U|ò?Uç?Uü™?Sœ·?PeÜùW>á&E>âìO>ãÊT>å8*>åx>âíÅ>ߌ6>ÝÃ>Ûcñ>Û‹>Þ'>àÑ`>âëÌ>âfR>àÓ>á<ž>ßÀ>ÛbA>Ø^ë>Ùr\>Þv§>ß¼9>á´o>å)>åä>âF >ÝqÒ>ØÒ3>ÕTV>Õ{>Û.Ò>ÛR>݃ >àê>Ýæô>ÚRu>×wá>Ôóß>ÒÓ¡>Í?Ð>Éæ&>ͼÍ>Ðq>Ï’Ž>Ñ^Œ>Ö}'>ÓÑd>Ю3>ÏÑS>Êk>É$«>ȤÞ>ƽ>Âú‰>Ã/ú>ÃaŒ>Åê²>ɨ€>˘°>Éö>Ê>Æl>¾UM>¸2e>µD>µøÕ>¼ÿÙ>ÃñG>Çc›>ÀM’>¿;+>¼ÁZ>¸›3>¶l›>¶Ol>¸)>¼!>Àb|>ÃÜ>»¶>¶S°>¶cs>·©Ó>¶ú1>·ù>¸c>ºñq>½è6>ÀvÜ>»GC>¸‚ >¹}œ>»->¸CÌ>¶ÏG>·ã;>º+²>¼žU>¾Ì^>ßÈÎ>éî>êì>éž@>é^…>éb>çÐ<>ä>áFæ>áx">Ýpé>àÏ%>å§[>éÀy>êOÄ>é°Ý>êØœ>傉>ÝÕ>×p}>Û³>ݲ->â?T>é°>î´ó>íÌ>ê8j>â`Ø>ÚßÀ>Ôh >Ö „>Ù(">Üöÿ>æv7>î?…>è«I>Ýß>Ù*`>Ö±>ÓlÓ>ο>Θ¶>Ñ»Ï>ÖøÙ>Ö¯ù>Óß®>Ð(à>ÐT >ÐgÒ>Ð:”>Éõó>ȶ‘>Ê8²>ÉÐ¥>Á)W>¿±>Àeœ>Äìh>Él>Ë)è>Êeµ>Èaû>ÁêÊ>±èv>©cÃ>®MI>µ`Ü>½mL>Ão>Æ8Ü>»Wz>·Žs>°sc>ªUS>ª´J>±k¿>µÐ–>¹½1>Á¦>·'˜>²´>¯÷‚>®ÕÖ>¯^>±D%>´2>¶æW>º,i>½‡²>¸˜>µT+>´ƒ>´]>²6É>¯D5>±ÛL>¶E >¹¢™>¼|»>àô>èB>íÂB>ïDe>îûb>íT}>ê¢J>åe#>á¬ß>à³ >àª>èöé>ô¶>õ(í>ôÆo>óT>ñòj>é¯>àrA>Ú!å>Þ]>æ¸w>éñþ>ñä=>ùÊ¢>øçµ>îá®>çL>Þû¼>×>½>Ø¿i>ÚëX>á{î>ð»?¬F?if>ìþ&>àø>Ú]>Õçc>Ñ¡5>Ñ¡>Õ«>ÞÌà>å®>Ôh>Ñ–J>Ð>ÐÂü>Ñ\}>Æ_p>Ã=8>Æ.n>Éñ%>µ¾×>®?Å>º©'>ÃÐ>ÈÕº>ÊÙ>Ä´@>ÃgÖ>ºñÕ>¢×H>š¿>£ä>°b>½¨>ÆÉa>ÆÄd>ºÈb>°ñ >¤x|>›á'>›ç+>¡Ý5>¬us>²èu>¶¿>½[4>¶°$>®ÛÄ>¨¿q>¥É>¥uK>§…}>¬ã>±¸>´¿º>¸ôÎ>¶ÿª>²{›>®ö7>¬äý>«‡·>ªœ¼>­m§>±óI>µžÃ>¹%¥>âFº>èsH>ï…ë>ö„Y>öë>ò]>ë¡>åŽ>áw¬>ÞžÈ>â5Ä>ëˆã>÷Äý>þöÒ?´0>üò%>óf`>ê!m>â–C>Üø?>àaæ>鮄>õ® ?œÖ?æ?@>ù3>ñ"Æ>å)Ô>Ûð­>Ú5>à r>îg¶?q?ƒ·? h?Æ¿>î/>á¥<>ÛM>ÑD>Óbz>×£>ßgt>ú°~>ôýø>ß'ï>Ðqc>Ò[°>Ô-V>Å.]>ÀÔÒ>½qX>¬€±>•>Â>²h>Á¤P>Æ[G>ÇöW>À&:>ºGq>©¬’>èR>‚h>ŠC>ž˜Ç>·ß_>Ã!>Ç:,>¹éµ>®,Z> j»>•Žï>KÌ>‘oO>ži„>®®>¶f$>¾0>¶hL>¯A>©NG>£÷N> -1> Ù5>¦­>¯8>²5Á>´9þ>¶³>±"í>ª©>§ªh>§T˜>§È>ª= >­îõ>²LG>µø]>äeN>ë †>ò/J>øH>ø/©>ò*i>ëh>åµ+>ᜨ>Þ[u>äz>샨>ùC?†Z?/7>üÝ~>òŸ>é²×>âý^>ÝΙ>ã;9>ëÒ>÷1?NQ?_^?PŒ>û¦0>ïhò>ä_>ÜÝZ>Ü,ž>âìÅ>ð¸|? Öü?Å„?ïë?¬M>ízÌ>à>ÚLQ>Ì\Ü>Î'.>Ó‡D>ߤ^?M? äd>í $>ÙØj>ÕŽ•>ÓÛ>>Ä~>Àp2>¸Â>’HÆ>VCH>{%ä>´ é>Ç]>Ì”©>ËÄË>¾ðÜ>¹~ç>¨nð>„æ€>Te>tj>šD>·o}>Á˜¾>Ækq>ºEØ>¯-ø> ™P>“N×>Š„>Фb>–GÌ>®*þ>´Û˜>¼„©>µð¼>¯®6>«–ø>¥ > f‚>Ÿ'f>¢§>¨N>¯9ý>´18>µLØ>°Ë >«M`>§0ç>¥•'>¥þj>§¤i>ª å>°9$>µyt>ä´Œ>ìÇ<>òÌÄ>ôÚß>ó`]>ï' >ê7>å)#>áY†>Þ(D>ä)ß>ìyw>ü¶’?7>þï¤>÷YØ>ï„>èS">âŸ6>ݽ>âÚ >éä>ó×_>ÿtD?Cb?¾,>øˆÔ>ëšU>âýÕ>Ü‹„>Ù›¤>Þè>àfÍ>øT?D?Ï>ÿÃe>êÁë>ÝàN>ØQb>Ì€q>ÏÈ>Ò‘>ç³+>ûñû>ÿ >ãM>ØßF>Ô4¦>ÒCS>Ä’ì>¾>x>¸çp>š.>—zn>œÉÄ>±ï>ÄØ·>Ë9®>ËžÌ>À™È>½¬¸>­¿P>“+ª>É>‰â0>£j¦>¸3³>À‡»>Äî;>¿¶>·”j>§Òy>š±R>‘}Û>’0Š>hØ>° b>·/]>¼Ýc>µîB>­ö¿>ªqL>¥ø&>¥²‰>¤)x>¦ >¬Ø’>²=>¶Ï¹>´gœ>¯Óä>­o>©æj>¦3Y>¦´f>¨Ã>>«iv>±–ú>¶ÐF>ãX&>èf>ìs>í4+>îø>ï. >êS–>ä´N>àÚB>ݰè>âÈ>é>ò²Ç>õ(>ñF->뼓>én¨>æ>âsq>݇´>âw¬>é’>ðŸü>ó1Ø>õD>ôâ¿>îÁ{>é‘n>åR>Ýè>Ôž0>Ö79>Ú¡>èw*>>ò©&>îÑ¥>á¸>Ø=ó>Õôˆ>Ë6->Í>ÌØ”>ÑÖÂ>ÞÐ>èîæ>⓽>Õ¡=>ÏŸò>Ï`³>ÅU0>¿’l>»«°>»µõ>À°–>»2T>»YC>ª–>Çhø>ÉšÇ>Á >½c>´]“>¦¾>¥A>£»‰>¬+Ù>¸À–>¿¿e>Ä„D>¿i$>º%ý>°Aš>¥ >šÄ¦>>ò>¨•M>´V‡>º,>¾7~>ºâ>³ÑÕ>¯I >ª°$>§‡á>¨´«>¬ós>²10>¶*ë>¹œG>·=Š>³}Ó>°ÖÐ>®k]>¬"ˆ>­Ç>®îé>±‰Â>µY:>¹ L>àqŠ>ã€T>è5X>éÚù>êti>ëMZ>çPF>âÞà>ßÖã>Ýœo>ß\±>ãF¿>çA>çl>åå÷>ã=M>ã½¼>ãÉ>àŠT>ܰ£>ÝQ„>á„j>ç\>å¬U>åÐ>åÏÛ>å@¿>ä {>áäH>Ûo£>Ô,}>Ô]>>Ó™ >Øö?>á´B>è¬>åîÙ>Þ*>Û¶>×rš>ÌcÛ>ËÕè>ÉÖˆ>ÍAÎ>Õ*)>ÚT>Ý »>Öj%>ÍÛ×>͘>ÆëÍ>Äž>Â’/>Ƙ>Ç@i>±Š>¾‡v>Á»E>Ã…v>Æ‹->Â$ >¿‡>»dÙ>¶´À>¸4>´Éù>µ9Z>º•>¿•>ÃY>¿Â>¼N£>· Å>¯ø>ªV>¬G>²2>¸L™>¼tÂ>¿lœ>½ø–>¹©I>µGC>±>Ó>­w >°äÏ>´€>¶ë>¹Zì>»Òµ>ºÕŽ>·÷>µ]•>³ ó>² »>µ¶>µÚ¢>¶wÀ>¸s>»ç>Üç>ß{>ãÈ“>éxB>æ‘F>ä1Ê>â >ßû>ßÊ>ß7¿>Û­b>ÝÏ’>ßHp>ݽ‹>ßÓA>ààš>à„È>ß@u>ݧ>ÛðË>Ù†@>ÚÌæ>Û>ÙÑz>Ý:q>àBæ>ßÏÁ>Þ­>Û|>×Qˆ>Óÿ>Ô]>Ó‚ >Õk>ÙÎ^>ߎ0>ݘ>>Ù× >×îÑ>Ô¸”>Íük>ÍZt>Ì®ó>Î2.>Ñlj>ÒÜ>Õ˜)>Órâ>Îí=>Ëêv>É2>ÇÖ>Ưè>ÇL>È­>Ç”º>Åe]>Ɔ$>ÆÔh>Èš>ì—>€Ñ>Àl=>¾¥›>¿¼€>½Í>½eþ>¿Á•>Ât>Ãs8>Àå>¿²>¼w_>¹e°>·7ß>¶Ù„>¹aÈ>¼ùX>¿i>À«>¾ÿ…>¼Ân>¹ÿ >·1>³£ž>µ¿Ü>¸S2>¹òI>»€=>½k>½TÍ>»I>¹*Ó>·O>¶ «>¸$>¸à>¹-E>ºC¤>¼‡‰>ÙÒÃ>Û÷‚>Þz>â ">à¦f>ß,/>Þ!`>Ý…>ÜC|>Û„®>س>Ùßï>Ú¡ >ÚlA>Ü!#>ÝjË>ÝÇ>Ûì¦>ÚÆ‡>Ùœ">Öç@>Öž>ÖP->ÖER>Ù!Q>Ü>Û²¢>Ú> >د>Ø'â>ÓY>Ò¿å>ÒOº>ÒÐå>Õ0ž>ךš>×XO>Ö@ö>Ôæ^>Ó™<>ÎöP>Î\ >ÍåÂ>ÎSU>ϯ6>ÐG>ÑG²>ÐÅâ>Ïk:>Ì×]>Ì «>ÊL>Éž>ɽ>ÊK >˰X>ÊPá>Ê8>Ê®å>Íðt>Ĩ>Äýi>Ä-Ì>ñ>Â|>ÂJ>³­>ÄœP>Å >Âø>¤>Áµ>À’9>¾èÂ>¼ƒ>ºûX>½³ð>Â…õ>Ä®Ã>§>Àc9>¿b›>½òg>¼?˜>º'@>º#r>»Œ>»s.>»øö>¾ j>¿V.>½æ¾>¼]ò>»÷>º&|>ºˆ>º¸µ>¹øÌ>º!Q>½D>ÝÚ‚>áK™>áˆT>á=Ô>â%D>ã>˜>á­ã>ßîD>ÞÉ•>Ý >àÊ>ä1â>厹>åèâ>åçµ>åÛm>åÉØ>ãïô>áUÛ>ßÁ>â:I>çÒª>é±’>ëxd>î>î’o>í‡û>èê >ル>ߤñ>ä>í}>îÉ>óCp>øWƒ>öì>ñ+7>êg>ä€->à&¢>ã†>ér2>îÓV>õŠÂ>ø¿Ñ>÷»Æ>õ¸³>ì¡p>ä©ÿ>à+b>ã(>èã>î- >ôQT>õÄ">ôÅÊ>ñ—>é˜h>ãRf>ßw->ä"Î>ê¡>í_8>înÉ>îÂØ>í¬@>êpµ>å~*>àý>Ý‚Ò>àcÆ>æ¼Ë>è>ç=>稘>ç”:>å®J>â,Ù>Þa#>ÛN>Ü'>ÞÔÁ>àb>à¾È>áÕb>â"'>á>Þ®2>Ûƒ+>Ù2[>Øý½>Ú¤z>ÝLá>à¸>Þ°C>ÝÐ,>Ýo>Û¤`>ÙD>ת>ÜŽ4>ãóy>ãÂE>á™±>ÞÚá>ÝÊ:>ßú>ß:>Þ“n>ßÈ‚>Þï4>æ´ð>éJ>è>z>æ‘ò>æA >éÙ>å­©>àþú>ßOf>àp>åŠ>ë«Æ>ïºì>ï©_>îÄ›>ñQp>ë­>ã&Þ>ÞØ>á3Å>ãœÖ>íP>øÃp?‹>ûJ>ô3®>ëCÇ>ä]ð>ßz)>â…y>è-ò>ñÚ¾?U?>ü’Ä>ìD>çmâ>ãå„>ßê¹>â<ô>èLÄ>ô;'?|à?ßu>ø½œ>êàÔ>æ–—>ãO>ßtß>ãp>æÑ7>ïk¥>÷¬ð>÷ >ñ™‡>êËW>æM>âŠZ>Þ?÷>Þã]>ßAí>âû˜>çP“>鬗>ìeË>é9>äi>ߌ5>ÛÉs>Ú~å>Ûv>Ü×ý>Þ~¡>à®4>ä—f>âëÝ>ß)ê>Û¹(>Øê9>×¾·>ØGù>×®Á>×IØ>Úg>܉:>ÜE|>Úê/>Ø• >Ö}—>Ø’®>ØYF>Ûߨ>ß©>ßr>ÜÍi>Ü{¡>Û<>Øâ>Õå>ÛTL>ß]a>èÏ´>ê0ì>éÑ>å–>âã(>ß[Æ>Ûu9>ØL>Þy]>è—Ä>ðá>óã3>ôãP>ñc¤>æå+>änÚ>ßÓd>ÛW–>à»Ù>éº>ôzg>ü}„?Σ?íâ>ô›ù>ëϨ>ãç>ÝæÇ>âÓ;>ì„ý>öxD?-é? ×?ë«>öÚŸ>êû >ä,>ß!Ë>ßÒo>çÎ>ñ€±?­?5õ>ûÜ)>òV»>éÌ`>ä©>ÞÞÇ>Ùæu>â8í>ëªí>óJó>ôà%>ô(Ê>íÀö>éÎÖ>çpÕ>ßùM>ØÞÛ>ÛÞš>ànà>ãö5>åû»>å<×>äF>ã²Q>áÄL>ÜÍ >Ø3>ØËê>Ú§k>Ü#ç>Ý>Ý>>ÞŸ>Þ¼>Û¢°>ØZ>ÖYæ>Õü5>ÕjG>Õ5>×'^>ØË >ÙòÁ>ÙÈö>×|Ø>ÔšE>Ów´>ÓÇ>ÖÀª>Ý$™>Ýõ?>Úfs>ÕG¨>ÓÔ>Ñõl>Îc >ÕÆØ>Ö6j>ÚÏe>æ3:>ê°°>âðº>Ö˜U>Õ…>Ôá>ÐÐ>ØB•>Ú 3>è'#>õpò>øzO>íO>â=>Þ':>Ùf´>Õ/Ê>Ú9½>ßÎŽ>ôO”?t’?ž'>ýÆe>öÈ>ìG9>à}Q>ÚO»>Ú*I>àøý>ök?îœ? °¸?~5?¥.>êô>áIa>ܺö>ØE#>Þ:>ñI >ð.->ùcÑ?râ>úåÈ>é>ß™Ó>Úƒ§>ÓB3>Õ9È>ÜvÄ>ßï€>æ5>ì>ë Ô>äE²>ÛóÁ>Ú³Z>Óœ>Óƒ]>Öõâ>ÚE¥>Ü”–>Ü«>Þ·v>ß¿>ÛŽZ>ÚLY>Óu>Ô+P>×ùb>Ø~þ>ØM¥>×B8>ÙjG>Ü£>Øz(>Õ+l>Ò.ý>ÒÇß>Ô.8>Óé]>ÔKõ>ÔÀÈ>Ö<>×¶Í>Ô—ñ>ÑŠ«>Îaª>Ь>Ó1G>Õ™>ÔøŒ>ѵ×>Î9>Íû>ÌY±>Ë#>ж>Ñp>Ô•é>ÚŒ>Ø<ä>Ó>Í ž>ÌÙf>ÌÀà>ËÞC>Óì>Ò÷Ä>Ô*±>ßâÙ>àáÖ>×T“>Ж>Ë£ˆ>Ì”7>Ìé°>Ôã‹>Öw>×Ì>îOx>ý§>çÄS>Ø»->Ì·ã>̘]>Ì:1>Ð?>ÐÌ>Ó>ìf??´?~>èZW>ÕóO>Ïn7>Ìf>Ï'>Ðg>ϸª>Ùr>øµ>ñs½>èŽ>Úð4>Õlˆ>Ïíå>Î)>Ιv>Ï}Ú>ÑcÊ>Ö‡Å>Û >ÞC‚>Ùaß>Óžn>ÎÔE>βf>ÏV>>Ï>Ð>Ѥ9>Ó‰ä>×]>Øa³>Ó4>Íò>Îp>ϺŠ>ÒX>ÑAp>Ñü6>Ñïº>Ñç>Ñ\Î>ϼ¢>ÍÁË>Í"t>Ï7³>Ò€ã>Жê>О¸>ÐQ>ÎÔI>Ìsq>Í!>Í>Èþú>ÊS>ÊÝ>ÆôJ>Ä9»>Åíæ>ÆÊû>Çji>ÈOH>É ·>É.Š>Ê6/>ÌÞ'>Çy$>½Ý1>ÀÁ>ÃÔ$>Åè[>ÇÏ©>Èí‹>É;„>È„ž>Çœp>À¤«>³ùƒ>À3©>Àqþ>ÂÆ_H>ÈŠ+>ÆO>¢œ>¹_ö>Ÿò(>¡Ê>¡êÇ>»%>¾Í´>Ã…à>Æ”‰>Å‚>æ˜>¸…›>¨†æ>c;õ>Z”>¯=î>½x0>Š3>Ä÷¯>Ƙ>ÂuÂ>·°ù>­]>>H_>­}†>®õ>»0¶>ÁD>Äßþ>ǯ0>Åùæ>¼Ð>ºd>¼¸—>½–o>¼©„>½‡>¿ùq>ÃP>Í‹ç>Í‘>Äà#>ÂàÀ>Ä?>Ävù>úT>À3d>Â9Ú>ÃÌ>ÊÒ>ɤ>—Ý>Ãã¼>Ê Z>Êh˜>È]‹>ÆVú>Æš5>ÇÞ>Éz>LjË>Ä=¿>Æz¦>ʼI>Êÿž>ÉM>ÇZ>Çû>Ȥ®>Âáæ>¾·>¼¬>»dª>½*J>ÁÞþ>Áv>ÂSe>ÄÁp>Ç8û>ÁO>»>±;±>­.>°’>·p¥>»ûX>À‚Ì>İ™>Ç>Á,_>¼™Æ>¯èì>¡“>ŸJh>¥®9>±>¾lê>Ç4>Ç(„>¼º>µ3j>§Á>Œâ>ƒc(>ŽT>¡R+>µþÁ>ÁÌ>ÄÜñ>»``>² ò>¢œ>‚Q >b?´>€æ¬>ž@«>³.>¼ÂÝ>Á¯’>»Ûj>³|'>¥`¼>™Ôû>Ž­>•²¾>£b2>°Ï>¸³5>¿5Ÿ>¼T5>²€K>¬ˆq>®S>²±4>®%Õ>«…Â>±Mø>¸o>¾öZ>¼X®>¶>µ.>·å–>ºl¡>¸ûž>µÖ>´ì²>º,$>À>ÁEÚ>½Y>º›H>»’'>»QL>¼ãY>¼yc>»SN>¾ÕØ>Âã>ÅmX>Áî>¾Á>¿«R>Àe.>Âå>à ’>ÂOÈ>ÃG.>Ä×_>¾—Ò>»>» >ºÜp>¸p >¸ü@>º§á>½«–>Áe/>Äóº>»W>µ@V>±Î³>¯ÐL>®­>°‘ƒ>µ=>º++>¿db>Ä6·>´º>«¥->¨ >¡|#> ‡a>¤&a>¬Çœ>´j>»G’>Â(@>´ó„>ªãû> >«Ü>‘'/>˜ðS>¤*v>°Ü >¾6y>ÃmY>¶G}>ª&&>ž:‚>!M>‡4>‰GY>Šó>±-p>¼|k>Á;â>·Ô£>®J>¤>˜à…>$™>’VÛ>¡[5>°oé>¸Ýð>¾NÝ>¸éå>°§µ>«~«>§–É>£Â³>£Ë5>©«B>²)ë>¸Ûð>½p>¸ô¼>³=¸>²‘E>²í >´S‡>³Q–>³„>¶<>ºÇ>¾0=>½¢y>º>¸«,>¸pÎ>¸¢ê>º¢m>ºß>»g‰>¾ >ÀÚ–>ÁîP>¿3 >½2y>¼×ý>½\E>Á÷¤>Á‰o>Àc;>ÁX[>Ã>»G,>¸Õ\>·°Á>¹—@>µ>²>Õ>µÌ >º1Q>¾‹V>ê)>·ê>´Ñ>°xï>°ZÁ>®a|>®2ù>±ÜS>¶¯Þ>¼+ô>þ>´ªI>­ã[>£œá>£Wf>¤½;>§p¯>¬xú>±b#>¶€ü>¿µü>´}Ø>¬Ã›>¢ªº>œÀÞ>ûž>¡Œ¾>¦÷Q>®> >µÈ>¾+ã>µ“Z>­„–>¤7q>bâ>šŒ>š,á>  ¿>¬ké>µ~>¼á>·ƒß>°cú>¨< >Ÿ2>›k^>,ã>¤õ—>®òÊ>·>½²N>¹%>³O´>­¥Î>¨{>¥‰>§$P>¬†+>³B5>¹:–>½©'>º#.>¶ i>³ë>°‡E>¯«•>°,©>³ >··œ>¼+K>¾¼>»<>¹“å>·Ò‹>¶$›>´üÈ>´?>¶‘Á>ºÌ³>¾Oa>À^(>½Ãx>¼ö…>»§²>ºI^>¹š>·x»>¹Äi>½°>Àâ>ÁÞ¨>ºÕÒ>¸ >³õf>¯r>±z1>³–†>¶W;>¸ö©>ºxe>»'‰>¸.>´À>¯Œ¯>¬9H>­Âå>¯Ð:>²Á’>µÒÄ>·Ét>¸Ôš>µÎ¤>±.Ÿ>ª F>¨Á!>©¤@>«a>®¡à>²8>µˆG>ºQ>µ%‹>° 6>©ò >¦u>¥V›>¤Yâ>© A>¯?->³'>¹C;>¶®>°ã£>ª÷N>¦•Î>¤2[>£¹Ü>¥â²>­6×>³ÄÙ>º!>¹Y>³*À>­6%>¨>¦í>§[ž>ª>…>°5>¶å>À›l>º9>µF>°»>¬Eš>¨äý>«œƒ>¯ŽR>´­Z>¹çß>¾Áù>»/R>·úC>´«¥>±‘’>­·ß>¯™~>³²L>º„>¿*>¿¦Q>¼~¿>º„P>¸ê>¶ƒë>´ ÿ>´Ò>¶f*>»I’>¿} >ÀAl>¾—>½,¨>»Ï‹>ºG>¸PV>¶$=>¸M>¼! >¿Àí"?ì74A :Y@Ô¬ì@ÑGAÃt¼B›™žAkŸ(@Ι?Û›ð@ 1?½Xâ@Þ¶@ák@rÃ?AÜ—{BU)A–„ô@%¯%?ô;b@ Vƒ@ùv¨A×ÝæA«AýB†å‹CWàBŒßA‰):@p½½?êTAÊ–‘CŽ Bá9^CUCÃSCYlB[¤@ÇÑ¡@ƒÁê@ƒo@¯hAû8¨A¤‰ªBfð×C1xCR—ùD&„CEùŽA–7 @~e-@%Ög@…¼/ACÐBCÏÂB¸¢±C´ÒC+ABSc[Aû@~`’B98ùC×iB •LA»h²A2ƒAÜGWAEáé@$áÔ@M÷2@\Þ²A8õ7BˆBirëAØ@«­õA9tã@™"3?ÂãÁ?ùNê@µ‡@&½ãARnoA±,RA¦+@ì@ô@Ñ»o@é? Ù?ÀœÈ?Ý—@ q@JÎJA´ŸSB¯©ŒA“ö@OÂ?º³?¶*?Æ6?榿@³´¦CmBëB­P…@÷HA Ø×AÜJA™Æ5@æãA‚,B¸Qe@[¢pB•U Bõ@µe AsñPA¡%‰Bæá¢BÚGA0ÖÜA®q*Az¯BMlAÀ§¬AD B˜ÌBž-C»œ?BØê@ükÍ@1Ó A¡ãB‰þ/BlGACpZ«DÒÕ^D5ÿ=CCgAøC¬AUf@.ÉÖ@áU Aj$lA®Ï3C?D®´²Dö)CXÓ)B…'•A€Y¡@²»3@2i§?Î:ÍBDXmD`ÃþDutCÖ~BˆÝÑA©†ÒAk'@­!âCWÊCwÌ\CŒæ,C`ÈBÔ BY*@Î0@¬\A?Ë@¼ØAþ•ÐBUS¤BŠ­A¯„AáC ª°BD•@fúç@&+ @2‹1@83º@×ÊA—__Aš¡À@ö§jB‡¸0A¥ÓT?Ä@Þ?™‹p?ªˆ]@HQ@NÎOAX¤B]¸Aª@ïÉQ@@Ž ?¡¯ ?¤ˆÂ?Ä02A B(«Aò¦ A4ý¨A”„¼A­Bð,AµyAmu³AíÁ*A&˜ÏB–kÀC¨d(B† cB×ÚA¯O)C&ÌBjwAx?§AŽ{Aɲ"CÍ®œC÷A“ \B”ðCä¸B]i›B¿A†ŸA4„ZB^ÙCB„_BâüIEA]EªÇ[D ÑCžBÀYA5?Bô ÛC5ã’B²æbC kE›üŠE ÉOC-ª€B}>A±F¨AD»>B0BB9¿B¶¨€EÅETĶBx¶¼A¨Ã}A7†A—AVðB¢Ý+C»ðpDO$DšHVCùoC?¼A¨ËBÑK™CvÏÚB‡þA¹YNB…ëýCÊRBŠœ#BGGrBèlCAç1A¸ 1BzQAUyA'ª¾@ÿêËAŸ¨%A¥òÅAf©‹B¼cAÙ{@øŒÝ@¯þ…@buÈA‰µ@‹ÆA$ÓAiÜÎA:‘Aš@ïã@ÚØŒ@‡5@780AZ¥@”¥@ÿvÏBjC#WØB‹¢²A¹PAxØÈAÓG@©ñ AèAläpB …A˜ÁàA‚»UAÎAÃBš…Aþ¡œA¨ÅGA#»€@Œ½µAƒ²‚AiÊ3A ô7A·˜A¥¯B…=F 7EРŒE -CñBç´ÉAÓ¾BçZ•CaêšD„†Eö§«F ŸkFýE¼TtD=‚­BßéBjèL@Ç~%@óÚA=5ÃB”$zB®kèAÙÝAY™C³¬BȸAÉã‹@ôÔ@€•U@¸)åAŸ A›wf@Ãך@´à9BFÕB(ÈA¾ê²@÷ux@2ÿ?à_@”ïY@=Âm@Q4ç@–}A®µòB6# Aƒ®–@Tí@5º?Ãa@pRå?ÞBb‰ÄBzñ>B Ø2B¬ŠÞA¤¿@C`í@vø@˜J@“®?ÍöCB¤ñAB¾ä.Aá$—Aõ’@û#ì@g¦ˆ@>^@ 9QB‡wC |4D$`õCoU!AãÖB ¾AAøB;IBhNAñêAÜÿŠB0ECá>BìdƒBü5CÚâ]C‰ÁAå-Cñ“B|\¶A“¯AŸ¬A§ë6Aúø•C!CçD_€ÒD¶úCõÜB‡+A{¹í@ÝSâ@Ä™~AƒÙ8CB‰C¾?!CØÈºC~þ»Bçb@ÖFÎ@.ñù@ ™@*_ë@ >)BG”DÔC„¦A{_¡@Ã@s@m†n@‹œ@›?·<ö@_åHCKÆ¿B´bü@ç,¹?µëÖ@ø@7Ÿ0@Š­Ä?ôðÏ?æ ¨@(ÇžAÔÔ´B¯7A¿¦©@/YÜ?«(ö?œ}D@—?þ7“?ûÄG@$™÷A<™CsB&vp@cL?©('?”ÿÃ?´n­?ɾõA³¾|CWÏBm“þA.¥@L=?¢k%@t>9A½ÇÆ?ÔÏ.@ÑÓAº ]BáaAÛy:A18@,ë{? Æ@‹-UB÷A0Õ,Ax½¡B¡ÖûBR£AÖ¡rB EA<­@úÌã@ÁÑAy¨A<¬AO§yAß4ZAÄÉÜBF™“Cy«EB€spAlǪB&0Bõ@ÿ[JA+¡A@âÒA ‡yBƒi6C@êC\,MBÝ-|AÍ;5@øåÂ@÷Âz@äûA:¥¯Aú®ÏB¼'BC$?B”¸AÌÎáA:ã@ÐH@!L}@6à@Š<–AP"C–ËBË wAx¡Á@r¦€@-øæ@:Ž~?ér?óÀ)@ §G@$ËB=¼B.±@¿G@q¿@tVœ@3Œ?Ǽ±?·p‘?¼§,?Ó—»AqZëBEGA!Þ@!ÀŠ?Ídb?¢®÷?Ø”Ý?³õÒ?¯”f?±Ð;@‹.ÅBGT´AíE@#@?”†°?ІÍ?¦Ã™?ØÄA86B¬¡\AµR@C¦?ÅS?•C?á$b@‡lC?¼‡ý@,AJ®ƒB1‡sA€*Ù@â­@@ /æ?£ŽK@Ž@ÌŸ@®Ú @“:¸A êAS¢AŽO=BèA_NÖ@µ²O@´‰@Aƒ\ @Ɉ‹@¯•s@Ø»·AGîAÃÀ—Buí[A¶¸VAêcA%A«}@¬EZ@Ÿ{Í@ÙýãA•AAÜaÙBPâAà^AYí@Ù°óAËAAOb@Äs6@ÚÈÜA%m¢B sBËyÈB A&h#A­ÖBÃŽÝ@6åN@00(@e×ó@›^.AºÄ Bd;0A€ì@w@@œšA9Ah?è›È?õÒ$@!b†@:©AA5`AhZ@´ A”ŒÄB ÿsA?­ý?µ0?Ͼ›?èº@·ÊºA y@`cb@¬AIAî»@!$?ª3²?²ûÏ?½Ds?¿“»@DÂÂAˆ‚@q\@?ÉȤ?˜²FØãÏGß0!F•uE–#æF¿12GŒfÈE)áíC—sƒH[|G‚CE~\oGsädGžëDº(C%öDßCùFn3G4¹HNéG (WG’”ÞGÙe÷F›³’FÄsAGÊnGÓÎFö_Gq÷õGFs‘GÆ—GˆšuGHhcF IGÄ‹D¬Õ†CɱdGºMüCd\G1‡G(D~êE·e¦F]¦G»0EyÑáG#±GƒààF©*OC®¬ÃC^÷?DàEyD#G7sŒFnB E˜BÔF*ߪE‰¨CŸsEé<G;->G‘SÖGÛìF(7æG–àHQ?çð”/bF?çîRÚøsy?ççlüRô?è ±ñë?çê®|M)?è"õõÊy?çùÕJ‘["?çö_ÕÈñ?è †3„'?èÓ‹xö?èñIÞ ?ç÷Š¥‡@?ç÷¦Ï?ç÷• ²>?çø?XÎX·?è >o?çâfRœ1o?çæV1‹[¨?èú±b?è.ýäÉb…?çàq5aÍ?èÁ="ÑP?è’9Ä*J?è#EµÜÎ?è(#ô²ª2?çó»‚Ëzy?è)ñ’f A?è&Ž`iÒ;?çÿ8Y»??è(…Æ †ü?è*²7Ý{?çå‚ÒL„?çè׊h5?çøÝÁZÑàÓ‡?è“í[ÆL?çéCÛ™+?ç줖þм?çøJ®~~Ç?çùDg‚§?çýr&5²?è1téh?èÁXë?çéŸNªKk?çû/ýœh€?èêËLÔ?è!–ßèåî?çîYâ곺?çõޤ}«&?èWB«?èŽ5a}?èf“ÞÜ?çå‡`Ÿ•?çö8°ÅÛé?çó¿(AwÏ?çöã°kš~?è¨3rµs?çÿ –ù¾²?çÿ=ù³§?çû«¾AŠg?çü­h8?è¡£Ú4s?è4,%¼&?è º3CÄp?è±öú‚ß?葾c?è Íë¶>5?è •‰ÚÆ?è¬,GQ?è¿ìW>Ö?è Œþb|?è'ÚêþL?è§â#Ô–?è¼wŽ=Ö?èål†¦?è!0¼»‰?è…Z» Ä?è!J{J´?è7¦1^?è!‘)Å?çþpçîfo?çûƒ‚^ ¯?èå^ÁY?Hü?EÒ¢?Iûå?BT8?Zk?M×?Eõ«?8¶n?>)è?QëP?DXd?]ôW?RyÁ?K/á??¿ž?Sô>?Hæ)?Nj?Då?C.Æ?S$¯?J»k?CbE?F}?WXO?O©™??\Ô?S~R?Tµ?A’Ä?>Å$?I]?VÇC?Sâ??G3?Xv?^u~?dló?[;?>Ö?LÔØ?H-Ž?ibP?37>?.ìç?_]¶?=Øf?Zë­?eNö?7Ôm?BÔ?TO??\º?d’Å?XÔH?YX?F™Ç?^®w?>ÝE?Q;Ö?UnY?Huõ??L?M6´?q!Ø?c|?aÿSNOD,ãüÙx\zl{¨tÔ(<äÀ„Õ!?@4 4ÿC\ÏC¶H€! CtÑC¶H€! C,ÛC¶H€! C8ÜC¶H€SNODØ”Öð¤× u0v@,w! CDÝC¶H€>Û··>Â1>å¹>Úÿü>¿Ód>·Rí>ì‹ý>ÐÒ«>¼òA>¨Äñ>®ˆ$>ÙŽ>¬Y>¦í+>Ι>ºl>³¾¬>ßå~>Úá}>Ѻ)>ǘð>Â1J>Çà>¾ ë>ÓìØ>Ú\¹>Ç.S>»£î>å^Õ>ìÇI>ÞN6>¸•j>¶•¨>¦µ²>®ÉM>òy>>Þ}|>Êk>´&,>»Ï?ÓÂ?$¦>üÔT>åÚÜ>ßÀ>¨¬Ô>®µÎ>ô>×ýú>À`>ªÇ>ý ·>ä[õ>È>³gÆ>´äf>ért>Û,¾>Ý ë>â3>Ú…i>ا™>Êg“>³Þ«>½ö >¢ZI> e>ô=“>ó¨ƒ>ß3°>×?>ß•N>ÙV5>Ô¿{>Ò’Û>Ï*e>ÍâÕ>Ó:1>Æ>Èâ>ă¦>ºŒ>ÀÞÖ>¼•¡>±ù>¾C>¹Åï>¸Íæ>©sÐ>»K0>»~=>énÃ?Ñ>íõh>æ¡>á Y>ß?\>î\?Zd>þ½P>å¶|>àÏb>á"¬>çÞÙ>ßž9>Û¦L>ÞŠL>ϳ>פù>ßã¾>ÚH>Ýó >˰e>Í6¸>Ã¥->Ç͉>Ť>Â`>µ6†>®<>¬}q>º[ä>ÁfÚ>·#F>´©>¼÷²>²B¬>¡–>Ÿ>¥Ë“>ªî‘>ä ì>ãžJ>¶$THfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffHfffIþJJJJJ#J.JAJSJUJVJhJ‡JˆJœJ¦J§J²J³J¶J·J¸J¹J»JæJçJèJéJêJëJìJîJïJðJñJòJóJ÷JûJüJýJþJÿKKKKKKK%K'K(K)K-K1K2K3K4K5K:K;K<K=K>KGKIKdDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH  CPÞC¶Hˆ  C\ßC¶Hˆ pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dHEAP€8„mTREEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäø,)À i@?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™™?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™š?é™™™™™š?é™™™™™š?é™™™™™›?é™™™™™™?é™™™™™›?é™™™™™™?é™™™™™š?é™™™™™™ „õLõ !    Lý öHoèPSNOD`ü%Ô÷Pì$,ú !    Ì  öHìpèP !    L öHÔrèP?JD?VÇÚ?>÷?<9T?Fÿ.?VU5?J°$?Ntô?ZN3?7~ß?=Ÿ?Pì‹?C?iºQ?fÀ ?#‰ç?KÌû?qŸf?#b?wðª?4[?ûð?D©b?4?Ó"??¨w?0ù?YqU?>üc?kO¦?Jÿ_?p?zÿ?c7ù?-ºp?m,’?&ë?<(?z.¶?k I?xaN?)˜’?AÎ?-·¡?>N?JÓ—?Wçé?6ðÈE_lÉEtä9E‚‡üE„•áEƒ?RE{/¶EjÍEVÝ`E}aEEkE›,ƒEê!Eœ,öE”,LE‡"ErX^E”,«E¬¢LEÅ ìEÔÁŠE˸FEµËcEŸƒQE‹Ÿ'E¢›EÄŠäEïprFÅ»EíZEÊ$ÉE­1E”óŠE©¹"EΧüF;çF .EöÖEÔÁ&E³Ÿ,E—òE«/_EÒ6¥EúâdEÿÉEîŒEÓ™E²eÑE–o)E¨â5EÙ½0EâäáEÞÅ¿EÓ°`EÀ¦àE¤ô*EŒÁÇE•QE³ËŠE¾P‹E¾?RE¸–E©r!E•GàE‚„1EŽ3…E“ÜoE cÂE cÂE›ÑêE™ôE}–ÌEjÀ»Ei©XEtû,E„³áE„³áEúE|x E[/EHXóE€*­E—óEšÛEœÑ½E™´–E¦ôE„íÉEoÒ7Eš;Eº$ EÐrEÕ,™EÓ¤EÄåE¦éóEŠÐõE·¦Eé_F ™úEйwF ŒçF23‡F\ dF^åèF0¶MEò׎E¹Ý8Eܯ#FBõFNú´Fi~F€ÒF°F¶4F¬MF…ï§F0­E÷‰cF©HFe*KF‘sªFŽTFƒ™¬FV32FÝ$EÞ?ÅEò"ëFF9ÅñF?ø½F:ÎÃF!O®EöYEÁÄ%E³EÚÎÝEý_Fñ±EìÃiEä°ÛEÄL‡EªˆGEš[±E³ÛEºUEÀÙTE¸Æ8E°³ªE£·E‰OvE¢mhEÎboEùEøùðEå¤ìEÉuE¬ZåE”/ØEÙ¦ÎFƒhFI“ÑFIUF6MFåýEèHïE¹£qF1^ÁFs¾âFœ´žF¡ÆÌF˜zÉFwž•F*òEúghFfEcFÐ?OG0¥ GJ¿ÍG4^ Fâ.rF`óïF|ZFb|+F÷vMG”4ˆGÈ·¦G±†KG8,¤F~'FËÏFVìÀFÕ'{GuîüG£èòG‘ª?GFsþxF ¬HF5¨F•D±Fú.rGöWGÜzF¼’FUoâFÊåF ¿©F?E†FuÄ^F˜[FvÝ–FYb¯F#ß&Eèñ EÑŒEéѵFÎF ^ðF¶8F¬ñEËÁE±ü×E©^ŠE³EÉ/ŽEϳÑEÕo?EÍ\±EªˆGEÄEªÔrEÔÀÍEúEúîÕEìPEÕ,®E³ÉÎE—yðEãÅhF$VFUÔqFSô¥FB"ÒF"HEóHE¾$6F3C#F“$FÂÞF¾ªvF«ƒjF‰àF7ûHEûë?FjäG’EGŸr)GÈØØG—„5G¼>F}4ÕF%_FcvvG!C H*¶xHzîžH2~hG…ÑŠF3ªFéøFUÃGmHÔøHK¦QH³GX©ÑF‡qøF"\F6GF¸ {GL¤Gu¨ GAÌìFáìTFkèœFøÎFÀ¼FNÎF‡8FŽáF‡©½FmríF.f¼EôcôEÖø×Eô½F :¨F ’ÓF gNFçÛEß_vE¼”æE¨áÜEƦ EÌÀEÑp\EÏ4EÆ5(E¼”æE™ÊVE°GgEÓREî’EñÉ@Eè&QEÒÿ¨E²(äE–$£EáÛtF»FBçñFE§0F;|ØF¾äEñ‚E½QF%wFˆ¤¡Fµô©F³ýÝF¤=F‚¦cF1~GEöj€Fc2`GqGGœ4{G±loG›ßG&´Fi=sF >ÛFi÷G'ÀkHçƒH?m+H ãGI‡þF‚'¤F+·FU=§G‡*GýÁ¤HJSGÒE6G./_F~=F¯ðF4÷‡F±ÏuG2ÊpGGþ¥G%ØÉFË»aF[b F èFm'FL=™FdÞFˆnFƒR¢Fb HF#„vEìWEÕIWEó ˆF %F }2F }:FýÆEß_vE¼”æE§2\EÄöEÊ”ÂEÏEEÍ_çEÄaE¼”æE™ÊVE°¨ÖEÇ EÓ^8EÞ‹ÒEÚ¾kEÅ@~E¥ƒIEŒ¯bEÕ–ÑF ¦F!¬½F/,#F,¼FÒÛEÝ2E¬ÄF ùFIÝF„"F'ÁF†B€F`§ãF!JXEâ.F7½Fª{¢G §üGÈPFûyýF®”ºFW/%F=FY ÚFõÀ GLBGf¿GK“Fî8{F\€ÑFáXFCî¼FÎÑ}G/7GH9jG<œ©Fïç-FZÚYFF…©F‚*¥FÊžCFë–gFÞgF  ’F4BEðLbFÌF./F]|Fp§Feÿ_F@yÊF zÒEаUE½£E×½EôxFFGEûÊ#Eë¹êEÂÆE«x–E™á¼E³VsE½šÉEË®ÇEÍ,E½âE£BàEŒ+°Ešâ%E©YHEµqáEÅî¿EÇ 6E°ÿêE–xE‚@ƒE¿åÔEáHþEþFhsFDÄE÷ÜÆE¿ž½Eœ½½Eê=hF²‘F5eÚFAÜUF=¶rF$Ï$Eÿ™ßEÃ*œFmÉFC1LFu2’F~QDFs;‘FP„3F?‹Eç¯ÐFßoF[¸³F†dÛF”„LF–‹RF€u>F*Ó‰EF §FLÍ^F€‰ÛFŽš-F–@¤F„E×EºZE£BàE•)E© ßE¯Á]E½Õ[E¸z5E¨iüE› *EƒõúEˆ4E™°EžŸàEžŸàEšüE˜TE{+7Ei…^EœièE£Ï„E¹¨ÈE¹¨ÈE²ß E°7&EõŽE‡"¡EÄjöEݘþEúÿ™FnYEì>ÜEäP2EÄ/nEªf5EÔ/EíI8FÔFöaF°qEÿr8EË¢E±?iEÚ|&Eõ¯’F…Fr£FßF' EâDãE¾tƒEØÑ.EôšFF©FMFÕJEâDãE¾tƒEÁÿáEÞ AFûºF /£FáÉEôEÀ[ E©–LE¸rEÔÒEì_~EüÇOEîèúEÛ7E¸rE¡ÈžE‘?ÇE˜›EµníEµníE¨îEE¨îEE‰‚GE~¨jEyÚcEƒ±EšxjEšxjE’"¤E’"¤Erq`E^;E`©—EotÏEƒ–øEƒ–øE€qE{’bEYbåEG½ E„Š E‹ð˜E6Wý5Ü!û6ZÚ¥6ÃèG6®õh6jÑe75ä7:¾y5ò¬›5ˆÃ6¥¸7™ñ·7ƒÇ¨6æP6÷…7.6ËÔÂ7j8xF9‡/%9(ͪ8rIÿ7©÷˜7-ÕJ7(àþ7ÂH¿9—:bã9ê-Ç8ÞW8õÔ7«Ñ7iÛ7ª¦B:#ýB:Î9ÇîK8Š,$7Ä7ü7DÎ\7<½8÷š9ŸÞí9¾âf8ßRD7×7^Q7…9c¢:‰ÚÓ:u¦8µÊ¼7A<-6Àì¨6l™ô60èŒ9é›9ÚÿŽ9d¥¢8M7 AS6™ËŠ6%'5¹”x9s9,àL8Ÿ,ó8‘ϵ7Ì›)7©+«6*e6*e8ùt-9 'H8D°*8)õ°7ª,?7†¼Â6*e6*e6ŠFq6 Å6«u7 ò6éO,6oÐ]6ˆºL6§».6ÍÒ5ît 6Ô“7†«!8W^H8 «6òir6)Ëe6Â*o6§W7@ 8– ™:gÈk:Kí95q7£‰7_oØ7ô”ÿ8¯û:ŽÑÍ:ˆÛÛ:vN9$x!8 R762–8ë9߯:ˆ†ê:‡@,9—ò8eg7¥17X¨ý9øE9ðPI9Ü—À9Ÿ;7á~7ƒ‰77v9s5 :‰KF:à 9¨Ø^7ó*6o,6RÁï63¾~8¨Õj:&9î…¿8—}7%øÏ6Õ•ß6AÉÈ5µÜ 9+¾ì9<,8¨í8š¶°7þ'7Ú®©6*e6*e9 é9s8V~8;ä7Û¯=7¸?À6*e6*e7à$7Q(£8üV8$ÐÕ7½š71 Ö6¼ù6±‚Â7ʽ6ã;¡7жK7éì¨8:%˜7ß,ó7*Y7%Ç8¹vÚ7¾ß®7®g8æ9âð 9¹Xø8ÂqD8¥{´9 ü9*:Æ9@$ :5°ë:‰Æü:KªŽ90I9AÉJ8þ|:b:€V£:¸':Z¾:Ú/9ªv8Ñ(&8¢•Á9‘Ë/:,x:#6:†9½xQ9ä«8eõ&9™×%:Q«#:^€¢9‰R¾8+7ÔP&7ªåº7´Õ9b•î9ƒT–9L§38TÐÊ7n½§7|œŒ7Að²76gÒ8%Á8%Á88ño8;gB8Lï8—_¥7çÿt7çÿt8"o…8"o…815•83«g8L‰18\®7Äá7Äá8CýU9+, : U9¹~8¤óm72‰7 a6õÕP9b÷9áß:Bì9ì–8ªªL7°ïy7ù¶7¦Jþ:”ô!:6í39Ÿ³Ë9.zˆ8Ñt 8¹¡Ï8À Ð9`ú:‹X×:¥·:äM:l9:U­í:¾¡9œ½9´¾’:d·S:–°Œ:‘0Š:‘ Ö:ŠE:Ä5:–4d9™ò29ßE@:!\L:™÷:’M¼:. :”íÒ:RîÖ9!fÑ9>?8è.’:­Ë:Z“:.öà:PX˜:|9u&8ÖF7®!Ü8˜°8ù”b8¦t¢9‹‚9¬l·8úØn8"o…8"o…8>7w8@­J8°B8¸UF8ü8ü8ñ8ñ86{88ño8‡:98Lï7çÿt7çÿt7ý‹&9èZ9ÈÇ„9…¦Ù8r‘Ö7ŠÂm6út…69—8Ö™9«U:{ò»:Ë«8½©77û÷÷7ŠÚ7 K¡:g¹:Ž:Š:cº9…P}9Vä9ŠÍ8sË:Só:ˆR.:‘¦:/#:Q|:Žî:…8î9j¿À:[(::çY:~`:’×:Š67:Œ[/9Ïÿ 9`‡:Ž¡!:ÓÒ:Žû7: :(Ô:9º< 8£‡Ä:0¬¢:Šè±:Œç:ŒvZ:’Ã:Œ?:5‚8 Q²8›j9öî=:HoÙ9óÏi9á=¨9ùv?9r}{8Á;#8Å$þ8ýÄ 9 ‚ñ9xgœ9ræ9t$9E™ã8`Bñ8h¨8¨…8§êZ9 ˜9Œ9E™ã9¢8¤w>8ËŠ^8Ôħ8‹bM7³R 7 ì˜6‡äÊ6F8sÙù9rÅM9¡Í9ã7örú74ñM6Ï[ 6Eƒ9dü˜:x\:ޏ:;H09æ}8z›Z8 ¹š7ˆú{:•N…:•¯ý:‘ T:ñˆ:[^:0z©9‚“8Š–¯: r:Œgx:ŽŠŽ:ÖA:iO:‘€á:¥Å98XW9¨^Ú:•ž/:”:Õ•:’:’¼í:>K¸9XêA9Q¢ :ŒþÔ:‹h}:Šz7:“mƒ9ßÖx9¤Ô9‡ƒ9µê9Œéë9¬ ½9ȬP9JÓ@8Ñ6÷8äf$8Ò$*8¼Ÿ8Àˆì8þë]9 ™9€nõ9{\ß9t$9E™ã8W Í8^Þ„8‘ÏÖ8©«9†ç9Û9E™ã9¢9ú°9iÀF7ï5C8J%‹8›Ðj8eA6êß6ü‚9ïŒ8áfÛ8y#8Œ8¿ºO8;ÿ-7M/¾6€~­8>Žæ9˜Ïå9–Jø9%HÕ8ˆ88£ÿj8ö §8dUó9æ@Ã:˜…y:“zÉ:‰Þk:+9&>9ß­Ž9]8‘:‰z:“¤»:Ž€:‰Õõ:“x`:–¥ç9·à½8ÛŒ:=Ž+:Žß:“õï:•û:‰Š:²:V’9s9 xŸ9Ì :OÕP:r"E:sç±:ø9BÆ38cÊØ9žC8 }L8ª†²9„G 90,ê837I$j7SêÂ7mw7mw8448G—ü8ó9É8 d¨8Š´ƒ7mw7mw8 ”k8448§¬z8ºÇ8‹{8k•À9:äw8• j7r…,8œ.9q€8Ø?É79€í6>9Z@7ýó6‹a8ŒZê9ìÁ9-…(7am•6JCÊ8xMã8ò•7WÄ8Ï8Ëú·8˜Âö8kÝf7ÐòÁ8ÃH99c.9s58Ö8I8CŒÔ83ád910&8È®:Tú:‰u:H°(:8˜:‡Cü:)b9V¬´8—;¼9ú<: Ó 9® ‚9¸ó:ÿ:øª:> í9ù37å47í29"Z:wp:vsA:–Ú92uÁ8í÷7”<ü6k÷h8%rD9ÞÔ9ŠÛÍ8C)”7 ’ý6´g}7mw7mw8448G—ü8æyî8ù”y8k•À8@5v7mw7mw8 ”k8448› a8®%ì8AÂz8b08Mð#8=È7•uÆ7•uÆ7ëu~7ãÜd6À‡6¬¶Â8S‘+8B¥Ð7•uÆ7•uÆ8„‰8·ü6Ó‚K6À‡9€Œ9 *À8dó8dó8¡L8¡L8ZŸ8ZŸ9 ç9´8„D28„D28£Z!8£Z!8 ]b8 ]b9, ø9LRö96ÑP9dí|:fè†:fè†9ÜÕŽ9ÜÕŽ9'ÎT9G€S98¡š9f½Æ:fè†:fè†9ÜÕŽ9ÜÕŽ8‡ø½8½7õ9Þ,9•[(9Å@Ú9Å@Ú9›Z8Éš8cù¤8§< 9a°í9pªä9—fb9—fb8¸§<8 ¥7™7™9 £9 £8Ö Š8´Wt6î4ù6î4ù7™7™8úˆ«8úˆ«8›?8rÌR6î4ù6î4ù87cÀ8&xf7•uÆ7•uÆ7šHÐ7’¯¶6À‡6¬¶Â8=È8,m7•uÆ7•uÆ7ãÜd7ÜCK6Ó‚K6À‡8²Œ²8Åá8?¡ƒ8?¡ƒ8?Ðí8?Ðí7ø¯·7ø¯·8ÑŸg8äóÏ8dó8dó8¡L8¡L8ZŸ8ZŸ8Á¢¯9ƒV9äÚ95:fè†:fè†9ÜÕŽ9ÜÕŽ8·ýh8÷ae9µ$96ÑP:fè†:fè†9ÜÕŽ9ÜÕŽ8Qjá8ô¨9X|>9gv69—fb9—fb8ðCÓ8¸§<8%s 8‡ø½9pÓ9-jÊ9SÖ9SÖ8§´Z8`/‡7™7™8úˆ«8úˆ«8¼ÐU8›?6î4ù6î4ù7™7™8àæ8àæ8ß 8@Sè6î4ù6î4ù?TIl?R¨P?Píê?Mæ?Ií0?FC%?Cˆ²?@Ù?Vâu?U û?SÓ:?P)?I ;?C±«?@ ú?=±?Z?YÏ]?Xø¶?UpÑ?H6k?=­®?:vò?99ê?]Åø?]»÷?]À>?XŒÃ?DuÉ?8;À?6«×?6´‘?_è›?_î.?_Sœ?U’9??^Î?7L¼?6e©?6tB?acù?`M«?[y?Num??Jè?9N?7‘Ù?7>[?c‚ ?a_é?R»æ?F%ø??l¯?<•?9Ô²?8þ?_¸Œ?\¸=?Pb¾?Fõ?A·º?>Æ@?<3a?;2²?YÂ?T"Ï?O3Ý?JZg?E€ò?AóÏ?>f¬?> c?USn?Q¤;?Mõ?J÷ù?Gúê?D\??@½”?@O ?U´v?S¦ù?Q^i?Mh0?HjÔ?CãÔ?@áG?>5š?XyY?V¼S?T3“?O²˜?IYå?AMQ?<0V?:¶?] ‡?[P¸?XöÒ?UÚ?Ló?=?2•»?4,¬?anü?`À%?_&ô?Zck?KI?3ßã?-²?0×?d~?dÈ?bB»?WüP?C6p?14–?.êD?0ð¬?eȯ?eªV?^’8?Nt2?>ÿó?58y?2(h?2Zô?fƒƒ?g65?WØ?B/‰?=?@^ ?=Ö?e?9“ ?8ÍÏ?b'è?\ì¥?W]?Q¨~?J½Í?<€?1À?3Ê?h´y?e>?]?V…?J0‘?4Z?*Ôå?/ ¼?jl½?jŽ?bþì?UL?F>”?1w?(O?*^t?i3ß?h›\?`Ëb?P`d?Ai­?3—?+"Ç?+f+?dÀŸ?c•£?Z¨™?He¾?>×¹?8$??1á÷?/t ?^o¨?Zl~?Sër?J6÷?Bæ›?=Çu?8¡?4u#?ZÜŸ?VäW?Rì?Lý‰?G?A³±?ÌÞ?\?Yû?9»1?7z+?7ý?iIŽ?^@À?U?×?Jܾ?A2?8Ä1?1§y?28?vI¹?h4ê?\»R?R6x?BX?3% ?(oƒ?*b\?s1?t œ?i¯ô?Z[?CU9?-€V?O¡?‡?l‘û?o!Ú?kÐ?Zk]?BTY?.[I?ë? •?eç[?eÏ?bÑä?VÜ?BŠ|?63C?-ü6?'¹?_Rþ?[ë9?X¯U?Pá6?Ea´?@?9ý³?/‘=?[†C?XlB?URA?N¤¢?G÷?B ç?„ ?^­Ù?Z¸q?T·?Ho©?>r ?;Oé?9ÿ ?9p¤?cø?^û!?X¬õ?F‘?;\?8í?5Û?5 m?nùç?fœ„?Y ø?Fð–?9â?5«Ä?0A%?-ý:?|¤P?q~g?dN©?Riö?6^?-Rã?$ˆÃ?"[:?y”ü?€¨š?y¿Ä?aÖD?53N?áÔ??Ÿ„?sËÕ?€ž?|Ü£?aj?4/x?ÌI?C:?Î?kþ4?s¦Ò?r¥N?]Mç?7¸®?+”ã?+¶Ð?&ðŸ?aíL?`ÿ>?^Êt?T©.?CÆ“?>?9r'?.Éà?\r¢?Z$p?WÖ>?P+?HÅ?BªY?<Ôì?9!M?Xö?Uq?RËC?O4Ð?Kž^?FÉ|?Aô™?>œ?`æ?Y$?P*¬?G—ô?@„?;¾/?9œ‹?9 Ø?dÖ%?^¶*?T-ß?G”?=»Ü?8“?5²D?4¤?mðJ?igŸ?YÍà?EF?9Ь?4è?0œ$?-áÂ?~Q?x1?eË)?NZÂ?/ÑÔ?(n?%à-?$¼â?&?‚­Ï?x-1?Xè‘?/Œb?rá?ç›?ן?z‰´?‚g÷?{¦"?TY£?1’à?p?§"?ÄF?mp?s€à?nøo?Vy–?8K?+öþ?+U?(žó?_\p?]°3?\CB?S¯í?DÄê?<:?6¼­?0;?\l•?Z˜~?XÄg?Qa?IrZ?C&ü?<Û?8ëž?Wè?U¹®?S‹U?OìX?LM\?GQ?BTÕ?>?`½ ?T!W?J£¬?HMD?D2?<\>?9^?9EW?b¶h?YŠ?,ãœ?Ά?"!~?k™?k#1?dÅ?VïÄ?C¥?3ƒ?)¹è?)[Ê?^ƒ¿?\7r?[>#?U@³?GXH?;•±?4à?1—?[t?YÈl?X½?QuÀ?JÎÄ?C–Ñ?<^Þ?9Ö?W*×?U—H?T¹?P?’?L{k?G7*?Aòê?>XÜ?Y?<ì¬?9¢¯?:2Š?Y”?Q=¦?M€»?M‘æ?JöQ?:€ß?6.?6™Æ?`H?XÓ)?Qèn?LɈ?Fà ?=?8/ç?2¾“?h ?b€m?W}?Mv?D—8?<Š*?7ã?.—?mŠ?iPæ?[ô?QºZ?H²?8΂?. ¨?*.\?jíþ?hø?^µ-?Vmû?Mk£?;*u?*Ã?(ë:?d£#?cSƒ?_ø¾?[ Æ?JÏh?8‰?-«p?,û…?^¨„?\à?[Ñy?Xu&?I© ?;¹K?58Ñ?3m‚?ZÔ‡?YD^?W´4?QÒ?Kgp?CèÓ?Y0?Wc.?RŽ}?U/å?Q?LÔS?Iüý?G%§?CÀÿ?@\X?@>Ö?VÜ"?RV?MÏï?J¥{?G{?Csb??k¼?>ÝX?Y·ã?T­?O¢F?L¼?Hi2?C„‡?>ŸÛ?= ¢?[²9?Vî?R)ù?N-?J0?CðG?=°ˆ?;*A?\Ë$?Y?Ug?QF?LÏ…?D¶¤?<Ã?967?]H?ZK?WMc?RdM?M{8?D±=?;çC?8¹?]*¥?ZƒØ?WÝ ?R?L3 ?Cà?;?96Ç?\=?YÝw?Wº°?Q’\?Kj?Cæñ?k¸?<ðû?Wl-?Vl?UlÖ?PE[?Ká?E÷’?@ÑC??_K !    Ì* öHŒuèP !    L7 öHtwèP !    ÌC öH\yèP !    LP öHD{èP!?@4 4ÿ0Ì\0öH€!?@4 4ÿ0L^0öH€SNODdkTjx '(¨Ìd(tlÀÜe>ìÉß>ñ£>õÖÂ>÷®ñ>÷6 >ô½n>爐>ê)>ë’k>ñú4>øSZ>üw|>ûŒ>÷5>ï"Ô>èk‡>韔>ñ2#>üˆô?Ôg?çú>ö>ët1>ã«ü>ã5ª>êÍÏ>õžˆ>ùüO>ó>èR>à(È>Û¡>ÚZq>ß~î>ä>Ýzp>Õ¿›>ÒÐû>Ñ^Y>Ð$Ô>Òù >Ó°±>É>º >¼B»>Á…>Ä»>ÆŸ~>Êéª>Ì'w>¿8+>°î>±&—>µši>ºŽ>¾”[>·«>«–Ù>¦i®>¦8›>ªƒ*>¯àÓ>´¡€>¹2>¬ ³>¤ŽÁ> u> mã>¤y>©Gv>®Øÿ>´Ÿ¬>¦Î“>¢ > ?> È÷>¤X+>¨¥“>­±1>²l>ñ¿>÷dê>ûç>ý½Á>ýL,>ú^˜>óác>ípz>ñk>ø¬’>ÿ˜?W¥?Á1?úë>ø¶É>îZƒ>ïƒT>÷íâ? ÷?wB? (”?•>øÛ>ëý\>è >ñþ7>ý«ô< >èÙ>ás›>Ü£O>ä¶Ô>ì®>æ}}>Ö°>Ò_®>ÓJ>ÑÕ±>ÑÊ€>Õf>Î}ž>¹[€>±s¶>·¼(>ÁDä>ÅY >Ç0n>Ƀ>½é">¨ëö>¦7—>¬{>µnE>¼è>¯Ú|>™ûí>íý>•;°> Ù>¨¿>¯_}>µ×Ñ>¤<É>›¾þ>—å>–Ùî>›;>¡ ³>¨Nº>°I> i¿>šÔC>˜¥>˜mN>›Þ>> ÍO>§:>­–Í>ú:>ÿZ?Ù2?èi?Ã+>ÿ6‹>ùäÑ>ôli>ü7ó?O?ô~?g8?‡æ?w–?H;>÷;î>û€ ?!Ô?'K? …€? ¦d? °R? }>ø‹å>ïô“>þ¹b?¦Œ?L?y[?â>õ¬ƒ>ì?>ܶn>êÿ×>ò >ï8å>ä^Ø>߸>ÜÒR>Ôy©>ÊK>ÉíÀ>Ç‘>½Ok>³Œk>·‰>Á~>ÄP>¼Úg>µƒx>«Mâ>r>˜~¶>žŠw>­º°>¸Úñ>¦÷“>±_>„¢>†¥G> ©>šü >¦MÛ>°Ù3>šú…>‘š~>‹‡[>Š|Ü>Ž{>•T>žf×>¨œ¶>šXW>“Èâ>“>ŽÚÄ>‘Ÿà>–àÅ>žu>¦þ ?É]? 0?(›?!`?]?Dû>ýÆ,>ø¶Î?R?Õ¿? (ñ? X§?ï?ÀÍ?k»>üÙN? a—? €¾?¸? âX?6C? à¨?=C?÷K>ûúÿ?¸? h.?1ö¬>Ò©>ã×ã>þžä?5?ã? q>ðà6>Ús‰>»T=>´ ^>¾™(>ÁZ >Àhg>Âx>>Å*r>ÆkÚ>¨„f>— >‰Ùœ>‚>~ ‘>ˆùP>©Gú>¹Âf>™Êí>…„2>a#õ>UÒ«>`»×>€©æ>˜Ñá>«µ7>”51>ˆñš>€…>}}P>ƒž[>‹èQ>—œŠ>¤ Ö>–ü>Žó>ˆ!>†•”>‰ut>g>˜j@>¢zý?S@?RØ?K?Ï!?~Ê?5ø>þþN>øô?be? ¬¤?Þ.? çÓ? ’ñ?Â?ï>ýï? Zd?û?Äz?6k?§3?m’? ì?ý >ÿ7? ª?·l?#q2? Ь? q? ’f>ú-ÿ>ÒW>ãY? »¯??'n?90>ø€å>ãü¶>´Á³>µù•>ÃC¥>ÇÕé>ÇP­>ÃCÓ>ż>Γ>¡ïÏ>Š‚>eä&>Us(>`@“>‚°s>§ï¾>¾¤z>—J„>p0>.J=ñ¡>!~r>iÊs>>£ž×>ìÎ>ÄQ>kþ>g>§>uJZ>…j¬>“ïÒ>¢H©>“{¯>‰Áv>¾‘>;{>ƒ^ü>Š` >” â>  ã?ú¿?˜m?xÅ?ê‘?/Š?]æ>þ¯Û>÷ö‰?4…?? R(? »?àC?0z?t˜>û¦M?"8?ÉX?·ö?c3?ný? ;ë?§R>þ@s?œ|? £?Î?"ñø?Ï?¾ƒ?ñ+>÷Œ•>Úûž>î'c? a‚?ÿ?kp?Å>ô³;>åO»>±;í>´H3>Äa>ÀÕ©>Âá\>ǵ>ÌOÓ>ÑÑÀ> §Ç>xÛþ>LQy>m)Ø>Œæ>ªlÅ>¿*¨>j/>o8>Qã>÷t>:êÖ>wxŒ>’…¢>¤‹O>Êš>‚KÎ>kçª>f»ï>ul>…±÷>”Õ:>£5H>”¡ >Š‘þ>‚»>o–>ƒu/>ŠÆ>•‘> yµ?Ô\?‡N?¨Í?ÊÂ?ÚÐ?—»>ýñ¸>õü:>ï3ñ>úàx?¥=?•Ï?_ƒ?Hô?ù>ù-€>ö%$?y¯? 2¡? #a?Iù?=Ó?'>þz>ú}5? ê?Ÿë?P=?^ž?Xñ?¡Ö>öÛ>ã¯>ûy­?·\?Ä—?@?X$>è_i>ßsê>´•>µ ¢>½ëR>ÆÖ>ОF>ÕC1>Í´8>ÍfD>žÜ¼>€P>}O>ˆ $>Œ‹y>”¡>§È5>º@O>Ÿºì>ƒiE>[øq>Z‚Í>eŸ>‚ÈÊ>˜˜¿>«Hk>–Ζ>Šˆ>€cs>{õ)>ƒMv>Œ¾1>šLÄ>¦Ï³>™y>‰Œ>‰)’>†ã¼>‰¸ >ð;>™ŒJ>£Äs>úkî>ú­Î>üöø?~?[ó?*1>û+\>ó >ïzð>ô™ä>üß3?wZ?³8?Y¡>ûö—>ôêú>æj>öc>ÿ=‚>ü:å>òb>ù >þ°ü>÷ù >åXr>ö‰œ>þEk>þ­…>ü"ì>ûP>ú·˜>ñ½U>ÙðÝ>æCu>ìÚ˜>óõm>û#>öt;>æqÁ>Ýü">½-->½“À>ÄÎi>ϱm>ÙI >Ø·Ê>ÐÎ>ÍPy>¨ôB> ‹k>¥ð®>±_=>«;o>¦¿>¬ ò>¹Õ>¥kù>™¹n>‘aù>‹æ˜>‰Ž±>T'>ŸO¦>¯=I>åì>”‘>”O>‹¦_>ŽÇÏ>•©!> JW>ªIñ>Ÿ9V>—ËY>‘å…>߉>‘¹g>–¶>ž×,>§j2>õ K>öï¿>øJX>ù<>ùÄÅ>÷Ó°>óhÆ>ïkg>î«>òã£>ö ‚>÷ÇÇ>øq>ö²>ó¦x>ïZK>èHñ>îׇ>óЭ>öSŠ>ö`>õS>óä*>ïI/>Þ¦ö>äì>ëe>ïEþ>ñkÜ>ð—7>ìÈ>çuÃ>Õú>Û>âf>è8r>ìw›>ëž>å«ô>ߢX>ÅB>ÇŠ!>Ì>Îôd>ÐaA>ÐG÷>Ψ…>Í—u>µ9‹>´¦>µê>µ°U>´Jç>´ñÒ>·¥>»Œ‘>­¥>ª> >©’)>©Ö>¨¢>ª²M>¯9…>´˜H>¦œ> hu>†i>œaW>œù?> rÉ>¦Íö>­¤>¥áÔ> Wd>œJ“>š«2>›y?>žàì>¤â8>«jò>ïp>ð.Ø>ðë™>ñ>ý>ñ)>ïî×>íz>ëTõ>茖>ëS>í‘®>ï,Ë>ð$^>ï—e>í…á>ê~>á©>æw6>ê7Ã>í™>ï¸>ï?ò>í{G>é§(>Øš>Ý >áØª>åÜë>éÍ>éÁL>çÙi>äÂ>Ï‹(>ÓžÞ>Ùy‘>ÞŸ<>ãá>äB¦>â7Š>ÞZ[>ÆÍ©>Éul>Íe >ÏÑ–>йN>ÐŒ¹>ÏK×>ÎÔ>¾+>¿Kù>ÁQ°>Áð>¾b¼>¼ÖÍ>¼`$>½·L>µÒj>´è>µ³,>µ4¹>³l«>³8ü>´™¬>·l>­”©>ª„ >ª¨>©e‚>¨v™>©›*>¬Ó4>± Ù>¬E>¨*’>¥.>¤Pº>¤l8>¦ˆð>ª¦ã>¯Yž>ñwë>ñ¼+>ñ¢`>ñ¼>ï8>ë¾(>ææØ>ã1l>õr«>öæ >÷ý…>øé >÷ž>ó,>ë’Æ>åé¢>øÚ™>û¶³>ÿL?®X?D>ÿŒÔ>õ¿Ý>î,Æ>üQˆ?‰Ÿ?–Š? n? RZ?ZÂ>ûVÏ>ó,s>ÿ(^? •? Üf?? Å$? Ï>û©ª>ó¡@?S?? Dƒ? s?¬?+>ùÃ7>ò\]?\?Y\?‹ï?F?” >üM™>ö$>ðn>ùƒr?<ð?b¬>ûFk>úK>÷ó¥>ó”>î·Ì>ôÃ!>÷«Ï>øeþ>ø¹>öÀÿ>ôIÓ>ð²6>í [>íÏM>ïâ>ðº¸>ðø»>ðœ>ï' >왋>ê>ð^>òŠ¡>òóC>ñëß>îÐé>êÄ>ä„>à™>öëè>ùHò>ù«>ùÇ>÷±>óíš>ìš>æÉ>ý ý>ÿK>þÙw>ýû?íf?J%>ûTe>ïù“?ÚO? .?iÉ?ëk? ÷r? 8?8º>öûÃ?‰¶?—?Þý?Æ=? ³=? œÅ?†>ø#å?í"?ñ£? :?è?Tn?‚„?¥y>÷—>ýWÁ?P²?`?]?+?ö>üß>ôt¦>ôw€>ú]s>ýöî>ýú>ü\¹>úñJ>öÔ·>ðð¯>ì«>î½ó>ðë÷>ò>ó§E>òã¶>ðDe>í …>å¼[>çäÐ>é³¢>ë P>ëèÙ>ë«_>êQâ>è©W>í·å>ó3W>õÝ)>óÃÐ>íŽ}>æ£é>à”ø>ÜŸc>÷øV>üà4>ýÀ>úˆD>ó@>ìø•>çÑœ>ãîN??ÚŒ?áô>ýc>÷g½>õ—>ò@õ>ïlº?fV? rb? ކ?V ?±'?[ý>ÿ)>ù ]?ÍY?p9? Xj? I?ÏÙ?ô ?­Ï>ü;”?^É?? ]{? ô´?W‘? Ýa?ç>ûno>øF >îÞÌ>÷Ì7?q¾??o1?Áé>øšÐ>ï¿>éÔÁ>í]È>÷È>ûãN>ü™;>úV»>ó!Ã>ãj6>åÐ>éqï>íl>ðŒ>ñ}™>ïÖ”>ì ¯>Ý©j>ßçŠ>⬌>åå>ç5–>è/²>è 9>æÂ©>ç,Ó>î³&>ò¦™>ï,P>æq2>Þõž>ÙÌM>Ö_(>ó'>ú‹?–J>ùé–>êµy>â¾Ù>ÞñÝ>Û•è?Ì«?Ú}?Êe>þÔÏ>ì˜ÿ>èPE>ç¢û>ä `? ’º? ÁÜ?7N? {ã?Ê>þÅv>ùrn>ít[?««? úi?‰¢?Ðß?¾C?‰O?x‚>ó¥ð>û;?'?ù??“Ù?? ª>õ³€>èYa>ë•–?é? h.? ¾ë?”€?cH>öeÙ>ÝŽp>ß¹ë>ëÇ>ö•l>ùp>ûá`>ûÁ”>ñÛ8>Úín>Þ4>âÍ>æ@ >èkÌ>él>èF>åÎ>×+>Ù#>Ûv>ݲm>ß¹Z>á³>áÙx>á3>ÜwØ>߬>Þç½>Ûɽ>×­>ÔB">Ñ4›>ÎVÙ>ÝÇþ>å»ã>æ¥Y>â0†>ÚÇj>Ö›">ÓƒW>ÏIF>âÏû>ò¸>ô:ê>í-«>ßž>Û•¡>Øýù>ÐG>é¤ý>úÉ?£q?­’>÷óé>ëé¿>å€þ>Ôgv>å9->÷„1?b?õò? < >ûÏz>ï’V>ÝkØ>ÝŽT>÷MK?Ï??{„? ªš>û71>ïÕ0>ä•þ>Õ ï>æ;¦? I?ä?HQ>ò…>î{s>é¯>ÑÀu>ØÁ>å¥>ë±>>éês>æ¶Z>æŒ>⫉>Òp¦>ÖRQ>Ü(@>ßzÓ>àJ >à³?>à¶m>Þûa>Ь´>Ò »>Ô?>ÖHö>Ø=>Ú³>Û¨·>Û£_>ÔÔ>йª>ÆâN>Æo>Èb>É@>Ȥ >Ljx>Ì*£>É—->¿·l>ÁX½>Æ´>ÇŒ‚>Ç?@>Æö>Àp‘>Çß >ÇÅ>Æ&>ÄWô>ÁÕÐ>Âv#>Âu…>à >ÍUy>Ó¯>Íá»>Àé->·•0>¹ˆX>¿°E>Ìd‰>×ø‡>ØÞ>Ȉâ>»ú}>®ie>µ·Ä>Âc>ÈE>Ö©–>Û”>Á»>¸8À>µ(Ù>½‹>ÇŒ>ÆŒF>Ìò‚>Î_i>Ã5>Á¦ð>¾ú0>¿õð>ÇW¼>Ëa>ÊAy>ȃÞ>ÇYQ>Ǧ>Ãí>ÁƒÔ>Ç>>ÅS8>Æ»>ÈÐ>ÊÂ$>ÌV«>Í@>Íä>Ì’‡>Æß >Ǥú>É/Z>ʈ >˯L>Ì>Í'S>Í>Êèt>ÃY>¸€ß>ºI>Ávz>ÂC >Á¶e>Áæ@>¿á|>´Âb>«£à>±¥>¾—>¿‡>¾ÌÒ>¿^‚>«j>™Õæ>—´ñ> Ø>¬îµ>³¬v>»±ú>½¢q>£Á¤>–Ɔ>††.>€I0>‰‚:>¶>·U>¼/}>®4.>¤’>w1x>OÆ8>n°ƒ>‘:ð>©>¶ã>¯aT>Ÿó™>kŸ>Z܆>‡e>¥¿º>¬=W>±—f>¯¿´>¢{¬>‹Ì®>R>¢*>¬ÆØ>ªò9>¬ì¤>³ç6>¯Rc>¬ð>¯ëÖ>´W.>²Ðé>°Šc>³jö>¸5Ê>¶±&>µx½>¶ t>¸cJ>¹{A>¹QZ>º)­>½e>½)9>¾5>¾ÇJ>¿!z>¿[>¾¥ï>¾^Õ>®ÆÎ>ªû>«Üó>± M>ºwá>ºq7>ºv->¼1ª>¯ð>£KÊ> ãº>§Y)>¶É”>µ¤Õ>µ&â>¸Œ>¤Á>’C>ŒÓi>‘ÖT>ŸÏ>¥’‰>ªÃí>±Ø>Žvn>jsu>Qâ>XË>y}ô>G€>ŸUè>«â«>† q>;ùI>²S>" >I•ô>„:v>œt­>«+s>š6>Q»Å>/‘”>6´Ü>[æ›>‰R>>žŽ·>ª Ú>™ú‡>‡¢‹>~‘F>†>‘ø>›î÷>£¹‹>ª[@> øê>Ÿ7 > "¨>§LÓ>©Y>©†>ªo.>­”)>°æ>°é>¯„ß>¯§í>°k>°ð¢>±8¢>²Ér>¶§E>·m}>¸â•>¹;>¸vú>·š1>¶¤»>¶¬z>§À>£ë¥>£¢>¥%›>¨vk>¬‡>±Wh>¶ŒL> YV>šÿÓ>™5¬>š2]>õç>£'>©Åæ>°äS>—®>Ði>Œ.>ŒÐ_>‘·->˜og> ù>©ôü>öÖ>‡+·>šÊ>Ûë>‡ï>]÷>›(„>¥ºo>Œ”Î>½>r÷e>rª>€®>ŠòÅ>˜TI>¤4­>ŽøÒ>„FP>xÝÁ>wÑ•>‚´>Œuˆ>™-6>¤D´>˜"ã>Éq>ŠtT>‰—C>Ž2=>”æ?>³K>¥ê…>¡>"å>™µ>˜¶v>šÇ*>ž6h>£2>¨L>©–A>©R«>©‘>©Fe>¨r×>¨f>©ê>«i7>°=%>±±Á>³¥õ>³®Ý>±Ìy>°>®£‡>®ú>¦Å¦>¤^]>£ùt>¥?>§u¿>ª×+>¯%ƒ>³“%>ŸÓQ>›øÛ>šý˜>œ >Ÿ1>£hè>© .>®ß%>—–>’>zÃ>‘¿ >•â®>›i>¢Rê>©‘>’B÷>‹>ž>ˆ>t>‰ >¯">”a–>%ý>¥ì<>Ú>‡xt>‚H«>ó>†w‹>ŽRV>™ƒg>£ð‰>’ D>‰ç™>ƒó©>‚ïâ>†ÜD>Ž^Ú>™w£>£±½>˜Ðh>’Œ >?n>Œ.>ŽÝL>”‡">²>¥/Ù>Ÿõ>›è?>˜6)>—E>˜ˆ”>œ –>¡¢L>§‹…>§wj>¥ü0>¤×Ø>¤#'>£Þ>¤ï5>§Vq>ªÄÀ>­¯ž>­¾_>®>­ÃŸ>¬Ãç>¬sÿ>¬Óê>®m¬AÔÿ1AYeA×Ò˜BA1˜B,‰Aç½B…VqB¸(IAoPAÞšB#lkCÏÕCô_Bc_BtßB˜ð?BIûBbìC“fBEOÉD¦wCîîØC'œåB«láB¦Š'C?—ÒD†-EÞüEfïcD[C"C†·Bü!7BƒÆC(I$E¡·ÂFcEE)XDB0CA€3BÂ~Bº¿C›ÇuE¨E<=‡D\:_CTeBÚþÛB‚¯E g.FñÿExòD3FB¾ŽøB>@ŽAéRøA®u=D… 5EWöÿDázÛC“;²B‡ZŸBª0A“JA7hDšHVDª{HDø„DʪCIÅ¢C&ÓÌA¨ËA¨ËDuÿŸD‹2ÂCÁö·C§›C'ÐÒCÞûA¨ËA¨ËB\Až‹B(©ûBŠþQBfÝAì}ýBÕwB%hiA’¾+Ak&~BQ¡8CÍšCÔb—CŠ/Bo ÜA§qOB?yíB% B„dDø‡Eä’†EÉ÷D²î8C!E:BÜW‹Cq1·D?ý F ×^FöŽEó%ÿD¢0íCž’B³¬…C‚ìäE[êéF¢ËF`”E×CCáã)C"çZBÕ¨¸D'ÛElü$EY‰‰D†ºÅC^^öCE³B“&WDïÖ¸FdoF Ì^E&¥B›ÃAëÁsAÏÖ˜A±A D&~¼Eœò8Ek7óD§B£¬GBR A¿A3WAD©]ëD¹ÝD%¿íD’Cz™CW§9A¨ËA¨ËDŠdDšHVCÓ…‰C¹)ÕCX¤>C5²hA¨ËA¨ËC\ö›BÎB÷C”äòC¢ˆgC:p6B¯*ùB9ôRB/ OCGîB`ÞCMÒ4Cf¯*C·‘†C\“B§ÄB£{;D6å3C<:ØBÿÅC€ÀîE_ËRE6Ç»D?¿ÇD#0çDŠsD§ß%D½z·E³,¦FÞoEÈØ\D®5D¿!Dzõ®E’SìEýF ÁŸF ^iFØ‹E(@wDNB|D UF`‡DçVE„~¼F ˜F ð*F ˆ¤FÎcFLF i|EM€DÝjëF §^F ÜèF 5F PF)¤F ™¤E7¨=D!CäE®:&Fü F óêF „GFº´F ²Ed?CŠ`!D?éWEs‚¡EÅ©EpnÞE^õEvªDï!žD>ñDBiìDz@3Dˆ—ÈDôöÆDï‰?Dð¶—DÂÝ3CÝ'·Cäß­D§‘D%–îD‰#zDƒµòDÂÝ3D•ÏD"0 DH¸ŸDQÒ.D t C0Ö,Bˆ…BêAœÝCðyHDïhqEÚ˜DZCs B²o¯BL|WAÂUºDáÐFxF E¸°D¯}C÷"µC‹Ã CÃF=F3FF óF[{E®ßE7‰D«@F «F u™F ‘FÔªFi;F|îEÿ´DµÊ©E& ÐF‹¥F –ýF Þ¤F˜F´™E»¨ïDÕéDκ±F ÝF z&F-Fb½E\¼¿E!ÊE1”E3dôE ö>E*<›EEä»DÈ DNQDa<>DO;D:8D=Þ3D{cmD‰)eDýOD÷áŒDð¶—DÂÝ3CÔDCÛÈ:DÊÊD&º'D‘{ÇDŒ?DÂÝ3D•ÏEw7DæƒfCkå CÇX½D§çC™>:BfÞ·A—ÚzD›¿ƒD^G–C“gCÿ‚YD=sC¹dŒBÊX(AýnC»ë/E²E6 D¢þ½CûþD!¹âDr¤+Cá,GEcKFh¢FoÓFõŠE‚VáD£Ò-E\”fDÚ(F©F™1F ÷Fí2FmsFµE5T¶DX˜EºîF ÿFéEFç?F¢iF ƒEÓ™,D~—rD‹‚øEI=‚EÌôWEîÇ«Eð†ÏE˜ÒDÀ‰Cà£DhCDD5D(*EqüD­¼3C°ˆIBÆ[,BÐûNBéÆÛBéÆÛC±—ŸCÄÔCàÑ@D$ëDÞ¡DíU~EM†EM†D6uCþ‚"B€Q3B€Q3Dˆ+þDˆ+þDS‡D1×÷BjèLBjèLB€Q3B€Q3DwHDwHDõ>Cïo]BjèLBjèLC´ÙuC¤*CcËCcËC%¹C§‘B=sNB*RRCºf‡C©·+CcËCcËC`´gCY6?BP”JB=sND0DC#mC¼ùþC¼ùþC½(ÀC½(ÀCu=âCu=âDN¸DaÇóCàì.Càì.Cÿ«CCÿ«CCŒaÖCŒaÖD>ôD}wDD……D²0Eãµ»Eãµ»EYÆ|EYÆ|D5pûDsô0D†ÐaD´I Eãµ»Eãµ»EYÆ|EYÆ|C΄KDÄDÕ|˜DäAtEM†EM†DlïÚD6uC£(]D–Dœ>ÿD«ÜDÐ+ODÐ+OD%a­CÝ’B€Q3B€Q3DwHDwHD:2ÍDõ>BjèLBjèLB€Q3B€Q3D]È•D]È•D…C½©ëBjèLBjèLH· G¾/¶IO#GHW<2H@è¦IVèF{†’GB·«HÎßÜG¡£%H3ÔFæ G¤ŽOH~T`HøHSÙÕI¥H9yÚH_ÑI1GHƒ5EäÇÐGWšGa7\I[à~IfaH€¸’GI%°I#I©Hoé½G¹T°G&lHj(+HÄG1H˜I;‡›H:nŸH ç¥H\j;Fû²›Hâ¸[F)üG/×H ÎI,dyIÙªHA¹G•¦@FÛßîGWŸF‘YjI @ÀH:ß›HhÖHþtIpŽhI³šFnÃFzAG§¤ÒG°Œ,I ¯0In»‹H5óÑIäõIqÞÐDüåÍF†cGgפHÊKHî Icø#Fæ:HxQÍGC–jHÒõHGˆrF¸¨ØI/¤ÛHmjGÿ+›H'jHT òI†UòIè#GÆ5JHØX³GÝ`éIF’(IQIO-§J#4¨IŸ|žInPÿHŤF?cÆI‹qJ[´Iˆ¨ÇIAžóH'Ç&GŠØDÖÆAHåË×I¥éÃH7õÅG(”WI—:I›]G7+IxgIx\ÏE+2HgkD:‚GÿšZG޽ñFONÔEç‹HšîÍI&eÚH¯|_IúI=É­IS‚<H3—H”0IÝçBJ +EIcF9HI ÈG%krHò/HD©ÎIN#G¹:(G%¹I3§µI€ôÂGs¿¸Gˆ[¬H €íGH•šFËŠ9GµNIi…Fõ±}I:ŸaH¢&VF®ÜIª%\G¶°ÏGD\Fª°8HóWHS=FG3ÆHÊd:Fïi›HÛM#I,PPG”iÁIv£HŸ<(GYÕÎE\ aHúí¶H~ >E}¶GGÁ5DÒ.6G¥ÞHåÒ•HçÌïHÍîeHÏ´GbŒ±GÆøIˆ7aHZÔ_GFÞFùõF±¨FÄ^œF®tUF¯ô²HÕNfI;VûGÍmH,=\HÂYISÀH@Im¬HËézFv¾F0,¡I+èHÁrHÿÃPH¹lÐ?èÓ'²âè?çûnðÔœ?è׉3Ž?èŒ„Ø Û?è ½a¾v?çÿ«5:>‡?è®_fÎï?è )?çþ§U¬¦?è ¯V/?è ½(›?èU7œõ?è uàÿ?çûnj-úð?èrfÐ.?è –¦y­£"?èHXi_?è™0äxž?èv{~Y?èO3åøf?èe2€ ?è :%9Ùš?è‰_e(ø?蕦´¿?躶Ïê\?èå öó®?èIò?èɿ٬?è &éë?èxlºF?èK‰Á€Y?è ÍÛÇÌ?èðÕv™–?è<æÛÂ?è¿ñ¤ã?çü–”?çÿ5»†#?çýqÝNå?è )[Ên ?è @e¨ž>?è6:v0?è ³ÈºR?èX«€(s?çü1›òð?è”'ˆ‚…?çüÆ7Ò9Æ?çò^BCΟ?èÆÝKR?èò‘(L?è ÔS¬Z?è똔?èõŸì7‘?èM(¼vG?è ¶´± ?çûJJ?çÿ© >?è Ó¿gk?è:—®Ã?çý£õp?èÚ«F?èyó¬_!?çõ¥C“Æx?çý´xÛ8?çþjê4€?èÉ*ã?èXW;Ý?è!Sã?蘣^TÉ?èŒpv´?èªÕü#²?è u8-W?è „œÁFT?çû5pJiæ?èŸîê²?è!:ß,?è:< P•?èÉ7îà?è}ø*T×?è)Îõ+?èâ!<ó?èþƒßÏ?è ”—h?èÁ/YH?çÿ7>M?èÄCe¦³?èËÖŽk¾?èz¾±»Þ?çõ:ùÂ?çý¢bµò“>þXl>›á~>ÒÐ>¼ÕE>´-V>¤t–>Êë|>ª]a?›i>àh>¡‰>š”Å>÷}˜>Ö‘‹>ȱ×? Ý>ðqý? “+>Ý›G?žÑ>Ìb>ÎÕ„> ž>¬¯o=ÖŸS>“¤ï?•?% ·?Ñ™?;Ÿ>ãœ{>n >pY’>MÎW>tç>²r¨>–#·>úvô>Ëô>¯ÿH>©üè?\¡>ïNï>Ôû>>ßTò>Áaì?N>¼‘¯>·õ>•ëù>«÷>œ[*>¨’? Vq?å†>þë? Š2>ü >î A?ç ?Þ ?,>â0?>ÝÇÔ>ÔˆÖ? Z?L>¼¯ô>Ò¥ï?à>üõ‹>à^^>ã”h>¸Z>©6 >‰ R>j¢ˆ>‹õ¥>—Ÿ>™ŽZ>ÃLÏ>âg>Ä8˜>ΑÛ>›f¾>d)Ô>ƒvë>–êE>¦ð->§÷>RžË>”u°>’ ¯>-=IæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffIæffJíKK&K6K7K8K9K?K@KAKBKCKDKEKFKHKJKKKLKMKNKOKPKQKRKSKTKUKVKWKXKYKZK[K\K]K^K_K`KaKbKcKeKfKgKhKiKj!?@4 4ÿ0Ì_0öH€! 0Lü0öH€! 0La0öH€! 0 b0öH€SNODØìfðüg <û0Ì"@Ü#! 0Ìb0öH€  0Œc0öHˆ  0Ìt0öHˆDensityTotalEnergyx-velocityy-velocityz-velocityTemperatureDark_Matter_Densityparticle_position_xparticle_position_yparticle_position_zparticle_velocity_xparticle_velocity_yparticle_velocity_zparticle_massparticle_indexparticle_typeH pLabelPDensityTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTotalEnergyGasEnergyx-velocityy-velocityz-velocitycolourIDz?€IBM pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPx-velocityy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?Collapse pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPy-velocityz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOf pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPz-velocitycolourIDz?€IBM ?¹™™™™™š?CollapseTestNumberOfSpheres = %d pUnitsPnoneIBM%8.8dIBGridRank = %d GridDimension = IBMGridStartIndex pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPTemperatureKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_ pUnitsPKIB?€H5Dcreate with Name = Dark_Matter_Density IDark_Matter_DensityIBM pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %d pLabelPDark_Matter_DensityIBMÇÃO€NumberOfParticles = %d IBParticleFileName = %s I pUnitsPnoneIBMEnter WSA I H5Screate attr_dsp_id: %d IBMattr_dsp_id != h5_errorHDF5 pFormatPe10.4IBGeometryIBMCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose x GeometryPCartesianIBH5Dwrite: %d IBh5_status != h5_errorIBH5Sclose: %d IBH5Dclose: %dyt-project-yt-f043ac8/tests/DD0010/moving7_0010.hierarchy000066400000000000000000000160121510711153200225320ustar00rootroot00000000000000 Grid = 1 GridRank = 3 GridDimension = 22 22 22 GridStartIndex = 3 3 3 GridEndIndex = 18 18 18 GridLeftEdge = 0 0 0 GridRightEdge = 1 1 1 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 4962 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 0 Pointer: Grid[1]->NextGridThisLevel = 0 Pointer: Grid[1]->NextGridNextLevel = 2 Grid = 2 GridRank = 3 GridDimension = 16 16 16 GridStartIndex = 3 3 3 GridEndIndex = 12 12 12 GridLeftEdge = 0.1875 0.1875 0.1875 GridRightEdge = 0.5 0.5 0.5 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 577 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[2]->NextGridThisLevel = 3 Grid = 3 GridRank = 3 GridDimension = 16 18 18 GridStartIndex = 3 3 3 GridEndIndex = 12 14 14 GridLeftEdge = 0.5625 0.5 0.5 GridRightEdge = 0.875 0.875 0.875 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 49 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[3]->NextGridThisLevel = 4 Grid = 4 GridRank = 3 GridDimension = 8 16 16 GridStartIndex = 3 3 3 GridEndIndex = 4 12 12 GridLeftEdge = 0.5 0.5625 0.5625 GridRightEdge = 0.5625 0.875 0.875 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 28 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[4]->NextGridThisLevel = 0 Pointer: Grid[4]->NextGridNextLevel = 0 Pointer: Grid[3]->NextGridNextLevel = 5 Grid = 5 GridRank = 3 GridDimension = 26 26 26 GridStartIndex = 3 3 3 GridEndIndex = 22 22 22 GridLeftEdge = 0.5625 0.5625 0.5625 GridRightEdge = 0.875 0.875 0.875 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 1670 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[5]->NextGridThisLevel = 0 Pointer: Grid[5]->NextGridNextLevel = 6 Grid = 6 GridRank = 3 GridDimension = 26 26 26 GridStartIndex = 3 3 3 GridEndIndex = 22 22 22 GridLeftEdge = 0.671875 0.671875 0.671875 GridRightEdge = 0.828125 0.828125 0.828125 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 1173 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[6]->NextGridThisLevel = 0 Pointer: Grid[6]->NextGridNextLevel = 7 Grid = 7 GridRank = 3 GridDimension = 22 22 22 GridStartIndex = 3 3 3 GridEndIndex = 18 18 18 GridLeftEdge = 0.71875 0.71875 0.71875 GridRightEdge = 0.78125 0.78125 0.78125 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 490 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[7]->NextGridThisLevel = 0 Pointer: Grid[7]->NextGridNextLevel = 8 Grid = 8 GridRank = 3 GridDimension = 18 18 18 GridStartIndex = 3 3 3 GridEndIndex = 14 14 14 GridLeftEdge = 0.73828125 0.73828125 0.73828125 GridRightEdge = 0.76171875 0.76171875 0.76171875 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 169 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[8]->NextGridThisLevel = 0 Pointer: Grid[8]->NextGridNextLevel = 9 Grid = 9 GridRank = 3 GridDimension = 16 16 16 GridStartIndex = 3 3 3 GridEndIndex = 12 12 12 GridLeftEdge = 0.74609375 0.74609375 0.74609375 GridRightEdge = 0.755859375 0.755859375 0.755859375 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 67 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[9]->NextGridThisLevel = 0 Pointer: Grid[9]->NextGridNextLevel = 10 Grid = 10 GridRank = 3 GridDimension = 14 16 16 GridStartIndex = 3 3 3 GridEndIndex = 10 12 12 GridLeftEdge = 0.7490234375 0.748046875 0.748046875 GridRightEdge = 0.7529296875 0.7529296875 0.7529296875 Time = 0.81751317119117 SubgridsAreStatic = 0 NumberOfBaryonFields = 5 FieldType = 0 1 4 5 6 BaryonFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 CourantSafetyNumber = 0.500000 PPMFlatteningParameter = 0 PPMDiffusionParameter = 0 PPMSteepeningParameter = 0 NumberOfParticles = 48 ParticleFileName = /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 GravityBoundaryType = 2 Pointer: Grid[10]->NextGridThisLevel = 0 Pointer: Grid[10]->NextGridNextLevel = 0 Pointer: Grid[2]->NextGridNextLevel = 0 yt-project-yt-f043ac8/tests/DD0010/moving7_0010.procmap000066400000000000000000000033401510711153200222150ustar00rootroot00000000000000 1 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000001 2 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000002 3 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000003 4 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000004 5 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000005 6 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000006 7 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000007 8 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000008 9 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000009 10 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000010 1 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000001 2 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000002 3 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000003 4 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000004 5 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000005 6 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000006 7 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000007 8 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000008 9 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000009 10 /rmount/users08/stanford/mturk/data/DD0010/moving7_0010.cpu0000 Grid00000010 yt-project-yt-f043ac8/tests/README000066400000000000000000000002051510711153200166710ustar00rootroot00000000000000This directory contains two tiny enzo cosmological datasets. They were added a long time ago and are provided for testing purposes. yt-project-yt-f043ac8/tests/ci_install.sh000066400000000000000000000035061510711153200204750ustar00rootroot00000000000000set -x # Show which command is being run # Sets default backend to Agg cp tests/matplotlibrc . # Step 1: pre-install required packages if [[ ${dependencies} == "full" || ${dependencies} == "cartopy" ]]; then case ${RUNNER_OS} in linux|Linux) sudo apt-get -qqy update sudo apt-get -qqy install \ libhdf5-serial-dev \ libnetcdf-dev \ libopenmpi-dev \ libfuse2 ;; osx|macOS) sudo mkdir -p /usr/local/man sudo chown -R "${USER}:admin" /usr/local/man # uninstalling pkg-config to workaround a bug in macOS image # https://github.com/Homebrew/homebrew-core/pull/198691#issuecomment-2495500991 # this can be cleaned-up once the following patch is released: # https://github.com/actions/runner-images/pull/11015 HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall pkg-config@0.29.2 || true HOMEBREW_NO_AUTO_UPDATE=1 brew install hdf5 open-mpi netcdf ccache macfuse ;; esac fi # Step 2: install deps and yt # installing in editable mode so this script may be used locally by developers # but the primary intention is to embed this script in CI jobs if [[ ${dependencies} == "minimal" ]]; then # test with minimal versions of runtime dependencies python -m pip install -e ".[test]" -r minimal_requirements.txt elif [[ ${dependencies} == "cartopy" ]]; then python -m pip install 'cartopy>=0.22' # scipy is an optional dependency to cartopy python -m pip install scipy python -m pip install -e ".[test]" elif [[ ${dependencies} == "full" ]]; then # test with all optional runtime dependencies python -m pip install -e ".[test,full]" else # test with no special requirements python -m pip install -e ".[test]" fi # Disable excessive output yt config set --local yt log_level 50 cat yt.toml set +x yt-project-yt-f043ac8/tests/local001.bak000066400000000000000000000000001510711153200177740ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/local001.dat000066400000000000000000000000001510711153200200070ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/local001.dir000066400000000000000000000000001510711153200200150ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/matplotlibrc000066400000000000000000000000501510711153200204260ustar00rootroot00000000000000#### MATPLOTLIBRC FORMAT backend : Agg yt-project-yt-f043ac8/tests/nose_runner.py000066400000000000000000000077471510711153200207420ustar00rootroot00000000000000import multiprocessing import os import sys import nose import numpy import yaml from yt.config import ytcfg from yt.utilities.answer_testing.framework import AnswerTesting numpy.set_printoptions(threshold=5, edgeitems=1, precision=4) class NoseWorker(multiprocessing.Process): def __init__(self, task_queue, result_queue): multiprocessing.Process.__init__(self) self.task_queue = task_queue self.result_queue = result_queue def run(self): proc_name = self.name while True: next_task = self.task_queue.get() if next_task is None: print(f"{proc_name}: Exiting") self.task_queue.task_done() break print(f"{proc_name}: {next_task}") result = next_task() self.task_queue.task_done() self.result_queue.put(result) if next_task.exclusive: print(f"{proc_name}: Exiting (exclusive)") break return class NoseTask: def __init__(self, job): argv, exclusive = job self.argv = argv self.name = argv[0] self.exclusive = exclusive def __call__(self): test_dir = ytcfg.get("yt", "test_data_dir") answers_dir = os.path.join(test_dir, "answers") if "--with-answer-testing" in self.argv and not os.path.isdir( os.path.join(answers_dir, self.name) ): nose.run( argv=self.argv + ["--answer-store"], addplugins=[AnswerTesting()], exit=False, ) if os.path.isfile(f"{self.name}.xml"): os.remove(f"{self.name}.xml") nose.run(argv=self.argv, addplugins=[AnswerTesting()], exit=False) return "" def __str__(self): return f"WILL DO self.name = {self.name}" def generate_tasks_input(): pyver = f"py{sys.version_info.major}{sys.version_info.minor}" test_dir = ytcfg.get("yt", "test_data_dir") answers_dir = os.path.join(test_dir, "answers") tests = yaml.load(open("tests/tests.yaml"), Loader=yaml.FullLoader) base_argv = ["-s", "--nologcapture", "--with-xunit"] base_answer_argv = [ f"--local-dir={answers_dir}", "--with-answer-testing", "--answer-big-data", "--local", ] args = [] for test in list(tests["other_tests"].keys()): args.append(([test] + base_argv + tests["other_tests"][test], True)) for answer in list(tests["answer_tests"].keys()): if tests["answer_tests"][answer] is None: continue argv = [f"{pyver}_{answer}"] argv += base_argv + base_answer_argv argv.append(f"--answer-name={argv[0]}") argv += tests["answer_tests"][answer] args.append((argv, False)) exclude_answers = [] answer_tests = tests["answer_tests"] for key in answer_tests: for t in answer_tests[key]: exclude_answers.append(t.replace(".py:", ".").replace("/", ".")) exclude_answers = [f"--exclude-test={ex}" for ex in exclude_answers] args = [ ( (item + [f"--xunit-file={item[0]}.xml"], exclusive) if item[0] != "unittests" else (item + ["--xunit-file=unittests.xml"] + exclude_answers, exclusive) ) for item, exclusive in args ] return args if __name__ == "__main__": # multiprocessing.log_to_stderr(logging.DEBUG) tasks = multiprocessing.JoinableQueue() results = multiprocessing.Queue() num_consumers = int(os.environ.get("NUM_WORKERS", 6)) consumers = [NoseWorker(tasks, results) for i in range(num_consumers)] for w in consumers: w.start() num_jobs = 0 for job in generate_tasks_input(): if job[1]: num_consumers -= 1 # take into account exclusive jobs tasks.put(NoseTask(job)) num_jobs += 1 for _i in range(num_consumers): tasks.put(None) tasks.join() while num_jobs: result = results.get() num_jobs -= 1 yt-project-yt-f043ac8/tests/pytest_mpl_baseline/000077500000000000000000000000001510711153200220565ustar00rootroot00000000000000yt-project-yt-f043ac8/tests/pytest_runner.py000066400000000000000000000020601510711153200213050ustar00rootroot00000000000000"""This is a helper script for running answer tests on CI services. It's currently used on: * Jenkins * GHA for executing answer tests and optionally generating new answers. """ import glob import os import pytest if __name__ == "__main__": os.environ["OMP_NUM_THREADS"] = "1" pytest_args = [ "-s", "-v", "-rsfE", # it means -r "sfE" (show skipped, failed, errors), no -r -s -f -E "--with-answer-testing", "-m answer_test", f"-n {int(os.environ.get('NUM_WORKERS', 1))}", "--dist=loadscope", ] pytest.main(pytest_args + ["--local-dir=answer-store", "--junitxml=answers.xml"]) if files := glob.glob("generate_test*.txt"): tests = set() for fname in files: with open(fname) as fp: tests |= set(fp.read().splitlines()) output_dir = "artifacts" if not os.path.isdir(output_dir): os.mkdir(output_dir) pytest.main( pytest_args + [f"--local-dir={output_dir}", "--answer-store"] + list(tests) ) yt-project-yt-f043ac8/tests/report_failed_answers.py000066400000000000000000000340071510711153200227530ustar00rootroot00000000000000""" Script to generate report of failed answer tests or to generate golden answers on cloud platforms like Travis """ import argparse import base64 import collections import datetime import logging import os import re import shutil import sys import tempfile import xml.etree.ElementTree as ET import nose import numpy import requests from yt.config import ytcfg from yt.utilities.answer_testing.framework import AnswerTesting from yt.utilities.command_line import FileStreamer logging.basicConfig(level=logging.INFO) log = logging.getLogger("yt_report_failed_answers") numpy.set_printoptions(threshold=5, edgeitems=1, precision=4) def generate_failed_answers_html(failed_answers): """Generates html for the failed answer tests This function creates a html and embeds the images (actual, expected, difference) in it for the failed answers. Parameters ---------- failed_answers : dict mapping string to dict the key is a string denoting the test name, the value is a dictionary that stores the actual, expected and difference plot file locations of the test. Returns ------- string a html page """ html_template = """

Failed Answer Tests

This report shows images of answer tests that failed when running the answer tests.

Actual Image: plot generated while running the test
Expected Image: golden answer image
Difference Image: difference in the "actual" and "expected" image


{rows}
""" row_template = """ Actual Expected Difference Test: {3}
""" rows = [] for failed_test_file in failed_answers.values(): for test_name, images in failed_test_file.items(): encoded_images = {} for key in images: with open(images[key], "rb") as img: img_data = base64.b64encode(img.read()).decode() encoded_images[key] = img_data formatted_row = row_template.format( encoded_images["Actual"], encoded_images["Expected"], encoded_images["Difference"], test_name, ) rows.append(formatted_row) html = html_template.format(rows="\n".join(rows)) return html def upload_to_curldrop(data, filename): """Uploads file to yt's curldrop server Uploads bytes `data` by the name `filename` to yt curldrop server. Parameters ---------- data : bytes Content to be uploaded filename : string Name of file at curldrop's upload server Returns ------- requests.models.Response Response returned by curldrop server """ if "TRAVIS" in os.environ: job_num = os.environ["TRAVIS_JOB_NUMBER"] file_id = "Travis_Job_Num_" + job_num.replace(".", "_") else: file_id = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") filename = filename.format(file_id) base_url = ytcfg.get("yt", "curldrop_upload_url") upload_url = base_url + "/" + os.path.basename(filename) response = requests.put(upload_url, data=data) return response def upload_failed_answers(failed_answers): """Uploads the result of failed answer tests Uploads a html page of the failed answer tests. Parameters ---------- failed_answers : dict mapping string to dict the key is a string denoting the test name, the value is a dictionary that stores the actual, expected and difference plot file locations of the test. Returns ------- requests.models.Response Response as returned by the upload service """ html = generate_failed_answers_html(failed_answers) # convert html str to bytes html = html.encode() response = upload_to_curldrop(data=html, filename="failed_answers_{}.html") return response def generate_answers(answer_dir, answers): """Generate golden answers Generates golden answers for the list of answers in ``answers`` and saves them at ``answer_dir``. Parameters ---------- answer_dir : string directory location to save the generated answers answers : list of string Collection of missing answer tests specifying full name of the test. eg. ['yt.visualization.tests.test_line_plots:test_multi_line_plot'] Returns ------- bool True, if all the missing answers are successfully generated False, otherwise """ status = True test_argv = [ os.path.basename(__file__), "--with-answer-testing", "--nologcapture", "-s", "-d", "-v", "--local", f"--local-dir={answer_dir}", "--answer-store", ] for job in answers: log.info("\n Generating answers for %s", job) status &= nose.run( argv=test_argv + [job], addplugins=[AnswerTesting()], exit=False ) return status def upload_answers(answers): """Uploads answers not present in answer-store This function generates the answers for tests that are not present in answer store and uploads a zip file of the same. Parameters ---------- answers : list of string Collection of missing answer tests specifying full name of the test. eg. ['yt.visualization.tests.test_line_plots:test_multi_line_plot'] Returns ------- requests.models.Response Response as returned by the upload service when answers are successfully uploaded None for the case when there was some error while generating the missing golden-answers """ # Create temporary location to save new answers tmpdir = tempfile.mkdtemp() answer_dir = os.path.join(tmpdir, "answer-store") if not os.path.exists(answer_dir): os.mkdir(answer_dir) zip_file = os.path.join(tmpdir, "new-answers") status = generate_answers(answer_dir, answers) if status: zip_file = shutil.make_archive(zip_file, "zip", answer_dir) data = iter(FileStreamer(open(zip_file, "rb"))) response = upload_to_curldrop(data=data, filename="new_answers_{}.zip") shutil.rmtree(tmpdir) return response return None def extract_image_locations(error_string): """Regex based function to extract image file locations. Parameters ---------- error_string : String The input string having file locations of 'Actual', 'Expected' and 'Difference' plots. This string is generated by yt's answer-testing plugin, when the plot generated in the test does not match to its golden answer image. Returns ------- dict If the `error_string` is successfully parsed to extract plot locations, then a dictionary with the keys 'Actual', 'Expected','Difference' and values having corresponding plot file locations is returned. eg. {'Actual': '/usr/tmp/tmp43la9b0w.png', 'Expected': '/usr/tmp/tmpbpaqbgi3.png', 'Difference': '/usr/tmp/tmp43la9b0w-failed-diff.png'} None When `error_string` does not conform to yt's answer-testing error message, which has the information for plot file locations on disk. """ unknown_failure = False base_regex = r"\s*\n\s*(.*?.png)" img_regex = { "Actual": "Actual:" + base_regex, "Expected": "Expected:" + base_regex, "Difference": "Difference:" + base_regex, } img_path = {} for key in img_regex: result = re.search(img_regex[key], error_string, re.MULTILINE) if not result: unknown_failure = True break # store the locations of actual, expected and diff plot files img_path[key] = result.group(1) if not unknown_failure: return img_path return None def parse_nose_xml(nose_xml): """Parse xml file generated by nosetests. Parse nose xml file to find following details: Failed tests: These could be due to difference in golden answer image and corresponding test plot. Missing tests: These errors occur when a corresponding golden answer image is not found. Parameters ---------- nose_xml : string full path of xml file to be parsed Returns ------- tuple : (failed_answers, missing_answers) failed_answers : list of tuples (string, dict) Collection of tuples where the first part is a string denoting the test name, the second part is a dictionary that stores the actual, expected and difference plot file locations of the test. eg. [('yt.visualization.tests.test_line_plots:test_line_plot', {'Actual': '/usr/tmp/tmp43la9b0w.png', 'Expected': '/usr/tmp/tmpbpaqbgi3.png', 'Difference': '/usr/tmp/tmp43la9b0w-failed-diff.png'} )] missing_answers : list of string Collection of missing answer tests specifying full name of the test. eg. ['yt.visualization.tests.test_line_plots:test_multi_line_plot'] """ missing_answers = set() failed_answers = collections.defaultdict(lambda: {}) missing_errors = ["No such file or directory", "There is no old answer available"] tree = ET.parse(nose_xml) testsuite = tree.getroot() for testcase in testsuite: for error in testcase.iter("error"): handle_error( error, testcase, missing_errors, missing_answers, failed_answers ) for error in testcase.iter("failure"): handle_error( error, testcase, missing_errors, missing_answers, failed_answers ) return failed_answers, missing_answers def handle_error(error, testcase, missing_errors, missing_answers, failed_answers): attribs = ["classname", "name"] test_name = ":".join(testcase.attrib[a] for a in attribs) message = error.attrib["message"] if ( missing_errors[0] in error.attrib["message"] or missing_errors[1] in error.attrib["message"] ): missing_answers.add(test_name) elif "Items are not equal" in error.attrib["message"]: img_path = extract_image_locations(error.attrib["message"]) if img_path: failed_answers[test_name][message] = img_path if __name__ == "__main__": """Report failed answer tests of cloud platforms like Travis, Appveyor This script parses the nosetests xml file generated after answer tests are executed. If the test fail due to difference in actual and expected images, this function uploads a html page having all the plots which got failed (if executed with `-f` command line argument). In case, answer store does not has a golden answer and if executed with `-m` argument, it uploads missing answers zip file to yt's curldrop server. """ parser = argparse.ArgumentParser() parser.add_argument( "-f", "--upload-failed-tests", action="store_true", help="Upload a comparison report of failed answer tests" " to yt's curldrop server.", ) parser.add_argument( "-m", "--upload-missing-answers", action="store_true", help="Upload tests' answers that are not found in answer-store.", ) parser.add_argument( "--xunit-file", action="store", dest="nosetest_xml", required=True, help="Name of the nosetests xml file to parse for failed answer tests.", ) args = parser.parse_args() # ANSI color codes COLOR_PURPLE = "\x1b[35;1m" COLOR_CYAN = "\x1b[36;1m" COLOR_RESET = "\x1b[0m" FLAG_EMOJI = " \U0001f6a9 " failed_answers = missing_answers = None if args.upload_failed_tests or args.upload_missing_answers: failed_answers, missing_answers = parse_nose_xml(args.nosetest_xml) if args.upload_failed_tests and failed_answers: response = upload_failed_answers(failed_answers) msg = "" if response.ok: msg += ( "\n" + FLAG_EMOJI + COLOR_PURPLE + "Successfully uploaded failed answer test(s) result." " More details about the test failure can be found at the" " URL: " + response.text.split("\n")[1] + COLOR_RESET + FLAG_EMOJI + "\n" ) response = upload_answers(failed_answers) if response is None: log.error("Failed to upload answers for failed tests !") sys.exit(1) if response.ok: msg += ( FLAG_EMOJI + COLOR_CYAN + "Successfully uploaded answer(s) for failed test at URL: " + response.text.split("\n")[1] + " . Please commit these " "answers in the repository's answer-store." + COLOR_RESET + FLAG_EMOJI ) log.info(msg) if args.upload_missing_answers and missing_answers: response = upload_answers(missing_answers) if response is None: log.error("Failed to upload missing answers !") sys.exit(1) if response.ok: msg = ( FLAG_EMOJI + COLOR_CYAN + "Successfully uploaded missing answer(s) at URL: " + response.text.split("\n")[1] + " . Please commit these " "answers in the repository's answer-store." + COLOR_RESET + FLAG_EMOJI ) log.info(msg) yt-project-yt-f043ac8/tests/tests.yaml000066400000000000000000000252451510711153200200520ustar00rootroot00000000000000answer_tests: local_art_004: # PR 3081, 3101 - yt/frontends/art/tests/test_outputs.py:test_d9p #copied from boxlib frontend local_amrex_012: - yt/frontends/amrex/tests/test_outputs.py:test_radadvect - yt/frontends/amrex/tests/test_outputs.py:test_radtube - yt/frontends/amrex/tests/test_outputs.py:test_star - yt/frontends/amrex/tests/test_outputs.py:test_OrionDataset - yt/frontends/amrex/tests/test_outputs.py:test_CastroDataset - yt/frontends/amrex/tests/test_outputs.py:test_RT_particles - yt/frontends/amrex/tests/test_outputs.py:test_units_override - yt/frontends/amrex/tests/test_outputs.py:test_raw_fields local_amrex_particles_010: - yt/frontends/amrex/tests/test_outputs.py:test_LyA - yt/frontends/amrex/tests/test_outputs.py:test_nyx_particle_io - yt/frontends/amrex/tests/test_outputs.py:test_castro_particle_io - yt/frontends/amrex/tests/test_outputs.py:test_langmuir - yt/frontends/amrex/tests/test_outputs.py:test_plasma - yt/frontends/amrex/tests/test_outputs.py:test_beam - yt/frontends/amrex/tests/test_outputs.py:test_warpx_particle_io - yt/frontends/amrex/tests/test_outputs.py:test_NyxDataset - yt/frontends/amrex/tests/test_outputs.py:test_WarpXDataset local_amrvac_009: # PR 2945 - yt/frontends/amrvac/tests/test_outputs.py:test_domain_size - yt/frontends/amrvac/tests/test_outputs.py:test_bw_polar_2d - yt/frontends/amrvac/tests/test_outputs.py:test_blastwave_cartesian_3D - yt/frontends/amrvac/tests/test_outputs.py:test_blastwave_spherical_2D - yt/frontends/amrvac/tests/test_outputs.py:test_blastwave_cylindrical_3D - yt/frontends/amrvac/tests/test_outputs.py:test_khi_cartesian_2D - yt/frontends/amrvac/tests/test_outputs.py:test_khi_cartesian_3D - yt/frontends/amrvac/tests/test_outputs.py:test_jet_cylindrical_25D - yt/frontends/amrvac/tests/test_outputs.py:test_riemann_cartesian_175D - yt/frontends/amrvac/tests/test_outputs.py:test_rmi_cartesian_dust_2D local_arepo_013: # PR 4939 - yt/frontends/arepo/tests/test_outputs.py:test_arepo_bullet - yt/frontends/arepo/tests/test_outputs.py:test_arepo_tng59 - yt/frontends/arepo/tests/test_outputs.py:test_arepo_cr local_artio_005: - yt/frontends/artio/tests/test_outputs.py:test_sizmbhloz local_athena_011: # PR 3971 - yt/frontends/athena/tests/test_outputs.py:test_cloud - yt/frontends/athena/tests/test_outputs.py:test_blast - yt/frontends/athena/tests/test_outputs.py:test_stripping local_athena_pp_008: - yt/frontends/athena_pp/tests/test_outputs.py:test_disk - yt/frontends/athena_pp/tests/test_outputs.py:test_AM06 local_chimera_002: #PR 3638 - yt/frontends/chimera/tests/test_outputs.py:test_multimesh - yt/frontends/chimera/tests/test_outputs.py:test_2D - yt/frontends/chimera/tests/test_outputs.py:test_3D local_cholla_001: - yt/frontends/cholla/tests/test_outputs.py:test_cholla_data local_chombo_006: - yt/frontends/chombo/tests/test_outputs.py:test_gc - yt/frontends/chombo/tests/test_outputs.py:test_tb - yt/frontends/chombo/tests/test_outputs.py:test_iso - yt/frontends/chombo/tests/test_outputs.py:test_zp - yt/frontends/chombo/tests/test_outputs.py:test_kho local_enzo_011: # PR 4930 - yt/frontends/enzo/tests/test_outputs.py:test_moving7 - yt/frontends/enzo/tests/test_outputs.py:test_galaxy0030 - yt/frontends/enzo/tests/test_outputs.py:test_toro1d - yt/frontends/enzo/tests/test_outputs.py:test_kh2d - yt/frontends/enzo/tests/test_outputs.py:test_ecp - yt/frontends/enzo/tests/test_outputs.py:test_nuclei_density_fields local_enzo_e_004: # PR 3971 - yt/frontends/enzo_e/tests/test_outputs.py:test_hello_world - yt/frontends/enzo_e/tests/test_outputs.py:test_particle_fields local_fits_005: - yt/frontends/fits/tests/test_outputs.py:test_grs - yt/frontends/fits/tests/test_outputs.py:test_velocity_field - yt/frontends/fits/tests/test_outputs.py:test_acis - yt/frontends/fits/tests/test_outputs.py:test_A2052 local_flash_014: - yt/frontends/flash/tests/test_outputs.py:test_sloshing - yt/frontends/flash/tests/test_outputs.py:test_wind_tunnel - yt/frontends/flash/tests/test_outputs.py:test_fid_1to3_b1 local_gadget_010: # PR 4939 - yt/frontends/gadget/tests/test_outputs.py:test_iso_collapse - yt/frontends/gadget/tests/test_outputs.py:test_pid_uniqueness - yt/frontends/gadget/tests/test_outputs.py:test_bigendian_field_access - yt/frontends/gadget/tests/test_outputs.py:test_magneticum local_gamer_012: # PR 4782 - yt/frontends/gamer/tests/test_outputs.py:test_jet - yt/frontends/gamer/tests/test_outputs.py:test_psiDM - yt/frontends/gamer/tests/test_outputs.py:test_plummer - yt/frontends/gamer/tests/test_outputs.py:test_mhdvortex - yt/frontends/gamer/tests/test_outputs.py:test_jiw local_gdf_002: - yt/frontends/gdf/tests/test_outputs_nose.py:test_sedov_tunnel local_gizmo_009: # PR 4939 - yt/frontends/gizmo/tests/test_outputs.py:test_gizmo_64 local_halos_012: # PR 3325 - yt/frontends/ahf/tests/test_outputs.py:test_fields_ahf_halos - yt/frontends/owls_subfind/tests/test_outputs.py:test_fields_g1 - yt/frontends/owls_subfind/tests/test_outputs.py:test_fields_g8 - yt/frontends/gadget_fof/tests/test_outputs.py:test_fields_g5 - yt/frontends/gadget_fof/tests/test_outputs.py:test_fields_g42 local_owls_009: # PR 4939 - yt/frontends/owls/tests/test_outputs.py:test_snapshot_033 - yt/frontends/owls/tests/test_outputs.py:test_OWLS_particlefilter local_pw_049: # PR 4440 - yt/visualization/tests/test_plotwindow.py:test_attributes - yt/visualization/tests/test_particle_plot.py:test_particle_projection_answers - yt/visualization/tests/test_particle_plot.py:test_particle_offaxis_projection_answers - yt/visualization/tests/test_particle_plot.py:test_particle_projection_filter - yt/visualization/tests/test_particle_plot.py:test_particle_phase_answers - yt/visualization/tests/test_callbacks.py:test_axis_manipulations local_tipsy_010: # PR 4939 - yt/frontends/tipsy/tests/test_outputs.py:test_pkdgrav - yt/frontends/tipsy/tests/test_outputs.py:test_gasoline_dmonly - yt/frontends/tipsy/tests/test_outputs.py:test_tipsy_galaxy local_varia_017: - yt/frontends/moab/tests/test_c5.py:test_cantor_5 - yt/fields/tests/test_xray_fields.py:test_sloshing_apec - yt/fields/tests/test_xray_fields.py:test_d9p_cloudy - yt/fields/tests/test_xray_fields.py:test_d9p_cloudy_local local_boxlib_012: - yt/frontends/boxlib/tests/test_outputs.py:test_radadvect local_boxlib_particles_010: - yt/frontends/boxlib/tests/test_outputs.py:test_WarpXDataset local_ramses_005: # PR 3856 - yt/frontends/ramses/tests/test_outputs.py:test_output_00080 local_ytdata_008: - yt/frontends/ytdata/tests/test_outputs.py:test_datacontainer_data - yt/frontends/ytdata/tests/test_outputs.py:test_grid_datacontainer_data - yt/frontends/ytdata/tests/test_outputs.py:test_spatial_data - yt/frontends/ytdata/tests/test_outputs.py:test_profile_data - yt/frontends/ytdata/tests/test_outputs.py:test_nonspatial_data - yt/frontends/ytdata/tests/test_old_outputs.py:test_old_datacontainer_data - yt/frontends/ytdata/tests/test_old_outputs.py:test_old_grid_datacontainer_data - yt/frontends/ytdata/tests/test_old_outputs.py:test_old_spatial_data - yt/frontends/ytdata/tests/test_old_outputs.py:test_old_profile_data - yt/frontends/ytdata/tests/test_old_outputs.py:test_old_nonspatial_data local_axialpix_009: # PR 3818 - yt/geometry/coordinates/tests/test_axial_pixelization.py:test_axial_pixelization #local_particle_trajectory_001: # - yt/data_objects/tests/test_particle_trajectories.py local_nc4_cm1_002: # PR 2176, 2998 - yt/frontends/nc4_cm1/tests/test_outputs.py:test_cm1_mesh_fields local_parthenon_001: # PR 4323 - yt/frontends/parthenon/tests/test_outputs.py:test_loading_data - yt/frontends/parthenon/tests/test_outputs.py:test_cluster - yt/frontends/parthenon/tests/test_outputs.py:test_derived_fields other_tests: unittests: # keep in sync with nose_ignores.txt - "--exclude=test_mesh_slices" # disable randomly failing test - "--ignore=test_outputs_pytest" - "--ignore-file=test_add_field\\.py" - "--ignore-file=test_ambiguous_fields\\.py" - "--ignore-file=test_callable_grids\\.py" - "--ignore-file=test_callbacks_geographic\\.py" - "--ignore-file=test_commons\\.py" - "--ignore-file=test_cython_fortran_utils\\.py" - "--ignore-file=test_data_reload\\.py" - "--ignore-file=test_eps_writer\\.py" - "--ignore-file=test_ewah_write_load\\.py" - "--ignore-file=test_external_frontends\\.py" - "--ignore-file=test_field_access_pytest\\.py" - "--ignore-file=test_field_parsing\\.py" - "--ignore-file=test_file_sanitizer\\.py" - "--ignore-file=test_firefly\\.py" - "--ignore-file=test_geometries\\.py" - "--ignore-file=test_geographic_coordinates\\.py" - "--ignore-file=test_glue\\.py" - "--ignore-file=test_image_comp_2D_plots\\.py" - "--ignore-file=test_image_comp_geo\\.py" - "--ignore-file=test_invalid_origin\\.py" - "--ignore-file=test_line_annotation_unit\\.py" - "--ignore-file=test_line_plots\\.py" - "--ignore-file=test_load_archive\\.py" - "--ignore-file=test_load_errors\\.py" - "--ignore-file=test_load_sample\\.py" - "--ignore-file=test_mesh_render\\.py" - "--ignore-file=test_mesh_slices\\.py" - "--ignore-file=test_normal_plot_api\\.py" - "--ignore-file=test_on_demand_imports\\.py" - "--ignore-file=test_outputs_pytest\\.py" - "--ignore-file=test_profile_plots\\.py" - "--ignore-file=test_raw_field_slices\\.py" - "--ignore-file=test_registration\\.py" - "--ignore-file=test_sanitize_center\\.py" - "--ignore-file=test_save\\.py" - "--ignore-file=test_set_zlim\\.py" - "--ignore-file=test_stream_particles\\.py" - "--ignore-file=test_stream_stretched\\.py" - "--ignore-file=test_version\\.py" - "--ignore-file=test_gadget_pytest\\.py" - "--ignore-file=test_vr_orientation\\.py" - "--ignore-file=test_particle_trajectories_pytest\\.py" - "--ignore-file=test_time_series\\.py" - "--exclude-test=yt.frontends.gdf.tests.test_outputs.TestGDF" - "--exclude-test=yt.frontends.adaptahop.tests.test_outputs" - "--exclude-test=yt.frontends.stream.tests.test_stream_particles.test_stream_non_cartesian_particles" - "--ignore-file=test_offaxisprojection_pytestonly\\.py" - "--ignore-file=test_sph_pixelization_pytestonly\\.py" - "--ignore-file=test_cf_radial_pytest\\.py" cookbook: - 'doc/source/cookbook/tests/test_cookbook.py' yt-project-yt-f043ac8/tests/unpin_requirements.py000066400000000000000000000016241510711153200223250ustar00rootroot00000000000000# /// script # requires-python = ">=3.10" # dependencies = [ # "tomli ; python_full_version < '3.11'", # "tomli-w", # ] # /// import re import sys import tomli_w if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib PINNED_VERSION_REGEXP = re.compile(r",?(<|<=|==)([0-9a-z]+\.?)+") def unpin_requirements(requirements: list[str]) -> list[str]: return [re.sub(PINNED_VERSION_REGEXP, "", _) for _ in requirements] if __name__ == "__main__": with open("pyproject.toml", "rb") as fr: config = tomllib.load(fr) config["project"]["dependencies"] = unpin_requirements( config["project"]["dependencies"] ) for key, reqs in config["project"]["optional-dependencies"].items(): config["project"]["optional-dependencies"][key] = unpin_requirements(reqs) with open("pyproject_out.toml", "wb") as fw: tomli_w.dump(config, fw) yt-project-yt-f043ac8/yt/000077500000000000000000000000001510711153200153065ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/__init__.py000066400000000000000000000072141510711153200174230ustar00rootroot00000000000000""" yt is a toolkit for analyzing and visualizing volumetric data. * Website: https://yt-project.org * Documentation: https://yt-project.org/doc * Data hub: https://girder.hub.yt * Contribute: https://github.com/yt-project/yt """ from ._version import __version__, version_info # isort: skip import yt.units as units import yt.utilities.physical_constants as physical_constants from yt.data_objects.api import ( DatasetSeries, ImageArray, ParticleProfile, Profile1D, Profile2D, Profile3D, add_particle_filter, create_profile, particle_filter, ) from yt.fields.api import ( DerivedField, FieldDetector, FieldInfoContainer, ValidateDataField, ValidateGridType, ValidateParameter, ValidateProperty, ValidateSpatial, add_field, add_xray_emissivity_field, derived_field, field_plugins, ) from yt.funcs import ( enable_plugins, get_memory_usage, get_pbar, get_version_stack, get_yt_version, insert_ipython, is_root, is_sequence, memory_checker, only_on_root, parallel_profile, print_tb, rootonly, toggle_interactivity, ) from yt.units import ( YTArray, YTQuantity, display_ytarray, loadtxt, savetxt, uconcatenate, ucross, udot, uhstack, uintersect1d, unorm, ustack, uunion1d, uvstack, ) from yt.units.unit_object import define_unit # type: ignore from yt.utilities.logger import set_log_level, ytLogger as mylog from yt import frontends import yt.visualization.volume_rendering.api as volume_rendering from yt.frontends.stream.api import hexahedral_connectivity from yt.frontends.ytdata.api import save_as_dataset from yt.loaders import ( load, load_amr_grids, load_archive, load_hdf5_file, load_hexahedral_mesh, load_octree, load_particles, load_sample, load_simulation, load_uniform_grid, load_unstructured_mesh, ) def run_nose(*args, **kwargs): # we hide this function behind a closure so we # don't make pytest a hard dependency for end users # see https://github.com/yt-project/yt/issues/3771 from yt.testing import run_nose return run_nose(*args, **kwargs) from yt.config import _setup_postinit_configuration from yt.units.unit_systems import UnitSystem, unit_system_registry # type: ignore # Import some helpful math utilities from yt.utilities.math_utils import ortho_find, periodic_position, quartiles from yt.utilities.parallel_tools.parallel_analysis_interface import ( communication_system, enable_parallelism, parallel_objects, ) # Now individual component imports from the visualization API from yt.visualization.api import ( AxisAlignedProjectionPlot, AxisAlignedSlicePlot, FITSImageData, FITSOffAxisProjection, FITSOffAxisSlice, FITSParticleOffAxisProjection, FITSParticleProjection, FITSProjection, FITSSlice, FixedResolutionBuffer, LineBuffer, LinePlot, OffAxisProjectionPlot, OffAxisSlicePlot, ParticleImageBuffer, ParticlePhasePlot, ParticlePlot, ParticleProjectionPlot, PhasePlot, ProfilePlot, ProjectionPlot, SlicePlot, add_colormap, apply_colormap, make_colormap, plot_2d, scale_image, show_colormaps, write_bitmap, write_image, write_projection, ) from yt.visualization.volume_rendering.api import ( ColorTransferFunction, TransferFunction, create_scene, off_axis_projection, volume_render, ) # TransferFunctionHelper, MultiVariateTransferFunction # off_axis_projection # run configuration callbacks _setup_postinit_configuration() del _setup_postinit_configuration yt-project-yt-f043ac8/yt/_maintenance/000077500000000000000000000000001510711153200177275ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/_maintenance/__init__.py000066400000000000000000000000001510711153200220260ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/_maintenance/backports.py000066400000000000000000000044201510711153200222710ustar00rootroot00000000000000# A namespace to store simple backports from most recent Python versions # Backports should be contained in (if/else) blocks, checking on the runtime # Python version, e.g. # # if sys.version_info < (3, 8): # ... # insert backported definitions # else: # pass # # while the else part if meant as a no-op, it is required to allow automated # cleanups with pyupgrade, when our minimal supported Python version reaches # the requirement for backports to be unneeded. # # Likewise, imports from this module should be guarded by a similar condition, # e.g., # # if sys.version_info >= (3, 8): # from functools import cached_property # else: # from yt._maintenance.backports import cached_property import sys if sys.version_info >= (3, 11): pass else: from enum import Enum # backported from Python 3.11.0 class ReprEnum(Enum): """ Only changes the repr(), leaving str() and format() to the mixed-in type. """ # backported from Python 3.11.0 class StrEnum(str, ReprEnum): """ Enum where members are also (and must be) strings """ def __new__(cls, *values): "values must already be of type `str`" if len(values) > 3: raise TypeError(f"too many arguments for str(): {values!r}") if len(values) == 1: # it must be a string if not isinstance(values[0], str): raise TypeError(f"{values[0]!r} is not a string") if len(values) >= 2: # check that encoding argument is a string if not isinstance(values[1], str): raise TypeError(f"encoding must be a string, not {values[1]!r}") if len(values) == 3: # check that errors argument is a string if not isinstance(values[2], str): raise TypeError("errors must be a string, not %r" % (values[2])) # noqa: UP031 value = str(*values) member = str.__new__(cls, value) member._value_ = value return member def _generate_next_value_(name, start, count, last_values): # noqa B902 """ Return the lower-cased version of the member name. """ return name.lower() yt-project-yt-f043ac8/yt/_maintenance/deprecation.py000066400000000000000000000043551510711153200226050ustar00rootroot00000000000000import warnings from functools import wraps from types import FunctionType def issue_deprecation_warning( msg: str, *, stacklevel: int, since: str, removal: str | None = None, ): """ Parameters ---------- msg : str A text message explaining that the code surrounding the call to this function is deprecated, and what should be changed on the user side to avoid it. stacklevel: int Number of stack frames to be skipped when pointing at caller code, starting from *this* function's frame. In general 3 is a minimum. since and removal: str version numbers, indicating the anticipated removal date Notes ----- removal can be left empty if it is not clear how many minor releases are expected to happen before the next major. removal and since arguments are keyword-only to forbid accidentally swapping them. Examples -------- >>> issue_deprecation_warning( ... "This code is deprecated.", stacklevel=3, since="4.0" ... ) """ msg += f"\nDeprecated since yt {since}" if removal is not None: msg += f"\nThis feature is planned for removal in yt {removal}" warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) def future_positional_only(positions2names: dict[int, str], /, **depr_kwargs): """Warn users when using a future positional-only argument as keyword. Note that positional-only arguments are available from Python 3.8 See https://www.python.org/dev/peps/pep-0570/ """ def outer(func: FunctionType): @wraps(func) def inner(*args, **kwargs): for no, name in sorted(positions2names.items()): if name not in kwargs: continue value = kwargs[name] issue_deprecation_warning( f"Using the {name!r} argument as keyword (on position {no}) " "is deprecated. " "Pass the argument as positional to suppress this warning, " f"i.e., use {func.__name__}({value!r}, ...)", stacklevel=3, **depr_kwargs, ) return func(*args, **kwargs) return inner return outer yt-project-yt-f043ac8/yt/_maintenance/ipython_compat.py000066400000000000000000000012031510711153200233320ustar00rootroot00000000000000from importlib.metadata import version from importlib.util import find_spec from packaging.version import Version __all__ = [ "IS_IPYTHON", "IPYWIDGETS_ENABLED", ] IS_IPYTHON: bool HAS_IPYWIDGETS_GE_8: bool IPYWIDGETS_ENABLED: bool try: # this name is only defined if running within ipython/jupyter __IPYTHON__ # type: ignore [name-defined] # noqa: B018 except NameError: IS_IPYTHON = False else: IS_IPYTHON = True HAS_IPYWIDGETS_GE_8 = ( Version(version("ipywidgets")) >= Version("8.0.0") if find_spec("ipywidgets") is not None else False ) IPYWIDGETS_ENABLED = IS_IPYTHON and HAS_IPYWIDGETS_GE_8 yt-project-yt-f043ac8/yt/_maintenance/numpy2_compat.py000066400000000000000000000004111510711153200230720ustar00rootroot00000000000000# avoid deprecation warnings in numpy >= 2.0 import numpy as np if hasattr(np, "trapezoid"): # np.trapz is deprecated in numpy 2.0 in favor of np.trapezoid trapezoid = np.trapezoid else: trapezoid = np.trapz # type: ignore[assignment] # noqa: NPY201 yt-project-yt-f043ac8/yt/_typing.py000066400000000000000000000020671510711153200173360ustar00rootroot00000000000000from typing import Any, Optional, TypeAlias import numpy as np import unyt as un FieldDescT = tuple[str, tuple[str, list[str], str | None]] KnownFieldsT = tuple[FieldDescT, ...] ParticleType = str FieldType = str FieldName = str FieldKey = tuple[FieldType, FieldName] ImplicitFieldKey = FieldName AnyFieldKey = FieldKey | ImplicitFieldKey DomainDimensions = tuple[int, ...] | list[int] | np.ndarray ParticleCoordinateTuple = tuple[ str, # particle type tuple[np.ndarray, np.ndarray, np.ndarray], # xyz float | np.ndarray, # hsml ] # Geometry specific types AxisName = str AxisOrder = tuple[AxisName, AxisName, AxisName] # types that can be converted to un.Unit Unit: TypeAlias = un.Unit | str # types that can be converted to un.unyt_quantity Quantity = un.unyt_quantity | tuple[float, Unit] # np.ndarray[...] syntax is runtime-valid from numpy 1.22, we quote it until our minimal # runtime requirement is bumped to, or beyond this version MaskT = Optional["np.ndarray[Any, np.dtype[np.bool_]]"] AlphaT = Optional["np.ndarray[Any, np.dtype[np.float64]]"] yt-project-yt-f043ac8/yt/_version.py000066400000000000000000000033221510711153200175040ustar00rootroot00000000000000from typing import NamedTuple from packaging.version import Version __all__ = [ "__version__", "version_info", ] __version__ = "4.4.2" # keep in sync with pyproject.toml class VersionTuple(NamedTuple): """ A minimal representation of the current version number that can be used downstream to check the runtime version simply by comparing with builtin tuples, as can be done with the runtime Python version using sys.version_info https://docs.python.org/3/library/sys.html#sys.version_info """ major: int minor: int micro: int releaselevel: str serial: int def _parse_to_version_info(version_str: str) -> VersionTuple: # adapted from matplotlib 3.5 """ Parse a version string to a namedtuple analogous to sys.version_info. See: https://packaging.pypa.io/en/latest/version.html#packaging.version.parse https://docs.python.org/3/library/sys.html#sys.version_info """ v = Version(version_str) if v.pre is None and v.post is None and v.dev is None: return VersionTuple(v.major, v.minor, v.micro, "final", 0) elif v.dev is not None: return VersionTuple(v.major, v.minor, v.micro, "alpha", v.dev) elif v.pre is not None: releaselevel = {"a": "alpha", "b": "beta", "rc": "candidate"}.get( v.pre[0], "alpha" ) return VersionTuple(v.major, v.minor, v.micro, releaselevel, v.pre[1]) elif v.post is not None: # fallback for v.post: guess-next-dev scheme from setuptools_scm return VersionTuple(v.major, v.minor, v.micro + 1, "alpha", v.post) else: return VersionTuple(v.major, v.minor, v.micro + 1, "alpha", 0) version_info = _parse_to_version_info(__version__) yt-project-yt-f043ac8/yt/analysis_modules/000077500000000000000000000000001510711153200206615ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/__init__.py000066400000000000000000000000001510711153200227600ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/absorption_spectrum/000077500000000000000000000000001510711153200247635ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/absorption_spectrum/__init__.py000066400000000000000000000000001510711153200270620ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/absorption_spectrum/api.py000066400000000000000000000003011510711153200261000ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "AbsorptionSpectrum", "https://github.com/trident-project/trident", "https://trident.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/cosmological_observation/000077500000000000000000000000001510711153200257475ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/cosmological_observation/__init__.py000066400000000000000000000000001510711153200300460ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/cosmological_observation/api.py000066400000000000000000000003331510711153200270710ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "CosmologySplice and LightCone", "https://github.com/yt-project/yt_astro_analysis", "https://yt-astro-analysis.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/halo_analysis/000077500000000000000000000000001510711153200235075ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/halo_analysis/__init__.py000066400000000000000000000000001510711153200256060ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/halo_analysis/api.py000066400000000000000000000003131510711153200246270ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "halo_analysis", "https://github.com/yt-project/yt_astro_analysis", "https://yt-astro-analysis.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/halo_finding/000077500000000000000000000000001510711153200233025ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/halo_finding/__init__.py000066400000000000000000000000001510711153200254010ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/halo_finding/api.py000066400000000000000000000003121510711153200244210ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "halo_finding", "https://github.com/yt-project/yt_astro_analysis", "https://yt-astro-analysis.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/halo_mass_function/000077500000000000000000000000001510711153200245345ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/halo_mass_function/__init__.py000066400000000000000000000000001510711153200266330ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/halo_mass_function/api.py000066400000000000000000000002761510711153200256640ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "halo_mass_function", "https://github.com/yt-project/yt_attic", "https://yt-attic.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/level_sets/000077500000000000000000000000001510711153200230265ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/level_sets/__init__.py000066400000000000000000000000001510711153200251250ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/level_sets/api.py000066400000000000000000000003601510711153200241500ustar00rootroot00000000000000raise RuntimeError( "The level_sets module has been moved to yt.data_objects.level_sets." "Please, change the import in your scripts from " "'from yt.analysis_modules.level_sets' to " "'from yt.data_objects.level_sets.'." ) yt-project-yt-f043ac8/yt/analysis_modules/particle_trajectories/000077500000000000000000000000001510711153200252425ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/particle_trajectories/__init__.py000066400000000000000000000000001510711153200273410ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/particle_trajectories/api.py000066400000000000000000000003051510711153200263630ustar00rootroot00000000000000raise RuntimeError( "Particle trajectories are now available from DatasetSeries " "objects as ts.particle_trajectories. The ParticleTrajectories " "analysis module has been removed." ) yt-project-yt-f043ac8/yt/analysis_modules/photon_simulator/000077500000000000000000000000001510711153200242675ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/photon_simulator/__init__.py000066400000000000000000000000001510711153200263660ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/photon_simulator/api.py000066400000000000000000000002411510711153200254070ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "photon_simulator", "pyXSIM", "http://hea-www.cfa.harvard.edu/~jzuhone/pyxsim" ) yt-project-yt-f043ac8/yt/analysis_modules/ppv_cube/000077500000000000000000000000001510711153200224645ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/ppv_cube/__init__.py000066400000000000000000000000001510711153200245630ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/ppv_cube/api.py000066400000000000000000000003051510711153200236050ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "PPVCube", "https://github.com/yt-project/yt_astro_analysis", "https://yt-astro-analysis.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/radmc3d_export/000077500000000000000000000000001510711153200235775ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/radmc3d_export/__init__.py000066400000000000000000000000001510711153200256760ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/radmc3d_export/api.py000066400000000000000000000003141510711153200247200ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "radmc3d_export", "https://github.com/yt-project/yt_astro_analysis", "https://yt-astro-analysis.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/spectral_integrator/000077500000000000000000000000001510711153200247345ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/spectral_integrator/__init__.py000066400000000000000000000000001510711153200270330ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/spectral_integrator/api.py000066400000000000000000000002511510711153200260550ustar00rootroot00000000000000raise RuntimeError( "The spectral_integrator module as been moved to yt.fields. " "'add_xray_emissivity_field' can now be imported " "from the yt module." ) yt-project-yt-f043ac8/yt/analysis_modules/star_analysis/000077500000000000000000000000001510711153200235355ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/star_analysis/__init__.py000066400000000000000000000000001510711153200256340ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/star_analysis/api.py000066400000000000000000000002711510711153200246600ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "star_analysis", "https://github.com/yt-project/yt_attic", "https://yt-attic.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/sunrise_export/000077500000000000000000000000001510711153200237525ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/sunrise_export/__init__.py000066400000000000000000000000001510711153200260510ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/sunrise_export/api.py000066400000000000000000000002721510711153200250760ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "sunrise_export", "https://github.com/yt-project/yt_attic", "https://yt-attic.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/sunyaev_zeldovich/000077500000000000000000000000001510711153200244225ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/sunyaev_zeldovich/__init__.py000066400000000000000000000000001510711153200265210ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/sunyaev_zeldovich/api.py000066400000000000000000000003171510711153200255460ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "sunyaev_zeldovich", "https://github.com/yt-project/yt_astro_analysis", "https://yt-astro-analysis.readthedocs.io/", ) yt-project-yt-f043ac8/yt/analysis_modules/two_point_functions/000077500000000000000000000000001510711153200247735ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/two_point_functions/__init__.py000066400000000000000000000000001510711153200270720ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/analysis_modules/two_point_functions/api.py000066400000000000000000000002771510711153200261240ustar00rootroot00000000000000from yt.utilities.exceptions import YTModuleRemoved raise YTModuleRemoved( "two_point_functions", "https://github.com/yt-project/yt_attic", "https://yt-attic.readthedocs.io/", ) yt-project-yt-f043ac8/yt/api.py000066400000000000000000000000261510711153200164270ustar00rootroot00000000000000""" API for yt """ yt-project-yt-f043ac8/yt/arraytypes.py000066400000000000000000000006361510711153200200700ustar00rootroot00000000000000import numpy as np # Now define convenience functions def blankRecordArray(desc, elements): """ Accept a descriptor describing a recordarray, and return one that's full of zeros This seems like it should be in the numpy distribution... """ blanks = [] for atype in desc["formats"]: blanks.append(np.zeros(elements, dtype=atype)) return np.rec.fromarrays(blanks, **desc) yt-project-yt-f043ac8/yt/config.py000066400000000000000000000047641510711153200171400ustar00rootroot00000000000000import os from yt.utilities.configure import YTConfig, config_dir, configuration_callbacks ytcfg_defaults = {} ytcfg_defaults["yt"] = { "serialize": False, "only_deserialize": False, "time_functions": False, "colored_logs": False, "suppress_stream_logging": False, "stdout_stream_logging": False, "log_level": 20, "inline": False, "num_threads": -1, "store_parameter_files": False, "parameter_file_store": "parameter_files.csv", "maximum_stored_datasets": 500, "skip_dataset_cache": True, "load_field_plugins": False, "plugin_filename": "my_plugins.py", "parallel_traceback": False, "pasteboard_repo": "", "reconstruct_index": True, "test_storage_dir": "/does/not/exist", "test_data_dir": "/does/not/exist", "enzo_db": "", "answer_testing_tolerance": 3, "answer_testing_bitwise": False, "gold_standard_filename": "gold311", "local_standard_filename": "local001", "answer_tests_url": "http://answers.yt-project.org/{1}_{2}", "sketchfab_api_key": "None", "imagebin_api_key": "e1977d9195fe39e", "imagebin_upload_url": "https://api.imgur.com/3/image", "imagebin_delete_url": "https://api.imgur.com/3/image/{delete_hash}", "curldrop_upload_url": "http://use.yt/upload", "thread_field_detection": False, "ignore_invalid_unit_operation_errors": False, "chunk_size": 1000, "xray_data_dir": "/does/not/exist", "supp_data_dir": "/does/not/exist", "default_colormap": "cmyt.arbre", "ray_tracing_engine": "yt", "internals": { "within_testing": False, "parallel": False, "strict_requires": False, "global_parallel_rank": 0, "global_parallel_size": 1, "topcomm_parallel_rank": 0, "topcomm_parallel_size": 1, "command_line": False, }, } # For backward compatibility, do not use these vars internally in yt CONFIG_DIR = config_dir() _global_config_file = YTConfig.get_global_config_file() _local_config_file = YTConfig.get_local_config_file() # Load the config ytcfg = YTConfig() ytcfg.update(ytcfg_defaults, metadata={"source": "defaults"}) # Try loading the local config first, otherwise fall back to global config if os.path.exists(_local_config_file): ytcfg.read(_local_config_file) elif os.path.exists(_global_config_file): ytcfg.read(_global_config_file) def _setup_postinit_configuration(): """This is meant to be run last in yt.__init__""" for callback in configuration_callbacks: callback(ytcfg) yt-project-yt-f043ac8/yt/data_objects/000077500000000000000000000000001510711153200177305ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/__init__.py000066400000000000000000000000001510711153200220270ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/analyzer_objects.py000066400000000000000000000056641510711153200236530ustar00rootroot00000000000000import inspect from yt.utilities.object_registries import analysis_task_registry class AnalysisTask: def __init_subclass__(cls, *args, **kwargs): if hasattr(cls, "skip") and not cls.skip: return analysis_task_registry[cls.__name__] = cls def __init__(self, *args, **kwargs): # This should only get called if the subclassed object # does not override if len(args) + len(kwargs) != len(self._params): raise RuntimeError self.__dict__.update(zip(self._params, args, strict=False)) self.__dict__.update(kwargs) def __repr__(self): # Stolen from YTDataContainer.__repr__ s = f"{self.__class__.__name__}: " s += ", ".join(f"{i}={getattr(self, i)}" for i in self._params) return s def analysis_task(params=None): if params is None: params = () def create_new_class(func): cls = type(func.__name__, (AnalysisTask,), {"eval": func, "_params": params}) return cls return create_new_class @analysis_task(("field",)) def MaximumValue(params, data_object): v = data_object.quantities["MaxLocation"](params.field)[0] return v @analysis_task() def CurrentTimeYears(params, ds): return ds.current_time * ds["years"] class SlicePlotDataset(AnalysisTask): _params = ["field", "axis", "center"] def __init__(self, *args, **kwargs): from yt.visualization.api import SlicePlot self.SlicePlot = SlicePlot AnalysisTask.__init__(self, *args, **kwargs) def eval(self, ds): slc = self.SlicePlot(ds, self.axis, self.field, center=self.center) return slc.save() class QuantityProxy(AnalysisTask): _params = None quantity_name = None def __repr__(self): # Stolen from YTDataContainer.__repr__ s = f"{self.__class__.__name__}: " s += ", ".join([str(list(self.args))]) s += ", ".join(f"{k}={v}" for k, v in self.kwargs.items()) return s def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def eval(self, data_object): rv = data_object.quantities[self.quantity_name](*self.args, **self.kwargs) return rv class ParameterValue(AnalysisTask): _params = ["parameter"] def __init__(self, parameter, cast=None): self.parameter = parameter if cast is None: def _identity(x): return x cast = _identity self.cast = cast def eval(self, ds): return self.cast(ds.get_parameter(self.parameter)) def create_quantity_proxy(quantity_object): args, varargs, kwargs, defaults = inspect.getargspec(quantity_object[1]) # Strip off 'data' which is on every quantity function params = args[1:] if kwargs is not None: params += kwargs dd = {"_params": params, "quantity_name": quantity_object[0]} cls = type(quantity_object[0], (QuantityProxy,), dd) return cls yt-project-yt-f043ac8/yt/data_objects/api.py000066400000000000000000000007401510711153200210540ustar00rootroot00000000000000from . import construction_data_containers as __cdc, selection_objects as __sdc from .analyzer_objects import AnalysisTask, analysis_task from .image_array import ImageArray from .index_subobjects import AMRGridPatch, OctreeSubset from .particle_filters import add_particle_filter, particle_filter from .profiles import ParticleProfile, Profile1D, Profile2D, Profile3D, create_profile from .static_output import Dataset from .time_series import DatasetSeries, DatasetSeriesObject yt-project-yt-f043ac8/yt/data_objects/construction_data_containers.py000066400000000000000000003367631510711153200262740ustar00rootroot00000000000000import fileinput import io import os import warnings import zipfile from functools import partial, wraps from re import finditer from tempfile import NamedTemporaryFile, TemporaryFile import numpy as np from more_itertools import always_iterable from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import FieldKey from yt.config import ytcfg from yt.data_objects.field_data import YTFieldData from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer1D, YTSelectionContainer2D, YTSelectionContainer3D, ) from yt.fields.field_exceptions import NeedsGridType, NeedsOriginalGrid from yt.frontends.sph.data_structures import ParticleDataset from yt.funcs import ( get_memory_usage, is_sequence, iter_fields, mylog, only_on_root, validate_moment, ) from yt.geometry import particle_deposit as particle_deposit from yt.geometry.coordinates.cartesian_coordinates import all_data from yt.loaders import load_uniform_grid from yt.units._numpy_wrapper_functions import uconcatenate from yt.units.unit_object import Unit # type: ignore from yt.units.yt_array import YTArray from yt.utilities.exceptions import ( YTNoAPIKey, YTNotInsideNotebook, YTParticleDepositionNotImplemented, YTTooManyVertices, ) from yt.utilities.grid_data_format.writer import write_to_gdf from yt.utilities.lib.cyoctree import CyOctree from yt.utilities.lib.interpolators import ghost_zone_interpolate from yt.utilities.lib.marching_cubes import march_cubes_grid, march_cubes_grid_flux from yt.utilities.lib.misc_utilities import fill_region, fill_region_float from yt.utilities.lib.pixelization_routines import ( interpolate_sph_grid_gather, interpolate_sph_positions_gather, normalization_1d_utility, normalization_3d_utility, pixelize_sph_kernel_arbitrary_grid, ) from yt.utilities.lib.quad_tree import QuadTree from yt.utilities.math_utils import compute_stddev_image from yt.utilities.minimal_representation import MinimalProjectionData from yt.utilities.parallel_tools.parallel_analysis_interface import ( communication_system, parallel_objects, parallel_root_only, ) from yt.visualization.color_maps import get_colormap_lut class YTStreamline(YTSelectionContainer1D): """ This is a streamline, which is a set of points defined as being parallel to some vector field. This object is typically accessed through the Streamlines.path function. The resulting arrays have their dimensionality reduced to one, and an ordered list of points at an (x,y) tuple along `axis` are available, as is the `t` field, which corresponds to a unitless measurement along the ray from start to end. Parameters ---------- positions : array-like List of streamline positions length : float The magnitude of the distance; dts will be divided by this fields : list of strings, optional If you want the object to pre-retrieve a set of fields, supply them here. This is not necessary. ds : dataset object Passed in to access the index kwargs : dict of items Any additional values are passed as field parameters that can be accessed by generated fields. Examples -------- >>> from yt.visualization.api import Streamlines >>> streamlines = Streamlines(ds, [0.5] * 3) >>> streamlines.integrate_through_volume() >>> stream = streamlines.path(0) >>> fig, ax = plt.subplots() >>> ax.set_yscale("log") >>> ax.plot(stream["t"], stream["gas", "density"], "-x") """ _type_name = "streamline" _con_args = ("positions",) sort_by = "t" def __init__(self, positions, length=1.0, fields=None, ds=None, **kwargs): YTSelectionContainer1D.__init__(self, ds, fields, **kwargs) self.positions = positions self.dts = np.empty_like(positions[:, 0]) self.dts[:-1] = np.sqrt( np.sum((self.positions[1:] - self.positions[:-1]) ** 2, axis=1) ) self.dts[-1] = self.dts[-1] self.length = length self.dts /= length self.ts = np.add.accumulate(self.dts) self._set_center(self.positions[0]) self.set_field_parameter("center", self.positions[0]) self._dts, self._ts = {}, {} # self._refresh_data() def _get_list_of_grids(self): # Get the value of the line at each LeftEdge and RightEdge LE = self.ds.grid_left_edge RE = self.ds.grid_right_edge # Check left faces first min_streampoint = np.min(self.positions, axis=0) max_streampoint = np.max(self.positions, axis=0) p = np.all((min_streampoint <= RE) & (max_streampoint > LE), axis=1) self._grids = self.index.grids[p] def _get_data_from_grid(self, grid, field): # No child masking here; it happens inside the mask cut mask = self._get_cut_mask(grid) if field == "dts": return self._dts[grid.id] if field == "t": return self._ts[grid.id] return grid[field].flat[mask] def _get_cut_mask(self, grid): points_in_grid = np.all(self.positions > grid.LeftEdge, axis=1) & np.all( self.positions <= grid.RightEdge, axis=1 ) pids = np.where(points_in_grid)[0] mask = np.zeros(points_in_grid.sum(), dtype="int64") dts = np.zeros(points_in_grid.sum(), dtype="float64") ts = np.zeros(points_in_grid.sum(), dtype="float64") for mi, (i, pos) in enumerate( zip(pids, self.positions[points_in_grid], strict=True) ): if not points_in_grid[i]: continue ci = ((pos - grid.LeftEdge) / grid.dds).astype("int64") if grid.child_mask[ci[0], ci[1], ci[2]] == 0: continue for j in range(3): ci[j] = min(ci[j], grid.ActiveDimensions[j] - 1) mask[mi] = np.ravel_multi_index(ci, grid.ActiveDimensions) dts[mi] = self.dts[i] ts[mi] = self.ts[i] self._dts[grid.id] = dts self._ts[grid.id] = ts return mask class YTProj(YTSelectionContainer2D): _key_fields = YTSelectionContainer2D._key_fields + ["weight_field"] _con_args = ("axis", "field", "weight_field") _container_fields = ("px", "py", "pdx", "pdy", "weight_field") def __init__( self, field, axis, weight_field=None, center=None, ds=None, data_source=None, method="integrate", field_parameters=None, max_level=None, *, moment=1, ): super().__init__(axis, ds, field_parameters) if method == "mip": issue_deprecation_warning( "The 'mip' method value is a deprecated alias for 'max'. " "Please use max directly.", since="4.1", stacklevel=4, ) method = "max" if method == "sum": self.method = "integrate" self._sum_only = True else: self.method = method self._sum_only = False if self.method in ["max", "mip"]: self.func = np.max elif self.method == "min": self.func = np.min elif self.method == "integrate": self.func = np.sum # for the future else: raise NotImplementedError(self.method) validate_moment(moment, weight_field) self.moment = moment self._set_center(center) self._projected_units = {} if data_source is None: data_source = self.ds.all_data() if max_level is not None: data_source.max_level = max_level for k, v in data_source.field_parameters.items(): if k not in self.field_parameters or self._is_default_field_parameter(k): self.set_field_parameter(k, v) self.data_source = data_source if weight_field is None: self.weight_field = weight_field else: self.weight_field = self._determine_fields(weight_field)[0] for f in self._determine_fields(field): nodal_flag = self.ds._get_field_info(f).nodal_flag if any(nodal_flag): raise RuntimeError( "Nodal fields are currently not supported for projections." ) @property def blocks(self): return self.data_source.blocks @property def field(self): return [k for k in self.field_data.keys() if k not in self._container_fields] def get_data(self, fields=None): fields = self._determine_fields(fields) sfields = [] if self.moment == 2: def _sq_field(field, data, fname: FieldKey): return data[fname] ** 2 for field in fields: fd = self.ds._get_field_info(field) ftype, fname = field self.ds.add_field( (ftype, f"tmp_{fname}_squared"), partial(_sq_field, fname=field), sampling_type=fd.sampling_type, units=f"({fd.units})*({fd.units})", ) sfields.append((ftype, f"tmp_{fname}_squared")) nfields = len(fields) nsfields = len(sfields) # We need a new tree for every single set of fields we add if nfields == 0: return if isinstance(self.ds, ParticleDataset): return tree = self._get_tree(nfields + nsfields) # This only needs to be done if we are in parallel; otherwise, we can # safely build the mesh as we go. if communication_system.communicators[-1].size > 1: for chunk in self.data_source.chunks([], "io", local_only=False): self._initialize_chunk(chunk, tree) _units_initialized = False with self.data_source._field_parameter_state(self.field_parameters): for chunk in parallel_objects( self.data_source.chunks([], "io", local_only=True) ): if not _units_initialized: self._initialize_projected_units(fields, chunk) _units_initialized = True self._handle_chunk(chunk, fields + sfields, tree) # if there's less than nprocs chunks, units won't be initialized # on all processors, so sync with _projected_units on rank 0 projected_units = self.comm.mpi_bcast(self._projected_units) self._projected_units = projected_units # Note that this will briefly double RAM usage if self.method in ["max", "mip"]: merge_style = -1 op = "max" elif self.method == "min": merge_style = -2 op = "min" elif self.method == "integrate": merge_style = 1 op = "sum" else: raise NotImplementedError # TODO: Add the combine operation xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] ix, iy, ires, nvals, nwvals = tree.get_all(False, merge_style) px, pdx = self.ds.index._icoords_to_fcoords( ix[:, None], ires // self.ds.ires_factor, axes=(xax,) ) py, pdy = self.ds.index._icoords_to_fcoords( iy[:, None], ires // self.ds.ires_factor, axes=(yax,) ) px = px.ravel() py = py.ravel() pdx = pdx.ravel() pdy = pdy.ravel() np.multiply(pdx, 0.5, pdx) np.multiply(pdy, 0.5, pdy) nvals = self.comm.mpi_allreduce(nvals, op=op) nwvals = self.comm.mpi_allreduce(nwvals, op=op) if self.weight_field is not None: # If there are 0s remaining in the weight vals # this will not throw an error, but silently # return nans for vals where dividing by 0 # Leave as NaNs to be auto-masked by Matplotlib with np.errstate(invalid="ignore"): np.divide(nvals, nwvals[:, None], nvals) # We now convert to half-widths and center-points data = {} code_length = self.ds.domain_width.units data["px"] = self.ds.arr(px, code_length) data["py"] = self.ds.arr(py, code_length) data["weight_field"] = nwvals data["pdx"] = self.ds.arr(pdx, code_length) data["pdy"] = self.ds.arr(pdy, code_length) data["fields"] = nvals # Now we run the finalizer, which is ignored if we don't need it field_data = np.hsplit(data.pop("fields"), nfields + nsfields) for fi, field in enumerate(fields): mylog.debug("Setting field %s", field) input_units = self._projected_units[field] fvals = field_data[fi].ravel() if self.moment == 2: fvals = compute_stddev_image(field_data[fi + nfields].ravel(), fvals) self[field] = self.ds.arr(fvals, input_units) for i in list(data.keys()): self[i] = data.pop(i) mylog.info("Projection completed") if self.moment == 2: for field in sfields: self.ds.field_info.pop(field) self.tree = tree def to_pw(self, fields=None, center="center", width=None, origin="center-window"): r"""Create a :class:`~yt.visualization.plot_window.PWViewerMPL` from this object. This is a bare-bones mechanism of creating a plot window from this object, which can then be moved around, zoomed, and on and on. All behavior of the plot window is relegated to that routine. """ pw = self._get_pw(fields, center, width, origin, "Projection") return pw def plot(self, fields=None): if hasattr(self.data_source, "left_edge") and hasattr( self.data_source, "right_edge" ): left_edge = self.data_source.left_edge right_edge = self.data_source.right_edge center = (left_edge + right_edge) / 2.0 width = right_edge - left_edge xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] lx, rx = left_edge[xax], right_edge[xax] ly, ry = left_edge[yax], right_edge[yax] width = (rx - lx), (ry - ly) else: width = self.ds.domain_width center = self.ds.domain_center pw = self._get_pw(fields, center, width, "native", "Projection") try: pw.show() except YTNotInsideNotebook: pass return pw def _initialize_projected_units(self, fields, chunk): for field in self.data_source._determine_fields(fields): if field in self._projected_units: continue finfo = self.ds._get_field_info(field) if finfo.units is None: # First time calling a units="auto" field, infer units and cache # for future field accesses. finfo.units = str(chunk[field].units) field_unit = Unit(finfo.output_units, registry=self.ds.unit_registry) if self.method in ("min", "max") or self._sum_only: path_length_unit = Unit(registry=self.ds.unit_registry) else: ax_name = self.ds.coordinates.axis_name[self.axis] path_element_name = ("index", f"path_element_{ax_name}") path_length_unit = self.ds.field_info[path_element_name].units path_length_unit = Unit( path_length_unit, registry=self.ds.unit_registry ) # Only convert to appropriate unit system for path # elements that aren't angles if not path_length_unit.is_dimensionless: path_length_unit = path_length_unit.get_base_equivalent( unit_system=self.ds.unit_system ) if self.weight_field is None: self._projected_units[field] = field_unit * path_length_unit else: self._projected_units[field] = field_unit class YTParticleProj(YTProj): """ A projection operation optimized for SPH particle data. """ _type_name = "particle_proj" def __init__( self, field, axis, weight_field=None, center=None, ds=None, data_source=None, method="integrate", field_parameters=None, max_level=None, *, moment=1, ): super().__init__( field, axis, weight_field, center, ds, data_source, method, field_parameters, max_level, moment=moment, ) def _handle_chunk(self, chunk, fields, tree): raise NotImplementedError("Particle projections have not yet been implemented") class YTQuadTreeProj(YTProj): """ This is a data object corresponding to a line integral through the simulation domain. This object is typically accessed through the `proj` object that hangs off of index objects. YTQuadTreeProj is a projection of a `field` along an `axis`. The field can have an associated `weight_field`, in which case the values are multiplied by a weight before being summed, and then divided by the sum of that weight; the two fundamental modes of operating are direct line integral (no weighting) and average along a line of sight (weighting.) What makes `proj` different from the standard projection mechanism is that it utilizes a quadtree data structure, rather than the old mechanism for projections. It will not run in parallel, but serial runs should be substantially faster. Note also that lines of sight are integrated at every projected finest-level cell. Parameters ---------- field : string This is the field which will be "projected" along the axis. If multiple are specified (in a list) they will all be projected in the first pass. axis : int The axis along which to slice. Can be 0, 1, or 2 for x, y, z. weight_field : string If supplied, the field being projected will be multiplied by this weight value before being integrated, and at the conclusion of the projection the resultant values will be divided by the projected `weight_field`. center : array_like, optional The 'center' supplied to fields that use it. Note that this does not have to have `coord` as one value. Strictly optional. data_source : `yt.data_objects.data_containers.YTSelectionContainer`, optional If specified, this will be the data source used for selecting regions to project. method : string, optional The method of projection to be performed. "integrate" : integration along the axis "mip" : maximum intensity projection (deprecated) "max" : maximum intensity projection "min" : minimum intensity projection "sum" : same as "integrate", except that we don't multiply by the path length WARNING: The "sum" option should only be used for uniform resolution grid datasets, as other datasets may result in unphysical images. style : string, optional The same as the method keyword. Deprecated as of version 3.0.2. Please use method keyword instead. field_parameters : dict of items Values to be passed as field parameters that can be accessed by generated fields. moment : integer, optional for a weighted projection, moment = 1 (the default) corresponds to a weighted average. moment = 2 corresponds to a weighted standard deviation. Examples -------- >>> ds = load("RedshiftOutput0005") >>> prj = ds.proj(("gas", "density"), 0) >>> print(proj["gas", "density"]) """ _type_name = "quad_proj" def __init__( self, field, axis, weight_field=None, center=None, ds=None, data_source=None, method="integrate", field_parameters=None, max_level=None, *, moment=1, ): super().__init__( field, axis, weight_field, center, ds, data_source, method, field_parameters, max_level, moment=moment, ) if not self.deserialize(field): self.get_data(field) self.serialize() @property def _mrep(self): return MinimalProjectionData(self) def deserialize(self, fields): if not ytcfg.get("yt", "serialize"): return False for field in fields: self[field] = None deserialized_successfully = False store_file = self.ds.parameter_filename + ".yt" if os.path.isfile(store_file): deserialized_successfully = self._mrep.restore(store_file, self.ds) if deserialized_successfully: mylog.info("Using previous projection data from %s", store_file) for field, field_data in self._mrep.field_data.items(): self[field] = field_data if not deserialized_successfully: for field in fields: del self[field] return deserialized_successfully def serialize(self): if not ytcfg.get("yt", "serialize"): return self._mrep.store(self.ds.parameter_filename + ".yt") def _get_tree(self, nvals): xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] xd = self.ds.domain_dimensions[xax] yd = self.ds.domain_dimensions[yax] bounds = ( self.ds.domain_left_edge[xax], self.ds.domain_right_edge[xax], self.ds.domain_left_edge[yax], self.ds.domain_right_edge[yax], ) return QuadTree( np.array([xd, yd], dtype="int64"), nvals, bounds, method=self.method ) def _initialize_chunk(self, chunk, tree): icoords = chunk.icoords xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] i1 = icoords[:, xax] i2 = icoords[:, yax] ilevel = chunk.ires * self.ds.ires_factor tree.initialize_chunk(i1, i2, ilevel) def _handle_chunk(self, chunk, fields, tree): mylog.debug( "Adding chunk (%s) to tree (%0.3e GB RAM)", chunk.ires.size, get_memory_usage() / 1024.0, ) if self.method in ("min", "max") or self._sum_only: dl = self.ds.quan(1.0, "") else: ax_name = self.ds.coordinates.axis_name[self.axis] dl = chunk["index", f"path_element_{ax_name}"] # This is done for cases where our path element does not have a CGS # equivalent. Once "preferred units" have been implemented, this # will not be necessary at all, as the final conversion will occur # at the display layer. if not dl.units.is_dimensionless: dl.convert_to_units(self.ds.unit_system["length"]) v = np.empty((chunk.ires.size, len(fields)), dtype="float64") for i, field in enumerate(fields): d = chunk[field] * dl v[:, i] = d if self.weight_field is not None: w = chunk[self.weight_field] np.multiply(v, w[:, None], v) np.multiply(w, dl, w) else: w = np.ones(chunk.ires.size, dtype="float64") icoords = chunk.icoords xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] i1 = icoords[:, xax] i2 = icoords[:, yax] ilevel = chunk.ires * self.ds.ires_factor tree.add_chunk_to_tree(i1, i2, ilevel, v, w) class YTCoveringGrid(YTSelectionContainer3D): """A 3D region with all data extracted to a single, specified resolution. Left edge should align with a cell boundary, but defaults to the closest cell boundary. Parameters ---------- level : int The resolution level data to which data will be gridded. Level 0 is the root grid dx for that dataset. (The grid resolution will be simulation size / 2**level along each grid axis.) left_edge : array_like The left edge of the region to be extracted. Specify units by supplying a YTArray, otherwise code length units are assumed. dims : array_like Number of cells along each axis of resulting covering_grid fields : array_like, optional A list of fields that you'd like pre-generated for your object num_ghost_zones : integer, optional The number of padding ghost zones used when accessing fields. data_source : An existing data object to intersect with the covering grid. Grid points outside the data_source will exist as empty values. Examples -------- >>> cube = ds.covering_grid(2, left_edge=[0.0, 0.0, 0.0], dims=[128, 128, 128]) """ _spatial = True _type_name = "covering_grid" _con_args = ("level", "left_edge", "ActiveDimensions") _container_fields = ( ("index", "dx"), ("index", "dy"), ("index", "dz"), ("index", "x"), ("index", "y"), ("index", "z"), ) _base_grid = None def __init__( self, level, left_edge, dims, fields=None, ds=None, num_ghost_zones=0, use_pbar=True, field_parameters=None, *, data_source=None, ): if field_parameters is None: center = None else: center = field_parameters.get("center", None) super().__init__(center, ds, field_parameters, data_source=data_source) self.level = level self.left_edge = self._sanitize_edge(left_edge) self.ActiveDimensions = self._sanitize_dims(dims) rdx = self.ds.domain_dimensions * self.ds.relative_refinement(0, level) # normalize dims as a non-zero dim array dims = np.array(list(always_iterable(dims))) rdx[np.where(dims - 2 * num_ghost_zones <= 1)] = 1 # issue 602 self.base_dds = self.ds.domain_width / self.ds.domain_dimensions self.dds = self.ds.domain_width / rdx.astype("float64") self.right_edge = self.left_edge + self.ActiveDimensions * self.dds self._num_ghost_zones = num_ghost_zones self._use_pbar = use_pbar self.global_startindex = ( np.rint((self.left_edge - self.ds.domain_left_edge) / self.dds).astype( "int64" ) + self.ds.domain_offset ) self._setup_data_source() self.get_data(fields) def get_global_startindex(self): r"""Get the global start index of the covering grid.""" return self.global_startindex def to_xarray(self, fields=None): r"""Export this fixed-resolution object to an xarray Dataset This function will take a regularized grid and optionally a list of fields and return an xarray Dataset object. If xarray is not importable, this will raise ImportError. Parameters ---------- fields : list of strings or tuple field names, default None If this is supplied, it is the list of fields to be exported into the data frame. If not supplied, whatever fields presently exist will be used. Returns ------- arr : Dataset The data contained in the object. Examples -------- >>> dd = ds.r[::256j, ::256j, ::256j] >>> xf1 = dd.to_xarray([("gas", "density"), ("gas", "temperature")]) >>> dd["gas", "velocity_magnitude"] >>> xf2 = dd.to_xarray() """ import xarray as xr data = {} coords = {} for f in fields or self.field_data.keys(): data[f] = { "dims": ( "x", "y", "z", ), "data": self[f], "attrs": {"units": str(self[f].uq)}, } # We have our data, so now we generate both our coordinates and our metadata. LE = self.LeftEdge + self.dds / 2.0 RE = self.RightEdge - self.dds / 2.0 N = self.ActiveDimensions u = str(LE.uq) for i, ax in enumerate("xyz"): coords[ax] = { "dims": (ax,), "data": np.mgrid[LE[i] : RE[i] : N[i] * 1j], "attrs": {"units": u}, } return xr.Dataset.from_dict({"data_vars": data, "coords": coords}) @property def icoords(self): ic = np.indices(self.ActiveDimensions).astype("int64") return np.column_stack( [ i.ravel() + gi for i, gi in zip(ic, self.get_global_startindex(), strict=True) ] ) @property def fwidth(self): fw = np.ones((self.ActiveDimensions.prod(), 3), dtype="float64") fw *= self.dds return fw @property def fcoords(self): LE = self.LeftEdge + self.dds / 2.0 RE = self.RightEdge - self.dds / 2.0 N = self.ActiveDimensions fc = np.mgrid[ LE[0] : RE[0] : N[0] * 1j, LE[1] : RE[1] : N[1] * 1j, LE[2] : RE[2] : N[2] * 1j, ] return np.column_stack([f.ravel() for f in fc]) @property def ires(self): tr = np.ones(self.ActiveDimensions.prod(), dtype="int64") tr *= self.level return tr def set_field_parameter(self, name, val): super().set_field_parameter(name, val) if self._data_source is not None: self._data_source.set_field_parameter(name, val) def _sanitize_dims(self, dims): if not is_sequence(dims): dims = [dims] * len(self.ds.domain_left_edge) if len(dims) != len(self.ds.domain_left_edge): raise RuntimeError( "Length of dims must match the dimensionality of the dataset" ) return np.array(dims, dtype="int32") def _sanitize_edge(self, edge): if not is_sequence(edge): edge = [edge] * len(self.ds.domain_left_edge) if len(edge) != len(self.ds.domain_left_edge): raise RuntimeError( "Length of edges must match the dimensionality of the dataset" ) if hasattr(edge, "units"): if edge.units.registry is self.ds.unit_registry: return edge edge_units = edge.units.copy() edge_units.registry = self.ds.unit_registry else: edge_units = "code_length" return self.ds.arr(edge, edge_units, dtype="float64") def _reshape_vals(self, arr): if len(arr.shape) == 3: return arr return arr.reshape(self.ActiveDimensions, order="C") @property def shape(self): return tuple(self.ActiveDimensions.tolist()) def _setup_data_source(self): reg = self.ds.region(self.center, self.left_edge, self.right_edge) if self._data_source is None: # note: https://github.com/yt-project/yt/pull/4063 implemented # a data_source kwarg for YTCoveringGrid, but not YTArbitraryGrid # so as of 4063, this will always be True for YTArbitraryGrid # instances. self._data_source = reg else: self._data_source = self.ds.intersection([self._data_source, reg]) self._data_source.min_level = 0 self._data_source.max_level = self.level # This triggers "special" behavior in the RegionSelector to ensure we # select *cells* whose bounding boxes overlap with our region, not just # their cell centers. self._data_source.loose_selection = True def get_data(self, fields=None): if fields is None: return fields = self._determine_fields(fields) fields_to_get = [f for f in fields if f not in self.field_data] fields_to_get = self._identify_dependencies(fields_to_get) if len(fields_to_get) == 0: return try: fill, gen, part, alias = self._split_fields(fields_to_get) except NeedsGridType as e: if self._num_ghost_zones == 0: num_ghost_zones = self._num_ghost_zones raise RuntimeError( "Attempting to access a field that needs ghost zones, but " f"{num_ghost_zones = }. You should create the covering grid " "with nonzero num_ghost_zones." ) from e else: raise # checking if we have a sph particles if len(part) == 0: is_sph_field = False else: is_sph_field = self.ds.field_info[part[0]].is_sph_field if len(part) > 0 and len(alias) == 0: if is_sph_field: self._fill_sph_particles(fields) for field in fields: if field in gen: gen.remove(field) else: self._fill_particles(part) if len(fill) > 0: self._fill_fields(fill) for a, f in sorted(alias.items()): if f.sampling_type == "particle" and not is_sph_field: self[a] = self._data_source[f] else: self[a] = f(self) self.field_data[a].convert_to_units(f.output_units) if len(gen) > 0: part_gen = [] cell_gen = [] for field in gen: finfo = self.ds.field_info[field] if finfo.sampling_type == "particle": part_gen.append(field) else: cell_gen.append(field) self._generate_fields(cell_gen) for p in part_gen: self[p] = self._data_source[p] def _split_fields(self, fields_to_get): fill, gen = self.index._split_fields(fields_to_get) particles = [] alias = {} for field in gen: finfo = self.ds._get_field_info(field) if finfo.is_alias: alias[field] = finfo continue try: finfo.check_available(self) except NeedsOriginalGrid: fill.append(field) for field in fill: finfo = self.ds._get_field_info(field) if finfo.sampling_type == "particle": particles.append(field) gen = [f for f in gen if f not in fill and f not in alias] fill = [f for f in fill if f not in particles] return fill, gen, particles, alias def _fill_particles(self, part): for p in part: self[p] = self._data_source[p] def _check_sph_type(self, finfo): """ Check if a particle field has an SPH type. There are several ways that this can happen, checked in this order: 1. If the field type is a known particle filter, and is in the list of SPH ptypes, use this type 2. If the field is an alias of an SPH field, but its type is not "gas", use this type 3. Otherwise, if the field type is not in the SPH types list and it is not "gas", we fail If we get through without erroring out, we either have a known SPH particle filter, an alias of an SPH field, the default SPH ptype, or "gas" for an SPH field. Then we return the particle type. """ ftype, fname = finfo.name sph_ptypes = self.ds._sph_ptypes ptype = sph_ptypes[0] err = KeyError(f"{ftype} is not a SPH particle type!") if ftype in self.ds.known_filters: if ftype not in sph_ptypes: raise err else: ptype = ftype elif finfo.is_alias: if finfo.alias_name[0] not in sph_ptypes: raise err elif ftype != "gas": ptype = ftype elif ftype not in sph_ptypes and ftype != "gas": raise err return ptype def _fill_sph_particles(self, fields): from tqdm import tqdm # checks that we have the field and gets information fields = [f for f in fields if f not in self.field_data] if len(fields) == 0: return smoothing_style = getattr(self.ds, "sph_smoothing_style", "scatter") normalize = getattr(self.ds, "use_sph_normalization", True) kernel_name = getattr(self.ds, "kernel_name", "cubic") bounds, size = self._get_grid_bounds_size() period = self.ds.coordinates.period.copy() if hasattr(period, "in_units"): period = period.in_units("code_length").d # check periodicity per dimension is_periodic = self.ds.periodicity if smoothing_style == "scatter": for field in fields: fi = self.ds._get_field_info(field) ptype = self._check_sph_type(fi) buff = np.zeros(size, dtype="float64") if normalize: buff_den = np.zeros(size, dtype="float64") pbar = tqdm(desc=f"Interpolating SPH field {field}") for chunk in self._data_source.chunks([field], "io"): px = chunk[ptype, "particle_position_x"].in_base("code").d py = chunk[ptype, "particle_position_y"].in_base("code").d pz = chunk[ptype, "particle_position_z"].in_base("code").d hsml = chunk[ptype, "smoothing_length"].in_base("code").d mass = chunk[ptype, "particle_mass"].in_base("code").d dens = chunk[ptype, "density"].in_base("code").d field_quantity = chunk[field].d pixelize_sph_kernel_arbitrary_grid( buff, px, py, pz, hsml, mass, dens, field_quantity, bounds, pbar=pbar, check_period=is_periodic, period=period, kernel_name=kernel_name, ) if normalize: pixelize_sph_kernel_arbitrary_grid( buff_den, px, py, pz, hsml, mass, dens, np.ones(dens.shape[0]), bounds, pbar=pbar, check_period=is_periodic, period=period, kernel_name=kernel_name, ) if normalize: normalization_3d_utility(buff, buff_den) self[field] = self.ds.arr(buff, fi.units) pbar.close() if smoothing_style == "gather": num_neighbors = getattr(self.ds, "num_neighbors", 32) for field in fields: fi = self.ds._get_field_info(field) ptype = self._check_sph_type(fi) buff = np.zeros(size, dtype="float64") fields_to_get = [ "particle_position", "density", "particle_mass", "smoothing_length", field[1], ] all_fields = all_data(self.ds, ptype, fields_to_get, kdtree=True) interpolate_sph_grid_gather( buff, all_fields["particle_position"], bounds, all_fields["smoothing_length"], all_fields["particle_mass"], all_fields["density"], all_fields[field[1]].in_units(fi.units), self.ds.index.kdtree, use_normalization=normalize, num_neigh=num_neighbors, ) self[field] = self.ds.arr(buff, fi.units) def _fill_fields(self, fields): fields = [f for f in fields if f not in self.field_data] if len(fields) == 0: return output_fields = [ np.zeros(self.ActiveDimensions, dtype="float64") for field in fields ] domain_dims = self.ds.domain_dimensions.astype( "int64" ) * self.ds.relative_refinement(0, self.level) refine_by = self.ds.refine_by if not is_sequence(self.ds.refine_by): refine_by = [refine_by, refine_by, refine_by] refine_by = np.array(refine_by, dtype="i8") for chunk in parallel_objects(self._data_source.chunks(fields, "io")): input_fields = [chunk[field] for field in fields] # NOTE: This usage of "refine_by" is actually *okay*, because it's # being used with respect to iref, which is *already* scaled! fill_region( input_fields, output_fields, self.level, self.global_startindex, chunk.icoords, chunk.ires, domain_dims, refine_by, ) if self.comm.size > 1: for i in range(len(fields)): output_fields[i] = self.comm.mpi_allreduce(output_fields[i], op="sum") for field, v in zip(fields, output_fields, strict=True): fi = self.ds._get_field_info(field) self[field] = self.ds.arr(v, fi.units) def _generate_container_field(self, field): rv = self.ds.arr(np.ones(self.ActiveDimensions, dtype="float64"), "") axis_name = self.ds.coordinates.axis_name if field == ("index", f"d{axis_name[0]}"): np.multiply(rv, self.dds[0], rv) elif field == ("index", f"d{axis_name[1]}"): np.multiply(rv, self.dds[1], rv) elif field == ("index", f"d{axis_name[2]}"): np.multiply(rv, self.dds[2], rv) elif field == ("index", axis_name[0]): x = np.mgrid[ self.left_edge[0] + 0.5 * self.dds[0] : self.right_edge[0] - 0.5 * self.dds[0] : self.ActiveDimensions[0] * 1j ] np.multiply(rv, x[:, None, None], rv) elif field == ("index", axis_name[1]): y = np.mgrid[ self.left_edge[1] + 0.5 * self.dds[1] : self.right_edge[1] - 0.5 * self.dds[1] : self.ActiveDimensions[1] * 1j ] np.multiply(rv, y[None, :, None], rv) elif field == ("index", axis_name[2]): z = np.mgrid[ self.left_edge[2] + 0.5 * self.dds[2] : self.right_edge[2] - 0.5 * self.dds[2] : self.ActiveDimensions[2] * 1j ] np.multiply(rv, z[None, None, :], rv) else: raise KeyError(field) return rv @property def LeftEdge(self): return self.left_edge @property def RightEdge(self): return self.right_edge def deposit(self, positions, fields=None, method=None, kernel_name="cubic"): cls = getattr(particle_deposit, f"deposit_{method}", None) if cls is None: raise YTParticleDepositionNotImplemented(method) # We allocate number of zones, not number of octs. Everything # inside this is Fortran ordered because of the ordering in the # octree deposit routines, so we reverse it here to match the # convention there nvals = tuple(self.ActiveDimensions[::-1]) # append a dummy dimension because we are only depositing onto # one grid op = cls(nvals + (1,), kernel_name) op.initialize() op.process_grid(self, positions, fields) # Fortran-ordered, so transpose. vals = op.finalize().transpose() # squeeze dummy dimension we appended above return np.squeeze(vals, axis=0) def write_to_gdf(self, gdf_path, fields, nprocs=1, field_units=None, **kwargs): r""" Write the covering grid data to a GDF file. Parameters ---------- gdf_path : string Pathname of the GDF file to write. fields : list of strings Fields to write to the GDF file. nprocs : integer, optional Split the covering grid into *nprocs* subgrids before writing to the GDF file. Default: 1 field_units : dictionary, optional Dictionary of units to convert fields to. If not set, fields are in their default units. All remaining keyword arguments are passed to yt.utilities.grid_data_format.writer.write_to_gdf. Examples -------- >>> cube.write_to_gdf( ... "clumps.h5", ... [("gas", "density"), ("gas", "temperature")], ... nprocs=16, ... overwrite=True, ... ) """ data = {} for field in fields: if field in field_units: units = field_units[field] else: units = str(self[field].units) data[field] = (self[field].in_units(units).v, units) le = self.left_edge.v re = self.right_edge.v bbox = np.array([[l, r] for l, r in zip(le, re, strict=True)]) ds = load_uniform_grid( data, self.ActiveDimensions, bbox=bbox, length_unit=self.ds.length_unit, time_unit=self.ds.time_unit, mass_unit=self.ds.mass_unit, nprocs=nprocs, sim_time=self.ds.current_time.v, ) write_to_gdf(ds, gdf_path, **kwargs) def _get_grid_bounds_size(self): dd = self.ds.domain_width / 2**self.level bounds = np.zeros(6, dtype="float64") bounds[0] = self.left_edge[0].in_base("code") bounds[1] = bounds[0] + dd[0].d * self.ActiveDimensions[0] bounds[2] = self.left_edge[1].in_base("code") bounds[3] = bounds[2] + dd[1].d * self.ActiveDimensions[1] bounds[4] = self.left_edge[2].in_base("code") bounds[5] = bounds[4] + dd[2].d * self.ActiveDimensions[2] size = np.ones(3, dtype="int64") * 2**self.level return bounds, size def to_fits_data(self, fields, length_unit=None): r"""Export a set of gridded fields to a FITS file. This will export a set of FITS images of either the fields specified or all the fields already in the object. Parameters ---------- fields : list of strings These fields will be pixelized and output. If "None", the keys of the FRB will be used. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. """ from yt.visualization.fits_image import FITSImageData if length_unit is None: length_unit = self.ds.length_unit fields = list(iter_fields(fields)) fid = FITSImageData(self, fields, length_unit=length_unit) return fid class YTArbitraryGrid(YTCoveringGrid): """A 3D region with arbitrary bounds and dimensions. In contrast to the Covering Grid, this object accepts a left edge, a right edge, and dimensions. This allows it to be used for creating 3D particle deposition fields that are independent of the underlying mesh, whether that is yt-generated or from the simulation data. For example, arbitrary boxes around particles can be drawn and particle deposition fields can be created. This object will refuse to generate any fluid fields. Parameters ---------- left_edge : array_like The left edge of the region to be extracted right_edge : array_like The left edge of the region to be extracted dims : array_like Number of cells along each axis of resulting grid. Examples -------- >>> obj = ds.arbitrary_grid( ... [0.0, 0.0, 0.0], [0.99, 0.99, 0.99], dims=[128, 128, 128] ... ) """ _spatial = True _type_name = "arbitrary_grid" _con_args = ("left_edge", "right_edge", "ActiveDimensions") _container_fields = ( ("index", "dx"), ("index", "dy"), ("index", "dz"), ("index", "x"), ("index", "y"), ("index", "z"), ) def __init__(self, left_edge, right_edge, dims, ds=None, field_parameters=None): if field_parameters is None: center = None else: center = field_parameters.get("center", None) YTSelectionContainer3D.__init__(self, center, ds, field_parameters) self.left_edge = self._sanitize_edge(left_edge) self.right_edge = self._sanitize_edge(right_edge) self.ActiveDimensions = self._sanitize_dims(dims) self.dds = self.base_dds = ( self.right_edge - self.left_edge ) / self.ActiveDimensions self.level = 99 self._setup_data_source() def _fill_fields(self, fields): fields = [f for f in fields if f not in self.field_data] if len(fields) == 0: return # It may be faster to adapt fill_region_float to fill multiple fields # instead of looping here for field in fields: dest = np.zeros(self.ActiveDimensions, dtype="float64") for chunk in self._data_source.chunks(fields, "io"): fill_region_float( chunk.fcoords, chunk.fwidth, chunk[field], self.left_edge, self.right_edge, dest, 1, self.ds.domain_width, int(any(self.ds.periodicity)), ) fi = self.ds._get_field_info(field) self[field] = self.ds.arr(dest, fi.units) def _get_grid_bounds_size(self): bounds = np.empty(6, dtype="float64") bounds[0] = self.left_edge[0].in_base("code") bounds[2] = self.left_edge[1].in_base("code") bounds[4] = self.left_edge[2].in_base("code") bounds[1] = self.right_edge[0].in_base("code") bounds[3] = self.right_edge[1].in_base("code") bounds[5] = self.right_edge[2].in_base("code") size = self.ActiveDimensions return bounds, size class LevelState: current_dx = None current_dims = None current_level = None global_startindex = None old_global_startindex = None fields = None data_source = None # These are all cached here as numpy arrays, without units, in # code_lengths. domain_width = None domain_left_edge = None domain_right_edge = None left_edge = None right_edge = None base_dx = None dds = None class YTSmoothedCoveringGrid(YTCoveringGrid): """A 3D region with all data extracted and interpolated to a single, specified resolution. (Identical to covering_grid, except that it interpolates.) Smoothed covering grids start at level 0, interpolating to fill the region to level 1, replacing any cells actually covered by level 1 data, and then recursively repeating this process until it reaches the specified `level`. Parameters ---------- level : int The resolution level data is uniformly gridded at left_edge : array_like The left edge of the region to be extracted dims : array_like Number of cells along each axis of resulting covering_grid. fields : array_like, optional A list of fields that you'd like pre-generated for your object Example ------- cube = ds.smoothed_covering_grid(2, left_edge=[0.0, 0.0, 0.0], \ dims=[128, 128, 128]) """ _type_name = "smoothed_covering_grid" filename = None _min_level = None @wraps(YTCoveringGrid.__init__) # type: ignore [misc] def __init__(self, *args, **kwargs): ds = kwargs["ds"] self._base_dx = ( ds.domain_right_edge - ds.domain_left_edge ) / ds.domain_dimensions.astype("float64") self.global_endindex = None YTCoveringGrid.__init__(self, *args, **kwargs) self._final_start_index = self.global_startindex def _setup_data_source(self, level_state=None): if level_state is None: super()._setup_data_source() return # We need a buffer region to allow for zones that contribute to the # interpolation but are not directly inside our bounds level_state.data_source = self.ds.region( self.center, level_state.left_edge - level_state.current_dx, level_state.right_edge + level_state.current_dx, ) level_state.data_source.min_level = level_state.current_level level_state.data_source.max_level = level_state.current_level self._pdata_source = self.ds.region( self.center, level_state.left_edge - level_state.current_dx, level_state.right_edge + level_state.current_dx, ) self._pdata_source.min_level = level_state.current_level self._pdata_source.max_level = level_state.current_level def _compute_minimum_level(self): # This attempts to determine the minimum level that we should be # starting on for this box. It does this by identifying the minimum # level that could contribute to the minimum bounding box at that # level; that means that all cells from coarser levels will be replaced. if self._min_level is not None: return self._min_level ils = LevelState() min_level = 0 for l in range(self.level, 0, -1): dx = self._base_dx / self.ds.relative_refinement(0, l) start_index, end_index, dims = self._minimal_box(dx) ils.left_edge = start_index * dx + self.ds.domain_left_edge ils.right_edge = ils.left_edge + dx * dims ils.current_dx = dx ils.current_level = l self._setup_data_source(ils) # Reset the max_level ils.data_source.min_level = 0 ils.data_source.max_level = l ils.data_source.loose_selection = False min_level = self.level for chunk in ils.data_source.chunks([], "io"): # With our odd selection methods, we can sometimes get no-sized ires. ir = chunk.ires if ir.size == 0: continue min_level = min(ir.min(), min_level) if min_level >= l: break self._min_level = min_level return min_level def _fill_fields(self, fields): fields = [f for f in fields if f not in self.field_data] if len(fields) == 0: return ls = self._initialize_level_state(fields) min_level = self._compute_minimum_level() # NOTE: This usage of "refine_by" is actually *okay*, because it's # being used with respect to iref, which is *already* scaled! refine_by = self.ds.refine_by if not is_sequence(self.ds.refine_by): refine_by = [refine_by, refine_by, refine_by] refine_by = np.array(refine_by, dtype="i8") runtime_errors_count = 0 for level in range(self.level + 1): if level < min_level: self._update_level_state(ls) continue nd = self.ds.dimensionality refinement = np.zeros_like(ls.base_dx) refinement += self.ds.relative_refinement(0, ls.current_level) refinement[nd:] = 1 domain_dims = self.ds.domain_dimensions * refinement domain_dims = domain_dims.astype("int64") tot = ls.current_dims.prod() for chunk in ls.data_source.chunks(fields, "io"): chunk[fields[0]] input_fields = [chunk[field] for field in fields] tot -= fill_region( input_fields, ls.fields, ls.current_level, ls.global_startindex, chunk.icoords, chunk.ires, domain_dims, refine_by, ) if level == 0 and tot != 0: runtime_errors_count += 1 self._update_level_state(ls) if runtime_errors_count: warnings.warn( "Something went wrong during field computation. " "This is likely due to missing ghost-zones support " f"in class {type(self.ds)}", category=RuntimeWarning, stacklevel=1, ) mylog.debug("Caught %d runtime errors.", runtime_errors_count) for field, v in zip(fields, ls.fields, strict=True): if self.level > 0: v = v[1:-1, 1:-1, 1:-1] fi = self.ds._get_field_info(field) self[field] = self.ds.arr(v, fi.units) def _initialize_level_state(self, fields): ls = LevelState() ls.domain_width = self.ds.domain_width ls.domain_left_edge = self.ds.domain_left_edge ls.domain_right_edge = self.ds.domain_right_edge ls.base_dx = self._base_dx ls.dds = self.dds ls.left_edge = self.left_edge ls.right_edge = self.right_edge for att in ( "domain_width", "domain_left_edge", "domain_right_edge", "left_edge", "right_edge", "base_dx", "dds", ): setattr(ls, att, getattr(ls, att).in_units("code_length").d) ls.current_dx = ls.base_dx ls.current_level = 0 ls.global_startindex, end_index, idims = self._minimal_box(ls.current_dx) ls.current_dims = idims.astype("int32") ls.left_edge = ls.global_startindex * ls.current_dx + self.ds.domain_left_edge.d ls.right_edge = ls.left_edge + ls.current_dims * ls.current_dx ls.fields = [np.zeros(idims, dtype="float64") - 999 for field in fields] self._setup_data_source(ls) return ls def _minimal_box(self, dds): LL = self.left_edge.d - self.ds.domain_left_edge.d # Nudge in case we're on the edge LL += np.finfo(np.float64).eps LS = self.right_edge.d - self.ds.domain_left_edge.d LS += np.finfo(np.float64).eps cell_start = LL / dds # This is the cell we're inside cell_end = LS / dds if self.level == 0: start_index = np.array(np.floor(cell_start), dtype="int64") end_index = np.array(np.ceil(cell_end), dtype="int64") dims = np.rint((self.ActiveDimensions * self.dds.d) / dds).astype("int64") else: # Give us one buffer start_index = np.rint(cell_start).astype("int64") - 1 # How many root cells do we occupy? end_index = np.rint(cell_end).astype("int64") dims = end_index - start_index + 1 return start_index, end_index, dims.astype("int32") def _update_level_state(self, level_state): ls = level_state if ls.current_level >= self.level: return rf = float(self.ds.relative_refinement(ls.current_level, ls.current_level + 1)) ls.current_level += 1 nd = self.ds.dimensionality refinement = np.zeros_like(ls.base_dx) refinement += self.ds.relative_refinement(0, ls.current_level) refinement[nd:] = 1 ls.current_dx = ls.base_dx / refinement ls.old_global_startindex = ls.global_startindex ls.global_startindex, end_index, ls.current_dims = self._minimal_box( ls.current_dx ) ls.left_edge = ls.global_startindex * ls.current_dx + self.ds.domain_left_edge.d ls.right_edge = ls.left_edge + ls.current_dims * ls.current_dx input_left = (level_state.old_global_startindex) * rf + 1 new_fields = [] for input_field in level_state.fields: output_field = np.zeros(ls.current_dims, dtype="float64") output_left = level_state.global_startindex + 0.5 ghost_zone_interpolate( rf, input_field, input_left, output_field, output_left ) new_fields.append(output_field) level_state.fields = new_fields self._setup_data_source(ls) class YTSurface(YTSelectionContainer3D): r"""This surface object identifies isocontours on a cell-by-cell basis, with no consideration of global connectedness, and returns the vertices of the Triangles in that isocontour. This object simply returns the vertices of all the triangles calculated by the `marching cubes `_ algorithm; for more complex operations, such as identifying connected sets of cells above a given threshold, see the extract_connected_sets function. This is more useful for calculating, for instance, total isocontour area, or visualizing in an external program (such as `MeshLab `_.) The object has the properties .vertices and will sample values if a field is requested. The values are interpolated to the center of a given face. Parameters ---------- data_source : YTSelectionContainer This is the object which will used as a source surface_field : string Any field that can be obtained in a data object. This is the field which will be isocontoured. field_value : float, YTQuantity, or unit tuple The value at which the isocontour should be calculated. Examples -------- This will create a data object, find a nice value in the center, and output the vertices to "triangles.obj" after rescaling them. >>> from yt.units import kpc >>> sp = ds.sphere("max", (10, "kpc")) >>> surf = ds.surface(sp, ("gas", "density"), 5e-27) >>> print(surf["gas", "temperature"]) >>> print(surf.vertices) >>> bounds = [ ... (sp.center[i] - 5.0 * kpc, sp.center[i] + 5.0 * kpc) for i in range(3) ... ] >>> surf.export_ply("my_galaxy.ply", bounds=bounds) """ _type_name = "surface" _con_args = ("data_source", "surface_field", "field_value") _container_fields = ( ("index", "dx"), ("index", "dy"), ("index", "dz"), ("index", "x"), ("index", "y"), ("index", "z"), ) def __init__(self, data_source, surface_field, field_value, ds=None): self.data_source = data_source self.surface_field = data_source._determine_fields(surface_field)[0] finfo = data_source.ds.field_info[self.surface_field] try: self.field_value = field_value.to(finfo.units) except AttributeError: if isinstance(field_value, tuple): self.field_value = data_source.ds.quan(*field_value) self.field_value = self.field_value.to(finfo.units) else: self.field_value = data_source.ds.quan(field_value, finfo.units) self.vertex_samples = YTFieldData() center = data_source.get_field_parameter("center") super().__init__(center=center, ds=ds) def _generate_container_field(self, field): self.get_data(field) return self[field] def get_data(self, fields=None, sample_type="face", no_ghost=False): if isinstance(fields, list) and len(fields) > 1: for field in fields: self.get_data(field) return elif isinstance(fields, list): fields = fields[0] # Now we have a "fields" value that is either a string or None if fields is not None: mylog.info("Extracting (sampling: %s)", fields) verts = [] samples = [] for _io_chunk in parallel_objects(self.data_source.chunks([], "io")): for block, mask in self.data_source.blocks: my_verts = self._extract_isocontours_from_grid( block, self.surface_field, self.field_value, mask, fields, sample_type, no_ghost=no_ghost, ) if fields is not None: my_verts, svals = my_verts samples.append(svals) verts.append(my_verts) verts = np.concatenate(verts).transpose() verts = self.comm.par_combine_object(verts, op="cat", datatype="array") # verts is an ndarray here and will always be in code units, so we # expose it in the public API as a YTArray self._vertices = self.ds.arr(verts, "code_length") if fields is not None: samples = uconcatenate(samples) samples = self.comm.par_combine_object(samples, op="cat", datatype="array") if sample_type == "face": self[fields] = samples elif sample_type == "vertex": self.vertex_samples[fields] = samples def _extract_isocontours_from_grid( self, grid, field, value, mask, sample_values=None, sample_type="face", no_ghost=False, ): # TODO: check if multiple fields can be passed here vals = grid.get_vertex_centered_data([field], no_ghost=no_ghost)[field] if sample_values is not None: # TODO: is no_ghost=False correct here? svals = grid.get_vertex_centered_data([sample_values])[sample_values] else: svals = None sample_type = {"face": 1, "vertex": 2}[sample_type] my_verts = march_cubes_grid( value, vals, mask, grid.LeftEdge, grid.dds, svals, sample_type ) return my_verts def calculate_flux(self, field_x, field_y, field_z, fluxing_field=None): r"""This calculates the flux over the surface. This function will conduct `Marching Cubes`_ on all the cells in a given data container (grid-by-grid), and then for each identified triangular segment of an isocontour in a given cell, calculate the gradient (i.e., normal) in the isocontoured field, interpolate the local value of the "fluxing" field, the area of the triangle, and then return: area * local_flux_value * (n dot v) Where area, local_value, and the vector v are interpolated at the barycenter (weighted by the vertex values) of the triangle. Note that this specifically allows for the field fluxing across the surface to be *different* from the field being contoured. If the fluxing_field is not specified, it is assumed to be 1.0 everywhere, and the raw flux with no local-weighting is returned. Additionally, the returned flux is defined as flux *into* the surface, not flux *out of* the surface. Parameters ---------- field_x : string The x-component field field_y : string The y-component field field_z : string The z-component field fluxing_field : string, optional The field whose passage over the surface is of interest. If not specified, assumed to be 1.0 everywhere. Returns ------- flux : YTQuantity The summed flux. References ---------- .. _Marching Cubes: https://en.wikipedia.org/wiki/Marching_cubes Examples -------- This will create a data object, find a nice value in the center, and calculate the metal flux over it. >>> sp = ds.sphere("max", (10, "kpc")) >>> surf = ds.surface(sp, ("gas", "density"), 5e-27) >>> flux = surf.calculate_flux( ... ("gas", "velocity_x"), ... ("gas", "velocity_y"), ... ("gas", "velocity_z"), ... ("gas", "metal_density"), ... ) """ flux = 0.0 mylog.info("Fluxing %s", fluxing_field) for _io_chunk in parallel_objects(self.data_source.chunks([], "io")): for block, mask in self.data_source.blocks: flux += self._calculate_flux_in_grid( block, mask, field_x, field_y, field_z, fluxing_field ) flux = self.comm.mpi_allreduce(flux, op="sum") return flux def _calculate_flux_in_grid( self, grid, mask, field_x, field_y, field_z, fluxing_field=None ): vc_fields = [self.surface_field, field_x, field_y, field_z] if fluxing_field is not None: vc_fields.append(fluxing_field) vc_data = grid.get_vertex_centered_data(vc_fields) if fluxing_field is None: ff = self.ds.arr( np.ones_like(vc_data[self.surface_field].d, dtype="float64"), "dimensionless", ) else: ff = vc_data[fluxing_field] surf_vals = vc_data[self.surface_field] field_x_vals = vc_data[field_x] field_y_vals = vc_data[field_y] field_z_vals = vc_data[field_z] ret = march_cubes_grid_flux( self.field_value, surf_vals, field_x_vals, field_y_vals, field_z_vals, ff, mask, grid.LeftEdge, grid.dds, ) # assumes all the fluxing fields have the same units ret_units = field_x_vals.units * ff.units * grid.dds.units**2 ret = self.ds.arr(ret, ret_units) ret.convert_to_units(self.ds.unit_system[ret_units.dimensions]) return ret _vertices = None @property def vertices(self): if self._vertices is None: self.get_data() return self._vertices @property def triangles(self): vv = np.empty((self.vertices.shape[1] // 3, 3, 3), dtype="float64") vv = self.ds.arr(vv, self.vertices.units) for i in range(3): for j in range(3): vv[:, i, j] = self.vertices[j, i::3] return vv _surface_area = None @property def surface_area(self): if self._surface_area is not None: return self._surface_area tris = self.triangles x = tris[:, 1, :] - tris[:, 0, :] y = tris[:, 2, :] - tris[:, 0, :] areas = (x[:, 1] * y[:, 2] - x[:, 2] * y[:, 1]) ** 2 np.add(areas, (x[:, 2] * y[:, 0] - x[:, 0] * y[:, 2]) ** 2, out=areas) np.add(areas, (x[:, 0] * y[:, 1] - x[:, 1] * y[:, 0]) ** 2, out=areas) np.sqrt(areas, out=areas) self._surface_area = 0.5 * areas.sum() return self._surface_area def export_obj( self, filename, transparency=1.0, dist_fac=None, color_field=None, emit_field=None, color_map=None, color_log=True, emit_log=True, plot_index=None, color_field_max=None, color_field_min=None, emit_field_max=None, emit_field_min=None, ): r"""Export the surface to the OBJ format Suitable for visualization in many different programs (e.g., Blender). NOTE: this exports an .obj file and an .mtl file, both with the general 'filename' as a prefix. The .obj file points to the .mtl file in its header, so if you move the 2 files, make sure you change the .obj header to account for this. ALSO NOTE: the emit_field needs to be a combination of the other 2 fields used to have the emissivity track with the color. Parameters ---------- filename : string The file this will be exported to. This cannot be a file-like object. If there are no file extensions included - both obj & mtl files are created. transparency : float This gives the transparency of the output surface plot. Values from 0.0 (invisible) to 1.0 (opaque). dist_fac : float Divide the axes distances by this amount. color_field : string Should a field be sample and colormapped? emit_field : string Should we track the emissivity of a field? This should be a combination of the other 2 fields being used. color_map : string Which color map should be applied? color_log : bool Should the color field be logged before being mapped? emit_log : bool Should the emitting field be logged before being mapped? plot_index : integer Index of plot for multiple plots. If none, then only 1 plot. color_field_max : float Maximum value of the color field across all surfaces. color_field_min : float Minimum value of the color field across all surfaces. emit_field_max : float Maximum value of the emitting field across all surfaces. emit_field_min : float Minimum value of the emitting field across all surfaces. Examples -------- >>> sp = ds.sphere("max", (10, "kpc")) >>> trans = 1.0 >>> surf = ds.surface(sp, ("gas", "density"), 5e-27) >>> surf.export_obj("my_galaxy", transparency=trans) >>> sp = ds.sphere("max", (10, "kpc")) >>> mi, ma = sp.quantities.extrema("temperature") >>> rhos = [1e-24, 1e-25] >>> trans = [0.5, 1.0] >>> for i, r in enumerate(rhos): ... surf = ds.surface(sp, "density", r) ... surf.export_obj( ... "my_galaxy", ... transparency=trans[i], ... color_field="temperature", ... plot_index=i, ... color_field_max=ma, ... color_field_min=mi, ... ) >>> sp = ds.sphere("max", (10, "kpc")) >>> rhos = [1e-24, 1e-25] >>> trans = [0.5, 1.0] >>> def _Emissivity(field, data): ... return ( ... data["gas", "density"] ... * data["gas", "density"] ... * np.sqrt(data["gas", "temperature"]) ... ) >>> ds.add_field( ... ("gas", "emissivity"), ... function=_Emissivity, ... sampling_type="cell", ... units=r"g**2*sqrt(K)/cm**6", ... ) >>> for i, r in enumerate(rhos): ... surf = ds.surface(sp, "density", r) ... surf.export_obj( ... "my_galaxy", ... transparency=trans[i], ... color_field="temperature", ... emit_field="emissivity", ... plot_index=i, ... ) """ if color_map is None: color_map = ytcfg.get("yt", "default_colormap") if self.vertices is None: if color_field is not None: self.get_data(color_field, "face") elif color_field is not None: if color_field not in self.field_data: self[color_field] if color_field is None: self.get_data(self.surface_field, "face") if emit_field is not None: if color_field not in self.field_data: self[emit_field] only_on_root( self._export_obj, filename, transparency, dist_fac, color_field, emit_field, color_map, color_log, emit_log, plot_index, color_field_max, color_field_min, emit_field_max, emit_field_min, ) def _color_samples_obj( self, cs, em, color_log, emit_log, color_map, arr, color_field_max, color_field_min, color_field, emit_field_max, emit_field_min, emit_field, ): # this now holds for obj files if color_field is not None: if color_log: cs = np.log10(cs) if emit_field is not None: if emit_log: em = np.log10(em) if color_field is not None: if color_field_min is None: cs = [float(field) for field in cs] cs = np.array(cs) mi = cs.min() else: mi = color_field_min if color_log: mi = np.log10(mi) if color_field_max is None: cs = [float(field) for field in cs] cs = np.array(cs) ma = cs.max() else: ma = color_field_max if color_log: ma = np.log10(ma) cs = (cs - mi) / (ma - mi) else: cs[:] = 1.0 # to get color indices for OBJ formatting lut = get_colormap_lut(color_map) x = np.mgrid[0.0 : 1.0 : lut[0].shape[0] * 1j] arr["cind"][:] = (np.interp(cs, x, x) * (lut[0].shape[0] - 1)).astype("uint8") # now, get emission if emit_field is not None: if emit_field_min is None: em = [float(field) for field in em] em = np.array(em) emi = em.min() else: emi = emit_field_min if emit_log: emi = np.log10(emi) if emit_field_max is None: em = [float(field) for field in em] em = np.array(em) ema = em.max() else: ema = emit_field_max if emit_log: ema = np.log10(ema) em = (em - emi) / (ema - emi) x = np.mgrid[0.0:255.0:2j] # assume 1 emissivity per color arr["emit"][:] = ( np.interp(em, x, x) ) * 2.0 # for some reason, max emiss = 2 else: arr["emit"][:] = 0.0 @parallel_root_only def _export_obj( self, filename, transparency, dist_fac=None, color_field=None, emit_field=None, color_map=None, color_log=True, emit_log=True, plot_index=None, color_field_max=None, color_field_min=None, emit_field_max=None, emit_field_min=None, ): if color_map is None: color_map = ytcfg.get("yt", "default_colormap") if plot_index is None: plot_index = 0 if isinstance(filename, io.IOBase): fobj = filename + ".obj" fmtl = filename + ".mtl" else: if plot_index == 0: fobj = open(filename + ".obj", "w") fmtl = open(filename + ".mtl", "w") cc = 1 else: # read in last vertex linesave = "" for line in fileinput.input(filename + ".obj"): if line[0] == "f": linesave = line p = [m.start() for m in finditer(" ", linesave)] cc = int(linesave[p[len(p) - 1] :]) + 1 fobj = open(filename + ".obj", "a") fmtl = open(filename + ".mtl", "a") ftype = [("cind", "uint8"), ("emit", "float")] vtype = [("x", "float"), ("y", "float"), ("z", "float")] if plot_index == 0: fobj.write("# yt OBJ file\n") fobj.write("# www.yt-project.org\n") fobj.write( f"mtllib {filename}.mtl\n\n" ) # use this material file for the faces fmtl.write("# yt MLT file\n") fmtl.write("# www.yt-project.org\n\n") # (0) formulate vertices nv = self.vertices.shape[1] # number of groups of vertices f = np.empty( nv // self.vertices.shape[0], dtype=ftype ) # store sets of face colors v = np.empty(nv, dtype=vtype) # stores vertices if color_field is not None: cs = self[color_field] else: cs = np.empty(self.vertices.shape[1] // self.vertices.shape[0]) if emit_field is not None: em = self[emit_field] else: em = np.empty(self.vertices.shape[1] // self.vertices.shape[0]) self._color_samples_obj( cs, em, color_log, emit_log, color_map, f, color_field_max, color_field_min, color_field, emit_field_max, emit_field_min, emit_field, ) # map color values to color scheme lut = get_colormap_lut(color_map) # interpolate emissivity to enumerated colors emiss = np.interp( np.mgrid[0 : lut[0].shape[0]], np.mgrid[0 : len(cs)], f["emit"][:] ) if dist_fac is None: # then normalize by bounds DLE = self.pf.domain_left_edge DRE = self.pf.domain_right_edge bounds = [(DLE[i], DRE[i]) for i in range(3)] for i, ax in enumerate("xyz"): # Do the bounds first since we cast to f32 tmp = self.vertices[i, :] np.subtract(tmp, bounds[i][0], tmp) w = bounds[i][1] - bounds[i][0] np.divide(tmp, w, tmp) np.subtract(tmp, 0.5, tmp) # Center at origin. v[ax][:] = tmp else: for i, ax in enumerate("xyz"): tmp = self.vertices[i, :] np.divide(tmp, dist_fac, tmp) v[ax][:] = tmp # (1) write all colors per surface to mtl file for i in range(0, lut[0].shape[0]): omname = f"material_{i}_{plot_index}" # name of the material fmtl.write( f"newmtl {omname}\n" ) # the specific material (color) for this face fmtl.write(f"Ka {0.0:.6f} {0.0:.6f} {0.0:.6f}\n") # ambient color, keep off fmtl.write( f"Kd {lut[0][i]:.6f} {lut[1][i]:.6f} {lut[2][i]:.6f}\n" ) # color of face fmtl.write( f"Ks {0.0:.6f} {0.0:.6f} {0.0:.6f}\n" ) # specular color, keep off fmtl.write(f"d {transparency:.6f}\n") # transparency fmtl.write(f"em {emiss[i]:.6f}\n") # emissivity per color fmtl.write("illum 2\n") # not relevant, 2 means highlights on? fmtl.write(f"Ns {0:.6f}\n\n") # keep off, some other specular thing # (2) write vertices for i in range(0, self.vertices.shape[1]): fobj.write(f"v {v['x'][i]:.6f} {v['y'][i]:.6f} {v['z'][i]:.6f}\n") fobj.write("#done defining vertices\n\n") # (3) define faces and materials for each face for i in range(0, self.triangles.shape[0]): omname = f"material_{f['cind'][i]}_{plot_index}" # which color to use fobj.write( f"usemtl {omname}\n" ) # which material to use for this face (color) fobj.write(f"f {cc} {cc+1} {cc+2}\n\n") # vertices to color cc = cc + 3 fmtl.close() fobj.close() def export_blender( self, transparency=1.0, dist_fac=None, color_field=None, emit_field=None, color_map=None, color_log=True, emit_log=True, plot_index=None, color_field_max=None, color_field_min=None, emit_field_max=None, emit_field_min=None, ): r"""This exports the surface to the OBJ format, suitable for visualization in many different programs (e.g., Blender). NOTE: this exports an .obj file and an .mtl file, both with the general 'filename' as a prefix. The .obj file points to the .mtl file in its header, so if you move the 2 files, make sure you change the .obj header to account for this. ALSO NOTE: the emit_field needs to be a combination of the other 2 fields used to have the emissivity track with the color. Parameters ---------- transparency : float This gives the transparency of the output surface plot. Values from 0.0 (invisible) to 1.0 (opaque). dist_fac : float Divide the axes distances by this amount. color_field : string Should a field be sample and colormapped? emit_field : string Should we track the emissivity of a field? NOTE: this should be a combination of the other 2 fields being used. color_map : string Which color map should be applied? color_log : bool Should the color field be logged before being mapped? emit_log : bool Should the emitting field be logged before being mapped? plot_index : integer Index of plot for multiple plots. If none, then only 1 plot. color_field_max : float Maximum value of the color field across all surfaces. color_field_min : float Minimum value of the color field across all surfaces. emit_field_max : float Maximum value of the emitting field across all surfaces. emit_field_min : float Minimum value of the emitting field across all surfaces. Examples -------- >>> sp = ds.sphere("max", (10, "kpc")) >>> trans = 1.0 >>> surf = ds.surface(sp, ("gas", "density"), 5e-27) >>> surf.export_obj("my_galaxy", transparency=trans) >>> sp = ds.sphere("max", (10, "kpc")) >>> mi, ma = sp.quantities.extrema("temperature")[0] >>> rhos = [1e-24, 1e-25] >>> trans = [0.5, 1.0] >>> for i, r in enumerate(rhos): ... surf = ds.surface(sp, "density", r) ... surf.export_obj( ... "my_galaxy", ... transparency=trans[i], ... color_field="temperature", ... plot_index=i, ... color_field_max=ma, ... color_field_min=mi, ... ) >>> sp = ds.sphere("max", (10, "kpc")) >>> rhos = [1e-24, 1e-25] >>> trans = [0.5, 1.0] >>> def _Emissivity(field, data): ... return ( ... data["gas", "density"] ... * data["gas", "density"] ... * np.sqrt(data["gas", "temperature"]) ... ) >>> ds.add_field(("gas", "emissivity"), function=_Emissivity, units="g / cm**6") >>> for i, r in enumerate(rhos): ... surf = ds.surface(sp, "density", r) ... surf.export_obj( ... "my_galaxy", ... transparency=trans[i], ... color_field="temperature", ... emit_field="emissivity", ... plot_index=i, ... ) """ if color_map is None: color_map = ytcfg.get("yt", "default_colormap") if self.vertices is None: if color_field is not None: self.get_data(color_field, "face") elif color_field is not None: if color_field not in self.field_data: self[color_field] if color_field is None: self.get_data(self.surface_field, "face") if emit_field is not None: if color_field not in self.field_data: self[emit_field] fullverts, colors, alpha, emisses, colorindex = only_on_root( self._export_blender, transparency, dist_fac, color_field, emit_field, color_map, color_log, emit_log, plot_index, color_field_max, color_field_min, emit_field_max, emit_field_min, ) return fullverts, colors, alpha, emisses, colorindex def _export_blender( self, transparency, dist_fac=None, color_field=None, emit_field=None, color_map=None, color_log=True, emit_log=True, plot_index=None, color_field_max=None, color_field_min=None, emit_field_max=None, emit_field_min=None, ): if color_map is None: color_map = ytcfg.get("yt", "default_colormap") if plot_index is None: plot_index = 0 ftype = [("cind", "uint8"), ("emit", "float")] vtype = [("x", "float"), ("y", "float"), ("z", "float")] # (0) formulate vertices nv = self.vertices.shape[1] # number of groups of vertices f = np.empty( nv / self.vertices.shape[0], dtype=ftype ) # store sets of face colors v = np.empty(nv, dtype=vtype) # stores vertices if color_field is not None: cs = self[color_field] else: cs = np.empty(self.vertices.shape[1] / self.vertices.shape[0]) if emit_field is not None: em = self[emit_field] else: em = np.empty(self.vertices.shape[1] / self.vertices.shape[0]) self._color_samples_obj( cs, em, color_log, emit_log, color_map, f, color_field_max, color_field_min, color_field, emit_field_max, emit_field_min, emit_field, ) # map color values to color scheme lut = get_colormap_lut(color_map) # interpolate emissivity to enumerated colors emiss = np.interp( np.mgrid[0 : lut[0].shape[0]], np.mgrid[0 : len(cs)], f["emit"][:] ) if dist_fac is None: # then normalize by bounds DLE = self.ds.domain_left_edge DRE = self.ds.domain_right_edge bounds = [(DLE[i], DRE[i]) for i in range(3)] for i, ax in enumerate("xyz"): # Do the bounds first since we cast to f32 tmp = self.vertices[i, :] np.subtract(tmp, bounds[i][0], tmp) w = bounds[i][1] - bounds[i][0] np.divide(tmp, w, tmp) np.subtract(tmp, 0.5, tmp) # Center at origin. v[ax][:] = tmp else: for i, ax in enumerate("xyz"): tmp = self.vertices[i, :] np.divide(tmp, dist_fac, tmp) v[ax][:] = tmp return v, lut, transparency, emiss, f["cind"] def export_ply( self, filename, bounds=None, color_field=None, color_map=None, color_log=True, sample_type="face", no_ghost=False, *, color_field_max=None, color_field_min=None, ): r"""This exports the surface to the PLY format, suitable for visualization in many different programs (e.g., MeshLab). Parameters ---------- filename : string The file this will be exported to. This cannot be a file-like object. bounds : list of tuples The bounds the vertices will be normalized to. This is of the format: [(xmin, xmax), (ymin, ymax), (zmin, zmax)]. Defaults to the full domain. color_field : string Should a field be sample and colormapped? color_map : string Which color map should be applied? color_log : bool Should the color field be logged before being mapped? color_field_max : float Maximum value of the color field across all surfaces. color_field_min : float Minimum value of the color field across all surfaces. Examples -------- >>> from yt.units import kpc >>> sp = ds.sphere("max", (10, "kpc")) >>> surf = ds.surface(sp, ("gas", "density"), 5e-27) >>> print(surf["gas", "temperature"]) >>> print(surf.vertices) >>> bounds = [ ... (sp.center[i] - 5.0 * kpc, sp.center[i] + 5.0 * kpc) for i in range(3) ... ] >>> surf.export_ply("my_galaxy.ply", bounds=bounds) """ if color_map is None: color_map = ytcfg.get("yt", "default_colormap") if self.vertices is None: self.get_data(color_field, sample_type, no_ghost=no_ghost) elif color_field is not None: if sample_type == "face" and color_field not in self.field_data: self[color_field] elif sample_type == "vertex" and color_field not in self.vertex_samples: self.get_data(color_field, sample_type, no_ghost=no_ghost) self._export_ply( filename, bounds, color_field, color_map, color_log, sample_type, color_field_max=color_field_max, color_field_min=color_field_min, ) def _color_samples( self, cs, color_log, color_map, arr, *, color_field_max=None, color_field_min=None, ): cs = np.asarray(cs) if color_log: cs = np.log10(cs) if color_field_min is None: mi = cs.min() else: mi = color_field_min if color_log: mi = np.log10(mi) if color_field_max is None: ma = cs.max() else: ma = color_field_max if color_log: ma = np.log10(ma) cs = (cs - mi) / (ma - mi) from yt.visualization.image_writer import map_to_colors cs = map_to_colors(cs, color_map) arr["red"][:] = cs[0, :, 0] arr["green"][:] = cs[0, :, 1] arr["blue"][:] = cs[0, :, 2] @parallel_root_only def _export_ply( self, filename, bounds=None, color_field=None, color_map=None, color_log=True, sample_type="face", *, color_field_max=None, color_field_min=None, ): if color_map is None: color_map = ytcfg.get("yt", "default_colormap") if hasattr(filename, "read"): f = filename else: f = open(filename, "wb") if bounds is None: DLE = self.ds.domain_left_edge DRE = self.ds.domain_right_edge bounds = [(DLE[i], DRE[i]) for i in range(3)] elif any(not all(isinstance(be, YTArray) for be in b) for b in bounds): bounds = [ tuple( be if isinstance(be, YTArray) else self.ds.quan(be, "code_length") for be in b ) for b in bounds ] nv = self.vertices.shape[1] vs = [ ("x", ">> from yt.units import kpc >>> dd = ds.sphere("max", (200, "kpc")) >>> rho = 5e-27 >>> bounds = [ ... (dd.center[i] - 100.0 * kpc, dd.center[i] + 100.0 * kpc) ... for i in range(3) ... ] ... >>> surf = ds.surface(dd, ("gas", "density"), rho) >>> rv = surf.export_sketchfab( ... title="Testing Upload", ... description="A simple test of the uploader", ... color_field="temperature", ... color_map="hot", ... color_log=True, ... bounds=bounds, ... ) ... """ if color_map is None: color_map = ytcfg.get("yt", "default_colormap") api_key = api_key or ytcfg.get("yt", "sketchfab_api_key") if api_key in (None, "None"): raise YTNoAPIKey("SketchFab.com", "sketchfab_api_key") ply_file = TemporaryFile() self.export_ply( ply_file, bounds, color_field, color_map, color_log, sample_type="vertex", no_ghost=no_ghost, color_field_max=color_field_max, color_field_min=color_field_min, ) ply_file.seek(0) # Greater than ten million vertices and we throw an error but dump # to a file. if self.vertices.shape[1] > 1e7: tfi = 0 fn = "temp_model_%03i.ply" % tfi while os.path.exists(fn): fn = "temp_model_%03i.ply" % tfi tfi += 1 open(fn, "wb").write(ply_file.read()) raise YTTooManyVertices(self.vertices.shape[1], fn) zfs = NamedTemporaryFile(suffix=".zip") with zipfile.ZipFile(zfs, "w", zipfile.ZIP_DEFLATED) as zf: zf.writestr("yt_export.ply", ply_file.read()) zfs.seek(0) zfs.seek(0) data = { "token": api_key, "name": title, "description": description, "tags": "yt", } files = {"modelFile": zfs} upload_id = self._upload_to_sketchfab(data, files) upload_id = self.comm.mpi_bcast(upload_id, root=0) return upload_id @parallel_root_only def _upload_to_sketchfab(self, data, files): from yt.utilities.on_demand_imports import _requests as requests SKETCHFAB_DOMAIN = "sketchfab.com" SKETCHFAB_API_URL = f"https://api.{SKETCHFAB_DOMAIN}/v2/models" SKETCHFAB_MODEL_URL = f"https://{SKETCHFAB_DOMAIN}/models/" try: r = requests.post(SKETCHFAB_API_URL, data=data, files=files, verify=False) except requests.exceptions.RequestException: mylog.exception("An error has occurred") return result = r.json() if r.status_code != requests.codes.created: mylog.error("Upload to SketchFab failed with error: %s", result) return model_uid = result["uid"] model_url = SKETCHFAB_MODEL_URL + model_uid if model_uid: mylog.info("Model uploaded to: %s", model_url) else: mylog.error("Problem uploading.") return model_uid class YTOctree(YTSelectionContainer3D): """A 3D region with all the data filled into an octree. This container will deposit particle fields onto octs using a kernel and SPH smoothing. The octree is built in a depth-first fashion. Depth-first search (DFS) means that tree starts refining at the root node (this is the largest node which contains every particles) and refines as far as possible along each branch before backtracking. Parameters ---------- right_edge : array_like The right edge of the region to be extracted. Specify units by supplying a YTArray, otherwise code length units are assumed. left_edge : array_like The left edge of the region to be extracted. Specify units by supplying a YTArray, otherwise code length units are assumed. n_ref: int This is the maximum number of particles per leaf in the resulting octree. ptypes: list This is the type of particles to include when building the tree. This will default to all particles. Examples -------- octree = ds.octree(n_ref=64) x_positions_of_cells = octree['index', 'x'] y_positions_of_cells = octree['index', 'y'] z_positions_of_cells = octree['index', 'z'] density_of_gas_in_cells = octree['gas', 'density'] """ _spatial = True _type_name = "octree" _con_args = ("left_edge", "right_edge", "n_ref") _container_fields = ( ("index", "dx"), ("index", "dy"), ("index", "dz"), ("index", "x"), ("index", "y"), ("index", "z"), ("index", "depth"), ("index", "refined"), ("index", "sizes"), ("index", "positions"), ) def __init__( self, left_edge=None, right_edge=None, n_ref=32, ptypes=None, ds=None, field_parameters=None, ): if field_parameters is None: center = None else: center = field_parameters.get("center", None) YTSelectionContainer3D.__init__(self, center, ds, field_parameters) self.left_edge = self._sanitize_edge(left_edge, ds.domain_left_edge) self.right_edge = self._sanitize_edge(right_edge, ds.domain_right_edge) self.n_ref = n_ref self.ptypes = self._sanitize_ptypes(ptypes) self._setup_data_source() self.tree def _generate_tree(self): positions = [] for ptype in self.ptypes: positions.append( self._data_source[ptype, "particle_position"].in_units("code_length").d ) positions = ( np.concatenate(positions) if len(positions) > 0 else np.array(positions) ) if not positions.size: mylog.info("No particles found!") self._octree = None return mylog.info("Allocating Octree for %s particles", positions.shape[0]) self._octree = CyOctree( positions, left_edge=self.left_edge.to("code_length").d, right_edge=self.right_edge.to("code_length").d, n_ref=self.n_ref, ) mylog.info("Allocated %s nodes in octree", self._octree.num_nodes) mylog.info("Octree bound %s particles", self._octree.bound_particles) # Now we store the index data about the octree in the python container ds = self.ds pos = ds.arr(self._octree.node_positions, "code_length") self["index", "positions"] = pos self["index", "x"] = pos[:, 0] self["index", "y"] = pos[:, 1] self["index", "z"] = pos[:, 2] self["index", "refined"] = self._octree.node_refined sizes = ds.arr(self._octree.node_sizes, "code_length") self["index", "sizes"] = sizes self["index", "dx"] = sizes[:, 0] self["index", "dy"] = sizes[:, 1] self["index", "dz"] = sizes[:, 2] self["index", "depth"] = self._octree.node_depth @property def tree(self): """ The Cython+Python octree instance """ if hasattr(self, "_octree"): return self._octree self._generate_tree() return self._octree @property def sph_smoothing_style(self): smoothing_style = getattr(self.ds, "sph_smoothing_style", "scatter") return smoothing_style @property def sph_normalize(self): normalize = getattr(self.ds, "use_sph_normalization", "normalize") return normalize @property def sph_num_neighbors(self): num_neighbors = getattr(self.ds, "num_neighbors", 32) return num_neighbors def _sanitize_ptypes(self, ptypes): if ptypes is None: return ["all"] if not isinstance(ptypes, list): ptypes = [ptypes] for ptype in ptypes: if ptype not in self.ds.particle_types: mess = f"{ptype} not found. Particle type must " mess += "be in the dataset!" raise TypeError(mess) return ptypes def _setup_data_source(self): mylog.info( ( "Allocating octree with spatial range " "[%.4e, %.4e, %.4e] code_length to " "[%.4e, %.4e, %.4e] code_length" ), *self.left_edge.to("code_length").d, *self.right_edge.to("code_length").d, ) self._data_source = self.ds.region(self.center, self.left_edge, self.right_edge) def _sanitize_edge(self, edge, default): if edge is None: return default.copy() if not is_sequence(edge): edge = [edge] * len(self.ds.domain_left_edge) if len(edge) != len(self.ds.domain_left_edge): raise RuntimeError( "Length of edges must match the dimensionality of the dataset" ) if hasattr(edge, "units"): if edge.units.registry is self.ds.unit_registry: return edge edge_units = edge.units else: edge_units = "code_length" return self.ds.arr(edge, edge_units) def get_data(self, fields=None): if fields is None: return if hasattr(self.ds, "_sph_ptypes"): sph_ptypes = self.ds._sph_ptypes else: sph_ptypes = tuple( value for value in self.ds.particle_types_raw if value in ["PartType0", "Gas", "gas", "io"] ) if len(sph_ptypes) == 0: raise RuntimeError smoothing_style = self.sph_smoothing_style normalize = self.sph_normalize if fields[0] in sph_ptypes: units = self.ds._get_field_info(fields).units if smoothing_style == "scatter": self._scatter_smooth(fields, units, normalize) else: self._gather_smooth(fields, units, normalize) elif fields[0] == "index": return self[fields] else: raise NotImplementedError def _gather_smooth(self, fields, units, normalize): buff = np.zeros(self.tree.num_nodes, dtype="float64") # Again, attempt to load num_neighbors from the octree num_neighbors = self.sph_num_neighbors # For the gather approach we load up all of the data, this like other # gather approaches is not memory conservative but with spatial chunking # this can be fixed fields_to_get = [ "particle_position", "density", "particle_mass", "smoothing_length", fields[1], ] all_fields = all_data(self.ds, fields[0], fields_to_get, kdtree=True) interpolate_sph_positions_gather( buff, all_fields["particle_position"], self["index", "positions"], all_fields["smoothing_length"], all_fields["particle_mass"], all_fields["density"], all_fields[fields[1]].in_units(units), self.ds.index.kdtree, use_normalization=normalize, num_neigh=num_neighbors, ) self[fields] = self.ds.arr(buff[~self["index", "refined"]], units) def _scatter_smooth(self, fields, units, normalize): from tqdm import tqdm buff = np.zeros(self.tree.num_nodes, dtype="float64") if normalize: buff_den = np.zeros(buff.shape[0], dtype="float64") else: buff_den = np.empty(0) ptype = fields[0] pbar = tqdm(desc=f"Interpolating (scatter) SPH field {fields[0]}") for chunk in self._data_source.chunks([fields], "io"): px = chunk[ptype, "particle_position_x"].to("code_length").d py = chunk[ptype, "particle_position_y"].to("code_length").d pz = chunk[ptype, "particle_position_z"].to("code_length").d hsml = chunk[ptype, "smoothing_length"].to("code_length").d pmass = chunk[ptype, "particle_mass"].to("code_mass").d pdens = chunk[ptype, "density"].to("code_mass/code_length**3").d field_quantity = chunk[fields].to(units).d if px.shape[0] > 0: self.tree.interpolate_sph_cells( buff, buff_den, px, py, pz, pmass, pdens, hsml, field_quantity, use_normalization=normalize, ) pbar.update(1) pbar.close() if normalize: normalization_1d_utility(buff, buff_den) self[fields] = self.ds.arr(buff[~self["index", "refined"]], units) yt-project-yt-f043ac8/yt/data_objects/data_containers.py000066400000000000000000001657651510711153200234640ustar00rootroot00000000000000import abc import weakref from collections import defaultdict from contextlib import contextmanager from typing import TYPE_CHECKING, Optional import numpy as np from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import AnyFieldKey, FieldKey, FieldName from yt.config import ytcfg from yt.data_objects.field_data import YTFieldData from yt.data_objects.profiles import create_profile from yt.fields.field_exceptions import NeedsGridType from yt.frontends.ytdata.utilities import save_as_dataset from yt.funcs import get_output_filename, iter_fields, mylog, parse_center_array from yt.units._numpy_wrapper_functions import uconcatenate from yt.utilities.amr_kdtree.api import AMRKDTree from yt.utilities.exceptions import ( YTCouldNotGenerateField, YTException, YTFieldNotFound, YTFieldTypeNotFound, YTNonIndexedDataContainer, YTSpatialFieldUnitError, ) from yt.utilities.object_registries import data_object_registry from yt.utilities.on_demand_imports import _firefly as firefly from yt.utilities.parameter_file_storage import ParameterFileStore if TYPE_CHECKING: from yt.data_objects.static_output import Dataset def sanitize_weight_field(ds, field, weight): if weight is None: field_object = ds._get_field_info(field) if field_object.sampling_type == "particle": if field_object.name[0] == "gas": ptype = ds._sph_ptypes[0] else: ptype = field_object.name[0] weight_field = (ptype, "particle_ones") else: weight_field = ("index", "ones") else: weight_field = weight return weight_field def _get_ipython_key_completion(ds): # tuple-completion (ftype, fname) was added in IPython 8.0.0 # with earlier versions, completion works with fname only # this implementation should work transparently with all IPython versions tuple_keys = ds.field_list + ds.derived_field_list fnames = list({k[1] for k in tuple_keys}) return tuple_keys + fnames class YTDataContainer(abc.ABC): """ Generic YTDataContainer container. By itself, will attempt to generate field, read fields (method defined by derived classes) and deal with passing back and forth field parameters. """ _chunk_info = None _num_ghost_zones = 0 _con_args: tuple[str, ...] = () _skip_add = False _container_fields: tuple[AnyFieldKey, ...] = () _tds_attrs: tuple[str, ...] = () _tds_fields: tuple[str, ...] = () _field_cache = None _index = None _key_fields: list[str] def __init__(self, ds: Optional["Dataset"], field_parameters) -> None: """ Typically this is never called directly, but only due to inheritance. It associates a :class:`~yt.data_objects.static_output.Dataset` with the class, sets its initial set of fields, and the remainder of the arguments are passed as field_parameters. """ # ds is typically set in the new object type created in # Dataset._add_object_class but it can also be passed as a parameter to the # constructor, in which case it will override the default. # This code ensures it is never not set. self.ds: Dataset if ds is not None: self.ds = ds else: if not hasattr(self, "ds"): raise RuntimeError( "Error: ds must be set either through class type " "or parameter to the constructor" ) self._current_particle_type = "all" self._current_fluid_type = self.ds.default_fluid_type self.ds.objects.append(weakref.proxy(self)) mylog.debug("Appending object to %s (type: %s)", self.ds, type(self)) self.field_data = YTFieldData() if self.ds.unit_system.has_current_mks: mag_unit = "T" else: mag_unit = "G" self._default_field_parameters = { "center": self.ds.arr(np.zeros(3, dtype="float64"), "cm"), "bulk_velocity": self.ds.arr(np.zeros(3, dtype="float64"), "cm/s"), "bulk_magnetic_field": self.ds.arr(np.zeros(3, dtype="float64"), mag_unit), "normal": self.ds.arr([0.0, 0.0, 1.0], ""), } if field_parameters is None: field_parameters = {} self._set_default_field_parameters() for key, val in field_parameters.items(): self.set_field_parameter(key, val) def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) if hasattr(cls, "_type_name") and not cls._skip_add: name = getattr(cls, "_override_selector_name", cls._type_name) data_object_registry[name] = cls @property def pf(self): return getattr(self, "ds", None) @property def index(self): if self._index is not None: return self._index self._index = self.ds.index return self._index def _debug(self): """ When called from within a derived field, this will run pdb. However, during field detection, it will not. This allows you to more easily debug fields that are being called on actual objects. """ import pdb pdb.set_trace() def _set_default_field_parameters(self): self.field_parameters = {} for k, v in self._default_field_parameters.items(): self.set_field_parameter(k, v) def _is_default_field_parameter(self, parameter): if parameter not in self._default_field_parameters: return False return ( self._default_field_parameters[parameter] is self.field_parameters[parameter] ) def apply_units(self, arr, units): try: arr.units.registry = self.ds.unit_registry return arr.to(units) except AttributeError: return self.ds.arr(arr, units=units) def _first_matching_field(self, field: FieldName) -> FieldKey: for ftype, fname in self.ds.derived_field_list: if fname == field: return (ftype, fname) raise YTFieldNotFound(field, self.ds) def _set_center(self, center): if center is None: self.center = None return else: axis = getattr(self, "axis", None) self.center = parse_center_array(center, ds=self.ds, axis=axis) self.set_field_parameter("center", self.center) def get_field_parameter(self, name, default=None): """ This is typically only used by derived field functions, but it returns parameters used to generate fields. """ if name in self.field_parameters: return self.field_parameters[name] else: return default def set_field_parameter(self, name, val): """ Here we set up dictionaries that get passed up and down and ultimately to derived fields. """ self.field_parameters[name] = val def has_field_parameter(self, name): """ Checks if a field parameter is set. """ return name in self.field_parameters def clear_data(self, fields: list[AnyFieldKey] | AnyFieldKey | None = None): """ Clears out data from the YTDataContainer instance, freeing memory. Parameters ---------- fields : list[str] | str | None The fields to clear. If None, all fields are cleared. """ if fields is None: self.field_data.clear() return if isinstance(fields, (str, tuple)): fields = [fields] for field in fields: self.field_data.pop(field, None) def has_key(self, key): """ Checks if a data field already exists. """ return key in self.field_data def keys(self): return self.field_data.keys() def _reshape_vals(self, arr): return arr def __getitem__(self, key): """ Returns a single field. Will add if necessary. """ f = self._determine_fields([key])[0] if f not in self.field_data and key not in self.field_data: if f in self._container_fields: self.field_data[f] = self.ds.arr(self._generate_container_field(f)) return self.field_data[f] else: self.get_data(f) # fi.units is the unit expression string. We depend on the registry # hanging off the dataset to define this unit object. # Note that this is less succinct so that we can account for the case # when there are, for example, no elements in the object. try: rv = self.field_data[f] except KeyError: fi = self.ds._get_field_info(f) rv = self.ds.arr(self.field_data[key], fi.units) return rv def _ipython_key_completions_(self): return _get_ipython_key_completion(self.ds) def __setitem__(self, key, val): """ Sets a field to be some other value. """ self.field_data[key] = val def __delitem__(self, key): """ Deletes a field """ if key not in self.field_data: key = self._determine_fields(key)[0] del self.field_data[key] def _generate_field(self, field): ftype, fname = field finfo = self.ds._get_field_info(field) with self._field_type_state(ftype, finfo): if fname in self._container_fields: tr = self._generate_container_field(field) if finfo.sampling_type == "particle": tr = self._generate_particle_field(field) else: tr = self._generate_fluid_field(field) if tr is None: raise YTCouldNotGenerateField(field, self.ds) return tr def _generate_fluid_field(self, field): # First we check the validator finfo = self.ds._get_field_info(field) if self._current_chunk is None or self._current_chunk.chunk_type != "spatial": gen_obj = self else: gen_obj = self._current_chunk.objs[0] gen_obj.field_parameters = self.field_parameters try: finfo.check_available(gen_obj) except NeedsGridType as ngt_exception: rv = self._generate_spatial_fluid(field, ngt_exception.ghost_zones) else: rv = finfo(gen_obj) return rv def _generate_spatial_fluid(self, field, ngz): finfo = self.ds._get_field_info(field) if finfo.units is None: raise YTSpatialFieldUnitError(field) units = finfo.units try: rv = self.ds.arr(np.zeros(self.ires.size, dtype="float64"), units) accumulate = False except YTNonIndexedDataContainer: # In this case, we'll generate many tiny arrays of unknown size and # then concatenate them. outputs = [] accumulate = True ind = 0 if ngz == 0: deps = self._identify_dependencies([field], spatial=True) deps = self._determine_fields(deps) for _io_chunk in self.chunks([], "io", cache=False): for _chunk in self.chunks([], "spatial", ngz=0, preload_fields=deps): o = self._current_chunk.objs[0] if accumulate: rv = self.ds.arr(np.empty(o.ires.size, dtype="float64"), units) outputs.append(rv) ind = 0 # Does this work with mesh? with o._activate_cache(): ind += o.select( self.selector, source=self[field], dest=rv, offset=ind ) else: chunks = self.index._chunk(self, "spatial", ngz=ngz) for chunk in chunks: with self._chunked_read(chunk): gz = self._current_chunk.objs[0] gz.field_parameters = self.field_parameters wogz = gz._base_grid if accumulate: rv = self.ds.arr( np.empty(wogz.ires.size, dtype="float64"), units ) outputs.append(rv) ind += wogz.select( self.selector, source=gz[field][ngz:-ngz, ngz:-ngz, ngz:-ngz], dest=rv, offset=ind, ) if accumulate: rv = uconcatenate(outputs) return rv def _generate_particle_field(self, field): # First we check the validator ftype, fname = field if self._current_chunk is None or self._current_chunk.chunk_type != "spatial": gen_obj = self else: gen_obj = self._current_chunk.objs[0] try: finfo = self.ds._get_field_info(field) finfo.check_available(gen_obj) except NeedsGridType as ngt_exception: if ngt_exception.ghost_zones != 0: raise NotImplementedError from ngt_exception size = self._count_particles(ftype) rv = self.ds.arr(np.empty(size, dtype="float64"), finfo.units) ind = 0 for _io_chunk in self.chunks([], "io", cache=False): for _chunk in self.chunks(field, "spatial"): x, y, z = (self[ftype, f"particle_position_{ax}"] for ax in "xyz") if x.size == 0: continue mask = self._current_chunk.objs[0].select_particles( self.selector, x, y, z ) if mask is None: continue # This requests it from the grid and does NOT mask it data = self[field][mask] rv[ind : ind + data.size] = data ind += data.size else: with self._field_type_state(ftype, finfo, gen_obj): rv = self.ds._get_field_info(field)(gen_obj) return rv def _count_particles(self, ftype): for (f1, _f2), val in self.field_data.items(): if f1 == ftype: return val.size size = 0 for _io_chunk in self.chunks([], "io", cache=False): for _chunk in self.chunks([], "spatial"): x, y, z = (self[ftype, f"particle_position_{ax}"] for ax in "xyz") if x.size == 0: continue size += self._current_chunk.objs[0].count_particles( self.selector, x, y, z ) return size def _generate_container_field(self, field): raise NotImplementedError def _parameter_iterate(self, seq): for obj in seq: old_fp = obj.field_parameters obj.field_parameters = self.field_parameters yield obj obj.field_parameters = old_fp def write_out(self, filename, fields=None, format="%0.16e"): """Write out the YTDataContainer object in a text file. This function will take a data object and produce a tab delimited text file containing the fields presently existing and the fields given in the ``fields`` list. Parameters ---------- filename : String The name of the file to write to. fields : List of string, Default = None If this is supplied, these fields will be added to the list of fields to be saved to disk. If not supplied, whatever fields presently exist will be used. format : String, Default = "%0.16e" Format of numbers to be written in the file. Raises ------ ValueError Raised when there is no existing field. YTException Raised when field_type of supplied fields is inconsistent with the field_type of existing fields. Examples -------- >>> ds = fake_particle_ds() >>> sp = ds.sphere(ds.domain_center, 0.25) >>> sp.write_out("sphere_1.txt") >>> sp.write_out("sphere_2.txt", fields=["cell_volume"]) """ if fields is None: fields = sorted(self.field_data.keys()) field_order = [("index", k) for k in self._key_fields] diff_fields = [field for field in fields if field not in field_order] field_order += diff_fields field_order = sorted(self._determine_fields(field_order)) field_shapes = defaultdict(list) for field in field_order: shape = self[field].shape field_shapes[shape].append(field) # Check all fields have the same shape if len(field_shapes) != 1: err_msg = ["Got fields with different number of elements:\n"] for shape, these_fields in field_shapes.items(): err_msg.append(f"\t {these_fields} with shape {shape}") raise YTException("\n".join(err_msg)) with open(filename, "w") as fid: field_header = [str(f) for f in field_order] fid.write("\t".join(["#"] + field_header + ["\n"])) field_data = np.array([self.field_data[field] for field in field_order]) for line in range(field_data.shape[1]): field_data[:, line].tofile(fid, sep="\t", format=format) fid.write("\n") def to_dataframe(self, fields): r"""Export a data object to a :class:`~pandas.DataFrame`. This function will take a data object and an optional list of fields and export them to a :class:`~pandas.DataFrame` object. If pandas is not importable, this will raise ImportError. Parameters ---------- fields : list of strings or tuple field names This is the list of fields to be exported into the DataFrame. Returns ------- df : :class:`~pandas.DataFrame` The data contained in the object. Examples -------- >>> dd = ds.all_data() >>> df = dd.to_dataframe([("gas", "density"), ("gas", "temperature")]) """ from yt.utilities.on_demand_imports import _pandas as pd data = {} fields = self._determine_fields(fields) for field in fields: data[field[-1]] = self[field] df = pd.DataFrame(data) return df def to_astropy_table(self, fields): """ Export region data to a :class:~astropy.table.table.QTable, which is a Table object which is unit-aware. The QTable can then be exported to an ASCII file, FITS file, etc. See the AstroPy Table docs for more details: http://docs.astropy.org/en/stable/table/ Parameters ---------- fields : list of strings or tuple field names This is the list of fields to be exported into the QTable. Examples -------- >>> sp = ds.sphere("c", (1.0, "Mpc")) >>> t = sp.to_astropy_table([("gas", "density"), ("gas", "temperature")]) """ from astropy.table import QTable t = QTable() fields = self._determine_fields(fields) for field in fields: t[field[-1]] = self[field].to_astropy() return t def save_as_dataset(self, filename=None, fields=None): r"""Export a data object to a reloadable yt dataset. This function will take a data object and output a dataset containing either the fields presently existing or fields given in the ``fields`` list. The resulting dataset can be reloaded as a yt dataset. Parameters ---------- filename : str, optional The name of the file to be written. If None, the name will be a combination of the original dataset and the type of data container. fields : list of string or tuple field names, optional If this is supplied, it is the list of fields to be saved to disk. If not supplied, all the fields that have been queried will be saved. Returns ------- filename : str The name of the file that has been created. Examples -------- >>> import yt >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") >>> sp = ds.sphere(ds.domain_center, (10, "Mpc")) >>> fn = sp.save_as_dataset(fields=[("gas", "density"), ("gas", "temperature")]) >>> sphere_ds = yt.load(fn) >>> # the original data container is available as the data attribute >>> print(sds.data["gas", "density"]) [ 4.46237613e-32 4.86830178e-32 4.46335118e-32 ..., 6.43956165e-30 3.57339907e-30 2.83150720e-30] g/cm**3 >>> ad = sphere_ds.all_data() >>> print(ad["gas", "temperature"]) [ 1.00000000e+00 1.00000000e+00 1.00000000e+00 ..., 4.40108359e+04 4.54380547e+04 4.72560117e+04] K """ keyword = f"{str(self.ds)}_{self._type_name}" filename = get_output_filename(filename, keyword, ".h5") data = {} if fields is not None: for f in self._determine_fields(fields): data[f] = self[f] else: data.update(self.field_data) # get the extra fields needed to reconstruct the container tds_fields = tuple(("index", t) for t in self._tds_fields) for f in [f for f in self._container_fields + tds_fields if f not in data]: data[f] = self[f] data_fields = list(data.keys()) need_grid_positions = False need_particle_positions = False ptypes = [] ftypes = {} for field in data_fields: if field in self._container_fields: ftypes[field] = "grid" need_grid_positions = True elif self.ds.field_info[field].sampling_type == "particle": if field[0] not in ptypes: ptypes.append(field[0]) ftypes[field] = field[0] need_particle_positions = True else: ftypes[field] = "grid" need_grid_positions = True # projections and slices use px and py, so don't need positions if self._type_name in ["cutting", "proj", "slice", "quad_proj"]: need_grid_positions = False if need_particle_positions: for ax in self.ds.coordinates.axis_order: for ptype in ptypes: p_field = (ptype, f"particle_position_{ax}") if p_field in self.ds.field_info and p_field not in data: data_fields.append(field) ftypes[p_field] = p_field[0] data[p_field] = self[p_field] if need_grid_positions: for ax in self.ds.coordinates.axis_order: g_field = ("index", ax) if g_field in self.ds.field_info and g_field not in data: data_fields.append(g_field) ftypes[g_field] = "grid" data[g_field] = self[g_field] g_field = ("index", "d" + ax) if g_field in self.ds.field_info and g_field not in data: data_fields.append(g_field) ftypes[g_field] = "grid" data[g_field] = self[g_field] extra_attrs = { arg: getattr(self, arg, None) for arg in self._con_args + self._tds_attrs } extra_attrs["con_args"] = repr(self._con_args) extra_attrs["data_type"] = "yt_data_container" extra_attrs["container_type"] = self._type_name extra_attrs["dimensionality"] = self._dimensionality save_as_dataset( self.ds, filename, data, field_types=ftypes, extra_attrs=extra_attrs ) return filename def to_glue(self, fields, label="yt", data_collection=None): """ Takes specific *fields* in the container and exports them to Glue (http://glueviz.org) for interactive analysis. Optionally add a *label*. If you are already within the Glue environment, you can pass a *data_collection* object, otherwise Glue will be started. """ from glue.core import Data, DataCollection if ytcfg.get("yt", "internals", "within_testing"): from glue.core.application_base import Application as GlueApplication else: try: from glue.app.qt.application import GlueApplication except ImportError: from glue.qt.glue_application import GlueApplication gdata = Data(label=label) for component_name in fields: gdata.add_component(self[component_name], component_name) if data_collection is None: dc = DataCollection([gdata]) app = GlueApplication(dc) try: app.start() except AttributeError: # In testing we're using a dummy glue application object # that doesn't have a start method pass else: data_collection.append(gdata) def create_firefly_object( self, datadir=None, fields_to_include=None, fields_units=None, default_decimation_factor=100, velocity_units="km/s", coordinate_units="kpc", show_unused_fields=0, *, JSONdir=None, match_any_particle_types=True, **kwargs, ): r"""This function links a region of data stored in a yt dataset to the Python frontend API for [Firefly](http://github.com/ageller/Firefly), a browser-based particle visualization tool. Parameters ---------- datadir : string Path to where any `.json` files should be saved. If a relative path will assume relative to `${HOME}`. A value of `None` will default to `${HOME}/Data`. fields_to_include : array_like of strings or field tuples A list of fields that you want to include in your Firefly visualization for on-the-fly filtering and colormapping. default_decimation_factor : integer The factor by which you want to decimate each particle group by (e.g. if there are 1e7 total particles in your simulation you might want to set this to 100 at first). Randomly samples your data like `shuffled_data[::decimation_factor]` so as to not overtax a system. This is adjustable on a per particle group basis by changing the returned reader's `reader.particleGroup[i].decimation_factor` before calling `reader.writeToDisk()`. velocity_units : string The units that the velocity should be converted to in order to show streamlines in Firefly. Defaults to km/s. coordinate_units : string The units that the coordinates should be converted to. Defaults to kpc. show_unused_fields : boolean A flag to optionally print the fields that are available, in the dataset but were not explicitly requested to be tracked. match_any_particle_types : boolean If True, when any of the fields_to_include match multiple particle groups then the field will be added for all matching particle groups. If False, an error is raised when encountering an ambiguous field. Default is True. Any additional keyword arguments are passed to firefly.data_reader.Reader.__init__ Returns ------- reader : Firefly.data_reader.Reader object A reader object from the Firefly, configured to output the current region selected Examples -------- >>> ramses_ds = yt.load( ... "/Users/agurvich/Desktop/yt_workshop/" ... + "DICEGalaxyDisk_nonCosmological/output_00002/info_00002.txt" ... ) >>> region = ramses_ds.sphere(ramses_ds.domain_center, (1000, "kpc")) >>> reader = region.create_firefly_object( ... "IsoGalaxyRamses", ... fields_to_include=[ ... "particle_extra_field_1", ... "particle_extra_field_2", ... ], ... fields_units=["dimensionless", "dimensionless"], ... ) >>> reader.settings["color"]["io"] = [1, 1, 0, 1] >>> reader.particleGroups[0].decimation_factor = 100 >>> reader.writeToDisk() """ ## handle default arguments if fields_to_include is None: fields_to_include = [] if fields_units is None: fields_units = [] ## handle input validation, if any if len(fields_units) != len(fields_to_include): raise RuntimeError("Each requested field must have units.") ## for safety, in case someone passes a float just cast it default_decimation_factor = int(default_decimation_factor) if JSONdir is not None: issue_deprecation_warning( "The 'JSONdir' keyword argument is a deprecated alias for 'datadir'." "Please use 'datadir' directly.", stacklevel=3, since="4.1", ) datadir = JSONdir ## initialize a firefly reader instance reader = firefly.data_reader.Reader( datadir=datadir, clean_datadir=True, **kwargs ) ## Ensure at least one field type contains every field requested if match_any_particle_types: # Need to keep previous behavior: single string field names that # are ambiguous should bring in any matching ParticleGroups instead # of raising an error # This can be expanded/changed in the future to include field # tuples containing some sort of special "any" ParticleGroup unambiguous_fields_to_include = [] unambiguous_fields_units = [] for field, field_unit in zip(fields_to_include, fields_units, strict=True): if isinstance(field, tuple): # skip tuples, they'll be checked with _determine_fields unambiguous_fields_to_include.append(field) unambiguous_fields_units.append(field_unit) continue _, candidates = self.ds._get_field_info_helper(field) if len(candidates) == 1: # Field is unambiguous, add in tuple form # This should be equivalent to _tupleize_field unambiguous_fields_to_include.append(candidates[0]) unambiguous_fields_units.append(field_unit) else: # Field has multiple candidates, add all of them instead # of original field. Note this may bring in aliases and # equivalent particle fields for c in candidates: unambiguous_fields_to_include.append(c) unambiguous_fields_units.append(field_unit) fields_to_include = unambiguous_fields_to_include fields_units = unambiguous_fields_units # error if any requested field is unknown or (still) ambiguous # This is also sufficient if match_any_particle_types=False fields_to_include = self._determine_fields(fields_to_include) ## Also generate equivalent of particle_fields_by_type including ## derived fields kysd = defaultdict(list) for k, v in self.ds.derived_field_list: kysd[k].append(v) ## create a ParticleGroup object that contains *every* field for ptype in sorted(self.ds.particle_types_raw): ## skip this particle type if it has no particles in this dataset if self[ptype, "relative_particle_position"].shape[0] == 0: continue ## loop through the fields and print them to the screen if show_unused_fields: ## read the available extra fields from yt this_ptype_fields = self.ds.particle_fields_by_type[ptype] ## load the extra fields and print them for field in this_ptype_fields: if field not in fields_to_include: mylog.warning( "detected (but did not request) %s %s", ptype, field ) field_arrays = [] field_names = [] ## explicitly go after the fields we want for field, units in zip(fields_to_include, fields_units, strict=True): ## Only interested in fields with the current particle type, ## whether that means general fields or field tuples ftype, fname = field if ftype not in (ptype, "all"): continue ## determine if you want to take the log of the field for Firefly log_flag = "log(" in units ## read the field array from the dataset this_field_array = self[ptype, fname] ## fix the units string and prepend 'log' to the field for ## the UI name if log_flag: units = units[len("log(") : -1] fname = f"log{fname}" ## perform the unit conversion and take the log if ## necessary. this_field_array.in_units(units) if log_flag: this_field_array = np.log10(this_field_array) ## add this array to the tracked arrays field_arrays += [this_field_array] field_names = np.append(field_names, [fname], axis=0) ## flag whether we want to filter and/or color by these fields ## we'll assume yes for both cases, this can be changed after ## the reader object is returned to the user. field_filter_flags = np.ones(len(field_names)) field_colormap_flags = np.ones(len(field_names)) ## field_* needs to be explicitly set None if empty ## so that Firefly will correctly compute the binary ## headers if len(field_arrays) == 0: if len(fields_to_include) > 0: mylog.warning("No additional fields specified for %s", ptype) field_arrays = None field_names = None field_filter_flags = None field_colormap_flags = None ## Check if particles have velocities if "relative_particle_velocity" in kysd[ptype]: velocities = self[ptype, "relative_particle_velocity"].in_units( velocity_units ) else: velocities = None ## create a firefly ParticleGroup for this particle type pg = firefly.data_reader.ParticleGroup( UIname=ptype, coordinates=self[ptype, "relative_particle_position"].in_units( coordinate_units ), velocities=velocities, field_arrays=field_arrays, field_names=field_names, field_filter_flags=field_filter_flags, field_colormap_flags=field_colormap_flags, decimation_factor=default_decimation_factor, ) ## bind this particle group to the firefly reader object reader.addParticleGroup(pg) return reader # Numpy-like Operations def argmax(self, field, axis=None): r"""Return the values at which the field is maximized. This will, in a parallel-aware fashion, find the maximum value and then return to you the values at that maximum location that are requested for "axis". By default it will return the spatial positions (in the natural coordinate system), but it can be any field Parameters ---------- field : string or tuple field name The field to maximize. axis : string or list of strings, optional If supplied, the fields to sample along; if not supplied, defaults to the coordinate fields. This can be the name of the coordinate fields (i.e., 'x', 'y', 'z') or a list of fields, but cannot be 0, 1, 2. Returns ------- A list of YTQuantities as specified by the axis argument. Examples -------- >>> temp_at_max_rho = reg.argmax( ... ("gas", "density"), axis=("gas", "temperature") ... ) >>> max_rho_xyz = reg.argmax(("gas", "density")) >>> t_mrho, v_mrho = reg.argmax( ... ("gas", "density"), ... axis=[("gas", "temperature"), ("gas", "velocity_magnitude")], ... ) >>> x, y, z = reg.argmax(("gas", "density")) """ if axis is None: mv, pos0, pos1, pos2 = self.quantities.max_location(field) return pos0, pos1, pos2 if isinstance(axis, str): axis = [axis] rv = self.quantities.sample_at_max_field_values(field, axis) if len(rv) == 2: return rv[1] return rv[1:] def argmin(self, field, axis=None): r"""Return the values at which the field is minimized. This will, in a parallel-aware fashion, find the minimum value and then return to you the values at that minimum location that are requested for "axis". By default it will return the spatial positions (in the natural coordinate system), but it can be any field Parameters ---------- field : string or tuple field name The field to minimize. axis : string or list of strings, optional If supplied, the fields to sample along; if not supplied, defaults to the coordinate fields. This can be the name of the coordinate fields (i.e., 'x', 'y', 'z') or a list of fields, but cannot be 0, 1, 2. Returns ------- A list of YTQuantities as specified by the axis argument. Examples -------- >>> temp_at_min_rho = reg.argmin( ... ("gas", "density"), axis=("gas", "temperature") ... ) >>> min_rho_xyz = reg.argmin(("gas", "density")) >>> t_mrho, v_mrho = reg.argmin( ... ("gas", "density"), ... axis=[("gas", "temperature"), ("gas", "velocity_magnitude")], ... ) >>> x, y, z = reg.argmin(("gas", "density")) """ if axis is None: mv, pos0, pos1, pos2 = self.quantities.min_location(field) return pos0, pos1, pos2 if isinstance(axis, str): axis = [axis] rv = self.quantities.sample_at_min_field_values(field, axis) if len(rv) == 2: return rv[1] return rv[1:] def _compute_extrema(self, field): if self._extrema_cache is None: self._extrema_cache = {} if field not in self._extrema_cache: # Note we still need to call extrema for each field, as of right # now mi, ma = self.quantities.extrema(field) self._extrema_cache[field] = (mi, ma) return self._extrema_cache[field] _extrema_cache = None def max(self, field, axis=None): r"""Compute the maximum of a field, optionally along an axis. This will, in a parallel-aware fashion, compute the maximum of the given field. Supplying an axis will result in a return value of a YTProjection, with method 'max' for maximum intensity. If the max has already been requested, it will use the cached extrema value. Parameters ---------- field : string or tuple field name The field to maximize. axis : string, optional If supplied, the axis to project the maximum along. Returns ------- Either a scalar or a YTProjection. Examples -------- >>> max_temp = reg.max(("gas", "temperature")) >>> max_temp_proj = reg.max(("gas", "temperature"), axis=("index", "x")) """ if axis is None: rv = tuple(self._compute_extrema(f)[1] for f in iter_fields(field)) if len(rv) == 1: return rv[0] return rv elif axis in self.ds.coordinates.axis_name: return self.ds.proj(field, axis, data_source=self, method="max") else: raise NotImplementedError(f"Unknown axis {axis}") def min(self, field, axis=None): r"""Compute the minimum of a field. This will, in a parallel-aware fashion, compute the minimum of the given field. Supplying an axis will result in a return value of a YTProjection, with method 'min' for minimum intensity. If the min has already been requested, it will use the cached extrema value. Parameters ---------- field : string or tuple field name The field to minimize. axis : string, optional If supplied, the axis to compute the minimum along. Returns ------- Either a scalar or a YTProjection. Examples -------- >>> min_temp = reg.min(("gas", "temperature")) >>> min_temp_proj = reg.min(("gas", "temperature"), axis=("index", "x")) """ if axis is None: rv = tuple(self._compute_extrema(f)[0] for f in iter_fields(field)) if len(rv) == 1: return rv[0] return rv elif axis in self.ds.coordinates.axis_name: return self.ds.proj(field, axis, data_source=self, method="min") else: raise NotImplementedError(f"Unknown axis {axis}") def std(self, field, axis=None, weight=None): """Compute the standard deviation of a field, optionally along an axis, with a weight. This will, in a parallel-ware fashion, compute the standard deviation of the given field. If an axis is supplied, it will return a projection, where the weight is also supplied. By default the weight field will be "ones" or "particle_ones", depending on the field, resulting in an unweighted standard deviation. Parameters ---------- field : string or tuple field name The field to calculate the standard deviation of axis : string, optional If supplied, the axis to compute the standard deviation along (i.e., to project along) weight : string, optional The field to use as a weight. Returns ------- Scalar or YTProjection. """ weight_field = sanitize_weight_field(self.ds, field, weight) if axis in self.ds.coordinates.axis_name: r = self.ds.proj( field, axis, data_source=self, weight_field=weight_field, moment=2 ) elif axis is None: r = self.quantities.weighted_standard_deviation(field, weight_field)[0] else: raise NotImplementedError(f"Unknown axis {axis}") return r def ptp(self, field): r"""Compute the range of values (maximum - minimum) of a field. This will, in a parallel-aware fashion, compute the "peak-to-peak" of the given field. Parameters ---------- field : string or tuple field name The field to average. Returns ------- Scalar Examples -------- >>> rho_range = reg.ptp(("gas", "density")) """ ex = self._compute_extrema(field) return ex[1] - ex[0] def profile( self, bin_fields, fields, n_bins=64, extrema=None, logs=None, units=None, weight_field=("gas", "mass"), accumulation=False, fractional=False, deposition="ngp", *, override_bins=None, ): r""" Create a 1, 2, or 3D profile object from this data_source. The dimensionality of the profile object is chosen by the number of fields given in the bin_fields argument. This simply calls :func:`yt.data_objects.profiles.create_profile`. Parameters ---------- bin_fields : list of strings List of the binning fields for profiling. fields : list of strings The fields to be profiled. n_bins : int or list of ints The number of bins in each dimension. If None, 64 bins for each bin are used for each bin field. Default: 64. extrema : dict of min, max tuples Minimum and maximum values of the bin_fields for the profiles. The keys correspond to the field names. Defaults to the extrema of the bin_fields of the dataset. If a units dict is provided, extrema are understood to be in the units specified in the dictionary. logs : dict of boolean values Whether or not to log the bin_fields for the profiles. The keys correspond to the field names. Defaults to the take_log attribute of the field. units : dict of strings The units of the fields in the profiles, including the bin_fields. weight_field : str or tuple field identifier The weight field for computing weighted average for the profile values. If None, the profile values are sums of the data in each bin. accumulation : bool or list of bools If True, the profile values for a bin n are the cumulative sum of all the values from bin 0 to n. If -True, the sum is reversed so that the value for bin n is the cumulative sum from bin N (total bins) to n. If the profile is 2D or 3D, a list of values can be given to control the summation in each dimension independently. Default: False. fractional : If True the profile values are divided by the sum of all the profile data such that the profile represents a probability distribution function. deposition : Controls the type of deposition used for ParticlePhasePlots. Valid choices are 'ngp' and 'cic'. Default is 'ngp'. This parameter is ignored if the input fields are not of particle type. override_bins : dict of bins to profile plot with If set, ignores n_bins and extrema settings and uses the supplied bins to profile the field. If a units dict is provided, bins are understood to be in the units specified in the dictionary. Examples -------- Create a 1d profile. Access bin field from profile.x and field data from profile[]. >>> ds = load("DD0046/DD0046") >>> ad = ds.all_data() >>> profile = ad.profile( ... ad, ... [("gas", "density")], ... [("gas", "temperature"), ("gas", "velocity_x")], ... ) >>> print(profile.x) >>> print(profile["gas", "temperature"]) >>> plot = profile.plot() """ p = create_profile( self, bin_fields, fields, n_bins, extrema, logs, units, weight_field, accumulation, fractional, deposition, override_bins=override_bins, ) return p def mean(self, field, axis=None, weight=None): r"""Compute the mean of a field, optionally along an axis, with a weight. This will, in a parallel-aware fashion, compute the mean of the given field. If an axis is supplied, it will return a projection, where the weight is also supplied. By default the weight field will be "ones" or "particle_ones", depending on the field being averaged, resulting in an unweighted average. Parameters ---------- field : string or tuple field name The field to average. axis : string, optional If supplied, the axis to compute the mean along (i.e., to project along) weight : string, optional The field to use as a weight. Returns ------- Scalar or YTProjection. Examples -------- >>> avg_rho = reg.mean(("gas", "density"), weight="cell_volume") >>> rho_weighted_T = reg.mean( ... ("gas", "temperature"), axis=("index", "y"), weight=("gas", "density") ... ) """ weight_field = sanitize_weight_field(self.ds, field, weight) if axis in self.ds.coordinates.axis_name: r = self.ds.proj(field, axis, data_source=self, weight_field=weight_field) elif axis is None: r = self.quantities.weighted_average_quantity(field, weight_field) else: raise NotImplementedError(f"Unknown axis {axis}") return r def sum(self, field, axis=None): r"""Compute the sum of a field, optionally along an axis. This will, in a parallel-aware fashion, compute the sum of the given field. If an axis is specified, it will return a projection (using method type "sum", which does not take into account path length) along that axis. Parameters ---------- field : string or tuple field name The field to sum. axis : string, optional If supplied, the axis to sum along. Returns ------- Either a scalar or a YTProjection. Examples -------- >>> total_vol = reg.sum("cell_volume") >>> cell_count = reg.sum(("index", "ones"), axis=("index", "x")) """ # Because we're using ``sum`` to specifically mean a sum or a # projection with the method="sum", we do not utilize the ``mean`` # function. if axis in self.ds.coordinates.axis_name: with self._field_parameter_state({"axis": axis}): r = self.ds.proj(field, axis, data_source=self, method="sum") elif axis is None: r = self.quantities.total_quantity(field) else: raise NotImplementedError(f"Unknown axis {axis}") return r def integrate(self, field, weight=None, axis=None, *, moment=1): r"""Compute the integral (projection) of a field along an axis. This projects a field along an axis. Parameters ---------- field : string or tuple field name The field to project. weight : string or tuple field name The field to weight the projection by axis : string The axis to project along. moment : integer, optional for a weighted projection, moment = 1 (the default) corresponds to a weighted average. moment = 2 corresponds to a weighted standard deviation. Returns ------- YTProjection Examples -------- >>> column_density = reg.integrate(("gas", "density"), axis=("index", "z")) """ if weight is not None: weight_field = sanitize_weight_field(self.ds, field, weight) else: weight_field = None if axis in self.ds.coordinates.axis_name: r = self.ds.proj( field, axis, data_source=self, weight_field=weight_field, moment=moment ) else: raise NotImplementedError(f"Unknown axis {axis}") return r @property def _hash(self): s = f"{self}" try: import hashlib return hashlib.md5(s.encode("utf-8")).hexdigest() except ImportError: return s def __reduce__(self): args = tuple( [self.ds._hash(), self._type_name] + [getattr(self, n) for n in self._con_args] + [self.field_parameters] ) return (_reconstruct_object, args) def clone(self): r"""Clone a data object. This will make a duplicate of a data object; note that the `field_parameters` may not necessarily be deeply-copied. If you modify the field parameters in-place, it may or may not be shared between the objects, depending on the type of object that that particular field parameter is. Notes ----- One use case for this is to have multiple identical data objects that are being chunked over in different orders. Examples -------- >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sp = ds.sphere("c", 0.1) >>> sp_clone = sp.clone() >>> sp["gas", "density"] >>> print(sp.field_data.keys()) [("gas", "density")] >>> print(sp_clone.field_data.keys()) [] """ args = self.__reduce__() return args[0](self.ds, *args[1][1:]) def __repr__(self): # We'll do this the slow way to be clear what's going on s = f"{self.__class__.__name__} ({self.ds}): " for i in self._con_args: try: s += ( f", {i}={getattr(self, i).in_base(unit_system=self.ds.unit_system)}" ) except AttributeError: s += f", {i}={getattr(self, i)}" return s @contextmanager def _field_parameter_state(self, field_parameters): # What we're doing here is making a copy of the incoming field # parameters, and then updating it with our own. This means that we'll # be using our own center, if set, rather than the supplied one. But # it also means that any additionally set values can override it. old_field_parameters = self.field_parameters new_field_parameters = field_parameters.copy() new_field_parameters.update(old_field_parameters) self.field_parameters = new_field_parameters yield self.field_parameters = old_field_parameters @contextmanager def _field_type_state(self, ftype, finfo, obj=None): if obj is None: obj = self old_particle_type = obj._current_particle_type old_fluid_type = obj._current_fluid_type fluid_types = self.ds.fluid_types if finfo.sampling_type == "particle" and ftype not in fluid_types: obj._current_particle_type = ftype else: obj._current_fluid_type = ftype yield obj._current_particle_type = old_particle_type obj._current_fluid_type = old_fluid_type def _determine_fields(self, fields): if str(fields) in self.ds._determined_fields: return self.ds._determined_fields[str(fields)] explicit_fields = [] for field in iter_fields(fields): if field in self._container_fields: explicit_fields.append(field) continue finfo = self.ds._get_field_info(field) ftype, fname = finfo.name # really ugly check to ensure that this field really does exist somewhere, # in some naming convention, before returning it as a possible field type if ( (ftype, fname) not in self.ds.field_info and (ftype, fname) not in self.ds.field_list and fname not in self.ds.field_list and (ftype, fname) not in self.ds.derived_field_list and fname not in self.ds.derived_field_list and (ftype, fname) not in self._container_fields ): raise YTFieldNotFound((ftype, fname), self.ds) # these tests are really insufficient as a field type may be valid, and the # field name may be valid, but not the combination (field type, field name) particle_field = finfo.sampling_type == "particle" local_field = finfo.local_sampling if local_field: pass elif particle_field and ftype not in self.ds.particle_types: raise YTFieldTypeNotFound(ftype, ds=self.ds) elif not particle_field and ftype not in self.ds.fluid_types: raise YTFieldTypeNotFound(ftype, ds=self.ds) explicit_fields.append((ftype, fname)) self.ds._determined_fields[str(fields)] = explicit_fields return explicit_fields _tree = None @property def tiles(self): if self._tree is not None: return self._tree self._tree = AMRKDTree(self.ds, data_source=self) return self._tree @property def blocks(self): for _io_chunk in self.chunks([], "io"): for _chunk in self.chunks([], "spatial", ngz=0): # For grids this will be a grid object, and for octrees it will # be an OctreeSubset. Note that we delegate to the sub-object. o = self._current_chunk.objs[0] cache_fp = o.field_parameters.copy() o.field_parameters.update(self.field_parameters) for b, m in o.select_blocks(self.selector): if m is None: continue yield b, m o.field_parameters = cache_fp # PR3124: Given that save_as_dataset is now the recommended method for saving # objects (see Issue 2021 and references therein), the following has been re-written. # # Original comments (still true): # # In the future, this would be better off being set up to more directly # reference objects or retain state, perhaps with a context manager. # # One final detail: time series or multiple datasets in a single pickle # seems problematic. def _get_ds_by_hash(hash): from yt.data_objects.static_output import Dataset if isinstance(hash, Dataset): return hash from yt.data_objects.static_output import _cached_datasets for ds in _cached_datasets.values(): if ds._hash() == hash: return ds return None def _reconstruct_object(*args, **kwargs): # returns a reconstructed YTDataContainer. As of PR 3124, we now return # the actual YTDataContainer rather than a (ds, YTDataContainer) tuple. # pull out some arguments dsid = args[0] # the hash id dtype = args[1] # DataContainer type (e.g., 'region') field_parameters = args[-1] # the field parameters # re-instantiate the base dataset from the hash and ParameterFileStore ds = _get_ds_by_hash(dsid) override_weakref = False if not ds: override_weakref = True datasets = ParameterFileStore() ds = datasets.get_ds_hash(dsid) # instantiate the class with remainder of the args and adjust the state cls = getattr(ds, dtype) obj = cls(*args[2:-1]) obj.field_parameters.update(field_parameters) # any nested ds references are weakref.proxy(ds), so need to ensure the ds # we just loaded persists when we leave this function (nosetests fail without # this) if we did not have an actual dataset as an argument. if hasattr(obj, "ds") and override_weakref: obj.ds = ds return obj yt-project-yt-f043ac8/yt/data_objects/derived_quantities.py000066400000000000000000000666341510711153200242110ustar00rootroot00000000000000import numpy as np from yt.funcs import camelcase_to_underscore, iter_fields from yt.units.yt_array import array_like_field from yt.utilities.exceptions import YTParticleTypeNotFound from yt.utilities.object_registries import derived_quantity_registry from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, parallel_objects, ) from yt.utilities.physical_constants import gravitational_constant_cgs from yt.utilities.physical_ratios import HUGE def get_position_fields(field, data): axis_names = [data.ds.coordinates.axis_name[num] for num in [0, 1, 2]] field = data._determine_fields(field)[0] finfo = data.ds.field_info[field] if finfo.sampling_type == "particle": if finfo.is_alias: ftype = finfo.alias_name[0] else: ftype = finfo.name[0] position_fields = [(ftype, f"particle_position_{d}") for d in axis_names] else: position_fields = [("index", ax_name) for ax_name in axis_names] return position_fields class DerivedQuantity(ParallelAnalysisInterface): num_vals = -1 def __init__(self, data_source): self.data_source = data_source def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) if cls.__name__ != "DerivedQuantity": derived_quantity_registry[cls.__name__] = cls def count_values(self, *args, **kwargs): return def __call__(self, *args, **kwargs): """Calculate results for the derived quantity""" # create the index if it doesn't exist yet self.data_source.ds.index self.count_values(*args, **kwargs) chunks = self.data_source.chunks( [], chunking_style=self.data_source._derived_quantity_chunking ) storage = {} for sto, ds in parallel_objects(chunks, -1, storage=storage): sto.result = self.process_chunk(ds, *args, **kwargs) # Now storage will have everything, and will be done via pickling, so # the units will be preserved. (Credit to Nathan for this # idea/implementation.) values = [[] for i in range(self.num_vals)] for key in sorted(storage): for i in range(self.num_vals): values[i].append(storage[key][i]) # These will be YTArrays values = [self.data_source.ds.arr(values[i]) for i in range(self.num_vals)] values = self.reduce_intermediate(values) return values def process_chunk(self, data, *args, **kwargs): raise NotImplementedError def reduce_intermediate(self, values): raise NotImplementedError class DerivedQuantityCollection: def __new__(cls, data_source, *args, **kwargs): inst = object.__new__(cls) inst.data_source = data_source for f in inst.keys(): setattr(inst, camelcase_to_underscore(f), inst[f]) return inst def __getitem__(self, key): dq = derived_quantity_registry[key] # Instantiate here, so we can pass it the data object # Note that this means we instantiate every time we run help, etc # I have made my peace with this. return dq(self.data_source) def keys(self): return derived_quantity_registry.keys() class WeightedAverageQuantity(DerivedQuantity): r""" Calculates the weight average of a field or fields. Returns a YTQuantity for each field requested; if one, it returns a single YTQuantity, if many, it returns a list of YTQuantities in order of the listed fields. Where f is the field and w is the weight, the weighted average is Sum_i(f_i \* w_i) / Sum_i(w_i). Parameters ---------- fields : string / tuple, or list of strings / tuples The field or fields of which the average value is to be calculated. weight : string or tuple The weight field. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print( ... ad.quantities.weighted_average_quantity( ... [("gas", "density"), ("gas", "temperature")], ("gas", "mass") ... ) ... ) """ def count_values(self, fields, weight): # This is a list now self.num_vals = len(fields) + 1 def __call__(self, fields, weight): fields = list(iter_fields(fields)) rv = super().__call__(fields, weight) if len(rv) == 1: rv = rv[0] return rv def process_chunk(self, data, fields, weight): vals = [(data[field] * data[weight]).sum(dtype=np.float64) for field in fields] wv = data[weight].sum(dtype=np.float64) return vals + [wv] def reduce_intermediate(self, values): w = values.pop(-1).sum(dtype=np.float64) return [v.sum(dtype=np.float64) / w for v in values] class TotalQuantity(DerivedQuantity): r""" Calculates the sum of the field or fields. Parameters ---------- fields The field or list of fields to be summed. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.total_quantity([("gas", "mass")])) """ def count_values(self, fields): # This is a list now self.num_vals = len(fields) def __call__(self, fields): fields = list(iter_fields(fields)) rv = super().__call__(fields) if len(rv) == 1: rv = rv[0] return rv def process_chunk(self, data, fields): vals = [data[field].sum(dtype=np.float64) for field in fields] return vals def reduce_intermediate(self, values): return [v.sum(dtype=np.float64) for v in values] class TotalMass(TotalQuantity): r""" Calculates the total mass of the object. Returns a YTArray where the first element is total gas mass and the second element is total particle mass. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.total_mass()) """ def __call__(self): self.data_source.ds.index fi = self.data_source.ds.field_info if ("gas", "mass") in fi: gas = super().__call__([("gas", "mass")]) else: gas = self.data_source.ds.quan(0.0, "g") if ("nbody", "particle_mass") in fi: part = super().__call__([("nbody", "particle_mass")]) else: part = self.data_source.ds.quan(0.0, "g") return self.data_source.ds.arr([gas, part]) class CenterOfMass(DerivedQuantity): r""" Calculates the center of mass, using gas and/or particles. The center of mass is the mass-weighted mean position. Parameters ---------- use_gas : bool Flag to include gas in the calculation. Gas is ignored if not present. Default: True use_particles : bool Flag to include particles in the calculation. Particles are ignored if not present. Default: False particle_type: string Flag to specify the field type of the particles to use. Useful for particle-based codes where you don't want to use all of the particles in your calculation. Default: 'all' Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.center_of_mass()) """ def count_values(self, use_gas=True, use_particles=False, particle_type="nbody"): finfo = self.data_source.ds.field_info includes_gas = ("gas", "mass") in finfo includes_particles = (particle_type, "particle_mass") in finfo self.use_gas = use_gas & includes_gas self.use_particles = use_particles & includes_particles self.num_vals = 0 if self.use_gas: self.num_vals += 4 if self.use_particles: self.num_vals += 4 def process_chunk( self, data, use_gas=True, use_particles=False, particle_type="nbody" ): vals = [] if self.use_gas: vals += [ (data["gas", ax] * data["gas", "mass"]).sum(dtype=np.float64) for ax in "xyz" ] vals.append(data["gas", "mass"].sum(dtype=np.float64)) if self.use_particles: vals += [ ( data[particle_type, f"particle_position_{ax}"] * data[particle_type, "particle_mass"] ).sum(dtype=np.float64) for ax in "xyz" ] vals.append(data[particle_type, "particle_mass"].sum(dtype=np.float64)) return vals def reduce_intermediate(self, values): if len(values) not in (4, 8): raise RuntimeError x = values.pop(0).sum(dtype=np.float64) y = values.pop(0).sum(dtype=np.float64) z = values.pop(0).sum(dtype=np.float64) w = values.pop(0).sum(dtype=np.float64) if len(values) > 0: # Note that this could be shorter if we pre-initialized our x,y,z,w # values as YTQuantity objects. x += values.pop(0).sum(dtype=np.float64) y += values.pop(0).sum(dtype=np.float64) z += values.pop(0).sum(dtype=np.float64) w += values.pop(0).sum(dtype=np.float64) return self.data_source.ds.arr([v / w for v in [x, y, z]]) class BulkVelocity(DerivedQuantity): r""" Calculates the bulk velocity, using gas and/or particles. The bulk velocity is the mass-weighted mean velocity. Parameters ---------- use_gas : bool Flag to include gas in the calculation. Gas is ignored if not present. Default: True use_particles : bool Flag to include particles in the calculation. Particles are ignored if not present. Default: True particle_type: string Flag to specify the field type of the particles to use. Useful for particle-based codes where you don't want to use all of the particles in your calculation. Default: 'all' Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.bulk_velocity()) """ def count_values(self, use_gas=True, use_particles=False, particle_type="nbody"): if use_particles and particle_type not in self.data_source.ds.particle_types: raise YTParticleTypeNotFound(particle_type, self.data_source.ds) # This is a list now self.num_vals = 0 if use_gas: self.num_vals += 4 if use_particles and "nbody" in self.data_source.ds.particle_types: self.num_vals += 4 def process_chunk( self, data, use_gas=True, use_particles=False, particle_type="nbody" ): vals = [] if use_gas: vals += [ (data["gas", f"velocity_{ax}"] * data["gas", "mass"]).sum( dtype=np.float64 ) for ax in "xyz" ] vals.append(data["gas", "mass"].sum(dtype=np.float64)) if use_particles and "nbody" in data.ds.particle_types: vals += [ ( data[particle_type, f"particle_velocity_{ax}"] * data[particle_type, "particle_mass"] ).sum(dtype=np.float64) for ax in "xyz" ] vals.append(data[particle_type, "particle_mass"].sum(dtype=np.float64)) return vals def reduce_intermediate(self, values): if len(values) not in (4, 8): raise RuntimeError x = values.pop(0).sum(dtype=np.float64) y = values.pop(0).sum(dtype=np.float64) z = values.pop(0).sum(dtype=np.float64) w = values.pop(0).sum(dtype=np.float64) if len(values) > 0: # Note that this could be shorter if we pre-initialized our x,y,z,w # values as YTQuantity objects. x += values.pop(0).sum(dtype=np.float64) y += values.pop(0).sum(dtype=np.float64) z += values.pop(0).sum(dtype=np.float64) w += values.pop(0).sum(dtype=np.float64) return self.data_source.ds.arr([v / w for v in [x, y, z]]) class WeightedStandardDeviation(DerivedQuantity): r""" Calculates the weighted standard deviation and weighted mean for a field or list of fields. Returns a YTArray for each field requested; if one, it returns a single YTArray, if many, it returns a list of YTArrays in order of the listed fields. The first element of each YTArray is the weighted standard deviation, and the second element is the weighted mean. Where f is the field, w is the weight, and is the weighted mean, the weighted standard deviation is sqrt( Sum_i( (f_i - )^2 \* w_i ) / Sum_i(w_i) ). Parameters ---------- fields : string / tuple, or list of strings / tuples The field or fields of which the average value is to be calculated. weight : string or tuple The weight field. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print( ... ad.quantities.weighted_standard_deviation( ... [("gas", "density"), ("gas", "temperature")], ("gas", "mass") ... ) ... ) """ def count_values(self, fields, weight): # This is a list now self.num_vals = 2 * len(fields) + 1 def __call__(self, fields, weight): fields = list(iter_fields(fields)) units = [self.data_source.ds._get_field_info(field).units for field in fields] rv = super().__call__(fields, weight) rv = [self.data_source.ds.arr(v, u) for v, u in zip(rv, units, strict=True)] if len(rv) == 1: rv = rv[0] return rv def process_chunk(self, data, fields, weight): my_weight = data[weight].d.sum(dtype=np.float64) if my_weight == 0: return [0.0 for field in fields] + [0.0 for field in fields] + [0.0] my_means = [ (data[field].d * data[weight].d).sum(dtype=np.float64) / my_weight for field in fields ] my_var2s = [ (data[weight].d * (data[field].d - my_mean) ** 2).sum(dtype=np.float64) / my_weight for field, my_mean in zip(fields, my_means, strict=True) ] return my_means + my_var2s + [my_weight] def reduce_intermediate(self, values): my_weight = values.pop(-1) all_weight = my_weight.sum(dtype=np.float64) rvals = [] for i in range(int(len(values) / 2)): my_mean = values[i] my_var2 = values[i + int(len(values) / 2)] all_mean = (my_weight * my_mean).sum(dtype=np.float64) / all_weight ret = [ ( np.sqrt( (my_weight * (my_var2 + (my_mean - all_mean) ** 2)).sum( dtype=np.float64 ) / all_weight ) ), all_mean, ] rvals.append(np.array(ret)) return rvals class AngularMomentumVector(DerivedQuantity): r""" Calculates the angular momentum vector, using gas (grid-based) and/or particles. The angular momentum vector is the mass-weighted mean specific angular momentum. Returns a YTArray of the vector. Parameters ---------- use_gas : bool Flag to include grid-based gas in the calculation. Gas is ignored if not present. Default: True use_particles : bool Flag to include particles in the calculation. Particles are ignored if not present. Default: True particle_type: string Flag to specify the field type of the particles to use. Useful for particle-based codes where you don't want to use all of the particles in your calculation. Default: 'all' Examples -------- Find angular momentum vector of galaxy in grid-based isolated galaxy dataset >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") ... ad = ds.all_data() ... print(ad.quantities.angular_momentum_vector()) [-7.50868209e+26 1.06032596e+27 2.19274002e+29] cm**2/s >>> # Find angular momentum vector of gas disk in particle-based dataset >>> ds = yt.load("FIRE_M12i_ref11/snapshot_600.hdf5") ... _, c = ds.find_max(("gas", "density")) ... sp = ds.sphere(c, (10, "kpc")) ... search_args = dict(use_gas=False, use_particles=True, particle_type="PartType0") ... print(sp.quantities.angular_momentum_vector(**search_args)) [4.88104442e+28 7.38463362e+28 6.20030135e+28] cm**2/s """ def count_values(self, use_gas=True, use_particles=True, particle_type="all"): if use_particles and particle_type not in self.data_source.ds.particle_types: raise YTParticleTypeNotFound(particle_type, self.data_source.ds) num_vals = 0 # create the index if it doesn't exist yet self.data_source.ds.index self.particle_type = particle_type self.use_gas = use_gas & (("gas", "mass") in self.data_source.ds.field_info) self.use_particles = use_particles & ( (self.particle_type, "particle_mass") in self.data_source.ds.field_info ) if self.use_gas: num_vals += 4 if self.use_particles: num_vals += 4 self.num_vals = num_vals def process_chunk( self, data, use_gas=True, use_particles=False, particle_type="all" ): rvals = [] if self.use_gas: rvals.extend( [ ( data["gas", f"specific_angular_momentum_{axis}"] * data["gas", "mass"] ).sum(dtype=np.float64) for axis in "xyz" ] ) rvals.append(data["gas", "mass"].sum(dtype=np.float64)) if self.use_particles: rvals.extend( [ ( data[ self.particle_type, f"particle_specific_angular_momentum_{axis}", ] * data[self.particle_type, "particle_mass"] ).sum(dtype=np.float64) for axis in "xyz" ] ) rvals.append( data[self.particle_type, "particle_mass"].sum(dtype=np.float64) ) return rvals def reduce_intermediate(self, values): jx = values.pop(0).sum(dtype=np.float64) jy = values.pop(0).sum(dtype=np.float64) jz = values.pop(0).sum(dtype=np.float64) m = values.pop(0).sum(dtype=np.float64) if values: jx += values.pop(0).sum(dtype=np.float64) jy += values.pop(0).sum(dtype=np.float64) jz += values.pop(0).sum(dtype=np.float64) m += values.pop(0).sum(dtype=np.float64) return self.data_source.ds.arr([jx / m, jy / m, jz / m]) class Extrema(DerivedQuantity): r""" Calculates the min and max value of a field or list of fields. Returns a YTArray for each field requested. If one, a single YTArray is returned, if many, a list of YTArrays in order of field list is returned. The first element of each YTArray is the minimum of the field and the second is the maximum of the field. Parameters ---------- fields The field or list of fields over which the extrema are to be calculated. non_zero : bool If True, only positive values are considered in the calculation. Default: False check_finite : bool If True, non-finite values will be explicitly excluded. Default: False Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.extrema([("gas", "density"), ("gas", "temperature")])) """ def count_values(self, fields, non_zero, *, check_finite=False): self.num_vals = len(fields) * 2 def __call__(self, fields, non_zero=False, *, check_finite=False): fields = list(iter_fields(fields)) rv = super().__call__(fields, non_zero, check_finite=check_finite) if len(rv) == 1: rv = rv[0] return rv def process_chunk(self, data, fields, non_zero, *, check_finite=False): vals = [] for field in fields: field = data._determine_fields(field)[0] fd = data[field] if non_zero: fd = fd[fd > 0.0] if check_finite: fd = fd[np.isfinite(fd)] if fd.size > 0: vals += [fd.min(), fd.max()] else: vals += [ array_like_field(data, HUGE, field), array_like_field(data, -HUGE, field), ] return vals def reduce_intermediate(self, values): # The values get turned into arrays here. return [ self.data_source.ds.arr([mis.min(), mas.max()]) for mis, mas in zip(values[::2], values[1::2], strict=True) ] class SampleAtMaxFieldValues(DerivedQuantity): _sign = -1 r""" Calculates the maximum value and returns whichever fields are asked to be sampled. Parameters ---------- field : tuple or string The field over which the extrema are to be calculated. sample_fields : list of fields The fields to sample and return at the minimum value. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.sample_at_max_field_values(("gas", "density"), ... [("gas", "temperature"), ("gas", "velocity_magnitude")])) """ def count_values(self, field, sample_fields): # field itself, then index, then the number of sample fields self.num_vals = 1 + len(sample_fields) def __call__(self, field, sample_fields): rv = super().__call__(field, sample_fields) if len(rv) == 1: rv = rv[0] return rv def process_chunk(self, data, field, sample_fields): field = data._determine_fields(field)[0] ma = array_like_field(data, self._sign * HUGE, field) vals = [array_like_field(data, -1, sf) for sf in sample_fields] maxi = -1 if data[field].size > 0: maxi = self._func(data[field]) ma = data[field][maxi] vals = [data[sf][maxi] for sf in sample_fields] return (ma,) + tuple(vals) def reduce_intermediate(self, values): i = self._func(values[0]) # ma is values[0] return [val[i] for val in values] def _func(self, arr): return np.argmax(arr) class MaxLocation(SampleAtMaxFieldValues): r""" Calculates the maximum value plus the x, y, and z position of the maximum. Parameters ---------- field : tuple or string The field over which the extrema are to be calculated. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.max_location(("gas", "density"))) """ def __call__(self, field): # Make sure we have an index self.data_source.index sample_fields = get_position_fields(field, self.data_source) rv = super().__call__(field, sample_fields) if len(rv) == 1: rv = rv[0] return rv class SampleAtMinFieldValues(SampleAtMaxFieldValues): _sign = 1 r""" Calculates the minimum value and returns whichever fields are asked to be sampled. Parameters ---------- field : tuple or string The field over which the extrema are to be calculated. sample_fields : list of fields The fields to sample and return at the minimum value. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.sample_at_min_field_values(("gas", "density"), ... [("gas", "temperature"), ("gas", "velocity_magnitude")])) """ def _func(self, arr): return np.argmin(arr) class MinLocation(SampleAtMinFieldValues): r""" Calculates the minimum value plus the x, y, and z position of the minimum. Parameters ---------- field : tuple or string The field over which the extrema are to be calculated. Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.min_location(("gas", "density"))) """ def __call__(self, field): # Make sure we have an index self.data_source.index sample_fields = get_position_fields(field, self.data_source) rv = super().__call__(field, sample_fields) if len(rv) == 1: rv = rv[0] return rv class SpinParameter(DerivedQuantity): r""" Calculates the dimensionless spin parameter. Given by Equation 3 of Peebles (1971, A&A, 11, 377), the spin parameter is defined as .. math:: \lambda = (L * |E|^(1/2)) / (G * M^5/2), where L is the total angular momentum, E is the total energy (kinetic and potential), G is the gravitational constant, and M is the total mass. Parameters ---------- use_gas : bool Flag to include gas in the calculation. Gas is ignored if not present. Default: True use_particles : bool Flag to include particles in the calculation. Particles are ignored if not present. Default: True particle_type : str Particle type to be used for Center of mass calculation when use_particle = True. Default: all Examples -------- >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> print(ad.quantities.spin_parameter()) """ def count_values(self, **kwargs): self.num_vals = 3 def process_chunk( self, data, use_gas=True, use_particles=True, particle_type="nbody" ): if use_particles and particle_type not in self.data_source.ds.particle_types: raise YTParticleTypeNotFound(particle_type, self.data_source.ds) use_gas &= ("gas", "mass") in self.data_source.ds.field_info use_particles &= ( particle_type, "particle_mass", ) in self.data_source.ds.field_info e = data.ds.quan(0.0, "erg") j = data.ds.quan(0.0, "g*cm**2/s") m = data.ds.quan(0.0, "g") if use_gas: e += (data["gas", "kinetic_energy_density"] * data["gas", "volume"]).sum( dtype=np.float64 ) j += data["gas", "angular_momentum_magnitude"].sum(dtype=np.float64) m += data["gas", "mass"].sum(dtype=np.float64) if use_particles: e += ( data[particle_type, "particle_velocity_magnitude"] ** 2 * data[particle_type, "particle_mass"] ).sum(dtype=np.float64) j += data[particle_type, "particle_angular_momentum_magnitude"].sum( dtype=np.float64 ) m += data[particle_type, "particle_mass"].sum(dtype=np.float64) return (e, j, m) def reduce_intermediate(self, values): e = values.pop(0).sum(dtype=np.float64) j = values.pop(0).sum(dtype=np.float64) m = values.pop(0).sum(dtype=np.float64) return j * np.sqrt(np.abs(e)) / m**2.5 / gravitational_constant_cgs yt-project-yt-f043ac8/yt/data_objects/field_data.py000066400000000000000000000001771510711153200223630ustar00rootroot00000000000000class YTFieldData(dict): """ A Container object for field data, instead of just having it be a dict. """ pass yt-project-yt-f043ac8/yt/data_objects/image_array.py000066400000000000000000000325741510711153200225750ustar00rootroot00000000000000import numpy as np from unyt import unyt_array from yt.config import ytcfg from yt.visualization.image_writer import write_bitmap, write_image class ImageArray(unyt_array): r"""A custom Numpy ndarray used for images. This differs from ndarray in that you can optionally specify an info dictionary which is used later in saving, and can be accessed with ImageArray.info. Parameters ---------- input_array: array_like A numpy ndarray, or list. Other Parameters ---------------- info: dictionary Contains information to be stored with image. Returns ------- obj: ImageArray object Raises ------ None See Also -------- numpy.ndarray : Inherits Notes ----- References ---------- Examples -------- These are written in doctest format, and should illustrate how to use the function. Use the variables 'ds' for the dataset, 'pc' for a plot collection, 'c' for a center, and 'L' for a vector. >>> im = np.zeros([64, 128, 3]) >>> for i in range(im.shape[0]): ... for k in range(im.shape[2]): ... im[i, :, k] = np.linspace(0.0, 0.3 * k, im.shape[1]) >>> myinfo = { ... "field": "dinosaurs", ... "east_vector": np.array([1.0, 0.0, 0.0]), ... "north_vector": np.array([0.0, 0.0, 1.0]), ... "normal_vector": np.array([0.0, 1.0, 0.0]), ... "width": 0.245, ... "units": "cm", ... "type": "rendering", ... } >>> im_arr = ImageArray(im, info=myinfo) >>> im_arr.save("test_ImageArray") Numpy ndarray documentation appended: """ def __new__( cls, input_array, units=None, registry=None, info=None, bypass_validation=False, ): obj = super().__new__( cls, input_array, units, registry, bypass_validation=bypass_validation ) if info is None: info = {} obj.info = info return obj def __array_finalize__(self, obj): # see InfoArray.__array_finalize__ for comments super().__array_finalize__(obj) self.info = getattr(obj, "info", None) def write_hdf5(self, filename, dataset_name=None): r"""Writes ImageArray to hdf5 file. Parameters ---------- filename : string The filename to create and write a dataset to dataset_name : string The name of the dataset to create in the file. Examples -------- >>> im = np.zeros([64, 128, 3]) >>> for i in range(im.shape[0]): ... for k in range(im.shape[2]): ... im[i, :, k] = np.linspace(0.0, 0.3 * k, im.shape[1]) >>> myinfo = { ... "field": "dinosaurs", ... "east_vector": np.array([1.0, 0.0, 0.0]), ... "north_vector": np.array([0.0, 0.0, 1.0]), ... "normal_vector": np.array([0.0, 1.0, 0.0]), ... "width": 0.245, ... "units": "cm", ... "type": "rendering", ... } >>> im_arr = ImageArray(im, info=myinfo) >>> im_arr.write_hdf5("test_ImageArray.h5") """ if dataset_name is None: dataset_name = self.info.get("name", "image") super().write_hdf5(filename, dataset_name=dataset_name, info=self.info) def add_background_color(self, background="black", inline=True): r"""Adds a background color to a 4-channel ImageArray This adds a background color to a 4-channel ImageArray, by default doing so inline. The ImageArray must already be normalized to the [0,1] range. Parameters ---------- background: This can be used to set a background color for the image, and can take several types of values: * ``white``: white background, opaque * ``black``: black background, opaque * ``None``: transparent background * 4-element array [r,g,b,a]: arbitrary rgba setting. Default: 'black' inline : boolean, optional If True, original ImageArray is modified. If False, a copy is first created, then modified. Default: True Returns ------- out: ImageArray The modified ImageArray with a background color added. Examples -------- >>> im = np.zeros([64, 128, 4]) >>> for i in range(im.shape[0]): ... for k in range(im.shape[2]): ... im[i, :, k] = np.linspace(0.0, 10.0 * k, im.shape[1]) >>> im_arr = ImageArray(im) >>> im_arr.rescale() >>> new_im = im_arr.add_background_color([1.0, 0.0, 0.0, 1.0], inline=False) >>> new_im.write_png("red_bg.png") >>> im_arr.add_background_color("black") >>> im_arr.write_png("black_bg.png") """ assert self.shape[-1] == 4 if background is None: background = (0.0, 0.0, 0.0, 0.0) elif background == "white": background = (1.0, 1.0, 1.0, 1.0) elif background == "black": background = (0.0, 0.0, 0.0, 1.0) # Alpha blending to background if inline: out = self else: out = self.copy() for i in range(3): out[:, :, i] = self[:, :, i] * self[:, :, 3] out[:, :, i] += background[i] * background[3] * (1.0 - self[:, :, 3]) out[:, :, 3] = self[:, :, 3] + background[3] * (1.0 - self[:, :, 3]) return out def rescale(self, cmax=None, amax=None, inline=True): r"""Rescales the image to be in [0,1] range. Parameters ---------- cmax : float, optional Normalization value to use for rgb channels. Defaults to None, corresponding to using the maximum value in the rgb channels. amax : float, optional Normalization value to use for alpha channel. Defaults to None, corresponding to using the maximum value in the alpha channel. inline : boolean, optional Specifies whether or not the rescaling is done inline. If false, a new copy of the ImageArray will be created, returned. Default:True. Returns ------- out: ImageArray The rescaled ImageArray, clipped to the [0,1] range. Notes ----- This requires that the shape of the ImageArray to have a length of 3, and for the third dimension to be >= 3. If the third dimension has a shape of 4, the alpha channel will also be rescaled. Examples -------- >>> im = np.zeros([64, 128, 4]) >>> for i in range(im.shape[0]): ... for k in range(im.shape[2]): ... im[i, :, k] = np.linspace(0.0, 0.3 * k, im.shape[1]) >>> im = ImageArray(im) >>> im.write_png("original.png") >>> im.rescale() >>> im.write_png("normalized.png") """ assert len(self.shape) == 3 assert self.shape[2] >= 3 if inline: out = self else: out = self.copy() if cmax is None: cmax = self[:, :, :3].sum(axis=2).max() if cmax > 0.0: np.multiply(self[:, :, :3], 1.0 / cmax, out[:, :, :3]) if self.shape[2] == 4: if amax is None: amax = self[:, :, 3].max() if amax > 0.0: np.multiply(self[:, :, 3], 1.0 / amax, out[:, :, 3]) np.clip(out, 0.0, 1.0, out) return out def write_png( self, filename, sigma_clip=None, background="black", rescale=True, ): r"""Writes ImageArray to png file. Parameters ---------- filename : string Filename to save to. If None, PNG contents will be returned as a string. sigma_clip : float, optional Image will be clipped before saving to the standard deviation of the image multiplied by this value. Useful for enhancing images. Default: None background: This can be used to set a background color for the image, and can take several types of values: * ``white``: white background, opaque * ``black``: black background, opaque * ``None``: transparent background * 4-element array [r,g,b,a]: arbitrary rgba setting. Default: 'black' rescale : boolean, optional If True, will write out a rescaled image (without modifying the original image). Default: True Examples -------- >>> im = np.zeros([64, 128, 4]) >>> for i in range(im.shape[0]): ... for k in range(im.shape[2]): ... im[i, :, k] = np.linspace(0.0, 10.0 * k, im.shape[1]) >>> im_arr = ImageArray(im) >>> im_arr.write_png("standard.png") >>> im_arr.write_png("non-scaled.png", rescale=False) >>> im_arr.write_png("black_bg.png", background="black") >>> im_arr.write_png("white_bg.png", background="white") >>> im_arr.write_png("green_bg.png", background=[0, 1, 0, 1]) >>> im_arr.write_png("transparent_bg.png", background=None) """ if rescale: scaled = self.rescale(inline=False) else: scaled = self if self.shape[-1] == 4: out = scaled.add_background_color(background, inline=False) else: out = scaled if filename is not None and filename[-4:] != ".png": filename += ".png" if sigma_clip is not None: clip_value = self._clipping_value(sigma_clip, im=out) return write_bitmap(out.swapaxes(0, 1), filename, clip_value) else: return write_bitmap(out.swapaxes(0, 1), filename) def write_image( self, filename, color_bounds=None, channel=None, cmap_name=None, func=lambda x: x, ): r"""Writes a single channel of the ImageArray to a png file. Parameters ---------- filename : string Note filename not be modified. Other Parameters ---------------- channel: int Which channel to write out as an image. Defaults to 0 cmap_name: string Name of the colormap to be used. color_bounds : tuple of floats, optional The min and max to scale between. Outlying values will be clipped. cmap_name : string, optional An acceptable colormap. See either yt.visualization.color_maps or https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html . func : function, optional A function to transform the buffer before applying a colormap. Returns ------- scaled_image : uint8 image that has been saved Examples -------- >>> im = np.zeros([64, 128]) >>> for i in range(im.shape[0]): ... im[i, :] = np.linspace(0.0, 0.3 * i, im.shape[1]) >>> myinfo = { ... "field": "dinosaurs", ... "east_vector": np.array([1.0, 0.0, 0.0]), ... "north_vector": np.array([0.0, 0.0, 1.0]), ... "normal_vector": np.array([0.0, 1.0, 0.0]), ... "width": 0.245, ... "units": "cm", ... "type": "rendering", ... } >>> im_arr = ImageArray(im, info=myinfo) >>> im_arr.write_image("test_ImageArray.png") """ if cmap_name is None: cmap_name = ytcfg.get("yt", "default_colormap") if filename is not None and filename[-4:] != ".png": filename += ".png" # TODO: Write info dict as png metadata if channel is None: return write_image( self.swapaxes(0, 1).to_ndarray(), filename, color_bounds=color_bounds, cmap_name=cmap_name, func=func, ) else: return write_image( self.swapaxes(0, 1)[:, :, channel].to_ndarray(), filename, color_bounds=color_bounds, cmap_name=cmap_name, func=func, ) def save(self, filename, png=True, hdf5=True, dataset_name=None): """ Saves ImageArray. Arguments: filename: string This should not contain the extension type (.png, .h5, ...) Optional Arguments: png: boolean, default True Save to a png hdf5: boolean, default True Save to hdf5 file, including info dictionary as attributes. """ if png: if not filename.endswith(".png"): filename = filename + ".png" if len(self.shape) > 2: self.write_png(filename) else: self.write_image(filename) if hdf5: if not filename.endswith(".h5"): filename = filename + ".h5" self.write_hdf5(filename, dataset_name) def _clipping_value(self, sigma_clip, im=None): # return the max value to clip with given a sigma_clip value. If im # is None, the current instance is used if im is None: im = self nz = im[:, :, :3][im[:, :, :3].nonzero()] return nz.mean() + sigma_clip * nz.std() yt-project-yt-f043ac8/yt/data_objects/index_subobjects/000077500000000000000000000000001510711153200232625ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/index_subobjects/__init__.py000066400000000000000000000001151510711153200253700ustar00rootroot00000000000000from .grid_patch import AMRGridPatch from .octree_subset import OctreeSubset yt-project-yt-f043ac8/yt/data_objects/index_subobjects/grid_patch.py000066400000000000000000000370531510711153200257500ustar00rootroot00000000000000import weakref import numpy as np import yt.geometry.particle_deposit as particle_deposit from yt._typing import FieldKey from yt.config import ytcfg from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.funcs import is_sequence from yt.geometry.selection_routines import convert_mask_to_indices from yt.units.yt_array import YTArray from yt.utilities.exceptions import ( YTFieldTypeNotFound, YTParticleDepositionNotImplemented, ) from yt.utilities.lib.interpolators import ghost_zone_interpolate from yt.utilities.lib.mesh_utilities import clamp_edges from yt.utilities.nodal_data_utils import get_nodal_slices class AMRGridPatch(YTSelectionContainer): _spatial = True _num_ghost_zones = 0 _grids = None _id_offset = 1 _cache_mask = True _type_name = "grid" _skip_add = True _con_args = ("id", "filename") _container_fields = ( ("index", "dx"), ("index", "dy"), ("index", "dz"), ("index", "x"), ("index", "y"), ("index", "z"), ) OverlappingSiblings = None def __init__(self, id, filename=None, index=None): super().__init__(index.dataset, None) self.id = id self._child_mask = self._child_indices = self._child_index_mask = None self.ds = index.dataset self._index = weakref.proxy(index) self.start_index = None self.filename = filename self._last_mask = None self._last_count = -1 self._last_selector_id = None def get_global_startindex(self): """ Return the integer starting index for each dimension at the current level. """ if self.start_index is not None: return self.start_index if self.Parent is None: left = self.LeftEdge.d - self.ds.domain_left_edge.d start_index = left / self.dds.d return np.rint(start_index).astype("int64").ravel() pdx = self.Parent.dds.d di = np.rint((self.LeftEdge.d - self.Parent.LeftEdge.d) / pdx) start_index = self.Parent.get_global_startindex() + di self.start_index = (start_index * self.ds.refine_by).astype("int64").ravel() return self.start_index def __getitem__(self, key): tr = super().__getitem__(key) try: fields = self._determine_fields(key) except YTFieldTypeNotFound: return tr finfo = self.ds._get_field_info(fields[0]) if not finfo.sampling_type == "particle": num_nodes = 2 ** sum(finfo.nodal_flag) new_shape = list(self.ActiveDimensions) if num_nodes > 1: new_shape += [num_nodes] return tr.reshape(new_shape) return tr def convert(self, datatype): """ This will attempt to convert a given unit to cgs from code units. It either returns the multiplicative factor or throws a KeyError. """ return self.ds[datatype] @property def shape(self): return self.ActiveDimensions def _reshape_vals(self, arr): if len(arr.shape) == 3: return arr return arr.reshape(self.ActiveDimensions, order="C") def _generate_container_field(self, field): if self._current_chunk is None: self.index._identify_base_chunk(self) if field == ("index", "dx"): tr = self._current_chunk.fwidth[:, 0] elif field == ("index", "dy"): tr = self._current_chunk.fwidth[:, 1] elif field == ("index", "dz"): tr = self._current_chunk.fwidth[:, 2] elif field == ("index", "x"): tr = self._current_chunk.fcoords[:, 0] elif field == ("index", "y"): tr = self._current_chunk.fcoords[:, 1] elif field == ("index", "z"): tr = self._current_chunk.fcoords[:, 2] return self._reshape_vals(tr) def _setup_dx(self): # So first we figure out what the index is. We don't assume # that dx=dy=dz, at least here. We probably do elsewhere. id = self.id - self._id_offset ds = self.ds index = self.index if self.Parent is not None: if not hasattr(self.Parent, "dds"): self.Parent._setup_dx() self.dds = self.Parent.dds.d / self.ds.refine_by else: LE, RE = (index.grid_left_edge[id, :].d, index.grid_right_edge[id, :].d) self.dds = (RE - LE) / self.ActiveDimensions if self.ds.dimensionality < 3: self.dds[2] = ds.domain_right_edge[2] - ds.domain_left_edge[2] elif self.ds.dimensionality < 2: self.dds[1] = ds.domain_right_edge[1] - ds.domain_left_edge[1] self.dds = self.dds.view(YTArray) self.dds.units = self.index.grid_left_edge.units def __repr__(self): cls_name = self.__class__.__name__ return f"{cls_name}_{self.id:04d} ({self.ActiveDimensions})" def __int__(self): return self.id def clear_data(self): """ Clear out the following things: child_mask, child_indices, all fields, all field parameters. """ super().clear_data() self._setup_dx() def _prepare_grid(self): """Copies all the appropriate attributes from the index.""" # This is definitely the slowest part of generating the index # Now we give it pointers to all of its attributes # Note that to keep in line with Enzo, we have broken PEP-8 h = self.index # cache it my_ind = self.id - self._id_offset self.ActiveDimensions = h.grid_dimensions[my_ind] self.LeftEdge = h.grid_left_edge[my_ind] self.RightEdge = h.grid_right_edge[my_ind] # This can be expensive so we allow people to disable this behavior # via a config option if ytcfg.get("yt", "reconstruct_index"): if is_sequence(self.Parent) and len(self.Parent) > 0: p = self.Parent[0] else: p = self.Parent if p is not None and p != []: # clamp grid edges to an integer multiple of the parent cell # width clamp_edges(self.LeftEdge, p.LeftEdge, p.dds) clamp_edges(self.RightEdge, p.RightEdge, p.dds) h.grid_levels[my_ind, 0] = self.Level # This might be needed for streaming formats # self.Time = h.gridTimes[my_ind,0] self.NumberOfParticles = h.grid_particle_count[my_ind, 0] def get_position(self, index): """Returns center position of an *index*.""" pos = (index + 0.5) * self.dds + self.LeftEdge return pos def _fill_child_mask(self, child, mask, tofill, dlevel=1): rf = self.ds.refine_by if dlevel != 1: rf = rf**dlevel gi, cgi = self.get_global_startindex(), child.get_global_startindex() startIndex = np.maximum(0, cgi // rf - gi) endIndex = np.minimum( (cgi + child.ActiveDimensions) // rf - gi, self.ActiveDimensions ) endIndex += startIndex == endIndex mask[ startIndex[0] : endIndex[0], startIndex[1] : endIndex[1], startIndex[2] : endIndex[2], ] = tofill @property def child_mask(self): """ Generates self.child_mask, which is zero where child grids exist (and thus, where higher resolution data is available). """ child_mask = np.ones(self.ActiveDimensions, "bool") for child in self.Children: self._fill_child_mask(child, child_mask, 0) for sibling in self.OverlappingSiblings or []: self._fill_child_mask(sibling, child_mask, 0, dlevel=0) return child_mask @property def child_indices(self): return self.child_mask == 0 @property def child_index_mask(self): """ Generates self.child_index_mask, which is -1 where there is no child, and otherwise has the ID of the grid that resides there. """ child_index_mask = np.zeros(self.ActiveDimensions, "int32") - 1 for child in self.Children: self._fill_child_mask(child, child_index_mask, child.id) for sibling in self.OverlappingSiblings or []: self._fill_child_mask(sibling, child_index_mask, sibling.id, dlevel=0) return child_index_mask def retrieve_ghost_zones(self, n_zones, fields, all_levels=False, smoothed=False): # We will attempt this by creating a datacube that is exactly bigger # than the grid by nZones*dx in each direction nl = self.get_global_startindex() - n_zones new_left_edge = nl * self.dds + self.ds.domain_left_edge # Something different needs to be done for the root grid, though level = self.Level if all_levels: level = self.index.max_level + 1 kwargs = { "dims": self.ActiveDimensions + 2 * n_zones, "num_ghost_zones": n_zones, "use_pbar": False, "fields": fields, } # This should update the arguments to set the field parameters to be # those of this grid. field_parameters = {} field_parameters.update(self.field_parameters) if smoothed: cube = self.ds.smoothed_covering_grid( level, new_left_edge, field_parameters=field_parameters, **kwargs ) else: cube = self.ds.covering_grid( level, new_left_edge, field_parameters=field_parameters, **kwargs ) cube._base_grid = self return cube def get_vertex_centered_data( self, fields: list[FieldKey], smoothed: bool = True, no_ghost: bool = False, ): # Make sure the field list has only unique entries fields = list(set(fields)) new_fields = {} for field in fields: finfo = self.ds._get_field_info(field) new_fields[field] = self.ds.arr( np.zeros(self.ActiveDimensions + 1), finfo.output_units ) if no_ghost: for field in fields: # Ensure we have the native endianness in this array. Avoid making # a copy if possible. old_field = np.asarray(self[field], dtype="=f8") # We'll use the ghost zone routine, which will naturally # extrapolate here. input_left = np.array([0.5, 0.5, 0.5], dtype="float64") output_left = np.array([0.0, 0.0, 0.0], dtype="float64") # rf = 1 here ghost_zone_interpolate( 1, old_field, input_left, new_fields[field], output_left ) else: cg = self.retrieve_ghost_zones(1, fields, smoothed=smoothed) for field in fields: src = cg[field].in_units(new_fields[field].units).d dest = new_fields[field].d np.add(dest, src[1:, 1:, 1:], dest) np.add(dest, src[:-1, 1:, 1:], dest) np.add(dest, src[1:, :-1, 1:], dest) np.add(dest, src[1:, 1:, :-1], dest) np.add(dest, src[:-1, 1:, :-1], dest) np.add(dest, src[1:, :-1, :-1], dest) np.add(dest, src[:-1, :-1, 1:], dest) np.add(dest, src[:-1, :-1, :-1], dest) np.multiply(dest, 0.125, dest) return new_fields def select_icoords(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, 3), dtype="int64") coords = convert_mask_to_indices(mask, self._last_count) coords += self.get_global_startindex()[None, :] return coords def select_fcoords(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, 3), dtype="float64") coords = convert_mask_to_indices(mask, self._last_count).astype("float64") coords += 0.5 coords *= self.dds.d[None, :] coords += self.LeftEdge.d[None, :] return coords def select_fwidth(self, dobj): count = self.count(dobj.selector) if count == 0: return np.empty((0, 3), dtype="float64") coords = np.empty((count, 3), dtype="float64") for axis in range(3): coords[:, axis] = self.dds.d[axis] return coords def select_ires(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty(0, dtype="int64") coords = np.empty(self._last_count, dtype="int64") coords[:] = self.Level return coords def select_tcoords(self, dobj): dt, t = dobj.selector.get_dt(self) return dt, t def smooth(self, *args, **kwargs): raise NotImplementedError def particle_operation(self, *args, **kwargs): raise NotImplementedError def deposit(self, positions, fields=None, method=None, kernel_name="cubic"): # Here we perform our particle deposition. cls = getattr(particle_deposit, f"deposit_{method}", None) if cls is None: raise YTParticleDepositionNotImplemented(method) # We allocate number of zones, not number of octs. Everything # inside this is Fortran ordered because of the ordering in the # octree deposit routines, so we reverse it here to match the # convention there nvals = tuple(self.ActiveDimensions[::-1]) # append a dummy dimension because we are only depositing onto # one grid op = cls(nvals + (1,), kernel_name) op.initialize() if positions.size > 0: op.process_grid(self, positions, fields) vals = op.finalize() if vals is None: return # Fortran-ordered, so transpose. vals = vals.transpose() # squeeze dummy dimension we appended above return np.squeeze(vals, axis=0) def select_blocks(self, selector): mask = self._get_selector_mask(selector) yield self, mask def _get_selector_mask(self, selector): if self._cache_mask and hash(selector) == self._last_selector_id: mask = self._last_mask else: mask, count = selector.fill_mask_regular_grid(self) if self._cache_mask: self._last_mask = mask self._last_selector_id = hash(selector) if mask is None: self._last_count = 0 else: self._last_count = count return mask def select(self, selector, source, dest, offset): mask = self._get_selector_mask(selector) count = self.count(selector) if count == 0: return 0 dim = np.squeeze(self.ds.dimensionality) nodal_flag = source.shape[:dim] - self.ActiveDimensions[:dim] if sum(nodal_flag) == 0: dest[offset : offset + count] = source[mask] else: slices = get_nodal_slices(source.shape, nodal_flag, dim) for i, sl in enumerate(slices): dest[offset : offset + count, i] = source[tuple(sl)][np.squeeze(mask)] return count def count(self, selector): mask = self._get_selector_mask(selector) if mask is None: return 0 return self._last_count def count_particles(self, selector, x, y, z): # We don't cache the selector results count = selector.count_points(x, y, z, 0.0) return count def select_particles(self, selector, x, y, z): mask = selector.select_points(x, y, z, 0.0) return mask yt-project-yt-f043ac8/yt/data_objects/index_subobjects/octree_subset.py000066400000000000000000000613421510711153200265100ustar00rootroot00000000000000import abc from contextlib import contextmanager from functools import cached_property from itertools import product, repeat import numpy as np from unyt import unyt_array import yt.geometry.particle_deposit as particle_deposit import yt.geometry.particle_smooth as particle_smooth from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.geometry.particle_oct_container import ParticleOctreeContainer from yt.units.dimensions import length # type: ignore from yt.utilities.exceptions import ( YTFieldTypeNotFound, YTInvalidPositionArray, YTParticleDepositionNotImplemented, ) from yt.utilities.lib.geometry_utils import compute_morton from yt.utilities.logger import ytLogger as mylog def cell_count_cache(func): def cc_cache_func(self, dobj): if hash(dobj.selector) != self._last_selector_id: self._cell_count = -1 rv = func(self, dobj) self._cell_count = rv.shape[0] self._last_selector_id = hash(dobj.selector) return rv return cc_cache_func class OctreeSubset(YTSelectionContainer, abc.ABC): _spatial = True _num_ghost_zones = 0 _type_name = "octree_subset" _skip_add = True _con_args: tuple[str, ...] = ("base_region", "domain", "ds") _domain_offset = 0 _cell_count = -1 _block_order = "C" def __init__(self, base_region, domain, ds, num_zones=2, num_ghost_zones=0): super().__init__(ds, None) self._num_zones = num_zones self._num_ghost_zones = num_ghost_zones self.domain = domain self.domain_id = domain.domain_id self.ds = domain.ds self._index = self.ds.index self._last_mask = None self._last_selector_id = None self.base_region = base_region self.base_selector = base_region.selector @property @abc.abstractmethod def oct_handler(self): # In charge of returning the oct_handler pass def __getitem__(self, key): tr = super().__getitem__(key) try: fields = self._determine_fields(key) except YTFieldTypeNotFound: return tr finfo = self.ds._get_field_info(fields[0]) if not finfo.sampling_type == "particle": # We may need to reshape the field, if it is being queried from # field_data. If it's already cached, it just passes through. if len(tr.shape) < 4: tr = self._reshape_vals(tr) return tr return tr @property def nz(self): return self._num_zones + 2 * self._num_ghost_zones def _reshape_vals(self, arr): nz = self.nz if len(arr.shape) <= 2: n_oct = arr.shape[0] // (nz**3) elif arr.shape[-1] == 3: n_oct = arr.shape[-2] else: n_oct = arr.shape[-1] if arr.size == nz * nz * nz * n_oct: new_shape = (nz, nz, nz, n_oct) elif arr.size == nz * nz * nz * n_oct * 3: new_shape = (nz, nz, nz, n_oct, 3) else: raise RuntimeError # Note that if arr is already F-contiguous, this *shouldn't* copy the # data. But, it might. However, I can't seem to figure out how to # make the assignment to .shape, which *won't* copy the data, make the # resultant array viewed in Fortran order. arr = arr.reshape(new_shape, order="F") return arr def mask_refinement(self, selector): mask = self.oct_handler.mask(selector, domain_id=self.domain_id) return mask def select_blocks(self, selector): mask = self.oct_handler.mask(selector, domain_id=self.domain_id) slicer = OctreeSubsetBlockSlice(self, self.ds) for i, sl in slicer: yield sl, np.atleast_3d(mask[i, ...]) def select_tcoords(self, dobj): # These will not be pre-allocated, which can be a problem for speed and # memory usage. dts, ts = [], [] for sl, mask in self.select_blocks(dobj.selector): sl.child_mask = np.asfortranarray(mask) dt, t = dobj.selector.get_dt(sl) dts.append(dt) ts.append(t) if len(dts) == len(ts) == 0: return np.empty(0, "f8"), np.empty(0, "f8") return np.concatenate(dts), np.concatenate(ts) @cached_property def domain_ind(self): return self.oct_handler.domain_ind(self.selector) def deposit(self, positions, fields=None, method=None, kernel_name="cubic"): r"""Operate on the mesh, in a particle-against-mesh fashion, with exclusively local input. This uses the octree indexing system to call a "deposition" operation (defined in yt/geometry/particle_deposit.pyx) that can take input from several particles (local to the mesh) and construct some value on the mesh. The canonical example is to sum the total mass in a mesh cell and then divide by its volume. Parameters ---------- positions : array_like (Nx3) The positions of all of the particles to be examined. A new indexed octree will be constructed on these particles. fields : list of arrays All the necessary fields for computing the particle operation. For instance, this might include mass, velocity, etc. method : string This is the "method name" which will be looked up in the `particle_deposit` namespace as `methodname_deposit`. Current methods include `count`, `simple_smooth`, `sum`, `std`, `cic`, `weighted_mean`, `mesh_id`, and `nearest`. kernel_name : string, default 'cubic' This is the name of the smoothing kernel to use. Current supported kernel names include `cubic`, `quartic`, `quintic`, `wendland2`, `wendland4`, and `wendland6`. Returns ------- List of fortran-ordered, mesh-like arrays. """ # Here we perform our particle deposition. if fields is None: fields = [] cls = getattr(particle_deposit, f"deposit_{method}", None) if cls is None: raise YTParticleDepositionNotImplemented(method) nz = self.nz nvals = (nz, nz, nz, (self.domain_ind >= 0).sum()) if np.max(self.domain_ind) >= nvals[-1]: print( f"nocts, domain_ind >= 0, max {self.oct_handler.nocts} {nvals[-1]} {np.max(self.domain_ind)}" ) raise Exception() # We allocate number of zones, not number of octs op = cls(nvals, kernel_name) op.initialize() mylog.debug( "Depositing %s (%s^3) particles into %s Octs", positions.shape[0], positions.shape[0] ** 0.3333333, nvals[-1], ) positions.convert_to_units("code_length") pos = positions.d # We should not need the following if we know in advance all our fields # need no casting. fields = [np.ascontiguousarray(f, dtype="float64") for f in fields] op.process_octree( self.oct_handler, self.domain_ind, pos, fields, self.domain_id, self._domain_offset, ) vals = op.finalize() if vals is None: return return np.asfortranarray(vals) def mesh_sampling_particle_field(self, positions, mesh_field, lvlmax=None): r"""Operate on the particles, in a mesh-against-particle fashion, with exclusively local input. This uses the octree indexing system to call a "mesh sampling" operation (defined in yt/geometry/particle_deposit.pyx). For each particle, the function returns the value of the cell containing the particle. Parameters ---------- positions : array_like (Nx3) The positions of all of the particles to be examined. mesh_field : array_like (M,) The value of the field to deposit. lvlmax : array_like (N), optional If provided, the maximum level where to look for cells Returns ------- List of fortran-ordered, particle-like arrays containing the value of the mesh at the location of the particles. """ # Here we perform our particle deposition. npart = positions.shape[0] nocts = (self.domain_ind >= 0).sum() # We allocate number of zones, not number of octs op = particle_deposit.CellIdentifier(npart, "none") op.initialize(npart) mylog.debug( "Depositing %s Octs onto %s (%s^3) particles", nocts, positions.shape[0], positions.shape[0] ** 0.3333333, ) pos = positions.to_value("code_length").astype("float64", copy=False) op.process_octree( self.oct_handler, self.domain_ind, pos, None, self.domain_id, self._domain_offset, lvlmax=lvlmax, ) igrid, icell = op.finalize() igrid = np.asfortranarray(igrid) icell = np.asfortranarray(icell) # Some particle may fall outside of the local domain, so we # need to be careful here ids = igrid * 8 + icell mask = ids >= 0 # Fill the return array ret = np.empty(npart) ret[mask] = mesh_field[ids[mask]] ret[~mask] = np.nan return ret def smooth( self, positions, fields=None, index_fields=None, method=None, create_octree=False, nneighbors=64, kernel_name="cubic", ): r"""Operate on the mesh, in a particle-against-mesh fashion, with non-local input. This uses the octree indexing system to call a "smoothing" operation (defined in yt/geometry/particle_smooth.pyx) that can take input from several (non-local) particles and construct some value on the mesh. The canonical example is to conduct a smoothing kernel operation on the mesh. Parameters ---------- positions : array_like (Nx3) The positions of all of the particles to be examined. A new indexed octree will be constructed on these particles. fields : list of arrays All the necessary fields for computing the particle operation. For instance, this might include mass, velocity, etc. index_fields : list of arrays All of the fields defined on the mesh that may be used as input to the operation. method : string This is the "method name" which will be looked up in the `particle_smooth` namespace as `methodname_smooth`. Current methods include `volume_weighted`, `nearest`, `idw`, `nth_neighbor`, and `density`. create_octree : bool Should we construct a new octree for indexing the particles? In cases where we are applying an operation on a subset of the particles used to construct the mesh octree, this will ensure that we are able to find and identify all relevant particles. nneighbors : int, default 64 The number of neighbors to examine during the process. kernel_name : string, default 'cubic' This is the name of the smoothing kernel to use. Current supported kernel names include `cubic`, `quartic`, `quintic`, `wendland2`, `wendland4`, and `wendland6`. Returns ------- List of fortran-ordered, mesh-like arrays. """ # Here we perform our particle deposition. positions.convert_to_units("code_length") if create_octree: morton = compute_morton( positions[:, 0], positions[:, 1], positions[:, 2], self.ds.domain_left_edge, self.ds.domain_right_edge, ) morton.sort() particle_octree = ParticleOctreeContainer( [1, 1, 1], self.ds.domain_left_edge, self.ds.domain_right_edge, num_zones=self._nz, ) # This should ensure we get everything within one neighbor of home. particle_octree.n_ref = nneighbors * 2 particle_octree.add(morton) particle_octree.finalize(self.domain_id) pdom_ind = particle_octree.domain_ind(self.selector) else: particle_octree = self.oct_handler pdom_ind = self.domain_ind if fields is None: fields = [] if index_fields is None: index_fields = [] cls = getattr(particle_smooth, f"{method}_smooth", None) if cls is None: raise YTParticleDepositionNotImplemented(method) nz = self.nz mdom_ind = self.domain_ind nvals = (nz, nz, nz, (mdom_ind >= 0).sum()) op = cls(nvals, len(fields), nneighbors, kernel_name) op.initialize() mylog.debug( "Smoothing %s particles into %s Octs", positions.shape[0], nvals[-1] ) # Pointer operations within 'process_octree' require arrays to be # contiguous cf. https://bitbucket.org/yt_analysis/yt/issues/1079 fields = [np.ascontiguousarray(f, dtype="float64") for f in fields] op.process_octree( self.oct_handler, mdom_ind, positions, self.fcoords, fields, self.domain_id, self._domain_offset, self.ds.periodicity, index_fields, particle_octree, pdom_ind, self.ds.geometry, ) # If there are 0s in the smoothing field this will not throw an error, # but silently return nans for vals where dividing by 0 # Same as what is currently occurring, but suppressing the div by zero # error. with np.errstate(invalid="ignore"): vals = op.finalize() if isinstance(vals, list): vals = [np.asfortranarray(v) for v in vals] else: vals = np.asfortranarray(vals) return vals def particle_operation( self, positions, fields=None, method=None, nneighbors=64, kernel_name="cubic" ): r"""Operate on particles, in a particle-against-particle fashion. This uses the octree indexing system to call a "smoothing" operation (defined in yt/geometry/particle_smooth.pyx) that expects to be called in a particle-by-particle fashion. For instance, the canonical example of this would be to compute the Nth nearest neighbor, or to compute the density for a given particle based on some kernel operation. Many of the arguments to this are identical to those used in the smooth and deposit functions. Note that the `fields` argument must not be empty, as these fields will be modified in place. Parameters ---------- positions : array_like (Nx3) The positions of all of the particles to be examined. A new indexed octree will be constructed on these particles. fields : list of arrays All the necessary fields for computing the particle operation. For instance, this might include mass, velocity, etc. One of these will likely be modified in place. method : string This is the "method name" which will be looked up in the `particle_smooth` namespace as `methodname_smooth`. nneighbors : int, default 64 The number of neighbors to examine during the process. kernel_name : string, default 'cubic' This is the name of the smoothing kernel to use. Current supported kernel names include `cubic`, `quartic`, `quintic`, `wendland2`, `wendland4`, and `wendland6`. Returns ------- Nothing. """ # Here we perform our particle deposition. positions.convert_to_units("code_length") morton = compute_morton( positions[:, 0], positions[:, 1], positions[:, 2], self.ds.domain_left_edge, self.ds.domain_right_edge, ) morton.sort() particle_octree = ParticleOctreeContainer( [1, 1, 1], self.ds.domain_left_edge, self.ds.domain_right_edge, num_zones=2, ) particle_octree.n_ref = nneighbors * 2 particle_octree.add(morton) particle_octree.finalize() pdom_ind = particle_octree.domain_ind(self.selector) if fields is None: fields = [] cls = getattr(particle_smooth, f"{method}_smooth", None) if cls is None: raise YTParticleDepositionNotImplemented(method) nz = self.nz mdom_ind = self.domain_ind nvals = (nz, nz, nz, (mdom_ind >= 0).sum()) op = cls(nvals, len(fields), nneighbors, kernel_name) op.initialize() mylog.debug( "Smoothing %s particles into %s Octs", positions.shape[0], nvals[-1] ) op.process_particles( particle_octree, pdom_ind, positions, fields, self.domain_id, self._domain_offset, self.ds.periodicity, self.ds.geometry, ) vals = op.finalize() if vals is None: return if isinstance(vals, list): vals = [np.asfortranarray(v) for v in vals] else: vals = np.asfortranarray(vals) return vals @cell_count_cache def select_icoords(self, dobj): return self.oct_handler.icoords( dobj.selector, domain_id=self.domain_id, num_cells=self._cell_count ) @cell_count_cache def select_fcoords(self, dobj): fcoords = self.oct_handler.fcoords( dobj.selector, domain_id=self.domain_id, num_cells=self._cell_count ) return self.ds.arr(fcoords, "code_length") @cell_count_cache def select_fwidth(self, dobj): fwidth = self.oct_handler.fwidth( dobj.selector, domain_id=self.domain_id, num_cells=self._cell_count ) return self.ds.arr(fwidth, "code_length") @cell_count_cache def select_ires(self, dobj): return self.oct_handler.ires( dobj.selector, domain_id=self.domain_id, num_cells=self._cell_count ) def select(self, selector, source, dest, offset): n = self.oct_handler.selector_fill( selector, source, dest, offset, domain_id=self.domain_id ) return n def count(self, selector): return -1 def count_particles(self, selector, x, y, z): # We don't cache the selector results count = selector.count_points(x, y, z, 0.0) return count def select_particles(self, selector, x, y, z): mask = selector.select_points(x, y, z, 0.0) return mask def get_vertex_centered_data(self, fields): # Make sure the field list has only unique entries fields = list(set(fields)) new_fields = {} cg = self.retrieve_ghost_zones(1, fields) for field in fields: new_fields[field] = cg[field][1:, 1:, 1:].copy() np.add(new_fields[field], cg[field][:-1, 1:, 1:], new_fields[field]) np.add(new_fields[field], cg[field][1:, :-1, 1:], new_fields[field]) np.add(new_fields[field], cg[field][1:, 1:, :-1], new_fields[field]) np.add(new_fields[field], cg[field][:-1, 1:, :-1], new_fields[field]) np.add(new_fields[field], cg[field][1:, :-1, :-1], new_fields[field]) np.add(new_fields[field], cg[field][:-1, :-1, 1:], new_fields[field]) np.add(new_fields[field], cg[field][:-1, :-1, :-1], new_fields[field]) np.multiply(new_fields[field], 0.125, new_fields[field]) return new_fields class OctreeSubsetBlockSlicePosition: def __init__(self, ind, block_slice): self.ind = ind self.block_slice = block_slice nz = self.block_slice.octree_subset.nz self.ActiveDimensions = np.array([nz, nz, nz], dtype="int64") self.ds = block_slice.ds def __getitem__(self, key): bs = self.block_slice rv = np.require( bs.octree_subset[key][:, :, :, self.ind].T, requirements=bs.octree_subset._block_order, ) return rv @property def id(self): return self.ind @property def Level(self): return self.block_slice._ires[0, 0, 0, self.ind] @property def LeftEdge(self): LE = ( self.block_slice._fcoords[0, 0, 0, self.ind, :].d - self.block_slice._fwidth[0, 0, 0, self.ind, :].d * 0.5 ) return self.block_slice.octree_subset.ds.arr( LE, self.block_slice._fcoords.units ) @property def RightEdge(self): RE = ( self.block_slice._fcoords[-1, -1, -1, self.ind, :].d + self.block_slice._fwidth[-1, -1, -1, self.ind, :].d * 0.5 ) return self.block_slice.octree_subset.ds.arr( RE, self.block_slice._fcoords.units ) @property def dds(self): return self.block_slice._fwidth[0, 0, 0, self.ind, :] def clear_data(self): pass def get_vertex_centered_data(self, fields, smoothed=False, no_ghost=False): field = fields[0] new_field = self.block_slice.get_vertex_centered_data(fields)[field] return {field: new_field[..., self.ind]} @contextmanager def _field_parameter_state(self, field_parameters): yield self.block_slice.octree_subset._field_parameter_state(field_parameters) class OctreeSubsetBlockSlice: def __init__(self, octree_subset, ds): self.octree_subset = octree_subset self.ds = ds self._vertex_centered_data = {} # Cache some attributes for attr in ["ires", "icoords", "fcoords", "fwidth"]: v = getattr(octree_subset, attr) setattr(self, f"_{attr}", octree_subset._reshape_vals(v)) @property def octree_subset_with_gz(self): subset_with_gz = getattr(self, "_octree_subset_with_gz", None) if not subset_with_gz: self._octree_subset_with_gz = self.octree_subset.retrieve_ghost_zones(1, []) return self._octree_subset_with_gz def get_vertex_centered_data(self, fields, smoothed=False, no_ghost=False): if no_ghost is True: raise NotImplementedError( "get_vertex_centered_data without ghost zones for " "oct-based datasets has not been implemented." ) # Make sure the field list has only unique entries fields = list(set(fields)) new_fields = {} cg = self.octree_subset_with_gz for field in fields: if field in self._vertex_centered_data: new_fields[field] = self._vertex_centered_data[field] else: finfo = self.ds._get_field_info(field) orig_field = cg[field] nocts = orig_field.shape[-1] new_field = np.zeros((3, 3, 3, nocts), order="F") # Compute vertex-centred data as mean of 8 neighbours cell data slices = (slice(1, None), slice(None, -1)) for slx, sly, slz in product(*repeat(slices, 3)): new_field += orig_field[slx, sly, slz] new_field *= 0.125 new_fields[field] = self.ds.arr(new_field, finfo.output_units) self._vertex_centered_data[field] = new_fields[field] return new_fields def __iter__(self): for i in range(self._ires.shape[-1]): yield i, OctreeSubsetBlockSlicePosition(i, self) class YTPositionArray(unyt_array): @property def morton(self): self.validate() eps = np.finfo(self.dtype).eps LE = self.min(axis=0) LE -= np.abs(LE) * eps RE = self.max(axis=0) RE += np.abs(RE) * eps morton = compute_morton(self[:, 0], self[:, 1], self[:, 2], LE, RE) return morton def to_octree(self, num_zones=2, dims=(1, 1, 1), n_ref=64): mi = self.morton mi.sort() eps = np.finfo(self.dtype).eps LE = self.min(axis=0) LE -= np.abs(LE) * eps RE = self.max(axis=0) RE += np.abs(RE) * eps octree = ParticleOctreeContainer(dims, LE, RE, num_zones=num_zones) octree.n_ref = n_ref octree.add(mi) octree.finalize() return octree def validate(self): if ( len(self.shape) != 2 or self.shape[1] != 3 or self.units.dimensions != length ): raise YTInvalidPositionArray(self.shape, self.units.dimensions) yt-project-yt-f043ac8/yt/data_objects/index_subobjects/particle_container.py000066400000000000000000000067151510711153200275120ustar00rootroot00000000000000import contextlib from more_itertools import always_iterable from yt.data_objects.data_containers import YTFieldData from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.utilities.exceptions import ( YTDataSelectorNotImplemented, YTNonIndexedDataContainer, ) def _non_indexed(name): def _func_non_indexed(self, *args, **kwargs): raise YTNonIndexedDataContainer(self) return _func_non_indexed class ParticleContainer(YTSelectionContainer): _spatial = False _type_name = "particle_container" _skip_add = True _con_args = ("base_region", "base_selector", "data_files", "overlap_files") def __init__( self, base_region, base_selector, data_files, overlap_files=None, domain_id=-1 ): if overlap_files is None: overlap_files = [] self.field_data = YTFieldData() self.field_parameters = {} self.data_files = list(always_iterable(data_files)) self.overlap_files = list(always_iterable(overlap_files)) self.ds = self.data_files[0].ds self._last_mask = None self._last_selector_id = None self._current_particle_type = "all" # self._current_fluid_type = self.ds.default_fluid_type self.base_region = base_region self.base_selector = base_selector self._octree = None self._temp_spatial = False if isinstance(base_region, ParticleContainer): self._temp_spatial = base_region._temp_spatial self._octree = base_region._octree # To ensure there are not domains if global octree not used self.domain_id = -1 def __reduce__(self): # we need to override the __reduce__ from data_containers as this method is not # a registered dataset method (i.e., ds.particle_container does not exist) arg_tuple = tuple(getattr(self, attr) for attr in self._con_args) return (self.__class__, arg_tuple) @property def selector(self): raise YTDataSelectorNotImplemented(self.oc_type_name) def select_particles(self, selector, x, y, z): mask = selector.select_points(x, y, z) return mask @contextlib.contextmanager def _expand_data_files(self): old_data_files = self.data_files old_overlap_files = self.overlap_files self.data_files = list(set(self.data_files + self.overlap_files)) self.data_files.sort() self.overlap_files = [] yield self self.data_files = old_data_files self.overlap_files = old_overlap_files def retrieve_ghost_zones(self, ngz, coarse_ghosts=False): gz_oct = self.octree.retrieve_ghost_zones(ngz, coarse_ghosts=coarse_ghosts) gz = ParticleContainer( gz_oct.base_region, gz_oct.base_selector, gz_oct.data_files, overlap_files=gz_oct.overlap_files, selector_mask=gz_oct.selector_mask, domain_id=gz_oct.domain_id, ) gz._octree = gz_oct return gz select_blocks = _non_indexed("select_blocks") deposit = _non_indexed("deposit") smooth = _non_indexed("smooth") select_icoords = _non_indexed("select_icoords") select_fcoords = _non_indexed("select_fcoords") select_fwidth = _non_indexed("select_fwidth") select_ires = _non_indexed("select_ires") select = _non_indexed("select") count = _non_indexed("count") count_particles = _non_indexed("count_particles") yt-project-yt-f043ac8/yt/data_objects/index_subobjects/stretched_grid.py000066400000000000000000000042071510711153200266310ustar00rootroot00000000000000import numpy as np from yt.geometry.selection_routines import convert_mask_to_indices from .grid_patch import AMRGridPatch class StretchedGrid(AMRGridPatch): def __init__(self, id, cell_widths, *, filename=None, index=None): self.cell_widths = [np.array(_) for _ in cell_widths] super().__init__(id, filename, index) def _check_consistency(self): computed_right_edge = self.LeftEdge + [ _.sum() for _ in self.cell_widths * self.LeftEdge.uq ] assert (computed_right_edge == self.RightEdge).all() def _get_selector_mask(self, selector): if self._cache_mask and hash(selector) == self._last_selector_id: mask = self._last_mask else: mask = selector.fill_mask(self) if self._cache_mask: self._last_mask = mask self._last_selector_id = hash(selector) if mask is None: self._last_count = 0 else: self._last_count = mask.sum() return mask def select_fwidth(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, 3), dtype="float64") indices = convert_mask_to_indices(mask, self._last_count) coords = np.array( [ self.cell_widths[0][indices[:, 0]], self.cell_widths[1][indices[:, 1]], self.cell_widths[2][indices[:, 2]], ] ).T return coords def select_fcoords(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, 3), dtype="float64") cell_centers = [ self.LeftEdge[i].d + np.add.accumulate(self.cell_widths[i]) - 0.5 * self.cell_widths[i] for i in range(3) ] indices = convert_mask_to_indices(mask, self._last_count) coords = np.array( [ cell_centers[0][indices[:, 0]], cell_centers[1][indices[:, 1]], cell_centers[2][indices[:, 2]], ] ).T return coords yt-project-yt-f043ac8/yt/data_objects/index_subobjects/unstructured_mesh.py000066400000000000000000000155321510711153200274250ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.funcs import mylog from yt.utilities.lib.mesh_utilities import fill_fcoords, fill_fwidths class UnstructuredMesh(YTSelectionContainer): # This is a base class, not meant to be used directly. _spatial = False _connectivity_length = -1 _type_name = "unstructured_mesh" _skip_add = True _index_offset = 0 _con_args = ("mesh_id", "filename", "connectivity_indices", "connectivity_coords") def __init__( self, mesh_id, filename, connectivity_indices, connectivity_coords, index ): super().__init__(index.dataset, None) self.filename = filename self.mesh_id = mesh_id # This is where we set up the connectivity information self.connectivity_indices = connectivity_indices if connectivity_indices.shape[1] != self._connectivity_length: if self._connectivity_length == -1: self._connectivity_length = connectivity_indices.shape[1] else: raise RuntimeError self.connectivity_coords = connectivity_coords self.ds = index.dataset self._index = index self._last_mask = None self._last_count = -1 self._last_selector_id = None def _check_consistency(self): if self.connectivity_indices.shape[1] != self._connectivity_length: raise RuntimeError for gi in range(self.connectivity_indices.shape[0]): ind = self.connectivity_indices[gi, :] - self._index_offset coords = self.connectivity_coords[ind, :] for i in range(3): assert np.unique(coords[:, i]).size == 2 mylog.debug("Connectivity is consistent.") def __repr__(self): return "UnstructuredMesh_%04i" % (self.mesh_id) def get_global_startindex(self): """ Return the integer starting index for each dimension at the current level. """ raise NotImplementedError def convert(self, datatype): """ This will attempt to convert a given unit to cgs from code units. It either returns the multiplicative factor or throws a KeyError. """ return self.ds[datatype] @property def shape(self): raise NotImplementedError def _generate_container_field(self, field): raise NotImplementedError def select_fcoords(self, dobj=None): # This computes centroids! mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, 3), dtype="float64") centers = fill_fcoords( self.connectivity_coords, self.connectivity_indices, self._index_offset ) return centers[mask, :] def select_fwidth(self, dobj): raise NotImplementedError def select_icoords(self, dobj): raise NotImplementedError def select_ires(self, dobj): raise NotImplementedError def select_tcoords(self, dobj): raise NotImplementedError def deposit(self, positions, fields=None, method=None, kernel_name="cubic"): raise NotImplementedError def select_blocks(self, selector): mask = self._get_selector_mask(selector) yield self, mask def select(self, selector, source, dest, offset): mask = self._get_selector_mask(selector) count = self.count(selector) if count == 0: return 0 dest[offset : offset + count] = source[mask, ...] return count def count(self, selector): mask = self._get_selector_mask(selector) if mask is None: return 0 return self._last_count def count_particles(self, selector, x, y, z): # We don't cache the selector results count = selector.count_points(x, y, z, 0.0) return count def select_particles(self, selector, x, y, z): mask = selector.select_points(x, y, z, 0.0) return mask def _get_selector_mask(self, selector): if hash(selector) == self._last_selector_id: mask = self._last_mask else: self._last_mask = mask = selector.fill_mesh_cell_mask(self) self._last_selector_id = hash(selector) if mask is None: self._last_count = 0 else: self._last_count = mask.sum() return mask def select_fcoords_vertex(self, dobj=None): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, self._connectivity_length, 3), dtype="float64") vertices = self.connectivity_coords[self.connectivity_indices - 1] return vertices[mask, :, :] class SemiStructuredMesh(UnstructuredMesh): _connectivity_length = 8 _type_name = "semi_structured_mesh" _container_fields = ("dx", "dy", "dz") def __repr__(self): return "SemiStructuredMesh_%04i" % (self.mesh_id) def _generate_container_field(self, field): if self._current_chunk is None: self.index._identify_base_chunk(self) if field == "dx": return self._current_chunk.fwidth[:, 0] elif field == "dy": return self._current_chunk.fwidth[:, 1] elif field == "dz": return self._current_chunk.fwidth[:, 2] def select_fwidth(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty((0, 3), dtype="float64") widths = fill_fwidths( self.connectivity_coords, self.connectivity_indices, self._index_offset ) return widths[mask, :] def select_ires(self, dobj): ind = np.zeros(self.connectivity_indices.shape[0]) mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty(0, dtype="int32") return ind[mask] def select_tcoords(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty(0, dtype="float64") dt, t = dobj.selector.get_dt_mesh(self, mask.sum(), self._index_offset) return dt, t def _get_selector_mask(self, selector): if hash(selector) == self._last_selector_id: mask = self._last_mask else: self._last_mask = mask = selector.fill_mesh_cell_mask(self) self._last_selector_id = hash(selector) if mask is None: self._last_count = 0 else: self._last_count = mask.sum() return mask def select(self, selector, source, dest, offset): mask = self._get_selector_mask(selector) count = self.count(selector) if count == 0: return 0 # Note: this likely will not work with vector fields. dest[offset : offset + count] = source.flat[mask] return count yt-project-yt-f043ac8/yt/data_objects/level_sets/000077500000000000000000000000001510711153200220755ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/level_sets/__init__.py000066400000000000000000000000001510711153200241740ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/level_sets/api.py000066400000000000000000000005201510711153200232150ustar00rootroot00000000000000from .clump_handling import Clump, find_clumps from .clump_info_items import add_clump_info from .clump_tools import ( clump_list_sort, recursive_all_clumps, recursive_bottom_clumps, return_all_clumps, return_bottom_clumps, ) from .clump_validators import add_validator from .contour_finder import identify_contours yt-project-yt-f043ac8/yt/data_objects/level_sets/clump_handling.py000066400000000000000000000400561510711153200254400ustar00rootroot00000000000000import uuid import numpy as np from yt.fields.derived_field import ValidateSpatial from yt.frontends.ytdata.utilities import save_as_dataset from yt.funcs import get_output_filename, mylog from yt.utilities.tree_container import TreeContainer from .clump_info_items import clump_info_registry from .clump_validators import clump_validator_registry from .contour_finder import identify_contours def add_contour_field(ds, contour_key): def _contours(field, data): fd = data.get_field_parameter(f"contour_slices_{contour_key}") vals = data["index", "ones"] * -1 if fd is None or fd == 0.0: return vals for sl, v in fd.get(data.id, []): vals[sl] = v return vals ds.add_field( ("index", f"contours_{contour_key}"), function=_contours, validators=[ValidateSpatial(0)], take_log=False, display_field=False, sampling_type="cell", units="", ) class Clump(TreeContainer): def __init__( self, data, field, parent=None, clump_info=None, validators=None, base=None, contour_key=None, contour_id=None, ): self.data = data self.field = field self.parent = parent self.quantities = data.quantities self.min_val = self.data[field].min() self.max_val = self.data[field].max() self.info = {} self.children = [] # is this the parent clump? if base is None: base = self self.total_clumps = 0 if clump_info is None: self.set_default_clump_info() else: self.clump_info = clump_info for ci in self.clump_info: ci(self) self.base = base self.clump_id = self.base.total_clumps self.base.total_clumps += 1 self.contour_key = contour_key self.contour_id = contour_id if parent is not None: self.data.parent = self.parent.data if validators is None: validators = [] self.validators = validators # Return value of validity function. self.valid = None _leaves = None @property def leaves(self): if self._leaves is not None: return self._leaves self._leaves = [] for clump in self: if not clump.children: self._leaves.append(clump) return self._leaves def add_validator(self, validator, *args, **kwargs): """ Add a validating function to determine whether the clump should be kept. """ callback = clump_validator_registry.find(validator, *args, **kwargs) self.validators.append(callback) for child in self.children: child.add_validator(validator) def add_info_item(self, info_item, *args, **kwargs): "Adds an entry to clump_info list and tells children to do the same." callback = clump_info_registry.find(info_item, *args, **kwargs) callback(self) self.clump_info.append(callback) for child in self.children: child.add_info_item(info_item) def set_default_clump_info(self): "Defines default entries in the clump_info array." # add_info_item is recursive so this function does not need to be. self.clump_info = [] self.add_info_item("total_cells") self.add_info_item("cell_mass") if any("jeans" in f for f in self.data.pf.field_list): self.add_info_item("mass_weighted_jeans_mass") self.add_info_item("volume_weighted_jeans_mass") self.add_info_item("max_grid_level") if any("number_density" in f for f in self.data.pf.field_list): self.add_info_item("min_number_density") self.add_info_item("max_number_density") def clear_clump_info(self): """ Clears the clump_info array and passes the instruction to its children. """ self.clump_info = [] for child in self.children: child.clear_clump_info() def find_children(self, min_val, max_val=None): if self.children: mylog.info("Wiping out existing children clumps: %d.", len(self.children)) self.children = [] if max_val is None: max_val = self.max_val nj, cids = identify_contours(self.data, self.field, min_val, max_val) # Here, cids is the set of slices and values, keyed by the # parent_grid_id, that defines the contours. So we can figure out all # the unique values of the contours by examining the list here. unique_contours = set() for sl_list in cids.values(): for _sl, ff in sl_list: unique_contours.update(np.unique(ff)) contour_key = uuid.uuid4().hex base_object = getattr(self.data, "base_object", self.data) add_contour_field(base_object.ds, contour_key) for cid in sorted(unique_contours): if cid == -1: continue new_clump = base_object.cut_region( [f"obj['contours_{contour_key}'] == {cid}"], {(f"contour_slices_{contour_key}"): cids}, ) if new_clump["index", "ones"].size == 0: # This is to skip possibly duplicate clumps. # Using "ones" here will speed things up. continue self.children.append( Clump( new_clump, self.field, parent=self, validators=self.validators, base=self.base, clump_info=self.clump_info, contour_key=contour_key, contour_id=cid, ) ) def __iter__(self): yield self for child in self.children: yield from child def save_as_dataset(self, filename=None, fields=None): r"""Export clump tree to a reloadable yt dataset. This function will take a clump object and output a dataset containing the fields given in the ``fields`` list and all info items. The resulting dataset can be reloaded as a yt dataset. Parameters ---------- filename : str, optional The name of the file to be written. If None, the name will be a combination of the original dataset and the clump index. fields : list of strings or tuples, optional If this is supplied, it is the list of fields to be saved to disk. Returns ------- filename : str The name of the file that has been created. Examples -------- >>> import yt >>> from yt.data_objects.level_sets.api import Clump, find_clumps >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> data_source = ds.disk( ... [0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8, "kpc"), (1, "kpc") ... ) >>> field = ("gas", "density") >>> step = 2.0 >>> c_min = 10 ** np.floor(np.log10(data_source[field]).min()) >>> c_max = 10 ** np.floor(np.log10(data_source[field]).max() + 1) >>> master_clump = Clump(data_source, field) >>> master_clump.add_info_item("center_of_mass") >>> master_clump.add_validator("min_cells", 20) >>> find_clumps(master_clump, c_min, c_max, step) >>> fn = master_clump.save_as_dataset( ... fields=[("gas", "density"), ("all", "particle_mass")] ... ) >>> new_ds = yt.load(fn) >>> print(ds.tree["clump", "cell_mass"]) 1296926163.91 Msun >>> print(ds.tree["grid", "density"]) [ 2.54398434e-26 2.46620353e-26 2.25120154e-26 ..., 1.12879234e-25 1.59561490e-25 1.09824903e-24] g/cm**3 >>> print(ds.tree["all", "particle_mass"]) [ 4.25472446e+38 4.25472446e+38 4.25472446e+38 ..., 2.04238266e+38 2.04523901e+38 2.04770938e+38] g >>> print(ds.tree.children[0]["clump", "cell_mass"]) 909636495.312 Msun >>> print(ds.leaves[0]["clump", "cell_mass"]) 3756566.99809 Msun >>> print(ds.leaves[0]["grid", "density"]) [ 6.97820274e-24 6.58117370e-24 7.32046082e-24 6.76202430e-24 7.41184837e-24 6.76981480e-24 6.94287213e-24 6.56149658e-24 6.76584569e-24 6.94073710e-24 7.06713082e-24 7.22556526e-24 7.08338898e-24 6.78684331e-24 7.40647040e-24 7.03050456e-24 7.12438678e-24 6.56310217e-24 7.23201662e-24 7.17314333e-24] g/cm**3 """ ds = self.data.ds keyword = "%s_clump_%d" % (str(ds), self.clump_id) filename = get_output_filename(filename, keyword, ".h5") # collect clump info fields clump_info = {ci.name: [] for ci in self.base.clump_info} clump_info.update( { field: [] for field in ["clump_id", "parent_id", "contour_key", "contour_id"] } ) for clump in self: clump_info["clump_id"].append(clump.clump_id) if clump.parent is None: parent_id = -1 else: parent_id = clump.parent.clump_id clump_info["parent_id"].append(parent_id) contour_key = clump.contour_key if contour_key is None: contour_key = -1 clump_info["contour_key"].append(contour_key) contour_id = clump.contour_id if contour_id is None: contour_id = -1 clump_info["contour_id"].append(contour_id) for ci in self.base.clump_info: clump_info[ci.name].append(clump.info[ci.name][1]) for ci in clump_info: if hasattr(clump_info[ci][0], "units"): clump_info[ci] = ds.arr(clump_info[ci]) else: clump_info[ci] = np.array(clump_info[ci]) ftypes = {ci: "clump" for ci in clump_info} # collect data fields if fields is not None: contour_fields = [ ("index", f"contours_{ckey}") for ckey in np.unique(clump_info["contour_key"]) if str(ckey) != "-1" ] ptypes = [] field_data = {} need_grid_positions = False for f in self.base.data._determine_fields(fields) + contour_fields: if ds.field_info[f].sampling_type == "particle": if f[0] not in ptypes: ptypes.append(f[0]) ftypes[f] = f[0] else: need_grid_positions = True if f[1] in ("x", "y", "z", "dx", "dy", "dz"): # skip 'xyz' if a user passes that in because they # will be added to ftypes below continue ftypes[f] = "grid" field_data[f] = self.base[f] if len(ptypes) > 0: for ax in "xyz": for ptype in ptypes: p_field = (ptype, f"particle_position_{ax}") if p_field in ds.field_info and p_field not in field_data: ftypes[p_field] = p_field[0] field_data[p_field] = self.base[p_field] for clump in self: if clump.contour_key is None: continue for ptype in ptypes: cfield = (ptype, f"contours_{clump.contour_key}") if cfield not in field_data: field_data[cfield] = clump.data._part_ind(ptype).astype( np.int64 ) ftypes[cfield] = ptype field_data[cfield][clump.data._part_ind(ptype)] = ( clump.contour_id ) if need_grid_positions: for ax in "xyz": g_field = ("index", ax) if g_field in ds.field_info and g_field not in field_data: field_data[g_field] = self.base[g_field] ftypes[g_field] = "grid" g_field = ("index", "d" + ax) if g_field in ds.field_info and g_field not in field_data: ftypes[g_field] = "grid" field_data[g_field] = self.base[g_field] if self.contour_key is not None: cfilters = {} for field in field_data: if ftypes[field] == "grid": ftype = "index" else: ftype = field[0] cfield = (ftype, f"contours_{self.contour_key}") if cfield not in cfilters: cfilters[cfield] = field_data[cfield] == self.contour_id field_data[field] = field_data[field][cfilters[cfield]] clump_info.update(field_data) extra_attrs = {"data_type": "yt_clump_tree", "container_type": "yt_clump_tree"} save_as_dataset( ds, filename, clump_info, field_types=ftypes, extra_attrs=extra_attrs ) return filename def pass_down(self, operation): """ Performs an operation on a clump with an exec and passes the instruction down to clump children. """ # Call if callable, otherwise do an exec. if callable(operation): operation() else: exec(operation) for child in self.children: child.pass_down(operation) def _validate(self): "Apply all user specified validator functions." # Only call functions if not done already. if self.valid is not None: return self.valid self.valid = True for validator in self.validators: self.valid &= validator(self) if not self.valid: break return self.valid def __reduce__(self): raise RuntimeError( "Pickling Clump instances is not supported. Please use " "Clump.save_as_dataset instead" ) def __getitem__(self, request): return self.data[request] def find_clumps(clump, min_val, max_val, d_clump): mylog.info("Finding clumps: min: %e, max: %e, step: %f", min_val, max_val, d_clump) if min_val >= max_val: return clump.find_children(min_val, max_val=max_val) if len(clump.children) == 1: find_clumps(clump, min_val * d_clump, max_val, d_clump) elif len(clump.children) > 0: these_children = [] mylog.info("Investigating %d children.", len(clump.children)) for child in clump.children: find_clumps(child, min_val * d_clump, max_val, d_clump) if len(child.children) > 0: these_children.append(child) elif child._validate(): these_children.append(child) else: mylog.info( "Eliminating invalid, childless clump with %d cells.", len(child.data["index", "ones"]), ) if len(these_children) > 1: mylog.info( "%d of %d children survived.", len(these_children), len(clump.children) ) clump.children = these_children elif len(these_children) == 1: mylog.info( "%d of %d children survived, linking its children to parent.", len(these_children), len(clump.children), ) clump.children = these_children[0].children for child in clump.children: child.parent = clump child.data.parent = clump.data else: mylog.info( "%d of %d children survived, erasing children.", len(these_children), len(clump.children), ) clump.children = [] yt-project-yt-f043ac8/yt/data_objects/level_sets/clump_info_items.py000066400000000000000000000063241510711153200260100ustar00rootroot00000000000000import numpy as np from yt.utilities.operator_registry import OperatorRegistry clump_info_registry = OperatorRegistry() def add_clump_info(name, function): clump_info_registry[name] = ClumpInfoCallback(name, function) class ClumpInfoCallback: r""" A ClumpInfoCallback is a function that takes a clump, computes a quantity, and returns a string to be printed out for writing clump info. """ def __init__(self, name, function, args=None, kwargs=None): self.name = name self.function = function self.args = args if self.args is None: self.args = [] self.kwargs = kwargs if self.kwargs is None: self.kwargs = {} def __call__(self, clump): if self.name not in clump.info: clump.info[self.name] = self.function(clump, *self.args, **self.kwargs) rv = clump.info[self.name] return rv[0] % rv[1] def _center_of_mass(clump, units="code_length", **kwargs): p = clump.quantities.center_of_mass(**kwargs) return "Center of mass: %s.", p.to(units) add_clump_info("center_of_mass", _center_of_mass) def _total_cells(clump): n_cells = clump.data["index", "ones"].size return "Cells: %d.", n_cells add_clump_info("total_cells", _total_cells) def _cell_mass(clump): cell_mass = clump.data["gas", "cell_mass"].sum().in_units("Msun") return "Mass: %e Msun.", cell_mass add_clump_info("cell_mass", _cell_mass) def _mass_weighted_jeans_mass(clump): jeans_mass = clump.data.quantities.weighted_average_quantity( "jeans_mass", ("gas", "cell_mass") ).in_units("Msun") return "Jeans Mass (mass-weighted): %.6e Msolar.", jeans_mass add_clump_info("mass_weighted_jeans_mass", _mass_weighted_jeans_mass) def _volume_weighted_jeans_mass(clump): jeans_mass = clump.data.quantities.weighted_average_quantity( "jeans_mass", ("index", "cell_volume") ).in_units("Msun") return "Jeans Mass (volume-weighted): %.6e Msolar.", jeans_mass add_clump_info("volume_weighted_jeans_mass", _volume_weighted_jeans_mass) def _max_grid_level(clump): max_level = clump.data["index", "grid_level"].max() return "Max grid level: %d.", max_level add_clump_info("max_grid_level", _max_grid_level) def _min_number_density(clump): min_n = clump.data["gas", "number_density"].min().in_units("cm**-3") return "Min number density: %.6e cm^-3.", min_n add_clump_info("min_number_density", _min_number_density) def _max_number_density(clump): max_n = clump.data["gas", "number_density"].max().in_units("cm**-3") return "Max number density: %.6e cm^-3.", max_n add_clump_info("max_number_density", _max_number_density) def _distance_to_main_clump(clump, units="pc"): master = clump while master.parent is not None: master = master.parent master_com = clump.data.ds.arr(master.data.quantities.center_of_mass()) my_com = clump.data.ds.arr(clump.data.quantities.center_of_mass()) distance = np.sqrt(((master_com - my_com) ** 2).sum()) distance.convert_to_units("pc") return ( f"Distance from master center of mass: %.6e {units}.", distance.in_units(units), ) add_clump_info("distance_to_main_clump", _distance_to_main_clump) yt-project-yt-f043ac8/yt/data_objects/level_sets/clump_tools.py000066400000000000000000000043351510711153200250140ustar00rootroot00000000000000import numpy as np nar = np.array counter = 0 def recursive_all_clumps(clump, list, level, parentnumber): """A recursive function to flatten the index in *clump*. Not to be called directly: please call return_all_clumps, below.""" global counter counter += 1 clump.number = counter clump.parentnumber = parentnumber counter += 1 list.append(clump) clump.level = level if clump.children is not None: for child in clump.children: recursive_all_clumps(child, list, level + 1, clump.number) return list def return_all_clumps(clump): """Flatten the index defined by *clump*. Additionally adds three variables to the clump: level = depth of index number = index of clump in the final array parentnumber = index of this clumps parent """ global counter counter = 0 list = [] level = 0 clump.level = level parentnumber = -1 recursive_all_clumps(clump, list, level, parentnumber) return list def return_bottom_clumps(clump, dbg=0): """ Recursively return clumps at the bottom of the index. This gives a list of clumps similar to what one would get from a CLUMPFIND routine """ global counter counter = 0 list = [] level = 0 recursive_bottom_clumps(clump, list, dbg, level) return list def recursive_bottom_clumps(clump, clump_list, dbg=0, level=0): """Loops over a list of clumps (clumps) and fills clump_list with the bottom most. Recursive. Prints the level and the number of cores to the screen.""" global counter if (clump.children is None) or (len(clump.children) == 0): counter += 1 clump_list.append(clump) else: for child in clump.children: recursive_bottom_clumps(child, clump_list, dbg=dbg, level=level + 1) def clump_list_sort(clump_list): """Returns a copy of clump_list, sorted by ascending minimum density. This eliminates overlap when passing to yt.visualization.plot_modification.ClumpContourCallback""" minDensity = [c["gas", "density"].min() for c in clump_list] args = np.argsort(minDensity) list = nar(clump_list)[args] reverse = range(list.size - 1, -1, -1) return list[reverse] yt-project-yt-f043ac8/yt/data_objects/level_sets/clump_validators.py000066400000000000000000000065251510711153200260270ustar00rootroot00000000000000import numpy as np from yt.utilities.lib.misc_utilities import gravitational_binding_energy from yt.utilities.operator_registry import OperatorRegistry from yt.utilities.physical_constants import gravitational_constant_cgs as G clump_validator_registry = OperatorRegistry() def add_validator(name, function): clump_validator_registry[name] = ClumpValidator(function) class ClumpValidator: r""" A ClumpValidator is a function that takes a clump and returns True or False as to whether the clump is valid and shall be kept. """ def __init__(self, function, args=None, kwargs=None): self.function = function self.args = args if self.args is None: self.args = [] self.kwargs = kwargs if self.kwargs is None: self.kwargs = {} def __call__(self, clump): return self.function(clump, *self.args, **self.kwargs) def _gravitationally_bound( clump, use_thermal_energy=True, use_particles=True, truncate=True, num_threads=0 ): "True if clump is gravitationally bound." use_particles &= ("all", "particle_mass") in clump.data.ds.field_info bulk_velocity = clump.quantities.bulk_velocity(use_particles=use_particles) kinetic = ( 0.5 * ( clump["gas", "mass"] * ( (bulk_velocity[0] - clump["gas", "velocity_x"]) ** 2 + (bulk_velocity[1] - clump["gas", "velocity_y"]) ** 2 + (bulk_velocity[2] - clump["gas", "velocity_z"]) ** 2 ) ).sum() ) if use_thermal_energy: kinetic += ( clump["gas", "mass"] * clump["gas", "specific_thermal_energy"] ).sum() if use_particles: kinetic += ( 0.5 * ( clump["all", "particle_mass"] * ( (bulk_velocity[0] - clump["all", "particle_velocity_x"]) ** 2 + (bulk_velocity[1] - clump["all", "particle_velocity_y"]) ** 2 + (bulk_velocity[2] - clump["all", "particle_velocity_z"]) ** 2 ) ).sum() ) if use_particles: m = np.concatenate( [clump["gas", "mass"].in_cgs(), clump["all", "particle_mass"].in_cgs()] ) px = np.concatenate( [clump["index", "x"].in_cgs(), clump["all", "particle_position_x"].in_cgs()] ) py = np.concatenate( [clump["index", "y"].in_cgs(), clump["all", "particle_position_y"].in_cgs()] ) pz = np.concatenate( [clump["index", "z"].in_cgs(), clump["all", "particle_position_z"].in_cgs()] ) else: m = clump["gas", "mass"].in_cgs() px = clump["index", "x"].in_cgs() py = clump["index", "y"].in_cgs() pz = clump["index", "z"].in_cgs() potential = clump.data.ds.quan( G * gravitational_binding_energy( m, px, py, pz, truncate, (kinetic / G).in_cgs(), num_threads=num_threads ), kinetic.in_cgs().units, ) if truncate and potential >= kinetic: return True return potential >= kinetic add_validator("gravitationally_bound", _gravitationally_bound) def _min_cells(clump, n_cells): "True if clump has a minimum number of cells." return clump["index", "ones"].size >= n_cells add_validator("min_cells", _min_cells) yt-project-yt-f043ac8/yt/data_objects/level_sets/contour_finder.py000066400000000000000000000052111510711153200254660ustar00rootroot00000000000000from collections import defaultdict import numpy as np from yt.funcs import get_pbar, mylog from yt.utilities.lib.contour_finding import ( ContourTree, TileContourTree, link_node_contours, update_joins, ) from yt.utilities.lib.partitioned_grid import PartitionedGrid def identify_contours(data_source, field, min_val, max_val, cached_fields=None): tree = ContourTree() gct = TileContourTree(min_val, max_val) total_contours = 0 contours = {} node_ids = [] DLE = data_source.ds.domain_left_edge masks = {g.id: m for g, m in data_source.blocks} for g, node, (sl, dims, gi) in data_source.tiles.slice_traverse(): g.field_parameters.update(data_source.field_parameters) node.node_ind = len(node_ids) nid = node.node_id node_ids.append(nid) values = g[field][sl].astype("float64") contour_ids = np.zeros(dims, "int64") - 1 mask = masks[g.id][sl].astype("uint8") total_contours += gct.identify_contours( values, contour_ids, mask, total_contours ) new_contours = tree.cull_candidates(contour_ids) tree.add_contours(new_contours) # Now we can create a partitioned grid with the contours. LE = (DLE + g.dds * gi).in_units("code_length").ndarray_view() RE = LE + (dims * g.dds).in_units("code_length").ndarray_view() pg = PartitionedGrid( g.id, [contour_ids.view("float64")], mask, LE, RE, dims.astype("int64") ) contours[nid] = (g.Level, node.node_ind, pg, sl) node_ids = np.array(node_ids, dtype="int64") if node_ids.size == 0: return 0, {} trunk = data_source.tiles.tree.trunk mylog.info("Linking node (%s) contours.", len(contours)) link_node_contours(trunk, contours, tree, node_ids) mylog.info("Linked.") # joins = tree.cull_joins(bt) # tree.add_joins(joins) joins = tree.export() contour_ids = defaultdict(list) pbar = get_pbar("Updating joins ... ", len(contours)) final_joins = np.unique(joins[:, 1]) for i, nid in enumerate(sorted(contours)): level, node_ind, pg, sl = contours[nid] ff = pg.my_data[0].view("int64") update_joins(joins, ff, final_joins) contour_ids[pg.parent_grid_id].append((sl, ff)) pbar.update(i + 1) pbar.finish() rv = {} rv.update(contour_ids) # NOTE: Because joins can appear in both a "final join" and a subsequent # "join", we can't know for sure how many unique joins there are without # checking if no cells match or doing an expensive operation checking for # the unique set of final join values. return final_joins.size, rv yt-project-yt-f043ac8/yt/data_objects/level_sets/tests/000077500000000000000000000000001510711153200232375ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/level_sets/tests/__init__.py000066400000000000000000000000001510711153200253360ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/level_sets/tests/test_clump_finding.py000066400000000000000000000136621510711153200274760ustar00rootroot00000000000000import os import shutil import tempfile import numpy as np from numpy.testing import assert_array_equal, assert_equal from yt.data_objects.level_sets.api import Clump, add_clump_info, find_clumps from yt.data_objects.level_sets.clump_info_items import clump_info_registry from yt.fields.derived_field import ValidateParameter from yt.loaders import load, load_uniform_grid from yt.testing import requires_file, requires_module from yt.utilities.answer_testing.framework import data_dir_load def test_clump_finding(): n_c = 8 n_p = 1 dims = (n_c, n_c, n_c) density = np.ones(dims) high_rho = 10.0 # add a couple disconnected density enhancements density[2, 2, 2] = high_rho density[6, 6, 6] = high_rho # put a particle at the center of one of them dx = 1.0 / n_c px = 2.5 * dx * np.ones(n_p) data = { "density": density, "particle_mass": np.ones(n_p), "particle_position_x": px, "particle_position_y": px, "particle_position_z": px, } ds = load_uniform_grid(data, dims) ad = ds.all_data() master_clump = Clump(ad, ("gas", "density")) master_clump.add_validator("min_cells", 1) def _total_volume(clump): total_vol = clump.data.quantities.total_quantity( [("index", "cell_volume")] ).in_units("cm**3") return "Cell Volume: %6e cm**3.", total_vol add_clump_info("total_volume", _total_volume) master_clump.add_info_item("total_volume") find_clumps(master_clump, 0.5, 2.0 * high_rho, 10.0) # there should be two children assert_equal(len(master_clump.children), 2) leaf_clumps = master_clump.leaves for l in leaf_clumps: keys = l.info.keys() assert "total_cells" in keys assert "cell_mass" in keys assert "max_grid_level" in keys assert "total_volume" in keys # two leaf clumps assert_equal(len(leaf_clumps), 2) # check some clump fields assert_equal(master_clump.children[0]["gas", "density"][0].size, 1) assert_equal( master_clump.children[0]["gas", "density"][0], ad["gas", "density"].max() ) assert_equal(master_clump.children[0]["all", "particle_mass"].size, 1) assert_array_equal( master_clump.children[0]["all", "particle_mass"], ad["all", "particle_mass"] ) assert_equal(master_clump.children[1]["gas", "density"][0].size, 1) assert_equal( master_clump.children[1]["gas", "density"][0], ad["gas", "density"].max() ) assert_equal(master_clump.children[1]["all", "particle_mass"].size, 0) # clean up global registry to avoid polluting other tests del clump_info_registry["total_volume"] i30 = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(i30) def test_clump_tree_save(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(i30) data_source = ds.disk([0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8, "kpc"), (1, "kpc")) field = ("gas", "density") step = 2.0 c_min = 10 ** np.floor(np.log10(data_source[field]).min()) c_max = 10 ** np.floor(np.log10(data_source[field]).max() + 1) master_clump = Clump(data_source, field) master_clump.add_info_item("center_of_mass") master_clump.add_validator("min_cells", 20) find_clumps(master_clump, c_min, c_max, step) leaf_clumps = master_clump.leaves fn = master_clump.save_as_dataset( fields=[ ("gas", "density"), ("index", "x"), ("index", "y"), ("index", "z"), ("all", "particle_mass"), ] ) ds2 = load(fn) # compare clumps in the tree t1 = list(master_clump) t2 = list(ds2.tree) mt1 = ds.arr([c.info["cell_mass"][1] for c in t1]) mt2 = ds2.arr([c["clump", "cell_mass"] for c in t2]) it1 = np.argsort(mt1).astype("int64") it2 = np.argsort(mt2).astype("int64") assert_array_equal(mt1[it1], mt2[it2]) for i1, i2 in zip(it1, it2, strict=True): ct1 = t1[i1] ct2 = t2[i2] assert_array_equal(ct1["gas", "density"], ct2["grid", "density"]) assert_array_equal(ct1["all", "particle_mass"], ct2["all", "particle_mass"]) # compare leaf clumps c1 = list(leaf_clumps) c2 = list(ds2.leaves) mc1 = ds.arr([c.info["cell_mass"][1] for c in c1]) mc2 = ds2.arr([c["clump", "cell_mass"] for c in c2]) ic1 = np.argsort(mc1).astype("int64") ic2 = np.argsort(mc2).astype("int64") assert_array_equal(mc1[ic1], mc2[ic2]) os.chdir(curdir) shutil.rmtree(tmpdir) i30 = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(i30) def test_clump_field_parameters(): """ Make sure clump finding on fields with field parameters works. """ def _also_density(field, data): factor = data.get_field_parameter("factor") return factor * data["gas", "density"] ds = data_dir_load(i30) ds.add_field( ("gas", "also_density"), function=_also_density, units=ds.fields.gas.density.units, sampling_type="cell", validators=[ValidateParameter("factor")], ) data_source = ds.disk([0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8, "kpc"), (1, "kpc")) data_source.set_field_parameter("factor", 1) step = 2.0 field = ("gas", "density") c_min = 10 ** np.floor(np.log10(data_source[field]).min()) c_max = 10 ** np.floor(np.log10(data_source[field]).max() + 1) master_clump_1 = Clump(data_source, ("gas", "density")) master_clump_1.add_validator("min_cells", 20) master_clump_2 = Clump(data_source, ("gas", "also_density")) master_clump_2.add_validator("min_cells", 20) find_clumps(master_clump_1, c_min, c_max, step) find_clumps(master_clump_2, c_min, c_max, step) leaf_clumps_1 = master_clump_1.leaves leaf_clumps_2 = master_clump_2.leaves for c1, c2 in zip(leaf_clumps_1, leaf_clumps_2, strict=True): assert_array_equal(c1["gas", "density"], c2["gas", "density"]) yt-project-yt-f043ac8/yt/data_objects/particle_filters.py000066400000000000000000000145551510711153200236470ustar00rootroot00000000000000import copy from contextlib import contextmanager from yt.fields.field_info_container import NullFunc, TranslationFunc from yt.funcs import mylog from yt.utilities.exceptions import YTIllDefinedFilter # One to one mapping filter_registry: dict[str, "ParticleFilter"] = {} class DummyFieldInfo: particle_type = True sampling_type = "particle" dfi = DummyFieldInfo() class ParticleFilter: def __init__(self, name, function, requires, filtered_type): self.name = name self.function = function self.requires = requires[:] self.filtered_type = filtered_type @contextmanager def apply(self, dobj): with dobj._chunked_read(dobj._current_chunk): with dobj._field_type_state(self.filtered_type, dfi): # We won't be storing the field data from the whole read, so we # start by filtering now. filter = self.function(self, dobj) yield # Retain a reference here, and we'll filter all appropriate fields # later. fd = dobj.field_data for f, tr in fd.items(): if f[0] != self.filtered_type: continue if tr.shape != filter.shape and tr.shape[0] != filter.shape[0]: raise YTIllDefinedFilter(self, tr.shape, filter.shape) else: d = tr[filter] dobj.field_data[self.name, f[1]] = d def available(self, field_list): # Note that this assumes that all the fields in field_list have the # same form as the 'requires' attributes. This won't be true if the # fields are implicitly "all" or something. return all((self.filtered_type, field) in field_list for field in self.requires) def missing(self, field_list): return [ (self.filtered_type, field) for field in self.requires if (self.filtered_type, field) not in field_list ] def wrap_func(self, field_name, old_fi): new_fi = copy.copy(old_fi) new_fi.name = (self.name, field_name[1]) if old_fi._function == NullFunc: new_fi._function = TranslationFunc(old_fi.name) # Marking the field as inherited new_fi._inherited_particle_filter = True return new_fi def add_particle_filter(name, function, requires=None, filtered_type="all"): r"""Create a new particle filter in the global namespace of filters A particle filter is a short name that corresponds to an algorithm for filtering a set of particles into a subset. This is useful for creating new particle types based on a cut on a particle field, such as particle mass, ID or type. After defining a new filter, it still needs to be added to the dataset by calling :func:`~yt.data_objects.static_output.add_particle_filter`. .. note:: Alternatively, you can make use of the :func:`~yt.data_objects.particle_filters.particle_filter` decorator to define a new particle filter. Parameters ---------- name : string The name of the particle filter. New particle fields with particle type set by this name will be added to any dataset that enables this particle filter. function : reference to a function The function that defines the particle filter. The function should accept two arguments: a reference to a particle filter object and a reference to an abstract yt data object. See the example below. requires : a list of field names A list of field names required by the particle filter definition. filtered_type : string The name of the particle type to be filtered. Examples -------- >>> import yt >>> def _stars(pfilter, data): ... return data[pfilter.filtered_type, "particle_type"] == 2 >>> yt.add_particle_filter( ... "stars", function=_stars, filtered_type="all", requires=["particle_type"] ... ) >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ds.add_particle_filter("stars") >>> ad = ds.all_data() >>> print(ad["stars", "particle_mass"]) [ 1.68243760e+38 1.65690882e+38 1.65813321e+38 ..., 2.04238266e+38 2.04523901e+38 2.04770938e+38] g """ if requires is None: requires = [] filter = ParticleFilter(name, function, requires, filtered_type) if filter_registry.get(name, None) is not None: mylog.warning("The %s particle filter already exists. Overriding.", name) filter_registry[name] = filter def particle_filter(name=None, requires=None, filtered_type="all"): r"""A decorator that adds a new particle filter A particle filter is a short name that corresponds to an algorithm for filtering a set of particles into a subset. This is useful for creating new particle types based on a cut on a particle field, such as particle mass, ID or type. .. note:: Alternatively, you can make use of the :func:`~yt.data_objects.particle_filters.add_particle_filter` function to define a new particle filter using a more declarative syntax. Parameters ---------- name : string The name of the particle filter. New particle fields with particle type set by this name will be added to any dataset that enables this particle filter. If not set, the name will be inferred from the name of the filter function. requires : a list of field names A list of field names required by the particle filter definition. filtered_type : string The name of the particle type to be filtered. Examples -------- >>> import yt >>> # define a filter named "stars" >>> @yt.particle_filter(requires=["particle_type"], filtered_type="all") ... def stars(pfilter, data): ... return data[pfilter.filtered_type, "particle_type"] == 2 >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ds.add_particle_filter("stars") >>> ad = ds.all_data() >>> print(ad["stars", "particle_mass"]) [ 1.68243760e+38 1.65690882e+38 1.65813321e+38 ..., 2.04238266e+38 2.04523901e+38 2.04770938e+38] g """ def wrapper(function): if name is None: used_name = function.__name__ else: used_name = name return add_particle_filter(used_name, function, requires, filtered_type) return wrapper yt-project-yt-f043ac8/yt/data_objects/particle_trajectories.py000066400000000000000000000350431510711153200246700ustar00rootroot00000000000000import numpy as np from yt.config import ytcfg from yt.data_objects.field_data import YTFieldData from yt.funcs import get_pbar, mylog from yt.units.yt_array import array_like_field from yt.utilities.exceptions import YTIllDefinedParticleData from yt.utilities.lib.particle_mesh_operations import CICSample_3 from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_root_only class ParticleTrajectories: r"""A collection of particle trajectories in time over a series of datasets. Parameters ---------- outputs : ~yt.data_objects.time_series.DatasetSeries DatasetSeries object from which to draw the particles. indices : array_like An integer array of particle indices whose trajectories we want to track. If they are not sorted they will be sorted. fields : list of strings, optional A set of fields that is retrieved when the trajectory collection is instantiated. Default: None (will default to the fields 'particle_position_x', 'particle_position_y', 'particle_position_z') suppress_logging : boolean Suppress yt's logging when iterating over the simulation time series. Default: False ptype : str, optional Only use this particle type. Default: None, which uses all particle type. Examples -------- >>> my_fns = glob.glob("orbit_hdf5_chk_00[0-9][0-9]") >>> my_fns.sort() >>> fields = [ ... ("all", "particle_position_x"), ... ("all", "particle_position_y"), ... ("all", "particle_position_z"), ... ("all", "particle_velocity_x"), ... ("all", "particle_velocity_y"), ... ("all", "particle_velocity_z"), ... ] >>> ds = load(my_fns[0]) >>> init_sphere = ds.sphere(ds.domain_center, (0.5, "unitary")) >>> indices = init_sphere["all", "particle_index"].astype("int64") >>> ts = DatasetSeries(my_fns) >>> trajs = ts.particle_trajectories(indices, fields=fields) >>> for t in trajs: ... print( ... t["all", "particle_velocity_x"].max(), ... t["all", "particle_velocity_x"].min(), ... ) """ def __init__( self, outputs, indices, fields=None, suppress_logging=False, ptype=None ): indices.sort() # Just in case the caller wasn't careful self.field_data = YTFieldData() self.data_series = outputs self.masks = [] self.sorts = [] self.array_indices = [] self.indices = indices self.num_indices = len(indices) self.num_steps = len(outputs) self.times = [] self.suppress_logging = suppress_logging self.ptype = ptype if ptype else "all" if fields is None: fields = [] if self.suppress_logging: old_level = int(ytcfg.get("yt", "log_level")) mylog.setLevel(40) ds_first = self.data_series[0] dd_first = ds_first.all_data() fds = {} for field in ( "particle_index", "particle_position_x", "particle_position_y", "particle_position_z", ): fds[field] = dd_first._determine_fields((self.ptype, field))[0] # Note: we explicitly pass dynamic=False to prevent any change in piter from # breaking the assumption that the same processors load the same datasets my_storage = {} pbar = get_pbar("Constructing trajectory information", len(self.data_series)) for i, (sto, ds) in enumerate( self.data_series.piter(storage=my_storage, dynamic=False) ): dd = ds.all_data() newtags = dd[fds["particle_index"]].d.astype("int64") mask = np.isin(newtags, indices, assume_unique=True) sort = np.argsort(newtags[mask]) array_indices = np.where(np.isin(indices, newtags, assume_unique=True))[0] self.array_indices.append(array_indices) self.masks.append(mask) self.sorts.append(sort) pfields = {} for field in (f"particle_position_{ax}" for ax in "xyz"): pfields[field] = dd[fds[field]].ndarray_view()[mask][sort] sto.result_id = ds.parameter_filename sto.result = (ds.current_time, array_indices, pfields) pbar.update(i + 1) pbar.finish() if self.suppress_logging: mylog.setLevel(old_level) sorted_storage = sorted(my_storage.items()) _fn, (time, *_) = sorted_storage[0] time_units = time.units times = [time.to(time_units) for _fn, (time, *_) in sorted_storage] self.times = self.data_series[0].arr([time.value for time in times], time_units) self.particle_fields = [] output_field = np.empty((self.num_indices, self.num_steps)) output_field.fill(np.nan) for field in (f"particle_position_{ax}" for ax in "xyz"): for i, (_fn, (_time, indices, pfields)) in enumerate(sorted_storage): try: # This will fail if particles ids are # duplicate. This is due to the fact that the rhs # would then have a different shape as the lhs output_field[indices, i] = pfields[field] except ValueError as e: raise YTIllDefinedParticleData( "This dataset contains duplicate particle indices!" ) from e self.field_data[fds[field]] = array_like_field( dd_first, output_field.copy(), fds[field] ) self.particle_fields.append(field) # Instantiate fields the caller requested self._get_data(fields) def has_key(self, key): return key in self.field_data def keys(self): return self.field_data.keys() def __getitem__(self, key): """ Get the field associated with key. """ if key == "particle_time": return self.times if key not in self.field_data: self._get_data([key]) return self.field_data[key] def __setitem__(self, key, val): """ Sets a field to be some other value. """ self.field_data[key] = val def __delitem__(self, key): """ Delete the field from the trajectory """ del self.field_data[key] def __iter__(self): """ This iterates over the trajectories for the different particles, returning dicts of fields for each trajectory """ for idx in range(self.num_indices): traj = {} traj["particle_index"] = self.indices[idx] traj["particle_time"] = self.times for field in self.field_data.keys(): traj[field] = self[field][idx, :] yield traj def __len__(self): """ The number of individual trajectories """ return self.num_indices def add_fields(self, fields): """ Add a list of fields to an existing trajectory Parameters ---------- fields : list of strings A list of fields to be added to the current trajectory collection. Examples ________ >>> trajs = ParticleTrajectories(my_fns, indices) >>> trajs.add_fields([("all", "particle_mass"), ("all", "particle_gpot")]) """ self._get_data(fields) def _get_data(self, fields): """ Get a list of fields to include in the trajectory collection. The trajectory collection itself is a dict of 2D numpy arrays, with shape (num_indices, num_steps) """ missing_fields = [field for field in fields if field not in self.field_data] if not missing_fields: return if self.suppress_logging: old_level = int(ytcfg.get("yt", "log_level")) mylog.setLevel(40) ds_first = self.data_series[0] dd_first = ds_first.all_data() fds = {} new_particle_fields = [] for field in missing_fields: fds[field] = dd_first._determine_fields(field)[0] if field not in self.particle_fields: ftype = fds[field][0] if ftype in ds_first.particle_types: self.particle_fields.append(field) new_particle_fields.append(field) grid_fields = [ field for field in missing_fields if field not in self.particle_fields ] step = 0 fields_str = ", ".join(str(f) for f in missing_fields) pbar = get_pbar( f"Generating [{fields_str}] fields in trajectories", self.num_steps, ) # Note: we explicitly pass dynamic=False to prevent any change in piter from # breaking the assumption that the same processors load the same datasets my_storage = {} for i, (sto, ds) in enumerate( self.data_series.piter(storage=my_storage, dynamic=False) ): mask = self.masks[i] sort = self.sorts[i] pfield = {} if new_particle_fields: # there's at least one particle field dd = ds.all_data() for field in new_particle_fields: # This is easy... just get the particle fields pfield[field] = dd[fds[field]].d[mask][sort] if grid_fields: # This is hard... must loop over grids for field in grid_fields: pfield[field] = np.zeros(self.num_indices) x = self["particle_position_x"][:, step].d y = self["particle_position_y"][:, step].d z = self["particle_position_z"][:, step].d particle_grids, particle_grid_inds = ds.index._find_points(x, y, z) # This will fail for non-grid index objects for grid in particle_grids: cube = grid.retrieve_ghost_zones(1, grid_fields) for field in grid_fields: CICSample_3( x, y, z, pfield[field], self.num_indices, cube[fds[field]], np.array(grid.LeftEdge, dtype="float64"), np.array(grid.ActiveDimensions, dtype="int32"), grid.dds[0], ) sto.result_id = ds.parameter_filename sto.result = (self.array_indices[i], pfield) pbar.update(step) step += 1 pbar.finish() output_field = np.empty((self.num_indices, self.num_steps)) output_field.fill(np.nan) for field in missing_fields: fd = fds[field] for i, (_fn, (indices, pfield)) in enumerate(sorted(my_storage.items())): output_field[indices, i] = pfield[field] self.field_data[field] = array_like_field(dd_first, output_field.copy(), fd) if self.suppress_logging: mylog.setLevel(old_level) def trajectory_from_index(self, index): """ Retrieve a single trajectory corresponding to a specific particle index Parameters ---------- index : int This defines which particle trajectory from the ParticleTrajectories object will be returned. Returns ------- A dictionary corresponding to the particle's trajectory and the fields along that trajectory Examples -------- >>> import matplotlib.pyplot as plt >>> trajs = ParticleTrajectories(my_fns, indices) >>> traj = trajs.trajectory_from_index(indices[0]) >>> plt.plot( ... traj["all", "particle_time"], ... traj["all", "particle_position_x"], ... "-x", ... ) >>> plt.savefig("orbit") """ mask = np.isin(self.indices, (index,), assume_unique=True) if not np.any(mask): print("The particle index %d is not in the list!" % (index)) raise IndexError fields = sorted(self.field_data.keys()) traj = {} traj[self.ptype, "particle_time"] = self.times traj[self.ptype, "particle_index"] = index for field in fields: traj[field] = self[field][mask, :][0] return traj @parallel_root_only def write_out(self, filename_base): """ Write out particle trajectories to tab-separated ASCII files (one for each trajectory) with the field names in the file header. Each file is named with a basename and the index number. Parameters ---------- filename_base : string The prefix for the outputted ASCII files. Examples -------- >>> trajs = ParticleTrajectories(my_fns, indices) >>> trajs.write_out("orbit_trajectory") """ fields = sorted(self.field_data.keys()) num_fields = len(fields) first_str = "# particle_time\t" + "\t".join(fields) + "\n" template_str = "%g\t" * num_fields + "%g\n" for ix in range(self.num_indices): outlines = [first_str] for it in range(self.num_steps): outlines.append( template_str % tuple( [self.times[it]] + [self[field][ix, it] for field in fields] ) ) fid = open(filename_base + "_%d.dat" % self.indices[ix], "w") fid.writelines(outlines) fid.close() del fid @parallel_root_only def write_out_h5(self, filename): """ Write out all the particle trajectories to a single HDF5 file that contains the indices, the times, and the 2D array for each field individually Parameters ---------- filename : string The output filename for the HDF5 file Examples -------- >>> trajs = ParticleTrajectories(my_fns, indices) >>> trajs.write_out_h5("orbit_trajectories") """ fid = h5py.File(filename, mode="w") fid.create_dataset("particle_indices", dtype=np.int64, data=self.indices) fid.close() self.times.write_hdf5(filename, dataset_name="particle_times") fields = sorted(self.field_data.keys()) for field in fields: self[field].write_hdf5(filename, dataset_name=f"{field}") yt-project-yt-f043ac8/yt/data_objects/particle_unions.py000066400000000000000000000005131510711153200234770ustar00rootroot00000000000000from yt._maintenance.deprecation import issue_deprecation_warning from .unions import ParticleUnion # noqa: F401 issue_deprecation_warning( "Importing ParticleUnion from yt.data_objects.particle_unions is deprecated. " "Please import this class from yt.data_objects.unions instead", stacklevel=3, since="4.2", ) yt-project-yt-f043ac8/yt/data_objects/profiles.py000066400000000000000000001562201510711153200221330ustar00rootroot00000000000000import numpy as np from more_itertools import collapse from yt.data_objects.field_data import YTFieldData from yt.fields.derived_field import DerivedField from yt.frontends.ytdata.utilities import save_as_dataset from yt.funcs import get_output_filename, is_sequence, iter_fields, mylog from yt.units.unit_object import Unit # type: ignore from yt.units.yt_array import YTQuantity, array_like_field from yt.utilities.exceptions import ( YTIllDefinedBounds, YTIllDefinedProfile, YTProfileDataShape, ) from yt.utilities.lib.misc_utilities import ( new_bin_profile1d, new_bin_profile2d, new_bin_profile3d, ) from yt.utilities.lib.particle_mesh_operations import CICDeposit_2, NGPDeposit_2 from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, parallel_objects, ) def _sanitize_min_max_units(amin, amax, finfo, registry): # returns a copy of amin and amax, converted to finfo's output units umin = getattr(amin, "units", None) umax = getattr(amax, "units", None) if umin is None: umin = Unit(finfo.output_units, registry=registry) rmin = YTQuantity(amin, umin) else: rmin = amin.in_units(finfo.output_units) if umax is None: umax = Unit(finfo.output_units, registry=registry) rmax = YTQuantity(amax, umax) else: rmax = amax.in_units(finfo.output_units) return rmin, rmax def preserve_source_parameters(func): def save_state(*args, **kwargs): # Temporarily replace the 'field_parameters' for a # grid with the 'field_parameters' for the data source prof = args[0] source = args[1] if hasattr(source, "field_parameters"): old_params = source.field_parameters source.field_parameters = prof._data_source.field_parameters tr = func(*args, **kwargs) source.field_parameters = old_params else: tr = func(*args, **kwargs) return tr return save_state class ProfileFieldAccumulator: def __init__(self, n_fields, size): shape = size + (n_fields,) self.values = np.zeros(shape, dtype="float64") self.mvalues = np.zeros(shape, dtype="float64") self.qvalues = np.zeros(shape, dtype="float64") self.used = np.zeros(size, dtype="bool") self.weight_values = np.zeros(size, dtype="float64") class ProfileND(ParallelAnalysisInterface): """The profile object class""" def __init__(self, data_source, weight_field=None): self.data_source = data_source self.ds = data_source.ds self.field_map = {} self.field_info = {} self.field_data = YTFieldData() if weight_field is not None: self.standard_deviation = YTFieldData() weight_field = self.data_source._determine_fields(weight_field)[0] else: self.standard_deviation = None self.weight_field = weight_field self.field_units = {} ParallelAnalysisInterface.__init__(self, comm=data_source.comm) def add_fields(self, fields): """Add fields to profile Parameters ---------- fields : list of field names A list of fields to create profile histograms for """ fields = self.data_source._determine_fields(fields) for f in fields: self.field_info[f] = self.data_source.ds.field_info[f] temp_storage = ProfileFieldAccumulator(len(fields), self.size) citer = self.data_source.chunks([], "io") for chunk in parallel_objects(citer): self._bin_chunk(chunk, fields, temp_storage) self._finalize_storage(fields, temp_storage) def set_field_unit(self, field, new_unit): """Sets a new unit for the requested field Parameters ---------- field : string or field tuple The name of the field that is to be changed. new_unit : string or Unit object The name of the new unit. """ if field in self.field_units: self.field_units[field] = Unit(new_unit, registry=self.ds.unit_registry) else: fd = self.field_map[field] if fd in self.field_units: self.field_units[fd] = Unit(new_unit, registry=self.ds.unit_registry) else: raise KeyError(f"{field} not in profile!") def _finalize_storage(self, fields, temp_storage): # We use our main comm here # This also will fill _field_data for i, _field in enumerate(fields): # q values are returned as q * weight but we want just q temp_storage.qvalues[..., i][temp_storage.used] /= ( temp_storage.weight_values[temp_storage.used] ) # get the profile data from all procs all_store = {self.comm.rank: temp_storage} all_store = self.comm.par_combine_object(all_store, "join", datatype="dict") all_val = np.zeros_like(temp_storage.values) all_mean = np.zeros_like(temp_storage.mvalues) all_std = np.zeros_like(temp_storage.qvalues) all_weight = np.zeros_like(temp_storage.weight_values) all_used = np.zeros_like(temp_storage.used, dtype="bool") # Combine the weighted mean and standard deviation from each processor. # For two samples with total weight, mean, and standard deviation # given by w, m, and s, their combined mean and standard deviation are: # m12 = (m1 * w1 + m2 * w2) / (w1 + w2) # s12 = (m1 * (s1**2 + (m1 - m12)**2) + # m2 * (s2**2 + (m2 - m12)**2)) / (w1 + w2) # Here, the mvalues are m and the qvalues are s**2. for p in sorted(all_store.keys()): all_used += all_store[p].used old_mean = all_mean.copy() old_weight = all_weight.copy() all_weight[all_store[p].used] += all_store[p].weight_values[ all_store[p].used ] for i, _field in enumerate(fields): all_val[..., i][all_store[p].used] += all_store[p].values[..., i][ all_store[p].used ] all_mean[..., i][all_store[p].used] = ( all_mean[..., i] * old_weight + all_store[p].mvalues[..., i] * all_store[p].weight_values )[all_store[p].used] / all_weight[all_store[p].used] all_std[..., i][all_store[p].used] = ( old_weight * (all_std[..., i] + (old_mean[..., i] - all_mean[..., i]) ** 2) + all_store[p].weight_values * ( all_store[p].qvalues[..., i] + (all_store[p].mvalues[..., i] - all_mean[..., i]) ** 2 ) )[all_store[p].used] / all_weight[all_store[p].used] all_std = np.sqrt(all_std) del all_store self.used = all_used blank = ~all_used self.weight = all_weight self.weight[blank] = 0.0 for i, field in enumerate(fields): if self.weight_field is None: self.field_data[field] = array_like_field( self.data_source, all_val[..., i], field ) else: self.field_data[field] = array_like_field( self.data_source, all_mean[..., i], field ) self.standard_deviation[field] = array_like_field( self.data_source, all_std[..., i], field ) self.standard_deviation[field][blank] = 0.0 self.weight = array_like_field( self.data_source, self.weight, self.weight_field ) self.field_data[field][blank] = 0.0 self.field_units[field] = self.field_data[field].units if isinstance(field, tuple): self.field_map[field[1]] = field else: self.field_map[field] = field def _bin_chunk(self, chunk, fields, storage): raise NotImplementedError def _filter(self, bin_fields): # cut_points is set to be everything initially, but # we also want to apply a filtering based on min/max pfilter = np.ones(bin_fields[0].shape, dtype="bool") for (mi, ma), data in zip(self.bounds, bin_fields, strict=True): pfilter &= data > mi pfilter &= data < ma return pfilter, [data[pfilter] for data in bin_fields] def _get_data(self, chunk, fields): # We are using chunks now, which will manage the field parameters and # the like. bin_fields = [chunk[bf] for bf in self.bin_fields] for i in range(1, len(bin_fields)): if bin_fields[0].shape != bin_fields[i].shape: raise YTProfileDataShape( self.bin_fields[0], bin_fields[0].shape, self.bin_fields[i], bin_fields[i].shape, ) # We want to make sure that our fields are within the bounds of the # binning pfilter, bin_fields = self._filter(bin_fields) if not np.any(pfilter): return None arr = np.zeros((bin_fields[0].size, len(fields)), dtype="float64") for i, field in enumerate(fields): if pfilter.shape != chunk[field].shape: raise YTProfileDataShape( self.bin_fields[0], bin_fields[0].shape, field, chunk[field].shape ) units = chunk.ds.field_info[field].output_units arr[:, i] = chunk[field][pfilter].in_units(units) if self.weight_field is not None: if pfilter.shape != chunk[self.weight_field].shape: raise YTProfileDataShape( self.bin_fields[0], bin_fields[0].shape, self.weight_field, chunk[self.weight_field].shape, ) units = chunk.ds.field_info[self.weight_field].output_units weight_data = chunk[self.weight_field].in_units(units) else: weight_data = np.ones(pfilter.shape, dtype="float64") weight_data = weight_data[pfilter] # So that we can pass these into return arr, weight_data, bin_fields def __getitem__(self, field): if field in self.field_data: fname = field else: # deal with string vs tuple field names and attempt to guess which field # we are supposed to be talking about fname = self.field_map.get(field, None) if isinstance(field, tuple): fname = self.field_map.get(field[1], None) if fname != field: raise KeyError( f"Asked for field '{field}' but only have data for " f"fields '{list(self.field_data.keys())}'" ) elif isinstance(field, DerivedField): fname = self.field_map.get(field.name[1], None) if fname is None: raise KeyError(field) if getattr(self, "fractional", False): return self.field_data[fname] else: return self.field_data[fname].in_units(self.field_units[fname]) def items(self): return [(k, self[k]) for k in self.field_data.keys()] def keys(self): return self.field_data.keys() def __iter__(self): return sorted(self.items()) def _get_bins(self, mi, ma, n, take_log): if take_log: ret = np.logspace(np.log10(mi), np.log10(ma), n + 1) # at this point ret[0] and ret[-1] are not exactly equal to # mi and ma due to round-off error. Let's force them to be # mi and ma exactly to avoid incorrectly discarding cells near # the edges. See Issue #1300. ret[0], ret[-1] = mi, ma return ret else: return np.linspace(mi, ma, n + 1) def save_as_dataset(self, filename=None): r"""Export a profile to a reloadable yt dataset. This function will take a profile and output a dataset containing all relevant fields. The resulting dataset can be reloaded as a yt dataset. Parameters ---------- filename : str, optional The name of the file to be written. If None, the name will be a combination of the original dataset plus the type of object, e.g., Profile1D. Returns ------- filename : str The name of the file that has been created. Examples -------- >>> import yt >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") >>> ad = ds.all_data() >>> profile = yt.create_profile( ... ad, ... [("gas", "density"), ("gas", "temperature")], ... ("gas", "mass"), ... weight_field=None, ... n_bins=(128, 128), ... ) >>> fn = profile.save_as_dataset() >>> prof_ds = yt.load(fn) >>> print(prof_ds.data["gas", "mass"]) (128, 128) >>> print(prof_ds.data["index", "x"].shape) # x bins as 1D array (128,) >>> print(prof_ds.data["gas", "density"]) # x bins as 2D array (128, 128) >>> p = yt.PhasePlot( ... prof_ds.data, ... ("gas", "density"), ... ("gas", "temperature"), ... ("gas", "mass"), ... weight_field=None, ... ) >>> p.save() """ keyword = f"{str(self.ds)}_{self.__class__.__name__}" filename = get_output_filename(filename, keyword, ".h5") args = ("field", "log") extra_attrs = { "data_type": "yt_profile", "profile_dimensions": self.size, "weight_field": self.weight_field, "fractional": self.fractional, "accumulation": self.accumulation, } data = {} data.update(self.field_data) data["weight"] = self.weight data["used"] = self.used.astype("float64") std = "standard_deviation" if self.weight_field is not None: std_data = getattr(self, std) data.update({(std, field[1]): std_data[field] for field in self.field_data}) dimensionality = 0 bin_data = [] for ax in "xyz": if hasattr(self, ax): dimensionality += 1 data[ax] = getattr(self, ax) bin_data.append(data[ax]) bin_field_name = f"{ax}_bins" data[bin_field_name] = getattr(self, bin_field_name) extra_attrs[f"{ax}_range"] = self.ds.arr( [data[bin_field_name][0], data[bin_field_name][-1]] ) for arg in args: key = f"{ax}_{arg}" extra_attrs[key] = getattr(self, key) bin_fields = np.meshgrid(*bin_data) for i, ax in enumerate("xyz"[:dimensionality]): data[getattr(self, f"{ax}_field")] = bin_fields[i] extra_attrs["dimensionality"] = dimensionality ftypes = {field: "data" for field in data if field[0] != std} if self.weight_field is not None: ftypes.update({(std, field[1]): std for field in self.field_data}) save_as_dataset( self.ds, filename, data, field_types=ftypes, extra_attrs=extra_attrs ) return filename class ProfileNDFromDataset(ProfileND): """ An ND profile object loaded from a ytdata dataset. """ def __init__(self, ds): ProfileND.__init__(self, ds.data, ds.parameters.get("weight_field", None)) self.fractional = ds.parameters.get("fractional", False) self.accumulation = ds.parameters.get("accumulation", False) exclude_fields = ["used", "weight"] for ax in "xyz"[: ds.dimensionality]: setattr(self, ax, ds.data["data", ax]) ax_bins = f"{ax}_bins" ax_field = f"{ax}_field" ax_log = f"{ax}_log" setattr(self, ax_bins, ds.data["data", ax_bins]) field_name = tuple(ds.parameters.get(ax_field, (None, None))) setattr(self, ax_field, field_name) self.field_info[field_name] = ds.field_info[field_name] setattr(self, ax_log, ds.parameters.get(ax_log, False)) exclude_fields.extend([ax, ax_bins, field_name[1]]) self.weight = ds.data["data", "weight"] self.used = ds.data["data", "used"].d.astype(bool) profile_fields = [ f for f in ds.field_list if f[1] not in exclude_fields and f[0] != "standard_deviation" ] for field in profile_fields: self.field_map[field[1]] = field self.field_data[field] = ds.data[field] self.field_info[field] = ds.field_info[field] self.field_units[field] = ds.data[field].units if ("standard_deviation", field[1]) in ds.field_list: self.standard_deviation[field] = ds.data["standard_deviation", field[1]] class Profile1D(ProfileND): """An object that represents a 1D profile. Parameters ---------- data_source : AMD3DData object The data object to be profiled x_field : string field name The field to profile as a function of x_n : integer The number of bins along the x direction. x_min : float The minimum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_max : float The maximum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_log : boolean Controls whether or not the bins for the x field are evenly spaced in linear (False) or log (True) space. weight_field : string field name The field to weight the profiled fields by. override_bins_x : array Array to set as xbins and ignore other parameters if set """ def __init__( self, data_source, x_field, x_n, x_min, x_max, x_log, weight_field=None, override_bins_x=None, ): super().__init__(data_source, weight_field) self.x_field = data_source._determine_fields(x_field)[0] self.field_info[self.x_field] = self.data_source.ds.field_info[self.x_field] self.x_log = x_log x_min, x_max = _sanitize_min_max_units( x_min, x_max, self.field_info[self.x_field], self.ds.unit_registry ) self.x_bins = array_like_field( data_source, self._get_bins(x_min, x_max, x_n, x_log), self.x_field ) if override_bins_x is not None: self.x_bins = array_like_field(data_source, override_bins_x, self.x_field) self.size = (self.x_bins.size - 1,) self.bin_fields = (self.x_field,) self.x = 0.5 * (self.x_bins[1:] + self.x_bins[:-1]) def _bin_chunk(self, chunk, fields, storage): rv = self._get_data(chunk, fields) if rv is None: return fdata, wdata, (bf_x,) = rv bf_x.convert_to_units(self.field_info[self.x_field].output_units) bin_ind = np.digitize(bf_x, self.x_bins) - 1 new_bin_profile1d( bin_ind, wdata, fdata, storage.weight_values, storage.values, storage.mvalues, storage.qvalues, storage.used, ) # We've binned it! def set_x_unit(self, new_unit): """Sets a new unit for the x field Parameters ---------- new_unit : string or Unit object The name of the new unit. """ self.x_bins.convert_to_units(new_unit) self.x = 0.5 * (self.x_bins[1:] + self.x_bins[:-1]) @property def bounds(self): return ((self.x_bins[0], self.x_bins[-1]),) def plot(self): r""" This returns a :class:`~yt.visualization.profile_plotter.ProfilePlot` with the fields that have been added to this object. """ from yt.visualization.profile_plotter import ProfilePlot return ProfilePlot.from_profiles(self) def _export_prep(self, fields, only_used): if only_used: idxs = self.used else: idxs = slice(None, None, None) if not only_used and not np.all(self.used): masked = True else: masked = False if fields is None: fields = self.field_data.keys() else: fields = self.data_source._determine_fields(fields) return idxs, masked, fields def to_dataframe(self, fields=None, only_used=False, include_std=False): r"""Export a profile object to a pandas DataFrame. This function will take a data object and construct from it and optionally a list of fields a pandas DataFrame object. If pandas is not importable, this will raise ImportError. Parameters ---------- fields : list of strings or tuple field names, default None If this is supplied, it is the list of fields to be exported into the DataFrame. If not supplied, whatever fields exist in the profile, along with the bin field, will be exported. only_used : boolean, default False If True, only the bins which have data will be exported. If False, all the bins will be exported, but the elements for those bins in the data arrays will be filled with NaNs. include_std : boolean, optional If True, include the standard deviation of the profile in the pandas DataFrame. It will appear in the table as the field name with "_stddev" appended, e.g. "velocity_x_stddev". Default: False Returns ------- df : :class:`~pandas.DataFrame` The data contained in the profile. Examples -------- >>> sp = ds.sphere("c", (0.1, "unitary")) >>> p = sp.profile( ... ("index", "radius"), [("gas", "density"), ("gas", "temperature")] ... ) >>> df1 = p.to_dataframe() >>> df2 = p.to_dataframe(fields=("gas", "density"), only_used=True) """ from yt.utilities.on_demand_imports import _pandas as pd idxs, masked, fields = self._export_prep(fields, only_used) pdata = {self.x_field[-1]: self.x[idxs]} for field in fields: pdata[field[-1]] = self[field][idxs] if include_std: pdata[f"{field[-1]}_stddev"] = self.standard_deviation[field][idxs] df = pd.DataFrame(pdata) if masked: mask = np.zeros(df.shape, dtype="bool") mask[~self.used, 1:] = True df.mask(mask, inplace=True) return df def to_astropy_table(self, fields=None, only_used=False, include_std=False): """ Export the profile data to a :class:`~astropy.table.table.QTable`, which is a Table object which is unit-aware. The QTable can then be exported to an ASCII file, FITS file, etc. See the AstroPy Table docs for more details: http://docs.astropy.org/en/stable/table/ Parameters ---------- fields : list of strings or tuple field names, default None If this is supplied, it is the list of fields to be exported into the DataFrame. If not supplied, whatever fields exist in the profile, along with the bin field, will be exported. only_used : boolean, optional If True, only the bins which are used are copied to the QTable as rows. If False, all bins are copied, but the bins which are not used are masked. Default: False include_std : boolean, optional If True, include the standard deviation of the profile in the AstroPy QTable. It will appear in the table as the field name with "_stddev" appended, e.g. "velocity_x_stddev". Default: False Returns ------- qt : :class:`~astropy.table.QTable` The data contained in the profile. Examples -------- >>> sp = ds.sphere("c", (0.1, "unitary")) >>> p = sp.profile( ... ("index", "radius"), [("gas", "density"), ("gas", "temperature")] ... ) >>> qt1 = p.to_astropy_table() >>> qt2 = p.to_astropy_table(fields=("gas", "density"), only_used=True) """ from astropy.table import QTable idxs, masked, fields = self._export_prep(fields, only_used) qt = QTable(masked=masked) qt[self.x_field[-1]] = self.x[idxs].to_astropy() if masked: qt[self.x_field[-1]].mask = self.used for field in fields: qt[field[-1]] = self[field][idxs].to_astropy() if masked: qt[field[-1]].mask = self.used if include_std: qt[f"{field[-1]}_stddev"] = self.standard_deviation[field][ idxs ].to_astropy() if masked: qt[f"{field[-1]}_stddev"].mask = self.used return qt class Profile1DFromDataset(ProfileNDFromDataset, Profile1D): """ A 1D profile object loaded from a ytdata dataset. """ def __init(self, ds): ProfileNDFromDataset.__init__(self, ds) class Profile2D(ProfileND): """An object that represents a 2D profile. Parameters ---------- data_source : AMD3DData object The data object to be profiled x_field : string field name The field to profile as a function of along the x axis. x_n : integer The number of bins along the x direction. x_min : float The minimum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_max : float The maximum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_log : boolean Controls whether or not the bins for the x field are evenly spaced in linear (False) or log (True) space. y_field : string field name The field to profile as a function of along the y axis y_n : integer The number of bins along the y direction. y_min : float The minimum value of the y profile field. If supplied without units, assumed to be in the output units for y_field. y_max : float The maximum value of the y profile field. If supplied without units, assumed to be in the output units for y_field. y_log : boolean Controls whether or not the bins for the y field are evenly spaced in linear (False) or log (True) space. weight_field : string field name The field to weight the profiled fields by. override_bins_x : array Array to set as xbins and ignore other parameters if set override_bins_y : array Array to set as ybins and ignore other parameters if set """ def __init__( self, data_source, x_field, x_n, x_min, x_max, x_log, y_field, y_n, y_min, y_max, y_log, weight_field=None, override_bins_x=None, override_bins_y=None, ): super().__init__(data_source, weight_field) # X self.x_field = data_source._determine_fields(x_field)[0] self.x_log = x_log self.field_info[self.x_field] = self.data_source.ds.field_info[self.x_field] x_min, x_max = _sanitize_min_max_units( x_min, x_max, self.field_info[self.x_field], self.ds.unit_registry ) self.x_bins = array_like_field( data_source, self._get_bins(x_min, x_max, x_n, x_log), self.x_field ) if override_bins_x is not None: self.x_bins = array_like_field(data_source, override_bins_x, self.x_field) # Y self.y_field = data_source._determine_fields(y_field)[0] self.y_log = y_log self.field_info[self.y_field] = self.data_source.ds.field_info[self.y_field] y_min, y_max = _sanitize_min_max_units( y_min, y_max, self.field_info[self.y_field], self.ds.unit_registry ) self.y_bins = array_like_field( data_source, self._get_bins(y_min, y_max, y_n, y_log), self.y_field ) if override_bins_y is not None: self.y_bins = array_like_field(data_source, override_bins_y, self.y_field) self.size = (self.x_bins.size - 1, self.y_bins.size - 1) self.bin_fields = (self.x_field, self.y_field) self.x = 0.5 * (self.x_bins[1:] + self.x_bins[:-1]) self.y = 0.5 * (self.y_bins[1:] + self.y_bins[:-1]) def _bin_chunk(self, chunk, fields, storage): rv = self._get_data(chunk, fields) if rv is None: return fdata, wdata, (bf_x, bf_y) = rv bf_x.convert_to_units(self.field_info[self.x_field].output_units) bin_ind_x = np.digitize(bf_x, self.x_bins) - 1 bf_y.convert_to_units(self.field_info[self.y_field].output_units) bin_ind_y = np.digitize(bf_y, self.y_bins) - 1 new_bin_profile2d( bin_ind_x, bin_ind_y, wdata, fdata, storage.weight_values, storage.values, storage.mvalues, storage.qvalues, storage.used, ) # We've binned it! def set_x_unit(self, new_unit): """Sets a new unit for the x field Parameters ---------- new_unit : string or Unit object The name of the new unit. """ self.x_bins.convert_to_units(new_unit) self.x = 0.5 * (self.x_bins[1:] + self.x_bins[:-1]) def set_y_unit(self, new_unit): """Sets a new unit for the y field Parameters ---------- new_unit : string or Unit object The name of the new unit. """ self.y_bins.convert_to_units(new_unit) self.y = 0.5 * (self.y_bins[1:] + self.y_bins[:-1]) @property def bounds(self): return ((self.x_bins[0], self.x_bins[-1]), (self.y_bins[0], self.y_bins[-1])) def plot(self): r""" This returns a :class:~yt.visualization.profile_plotter.PhasePlot with the fields that have been added to this object. """ from yt.visualization.profile_plotter import PhasePlot return PhasePlot.from_profile(self) class Profile2DFromDataset(ProfileNDFromDataset, Profile2D): """ A 2D profile object loaded from a ytdata dataset. """ def __init(self, ds): ProfileNDFromDataset.__init__(self, ds) class ParticleProfile(Profile2D): """An object that represents a *deposited* 2D profile. This is like a Profile2D, except that it is intended for particle data. Instead of just binning the particles, the added fields will be deposited onto the mesh using either the nearest-grid-point or cloud-in-cell interpolation kernels. Parameters ---------- data_source : AMD3DData object The data object to be profiled x_field : string field name The field to profile as a function of along the x axis. x_n : integer The number of bins along the x direction. x_min : float The minimum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_max : float The maximum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. y_field : string field name The field to profile as a function of along the y axis y_n : integer The number of bins along the y direction. y_min : float The minimum value of the y profile field. If supplied without units, assumed to be in the output units for y_field. y_max : float The maximum value of the y profile field. If supplied without units, assumed to be in the output units for y_field. weight_field : string field name The field to use for weighting. Default is None. deposition : string, optional The interpolation kernel to be used for deposition. Valid choices: "ngp" : nearest grid point interpolation "cic" : cloud-in-cell interpolation """ accumulation = False fractional = False def __init__( self, data_source, x_field, x_n, x_min, x_max, x_log, y_field, y_n, y_min, y_max, y_log, weight_field=None, deposition="ngp", ): x_field = data_source._determine_fields(x_field)[0] y_field = data_source._determine_fields(y_field)[0] if deposition not in ["ngp", "cic"]: raise NotImplementedError(deposition) elif (x_log or y_log) and deposition != "ngp": mylog.warning( "cic deposition is only supported for linear axis " "scales, falling back to ngp deposition" ) deposition = "ngp" self.deposition = deposition # set the log parameters to False (since that doesn't make much sense # for deposited data) and also turn off the weight field. super().__init__( data_source, x_field, x_n, x_min, x_max, x_log, y_field, y_n, y_min, y_max, y_log, weight_field=weight_field, ) # Either stick the particle field in the nearest bin, # or spread it out using the 2D CIC deposition function def _bin_chunk(self, chunk, fields, storage): rv = self._get_data(chunk, fields) if rv is None: return fdata, wdata, (bf_x, bf_y) = rv # make sure everything has the same units before deposition. # the units will be scaled to the correct values later. if self.deposition == "ngp": func = NGPDeposit_2 elif self.deposition == "cic": func = CICDeposit_2 for fi, _field in enumerate(fields): if self.weight_field is None: deposit_vals = fdata[:, fi] else: deposit_vals = wdata * fdata[:, fi] field_mask = np.zeros(self.size, dtype="uint8") func( bf_x, bf_y, deposit_vals, fdata[:, fi].size, storage.values[:, :, fi], field_mask, self.x_bins, self.y_bins, ) locs = field_mask > 0 storage.used[locs] = True if self.weight_field is not None: func( bf_x, bf_y, wdata, fdata[:, fi].size, storage.weight_values, field_mask, self.x_bins, self.y_bins, ) else: storage.weight_values[locs] = 1.0 storage.mvalues[locs, fi] = ( storage.values[locs, fi] / storage.weight_values[locs] ) # We've binned it! class Profile3D(ProfileND): """An object that represents a 2D profile. Parameters ---------- data_source : AMD3DData object The data object to be profiled x_field : string field name The field to profile as a function of along the x axis. x_n : integer The number of bins along the x direction. x_min : float The minimum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_max : float The maximum value of the x profile field. If supplied without units, assumed to be in the output units for x_field. x_log : boolean Controls whether or not the bins for the x field are evenly spaced in linear (False) or log (True) space. y_field : string field name The field to profile as a function of along the y axis y_n : integer The number of bins along the y direction. y_min : float The minimum value of the y profile field. If supplied without units, assumed to be in the output units for y_field. y_max : float The maximum value of the y profile field. If supplied without units, assumed to be in the output units for y_field. y_log : boolean Controls whether or not the bins for the y field are evenly spaced in linear (False) or log (True) space. z_field : string field name The field to profile as a function of along the z axis z_n : integer The number of bins along the z direction. z_min : float The minimum value of the z profile field. If supplied without units, assumed to be in the output units for z_field. z_max : float The maximum value of thee z profile field. If supplied without units, assumed to be in the output units for z_field. z_log : boolean Controls whether or not the bins for the z field are evenly spaced in linear (False) or log (True) space. weight_field : string field name The field to weight the profiled fields by. override_bins_x : array Array to set as xbins and ignore other parameters if set override_bins_y : array Array to set as xbins and ignore other parameters if set override_bins_z : array Array to set as xbins and ignore other parameters if set """ def __init__( self, data_source, x_field, x_n, x_min, x_max, x_log, y_field, y_n, y_min, y_max, y_log, z_field, z_n, z_min, z_max, z_log, weight_field=None, override_bins_x=None, override_bins_y=None, override_bins_z=None, ): super().__init__(data_source, weight_field) # X self.x_field = data_source._determine_fields(x_field)[0] self.x_log = x_log self.field_info[self.x_field] = self.data_source.ds.field_info[self.x_field] x_min, x_max = _sanitize_min_max_units( x_min, x_max, self.field_info[self.x_field], self.ds.unit_registry ) self.x_bins = array_like_field( data_source, self._get_bins(x_min, x_max, x_n, x_log), self.x_field ) if override_bins_x is not None: self.x_bins = array_like_field(data_source, override_bins_x, self.x_field) # Y self.y_field = data_source._determine_fields(y_field)[0] self.y_log = y_log self.field_info[self.y_field] = self.data_source.ds.field_info[self.y_field] y_min, y_max = _sanitize_min_max_units( y_min, y_max, self.field_info[self.y_field], self.ds.unit_registry ) self.y_bins = array_like_field( data_source, self._get_bins(y_min, y_max, y_n, y_log), self.y_field ) if override_bins_y is not None: self.y_bins = array_like_field(data_source, override_bins_y, self.y_field) # Z self.z_field = data_source._determine_fields(z_field)[0] self.z_log = z_log self.field_info[self.z_field] = self.data_source.ds.field_info[self.z_field] z_min, z_max = _sanitize_min_max_units( z_min, z_max, self.field_info[self.z_field], self.ds.unit_registry ) self.z_bins = array_like_field( data_source, self._get_bins(z_min, z_max, z_n, z_log), self.z_field ) if override_bins_z is not None: self.z_bins = array_like_field(data_source, override_bins_z, self.z_field) self.size = (self.x_bins.size - 1, self.y_bins.size - 1, self.z_bins.size - 1) self.bin_fields = (self.x_field, self.y_field, self.z_field) self.x = 0.5 * (self.x_bins[1:] + self.x_bins[:-1]) self.y = 0.5 * (self.y_bins[1:] + self.y_bins[:-1]) self.z = 0.5 * (self.z_bins[1:] + self.z_bins[:-1]) def _bin_chunk(self, chunk, fields, storage): rv = self._get_data(chunk, fields) if rv is None: return fdata, wdata, (bf_x, bf_y, bf_z) = rv bf_x.convert_to_units(self.field_info[self.x_field].output_units) bin_ind_x = np.digitize(bf_x, self.x_bins) - 1 bf_y.convert_to_units(self.field_info[self.y_field].output_units) bin_ind_y = np.digitize(bf_y, self.y_bins) - 1 bf_z.convert_to_units(self.field_info[self.z_field].output_units) bin_ind_z = np.digitize(bf_z, self.z_bins) - 1 new_bin_profile3d( bin_ind_x, bin_ind_y, bin_ind_z, wdata, fdata, storage.weight_values, storage.values, storage.mvalues, storage.qvalues, storage.used, ) # We've binned it! @property def bounds(self): return ( (self.x_bins[0], self.x_bins[-1]), (self.y_bins[0], self.y_bins[-1]), (self.z_bins[0], self.z_bins[-1]), ) def set_x_unit(self, new_unit): """Sets a new unit for the x field Parameters ---------- new_unit : string or Unit object The name of the new unit. """ self.x_bins.convert_to_units(new_unit) self.x = 0.5 * (self.x_bins[1:] + self.x_bins[:-1]) def set_y_unit(self, new_unit): """Sets a new unit for the y field Parameters ---------- new_unit : string or Unit object The name of the new unit. """ self.y_bins.convert_to_units(new_unit) self.y = 0.5 * (self.y_bins[1:] + self.y_bins[:-1]) def set_z_unit(self, new_unit): """Sets a new unit for the z field Parameters ---------- new_unit : string or Unit object The name of the new unit. """ self.z_bins.convert_to_units(new_unit) self.z = 0.5 * (self.z_bins[1:] + self.z_bins[:-1]) class Profile3DFromDataset(ProfileNDFromDataset, Profile3D): """ A 2D profile object loaded from a ytdata dataset. """ def __init(self, ds): ProfileNDFromDataset.__init__(self, ds) def sanitize_field_tuple_keys(input_dict, data_source): if input_dict is not None: dummy = {} for item in input_dict: dummy[data_source._determine_fields(item)[0]] = input_dict[item] return dummy else: return input_dict def create_profile( data_source, bin_fields, fields, n_bins=64, extrema=None, logs=None, units=None, weight_field=("gas", "mass"), accumulation=False, fractional=False, deposition="ngp", override_bins=None, ): r""" Create a 1, 2, or 3D profile object. The dimensionality of the profile object is chosen by the number of fields given in the bin_fields argument. Parameters ---------- data_source : YTSelectionContainer Object The data object to be profiled. bin_fields : list of strings List of the binning fields for profiling. fields : list of strings The fields to be profiled. n_bins : int or list of ints The number of bins in each dimension. If None, 64 bins for each bin are used for each bin field. Default: 64. extrema : dict of min, max tuples Minimum and maximum values of the bin_fields for the profiles. The keys correspond to the field names. Defaults to the extrema of the bin_fields of the dataset. If a units dict is provided, extrema are understood to be in the units specified in the dictionary. logs : dict of boolean values Whether or not to log the bin_fields for the profiles. The keys correspond to the field names. Defaults to the take_log attribute of the field. units : dict of strings The units of the fields in the profiles, including the bin_fields. weight_field : str or tuple field identifier The weight field for computing weighted average for the profile values. If None, the profile values are sums of the data in each bin. Defaults to ("gas", "mass"). accumulation : bool or list of bools If True, the profile values for a bin n are the cumulative sum of all the values from bin 0 to n. If -True, the sum is reversed so that the value for bin n is the cumulative sum from bin N (total bins) to n. If the profile is 2D or 3D, a list of values can be given to control the summation in each dimension independently. Default: False. fractional : bool If True the profile values are divided by the sum of all the profile data such that the profile represents a probability distribution function. deposition : strings Controls the type of deposition used for ParticlePhasePlots. Valid choices are 'ngp' and 'cic'. Default is 'ngp'. This parameter is ignored if the input fields are not of particle type. override_bins : dict of bins to profile plot with If set, ignores n_bins and extrema settings and uses the supplied bins to profile the field. If a units dict is provided, bins are understood to be in the units specified in the dictionary. Examples -------- Create a 1d profile. Access bin field from profile.x and field data from profile[]. >>> ds = load("DD0046/DD0046") >>> ad = ds.all_data() >>> profile = create_profile( ... ad, [("gas", "density")], [("gas", "temperature"), ("gas", "velocity_x")] ... ) >>> print(profile.x) >>> print(profile["gas", "temperature"]) """ bin_fields = data_source._determine_fields(bin_fields) fields = list(iter_fields(fields)) is_pfield = [ data_source.ds._get_field_info(f).sampling_type == "particle" for f in bin_fields + fields ] wf = None if weight_field is not None: wf = data_source.ds._get_field_info(weight_field) is_pfield.append(wf.sampling_type == "particle") wf = wf.name if len(bin_fields) > 1 and isinstance(accumulation, bool): accumulation = [accumulation for _ in range(len(bin_fields))] bin_fields = data_source._determine_fields(bin_fields) fields = data_source._determine_fields(fields) units = sanitize_field_tuple_keys(units, data_source) extrema = sanitize_field_tuple_keys(extrema, data_source) logs = sanitize_field_tuple_keys(logs, data_source) override_bins = sanitize_field_tuple_keys(override_bins, data_source) if any(is_pfield) and not all(is_pfield): if hasattr(data_source.ds, "_sph_ptypes"): is_local = [ data_source.ds.field_info[f].sampling_type == "local" for f in bin_fields + fields ] if wf is not None: is_local.append(data_source.ds.field_info[wf].sampling_type == "local") is_local_or_pfield = [ pf or lf for (pf, lf) in zip(is_pfield, is_local, strict=True) ] if not all(is_local_or_pfield): raise YTIllDefinedProfile( bin_fields, data_source._determine_fields(fields), wf, is_pfield ) else: raise YTIllDefinedProfile( bin_fields, data_source._determine_fields(fields), wf, is_pfield ) if len(bin_fields) == 1: cls = Profile1D elif len(bin_fields) == 2 and all(is_pfield): if deposition == "cic": if logs is not None: if (bin_fields[0] in logs and logs[bin_fields[0]]) or ( bin_fields[1] in logs and logs[bin_fields[1]] ): raise RuntimeError( "CIC deposition is only implemented for linear-scaled axes" ) else: logs = {bin_fields[0]: False, bin_fields[1]: False} if any(accumulation) or fractional: raise RuntimeError( "The accumulation and fractional keyword arguments must be " "False for CIC deposition" ) cls = ParticleProfile elif len(bin_fields) == 2: cls = Profile2D elif len(bin_fields) == 3: cls = Profile3D else: raise NotImplementedError if weight_field is not None and cls == ParticleProfile: (weight_field,) = data_source._determine_fields([weight_field]) wf = data_source.ds._get_field_info(weight_field) if not wf.sampling_type == "particle": weight_field = None if not is_sequence(n_bins): n_bins = [n_bins] * len(bin_fields) if not is_sequence(accumulation): accumulation = [accumulation] * len(bin_fields) if logs is None: logs = {} logs_list = [] for bin_field in bin_fields: if bin_field in logs: logs_list.append(logs[bin_field]) else: logs_list.append(data_source.ds.field_info[bin_field].take_log) logs = logs_list # Are the extrema all Nones? Then treat them as though extrema was set as None if extrema is None or not any(collapse(extrema.values())): ex = [ data_source.quantities["Extrema"](f, non_zero=l) for f, l in zip(bin_fields, logs, strict=True) ] # pad extrema by epsilon so cells at bin edges are not excluded for i, (mi, ma) in enumerate(ex): mi = mi - np.spacing(mi) ma = ma + np.spacing(ma) ex[i][0], ex[i][1] = mi, ma else: ex = [] for bin_field in bin_fields: bf_units = data_source.ds.field_info[bin_field].output_units try: field_ex = list(extrema[bin_field[-1]]) except KeyError as e: try: field_ex = list(extrema[bin_field]) except KeyError: raise RuntimeError( f"Could not find field {bin_field[-1]} or {bin_field} in extrema" ) from e if isinstance(field_ex[0], tuple): field_ex = [data_source.ds.quan(*f) for f in field_ex] if any(exi is None for exi in field_ex): try: ds_extrema = data_source.quantities.extrema(bin_field) except AttributeError: # ytdata profile datasets don't have data_source.quantities bf_vals = data_source[bin_field] ds_extrema = data_source.ds.arr([bf_vals.min(), bf_vals.max()]) for i, exi in enumerate(field_ex): if exi is None: field_ex[i] = ds_extrema[i] # pad extrema by epsilon so cells at bin edges are # not excluded field_ex[i] -= (-1) ** i * np.spacing(field_ex[i]) if units is not None and bin_field in units: for i, exi in enumerate(field_ex): if hasattr(exi, "units"): field_ex[i] = exi.to(units[bin_field]) else: field_ex[i] = data_source.ds.quan(exi, units[bin_field]) fe = data_source.ds.arr(field_ex) else: if hasattr(field_ex, "units"): fe = field_ex.to(bf_units) else: fe = data_source.ds.arr(field_ex, bf_units) fe.convert_to_units(bf_units) field_ex = [fe[0].v, fe[1].v] if is_sequence(field_ex[0]): field_ex[0] = data_source.ds.quan(field_ex[0][0], field_ex[0][1]) field_ex[0] = field_ex[0].in_units(bf_units) if is_sequence(field_ex[1]): field_ex[1] = data_source.ds.quan(field_ex[1][0], field_ex[1][1]) field_ex[1] = field_ex[1].in_units(bf_units) ex.append(field_ex) if override_bins is not None: o_bins = [] for bin_field in bin_fields: bf_units = data_source.ds.field_info[bin_field].output_units try: field_obin = override_bins[bin_field[-1]] except KeyError: field_obin = override_bins[bin_field] if field_obin is None: o_bins.append(None) continue if isinstance(field_obin, tuple): field_obin = data_source.ds.arr(*field_obin) if units is not None and bin_field in units: fe = data_source.ds.arr(field_obin, units[bin_field]) else: if hasattr(field_obin, "units"): fe = field_obin.to(bf_units) else: fe = data_source.ds.arr(field_obin, bf_units) fe.convert_to_units(bf_units) field_obin = fe.d o_bins.append(field_obin) args = [data_source] for f, n, (mi, ma), l in zip(bin_fields, n_bins, ex, logs, strict=True): if mi <= 0 and l: raise YTIllDefinedBounds(mi, ma) args += [f, n, mi, ma, l] kwargs = {"weight_field": weight_field} if cls is ParticleProfile: kwargs["deposition"] = deposition if override_bins is not None: for o_bin, ax in zip(o_bins, ["x", "y", "z"], strict=False): kwargs[f"override_bins_{ax}"] = o_bin obj = cls(*args, **kwargs) obj.accumulation = accumulation obj.fractional = fractional if fields is not None: obj.add_fields(list(fields)) for field in fields: if fractional: obj.field_data[field] /= obj.field_data[field].sum() for axis, acc in enumerate(accumulation): if not acc: continue temp = obj.field_data[field] temp = np.rollaxis(temp, axis) if weight_field is not None: temp_weight = obj.weight temp_weight = np.rollaxis(temp_weight, axis) if acc < 0: temp = temp[::-1] if weight_field is not None: temp_weight = temp_weight[::-1] if weight_field is None: temp = temp.cumsum(axis=0) else: # avoid 0-division warnings by nan-masking _denom = temp_weight.cumsum(axis=0) _denom[_denom == 0.0] = np.nan temp = (temp * temp_weight).cumsum(axis=0) / _denom if acc < 0: temp = temp[::-1] if weight_field is not None: temp_weight = temp_weight[::-1] temp = np.rollaxis(temp, axis) obj.field_data[field] = temp if weight_field is not None: temp_weight = np.rollaxis(temp_weight, axis) obj.weight = temp_weight if units is not None: for field, unit in units.items(): field = data_source._determine_fields(field)[0] if field == obj.x_field: obj.set_x_unit(unit) elif field == getattr(obj, "y_field", None): obj.set_y_unit(unit) elif field == getattr(obj, "z_field", None): obj.set_z_unit(unit) else: obj.set_field_unit(field, unit) return obj yt-project-yt-f043ac8/yt/data_objects/region_expression.py000066400000000000000000000214351510711153200240510ustar00rootroot00000000000000import weakref from functools import cached_property from yt.funcs import obj_length from yt.units.yt_array import YTQuantity from yt.utilities.exceptions import ( YTDimensionalityError, YTFieldNotFound, YTFieldNotParseable, ) from yt.visualization.line_plot import LineBuffer from .data_containers import _get_ipython_key_completion class RegionExpression: def __init__(self, ds): self.ds = weakref.proxy(ds) @cached_property def all_data(self): return self.ds.all_data() def __getitem__(self, item): # At first, we will only implement this as accepting a slice that is # (optionally) unitful corresponding to a specific set of coordinates # that result in a rectangular prism or a slice. try: return self.all_data[item] except (YTFieldNotParseable, YTFieldNotFound): # any error raised by self.ds._get_field_info # signals a type error (not a field), however we don't want to # catch plain TypeErrors as this may create subtle bugs very hard # to decipher, like broken internal function calls. pass if isinstance(item, slice): if obj_length(item.start) == 3 and obj_length(item.stop) == 3: # This is for a ray that is not orthogonal to an axis. # it's straightforward to do this, so we create a ray # and drop out here. return self._create_ray(item) else: # This is for the case where we give a slice as an index; one # possible use case of this would be where we supply something # like ds.r[::256j] . This would be expanded, implicitly into # ds.r[::256j, ::256j, ::256j]. Other cases would be if we do # ds.r[0.1:0.9] where it will be expanded along all dimensions. item = tuple(item for _ in range(self.ds.dimensionality)) if item is Ellipsis: item = (Ellipsis,) # from this point, item is implicitly assumed to be iterable if Ellipsis in item: # expand "..." into the appropriate number of ":" item = list(item) idx = item.index(Ellipsis) item.pop(idx) if Ellipsis in item: # this error mimics numpy's raise IndexError("an index can only have a single ellipsis ('...')") while len(item) < self.ds.dimensionality: item.insert(idx, slice(None)) if len(item) != self.ds.dimensionality: # Not the right specification, and we don't want to do anything # implicitly. Note that this happens *after* the implicit expansion # of a single slice. raise YTDimensionalityError(len(item), self.ds.dimensionality) # OK, now we need to look at our slices. How many are a specific # coordinate? nslices = sum(isinstance(v, slice) for v in item) if nslices == 0: return self._create_point(item) elif nslices == 1: return self._create_ortho_ray(item) elif nslices == 2: return self._create_slice(item) else: if all(s.start is s.stop is s.step is None for s in item): return self.all_data return self._create_region(item) def _ipython_key_completions_(self): return _get_ipython_key_completion(self.ds) def _spec_to_value(self, input): if isinstance(input, tuple): v = self.ds.quan(input[0], input[1]).to("code_length") elif isinstance(input, YTQuantity): v = self.ds.quan(input).to("code_length") else: v = self.ds.quan(input, "code_length") return v def _create_slice(self, slice_tuple): # This is somewhat more complex because we want to allow for slicing # in one dimension but also *not* using the entire domain; for instance # this means we allow something like ds.r[0.5, 0.1:0.4, 0.1:0.4]. axis = None new_slice = [] for ax, v in enumerate(slice_tuple): if not isinstance(v, slice): if axis is not None: raise RuntimeError axis = ax coord = self._spec_to_value(v) new_slice.append(slice(None, None, None)) else: new_slice.append(v) # This new slice doesn't need to be a tuple dim = self.ds.dimensionality if dim < 2: raise ValueError( "Can not create a slice from data with dimensionality '%d'" % dim ) if dim == 2: coord = self.ds.domain_center[2] axis = 2 source = self._create_region(new_slice) sl = self.ds.slice(axis, coord, data_source=source) # Now, there's the possibility that what we're also seeing here # includes some steps, which would be for getting back a fixed # resolution buffer. We check for that by checking if we have # exactly two imaginary steps. xax = self.ds.coordinates.x_axis[axis] yax = self.ds.coordinates.y_axis[axis] if ( getattr(new_slice[xax].step, "imag", 0.0) != 0.0 and getattr(new_slice[yax].step, "imag", 0.0) != 0.0 ): # We now need to convert to a fixed res buffer. # We'll do this by getting the x/y axes, and then using that. width = source.right_edge[xax] - source.left_edge[xax] height = source.right_edge[yax] - source.left_edge[yax] # Make a resolution tuple with resolution = (int(new_slice[xax].step.imag), int(new_slice[yax].step.imag)) # Use the center of the slice, not the entire domain center = source.center sl = sl.to_frb( width=width, resolution=resolution, height=height, center=center ) return sl def _slice_to_edges(self, ax, val): if val.start is None: l = self.ds.domain_left_edge[ax] else: l = self._spec_to_value(val.start) if val.stop is None: r = self.ds.domain_right_edge[ax] else: r = self._spec_to_value(val.stop) if r < l: raise RuntimeError return l, r def _create_region(self, bounds_tuple): left_edge = self.ds.domain_left_edge.copy() right_edge = self.ds.domain_right_edge.copy() dims = [1, 1, 1] for ax, b in enumerate(bounds_tuple): l, r = self._slice_to_edges(ax, b) left_edge[ax] = l right_edge[ax] = r d = getattr(b.step, "imag", None) if d is not None: d = int(d) dims[ax] = d center = (left_edge + right_edge) / 2.0 if None not in dims: return self.ds.arbitrary_grid(left_edge, right_edge, dims) return self.ds.region(center, left_edge, right_edge) def _create_point(self, point_tuple): coord = [self._spec_to_value(p) for p in point_tuple] return self.ds.point(coord) def _create_ray(self, ray_slice): start_point = [self._spec_to_value(v) for v in ray_slice.start] end_point = [self._spec_to_value(v) for v in ray_slice.stop] if getattr(ray_slice.step, "imag", 0.0) != 0.0: return LineBuffer(self.ds, start_point, end_point, int(ray_slice.step.imag)) else: return self.ds.ray(start_point, end_point) def _create_ortho_ray(self, ray_tuple): axis = None new_slice = [] coord = [] npoints = 0 start_point = [] end_point = [] for ax, v in enumerate(ray_tuple): if not isinstance(v, slice): val = self._spec_to_value(v) coord.append(val) new_slice.append(slice(None, None, None)) start_point.append(val) end_point.append(val) else: if axis is not None: raise RuntimeError if getattr(v.step, "imag", 0.0) != 0.0: npoints = int(v.step.imag) xi = self._spec_to_value(v.start) xf = self._spec_to_value(v.stop) dx = (xf - xi) / npoints start_point.append(xi + 0.5 * dx) end_point.append(xf - 0.5 * dx) else: axis = ax new_slice.append(v) if npoints > 0: ray = LineBuffer(self.ds, start_point, end_point, npoints) else: if axis == 1: coord = [coord[1], coord[0]] source = self._create_region(new_slice) ray = self.ds.ortho_ray(axis, coord, data_source=source) return ray yt-project-yt-f043ac8/yt/data_objects/selection_objects/000077500000000000000000000000001510711153200234265ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/selection_objects/__init__.py000066400000000000000000000006441510711153200255430ustar00rootroot00000000000000from .boolean_operations import ( YTBooleanContainer, YTDataObjectUnion, YTIntersectionContainer3D, ) from .cut_region import YTCutRegion from .disk import YTDisk from .object_collection import YTDataCollection from .point import YTPoint from .ray import YTOrthoRay, YTRay from .region import YTRegion from .slices import YTCuttingPlane, YTSlice from .spheroids import YTEllipsoid, YTMinimalSphere, YTSphere yt-project-yt-f043ac8/yt/data_objects/selection_objects/boolean_operations.py000066400000000000000000000114751510711153200276720ustar00rootroot00000000000000import numpy as np from more_itertools import always_iterable import yt.geometry from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.funcs import validate_object, validate_sequence class YTBooleanContainer(YTSelectionContainer3D): r""" This is a boolean operation, accepting AND, OR, XOR, and NOT for combining multiple data objects. This object is not designed to be created directly; it is designed to be created implicitly by using one of the bitwise operations (&, \|, ^, \~) on one or two other data objects. These correspond to the appropriate boolean operations, and the resultant object can be nested. Parameters ---------- op : string Can be AND, OR, XOR, NOT or NEG. dobj1 : yt.data_objects.selection_objects.data_selection_objects. YTSelectionContainer The first selection object dobj2 : yt.data_objects.selection_objects.base_objects.YTSelectionContainer The second object Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sp = ds.sphere("c", 0.1) >>> dd = ds.r[:, :, :] >>> new_obj = sp ^ dd >>> print(new_obj.sum("cell_volume"), dd.sum("cell_volume") - sp.sum("cell_volume")) """ _type_name = "bool" _con_args = ("op", "dobj1", "dobj2") def __init__( self, op, dobj1, dobj2, ds=None, field_parameters=None, data_source=None ): YTSelectionContainer3D.__init__(self, None, ds, field_parameters, data_source) self.op = op.upper() self.dobj1 = dobj1 self.dobj2 = dobj2 name = f"Boolean{self.op}Selector" sel_cls = getattr(yt.geometry.selection_routines, name) self._selector = sel_cls(self) def _get_bbox(self): le1, re1 = self.dobj1._get_bbox() if self.op == "NOT": return le1, re1 else: le2, re2 = self.dobj2._get_bbox() return np.minimum(le1, le2), np.maximum(re1, re2) class YTIntersectionContainer3D(YTSelectionContainer3D): """ This is a more efficient method of selecting the intersection of multiple data selection objects. Creating one of these objects returns the intersection of all of the sub-objects; it is designed to be a faster method than chaining & ("and") operations to create a single, large intersection. Parameters ---------- data_objects : Iterable of YTSelectionContainer The data objects to intersect Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> sp1 = ds.sphere((0.4, 0.5, 0.6), 0.15) >>> sp2 = ds.sphere((0.38, 0.51, 0.55), 0.1) >>> sp3 = ds.sphere((0.35, 0.5, 0.6), 0.15) >>> new_obj = ds.intersection((sp1, sp2, sp3)) >>> print(new_obj.sum("cell_volume")) """ _type_name = "intersection" _con_args = ("data_objects",) def __init__(self, data_objects, ds=None, field_parameters=None, data_source=None): validate_sequence(data_objects) for obj in data_objects: validate_object(obj, YTSelectionContainer) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer3D.__init__(self, None, ds, field_parameters, data_source) self.data_objects = list(always_iterable(data_objects)) class YTDataObjectUnion(YTSelectionContainer3D): """ This is a more efficient method of selecting the union of multiple data selection objects. Creating one of these objects returns the union of all of the sub-objects; it is designed to be a faster method than chaining | (or) operations to create a single, large union. Parameters ---------- data_objects : Iterable of YTSelectionContainer The data objects to union Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sp1 = ds.sphere((0.4, 0.5, 0.6), 0.1) >>> sp2 = ds.sphere((0.3, 0.5, 0.15), 0.1) >>> sp3 = ds.sphere((0.5, 0.5, 0.9), 0.1) >>> new_obj = ds.union((sp1, sp2, sp3)) >>> print(new_obj.sum("cell_volume")) """ _type_name = "union" _con_args = ("data_objects",) def __init__(self, data_objects, ds=None, field_parameters=None, data_source=None): validate_sequence(data_objects) for obj in data_objects: validate_object(obj, YTSelectionContainer) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer3D.__init__(self, None, ds, field_parameters, data_source) self.data_objects = list(always_iterable(data_objects)) yt-project-yt-f043ac8/yt/data_objects/selection_objects/cut_region.py000066400000000000000000000222741510711153200261450ustar00rootroot00000000000000import re import numpy as np from more_itertools import always_iterable from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.funcs import iter_fields, validate_object, validate_sequence from yt.geometry.selection_routines import points_in_cells from yt.utilities.exceptions import YTIllDefinedCutRegion from yt.utilities.on_demand_imports import _scipy class YTCutRegion(YTSelectionContainer3D): """ This is a data object designed to allow individuals to apply logical operations to fields and filter as a result of those cuts. Parameters ---------- data_source : YTSelectionContainer3D The object to which cuts will be applied. conditionals : list of strings A list of conditionals that will be evaluated. In the namespace available, these conditionals will have access to 'obj' which is a data object of unknown shape, and they must generate a boolean array. For instance, conditionals = ["obj['gas', 'temperature'] < 1e3"] Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> sp = ds.sphere("max", (1.0, "Mpc")) >>> cr = ds.cut_region(sp, ["obj['gas', 'temperature'] < 1e3"]) """ _type_name = "cut_region" _con_args = ("base_object", "conditionals") _derived_quantity_chunking = "all" def __init__( self, data_source, conditionals, ds=None, field_parameters=None, base_object=None, locals=None, ): if locals is None: locals = {} validate_object(data_source, YTSelectionContainer) validate_sequence(conditionals) for condition in conditionals: validate_object(condition, str) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(base_object, YTSelectionContainer) self.conditionals = list(always_iterable(conditionals)) if isinstance(data_source, YTCutRegion): # If the source is also a cut region, add its conditionals # and set the source to be its source. # Preserve order of conditionals. self.conditionals = data_source.conditionals + self.conditionals data_source = data_source.base_object super().__init__( data_source.center, ds, field_parameters, data_source=data_source ) self.filter_fields = self._check_filter_fields() self.base_object = data_source self.locals = locals self._selector = None # Need to interpose for __getitem__, fwidth, fcoords, icoords, iwidth, # ires and get_data def _check_filter_fields(self): fields = [] for cond in self.conditionals: for field in re.findall(r"\[([A-Za-z0-9_,.'\"\(\)]+)\]", cond): fd = field.replace('"', "").replace("'", "") if "," in fd: fd = tuple(fd.strip("()").split(",")) fd = self.ds._get_field_info(fd) if fd.sampling_type == "particle" or fd.is_sph_field: raise RuntimeError( f"cut_region requires a mesh-based field, " f"but {fd.name} is a particle field! Use " f"a particle filter instead. " ) fields.append(fd.name) return fields def chunks(self, fields, chunking_style, **kwargs): # We actually want to chunk the sub-chunk, not ourselves. We have no # chunks to speak of, as we do not data IO. for chunk in self.index._chunk(self.base_object, chunking_style, **kwargs): with self.base_object._chunked_read(chunk): with self._chunked_read(chunk): self.get_data(fields) yield self def get_data(self, fields=None): fields = list(iter_fields(fields)) self.base_object.get_data(fields) ind = self._cond_ind for field in fields: f = self.base_object[field] if f.shape != ind.shape: parent = getattr(self, "parent", self.base_object) self.field_data[field] = parent[field][self._part_ind(field[0])] else: self.field_data[field] = self.base_object[field][ind] @property def blocks(self): # We have to take a slightly different approach here. Note that all # that .blocks has to yield is a 3D array and a mask. for obj, m in self.base_object.blocks: m = m.copy() with obj._field_parameter_state(self.field_parameters): for cond in self.conditionals: ss = eval(cond) m = np.logical_and(m, ss, m) if not np.any(m): continue yield obj, m @property def _cond_ind(self): ind = None obj = self.base_object locals = self.locals.copy() if "obj" in locals: raise RuntimeError( '"obj" has been defined in the "locals" ; ' "this is not supported, please rename the variable." ) locals["obj"] = obj with obj._field_parameter_state(self.field_parameters): for cond in self.conditionals: res = eval(cond, locals) if ind is None: ind = res if ind.shape != res.shape: raise YTIllDefinedCutRegion(self.conditionals) np.logical_and(res, ind, ind) return ind def _part_ind_KDTree(self, ptype): """Find the particles in cells using a KDTree approach.""" parent = getattr(self, "parent", self.base_object) units = "code_length" pos = np.stack( [ self["index", "x"].to(units), self["index", "y"].to(units), self["index", "z"].to(units), ], axis=1, ).value dx = np.stack( [ self["index", "dx"].to(units), self["index", "dy"].to(units), self["index", "dz"].to(units), ], axis=1, ).value ppos = np.stack( [ parent[ptype, "particle_position_x"], parent[ptype, "particle_position_y"], parent[ptype, "particle_position_z"], ], axis=1, ).value mask = np.zeros(ppos.shape[0], dtype=bool) levels = self["index", "grid_level"].astype("int32").value if levels.size == 0: return mask levelmin = levels.min() levelmax = levels.max() for lvl in range(levelmax, levelmin - 1, -1): # Filter out cells not in the current level lvl_mask = levels == lvl dx_loc = dx[lvl_mask] pos_loc = pos[lvl_mask] grid_tree = _scipy.spatial.cKDTree(pos_loc, boxsize=1) # Compute closest cell for all remaining particles dist, icell = grid_tree.query( ppos[~mask], distance_upper_bound=dx_loc.max(), p=np.inf ) mask_loc = np.isfinite(dist[:]) # Check that particles within dx of a cell are in it i = icell[mask_loc] dist = np.abs(ppos[~mask][mask_loc, :] - pos_loc[i]) tmp_mask = np.all(dist <= (dx_loc[i] / 2), axis=1) mask_loc[mask_loc] = tmp_mask # Update the particle mask with particles found at this level mask[~mask] |= mask_loc return mask def _part_ind_brute_force(self, ptype): parent = getattr(self, "parent", self.base_object) units = "code_length" mask = points_in_cells( self["index", "x"].to(units), self["index", "y"].to(units), self["index", "z"].to(units), self["index", "dx"].to(units), self["index", "dy"].to(units), self["index", "dz"].to(units), parent[ptype, "particle_position_x"].to(units), parent[ptype, "particle_position_y"].to(units), parent[ptype, "particle_position_z"].to(units), ) return mask def _part_ind(self, ptype): # If scipy is installed, use the fast KD tree # implementation. Else, fall back onto the direct # brute-force algorithm. try: _scipy.spatial.KDTree return self._part_ind_KDTree(ptype) except ImportError: return self._part_ind_brute_force(ptype) @property def icoords(self): return self.base_object.icoords[self._cond_ind, :] @property def fcoords(self): return self.base_object.fcoords[self._cond_ind, :] @property def ires(self): return self.base_object.ires[self._cond_ind] @property def fwidth(self): return self.base_object.fwidth[self._cond_ind, :] def _get_bbox(self): """ Get the bounding box for the cut region. Here we just use the bounding box for the source region. """ return self.base_object._get_bbox() yt-project-yt-f043ac8/yt/data_objects/selection_objects/data_selection_objects.py000066400000000000000000001526211510711153200304760ustar00rootroot00000000000000import abc import itertools import sys import uuid from collections import defaultdict from contextlib import contextmanager import numpy as np from more_itertools import always_iterable from unyt import unyt_array from unyt.exceptions import UnitConversionError, UnitParseError import yt.geometry from yt.data_objects.data_containers import YTDataContainer from yt.data_objects.derived_quantities import DerivedQuantityCollection from yt.data_objects.field_data import YTFieldData from yt.fields.field_exceptions import NeedsGridType from yt.funcs import fix_axis, is_sequence, iter_fields, validate_width_tuple from yt.geometry.api import Geometry from yt.geometry.selection_routines import compose_selector from yt.units import YTArray from yt.utilities.exceptions import ( GenerationInProgress, YTBooleanObjectError, YTBooleanObjectsWrongDataset, YTDataSelectorNotImplemented, YTDimensionalityError, YTFieldUnitError, YTFieldUnitParseError, ) from yt.utilities.lib.marching_cubes import march_cubes_grid, march_cubes_grid_flux from yt.utilities.logger import ytLogger as mylog from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, ) if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never class YTSelectionContainer(YTDataContainer, ParallelAnalysisInterface, abc.ABC): _locked = False _sort_by = None _selector = None _current_chunk = None _data_source = None _dimensionality: int _max_level = None _min_level = None _derived_quantity_chunking = "io" def __init__(self, ds, field_parameters, data_source=None): ParallelAnalysisInterface.__init__(self) super().__init__(ds, field_parameters) self._data_source = data_source if data_source is not None: if data_source.ds != self.ds: raise RuntimeError( "Attempted to construct a DataContainer with a data_source " "from a different Dataset", ds, data_source.ds, ) if data_source._dimensionality < self._dimensionality: raise RuntimeError( "Attempted to construct a DataContainer with a data_source " "of lower dimensionality (%u vs %u)" % (data_source._dimensionality, self._dimensionality) ) self.field_parameters.update(data_source.field_parameters) self.quantities = DerivedQuantityCollection(self) @property def selector(self): if self._selector is not None: return self._selector s_module = getattr(self, "_selector_module", yt.geometry.selection_routines) sclass = getattr(s_module, f"{self._type_name}_selector", None) if sclass is None: raise YTDataSelectorNotImplemented(self._type_name) if self._data_source is not None: self._selector = compose_selector( self, self._data_source.selector, sclass(self) ) else: self._selector = sclass(self) return self._selector def chunks(self, fields, chunking_style, **kwargs): # This is an iterator that will yield the necessary chunks. self.get_data() # Ensure we have built ourselves if fields is None: fields = [] # chunk_ind can be supplied in the keyword arguments. If it's a # scalar, that'll be the only chunk that gets returned; if it's a list, # those are the ones that will be. chunk_ind = kwargs.pop("chunk_ind", None) if chunk_ind is not None: chunk_ind = list(always_iterable(chunk_ind)) for ci, chunk in enumerate(self.index._chunk(self, chunking_style, **kwargs)): if chunk_ind is not None and ci not in chunk_ind: continue with self._chunked_read(chunk): self.get_data(fields) # NOTE: we yield before releasing the context yield self def _identify_dependencies(self, fields_to_get, spatial=False): inspected = 0 fields_to_get = fields_to_get[:] for field in itertools.cycle(fields_to_get): if inspected >= len(fields_to_get): break inspected += 1 fi = self.ds._get_field_info(field) fd = self.ds.field_dependencies.get( field, None ) or self.ds.field_dependencies.get(field[1], None) # This is long overdue. Any time we *can't* find a field # dependency -- for instance, if the derived field has been added # after dataset instantiation -- let's just try to # recalculate it. if fd is None: try: fd = fi.get_dependencies(ds=self.ds) self.ds.field_dependencies[field] = fd except Exception: continue requested = self._determine_fields(list(set(fd.requested))) deps = [d for d in requested if d not in fields_to_get] fields_to_get += deps return sorted(fields_to_get) def get_data(self, fields=None): if self._current_chunk is None: self.index._identify_base_chunk(self) if fields is None: return nfields = [] apply_fields = defaultdict(list) for field in self._determine_fields(fields): # We need to create the field on the raw particle types # for particles types (when the field is not directly # defined for the derived particle type only) finfo = self.ds.field_info[field] if ( field[0] in self.ds.filtered_particle_types and finfo._inherited_particle_filter ): f = self.ds.known_filters[field[0]] apply_fields[field[0]].append((f.filtered_type, field[1])) else: nfields.append(field) for filter_type in apply_fields: f = self.ds.known_filters[filter_type] with f.apply(self): self.get_data(apply_fields[filter_type]) fields = nfields if len(fields) == 0: return # Now we collect all our fields # Here is where we need to perform a validation step, so that if we # have a field requested that we actually *can't* yet get, we put it # off until the end. This prevents double-reading fields that will # need to be used in spatial fields later on. fields_to_get = [] # This will be pre-populated with spatial fields fields_to_generate = [] for field in self._determine_fields(fields): if field in self.field_data: continue finfo = self.ds._get_field_info(field) try: finfo.check_available(self) except NeedsGridType: fields_to_generate.append(field) continue fields_to_get.append(field) if len(fields_to_get) == 0 and len(fields_to_generate) == 0: return elif self._locked: raise GenerationInProgress(fields) # Track which ones we want in the end ofields = set(list(self.field_data.keys()) + fields_to_get + fields_to_generate) # At this point, we want to figure out *all* our dependencies. fields_to_get = self._identify_dependencies(fields_to_get, self._spatial) # We now split up into readers for the types of fields fluids, particles = [], [] finfos = {} for field_key in fields_to_get: finfo = self.ds._get_field_info(field_key) finfos[field_key] = finfo if finfo.sampling_type == "particle": particles.append(field_key) elif field_key not in fluids: fluids.append(field_key) # The _read method will figure out which fields it needs to get from # disk, and return a dict of those fields along with the fields that # need to be generated. read_fluids, gen_fluids = self.index._read_fluid_fields( fluids, self, self._current_chunk ) for f, v in read_fluids.items(): self.field_data[f] = self.ds.arr(v, units=finfos[f].units) self.field_data[f].convert_to_units(finfos[f].output_units) read_particles, gen_particles = self.index._read_particle_fields( particles, self, self._current_chunk ) for f, v in read_particles.items(): self.field_data[f] = self.ds.arr(v, units=finfos[f].units) self.field_data[f].convert_to_units(finfos[f].output_units) fields_to_generate += gen_fluids + gen_particles self._generate_fields(fields_to_generate) for field in list(self.field_data.keys()): if field not in ofields: self.field_data.pop(field) def _generate_fields(self, fields_to_generate): index = 0 def dimensions_compare_equal(a, b, /) -> bool: if a == b: return True try: if (a == 1 and b.is_dimensionless) or (a.is_dimensionless and b == 1): return True except AttributeError: return False return False with self._field_lock(): # At this point, we assume that any fields that are necessary to # *generate* a field are in fact already available to us. Note # that we do not make any assumption about whether or not the # fields have a spatial requirement. This will be checked inside # _generate_field, at which point additional dependencies may # actually be noted. while any(f not in self.field_data for f in fields_to_generate): field = fields_to_generate[index % len(fields_to_generate)] index += 1 if field in self.field_data: continue fi = self.ds._get_field_info(field) try: fd = self._generate_field(field) if hasattr(fd, "units"): fd.units.registry = self.ds.unit_registry if fd is None: raise RuntimeError if fi.units is None: # first time calling a field with units='auto', so we # infer the units from the units of the data we get back # from the field function and use these units for future # field accesses units = getattr(fd, "units", "") if units == "": sunits = "" dimensions = 1 else: sunits = str( units.get_base_equivalent(self.ds.unit_system.name) ) dimensions = units.dimensions if fi.dimensions is None: mylog.warning( "Field %s was added without specifying units or dimensions, " "auto setting units to %r", fi.name, sunits or "dimensionless", ) elif not dimensions_compare_equal(fi.dimensions, dimensions): raise YTDimensionalityError(fi.dimensions, dimensions) fi.units = sunits fi.dimensions = dimensions self.field_data[field] = self.ds.arr(fd, units) if fi.output_units is None: fi.output_units = fi.units try: fd.convert_to_units(fi.units) except AttributeError: # If the field returns an ndarray, coerce to a # dimensionless YTArray and verify that field is # supposed to be unitless fd = self.ds.arr(fd, "") if fi.units != "": raise YTFieldUnitError(fi, fd.units) from None except UnitConversionError as e: raise YTFieldUnitError(fi, fd.units) from e except UnitParseError as e: raise YTFieldUnitParseError(fi) from e self.field_data[field] = fd except GenerationInProgress as gip: for f in gip.fields: if f not in fields_to_generate: fields_to_generate.append(f) def __or__(self, other): if not isinstance(other, YTSelectionContainer): raise YTBooleanObjectError(other) if self.ds is not other.ds: raise YTBooleanObjectsWrongDataset() # Should maybe do something with field parameters here from yt.data_objects.selection_objects.boolean_operations import ( YTBooleanContainer, ) return YTBooleanContainer("OR", self, other, ds=self.ds) def __invert__(self): # ~obj asel = yt.geometry.selection_routines.AlwaysSelector(self.ds) from yt.data_objects.selection_objects.boolean_operations import ( YTBooleanContainer, ) return YTBooleanContainer("NOT", self, asel, ds=self.ds) def __xor__(self, other): if not isinstance(other, YTSelectionContainer): raise YTBooleanObjectError(other) if self.ds is not other.ds: raise YTBooleanObjectsWrongDataset() from yt.data_objects.selection_objects.boolean_operations import ( YTBooleanContainer, ) return YTBooleanContainer("XOR", self, other, ds=self.ds) def __and__(self, other): if not isinstance(other, YTSelectionContainer): raise YTBooleanObjectError(other) if self.ds is not other.ds: raise YTBooleanObjectsWrongDataset() from yt.data_objects.selection_objects.boolean_operations import ( YTBooleanContainer, ) return YTBooleanContainer("AND", self, other, ds=self.ds) def __add__(self, other): return self.__or__(other) def __sub__(self, other): if not isinstance(other, YTSelectionContainer): raise YTBooleanObjectError(other) if self.ds is not other.ds: raise YTBooleanObjectsWrongDataset() from yt.data_objects.selection_objects.boolean_operations import ( YTBooleanContainer, ) return YTBooleanContainer("NEG", self, other, ds=self.ds) @contextmanager def _field_lock(self): self._locked = True yield self._locked = False @contextmanager def _ds_hold(self, new_ds): """ This contextmanager is used to take a data object and preserve its attributes but allow the dataset that underlies it to be swapped out. This is typically only used internally, and differences in unit systems may present interesting possibilities. """ old_ds = self.ds old_index = self._index self.ds = new_ds self._index = new_ds.index old_chunk_info = self._chunk_info old_chunk = self._current_chunk old_size = self.size self._chunk_info = None self._current_chunk = None self.size = None self._index._identify_base_chunk(self) with self._chunked_read(None): yield self._index = old_index self.ds = old_ds self._chunk_info = old_chunk_info self._current_chunk = old_chunk self.size = old_size @contextmanager def _chunked_read(self, chunk): # There are several items that need to be swapped out # field_data, size, shape obj_field_data = [] if hasattr(chunk, "objs"): for obj in chunk.objs: obj_field_data.append(obj.field_data) obj.field_data = YTFieldData() old_field_data, self.field_data = self.field_data, YTFieldData() old_chunk, self._current_chunk = self._current_chunk, chunk old_locked, self._locked = self._locked, False yield self.field_data = old_field_data self._current_chunk = old_chunk self._locked = old_locked if hasattr(chunk, "objs"): for obj in chunk.objs: obj.field_data = obj_field_data.pop(0) @contextmanager def _activate_cache(self): cache = self._field_cache or {} old_fields = {} for field in (f for f in cache if f in self.field_data): old_fields[field] = self.field_data[field] self.field_data.update(cache) yield for field in cache: self.field_data.pop(field) if field in old_fields: self.field_data[field] = old_fields.pop(field) self._field_cache = None def _initialize_cache(self, cache): # Wipe out what came before self._field_cache = {} self._field_cache.update(cache) @property def icoords(self): if self._current_chunk is None: self.index._identify_base_chunk(self) return self._current_chunk.icoords @property def fcoords(self): if self._current_chunk is None: self.index._identify_base_chunk(self) return self._current_chunk.fcoords @property def ires(self): if self._current_chunk is None: self.index._identify_base_chunk(self) return self._current_chunk.ires @property def fwidth(self): if self._current_chunk is None: self.index._identify_base_chunk(self) return self._current_chunk.fwidth @property def fcoords_vertex(self): if self._current_chunk is None: self.index._identify_base_chunk(self) return self._current_chunk.fcoords_vertex @property def max_level(self): if self._max_level is None: try: return self.ds.max_level except AttributeError: return None return self._max_level @max_level.setter def max_level(self, value): if self._selector is not None: del self._selector self._selector = None self._current_chunk = None self.size = None self.shape = None self.field_data.clear() self._max_level = value @property def min_level(self): if self._min_level is None: try: return 0 except AttributeError: return None return self._min_level @min_level.setter def min_level(self, value): if self._selector is not None: del self._selector self._selector = None self.field_data.clear() self.size = None self.shape = None self._current_chunk = None self._min_level = value class YTSelectionContainer0D(YTSelectionContainer): _spatial = False _dimensionality = 0 def __init__(self, ds, field_parameters=None, data_source=None): super().__init__(ds, field_parameters, data_source) class YTSelectionContainer1D(YTSelectionContainer): _spatial = False _dimensionality = 1 def __init__(self, ds, field_parameters=None, data_source=None): super().__init__(ds, field_parameters, data_source) self._grids = None self._sortkey = None self._sorted = {} class YTSelectionContainer2D(YTSelectionContainer): _key_fields = ["px", "py", "pdx", "pdy"] _dimensionality = 2 """ Prepares the YTSelectionContainer2D, normal to *axis*. If *axis* is 4, we are not aligned with any axis. """ _spatial = False def __init__(self, axis, ds, field_parameters=None, data_source=None): super().__init__(ds, field_parameters, data_source) # We need the ds, which will exist by now, for fix_axis. self.axis = fix_axis(axis, self.ds) self.set_field_parameter("axis", axis) def _convert_field_name(self, field): return field def _get_pw(self, fields, center, width, origin, plot_type): from yt.visualization.fixed_resolution import FixedResolutionBuffer as frb from yt.visualization.plot_window import PWViewerMPL, get_window_parameters axis = self.axis skip = self._key_fields skip += list(set(frb._exclude_fields).difference(set(self._key_fields))) self.fields = [k for k in self.field_data if k not in skip] if fields is not None: self.fields = list(iter_fields(fields)) + self.fields if len(self.fields) == 0: raise ValueError("No fields found to plot in get_pw") (bounds, center, display_center) = get_window_parameters( axis, center, width, self.ds ) pw = PWViewerMPL( self, bounds, fields=self.fields, origin=origin, frb_generator=frb, plot_type=plot_type, geometry=self.ds.geometry, ) pw._setup_plots() return pw def to_frb(self, width, resolution, center=None, height=None, periodic=False): r"""This function returns a FixedResolutionBuffer generated from this object. A FixedResolutionBuffer is an object that accepts a variable-resolution 2D object and transforms it into an NxM bitmap that can be plotted, examined or processed. This is a convenience function to return an FRB directly from an existing 2D data object. Parameters ---------- width : width specifier This can either be a floating point value, in the native domain units of the simulation, or a tuple of the (value, unit) style. This will be the width of the FRB. height : height specifier This will be the physical height of the FRB, by default it is equal to width. Note that this will not make any corrections to resolution for the aspect ratio. resolution : int or tuple of ints The number of pixels on a side of the final FRB. If iterable, this will be the width then the height. center : array-like of floats, optional The center of the FRB. If not specified, defaults to the center of the current object. periodic : bool Should the returned Fixed Resolution Buffer be periodic? (default: False). Returns ------- frb : :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer` A fixed resolution buffer, which can be queried for fields. Examples -------- >>> proj = ds.proj(("gas", "density"), 0) >>> frb = proj.to_frb((100.0, "kpc"), 1024) >>> write_image(np.log10(frb["gas", "density"]), "density_100kpc.png") """ if (self.ds.geometry is Geometry.CYLINDRICAL and self.axis == 1) or ( self.ds.geometry is Geometry.POLAR and self.axis == 2 ): if center is not None and center != (0.0, 0.0): raise NotImplementedError( "Currently we only support images centered at R=0. " + "We plan to generalize this in the near future" ) from yt.visualization.fixed_resolution import ( CylindricalFixedResolutionBuffer, ) validate_width_tuple(width) if is_sequence(resolution): resolution = max(resolution) frb = CylindricalFixedResolutionBuffer(self, width, resolution) return frb if center is None: center = self.center if center is None: center = (self.ds.domain_right_edge + self.ds.domain_left_edge) / 2.0 elif is_sequence(center) and not isinstance(center, YTArray): center = self.ds.arr(center, "code_length") if is_sequence(width): w, u = width if isinstance(w, tuple) and isinstance(u, tuple): height = u w, u = w width = self.ds.quan(w, units=u) elif not isinstance(width, YTArray): width = self.ds.quan(width, "code_length") if height is None: height = width elif is_sequence(height): h, u = height height = self.ds.quan(h, units=u) elif not isinstance(height, YTArray): height = self.ds.quan(height, "code_length") if not is_sequence(resolution): resolution = (resolution, resolution) from yt.visualization.fixed_resolution import FixedResolutionBuffer xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] bounds = ( center[xax] - width * 0.5, center[xax] + width * 0.5, center[yax] - height * 0.5, center[yax] + height * 0.5, ) frb = FixedResolutionBuffer(self, bounds, resolution, periodic=periodic) return frb class YTSelectionContainer3D(YTSelectionContainer): """ Returns an instance of YTSelectionContainer3D, or prepares one. Usually only used as a base class. Note that *center* is supplied, but only used for fields and quantities that require it. """ _key_fields = ["x", "y", "z", "dx", "dy", "dz"] _spatial = False _num_ghost_zones = 0 _dimensionality = 3 def __init__(self, center, ds, field_parameters=None, data_source=None): super().__init__(ds, field_parameters, data_source) self._set_center(center) self.coords = None self._grids = None def cut_region(self, field_cuts, field_parameters=None, locals=None): """ Return a YTCutRegion, where the a cell is identified as being inside the cut region based on the value of one or more fields. Note that in previous versions of yt the name 'grid' was used to represent the data object used to construct the field cut, as of yt 3.0, this has been changed to 'obj'. Parameters ---------- field_cuts : list of strings A list of conditionals that will be evaluated. In the namespace available, these conditionals will have access to 'obj' which is a data object of unknown shape, and they must generate a boolean array. For instance, conditionals = ["obj['gas', 'temperature'] < 1e3"] field_parameters : dictionary A dictionary of field parameters to be used when applying the field cuts. locals : dictionary A dictionary of local variables to use when defining the cut region. Examples -------- To find the total mass of hot gas with temperature greater than 10^6 K in your volume: >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.cut_region(["obj['gas', 'temperature'] > 1e6"]) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ if locals is None: locals = {} cr = self.ds.cut_region( self, field_cuts, field_parameters=field_parameters, locals=locals ) return cr def _build_operator_cut(self, operation, field, value, units=None): """ Given an operation (>, >=, etc.), a field and a value, return the cut_region implementing it. This is only meant to be used internally. Examples -------- >>> ds._build_operator_cut(">", ("gas", "density"), 1e-24) ... # is equivalent to ... ds.cut_region(['obj["gas", "density"] > 1e-24']) """ ftype, fname = self._determine_fields(field)[0] if units is None: field_cuts = f'obj["{ftype}", "{fname}"] {operation} {value}' else: field_cuts = ( f'obj["{ftype}", "{fname}"].in_units("{units}") {operation} {value}' ) return self.cut_region(field_cuts) def _build_function_cut(self, function, field, units=None, **kwargs): """ Given a function (np.abs, np.all) and a field, return the cut_region implementing it. This is only meant to be used internally. Examples -------- >>> ds._build_function_cut("np.isnan", ("gas", "density"), locals={"np": np}) ... # is equivalent to ... ds.cut_region(['np.isnan(obj["gas", "density"])'], locals={"np": np}) """ ftype, fname = self._determine_fields(field)[0] if units is None: field_cuts = f'{function}(obj["{ftype}", "{fname}"])' else: field_cuts = f'{function}(obj["{ftype}", "{fname}"].in_units("{units}"))' return self.cut_region(field_cuts, **kwargs) def exclude_above(self, field, value, units=None): """ This function will return a YTCutRegion where all of the regions whose field is above a given value are masked. Parameters ---------- field : string The field in which the conditional will be applied. value : float The minimum value that will not be masked in the output YTCutRegion. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field above the given value masked. Examples -------- To find the total mass of hot gas with temperature colder than 10^6 K in your volume: >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_above(("gas", "temperature"), 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_operator_cut("<=", field, value, units) def include_above(self, field, value, units=None): """ This function will return a YTCutRegion where only the regions whose field is above a given value are included. Parameters ---------- field : string The field in which the conditional will be applied. value : float The minimum value that will not be masked in the output YTCutRegion. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field above the given value masked. Examples -------- To find the total mass of hot gas with temperature warmer than 10^6 K in your volume: >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.include_above(("gas", "temperature"), 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_operator_cut(">", field, value, units) def exclude_equal(self, field, value, units=None): """ This function will return a YTCutRegion where all of the regions whose field are equal to given value are masked. Parameters ---------- field : string The field in which the conditional will be applied. value : float The minimum value that will not be masked in the output YTCutRegion. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field equal to the given value masked. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_equal(("gas", "temperature"), 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_operator_cut("!=", field, value, units) def include_equal(self, field, value, units=None): """ This function will return a YTCutRegion where only the regions whose field are equal to given value are included. Parameters ---------- field : string The field in which the conditional will be applied. value : float The minimum value that will not be masked in the output YTCutRegion. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field equal to the given value included. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.include_equal(("gas", "temperature"), 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_operator_cut("==", field, value, units) def exclude_inside(self, field, min_value, max_value, units=None): """ This function will return a YTCutRegion where all of the regions whose field are inside the interval from min_value to max_value. Parameters ---------- field : string The field in which the conditional will be applied. min_value : float The minimum value inside the interval to be excluded. max_value : float The maximum value inside the interval to be excluded. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field inside the given interval excluded. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_inside(("gas", "temperature"), 1e5, 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ ftype, fname = self._determine_fields(field)[0] if units is None: field_cuts = ( f'(obj["{ftype}", "{fname}"] <= {min_value}) | ' f'(obj["{ftype}", "{fname}"] >= {max_value})' ) else: field_cuts = ( f'(obj["{ftype}", "{fname}"].in_units("{units}") <= {min_value}) | ' f'(obj["{ftype}", "{fname}"].in_units("{units}") >= {max_value})' ) cr = self.cut_region(field_cuts) return cr def include_inside(self, field, min_value, max_value, units=None): """ This function will return a YTCutRegion where only the regions whose field are inside the interval from min_value to max_value are included. Parameters ---------- field : string The field in which the conditional will be applied. min_value : float The minimum value inside the interval to be excluded. max_value : float The maximum value inside the interval to be excluded. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field inside the given interval excluded. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.include_inside(("gas", "temperature"), 1e5, 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ ftype, fname = self._determine_fields(field)[0] if units is None: field_cuts = ( f'(obj["{ftype}", "{fname}"] > {min_value}) & ' f'(obj["{ftype}", "{fname}"] < {max_value})' ) else: field_cuts = ( f'(obj["{ftype}", "{fname}"].in_units("{units}") > {min_value}) & ' f'(obj["{ftype}", "{fname}"].in_units("{units}") < {max_value})' ) cr = self.cut_region(field_cuts) return cr def exclude_outside(self, field, min_value, max_value, units=None): """ This function will return a YTCutRegion where all of the regions whose field are outside the interval from min_value to max_value. Parameters ---------- field : string The field in which the conditional will be applied. min_value : float The minimum value inside the interval to be excluded. max_value : float The maximum value inside the interval to be excluded. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field outside the given interval excluded. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_outside(("gas", "temperature"), 1e5, 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ cr = self.exclude_below(field, min_value, units) cr = cr.exclude_above(field, max_value, units) return cr def include_outside(self, field, min_value, max_value, units=None): """ This function will return a YTCutRegion where only the regions whose field are outside the interval from min_value to max_value are included. Parameters ---------- field : string The field in which the conditional will be applied. min_value : float The minimum value inside the interval to be excluded. max_value : float The maximum value inside the interval to be excluded. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field outside the given interval excluded. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_outside(("gas", "temperature"), 1e5, 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ cr = self.exclude_inside(field, min_value, max_value, units) return cr def exclude_below(self, field, value, units=None): """ This function will return a YTCutRegion where all of the regions whose field is below a given value are masked. Parameters ---------- field : string The field in which the conditional will be applied. value : float The minimum value that will not be masked in the output YTCutRegion. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the field below the given value masked. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_below(("gas", "temperature"), 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_operator_cut(">=", field, value, units) def exclude_nan(self, field, units=None): """ This function will return a YTCutRegion where all of the regions whose field is NaN are masked. Parameters ---------- field : string The field in which the conditional will be applied. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with the NaN entries of the field masked. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.exclude_nan(("gas", "temperature")) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_function_cut("~np.isnan", field, units, locals={"np": np}) def include_below(self, field, value, units=None): """ This function will return a YTCutRegion where only the regions whose field is below a given value are included. Parameters ---------- field : string The field in which the conditional will be applied. value : float The minimum value that will not be masked in the output YTCutRegion. units : string or None The units of the value threshold. None will use the default units given in the field. Returns ------- cut_region : YTCutRegion The YTCutRegion with only regions with the field below the given value included. Examples -------- >>> ds = yt.load("RedshiftOutput0005") >>> ad = ds.all_data() >>> cr = ad.include_below(("gas", "temperature"), 1e5, 1e6) >>> print(cr.quantities.total_quantity(("gas", "cell_mass")).in_units("Msun")) """ return self._build_operator_cut("<", field, value, units) def extract_isocontours( self, field, value, filename=None, rescale=False, sample_values=None ): r"""This identifies isocontours on a cell-by-cell basis, with no consideration of global connectedness, and returns the vertices of the Triangles in that isocontour. This function simply returns the vertices of all the triangles calculated by the `marching cubes `_ algorithm; for more complex operations, such as identifying connected sets of cells above a given threshold, see the extract_connected_sets function. This is more useful for calculating, for instance, total isocontour area, or visualizing in an external program (such as `MeshLab `_.) Parameters ---------- field : string Any field that can be obtained in a data object. This is the field which will be isocontoured. value : float The value at which the isocontour should be calculated. filename : string, optional If supplied, this file will be filled with the vertices in .obj format. Suitable for loading into meshlab. rescale : bool, optional If true, the vertices will be rescaled within their min/max. sample_values : string, optional Any field whose value should be extracted at the center of each triangle. Returns ------- verts : array of floats The array of vertices, x,y,z. Taken in threes, these are the triangle vertices. samples : array of floats If `sample_values` is specified, this will be returned and will contain the values of the field specified at the center of each triangle. Examples -------- This will create a data object, find a nice value in the center, and output the vertices to "triangles.obj" after rescaling them. >>> dd = ds.all_data() >>> rho = dd.quantities["WeightedAverageQuantity"]( ... ("gas", "density"), weight=("gas", "cell_mass") ... ) >>> verts = dd.extract_isocontours( ... ("gas", "density"), rho, "triangles.obj", True ... ) """ from yt.data_objects.static_output import ParticleDataset from yt.frontends.stream.data_structures import StreamParticlesDataset verts = [] samples = [] if isinstance(self.ds, (ParticleDataset, StreamParticlesDataset)): raise NotImplementedError for block, mask in self.blocks: my_verts = self._extract_isocontours_from_grid( block, mask, field, value, sample_values ) if sample_values is not None: my_verts, svals = my_verts samples.append(svals) verts.append(my_verts) verts = np.concatenate(verts).transpose() verts = self.comm.par_combine_object(verts, op="cat", datatype="array") verts = verts.transpose() if sample_values is not None: samples = np.concatenate(samples) samples = self.comm.par_combine_object(samples, op="cat", datatype="array") if rescale: mi = np.min(verts, axis=0) ma = np.max(verts, axis=0) verts = (verts - mi) / (ma - mi).max() if filename is not None and self.comm.rank == 0: if hasattr(filename, "write"): f = filename else: f = open(filename, "w") for v1 in verts: f.write(f"v {v1[0]:0.16e} {v1[1]:0.16e} {v1[2]:0.16e}\n") for i in range(len(verts) // 3): f.write(f"f {i * 3 + 1} {i * 3 + 2} {i * 3 + 3}\n") if not hasattr(filename, "write"): f.close() if sample_values is not None: return verts, samples return verts def _extract_isocontours_from_grid( self, grid, mask, field, value, sample_values=None ): vc_fields = [field] if sample_values is not None: vc_fields.append(sample_values) vc_data = grid.get_vertex_centered_data(vc_fields, no_ghost=False) try: svals = vc_data[sample_values] except KeyError: svals = None my_verts = march_cubes_grid( value, vc_data[field], mask, grid.LeftEdge, grid.dds, svals ) return my_verts def calculate_isocontour_flux( self, field, value, field_x, field_y, field_z, fluxing_field=None ): r"""This identifies isocontours on a cell-by-cell basis, with no consideration of global connectedness, and calculates the flux over those contours. This function will conduct `marching cubes `_ on all the cells in a given data container (grid-by-grid), and then for each identified triangular segment of an isocontour in a given cell, calculate the gradient (i.e., normal) in the isocontoured field, interpolate the local value of the "fluxing" field, the area of the triangle, and then return: area * local_flux_value * (n dot v) Where area, local_value, and the vector v are interpolated at the barycenter (weighted by the vertex values) of the triangle. Note that this specifically allows for the field fluxing across the surface to be *different* from the field being contoured. If the fluxing_field is not specified, it is assumed to be 1.0 everywhere, and the raw flux with no local-weighting is returned. Additionally, the returned flux is defined as flux *into* the surface, not flux *out of* the surface. Parameters ---------- field : string Any field that can be obtained in a data object. This is the field which will be isocontoured and used as the "local_value" in the flux equation. value : float The value at which the isocontour should be calculated. field_x : string The x-component field field_y : string The y-component field field_z : string The z-component field fluxing_field : string, optional The field whose passage over the surface is of interest. If not specified, assumed to be 1.0 everywhere. Returns ------- flux : float The summed flux. Note that it is not currently scaled; this is simply the code-unit area times the fields. Examples -------- This will create a data object, find a nice value in the center, and calculate the metal flux over it. >>> dd = ds.all_data() >>> rho = dd.quantities["WeightedAverageQuantity"]( ... ("gas", "density"), weight=("gas", "cell_mass") ... ) >>> flux = dd.calculate_isocontour_flux( ... ("gas", "density"), ... rho, ... ("gas", "velocity_x"), ... ("gas", "velocity_y"), ... ("gas", "velocity_z"), ... ("gas", "metallicity"), ... ) """ flux = 0.0 for block, mask in self.blocks: flux += self._calculate_flux_in_grid( block, mask, field, value, field_x, field_y, field_z, fluxing_field ) flux = self.comm.mpi_allreduce(flux, op="sum") return flux def _calculate_flux_in_grid( self, grid, mask, field, value, field_x, field_y, field_z, fluxing_field=None ): vc_fields = [field, field_x, field_y, field_z] if fluxing_field is not None: vc_fields.append(fluxing_field) vc_data = grid.get_vertex_centered_data(vc_fields) if fluxing_field is None: ff = np.ones_like(vc_data[field], dtype="float64") else: ff = vc_data[fluxing_field] return march_cubes_grid_flux( value, vc_data[field], vc_data[field_x], vc_data[field_y], vc_data[field_z], ff, mask, grid.LeftEdge, grid.dds, ) def extract_connected_sets( self, field, num_levels, min_val, max_val, log_space=True, cumulative=True ): """ This function will create a set of contour objects, defined by having connected cell structures, which can then be studied and used to 'paint' their source grids, thus enabling them to be plotted. Note that this function *can* return a connected set object that has no member values. """ if log_space: cons = np.logspace(np.log10(min_val), np.log10(max_val), num_levels + 1) else: cons = np.linspace(min_val, max_val, num_levels + 1) contours = {} for level in range(num_levels): contours[level] = {} if cumulative: mv = max_val else: mv = cons[level + 1] from yt.data_objects.level_sets.api import identify_contours from yt.data_objects.level_sets.clump_handling import add_contour_field nj, cids = identify_contours(self, field, cons[level], mv) unique_contours = set() for sl_list in cids.values(): for _sl, ff in sl_list: unique_contours.update(np.unique(ff)) contour_key = uuid.uuid4().hex # In case we're a cut region already... base_object = getattr(self, "base_object", self) add_contour_field(base_object.ds, contour_key) for cid in sorted(unique_contours): if cid == -1: continue contours[level][cid] = base_object.cut_region( [f"obj['contours_{contour_key}'] == {cid}"], {f"contour_slices_{contour_key}": cids}, ) return cons, contours def _get_bbox(self): """ Return the bounding box for this data container. This generic version will return the bounds of the entire domain. """ return self.ds.domain_left_edge, self.ds.domain_right_edge def get_bbox(self) -> tuple[unyt_array, unyt_array]: """ Return the bounding box for this data container. """ geometry: Geometry = self.ds.geometry if geometry is Geometry.CARTESIAN: le, re = self._get_bbox() return (le.to("code_length"), re.to("code_length")) elif ( geometry is Geometry.CYLINDRICAL or geometry is Geometry.POLAR or geometry is Geometry.SPHERICAL or geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): raise NotImplementedError( f"get_bbox is currently not implemented for {geometry=}!" ) else: assert_never(geometry) def volume(self): """ Return the volume of the data container. This is found by adding up the volume of the cells with centers in the container, rather than using the geometric shape of the container, so this may vary very slightly from what might be expected from the geometric volume. """ return self.quantities.total_quantity(("index", "cell_volume")) yt-project-yt-f043ac8/yt/data_objects/selection_objects/disk.py000066400000000000000000000061001510711153200247270ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.funcs import ( fix_length, validate_3d_array, validate_center, validate_float, validate_object, validate_sequence, ) class YTDisk(YTSelectionContainer3D): """ By providing a *center*, a *normal*, a *radius* and a *height* we can define a cylinder of any proportion. Only cells whose centers are within the cylinder will be selected. Parameters ---------- center : array_like coordinate to which the normal, radius, and height all reference normal : array_like the normal vector defining the direction of lengthwise part of the cylinder radius : float the radius of the cylinder height : float the distance from the midplane of the cylinder to the top and bottom planes fields : array of fields, optional any fields to be pre-loaded in the cylinder object ds: ~yt.data_objects.static_output.Dataset, optional An optional dataset to use rather than self.ds field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: optional Draw the selection from the provided data source rather than all data associated with the data_set Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> c = [0.5, 0.5, 0.5] >>> disk = ds.disk(c, [1, 0, 0], (1, "kpc"), (10, "kpc")) """ _type_name = "disk" _con_args = ("center", "_norm_vec", "radius", "height") def __init__( self, center, normal, radius, height, fields=None, ds=None, field_parameters=None, data_source=None, ): validate_center(center) validate_3d_array(normal) validate_float(radius) validate_float(height) validate_sequence(fields) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer3D.__init__(self, center, ds, field_parameters, data_source) self._norm_vec = np.array(normal) / np.sqrt(np.dot(normal, normal)) self.set_field_parameter("normal", self._norm_vec) self.set_field_parameter("center", self.center) self.height = fix_length(height, self.ds) self.radius = fix_length(radius, self.ds) self._d = -1.0 * np.dot(self._norm_vec, self.center) def _get_bbox(self): """ Return the minimum bounding box for the disk. """ # http://www.iquilezles.org/www/articles/diskbbox/diskbbox.htm pa = self.center + self._norm_vec * self.height pb = self.center - self._norm_vec * self.height a = pa - pb db = self.radius * np.sqrt(1.0 - a.d * a.d / np.dot(a.d, a.d)) return np.minimum(pa - db, pb - db), np.maximum(pa + db, pb + db) yt-project-yt-f043ac8/yt/data_objects/selection_objects/object_collection.py000066400000000000000000000020701510711153200274600ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.funcs import validate_center, validate_object, validate_sequence class YTDataCollection(YTSelectionContainer3D): """ By selecting an arbitrary *object_list*, we can act on those grids. Child cells are not returned. """ _type_name = "data_collection" _con_args = ("_obj_list",) def __init__( self, obj_list, ds=None, field_parameters=None, data_source=None, center=None ): validate_sequence(obj_list) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) if center is not None: validate_center(center) YTSelectionContainer3D.__init__(self, center, ds, field_parameters, data_source) self._obj_ids = np.array([o.id - o._id_offset for o in obj_list], dtype="int64") self._obj_list = obj_list yt-project-yt-f043ac8/yt/data_objects/selection_objects/point.py000066400000000000000000000032741510711153200251370ustar00rootroot00000000000000from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer0D, ) from yt.data_objects.static_output import Dataset from yt.funcs import validate_3d_array, validate_object from yt.units import YTArray class YTPoint(YTSelectionContainer0D): """ A 0-dimensional object defined by a single point Parameters ---------- p: array_like A points defined within the domain. If the domain is periodic its position will be corrected to lie inside the range [DLE,DRE) to ensure one and only one cell may match that point ds: ~yt.data_objects.static_output.Dataset, optional An optional dataset to use rather than self.ds field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: optional Draw the selection from the provided data source rather than all data associated with the data_set Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> c = [0.5, 0.5, 0.5] >>> point = ds.point(c) """ _type_name = "point" _con_args = ("p",) def __init__(self, p, ds=None, field_parameters=None, data_source=None): validate_3d_array(p) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) super().__init__(ds, field_parameters, data_source) if isinstance(p, YTArray): # we pass p through ds.arr to ensure code units are attached self.p = self.ds.arr(p) else: self.p = self.ds.arr(p, "code_length") yt-project-yt-f043ac8/yt/data_objects/selection_objects/ray.py000066400000000000000000000213011510711153200245700ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer1D, ) from yt.data_objects.static_output import Dataset from yt.frontends.sph.data_structures import SPHDataset from yt.funcs import ( fix_axis, validate_3d_array, validate_axis, validate_float, validate_object, validate_sequence, ) from yt.units import YTArray, YTQuantity from yt.units._numpy_wrapper_functions import udot, unorm from yt.utilities.lib.pixelization_routines import SPHKernelInterpolationTable from yt.utilities.logger import ytLogger as mylog class YTOrthoRay(YTSelectionContainer1D): """ This is an orthogonal ray cast through the entire domain, at a specific coordinate. This object is typically accessed through the `ortho_ray` object that hangs off of index objects. The resulting arrays have their dimensionality reduced to one, and an ordered list of points at an (x,y) tuple along `axis` are available. Parameters ---------- axis : int or char The axis along which to slice. Can be 0, 1, or 2 for x, y, z. coords : tuple of floats The (plane_x, plane_y) coordinates at which to cast the ray. Note that this is in the plane coordinates: so if you are casting along x, this will be (y, z). If you are casting along y, this will be (z, x). If you are casting along z, this will be (x, y). ds: ~yt.data_objects.static_output.Dataset, optional An optional dataset to use rather than self.ds field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: optional Draw the selection from the provided data source rather than all data associated with the data_set Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> oray = ds.ortho_ray(0, (0.2, 0.74)) >>> print(oray["gas", "density"]) Note: The low-level data representation for rays are not guaranteed to be spatially ordered. In particular, with AMR datasets, higher resolution data is tagged on to the end of the ray. If you want this data represented in a spatially ordered manner, manually sort it by the "t" field, which is the value of the parametric variable that goes from 0 at the start of the ray to 1 at the end: >>> my_ray = ds.ortho_ray(...) >>> ray_sort = np.argsort(my_ray["t"]) >>> density = my_ray["gas", "density"][ray_sort] """ _key_fields = ["x", "y", "z", "dx", "dy", "dz"] _type_name = "ortho_ray" _con_args = ("axis", "coords") def __init__(self, axis, coords, ds=None, field_parameters=None, data_source=None): validate_axis(ds, axis) validate_sequence(coords) for c in coords: validate_float(c) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) super().__init__(ds, field_parameters, data_source) self.axis = fix_axis(axis, self.ds) xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] self.px_ax = xax self.py_ax = yax # Even though we may not be using x,y,z we use them here. self.px_dx = f"d{'xyz'[self.px_ax]}" self.py_dx = f"d{'xyz'[self.py_ax]}" # Convert coordinates to code length. if isinstance(coords[0], YTQuantity): self.px = self.ds.quan(coords[0]).to("code_length") else: self.px = self.ds.quan(coords[0], "code_length") if isinstance(coords[1], YTQuantity): self.py = self.ds.quan(coords[1]).to("code_length") else: self.py = self.ds.quan(coords[1], "code_length") self.sort_by = "xyz"[self.axis] @property def coords(self): return (self.px, self.py) class YTRay(YTSelectionContainer1D): """ This is an arbitrarily-aligned ray cast through the entire domain, at a specific coordinate. This object is typically accessed through the `ray` object that hangs off of index objects. The resulting arrays have their dimensionality reduced to one, and an ordered list of points at an (x,y) tuple along `axis` are available, as is the `t` field, which corresponds to a unitless measurement along the ray from start to end. Parameters ---------- start_point : array-like set of 3 floats The place where the ray starts. end_point : array-like set of 3 floats The place where the ray ends. ds: ~yt.data_objects.static_output.Dataset, optional An optional dataset to use rather than self.ds field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: optional Draw the selection from the provided data source rather than all data associated with the data_set Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> ray = ds.ray((0.2, 0.74, 0.11), (0.4, 0.91, 0.31)) >>> print(ray["gas", "density"], ray["t"], ray["dts"]) Note: The low-level data representation for rays are not guaranteed to be spatially ordered. In particular, with AMR datasets, higher resolution data is tagged on to the end of the ray. If you want this data represented in a spatially ordered manner, manually sort it by the "t" field, which is the value of the parametric variable that goes from 0 at the start of the ray to 1 at the end: >>> my_ray = ds.ray(...) >>> ray_sort = np.argsort(my_ray["t"]) >>> density = my_ray["gas", "density"][ray_sort] """ _type_name = "ray" _con_args = ("start_point", "end_point") _container_fields = ("t", "dts") def __init__( self, start_point, end_point, ds=None, field_parameters=None, data_source=None ): validate_3d_array(start_point) validate_3d_array(end_point) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) super().__init__(ds, field_parameters, data_source) if isinstance(start_point, YTArray): self.start_point = self.ds.arr(start_point).to("code_length") else: self.start_point = self.ds.arr(start_point, "code_length", dtype="float64") if isinstance(end_point, YTArray): self.end_point = self.ds.arr(end_point).to("code_length") else: self.end_point = self.ds.arr(end_point, "code_length", dtype="float64") if (self.start_point < self.ds.domain_left_edge).any() or ( self.end_point > self.ds.domain_right_edge ).any(): mylog.warning( "Ray start or end is outside the domain. " "Returned data will only be for the ray section inside the domain." ) self.vec = self.end_point - self.start_point self._set_center(self.start_point) self.set_field_parameter("center", self.start_point) self._dts, self._ts = None, None def _generate_container_field(self, field): # What should we do with `ParticleDataset`? if isinstance(self.ds, SPHDataset): return self._generate_container_field_sph(field) else: return self._generate_container_field_grid(field) def _generate_container_field_grid(self, field): if self._current_chunk is None: self.index._identify_base_chunk(self) if field == "dts": return self._current_chunk.dtcoords elif field == "t": return self._current_chunk.tcoords else: raise KeyError(field) def _generate_container_field_sph(self, field): if field not in ["dts", "t"]: raise KeyError(field) length = unorm(self.vec) pos = self[self.ds._sph_ptypes[0], "particle_position"] r = pos - self.start_point l = udot(r, self.vec / length) if field == "t": return l / length hsml = self[self.ds._sph_ptypes[0], "smoothing_length"] mass = self[self.ds._sph_ptypes[0], "particle_mass"] dens = self[self.ds._sph_ptypes[0], "density"] # impact parameter from particle to ray b = np.sqrt(np.sum(r**2, axis=1) - l**2) # Use an interpolation table to evaluate the integrated 2D # kernel from the dimensionless impact parameter b/hsml. # The table tabulates integrals for values of (b/hsml)**2 itab = SPHKernelInterpolationTable(self.ds.kernel_name) dl = itab.interpolate_array((b / hsml) ** 2) * mass / dens / hsml**2 return dl / length yt-project-yt-f043ac8/yt/data_objects/selection_objects/region.py000066400000000000000000000045231510711153200252670ustar00rootroot00000000000000from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.funcs import ( validate_3d_array, validate_center, validate_object, validate_sequence, ) from yt.units import YTArray class YTRegion(YTSelectionContainer3D): """A 3D region of data with an arbitrary center. Takes an array of three *left_edge* coordinates, three *right_edge* coordinates, and a *center* that can be anywhere in the domain. If the selected region extends past the edges of the domain, no data will be found there, though the object's `left_edge` or `right_edge` are not modified. Parameters ---------- center : array_like The center of the region left_edge : array_like The left edge of the region right_edge : array_like The right edge of the region """ _type_name = "region" _con_args = ("center", "left_edge", "right_edge") def __init__( self, center, left_edge, right_edge, fields=None, ds=None, field_parameters=None, data_source=None, ): if center is not None: validate_center(center) validate_3d_array(left_edge) validate_3d_array(right_edge) validate_sequence(fields) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer3D.__init__(self, center, ds, field_parameters, data_source) if not isinstance(left_edge, YTArray): self.left_edge = self.ds.arr(left_edge, "code_length", dtype="float64") else: # need to assign this dataset's unit registry to the YTArray self.left_edge = self.ds.arr(left_edge.copy(), dtype="float64") if not isinstance(right_edge, YTArray): self.right_edge = self.ds.arr(right_edge, "code_length", dtype="float64") else: # need to assign this dataset's unit registry to the YTArray self.right_edge = self.ds.arr(right_edge.copy(), dtype="float64") def _get_bbox(self): """ Return the minimum bounding box for the region. """ return self.left_edge.copy(), self.right_edge.copy() yt-project-yt-f043ac8/yt/data_objects/selection_objects/slices.py000066400000000000000000000336471510711153200252770ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer2D, ) from yt.data_objects.static_output import Dataset from yt.funcs import ( fix_length, is_sequence, iter_fields, validate_3d_array, validate_axis, validate_center, validate_float, validate_object, validate_width_tuple, ) from yt.utilities.exceptions import YTNotInsideNotebook from yt.utilities.minimal_representation import MinimalSliceData from yt.utilities.orientation import Orientation class YTSlice(YTSelectionContainer2D): """ This is a data object corresponding to a slice through the simulation domain. This object is typically accessed through the `slice` object that hangs off of index objects. Slice is an orthogonal slice through the data, taking all the points at the finest resolution available and then indexing them. It is more appropriately thought of as a slice 'operator' than an object, however, as its field and coordinate can both change. Parameters ---------- axis : int or char The axis along which to slice. Can be 0, 1, or 2 for x, y, z. coord : float The coordinate along the axis at which to slice. This is in "domain" coordinates. center : array_like, optional The 'center' supplied to fields that use it. Note that this does not have to have `coord` as one value. optional. ds: ~yt.data_objects.static_output.Dataset, optional An optional dataset to use rather than self.ds field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: optional Draw the selection from the provided data source rather than all data associated with the data_set Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> slice = ds.slice(0, 0.25) >>> print(slice["gas", "density"]) """ _top_node = "/Slices" _type_name = "slice" _con_args = ("axis", "coord") _container_fields = ("px", "py", "pz", "pdx", "pdy", "pdz") def __init__( self, axis, coord, center=None, ds=None, field_parameters=None, data_source=None ): validate_axis(ds, axis) validate_float(coord) # center is an optional parameter if center is not None: validate_center(center) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer2D.__init__(self, axis, ds, field_parameters, data_source) self._set_center(center) self.coord = fix_length(coord, ds) def _generate_container_field(self, field): xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] if self._current_chunk is None: self.index._identify_base_chunk(self) if field == "px": return self._current_chunk.fcoords[:, xax] elif field == "py": return self._current_chunk.fcoords[:, yax] elif field == "pz": return self._current_chunk.fcoords[:, self.axis] elif field == "pdx": return self._current_chunk.fwidth[:, xax] * 0.5 elif field == "pdy": return self._current_chunk.fwidth[:, yax] * 0.5 elif field == "pdz": return self._current_chunk.fwidth[:, self.axis] * 0.5 else: raise KeyError(field) @property def _mrep(self): return MinimalSliceData(self) def to_pw(self, fields=None, center="center", width=None, origin="center-window"): r"""Create a :class:`~yt.visualization.plot_window.PWViewerMPL` from this object. This is a bare-bones mechanism of creating a plot window from this object, which can then be moved around, zoomed, and on and on. All behavior of the plot window is relegated to that routine. """ pw = self._get_pw(fields, center, width, origin, "Slice") return pw def plot(self, fields=None): if hasattr(self._data_source, "left_edge") and hasattr( self._data_source, "right_edge" ): left_edge = self._data_source.left_edge right_edge = self._data_source.right_edge center = (left_edge + right_edge) / 2.0 width = right_edge - left_edge xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] lx, rx = left_edge[xax], right_edge[xax] ly, ry = left_edge[yax], right_edge[yax] width = (rx - lx), (ry - ly) else: width = self.ds.domain_width center = self.ds.domain_center pw = self._get_pw(fields, center, width, "native", "Slice") try: pw.show() except YTNotInsideNotebook: pass return pw class YTCuttingPlane(YTSelectionContainer2D): """ This is a data object corresponding to an oblique slice through the simulation domain. This object is typically accessed through the `cutting` object that hangs off of index objects. A cutting plane is an oblique plane through the data, defined by a normal vector and a coordinate. It attempts to guess an 'north' vector, which can be overridden, and then it pixelizes the appropriate data onto the plane without interpolation. Parameters ---------- normal : array_like The vector that defines the desired plane. For instance, the angular momentum of a sphere. center : array_like The center of the cutting plane, where the normal vector is anchored. north_vector: array_like, optional An optional vector to describe the north-facing direction in the resulting plane. ds: ~yt.data_objects.static_output.Dataset, optional An optional dataset to use rather than self.ds field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: optional Draw the selection from the provided data source rather than all data associated with the dataset Notes ----- This data object in particular can be somewhat expensive to create. It's also important to note that unlike the other 2D data objects, this object provides px, py, pz, as some cells may have a height from the plane. Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> cp = ds.cutting([0.1, 0.2, -0.9], [0.5, 0.42, 0.6]) >>> print(cp["gas", "density"]) """ _plane = None _top_node = "/CuttingPlanes" _key_fields = YTSelectionContainer2D._key_fields + ["pz", "pdz"] _type_name = "cutting" _con_args = ("normal", "center") _tds_attrs = ("_inv_mat",) _tds_fields = ("x", "y", "z", "dx") _container_fields = ("px", "py", "pz", "pdx", "pdy", "pdz") def __init__( self, normal, center, north_vector=None, ds=None, field_parameters=None, data_source=None, ): validate_3d_array(normal) validate_center(center) if north_vector is not None: validate_3d_array(north_vector) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer2D.__init__(self, None, ds, field_parameters, data_source) self._set_center(center) self.set_field_parameter("center", self.center) # Let's set up our plane equation # ax + by + cz + d = 0 self.orienter = Orientation(normal, north_vector=north_vector) self._norm_vec = self.orienter.normal_vector self._d = -1.0 * np.dot(self._norm_vec, self.center) self._x_vec = self.orienter.unit_vectors[0] self._y_vec = self.orienter.unit_vectors[1] # First we try all three, see which has the best result: self._rot_mat = np.array([self._x_vec, self._y_vec, self._norm_vec]) self._inv_mat = np.linalg.pinv(self._rot_mat) self.set_field_parameter("cp_x_vec", self._x_vec) self.set_field_parameter("cp_y_vec", self._y_vec) self.set_field_parameter("cp_z_vec", self._norm_vec) @property def normal(self): return self._norm_vec def _generate_container_field(self, field): if self._current_chunk is None: self.index._identify_base_chunk(self) if field == "px": x = self._current_chunk.fcoords[:, 0] - self.center[0] y = self._current_chunk.fcoords[:, 1] - self.center[1] z = self._current_chunk.fcoords[:, 2] - self.center[2] tr = np.zeros(x.size, dtype="float64") tr = self.ds.arr(tr, "code_length") tr += x * self._x_vec[0] tr += y * self._x_vec[1] tr += z * self._x_vec[2] return tr elif field == "py": x = self._current_chunk.fcoords[:, 0] - self.center[0] y = self._current_chunk.fcoords[:, 1] - self.center[1] z = self._current_chunk.fcoords[:, 2] - self.center[2] tr = np.zeros(x.size, dtype="float64") tr = self.ds.arr(tr, "code_length") tr += x * self._y_vec[0] tr += y * self._y_vec[1] tr += z * self._y_vec[2] return tr elif field == "pz": x = self._current_chunk.fcoords[:, 0] - self.center[0] y = self._current_chunk.fcoords[:, 1] - self.center[1] z = self._current_chunk.fcoords[:, 2] - self.center[2] tr = np.zeros(x.size, dtype="float64") tr = self.ds.arr(tr, "code_length") tr += x * self._norm_vec[0] tr += y * self._norm_vec[1] tr += z * self._norm_vec[2] return tr elif field == "pdx": return self._current_chunk.fwidth[:, 0] * 0.5 elif field == "pdy": return self._current_chunk.fwidth[:, 1] * 0.5 elif field == "pdz": return self._current_chunk.fwidth[:, 2] * 0.5 else: raise KeyError(field) def to_pw(self, fields=None, center="center", width=None, axes_unit=None): r"""Create a :class:`~yt.visualization.plot_window.PWViewerMPL` from this object. This is a bare-bones mechanism of creating a plot window from this object, which can then be moved around, zoomed, and on and on. All behavior of the plot window is relegated to that routine. """ normal = self.normal center = self.center self.fields = list(iter_fields(fields)) + [ k for k in self.field_data.keys() if k not in self._key_fields ] from yt.visualization.fixed_resolution import FixedResolutionBuffer from yt.visualization.plot_window import ( PWViewerMPL, get_oblique_window_parameters, ) (bounds, center_rot) = get_oblique_window_parameters( normal, center, width, self.ds ) pw = PWViewerMPL( self, bounds, fields=self.fields, origin="center-window", periodic=False, oblique=True, frb_generator=FixedResolutionBuffer, plot_type="OffAxisSlice", ) if axes_unit is not None: pw.set_axes_unit(axes_unit) pw._setup_plots() return pw def to_frb(self, width, resolution, height=None, periodic=False): r"""This function returns a FixedResolutionBuffer generated from this object. An FixedResolutionBuffer is an object that accepts a variable-resolution 2D object and transforms it into an NxM bitmap that can be plotted, examined or processed. This is a convenience function to return an FRB directly from an existing 2D data object. Unlike the corresponding to_frb function for other YTSelectionContainer2D objects, this does not accept a 'center' parameter as it is assumed to be centered at the center of the cutting plane. Parameters ---------- width : width specifier This can either be a floating point value, in the native domain units of the simulation, or a tuple of the (value, unit) style. This will be the width of the FRB. height : height specifier, optional This will be the height of the FRB, by default it is equal to width. resolution : int or tuple of ints The number of pixels on a side of the final FRB. periodic : boolean This can be true or false, and governs whether the pixelization will span the domain boundaries. Returns ------- frb : :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer` A fixed resolution buffer, which can be queried for fields. Examples -------- >>> v, c = ds.find_max(("gas", "density")) >>> sp = ds.sphere(c, (100.0, "au")) >>> L = sp.quantities.angular_momentum_vector() >>> cutting = ds.cutting(L, c) >>> frb = cutting.to_frb((1.0, "pc"), 1024) >>> write_image(np.log10(frb["gas", "density"]), "density_1pc.png") """ if is_sequence(width): validate_width_tuple(width) width = self.ds.quan(width[0], width[1]) if height is None: height = width elif is_sequence(height): validate_width_tuple(height) height = self.ds.quan(height[0], height[1]) if not is_sequence(resolution): resolution = (resolution, resolution) from yt.visualization.fixed_resolution import FixedResolutionBuffer bounds = (-width / 2.0, width / 2.0, -height / 2.0, height / 2.0) frb = FixedResolutionBuffer(self, bounds, resolution, periodic=periodic) return frb yt-project-yt-f043ac8/yt/data_objects/selection_objects/spheroids.py000066400000000000000000000171741510711153200260120ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.funcs import ( fix_length, validate_3d_array, validate_center, validate_float, validate_object, validate_sequence, ) from yt.units import YTArray from yt.utilities.exceptions import YTEllipsoidOrdering, YTException, YTSphereTooSmall from yt.utilities.logger import ytLogger as mylog from yt.utilities.math_utils import get_rotation_matrix from yt.utilities.on_demand_imports import _miniball class YTSphere(YTSelectionContainer3D): """ A sphere of points defined by a *center* and a *radius*. Parameters ---------- center : array_like The center of the sphere. radius : float, width specifier, or YTQuantity The radius of the sphere. If passed a float, that will be interpreted in code units. Also accepts a (radius, unit) tuple or YTQuantity instance with units attached. Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> c = [0.5, 0.5, 0.5] >>> sphere = ds.sphere(c, (1.0, "kpc")) """ _type_name = "sphere" _con_args = ("center", "radius") def __init__( self, center, radius, ds=None, field_parameters=None, data_source=None ): validate_center(center) validate_float(radius) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) super().__init__(center, ds, field_parameters, data_source) # Unpack the radius, if necessary radius = fix_length(radius, self.ds) if radius < self.index.get_smallest_dx(): raise YTSphereTooSmall( ds, radius.in_units("code_length"), self.index.get_smallest_dx().in_units("code_length"), ) self.set_field_parameter("radius", radius) self.set_field_parameter("center", self.center) self.radius = radius def _get_bbox(self): """ Return the minimum bounding box for the sphere. """ return -self.radius + self.center, self.radius + self.center class YTMinimalSphere(YTSelectionContainer3D): """ Build the smallest sphere that encompasses a set of points. Parameters ---------- points : YTArray The points that the sphere will contain. Examples -------- >>> import yt >>> ds = yt.load("output_00080/info_00080.txt") >>> points = ds.r["particle_position"] >>> sphere = ds.minimal_sphere(points) """ _type_name = "sphere" _override_selector_name = "minimal_sphere" _con_args = ("center", "radius") def __init__(self, points, ds=None, field_parameters=None, data_source=None): validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) validate_object(points, YTArray) points = fix_length(points, ds) if len(points) < 2: raise YTException( f"Not enough points. Expected at least 2, got {len(points)}" ) mylog.debug("Building minimal sphere around points.") mb = _miniball.Miniball(points) if not mb.is_valid(): raise YTException("Could not build valid sphere around points.") center = ds.arr(mb.center(), points.units) radius = ds.quan(np.sqrt(mb.squared_radius()), points.units) super().__init__(center, ds, field_parameters, data_source) self.set_field_parameter("radius", radius) self.set_field_parameter("center", self.center) self.radius = radius class YTEllipsoid(YTSelectionContainer3D): """ By providing a *center*,*A*,*B*,*C*,*e0*,*tilt* we can define a ellipsoid of any proportion. Only cells whose centers are within the ellipsoid will be selected. Parameters ---------- center : array_like The center of the ellipsoid. A : float The magnitude of the largest axis (semi-major) of the ellipsoid. B : float The magnitude of the medium axis (semi-medium) of the ellipsoid. C : float The magnitude of the smallest axis (semi-minor) of the ellipsoid. e0 : array_like (automatically normalized) the direction of the largest semi-major axis of the ellipsoid tilt : float After the rotation about the z-axis to align e0 to x in the x-y plane, and then rotating about the y-axis to align e0 completely to the x-axis, tilt is the angle in radians remaining to rotate about the x-axis to align both e1 to the y-axis and e2 to the z-axis. Examples -------- >>> import yt >>> ds = yt.load("RedshiftOutput0005") >>> c = [0.5, 0.5, 0.5] >>> ell = ds.ellipsoid(c, 0.1, 0.1, 0.1, np.array([0.1, 0.1, 0.1]), 0.2) """ _type_name = "ellipsoid" _con_args = ("center", "_A", "_B", "_C", "_e0", "_tilt") def __init__( self, center, A, B, C, e0, tilt, fields=None, ds=None, field_parameters=None, data_source=None, ): validate_center(center) validate_float(A) validate_float(B) validate_float(C) validate_3d_array(e0) validate_float(tilt) validate_sequence(fields) validate_object(ds, Dataset) validate_object(field_parameters, dict) validate_object(data_source, YTSelectionContainer) YTSelectionContainer3D.__init__(self, center, ds, field_parameters, data_source) # make sure the magnitudes of semi-major axes are in order if A < B or B < C: raise YTEllipsoidOrdering(ds, A, B, C) # make sure the smallest side is not smaller than dx self._A = self.ds.quan(A, "code_length") self._B = self.ds.quan(B, "code_length") self._C = self.ds.quan(C, "code_length") if self._C < self.index.get_smallest_dx(): raise YTSphereTooSmall(self.ds, self._C, self.index.get_smallest_dx()) self._e0 = e0 = e0 / (e0**2.0).sum() ** 0.5 self._tilt = tilt # find the t1 angle needed to rotate about z axis to align e0 to x t1 = np.arctan(e0[1] / e0[0]) # rotate e0 by -t1 RZ = get_rotation_matrix(t1, (0, 0, 1)).transpose() r1 = (e0 * RZ).sum(axis=1) # find the t2 angle needed to rotate about y axis to align e0 to x t2 = np.arctan(-r1[2] / r1[0]) """ calculate the original e1 given the tilt about the x axis when e0 was aligned to x after t1, t2 rotations about z, y """ RX = get_rotation_matrix(-tilt, (1, 0, 0)).transpose() RY = get_rotation_matrix(-t2, (0, 1, 0)).transpose() RZ = get_rotation_matrix(-t1, (0, 0, 1)).transpose() e1 = ((0, 1, 0) * RX).sum(axis=1) e1 = (e1 * RY).sum(axis=1) e1 = (e1 * RZ).sum(axis=1) e2 = np.cross(e0, e1) self._e1 = e1 self._e2 = e2 self.set_field_parameter("A", A) self.set_field_parameter("B", B) self.set_field_parameter("C", C) self.set_field_parameter("e0", e0) self.set_field_parameter("e1", e1) self.set_field_parameter("e2", e2) def _get_bbox(self): """ Get the bounding box for the ellipsoid. NOTE that in this case it is not the *minimum* bounding box. """ radius = self.ds.arr(np.max([self._A, self._B, self._C]), "code_length") return -radius + self.center, radius + self.center yt-project-yt-f043ac8/yt/data_objects/static_output.py000066400000000000000000002342551510711153200232240ustar00rootroot00000000000000import abc import functools import hashlib import itertools import os import pickle import sys import time import warnings import weakref from collections import defaultdict from collections.abc import MutableMapping from functools import cached_property from importlib.util import find_spec from stat import ST_CTIME from typing import TYPE_CHECKING, Any, Literal, Optional import numpy as np import unyt as un from more_itertools import unzip from unyt import Unit, UnitSystem, unyt_quantity from unyt.exceptions import UnitConversionError, UnitParseError from yt._maintenance.deprecation import issue_deprecation_warning from yt._maintenance.ipython_compat import IPYWIDGETS_ENABLED from yt._typing import ( AnyFieldKey, AxisOrder, FieldKey, FieldType, ImplicitFieldKey, ParticleType, ) from yt.config import ytcfg from yt.data_objects.particle_filters import ParticleFilter, filter_registry from yt.data_objects.region_expression import RegionExpression from yt.data_objects.unions import ParticleUnion from yt.fields.derived_field import DerivedField, ValidateSpatial from yt.fields.field_type_container import FieldTypeContainer from yt.fields.fluid_fields import setup_gradient_fields from yt.funcs import iter_fields, mylog, set_intersection, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.coordinates.api import ( CartesianCoordinateHandler, CoordinateHandler, CylindricalCoordinateHandler, GeographicCoordinateHandler, InternalGeographicCoordinateHandler, PolarCoordinateHandler, SpectralCubeCoordinateHandler, SphericalCoordinateHandler, ) from yt.geometry.geometry_handler import Index from yt.units import UnitContainer, _wrap_display_ytarray, dimensions from yt.units.dimensions import current_mks from yt.units.unit_object import define_unit # type: ignore from yt.units.unit_registry import UnitRegistry # type: ignore from yt.units.unit_systems import ( # type: ignore create_code_unit_system, unit_system_registry, ) from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.configure import YTConfig, configuration_callbacks from yt.utilities.cosmology import Cosmology from yt.utilities.exceptions import ( YTFieldNotFound, YTFieldNotParseable, YTIllDefinedParticleFilter, YTObjectNotImplemented, ) from yt.utilities.lib.fnv_hash import fnv_hash from yt.utilities.minimal_representation import MinimalDataset from yt.utilities.object_registries import data_object_registry, output_type_registry from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_root_only from yt.utilities.parameter_file_storage import NoParameterShelf, ParameterFileStore if TYPE_CHECKING: from sympy import Symbol if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never # We want to support the movie format in the future. # When such a thing comes to pass, I'll move all the stuff that is constant up # to here, and then have it instantiate EnzoDatasets as appropriate. _cached_datasets: MutableMapping[int | str, "Dataset"] = weakref.WeakValueDictionary() # we set this global to None as a place holder # its actual instantiation is delayed until after yt.__init__ # is completed because we need yt.config.ytcfg to be instantiated first _ds_store: ParameterFileStore | None = None def _setup_ds_store(ytcfg: YTConfig) -> None: global _ds_store _ds_store = ParameterFileStore() configuration_callbacks.append(_setup_ds_store) def _unsupported_object(ds, obj_name): def _raise_unsupp(*args, **kwargs): raise YTObjectNotImplemented(ds, obj_name) return _raise_unsupp class MutableAttribute: """A descriptor for mutable data""" def __init__(self, display_array=False): self.data = weakref.WeakKeyDictionary() # We can assume that ipywidgets will not be *added* to the system # during the course of execution, and if it is, we will not wrap the # array. self.display_array = display_array and IPYWIDGETS_ENABLED def __get__(self, instance, owner): return self.data.get(instance, None) def __set__(self, instance, value): if self.display_array: try: value._ipython_display_ = functools.partial( _wrap_display_ytarray, value ) except AttributeError: # If they have slots, we can't assign a new item. So, let's catch # the error! pass if isinstance(value, np.ndarray): value.flags.writeable = False self.data[instance] = value def requires_index(attr_name): @property def ireq(self): self.index # By now it should have been set attr = self.__dict__[attr_name] return attr @ireq.setter def ireq(self, value): self.__dict__[attr_name] = value return ireq class Dataset(abc.ABC): _load_requirements: list[str] = [] default_fluid_type = "gas" default_field = ("gas", "density") fluid_types: tuple[FieldType, ...] = ("gas", "deposit", "index") particle_types: tuple[ParticleType, ...] = ("io",) # By default we have an 'all' particle_types_raw: tuple[ParticleType, ...] | None = ("io",) geometry: Geometry = Geometry.CARTESIAN coordinates = None storage_filename = None particle_unions: dict[ParticleType, ParticleUnion] | None = None known_filters: dict[ParticleType, ParticleFilter] | None = None _index_class: type[Index] field_units: dict[AnyFieldKey, Unit] | None = None derived_field_list = requires_index("derived_field_list") fields = requires_index("fields") _field_info = None conversion_factors: dict[str, float] | None = None # _instantiated represents an instantiation time (since Epoch) # the default is a place holder sentinel, falsy value _instantiated: float = 0 _particle_type_counts = None _proj_type = "quad_proj" _ionization_label_format = "roman_numeral" _determined_fields: dict[str, list[FieldKey]] | None = None fields_detected = False # these are set in self._parse_parameter_file() domain_left_edge = MutableAttribute(True) domain_right_edge = MutableAttribute(True) domain_dimensions = MutableAttribute(True) # the point in index space "domain_left_edge" doesn't necessarily # map to (0, 0, 0) domain_offset = np.zeros(3, dtype="int64") _periodicity = MutableAttribute() _force_periodicity = False # these are set in self._set_derived_attrs() domain_width = MutableAttribute(True) domain_center = MutableAttribute(True) def __new__(cls, filename=None, *args, **kwargs): if not isinstance(filename, str): obj = object.__new__(cls) # The Stream frontend uses a StreamHandler object to pass metadata # to __init__. is_stream = hasattr(filename, "get_fields") and hasattr( filename, "get_particle_type" ) if not is_stream: obj.__init__(filename, *args, **kwargs) return obj apath = os.path.abspath(os.path.expanduser(filename)) cache_key = (apath, pickle.dumps(args), pickle.dumps(kwargs)) if ytcfg.get("yt", "skip_dataset_cache"): obj = object.__new__(cls) elif cache_key not in _cached_datasets: obj = object.__new__(cls) if not obj._skip_cache: _cached_datasets[cache_key] = obj else: obj = _cached_datasets[cache_key] return obj def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) if cls.__name__ in output_type_registry: warnings.warn( f"Overwriting {cls.__name__}, which was previously registered. " "This is expected if you're importing a yt extension with a " "frontend that was already migrated to the main code base.", stacklevel=2, ) output_type_registry[cls.__name__] = cls mylog.debug("Registering: %s as %s", cls.__name__, cls) def __init__( self, filename: str, dataset_type: str | None = None, units_override: dict[str, str] | None = None, # valid unit_system values include all keys from unyt.unit_systems.unit_systems_registry + "code" unit_system: Literal[ "cgs", "mks", "imperial", "galactic", "solar", "geometrized", "planck", "code", ] = "cgs", default_species_fields: Optional[ "Any" ] = None, # Any used as a placeholder here *, axis_order: AxisOrder | None = None, ) -> None: """ Base class for generating new output types. Principally consists of a *filename* and a *dataset_type* which will be passed on to children. """ # We return early and do NOT initialize a second time if this file has # already been initialized. if self._instantiated != 0: return self.dataset_type = dataset_type self.conversion_factors = {} self.parameters: dict[str, Any] = {} self.region_expression = self.r = RegionExpression(self) self.known_filters = self.known_filters or {} self.particle_unions = self.particle_unions or {} self.field_units = self.field_units or {} self._determined_fields = {} self.units_override = self.__class__._sanitize_units_override(units_override) self.default_species_fields = default_species_fields self._input_filename: str = os.fspath(filename) # to get the timing right, do this before the heavy lifting self._instantiated = time.time() self.no_cgs_equiv_length = False if unit_system == "code": # create a fake MKS unit system which we will override later to # avoid chicken/egg issue of the unit registry needing a unit system # but code units need a unit registry to define the code units on used_unit_system = "mks" else: used_unit_system = unit_system self._create_unit_registry(used_unit_system) self._parse_parameter_file() self.set_units() self.setup_cosmology() self._assign_unit_system(unit_system) self._setup_coordinate_handler(axis_order) self.print_key_parameters() self._set_derived_attrs() # Because we need an instantiated class to check the ds's existence in # the cache, we move that check to here from __new__. This avoids # double-instantiation. # PR 3124: _set_derived_attrs() can change the hash, check store here if _ds_store is None: raise RuntimeError( "Something went wrong during yt's initialization: " "dataset cache isn't properly initialized" ) try: _ds_store.check_ds(self) except NoParameterShelf: pass self._setup_classes() @property def filename(self): if self._input_filename.startswith("http"): return self._input_filename else: return os.path.abspath(os.path.expanduser(self._input_filename)) @property def parameter_filename(self): # historic alias return self.filename @property def basename(self): return os.path.basename(self.filename) @property def directory(self): return os.path.dirname(self.filename) @property def fullpath(self): issue_deprecation_warning( "the Dataset.fullpath attribute is now aliased to Dataset.directory, " "and all path attributes are now absolute. " "Please use the directory attribute instead", stacklevel=3, since="4.1", ) return self.directory @property def backup_filename(self): name, _ext = os.path.splitext(self.filename) return name + "_backup.gdf" @cached_property def unique_identifier(self) -> str: retv = int(os.stat(self.parameter_filename)[ST_CTIME]) name_as_bytes = bytearray(self.parameter_filename.encode("utf-8")) retv += fnv_hash(name_as_bytes) return str(retv) @property def periodicity(self): if self._force_periodicity: return (True, True, True) elif getattr(self, "_domain_override", False): # dataset loaded with a bounding box return (False, False, False) return self._periodicity def force_periodicity(self, val=True): """ Override box periodicity to (True, True, True). Use ds.force_periodicty(False) to use the actual box periodicity. """ # This is a user-facing method that embrace a long-standing # workaround in yt user codes. if not isinstance(val, bool): raise TypeError("force_periodicity expected a boolean.") self._force_periodicity = val @classmethod def _missing_load_requirements(cls) -> list[str]: # return a list of optional dependencies that are # needed for the present class to function, but currently missing. # returning an empty list means that all requirements are met return [name for name in cls._load_requirements if not find_spec(name)] # abstract methods require implementation in subclasses @classmethod @abc.abstractmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # A heuristic test to determine if the data format can be interpreted # with the present frontend return False @abc.abstractmethod def _parse_parameter_file(self): # set up various attributes from self.parameter_filename # for a full description of what is required here see # yt.frontends._skeleton.SkeletonDataset pass @abc.abstractmethod def _set_code_unit_attributes(self): # set up code-units to physical units normalization factors # for a full description of what is required here see # yt.frontends._skeleton.SkeletonDataset pass def _set_derived_attrs(self): if self.domain_left_edge is None or self.domain_right_edge is None: self.domain_center = np.zeros(3) self.domain_width = np.zeros(3) else: self.domain_center = 0.5 * (self.domain_right_edge + self.domain_left_edge) self.domain_width = self.domain_right_edge - self.domain_left_edge if not isinstance(self.current_time, YTQuantity): self.current_time = self.quan(self.current_time, "code_time") for attr in ("center", "width", "left_edge", "right_edge"): n = f"domain_{attr}" v = getattr(self, n) if not isinstance(v, YTArray) and v is not None: # Note that we don't add on _ipython_display_ here because # everything is stored inside a MutableAttribute. v = self.arr(v, "code_length") setattr(self, n, v) def __reduce__(self): args = (self._hash(),) return (_reconstruct_ds, args) def __repr__(self): return f"{self.__class__.__name__}: {self.parameter_filename}" def __str__(self): return self.basename def _hash(self): s = f"{self.basename};{self.current_time};{self.unique_identifier}" return hashlib.md5(s.encode("utf-8")).hexdigest() @cached_property def checksum(self): """ Computes md5 sum of a dataset. Note: Currently this property is unable to determine a complete set of files that are a part of a given dataset. As a first approximation, the checksum of :py:attr:`~parameter_file` is calculated. In case :py:attr:`~parameter_file` is a directory, checksum of all files inside the directory is calculated. """ def generate_file_md5(m, filename, blocksize=2**20): with open(filename, "rb") as f: while True: buf = f.read(blocksize) if not buf: break m.update(buf) m = hashlib.md5() if os.path.isdir(self.parameter_filename): for root, _, files in os.walk(self.parameter_filename): for fname in files: fname = os.path.join(root, fname) generate_file_md5(m, fname) elif os.path.isfile(self.parameter_filename): generate_file_md5(m, self.parameter_filename) else: m = "notafile" if hasattr(m, "hexdigest"): m = m.hexdigest() return m @property def _mrep(self): return MinimalDataset(self) @property def _skip_cache(self): return False @classmethod def _guess_candidates(cls, base, directories, files): """ This is a class method that accepts a directory (base), a list of files in that directory, and a list of subdirectories. It should return a list of filenames (defined relative to the supplied directory) and a boolean as to whether or not further directories should be recursed. This function doesn't need to catch all possibilities, nor does it need to filter possibilities. """ return [], True def close(self): # noqa: B027 pass def __getitem__(self, key): """Returns units, parameters, or conversion_factors in that order.""" return self.parameters[key] def __iter__(self): yield from self.parameters def get_smallest_appropriate_unit( self, v, quantity="distance", return_quantity=False ): """ Returns the largest whole unit smaller than the YTQuantity passed to it as a string. The quantity keyword can be equal to `distance` or `time`. In the case of distance, the units are: 'Mpc', 'kpc', 'pc', 'au', 'rsun', 'km', etc. For time, the units are: 'Myr', 'kyr', 'yr', 'day', 'hr', 's', 'ms', etc. If return_quantity is set to True, it finds the largest YTQuantity object with a whole unit and a power of ten as the coefficient, and it returns this YTQuantity. """ good_u = None if quantity == "distance": unit_list = [ "Ppc", "Tpc", "Gpc", "Mpc", "kpc", "pc", "au", "rsun", "km", "m", "cm", "mm", "um", "nm", "pm", ] elif quantity == "time": unit_list = [ "Yyr", "Zyr", "Eyr", "Pyr", "Tyr", "Gyr", "Myr", "kyr", "yr", "day", "hr", "s", "ms", "us", "ns", "ps", "fs", ] else: raise ValueError( "Specified quantity must be equal to 'distance' or 'time'." ) for unit in unit_list: uq = self.quan(1.0, unit) if uq <= v: good_u = unit break if good_u is None and quantity == "distance": good_u = "cm" if good_u is None and quantity == "time": good_u = "s" if return_quantity: unit_index = unit_list.index(good_u) # This avoids indexing errors if unit_index == 0: return self.quan(1, unit_list[0]) # Number of orders of magnitude between unit and next one up OOMs = np.ceil( np.log10( self.quan(1, unit_list[unit_index - 1]) / self.quan(1, unit_list[unit_index]) ) ) # Backwards order of coefficients (e.g. [100, 10, 1]) coeffs = 10 ** np.arange(OOMs)[::-1] for j in coeffs: uq = self.quan(j, good_u) if uq <= v: return uq else: return good_u def has_key(self, key): """ Checks units, parameters, and conversion factors. Returns a boolean. """ return key in self.parameters _instantiated_index = None @property def index(self): if self._instantiated_index is None: self._instantiated_index = self._index_class( self, dataset_type=self.dataset_type ) # Now we do things that we need an instantiated index for # ...first off, we create our field_info now. oldsettings = np.geterr() np.seterr(all="ignore") self.create_field_info() np.seterr(**oldsettings) return self._instantiated_index @parallel_root_only def print_key_parameters(self): for a in [ "current_time", "domain_dimensions", "domain_left_edge", "domain_right_edge", "cosmological_simulation", ]: if not hasattr(self, a): mylog.error("Missing %s in parameter file definition!", a) continue v = getattr(self, a) mylog.info("Parameters: %-25s = %s", a, v) if hasattr(self, "cosmological_simulation") and self.cosmological_simulation: for a in [ "current_redshift", "omega_lambda", "omega_matter", "omega_radiation", "hubble_constant", ]: if not hasattr(self, a): mylog.error("Missing %s in parameter file definition!", a) continue v = getattr(self, a) mylog.info("Parameters: %-25s = %s", a, v) if getattr(self, "_domain_override", False): if any(self._periodicity): mylog.warning( "A bounding box was explicitly specified, so we " "are disabling periodicity." ) @parallel_root_only def print_stats(self): self.index.print_stats() @property def field_list(self): return self.index.field_list @property def field_info(self): if self._field_info is None: self.index return self._field_info @field_info.setter def field_info(self, value): self._field_info = value def create_field_info(self): # create_field_info will be called at the end of instantiating # the index object. This will trigger index creation, which will # call this function again. if self._instantiated_index is None: self.index return self.field_dependencies = {} self.derived_field_list = [] self.filtered_particle_types = [] self.field_info = self._field_info_class(self, self.field_list) self.coordinates.setup_fields(self.field_info) self.field_info.setup_fluid_fields() for ptype in self.particle_types: self.field_info.setup_particle_fields(ptype) self.field_info.setup_fluid_index_fields() if "all" not in self.particle_types: mylog.debug("Creating Particle Union 'all'") pu = ParticleUnion("all", list(self.particle_types_raw)) nfields = self.add_particle_union(pu) if nfields == 0: mylog.debug("zero common fields: skipping particle union 'all'") if "nbody" not in self.particle_types: mylog.debug("Creating Particle Union 'nbody'") ptypes = list(self.particle_types_raw) if hasattr(self, "_sph_ptypes"): for sph_ptype in self._sph_ptypes: if sph_ptype in ptypes: ptypes.remove(sph_ptype) if ptypes: nbody_ptypes = [] for ptype in ptypes: if (ptype, "particle_mass") in self.field_info: nbody_ptypes.append(ptype) pu = ParticleUnion("nbody", nbody_ptypes) nfields = self.add_particle_union(pu) if nfields == 0: mylog.debug("zero common fields, skipping particle union 'nbody'") self.field_info.setup_extra_union_fields() self.field_info.load_all_plugins(self.default_fluid_type) deps, unloaded = self.field_info.check_derived_fields() self.field_dependencies.update(deps) self.fields = FieldTypeContainer(self) self.index.field_list = sorted(self.field_list) # Now that we've detected the fields, set this flag so that # deprecated fields will be logged if they are used self.fields_detected = True def set_field_label_format(self, format_property, value): """ Set format properties for how fields will be written out. Accepts format_property : string indicating what property to set value: the value to set for that format_property """ available_formats = {"ionization_label": ("plus_minus", "roman_numeral")} if format_property in available_formats: if value in available_formats[format_property]: setattr(self, f"_{format_property}_format", value) else: raise ValueError( f"{value} not an acceptable value for format_property " f"{format_property}. Choices are {available_formats[format_property]}." ) else: raise ValueError( f"{format_property} not a recognized format_property. Available " f"properties are: {list(available_formats.keys())}" ) def setup_deprecated_fields(self): from yt.fields.field_aliases import _field_name_aliases added = [] for old_name, new_name in _field_name_aliases: try: fi = self._get_field_info(new_name) except YTFieldNotFound: continue self.field_info.alias(("gas", old_name), fi.name) added.append(("gas", old_name)) self.field_info.find_dependencies(added) def _setup_coordinate_handler(self, axis_order: AxisOrder | None) -> None: # backward compatibility layer: # turning off type-checker on a per-line basis cls: type[CoordinateHandler] if isinstance(self.geometry, tuple): # type: ignore [unreachable] issue_deprecation_warning( # type: ignore [unreachable] f"Dataset object {self} has a tuple for its geometry attribute. " "This is interpreted as meaning the first element is the actual geometry string, " "and the second represents an arbitrary axis order. " "This will stop working in a future version of yt.\n" "If you're loading data using yt.load_* functions, " "you should be able to clear this warning by using the axis_order keyword argument.\n" "Otherwise, if your code relies on this behaviour, please reach out and open an issue:\n" "https://github.com/yt-project/yt/issues/new\n" "Also see https://github.com/yt-project/yt/pull/4244#discussion_r1063486520 for reference", since="4.2", stacklevel=2, ) self.geometry, axis_order = self.geometry elif callable(self.geometry): issue_deprecation_warning( f"Dataset object {self} has a class for its geometry attribute. " "This was accepted in previous versions of yt but leads to undefined behaviour. " "This will stop working in a future version of yt.\n" "If you are relying on this behaviour, please reach out and open an issue:\n" "https://github.com/yt-project/yt/issues/new", since="4.2", stacklevel=2, ) cls = self.geometry # type: ignore [assignment] if type(self.geometry) is str: # noqa: E721 issue_deprecation_warning( f"Dataset object {self} has a raw string for its geometry attribute. " "In yt>=4.2, a yt.geometry.geometry_enum.Geometry member is expected instead. " "This will stop working in a future version of yt.\n", since="4.2", stacklevel=2, ) self.geometry = Geometry(self.geometry.lower()) if isinstance(self.geometry, CoordinateHandler): issue_deprecation_warning( f"Dataset object {self} has a CoordinateHandler object for its geometry attribute. " "In yt>=4.2, a yt.geometry.geometry_enum.Geometry member is expected instead. " "This will stop working in a future version of yt.\n", since="4.2", stacklevel=2, ) _class_name = type(self.geometry).__name__ if not _class_name.endswith("CoordinateHandler"): raise RuntimeError( "Expected CoordinateHandler child class name to end with CoordinateHandler" ) _geom_str = _class_name[: -len("CoordinateHandler")] self.geometry = Geometry(_geom_str.lower()) del _class_name, _geom_str # end compatibility layer if not isinstance(self.geometry, Geometry): raise TypeError( "Expected dataset.geometry attribute to be of " "type yt.geometry.geometry_enum.Geometry\n" f"Got {self.geometry=} with type {type(self.geometry)}" ) if self.geometry is Geometry.CARTESIAN: cls = CartesianCoordinateHandler elif self.geometry is Geometry.CYLINDRICAL: cls = CylindricalCoordinateHandler elif self.geometry is Geometry.POLAR: cls = PolarCoordinateHandler elif self.geometry is Geometry.SPHERICAL: cls = SphericalCoordinateHandler # It shouldn't be required to reset self.no_cgs_equiv_length # to the default value (False) here, but it's still necessary # see https://github.com/yt-project/yt/pull/3618 self.no_cgs_equiv_length = False elif self.geometry is Geometry.GEOGRAPHIC: cls = GeographicCoordinateHandler self.no_cgs_equiv_length = True elif self.geometry is Geometry.INTERNAL_GEOGRAPHIC: cls = InternalGeographicCoordinateHandler self.no_cgs_equiv_length = True elif self.geometry is Geometry.SPECTRAL_CUBE: cls = SpectralCubeCoordinateHandler else: assert_never(self.geometry) self.coordinates = cls(self, ordering=axis_order) def add_particle_union(self, union): # No string lookups here, we need an actual union. f = self.particle_fields_by_type # find fields common to all particle types in the union fields = set_intersection([f[s] for s in union if s in self.particle_types_raw]) if len(fields) == 0: # don't create this union if no fields are common to all # particle types return len(fields) for field in fields: units = set() for s in union: # First we check our existing fields for units funits = self._get_field_info((s, field)).units # Then we override with field_units settings. funits = self.field_units.get((s, field), funits) units.add(funits) if len(units) == 1: self.field_units[union.name, field] = list(units)[0] self.particle_types += (union.name,) self.particle_unions[union.name] = union fields = [(union.name, field) for field in fields] new_fields = [_ for _ in fields if _ not in self.field_list] self.field_list.extend(new_fields) new_field_info_fields = [ _ for _ in fields if _ not in self.field_info.field_list ] self.field_info.field_list.extend(new_field_info_fields) self.index.field_list = sorted(self.field_list) # Give ourselves a chance to add them here, first, then... # ...if we can't find them, we set them up as defaults. new_fields = self._setup_particle_types([union.name]) self.field_info.find_dependencies(new_fields) return len(new_fields) def add_particle_filter(self, filter): """Add particle filter to the dataset. Add ``filter`` to the dataset and set up relevant derived_field. It will also add any ``filtered_type`` that the ``filter`` depends on. """ # This requires an index self.index # This is a dummy, which we set up to enable passthrough of "all" # concatenation fields. n = getattr(filter, "name", filter) self.known_filters[n] = None if isinstance(filter, str): used = False f = filter_registry.get(filter, None) if f is None: return False used = self._setup_filtered_type(f) if used: filter = f else: used = self._setup_filtered_type(filter) if not used: self.known_filters.pop(n, None) return False self.known_filters[filter.name] = filter return True def _setup_filtered_type(self, filter): # Check if the filtered_type of this filter is known, # otherwise add it first if it is in the filter_registry if filter.filtered_type not in self.known_filters.keys(): if filter.filtered_type in filter_registry: add_success = self.add_particle_filter(filter.filtered_type) if add_success: mylog.info( "Added filter dependency '%s' for '%s'", filter.filtered_type, filter.name, ) if not filter.available(self.derived_field_list): raise YTIllDefinedParticleFilter( filter, filter.missing(self.derived_field_list) ) fi = self.field_info fd = self.field_dependencies available = False for fn in self.derived_field_list: if fn[0] == filter.filtered_type: # Now we can add this available = True self.derived_field_list.append((filter.name, fn[1])) fi[filter.name, fn[1]] = filter.wrap_func(fn, fi[fn]) # Now we append the dependencies fd[filter.name, fn[1]] = fd[fn] if available: if filter.name not in self.particle_types: self.particle_types += (filter.name,) if filter.name not in self.filtered_particle_types: self.filtered_particle_types.append(filter.name) if hasattr(self, "_sph_ptypes"): if filter.filtered_type == self._sph_ptypes[0]: mylog.warning( "It appears that you are filtering on an SPH field " "type. It is recommended to use 'gas' as the " "filtered particle type in this case instead." ) if filter.filtered_type in (self._sph_ptypes + ("gas",)): self._sph_ptypes = self._sph_ptypes + (filter.name,) new_fields = self._setup_particle_types([filter.name]) deps, _ = self.field_info.check_derived_fields(new_fields) self.field_dependencies.update(deps) return available def _setup_particle_types(self, ptypes=None): df = [] if ptypes is None: ptypes = self.ds.particle_types_raw for ptype in set(ptypes): df += self._setup_particle_type(ptype) return df def _get_field_info( self, field: FieldKey | ImplicitFieldKey | DerivedField, /, ) -> DerivedField: field_info, candidates = self._get_field_info_helper(field) if field_info.name[1] in ("px", "py", "pz", "pdx", "pdy", "pdz"): # escape early as a bandaid solution to # https://github.com/yt-project/yt/issues/3381 return field_info def _are_ambiguous(candidates: list[FieldKey]) -> bool: if len(candidates) < 2: return False ftypes, fnames = (list(_) for _ in unzip(candidates)) assert all(name == fnames[0] for name in fnames) fi = self.field_info all_aliases: bool = all( fi[c].is_alias_to(fi[candidates[0]]) for c in candidates ) all_equivalent_particle_fields: bool if ( not self.particle_types or not self.particle_unions or not self.particle_types_raw ): all_equivalent_particle_fields = False elif all(ft in self.particle_types for ft in ftypes): ptypes = ftypes sub_types_list: list[set[str]] = [] for pt in ptypes: if pt in self.particle_types_raw: sub_types_list.append({pt}) elif pt in self.particle_unions: sub_types_list.append(set(self.particle_unions[pt].sub_types)) all_equivalent_particle_fields = all( st == sub_types_list[0] for st in sub_types_list ) else: all_equivalent_particle_fields = False return not (all_aliases or all_equivalent_particle_fields) if _are_ambiguous(candidates): ft, fn = field_info.name possible_ftypes = [c[0] for c in candidates] raise ValueError( f"The requested field name {fn!r} " "is ambiguous and corresponds to any one of " f"the following field types:\n {possible_ftypes}\n" "Please specify the requested field as an explicit " "tuple (, ).\n" ) return field_info def _get_field_info_helper( self, field: FieldKey | ImplicitFieldKey | DerivedField, /, ) -> tuple[DerivedField, list[FieldKey]]: self.index ftype: str fname: str if isinstance(field, str): ftype, fname = "unknown", field elif isinstance(field, tuple) and len(field) == 2: ftype, fname = field elif isinstance(field, DerivedField): ftype, fname = field.name else: raise YTFieldNotParseable(field) if ftype == "unknown": candidates: list[FieldKey] = [ (ft, fn) for ft, fn in self.field_info if fn == fname ] # We also should check "all" for particles, which can show up if you're # mixing deposition/gas fields with particle fields. if hasattr(self, "_sph_ptype"): to_guess = [self.default_fluid_type, "all"] else: to_guess = ["all", self.default_fluid_type] to_guess += list(self.fluid_types) + list(self.particle_types) for ftype in to_guess: if (ftype, fname) in self.field_info: return self.field_info[ftype, fname], candidates elif (ftype, fname) in self.field_info: return self.field_info[ftype, fname], [] raise YTFieldNotFound(field, ds=self) def _setup_classes(self): # Called by subclass self.object_types = [] self.objects = [] self.plots = [] for name, cls in sorted(data_object_registry.items()): if name in self._index_class._unsupported_objects: setattr(self, name, _unsupported_object(self, name)) continue self._add_object_class(name, cls) self.object_types.sort() def _add_object_class(self, name, base): # skip projection data objects that don't make sense # for this type of data if "proj" in name and name != self._proj_type: return elif "proj" in name: name = "proj" self.object_types.append(name) obj = functools.partial(base, ds=weakref.proxy(self)) obj.__doc__ = base.__doc__ setattr(self, name, obj) def _find_extremum(self, field, ext, source=None, to_array=True): """ Find the extremum value of a field in a data object (source) and its position. Parameters ---------- field : str or tuple(str, str) ext : str 'min' or 'max', select an extremum source : a Yt data object to_array : bool select the return type. Returns ------- val, coords val: unyt.unyt_quantity extremum value detected coords: unyt.unyt_array or list(unyt.unyt_quantity) Conversion to a single unyt_array object is only possible for coordinate systems with homogeneous dimensions across axes (i.e. cartesian). """ ext = ext.lower() if source is None: source = self.all_data() method = { "min": source.quantities.min_location, "max": source.quantities.max_location, }[ext] val, x1, x2, x3 = method(field) coords = [x1, x2, x3] mylog.info("%s value is %0.5e at %0.16f %0.16f %0.16f", ext, val, *coords) if to_array: if any(x.units.is_dimensionless for x in coords): mylog.warning( "dataset `%s` has angular coordinates. " "Use 'to_array=False' to preserve " "dimensionality in each coordinate.", str(self), ) # force conversion to length alt_coords = [] for x in coords: alt_coords.append( self.quan(x.v, "code_length") if x.units.is_dimensionless else x.to("code_length") ) coords = self.arr(alt_coords, dtype="float64").to("code_length") return val, coords def find_max(self, field, source=None, to_array=True): """ Returns (value, location) of the maximum of a given field. This is a wrapper around _find_extremum """ mylog.debug("Searching for maximum value of %s", field) return self._find_extremum(field, "max", source=source, to_array=to_array) def find_min(self, field, source=None, to_array=True): """ Returns (value, location) for the minimum of a given field. This is a wrapper around _find_extremum """ mylog.debug("Searching for minimum value of %s", field) return self._find_extremum(field, "min", source=source, to_array=to_array) def find_field_values_at_point(self, fields, coords): """ Returns the values [field1, field2,...] of the fields at the given coordinates. Returns a list of field values in the same order as the input *fields*. """ point = self.point(coords) ret = [point[f] for f in iter_fields(fields)] if len(ret) == 1: return ret[0] else: return ret def find_field_values_at_points(self, fields, coords): """ Returns the values [field1, field2,...] of the fields at the given [(x1, y1, z2), (x2, y2, z2),...] points. Returns a list of field values in the same order as the input *fields*. """ # If an optimized version exists on the Index object we'll use that try: return self.index._find_field_values_at_points(fields, coords) except AttributeError: pass fields = list(iter_fields(fields)) out = [] # This may be slow because it creates a data object for each point for field_index, field in enumerate(fields): funit = self._get_field_info(field).units out.append(self.arr(np.empty((len(coords),)), funit)) for coord_index, coord in enumerate(coords): out[field_index][coord_index] = self.point(coord)[field] if len(fields) == 1: return out[0] else: return out # Now all the object related stuff def all_data(self, find_max=False, **kwargs): """ all_data is a wrapper to the Region object for creating a region which covers the entire simulation domain. """ self.index if find_max: c = self.find_max("density")[1] else: c = (self.domain_right_edge + self.domain_left_edge) / 2.0 return self.region(c, self.domain_left_edge, self.domain_right_edge, **kwargs) def box(self, left_edge, right_edge, **kwargs): """ box is a wrapper to the Region object for creating a region without having to specify a *center* value. It assumes the center is the midpoint between the left_edge and right_edge. Keyword arguments are passed to the initializer of the YTRegion object (e.g. ds.region). """ # we handle units in the region data object # but need to check if left_edge or right_edge is a # list or other non-array iterable before calculating # the center if isinstance(left_edge[0], YTQuantity): left_edge = YTArray(left_edge) right_edge = YTArray(right_edge) left_edge = np.asanyarray(left_edge, dtype="float64") right_edge = np.asanyarray(right_edge, dtype="float64") c = (left_edge + right_edge) / 2.0 return self.region(c, left_edge, right_edge, **kwargs) def _setup_particle_type(self, ptype): orig = set(self.field_info.items()) self.field_info.setup_particle_fields(ptype) return [n for n, v in set(self.field_info.items()).difference(orig)] @property def particle_fields_by_type(self): fields = defaultdict(list) for field in self.field_list: if field[0] in self.particle_types_raw: fields[field[0]].append(field[1]) return fields @property def particles_exist(self): for pt, f in itertools.product(self.particle_types_raw, self.field_list): if pt == f[0]: return True return False @property def particle_type_counts(self): self.index if not self.particles_exist: return {} # frontends or index implementation can populate this dict while # creating the index if they know particle counts at that time if self._particle_type_counts is not None: return self._particle_type_counts self._particle_type_counts = self.index._get_particle_type_counts() return self._particle_type_counts @property def ires_factor(self): o2 = np.log2(self.refine_by) if o2 != int(o2): raise RuntimeError # In the case that refine_by is 1 or 0 or something, we just # want to make it a non-operative number, so we set to 1. return max(1, int(o2)) def relative_refinement(self, l0, l1): return self.refine_by ** (l1 - l0) def _assign_unit_system( self, # valid unit_system values include all keys from unyt.unit_systems.unit_systems_registry + "code" unit_system: Literal[ "cgs", "mks", "imperial", "galactic", "solar", "geometrized", "planck", "code", ], ) -> None: # we need to determine if the requested unit system # is mks-like: i.e., it has a current with the same # dimensions as amperes. mks_system = False mag_unit: unyt_quantity | None = getattr(self, "magnetic_unit", None) mag_dims: set[Symbol] | None if mag_unit is not None: mag_dims = mag_unit.units.dimensions.free_symbols else: mag_dims = None if unit_system != "code": # if the unit system is known, we can check if it # has a "current_mks" unit us = unit_system_registry[str(unit_system).lower()] mks_system = us.base_units[current_mks] is not None elif mag_dims and current_mks in mag_dims: # if we're using the not-yet defined code unit system, # then we check if the magnetic field unit has a SI # current dimension in it mks_system = True # Now we get to the tricky part. If we have an MKS-like system but # we asked for a conversion to something CGS-like, or vice-versa, # we have to convert the magnetic field if mag_dims is not None: self.magnetic_unit: unyt_quantity if mks_system and current_mks not in mag_dims: self.magnetic_unit = self.quan( self.magnetic_unit.to_value("gauss") * 1.0e-4, "T" ) # The following modification ensures that we get the conversion to # mks correct self.unit_registry.modify( "code_magnetic", self.magnetic_unit.value * 1.0e3 * 0.1**-0.5 ) elif not mks_system and current_mks in mag_dims: self.magnetic_unit = self.quan( self.magnetic_unit.to_value("T") * 1.0e4, "gauss" ) # The following modification ensures that we get the conversion to # cgs correct self.unit_registry.modify( "code_magnetic", self.magnetic_unit.value * 1.0e-4 ) current_mks_unit = "A" if mks_system else None us = create_code_unit_system( self.unit_registry, current_mks_unit=current_mks_unit ) if unit_system != "code": us = unit_system_registry[str(unit_system).lower()] self._unit_system_name: str = unit_system self.unit_system: UnitSystem = us self.unit_registry.unit_system = self.unit_system @property def _uses_code_length_unit(self) -> bool: return self._unit_system_name == "code" or self.no_cgs_equiv_length @property def _uses_code_time_unit(self) -> bool: return self._unit_system_name == "code" def _create_unit_registry(self, unit_system): from yt.units import dimensions # yt assumes a CGS unit system by default (for back compat reasons). # Since unyt is MKS by default we specify the MKS values of the base # units in the CGS system. So, for length, 1 cm = .01 m. And so on. # Note that the values associated with the code units here will be # modified once we actually determine what the code units are from # the dataset # NOTE that magnetic fields are not done here yet, see set_code_units self.unit_registry = UnitRegistry(unit_system=unit_system) # 1 cm = 0.01 m self.unit_registry.add("code_length", 0.01, dimensions.length) # 1 g = 0.001 kg self.unit_registry.add("code_mass", 0.001, dimensions.mass) # 1 g/cm**3 = 1000 kg/m**3 self.unit_registry.add("code_density", 1000.0, dimensions.density) # 1 erg/g = 1.0e-4 J/kg self.unit_registry.add( "code_specific_energy", 1.0e-4, dimensions.energy / dimensions.mass ) # 1 s = 1 s self.unit_registry.add("code_time", 1.0, dimensions.time) # 1 K = 1 K self.unit_registry.add("code_temperature", 1.0, dimensions.temperature) # 1 dyn/cm**2 = 0.1 N/m**2 self.unit_registry.add("code_pressure", 0.1, dimensions.pressure) # 1 cm/s = 0.01 m/s self.unit_registry.add("code_velocity", 0.01, dimensions.velocity) # metallicity self.unit_registry.add("code_metallicity", 1.0, dimensions.dimensionless) # dimensionless hubble parameter self.unit_registry.add("h", 1.0, dimensions.dimensionless, r"h") # cosmological scale factor self.unit_registry.add("a", 1.0, dimensions.dimensionless) def set_units(self): """ Creates the unit registry for this dataset. """ if getattr(self, "cosmological_simulation", False): # this dataset is cosmological, so add cosmological units. self.unit_registry.modify("h", self.hubble_constant) if getattr(self, "current_redshift", None) is not None: # Comoving lengths for my_unit in ["m", "pc", "AU", "au"]: new_unit = f"{my_unit}cm" my_u = Unit(my_unit, registry=self.unit_registry) self.unit_registry.add( new_unit, my_u.base_value / (1 + self.current_redshift), dimensions.length, f"\\rm{{{my_unit}}}/(1+z)", prefixable=True, ) self.unit_registry.modify("a", 1 / (1 + self.current_redshift)) self.set_code_units() def setup_cosmology(self): """ If this dataset is cosmological, add a cosmology object. """ if not getattr(self, "cosmological_simulation", False): return # Set dynamical dark energy parameters use_dark_factor = getattr(self, "use_dark_factor", False) w_0 = getattr(self, "w_0", -1.0) w_a = getattr(self, "w_a", 0.0) # many frontends do not set this setdefaultattr(self, "omega_radiation", 0.0) self.cosmology = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, omega_radiation=self.omega_radiation, use_dark_factor=use_dark_factor, w_0=w_0, w_a=w_a, ) if not hasattr(self, "current_time"): self.current_time = self.cosmology.t_from_z(self.current_redshift) if getattr(self, "current_redshift", None) is not None: self.critical_density = self.cosmology.critical_density( self.current_redshift ) self.scale_factor = 1.0 / (1.0 + self.current_redshift) def get_unit_from_registry(self, unit_str): """ Creates a unit object matching the string expression, using this dataset's unit registry. Parameters ---------- unit_str : str string that we can parse for a sympy Expr. """ new_unit = Unit(unit_str, registry=self.unit_registry) return new_unit def set_code_units(self): # here we override units, if overrides have been provided. self._override_code_units() # set attributes like ds.length_unit self._set_code_unit_attributes() self.unit_registry.modify("code_length", self.length_unit) self.unit_registry.modify("code_mass", self.mass_unit) self.unit_registry.modify("code_time", self.time_unit) vel_unit = getattr(self, "velocity_unit", self.length_unit / self.time_unit) pressure_unit = getattr( self, "pressure_unit", self.mass_unit / (self.length_unit * (self.time_unit) ** 2), ) temperature_unit = getattr(self, "temperature_unit", 1.0) density_unit = getattr( self, "density_unit", self.mass_unit / self.length_unit**3 ) specific_energy_unit = getattr(self, "specific_energy_unit", vel_unit**2) self.unit_registry.modify("code_velocity", vel_unit) self.unit_registry.modify("code_temperature", temperature_unit) self.unit_registry.modify("code_pressure", pressure_unit) self.unit_registry.modify("code_density", density_unit) self.unit_registry.modify("code_specific_energy", specific_energy_unit) # Defining code units for magnetic fields are tricky because # they have different dimensions in different unit systems, so we have # to handle them carefully if hasattr(self, "magnetic_unit"): if self.magnetic_unit.units.dimensions == dimensions.magnetic_field_cgs: # We have to cast this explicitly to MKS base units, otherwise # unyt will convert it automatically to Tesla value = self.magnetic_unit.to_value("sqrt(kg)/(sqrt(m)*s)") dims = dimensions.magnetic_field_cgs else: value = self.magnetic_unit.to_value("T") dims = dimensions.magnetic_field_mks else: # Fallback to gauss if no magnetic unit is specified # 1 gauss = 1 sqrt(g)/(sqrt(cm)*s) = 0.1**0.5 sqrt(kg)/(sqrt(m)*s) value = 0.1**0.5 dims = dimensions.magnetic_field_cgs self.unit_registry.add("code_magnetic", value, dims) # domain_width does not yet exist if self.domain_left_edge is not None and self.domain_right_edge is not None: DW = self.arr(self.domain_right_edge - self.domain_left_edge, "code_length") self.unit_registry.add( "unitary", float(DW.max() * DW.units.base_value), DW.units.dimensions ) @classmethod def _validate_units_override_keys(cls, units_override): valid_keys = set(cls.default_units.keys()) invalid_keys_found = set(units_override.keys()) - valid_keys if invalid_keys_found: raise ValueError( f"units_override contains invalid keys: {invalid_keys_found}" ) default_units = { "length_unit": "cm", "time_unit": "s", "mass_unit": "g", "velocity_unit": "cm/s", "magnetic_unit": "gauss", "temperature_unit": "K", } @classmethod def _sanitize_units_override(cls, units_override): """ Convert units_override values to valid input types for unyt. Throw meaningful errors early if units_override is ill-formed. Parameters ---------- units_override : dict keys should be strings with format "_unit" (e.g. "mass_unit"), and need to match a key in cls.default_units values should be mappable to unyt.unyt_quantity objects, and can be any combinations of: - unyt.unyt_quantity - 2-long sequence (tuples, list, ...) with types (number, str) e.g. (10, "km"), (0.1, "s") - number (in which case the associated is taken from cls.default_unit) Raises ------ TypeError If unit_override has invalid types ValueError If provided units do not match the intended dimensionality, or in case of a zero scaling factor. """ uo = {} if units_override is None: return uo cls._validate_units_override_keys(units_override) for key in cls.default_units: try: val = units_override[key] except KeyError: continue # Now attempt to instantiate a unyt.unyt_quantity from val ... try: # ... directly (valid if val is a number, or a unyt_quantity) uo[key] = YTQuantity(val) continue except RuntimeError: # note that unyt.unyt_quantity throws RuntimeError in lieu of TypeError pass try: # ... with tuple unpacking (valid if val is a sequence) uo[key] = YTQuantity(*val) continue except (RuntimeError, TypeError, UnitParseError): pass raise TypeError( "units_override values should be 2-sequence (float, str), " "YTQuantity objects or real numbers; " f"received {val} with type {type(val)}." ) for key, q in uo.items(): if q.units.is_dimensionless: uo[key] = YTQuantity(q, cls.default_units[key]) try: uo[key].to(cls.default_units[key]) except UnitConversionError as err: raise ValueError( "Inconsistent dimensionality in units_override. " f"Received {key} = {uo[key]}" ) from err if uo[key].value == 0.0: raise ValueError( f"Invalid 0 normalisation factor in units_override for {key}." ) return uo def _override_code_units(self): if not self.units_override: return mylog.warning( "Overriding code units: Use this option only if you know that the " "dataset doesn't define the units correctly or at all." ) for ukey, val in self.units_override.items(): mylog.info("Overriding %s: %s.", ukey, val) setattr(self, ukey, self.quan(val)) _units = None _unit_system_id = None @property def units(self): current_uid = self.unit_registry.unit_system_id if self._units is not None and self._unit_system_id == current_uid: return self._units self._unit_system_id = current_uid self._units = UnitContainer(self.unit_registry) return self._units _arr = None @property def arr(self): """Converts an array into a :class:`yt.units.yt_array.YTArray` The returned YTArray will be dimensionless by default, but can be cast to arbitrary units using the ``units`` keyword argument. Parameters ---------- input_array : Iterable A tuple, list, or array to attach units to units: String unit specification, unit symbol or astropy object The units of the array. Powers must be specified using python syntax (cm**3, not cm^3). input_units : Deprecated in favor of 'units' dtype : string or NumPy dtype object The dtype of the returned array data Examples -------- >>> import yt >>> import numpy as np >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> a = ds.arr([1, 2, 3], "cm") >>> b = ds.arr([4, 5, 6], "m") >>> a + b YTArray([ 401., 502., 603.]) cm >>> b + a YTArray([ 4.01, 5.02, 6.03]) m Arrays returned by this function know about the dataset's unit system >>> a = ds.arr(np.ones(5), "code_length") >>> a.in_units("Mpccm/h") YTArray([ 1.00010449, 1.00010449, 1.00010449, 1.00010449, 1.00010449]) Mpc """ if self._arr is not None: return self._arr self._arr = functools.partial(YTArray, registry=self.unit_registry) return self._arr _quan = None @property def quan(self): """Converts an scalar into a :class:`yt.units.yt_array.YTQuantity` The returned YTQuantity will be dimensionless by default, but can be cast to arbitrary units using the ``units`` keyword argument. Parameters ---------- input_scalar : an integer or floating point scalar The scalar to attach units to units: String unit specification, unit symbol or astropy object The units of the quantity. Powers must be specified using python syntax (cm**3, not cm^3). input_units : Deprecated in favor of 'units' dtype : string or NumPy dtype object The dtype of the array data. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> a = ds.quan(1, "cm") >>> b = ds.quan(2, "m") >>> a + b 201.0 cm >>> b + a 2.01 m Quantities created this way automatically know about the unit system of the dataset. >>> a = ds.quan(5, "code_length") >>> a.in_cgs() 1.543e+25 cm """ if self._quan is not None: return self._quan self._quan = functools.partial(YTQuantity, registry=self.unit_registry) return self._quan def add_field( self, name, function, sampling_type, *, force_override=False, **kwargs ): """ Dataset-specific call to add_field Add a new field, along with supplemental metadata, to the list of available fields. This respects a number of arguments, all of which are passed on to the constructor for :class:`~yt.data_objects.api.DerivedField`. Parameters ---------- name : str is the name of the field. function : callable A function handle that defines the field. Should accept arguments (field, data) sampling_type: str "cell" or "particle" or "local" force_override: bool If False (default), an error will be raised if a field of the same name already exists. units : str A plain text string encoding the unit. Powers must be in python syntax (** instead of ^). take_log : bool Describes whether the field should be logged validators : list A list of :class:`FieldValidator` objects vector_field : bool Describes the dimensionality of the field. Currently unused. display_name : str A name used in the plots force_override : bool Whether to override an existing derived field. Does not work with on-disk fields. """ from yt.fields.field_functions import validate_field_function validate_field_function(function) self.index if force_override and name in self.index.field_list: raise RuntimeError( "force_override is only meant to be used with " "derived fields, not on-disk fields." ) if not force_override and name in self.field_info: mylog.warning( "Field %s already exists. To override use `force_override=True`.", name, ) self.field_info.add_field( name, function, sampling_type, force_override=force_override, **kwargs ) self.field_info._show_field_errors.append(name) deps, _ = self.field_info.check_derived_fields([name]) self.field_dependencies.update(deps) def add_mesh_sampling_particle_field(self, sample_field, ptype="all"): """Add a new mesh sampling particle field Creates a new particle field which has the value of the *deposit_field* at the location of each particle of type *ptype*. Parameters ---------- sample_field : tuple The field name tuple of the mesh field to be deposited onto the particles. This must be a field name tuple so yt can appropriately infer the correct particle type. ptype : string, default 'all' The particle type onto which the deposition will occur. Returns ------- The field name tuple for the newly created field. Examples -------- >>> ds = yt.load("output_00080/info_00080.txt") ... ds.add_mesh_sampling_particle_field(("gas", "density"), ptype="all") >>> print("The density at the location of the particle is:") ... print(ds.r["all", "cell_gas_density"]) The density at the location of the particle is: [9.33886124e-30 1.22174333e-28 1.20402333e-28 ... 2.77410331e-30 8.79467609e-31 3.50665136e-30] g/cm**3 >>> len(ds.r["all", "cell_gas_density"]) == len(ds.r["all", "particle_ones"]) True """ if isinstance(sample_field, tuple): ftype, sample_field = sample_field[0], sample_field[1] else: raise RuntimeError return self.index._add_mesh_sampling_particle_field(sample_field, ftype, ptype) def add_deposited_particle_field( self, deposit_field, method, kernel_name="cubic", weight_field=None ): """Add a new deposited particle field Creates a new deposited field based on the particle *deposit_field*. Parameters ---------- deposit_field : tuple The field name tuple of the particle field the deposited field will be created from. This must be a field name tuple so yt can appropriately infer the correct particle type. method : string This is the "method name" which will be looked up in the `particle_deposit` namespace as `methodname_deposit`. Current methods include `simple_smooth`, `sum`, `std`, `cic`, `weighted_mean`, `nearest` and `count`. kernel_name : string, default 'cubic' This is the name of the smoothing kernel to use. It is only used for the `simple_smooth` method and is otherwise ignored. Current supported kernel names include `cubic`, `quartic`, `quintic`, `wendland2`, `wendland4`, and `wendland6`. weight_field : (field_type, field_name) or None Weighting field name for deposition method `weighted_mean`. If None, use the particle mass. Returns ------- The field name tuple for the newly created field. """ self.index if isinstance(deposit_field, tuple): ptype, deposit_field = deposit_field[0], deposit_field[1] else: raise RuntimeError if weight_field is None: weight_field = (ptype, "particle_mass") units = self.field_info[ptype, deposit_field].output_units take_log = self.field_info[ptype, deposit_field].take_log name_map = { "sum": "sum", "std": "std", "cic": "cic", "weighted_mean": "avg", "nearest": "nn", "simple_smooth": "ss", "count": "count", } field_name = "%s_" + name_map[method] + "_%s" field_name = field_name % (ptype, deposit_field.replace("particle_", "")) if method == "count": field_name = f"{ptype}_count" if ("deposit", field_name) in self.field_info: mylog.warning("The deposited field %s already exists", field_name) return ("deposit", field_name) else: units = "dimensionless" take_log = False def _deposit_field(field, data): """ Create a grid field for particle quantities using given method. """ pos = data[ptype, "particle_position"] fields = [data[ptype, deposit_field]] if method == "weighted_mean": fields.append(data[ptype, weight_field]) fields = [np.ascontiguousarray(f) for f in fields] d = data.deposit(pos, fields, method=method, kernel_name=kernel_name) d = data.ds.arr(d, units=units) if method == "weighted_mean": d[np.isnan(d)] = 0.0 return d self.add_field( ("deposit", field_name), function=_deposit_field, sampling_type="cell", units=units, take_log=take_log, validators=[ValidateSpatial()], ) return ("deposit", field_name) def add_gradient_fields(self, fields=None): """Add gradient fields. Creates four new grid-based fields that represent the components of the gradient of an existing field, plus an extra field for the magnitude of the gradient. The gradient is computed using second-order centered differences. Parameters ---------- fields : str or tuple(str, str), or a list of the previous Label(s) for at least one field. Can either represent a tuple (, ) or simply the field name. Warning: several field types may match the provided field name, in which case the first one discovered internally is used. Returns ------- A list of field name tuples for the newly created fields. Raises ------ YTFieldNotParsable If fields are not parsable to yt field keys. YTFieldNotFound : If at least one field can not be identified. Examples -------- >>> grad_fields = ds.add_gradient_fields(("gas", "density")) >>> print(grad_fields) ... [ ... ("gas", "density_gradient_x"), ... ("gas", "density_gradient_y"), ... ("gas", "density_gradient_z"), ... ("gas", "density_gradient_magnitude"), ... ] Note that the above example assumes ds.geometry == 'cartesian'. In general, the function will create gradient components along the axes of the dataset coordinate system. For instance, with cylindrical data, one gets 'density_gradient_' """ if fields is None: raise TypeError("Missing required positional argument: fields") self.index data_obj = self.all_data() explicit_fields = data_obj._determine_fields(fields) grad_fields = [] for ftype, fname in explicit_fields: units = self.field_info[ftype, fname].units setup_gradient_fields(self.field_info, (ftype, fname), units) # Now we make a list of the fields that were just made, to check them # and to return them grad_fields += [ (ftype, f"{fname}_gradient_{suffix}") for suffix in self.coordinates.axis_order ] grad_fields.append((ftype, f"{fname}_gradient_magnitude")) deps, _ = self.field_info.check_derived_fields(grad_fields) self.field_dependencies.update(deps) return grad_fields _max_level = None @property def max_level(self): if self._max_level is None: self._max_level = self.index.max_level return self._max_level @max_level.setter def max_level(self, value): self._max_level = value _min_level = None @property def min_level(self): if self._min_level is None: self._min_level = self.index.min_level return self._min_level @min_level.setter def min_level(self, value): self._min_level = value def define_unit(self, symbol, value, tex_repr=None, offset=None, prefixable=False): """ Define a new unit and add it to the dataset's unit registry. Parameters ---------- symbol : string The symbol for the new unit. value : tuple or ~yt.units.yt_array.YTQuantity The definition of the new unit in terms of some other units. For example, one would define a new "mph" unit with (1.0, "mile/hr") tex_repr : string, optional The LaTeX representation of the new unit. If one is not supplied, it will be generated automatically based on the symbol string. offset : float, optional The default offset for the unit. If not set, an offset of 0 is assumed. prefixable : bool, optional Whether or not the new unit can use SI prefixes. Default: False Examples -------- >>> ds.define_unit("mph", (1.0, "mile/hr")) >>> two_weeks = YTQuantity(14.0, "days") >>> ds.define_unit("fortnight", two_weeks) """ define_unit( symbol, value, tex_repr=tex_repr, offset=offset, prefixable=prefixable, registry=self.unit_registry, ) def _is_within_domain(self, point) -> bool: assert len(point) == len(self.domain_left_edge) assert point.units.dimensions == un.dimensions.length for i, x in enumerate(point): if self.periodicity[i]: continue if x < self.domain_left_edge[i]: return False if x > self.domain_right_edge[i]: return False return True def _reconstruct_ds(*args, **kwargs): datasets = ParameterFileStore() ds = datasets.get_ds_hash(*args) return ds @functools.total_ordering class ParticleFile: filename: str file_id: int start: int | None = None end: int | None = None total_particles: defaultdict[str, int] | None = None def __init__(self, ds, io, filename, file_id, range=None): self.ds = ds self.io = weakref.proxy(io) self.filename = filename self.file_id = file_id if range is None: range = (None, None) self.start, self.end = range self.total_particles = self.io._count_particles(self) # Now we adjust our start/end, in case there are fewer particles than # we realized if self.start is None: self.start = 0 self.end = max(self.total_particles.values()) + self.start def select(self, selector): # noqa: B027 pass def count(self, selector): # noqa: B027 pass def _calculate_offsets(self, fields, pcounts): # noqa: B027 pass def __lt__(self, other): if self.filename != other.filename: return self.filename < other.filename return self.start < other.start def __eq__(self, other): if self.filename != other.filename: return False return self.start == other.start def __hash__(self): return hash((self.filename, self.file_id, self.start, self.end)) class ParticleDataset(Dataset): _unit_base = None filter_bbox = False _proj_type = "particle_proj" def __init__( self, filename, dataset_type=None, units_override=None, unit_system="cgs", index_order=None, index_filename=None, default_species_fields=None, ): self.index_order = index_order self.index_filename = index_filename super().__init__( filename, dataset_type=dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) yt-project-yt-f043ac8/yt/data_objects/tests/000077500000000000000000000000001510711153200210725ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/tests/__init__.py000066400000000000000000000000001510711153200231710ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/data_objects/tests/test_add_field.py000066400000000000000000000072621510711153200244050ustar00rootroot00000000000000from functools import partial import pytest import unyt import yt from yt import derived_field from yt.fields import local_fields from yt.testing import fake_random_ds def test_add_field_lambda(): ds = fake_random_ds(16) ds.add_field( ("gas", "spam"), lambda field, data: data["gas", "density"], sampling_type="cell", ) # check access ds.all_data()["gas", "spam"] def test_add_field_partial(): ds = fake_random_ds(16) def _spam(field, data, factor): return factor * data["gas", "density"] ds.add_field( ("gas", "spam"), partial(_spam, factor=1), sampling_type="cell", ) # check access ds.all_data()["gas", "spam"] def test_add_field_arbitrary_callable(): ds = fake_random_ds(16) class Spam: def __call__(self, field, data): return data["gas", "density"] ds.add_field(("gas", "spam"), Spam(), sampling_type="cell") # check access ds.all_data()["gas", "spam"] def test_add_field_uncallable(): ds = fake_random_ds(16) class Spam: pass with pytest.raises(TypeError, match=r"(is not a callable object)$"): ds.add_field(("bacon", "spam"), Spam(), sampling_type="cell") def test_add_field_wrong_signature(): ds = fake_random_ds(16) def _spam(data, field): return data["gas", "density"] with pytest.raises( TypeError, match=( r"Received field function with invalid signature\. " r"Expected exactly 2 positional parameters \('field', 'data'\), got \('data', 'field'\)" ), ): ds.add_field(("bacon", "spam"), _spam, sampling_type="cell") def test_add_field_keyword_only(): ds = fake_random_ds(16) def _spam(field, *, data): return data["gas", "density"] with pytest.raises( TypeError, match=( r"Received field function .* with invalid signature\. " r"Parameters 'field' and 'data' must accept positional values \(they cannot be keyword-only\)" ), ): ds.add_field( ("bacon", "spam"), _spam, sampling_type="cell", ) def test_derived_field(monkeypatch): tmp_field_info = local_fields.LocalFieldInfoContainer(None, [], None) monkeypatch.setattr(local_fields, "local_fields", tmp_field_info) @derived_field(name="pressure", sampling_type="cell", units="dyne/cm**2") def _pressure(field, data): return ( (data.ds.gamma - 1.0) * data["gas", "density"] * data["gas", "specific_thermal_energy"] ) @pytest.mark.parametrize( "add_field_kwargs", [ # full default: auto unit detection, no (in)validation {}, # explicit "auto", should be identical to default behaviour {"units": "auto"}, # explicitly requesting dimensionless units {"units": "dimensionless"}, # explicitly requesting dimensionless units (short hand) {"units": ""}, # explictly requesting no dimensions {"dimensions": yt.units.dimensionless}, # should work with unyt.dimensionless too {"dimensions": unyt.dimensionless}, # supported short hand {"dimensions": "dimensionless"}, ], ) def test_dimensionless_field(add_field_kwargs): ds = fake_random_ds(16) def _dimensionless_field(field, data): return data["gas", "density"] / data["gas", "density"].units ds.add_field( name=("gas", "dimensionless_density"), function=_dimensionless_field, sampling_type="local", **add_field_kwargs, ) # check access ds.all_data()["gas", "dimensionless_density"] yt-project-yt-f043ac8/yt/data_objects/tests/test_bbox.py000066400000000000000000000037751510711153200234510ustar00rootroot00000000000000# Some tests for finding bounding boxes import numpy as np from numpy.testing import assert_equal from yt.testing import assert_allclose_units, fake_amr_ds def test_object_bbox(): ds = fake_amr_ds() reg = ds.box( ds.domain_left_edge + 0.5 * ds.domain_width, ds.domain_right_edge - 0.5 * ds.domain_width, ) le, re = reg.get_bbox() assert_equal(le, ds.domain_left_edge + 0.5 * ds.domain_width) assert_equal(re, ds.domain_right_edge - 0.5 * ds.domain_width) sp = ds.sphere("c", (0.1, "unitary")) le, re = sp.get_bbox() assert_equal(le, -sp.radius + sp.center) assert_equal(re, sp.radius + sp.center) dk = ds.disk("c", [1, 1, 0], (0.25, "unitary"), (0.25, "unitary")) le, re = dk.get_bbox() le0 = ds.arr( [0.5 - 0.25 * np.sqrt(2.0), 0.5 - 0.25 * np.sqrt(2.0), 0.25], "code_length" ) re0 = ds.arr( [0.5 + 0.25 * np.sqrt(2.0), 0.5 + 0.25 * np.sqrt(2.0), 0.75], "code_length" ) assert_allclose_units(le, le0) assert_allclose_units(re, re0) ep = ds.ellipsoid("c", 0.3, 0.2, 0.1, np.array([0.1, 0.1, 0.1]), 0.2) le, re = ep.get_bbox() assert_equal(le, -ds.quan(0.3, "code_length") + sp.center) assert_equal(re, ds.quan(0.3, "code_length") + sp.center) spb = ds.sphere( ds.domain_center - ds.quan(0.1, "code_length"), (0.1, "code_length") ) regb = ds.box(ds.domain_center, ds.domain_center + ds.quan(0.2, "code_length")) br1 = spb & regb br2 = spb | regb br3 = spb ^ regb br4 = ~regb le1, re1 = br1.get_bbox() le2, re2 = br2.get_bbox() le3, re3 = br3.get_bbox() le4, re4 = br4.get_bbox() le0 = ds.arr([0.3, 0.3, 0.3], "code_length") re0 = ds.arr([0.7, 0.7, 0.7], "code_length") assert_allclose_units(le1, le0) assert_allclose_units(re1, re0) assert_allclose_units(le2, le0) assert_allclose_units(re2, re0) assert_allclose_units(le3, le0) assert_allclose_units(re3, re0) assert_equal(le4, regb.left_edge) assert_equal(re4, regb.right_edge) yt-project-yt-f043ac8/yt/data_objects/tests/test_boolean_regions.py000066400000000000000000000612341510711153200256560ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal from yt.testing import fake_amr_ds # We use morton indices in this test because they are single floating point # values that uniquely identify each cell. That's a convenient way to compare # inclusion in set operations, since there are no duplicates. def test_boolean_spheres_no_overlap(): r"""Test to make sure that boolean objects (spheres, no overlap) behave the way we expect. Test non-overlapping spheres. This also checks that the original spheres don't change as part of constructing the booleans. """ ds = fake_amr_ds() sp1 = ds.sphere([0.25, 0.25, 0.25], 0.15) sp2 = ds.sphere([0.75, 0.75, 0.75], 0.15) # Store the original indices i1 = sp1["index", "morton_index"] i1.sort() i2 = sp2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = sp1 & sp2 bo2 = sp1 - sp2 bo3 = sp1 | sp2 # also works with + bo4 = ds.union([sp1, sp2]) bo5 = ds.intersection([sp1, sp2]) # This makes sure the original containers didn't change. new_i1 = sp1["index", "morton_index"] new_i1.sort() new_i2 = sp2["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() assert_array_equal(b4, ii) bo6 = sp1 ^ sp2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_spheres_overlap(): r"""Test to make sure that boolean objects (spheres, overlap) behave the way we expect. Test overlapping spheres. """ ds = fake_amr_ds() sp1 = ds.sphere([0.45, 0.45, 0.45], 0.15) sp2 = ds.sphere([0.55, 0.55, 0.55], 0.15) # Get indices of both. i1 = sp1["index", "morton_index"] i2 = sp2["index", "morton_index"] # Make some booleans bo1 = sp1 & sp2 bo2 = sp1 - sp2 bo3 = sp1 | sp2 bo4 = ds.union([sp1, sp2]) bo5 = ds.intersection([sp1, sp2]) # Now make sure the indices also behave as we expect. lens = np.intersect1d(i1, i2) apple = np.setdiff1d(i1, i2) both = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, lens) assert_array_equal(b2, apple) assert_array_equal(b3, both) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = sp1 ^ sp2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_regions_no_overlap(): r"""Test to make sure that boolean objects (regions, no overlap) behave the way we expect. Test non-overlapping regions. This also checks that the original regions don't change as part of constructing the booleans. """ ds = fake_amr_ds() re1 = ds.region([0.25] * 3, [0.2] * 3, [0.3] * 3) re2 = ds.region([0.65] * 3, [0.6] * 3, [0.7] * 3) # Store the original indices i1 = re1["index", "morton_index"] i1.sort() i2 = re2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = re1 & re2 bo2 = re1 - re2 bo3 = re1 | re2 bo4 = ds.union([re1, re2]) bo5 = ds.intersection([re1, re2]) # This makes sure the original containers didn't change. new_i1 = re1["index", "morton_index"] new_i1.sort() new_i2 = re2["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = re1 ^ re2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_regions_overlap(): r"""Test to make sure that boolean objects (regions, overlap) behave the way we expect. Test overlapping regions. """ ds = fake_amr_ds() re1 = ds.region([0.55] * 3, [0.5] * 3, [0.6] * 3) re2 = ds.region([0.6] * 3, [0.55] * 3, [0.65] * 3) # Get indices of both. i1 = re1["index", "morton_index"] i2 = re2["index", "morton_index"] # Make some booleans bo1 = re1 & re2 bo2 = re1 - re2 bo3 = re1 | re2 bo4 = ds.union([re1, re2]) bo5 = ds.intersection([re1, re2]) # Now make sure the indices also behave as we expect. cube = np.intersect1d(i1, i2) bite_cube = np.setdiff1d(i1, i2) both = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, cube) assert_array_equal(b2, bite_cube) assert_array_equal(b3, both) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = re1 ^ re2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_cylinders_no_overlap(): r"""Test to make sure that boolean objects (cylinders, no overlap) behave the way we expect. Test non-overlapping cylinders. This also checks that the original cylinders don't change as part of constructing the booleans. """ ds = fake_amr_ds() cyl1 = ds.disk([0.25] * 3, [1, 0, 0], 0.1, 0.1) cyl2 = ds.disk([0.75] * 3, [1, 0, 0], 0.1, 0.1) # Store the original indices i1 = cyl1["index", "morton_index"] i1.sort() i2 = cyl2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = cyl1 & cyl2 bo2 = cyl1 - cyl2 bo3 = cyl1 | cyl2 bo4 = ds.union([cyl1, cyl2]) bo5 = ds.intersection([cyl1, cyl2]) # This makes sure the original containers didn't change. new_i1 = cyl1["index", "morton_index"] new_i1.sort() new_i2 = cyl2["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = cyl1 ^ cyl2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_cylinders_overlap(): r"""Test to make sure that boolean objects (cylinders, overlap) behave the way we expect. Test overlapping cylinders. """ ds = fake_amr_ds() cyl1 = ds.disk([0.45] * 3, [1, 0, 0], 0.2, 0.2) cyl2 = ds.disk([0.55] * 3, [1, 0, 0], 0.2, 0.2) # Get indices of both. i1 = cyl1["index", "morton_index"] i2 = cyl2["index", "morton_index"] # Make some booleans bo1 = cyl1 & cyl2 bo2 = cyl1 - cyl2 bo3 = cyl1 | cyl2 bo4 = ds.union([cyl1, cyl2]) bo5 = ds.intersection([cyl1, cyl2]) # Now make sure the indices also behave as we expect. vlens = np.intersect1d(i1, i2) bite_disk = np.setdiff1d(i1, i2) both = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, vlens) assert_array_equal(b2, bite_disk) assert_array_equal(b3, both) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = cyl1 ^ cyl2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) del ds def test_boolean_ellipsoids_no_overlap(): r"""Test to make sure that boolean objects (ellipsoids, no overlap) behave the way we expect. Test non-overlapping ellipsoids. This also checks that the original ellipsoids don't change as part of constructing the booleans. """ ds = fake_amr_ds() ell1 = ds.ellipsoid([0.25] * 3, 0.05, 0.05, 0.05, np.array([0.1] * 3), 0.1) ell2 = ds.ellipsoid([0.75] * 3, 0.05, 0.05, 0.05, np.array([0.1] * 3), 0.1) # Store the original indices i1 = ell1["index", "morton_index"] i1.sort() i2 = ell2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = ell1 & ell2 bo2 = ell1 - ell2 bo3 = ell1 | ell2 bo4 = ds.union([ell1, ell2]) bo5 = ds.intersection([ell1, ell2]) # This makes sure the original containers didn't change. new_i1 = ell1["index", "morton_index"] new_i1.sort() new_i2 = ell2["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = ell1 ^ ell2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_ellipsoids_overlap(): r"""Test to make sure that boolean objects (ellipsoids, overlap) behave the way we expect. Test overlapping ellipsoids. """ ds = fake_amr_ds() ell1 = ds.ellipsoid([0.45] * 3, 0.05, 0.05, 0.05, np.array([0.1] * 3), 0.1) ell2 = ds.ellipsoid([0.55] * 3, 0.05, 0.05, 0.05, np.array([0.1] * 3), 0.1) # Get indices of both. i1 = ell1["index", "morton_index"] i2 = ell2["index", "morton_index"] # Make some booleans bo1 = ell1 & ell2 bo2 = ell1 - ell2 bo3 = ell1 | ell2 bo4 = ds.union([ell1, ell2]) bo5 = ds.intersection([ell1, ell2]) # Now make sure the indices also behave as we expect. overlap = np.intersect1d(i1, i2) diff = np.setdiff1d(i1, i2) both = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, overlap) assert_array_equal(b2, diff) assert_array_equal(b3, both) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = ell1 ^ ell2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_mix_periodicity(): r"""Test that a hybrid boolean region behaves as we expect. This also tests nested logic and that periodicity works. """ ds = fake_amr_ds() re = ds.region([0.5] * 3, [0.0] * 3, [1] * 3) # whole thing sp = ds.sphere([0.95] * 3, 0.3) # wraps around cyl = ds.disk([0.05] * 3, [1, 1, 1], 0.1, 0.4) # wraps around # Get original indices rei = re["index", "morton_index"] spi = sp["index", "morton_index"] cyli = cyl["index", "morton_index"] # Make some booleans # whole box minux spherical bites at corners bo1 = re - sp # sphere plus cylinder bo2 = sp | cyl # a jumble, the region minus the sp+cyl bo3 = re - (sp | cyl) # Now make sure the indices also behave as we expect. bo4 = ds.union([re, sp, cyl]) bo5 = ds.intersection([re, sp, cyl]) expect = np.setdiff1d(rei, spi) ii = bo1["index", "morton_index"] ii.sort() assert_array_equal(expect, ii) # expect = np.union1d(spi, cyli) ii = bo2["index", "morton_index"] ii.sort() assert_array_equal(expect, ii) # expect = np.union1d(spi, cyli) expect = np.setdiff1d(rei, expect) ii = bo3["index", "morton_index"] ii.sort() assert_array_equal(expect, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() ii = np.union1d(np.union1d(rei, cyli), spi) ii.sort() assert_array_equal(ii, b4) ii = np.intersect1d(np.intersect1d(rei, cyli), spi) ii.sort() assert_array_equal(ii, b5) bo6 = (re ^ sp) ^ cyl b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(np.setxor1d(rei, spi), cyli)) def test_boolean_ray_region_no_overlap(): r"""Test to make sure that boolean objects (ray, region, no overlap) behave the way we expect. Test non-overlapping ray and region. This also checks that the original objects don't change as part of constructing the booleans. """ ds = fake_amr_ds() re = ds.box([0.25] * 3, [0.75] * 3) ra = ds.ray([0.1] * 3, [0.1, 0.1, 0.9]) # Store the original indices i1 = re["index", "morton_index"] i1.sort() i2 = ra["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = re & ra bo2 = re - ra bo3 = re | ra bo4 = ds.union([re, ra]) bo5 = ds.intersection([re, ra]) # This makes sure the original containers didn't change. new_i1 = re["index", "morton_index"] new_i1.sort() new_i2 = ra["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = re ^ ra b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_ray_region_overlap(): r"""Test to make sure that boolean objects (ray, region, overlap) behave the way we expect. Test overlapping ray and region. This also checks that the original objects don't change as part of constructing the booleans. """ ds = fake_amr_ds() re = ds.box([0.25] * 3, [0.75] * 3) ra = ds.ray([0] * 3, [1] * 3) # Get indices of both. i1 = re["index", "morton_index"] i2 = ra["index", "morton_index"] # Make some booleans bo1 = re & ra bo2 = re - ra bo3 = re | ra bo4 = ds.union([re, ra]) bo5 = ds.intersection([re, ra]) # Now make sure the indices also behave as we expect. short_line = np.intersect1d(i1, i2) cube_minus_line = np.setdiff1d(i1, i2) both = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, short_line) assert_array_equal(b2, cube_minus_line) assert_array_equal(b3, both) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = re ^ ra b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_rays_no_overlap(): r"""Test to make sure that boolean objects (rays, no overlap) behave the way we expect. Test non-overlapping rays. """ ds = fake_amr_ds() ra1 = ds.ray([0, 0, 0], [0, 0, 1]) ra2 = ds.ray([1, 0, 0], [1, 0, 1]) # Store the original indices i1 = ra1["index", "morton_index"] i1.sort() i2 = ra2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = ra1 & ra2 bo2 = ra1 - ra2 bo3 = ra1 | ra2 bo4 = ds.union([ra1, ra2]) bo5 = ds.intersection([ra1, ra2]) # This makes sure the original containers didn't change. new_i1 = ra1["index", "morton_index"] new_i1.sort() new_i2 = ra2["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = ra1 ^ ra2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_rays_overlap(): r"""Test to make sure that boolean objects (rays, overlap) behave the way we expect. Test non-overlapping rays. """ ds = fake_amr_ds() ra1 = ds.ray([0] * 3, [1] * 3) ra2 = ds.ray([0] * 3, [0.5] * 3) # Get indices of both. i1 = ra1["index", "morton_index"] i1.sort() i2 = ra2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = ra1 & ra2 bo2 = ra1 - ra2 bo3 = ra1 | ra2 bo4 = ds.union([ra1, ra2]) bo5 = ds.intersection([ra1, ra2]) # Now make sure the indices also behave as we expect. short_line = np.intersect1d(i1, i2) short_line_b = np.setdiff1d(i1, i2) full_line = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, short_line) assert_array_equal(b2, short_line_b) assert_array_equal(b3, full_line) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, i1) assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = ra1 ^ ra2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_slices_no_overlap(): r"""Test to make sure that boolean objects (slices, no overlap) behave the way we expect. Test non-overlapping slices. This also checks that the original regions don't change as part of constructing the booleans. """ ds = fake_amr_ds() sl1 = ds.r[:, :, 0.25] sl2 = ds.r[:, :, 0.75] # Store the original indices i1 = sl1["index", "morton_index"] i1.sort() i2 = sl2["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = sl1 & sl2 bo2 = sl1 - sl2 bo3 = sl1 | sl2 bo4 = ds.union([sl1, sl2]) bo5 = ds.intersection([sl1, sl2]) # This makes sure the original containers didn't change. new_i1 = sl1["index", "morton_index"] new_i1.sort() new_i2 = sl2["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = sl1 ^ sl2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_slices_overlap(): r"""Test to make sure that boolean objects (slices, overlap) behave the way we expect. Test overlapping slices. """ ds = fake_amr_ds() sl1 = ds.r[:, :, 0.25] sl2 = ds.r[:, 0.75, :] # Get indices of both. i1 = sl1["index", "morton_index"] i2 = sl2["index", "morton_index"] # Make some booleans bo1 = sl1 & sl2 bo2 = sl1 - sl2 bo3 = sl1 | sl2 bo4 = ds.union([sl1, sl2]) bo5 = ds.intersection([sl1, sl2]) # Now make sure the indices also behave as we expect. line = np.intersect1d(i1, i2) orig = np.setdiff1d(i1, i2) both = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, line) assert_array_equal(b2, orig) assert_array_equal(b3, both) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = sl1 ^ sl2 b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_ray_slice_no_overlap(): r"""Test to make sure that boolean objects (ray, slice, no overlap) behave the way we expect. Test non-overlapping ray and slice. This also checks that the original regions don't change as part of constructing the booleans. """ ds = fake_amr_ds() sl = ds.r[:, :, 0.25] ra = ds.ray([0] * 3, [0, 1, 0]) # Store the original indices i1 = sl["index", "morton_index"] i1.sort() i2 = ra["index", "morton_index"] i2.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = sl & ra bo2 = sl - ra bo3 = sl | ra bo4 = ds.union([sl, ra]) bo5 = ds.intersection([sl, ra]) # This makes sure the original containers didn't change. new_i1 = sl["index", "morton_index"] new_i1.sort() new_i2 = ra["index", "morton_index"] new_i2.sort() assert_array_equal(new_i1, i1) assert_array_equal(new_i2, i2) # Now make sure the indices also behave as we expect. empty = np.array([]) assert_array_equal(bo1["index", "morton_index"], empty) assert_array_equal(bo5["index", "morton_index"], empty) b2 = bo2["index", "morton_index"] b2.sort() assert_array_equal(b2, i1) b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b3, ii) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, b4) bo6 = sl ^ ra b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) def test_boolean_ray_slice_overlap(): r"""Test to make sure that boolean objects (rays and slices, overlap) behave the way we expect. Test overlapping rays and slices. """ ds = fake_amr_ds() sl = ds.r[:, :, 0.25] ra = ds.ray([0, 0, 0.25], [0, 1, 0.25]) # Get indices of both. i1 = sl["index", "morton_index"] i1.sort() i2 = ra["index", "morton_index"] i1.sort() ii = np.concatenate((i1, i2)) ii.sort() # Make some booleans bo1 = sl & ra bo2 = sl - ra bo3 = sl | ra bo4 = ds.union([sl, ra]) bo5 = ds.intersection([sl, ra]) # Now make sure the indices also behave as we expect. line = np.intersect1d(i1, i2) sheet_minus_line = np.setdiff1d(i1, i2) sheet = np.union1d(i1, i2) b1 = bo1["index", "morton_index"] b1.sort() b2 = bo2["index", "morton_index"] b2.sort() b3 = bo3["index", "morton_index"] b3.sort() assert_array_equal(b1, line) assert_array_equal(b2, sheet_minus_line) assert_array_equal(b3, sheet) b4 = bo4["index", "morton_index"] b4.sort() b5 = bo5["index", "morton_index"] b5.sort() assert_array_equal(b3, i1) assert_array_equal(b3, b4) assert_array_equal(b1, b5) bo6 = sl ^ ra b6 = bo6["index", "morton_index"] b6.sort() assert_array_equal(b6, np.setxor1d(i1, i2)) yt-project-yt-f043ac8/yt/data_objects/tests/test_center_squeeze.py000066400000000000000000000016351510711153200255310ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.testing import fake_amr_ds, fake_particle_ds, fake_random_ds def test_center_squeeze(): # checks that the center is reshaped correctly # create and test amr, random and particle data check_single_ds(fake_amr_ds(fields=("Density",), units=("g/cm**3",))) check_single_ds(fake_random_ds(16, fields=("Density",), units=("g/cm**3",))) check_single_ds(fake_particle_ds(npart=100)) def check_single_ds(ds): # checks that the center center = ds.domain_center # reference center value for test_shape in [(1, 3), (1, 1, 3)]: new_center = center.reshape(test_shape) assert_equal(ds.sphere(new_center, 0.25).center, center) assert_equal(ds.slice(0, 0.25, center=new_center).center, center) assert_equal( ds.region(new_center, [-0.25, -0.25, -0.25], [0.25, 0.25, 0.25]).center, center, ) yt-project-yt-f043ac8/yt/data_objects/tests/test_chunking.py000066400000000000000000000042741510711153200243200ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.testing import fake_random_ds from yt.units._numpy_wrapper_functions import uconcatenate def _get_dobjs(c): dobjs = [ ("sphere", ("center", (1.0, "unitary"))), ("sphere", ("center", (0.1, "unitary"))), ("ortho_ray", (0, (c[1], c[2]))), ("slice", (0, c[0])), # ("disk", ("center", [0.1, 0.3, 0.6], # (0.2, 'unitary'), (0.1, 'unitary'))), ("cutting", ([0.1, 0.3, 0.6], "center")), ("all_data", ()), ] return dobjs def test_chunking(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(64, nprocs=nprocs) c = (ds.domain_right_edge + ds.domain_left_edge) / 2.0 c += ds.arr(0.5 / ds.domain_dimensions, "code_length") for dobj in _get_dobjs(c): obj = getattr(ds, dobj[0])(*dobj[1]) coords = {"f": {}, "i": {}} for t in ["io", "all", "spatial"]: coords["i"][t] = [] coords["f"][t] = [] for chunk in obj.chunks(None, t): coords["f"][t].append(chunk.fcoords[:, :]) coords["i"][t].append(chunk.icoords[:, :]) coords["f"][t] = uconcatenate(coords["f"][t]) coords["i"][t] = uconcatenate(coords["i"][t]) coords["f"][t].sort() coords["i"][t].sort() assert_equal(coords["f"]["io"], coords["f"]["all"]) assert_equal(coords["f"]["io"], coords["f"]["spatial"]) assert_equal(coords["i"]["io"], coords["i"]["all"]) assert_equal(coords["i"]["io"], coords["i"]["spatial"]) def test_ds_hold(): ds1 = fake_random_ds(64) ds2 = fake_random_ds(128) dd = ds1.all_data() # dd.ds is a weakref, so can't use "is" assert dd.ds.__hash__() == ds1.__hash__() assert dd.index is ds1.index assert_equal(dd["index", "ones"].size, 64**3) with dd._ds_hold(ds2): assert dd.ds.__hash__() == ds2.__hash__() assert dd.index is ds2.index assert_equal(dd["index", "ones"].size, 128**3) assert dd.ds.__hash__() == ds1.__hash__() assert dd.index is ds1.index assert_equal(dd["index", "ones"].size, 64**3) yt-project-yt-f043ac8/yt/data_objects/tests/test_clone.py000066400000000000000000000021611510711153200236030ustar00rootroot00000000000000from numpy.testing import assert_array_equal, assert_equal from yt.testing import fake_random_ds def test_clone_sphere(): # Now we test that we can get different radial velocities based on field # parameters. fields = ("density", "velocity_x", "velocity_y", "velocity_z") units = ("g/cm**3", "cm/s", "cm/s", "cm/s") # Get the first sphere ds = fake_random_ds(16, fields=fields, units=units) sp0 = ds.sphere(ds.domain_center, 0.25) assert_equal(list(sp0.keys()), []) sp1 = sp0.clone() sp0["gas", "density"] assert_equal(list(sp0.keys()), (("gas", "density"),)) assert_equal(list(sp1.keys()), []) sp1["gas", "density"] assert_array_equal(sp0["gas", "density"], sp1["gas", "density"]) def test_clone_cut_region(): fields = ("density", "temperature") units = ("g/cm**3", "K") ds = fake_random_ds(64, nprocs=4, fields=fields, units=units) dd = ds.all_data() reg1 = dd.cut_region( ["obj['gas', 'temperature'] > 0.5", "obj['gas', 'density'] < 0.75"] ) reg2 = reg1.clone() assert_array_equal(reg1["gas", "density"], reg2["gas", "density"]) yt-project-yt-f043ac8/yt/data_objects/tests/test_compose.py000066400000000000000000000134471510711153200241610ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal from yt.testing import fake_amr_ds, fake_random_ds from yt.units._numpy_wrapper_functions import uintersect1d from yt.units.yt_array import YTArray def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True # Copied from test_boolean for computing a unique identifier for # each cell from cell positions def _IDFIELD(field, data): width = data.ds.domain_right_edge - data.ds.domain_left_edge min_dx = YTArray(1.0 / 8192, units="code_length", registry=data.ds.unit_registry) delta = width / min_dx x = data["index", "x"] - min_dx / 2.0 y = data["index", "y"] - min_dx / 2.0 z = data["index", "z"] - min_dx / 2.0 xi = x / min_dx yi = y / min_dx zi = z / min_dx index = xi + delta[0] * (yi + delta[1] * zi) return index def test_compose_no_overlap(): r"""Test to make sure that composed data objects that don't overlap behave the way we expect (return empty collections) """ empty = np.array([]) for n in [1, 2, 4, 8]: ds = fake_random_ds(64, nprocs=n) ds.add_field(("index", "ID"), sampling_type="cell", function=_IDFIELD) # position parameters for initial region center = [0.25] * 3 left_edge = [0.1] * 3 right_edge = [0.4] * 3 normal = [1, 0, 0] radius = height = 0.15 # initial 3D regions sources = [ ds.sphere(center, radius), ds.region(center, left_edge, right_edge), ds.disk(center, normal, radius, height), ] # position parameters for non-overlapping regions center = [0.75] * 3 left_edge = [0.6] * 3 right_edge = [0.9] * 3 # subselect non-overlapping 0, 1, 2, 3D regions for data1 in sources: data2 = ds.sphere(center, radius, data_source=data1) assert_array_equal(data2["index", "ID"], empty) data2 = ds.region(center, left_edge, right_edge, data_source=data1) assert_array_equal(data2["index", "ID"], empty) data2 = ds.disk(center, normal, radius, height, data_source=data1) assert_array_equal(data2["index", "ID"], empty) for d in range(3): data2 = ds.slice(d, center[d], data_source=data1) assert_array_equal(data2["index", "ID"], empty) for d in range(3): data2 = ds.ortho_ray( d, center[0:d] + center[d + 1 :], data_source=data1 ) assert_array_equal(data2["index", "ID"], empty) data2 = ds.point(center, data_source=data1) assert_array_equal(data2["index", "ID"], empty) def test_compose_overlap(): r"""Test to make sure that composed data objects that do overlap behave the way we expect """ for n in [1, 2, 4, 8]: ds = fake_random_ds(64, nprocs=n) ds.add_field(("index", "ID"), sampling_type="cell", function=_IDFIELD) # position parameters for initial region center = [0.4, 0.5, 0.5] left_edge = [0.1] * 3 right_edge = [0.7] * 3 normal = [1, 0, 0] radius = height = 0.15 # initial 3D regions sources = [ ds.sphere(center, radius), ds.region(center, left_edge, right_edge), ds.disk(center, normal, radius, height), ] # position parameters for overlapping regions center = [0.6, 0.5, 0.5] left_edge = [0.3] * 3 right_edge = [0.9] * 3 # subselect non-overlapping 0, 1, 2, 3D regions for data1 in sources: id1 = data1["index", "ID"] data2 = ds.sphere(center, radius) data3 = ds.sphere(center, radius, data_source=data1) id2 = data2["index", "ID"] id3 = data3["index", "ID"] id3.sort() assert_array_equal(uintersect1d(id1, id2), id3) data2 = ds.region(center, left_edge, right_edge) data3 = ds.region(center, left_edge, right_edge, data_source=data1) id2 = data2["index", "ID"] id3 = data3["index", "ID"] id3.sort() assert_array_equal(uintersect1d(id1, id2), id3) data2 = ds.disk(center, normal, radius, height) data3 = ds.disk(center, normal, radius, height, data_source=data1) id2 = data2["index", "ID"] id3 = data3["index", "ID"] id3.sort() assert_array_equal(uintersect1d(id1, id2), id3) for d in range(3): data2 = ds.slice(d, center[d]) data3 = ds.slice(d, center[d], data_source=data1) id2 = data2["index", "ID"] id3 = data3["index", "ID"] id3.sort() assert_array_equal(uintersect1d(id1, id2), id3) for d in range(3): data2 = ds.ortho_ray(d, center[0:d] + center[d + 1 :]) data3 = ds.ortho_ray( d, center[0:d] + center[d + 1 :], data_source=data1 ) id2 = data2["index", "ID"] id3 = data3["index", "ID"] id3.sort() assert_array_equal(uintersect1d(id1, id2), id3) data2 = ds.point(center) data3 = ds.point(center, data_source=data1) id2 = data2["index", "ID"] id3 = data3["index", "ID"] id3.sort() assert_array_equal(uintersect1d(id1, id2), id3) def test_compose_max_level_min_level(): ds = fake_amr_ds() ad = ds.all_data() ad.max_level = 2 slc = ds.slice("x", 0.5, data_source=ad) assert slc["index", "grid_level"].max() == 2 frb = slc.to_frb(1.0, 128) assert np.all(frb["stream", "Density"] > 0) assert frb["index", "grid_level"].max() == 2 yt-project-yt-f043ac8/yt/data_objects/tests/test_connected_sets.py000066400000000000000000000011231510711153200255000ustar00rootroot00000000000000from yt.testing import fake_random_ds from yt.utilities.answer_testing.level_sets_tests import ExtractConnectedSetsTest def test_connected_sets(): ds = fake_random_ds(16, nprocs=8, particles=16**3) data_source = ds.disk([0.5, 0.5, 0.5], [0.0, 0.0, 1.0], (8, "kpc"), (1, "kpc")) field = ("gas", "density") min_val, max_val = data_source[field].min() / 2, data_source[field].max() / 2 data_source.extract_connected_sets( field, 5, min_val, max_val, log_space=True, cumulative=True ) yield ExtractConnectedSetsTest(ds, data_source, field, 5, min_val, max_val) yt-project-yt-f043ac8/yt/data_objects/tests/test_covering_grid.py000066400000000000000000000342141510711153200253300ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_array_equal, assert_equal from yt.fields.derived_field import ValidateParameter from yt.loaders import load, load_particles from yt.testing import ( fake_octree_ds, fake_random_ds, requires_file, requires_module, ) from yt.units import kpc # cylindrical data for covering_grid test cyl_2d = "WDMerger_hdf5_chk_1000/WDMerger_hdf5_chk_1000.hdf5" cyl_3d = "MHD_Cyl3d_hdf5_plt_cnt_0100/MHD_Cyl3d_hdf5_plt_cnt_0100.hdf5" def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_covering_grid(): # We decompose in different ways for level in [0, 1, 2]: for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs) axis_name = ds.coordinates.axis_name dn = ds.refine_by**level cg = ds.covering_grid(level, [0.0, 0.0, 0.0], dn * ds.domain_dimensions) # Test coordinate generation assert_equal(np.unique(cg["index", f"d{axis_name[0]}"]).size, 1) xmi = cg["index", axis_name[0]].min() xma = cg["index", axis_name[0]].max() dx = cg["index", f"d{axis_name[0]}"].flat[0:1] edges = ds.arr([[0, 1], [0, 1], [0, 1]], "code_length") assert_equal(xmi, edges[0, 0] + dx / 2.0) assert_equal(xmi, cg["index", axis_name[0]][0, 0, 0]) assert_equal(xmi, cg["index", axis_name[0]][0, 1, 1]) assert_equal(xma, edges[0, 1] - dx / 2.0) assert_equal(xma, cg["index", axis_name[0]][-1, 0, 0]) assert_equal(xma, cg["index", axis_name[0]][-1, 1, 1]) assert_equal(np.unique(cg["index", f"d{axis_name[1]}"]).size, 1) ymi = cg["index", axis_name[1]].min() yma = cg["index", axis_name[1]].max() dy = cg["index", f"d{axis_name[1]}"][0] assert_equal(ymi, edges[1, 0] + dy / 2.0) assert_equal(ymi, cg["index", axis_name[1]][0, 0, 0]) assert_equal(ymi, cg["index", axis_name[1]][1, 0, 1]) assert_equal(yma, edges[1, 1] - dy / 2.0) assert_equal(yma, cg["index", axis_name[1]][0, -1, 0]) assert_equal(yma, cg["index", axis_name[1]][1, -1, 1]) assert_equal(np.unique(cg["index", f"d{axis_name[2]}"]).size, 1) zmi = cg["index", axis_name[2]].min() zma = cg["index", axis_name[2]].max() dz = cg["index", f"d{axis_name[2]}"][0] assert_equal(zmi, edges[2, 0] + dz / 2.0) assert_equal(zmi, cg["index", axis_name[2]][0, 0, 0]) assert_equal(zmi, cg["index", axis_name[2]][1, 1, 0]) assert_equal(zma, edges[2, 1] - dz / 2.0) assert_equal(zma, cg["index", axis_name[2]][0, 0, -1]) assert_equal(zma, cg["index", axis_name[2]][1, 1, -1]) # Now we test other attributes assert_equal(cg["index", "ones"].max(), 1.0) assert_equal(cg["index", "ones"].min(), 1.0) assert_equal(cg["index", "grid_level"], level) assert_equal(cg["index", "cell_volume"].sum(), ds.domain_width.prod()) for g in ds.index.grids: di = g.get_global_startindex() dd = g.ActiveDimensions for i in range(dn): f = cg["gas", "density"][ dn * di[0] + i : dn * (di[0] + dd[0]) + i : dn, dn * di[1] + i : dn * (di[1] + dd[1]) + i : dn, dn * di[2] + i : dn * (di[2] + dd[2]) + i : dn, ] assert_equal(f, g["gas", "density"]) # More tests for cylindrical geometry for fn in [cyl_2d, cyl_3d]: ds = load(fn) ad = ds.all_data() upper_ad = ad.cut_region(["obj['index', 'z'] > 0"]) sp = ds.sphere((0, 0, 0), 0.5 * ds.domain_width[0], data_source=upper_ad) sp.quantities.total_mass() @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_covering_grid_data_source(): # test the data_source kwarg (new with PR 4063) for level in [0, 1, 2]: for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs) dn = ds.refine_by**level cg = ds.covering_grid(level, [0.0, 0.0, 0.0], dn * ds.domain_dimensions) # check that the regularized sphere is centered where it should be, # to within the tolerance of the grid. Use a center offsect from # the domain center by a bit center = ds.domain_center + np.min(ds.domain_width) * 0.15 sp = ds.sphere(center, (0.1, "code_length")) dims = dn * ds.domain_dimensions cg_sp = ds.covering_grid(level, [0.0, 0.0, 0.0], dims, data_source=sp) # find the discrete center of the sphere by averaging the position # along each dimension where values are non-zero cg_mask = cg_sp["gas", "density"] != 0 discrete_c = ds.arr( [ np.mean(cg_sp["index", dim][cg_mask]) for dim in ds.coordinates.axis_order ] ) # should be centered to within the tolerance of the grid at least grid_tol = ds.arr( [cg_sp["index", dim][0, 0, 0] for dim in ds.coordinates.axis_order] ) assert np.all(np.abs(discrete_c - center) <= grid_tol) # check that a region covering the whole domain matches no data_source reg = ds.region(ds.domain_center, ds.domain_left_edge, ds.domain_right_edge) cg_reg = ds.covering_grid(level, [0.0, 0.0, 0.0], dims, data_source=reg) assert np.all(cg["gas", "density"] == cg_reg["gas", "density"]) # check that a box covering a subset of the domain is the right volume right_edge = ds.domain_left_edge + ds.domain_width * 0.5 c = (right_edge + ds.domain_left_edge) / 2 reg = ds.region(c, ds.domain_left_edge, right_edge) cg_reg = ds.covering_grid(level, [0.0, 0.0, 0.0], dims, data_source=reg) box_vol = cg_reg["index", "cell_volume"][ cg_reg["gas", "density"] != 0 ].sum() actual_vol = np.prod(right_edge - ds.domain_left_edge) assert box_vol == actual_vol @requires_module("xarray") def test_xarray_export(): def _run_tests(cg): xarr = cg.to_xarray(fields=[("gas", "density"), ("gas", "temperature")]) assert ("gas", "density") in xarr.variables assert ("gas", "temperature") in xarr.variables assert ("gas", "specific_thermal_energy") not in xarr.variables assert "x" in xarr.coords assert "y" in xarr.coords assert "z" in xarr.coords assert xarr.sizes["x"] == dn * ds.domain_dimensions[0] assert xarr.sizes["y"] == dn * ds.domain_dimensions[1] assert xarr.sizes["z"] == dn * ds.domain_dimensions[2] assert_equal(xarr.x, cg["index", "x"][:, 0, 0]) assert_equal(xarr.y, cg["index", "y"][0, :, 0]) assert_equal(xarr.z, cg["index", "z"][0, 0, :]) fields = ("density", "temperature", "specific_thermal_energy") units = ("g/cm**3", "K", "erg/g") for level in [0, 1, 2]: ds = fake_random_ds(16, fields=fields, units=units) dn = ds.refine_by**level rcg = ds.covering_grid(level, [0.0, 0.0, 0.0], dn * ds.domain_dimensions) _run_tests(rcg) scg = ds.smoothed_covering_grid( level, [0.0, 0.0, 0.0], dn * ds.domain_dimensions ) _run_tests(scg) ag1 = ds.arbitrary_grid( [0.0, 0.0, 0.0], [1.0, 1.0, 1.0], dn * ds.domain_dimensions ) _run_tests(ag1) ag2 = ds.arbitrary_grid( [0.1, 0.3, 0.2], [0.4, 1.0, 0.9], dn * ds.domain_dimensions ) _run_tests(ag2) def test_smoothed_covering_grid(): # We decompose in different ways for level in [0, 1, 2]: for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs) dn = ds.refine_by**level cg = ds.smoothed_covering_grid( level, [0.0, 0.0, 0.0], dn * ds.domain_dimensions ) assert_equal(cg["index", "ones"].max(), 1.0) assert_equal(cg["index", "ones"].min(), 1.0) assert_equal(cg["index", "cell_volume"].sum(), ds.domain_width.prod()) for g in ds.index.grids: if level != g.Level: continue di = g.get_global_startindex() dd = g.ActiveDimensions for i in range(dn): f = cg["gas", "density"][ dn * di[0] + i : dn * (di[0] + dd[0]) + i : dn, dn * di[1] + i : dn * (di[1] + dd[1]) + i : dn, dn * di[2] + i : dn * (di[2] + dd[2]) + i : dn, ] assert_equal(f, g["gas", "density"]) def test_arbitrary_grid(): for ncells in [32, 64]: for px in [0.125, 0.25, 0.55519]: particle_data = { "particle_position_x": np.array([px]), "particle_position_y": np.array([0.5]), "particle_position_z": np.array([0.5]), "particle_mass": np.array([1.0]), } ds = load_particles(particle_data) for dims in ([ncells] * 3, [ncells, ncells / 2, ncells / 4]): LE = np.array([0.05, 0.05, 0.05]) RE = np.array([0.95, 0.95, 0.95]) dims = np.array(dims) dds = (RE - LE) / dims volume = ds.quan(np.prod(dds), "cm**3") obj = ds.arbitrary_grid(LE, RE, dims) deposited_mass = obj["deposit", "all_density"].sum() * volume assert_equal(deposited_mass, ds.quan(1.0, "g")) LE = np.array([0.00, 0.00, 0.00]) RE = np.array([0.05, 0.05, 0.05]) obj = ds.arbitrary_grid(LE, RE, dims) deposited_mass = obj["deposit", "all_density"].sum() assert_equal(deposited_mass, 0) # Test that we get identical results to the covering grid for unigrid data. # Testing AMR data is much harder. for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(32, nprocs=nprocs) for ref_level in [0, 1, 2]: cg = ds.covering_grid( ref_level, [0.0, 0.0, 0.0], 2**ref_level * ds.domain_dimensions ) ag = ds.arbitrary_grid( [0.0, 0.0, 0.0], [1.0, 1.0, 1.0], 2**ref_level * ds.domain_dimensions ) assert_almost_equal(cg["gas", "density"], ag["gas", "density"]) def test_octree_cg(): ds = fake_octree_ds(num_zones=1, partial_coverage=0) cgrid = ds.covering_grid( 0, left_edge=ds.domain_left_edge, dims=ds.domain_dimensions ) density_field = cgrid["gas", "density"] assert_equal((density_field == 0.0).sum(), 0) def test_smoothed_covering_grid_2d_dataset(): ds = fake_random_ds([32, 32, 1], nprocs=4) ds.force_periodicity() scg = ds.smoothed_covering_grid(1, [0.0, 0.0, 0.0], [32, 32, 1]) assert_equal(scg["gas", "density"].shape, [32, 32, 1]) def test_arbitrary_grid_derived_field(): def custom_metal_density(field, data): # Calculating some random value return data["gas", "density"] * np.random.random_sample() ds = fake_random_ds(64, nprocs=8, particles=16**2) ds.add_field( ("gas", "Metal_Density"), units="g/cm**3", function=custom_metal_density, sampling_type="cell", ) def _tracerf(field, data): return data["gas", "Metal_Density"] / data["gas", "density"] ds.add_field( ("gas", "tracerf"), function=_tracerf, units="dimensionless", sampling_type="cell", take_log=False, ) galgas = ds.arbitrary_grid([0.4, 0.4, 0.4], [0.99, 0.99, 0.99], dims=[32, 32, 32]) galgas["gas", "tracerf"] def test_arbitrary_field_parameters(): def _test_field(field, data): par = data.get_field_parameter("test_parameter") return par * data["all", "particle_mass"] ds = fake_random_ds(64, nprocs=8, particles=16**2) ds.add_field( ("all", "test_field"), units="g", function=_test_field, sampling_type="particle", validators=[ValidateParameter("test_parameter")], ) agrid = ds.arbitrary_grid([0.4, 0.4, 0.4], [0.99, 0.99, 0.99], dims=[32, 32, 32]) agrid.set_field_parameter("test_parameter", 2) assert_array_equal(2 * agrid["all", "particle_mass"], agrid["all", "test_field"]) def test_arbitrary_grid_edge(): # Tests bug fix for issue #2087 # Regardless of how left_edge and right_edge are passed, the result should be # a YTArray with a unit registry that matches that of the dataset. dims = [32, 32, 32] ds = fake_random_ds(dims) # Test when edge is a list, numpy array, YTArray with dataset units, and # YTArray with non-dataset units ledge = [ [0.0, 0.0, 0.0], np.array([0.0, 0.0, 0.0]), [0.0, 0.0, 0.0] * ds.length_unit, [0.0, 0.0, 0.0] * kpc, ] redge = [ [1.0, 1.0, 1.0], np.array([1.0, 1.0, 1.0]), [1.0, 1.0, 1.0] * ds.length_unit, [1.0, 1.0, 1.0] * kpc, ] ledge_ans = [ [0.0, 0.0, 0.0] * ds.length_unit.to("code_length"), np.array([0.0, 0.0, 0.0]) * ds.length_unit.to("code_length"), [0.0, 0.0, 0.0] * ds.length_unit, [0.0, 0.0, 0.0] * kpc, ] redge_ans = [ [1.0, 1.0, 1.0] * ds.length_unit.to("code_length"), np.array([1.0, 1.0, 1.0]) * ds.length_unit.to("code_length"), [1.0, 1.0, 1.0] * ds.length_unit, [1.0, 1.0, 1.0] * kpc, ] for le, re, le_ans, re_ans in zip(ledge, redge, ledge_ans, redge_ans, strict=True): ag = ds.arbitrary_grid(left_edge=le, right_edge=re, dims=dims) assert np.array_equal(ag.left_edge, le_ans) assert np.array_equal(ag.right_edge, re_ans) assert ag.left_edge.units.registry == ds.unit_registry assert ag.right_edge.units.registry == ds.unit_registry ag["gas", "density"] yt-project-yt-f043ac8/yt/data_objects/tests/test_cutting_plane.py000066400000000000000000000036161510711153200253450ustar00rootroot00000000000000import os import tempfile from numpy.testing import assert_equal from yt.testing import fake_random_ds from yt.units.unit_object import Unit def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def teardown_func(fns): for fn in fns: try: os.remove(fn) except OSError: pass def test_cutting_plane(): fns = [] for nprocs in [8, 1]: # We want to test both 1 proc and 8 procs, to make sure that # parallelism isn't broken ds = fake_random_ds(64, nprocs=nprocs) center = [0.5, 0.5, 0.5] normal = [1, 1, 1] cut = ds.cutting(normal, center) assert_equal(cut["index", "ones"].sum(), cut["index", "ones"].size) assert_equal(cut["index", "ones"].min(), 1.0) assert_equal(cut["index", "ones"].max(), 1.0) pw = cut.to_pw(fields=("gas", "density")) for p in pw.plots.values(): tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) p.save(name=tmpname) fns.append(tmpname) for width in [(1.0, "unitary"), 1.0, ds.quan(0.5, "code_length")]: frb = cut.to_frb(width, 64) for cut_field in [("index", "ones"), ("gas", "density")]: fi = ds._get_field_info(cut_field) data = frb[cut_field] assert_equal(data.info["data_source"], cut.__str__()) assert_equal(data.info["axis"], None) assert_equal(data.info["field"], str(cut_field)) assert_equal(data.units, Unit(fi.units)) assert_equal(data.info["xlim"], frb.bounds[:2]) assert_equal(data.info["ylim"], frb.bounds[2:]) assert_equal(data.info["length_to_cm"], ds.length_unit.in_cgs()) assert_equal(data.info["center"], cut.center) teardown_func(fns) yt-project-yt-f043ac8/yt/data_objects/tests/test_data_collection.py000066400000000000000000000023531510711153200256320ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.testing import assert_rel_equal, fake_random_ds def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_data_collection(): # We decompose in different ways for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs) coll = ds.data_collection(ds.index.grids) crho = coll["gas", "density"].sum(dtype="float64").to_ndarray() grho = np.sum( [g["gas", "density"].sum(dtype="float64") for g in ds.index.grids], dtype="float64", ) assert_rel_equal(np.array([crho]), np.array([grho]), 12) assert_equal(coll.size, ds.domain_dimensions.prod()) for gi in range(ds.index.num_grids): grids = ds.index.grids[: gi + 1] coll = ds.data_collection(grids) crho = coll["gas", "density"].sum(dtype="float64") grho = np.sum( [g["gas", "density"].sum(dtype="float64") for g in grids], dtype="float64", ) assert_rel_equal(np.array([crho]), np.array([grho]), 12) assert_equal(coll.size, sum(g.ActiveDimensions.prod() for g in grids)) yt-project-yt-f043ac8/yt/data_objects/tests/test_data_containers.py000066400000000000000000000146731510711153200256540ustar00rootroot00000000000000import os import shutil import tempfile import unittest import numpy as np from nose.tools import assert_raises from numpy.testing import assert_array_equal, assert_equal from yt.data_objects.data_containers import YTDataContainer from yt.data_objects.particle_filters import particle_filter from yt.testing import ( fake_amr_ds, fake_particle_ds, fake_random_ds, requires_module, ) from yt.utilities.exceptions import YTException, YTFieldNotFound class TestDataContainers(unittest.TestCase): @classmethod def setUpClass(cls): cls.tmpdir = tempfile.mkdtemp() cls.curdir = os.getcwd() os.chdir(cls.tmpdir) @classmethod def tearDownClass(cls): os.chdir(cls.curdir) shutil.rmtree(cls.tmpdir) def test_yt_data_container(self): # Test if ds could be None with assert_raises(RuntimeError) as err: YTDataContainer(None, None) desired = ( "Error: ds must be set either through class" " type or parameter to the constructor" ) assert_equal(str(err.exception), desired) # Test if field_data key exists ds = fake_random_ds(5) proj = ds.proj(("gas", "density"), 0, data_source=ds.all_data()) assert_equal("px" in proj.keys(), True) assert_equal("pz" in proj.keys(), False) # Delete the key and check if exits del proj["px"] assert_equal("px" in proj.keys(), False) del proj["gas", "density"] assert_equal("density" in proj.keys(), False) # Delete a non-existent field with assert_raises(YTFieldNotFound) as ex: del proj["p_mass"] desired = "Could not find field 'p_mass' in UniformGridData." assert_equal(str(ex.exception), desired) def test_write_out(self): filename = "sphere.txt" ds = fake_random_ds(16, particles=10) sp = ds.sphere(ds.domain_center, 0.25) sp.write_out(filename, fields=[("gas", "cell_volume")]) with open(filename) as file: file_row_1 = file.readline() file_row_2 = file.readline() file_row_2 = np.array(file_row_2.split("\t"), dtype=np.float64) sorted_keys = sorted(sp.field_data.keys()) keys = [str(k) for k in sorted_keys] keys = "\t".join(["#"] + keys + ["\n"]) data = [sp.field_data[k][0] for k in sorted_keys] assert_equal(keys, file_row_1) assert_array_equal(data, file_row_2) def test_invalid_write_out(self): filename = "sphere.txt" ds = fake_random_ds(16, particles=10) sp = ds.sphere(ds.domain_center, 0.25) with assert_raises(YTException): sp.write_out(filename, fields=[("all", "particle_ones")]) @requires_module("pandas") def test_to_dataframe(self): fields = [("gas", "density"), ("gas", "velocity_z")] ds = fake_random_ds(6) dd = ds.all_data() df = dd.to_dataframe(fields) assert_array_equal(dd[fields[0]], df[fields[0][1]]) assert_array_equal(dd[fields[1]], df[fields[1][1]]) @requires_module("astropy") def test_to_astropy_table(self): from yt.units.yt_array import YTArray fields = [("gas", "density"), ("gas", "velocity_z")] ds = fake_random_ds(6) dd = ds.all_data() at1 = dd.to_astropy_table(fields) assert_array_equal(dd[fields[0]].d, at1[fields[0][1]].value) assert_array_equal(dd[fields[1]].d, at1[fields[1][1]].value) assert dd[fields[0]].units == YTArray.from_astropy(at1[fields[0][1]]).units assert dd[fields[1]].units == YTArray.from_astropy(at1[fields[1][1]]).units def test_std(self): ds = fake_random_ds(3) ds.all_data().std(("gas", "density"), weight=("gas", "velocity_z")) def test_to_frb(self): # Test cylindrical geometry fields = ["density", "cell_mass"] units = ["g/cm**3", "g"] ds = fake_amr_ds( fields=fields, units=units, geometry="cylindrical", particles=16**3 ) dd = ds.all_data() proj = ds.proj( ("gas", "density"), weight_field=("gas", "cell_mass"), axis=1, data_source=dd, ) frb = proj.to_frb((1.0, "unitary"), 64) assert_equal(frb.radius, (1.0, "unitary")) assert_equal(frb.buff_size, 64) def test_extract_isocontours(self): # Test isocontour properties for AMRGridData fields = ["density", "cell_mass"] units = ["g/cm**3", "g"] ds = fake_amr_ds(fields=fields, units=units, particles=16**3) dd = ds.all_data() q = dd.quantities["WeightedAverageQuantity"] rho = q(("gas", "density"), weight=("gas", "cell_mass")) dd.extract_isocontours(("gas", "density"), rho, "triangles.obj", True) dd.calculate_isocontour_flux( ("gas", "density"), rho, ("index", "x"), ("index", "y"), ("index", "z"), ("index", "dx"), ) # Test error in case of ParticleData ds = fake_particle_ds() dd = ds.all_data() q = dd.quantities["WeightedAverageQuantity"] rho = q(("all", "particle_velocity_x"), weight=("all", "particle_mass")) with assert_raises(NotImplementedError): dd.extract_isocontours("density", rho, sample_values="x") def test_derived_field(self): # Test that derived field on filtered particles do not require # their parent field to be created ds = fake_particle_ds() dd = ds.all_data() dd.set_field_parameter("axis", 0) @particle_filter(requires=["particle_mass"], filtered_type="io") def massive(pfilter, data): return data[pfilter.filtered_type, "particle_mass"].to("code_mass") > 0.5 ds.add_particle_filter("massive") def fun(field, data): return data[field.name[0], "particle_mass"] # Add the field to the massive particles ds.add_field( ("massive", "test"), function=fun, sampling_type="particle", units="code_mass", ) expected_size = (dd["io", "particle_mass"].to("code_mass") > 0.5).sum() fields_to_test = [f for f in ds.derived_field_list if f[0] == "massive"] def test_this(fname): data = dd[fname] assert_equal(data.shape[0], expected_size) for fname in fields_to_test: test_this(fname) yt-project-yt-f043ac8/yt/data_objects/tests/test_dataset_access.py000066400000000000000000000162711510711153200254600ustar00rootroot00000000000000import numpy as np from nose.tools import assert_raises from numpy.testing import assert_almost_equal, assert_equal from yt.testing import ( fake_amr_ds, fake_particle_ds, fake_random_ds, requires_file, requires_module, ) from yt.utilities.answer_testing.framework import data_dir_load from yt.utilities.exceptions import YTDimensionalityError from yt.visualization.line_plot import LineBuffer # This will test the "dataset access" method. def test_box_creation(): ds = fake_random_ds(32, length_unit=2) left_edge = ds.arr([0.2, 0.2, 0.2], "cm") right_edge = ds.arr([0.6, 0.6, 0.6], "cm") center = (left_edge + right_edge) / 2 boxes = [ ds.box(left_edge, right_edge), ds.box(0.5 * np.array(left_edge), 0.5 * np.array(right_edge)), ds.box((0.5 * left_edge).tolist(), (0.5 * right_edge).tolist()), ] region = ds.region(center, left_edge, right_edge) for b in boxes: assert_almost_equal(b.left_edge, region.left_edge) assert_almost_equal(b.right_edge, region.right_edge) assert_almost_equal(b.center, region.center) def test_region_from_d(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) # We'll do a couple here # First, no string units reg1 = ds.r[0.2:0.3, 0.4:0.6, :] reg2 = ds.region([0.25, 0.5, 0.5], [0.2, 0.4, 0.0], [0.3, 0.6, 1.0]) assert_equal(reg1["gas", "density"], reg2["gas", "density"]) # Now, string units in some -- 1.0 == cm reg1 = ds.r[(0.1, "cm") : (0.5, "cm"), :, (0.25, "cm") : (0.35, "cm")] reg2 = ds.region([0.3, 0.5, 0.3], [0.1, 0.0, 0.25], [0.5, 1.0, 0.35]) assert_equal(reg1["gas", "density"], reg2["gas", "density"]) # Now, string units in some -- 1.0 == cm reg1 = ds.r[(0.1, "cm") : (0.5, "cm"), :, 0.25:0.35] reg2 = ds.region([0.3, 0.5, 0.3], [0.1, 0.0, 0.25], [0.5, 1.0, 0.35]) assert_equal(reg1["gas", "density"], reg2["gas", "density"]) # And, lots of : usage! reg1 = ds.r[:, :, :] reg2 = ds.all_data() assert_equal(reg1["gas", "density"], reg2["gas", "density"]) # Test slice as an index reg1 = ds.r[0.1:0.8] reg2 = ds.region([0.45, 0.45, 0.45], [0.1, 0.1, 0.1], [0.8, 0.8, 0.8]) assert_equal(reg1["gas", "density"], reg2["gas", "density"]) # Test with bad boundary initialization with assert_raises(RuntimeError): ds.r[0.3:0.1, 0.4:0.6, :] # Test region by creating an arbitrary grid reg1 = ds.r[0.15:0.55:16j, 0.25:0.65:32j, 0.35:0.75:64j] left_edge = np.array([0.15, 0.25, 0.35]) right_edge = np.array([0.55, 0.65, 0.75]) dims = np.array([16.0, 32.0, 64.0]) reg2 = ds.arbitrary_grid(left_edge, right_edge, dims) assert_equal(reg1["gas", "density"], reg2["gas", "density"]) def test_accessing_all_data(): # This will test first that we can access all_data, and next that we can # access it multiple times and get the *same object*. ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) dd = ds.all_data() assert_equal(ds.r["gas", "density"], dd["gas", "density"]) # Now let's assert that it's the same object rho = ds.r["gas", "density"] rho *= 2.0 assert_equal(dd["gas", "density"] * 2.0, ds.r["gas", "density"]) assert_equal(dd["gas", "density"] * 2.0, ds.r["gas", "density"]) def test_slice_from_r(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) sl1 = ds.r[0.5, :, :] sl2 = ds.slice("x", 0.5) assert_equal(sl1["gas", "density"], sl2["gas", "density"]) frb1 = sl1.to_frb(width=1.0, height=1.0, resolution=(1024, 512)) frb2 = ds.r[0.5, ::1024j, ::512j] assert_equal(frb1["gas", "density"], frb2["gas", "density"]) # Test slice which doesn't cover the whole domain box = ds.box([0.0, 0.25, 0.25], [1.0, 0.75, 0.75]) sl3 = ds.r[0.5, 0.25:0.75, 0.25:0.75] sl4 = ds.slice("x", 0.5, data_source=box) assert_equal(sl3["gas", "density"], sl4["gas", "density"]) frb3 = sl3.to_frb(width=0.5, height=0.5, resolution=(1024, 512)) frb4 = ds.r[0.5, 0.25:0.75:1024j, 0.25:0.75:512j] assert_equal(frb3["gas", "density"], frb4["gas", "density"]) # Test off-center slice offset_box = ds.box([0.0, 0.0, 0.4], [1.0, 0.5, 0.9]) sl5 = ds.r[0.5, 0:0.5, 0.4:0.9] sl6 = ds.slice("x", 0.5, data_source=offset_box) assert_equal(sl5["gas", "density"], sl6["gas", "density"]) frb5 = sl5.to_frb( width=0.5, height=0.5, resolution=(1024, 512), center=(0.5, 0.25, 0.65) ) frb6 = ds.r[0.5, 0.0:0.5:1024j, 0.4:0.9:512j] assert_equal(frb5["gas", "density"], frb6["gas", "density"]) def test_point_from_r(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) pt1 = ds.r[0.5, 0.3, 0.1] pt2 = ds.point([0.5, 0.3, 0.1]) assert_equal(pt1["gas", "density"], pt2["gas", "density"]) # Test YTDimensionalityError with assert_raises(YTDimensionalityError) as ex: ds.r[0.5, 0.1] assert_equal(str(ex.exception), "Dimensionality specified was 2 but we need 3") def test_ray_from_r(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) ray1 = ds.r[(0.1, 0.2, 0.3) : (0.4, 0.5, 0.6)] ray2 = ds.ray((0.1, 0.2, 0.3), (0.4, 0.5, 0.6)) assert_equal(ray1["gas", "density"], ray2["gas", "density"]) ray3 = ds.r[0.5 * ds.domain_left_edge : 0.5 * ds.domain_right_edge] ray4 = ds.ray(0.5 * ds.domain_left_edge, 0.5 * ds.domain_right_edge) assert_equal(ray3["gas", "density"], ray4["gas", "density"]) start = [(0.1, "cm"), 0.2, (0.3, "cm")] end = [(0.5, "cm"), (0.4, "cm"), 0.6] ray5 = ds.r[start:end] start_arr = [ds.quan(0.1, "cm"), ds.quan(0.2, "cm"), ds.quan(0.3, "cm")] end_arr = [ds.quan(0.5, "cm"), ds.quan(0.4, "cm"), ds.quan(0.6, "cm")] ray6 = ds.ray(start_arr, end_arr) assert_equal(ray5["gas", "density"], ray6["gas", "density"]) ray7 = ds.r[start:end:500j] ray8 = LineBuffer(ds, [0.1, 0.2, 0.3], [0.5, 0.4, 0.6], 500) assert_equal(ray7["gas", "density"], ray8["gas", "density"]) def test_ortho_ray_from_r(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) ray1 = ds.r[:, 0.3, 0.2] ray2 = ds.ortho_ray("x", [0.3, 0.2]) assert_equal(ray1["gas", "density"], ray2["gas", "density"]) # the y-coord is funny so test it too ray3 = ds.r[0.3, :, 0.2] ray4 = ds.ortho_ray("y", [0.2, 0.3]) assert_equal(ray3["gas", "density"], ray4["gas", "density"]) # Test ray which doesn't cover the whole domain box = ds.box([0.25, 0.0, 0.0], [0.75, 1.0, 1.0]) ray5 = ds.r[0.25:0.75, 0.3, 0.2] ray6 = ds.ortho_ray("x", [0.3, 0.2], data_source=box) assert_equal(ray5["gas", "density"], ray6["gas", "density"]) # Test fixed-resolution rays ray7 = ds.r[0.25:0.75:100j, 0.3, 0.2] ray8 = LineBuffer(ds, [0.2525, 0.3, 0.2], [0.7475, 0.3, 0.2], 100) assert_equal(ray7["gas", "density"], ray8["gas", "density"]) def test_particle_counts(): ds = fake_random_ds(16, particles=100) assert ds.particle_type_counts == {"io": 100} pds = fake_particle_ds(npart=128) assert pds.particle_type_counts == {"io": 128} g30 = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(g30) def test_checksum(): assert fake_random_ds(16).checksum == "notafile" assert data_dir_load(g30).checksum == "6169536e4b9f737ce3d3ad440df44c58" yt-project-yt-f043ac8/yt/data_objects/tests/test_derived_quantities.py000066400000000000000000000235461510711153200264050ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal import yt from yt import particle_filter from yt.testing import ( assert_rel_equal, fake_particle_ds, fake_random_ds, fake_sph_orientation_ds, requires_file, ) def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_extrema(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds( 16, nprocs=nprocs, fields=("density", "velocity_x", "velocity_y", "velocity_z"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), ) for sp in [ds.sphere("c", (0.25, "unitary")), ds.r[0.5, :, :]]: mi, ma = sp.quantities["Extrema"](("gas", "density")) assert_equal(mi, np.nanmin(sp["gas", "density"])) assert_equal(ma, np.nanmax(sp["gas", "density"])) dd = ds.all_data() mi, ma = dd.quantities["Extrema"](("gas", "density")) assert_equal(mi, np.nanmin(dd["gas", "density"])) assert_equal(ma, np.nanmax(dd["gas", "density"])) sp = ds.sphere("max", (0.25, "unitary")) assert_equal(np.any(np.isnan(sp["gas", "radial_velocity"])), False) mi, ma = dd.quantities["Extrema"](("gas", "radial_velocity")) assert_equal(mi, np.nanmin(dd["gas", "radial_velocity"])) assert_equal(ma, np.nanmax(dd["gas", "radial_velocity"])) def test_extrema_with_nan(): dens = np.ones((16, 16, 16)) dens[0, 0, 0] = np.nan data = {"density": dens} ds = yt.load_uniform_grid(data, data["density"].shape) ad = ds.all_data() mi, ma = ad.quantities.extrema(("stream", "density")) assert np.isnan(mi) assert np.isnan(ma) mi, ma = ad.quantities.extrema(("stream", "density"), check_finite=True) assert mi == 1.0 assert ma == 1.0 def test_average(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs, fields=("density",), units=("g/cm**3",)) for ad in [ds.all_data(), ds.r[0.5, :, :]]: my_mean = ad.quantities["WeightedAverageQuantity"]( ("gas", "density"), ("index", "ones") ) assert_rel_equal(my_mean, ad["gas", "density"].mean(), 12) my_mean = ad.quantities["WeightedAverageQuantity"]( ("gas", "density"), ("gas", "cell_mass") ) a_mean = (ad["gas", "density"] * ad["gas", "cell_mass"]).sum() / ad[ ("gas", "cell_mass") ].sum() assert_rel_equal(my_mean, a_mean, 12) def test_standard_deviation(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs, fields=("density",), units=("g/cm**3",)) for ad in [ds.all_data(), ds.r[0.5, :, :]]: my_std, my_mean = ad.quantities["WeightedStandardDeviation"]( ("gas", "density"), ("index", "ones") ) assert_rel_equal(my_mean, ad["gas", "density"].mean(), 12) assert_rel_equal(my_std, ad["gas", "density"].std(), 12) my_std, my_mean = ad.quantities["WeightedStandardDeviation"]( ("gas", "density"), ("gas", "cell_mass") ) a_mean = (ad["gas", "density"] * ad["gas", "cell_mass"]).sum() / ad[ ("gas", "cell_mass") ].sum() assert_rel_equal(my_mean, a_mean, 12) a_std = np.sqrt( (ad["gas", "cell_mass"] * (ad["gas", "density"] - a_mean) ** 2).sum() / ad["gas", "cell_mass"].sum() ) assert_rel_equal(my_std, a_std, 12) def test_max_location(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs, fields=("density",), units=("g/cm**3",)) for ad in [ds.all_data(), ds.r[0.5, :, :]]: mv, x, y, z = ad.quantities.max_location(("gas", "density")) assert_equal(mv, ad["gas", "density"].max()) mi = np.argmax(ad["gas", "density"]) assert_equal(ad["index", "x"][mi], x) assert_equal(ad["index", "y"][mi], y) assert_equal(ad["index", "z"][mi], z) def test_min_location(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(16, nprocs=nprocs, fields=("density",), units=("g/cm**3",)) for ad in [ds.all_data(), ds.r[0.5, :, :]]: mv, x, y, z = ad.quantities.min_location(("gas", "density")) assert_equal(mv, ad["gas", "density"].min()) mi = np.argmin(ad["gas", "density"]) assert_equal(ad["index", "x"][mi], x) assert_equal(ad["index", "y"][mi], y) assert_equal(ad["index", "z"][mi], z) def test_sample_at_min_field_values(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds( 16, nprocs=nprocs, fields=("density", "temperature", "velocity_x"), units=("g/cm**3", "K", "cm/s"), ) for ad in [ds.all_data(), ds.r[0.5, :, :]]: mv, temp, vm = ad.quantities.sample_at_min_field_values( ("gas", "density"), [("gas", "temperature"), ("gas", "velocity_x")] ) assert_equal(mv, ad["gas", "density"].min()) mi = np.argmin(ad["gas", "density"]) assert_equal(ad["gas", "temperature"][mi], temp) assert_equal(ad["gas", "velocity_x"][mi], vm) def test_sample_at_max_field_values(): for nprocs in [1, 2, 4, 8]: ds = fake_random_ds( 16, nprocs=nprocs, fields=("density", "temperature", "velocity_x"), units=("g/cm**3", "K", "cm/s"), ) for ad in [ds.all_data(), ds.r[0.5, :, :]]: mv, temp, vm = ad.quantities.sample_at_max_field_values( ("gas", "density"), [("gas", "temperature"), ("gas", "velocity_x")] ) assert_equal(mv, ad["gas", "density"].max()) mi = np.argmax(ad["gas", "density"]) assert_equal(ad["gas", "temperature"][mi], temp) assert_equal(ad["gas", "velocity_x"][mi], vm) def test_in_memory_sph_derived_quantities(): ds = fake_sph_orientation_ds() ad = ds.all_data() ang_mom = ad.quantities.angular_momentum_vector() assert_equal(ang_mom, [0, 0, 0]) bv = ad.quantities.bulk_velocity() assert_equal(bv, [0, 0, 0]) com = ad.quantities.center_of_mass() assert_equal(com, [1 / 7, (1 + 2) / 7, (1 + 2 + 3) / 7]) ex = ad.quantities.extrema([("io", "x"), ("io", "y"), ("io", "z")]) for fex, ans in zip(ex, [[0, 1], [0, 2], [0, 3]], strict=True): assert_equal(fex, ans) for d, v, l in [ ("x", 1, [1, 0, 0]), ("y", 2, [0, 2, 0]), ("z", 3, [0, 0, 3]), ]: max_d, x, y, z = ad.quantities.max_location(("io", d)) assert_equal(max_d, v) assert_equal([x, y, z], l) for d in "xyz": min_d, x, y, z = ad.quantities.min_location(("io", d)) assert_equal(min_d, 0) assert_equal([x, y, z], [0, 0, 0]) tot_m = ad.quantities.total_mass() assert_equal(tot_m, [7, 0]) weighted_av_z = ad.quantities.weighted_average_quantity(("io", "z"), ("io", "z")) assert_equal(weighted_av_z, 7 / 3) iso_collapse = "IsothermalCollapse/snap_505" tipsy_gal = "TipsyGalaxy/galaxy.00300" @requires_file(iso_collapse) @requires_file(tipsy_gal) def test_sph_datasets_derived_quantities(): for fname in [tipsy_gal, iso_collapse]: ds = yt.load(fname) ad = ds.all_data() use_particles = "nbody" in ds.particle_types ad.quantities.angular_momentum_vector() ad.quantities.bulk_velocity(True, use_particles) ad.quantities.center_of_mass(True, use_particles) ad.quantities.extrema([("gas", "density"), ("gas", "temperature")]) ad.quantities.min_location(("gas", "density")) ad.quantities.max_location(("gas", "density")) ad.quantities.total_mass() ad.quantities.weighted_average_quantity(("gas", "density"), ("gas", "mass")) def test_derived_quantities_with_particle_types(): ds = fake_particle_ds() @particle_filter(requires=["particle_position_x"], filtered_type="all") def low_x(pfilter, data): return ( data[pfilter.filtered_type, "particle_position_x"].in_units("code_length") < 0.5 ) ds.add_particle_filter("low_x") ad = ds.all_data() for ptype in ["all", "low_x"]: # Check bulk velocity bulk_vx = ( ad[ptype, "particle_mass"] * ad[ptype, "particle_velocity_x"] / ad[ptype, "particle_mass"].sum() ).sum() assert_almost_equal( ad.quantities.bulk_velocity( use_gas=False, use_particles=True, particle_type=ptype )[0], bulk_vx, 5, ) # Check center of mass com_x = ( ad[ptype, "particle_mass"] * ad[ptype, "particle_position_x"] / ad[ptype, "particle_mass"].sum() ).sum() assert_almost_equal( ad.quantities.center_of_mass( use_gas=False, use_particles=True, particle_type=ptype )[0], com_x, 5, ) # Check angular momentum vector l_x = ( ad[ptype, "particle_specific_angular_momentum_x"] * ad[ptype, "particle_mass"] / ad[ptype, "particle_mass"].sum() ).sum() assert_almost_equal( ad.quantities.angular_momentum_vector( use_gas=False, use_particles=True, particle_type=ptype )[0], l_x, 5, ) # Check spin parameter values assert_almost_equal( ad.quantities.spin_parameter(use_gas=False, use_particles=True), 655.7311454765503, ) assert_almost_equal( ad.quantities.spin_parameter( use_gas=False, use_particles=True, particle_type="low_x" ), 1309.164886405665, ) yt-project-yt-f043ac8/yt/data_objects/tests/test_disks.py000066400000000000000000000034761510711153200236320ustar00rootroot00000000000000import pytest from yt import YTQuantity from yt.testing import fake_random_ds def test_bad_disk_input(): # Fixes 1768 ds = fake_random_ds(16) # Test invalid 3d array with pytest.raises( TypeError, match=r"^Expected an array of size \(3,\), received 'list' of length 4$", ): ds.disk(ds.domain_center, [0, 0, 1, 1], (10, "kpc"), (20, "kpc")) # Test invalid float with pytest.raises( TypeError, match=( r"^Expected a numeric value \(or size-1 array\), " r"received 'unyt.array.unyt_array' of length 3$" ), ): ds.disk(ds.domain_center, [0, 0, 1], ds.domain_center, (20, "kpc")) # Test invalid float with pytest.raises( TypeError, match=( r"^Expected a numeric value \(or tuple of format \(float, String\)\), " r"received an inconsistent tuple '\(10, 10\)'.$" ), ): ds.disk(ds.domain_center, [0, 0, 1], (10, 10), (20, "kpc")) # Test invalid iterable with pytest.raises( TypeError, match=r"^Expected an iterable object, received 'unyt\.array\.unyt_quantity'$", ): ds.disk( ds.domain_center, [0, 0, 1], (10, "kpc"), (20, "kpc"), fields=YTQuantity(1, "kpc"), ) # Test invalid object with pytest.raises( TypeError, match=( r"^Expected an object of 'yt\.data_objects\.static_output\.Dataset' type, " r"received 'yt\.data_objects\.selection_objects\.region\.YTRegion'$" ), ): ds.disk(ds.domain_center, [0, 0, 1], (10, "kpc"), (20, "kpc"), ds=ds.all_data()) # Test valid disk ds.disk(ds.domain_center, [0, 0, 1], (10, "kpc"), (20, "kpc")) ds.disk(ds.domain_center, [0, 0, 1], 10, (20, "kpc")) yt-project-yt-f043ac8/yt/data_objects/tests/test_ellipsoid.py000066400000000000000000000041401510711153200244660ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_less from yt.testing import fake_random_ds def setup_module(): from yt.config import ytcfg ytcfg["yt", "log_level"] = 50 ytcfg["yt", "internals", "within_testing"] = True def _difference(x1, x2, dw): rel = x1 - x2 rel[rel > dw / 2.0] -= dw rel[rel < -dw / 2.0] += dw return rel def test_ellipsoid(): # We decompose in different ways cs = [ np.array([0.5, 0.5, 0.5]), np.array([0.1, 0.2, 0.3]), np.array([0.8, 0.8, 0.8]), ] np.random.seed(0x4D3D3D3) for nprocs in [1, 2, 4, 8]: ds = fake_random_ds(64, nprocs=nprocs) DW = ds.domain_right_edge - ds.domain_left_edge min_dx = 2.0 / ds.domain_dimensions ABC = np.random.random((3, 12)) * 0.1 e0s = np.random.random((3, 12)) tilts = np.random.random(12) ABC[:, 0] = 0.1 for i in range(12): for c in cs: A, B, C = sorted(ABC[:, i], reverse=True) A = max(A, min_dx[0]) B = max(B, min_dx[1]) C = max(C, min_dx[2]) e0 = e0s[:, i] tilt = tilts[i] ell = ds.ellipsoid(c, A, B, C, e0, tilt) assert_array_less(ell["index", "radius"], A) p = np.array([ell["index", ax] for ax in "xyz"]) dot_evec = [np.zeros_like(ell["index", "radius"]) for i in range(3)] vecs = [ell._e0, ell._e1, ell._e2] mags = [ell._A, ell._B, ell._C] my_c = np.array([c] * p.shape[1]).transpose() dot_evec = [de.to_ndarray() for de in dot_evec] mags = [m.to_ndarray() for m in mags] for ax_i in range(3): dist = _difference(p[ax_i, :], my_c[ax_i, :], DW[ax_i]) for ax_j in range(3): dot_evec[ax_j] += dist * vecs[ax_j][ax_i] dist = 0 for ax_i in range(3): dist += dot_evec[ax_i] ** 2.0 / mags[ax_i] ** 2.0 assert_array_less(dist, 1.0) yt-project-yt-f043ac8/yt/data_objects/tests/test_exclude_functions.py000066400000000000000000000072461510711153200262350ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.loaders import load_uniform_grid from yt.testing import fake_random_ds def test_exclude_above(): the_ds = fake_random_ds(ndims=3) all_data = the_ds.all_data() new_ds = all_data.exclude_above(("gas", "density"), 1) assert_equal(new_ds["gas", "density"], all_data["gas", "density"]) new_ds = all_data.exclude_above(("gas", "density"), 1e6, "g/m**3") assert_equal(new_ds["gas", "density"], all_data["gas", "density"]) new_ds = all_data.exclude_above(("gas", "density"), 0) assert_equal(new_ds["gas", "density"], []) def test_exclude_below(): the_ds = fake_random_ds(ndims=3) all_data = the_ds.all_data() new_ds = all_data.exclude_below(("gas", "density"), 1) assert_equal(new_ds["gas", "density"], []) new_ds = all_data.exclude_below(("gas", "density"), 1e6, "g/m**3") assert_equal(new_ds["gas", "density"], []) new_ds = all_data.exclude_below(("gas", "density"), 0) assert_equal(new_ds["gas", "density"], all_data["gas", "density"]) def test_exclude_nan(): test_array = np.nan * np.ones((10, 10, 10)) test_array[1, 1, :] = 1 data = {"density": test_array} ds = load_uniform_grid(data, test_array.shape, length_unit="cm", nprocs=1) ad = ds.all_data() no_nan_ds = ad.exclude_nan(("gas", "density")) assert_equal(no_nan_ds["gas", "density"], np.array(np.ones(10))) def test_equal(): test_array = np.ones((10, 10, 10)) test_array[1, 1, :] = 2.0 test_array[2, 1, :] = 3.0 data = {"density": test_array} ds = load_uniform_grid(data, test_array.shape, length_unit="cm", nprocs=1) ad = ds.all_data() no_ones = ad.exclude_equal(("gas", "density"), 1.0) assert np.all(no_ones["gas", "density"] != 1.0) only_ones = ad.include_equal(("gas", "density"), 1.0) assert np.all(only_ones["gas", "density"] == 1.0) def test_inside_outside(): test_array = np.ones((10, 10, 10)) test_array[1, 1, :] = 2.0 test_array[2, 1, :] = 3.0 data = {"density": test_array} ds = load_uniform_grid(data, test_array.shape, length_unit="cm", nprocs=1) ad = ds.all_data() only_ones_and_twos = ad.include_inside(("gas", "density"), 0.9, 2.1) assert np.all(only_ones_and_twos["gas", "density"] != 3.0) assert len(only_ones_and_twos["gas", "density"]) == 990 only_ones_and_twos = ad.exclude_outside(("gas", "density"), 0.9, 2.1) assert len(only_ones_and_twos["gas", "density"]) == 990 assert np.all(only_ones_and_twos["gas", "density"] != 3.0) only_threes = ad.include_outside(("gas", "density"), 0.9, 2.1) assert np.all(only_threes["gas", "density"] == 3) assert len(only_threes["gas", "density"]) == 10 only_threes = ad.include_outside(("gas", "density"), 0.9, 2.1) assert np.all(only_threes["gas", "density"] == 3) assert len(only_threes["gas", "density"]) == 10 # Repeat, but convert units to g/m**3 only_ones_and_twos = ad.include_inside(("gas", "density"), 0.9e6, 2.1e6, "g/m**3") assert np.all(only_ones_and_twos["gas", "density"] != 3.0) assert len(only_ones_and_twos["gas", "density"]) == 990 only_ones_and_twos = ad.exclude_outside(("gas", "density"), 0.9e6, 2.1e6, "g/m**3") assert len(only_ones_and_twos["gas", "density"]) == 990 assert np.all(only_ones_and_twos["gas", "density"] != 3.0) only_threes = ad.include_outside(("gas", "density"), 0.9e6, 2.1e6, "g/m**3") assert np.all(only_threes["gas", "density"] == 3) assert len(only_threes["gas", "density"]) == 10 only_threes = ad.include_outside(("gas", "density"), 0.9e6, 2.1e6, "g/m**3") assert np.all(only_threes["gas", "density"] == 3) assert len(only_threes["gas", "density"]) == 10 yt-project-yt-f043ac8/yt/data_objects/tests/test_extract_regions.py000066400000000000000000000073751510711153200257170ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.loaders import load from yt.testing import ( fake_amr_ds, fake_random_ds, requires_file, requires_module, ) def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_cut_region(): # We decompose in different ways for nprocs in [1, 2, 4, 8]: ds = fake_random_ds( 64, nprocs=nprocs, fields=("density", "temperature", "velocity_x"), units=("g/cm**3", "K", "cm/s"), ) # We'll test two objects dd = ds.all_data() r = dd.cut_region( [ "obj['gas', 'temperature'] > 0.5", "obj['gas', 'density'] < 0.75", "obj['gas', 'velocity_x'] > 0.25", ] ) t = ( (dd["gas", "temperature"] > 0.5) & (dd["gas", "density"] < 0.75) & (dd["gas", "velocity_x"] > 0.25) ) assert_equal(np.all(r["gas", "temperature"] > 0.5), True) assert_equal(np.all(r["gas", "density"] < 0.75), True) assert_equal(np.all(r["gas", "velocity_x"] > 0.25), True) assert_equal(np.sort(dd["gas", "density"][t]), np.sort(r["gas", "density"])) assert_equal(np.sort(dd["index", "x"][t]), np.sort(r["index", "x"])) r2 = r.cut_region(["obj['gas', 'temperature'] < 0.75"]) t2 = r["gas", "temperature"] < 0.75 assert_equal( np.sort(r2["gas", "temperature"]), np.sort(r["gas", "temperature"][t2]) ) assert_equal(np.all(r2["gas", "temperature"] < 0.75), True) # Now we can test some projections dd = ds.all_data() cr = dd.cut_region(["obj['index', 'ones'] > 0"]) for weight in [None, ("gas", "density")]: p1 = ds.proj(("gas", "density"), 0, data_source=dd, weight_field=weight) p2 = ds.proj(("gas", "density"), 0, data_source=cr, weight_field=weight) for f in p1.field_data: assert_almost_equal(p1[f], p2[f]) cr = dd.cut_region(["obj['gas', 'density'] > 0.25"]) p2 = ds.proj(("gas", "density"), 2, data_source=cr) assert_equal(p2["gas", "density"].max() > 0.25, True) p2 = ds.proj( ("gas", "density"), 2, data_source=cr, weight_field=("gas", "density") ) assert_equal(p2["gas", "density"].max() > 0.25, True) def test_region_and_particles(): ds = fake_amr_ds(particles=10000) ad = ds.all_data() reg = ad.cut_region('obj["index", "x"] < .5') mask = ad["all", "particle_position_x"] < 0.5 expected = np.sort(ad["all", "particle_position_x"][mask].value) result = np.sort(reg["all", "particle_position_x"]) assert_equal(expected.shape, result.shape) assert_equal(expected, result) ISOGAL = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(ISOGAL) def test_region_chunked_read(): # see #2104 ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") sp = ds.sphere((0.5, 0.5, 0.5), (2, "kpc")) dense_sp = sp.cut_region(['obj["gas", "H_p0_number_density"]>= 1e-2']) dense_sp.quantities.angular_momentum_vector() @requires_module("h5py") @requires_file(ISOGAL) def test_chained_cut_region(): # see Issue #2233 ds = load(ISOGAL) base = ds.disk([0.5, 0.5, 0.5], [0, 0, 1], (4, "kpc"), (10, "kpc")) c1 = "(obj['index', 'cylindrical_radius'].in_units('kpc') > 2.0)" c2 = "(obj['gas', 'density'].to('g/cm**3') > 1e-26)" cr12 = base.cut_region([c1, c2]) cr1 = base.cut_region([c1]) cr12c = cr1.cut_region([c2]) field = ("index", "cell_volume") assert_equal( cr12.quantities.total_quantity(field), cr12c.quantities.total_quantity(field) ) yt-project-yt-f043ac8/yt/data_objects/tests/test_firefly.py000066400000000000000000000207621510711153200241520ustar00rootroot00000000000000import numpy as np import pytest from numpy.testing import assert_array_equal from yt.testing import fake_particle_ds, requires_module from yt.utilities.exceptions import YTFieldNotFound @requires_module("firefly") def test_firefly_JSON_string(): ds = fake_particle_ds() ad = ds.all_data() reader = ad.create_firefly_object( None, velocity_units="cm/s", coordinate_units="cm", ) reader.writeToDisk(write_to_disk=False, file_extension=".json") ## reader.JSON was not output to string correctly ## either Firefly is damaged or needs a hotfix-- try reinstalling. ## if that doesn't work contact the developers ## at github.com/ageller/Firefly/issues. assert len(reader.JSON) > 0 @requires_module("firefly") def test_firefly_write_to_disk(tmp_path): tmpdir = str(tmp_path) # create_firefly_object needs a str, not PosixPath ds = fake_particle_ds() ad = ds.all_data() reader = ad.create_firefly_object( tmpdir, velocity_units="cm/s", coordinate_units="cm", match_any_particle_types=True, # Explicitly specifying to avoid deprecation warning ) reader.writeToDisk() @pytest.fixture def firefly_test_dataset(): # create dataset ds_fields = [ # Assumed present ("pt1", "particle_position_x"), ("pt1", "particle_position_y"), ("pt1", "particle_position_z"), ("pt2", "particle_position_x"), ("pt2", "particle_position_y"), ("pt2", "particle_position_z"), # User input ("pt1", "common_field"), ("pt2", "common_field"), ("pt2", "pt2only_field"), ] ds_field_units = ["code_length"] * 9 ds_negative = [0] * 9 ds = fake_particle_ds( fields=ds_fields, units=ds_field_units, negative=ds_negative, ) return ds @requires_module("firefly") @pytest.mark.parametrize( "fields_to_include,fields_units", [ (None, None), # Test default values ([], []), # Test empty fields ], ) def test_field_empty_specification( firefly_test_dataset, fields_to_include, fields_units ): dd = firefly_test_dataset.all_data() reader = dd.create_firefly_object( fields_to_include=fields_to_include, fields_units=fields_units, coordinate_units="code_length", ) assert_array_equal( dd["pt1", "relative_particle_position"].d, reader.particleGroups[0].coordinates, ) assert_array_equal( dd["pt2", "relative_particle_position"].d, reader.particleGroups[1].coordinates, ) @requires_module("firefly") def test_field_unique_string_specification(firefly_test_dataset): # Test unique field (pt2only_field) dd = firefly_test_dataset.all_data() # Unique field string will fallback to "all" field type and fail # as nonexistent ("all", "pt2only_field") unless we set # match_any_particle_types=True reader = dd.create_firefly_object( fields_to_include=["pt2only_field"], fields_units=["code_length"], coordinate_units="code_length", match_any_particle_types=True, ) pt1 = reader.particleGroups[0] pt2 = reader.particleGroups[1] assert_array_equal( dd["pt1", "relative_particle_position"].d, pt1.coordinates, ) assert_array_equal( dd["pt2", "relative_particle_position"].d, pt2.coordinates, ) assert "pt2only_field" not in pt1.field_names assert "pt2only_field" in pt2.field_names arrind = np.flatnonzero(pt2.field_names == "pt2only_field")[0] assert_array_equal(dd["pt2", "pt2only_field"].d, pt2.field_arrays[arrind]) @requires_module("firefly") def test_field_common_string_specification(firefly_test_dataset): # Test common field (common_field) dd = firefly_test_dataset.all_data() # Common field string will be ambiguous and fail # unless we set match_any_particle_types=True reader = dd.create_firefly_object( fields_to_include=["common_field"], fields_units=["code_length"], coordinate_units="code_length", match_any_particle_types=True, ) pt1 = reader.particleGroups[0] pt2 = reader.particleGroups[1] assert_array_equal( dd["pt1", "relative_particle_position"].d, pt1.coordinates, ) assert_array_equal( dd["pt2", "relative_particle_position"].d, pt2.coordinates, ) assert "common_field" in pt1.field_names assert "common_field" in pt2.field_names arrind = np.flatnonzero(pt1.field_names == "common_field")[0] assert_array_equal(dd["pt1", "common_field"].d, pt1.field_arrays[arrind]) arrind = np.flatnonzero(pt2.field_names == "common_field")[0] assert_array_equal(dd["pt2", "common_field"].d, pt2.field_arrays[arrind]) @requires_module("firefly") @pytest.mark.parametrize( "fields_to_include,fields_units", [ ( [("pt2", "pt2only_field")], ["code_length"], ), # Test existing field tuple (pt2, pt2only_field) ( [("pt1", "common_field")], ["code_length"], ), # Test that tuples only bring in referenced particleGroup ( [("all", "common_field")], ["code_length"], ), # Test that "all" brings in all particleGroups ], ) def test_field_tuple_specification( firefly_test_dataset, fields_to_include, fields_units, ): dd = firefly_test_dataset.all_data() reader = dd.create_firefly_object( fields_to_include=fields_to_include, fields_units=fields_units, coordinate_units="code_length", ) assert_array_equal( dd["pt1", "relative_particle_position"].d, reader.particleGroups[0].coordinates, ) assert_array_equal( dd["pt2", "relative_particle_position"].d, reader.particleGroups[1].coordinates, ) all_pgs = reader.particleGroups all_pgs_names = ["pt1", "pt2"] for field in fields_to_include: ftype, fname = field for pgi in range(2): pg = all_pgs[pgi] if ftype == all_pgs_names[pgi]: assert fname in pg.field_names arrind = np.flatnonzero(pg.field_names == fname)[0] assert_array_equal(dd[field].d, pg.field_arrays[arrind]) elif ftype == "all": assert fname in pg.field_names this_pg_name = all_pgs_names[pgi] arrind = np.flatnonzero(pg.field_names == fname)[0] assert_array_equal(dd[this_pg_name, fname].d, pg.field_arrays[arrind]) else: assert fname not in pg.field_names @requires_module("firefly") @pytest.mark.parametrize( "fields_to_include,fields_units,ErrorType", [ ( ["dinos"], ["code_length"], YTFieldNotFound, ), # Test nonexistent field (dinos) ( ["common_field"], ["code_length"], ValueError, ), # Test ambiguous field (match_any_particle_types=False) ( [("pt1", "pt2only_field")], ["code_length"], YTFieldNotFound, ), # Test nonexistent field tuple (pt1, pt2only_field) ], ) def test_field_invalid_specification( firefly_test_dataset, fields_to_include, fields_units, ErrorType ): dd = firefly_test_dataset.all_data() # Note that we have specified match_any_particle_types as False since # that is the behavior expected in the future with pytest.raises(ErrorType): dd.create_firefly_object( fields_to_include=fields_to_include, fields_units=fields_units, coordinate_units="code_length", match_any_particle_types=False, ) @requires_module("firefly") def test_field_mixed_specification(firefly_test_dataset): dd = firefly_test_dataset.all_data() reader = dd.create_firefly_object( fields_to_include=["pt2only_field", ("pt1", "common_field")], fields_units=["code_length", "code_length"], ) pt1 = reader.particleGroups[0] pt2 = reader.particleGroups[1] assert "common_field" in pt1.field_names assert "common_field" not in pt2.field_names arrind = np.flatnonzero(pt1.field_names == "common_field")[0] assert_array_equal(dd["pt1", "common_field"].d, pt1.field_arrays[arrind]) assert "pt2only_field" not in pt1.field_names assert "pt2only_field" in pt2.field_names arrind = np.flatnonzero(pt2.field_names == "pt2only_field")[0] assert_array_equal(dd["pt2", "pt2only_field"].d, pt2.field_arrays[arrind]) yt-project-yt-f043ac8/yt/data_objects/tests/test_fluxes.py000066400000000000000000000121731510711153200240150ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.testing import fake_random_ds def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_flux_calculation(): ds = fake_random_ds(64, nprocs=4) dd = ds.all_data() surf = ds.surface(dd, ("index", "x"), 0.51) assert_equal(surf["index", "x"], 0.51) flux = surf.calculate_flux( ("index", "ones"), ("index", "zeros"), ("index", "zeros"), ("index", "ones") ) assert_almost_equal(flux.value, 1.0, 12) assert_equal(str(flux.units), "cm**2") flux2 = surf.calculate_flux( ("index", "ones"), ("index", "zeros"), ("index", "zeros") ) assert_almost_equal(flux2.value, 1.0, 12) assert_equal(str(flux2.units), "cm**2") def test_sampling(): ds = fake_random_ds(64, nprocs=4) dd = ds.all_data() for i, ax in enumerate([("index", "x"), ("index", "y"), ("index", "z")]): surf = ds.surface(dd, ax, 0.51) surf.get_data(ax, sample_type="vertex") assert_equal(surf.vertex_samples[ax], surf.vertices[i, :]) assert_equal(str(surf.vertices.units), "code_length") dens = surf["gas", "density"] vert_shape = surf.vertices.shape assert_equal(dens.shape[0], vert_shape[1] // vert_shape[0]) assert_equal(str(dens.units), "g/cm**3") class ExporterTests(TestCase): def setUp(self): self.curdir = os.getcwd() self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_export_ply(self): ds = fake_random_ds(64, nprocs=4) dd = ds.all_data() surf = ds.surface(dd, ("index", "x"), 0.51) surf.export_ply("my_ply.ply", bounds=[(0, 1), (0, 1), (0, 1)]) assert os.path.exists("my_ply.ply") surf.export_ply( "my_ply2.ply", bounds=[(0, 1), (0, 1), (0, 1)], sample_type="vertex", color_field=("gas", "density"), ) assert os.path.exists("my_ply2.ply") def test_export_obj(self): ds = fake_random_ds( 16, nprocs=4, particles=16**3, fields=("density", "temperature"), units=("g/cm**3", "K"), ) sp = ds.sphere("max", (1.0, "cm")) surf = ds.surface(sp, ("gas", "density"), 0.5) surf.export_obj("my_galaxy", transparency=1.0, dist_fac=1.0) assert os.path.exists("my_galaxy.obj") assert os.path.exists("my_galaxy.mtl") mi, ma = sp.quantities.extrema(("gas", "temperature")) rhos = [0.5, 0.25] trans = [0.5, 1.0] for i, r in enumerate(rhos): basename = "my_galaxy_color" surf = ds.surface(sp, ("gas", "density"), r) surf.export_obj( basename, transparency=trans[i], color_field=("gas", "temperature"), dist_fac=1.0, plot_index=i, color_field_max=ma, color_field_min=mi, ) assert os.path.exists(f"{basename}.obj") assert os.path.exists(f"{basename}.mtl") def _Emissivity(field, data): return ( data["gas", "density"] * data["gas", "density"] * np.sqrt(data["gas", "temperature"]) ) ds.add_field( ("gas", "emissivity"), sampling_type="cell", function=_Emissivity, units=r"g**2*sqrt(K)/cm**6", ) for i, r in enumerate(rhos): basename = "my_galaxy_emis" surf = ds.surface(sp, ("gas", "density"), r) surf.export_obj( basename, transparency=trans[i], color_field=("gas", "temperature"), emit_field=("gas", "emissivity"), dist_fac=1.0, plot_index=i, ) basename = "my_galaxy_emis" assert os.path.exists(f"{basename}.obj") assert os.path.exists(f"{basename}.mtl") def test_correct_output_unit_fake_ds(): # see issue #1368 ds = fake_random_ds(64, nprocs=4, particles=16**3) x = y = z = 0.5 sp1 = ds.sphere((x, y, z), (300, "kpc")) Nmax = sp1.max(("gas", "density")) sur = ds.surface(sp1, ("gas", "density"), 0.5 * Nmax) sur["index", "x"][0] def test_radius_surface(): # see #1407 ds = fake_random_ds(64, nprocs=4, particles=16**3, length_unit=10.0) reg = ds.all_data() sp = ds.sphere(ds.domain_center, (0.5, "code_length")) for obj in [reg, sp]: for rad in [0.05, 0.1, 0.4]: surface = ds.surface(obj, ("index", "radius"), (rad, "code_length")) assert_almost_equal(surface.surface_area.v, 4 * np.pi * rad**2, decimal=2) verts = surface.vertices for i in range(3): assert_almost_equal(verts[i, :].min().v, 0.5 - rad, decimal=2) assert_almost_equal(verts[i, :].max().v, 0.5 + rad, decimal=2) yt-project-yt-f043ac8/yt/data_objects/tests/test_glue.py000066400000000000000000000010401510711153200234320ustar00rootroot00000000000000import pytest from yt.config import ytcfg from yt.testing import fake_random_ds, requires_module_pytest as requires_module @pytest.fixture def within_testing(): old_value = ytcfg["yt", "internals", "within_testing"] ytcfg["yt", "internals", "within_testing"] = True yield ytcfg["yt", "internals", "within_testing"] = old_value @pytest.mark.usefixtures("within_testing") @requires_module("glue", "astropy") def test_glue_data_object(): ds = fake_random_ds(16) ad = ds.all_data() ad.to_glue([("gas", "density")]) yt-project-yt-f043ac8/yt/data_objects/tests/test_image_array.py000066400000000000000000000111211510711153200247570ustar00rootroot00000000000000import os import shutil import tempfile import unittest import numpy as np from numpy.testing import assert_equal from yt.data_objects.image_array import ImageArray from yt.testing import requires_module old_settings = None def setup_module(): global old_settings from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True old_settings = np.geterr() np.seterr(all="ignore") def teardown_module(): np.seterr(**old_settings) def dummy_image(kstep, nlayers): im = np.zeros([64, 128, nlayers]) for i in range(im.shape[0]): for k in range(im.shape[2]): im[i, :, k] = np.linspace(0.0, kstep * k, im.shape[1]) return im def test_rgba_rescale(): im_arr = ImageArray(dummy_image(10.0, 4)) new_im = im_arr.rescale(inline=False) assert_equal(im_arr[:, :, :3].max(), 2 * 10.0) assert_equal(im_arr[:, :, 3].max(), 3 * 10.0) assert_equal(new_im[:, :, :3].sum(axis=2).max(), 1.0) assert_equal(new_im[:, :, 3].max(), 1.0) im_arr.rescale() assert_equal(im_arr[:, :, :3].sum(axis=2).max(), 1.0) assert_equal(im_arr[:, :, 3].max(), 1.0) im_arr.rescale(cmax=0.0, amax=0.0) assert_equal(im_arr[:, :, :3].sum(axis=2).max(), 1.0) assert_equal(im_arr[:, :, 3].max(), 1.0) class TestImageArray(unittest.TestCase): tmpdir = None curdir = None def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def test_image_arry_units(self): im_arr = ImageArray(dummy_image(0.3, 3), units="cm") assert str(im_arr.units) == "cm" new_im = im_arr.in_units("km") assert str(new_im.units) == "km" @requires_module("h5py") def test_image_array_hdf5(self): myinfo = { "field": "dinosaurs", "east_vector": np.array([1.0, 0.0, 0.0]), "north_vector": np.array([0.0, 0.0, 1.0]), "normal_vector": np.array([0.0, 1.0, 0.0]), "width": 0.245, "type": "rendering", } im_arr = ImageArray(dummy_image(0.3, 3), units="cm", info=myinfo) im_arr.save("test_3d_ImageArray", png=False) im = np.zeros([64, 128]) for i in range(im.shape[0]): im[i, :] = np.linspace(0.0, 0.3 * 2, im.shape[1]) myinfo = { "field": "dinosaurs", "east_vector": np.array([1.0, 0.0, 0.0]), "north_vector": np.array([0.0, 0.0, 1.0]), "normal_vector": np.array([0.0, 1.0, 0.0]), "width": 0.245, "type": "rendering", } im_arr = ImageArray(im, info=myinfo, units="cm") im_arr.save("test_2d_ImageArray", png=False) im_arr.save("test_2d_ImageArray_ds", png=False, dataset_name="Random_DS") def test_image_array_rgb_png(self): im = np.zeros([64, 128]) for i in range(im.shape[0]): im[i, :] = np.linspace(0.0, 0.3 * 2, im.shape[1]) im_arr = ImageArray(im) im_arr.save("standard-image", hdf5=False) im_arr = ImageArray(dummy_image(10.0, 3)) im_arr.save("standard-png", hdf5=False) def test_image_array_rgba_png(self): im_arr = ImageArray(dummy_image(10.0, 4)) im_arr.write_png("standard") im_arr.write_png("non-scaled.png", rescale=False) im_arr.write_png("black_bg.png", background="black") im_arr.write_png("white_bg.png", background="white") im_arr.write_png("green_bg.png", background=[0.0, 1.0, 0.0, 1.0]) im_arr.write_png("transparent_bg.png", background=None) def test_image_array_background(self): im_arr = ImageArray(dummy_image(10.0, 4)) im_arr.rescale() new_im = im_arr.add_background_color([1.0, 0.0, 0.0, 1.0], inline=False) new_im.write_png("red_bg.png") im_arr.add_background_color("black") im_arr.write_png("black_bg2.png") def test_write_image(self): im_arr = ImageArray(dummy_image(10.0, 4)) im_arr.write_image("with_cmap", cmap_name="hot") im_arr.write_image("channel_1.png", channel=1) def test_clipping_value(self): im_arr = ImageArray(dummy_image(10.0, 4)) clip_val1 = im_arr._clipping_value(1) clip_val2 = im_arr._clipping_value(1, im=im_arr) assert clip_val2 == clip_val1 clip_val3 = im_arr._clipping_value(6) assert clip_val3 > clip_val2 im_arr[:] = 1.0 # std will be 0, mean will be 1, so clip value will be 1 assert im_arr._clipping_value(1) == 1.0 def tearDown(self): os.chdir(self.curdir) # clean up shutil.rmtree(self.tmpdir) yt-project-yt-f043ac8/yt/data_objects/tests/test_io_geometry.py000066400000000000000000000026141510711153200250300ustar00rootroot00000000000000import os from tempfile import TemporaryDirectory import numpy as np from yt.frontends.ytdata.api import save_as_dataset from yt.frontends.ytdata.data_structures import YTDataContainerDataset from yt.loaders import load from yt.testing import fake_amr_ds, requires_module from yt.units import YTQuantity @requires_module("h5py") def test_preserve_geometric_properties(): for geom in ("cartesian", "cylindrical", "spherical"): ds1 = fake_amr_ds(fields=[("gas", "density")], units=["g/cm**3"], geometry=geom) ad = ds1.all_data() with TemporaryDirectory() as tmpdir: tmpf = os.path.join(tmpdir, "savefile.h5") fn = ad.save_as_dataset(tmpf, fields=[("gas", "density")]) ds2 = load(fn) assert isinstance(ds2, YTDataContainerDataset) dfl = ds2.derived_field_list assert ds1.geometry == ds2.geometry == geom expected = set(ds1.coordinates.axis_order) actual = {fname for ftype, fname in dfl} assert expected.difference(actual) == set() @requires_module("h5py") def test_default_to_cartesian(): data = {"density": np.random.random(128)} ds_attrs = {"current_time": YTQuantity(10, "Myr")} with TemporaryDirectory() as tmpdir: tmpf = os.path.join(tmpdir, "savefile.h5") fn = save_as_dataset(ds_attrs, tmpf, data) ds2 = load(fn) assert ds2.geometry == "cartesian" yt-project-yt-f043ac8/yt/data_objects/tests/test_numpy_ops.py000066400000000000000000000152451510711153200245430ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.testing import fake_amr_ds, fake_random_ds def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_mean_sum_integrate(): for nprocs in [-1, 1, 2, 16]: if nprocs == -1: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), particles=20) else: ds = fake_random_ds( 32, nprocs=nprocs, fields=("density",), units=("g/cm**3",), particles=20 ) ad = ds.all_data() # Sums q = ad.sum(("gas", "density")) q1 = ad.quantities.total_quantity(("gas", "density")) assert_equal(q, q1) q = ad.sum(("all", "particle_ones")) q1 = ad.quantities.total_quantity(("all", "particle_ones")) assert_equal(q, q1) # Weighted Averages w = ad.mean(("gas", "density")) w1 = ad.quantities.weighted_average_quantity( ("gas", "density"), ("index", "ones") ) assert_equal(w, w1) w = ad.mean(("gas", "density"), weight=("gas", "density")) w1 = ad.quantities.weighted_average_quantity( ("gas", "density"), ("gas", "density") ) assert_equal(w, w1) w = ad.mean(("all", "particle_mass")) w1 = ad.quantities.weighted_average_quantity( ("all", "particle_mass"), ("all", "particle_ones") ) assert_equal(w, w1) w = ad.mean(("all", "particle_mass"), weight=("all", "particle_mass")) w1 = ad.quantities.weighted_average_quantity( ("all", "particle_mass"), ("all", "particle_mass") ) assert_equal(w, w1) # Projections p = ad.sum(("gas", "density"), axis=0) p1 = ds.proj(("gas", "density"), 0, data_source=ad, method="sum") assert_equal(p["gas", "density"], p1["gas", "density"]) # Check by axis-name p = ad.sum(("gas", "density"), axis="x") assert_equal(p["gas", "density"], p1["gas", "density"]) # Now we check proper projections p = ad.integrate(("gas", "density"), axis=0) p1 = ds.proj(("gas", "density"), 0, data_source=ad) assert_equal(p["gas", "density"], p1["gas", "density"]) # Check by axis-name p = ad.integrate(("gas", "density"), axis="x") assert_equal(p["gas", "density"], p1["gas", "density"]) def test_min_max(): for nprocs in [-1, 1, 2, 16]: fields = ["density", "temperature"] units = ["g/cm**3", "K"] if nprocs == -1: ds = fake_amr_ds(fields=fields, units=units, particles=20) else: ds = fake_random_ds( 32, nprocs=nprocs, fields=fields, units=units, particles=20 ) ad = ds.all_data() q = ad.min(("gas", "density")).v assert_equal(q, ad["gas", "density"].min()) q = ad.max(("gas", "density")).v assert_equal(q, ad["gas", "density"].max()) q = ad.min(("all", "particle_mass")).v assert_equal(q, ad["all", "particle_mass"].min()) q = ad.max(("all", "particle_mass")).v assert_equal(q, ad["all", "particle_mass"].max()) ptp = ad.ptp(("gas", "density")).v assert_equal(ptp, ad["gas", "density"].max() - ad["gas", "density"].min()) ptp = ad.ptp(("all", "particle_mass")).v assert_equal( ptp, ad["all", "particle_mass"].max() - ad["all", "particle_mass"].min() ) p = ad.max(("gas", "density"), axis=1) p1 = ds.proj(("gas", "density"), 1, data_source=ad, method="max") assert_equal(p["gas", "density"], p1["gas", "density"]) p = ad.min(("gas", "density"), axis=1) p1 = ds.proj(("gas", "density"), 1, data_source=ad, method="min") assert_equal(p["gas", "density"], p1["gas", "density"]) p = ad.max(("gas", "density"), axis="y") p1 = ds.proj(("gas", "density"), 1, data_source=ad, method="max") assert_equal(p["gas", "density"], p1["gas", "density"]) p = ad.min(("gas", "density"), axis="y") p1 = ds.proj(("gas", "density"), 1, data_source=ad, method="min") assert_equal(p["gas", "density"], p1["gas", "density"]) # Test that we can get multiple in a single pass qrho, qtemp = ad.max([("gas", "density"), ("gas", "temperature")]) assert_equal(qrho, ad["gas", "density"].max()) assert_equal(qtemp, ad["gas", "temperature"].max()) qrho, qtemp = ad.min([("gas", "density"), ("gas", "temperature")]) assert_equal(qrho, ad["gas", "density"].min()) assert_equal(qtemp, ad["gas", "temperature"].min()) def test_argmin(): fields = ["density", "temperature"] units = ["g/cm**3", "K"] for nprocs in [-1, 1, 2, 16]: if nprocs == -1: ds = fake_amr_ds(fields=fields, units=units) else: ds = fake_random_ds( 32, nprocs=nprocs, fields=fields, units=units, ) ad = ds.all_data() q = ad.argmin(("gas", "density"), axis=[("gas", "density")]) assert_equal(q, ad["gas", "density"].min()) q1, q2 = ad.argmin( ("gas", "density"), axis=[("gas", "density"), ("gas", "temperature")] ) mi = np.argmin(ad["gas", "density"]) assert_equal(q1, ad["gas", "density"].min()) assert_equal(q2, ad["gas", "temperature"][mi]) pos = ad.argmin(("gas", "density")) mi = np.argmin(ad["gas", "density"]) assert_equal(pos[0], ad["index", "x"][mi]) assert_equal(pos[1], ad["index", "y"][mi]) assert_equal(pos[2], ad["index", "z"][mi]) def test_argmax(): fields = ["density", "temperature"] units = ["g/cm**3", "K"] for nprocs in [-1, 1, 2, 16]: if nprocs == -1: ds = fake_amr_ds(fields=fields, units=units) else: ds = fake_random_ds( 32, nprocs=nprocs, fields=fields, units=units, ) ad = ds.all_data() q = ad.argmax(("gas", "density"), axis=[("gas", "density")]) assert_equal(q, ad["gas", "density"].max()) q1, q2 = ad.argmax( ("gas", "density"), axis=[("gas", "density"), ("gas", "temperature")] ) mi = np.argmax(ad["gas", "density"]) assert_equal(q1, ad["gas", "density"].max()) assert_equal(q2, ad["gas", "temperature"][mi]) pos = ad.argmax(("gas", "density")) mi = np.argmax(ad["gas", "density"]) assert_equal(pos[0], ad["index", "x"][mi]) assert_equal(pos[1], ad["index", "y"][mi]) assert_equal(pos[2], ad["index", "z"][mi]) yt-project-yt-f043ac8/yt/data_objects/tests/test_octree.py000066400000000000000000000055631510711153200237750ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.testing import fake_sph_grid_ds n_ref = 4 def test_building_tree(): """ Build an octree and make sure correct number of particles """ ds = fake_sph_grid_ds() octree = ds.octree(n_ref=n_ref) assert octree["index", "x"].shape[0] == 17 def test_sph_interpolation_scatter(): """ Generate an octree, perform some SPH interpolation and check with some answer testing """ ds = fake_sph_grid_ds(hsml_factor=26.0) ds._sph_ptypes = ("io",) ds.use_sph_normalization = False octree = ds.octree(n_ref=n_ref) density = octree["io", "density"] answers = np.array( [ 1.00434706, 1.00434706, 1.00434706, 1.00434706, 1.00434706, 1.00434706, 1.00434706, 0.7762907, 0.89250848, 0.89250848, 0.97039088, 0.89250848, 0.97039088, 0.97039088, 1.01156175, ] ) assert_almost_equal(density.d, answers) def test_sph_interpolation_gather(): """ Generate an octree, perform some SPH interpolation and check with some answer testing """ ds = fake_sph_grid_ds(hsml_factor=26.0) ds.index ds._sph_ptypes = ("io",) ds.sph_smoothing_style = "gather" ds.num_neighbors = 5 ds.use_sph_normalization = False octree = ds.octree(n_ref=n_ref) density = octree["io", "density"] answers = np.array( [ 0.59240874, 0.59240874, 0.59240874, 0.59240874, 0.59240874, 0.59240874, 0.59240874, 0.10026846, 0.77014968, 0.77014968, 0.96127825, 0.77014968, 0.96127825, 0.96127825, 1.21183996, ] ) assert_almost_equal(density.d, answers) def test_octree_properties(): """ Generate an octree, and test the refinement, depth and sizes of the cells. """ ds = fake_sph_grid_ds() octree = ds.octree(n_ref=n_ref) depth = octree["index", "depth"] depth_ans = np.array([0] + [1] * 8 + [2] * 8, dtype=np.int64) assert_equal(depth, depth_ans) size_ans = np.zeros((depth.shape[0], 3), dtype=np.float64) for i in range(size_ans.shape[0]): size_ans[i, :] = (ds.domain_right_edge - ds.domain_left_edge) / 2.0 ** depth[i] dx = octree["index", "dx"].d assert_almost_equal(dx, size_ans[:, 0]) dy = octree["index", "dy"].d assert_almost_equal(dy, size_ans[:, 1]) dz = octree["index", "dz"].d assert_almost_equal(dz, size_ans[:, 2]) refined = octree["index", "refined"] refined_ans = np.array([True] + [False] * 7 + [True] + [False] * 8, dtype=np.bool_) assert_equal(refined, refined_ans) yt-project-yt-f043ac8/yt/data_objects/tests/test_ortho_rays.py000066400000000000000000000016431510711153200247000ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.testing import fake_random_ds def test_ortho_ray(): ds = fake_random_ds(64, nprocs=8) dx = (ds.domain_right_edge - ds.domain_left_edge) / ds.domain_dimensions axes = ["x", "y", "z"] for ax in range(3): ocoord = ds.arr(np.random.random(2), "code_length") my_oray = ds.ortho_ray(ax, ocoord) my_axes = ds.coordinates.x_axis[ax], ds.coordinates.y_axis[ax] # find the cells intersected by the ortho ray my_all = ds.all_data() my_cells = ( np.abs(my_all["index", axes[my_axes[0]]] - ocoord[0]) <= 0.5 * dx[my_axes[0]] ) & ( np.abs(my_all["index", axes[my_axes[1]]] - ocoord[1]) <= 0.5 * dx[my_axes[1]] ) assert_equal( my_oray["gas", "density"].sum(), my_all["gas", "density"][my_cells].sum(), ) yt-project-yt-f043ac8/yt/data_objects/tests/test_particle_filter.py000066400000000000000000000157251510711153200256650ustar00rootroot00000000000000import os import shutil import tempfile import numpy as np from nose.tools import assert_raises from numpy.testing import assert_equal from yt.data_objects.particle_filters import add_particle_filter, particle_filter from yt.testing import fake_random_ds, fake_sph_grid_ds from yt.utilities.exceptions import YTIllDefinedFilter, YTIllDefinedParticleFilter from yt.visualization.plot_window import ProjectionPlot def test_add_particle_filter(): """Test particle filters created via add_particle_filter This accesses a deposition field using the particle filter, which was a problem in previous versions on this dataset because there are chunks with no stars in them. """ def stars(pfilter, data): filter_field = (pfilter.filtered_type, "particle_mass") return data[filter_field] > 0.5 add_particle_filter( "stars1", function=stars, filtered_type="all", requires=["particle_mass"] ) ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("stars1") assert ("deposit", "stars1_cic") in ds.derived_field_list # Test without requires field add_particle_filter("stars2", function=stars) ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("stars2") assert ("deposit", "stars2_cic") in ds.derived_field_list # Test adding filter with fields not defined on the ds with assert_raises(YTIllDefinedParticleFilter) as ex: add_particle_filter( "bad_stars", function=stars, filtered_type="all", requires=["wrong_field"] ) ds.add_particle_filter("bad_stars") actual = str(ex.exception) desired = ( "\nThe fields\n\t('all', 'wrong_field'),\nrequired by the" ' "bad_stars" particle filter, are not defined for this dataset.' ) assert_equal(actual, desired) def test_add_particle_filter_overriding(): """Test the add_particle_filter overriding""" from yt.data_objects.particle_filters import filter_registry from yt.funcs import mylog def star_0(pfilter, data): pass def star_1(pfilter, data): pass # Use a closure to store whether the warning was called def closure(status): def warning_patch(*args, **kwargs): status[0] = True def was_called(): return status[0] return warning_patch, was_called ## Test 1: we add a dummy particle filter add_particle_filter( "dummy", function=star_0, filtered_type="all", requires=["creation_time"] ) assert "dummy" in filter_registry assert_equal(filter_registry["dummy"].function, star_0) ## Test 2: we add another dummy particle filter. ## a warning is expected. We use the above closure to ## check that. # Store the original warning function warning = mylog.warning monkey_warning, monkey_patch_was_called = closure([False]) mylog.warning = monkey_warning add_particle_filter( "dummy", function=star_1, filtered_type="all", requires=["creation_time"] ) assert_equal(filter_registry["dummy"].function, star_1) assert_equal(monkey_patch_was_called(), True) # Restore the original warning function mylog.warning = warning def test_particle_filter_decorator(): """Test the particle_filter decorator""" @particle_filter(filtered_type="all", requires=["particle_mass"]) def heavy_stars(pfilter, data): filter_field = (pfilter.filtered_type, "particle_mass") return data[filter_field] > 0.5 ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("heavy_stars") assert "heavy_stars" in ds.particle_types assert ("deposit", "heavy_stars_cic") in ds.derived_field_list # Test name of particle filter @particle_filter(name="my_stars", filtered_type="all", requires=["particle_mass"]) def custom_stars(pfilter, data): filter_field = (pfilter.filtered_type, "particle_mass") return data[filter_field] == 0.5 ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("my_stars") assert "my_stars" in ds.particle_types assert ("deposit", "my_stars_cic") in ds.derived_field_list def test_particle_filter_exceptions(): @particle_filter(filtered_type="all", requires=["particle_mass"]) def filter1(pfilter, data): return data ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("filter1") ad = ds.all_data() with assert_raises(YTIllDefinedFilter): ad["filter1", "particle_mass"].shape[0] @particle_filter(filtered_type="all", requires=["particle_mass"]) def filter2(pfilter, data): filter_field = ("io", "particle_mass") return data[filter_field] > 0.5 ds.add_particle_filter("filter2") ad = ds.all_data() ad["filter2", "particle_mass"].min() def test_particle_filter_dependency(): """ Test dataset add_particle_filter which should automatically add the dependency of the filter. """ @particle_filter(filtered_type="all", requires=["particle_mass"]) def h_stars(pfilter, data): filter_field = (pfilter.filtered_type, "particle_mass") return data[filter_field] > 0.5 @particle_filter(filtered_type="h_stars", requires=["particle_mass"]) def hh_stars(pfilter, data): filter_field = (pfilter.filtered_type, "particle_mass") return data[filter_field] > 0.9 ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("hh_stars") assert "hh_stars" in ds.particle_types assert "h_stars" in ds.particle_types assert ("deposit", "hh_stars_cic") in ds.derived_field_list assert ("deposit", "h_stars_cic") in ds.derived_field_list def test_covering_grid_particle_filter(): @particle_filter(filtered_type="all", requires=["particle_mass"]) def heavy_stars(pfilter, data): filter_field = (pfilter.filtered_type, "particle_mass") return data[filter_field] > 0.5 ds = fake_random_ds(16, nprocs=8, particles=16) ds.add_particle_filter("heavy_stars") for grid in ds.index.grids: cg = ds.covering_grid(grid.Level, grid.LeftEdge, grid.ActiveDimensions) assert_equal( cg["heavy_stars", "particle_mass"].shape[0], grid["heavy_stars", "particle_mass"].shape[0], ) assert_equal( cg["heavy_stars", "particle_mass"].shape[0], grid["heavy_stars", "particle_mass"].shape[0], ) def test_sph_particle_filter_plotting(): ds = fake_sph_grid_ds() @particle_filter("central_gas", requires=["particle_position"], filtered_type="io") def _filter(pfilter, data): coords = np.abs(data[pfilter.filtered_type, "particle_position"]) return (coords[:, 0] < 1.6) & (coords[:, 1] < 1.6) & (coords[:, 2] < 1.6) ds.add_particle_filter("central_gas") plot = ProjectionPlot(ds, "z", ("central_gas", "density")) tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) plot.save() os.chdir(curdir) shutil.rmtree(tmpdir) yt-project-yt-f043ac8/yt/data_objects/tests/test_particle_trajectories.py000066400000000000000000000036011510711153200270640ustar00rootroot00000000000000import glob import os from yt.config import ytcfg from yt.data_objects.time_series import DatasetSeries from yt.utilities.answer_testing.framework import GenericArrayTest, requires_ds def setup_module(): ytcfg["yt", "internals", "within_testing"] = True data_path = ytcfg.get("yt", "test_data_dir") pfields = [ ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ] vfields = [ ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_velocity_z"), ] @requires_ds("Orbit/orbit_hdf5_chk_0000") def test_orbit_traj(): fields = ["particle_velocity_x", "particle_velocity_y", "particle_velocity_z"] my_fns = glob.glob(os.path.join(data_path, "Orbit/orbit_hdf5_chk_00[0-9][0-9]")) my_fns.sort() ts = DatasetSeries(my_fns) ds = ts[0] traj = ts.particle_trajectories([1, 2], fields=fields, suppress_logging=True) for field in pfields + vfields: def field_func(name): return traj[field] # noqa: B023 yield GenericArrayTest(ds, field_func, args=[field]) @requires_ds("enzo_tiny_cosmology/DD0000/DD0000") def test_etc_traj(): fields = [ ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_velocity_z"), ] my_fns = glob.glob( os.path.join(data_path, "enzo_tiny_cosmology/DD000[0-9]/*.hierarchy") ) my_fns.sort() ts = DatasetSeries(my_fns) ds = ts[0] sp = ds.sphere("max", (0.5, "Mpc")) indices = sp["particle_index"][sp["particle_type"] == 1][:5] traj = ts.particle_trajectories(indices, fields=fields, suppress_logging=True) traj.add_fields([("gas", "density")]) for field in pfields + vfields + [("gas", "density")]: def field_func(name): return traj[field] # noqa: B023 yield GenericArrayTest(ds, field_func, args=[field]) yt-project-yt-f043ac8/yt/data_objects/tests/test_particle_trajectories_pytest.py000066400000000000000000000111041510711153200304710ustar00rootroot00000000000000import numpy as np import pytest from numpy.testing import assert_raises from yt.data_objects.particle_filters import particle_filter from yt.data_objects.time_series import DatasetSeries from yt.testing import fake_particle_ds from yt.utilities.exceptions import YTIllDefinedParticleData pfields = [ ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ] vfields = [ ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_velocity_z"), ] @pytest.fixture def particle_trajectories_test_dataset(): n_particles = 2 n_steps = 2 ids = np.arange(n_particles, dtype="int64") data = {"particle_index": ids} fields = [ "particle_position_x", "particle_position_y", "particle_position_z", "particle_velocity_x", # adding a non-default field "particle_index", ] negative = [False, False, False, True, False] units = ["cm", "cm", "cm", "cm/s", "1"] ts = DatasetSeries( [ fake_particle_ds( fields=fields, negative=negative, units=units, npart=n_particles, data=data, ) for i in range(n_steps) ] ) return ts def test_uniqueness(): n_particles = 2 n_steps = 2 ids = np.arange(n_particles, dtype="int64") % (n_particles // 2) data = {"particle_index": ids} fields = [ "particle_position_x", "particle_position_y", "particle_position_z", "particle_index", ] negative = [False, False, False, False] units = ["cm", "cm", "cm", "1"] ts = DatasetSeries( [ fake_particle_ds( fields=fields, negative=negative, units=units, npart=n_particles, data=data, ) for i in range(n_steps) ] ) assert_raises(YTIllDefinedParticleData, ts.particle_trajectories, [0]) def test_ptype(): n_particles = 100 fields = [ "particle_position_x", "particle_position_y", "particle_position_z", "particle_index", "particle_dummy", ] negative = [False, False, False, False, False] units = ["cm", "cm", "cm", "1", "1"] # Setup filters on the 'particle_dummy' field, keeping only the first 50 @particle_filter(name="dummy", requires=["particle_dummy"]) def dummy(pfilter, data): return data[pfilter.filtered_type, "particle_dummy"] <= n_particles // 2 # Setup fake particle datasets with repeated ids. This should work because # the ids are unique among `dummy_particles` so let's test this data = { "particle_index": np.arange(n_particles) % (n_particles // 2), "particle_dummy": np.arange(n_particles), } all_ds = [ fake_particle_ds( fields=fields, negative=negative, units=units, npart=n_particles, data=data ) ] for ds in all_ds: ds.add_particle_filter("dummy") ts = DatasetSeries(all_ds) # Select all dummy particles ids = ts[0].all_data()["dummy", "particle_index"] # Build trajectories ts.particle_trajectories(ids, ptype="dummy") @pytest.mark.parametrize("ptype", [None, "io"]) def test_default_field_tuple(particle_trajectories_test_dataset, ptype): ds = particle_trajectories_test_dataset[0] ids = ds.all_data()[("all", "particle_index")] trajs = particle_trajectories_test_dataset.particle_trajectories( ids, ptype=ptype, suppress_logging=True ) ptype = ptype if ptype else "all" # ptype defaults to "all" for k in trajs.field_data.keys(): assert isinstance(k, tuple), f"Expected key to be tuple, received {type(k)}" assert ( k[0] == ptype ), f"Default field type ({k[0]}) does not match expected ({ptype})" assert ("all", k[1]) in pfields, f"Unexpected field: {k[1]}" @pytest.mark.parametrize("ptype", [None, "io"]) def test_time_and_index(particle_trajectories_test_dataset, ptype): ds = particle_trajectories_test_dataset[0] ids = ds.all_data()[("all", "particle_index")] trajs = particle_trajectories_test_dataset.particle_trajectories( ids, ptype=ptype, suppress_logging=True ) ptype = ptype if ptype else "all" # ptype defaults to "all" traj = trajs.trajectory_from_index(1) for field in ["particle_time", "particle_index"]: assert (ptype, field) in traj.keys(), f"Missing ({ptype},{field})" assert (field) not in traj.keys(), f"{field} present as bare string" yt-project-yt-f043ac8/yt/data_objects/tests/test_pickling.py000066400000000000000000000017031510711153200243040ustar00rootroot00000000000000import pickle from numpy.testing import assert_equal from yt.testing import requires_file, requires_module from yt.utilities.answer_testing.framework import data_dir_load tipsy_ds = "TipsyGalaxy/galaxy.00300" enzo_ds = "enzo_tiny_cosmology/DD0000/DD0000" @requires_module("h5py") @requires_file(enzo_ds) def test_grid_pickles(): ds = data_dir_load(enzo_ds) ad = ds.all_data() # just test ad since there is a nested ds that will get (un)pickled ad_pickle = pickle.loads(pickle.dumps(ad)) assert_equal(ad.ds.basename, ad_pickle.ds.basename) @requires_file(tipsy_ds) def test_particle_pickles(): ds = data_dir_load(tipsy_ds) ad = ds.all_data() ds.index._identify_base_chunk(ad) ch0 = list(ds.index._chunk_io(ad, cache=False))[0] # just test one chunk since there is a nested ds that will get (un)pickled ch_pickle = pickle.loads(pickle.dumps(ch0)) assert_equal(ch0.dobj.ds.basename, ch_pickle.dobj.ds.basename) yt-project-yt-f043ac8/yt/data_objects/tests/test_points.py000066400000000000000000000051051510711153200240200ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal import yt from yt.testing import fake_random_ds def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_point_creation(): ds = fake_random_ds(16) p1 = ds.point(ds.domain_center) p2 = ds.point([0.5, 0.5, 0.5]) p3 = ds.point([0.5, 0.5, 0.5] * yt.units.cm) # ensure all three points are really at the same position for fname in "xyz": assert_equal(p1["index", fname], p2["index", fname]) assert_equal(p1["index", fname], p3["index", fname]) def test_domain_point(): nparticles = 3 ds = fake_random_ds(16, particles=nparticles) p = ds.point(ds.domain_center) # ensure accessing one field works, store for comparison later point_den = p["gas", "density"] point_vel = p["gas", "velocity_x"] ad = ds.all_data() ppos = ad["all", "particle_position"] fpoint_den = ds.find_field_values_at_point(("gas", "density"), ds.domain_center) fpoint_den_vel = ds.find_field_values_at_point( [("gas", "density"), ("gas", "velocity_x")], ds.domain_center ) assert_equal(point_den, fpoint_den) assert_equal(point_den, fpoint_den_vel[0]) assert_equal(point_vel, fpoint_den_vel[1]) ppos_den = ds.find_field_values_at_points(("gas", "density"), ppos) ppos_vel = ds.find_field_values_at_points(("gas", "velocity_x"), ppos) ppos_den_vel = ds.find_field_values_at_points( [("gas", "density"), ("gas", "velocity_x")], ppos ) assert_equal(ppos_den.shape, (nparticles,)) assert_equal(ppos_vel.shape, (nparticles,)) assert_equal(len(ppos_den_vel), 2) assert_equal(ppos_den_vel[0], ppos_den) assert_equal(ppos_den_vel[1], ppos_vel) def test_fast_find_field_values_at_points(): ds = fake_random_ds(64, nprocs=8, particles=16**3) ad = ds.all_data() # right now this is slow for large numbers of particles, so randomly # sample 100 particles nparticles = 100 ppos = ad["all", "particle_position"] ppos = ppos[np.random.randint(low=0, high=len(ppos), size=nparticles)] ppos_den = ds.find_field_values_at_points(("gas", "density"), ppos) ppos_vel = ds.find_field_values_at_points(("gas", "velocity_x"), ppos) ppos_den_vel = ds.find_field_values_at_points( [("gas", "density"), ("gas", "velocity_x")], ppos ) assert_equal(ppos_den.shape, (nparticles,)) assert_equal(ppos_vel.shape, (nparticles,)) assert_equal(len(ppos_den_vel), 2) assert_equal(ppos_den_vel[0], ppos_den) assert_equal(ppos_den_vel[1], ppos_vel) yt-project-yt-f043ac8/yt/data_objects/tests/test_print_stats.py000066400000000000000000000004261510711153200250570ustar00rootroot00000000000000import pytest from yt.testing import ( fake_amr_ds, fake_particle_ds, ) def test_no_print_stats(): ds = fake_particle_ds() with pytest.raises(NotImplementedError): ds.print_stats() def test_print_stats(): ds = fake_amr_ds() ds.print_stats() yt-project-yt-f043ac8/yt/data_objects/tests/test_profiles.py000066400000000000000000000552171510711153200243400ustar00rootroot00000000000000import os import shutil import tempfile import unittest import numpy as np from numpy.testing import assert_equal, assert_raises import yt from yt.data_objects.particle_filters import add_particle_filter from yt.data_objects.profiles import Profile1D, Profile2D, Profile3D, create_profile from yt.testing import ( assert_rel_equal, fake_random_ds, fake_sph_orientation_ds, requires_module, ) from yt.utilities.exceptions import YTIllDefinedProfile, YTProfileDataShape from yt.visualization.profile_plotter import PhasePlot, ProfilePlot _fields = ("density", "temperature", "dinosaurs", "tribbles") _units = ("g/cm**3", "K", "dyne", "erg") def test_profiles(): ds = fake_random_ds(64, nprocs=8, fields=_fields, units=_units) nv = ds.domain_dimensions.prod() dd = ds.all_data() rt, tt, dt = dd.quantities["TotalQuantity"]( [("gas", "density"), ("gas", "temperature"), ("stream", "dinosaurs")] ) e1, e2 = 0.9, 1.1 for nb in [8, 16, 32, 64]: for input_units in ["mks", "cgs"]: (rmi, rma), (tmi, tma), (dmi, dma) = ( getattr(ex, f"in_{input_units}")() for ex in dd.quantities["Extrema"]( [ ("gas", "density"), ("gas", "temperature"), ("stream", "dinosaurs"), ] ) ) # We log all the fields or don't log 'em all. No need to do them # individually. for lf in [True, False]: direct_profile = Profile1D( dd, ("gas", "density"), nb, rmi * e1, rma * e2, lf, weight_field=None, ) direct_profile.add_fields([("index", "ones"), ("gas", "temperature")]) indirect_profile_s = create_profile( dd, ("gas", "density"), [("index", "ones"), ("gas", "temperature")], n_bins=nb, extrema={("gas", "density"): (rmi * e1, rma * e2)}, logs={("gas", "density"): lf}, weight_field=None, ) indirect_profile_t = create_profile( dd, ("gas", "density"), [("index", "ones"), ("gas", "temperature")], n_bins=nb, extrema={("gas", "density"): (rmi * e1, rma * e2)}, logs={("gas", "density"): lf}, weight_field=None, ) for p1d in [direct_profile, indirect_profile_s, indirect_profile_t]: assert_equal(p1d["index", "ones"].sum(), nv) assert_rel_equal(tt, p1d["gas", "temperature"].sum(), 7) p2d = Profile2D( dd, ("gas", "density"), nb, rmi * e1, rma * e2, lf, ("gas", "temperature"), nb, tmi * e1, tma * e2, lf, weight_field=None, ) p2d.add_fields([("index", "ones"), ("gas", "temperature")]) assert_equal(p2d["index", "ones"].sum(), nv) assert_rel_equal(tt, p2d["gas", "temperature"].sum(), 7) p3d = Profile3D( dd, ("gas", "density"), nb, rmi * e1, rma * e2, lf, ("gas", "temperature"), nb, tmi * e1, tma * e2, lf, ("stream", "dinosaurs"), nb, dmi * e1, dma * e2, lf, weight_field=None, ) p3d.add_fields([("index", "ones"), ("gas", "temperature")]) assert_equal(p3d["index", "ones"].sum(), nv) assert_rel_equal(tt, p3d["gas", "temperature"].sum(), 7) p1d = Profile1D(dd, ("index", "x"), nb, 0.0, 1.0, False, weight_field=None) p1d.add_fields(("index", "ones")) av = nv / nb assert_equal(p1d["index", "ones"], np.ones(nb) * av) # We re-bin ones with a weight now p1d = Profile1D( dd, ("index", "x"), nb, 0.0, 1.0, False, weight_field=("gas", "temperature") ) p1d.add_fields([("index", "ones")]) assert_equal(p1d["index", "ones"], np.ones(nb)) # Verify we can access "ones" after adding a new field # See issue 988 p1d.add_fields([("gas", "density")]) assert_equal(p1d["index", "ones"], np.ones(nb)) p2d = Profile2D( dd, ("index", "x"), nb, 0.0, 1.0, False, ("index", "y"), nb, 0.0, 1.0, False, weight_field=None, ) p2d.add_fields(("index", "ones")) av = nv / nb**2 assert_equal(p2d["index", "ones"], np.ones((nb, nb)) * av) # We re-bin ones with a weight now p2d = Profile2D( dd, ("index", "x"), nb, 0.0, 1.0, False, ("index", "y"), nb, 0.0, 1.0, False, weight_field=("gas", "temperature"), ) p2d.add_fields([("index", "ones")]) assert_equal(p2d["index", "ones"], np.ones((nb, nb))) p3d = Profile3D( dd, ("index", "x"), nb, 0.0, 1.0, False, ("index", "y"), nb, 0.0, 1.0, False, ("index", "z"), nb, 0.0, 1.0, False, weight_field=None, ) p3d.add_fields(("index", "ones")) av = nv / nb**3 assert_equal(p3d["index", "ones"], np.ones((nb, nb, nb)) * av) # We re-bin ones with a weight now p3d = Profile3D( dd, ("index", "x"), nb, 0.0, 1.0, False, ("index", "y"), nb, 0.0, 1.0, False, ("index", "z"), nb, 0.0, 1.0, False, weight_field=("gas", "temperature"), ) p3d.add_fields([("index", "ones")]) assert_equal(p3d["index", "ones"], np.ones((nb, nb, nb))) p2d = create_profile( dd, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "cell_mass"), extrema={("gas", "density"): (None, rma * e2)}, ) assert_equal(p2d.x_bins[0], rmi - np.spacing(rmi)) assert_equal(p2d.x_bins[-1], rma * e2) assert str(ds.field_info["gas", "cell_mass"].units) == str(p2d.weight.units) p2d = create_profile( dd, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "cell_mass"), extrema={("gas", "density"): (rmi * e2, None)}, ) assert_equal(p2d.x_bins[0], rmi * e2) assert_equal(p2d.x_bins[-1], rma + np.spacing(rma)) extrema_s = {("all", "particle_position_x"): (0, 1)} logs_s = {("all", "particle_position_x"): False} extrema_t = {("all", "particle_position_x"): (0, 1)} logs_t = {("all", "particle_position_x"): False} def test_particle_profiles(): for nproc in [1, 2, 4, 8]: ds = fake_random_ds(32, nprocs=nproc, particles=32**3) dd = ds.all_data() p1d = Profile1D( dd, ("all", "particle_position_x"), 128, 0.0, 1.0, False, weight_field=None ) p1d.add_fields([("all", "particle_ones")]) assert_equal(p1d["all", "particle_ones"].sum(), 32**3) p1d = create_profile( dd, [("all", "particle_position_x")], [("all", "particle_ones")], weight_field=None, n_bins=128, extrema=extrema_s, logs=logs_s, ) assert_equal(p1d["all", "particle_ones"].sum(), 32**3) p1d = create_profile( dd, [("all", "particle_position_x")], [("all", "particle_ones")], weight_field=None, n_bins=128, extrema=extrema_t, logs=logs_t, ) assert_equal(p1d["all", "particle_ones"].sum(), 32**3) p2d = Profile2D( dd, ("all", "particle_position_x"), 128, 0.0, 1.0, False, ("all", "particle_position_y"), 128, 0.0, 1.0, False, weight_field=None, ) p2d.add_fields([("all", "particle_ones")]) assert_equal(p2d["all", "particle_ones"].sum(), 32**3) p3d = Profile3D( dd, ("all", "particle_position_x"), 128, 0.0, 1.0, False, ("all", "particle_position_y"), 128, 0.0, 1.0, False, ("all", "particle_position_z"), 128, 0.0, 1.0, False, weight_field=None, ) p3d.add_fields([("all", "particle_ones")]) assert_equal(p3d["all", "particle_ones"].sum(), 32**3) def test_mixed_particle_mesh_profiles(): ds = fake_random_ds(32, particles=10) ad = ds.all_data() assert_raises( YTIllDefinedProfile, ProfilePlot, ad, ("index", "radius"), ("all", "particle_mass"), ) assert_raises( YTIllDefinedProfile, ProfilePlot, ad, "radius", [("all", "particle_mass"), ("all", "particle_ones")], ) assert_raises( YTIllDefinedProfile, ProfilePlot, ad, ("index", "radius"), [("all", "particle_mass"), ("index", "ones")], ) assert_raises( YTIllDefinedProfile, ProfilePlot, ad, ("all", "particle_radius"), ("all", "particle_mass"), ("gas", "cell_mass"), ) assert_raises( YTIllDefinedProfile, ProfilePlot, ad, ("index", "radius"), ("gas", "cell_mass"), ("all", "particle_ones"), ) assert_raises( YTIllDefinedProfile, PhasePlot, ad, ("index", "radius"), ("all", "particle_mass"), ("gas", "velocity_x"), ) assert_raises( YTIllDefinedProfile, PhasePlot, ad, ("all", "particle_radius"), ("all", "particle_mass"), ("gas", "cell_mass"), ) assert_raises( YTIllDefinedProfile, PhasePlot, ad, ("index", "radius"), ("gas", "cell_mass"), ("all", "particle_ones"), ) assert_raises( YTIllDefinedProfile, PhasePlot, ad, ("all", "particle_radius"), ("all", "particle_mass"), ("all", "particle_ones"), ) def test_particle_profile_negative_field(): # see Issue #1340 n_particles = int(1e4) ppx, ppy, ppz = np.random.normal(size=[3, n_particles]) pvx, pvy, pvz = -np.ones((3, n_particles)) data = { "particle_position_x": ppx, "particle_position_y": ppy, "particle_position_z": ppz, "particle_velocity_x": pvx, "particle_velocity_y": pvy, "particle_velocity_z": pvz, } bbox = 1.1 * np.array( [[min(ppx), max(ppx)], [min(ppy), max(ppy)], [min(ppz), max(ppz)]] ) ds = yt.load_particles(data, bbox=bbox) ad = ds.all_data() profile = yt.create_profile( ad, [("all", "particle_position_x"), ("all", "particle_position_y")], ("all", "particle_velocity_x"), logs={ ("all", "particle_position_x"): True, ("all", "particle_position_y"): True, ("all", "particle_position_z"): True, }, weight_field=None, ) assert profile["all", "particle_velocity_x"].min() < 0 assert profile.x_bins.min() > 0 assert profile.y_bins.min() > 0 profile = yt.create_profile( ad, [("all", "particle_position_x"), ("all", "particle_position_y")], ("all", "particle_velocity_x"), weight_field=None, ) assert profile["all", "particle_velocity_x"].min() < 0 assert profile.x_bins.min() < 0 assert profile.y_bins.min() < 0 # can't use CIC deposition with log-scaled bin fields with assert_raises(RuntimeError): yt.create_profile( ad, [("all", "particle_position_x"), ("all", "particle_position_y")], ("all", "particle_velocity_x"), logs={ ("all", "particle_position_x"): True, ("all", "particle_position_y"): False, ("all", "particle_position_z"): False, }, weight_field=None, deposition="cic", ) # can't use CIC deposition with accumulation or fractional with assert_raises(RuntimeError): yt.create_profile( ad, [("all", "particle_position_x"), ("all", "particle_position_y")], ("all", "particle_velocity_x"), logs={ ("all", "particle_position_x"): False, ("all", "particle_position_y"): False, ("all", "particle_position_z"): False, }, weight_field=None, deposition="cic", accumulation=True, fractional=True, ) def test_profile_zero_weight(): def DMparticles(pfilter, data): filter = data[pfilter.filtered_type, "particle_type"] == 1 return filter def DM_in_cell_mass(field, data): return data["deposit", "DM_density"] * data["index", "cell_volume"] add_particle_filter( "DM", function=DMparticles, filtered_type="io", requires=["particle_type"] ) _fields = ( "particle_position_x", "particle_position_y", "particle_position_z", "particle_mass", "particle_velocity_x", "particle_velocity_y", "particle_velocity_z", "particle_type", ) _units = ("cm", "cm", "cm", "g", "cm/s", "cm/s", "cm/s", "dimensionless") ds = fake_random_ds( 32, particle_fields=_fields, particle_field_units=_units, particles=16 ) ds.add_particle_filter("DM") ds.add_field( ("gas", "DM_cell_mass"), units="g", function=DM_in_cell_mass, sampling_type="cell", ) sp = ds.sphere(ds.domain_center, (10, "kpc")) profile = yt.create_profile( sp, [("gas", "density")], [("gas", "radial_velocity")], weight_field=("gas", "DM_cell_mass"), ) assert not np.any(np.isnan(profile["gas", "radial_velocity"])) def test_profile_sph_data(): ds = fake_sph_orientation_ds() # test we create a profile without raising YTIllDefinedProfile yt.create_profile( ds.all_data(), [("gas", "density"), ("gas", "temperature")], [("gas", "kinetic_energy_density")], weight_field=None, ) yt.create_profile( ds.all_data(), [("gas", "density"), ("gas", "temperature")], [("gas", "kinetic_energy_density")], weight_field=("gas", "density"), ) def test_profile_override_limits(): ds = fake_random_ds(64, nprocs=8, fields=_fields, units=_units) sp = ds.sphere(ds.domain_center, (10, "kpc")) obins = np.linspace(-5, 5, 10) profile = yt.create_profile( sp, [("gas", "density")], [("gas", "temperature")], override_bins={("gas", "density"): (obins, "g/cm**3")}, ) assert_equal(ds.arr(obins, "g/cm**3"), profile.x_bins) profile = yt.create_profile( sp, [("gas", "density"), ("stream", "dinosaurs")], [("gas", "temperature")], override_bins={ ("gas", "density"): (obins, "g/cm**3"), ("stream", "dinosaurs"): obins, }, ) assert_equal(ds.arr(obins, "g/cm**3"), profile.x_bins) assert_equal(ds.arr(obins, "dyne"), profile.y_bins) profile = yt.create_profile( sp, [("gas", "density"), (("stream", "dinosaurs")), ("stream", "tribbles")], [("gas", "temperature")], override_bins={ ("gas", "density"): (obins, "g/cm**3"), (("stream", "dinosaurs")): obins, ("stream", "tribbles"): (obins, "erg"), }, ) assert_equal(ds.arr(obins, "g/cm**3"), profile.x_bins) assert_equal(ds.arr(obins, "dyne"), profile.y_bins) assert_equal(ds.arr(obins, "erg"), profile.z_bins) class TestBadProfiles(unittest.TestCase): tmpdir = None curdir = None def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) # clean up shutil.rmtree(self.tmpdir) @requires_module("h5py") def test_unequal_data_shape_profile(self): density = np.random.random(128) temperature = np.random.random(128) mass = np.random.random((128, 128)) my_data = { ("gas", "density"): density, ("gas", "temperature"): temperature, ("gas", "mass"): mass, } fake_ds_med = {"current_time": yt.YTQuantity(10, "Myr")} field_types = {field: "gas" for field in my_data.keys()} yt.save_as_dataset(fake_ds_med, "mydata.h5", my_data, field_types=field_types) ds = yt.load("mydata.h5") with assert_raises(YTProfileDataShape): yt.PhasePlot( ds.data, ("gas", "temperature"), ("gas", "density"), ("gas", "mass"), ) @requires_module("h5py") def test_unequal_bin_field_profile(self): density = np.random.random(128) temperature = np.random.random(127) mass = np.random.random((128, 128)) my_data = { ("gas", "density"): density, ("gas", "temperature"): temperature, ("gas", "mass"): mass, } fake_ds_med = {"current_time": yt.YTQuantity(10, "Myr")} field_types = {field: "gas" for field in my_data.keys()} yt.save_as_dataset(fake_ds_med, "mydata.h5", my_data, field_types=field_types) ds = yt.load("mydata.h5") with assert_raises(YTProfileDataShape): yt.PhasePlot( ds.data, ("gas", "temperature"), ("gas", "density"), ("gas", "mass"), ) def test_set_linear_scaling_for_none_extrema(self): # See Issue #3431 # Ensures that extrema are calculated in the same way on subsequent passes # through the PhasePlot machinery. ds = fake_sph_orientation_ds() p = yt.PhasePlot( ds, ("all", "particle_position_spherical_theta"), ("all", "particle_position_spherical_radius"), ("all", "particle_mass"), weight_field=None, ) p.set_log(("all", "particle_position_spherical_theta"), False) p.save() def test_index_field_units(): # see #1849 ds = fake_random_ds(16, length_unit=2) ad = ds.all_data() icv_units = ad["index", "cell_volume"].units assert str(icv_units) == "code_length**3" gcv_units = ad["gas", "cell_volume"].units assert str(gcv_units) == "cm**3" prof = ad.profile( [("gas", "density"), ("gas", "velocity_x")], [("gas", "cell_volume"), ("index", "cell_volume")], weight_field=None, ) assert str(prof["index", "cell_volume"].units) == "code_length**3" assert str(prof["gas", "cell_volume"].units) == "cm**3" @requires_module("astropy") def test_export_astropy(): from yt.units.yt_array import YTArray ds = fake_random_ds(64) ad = ds.all_data() prof = ad.profile( ("index", "radius"), [("gas", "density"), ("gas", "velocity_x")], weight_field=("index", "ones"), n_bins=32, ) # export to AstroPy table at1 = prof.to_astropy_table() assert "radius" in at1.colnames assert "density" in at1.colnames assert "velocity_x" in at1.colnames assert_equal(prof.x.d, at1["radius"].value) assert_equal(prof["gas", "density"].d, at1["density"].value) assert_equal(prof["gas", "velocity_x"].d, at1["velocity_x"].value) assert prof.x.units == YTArray.from_astropy(at1["radius"]).units assert prof["gas", "density"].units == YTArray.from_astropy(at1["density"]).units assert ( prof["gas", "velocity_x"].units == YTArray.from_astropy(at1["velocity_x"]).units ) assert np.all(at1.mask["density"] == prof.used) at2 = prof.to_astropy_table(fields=("gas", "density"), only_used=True) assert "radius" in at2.colnames assert "velocity_x" not in at2.colnames assert_equal(prof.x.d[prof.used], at2["radius"].value) assert_equal(prof["gas", "density"].d[prof.used], at2["density"].value) at3 = prof.to_astropy_table(fields=("gas", "density"), include_std=True) assert_equal(prof["gas", "density"].d, at3["density"].value) assert_equal( prof.standard_deviation["gas", "density"].d, at3["density_stddev"].value ) assert ( prof.standard_deviation["gas", "density"].units == YTArray.from_astropy(at3["density_stddev"]).units ) @requires_module("pandas") def test_export_pandas(): ds = fake_random_ds(64) ad = ds.all_data() prof = ad.profile( "radius", [("gas", "density"), ("gas", "velocity_x")], weight_field=("index", "ones"), n_bins=32, ) # export to pandas DataFrame df1 = prof.to_dataframe() assert "radius" in df1.columns assert "density" in df1.columns assert "velocity_x" in df1.columns assert_equal(prof.x.d, df1["radius"]) assert_equal(prof["gas", "density"].d, np.nan_to_num(df1["density"])) assert_equal(prof["velocity_x"].d, np.nan_to_num(df1["velocity_x"])) df2 = prof.to_dataframe(fields=("gas", "density"), only_used=True) assert "radius" in df2.columns assert "velocity_x" not in df2.columns assert_equal(prof.x.d[prof.used], df2["radius"]) assert_equal(prof["gas", "density"].d[prof.used], df2["density"]) df3 = prof.to_dataframe(fields=("gas", "density"), include_std=True) assert_equal( prof.standard_deviation["gas", "density"].d, np.nan_to_num(df3["density_stddev"]), ) yt-project-yt-f043ac8/yt/data_objects/tests/test_projection.py000066400000000000000000000162211510711153200246610ustar00rootroot00000000000000import os import tempfile from unittest import mock import numpy as np from numpy.testing import assert_equal from yt.testing import assert_rel_equal, fake_amr_ds, fake_random_ds from yt.units.unit_object import Unit LENGTH_UNIT = 2.0 def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def teardown_func(fns): for fn in fns: try: os.remove(fn) except OSError: pass @mock.patch("matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure") def test_projection(pf): fns = [] for nprocs in [8, 1]: # We want to test both 1 proc and 8 procs, to make sure that # parallelism isn't broken fields = ("density", "temperature", "velocity_x", "velocity_y", "velocity_z") units = ("g/cm**3", "K", "cm/s", "cm/s", "cm/s") ds = fake_random_ds( 64, fields=fields, units=units, nprocs=nprocs, length_unit=LENGTH_UNIT ) dims = ds.domain_dimensions xn, yn, zn = ds.domain_dimensions xi, yi, zi = ds.domain_left_edge.to_ndarray() + 1.0 / (ds.domain_dimensions * 2) xf, yf, zf = ds.domain_right_edge.to_ndarray() - 1.0 / ( ds.domain_dimensions * 2 ) dd = ds.all_data() coords = np.mgrid[xi : xf : xn * 1j, yi : yf : yn * 1j, zi : zf : zn * 1j] uc = [np.unique(c) for c in coords] # test if projections inherit the field parameters of their data sources dd.set_field_parameter("bulk_velocity", np.array([0, 1, 2])) proj = ds.proj(("gas", "density"), 0, data_source=dd) assert_equal( dd.field_parameters["bulk_velocity"], proj.field_parameters["bulk_velocity"] ) # Some simple projection tests with single grids for ax, an in enumerate("xyz"): xax = ds.coordinates.x_axis[ax] yax = ds.coordinates.y_axis[ax] for wf in [("gas", "density"), None]: proj = ds.proj( [("index", "ones"), ("gas", "density")], ax, weight_field=wf ) if wf is None: assert_equal( proj["index", "ones"].sum(), LENGTH_UNIT * proj["index", "ones"].size, ) assert_equal(proj["index", "ones"].min(), LENGTH_UNIT) assert_equal(proj["index", "ones"].max(), LENGTH_UNIT) else: assert_equal( proj["index", "ones"].sum(), proj["index", "ones"].size ) assert_equal(proj["index", "ones"].min(), 1.0) assert_equal(proj["index", "ones"].max(), 1.0) assert_equal(np.unique(proj["px"]), uc[xax]) assert_equal(np.unique(proj["py"]), uc[yax]) assert_equal(np.unique(proj["pdx"]), 1.0 / (dims[xax] * 2.0)) assert_equal(np.unique(proj["pdy"]), 1.0 / (dims[yax] * 2.0)) plots = [proj.to_pw(fields=("gas", "density")), proj.to_pw()] for pw in plots: for p in pw.plots.values(): tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) p.save(name=tmpname) fns.append(tmpname) frb = proj.to_frb((1.0, "unitary"), 64) for proj_field in [ ("index", "ones"), ("gas", "density"), ("gas", "temperature"), ]: fi = ds._get_field_info(proj_field) assert_equal(frb[proj_field].info["data_source"], proj.__str__()) assert_equal(frb[proj_field].info["axis"], ax) assert_equal(frb[proj_field].info["field"], str(proj_field)) field_unit = Unit(fi.units) if wf is not None: assert_equal( frb[proj_field].units, Unit(field_unit, registry=ds.unit_registry), ) else: if frb[proj_field].units.is_code_unit: proj_unit = "code_length" else: proj_unit = "cm" if field_unit != "" and field_unit != Unit(): proj_unit = f"({field_unit}) * {proj_unit}" assert_equal( frb[proj_field].units, Unit(proj_unit, registry=ds.unit_registry), ) assert_equal(frb[proj_field].info["xlim"], frb.bounds[:2]) assert_equal(frb[proj_field].info["ylim"], frb.bounds[2:]) assert_equal(frb[proj_field].info["center"], proj.center) if wf is None: assert_equal(frb[proj_field].info["weight_field"], wf) else: assert_equal( frb[proj_field].info["weight_field"], proj.data_source._determine_fields(wf)[0], ) # wf == None assert_equal(wf, None) v1 = proj["gas", "density"].sum() v2 = (dd["gas", "density"] * dd["index", f"d{an}"]).sum() assert_rel_equal(v1, v2.in_units(v1.units), 10) # Test moment projections def make_vsq_field(aname): def _vsquared(field, data): return data["gas", f"velocity_{aname}"] ** 2 return _vsquared for ax, an in enumerate("xyz"): ds.add_field( ("gas", f"velocity_{an}_squared"), make_vsq_field(an), sampling_type="local", units="cm**2/s**2", ) proj1 = ds.proj( [("gas", f"velocity_{an}"), ("gas", f"velocity_{an}_squared")], ax, weight_field=("gas", "density"), moment=1, ) proj2 = ds.proj( ("gas", f"velocity_{an}"), ax, weight_field=("gas", "density"), moment=2 ) assert_rel_equal( np.sqrt( proj1["gas", f"velocity_{an}_squared"] - proj1["gas", f"velocity_{an}"] ** 2 ), proj2["gas", f"velocity_{an}"], 10, ) teardown_func(fns) def test_max_level(): ds = fake_amr_ds(fields=[("gas", "density")], units=["mp/cm**3"]) proj = ds.proj(("gas", "density"), 2, method="max", max_level=2) assert proj["index", "grid_level"].max() == 2 proj = ds.proj(("gas", "density"), 2, method="max") assert proj["index", "grid_level"].max() == ds.index.max_level def test_min_level(): ds = fake_amr_ds(fields=[("gas", "density")], units=["mp/cm**3"]) proj = ds.proj(("gas", "density"), 2, method="min") assert proj["index", "grid_level"].min() == 0 proj = ds.proj(("gas", "density"), 2, method="max") assert proj["index", "grid_level"].min() == ds.index.min_level yt-project-yt-f043ac8/yt/data_objects/tests/test_rays.py000066400000000000000000000141511510711153200234630ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt import load from yt.testing import ( assert_rel_equal, cubicspline_python, fake_random_ds, fake_sph_grid_ds, integrate_kernel, requires_file, requires_module, ) from yt.units._numpy_wrapper_functions import uconcatenate def test_ray(): for nproc in [1, 2, 4, 8]: ds = fake_random_ds(64, nprocs=nproc) dx = (ds.domain_right_edge - ds.domain_left_edge) / ds.domain_dimensions # Three we choose, to get varying vectors, and ten random pp1 = np.random.random((3, 13)) pp2 = np.random.random((3, 13)) pp1[:, 0] = [0.1, 0.2, 0.3] pp2[:, 0] = [0.8, 0.1, 0.4] pp1[:, 1] = [0.9, 0.2, 0.3] pp2[:, 1] = [0.8, 0.1, 0.4] pp1[:, 2] = [0.9, 0.2, 0.9] pp2[:, 2] = [0.8, 0.1, 0.4] unitary = ds.arr(1.0, "") for i in range(pp1.shape[1]): p1 = ds.arr(pp1[:, i] + 1e-8 * np.random.random(3), "code_length") p2 = ds.arr(pp2[:, i] + 1e-8 * np.random.random(3), "code_length") my_ray = ds.ray(p1, p2) assert_rel_equal(my_ray["dts"].sum(), unitary, 14) ray_cells = my_ray["dts"] > 0 # find cells intersected by the ray my_all = ds.all_data() dt = np.abs(dx / (p2 - p1)) tin = uconcatenate( [ [(my_all["index", "x"] - p1[0]) / (p2 - p1)[0] - 0.5 * dt[0]], [(my_all["index", "y"] - p1[1]) / (p2 - p1)[1] - 0.5 * dt[1]], [(my_all["index", "z"] - p1[2]) / (p2 - p1)[2] - 0.5 * dt[2]], ] ) tout = uconcatenate( [ [(my_all["index", "x"] - p1[0]) / (p2 - p1)[0] + 0.5 * dt[0]], [(my_all["index", "y"] - p1[1]) / (p2 - p1)[1] + 0.5 * dt[1]], [(my_all["index", "z"] - p1[2]) / (p2 - p1)[2] + 0.5 * dt[2]], ] ) tin = tin.max(axis=0) tout = tout.min(axis=0) my_cells = (tin < tout) & (tin < 1) & (tout > 0) assert_equal(ray_cells.sum(), my_cells.sum()) assert_rel_equal( my_ray["gas", "density"][ray_cells].sum(), my_all["gas", "density"][my_cells].sum(), 14, ) assert_rel_equal(my_ray["dts"].sum(), unitary, 14) @requires_module("h5py") @requires_file("GadgetDiskGalaxy/snapshot_200.hdf5") def test_ray_particle(): ds = load("GadgetDiskGalaxy/snapshot_200.hdf5") ray = ds.ray(ds.domain_left_edge, ds.domain_right_edge) assert_equal(ray["t"].shape, (1451,)) assert ray["dts"].sum(dtype="f8") > 0 ## test that kernels integrate correctly # (1) including the right particles # (2) correct t and dts values for those particles ## fake_sph_grid_ds: # This dataset should have 27 particles with the particles arranged # uniformly on a 3D grid. The bottom left corner is (0.5,0.5,0.5) and # the top right corner is (2.5,2.5,2.5). All particles will have # non-overlapping smoothing regions with a radius of 0.05, masses of # 1, and densities of 1, and zero velocity. def test_ray_particle2(): kernelfunc = cubicspline_python ds = fake_sph_grid_ds(hsml_factor=1.0) ds.kernel_name = "cubic" ## Ray through the one particle at (0.5, 0.5, 0.5): ## test basic kernel integration eps = 0.0 # 1e-7 start0 = np.array((1.0 + eps, 0.0, 0.5)) end0 = np.array((0.0, 1.0 + eps, 0.5)) ray0 = ds.ray(start0, end0) b0 = np.array([np.sqrt(2.0) * eps]) hsml0 = np.array([0.05]) len0 = np.sqrt(np.sum((end0 - start0) ** 2)) # for a ParticleDataset like this one, the Ray object attempts # to generate the 't' and 'dts' fields using the grid method ray0.field_data["t"] = ray0.ds.arr(ray0._generate_container_field_sph("t")) ray0.field_data["dts"] = ray0.ds.arr(ray0._generate_container_field_sph("dts")) # not demanding too much precision; # from kernel volume integrals, the linear interpolation # restricts you to 4 -- 5 digits precision assert_equal(ray0["t"].shape, (1,)) assert_rel_equal(ray0["t"], np.array([0.5]), 5) assert_rel_equal(ray0[("gas", "position")].v, np.array([[0.5, 0.5, 0.5]]), 5) dl0 = integrate_kernel(kernelfunc, b0, hsml0) dl0 *= ray0[("gas", "mass")].v / ray0[("gas", "density")].v assert_rel_equal(ray0[("dts")].v, dl0 / len0, 4) ## Ray in the middle of the box: ## test end points, >1 particle start1 = np.array((1.53, 0.53, 1.0)) end1 = np.array((1.53, 0.53, 3.0)) ray1 = ds.ray(start1, end1) b1 = np.array([np.sqrt(2.0) * 0.03] * 2) hsml1 = np.array([0.05] * 2) len1 = np.sqrt(np.sum((end1 - start1) ** 2)) # for a ParticleDataset like this one, the Ray object attempts # to generate the 't' and 'dts' fields using the grid method ray1.field_data["t"] = ray1.ds.arr(ray1._generate_container_field_sph("t")) ray1.field_data["dts"] = ray1.ds.arr(ray1._generate_container_field_sph("dts")) # not demanding too much precision; # from kernel volume integrals, the linear interpolation # restricts you to 4 -- 5 digits precision assert_equal(ray1["t"].shape, (2,)) assert_rel_equal(ray1["t"], np.array([0.25, 0.75]), 5) assert_rel_equal( ray1[("gas", "position")].v, np.array([[1.5, 0.5, 1.5], [1.5, 0.5, 2.5]]), 5 ) dl1 = integrate_kernel(kernelfunc, b1, hsml1) dl1 *= ray1[("gas", "mass")].v / ray1[("gas", "density")].v assert_rel_equal(ray1[("dts")].v, dl1 / len1, 4) ## Ray missing all particles: ## test handling of size-0 selections start2 = np.array((1.0, 2.0, 0.0)) end2 = np.array((1.0, 2.0, 3.0)) ray2 = ds.ray(start2, end2) # for a ParticleDataset like this one, the Ray object attempts # to generate the 't' and 'dts' fields using the grid method ray2.field_data["t"] = ray2.ds.arr(ray2._generate_container_field_sph("t")) ray2.field_data["dts"] = ray2.ds.arr(ray2._generate_container_field_sph("dts")) assert_equal(ray2["t"].shape, (0,)) assert_equal(ray2["dts"].shape, (0,)) assert_equal(ray2[("gas", "position")].v.shape, (0, 3)) yt-project-yt-f043ac8/yt/data_objects/tests/test_refinement.py000066400000000000000000000031561510711153200246440ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal, assert_equal import yt def setup_fake_refby(): refine_by = np.array([5, 1, 1]) top_grid_dim = [100, 10, 2] n1 = 100 n2 = 10 n3 = 2 grid_data = [ { "left_edge": [0.0, 0.0, 0.0], "right_edge": [1.0, np.pi, np.pi * 2.0], "level": 0, "dimensions": np.array([n1, n2, n3]), }, { "left_edge": [0.0, 0.0, 0.0], "right_edge": [0.5, np.pi, np.pi * 2.0], "level": 1, "dimensions": refine_by * [n1 / 2.0, n2, n3], }, ] for g in grid_data: g["density"] = (np.random.random(g["dimensions"].astype("i8")), "g/cm**3") bbox = np.array([[0.0, 1.0], [0.0, np.pi], [0.0, np.pi * 2]]) ds = yt.load_amr_grids( grid_data, top_grid_dim, bbox=bbox, geometry="spherical", refine_by=refine_by, length_unit="kpc", ) return ds def test_refine_by(): ds = setup_fake_refby() dd = ds.all_data() # This checks that we always refine_by 1 in dimensions 2 and 3 dims = ds.domain_dimensions * ds.refine_by**ds.max_level for i in range(1, 3): # Check the refine_by == 1 ncoords = np.unique(dd.icoords[:, i]).size assert_equal(ncoords, dims[i]) for g in ds.index.grids: dims = ds.domain_dimensions * ds.refine_by**g.Level # Now we can check converting back to the reference space v = ((g.icoords + 1) / dims.astype("f8")).max(axis=0) v *= ds.domain_width assert_array_equal(v, g.RightEdge.d) yt-project-yt-f043ac8/yt/data_objects/tests/test_regions.py000066400000000000000000000041001510711153200241440ustar00rootroot00000000000000from numpy.testing import assert_array_equal, assert_raises from yt.testing import fake_amr_ds, fake_random_ds from yt.units import cm def test_box_creation(): # test that creating a region with left and right edge # with units works ds = fake_random_ds(32, length_unit=2) reg = ds.box([0, 0, 0] * cm, [2, 2, 2] * cm) dens_units = reg["gas", "density"] reg = ds.box([0, 0, 0], [1, 1, 1]) dens_no_units = reg["gas", "density"] assert_array_equal(dens_units, dens_no_units) def test_max_level_min_level_semantics(): ds = fake_amr_ds() ad = ds.all_data() assert ad["index", "grid_level"].max() == 4 ad.max_level = 2 assert ad["index", "grid_level"].max() == 2 ad.max_level = 8 assert ad["index", "grid_level"].max() == 4 ad.min_level = 2 assert ad["index", "grid_level"].min() == 2 ad.min_level = 0 assert ad["index", "grid_level"].min() == 0 def test_ellipsis_selection(): ds = fake_amr_ds() reg = ds.r[:, :, :] ereg = ds.r[...] assert_array_equal(reg.fwidth, ereg.fwidth) reg = ds.r[(0.5, "cm"), :, :] ereg = ds.r[(0.5, "cm"), ...] assert_array_equal(reg.fwidth, ereg.fwidth) reg = ds.r[:, :, (0.5, "cm")] ereg = ds.r[..., (0.5, "cm")] assert_array_equal(reg.fwidth, ereg.fwidth) reg = ds.r[:, :, (0.5, "cm")] ereg = ds.r[..., (0.5, "cm")] assert_array_equal(reg.fwidth, ereg.fwidth) assert_raises(IndexError, ds.r.__getitem__, (..., (0.5, "cm"), ...)) # this test will fail until "arbitrary_grid" selector is implemented for 2D datasets # see issue https://github.com/yt-project/yt/issues/3437 """ from yt.utilities.answer_testing.framework import data_dir_load, requires_ds @requires_ds("castro_sedov_2d_cyl_in_cart_plt00150") def test_complex_slicing_2D_consistency(): # see https://github.com/yt-project/yt/issues/3429 ds = data_dir_load("castro_sedov_2d_cyl_in_cart_plt00150") reg = ds.r[0.1:0.2:8j, :] reg["gas", "density"] reg = ds.r[:, 1:2:8j] reg["gas", "density"] reg = ds.r[0.1:0.2:8j, 1:2:8j] reg["gas", "density"] """ yt-project-yt-f043ac8/yt/data_objects/tests/test_registration.py000066400000000000000000000013511510711153200252150ustar00rootroot00000000000000import pytest from yt.data_objects.static_output import Dataset from yt.utilities.object_registries import output_type_registry def test_reregistration_warning(): from yt.frontends import enzo # noqa true_EnzoDataset = output_type_registry["EnzoDataset"] try: with pytest.warns( UserWarning, match=( "Overwriting EnzoDataset, which was previously registered. " "This is expected if you're importing a yt extension with a " "frontend that was already migrated to the main code base." ), ): class EnzoDataset(Dataset): pass finally: output_type_registry["EnzoDataset"] = true_EnzoDataset yt-project-yt-f043ac8/yt/data_objects/tests/test_slice.py000066400000000000000000000070441510711153200236070ustar00rootroot00000000000000import os import tempfile from unittest import mock import numpy as np from numpy.testing import assert_equal from yt.testing import fake_random_ds from yt.units.unit_object import Unit def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def teardown_func(fns): for fn in fns: try: os.remove(fn) except OSError: pass @mock.patch("matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure") def test_slice(pf): fns = [] grid_eps = np.finfo(np.float64).eps for nprocs in [8, 1]: # We want to test both 1 proc and 8 procs, to make sure that # parallelism isn't broken ds = fake_random_ds(64, nprocs=nprocs) dims = ds.domain_dimensions xn, yn, zn = ds.domain_dimensions dx = ds.arr(1.0 / (ds.domain_dimensions * 2), "code_length") xi, yi, zi = ds.domain_left_edge + dx xf, yf, zf = ds.domain_right_edge - dx coords = np.mgrid[xi : xf : xn * 1j, yi : yf : yn * 1j, zi : zf : zn * 1j] uc = [np.unique(c) for c in coords] slc_pos = 0.5 # Some simple slice tests with single grids for ax in range(3): xax = ds.coordinates.x_axis[ax] yax = ds.coordinates.y_axis[ax] slc = ds.slice(ax, slc_pos) shifted_slc = ds.slice(ax, slc_pos + grid_eps) assert_equal(slc["index", "ones"].sum(), slc["index", "ones"].size) assert_equal(slc["index", "ones"].min(), 1.0) assert_equal(slc["index", "ones"].max(), 1.0) assert_equal(np.unique(slc["px"]), uc[xax]) assert_equal(np.unique(slc["py"]), uc[yax]) assert_equal(np.unique(slc["pdx"]), 0.5 / dims[xax]) assert_equal(np.unique(slc["pdy"]), 0.5 / dims[yax]) pw = slc.to_pw(fields=("gas", "density")) for p in pw.plots.values(): tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) p.save(name=tmpname) fns.append(tmpname) for width in [(1.0, "unitary"), 1.0, ds.quan(0.5, "code_length")]: frb = slc.to_frb(width, 64) shifted_frb = shifted_slc.to_frb(width, 64) for slc_field in [("index", "ones"), ("gas", "density")]: fi = ds._get_field_info(slc_field) assert_equal(frb[slc_field].info["data_source"], slc.__str__()) assert_equal(frb[slc_field].info["axis"], ax) assert_equal(frb[slc_field].info["field"], str(slc_field)) assert_equal(frb[slc_field].units, Unit(fi.units)) assert_equal(frb[slc_field].info["xlim"], frb.bounds[:2]) assert_equal(frb[slc_field].info["ylim"], frb.bounds[2:]) assert_equal(frb[slc_field].info["center"], slc.center) assert_equal(frb[slc_field].info["coord"], slc_pos) assert_equal(frb[slc_field], shifted_frb[slc_field]) teardown_func(fns) def test_slice_over_edges(): ds = fake_random_ds( 64, nprocs=8, fields=("density",), units=("g/cm**3",), negative=[False] ) slc = ds.slice(0, 0.0) slc["gas", "density"] slc = ds.slice(1, 0.5) slc["gas", "density"] def test_slice_over_outer_boundary(): ds = fake_random_ds( 64, nprocs=8, fields=("density",), units=("g/cm**3",), negative=[False] ) slc = ds.slice(2, 1.0) slc["gas", "density"] assert_equal(slc["gas", "density"].size, 0) yt-project-yt-f043ac8/yt/data_objects/tests/test_sph_data_objects.py000066400000000000000000000257521510711153200260120ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_allclose, assert_almost_equal, assert_equal from yt import SlicePlot, add_particle_filter from yt.loaders import load from yt.testing import fake_sph_grid_ds, fake_sph_orientation_ds, requires_file def test_point(): ds = fake_sph_orientation_ds() field_data = ds.stream_handler.fields["stream_file"] ppos = [field_data["io", f"particle_position_{d}"] for d in "xyz"] ppos = np.array(ppos).T for pos in ppos: for i in range(-1, 2): offset = 0.1 * np.array([i, 0, 0]) pt = ds.point(pos + offset) assert_equal(pt["gas", "density"].shape[0], 1) for j in range(-1, 2): offset = 0.1 * np.array([0, j, 0]) pt = ds.point(pos + offset) assert_equal(pt["gas", "density"].shape[0], 1) for k in range(-1, 2): offset = 0.1 * np.array([0, 0, k]) pt = ds.point(pos + offset) assert_equal(pt["gas", "density"].shape[0], 1) # The number of particles along each slice axis at that coordinate SLICE_ANSWERS = { ("x", 0): 6, ("x", 0.5): 0, ("x", 1): 1, ("y", 0): 5, ("y", 1): 1, ("y", 2): 1, ("z", 0): 4, ("z", 1): 1, ("z", 2): 1, ("z", 3): 1, } def test_slice(): ds = fake_sph_orientation_ds() for (ax, coord), answer in SLICE_ANSWERS.items(): # test that we can still select particles even if we offset the slice # within each particle's smoothing volume for i in range(-1, 2): sl = ds.slice(ax, coord + i * 0.1) assert_equal(sl["gas", "density"].shape[0], answer) REGION_ANSWERS = { ((-4, -4, -4), (4, 4, 4)): 7, ((0, 0, 0), (4, 4, 4)): 7, ((1, 0, 0), (4, 4, 4)): 1, ((0, 1, 0), (4, 4, 4)): 2, ((0, 0, 1), (4, 4, 4)): 3, ((0, 0, 0), (4, 4, 2)): 6, ((0, 0, 0), (4, 4, 1)): 5, ((0, 0, 0), (4, 1, 4)): 6, ((0, 0, 0), (1, 1, 4)): 6, } def test_slice_to_frb(): ds = fake_sph_orientation_ds() frb = ds.slice(0, 0.5).to_frb(ds.domain_width[0], (64, 64)) ref_vals = frb["gas", "density"] for center in ((0.5, "code_length"), (0.5, "cm"), ds.quan(0.5, "code_length")): frb = ds.slice(0, center).to_frb(ds.domain_width[0], (64, 64)) vals = frb["gas", "density"] assert_equal(vals, ref_vals) def test_region(): ds = fake_sph_orientation_ds() for (left_edge, right_edge), answer in REGION_ANSWERS.items(): # test that regions enclosing a particle's smoothing region # correctly select SPH particles for i in range(-1, 2): for j in range(-1, 2): le = np.array([le + i * 0.1 for le in left_edge]) re = np.array([re + j * 0.1 for re in right_edge]) # check if we went off the edge of the domain whl = le < ds.domain_left_edge le[whl] = ds.domain_left_edge[whl] whr = re > ds.domain_right_edge re[whr] = ds.domain_right_edge[whr] reg = ds.box(le, re) assert_equal(reg["gas", "density"].shape[0], answer) def test_periodic_region(): ds = fake_sph_grid_ds(10.0) coords = [0.7, 1.4, 2.8] for x in coords: for y in coords: for z in coords: center = np.array([x, y, z]) for n, w in [(8, 1.0), (27, 2.0)]: le = center - 0.5 * w re = center + 0.5 * w box = ds.box(le, re) assert box["io", "particle_ones"].size == n SPHERE_ANSWERS = { ((0, 0, 0), 4): 7, ((0, 0, 0), 3): 7, ((0, 0, 0), 2): 6, ((0, 0, 0), 1): 4, ((0, 0, 0), 0.5): 1, ((1, 0, 0), 0.5): 1, ((1, 0, 0), 1.0): 2, ((0, 1, 0), 1.0): 3, ((0, 0, 1), 1.0): 3, } def test_sphere(): ds = fake_sph_orientation_ds() for (center, radius), answer in SPHERE_ANSWERS.items(): # test that spheres enclosing a particle's smoothing region # correctly select SPH particles for i in range(-1, 2): for j in range(-1, 2): cent = np.array([c + i * 0.1 for c in center]) rad = radius + 0.1 * j sph = ds.sphere(cent, rad) assert_equal(sph["gas", "density"].shape[0], answer) DISK_ANSWERS = { ((0, 0, 0), (0, 0, 1), 4, 3): 7, ((0, 0, 0), (0, 0, 1), 4, 2): 6, ((0, 0, 0), (0, 0, 1), 4, 1): 5, ((0, 0, 0), (0, 0, 1), 4, 0.5): 4, ((0, 0, 0), (0, 1, 0), 4, 3): 7, ((0, 0, 0), (0, 1, 0), 4, 2): 7, ((0, 0, 0), (0, 1, 0), 4, 1): 6, ((0, 0, 0), (0, 1, 0), 4, 0.5): 5, ((0, 0, 0), (1, 0, 0), 4, 3): 7, ((0, 0, 0), (1, 0, 0), 4, 2): 7, ((0, 0, 0), (1, 0, 0), 4, 1): 7, ((0, 0, 0), (1, 0, 0), 4, 0.5): 6, ((0, 0, 0), (1, 1, 1), 1, 1): 4, ((-0.5, -0.5, -0.5), (1, 1, 1), 4, 4): 7, } def test_disk(): ds = fake_sph_orientation_ds() for (center, normal, radius, height), answer in DISK_ANSWERS.items(): # test that disks enclosing a particle's smoothing region # correctly select SPH particles for i in range(-1, 2): cent = np.array([c + i * 0.1 for c in center]) disk = ds.disk(cent, normal, radius, height) assert_equal(disk["gas", "density"].shape[0], answer) RAY_ANSWERS = { ((0, 0, 0), (3, 0, 0)): 2, ((0, 0, 0), (0, 3, 0)): 3, ((0, 0, 0), (0, 0, 3)): 4, ((0, 1, 0), (0, 2, 0)): 2, ((1, 0, 0), (0, 2, 0)): 2, ((0.5, 0.5, 0.5), (0.5, 0.5, 3.5)): 0, } def test_ray(): ds = fake_sph_orientation_ds() for (start_point, end_point), answer in RAY_ANSWERS.items(): for i in range(-1, 2): start = np.array([s + i * 0.1 for s in start_point]) end = np.array([e + i * 0.1 for e in end_point]) ray = ds.ray(start, end) assert_equal(ray["gas", "density"].shape[0], answer) CUTTING_ANSWERS = { ((1, 0, 0), (0, 0, 0)): 6, ((0, 1, 0), (0, 0, 0)): 5, ((0, 0, 1), (0, 0, 0)): 4, ((1, 1, 1), (1.0 / 3, 1.0 / 3, 1.0 / 3)): 3, ((1, 1, 1), (2.0 / 3, 2.0 / 3, 2.0 / 3)): 2, ((1, 1, 1), (1, 1, 1)): 1, } def test_cutting(): ds = fake_sph_orientation_ds() for (normal, center), answer in CUTTING_ANSWERS.items(): for i in range(-1, 2): cen = [c + 0.1 * i for c in center] cut = ds.cutting(normal, cen) assert_equal(cut["gas", "density"].shape[0], answer) def test_chained_selection(): ds = fake_sph_orientation_ds() for (center, radius), answer in SPHERE_ANSWERS.items(): sph = ds.sphere(center, radius) region = ds.box(ds.domain_left_edge, ds.domain_right_edge, data_source=sph) assert_equal(region["gas", "density"].shape[0], answer) def test_boolean_selection(): ds = fake_sph_orientation_ds() sph = ds.sphere([0, 0, 0], 0.5) sph2 = ds.sphere([1, 0, 0], 0.5) reg = ds.all_data() neg = reg - sph assert_equal(neg["gas", "density"].shape[0], 6) plus = sph + sph2 assert_equal(plus["gas", "density"].shape[0], 2) intersect = sph & sph2 assert_equal(intersect["gas", "density"].shape[0], 0) intersect = reg & sph2 assert_equal(intersect["gas", "density"].shape[0], 1) exclusive = sph ^ sph2 assert_equal(exclusive["gas", "density"].shape[0], 2) exclusive = sph ^ reg assert_equal(exclusive["gas", "density"].shape[0], 6) intersect = ds.intersection([sph, sph2]) assert_equal(intersect["gas", "density"].shape[0], 0) intersect = ds.intersection([reg, sph2]) assert_equal(intersect["gas", "density"].shape[0], 1) union = ds.union([sph, sph2]) assert_equal(union["gas", "density"].shape[0], 2) union = ds.union([sph, reg]) assert_equal(union["gas", "density"].shape[0], 7) def test_arbitrary_grid(): ds = fake_sph_grid_ds() # this loads up some sph data in a test grid agrid = ds.arbitrary_grid([0, 0, 0], [3, 3, 3], dims=[3, 3, 3]) # the field should be equal to the density of a particle in every voxel # which is 1. dens = agrid["gas", "density"] answers = np.ones(shape=(3, 3, 3)) assert_equal(dens, answers) def test_compare_arbitrary_grid_slice(): ds = fake_sph_orientation_ds() c = np.array([0.0, 0.0, 0.0]) width = 1.5 buff_size = 51 field = ("gas", "density") # buffer from arbitrary grid ag = ds.arbitrary_grid(c - width / 2, c + width / 2, [buff_size] * 3) buff_ag = ag[field][:, :, int(np.floor(buff_size / 2))].d.T # buffer from slice p = SlicePlot(ds, "z", field, center=c, width=width) p.set_buff_size(51) buff_slc = p.frb.data[field].d assert_equal(buff_slc, buff_ag) def test_gather_slice(): ds = fake_sph_grid_ds() ds.num_neighbors = 5 field = ("gas", "density") c = np.array([1.5, 1.5, 0.5]) width = 3.0 p = SlicePlot(ds, "z", field, center=c, width=width) p.set_buff_size(3) buff_scatter = p.frb.data[field].d ds.sph_smoothing_style = "gather" p = SlicePlot(ds, "z", field, center=c, width=width) p.set_buff_size(3) buff_gather = p.frb.data[field].d assert_allclose(buff_scatter, buff_gather, rtol=3e-16) def test_gather_grid(): ds = fake_sph_grid_ds() ds.num_neighbors = 5 field = ("gas", "density") ag = ds.arbitrary_grid([0, 0, 0], [3, 3, 3], dims=[3, 3, 3]) scatter = ag[field] ds.sph_smoothing_style = "gather" ag = ds.arbitrary_grid([0, 0, 0], [3, 3, 3], dims=[3, 3, 3]) gather = ag[field] assert_allclose(gather, scatter, rtol=3e-16) def test_covering_grid_scatter(): ds = fake_sph_grid_ds() field = ("gas", "density") buff_size = 8 ag = ds.arbitrary_grid(0, 3, [buff_size] * 3) ag_dens = ag[field].to("g*cm**-3").d cg = ds.covering_grid(3, 0, 8) cg_dens = cg[field].to("g*cm**-3").d assert_equal(ag_dens, cg_dens) def test_covering_grid_gather(): ds = fake_sph_grid_ds() ds.sph_smoothing_style = "gather" ds.num_neighbors = 5 field = ("gas", "density") buff_size = 8 ag = ds.arbitrary_grid(0, 3, [buff_size] * 3) ag_dens = ag[field].to("g*cm**-3").d cg = ds.covering_grid(3, 0, 8) cg_dens = cg[field].to("g*cm**-3").d assert_equal(ag_dens, cg_dens) @requires_file("TNGHalo/halo_59.hdf5") def test_covering_grid_derived_fields(): def hot_gas(pfilter, data): return data[pfilter.filtered_type, "temperature"] > 1.0e6 add_particle_filter( "hot_gas", function=hot_gas, filtered_type="gas", requires=["temperature"], ) bbox = [[40669.34, 56669.34], [45984.04, 61984.04], [54114.9, 70114.9]] ds = load("TNGHalo/halo_59.hdf5", bounding_box=bbox) ds.add_particle_filter("hot_gas") w = ds.quan(0.2, "Mpc") le = ds.domain_center - 0.5 * w re = ds.domain_center + 0.5 * w g = ds.r[le[0] : re[0] : 128j, le[1] : re[1] : 128j, le[2] : re[2] : 128j] T1 = g["gas", "temperature"].to("keV", "thermal") T2 = g["gas", "kT"] assert_almost_equal(T1, T2) T3 = g["hot_gas", "temperature"].to("keV", "thermal") T4 = g["hot_gas", "kT"] assert_almost_equal(T3, T4) yt-project-yt-f043ac8/yt/data_objects/tests/test_spheres.py000066400000000000000000000076401510711153200241630ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal, assert_equal, assert_raises from yt.data_objects.profiles import create_profile from yt.testing import fake_random_ds, periodicity_cases, requires_module from yt.utilities.exceptions import YTException, YTFieldNotFound def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True _fields_to_compare = ( "spherical_radius", "cylindrical_radius", "spherical_theta", "cylindrical_theta", "spherical_phi", "cylindrical_z", ) def test_domain_sphere(): # Now we test that we can get different radial velocities based on field # parameters. # Get the first sphere ds = fake_random_ds( 16, fields=("density", "velocity_x", "velocity_y", "velocity_z"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), ) sp0 = ds.sphere(ds.domain_center, 0.25) # Compute the bulk velocity from the cells in this sphere bulk_vel = sp0.quantities.bulk_velocity() # Get the second sphere sp1 = ds.sphere(ds.domain_center, 0.25) # Set the bulk velocity field parameter sp1.set_field_parameter("bulk_velocity", bulk_vel) assert_equal( np.any(sp0["gas", "radial_velocity"] == sp1["gas", "radial_velocity"]), False, ) # Radial profile without correction # Note we set n_bins = 8 here. rp0 = create_profile( sp0, "radius", "radial_velocity", units={"radius": "kpc"}, logs={"radius": False}, n_bins=8, ) # Radial profile with correction for bulk velocity rp1 = create_profile( sp1, "radius", "radial_velocity", units={"radius": "kpc"}, logs={"radius": False}, n_bins=8, ) assert_equal(rp0.x_bins, rp1.x_bins) assert_equal(rp0.used, rp1.used) assert_equal(rp0.used.sum() > rp0.used.size / 2.0, True) assert_equal( np.any(rp0["radial_velocity"][rp0.used] == rp1["radial_velocity"][rp1.used]), False, ) ref_sp = ds.sphere("c", 0.25) for f in _fields_to_compare: ref_sp[f].sort() for center in periodicity_cases(ds): sp = ds.sphere(center, 0.25) for f in _fields_to_compare: sp[f].sort() assert_equal(sp[f], ref_sp[f]) def test_sphere_center(): ds = fake_random_ds( 16, nprocs=8, fields=("density", "temperature", "velocity_x"), units=("g/cm**3", "K", "cm/s"), ) # Test if we obtain same center in different ways sp1 = ds.sphere("max", (0.25, "unitary")) sp2 = ds.sphere("max_density", (0.25, "unitary")) assert_array_equal(sp1.center, sp2.center) sp1 = ds.sphere("min", (0.25, "unitary")) sp2 = ds.sphere("min_density", (0.25, "unitary")) assert_array_equal(sp1.center, sp2.center) def test_center_error(): ds = fake_random_ds(16, nprocs=16) with assert_raises(YTFieldNotFound): ds.sphere("min_non_existing_field_name", (0.25, "unitary")) with assert_raises(YTFieldNotFound): ds.sphere("max_non_existing_field_name", (0.25, "unitary")) @requires_module("miniball") def test_minimal_sphere(): ds = fake_random_ds(16, nprocs=8, particles=100) pos = ds.r["all", "particle_position"] sp1 = ds.minimal_sphere(pos) N0 = len(pos) # Check all particles have been found N1 = len(sp1["all", "particle_ones"]) assert_equal(N0, N1) # Check that any smaller sphere is missing some particles sp2 = ds.sphere(sp1.center, sp1.radius * 0.9) N2 = len(sp2["all", "particle_ones"]) assert N2 < N0 @requires_module("miniball") def test_minimal_sphere_bad_inputs(): ds = fake_random_ds(16, nprocs=8, particles=100) pos = ds.r["all", "particle_position"] ## Check number of points >= 2 # -> should fail assert_raises(YTException, ds.minimal_sphere, pos[:1, :]) # -> should not fail ds.minimal_sphere(pos[:2, :]) yt-project-yt-f043ac8/yt/data_objects/tests/test_time_series.py000066400000000000000000000113321510711153200250130ustar00rootroot00000000000000import tempfile from pathlib import Path import pytest from yt.data_objects.static_output import Dataset from yt.data_objects.time_series import DatasetSeries from yt.utilities.exceptions import YTUnidentifiedDataType from yt.utilities.object_registries import output_type_registry def test_pattern_expansion(): file_list = [f"fake_data_file_{str(i).zfill(4)}" for i in range(10)] with tempfile.TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) for file in file_list: (tmp_path / file).touch() pattern = tmp_path / "fake_data_file_*" expected = [str(tmp_path / file) for file in file_list] found = DatasetSeries._get_filenames_from_glob_pattern(pattern) assert found == expected found2 = DatasetSeries._get_filenames_from_glob_pattern(Path(pattern)) assert found2 == expected def test_no_match_pattern(): with tempfile.TemporaryDirectory() as tmpdir: pattern = Path(tmpdir).joinpath("fake_data_file_*") with pytest.raises(FileNotFoundError): DatasetSeries._get_filenames_from_glob_pattern(pattern) @pytest.fixture def FakeDataset(): class _FakeDataset(Dataset): """A minimal loadable fake dataset subclass""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @classmethod def _is_valid(cls, *args, **kwargs): return True def _parse_parameter_file(self): return def _set_code_unit_attributes(self): return def set_code_units(self): i = int(Path(self.filename).name.split("_")[-1]) self.current_time = i self.current_redshift = 1 / (i + 1) return def _hash(self): return def _setup_classes(self): return try: yield _FakeDataset finally: output_type_registry.pop("_FakeDataset") @pytest.fixture def fake_datasets(): file_list = [f"fake_data_file_{i:04d}" for i in range(10)] with tempfile.TemporaryDirectory() as tmpdir: pfile_list = [Path(tmpdir) / file for file in file_list] sfile_list = [str(file) for file in pfile_list] for file in pfile_list: file.touch() pattern = Path(tmpdir) / "fake_data_file_*" yield file_list, pfile_list, sfile_list, pattern def test_init_fake_dataseries(fake_datasets): file_list, pfile_list, sfile_list, pattern = fake_datasets # init from str pattern ts = DatasetSeries(pattern) assert ts._pre_outputs == sfile_list # init from Path pattern ppattern = Path(pattern) ts = DatasetSeries(ppattern) assert ts._pre_outputs == sfile_list # init form str list ts = DatasetSeries(sfile_list) assert ts._pre_outputs == sfile_list # init form Path list ts = DatasetSeries(pfile_list) assert ts._pre_outputs == pfile_list # rejected input type (str repr of a list) "[file1, file2, ...]" with pytest.raises(FileNotFoundError): DatasetSeries(str(file_list)) # finally, check that ts[0] fails to actually load with pytest.raises(YTUnidentifiedDataType): ts[0] def test_init_fake_dataseries2(FakeDataset, fake_datasets): _file_list, _pfile_list, _sfile_list, pattern = fake_datasets ds = DatasetSeries(pattern)[0] assert isinstance(ds, FakeDataset) ts = DatasetSeries(pattern, my_unsupported_kwarg=None) with pytest.raises(TypeError): ts[0] def test_get_by_key(FakeDataset, fake_datasets): _file_list, _pfile_list, sfile_list, pattern = fake_datasets ts = DatasetSeries(pattern) Ntot = len(sfile_list) t = ts[0].quan(1, "code_time") assert sfile_list[0] == ts.get_by_time(-t).filename assert sfile_list[0] == ts.get_by_time(t - t).filename assert sfile_list[1] == ts.get_by_time((0.8, "code_time")).filename assert sfile_list[1] == ts.get_by_time((1.2, "code_time")).filename assert sfile_list[Ntot - 1] == ts.get_by_time(t * (Ntot - 1)).filename assert sfile_list[Ntot - 1] == ts.get_by_time(t * Ntot).filename with pytest.raises(ValueError): ts.get_by_time(-2 * t, tolerance=0.1 * t) with pytest.raises(ValueError): ts.get_by_time(1000 * t, tolerance=0.1 * t) assert sfile_list[1] == ts.get_by_redshift(1 / 2.2).filename assert sfile_list[1] == ts.get_by_redshift(1 / 2).filename assert sfile_list[1] == ts.get_by_redshift(1 / 1.6).filename with pytest.raises(ValueError): ts.get_by_redshift(1000, tolerance=0.1) zmid = (ts[0].current_redshift + ts[1].current_redshift) / 2 assert sfile_list[1] == ts.get_by_redshift(zmid, prefer="smaller").filename assert sfile_list[0] == ts.get_by_redshift(zmid, prefer="larger").filename yt-project-yt-f043ac8/yt/data_objects/tests/test_units_override.py000066400000000000000000000044111510711153200255440ustar00rootroot00000000000000from functools import partial from numpy.testing import assert_raises from yt.data_objects.static_output import Dataset from yt.units import YTQuantity from yt.units.unit_registry import UnitRegistry mock_quan = partial(YTQuantity, registry=UnitRegistry()) def test_schema_validation(): valid_schemas = [ {"length_unit": 1.0}, {"length_unit": [1.0]}, {"length_unit": (1.0,)}, {"length_unit": int(1.0)}, {"length_unit": (1.0, "m")}, {"length_unit": [1.0, "m"]}, {"length_unit": YTQuantity(1.0, "m")}, ] for schema in valid_schemas: uo = Dataset._sanitize_units_override(schema) for v in uo.values(): q = mock_quan(v) # check that no error (TypeError) is raised q.to("pc") # check that q is a length def test_invalid_schema_detection(): invalid_key_schemas = [ {"len_unit": 1.0}, # plain invalid key {"lenght_unit": 1.0}, # typo ] for invalid_schema in invalid_key_schemas: assert_raises(ValueError, Dataset._sanitize_units_override, invalid_schema) invalid_val_schemas = [ {"length_unit": [1, 1, 1]}, # len(val) > 2 {"length_unit": [1, 1, 1, 1, 1]}, # "data type not understood" in unyt ] for invalid_schema in invalid_val_schemas: assert_raises(TypeError, Dataset._sanitize_units_override, invalid_schema) # 0 shouldn't make sense invalid_number_schemas = [ {"length_unit": 0}, {"length_unit": [0]}, {"length_unit": (0,)}, {"length_unit": (0, "cm")}, ] for invalid_schema in invalid_number_schemas: assert_raises(ValueError, Dataset._sanitize_units_override, invalid_schema) def test_typing_error_detection(): invalid_schema = {"length_unit": "1m"} # this is the error that is raised by unyt on bad input assert_raises(RuntimeError, mock_quan, invalid_schema["length_unit"]) # check that the sanitizer function is able to catch the # type issue before passing down to unyt assert_raises(TypeError, Dataset._sanitize_units_override, invalid_schema) def test_dimensionality_error_detection(): invalid_schema = {"length_unit": YTQuantity(1.0, "s")} assert_raises(ValueError, Dataset._sanitize_units_override, invalid_schema) yt-project-yt-f043ac8/yt/data_objects/time_series.py000066400000000000000000000657141510711153200226270ustar00rootroot00000000000000import functools import glob import inspect import os import weakref from abc import ABC, abstractmethod from functools import wraps from typing import TYPE_CHECKING, Literal import numpy as np from more_itertools import always_iterable from unyt import Unit, unyt_quantity from yt.config import ytcfg from yt.data_objects.analyzer_objects import AnalysisTask, create_quantity_proxy from yt.data_objects.particle_trajectories import ParticleTrajectories from yt.funcs import is_sequence, mylog from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.exceptions import YTException from yt.utilities.object_registries import ( analysis_task_registry, data_object_registry, derived_quantity_registry, simulation_time_series_registry, ) from yt.utilities.parallel_tools.parallel_analysis_interface import ( communication_system, parallel_objects, parallel_root_only, ) if TYPE_CHECKING: from yt.data_objects.static_output import Dataset class AnalysisTaskProxy: def __init__(self, time_series): self.time_series = time_series def __getitem__(self, key): task_cls = analysis_task_registry[key] @wraps(task_cls.__init__) def func(*args, **kwargs): task = task_cls(*args, **kwargs) return self.time_series.eval(task) return func def keys(self): return analysis_task_registry.keys() def __contains__(self, key): return key in analysis_task_registry def get_ds_prop(propname): def _eval(params, ds): return getattr(ds, propname) cls = type(propname, (AnalysisTask,), {"eval": _eval, "_params": ()}) return cls attrs = ( "refine_by", "dimensionality", "current_time", "domain_dimensions", "domain_left_edge", "domain_right_edge", "unique_identifier", "current_redshift", "cosmological_simulation", "omega_matter", "omega_lambda", "omega_radiation", "hubble_constant", ) class TimeSeriesParametersContainer: def __init__(self, data_object): self.data_object = data_object def __getattr__(self, attr): if attr in attrs: return self.data_object.eval(get_ds_prop(attr)()) raise AttributeError(attr) class DatasetSeries: r"""The DatasetSeries object is a container of multiple datasets, allowing easy iteration and computation on them. DatasetSeries objects are designed to provide easy ways to access, analyze, parallelize and visualize multiple datasets sequentially. This is primarily expressed through iteration, but can also be constructed via analysis tasks (see :ref:`time-series-analysis`). Note that contained datasets are lazily loaded and weakly referenced. This means that in order to perform follow-up operations on data it's best to define handles on these datasets during iteration. Parameters ---------- outputs : list of filenames, or pattern A list of filenames, for instance ["DD0001/DD0001", "DD0002/DD0002"], or a glob pattern (i.e. containing wildcards '[]?!*') such as "DD*/DD*.index". In the latter case, results are sorted automatically. Filenames and patterns can be of type str, os.Pathlike or bytes. parallel : True, False or int This parameter governs the behavior when .piter() is called on the resultant DatasetSeries object. If this is set to False, the time series will not iterate in parallel when .piter() is called. If this is set to either True, one processor will be allocated for each iteration of the loop. If this is set to an integer, the loop will be parallelized over this many workgroups. It the integer value is less than the total number of available processors, more than one processor will be allocated to a given loop iteration, causing the functionality within the loop to be run in parallel. setup_function : callable, accepts a ds This function will be called whenever a dataset is loaded. mixed_dataset_types : True or False, default False Set to True if the DatasetSeries will load different dataset types, set to False if loading dataset of a single type as this will result in a considerable speed up from not having to figure out the dataset type. Examples -------- >>> ts = DatasetSeries( ... "GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0[0-6][0-9]0" ... ) >>> for ds in ts: ... SlicePlot(ds, "x", ("gas", "density")).save() ... >>> def print_time(ds): ... print(ds.current_time) ... >>> ts = DatasetSeries( ... "GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0[0-6][0-9]0", ... setup_function=print_time, ... ) ... >>> for ds in ts: ... SlicePlot(ds, "x", ("gas", "density")).save() """ _dataset_cls: type["Dataset"] | None = None def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) code_name = cls.__name__[: cls.__name__.find("Simulation")] if code_name: simulation_time_series_registry[code_name] = cls mylog.debug("Registering simulation: %s as %s", code_name, cls) def __new__(cls, outputs, *args, **kwargs): try: outputs = cls._get_filenames_from_glob_pattern(outputs) except TypeError: pass ret = super().__new__(cls) ret._pre_outputs = outputs[:] ret.kwargs = {} return ret def __init__( self, outputs, parallel=True, setup_function=None, mixed_dataset_types=False, **kwargs, ): # This is needed to properly set _pre_outputs for Simulation subclasses. self._mixed_dataset_types = mixed_dataset_types if is_sequence(outputs) and not isinstance(outputs, str): self._pre_outputs = outputs[:] self.tasks = AnalysisTaskProxy(self) self.params = TimeSeriesParametersContainer(self) if setup_function is None: def _null(x): return None setup_function = _null self._setup_function = setup_function for type_name in data_object_registry: setattr( self, type_name, functools.partial(DatasetSeriesObject, self, type_name) ) self.parallel = parallel self.kwargs = kwargs @staticmethod def _get_filenames_from_glob_pattern(outputs): """ Helper function to DatasetSeries.__new__ handle a special case where "outputs" is assumed to be really a pattern string """ pattern = outputs epattern = os.path.expanduser(pattern) data_dir = ytcfg.get("yt", "test_data_dir") # if no match if found from the current work dir, # we try to match the pattern from the test data dir file_list = glob.glob(epattern) or glob.glob(os.path.join(data_dir, epattern)) if not file_list: raise FileNotFoundError(f"No match found for pattern : {pattern}") return sorted(file_list) def __getitem__(self, key): if isinstance(key, slice): if isinstance(key.start, float): return self.get_range(key.start, key.stop) # This will return a sliced up object! return DatasetSeries( self._pre_outputs[key], parallel=self.parallel, **self.kwargs ) o = self._pre_outputs[key] if isinstance(o, (str, os.PathLike)): o = self._load(o, **self.kwargs) self._setup_function(o) return o def __len__(self): return len(self._pre_outputs) @property def outputs(self): return self._pre_outputs def piter(self, storage=None, dynamic=False): r"""Iterate over time series components in parallel. This allows you to iterate over a time series while dispatching individual components of that time series to different processors or processor groups. If the parallelism strategy was set to be multi-processor (by "parallel = N" where N is an integer when the DatasetSeries was created) this will issue each dataset to an N-processor group. For instance, this would allow you to start a 1024 processor job, loading up 100 datasets in a time series and creating 8 processor groups of 128 processors each, each of which would be assigned a different dataset. This could be accomplished as shown in the examples below. The *storage* option is as seen in :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_objects` which is a mechanism for storing results of analysis on an individual dataset and then combining the results at the end, so that the entire set of processors have access to those results. Note that supplying a *store* changes the iteration mechanism; see below. Parameters ---------- storage : dict This is a dictionary, which will be filled with results during the course of the iteration. The keys will be the dataset indices and the values will be whatever is assigned to the *result* attribute on the storage during iteration. dynamic : boolean This governs whether or not dynamic load balancing will be enabled. This requires one dedicated processor; if this is enabled with a set of 128 processors available, only 127 will be available to iterate over objects as one will be load balancing the rest. Examples -------- Here is an example of iteration when the results do not need to be stored. One processor will be assigned to each dataset. >>> ts = DatasetSeries("DD*/DD*.index") >>> for ds in ts.piter(): ... SlicePlot(ds, "x", ("gas", "density")).save() ... This demonstrates how one might store results: >>> def print_time(ds): ... print(ds.current_time) ... >>> ts = DatasetSeries("DD*/DD*.index", setup_function=print_time) ... >>> my_storage = {} >>> for sto, ds in ts.piter(storage=my_storage): ... v, c = ds.find_max(("gas", "density")) ... sto.result = (v, c) ... >>> for i, (v, c) in sorted(my_storage.items()): ... print("% 4i %0.3e" % (i, v)) ... This shows how to dispatch 4 processors to each dataset: >>> ts = DatasetSeries("DD*/DD*.index", parallel=4) >>> for ds in ts.piter(): ... ProjectionPlot(ds, "x", ("gas", "density")).save() ... """ if not self.parallel: njobs = 1 elif not dynamic: if self.parallel: njobs = -1 else: njobs = self.parallel else: my_communicator = communication_system.communicators[-1] nsize = my_communicator.size if nsize == 1: self.parallel = False dynamic = False njobs = 1 else: njobs = nsize - 1 for output in parallel_objects( self._pre_outputs, njobs=njobs, storage=storage, dynamic=dynamic ): if storage is not None: sto, output = output if isinstance(output, str): ds = self._load(output, **self.kwargs) self._setup_function(ds) else: ds = output if storage is not None: next_ret = (sto, ds) else: next_ret = ds yield next_ret def eval(self, tasks, obj=None): return_values = {} for store, ds in self.piter(return_values): store.result = [] for task in always_iterable(tasks): try: style = inspect.getargspec(task.eval)[0][1] if style == "ds": arg = ds elif style == "data_object": if obj is None: obj = DatasetSeriesObject(self, "all_data") arg = obj.get(ds) rv = task.eval(arg) # We catch and store YT-originating exceptions # This fixes the standard problem of having a sphere that's too # small. except YTException: pass store.result.append(rv) return [v for k, v in sorted(return_values.items())] @classmethod def from_output_log(cls, output_log, line_prefix="DATASET WRITTEN", parallel=True): filenames = [] for line in open(output_log): if not line.startswith(line_prefix): continue cut_line = line[len(line_prefix) :].strip() fn = cut_line.split()[0] filenames.append(fn) obj = cls(filenames, parallel=parallel) return obj def _load(self, output_fn, *, hint: str | None = None, **kwargs): from yt.loaders import load if self._dataset_cls is not None: return self._dataset_cls(output_fn, **kwargs) elif self._mixed_dataset_types: return load(output_fn, hint=hint, **kwargs) ds = load(output_fn, hint=hint, **kwargs) self._dataset_cls = ds.__class__ return ds def particle_trajectories( self, indices, fields=None, suppress_logging=False, ptype=None ): r"""Create a collection of particle trajectories in time over a series of datasets. Parameters ---------- indices : array_like An integer array of particle indices whose trajectories we want to track. If they are not sorted they will be sorted. fields : list of strings, optional A set of fields that is retrieved when the trajectory collection is instantiated. Default: None (will default to the fields 'particle_position_x', 'particle_position_y', 'particle_position_z') suppress_logging : boolean Suppress yt's logging when iterating over the simulation time series. Default: False ptype : str, optional Only use this particle type. Default: None, which uses all particle type. Examples -------- >>> my_fns = glob.glob("orbit_hdf5_chk_00[0-9][0-9]") >>> my_fns.sort() >>> fields = [ ... ("all", "particle_position_x"), ... ("all", "particle_position_y"), ... ("all", "particle_position_z"), ... ("all", "particle_velocity_x"), ... ("all", "particle_velocity_y"), ... ("all", "particle_velocity_z"), ... ] >>> ds = load(my_fns[0]) >>> init_sphere = ds.sphere(ds.domain_center, (0.5, "unitary")) >>> indices = init_sphere["all", "particle_index"].astype("int64") >>> ts = DatasetSeries(my_fns) >>> trajs = ts.particle_trajectories(indices, fields=fields) >>> for t in trajs: ... print( ... t["all", "particle_velocity_x"].max(), ... t["all", "particle_velocity_x"].min(), ... ) Notes ----- This function will fail if there are duplicate particle ids or if some of the particle disappear. """ return ParticleTrajectories( self, indices, fields=fields, suppress_logging=suppress_logging, ptype=ptype ) def _get_by_attribute( self, attribute: str, value: unyt_quantity | tuple[float, Unit | str], tolerance: None | unyt_quantity | tuple[float, Unit | str] = None, prefer: Literal["nearest", "smaller", "larger"] = "nearest", ) -> "Dataset": r""" Get a dataset at or near to a given value. Parameters ---------- attribute : str The key by which to retrieve an output, usually 'current_time' or 'current_redshift'. The key must be an attribute of the dataset and monotonic. value : unyt_quantity or (value, unit) The value to search for. tolerance : unyt_quantity or (value, unit), optional If not None, do not return a dataset unless the value is within the tolerance value. If None, simply return the nearest dataset. Default: None. prefer : str The side of the value to return. Can be 'nearest', 'smaller' or 'larger'. Default: 'nearest'. """ if prefer not in ("nearest", "smaller", "larger"): raise ValueError( f"Side must be 'nearest', 'smaller' or 'larger', got {prefer}." ) # Use a binary search to find the closest value iL = 0 iR = len(self._pre_outputs) - 1 if iL == iR: ds = self[0] if ( tolerance is not None and abs(getattr(ds, attribute) - value) > tolerance ): raise ValueError( f"No dataset found with {attribute} within {tolerance} of {value}." ) return ds # Check signedness dsL = self[iL] dsR = self[iR] vL = getattr(dsL, attribute) vR = getattr(dsR, attribute) if vL < vR: sign = 1 elif vL > vR: sign = -1 else: raise ValueError( f"{dsL} and {dsR} have both {attribute}={vL}, cannot perform search." ) if isinstance(value, tuple): value = dsL.quan(*value) if isinstance(tolerance, tuple): tolerance = dsL.quan(*tolerance) # Short-circuit if value is out-of-range if not (vL * sign < value * sign < vR * sign): iL = iR = 0 while iR - iL > 1: iM = (iR + iL) // 2 dsM = self[iM] vM = getattr(dsM, attribute) if sign * value < sign * vM: iR = iM dsR = dsM elif sign * value > sign * vM: iL = iM dsL = dsM else: # Exact match dsL = dsR = dsM break if prefer == "smaller": ds_best = dsL if sign > 0 else dsR elif prefer == "larger": ds_best = dsR if sign > 0 else dsL elif abs(value - getattr(dsL, attribute)) < abs( value - getattr(dsR, attribute) ): ds_best = dsL else: ds_best = dsR if tolerance is not None: if abs(value - getattr(ds_best, attribute)) > tolerance: raise ValueError( f"No dataset found with {attribute} within {tolerance} of {value}." ) return ds_best def get_by_time( self, time: unyt_quantity | tuple[float, Unit | str], tolerance: None | unyt_quantity | tuple[float, Unit | str] = None, prefer: Literal["nearest", "smaller", "larger"] = "nearest", ) -> "Dataset": """ Get a dataset at or near to a given time. Parameters ---------- time : unyt_quantity or (value, unit) The time to search for. tolerance : unyt_quantity or (value, unit) If not None, do not return a dataset unless the time is within the tolerance value. If None, simply return the nearest dataset. Default: None. prefer : str The side of the value to return. Can be 'nearest', 'smaller' or 'larger'. Default: 'nearest'. Examples -------- >>> ds = ts.get_by_time((12, "Gyr")) >>> t = ts[0].quan(12, "Gyr") ... ds = ts.get_by_time(t, tolerance=(100, "Myr")) """ return self._get_by_attribute( "current_time", time, tolerance=tolerance, prefer=prefer ) def get_by_redshift( self, redshift: float, tolerance: float | None = None, prefer: Literal["nearest", "smaller", "larger"] = "nearest", ) -> "Dataset": """ Get a dataset at or near to a given time. Parameters ---------- redshift : float The redshift to search for. tolerance : float If not None, do not return a dataset unless the redshift is within the tolerance value. If None, simply return the nearest dataset. Default: None. prefer : str The side of the value to return. Can be 'nearest', 'smaller' or 'larger'. Default: 'nearest'. Examples -------- >>> ds = ts.get_by_redshift(0.0) """ return self._get_by_attribute( "current_redshift", redshift, tolerance=tolerance, prefer=prefer ) class TimeSeriesQuantitiesContainer: def __init__(self, data_object, quantities): self.data_object = data_object self.quantities = quantities def __getitem__(self, key): if key not in self.quantities: raise KeyError(key) q = self.quantities[key] def run_quantity_wrapper(quantity, quantity_name): @wraps(derived_quantity_registry[quantity_name][1]) def run_quantity(*args, **kwargs): to_run = quantity(*args, **kwargs) return self.data_object.eval(to_run) return run_quantity return run_quantity_wrapper(q, key) class DatasetSeriesObject: def __init__(self, time_series, data_object_name, *args, **kwargs): self.time_series = weakref.proxy(time_series) self.data_object_name = data_object_name self._args = args self._kwargs = kwargs qs = { qn: create_quantity_proxy(qv) for qn, qv in derived_quantity_registry.items() } self.quantities = TimeSeriesQuantitiesContainer(self, qs) def eval(self, tasks): return self.time_series.eval(tasks, self) def get(self, ds): # We get the type name, which corresponds to an attribute of the # index cls = getattr(ds, self.data_object_name) return cls(*self._args, **self._kwargs) class SimulationTimeSeries(DatasetSeries, ABC): def __init__(self, parameter_filename, find_outputs=False): """ Base class for generating simulation time series types. Principally consists of a *parameter_filename*. """ if not os.path.exists(parameter_filename): raise FileNotFoundError(parameter_filename) self.parameter_filename = parameter_filename self.basename = os.path.basename(parameter_filename) self.directory = os.path.dirname(parameter_filename) self.parameters = {} self.key_parameters = [] # Set some parameter defaults. self._set_parameter_defaults() # Read the simulation dataset. self._parse_parameter_file() # Set units self._set_units() # Figure out the starting and stopping times and redshift. self._calculate_simulation_bounds() # Get all possible datasets. self._get_all_outputs(find_outputs=find_outputs) self.print_key_parameters() def _set_parameter_defaults(self): # noqa: B027 pass @abstractmethod def _parse_parameter_file(self): pass @abstractmethod def _set_units(self): pass @abstractmethod def _calculate_simulation_bounds(self): pass @abstractmethod def _get_all_outputs(self, *, find_outputs=False): pass def __repr__(self): return self.parameter_filename _arr = None @property def arr(self): if self._arr is not None: return self._arr self._arr = functools.partial(YTArray, registry=self.unit_registry) return self._arr _quan = None @property def quan(self): if self._quan is not None: return self._quan self._quan = functools.partial(YTQuantity, registry=self.unit_registry) return self._quan @parallel_root_only def print_key_parameters(self): """ Print out some key parameters for the simulation. """ if self.simulation_type == "grid": for a in ["domain_dimensions", "domain_left_edge", "domain_right_edge"]: self._print_attr(a) for a in ["initial_time", "final_time", "cosmological_simulation"]: self._print_attr(a) if getattr(self, "cosmological_simulation", False): for a in [ "box_size", "omega_matter", "omega_lambda", "omega_radiation", "hubble_constant", "initial_redshift", "final_redshift", ]: self._print_attr(a) for a in self.key_parameters: self._print_attr(a) mylog.info("Total datasets: %d.", len(self.all_outputs)) def _print_attr(self, a): """ Print the attribute or warn about it missing. """ if not hasattr(self, a): mylog.error("Missing %s in dataset definition!", a) return v = getattr(self, a) mylog.info("Parameters: %-25s = %s", a, v) def _get_outputs_by_key(self, key, values, tolerance=None, outputs=None): r""" Get datasets at or near to given values. Parameters ---------- key : str The key by which to retrieve outputs, usually 'time' or 'redshift'. values : array_like A list of values, given as floats. tolerance : float If not None, do not return a dataset unless the value is within the tolerance value. If None, simply return the nearest dataset. Default: None. outputs : list The list of outputs from which to choose. If None, self.all_outputs is used. Default: None. Examples -------- >>> datasets = es.get_outputs_by_key("redshift", [0, 1, 2], tolerance=0.1) """ if not isinstance(values, YTArray): if isinstance(values, tuple) and len(values) == 2: values = self.arr(*values) else: values = self.arr(values) values = values.in_base() if outputs is None: outputs = self.all_outputs my_outputs = [] if not outputs: return my_outputs for value in values: outputs.sort(key=lambda obj, value=value: np.abs(value - obj[key])) if ( tolerance is None or np.abs(value - outputs[0][key]) <= tolerance ) and outputs[0] not in my_outputs: my_outputs.append(outputs[0]) else: mylog.error("No dataset added for %s = %f.", key, value) outputs.sort(key=lambda obj: obj["time"]) return my_outputs yt-project-yt-f043ac8/yt/data_objects/unions.py000066400000000000000000000011201510711153200216070ustar00rootroot00000000000000from abc import ABC, abstractmethod from more_itertools import always_iterable class Union(ABC): @property @abstractmethod def _union_type(self) -> str: ... def __init__(self, name, sub_types): self.name = name self.sub_types = list(always_iterable(sub_types)) def __iter__(self): yield from self.sub_types def __repr__(self): return f"{self._union_type.capitalize()} Union: '{self.name}' composed of: {self.sub_types}" class MeshUnion(Union): _union_type = "mesh" class ParticleUnion(Union): _union_type = "particle" yt-project-yt-f043ac8/yt/default.mplstyle000066400000000000000000000004311510711153200205230ustar00rootroot00000000000000# basic usage (requires matplotlib 3.7+) # >>> import matplotlib as mpl # >>> mpl.style.use("yt.default") xtick.top: True ytick.right: True xtick.minor.visible: True ytick.minor.visible: True xtick.direction: in ytick.direction: in font.family: stixgeneral mathtext.fontset: cm yt-project-yt-f043ac8/yt/extensions/000077500000000000000000000000001510711153200175055ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/extensions/__init__.py000066400000000000000000000016011510711153200216140ustar00rootroot00000000000000""" yt.extensions ~~~~~~~~~~~~~ Redirect imports for extensions. This module basically makes it possible for us to transition from ytext.foo to ytext_foo without having to force all extensions to upgrade at the same time. When a user does ``from yt.extensions.foo import bar`` it will attempt to import ``from yt_foo import bar`` first and when that fails it will try to import ``from ytext.foo import bar``. We're switching from namespace packages because it was just too painful for everybody involved. :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ # This source code is originally from flask, in the flask/ext/__init__.py file. def setup(): from ..exthook import ExtensionImporter importer = ExtensionImporter(["yt_%s", "ytext.%s"], __name__) importer.install() setup() del setup yt-project-yt-f043ac8/yt/exthook.py000066400000000000000000000117641510711153200173520ustar00rootroot00000000000000""" yt.exthook ~~~~~~~~~~ Redirect imports for extensions. This module basically makes it possible for us to transition from ytext.foo to yt_foo without having to force all extensions to upgrade at the same time. When a user does ``from yt.extensions.foo import bar`` it will attempt to import ``from yt_foo import bar`` first and when that fails it will try to import ``from ytext.foo import bar``. We're switching from namespace packages because it was just too painful for everybody involved. This is used by `yt.extensions`. :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ # This source code was originally in flask/exthook.py import os import sys class ExtensionImporter: """This importer redirects imports from this submodule to other locations. This makes it possible to transition from the old flaskext.name to the newer flask_name without people having a hard time. """ def __init__(self, module_choices, wrapper_module): self.module_choices = module_choices self.wrapper_module = wrapper_module self.prefix = wrapper_module + "." self.prefix_cutoff = wrapper_module.count(".") + 1 def __eq__(self, other): return ( self.__class__.__module__ == other.__class__.__module__ and self.__class__.__name__ == other.__class__.__name__ and self.wrapper_module == other.wrapper_module and self.module_choices == other.module_choices ) def __ne__(self, other): return not self.__eq__(other) def install(self): sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] def find_module(self, fullname, path=None): if fullname.startswith(self.prefix): return self def load_module(self, fullname): if fullname in sys.modules: return sys.modules[fullname] modname = fullname.split(".", self.prefix_cutoff)[self.prefix_cutoff] for path in self.module_choices: realname = path % modname try: __import__(realname) except ImportError: exc_type, exc_value, tb = sys.exc_info() # since we only establish the entry in sys.modules at the # very this seems to be redundant, but if recursive imports # happen we will call into the move import a second time. # On the second invocation we still don't have an entry for # fullname in sys.modules, but we will end up with the same # fake module name and that import will succeed since this # one already has a temporary entry in the modules dict. # Since this one "succeeded" temporarily that second # invocation now will have created a fullname entry in # sys.modules which we have to kill. sys.modules.pop(fullname, None) # If it's an important traceback we reraise it, otherwise # we swallow it and try the next choice. The skipped frame # is the one from __import__ above which we don't care about if self.is_important_traceback(realname, tb): raise exc_value.with_traceback(tb.tb_next) # noqa: B904 continue module = sys.modules[fullname] = sys.modules[realname] if "." not in modname: setattr(sys.modules[self.wrapper_module], modname, module) return module raise ImportError(f"No module named {fullname}") def is_important_traceback(self, important_module, tb): """Walks a traceback's frames and checks if any of the frames originated in the given important module. If that is the case then we were able to import the module itself but apparently something went wrong when the module was imported. (Eg: import of an import failed). """ while tb is not None: if self.is_important_frame(important_module, tb): return True tb = tb.tb_next return False def is_important_frame(self, important_module, tb): """Checks a single frame if it's important.""" g = tb.tb_frame.f_globals if "__name__" not in g: return False module_name = g["__name__"] # Python 2.7 Behavior. Modules are cleaned up late so the # name shows up properly here. Success! if module_name == important_module: return True # Some python versions will clean up modules so early that the # module name at that point is no longer set. Try guessing from # the filename then. filename = os.path.abspath(tb.tb_frame.f_code.co_filename) test_string = os.path.sep + important_module.replace(".", os.path.sep) return ( test_string + ".py" in filename or test_string + os.path.sep + "__init__.py" in filename ) yt-project-yt-f043ac8/yt/fields/000077500000000000000000000000001510711153200165545ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/fields/__init__.py000066400000000000000000000000001510711153200206530ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/fields/angular_momentum.py000066400000000000000000000074261510711153200225110ustar00rootroot00000000000000import numpy as np from yt.utilities.lib.misc_utilities import ( obtain_position_vector, obtain_relative_velocity_vector, ) from .derived_field import ValidateParameter from .field_plugin_registry import register_field_plugin from .vector_operations import create_magnitude_field @register_field_plugin def setup_angular_momentum(registry, ftype="gas", slice_info=None): # Angular momentum defined here needs to be consistent with # _particle_specific_angular_momentum in particle_fields.py unit_system = registry.ds.unit_system def _specific_angular_momentum_x(field, data): xv, yv, zv = obtain_relative_velocity_vector(data) rv = obtain_position_vector(data) units = rv.units rv = np.rollaxis(rv, 0, len(rv.shape)) rv = data.ds.arr(rv, units=units) return rv[..., 1] * zv - rv[..., 2] * yv def _specific_angular_momentum_y(field, data): xv, yv, zv = obtain_relative_velocity_vector(data) rv = obtain_position_vector(data) units = rv.units rv = np.rollaxis(rv, 0, len(rv.shape)) rv = data.ds.arr(rv, units=units) return rv[..., 2] * xv - rv[..., 0] * zv def _specific_angular_momentum_z(field, data): xv, yv, zv = obtain_relative_velocity_vector(data) rv = obtain_position_vector(data) units = rv.units rv = np.rollaxis(rv, 0, len(rv.shape)) rv = data.ds.arr(rv, units=units) return rv[..., 0] * yv - rv[..., 1] * xv registry.add_field( (ftype, "specific_angular_momentum_x"), sampling_type="local", function=_specific_angular_momentum_x, units=unit_system["specific_angular_momentum"], validators=[ValidateParameter("center"), ValidateParameter("bulk_velocity")], ) registry.add_field( (ftype, "specific_angular_momentum_y"), sampling_type="local", function=_specific_angular_momentum_y, units=unit_system["specific_angular_momentum"], validators=[ValidateParameter("center"), ValidateParameter("bulk_velocity")], ) registry.add_field( (ftype, "specific_angular_momentum_z"), sampling_type="local", function=_specific_angular_momentum_z, units=unit_system["specific_angular_momentum"], validators=[ValidateParameter("center"), ValidateParameter("bulk_velocity")], ) create_magnitude_field( registry, "specific_angular_momentum", unit_system["specific_angular_momentum"], ftype=ftype, ) def _angular_momentum_x(field, data): return data[ftype, "mass"] * data[ftype, "specific_angular_momentum_x"] registry.add_field( (ftype, "angular_momentum_x"), sampling_type="local", function=_angular_momentum_x, units=unit_system["angular_momentum"], validators=[ValidateParameter("center"), ValidateParameter("bulk_velocity")], ) def _angular_momentum_y(field, data): return data[ftype, "mass"] * data[ftype, "specific_angular_momentum_y"] registry.add_field( (ftype, "angular_momentum_y"), sampling_type="local", function=_angular_momentum_y, units=unit_system["angular_momentum"], validators=[ValidateParameter("center"), ValidateParameter("bulk_velocity")], ) def _angular_momentum_z(field, data): return data[ftype, "mass"] * data[ftype, "specific_angular_momentum_z"] registry.add_field( (ftype, "angular_momentum_z"), sampling_type="local", function=_angular_momentum_z, units=unit_system["angular_momentum"], validators=[ValidateParameter("center"), ValidateParameter("bulk_velocity")], ) create_magnitude_field( registry, "angular_momentum", unit_system["angular_momentum"], ftype=ftype ) yt-project-yt-f043ac8/yt/fields/api.py000066400000000000000000000013161510711153200177000ustar00rootroot00000000000000# from . import species_fields from . import ( angular_momentum, astro_fields, cosmology_fields, fluid_fields, fluid_vector_fields, geometric_fields, local_fields, magnetic_field, my_plugin_fields, particle_fields, vector_operations, ) from .derived_field import ( DerivedField, ValidateDataField, ValidateGridType, ValidateParameter, ValidateProperty, ValidateSpatial, ) from .field_detector import FieldDetector from .field_info_container import FieldInfoContainer from .field_plugin_registry import field_plugins, register_field_plugin from .local_fields import add_field, derived_field from .xray_emission_fields import add_xray_emissivity_field yt-project-yt-f043ac8/yt/fields/astro_fields.py000066400000000000000000000117361510711153200216140ustar00rootroot00000000000000import numpy as np from .derived_field import ValidateParameter from .field_plugin_registry import register_field_plugin from .vector_operations import create_magnitude_field @register_field_plugin def setup_astro_fields(registry, ftype="gas", slice_info=None): unit_system = registry.ds.unit_system pc = registry.ds.units.physical_constants # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info def _dynamical_time(field, data): """ sqrt(3 pi / (16 G rho)) """ return np.sqrt(3.0 * np.pi / (16.0 * pc.G * data[ftype, "density"])) registry.add_field( (ftype, "dynamical_time"), sampling_type="local", function=_dynamical_time, units=unit_system["time"], ) def _jeans_mass(field, data): MJ_constant = (((5.0 * pc.kboltz) / (pc.G * pc.mh)) ** 1.5) * ( 3.0 / (4.0 * np.pi) ) ** 0.5 u = ( MJ_constant * ( (data[ftype, "temperature"] / data[ftype, "mean_molecular_weight"]) ** 1.5 ) * (data[ftype, "density"] ** (-0.5)) ) return u registry.add_field( (ftype, "jeans_mass"), sampling_type="local", function=_jeans_mass, units=unit_system["mass"], ) def _emission_measure(field, data): dV = data[ftype, "mass"] / data[ftype, "density"] nenhdV = data[ftype, "H_nuclei_density"] * dV nenhdV *= data[ftype, "El_number_density"] return nenhdV registry.add_field( (ftype, "emission_measure"), sampling_type="local", function=_emission_measure, units=unit_system["number_density"], ) def _mazzotta_weighting(field, data): # Spectroscopic-like weighting field for galaxy clusters # Only useful as a weight_field for temperature, metallicity, velocity ret = data[ftype, "El_number_density"].d ** 2 ret *= data[ftype, "kT"].d ** -0.75 return ret registry.add_field( (ftype, "mazzotta_weighting"), sampling_type="local", function=_mazzotta_weighting, units="", ) def _optical_depth(field, data): return data[ftype, "El_number_density"] * pc.sigma_thompson registry.add_field( (ftype, "optical_depth"), sampling_type="local", function=_optical_depth, units=unit_system["length"] ** -1, ) def _sz_kinetic(field, data): # minus sign is because radial velocity is WRT viewer # See issue #1225 return -data[ftype, "velocity_los"] * data[ftype, "optical_depth"] / pc.clight registry.add_field( (ftype, "sz_kinetic"), sampling_type="local", function=_sz_kinetic, units=unit_system["length"] ** -1, validators=[ValidateParameter("axis", {"axis": [0, 1, 2]})], ) def _szy(field, data): kT = data[ftype, "kT"] / (pc.me * pc.clight * pc.clight) return data[ftype, "optical_depth"] * kT registry.add_field( (ftype, "szy"), sampling_type="local", function=_szy, units=unit_system["length"] ** -1, ) def _entropy(field, data): mgammam1 = -2.0 / 3.0 tr = data[ftype, "kT"] * data[ftype, "El_number_density"] ** mgammam1 return data.apply_units(tr, field.units) registry.add_field( (ftype, "entropy"), sampling_type="local", units="keV*cm**2", function=_entropy ) def _lorentz_factor(field, data): b2 = data[ftype, "velocity_magnitude"].to_value("c") b2 *= b2 return 1.0 / np.sqrt(1.0 - b2) registry.add_field( (ftype, "lorentz_factor"), sampling_type="local", units="", function=_lorentz_factor, ) # 4-velocity spatial components def four_velocity_xyz(u): def _four_velocity(field, data): return data["gas", f"velocity_{u}"] * data["gas", "lorentz_factor"] return _four_velocity for u in registry.ds.coordinates.axis_order: registry.add_field( ("gas", f"four_velocity_{u}"), sampling_type="local", function=four_velocity_xyz(u), units=unit_system["velocity"], ) # 4-velocity t-component def _four_velocity_t(field, data): return data["gas", "lorentz_factor"] * pc.clight registry.add_field( ("gas", "four_velocity_t"), sampling_type="local", function=_four_velocity_t, units=unit_system["velocity"], ) create_magnitude_field( registry, "four_velocity", unit_system["velocity"], ftype=ftype, ) yt-project-yt-f043ac8/yt/fields/astro_simulations.py000066400000000000000000000031611510711153200227060ustar00rootroot00000000000000from .domain_context import DomainContext # Here's how this all works: # # 1. We have a mapping here that defines fields we might expect to find in an # astrophysical simulation to units (not necessarily definitive) that we # may want to use them in. # 2. Simulations and frontends will register aliases from fields (which can # utilize units) to the fields enumerated here. # 3. This plugin can call additional plugins on the registry. class AstroSimulation(DomainContext): # This is an immutable of immutables. Note that we do not specify the # fluid type here, although in most cases we expect it to be "gas". _known_fluid_fields = ( ("density", "g/cm**3"), ("number_density", "1/cm**3"), ("pressure", "dyne / cm**2"), ("specific_thermal_energy", "erg / g"), ("temperature", "K"), ("velocity_x", "cm / s"), ("velocity_y", "cm / s"), ("velocity_z", "cm / s"), ("magnetic_field_x", "gauss"), ("magnetic_field_y", "gauss"), ("magnetic_field_z", "gauss"), ("radiation_acceleration_x", "cm / s**2"), ("radiation_acceleration_y", "cm / s**2"), ("radiation_acceleration_z", "cm / s**2"), ) # This set of fields can be applied to any particle type. _known_particle_fields = ( ("particle_position_x", "cm"), ("particle_position_y", "cm"), ("particle_position_z", "cm"), ("particle_velocity_x", "cm / s"), ("particle_velocity_y", "cm / s"), ("particle_velocity_z", "cm / s"), ("particle_mass", "g"), ("particle_index", ""), ) yt-project-yt-f043ac8/yt/fields/cosmology_fields.py000066400000000000000000000130411510711153200224660ustar00rootroot00000000000000from .derived_field import ValidateParameter from .field_exceptions import NeedsConfiguration, NeedsParameter from .field_plugin_registry import register_field_plugin @register_field_plugin def setup_cosmology_fields(registry, ftype="gas", slice_info=None): unit_system = registry.ds.unit_system # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info def _matter_density(field, data): return data[ftype, "density"] + data[ftype, "dark_matter_density"] registry.add_field( (ftype, "matter_density"), sampling_type="local", function=_matter_density, units=unit_system["density"], ) def _matter_mass(field, data): return data[ftype, "matter_density"] * data["index", "cell_volume"] registry.add_field( (ftype, "matter_mass"), sampling_type="local", function=_matter_mass, units=unit_system["mass"], ) # rho_total / rho_cr(z). def _overdensity(field, data): if ( not hasattr(data.ds, "cosmological_simulation") or not data.ds.cosmological_simulation ): raise NeedsConfiguration("cosmological_simulation", 1) co = data.ds.cosmology return data[ftype, "matter_density"] / co.critical_density( data.ds.current_redshift ) registry.add_field( (ftype, "overdensity"), sampling_type="local", function=_overdensity, units="" ) # rho_baryon / def _baryon_overdensity(field, data): if ( not hasattr(data.ds, "cosmological_simulation") or not data.ds.cosmological_simulation ): raise NeedsConfiguration("cosmological_simulation", 1) omega_baryon = data.get_field_parameter("omega_baryon") if omega_baryon is None: raise NeedsParameter("omega_baryon") co = data.ds.cosmology # critical_density(z) ~ omega_lambda + omega_matter * (1 + z)^3 # mean matter density(z) ~ omega_matter * (1 + z)^3 return ( data[ftype, "density"] / omega_baryon / co.critical_density(0.0) / (1.0 + data.ds.current_redshift) ** 3 ) registry.add_field( (ftype, "baryon_overdensity"), sampling_type="local", function=_baryon_overdensity, units="", validators=[ValidateParameter("omega_baryon")], ) # rho_matter / def _matter_overdensity(field, data): if ( not hasattr(data.ds, "cosmological_simulation") or not data.ds.cosmological_simulation ): raise NeedsConfiguration("cosmological_simulation", 1) co = data.ds.cosmology # critical_density(z) ~ omega_lambda + omega_matter * (1 + z)^3 # mean density(z) ~ omega_matter * (1 + z)^3 return ( data[ftype, "matter_density"] / data.ds.omega_matter / co.critical_density(0.0) / (1.0 + data.ds.current_redshift) ** 3 ) registry.add_field( (ftype, "matter_overdensity"), sampling_type="local", function=_matter_overdensity, units="", ) # r / r_vir def _virial_radius_fraction(field, data): virial_radius = data.get_field_parameter("virial_radius") if virial_radius == 0.0: ret = 0.0 else: ret = data["index", "radius"] / virial_radius return ret registry.add_field( ("index", "virial_radius_fraction"), sampling_type="local", function=_virial_radius_fraction, validators=[ValidateParameter("virial_radius")], units="", ) # Weak lensing convergence. # Eqn 4 of Metzler, White, & Loken (2001, ApJ, 547, 560). # This needs to be checked for accuracy. def _weak_lensing_convergence(field, data): if ( not hasattr(data.ds, "cosmological_simulation") or not data.ds.cosmological_simulation ): raise NeedsConfiguration("cosmological_simulation", 1) co = data.ds.cosmology pc = data.ds.units.physical_constants observer_redshift = data.get_field_parameter("observer_redshift") source_redshift = data.get_field_parameter("source_redshift") # observer to lens dl = co.angular_diameter_distance(observer_redshift, data.ds.current_redshift) # observer to source ds = co.angular_diameter_distance(observer_redshift, source_redshift) # lens to source dls = co.angular_diameter_distance(data.ds.current_redshift, source_redshift) # removed the factor of 1 / a to account for the fact that we are projecting # with a proper distance. return ( 1.5 * (co.hubble_constant / pc.clight) ** 2 * (dl * dls / ds) * data[ftype, "matter_overdensity"] ).in_units("1/cm") registry.add_field( (ftype, "weak_lensing_convergence"), sampling_type="local", function=_weak_lensing_convergence, units=unit_system["length"] ** -1, validators=[ ValidateParameter("observer_redshift"), ValidateParameter("source_redshift"), ], ) yt-project-yt-f043ac8/yt/fields/derived_field.py000066400000000000000000000506551510711153200217260ustar00rootroot00000000000000import contextlib import inspect import re from collections.abc import Iterable from typing import Optional from more_itertools import always_iterable import yt.units.dimensions as ytdims from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import FieldKey from yt.funcs import iter_fields, validate_field_key from yt.units.unit_object import Unit # type: ignore from yt.utilities.exceptions import YTFieldNotFound from yt.utilities.logger import ytLogger as mylog from yt.visualization._commons import _get_units_label from .field_detector import FieldDetector from .field_exceptions import ( FieldUnitsError, NeedsDataField, NeedsGridType, NeedsOriginalGrid, NeedsParameter, NeedsProperty, ) def TranslationFunc(field_name): def _TranslationFunc(field, data): # We do a bunch of in-place modifications, so we will copy this. return data[field_name].copy() _TranslationFunc.alias_name = field_name return _TranslationFunc def NullFunc(field, data): raise YTFieldNotFound(field.name) def DeprecatedFieldFunc(ret_field, func, since, removal): def _DeprecatedFieldFunc(field, data): # Only log a warning if we've already done # field detection if data.ds.fields_detected: args = [field.name, since] msg = "The Derived Field %s is deprecated as of yt v%s " if removal is not None: msg += "and will be removed in yt v%s " args.append(removal) if ret_field != field.name: msg += ", use %s instead" args.append(ret_field) mylog.warning(msg, *args) return func(field, data) return _DeprecatedFieldFunc class DerivedField: """ This is the base class used to describe a cell-by-cell derived field. Parameters ---------- name : str is the name of the field. function : callable A function handle that defines the field. Should accept arguments (field, data) units : str A plain text string encoding the unit, or a query to a unit system of a dataset. Powers must be in Python syntax (** instead of ^). If set to 'auto' or None (default), units will be inferred from the return value of the field function. take_log : bool Describes whether the field should be logged validators : list A list of :class:`FieldValidator` objects sampling_type : string, default = "cell" How is the field sampled? This can be one of the following options at present: "cell" (cell-centered), "discrete" (or "particle") for discretely sampled data. vector_field : bool Describes the dimensionality of the field. Currently unused. display_field : bool Governs its appearance in the dropdowns in Reason not_in_all : bool Used for baryon fields from the data that are not in all the grids display_name : str A name used in the plots output_units : str For fields that exist on disk, which we may want to convert to other fields or that get aliased to themselves, we can specify a different desired output unit than the unit found on disk. dimensions : str or object from yt.units.dimensions The dimensions of the field, only used for error checking with units='auto'. nodal_flag : array-like with three components This describes how the field is centered within a cell. If nodal_flag is [0, 0, 0], then the field is cell-centered. If any of the components of nodal_flag are 1, then the field is nodal in that direction, meaning it is defined at the lo and hi sides of the cell rather than at the center. For example, a field with nodal_flag = [1, 0, 0] would be defined at the middle of the 2 x-faces of each cell. nodal_flag = [0, 1, 1] would mean the that the field defined at the centers of the 4 edges that are normal to the x axis, while nodal_flag = [1, 1, 1] would be defined at the 8 cell corners. """ _inherited_particle_filter = False def __init__( self, name: FieldKey, sampling_type, function, units: str | bytes | Unit | None = None, take_log=True, validators=None, vector_field=False, display_field=True, not_in_all=False, display_name=None, output_units=None, dimensions=None, ds=None, nodal_flag=None, *, alias: Optional["DerivedField"] = None, ): validate_field_key(name) self.name = name self.take_log = take_log self.display_name = display_name self.not_in_all = not_in_all self.display_field = display_field self.sampling_type = sampling_type self.vector_field = vector_field self.ds = ds if self.ds is not None: self._ionization_label_format = self.ds._ionization_label_format else: self._ionization_label_format = "roman_numeral" if nodal_flag is None: self.nodal_flag = [0, 0, 0] else: self.nodal_flag = nodal_flag self._function = function self.validators = list(always_iterable(validators)) # handle units self.units: str | bytes | Unit | None if units in (None, "auto"): self.units = None elif isinstance(units, str): self.units = units elif isinstance(units, Unit): self.units = str(units) elif isinstance(units, bytes): self.units = units.decode("utf-8") else: raise FieldUnitsError( f"Cannot handle units {units!r} (type {type(units)}). " "Please provide a string or Unit object." ) if output_units is None: output_units = self.units self.output_units = output_units if isinstance(dimensions, str): dimensions = getattr(ytdims, dimensions) self.dimensions = dimensions if alias is None: self._shared_aliases_list = [self] else: self._shared_aliases_list = alias._shared_aliases_list self._shared_aliases_list.append(self) def _copy_def(self): dd = {} dd["name"] = self.name dd["units"] = self.units dd["take_log"] = self.take_log dd["validators"] = list(self.validators) dd["sampling_type"] = self.sampling_type dd["vector_field"] = self.vector_field dd["display_field"] = True dd["not_in_all"] = self.not_in_all dd["display_name"] = self.display_name return dd @property def is_sph_field(self): if self.sampling_type == "cell": return False is_sph_field = False if self.is_alias: name = self.alias_name else: name = self.name if hasattr(self.ds, "_sph_ptypes"): is_sph_field |= name[0] in (self.ds._sph_ptypes + ("gas",)) return is_sph_field @property def local_sampling(self): return self.sampling_type in ("discrete", "particle", "local") def get_units(self): if self.ds is not None: u = Unit(self.units, registry=self.ds.unit_registry) else: u = Unit(self.units) return u.latex_representation() def get_projected_units(self): if self.ds is not None: u = Unit(self.units, registry=self.ds.unit_registry) else: u = Unit(self.units) return (u * Unit("cm")).latex_representation() def check_available(self, data): """ This raises an exception of the appropriate type if the set of validation mechanisms are not met, and otherwise returns True. """ for validator in self.validators: validator(data) # If we don't get an exception, we're good to go return True def get_dependencies(self, *args, **kwargs): """ This returns a list of names of fields that this field depends on. """ e = FieldDetector(*args, **kwargs) e[self.name] return e def _get_needed_parameters(self, fd): params = [] values = [] permute_params = {} vals = [v for v in self.validators if isinstance(v, ValidateParameter)] for val in vals: if val.parameter_values is not None: permute_params.update(val.parameter_values) else: params.extend(val.parameters) values.extend([fd.get_field_parameter(fp) for fp in val.parameters]) return dict(zip(params, values, strict=True)), permute_params _unit_registry = None @contextlib.contextmanager def unit_registry(self, data): old_registry = self._unit_registry if hasattr(data, "unit_registry"): ur = data.unit_registry elif hasattr(data, "ds"): ur = data.ds.unit_registry else: ur = None self._unit_registry = ur yield self._unit_registry = old_registry def __call__(self, data): """Return the value of the field in a given *data* object.""" self.check_available(data) original_fields = data.keys() # Copy if self._function is NullFunc: raise RuntimeError( "Something has gone terribly wrong, _function is NullFunc " + f"for {self.name}" ) with self.unit_registry(data): dd = self._function(self, data) for field_name in data.keys(): if field_name not in original_fields: del data[field_name] return dd def get_source(self): """ Return a string containing the source of the function (if possible.) """ return inspect.getsource(self._function) def get_label(self, projected=False): """ Return a data label for the given field, including units. """ name = self.name[1] if self.display_name is not None: name = self.display_name # Start with the field name data_label = rf"$\rm{{{name}}}" # Grab the correct units if projected: raise NotImplementedError else: if self.ds is not None: units = Unit(self.units, registry=self.ds.unit_registry) else: units = Unit(self.units) # Add unit label if not units.is_dimensionless: data_label += _get_units_label(units.latex_representation()).strip("$") data_label += r"$" return data_label @property def alias_field(self) -> bool: issue_deprecation_warning( "DerivedField.alias_field is a deprecated equivalent to DerivedField.is_alias ", stacklevel=3, since="4.1", ) return self.is_alias @property def is_alias(self) -> bool: return self._shared_aliases_list.index(self) > 0 def is_alias_to(self, other: "DerivedField") -> bool: return self._shared_aliases_list is other._shared_aliases_list @property def alias_name(self) -> FieldKey | None: if self.is_alias: return self._shared_aliases_list[0].name return None def __repr__(self): if self._function is NullFunc: s = "On-Disk Field " elif self.is_alias: s = f"Alias Field for {self.alias_name!r} " else: s = "Derived Field " s += f"{self.name!r}: (units: {self.units!r}" if self.display_name is not None: s += f", display_name: {self.display_name!r}" if self.sampling_type == "particle": s += ", particle field" s += ")" return s def _is_ion(self): p = re.compile("_p[0-9]+_") result = False if p.search(self.name[1]) is not None: result = True return result def _ion_to_label(self): # check to see if the output format has changed if self.ds is not None: self._ionization_label_format = self.ds._ionization_label_format pnum2rom = { "0": "I", "1": "II", "2": "III", "3": "IV", "4": "V", "5": "VI", "6": "VII", "7": "VIII", "8": "IX", "9": "X", "10": "XI", "11": "XII", "12": "XIII", "13": "XIV", "14": "XV", "15": "XVI", "16": "XVII", "17": "XVIII", "18": "XIX", "19": "XX", "20": "XXI", "21": "XXII", "22": "XXIII", "23": "XXIV", "24": "XXV", "25": "XXVI", "26": "XXVII", "27": "XXVIII", "28": "XXIX", "29": "XXX", } # first look for charge to decide if it is an ion p = re.compile("_p[0-9]+_") m = p.search(self.name[1]) if m is not None: # Find the ionization state pstr = m.string[m.start() + 1 : m.end() - 1] segments = self.name[1].split("_") # find the ionization index for i, s in enumerate(segments): if s == pstr: ipstr = i for i, s in enumerate(segments): # If its the species we don't want to change the capitalization if i == ipstr - 1: continue segments[i] = s.capitalize() species = segments[ipstr - 1] # If there is a number in the species part of the label # that indicates part of a molecule symbols = [] for symb in species: # can't just use underscore b/c gets replaced later with space if symb.isdigit(): symbols.append("latexsub{" + symb + "}") else: symbols.append(symb) species_label = "".join(symbols) # Use roman numerals for ionization if self._ionization_label_format == "roman_numeral": roman = pnum2rom[pstr[1:]] label = ( species_label + r"\ " + roman + r"\ " + r"\ ".join(segments[ipstr + 1 :]) ) # use +/- for ionization else: sign = "+" * int(pstr[1:]) label = ( "{" + species_label + "}" + "^{" + sign + "}" + r"\ " + r"\ ".join(segments[ipstr + 1 :]) ) else: label = self.name[1] return label def get_latex_display_name(self): label = self.display_name if label is None: if self._is_ion(): fname = self._ion_to_label() label = r"$\rm{" + fname.replace("_", r"\ ") + r"}$" label = label.replace("latexsub", "_") else: label = r"$\rm{" + self.name[1].replace("_", r"\ ").title() + r"}$" elif label.find("$") == -1: label = label.replace(" ", r"\ ") label = r"$\rm{" + label + r"}$" return label def __copy__(self): # a shallow copy doesn't copy the _shared_alias_list attr # This method is implemented in support to ParticleFilter.wrap_func return type(self)( name=self.name, sampling_type=self.sampling_type, function=self._function, units=self.units, take_log=self.take_log, validators=self.validators, vector_field=self.vector_field, display_field=self.display_field, not_in_all=self.not_in_all, display_name=self.display_name, output_units=self.output_units, dimensions=self.dimensions, ds=self.ds, nodal_flag=self.nodal_flag, ) class FieldValidator: """ Base class for FieldValidator objects. Available subclasses include: """ def __init_subclass__(cls, **kwargs): # add the new subclass to the list of subclasses in the docstring class_str = f":class:`{cls.__name__}`" if ":class:" in FieldValidator.__doc__: class_str = ", " + class_str FieldValidator.__doc__ += class_str class ValidateParameter(FieldValidator): """ A :class:`FieldValidator` that ensures the dataset has a given parameter. Parameters ---------- parameters: str, iterable[str] a single parameter or list of parameters to require parameter_values: dict If *parameter_values* is supplied, this dict should map from field parameter to a value or list of values. It will ensure that the field is available for all permutations of the field parameter. """ def __init__( self, parameters: str | Iterable[str], parameter_values: dict | None = None, ): FieldValidator.__init__(self) self.parameters = list(always_iterable(parameters)) self.parameter_values = parameter_values def __call__(self, data): doesnt_have = [] for p in self.parameters: if not data.has_field_parameter(p): doesnt_have.append(p) if len(doesnt_have) > 0: raise NeedsParameter(doesnt_have) return True class ValidateDataField(FieldValidator): """ A :class:`FieldValidator` that ensures the output file has a given data field stored in it. Parameters ---------- field: str, tuple[str, str], or any iterable of the previous types. the field or fields to require """ def __init__(self, field): FieldValidator.__init__(self) self.fields = list(iter_fields(field)) def __call__(self, data): doesnt_have = [] if isinstance(data, FieldDetector): return True for f in self.fields: if f not in data.index.field_list: doesnt_have.append(f) if len(doesnt_have) > 0: raise NeedsDataField(doesnt_have) return True class ValidateProperty(FieldValidator): """ A :class:`FieldValidator` that ensures the data object has a given python attribute. Parameters ---------- prop: str, iterable[str] the required property or properties to require """ def __init__(self, prop: str | Iterable[str]): FieldValidator.__init__(self) self.prop = list(always_iterable(prop)) def __call__(self, data): doesnt_have = [p for p in self.prop if not hasattr(data, p)] if len(doesnt_have) > 0: raise NeedsProperty(doesnt_have) return True class ValidateSpatial(FieldValidator): """ A :class:`FieldValidator` that ensures the data handed to the field is of spatial nature -- that is to say, 3-D. Parameters ---------- ghost_zones: int If supplied, will validate that the number of ghost zones required for the field is <= the available ghost zones. Default is 0. fields: Optional str, tuple[str, str], or any iterable of the previous types. The field or fields to validate. """ def __init__(self, ghost_zones: int | None = 0, fields=None): FieldValidator.__init__(self) self.ghost_zones = ghost_zones self.fields = fields def __call__(self, data): # When we say spatial information, we really mean # that it has a three-dimensional data structure if not getattr(data, "_spatial", False): raise NeedsGridType(self.ghost_zones, self.fields) if self.ghost_zones <= data._num_ghost_zones: return True raise NeedsGridType(self.ghost_zones, self.fields) class ValidateGridType(FieldValidator): """ A :class:`FieldValidator` that ensures the data handed to the field is an actual grid patch, not a covering grid of any kind. Does not accept parameters. """ def __init__(self): FieldValidator.__init__(self) def __call__(self, data): # We need to make sure that it's an actual AMR grid if isinstance(data, FieldDetector): return True if getattr(data, "_type_name", None) == "grid": return True raise NeedsOriginalGrid() yt-project-yt-f043ac8/yt/fields/domain_context.py000066400000000000000000000006351510711153200221450ustar00rootroot00000000000000import abc from yt._typing import FieldKey domain_context_registry = {} class DomainContext(abc.ABC): class __metaclass__(type): def __init__(cls, name, b, d): type.__init__(cls, name, b, d) domain_context_registry[name] = cls _known_fluid_fields: tuple[FieldKey, ...] _known_particle_fields: tuple[FieldKey, ...] def __init__(self, ds): self.ds = ds yt-project-yt-f043ac8/yt/fields/field_aliases.py000066400000000000000000000157251510711153200217240ustar00rootroot00000000000000_field_name_aliases = [ ("GridLevel", "grid_level"), ("GridIndices", "grid_indices"), ("OnesOverDx", "ones_over_dx"), ("Ones", "ones"), # ("CellsPerBin", "cells_per_bin"), ("SoundSpeed", "sound_speed"), ("RadialMachNumber", "radial_mach_number"), ("MachNumber", "mach_number"), ("CourantTimeStep", "courant_time_step"), # ("ParticleVelocityMagnitude", "particle_velocity_magnitude"), ("VelocityMagnitude", "velocity_magnitude"), ("TangentialOverVelocityMagnitude", "tangential_over_velocity_magnitude"), ("Pressure", "pressure"), ("Entropy", "entropy"), ("sph_r", "spherical_r"), ("sph_theta", "spherical_theta"), ("sph_phi", "spherical_phi"), ("cyl_R", "cylindrical_radius"), ("cyl_z", "cylindrical_z"), ("cyl_theta", "cylindrical_theta"), ("cyl_RadialVelocity", "cylindrical_radial_velocity"), ("cyl_RadialVelocityABS", "cylindrical_radial_velocity_absolute"), ("cyl_TangentialVelocity", "velocity_cylindrical_theta"), ("cyl_TangentialVelocityABS", "velocity_cylindrical_theta"), ("DynamicalTime", "dynamical_time"), ("JeansMassMsun", "jeans_mass"), ("CellMass", "cell_mass"), ("TotalMass", "total_mass"), ("StarMassMsun", "star_mass"), ("Matter_Density", "matter_density"), ("ComovingDensity", "comoving_density"), ("Overdensity", "overdensity"), ("DensityPerturbation", "density_perturbation"), ("Baryon_Overdensity", "baryon_overdensity"), ("WeakLensingConvergence", "weak_lensing_convergence"), ("CellVolume", "cell_volume"), ("ChandraEmissivity", "chandra_emissivity"), ("XRayEmissivity", "xray_emissivity"), ("SZKinetic", "sz_kinetic"), ("SZY", "szy"), ("AveragedDensity", "averaged_density"), ("DivV", "div_v"), ("AbsDivV", "div_v_absolute"), ("Contours", "contours"), ("tempContours", "temp_contours"), ("SpecificAngularMomentumX", "specific_angular_momentum_x"), ("SpecificAngularMomentumY", "specific_angular_momentum_y"), ("SpecificAngularMomentumZ", "specific_angular_momentum_z"), ("AngularMomentumX", "angular_momentum_x"), ("AngularMomentumY", "angular_momentum_y"), ("AngularMomentumZ", "angular_momentum_z"), # ("ParticleSpecificAngularMomentumX", "particle_specific_angular_momentum_x"), # ("ParticleSpecificAngularMomentumY", "particle_specific_angular_momentum_y"), # ("ParticleSpecificAngularMomentumZ", "particle_specific_angular_momentum_z"), # ("ParticleAngularMomentumX", "particle_angular_momentum_x"), # ("ParticleAngularMomentumY", "particle_angular_momentum_y"), # ("ParticleAngularMomentumZ", "particle_angular_momentum_z"), # ("ParticleRadius", "particle_radius"), ("Radius", "radius"), ("RadialVelocity", "radial_velocity"), ("RadialVelocityABS", "radial_velocity_absolute"), ("TangentialVelocity", "tangential_velocity"), ("CuttingPlaneVelocityX", "cutting_plane_velocity_x"), ("CuttingPlaneVelocityY", "cutting_plane_velocity_y"), ("CuttingPlaneBX", "cutting_plane_magnetic_field_x"), ("CuttingPlaneBy", "cutting_plane_magnetic_field_y"), ("MeanMolecularWeight", "mean_molecular_weight"), ("particle_density", "particle_density"), ("ThermalEnergy", "specific_thermal_energy"), ("TotalEnergy", "specific_total_energy"), ("MagneticEnergy", "magnetic_energy_density"), ("GasEnergy", "specific_thermal_energy"), ("Gas_Energy", "specific_thermal_energy"), ("BMagnitude", "b_magnitude"), ("PlasmaBeta", "plasma_beta"), ("MagneticPressure", "magnetic_pressure"), ("BPoloidal", "b_poloidal"), ("BToroidal", "b_toroidal"), ("BRadial", "b_radial"), ("VorticitySquared", "vorticity_squared"), ("gradPressureX", "grad_pressure_x"), ("gradPressureY", "grad_pressure_y"), ("gradPressureZ", "grad_pressure_z"), ("gradPressureMagnitude", "grad_pressure_magnitude"), ("gradDensityX", "grad_density_x"), ("gradDensityY", "grad_density_y"), ("gradDensityZ", "grad_density_z"), ("gradDensityMagnitude", "grad_density_magnitude"), ("BaroclinicVorticityX", "baroclinic_vorticity_x"), ("BaroclinicVorticityY", "baroclinic_vorticity_y"), ("BaroclinicVorticityZ", "baroclinic_vorticity_z"), ("BaroclinicVorticityMagnitude", "baroclinic_vorticity_magnitude"), ("VorticityX", "vorticity_x"), ("VorticityY", "vorticity_y"), ("VorticityZ", "vorticity_z"), ("VorticityMagnitude", "vorticity_magnitude"), ("VorticityStretchingX", "vorticity_stretching_x"), ("VorticityStretchingY", "vorticity_stretching_y"), ("VorticityStretchingZ", "vorticity_stretching_z"), ("VorticityStretchingMagnitude", "vorticity_stretching_magnitude"), ("VorticityGrowthX", "vorticity_growth_x"), ("VorticityGrowthY", "vorticity_growth_y"), ("VorticityGrowthZ", "vorticity_growth_z"), ("VorticityGrowthMagnitude", "vorticity_growth_magnitude"), ("VorticityGrowthMagnitudeABS", "vorticity_growth_magnitude_absolute"), ("VorticityGrowthTimescale", "vorticity_growth_timescale"), ("VorticityRadPressureX", "vorticity_radiation_pressure_x"), ("VorticityRadPressureY", "vorticity_radiation_pressure_y"), ("VorticityRadPressureZ", "vorticity_radiation_pressure_z"), ("VorticityRadPressureMagnitude", "vorticity_radiation_pressure_magnitude"), ("VorticityRPGrowthX", "vorticity_radiation_pressure_growth_x"), ("VorticityRPGrowthY", "vorticity_radiation_pressure_growth_y"), ("VorticityRPGrowthZ", "vorticity_radiation_pressure_growth_z"), ("VorticityRPGrowthMagnitude", "vorticity_radiation_pressure_growth_magnitude"), ("VorticityRPGrowthTimescale", "vorticity_radiation_pressure_growth_timescale"), ("DiskAngle", "theta"), ("Height", "height"), ("HI density", "H_density"), ("HII density", "H_p1_density"), ("HeI density", "He_density"), ("HeII density", "He_p1_density"), ("HeIII density", "He_p2_density"), ] _field_units_aliases = [ ("cyl_RCode", "code_length"), ("HeightAU", "au"), ("cyl_RadialVelocityKMS", "km/s"), ("cyl_RadialVelocityKMSABS", "km/s"), ("cyl_TangentialVelocityKMS", "km/s"), ("cyl_TangentialVelocityKMSABS", "km/s"), ("CellMassMsun", "msun"), ("CellMassCode", "code_mass"), ("TotalMassMsun", "msun"), ("CellVolumeCode", "code_length"), ("CellVolumeMpc", "Mpc**3"), ("ParticleSpecificAngularMomentumXKMSMPC", "km/s/Mpc"), ("ParticleSpecificAngularMomentumYKMSMPC", "km/s/Mpc"), ("ParticleSpecificAngularMomentumZKMSMPC", "km/s/Mpc"), ("RadiusMpc", "Mpc"), ("ParticleRadiusMpc", "Mpc"), ("ParticleRadiuskpc", "kpc"), ("Radiuskpc", "kpc"), ("ParticleRadiuskpch", "kpc"), ("Radiuskpch", "kpc"), ("ParticleRadiuspc", "pc"), ("Radiuspc", "pc"), ("ParticleRadiusAU", "au"), ("RadiusAU", "au"), ("ParticleRadiusCode", "code_length"), ("RadiusCode", "code_length"), ("RadialVelocityKMS", "km/s"), ("RadialVelocityKMSABS", "km/s"), ("JeansMassMsun", "msun"), ] yt-project-yt-f043ac8/yt/fields/field_detector.py000066400000000000000000000250711510711153200221070ustar00rootroot00000000000000from collections import defaultdict import numpy as np from yt.units.yt_array import YTArray from yt.utilities.io_handler import io_registry from .field_exceptions import NeedsGridType fp_units = { "bulk_velocity": "cm/s", "center": "cm", "normal": "", "cp_x_vec": "", "cp_y_vec": "", "cp_z_vec": "", "x_hat": "", "y_hat": "", "z_hat": "", "omega_baryon": "", "virial_radius": "cm", "observer_redshift": "", "source_redshift": "", } class FieldDetector(defaultdict): Level = 1 NumberOfParticles = 1 _read_exception = None _id_offset = 0 domain_id = 0 def __init__(self, nd=16, ds=None, flat=False, field_parameters=None): self.nd = nd self.flat = flat self._spatial = not flat self.ActiveDimensions = [nd, nd, nd] self.shape = tuple(self.ActiveDimensions) self.size = np.prod(self.ActiveDimensions) self.LeftEdge = [0.0, 0.0, 0.0] self.RightEdge = [1.0, 1.0, 1.0] self.dds = np.ones(3, "float64") if field_parameters is None: self.field_parameters = {} else: self.field_parameters = field_parameters class fake_dataset(defaultdict): pass if ds is None: # required attrs ds = fake_dataset(lambda: 1) ds["Massarr"] = np.ones(6) ds.current_redshift = 0.0 ds.omega_lambda = 0.0 ds.omega_matter = 0.0 ds.cosmological_simulation = 0 ds.gamma = 5.0 / 3.0 ds.hubble_constant = 0.7 ds.domain_left_edge = np.zeros(3, "float64") ds.domain_right_edge = np.ones(3, "float64") ds.dimensionality = 3 ds.periodicity = (True, True, True) self.ds = ds class fake_index: class fake_io: def _read_data_set(io_self, data, field): # noqa: B902 return self._read_data(field) _read_exception = RuntimeError io = fake_io() def get_smallest_dx(self): return 1.0 self.index = fake_index() self.requested = [] self.requested_parameters = [] rng = np.random.default_rng() if not self.flat: defaultdict.__init__( self, lambda: np.ones((nd, nd, nd), dtype="float64") + 1e-4 * rng.random((nd, nd, nd)), ) else: defaultdict.__init__( self, lambda: np.ones((nd * nd * nd), dtype="float64") + 1e-4 * rng.random(nd * nd * nd), ) def _reshape_vals(self, arr): if not self._spatial: return arr if len(arr.shape) == 3: return arr return arr.reshape(self.ActiveDimensions, order="C") def __missing__(self, item: tuple[str, str] | str): from yt.fields.derived_field import NullFunc if not isinstance(item, tuple): field = ("unknown", item) else: field = item finfo = self.ds._get_field_info(field) params, permute_params = finfo._get_needed_parameters(self) self.field_parameters.update(params) # For those cases where we are guessing the field type, we will # need to re-update -- otherwise, our item will always not have the # field type. This can lead to, for instance, "unknown" particle # types not getting correctly identified. # Note that the *only* way this works is if we also fix our field # dependencies during checking. Bug #627 talks about this. _item: tuple[str, str] = finfo.name if finfo is not None and finfo._function is not NullFunc: try: for param, param_v in permute_params.items(): for v in param_v: self.field_parameters[param] = v vv = finfo(self) if not permute_params: vv = finfo(self) except NeedsGridType as exc: ngz = exc.ghost_zones nfd = FieldDetector( self.nd + ngz * 2, ds=self.ds, field_parameters=self.field_parameters.copy(), ) nfd._num_ghost_zones = ngz vv = finfo(nfd) if ngz > 0: vv = vv[ngz:-ngz, ngz:-ngz, ngz:-ngz] for i in nfd.requested: if i not in self.requested: self.requested.append(i) for i in nfd.requested_parameters: if i not in self.requested_parameters: self.requested_parameters.append(i) if vv is not None: if not self.flat: self[_item] = vv else: self[_item] = vv.ravel() return self[_item] elif finfo is not None and finfo.sampling_type == "particle": io = io_registry[self.ds.dataset_type](self.ds) if hasattr(io, "_vector_fields") and ( _item in io._vector_fields or _item[1] in io._vector_fields ): try: cols = io._vector_fields[_item] except KeyError: cols = io._vector_fields[_item[1]] # A vector self[_item] = YTArray( np.ones((self.NumberOfParticles, cols)), finfo.units, registry=self.ds.unit_registry, ) else: # Not a vector self[_item] = YTArray( np.ones(self.NumberOfParticles), finfo.units, registry=self.ds.unit_registry, ) if _item == ("STAR", "BIRTH_TIME"): # hack for the artio frontend so we pass valid times to # the artio functions for calculating physical times # from internal times self[_item] *= -0.1 self.requested.append(_item) return self[_item] self.requested.append(_item) if _item not in self: self[_item] = self._read_data(_item) return self[_item] def _debug(self): # We allow this to pass through. return def deposit(self, *args, **kwargs): from yt.data_objects.static_output import ParticleDataset from yt.frontends.stream.data_structures import StreamParticlesDataset if kwargs["method"] == "mesh_id": if isinstance(self.ds, (StreamParticlesDataset, ParticleDataset)): raise ValueError rng = np.random.default_rng() return rng.random((self.nd, self.nd, self.nd)) def mesh_sampling_particle_field(self, *args, **kwargs): pos = args[0] npart = len(pos) rng = np.random.default_rng() return rng.random(npart) def smooth(self, *args, **kwargs): rng = np.random.default_rng() tr = rng.random((self.nd, self.nd, self.nd)) if kwargs["method"] == "volume_weighted": return [tr] return tr def particle_operation(self, *args, **kwargs): return None def _read_data(self, field_name): self.requested.append(field_name) finfo = self.ds._get_field_info(field_name) if finfo.sampling_type == "particle": self.requested.append(field_name) return np.ones(self.NumberOfParticles) return YTArray( defaultdict.__missing__(self, field_name), units=finfo.units, registry=self.ds.unit_registry, ) def get_field_parameter(self, param, default=0.0): if self.field_parameters and param in self.field_parameters: return self.field_parameters[param] self.requested_parameters.append(param) if param in ["center", "normal"] or param.startswith("bulk"): if param == "bulk_magnetic_field": if self.ds.unit_system.has_current_mks: unit = "T" else: unit = "G" else: unit = fp_units[param] rng = np.random.default_rng() return self.ds.arr(rng.random(3) * 1e-2, unit) elif param in ["surface_height"]: return self.ds.quan(0.0, "code_length") elif param in ["axis"]: return 0 elif param.startswith("cp_"): ax = param[3] rv = self.ds.arr((0.0, 0.0, 0.0), fp_units[param]) rv["xyz".index(ax)] = 1.0 return rv elif param.endswith("_hat"): ax = param[0] rv = YTArray((0.0, 0.0, 0.0), fp_units[param]) rv["xyz".index(ax)] = 1.0 return rv elif param == "fof_groups": return None elif param == "mu": return 1.0 else: return default _num_ghost_zones = 0 id = 1 def apply_units(self, arr, units): return self.ds.arr(arr, units=units) def has_field_parameter(self, param): return param in self.field_parameters @property def fcoords(self): fc = np.array( np.mgrid[0 : 1 : self.nd * 1j, 0 : 1 : self.nd * 1j, 0 : 1 : self.nd * 1j] ) if self.flat: fc = fc.reshape(self.nd * self.nd * self.nd, 3) else: fc = fc.transpose() return self.ds.arr(fc, units="code_length") @property def fcoords_vertex(self): if self.flat: shape = (self.nd * self.nd * self.nd, 8, 3) else: shape = (self.nd, self.nd, self.nd, 8, 3) rng = np.random.default_rng() return self.ds.arr(rng.random(shape), units="code_length") @property def icoords(self): ic = np.mgrid[ 0 : self.nd - 1 : self.nd * 1j, 0 : self.nd - 1 : self.nd * 1j, 0 : self.nd - 1 : self.nd * 1j, ] if self.flat: return ic.reshape(self.nd * self.nd * self.nd, 3) else: return ic.transpose() @property def ires(self): if self.flat: shape = (self.nd**3,) else: shape = (self.nd, self.nd, self.nd) return np.ones(shape, dtype="int64") @property def fwidth(self): if self.flat: shape = (self.nd**3, 3) else: shape = (self.nd, self.nd, self.nd, 3) fw = np.full(shape, 1 / self.nd, dtype="float64") return self.ds.arr(fw, units="code_length") yt-project-yt-f043ac8/yt/fields/field_exceptions.py000066400000000000000000000025171510711153200224570ustar00rootroot00000000000000class ValidationException(Exception): pass class NeedsGridType(ValidationException): def __init__(self, ghost_zones=0, fields=None): self.ghost_zones = ghost_zones self.fields = fields def __str__(self): s = "s" if self.ghost_zones != 1 else "" return f"fields {self.fields} require {self.ghost_zones} ghost zone{s}." class NeedsOriginalGrid(NeedsGridType): def __init__(self): self.ghost_zones = 0 class NeedsDataField(ValidationException): def __init__(self, missing_fields): self.missing_fields = missing_fields def __str__(self): return f"({self.missing_fields})" class NeedsProperty(ValidationException): def __init__(self, missing_properties): self.missing_properties = missing_properties def __str__(self): return f"({self.missing_properties})" class NeedsParameter(ValidationException): def __init__(self, missing_parameters): self.missing_parameters = missing_parameters def __str__(self): return f"({self.missing_parameters})" class NeedsConfiguration(ValidationException): def __init__(self, parameter, value): self.parameter = parameter self.value = value def __str__(self): return f"(Needs {self.parameter} = {self.value})" class FieldUnitsError(Exception): pass yt-project-yt-f043ac8/yt/fields/field_functions.py000066400000000000000000000071261510711153200223070ustar00rootroot00000000000000from collections.abc import Callable from inspect import signature import numpy as np from yt.utilities.lib.misc_utilities import obtain_position_vector def get_radius(data, field_prefix, ftype): center = data.get_field_parameter("center").to("code_length") DW = (data.ds.domain_right_edge - data.ds.domain_left_edge).to("code_length") # This is in code_length so it can be the destination for our r later. radius2 = data.ds.arr( np.zeros(data[ftype, field_prefix + "x"].shape, dtype="float64"), "code_length" ) r = np.empty_like(radius2, subok=False) if any(data.ds.periodicity): rdw = radius2.v for i, ax in enumerate("xyz"): pos = data[ftype, f"{field_prefix}{ax}"] if str(pos.units) != "code_length": pos = pos.to("code_length") np.subtract( pos.d, center[i].d, r, ) if data.ds.periodicity[i]: np.abs(r, r) np.subtract(r, DW.d[i], rdw) np.abs(rdw, rdw) np.minimum(r, rdw, out=r) np.multiply(r, r, r) np.add(radius2.d, r, radius2.d) if data.ds.dimensionality < i + 1: break # Using the views into the array is not changing units and as such keeps # from having to do symbolic manipulations np.sqrt(radius2.d, radius2.d) # Alias it, just for clarity. radius = radius2 return radius def get_periodic_rvec(data): coords = obtain_position_vector(data).d if sum(data.ds.periodicity) == 0: return coords le = data.ds.domain_left_edge.in_units("code_length").d dw = data.ds.domain_width.in_units("code_length").d for i in range(coords.shape[0]): if not data.ds.periodicity[i]: continue coords[i, ...] -= le[i] # figure out which measure is less mins = np.argmin( [ np.abs(np.mod(coords[i, ...], dw[i])), np.abs(np.mod(coords[i, ...], -dw[i])), ], axis=0, ) temp_coords = np.mod(coords[i, ...], dw[i]) # Where second measure is better, updating temporary coords ii = mins == 1 temp_coords[ii] = np.mod(coords[i, ...], -dw[i])[ii] # Putting the temporary coords into the actual storage coords[i, ...] = temp_coords coords[i, ...] + le[i] return coords def validate_field_function(function: Callable) -> None: """ Inspect signature, raise a TypeError if invalid, return None otherwise. """ # This is a helper function to user-intended field registration methods # (e.g. Dataset.add_field and yt.derived_field) # it is not used in FieldInfoContainer.add_field to optimize performance # (inspect.signature is quite expensive and we don't want to validate yt's # internal code every time a dataset's fields are defined). # lookup parameters that do not have default values fparams = signature(function).parameters nodefaults = tuple(p.name for p in fparams.values() if p.default is p.empty) if nodefaults != ("field", "data"): raise TypeError( f"Received field function {function} with invalid signature. " f"Expected exactly 2 positional parameters ('field', 'data'), got {nodefaults!r}" ) if any( fparams[name].kind == fparams[name].KEYWORD_ONLY for name in ("field", "data") ): raise TypeError( f"Received field function {function} with invalid signature. " "Parameters 'field' and 'data' must accept positional values " "(they cannot be keyword-only)" ) yt-project-yt-f043ac8/yt/fields/field_info_container.py000066400000000000000000000652621510711153200233010ustar00rootroot00000000000000import sys from collections import UserDict from collections.abc import Callable from unyt.exceptions import UnitConversionError from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import FieldKey, FieldName, FieldType, KnownFieldsT from yt.config import ytcfg from yt.fields.field_exceptions import NeedsConfiguration from yt.funcs import mylog, obj_length, only_on_root from yt.geometry.api import Geometry from yt.units.dimensions import dimensionless # type: ignore from yt.units.unit_object import Unit # type: ignore from yt.utilities.exceptions import ( YTCoordinateNotImplemented, YTDomainOverflow, YTFieldNotFound, ) from .derived_field import DeprecatedFieldFunc, DerivedField, NullFunc, TranslationFunc from .field_plugin_registry import FunctionName, field_plugins from .particle_fields import ( add_union_field, particle_deposition_functions, particle_scalar_functions, particle_vector_functions, sph_whitelist_fields, standard_particle_fields, ) if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never class FieldInfoContainer(UserDict): """ This is a generic field container. It contains a list of potential derived fields, all of which know how to act on a data object and return a value. This object handles converting units as well as validating the availability of a given field. """ fallback = None known_other_fields: KnownFieldsT = () known_particle_fields: KnownFieldsT = () extra_union_fields: tuple[FieldKey, ...] = () def __init__(self, ds, field_list: list[FieldKey], slice_info=None): super().__init__() self._show_field_errors: list[Exception] = [] self.ds = ds # Now we start setting things up. self.field_list = field_list self.slice_info = slice_info self.field_aliases: dict[FieldKey, FieldKey] = {} self.species_names: list[FieldName] = [] self.setup_fluid_aliases() @property def curvilinear(self) -> bool: issue_deprecation_warning( "FieldInfoContainer.curvilinear attribute is deprecated. " "Please compare the internal dataset geometry directly to known Geometry enum members instead. ", stacklevel=3, since="4.2", ) geometry = self.ds.geometry return ( geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL ) def setup_fluid_fields(self): pass def setup_fluid_index_fields(self): # Now we get all our index types and set up aliases to them if self.ds is None: return index_fields = {f for _, f in self if _ == "index"} for ftype in self.ds.fluid_types: if ftype in ("index", "deposit"): continue for f in index_fields: if (ftype, f) in self: continue self.alias((ftype, f), ("index", f)) def setup_particle_fields(self, ptype, ftype="gas", num_neighbors=64): skip_output_units = ("code_length",) for f, (units, aliases, dn) in sorted(self.known_particle_fields): units = self.ds.field_units.get((ptype, f), units) output_units = units if ( f in aliases or ptype not in self.ds.particle_types_raw ) and units not in skip_output_units: u = Unit(units, registry=self.ds.unit_registry) if u.dimensions is not dimensionless: output_units = str(self.ds.unit_system[u.dimensions]) if (ptype, f) not in self.field_list: continue self.add_output_field( (ptype, f), sampling_type="particle", units=units, display_name=dn, output_units=output_units, ) for alias in aliases: self.alias((ptype, alias), (ptype, f), units=output_units) # We'll either have particle_position or particle_position_[xyz] if (ptype, "particle_position") in self.field_list or ( ptype, "particle_position", ) in self.field_aliases: particle_scalar_functions( ptype, "particle_position", "particle_velocity", self ) else: # We need to check to make sure that there's a "known field" that # overlaps with one of the vector fields. For instance, if we are # in the Stream frontend, and we have a set of scalar position # fields, they will overlap with -- and be overridden by -- the # "known" vector field that the frontend creates. So the easiest # thing to do is to simply remove the on-disk field (which doesn't # exist) and replace it with a derived field. if (ptype, "particle_position") in self and self[ ptype, "particle_position" ]._function == NullFunc: self.pop((ptype, "particle_position")) particle_vector_functions( ptype, [f"particle_position_{ax}" for ax in "xyz"], [f"particle_velocity_{ax}" for ax in "xyz"], self, ) particle_deposition_functions(ptype, "particle_position", "particle_mass", self) standard_particle_fields(self, ptype) # Now we check for any leftover particle fields for field in sorted(self.field_list): if field in self: continue if not isinstance(field, tuple): raise RuntimeError if field[0] not in self.ds.particle_types: continue units = self.ds.field_units.get(field, None) if units is None: try: units = ytcfg.get("fields", *field, "units") except KeyError: units = "" self.add_output_field( field, sampling_type="particle", units=units, ) self.setup_smoothed_fields(ptype, num_neighbors=num_neighbors, ftype=ftype) def setup_extra_union_fields(self, ptype="all"): if ptype != "all": raise RuntimeError( "setup_extra_union_fields is currently" + 'only enabled for particle type "all".' ) for units, field in self.extra_union_fields: add_union_field(self, ptype, field, units) def setup_smoothed_fields(self, ptype, num_neighbors=64, ftype="gas"): # We can in principle compute this, but it is not yet implemented. if (ptype, "density") not in self or not hasattr(self.ds, "_sph_ptypes"): return new_aliases = [] for ptype2, alias_name in list(self): if ptype2 != ptype: continue if alias_name not in sph_whitelist_fields: if alias_name.startswith("particle_"): pass else: continue uni_alias_name = alias_name if "particle_position_" in alias_name: uni_alias_name = alias_name.replace("particle_position_", "") elif "particle_" in alias_name: uni_alias_name = alias_name.replace("particle_", "") new_aliases.append( ( (ftype, uni_alias_name), (ptype, alias_name), ) ) if "particle_position_" in alias_name: new_aliases.append( ( (ftype, alias_name), (ptype, alias_name), ) ) new_aliases.append( ( (ptype, uni_alias_name), (ptype, alias_name), ) ) for alias, source in new_aliases: self.alias(alias, source) self.alias((ftype, "particle_position"), (ptype, "particle_position")) self.alias((ftype, "particle_mass"), (ptype, "particle_mass")) # Collect the names for all aliases if geometry is curvilinear def get_aliases_gallery(self) -> list[FieldName]: aliases_gallery: list[FieldName] = [] known_other_fields = dict(self.known_other_fields) if self.ds is None: return aliases_gallery geometry: Geometry = self.ds.geometry if ( geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL ): aliases: list[FieldName] for field in sorted(self.field_list): if field[0] in self.ds.particle_types: continue args = known_other_fields.get(field[1], ("", [], None)) units, aliases, display_name = args aliases_gallery.extend(aliases) elif ( geometry is Geometry.CARTESIAN or geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): # nothing to do pass else: assert_never(geometry) return aliases_gallery def setup_fluid_aliases(self, ftype: FieldType = "gas") -> None: known_other_fields = dict(self.known_other_fields) # For non-Cartesian geometry, convert alias of vector fields to # curvilinear coordinates aliases_gallery = self.get_aliases_gallery() for field in sorted(self.field_list): if not isinstance(field, tuple) or len(field) != 2: raise RuntimeError if field[0] in self.ds.particle_types: continue args = known_other_fields.get(field[1], None) if args is not None: units, aliases, display_name = args else: try: node = ytcfg.get("fields", *field).as_dict() except KeyError: node = {} units = node.get("units", "") aliases = node.get("aliases", []) display_name = node.get("display_name", None) # We allow field_units to override this. First we check if the # field *name* is in there, then the field *tuple*. units = self.ds.field_units.get(field[1], units) units = self.ds.field_units.get(field, units) self.add_output_field( field, sampling_type="cell", units=units, display_name=display_name ) axis_names = self.ds.coordinates.axis_order geometry: Geometry = self.ds.geometry for alias in aliases: if ( geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL ): if alias[-2:] not in ["_x", "_y", "_z"]: to_convert = False else: for suffix in ["x", "y", "z"]: if f"{alias[:-2]}_{suffix}" not in aliases_gallery: to_convert = False break to_convert = True if to_convert: if alias[-2:] == "_x": alias = f"{alias[:-2]}_{axis_names[0]}" elif alias[-2:] == "_y": alias = f"{alias[:-2]}_{axis_names[1]}" elif alias[-2:] == "_z": alias = f"{alias[:-2]}_{axis_names[2]}" elif ( geometry is Geometry.CARTESIAN or geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): # nothing to do pass else: assert_never(geometry) self.alias((ftype, alias), field) @staticmethod def _sanitize_sampling_type(sampling_type: str) -> str: """Detect conflicts between deprecated and new parameters to specify the sampling type in a new field. This is a helper function to add_field methods. Parameters ---------- sampling_type : str One of "cell", "particle" or "local" (case insensitive) Raises ------ ValueError For unsupported values in sampling_type """ if not isinstance(sampling_type, str): raise TypeError("sampling_type should be a string.") sampling_type = sampling_type.lower() acceptable_samplings = ("cell", "particle", "local") if sampling_type not in acceptable_samplings: raise ValueError( f"Received invalid sampling type {sampling_type!r}. " f"Expected any of {acceptable_samplings}" ) return sampling_type def add_field( self, name: FieldKey, function: Callable, sampling_type: str, *, alias: DerivedField | None = None, force_override: bool = False, **kwargs, ) -> None: """ Add a new field, along with supplemental metadata, to the list of available fields. This respects a number of arguments, all of which are passed on to the constructor for :class:`~yt.data_objects.api.DerivedField`. Parameters ---------- name : tuple[str, str] field (or particle) type, field name function : callable A function handle that defines the field. Should accept arguments (field, data) sampling_type: str "cell" or "particle" or "local" force_override: bool If False (default), an error will be raised if a field of the same name already exists. alias: DerivedField (optional): existing field to be aliased units : str A plain text string encoding the unit. Powers must be in python syntax (** instead of ^). If set to "auto" the units will be inferred from the return value of the field function. take_log : bool Describes whether the field should be logged validators : list A list of :class:`FieldValidator` objects vector_field : bool Describes the dimensionality of the field. Currently unused. display_name : str A name used in the plots """ # Handle the case where the field has already been added. if not force_override and name in self: return kwargs.setdefault("ds", self.ds) sampling_type = self._sanitize_sampling_type(sampling_type) if ( not isinstance(name, str) and obj_length(name) == 2 and all(isinstance(e, str) for e in name) ): self[name] = DerivedField( name, sampling_type, function, alias=alias, **kwargs ) else: raise ValueError(f"Expected name to be a tuple[str, str], got {name}") def load_all_plugins(self, ftype: str | None = "gas") -> None: if ftype is None: return mylog.debug("Loading field plugins for field type: %s.", ftype) loaded = [] for n in sorted(field_plugins): loaded += self.load_plugin(n, ftype) only_on_root(mylog.debug, "Loaded %s (%s new fields)", n, len(loaded)) self.find_dependencies(loaded) def load_plugin( self, plugin_name: FunctionName, ftype: FieldType = "gas", skip_check: bool = False, ): f = field_plugins[plugin_name] orig = set(self.items()) f(self, ftype, slice_info=self.slice_info) loaded = [n for n, v in set(self.items()).difference(orig)] return loaded def find_dependencies(self, loaded): deps, unavailable = self.check_derived_fields(loaded) self.ds.field_dependencies.update(deps) # Note we may have duplicated dfl = set(self.ds.derived_field_list).union(deps.keys()) self.ds.derived_field_list = sorted(dfl) return loaded, unavailable def add_output_field(self, name, sampling_type, **kwargs): if name[1] == "density": if name in self: # this should not happen, but it does # it'd be best to raise an error here but # it may take a while to cleanup internal issues return kwargs.setdefault("ds", self.ds) self[name] = DerivedField(name, sampling_type, NullFunc, **kwargs) def alias( self, alias_name: FieldKey, original_name: FieldKey, units: str | None = None, deprecate: tuple[str, str | None] | None = None, ): """ Alias one field to another field. Parameters ---------- alias_name : tuple[str, str] The new field name. original_name : tuple[str, str] The field to be aliased. units : str A plain text string encoding the unit. Powers must be in python syntax (** instead of ^). If set to "auto" the units will be inferred from the return value of the field function. deprecate : tuple[str, str | None] | None If this is set, then the tuple contains two string version numbers: the first marking the version when the field was deprecated, and the second marking when the field will be removed. """ if original_name not in self: return if units is None: # We default to CGS here, but in principle, this can be pluggable # as well. # self[original_name].units may be set to `None` at this point # to signal that units should be autoset later oru = self[original_name].units if oru is None: units = None else: u = Unit(oru, registry=self.ds.unit_registry) if u.dimensions is not dimensionless: units = str(self.ds.unit_system[u.dimensions]) else: units = oru self.field_aliases[alias_name] = original_name function = TranslationFunc(original_name) if deprecate is not None: self.add_deprecated_field( alias_name, function=function, sampling_type=self[original_name].sampling_type, display_name=self[original_name].display_name, units=units, since=deprecate[0], removal=deprecate[1], ret_name=original_name, ) else: self.add_field( alias_name, function=function, sampling_type=self[original_name].sampling_type, display_name=self[original_name].display_name, units=units, alias=self[original_name], ) def add_deprecated_field( self, name, function, sampling_type, since, removal=None, ret_name=None, **kwargs, ): """ Add a new field which is deprecated, along with supplemental metadata, to the list of available fields. This respects a number of arguments, all of which are passed on to the constructor for :class:`~yt.data_objects.api.DerivedField`. Parameters ---------- name : str is the name of the field. function : callable A function handle that defines the field. Should accept arguments (field, data) sampling_type : str "cell" or "particle" or "local" since : str The version string marking when this field was deprecated. removal : str The version string marking when this field will be removed. ret_name : str The name of the field which will actually be returned, used only by :meth:`~yt.fields.field_info_container.FieldInfoContainer.alias`. units : str A plain text string encoding the unit. Powers must be in python syntax (** instead of ^). If set to "auto" the units will be inferred from the return value of the field function. take_log : bool Describes whether the field should be logged validators : list A list of :class:`FieldValidator` objects vector_field : bool Describes the dimensionality of the field. Currently unused. display_name : str A name used in the plots """ if ret_name is None: ret_name = name self.add_field( name, function=DeprecatedFieldFunc(ret_name, function, since, removal), sampling_type=sampling_type, **kwargs, ) def has_key(self, key): # This gets used a lot if key in self: return True if self.fallback is None: return False return key in self.fallback def __missing__(self, key): if self.fallback is None: raise KeyError(f"No field named {key}") return self.fallback[key] @classmethod def create_with_fallback(cls, fallback, name=""): obj = cls() obj.fallback = fallback obj.name = name return obj def __contains__(self, key): if super().__contains__(key): return True if self.fallback is None: return False return key in self.fallback def __iter__(self): yield from super().__iter__() if self.fallback is not None: yield from self.fallback def keys(self): keys = super().keys() if self.fallback: keys += list(self.fallback.keys()) return keys def check_derived_fields(self, fields_to_check=None): # The following exceptions lists were obtained by expanding an # all-catching `except Exception`. # We define # - a blacklist (exceptions that we know should be caught) # - a whitelist (exceptions that should be handled) # - a greylist (exceptions that may be covering bugs but should be checked) # See https://github.com/yt-project/yt/issues/2853 # in the long run, the greylist should be removed blacklist = () whitelist = (NotImplementedError,) greylist = ( YTFieldNotFound, YTDomainOverflow, YTCoordinateNotImplemented, NeedsConfiguration, TypeError, ValueError, IndexError, AttributeError, KeyError, # code smells -> those are very likely bugs UnitConversionError, # solved in GH PR 2897 ? # RecursionError is clearly a bug, and was already solved once # in GH PR 2851 RecursionError, ) deps = {} unavailable = [] fields_to_check = fields_to_check or list(self.keys()) for field in fields_to_check: fi = self[field] try: # fd: field detector fd = fi.get_dependencies(ds=self.ds) except blacklist as err: print(f"{err.__class__} raised for field {field}") raise SystemExit(1) from err except (*whitelist, *greylist) as e: if field in self._show_field_errors: raise if not isinstance(e, YTFieldNotFound): # if we're doing field tests, raise an error # see yt.fields.tests.test_fields if hasattr(self.ds, "_field_test_dataset"): raise mylog.debug( "Raises %s during field %s detection.", str(type(e)), field ) self.pop(field) continue # This next bit checks that we can't somehow generate everything. # We also manually update the 'requested' attribute missing = not all(f in self.field_list for f in fd.requested) if missing: self.pop(field) unavailable.append(field) continue fd.requested = set(fd.requested) deps[field] = fd mylog.debug("Succeeded with %s (needs %s)", field, fd.requested) # now populate the derived field list with results # this violates isolation principles and should be refactored dfl = set(self.ds.derived_field_list).union(deps.keys()) dfl = sorted(dfl) if not hasattr(self.ds.index, "meshes"): # the meshes attribute characterizes an unstructured-mesh data structure # ideally this filtering should not be required # and this could maybe be handled in fi.get_dependencies # but it's a lot easier to do here filtered_dfl = [] for field in dfl: try: ftype, fname = field if "vertex" in fname: continue except ValueError: # in very rare cases, there can a field represented by a single # string, like "emissivity" # this try block _should_ be removed and the error fixed upstream # for reference, a test that would break is # yt/data_objects/tests/test_fluxes.py::ExporterTests pass filtered_dfl.append(field) dfl = filtered_dfl self.ds.derived_field_list = dfl self._set_linear_fields() return deps, unavailable def _set_linear_fields(self): """ Sets which fields use linear as their default scaling in Profiles and PhasePlots. Default for all fields is set to log, so this sets which are linear. For now, set linear to geometric fields: position and velocity coordinates. """ non_log_prefixes = ("", "velocity_", "particle_position_", "particle_velocity_") coords = ("x", "y", "z") non_log_fields = [ prefix + coord for prefix in non_log_prefixes for coord in coords ] for field in self.ds.derived_field_list: if field[1] in non_log_fields: self[field].take_log = False yt-project-yt-f043ac8/yt/fields/field_plugin_registry.py000066400000000000000000000006671510711153200235300ustar00rootroot00000000000000from collections.abc import Callable FunctionName = str FieldPluginMap = dict[FunctionName, Callable] field_plugins: FieldPluginMap = {} def register_field_plugin(func: Callable) -> Callable: name = func.__name__ if name.startswith("setup_"): name = name[len("setup_") :] if name.endswith("_fields"): name = name[: -len("_fields")] field_plugins[name] = func # And, we return it, too return func yt-project-yt-f043ac8/yt/fields/field_type_container.py000066400000000000000000000126471510711153200233260ustar00rootroot00000000000000""" A proxy object for field descriptors, usually living as ds.fields. """ import inspect import textwrap import weakref from functools import cached_property from yt._maintenance.ipython_compat import IPYWIDGETS_ENABLED from yt.fields.derived_field import DerivedField def _fill_values(values): value = ( '" ) return value class FieldTypeContainer: def __init__(self, ds): self.ds = weakref.proxy(ds) def __getattr__(self, attr): ds = self.__getattribute__("ds") fnc = FieldNameContainer(ds, attr) if len(dir(fnc)) == 0: return self.__getattribute__(attr) return fnc @cached_property def field_types(self): return {t for t, n in self.ds.field_info} def __dir__(self): return list(self.field_types) def __iter__(self): for ft in self.field_types: fnc = FieldNameContainer(self.ds, ft) if len(dir(fnc)) == 0: yield self.__getattribute__(ft) else: yield fnc def __contains__(self, obj): ob = None if isinstance(obj, FieldNameContainer): ob = obj.field_type elif isinstance(obj, str): ob = obj return ob in self.field_types if IPYWIDGETS_ENABLED: def _ipython_display_(self): import ipywidgets from IPython.display import display fnames = [] children = [] for ftype in sorted(self.field_types): fnc = getattr(self, ftype) children.append(ipywidgets.Output()) with children[-1]: display(fnc) fnames.append(ftype) tabs = ipywidgets.Tab(children=children) for i, n in enumerate(fnames): tabs.set_title(i, n) display(tabs) class FieldNameContainer: def __init__(self, ds, field_type): self.ds = ds self.field_type = field_type def __getattr__(self, attr): ft = self.__getattribute__("field_type") ds = self.__getattribute__("ds") if (ft, attr) not in ds.field_info: return self.__getattribute__(attr) return ds.field_info[ft, attr] def __dir__(self): return [n for t, n in self.ds.field_info if t == self.field_type] def __iter__(self): for t, n in self.ds.field_info: if t == self.field_type: yield self.ds.field_info[t, n] def __contains__(self, obj): if isinstance(obj, DerivedField): if self.field_type == obj.name[0] and obj.name in self.ds.field_info: # e.g. from a completely different dataset if self.ds.field_info[obj.name] is not obj: return False return True elif isinstance(obj, tuple): if self.field_type == obj[0] and obj in self.ds.field_info: return True elif isinstance(obj, str): if (self.field_type, obj) in self.ds.field_info: return True return False if IPYWIDGETS_ENABLED: # for discussion of this class-level conditional: https://github.com/yt-project/yt/pull/4941 def _ipython_display_(self): import ipywidgets from IPython.display import Markdown, display names = dir(self) names.sort() def change_field(_ftype, _box, _var_window): def _change_field(event): fobj = getattr(_ftype, event["new"]) _box.clear_output() with _box: display( Markdown( data="```python\n" + textwrap.dedent(fobj.get_source()) + "\n```" ) ) values = inspect.getclosurevars(fobj._function).nonlocals _var_window.value = _fill_values(values) return _change_field flist = ipywidgets.Select( options=names, layout=ipywidgets.Layout(height="95%") ) source = ipywidgets.Output( layout=ipywidgets.Layout(width="100%", height="9em") ) var_window = ipywidgets.HTML(value="Empty") var_box = ipywidgets.Box( layout=ipywidgets.Layout( width="100%", height="100%", overflow_y="scroll" ) ) var_box.children = [var_window] ftype_tabs = ipywidgets.Tab( children=[source, var_box], layout=ipywidgets.Layout(flex="2 1 auto", width="auto", height="95%"), ) ftype_tabs.set_title(0, "Source") ftype_tabs.set_title(1, "Variables") flist.observe(change_field(self, source, var_window), "value") display( ipywidgets.HBox( [flist, ftype_tabs], layout=ipywidgets.Layout(height="14em") ) ) yt-project-yt-f043ac8/yt/fields/fluid_fields.py000066400000000000000000000221651510711153200215650ustar00rootroot00000000000000import numpy as np from yt.units.dimensions import current_mks from yt.units.unit_object import Unit # type: ignore from yt.utilities.chemical_formulas import compute_mu from yt.utilities.lib.misc_utilities import obtain_relative_velocity_vector from .derived_field import ValidateParameter, ValidateSpatial from .field_plugin_registry import register_field_plugin from .vector_operations import ( create_averaged_field, create_magnitude_field, create_vector_fields, ) @register_field_plugin def setup_fluid_fields(registry, ftype="gas", slice_info=None): pc = registry.ds.units.physical_constants # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info unit_system = registry.ds.unit_system if unit_system.base_units[current_mks] is None: mag_units = "magnetic_field_cgs" else: mag_units = "magnetic_field_mks" create_vector_fields( registry, "velocity", unit_system["velocity"], ftype, slice_info ) create_vector_fields( registry, "magnetic_field", unit_system[mag_units], ftype, slice_info ) def _cell_mass(field, data): return data[ftype, "density"] * data[ftype, "cell_volume"] registry.add_field( (ftype, "cell_mass"), sampling_type="cell", function=_cell_mass, units=unit_system["mass"], ) registry.alias((ftype, "mass"), (ftype, "cell_mass")) # momentum def momentum_xyz(v): def _momm(field, data): return data["gas", "mass"] * data["gas", f"velocity_{v}"] def _momd(field, data): return data["gas", "density"] * data["gas", f"velocity_{v}"] return _momm, _momd for v in registry.ds.coordinates.axis_order: _momm, _momd = momentum_xyz(v) registry.add_field( ("gas", f"momentum_{v}"), sampling_type="local", function=_momm, units=unit_system["momentum"], ) registry.add_field( ("gas", f"momentum_density_{v}"), sampling_type="local", function=_momd, units=unit_system["density"] * unit_system["velocity"], ) def _sound_speed(field, data): tr = data.ds.gamma * data[ftype, "pressure"] / data[ftype, "density"] return np.sqrt(tr) registry.add_field( (ftype, "sound_speed"), sampling_type="local", function=_sound_speed, units=unit_system["velocity"], ) def _radial_mach_number(field, data): """Radial component of M{|v|/c_sound}""" tr = data[ftype, "radial_velocity"] / data[ftype, "sound_speed"] return np.abs(tr) registry.add_field( (ftype, "radial_mach_number"), sampling_type="local", function=_radial_mach_number, units="", ) def _kinetic_energy_density(field, data): v = obtain_relative_velocity_vector(data) return 0.5 * data[ftype, "density"] * (v**2).sum(axis=0) registry.add_field( (ftype, "kinetic_energy_density"), sampling_type="local", function=_kinetic_energy_density, units=unit_system["pressure"], validators=[ValidateParameter("bulk_velocity")], ) def _mach_number(field, data): """M{|v|/c_sound}""" return data[ftype, "velocity_magnitude"] / data[ftype, "sound_speed"] registry.add_field( (ftype, "mach_number"), sampling_type="local", function=_mach_number, units="" ) def _courant_time_step(field, data): t1 = data[ftype, "dx"] / ( data[ftype, "sound_speed"] + np.abs(data[ftype, "velocity_x"]) ) t2 = data[ftype, "dy"] / ( data[ftype, "sound_speed"] + np.abs(data[ftype, "velocity_y"]) ) t3 = data[ftype, "dz"] / ( data[ftype, "sound_speed"] + np.abs(data[ftype, "velocity_z"]) ) tr = np.minimum(np.minimum(t1, t2), t3) return tr registry.add_field( (ftype, "courant_time_step"), sampling_type="cell", function=_courant_time_step, units=unit_system["time"], ) def _pressure(field, data): """M{(Gamma-1.0)*rho*E}""" tr = (data.ds.gamma - 1.0) * ( data[ftype, "density"] * data[ftype, "specific_thermal_energy"] ) return tr registry.add_field( (ftype, "pressure"), sampling_type="local", function=_pressure, units=unit_system["pressure"], ) def _kT(field, data): return (pc.kboltz * data[ftype, "temperature"]).in_units("keV") registry.add_field( (ftype, "kT"), sampling_type="local", function=_kT, units="keV", display_name="Temperature", ) def _metallicity(field, data): return data[ftype, "metal_density"] / data[ftype, "density"] registry.add_field( (ftype, "metallicity"), sampling_type="local", function=_metallicity, units="Zsun", ) def _metal_mass(field, data): Z = data[ftype, "metallicity"].to("dimensionless") return Z * data[ftype, "mass"] registry.add_field( (ftype, "metal_mass"), sampling_type="local", function=_metal_mass, units=unit_system["mass"], ) if len(registry.ds.field_info.species_names) > 0: def _number_density(field, data): field_data = np.zeros_like( data["gas", f"{data.ds.field_info.species_names[0]}_number_density"] ) for species in data.ds.field_info.species_names: field_data += data["gas", f"{species}_number_density"] return field_data else: def _number_density(field, data): mu = getattr(data.ds, "mu", compute_mu(data.ds.default_species_fields)) return data[ftype, "density"] / (pc.mh * mu) registry.add_field( (ftype, "number_density"), sampling_type="local", function=_number_density, units=unit_system["number_density"], ) def _mean_molecular_weight(field, data): return data[ftype, "density"] / (pc.mh * data[ftype, "number_density"]) registry.add_field( (ftype, "mean_molecular_weight"), sampling_type="local", function=_mean_molecular_weight, units="", ) setup_gradient_fields( registry, (ftype, "pressure"), unit_system["pressure"], slice_info ) setup_gradient_fields( registry, (ftype, "density"), unit_system["density"], slice_info ) create_averaged_field( registry, "density", unit_system["density"], ftype=ftype, slice_info=slice_info, weight="mass", ) def setup_gradient_fields(registry, grad_field, field_units, slice_info=None): assert isinstance(grad_field, tuple) ftype, fname = grad_field if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info slice_3d = (slice(1, -1), slice(1, -1), slice(1, -1)) def grad_func(axi, ax): slice_3dl = slice_3d[:axi] + (sl_left,) + slice_3d[axi + 1 :] slice_3dr = slice_3d[:axi] + (sl_right,) + slice_3d[axi + 1 :] def func(field, data): block_order = getattr(data, "_block_order", "C") if block_order == "F": # Fortran-ordering: we need to swap axes here and # reswap below field_data = data[grad_field].swapaxes(0, 2) else: field_data = data[grad_field] dx = div_fac * data[ftype, f"d{ax}"] if ax == "theta": dx *= data[ftype, "r"] if ax == "phi": dx *= data[ftype, "r"] * np.sin(data[ftype, "theta"]) f = field_data[slice_3dr] / dx[slice_3d] f -= field_data[slice_3dl] / dx[slice_3d] new_field = np.zeros_like(data[grad_field], dtype=np.float64) new_field = data.ds.arr(new_field, field_data.units / dx.units) new_field[slice_3d] = f if block_order == "F": new_field = new_field.swapaxes(0, 2) return new_field return func field_units = Unit(field_units, registry=registry.ds.unit_registry) grad_units = field_units / registry.ds.unit_system["length"] for axi, ax in enumerate(registry.ds.coordinates.axis_order): f = grad_func(axi, ax) registry.add_field( (ftype, f"{fname}_gradient_{ax}"), sampling_type="local", function=f, validators=[ValidateSpatial(1, [grad_field])], units=grad_units, ) create_magnitude_field( registry, f"{fname}_gradient", grad_units, ftype=ftype, validators=[ValidateSpatial(1, [grad_field])], ) yt-project-yt-f043ac8/yt/fields/fluid_vector_fields.py000066400000000000000000000505111510711153200231430ustar00rootroot00000000000000import numpy as np from yt._typing import FieldType from yt.fields.derived_field import ValidateParameter, ValidateSpatial from yt.fields.field_info_container import FieldInfoContainer from yt.funcs import just_one from yt.geometry.api import Geometry from yt.utilities.exceptions import YTDimensionalityError, YTFieldNotFound from .field_plugin_registry import register_field_plugin from .vector_operations import create_magnitude_field, create_squared_field @register_field_plugin def setup_fluid_vector_fields( registry: FieldInfoContainer, ftype: FieldType = "gas", slice_info=None ) -> None: # Current implementation for gradient is not valid for curvilinear geometries geometry: Geometry = registry.ds.geometry if geometry is not Geometry.CARTESIAN: return unit_system = registry.ds.unit_system # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info sl_center = slice(1, -1, None) def _baroclinic_vorticity_x(field, data): rho2 = data[ftype, "density"].astype("float64", copy=False) ** 2 return ( data[ftype, "pressure_gradient_y"] * data[ftype, "density_gradient_z"] - data[ftype, "pressure_gradient_z"] * data[ftype, "density_gradient_y"] ) / rho2 def _baroclinic_vorticity_y(field, data): rho2 = data[ftype, "density"].astype("float64", copy=False) ** 2 return ( data[ftype, "pressure_gradient_z"] * data[ftype, "density_gradient_x"] - data[ftype, "pressure_gradient_x"] * data[ftype, "density_gradient_z"] ) / rho2 def _baroclinic_vorticity_z(field, data): rho2 = data[ftype, "density"].astype("float64", copy=False) ** 2 return ( data[ftype, "pressure_gradient_x"] * data[ftype, "density_gradient_y"] - data[ftype, "pressure_gradient_y"] * data[ftype, "density_gradient_x"] ) / rho2 bv_validators = [ValidateSpatial(1, [(ftype, "density"), (ftype, "pressure")])] for ax in "xyz": n = f"baroclinic_vorticity_{ax}" registry.add_field( (ftype, n), sampling_type="cell", function=eval(f"_{n}"), validators=bv_validators, units=unit_system["frequency"] ** 2, ) create_magnitude_field( registry, "baroclinic_vorticity", unit_system["frequency"] ** 2, ftype=ftype, slice_info=slice_info, validators=bv_validators, ) def _vorticity_x(field, data): vz = data[ftype, "relative_velocity_z"] vy = data[ftype, "relative_velocity_y"] f = (vz[sl_center, sl_right, sl_center] - vz[sl_center, sl_left, sl_center]) / ( div_fac * just_one(data["index", "dy"]) ) f -= ( vy[sl_center, sl_center, sl_right] - vy[sl_center, sl_center, sl_left] ) / (div_fac * just_one(data["index", "dz"])) new_field = data.ds.arr(np.zeros_like(vz, dtype=np.float64), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field def _vorticity_y(field, data): vx = data[ftype, "relative_velocity_x"] vz = data[ftype, "relative_velocity_z"] f = (vx[sl_center, sl_center, sl_right] - vx[sl_center, sl_center, sl_left]) / ( div_fac * just_one(data["index", "dz"]) ) f -= ( vz[sl_right, sl_center, sl_center] - vz[sl_left, sl_center, sl_center] ) / (div_fac * just_one(data["index", "dx"])) new_field = data.ds.arr(np.zeros_like(vz, dtype=np.float64), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field def _vorticity_z(field, data): vx = data[ftype, "relative_velocity_x"] vy = data[ftype, "relative_velocity_y"] f = (vy[sl_right, sl_center, sl_center] - vy[sl_left, sl_center, sl_center]) / ( div_fac * just_one(data["index", "dx"]) ) f -= ( vx[sl_center, sl_right, sl_center] - vx[sl_center, sl_left, sl_center] ) / (div_fac * just_one(data["index", "dy"])) new_field = data.ds.arr(np.zeros_like(vy, dtype=np.float64), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field vort_validators = [ ValidateSpatial(1, [(ftype, f"velocity_{d}") for d in "xyz"]), ValidateParameter("bulk_velocity"), ] for ax in "xyz": n = f"vorticity_{ax}" registry.add_field( (ftype, n), sampling_type="cell", function=eval(f"_{n}"), units=unit_system["frequency"], validators=vort_validators, ) create_magnitude_field( registry, "vorticity", unit_system["frequency"], ftype=ftype, slice_info=slice_info, validators=vort_validators, ) create_squared_field( registry, "vorticity", unit_system["frequency"] ** 2, ftype=ftype, slice_info=slice_info, validators=vort_validators, ) def _vorticity_stretching_x(field, data): return data[ftype, "velocity_divergence"] * data[ftype, "vorticity_x"] def _vorticity_stretching_y(field, data): return data[ftype, "velocity_divergence"] * data[ftype, "vorticity_y"] def _vorticity_stretching_z(field, data): return data[ftype, "velocity_divergence"] * data[ftype, "vorticity_z"] for ax in "xyz": n = f"vorticity_stretching_{ax}" registry.add_field( (ftype, n), sampling_type="cell", function=eval(f"_{n}"), units=unit_system["frequency"] ** 2, validators=vort_validators, ) create_magnitude_field( registry, "vorticity_stretching", unit_system["frequency"] ** 2, ftype=ftype, slice_info=slice_info, validators=vort_validators, ) def _vorticity_growth_x(field, data): return ( -data[ftype, "vorticity_stretching_x"] - data[ftype, "baroclinic_vorticity_x"] ) def _vorticity_growth_y(field, data): return ( -data[ftype, "vorticity_stretching_y"] - data[ftype, "baroclinic_vorticity_y"] ) def _vorticity_growth_z(field, data): return ( -data[ftype, "vorticity_stretching_z"] - data[ftype, "baroclinic_vorticity_z"] ) for ax in "xyz": n = f"vorticity_growth_{ax}" registry.add_field( (ftype, n), sampling_type="cell", function=eval(f"_{n}"), units=unit_system["frequency"] ** 2, validators=vort_validators, ) def _vorticity_growth_magnitude(field, data): result = np.sqrt( data[ftype, "vorticity_growth_x"] ** 2 + data[ftype, "vorticity_growth_y"] ** 2 + data[ftype, "vorticity_growth_z"] ** 2 ) dot = data.ds.arr(np.zeros(result.shape), "") for ax in "xyz": dot += ( data[ftype, f"vorticity_{ax}"] * data[ftype, f"vorticity_growth_{ax}"] ).to_ndarray() result = np.sign(dot) * result return result registry.add_field( (ftype, "vorticity_growth_magnitude"), sampling_type="cell", function=_vorticity_growth_magnitude, units=unit_system["frequency"] ** 2, validators=vort_validators, take_log=False, ) def _vorticity_growth_magnitude_absolute(field, data): return np.sqrt( data[ftype, "vorticity_growth_x"] ** 2 + data[ftype, "vorticity_growth_y"] ** 2 + data[ftype, "vorticity_growth_z"] ** 2 ) registry.add_field( (ftype, "vorticity_growth_magnitude_absolute"), sampling_type="cell", function=_vorticity_growth_magnitude_absolute, units=unit_system["frequency"] ** 2, validators=vort_validators, ) def _vorticity_growth_timescale(field, data): domegax_dt = data[ftype, "vorticity_x"] / data[ftype, "vorticity_growth_x"] domegay_dt = data[ftype, "vorticity_y"] / data[ftype, "vorticity_growth_y"] domegaz_dt = data[ftype, "vorticity_z"] / data[ftype, "vorticity_growth_z"] return np.sqrt(domegax_dt**2 + domegay_dt**2 + domegaz_dt**2) registry.add_field( (ftype, "vorticity_growth_timescale"), sampling_type="cell", function=_vorticity_growth_timescale, units=unit_system["time"], validators=vort_validators, ) ######################################################################## # With radiation pressure ######################################################################## def _vorticity_radiation_pressure_x(field, data): rho = data[ftype, "density"].astype("float64", copy=False) return ( data[ftype, "radiation_acceleration_y"] * data[ftype, "density_gradient_z"] - data[ftype, "radiation_acceleration_z"] * data[ftype, "density_gradient_y"] ) / rho def _vorticity_radiation_pressure_y(field, data): rho = data[ftype, "density"].astype("float64", copy=False) return ( data[ftype, "radiation_acceleration_z"] * data[ftype, "density_gradient_x"] - data[ftype, "radiation_acceleration_x"] * data[ftype, "density_gradient_z"] ) / rho def _vorticity_radiation_pressure_z(field, data): rho = data[ftype, "density"].astype("float64", copy=False) return ( data[ftype, "radiation_acceleration_x"] * data[ftype, "density_gradient_y"] - data[ftype, "radiation_acceleration_y"] * data[ftype, "density_gradient_x"] ) / rho vrp_validators = [ ValidateSpatial( 1, [ (ftype, "density"), (ftype, "radiation_acceleration_x"), (ftype, "radiation_acceleration_y"), (ftype, "radiation_acceleration_z"), ], ) ] for ax in "xyz": n = f"vorticity_radiation_pressure_{ax}" registry.add_field( (ftype, n), sampling_type="cell", function=eval(f"_{n}"), units=unit_system["frequency"] ** 2, validators=vrp_validators, ) create_magnitude_field( registry, "vorticity_radiation_pressure", unit_system["frequency"] ** 2, ftype=ftype, slice_info=slice_info, validators=vrp_validators, ) def _vorticity_radiation_pressure_growth_x(field, data): return ( -data[ftype, "vorticity_stretching_x"] - data[ftype, "baroclinic_vorticity_x"] - data[ftype, "vorticity_radiation_pressure_x"] ) def _vorticity_radiation_pressure_growth_y(field, data): return ( -data[ftype, "vorticity_stretching_y"] - data[ftype, "baroclinic_vorticity_y"] - data[ftype, "vorticity_radiation_pressure_y"] ) def _vorticity_radiation_pressure_growth_z(field, data): return ( -data[ftype, "vorticity_stretching_z"] - data[ftype, "baroclinic_vorticity_z"] - data[ftype, "vorticity_radiation_pressure_z"] ) for ax in "xyz": n = f"vorticity_radiation_pressure_growth_{ax}" registry.add_field( (ftype, n), sampling_type="cell", function=eval(f"_{n}"), units=unit_system["frequency"] ** 2, validators=vrp_validators, ) def _vorticity_radiation_pressure_growth_magnitude(field, data): result = np.sqrt( data[ftype, "vorticity_radiation_pressure_growth_x"] ** 2 + data[ftype, "vorticity_radiation_pressure_growth_y"] ** 2 + data[ftype, "vorticity_radiation_pressure_growth_z"] ** 2 ) dot = data.ds.arr(np.zeros(result.shape), "") for ax in "xyz": dot += ( data[ftype, f"vorticity_{ax}"] * data[ftype, f"vorticity_growth_{ax}"] ).to_ndarray() result = np.sign(dot) * result return result registry.add_field( (ftype, "vorticity_radiation_pressure_growth_magnitude"), sampling_type="cell", function=_vorticity_radiation_pressure_growth_magnitude, units=unit_system["frequency"] ** 2, validators=vrp_validators, take_log=False, ) def _vorticity_radiation_pressure_growth_magnitude_absolute(field, data): return np.sqrt( data[ftype, "vorticity_radiation_pressure_growth_x"] ** 2 + data[ftype, "vorticity_radiation_pressure_growth_y"] ** 2 + data[ftype, "vorticity_radiation_pressure_growth_z"] ** 2 ) registry.add_field( (ftype, "vorticity_radiation_pressure_growth_magnitude_absolute"), sampling_type="cell", function=_vorticity_radiation_pressure_growth_magnitude_absolute, units="s**(-2)", validators=vrp_validators, ) def _vorticity_radiation_pressure_growth_timescale(field, data): domegax_dt = ( data[ftype, "vorticity_x"] / data[ftype, "vorticity_radiation_pressure_growth_x"] ) domegay_dt = ( data[ftype, "vorticity_y"] / data[ftype, "vorticity_radiation_pressure_growth_y"] ) domegaz_dt = ( data[ftype, "vorticity_z"] / data[ftype, "vorticity_radiation_pressure_growth_z"] ) return np.sqrt(domegax_dt**2 + domegay_dt**2 + domegaz_dt**2) registry.add_field( (ftype, "vorticity_radiation_pressure_growth_timescale"), sampling_type="cell", function=_vorticity_radiation_pressure_growth_timescale, units=unit_system["time"], validators=vrp_validators, ) def _shear(field, data): """ Shear is defined as [(dvx/dy + dvy/dx)^2 + (dvz/dy + dvy/dz)^2 + (dvx/dz + dvz/dx)^2 ]^(0.5) where dvx/dy = [vx(j-1) - vx(j+1)]/[2dy] and is in units of s^(-1) (it's just like vorticity except add the derivative pairs instead of subtracting them) """ if data.ds.geometry != "cartesian": raise NotImplementedError("shear is only supported in cartesian geometries") try: vx = data[ftype, "relative_velocity_x"] vy = data[ftype, "relative_velocity_y"] except YTFieldNotFound as e: raise YTDimensionalityError( "shear computation requires 2 velocity components" ) from e dvydx = ( vy[sl_right, sl_center, sl_center] - vy[sl_left, sl_center, sl_center] ) / (div_fac * just_one(data["index", "dx"])) dvxdy = ( vx[sl_center, sl_right, sl_center] - vx[sl_center, sl_left, sl_center] ) / (div_fac * just_one(data["index", "dy"])) f = (dvydx + dvxdy) ** 2.0 del dvydx, dvxdy try: vz = data[ftype, "relative_velocity_z"] dvzdy = ( vz[sl_center, sl_right, sl_center] - vz[sl_center, sl_left, sl_center] ) / (div_fac * just_one(data["index", "dy"])) dvydz = ( vy[sl_center, sl_center, sl_right] - vy[sl_center, sl_center, sl_left] ) / (div_fac * just_one(data["index", "dz"])) f += (dvzdy + dvydz) ** 2.0 del dvzdy, dvydz dvxdz = ( vx[sl_center, sl_center, sl_right] - vx[sl_center, sl_center, sl_left] ) / (div_fac * just_one(data["index", "dz"])) dvzdx = ( vz[sl_right, sl_center, sl_center] - vz[sl_left, sl_center, sl_center] ) / (div_fac * just_one(data["index", "dx"])) f += (dvxdz + dvzdx) ** 2.0 del dvxdz, dvzdx except YTFieldNotFound: # the absence of a z velocity component is not blocking pass np.sqrt(f, out=f) new_field = data.ds.arr(np.zeros_like(data[ftype, "velocity_x"]), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field registry.add_field( (ftype, "shear"), sampling_type="cell", function=_shear, validators=[ ValidateSpatial( 1, [(ftype, "velocity_x"), (ftype, "velocity_y"), (ftype, "velocity_z")] ), ValidateParameter("bulk_velocity"), ], units=unit_system["frequency"], ) def _shear_criterion(field, data): """ Divide by c_s to leave shear in units of length**-1, which can be compared against the inverse of the local cell size (1/dx) to determine if refinement should occur. """ return data[ftype, "shear"] / data[ftype, "sound_speed"] registry.add_field( (ftype, "shear_criterion"), sampling_type="cell", function=_shear_criterion, units=unit_system["length"] ** -1, validators=[ ValidateSpatial( 1, [ (ftype, "sound_speed"), (ftype, "velocity_x"), (ftype, "velocity_y"), (ftype, "velocity_z"), ], ) ], ) def _shear_mach(field, data): """ Dimensionless shear (shear_mach) is defined nearly the same as shear, except that it is scaled by the local dx/dy/dz and the local sound speed. So it results in a unitless quantity that is effectively measuring shear in mach number. In order to avoid discontinuities created by multiplying by dx/dy/dz at grid refinement boundaries, we also multiply by 2**GridLevel. Shear (Mach) = [(dvx + dvy)^2 + (dvz + dvy)^2 + (dvx + dvz)^2 ]^(0.5) / c_sound """ if data.ds.geometry != "cartesian": raise NotImplementedError( "shear_mach is only supported in cartesian geometries" ) try: vx = data[ftype, "relative_velocity_x"] vy = data[ftype, "relative_velocity_y"] except YTFieldNotFound as e: raise YTDimensionalityError( "shear_mach computation requires 2 velocity components" ) from e dvydx = ( vy[sl_right, sl_center, sl_center] - vy[sl_left, sl_center, sl_center] ) / div_fac dvxdy = ( vx[sl_center, sl_right, sl_center] - vx[sl_center, sl_left, sl_center] ) / div_fac f = (dvydx + dvxdy) ** 2.0 del dvydx, dvxdy try: vz = data[ftype, "relative_velocity_z"] dvzdy = ( vz[sl_center, sl_right, sl_center] - vz[sl_center, sl_left, sl_center] ) / div_fac dvydz = ( vy[sl_center, sl_center, sl_right] - vy[sl_center, sl_center, sl_left] ) / div_fac f += (dvzdy + dvydz) ** 2.0 del dvzdy, dvydz dvxdz = ( vx[sl_center, sl_center, sl_right] - vx[sl_center, sl_center, sl_left] ) / div_fac dvzdx = ( vz[sl_right, sl_center, sl_center] - vz[sl_left, sl_center, sl_center] ) / div_fac f += (dvxdz + dvzdx) ** 2.0 del dvxdz, dvzdx except YTFieldNotFound: # the absence of a z velocity component is not blocking pass f *= ( 2.0 ** data["index", "grid_level"][sl_center, sl_center, sl_center] / data[ftype, "sound_speed"][sl_center, sl_center, sl_center] ) ** 2.0 np.sqrt(f, out=f) new_field = data.ds.arr(np.zeros_like(vx), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field vs_fields = [ (ftype, "sound_speed"), (ftype, "velocity_x"), (ftype, "velocity_y"), (ftype, "velocity_z"), ] registry.add_field( (ftype, "shear_mach"), sampling_type="cell", function=_shear_mach, units="", validators=[ValidateSpatial(1, vs_fields), ValidateParameter("bulk_velocity")], ) yt-project-yt-f043ac8/yt/fields/geometric_fields.py000066400000000000000000000203441510711153200224350ustar00rootroot00000000000000import numpy as np from yt.utilities.lib.geometry_utils import compute_morton from yt.utilities.math_utils import ( get_cyl_r, get_cyl_theta, get_cyl_z, get_sph_phi, get_sph_r, get_sph_theta, ) from .derived_field import ValidateParameter, ValidateSpatial from .field_functions import get_periodic_rvec, get_radius from .field_plugin_registry import register_field_plugin @register_field_plugin def setup_geometric_fields(registry, ftype="gas", slice_info=None): unit_system = registry.ds.unit_system def _radius(field, data): """The spherical radius component of the mesh cells. Relative to the coordinate system defined by the *center* field parameter. """ return get_radius(data, "", field.name[0]) registry.add_field( ("index", "radius"), sampling_type="cell", function=_radius, validators=[ValidateParameter("center")], units=unit_system["length"], ) def _grid_level(field, data): """The AMR refinement level""" arr = np.ones(data.ires.shape, dtype="float64") arr *= data.ires if data._spatial: return data._reshape_vals(arr) return arr registry.add_field( ("index", "grid_level"), sampling_type="cell", function=_grid_level, units="", validators=[ValidateSpatial(0)], ) def _grid_indices(field, data): """The index of the leaf grid the mesh cells exist on""" if hasattr(data, "domain_id"): this_id = data.domain_id else: this_id = data.id - data._id_offset arr = np.ones(data["index", "ones"].shape) arr *= this_id if data._spatial: return data._reshape_vals(arr) return arr registry.add_field( ("index", "grid_indices"), sampling_type="cell", function=_grid_indices, units="", validators=[ValidateSpatial(0)], take_log=False, ) def _ones_over_dx(field, data): """The inverse of the local cell spacing""" return ( np.ones(data["index", "ones"].shape, dtype="float64") / data["index", "dx"] ) registry.add_field( ("index", "ones_over_dx"), sampling_type="cell", function=_ones_over_dx, units=unit_system["length"] ** -1, display_field=False, ) def _zeros(field, data): """Returns zero for all cells""" arr = np.zeros(data["index", "ones"].shape, dtype="float64") return data.apply_units(arr, field.units) registry.add_field( ("index", "zeros"), sampling_type="cell", function=_zeros, units="", display_field=False, ) def _ones(field, data): """Returns one for all cells""" tmp = np.ones(data.ires.shape, dtype="float64") arr = data.apply_units(tmp, field.units) if data._spatial: return data._reshape_vals(arr) return arr registry.add_field( ("index", "ones"), sampling_type="cell", function=_ones, units="", display_field=False, ) def _morton_index(field, data): """This is the morton index, which is properly a uint64 field. Because we make some assumptions that the fields returned by derived fields are float64, this returns a "view" on the data that is float64. To get back the original uint64, you need to call .view("uint64") on it; however, it should be true that if you sort the uint64, you will get the same order as if you sort the float64 view. """ eps = np.finfo("f8").eps uq = data.ds.domain_left_edge.uq LE = data.ds.domain_left_edge - eps * uq RE = data.ds.domain_right_edge + eps * uq # .ravel() only copies if it needs to morton = compute_morton( data["index", "x"].ravel(), data["index", "y"].ravel(), data["index", "z"].ravel(), LE, RE, ).reshape(data["index", "x"].shape) return morton.view("f8") registry.add_field( ("index", "morton_index"), sampling_type="cell", function=_morton_index, units="", ) def _spherical_radius(field, data): """The spherical radius component of the positions of the mesh cells. Relative to the coordinate system defined by the *center* field parameter. """ coords = get_periodic_rvec(data) return data.ds.arr(get_sph_r(coords), "code_length").in_base(unit_system.name) registry.add_field( ("index", "spherical_radius"), sampling_type="cell", function=_spherical_radius, validators=[ValidateParameter("center")], units=unit_system["length"], ) def _spherical_theta(field, data): """The spherical theta component of the positions of the mesh cells. theta is the poloidal position angle in the plane parallel to the *normal* vector Relative to the coordinate system defined by the *center* and *normal* field parameters. """ normal = data.get_field_parameter("normal") coords = get_periodic_rvec(data) return get_sph_theta(coords, normal) registry.add_field( ("index", "spherical_theta"), sampling_type="cell", function=_spherical_theta, validators=[ValidateParameter("center"), ValidateParameter("normal")], units="", ) def _spherical_phi(field, data): """The spherical phi component of the positions of the mesh cells. phi is the azimuthal position angle in the plane perpendicular to the *normal* vector Relative to the coordinate system defined by the *center* and *normal* field parameters. """ normal = data.get_field_parameter("normal") coords = get_periodic_rvec(data) return get_sph_phi(coords, normal) registry.add_field( ("index", "spherical_phi"), sampling_type="cell", function=_spherical_phi, validators=[ValidateParameter("center"), ValidateParameter("normal")], units="", ) def _cylindrical_radius(field, data): """The cylindrical radius component of the positions of the mesh cells. Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") coords = get_periodic_rvec(data) return data.ds.arr(get_cyl_r(coords, normal), "code_length").in_base( unit_system.name ) registry.add_field( ("index", "cylindrical_radius"), sampling_type="cell", function=_cylindrical_radius, validators=[ValidateParameter("center"), ValidateParameter("normal")], units=unit_system["length"], ) def _cylindrical_z(field, data): """The cylindrical z component of the positions of the mesh cells. Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") coords = get_periodic_rvec(data) return data.ds.arr(get_cyl_z(coords, normal), "code_length").in_base( unit_system.name ) registry.add_field( ("index", "cylindrical_z"), sampling_type="cell", function=_cylindrical_z, validators=[ValidateParameter("center"), ValidateParameter("normal")], units=unit_system["length"], ) def _cylindrical_theta(field, data): """The cylindrical z component of the positions of the mesh cells. theta is the azimuthal position angle in the plane perpendicular to the *normal* vector. Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") coords = get_periodic_rvec(data) return get_cyl_theta(coords, normal) registry.add_field( ("index", "cylindrical_theta"), sampling_type="cell", function=_cylindrical_theta, validators=[ValidateParameter("center"), ValidateParameter("normal")], units="", ) yt-project-yt-f043ac8/yt/fields/interpolated_fields.py000066400000000000000000000025271510711153200231540ustar00rootroot00000000000000from yt.fields.local_fields import add_field from yt.utilities.linear_interpolators import ( BilinearFieldInterpolator, TrilinearFieldInterpolator, UnilinearFieldInterpolator, ) _int_class = { 1: UnilinearFieldInterpolator, 2: BilinearFieldInterpolator, 3: TrilinearFieldInterpolator, } def add_interpolated_field( name, units, table_data, axes_data, axes_fields, ftype="gas", particle_type=False, validators=None, truncate=True, ): if len(table_data.shape) not in _int_class: raise RuntimeError( "Interpolated field can only be created from 1d, 2d, or 3d data." ) if len(axes_fields) != len(axes_data) or len(axes_fields) != len(table_data.shape): raise RuntimeError( "Data dimension mismatch: data is %d, " "%d axes data provided, and %d axes fields provided." % (len(table_data.shape), len(axes_data), len(axes_fields)) ) int_class = _int_class[len(table_data.shape)] my_interpolator = int_class(table_data, axes_data, axes_fields, truncate=truncate) def _interpolated_field(field, data): return my_interpolator(data) add_field( (ftype, name), function=_interpolated_field, units=units, validators=validators, particle_type=particle_type, ) yt-project-yt-f043ac8/yt/fields/local_fields.py000066400000000000000000000051421510711153200215500ustar00rootroot00000000000000from collections.abc import Callable from functools import partial from typing import Any, TypeVar from yt.funcs import is_sequence from yt.utilities.logger import ytLogger as mylog from .field_info_container import FieldInfoContainer from .field_plugin_registry import register_field_plugin # workaround mypy not being comfortable around decorator preserving signatures # adapted from # https://github.com/python/mypy/issues/1551#issuecomment-253978622 TFun = TypeVar("TFun", bound=Callable[..., Any]) class LocalFieldInfoContainer(FieldInfoContainer): def add_field( self, name, function, sampling_type, *, force_override=False, **kwargs ): from yt.fields.field_functions import validate_field_function validate_field_function(function) if isinstance(name, str) or not is_sequence(name): # the base method only accepts proper tuple field keys # and is only used internally, while this method is exposed to users # and is documented as usable with single strings as name if sampling_type == "particle": ftype = "all" else: ftype = "gas" name = (ftype, name) # Handle the case where the field has already been added. if not force_override and name in self: mylog.warning( "Field %s already exists. To override use `force_override=True`.", name, ) return super().add_field( name, function, sampling_type, force_override=force_override, **kwargs ) # Empty FieldInfoContainer local_fields = LocalFieldInfoContainer(None, [], None) # we define two handles, essentially pointing to the same function but documented differently # yt.add_field() is meant to be used directly, while yt.derived_field is documented # as a decorator. add_field = local_fields.add_field class derived_field: # implement a decorator accepting keyword arguments to be passed down to add_field def __init__(self, **kwargs) -> None: self._kwargs = kwargs def __call__(self, f: Callable) -> Callable: partial(local_fields.add_field, function=f)(**self._kwargs) return f @register_field_plugin def setup_local_fields(registry, ftype="gas", slice_info=None): # This is easy. We just update with the contents of the local_fields field # info container, and since they are not mutable in any real way, we are # fine. # Note that we actually don't care about the ftype here. for f in local_fields: registry._show_field_errors.append(f) registry.update(local_fields) yt-project-yt-f043ac8/yt/fields/magnetic_field.py000066400000000000000000000303311510711153200220600ustar00rootroot00000000000000import sys import numpy as np from yt._typing import FieldType from yt.fields.derived_field import ValidateParameter from yt.fields.field_info_container import FieldInfoContainer from yt.geometry.api import Geometry from yt.units import dimensions from .field_plugin_registry import register_field_plugin if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never cgs_normalizations = {"gaussian": 4.0 * np.pi, "lorentz_heaviside": 1.0} def get_magnetic_normalization(key: str) -> float: if key not in cgs_normalizations: raise ValueError( "Unknown magnetic normalization convention. " f"Got {key!r}, expected one of {tuple(cgs_normalizations)}" ) return cgs_normalizations[key] @register_field_plugin def setup_magnetic_field_fields( registry: FieldInfoContainer, ftype: FieldType = "gas", slice_info=None ) -> None: ds = registry.ds unit_system = ds.unit_system pc = registry.ds.units.physical_constants axis_names = registry.ds.coordinates.axis_order if (ftype, f"magnetic_field_{axis_names[0]}") not in registry: return u = registry[ftype, f"magnetic_field_{axis_names[0]}"].units def mag_factors(dims): if dims == dimensions.magnetic_field_cgs: return getattr(ds, "_magnetic_factor", 4.0 * np.pi) elif dims == dimensions.magnetic_field_mks: return ds.units.physical_constants.mu_0 def _magnetic_field_strength(field, data): xm = f"relative_magnetic_field_{axis_names[0]}" ym = f"relative_magnetic_field_{axis_names[1]}" zm = f"relative_magnetic_field_{axis_names[2]}" B2 = (data[ftype, xm]) ** 2 + (data[ftype, ym]) ** 2 + (data[ftype, zm]) ** 2 return np.sqrt(B2) registry.add_field( (ftype, "magnetic_field_strength"), sampling_type="local", function=_magnetic_field_strength, validators=[ValidateParameter("bulk_magnetic_field")], units=u, ) def _magnetic_energy_density(field, data): B = data[ftype, "magnetic_field_strength"] return 0.5 * B * B / mag_factors(B.units.dimensions) registry.add_field( (ftype, "magnetic_energy_density"), sampling_type="local", function=_magnetic_energy_density, units=unit_system["pressure"], ) def _plasma_beta(field, data): return data[ftype, "pressure"] / data[ftype, "magnetic_energy_density"] registry.add_field( (ftype, "plasma_beta"), sampling_type="local", function=_plasma_beta, units="" ) def _magnetic_pressure(field, data): return data[ftype, "magnetic_energy_density"] registry.add_field( (ftype, "magnetic_pressure"), sampling_type="local", function=_magnetic_pressure, units=unit_system["pressure"], ) _magnetic_field_poloidal_magnitude = None _magnetic_field_toroidal_magnitude = None geometry: Geometry = registry.ds.geometry if geometry is Geometry.CARTESIAN: def _magnetic_field_poloidal_magnitude(field, data): B2 = ( data[ftype, "relative_magnetic_field_x"] * data[ftype, "relative_magnetic_field_x"] + data[ftype, "relative_magnetic_field_y"] * data[ftype, "relative_magnetic_field_y"] + data[ftype, "relative_magnetic_field_z"] * data[ftype, "relative_magnetic_field_z"] ) Bt2 = ( data[ftype, "magnetic_field_spherical_phi"] * data[ftype, "magnetic_field_spherical_phi"] ) return np.sqrt(B2 - Bt2) elif geometry is Geometry.CYLINDRICAL or geometry is Geometry.POLAR: def _magnetic_field_poloidal_magnitude(field, data): bm = data.get_field_parameter("bulk_magnetic_field") rax = axis_names.index("r") zax = axis_names.index("z") return np.sqrt( (data[ftype, "magnetic_field_r"] - bm[rax]) ** 2 + (data[ftype, "magnetic_field_z"] - bm[zax]) ** 2 ) def _magnetic_field_toroidal_magnitude(field, data): ax = axis_names.find("theta") bm = data.get_field_parameter("bulk_magnetic_field") return data[ftype, "magnetic_field_theta"] - bm[ax] elif geometry is Geometry.SPHERICAL: def _magnetic_field_poloidal_magnitude(field, data): bm = data.get_field_parameter("bulk_magnetic_field") rax = axis_names.index("r") tax = axis_names.index("theta") return np.sqrt( (data[ftype, "magnetic_field_r"] - bm[rax]) ** 2 + (data[ftype, "magnetic_field_theta"] - bm[tax]) ** 2 ) def _magnetic_field_toroidal_magnitude(field, data): ax = axis_names.find("phi") bm = data.get_field_parameter("bulk_magnetic_field") return data[ftype, "magnetic_field_phi"] - bm[ax] elif geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC: # not implemented pass elif geometry is Geometry.SPECTRAL_CUBE: # nothing to be done pass else: assert_never(geometry) if _magnetic_field_poloidal_magnitude is not None: registry.add_field( (ftype, "magnetic_field_poloidal_magnitude"), sampling_type="local", function=_magnetic_field_poloidal_magnitude, units=u, validators=[ ValidateParameter("normal"), ValidateParameter("bulk_magnetic_field"), ], ) if _magnetic_field_toroidal_magnitude is not None: registry.add_field( (ftype, "magnetic_field_toroidal_magnitude"), sampling_type="local", function=_magnetic_field_toroidal_magnitude, units=u, validators=[ ValidateParameter("normal"), ValidateParameter("bulk_magnetic_field"), ], ) if geometry is Geometry.CARTESIAN: registry.alias( (ftype, "magnetic_field_toroidal_magnitude"), (ftype, "magnetic_field_spherical_phi"), units=u, ) registry.alias( (ftype, "magnetic_field_toroidal"), (ftype, "magnetic_field_spherical_phi"), units=u, deprecate=("4.1.0", None), ) registry.alias( (ftype, "magnetic_field_poloidal"), (ftype, "magnetic_field_spherical_theta"), units=u, deprecate=("4.1.0", None), ) elif ( geometry is Geometry.CYLINDRICAL or geometry is Geometry.POLAR or geometry is Geometry.SPHERICAL ): # These cases should be covered already, just check that they are assert (ftype, "magnetic_field_toroidal_magnitude") in registry assert (ftype, "magnetic_field_poloidal_magnitude") in registry elif geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC: # not implemented pass elif geometry is Geometry.SPECTRAL_CUBE: # nothing to be done pass else: assert_never(Geometry) def _alfven_speed(field, data): B = data[ftype, "magnetic_field_strength"] return B / np.sqrt(mag_factors(B.units.dimensions) * data[ftype, "density"]) registry.add_field( (ftype, "alfven_speed"), sampling_type="local", function=_alfven_speed, units=unit_system["velocity"], ) def _mach_alfven(field, data): return data[ftype, "velocity_magnitude"] / data[ftype, "alfven_speed"] registry.add_field( (ftype, "mach_alfven"), sampling_type="local", function=_mach_alfven, units="dimensionless", ) b_units = registry.ds.quan(1.0, u).units if dimensions.current_mks in b_units.dimensions.free_symbols: rm_scale = pc.qp.to("C", "SI") ** 3 / (4.0 * np.pi * pc.eps_0) else: rm_scale = pc.qp**3 / pc.clight rm_scale *= registry.ds.quan(1.0, "rad") / (2.0 * np.pi * pc.me**2 * pc.clight**3) rm_units = registry.ds.quan(1.0, "rad/m**2").units / unit_system["length"] def _rotation_measure(field, data): return ( rm_scale * data[ftype, "magnetic_field_los"] * data[ftype, "El_number_density"] ) registry.add_field( (ftype, "rotation_measure"), sampling_type="local", function=_rotation_measure, units=rm_units, validators=[ValidateParameter("axis", {"axis": [0, 1, 2]})], ) def setup_magnetic_field_aliases(registry, ds_ftype, ds_fields, ftype="gas"): r""" This routine sets up special aliases between dataset-specific magnetic fields and the default magnetic fields in yt so that unit conversions between different unit systems can be handled properly. This is only called from the `setup_fluid_fields` method (for grid dataset) or the `setup_gas_particle_fields` method (for particle dataset) of a frontend's :class:`FieldInfoContainer` instance. Parameters ---------- registry : :class:`FieldInfoContainer` The field registry that these definitions will be installed into. ds_ftype : string The field type for the fields we're going to alias, e.g. "flash", "enzo", "athena", "PartType0", etc. ds_fields : list of strings or string The fields that will be aliased. For grid dataset, this should be a list of strings corresponding to the components of magnetic field. For particle dataset, this should be a single string corresponding to the vector magnetic field. ftype : string, optional The resulting field type of the fields. Default "gas". Examples -------- >>> from yt.fields.magnetic_field import setup_magnetic_field_aliases >>> class PlutoFieldInfo(ChomboFieldInfo): ... def setup_fluid_fields(self): ... setup_magnetic_field_aliases( ... self, "chombo", ["bx%s" % ax for ax in [1, 2, 3]] ... ) >>> class GizmoFieldInfo(GadgetFieldInfo): ... def setup_gas_particle_fields(self): ... setup_magnetic_field_aliases( ... self, "PartType0", "MagneticField", ftype="PartType0" ... ) """ unit_system = registry.ds.unit_system if isinstance(ds_fields, list): # If ds_fields is a list, we assume a grid dataset sampling_type = "local" ds_fields = [(ds_ftype, fd) for fd in ds_fields] ds_field = ds_fields[0] else: # Otherwise, we assume a particle dataset sampling_type = "particle" ds_field = (ds_ftype, ds_fields) if ds_field not in registry: return # Figure out the unit conversion to use if unit_system.base_units[dimensions.current_mks] is not None: to_units = unit_system["magnetic_field_mks"] else: to_units = unit_system["magnetic_field_cgs"] units = unit_system[to_units.dimensions] # Add fields if sampling_type in ["cell", "local"]: # Grid dataset case def mag_field_from_field(fd): def _mag_field(field, data): return data[fd].to(field.units) return _mag_field for ax, fd in zip(registry.ds.coordinates.axis_order, ds_fields, strict=False): registry.add_field( (ftype, f"magnetic_field_{ax}"), sampling_type=sampling_type, function=mag_field_from_field(fd), units=units, ) else: # Particle dataset case def mag_field_from_ax(ax): def _mag_field(field, data): return data[ds_field][:, "xyz".index(ax)] return _mag_field for ax in registry.ds.coordinates.axis_order: fname = f"particle_magnetic_field_{ax}" registry.add_field( (ds_ftype, fname), sampling_type=sampling_type, function=mag_field_from_ax(ax), units=units, ) sph_ptypes = getattr(registry.ds, "_sph_ptypes", ()) if ds_ftype in sph_ptypes: registry.alias((ftype, f"magnetic_field_{ax}"), (ds_ftype, fname)) yt-project-yt-f043ac8/yt/fields/my_plugin_fields.py000066400000000000000000000007321510711153200224610ustar00rootroot00000000000000from .field_plugin_registry import register_field_plugin from .local_fields import LocalFieldInfoContainer # Empty FieldInfoContainer my_plugins_fields = LocalFieldInfoContainer(None, [], None) @register_field_plugin def setup_my_plugins_fields(registry, ftype="gas", slice_info=None): # fields end up inside this container when added via add_field in # my_plugins.py. See yt.funcs.enable_plugins to see how this is set up. registry.update(my_plugins_fields) yt-project-yt-f043ac8/yt/fields/particle_fields.py000066400000000000000000000674171510711153200222760ustar00rootroot00000000000000import numpy as np from yt.fields.derived_field import ValidateParameter, ValidateSpatial from yt.units._numpy_wrapper_functions import uconcatenate, ucross from yt.utilities.lib.misc_utilities import ( obtain_position_vector, obtain_relative_velocity_vector, ) from yt.utilities.math_utils import ( get_cyl_r, get_cyl_r_component, get_cyl_theta, get_cyl_theta_component, get_cyl_z, get_cyl_z_component, get_sph_phi, get_sph_phi_component, get_sph_r_component, get_sph_theta, get_sph_theta_component, modify_reference_frame, ) from .field_functions import get_radius from .vector_operations import create_los_field, create_magnitude_field sph_whitelist_fields = ( "density", "temperature", "metallicity", "thermal_energy", "smoothing_length", "star_formation_rate", "cold_fraction", "hot_temperature", "H_fraction", "He_fraction", "C_fraction", "Ca_fraction", "N_fraction", "O_fraction", "S_fraction", "Ne_fraction", "Mg_fraction", "Si_fraction", "Fe_fraction", "Na_fraction", "Al_fraction", "Ar_fraction", "Ni_fraction", "Ej_fraction", "H_density", "He_density", "C_density", "Ca_density", "N_density", "O_density", "S_density", "Ne_density", "Mg_density", "Si_density", "Fe_density", "Na_density", "Al_density", "Ar_density", "Ni_density", "Ej_density", ) def _field_concat(fname): def _AllFields(field, data): v = [] for ptype in data.ds.particle_types: if ptype == "all" or ptype in data.ds.known_filters: continue v.append(data[ptype, fname].copy()) rv = uconcatenate(v, axis=0) return rv return _AllFields def _field_concat_slice(fname, axi): def _AllFields(field, data): v = [] for ptype in data.ds.particle_types: if ptype == "all" or ptype in data.ds.known_filters: continue v.append(data[ptype, fname][:, axi]) rv = uconcatenate(v, axis=0) return rv return _AllFields def particle_deposition_functions(ptype, coord_name, mass_name, registry): unit_system = registry.ds.unit_system orig = set(registry.keys()) ptype_dn = ptype.replace("_", " ").title() def particle_count(field, data): pos = data[ptype, coord_name] d = data.deposit(pos, method="count") return data.apply_units(d, field.units) registry.add_field( ("deposit", f"{ptype}_count"), sampling_type="cell", function=particle_count, validators=[ValidateSpatial()], units="", display_name=rf"\mathrm{{{ptype_dn} Count}}", ) def particle_mass(field, data): pos = data[ptype, coord_name] pmass = data[ptype, mass_name] pmass.convert_to_units(field.units) d = data.deposit(pos, [pmass], method="sum") return data.apply_units(d, field.units) registry.add_field( ("deposit", f"{ptype}_mass"), sampling_type="cell", function=particle_mass, validators=[ValidateSpatial()], display_name=rf"\mathrm{{{ptype_dn} Mass}}", units=unit_system["mass"], ) def particle_density(field, data): pos = data[ptype, coord_name] pos.convert_to_units("code_length") mass = data[ptype, mass_name] mass.convert_to_units("code_mass") d = data.deposit(pos, [mass], method="sum") d = data.ds.arr(d, "code_mass") d /= data["index", "cell_volume"] return d registry.add_field( ("deposit", f"{ptype}_density"), sampling_type="cell", function=particle_density, validators=[ValidateSpatial()], display_name=rf"\mathrm{{{ptype_dn} Density}}", units=unit_system["density"], ) def particle_cic(field, data): pos = data[ptype, coord_name] d = data.deposit(pos, [data[ptype, mass_name]], method="cic") d = data.apply_units(d, data[ptype, mass_name].units) d /= data["index", "cell_volume"] return d registry.add_field( ("deposit", f"{ptype}_cic"), sampling_type="cell", function=particle_cic, validators=[ValidateSpatial()], display_name=rf"\mathrm{{{ptype_dn} CIC Density}}", units=unit_system["density"], ) def _get_density_weighted_deposit_field(fname, units, method): def _deposit_field(field, data): """ Create a grid field for particle quantities weighted by particle mass, using cloud-in-cell deposit. """ pos = data[ptype, "particle_position"] # Get back into density pden = data[ptype, "particle_mass"] top = data.deposit(pos, [pden * data[ptype, fname]], method=method) bottom = data.deposit(pos, [pden], method=method) top[bottom == 0] = 0.0 bnz = bottom.nonzero() top[bnz] /= bottom[bnz] d = data.ds.arr(top, units=units) return d return _deposit_field for ax in "xyz": for method, name in [("cic", "cic"), ("sum", "nn")]: function = _get_density_weighted_deposit_field( f"particle_velocity_{ax}", "code_velocity", method ) registry.add_field( ("deposit", ("%s_" + name + "_velocity_%s") % (ptype, ax)), sampling_type="cell", function=function, units=unit_system["velocity"], take_log=False, validators=[ValidateSpatial(0)], ) for method, name in [("cic", "cic"), ("sum", "nn")]: function = _get_density_weighted_deposit_field("age", "code_time", method) registry.add_field( ("deposit", ("%s_" + name + "_age") % (ptype)), sampling_type="cell", function=function, units=unit_system["time"], take_log=False, validators=[ValidateSpatial(0)], ) # Now some translation functions. def particle_ones(field, data): v = np.ones(data[ptype, coord_name].shape[0], dtype="float64") return data.apply_units(v, field.units) registry.add_field( (ptype, "particle_ones"), sampling_type="particle", function=particle_ones, units="", display_name=r"Particle Count", ) def particle_mesh_ids(field, data): pos = data[ptype, coord_name] ids = np.zeros(pos.shape[0], dtype="float64") - 1 # This is float64 in name only. It will be properly cast inside the # deposit operation. # _ids = ids.view("float64") data.deposit(pos, [ids], method="mesh_id") return data.apply_units(ids, "") registry.add_field( (ptype, "mesh_id"), sampling_type="particle", function=particle_mesh_ids, validators=[ValidateSpatial()], units="", ) return list(set(registry.keys()).difference(orig)) def particle_scalar_functions(ptype, coord_name, vel_name, registry): # Now we have to set up the various velocity and coordinate things. In the # future, we'll actually invert this and use the 3-component items # elsewhere, and stop using these. # Note that we pass in _ptype here so that it's defined inside the closure. def _get_coord_funcs(axi, _ptype): def _particle_velocity(field, data): return data[_ptype, vel_name][:, axi] def _particle_position(field, data): return data[_ptype, coord_name][:, axi] return _particle_velocity, _particle_position for axi, ax in enumerate("xyz"): v, p = _get_coord_funcs(axi, ptype) registry.add_field( (ptype, f"particle_velocity_{ax}"), sampling_type="particle", function=v, units="code_velocity", ) registry.add_field( (ptype, f"particle_position_{ax}"), sampling_type="particle", function=p, units="code_length", ) def particle_vector_functions(ptype, coord_names, vel_names, registry): unit_system = registry.ds.unit_system # This will column_stack a set of scalars to create vector fields. def _get_vec_func(_ptype, names): def particle_vectors(field, data): v = [data[_ptype, name].in_units(field.units) for name in names] return data.ds.arr(np.column_stack(v), v[0].units) return particle_vectors registry.add_field( (ptype, "particle_position"), sampling_type="particle", function=_get_vec_func(ptype, coord_names), units="code_length", ) registry.add_field( (ptype, "particle_velocity"), sampling_type="particle", function=_get_vec_func(ptype, vel_names), units=unit_system["velocity"], ) def get_angular_momentum_components(ptype, data, spos, svel): normal = data.ds.arr([0.0, 0.0, 1.0], "code_length") # default to simulation axis pos = data.ds.arr([data[ptype, spos % ax] for ax in "xyz"]).T vel = data.ds.arr([data[ptype, f"relative_{svel % ax}"] for ax in "xyz"]).T return pos, vel, normal def standard_particle_fields( registry, ptype, spos="particle_position_%s", svel="particle_velocity_%s" ): unit_system = registry.ds.unit_system def _particle_velocity_magnitude(field, data): """M{|v|}""" return np.sqrt( data[ptype, f"relative_{svel % 'x'}"] ** 2 + data[ptype, f"relative_{svel % 'y'}"] ** 2 + data[ptype, f"relative_{svel % 'z'}"] ** 2 ) registry.add_field( (ptype, "particle_velocity_magnitude"), sampling_type="particle", function=_particle_velocity_magnitude, take_log=False, units=unit_system["velocity"], ) create_los_field( registry, "particle_velocity", unit_system["velocity"], ftype=ptype, sampling_type="particle", ) def _particle_specific_angular_momentum(field, data): """Calculate the angular of a particle velocity. Returns a vector for each particle. """ center = data.get_field_parameter("center") pos, vel, normal = get_angular_momentum_components(ptype, data, spos, svel) L, r_vec, v_vec = modify_reference_frame(center, normal, P=pos, V=vel) # adding in the unit registry allows us to have a reference to the # dataset and thus we will always get the correct units after applying # the cross product. return ucross(r_vec, v_vec, registry=data.ds.unit_registry) registry.add_field( (ptype, "particle_specific_angular_momentum"), sampling_type="particle", function=_particle_specific_angular_momentum, units=unit_system["specific_angular_momentum"], validators=[ValidateParameter("center")], ) def _get_spec_ang_mom_comp(axi, ax, _ptype): def _particle_specific_angular_momentum_component(field, data): return data[_ptype, "particle_specific_angular_momentum"][:, axi] def _particle_angular_momentum_component(field, data): return ( data[_ptype, "particle_mass"] * data[ptype, f"particle_specific_angular_momentum_{ax}"] ) return ( _particle_specific_angular_momentum_component, _particle_angular_momentum_component, ) for axi, ax in enumerate("xyz"): f, v = _get_spec_ang_mom_comp(axi, ax, ptype) registry.add_field( (ptype, f"particle_specific_angular_momentum_{ax}"), sampling_type="particle", function=f, units=unit_system["specific_angular_momentum"], validators=[ValidateParameter("center")], ) registry.add_field( (ptype, f"particle_angular_momentum_{ax}"), sampling_type="particle", function=v, units=unit_system["angular_momentum"], validators=[ValidateParameter("center")], ) def _particle_angular_momentum(field, data): am = ( data[ptype, "particle_mass"] * data[ptype, "particle_specific_angular_momentum"].T ) return am.T registry.add_field( (ptype, "particle_angular_momentum"), sampling_type="particle", function=_particle_angular_momentum, units=unit_system["angular_momentum"], validators=[ValidateParameter("center")], ) create_magnitude_field( registry, "particle_angular_momentum", unit_system["angular_momentum"], sampling_type="particle", ftype=ptype, ) def _particle_radius(field, data): """The spherical radius component of the particle positions Relative to the coordinate system defined by the *normal* vector, and *center* field parameters. """ return get_radius(data, "particle_position_", field.name[0]) registry.add_field( (ptype, "particle_radius"), sampling_type="particle", function=_particle_radius, units=unit_system["length"], validators=[ValidateParameter("center")], ) def _relative_particle_position(field, data): """The cartesian particle positions in a rotated reference frame Relative to the coordinate system defined by *center* field parameter. Note that the orientation of the x and y axes are arbitrary. """ field_names = [(ptype, f"particle_position_{ax}") for ax in "xyz"] return obtain_position_vector(data, field_names=field_names).T registry.add_field( (ptype, "relative_particle_position"), sampling_type="particle", function=_relative_particle_position, units=unit_system["length"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _relative_particle_velocity(field, data): """The vector particle velocities in an arbitrary coordinate system Relative to the coordinate system defined by the *bulk_velocity* vector field parameter. Note that the orientation of the x and y axes are arbitrary. """ field_names = [(ptype, f"particle_velocity_{ax}") for ax in "xyz"] return obtain_relative_velocity_vector(data, field_names=field_names).T registry.add_field( (ptype, "relative_particle_velocity"), sampling_type="particle", function=_relative_particle_velocity, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _get_coord_funcs_relative(axi, _ptype): def _particle_pos_rel(field, data): return data[_ptype, "relative_particle_position"][:, axi] def _particle_vel_rel(field, data): return data[_ptype, "relative_particle_velocity"][:, axi] return _particle_vel_rel, _particle_pos_rel for axi, ax in enumerate("xyz"): v, p = _get_coord_funcs_relative(axi, ptype) registry.add_field( (ptype, f"particle_velocity_relative_{ax}"), sampling_type="particle", function=v, units="code_velocity", ) registry.add_field( (ptype, f"particle_position_relative_{ax}"), sampling_type="particle", function=p, units="code_length", ) registry.add_field( (ptype, f"relative_particle_velocity_{ax}"), sampling_type="particle", function=v, units="code_velocity", ) registry.add_field( (ptype, f"relative_particle_position_{ax}"), sampling_type="particle", function=p, units="code_length", ) # this is just particle radius but we add it with an alias for the sake of # consistent naming registry.add_field( (ptype, "particle_position_spherical_radius"), sampling_type="particle", function=_particle_radius, units=unit_system["length"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_position_spherical_theta(field, data): """The spherical theta coordinate of the particle positions. Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T return data.ds.arr(get_sph_theta(pos, normal), "") registry.add_field( (ptype, "particle_position_spherical_theta"), sampling_type="particle", function=_particle_position_spherical_theta, units="", validators=[ValidateParameter("center"), ValidateParameter("normal")], ) def _particle_position_spherical_phi(field, data): """The spherical phi component of the particle positions Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T return data.ds.arr(get_sph_phi(pos, normal), "") registry.add_field( (ptype, "particle_position_spherical_phi"), sampling_type="particle", function=_particle_position_spherical_phi, units="", validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_velocity_spherical_radius(field, data): """The spherical radius component of the particle velocities in an arbitrary coordinate system Relative to the coordinate system defined by the *normal* vector, *bulk_velocity* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T vel = data[ptype, "relative_particle_velocity"].T theta = get_sph_theta(pos, normal) phi = get_sph_phi(pos, normal) sphr = get_sph_r_component(vel, theta, phi, normal) return sphr registry.add_field( (ptype, "particle_velocity_spherical_radius"), sampling_type="particle", function=_particle_velocity_spherical_radius, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) registry.alias( (ptype, "particle_radial_velocity"), (ptype, "particle_velocity_spherical_radius"), ) def _particle_velocity_spherical_theta(field, data): """The spherical theta component of the particle velocities in an arbitrary coordinate system Relative to the coordinate system defined by the *normal* vector, *bulk_velocity* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T vel = data[ptype, "relative_particle_velocity"].T theta = get_sph_theta(pos, normal) phi = get_sph_phi(pos, normal) spht = get_sph_theta_component(vel, theta, phi, normal) return spht registry.add_field( (ptype, "particle_velocity_spherical_theta"), sampling_type="particle", function=_particle_velocity_spherical_theta, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_velocity_spherical_phi(field, data): """The spherical phi component of the particle velocities Relative to the coordinate system defined by the *normal* vector, *bulk_velocity* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T vel = data[ptype, "relative_particle_velocity"].T phi = get_sph_phi(pos, normal) sphp = get_sph_phi_component(vel, phi, normal) return sphp registry.add_field( (ptype, "particle_velocity_spherical_phi"), sampling_type="particle", function=_particle_velocity_spherical_phi, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_position_cylindrical_radius(field, data): """The cylindrical radius component of the particle positions Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T pos.convert_to_units("code_length") return data.ds.arr(get_cyl_r(pos, normal), "code_length") registry.add_field( (ptype, "particle_position_cylindrical_radius"), sampling_type="particle", function=_particle_position_cylindrical_radius, units=unit_system["length"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_position_cylindrical_theta(field, data): """The cylindrical theta component of the particle positions Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T return data.ds.arr(get_cyl_theta(pos, normal), "") registry.add_field( (ptype, "particle_position_cylindrical_theta"), sampling_type="particle", function=_particle_position_cylindrical_theta, units="", validators=[ValidateParameter("center"), ValidateParameter("normal")], ) def _particle_position_cylindrical_z(field, data): """The cylindrical z component of the particle positions Relative to the coordinate system defined by the *normal* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T pos.convert_to_units("code_length") return data.ds.arr(get_cyl_z(pos, normal), "code_length") registry.add_field( (ptype, "particle_position_cylindrical_z"), sampling_type="particle", function=_particle_position_cylindrical_z, units=unit_system["length"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_velocity_cylindrical_radius(field, data): """The cylindrical radius component of the particle velocities Relative to the coordinate system defined by the *normal* vector, *bulk_velocity* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T vel = data[ptype, "relative_particle_velocity"].T theta = get_cyl_theta(pos, normal) cylr = get_cyl_r_component(vel, theta, normal) return cylr registry.add_field( (ptype, "particle_velocity_cylindrical_radius"), sampling_type="particle", function=_particle_velocity_cylindrical_radius, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_velocity_cylindrical_theta(field, data): """The cylindrical theta component of the particle velocities Relative to the coordinate system defined by the *normal* vector, *bulk_velocity* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") pos = data[ptype, "relative_particle_position"].T vel = data[ptype, "relative_particle_velocity"].T theta = get_cyl_theta(pos, normal) cylt = get_cyl_theta_component(vel, theta, normal) return cylt registry.add_field( (ptype, "particle_velocity_cylindrical_theta"), sampling_type="particle", function=_particle_velocity_cylindrical_theta, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def _particle_velocity_cylindrical_z(field, data): """The cylindrical z component of the particle velocities Relative to the coordinate system defined by the *normal* vector, *bulk_velocity* vector and *center* field parameters. """ normal = data.get_field_parameter("normal") vel = data[ptype, "relative_particle_velocity"].T cylz = get_cyl_z_component(vel, normal) return cylz registry.add_field( (ptype, "particle_velocity_cylindrical_z"), sampling_type="particle", function=_particle_velocity_cylindrical_z, units=unit_system["velocity"], validators=[ValidateParameter("normal"), ValidateParameter("center")], ) def add_particle_average(registry, ptype, field_name, weight=None, density=True): if weight is None: weight = (ptype, "particle_mass") field_units = registry[ptype, field_name].units def _pfunc_avg(field, data): pos = data[ptype, "particle_position"] f = data[ptype, field_name] wf = data[ptype, weight] f *= wf v = data.deposit(pos, [f], method="sum") w = data.deposit(pos, [wf], method="sum") v /= w if density: v /= data["index", "cell_volume"] v[np.isnan(v)] = 0.0 return v fn = ("deposit", f"{ptype}_avg_{field_name}") registry.add_field( fn, sampling_type="cell", function=_pfunc_avg, validators=[ValidateSpatial(0)], units=field_units, ) return fn def add_nearest_neighbor_field(ptype, coord_name, registry, nneighbors=64): field_name = (ptype, f"nearest_neighbor_distance_{nneighbors}") def _nth_neighbor(field, data): pos = data[ptype, coord_name] pos.convert_to_units("code_length") distances = 0.0 * pos[:, 0] data.particle_operation( pos, [distances], method="nth_neighbor", nneighbors=nneighbors ) # Now some quick unit conversions. return distances registry.add_field( field_name, sampling_type="particle", function=_nth_neighbor, validators=[ValidateSpatial(0)], units="code_length", ) return [field_name] def add_nearest_neighbor_value_field(ptype, coord_name, sampled_field, registry): """ This adds a nearest-neighbor field, where values on the mesh are assigned based on the nearest particle value found. This is useful, for instance, with voronoi-tesselations. """ field_name = ("deposit", f"{ptype}_nearest_{sampled_field}") field_units = registry[ptype, sampled_field].units unit_system = registry.ds.unit_system def _nearest_value(field, data): pos = data[ptype, coord_name] pos = pos.convert_to_units("code_length") value = data[ptype, sampled_field].in_base(unit_system.name) rv = data.smooth( pos, [value], method="nearest", create_octree=True, nneighbors=1 ) rv = data.apply_units(rv, field_units) return rv registry.add_field( field_name, sampling_type="cell", function=_nearest_value, validators=[ValidateSpatial(0)], units=field_units, ) return [field_name] def add_union_field(registry, ptype, field_name, units): """ Create a field that is the concatenation of multiple particle types. This allows us to create fields for particle unions using alias names. """ def _cat_field(field, data): return uconcatenate( [data[dep_type, field_name] for dep_type in data.ds.particle_types_raw] ) registry.add_field( (ptype, field_name), sampling_type="particle", function=_cat_field, units=units ) yt-project-yt-f043ac8/yt/fields/species_fields.py000066400000000000000000000267521510711153200221230ustar00rootroot00000000000000import re import numpy as np from yt.frontends.sph.data_structures import ParticleDataset from yt.utilities.chemical_formulas import ChemicalFormula from yt.utilities.physical_ratios import _primordial_mass_fraction from .field_plugin_registry import register_field_plugin # See YTEP-0003 for details, but we want to ensure these fields are all # populated: # # * _mass # * _density # * _fraction # * _number_density # def _create_fraction_func(ftype, species): def _frac(field, data): return data[ftype, f"{species}_density"] / data[ftype, "density"] return _frac def _mass_from_cell_volume_and_density(ftype, species): def _mass(field, data): return data[ftype, f"{species}_density"] * data["index", "cell_volume"] return _mass def _mass_from_particle_mass_and_fraction(ftype, species): def _mass(field, data): return data[ftype, f"{species}_fraction"] * data[ftype, "particle_mass"] return _mass def _create_number_density_func(ftype, species): formula = ChemicalFormula(species) def _number_density(field, data): weight = formula.weight # This is in AMU weight *= data.ds.quan(1.0, "amu").in_cgs() return data[ftype, f"{species}_density"] / weight return _number_density def _create_density_func(ftype, species): def _density(field, data): return data[ftype, f"{species}_fraction"] * data[ftype, "density"] return _density def add_species_field_by_density(registry, ftype, species): """ This takes a field registry, a fluid type, and a species name and then adds the other fluids based on that. This assumes that the field "SPECIES_density" already exists and refers to mass density. """ unit_system = registry.ds.unit_system registry.add_field( (ftype, f"{species}_fraction"), sampling_type="local", function=_create_fraction_func(ftype, species), units="", ) if isinstance(registry.ds, ParticleDataset): _create_mass_func = _mass_from_particle_mass_and_fraction else: _create_mass_func = _mass_from_cell_volume_and_density registry.add_field( (ftype, f"{species}_mass"), sampling_type="local", function=_create_mass_func(ftype, species), units=unit_system["mass"], ) registry.add_field( (ftype, f"{species}_number_density"), sampling_type="local", function=_create_number_density_func(ftype, species), units=unit_system["number_density"], ) return [ (ftype, f"{species}_number_density"), (ftype, f"{species}_density"), (ftype, f"{species}_mass"), ] def add_species_field_by_fraction(registry, ftype, species): """ This takes a field registry, a fluid type, and a species name and then adds the other fluids based on that. This assumes that the field "SPECIES_fraction" already exists and refers to mass fraction. """ unit_system = registry.ds.unit_system registry.add_field( (ftype, f"{species}_density"), sampling_type="local", function=_create_density_func(ftype, species), units=unit_system["density"], ) if isinstance(registry.ds, ParticleDataset): _create_mass_func = _mass_from_particle_mass_and_fraction else: _create_mass_func = _mass_from_cell_volume_and_density registry.add_field( (ftype, f"{species}_mass"), sampling_type="local", function=_create_mass_func(ftype, species), units=unit_system["mass"], ) registry.add_field( (ftype, f"{species}_number_density"), sampling_type="local", function=_create_number_density_func(ftype, species), units=unit_system["number_density"], ) return [ (ftype, f"{species}_number_density"), (ftype, f"{species}_density"), (ftype, f"{species}_mass"), ] def add_species_aliases(registry, ftype, alias_species, species): r""" This takes a field registry, a fluid type, and two species names. The first species name is one you wish to alias to an existing species name. For instance you might alias all "H_p0" fields to "H\_" fields to indicate that "H\_" fields are really just neutral hydrogen fields. This function registers field aliases for the density, number_density, mass, and fraction fields between the two species given in the arguments. """ registry.alias((ftype, f"{alias_species}_density"), (ftype, f"{species}_density")) registry.alias((ftype, f"{alias_species}_fraction"), (ftype, f"{species}_fraction")) registry.alias( (ftype, f"{alias_species}_number_density"), (ftype, f"{species}_number_density"), ) registry.alias((ftype, f"{alias_species}_mass"), (ftype, f"{species}_mass")) def add_deprecated_species_aliases(registry, ftype, alias_species, species): """ Add the species aliases but with deprecation warnings. """ for suffix in ["density", "fraction", "number_density", "mass"]: add_deprecated_species_alias(registry, ftype, alias_species, species, suffix) def add_deprecated_species_alias(registry, ftype, alias_species, species, suffix): """ Add a deprecated species alias field. """ unit_system = registry.ds.unit_system if suffix == "fraction": my_units = "" else: my_units = unit_system[suffix] def _dep_field(field, data): return data[ftype, f"{species}_{suffix}"] registry.add_field( (ftype, f"{alias_species}_{suffix}"), sampling_type="local", function=_dep_field, units=my_units, ) def add_nuclei_density_fields(registry, ftype): unit_system = registry.ds.unit_system elements = _get_all_elements(registry.species_names) for element in elements: registry.add_field( (ftype, f"{element}_nuclei_density"), sampling_type="local", function=_nuclei_density, units=unit_system["number_density"], ) # Here, we add default nuclei and number density fields for H and # He if they are not defined above, and if it was requested by # setting "default_species_fields" if registry.ds.default_species_fields is None: return dsf = registry.ds.default_species_fields # Right now, this only handles default fields for H and He for element in ["H", "He"]: # If these elements are already present in the dataset, # DO NOT set them if element in elements: continue # First add the default nuclei density fields registry.add_field( (ftype, f"{element}_nuclei_density"), sampling_type="local", function=_default_nuclei_density, units=unit_system["number_density"], ) # Set up number density fields for hydrogen, either fully ionized or neutral. if element == "H": if dsf == "ionized": state = "p1" elif dsf == "neutral": state = "p0" else: raise NotImplementedError( f"'default_species_fields' option '{dsf}' is not implemented!" ) registry.alias( (ftype, f"H_{state}_number_density"), (ftype, "H_nuclei_density") ) # Set up number density fields for helium, either fully ionized or neutral. if element == "He": if dsf == "ionized": state = "p2" elif dsf == "neutral": state = "p0" registry.alias( (ftype, f"He_{state}_number_density"), (ftype, "He_nuclei_density") ) # If we're fully ionized, we need to setup the electron number density field if (ftype, "El_number_density") not in registry and dsf == "ionized": registry.add_field( (ftype, "El_number_density"), sampling_type="local", function=_default_nuclei_density, units=unit_system["number_density"], ) def _default_nuclei_density(field, data): ftype = field.name[0] element = field.name[1][: field.name[1].find("_")] amu_cgs = data.ds.quan(1.0, "amu").in_cgs() if element == "El": # This is for determining the electron number density. # If we got here, this assumes full ionization! muinv = 1.0 * _primordial_mass_fraction["H"] / ChemicalFormula("H").weight muinv += 2.0 * _primordial_mass_fraction["He"] / ChemicalFormula("He").weight else: # This is for anything else besides electrons muinv = _primordial_mass_fraction[element] / ChemicalFormula(element).weight return data[ftype, "density"] * muinv / amu_cgs def _nuclei_density(field, data): ftype = field.name[0] element = field.name[1][: field.name[1].find("_")] nuclei_mass_field = f"{element}_nuclei_mass_density" if (ftype, nuclei_mass_field) in data.ds.field_info: return ( data[ftype, nuclei_mass_field] / ChemicalFormula(element).weight / data.ds.quan(1.0, "amu").in_cgs() ) metal_field = f"{element}_metallicity" if (ftype, metal_field) in data.ds.field_info: return ( data[ftype, "density"] * data[ftype, metal_field] / ChemicalFormula(element).weight / data.ds.quan(1.0, "amu").in_cgs() ) field_data = np.zeros_like( data[ftype, f"{data.ds.field_info.species_names[0]}_number_density"] ) for species in data.ds.field_info.species_names: nucleus = species if "_" in species: nucleus = species[: species.find("_")] # num is the number of nuclei contributed by this species. num = _get_element_multiple(nucleus, element) # Since this is a loop over all species existing in this dataset, # we will encounter species that contribute nothing, so we skip them. if num == 0: continue field_data += num * data[ftype, f"{species}_number_density"] return field_data def _get_all_elements(species_list): elements = [] for species in species_list: for item in re.findall("[A-Z][a-z]?|[0-9]+", species): if not item.isdigit() and item not in elements and item != "El": elements.append(item) return elements def _get_element_multiple(compound, element): my_split = re.findall("[A-Z][a-z]?|[0-9]+", compound) if element not in my_split: return 0 loc = my_split.index(element) if loc == len(my_split) - 1 or not my_split[loc + 1].isdigit(): return 1 return int(my_split[loc + 1]) @register_field_plugin def setup_species_fields(registry, ftype="gas", slice_info=None): for species in registry.species_names: # These are all the species we should be looking for fractions or # densities of. if (ftype, f"{species}_density") in registry: func = add_species_field_by_density elif (ftype, f"{species}_fraction") in registry: func = add_species_field_by_fraction else: # Skip it continue func(registry, ftype, species) # Add aliases of X_p0_ to X_. # These are deprecated and will be removed soon. if ChemicalFormula(species).charge == 0: alias_species = species.split("_")[0] if (ftype, f"{alias_species}_density") in registry: continue add_deprecated_species_aliases(registry, "gas", alias_species, species) add_nuclei_density_fields(registry, ftype) yt-project-yt-f043ac8/yt/fields/tensor_fields.py000066400000000000000000000021031510711153200217620ustar00rootroot00000000000000from functools import partial # This is the metric for Minkowski spacetime in SR def metric(mu: int, nu: int): # This assumes the -+++ signature if (mu, nu) == (0, 0): return -1 elif mu == nu: return 1 else: return 0 def setup_stress_energy_ideal(registry, ftype="gas"): ax = ("t",) + registry.ds.coordinates.axis_order pc = registry.ds.units.physical_constants inv_c2 = 1.0 / (pc.clight * pc.clight) def _T(field, data, mu: int, nu: int): Umu = data[ftype, f"four_velocity_{ax[mu]}"] Unu = data[ftype, f"four_velocity_{ax[nu]}"] p = data[ftype, "pressure"] e = data[ftype, "thermal_energy_density"] rho = data[ftype, "density"] return (rho + (e + p) * inv_c2) * Umu * Unu + metric(mu, nu) * p for mu in range(5): for nu in range(5): registry.add_field( (ftype, f"T{mu}{nu}"), sampling_type="local", function=partial(_T, mu=mu, nu=nu), units=registry.ds.unit_system["pressure"], ) yt-project-yt-f043ac8/yt/fields/tests/000077500000000000000000000000001510711153200177165ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/fields/tests/__init__.py000066400000000000000000000000001510711153200220150ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/fields/tests/test_ambiguous_fields.py000066400000000000000000000024361510711153200246550ustar00rootroot00000000000000import pytest from yt.testing import fake_amr_ds def test_ambiguous_fails(): ds = fake_amr_ds(particles=10) msg = "The requested field name '{}' is ambiguous" def _mock_field(field, data): return data["ones"] # create a pair of ambiguous field ds.add_field(("io", "mock_field"), _mock_field, sampling_type="particle") ds.add_field(("gas", "mock_field"), _mock_field, sampling_type="cell") # Test errors are raised for ambiguous fields with pytest.raises(ValueError, match=msg.format("mock_field")): ds.r["mock_field"] # check that explicit name tuples don't raise a warning ds.r["io", "mock_field"] ds.r["gas", "mock_field"] def test_nameonly_field_with_all_aliases_candidates(): # see https://github.com/yt-project/yt/issues/3839 ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) # here we rely on implementations details from fake_amr_ds, # so we verify that it provides the appropriate conditions # for the actual test. candidates = [f for f in ds.derived_field_list if f[1] == "density"] assert len(candidates) == 2 fi = ds.field_info assert fi[candidates[0]].is_alias_to(fi[candidates[1]]) # this is the actual test (check that no error or warning is raised) ds.all_data()["density"] yt-project-yt-f043ac8/yt/fields/tests/test_angular_momentum.py000066400000000000000000000013721510711153200247040ustar00rootroot00000000000000import numpy as np from yt.testing import assert_allclose_units, fake_amr_ds def test_AM_value(): ds = fake_amr_ds( fields=("Density", "velocity_x", "velocity_y", "velocity_z"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), length_unit=0.5, ) sp = ds.sphere([0.5] * 3, (0.1, "code_length")) x0 = sp.center v0 = ds.arr([1, 2, 3], "km/s") sp.set_field_parameter("bulk_velocity", v0) X = (ds.arr([sp["index", k] for k in "xyz"]) - x0[:, None]).T V = (ds.arr([sp["gas", f"velocity_{k}"] for k in "xyz"]) - v0[:, None]).T sAM_manual = ds.arr(np.cross(X, V), X.units * V.units) sAM = ds.arr([sp["gas", f"specific_angular_momentum_{k}"] for k in "xyz"]).T assert_allclose_units(sAM_manual, sAM) yt-project-yt-f043ac8/yt/fields/tests/test_field_access.py000066400000000000000000000026171510711153200237410ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.data_objects.profiles import create_profile from yt.testing import fake_random_ds from yt.visualization.plot_window import ProjectionPlot, SlicePlot from yt.visualization.profile_plotter import PhasePlot, ProfilePlot def test_field_access(): ds = fake_random_ds(16) ad = ds.all_data() sp = ds.sphere(ds.domain_center, 0.25) cg = ds.covering_grid(0, ds.domain_left_edge, ds.domain_dimensions) scg = ds.smoothed_covering_grid(0, ds.domain_left_edge, ds.domain_dimensions) sl = ds.slice(0, ds.domain_center[0]) proj = ds.proj(("gas", "density"), 0) prof = create_profile(ad, ("index", "radius"), ("gas", "density")) for data_object in [ad, sp, cg, scg, sl, proj, prof]: assert_equal(data_object["gas", "density"], data_object[ds.fields.gas.density]) for field in [("gas", "density"), ds.fields.gas.density]: ad = ds.all_data() prof = ProfilePlot(ad, ("index", "radius"), field) phase = PhasePlot(ad, ("index", "radius"), field, ("gas", "cell_mass")) s = SlicePlot(ds, 2, field) oas = SlicePlot(ds, [1, 1, 1], field) p = ProjectionPlot(ds, 2, field) oap = ProjectionPlot(ds, [1, 1, 1], field) for plot_object in [s, oas, p, oap, prof, phase]: plot_object.render() if hasattr(plot_object, "_frb"): plot_object._frb[field] yt-project-yt-f043ac8/yt/fields/tests/test_field_access_pytest.py000066400000000000000000000024361510711153200253500ustar00rootroot00000000000000from collections import defaultdict from itertools import product import pytest from yt.testing import fake_random_ds from yt.utilities.exceptions import YTFieldNotFound def test_unexisting_field_access(): ds = fake_random_ds(16, particles=10) fname2ftype = defaultdict(list) for ft, fn in ds.derived_field_list: fname2ftype[fn].append(ft) ad = ds.all_data() ftypes = ("gas", "io") fnames = ( "density", "particle_position_x", "particle_position_y", "particle_position_z", ) # Try invalid ftypes, fnames combinations for ft, fn in product(ftypes, fnames): if (ft, fn) in ds.derived_field_list: continue with pytest.raises(YTFieldNotFound) as excinfo: ad[ft, fn] # Make sure the existing field has been suggested for possible_ft in fname2ftype[fn]: assert str((possible_ft, fn)) in str(excinfo.value) # Try typos for bad_field, good_field in ( (("gas", "densi_y"), ("gas", "density")), (("oi", "particle_mass"), ("io", "particle_mass")), (("gas", "DENSITY"), ("gas", "density")), ): with pytest.raises(YTFieldNotFound) as excinfo: ad[bad_field] assert str(good_field) in str(excinfo.value) yt-project-yt-f043ac8/yt/fields/tests/test_field_name_container.py000066400000000000000000000032671510711153200254640ustar00rootroot00000000000000from yt import load from yt.testing import fake_amr_ds, fake_hexahedral_ds, requires_file, requires_module def do_field_type(ft): assert dir(ft) == sorted(dir(ft)) assert sorted(dir(ft)) == sorted(f.name[1] for f in ft) for field_name in dir(ft): f = getattr(ft, field_name) assert (ft.field_type, field_name) == f.name for field in ft: f = getattr(ft, field.name[1]) assert f == field assert f in ft assert f.name in ft assert f.name[1] in ft enzotiny = "enzo_tiny_cosmology/DD0046/DD0046" @requires_module("h5py") @requires_file(enzotiny) def test_field_name_container(): ds = load(enzotiny) assert dir(ds.fields) == sorted(dir(ds.fields)) assert sorted(ft.field_type for ft in ds.fields) == sorted(dir(ds.fields)) for field_type in dir(ds.fields): assert field_type in ds.fields ft = getattr(ds.fields, field_type) do_field_type(ft) for field_type in ds.fields: assert field_type in ds.fields do_field_type(field_type) def test_vertex_fields_only_in_unstructured_ds(): def get_vertex_fields(ds): return [(ft, fn) for ft, fn in ds.derived_field_list if "vertex" in fn] ds = fake_amr_ds() vertex_fields = get_vertex_fields(ds) assert not vertex_fields ds = fake_hexahedral_ds() actual = get_vertex_fields(ds) expected = [ ("all", "vertex_x"), ("all", "vertex_y"), ("all", "vertex_z"), ("connect1", "vertex_x"), ("connect1", "vertex_y"), ("connect1", "vertex_z"), ("index", "vertex_x"), ("index", "vertex_y"), ("index", "vertex_z"), ] assert actual == expected yt-project-yt-f043ac8/yt/fields/tests/test_fields.py000066400000000000000000000422631510711153200226040ustar00rootroot00000000000000from dataclasses import dataclass import numpy as np from numpy.testing import ( assert_almost_equal, assert_array_almost_equal_nulp, assert_array_equal, assert_equal, assert_raises, ) from yt import load from yt.data_objects.static_output import Dataset from yt.frontends.stream.fields import StreamFieldInfo from yt.testing import ( assert_allclose_units, fake_amr_ds, fake_particle_ds, fake_random_ds, requires_file, requires_module, ) from yt.units.yt_array import YTArray, YTQuantity, array_like_field from yt.utilities.cosmology import Cosmology from yt.utilities.exceptions import ( YTDimensionalityError, YTFieldUnitError, YTFieldUnitParseError, ) def get_params(ds): return { "axis": 0, "center": YTArray((0.0, 0.0, 0.0), "cm", registry=ds.unit_registry), "bulk_velocity": YTArray((0.0, 0.0, 0.0), "cm/s", registry=ds.unit_registry), "bulk_magnetic_field": YTArray((0.0, 0.0, 0.0), "G", registry=ds.unit_registry), "normal": YTArray((0.0, 0.0, 1.0), "", registry=ds.unit_registry), "cp_x_vec": YTArray((1.0, 0.0, 0.0), "", registry=ds.unit_registry), "cp_y_vec": YTArray((0.0, 1.0, 0.0), "", registry=ds.unit_registry), "cp_z_vec": YTArray((0.0, 0.0, 1.0), "", registry=ds.unit_registry), "omega_baryon": 0.04, "observer_redshift": 0.0, "source_redshift": 3.0, "virial_radius": YTQuantity(1.0, "cm"), } _base_fields = ( ("gas", "density"), ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z"), ) def _strip_ftype(field): if not isinstance(field, tuple): return field elif field[0] in ("all", "io"): return field return field[1] @dataclass(slots=True, frozen=True) class FieldAccessTestCase: field_name: str ds: Dataset nprocs: int @property def description(self) -> str: return f"Accessing_{self.field_name}_{self.nprocs}" def get_base_ds(nprocs): fields, units = [], [] for fname, (code_units, *_) in StreamFieldInfo.known_other_fields: fields.append(("gas", fname)) units.append(code_units) pfields, punits = [], [] for fname, (code_units, _aliases, _dn) in StreamFieldInfo.known_particle_fields: if fname == "smoothing_lenth": # we test SPH fields elsewhere continue pfields.append(fname) punits.append(code_units) ds = fake_random_ds( 4, fields=fields, units=units, particles=20, nprocs=nprocs, particle_fields=pfields, particle_field_units=punits, ) ds.parameters["HydroMethod"] = "streaming" ds.parameters["EOSType"] = 1.0 ds.parameters["EOSSoundSpeed"] = 1.0 ds.conversion_factors["Time"] = 1.0 ds.conversion_factors.update({f: 1.0 for f in fields}) ds.gamma = 5.0 / 3.0 ds.current_redshift = 0.0001 ds.cosmological_simulation = 1 ds.hubble_constant = 0.7 ds.omega_matter = 0.27 ds.omega_lambda = 0.73 ds.cosmology = Cosmology( hubble_constant=ds.hubble_constant, omega_matter=ds.omega_matter, omega_lambda=ds.omega_lambda, unit_registry=ds.unit_registry, ) # ensures field errors are raised during testing # see FieldInfoContainer.check_derived_fields ds._field_test_dataset = True ds.index return ds def test_all_fields(): datasets = {} for nprocs in [1, 4, 8]: ds = get_base_ds(nprocs) datasets[nprocs] = ds for field in sorted(ds.field_info): if field[1].find("beta_p") > -1: continue if field[1].find("vertex") > -1: # don't test the vertex fields for now continue if field[1].find("smoothed") > -1: # smoothed fields aren't implemented for grid data continue if field in ds.field_list: # Don't know how to test this. We need some way of having fields # that are fallbacks be tested, but we don't have that now. continue for nprocs in [1, 4, 8]: test_all_fields.__name__ = f"{field}_{nprocs}" tc = FieldAccessTestCase(field, datasets[nprocs], nprocs) field = tc.ds._get_field_info(tc.field_name) skip_grids = False needs_spatial = False for v in field.validators: if getattr(v, "ghost_zones", 0) > 0: skip_grids = True if hasattr(v, "ghost_zones"): needs_spatial = True ds = tc.ds # This gives unequal sized grids as well as subgrids dd1 = ds.all_data() dd2 = ds.all_data() sp = get_params(ds) dd1.field_parameters.update(sp) dd2.field_parameters.update(sp) with np.errstate(all="ignore"): v1 = dd1[tc.field_name] # No more conversion checking assert_equal(v1, dd1[tc.field_name]) if not needs_spatial: with field.unit_registry(dd2): res = field._function(field, dd2) res = dd2.apply_units(res, field.units) assert_array_almost_equal_nulp(v1, res, 4) if not skip_grids: for g in ds.index.grids: g.field_parameters.update(sp) v1 = g[tc.field_name] g.clear_data() g.field_parameters.update(sp) r1 = field._function(field, g) if field.sampling_type == "particle": assert_equal(v1.shape[0], g.NumberOfParticles) else: assert_array_equal(r1.shape, v1.shape) for ax in "xyz": assert_array_equal(g["index", ax].shape, v1.shape) with field.unit_registry(g): res = field._function(field, g) assert_array_equal(v1.shape, res.shape) res = g.apply_units(res, field.units) assert_array_almost_equal_nulp(v1, res, 4) def test_add_deposited_particle_field(): # NOT tested: "std", "mesh_id", "nearest" and "simple_smooth" base_ds = get_base_ds(1) ad = base_ds.all_data() # Test "count", "sum" and "cic" method for method in ["count", "sum", "cic"]: fn = base_ds.add_deposited_particle_field(("io", "particle_mass"), method) expected_fn = "io_%s" if method == "count" else "io_%s_mass" assert_equal(fn, ("deposit", expected_fn % method)) ret = ad[fn] if method == "count": assert_equal(ret.sum(), ad["io", "particle_ones"].sum()) else: assert_almost_equal(ret.sum(), ad["io", "particle_mass"].sum()) # Test "weighted_mean" method fn = base_ds.add_deposited_particle_field( ("io", "particle_ones"), "weighted_mean", weight_field="particle_ones" ) assert_equal(fn, ("deposit", "io_avg_ones")) ret = ad[fn] # The sum should equal the number of cells that have particles assert_equal(ret.sum(), np.count_nonzero(ad["deposit", "io_count"])) def test_add_gradient_fields(): ds = get_base_ds(1) gfields = ds.add_gradient_fields(("gas", "density")) gfields += ds.add_gradient_fields(("index", "ones")) field_list = [ ("gas", "density_gradient_x"), ("gas", "density_gradient_y"), ("gas", "density_gradient_z"), ("gas", "density_gradient_magnitude"), ("index", "ones_gradient_x"), ("index", "ones_gradient_y"), ("index", "ones_gradient_z"), ("index", "ones_gradient_magnitude"), ] assert_equal(gfields, field_list) ad = ds.all_data() for field in field_list: ret = ad[field] if field[0] == "gas": assert str(ret.units) == "g/cm**4" else: assert str(ret.units) == "1/cm" def test_add_gradient_fields_by_fname(): ds = fake_amr_ds(fields=("density", "temperature"), units=("g/cm**3", "K")) actual = ds.add_gradient_fields(("gas", "density")) expected = [ ("gas", "density_gradient_x"), ("gas", "density_gradient_y"), ("gas", "density_gradient_z"), ("gas", "density_gradient_magnitude"), ] assert_equal(actual, expected) def test_add_gradient_multiple_fields(): ds = fake_amr_ds(fields=("density", "temperature"), units=("g/cm**3", "K")) actual = ds.add_gradient_fields([("gas", "density"), ("gas", "temperature")]) expected = [ ("gas", "density_gradient_x"), ("gas", "density_gradient_y"), ("gas", "density_gradient_z"), ("gas", "density_gradient_magnitude"), ("gas", "temperature_gradient_x"), ("gas", "temperature_gradient_y"), ("gas", "temperature_gradient_z"), ("gas", "temperature_gradient_magnitude"), ] assert_equal(actual, expected) ds = fake_amr_ds(fields=("density", "temperature"), units=("g/cm**3", "K")) actual = ds.add_gradient_fields([("gas", "density"), ("gas", "temperature")]) assert_equal(actual, expected) def test_add_gradient_fields_curvilinear(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"], geometry="spherical") gfields = ds.add_gradient_fields(("gas", "density")) gfields += ds.add_gradient_fields(("index", "ones")) field_list = [ ("gas", "density_gradient_r"), ("gas", "density_gradient_theta"), ("gas", "density_gradient_phi"), ("gas", "density_gradient_magnitude"), ("index", "ones_gradient_r"), ("index", "ones_gradient_theta"), ("index", "ones_gradient_phi"), ("index", "ones_gradient_magnitude"), ] assert_equal(gfields, field_list) ad = ds.all_data() for field in field_list: ret = ad[field] if field[0] == "gas": assert str(ret.units) == "g/cm**4" else: assert str(ret.units) == "1/cm" def get_data(ds, field_name): # Need to create a new data object otherwise the errors we are # intentionally raising lead to spurious GenerationInProgress errors ad = ds.all_data() return ad[field_name] def test_add_field_unit_semantics(): ds = fake_random_ds(16) ad = ds.all_data() def density_alias(field, data): return data["gas", "density"].in_cgs() def unitless_data(field, data): return np.ones(data["gas", "density"].shape) ds.add_field( ("gas", "density_alias_auto"), sampling_type="cell", function=density_alias, units="auto", dimensions="density", ) ds.add_field( ("gas", "density_alias_wrong_units"), function=density_alias, sampling_type="cell", units="m/s", ) ds.add_field( ("gas", "density_alias_unparseable_units"), sampling_type="cell", function=density_alias, units="dragons", ) ds.add_field( ("gas", "density_alias_auto_wrong_dims"), function=density_alias, sampling_type="cell", units="auto", dimensions="temperature", ) assert_raises(YTFieldUnitError, get_data, ds, ("gas", "density_alias_wrong_units")) assert_raises( YTFieldUnitParseError, get_data, ds, ("gas", "density_alias_unparseable_units") ) assert_raises( YTDimensionalityError, get_data, ds, ("gas", "density_alias_auto_wrong_dims") ) dens = ad["gas", "density_alias_auto"] assert_equal(str(dens.units), "g/cm**3") ds.add_field(("gas", "dimensionless"), sampling_type="cell", function=unitless_data) ds.add_field( ("gas", "dimensionless_auto"), function=unitless_data, sampling_type="cell", units="auto", dimensions="dimensionless", ) ds.add_field( ("gas", "dimensionless_explicit"), function=unitless_data, sampling_type="cell", units="", ) ds.add_field( ("gas", "dimensionful"), sampling_type="cell", function=unitless_data, units="g/cm**3", ) assert_equal(str(ad["gas", "dimensionless"].units), "dimensionless") assert_equal(str(ad["gas", "dimensionless_auto"].units), "dimensionless") assert_equal(str(ad["gas", "dimensionless_explicit"].units), "dimensionless") assert_raises(YTFieldUnitError, get_data, ds, ("gas", "dimensionful")) def test_add_field_from_lambda(): ds = fake_amr_ds(fields=["density"], units=["g/cm**3"]) def _function_density(field, data): return data["gas", "density"] ds.add_field( ("gas", "function_density"), function=_function_density, sampling_type="cell", units="g/cm**3", ) ds.add_field( ("gas", "lambda_density"), function=lambda field, data: data["gas", "density"], sampling_type="cell", units="g/cm**3", ) ad = ds.all_data() # check that the fields are accessible ad["gas", "function_density"] ad["gas", "lambda_density"] def test_array_like_field(): ds = fake_random_ds(4, particles=64) ad = ds.all_data() u1 = ad["all", "particle_mass"].units u2 = array_like_field(ad, 1.0, ("all", "particle_mass")).units assert u1 == u2 ISOGAL = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(ISOGAL) def test_array_like_field_output_units(): ds = load(ISOGAL) ad = ds.all_data() u1 = ad["all", "particle_mass"].units u2 = array_like_field(ad, 1.0, ("all", "particle_mass")).units assert u1 == u2 assert str(u1) == ds.fields.all.particle_mass.output_units u1 = ad["gas", "x"].units u2 = array_like_field(ad, 1.0, ("gas", "x")).units assert u1 == u2 assert str(u1) == ds.fields.gas.x.units def test_add_field_string(): ds = fake_random_ds(16) ad = ds.all_data() def density_alias(field, data): return data["gas", "density"] ds.add_field( ("gas", "density_alias"), sampling_type="cell", function=density_alias, units="g/cm**3", ) ad["gas", "density_alias"] assert ("gas", "density_alias") in ds.derived_field_list def test_add_field_string_aliasing(): ds = fake_random_ds(16) def density_alias(field, data): return data["gas", "density"] ds.add_field( ("gas", "density_alias"), sampling_type="cell", function=density_alias, units="g/cm**3", ) ds.field_info["gas", "density_alias"] ds.field_info["gas", "density_alias"] ds = fake_particle_ds() def pmass_alias(field, data): return data["all", "particle_mass"] ds.add_field( ("all", "particle_mass_alias"), function=pmass_alias, units="g", sampling_type="particle", ) ds.field_info["all", "particle_mass_alias"] ds.field_info["all", "particle_mass_alias"] def test_morton_index(): ds = fake_amr_ds() mi = ds.r["index", "morton_index"] mi2 = mi.view("uint64") assert_equal(np.unique(mi2).size, mi2.size) a1 = np.argsort(mi) a2 = np.argsort(mi2) assert_array_equal(a1, a2) @requires_module("h5py") @requires_file(ISOGAL) def test_deposit_amr(): ds = load(ISOGAL) for g in ds.index.grids: gpm = g["all", "particle_mass"].sum() dpm = g["deposit", "all_mass"].sum() assert_allclose_units(gpm, dpm) def test_ion_field_labels(): fields = [ "O_p1_number_density", "O2_p1_number_density", "CO2_p1_number_density", "Co_p1_number_density", "O2_p2_number_density", "H2O_p1_number_density", ] units = ["cm**-3" for f in fields] ds = fake_random_ds(16, fields=fields, units=units) # by default labels should use roman numerals default_labels = { "O_p1_number_density": "$\\rm{O\\ II\\ Number\\ Density}$", "O2_p1_number_density": "$\\rm{O_{2}\\ II\\ Number\\ Density}$", "CO2_p1_number_density": "$\\rm{CO_{2}\\ II\\ Number\\ Density}$", "Co_p1_number_density": "$\\rm{Co\\ II\\ Number\\ Density}$", "O2_p2_number_density": "$\\rm{O_{2}\\ III\\ Number\\ Density}$", "H2O_p1_number_density": "$\\rm{H_{2}O\\ II\\ Number\\ Density}$", } pm_labels = { "O_p1_number_density": "$\\rm{{O}^{+}\\ Number\\ Density}$", "O2_p1_number_density": "$\\rm{{O_{2}}^{+}\\ Number\\ Density}$", "CO2_p1_number_density": "$\\rm{{CO_{2}}^{+}\\ Number\\ Density}$", "Co_p1_number_density": "$\\rm{{Co}^{+}\\ Number\\ Density}$", "O2_p2_number_density": "$\\rm{{O_{2}}^{++}\\ Number\\ Density}$", "H2O_p1_number_density": "$\\rm{{H_{2}O}^{+}\\ Number\\ Density}$", } fobj = ds.fields.stream for f in fields: label = getattr(fobj, f).get_latex_display_name() assert_equal(label, default_labels[f]) ds.set_field_label_format("ionization_label", "plus_minus") fobj = ds.fields.stream for f in fields: label = getattr(fobj, f).get_latex_display_name() assert_equal(label, pm_labels[f]) def test_default_fluid_type_None(): """ Check for bad behavior when default_fluid_type is None. See PR #3710. """ ds = fake_amr_ds() ds.default_fluid_type = None ds.field_list yt-project-yt-f043ac8/yt/fields/tests/test_fields_plugins.py000066400000000000000000000040021510711153200243320ustar00rootroot00000000000000import os import shutil import tempfile import unittest from numpy.testing import assert_raises import yt from yt.config import ytcfg from yt.testing import fake_random_ds from yt.utilities.configure import YTConfig, config_dir _TEST_PLUGIN = "_test_plugin.py" _DUMMY_CFG_TOML = f"""[yt] log_level = 49 plugin_filename = "{_TEST_PLUGIN}" boolean_stuff = true chunk_size = 3 """ TEST_PLUGIN_FILE = """ import numpy as np def _myfunc(field, data): return np.random.random(data['gas', 'density'].shape) add_field(('gas', 'random'), dimensions='dimensionless', function=_myfunc, units='auto', sampling_type='local') constant = 3 def myfunc(): return constant*4 foobar = 17 """ class TestPluginFile(unittest.TestCase): @classmethod def setUpClass(cls): cls.xdg_config_home = os.environ.get("XDG_CONFIG_HOME") cls.tmpdir = tempfile.mkdtemp() os.mkdir(os.path.join(cls.tmpdir, "yt")) os.environ["XDG_CONFIG_HOME"] = cls.tmpdir with open(YTConfig.get_global_config_file(), mode="w") as fh: fh.write(_DUMMY_CFG_TOML) cls.plugin_path = os.path.join(config_dir(), ytcfg.get("yt", "plugin_filename")) with open(cls.plugin_path, mode="w") as fh: fh.write(TEST_PLUGIN_FILE) @classmethod def tearDownClass(cls): shutil.rmtree(cls.tmpdir) if cls.xdg_config_home: os.environ["XDG_CONFIG_HOME"] = cls.xdg_config_home else: os.environ.pop("XDG_CONFIG_HOME") def testCustomField(self): msg = f"INFO:yt:Loading plugins from {self.plugin_path}" with self.assertLogs("yt", level="INFO") as cm: yt.enable_plugins() self.assertEqual(cm.output, [msg]) ds = fake_random_ds(16) dd = ds.all_data() self.assertEqual(str(dd["gas", "random"].units), "dimensionless") self.assertEqual(dd["gas", "random"].shape, dd["gas", "density"].shape) assert yt.myfunc() == 12 assert_raises(AttributeError, getattr, yt, "foobar") yt-project-yt-f043ac8/yt/fields/tests/test_magnetic_fields.py000066400000000000000000000104161510711153200244460ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal from yt.loaders import load_uniform_grid from yt.utilities.physical_constants import mu_0 def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_magnetic_fields(): ddims = (16, 16, 16) data1 = { "magnetic_field_x": (np.random.random(size=ddims), "T"), "magnetic_field_y": (np.random.random(size=ddims), "T"), "magnetic_field_z": (np.random.random(size=ddims), "T"), } data2 = {} for field in data1: data2[field] = (data1[field][0] * 1.0e4, "gauss") ds0 = load_uniform_grid(data1, ddims, unit_system="cgs") ds1 = load_uniform_grid(data1, ddims, magnetic_unit=(1.0, "T"), unit_system="cgs") ds2 = load_uniform_grid(data2, ddims, unit_system="mks") # For this test dataset, code units are cgs units ds3 = load_uniform_grid(data2, ddims, unit_system="code") # For this test dataset, code units are SI units ds4 = load_uniform_grid(data1, ddims, magnetic_unit=(1.0, "T"), unit_system="code") ds0.index ds1.index ds2.index ds3.index ds4.index dd0 = ds0.all_data() dd1 = ds1.all_data() dd2 = ds2.all_data() dd3 = ds3.all_data() dd4 = ds4.all_data() assert ds0.fields.gas.magnetic_field_strength.units == "G" assert ds1.fields.gas.magnetic_field_strength.units == "G" assert ds1.fields.gas.magnetic_field_poloidal.units == "G" assert ds1.fields.gas.magnetic_field_toroidal.units == "G" assert ds2.fields.gas.magnetic_field_strength.units == "T" assert ds2.fields.gas.magnetic_field_poloidal.units == "T" assert ds2.fields.gas.magnetic_field_toroidal.units == "T" assert ds3.fields.gas.magnetic_field_strength.units == "code_magnetic" assert ds3.fields.gas.magnetic_field_poloidal.units == "code_magnetic" assert ds3.fields.gas.magnetic_field_toroidal.units == "code_magnetic" assert ds4.fields.gas.magnetic_field_strength.units == "code_magnetic" assert ds4.fields.gas.magnetic_field_poloidal.units == "code_magnetic" assert ds4.fields.gas.magnetic_field_toroidal.units == "code_magnetic" emag0 = ( dd0["gas", "magnetic_field_x"] ** 2 + dd0["gas", "magnetic_field_y"] ** 2 + dd0["gas", "magnetic_field_z"] ** 2 ) / (8.0 * np.pi) emag0.convert_to_units("dyne/cm**2") emag1 = ( dd1["gas", "magnetic_field_x"] ** 2 + dd1["gas", "magnetic_field_y"] ** 2 + dd1["gas", "magnetic_field_z"] ** 2 ) / (8.0 * np.pi) emag1.convert_to_units("dyne/cm**2") emag2 = ( dd2["gas", "magnetic_field_x"] ** 2 + dd2["gas", "magnetic_field_y"] ** 2 + dd2["gas", "magnetic_field_z"] ** 2 ) / (2.0 * mu_0) emag2.convert_to_units("Pa") emag3 = ( dd3["gas", "magnetic_field_x"] ** 2 + dd3["gas", "magnetic_field_y"] ** 2 + dd3["gas", "magnetic_field_z"] ** 2 ) / (8.0 * np.pi) emag3.convert_to_units("code_pressure") emag4 = ( dd4["gas", "magnetic_field_x"] ** 2 + dd4["gas", "magnetic_field_y"] ** 2 + dd4["gas", "magnetic_field_z"] ** 2 ) / (2.0 * mu_0) emag4.convert_to_units("code_pressure") # note that "magnetic_energy_density" and "magnetic_pressure" are aliased assert_almost_equal(emag0, dd0["gas", "magnetic_energy_density"]) assert_almost_equal(emag1, dd1["gas", "magnetic_energy_density"]) assert_almost_equal(emag2, dd2["gas", "magnetic_energy_density"]) assert_almost_equal(emag3, dd3["gas", "magnetic_energy_density"]) assert_almost_equal(emag4, dd4["gas", "magnetic_energy_density"]) assert str(emag0.units) == str(dd0["gas", "magnetic_energy_density"].units) assert str(emag1.units) == str(dd1["gas", "magnetic_energy_density"].units) assert str(emag2.units) == str(dd2["gas", "magnetic_energy_density"].units) assert str(emag3.units) == str(dd3["gas", "magnetic_energy_density"].units) assert str(emag4.units) == str(dd4["gas", "magnetic_energy_density"].units) assert_almost_equal(emag1.in_cgs(), emag0.in_cgs()) assert_almost_equal(emag2.in_cgs(), emag0.in_cgs()) assert_almost_equal(emag1.in_cgs(), emag2.in_cgs()) assert_almost_equal(emag1.in_cgs(), emag3.in_cgs()) assert_almost_equal(emag1.in_cgs(), emag4.in_cgs()) yt-project-yt-f043ac8/yt/fields/tests/test_particle_fields.py000066400000000000000000000031631510711153200244630ustar00rootroot00000000000000import numpy as np from yt.testing import assert_allclose_units, requires_file, requires_module from yt.utilities.answer_testing.framework import data_dir_load g30 = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(g30) def test_relative_particle_fields(): ds = data_dir_load(g30) offset = ds.arr([0.1, -0.2, 0.3], "code_length") c = ds.domain_center + offset sp = ds.sphere(c, (10, "kpc")) bv = ds.arr([1.0, 2.0, 3.0], "code_velocity") sp.set_field_parameter("bulk_velocity", bv) assert_allclose_units( sp["all", "relative_particle_position"], sp["all", "particle_position"] - c ) assert_allclose_units( sp["all", "relative_particle_velocity"], sp["all", "particle_velocity"] - bv ) @requires_module("h5py") @requires_file(g30) def test_los_particle_fields(): ds = data_dir_load(g30) offset = ds.arr([0.1, -0.2, 0.3], "code_length") c = ds.domain_center + offset sp = ds.sphere(c, (10, "kpc")) bv = ds.arr([1.0, 2.0, 3.0], "code_velocity") sp.set_field_parameter("bulk_velocity", bv) ax = [0.1, 0.2, -0.3] sp.set_field_parameter("axis", ax) ax /= np.linalg.norm(ax) vlos = ( sp["all", "relative_particle_velocity_x"] * ax[0] + sp["all", "relative_particle_velocity_y"] * ax[1] + sp["all", "relative_particle_velocity_z"] * ax[2] ) assert_allclose_units(sp["all", "particle_velocity_los"], vlos) sp.clear_data() ax = 2 sp.set_field_parameter("axis", ax) vlos = sp["all", "relative_particle_velocity_z"] assert_allclose_units(sp["all", "particle_velocity_los"], vlos) yt-project-yt-f043ac8/yt/fields/tests/test_species_fields.py000066400000000000000000000064211510711153200243130ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.testing import assert_allclose_units, fake_random_ds from yt.utilities.chemical_formulas import ChemicalFormula from yt.utilities.physical_ratios import _primordial_mass_fraction def test_default_species_fields(): # Test default case (no species fields) ds = fake_random_ds(32) sp = ds.sphere("c", (0.2, "unitary")) mu = 0.5924489101195808 assert_allclose_units(mu * sp["index", "ones"], sp["gas", "mean_molecular_weight"]) assert ("gas", "H_nuclei_density") not in ds.derived_field_list assert ("gas", "He_nuclei_density") not in ds.derived_field_list assert ("gas", "El_number_density") not in ds.derived_field_list assert ("gas", "H_p1_number_density") not in ds.derived_field_list assert ("gas", "He_p2_number_density") not in ds.derived_field_list assert ("gas", "H_p0_number_density") not in ds.derived_field_list assert ("gas", "He_p0_number_density") not in ds.derived_field_list # Test fully ionized case dsi = fake_random_ds(32, default_species_fields="ionized") spi = dsi.sphere("c", (0.2, "unitary")) amu_cgs = dsi.quan(1.0, "amu").in_cgs() mueinv = 1.0 * _primordial_mass_fraction["H"] / ChemicalFormula("H").weight mueinv *= spi["index", "ones"] mueinv += 2.0 * _primordial_mass_fraction["He"] / ChemicalFormula("He").weight mupinv = _primordial_mass_fraction["H"] / ChemicalFormula("H").weight mupinv *= spi["index", "ones"] muainv = _primordial_mass_fraction["He"] / ChemicalFormula("He").weight muainv *= spi["index", "ones"] mueinv2 = spi["gas", "El_number_density"] * amu_cgs / spi["gas", "density"] mupinv2 = spi["gas", "H_p1_number_density"] * amu_cgs / spi["gas", "density"] muainv2 = spi["gas", "He_p2_number_density"] * amu_cgs / spi["gas", "density"] assert_allclose_units(mueinv, mueinv2) assert_allclose_units(mupinv, mupinv2) assert_allclose_units(muainv, muainv2) assert_equal(spi["gas", "H_p1_number_density"], spi["gas", "H_nuclei_density"]) assert_equal(spi["gas", "He_p2_number_density"], spi["gas", "He_nuclei_density"]) mu = 0.5924489101195808 assert_allclose_units( mu * spi["index", "ones"], spi["gas", "mean_molecular_weight"] ) # Test fully neutral case dsn = fake_random_ds(32, default_species_fields="neutral") spn = dsn.sphere("c", (0.2, "unitary")) amu_cgs = dsn.quan(1.0, "amu").in_cgs() assert ("gas", "El_number_density") not in ds.derived_field_list mupinv = _primordial_mass_fraction["H"] / ChemicalFormula("H").weight mupinv *= spn["index", "ones"] muainv = _primordial_mass_fraction["He"] / ChemicalFormula("He").weight muainv *= spn["index", "ones"] mupinv2 = spn["gas", "H_p0_number_density"] * amu_cgs / spn["gas", "density"] muainv2 = spn["gas", "He_p0_number_density"] * amu_cgs / spn["gas", "density"] assert_allclose_units(mueinv, mueinv2) assert_allclose_units(mupinv, mupinv2) assert_allclose_units(muainv, muainv2) assert_equal(spn["gas", "H_p0_number_density"], spn["gas", "H_nuclei_density"]) assert_equal(spn["gas", "He_p0_number_density"], spn["gas", "He_nuclei_density"]) mu = 1.2285402715185552 assert_allclose_units( mu * spn["index", "ones"], spn["gas", "mean_molecular_weight"] ) yt-project-yt-f043ac8/yt/fields/tests/test_sph_fields.py000066400000000000000000000040561510711153200234540ustar00rootroot00000000000000from collections import defaultdict from numpy.testing import assert_array_almost_equal, assert_equal import yt from yt.testing import requires_file, skip isothermal_h5 = "IsothermalCollapse/snap_505.hdf5" isothermal_bin = "IsothermalCollapse/snap_505" snap_33 = "snapshot_033/snap_033.0.hdf5" tipsy_gal = "TipsyGalaxy/galaxy.00300" FIRE_m12i = "FIRE_M12i_ref11/snapshot_600.hdf5" iso_kwargs = { "bounding_box": [[-3, 3], [-3, 3], [-3, 3]], "unit_base": { "UnitLength_in_cm": 5.0e16, "UnitMass_in_g": 1.98992e33, "UnitVelocity_in_cm_per_s": 46385.190, }, } load_kwargs = defaultdict(dict) load_kwargs.update( { isothermal_h5: iso_kwargs, isothermal_bin: iso_kwargs, } ) gas_fields_to_particle_fields = { "temperature": "Temperature", "density": "Density", "velocity_x": "particle_velocity_x", "velocity_magnitude": "particle_velocity_magnitude", } @skip(reason="See https://github.com/yt-project/yt/issues/3909") @requires_file(isothermal_bin) @requires_file(isothermal_h5) @requires_file(snap_33) @requires_file(tipsy_gal) @requires_file(FIRE_m12i) def test_sph_field_semantics(): for ds_fn in [tipsy_gal, isothermal_h5, isothermal_bin, snap_33, FIRE_m12i]: yield sph_fields_validate, ds_fn def sph_fields_validate(ds_fn): ds = yt.load(ds_fn, **(load_kwargs[ds_fn])) ad = ds.all_data() for gf, pf in gas_fields_to_particle_fields.items(): gas_field = ad["gas", gf] part_field = ad[ds._sph_ptypes[0], pf] assert_array_almost_equal(gas_field, part_field) npart = ds.particle_type_counts[ds._sph_ptypes[0]] err_msg = f"Field {gf} is not the correct shape" assert_equal(npart, gas_field.shape[0], err_msg=err_msg) dd = ds.r[0.4:0.6, 0.4:0.6, 0.4:0.6] for i, ax in enumerate("xyz"): dd.set_field_parameter(f"cp_{ax}_vec", yt.YTArray([1, 1, 1])) dd.set_field_parameter("axis", i) dd.set_field_parameter("omega_baryon", 0.3) for f in ds.fields.gas: gas_field = dd[f] assert f.is_sph_field yt-project-yt-f043ac8/yt/fields/tests/test_vector_fields.py000066400000000000000000000120401510711153200241540ustar00rootroot00000000000000import numpy as np from yt.testing import ( assert_allclose_units, fake_random_ds, requires_file, requires_module, ) from yt.units import cm, s # type: ignore from yt.utilities.answer_testing.framework import data_dir_load from yt.visualization.volume_rendering.off_axis_projection import off_axis_projection def random_unit_vector(prng): v = prng.random_sample(3) while (v == 0).all(): v = prng.random_sample(3) return v / np.sqrt((v**2).sum()) def random_velocity_vector(prng): return 2e5 * prng.random_sample(3) - 1e5 def compare_vector_conversions(data_source): prng = np.random.RandomState(8675309) normals = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] + [ random_unit_vector(prng) for i in range(2) ] bulk_velocities = [random_velocity_vector(prng) for i in range(2)] for bv in bulk_velocities: bulk_velocity = bv * cm / s data_source.set_field_parameter("bulk_velocity", bulk_velocity) data_source.clear_data() vmag = data_source["gas", "velocity_magnitude"] vrad = data_source["gas", "velocity_spherical_radius"] for normal in normals: data_source.set_field_parameter("normal", normal) data_source.clear_data() assert_allclose_units(vrad, data_source["gas", "velocity_spherical_radius"]) vmag_new = data_source["gas", "velocity_magnitude"] assert_allclose_units(vmag, vmag_new) vmag_cart = np.sqrt( (data_source["gas", "velocity_x"] - bulk_velocity[0]) ** 2 + (data_source["gas", "velocity_y"] - bulk_velocity[1]) ** 2 + (data_source["gas", "velocity_z"] - bulk_velocity[2]) ** 2 ) assert_allclose_units(vmag, vmag_cart) vmag_cyl = np.sqrt( data_source["gas", "velocity_cylindrical_radius"] ** 2 + data_source["gas", "velocity_cylindrical_theta"] ** 2 + data_source["gas", "velocity_cylindrical_z"] ** 2 ) assert_allclose_units(vmag, vmag_cyl) vmag_sph = np.sqrt( data_source["gas", "velocity_spherical_radius"] ** 2 + data_source["gas", "velocity_spherical_theta"] ** 2 + data_source["gas", "velocity_spherical_phi"] ** 2 ) assert_allclose_units(vmag, vmag_sph) for i, d in enumerate("xyz"): assert_allclose_units( data_source["gas", f"velocity_{d}"] - bulk_velocity[i], data_source["gas", f"relative_velocity_{d}"], ) for i, ax in enumerate("xyz"): data_source.set_field_parameter("axis", i) data_source.clear_data() assert_allclose_units( data_source["gas", "velocity_los"], data_source["gas", f"relative_velocity_{ax}"], ) for i, ax in enumerate("xyz"): prj = data_source.ds.proj( ("gas", "velocity_los"), i, weight_field=("gas", "density") ) assert_allclose_units( prj["gas", "velocity_los"], prj["gas", f"velocity_{ax}"] ) data_source.clear_data() ax = [0.1, 0.2, -0.3] data_source.set_field_parameter("axis", ax) ax /= np.sqrt(np.dot(ax, ax)) vlos = data_source["gas", "relative_velocity_x"] * ax[0] vlos += data_source["gas", "relative_velocity_y"] * ax[1] vlos += data_source["gas", "relative_velocity_z"] * ax[2] assert_allclose_units(data_source["gas", "velocity_los"], vlos) buf_los = off_axis_projection( data_source, data_source.ds.domain_center, ax, 0.5, 128, ("gas", "velocity_los"), weight=("gas", "density"), ) buf_x = off_axis_projection( data_source, data_source.ds.domain_center, ax, 0.5, 128, ("gas", "relative_velocity_x"), weight=("gas", "density"), ) buf_y = off_axis_projection( data_source, data_source.ds.domain_center, ax, 0.5, 128, ("gas", "relative_velocity_y"), weight=("gas", "density"), ) buf_z = off_axis_projection( data_source, data_source.ds.domain_center, ax, 0.5, 128, ("gas", "relative_velocity_z"), weight=("gas", "density"), ) vlos = buf_x * ax[0] + buf_y * ax[1] + buf_z * ax[2] assert_allclose_units(buf_los, vlos, rtol=1.0e-6) def test_vector_component_conversions_fake(): ds = fake_random_ds(16) ad = ds.all_data() compare_vector_conversions(ad) g30 = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_module("h5py") @requires_file(g30) def test_vector_component_conversions_real(): ds = data_dir_load(g30) sp = ds.sphere(ds.domain_center, (10, "kpc")) compare_vector_conversions(sp) yt-project-yt-f043ac8/yt/fields/tests/test_xray_fields.py000066400000000000000000000042221510711153200236400ustar00rootroot00000000000000from yt.fields.xray_emission_fields import add_xray_emissivity_field from yt.utilities.answer_testing.framework import ( FieldValuesTest, ProjectionValuesTest, can_run_ds, data_dir_load, requires_ds, ) def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def check_xray_fields(ds_fn, fields): if not can_run_ds(ds_fn): return dso = [None, ("sphere", ("m", (0.1, "unitary")))] for field in fields: for dobj_name in dso: for axis in [0, 1, 2]: yield ProjectionValuesTest(ds_fn, axis, field, None, dobj_name) yield FieldValuesTest(ds_fn, field, dobj_name) sloshing = "GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0300" d9p = "D9p_500/10MpcBox_HartGal_csf_a0.500.d" @requires_ds(sloshing, big_data=True) def test_sloshing_apec(): ds = data_dir_load(sloshing, kwargs={"default_species_fields": "ionized"}) fields = add_xray_emissivity_field(ds, 0.5, 7.0, table_type="apec", metallicity=0.3) for test in check_xray_fields(ds, fields): test_sloshing_apec.__name__ = test.description yield test @requires_ds(d9p, big_data=True) def test_d9p_cloudy(): ds = data_dir_load(d9p, kwargs={"default_species_fields": "ionized"}) fields = add_xray_emissivity_field( ds, 0.5, 2.0, redshift=ds.current_redshift, table_type="cloudy", cosmology=ds.cosmology, metallicity=("gas", "metallicity"), ) for test in check_xray_fields(ds, fields): test.suffix = "current_redshift" test_d9p_cloudy.__name__ = test.description + test.suffix yield test @requires_ds(d9p, big_data=True) def test_d9p_cloudy_local(): ds = data_dir_load(d9p, kwargs={"default_species_fields": "ionized"}) fields = add_xray_emissivity_field( ds, 0.5, 2.0, dist=(1.0, "Mpc"), table_type="cloudy", metallicity=("gas", "metallicity"), ) for test in check_xray_fields(ds, fields): test.suffix = "dist_1Mpc" test_d9p_cloudy_local.__name__ = test.description + test.suffix yield test yt-project-yt-f043ac8/yt/fields/vector_operations.py000066400000000000000000000610531510711153200227000ustar00rootroot00000000000000import sys from typing import TYPE_CHECKING import numpy as np from unyt import Unit from yt._typing import FieldName, FieldType from yt.funcs import is_sequence, just_one from yt.geometry.api import Geometry from yt.utilities.lib.misc_utilities import obtain_relative_velocity_vector from yt.utilities.math_utils import ( get_cyl_r_component, get_cyl_theta_component, get_cyl_z_component, get_sph_phi_component, get_sph_r_component, get_sph_theta_component, ) from .derived_field import NeedsParameter, ValidateParameter, ValidateSpatial if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never if TYPE_CHECKING: # avoid circular imports from yt.fields.field_info_container import FieldInfoContainer def get_bulk(data, basename, unit): if data.has_field_parameter(f"bulk_{basename}"): bulk = data.get_field_parameter(f"bulk_{basename}") else: bulk = [0, 0, 0] * unit return bulk def create_magnitude_field( registry, basename, field_units, ftype="gas", slice_info=None, validators=None, sampling_type=None, ): axis_order = registry.ds.coordinates.axis_order field_components = [(ftype, f"{basename}_{ax}") for ax in axis_order] if sampling_type is None: sampling_type = "local" def _magnitude(field, data): fn = field_components[0] if data.has_field_parameter(f"bulk_{basename}"): fn = (fn[0], f"relative_{fn[1]}") d = data[fn] mag = (d) ** 2 for idim in range(1, registry.ds.dimensionality): fn = field_components[idim] if data.has_field_parameter(f"bulk_{basename}"): fn = (fn[0], f"relative_{fn[1]}") mag += (data[fn]) ** 2 return np.sqrt(mag) registry.add_field( (ftype, f"{basename}_magnitude"), sampling_type=sampling_type, function=_magnitude, units=field_units, validators=validators, ) def create_relative_field( registry, basename, field_units, ftype="gas", slice_info=None, validators=None ): axis_order = registry.ds.coordinates.axis_order field_components = [(ftype, f"{basename}_{ax}") for ax in axis_order] def relative_vector(ax): def _relative_vector(field, data): iax = axis_order.index(ax) d = data[field_components[iax]] bulk = get_bulk(data, basename, d.unit_quantity) return d - bulk[iax] return _relative_vector for d in axis_order: registry.add_field( (ftype, f"relative_{basename}_{d}"), sampling_type="local", function=relative_vector(d), units=field_units, validators=validators, ) def create_los_field( registry, basename, field_units, ftype="gas", slice_info=None, *, sampling_type="local", ): axis_order = registry.ds.coordinates.axis_order # Here we need to check if we are a particle field, so that we can # correctly identify the "bulk" field parameter corresponding to # this vector field. if sampling_type == "particle": basenm = basename.removeprefix("particle_") else: basenm = basename validators = [ ValidateParameter(f"bulk_{basenm}"), ValidateParameter("axis", {"axis": [0, 1, 2]}), ] field_comps = [(ftype, f"{basename}_{ax}") for ax in axis_order] def _los_field(field, data): if data.has_field_parameter(f"bulk_{basenm}"): fns = [(fc[0], f"relative_{fc[1]}") for fc in field_comps] else: fns = field_comps ax = data.get_field_parameter("axis") if is_sequence(ax): # Make sure this is a unit vector ax /= np.sqrt(np.dot(ax, ax)) ret = data[fns[0]] * ax[0] + data[fns[1]] * ax[1] + data[fns[2]] * ax[2] elif ax in [0, 1, 2]: ret = data[fns[ax]] else: raise NeedsParameter(["axis"]) return ret registry.add_field( (ftype, f"{basename}_los"), sampling_type=sampling_type, function=_los_field, units=field_units, validators=validators, display_name=rf"\mathrm{{Line of Sight {basename.capitalize()}}}", ) def create_squared_field( registry, basename, field_units, ftype="gas", slice_info=None, validators=None ): axis_order = registry.ds.coordinates.axis_order field_components = [(ftype, f"{basename}_{ax}") for ax in axis_order] def _squared(field, data): fn = field_components[0] if data.has_field_parameter(f"bulk_{basename}"): fn = (fn[0], f"relative_{fn[1]}") squared = data[fn] * data[fn] for idim in range(1, registry.ds.dimensionality): fn = field_components[idim] squared += data[fn] * data[fn] return squared registry.add_field( (ftype, f"{basename}_squared"), sampling_type="local", function=_squared, units=field_units, validators=validators, ) def create_vector_fields( registry: "FieldInfoContainer", basename: FieldName, field_units, ftype: FieldType = "gas", slice_info=None, ) -> None: # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info axis_order = registry.ds.coordinates.axis_order xn, yn, zn = ((ftype, f"{basename}_{ax}") for ax in axis_order) # Is this safe? if registry.ds.dimensionality < 3: zn = ("index", "zeros") if registry.ds.dimensionality < 2: yn = ("index", "zeros") create_relative_field( registry, basename, field_units, ftype=ftype, slice_info=slice_info, validators=[ValidateParameter(f"bulk_{basename}")], ) create_magnitude_field( registry, basename, field_units, ftype=ftype, slice_info=slice_info, validators=[ValidateParameter(f"bulk_{basename}")], ) axis_names = registry.ds.coordinates.axis_order geometry: Geometry = registry.ds.geometry if geometry is Geometry.CARTESIAN: # The following fields are invalid for curvilinear geometries def _spherical_radius_component(field, data): """The spherical radius component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector( data, (xn, yn, zn), f"bulk_{basename}" ) theta = data["index", "spherical_theta"] phi = data["index", "spherical_phi"] rv = get_sph_r_component(vectors, theta, phi, normal) # Now, anywhere that radius is in fact zero, we want to zero out our # return values. rv[np.isnan(theta)] = 0.0 return rv registry.add_field( (ftype, f"{basename}_spherical_radius"), sampling_type="local", function=_spherical_radius_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) create_los_field( registry, basename, field_units, ftype=ftype, slice_info=slice_info ) def _radial(field, data): return data[ftype, f"{basename}_spherical_radius"] def _radial_absolute(field, data): return np.abs(data[ftype, f"{basename}_spherical_radius"]) def _tangential(field, data): return np.sqrt( data[ftype, f"{basename}_spherical_theta"] ** 2.0 + data[ftype, f"{basename}_spherical_phi"] ** 2.0 ) registry.add_field( (ftype, f"radial_{basename}"), sampling_type="local", function=_radial, units=field_units, validators=[ValidateParameter("normal"), ValidateParameter("center")], ) registry.add_field( (ftype, f"radial_{basename}_absolute"), sampling_type="local", function=_radial_absolute, units=field_units, ) registry.add_field( (ftype, f"tangential_{basename}"), sampling_type="local", function=_tangential, units=field_units, ) def _spherical_theta_component(field, data): """The spherical theta component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector( data, (xn, yn, zn), f"bulk_{basename}" ) theta = data["index", "spherical_theta"] phi = data["index", "spherical_phi"] return get_sph_theta_component(vectors, theta, phi, normal) registry.add_field( (ftype, f"{basename}_spherical_theta"), sampling_type="local", function=_spherical_theta_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) def _spherical_phi_component(field, data): """The spherical phi component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector( data, (xn, yn, zn), f"bulk_{basename}" ) phi = data["index", "spherical_phi"] return get_sph_phi_component(vectors, phi, normal) registry.add_field( (ftype, f"{basename}_spherical_phi"), sampling_type="local", function=_spherical_phi_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) def _cp_vectors(ax): def _cp_val(field, data): vec = data.get_field_parameter(f"cp_{ax}_vec") tr = data[xn[0], f"relative_{xn[1]}"] * vec.d[0] tr += data[yn[0], f"relative_{yn[1]}"] * vec.d[1] tr += data[zn[0], f"relative_{zn[1]}"] * vec.d[2] return tr return _cp_val for ax in "xyz": registry.add_field( (ftype, f"cutting_plane_{basename}_{ax}"), sampling_type="local", function=_cp_vectors(ax), units=field_units, ) def _divergence(field, data): ds = div_fac * just_one(data["index", "dx"]) f = data[xn[0], f"relative_{xn[1]}"][sl_right, 1:-1, 1:-1] / ds f -= data[xn[0], f"relative_{xn[1]}"][sl_left, 1:-1, 1:-1] / ds ds = div_fac * just_one(data["index", "dy"]) f += data[yn[0], f"relative_{yn[1]}"][1:-1, sl_right, 1:-1] / ds f -= data[yn[0], f"relative_{yn[1]}"][1:-1, sl_left, 1:-1] / ds ds = div_fac * just_one(data["index", "dz"]) f += data[zn[0], f"relative_{zn[1]}"][1:-1, 1:-1, sl_right] / ds f -= data[zn[0], f"relative_{zn[1]}"][1:-1, 1:-1, sl_left] / ds new_field = data.ds.arr(np.zeros(data[xn].shape, dtype="f8"), str(f.units)) new_field[1:-1, 1:-1, 1:-1] = f return new_field def _divergence_abs(field, data): return np.abs(data[ftype, f"{basename}_divergence"]) field_units = Unit(field_units, registry=registry.ds.unit_registry) div_units = field_units / registry.ds.unit_system["length"] registry.add_field( (ftype, f"{basename}_divergence"), sampling_type="local", function=_divergence, units=div_units, validators=[ValidateSpatial(1), ValidateParameter(f"bulk_{basename}")], ) registry.add_field( (ftype, f"{basename}_divergence_absolute"), sampling_type="local", function=_divergence_abs, units=div_units, ) def _tangential_over_magnitude(field, data): tr = ( data[ftype, f"tangential_{basename}"] / data[ftype, f"{basename}_magnitude"] ) return np.abs(tr) registry.add_field( (ftype, f"tangential_over_{basename}_magnitude"), sampling_type="local", function=_tangential_over_magnitude, take_log=False, ) def _cylindrical_radius_component(field, data): """The cylindrical radius component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector( data, (xn, yn, zn), f"bulk_{basename}" ) theta = data["index", "cylindrical_theta"] return get_cyl_r_component(vectors, theta, normal) registry.add_field( (ftype, f"{basename}_cylindrical_radius"), sampling_type="local", function=_cylindrical_radius_component, units=field_units, validators=[ValidateParameter("normal")], ) def _cylindrical_theta_component(field, data): """The cylindrical theta component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector( data, (xn, yn, zn), f"bulk_{basename}" ) theta = data["index", "cylindrical_theta"].copy() theta = np.tile(theta, (3,) + (1,) * len(theta.shape)) return get_cyl_theta_component(vectors, theta, normal) registry.add_field( (ftype, f"{basename}_cylindrical_theta"), sampling_type="local", function=_cylindrical_theta_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) def _cylindrical_z_component(field, data): """The cylindrical z component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector( data, (xn, yn, zn), f"bulk_{basename}" ) return get_cyl_z_component(vectors, normal) registry.add_field( (ftype, f"{basename}_cylindrical_z"), sampling_type="local", function=_cylindrical_z_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) elif ( geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL ): # Create Cartesian fields for curvilinear coordinates if geometry is Geometry.POLAR: def _cartesian_x(field, data): return data[ftype, f"{basename}_r"] * np.cos(data[ftype, "theta"]) def _cartesian_y(field, data): return data[ftype, f"{basename}_r"] * np.sin(data[ftype, "theta"]) def _cartesian_z(field, data): return data[ftype, f"{basename}_z"] elif geometry is Geometry.CYLINDRICAL: def _cartesian_x(field, data): if data.ds.dimensionality == 2: return data[ftype, f"{basename}_r"] elif data.ds.dimensionality == 3: return data[ftype, f"{basename}_r"] * np.cos( data[ftype, "theta"] ) - data[ftype, f"{basename}_theta"] * np.sin(data[ftype, "theta"]) def _cartesian_y(field, data): if data.ds.dimensionality == 2: return data[ftype, f"{basename}_z"] elif data.ds.dimensionality == 3: return data[ftype, f"{basename}_r"] * np.sin( data[ftype, "theta"] ) + data[ftype, f"{basename}_theta"] * np.cos(data[ftype, "theta"]) def _cartesian_z(field, data): return data[ftype, f"{basename}_z"] elif geometry is Geometry.SPHERICAL: def _cartesian_x(field, data): if data.ds.dimensionality == 2: return data[ftype, f"{basename}_r"] * np.sin( data[ftype, "theta"] ) + data[ftype, f"{basename}_theta"] * np.cos(data[ftype, "theta"]) elif data.ds.dimensionality == 3: return ( data[ftype, f"{basename}_r"] * np.sin(data[ftype, "theta"]) * np.cos(data[ftype, "phi"]) + data[ftype, f"{basename}_theta"] * np.cos(data[ftype, "theta"]) * np.cos(data[ftype, "phi"]) - data[ftype, f"{basename}_phi"] * np.sin(data[ftype, "phi"]) ) def _cartesian_y(field, data): if data.ds.dimensionality == 2: return data[ftype, f"{basename}_r"] * np.cos( data[ftype, "theta"] ) - data[f"{basename}_theta"] * np.sin(data[ftype, "theta"]) elif data.ds.dimensionality == 3: return ( data[ftype, f"{basename}_r"] * np.sin(data[ftype, "theta"]) * np.sin(data[ftype, "phi"]) + data[ftype, f"{basename}_theta"] * np.cos(data[ftype, "theta"]) * np.sin(data[ftype, "phi"]) + data[ftype, f"{basename}_phi"] * np.cos(data[ftype, "phi"]) ) def _cartesian_z(field, data): return data[ftype, f"{basename}_r"] * np.cos( data[ftype, "theta"] ) - data[ftype, f"{basename}_theta"] * np.sin(data[ftype, "theta"]) else: assert_never(geometry) # it's redundant to define a cartesian x field for 1D data if registry.ds.dimensionality >= 2: registry.add_field( (ftype, f"{basename}_cartesian_x"), sampling_type="local", function=_cartesian_x, units=field_units, display_field=True, ) registry.add_field( (ftype, f"{basename}_cartesian_y"), sampling_type="local", function=_cartesian_y, units=field_units, display_field=True, ) registry.add_field( (ftype, f"{basename}_cartesian_z"), sampling_type="local", function=_cartesian_z, units=field_units, display_field=True, ) elif ( geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): # nothing to do pass else: assert_never(geometry) if registry.ds.geometry is Geometry.SPHERICAL: def _cylindrical_radius_component(field, data): return ( np.sin(data[ftype, "theta"]) * data[ftype, f"{basename}_r"] + np.cos(data[ftype, "theta"]) * data[ftype, f"{basename}_theta"] ) registry.add_field( (ftype, f"{basename}_cylindrical_radius"), sampling_type="local", function=_cylindrical_radius_component, units=field_units, display_field=True, ) registry.alias( (ftype, f"{basename}_cylindrical_z"), (ftype, f"{basename}_cartesian_z"), ) # define vector components appropriate for 'theta'-normal plots. # The projection plane is called 'conic plane' in the code base as well as docs. # Contrary to 'poloidal' and 'toroidal', this isn't a widely spread # naming convention, but here it is exposed to users as part of dedicated # field names, so it needs to be stable. def _conic_x(field, data): rax = axis_names.index("r") pax = axis_names.index("phi") bc = data.get_field_parameter(f"bulk_{basename}") return np.cos(data[ftype, "phi"]) * ( data[ftype, f"{basename}_r"] - bc[rax] ) - np.sin(data[ftype, "phi"]) * (data[ftype, f"{basename}_phi"] - bc[pax]) def _conic_y(field, data): rax = axis_names.index("r") pax = axis_names.index("phi") bc = data.get_field_parameter(f"bulk_{basename}") return np.sin(data[ftype, "phi"]) * ( data[ftype, f"{basename}_r"] - bc[rax] ) + np.cos(data[ftype, "phi"]) * (data[ftype, f"{basename}_phi"] - bc[pax]) if registry.ds.dimensionality == 3: registry.add_field( (ftype, f"{basename}_conic_x"), sampling_type="local", function=_conic_x, units=field_units, display_field=True, ) registry.add_field( (ftype, f"{basename}_conic_y"), sampling_type="local", function=_conic_y, units=field_units, display_field=True, ) def create_averaged_field( registry, basename, field_units, ftype="gas", slice_info=None, validators=None, weight="mass", ): if validators is None: validators = [] validators += [ValidateSpatial(1, [(ftype, basename)])] def _averaged_field(field, data): def atleast_4d(array): if array.ndim == 3: return array[..., None] else: return array nx, ny, nz, ngrids = atleast_4d(data[ftype, basename]).shape new_field = data.ds.arr( np.zeros((nx - 2, ny - 2, nz - 2, ngrids), dtype=np.float64), (just_one(data[ftype, basename]) * just_one(data[ftype, weight])).units, ) weight_field = data.ds.arr( np.zeros((nx - 2, ny - 2, nz - 2, ngrids), dtype=np.float64), data[ftype, weight].units, ) i_i, j_i, k_i = np.mgrid[0:3, 0:3, 0:3] for i, j, k in zip(i_i.ravel(), j_i.ravel(), k_i.ravel(), strict=True): sl = ( slice(i, nx - (2 - i)), slice(j, ny - (2 - j)), slice(k, nz - (2 - k)), ) new_field += ( atleast_4d(data[ftype, basename])[sl] * atleast_4d(data[ftype, weight])[sl] ) weight_field += atleast_4d(data[ftype, weight])[sl] # Now some fancy footwork new_field2 = data.ds.arr( np.zeros((nx, ny, nz, ngrids)), data[ftype, basename].units ) new_field2[1:-1, 1:-1, 1:-1] = new_field / weight_field if data[ftype, basename].ndim == 3: return new_field2[..., 0] else: return new_field2 registry.add_field( (ftype, f"averaged_{basename}"), sampling_type="cell", function=_averaged_field, units=field_units, validators=validators, ) yt-project-yt-f043ac8/yt/fields/xray_emission_fields.py000066400000000000000000000333251510711153200233530ustar00rootroot00000000000000import os import numpy as np from yt.config import ytcfg from yt.fields.derived_field import DerivedField from yt.funcs import mylog, only_on_root, parse_h5_attr from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.cosmology import Cosmology from yt.utilities.exceptions import YTException, YTFieldNotFound from yt.utilities.linear_interpolators import ( BilinearFieldInterpolator, UnilinearFieldInterpolator, ) from yt.utilities.on_demand_imports import _h5py as h5py data_version = {"cloudy": 2, "apec": 3} data_url = "http://yt-project.org/data" def _get_data_file(table_type, data_dir=None): data_file = "%s_emissivity_v%d.h5" % (table_type, data_version[table_type]) if data_dir is None: supp_data_dir = ytcfg.get("yt", "supp_data_dir") data_dir = supp_data_dir if os.path.exists(supp_data_dir) else "." data_path = os.path.join(data_dir, data_file) if not os.path.exists(data_path): msg = f"Failed to find emissivity data file {data_file}! Please download from {data_url}" mylog.error(msg) raise OSError(msg) return data_path class EnergyBoundsException(YTException): def __init__(self, lower, upper): self.lower = lower self.upper = upper def __str__(self): return f"Energy bounds are {self.lower:e} to {self.upper:e} keV." class ObsoleteDataException(YTException): def __init__(self, table_type): data_file = "%s_emissivity_v%d.h5" % (table_type, data_version[table_type]) self.msg = "X-ray emissivity data is out of date.\n" self.msg += f"Download the latest data from {data_url}/{data_file}." def __str__(self): return self.msg class XrayEmissivityIntegrator: r"""Class for making X-ray emissivity fields. Uses hdf5 data tables generated from Cloudy and AtomDB/APEC. Initialize an XrayEmissivityIntegrator object. Parameters ---------- table_type : string The type of data to use when computing the emissivity values. If "cloudy", a file called "cloudy_emissivity.h5" is used, for photoionized plasmas. If, "apec", a file called "apec_emissivity.h5" is used for collisionally ionized plasmas. These files contain emissivity tables for primordial elements and for metals at solar metallicity for the energy range 0.1 to 100 keV. redshift : float, optional The cosmological redshift of the source of the field. Default: 0.0. data_dir : string, optional The location to look for the data table in. If not supplied, the file will be looked for in the location of the YT_DEST environment variable or in the current working directory. use_metals : boolean, optional If set to True, the emissivity will include contributions from metals. Default: True """ def __init__(self, table_type, redshift=0.0, data_dir=None, use_metals=True): filename = _get_data_file(table_type, data_dir=data_dir) only_on_root(mylog.info, "Loading emissivity data from %s", filename) in_file = h5py.File(filename, mode="r") if "info" in in_file.attrs: only_on_root(mylog.info, parse_h5_attr(in_file, "info")) if parse_h5_attr(in_file, "version") != data_version[table_type]: raise ObsoleteDataException(table_type) else: only_on_root( mylog.info, f"X-ray '{table_type}' emissivity data version: " f"{parse_h5_attr(in_file, 'version')}.", ) self.log_T = in_file["log_T"][:] self.emissivity_primordial = in_file["emissivity_primordial"][:] if "log_nH" in in_file: self.log_nH = in_file["log_nH"][:] if use_metals: self.emissivity_metals = in_file["emissivity_metals"][:] self.ebin = YTArray(in_file["E"], "keV") in_file.close() self.dE = np.diff(self.ebin) self.emid = 0.5 * (self.ebin[1:] + self.ebin[:-1]).to("erg") self.redshift = redshift def get_interpolator(self, data_type, e_min, e_max, energy=True): data = getattr(self, f"emissivity_{data_type}") if not energy: data = data[..., :] / self.emid.v e_min = YTQuantity(e_min, "keV") * (1.0 + self.redshift) e_max = YTQuantity(e_max, "keV") * (1.0 + self.redshift) if (e_min - self.ebin[0]) / e_min < -1e-3 or ( e_max - self.ebin[-1] ) / e_max > 1e-3: raise EnergyBoundsException(self.ebin[0], self.ebin[-1]) e_is, e_ie = np.digitize([e_min, e_max], self.ebin) e_is = np.clip(e_is - 1, 0, self.ebin.size - 1) e_ie = np.clip(e_ie, 0, self.ebin.size - 1) my_dE = self.dE[e_is:e_ie].copy() # clip edge bins if the requested range is smaller my_dE[0] -= e_min - self.ebin[e_is] my_dE[-1] -= self.ebin[e_ie] - e_max interp_data = (data[..., e_is:e_ie] * my_dE).sum(axis=-1) if data.ndim == 2: emiss = UnilinearFieldInterpolator( np.log10(interp_data), [self.log_T[0], self.log_T[-1]], "log_T", truncate=True, ) else: emiss = BilinearFieldInterpolator( np.log10(interp_data), [self.log_nH[0], self.log_nH[-1], self.log_T[0], self.log_T[-1]], ["log_nH", "log_T"], truncate=True, ) return emiss def add_xray_emissivity_field( ds, e_min, e_max, redshift=0.0, metallicity=("gas", "metallicity"), table_type="cloudy", data_dir=None, cosmology=None, dist=None, ftype="gas", ): r"""Create X-ray emissivity fields for a given energy range. Parameters ---------- e_min : float The minimum energy in keV for the energy band. e_min : float The maximum energy in keV for the energy band. redshift : float, optional The cosmological redshift of the source of the field. Default: 0.0. metallicity : str or tuple of str or float, optional Either the name of a metallicity field or a single floating-point number specifying a spatially constant metallicity. Must be in solar units. If set to None, no metals will be assumed. Default: ("gas", "metallicity") table_type : string, optional The type of emissivity table to be used when creating the fields. Options are "cloudy" or "apec". Default: "cloudy" data_dir : string, optional The location to look for the data table in. If not supplied, the file will be looked for in the location of the YT_DEST environment variable or in the current working directory. cosmology : :class:`~yt.utilities.cosmology.Cosmology`, optional If set and redshift > 0.0, this cosmology will be used when computing the cosmological dependence of the emission fields. If not set, yt's default LCDM cosmology will be used. dist : (value, unit) tuple or :class:`~yt.units.yt_array.YTQuantity`, optional The distance to the source, used for making intensity fields. You should only use this if your source is nearby (not cosmological). Default: None ftype : string, optional The field type to use when creating the fields, default "gas" This will create at least three fields: "xray_emissivity_{e_min}_{e_max}_keV" (erg s^-1 cm^-3) "xray_luminosity_{e_min}_{e_max}_keV" (erg s^-1) "xray_photon_emissivity_{e_min}_{e_max}_keV" (photons s^-1 cm^-3) and if a redshift or distance is specified it will create two others: "xray_intensity_{e_min}_{e_max}_keV" (erg s^-1 cm^-3 arcsec^-2) "xray_photon_intensity_{e_min}_{e_max}_keV" (photons s^-1 cm^-3 arcsec^-2) These latter two are really only useful when making projections. Examples -------- >>> import yt >>> ds = yt.load("sloshing_nomag2_hdf5_plt_cnt_0100") >>> yt.add_xray_emissivity_field(ds, 0.5, 2) >>> p = yt.ProjectionPlot( ... ds, "x", ("gas", "xray_emissivity_0.5_2_keV"), table_type="apec" ... ) >>> p.save() """ if not isinstance(metallicity, float) and metallicity is not None: try: metallicity = ds._get_field_info(metallicity) except YTFieldNotFound as e: raise RuntimeError( f"Your dataset does not have a {metallicity} field! " + "Perhaps you should specify a constant metallicity instead?" ) from e if table_type == "cloudy": # Cloudy wants to scale by nH**2 other_n = "H_nuclei_density" else: # APEC wants to scale by nH*ne other_n = "El_number_density" def _norm_field(field, data): return data[ftype, "H_nuclei_density"] * data[ftype, other_n] ds.add_field( (ftype, "norm_field"), _norm_field, units="cm**-6", sampling_type="local" ) my_si = XrayEmissivityIntegrator(table_type, data_dir=data_dir, redshift=redshift) em_0 = my_si.get_interpolator("primordial", e_min, e_max) emp_0 = my_si.get_interpolator("primordial", e_min, e_max, energy=False) if metallicity is not None: em_Z = my_si.get_interpolator("metals", e_min, e_max) emp_Z = my_si.get_interpolator("metals", e_min, e_max, energy=False) def _emissivity_field(field, data): with np.errstate(all="ignore"): dd = { "log_nH": np.log10(data[ftype, "H_nuclei_density"]), "log_T": np.log10(data[ftype, "temperature"]), } my_emissivity = np.power(10, em_0(dd)) if metallicity is not None: if isinstance(metallicity, DerivedField): my_Z = data[metallicity.name].to_value("Zsun") else: my_Z = metallicity my_emissivity += my_Z * np.power(10, em_Z(dd)) my_emissivity[np.isnan(my_emissivity)] = 0 return data[ftype, "norm_field"] * YTArray(my_emissivity, "erg*cm**3/s") emiss_name = (ftype, f"xray_emissivity_{e_min}_{e_max}_keV") ds.add_field( emiss_name, function=_emissivity_field, display_name=rf"\epsilon_{{X}} ({e_min}-{e_max} keV)", sampling_type="local", units="erg/cm**3/s", ) def _luminosity_field(field, data): return data[emiss_name] * data[ftype, "mass"] / data[ftype, "density"] lum_name = (ftype, f"xray_luminosity_{e_min}_{e_max}_keV") ds.add_field( lum_name, function=_luminosity_field, display_name=rf"\rm{{L}}_{{X}} ({e_min}-{e_max} keV)", sampling_type="local", units="erg/s", ) def _photon_emissivity_field(field, data): dd = { "log_nH": np.log10(data[ftype, "H_nuclei_density"]), "log_T": np.log10(data[ftype, "temperature"]), } my_emissivity = np.power(10, emp_0(dd)) if metallicity is not None: if isinstance(metallicity, DerivedField): my_Z = data[metallicity.name].to_value("Zsun") else: my_Z = metallicity my_emissivity += my_Z * np.power(10, emp_Z(dd)) return data[ftype, "norm_field"] * YTArray(my_emissivity, "photons*cm**3/s") phot_name = (ftype, f"xray_photon_emissivity_{e_min}_{e_max}_keV") ds.add_field( phot_name, function=_photon_emissivity_field, display_name=rf"\epsilon_{{X}} ({e_min}-{e_max} keV)", sampling_type="local", units="photons/cm**3/s", ) fields = [emiss_name, lum_name, phot_name] if redshift > 0.0 or dist is not None: if dist is None: if cosmology is None: if hasattr(ds, "cosmology"): cosmology = ds.cosmology else: cosmology = Cosmology() D_L = cosmology.luminosity_distance(0.0, redshift) angular_scale = 1.0 / cosmology.angular_scale(0.0, redshift) dist_fac = ds.quan( 1.0 / (4.0 * np.pi * D_L * D_L * angular_scale * angular_scale).v, "rad**-2", ) else: redshift = 0.0 # Only for local sources! try: # normal behaviour, if dist is a YTQuantity dist = ds.quan(dist.value, dist.units) except AttributeError as e: try: dist = ds.quan(*dist) except (RuntimeError, TypeError): raise TypeError( "dist should be a YTQuantity or a (value, unit) tuple!" ) from e angular_scale = dist / ds.quan(1.0, "radian") dist_fac = ds.quan( 1.0 / (4.0 * np.pi * dist * dist * angular_scale * angular_scale).v, "rad**-2", ) ei_name = (ftype, f"xray_intensity_{e_min}_{e_max}_keV") def _intensity_field(field, data): I = dist_fac * data[emiss_name] return I.in_units("erg/cm**3/s/arcsec**2") ds.add_field( ei_name, function=_intensity_field, display_name=rf"I_{{X}} ({e_min}-{e_max} keV)", sampling_type="local", units="erg/cm**3/s/arcsec**2", ) i_name = (ftype, f"xray_photon_intensity_{e_min}_{e_max}_keV") def _photon_intensity_field(field, data): I = (1.0 + redshift) * dist_fac * data[phot_name] return I.in_units("photons/cm**3/s/arcsec**2") ds.add_field( i_name, function=_photon_intensity_field, display_name=rf"I_{{X}} ({e_min}-{e_max} keV)", sampling_type="local", units="photons/cm**3/s/arcsec**2", ) fields += [ei_name, i_name] for field in fields: mylog.info("Adding ('%s','%s') field.", field[0], field[1]) return fields yt-project-yt-f043ac8/yt/frontends/000077500000000000000000000000001510711153200173105ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/__init__.py000066400000000000000000000023471510711153200214270ustar00rootroot00000000000000__all__ = [ "adaptahop", "ahf", "amrex", "amrvac", "art", "artio", "athena", "athena_pp", "boxlib", # the boxlib frontend is deprecated, use 'amrex' "cf_radial", "chimera", "chombo", "cholla", "enzo_e", "enzo", "exodus_ii", "fits", "flash", "gadget", # breaking alphabetical order intentionnally here: # arepo and eagle depend on gadget. Importing them first causes # unintended side effects. See # https://github.com/yt-project/yt/issues/4563 "arepo", "eagle", "gadget_fof", "gamer", "gdf", "gizmo", "halo_catalog", "http_stream", "moab", "nc4_cm1", "open_pmd", "owls", "owls_subfind", "parthenon", "ramses", "rockstar", "sdf", "stream", "swift", "tipsy", "ytdata", ] from functools import lru_cache @lru_cache(maxsize=None) def __getattr__(value): import importlib if value == "_all": # recursively import all frontends for _ in __all__: __getattr__(_) return if value not in __all__: raise AttributeError(f"yt.frontends has no attribute {value!r}") return importlib.import_module(f"yt.frontends.{value}.api") yt-project-yt-f043ac8/yt/frontends/_skeleton/000077500000000000000000000000001510711153200212735ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/_skeleton/__init__.py000066400000000000000000000000001510711153200233720ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/_skeleton/api.py000066400000000000000000000002261510711153200224160ustar00rootroot00000000000000from .data_structures import SkeletonDataset, SkeletonGrid, SkeletonHierarchy from .fields import SkeletonFieldInfo from .io import SkeletonIOHandler yt-project-yt-f043ac8/yt/frontends/_skeleton/data_structures.py000066400000000000000000000153711510711153200250700ustar00rootroot00000000000000import os import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.funcs import setdefaultattr from yt.geometry.grid_geometry_handler import GridIndex from .fields import SkeletonFieldInfo class SkeletonGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level): super().__init__(id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level class SkeletonHierarchy(GridIndex): grid = SkeletonGrid def __init__(self, ds, dataset_type="skeleton"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) # for now, the index file is the dataset! self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) # float type for the simulation edges and must be float64 now self.float_type = np.float64 super().__init__(ds, dataset_type) def _detect_output_fields(self): # This needs to set a self.field_list that contains all the available, # on-disk fields. No derived fields should be defined here. # NOTE: Each should be a tuple, where the first element is the on-disk # fluid type or particle type. Convention suggests that the on-disk # fluid type is usually the dataset_type and the on-disk particle type # (for a single population of particles) is "io". pass def _count_grids(self): # This needs to set self.num_grids (int) pass def _parse_index(self): # This needs to fill the following arrays, where N is self.num_grids: # self.grid_left_edge (N, 3) <= float64 # self.grid_right_edge (N, 3) <= float64 # self.grid_dimensions (N, 3) <= int # self.grid_particle_count (N, 1) <= int # self.grid_levels (N, 1) <= int # self.grids (N, 1) <= grid objects # self.max_level = self.grid_levels.max() pass def _populate_grid_objects(self): # the minimal form of this method is # # for g in self.grids: # g._prepare_grid() # g._setup_dx() # # This must also set: # g.Children <= list of child grids # g.Parent <= parent grid # This is handled by the frontend because often the children must be identified. pass class SkeletonDataset(Dataset): _index_class = SkeletonHierarchy _field_info_class = SkeletonFieldInfo # names of additional modules that may be required to load data from disk _load_requirements: list[str] = [] def __init__( self, filename, dataset_type="skeleton", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): self.fluid_types += ("skeleton",) super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename # refinement factor between a grid and its subgrid # self.refine_by = 2 def _set_code_unit_attributes(self): # This is where quantities are created that represent the various # on-disk units. These are the currently available quantities which # should be set, along with examples of how to set them to standard # values. # # self.length_unit = self.quan(1.0, "cm") # self.mass_unit = self.quan(1.0, "g") # self.time_unit = self.quan(1.0, "s") # self.time_unit = self.quan(1.0, "s") # # These can also be set: # self.velocity_unit = self.quan(1.0, "cm/s") # self.magnetic_unit = self.quan(1.0, "gauss") # # If your frontend uses SI EM units, set magnetic units like this # instead: # self.magnetic_unit = self.quan(1.0, "T") # this minimalistic implementation fills the requirements for # this frontend to run, change it to make it run _correctly_ ! for key, unit in self.__class__.default_units.items(): setdefaultattr(self, key, self.quan(1, unit)) def _parse_parameter_file(self): # This needs to set up the following items. Note that these are all # assumed to be in code units; domain_left_edge and domain_right_edge # will be converted to YTArray automatically at a later time. # This includes the cosmological parameters. # # self.unique_identifier <= unique identifier for the dataset # being read (e.g., UUID or ST_CTIME) # self.parameters <= dict full of code-specific items of use # self.domain_left_edge <= three-element array of float64 # self.domain_right_edge <= three-element array of float64 # self.dimensionality <= int # self.domain_dimensions <= three-element array of int64 # self.periodicity <= three-element tuple of booleans # self.current_time <= simulation time in code units (float) # # We also set up cosmological information. Set these to zero if # non-cosmological. # # self.cosmological_simulation <= int, 0 or 1 # self.current_redshift <= float # self.omega_lambda <= float # self.omega_matter <= float # self.hubble_constant <= float # optional (the following have default implementations) # # self.geometry: any yt.geometry.api.Geometry enum member # defaults to Geometry.CARTESIAN # this attribute is required. # Change this value to a constant 0 if time is not relevant to your dataset. # Otherwise, parse its value in any appropriate fashion. self.current_time = -1 # required. Change this if need be. self.cosmological_simulation = 0 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # This accepts a filename or a set of arguments and returns True or # False depending on if the file is of the type requested. # # The functionality in this method should be unique enough that it can # differentiate the frontend from others. Sometimes this means looking # for specific fields or attributes in the dataset in addition to # looking at the file name or extension. return False yt-project-yt-f043ac8/yt/frontends/_skeleton/definitions.py000066400000000000000000000001141510711153200241540ustar00rootroot00000000000000# This file is often empty. It can hold definitions related to a frontend. yt-project-yt-f043ac8/yt/frontends/_skeleton/fields.py000066400000000000000000000022121510711153200231100ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer # We need to specify which fields we might have in our dataset. The field info # container subclass here will define which fields it knows about. There are # optionally methods on it that get called which can be subclassed. class SkeletonFieldInfo(FieldInfoContainer): known_other_fields = ( # Each entry here is of the form # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ) known_particle_fields = ( # Identical form to above # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ) def __init__(self, ds, field_list): super().__init__(ds, field_list) # If you want, you can check self.field_list def setup_fluid_fields(self): # Here we do anything that might need info about the dataset. # You can use self.alias, self.add_output_field (for on-disk fields) # and self.add_field (for derived fields). pass def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) # This will get called for every particle type. yt-project-yt-f043ac8/yt/frontends/_skeleton/io.py000066400000000000000000000044541510711153200222630ustar00rootroot00000000000000from yt.utilities.io_handler import BaseIOHandler class SkeletonIOHandler(BaseIOHandler): _particle_reader = False _dataset_type = "skeleton" def _read_particle_coords(self, chunks, ptf): # This needs to *yield* a series of tuples of (ptype, (x, y, z)). # chunks is a list of chunks, and ptf is a dict where the keys are # ptypes and the values are lists of fields. pass def _read_particle_fields(self, chunks, ptf, selector): # This gets called after the arrays have been allocated. It needs to # yield ((ptype, field), data) where data is the masked results of # reading ptype, field and applying the selector to the data read in. # Selector objects have a .select_points(x,y,z) that returns a mask, so # you need to do your masking here. pass def _read_fluid_selection(self, chunks, selector, fields, size): # This needs to allocate a set of arrays inside a dictionary, where the # keys are the (ftype, fname) tuples and the values are arrays that # have been masked using whatever selector method is appropriate. The # dict gets returned at the end and it should be flat, with selected # data. Note that if you're reading grid data, you might need to # special-case a grid selector object. # Also note that "chunks" is a generator for multiple chunks, each of # which contains a list of grids. The returned numpy arrays should be # in 64-bit float and contiguous along the z direction. Therefore, for # a C-like input array with the dimension [x][y][z] or a # Fortran-like input array with the dimension (z,y,x), a matrix # transpose is required (e.g., using np_array.transpose() or # np_array.swapaxes(0,2)). # This method is not abstract, and has a default implementation # in the base class.However, the default implementation requires that the method # io_iter be defined pass def _read_chunk_data(self, chunk, fields): # This reads the data from a single chunk without doing any selection, # and is only used for caching data that might be used by multiple # different selectors later. For instance, this can speed up ghost zone # computation. pass yt-project-yt-f043ac8/yt/frontends/_skeleton/misc.py000066400000000000000000000000001510711153200225660ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/adaptahop/000077500000000000000000000000001510711153200212515ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/adaptahop/__init__.py000066400000000000000000000000501510711153200233550ustar00rootroot00000000000000""" API for AdaptaHOP frontend. """ yt-project-yt-f043ac8/yt/frontends/adaptahop/api.py000066400000000000000000000002721510711153200223750ustar00rootroot00000000000000""" API for AdaptaHOP frontend """ from . import tests from .data_structures import AdaptaHOPDataset from .fields import AdaptaHOPFieldInfo from .io import IOHandlerAdaptaHOPBinary yt-project-yt-f043ac8/yt/frontends/adaptahop/data_structures.py000066400000000000000000000277011510711153200250460ustar00rootroot00000000000000""" Data structures for AdaptaHOP frontend. """ import os import re from itertools import product import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.data_objects.static_output import Dataset from yt.frontends.halo_catalog.data_structures import HaloCatalogFile from yt.funcs import mylog, setdefaultattr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.units import Mpc # type: ignore from yt.utilities.cython_fortran_utils import FortranFile from .definitions import ADAPTAHOP_TEMPLATES, ATTR_T, HEADER_ATTRIBUTES from .fields import AdaptaHOPFieldInfo class AdaptaHOPParticleIndex(ParticleIndex): def _setup_filenames(self): template = self.dataset.filename_template ndoms = self.dataset.file_count cls = self.dataset._file_class if ndoms > 1: self.data_files = [ cls(self.dataset, self.io, template % {"num": i}, i, None) for i in range(ndoms) ] else: self.data_files = [ cls( self.dataset, self.io, self.dataset.parameter_filename, 0, None, ) ] class AdaptaHOPDataset(Dataset): _index_class = AdaptaHOPParticleIndex _file_class = HaloCatalogFile _field_info_class = AdaptaHOPFieldInfo # AdaptaHOP internally assumes 1Mpc == 3.0824cm _code_length_to_Mpc = (1.0 * Mpc).to_value("cm") / 3.08e24 _header_attributes: ATTR_T | None = None _halo_attributes: ATTR_T | None = None def __init__( self, filename, dataset_type="adaptahop_binary", n_ref=16, num_zones=2, units_override=None, unit_system="cgs", parent_ds=None, ): self.n_ref = n_ref self.num_zones = num_zones if parent_ds is None: raise RuntimeError( "The AdaptaHOP frontend requires a parent dataset " "to be passed as `parent_ds`." ) self.parent_ds = parent_ds self._guess_headers_from_file(filename) super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, ) def _set_code_unit_attributes(self): setdefaultattr(self, "length_unit", self.quan(self._code_length_to_Mpc, "Mpc")) setdefaultattr(self, "mass_unit", self.quan(1e11, "Msun")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "km / s")) setdefaultattr(self, "time_unit", self.length_unit / self.velocity_unit) def _guess_headers_from_file(self, filename) -> None: with FortranFile(filename) as fpu: ok = False for dp, longint in product((True, False), (True, False)): fpu.seek(0) try: header_attributes = HEADER_ATTRIBUTES(double=dp, longint=longint) fpu.read_attrs(header_attributes) ok = True break except (ValueError, OSError): pass if not ok: raise OSError(f"Could not read headers from file {filename}") istart = fpu.tell() fpu.seek(0, 2) iend = fpu.tell() # Try different templates ok = False for name, cls in ADAPTAHOP_TEMPLATES.items(): fpu.seek(istart) attributes = cls(longint, dp).HALO_ATTRIBUTES mylog.debug("Trying %s(longint=%s, dp=%s)", name, longint, dp) try: # Try to read two halos to be sure fpu.read_attrs(attributes) if fpu.tell() < iend: fpu.read_attrs(attributes) ok = True break except (ValueError, OSError): continue if not ok: raise OSError(f"Could not guess fields from file {filename}") self._header_attributes = header_attributes self._halo_attributes = attributes def _parse_parameter_file(self): with FortranFile(self.parameter_filename) as fpu: params = fpu.read_attrs(self._header_attributes) self.dimensionality = 3 # Domain related things self.filename_template = self.parameter_filename self.file_count = 1 nz = self.num_zones self.domain_dimensions = np.ones(3, "int32") * nz # Set things up self.cosmological_simulation = 1 self.current_redshift = (1.0 / params["aexp"]) - 1.0 self.omega_matter = params["omega_t"] self.current_time = self.quan(params["age"], "Gyr") self.omega_lambda = 0.724 # hard coded if not inferred from parent ds self.hubble_constant = 0.7 # hard coded if not inferred from parent ds self._periodicity = (True, True, True) self.particle_types = "halos" self.particle_types_raw = "halos" # Inherit stuff from parent ds -- if they exist for k in ("omega_lambda", "hubble_constant", "omega_matter", "omega_radiation"): v = getattr(self.parent_ds, k, None) if v is not None: setattr(self, k, v) self.domain_left_edge = np.array([0.0, 0.0, 0.0]) self.domain_right_edge = ( self.parent_ds.domain_right_edge.to_value("Mpc") * self._code_length_to_Mpc ) self.parameters.update(params) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: fname = os.path.split(filename)[1] if not fname.startswith("tree_bricks") or not re.match( r"^tree_bricks\d{3}$", fname ): return False return True def halo(self, halo_id, ptype="DM"): """ Create a data container to get member particles and individual values from halos. Halo mass, position, and velocity are set as attributes. Halo IDs are accessible through the field, "member_ids". Other fields that are one value per halo are accessible as normal. The field list for halo objects can be seen in `ds.halos_field_list`. Parameters ---------- halo_id : int The id of the halo or group ptype : str, default DM The type of particles the halo is made of. """ return AdaptaHOPHaloContainer( ptype, halo_id, parent_ds=self.parent_ds, halo_ds=self ) class AdaptaHOPHaloContainer(YTSelectionContainer): """ Create a data container to get member particles and individual values from halos. Halo mass, position, and velocity are set as attributes. Halo IDs are accessible through the field, "member_ids". Other fields that are one value per halo are accessible as normal. The field list for halo objects can be seen in `ds.halos_field_list`. Parameters ---------- ptype : string The type of halo, either "Group" for the main halo or "Subhalo" for subhalos. particle_identifier : int or tuple of ints The halo or subhalo id. If requesting a subhalo, the id can also be given as a tuple of the main halo id and subgroup id, such as (1, 4) for subgroup 4 of halo 1. Attributes ---------- particle_identifier : int The id of the halo or subhalo. group_identifier : int For subhalos, the id of the enclosing halo. subgroup_identifier : int For subhalos, the relative id of the subhalo within the enclosing halo. particle_number : int Number of particles in the halo. mass : float Halo mass. position : array of floats Halo position. velocity : array of floats Halo velocity. Note ---- Relevant Fields: * particle_number - number of particles * subhalo_number - number of subhalos * group_identifier - id of parent group for subhalos Examples -------- >>> import yt >>> ds = yt.load( ... "output_00080_halos/tree_bricks080", ... parent_ds=yt.load("output_00080/info_00080.txt"), ... ) >>> ds.halo(1, ptype="io") >>> print(halo.mass) 119.22804260253906 100000000000.0*Msun >>> print(halo.position) [26.80901299 24.35978484 5.45388672] code_length >>> print(halo.velocity) [3306394.95849609 8584366.60766602 9982682.80029297] cm/s >>> print(halo["io", "particle_mass"]) [3.19273578e-06 3.19273578e-06 ... 3.19273578e-06 3.19273578e-06] code_mass >>> # particle ids for this halo >>> print(halo.member_ids) [ 48 64 176 ... 999947 1005471 1006779] """ _type_name = "halo" _con_args = ("ptype", "particle_identifier", "parent_ds", "halo_ds") _spatial = False # Do not register it to prevent .halo from being attached to all datasets _skip_add = True def __init__(self, ptype, particle_identifier, parent_ds, halo_ds): if ptype not in parent_ds.particle_types_raw: raise RuntimeError( f'Possible halo types are {parent_ds.particle_types_raw}, supplied "{ptype}".' ) # Setup required fields self._dimensionality = 3 self.ds = parent_ds # Halo-specific self.halo_ds = halo_ds self.ptype = ptype self.particle_identifier = particle_identifier # Set halo particles data self._set_halo_properties() self._set_halo_member_data() # Call constructor super().__init__(parent_ds, {}) def __repr__(self): return "%s_%s_%09d" % (self.ds, self.ptype, self.particle_identifier) def __getitem__(self, key): return self.region[key] @property def ihalo(self): """The index in the catalog of the halo""" ihalo = getattr(self, "_ihalo", None) if ihalo: return ihalo else: halo_id = self.particle_identifier halo_ids = self.halo_ds.r["halos", "particle_identifier"].astype("int64") ihalo = np.searchsorted(halo_ids, halo_id) assert halo_ids[ihalo] == halo_id self._ihalo = ihalo return self._ihalo def _set_halo_member_data(self): ptype = self.ptype halo_ds = self.halo_ds parent_ds = self.ds ihalo = self.ihalo # Note: convert to physical units to prevent errors when jumping # from halo_ds to parent_ds halo_pos = halo_ds.r["halos", "particle_position"][ihalo, :].to_value("Mpc") halo_vel = halo_ds.r["halos", "particle_velocity"][ihalo, :].to_value("km/s") halo_radius = halo_ds.r["halos", "r"][ihalo].to_value("Mpc") members = self.member_ids ok = False f = 1 / 1.1 center = parent_ds.arr(halo_pos, "Mpc") radius = parent_ds.arr(halo_radius, "Mpc") # Find smallest sphere containing all particles while not ok: f *= 1.1 sph = parent_ds.sphere(center, f * radius) part_ids = sph[ptype, "particle_identity"].astype("int64") ok = len(np.lib.arraysetops.setdiff1d(members, part_ids)) == 0 # Set bulk velocity sph.set_field_parameter("bulk_velocity", (halo_vel, "km/s")) # Build subregion that only contains halo particles reg = sph.cut_region( ['np.isin(obj["io", "particle_identity"].astype("int64"), members)'], locals={"members": members, "np": np}, ) self.sphere = sph self.region = reg def _set_halo_properties(self): ihalo = self.ihalo ds = self.halo_ds # Add position, mass, velocity member functions for attr_name in ("mass", "position", "velocity"): setattr(self, attr_name, ds.r["halos", f"particle_{attr_name}"][ihalo]) # Add members self.member_ids = self.halo_ds.index.io.members(ihalo).astype(np.int64) yt-project-yt-f043ac8/yt/frontends/adaptahop/definitions.py000066400000000000000000000123471510711153200241450ustar00rootroot00000000000000""" Data structures for AdaptaHOP """ from yt.funcs import mylog ATTR_T = tuple[tuple[tuple[str, ...] | str, int, str], ...] def HEADER_ATTRIBUTES(*, double: bool, longint: bool) -> ATTR_T: int_type = "l" if longint else "i" float_type = "d" if double else "f" return ( ("npart", 1, int_type), ("massp", 1, float_type), ("aexp", 1, float_type), ("omega_t", 1, float_type), ("age", 1, float_type), (("nhalos", "nsubs"), 2, "i"), ) ADAPTAHOP_TEMPLATES = {} class AdaptaHOPDefTemplate: # this is a mixin class def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) mylog.debug("Registering AdaptaHOP template class %s", cls.__name__) ADAPTAHOP_TEMPLATES[cls.__name__] = cls def __init__(self, longint, double_precision): self.longint = longint self.double_precision = double_precision class AdaptaHOPOld(AdaptaHOPDefTemplate): @property def HALO_ATTRIBUTES(self) -> ATTR_T: int_type = "l" if self.longint else "i" float_type = "d" if self.double_precision else "f" return ( ("npart", 1, int_type), ("particle_identities", -1, int_type), ("particle_identifier", 1, "i"), # this is the halo id, always an int32 ("timestep", 1, "i"), ( ( "level", "host_id", "first_subhalo_id", "n_subhalos", "next_subhalo_id", ), 5, "i", ), ("particle_mass", 1, float_type), (("raw_position_x", "raw_position_y", "raw_position_z"), 3, float_type), ( ("particle_velocity_x", "particle_velocity_y", "particle_velocity_z"), 3, float_type, ), ( ( "particle_angular_momentum_x", "particle_angular_momentum_y", "particle_angular_momentum_z", ), 3, float_type, ), (("r", "a", "b", "c"), 4, float_type), (("ek", "ep", "etot"), 3, float_type), ("spin", 1, float_type), ( ( "virial_radius", "virial_mass", "virial_temperature", "virial_velocity", ), 4, float_type, ), (("rho0", "R_c"), 2, float_type), ) class AdaptaHOPNewNoContam(AdaptaHOPDefTemplate): @property def HALO_ATTRIBUTES(self) -> ATTR_T: int_type = "l" if self.longint else "i" float_type = "d" if self.double_precision else "f" return ( ("npart", 1, int_type), ("particle_identities", -1, int_type), ("particle_identifier", 1, "i"), # this is the halo id, always an int32 ("timestep", 1, "i"), ( ( "level", "host_id", "first_subhalo_id", "n_subhalos", "next_subhalo_id", ), 5, "i", ), ("particle_mass", 1, float_type), ("npart_tot", 1, int_type), ("particle_mass_tot", 1, float_type), (("raw_position_x", "raw_position_y", "raw_position_z"), 3, float_type), ( ("particle_velocity_x", "particle_velocity_y", "particle_velocity_z"), 3, float_type, ), ( ( "particle_angular_momentum_x", "particle_angular_momentum_y", "particle_angular_momentum_z", ), 3, float_type, ), (("r", "a", "b", "c"), 4, float_type), (("ek", "ep", "etot"), 3, float_type), ("spin", 1, float_type), ("velocity_dispersion", 1, float_type), ( ( "virial_radius", "virial_mass", "virial_temperature", "virial_velocity", ), 4, float_type, ), (("rmax", "vmax"), 2, float_type), ("concentration", 1, float_type), (("radius_200", "mass_200"), 2, float_type), (("radius_50", "mass_50"), 2, float_type), ("radius_profile", -1, float_type), ("rho_profile", -1, float_type), (("rho0", "R_c"), 2, float_type), ) class AdaptaHOPNewContam(AdaptaHOPNewNoContam): @property def HALO_ATTRIBUTES(self) -> ATTR_T: attrs = list(super().HALO_ATTRIBUTES) int_type = "l" if self.longint else "i" float_type = "d" if self.double_precision else "f" return tuple( attrs + [ ("contaminated", 1, "i"), (("m_contam", "mtot_contam"), 2, float_type), (("n_contam", "ntot_contam"), 2, int_type), ] ) yt-project-yt-f043ac8/yt/frontends/adaptahop/fields.py000066400000000000000000000057021510711153200230750ustar00rootroot00000000000000""" AdaptaHOP-specific fields """ from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer m_units = "1e11 * Msun" r_units = "Mpc" v_units = "km / s" l_units = "1e11 * Msun * Mpc * km / s" e_units = "1e11 * Msun * km**2 / s**2" dens_units = "1e11 * Msun / Mpc**3" class AdaptaHOPFieldInfo(FieldInfoContainer): known_particle_fields: KnownFieldsT = ( ("particle_identifier", ("", [], "Halo Identity")), ("raw_position_x", (r_units, [], None)), ("raw_position_y", (r_units, [], None)), ("raw_position_z", (r_units, [], None)), ("r", (r_units, [], "Halo radius")), ("a", (r_units, [], "Halo semi-major axis")), ("b", (r_units, [], "Halo semi-medium axis")), ("c", (r_units, [], "Halo semi-minor axis")), ("particle_velocity_x", (v_units, [], "Halo velocity x")), ("particle_velocity_y", (v_units, [], "Halo velocity y")), ("particle_velocity_z", (v_units, [], "Halo velocity z")), ("particle_angular_momentum_x", (l_units, [], "Halo Angular Momentum x")), ("particle_angular_momentum_y", (l_units, [], "Halo Angular Momentum y")), ("particle_angular_momentum_z", (l_units, [], "Halo Angular Momentum z")), ("particle_mass", (m_units, [], "Halo mass")), ("ek", (e_units, [], "Halo Kinetic Energy")), ("ep", (e_units, [], "Halo Gravitational Energy")), ("ek", (e_units, [], "Halo Total Energy")), ("spin", ("", [], "Halo Spin Parameter")), # Virial parameters ("virial_radius", (r_units, [], "Halo Virial Radius")), ("virial_mass", (m_units, [], "Halo Virial Mass")), ("virial_temperature", ("K", [], "Halo Virial Temperature")), ("virial_velocity", (v_units, [], "Halo Virial Velocity")), # NFW parameters ("rho0", (dens_units, [], "Halo NFW Density")), ("R_c", (r_units, [], "Halo NFW Scale Radius")), ("velocity_dispersion", ("km/s", [], "Velocity Dispersion")), ("radius_200", (r_units, [], r"$R_\mathrm{200}$")), ("radius_50", (r_units, [], r"$R_\mathrm{50}$")), ("mass_200", (m_units, [], r"$M_\mathrm{200}$")), ("mass_50", (m_units, [], r"$M_\mathrm{50}$")), # Contamination ("contaminated", ("", [], "Contaminated")), ("m_contam", (m_units, [], "Contaminated Mass")), ) def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) # Add particle position def generate_pos_field(d): shift = self.ds.domain_width[0] / 2 def closure(field, data): return data["halos", f"raw_position_{d}"] + shift return closure for k in "xyz": fun = generate_pos_field(k) self.add_field( ("halos", f"particle_position_{k}"), sampling_type="particle", function=fun, units="Mpc", ) yt-project-yt-f043ac8/yt/frontends/adaptahop/io.py000066400000000000000000000212311510711153200222310ustar00rootroot00000000000000""" AdaptaHOP data-file handling function """ from functools import partial from operator import attrgetter import numpy as np from yt.utilities.cython_fortran_utils import FortranFile from yt.utilities.io_handler import BaseParticleIOHandler from .definitions import ATTR_T class IOHandlerAdaptaHOPBinary(BaseParticleIOHandler): _dataset_type = "adaptahop_binary" _offsets = None # Location of halos in the file _particle_positions = None # Buffer of halo position def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_chunk_data(self, chunk, fields): raise NotImplementedError def _yield_coordinates(self, data_file): yield "halos", self._get_particle_positions() def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" ptype = "halos" for data_file in self._sorted_chunk_iterator(chunks): pcount = ( data_file.ds.parameters["nhalos"] + data_file.ds.parameters["nsubs"] ) if pcount == 0: continue pos = self._get_particle_positions() yield ptype, [pos[:, i] for i in range(3)], 0.0 def _read_particle_fields(self, chunks, ptf, selector): # Now we have all the sizes, and we can allocate # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" def iterate_over_attributes(attr_list): for attr, *_ in attr_list: if isinstance(attr, tuple): yield from attr else: yield attr halo_attributes = self.ds._halo_attributes attr_pos = partial(_find_attr_position, halo_attributes=halo_attributes) for data_file in self._sorted_chunk_iterator(chunks): pcount = ( data_file.ds.parameters["nhalos"] + data_file.ds.parameters["nsubs"] ) if pcount == 0: continue ptype = "halos" field_list0 = sorted(ptf[ptype], key=attr_pos) field_list_pos = [f"raw_position_{k}" for k in "xyz"] field_list = sorted(set(field_list0 + field_list_pos), key=attr_pos) with FortranFile(self.ds.parameter_filename) as fpu: params = fpu.read_attrs(self.ds._header_attributes) todo = _todo_from_attributes(field_list, self.ds._halo_attributes) nhalos = params["nhalos"] + params["nsubs"] data = np.zeros((nhalos, len(field_list))) for ihalo in range(nhalos): jj = 0 for it in todo: if isinstance(it, int): fpu.skip(it) else: tmp = fpu.read_attrs(it) for key in iterate_over_attributes(it): v = tmp[key] if key not in field_list: continue data[ihalo, jj] = v jj += 1 ipos = [field_list.index(k) for k in field_list_pos] w = self.ds.domain_width.to("code_length")[0].value / 2 x, y, z = (data[:, i] + w for i in ipos) mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list0: i = field_list.index(field) yield (ptype, field), data[mask, i] def _count_particles(self, data_file): nhalos = data_file.ds.parameters["nhalos"] + data_file.ds.parameters["nsubs"] return {"halos": nhalos} def _identify_fields(self, data_file): fields = [] for attr, _1, _2 in self.ds._halo_attributes: if isinstance(attr, str): fields.append(("halos", attr)) else: for a in attr: fields.append(("halos", a)) return fields, {} # ----------------------------------------------------- # Specific to AdaptaHOP def _get_particle_positions(self): """Read the particles and return them in code_units""" data = getattr(self, "_particle_positions", None) if data is not None: return data with FortranFile(self.ds.parameter_filename) as fpu: params = fpu.read_attrs(self.ds._header_attributes) todo = _todo_from_attributes( ( "particle_identifier", "raw_position_x", "raw_position_y", "raw_position_z", ), self.ds._halo_attributes, ) nhalos = params["nhalos"] + params["nsubs"] data = np.zeros((nhalos, 3)) offset_map = np.zeros((nhalos, 2), dtype="int64") for ihalo in range(nhalos): ipos = fpu.tell() for it in todo: if isinstance(it, int): fpu.skip(it) elif it[0][0] != "particle_identifier": # Small optimisation here: we can read as vector # dt = fpu.read_attrs(it) # data[ihalo, 0] = dt['particle_position_x'] # data[ihalo, 1] = dt['particle_position_y'] # data[ihalo, 2] = dt['particle_position_z'] data[ihalo, :] = fpu.read_vector(it[0][-1]) else: halo_id = fpu.read_int() offset_map[ihalo, 0] = halo_id offset_map[ihalo, 1] = ipos data = self.ds.arr(data, "code_length") + self.ds.domain_width / 2 # Make sure halos are loaded in increasing halo_id order assert np.all(np.diff(offset_map[:, 0]) > 0) # Cache particle positions as one do not expect a large number of halos anyway self._particle_positions = data self._offsets = offset_map return data def members(self, ihalo): offset = self._offsets[ihalo, 1] todo = _todo_from_attributes(("particle_identities",), self.ds._halo_attributes) with FortranFile(self.ds.parameter_filename) as fpu: fpu.seek(offset) if isinstance(todo[0], int): fpu.skip(todo.pop(0)) members = fpu.read_attrs(todo.pop(0))["particle_identities"] return members def _sorted_chunk_iterator(self, chunks): data_files = self._get_data_files(chunks) yield from sorted(data_files, key=attrgetter("filename")) def _todo_from_attributes(attributes: ATTR_T, halo_attributes: ATTR_T): # Helper function to generate a list of read-skip instructions given a list of # attributes. This is used to skip fields most of the fields when reading # the tree_brick files. iskip = 0 todo: list[int | list[tuple[tuple[str, ...] | str, int, str]]] = [] attributes = tuple(set(attributes)) for i, (attrs, l, k) in enumerate(halo_attributes): attrs_list: tuple[str, ...] if isinstance(attrs, tuple): if not all(isinstance(a, str) for a in attrs): raise TypeError(f"Expected a single str or a tuple of str, got {attrs}") attrs_list = attrs else: attrs_list = (attrs,) ok = False for attr in attrs_list: if attr in attributes: ok = True break if i == 0: if ok: state = "read" todo.append([]) else: state = "skip" if ok: if state == "skip": # Switched from skip to read, store skip information and start # new read list todo.append(iskip) todo.append([]) iskip = 0 if not isinstance(todo[-1], list): raise TypeError todo[-1].append((attrs, l, k)) state = "read" else: iskip += 1 state = "skip" if state == "skip" and iskip > 0: todo.append(iskip) return todo def _find_attr_position(key, halo_attributes: ATTR_T) -> int: j = 0 for attrs, *_ in halo_attributes: if not isinstance(attrs, tuple): attrs = (attrs,) for a in attrs: if key == a: return j j += 1 raise KeyError yt-project-yt-f043ac8/yt/frontends/adaptahop/tests/000077500000000000000000000000001510711153200224135ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/adaptahop/tests/__init__.py000066400000000000000000000000001510711153200245120ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/adaptahop/tests/test_outputs.py000066400000000000000000000041211510711153200255450ustar00rootroot00000000000000""" AdaptaHOP frontend tests """ import numpy as np import pytest from yt.frontends.adaptahop.data_structures import AdaptaHOPDataset from yt.testing import requires_file from yt.utilities.answer_testing.framework import data_dir_load r0 = "output_00080/info_00080.txt" brick_old = "output_00080_halos/tree_bricks080" brick_new = "output_00080_new_halos/tree_bricks080" @requires_file(r0) @requires_file(brick_old) @requires_file(brick_new) @pytest.mark.parametrize("brick", [brick_old, brick_new]) def test_opening(brick): ds_parent = data_dir_load(r0) ds = data_dir_load(brick, kwargs={"parent_ds": ds_parent}) ds.index assert isinstance(ds, AdaptaHOPDataset) @requires_file(r0) @requires_file(brick_old) @requires_file(brick_new) @pytest.mark.parametrize("brick", [brick_old, brick_new]) def test_field_access(brick): ds_parent = data_dir_load(r0) ds = data_dir_load(brick, kwargs={"parent_ds": ds_parent}) skip_list = ("particle_identities", "mesh_id", "radius_profile", "rho_profile") fields = [ (ptype, field) for (ptype, field) in ds.derived_field_list if (ptype == "halos") and (field not in skip_list) ] ad = ds.all_data() for ptype, field in fields: ad[ptype, field] @requires_file(r0) @requires_file(brick_old) @requires_file(brick_new) @pytest.mark.parametrize("brick", [brick_old, brick_new]) def test_get_halo(brick): ds_parent = data_dir_load(r0) ds = data_dir_load(brick, kwargs={"parent_ds": ds_parent}) halo = ds.halo(1, ptype="io") # Check halo object has position, velocity, mass and members attributes for attr_name in ("mass", "position", "velocity", "member_ids"): getattr(halo, attr_name) members = np.sort(halo.member_ids) # Check sphere contains all the members id_sphere = halo.sphere["io", "particle_identity"].astype("int64") assert len(np.lib.arraysetops.setdiff1d(members, id_sphere)) == 0 # Check region contains *only* halo particles id_reg = np.sort(halo["io", "particle_identity"].astype("int64")) np.testing.assert_equal(members, id_reg) yt-project-yt-f043ac8/yt/frontends/ahf/000077500000000000000000000000001510711153200200465ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ahf/__init__.py000066400000000000000000000000001510711153200221450ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ahf/api.py000066400000000000000000000001651510711153200211730ustar00rootroot00000000000000from .data_structures import AHFHalosDataset from .fields import AHFHalosFieldInfo from .io import IOHandlerAHFHalos yt-project-yt-f043ac8/yt/frontends/ahf/data_structures.py000066400000000000000000000121031510711153200236310ustar00rootroot00000000000000import glob import os import numpy as np from yt.data_objects.static_output import Dataset from yt.frontends.halo_catalog.data_structures import HaloCatalogFile from yt.funcs import setdefaultattr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.cosmology import Cosmology from .fields import AHFHalosFieldInfo class AHFHalosFile(HaloCatalogFile): def __init__(self, ds, io, filename, file_id, range=None): root, _ = os.path.splitext(filename) candidates = glob.glob(root + "*.AHF_halos") if len(candidates) == 1: filename = candidates[0] else: raise ValueError("Too many AHF_halos files.") self.col_names = self._read_column_names(filename) super().__init__(ds, io, filename, file_id, range) def read_data(self, usecols=None): return np.genfromtxt(self.filename, names=self.col_names, usecols=usecols) def _read_column_names(self, filename): with open(filename) as f: line = f.readline() # Remove leading '#' line = line[1:] names = line.split() # Remove trailing '()' names = [name.split("(")[0] for name in names] return names def _read_particle_positions(self, ptype, f=None): """ Read all particle positions in this file. """ halos = self.read_data(usecols=["Xc", "Yc", "Zc"]) pos = np.empty((halos.size, 3), dtype="float64") for i, ax in enumerate("XYZ"): pos[:, i] = halos[f"{ax}c"].astype("float64") return pos class AHFHalosDataset(Dataset): _index_class = ParticleIndex _file_class = AHFHalosFile _field_info_class = AHFHalosFieldInfo def __init__( self, filename, dataset_type="ahf", n_ref=16, num_zones=2, units_override=None, unit_system="cgs", hubble_constant=1.0, ): root, _ = os.path.splitext(filename) self.log_filename = root + ".log" self.hubble_constant = hubble_constant self.n_ref = n_ref self.num_zones = num_zones super().__init__( filename, dataset_type=dataset_type, units_override=units_override, unit_system=unit_system, ) def _set_code_unit_attributes(self): setdefaultattr(self, "length_unit", self.quan(1.0, "kpccm/h")) setdefaultattr(self, "mass_unit", self.quan(1.0, "Msun/h")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "km/s")) def _parse_parameter_file(self): # Read all parameters. simu = self._read_log_simu() param = self._read_parameter() # Set up general information. self.filename_template = self.parameter_filename self.file_count = 1 self.parameters.update(param) self.particle_types = "halos" self.particle_types_raw = "halos" # Set up geometrical information. self.refine_by = 2 self.dimensionality = 3 nz = self.num_zones self.domain_dimensions = np.ones(self.dimensionality, "int32") * nz self.domain_left_edge = np.array([0.0, 0.0, 0.0]) # Note that boxsize is in Mpc but particle positions are in kpc. self.domain_right_edge = np.array([simu["boxsize"]] * 3) * 1000 self._periodicity = (True, True, True) # Set up cosmological information. self.cosmological_simulation = 1 self.current_redshift = param["z"] self.omega_lambda = simu["lambda0"] self.omega_matter = simu["omega0"] cosmo = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmo.lookback_time(param["z"], 1e6).in_units("s") @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".parameter"): return False with open(filename) as f: return f.readlines()[11].startswith("AHF") # Helper methods def _read_log_simu(self): simu = {} with open(self.log_filename) as f: for l in f: if l.startswith("simu."): name, val = l.split(":") key = name.strip().split(".")[1] try: val = float(val) except Exception: val = float.fromhex(val) simu[key] = val return simu def _read_parameter(self): param = {} with open(self.parameter_filename) as f: for l in f: words = l.split() if len(words) == 2: key, val = words try: val = float(val) param[key] = val except Exception: pass return param @property def _skip_cache(self): return True yt-project-yt-f043ac8/yt/frontends/ahf/fields.py000066400000000000000000000041521510711153200216700ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer m_units = "Msun/h" p_units = "kpccm/h" r_units = "kpccm/h" v_units = "km/s" class AHFHalosFieldInfo(FieldInfoContainer): # See http://popia.ft.uam.es/AHF/files/AHF.pdf # and search for '*.AHF_halos'. known_particle_fields: KnownFieldsT = ( ("ID", ("", ["particle_identifier"], None)), ("hostHalo", ("", [], None)), ("numSubStruct", ("", [], None)), ("Mvir", (m_units, ["particle_mass"], "Virial Mass")), ("npart", ("", [], None)), ("Xc", (p_units, ["particle_position_x"], None)), ("Yc", (p_units, ["particle_position_y"], None)), ("Zc", (p_units, ["particle_position_z"], None)), ("VXc", (v_units, ["particle_velocity_x"], None)), ("VYc", (v_units, ["particle_velocity_y"], None)), ("VZc", (v_units, ["particle_velocity_z"], None)), ("Rvir", (r_units, ["virial_radius"], "Virial Radius")), ("Rmax", (r_units, [], None)), ("r2", (r_units, [], None)), ("mbp_offset", (r_units, [], None)), ("com_offset", (r_units, [], None)), ("Vmax", (v_units, [], None)), ("v_sec", (v_units, [], None)), ("sigV", (v_units, [], None)), ("lambda", ("", [], None)), ("lambdaE", ("", [], None)), ("Lx", ("", [], None)), ("Ly", ("", [], None)), ("Lz", ("", [], None)), ("b", ("", [], None)), ("c", ("", [], None)), ("Eax", ("", [], None)), ("Eay", ("", [], None)), ("Eaz", ("", [], None)), ("Ebx", ("", [], None)), ("Eby", ("", [], None)), ("Ebz", ("", [], None)), ("Ecx", ("", [], None)), ("Ecy", ("", [], None)), ("Ecz", ("", [], None)), ("ovdens", ("", [], None)), ("nbins", ("", [], None)), ("fMhires", ("", [], None)), ("Ekin", ("Msun/h*(km/s)**2", [], None)), ("Epot", ("Msun/h*(km/s)**2", [], None)), ("SurfP", ("Msun/h*(km/s)**2", [], None)), ("Phi0", ("(km/s)**2", [], None)), ("cNFW", ("", [], None)), ) yt-project-yt-f043ac8/yt/frontends/ahf/io.py000066400000000000000000000061611510711153200210330ustar00rootroot00000000000000from operator import attrgetter import numpy as np from yt.utilities.io_handler import BaseParticleIOHandler class IOHandlerAHFHalos(BaseParticleIOHandler): _particle_reader = False _dataset_type = "ahf" def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): # This needs to *yield* a series of tuples of (ptype, (x, y, z), hsml). # chunks is a list of chunks, and ptf is a dict where the keys are # ptypes and the values are lists of fields. # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" for data_file in self._sorted_chunk_iterator(chunks): pos = data_file._get_particle_positions("halos") x, y, z = (pos[:, i] for i in range(3)) yield "halos", (x, y, z), 0.0 def _yield_coordinates(self, data_file): halos = data_file.read_data(usecols=["Xc", "Yc", "Zc"]) x = halos["Xc"].astype("float64") y = halos["Yc"].astype("float64") z = halos["Zc"].astype("float64") yield "halos", np.asarray((x, y, z)).T def _read_particle_fields(self, chunks, ptf, selector): # This gets called after the arrays have been allocated. It needs to # yield ((ptype, field), data) where data is the masked results of # reading ptype, field and applying the selector to the data read in. # Selector objects have a .select_points(x,y,z) that returns a mask, so # you need to do your masking here. # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" for data_file in self._sorted_chunk_iterator(chunks): si, ei = data_file.start, data_file.end cols = [] for field_list in ptf.values(): cols.extend(field_list) cols = list(set(cols)) halos = data_file.read_data(usecols=cols) pos = data_file._get_particle_positions("halos") x, y, z = (pos[:, i] for i in range(3)) yield "halos", (x, y, z) mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for ptype, field_list in sorted(ptf.items()): for field in field_list: data = halos[field][si:ei][mask].astype("float64") yield (ptype, field), data def _count_particles(self, data_file): halos = data_file.read_data(usecols=["ID"]) nhalos = len(halos["ID"]) si, ei = data_file.start, data_file.end if None not in (si, ei): nhalos = np.clip(nhalos - si, 0, ei - si) return {"halos": nhalos} def _identify_fields(self, data_file): fields = [("halos", f) for f in data_file.col_names] return fields, {} def _sorted_chunk_iterator(self, chunks): # yield from sorted list of data_files data_files = self._get_data_files(chunks) yield from sorted(data_files, key=attrgetter("filename")) yt-project-yt-f043ac8/yt/frontends/ahf/tests/000077500000000000000000000000001510711153200212105ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ahf/tests/__init__.py000066400000000000000000000000001510711153200233070ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ahf/tests/test_outputs.py000066400000000000000000000020211510711153200243370ustar00rootroot00000000000000import os.path from numpy.testing import assert_equal from yt.frontends.ahf.api import AHFHalosDataset from yt.testing import ParticleSelectionComparison, requires_file from yt.utilities.answer_testing.framework import ( FieldValuesTest, data_dir_load, requires_ds, ) _fields = ( ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ("all", "particle_mass"), ) ahf_halos = "ahf_halos/snap_N64L16_135.parameter" def load(filename): return data_dir_load(filename, kwargs={"hubble_constant": 0.7}) @requires_ds(ahf_halos) def test_fields_ahf_halos(): ds = load(ahf_halos) assert_equal(str(ds), os.path.basename(ahf_halos)) for field in _fields: yield FieldValuesTest(ds, field, particle_type=True) @requires_file(ahf_halos) def test_AHFHalosDataset(): ds = load(ahf_halos) assert isinstance(ds, AHFHalosDataset) ad = ds.all_data() ad["all", "particle_mass"] psc = ParticleSelectionComparison(ds) psc.run_defaults() yt-project-yt-f043ac8/yt/frontends/amrex/000077500000000000000000000000001510711153200204245ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrex/__init__.py000066400000000000000000000000001510711153200225230ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrex/api.py000066400000000000000000000007061510711153200215520ustar00rootroot00000000000000from . import tests from .data_structures import ( AMReXDataset, AMReXHierarchy, BoxlibDataset, BoxlibGrid, BoxlibHierarchy, CastroDataset, MaestroDataset, NyxDataset, NyxHierarchy, OrionDataset, OrionHierarchy, WarpXDataset, WarpXHierarchy, ) from .fields import ( BoxlibFieldInfo, CastroFieldInfo, MaestroFieldInfo, NyxFieldInfo, WarpXFieldInfo, ) from .io import IOHandlerBoxlib yt-project-yt-f043ac8/yt/frontends/amrex/data_structures.py000066400000000000000000001731561510711153200242270ustar00rootroot00000000000000import glob import os import re from collections import namedtuple from functools import cached_property from stat import ST_CTIME import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.field_info_container import FieldInfoContainer from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.io_handler import io_registry from yt.utilities.lib.misc_utilities import get_box_grids_level from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_root_only from .fields import ( BoxlibFieldInfo, CastroFieldInfo, MaestroFieldInfo, NyxFieldInfo, WarpXFieldInfo, ) # This is what we use to find scientific notation that might include d's # instead of e's. _scinot_finder = re.compile(r"[-+]?[0-9]*\.?[0-9]+([eEdD][-+]?[0-9]+)?") # This is the dimensions in the Cell_H file for each level # It is different for different dimensionalities, so we make a list _1dregx = r"-?\d+" _2dregx = r"-?\d+,-?\d+" _3dregx = r"-?\d+,-?\d+,-?\d+" _dim_finder = [ re.compile(rf"\(\(({ndregx})\) \(({ndregx})\) \({ndregx}\)\)$") for ndregx in (_1dregx, _2dregx, _3dregx) ] # This is the line that prefixes each set of data for a FAB in the FAB file # It is different for different dimensionalities, so we make a list _endian_regex = r"^FAB\ \(\(\d+,\ \([\d\ ]+\)\),\((\d+),\ \(([\d\ ]+)\)\)\)" _header_pattern = [ re.compile( rf"""{_endian_regex} # match `endianness` \( \(( {ndregx} )\) # match `start` \ \(( {ndregx} )\) # match `end` \ \(( {ndregx} )\) # match `centering` \) \ (-?\d+) # match `nc` $ # end of line """, re.VERBOSE, ) for ndregx in (_1dregx, _2dregx, _3dregx) ] class BoxlibGrid(AMRGridPatch): _id_offset = 0 _offset = -1 def __init__(self, grid_id, offset, filename=None, index=None): super().__init__(grid_id, filename, index) self._base_offset = offset self._parent_id = [] self._children_ids = [] self._pdata = {} def _prepare_grid(self): super()._prepare_grid() my_ind = self.id - self._id_offset self.start_index = self.index.grid_start_index[my_ind] def get_global_startindex(self): return self.start_index def _setup_dx(self): # has already been read in and stored in index self.dds = self.index.ds.arr(self.index.level_dds[self.Level, :], "code_length") self.field_data["dx"], self.field_data["dy"], self.field_data["dz"] = self.dds @property def Parent(self): if len(self._parent_id) == 0: return None return [self.index.grids[pid - self._id_offset] for pid in self._parent_id] @property def Children(self): return [self.index.grids[cid - self._id_offset] for cid in self._children_ids] def _get_offset(self, f): # This will either seek to the _offset or figure out the correct # _offset. if self._offset == -1: f.seek(self._base_offset, os.SEEK_SET) f.readline() self._offset = f.tell() return self._offset # We override here because we can have varying refinement levels def select_ires(self, dobj): mask = self._get_selector_mask(dobj.selector) if mask is None: return np.empty(0, dtype="int64") coords = np.empty(self._last_count, dtype="int64") coords[:] = self.Level + self.ds.level_offsets[self.Level] return coords # Override this as well, since refine_by can vary def _fill_child_mask(self, child, mask, tofill, dlevel=1): rf = self.ds.ref_factors[self.Level] if dlevel != 1: raise NotImplementedError gi, cgi = self.get_global_startindex(), child.get_global_startindex() startIndex = np.maximum(0, cgi // rf - gi) endIndex = np.minimum( (cgi + child.ActiveDimensions) // rf - gi, self.ActiveDimensions ) endIndex += startIndex == endIndex mask[ startIndex[0] : endIndex[0], startIndex[1] : endIndex[1], startIndex[2] : endIndex[2], ] = tofill class BoxLibParticleHeader: def __init__(self, ds, directory_name, is_checkpoint, extra_field_names=None): self.particle_type = directory_name header_filename = os.path.join(ds.output_dir, directory_name, "Header") with open(header_filename) as f: self.version_string = f.readline().strip() particle_real_type = self.version_string.split("_")[-1] known_real_types = {"double": np.float64, "single": np.float32} try: self.real_type = known_real_types[particle_real_type] except KeyError: mylog.warning( "yt did not recognize particle real type '%s'. Assuming 'double'.", particle_real_type, ) self.real_type = known_real_types["double"] self.int_type = np.int32 self.dim = int(f.readline().strip()) self.num_int_base = 2 + self.dim self.num_real_base = self.dim self.num_int_extra = 0 # this should be written by Boxlib, but isn't self.num_real_extra = int(f.readline().strip()) self.num_int = self.num_int_base + self.num_int_extra self.num_real = self.num_real_base + self.num_real_extra self.num_particles = int(f.readline().strip()) self.max_next_id = int(f.readline().strip()) self.finest_level = int(f.readline().strip()) self.num_levels = self.finest_level + 1 # Boxlib particles can be written in checkpoint or plotfile mode # The base integer fields are only there for checkpoints, but some # codes use the checkpoint format for plotting if not is_checkpoint: self.num_int_base = 0 self.num_int_extra = 0 self.num_int = 0 self.grids_per_level = np.zeros(self.num_levels, dtype="int64") self.data_map = {} for level_num in range(self.num_levels): self.grids_per_level[level_num] = int(f.readline().strip()) self.data_map[level_num] = {} pfd = namedtuple( "ParticleFileDescriptor", ["file_number", "num_particles", "offset"] ) for level_num in range(self.num_levels): for grid_num in range(self.grids_per_level[level_num]): entry = [int(val) for val in f.readline().strip().split()] self.data_map[level_num][grid_num] = pfd(*entry) self._generate_particle_fields(extra_field_names) def _generate_particle_fields(self, extra_field_names): # these are the 'base' integer fields self.known_int_fields = [ (self.particle_type, "particle_id"), (self.particle_type, "particle_cpu"), (self.particle_type, "particle_cell_x"), (self.particle_type, "particle_cell_y"), (self.particle_type, "particle_cell_z"), ] self.known_int_fields = self.known_int_fields[0 : self.num_int_base] # these are extra integer fields extra_int_fields = [ "particle_int_comp%d" % i for i in range(self.num_int_extra) ] self.known_int_fields.extend( [(self.particle_type, field) for field in extra_int_fields] ) # these are the base real fields self.known_real_fields = [ (self.particle_type, "particle_position_x"), (self.particle_type, "particle_position_y"), (self.particle_type, "particle_position_z"), ] self.known_real_fields = self.known_real_fields[0 : self.num_real_base] # these are the extras if extra_field_names is not None: assert len(extra_field_names) == self.num_real_extra else: extra_field_names = [ "particle_real_comp%d" % i for i in range(self.num_real_extra) ] self.known_real_fields.extend( [(self.particle_type, field) for field in extra_field_names] ) self.known_fields = self.known_int_fields + self.known_real_fields self.particle_int_dtype = np.dtype( [(t[1], self.int_type) for t in self.known_int_fields] ) self.particle_real_dtype = np.dtype( [(t[1], self.real_type) for t in self.known_real_fields] ) class AMReXParticleHeader: def __init__(self, ds, directory_name, is_checkpoint, extra_field_names=None): self.particle_type = directory_name header_filename = os.path.join(ds.output_dir, directory_name, "Header") self.real_component_names = [] self.int_component_names = [] with open(header_filename) as f: self.version_string = f.readline().strip() particle_real_type = self.version_string.split("_")[-1] if particle_real_type == "double": self.real_type = np.float64 elif particle_real_type == "single": self.real_type = np.float32 else: raise RuntimeError("yt did not recognize particle real type.") self.int_type = np.int32 self.dim = int(f.readline().strip()) self.num_int_base = 2 self.num_real_base = self.dim self.num_real_extra = int(f.readline().strip()) for _ in range(self.num_real_extra): self.real_component_names.append(f.readline().strip()) self.num_int_extra = int(f.readline().strip()) for _ in range(self.num_int_extra): self.int_component_names.append(f.readline().strip()) self.num_int = self.num_int_base + self.num_int_extra self.num_real = self.num_real_base + self.num_real_extra self.is_checkpoint = bool(int(f.readline().strip())) self.num_particles = int(f.readline().strip()) self.max_next_id = int(f.readline().strip()) self.finest_level = int(f.readline().strip()) self.num_levels = self.finest_level + 1 if not self.is_checkpoint: self.num_int_base = 0 self.num_int_extra = 0 self.num_int = 0 self.grids_per_level = np.zeros(self.num_levels, dtype="int64") self.data_map = {} for level_num in range(self.num_levels): self.grids_per_level[level_num] = int(f.readline().strip()) self.data_map[level_num] = {} pfd = namedtuple( "ParticleFileDescriptor", ["file_number", "num_particles", "offset"] ) for level_num in range(self.num_levels): for grid_num in range(self.grids_per_level[level_num]): entry = [int(val) for val in f.readline().strip().split()] self.data_map[level_num][grid_num] = pfd(*entry) self._generate_particle_fields() def _generate_particle_fields(self): # these are the 'base' integer fields self.known_int_fields = [ (self.particle_type, "particle_id"), (self.particle_type, "particle_cpu"), ] self.known_int_fields = self.known_int_fields[0 : self.num_int_base] self.known_int_fields.extend( [ (self.particle_type, "particle_" + field) for field in self.int_component_names ] ) # these are the base real fields self.known_real_fields = [ (self.particle_type, "particle_position_x"), (self.particle_type, "particle_position_y"), (self.particle_type, "particle_position_z"), ] self.known_real_fields = self.known_real_fields[0 : self.num_real_base] self.known_real_fields.extend( [ (self.particle_type, "particle_" + field) for field in self.real_component_names ] ) self.known_fields = self.known_int_fields + self.known_real_fields self.particle_int_dtype = np.dtype( [(t[1], self.int_type) for t in self.known_int_fields] ) self.particle_real_dtype = np.dtype( [(t[1], self.real_type) for t in self.known_real_fields] ) class BoxlibHierarchy(GridIndex): grid = BoxlibGrid def __init__(self, ds, dataset_type="boxlib_native"): self.dataset_type = dataset_type self.header_filename = os.path.join(ds.output_dir, "Header") self.directory = ds.output_dir self.particle_headers = {} GridIndex.__init__(self, ds, dataset_type) self._cache_endianness(self.grids[-1]) def _parse_index(self): """ read the global header file for an Boxlib plotfile output. """ self.max_level = self.dataset._max_level header_file = open(self.header_filename) self.dimensionality = self.dataset.dimensionality _our_dim_finder = _dim_finder[self.dimensionality - 1] DRE = self.dataset.domain_right_edge # shortcut DLE = self.dataset.domain_left_edge # shortcut # We can now skip to the point in the file we want to start parsing. header_file.seek(self.dataset._header_mesh_start) dx = [] for i in range(self.max_level + 1): dx.append([float(v) for v in next(header_file).split()]) # account for non-3d data sets if self.dimensionality < 2: dx[i].append(DRE[1] - DLE[1]) if self.dimensionality < 3: dx[i].append(DRE[2] - DLE[2]) self.level_dds = np.array(dx, dtype="float64") next(header_file) match self.ds.geometry: case Geometry.CARTESIAN: default_ybounds = (0.0, 1.0) default_zbounds = (0.0, 1.0) case Geometry.CYLINDRICAL: default_ybounds = (0.0, 1.0) default_zbounds = (0.0, 2 * np.pi) case Geometry.SPHERICAL: default_ybounds = (0.0, np.pi) default_zbounds = (0.0, 2 * np.pi) case _: header_file.close() raise RuntimeError("Unknown BoxLib coordinate system.") if int(next(header_file)) != 0: header_file.close() raise RuntimeError("INTERNAL ERROR! This should be a zero.") # each level is one group with ngrids on it. # each grid has self.dimensionality number of lines of 2 reals self.grids = [] grid_counter = 0 for level in range(self.max_level + 1): vals = next(header_file).split() lev, ngrids = int(vals[0]), int(vals[1]) assert lev == level nsteps = int(next(header_file)) # NOQA for gi in range(ngrids): xlo, xhi = (float(v) for v in next(header_file).split()) if self.dimensionality > 1: ylo, yhi = (float(v) for v in next(header_file).split()) else: ylo, yhi = default_ybounds if self.dimensionality > 2: zlo, zhi = (float(v) for v in next(header_file).split()) else: zlo, zhi = default_zbounds self.grid_left_edge[grid_counter + gi, :] = [xlo, ylo, zlo] self.grid_right_edge[grid_counter + gi, :] = [xhi, yhi, zhi] # Now we get to the level header filename, which we open and parse. fn = os.path.join(self.dataset.output_dir, next(header_file).strip()) level_header_file = open(fn + "_H") level_dir = os.path.dirname(fn) # We skip the first two lines, which contain BoxLib header file # version and 'how' the data was written next(level_header_file) next(level_header_file) # Now we get the number of components ncomp_this_file = int(next(level_header_file)) # NOQA # Skip the next line, which contains the number of ghost zones next(level_header_file) # To decipher this next line, we expect something like: # (8 0 # where the first is the number of FABs in this level. ngrids = int(next(level_header_file).split()[0][1:]) # Now we can iterate over each and get the indices. for gi in range(ngrids): # components within it start, stop = _our_dim_finder.match(next(level_header_file)).groups() # fix for non-3d data # note we append '0' to both ends b/c of the '+1' in dims below start += ",0" * (3 - self.dimensionality) stop += ",0" * (3 - self.dimensionality) start = np.array(start.split(","), dtype="int64") stop = np.array(stop.split(","), dtype="int64") dims = stop - start + 1 self.grid_dimensions[grid_counter + gi, :] = dims self.grid_start_index[grid_counter + gi, :] = start # Now we read two more lines. The first of these is a close # parenthesis. next(level_header_file) # The next is again the number of grids next(level_header_file) # Now we iterate over grids to find their offsets in each file. for gi in range(ngrids): # Now we get the data file, at which point we're ready to # create the grid. dummy, filename, offset = next(level_header_file).split() filename = os.path.join(level_dir, filename) go = self.grid(grid_counter + gi, int(offset), filename, self) go.Level = self.grid_levels[grid_counter + gi, :] = level self.grids.append(go) level_header_file.close() grid_counter += ngrids # already read the filenames above... self.float_type = "float64" header_file.close() def _cache_endianness(self, test_grid): """ Cache the endianness and bytes perreal of the grids by using a test grid and assuming that all grids have the same endianness. This is a pretty safe assumption since Boxlib uses one file per processor, and if you're running on a cluster with different endian processors, then you're on your own! """ # open the test file & grab the header with open(os.path.expanduser(test_grid.filename), "rb") as f: header = f.readline().decode("ascii", "ignore") bpr, endian, start, stop, centering, nc = ( _header_pattern[self.dimensionality - 1].search(header).groups() ) # Note that previously we were using a different value for BPR than we # use now. Here is an example set of information directly from BoxLib """ * DOUBLE data * FAB ((8, (64 11 52 0 1 12 0 1023)),(8, (1 2 3 4 5 6 7 8)))((0,0) (63,63) (0,0)) 27 # NOQA: E501 * FLOAT data * FAB ((8, (32 8 23 0 1 9 0 127)),(4, (1 2 3 4)))((0,0) (63,63) (0,0)) 27 """ if bpr == endian[0]: dtype = f"f{bpr}" else: raise ValueError( "FAB header is neither big nor little endian. " "Perhaps the file is corrupt?" ) mylog.debug("FAB header suggests dtype of %s", dtype) self._dtype = np.dtype(dtype) def _populate_grid_objects(self): mylog.debug("Creating grid objects") self.grids = np.array(self.grids, dtype="object") self._reconstruct_parent_child() for i, grid in enumerate(self.grids): if (i % 1e4) == 0: mylog.debug("Prepared % 7i / % 7i grids", i, self.num_grids) grid._prepare_grid() grid._setup_dx() mylog.debug("Done creating grid objects") def _reconstruct_parent_child(self): if self.max_level == 0: return mask = np.empty(len(self.grids), dtype="int32") mylog.debug("First pass; identifying child grids") for i, grid in enumerate(self.grids): get_box_grids_level( self.grid_left_edge[i, :], self.grid_right_edge[i, :], self.grid_levels[i].item() + 1, self.grid_left_edge, self.grid_right_edge, self.grid_levels, mask, ) ids = np.where(mask.astype("bool")) # where is a tuple grid._children_ids = ids[0] + grid._id_offset mylog.debug("Second pass; identifying parents") for i, grid in enumerate(self.grids): # Second pass for child in grid.Children: child._parent_id.append(i + grid._id_offset) def _count_grids(self): # We can get everything from the Header file, but note that we're # duplicating some work done elsewhere. In a future where we don't # pre-allocate grid arrays, this becomes unnecessary. header_file = open(self.header_filename) header_file.seek(self.dataset._header_mesh_start) # Skip over the level dxs, geometry and the zero: [next(header_file) for i in range(self.dataset._max_level + 3)] # Now we need to be very careful, as we've seeked, and now we iterate. # Does this work? We are going to count the number of places that we # have a three-item line. The three items would be level, number of # grids, and then grid time. self.num_grids = 0 for line in header_file: if len(line.split()) != 3: continue self.num_grids += int(line.split()[1]) header_file.close() def _initialize_grid_arrays(self): super()._initialize_grid_arrays() self.grid_start_index = np.zeros((self.num_grids, 3), "int64") def _initialize_state_variables(self): """override to not re-initialize num_grids in AMRHierarchy.__init__""" self._parallel_locking = False self._data_file = None self._data_mode = None def _detect_output_fields(self): # This is all done in _parse_header_file self.field_list = [("boxlib", f) for f in self.dataset._field_list] self.field_indexes = {f[1]: i for i, f in enumerate(self.field_list)} # There are times when field_list may change. We copy it here to # avoid that possibility. self.field_order = list(self.field_list) def _setup_data_io(self): self.io = io_registry[self.dataset_type](self.dataset) def _determine_particle_output_type(self, directory_name): header_filename = os.path.join(self.ds.output_dir, directory_name, "Header") with open(header_filename) as f: version_string = f.readline().strip() if version_string.startswith("Version_Two"): return AMReXParticleHeader else: return BoxLibParticleHeader def _read_particles(self, directory_name, is_checkpoint, extra_field_names=None): pheader = self._determine_particle_output_type(directory_name) self.particle_headers[directory_name] = pheader( self.ds, directory_name, is_checkpoint, extra_field_names ) num_parts = self.particle_headers[directory_name].num_particles if self.ds._particle_type_counts is None: self.ds._particle_type_counts = {} self.ds._particle_type_counts[directory_name] = num_parts base = os.path.join(self.ds.output_dir, directory_name) if len(glob.glob(os.path.join(base, "Level_?", "DATA_????"))) > 0: base_particle_fn = os.path.join(base, "Level_%d", "DATA_%.4d") elif len(glob.glob(os.path.join(base, "Level_?", "DATA_?????"))) > 0: base_particle_fn = os.path.join(base, "Level_%d", "DATA_%.5d") else: return gid = 0 for lev, data in self.particle_headers[directory_name].data_map.items(): for pdf in data.values(): pdict = self.grids[gid]._pdata pdict[directory_name] = {} pdict[directory_name]["particle_filename"] = base_particle_fn % ( lev, pdf.file_number, ) pdict[directory_name]["offset"] = pdf.offset pdict[directory_name]["NumberOfParticles"] = pdf.num_particles self.grid_particle_count[gid] += pdf.num_particles self.grids[gid].NumberOfParticles += pdf.num_particles gid += 1 # add particle fields to field_list pfield_list = self.particle_headers[directory_name].known_fields self.field_list.extend(pfield_list) class BoxlibDataset(Dataset): """ This class is a stripped down class that simply reads and parses *filename*, without looking at the Boxlib index. """ _index_class = BoxlibHierarchy _field_info_class: type[FieldInfoContainer] = BoxlibFieldInfo _output_prefix = None _default_cparam_filename = "job_info" def __init__( self, output_dir, cparam_filename=None, fparam_filename=None, dataset_type="boxlib_native", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): """ The paramfile is usually called "inputs" and there may be a fortran inputs file usually called "probin" plotname here will be a directory name as per BoxLib, dataset_type will be Native (implemented here), IEEE (not yet implemented) or ASCII (not yet implemented.) """ self.fluid_types += ("boxlib",) self.output_dir = os.path.abspath(os.path.expanduser(output_dir)) cparam_filename = cparam_filename or self.__class__._default_cparam_filename self.cparam_filename = self._lookup_cparam_filepath( self.output_dir, cparam_filename=cparam_filename ) self.fparam_filename = self._localize_check(fparam_filename) self.storage_filename = storage_filename Dataset.__init__( self, output_dir, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) # These are still used in a few places. if "HydroMethod" not in self.parameters.keys(): self.parameters["HydroMethod"] = "boxlib" self.parameters["Time"] = 1.0 # default unit is 1... self.parameters["EOSType"] = -1 # default self.parameters["gamma"] = self.parameters.get("materials.gamma", 1.6667) def _localize_check(self, fn): if fn is None: return None # If the file exists, use it. If not, set it to None. root_dir = os.path.dirname(self.output_dir) full_fn = os.path.join(root_dir, fn) if os.path.exists(full_fn): return full_fn return None @classmethod def _is_valid(cls, filename, *args, cparam_filename=None, **kwargs): output_dir = filename header_filename = os.path.join(output_dir, "Header") # boxlib datasets are always directories, and # We *know* it's not boxlib if Header doesn't exist. if not os.path.exists(header_filename): return False if cls is BoxlibDataset: # Stop checks here for the boxlib base class. # Further checks are performed on subclasses. return True cparam_filename = cparam_filename or cls._default_cparam_filename cparam_filepath = cls._lookup_cparam_filepath(output_dir, cparam_filename) if cparam_filepath is None: return False with open(cparam_filepath) as f: lines = [line.lower() for line in f] return any(cls._subtype_keyword in line for line in lines) @classmethod def _lookup_cparam_filepath(cls, output_dir, cparam_filename): lookup_table = [ os.path.abspath(os.path.join(p, cparam_filename)) for p in (output_dir, os.path.dirname(output_dir)) ] found = [os.path.exists(file) for file in lookup_table] if not any(found): return None return lookup_table[found.index(True)] @cached_property def unique_identifier(self) -> str: hfn = os.path.join(self.output_dir, "Header") return str(int(os.stat(hfn)[ST_CTIME])) def _parse_parameter_file(self): """ Parses the parameter file and establishes the various dictionaries. """ self._periodicity = (False, False, False) self._parse_header_file() # Let's read the file # the 'inputs' file is now optional self._parse_cparams() self._parse_fparams() def _parse_cparams(self): if self.cparam_filename is None: return with open(self.cparam_filename) as param_file: for line in (line.split("#")[0].strip() for line in param_file): try: param, vals = (s.strip() for s in line.split("=")) except ValueError: continue # Castro and Maestro mark overridden defaults with a "[*]" # before the parameter name param = param.removeprefix("[*]").strip() if param == "amr.ref_ratio": vals = self.refine_by = int(vals[0]) elif param == "Prob.lo_bc": vals = tuple(p == "1" for p in vals.split()) assert len(vals) == self.dimensionality # default to non periodic periodicity = [False, False, False] # fill in ndim parsed values periodicity[: self.dimensionality] = vals self._periodicity = tuple(periodicity) elif param == "castro.use_comoving": vals = self.cosmological_simulation = int(vals) else: try: vals = _guess_pcast(vals) except (IndexError, ValueError): # hitting an empty string or a comment vals = None self.parameters[param] = vals if getattr(self, "cosmological_simulation", 0) == 1: self.omega_lambda = self.parameters["comoving_OmL"] self.omega_matter = self.parameters["comoving_OmM"] self.hubble_constant = self.parameters["comoving_h"] with open(os.path.join(self.output_dir, "comoving_a")) as a_file: line = a_file.readline().strip() self.current_redshift = 1 / float(line) - 1 else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 def _parse_fparams(self): """ Parses the fortran parameter file for Orion. Most of this will be useless, but this is where it keeps mu = mass per particle/m_hydrogen. """ if self.fparam_filename is None: return param_file = open(self.fparam_filename) for line in (l for l in param_file if "=" in l): param, vals = (v.strip() for v in line.split("=")) # Now, there are a couple different types of parameters. # Some will be where you only have floating point values, others # will be where things are specified as string literals. # Unfortunately, we're also using Fortran values, which will have # things like 1.d-2 which is pathologically difficult to parse if # your C library doesn't include 'd' in its locale for strtod. # So we'll try to determine this. vals = vals.split() if any(_scinot_finder.match(v) for v in vals): vals = [float(v.replace("D", "e").replace("d", "e")) for v in vals] if len(vals) == 1: vals = vals[0] self.parameters[param] = vals param_file.close() def _parse_header_file(self): """ We parse the Boxlib header, which we use as our basis. Anything in the inputs file will override this, but the inputs file is not strictly necessary for orientation of the data in space. """ # Note: Python uses a read-ahead buffer, so using next(), which would # be my preferred solution, won't work here. We have to explicitly # call readline() if we want to end up with an offset at the very end. # Fortunately, elsewhere we don't care about the offset, so we're fine # everywhere else using iteration exclusively. header_file = open(os.path.join(self.output_dir, "Header")) self.orion_version = header_file.readline().rstrip() n_fields = int(header_file.readline()) self._field_list = [header_file.readline().strip() for i in range(n_fields)] self.dimensionality = int(header_file.readline()) self.current_time = float(header_file.readline()) # This is traditionally a index attribute, so we will set it, but # in a slightly hidden variable. self._max_level = int(header_file.readline()) for side, init in [("left", np.zeros), ("right", np.ones)]: domain_edge = init(3, dtype="float64") domain_edge[: self.dimensionality] = header_file.readline().split() setattr(self, f"domain_{side}_edge", domain_edge) ref_factors = np.array(header_file.readline().split(), dtype="int64") if ref_factors.size == 0: # We use a default of two, as Nyx doesn't always output this value ref_factors = [2] * (self._max_level + 1) # We can't vary refinement factors based on dimension, or whatever else # they are varied on. In one curious thing, I found that some Castro 3D # data has only two refinement factors, which I don't know how to # understand. self.ref_factors = ref_factors if np.unique(ref_factors).size > 1: # We want everything to be a multiple of this. self.refine_by = min(ref_factors) # Check that they're all multiples of the minimum. if not all( float(rf) / self.refine_by == int(float(rf) / self.refine_by) for rf in ref_factors ): header_file.close() raise RuntimeError base_log = np.log2(self.refine_by) self.level_offsets = [0] # level 0 has to have 0 offset lo = 0 for rf in self.ref_factors: lo += int(np.log2(rf) / base_log) - 1 self.level_offsets.append(lo) # assert(np.unique(ref_factors).size == 1) else: self.refine_by = ref_factors[0] self.level_offsets = [0 for l in range(self._max_level + 1)] # Now we read the global index space, to get index_space = header_file.readline() # This will be of the form: # ((0,0,0) (255,255,255) (0,0,0)) ((0,0,0) (511,511,511) (0,0,0)) # So note that if we split it all up based on spaces, we should be # fine, as long as we take the first two entries, which correspond to # the root level. I'm not 100% pleased with this solution. root_space = index_space.replace("(", "").replace(")", "").split()[:2] start = np.array(root_space[0].split(","), dtype="int64") stop = np.array(root_space[1].split(","), dtype="int64") dd = np.ones(3, dtype="int64") dd[: self.dimensionality] = stop - start + 1 self.domain_offset[: self.dimensionality] = start self.domain_dimensions = dd # Skip timesteps per level header_file.readline() self._header_mesh_start = header_file.tell() # Skip the cell size information per level - we'll get this later for _ in range(self._max_level + 1): header_file.readline() # Get the geometry next_line = header_file.readline() if len(next_line.split()) == 1: coordinate_type = int(next_line) else: coordinate_type = 0 known_types = {0: "cartesian", 1: "cylindrical", 2: "spherical"} try: geom_str = known_types[coordinate_type] except KeyError as err: header_file.close() raise ValueError(f"Unknown BoxLib coord_type `{coordinate_type}`.") from err else: self.geometry = Geometry(geom_str) if self.geometry is Geometry.CYLINDRICAL: dre = self.domain_right_edge.copy() dre[2] = 2.0 * np.pi self.domain_right_edge = dre if self.geometry is Geometry.SPHERICAL and self.dimensionality < 3: dre = self.domain_right_edge.copy() dre[2] = 2.0 * np.pi if self.dimensionality < 2: dre[1] = np.pi self.domain_right_edge = dre header_file.close() def _set_code_unit_attributes(self): setdefaultattr(self, "length_unit", self.quan(1.0, "cm")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "cm/s")) @parallel_root_only def print_key_parameters(self): for a in [ "current_time", "domain_dimensions", "domain_left_edge", "domain_right_edge", ]: if not hasattr(self, a): mylog.error("Missing %s in parameter file definition!", a) continue v = getattr(self, a) mylog.info("Parameters: %-25s = %s", a, v) def relative_refinement(self, l0, l1): offset = self.level_offsets[l1] - self.level_offsets[l0] return self.refine_by ** (l1 - l0 + offset) class AMReXHierarchy(BoxlibHierarchy): def __init__(self, ds, dataset_type="boxlib_native"): super().__init__(ds, dataset_type) if "particles" in self.ds.parameters: is_checkpoint = True for ptype in self.ds.particle_types: self._read_particles(ptype, is_checkpoint) class AMReXDataset(BoxlibDataset): _index_class: type[BoxlibHierarchy] = AMReXHierarchy _subtype_keyword = "amrex" _default_cparam_filename = "job_info" def _parse_parameter_file(self): super()._parse_parameter_file() particle_types = glob.glob(os.path.join(self.output_dir, "*", "Header")) particle_types = [cpt.split(os.sep)[-2] for cpt in particle_types] if len(particle_types) > 0: self.parameters["particles"] = 1 self.particle_types = tuple(particle_types) self.particle_types_raw = self.particle_types class OrionHierarchy(BoxlibHierarchy): def __init__(self, ds, dataset_type="orion_native"): BoxlibHierarchy.__init__(self, ds, dataset_type) self._read_particles() # self.io = IOHandlerOrion def _detect_output_fields(self): # This is all done in _parse_header_file self.field_list = [("boxlib", f) for f in self.dataset._field_list] self.field_indexes = {f[1]: i for i, f in enumerate(self.field_list)} # There are times when field_list may change. We copy it here to # avoid that possibility. self.field_order = list(self.field_list) # look for particle fields self.particle_filename = None for particle_filename in ["StarParticles", "SinkParticles"]: fn = os.path.join(self.ds.output_dir, particle_filename) if os.path.exists(fn): self.particle_filename = fn if self.particle_filename is None: return pfield_list = [("io", c) for c in self.io.particle_field_index.keys()] self.field_list.extend(pfield_list) def _read_particles(self): """ Reads in particles and assigns them to grids. Will search for Star particles, then sink particles if no star particle file is found, and finally will simply note that no particles are found if neither works. To add a new Orion particle type, simply add it to the if/elif/else block. """ self.grid_particle_count = np.zeros(len(self.grids)) if self.particle_filename is not None: self._read_particle_file(self.particle_filename) def _read_particle_file(self, fn): """actually reads the orion particle data file itself.""" if not os.path.exists(fn): return with open(fn) as f: lines = f.readlines() self.num_stars = int(lines[0].strip()[0]) for num, line in enumerate(lines[1:]): particle_position_x = float(line.split(" ")[1]) particle_position_y = float(line.split(" ")[2]) particle_position_z = float(line.split(" ")[3]) coord = [particle_position_x, particle_position_y, particle_position_z] # for each particle, determine which grids contain it # copied from object_finding_mixin.py mask = np.ones(self.num_grids) for i in range(len(coord)): np.choose( np.greater(self.grid_left_edge.d[:, i], coord[i]), (mask, 0), mask, ) np.choose( np.greater(self.grid_right_edge.d[:, i], coord[i]), (0, mask), mask, ) ind = np.where(mask == 1) selected_grids = self.grids[ind] # in orion, particles always live on the finest level. # so, we want to assign the particle to the finest of # the grids we just found if len(selected_grids) != 0: grid = sorted(selected_grids, key=lambda grid: grid.Level)[-1] ind = np.where(self.grids == grid)[0][0] self.grid_particle_count[ind] += 1 self.grids[ind].NumberOfParticles += 1 # store the position in the particle file for fast access. try: self.grids[ind]._particle_line_numbers.append(num + 1) except AttributeError: self.grids[ind]._particle_line_numbers = [num + 1] return True class OrionDataset(BoxlibDataset): _index_class = OrionHierarchy _subtype_keyword = "hyp." _default_cparam_filename = "inputs" def __init__( self, output_dir, cparam_filename=None, fparam_filename="probin", dataset_type="orion_native", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): BoxlibDataset.__init__( self, output_dir, cparam_filename, fparam_filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) class CastroHierarchy(BoxlibHierarchy): def __init__(self, ds, dataset_type="castro_native"): super().__init__(ds, dataset_type) if "particles" in self.ds.parameters: # extra beyond the base real fields that all Boxlib # particles have, i.e. the xyz positions castro_extra_real_fields = [ "particle_velocity_x", "particle_velocity_y", "particle_velocity_z", ] is_checkpoint = True self._read_particles( "Tracer", is_checkpoint, castro_extra_real_fields[0 : self.ds.dimensionality], ) class CastroDataset(AMReXDataset): _index_class = CastroHierarchy _field_info_class = CastroFieldInfo _subtype_keyword = "castro" _default_cparam_filename = "job_info" def __init__( self, output_dir, cparam_filename=None, fparam_filename=None, dataset_type="boxlib_native", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): super().__init__( output_dir, cparam_filename, fparam_filename, dataset_type, storage_filename, units_override, unit_system, default_species_fields=default_species_fields, ) def _parse_parameter_file(self): super()._parse_parameter_file() jobinfo_filename = os.path.join(self.output_dir, self.cparam_filename) line = "" with open(jobinfo_filename) as f: while not line.startswith(" Inputs File Parameters"): # boundary condition info starts with -x:, etc. bcs = ["-x:", "+x:", "-y:", "+y:", "-z:", "+z:"] if any(b in line for b in bcs): p, v = line.strip().split(":") self.parameters[p] = v.strip() if "git describe" in line or "git hash" in line: # Castro release 17.02 and later # line format: codename git describe: the-hash # Castro before release 17.02 # line format: codename git hash: the-hash fields = line.split(":") self.parameters[fields[0]] = fields[1].strip() line = next(f) # hydro method is set by the base class -- override it here self.parameters["HydroMethod"] = "Castro" # set the periodicity based on the runtime parameters # https://amrex-astro.github.io/Castro/docs/inputs.html?highlight=periodicity periodicity = [False, False, False] for i, axis in enumerate("xyz"): try: periodicity[i] = self.parameters[f"-{axis}"] == "interior" except KeyError: break self._periodicity = tuple(periodicity) if os.path.isdir(os.path.join(self.output_dir, "Tracer")): # we have particles self.parameters["particles"] = 1 self.particle_types = ("Tracer",) self.particle_types_raw = self.particle_types class MaestroDataset(AMReXDataset): _index_class = BoxlibHierarchy _field_info_class = MaestroFieldInfo _subtype_keyword = "maestro" _default_cparam_filename = "job_info" def __init__( self, output_dir, cparam_filename=None, fparam_filename=None, dataset_type="boxlib_native", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): super().__init__( output_dir, cparam_filename, fparam_filename, dataset_type, storage_filename, units_override, unit_system, default_species_fields=default_species_fields, ) def _parse_parameter_file(self): super()._parse_parameter_file() jobinfo_filename = os.path.join(self.output_dir, self.cparam_filename) with open(jobinfo_filename) as f: for line in f: # get the code git hashes if "git hash" in line: # line format: codename git hash: the-hash fields = line.split(":") self.parameters[fields[0]] = fields[1].strip() # hydro method is set by the base class -- override it here self.parameters["HydroMethod"] = "Maestro" # set the periodicity based on the integer BC runtime parameters periodicity = [False, False, False] for i, ax in enumerate("xyz"): try: periodicity[i] = self.parameters[f"bc{ax}_lo"] != -1 except KeyError: pass self._periodicity = tuple(periodicity) class NyxHierarchy(BoxlibHierarchy): def __init__(self, ds, dataset_type="nyx_native"): super().__init__(ds, dataset_type) if "particles" in self.ds.parameters: # extra beyond the base real fields that all Boxlib # particles have, i.e. the xyz positions nyx_extra_real_fields = [ "particle_mass", "particle_velocity_x", "particle_velocity_y", "particle_velocity_z", ] is_checkpoint = False self._read_particles( "DM", is_checkpoint, nyx_extra_real_fields[0 : self.ds.dimensionality + 1], ) class NyxDataset(BoxlibDataset): _index_class = NyxHierarchy _field_info_class = NyxFieldInfo _subtype_keyword = "nyx" _default_cparam_filename = "job_info" def __init__( self, output_dir, cparam_filename=None, fparam_filename=None, dataset_type="boxlib_native", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): super().__init__( output_dir, cparam_filename, fparam_filename, dataset_type, storage_filename, units_override, unit_system, default_species_fields=default_species_fields, ) def _parse_parameter_file(self): super()._parse_parameter_file() jobinfo_filename = os.path.join(self.output_dir, self.cparam_filename) with open(jobinfo_filename) as f: for line in f: # get the code git hashes if "git hash" in line: # line format: codename git hash: the-hash fields = line.split(":") self.parameters[fields[0]] = fields[1].strip() if line.startswith(" Cosmology Information"): self.cosmological_simulation = 1 break else: self.cosmological_simulation = 0 if self.cosmological_simulation: # note that modern Nyx is always cosmological, but there are some old # files without these parameters so we want to special-case them for line in f: if "Omega_m (comoving)" in line: self.omega_matter = float(line.split(":")[1]) elif "Omega_lambda (comoving)" in line: self.omega_lambda = float(line.split(":")[1]) elif "h (comoving)" in line: self.hubble_constant = float(line.split(":")[1]) # Read in the `comoving_a` file and parse the value. We should fix this # in the new Nyx output format... with open(os.path.join(self.output_dir, "comoving_a")) as a_file: a_string = a_file.readline().strip() # Set the scale factor and redshift self.cosmological_scale_factor = float(a_string) self.parameters["CosmologyCurrentRedshift"] = 1 / float(a_string) - 1 # alias self.current_redshift = self.parameters["CosmologyCurrentRedshift"] if os.path.isfile(os.path.join(self.output_dir, "DM/Header")): # we have particles self.parameters["particles"] = 1 self.particle_types = ("DM",) self.particle_types_raw = self.particle_types def _set_code_unit_attributes(self): setdefaultattr(self, "mass_unit", self.quan(1.0, "Msun")) setdefaultattr(self, "time_unit", self.quan(1.0 / 3.08568025e19, "s")) setdefaultattr( self, "length_unit", self.quan(1.0 / (1 + self.current_redshift), "Mpc") ) setdefaultattr(self, "velocity_unit", self.length_unit / self.time_unit) class QuokkaDataset(AMReXDataset): # match any plotfiles that have a metadata.yaml file in the root _subtype_keyword = "" _default_cparam_filename = "metadata.yaml" def _guess_pcast(vals): # Now we guess some things about the parameter and its type # Just in case there are multiple; we'll go # back afterward to using vals. v = vals.split()[0] try: float(v.upper().replace("D", "E")) except Exception: pcast = str if v in ("F", "T"): pcast = bool else: syms = (".", "D+", "D-", "E+", "E-", "E", "D") if any(sym in v.upper() for sym in syms for v in vals.split()): pcast = float else: pcast = int if pcast is bool: vals = [value == "T" for value in vals.split()] else: vals = [pcast(value) for value in vals.split()] if len(vals) == 1: vals = vals[0] return vals def _read_raw_field_names(raw_file): header_files = glob.glob(os.path.join(raw_file, "*_H")) return [hf.split(os.sep)[-1][:-2] for hf in header_files] def _string_to_numpy_array(s): return np.array([int(v) for v in s[1:-1].split(",")], dtype=np.int64) def _line_to_numpy_arrays(line): lo_corner = _string_to_numpy_array(line[0][1:]) hi_corner = _string_to_numpy_array(line[1][:]) node_type = _string_to_numpy_array(line[2][:-1]) return lo_corner, hi_corner, node_type def _get_active_dimensions(box): return box[1] - box[2] - box[0] + 1 def _read_header(raw_file, field): level_files = glob.glob(os.path.join(raw_file, "Level_*")) level_files.sort() all_boxes = [] all_file_names = [] all_offsets = [] for level_file in level_files: header_file = os.path.join(level_file, field + "_H") with open(header_file) as f: f.readline() # version f.readline() # how f.readline() # ncomp # nghost_line will be parsed below after the number of dimensions # is determined when the boxes are read in nghost_line = f.readline().strip().split() f.readline() # num boxes # read boxes boxes = [] for line in f: clean_line = line.strip().split() if clean_line == [")"]: break lo_corner, hi_corner, node_type = _line_to_numpy_arrays(clean_line) boxes.append((lo_corner, hi_corner, node_type)) try: # nghost_line[0] is a single number ng = int(nghost_line[0]) ndims = len(lo_corner) nghost = np.array(ndims * [ng]) except ValueError: # nghost_line[0] is (#,#,#) nghost_list = nghost_line[0].strip("()").split(",") nghost = np.array(nghost_list, dtype="int64") # read the file and offset position for the corresponding box file_names = [] offsets = [] for line in f: if line.startswith("FabOnDisk:"): clean_line = line.strip().split() file_names.append(clean_line[1]) offsets.append(int(clean_line[2])) all_boxes += boxes all_file_names += file_names all_offsets += offsets return nghost, all_boxes, all_file_names, all_offsets class WarpXHeader: def __init__(self, header_fn): self.data = {} with open(header_fn) as f: self.data["Checkpoint_version"] = int(f.readline().strip().split()[-1]) self.data["num_levels"] = int(f.readline().strip().split()[-1]) self.data["istep"] = [int(num) for num in f.readline().strip().split()] self.data["nsubsteps"] = [int(num) for num in f.readline().strip().split()] self.data["t_new"] = [float(num) for num in f.readline().strip().split()] self.data["t_old"] = [float(num) for num in f.readline().strip().split()] self.data["dt"] = [float(num) for num in f.readline().strip().split()] self.data["moving_window_x"] = float(f.readline().strip().split()[-1]) # not all datasets will have is_synchronized line = f.readline().strip().split() if len(line) == 1: self.data["is_synchronized"] = bool(line[-1]) self.data["prob_lo"] = [ float(num) for num in f.readline().strip().split() ] else: self.data["is_synchronized"] = True self.data["prob_lo"] = [float(num) for num in line] self.data["prob_hi"] = [float(num) for num in f.readline().strip().split()] for _ in range(self.data["num_levels"]): num_boxes = int(f.readline().strip().split()[0][1:]) for __ in range(num_boxes): f.readline() f.readline() i = 0 line = f.readline() while line: line = line.strip().split() if len(line) == 1: line = f.readline() continue self.data["species_%d" % i] = [float(val) for val in line] i = i + 1 line = f.readline() class WarpXHierarchy(BoxlibHierarchy): def __init__(self, ds, dataset_type="boxlib_native"): super().__init__(ds, dataset_type) is_checkpoint = True for ptype in self.ds.particle_types: self._read_particles(ptype, is_checkpoint) # Additional WarpX particle information (used to set up species) self.warpx_header = WarpXHeader(os.path.join(self.ds.output_dir, "WarpXHeader")) for key, val in self.warpx_header.data.items(): if key.startswith("species_"): i = int(key.split("_")[-1]) charge_name = "particle%.1d_charge" % i mass_name = "particle%.1d_mass" % i self.parameters[charge_name] = val[0] self.parameters[mass_name] = val[1] def _detect_output_fields(self): super()._detect_output_fields() # now detect the optional, non-cell-centered fields self.raw_file = os.path.join(self.ds.output_dir, "raw_fields") self.raw_fields = _read_raw_field_names(os.path.join(self.raw_file, "Level_0")) self.field_list += [("raw", f) for f in self.raw_fields] self.raw_field_map = {} self.ds.nodal_flags = {} self.raw_field_nghost = {} for field_name in self.raw_fields: nghost, boxes, file_names, offsets = _read_header(self.raw_file, field_name) self.raw_field_map[field_name] = (boxes, file_names, offsets) self.raw_field_nghost[field_name] = nghost self.ds.nodal_flags[field_name] = np.array(boxes[0][2]) def _skip_line(line): if len(line) == 0: return True if line[0] == "\n": return True if line[0] == "=": return True if line[0] == " ": return True class WarpXDataset(BoxlibDataset): _index_class = WarpXHierarchy _field_info_class = WarpXFieldInfo _subtype_keyword = "warpx" _default_cparam_filename = "warpx_job_info" def __init__( self, output_dir, cparam_filename=None, fparam_filename=None, dataset_type="boxlib_native", storage_filename=None, units_override=None, unit_system="mks", ): self.default_fluid_type = "mesh" self.default_field = ("mesh", "density") self.fluid_types = ("mesh", "index", "raw") super().__init__( output_dir, cparam_filename, fparam_filename, dataset_type, storage_filename, units_override, unit_system, ) def _parse_parameter_file(self): super()._parse_parameter_file() jobinfo_filename = os.path.join(self.output_dir, self.cparam_filename) with open(jobinfo_filename) as f: for line in f.readlines(): if _skip_line(line): continue l = line.strip().split(":") if len(l) == 2: self.parameters[l[0].strip()] = l[1].strip() l = line.strip().split("=") if len(l) == 2: self.parameters[l[0].strip()] = l[1].strip() # set the periodicity based on the integer BC runtime parameters # https://amrex-codes.github.io/amrex/docs_html/InputsProblemDefinition.html periodicity = [False, False, False] try: is_periodic = self.parameters["geometry.is_periodic"].split() periodicity[: len(is_periodic)] = [p == "1" for p in is_periodic] except KeyError: pass self._periodicity = tuple(periodicity) particle_types = glob.glob(os.path.join(self.output_dir, "*", "Header")) particle_types = [cpt.split(os.sep)[-2] for cpt in particle_types] if len(particle_types) > 0: self.parameters["particles"] = 1 self.particle_types = tuple(particle_types) self.particle_types_raw = self.particle_types else: self.particle_types = () self.particle_types_raw = () def _set_code_unit_attributes(self): setdefaultattr(self, "length_unit", self.quan(1.0, "m")) setdefaultattr(self, "mass_unit", self.quan(1.0, "kg")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "m/s")) setdefaultattr(self, "magnetic_unit", self.quan(1.0, "T")) yt-project-yt-f043ac8/yt/frontends/amrex/definitions.py000066400000000000000000000000001510711153200232770ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrex/fields.py000066400000000000000000000523001510711153200222440ustar00rootroot00000000000000import re from typing import TypeAlias import numpy as np from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.units import YTQuantity from yt.utilities.physical_constants import amu_cgs, boltzmann_constant_cgs, c rho_units = "code_mass / code_length**3" mom_units = "code_mass / (code_time * code_length**2)" eden_units = "code_mass / (code_time**2 * code_length)" # erg / cm^3 def _thermal_energy_density(field, data): # What we've got here is UEINT: # u here is velocity # E is energy density from the file # rho e = rho E - rho * u * u / 2 ke = ( 0.5 * ( data["gas", "momentum_density_x"] ** 2 + data["gas", "momentum_density_y"] ** 2 + data["gas", "momentum_density_z"] ** 2 ) / data["gas", "density"] ) return data["boxlib", "eden"] - ke def _specific_thermal_energy(field, data): # This is little e, so we take thermal_energy_density and divide by density return data["gas", "thermal_energy_density"] / data["gas", "density"] def _temperature(field, data): mu = data.ds.parameters["mu"] gamma = data.ds.parameters["gamma"] tr = data["gas", "thermal_energy_density"] / data["gas", "density"] tr *= mu * amu_cgs / boltzmann_constant_cgs tr *= gamma - 1.0 return tr class WarpXFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("Bx", ("T", ["magnetic_field_x", "B_x"], None)), ("By", ("T", ["magnetic_field_y", "B_y"], None)), ("Bz", ("T", ["magnetic_field_z", "B_z"], None)), ("Ex", ("V/m", ["electric_field_x", "E_x"], None)), ("Ey", ("V/m", ["electric_field_y", "E_y"], None)), ("Ez", ("V/m", ["electric_field_z", "E_z"], None)), ("jx", ("A", ["current_x", "Jx", "J_x"], None)), ("jy", ("A", ["current_y", "Jy", "J_y"], None)), ("jz", ("A", ["current_z", "Jz", "J_z"], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_weight", ("", ["particle_weighting"], None)), ("particle_position_x", ("m", [], None)), ("particle_position_y", ("m", [], None)), ("particle_position_z", ("m", [], None)), ("particle_velocity_x", ("m/s", [], None)), ("particle_velocity_y", ("m/s", [], None)), ("particle_velocity_z", ("m/s", [], None)), ("particle_momentum_x", ("kg*m/s", [], None)), ("particle_momentum_y", ("kg*m/s", [], None)), ("particle_momentum_z", ("kg*m/s", [], None)), ) extra_union_fields = ( ("kg", "particle_mass"), ("C", "particle_charge"), ("", "particle_ones"), ) def __init__(self, ds, field_list): super().__init__(ds, field_list) # setup nodal flag information for field in ds.index.raw_fields: finfo = self.__getitem__(("raw", field)) finfo.nodal_flag = ds.nodal_flags[field] def setup_fluid_fields(self): for field in self.known_other_fields: fname = field[0] self.alias(("mesh", fname), ("boxlib", fname)) def setup_fluid_aliases(self): super().setup_fluid_aliases("mesh") def setup_particle_fields(self, ptype): def get_mass(field, data): species_mass = data.ds.index.parameters[ptype + "_mass"] return data[ptype, "particle_weight"] * YTQuantity(species_mass, "kg") self.add_field( (ptype, "particle_mass"), sampling_type="particle", function=get_mass, units="kg", ) def get_charge(field, data): species_charge = data.ds.index.parameters[ptype + "_charge"] return data[ptype, "particle_weight"] * YTQuantity(species_charge, "C") self.add_field( (ptype, "particle_charge"), sampling_type="particle", function=get_charge, units="C", ) def get_energy(field, data): p2 = ( data[ptype, "particle_momentum_x"] ** 2 + data[ptype, "particle_momentum_y"] ** 2 + data[ptype, "particle_momentum_z"] ** 2 ) return np.sqrt(p2 * c**2 + data[ptype, "particle_mass"] ** 2 * c**4) self.add_field( (ptype, "particle_energy"), sampling_type="particle", function=get_energy, units="J", ) def get_velocity_x(field, data): return ( c**2 * data[ptype, "particle_momentum_x"] / data[ptype, "particle_energy"] ) def get_velocity_y(field, data): return ( c**2 * data[ptype, "particle_momentum_y"] / data[ptype, "particle_energy"] ) def get_velocity_z(field, data): return ( c**2 * data[ptype, "particle_momentum_z"] / data[ptype, "particle_energy"] ) self.add_field( (ptype, "particle_velocity_x"), sampling_type="particle", function=get_velocity_x, units="m/s", ) self.add_field( (ptype, "particle_velocity_y"), sampling_type="particle", function=get_velocity_y, units="m/s", ) self.add_field( (ptype, "particle_velocity_z"), sampling_type="particle", function=get_velocity_z, units="m/s", ) super().setup_particle_fields(ptype) class NyxFieldInfo(FieldInfoContainer): known_particle_fields: KnownFieldsT = ( ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ) class BoxlibFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", (rho_units, ["density"], None)), ("eden", (eden_units, ["total_energy_density"], None)), ("xmom", (mom_units, ["momentum_density_x"], None)), ("ymom", (mom_units, ["momentum_density_y"], None)), ("zmom", (mom_units, ["momentum_density_z"], None)), ("temperature", ("K", ["temperature"], None)), ("Temp", ("K", ["temperature"], None)), ("x_velocity", ("cm/s", ["velocity_x"], None)), ("y_velocity", ("cm/s", ["velocity_y"], None)), ("z_velocity", ("cm/s", ["velocity_z"], None)), ("xvel", ("cm/s", ["velocity_x"], None)), ("yvel", ("cm/s", ["velocity_y"], None)), ("zvel", ("cm/s", ["velocity_z"], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_mass", ("code_mass", [], None)), ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_momentum_x", ("code_mass*code_length/code_time", [], None)), ("particle_momentum_y", ("code_mass*code_length/code_time", [], None)), ("particle_momentum_z", ("code_mass*code_length/code_time", [], None)), # Note that these are *internal* agmomen ("particle_angmomen_x", ("code_length**2/code_time", [], None)), ("particle_angmomen_y", ("code_length**2/code_time", [], None)), ("particle_angmomen_z", ("code_length**2/code_time", [], None)), ("particle_id", ("", ["particle_index"], None)), ("particle_mdot", ("code_mass/code_time", [], None)), # "mlast", # "r", # "mdeut", # "n", # "burnstate", # "luminosity", ) def setup_particle_fields(self, ptype): def _get_vel(axis): def velocity(field, data): return ( data[ptype, f"particle_momentum_{axis}"] / data[ptype, "particle_mass"] ) return velocity for ax in "xyz": self.add_field( (ptype, f"particle_velocity_{ax}"), sampling_type="particle", function=_get_vel(ax), units="code_length/code_time", ) super().setup_particle_fields(ptype) def setup_fluid_fields(self): unit_system = self.ds.unit_system # Now, let's figure out what fields are included. if any(f[1] == "xmom" for f in self.field_list): self.setup_momentum_to_velocity() elif any(f[1] == "xvel" for f in self.field_list): self.setup_velocity_to_momentum() self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) self.add_field( ("gas", "thermal_energy_density"), sampling_type="cell", function=_thermal_energy_density, units=unit_system["pressure"], ) if ("gas", "temperature") not in self.field_aliases: self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) def setup_momentum_to_velocity(self): def _get_vel(axis): def velocity(field, data): return data["boxlib", f"{axis}mom"] / data["boxlib", "density"] return velocity for ax in "xyz": self.add_field( ("gas", f"velocity_{ax}"), sampling_type="cell", function=_get_vel(ax), units=self.ds.unit_system["velocity"], ) def setup_velocity_to_momentum(self): def _get_mom(axis): def momentum(field, data): return data["boxlib", f"{axis}vel"] * data["boxlib", "density"] return momentum for ax in "xyz": self.add_field( ("gas", f"momentum_density_{ax}"), sampling_type="cell", function=_get_mom(ax), units=mom_units, ) class CastroFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", ("g/cm**3", ["density"], r"\rho")), ("xmom", ("g/(cm**2 * s)", ["momentum_density_x"], r"\rho u")), ("ymom", ("g/(cm**2 * s)", ["momentum_density_y"], r"\rho v")), ("zmom", ("g/(cm**2 * s)", ["momentum_density_z"], r"\rho w")), # velocity components are not always present ("x_velocity", ("cm/s", ["velocity_x"], r"u")), ("y_velocity", ("cm/s", ["velocity_y"], r"v")), ("z_velocity", ("cm/s", ["velocity_z"], r"w")), ("rho_E", ("erg/cm**3", ["total_energy_density"], r"\rho E")), # internal energy density (not just thermal) ("rho_e", ("erg/cm**3", [], r"\rho e")), ("Temp", ("K", ["temperature"], r"T")), ("grav_x", ("cm/s**2", [], r"\mathbf{g} \cdot \mathbf{e}_x")), ("grav_y", ("cm/s**2", [], r"\mathbf{g} \cdot \mathbf{e}_y")), ("grav_z", ("cm/s**2", [], r"\mathbf{g} \cdot \mathbf{e}_z")), ("pressure", ("dyne/cm**2", [], r"p")), ( "kineng", ("erg/cm**3", ["kinetic_energy_density"], r"\frac{1}{2}\rho|\mathbf{U}|^2"), ), ("soundspeed", ("cm/s", ["sound_speed"], "Sound Speed")), ("MachNumber", ("", ["mach_number"], "Mach Number")), ("abar", ("", [], r"$\bar{A}$")), ("Ye", ("", [], r"$Y_e$")), ("entropy", ("erg/(g*K)", ["entropy"], r"s")), ("magvort", ("1/s", ["vorticity_magnitude"], r"|\nabla \times \mathbf{U}|")), ("divu", ("1/s", ["velocity_divergence"], r"\nabla \cdot \mathbf{U}")), ("eint_E", ("erg/g", [], r"e(E,U)")), ("eint_e", ("erg/g", [], r"e")), ("magvel", ("cm/s", ["velocity_magnitude"], r"|\mathbf{U}|")), ("radvel", ("cm/s", ["radial_velocity"], r"\mathbf{U} \cdot \mathbf{e}_r")), ("magmom", ("g*cm/s", ["momentum_magnitude"], r"\rho |\mathbf{U}|")), ("maggrav", ("cm/s**2", [], r"|\mathbf{g}|")), ("phiGrav", ("erg/g", [], r"\Phi")), ("enuc", ("erg/(g*s)", [], r"\dot{e}_{\rm{nuc}}")), ("rho_enuc", ("erg/(cm**3*s)", [], r"\rho \dot{e}_{\rm{nuc}}")), ("angular_momentum_x", ("g/(cm*s)", [], r"\ell_x")), ("angular_momentum_y", ("g/(cm*s)", [], r"\ell_y")), ("angular_momentum_z", ("g/(cm*s)", [], r"\ell_z")), ("phiRot", ("erg/g", [], r"\Phi_{\rm{rot}}")), ("rot_x", ("cm/s**2", [], r"\mathbf{f}_{\rm{rot}} \cdot \mathbf{e}_x")), ("rot_y", ("cm/s**2", [], r"\mathbf{f}_{\rm{rot}} \cdot \mathbf{e}_y")), ("rot_z", ("cm/s**2", [], r"\mathbf{f}_{\rm{rot}} \cdot \mathbf{e}_z")), ) known_particle_fields: KnownFieldsT = ( ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ) def setup_fluid_fields(self): # add X's for _, field in self.ds.field_list: if field.startswith("X("): # We have a fraction sub = Substance(field) # Overwrite field to use nicer tex_label display_name self.add_output_field( ("boxlib", field), sampling_type="cell", units="", display_name=rf"X\left({sub.to_tex()}\right)", ) self.alias(("gas", f"{sub}_fraction"), ("boxlib", field), units="") func = _create_density_func(("gas", f"{sub}_fraction")) self.add_field( name=("gas", f"{sub}_density"), sampling_type="cell", function=func, units=self.ds.unit_system["density"], display_name=rf"\rho {sub.to_tex()}", ) class MaestroFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", ("g/cm**3", ["density"], None)), ("x_vel", ("cm/s", ["velocity_x"], r"\tilde{u}")), ("y_vel", ("cm/s", ["velocity_y"], r"\tilde{v}")), ("z_vel", ("cm/s", ["velocity_z"], r"\tilde{w}")), ( "magvel", ( "cm/s", ["velocity_magnitude"], r"|\tilde{\mathbf{U}} + w_0 \mathbf{e}_r|", ), ), ( "radial_velocity", ("cm/s", ["radial_velocity"], r"\mathbf{U}\cdot \mathbf{e}_r"), ), ("circum_velocity", ("cm/s", ["tangential_velocity"], r"U - U\cdot e_r")), ("tfromp", ("K", [], "T(\\rho,p,X)")), ("tfromh", ("K", [], "T(\\rho,h,X)")), ("Machnumber", ("", ["mach_number"], "M")), ("S", ("1/s", [], None)), ("ad_excess", ("", [], r"\nabla - \nabla_\mathrm{ad}")), ("deltaT", ("", [], "[T(\\rho,h,X) - T(\\rho,p,X)]/T(\\rho,h,X)")), ("deltagamma", ("", [], r"\Gamma_1 - \overline{\Gamma_1}")), ("deltap", ("", [], "[p(\\rho,h,X) - p_0] / p_0")), ("divw0", ("1/s", [], r"\nabla \cdot \mathbf{w}_0")), # Specific entropy ("entropy", ("erg/(g*K)", ["entropy"], "s")), ("entropypert", ("", [], r"[s - \overline{s}] / \overline{s}")), ("enucdot", ("erg/(g*s)", [], r"\dot{\epsilon}_{nuc}")), ("Hext", ("erg/(g*s)", [], "H_{ext}")), # Perturbational pressure grad ("gpi_x", ("dyne/cm**3", [], r"\left(\nabla\pi\right)_x")), ("gpi_y", ("dyne/cm**3", [], r"\left(\nabla\pi\right)_y")), ("gpi_z", ("dyne/cm**3", [], r"\left(\nabla\pi\right)_z")), ("h", ("erg/g", [], "h")), ("h0", ("erg/g", [], "h_0")), # Momentum cannot be computed because we need to include base and # full state. ("momentum", ("g*cm/s", ["momentum_magnitude"], r"\rho |\mathbf{U}|")), ("p0", ("erg/cm**3", [], "p_0")), ("p0pluspi", ("erg/cm**3", [], r"p_0 + \pi")), ("pi", ("erg/cm**3", [], r"\pi")), ("pioverp0", ("", [], r"\pi/p_0")), # Base state density ("rho0", ("g/cm**3", [], "\\rho_0")), ("rhoh", ("erg/cm**3", ["enthalpy_density"], "(\\rho h)")), # Base state enthalpy density ("rhoh0", ("erg/cm**3", [], "(\\rho h)_0")), ("rhohpert", ("erg/cm**3", [], "(\\rho h)^\\prime")), ("rhopert", ("g/cm**3", [], "\\rho^\\prime")), ("soundspeed", ("cm/s", ["sound_speed"], None)), ("sponge", ("", [], None)), ("tpert", ("K", [], r"T - \overline{T}")), # Again, base state -- so we can't compute ourselves. ("vort", ("1/s", ["vorticity_magnitude"], r"|\nabla\times\tilde{U}|")), # Base state ("w0_x", ("cm/s", [], "(w_0)_x")), ("w0_y", ("cm/s", [], "(w_0)_y")), ("w0_z", ("cm/s", [], "(w_0)_z")), ) def setup_fluid_fields(self): unit_system = self.ds.unit_system # pick the correct temperature field tfromp = False if "use_tfromp" in self.ds.parameters: # original MAESTRO (F90) code tfromp = self.ds.parameters["use_tfromp"] elif "maestro.use_tfromp" in self.ds.parameters: # new MAESTROeX (C++) code tfromp = self.ds.parameters["maestro.use_tfromp"] if tfromp: self.alias( ("gas", "temperature"), ("boxlib", "tfromp"), units=unit_system["temperature"], ) else: self.alias( ("gas", "temperature"), ("boxlib", "tfromh"), units=unit_system["temperature"], ) # Add X's and omegadots, units of 1/s for _, field in self.ds.field_list: if field.startswith("X("): # We have a mass fraction sub = Substance(field) # Overwrite field to use nicer tex_label display_name self.add_output_field( ("boxlib", field), sampling_type="cell", units="", display_name=rf"X\left({sub.to_tex()}\right)", ) self.alias(("gas", f"{sub}_fraction"), ("boxlib", field), units="") func = _create_density_func(("gas", f"{sub}_fraction")) self.add_field( name=("gas", f"{sub}_density"), sampling_type="cell", function=func, units=unit_system["density"], display_name=rf"\rho {sub.to_tex()}", ) elif field.startswith("omegadot("): sub = Substance(field) display_name = rf"\dot{{\omega}}\left[{sub.to_tex()}\right]" # Overwrite field to use nicer tex_label'ed display_name self.add_output_field( ("boxlib", field), sampling_type="cell", units=unit_system["frequency"], display_name=display_name, ) self.alias( ("gas", f"{sub}_creation_rate"), ("boxlib", field), units=unit_system["frequency"], ) substance_expr_re = re.compile(r"\(([a-zA-Z][a-zA-Z0-9]*)\)") substance_elements_re = re.compile(r"(?P[a-zA-Z]+)(?P\d*)") SubstanceSpec: TypeAlias = list[tuple[str, int]] class Substance: def __init__(self, data: str) -> None: if (m := substance_expr_re.search(data)) is None: raise ValueError(f"{data!r} doesn't match expected regular expression") sub_str = m.group() constituents = substance_elements_re.findall(sub_str) # 0 is used as a sentinel value to mark descriptive names default_value = 1 if len(constituents) > 1 else 0 self._spec: SubstanceSpec = [ (name, int(count or default_value)) for (name, count) in constituents ] def get_spec(self) -> SubstanceSpec: return self._spec.copy() def is_isotope(self) -> bool: return len(self._spec) == 1 and self._spec[0][1] > 0 def is_molecule(self) -> bool: return len(self._spec) != 1 def is_descriptive_name(self) -> bool: return len(self._spec) == 1 and self._spec[0][1] == 0 def __str__(self) -> str: return "".join( f"{element}{count if count > 1 else ''}" for element, count in self._spec ) def _to_tex_isotope(self) -> str: element, count = self._spec[0] return rf"^{{{count}}}{element}" def _to_tex_molecule(self) -> str: return "".join( rf"{element}_{{{count if count>1 else ''}}}" for element, count in self._spec ) def _to_tex_descriptive(self) -> str: return str(self) def to_tex(self) -> str: if self.is_isotope(): return self._to_tex_isotope() elif self.is_molecule(): return self._to_tex_molecule() elif self.is_descriptive_name(): return self._to_tex_descriptive() else: # should only be reachable in case of a regular expression defect raise RuntimeError def _create_density_func(field_name): def _func(field, data): return data[field_name] * data["gas", "density"] return _func yt-project-yt-f043ac8/yt/frontends/amrex/io.py000066400000000000000000000244321510711153200214120ustar00rootroot00000000000000import os from collections import defaultdict import numpy as np from yt.frontends.chombo.io import parse_orion_sinks from yt.funcs import mylog from yt.geometry.selection_routines import GridSelector from yt.utilities.io_handler import BaseIOHandler def _remove_raw(all_fields, raw_fields): centered_fields = set(all_fields) for raw in raw_fields: centered_fields.discard(raw) return list(centered_fields) class IOHandlerBoxlib(BaseIOHandler): _dataset_type = "boxlib_native" def __init__(self, ds, *args, **kwargs): super().__init__(ds) def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) if any((not (ftype == "boxlib" or ftype == "raw") for ftype, fname in fields)): raise NotImplementedError rv = {} raw_fields = [] for field in fields: if field[0] == "raw": nodal_flag = self.ds.nodal_flags[field[1]] num_nodes = 2 ** sum(nodal_flag) rv[field] = np.empty((size, num_nodes), dtype="float64") raw_fields.append(field) else: rv[field] = np.empty(size, dtype="float64") centered_fields = _remove_raw(fields, raw_fields) ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) ind = 0 for chunk in chunks: data = self._read_chunk_data(chunk, centered_fields) for g in chunk.objs: for field in fields: if field in centered_fields: ds = data[g.id].pop(field) else: ds = self._read_raw_field(g, field) nd = g.select(selector, ds, rv[field], ind) ind += nd data.pop(g.id) return rv def _read_raw_field(self, grid, field): field_name = field[1] base_dir = self.ds.index.raw_file nghost = self.ds.index.raw_field_nghost[field_name] box_list = self.ds.index.raw_field_map[field_name][0] fn_list = self.ds.index.raw_field_map[field_name][1] offset_list = self.ds.index.raw_field_map[field_name][2] lev = grid.Level filename = os.path.join(base_dir, f"Level_{lev}", fn_list[grid.id]) offset = offset_list[grid.id] box = box_list[grid.id] lo = box[0] - nghost hi = box[1] + nghost shape = hi - lo + 1 with open(filename, "rb") as f: f.seek(offset) f.readline() # always skip the first line arr = np.fromfile(f, "float64", np.prod(shape)) arr = arr.reshape(shape, order="F") return arr[ tuple( slice(None) if (nghost[dim] == 0) else slice(nghost[dim], -nghost[dim]) for dim in range(self.ds.dimensionality) ) ] def _read_chunk_data(self, chunk, fields): data = {} grids_by_file = defaultdict(list) if len(chunk.objs) == 0: return data for g in chunk.objs: if g.filename is None: continue grids_by_file[g.filename].append(g) dtype = self.ds.index._dtype bpr = dtype.itemsize for filename in grids_by_file: grids = grids_by_file[filename] grids.sort(key=lambda a: a._offset) f = open(filename, "rb") for grid in grids: data[grid.id] = {} local_offset = grid._get_offset(f) - f.tell() count = grid.ActiveDimensions.prod() size = count * bpr for field in self.ds.index.field_order: if field in fields: # We read it ... f.seek(local_offset, os.SEEK_CUR) v = np.fromfile(f, dtype=dtype, count=count) v = v.reshape(grid.ActiveDimensions, order="F") data[grid.id][field] = v local_offset = 0 else: local_offset += size f.close() return data def _read_particle_coords(self, chunks, ptf): yield from ( (ptype, xyz, 0.0) for ptype, xyz in self._read_particle_fields(chunks, ptf, None) ) def _read_particle_fields(self, chunks, ptf, selector): for chunk in chunks: # These should be organized by grid filename for g in chunk.objs: for ptype, field_list in sorted(ptf.items()): npart = g._pdata[ptype]["NumberOfParticles"] if npart == 0: continue fn = g._pdata[ptype]["particle_filename"] offset = g._pdata[ptype]["offset"] pheader = self.ds.index.particle_headers[ptype] with open(fn, "rb") as f: # read in the position fields for selection f.seek(offset + pheader.particle_int_dtype.itemsize * npart) rdata = np.fromfile( f, pheader.real_type, pheader.num_real * npart ) # Allow reading particles in 1, 2, and 3 dimensions, # setting the appropriate default for unused dimensions. pos = [] for idim in [1, 2, 3]: if g.ds.dimensionality >= idim: pos.append( np.asarray( rdata[idim - 1 :: pheader.num_real], dtype=np.float64, ) ) else: center = 0.5 * ( g.LeftEdge[idim - 1] + g.RightEdge[idim - 1] ) pos.append(np.full(npart, center, dtype=np.float64)) x, y, z = pos if selector is None: # This only ever happens if the call is made from # _read_particle_coords. yield ptype, (x, y, z) continue mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: # handle the case that this is an integer field int_fnames = [ fname for _, fname in pheader.known_int_fields ] if field in int_fnames: ind = int_fnames.index(field) f.seek(offset) idata = np.fromfile( f, pheader.int_type, pheader.num_int * npart ) data = np.asarray( idata[ind :: pheader.num_int], dtype=np.float64 ) yield (ptype, field), data[mask].flatten() # handle case that this is a real field real_fnames = [ fname for _, fname in pheader.known_real_fields ] if field in real_fnames: ind = real_fnames.index(field) data = np.asarray( rdata[ind :: pheader.num_real], dtype=np.float64 ) yield (ptype, field), data[mask].flatten() class IOHandlerOrion(IOHandlerBoxlib): _dataset_type = "orion_native" _particle_filename = None @property def particle_filename(self): fn = os.path.join(self.ds.output_dir, "StarParticles") if not os.path.exists(fn): fn = os.path.join(self.ds.output_dir, "SinkParticles") self._particle_filename = fn return self._particle_filename _particle_field_index = None @property def particle_field_index(self): index = parse_orion_sinks(self.particle_filename) self._particle_field_index = index return self._particle_field_index def _read_particle_selection(self, chunks, selector, fields): rv = {} chunks = list(chunks) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError grid = chunks[0].objs[0] for ftype, fname in fields: rv[ftype, fname] = self._read_particles(grid, fname) return rv rv = {f: np.array([]) for f in fields} for chunk in chunks: for grid in chunk.objs: for ftype, fname in fields: data = self._read_particles(grid, fname) rv[ftype, fname] = np.concatenate((data, rv[ftype, fname])) return rv def _read_particles(self, grid, field): """ parses the Orion Star Particle text files """ particles = [] if grid.NumberOfParticles == 0: return np.array(particles) def read(line, field): entry = line.strip().split(" ")[self.particle_field_index[field]] return float(entry) try: lines = self._cached_lines for num in grid._particle_line_numbers: line = lines[num] particles.append(read(line, field)) return np.array(particles) except AttributeError: fn = self.particle_filename with open(fn) as f: lines = f.readlines() self._cached_lines = lines for num in grid._particle_line_numbers: line = lines[num] particles.append(read(line, field)) return np.array(particles) yt-project-yt-f043ac8/yt/frontends/amrex/misc.py000066400000000000000000000000001510711153200217170ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrex/tests/000077500000000000000000000000001510711153200215665ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrex/tests/__init__.py000066400000000000000000000000001510711153200236650ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrex/tests/test_field_parsing.py000066400000000000000000000035301510711153200260060ustar00rootroot00000000000000import pytest from yt.frontends.amrex.fields import Substance @pytest.mark.parametrize( "data, expected", [ pytest.param("X(He5)", [("He", 5)], id="isotope_1"), pytest.param("X(C12)", [("C", 12)], id="isotope_2"), pytest.param("X(A1B2C3)", [("A", 1), ("B", 2), ("C", 3)], id="molecule_1"), pytest.param("X(C12H24)", [("C", 12), ("H", 24)], id="molecule_2"), pytest.param("X(H2O)", [("H", 2), ("O", 1)], id="molecule_3"), pytest.param("X(ash)", [("ash", 0)], id="descriptive_name"), ], ) def test_Substance_spec(data, expected): assert Substance(data)._spec == expected @pytest.mark.parametrize( "data, expected_type", [ pytest.param("X(He5)", "isotope", id="isotope_1"), pytest.param("X(C12)", "isotope", id="isotope_2"), pytest.param("X(A1B2C3)", "molecule", id="molecule_1"), pytest.param("X(C12H24)", "molecule", id="molecule_2"), pytest.param("X(H2O)", "molecule", id="molecule_3"), pytest.param("X(ash)", "descriptive_name", id="descriptive_name"), ], ) def test_Substance_type(data, expected_type): sub = Substance(data) assert getattr(sub, f"is_{expected_type}")() @pytest.mark.parametrize( "data, expected_str, expected_tex", [ pytest.param("X(He5)", "He5", "^{5}He", id="isotope_1"), pytest.param("X(C12)", "C12", "^{12}C", id="isotope_2"), pytest.param("X(A1B2C3)", "AB2C3", "A_{}B_{2}C_{3}", id="molecule_1"), pytest.param("X(C12H24)", "C12H24", "C_{12}H_{24}", id="molecule_2"), pytest.param("X(H2O)", "H2O", "H_{2}O_{}", id="molecule_2"), pytest.param("X(ash)", "ash", "ash", id="descriptive_name"), ], ) def test_Substance_to_str(data, expected_str, expected_tex): sub = Substance(data) assert str(sub) == expected_str assert sub.to_tex() == expected_tex yt-project-yt-f043ac8/yt/frontends/amrex/tests/test_outputs.py000066400000000000000000000336521510711153200247330ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_allclose, assert_equal from yt.frontends.amrex.api import ( AMReXDataset, CastroDataset, MaestroDataset, NyxDataset, OrionDataset, WarpXDataset, ) from yt.loaders import load from yt.testing import ( disable_dataset_cache, requires_file, units_override_check, ) from yt.utilities.answer_testing.framework import ( GridValuesTest, data_dir_load, requires_ds, small_patch_amr, ) # We don't do anything needing ghost zone generation right now, because these # are non-periodic datasets. _orion_fields = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ) _nyx_fields = ( ("boxlib", "Ne"), ("boxlib", "Temp"), ("boxlib", "particle_mass_density"), ) _warpx_fields = (("mesh", "Ex"), ("mesh", "By"), ("mesh", "jz")) _castro_fields = ( ("boxlib", "Temp"), ("gas", "density"), ("boxlib", "particle_count"), ) radadvect = "RadAdvect/plt00000" @requires_ds(radadvect) def test_radadvect(): ds = data_dir_load(radadvect) assert_equal(str(ds), "plt00000") for test in small_patch_amr(ds, _orion_fields): test_radadvect.__name__ = test.description yield test rt = "RadTube/plt00500" @requires_ds(rt) def test_radtube(): ds = data_dir_load(rt) assert_equal(str(ds), "plt00500") for test in small_patch_amr(ds, _orion_fields): test_radtube.__name__ = test.description yield test star = "StarParticles/plrd01000" @requires_ds(star) def test_star(): ds = data_dir_load(star) assert_equal(str(ds), "plrd01000") for test in small_patch_amr(ds, _orion_fields): test_star.__name__ = test.description yield test LyA = "Nyx_LyA/plt00000" @requires_ds(LyA) def test_LyA(): ds = data_dir_load(LyA) assert_equal(str(ds), "plt00000") for test in small_patch_amr( ds, _nyx_fields, input_center="c", input_weight=("boxlib", "Ne") ): test_LyA.__name__ = test.description yield test @requires_file(LyA) def test_nyx_particle_io(): ds = data_dir_load(LyA) grid = ds.index.grids[0] npart_grid_0 = 7908 # read directly from the header assert_equal(grid[("all", "particle_position_x")].size, npart_grid_0) assert_equal(grid["DM", "particle_position_y"].size, npart_grid_0) assert_equal(grid["all", "particle_position_z"].size, npart_grid_0) ad = ds.all_data() npart = 32768 # read directly from the header assert_equal(ad[("all", "particle_velocity_x")].size, npart) assert_equal(ad["DM", "particle_velocity_y"].size, npart) assert_equal(ad["all", "particle_velocity_z"].size, npart) assert np.all(ad[("all", "particle_mass")] == ad[("all", "particle_mass")][0]) left_edge = ds.arr([0.0, 0.0, 0.0], "code_length") right_edge = ds.arr([4.0, 4.0, 4.0], "code_length") center = 0.5 * (left_edge + right_edge) reg = ds.region(center, left_edge, right_edge) assert np.all( np.logical_and( reg[("all", "particle_position_x")] <= right_edge[0], reg[("all", "particle_position_x")] >= left_edge[0], ) ) assert np.all( np.logical_and( reg[("all", "particle_position_y")] <= right_edge[1], reg[("all", "particle_position_y")] >= left_edge[1], ) ) assert np.all( np.logical_and( reg[("all", "particle_position_z")] <= right_edge[2], reg[("all", "particle_position_z")] >= left_edge[2], ) ) RT_particles = "RT_particles/plt00050" @requires_ds(RT_particles) def test_RT_particles(): ds = data_dir_load(RT_particles) assert_equal(str(ds), "plt00050") for test in small_patch_amr(ds, _castro_fields): test_RT_particles.__name__ = test.description yield test @requires_file(RT_particles) def test_castro_particle_io(): ds = data_dir_load(RT_particles) grid = ds.index.grids[2] npart_grid_2 = 49 # read directly from the header assert_equal(grid[("all", "particle_position_x")].size, npart_grid_2) assert_equal(grid["Tracer", "particle_position_y"].size, npart_grid_2) assert_equal(grid["all", "particle_position_y"].size, npart_grid_2) ad = ds.all_data() npart = 49 # read directly from the header assert_equal(ad[("all", "particle_velocity_x")].size, npart) assert_equal(ad["Tracer", "particle_velocity_y"].size, npart) assert_equal(ad["all", "particle_velocity_y"].size, npart) left_edge = ds.arr([0.0, 0.0, 0.0], "code_length") right_edge = ds.arr([0.25, 1.0, 1.0], "code_length") center = 0.5 * (left_edge + right_edge) reg = ds.region(center, left_edge, right_edge) assert np.all( np.logical_and( reg[("all", "particle_position_x")] <= right_edge[0], reg[("all", "particle_position_x")] >= left_edge[0], ) ) assert np.all( np.logical_and( reg[("all", "particle_position_y")] <= right_edge[1], reg[("all", "particle_position_y")] >= left_edge[1], ) ) langmuir = "LangmuirWave/plt00020_v2" @requires_ds(langmuir) def test_langmuir(): ds = data_dir_load(langmuir) assert_equal(str(ds), "plt00020_v2") for test in small_patch_amr( ds, _warpx_fields, input_center="c", input_weight=("mesh", "Ex") ): test_langmuir.__name__ = test.description yield test plasma = "PlasmaAcceleration/plt00030_v2" @requires_ds(plasma) def test_plasma(): ds = data_dir_load(plasma) assert_equal(str(ds), "plt00030_v2") for test in small_patch_amr( ds, _warpx_fields, input_center="c", input_weight=("mesh", "Ex") ): test_plasma.__name__ = test.description yield test beam = "GaussianBeam/plt03008" @requires_ds(beam) def test_beam(): ds = data_dir_load(beam) assert_equal(str(ds), "plt03008") for param in ("number of boxes", "maximum zones"): # PR 2807 # these parameters are only populated if the config file attached to this # dataset is read correctly assert param in ds.parameters for test in small_patch_amr( ds, _warpx_fields, input_center="c", input_weight=("mesh", "Ex") ): test_beam.__name__ = test.description yield test @requires_file(plasma) def test_warpx_particle_io(): ds = data_dir_load(plasma) grid = ds.index.grids[0] # read directly from the header npart0_grid_0 = 344 npart1_grid_0 = 69632 assert_equal(grid["particle0", "particle_position_x"].size, npart0_grid_0) assert_equal(grid["particle1", "particle_position_y"].size, npart1_grid_0) assert_equal(grid["all", "particle_position_z"].size, npart0_grid_0 + npart1_grid_0) # read directly from the header npart0 = 1360 npart1 = 802816 ad = ds.all_data() assert_equal(ad["particle0", "particle_velocity_x"].size, npart0) assert_equal(ad["particle1", "particle_velocity_y"].size, npart1) assert_equal(ad["all", "particle_velocity_z"].size, npart0 + npart1) np.all(ad["particle1", "particle_mass"] == ad["particle1", "particle_mass"][0]) np.all(ad["particle0", "particle_mass"] == ad["particle0", "particle_mass"][0]) left_edge = ds.arr([-7.5e-5, -7.5e-5, -7.5e-5], "code_length") right_edge = ds.arr([2.5e-5, 2.5e-5, 2.5e-5], "code_length") center = 0.5 * (left_edge + right_edge) reg = ds.region(center, left_edge, right_edge) assert np.all( np.logical_and( reg[("all", "particle_position_x")] <= right_edge[0], reg[("all", "particle_position_x")] >= left_edge[0], ) ) assert np.all( np.logical_and( reg[("all", "particle_position_y")] <= right_edge[1], reg[("all", "particle_position_y")] >= left_edge[1], ) ) assert np.all( np.logical_and( reg[("all", "particle_position_z")] <= right_edge[2], reg[("all", "particle_position_z")] >= left_edge[2], ) ) _raw_fields = [("raw", "Bx"), ("raw", "Ey"), ("raw", "jz")] laser = "Laser/plt00015" @requires_ds(laser) def test_raw_fields(): for field in _raw_fields: yield GridValuesTest(laser, field) @requires_file(rt) def test_OrionDataset(): assert isinstance(data_dir_load(rt), OrionDataset) @requires_file(LyA) def test_NyxDataset(): assert isinstance(data_dir_load(LyA), NyxDataset) @requires_file("nyx_small/nyx_small_00000") def test_NyxDataset_2(): assert isinstance(data_dir_load("nyx_small/nyx_small_00000"), NyxDataset) @requires_file(RT_particles) def test_CastroDataset(): assert isinstance(data_dir_load(RT_particles), CastroDataset) @requires_file("castro_sod_x_plt00036") def test_CastroDataset_2(): assert isinstance(data_dir_load("castro_sod_x_plt00036"), CastroDataset) @requires_file("castro_sedov_1d_cyl_plt00150") def test_CastroDataset_3(): assert isinstance(data_dir_load("castro_sedov_1d_cyl_plt00150"), CastroDataset) @requires_file(plasma) def test_WarpXDataset(): assert isinstance(data_dir_load(plasma), WarpXDataset) @disable_dataset_cache @requires_file(plasma) def test_magnetic_units(): ds1 = load(plasma) assert_allclose(ds1.magnetic_unit.value, 1.0) assert str(ds1.magnetic_unit.units) == "T" mag_unit1 = ds1.magnetic_unit.to("code_magnetic") assert_allclose(mag_unit1.value, 1.0) assert str(mag_unit1.units) == "code_magnetic" ds2 = load(plasma, unit_system="cgs") assert_allclose(ds2.magnetic_unit.value, 1.0e4) assert str(ds2.magnetic_unit.units) == "G" mag_unit2 = ds2.magnetic_unit.to("code_magnetic") assert_allclose(mag_unit2.value, 1.0) assert str(mag_unit2.units) == "code_magnetic" @requires_ds(laser) def test_WarpXDataset_2(): assert isinstance(data_dir_load(laser), WarpXDataset) @requires_file("plt.Cavity00010") def test_AMReXDataset(): ds = data_dir_load("plt.Cavity00010", kwargs={"cparam_filename": "inputs"}) assert isinstance(ds, AMReXDataset) @requires_file(rt) def test_units_override(): units_override_check(rt) nyx_no_particles = "nyx_sedov_plt00086" @requires_file(nyx_no_particles) def test_nyx_no_part(): assert isinstance(data_dir_load(nyx_no_particles), NyxDataset) fields = sorted( [ ("boxlib", "H"), ("boxlib", "He"), ("boxlib", "MachNumber"), ("boxlib", "Ne"), ("boxlib", "Rank"), ("boxlib", "StateErr"), ("boxlib", "Temp"), ("boxlib", "X(H)"), ("boxlib", "X(He)"), ("boxlib", "density"), ("boxlib", "divu"), ("boxlib", "eint_E"), ("boxlib", "eint_e"), ("boxlib", "entropy"), ("boxlib", "forcex"), ("boxlib", "forcey"), ("boxlib", "forcez"), ("boxlib", "kineng"), ("boxlib", "logden"), ("boxlib", "magmom"), ("boxlib", "magvel"), ("boxlib", "magvort"), ("boxlib", "pressure"), ("boxlib", "rho_E"), ("boxlib", "rho_H"), ("boxlib", "rho_He"), ("boxlib", "rho_e"), ("boxlib", "soundspeed"), ("boxlib", "x_velocity"), ("boxlib", "xmom"), ("boxlib", "y_velocity"), ("boxlib", "ymom"), ("boxlib", "z_velocity"), ("boxlib", "zmom"), ] ) ds = data_dir_load(nyx_no_particles) assert_equal(sorted(ds.field_list), fields) msubch = "maestro_subCh_plt00248" @requires_file(msubch) def test_maestro_parameters(): assert isinstance(data_dir_load(msubch), MaestroDataset) ds = data_dir_load(msubch) # Check a string parameter assert ds.parameters["plot_base_name"] == "subCh_hot_baserun_plt" assert type(ds.parameters["plot_base_name"]) is str # noqa: E721 # Check boolean parameters: T or F assert not ds.parameters["use_thermal_diffusion"] assert type(ds.parameters["use_thermal_diffusion"]) is bool # noqa: E721 assert ds.parameters["do_burning"] assert type(ds.parameters["do_burning"]) is bool # noqa: E721 # Check a float parameter with a decimal point assert ds.parameters["sponge_kappa"] == float("10.00000000") assert type(ds.parameters["sponge_kappa"]) is float # noqa: E721 # Check a float parameter with E exponent notation assert ds.parameters["small_dt"] == float("0.1000000000E-09") # Check an int parameter assert ds.parameters["s0_interp_type"] == 3 assert type(ds.parameters["s0_interp_type"]) is int # noqa: E721 # test loading non-Cartesian coordinate systems in different dimensionalities def check_coordsys_data(ds): # check that level_dds is consistent with domain_width assert_allclose( ds.index.level_dds[0] * ds.domain_dimensions, ds.domain_width.to_value("code_length"), rtol=1e-12, atol=0.0, ) # check that we get the expected number of data points when selecting the # entire domain expected_size = sum(np.count_nonzero(g.child_mask) for g in ds.index.grids) ad = ds.all_data() assert ad["boxlib", "Temp"].size == expected_size cyl_1d = "castro_sedov_1d_cyl_plt00150" cyl_2d = "castro_sedov_2d_sph_in_cyl_plt00130" sph_1d = "sedov_1d_sph_plt00120" sph_2d = "xrb_spherical_smallplt00010" @requires_file(cyl_1d) def test_coordsys_1d_cylindrical(): ds = data_dir_load(cyl_1d) assert ds.geometry == "cylindrical" assert ds.dimensionality == 1 check_coordsys_data(ds) @requires_file(cyl_2d) def test_coordsys_2d_cylindrical(): ds = data_dir_load(cyl_2d) assert ds.geometry == "cylindrical" assert ds.dimensionality == 2 check_coordsys_data(ds) @requires_file(sph_1d) def test_coordsys_1d_spherical(): ds = data_dir_load(sph_1d) assert ds.geometry == "spherical" assert ds.dimensionality == 1 check_coordsys_data(ds) @requires_file(sph_2d) def test_coordsys_2d_spherical(): ds = data_dir_load(sph_2d) assert ds.geometry == "spherical" assert ds.dimensionality == 2 check_coordsys_data(ds) yt-project-yt-f043ac8/yt/frontends/amrvac/000077500000000000000000000000001510711153200205615ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrvac/__init__.py000066400000000000000000000000001510711153200226600ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrvac/api.py000066400000000000000000000003571510711153200217110ustar00rootroot00000000000000""" frontend API: a submodule that exposes user-facing defs and classes """ from .data_structures import AMRVACDataset, AMRVACGrid, AMRVACHierarchy from .fields import AMRVACFieldInfo from .io import AMRVACIOHandler, read_amrvac_namelist yt-project-yt-f043ac8/yt/frontends/amrvac/data_structures.py000066400000000000000000000455001510711153200243530ustar00rootroot00000000000000""" AMRVAC data structures """ import os import struct import warnings import weakref from pathlib import Path import numpy as np from more_itertools import always_iterable from yt.config import ytcfg from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.physical_constants import boltzmann_constant_cgs as kb_cgs from .datfile_utils import get_header, get_tree_info from .fields import AMRVACFieldInfo from .io import read_amrvac_namelist def _parse_geometry(geometry_tag: str) -> Geometry: """Translate AMRVAC's geometry tag to yt's format. Parameters ---------- geometry_tag : str A geometry tag as read from AMRVAC's datfile from v5. Returns ------- geometry_yt : Geometry An enum member of the yt.geometry.geometry_enum.Geometry class Examples -------- >>> _parse_geometry("Polar_2.5D") >>> _parse_geometry("Cartesian_2.5D") """ geometry_str, _, _dimension_str = geometry_tag.partition("_") return Geometry(geometry_str.lower()) class AMRVACGrid(AMRGridPatch): """A class to populate AMRVACHierarchy.grids, setting parent/children relations.""" _id_offset = 0 def __init__(self, id, index, level): # should use yt's convention (start from 0) super().__init__(id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level def get_global_startindex(self): """Refresh and retrieve the starting index for each dimension at current level. Returns ------- self.start_index : int """ start_index = (self.LeftEdge - self.ds.domain_left_edge) / self.dds self.start_index = np.rint(start_index).astype("int64").ravel() return self.start_index def retrieve_ghost_zones(self, n_zones, fields, all_levels=False, smoothed=False): if smoothed: warnings.warn( "ghost-zones interpolation/smoothing is not " "currently supported for AMRVAC data.", category=RuntimeWarning, stacklevel=2, ) smoothed = False return super().retrieve_ghost_zones( n_zones, fields, all_levels=all_levels, smoothed=smoothed ) class AMRVACHierarchy(GridIndex): grid = AMRVACGrid def __init__(self, ds, dataset_type="amrvac"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) # the index file *is* the datfile self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self.float_type = np.float64 super().__init__(ds, dataset_type) def _detect_output_fields(self): """Parse field names from the header, as stored in self.dataset.parameters""" self.field_list = [ (self.dataset_type, f) for f in self.dataset.parameters["w_names"] ] def _count_grids(self): """Set self.num_grids from datfile header.""" self.num_grids = self.dataset.parameters["nleafs"] def _parse_index(self): """Populate self.grid_* attributes from tree info from datfile header.""" with open(self.index_filename, "rb") as istream: vaclevels, morton_indices, block_offsets = get_tree_info(istream) assert ( len(vaclevels) == len(morton_indices) == len(block_offsets) == self.num_grids ) self.block_offsets = block_offsets # YT uses 0-based grid indexing: # lowest level = 0, while AMRVAC uses 1 for lowest level ytlevels = np.array(vaclevels, dtype="int32") - 1 self.grid_levels.flat[:] = ytlevels self.min_level = np.min(ytlevels) self.max_level = np.max(ytlevels) assert self.max_level == self.dataset.parameters["levmax"] - 1 # some aliases for left/right edges computation in the coming loop domain_width = self.dataset.parameters["xmax"] - self.dataset.parameters["xmin"] block_nx = self.dataset.parameters["block_nx"] xmin = self.dataset.parameters["xmin"] dx0 = ( domain_width / self.dataset.parameters["domain_nx"] ) # dx at coarsest grid level (YT level 0) dim = self.dataset.dimensionality self.grids = np.empty(self.num_grids, dtype="object") for igrid, (ytlevel, morton_index) in enumerate( zip(ytlevels, morton_indices, strict=True) ): dx = dx0 / self.dataset.refine_by**ytlevel left_edge = xmin + (morton_index - 1) * block_nx * dx # edges and dimensions are filled in a dimensionality-agnostic way self.grid_left_edge[igrid, :dim] = left_edge self.grid_right_edge[igrid, :dim] = left_edge + block_nx * dx self.grid_dimensions[igrid, :dim] = block_nx self.grids[igrid] = self.grid(igrid, self, ytlevels[igrid]) def _populate_grid_objects(self): # required method for g in self.grids: g._prepare_grid() g._setup_dx() class AMRVACDataset(Dataset): _index_class = AMRVACHierarchy _field_info_class = AMRVACFieldInfo def __init__( self, filename, dataset_type="amrvac", units_override=None, unit_system="cgs", geometry_override=None, parfiles=None, default_species_fields=None, ): """Instantiate AMRVACDataset. Parameters ---------- filename : str Path to a datfile. dataset_type : str, optional This should always be 'amrvac'. units_override : dict, optional A dictionary of physical normalisation factors to interpret on disk data. unit_system : str, optional Either "cgs" (default), "mks" or "code" geometry_override : str, optional A geometry flag formatted either according to either AMRVAC or yt standards. When this parameter is passed along with v5 or more newer datfiles, will precede over their internal "geometry" tag. parfiles : str or list, optional One or more parfiles to be passed to yt.frontends.amrvac.read_amrvac_parfiles() """ # note: geometry_override and parfiles are specific to this frontend self._geometry_override = geometry_override self._parfiles = [] if parfiles is not None: parfiles = list(always_iterable(parfiles)) ppf = Path(parfiles[0]) if not ppf.is_absolute() and Path(filename).resolve().is_relative_to( ytcfg["yt", "test_data_dir"] ): mylog.debug( "Looks like %s is relative to your test_data_dir. Assuming this is also true for parfiles.", filename, ) parfiles = [Path(ytcfg["yt", "test_data_dir"]) / pf for pf in parfiles] self._parfiles = parfiles super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) namelist = None namelist_gamma = None c_adiab = None e_is_internal = None if parfiles is not None: namelist = read_amrvac_namelist(parfiles) if "hd_list" in namelist: c_adiab = namelist["hd_list"].get("hd_adiab", 1.0) namelist_gamma = namelist["hd_list"].get("hd_gamma") elif "mhd_list" in namelist: c_adiab = namelist["mhd_list"].get("mhd_adiab", 1.0) namelist_gamma = namelist["mhd_list"].get("mhd_gamma") if namelist_gamma is not None and self.gamma != namelist_gamma: mylog.error( "Inconsistent values in gamma: datfile %s, parfiles %s", self.gamma, namelist_gamma, ) if "method_list" in namelist: e_is_internal = namelist["method_list"].get("solve_internal_e", False) if c_adiab is not None: # this complicated unit is required for the adiabatic equation # of state to make physical sense c_adiab *= ( self.mass_unit ** (1 - self.gamma) * self.length_unit ** (2 + 3 * (self.gamma - 1)) / self.time_unit**2 ) self.namelist = namelist self._c_adiab = c_adiab self._e_is_internal = e_is_internal self.fluid_types += ("amrvac",) # refinement factor between a grid and its subgrid self.refine_by = 2 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: """At load time, check whether data is recognized as AMRVAC formatted.""" validation = False if filename.endswith(".dat"): try: with open(filename, mode="rb") as istream: fmt = "=i" [datfile_version] = struct.unpack( fmt, istream.read(struct.calcsize(fmt)) ) if 3 <= datfile_version < 6: fmt = "=ii" offset_tree, offset_blocks = struct.unpack( fmt, istream.read(struct.calcsize(fmt)) ) istream.seek(0, 2) file_size = istream.tell() validation = ( offset_tree < file_size and offset_blocks < file_size ) except Exception: pass return validation def _parse_parameter_file(self): """Parse input datfile's header. Apply geometry_override if specified.""" # required method # populate self.parameters with header data with open(self.parameter_filename, "rb") as istream: self.parameters.update(get_header(istream)) self.current_time = self.parameters["time"] self.dimensionality = self.parameters["ndim"] # force 3D for this definition dd = np.ones(3, dtype="int64") dd[: self.dimensionality] = self.parameters["domain_nx"] self.domain_dimensions = dd if self.parameters.get("staggered", False): mylog.warning( "'staggered' flag was found, but is currently ignored (unsupported)" ) # parse geometry # by order of decreasing priority, we use # - geometry_override # - "geometry" parameter from datfile # - if all fails, default to "cartesian" self.geometry = Geometry.CARTESIAN amrvac_geom = self.parameters.get("geometry", None) if amrvac_geom is not None: self.geometry = _parse_geometry(amrvac_geom) elif self.parameters["datfile_version"] > 4: mylog.error( "No 'geometry' flag found in datfile with version %d >4.", self.parameters["datfile_version"], ) if self._geometry_override is not None: try: new_geometry = _parse_geometry(self._geometry_override) if new_geometry == self.geometry: mylog.info("geometry_override is identical to datfile parameter.") else: self.geometry = new_geometry mylog.warning( "Overriding geometry, this may lead to surprising results." ) except ValueError: mylog.error( "Unable to parse geometry_override '%s' (will be ignored).", self._geometry_override, ) # parse peridiocity periodicity = self.parameters.get("periodic", ()) missing_dim = 3 - len(periodicity) self._periodicity = (*periodicity, *(missing_dim * (False,))) self.gamma = self.parameters.get("gamma", 5.0 / 3.0) # parse domain edges dle = np.zeros(3) dre = np.ones(3) dle[: self.dimensionality] = self.parameters["xmin"] dre[: self.dimensionality] = self.parameters["xmax"] self.domain_left_edge = dle self.domain_right_edge = dre # defaulting to non-cosmological self.cosmological_simulation = 0 self.current_redshift = 0.0 self.omega_matter = 0.0 self.omega_lambda = 0.0 self.hubble_constant = 0.0 # units stuff ====================================================================== def _set_code_unit_attributes(self): """Reproduce how AMRVAC internally set up physical normalisation factors.""" # This gets called later than Dataset._override_code_units() # This is the reason why it uses setdefaultattr: it will only fill in the gaps # left by the "override", instead of overriding them again. # note: yt sets hydrogen mass equal to proton mass, amrvac doesn't. mp_cgs = self.quan(1.672621898e-24, "g") # This value is taken from AstroPy # get self.length_unit if overrides are supplied, otherwise use default length_unit = getattr(self, "length_unit", self.quan(1, "cm")) namelist = read_amrvac_namelist(self._parfiles) He_abundance = namelist.get("mhd_list", {}).get("he_abundance", 0.1) # 1. calculations for mass, density, numberdensity if "mass_unit" in self.units_override: # in this case unit_mass is supplied (and has been set as attribute) mass_unit = self.mass_unit density_unit = mass_unit / length_unit**3 nd_unit = density_unit / ((1.0 + 4.0 * He_abundance) * mp_cgs) else: # other case: numberdensity is supplied. # Fall back to one (default) if no overrides supplied try: nd_unit = self.quan(self.units_override["numberdensity_unit"]) except KeyError: nd_unit = self.quan( 1.0, self.__class__.default_units["numberdensity_unit"] ) density_unit = (1.0 + 4.0 * He_abundance) * mp_cgs * nd_unit mass_unit = density_unit * length_unit**3 # 2. calculations for velocity if "time_unit" in self.units_override: # in this case time was supplied velocity_unit = length_unit / self.time_unit else: # other case: velocity was supplied. # Fall back to None if no overrides supplied velocity_unit = getattr(self, "velocity_unit", None) # 3. calculations for pressure and temperature if velocity_unit is None: # velocity and time not given, see if temperature is given. # Fall back to one (default) if not temperature_unit = getattr(self, "temperature_unit", self.quan(1, "K")) pressure_unit = ( (2.0 + 3.0 * He_abundance) * nd_unit * kb_cgs * temperature_unit ).in_cgs() velocity_unit = (np.sqrt(pressure_unit / density_unit)).in_cgs() else: # velocity is not zero if either time was given OR velocity was given pressure_unit = (density_unit * velocity_unit**2).in_cgs() temperature_unit = ( pressure_unit / ((2.0 + 3.0 * He_abundance) * nd_unit * kb_cgs) ).in_cgs() # 4. calculations for magnetic unit and time time_unit = getattr( self, "time_unit", length_unit / velocity_unit ) # if time given use it, else calculate magnetic_unit = (np.sqrt(4 * np.pi * pressure_unit)).to("gauss") setdefaultattr(self, "mass_unit", mass_unit) setdefaultattr(self, "density_unit", density_unit) setdefaultattr(self, "length_unit", length_unit) setdefaultattr(self, "velocity_unit", velocity_unit) setdefaultattr(self, "time_unit", time_unit) setdefaultattr(self, "temperature_unit", temperature_unit) setdefaultattr(self, "pressure_unit", pressure_unit) setdefaultattr(self, "magnetic_unit", magnetic_unit) allowed_unit_combinations = [ {"numberdensity_unit", "temperature_unit", "length_unit"}, {"mass_unit", "temperature_unit", "length_unit"}, {"mass_unit", "time_unit", "length_unit"}, {"numberdensity_unit", "velocity_unit", "length_unit"}, {"mass_unit", "velocity_unit", "length_unit"}, ] default_units = { "length_unit": "cm", "time_unit": "s", "mass_unit": "g", "velocity_unit": "cm/s", "magnetic_unit": "gauss", "temperature_unit": "K", # this is the one difference with Dataset.default_units: # we accept numberdensity_unit as a valid override "numberdensity_unit": "cm**-3", } @classmethod def _validate_units_override_keys(cls, units_override): """Check that keys in units_override are consistent with AMRVAC's internal normalisations factors. """ # YT supports overriding other normalisations, this method ensures consistency # between supplied 'units_override' items and those used by AMRVAC. # AMRVAC's normalisations/units have 3 degrees of freedom. # Moreover, if temperature unit is specified then velocity unit will be # calculated accordingly, and vice-versa. # We replicate this by allowing a finite set of combinations. # there are only three degrees of freedom, so explicitly check for this if len(units_override) > 3: raise ValueError( "More than 3 degrees of freedom were specified " f"in units_override ({len(units_override)} given)" ) # temperature and velocity cannot both be specified if "temperature_unit" in units_override and "velocity_unit" in units_override: raise ValueError( "Either temperature or velocity is allowed in units_override, not both." ) # check if provided overrides are allowed suo = set(units_override) for allowed_combo in cls.allowed_unit_combinations: if suo.issubset(allowed_combo): break else: raise ValueError( f"Combination {suo} passed to units_override " "is not consistent with AMRVAC.\n" f"Allowed combinations are {cls.allowed_unit_combinations}" ) # syntax for mixing super with classmethod is weird... super(cls, cls)._validate_units_override_keys(units_override) yt-project-yt-f043ac8/yt/frontends/amrvac/datfile_utils.py000066400000000000000000000122561510711153200237710ustar00rootroot00000000000000import struct import numpy as np # Size of basic types (in bytes) SIZE_LOGICAL = 4 SIZE_INT = 4 SIZE_DOUBLE = 8 NAME_LEN = 16 # For un-aligned data, use '=' (for aligned data set to '') ALIGN = "=" def get_header(istream): """Read header from an MPI-AMRVAC 2.0 snapshot. istream' should be a file opened in binary mode. """ istream.seek(0) h = {} fmt = ALIGN + "i" [h["datfile_version"]] = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) if h["datfile_version"] < 3: raise OSError("Unsupported AMRVAC .dat file version: %d", h["datfile_version"]) # Read scalar data at beginning of file fmt = ALIGN + 9 * "i" + "d" hdr = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) [ h["offset_tree"], h["offset_blocks"], h["nw"], h["ndir"], h["ndim"], h["levmax"], h["nleafs"], h["nparents"], h["it"], h["time"], ] = hdr # Read min/max coordinates fmt = ALIGN + h["ndim"] * "d" h["xmin"] = np.array(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))) h["xmax"] = np.array(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))) # Read domain and block size (in number of cells) fmt = ALIGN + h["ndim"] * "i" h["domain_nx"] = np.array(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))) h["block_nx"] = np.array(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))) if h["datfile_version"] >= 5: # Read periodicity fmt = ALIGN + h["ndim"] * "i" # Fortran logical is 4 byte int h["periodic"] = np.array( struct.unpack(fmt, istream.read(struct.calcsize(fmt))), dtype=bool ) # Read geometry name fmt = ALIGN + NAME_LEN * "c" hdr = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) h["geometry"] = b"".join(hdr).strip().decode() # Read staggered flag fmt = ALIGN + "i" # Fortran logical is 4 byte int h["staggered"] = bool(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))[0]) # Read w_names w_names = [] for _ in range(h["nw"]): fmt = ALIGN + NAME_LEN * "c" hdr = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) w_names.append(b"".join(hdr).strip().decode()) h["w_names"] = w_names # Read physics type fmt = ALIGN + NAME_LEN * "c" hdr = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) h["physics_type"] = b"".join(hdr).strip().decode() # Read number of physics-defined parameters fmt = ALIGN + "i" [n_pars] = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) # First physics-parameter values are given, then their names fmt = ALIGN + n_pars * "d" vals = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) fmt = ALIGN + n_pars * NAME_LEN * "c" names = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) # Split and join the name strings (from one character array) names = [ b"".join(names[i : i + NAME_LEN]).strip().decode() for i in range(0, len(names), NAME_LEN) ] # Store the values corresponding to the names for val, name in zip(vals, names, strict=True): h[name] = val return h def get_tree_info(istream): """ Read levels, morton-curve indices, and byte offsets for each block as stored in the datfile istream is an open datfile buffer with 'rb' mode This can be used as the "first pass" data reading required by YT's interface. """ istream.seek(0) header = get_header(istream) nleafs = header["nleafs"] nparents = header["nparents"] # Read tree info. Skip 'leaf' array istream.seek(header["offset_tree"] + (nleafs + nparents) * SIZE_LOGICAL) # Read block levels fmt = ALIGN + nleafs * "i" block_lvls = np.array(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))) # Read block indices fmt = ALIGN + nleafs * header["ndim"] * "i" block_ixs = np.reshape( struct.unpack(fmt, istream.read(struct.calcsize(fmt))), [nleafs, header["ndim"]] ) # Read block offsets (skip ghost cells !) bcfmt = ALIGN + header["ndim"] * "i" bcsize = struct.calcsize(bcfmt) * 2 fmt = ALIGN + nleafs * "q" block_offsets = ( np.array(struct.unpack(fmt, istream.read(struct.calcsize(fmt)))) + bcsize ) return block_lvls, block_ixs, block_offsets def get_single_block_data(istream, byte_offset, block_shape): """retrieve a specific block (all fields) from a datfile""" istream.seek(byte_offset) # Read actual data fmt = ALIGN + np.prod(block_shape) * "d" d = struct.unpack(fmt, istream.read(struct.calcsize(fmt))) # Fortran ordering block_data = np.reshape(d, block_shape, order="F") return block_data def get_single_block_field_data(istream, byte_offset, block_shape, field_idx): """retrieve a specific block (ONE field) from a datfile""" # compute byte size of a single field field_shape = block_shape[:-1][::-1] count = np.prod(field_shape) fmt = ALIGN + count * "d" byte_size_field = struct.calcsize(fmt) istream.seek(byte_offset + byte_size_field * field_idx) return np.fromfile(istream, dtype="=f8", count=count).reshape(field_shape).T yt-project-yt-f043ac8/yt/frontends/amrvac/definitions.py000066400000000000000000000001141510711153200234420ustar00rootroot00000000000000# This file is often empty. It can hold definitions related to a frontend. yt-project-yt-f043ac8/yt/frontends/amrvac/fields.py000066400000000000000000000255201510711153200224050ustar00rootroot00000000000000""" AMRVAC-specific fields """ import functools import numpy as np from yt.fields.field_info_container import FieldInfoContainer from yt.units import dimensions from yt.utilities.logger import ytLogger as mylog # We need to specify which fields we might have in our dataset. The field info # container subclass here will define which fields it knows about. There are # optionally methods on it that get called which can be subclassed. direction_aliases = { "cartesian": ("x", "y", "z"), "polar": ("r", "theta", "z"), "cylindrical": ("r", "z", "theta"), "spherical": ("r", "theta", "phi"), } def _velocity(field, data, idir, prefix=None): """Velocity = linear momentum / density""" # This is meant to be used with functools.partial to produce # functions with only 2 arguments (field, data) # idir : int # the direction index (1, 2 or 3) # prefix : str # used to generalize to dust fields if prefix is None: prefix = "" moment = data["gas", "%smoment_%d" % (prefix, idir)] rho = data["gas", f"{prefix}density"] mask1 = rho == 0 if mask1.any(): mylog.info( "zeros found in %sdensity, " "patching them to compute corresponding velocity field.", prefix, ) mask2 = moment == 0 if not ((mask1 & mask2) == mask1).all(): raise RuntimeError rho[mask1] = 1 return moment / rho code_density = "code_mass / code_length**3" code_moment = "code_mass / code_length**2 / code_time" code_pressure = "code_mass / code_length / code_time**2" class AMRVACFieldInfo(FieldInfoContainer): # for now, define a finite family of dust fields (up to 100 species) MAXN_DUST_SPECIES = 100 known_dust_fields = [ ("rhod%d" % idust, (code_density, ["dust%d_density" % idust], None)) for idust in range(1, MAXN_DUST_SPECIES + 1) ] + [ ( "m%dd%d" % (idir, idust), (code_moment, ["dust%d_moment_%d" % (idust, idir)], None), ) for idust in range(1, MAXN_DUST_SPECIES + 1) for idir in (1, 2, 3) ] # format: (native(?) field, (units, [aliases], display_name)) # note: aliases will correspond to "gas" typed fields # whereas the native ones are "amrvac" typed known_other_fields = ( ("rho", (code_density, ["density"], None)), ("m1", (code_moment, ["moment_1"], None)), ("m2", (code_moment, ["moment_2"], None)), ("m3", (code_moment, ["moment_3"], None)), ("e", (code_pressure, ["energy_density"], None)), ("b1", ("code_magnetic", ["magnetic_1"], None)), ("b2", ("code_magnetic", ["magnetic_2"], None)), ("b3", ("code_magnetic", ["magnetic_3"], None)), ("Te", ("code_temperature", ["temperature"], None)), *known_dust_fields, ) known_particle_fields = () def _setup_velocity_fields(self, idust=None): if idust is None: dust_flag = dust_label = "" else: dust_flag = "d%d" % idust dust_label = "dust%d_" % idust us = self.ds.unit_system for idir, alias in enumerate(direction_aliases[self.ds.geometry], start=1): if ("amrvac", "m%d%s" % (idir, dust_flag)) not in self.field_list: break velocity_fn = functools.partial(_velocity, idir=idir, prefix=dust_label) self.add_field( ("gas", f"{dust_label}velocity_{alias}"), function=velocity_fn, units=us["velocity"], dimensions=dimensions.velocity, sampling_type="cell", ) self.alias( ("gas", "%svelocity_%d" % (dust_label, idir)), ("gas", f"{dust_label}velocity_{alias}"), units=us["velocity"], ) self.alias( ("gas", f"{dust_label}moment_{alias}"), ("gas", "%smoment_%d" % (dust_label, idir)), units=us["density"] * us["velocity"], ) def _setup_dust_fields(self): idust = 1 imax = self.__class__.MAXN_DUST_SPECIES while ("amrvac", "rhod%d" % idust) in self.field_list: if idust > imax: mylog.error( "Only the first %d dust species are currently read by yt. " "If you read this, please consider issuing a ticket. ", imax, ) break self._setup_velocity_fields(idust) idust += 1 n_dust_found = idust - 1 us = self.ds.unit_system if n_dust_found > 0: def _total_dust_density(field, data): tot = np.zeros_like(data["gas", "density"]) for idust in range(1, n_dust_found + 1): tot += data["dust%d_density" % idust] return tot self.add_field( ("gas", "total_dust_density"), function=_total_dust_density, dimensions=dimensions.density, units=us["density"], sampling_type="cell", ) def dust_to_gas_ratio(field, data): return data["gas", "total_dust_density"] / data["gas", "density"] self.add_field( ("gas", "dust_to_gas_ratio"), function=dust_to_gas_ratio, dimensions=dimensions.dimensionless, sampling_type="cell", ) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases setup_magnetic_field_aliases(self, "amrvac", [f"mag{ax}" for ax in "xyz"]) self._setup_velocity_fields() # gas velocities self._setup_dust_fields() # dust derived fields (including velocities) # fields with nested dependencies are defined thereafter # by increasing level of complexity us = self.ds.unit_system def _kinetic_energy_density(field, data): # devnote : have a look at issue 1301 return 0.5 * data["gas", "density"] * data["gas", "velocity_magnitude"] ** 2 self.add_field( ("gas", "kinetic_energy_density"), function=_kinetic_energy_density, units=us["density"] * us["velocity"] ** 2, dimensions=dimensions.density * dimensions.velocity**2, sampling_type="cell", ) # magnetic energy density if ("amrvac", "b1") in self.field_list: def _magnetic_energy_density(field, data): emag = 0.5 * data["gas", "magnetic_1"] ** 2 for idim in "23": if ("amrvac", f"b{idim}") not in self.field_list: break emag += 0.5 * data["gas", f"magnetic_{idim}"] ** 2 # in AMRVAC the magnetic field is defined in units where mu0 = 1, # such that # Emag = 0.5*B**2 instead of Emag = 0.5*B**2 / mu0 # To correctly transform the dimensionality from gauss**2 -> rho*v**2, # we have to take mu0 into account. If we divide here, units when adding # the field should be us["density"]*us["velocity"]**2. # If not, they should be us["magnetic_field"]**2 and division should # happen elsewhere. emag /= 4 * np.pi # divided by mu0 = 4pi in cgs, # yt handles 'mks' and 'code' unit systems internally. return emag self.add_field( ("gas", "magnetic_energy_density"), function=_magnetic_energy_density, units=us["density"] * us["velocity"] ** 2, dimensions=dimensions.density * dimensions.velocity**2, sampling_type="cell", ) # Adding the thermal pressure field. # In AMRVAC we have multiple physics possibilities: # - if HD/MHD + energy equation P = (gamma-1)*(e - ekin (- emag)) for (M)HD # - if HD/MHD but solve_internal_e is true in parfile, P = (gamma-1)*e for both # - if (m)hd_energy is false in parfile (isothermal), P = c_adiab * rho**gamma def _full_thermal_pressure_HD(field, data): # energy density and pressure are actually expressed in the same unit pthermal = (data.ds.gamma - 1) * ( data["gas", "energy_density"] - data["gas", "kinetic_energy_density"] ) return pthermal def _full_thermal_pressure_MHD(field, data): pthermal = ( _full_thermal_pressure_HD(field, data) - (data.ds.gamma - 1) * data["gas", "magnetic_energy_density"] ) return pthermal def _polytropic_thermal_pressure(field, data): return (data.ds.gamma - 1) * data["gas", "energy_density"] def _adiabatic_thermal_pressure(field, data): return data.ds._c_adiab * data["gas", "density"] ** data.ds.gamma pressure_recipe = None if ("amrvac", "e") in self.field_list: if self.ds._e_is_internal: pressure_recipe = _polytropic_thermal_pressure mylog.info("Using polytropic EoS for thermal pressure.") elif ("amrvac", "b1") in self.field_list: pressure_recipe = _full_thermal_pressure_MHD mylog.info("Using full MHD energy for thermal pressure.") else: pressure_recipe = _full_thermal_pressure_HD mylog.info("Using full HD energy for thermal pressure.") elif self.ds._c_adiab is not None: pressure_recipe = _adiabatic_thermal_pressure mylog.info("Using adiabatic EoS for thermal pressure (isothermal).") mylog.warning( "If you used usr_set_pthermal you should " "redefine the thermal_pressure field." ) if pressure_recipe is not None: self.add_field( ("gas", "thermal_pressure"), function=pressure_recipe, units=us["density"] * us["velocity"] ** 2, dimensions=dimensions.density * dimensions.velocity**2, sampling_type="cell", ) # sound speed and temperature depend on thermal pressure def _sound_speed(field, data): return np.sqrt( data.ds.gamma * data["gas", "thermal_pressure"] / data["gas", "density"] ) self.add_field( ("gas", "sound_speed"), function=_sound_speed, units=us["velocity"], dimensions=dimensions.velocity, sampling_type="cell", ) else: mylog.warning( "e not found and no parfile passed, can not set thermal_pressure." ) yt-project-yt-f043ac8/yt/frontends/amrvac/io.py000066400000000000000000000150721510711153200215470ustar00rootroot00000000000000""" AMRVAC-specific IO functions """ import os import numpy as np from more_itertools import always_iterable from yt.geometry.selection_routines import GridSelector from yt.utilities.io_handler import BaseIOHandler from yt.utilities.on_demand_imports import _f90nml as f90nml def read_amrvac_namelist(parfiles): """Read one or more parfiles, and return a unified f90nml.Namelist object. This function replicates the patching logic of MPI-AMRVAC where redundant parameters only retain last-in-line values, with the exception of `&filelist:base_filename`, which is accumulated. When passed a single file, this function acts as a mere wrapper of f90nml.read(). Parameters ---------- parfiles : str, os.Pathlike, byte, or an iterable returning those types A file path, or a list of file paths to MPI-AMRVAC configuration parfiles. Returns ------- unified_namelist : f90nml.Namelist A single namelist object. The class inherits from ordereddict. """ parfiles = (os.path.expanduser(pf) for pf in always_iterable(parfiles)) # first merge the namelists namelists = [f90nml.read(parfile) for parfile in parfiles] unified_namelist = f90nml.Namelist() for nml in namelists: unified_namelist.patch(nml) if "filelist" not in unified_namelist: return unified_namelist # accumulate `&filelist:base_filename` base_filename = "".join( nml.get("filelist", {}).get("base_filename", "") for nml in namelists ) unified_namelist["filelist"]["base_filename"] = base_filename return unified_namelist class AMRVACIOHandler(BaseIOHandler): _particle_reader = False _dataset_type = "amrvac" def __init__(self, ds): BaseIOHandler.__init__(self, ds) self.ds = ds self.datfile = ds.parameter_filename header = self.ds.parameters self.block_shape = np.append(header["block_nx"], header["nw"]) def _read_particle_coords(self, chunks, ptf): """Not implemented yet.""" # This needs to *yield* a series of tuples of (ptype, (x, y, z)). # chunks is a list of chunks, and ptf is a dict where the keys are # ptypes and the values are lists of fields. raise NotImplementedError def _read_particle_fields(self, chunks, ptf, selector): """Not implemented yet.""" # This gets called after the arrays have been allocated. It needs to # yield ((ptype, field), data) where data is the masked results of # reading ptype, field and applying the selector to the data read in. # Selector objects have a .select_points(x,y,z) that returns a mask, so # you need to do your masking here. raise NotImplementedError def _read_data(self, fid, grid, field): """Retrieve field data from a grid. Parameters ---------- fid: file descriptor (open binary file with read access) grid : yt.frontends.amrvac.data_structures.AMRVACGrid The grid from which data is to be read. field : str A field name. Returns ------- data : np.ndarray A 3D array of float64 type representing grid data. """ ileaf = grid.id offset = grid._index.block_offsets[ileaf] field_idx = self.ds.parameters["w_names"].index(field) field_shape = self.block_shape[:-1][::-1] count = np.prod(field_shape) byte_size_field = count * 8 # size of a double fid.seek(offset + byte_size_field * field_idx) data = np.fromfile(fid, dtype="=f8", count=count).reshape(field_shape).T # Always convert data to 3D, as grid.ActiveDimensions is always 3D while len(data.shape) < 3: data = data[..., np.newaxis] return data def _read_fluid_selection(self, chunks, selector, fields, size): """Retrieve field(s) data in a selected region of space. Parameters ---------- chunks : generator A generator for multiple chunks, each of which contains a list of grids. selector : yt.geometry.selection_routines.SelectorObject A spatial region selector. fields : list A list of tuples (ftype, fname). size : np.int64 The cumulative number of objs contained in all chunks. Returns ------- data_dict : dict keys are the (ftype, fname) tuples, values are arrays that have been masked using whatever selector method is appropriate. Arrays have dtype float64. """ # @Notes from Niels: # The chunks list has YTDataChunk objects containing the different grids. # The list of grids can be obtained by doing eg. # grids_list = chunks[0].objs or chunks[1].objs etc. # Every element in "grids_list" is then an AMRVACGrid object, # and has hence all attributes of a grid : # (Level, ActiveDimensions, LeftEdge, etc.) chunks = list(chunks) data_dict = {} # <- return variable if isinstance(selector, GridSelector): if not len(chunks) == len(chunks[0].objs) == 1: raise RuntimeError grid = chunks[0].objs[0] with open(self.datfile, "rb") as fh: for ftype, fname in fields: data_dict[ftype, fname] = self._read_data(fh, grid, fname) else: if size is None: size = sum(g.count(selector) for chunk in chunks for g in chunk.objs) for field in fields: data_dict[field] = np.empty(size, dtype="float64") # nb_grids = sum(len(chunk.objs) for chunk in chunks) with open(self.datfile, "rb") as fh: ind = 0 for chunk in chunks: for grid in chunk.objs: nd = 0 for field in fields: ftype, fname = field data = self._read_data(fh, grid, fname) nd = grid.select(selector, data, data_dict[field], ind) ind += nd return data_dict def _read_chunk_data(self, chunk, fields): """Not implemented yet.""" # This reads the data from a single chunk without doing any selection, # and is only used for caching data that might be used by multiple # different selectors later. For instance, this can speed up ghost zone # computation. # it should be used by _read_fluid_selection instead of _read_data raise NotImplementedError yt-project-yt-f043ac8/yt/frontends/amrvac/tests/000077500000000000000000000000001510711153200217235ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrvac/tests/__init__.py000066400000000000000000000000001510711153200240220ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrvac/tests/sample_parfiles/000077500000000000000000000000001510711153200250715ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/amrvac/tests/sample_parfiles/bw_3d.par000066400000000000000000000021201510711153200265660ustar00rootroot00000000000000!setup.pl -d=3 &filelist typefilelog='regression_test' base_filename='bw_3d' saveprim=.true. convert_type='vtuBCCmpi' autoconvert=.true. nwauxio=1 / &savelist dtsave_log = 1.d-3 / &stoplist time_max = 2.d-2 / &methodlist time_integrator= 'threestep' flux_scheme= 20*'hllc' limiter= 20*'koren' / &boundlist typeboundary_min1=5*'cont' typeboundary_max1=5*'cont' typeboundary_min2=5*'cont' typeboundary_max2=5*'cont' typeboundary_min3=5*'cont' typeboundary_max3=5*'cont' / &meshlist refine_criterion=3 refine_max_level=3 w_refine_weight(1)=0.5d0 w_refine_weight(5)=0.5d0 block_nx1=8 block_nx2=8 block_nx3=8 domain_nx1=16 domain_nx2=16 domain_nx3=16 iprob=1 xprobmin1=0.d0 xprobmax1=2.d0 xprobmin2=0.d0 xprobmax2=2.d0 xprobmin3=0.d0 xprobmax3=2.d0 / ¶mlist typecourant='maxsum' courantpar=0.5d0 / yt-project-yt-f043ac8/yt/frontends/amrvac/tests/sample_parfiles/tvdlf_scheme.par000066400000000000000000000003671510711153200302460ustar00rootroot00000000000000! This is a fake example modifier parfile that can be used as an ! extension to bw_3d.par in order to change the integrator scheme. ! This file is here for testing. &methodlist flux_scheme= 20*'tvdlf' / &filelist base_filename='_tvdlf' / yt-project-yt-f043ac8/yt/frontends/amrvac/tests/test_outputs.py000066400000000000000000000106331510711153200250620ustar00rootroot00000000000000import numpy as np import yt # NOQA from yt.frontends.amrvac.api import AMRVACDataset, AMRVACGrid from yt.testing import requires_file from yt.units import YTArray from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) blastwave_spherical_2D = "amrvac/bw_2d0000.dat" khi_cartesian_2D = "amrvac/kh_2d0000.dat" khi_cartesian_3D = "amrvac/kh_3D0000.dat" jet_cylindrical_25D = "amrvac/Jet0003.dat" riemann_cartesian_175D = "amrvac/R_1d0005.dat" blastwave_cartesian_3D = "amrvac/bw_3d0000.dat" blastwave_polar_2D = "amrvac/bw_polar_2D0000.dat" blastwave_cylindrical_3D = "amrvac/bw_cylindrical_3D0000.dat" rmi_cartesian_dust_2D = "amrvac/Richtmyer_Meshkov_dust_2D/RM2D_dust_Kwok0000.dat" def _get_fields_to_check(ds): fields = ["density", "velocity_magnitude"] raw_fields_labels = [fname for ftype, fname in ds.field_list] if "b1" in raw_fields_labels: fields.append("magnetic_energy_density") if "e" in raw_fields_labels: fields.append("energy_density") if "rhod1" in raw_fields_labels: fields.append("total_dust_density") # note : not hitting dust velocity fields return fields @requires_file(khi_cartesian_2D) def test_AMRVACDataset(): assert isinstance(data_dir_load(khi_cartesian_2D), AMRVACDataset) @requires_ds(blastwave_cartesian_3D) def test_domain_size(): # "Check for correct box size, see bw_3d.par" ds = data_dir_load(blastwave_cartesian_3D) for lb in ds.domain_left_edge: assert int(lb) == 0 for rb in ds.domain_right_edge: assert int(rb) == 2 for w in ds.domain_width: assert int(w) == 2 @requires_file(blastwave_cartesian_3D) def test_grid_attributes(): # "Check various grid attributes" ds = data_dir_load(blastwave_cartesian_3D) grids = ds.index.grids assert ds.index.max_level == 2 for g in grids: assert isinstance(g, AMRVACGrid) assert isinstance(g.LeftEdge, YTArray) assert isinstance(g.RightEdge, YTArray) assert isinstance(g.ActiveDimensions, np.ndarray) assert isinstance(g.Level, (np.int32, np.int64, int)) @requires_ds(blastwave_polar_2D) def test_bw_polar_2d(): ds = data_dir_load(blastwave_polar_2D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_bw_polar_2d.__name__ = test.description yield test @requires_ds(blastwave_cartesian_3D) def test_blastwave_cartesian_3D(): ds = data_dir_load(blastwave_cartesian_3D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_blastwave_cartesian_3D.__name__ = test.description yield test @requires_ds(blastwave_spherical_2D) def test_blastwave_spherical_2D(): ds = data_dir_load(blastwave_spherical_2D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_blastwave_spherical_2D.__name__ = test.description yield test @requires_ds(blastwave_cylindrical_3D) def test_blastwave_cylindrical_3D(): ds = data_dir_load(blastwave_cylindrical_3D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_blastwave_cylindrical_3D.__name__ = test.description yield test @requires_ds(khi_cartesian_2D) def test_khi_cartesian_2D(): ds = data_dir_load(khi_cartesian_2D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_khi_cartesian_2D.__name__ = test.description yield test @requires_ds(khi_cartesian_3D) def test_khi_cartesian_3D(): ds = data_dir_load(khi_cartesian_3D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_khi_cartesian_3D.__name__ = test.description yield test @requires_ds(jet_cylindrical_25D) def test_jet_cylindrical_25D(): ds = data_dir_load(jet_cylindrical_25D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_jet_cylindrical_25D.__name__ = test.description yield test @requires_ds(riemann_cartesian_175D) def test_riemann_cartesian_175D(): ds = data_dir_load(riemann_cartesian_175D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_riemann_cartesian_175D.__name__ = test.description yield test @requires_ds(rmi_cartesian_dust_2D) def test_rmi_cartesian_dust_2D(): # dataset with dust fields ds = data_dir_load(rmi_cartesian_dust_2D) for test in small_patch_amr(ds, _get_fields_to_check(ds)): test_rmi_cartesian_dust_2D.__name__ = test.description yield test yt-project-yt-f043ac8/yt/frontends/amrvac/tests/test_read_amrvac_namelist.py000066400000000000000000000033441510711153200275000ustar00rootroot00000000000000import os from copy import deepcopy from pathlib import Path from yt.frontends.amrvac.api import read_amrvac_namelist from yt.testing import requires_module from yt.utilities.on_demand_imports import _f90nml as f90nml test_dir = os.path.dirname(os.path.abspath(__file__)) blast_wave_parfile = os.path.join(test_dir, "sample_parfiles", "bw_3d.par") modifier_parfile = os.path.join(test_dir, "sample_parfiles", "tvdlf_scheme.par") @requires_module("f90nml") def test_read_pathlike(): read_amrvac_namelist(Path(blast_wave_parfile)) @requires_module("f90nml") def test_read_one_file(): """when provided a single file, the function should merely act as a wrapper for f90nml.read()""" namelist1 = read_amrvac_namelist(blast_wave_parfile) namelist2 = f90nml.read(blast_wave_parfile) assert namelist1 == namelist2 @requires_module("f90nml") def test_accumulate_basename(): """When two (or more) parfiles are passed, the filelist:base_filename should be special-cased""" namelist_base = f90nml.read(blast_wave_parfile) namelist_update = f90nml.read(modifier_parfile) namelist_tot1 = read_amrvac_namelist([blast_wave_parfile, modifier_parfile]) namelist_tot2 = deepcopy(namelist_base) namelist_tot2.patch(namelist_update) # remove and store the special-case value name1 = namelist_tot1["filelist"].pop("base_filename") name2 = namelist_tot2["filelist"].pop("base_filename") assert ( name1 == namelist_base["filelist"]["base_filename"] + namelist_update["filelist"]["base_filename"] ) assert name2 == namelist_update["filelist"]["base_filename"] assert name1 != name2 # test equality for the rest of the namelist assert namelist_tot1 == namelist_tot2 yt-project-yt-f043ac8/yt/frontends/amrvac/tests/test_units_override.py000066400000000000000000000116231510711153200264000ustar00rootroot00000000000000from numpy.testing import assert_raises from yt.testing import assert_allclose_units, requires_file from yt.units import YTQuantity from yt.utilities.answer_testing.framework import data_dir_load khi_cartesian_2D = "amrvac/kh_2d0000.dat" # Tests for units: check that overriding certain units yields the correct derived units. # The following are the correct normalisations # based on length, numberdensity and temperature length_unit = (1e9, "cm") numberdensity_unit = (1e9, "cm**-3") temperature_unit = (1e6, "K") density_unit = (2.341670657200000e-15, "g*cm**-3") mass_unit = (2.341670657200000e12, "g") velocity_unit = (1.164508387441102e07, "cm*s**-1") pressure_unit = (3.175492240000000e-01, "dyn*cm**-2") time_unit = (8.587314705370271e01, "s") magnetic_unit = (1.997608879907716, "gauss") def _assert_normalisations_equal(ds): assert_allclose_units(ds.length_unit, YTQuantity(*length_unit)) assert_allclose_units(ds.temperature_unit, YTQuantity(*temperature_unit)) assert_allclose_units(ds.density_unit, YTQuantity(*density_unit)) assert_allclose_units(ds.mass_unit, YTQuantity(*mass_unit)) assert_allclose_units(ds.velocity_unit, YTQuantity(*velocity_unit)) assert_allclose_units(ds.pressure_unit, YTQuantity(*pressure_unit)) assert_allclose_units(ds.time_unit, YTQuantity(*time_unit)) assert_allclose_units(ds.magnetic_unit, YTQuantity(*magnetic_unit)) @requires_file(khi_cartesian_2D) def test_normalisations_length_temp_nb(): # overriding length, temperature, numberdensity overrides = { "length_unit": length_unit, "temperature_unit": temperature_unit, "numberdensity_unit": numberdensity_unit, } ds = data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) _assert_normalisations_equal(ds) @requires_file(khi_cartesian_2D) def test_normalisations_length_temp_mass(): # overriding length, temperature, mass overrides = { "length_unit": length_unit, "temperature_unit": temperature_unit, "mass_unit": mass_unit, } ds = data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) _assert_normalisations_equal(ds) @requires_file(khi_cartesian_2D) def test_normalisations_length_time_mass(): # overriding length, time, mass overrides = { "length_unit": length_unit, "time_unit": time_unit, "mass_unit": mass_unit, } ds = data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) _assert_normalisations_equal(ds) @requires_file(khi_cartesian_2D) def test_normalisations_length_vel_nb(): # overriding length, velocity, numberdensity overrides = { "length_unit": length_unit, "velocity_unit": velocity_unit, "numberdensity_unit": numberdensity_unit, } ds = data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) _assert_normalisations_equal(ds) @requires_file(khi_cartesian_2D) def test_normalisations_length_vel_mass(): # overriding length, velocity, mass overrides = { "length_unit": length_unit, "velocity_unit": velocity_unit, "mass_unit": mass_unit, } ds = data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) _assert_normalisations_equal(ds) @requires_file(khi_cartesian_2D) def test_normalisations_default(): # test default normalisations, without overrides ds = data_dir_load(khi_cartesian_2D) assert_allclose_units(ds.length_unit, YTQuantity(1, "cm")) assert_allclose_units(ds.temperature_unit, YTQuantity(1, "K")) assert_allclose_units( ds.density_unit, YTQuantity(2.341670657200000e-24, "g*cm**-3") ) assert_allclose_units(ds.mass_unit, YTQuantity(2.341670657200000e-24, "g")) assert_allclose_units( ds.velocity_unit, YTQuantity(1.164508387441102e04, "cm*s**-1") ) assert_allclose_units( ds.pressure_unit, YTQuantity(3.175492240000000e-16, "dyn*cm**-2") ) assert_allclose_units(ds.time_unit, YTQuantity(8.587314705370271e-05, "s")) assert_allclose_units(ds.magnetic_unit, YTQuantity(6.316993934686148e-08, "gauss")) @requires_file(khi_cartesian_2D) def test_normalisations_too_many_args(): # test forbidden case: too many arguments (max 3 are allowed) overrides = { "length_unit": length_unit, "numberdensity_unit": numberdensity_unit, "temperature_unit": temperature_unit, "time_unit": time_unit, } with assert_raises(ValueError): data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) @requires_file(khi_cartesian_2D) def test_normalisations_vel_and_length(): # test forbidden case: both velocity and temperature are specified as overrides overrides = { "length_unit": length_unit, "velocity_unit": velocity_unit, "temperature_unit": temperature_unit, } with assert_raises(ValueError): data_dir_load(khi_cartesian_2D, kwargs={"units_override": overrides}) yt-project-yt-f043ac8/yt/frontends/api.py000066400000000000000000000000671510711153200204360ustar00rootroot00000000000000from . import __all__ as _frontends # backward compat yt-project-yt-f043ac8/yt/frontends/arepo/000077500000000000000000000000001510711153200204165ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/arepo/__init__.py000066400000000000000000000000001510711153200225150ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/arepo/api.py000066400000000000000000000001651510711153200215430ustar00rootroot00000000000000from . import tests from .data_structures import ArepoFieldInfo, ArepoHDF5Dataset from .io import IOHandlerArepoHDF5 yt-project-yt-f043ac8/yt/frontends/arepo/data_structures.py000066400000000000000000000112701510711153200242050ustar00rootroot00000000000000import numpy as np from yt.frontends.gadget.api import GadgetHDF5Dataset from yt.funcs import mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import ArepoFieldInfo class ArepoHDF5Dataset(GadgetHDF5Dataset): _load_requirements = ["h5py"] _field_info_class = ArepoFieldInfo def __init__( self, filename, dataset_type="arepo_hdf5", unit_base=None, smoothing_factor=2.0, index_order=None, index_filename=None, kernel_name=None, bounding_box=None, units_override=None, unit_system="cgs", default_species_fields=None, ): super().__init__( filename, dataset_type=dataset_type, unit_base=unit_base, index_order=index_order, index_filename=index_filename, kernel_name=kernel_name, bounding_box=bounding_box, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) # The "smoothing_factor" is a user-configurable parameter which # is multiplied by the radius of the sphere with a volume equal # to that of the Voronoi cell to create smoothing lengths. self.smoothing_factor = smoothing_factor self.gamma = 5.0 / 3.0 self.gamma_cr = self.parameters.get("GammaCR", 4.0 / 3.0) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = ["Header", "Config"] veto_groups = ["FOF", "Group", "Subhalo"] valid = True try: fh = h5py.File(filename, mode="r") valid = ( all(ng in fh["/"] for ng in need_groups) and not any(vg in fh["/"] for vg in veto_groups) and ( "VORONOI" in fh["/Config"].attrs.keys() or "AMR" in fh["/Config"].attrs.keys() ) # Datasets with GFM_ fields present are AREPO or any(field.startswith("GFM_") for field in fh["/PartType0"]) ) fh.close() except Exception: valid = False return valid def _get_uvals(self): handle = h5py.File(self.parameter_filename, mode="r") uvals = {} missing = [True] * 3 for i, unit in enumerate( ["UnitLength_in_cm", "UnitMass_in_g", "UnitVelocity_in_cm_per_s"] ): for grp in ["Header", "Parameters", "Units"]: if grp in handle and unit in handle[grp].attrs: uvals[unit] = handle[grp].attrs[unit] missing[i] = False break if "UnitLength_in_cm" in uvals: # We assume this is comoving, because in the absence of comoving # integration the redshift will be zero. uvals["cmcm"] = 1.0 / uvals["UnitLength_in_cm"] handle.close() if all(missing): uvals = None return uvals def _set_code_unit_attributes(self): arepo_unit_base = self._get_uvals() # This rather convoluted logic is required to ensure that # units which are present in the Arepo dataset will be used # no matter what but that the user gets warned if arepo_unit_base is not None: if self._unit_base is None: self._unit_base = arepo_unit_base else: for unit in arepo_unit_base: if unit == "cmcm": continue short_unit = unit.split("_")[0][4:].lower() if short_unit in self._unit_base: which_unit = short_unit self._unit_base.pop(short_unit, None) elif unit in self._unit_base: which_unit = unit else: which_unit = None if which_unit is not None: msg = f"Overwriting '{which_unit}' in unit_base with what we found in the dataset." mylog.warning(msg) self._unit_base[unit] = arepo_unit_base[unit] if "cmcm" in arepo_unit_base: self._unit_base["cmcm"] = arepo_unit_base["cmcm"] super()._set_code_unit_attributes() munit = np.sqrt(self.mass_unit / (self.time_unit**2 * self.length_unit)).to( "gauss" ) if self.cosmological_simulation: self.magnetic_unit = self.quan(munit.value, f"{munit.units}/a**2") else: self.magnetic_unit = munit yt-project-yt-f043ac8/yt/frontends/arepo/fields.py000066400000000000000000000210561510711153200222420ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer from yt.fields.species_fields import add_species_field_by_fraction, setup_species_fields from yt.frontends.gadget.api import GadgetFieldInfo from yt.utilities.chemical_formulas import ChemicalFormula from yt.utilities.physical_ratios import _primordial_mass_fraction metal_elements = ["He", "C", "N", "O", "Ne", "Mg", "Si", "Fe"] class ArepoFieldInfo(GadgetFieldInfo): def __init__(self, ds, field_list, slice_info=None): if ds.cosmological_simulation: GFM_SFT_units = "dimensionless" else: GFM_SFT_units = "code_length/code_velocity" self.known_particle_fields += ( ("GFM_StellarFormationTime", (GFM_SFT_units, ["stellar_age"], None)), ("MagneticField", ("code_magnetic", ["particle_magnetic_field"], None)), ( "MagneticFieldDivergence", ("code_magnetic/code_length", ["magnetic_field_divergence"], None), ), ("GFM_CoolingRate", ("erg*cm**3/s", ["cooling_rate"], None)), ("GFM_Metallicity", ("", ["metallicity"], None)), ("GFM_Metals_00", ("", ["H_fraction"], None)), ("GFM_Metals_01", ("", ["He_fraction"], None)), ("GFM_Metals_02", ("", ["C_fraction"], None)), ("GFM_Metals_03", ("", ["N_fraction"], None)), ("GFM_Metals_04", ("", ["O_fraction"], None)), ("GFM_Metals_05", ("", ["Ne_fraction"], None)), ("GFM_Metals_06", ("", ["Mg_fraction"], None)), ("GFM_Metals_07", ("", ["Si_fraction"], None)), ("GFM_Metals_08", ("", ["Fe_fraction"], None)), ("GFM_StellarPhotometrics_00", ("", ["U_magnitude"], None)), ("GFM_StellarPhotometrics_01", ("", ["B_magnitude"], None)), ("GFM_StellarPhotometrics_02", ("", ["V_magnitude"], None)), ("GFM_StellarPhotometrics_03", ("", ["K_magnitude"], None)), ("GFM_StellarPhotometrics_04", ("", ["g_magnitude"], None)), ("GFM_StellarPhotometrics_05", ("", ["r_magnitude"], None)), ("GFM_StellarPhotometrics_06", ("", ["i_magnitude"], None)), ("GFM_StellarPhotometrics_07", ("", ["z_magnitude"], None)), ( "CosmicRaySpecificEnergy", ("code_specific_energy", ["specific_cosmic_ray_energy"], None), ), ) super().__init__(ds, field_list, slice_info=slice_info) def setup_particle_fields(self, ptype, *args, **kwargs): FieldInfoContainer.setup_particle_fields(self, ptype) if ptype == "PartType0": self.setup_gas_particle_fields(ptype) setup_species_fields(self, ptype) def setup_gas_particle_fields(self, ptype): from yt.fields.magnetic_field import setup_magnetic_field_aliases super().setup_gas_particle_fields(ptype) # Since the AREPO gas "particles" are Voronoi cells, we can # define a volume here def _volume(field, data): return data["gas", "mass"] / data["gas", "density"] self.add_field( ("gas", "cell_volume"), function=_volume, sampling_type="local", units=self.ds.unit_system["volume"], ) if (ptype, "InternalEnergy") in self.field_list: def _pressure(field, data): return ( (data.ds.gamma - 1.0) * data[ptype, "density"] * data[ptype, "InternalEnergy"] ) self.add_field( ("gas", "pressure"), function=_pressure, sampling_type="local", units=self.ds.unit_system["pressure"], ) if (ptype, "GFM_Metals_00") in self.field_list: self.nuclei_names = metal_elements self.species_names = ["H"] + metal_elements if (ptype, "MagneticField") in self.field_list: setup_magnetic_field_aliases(self, ptype, "MagneticField") if (ptype, "NeutralHydrogenAbundance") in self.field_list: def _h_p0_fraction(field, data): return ( data[ptype, "GFM_Metals_00"] * data[ptype, "NeutralHydrogenAbundance"] ) self.add_field( (ptype, "H_p0_fraction"), sampling_type="particle", function=_h_p0_fraction, units="", ) def _h_p1_fraction(field, data): return data[ptype, "GFM_Metals_00"] * ( 1.0 - data[ptype, "NeutralHydrogenAbundance"] ) self.add_field( (ptype, "H_p1_fraction"), sampling_type="particle", function=_h_p1_fraction, units="", ) add_species_field_by_fraction(self, ptype, "H_p0") add_species_field_by_fraction(self, ptype, "H_p1") for species in ["H", "H_p0", "H_p1"]: for suf in ["_density", "_number_density"]: field = f"{species}{suf}" self.alias(("gas", field), (ptype, field)) if (ptype, "ElectronAbundance") in self.field_list: # If we have ElectronAbundance but not NeutralHydrogenAbundance, # try first to use the H_fraction, but otherwise we assume the # cosmic value for hydrogen to generate the H_number_density if (ptype, "NeutralHydrogenAbundance") not in self.field_list: m_u = self.ds.quan(1.0, "amu").in_cgs() A_H = ChemicalFormula("H").weight if (ptype, "GFM_Metals_00") in self.field_list: def _h_number_density(field, data): return ( data["gas", "density"] * data["gas", "H_fraction"] / (A_H * m_u) ) else: X_H = _primordial_mass_fraction["H"] def _h_number_density(field, data): return data["gas", "density"] * X_H / (A_H * m_u) self.add_field( (ptype, "H_number_density"), sampling_type="particle", function=_h_number_density, units=self.ds.unit_system["number_density"], ) self.alias(("gas", "H_number_density"), (ptype, "H_number_density")) self.alias(("gas", "H_nuclei_density"), ("gas", "H_number_density")) def _el_number_density(field, data): return ( data[ptype, "ElectronAbundance"] * data[ptype, "H_number_density"] ) self.add_field( (ptype, "El_number_density"), sampling_type="particle", function=_el_number_density, units=self.ds.unit_system["number_density"], ) self.alias(("gas", "El_number_density"), (ptype, "El_number_density")) if (ptype, "GFM_CoolingRate") in self.field_list: self.alias(("gas", "cooling_rate"), ("PartType0", "cooling_rate")) def _cooling_time(field, data): nH = data["gas", "H_nuclei_density"] dedt = -data["gas", "cooling_rate"] * nH * nH e = 1.5 * data["gas", "pressure"] return e / dedt self.add_field( ("gas", "cooling_time"), _cooling_time, sampling_type="local", units="s" ) if (ptype, "CosmicRaySpecificEnergy") in self.field_list: self.alias( (ptype, "specific_cosmic_ray_energy"), ("gas", "specific_cosmic_ray_energy"), ) def _cr_energy_density(field, data): return ( data["PartType0", "specific_cosmic_ray_energy"] * data["gas", "density"] ) self.add_field( ("gas", "cosmic_ray_energy_density"), _cr_energy_density, sampling_type="local", units=self.ds.unit_system["pressure"], ) def _cr_pressure(field, data): return (data.ds.gamma_cr - 1.0) * data[ "gas", "cosmic_ray_energy_density" ] self.add_field( ("gas", "cosmic_ray_pressure"), _cr_pressure, sampling_type="local", units=self.ds.unit_system["pressure"], ) yt-project-yt-f043ac8/yt/frontends/arepo/io.py000066400000000000000000000025701510711153200214030ustar00rootroot00000000000000import numpy as np from yt.frontends.gadget.api import IOHandlerGadgetHDF5 from yt.utilities.on_demand_imports import _h5py as h5py class IOHandlerArepoHDF5(IOHandlerGadgetHDF5): _dataset_type = "arepo_hdf5" def _generate_smoothing_length(self, index): # This is handled below in _get_smoothing_length return def _get_smoothing_length(self, data_file, position_dtype, position_shape): ptype = self.ds._sph_ptypes[0] ind = int(ptype[-1]) si, ei = data_file.start, data_file.end with h5py.File(data_file.filename, mode="r") as f: pcount = f["/Header"].attrs["NumPart_ThisFile"][ind].astype("int64") pcount = np.clip(pcount - si, 0, ei - si) # Arepo cells do not have "smoothing lengths" by definition, so # we compute one here by finding the radius of the sphere # corresponding to the volume of the Voroni cell and multiplying # by a user-configurable smoothing factor. hsml = f[ptype]["Masses"][si:ei, ...] / f[ptype]["Density"][si:ei, ...] hsml *= 3.0 / (4.0 * np.pi) hsml **= 1.0 / 3.0 hsml *= self.ds.smoothing_factor dt = hsml.dtype.newbyteorder("N") # Native if position_dtype is not None and dt < position_dtype: dt = position_dtype return hsml.astype(dt) yt-project-yt-f043ac8/yt/frontends/arepo/tests/000077500000000000000000000000001510711153200215605ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/arepo/tests/__init__.py000066400000000000000000000000001510711153200236570ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/arepo/tests/test_outputs.py000066400000000000000000000062711510711153200247220ustar00rootroot00000000000000from collections import OrderedDict from yt.frontends.arepo.api import ArepoHDF5Dataset from yt.testing import ( ParticleSelectionComparison, assert_allclose_units, requires_file, requires_module, ) from yt.utilities.answer_testing.framework import data_dir_load, requires_ds, sph_answer bullet_h5 = "ArepoBullet/snapshot_150.hdf5" tng59_h5 = "TNGHalo/halo_59.hdf5" _tng59_bbox = [[40669.34, 56669.34], [45984.04, 61984.04], [54114.9, 70114.9]] cr_h5 = "ArepoCosmicRays/snapshot_039.hdf5" @requires_module("h5py") @requires_file(bullet_h5) def test_arepo_hdf5_selection(): ds = data_dir_load(bullet_h5) assert isinstance(ds, ArepoHDF5Dataset) psc = ParticleSelectionComparison(ds) psc.run_defaults() bullet_fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "velocity_magnitude"), None), ] ) @requires_module("h5py") @requires_ds(bullet_h5) def test_arepo_bullet(): ds = data_dir_load(bullet_h5) for test in sph_answer(ds, "snapshot_150", 26529600, bullet_fields): test_arepo_bullet.__name__ = test.description yield test tng59_fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "H_number_density"), None), (("gas", "H_p0_number_density"), None), (("gas", "H_p1_number_density"), None), (("gas", "El_number_density"), None), (("gas", "C_number_density"), None), (("gas", "velocity_magnitude"), None), (("gas", "magnetic_field_strength"), None), ] ) @requires_module("h5py") @requires_ds(tng59_h5) def test_arepo_tng59(): ds = data_dir_load(tng59_h5, kwargs={"bounding_box": _tng59_bbox}) for test in sph_answer(ds, "halo_59", 10107142, tng59_fields): test_arepo_tng59.__name__ = test.description yield test @requires_module("h5py") @requires_ds(tng59_h5) def test_arepo_tng59_periodicity(): ds1 = data_dir_load(tng59_h5) assert ds1.periodicity == (True, True, True) ds2 = data_dir_load(tng59_h5, kwargs={"bounding_box": _tng59_bbox}) assert ds2.periodicity == (False, False, False) @requires_module("h5py") @requires_file(tng59_h5) def test_nh_density(): ds = data_dir_load(tng59_h5, kwargs={"bounding_box": _tng59_bbox}) ad = ds.all_data() assert_allclose_units( ad["gas", "H_number_density"], (ad["gas", "H_nuclei_density"]) ) @requires_module("h5py") @requires_file(tng59_h5) def test_arepo_tng59_selection(): ds = data_dir_load(tng59_h5, kwargs={"bounding_box": _tng59_bbox}) psc = ParticleSelectionComparison(ds) psc.run_defaults() cr_fields = OrderedDict( [ (("gas", "density"), None), (("gas", "cosmic_ray_energy_density"), None), (("gas", "cosmic_ray_pressure"), None), ] ) @requires_module("h5py") @requires_ds(cr_h5) def test_arepo_cr(): ds = data_dir_load(cr_h5) assert ds.gamma_cr == ds.parameters["GammaCR"] for test in sph_answer(ds, "snapshot_039", 28313510, cr_fields): test_arepo_cr.__name__ = test.description yield test yt-project-yt-f043ac8/yt/frontends/art/000077500000000000000000000000001510711153200200765ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/art/__init__.py000066400000000000000000000000001510711153200221750ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/art/api.py000066400000000000000000000003231510711153200212170ustar00rootroot00000000000000from . import tests from .data_structures import ( ARTDataset, ARTDomainFile, ARTDomainSubset, ARTIndex, DarkMatterARTDataset, ) from .fields import ARTFieldInfo from .io import IOHandlerART yt-project-yt-f043ac8/yt/frontends/art/data_structures.py000066400000000000000000001125421510711153200236710ustar00rootroot00000000000000import glob import os import struct import weakref import numpy as np import yt.utilities.fortran_utils as fpu from yt.data_objects.index_subobjects.octree_subset import OctreeSubset from yt.data_objects.static_output import Dataset, ParticleFile from yt.data_objects.unions import ParticleUnion from yt.frontends.art.definitions import ( amr_header_struct, constants, dmparticle_header_struct, filename_pattern, fluid_fields, particle_fields, particle_header_struct, seek_extras, ) from yt.frontends.art.fields import ARTFieldInfo from yt.frontends.art.io import ( _read_art_level_info, _read_child_level, _read_root_level, a2b, b2t, ) from yt.funcs import mylog, setdefaultattr from yt.geometry.geometry_handler import Index, YTDataChunk from yt.geometry.oct_container import ARTOctreeContainer from yt.geometry.oct_geometry_handler import OctreeIndex from yt.geometry.particle_geometry_handler import ParticleIndex class ARTIndex(OctreeIndex): def __init__(self, ds, dataset_type="art"): self.fluid_field_list = fluid_fields self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self.max_level = ds.max_level self.float_type = np.float64 super().__init__(ds, dataset_type) def get_smallest_dx(self): """ Returns (in code units) the smallest cell size in the simulation. """ # Overloaded ds = self.dataset return (ds.domain_width / ds.domain_dimensions / (2**self.max_level)).min() def _initialize_oct_handler(self): """ Just count the number of octs per domain and allocate the requisite memory in the oct tree """ nv = len(self.fluid_field_list) self.oct_handler = ARTOctreeContainer( self.dataset.domain_dimensions / 2, # dd is # of root cells self.dataset.domain_left_edge, self.dataset.domain_right_edge, 1, ) # The 1 here refers to domain_id == 1 always for ARTIO. self.domains = [ARTDomainFile(self.dataset, nv, self.oct_handler, 1)] self.octs_per_domain = [dom.level_count.sum() for dom in self.domains] self.total_octs = sum(self.octs_per_domain) mylog.debug("Allocating %s octs", self.total_octs) self.oct_handler.allocate_domains(self.octs_per_domain) domain = self.domains[0] domain._read_amr_root(self.oct_handler) domain._read_amr_level(self.oct_handler) self.oct_handler.finalize() def _detect_output_fields(self): self.particle_field_list = list(particle_fields) self.field_list = [("art", f) for f in fluid_fields] # now generate all of the possible particle fields for ptype in self.dataset.particle_types_raw: for pfield in self.particle_field_list: pfn = (ptype, pfield) self.field_list.append(pfn) def _identify_base_chunk(self, dobj): """ Take the passed in data source dobj, and use its embedded selector to calculate the domain mask, build the reduced domain subsets and oct counts. Attach this information to dobj. """ if getattr(dobj, "_chunk_info", None) is None: # Get all octs within this oct handler domains = [dom for dom in self.domains if dom.included(dobj.selector)] base_region = getattr(dobj, "base_region", dobj) if len(domains) > 1: mylog.debug("Identified %s intersecting domains", len(domains)) subsets = [ ARTDomainSubset(base_region, domain, self.dataset) for domain in domains ] dobj._chunk_info = subsets dobj._current_chunk = list(self._chunk_all(dobj))[0] def _chunk_all(self, dobj): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) # We pass the chunk both the current chunk and list of chunks, # as well as the referring data source yield YTDataChunk(dobj, "all", oobjs, None) def _chunk_spatial(self, dobj, ngz, sort=None, preload_fields=None): sobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for og in sobjs: if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og yield YTDataChunk(dobj, "spatial", [g], None) def _chunk_io(self, dobj, cache=True, local_only=False): """ Since subsets are calculated per domain, i.e. per file, yield each domain at a time to organize by IO. We will eventually chunk out NMSU ART to be level-by-level. """ oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for subset in oobjs: yield YTDataChunk(dobj, "io", [subset], None, cache=cache) class ARTDataset(Dataset): _index_class: type[Index] = ARTIndex _field_info_class = ARTFieldInfo def __init__( self, filename, dataset_type="art", fields=None, storage_filename=None, skip_particles=False, skip_stars=False, limit_level=None, spread_age=True, force_max_level=None, file_particle_header=None, file_particle_data=None, file_particle_stars=None, units_override=None, unit_system="cgs", default_species_fields=None, ): self.fluid_types += ("art",) if fields is None: fields = fluid_fields filename = os.path.abspath(filename) self._fields_in_file = fields self._file_amr = filename self._file_particle_header = file_particle_header self._file_particle_data = file_particle_data self._file_particle_stars = file_particle_stars self._find_files(filename) self.skip_particles = skip_particles self.skip_stars = skip_stars self.limit_level = limit_level self.max_level = limit_level self.force_max_level = force_max_level self.spread_age = spread_age Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename def _find_files(self, file_amr): """ Given the AMR base filename, attempt to find the particle header, star files, etc. """ base_prefix, base_suffix = filename_pattern["amr"] numericstr = file_amr.rsplit("_", 1)[1].replace(base_suffix, "") possibles = glob.glob( os.path.join(os.path.dirname(os.path.abspath(file_amr)), "*") ) for filetype, (prefix, suffix) in filename_pattern.items(): # if this attribute is already set skip it if getattr(self, "_file_" + filetype, None) is not None: continue match = None for possible in possibles: if possible.endswith(numericstr + suffix): if os.path.basename(possible).startswith(prefix): match = possible if match is not None: mylog.info("discovered %s:%s", filetype, match) setattr(self, "_file_" + filetype, match) else: setattr(self, "_file_" + filetype, None) def __str__(self): return self._file_amr.split("/")[-1] def _set_code_unit_attributes(self): """ Generates the conversion to various physical units based on the parameters from the header """ # spatial units z = self.current_redshift h = self.hubble_constant boxcm_cal = self.parameters["boxh"] boxcm_uncal = boxcm_cal / h box_proper = boxcm_uncal / (1 + z) aexpn = self.parameters["aexpn"] # all other units Om0 = self.parameters["Om0"] ng = self.parameters["ng"] boxh = self.parameters["boxh"] aexpn = self.parameters["aexpn"] hubble = self.parameters["hubble"] r0 = boxh / ng v0 = 50.0 * r0 * np.sqrt(Om0) rho0 = 2.776e11 * hubble**2.0 * Om0 aM0 = rho0 * (boxh / hubble) ** 3.0 / ng**3.0 velocity = v0 / aexpn * 1.0e5 # proper cm/s mass = aM0 * 1.98892e33 self.cosmological_simulation = True setdefaultattr(self, "mass_unit", self.quan(mass, f"g*{ng ** 3}")) setdefaultattr(self, "length_unit", self.quan(box_proper, "Mpc")) setdefaultattr(self, "velocity_unit", self.quan(velocity, "cm/s")) setdefaultattr(self, "time_unit", self.length_unit / self.velocity_unit) def _parse_parameter_file(self): """ Get the various simulation parameters & constants. """ self.domain_left_edge = np.zeros(3, dtype="float") self.domain_right_edge = np.zeros(3, dtype="float") + 1.0 self.dimensionality = 3 self.refine_by = 2 self._periodicity = (True, True, True) self.cosmological_simulation = True self.parameters = {} self.parameters.update(constants) self.parameters["Time"] = 1.0 # read the amr header with open(self._file_amr, "rb") as f: amr_header_vals = fpu.read_attrs(f, amr_header_struct, ">") n_to_skip = len(("tl", "dtl", "tlold", "dtlold", "iSO")) fpu.skip(f, n_to_skip, endian=">") (self.ncell) = fpu.read_vector(f, "i", ">")[0] # Try to figure out the root grid dimensions est = int(np.rint(self.ncell ** (1.0 / 3.0))) # Note here: this is the number of *cells* on the root grid. # This is not the same as the number of Octs. # domain dimensions is the number of root *cells* self.domain_dimensions = np.ones(3, dtype="int64") * est self.root_grid_mask_offset = f.tell() self.root_nocts = self.domain_dimensions.prod() // 8 self.root_ncells = self.root_nocts * 8 mylog.debug( "Estimating %i cells on a root grid side, %i root octs", est, self.root_nocts, ) self.root_iOctCh = fpu.read_vector(f, "i", ">")[: self.root_ncells] self.root_iOctCh = self.root_iOctCh.reshape( self.domain_dimensions, order="F" ) self.root_grid_offset = f.tell() self.root_nhvar = fpu.skip(f, endian=">") self.root_nvar = fpu.skip(f, endian=">") # make sure that the number of root variables is a multiple of # rootcells assert self.root_nhvar % self.root_ncells == 0 assert self.root_nvar % self.root_ncells == 0 self.nhydro_variables = ( self.root_nhvar + self.root_nvar ) / self.root_ncells self.iOctFree, self.nOct = fpu.read_vector(f, "i", ">") self.child_grid_offset = f.tell() # lextra needs to be loaded as a string, but it's actually # array values. So pop it off here, and then re-insert. lextra = amr_header_vals.pop("lextra") amr_header_vals["lextra"] = np.frombuffer(lextra, ">f4") self.parameters.update(amr_header_vals) amr_header_vals = None # estimate the root level float_center, fl, iocts, nocts, root_level = _read_art_level_info( f, [0, self.child_grid_offset], 1, coarse_grid=self.domain_dimensions[0] ) del float_center, fl, iocts, nocts self.root_level = root_level mylog.info("Using root level of %02i", self.root_level) # read the particle header self.particle_types = [] self.particle_types_raw = () if not self.skip_particles and self._file_particle_header: with open(self._file_particle_header, "rb") as fh: particle_header_vals = fpu.read_attrs(fh, particle_header_struct, ">") fh.seek(seek_extras) n = particle_header_vals["Nspecies"] wspecies = np.fromfile(fh, dtype=">f", count=10) lspecies = np.fromfile(fh, dtype=">i", count=10) # extras needs to be loaded as a string, but it's actually # array values. So pop it off here, and then re-insert. extras = particle_header_vals.pop("extras") particle_header_vals["extras"] = np.frombuffer(extras, ">f4") self.parameters["wspecies"] = wspecies[:n] self.parameters["lspecies"] = lspecies[:n] for specie in range(n): self.particle_types.append("specie%i" % specie) self.particle_types_raw = tuple(self.particle_types) ls_nonzero = np.diff(lspecies)[: n - 1] ls_nonzero = np.append(lspecies[0], ls_nonzero) self.star_type = len(ls_nonzero) mylog.info("Discovered %i species of particles", len(ls_nonzero)) info_str = "Particle populations: " + "%9i " * len(ls_nonzero) mylog.info(info_str, *ls_nonzero) self._particle_type_counts = dict( zip(self.particle_types_raw, ls_nonzero, strict=True) ) for k, v in particle_header_vals.items(): if k in self.parameters.keys(): if not self.parameters[k] == v: mylog.info( "Inconsistent parameter %s %1.1e %1.1e", k, v, self.parameters[k], ) else: self.parameters[k] = v self.parameters_particles = particle_header_vals self.parameters.update(particle_header_vals) self.parameters["ng"] = self.parameters["Ngridc"] self.parameters["ncell0"] = self.parameters["ng"] ** 3 # setup standard simulation params yt expects to see self.current_redshift = self.parameters["aexpn"] ** -1.0 - 1.0 self.omega_lambda = self.parameters["Oml0"] self.omega_matter = self.parameters["Om0"] self.hubble_constant = self.parameters["hubble"] self.min_level = self.parameters["min_level"] self.max_level = self.parameters["max_level"] if self.limit_level is not None: self.max_level = min(self.limit_level, self.parameters["max_level"]) if self.force_max_level is not None: self.max_level = self.force_max_level self.hubble_time = 1.0 / (self.hubble_constant * 100 / 3.08568025e19) self.current_time = self.quan(b2t(self.parameters["t"]), "Gyr") self.gamma = self.parameters["gamma"] mylog.info("Max level is %02i", self.max_level) def create_field_info(self): super().create_field_info() if "wspecies" in self.parameters: # We create dark_matter and stars unions. ptr = self.particle_types_raw pu = ParticleUnion("darkmatter", list(ptr[:-1])) self.add_particle_union(pu) pu = ParticleUnion("stars", list(ptr[-1:])) self.add_particle_union(pu) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: """ Defined for the NMSU file naming scheme. This could differ for other formats. """ f = str(filename) prefix, suffix = filename_pattern["amr"] if not os.path.isfile(f): return False if not f.endswith(suffix): return False with open(f, "rb") as fh: try: fpu.read_attrs(fh, amr_header_struct, ">") return True except Exception: return False class ARTParticleFile(ParticleFile): def __init__(self, ds, io, filename, file_id): super().__init__(ds, io, filename, file_id, range=None) self.total_particles = {} for ptype, count in zip( ds.particle_types_raw, ds.parameters["total_particles"], strict=True, ): self.total_particles[ptype] = count with open(filename, "rb") as f: f.seek(0, os.SEEK_END) self._file_size = f.tell() class ARTParticleIndex(ParticleIndex): def _setup_filenames(self): # no need for template, all data in one file template = self.dataset.filename_template ndoms = self.dataset.file_count cls = self.dataset._file_class self.data_files = [] fi = 0 for i in range(int(ndoms)): df = cls(self.dataset, self.io, template % {"num": i}, fi) fi += 1 self.data_files.append(df) class DarkMatterARTDataset(ARTDataset): _index_class = ARTParticleIndex _file_class = ARTParticleFile filter_bbox = False def __init__( self, filename, dataset_type="dm_art", fields=None, storage_filename=None, skip_particles=False, skip_stars=False, limit_level=None, spread_age=True, force_max_level=None, file_particle_header=None, file_particle_stars=None, units_override=None, unit_system="cgs", ): self.num_zones = 2 self.n_ref = 64 self.particle_types += ("all",) if fields is None: fields = particle_fields filename = os.path.abspath(filename) self._fields_in_file = fields self._file_particle = filename self._file_particle_header = file_particle_header self._find_files(filename) self.skip_stars = skip_stars self.spread_age = spread_age Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename def _find_files(self, file_particle): """ Given the particle base filename, attempt to find the particle header and star files. """ base_prefix, base_suffix = filename_pattern["particle_data"] aexpstr = file_particle.rsplit("s0", 1)[1].replace(base_suffix, "") possibles = glob.glob( os.path.join(os.path.dirname(os.path.abspath(file_particle)), "*") ) for filetype, (prefix, suffix) in filename_pattern.items(): # if this attribute is already set skip it if getattr(self, "_file_" + filetype, None) is not None: continue match = None for possible in possibles: if possible.endswith(aexpstr + suffix): if os.path.basename(possible).startswith(prefix): match = possible if match is not None: mylog.info("discovered %s:%s", filetype, match) setattr(self, "_file_" + filetype, match) else: setattr(self, "_file_" + filetype, None) def __str__(self): return self._file_particle.split("/")[-1] def _set_code_unit_attributes(self): """ Generates the conversion to various physical units based on the parameters from the header """ # spatial units z = self.current_redshift h = self.hubble_constant boxcm_cal = self.parameters["boxh"] boxcm_uncal = boxcm_cal / h box_proper = boxcm_uncal / (1 + z) aexpn = self.parameters["aexpn"] # all other units Om0 = self.parameters["Om0"] ng = self.parameters["ng"] boxh = self.parameters["boxh"] aexpn = self.parameters["aexpn"] hubble = self.parameters["hubble"] r0 = boxh / ng rho0 = 2.776e11 * hubble**2.0 * Om0 aM0 = rho0 * (boxh / hubble) ** 3.0 / ng**3.0 velocity = 100.0 * r0 / aexpn * 1.0e5 # proper cm/s mass = aM0 * 1.98892e33 self.cosmological_simulation = True self.mass_unit = self.quan(mass, f"g*{ng ** 3}") self.length_unit = self.quan(box_proper, "Mpc") self.velocity_unit = self.quan(velocity, "cm/s") self.time_unit = self.length_unit / self.velocity_unit def _parse_parameter_file(self): """ Get the various simulation parameters & constants. """ self.domain_left_edge = np.zeros(3, dtype="float") self.domain_right_edge = np.zeros(3, dtype="float") + 1.0 self.dimensionality = 3 self.refine_by = 2 self._periodicity = (True, True, True) self.cosmological_simulation = True self.parameters = {} self.parameters.update(constants) self.parameters["Time"] = 1.0 self.file_count = 1 self.filename_template = self.parameter_filename # read the particle header self.particle_types = [] self.particle_types_raw = () assert self._file_particle_header with open(self._file_particle_header, "rb") as fh: seek = 4 fh.seek(seek) headerstr = fh.read(45).decode("ascii") aexpn = np.fromfile(fh, count=1, dtype=">f4") aexp0 = np.fromfile(fh, count=1, dtype=">f4") amplt = np.fromfile(fh, count=1, dtype=">f4") astep = np.fromfile(fh, count=1, dtype=">f4") istep = np.fromfile(fh, count=1, dtype=">i4") partw = np.fromfile(fh, count=1, dtype=">f4") tintg = np.fromfile(fh, count=1, dtype=">f4") ekin = np.fromfile(fh, count=1, dtype=">f4") ekin1 = np.fromfile(fh, count=1, dtype=">f4") ekin2 = np.fromfile(fh, count=1, dtype=">f4") au0 = np.fromfile(fh, count=1, dtype=">f4") aeu0 = np.fromfile(fh, count=1, dtype=">f4") nrowc = np.fromfile(fh, count=1, dtype=">i4") ngridc = np.fromfile(fh, count=1, dtype=">i4") nspecs = np.fromfile(fh, count=1, dtype=">i4") nseed = np.fromfile(fh, count=1, dtype=">i4") Om0 = np.fromfile(fh, count=1, dtype=">f4") Oml0 = np.fromfile(fh, count=1, dtype=">f4") hubble = np.fromfile(fh, count=1, dtype=">f4") Wp5 = np.fromfile(fh, count=1, dtype=">f4") Ocurv = np.fromfile(fh, count=1, dtype=">f4") wspecies = np.fromfile(fh, count=10, dtype=">f4") lspecies = np.fromfile(fh, count=10, dtype=">i4") extras = np.fromfile(fh, count=79, dtype=">f4") boxsize = np.fromfile(fh, count=1, dtype=">f4") n = nspecs[0] particle_header_vals = {} tmp = [ headerstr, aexpn, aexp0, amplt, astep, istep, partw, tintg, ekin, ekin1, ekin2, au0, aeu0, nrowc, ngridc, nspecs, nseed, Om0, Oml0, hubble, Wp5, Ocurv, wspecies, lspecies, extras, boxsize, ] for i, arr in enumerate(tmp): a1 = dmparticle_header_struct[0][i] a2 = dmparticle_header_struct[1][i] if a2 == 1: particle_header_vals[a1] = arr[0] else: particle_header_vals[a1] = arr[:a2] for specie in range(n): self.particle_types.append("specie%i" % specie) self.particle_types_raw = tuple(self.particle_types) ls_nonzero = np.diff(lspecies)[: n - 1] ls_nonzero = np.append(lspecies[0], ls_nonzero) self.star_type = len(ls_nonzero) mylog.info("Discovered %i species of particles", len(ls_nonzero)) info_str = "Particle populations: " + "%9i " * len(ls_nonzero) mylog.info(info_str, *ls_nonzero) for k, v in particle_header_vals.items(): if k in self.parameters.keys(): if not self.parameters[k] == v: mylog.info( "Inconsistent parameter %s %1.1e %1.1e", k, v, self.parameters[k], ) else: self.parameters[k] = v self.parameters_particles = particle_header_vals self.parameters.update(particle_header_vals) self.parameters["wspecies"] = wspecies[:n] self.parameters["lspecies"] = lspecies[:n] self.parameters["ng"] = self.parameters["Ngridc"] self.parameters["ncell0"] = self.parameters["ng"] ** 3 self.parameters["boxh"] = self.parameters["boxsize"] self.parameters["total_particles"] = ls_nonzero self.domain_dimensions = np.ones(3, dtype="int64") * 2 # NOT ng # setup standard simulation params yt expects to see # Convert to float to please unyt self.current_redshift = float(self.parameters["aexpn"] ** -1.0 - 1.0) self.omega_lambda = float(particle_header_vals["Oml0"]) self.omega_matter = float(particle_header_vals["Om0"]) self.hubble_constant = float(particle_header_vals["hubble"]) self.min_level = 0 self.max_level = 0 # self.min_level = particle_header_vals['min_level'] # self.max_level = particle_header_vals['max_level'] # if self.limit_level is not None: # self.max_level = min( # self.limit_level, particle_header_vals['max_level']) # if self.force_max_level is not None: # self.max_level = self.force_max_level self.hubble_time = 1.0 / (self.hubble_constant * 100 / 3.08568025e19) self.parameters["t"] = a2b(self.parameters["aexpn"]) self.current_time = self.quan(b2t(self.parameters["t"]), "Gyr") self.gamma = self.parameters["gamma"] mylog.info("Max level is %02i", self.max_level) def create_field_info(self): super(ARTDataset, self).create_field_info() ptr = self.particle_types_raw pu = ParticleUnion("darkmatter", list(ptr)) self.add_particle_union(pu) pass @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: """ Defined for the NMSU file naming scheme. This could differ for other formats. """ f = str(filename) prefix, suffix = filename_pattern["particle_data"] if not os.path.isfile(f): return False if not f.endswith(suffix): return False if "s0" not in f: # ATOMIC.DAT, for instance, passes the other tests, but then dies # during _find_files because it can't be split. return False with open(f, "rb") as fh: try: amr_prefix, amr_suffix = filename_pattern["amr"] possibles = glob.glob( os.path.join(os.path.dirname(os.path.abspath(f)), "*") ) for possible in possibles: if possible.endswith(amr_suffix): if os.path.basename(possible).startswith(amr_prefix): return False except Exception: pass try: seek = 4 fh.seek(seek) headerstr = np.fromfile(fh, count=1, dtype=(str, 45)) # NOQA aexpn = np.fromfile(fh, count=1, dtype=">f4") # NOQA aexp0 = np.fromfile(fh, count=1, dtype=">f4") # NOQA amplt = np.fromfile(fh, count=1, dtype=">f4") # NOQA astep = np.fromfile(fh, count=1, dtype=">f4") # NOQA istep = np.fromfile(fh, count=1, dtype=">i4") # NOQA partw = np.fromfile(fh, count=1, dtype=">f4") # NOQA tintg = np.fromfile(fh, count=1, dtype=">f4") # NOQA ekin = np.fromfile(fh, count=1, dtype=">f4") # NOQA ekin1 = np.fromfile(fh, count=1, dtype=">f4") # NOQA ekin2 = np.fromfile(fh, count=1, dtype=">f4") # NOQA au0 = np.fromfile(fh, count=1, dtype=">f4") # NOQA aeu0 = np.fromfile(fh, count=1, dtype=">f4") # NOQA nrowc = np.fromfile(fh, count=1, dtype=">i4") # NOQA ngridc = np.fromfile(fh, count=1, dtype=">i4") # NOQA nspecs = np.fromfile(fh, count=1, dtype=">i4") # NOQA nseed = np.fromfile(fh, count=1, dtype=">i4") # NOQA Om0 = np.fromfile(fh, count=1, dtype=">f4") # NOQA Oml0 = np.fromfile(fh, count=1, dtype=">f4") # NOQA hubble = np.fromfile(fh, count=1, dtype=">f4") # NOQA Wp5 = np.fromfile(fh, count=1, dtype=">f4") # NOQA Ocurv = np.fromfile(fh, count=1, dtype=">f4") # NOQA wspecies = np.fromfile(fh, count=10, dtype=">f4") # NOQA lspecies = np.fromfile(fh, count=10, dtype=">i4") # NOQA extras = np.fromfile(fh, count=79, dtype=">f4") # NOQA boxsize = np.fromfile(fh, count=1, dtype=">f4") # NOQA return True except Exception: return False class ARTDomainSubset(OctreeSubset): @property def oct_handler(self): return self.domain.oct_handler def fill(self, content, ftfields, selector): """ This is called from IOHandler. It takes content which is a binary stream, reads the requested field over this while domain. It then uses oct_handler fill to reorganize values from IO read index order to the order they are in in the octhandler. """ oct_handler = self.oct_handler all_fields = self.domain.ds.index.fluid_field_list fields = [f for ft, f in ftfields] field_idxs = [all_fields.index(f) for f in fields] source, tr = {}, {} cell_count = selector.count_oct_cells(self.oct_handler, self.domain_id) levels, cell_inds, file_inds = self.oct_handler.file_index_octs( selector, self.domain_id, cell_count ) for field in fields: tr[field] = np.zeros(cell_count, "float64") data = _read_root_level( content, self.domain.level_child_offsets, self.domain.level_count ) ns = (self.domain.ds.domain_dimensions.prod() // 8, 8) for field, fi in zip(fields, field_idxs, strict=True): source[field] = np.empty(ns, dtype="float64", order="C") dt = data[fi, :].reshape(self.domain.ds.domain_dimensions, order="F") for i in range(2): for j in range(2): for k in range(2): ii = ((k * 2) + j) * 2 + i # Note: C order because our index converts C to F. source[field][:, ii] = dt[i::2, j::2, k::2].ravel(order="C") oct_handler.fill_level(0, levels, cell_inds, file_inds, tr, source) del source # Now we continue with the additional levels. for level in range(1, self.ds.index.max_level + 1): no = self.domain.level_count[level] noct_range = [0, no] source = _read_child_level( content, self.domain.level_child_offsets, self.domain.level_offsets, self.domain.level_count, level, fields, self.domain.ds.domain_dimensions, self.domain.ds.parameters["ncell0"], noct_range=noct_range, ) oct_handler.fill_level(level, levels, cell_inds, file_inds, tr, source) return tr class ARTDomainFile: """ Read in the AMR, left/right edges, fill out the octhandler """ # We already read in the header in static output, # and since these headers are defined in only a single file it's # best to leave them in the static output _last_mask = None _last_selector_id = None def __init__(self, ds, nvar, oct_handler, domain_id): self.nvar = nvar self.ds = ds self.domain_id = domain_id self._level_count = None self._level_oct_offsets = None self._level_child_offsets = None self._oct_handler = oct_handler @property def oct_handler(self): return self._oct_handler @property def level_count(self): # this is number of *octs* if self._level_count is not None: return self._level_count self.level_offsets return self._level_count @property def level_child_offsets(self): if self._level_count is not None: return self._level_child_offsets self.level_offsets return self._level_child_offsets @property def level_offsets(self): # this is used by the IO operations to find the file offset, # and then start reading to fill values # note that this is called hydro_offset in ramses if self._level_oct_offsets is not None: return self._level_oct_offsets # We now have to open the file and calculate it f = open(self.ds._file_amr, "rb") ( nhydrovars, inoll, _level_oct_offsets, _level_child_offsets, ) = self._count_art_octs( f, self.ds.child_grid_offset, self.ds.min_level, self.ds.max_level ) # remember that the root grid is by itself; manually add it back in inoll[0] = self.ds.domain_dimensions.prod() // 8 _level_child_offsets[0] = self.ds.root_grid_offset self.nhydrovars = nhydrovars self.inoll = inoll # number of octs self._level_oct_offsets = _level_oct_offsets self._level_child_offsets = _level_child_offsets self._level_count = inoll return self._level_oct_offsets def _count_art_octs(self, f, offset, MinLev, MaxLevelNow): level_oct_offsets = [ 0, ] level_child_offsets = [ 0, ] f.seek(offset) nchild, ntot = 8, 0 Level = np.zeros(MaxLevelNow + 1 - MinLev, dtype="int64") iNOLL = np.zeros(MaxLevelNow + 1 - MinLev, dtype="int64") iHOLL = np.zeros(MaxLevelNow + 1 - MinLev, dtype="int64") for Lev in range(MinLev + 1, MaxLevelNow + 1): level_oct_offsets.append(f.tell()) # Get the info for this level, skip the rest # print("Reading oct tree data for level", Lev) # print('offset:',f.tell()) Level[Lev], iNOLL[Lev], iHOLL[Lev] = fpu.read_vector(f, "i", ">") # print('Level %i : '%Lev, iNOLL) # print('offset after level record:',f.tell()) nLevel = iNOLL[Lev] ntot = ntot + nLevel # Skip all the oct hierarchy data ns = fpu.peek_record_size(f, endian=">") size = struct.calcsize(">i") + ns + struct.calcsize(">i") f.seek(f.tell() + size * nLevel) level_child_offsets.append(f.tell()) # Skip the child vars data ns = fpu.peek_record_size(f, endian=">") size = struct.calcsize(">i") + ns + struct.calcsize(">i") f.seek(f.tell() + size * nLevel * nchild) # find nhydrovars nhydrovars = 8 + 2 f.seek(offset) return nhydrovars, iNOLL, level_oct_offsets, level_child_offsets def _read_amr_level(self, oct_handler): """Open the oct file, read in octs level-by-level. For each oct, only the position, index, level and domain are needed - its position in the octree is found automatically. The most important is finding all the information to feed oct_handler.add """ self.level_offsets f = open(self.ds._file_amr, "rb") for level in range(1, self.ds.max_level + 1): unitary_center, fl, iocts, nocts, root_level = _read_art_level_info( f, self._level_oct_offsets, level, coarse_grid=self.ds.domain_dimensions[0], root_level=self.ds.root_level, ) nocts_check = oct_handler.add(self.domain_id, level, unitary_center) assert nocts_check == nocts mylog.debug( "Added %07i octs on level %02i, cumulative is %07i", nocts, level, oct_handler.nocts, ) def _read_amr_root(self, oct_handler): self.level_offsets # add the root *cell* not *oct* mesh root_octs_side = self.ds.domain_dimensions[0] / 2 NX = np.ones(3) * root_octs_side LE = np.array([0.0, 0.0, 0.0], dtype="float64") RE = np.array([1.0, 1.0, 1.0], dtype="float64") root_dx = (RE - LE) / NX LL = LE + root_dx / 2.0 RL = RE - root_dx / 2.0 # compute floating point centers of root octs root_fc = np.mgrid[ LL[0] : RL[0] : NX[0] * 1j, LL[1] : RL[1] : NX[1] * 1j, LL[2] : RL[2] : NX[2] * 1j, ] root_fc = np.vstack([p.ravel() for p in root_fc]).T oct_handler.add(self.domain_id, 0, root_fc) assert oct_handler.nocts == root_fc.shape[0] mylog.debug( "Added %07i octs on level %02i, cumulative is %07i", root_octs_side**3, 0, oct_handler.nocts, ) def included(self, selector): return True yt-project-yt-f043ac8/yt/frontends/art/definitions.py000066400000000000000000000067101510711153200227670ustar00rootroot00000000000000# If not otherwise specified, we are big endian endian = ">" fluid_fields = [ "Density", "TotalEnergy", "XMomentumDensity", "YMomentumDensity", "ZMomentumDensity", "Pressure", "Gamma", "GasEnergy", "MetalDensitySNII", "MetalDensitySNIa", "PotentialNew", "PotentialOld", ] hydro_struct = [("pad1", ">i"), ("idc", ">i"), ("iOctCh", ">i")] for field in fluid_fields: hydro_struct += ((field, ">f"),) hydro_struct += (("pad2", ">i"),) particle_fields = [ "particle_mass", # stars have variable mass "particle_index", "particle_type", "particle_position_x", "particle_position_y", "particle_position_z", "particle_velocity_x", "particle_velocity_y", "particle_velocity_z", "particle_mass_initial", "particle_creation_time", "particle_metallicity1", "particle_metallicity2", "particle_metallicity", ] particle_star_fields = [ "particle_mass", "particle_mass_initial", "particle_creation_time", "particle_metallicity1", "particle_metallicity2", "particle_metallicity", ] filename_pattern = { "amr": ["10MpcBox_", ".d"], "particle_header": ["PMcrd", ".DAT"], "particle_data": ["PMcrs", ".DAT"], "particle_stars": ["stars", ".dat"], } amr_header_struct = [ ("jname", 1, "256s"), (("istep", "t", "dt", "aexpn", "ainit"), 1, "iddff"), (("boxh", "Om0", "Oml0", "Omb0", "hubble"), 5, "f"), ("nextras", 1, "i"), (("extra1", "extra2"), 2, "f"), ("lextra", 1, "512s"), (("min_level", "max_level"), 2, "i"), ] particle_header_struct = [ ( ( "header", "aexpn", "aexp0", "amplt", "astep", "istep", "partw", "tintg", "Ekin", "Ekin1", "Ekin2", "au0", "aeu0", "Nrow", "Ngridc", "Nspecies", "Nseed", "Om0", "Oml0", "hubble", "Wp5", "Ocurv", "Omb0", "extras", "unknown", ), 1, "45sffffi" + "fffffff" + "iiii" + "ffffff" + "396s" + "f", ) ] dmparticle_header_struct = [ ( "header", "aexpn", "aexp0", "amplt", "astep", "istep", "partw", "tintg", "Ekin", "Ekin1", "Ekin2", "au0", "aeu0", "Nrow", "Ngridc", "Nspecies", "Nseed", "Om0", "Oml0", "hubble", "Wp5", "Ocurv", "wspecies", "lspecies", "extras", "boxsize", ), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 10, 79, 1), ] star_struct = [ (">d", ("t_stars", "a_stars")), (">i", "nstars"), (">d", ("ws_old", "ws_oldi")), (">f", "particle_mass"), (">f", "particle_mass_initial"), (">f", "particle_creation_time"), (">f", "particle_metallicity1"), (">f", "particle_metallicity2"), ] star_name_map = { "particle_mass": "mass", "particle_mass_initial": "imass", "particle_creation_time": "tbirth", "particle_metallicity1": "metallicity1", "particle_metallicity2": "metallicity2", "particle_metallicity": "metallicity", } constants = { "Y_p": 0.245, "gamma": 5.0 / 3.0, "T_CMB0": 2.726, "T_min": 300.0, "wmu": 4.0 / (8.0 - 5.0 * 0.245), } seek_extras = 137 yt-project-yt-f043ac8/yt/frontends/art/fields.py000066400000000000000000000171521510711153200217240ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer b_units = "code_magnetic" ra_units = "code_length / code_time**2" rho_units = "code_mass / code_length**3" vel_units = "code_velocity" # NOTE: ARTIO uses momentum density. mom_units = "code_mass / (code_length**2 * code_time)" en_units = "code_mass*code_velocity**2/code_length**3" class ARTFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("Density", (rho_units, ["density"], None)), ("TotalEnergy", (en_units, ["total_energy_density"], None)), ("XMomentumDensity", (mom_units, ["momentum_density_x"], None)), ("YMomentumDensity", (mom_units, ["momentum_density_y"], None)), ("ZMomentumDensity", (mom_units, ["momentum_density_z"], None)), ("Pressure", ("", ["pressure"], None)), # Unused ("Gamma", ("", ["gamma"], None)), ("GasEnergy", (en_units, ["thermal_energy_density"], None)), ("MetalDensitySNII", (rho_units, ["metal_ii_density"], None)), ("MetalDensitySNIa", (rho_units, ["metal_ia_density"], None)), ("PotentialNew", ("", ["potential"], None)), ("PotentialOld", ("", ["gas_potential"], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_velocity_x", (vel_units, [], None)), ("particle_velocity_y", (vel_units, [], None)), ("particle_velocity_z", (vel_units, [], None)), ("particle_mass", ("code_mass", [], None)), ("particle_index", ("", [], None)), ("particle_species", ("", ["particle_type"], None)), ("particle_creation_time", ("Gyr", [], None)), ("particle_mass_initial", ("code_mass", [], None)), ("particle_metallicity1", ("", [], None)), ("particle_metallicity2", ("", [], None)), ) def setup_fluid_fields(self): unit_system = self.ds.unit_system def _temperature(field, data): r0 = data.ds.parameters["boxh"] / data.ds.parameters["ng"] tr = data.ds.quan(3.03e5 * r0**2, "K/code_velocity**2") tr *= data.ds.parameters["wmu"] * data.ds.parameters["Om0"] tr *= data.ds.parameters["gamma"] - 1.0 tr /= data.ds.parameters["aexpn"] ** 2 return tr * data["art", "GasEnergy"] / data["art", "Density"] self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) def _get_vel(axis): def velocity(field, data): return data["gas", f"momentum_density_{axis}"] / data["gas", "density"] return velocity for ax in "xyz": self.add_field( ("gas", f"velocity_{ax}"), sampling_type="cell", function=_get_vel(ax), units=unit_system["velocity"], ) def _momentum_magnitude(field, data): tr = ( data["gas", "momentum_density_x"] ** 2 + data["gas", "momentum_density_y"] ** 2 + data["gas", "momentum_density_z"] ** 2 ) ** 0.5 tr *= data["index", "cell_volume"].in_units("cm**3") return tr self.add_field( ("gas", "momentum_magnitude"), sampling_type="cell", function=_momentum_magnitude, units=unit_system["momentum"], ) def _velocity_magnitude(field, data): tr = data["gas", "momentum_magnitude"] tr /= data["gas", "cell_mass"] return tr self.add_field( ("gas", "velocity_magnitude"), sampling_type="cell", function=_velocity_magnitude, units=unit_system["velocity"], ) def _metal_density(field, data): tr = data["gas", "metal_ia_density"] tr += data["gas", "metal_ii_density"] return tr self.add_field( ("gas", "metal_density"), sampling_type="cell", function=_metal_density, units=unit_system["density"], ) def _metal_mass_fraction(field, data): tr = data["gas", "metal_density"] tr /= data["gas", "density"] return tr self.add_field( ("gas", "metal_mass_fraction"), sampling_type="cell", function=_metal_mass_fraction, units="", ) def _H_mass_fraction(field, data): tr = 1.0 - data.ds.parameters["Y_p"] - data["gas", "metal_mass_fraction"] return tr self.add_field( ("gas", "H_mass_fraction"), sampling_type="cell", function=_H_mass_fraction, units="", ) def _metallicity(field, data): tr = data["gas", "metal_mass_fraction"] tr /= data["gas", "H_mass_fraction"] return tr self.add_field( ("gas", "metallicity"), sampling_type="cell", function=_metallicity, units="", ) atoms = [ "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", ] def _specific_metal_density_function(atom): def _specific_metal_density(field, data): nucleus_densityIa = ( data["gas", "metal_ia_density"] * SNIa_abundance[atom] ) nucleus_densityII = ( data["gas", "metal_ii_density"] * SNII_abundance[atom] ) return nucleus_densityIa + nucleus_densityII return _specific_metal_density for atom in atoms: self.add_field( ("gas", f"{atom}_nuclei_mass_density"), sampling_type="cell", function=_specific_metal_density_function(atom), units=unit_system["density"], ) # based on Iwamoto et al 1999 # mass fraction of each atom in SNIa metal SNIa_abundance = { "H": 0.00e00, "He": 0.00e00, "C": 3.52e-02, "N": 8.47e-07, "O": 1.04e-01, "F": 4.14e-10, "Ne": 3.30e-03, "Na": 4.61e-05, "Mg": 6.25e-03, "Al": 7.19e-04, "Si": 1.14e-01, "P": 2.60e-04, "S": 6.35e-02, "Cl": 1.27e-04, "Ar": 1.14e-02, "K": 5.72e-05, "Ca": 8.71e-03, "Sc": 1.61e-07, "Ti": 2.50e-04, "V": 5.46e-05, "Cr": 6.19e-03, "Mn": 6.47e-03, "Fe": 5.46e-01, "Co": 7.59e-04, "Ni": 9.17e-02, "Cu": 2.19e-06, "Zn": 2.06e-05, } # mass fraction of each atom in SNII metal SNII_abundance = { "H": 0.00e00, "He": 0.00e00, "C": 3.12e-02, "N": 6.15e-04, "O": 7.11e-01, "F": 4.57e-10, "Ne": 9.12e-02, "Na": 2.56e-03, "Mg": 4.84e-02, "Al": 5.83e-03, "Si": 4.81e-02, "P": 4.77e-04, "S": 1.62e-02, "Cl": 4.72e-05, "Ar": 3.15e-03, "K": 2.65e-05, "Ca": 2.31e-03, "Sc": 9.02e-08, "Ti": 5.18e-05, "V": 3.94e-06, "Cr": 5.18e-04, "Mn": 1.52e-04, "Fe": 3.58e-02, "Co": 2.86e-05, "Ni": 2.35e-03, "Cu": 4.90e-07, "Zn": 7.46e-06, } yt-project-yt-f043ac8/yt/frontends/art/io.py000066400000000000000000000541631510711153200210700ustar00rootroot00000000000000import os import os.path from collections import defaultdict from functools import partial import numpy as np from yt.frontends.art.definitions import ( hydro_struct, particle_fields, particle_star_fields, star_struct, ) from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.fortran_utils import read_vector, skip from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog class IOHandlerART(BaseIOHandler): _dataset_type = "art" tb, ages = None, None cache = None masks = None caching = True def __init__(self, *args, **kwargs): self.cache = {} self.masks = {} super().__init__(*args, **kwargs) self.ws = self.ds.parameters["wspecies"] self.ls = self.ds.parameters["lspecies"] self.file_particle = self.ds._file_particle_data self.file_stars = self.ds._file_particle_stars self.Nrow = self.ds.parameters["Nrow"] def _read_fluid_selection(self, chunks, selector, fields, size): # Chunks in this case will have affiliated domain subset objects # Each domain subset will contain a hydro_offset array, which gives # pointers to level-by-level hydro information tr = defaultdict(list) cp = 0 for chunk in chunks: for subset in chunk.objs: # Now we read the entire thing f = open(subset.domain.ds._file_amr, "rb") # This contains the boundary information, so we skim through # and pick off the right vectors rv = subset.fill(f, fields, selector) for ft, f in fields: d = rv.pop(f) mylog.debug( "Filling %s with %s (%0.3e %0.3e) (%s:%s)", f, d.size, d.min(), d.max(), cp, cp + d.size, ) tr[ft, f].append(d) cp += d.size d = {} for field in fields: d[field] = np.concatenate(tr.pop(field)) return d def _get_mask(self, selector, ftype): key = (selector, ftype) if key in self.masks.keys() and self.caching: return self.masks[key] pstr = "particle_position_%s" x, y, z = (self._get_field((ftype, pstr % ax)) for ax in "xyz") mask = selector.select_points(x, y, z, 0.0) if self.caching: self.masks[key] = mask return self.masks[key] else: return mask def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) for _chunk in chunks: for ptype in sorted(ptf): x = self._get_field((ptype, "particle_position_x")) y = self._get_field((ptype, "particle_position_y")) z = self._get_field((ptype, "particle_position_z")) yield ptype, (x, y, z), 0.0 def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) for _chunk in chunks: for ptype, field_list in sorted(ptf.items()): x = self._get_field((ptype, "particle_position_x")) y = self._get_field((ptype, "particle_position_y")) z = self._get_field((ptype, "particle_position_z")) mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: data = self._get_field((ptype, field)) yield (ptype, field), data[mask] def _get_field(self, field): if field in self.cache.keys() and self.caching: mylog.debug("Cached %s", str(field)) return self.cache[field] mylog.debug("Reading %s", str(field)) tr = {} ftype, fname = field ptmax = self.ws[-1] pbool, idxa, idxb = _determine_field_size(self.ds, ftype, self.ls, ptmax) npa = idxb - idxa sizes = np.diff(np.concatenate(([0], self.ls))) rp = partial( read_particles, self.file_particle, self.Nrow, idxa=idxa, idxb=idxb ) for ax in "xyz": if fname.startswith(f"particle_position_{ax}"): dd = self.ds.domain_dimensions[0] off = 1.0 / dd tr[field] = rp(fields=[ax])[0] / dd - off if fname.startswith(f"particle_velocity_{ax}"): (tr[field],) = rp(fields=["v" + ax]) if fname.startswith("particle_mass"): a = 0 data = np.zeros(npa, dtype="f8") for ptb, size, m in zip(pbool, sizes, self.ws, strict=True): if ptb: data[a : a + size] = m a += size tr[field] = data elif fname == "particle_index": tr[field] = np.arange(idxa, idxb) elif fname == "particle_type": a = 0 data = np.zeros(npa, dtype="int64") for i, (ptb, size) in enumerate(zip(pbool, sizes, strict=True)): if ptb: data[a : a + size] = i a += size tr[field] = data if pbool[-1] and fname in particle_star_fields: data = read_star_field(self.file_stars, field=fname) temp = tr.get(field, np.zeros(npa, "f8")) nstars = self.ls[-1] - self.ls[-2] if nstars > 0: temp[-nstars:] = data tr[field] = temp if fname == "particle_creation_time": self.tb, self.ages, data = interpolate_ages( tr[field][-nstars:], self.file_stars, self.tb, self.ages, self.ds.current_time, ) temp = tr.get(field, np.zeros(npa, "f8")) temp[-nstars:] = data tr[field] = temp del data # We check again, after it's been filled if fname.startswith("particle_mass"): # We now divide by NGrid in order to make this match up. Note that # this means that even when requested in *code units*, we are # giving them as modified by the ng value. This only works for # dark_matter -- stars are regular matter. tr[field] /= self.ds.domain_dimensions.prod() if tr == {}: tr = {f: np.array([]) for f in [field]} if self.caching: self.cache[field] = tr[field] return self.cache[field] else: return tr[field] class IOHandlerDarkMatterART(IOHandlerART): _dataset_type = "dm_art" def _count_particles(self, data_file): return { k: self.ds.parameters["lspecies"][i] for i, k in enumerate(self.ds.particle_types_raw) } def _identify_fields(self, domain): field_list = [] self.particle_field_list = list(particle_fields) for ptype in self.ds.particle_types_raw: for pfield in self.particle_field_list: pfn = (ptype, pfield) field_list.append(pfn) return field_list, {} def _get_field(self, field): if field in self.cache.keys() and self.caching: mylog.debug("Cached %s", str(field)) return self.cache[field] mylog.debug("Reading %s", str(field)) tr = {} ftype, fname = field ptmax = self.ws[-1] pbool, idxa, idxb = _determine_field_size(self.ds, ftype, self.ls, ptmax) npa = idxb - idxa sizes = np.diff(np.concatenate(([0], self.ls))) rp = partial( read_particles, self.file_particle, self.Nrow, idxa=idxa, idxb=idxb ) for ax in "xyz": if fname.startswith(f"particle_position_{ax}"): # This is not the same as domain_dimensions dd = self.ds.parameters["ng"] off = 1.0 / dd tr[field] = rp(fields=[ax])[0] / dd - off if fname.startswith(f"particle_velocity_{ax}"): (tr[field],) = rp(["v" + ax]) if fname.startswith("particle_mass"): a = 0 data = np.zeros(npa, dtype="f8") for ptb, size, m in zip(pbool, sizes, self.ws, strict=True): if ptb: data[a : a + size] = m a += size tr[field] = data elif fname == "particle_index": tr[field] = np.arange(idxa, idxb) elif fname == "particle_type": a = 0 data = np.zeros(npa, dtype="int64") for i, (ptb, size) in enumerate(zip(pbool, sizes, strict=True)): if ptb: data[a : a + size] = i a += size tr[field] = data # We check again, after it's been filled if fname.startswith("particle_mass"): # We now divide by NGrid in order to make this match up. Note that # this means that even when requested in *code units*, we are # giving them as modified by the ng value. This only works for # dark_matter -- stars are regular matter. tr[field] /= self.ds.domain_dimensions.prod() if tr == {}: tr[field] = np.array([]) if self.caching: self.cache[field] = tr[field] return self.cache[field] else: return tr[field] def _yield_coordinates(self, data_file): for ptype in self.ds.particle_types_raw: x = self._get_field((ptype, "particle_position_x")) y = self._get_field((ptype, "particle_position_y")) z = self._get_field((ptype, "particle_position_z")) yield ptype, np.stack((x, y, z), axis=-1) def _determine_field_size(pf, field, lspecies, ptmax): pbool = np.zeros(len(lspecies), dtype="bool") idxas = np.concatenate( ( [ 0, ], lspecies[:-1], ) ) idxbs = lspecies if "specie" in field: index = int(field.replace("specie", "")) pbool[index] = True else: raise RuntimeError idxa, idxb = idxas[pbool][0], idxbs[pbool][-1] return pbool, idxa, idxb def interpolate_ages( data, file_stars, interp_tb=None, interp_ages=None, current_time=None ): if interp_tb is None: t_stars, a_stars = read_star_field(file_stars, field="t_stars") # timestamp of file should match amr timestamp if current_time: tdiff = YTQuantity(b2t(t_stars), "Gyr") - current_time.in_units("Gyr") if np.abs(tdiff) > 1e-4: mylog.info("Timestamp mismatch in star particle header: %s", tdiff) mylog.info("Interpolating ages") interp_tb, interp_ages = b2t(data) interp_tb = YTArray(interp_tb, "Gyr") interp_ages = YTArray(interp_ages, "Gyr") temp = np.interp(data, interp_tb, interp_ages) return interp_tb, interp_ages, temp def _read_art_level_info( f, level_oct_offsets, level, coarse_grid=128, ncell0=None, root_level=None ): pos = f.tell() f.seek(level_oct_offsets[level]) # Get the info for this level, skip the rest junk, nLevel, iOct = read_vector(f, "i", ">") # fortran indices start at 1 # Skip all the oct index data le = np.zeros((nLevel, 3), dtype="int64") fl = np.ones((nLevel, 6), dtype="int64") iocts = np.zeros(nLevel + 1, dtype="int64") idxa, idxb = 0, 0 chunk = int(1e6) # this is ~111MB for 15 dimensional 64 bit arrays left = nLevel while left > 0: this_chunk = min(chunk, left) idxb = idxa + this_chunk data = np.fromfile(f, dtype=">i", count=this_chunk * 15) data = data.reshape(this_chunk, 15) left -= this_chunk le[idxa:idxb, :] = data[:, 1:4] fl[idxa:idxb, 1] = np.arange(idxa, idxb) # pad byte is last, LL2, then ioct right before it iocts[idxa:idxb] = data[:, -3] idxa = idxa + this_chunk del data # emulate fortran code # do ic1 = 1 , nLevel # read(19) (iOctPs(i,iOct),i=1,3),(iOctNb(i,iOct),i=1,6), # & iOctPr(iOct), iOctLv(iOct), iOctLL1(iOct), # & iOctLL2(iOct) # iOct = iOctLL1(iOct) # ioct always represents the index of the next variable # not the current, so shift forward one index # the last index isn't used iocts[1:] = iocts[:-1] # shift iocts = iocts[:nLevel] # chop off the last, unused, index iocts[0] = iOct # starting value # now correct iocts for fortran indices start @ 1 iocts = iocts - 1 assert np.unique(iocts).shape[0] == nLevel # left edges are expressed as if they were on # level 15, so no matter what level max(le)=2**15 # correct to the yt convention # le = le/2**(root_level-1-level)-1 # try to find the root_level first def cfc(root_level, level, le): d_x = 1.0 / (2.0 ** (root_level - level + 1)) fc = (d_x * le) - 2 ** (level - 1) return fc if root_level is None: root_level = np.floor(np.log2(le.max() * 1.0 / coarse_grid)) root_level = root_level.astype("int64") for _ in range(10): fc = cfc(root_level, level, le) go = np.diff(np.unique(fc)).min() < 1.1 if go: break root_level += 1 else: fc = cfc(root_level, level, le) unitary_center = fc / (coarse_grid * 2.0 ** (level - 1)) assert np.all(unitary_center < 1.0) # again emulate the fortran code # This is all for calculating child oct locations # iC_ = iC + nbshift # iO = ishft ( iC_ , - ndim ) # id = ishft ( 1, MaxLevel - iOctLv(iO) ) # j = iC_ + 1 - ishft( iO , ndim ) # Posx = d_x * (iOctPs(1,iO) + sign ( id , idelta(j,1) )) # Posy = d_x * (iOctPs(2,iO) + sign ( id , idelta(j,2) )) # Posz = d_x * (iOctPs(3,iO) + sign ( id , idelta(j,3) )) # idelta = [[-1, 1, -1, 1, -1, 1, -1, 1], # [-1, -1, 1, 1, -1, -1, 1, 1], # [-1, -1, -1, -1, 1, 1, 1, 1]] # idelta = np.array(idelta) # if ncell0 is None: # ncell0 = coarse_grid**3 # nchild = 8 # ndim = 3 # nshift = nchild -1 # nbshift = nshift - ncell0 # iC = iocts #+ nbshift # iO = iC >> ndim #possibly >> # id = 1 << (root_level - level) # j = iC + 1 - ( iO << 3) # delta = np.abs(id)*idelta[:,j-1] # try without the -1 # le = le/2**(root_level+1-level) # now read the hvars and vars arrays # we are looking for iOctCh # we record if iOctCh is >0, in which it is subdivided # iOctCh = np.zeros((nLevel+1,8),dtype='bool') f.seek(pos) return unitary_center, fl, iocts, nLevel, root_level def get_ranges( skip, count, field, words=6, real_size=4, np_per_page=4096**2, num_pages=1 ): # translate every particle index into a file position ranges ranges = [] arr_size = np_per_page * real_size idxa, idxb = 0, 0 posa, posb = 0, 0 for _page in range(num_pages): idxb += np_per_page for i, fname in enumerate(["x", "y", "z", "vx", "vy", "vz"]): posb += arr_size if i == field or fname == field: if skip < np_per_page and count > 0: left_in_page = np_per_page - skip this_count = min(left_in_page, count) count -= this_count start = posa + skip * real_size end = posa + this_count * real_size ranges.append((start, this_count)) skip = 0 assert end <= posb else: skip -= np_per_page posa += arr_size idxa += np_per_page assert count == 0 return ranges def read_particles(file, Nrow, idxa, idxb, fields): words = 6 # words (reals) per particle: x,y,z,vx,vy,vz real_size = 4 # for file_particle_data; not always true? np_per_page = Nrow**2 # defined in ART a_setup.h, # of particles/page num_pages = os.path.getsize(file) // (real_size * words * np_per_page) fh = open(file) skip, count = idxa, idxb - idxa kwargs = { "words": words, "real_size": real_size, "np_per_page": np_per_page, "num_pages": num_pages, } arrs = [] for field in fields: ranges = get_ranges(skip, count, field, **kwargs) data = None for seek, this_count in ranges: fh.seek(seek) temp = np.fromfile(fh, count=this_count, dtype=">f4") if data is None: data = temp else: data = np.concatenate((data, temp)) arrs.append(data.astype("f8")) fh.close() return arrs def read_star_field(file, field=None): data = {} with open(file, "rb") as fh: for dtype, variables in star_struct: found = ( isinstance(variables, tuple) and field in variables ) or field == variables if found: data[field] = read_vector(fh, dtype[1], dtype[0]) else: skip(fh, endian=">") return data.pop(field) def _read_child_mask_level(f, level_child_offsets, level, nLevel, nhydro_vars): f.seek(level_child_offsets[level]) ioctch = np.zeros(nLevel, dtype="uint8") idc = np.zeros(nLevel, dtype="int32") chunk = int(1e6) left = nLevel width = nhydro_vars + 6 a, b = 0, 0 while left > 0: chunk = min(chunk, left) b += chunk arr = np.fromfile(f, dtype=">i", count=chunk * width) arr = arr.reshape((width, chunk), order="F") assert np.all(arr[0, :] == arr[-1, :]) # pads must be equal idc[a:b] = arr[1, :] - 1 # fix fortran indexing ioctch[a:b] = arr[2, :] == 0 # if it is above zero, then refined available # zero in the mask means there is refinement available a = b left -= chunk assert left == 0 return idc, ioctch nchem = 8 + 2 dtyp = np.dtype(f">i4,>i8,>i8,>{nchem}f4,>2f4,>i4") def _read_child_level( f, level_child_offsets, level_oct_offsets, level_info, level, fields, domain_dimensions, ncell0, nhydro_vars=10, nchild=8, noct_range=None, ): # emulate the fortran code for reading cell data # read ( 19 ) idc, iOctCh(idc), (hvar(i,idc),i=1,nhvar), # & (var(i,idc), i=2,3) # contiguous 8-cell sections are for the same oct; # ie, we don't write out just the 0 cells, then the 1 cells # optionally, we only read noct_range to save memory left_index, fl, octs, nocts, root_level = _read_art_level_info( f, level_oct_offsets, level, coarse_grid=domain_dimensions[0] ) if noct_range is None: nocts = level_info[level] ncells = nocts * 8 f.seek(level_child_offsets[level]) arr = np.fromfile(f, dtype=hydro_struct, count=ncells) assert np.all(arr["pad1"] == arr["pad2"]) # pads must be equal # idc = np.argsort(arr['idc']) #correct fortran indices # translate idc into icell, and then to iOct icell = (arr["idc"] >> 3) << 3 iocts = (icell - ncell0) / nchild # without a F correction, there's a +1 # assert that the children are read in the same order as the octs assert np.all(octs == iocts[::nchild]) else: start, end = noct_range nocts = min(end - start, level_info[level]) end = start + nocts ncells = nocts * 8 skip = np.dtype(hydro_struct).itemsize * start * 8 f.seek(level_child_offsets[level] + skip) arr = np.fromfile(f, dtype=hydro_struct, count=ncells) assert np.all(arr["pad1"] == arr["pad2"]) # pads must be equal source = {} for field in fields: sh = (nocts, 8) source[field] = np.reshape(arr[field], sh, order="C").astype("float64") return source def _read_root_level(f, level_offsets, level_info, nhydro_vars=10): nocts = level_info[0] f.seek(level_offsets[0]) # Ditch the header hvar = read_vector(f, "f", ">") var = read_vector(f, "f", ">") hvar = hvar.reshape((nhydro_vars, nocts * 8), order="F") var = var.reshape((2, nocts * 8), order="F") arr = np.concatenate((hvar, var)) return arr # All of these functions are to convert from hydro time var to # proper time sqrt = np.sqrt sign = np.sign def find_root(f, a, b, tol=1e-6): c = (a + b) / 2.0 last = -np.inf assert sign(f(a)) != sign(f(b)) while np.abs(f(c) - last) > tol: last = f(c) if sign(last) == sign(f(b)): b = c else: a = c c = (a + b) / 2.0 return c def quad(fintegrand, xmin, xmax, n=1e4): from yt._maintenance.numpy2_compat import trapezoid spacings = np.logspace(np.log10(xmin), np.log10(xmax), num=int(n)) integrand_arr = fintegrand(spacings) val = trapezoid(integrand_arr, dx=np.diff(spacings)) return val def a2b(at, Om0=0.27, Oml0=0.73, h=0.700): def f_a2b(x): val = 0.5 * sqrt(Om0) / x**3.0 val /= sqrt(Om0 / x**3.0 + Oml0 + (1.0 - Om0 - Oml0) / x**2.0) return val # val, err = si.quad(f_a2b,1,at) val = quad(f_a2b, 1, at) return val def b2a(bt, **kwargs): # converts code time into expansion factor # if Om0 ==1and OmL == 0 then b2a is (1 / (1-td))**2 # if bt < -190.0 or bt > -.10: raise 'bt outside of range' def f_b2a(at): return a2b(at, **kwargs) - bt return find_root(f_b2a, 1e-4, 1.1) # return so.brenth(f_b2a,1e-4,1.1) # return brent.brent(f_b2a) def a2t(at, Om0=0.27, Oml0=0.73, h=0.700): def integrand(x): return 1.0 / (x * sqrt(Oml0 + Om0 * x**-3.0)) current_time = quad(integrand, 1e-4, at) current_time *= 9.779 / h return current_time def b2t(tb, n=1e2, logger=None, **kwargs): tb = np.array(tb) if isinstance(tb, float): return a2t(b2a(tb)) if tb.shape == (): return a2t(b2a(tb)) if len(tb) < n: n = len(tb) tbs = -1.0 * np.logspace(np.log10(-tb.min()), np.log10(-tb.max()), int(n)) ages = [] for i, tbi in enumerate(tbs): ages += (a2t(b2a(tbi)),) if logger: logger(i) ages = np.array(ages) return tbs, ages yt-project-yt-f043ac8/yt/frontends/art/tests/000077500000000000000000000000001510711153200212405ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/art/tests/__init__.py000066400000000000000000000000001510711153200233370ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/art/tests/test_outputs.py000066400000000000000000000100131510711153200243670ustar00rootroot00000000000000from numpy.testing import assert_almost_equal, assert_equal from yt.frontends.art.api import ARTDataset from yt.testing import ( ParticleSelectionComparison, requires_file, units_override_check, ) from yt.units.yt_array import YTQuantity from yt.utilities.answer_testing.framework import ( FieldValuesTest, PixelizedProjectionValuesTest, data_dir_load, requires_ds, ) _fields = ( ("gas", "density"), ("gas", "temperature"), ("all", "particle_mass"), ("all", "particle_position_x"), ("all", "particle_velocity_y"), ) d9p = "D9p_500/10MpcBox_HartGal_csf_a0.500.d" dmonly = "DMonly/PMcrs0.0100.DAT" @requires_ds(d9p, big_data=True) def test_d9p(): ds = data_dir_load(d9p) ds.index assert_equal(str(ds), "10MpcBox_HartGal_csf_a0.500.d") dso = [None, ("sphere", ("max", (0.1, "unitary")))] for field in _fields: for axis in [0, 1]: for dobj_name in dso: for weight_field in [None, ("gas", "density")]: if field[0] not in ds.particle_types: yield PixelizedProjectionValuesTest( d9p, axis, field, weight_field, dobj_name ) yield FieldValuesTest( d9p, field, obj_type=dobj_name, particle_type=field[0] in ds.particle_types, ) @requires_ds(d9p, big_data=True) def test_d9p_global_values(): ds = data_dir_load(d9p) ad = ds.all_data() # 'Ana' variable values output from the ART Fortran 'ANA' analysis code AnaNStars = 6255 assert_equal(ad["stars", "particle_type"].size, AnaNStars) assert_equal(ad["specie4", "particle_type"].size, AnaNStars) # The *real* answer is 2833405, but yt misses one particle since it lives # on a domain boundary. See issue 814. When that is fixed, this test # will need to be updated AnaNDM = 2833404 assert_equal(ad["darkmatter", "particle_type"].size, AnaNDM) assert_equal( ( ad["specie0", "particle_type"].size + ad["specie1", "particle_type"].size + ad["specie2", "particle_type"].size + ad["specie3", "particle_type"].size ), AnaNDM, ) for spnum in range(5): npart_read = ad[f"specie{spnum}", "particle_type"].size npart_header = ds.particle_type_counts[f"specie{spnum}"] if spnum == 3: # see issue 814 npart_read += 1 assert_equal(npart_read, npart_header) AnaBoxSize = YTQuantity(7.1442196564, "Mpc") AnaVolume = YTQuantity(364.640074656, "Mpc**3") Volume = 1 for i in ds.domain_width.in_units("Mpc"): assert_almost_equal(i, AnaBoxSize) Volume *= i assert_almost_equal(Volume, AnaVolume) AnaNCells = 4087490 assert_equal(len(ad["index", "cell_volume"]), AnaNCells) AnaTotDMMass = YTQuantity(1.01191786808255e14, "Msun") assert_almost_equal( ad["darkmatter", "particle_mass"].sum().in_units("Msun"), AnaTotDMMass ) AnaTotStarMass = YTQuantity(1776701.3990607238, "Msun") assert_almost_equal( ad["stars", "particle_mass"].sum().in_units("Msun"), AnaTotStarMass ) AnaTotStarMassInitial = YTQuantity(2423468.2801332865, "Msun") assert_almost_equal( ad["stars", "particle_mass_initial"].sum().in_units("Msun"), AnaTotStarMassInitial, ) AnaTotGasMass = YTQuantity(1.7826982029216785e13, "Msun") assert_almost_equal(ad["gas", "cell_mass"].sum().in_units("Msun"), AnaTotGasMass) AnaTotTemp = YTQuantity(150219844793.3907, "K") # just leaves assert_almost_equal(ad["gas", "temperature"].sum().in_units("K"), AnaTotTemp) @requires_file(d9p) def test_ARTDataset(): assert isinstance(data_dir_load(d9p), ARTDataset) @requires_file(d9p) def test_units_override(): units_override_check(d9p) @requires_file(dmonly) def test_particle_selection(): ds = data_dir_load(dmonly) psc = ParticleSelectionComparison(ds) psc.run_defaults() yt-project-yt-f043ac8/yt/frontends/artio/000077500000000000000000000000001510711153200204265ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/artio/__init__.py000066400000000000000000000000001510711153200225250ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/artio/_artio_caller.pyx000066400000000000000000002117121510711153200237730ustar00rootroot00000000000000# distutils: sources = ARTIO_SOURCE # distutils: include_dirs = LIB_DIR_GEOM_ARTIO # distutils: libraries = STD_LIBS cimport cython import numpy as np cimport numpy as np import sys from libc.stdlib cimport free, malloc from libc.string cimport memcpy from yt.geometry.oct_container cimport OctObjectPool, SparseOctreeContainer from yt.geometry.oct_visitors cimport Oct from yt.geometry.particle_deposit cimport ParticleDepositOperation from yt.geometry.selection_routines cimport AlwaysSelector, SelectorObject from yt.utilities.lib.fp_utils cimport imax from yt.utilities.lib.misc_utilities import OnceIndirect cdef extern from "platform_dep.h": ctypedef int int32_t ctypedef long long int64_t void *alloca(int) cdef extern from "cosmology.h": ctypedef struct CosmologyParameters "CosmologyParameters" : pass CosmologyParameters *cosmology_allocate() void cosmology_free(CosmologyParameters *c) void cosmology_set_fixed(CosmologyParameters *c) void cosmology_set_OmegaM(CosmologyParameters *c, double value) void cosmology_set_OmegaB(CosmologyParameters *c, double value) void cosmology_set_OmegaL(CosmologyParameters *c, double value) void cosmology_set_h(CosmologyParameters *c, double value) void cosmology_set_DeltaDC(CosmologyParameters *c, double value) double abox_from_auni(CosmologyParameters *c, double a) noexcept nogil double tcode_from_auni(CosmologyParameters *c, double a) noexcept nogil double tphys_from_auni(CosmologyParameters *c, double a) noexcept nogil double auni_from_abox(CosmologyParameters *c, double v) noexcept nogil double auni_from_tcode(CosmologyParameters *c, double v) noexcept nogil double auni_from_tphys(CosmologyParameters *c, double v) noexcept nogil double abox_from_tcode(CosmologyParameters *c, double tcode) noexcept nogil double tcode_from_abox(CosmologyParameters *c, double abox) noexcept nogil double tphys_from_abox(CosmologyParameters *c, double abox) noexcept nogil double tphys_from_tcode(CosmologyParameters *c, double tcode) noexcept nogil cdef extern from "artio.h": ctypedef struct artio_fileset_handle "artio_fileset" : pass ctypedef struct artio_selection "artio_selection" : pass ctypedef struct artio_context : pass cdef extern artio_context *artio_context_global # open modes cdef int ARTIO_OPEN_HEADER "ARTIO_OPEN_HEADER" cdef int ARTIO_OPEN_GRID "ARTIO_OPEN_GRID" cdef int ARTIO_OPEN_PARTICLES "ARTIO_OPEN_PARTICLES" # parameter constants cdef int ARTIO_TYPE_STRING "ARTIO_TYPE_STRING" cdef int ARTIO_TYPE_CHAR "ARTIO_TYPE_CHAR" cdef int ARTIO_TYPE_INT "ARTIO_TYPE_INT" cdef int ARTIO_TYPE_FLOAT "ARTIO_TYPE_FLOAT" cdef int ARTIO_TYPE_DOUBLE "ARTIO_TYPE_DOUBLE" cdef int ARTIO_TYPE_LONG "ARTIO_TYPE_LONG" cdef int ARTIO_MAX_STRING_LENGTH "ARTIO_MAX_STRING_LENGTH" cdef int ARTIO_PARAMETER_EXHAUSTED "ARTIO_PARAMETER_EXHAUSTED" # grid read options cdef int ARTIO_READ_LEAFS "ARTIO_READ_LEAFS" cdef int ARTIO_READ_REFINED "ARTIO_READ_REFINED" cdef int ARTIO_READ_ALL "ARTIO_READ_ALL" cdef int ARTIO_READ_REFINED_NOT_ROOT "ARTIO_READ_REFINED_NOT_ROOT" cdef int ARTIO_RETURN_CELLS "ARTIO_RETURN_CELLS" cdef int ARTIO_RETURN_OCTS "ARTIO_RETURN_OCTS" # errors cdef int ARTIO_SUCCESS "ARTIO_SUCCESS" cdef int ARTIO_ERR_MEMORY_ALLOCATION "ARTIO_ERR_MEMORY_ALLOCATION" artio_fileset_handle *artio_fileset_open(char *file_prefix, int type, artio_context *context ) int artio_fileset_close( artio_fileset_handle *handle ) int artio_fileset_open_particle( artio_fileset_handle *handle ) int artio_fileset_open_grid(artio_fileset_handle *handle) int artio_fileset_close_grid(artio_fileset_handle *handle) int artio_fileset_has_grid( artio_fileset_handle *handle ) int artio_fileset_has_particles( artio_fileset_handle *handle ) # selection functions artio_selection *artio_selection_allocate( artio_fileset_handle *handle ) artio_selection *artio_select_all( artio_fileset_handle *handle ) artio_selection *artio_select_volume( artio_fileset_handle *handle, double lpos[3], double rpos[3] ) int artio_selection_add_root_cell( artio_selection *selection, int coords[3] ) int artio_selection_destroy( artio_selection *selection ) int artio_selection_iterator( artio_selection *selection, int64_t max_range_size, int64_t *start, int64_t *end ) int64_t artio_selection_size( artio_selection *selection ) void artio_selection_print( artio_selection *selection ) # parameter functions int artio_parameter_iterate( artio_fileset_handle *handle, char *key, int *type, int *length ) int artio_parameter_get_int_array(artio_fileset_handle *handle, char * key, int length, int32_t *values) int artio_parameter_get_float_array(artio_fileset_handle *handle, char * key, int length, float *values) int artio_parameter_get_long_array(artio_fileset_handle *handle, char * key, int length, int64_t *values) int artio_parameter_get_double_array(artio_fileset_handle *handle, char * key, int length, double *values) int artio_parameter_get_string_array(artio_fileset_handle *handle, char * key, int length, char **values ) # grid functions int artio_grid_cache_sfc_range(artio_fileset_handle *handle, int64_t start, int64_t end) int artio_grid_clear_sfc_cache( artio_fileset_handle *handle ) int artio_grid_read_root_cell_begin(artio_fileset_handle *handle, int64_t sfc, double *pos, float *variables, int *num_tree_levels, int *num_octs_per_level) int artio_grid_read_root_cell_end(artio_fileset_handle *handle) int artio_grid_read_level_begin(artio_fileset_handle *handle, int level ) int artio_grid_read_level_end(artio_fileset_handle *handle) int artio_grid_read_oct(artio_fileset_handle *handle, double *pos, float *variables, int *refined) int artio_grid_count_octs_in_sfc_range(artio_fileset_handle *handle, int64_t start, int64_t end, int64_t *num_octs) #particle functions int artio_fileset_open_particles(artio_fileset_handle *handle) int artio_particle_read_root_cell_begin(artio_fileset_handle *handle, int64_t sfc, int * num_particle_per_species) int artio_particle_read_root_cell_end(artio_fileset_handle *handle) int artio_particle_read_particle(artio_fileset_handle *handle, int64_t *pid, int *subspecies, double *primary_variables, float *secondary_variables) int artio_particle_cache_sfc_range(artio_fileset_handle *handle, int64_t sfc_start, int64_t sfc_end) int artio_particle_clear_sfc_cache(artio_fileset_handle *handle) int artio_particle_read_species_begin(artio_fileset_handle *handle, int species) int artio_particle_read_species_end(artio_fileset_handle *handle) cdef extern from "artio_internal.h": np.int64_t artio_sfc_index( artio_fileset_handle *handle, int coords[3] ) noexcept nogil void artio_sfc_coords( artio_fileset_handle *handle, int64_t index, int coords[3] ) noexcept nogil cdef void check_artio_status(int status, char *fname="[unknown]"): if status != ARTIO_SUCCESS: import traceback traceback.print_stack() callername = sys._getframe().f_code.co_name nline = sys._getframe().f_lineno raise RuntimeError('failure with status', status, 'in function',fname,'from caller', callername, nline) cdef class artio_fileset : cdef public object parameters cdef artio_fileset_handle *handle # cosmology parameter for time unit conversion cdef CosmologyParameters *cosmology cdef float tcode_to_years # common attributes cdef public int num_grid cdef int64_t num_root_cells cdef int64_t sfc_min, sfc_max # grid attributes cdef public int has_grid cdef public int min_level, max_level cdef public int num_grid_variables cdef int *num_octs_per_level cdef float *grid_variables # particle attributes cdef public int has_particles cdef public int num_species cdef int *particle_position_index cdef int *num_particles_per_species cdef double *primary_variables cdef float *secondary_variables def __init__(self, char *file_prefix) : cdef int artio_type = ARTIO_OPEN_HEADER cdef int64_t num_root self.handle = artio_fileset_open( file_prefix, artio_type, artio_context_global ) if not self.handle : raise RuntimeError self.read_parameters() self.num_root_cells = self.parameters['num_root_cells'][0] self.num_grid = 1 num_root = self.num_root_cells while num_root > 1 : self.num_grid <<= 1 num_root >>= 3 self.sfc_min = 0 self.sfc_max = self.num_root_cells-1 # initialize cosmology module if "abox" in self.parameters: self.cosmology = cosmology_allocate() cosmology_set_OmegaM(self.cosmology, self.parameters['OmegaM'][0]) cosmology_set_OmegaL(self.cosmology, self.parameters['OmegaL'][0]) cosmology_set_OmegaB(self.cosmology, self.parameters['OmegaB'][0]) cosmology_set_h(self.cosmology, self.parameters['hubble'][0]) cosmology_set_DeltaDC(self.cosmology, self.parameters['DeltaDC'][0]) cosmology_set_fixed(self.cosmology) else: self.cosmology = NULL self.tcode_to_years = self.parameters['time_unit'][0]/(365.25*86400) # grid detection self.min_level = 0 self.max_level = self.parameters['grid_max_level'][0] self.num_grid_variables = self.parameters['num_grid_variables'][0] self.num_octs_per_level = malloc(self.max_level*sizeof(int)) self.grid_variables = malloc(8*self.num_grid_variables*sizeof(float)) if (not self.num_octs_per_level) or (not self.grid_variables) : raise MemoryError if artio_fileset_has_grid(self.handle): status = artio_fileset_open_grid(self.handle) check_artio_status(status) self.has_grid = 1 else: self.has_grid = 0 # particle detection if ( artio_fileset_has_particles(self.handle) ): status = artio_fileset_open_particles(self.handle) check_artio_status(status) self.has_particles = 1 for v in ["num_particle_species","num_primary_variables","num_secondary_variables"]: if v not in self.parameters: raise RuntimeError("Unable to locate particle header information in artio header: key=", v) self.num_species = self.parameters['num_particle_species'][0] self.particle_position_index = malloc(3*sizeof(int)*self.num_species) if not self.particle_position_index : raise MemoryError for ispec in range(self.num_species) : species_labels = "species_%02d_primary_variable_labels"% (ispec,) if species_labels not in self.parameters: raise RuntimeError("Unable to locate variable labels for species",ispec) labels = self.parameters[species_labels] try : self.particle_position_index[3*ispec+0] = labels.index('POSITION_X') self.particle_position_index[3*ispec+1] = labels.index('POSITION_Y') self.particle_position_index[3*ispec+2] = labels.index('POSITION_Z') except ValueError : raise RuntimeError("Unable to locate position information for particle species", ispec) self.num_particles_per_species = malloc(sizeof(int)*self.num_species) self.primary_variables = malloc(sizeof(double)*max(self.parameters['num_primary_variables'])) self.secondary_variables = malloc(sizeof(float)*max(self.parameters['num_secondary_variables'])) if (not self.num_particles_per_species) or (not self.primary_variables) or (not self.secondary_variables) : raise MemoryError else: self.has_particles = 0 def __dealloc__(self) : if self.num_octs_per_level : free(self.num_octs_per_level) if self.grid_variables : free(self.grid_variables) if self.particle_position_index : free(self.particle_position_index) if self.num_particles_per_species : free(self.num_particles_per_species) if self.primary_variables : free(self.primary_variables) if self.secondary_variables : free(self.secondary_variables) if self.cosmology : cosmology_free(self.cosmology) if self.handle : artio_fileset_close(self.handle) def read_parameters(self) : cdef char key[64] cdef int type cdef int length cdef char ** char_values cdef int32_t *int_values cdef int64_t *long_values cdef float *float_values cdef double *double_values self.parameters = {} while artio_parameter_iterate( self.handle, key, &type, &length ) == ARTIO_SUCCESS : if type == ARTIO_TYPE_STRING : char_values = malloc(length*sizeof(char *)) for i in range(length) : char_values[i] = malloc( ARTIO_MAX_STRING_LENGTH*sizeof(char) ) artio_parameter_get_string_array( self.handle, key, length, char_values ) parameter = [ char_values[i] for i in range(length) ] for i in range(length) : free(char_values[i]) free(char_values) for i in range(len(parameter)): parameter[i] = parameter[i].decode('utf-8') elif type == ARTIO_TYPE_INT : int_values = malloc(length*sizeof(int32_t)) artio_parameter_get_int_array( self.handle, key, length, int_values ) parameter = [ int_values[i] for i in range(length) ] free(int_values) elif type == ARTIO_TYPE_LONG : long_values = malloc(length*sizeof(int64_t)) artio_parameter_get_long_array( self.handle, key, length, long_values ) parameter = [ long_values[i] for i in range(length) ] free(long_values) elif type == ARTIO_TYPE_FLOAT : float_values = malloc(length*sizeof(float)) artio_parameter_get_float_array( self.handle, key, length, float_values ) parameter = [ float_values[i] for i in range(length) ] free(float_values) elif type == ARTIO_TYPE_DOUBLE : double_values = malloc(length*sizeof(double)) artio_parameter_get_double_array( self.handle, key, length, double_values ) parameter = [ double_values[i] for i in range(length) ] free(double_values) else : raise RuntimeError("ARTIO file corruption detected: invalid type!") self.parameters[key.decode('utf-8')] = parameter def abox_from_auni(self, np.float64_t a): if self.cosmology: return abox_from_auni(self.cosmology, a) else: raise RuntimeError("abox_from_auni called for non-cosmological ARTIO fileset!") def tcode_from_auni(self, np.float64_t a): if self.cosmology: return tcode_from_auni(self.cosmology, a) else: raise RuntimeError("tcode_from_auni called for non-cosmological ARTIO fileset!") def tphys_from_auni(self, np.float64_t a): if self.cosmology: return tphys_from_auni(self.cosmology, a) else: raise RuntimeError("tphys_from_auni called for non-cosmological ARTIO fileset!") def auni_from_abox(self, np.float64_t v): if self.cosmology: return auni_from_abox(self.cosmology, v) else: raise RuntimeError("auni_from_abox called for non-cosmological ARTIO fileset!") def auni_from_tcode(self, np.float64_t v): if self.cosmology: return auni_from_tcode(self.cosmology, v) else: raise RuntimeError("auni_from_tcode called for non-cosmological ARTIO fileset!") @cython.wraparound(False) @cython.boundscheck(False) def auni_from_tcode_array(self, np.ndarray[np.float64_t] tcode): cdef int i, nauni cdef np.ndarray[np.float64_t, ndim=1] auni cdef CosmologyParameters *cosmology = self.cosmology if not cosmology: raise RuntimeError("auni_from_tcode_array called for non-cosmological ARTIO fileset!") auni = np.empty_like(tcode) nauni = auni.shape[0] with nogil: for i in range(nauni): auni[i] = auni_from_tcode(self.cosmology, tcode[i]) return auni def auni_from_tphys(self, np.float64_t v): if self.cosmology: return auni_from_tphys(self.cosmology, v) else: raise RuntimeError("auni_from_tphys called for non-cosmological ARTIO fileset!") def abox_from_tcode(self, np.float64_t abox): if self.cosmology: return abox_from_tcode(self.cosmology, abox) else: raise RuntimeError("abox_from_tcode called for non-cosmological ARTIO fileset!") def tcode_from_abox(self, np.float64_t abox): if self.cosmology: return tcode_from_abox(self.cosmology, abox) else: raise RuntimeError("tcode_from_abox called for non-cosmological ARTIO fileset!") def tphys_from_abox(self, np.float64_t abox): if self.cosmology: return tphys_from_abox(self.cosmology, abox) else: raise RuntimeError("tphys_from_abox called for non-cosmological ARTIO fileset!") def tphys_from_tcode(self, np.float64_t tcode): if self.cosmology: return tphys_from_tcode(self.cosmology, tcode) else: return self.tcode_to_years*tcode @cython.wraparound(False) @cython.boundscheck(False) def tphys_from_tcode_array(self, np.ndarray[np.float64_t] tcode): cdef int i, ntphys cdef np.ndarray[np.float64_t, ndim=1] tphys cdef CosmologyParameters *cosmology = self.cosmology tphys = np.empty_like(tcode) ntphys = tcode.shape[0] if cosmology: tphys = np.empty_like(tcode) ntphys = tcode.shape[0] with nogil: for i in range(ntphys): tphys[i] = tphys_from_tcode(cosmology, tcode[i]) return tphys else: return tcode*self.tcode_to_years # @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def read_particle_chunk(self, SelectorObject selector, int64_t sfc_start, int64_t sfc_end, fields) : cdef int i cdef int status cdef int subspecies cdef int64_t pid cdef np.float64_t pos[3] # since RuntimeErrors are not fatal, ensure no artio_particles* functions # called if fileset lacks particles if not self.has_particles: return data = {} accessed_species = np.zeros( self.num_species, dtype="int64") selected_mass = [ None for i in range(self.num_species)] selected_pid = [ None for i in range(self.num_species)] selected_species = [ None for i in range(self.num_species)] selected_primary = [ [] for i in range(self.num_species)] selected_secondary = [ [] for i in range(self.num_species)] for species,field in fields : if species < 0 or species > self.num_species : raise RuntimeError("Invalid species provided to read_particle_chunk") accessed_species[species] = 1 if self.parameters["num_primary_variables"][species] > 0 and \ field in self.parameters["species_%02u_primary_variable_labels"%(species,)] : selected_primary[species].append((self.parameters["species_%02u_primary_variable_labels"%(species,)].index(field),(species,field))) data[species,field] = np.empty(0,dtype="float64") elif self.parameters["num_secondary_variables"][species] > 0 and \ field in self.parameters["species_%02u_secondary_variable_labels"%(species,)] : selected_secondary[species].append((self.parameters["species_%02u_secondary_variable_labels"%(species,)].index(field),(species,field))) data[species,field] = np.empty(0,dtype="float64") elif field == "MASS" : selected_mass[species] = (species,field) data[species,field] = np.empty(0,dtype="float64") elif field == "PID" : selected_pid[species] = (species,field) data[species,field] = np.empty(0,dtype="int64") elif field == "SPECIES" : selected_species[species] = (species,field) data[species,field] = np.empty(0,dtype="int8") else : raise RuntimeError("invalid field name provided to read_particle_chunk") # cache the range status = artio_particle_cache_sfc_range( self.handle, self.sfc_min, self.sfc_max ) check_artio_status(status) for sfc in range( sfc_start, sfc_end+1 ) : status = artio_particle_read_root_cell_begin( self.handle, sfc, self.num_particles_per_species ) check_artio_status(status) for ispec in range(self.num_species) : if accessed_species[ispec] : status = artio_particle_read_species_begin(self.handle, ispec) check_artio_status(status) for particle in range( self.num_particles_per_species[ispec] ) : status = artio_particle_read_particle(self.handle, &pid, &subspecies, self.primary_variables, self.secondary_variables) check_artio_status(status) for i in range(3) : pos[i] = self.primary_variables[self.particle_position_index[3*ispec+i]] if selector.select_point(pos) : # loop over primary variables for i,field in selected_primary[ispec] : count = len(data[field]) data[field].resize(count+1) data[field][count] = self.primary_variables[i] # loop over secondary variables for i,field in selected_secondary[ispec] : count = len(data[field]) data[field].resize(count+1) data[field][count] = self.secondary_variables[i] # add particle id if selected_pid[ispec] : count = len(data[selected_pid[ispec]]) data[selected_pid[ispec]].resize(count+1) data[selected_pid[ispec]][count] = pid # add mass if requested if selected_mass[ispec] : count = len(data[selected_mass[ispec]]) data[selected_mass[ispec]].resize(count+1) data[selected_mass[ispec]][count] = self.parameters["particle_species_mass"][ispec] status = artio_particle_read_species_end( self.handle ) check_artio_status(status) status = artio_particle_read_root_cell_end( self.handle ) check_artio_status(status) return data #@cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def read_grid_chunk(self, SelectorObject selector, int64_t sfc_start, int64_t sfc_end, fields ): cdef int i cdef int level cdef int num_oct_levels cdef int refined[8] cdef int status cdef int64_t count cdef double dpos[3] cdef np.float64_t left[3] cdef np.float64_t right[3] cdef np.float64_t dds[3] cdef int *field_order cdef int num_fields = len(fields) field_order = malloc(sizeof(int)*num_fields) # translate fields from ARTIO names to indices var_labels = self.parameters['grid_variable_labels'] for i, f in enumerate(fields): if f not in var_labels: raise RuntimeError("Field",f,"is not known to ARTIO") field_order[i] = var_labels.index(f) status = artio_grid_cache_sfc_range( self.handle, self.sfc_min, self.sfc_max ) check_artio_status(status) # determine max number of cells we could hit (optimize later) #status = artio_grid_count_octs_in_sfc_range( self.handle, # sfc_start, sfc_end, &max_octs ) #check_artio_status(status) #max_cells = sfc_end-sfc_start+1 + max_octs*8 # allocate space for _fcoords, _icoords, _fwidth, _ires #fcoords = np.empty((max_cells, 3), dtype="float64") #ires = np.empty(max_cells, dtype="int64") fcoords = np.empty((0, 3), dtype="float64") ires = np.empty(0, dtype="int64") #data = [ np.empty(max_cells, dtype="float32") for i in range(num_fields) ] data = [ np.empty(0,dtype="float64") for i in range(num_fields)] count = 0 for sfc in range( sfc_start, sfc_end+1 ) : status = artio_grid_read_root_cell_begin( self.handle, sfc, dpos, self.grid_variables, &num_oct_levels, self.num_octs_per_level ) check_artio_status(status) if num_oct_levels == 0 : for i in range(num_fields) : data[i].resize(count+1) data[i][count] = self.grid_variables[field_order[i]] fcoords.resize((count+1,3)) for i in range(3) : fcoords[count][i] = dpos[i] ires.resize(count+1) ires[count] = 0 count += 1 for level in range(1,num_oct_levels+1) : status = artio_grid_read_level_begin( self.handle, level ) check_artio_status(status) for i in range(3) : dds[i] = 2.**-level for oct in range(self.num_octs_per_level[level-1]) : status = artio_grid_read_oct( self.handle, dpos, self.grid_variables, refined ) check_artio_status(status) for child in range(8) : if not refined[child] : for i in range(3) : left[i] = (dpos[i]-dds[i]) if (child & (i<<1)) else dpos[i] right[i] = left[i] + dds[i] if selector.select_bbox(left,right) : fcoords.resize((count+1, 3)) for i in range(3) : fcoords[count][i] = left[i]+0.5*dds[i] ires.resize(count+1) ires[count] = level for i in range(num_fields) : data[i].resize(count+1) data[i][count] = self.grid_variables[self.num_grid_variables*child+field_order[i]] count += 1 status = artio_grid_read_level_end( self.handle ) check_artio_status(status) status = artio_grid_read_root_cell_end( self.handle ) check_artio_status(status) free(field_order) #fcoords.resize((count,3)) #ires.resize(count) # #for i in range(num_fields) : # data[i].resize(count) return (fcoords, ires, data) def root_sfc_ranges_all(self, int max_range_size = 1024) : cdef int64_t sfc_start, sfc_end cdef artio_selection *selection selection = artio_select_all( self.handle ) if selection == NULL : raise RuntimeError sfc_ranges = [] while artio_selection_iterator(selection, max_range_size, &sfc_start, &sfc_end) == ARTIO_SUCCESS : sfc_ranges.append([sfc_start, sfc_end]) artio_selection_destroy(selection) return sfc_ranges def root_sfc_ranges(self, SelectorObject selector, int max_range_size = 1024): cdef int coords[3] cdef int64_t sfc_start, sfc_end cdef np.float64_t left[3] cdef np.float64_t right[3] cdef artio_selection *selection cdef int i, j, k sfc_ranges=[] selection = artio_selection_allocate(self.handle) for i in range(self.num_grid) : # stupid cython coords[0] = i left[0] = coords[0] right[0] = left[0] + 1.0 for j in range(self.num_grid) : coords[1] = j left[1] = coords[1] right[1] = left[1] + 1.0 for k in range(self.num_grid) : coords[2] = k left[2] = coords[2] right[2] = left[2] + 1.0 if selector.select_bbox(left,right) : status = artio_selection_add_root_cell(selection, coords) check_artio_status(status) while artio_selection_iterator(selection, max_range_size, &sfc_start, &sfc_end) == ARTIO_SUCCESS : sfc_ranges.append([sfc_start, sfc_end]) artio_selection_destroy(selection) return sfc_ranges ################################################### def artio_is_valid( char *file_prefix ) : cdef artio_fileset_handle *handle = artio_fileset_open( file_prefix, ARTIO_OPEN_HEADER, artio_context_global ) if handle == NULL : return False else : artio_fileset_close(handle) return True cdef class ARTIOSFCRangeHandler: cdef public np.int64_t sfc_start cdef public np.int64_t sfc_end cdef public artio_fileset artio_handle cdef public object root_mesh_handler cdef public object oct_count cdef public object octree_handler cdef artio_fileset_handle *handle cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] cdef np.float64_t dds[3] cdef np.int64_t dims[3] cdef public np.int64_t total_octs cdef np.int64_t *doct_count cdef np.int64_t **pcount cdef float **root_mesh_data cdef np.int64_t nvars[2] cdef int cache_root_mesh def __init__(self, domain_dimensions, # cells domain_left_edge, domain_right_edge, artio_fileset artio_handle, sfc_start, sfc_end, int cache_root_mesh = 0): cdef int i cdef np.int64_t sfc self.sfc_start = sfc_start self.sfc_end = sfc_end self.artio_handle = artio_handle self.root_mesh_handler = None self.octree_handler = None self.handle = artio_handle.handle self.oct_count = None self.root_mesh_data = NULL self.pcount = NULL self.cache_root_mesh = cache_root_mesh if artio_handle.has_particles: self.pcount = malloc(sizeof(np.int64_t*) * artio_handle.num_species) self.nvars[0] = artio_handle.num_species for i in range(artio_handle.num_species): self.pcount[i] = malloc(sizeof(np.int64_t) * (self.sfc_end - self.sfc_start + 1)) for sfc in range(self.sfc_end - self.sfc_start + 1): self.pcount[i][sfc] = 0 else: self.nvars[0] = 0 if artio_handle.has_grid: self.nvars[1] = artio_handle.num_grid_variables else: self.nvars[1] = 0 for i in range(3): self.dims[i] = domain_dimensions[i] self.DLE[i] = domain_left_edge[i] self.DRE[i] = domain_right_edge[i] self.dds[i] = (self.DRE[i] - self.DLE[i])/self.dims[i] def __dealloc__(self): cdef int i if self.artio_handle.has_particles: for i in range(self.nvars[0]): free(self.pcount[i]) free(self.pcount) if self.artio_handle.has_grid: if self.root_mesh_data != NULL: for i in range(self.nvars[1]): free(self.root_mesh_data[i]) free(self.root_mesh_data) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def construct_mesh(self): cdef int status, level, ngv cdef np.int64_t sfc, oc, i cdef double dpos[3] cdef int num_oct_levels cdef int max_level = self.artio_handle.max_level cdef int *num_octs_per_level = malloc( (max_level + 1)*sizeof(int)) cdef int num_species = self.artio_handle.num_species cdef int *num_particles_per_species cdef ARTIOOctreeContainer octree ngv = self.nvars[1] cdef float *grid_variables = malloc( ngv * sizeof(float)) self.octree_handler = octree = ARTIOOctreeContainer(self) if self.cache_root_mesh == 1: self.root_mesh_data = malloc(sizeof(float *) * ngv) for i in range(ngv): self.root_mesh_data[i] = malloc(sizeof(float) * \ (self.sfc_end - self.sfc_start + 1)) # We want to pre-allocate an array of root pointers. In the future, # this will be pre-determined by the ARTIO library. However, because # realloc plays havoc with our tree searching, we can't utilize an # expanding array at the present time. octree.allocate_domains([], self.sfc_end - self.sfc_start + 1) cdef np.ndarray[np.int64_t, ndim=1] oct_count oct_count = np.zeros(self.sfc_end - self.sfc_start + 1, dtype="int64") status = artio_grid_cache_sfc_range(self.handle, self.sfc_start, self.sfc_end) check_artio_status(status) for sfc in range(self.sfc_start, self.sfc_end + 1): status = artio_grid_read_root_cell_begin( self.handle, sfc, dpos, grid_variables, &num_oct_levels, num_octs_per_level) check_artio_status(status) for i in range(ngv * self.cache_root_mesh): self.root_mesh_data[i][sfc - self.sfc_start] = \ grid_variables[i] if num_oct_levels > 0: oc = 0 for level in range(num_oct_levels): oc += num_octs_per_level[level] self.total_octs += oc oct_count[sfc - self.sfc_start] = oc octree.initialize_local_mesh(oc, num_oct_levels, num_octs_per_level, sfc) status = artio_grid_read_root_cell_end(self.handle) check_artio_status(status) status = artio_grid_clear_sfc_cache(self.handle) check_artio_status(status) if self.artio_handle.has_particles: num_particles_per_species = malloc( sizeof(int)*num_species) # Now particles status = artio_particle_cache_sfc_range(self.handle, self.sfc_start, self.sfc_end) check_artio_status(status) for sfc in range(self.sfc_start, self.sfc_end + 1): # Now particles status = artio_particle_read_root_cell_begin(self.handle, sfc, num_particles_per_species) check_artio_status(status) for i in range(num_species): self.pcount[i][sfc - self.sfc_start] = \ num_particles_per_species[i] status = artio_particle_read_root_cell_end(self.handle) check_artio_status(status) status = artio_particle_clear_sfc_cache(self.handle) check_artio_status(status) free(num_particles_per_species) free(grid_variables) free(num_octs_per_level) self.oct_count = oct_count self.doct_count = oct_count.data self.root_mesh_handler = ARTIORootMeshContainer(self) def free_mesh(self): self.octree_handler = None self.root_mesh_handler = None self.doct_count = NULL self.oct_count = None def get_coords(artio_fileset handle, np.int64_t s): cdef int coords[3] artio_sfc_coords(handle.handle, s, coords) return (coords[0], coords[1], coords[2]) cdef struct particle_var_pointers: # The number of particles we have filled np.int64_t count # Number of primary variables and pointers to their indices int n_p int p_ind[16] # Max of 16 vars # Number of secondary variables and pointers to their indices int n_s int s_ind[16] # Max of 16 vars # Pointers to the bools and data arrays for mass, pid and species int n_mass np.float64_t *mass int n_pid np.int64_t *pid int n_species np.int8_t *species # Pointers to the pointers to primary and secondary vars np.float64_t *pvars[16] np.float64_t *svars[16] cdef class ARTIOOctreeContainer(SparseOctreeContainer): # This is a transitory, created-on-demand OctreeContainer. It should not # be considered to be long-lasting, and during its creation it will read # the index file. This means that when created it will then be able to # provide coordinates, but then on subsequent IO accesses it will pass over # the file again, despite knowing the indexing system already. Because of # this, we will avoid creating it as long as possible. cdef public artio_fileset artio_handle cdef ARTIOSFCRangeHandler range_handler cdef np.int64_t level_indices[32] cdef np.int64_t sfc_start cdef np.int64_t sfc_end def __init__(self, ARTIOSFCRangeHandler range_handler): self.range_handler = range_handler self.sfc_start = range_handler.sfc_start self.sfc_end = range_handler.sfc_end self.artio_handle = range_handler.artio_handle # Note the final argument is partial_coverage, which indicates whether # or not an Oct can be partially refined. dims, DLE, DRE = [], [], [] for i in range(32): self.level_indices[i] = 0 for i in range(3): # range_handler has dims in cells, which is the same as the number # of possible octs. This is because we have a forest of octrees. dims.append(range_handler.dims[i]) DLE.append(range_handler.DLE[i]) DRE.append(range_handler.DRE[i]) super(ARTIOOctreeContainer, self).__init__(dims, DLE, DRE) self.artio_handle = range_handler.artio_handle self.level_offset = 1 self.domains = OctObjectPool() self.root_nodes = NULL @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void initialize_local_mesh(self, np.int64_t oct_count, int num_oct_levels, int *num_octs_per_level, np.int64_t sfc): # We actually will not be initializing the root mesh here, we will be # initializing the entire mesh between sfc_start and sfc_end. cdef np.int64_t oct_ind, ipos, nadded cdef int i, status, level cdef artio_fileset_handle *handle = self.artio_handle.handle cdef double dpos[3] cdef np.ndarray[np.float64_t, ndim=2] pos # NOTE: We do not cache any SFC ranges here, as we should only ever be # called from within a pre-cached operation in the SFC handler. # We only allow one root oct. self.append_domain(oct_count) self.domains.containers[self.num_domains - 1].con_id = sfc oct_ind = -1 ipos = 0 for level in range(num_oct_levels): oct_ind = imax(oct_ind, num_octs_per_level[level]) self.level_indices[level] = ipos ipos += num_octs_per_level[level] pos = np.empty((oct_ind, 3), dtype="float64") # Now we initialize # Note that we also assume we have already started reading the level. ipos = 0 for level in range(num_oct_levels): status = artio_grid_read_level_begin(handle, level + 1) check_artio_status(status) for oct_ind in range(num_octs_per_level[level]): status = artio_grid_read_oct(handle, dpos, NULL, NULL) for i in range(3): pos[oct_ind, i] = dpos[i] check_artio_status(status) status = artio_grid_read_level_end(handle) check_artio_status(status) nadded = self.add(self.num_domains, level, pos[:num_octs_per_level[level],:]) if nadded != num_octs_per_level[level]: raise RuntimeError @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_sfc(self, np.ndarray[np.uint8_t, ndim=1] levels, np.ndarray[np.uint8_t, ndim=1] cell_inds, np.ndarray[np.int64_t, ndim=1] file_inds, np.ndarray[np.int64_t, ndim=1] domain_counts, field_indices, dest_fields): cdef np.ndarray[np.float32_t, ndim=2] source cdef np.ndarray[np.float64_t, ndim=1] dest cdef int status, i, num_oct_levels, nf, ngv, max_level cdef int level, j, oct_ind, si cdef np.int64_t sfc, ipos cdef artio_fileset_handle *handle = self.artio_handle.handle cdef double dpos[3] # We duplicate some of the grid_variables stuff here so that we can # potentially release the GIL nf = len(field_indices) ngv = self.artio_handle.num_grid_variables max_level = self.artio_handle.max_level cdef int *num_octs_per_level = malloc( (max_level + 1)*sizeof(int)) cdef float *grid_variables = malloc( 8 * ngv * sizeof(float)) cdef int* field_ind = malloc( nf * sizeof(int)) cdef np.float32_t **field_vals = malloc( nf * sizeof(np.float32_t*)) source_arrays = [] ipos = -1 for i in range(self.num_domains): ipos = imax(ipos, self.domains.containers[i].n) for i in range(nf): field_ind[i] = field_indices[i] # Note that we subtract one, because we're not using the root mesh. source = np.zeros((ipos, 8), dtype="float32") source_arrays.append(source) field_vals[i] = source.data cdef np.int64_t level_position[32] cdef np.int64_t lp # First we need to walk the mesh in the file. Then we fill in the dest # location based on the file index. # A few ways this could be improved: # * Create a new visitor function that actually queried the data, # rather than our somewhat hokey double-loop over SFC arrays. # * Go to pointers for the dest arrays. # * Enable preloading during mesh initialization # * Calculate domain indices on the fly rather than with a # double-loop to calculate domain_counts # The cons should be in order cdef np.int64_t sfc_start, sfc_end sfc_start = self.domains.containers[0].con_id sfc_end = self.domains.containers[self.num_domains - 1].con_id status = artio_grid_cache_sfc_range(handle, sfc_start, sfc_end) check_artio_status(status) cdef np.int64_t offset = 0 for si in range(self.num_domains): sfc = self.domains.containers[si].con_id status = artio_grid_read_root_cell_begin( handle, sfc, dpos, NULL, &num_oct_levels, num_octs_per_level) check_artio_status(status) lp = 0 for level in range(num_oct_levels): status = artio_grid_read_level_begin(handle, level + 1) check_artio_status(status) level_position[level] = lp for oct_ind in range(num_octs_per_level[level]): status = artio_grid_read_oct(handle, dpos, grid_variables, NULL) check_artio_status(status) for j in range(8): for i in range(nf): field_vals[i][(oct_ind + lp)*8+j] = \ grid_variables[ngv*j+field_ind[i]] status = artio_grid_read_level_end(handle) check_artio_status(status) lp += num_octs_per_level[level] status = artio_grid_read_root_cell_end(handle) check_artio_status(status) # Now we have all our sources. for j in range(nf): dest = dest_fields[j] source = source_arrays[j] for i in range(domain_counts[si]): level = levels[i + offset] oct_ind = file_inds[i + offset] + level_position[level] dest[i + offset] = source[oct_ind, cell_inds[i + offset]] # Now, we offset by the actual number filled here. offset += domain_counts[si] status = artio_grid_clear_sfc_cache(handle) check_artio_status(status) free(field_ind) free(field_vals) free(grid_variables) free(num_octs_per_level) def fill_sfc_particles(self, fields): # This handles not getting particles for refined sfc values. rv = read_sfc_particles(self.artio_handle, self.sfc_start, self.sfc_end, 0, fields, self.range_handler.doct_count, self.range_handler.pcount) return rv @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef read_sfc_particles(artio_fileset artio_handle, np.int64_t sfc_start, np.int64_t sfc_end, int read_unrefined, fields, np.int64_t *doct_count, np.int64_t **pcount): cdef int status, ispec, subspecies cdef np.int64_t sfc, particle, pid, ind, vind cdef int num_species = artio_handle.num_species cdef artio_fileset_handle *handle = artio_handle.handle cdef int *num_particles_per_species = malloc( sizeof(int)*num_species) cdef int *accessed_species = malloc( sizeof(int)*num_species) cdef int *total_particles = malloc( sizeof(int)*num_species) cdef particle_var_pointers *vpoints = malloc( sizeof(particle_var_pointers)*num_species) cdef double *primary_variables cdef float *secondary_variables cdef np.int64_t tp cdef int max_level = artio_handle.max_level cdef int *num_octs_per_level = malloc( (max_level + 1)*sizeof(int)) cdef np.ndarray[np.int8_t, ndim=1] npi8arr cdef np.ndarray[np.int64_t, ndim=1] npi64arr cdef np.ndarray[np.float64_t, ndim=1] npf64arr if not artio_handle.has_particles: raise RuntimeError("Attempted to read non-existent particles in ARTIO") # Now we set up our field pointers params = artio_handle.parameters npri_vars = params["num_primary_variables"] nsec_vars = params["num_secondary_variables"] primary_variables = malloc(sizeof(double) * max(npri_vars)) secondary_variables = malloc(sizeof(float) * max(nsec_vars)) cdef particle_var_pointers *vp for ispec in range(num_species): total_particles[ispec] = 0 accessed_species[ispec] = 0 # Initialize our vpoints array vpoints[ispec].count = 0 vpoints[ispec].n_mass = 0 vpoints[ispec].n_pid = 0 vpoints[ispec].n_species = 0 vpoints[ispec].n_p = 0 vpoints[ispec].n_s = 0 # Pass through once. We want every single particle. tp = 0 cdef np.int64_t c for sfc in range(sfc_start, sfc_end + 1): c = doct_count[sfc - sfc_start] if read_unrefined == 1 and c > 0: continue if read_unrefined == 0 and c == 0: continue for ispec in range(num_species): total_particles[ispec] += pcount[ispec][sfc - sfc_start] # Now we allocate our final fields, which will be filled #for ispec in range(num_species): # print "In SFC %s to %s reading %s of species %s" % ( # sfc_start, sfc_end + 1, total_particles[ispec], ispec) data = {} for species, field in sorted(fields): accessed_species[species] = 1 pri_vars = params.get( "species_%02u_primary_variable_labels" % (species,), []) sec_vars = params.get( "species_%02u_secondary_variable_labels" % (species,), []) tp = total_particles[species] vp = &vpoints[species] if field == "PID": vp.n_pid = 1 data[species, field] = np.zeros(tp, dtype="int64") npi64arr = data[species, field] vp.pid = npi64arr.data elif field == "SPECIES": vp.n_species = 1 data[species, field] = np.zeros(tp, dtype="int8") npi8arr = data[species, field] # We fill this *now* npi8arr += species vp.species = npi8arr.data elif npri_vars[species] > 0 and field in pri_vars : data[species, field] = np.zeros(tp, dtype="float64") npf64arr = data[species, field] vp.p_ind[vp.n_p] = pri_vars.index(field) vp.pvars[vp.n_p] = npf64arr.data vp.n_p += 1 elif nsec_vars[species] > 0 and field in sec_vars : data[species, field] = np.zeros(tp, dtype="float64") npf64arr = data[species, field] vp.s_ind[vp.n_s] = sec_vars.index(field) vp.svars[vp.n_s] = npf64arr.data vp.n_s += 1 elif field == "MASS": vp.n_mass = 1 data[species, field] = np.zeros(tp, dtype="float64") npf64arr = data[species, field] # We fill this *now* npf64arr += params["particle_species_mass"][species] vp.mass = npf64arr.data status = artio_particle_cache_sfc_range( handle, sfc_start, sfc_end ) check_artio_status(status) for sfc in range(sfc_start, sfc_end + 1): c = doct_count[sfc - sfc_start] check_artio_status(status) if read_unrefined == 1 and c > 0: continue if read_unrefined == 0 and c == 0: continue c = 0 for ispec in range(num_species) : if accessed_species[ispec] == 0: continue c += pcount[ispec][sfc - sfc_start] if c == 0: continue status = artio_particle_read_root_cell_begin( handle, sfc, num_particles_per_species ) check_artio_status(status) for ispec in range(num_species) : if accessed_species[ispec] == 0: continue status = artio_particle_read_species_begin(handle, ispec) check_artio_status(status) vp = &vpoints[ispec] for particle in range(num_particles_per_species[ispec]) : status = artio_particle_read_particle(handle, &pid, &subspecies, primary_variables, secondary_variables) check_artio_status(status) ind = vp.count for i in range(vp.n_p): vind = vp.p_ind[i] vp.pvars[i][ind] = primary_variables[vind] for i in range(vp.n_s): vind = vp.s_ind[i] vp.svars[i][ind] = secondary_variables[vind] if vp.n_pid: vp.pid[ind] = pid vp.count += 1 status = artio_particle_read_species_end( handle ) check_artio_status(status) status = artio_particle_read_root_cell_end( handle ) check_artio_status(status) status = artio_particle_clear_sfc_cache(handle) check_artio_status(status) free(num_octs_per_level) free(num_particles_per_species) free(total_particles) free(accessed_species) free(vpoints) free(primary_variables) free(secondary_variables) return data cdef class ARTIORootMeshContainer: cdef public artio_fileset artio_handle cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] cdef np.float64_t dds[3] cdef np.int64_t dims[3] cdef artio_fileset_handle *handle cdef np.uint64_t sfc_start cdef np.uint64_t sfc_end cdef public object _last_mask cdef public np.int64_t _last_selector_id cdef np.int64_t _last_mask_sum cdef ARTIOSFCRangeHandler range_handler cdef np.uint8_t *sfc_mask cdef np.int64_t nsfc def __init__(self, ARTIOSFCRangeHandler range_handler): cdef int i cdef np.int64_t sfci for i in range(3): self.DLE[i] = range_handler.DLE[i] self.DRE[i] = range_handler.DRE[i] self.dims[i] = range_handler.dims[i] self.dds[i] = range_handler.dds[i] self.handle = range_handler.handle self.artio_handle = range_handler.artio_handle self._last_mask = None self._last_selector_id = -1 self.sfc_start = range_handler.sfc_start self.sfc_end = range_handler.sfc_end self.range_handler = range_handler # We assume that the number of octs has been created and filled # already. We no longer care about ANY of the SFCs that have octs # inside them -- this goes for every operation that this object # performs. self.sfc_mask = malloc(sizeof(np.uint8_t) * self.sfc_end - self.sfc_start + 1) self.nsfc = 0 for sfci in range(self.sfc_end - self.sfc_start + 1): if self.range_handler.oct_count[sfci] > 0: self.sfc_mask[sfci] = 0 else: self.sfc_mask[sfci] = 1 self.nsfc += 1 def __dealloc__(self): free(self.sfc_mask) @cython.cdivision(True) cdef np.int64_t pos_to_sfc(self, np.float64_t pos[3]) noexcept nogil: # Calculate the index cdef int coords[3] cdef int i cdef np.int64_t sfc for i in range(3): coords[i] = ((pos[i] - self.DLE[i])/self.dds[i]) sfc = artio_sfc_index(self.handle, coords) return sfc @cython.cdivision(True) cdef void sfc_to_pos(self, np.int64_t sfc, np.float64_t pos[3]) noexcept nogil: cdef int coords[3] cdef int i artio_sfc_coords(self.handle, sfc, coords) for i in range(3): pos[i] = self.DLE[i] + (coords[i] + 0.5) * self.dds[i] cdef np.int64_t count_cells(self, SelectorObject selector): # We visit each cell if it is not refined and determine whether it is # included or not. return self.mask(selector).sum() @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def icoords(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): # Note that num_octs does not have to equal sfc_end - sfc_start + 1. cdef np.int64_t sfc, sfci = -1 cdef int acoords[3] cdef int i cdef np.ndarray[np.uint8_t, ndim=1, cast=True] mask mask = self.mask(selector) num_cells = self._last_mask_sum cdef np.ndarray[np.int64_t, ndim=2] coords coords = np.empty((num_cells, 3), dtype="int64") cdef int filled = 0 for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 if mask[sfci] == 0: continue # Note that we do *no* checks on refinement here. In fact, this # entire setup should not need to touch the disk except if the # artio sfc calculators need to. artio_sfc_coords(self.handle, sfc, acoords) for i in range(3): coords[filled, i] = acoords[i] filled += 1 return coords @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fcoords(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): # Note that num_cells does not have to equal sfc_end - sfc_start + 1. cdef np.int64_t sfc, sfci = -1 cdef np.float64_t pos[3] cdef int i cdef np.ndarray[np.uint8_t, ndim=1, cast=True] mask mask = self.mask(selector) num_cells = self._last_mask_sum cdef np.ndarray[np.float64_t, ndim=2] coords coords = np.empty((num_cells, 3), dtype="float64") cdef int filled = 0 for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 if mask[sfci] == 0: continue # Note that we do *no* checks on refinement here. In fact, this # entire setup should not need to touch the disk except if the # artio sfc calculators need to. self.sfc_to_pos(sfc, pos) for i in range(3): coords[filled, i] = pos[i] filled += 1 return coords @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fwidth(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): cdef int i num_cells = self._last_mask_sum cdef np.ndarray[np.float64_t, ndim=2] width width = np.zeros((num_cells, 3), dtype="float64") for i in range(3): width[:,i] = self.dds[i] return width @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def ires(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): # Note: self.mask has a side effect of setting self._last_mask_sum self.mask(selector) num_cells = self._last_mask_sum cdef np.ndarray[np.int64_t, ndim=1] res res = np.zeros(num_cells, dtype="int64") return res @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def selector_fill(self, SelectorObject selector, np.ndarray source, np.ndarray dest = None, np.int64_t offset = 0, int dims = 1, int domain_id = -1): # This is where we use the selector to transplant from one to the # other. Note that we *do* apply the selector here. cdef np.int64_t num_cells = -1 cdef np.int64_t ind cdef np.int64_t sfc, sfci = -1 cdef int filled = 0 cdef char *sdata = source.data cdef char *ddata cdef int ss = source.dtype.itemsize cdef np.ndarray[np.uint8_t, ndim=1, cast=True] mask mask = self.mask(selector) if dest is None: # Note that RAMSES can have partial refinement inside an Oct. This # means we actually do want the number of Octs, not the number of # cells. num_cells = self._last_mask_sum if dims > 1: dest = np.zeros((num_cells, dims), dtype=source.dtype, order='C') else: dest = np.zeros(num_cells, dtype=source.dtype, order='C') ddata = (dest.data) + offset*ss*dims ind = 0 for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 if mask[sfci] == 0: continue memcpy(ddata, sdata + ind, dims * ss) ddata += dims * ss filled += 1 ind += ss * dims if num_cells >= 0: return dest return filled @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def mask(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): # We take a domain_id here to avoid subclassing cdef np.float64_t pos[3] cdef np.int64_t sfc, sfci = -1 if self._last_selector_id == hash(selector): return self._last_mask cdef np.ndarray[np.uint8_t, ndim=1] mask mask = np.zeros((self.nsfc), dtype="uint8") self._last_mask_sum = 0 for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 self.sfc_to_pos(sfc, pos) if selector.select_cell(pos, self.dds) == 0: continue mask[sfci] = 1 self._last_mask_sum += 1 self._last_mask = mask.astype("bool") self._last_selector_id = hash(selector) return self._last_mask def fill_sfc_particles(self, fields): rv = read_sfc_particles(self.artio_handle, self.sfc_start, self.sfc_end, 1, fields, self.range_handler.doct_count, self.range_handler.pcount) return rv @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_sfc(self, SelectorObject selector, field_indices): cdef np.ndarray[np.float64_t, ndim=1] dest cdef int status, i, num_oct_levels, nf, ngv, max_level cdef np.int64_t sfc, num_cells, sfci = -1 cdef double dpos[3] max_level = self.artio_handle.max_level cdef int *num_octs_per_level = malloc( (max_level + 1)*sizeof(int)) # We duplicate some of the grid_variables stuff here so that we can # potentially release the GIL nf = len(field_indices) ngv = self.artio_handle.num_grid_variables cdef float *grid_variables = malloc( ngv * sizeof(float)) cdef np.ndarray[np.uint8_t, ndim=1, cast=True] mask mask = self.mask(selector, -1) num_cells = self._last_mask_sum tr = [] for i in range(nf): tr.append(np.zeros(num_cells, dtype="float64")) cdef int* field_ind = malloc( nf * sizeof(int)) cdef np.float64_t **field_vals = malloc( nf * sizeof(np.float64_t*)) for i in range(nf): field_ind[i] = field_indices[i] # This zeros should be an empty once we handle the root grid dest = tr[i] field_vals[i] = dest.data # First we need to walk the mesh in the file. Then we fill in the dest # location based on the file index. cdef int filled = 0 cdef float **mesh_data = self.range_handler.root_mesh_data if mesh_data == NULL: status = artio_grid_cache_sfc_range(self.handle, self.sfc_start, self.sfc_end) check_artio_status(status) for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 if mask[sfci] == 0: continue status = artio_grid_read_root_cell_begin( self.handle, sfc, dpos, grid_variables, &num_oct_levels, num_octs_per_level) check_artio_status(status) for i in range(nf): field_vals[i][filled] = grid_variables[field_ind[i]] filled += 1 status = artio_grid_read_root_cell_end(self.handle) check_artio_status(status) status = artio_grid_clear_sfc_cache(self.handle) check_artio_status(status) else: for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 if mask[sfci] == 0: continue for i in range(nf): field_vals[i][filled] = mesh_data[field_ind[i]][ sfc - self.sfc_start] filled += 1 # Now we have all our sources. free(field_ind) free(field_vals) free(grid_variables) free(num_octs_per_level) return tr @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def deposit(self, ParticleDepositOperation pdeposit, SelectorObject selector, np.ndarray[np.float64_t, ndim=2] positions, fields): # This implements the necessary calls to enable particle deposition to # occur as needed. cdef int nf, i, j cdef np.int64_t sfc, sfci if fields is None: fields = [] nf = len(fields) cdef np.float64_t[::cython.view.indirect, ::1] field_pointers if nf > 0: field_pointers = OnceIndirect(fields) cdef np.float64_t[:] field_vals = np.empty(nf, dtype="float64") cdef np.ndarray[np.uint8_t, ndim=1, cast=True] mask mask = self.mask(selector, -1) cdef np.ndarray[np.int64_t, ndim=1] domain_ind domain_ind = np.zeros(self.sfc_end - self.sfc_start + 1, dtype="int64") - 1 j = 0 sfci = -1 for sfc in range(self.sfc_start, self.sfc_end + 1): if self.sfc_mask[sfc - self.sfc_start] == 0: continue sfci += 1 if mask[sfci] == 0: continue domain_ind[sfc - self.sfc_start] = j j += 1 cdef np.float64_t pos[3] cdef np.float64_t left_edge[3] cdef int coords[3] cdef int dims[3] dims[0] = dims[1] = dims[2] = 1 cdef np.int64_t offset for i in range(positions.shape[0]): for j in range(nf): field_vals[j] = field_pointers[j][i] for j in range(3): pos[j] = positions[i, j] coords[j] = ((pos[j] - self.DLE[j])/self.dds[j]) sfc = artio_sfc_index(self.artio_handle.handle, coords) if sfc < self.sfc_start or sfc > self.sfc_end: continue offset = domain_ind[sfc - self.sfc_start] if offset < 0: continue # Check that we found the oct ... for j in range(3): left_edge[j] = coords[j] * self.dds[j] + self.DLE[j] pdeposit.process(dims, i, left_edge, self.dds, offset, pos, field_vals, sfc) if pdeposit.update_values == 1: for j in range(nf): field_pointers[j][i] = field_vals[j] cdef class SFCRangeSelector(SelectorObject): cdef SelectorObject base_selector cdef ARTIOSFCRangeHandler range_handler cdef ARTIORootMeshContainer mesh_container cdef np.int64_t sfc_start, sfc_end def __init__(self, dobj): self.base_selector = dobj.base_selector self.mesh_container = dobj.oct_handler self.range_handler = self.mesh_container.range_handler self.sfc_start = self.mesh_container.sfc_start self.sfc_end = self.mesh_container.sfc_end @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): raise RuntimeError @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return self.select_point(pos) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef np.int64_t sfc = self.mesh_container.pos_to_sfc(pos) if sfc > self.sfc_end: return 0 cdef np.int64_t oc = self.range_handler.doct_count[ sfc - self.sfc_start] if oc > 0: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: return self.base_selector.select_bbox(left_edge, right_edge) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: # Because visitors now use select_grid, we should be explicitly # checking this. return self.base_selector.select_grid(left_edge, right_edge, level, o) def _hash_vals(self): return (hash(self.base_selector), self.sfc_start, self.sfc_end) sfc_subset_selector = AlwaysSelector #sfc_subset_selector = SFCRangeSelector yt-project-yt-f043ac8/yt/frontends/artio/api.py000066400000000000000000000002001510711153200215410ustar00rootroot00000000000000from . import tests from .data_structures import ARTIODataset from .fields import ARTIOFieldInfo from .io import IOHandlerARTIO yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/000077500000000000000000000000001510711153200232375ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/LICENSE000066400000000000000000001243401510711153200242500ustar00rootroot00000000000000ARTIO is licensed under the GNU Lesser General Public License (LGPL) version 3, which is an extension of the GNU General Public License (GPL). The text of both licenses are included here. =============================================================================== GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. =============================================================================== GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . =============================================================================== yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio.c000066400000000000000000000172331510711153200245270ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif artio_fileset *artio_fileset_allocate( char *file_prefix, int mode, const artio_context *context ); void artio_fileset_destroy( artio_fileset *handle ); int artio_fh_buffer_size = ARTIO_DEFAULT_BUFFER_SIZE; int artio_fileset_set_buffer_size( int buffer_size ) { if ( buffer_size < 0 ) { return ARTIO_ERR_INVALID_BUFFER_SIZE; } artio_fh_buffer_size = buffer_size; return ARTIO_SUCCESS; } artio_fileset *artio_fileset_open(char * file_prefix, int type, const artio_context *context) { artio_fh *head_fh; char filename[256]; int ret; int64_t tmp; int artio_major, artio_minor; artio_fileset *handle = artio_fileset_allocate( file_prefix, ARTIO_FILESET_READ, context ); if ( handle == NULL ) { return NULL; } /* open header file */ sprintf(filename, "%s.art", handle->file_prefix); head_fh = artio_file_fopen(filename, ARTIO_MODE_READ | ARTIO_MODE_ACCESS, context); if ( head_fh == NULL ) { artio_fileset_destroy(handle); return NULL; } ret = artio_parameter_read(head_fh, handle->parameters ); if ( ret != ARTIO_SUCCESS ) { artio_fileset_destroy(handle); return NULL; } artio_file_fclose(head_fh); /* check versions */ if ( artio_parameter_get_int(handle, "ARTIO_MAJOR_VERSION", &artio_major ) == ARTIO_ERR_PARAM_NOT_FOUND ) { /* version pre 1.0 */ artio_major = 0; artio_minor = 9; } else { artio_parameter_get_int(handle, "ARTIO_MINOR_VERSION", &artio_minor ); } if ( artio_major > ARTIO_MAJOR_VERSION ) { fprintf(stderr,"ERROR: artio file version newer than library (%u.%u vs %u.%u).\n", artio_major, artio_minor, ARTIO_MAJOR_VERSION, ARTIO_MINOR_VERSION ); artio_fileset_destroy(handle); return NULL; } artio_parameter_get_long(handle, "num_root_cells", &handle->num_root_cells); if ( artio_parameter_get_int(handle, "sfc_type", &handle->sfc_type ) != ARTIO_SUCCESS ) { handle->sfc_type = ARTIO_SFC_HILBERT; } handle->nBitsPerDim = 0; tmp = handle->num_root_cells >> 3; while ( tmp ) { handle->nBitsPerDim++; tmp >>= 3; } handle->num_grid = 1<nBitsPerDim; /* default to accessing all sfc indices */ handle->proc_sfc_begin = 0; handle->proc_sfc_end = handle->num_root_cells-1; /* open data files */ if (type & ARTIO_OPEN_PARTICLES) { ret = artio_fileset_open_particles(handle); if ( ret != ARTIO_SUCCESS ) { artio_fileset_destroy(handle); return NULL; } } if (type & ARTIO_OPEN_GRID) { ret = artio_fileset_open_grid(handle); if ( ret != ARTIO_SUCCESS ) { artio_fileset_destroy(handle); return NULL; } } return handle; } artio_fileset *artio_fileset_create(char * file_prefix, int64_t root_cells, int64_t proc_sfc_begin, int64_t proc_sfc_end, const artio_context *context) { artio_fileset *handle = artio_fileset_allocate( file_prefix, ARTIO_FILESET_WRITE, context ); if ( handle == NULL ) { return NULL; } handle->proc_sfc_index = (int64_t*)malloc((handle->num_procs+1)*sizeof(int64_t)); if ( handle->proc_sfc_index == NULL ) { artio_fileset_destroy(handle); return NULL; } #ifdef ARTIO_MPI MPI_Allgather( &proc_sfc_begin, 1, MPI_LONG_LONG, handle->proc_sfc_index, 1, MPI_LONG_LONG, handle->context->comm ); #else handle->proc_sfc_index[0] = 0; #endif /* ARTIO_MPI */ handle->proc_sfc_index[handle->num_procs] = root_cells; handle->proc_sfc_begin = proc_sfc_begin; handle->proc_sfc_end = proc_sfc_end; handle->num_root_cells = root_cells; artio_parameter_set_long(handle, "num_root_cells", root_cells); artio_parameter_set_int(handle, "ARTIO_MAJOR_VERSION", ARTIO_MAJOR_VERSION ); artio_parameter_set_int(handle, "ARTIO_MINOR_VERSION", ARTIO_MINOR_VERSION ); return handle; } int artio_fileset_close(artio_fileset *handle) { char header_filename[256]; artio_fh *head_fh; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode == ARTIO_FILESET_WRITE) { /* ensure we've flushed open particle and * grid files before writing header */ if ( handle->grid != NULL ) { artio_fileset_close_grid(handle); } if ( handle->particle != NULL ) { artio_fileset_close_particles(handle); } sprintf(header_filename, "%s.art", handle->file_prefix); head_fh = artio_file_fopen(header_filename, ARTIO_MODE_WRITE | ((handle->rank == 0) ? ARTIO_MODE_ACCESS : 0), handle->context); if (head_fh == NULL) { return ARTIO_ERR_FILE_CREATE; } if (0 == handle->rank) { artio_parameter_write(head_fh, handle->parameters ); } artio_file_fclose(head_fh); } artio_fileset_destroy(handle); return ARTIO_SUCCESS; } artio_fileset *artio_fileset_allocate( char *file_prefix, int mode, const artio_context *context ) { int my_rank; int num_procs; artio_fileset *handle = (artio_fileset *)malloc(sizeof(artio_fileset)); if ( handle != NULL ) { handle->parameters = artio_parameter_list_init(); #ifdef ARTIO_MPI handle->context = (artio_context *)malloc(sizeof(artio_context)); if ( handle->context == NULL ) { return NULL; } memcpy( handle->context, context, sizeof(artio_context) ); MPI_Comm_size(handle->context->comm, &num_procs); MPI_Comm_rank(handle->context->comm, &my_rank); #else handle->context = NULL; num_procs = 1; my_rank = 0; #endif /* MPI */ strncpy(handle->file_prefix, file_prefix, 250); handle->open_mode = mode; handle->open_type = ARTIO_OPEN_HEADER; handle->rank = my_rank; handle->num_procs = num_procs; handle->endian_swap = 0; handle->proc_sfc_index = NULL; handle->proc_sfc_begin = -1; handle->proc_sfc_end = -1; handle->num_root_cells = -1; handle->grid = NULL; handle->particle = NULL; } return handle; } void artio_fileset_destroy( artio_fileset *handle ) { if ( handle == NULL ) return; if ( handle->proc_sfc_index != NULL ) free( handle->proc_sfc_index ); if ( handle->grid != NULL ) { artio_fileset_close_grid(handle); } if ( handle->particle != NULL ) { artio_fileset_close_particles(handle); } if ( handle->context != NULL ) free( handle->context ); artio_parameter_list_free(handle->parameters); free(handle); } int artio_fileset_has_grid( artio_fileset *handle ) { int num_grid_files = 0; return ( handle->grid != NULL || ( artio_parameter_get_int( handle, "num_grid_files", &num_grid_files ) == ARTIO_SUCCESS && num_grid_files > 0 ) ); } int artio_fileset_has_particles( artio_fileset *handle ) { int num_particle_files = 0; return ( handle->particle != NULL || ( artio_parameter_get_int( handle, "num_particle_files", &num_particle_files ) == ARTIO_SUCCESS && num_particle_files > 0 ) ); } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio.h000066400000000000000000000433561510711153200245410ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #ifndef __ARTIO_H__ #define __ARTIO_H__ #define ARTIO_MAJOR_VERSION 1 #define ARTIO_MINOR_VERSION 2 #ifdef ARTIO_MPI #include #endif #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif #define ARTIO_OPEN_HEADER 0 #define ARTIO_OPEN_PARTICLES 1 #define ARTIO_OPEN_GRID 2 #define ARTIO_READ_LEAFS 1 #define ARTIO_READ_REFINED 2 #define ARTIO_READ_ALL 3 #define ARTIO_RETURN_OCTS 4 #define ARTIO_RETURN_CELLS 0 /* allocation strategy */ #define ARTIO_ALLOC_EQUAL_SFC 0 #define ARTIO_ALLOC_EQUAL_PROC 1 #define ARTIO_ALLOC_MAX_FILE_SIZE 2 #define ARTIO_TYPE_STRING 0 #define ARTIO_TYPE_CHAR 1 #define ARTIO_TYPE_INT 2 #define ARTIO_TYPE_FLOAT 3 #define ARTIO_TYPE_DOUBLE 4 #define ARTIO_TYPE_LONG 5 /* error codes */ #define ARTIO_SUCCESS 0 #define ARTIO_ERR_PARAM_NOT_FOUND 1 #define ARTIO_PARAMETER_EXHAUSTED 2 #define ARTIO_ERR_PARAM_INVALID_LENGTH 3 #define ARTIO_ERR_PARAM_TYPE_MISMATCH 4 #define ARTIO_ERR_PARAM_LENGTH_MISMATCH 5 #define ARTIO_ERR_PARAM_LENGTH_INVALID 6 #define ARTIO_ERR_PARAM_DUPLICATE 7 #define ARTIO_ERR_PARAM_CORRUPTED 8 #define ARTIO_ERR_PARAM_CORRUPTED_MAGIC 9 #define ARTIO_ERR_STRING_LENGTH 10 #define ARTIO_ERR_INVALID_FILESET_MODE 100 #define ARTIO_ERR_INVALID_FILE_NUMBER 101 #define ARTIO_ERR_INVALID_FILE_MODE 102 #define ARTIO_ERR_INVALID_SFC_RANGE 103 #define ARTIO_ERR_INVALID_SFC 104 #define ARTIO_ERR_INVALID_STATE 105 #define ARTIO_ERR_INVALID_SEEK 106 #define ARTIO_ERR_INVALID_OCT_LEVELS 107 #define ARTIO_ERR_INVALID_SPECIES 108 #define ARTIO_ERR_INVALID_ALLOC_STRATEGY 109 #define ARTIO_ERR_INVALID_LEVEL 110 #define ARTIO_ERR_INVALID_PARAMETER_LIST 111 #define ARTIO_ERR_INVALID_DATATYPE 112 #define ARTIO_ERR_INVALID_OCT_REFINED 113 #define ARTIO_ERR_INVALID_HANDLE 114 #define ARTIO_ERR_INVALID_CELL_TYPES 115 #define ARTIO_ERR_INVALID_BUFFER_SIZE 116 #define ARTIO_ERR_INVALID_INDEX 117 #define ARTIO_ERR_DATA_EXISTS 200 #define ARTIO_ERR_INSUFFICIENT_DATA 201 #define ARTIO_ERR_FILE_CREATE 202 #define ARTIO_ERR_GRID_DATA_NOT_FOUND 203 #define ARTIO_ERR_GRID_FILE_NOT_FOUND 204 #define ARTIO_ERR_PARTICLE_DATA_NOT_FOUND 205 #define ARTIO_ERR_PARTICLE_FILE_NOT_FOUND 206 #define ARTIO_ERR_IO_OVERFLOW 207 #define ARTIO_ERR_IO_WRITE 208 #define ARTIO_ERR_IO_READ 209 #define ARTIO_ERR_BUFFER_EXISTS 210 #define ARTIO_SELECTION_EXHAUSTED 300 #define ARTIO_ERR_INVALID_SELECTION 301 #define ARTIO_ERR_INVALID_COORDINATES 302 #define ARTIO_ERR_MEMORY_ALLOCATION 400 #define ARTIO_ERR_VERSION_MISMATCH 500 #ifdef ARTIO_MPI typedef struct { MPI_Comm comm; } artio_context; #else typedef struct { int comm; } artio_context; #endif #define ARTIO_MAX_STRING_LENGTH 256 typedef struct artio_fileset_struct artio_fileset; typedef struct artio_selection_struct artio_selection; extern const artio_context *artio_context_global; /* * Description: Open the file * * filename The file prefix * type combination of ARTIO_OPEN_PARTICLES and ARTIO_OPEN_GRID flags */ artio_fileset *artio_fileset_open( char * file_name, int type, const artio_context *context); /** * Description: Create fileset and begin populating header information * * file_name file name of refined cells * root_cells the number of root level cells * proc_sfc_begin-end the range of local space-filling-curve indices * handle the artio file handle * */ artio_fileset *artio_fileset_create(char * file_prefix, int64_t root_cells, int64_t proc_sfc_begin, int64_t proc_sfc_end, const artio_context *context); /* * Description Close the file */ int artio_fileset_close(artio_fileset *handle); int artio_fileset_set_buffer_size( int buffer_size ); int artio_fileset_has_grid( artio_fileset *handle ); int artio_fileset_has_particles( artio_fileset *handle ); /* public parameter interface */ int artio_parameter_iterate( artio_fileset *handle, char *key, int *type, int *length ); int artio_parameter_get_array_length(artio_fileset *handle, const char * key, int *length); int artio_parameter_set_int(artio_fileset *handle, const char * key, int32_t value); int artio_parameter_get_int(artio_fileset *handle, const char * key, int32_t * value); int artio_parameter_set_int_array(artio_fileset *handle, const char * key, int length, int32_t *values); int artio_parameter_get_int_array(artio_fileset *handle, const char * key, int length, int32_t *values); int artio_parameter_get_int_array_index(artio_fileset *handle, const char * key, int index, int32_t *values); int artio_parameter_set_string(artio_fileset *handle, const char * key, char * value); int artio_parameter_get_string(artio_fileset *handle, const char * key, char * value ); int artio_parameter_set_string_array(artio_fileset *handle, const char * key, int length, char ** values); int artio_parameter_get_string_array(artio_fileset *handle, const char * key, int length, char ** values ); int artio_parameter_get_string_array_index(artio_fileset *handle, const char * key, int index, char * values ); int artio_parameter_set_float(artio_fileset *handle, const char * key, float value); int artio_parameter_get_float(artio_fileset *handle, const char * key, float * value); int artio_parameter_set_float_array(artio_fileset *handle, const char * key, int length, float *values); int artio_parameter_get_float_array(artio_fileset *handle, const char * key, int length, float * values); int artio_parameter_get_float_array_index(artio_fileset *handle, const char * key, int index, float * values); int artio_parameter_set_double(artio_fileset *handle, const char * key, double value); int artio_parameter_get_double(artio_fileset *handle, const char * key, double * value); int artio_parameter_set_double_array(artio_fileset *handle, const char * key, int length, double * values); int artio_parameter_get_double_array(artio_fileset *handle, const char * key, int length, double *values); int artio_parameter_get_double_array_index(artio_fileset *handle, const char * key, int index, double *values); int artio_parameter_set_long(artio_fileset *handle, const char * key, int64_t value); int artio_parameter_get_long(artio_fileset *handle, const char * key, int64_t *value); int artio_parameter_set_long_array(artio_fileset *handle, const char * key, int length, int64_t *values); int artio_parameter_get_long_array(artio_fileset *handle, const char * key, int length, int64_t *values); int artio_parameter_get_long_array_index(artio_fileset *handle, const char * key, int index, int64_t *values); /* public grid interface */ typedef void (* artio_grid_callback)( int64_t sfc_index, int level, double *pos, float * variables, int *refined, void *params ); /* * Description: Add a grid component to a fileset open for writing * * handle The fileset handle * num_grid_files The number of grid files to create * allocation_strategy How to apportion sfc indices to each grid file * num_grid_variables The number of variables per cell * grid_variable_labels Identifying labels for each variable * num_levels_per_root_tree Maximum tree depth for each oct tree * num_octs_per_root_tree Total octs in each oct tree */ int artio_fileset_add_grid(artio_fileset *handle, int num_grid_files, int allocation_strategy, int num_grid_variables, char ** grid_variable_labels, int * num_levels_per_root_tree, int * num_octs_per_root_tree ); int artio_fileset_open_grid(artio_fileset *handle); int artio_fileset_close_grid(artio_fileset *handle); /* * Description: Output the variables of the root level cell and the index of the Oct tree correlated with this root level cell * * handle The File handle * sfc The sfc index of root cell * variables The variables of the root level cell * level The depth of the Oct tree correlated to the root level cell * num_level_octs The array store the number of Oct nodes each level */ int artio_grid_write_root_cell_begin(artio_fileset *handle, int64_t sfc, float * variables, int level, int * num_octs_per_level); /* * Description: Do something at the end of writing the root level cell */ int artio_grid_write_root_cell_end(artio_fileset *handle); /* * Description: Do something at the beginning of each level */ int artio_grid_write_level_begin(artio_fileset *handle, int level ); /* * Description: Do something at the end of each level */ int artio_grid_write_level_end(artio_fileset *handle); /* * Description: Output the data of a special oct tree node to the file * * handle The handle of the file * variables The array recording the variables of the eight cells belonging to this Octree node. */ int artio_grid_write_oct(artio_fileset *handle, float *variables, int *refined); /* * Description: Read the variables of the root level cell and the index of the Octree * correlated with this root level cell * * handle The File handle * variables The variables of the root level cell * level The depth of the OCT tree * num_octs_per_level The number of node of each oct level * */ int artio_grid_read_root_cell_begin(artio_fileset *handle, int64_t sfc, double *pos, float *variables, int *num_tree_levels, int *num_octs_per_level); /* * Description: Do something at the end of reading the root level cell */ int artio_grid_read_root_cell_end(artio_fileset *handle); /* * Description: Do something at the beginning of each level */ int artio_grid_read_level_begin(artio_fileset *handle, int level ); /* * Description: Do something at the end of each level */ int artio_grid_read_level_end(artio_fileset *handle); /* * Description: Read the data of a special oct tree node from the file */ int artio_grid_read_oct(artio_fileset *handle, double *pos, float *variables, int *refined); int artio_grid_cache_sfc_range(artio_fileset *handle, int64_t sfc_start, int64_t sfc_end); int artio_grid_clear_sfc_cache(artio_fileset *handle ); int artio_grid_count_octs_in_sfc_range(artio_fileset *handle, int64_t start, int64_t end, int64_t *num_octs_in_range ); /* * Description: Read a segment of oct nodes * * handle file pointer * sfc1 the start sfc index * sfc2 the end sfc index * max_level_to_read max level to read for each oct tree * option 1. refined nodes; 2 leaf nodes; 3 all nodes * callback callback function * params a pointer to user-defined data passed to the callback */ int artio_grid_read_sfc_range_levels(artio_fileset *handle, int64_t sfc1, int64_t sfc2, int min_level_to_read, int max_level_to_read, int options, artio_grid_callback callback, void *params ); int artio_grid_read_sfc_range(artio_fileset *handle, int64_t sfc1, int64_t sfc2, int options, artio_grid_callback callback, void *params ); int artio_grid_read_selection(artio_fileset *handle, artio_selection *selection, int options, artio_grid_callback callback, void *params ); int artio_grid_read_selection_levels( artio_fileset *handle, artio_selection *selection, int min_level_to_read, int max_level_to_read, int options, artio_grid_callback callback, void *params ); /** * header head file name * num_particle_files the number of files to record refined cells * allocation_strategy * num_species number of particle species * species_labels string identifier for each species * handle the artio file handle * */ int artio_fileset_add_particles(artio_fileset *handle, int num_particle_files, int allocation_strategy, int num_species, char **species_labels, int *num_primary_variables, int *num_secondary_variables, char ***primary_variable_labels_per_species, char ***secondary_variable_labels_per_species, int *num_particles_per_species_per_root_tree ); int artio_fileset_open_particles(artio_fileset *handle); int artio_fileset_close_particles(artio_fileset *handle); /* * Description: Output the variables of the root level cell and the index of * the oct-tree correlated with this root level cell * * handle The File handle * sfc The sfc index of root cell * variables The variables of the root level cell * level The depth of the Oct tree correlated to the root level cell * num_level_octs The array store the number of Oct nodes each level */ int artio_particle_write_root_cell_begin(artio_fileset *handle, int64_t sfc, int *num_particles_per_species); /* * Description: Do something at the end of writing the root level cell */ int artio_particle_write_root_cell_end(artio_fileset *handle); /* * Description: Do something at the beginning of each level */ int artio_particle_write_species_begin(artio_fileset *handle, int species ); /* * Description: Do something at the end of each level */ int artio_particle_write_species_end(artio_fileset *handle); /* * Description: Output the data of a special oct tree node to the file * * handle The handle of the file * variables The array recording the variables of the eight cells belonging to this Octree node. */ int artio_particle_write_particle(artio_fileset *handle, int64_t pid, int subspecies, double* primary_variables, float *secondary_variables); /* * Description: Read the variables of the root level cell and the index of the Octree * correlated with this root level cell * * handle The File handle * variables The variables of the root level cell * level The depth of the OCT tree * num_octs_per_level The number of node of each oct level * */ int artio_particle_read_root_cell_begin(artio_fileset *handle, int64_t sfc, int * num_particle_per_species); /* * Description: Do something at the end of reading the root level cell */ int artio_particle_read_root_cell_end(artio_fileset *handle); /* * Description: Do something at the beginning of each level */ int artio_particle_read_species_begin(artio_fileset *handle, int species ); /* * Description: Do something at the end of each level */ int artio_particle_read_species_end(artio_fileset *handle); /* * Description: Read the data of a single particle from the file */ int artio_particle_read_particle(artio_fileset *handle, int64_t *pid, int *subspecies, double *primary_variables, float *secondary_variables); int artio_particle_cache_sfc_range(artio_fileset *handle, int64_t sfc_start, int64_t sfc_end); int artio_particle_clear_sfc_cache(artio_fileset *handle ); typedef void (* artio_particle_callback)(int64_t sfc_index, int species, int subspecies, int64_t pid, double *primary_variables, float *secondary_variables, void *params ); /* * Description: Read a segment of particles * * handle file pointer * sfc1 the start sfc index * sfc2 the end sfc index * start_species the first particle species to read * end_species the last particle species to read * callback callback function * params user defined data passed to the callback function */ int artio_particle_read_sfc_range(artio_fileset *handle, int64_t sfc1, int64_t sfc2, artio_particle_callback callback, void *params); int artio_particle_read_sfc_range_species( artio_fileset *handle, int64_t sfc1, int64_t sfc2, int start_species, int end_species, artio_particle_callback callback, void *params); int artio_particle_read_selection(artio_fileset *handle, artio_selection *selection, artio_particle_callback callback, void *params ); int artio_particle_read_selection_species( artio_fileset *handle, artio_selection *selection, int start_species, int end_species, artio_particle_callback callback, void *params ); artio_selection *artio_selection_allocate( artio_fileset *handle ); artio_selection *artio_select_all( artio_fileset *handle ); artio_selection *artio_select_volume( artio_fileset *handle, double lpos[3], double rpos[3] ); artio_selection *artio_select_cube( artio_fileset *handle, double center[3], double size ); int artio_selection_add_root_cell( artio_selection *selection, int coords[3] ); int artio_selection_destroy( artio_selection *selection ); void artio_selection_print( artio_selection *selection ); int artio_selection_iterator( artio_selection *selection, int64_t max_range_size, int64_t *start, int64_t *end ); int artio_selection_iterator_reset( artio_selection *selection ); int64_t artio_selection_size( artio_selection *selection ); #endif /* __ARTIO_H__ */ yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_endian.c000066400000000000000000000045121510711153200260410ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio_endian.h" #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif void artio_int_swap(int32_t *src, int count) { int i; union { int32_t f; unsigned char c[4]; } d1, d2; for ( i = 0; i < count; i++ ) { d1.f = src[i]; d2.c[0] = d1.c[3]; d2.c[1] = d1.c[2]; d2.c[2] = d1.c[1]; d2.c[3] = d1.c[0]; src[i] = d2.f; } } void artio_float_swap(float *src, int count) { int i; union { float f; unsigned char c[4]; } d1, d2; for ( i = 0; i < count; i++ ) { d1.f = src[i]; d2.c[0] = d1.c[3]; d2.c[1] = d1.c[2]; d2.c[2] = d1.c[1]; d2.c[3] = d1.c[0]; src[i] = d2.f; } } void artio_double_swap(double *src, int count) { int i; union { double d; unsigned char c[8]; } d1, d2; for ( i = 0; i < count; i++ ) { d1.d = src[i]; d2.c[0] = d1.c[7]; d2.c[1] = d1.c[6]; d2.c[2] = d1.c[5]; d2.c[3] = d1.c[4]; d2.c[4] = d1.c[3]; d2.c[5] = d1.c[2]; d2.c[6] = d1.c[1]; d2.c[7] = d1.c[0]; src[i] = d2.d; } } void artio_long_swap(int64_t *src, int count) { int i; union { int64_t d; unsigned char c[8]; } d1, d2; for ( i = 0; i < count; i++ ) { d1.d = src[i]; d2.c[0] = d1.c[7]; d2.c[1] = d1.c[6]; d2.c[2] = d1.c[5]; d2.c[3] = d1.c[4]; d2.c[4] = d1.c[3]; d2.c[5] = d1.c[2]; d2.c[6] = d1.c[1]; d2.c[7] = d1.c[0]; src[i] = d2.d; } } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_endian.h000066400000000000000000000025471510711153200260540ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #ifndef __ARTIO_EDIAN_H__ #define __ARTIO_EDIAN_H__ #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif void artio_int_swap(int32_t *src, int count); void artio_float_swap(float *src, int count); void artio_double_swap(double *src, int count); void artio_long_swap(int64_t *src, int count); #endif /* __ARTIO_ENDIAN_H__ */ yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_file.c000066400000000000000000000120601510711153200255170ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include artio_fh *artio_file_fopen( char * filename, int mode, const artio_context *context) { artio_fh *fh; #ifdef ARTIO_DEBUG printf( "artio_file_fopen( filename=%s, mode=%d, context=%p )\n", filename, mode, context ); fflush(stdout); #endif /* ARTIO_DEBUG */ fh = artio_file_fopen_i(filename,mode,context); #ifdef ARTIO_DEBUG printf(" artio_file_fopen = %p\n", fh ); fflush(stdout); #endif /* ARTIO_DEBUG */ return fh; } int artio_file_attach_buffer( artio_fh *handle, void *buf, int buf_size ) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_attach_buffer( handle=%p, buf=%p, buf_size = %d )\n", handle, buf, buf_size ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_attach_buffer_i(handle,buf,buf_size); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf(" artio_file_attach_buffer(%p) = %d\n", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_detach_buffer( artio_fh *handle ) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_detach_buffer( handle=%p )\n", handle ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_detach_buffer_i(handle); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf( "artio_file_detach_buffer(%p) = %d\n", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_fwrite( artio_fh *handle, const void *buf, int64_t count, int type ) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_fwrite( handle=%p, buf=%p, count=%ld, type=%d )\n", handle, buf, count, type ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_fwrite_i(handle,buf,count,type); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf( "artio_file_fwrite(%p) = %d", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_fflush(artio_fh *handle) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_fflush( handle=%p )\n", handle ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_fflush_i(handle); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf( "artio_file_fflush(%p) = %d\n", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_fread(artio_fh *handle, void *buf, int64_t count, int type ) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_fread( handle=%p, buf=%p, count=%ld, type=%d )\n", handle, buf, count, type ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_fread_i(handle,buf,count,type); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf( "artio_file_fread(%p) = %d", handle, status ); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_ftell(artio_fh *handle, int64_t *offset) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_ftell( handle=%p, offset=%p )\n", handle, offset ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_ftell_i(handle,offset); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf("artio_file_ftell(%p) = %d\n", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_fseek(artio_fh *handle, int64_t offset, int whence ) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_fseek( handle=%p, offset=%ld, whence=%d )\n", handle, offset, whence ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_fseek_i(handle,offset,whence); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf( "artio_file_fseek(%p) = %d\n", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } int artio_file_fclose(artio_fh *handle) { int status; #ifdef ARTIO_DEBUG printf( "artio_file_fclose( handle=%p )\n", handle ); fflush(stdout); #endif /* ARTIO_DEBUG */ status = artio_file_fclose_i(handle); #ifdef ARTIO_DEBUG if ( status != ARTIO_SUCCESS ) { printf( "artio_file_fclose(%p) = %d\n", handle, status ); fflush(stdout); } #endif /* ARTIO_DEBUG */ return status; } void artio_file_set_endian_swap_tag(artio_fh *handle) { artio_file_set_endian_swap_tag_i(handle); } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_grid.c000066400000000000000000001107631510711153200255360ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif int artio_grid_find_file(artio_grid_file *ghandle, int start, int end, int64_t sfc); artio_grid_file *artio_grid_file_allocate(void); void artio_grid_file_destroy(artio_grid_file *ghandle); const double oct_pos_offsets[8][3] = { { -0.5, -0.5, -0.5 }, { 0.5, -0.5, -0.5 }, { -0.5, 0.5, -0.5 }, { 0.5, 0.5, -0.5 }, { -0.5, -0.5, 0.5 }, { 0.5, -0.5, 0.5 }, { -0.5, 0.5, 0.5 }, { 0.5, 0.5, 0.5 } }; /* * Open grid component of the fileset */ int artio_fileset_open_grid(artio_fileset *handle) { int i; char filename[256]; int first_file, last_file; int mode; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } /* check that the fileset doesn't already contain a grid component */ if ( handle->open_type & ARTIO_OPEN_GRID || handle->open_mode != ARTIO_FILESET_READ || handle->grid != NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } handle->open_type |= ARTIO_OPEN_GRID; ghandle = artio_grid_file_allocate(); if ( ghandle == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } /* load grid parameters from header file */ if ( artio_parameter_get_int(handle, "num_grid_files", &ghandle->num_grid_files) != ARTIO_SUCCESS || artio_parameter_get_int( handle, "num_grid_variables", &ghandle->num_grid_variables ) != ARTIO_SUCCESS ) { return ARTIO_ERR_GRID_DATA_NOT_FOUND; } ghandle->file_sfc_index = (int64_t *)malloc(sizeof(int64_t) * (ghandle->num_grid_files + 1)); if ( ghandle->file_sfc_index == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } artio_parameter_get_long_array(handle, "grid_file_sfc_index", ghandle->num_grid_files + 1, ghandle->file_sfc_index); artio_parameter_get_int(handle, "grid_max_level", &ghandle->file_max_level); ghandle->octs_per_level = (int *)malloc(ghandle->file_max_level * sizeof(int)); if ( ghandle->octs_per_level == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } ghandle->ffh = (artio_fh **)malloc(ghandle->num_grid_files * sizeof(artio_fh *)); if ( ghandle->ffh == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } for ( i = 0; i < ghandle->num_grid_files; i++ ) { ghandle->ffh[i] = NULL; } first_file = artio_grid_find_file(ghandle, 0, ghandle->num_grid_files, handle->proc_sfc_begin); last_file = artio_grid_find_file(ghandle, first_file, ghandle->num_grid_files, handle->proc_sfc_end); /* open files on all processes */ for (i = 0; i < ghandle->num_grid_files; i++) { sprintf(filename, "%s.g%03d", handle->file_prefix, i); mode = ARTIO_MODE_READ; if (i >= first_file && i <= last_file) { mode |= ARTIO_MODE_ACCESS; } if (handle->endian_swap) { mode |= ARTIO_MODE_ENDIAN_SWAP; } ghandle->ffh[i] = artio_file_fopen(filename, mode, handle->context); if ( ghandle->ffh[i] == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_GRID_FILE_NOT_FOUND; } } handle->grid = ghandle; return ARTIO_SUCCESS; } int artio_fileset_add_grid(artio_fileset *handle, int num_grid_files, int allocation_strategy, int num_grid_variables, char ** grid_variable_labels, int * num_levels_per_root_tree, int * num_octs_per_root_tree ) { int i; int file_max_level, local_max_level; int64_t cur, sfc, l; int64_t first_file_sfc, last_file_sfc; int first_file, last_file; char filename[256]; int mode; int ret; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( handle->open_mode != ARTIO_FILESET_WRITE ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( handle->open_type & ARTIO_OPEN_GRID) { return ARTIO_ERR_DATA_EXISTS; } handle->open_type |= ARTIO_OPEN_GRID; artio_parameter_set_int(handle, "num_grid_files", num_grid_files); artio_parameter_set_int(handle, "num_grid_variables", num_grid_variables); artio_parameter_set_string_array(handle, "grid_variable_labels", num_grid_variables, grid_variable_labels); ghandle = artio_grid_file_allocate(); if ( ghandle == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } ghandle->file_sfc_index = (int64_t *)malloc(sizeof(int64_t) * (num_grid_files + 1)); if ( ghandle->file_sfc_index == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } /* compute global maximum level */ local_max_level = 0; for (sfc = 0; sfc < handle->proc_sfc_end - handle->proc_sfc_begin + 1; sfc++) { if (num_levels_per_root_tree[sfc] > local_max_level) { local_max_level = num_levels_per_root_tree[sfc]; } } #ifdef ARTIO_MPI MPI_Allreduce( &local_max_level, &file_max_level, 1, MPI_INT, MPI_MAX, handle->context->comm ); #else file_max_level = local_max_level; #endif /* ARTIO_MPI */ switch (allocation_strategy) { case ARTIO_ALLOC_EQUAL_PROC: if (num_grid_files > handle->num_procs) { return ARTIO_ERR_INVALID_FILE_NUMBER; } for (i = 0; i < num_grid_files; i++) { ghandle->file_sfc_index[i] = handle->proc_sfc_index[(handle->num_procs*i+num_grid_files-1) / num_grid_files]; } ghandle->file_sfc_index[num_grid_files] = handle->proc_sfc_index[handle->num_procs]; break; case ARTIO_ALLOC_EQUAL_SFC: if ( num_grid_files > handle->num_root_cells ) { return ARTIO_ERR_INVALID_FILE_NUMBER; } for (i = 0; i < num_grid_files; i++) { ghandle->file_sfc_index[i] = (handle->num_root_cells*i+num_grid_files-1) / num_grid_files; } ghandle->file_sfc_index[num_grid_files] = handle->num_root_cells; break; default: artio_grid_file_destroy(ghandle); return ARTIO_ERR_INVALID_ALLOC_STRATEGY; } ghandle->num_grid_files = num_grid_files; ghandle->num_grid_variables = num_grid_variables; ghandle->file_max_level = file_max_level; /* allocate space for sfc offset cache */ ghandle->cache_sfc_begin = handle->proc_sfc_begin; ghandle->cache_sfc_end = handle->proc_sfc_end; ghandle->sfc_offset_table = (int64_t *)malloc((size_t)(ghandle->cache_sfc_end - ghandle->cache_sfc_begin + 1) * sizeof(int64_t)); if ( ghandle->sfc_offset_table == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } ghandle->octs_per_level = (int *)malloc(ghandle->file_max_level * sizeof(int)); if ( ghandle->octs_per_level == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } /* allocate file handles */ ghandle->ffh = (artio_fh **)malloc(num_grid_files * sizeof(artio_fh *)); if ( ghandle->ffh == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_MEMORY_ALLOCATION; } for ( i = 0; i < num_grid_files; i++ ) { ghandle->ffh[i] = NULL; } /* open file handles */ first_file = artio_grid_find_file(ghandle, 0, num_grid_files, handle->proc_sfc_begin); last_file = artio_grid_find_file(ghandle, first_file, num_grid_files, handle->proc_sfc_end); if ( first_file < 0 || first_file >= num_grid_files || last_file < first_file || last_file >= num_grid_files ) { return ARTIO_ERR_INVALID_FILE_NUMBER; } for (i = 0; i < num_grid_files; i++) { sprintf(filename, "%s.g%03d", handle->file_prefix, i); mode = ARTIO_MODE_WRITE; if (i >= first_file && i <= last_file) { mode |= ARTIO_MODE_ACCESS; } ghandle->ffh[i] = artio_file_fopen(filename, mode, handle->context); if ( ghandle->ffh[i] == NULL ) { artio_grid_file_destroy(ghandle); return ARTIO_ERR_FILE_CREATE; } /* write sfc offset header if we contribute to this file */ if (i >= first_file && i <= last_file) { #ifdef ARTIO_MPI if (ghandle->file_sfc_index[i] >= handle->proc_sfc_index[ handle->rank ] && ghandle->file_sfc_index[i] < handle->proc_sfc_index[ handle->rank + 1] ) { cur = (ghandle->file_sfc_index[i + 1] - ghandle->file_sfc_index[i]) * sizeof(int64_t); } else { /* obtain offset from previous process */ MPI_Recv( &cur, 1, MPI_LONG_LONG_INT, handle->rank - 1, i, handle->context->comm, MPI_STATUS_IGNORE ); } #else cur = (ghandle->file_sfc_index[i + 1] - ghandle->file_sfc_index[i]) * sizeof(int64_t); #endif /* ARTIO_MPI */ first_file_sfc = MAX( handle->proc_sfc_begin, ghandle->file_sfc_index[i] ); last_file_sfc = MIN( handle->proc_sfc_end, ghandle->file_sfc_index[i+1]-1 ); for (l = first_file_sfc - ghandle->cache_sfc_begin; l < last_file_sfc - ghandle->cache_sfc_begin + 1; l++) { ghandle->sfc_offset_table[l] = cur; cur += sizeof(float) * ghandle->num_grid_variables + sizeof(int) * (1 + num_levels_per_root_tree[l]) + num_octs_per_root_tree[l] * 8 * (sizeof(float) * ghandle->num_grid_variables + sizeof(int)); } #ifdef ARTIO_MPI if ( ghandle->file_sfc_index[i+1] > handle->proc_sfc_end+1 ) { MPI_Send( &cur, 1, MPI_LONG_LONG_INT, handle->rank + 1, i, handle->context->comm ); } #endif /* ARTIO_MPI */ /* seek and write our portion of sfc table */ ret = artio_file_fseek(ghandle->ffh[i], (first_file_sfc - ghandle->file_sfc_index[i]) * sizeof(int64_t), ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) { artio_grid_file_destroy(ghandle); return ret; } ret = artio_file_fwrite(ghandle->ffh[i], &ghandle->sfc_offset_table[first_file_sfc - ghandle->cache_sfc_begin], last_file_sfc - first_file_sfc + 1, ARTIO_TYPE_LONG); if ( ret != ARTIO_SUCCESS ) { artio_grid_file_destroy(ghandle); return ret; } } } handle->grid = ghandle; artio_parameter_set_long_array(handle, "grid_file_sfc_index", ghandle->num_grid_files + 1, ghandle->file_sfc_index); artio_parameter_set_int(handle, "grid_max_level", ghandle->file_max_level); return ARTIO_SUCCESS; } artio_grid_file *artio_grid_file_allocate(void) { artio_grid_file *ghandle = (artio_grid_file *)malloc(sizeof(struct artio_grid_file_struct)); if ( ghandle != NULL ) { ghandle->ffh = NULL; ghandle->num_grid_variables = -1; ghandle->num_grid_files = -1; ghandle->file_sfc_index = NULL; ghandle->cache_sfc_begin = -1; ghandle->cache_sfc_end = -1; ghandle->sfc_offset_table = NULL; ghandle->file_max_level = -1; ghandle->cur_file = -1; ghandle->cur_num_levels = -1; ghandle->cur_level = -1; ghandle->cur_octs = -1; ghandle->cur_sfc = -1; ghandle->octs_per_level = NULL; ghandle->pos_flag = 0; ghandle->pos_cur_level = -1; ghandle->next_level_size = -1; ghandle->cur_level_size = -1; ghandle->cell_size_level = 1e20; ghandle->next_level_pos = NULL; ghandle->cur_level_pos = NULL; ghandle->next_level_oct = -1; ghandle->buffer_size = artio_fh_buffer_size; ghandle->buffer = malloc(ghandle->buffer_size); if ( ghandle->buffer == NULL ) { free(ghandle); return NULL; } } return ghandle; } void artio_grid_file_destroy(artio_grid_file *ghandle) { int i; if ( ghandle == NULL ) return; if ( ghandle->ffh != NULL ) { for (i = 0; i < ghandle->num_grid_files; i++) { if ( ghandle->ffh[i] != NULL ) { artio_file_fclose(ghandle->ffh[i]); } } free(ghandle->ffh); } if ( ghandle->sfc_offset_table != NULL ) free(ghandle->sfc_offset_table); if ( ghandle->octs_per_level != NULL ) free(ghandle->octs_per_level); if ( ghandle->file_sfc_index != NULL ) free(ghandle->file_sfc_index); if ( ghandle->next_level_pos != NULL ) free(ghandle->next_level_pos); if ( ghandle->cur_level_pos != NULL ) free(ghandle->cur_level_pos); if ( ghandle->buffer != NULL ) free( ghandle->buffer ); free(ghandle); } int artio_fileset_close_grid(artio_fileset *handle) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } artio_grid_file_destroy(handle->grid); handle->grid = NULL; return ARTIO_SUCCESS; } int artio_grid_count_octs_in_sfc_range(artio_fileset *handle, int64_t start, int64_t end, int64_t *num_octs_in_range ) { int i; int ret; int file, first; int64_t sfc; int64_t offset, next_offset, size_offset; int num_oct_levels; int *num_octs_per_level; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( start > end || start < handle->proc_sfc_begin || end > handle->proc_sfc_end ) { return ARTIO_ERR_INVALID_SFC_RANGE; } ghandle = handle->grid; /* check that we're not in the middle of a read */ if ( ghandle->cur_sfc != -1 ) { return ARTIO_ERR_INVALID_STATE; } *num_octs_in_range = 0; if ( 8*ghandle->num_grid_variables <= ghandle->file_max_level ) { /* we can't compute the number of octs through the offset table */ ret = artio_grid_cache_sfc_range( handle, start, end ); if ( ret != ARTIO_SUCCESS ) return ret; num_octs_per_level = (int *)malloc(ghandle->file_max_level*sizeof(int) ); if ( num_octs_per_level == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } for ( sfc = start; sfc <= end; sfc++ ) { ret = artio_grid_read_root_cell_begin( handle, sfc, NULL, NULL, &num_oct_levels, num_octs_per_level ); if ( ret != ARTIO_SUCCESS ) return ret; for ( i = 0; i < num_oct_levels; i++ ) { *num_octs_in_range += num_octs_per_level[i]; } ret = artio_grid_read_root_cell_end( handle ); if ( ret != ARTIO_SUCCESS ) return ret; } free( num_octs_per_level ); } else { /* TODO: add optimization if sfc range already cached */ file = artio_grid_find_file(ghandle, 0, ghandle->num_grid_files, start); first = MAX( 0, start - ghandle->file_sfc_index[file] ); ret = artio_file_fseek(ghandle->ffh[file], sizeof(int64_t) * first, ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(ghandle->ffh[file], &offset, 1, ARTIO_TYPE_LONG ); if ( ret != ARTIO_SUCCESS ) return ret; sfc = start; while ( sfc <= end ) { /* read next offset or compute end of file*/ if ( sfc < ghandle->file_sfc_index[file+1] - 1 ) { ret = artio_file_fread(ghandle->ffh[file], &size_offset, 1, ARTIO_TYPE_LONG ); if ( ret != ARTIO_SUCCESS ) return ret; next_offset = size_offset; } else { /* need to seek and ftell */ artio_file_fseek( ghandle->ffh[file], 0, ARTIO_SEEK_END ); artio_file_ftell( ghandle->ffh[file], &size_offset ); file++; if ( sfc < end && file < ghandle->num_grid_files ) { artio_file_fseek( ghandle->ffh[file], 0, ARTIO_SEEK_SET ); ret = artio_file_fread(ghandle->ffh[file], &next_offset, 1, ARTIO_TYPE_LONG ); if ( ret != ARTIO_SUCCESS ) return ret; } } /* this assumes (num_levels_per_root_tree)*sizeof(int) < * size of an oct, or 8*num_variables > max_level so the * number of levels drops off in rounding to int */ *num_octs_in_range += (size_offset - offset - sizeof(float)*ghandle->num_grid_variables - sizeof(int) ) / (8*(sizeof(float)*ghandle->num_grid_variables + sizeof(int) )); offset = next_offset; sfc++; } } return ARTIO_SUCCESS; } int artio_grid_cache_sfc_range(artio_fileset *handle, int64_t start, int64_t end) { int i; int ret; int first_file, last_file; int64_t first, count, cur; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( start > end || start < handle->proc_sfc_begin || end > handle->proc_sfc_end ) { return ARTIO_ERR_INVALID_SFC_RANGE; } ghandle = handle->grid; /* check if we've already cached the range */ if ( start >= ghandle->cache_sfc_begin && end <= ghandle->cache_sfc_end ) { return ARTIO_SUCCESS; } artio_grid_clear_sfc_cache(handle); first_file = artio_grid_find_file(ghandle, 0, ghandle->num_grid_files, start); last_file = artio_grid_find_file(ghandle, first_file, ghandle->num_grid_files, end); ghandle->cache_sfc_begin = start; ghandle->cache_sfc_end = end; ghandle->sfc_offset_table = (int64_t *)malloc(sizeof(int64_t) * (size_t)(end - start + 1)); if ( ghandle->sfc_offset_table == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } if ( ghandle->cur_file != -1 ) { artio_file_detach_buffer( ghandle->ffh[ghandle->cur_file]); ghandle->cur_file = -1; } cur = 0; for (i = first_file; i <= last_file; i++) { first = MAX( 0, start - ghandle->file_sfc_index[i] ); count = MIN( ghandle->file_sfc_index[i+1], end+1 ) - MAX( start, ghandle->file_sfc_index[i]); artio_file_attach_buffer( ghandle->ffh[i], ghandle->buffer, ghandle->buffer_size ); ret = artio_file_fseek(ghandle->ffh[i], sizeof(int64_t) * first, ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(ghandle->ffh[i], &ghandle->sfc_offset_table[cur], count, ARTIO_TYPE_LONG); if ( ret != ARTIO_SUCCESS ) return ret; artio_file_detach_buffer( ghandle->ffh[i] ); cur += count; } return ARTIO_SUCCESS; } int artio_grid_clear_sfc_cache( artio_fileset *handle ) { artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if ( ghandle->sfc_offset_table != NULL ) { free(ghandle->sfc_offset_table); ghandle->sfc_offset_table = NULL; } ghandle->cache_sfc_begin = -1; ghandle->cache_sfc_end = -1; return ARTIO_SUCCESS; } int artio_grid_find_file(artio_grid_file *ghandle, int start, int end, int64_t sfc) { int j; if ( start < 0 || start > ghandle->num_grid_files || end < 0 || end > ghandle->num_grid_files || sfc < ghandle->file_sfc_index[start] || sfc >= ghandle->file_sfc_index[end] ) { return -1; } if (start == end || sfc == ghandle->file_sfc_index[start]) { return start; } if (1 == end - start) { if (sfc < ghandle->file_sfc_index[end]) { return start; } else { return end; } } j = start + (end - start) / 2; if (sfc > ghandle->file_sfc_index[j]) { return artio_grid_find_file(ghandle, j, end, sfc); } else if (sfc < ghandle->file_sfc_index[j]) { return artio_grid_find_file(ghandle, start, j, sfc); } else { return j; } } int artio_grid_seek_to_sfc(artio_fileset *handle, int64_t sfc) { int64_t offset; artio_grid_file *ghandle; int file; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (ghandle->cache_sfc_begin == -1 || sfc < ghandle->cache_sfc_begin || sfc > ghandle->cache_sfc_end) { return ARTIO_ERR_INVALID_SFC; } file = artio_grid_find_file(ghandle, 0, ghandle->num_grid_files, sfc ); if ( file != ghandle->cur_file ) { if ( ghandle->cur_file != -1 ) { artio_file_detach_buffer( ghandle->ffh[ghandle->cur_file] ); } if ( ghandle->buffer_size > 0 ) { artio_file_attach_buffer( ghandle->ffh[file], ghandle->buffer, ghandle->buffer_size ); } ghandle->cur_file = file; } offset = ghandle->sfc_offset_table[sfc - ghandle->cache_sfc_begin]; return artio_file_fseek(ghandle->ffh[ghandle->cur_file], offset, ARTIO_SEEK_SET); } int artio_grid_write_root_cell_begin(artio_fileset *handle, int64_t sfc, float *variables, int num_oct_levels, int *num_octs_per_level) { int i; int ret; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (num_oct_levels < 0 || num_oct_levels > ghandle->file_max_level) { return ARTIO_ERR_INVALID_OCT_LEVELS; } ret = artio_grid_seek_to_sfc(handle, sfc); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(ghandle->ffh[ghandle->cur_file], variables, ghandle->num_grid_variables, ARTIO_TYPE_FLOAT); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(ghandle->ffh[ghandle->cur_file], &num_oct_levels, 1, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(ghandle->ffh[ghandle->cur_file], num_octs_per_level, num_oct_levels, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; for (i = 0; i < num_oct_levels; i++) { ghandle->octs_per_level[i] = num_octs_per_level[i]; } ghandle->cur_sfc = sfc; ghandle->cur_num_levels = num_oct_levels; ghandle->cur_level = -1; ghandle->cur_octs = 0; return ARTIO_SUCCESS; } int artio_grid_write_root_cell_end(artio_fileset *handle) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } handle->grid->cur_sfc = -1; return ARTIO_SUCCESS; } int artio_grid_write_level_begin(artio_fileset *handle, int level) { artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (ghandle->cur_sfc == -1 || level <= 0 || level > ghandle->cur_num_levels) { return ARTIO_ERR_INVALID_STATE; } ghandle->cur_level = level; return ARTIO_SUCCESS; } int artio_grid_write_level_end(artio_fileset *handle) { artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (ghandle->cur_level == -1 || ghandle->cur_octs != ghandle->octs_per_level[ghandle->cur_level - 1] ) { return ARTIO_ERR_INVALID_STATE; } ghandle->cur_level = -1; ghandle->cur_octs = 0; return ARTIO_SUCCESS; } int artio_grid_write_oct(artio_fileset *handle, float *variables, int *cellrefined) { int i; int ret; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (ghandle->cur_level == -1 || ghandle->cur_octs >= ghandle->octs_per_level[ghandle->cur_level - 1]) { return ARTIO_ERR_INVALID_STATE; } /* check that no last-level octs have refined cells */ if ( ghandle->cur_level == ghandle->cur_num_levels ) { for ( i = 0; i < 8; i++ ) { if ( cellrefined[i] > 0) { return ARTIO_ERR_INVALID_OCT_REFINED; } } } ret = artio_file_fwrite(ghandle->ffh[ghandle->cur_file], variables, 8 * ghandle->num_grid_variables, ARTIO_TYPE_FLOAT); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(ghandle->ffh[ghandle->cur_file], cellrefined, 8, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; ghandle->cur_octs++; return ARTIO_SUCCESS; } /* * */ int artio_grid_read_root_cell_begin(artio_fileset *handle, int64_t sfc, double *pos, float *variables, int *num_oct_levels, int *num_octs_per_level) { int i; int ret; int coords[3]; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; ret = artio_grid_seek_to_sfc(handle, sfc); if ( ret != ARTIO_SUCCESS ) return ret; if ( variables == NULL ) { ret = artio_file_fseek( ghandle->ffh[ghandle->cur_file], ghandle->num_grid_variables*sizeof(float), ARTIO_SEEK_CUR ); if ( ret != ARTIO_SUCCESS ) return ret; } else { ret = artio_file_fread(ghandle->ffh[ghandle->cur_file], variables, ghandle->num_grid_variables, ARTIO_TYPE_FLOAT); if ( ret != ARTIO_SUCCESS ) return ret; } ret = artio_file_fread(ghandle->ffh[ghandle->cur_file], num_oct_levels, 1, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; if ( *num_oct_levels > ghandle->file_max_level || *num_oct_levels < 0 ) { printf("*num_oct_levels = %d\n", *num_oct_levels ); return ARTIO_ERR_INVALID_OCT_LEVELS; } if ( pos != NULL ) { ghandle->pos_flag = 1; /* compute position from sfc */ artio_sfc_coords( handle, sfc, coords ); for ( i = 0; i < 3; i++ ) { pos[i] = (double)coords[i] + 0.5; } if ( *num_oct_levels > 0 ) { /* compute next level position */ if ( ghandle->next_level_pos == NULL ) { ghandle->next_level_pos = (double *)malloc( 3*sizeof(double) ); if ( ghandle->next_level_pos == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } ghandle->next_level_size = 1; } for ( i = 0; i < 3; i++ ) { ghandle->next_level_pos[i] = pos[i]; } ghandle->pos_cur_level = 0; } else { ghandle->pos_cur_level = -1; } } else { ghandle->pos_flag = 0; } if (*num_oct_levels > 0) { ret = artio_file_fread(ghandle->ffh[ghandle->cur_file], num_octs_per_level, *num_oct_levels, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; for (i = 0; i < *num_oct_levels; i++) { ghandle->octs_per_level[i] = num_octs_per_level[i]; } } ghandle->cur_sfc = sfc; ghandle->cur_num_levels = *num_oct_levels; ghandle->cur_level = -1; return ARTIO_SUCCESS; } int artio_grid_read_oct(artio_fileset *handle, double *pos, float *variables, int *refined) { int i, j; int ret; int local_refined[8]; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (ghandle->cur_level == -1 || ghandle->cur_octs > ghandle->octs_per_level[ghandle->cur_level - 1] || (pos != NULL && !ghandle->pos_flag )) { return ARTIO_ERR_INVALID_STATE; } if ( variables == NULL ) { ret = artio_file_fseek(ghandle->ffh[ghandle->cur_file], 8*ghandle->num_grid_variables*sizeof(float), ARTIO_SEEK_CUR ); if ( ret != ARTIO_SUCCESS ) return ret; } else { ret = artio_file_fread(ghandle->ffh[ghandle->cur_file], variables, 8 * ghandle->num_grid_variables, ARTIO_TYPE_FLOAT); if ( ret != ARTIO_SUCCESS ) return ret; } if ( !ghandle->pos_flag && refined == NULL ) { ret = artio_file_fseek(ghandle->ffh[ghandle->cur_file], 8*sizeof(int), ARTIO_SEEK_CUR ); if ( ret != ARTIO_SUCCESS ) return ret; } else { ret = artio_file_fread(ghandle->ffh[ghandle->cur_file], local_refined, 8, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; } if ( refined != NULL ) { for ( i = 0; i < 8; i++ ) { refined[i] = (local_refined[i] > 0); } } if ( ghandle->pos_flag ) { if ( pos != NULL ) { for ( i = 0; i < 3; i++ ) { pos[i] = ghandle->cur_level_pos[3*ghandle->cur_octs + i]; } } for ( i = 0; i < 8; i++ ) { if ( local_refined[i] > 0) { if ( ghandle->next_level_oct >= ghandle->next_level_size ) { return ARTIO_ERR_INVALID_STATE; } for ( j = 0; j < 3; j++ ) { ghandle->next_level_pos[3*ghandle->next_level_oct+j] = ghandle->cur_level_pos[3*ghandle->cur_octs + j] + ghandle->cell_size_level*oct_pos_offsets[i][j]; } ghandle->next_level_oct++; } } } ghandle->cur_octs++; return ARTIO_SUCCESS; } /* * Description Obtain data from an appointed level tree node */ int artio_grid_read_level_begin(artio_fileset *handle, int level) { int i; int ret; int64_t offset = 0; artio_grid_file *ghandle; int tmp_size; double *tmp_pos; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if ( ghandle->cur_sfc == -1 || level <= 0 || level > ghandle->cur_num_levels || (ghandle->pos_flag && ghandle->pos_cur_level != level - 1) ) { return ARTIO_ERR_INVALID_STATE; } if ( ghandle->pos_flag ) { ghandle->cell_size_level = 1.0 / (double)(1<cur_level_pos; tmp_size = ghandle->cur_level_size; ghandle->cur_level_pos = ghandle->next_level_pos; ghandle->cur_level_size = ghandle->next_level_size; ghandle->next_level_pos = tmp_pos; ghandle->next_level_size = tmp_size; ghandle->pos_cur_level = level; if ( level < ghandle->cur_num_levels ) { /* ensure the buffer for the next level positions is large enough */ if ( ghandle->octs_per_level[level] > ghandle->next_level_size ) { if ( ghandle->next_level_pos != NULL ) { free( ghandle->next_level_pos ); } ghandle->next_level_pos = (double *)malloc( 3*ghandle->octs_per_level[level]*sizeof(double) ); if ( ghandle->next_level_pos == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } ghandle->next_level_size = ghandle->octs_per_level[level]; } ghandle->next_level_oct = 0; } } offset = ghandle->sfc_offset_table[ghandle->cur_sfc - ghandle->cache_sfc_begin]; offset += sizeof(float) * ghandle->num_grid_variables + sizeof(int) * (ghandle->cur_num_levels + 1); for (i = 0; i < level - 1; i++) { offset += 8 * (sizeof(float) * ghandle->num_grid_variables + sizeof(int)) * ghandle->octs_per_level[i]; } ret = artio_file_fseek(ghandle->ffh[ghandle->cur_file], offset, ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) return ret; ghandle->cur_level = level; ghandle->cur_octs = 0; return ARTIO_SUCCESS; } /* * Description Do something at the end of each kind of read operation */ int artio_grid_read_level_end(artio_fileset *handle) { artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } ghandle = handle->grid; if (ghandle->cur_level == -1 && ( ghandle->cur_level < ghandle->cur_num_levels - 1 || ghandle->next_level_oct != ghandle->octs_per_level[ghandle->cur_level] ) ) { return ARTIO_ERR_INVALID_STATE; } ghandle->cur_level = -1; ghandle->cur_octs = -1; ghandle->next_level_oct = -1; return ARTIO_SUCCESS; } int artio_grid_read_root_cell_end(artio_fileset *handle) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } handle->grid->cur_sfc = -1; handle->grid->cur_level = -1; handle->grid->pos_flag = 0; handle->grid->pos_cur_level = -1; return ARTIO_SUCCESS; } int artio_grid_read_sfc_range_levels(artio_fileset *handle, int64_t sfc1, int64_t sfc2, int min_level_to_read, int max_level_to_read, int options, artio_grid_callback callback, void *params ) { int i, j; int64_t sfc; int oct, level; int ret; int *octs_per_level = NULL; int refined; int oct_refined[8]; int root_tree_levels; float *variables = NULL; double pos[3], cell_pos[3]; artio_grid_file *ghandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( ( (options & ARTIO_RETURN_CELLS) && !(options & ARTIO_READ_LEAFS) && !(options & ARTIO_READ_REFINED)) || ( (options & ARTIO_RETURN_OCTS) && ((options & ARTIO_READ_LEAFS) || (options & ARTIO_READ_REFINED) ) && !((options & ARTIO_READ_ALL) == ARTIO_READ_ALL ) ) ) { return ARTIO_ERR_INVALID_CELL_TYPES; } ghandle = handle->grid; if ((min_level_to_read < 0) || (min_level_to_read > max_level_to_read)) { return ARTIO_ERR_INVALID_LEVEL; } octs_per_level = (int *)malloc(ghandle->file_max_level * sizeof(int)); variables = (float *)malloc(8*ghandle->num_grid_variables * sizeof(float)); if ( octs_per_level == NULL || variables == NULL ) { if ( octs_per_level != NULL ) free(octs_per_level); if ( variables != NULL ) free(variables); return ARTIO_ERR_MEMORY_ALLOCATION; } ret = artio_grid_cache_sfc_range(handle, sfc1, sfc2); if ( ret != ARTIO_SUCCESS ) { free(octs_per_level); free(variables); return ret; } for (sfc = sfc1; sfc <= sfc2; sfc++) { ret = artio_grid_read_root_cell_begin(handle, sfc, pos, variables, &root_tree_levels, octs_per_level); if ( ret != ARTIO_SUCCESS ) { free(octs_per_level); free(variables); return ret; } if (min_level_to_read == 0 && ((options & ARTIO_READ_REFINED && root_tree_levels > 0) || (options & ARTIO_READ_LEAFS && root_tree_levels == 0)) ) { refined = (root_tree_levels > 0) ? 1 : 0; callback( sfc, 0, pos, variables, &refined, params ); } for (level = MAX(min_level_to_read,1); level <= MIN(root_tree_levels,max_level_to_read); level++) { ret = artio_grid_read_level_begin(handle, level); if ( ret != ARTIO_SUCCESS ) { free(octs_per_level); free(variables); return ret; } for (oct = 0; oct < octs_per_level[level - 1]; oct++) { ret = artio_grid_read_oct(handle, pos, variables, oct_refined); if ( ret != ARTIO_SUCCESS ) { free(octs_per_level); free(variables); return ret; } if ( options & ARTIO_RETURN_OCTS ) { callback( sfc, level, pos, variables, oct_refined, params ); } else { for (i = 0; i < 8; i++) { if ( (options & ARTIO_READ_REFINED && oct_refined[i]>0) || (options & ARTIO_READ_LEAFS && oct_refined[i]<=0) ) { for ( j = 0; j < 3; j++ ) { cell_pos[j] = pos[j] + ghandle->cell_size_level*oct_pos_offsets[i][j]; } callback( sfc, level, cell_pos, &variables[i * ghandle->num_grid_variables], &oct_refined[i], params ); } } } } artio_grid_read_level_end(handle); } artio_grid_read_root_cell_end(handle); } free(variables); free(octs_per_level); artio_grid_clear_sfc_cache(handle); return ARTIO_SUCCESS; } int artio_grid_read_sfc_range(artio_fileset *handle, int64_t sfc1, int64_t sfc2, int options, artio_grid_callback callback, void *params) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } return artio_grid_read_sfc_range_levels( handle, sfc1, sfc2, 0, handle->grid->file_max_level, options, callback, params ); } int artio_grid_read_selection(artio_fileset *handle, artio_selection *selection, int options, artio_grid_callback callback, void *params ) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } return artio_grid_read_selection_levels( handle, selection, 0, handle->grid->file_max_level, options, callback, params ); } int artio_grid_read_selection_levels( artio_fileset *handle, artio_selection *selection, int min_level_to_read, int max_level_to_read, int options, artio_grid_callback callback, void *params ) { int ret; int64_t start, end; /* loop over selected ranges */ artio_selection_iterator_reset( selection ); while ( artio_selection_iterator( selection, handle->num_root_cells, &start, &end ) == ARTIO_SUCCESS ) { ret = artio_grid_read_sfc_range_levels( handle, start, end, min_level_to_read, max_level_to_read, options, callback, params); if ( ret != ARTIO_SUCCESS ) return ret; } return ARTIO_SUCCESS; } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_internal.h000066400000000000000000000147331510711153200264320ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #ifndef __ARTIO_INTERNAL_H__ #define __ARTIO_INTERNAL_H__ #ifdef ARTIO_MPI #include #endif #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif #include "artio_endian.h" #ifndef ARTIO_DEFAULT_BUFFER_SIZE #define ARTIO_DEFAULT_BUFFER_SIZE 65536 #endif extern int artio_fh_buffer_size; #define nDim 3 #ifndef MIN #define MIN(x,y) (((x) < (y)) ? (x): (y)) #endif #ifndef MAX #define MAX(x,y) (((x) > (y)) ? (x): (y)) #endif /* limit individual writes to 32-bit safe quantity */ #define ARTIO_IO_MAX (1<<30) #ifdef INT64_MAX #define ARTIO_INT64_MAX INT64_MAX #else #define ARTIO_INT64_MAX 0x7fffffffffffffffLL #endif typedef struct ARTIO_FH artio_fh; typedef struct artio_particle_file_struct { artio_fh **ffh; void *buffer; int buffer_size; int num_particle_files; int64_t *file_sfc_index; int64_t cache_sfc_begin; int64_t cache_sfc_end; int64_t *sfc_offset_table; /* maintained for consistency and user-error detection */ int num_species; int cur_file; int cur_species; int cur_particle; int64_t cur_sfc; int *num_primary_variables; int *num_secondary_variables; int *num_particles_per_species; } artio_particle_file; typedef struct artio_grid_file_struct { artio_fh **ffh; void *buffer; int buffer_size; int num_grid_variables; int num_grid_files; int64_t *file_sfc_index; int64_t cache_sfc_begin; int64_t cache_sfc_end; int64_t *sfc_offset_table; int file_max_level; /* maintained for consistency and user-error detection */ int cur_file; int cur_num_levels; int cur_level; int cur_octs; int64_t cur_sfc; int *octs_per_level; int pos_flag; int pos_cur_level; int next_level_size; int cur_level_size; double cell_size_level; double *next_level_pos; double *cur_level_pos; int next_level_oct; } artio_grid_file; typedef struct parameter_struct { int key_length; char key[64]; int val_length; int type; char *value; struct parameter_struct * next; } parameter; typedef struct parameter_list_struct { parameter * head; parameter * tail; parameter * cursor; int iterate_flag; } parameter_list; struct artio_fileset_struct { char file_prefix[256]; int endian_swap; int open_type; int open_mode; int rank; int num_procs; artio_context *context; int64_t *proc_sfc_index; int64_t proc_sfc_begin; int64_t proc_sfc_end; int64_t num_root_cells; int sfc_type; int nBitsPerDim; int num_grid; parameter_list *parameters; artio_grid_file *grid; artio_particle_file *particle; }; struct artio_selection_struct { int64_t *list; int size; int num_ranges; int cursor; int64_t subcycle; artio_fileset *fileset; }; #define ARTIO_FILESET_READ 0 #define ARTIO_FILESET_WRITE 1 #define ARTIO_MODE_READ 1 #define ARTIO_MODE_WRITE 2 #define ARTIO_MODE_ACCESS 4 #define ARTIO_MODE_ENDIAN_SWAP 8 #define ARTIO_SEEK_SET 0 #define ARTIO_SEEK_CUR 1 #define ARTIO_SEEK_END 2 /* wrapper functions for profiling and debugging */ artio_fh *artio_file_fopen( char * filename, int amode, const artio_context *context ); int artio_file_attach_buffer( artio_fh *handle, void *buf, int buf_size ); int artio_file_detach_buffer( artio_fh *handle ); int artio_file_fwrite(artio_fh *handle, const void *buf, int64_t count, int type ); int artio_file_ftell( artio_fh *handle, int64_t *offset ); int artio_file_fflush(artio_fh *handle); int artio_file_fseek(artio_fh *ffh, int64_t offset, int whence); int artio_file_fread(artio_fh *handle, void *buf, int64_t count, int type ); int artio_file_fclose(artio_fh *handle); void artio_file_set_endian_swap_tag(artio_fh *handle); /* internal versions */ artio_fh *artio_file_fopen_i( char * filename, int amode, const artio_context *context ); int artio_file_attach_buffer_i( artio_fh *handle, void *buf, int buf_size ); int artio_file_detach_buffer_i( artio_fh *handle ); int artio_file_fwrite_i(artio_fh *handle, const void *buf, int64_t count, int type ); int artio_file_ftell_i( artio_fh *handle, int64_t *offset ); int artio_file_fflush_i(artio_fh *handle); int artio_file_fseek_i(artio_fh *ffh, int64_t offset, int whence); int artio_file_fread_i(artio_fh *handle, void *buf, int64_t count, int type ); int artio_file_fclose_i(artio_fh *handle); void artio_file_set_endian_swap_tag_i(artio_fh *handle); #define ARTIO_ENDIAN_MAGIC 0x1234 parameter_list *artio_parameter_list_init(void); parameter *artio_parameter_list_search(parameter_list *parameters, const char *key); int artio_parameter_array_length( parameter *item ); int artio_parameter_list_insert(parameter_list *parameters, const char *key, int length, void * value, int type); int artio_parameter_read(artio_fh *handle, parameter_list *parameters); int artio_parameter_write(artio_fh *handle, parameter_list *parameters); int artio_parameter_list_print(parameter_list *parameters); int artio_parameter_list_free(parameter_list *parameters); int artio_parameter_list_print(parameter_list *parameters); size_t artio_type_size( int type ); int artio_parameter_list_unpack(parameter_list *parameters, const char *key, int length, void *value, int type ); #define ARTIO_SFC_SLAB_X 0 #define ARTIO_SFC_MORTION 1 #define ARTIO_SFC_HILBERT 2 #define ARTIO_SFC_SLAB_Y 3 #define ARTIO_SFC_SLAB_Z 4 int64_t artio_sfc_index_position( artio_fileset *handle, double position[nDim] ); int64_t artio_sfc_index( artio_fileset *handle, int coords[nDim] ); void artio_sfc_coords( artio_fileset *handle, int64_t index, int coords[nDim] ); #endif /* __ARTIO_INTERNAL_H__ */ yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_mpi.c000066400000000000000000000233701510711153200253730ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #ifdef ARTIO_MPI #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif artio_context artio_context_global_struct = { MPI_COMM_WORLD }; const artio_context *artio_context_global = &artio_context_global_struct; struct ARTIO_FH { MPI_File fh; MPI_Comm comm; int mode; char *data; int bfsize; int bfptr; int bfend; }; artio_fh *artio_file_fopen_i( char * filename, int mode, const artio_context *context) { int status; int flag; int rank; int amode; if ( mode & ARTIO_MODE_WRITE && mode & ARTIO_MODE_READ ) { return NULL; } else if ( mode & ARTIO_MODE_WRITE ) { amode = MPI_MODE_CREATE | MPI_MODE_WRONLY; } else if ( mode & ARTIO_MODE_READ ) { amode = MPI_MODE_RDONLY; } else { return NULL; } artio_fh *ffh = (artio_fh *)malloc(sizeof(artio_fh)); if ( ffh == NULL ) { return NULL; } ffh->mode = mode; ffh->data = NULL; ffh->bfsize = -1; ffh->bfend = -1; ffh->bfptr = -1; flag = mode & ARTIO_MODE_ACCESS; MPI_Comm_rank( context->comm, &rank ); MPI_Comm_split( context->comm, flag, rank, &ffh->comm ); if ( flag ) { status = MPI_File_open( ffh->comm, filename, amode, MPI_INFO_NULL, &ffh->fh); if (status != MPI_SUCCESS) { MPI_Comm_free(&ffh->comm); free( ffh ); return NULL; } /* truncate the file on write */ if ( mode & ARTIO_MODE_WRITE ) { MPI_File_set_size(ffh->fh, 0); } } return ffh; } int artio_file_attach_buffer_i( artio_fh *handle, void *buf, int buf_size ) { if ( !(handle->mode & ARTIO_MODE_ACCESS ) ) { return ARTIO_ERR_INVALID_FILE_MODE; } if ( handle->data != NULL ) { return ARTIO_ERR_BUFFER_EXISTS; } handle->bfsize = buf_size; handle->bfend = -1; handle->bfptr = 0; handle->data = (char *)buf; return ARTIO_SUCCESS; } int artio_file_detach_buffer_i( artio_fh *handle ) { int ret; ret = artio_file_fflush(handle); if ( ret != ARTIO_SUCCESS ) return ret; handle->data = NULL; handle->bfsize = -1; handle->bfend = -1; handle->bfptr = -1; return ARTIO_SUCCESS; } int artio_file_fwrite_i( artio_fh *handle, const void *buf, int64_t count, int type ) { size_t size; int64_t remain; int size32; char *p; if ( !(handle->mode & ARTIO_MODE_WRITE) || !(handle->mode & ARTIO_MODE_ACCESS) ) { return ARTIO_ERR_INVALID_FILE_MODE; } size = artio_type_size( type ); if ( size == (size_t)-1 ) { return ARTIO_ERR_INVALID_DATATYPE; } if ( count > ARTIO_INT64_MAX / size ) { return ARTIO_ERR_IO_OVERFLOW; } remain = size*count; p = (char *)buf; if ( handle->data == NULL ) { while ( remain > 0 ) { size32 = MIN( ARTIO_IO_MAX, remain ); if ( MPI_File_write( handle->fh, p, size32, MPI_BYTE, MPI_STATUS_IGNORE ) != MPI_SUCCESS ) { return ARTIO_ERR_IO_WRITE; } remain -= size32; p += size32; } } else if ( remain < handle->bfsize - handle->bfptr ) { memcpy( handle->data + handle->bfptr, p, (size_t)remain ); handle->bfptr += remain; } else { /* complete buffer */ size32 = handle->bfsize - handle->bfptr; memcpy( handle->data + handle->bfptr, p, size32 ); if ( MPI_File_write(handle->fh, handle->data, handle->bfsize, MPI_BYTE, MPI_STATUS_IGNORE ) != MPI_SUCCESS ) { return ARTIO_ERR_IO_WRITE; } p += size32; remain -= size32; while ( remain > handle->bfsize ) { if ( MPI_File_write(handle->fh, p, handle->bfsize, MPI_BYTE, MPI_STATUS_IGNORE ) != MPI_SUCCESS ) { return ARTIO_ERR_IO_WRITE; } remain -= handle->bfsize; p += handle->bfsize; } memcpy( handle->data, p, (size_t)remain); handle->bfptr = remain; } return ARTIO_SUCCESS; } int artio_file_fflush_i(artio_fh *handle) { if ( !(handle->mode & ARTIO_MODE_ACCESS) ) { return ARTIO_ERR_INVALID_FILE_MODE; } if ( handle->mode & ARTIO_MODE_WRITE ) { if ( handle->bfptr > 0 ) { if ( MPI_File_write(handle->fh, handle->data, handle->bfptr, MPI_BYTE, MPI_STATUS_IGNORE ) != MPI_SUCCESS ) { return ARTIO_ERR_IO_WRITE; } handle->bfptr = 0; } } else if ( handle->mode & ARTIO_MODE_READ ) { handle->bfend = -1; handle->bfptr = 0; } else { return ARTIO_ERR_INVALID_FILE_MODE; } return ARTIO_SUCCESS; } int artio_file_fread_i(artio_fh *handle, void *buf, int64_t count, int type ) { MPI_Status status; size_t size, avail, remain; int size_read, size32; char *p; if ( !(handle->mode & ARTIO_MODE_READ) ) { return ARTIO_ERR_INVALID_FILE_MODE; } size = artio_type_size( type ); if ( size == (size_t)-1 ) { return ARTIO_ERR_INVALID_DATATYPE; } if ( count > ARTIO_INT64_MAX / size ) { return ARTIO_ERR_IO_OVERFLOW; } remain = size*count; p = (char *)buf; if ( handle->data == NULL ) { while ( remain > 0 ) { size32 = MIN( ARTIO_IO_MAX, remain ); if ( MPI_File_read(handle->fh, p, size32, MPI_BYTE, &status ) != MPI_SUCCESS ) { return ARTIO_ERR_IO_READ; } MPI_Get_count( &status, MPI_BYTE, &size_read ); if ( size_read != size32 ) { return ARTIO_ERR_INSUFFICIENT_DATA; } remain -= size32; p += size32; } } else { if ( handle->bfend == -1 ) { /* load initial data into buffer */ if ( MPI_File_read(handle->fh, handle->data, handle->bfsize, MPI_BYTE, &status) != MPI_SUCCESS ) { return ARTIO_ERR_IO_READ; } MPI_Get_count(&status, MPI_BYTE, &handle->bfend); handle->bfptr = 0; } /* read from buffer */ while ( remain > 0 && handle->bfend > 0 && handle->bfptr + remain >= handle->bfend ) { avail = handle->bfend - handle->bfptr; memcpy( p, handle->data + handle->bfptr, avail ); p += avail; remain -= avail; /* refill buffer */ if ( MPI_File_read(handle->fh, handle->data, handle->bfsize, MPI_BYTE, &status ) != MPI_SUCCESS ) { return ARTIO_ERR_IO_READ; } MPI_Get_count(&status, MPI_BYTE, &handle->bfend ); handle->bfptr = 0; } if ( remain > 0 ) { if ( handle->bfend == 0 ) { /* ran out of data, eof */ return ARTIO_ERR_INSUFFICIENT_DATA; } memcpy( p, handle->data + handle->bfptr, (size_t)remain ); handle->bfptr += (int)remain; } } if(handle->mode & ARTIO_MODE_ENDIAN_SWAP){ switch (type) { case ARTIO_TYPE_INT : artio_int_swap( (int32_t *)buf, count ); break; case ARTIO_TYPE_FLOAT : artio_float_swap( (float *)buf, count ); break; case ARTIO_TYPE_DOUBLE : artio_double_swap( (double *)buf, count ); break; case ARTIO_TYPE_LONG : artio_long_swap( (int64_t *)buf, count ); break; default : return ARTIO_ERR_INVALID_DATATYPE; } } return ARTIO_SUCCESS; } int artio_file_ftell_i(artio_fh *handle, int64_t *offset) { MPI_Offset current; MPI_File_get_position( handle->fh, ¤t ); if ( handle->bfend > 0 ) { current -= handle->bfend; } if ( handle->bfptr > 0 ) { current += handle->bfptr; } *offset = (int64_t)current; return ARTIO_SUCCESS; } int artio_file_fseek_i(artio_fh *handle, int64_t offset, int whence ) { MPI_Offset current; if ( handle->mode & ARTIO_MODE_ACCESS ) { if ( whence == ARTIO_SEEK_CUR ) { if ( offset == 0 ) { return ARTIO_SUCCESS; } else if ( handle->mode & ARTIO_MODE_READ && handle->bfend > 0 && handle->bfptr + offset >= 0 && handle->bfptr + offset < handle->bfend ) { handle->bfptr += offset; return ARTIO_SUCCESS; } else { if ( handle->bfptr > 0 ) { current = (MPI_Offset)offset - handle->bfend + handle->bfptr; } else { current = (MPI_Offset)offset; } artio_file_fflush( handle ); MPI_File_seek( handle->fh, current, MPI_SEEK_CUR ); } } else if ( whence == ARTIO_SEEK_SET ) { MPI_File_get_position( handle->fh, ¤t ); if (handle->mode & ARTIO_MODE_WRITE && current <= offset && offset < current + handle->bfsize && handle->bfptr == offset - current ) { return ARTIO_SUCCESS; } else if ( handle->mode & ARTIO_MODE_READ && handle->bfptr > 0 && handle->bfend > 0 && handle->bfptr < handle->bfend && offset >= current - handle->bfend && offset < current ) { handle->bfptr = offset - current + handle->bfend; } else { artio_file_fflush( handle ); MPI_File_seek( handle->fh, (MPI_Offset)offset, MPI_SEEK_SET ); } } else if ( whence == ARTIO_SEEK_END ) { artio_file_fflush(handle); MPI_File_seek( handle->fh, (MPI_Offset)offset, MPI_SEEK_END ); } else { /* unknown whence */ return ARTIO_ERR_INVALID_SEEK; } } else { /* seek on non-active file handle */ return ARTIO_ERR_INVALID_FILE_MODE; } return ARTIO_SUCCESS; } int artio_file_fclose_i(artio_fh *handle) { if ( handle->mode & ARTIO_MODE_ACCESS ) { artio_file_fflush(handle); MPI_File_close(&handle->fh); } MPI_Comm_free(&handle->comm); free(handle); return ARTIO_SUCCESS; } void artio_file_set_endian_swap_tag_i(artio_fh *handle) { handle->mode |= ARTIO_MODE_ENDIAN_SWAP; } #endif /* MPI */ yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_mpi.h000066400000000000000000000003441510711153200253740ustar00rootroot00000000000000/* * artio_mpi.h * * Created on: Mar 6, 2012 * Author: Nick Gnedin */ #ifndef __ARTIO_MPI_H__ #define __ARTIO_MPI_H__ #include struct artio_context_struct { MPI_Comm comm; }; #endif /* __ARTIO_MPI_H__ */ yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_parameter.c000066400000000000000000000365401510711153200265710ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif size_t artio_type_size(int type) { size_t t_len=0; switch (type) { case ARTIO_TYPE_STRING: case ARTIO_TYPE_CHAR: t_len = sizeof(char); break; case ARTIO_TYPE_INT: t_len = sizeof(int32_t); break; case ARTIO_TYPE_FLOAT: t_len = sizeof(float); break; case ARTIO_TYPE_DOUBLE: t_len = sizeof(double); break; case ARTIO_TYPE_LONG: t_len = sizeof(int64_t); break; default: t_len = (size_t)-1; break; } return t_len; } parameter_list *artio_parameter_list_init() { parameter_list *parameters = (parameter_list *)malloc(sizeof(parameter_list)); if ( parameters != NULL ) { parameters->head = NULL; parameters->tail = NULL; parameters->cursor = NULL; parameters->iterate_flag = 0; } return parameters; } int artio_parameter_read(artio_fh *handle, parameter_list *parameters) { parameter * item; int i; int length, re; int t_len; int32_t endian_tag; /* endian check */ re = artio_file_fread(handle, &endian_tag, 1, ARTIO_TYPE_INT); if ( re != ARTIO_SUCCESS ) { return ARTIO_ERR_PARAM_CORRUPTED; } if ( endian_tag != ARTIO_ENDIAN_MAGIC ) { artio_int_swap( &endian_tag, 1 ); if ( endian_tag == ARTIO_ENDIAN_MAGIC ) { artio_file_set_endian_swap_tag(handle); } else { return ARTIO_ERR_PARAM_CORRUPTED_MAGIC; } } re = artio_file_fread(handle, &length, 1, ARTIO_TYPE_INT); if ( re != ARTIO_SUCCESS ) { return ARTIO_ERR_PARAM_CORRUPTED; } for ( i = 0; i < length; i++ ) { item = (parameter *)malloc(sizeof(parameter)); if ( item == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } artio_file_fread(handle, &item->key_length, 1, ARTIO_TYPE_INT); artio_file_fread(handle, item->key, item->key_length, ARTIO_TYPE_CHAR); item->key[item->key_length] = 0; artio_file_fread(handle, &item->val_length, 1, ARTIO_TYPE_INT); artio_file_fread(handle, &item->type, 1, ARTIO_TYPE_INT); t_len = artio_type_size(item->type); item->value = (char *)malloc(item->val_length * t_len); re = artio_file_fread(handle, item->value, item->val_length, item->type); if ( re != ARTIO_SUCCESS ) { return ARTIO_ERR_PARAM_CORRUPTED; } item->next = NULL; if (NULL == parameters->tail) { parameters->tail = item; parameters->head = item; } else { parameters->tail->next = item; parameters->tail = item; } } return ARTIO_SUCCESS; } int artio_parameter_write(artio_fh *handle, parameter_list *parameters) { parameter * item; /* retain a number for endian check */ int32_t endian_tag = ARTIO_ENDIAN_MAGIC; int32_t length = 0; item = parameters->head; while (NULL != item) { length++; item = item->next; } artio_file_fwrite(handle, &endian_tag, 1, ARTIO_TYPE_INT); artio_file_fwrite(handle, &length, 1, ARTIO_TYPE_INT); item = parameters->head; while (NULL != item) { artio_file_fwrite(handle, &item->key_length, 1, ARTIO_TYPE_INT); artio_file_fwrite(handle, item->key, item->key_length, ARTIO_TYPE_CHAR); artio_file_fwrite(handle, &item->val_length, 1, ARTIO_TYPE_INT); artio_file_fwrite(handle, &item->type, 1, ARTIO_TYPE_INT); artio_file_fwrite(handle, item->value, item->val_length, item->type); item = item->next; } return ARTIO_SUCCESS; } int artio_parameter_iterate( artio_fileset *handle, char *key, int *type, int *length ) { parameter *item; parameter_list *parameters = handle->parameters; if ( parameters->iterate_flag == 0 ) { parameters->cursor = parameters->head; parameters->iterate_flag = 1; } if ( parameters->cursor == NULL ) { parameters->iterate_flag = 0; return ARTIO_PARAMETER_EXHAUSTED; } item = parameters->cursor; strncpy( key, item->key, 64 ); *type = item->type; *length = artio_parameter_array_length(item); parameters->cursor = item->next; return ARTIO_SUCCESS; } parameter *artio_parameter_list_search(parameter_list * parameters, const char *key ) { parameter * item = parameters->head; while ( NULL != item && strcmp(item->key, key) ) { item = item->next; } return item; } int artio_parameter_list_insert(parameter_list * parameters, const char * key, int length, void *value, int type) { int key_len; size_t val_len = 0; parameter * item; if ( length <= 0 ) { return ARTIO_ERR_PARAM_LENGTH_INVALID; } item = artio_parameter_list_search(parameters, key); if (NULL != item) { return ARTIO_ERR_PARAM_DUPLICATE; } /* create the list node */ item = (parameter *)malloc(sizeof(parameter)); if ( item == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } key_len = strlen(key); item->key_length = key_len; strcpy(item->key, key); item->val_length = length; item->type = type; val_len = artio_type_size(type); item->value = (char *)malloc(length * val_len); if ( item->value == NULL ) { free(item); return ARTIO_ERR_MEMORY_ALLOCATION; } memcpy(item->value, value, length * val_len); item->next = NULL; /* add to the list */ if (NULL == parameters->tail) { parameters->tail = item; parameters->head = item; } else { parameters->tail->next = item; parameters->tail = item; } return ARTIO_SUCCESS; } int artio_parameter_list_unpack(parameter_list *parameters, const char *key, int length, void *value, int type) { size_t t_len; parameter *item = artio_parameter_list_search(parameters, key); if (item != NULL) { if (length != item->val_length ) { return ARTIO_ERR_PARAM_LENGTH_MISMATCH; } else if ( type != item->type ) { return ARTIO_ERR_PARAM_TYPE_MISMATCH; } else { t_len = artio_type_size(type); memcpy(value, item->value, item->val_length * t_len); } } else { return ARTIO_ERR_PARAM_NOT_FOUND; } return ARTIO_SUCCESS; } int artio_parameter_list_unpack_index(parameter_list *parameters, const char *key, int index, void *value, int type) { size_t t_len; parameter *item; if ( index < 0 ) { return ARTIO_ERR_INVALID_INDEX; } item = artio_parameter_list_search(parameters, key); if (item != NULL) { if (index >= item->val_length ) { return ARTIO_ERR_PARAM_LENGTH_MISMATCH; } else if ( type != item->type ) { return ARTIO_ERR_PARAM_TYPE_MISMATCH; } else { t_len = artio_type_size(type); memcpy(value, item->value+index*t_len, t_len); } } else { return ARTIO_ERR_PARAM_NOT_FOUND; } return ARTIO_SUCCESS; } int artio_parameter_array_length( parameter *item ) { int i, length; if ( item->type == ARTIO_TYPE_STRING ) { length = 0; for ( i = 0; i < item->val_length; i++ ) { if ( item->value[i] == '\0' ) { length++; } } } else { length = item->val_length; } return length; } int artio_parameter_get_array_length(artio_fileset *handle, const char * key, int *length) { parameter *item = artio_parameter_list_search(handle->parameters, key); if (item != NULL) { *length = artio_parameter_array_length(item); } else { return ARTIO_ERR_PARAM_NOT_FOUND; } return ARTIO_SUCCESS; } int artio_parameter_list_free(parameter_list * parameters) { parameter * tmp; parameter * item; if ( parameters != NULL ) { item = parameters->head; while (NULL != item) { tmp = item; item = item->next; free(tmp->value); free(tmp); } parameters->head = NULL; parameters->tail = NULL; free( parameters ); } return ARTIO_SUCCESS; } int artio_parameter_list_print(parameter_list * parameters) { int32_t a; float b; double c; int64_t d; parameter * item = parameters->head; while (NULL != item) { switch ( item->type ) { case ARTIO_TYPE_STRING: printf("string: key %s %s\n", item->key, item->value); break; case ARTIO_TYPE_CHAR: printf("char: key %s %c\n", item->key, *item->value); break; case ARTIO_TYPE_INT: memcpy(&a, item->value, sizeof(int32_t)); printf("int: key %s %d\n", item->key, a); break; case ARTIO_TYPE_FLOAT: memcpy(&b, item->value, sizeof(float)); printf("float: key %s %f\n", item->key, b); break; case ARTIO_TYPE_DOUBLE: memcpy(&c, item->value, sizeof(double)); printf("double: key %s %f\n", item->key, c); break; case ARTIO_TYPE_LONG: memcpy(&d, item->value, sizeof(int64_t)); printf("long: %ld\n", d); break; default: printf("unrecognized type %d\n", item->type); } item = item->next; } return ARTIO_SUCCESS; } int artio_parameter_set_int(artio_fileset *handle, const char * key, int32_t value) { int32_t tmp = value; return artio_parameter_set_int_array(handle, key, 1, &tmp); } int artio_parameter_get_int(artio_fileset *handle, const char * key, int32_t * value) { return artio_parameter_get_int_array(handle, key, 1, value); } int artio_parameter_set_int_array(artio_fileset *handle, const char * key, int length, int32_t * value) { return artio_parameter_list_insert(handle->parameters, key, length, value, ARTIO_TYPE_INT); } int artio_parameter_get_int_array(artio_fileset *handle, const char * key, int length, int32_t * value) { return artio_parameter_list_unpack(handle->parameters, key, length, value, ARTIO_TYPE_INT); } int artio_parameter_get_int_array_index( artio_fileset *handle, const char *key, int index, int32_t * value ) { return artio_parameter_list_unpack_index(handle->parameters, key, index, value, ARTIO_TYPE_INT ); } int artio_parameter_set_float(artio_fileset *handle, const char * key, float value) { float tmp = value; return artio_parameter_set_float_array(handle, key, 1, &tmp); } int artio_parameter_get_float(artio_fileset *handle, const char * key, float * value) { return artio_parameter_get_float_array(handle, key, 1, value); } int artio_parameter_set_float_array(artio_fileset *handle, const char * key, int length, float * value) { return artio_parameter_list_insert(handle->parameters, key, length, value, ARTIO_TYPE_FLOAT); } int artio_parameter_get_float_array(artio_fileset *handle, const char * key, int length, float * value) { return artio_parameter_list_unpack(handle->parameters, key, length, value, ARTIO_TYPE_FLOAT); } int artio_parameter_get_float_array_index(artio_fileset *handle, const char * key, int index, float * value) { return artio_parameter_list_unpack_index(handle->parameters, key, index, value, ARTIO_TYPE_FLOAT); } int artio_parameter_set_double(artio_fileset *handle, const char * key, double value) { double tmp = value; return artio_parameter_set_double_array(handle, key, 1, &tmp); } int artio_parameter_get_double(artio_fileset *handle, const char * key, double * value) { return artio_parameter_get_double_array(handle, key, 1, value); } int artio_parameter_set_double_array(artio_fileset *handle, const char * key, int length, double * value) { return artio_parameter_list_insert(handle->parameters, key, length, value, ARTIO_TYPE_DOUBLE); } int artio_parameter_get_double_array(artio_fileset *handle, const char * key, int length, double * value) { return artio_parameter_list_unpack(handle->parameters, key, length, value, ARTIO_TYPE_DOUBLE); } int artio_parameter_get_double_array_index(artio_fileset *handle, const char * key, int index, double * value) { return artio_parameter_list_unpack_index(handle->parameters, key, index, value, ARTIO_TYPE_DOUBLE); } int artio_parameter_set_long(artio_fileset *handle, const char * key, int64_t value) { int64_t tmp = value; return artio_parameter_set_long_array(handle, key, 1, &tmp); } int artio_parameter_get_long(artio_fileset *handle, const char * key, int64_t * value) { return artio_parameter_get_long_array(handle, key, 1, value); } int artio_parameter_set_long_array(artio_fileset *handle, const char * key, int length, int64_t * value) { return artio_parameter_list_insert(handle->parameters, key, length, value, ARTIO_TYPE_LONG); } int artio_parameter_get_long_array(artio_fileset *handle, const char * key, int length, int64_t * value) { return artio_parameter_list_unpack(handle->parameters, key, length, value, ARTIO_TYPE_LONG); } int artio_parameter_get_long_array_index(artio_fileset *handle, const char * key, int index, int64_t * value) { return artio_parameter_list_unpack_index(handle->parameters, key, index, value, ARTIO_TYPE_LONG); } int artio_parameter_set_string(artio_fileset *handle, const char *key, char *value) { return artio_parameter_set_string_array(handle, key, 1, &value); } int artio_parameter_set_string_array(artio_fileset *handle, const char *key, int length, char **value) { int i; int len, loc_length; char *loc_value; char *p; int ret; for (i = 0, loc_length = 0; i < length; i++) { len = strlen(value[i]) + 1; if ( len > ARTIO_MAX_STRING_LENGTH ) { return ARTIO_ERR_STRING_LENGTH; } loc_length += len; } loc_value = (char *)malloc(loc_length * sizeof(char)); if ( loc_value == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } for (i = 0, p = loc_value; i < length; i++) { strcpy(p, value[i]); p += strlen(value[i]) + 1; } ret = artio_parameter_list_insert(handle->parameters, key, loc_length, loc_value, ARTIO_TYPE_STRING); free(loc_value); return ret; } int artio_parameter_get_string(artio_fileset *handle, const char *key, char *value ) { return artio_parameter_get_string_array(handle, key, 1, &value ); } int artio_parameter_get_string_array_index( artio_fileset *handle, const char *key, int index, char *value ) { int count; char *p; parameter *item = artio_parameter_list_search(handle->parameters, key); if ( item != NULL ) { count = 0; p = item->value; while ( count < index && p < item->value + item->val_length) { p += strlen(p) + 1; count++; } if ( count != index ) { return ARTIO_ERR_INVALID_INDEX; } strncpy(value, p, ARTIO_MAX_STRING_LENGTH-1); value[ARTIO_MAX_STRING_LENGTH-1] = 0; } else { return ARTIO_ERR_PARAM_NOT_FOUND; } return ARTIO_SUCCESS; } int artio_parameter_get_string_array(artio_fileset *handle, const char *key, int length, char **value ) { int i; char *p; int count; parameter *item = artio_parameter_list_search(handle->parameters, key); if (item != NULL) { /* count string items in item->value */ count = 0; p = item->value; while (p < item->value + item->val_length) { p += strlen(p) + 1; count++; } if (count != length) { return ARTIO_ERR_PARAM_LENGTH_MISMATCH; } for (i = 0, p = item->value; i < length; i++) { strncpy(value[i], p, ARTIO_MAX_STRING_LENGTH-1); value[i][ARTIO_MAX_STRING_LENGTH-1] = 0; p += strlen(p) + 1; } } else { return ARTIO_ERR_PARAM_NOT_FOUND; } return ARTIO_SUCCESS; } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_particle.c000066400000000000000000001011631510711153200264060ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif int artio_particle_find_file(artio_particle_file *phandle, int start, int end, int64_t sfc); artio_particle_file *artio_particle_file_allocate(void); void artio_particle_file_destroy( artio_particle_file *phandle ); /* * Open existing particle files and add to fileset */ int artio_fileset_open_particles(artio_fileset *handle) { int i; char filename[256]; int first_file, last_file; int mode; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( handle->open_type & ARTIO_OPEN_PARTICLES || handle->open_mode != ARTIO_FILESET_READ || handle->particle != NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } handle->open_type |= ARTIO_OPEN_PARTICLES; phandle = artio_particle_file_allocate(); if ( phandle == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } if ( artio_parameter_get_int( handle, "num_particle_files", &phandle->num_particle_files ) != ARTIO_SUCCESS || artio_parameter_get_int(handle, "num_particle_species", &phandle->num_species) != ARTIO_SUCCESS ) { return ARTIO_ERR_PARTICLE_DATA_NOT_FOUND; } phandle->num_primary_variables = (int *)malloc(sizeof(int) * phandle->num_species); if ( phandle->num_primary_variables == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } phandle->num_secondary_variables = (int *)malloc(sizeof(int) * phandle->num_species); if ( phandle->num_primary_variables == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } phandle->num_particles_per_species = (int *)malloc(phandle->num_species * sizeof(int)); if ( phandle->num_particles_per_species == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } artio_parameter_get_int_array(handle, "num_primary_variables", phandle->num_species, phandle->num_primary_variables); artio_parameter_get_int_array(handle, "num_secondary_variables", phandle->num_species, phandle->num_secondary_variables); phandle->file_sfc_index = (int64_t *)malloc(sizeof(int64_t) * (phandle->num_particle_files + 1)); if ( phandle->file_sfc_index == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } artio_parameter_get_long_array(handle, "particle_file_sfc_index", phandle->num_particle_files + 1, phandle->file_sfc_index); first_file = artio_particle_find_file(phandle, 0, phandle->num_particle_files, handle->proc_sfc_begin); last_file = artio_particle_find_file(phandle, first_file, phandle->num_particle_files, handle->proc_sfc_end); /* allocate file handles */ phandle->ffh = (artio_fh **)malloc(phandle->num_particle_files * sizeof(artio_fh *)); if ( phandle->ffh == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } for ( i = 0; i < phandle->num_particle_files; i++ ) { phandle->ffh[i] = NULL; } /* open files on all processes */ for (i = 0; i < phandle->num_particle_files; i++) { sprintf(filename, "%s.p%03d", handle->file_prefix, i); mode = ARTIO_MODE_READ; if (i >= first_file && i <= last_file) { mode |= ARTIO_MODE_ACCESS; } if (handle->endian_swap) { mode |= ARTIO_MODE_ENDIAN_SWAP; } phandle->ffh[i] = artio_file_fopen(filename, mode, handle->context); if ( phandle->ffh[i] == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_PARTICLE_FILE_NOT_FOUND; } } handle->particle = phandle; return ARTIO_SUCCESS; } int artio_fileset_add_particles( artio_fileset *handle, int num_particle_files, int allocation_strategy, int num_species, char ** species_labels, int * num_primary_variables, int * num_secondary_variables, char *** primary_variable_labels_per_species, char *** secondary_variable_labels_per_species, int * num_particles_per_species_per_root_tree ) { int i, k; int ret; int64_t l, cur; int64_t first_file_sfc, last_file_sfc; int first_file, last_file; char filename[256]; char species_label[64]; int mode; int64_t *local_particles_per_species, *total_particles_per_species; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( handle->open_mode != ARTIO_FILESET_WRITE ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( handle->open_type & ARTIO_OPEN_PARTICLES || handle->particle != NULL ) { return ARTIO_ERR_DATA_EXISTS; } handle->open_type |= ARTIO_OPEN_PARTICLES; /* compute total number of particles per species */ local_particles_per_species = (int64_t *)malloc( num_species * sizeof(int64_t)); if ( local_particles_per_species == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } total_particles_per_species = (int64_t *)malloc( num_species * sizeof(int64_t)); if ( total_particles_per_species == NULL ) { free( local_particles_per_species ); return ARTIO_ERR_MEMORY_ALLOCATION; } for ( i = 0; i < num_species; i++ ) { local_particles_per_species[i] = 0; } for ( l = 0; l < handle->proc_sfc_end-handle->proc_sfc_begin+1; l++ ) { for ( i = 0; i < num_species; i++ ) { local_particles_per_species[i] += num_particles_per_species_per_root_tree[num_species*l+i]; } } #ifdef ARTIO_MPI MPI_Allreduce( local_particles_per_species, total_particles_per_species, num_species, MPI_LONG_LONG_INT, MPI_SUM, handle->context->comm ); #else for ( i = 0; i < num_species; i++ ) { total_particles_per_species[i] = local_particles_per_species[i]; } #endif artio_parameter_set_long_array(handle, "num_particles_per_species", num_species, total_particles_per_species ); free( local_particles_per_species ); free( total_particles_per_species ); artio_parameter_set_int(handle, "num_particle_files", num_particle_files); artio_parameter_set_int(handle, "num_particle_species", num_species); artio_parameter_set_string_array(handle, "particle_species_labels", num_species, species_labels); artio_parameter_set_int_array(handle, "num_primary_variables", num_species, num_primary_variables); artio_parameter_set_int_array(handle, "num_secondary_variables", num_species, num_secondary_variables); for(i=0;ifile_sfc_index = (int64_t *)malloc(sizeof(int64_t) * (num_particle_files + 1)); if ( phandle->file_sfc_index == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } switch (allocation_strategy) { case ARTIO_ALLOC_EQUAL_PROC: if (num_particle_files > handle->num_procs) { return ARTIO_ERR_INVALID_FILE_NUMBER; } for (i = 0; i < num_particle_files; i++) { phandle->file_sfc_index[i] = handle->proc_sfc_index[ (handle->num_procs*i+num_particle_files-1) / num_particle_files ]; } phandle->file_sfc_index[num_particle_files] = handle->proc_sfc_index[handle->num_procs]; break; case ARTIO_ALLOC_EQUAL_SFC: for (i = 0; i < num_particle_files; i++) { phandle->file_sfc_index[i] = (handle->num_root_cells*i+num_particle_files-1) / num_particle_files; } phandle->file_sfc_index[num_particle_files] = handle->num_root_cells; break; default: artio_particle_file_destroy(phandle); return ARTIO_ERR_INVALID_ALLOC_STRATEGY; } phandle->num_particle_files = num_particle_files; phandle->num_species = num_species; phandle->num_primary_variables = (int *)malloc(sizeof(int) * num_species); if ( phandle->num_primary_variables == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } phandle->num_secondary_variables = (int *)malloc(sizeof(int) * num_species); if ( phandle->num_secondary_variables == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } phandle->num_particles_per_species = (int *)malloc(phandle->num_species * sizeof(int)); if ( phandle->num_particles_per_species == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } for (i = 0; i < num_species; i++) { phandle->num_primary_variables[i] = num_primary_variables[i]; phandle->num_secondary_variables[i] = num_secondary_variables[i]; } /* allocate space for sfc offset cache */ phandle->cache_sfc_begin = handle->proc_sfc_begin; phandle->cache_sfc_end = handle->proc_sfc_end; phandle->sfc_offset_table = (int64_t *)malloc( (size_t)(handle->proc_sfc_end - handle->proc_sfc_begin + 1) * sizeof(int64_t)); if ( phandle->sfc_offset_table == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } /* allocate file handles */ phandle->ffh = (artio_fh **)malloc(num_particle_files * sizeof(artio_fh *)); if ( phandle->ffh == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_MEMORY_ALLOCATION; } for ( i = 0; i < num_particle_files; i++ ) { phandle->ffh[i] = NULL; } /* open file handles */ first_file = artio_particle_find_file(phandle, 0, num_particle_files, handle->proc_sfc_begin); last_file = artio_particle_find_file(phandle, first_file, num_particle_files, handle->proc_sfc_end); for (i = 0; i < num_particle_files; i++) { sprintf(filename, "%s.p%03d", handle->file_prefix, i); mode = ARTIO_MODE_WRITE; if (i >= first_file && i <= last_file) { mode |= ARTIO_MODE_ACCESS; } phandle->ffh[i] = artio_file_fopen(filename, mode, handle->context); if ( phandle->ffh[i] == NULL ) { artio_particle_file_destroy(phandle); return ARTIO_ERR_FILE_CREATE; } /* write sfc offset header if we contribute to this file */ if (i >= first_file && i <= last_file) { #ifdef ARTIO_MPI if ( phandle->file_sfc_index[i] >= handle->proc_sfc_index[ handle->rank ] && phandle->file_sfc_index[i] < handle->proc_sfc_index[ handle->rank + 1 ] ) { cur = (phandle->file_sfc_index[i+1] - phandle->file_sfc_index[i]) * sizeof(int64_t); } else { /* obtain offset from previous process */ MPI_Recv( &cur, 1, MPI_LONG_LONG_INT, handle->rank - 1, i, handle->context->comm, MPI_STATUS_IGNORE ); } #else cur = (phandle->file_sfc_index[i+1] - phandle->file_sfc_index[i]) * sizeof(int64_t); #endif /* ARTIO_MPI */ first_file_sfc = MAX( phandle->cache_sfc_begin, phandle->file_sfc_index[i] ); last_file_sfc = MIN( phandle->cache_sfc_end, phandle->file_sfc_index[i+1]-1 ); for (l = first_file_sfc - phandle->cache_sfc_begin; l < last_file_sfc - phandle->cache_sfc_begin + 1; l++) { phandle->sfc_offset_table[l] = cur; cur += sizeof(int) * num_species; for (k = 0; k < num_species; k++) { cur += num_particles_per_species_per_root_tree[l*num_species+k] * (sizeof(int64_t) + sizeof(int) + num_primary_variables[k] * sizeof(double) + num_secondary_variables[k] * sizeof(float)); } } #ifdef ARTIO_MPI if ( phandle->file_sfc_index[i+1] > handle->proc_sfc_end+1 ) { MPI_Send( &cur, 1, MPI_LONG_LONG_INT, handle->rank+1, i, handle->context->comm ); } #endif /* ARTIO_MPI */ /* seek and write our portion of sfc table */ ret = artio_file_fseek(phandle->ffh[i], (first_file_sfc - phandle->file_sfc_index[i]) * sizeof(int64_t), ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) { artio_particle_file_destroy(phandle); return ret; } ret = artio_file_fwrite(phandle->ffh[i], &phandle->sfc_offset_table[first_file_sfc - phandle->cache_sfc_begin], last_file_sfc - first_file_sfc + 1, ARTIO_TYPE_LONG); if ( ret != ARTIO_SUCCESS ) { artio_particle_file_destroy(phandle); return ret; } } } artio_parameter_set_long_array(handle, "particle_file_sfc_index", phandle->num_particle_files + 1, phandle->file_sfc_index); handle->particle = phandle; return ARTIO_SUCCESS; } artio_particle_file *artio_particle_file_allocate(void) { artio_particle_file *phandle = (artio_particle_file *)malloc(sizeof(artio_particle_file)); if ( phandle != NULL ) { phandle->ffh = NULL; phandle->num_particle_files = -1; phandle->file_sfc_index = NULL; phandle->cache_sfc_begin = -1; phandle->cache_sfc_end = -1; phandle->sfc_offset_table = NULL; phandle->num_species = -1; phandle->cur_particle = -1; phandle->cur_sfc = -1; phandle->num_primary_variables = NULL; phandle->num_secondary_variables = NULL; phandle->num_particles_per_species = NULL; phandle->cur_file = -1; phandle->buffer_size = artio_fh_buffer_size; phandle->buffer = malloc(phandle->buffer_size); if ( phandle->buffer == NULL ) { free(phandle); return NULL; } } return phandle; } void artio_particle_file_destroy( artio_particle_file *phandle ) { int i; if ( phandle == NULL ) return; if ( phandle->ffh != NULL ) { for (i = 0; i < phandle->num_particle_files; i++) { if ( phandle->ffh[i] != NULL ) { artio_file_fclose(phandle->ffh[i]); } } free(phandle->ffh); } if (phandle->sfc_offset_table != NULL) free(phandle->sfc_offset_table); if (phandle->num_particles_per_species != NULL) free(phandle->num_particles_per_species); if (phandle->num_primary_variables != NULL) free(phandle->num_primary_variables); if (phandle->num_secondary_variables != NULL) free(phandle->num_secondary_variables); if (phandle->file_sfc_index != NULL) free(phandle->file_sfc_index); if (phandle->buffer != NULL) free(phandle->buffer); free(phandle); } int artio_fileset_close_particles(artio_fileset *handle) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } artio_particle_file_destroy(handle->particle); handle->particle = NULL; return ARTIO_SUCCESS; } int artio_particle_cache_sfc_range(artio_fileset *handle, int64_t start, int64_t end) { int i; int ret; int first_file, last_file; int64_t min, count, cur; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if ( start > end || start < handle->proc_sfc_begin || end > handle->proc_sfc_end) { return ARTIO_ERR_INVALID_SFC_RANGE; } /* check if we've already cached the range */ if ( start >= phandle->cache_sfc_begin && end <= phandle->cache_sfc_end ) { return ARTIO_SUCCESS; } artio_grid_clear_sfc_cache(handle); first_file = artio_particle_find_file(phandle, 0, phandle->num_particle_files, start); last_file = artio_particle_find_file(phandle, first_file, phandle->num_particle_files, end); phandle->cache_sfc_begin = start; phandle->cache_sfc_end = end; phandle->sfc_offset_table = (int64_t *)malloc(sizeof(int64_t) * (size_t)(end - start + 1)); if ( phandle->sfc_offset_table == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } if ( phandle->cur_file != -1 ) { artio_file_detach_buffer( phandle->ffh[phandle->cur_file]); phandle->cur_file = -1; } cur = 0; for (i = first_file; i <= last_file; i++) { min = MAX( 0, start - phandle->file_sfc_index[i] ); count = MIN( phandle->file_sfc_index[i+1], end+1 ) - MAX( start, phandle->file_sfc_index[i] ); artio_file_attach_buffer( phandle->ffh[i], phandle->buffer, phandle->buffer_size ); ret = artio_file_fseek(phandle->ffh[i], sizeof(int64_t) * min, ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(phandle->ffh[i], &phandle->sfc_offset_table[cur], count, ARTIO_TYPE_LONG); if ( ret != ARTIO_SUCCESS ) return ret; artio_file_detach_buffer( phandle->ffh[i] ); cur += count; } return ARTIO_SUCCESS; } int artio_particle_clear_sfc_cache( artio_fileset *handle ) { artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_GRID) || handle->grid == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if ( phandle->sfc_offset_table != NULL ) { free(phandle->sfc_offset_table); phandle->sfc_offset_table = NULL; } phandle->cache_sfc_begin = -1; phandle->cache_sfc_end = -1; return ARTIO_SUCCESS; } int artio_particle_find_file(artio_particle_file *phandle, int start, int end, int64_t sfc) { int j; if ( start < 0 || start > phandle->num_particle_files || end < 0 || end > phandle->num_particle_files || sfc < phandle->file_sfc_index[start] || sfc >= phandle->file_sfc_index[end] ) { return -1; } if (start == end || sfc == phandle->file_sfc_index[start] ) { return start; } if (1 == end - start) { if (sfc < phandle->file_sfc_index[end]) { return start; } else { return end; } } j = start + (end - start) / 2; if (sfc > phandle->file_sfc_index[j]) { return artio_particle_find_file(phandle, j, end, sfc); } else if (sfc < phandle->file_sfc_index[j]) { return artio_particle_find_file(phandle, start, j, sfc); } else { return j; } } int artio_particle_seek_to_sfc(artio_fileset *handle, int64_t sfc) { int64_t offset; artio_particle_file *phandle; int file; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if ( !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cache_sfc_begin == -1 || sfc < phandle->cache_sfc_begin || sfc > phandle->cache_sfc_end) { return ARTIO_ERR_INVALID_SFC; } file = artio_particle_find_file(phandle, 0, phandle->num_particle_files, sfc); if ( file != phandle->cur_file ) { if ( phandle->cur_file != -1 ) { artio_file_detach_buffer( phandle->ffh[phandle->cur_file] ); } if ( phandle->buffer_size > 0 ) { artio_file_attach_buffer( phandle->ffh[file], phandle->buffer, phandle->buffer_size ); } phandle->cur_file = file; } offset = phandle->sfc_offset_table[sfc - phandle->cache_sfc_begin]; return artio_file_fseek(phandle->ffh[phandle->cur_file], offset, ARTIO_SEEK_SET); } int artio_particle_write_root_cell_begin(artio_fileset *handle, int64_t sfc, int * num_particles_per_species) { int i; int ret; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if ( phandle->cur_sfc != -1 ) { return ARTIO_ERR_INVALID_STATE; } ret = artio_particle_seek_to_sfc(handle, sfc); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(phandle->ffh[phandle->cur_file], num_particles_per_species, phandle->num_species, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; for (i = 0; i < phandle->num_species; i++) { phandle->num_particles_per_species[i] = num_particles_per_species[i]; } phandle->cur_sfc = sfc; phandle->cur_species = -1; phandle->cur_particle = -1; return ARTIO_SUCCESS; } int artio_particle_write_root_cell_end(artio_fileset *handle) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( handle->particle->cur_sfc == -1 || handle->particle->cur_species != -1 ) { return ARTIO_ERR_INVALID_STATE; } handle->particle->cur_sfc = -1; return ARTIO_SUCCESS; } int artio_particle_write_species_begin(artio_fileset *handle, int species) { artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cur_sfc == -1 || phandle->cur_species != -1 ) { return ARTIO_ERR_INVALID_STATE; } if ( species < 0 || species >= phandle->num_species) { return ARTIO_ERR_INVALID_SPECIES; } phandle->cur_species = species; phandle->cur_particle = 0; return ARTIO_SUCCESS; } int artio_particle_write_species_end(artio_fileset *handle) { artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cur_species == -1 || phandle->cur_particle != phandle->num_particles_per_species[phandle->cur_species]) { return ARTIO_ERR_INVALID_STATE; } phandle->cur_species = -1; phandle->cur_particle = -1; return ARTIO_SUCCESS; } int artio_particle_write_particle(artio_fileset *handle, int64_t pid, int subspecies, double * primary_variables, float *secondary_variables) { int ret; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_WRITE || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cur_species == -1 || phandle->cur_particle >= phandle->num_particles_per_species[phandle->cur_species]) { return ARTIO_ERR_INVALID_STATE; } ret = artio_file_fwrite(phandle->ffh[phandle->cur_file], &pid, 1, ARTIO_TYPE_LONG); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(phandle->ffh[phandle->cur_file], &subspecies, 1, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(phandle->ffh[phandle->cur_file], primary_variables, phandle->num_primary_variables[phandle->cur_species], ARTIO_TYPE_DOUBLE); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fwrite(phandle->ffh[phandle->cur_file], secondary_variables, phandle->num_secondary_variables[phandle->cur_species], ARTIO_TYPE_FLOAT); if ( ret != ARTIO_SUCCESS ) return ret; phandle->cur_particle++; return ARTIO_SUCCESS; } /* * */ int artio_particle_read_root_cell_begin(artio_fileset *handle, int64_t sfc, int * num_particles_per_species) { int i; int ret; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; ret = artio_particle_seek_to_sfc(handle, sfc); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(phandle->ffh[phandle->cur_file], num_particles_per_species, phandle->num_species, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; for (i = 0; i < phandle->num_species; i++) { phandle->num_particles_per_species[i] = num_particles_per_species[i]; } phandle->cur_sfc = sfc; phandle->cur_species = -1; phandle->cur_particle = 0; return ARTIO_SUCCESS; } /* Description */ int artio_particle_read_particle(artio_fileset *handle, int64_t * pid, int *subspecies, double * primary_variables, float * secondary_variables) { int ret; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cur_species == -1 || phandle->cur_particle >= phandle->num_particles_per_species[phandle->cur_species]) { return ARTIO_ERR_INVALID_STATE; } ret = artio_file_fread(phandle->ffh[phandle->cur_file], pid, 1, ARTIO_TYPE_LONG); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(phandle->ffh[phandle->cur_file], subspecies, 1, ARTIO_TYPE_INT); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(phandle->ffh[phandle->cur_file], primary_variables, phandle->num_primary_variables[phandle->cur_species], ARTIO_TYPE_DOUBLE); if ( ret != ARTIO_SUCCESS ) return ret; ret = artio_file_fread(phandle->ffh[phandle->cur_file], secondary_variables, phandle->num_secondary_variables[phandle->cur_species], ARTIO_TYPE_FLOAT); if ( ret != ARTIO_SUCCESS ) return ret; phandle->cur_particle++; return ARTIO_SUCCESS; } /* * Description Start reading particle species */ int artio_particle_read_species_begin(artio_fileset *handle, int species) { int i; int ret; int64_t offset = 0; artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cur_sfc == -1) { return ARTIO_ERR_INVALID_STATE; } if (species < 0 || species >= phandle->num_species) { return ARTIO_ERR_INVALID_SPECIES; } offset = phandle->sfc_offset_table[phandle->cur_sfc - phandle->cache_sfc_begin]; offset += sizeof(int32_t) * (phandle->num_species); for (i = 0; i < species; i++) { offset += ( sizeof(int64_t) + sizeof(int) + phandle->num_primary_variables[i] * sizeof(double) + phandle->num_secondary_variables[i] * sizeof(float) ) * phandle->num_particles_per_species[i]; } ret = artio_file_fseek(phandle->ffh[phandle->cur_file], offset, ARTIO_SEEK_SET); if ( ret != ARTIO_SUCCESS ) return ret; phandle->cur_species = species; phandle->cur_particle = 0; return ARTIO_SUCCESS; } /* * Description Do something at the end of each kind of read operation */ int artio_particle_read_species_end(artio_fileset *handle) { artio_particle_file *phandle; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if (phandle->cur_species == -1) { return ARTIO_ERR_INVALID_STATE; } phandle->cur_species = -1; phandle->cur_particle = 0; return ARTIO_SUCCESS; } int artio_particle_read_root_cell_end(artio_fileset *handle) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } if ( handle->particle->cur_sfc == -1 ) { return ARTIO_ERR_INVALID_STATE; } handle->particle->cur_sfc = -1; return ARTIO_SUCCESS; } int artio_particle_read_selection(artio_fileset *handle, artio_selection *selection, artio_particle_callback callback, void *params ) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } return artio_particle_read_selection_species( handle, selection, 0, handle->particle->num_species-1, callback, params ); } int artio_particle_read_selection_species( artio_fileset *handle, artio_selection *selection, int start_species, int end_species, artio_particle_callback callback, void *params ) { int ret; int64_t start, end; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } artio_selection_iterator_reset( selection ); while ( artio_selection_iterator( selection, handle->num_root_cells, &start, &end ) == ARTIO_SUCCESS ) { ret = artio_particle_read_sfc_range_species( handle, start, end, start_species, end_species, callback, params ); if ( ret != ARTIO_SUCCESS ) return ret; } return ARTIO_SUCCESS; } int artio_particle_read_sfc_range(artio_fileset *handle, int64_t sfc1, int64_t sfc2, artio_particle_callback callback, void *params ) { if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) || handle->particle == NULL ) { return ARTIO_ERR_INVALID_FILESET_MODE; } return artio_particle_read_sfc_range_species( handle, sfc1, sfc2, 0, handle->particle->num_species-1, callback, params ); } int artio_particle_read_sfc_range_species(artio_fileset *handle, int64_t sfc1, int64_t sfc2, int start_species, int end_species, artio_particle_callback callback, void *params ) { int64_t sfc; int particle, species; int *num_particles_per_species; artio_particle_file *phandle; int64_t pid = 0l; int subspecies; double * primary_variables = NULL; float * secondary_variables = NULL; int num_primary, num_secondary; int ret; if ( handle == NULL ) { return ARTIO_ERR_INVALID_HANDLE; } if (handle->open_mode != ARTIO_FILESET_READ || !(handle->open_type & ARTIO_OPEN_PARTICLES) ) { return ARTIO_ERR_INVALID_FILESET_MODE; } phandle = handle->particle; if ( start_species < 0 || start_species > end_species || end_species > phandle->num_species-1 ) { return ARTIO_ERR_INVALID_SPECIES; } num_particles_per_species = (int *)malloc(phandle->num_species * sizeof(int)); if ( num_particles_per_species == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } ret = artio_particle_cache_sfc_range(handle, sfc1, sfc2); if ( ret != ARTIO_SUCCESS ) { free( num_particles_per_species ); return ret; } num_primary = num_secondary = 0; for ( species = start_species; species <= end_species; species++ ) { num_primary = MAX( phandle->num_primary_variables[species], num_primary ); num_secondary = MAX( phandle->num_secondary_variables[species], num_secondary ); } primary_variables = (double *)malloc(num_primary * sizeof(double)); if ( primary_variables == NULL ) { free( num_particles_per_species ); return ARTIO_ERR_MEMORY_ALLOCATION; } secondary_variables = (float *)malloc(num_secondary * sizeof(float)); if ( secondary_variables == NULL ) { free( num_particles_per_species ); free( primary_variables ); return ARTIO_ERR_MEMORY_ALLOCATION; } for ( sfc = sfc1; sfc <= sfc2; sfc++ ) { ret = artio_particle_read_root_cell_begin(handle, sfc, num_particles_per_species); if ( ret != ARTIO_SUCCESS ) { free( num_particles_per_species ); free( primary_variables ); free( secondary_variables ); return ret; } for ( species = start_species; species <= end_species; species++) { ret = artio_particle_read_species_begin(handle, species); if ( ret != ARTIO_SUCCESS ) { free( num_particles_per_species ); free( primary_variables ); free( secondary_variables ); return ret; } for (particle = 0; particle < num_particles_per_species[species]; particle++) { ret = artio_particle_read_particle(handle, &pid, &subspecies, primary_variables, secondary_variables); if ( ret != ARTIO_SUCCESS ) { free( num_particles_per_species ); free( primary_variables ); free( secondary_variables ); return ret; } callback(sfc, species, subspecies, pid, primary_variables, secondary_variables, params ); } artio_particle_read_species_end(handle); } artio_particle_read_root_cell_end(handle); } free(primary_variables); free(secondary_variables); free(num_particles_per_species); return ARTIO_SUCCESS; } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_posix.c000066400000000000000000000216671510711153200257570ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #ifndef ARTIO_MPI #include #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif struct ARTIO_FH { FILE *fh; int mode; char *data; int bfptr; int bfsize; int bfend; }; #ifdef _WIN32 #define FOPEN_FLAGS "b" #define fseek _fseeki64 #else #define FOPEN_FLAGS "" #endif artio_context artio_context_global_struct = { 0 }; const artio_context *artio_context_global = &artio_context_global_struct; artio_fh *artio_file_fopen_i( char * filename, int mode, const artio_context *not_used ) { artio_fh *ffh; /* check for invalid combination of mode parameter */ if ( ( mode & ARTIO_MODE_READ && mode & ARTIO_MODE_WRITE ) || !( mode & ARTIO_MODE_READ || mode & ARTIO_MODE_WRITE ) ) { return NULL; } ffh = (artio_fh *)malloc(sizeof(artio_fh)); if ( ffh == NULL ) { return NULL; } ffh->mode = mode; ffh->bfsize = -1; ffh->bfend = -1; ffh->bfptr = -1; ffh->data = NULL; if ( mode & ARTIO_MODE_ACCESS ) { ffh->fh = fopen( filename, ( mode & ARTIO_MODE_WRITE ) ? "w"FOPEN_FLAGS : "r"FOPEN_FLAGS ); if ( ffh->fh == NULL ) { free( ffh ); return NULL; } } return ffh; } int artio_file_attach_buffer_i( artio_fh *handle, void *buf, int buf_size ) { if ( !(handle->mode & ARTIO_MODE_ACCESS ) ) { return ARTIO_ERR_INVALID_FILE_MODE; } if ( handle->data != NULL ) { return ARTIO_ERR_BUFFER_EXISTS; } handle->bfsize = buf_size; handle->bfend = -1; handle->bfptr = 0; handle->data = (char *)buf; return ARTIO_SUCCESS; } int artio_file_detach_buffer_i( artio_fh *handle ) { int ret; ret = artio_file_fflush(handle); if ( ret != ARTIO_SUCCESS ) return ret; handle->data = NULL; handle->bfsize = -1; handle->bfend = -1; handle->bfptr = -1; return ARTIO_SUCCESS; } int artio_file_fwrite_i(artio_fh *handle, const void *buf, int64_t count, int type ) { size_t size; int64_t remain; char *p; int size32; if ( !(handle->mode & ARTIO_MODE_WRITE) || !(handle->mode & ARTIO_MODE_ACCESS) ) { return ARTIO_ERR_INVALID_FILE_MODE; } size = artio_type_size( type ); if ( size == (size_t)-1 ) { return ARTIO_ERR_INVALID_DATATYPE; } if ( count > ARTIO_INT64_MAX / size ) { return ARTIO_ERR_IO_OVERFLOW; } remain = count*size; p = (char *)buf; if ( handle->data == NULL ) { /* force writes to 32-bit sizes */ while ( remain > 0 ) { size32 = MIN( ARTIO_IO_MAX, remain ); if ( fwrite( p, 1, size32, handle->fh ) != size32 ) { return ARTIO_ERR_IO_WRITE; } remain -= size32; p += size32; } } else if ( remain < handle->bfsize - handle->bfptr ) { memcpy( handle->data + handle->bfptr, p, (size_t)remain ); handle->bfptr += remain; } else { size32 = handle->bfsize - handle->bfptr; memcpy( handle->data + handle->bfptr, p, size32 ); if ( fwrite( handle->data, 1, handle->bfsize, handle->fh ) != handle->bfsize ) { return ARTIO_ERR_IO_WRITE; } p += size32; remain -= size32; while ( remain > handle->bfsize ) { /* write directly to file-handle in unbuffered case */ if ( fwrite( p, 1, handle->bfsize, handle->fh ) != handle->bfsize ) { return ARTIO_ERR_IO_WRITE; } remain -= handle->bfsize; p += handle->bfsize; } memcpy( handle->data, p, (size_t)remain); handle->bfptr = remain; } return ARTIO_SUCCESS; } int artio_file_fflush_i(artio_fh *handle) { if ( !(handle->mode & ARTIO_MODE_ACCESS) ) { return ARTIO_ERR_INVALID_FILE_MODE; } if ( handle->mode & ARTIO_MODE_WRITE ) { if ( handle->bfptr > 0 ) { if ( fwrite( handle->data, 1, handle->bfptr, handle->fh ) != handle->bfptr ) { return ARTIO_ERR_IO_WRITE; } handle->bfptr = 0; } } else if ( handle->mode & ARTIO_MODE_READ ) { handle->bfend = -1; handle->bfptr = 0; } else { return ARTIO_ERR_INVALID_FILE_MODE; } return ARTIO_SUCCESS; } int artio_file_fread_i(artio_fh *handle, void *buf, int64_t count, int type ) { size_t size, avail, remain; int size32; char *p; if ( !(handle->mode & ARTIO_MODE_READ) ) { return ARTIO_ERR_INVALID_FILE_MODE; } size = artio_type_size( type ); if ( size == (size_t)-1 ) { return ARTIO_ERR_INVALID_DATATYPE; } if ( count > ARTIO_INT64_MAX / size ) { return ARTIO_ERR_IO_OVERFLOW; } remain = size*count; p = (char *)buf; if ( handle->data == NULL ) { while ( remain > 0 ) { size32 = MIN( ARTIO_IO_MAX, remain ); if ( fread( p, 1, size32, handle->fh) != size32 ) { return ARTIO_ERR_INSUFFICIENT_DATA; } remain -= size32; p += size32; } } else { if ( handle->bfend == -1 ) { /* load initial data into buffer */ handle->bfend = fread( handle->data, 1, handle->bfsize, handle->fh ); handle->bfptr = 0; } /* read from buffer */ while ( remain > 0 && handle->bfend > 0 && handle->bfptr + remain >= handle->bfend ) { avail = handle->bfend - handle->bfptr; memcpy( p, handle->data + handle->bfptr, avail ); p += avail; remain -= avail; /* refill buffer */ handle->bfend = fread( handle->data, 1, handle->bfsize, handle->fh ); handle->bfptr = 0; } if ( remain > 0 ) { if ( handle->bfend == 0 ) { /* ran out of data, eof */ return ARTIO_ERR_INSUFFICIENT_DATA; } memcpy( p, handle->data + handle->bfptr, (size_t)remain ); handle->bfptr += (int)remain; } } if(handle->mode & ARTIO_MODE_ENDIAN_SWAP) { switch (type) { case ARTIO_TYPE_INT : artio_int_swap( (int32_t *)buf, count ); break; case ARTIO_TYPE_FLOAT : artio_float_swap( (float *)buf, count ); break; case ARTIO_TYPE_DOUBLE : artio_double_swap( (double *)buf, count ); break; case ARTIO_TYPE_LONG : artio_long_swap( (int64_t *)buf, count ); break; default : return ARTIO_ERR_INVALID_DATATYPE; } } return ARTIO_SUCCESS; } int artio_file_ftell_i( artio_fh *handle, int64_t *offset ) { size_t current = ftell( handle->fh ); if ( handle->bfend > 0 ) { current -= handle->bfend; } if ( handle->bfptr > 0 ) { current += handle->bfptr; } *offset = (int64_t)current; return ARTIO_SUCCESS; } int artio_file_fseek_i(artio_fh *handle, int64_t offset, int whence ) { size_t current; if ( handle->mode & ARTIO_MODE_ACCESS ) { if ( whence == ARTIO_SEEK_CUR ) { if ( offset == 0 ) { return ARTIO_SUCCESS; } else if ( handle->mode & ARTIO_MODE_READ && handle->bfend > 0 && handle->bfptr + offset >= 0 && handle->bfptr + offset < handle->bfend ) { handle->bfptr += offset; return ARTIO_SUCCESS; } else { /* modify offset due to offset in buffer */ if ( handle->bfptr > 0 ) { current = offset - handle->bfend + handle->bfptr; } else { current = offset; } artio_file_fflush( handle ); fseek( handle->fh, (size_t)current, SEEK_CUR ); } } else if ( whence == ARTIO_SEEK_SET ) { current = ftell( handle->fh ); if ( handle->mode & ARTIO_MODE_WRITE && current <= offset && offset < current + handle->bfsize && handle->bfptr == offset - current ) { return ARTIO_SUCCESS; } else if ( handle->mode & ARTIO_MODE_READ && handle->bfptr > 0 && handle->bfend > 0 && handle->bfptr < handle->bfend && offset >= current - handle->bfend && offset < current ) { handle->bfptr = offset - current + handle->bfend; } else { artio_file_fflush( handle ); fseek( handle->fh, (size_t)offset, SEEK_SET ); } } else if ( whence == ARTIO_SEEK_END ) { artio_file_fflush( handle ); fseek( handle->fh, (size_t)offset, SEEK_END ); } else { /* unknown whence */ return ARTIO_ERR_INVALID_SEEK; } } else { return ARTIO_ERR_INVALID_FILE_MODE; } return ARTIO_SUCCESS; } int artio_file_fclose_i(artio_fh *handle) { if ( handle->mode & ARTIO_MODE_ACCESS ) { artio_file_fflush(handle); fclose(handle->fh); } free(handle); return ARTIO_SUCCESS; } void artio_file_set_endian_swap_tag_i(artio_fh *handle) { handle->mode |= ARTIO_MODE_ENDIAN_SWAP; } #endif /* ifndef ARTIO_MPI */ yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_selector.c000066400000000000000000000214211510711153200264210ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include #include #ifdef _WIN32 typedef __int64 int64_t; typedef __int32 int32_t; #else #include #endif #define ARTIO_SELECTION_LIST_SIZE 1024 #define ARTIO_SELECTION_VOLUME_LIMIT (1L<<60) int artio_add_volume_to_selection( artio_fileset *handle, int lcoords[3], int rcoords[3], int64_t sfcs[8], artio_selection *selection ); int artio_selection_iterator( artio_selection *selection, int64_t max_range_size, int64_t *start, int64_t *end ) { if ( selection->cursor < 0 ) { selection->cursor = 0; } if ( selection->cursor == selection->num_ranges ) { selection->cursor = -1; return ARTIO_SELECTION_EXHAUSTED; } if ( selection->subcycle > 0 ) { *start = selection->subcycle+1; } else { *start = selection->list[2*selection->cursor]; } *end = selection->list[2*selection->cursor+1]; if ( *end - *start > max_range_size ) { *end = *start + max_range_size-1; selection->subcycle = *end; } else { selection->subcycle = -1; selection->cursor++; } return ARTIO_SUCCESS; } int artio_selection_iterator_reset( artio_selection *selection ) { selection->cursor = -1; selection->subcycle = -1; return ARTIO_SUCCESS; } int64_t artio_selection_size( artio_selection *selection ) { int i; int64_t count = 0; for ( i = 0; i < selection->num_ranges; i++ ) { count += selection->list[2*i+1] - selection->list[2*i] + 1; } return count; } artio_selection *artio_selection_allocate( artio_fileset *handle ) { artio_selection *selection = (artio_selection *)malloc(sizeof(artio_selection)); if ( selection != NULL ) { selection->list = (int64_t *)malloc(2*ARTIO_SELECTION_LIST_SIZE*sizeof(int64_t)); if ( selection->list == NULL ) { free(selection); return NULL; } } selection->subcycle = -1; selection->cursor = -1; selection->size = ARTIO_SELECTION_LIST_SIZE; selection->num_ranges = 0; selection->fileset = handle; return selection; } int artio_selection_destroy( artio_selection *selection ) { if ( selection == NULL ) { return ARTIO_ERR_INVALID_SELECTION; } if ( selection->list != NULL ) { free( selection->list ); } free(selection); return ARTIO_SUCCESS; } int artio_selection_add_range( artio_selection *selection, int64_t start, int64_t end ) { int i, j; int64_t *new_list; if ( selection == NULL ) { return ARTIO_ERR_INVALID_SELECTION; } if ( start < 0 || end >= selection->fileset->num_root_cells || start > end ) { return ARTIO_ERR_INVALID_SFC_RANGE; } for ( i = 0; i < selection->num_ranges; i++ ) { if ( (start >= selection->list[2*i] && start <= selection->list[2*i+1] ) || (end >= selection->list[2*i] && end <= selection->list[2*i+1] ) ) { return ARTIO_ERR_INVALID_STATE; } } /* locate page */ if ( selection->num_ranges == 0 ) { selection->list[0] = start; selection->list[1] = end; selection->num_ranges = 1; return ARTIO_SUCCESS; } else { /* eventually replace with binary search */ for ( i = 0; i < selection->num_ranges; i++ ) { if ( end < selection->list[2*i] ) { break; } } if ( ( i == 0 && end < selection->list[2*i]-1 ) || ( i == selection->num_ranges && start > selection->list[2*i-1]+1 ) || ( end < selection->list[2*i]-1 && start > selection->list[2*i-1]+1 ) ) { if ( selection->num_ranges == selection->size ) { new_list = (int64_t *)malloc(4*selection->size*sizeof(int64_t)); if ( new_list == NULL ) { return ARTIO_ERR_MEMORY_ALLOCATION; } for ( j = 0; j < i; j++ ) { new_list[2*j] = selection->list[2*j]; new_list[2*j+1] = selection->list[2*j+1]; } for ( ; j < selection->num_ranges; j++ ) { new_list[2*j+2] = selection->list[2*j]; new_list[2*j+3] = selection->list[2*j+1]; } selection->size *= 2; free( selection->list ); selection->list = new_list; } else { for ( j = selection->num_ranges-1; j >= i; j-- ) { selection->list[2*j+2] = selection->list[2*j]; selection->list[2*j+3] = selection->list[2*j+1]; } } selection->list[2*i] = start; selection->list[2*i+1] = end; selection->num_ranges++; } else { if ( end == selection->list[2*i]-1 ) { selection->list[2*i] = start; } else if ( start == selection->list[2*i-1]+1 ) { selection->list[2*i-1] = end; } /* merge 2 ranges if necessary */ if ( selection->list[2*i] == selection->list[2*i-1]+1 ) { selection->list[2*i-1] = selection->list[2*i+1]; selection->num_ranges--; for ( ; i < selection->num_ranges; i++ ) { selection->list[2*i] = selection->list[2*i+2]; selection->list[2*i+1] = selection->list[2*i+3]; } } } } return ARTIO_SUCCESS; } int artio_selection_add_root_cell( artio_selection *selection, int coords[3] ) { int i; int64_t sfc; if ( selection == NULL ) { return ARTIO_ERR_INVALID_SELECTION; } for ( i = 0; i < 3; i++ ) { if ( coords[i] < 0 || coords[i] >= selection->fileset->num_grid ) { return ARTIO_ERR_INVALID_COORDINATES; } } sfc = artio_sfc_index( selection->fileset, coords ); return artio_selection_add_range( selection, sfc, sfc ); } void artio_selection_print( artio_selection *selection ) { int i; for ( i = 0; i < selection->num_ranges; i++ ) { printf("%u: %ld %ld\n", i, selection->list[2*i], selection->list[2*i+1] ); } } artio_selection *artio_select_all( artio_fileset *handle ) { artio_selection *selection; if ( handle == NULL ) { return NULL; } selection = artio_selection_allocate(handle); if ( selection == NULL ) { return NULL; } if ( artio_selection_add_range( selection, 0, handle->num_root_cells-1 ) != ARTIO_SUCCESS ) { artio_selection_destroy(selection); return NULL; } return selection; } artio_selection *artio_select_volume( artio_fileset *handle, double lpos[3], double rpos[3] ) { int i; int64_t sfc; int coords[3]; int lcoords[3]; int rcoords[3]; artio_selection *selection; if ( handle == NULL ) { return NULL; } for ( i = 0; i < 3; i++ ) { if ( lpos[i] < 0.0 || lpos[i] >= rpos[i] ) { return NULL; } } for ( i = 0; i < 3; i++ ) { lcoords[i] = (int)lpos[i]; rcoords[i] = (int)rpos[i]; } selection = artio_selection_allocate( handle ); if ( selection == NULL ) { return NULL; } for ( coords[0] = lcoords[0]; coords[0] <= rcoords[0]; coords[0]++ ) { for ( coords[1] = lcoords[1]; coords[1] <= rcoords[1]; coords[1]++ ) { for ( coords[2] = lcoords[2]; coords[2] <= rcoords[2]; coords[2]++ ) { sfc = artio_sfc_index( handle, coords ); if ( artio_selection_add_range( selection, sfc, sfc ) != ARTIO_SUCCESS ) { artio_selection_destroy(selection); return NULL; } } } } return selection; } artio_selection *artio_select_cube( artio_fileset *handle, double center[3], double size ) { int i, j, k, dx; int64_t sfc; int coords[3], coords2[3]; artio_selection *selection; if ( handle == NULL ) { return NULL; } if ( size <= 0.0 || size > handle->num_grid/2 ) { return NULL; } dx = (int)(center[0] + 0.5*size) - (int)(center[0] - 0.5*size) + 1; for ( i = 0; i < 3; i++ ) { if ( center[i] < 0.0 || center[i] >= handle->num_grid ) { return NULL; } coords[i] = (int)(center[i] - 0.5*size + handle->num_grid) % handle->num_grid; } selection = artio_selection_allocate( handle ); if ( selection == NULL ) { return NULL; } for ( i = coords[0]-dx; i <= coords[0]+dx; i++ ) { coords2[0] = (i + handle->num_grid) % handle->num_grid; for ( j = coords[1]-dx; j <= coords[1]+dx; j++ ) { coords2[1] = (j + handle->num_grid) % handle->num_grid; for ( k = coords[2]-dx; k <= coords[2]+dx; k++ ) { coords2[2] = (k + handle->num_grid) % handle->num_grid; sfc = artio_sfc_index( handle, coords2 ); if ( artio_selection_add_range( selection, sfc, sfc ) != ARTIO_SUCCESS ) { artio_selection_destroy(selection); return NULL; } } } } return selection; } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/artio_sfc.c000066400000000000000000000202321510711153200253530ustar00rootroot00000000000000/********************************************************************** * Copyright (c) 2012-2013, Douglas H. Rudd * All rights reserved. * * This file is part of the artio library. * * artio is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * artio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copies of the GNU Lesser General Public License and the GNU General * Public License are available in the file LICENSE, included with this * distribution. If you failed to receive a copy of this file, see * **********************************************************************/ #include "artio.h" #include "artio_internal.h" #include #include #include #define rollLeft(x,y,mask) ((x<>(nDim-y))) & mask #define rollRight(x,y,mask) ((x>>y) | (x<<(nDim-y))) & mask /******************************************************* * morton_index ******************************************************/ int64_t artio_morton_index( artio_fileset *handle, int coords[nDim] ) /* purpose: interleaves the bits of the nDim integer * coordinates, normally called Morton or z-ordering * * Used by the hilbert curve algorithm */ { int i, d; int64_t mortonnumber = 0; int64_t bitMask = 1L << (handle->nBitsPerDim - 1); /* interleave bits of coordinates */ for ( i = handle->nBitsPerDim; i > 0; i-- ) { for ( d = 0; d < nDim; d++ ) { mortonnumber |= ( coords[d] & bitMask ) << (((nDim - 1) * i ) - d ); } bitMask >>= 1; } return mortonnumber; } /******************************************************* * hilbert_index ******************************************************/ int64_t artio_hilbert_index( artio_fileset *handle, int coords[nDim] ) /* purpose: calculates the 1-d space-filling-curve index * corresponding to the nDim set of coordinates * * Uses the Hilbert curve algorithm given in * Alternative Algorithm for Hilbert's Space- * Filling Curve, A.R. Butz, IEEE Trans on Comp., * p. 424, 1971 */ { int i, j; int64_t hilbertnumber; int64_t singleMask; int64_t dimMask; int64_t numberShifts; int principal; int64_t o; int64_t rho; int64_t w; int64_t interleaved; /* begin by transposing bits */ interleaved = artio_morton_index( handle, coords ); /* mask out nDim and 1 bit blocks starting * at highest order bits */ singleMask = 1L << ((handle->nBitsPerDim - 1) * nDim); dimMask = singleMask; for ( i = 1; i < nDim; i++ ) { dimMask |= singleMask << i; } w = 0; numberShifts = 0; hilbertnumber = 0; while (singleMask) { o = (interleaved ^ w) & dimMask; o = rollLeft( o, numberShifts, dimMask ); rho = o; for ( j = 1; j < nDim; j++ ) { rho ^= (o>>j) & dimMask; } hilbertnumber |= rho; /* break out early (we already have complete number * no need to calculate other numbers) */ if ( singleMask == 1 ) { break; } /* calculate principal position */ principal = 0; for ( i = 1; i < nDim; i++ ) { if ( (hilbertnumber & singleMask) != ((hilbertnumber>>i) & singleMask)) { principal = i; break; } } /* complement lowest bit position */ o ^= singleMask; /* force even parity by complementing at principal position if necessary * Note: lowest order bit of hilbertnumber gives you parity of o at this * point due to xor operations of previous steps */ if ( !(hilbertnumber & singleMask) ) { o ^= singleMask << principal; } /* rotate o right by numberShifts */ o = rollRight( o, numberShifts, dimMask ); /* find next numberShifts */ numberShifts += (nDim - 1) - principal; numberShifts %= nDim; w ^= o; w >>= nDim; singleMask >>= nDim; dimMask >>= nDim; } return hilbertnumber; } /******************************************************* * hilbert_coords ******************************************************/ void artio_hilbert_coords( artio_fileset *handle, int64_t index, int coords[nDim] ) /* purpose: performs the inverse of sfc_index, * taking a 1-d space-filling-curve index * and transforming it into nDim coordinates * * returns: the coordinates in coords */ { int i, j; int64_t dimMask; int64_t singleMask; int64_t sigma; int64_t sigma_; int64_t tau; int64_t tau_; int num_shifts; int principal; int64_t w; int64_t x = 0; w = 0; sigma_ = 0; num_shifts = 0; singleMask = 1L << ((handle->nBitsPerDim - 1) * nDim); dimMask = singleMask; for ( i = 1; i < nDim; i++ ) { dimMask |= singleMask << i; } for ( i = 0; i < handle->nBitsPerDim; i++ ) { sigma = ((index & dimMask) ^ ( (index & dimMask) >> 1 )) & dimMask; sigma_ |= rollRight( sigma, num_shifts, dimMask ); principal = nDim - 1; for ( j = 1; j < nDim; j++ ) { if ( (index & singleMask) != ((index >> j) & singleMask) ) { principal = nDim - j - 1; break; } } /* complement nth bit */ tau = sigma ^ singleMask; /* if even parity, complement in principal bit position */ if ( !(index & singleMask) ) { tau ^= singleMask << ( nDim - principal - 1 ); } tau_ = rollRight( tau, num_shifts, dimMask ); num_shifts += principal; num_shifts %= nDim; w |= ((w & dimMask) ^ tau_) >> nDim; dimMask >>= nDim; singleMask >>= nDim; } x = w ^ sigma_; /* undo bit interleaving to get coordinates */ for ( i = 0; i < nDim; i++ ) { coords[i] = 0; singleMask = 1L << (nDim*handle->nBitsPerDim - 1 - i); for ( j = 0; j < handle->nBitsPerDim; j++ ) { if ( x & singleMask ) { coords[i] |= 1 << (handle->nBitsPerDim-j-1); } singleMask >>= nDim; } } } int64_t artio_slab_index( artio_fileset *handle, int coords[nDim], int slab_dim ) { int64_t num_grid = 1L << handle->nBitsPerDim; int64_t index; switch ( slab_dim ) { case 0: index = num_grid*num_grid*coords[0] + num_grid*coords[1] + coords[2]; break; case 1: index = num_grid*num_grid*coords[1] + num_grid*coords[0] + coords[2]; break; case 2: index = num_grid*num_grid*coords[2] + num_grid*coords[0] + coords[1]; break; default: index = -1; } return index; } void artio_slab_coords( artio_fileset *handle, int64_t index, int coords[nDim], int slab_dim ) { int64_t num_grid = 1L << handle->nBitsPerDim; switch ( slab_dim ) { case 0: coords[2] = index % num_grid; coords[1] = ((index - coords[2] )/num_grid) % num_grid; coords[0] = (index - coords[2] - num_grid*coords[1])/(num_grid*num_grid); break; case 1: coords[2] = index % num_grid; coords[0] = ((index - coords[2] )/num_grid) % num_grid; coords[1] = (index - coords[2] - num_grid*coords[0])/(num_grid*num_grid); break; case 2: coords[1] = index % num_grid; coords[0] = ((index - coords[1] )/num_grid) % num_grid; coords[2] = (index - coords[1] - num_grid*coords[0])/(num_grid*num_grid); break; } } int64_t artio_sfc_index_position( artio_fileset *handle, double position[nDim] ) { int i; int coords[nDim]; for ( i = 0; i < nDim; i++ ) { coords[i] = (int)position[i]; } return artio_sfc_index(handle, coords); } int64_t artio_sfc_index( artio_fileset *handle, int coords[nDim] ) { switch ( handle->sfc_type ) { case ARTIO_SFC_SLAB_X: return artio_slab_index(handle, coords, 0); case ARTIO_SFC_SLAB_Y: return artio_slab_index(handle, coords, 1); case ARTIO_SFC_SLAB_Z: return artio_slab_index(handle, coords, 2); case ARTIO_SFC_HILBERT: return artio_hilbert_index( handle, coords ); default: return -1; } } void artio_sfc_coords( artio_fileset *handle, int64_t index, int coords[nDim] ) { int i; switch ( handle->sfc_type ) { case ARTIO_SFC_SLAB_X: artio_slab_coords( handle, index, coords, 0 ); break; case ARTIO_SFC_SLAB_Y: artio_slab_coords( handle, index, coords, 1 ); break; case ARTIO_SFC_SLAB_Z: artio_slab_coords( handle, index, coords, 2 ); break; case ARTIO_SFC_HILBERT: artio_hilbert_coords( handle, index, coords ); break; default : for ( i = 0; i < nDim; i++ ) { coords[i] = -1; } break; } } yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/cosmology.c000066400000000000000000000311011510711153200254120ustar00rootroot00000000000000#include #include #include #ifndef ERROR #include #define ERROR(msg) { fprintf(stderr,"%s\n",msg); exit(1); } #endif #ifndef ASSERT #include #define ASSERT(exp) { if(!(exp)) { fprintf(stderr,"Failed assertion %s, line: %d\n",#exp,__LINE__); } } #endif #ifndef HEAPALLOC #include #define HEAPALLOC(type,size) (type *)malloc((size)*sizeof(type)) #endif #ifndef NEWARR #include #define NEWARR(size) HEAPALLOC(double,size) #endif #ifndef DELETE #include #define DELETE(ptr) free(ptr) #endif #include "cosmology.h" struct CosmologyParametersStruct { int set; int ndex; int size; double *la; double *aUni; double *aBox; double *tCode; double *tPhys; double *dPlus; double *qPlus; double aLow; double tCodeOffset; double OmegaM; double OmegaD; double OmegaB; double OmegaL; double OmegaK; double OmegaR; double h; double DeltaDC; int flat; double Omh2; double Obh2; }; void cosmology_clear_table(CosmologyParameters *c); void cosmology_fill_table(CosmologyParameters *c, double amin, double amax); void cosmology_fill_table_abox(CosmologyParameters *c, int istart, int n); CosmologyParameters *cosmology_allocate() { CosmologyParameters *c = HEAPALLOC(CosmologyParameters,1); if ( c != NULL ) { memset(c, 0, sizeof(CosmologyParameters)); c->ndex = 200; c->aLow = 1.0e-2; } return c; } void cosmology_free(CosmologyParameters *c) { cosmology_clear_table(c); DELETE(c); } int cosmology_is_set(CosmologyParameters *c) { return (c->OmegaM>0.0 && c->OmegaB>0.0 && c->h>0.0); } void cosmology_fail_on_reset(const char *name, double old_value, double new_value) { char str[150]; sprintf(str,"Trying to change %s from %lg to %lg...\nCosmology has been fixed and cannot be changed.\n",name,old_value,new_value); ERROR(str); } void cosmology_set_OmegaM(CosmologyParameters *c, double v) { if(v < 1.0e-3) v = 1.0e-3; if(fabs(c->OmegaM-v) > 1.0e-5) { if(c->set) cosmology_fail_on_reset("OmegaM",c->OmegaM,v); c->OmegaM = v; c->flat = (fabs(c->OmegaM+c->OmegaL-1.0) > 1.0e-5) ? 0 : 1; cosmology_clear_table(c); } } void cosmology_set_OmegaL(CosmologyParameters *c, double v) { if(fabs(c->OmegaL-v) > 1.0e-5) { if(c->set) cosmology_fail_on_reset("OmegaL",c->OmegaL,v); c->OmegaL = v; c->flat = (fabs(c->OmegaM+c->OmegaL-1.0) > 1.0e-5) ? 0 : 1; cosmology_clear_table(c); } } void cosmology_set_OmegaB(CosmologyParameters *c, double v) { if(v < 0.0) v = 0.0; if(fabs(c->OmegaB-v) > 1.0e-5) { if(c->set) cosmology_fail_on_reset("OmegaB",c->OmegaB,v); c->OmegaB = v; cosmology_clear_table(c); } } void cosmology_set_h(CosmologyParameters *c, double v) { if(fabs(c->h-v) > 1.0e-5) { if(c->set) cosmology_fail_on_reset("h",c->h,v); c->h = v; cosmology_clear_table(c); } } void cosmology_set_DeltaDC(CosmologyParameters *c, double v) { if(fabs(c->DeltaDC-v) > 1.0e-3) { if(c->set) cosmology_fail_on_reset("DeltaDC",c->DeltaDC,v); c->DeltaDC = v; cosmology_clear_table(c); } } void cosmology_init(CosmologyParameters *c) { if(c->size == 0) /* reset only if the state is dirty */ { if(!cosmology_is_set(c)) ERROR("Not all of the required cosmological parameters have been set; the minimum required set is (OmegaM,OmegaB,h)."); if(c->OmegaB > c->OmegaM) c->OmegaB = c->OmegaM; c->OmegaD = c->OmegaM - c->OmegaB; if(c->flat) { c->OmegaK = 0.0; c->OmegaL = 1.0 - c->OmegaM; } else { c->OmegaK = 1.0 - (c->OmegaM+c->OmegaL); } c->OmegaR = 4.166e-5/(c->h*c->h); c->Omh2 = c->OmegaM*c->h*c->h; c->Obh2 = c->OmegaB*c->h*c->h; cosmology_fill_table(c,c->aLow,1.0); c->tCodeOffset = 0.0; /* Do need to set it to zero first */ #ifndef NATIVE_TCODE_NORMALIZATION c->tCodeOffset = 0.0 - tCode(c,inv_aBox(c,1.0)); #endif } } void cosmology_set_fixed(CosmologyParameters *c) { cosmology_init(c); c->set = 1; } double cosmology_mu(CosmologyParameters *c, double a) { return sqrt(((a*a*c->OmegaL+c->OmegaK)*a+c->OmegaM)*a+c->OmegaR); } double cosmology_dc_factor(CosmologyParameters *c, double dPlus) { double dc = 1.0 + dPlus*c->DeltaDC; return 1.0/pow((dc>0.001)?dc:0.001,1.0/3.0); } void cosmology_fill_table_integrate(CosmologyParameters *c, double a, double y[], double f[]) { double mu = cosmology_mu(c, a); double abox = a*cosmology_dc_factor(c, y[2]); f[0] = a/(abox*abox*mu); f[1] = a/mu; f[2] = y[3]/(a*mu); f[3] = 1.5*c->OmegaM*y[2]/mu; } #ifdef _WIN32 double asinh(double x){ return log(x + sqrt((x * x) + 1.0)); } #endif void cosmology_fill_table_piece(CosmologyParameters *c, int istart, int n) { int i, j; double tPhysUnit = (3.0856775813e17/(365.25*86400))/c->h; /* 1/H0 in Julian years */ double x, aeq = c->OmegaR/c->OmegaM; double tCodeFac = 1.0/sqrt(aeq); double tPhysFac = tPhysUnit*aeq*sqrt(aeq)/sqrt(c->OmegaM); double da, a0, y0[4], y1[4]; double f1[4], f2[4], f3[4], f4[4]; for(i=istart; iaUni[i] = pow(10.0,c->la[i]); } /* // Small a regime, use analytical formulae for matter + radiation model */ for(i=istart; c->aUni[i]<(c->aLow+1.0e-9) && iaUni[i]/aeq; c->tPhys[i] = tPhysFac*2*x*x*(2+sqrt(x+1))/(3*pow(1+sqrt(x+1),2.0)); c->dPlus[i] = aeq*(x + 2.0/3.0 + (6*sqrt(1+x)+(2+3*x)*log(x)-2*(2+3*x)*log(1+sqrt(1+x)))/(log(64.0)-9)); /* long last term is the decaying mode generated after equality; it is very small for x > 10, I keep ot just for completeness; */ c->qPlus[i] = c->aUni[i]*cosmology_mu(c,c->aUni[i])*(1 + ((2+6*x)/(x*sqrt(1+x))+3*log(x)-6*log(1+sqrt(1+x)))/(log(64)-9)); /* this is a^2*dDPlus/dt/H0 */ c->aBox[i] = c->aUni[i]*cosmology_dc_factor(c,c->dPlus[i]); c->tCode[i] = 1.0 - tCodeFac*asinh(sqrt(aeq/c->aBox[i])); } /* // Large a regime, solve ODEs */ ASSERT(i > 0); tCodeFac = 0.5*sqrt(c->OmegaM); tPhysFac = tPhysUnit; y1[0] = c->tCode[i-1]/tCodeFac; y1[1] = c->tPhys[i-1]/tPhysFac; y1[2] = c->dPlus[i-1]; y1[3] = c->qPlus[i-1]; for(; iaUni[i-1]; da = c->aUni[i] - a0; /* RK4 integration */ for(j=0; j<4; j++) y0[j] = y1[j]; cosmology_fill_table_integrate(c, a0,y1,f1); for(j=0; j<4; j++) y1[j] = y0[j] + 0.5*da*f1[j]; cosmology_fill_table_integrate(c, a0+0.5*da,y1,f2); for(j=0; j<4; j++) y1[j] = y0[j] + 0.5*da*f2[j]; cosmology_fill_table_integrate(c, a0+0.5*da,y1,f3); for(j=0; j<4; j++) y1[j] = y0[j] + da*f3[j]; cosmology_fill_table_integrate(c, a0+da,y1,f4); for(j=0; j<4; j++) y1[j] = y0[j] + da*(f1[j]+2*f2[j]+2*f3[j]+f4[j])/6.0; c->tCode[i] = tCodeFac*y1[0]; c->tPhys[i] = tPhysFac*y1[1]; c->dPlus[i] = y1[2]; c->qPlus[i] = y1[3]; c->aBox[i] = c->aUni[i]*cosmology_dc_factor(c,c->dPlus[i]); } } void cosmology_fill_table(CosmologyParameters *c, double amin, double amax) { int i, imin, imax, iold; double dla = 1.0/c->ndex; double lamin, lamax; double *old_la = c->la; double *old_aUni = c->aUni; double *old_aBox = c->aBox; double *old_tCode = c->tCode; double *old_tPhys = c->tPhys; double *old_dPlus = c->dPlus; double *old_qPlus = c->qPlus; int old_size = c->size; if(amin > c->aLow) amin = c->aLow; lamin = dla*floor(c->ndex*log10(amin)); lamax = dla*ceil(c->ndex*log10(amax)); c->size = 1 + (int)(0.5+c->ndex*(lamax-lamin)); ASSERT(fabs(lamax-lamin-dla*(c->size-1)) < 1.0e-14); c->la = NEWARR(c->size); ASSERT(c->la != NULL); c->aUni = NEWARR(c->size); ASSERT(c->aUni != NULL); c->aBox = NEWARR(c->size); ASSERT(c->aBox != NULL); c->tCode = NEWARR(c->size); ASSERT(c->tCode != NULL); c->tPhys = NEWARR(c->size); ASSERT(c->tPhys != NULL); c->dPlus = NEWARR(c->size); ASSERT(c->dPlus != NULL); c->qPlus = NEWARR(c->size); ASSERT(c->qPlus != NULL); /* // New log10(aUni) table */ for(i=0; isize; i++) { c->la[i] = lamin + dla*i; } if(old_size == 0) { /* // Filling the table for the first time */ cosmology_fill_table_piece(c,0,c->size); } else { /* // Find if we need to expand the lower end */ if(lamin < old_la[0]) { imin = (int)(0.5+c->ndex*(old_la[0]-lamin)); ASSERT(fabs(old_la[0]-lamin-dla*imin) < 1.0e-14); } else imin = 0; /* // Find if we need to expand the upper end */ if(lamax > old_la[old_size-1]) { imax = (int)(0.5+c->ndex*(old_la[old_size-1]-lamin)); ASSERT(fabs(old_la[old_size-1]-lamin-dla*imax) < 1.0e-14); } else imax = c->size - 1; /* // Re-use the rest */ if(lamin > old_la[0]) { iold = (int)(0.5+c->ndex*(lamin-old_la[0])); ASSERT(fabs(lamin-old_la[0]-dla*iold) < 1.0e-14); } else iold = 0; memcpy(c->aUni+imin,old_aUni+iold,sizeof(double)*(imax-imin+1)); memcpy(c->aBox+imin,old_aBox+iold,sizeof(double)*(imax-imin+1)); memcpy(c->tCode+imin,old_tCode+iold,sizeof(double)*(imax-imin+1)); memcpy(c->tPhys+imin,old_tPhys+iold,sizeof(double)*(imax-imin+1)); memcpy(c->dPlus+imin,old_dPlus+iold,sizeof(double)*(imax-imin+1)); memcpy(c->qPlus+imin,old_qPlus+iold,sizeof(double)*(imax-imin+1)); DELETE(old_la); DELETE(old_aUni); DELETE(old_aBox); DELETE(old_tCode); DELETE(old_tPhys); DELETE(old_dPlus); DELETE(old_qPlus); /* // Fill in additional pieces */ if(imin > 0) cosmology_fill_table_piece(c,0,imin); if(imax < c->size-1) cosmology_fill_table_piece(c,imax,c->size); } } void cosmology_clear_table(CosmologyParameters *c) { if(c->size > 0) { DELETE(c->la); DELETE(c->aUni); DELETE(c->aBox); DELETE(c->tCode); DELETE(c->tPhys); DELETE(c->dPlus); DELETE(c->qPlus); c->size = 0; c->la = NULL; c->aUni = NULL; c->aBox = NULL; c->tCode = NULL; c->tPhys = NULL; c->dPlus = NULL; c->qPlus = NULL; } } void cosmology_check_range(CosmologyParameters *c, double a) { ASSERT((a > 1.0e-9) && (a < 1.0e9)); if(c->size == 0) cosmology_init(c); if(a < c->aUni[0]) { cosmology_fill_table(c,a,c->aUni[c->size-1]); } if(a > c->aUni[c->size-1]) { cosmology_fill_table(c,c->aUni[0],a); } } void cosmology_set_thread_safe_range(CosmologyParameters *c, double amin, double amax) { cosmology_check_range(c, amin); cosmology_check_range(c, amax); } double cosmology_get_value_from_table(CosmologyParameters *c, double a, double table[]) { // This is special case code for boundary conditions int idx; double la = log10(a); if (fabs(la - c->la[c->size-1]) < 1.0e-14) { return table[c->size-1]; } else if (fabs(la - c->la[0]) < 1.0e-14) { return table[0]; } idx = (int)(c->ndex*(la-c->la[0])); // Note that because we do idx+1 below, we need -1 here. ASSERT(idx>=0 && (idxsize-1)); /* // Do it as a function of aUni rather than la to ensure exact inversion */ return table[idx] + (table[idx+1]-table[idx])/(c->aUni[idx+1]-c->aUni[idx])*(a-c->aUni[idx]); } int cosmology_find_index(CosmologyParameters *c, double v, double table[]) { int ic, il = 0; int ih = c->size - 1; if(v < table[0]) { return -1; } if(v > table[c->size-1]) { return c->size + 1; } while((ih-il) > 1) { ic = (il+ih)/2; if(v > table[ic]) /* special, not fully optimal form to avoid checking that il < c->size-1 */ il = ic; else ih = ic; } ASSERT(il+1 < c->size); return il; } /* // Direct and inverse functions */ #define DEFINE_FUN(name,offset) \ double name(CosmologyParameters *c, double a) \ { \ cosmology_check_range(c,a); \ return cosmology_get_value_from_table(c,a,c->name) + offset; \ } \ double inv_##name(CosmologyParameters *c, double v) \ { \ int idx; \ double *table; \ if(c->size == 0) cosmology_init(c); \ v -= offset; \ table = c->name; \ idx = cosmology_find_index(c,v,table); \ while(idx < 0) \ { \ cosmology_check_range(c,0.5*c->aUni[0]); \ table = c->name; \ idx = cosmology_find_index(c,v,table); \ } \ while(idx > c->size) \ { \ cosmology_check_range(c,2.0*c->aUni[c->size-1]); \ table = c->name; \ idx = cosmology_find_index(c,v,table); \ } \ return c->aUni[idx] + (c->aUni[idx+1]-c->aUni[idx])/(table[idx+1]-table[idx])*(v-table[idx]); \ } DEFINE_FUN(aBox,0.0); DEFINE_FUN(tCode,c->tCodeOffset); DEFINE_FUN(tPhys,0.0); DEFINE_FUN(dPlus,0.0); DEFINE_FUN(qPlus,0.0); #undef DEFINE_FUN yt-project-yt-f043ac8/yt/frontends/artio/artio_headers/cosmology.h000066400000000000000000000061321510711153200254250ustar00rootroot00000000000000#ifndef __COSMOLOGY_H__ #define __COSMOLOGY_H__ typedef struct CosmologyParametersStruct CosmologyParameters; #define COSMOLOGY_DECLARE_PRIMARY_PARAMETER(name) \ void cosmology_set_##name(CosmologyParameters *c, double value) #define cosmology_set(c,name,value) \ cosmology_set_##name(c,value) COSMOLOGY_DECLARE_PRIMARY_PARAMETER(OmegaM); COSMOLOGY_DECLARE_PRIMARY_PARAMETER(OmegaB); COSMOLOGY_DECLARE_PRIMARY_PARAMETER(OmegaL); COSMOLOGY_DECLARE_PRIMARY_PARAMETER(h); COSMOLOGY_DECLARE_PRIMARY_PARAMETER(DeltaDC); #undef COSMOLOGY_DECLARE_PRIMARY_PARAMETER CosmologyParameters *cosmology_allocate(); void cosmology_free(CosmologyParameters *c); /* // Check that all required cosmological parameters have been set. // The minimum set is OmegaM, OmegaB, and h. By default, zero OmegaL, // OmegaK, and the DC mode are assumed. */ int cosmology_is_set(CosmologyParameters *c); /* // Freeze the cosmology and forbid any further changes to it. // In codes that include user-customizable segments (like plugins), // this function van be used for insuring that a user does not // change the cosmology in mid-run. */ void cosmology_set_fixed(CosmologyParameters *c); /* // Manual initialization. This does not need to be called, // the initialization is done automatically on the first call // to a relevant function. */ void cosmology_init(CosmologyParameters *c); /* // Set the range of global scale factors for thread-safe // calls to direct functions until the argument leaves the range. */ void cosmology_set_thread_safe_range(CosmologyParameters *c, double amin, double amax); /* // Direct functions take the global cosmological scale factor as the argument. // These functionsare are thread-safe if called with the argument in the // range set by a prior call to cosmology_set_thread_safe_range(...). // Calling them with the argument outside that range is ok, but breaks // thread-safety assurance. */ #define DEFINE_FUN(name) \ double name(CosmologyParameters *c, double a); \ double inv_##name(CosmologyParameters *c, double v); DEFINE_FUN(aBox); DEFINE_FUN(tCode); DEFINE_FUN(tPhys); DEFINE_FUN(dPlus); DEFINE_FUN(qPlus); /* Q+ = a^2 dD+/(H0 dt) */ #undef DEFINE_FUN /* // Conversion macros */ #define abox_from_auni(c,a) aBox(c,a) #define tcode_from_auni(c,a) tCode(c,a) #define tphys_from_auni(c,a) tPhys(c,a) #define dplus_from_auni(c,a) dPlus(c,a) #define auni_from_abox(c,v) inv_aBox(c,v) #define auni_from_tcode(c,v) inv_tCode(c,v) #define auni_from_tphys(c,v) inv_tPhys(c,v) #define auni_from_dplus(c,v) inv_dPlus(c,v) #define abox_from_tcode(c,tcode) aBox(c,inv_tCode(c,tcode)) #define tcode_from_abox(c,abox) tCode(c,inv_aBox(c,abox)) #define tphys_from_abox(c,abox) tPhys(c,inv_aBox(c,abox)) #define tphys_from_tcode(c,tcode) tPhys(c,inv_tCode(c,tcode)) #define dplus_from_tcode(c,tcode) dPlus(c,inv_tCode(c,tcode)) /* // Hubble parameter in km/s/Mpc; defined as macro so that it can be // undefined if needed to avoid the name clash. */ double cosmology_mu(CosmologyParameters *c, double a); #define Hubble(c,a) (100*c->h*cosmology_mu(c,a)/(a*a)) #endif /* __COSMOLOGY_H__ */ yt-project-yt-f043ac8/yt/frontends/artio/data_structures.py000066400000000000000000000505621510711153200242240ustar00rootroot00000000000000import os import weakref from collections import defaultdict import numpy as np from yt.data_objects.field_data import YTFieldData from yt.data_objects.index_subobjects.octree_subset import OctreeSubset from yt.data_objects.static_output import Dataset from yt.data_objects.unions import ParticleUnion from yt.frontends.artio import _artio_caller from yt.frontends.artio._artio_caller import ( ARTIOSFCRangeHandler, artio_fileset, artio_is_valid, ) from yt.frontends.artio.fields import ARTIOFieldInfo from yt.funcs import mylog, setdefaultattr from yt.geometry import particle_deposit as particle_deposit from yt.geometry.geometry_handler import Index, YTDataChunk from yt.utilities.exceptions import YTParticleDepositionNotImplemented class ARTIOOctreeSubset(OctreeSubset): _domain_offset = 0 domain_id = -1 _con_args = ("base_region", "sfc_start", "sfc_end", "oct_handler", "ds") _type_name = "octree_subset" _num_zones = 2 def __init__(self, base_region, sfc_start, sfc_end, oct_handler, ds): self.field_data = YTFieldData() self.field_parameters = {} self.sfc_start = sfc_start self.sfc_end = sfc_end self._oct_handler = oct_handler self.ds = ds self._last_mask = None self._last_selector_id = None self._current_particle_type = "all" self._current_fluid_type = self.ds.default_fluid_type self.base_region = base_region self.base_selector = base_region.selector @property def oct_handler(self): return self._oct_handler @property def min_ind(self): return self.sfc_start @property def max_ind(self): return self.sfc_end def fill(self, fields, selector): if len(fields) == 0: return [] handle = self.oct_handler.artio_handle field_indices = [ handle.parameters["grid_variable_labels"].index(f) for (ft, f) in fields ] cell_count = selector.count_oct_cells(self.oct_handler, self.domain_id) self.data_size = cell_count levels, cell_inds, file_inds = self.oct_handler.file_index_octs( selector, self.domain_id, cell_count ) domain_counts = self.oct_handler.domain_count(selector) tr = [np.zeros(cell_count, dtype="float64") for field in fields] self.oct_handler.fill_sfc( levels, cell_inds, file_inds, domain_counts, field_indices, tr ) tr = dict(zip(fields, tr, strict=True)) return tr def fill_particles(self, fields): if len(fields) == 0: return {} ptype_indices = self.ds.particle_types art_fields = [(ptype_indices.index(ptype), fname) for ptype, fname in fields] species_data = self.oct_handler.fill_sfc_particles(art_fields) tr = defaultdict(dict) # Now we need to sum things up and then fill for s, f in fields: count = 0 dt = "float64" # default i = ptype_indices.index(s) # No vector fields in ARTIO count += species_data[i, f].size dt = species_data[i, f].dtype tr[s][f] = np.zeros(count, dtype=dt) cp = 0 v = species_data.pop((i, f)) tr[s][f][cp : cp + v.size] = v cp += v.size return tr # We create something of a fake octree here. This is primarily to enable us to # reuse code for things like __getitem__ and the like. We will also create a # new oct_handler type that is functionally equivalent, except that it will # only manage the root mesh. class ARTIORootMeshSubset(ARTIOOctreeSubset): _num_zones = 1 _type_name = "sfc_subset" _selector_module = _artio_caller domain_id = -1 def fill(self, fields, selector): # We know how big these will be. if len(fields) == 0: return [] handle = self.ds._handle field_indices = [ handle.parameters["grid_variable_labels"].index(f) for (ft, f) in fields ] tr = self.oct_handler.fill_sfc(selector, field_indices) self.data_size = tr[0].size tr = dict(zip(fields, tr, strict=True)) return tr def deposit(self, positions, fields=None, method=None, kernel_name="cubic"): # Here we perform our particle deposition. if fields is None: fields = [] cls = getattr(particle_deposit, f"deposit_{method}", None) if cls is None: raise YTParticleDepositionNotImplemented(method) nz = self.nz nvals = (nz, nz, nz, self.ires.size) # We allocate number of zones, not number of octs op = cls(nvals, kernel_name) op.initialize() mylog.debug( "Depositing %s (%s^3) particles into %s Root Mesh", positions.shape[0], positions.shape[0] ** 0.3333333, nvals[-1], ) pos = np.array(positions, dtype="float64") f64 = [np.array(f, dtype="float64") for f in fields] self.oct_handler.deposit(op, self.base_selector, pos, f64) vals = op.finalize() if vals is None: return return np.asfortranarray(vals) class ARTIOIndex(Index): def __init__(self, ds, dataset_type="artio"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) # for now, the index file is the dataset! self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self.max_level = ds.max_level self.range_handlers = {} self.float_type = np.float64 super().__init__(ds, dataset_type) @property def max_range(self): return self.dataset.max_range def _setup_geometry(self): mylog.debug("Initializing Geometry Handler empty for now.") def get_smallest_dx(self): """ Returns (in code units) the smallest cell size in the simulation. """ return ( self.dataset.domain_width / (self.dataset.domain_dimensions * 2 ** (self.max_level)) ).min() def _get_particle_type_counts(self): # this could be done in the artio C interface without creating temporary # arrays but I don't want to touch that code # if a future brave soul wants to try, take a look at # `read_sfc_particles` in _artio_caller.pyx result = {} ad = self.ds.all_data() for ptype in self.ds.particle_types_raw: result[ptype] = ad[ptype, "PID"].size return result def convert(self, unit): return self.dataset.conversion_factors[unit] def find_max(self, field, finest_levels=3): """ Returns (value, center) of location of maximum for a given field. """ if (field, finest_levels) in self._max_locations: return self._max_locations[field, finest_levels] mv, pos = self.find_max_cell_location(field, finest_levels) self._max_locations[field, finest_levels] = (mv, pos) return mv, pos def find_max_cell_location(self, field, finest_levels=3): source = self.all_data() if finest_levels is not False: source.min_level = self.max_level - finest_levels mylog.debug("Searching for maximum value of %s", field) max_val, mx, my, mz = source.quantities["MaxLocation"](field) mylog.info("Max Value is %0.5e at %0.16f %0.16f %0.16f", max_val, mx, my, mz) self.ds.parameters[f"Max{field}Value"] = max_val self.ds.parameters[f"Max{field}Pos"] = f"{mx, my, mz}" return max_val, np.array((mx, my, mz), dtype="float64") def _detect_output_fields(self): self.fluid_field_list = self._detect_fluid_fields() self.particle_field_list = self._detect_particle_fields() self.field_list = self.fluid_field_list + self.particle_field_list mylog.debug("Detected fields: %s", (self.field_list,)) def _detect_fluid_fields(self): return [("artio", f) for f in self.ds.artio_parameters["grid_variable_labels"]] def _detect_particle_fields(self): fields = set() for i, ptype in enumerate(self.ds.particle_types): if ptype == "all": break # This will always be after all intrinsic for fname in self.ds.particle_variables[i]: fields.add((ptype, fname)) return list(fields) def _identify_base_chunk(self, dobj): if getattr(dobj, "_chunk_info", None) is None: try: all_data = all(dobj.left_edge == self.ds.domain_left_edge) and all( dobj.right_edge == self.ds.domain_right_edge ) except Exception: all_data = False base_region = getattr(dobj, "base_region", dobj) sfc_start = getattr(dobj, "sfc_start", None) sfc_end = getattr(dobj, "sfc_end", None) nz = getattr(dobj, "_num_zones", 0) if all_data: mylog.debug("Selecting entire artio domain") list_sfc_ranges = self.ds._handle.root_sfc_ranges_all( max_range_size=self.max_range ) elif sfc_start is not None and sfc_end is not None: mylog.debug("Restricting to %s .. %s", sfc_start, sfc_end) list_sfc_ranges = [(sfc_start, sfc_end)] else: mylog.debug("Running selector on artio base grid") list_sfc_ranges = self.ds._handle.root_sfc_ranges( dobj.selector, max_range_size=self.max_range ) ci = [] # v = np.array(list_sfc_ranges) # list_sfc_ranges = [ (v.min(), v.max()) ] for start, end in list_sfc_ranges: if (start, end) in self.range_handlers.keys(): range_handler = self.range_handlers[start, end] else: range_handler = ARTIOSFCRangeHandler( self.ds.domain_dimensions, self.ds.domain_left_edge, self.ds.domain_right_edge, self.ds._handle, start, end, ) range_handler.construct_mesh() self.range_handlers[start, end] = range_handler if nz != 2: ci.append( ARTIORootMeshSubset( base_region, start, end, range_handler.root_mesh_handler, self.ds, ) ) if nz != 1 and range_handler.total_octs > 0: ci.append( ARTIOOctreeSubset( base_region, start, end, range_handler.octree_handler, self.ds, ) ) dobj._chunk_info = ci if len(list_sfc_ranges) > 1: mylog.info("Created %d chunks for ARTIO", len(list_sfc_ranges)) dobj._current_chunk = list(self._chunk_all(dobj))[0] def _data_size(self, dobj, dobjs): size = 0 for d in dobjs: size += d.data_size return size def _chunk_all(self, dobj): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) yield YTDataChunk(dobj, "all", oobjs, None, cache=True) def _chunk_spatial(self, dobj, ngz, preload_fields=None): if ngz > 0: raise NotImplementedError sobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for og in sobjs: if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og yield YTDataChunk(dobj, "spatial", [g], None, cache=True) def _chunk_io(self, dobj, cache=True, local_only=False): # _current_chunk is made from identify_base_chunk oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for chunk in oobjs: yield YTDataChunk(dobj, "io", [chunk], None, cache=cache) def _read_fluid_fields(self, fields, dobj, chunk=None): if len(fields) == 0: return {}, [] if chunk is None: self._identify_base_chunk(dobj) fields_to_return = {} fields_to_read, fields_to_generate = self._split_fields(fields) if len(fields_to_read) == 0: return {}, fields_to_generate fields_to_return = self.io._read_fluid_selection( self._chunk_io(dobj), dobj.selector, fields_to_read ) return fields_to_return, fields_to_generate def _icoords_to_fcoords( self, icoords: np.ndarray, ires: np.ndarray, axes: tuple[int, ...] | None = None, ) -> tuple[np.ndarray, np.ndarray]: """ Accepts icoords and ires and returns appropriate fcoords and fwidth. Mostly useful for cases where we have irregularly spaced or structured grids. """ dds = self.ds.domain_width[axes,] / ( self.ds.domain_dimensions[axes,] * self.ds.refine_by ** ires[:, None] ) pos = (0.5 + icoords) * dds + self.ds.domain_left_edge[axes,] return pos, dds class ARTIODataset(Dataset): _handle = None _index_class = ARTIOIndex _field_info_class = ARTIOFieldInfo def __init__( self, filename, dataset_type="artio", storage_filename=None, max_range=1024, units_override=None, unit_system="cgs", default_species_fields=None, ): if self._handle is not None: return self.max_range = max_range self.fluid_types += ("artio",) self._filename = filename self._fileset_prefix = filename[:-4] self._handle = artio_fileset(bytes(self._fileset_prefix, "utf-8")) self.artio_parameters = self._handle.parameters # Here we want to initiate a traceback, if the reader is not built. Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename def _set_code_unit_attributes(self): setdefaultattr(self, "mass_unit", self.quan(self.parameters["unit_m"], "g")) setdefaultattr(self, "length_unit", self.quan(self.parameters["unit_l"], "cm")) setdefaultattr(self, "time_unit", self.quan(self.parameters["unit_t"], "s")) setdefaultattr(self, "velocity_unit", self.length_unit / self.time_unit) def _parse_parameter_file(self): # hard-coded -- not provided by headers self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "artio" self.parameters["Time"] = 1.0 # default unit is 1... # read header self.num_grid = self._handle.num_grid self.domain_dimensions = np.ones(3, dtype="int32") * self.num_grid self.domain_left_edge = np.zeros(3, dtype="float64") self.domain_right_edge = np.ones(3, dtype="float64") * self.num_grid # TODO: detect if grid exists self.min_level = 0 # ART has min_level=0 self.max_level = self.artio_parameters["grid_max_level"][0] # TODO: detect if particles exist if self._handle.has_particles: self.num_species = self.artio_parameters["num_particle_species"][0] self.particle_variables = [ ["PID", "SPECIES"] for i in range(self.num_species) ] # If multiple N-BODY species exist, they all have the same name, # which can lead to conflict if not renamed # A particle union will be created later to hold all N-BODY # particles and will take the name "N-BODY" labels = self.artio_parameters["particle_species_labels"] if labels.count("N-BODY") > 1: for species, label in enumerate(labels): if label == "N-BODY": labels[species] = f"N-BODY_{species}" self.particle_types_raw = self.artio_parameters["particle_species_labels"] self.particle_types = tuple(self.particle_types_raw) for species in range(self.num_species): # Mass would be best as a derived field, # but wouldn't detect under 'all' label = self.artio_parameters["particle_species_labels"][species] if "N-BODY" in label: self.particle_variables[species].append("MASS") if self.artio_parameters["num_primary_variables"][species] > 0: self.particle_variables[species].extend( self.artio_parameters[ "species_%02d_primary_variable_labels" % (species,) ] ) if self.artio_parameters["num_secondary_variables"][species] > 0: self.particle_variables[species].extend( self.artio_parameters[ "species_%02d_secondary_variable_labels" % (species,) ] ) else: self.num_species = 0 self.particle_variables = [] self.particle_types = () self.particle_types_raw = self.particle_types self.current_time = self.quan( self._handle.tphys_from_tcode(self.artio_parameters["tl"][0]), "yr" ) # detect cosmology if "abox" in self.artio_parameters: self.cosmological_simulation = True abox = self.artio_parameters["abox"][0] self.omega_lambda = self.artio_parameters["OmegaL"][0] self.omega_matter = self.artio_parameters["OmegaM"][0] self.hubble_constant = self.artio_parameters["hubble"][0] self.current_redshift = 1.0 / self.artio_parameters["auni"][0] - 1.0 self.current_redshift_box = 1.0 / abox - 1.0 self.parameters["initial_redshift"] = ( 1.0 / self.artio_parameters["auni_init"][0] - 1.0 ) self.parameters["CosmologyInitialRedshift"] = self.parameters[ "initial_redshift" ] self.parameters["unit_m"] = self.artio_parameters["mass_unit"][0] self.parameters["unit_t"] = self.artio_parameters["time_unit"][0] * abox**2 self.parameters["unit_l"] = self.artio_parameters["length_unit"][0] * abox if self.artio_parameters["DeltaDC"][0] != 0: mylog.warning( "DeltaDC != 0, which implies auni != abox. " "Be sure you understand which expansion parameter " "is appropriate for your use! (Gnedin, Kravtsov, & Rudd 2011)" ) else: self.cosmological_simulation = False self.parameters["unit_l"] = self.artio_parameters["length_unit"][0] self.parameters["unit_t"] = self.artio_parameters["time_unit"][0] self.parameters["unit_m"] = self.artio_parameters["mass_unit"][0] # hard coded assumption of 3D periodicity self._periodicity = (True, True, True) def create_field_info(self): super().create_field_info() # only make the particle union if there are multiple DM species. # If there are multiple, "N-BODY_0" will be the first species. If there # are not multiple, they will be all under "N-BODY" if "N-BODY_0" in self.particle_types_raw: dm_labels = [ label for label in self.particle_types_raw if "N-BODY" in label ] # Use the N-BODY label for the union to be consistent with the # previous single mass N-BODY case, where this label was used for # all N-BODY particles by default pu = ParticleUnion("N-BODY", dm_labels) self.add_particle_union(pu) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # a valid artio header file starts with a prefix and ends with .art name, _, ext = filename.rpartition(".") if ext != "art": return False return artio_is_valid(bytes(name, "utf-8")) yt-project-yt-f043ac8/yt/frontends/artio/definitions.py000066400000000000000000000037311510711153200233170ustar00rootroot00000000000000yt_to_art = { "Density": "HVAR_GAS_DENSITY", "TotalEnergy": "HVAR_GAS_ENERGY", "GasEnergy": "HVAR_INTERNAL_ENERGY", "Pressure": "HVAR_PRESSURE", "XMomentumDensity": "HVAR_MOMENTUM_X", "YMomentumDensity": "HVAR_MOMENTUM_Y", "ZMomentumDensity": "HVAR_MOMENTUM_Z", "Gamma": "HVAR_GAMMA", "MetalDensitySNIa": "HVAR_METAL_DENSITY_Ia", "MetalDensitySNII": "HVAR_METAL_DENSITY_II", "Potential": "VAR_POTENTIAL", "PotentialHydro": "VAR_POTENTIAL_HYDRO", "particle_position_x": "POSITION_X", "particle_position_y": "POSITION_Y", "particle_position_z": "POSITION_Z", "particle_velocity_x": "VELOCITY_X", "particle_velocity_y": "VELOCITY_Y", "particle_velocity_z": "VELOCITY_Z", "particle_mass": "MASS", "particle_index": "PID", "particle_species": "SPECIES", "creation_time": "BIRTH_TIME", "particle_mass_initial": "INITIAL_MASS", "particle_metallicity1": "METALLICITY_SNIa", "particle_metallicity2": "METALLICITY_SNII", "stars": "STAR", "nbody": "N-BODY", } art_to_yt = dict(zip(yt_to_art.values(), yt_to_art.keys(), strict=True)) class ARTIOconstants: def __init__(self): self.yr = 365.25 * 86400 self.Myr = 1.0e6 * self.yr self.Gyr = 1.0e9 * self.yr self.pc = 3.0856775813e18 self.kpc = 1.0e3 * self.pc self.Mpc = 1.0e6 * self.pc self.kms = 1.0e5 self.mp = 1.672621637e-24 self.k = 1.3806504e-16 self.G = 6.67428e-8 self.c = 2.99792458e10 self.eV = 1.602176487e-12 self.amu = 1.660538782e-24 self.mH = 1.007825 * self.amu self.mHe = 4.002602 * self.amu self.Msun = 1.32712440018e26 / self.G self.Zsun = 0.0199 self.Yp = 0.24 self.wmu = 4.0 / (8.0 - 5.0 * self.Yp) self.wmu_e = 1.0 / (1.0 - 0.5 * self.Yp) self.XH = 1.0 - self.Yp self.XHe = 0.25 * self.Yp self.gamma = 5.0 / 3.0 self.sigmaT = 6.6524e-25 yt-project-yt-f043ac8/yt/frontends/artio/fields.py000066400000000000000000000151051510711153200222500ustar00rootroot00000000000000import numpy as np from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.units.yt_array import YTArray from yt.utilities.physical_constants import amu_cgs, boltzmann_constant_cgs b_units = "code_magnetic" ra_units = "code_length / code_time**2" rho_units = "code_mass / code_length**3" vel_units = "code_velocity" # NOTE: ARTIO uses momentum density. mom_units = "code_mass / (code_length**2 * code_time)" en_units = "code_mass*code_velocity**2/code_length**3" p_units = "code_mass / (code_length * code_time**2)" class ARTIOFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("HVAR_GAS_DENSITY", (rho_units, ["density"], None)), ("HVAR_GAS_ENERGY", (en_units, ["total_energy_density"], None)), ("HVAR_INTERNAL_ENERGY", (en_units, ["thermal_energy_density"], None)), ("HVAR_PRESSURE", (p_units, ["pressure"], None)), ("HVAR_MOMENTUM_X", (mom_units, ["momentum_density_x"], None)), ("HVAR_MOMENTUM_Y", (mom_units, ["momentum_density_y"], None)), ("HVAR_MOMENTUM_Z", (mom_units, ["momentum_density_z"], None)), ("HVAR_GAMMA", ("", ["gamma"], None)), ("HVAR_METAL_DENSITY_Ia", (rho_units, ["metal_ia_density"], None)), ("HVAR_METAL_DENSITY_II", (rho_units, ["metal_ii_density"], None)), ("VAR_POTENTIAL", ("", ["potential"], None)), ("VAR_POTENTIAL_HYDRO", ("", ["gas_potential"], None)), ("RT_HVAR_HI", (rho_units, ["H_density"], None)), ("RT_HVAR_HII", (rho_units, ["H_p1_density"], None)), ("RT_HVAR_H2", (rho_units, ["H2_density"], None)), ("RT_HVAR_HeI", (rho_units, ["He_density"], None)), ("RT_HVAR_HeII", (rho_units, ["He_p1_density"], None)), ("RT_HVAR_HeIII", (rho_units, ["He_p2_density"], None)), ) known_particle_fields: KnownFieldsT = ( ("POSITION_X", ("code_length", ["particle_position_x"], None)), ("POSITION_Y", ("code_length", ["particle_position_y"], None)), ("POSITION_Z", ("code_length", ["particle_position_z"], None)), ("VELOCITY_X", (vel_units, ["particle_velocity_x"], None)), ("VELOCITY_Y", (vel_units, ["particle_velocity_y"], None)), ("VELOCITY_Z", (vel_units, ["particle_velocity_z"], None)), ("MASS", ("code_mass", ["particle_mass"], None)), ("PID", ("", ["particle_index"], None)), ("SPECIES", ("", ["particle_type"], None)), ("BIRTH_TIME", ("", [], None)), # code-units defined as dimensionless to # avoid incorrect conversion ("INITIAL_MASS", ("code_mass", ["initial_mass"], None)), ("METALLICITY_SNIa", ("", ["metallicity_snia"], None)), ("METALLICITY_SNII", ("", ["metallicity_snii"], None)), ) def setup_fluid_fields(self): unit_system = self.ds.unit_system def _get_vel(axis): def velocity(field, data): return data["gas", f"momentum_density_{axis}"] / data["gas", "density"] return velocity for ax in "xyz": self.add_field( ("gas", f"velocity_{ax}"), sampling_type="cell", function=_get_vel(ax), units=unit_system["velocity"], ) def _temperature(field, data): tr = data["gas", "thermal_energy_density"] / data["gas", "density"] # We want this to match *exactly* what ARTIO would compute # internally. We therefore use the exact values that are internal # to ARTIO, rather than yt's own internal constants. mH = 1.007825 * amu_cgs mHe = 4.002602 * amu_cgs Yp = 0.24 XH = 1.0 - Yp XHe = 0.25 * Yp mb = XH * mH + XHe * mHe wmu = 4.0 / (8.0 - 5.0 * Yp) # Note that we have gamma = 5.0/3.0 here tr *= data["gas", "gamma"] - 1.0 tr *= wmu tr *= mb / boltzmann_constant_cgs return tr # TODO: The conversion factor here needs to be addressed, as previously # it was set as: # unit_T = unit_v**2.0*mb / constants.k self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) # Create a metal_density field as sum of existing metal fields. flag1 = ("artio", "HVAR_METAL_DENSITY_Ia") in self.field_list flag2 = ("artio", "HVAR_METAL_DENSITY_II") in self.field_list if flag1 or flag2: if flag1 and flag2: def _metal_density(field, data): tr = data["gas", "metal_ia_density"].copy() np.add(tr, data["gas", "metal_ii_density"], out=tr) return tr elif flag1 and not flag2: def _metal_density(field, data): tr = data["metal_ia_density"] return tr else: def _metal_density(field, data): tr = data["metal_ii_density"] return tr self.add_field( ("gas", "metal_density"), sampling_type="cell", function=_metal_density, units=unit_system["density"], take_log=True, ) def setup_particle_fields(self, ptype): if ptype == "STAR": def _creation_time(field, data): return YTArray( data.ds._handle.tphys_from_tcode_array(data["STAR", "BIRTH_TIME"]), "yr", ) def _age(field, data): return data.ds.current_time - data["STAR", "creation_time"] self.add_field( (ptype, "creation_time"), sampling_type="particle", function=_creation_time, units="yr", ) self.add_field( (ptype, "age"), sampling_type="particle", function=_age, units="yr" ) if self.ds.cosmological_simulation: def _creation_redshift(field, data): return ( 1.0 / data.ds._handle.auni_from_tcode_array( data["STAR", "BIRTH_TIME"] ) - 1.0 ) self.add_field( (ptype, "creation_redshift"), sampling_type="particle", function=_creation_redshift, ) super().setup_particle_fields(ptype) yt-project-yt-f043ac8/yt/frontends/artio/io.py000066400000000000000000000045271510711153200214170ustar00rootroot00000000000000import numpy as np from yt.utilities.io_handler import BaseIOHandler class IOHandlerARTIO(BaseIOHandler): _dataset_type = "artio" def _read_fluid_selection(self, chunks, selector, fields): tr = {ftuple: np.empty(0, dtype="float64") for ftuple in fields} cp = 0 for onechunk in chunks: for artchunk in onechunk.objs: rv = artchunk.fill(fields, selector) for f in fields: tr[f].resize(cp + artchunk.data_size) tr[f][cp : cp + artchunk.data_size] = rv.pop(f) cp += artchunk.data_size return tr def _read_particle_coords(self, chunks, ptf): pn = "POSITION_%s" chunks = list(chunks) fields = [(ptype, pn % ax) for ptype, field_list in ptf.items() for ax in "XYZ"] for chunk in chunks: # These should be organized by grid filename for subset in chunk.objs: rv = dict(**subset.fill_particles(fields)) for ptype in sorted(ptf): x, y, z = ( np.asarray(rv[ptype][pn % ax], dtype="=f8") for ax in "XYZ" ) yield ptype, (x, y, z), 0.0 rv.pop(ptype) def _read_particle_fields(self, chunks, ptf, selector): pn = "POSITION_%s" chunks = list(chunks) fields = [ (ptype, fname) for ptype, field_list in ptf.items() for fname in field_list ] for ptype, field_list in sorted(ptf.items()): for ax in "XYZ": if pn % ax not in field_list: fields.append((ptype, pn % ax)) for chunk in chunks: # These should be organized by grid filename for subset in chunk.objs: rv = dict(**subset.fill_particles(fields)) for ptype, field_list in sorted(ptf.items()): x, y, z = ( np.asarray(rv[ptype][pn % ax], dtype="=f8") for ax in "XYZ" ) mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: data = np.asarray(rv[ptype][field], "=f8") yield (ptype, field), data[mask] rv.pop(ptype) yt-project-yt-f043ac8/yt/frontends/artio/tests/000077500000000000000000000000001510711153200215705ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/artio/tests/__init__.py000066400000000000000000000000001510711153200236670ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/artio/tests/test_outputs.py000066400000000000000000000042351510711153200247300ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.frontends.artio.api import ARTIODataset from yt.loaders import load from yt.testing import ( assert_allclose_units, requires_file, units_override_check, ) from yt.utilities.answer_testing.framework import ( FieldValuesTest, PixelizedProjectionValuesTest, create_obj, data_dir_load, requires_ds, ) _fields = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ("deposit", "all_density"), ("deposit", "all_count"), ) sizmbhloz = "sizmbhloz-clref04SNth-rs9_a0.9011/sizmbhloz-clref04SNth-rs9_a0.9011.art" @requires_ds(sizmbhloz) def test_sizmbhloz(): ds = data_dir_load(sizmbhloz) ds.max_range = 1024 * 1024 assert_equal(str(ds), "sizmbhloz-clref04SNth-rs9_a0.9011.art") dso = [None, ("sphere", ("max", (0.1, "unitary")))] for dobj_name in dso: for field in _fields: for axis in [0, 1, 2]: for weight_field in [None, ("gas", "density")]: yield PixelizedProjectionValuesTest( ds, axis, field, weight_field, dobj_name ) yield FieldValuesTest(ds, field, dobj_name) dobj = create_obj(ds, dobj_name) s1 = dobj["index", "ones"].sum() s2 = sum(mask.sum() for block, mask in dobj.blocks) assert_equal(s1, s2) assert_equal(ds.particle_type_counts, {"N-BODY": 100000, "STAR": 110650}) @requires_file(sizmbhloz) def test_ARTIODataset(): assert isinstance(data_dir_load(sizmbhloz), ARTIODataset) @requires_file(sizmbhloz) def test_units_override(): units_override_check(sizmbhloz) @requires_file(sizmbhloz) def test_particle_derived_field(): def star_age_alias(field, data): # test to make sure we get back data in the correct units # during field detection return data["STAR", "age"].in_units("Myr") ds = load(sizmbhloz) ds.add_field( ("STAR", "new_field"), function=star_age_alias, units="Myr", sampling_type="particle", ) ad = ds.all_data() assert_allclose_units(ad["STAR", "age"].in_units("Myr"), ad["STAR", "new_field"]) yt-project-yt-f043ac8/yt/frontends/athena/000077500000000000000000000000001510711153200205505ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena/__init__.py000066400000000000000000000000001510711153200226470ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena/api.py000066400000000000000000000002401510711153200216670ustar00rootroot00000000000000from . import tests from .data_structures import AthenaDataset, AthenaGrid, AthenaHierarchy from .fields import AthenaFieldInfo from .io import IOHandlerAthena yt-project-yt-f043ac8/yt/frontends/athena/data_structures.py000066400000000000000000000634071510711153200243500ustar00rootroot00000000000000import os import re import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.magnetic_field import get_magnetic_normalization from yt.funcs import mylog, sglob from yt.geometry.api import Geometry from yt.geometry.geometry_handler import YTDataChunk from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.chemical_formulas import compute_mu from yt.utilities.decompose import decompose_array, get_psize from yt.utilities.lib.misc_utilities import get_box_grids_level from .fields import AthenaFieldInfo def chk23(strin): return strin.encode("utf-8") def str23(strin): if isinstance(strin, list): return [s.decode("utf-8") for s in strin] else: return strin.decode("utf-8") def check_readline(fl): line = fl.readline() chk = chk23("SCALARS") if chk in line and not line.startswith(chk): line = line[line.find(chk) :] chk = chk23("VECTORS") if chk in line and not line.startswith(chk): line = line[line.find(chk) :] return line def check_break(line): splitup = line.strip().split() do_break = chk23("SCALAR") in splitup do_break = (chk23("VECTOR") in splitup) & do_break do_break = (chk23("TABLE") in splitup) & do_break do_break = (len(line) == 0) & do_break return do_break def _get_convert(fname): def _conv(data): return data.convert(fname) return _conv class AthenaGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level, start, dimensions, file_offset, read_dims): gname = index.grid_filenames[id] AMRGridPatch.__init__(self, id, filename=gname, index=index) self.filename = gname self.Parent = [] self.Children = [] self.Level = level self.start_index = start.copy() self.stop_index = self.start_index + dimensions self.ActiveDimensions = dimensions.copy() self.file_offset = file_offset self.read_dims = read_dims def _setup_dx(self): # So first we figure out what the index is. We don't assume # that dx=dy=dz , at least here. We probably do elsewhere. id = self.id - self._id_offset if len(self.Parent) > 0: self.dds = self.Parent[0].dds / self.ds.refine_by else: LE, RE = self.index.grid_left_edge[id, :], self.index.grid_right_edge[id, :] self.dds = self.ds.arr((RE - LE) / self.ActiveDimensions, "code_length") if self.ds.dimensionality < 2: self.dds[1] = 1.0 if self.ds.dimensionality < 3: self.dds[2] = 1.0 self.field_data["dx"], self.field_data["dy"], self.field_data["dz"] = self.dds def parse_line(line, grid): # grid is a dictionary splitup = line.strip().split() if chk23("vtk") in splitup: grid["vtk_version"] = str23(splitup[-1]) elif chk23("time=") in splitup: time_index = splitup.index(chk23("time=")) grid["time"] = float(str23(splitup[time_index + 1]).rstrip(",")) grid["level"] = int(str23(splitup[time_index + 3]).rstrip(",")) grid["domain"] = int(str23(splitup[time_index + 5]).rstrip(",")) elif chk23("DIMENSIONS") in splitup: grid["dimensions"] = np.array(str23(splitup[-3:]), dtype="int") elif chk23("ORIGIN") in splitup: grid["left_edge"] = np.array(str23(splitup[-3:]), dtype="float64") elif chk23("SPACING") in splitup: grid["dds"] = np.array(str23(splitup[-3:]), dtype="float64") elif chk23("CELL_DATA") in splitup or chk23("POINT_DATA") in splitup: grid["ncells"] = int(str23(splitup[-1])) elif chk23("SCALARS") in splitup: field = str23(splitup[1]) grid["read_field"] = field grid["read_type"] = "scalar" elif chk23("VECTORS") in splitup: field = str23(splitup[1]) grid["read_field"] = field grid["read_type"] = "vector" elif chk23("time") in splitup: time_index = splitup.index(chk23("time")) grid["time"] = float(str23(splitup[time_index + 1])) class AthenaHierarchy(GridIndex): grid = AthenaGrid _dataset_type = "athena" _data_file = None def __init__(self, ds, dataset_type="athena"): self.dataset = weakref.proxy(ds) self.directory = os.path.dirname(self.dataset.filename) self.dataset_type = dataset_type # for now, the index file is the dataset! self.index_filename = os.path.join(os.getcwd(), self.dataset.filename) self._fhandle = open(self.index_filename, "rb") GridIndex.__init__(self, ds, dataset_type) self._fhandle.close() def _detect_output_fields(self): field_map = {} f = open(self.index_filename, "rb") line = check_readline(f) chkwhile = chk23("") while line != chkwhile: splitup = line.strip().split() chkd = chk23("DIMENSIONS") chkc = chk23("CELL_DATA") chkp = chk23("POINT_DATA") if chkd in splitup: field = str23(splitup[-3:]) grid_dims = np.array(field, dtype="int64") line = check_readline(f) elif chkc in splitup or chkp in splitup: grid_ncells = int(str23(splitup[-1])) line = check_readline(f) if np.prod(grid_dims) != grid_ncells: grid_dims -= 1 grid_dims[grid_dims == 0] = 1 if np.prod(grid_dims) != grid_ncells: mylog.error( "product of dimensions %i not equal to number of cells %i", np.prod(grid_dims), grid_ncells, ) raise TypeError break else: line = check_readline(f) read_table = False read_table_offset = f.tell() while line != chkwhile: splitup = line.strip().split() chks = chk23("SCALARS") chkv = chk23("VECTORS") if chks in line and chks not in splitup: splitup = str23(line[line.find(chks) :].strip().split()) if chkv in line and chkv not in splitup: splitup = str23(line[line.find(chkv) :].strip().split()) if chks in splitup: field = ("athena", str23(splitup[1])) dtype = str23(splitup[-1]).lower() if not read_table: line = check_readline(f) # Read the lookup table line read_table = True field_map[field] = ("scalar", f.tell() - read_table_offset, dtype) read_table = False elif chkv in splitup: field = str23(splitup[1]) dtype = str23(splitup[-1]).lower() for ax in "xyz": field_map["athena", f"{field}_{ax}"] = ( "vector", f.tell() - read_table_offset, dtype, ) line = check_readline(f) f.close() self.field_list = list(field_map.keys()) self._field_map = field_map def _count_grids(self): self.num_grids = self.dataset.nvtk * self.dataset.nprocs def _parse_index(self): f = open(self.index_filename, "rb") grid = {} grid["read_field"] = None grid["read_type"] = None line = f.readline() while grid["read_field"] is None: parse_line(line, grid) if check_break(line): break line = f.readline() f.close() # It seems some datasets have a mismatch between ncells and # the actual grid dimensions. if np.prod(grid["dimensions"]) != grid["ncells"]: grid["dimensions"] -= 1 grid["dimensions"][grid["dimensions"] == 0] = 1 if np.prod(grid["dimensions"]) != grid["ncells"]: mylog.error( "product of dimensions %i not equal to number of cells %i", np.prod(grid["dimensions"]), grid["ncells"], ) raise TypeError # Need to determine how many grids: self.num_grids dataset_dir = os.path.dirname(self.index_filename) dname = os.path.split(self.index_filename)[-1] if dataset_dir.endswith("id0"): dname = "id0/" + dname dataset_dir = dataset_dir[:-3] gridlistread = sglob( os.path.join(dataset_dir, f"id*/{dname[4:-9]}-id*{dname[-9:]}") ) gridlistread.insert(0, self.index_filename) if "id0" in dname: gridlistread += sglob( os.path.join(dataset_dir, f"id*/lev*/{dname[4:-9]}*-lev*{dname[-9:]}") ) else: gridlistread += sglob( os.path.join(dataset_dir, f"lev*/{dname[:-9]}*-lev*{dname[-9:]}") ) ndots = dname.count(".") gridlistread = [ fn for fn in gridlistread if os.path.basename(fn).count(".") == ndots ] self.num_grids = len(gridlistread) dxs = [] levels = np.zeros(self.num_grids, dtype="int32") glis = np.empty((self.num_grids, 3), dtype="float64") gdds = np.empty((self.num_grids, 3), dtype="float64") gdims = np.ones_like(glis) j = 0 self.grid_filenames = gridlistread while j < (self.num_grids): f = open(gridlistread[j], "rb") gridread = {} gridread["read_field"] = None gridread["read_type"] = None line = f.readline() while gridread["read_field"] is None: parse_line(line, gridread) splitup = line.strip().split() if chk23("X_COORDINATES") in splitup: gridread["left_edge"] = np.zeros(3) gridread["dds"] = np.zeros(3) v = np.fromfile(f, dtype=">f8", count=2) gridread["left_edge"][0] = v[0] - 0.5 * (v[1] - v[0]) gridread["dds"][0] = v[1] - v[0] if chk23("Y_COORDINATES") in splitup: v = np.fromfile(f, dtype=">f8", count=2) gridread["left_edge"][1] = v[0] - 0.5 * (v[1] - v[0]) gridread["dds"][1] = v[1] - v[0] if chk23("Z_COORDINATES") in splitup: v = np.fromfile(f, dtype=">f8", count=2) gridread["left_edge"][2] = v[0] - 0.5 * (v[1] - v[0]) gridread["dds"][2] = v[1] - v[0] if check_break(line): break line = f.readline() f.close() levels[j] = gridread.get("level", 0) glis[j, 0] = gridread["left_edge"][0] glis[j, 1] = gridread["left_edge"][1] glis[j, 2] = gridread["left_edge"][2] # It seems some datasets have a mismatch between ncells and # the actual grid dimensions. if np.prod(gridread["dimensions"]) != gridread["ncells"]: gridread["dimensions"] -= 1 gridread["dimensions"][gridread["dimensions"] == 0] = 1 if np.prod(gridread["dimensions"]) != gridread["ncells"]: mylog.error( "product of dimensions %i not equal to number of cells %i", np.prod(gridread["dimensions"]), gridread["ncells"], ) raise TypeError gdims[j, 0] = gridread["dimensions"][0] gdims[j, 1] = gridread["dimensions"][1] gdims[j, 2] = gridread["dimensions"][2] # Setting dds=1 for non-active dimensions in 1D/2D datasets gridread["dds"][gridread["dimensions"] == 1] = 1.0 gdds[j, :] = gridread["dds"] j = j + 1 gres = glis + gdims * gdds # Now we convert the glis, which were left edges (floats), to indices # from the domain left edge. Then we do a bunch of fixing now that we # know the extent of all the grids. glis = np.round( (glis - self.dataset.domain_left_edge.ndarray_view()) / gdds ).astype("int64") new_dre = np.max(gres, axis=0) dre_units = self.dataset.domain_right_edge.uq self.dataset.domain_right_edge = np.round(new_dre, decimals=12) * dre_units self.dataset.domain_width = ( self.dataset.domain_right_edge - self.dataset.domain_left_edge ) self.dataset.domain_center = 0.5 * ( self.dataset.domain_left_edge + self.dataset.domain_right_edge ) domain_dimensions = np.round(self.dataset.domain_width / gdds[0]).astype( "int64" ) if self.dataset.dimensionality <= 2: domain_dimensions[2] = 1 if self.dataset.dimensionality == 1: domain_dimensions[1] = 1 self.dataset.domain_dimensions = domain_dimensions dle = self.dataset.domain_left_edge dre = self.dataset.domain_right_edge dx_root = ( self.dataset.domain_right_edge - self.dataset.domain_left_edge ) / self.dataset.domain_dimensions if self.dataset.nprocs > 1: gle_all = [] gre_all = [] shapes_all = [] levels_all = [] new_gridfilenames = [] file_offsets = [] read_dims = [] for i in range(levels.shape[0]): dx = dx_root / self.dataset.refine_by ** (levels[i]) gle_orig = self.ds.arr( np.round(dle + dx * glis[i], decimals=12), "code_length" ) gre_orig = self.ds.arr( np.round(gle_orig + dx * gdims[i], decimals=12), "code_length" ) bbox = np.array( [[le, re] for le, re in zip(gle_orig, gre_orig, strict=True)] ) psize = get_psize(self.ds.domain_dimensions, self.ds.nprocs) gle, gre, shapes, slices, _ = decompose_array(gdims[i], psize, bbox) gle_all += gle gre_all += gre shapes_all += shapes levels_all += [levels[i]] * self.dataset.nprocs new_gridfilenames += [self.grid_filenames[i]] * self.dataset.nprocs file_offsets += [ [slc[0].start, slc[1].start, slc[2].start] for slc in slices ] read_dims += [ np.array([gdims[i][0], gdims[i][1], shape[2]], dtype="int64") for shape in shapes ] self.num_grids *= self.dataset.nprocs self.grids = np.empty(self.num_grids, dtype="object") self.grid_filenames = new_gridfilenames self.grid_left_edge = self.ds.arr(gle_all, "code_length") self.grid_right_edge = self.ds.arr(gre_all, "code_length") self.grid_dimensions = np.array(list(shapes_all), dtype="int32") gdds = (self.grid_right_edge - self.grid_left_edge) / self.grid_dimensions glis = np.round( (self.grid_left_edge - self.ds.domain_left_edge) / gdds ).astype("int64") for i in range(self.num_grids): self.grids[i] = self.grid( i, self, levels_all[i], glis[i], shapes_all[i], file_offsets[i], read_dims[i], ) else: self.grids = np.empty(self.num_grids, dtype="object") for i in range(levels.shape[0]): self.grids[i] = self.grid( i, self, levels[i], glis[i], gdims[i], [0] * 3, gdims[i] ) dx = dx_root / self.dataset.refine_by ** (levels[i]) dxs.append(dx) dx = self.ds.arr(dxs, "code_length") self.grid_left_edge = self.ds.arr( np.round(dle + dx * glis, decimals=12), "code_length" ) self.grid_dimensions = gdims.astype("int32") self.grid_right_edge = self.ds.arr( np.round(self.grid_left_edge + dx * self.grid_dimensions, decimals=12), "code_length", ) if self.dataset.dimensionality <= 2: self.grid_right_edge[:, 2] = dre[2] if self.dataset.dimensionality == 1: self.grid_right_edge[:, 1:] = dre[1:] self.grid_particle_count = np.zeros([self.num_grids, 1], dtype="int64") def _populate_grid_objects(self): for g in self.grids: g._prepare_grid() g._setup_dx() self._reconstruct_parent_child() self.max_level = self.grid_levels.max() def _reconstruct_parent_child(self): mask = np.empty(len(self.grids), dtype="int32") mylog.debug("First pass; identifying child grids") for i, grid in enumerate(self.grids): get_box_grids_level( self.grid_left_edge[i, :], self.grid_right_edge[i, :], self.grid_levels[i].item() + 1, self.grid_left_edge, self.grid_right_edge, self.grid_levels, mask, ) grid.Children = [ g for g in self.grids[mask.astype("bool")] if g.Level == grid.Level + 1 ] mylog.debug("Second pass; identifying parents") for grid in self.grids: # Second pass for child in grid.Children: child.Parent.append(grid) def _get_grid_children(self, grid): mask = np.zeros(self.num_grids, dtype="bool") grids, grid_ind = self.get_box_grids(grid.LeftEdge, grid.RightEdge) mask[grid_ind] = True return [g for g in self.grids[mask] if g.Level == grid.Level + 1] def _chunk_io(self, dobj, cache=True, local_only=False): gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for subset in gobjs: yield YTDataChunk( dobj, "io", [subset], self._count_selection(dobj, [subset]), cache=cache ) class AthenaDataset(Dataset): _index_class = AthenaHierarchy _field_info_class = AthenaFieldInfo _dataset_type = "athena" def __init__( self, filename, dataset_type="athena", storage_filename=None, parameters=None, units_override=None, nprocs=1, unit_system="cgs", default_species_fields=None, magnetic_normalization="gaussian", ): self.fluid_types += ("athena",) self.nprocs = nprocs if parameters is None: parameters = {} self.specified_parameters = parameters.copy() if units_override is None: units_override = {} self._magnetic_factor = get_magnetic_normalization(magnetic_normalization) Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) if storage_filename is None: storage_filename = self.basename + ".yt" self.storage_filename = storage_filename # Unfortunately we now have to mandate that the index gets # instantiated so that we can make sure we have the correct left # and right domain edges. self.index def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ if "length_unit" not in self.units_override: self.no_cgs_equiv_length = True for unit, cgs in [("length", "cm"), ("time", "s"), ("mass", "g")]: # We set these to cgs for now, but they may have been overridden if getattr(self, unit + "_unit", None) is not None: continue mylog.warning("Assuming 1.0 = 1.0 %s", cgs) setattr(self, f"{unit}_unit", self.quan(1.0, cgs)) self.magnetic_unit = np.sqrt( self._magnetic_factor * self.mass_unit / (self.time_unit**2 * self.length_unit) ) self.magnetic_unit.convert_to_units("gauss") self.velocity_unit = self.length_unit / self.time_unit def _parse_parameter_file(self): self._handle = open(self.parameter_filename, "rb") # Read the start of a grid to get simulation parameters. grid = {} grid["read_field"] = None line = self._handle.readline() while grid["read_field"] is None: parse_line(line, grid) splitup = line.strip().split() if chk23("X_COORDINATES") in splitup: grid["left_edge"] = np.zeros(3) grid["dds"] = np.zeros(3) v = np.fromfile(self._handle, dtype=">f8", count=2) grid["left_edge"][0] = v[0] - 0.5 * (v[1] - v[0]) grid["dds"][0] = v[1] - v[0] if chk23("Y_COORDINATES") in splitup: v = np.fromfile(self._handle, dtype=">f8", count=2) grid["left_edge"][1] = v[0] - 0.5 * (v[1] - v[0]) grid["dds"][1] = v[1] - v[0] if chk23("Z_COORDINATES") in splitup: v = np.fromfile(self._handle, dtype=">f8", count=2) grid["left_edge"][2] = v[0] - 0.5 * (v[1] - v[0]) grid["dds"][2] = v[1] - v[0] if check_break(line): break line = self._handle.readline() self.domain_left_edge = grid["left_edge"] mylog.info( "Temporarily setting domain_right_edge = -domain_left_edge. " "This will be corrected automatically if it is not the case." ) self.domain_right_edge = -self.domain_left_edge self.domain_width = self.domain_right_edge - self.domain_left_edge domain_dimensions = np.round(self.domain_width / grid["dds"]).astype("int32") refine_by = None if refine_by is None: refine_by = 2 self.refine_by = refine_by dimensionality = 3 if grid["dimensions"][2] == 1: dimensionality = 2 if grid["dimensions"][1] == 1: dimensionality = 1 if dimensionality <= 2: domain_dimensions[2] = np.int32(1) if dimensionality == 1: domain_dimensions[1] = np.int32(1) if dimensionality != 3 and self.nprocs > 1: raise RuntimeError("Virtual grids are only supported for 3D outputs!") self.domain_dimensions = domain_dimensions self.dimensionality = dimensionality self.current_time = grid["time"] self.cosmological_simulation = False self.num_ghost_zones = 0 self.field_ordering = "fortran" self.boundary_conditions = [1] * 6 self._periodicity = tuple( self.specified_parameters.get("periodicity", (True, True, True)) ) if "gamma" in self.specified_parameters: self.gamma = float(self.specified_parameters["gamma"]) else: self.gamma = 5.0 / 3.0 dataset_dir = os.path.dirname(self.parameter_filename) dname = os.path.split(self.parameter_filename)[-1] if dataset_dir.endswith("id0"): dname = "id0/" + dname dataset_dir = dataset_dir[:-3] gridlistread = sglob( os.path.join(dataset_dir, f"id*/{dname[4:-9]}-id*{dname[-9:]}") ) if "id0" in dname: gridlistread += sglob( os.path.join(dataset_dir, f"id*/lev*/{dname[4:-9]}*-lev*{dname[-9:]}") ) else: gridlistread += sglob( os.path.join(dataset_dir, f"lev*/{dname[:-9]}*-lev*{dname[-9:]}") ) ndots = dname.count(".") gridlistread = [ fn for fn in gridlistread if os.path.basename(fn).count(".") == ndots ] self.nvtk = len(gridlistread) + 1 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 # Hardcode time conversion for now. self.parameters["Time"] = self.current_time # Hardcode for now until field staggering is supported. self.parameters["HydroMethod"] = 0 if "gamma" in self.specified_parameters: self.parameters["Gamma"] = self.specified_parameters["gamma"] else: self.parameters["Gamma"] = 5.0 / 3.0 self.geometry = Geometry(self.specified_parameters.get("geometry", "cartesian")) self._handle.close() self.mu = self.specified_parameters.get( "mu", compute_mu(self.default_species_fields) ) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".vtk"): return False with open(filename, "rb") as fh: if not re.match(b"# vtk DataFile Version \\d\\.\\d\n", fh.readline(256)): return False if ( re.search( b"at time= .*, level= \\d, domain= \\d\n", fh.readline(256), ) is None ): # vtk Dumps headers start with either "CONSERVED vars" or "PRIMITIVE vars", # while vtk output headers start with "Really cool Athena data", but # we will consider this an implementation detail and not attempt to # match it exactly here. # See Athena's user guide for reference # https://princetonuniversity.github.io/Athena-Cversion/AthenaDocsUGbtk return False return True @property def _skip_cache(self): return True def __str__(self): return self.basename.rsplit(".", 1)[0] yt-project-yt-f043ac8/yt/frontends/athena/definitions.py000066400000000000000000000001061510711153200234320ustar00rootroot00000000000000""" Various definitions for various other modules and routines """ yt-project-yt-f043ac8/yt/frontends/athena/fields.py000066400000000000000000000135661510711153200224030ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.utilities.physical_constants import kboltz, mh b_units = "code_magnetic" pres_units = "code_pressure" erg_units = "code_mass * (code_length/code_time)**2" rho_units = "code_mass / code_length**3" def velocity_field(comp): def _velocity(field, data): return data["athena", f"momentum_{comp}"] / data["athena", "density"] return _velocity class AthenaFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", ("code_mass/code_length**3", ["density"], None)), ("cell_centered_B_x", (b_units, [], None)), ("cell_centered_B_y", (b_units, [], None)), ("cell_centered_B_z", (b_units, [], None)), ("total_energy", ("code_pressure", ["total_energy_density"], None)), ( "gravitational_potential", ("code_velocity**2", ["gravitational_potential"], None), ), ) # In Athena, conservative or primitive variables may be written out. # By default, yt concerns itself with primitive variables. The following # field definitions allow for conversions to primitive variables in the # case that the file contains the conservative ones. def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases unit_system = self.ds.unit_system # Add velocity fields for comp in "xyz": vel_field = ("athena", f"velocity_{comp}") mom_field = ("athena", f"momentum_{comp}") if vel_field in self.field_list: self.add_output_field( vel_field, sampling_type="cell", units="code_length/code_time" ) self.alias( ("gas", f"velocity_{comp}"), vel_field, units=unit_system["velocity"], ) elif mom_field in self.field_list: self.add_output_field( mom_field, sampling_type="cell", units="code_mass/code_time/code_length**2", ) self.alias( ("gas", f"momentum_density_{comp}"), mom_field, units=unit_system["density"] * unit_system["velocity"], ) self.add_field( ("gas", f"velocity_{comp}"), sampling_type="cell", function=velocity_field(comp), units=unit_system["velocity"], ) # Add pressure, energy, and temperature fields def eint_from_etot(data): eint = ( data["athena", "total_energy"] - data["gas", "kinetic_energy_density"] ) if ("athena", "cell_centered_B_x") in self.field_list: eint -= data["gas", "magnetic_energy_density"] return eint def etot_from_pres(data): etot = data["athena", "pressure"] / (data.ds.gamma - 1.0) etot += data["gas", "kinetic_energy_density"] if ("athena", "cell_centered_B_x") in self.field_list: etot += data["gas", "magnetic_energy_density"] return etot if ("athena", "pressure") in self.field_list: self.add_output_field( ("athena", "pressure"), sampling_type="cell", units=pres_units ) self.alias( ("gas", "pressure"), ("athena", "pressure"), units=unit_system["pressure"], ) def _specific_thermal_energy(field, data): return ( data["athena", "pressure"] / (data.ds.gamma - 1.0) / data["athena", "density"] ) self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) def _specific_total_energy(field, data): return etot_from_pres(data) / data["athena", "density"] self.add_field( ("gas", "specific_total_energy"), sampling_type="cell", function=_specific_total_energy, units=unit_system["specific_energy"], ) elif ("athena", "total_energy") in self.field_list: self.add_output_field( ("athena", "total_energy"), sampling_type="cell", units=pres_units ) def _specific_thermal_energy(field, data): return eint_from_etot(data) / data["athena", "density"] self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) def _specific_total_energy(field, data): return data["athena", "total_energy"] / data["athena", "density"] self.add_field( ("gas", "specific_total_energy"), sampling_type="cell", function=_specific_total_energy, units=unit_system["specific_energy"], ) # Add temperature field def _temperature(field, data): return ( data.ds.mu * data["gas", "pressure"] / data["gas", "density"] * mh / kboltz ) self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) setup_magnetic_field_aliases( self, "athena", [f"cell_centered_B_{ax}" for ax in "xyz"] ) yt-project-yt-f043ac8/yt/frontends/athena/io.py000066400000000000000000000105631510711153200215360ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.utilities.io_handler import BaseIOHandler from .data_structures import chk23 float_size = {"float": np.dtype(">f4").itemsize, "double": np.dtype(">f8").itemsize} axis_list = ["_x", "_y", "_z"] class IOHandlerAthena(BaseIOHandler): _dataset_type = "athena" _offset_string = "data:offsets=0" _data_string = "data:datatype=0" _read_table_offset = None def _read_field_names(self, grid): pass def _read_chunk_data(self, chunk, fields): data = {} if len(chunk.objs) == 0: return data for grid in chunk.objs: if grid.filename is None: continue f = open(grid.filename, "rb") data[grid.id] = {} grid_dims = grid.ActiveDimensions read_dims = grid.read_dims.astype("int64") grid_ncells = np.prod(read_dims) grid0_ncells = np.prod(grid.index.grids[0].read_dims) read_table_offset = get_read_table_offset(f) for field in fields: ftype, offsetr, dtype = grid.index._field_map[field] if grid_ncells != grid0_ncells: offset = offsetr + ( (grid_ncells - grid0_ncells) * (offsetr // grid0_ncells) ) if grid_ncells == grid0_ncells: offset = offsetr offset = int(offset) # Casting to be certain. file_offset = ( grid.file_offset[2] * read_dims[0] * read_dims[1] * float_size[dtype] ) xread = slice(grid.file_offset[0], grid.file_offset[0] + grid_dims[0]) yread = slice(grid.file_offset[1], grid.file_offset[1] + grid_dims[1]) f.seek(read_table_offset + offset + file_offset) if dtype == "float": dt = ">f4" elif dtype == "double": dt = ">f8" if ftype == "scalar": f.seek(read_table_offset + offset + file_offset) v = np.fromfile(f, dtype=dt, count=grid_ncells).reshape( read_dims, order="F" ) if ftype == "vector": vec_offset = axis_list.index(field[-1][-2:]) f.seek(read_table_offset + offset + 3 * file_offset) v = np.fromfile(f, dtype=dt, count=3 * grid_ncells) v = v[vec_offset::3].reshape(read_dims, order="F") if grid.ds.field_ordering == 1: data[grid.id][field] = v[xread, yread, :].T.astype("float64") else: data[grid.id][field] = v[xread, yread, :].astype("float64") f.close() return data def _read_data_slice(self, grid, field, axis, coord): sl = [slice(None), slice(None), slice(None)] sl[axis] = slice(coord, coord + 1) if grid.ds.field_ordering == 1: sl.reverse() return self._read_data_set(grid, field)[tuple(sl)] def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) if any((ftype != "athena" for ftype, fname in fields)): raise NotImplementedError rv = {} for field in fields: rv[field] = np.empty(size, dtype="float64") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) ind = 0 for chunk in chunks: data = self._read_chunk_data(chunk, fields) for g in chunk.objs: for field in fields: ftype, fname = field ds = data[g.id].pop(field) nd = g.select(selector, ds, rv[field], ind) # caches ind += nd data.pop(g.id) return rv def get_read_table_offset(f): line = f.readline() while True: splitup = line.strip().split() chkc = chk23("CELL_DATA") chkp = chk23("POINT_DATA") if chkc in splitup or chkp in splitup: f.readline() read_table_offset = f.tell() break line = f.readline() return read_table_offset yt-project-yt-f043ac8/yt/frontends/athena/misc.py000066400000000000000000000000001510711153200220430ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena/tests/000077500000000000000000000000001510711153200217125ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena/tests/__init__.py000066400000000000000000000000001510711153200240110ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena/tests/test_outputs.py000066400000000000000000000116541510711153200250550ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.frontends.athena.api import AthenaDataset from yt.loaders import load from yt.testing import ( assert_allclose_units, disable_dataset_cache, requires_file, ) from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) _fields_cloud = ( ("athena", "scalar[0]"), ("gas", "density"), ("gas", "total_energy_density"), ) cloud = "ShockCloud/id0/Cloud.0050.vtk" @requires_ds(cloud) def test_cloud(): ds = data_dir_load(cloud) assert_equal(str(ds), "Cloud.0050") for test in small_patch_amr(ds, _fields_cloud): test_cloud.__name__ = test.description yield test _fields_blast = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ) blast = "MHDBlast/id0/Blast.0100.vtk" @requires_ds(blast) def test_blast(): ds = data_dir_load(blast) assert_equal(str(ds), "Blast.0100") for test in small_patch_amr(ds, _fields_blast): test_blast.__name__ = test.description yield test uo_blast = { "length_unit": (1.0, "pc"), "mass_unit": (2.38858753789e-24, "g/cm**3*pc**3"), "time_unit": (1.0, "s*pc/km"), } @requires_file(blast) def test_blast_override(): # verify that overriding units causes derived unit values to be updated. # see issue #1259 ds = load(blast, units_override=uo_blast) assert_equal(float(ds.magnetic_unit.in_units("gauss")), 5.47867467969813e-07) uo_stripping = { "time_unit": 3.086e14, "length_unit": 8.0236e22, "mass_unit": 9.999e-30 * 8.0236e22**3, } _fields_stripping = ( ("gas", "temperature"), ("gas", "density"), ("athena", "specific_scalar[0]"), ) stripping = "RamPressureStripping/id0/rps.0062.vtk" @requires_ds(stripping, big_data=True) def test_stripping(): ds = data_dir_load(stripping, kwargs={"units_override": uo_stripping}) assert_equal(str(ds), "rps.0062") for test in small_patch_amr(ds, _fields_stripping): test_stripping.__name__ = test.description yield test sloshing = "MHDSloshing/virgo_low_res.0054.vtk" uo_sloshing = { "length_unit": (1.0, "Mpc"), "time_unit": (1.0, "Myr"), "mass_unit": (1.0e14, "Msun"), } @requires_file(sloshing) @disable_dataset_cache def test_nprocs(): ds1 = load(sloshing, units_override=uo_sloshing) sp1 = ds1.sphere("c", (100.0, "kpc")) prj1 = ds1.proj(("gas", "density"), 0) ds2 = load(sloshing, units_override=uo_sloshing, nprocs=8) sp2 = ds2.sphere("c", (100.0, "kpc")) prj2 = ds1.proj(("gas", "density"), 0) assert_equal( sp1.quantities.extrema(("gas", "pressure")), sp2.quantities.extrema(("gas", "pressure")), ) assert_allclose_units( sp1.quantities.total_quantity(("gas", "pressure")), sp2.quantities.total_quantity(("gas", "pressure")), ) for ax in "xyz": assert_equal( sp1.quantities.extrema(("gas", f"velocity_{ax}")), sp2.quantities.extrema(("gas", f"velocity_{ax}")), ) assert_allclose_units( sp1.quantities.bulk_velocity(), sp2.quantities.bulk_velocity() ) assert_equal(prj1["gas", "density"], prj2["gas", "density"]) @requires_file(cloud) def test_AthenaDataset(): assert isinstance(data_dir_load(cloud), AthenaDataset) @requires_file(sloshing) @disable_dataset_cache def test_mag_factor(): ds1 = load(sloshing, units_override=uo_sloshing, magnetic_normalization="gaussian") assert ds1.magnetic_unit == np.sqrt( 4.0 * np.pi * ds1.mass_unit / (ds1.time_unit**2 * ds1.length_unit) ) sp1 = ds1.sphere("c", (100.0, "kpc")) pB1a = ( sp1["athena", "cell_centered_B_x"] ** 2 + sp1["athena", "cell_centered_B_y"] ** 2 + sp1["athena", "cell_centered_B_z"] ** 2 ) / (8.0 * np.pi) pB1b = ( sp1["gas", "magnetic_field_x"] ** 2 + sp1["gas", "magnetic_field_y"] ** 2 + sp1["gas", "magnetic_field_z"] ** 2 ) / (8.0 * np.pi) pB1a.convert_to_units("dyn/cm**2") pB1b.convert_to_units("dyn/cm**2") assert_allclose_units(pB1a, pB1b) assert_allclose_units(pB1a, sp1["magnetic_pressure"]) ds2 = load( sloshing, units_override=uo_sloshing, magnetic_normalization="lorentz_heaviside" ) assert ds2.magnetic_unit == np.sqrt( ds2.mass_unit / (ds2.time_unit**2 * ds2.length_unit) ) sp2 = ds2.sphere("c", (100.0, "kpc")) pB2a = ( sp2["athena", "cell_centered_B_x"] ** 2 + sp2["athena", "cell_centered_B_y"] ** 2 + sp2["athena", "cell_centered_B_z"] ** 2 ) / 2.0 pB2b = ( sp2["gas", "magnetic_field_x"] ** 2 + sp2["gas", "magnetic_field_y"] ** 2 + sp2["gas", "magnetic_field_z"] ** 2 ) / 2.0 pB2a.convert_to_units("dyn/cm**2") pB2b.convert_to_units("dyn/cm**2") assert_allclose_units(pB2a, pB2b) assert_allclose_units(pB2a, sp2["magnetic_pressure"]) assert_allclose_units(pB1a, pB2a) yt-project-yt-f043ac8/yt/frontends/athena_pp/000077500000000000000000000000001510711153200212475ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena_pp/__init__.py000066400000000000000000000000001510711153200233460ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena_pp/api.py000066400000000000000000000002521510711153200223710ustar00rootroot00000000000000from . import tests from .data_structures import AthenaPPDataset, AthenaPPGrid, AthenaPPHierarchy from .fields import AthenaPPFieldInfo from .io import IOHandlerAthenaPP yt-project-yt-f043ac8/yt/frontends/athena_pp/data_structures.py000066400000000000000000000234771510711153200250520ustar00rootroot00000000000000import os import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.index_subobjects.stretched_grid import StretchedGrid from yt.data_objects.static_output import Dataset from yt.fields.magnetic_field import get_magnetic_normalization from yt.funcs import mylog from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.chemical_formulas import compute_mu from yt.utilities.file_handler import HDF5FileHandler from .fields import AthenaPPFieldInfo geom_map = { "cartesian": "cartesian", "cylindrical": "cylindrical", "spherical_polar": "spherical", "minkowski": "cartesian", "tilted": "cartesian", "sinusoidal": "cartesian", "schwarzschild": "spherical", "kerr-schild": "spherical", } class AthenaPPGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level def _setup_dx(self): # So first we figure out what the index is. We don't assume # that dx=dy=dz , at least here. We probably do elsewhere. id = self.id - self._id_offset LE, RE = self.index.grid_left_edge[id, :], self.index.grid_right_edge[id, :] self.dds = self.ds.arr((RE - LE) / self.ActiveDimensions, "code_length") if self.ds.dimensionality < 2: self.dds[1] = 1.0 if self.ds.dimensionality < 3: self.dds[2] = 1.0 self.field_data["dx"], self.field_data["dy"], self.field_data["dz"] = self.dds class AthenaPPStretchedGrid(StretchedGrid): _id_offset = 0 def __init__(self, id, cell_widths, index, level): super().__init__(id, cell_widths, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level class AthenaPPHierarchy(GridIndex): _dataset_type = "athena_pp" _data_file = None def __init__(self, ds, dataset_type="athena_pp"): self.dataset = weakref.proxy(ds) self.grid = AthenaPPStretchedGrid if self.dataset._nonuniform else AthenaPPGrid self.directory = os.path.dirname(self.dataset.filename) self.dataset_type = dataset_type # for now, the index file is the dataset! self.index_filename = self.dataset.filename self._handle = ds._handle GridIndex.__init__(self, ds, dataset_type) def _detect_output_fields(self): self.field_list = [("athena_pp", k) for k in self.dataset._field_map] def _count_grids(self): self.num_grids = self._handle.attrs["NumMeshBlocks"] def _parse_index(self): num_grids = self._handle.attrs["NumMeshBlocks"] self.grid_left_edge = np.zeros((num_grids, 3), dtype="float64") self.grid_right_edge = np.zeros((num_grids, 3), dtype="float64") self.grid_dimensions = np.zeros((num_grids, 3), dtype="int32") # TODO: In an unlikely case this would use too much memory, implement # chunked read along 1 dim x = self._handle["x1f"][:, :].astype("float64") y = self._handle["x2f"][:, :].astype("float64") z = self._handle["x3f"][:, :].astype("float64") dx = np.diff(x, axis=1) dy = np.diff(y, axis=1) dz = np.diff(z, axis=1) mesh_block_size = self._handle.attrs["MeshBlockSize"] for i in range(num_grids): self.grid_left_edge[i] = np.array([x[i, 0], y[i, 0], z[i, 0]]) self.grid_right_edge[i] = np.array([x[i, -1], y[i, -1], z[i, -1]]) self.grid_dimensions[i] = mesh_block_size levels = self._handle["Levels"][:] self.grid_left_edge = self.ds.arr(self.grid_left_edge, "code_length") self.grid_right_edge = self.ds.arr(self.grid_right_edge, "code_length") self.grids = np.empty(self.num_grids, dtype="object") for i in range(num_grids): if self.dataset._nonuniform: self.grids[i] = self.grid(i, [dx[i], dy[i], dz[i]], self, levels[i]) else: self.grids[i] = self.grid(i, self, levels[i]) if self.dataset.dimensionality <= 2: self.grid_right_edge[:, 2] = self.dataset.domain_right_edge[2] if self.dataset.dimensionality == 1: self.grid_right_edge[:, 1:] = self.dataset.domain_right_edge[1:] self.grid_particle_count = np.zeros([self.num_grids, 1], dtype="int64") def _populate_grid_objects(self): for g in self.grids: g._prepare_grid() g._setup_dx() self.max_level = self._handle.attrs["MaxLevel"] class AthenaPPDataset(Dataset): _field_info_class = AthenaPPFieldInfo _dataset_type = "athena_pp" _index_class = AthenaPPHierarchy def __init__( self, filename, dataset_type="athena_pp", storage_filename=None, parameters=None, units_override=None, unit_system="code", default_species_fields=None, magnetic_normalization="gaussian", ): self.fluid_types += ("athena_pp",) if parameters is None: parameters = {} self.specified_parameters = parameters if units_override is None: units_override = {} self._handle = HDF5FileHandler(filename) xrat = self._handle.attrs["RootGridX1"][2] yrat = self._handle.attrs["RootGridX2"][2] zrat = self._handle.attrs["RootGridX3"][2] self._nonuniform = xrat != 1.0 or yrat != 1.0 or zrat != 1.0 self._magnetic_factor = get_magnetic_normalization(magnetic_normalization) geom = self._handle.attrs["Coordinates"].decode("utf-8") self.geometry = Geometry(geom_map[geom]) if self.geometry == "cylindrical": axis_order = ("r", "theta", "z") else: axis_order = None Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, axis_order=axis_order, ) if storage_filename is None: storage_filename = self.basename + ".yt" self.storage_filename = storage_filename def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ if "length_unit" not in self.units_override: self.no_cgs_equiv_length = True for unit, cgs in [ ("length", "cm"), ("time", "s"), ("mass", "g"), ("temperature", "K"), ]: # We set these to cgs for now, but they may have been overridden if getattr(self, unit + "_unit", None) is not None: continue mylog.warning("Assuming 1.0 = 1.0 %s", cgs) setattr(self, f"{unit}_unit", self.quan(1.0, cgs)) self.magnetic_unit = np.sqrt( self._magnetic_factor * self.mass_unit / (self.time_unit**2 * self.length_unit) ) self.magnetic_unit.convert_to_units("gauss") self.velocity_unit = self.length_unit / self.time_unit def _parse_parameter_file(self): xmin, xmax = self._handle.attrs["RootGridX1"][:2] ymin, ymax = self._handle.attrs["RootGridX2"][:2] zmin, zmax = self._handle.attrs["RootGridX3"][:2] self.domain_left_edge = np.array([xmin, ymin, zmin], dtype="float64") self.domain_right_edge = np.array([xmax, ymax, zmax], dtype="float64") self.domain_width = self.domain_right_edge - self.domain_left_edge self.domain_dimensions = self._handle.attrs["RootGridSize"] self._field_map = {} k = 0 for dname, num_var in zip( self._handle.attrs["DatasetNames"], self._handle.attrs["NumVariables"], strict=True, ): for j in range(num_var): fname = self._handle.attrs["VariableNames"][k].decode("ascii", "ignore") self._field_map[fname] = (dname.decode("ascii", "ignore"), j) k += 1 self.refine_by = 2 dimensionality = 3 if self.domain_dimensions[2] == 1: dimensionality = 2 if self.domain_dimensions[1] == 1: dimensionality = 1 self.dimensionality = dimensionality self.current_time = self._handle.attrs["Time"] self.cosmological_simulation = False self.num_ghost_zones = 0 self.field_ordering = "fortran" self.boundary_conditions = [1] * 6 self._periodicity = tuple( self.specified_parameters.get("periodicity", (True, True, True)) ) if "gamma" in self.specified_parameters: self.gamma = float(self.specified_parameters["gamma"]) else: self.gamma = 5.0 / 3.0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 # Hardcode time conversion for now. self.parameters["Time"] = self.current_time # Hardcode for now until field staggering is supported. self.parameters["HydroMethod"] = 0 if "gamma" in self.specified_parameters: self.parameters["Gamma"] = self.specified_parameters["gamma"] else: self.parameters["Gamma"] = 5.0 / 3.0 self.mu = self.specified_parameters.get( "mu", compute_mu(self.default_species_fields) ) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return filename.endswith(".athdf") @property def _skip_cache(self): return True def __str__(self): return self.basename.rsplit(".", 1)[0] yt-project-yt-f043ac8/yt/frontends/athena_pp/definitions.py000066400000000000000000000001061510711153200241310ustar00rootroot00000000000000""" Various definitions for various other modules and routines """ yt-project-yt-f043ac8/yt/frontends/athena_pp/fields.py000066400000000000000000000101231510711153200230640ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.utilities.physical_constants import kboltz, mh b_units = "code_magnetic" pres_units = "code_mass/(code_length*code_time**2)" rho_units = "code_mass / code_length**3" vel_units = "code_length / code_time" def velocity_field(j): def _velocity(field, data): return data["athena_pp", f"mom{j}"] / data["athena_pp", "dens"] return _velocity class AthenaPPFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("rho", (rho_units, ["density"], None)), ("dens", (rho_units, ["density"], None)), ("Bcc1", (b_units, [], None)), ("Bcc2", (b_units, [], None)), ("Bcc3", (b_units, [], None)), ) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases unit_system = self.ds.unit_system # Add velocity fields vel_prefix = "velocity" for i, comp in enumerate(self.ds.coordinates.axis_order): vel_field = ("athena_pp", "vel%d" % (i + 1)) mom_field = ("athena_pp", "mom%d" % (i + 1)) if vel_field in self.field_list: self.add_output_field( vel_field, sampling_type="cell", units="code_length/code_time" ) self.alias( ("gas", f"{vel_prefix}_{comp}"), vel_field, units=unit_system["velocity"], ) elif mom_field in self.field_list: self.add_output_field( mom_field, sampling_type="cell", units="code_mass/code_time/code_length**2", ) self.add_field( ("gas", f"{vel_prefix}_{comp}"), sampling_type="cell", function=velocity_field(i + 1), units=unit_system["velocity"], ) # Figure out thermal energy field if ("athena_pp", "press") in self.field_list: self.add_output_field( ("athena_pp", "press"), sampling_type="cell", units=pres_units ) self.alias( ("gas", "pressure"), ("athena_pp", "press"), units=unit_system["pressure"], ) def _specific_thermal_energy(field, data): return ( data["athena_pp", "press"] / (data.ds.gamma - 1.0) / data["athena_pp", "rho"] ) self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) elif ("athena_pp", "Etot") in self.field_list: self.add_output_field( ("athena_pp", "Etot"), sampling_type="cell", units=pres_units ) def _specific_thermal_energy(field, data): eint = data["athena_pp", "Etot"] - data["gas", "kinetic_energy_density"] if ("athena_pp", "B1") in self.field_list: eint -= data["gas", "magnetic_energy_density"] return eint / data["athena_pp", "dens"] self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) # Add temperature field def _temperature(field, data): return ( (data["gas", "pressure"] / data["gas", "density"]) * data.ds.mu * mh / kboltz ) self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) setup_magnetic_field_aliases( self, "athena_pp", ["Bcc%d" % ax for ax in (1, 2, 3)] ) yt-project-yt-f043ac8/yt/frontends/athena_pp/io.py000066400000000000000000000052311510711153200222310ustar00rootroot00000000000000from itertools import groupby import numpy as np from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog # http://stackoverflow.com/questions/2361945/detecting-consecutive-integers-in-a-list def grid_sequences(grids): g_iter = sorted(grids, key=lambda g: g.id) for _, g in groupby(enumerate(g_iter), lambda i_x1: i_x1[0] - i_x1[1].id): seq = [v[1] for v in g] yield seq class IOHandlerAthenaPP(BaseIOHandler): _particle_reader = False _dataset_type = "athena_pp" def __init__(self, ds): super().__init__(ds) self._handle = ds._handle def _read_particles( self, fields_to_read, type, args, grid_list, count_list, conv_factors ): pass def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) if any((ftype != "athena_pp" for ftype, fname in fields)): raise NotImplementedError f = self._handle rv = {} for field in fields: # Always use *native* 64-bit float. rv[field] = np.empty(size, dtype="=f8") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [f2 for f1, f2 in fields], ng, ) last_dname = None for field in fields: ftype, fname = field dname, fdi = self.ds._field_map[fname] if dname != last_dname: ds = f[f"/{dname}"] ind = 0 for chunk in chunks: for gs in grid_sequences(chunk.objs): start = gs[0].id - gs[0]._id_offset end = gs[-1].id - gs[-1]._id_offset + 1 data = ds[fdi, start:end, :, :, :].transpose() for i, g in enumerate(gs): ind += g.select(selector, data[..., i], rv[field], ind) last_dname = dname return rv def _read_chunk_data(self, chunk, fields): f = self._handle rv = {} for g in chunk.objs: rv[g.id] = {} if len(fields) == 0: return rv for field in fields: ftype, fname = field dname, fdi = self.ds._field_map[fname] ds = f[f"/{dname}"] for gs in grid_sequences(chunk.objs): start = gs[0].id - gs[0]._id_offset end = gs[-1].id - gs[-1]._id_offset + 1 data = ds[fdi, start:end, :, :, :].transpose() for i, g in enumerate(gs): rv[g.id][field] = np.asarray(data[..., i], "=f8") return rv yt-project-yt-f043ac8/yt/frontends/athena_pp/misc.py000066400000000000000000000000001510711153200225420ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena_pp/tests/000077500000000000000000000000001510711153200224115ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena_pp/tests/__init__.py000066400000000000000000000000001510711153200245100ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/athena_pp/tests/test_outputs.py000066400000000000000000000074131510711153200255520ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_allclose, assert_equal from yt.frontends.athena_pp.api import AthenaPPDataset from yt.loaders import load from yt.testing import ( assert_allclose_units, disable_dataset_cache, requires_file, units_override_check, ) from yt.units import dimensions from yt.utilities.answer_testing.framework import ( GenericArrayTest, data_dir_load, requires_ds, small_patch_amr, ) _fields_disk = ("density", "velocity_r") disk = "KeplerianDisk/disk.out1.00000.athdf" @requires_ds(disk) def test_disk(): ds = data_dir_load(disk) assert_equal(str(ds), "disk.out1.00000") dd = ds.all_data() vol = (ds.domain_right_edge[0] ** 3 - ds.domain_left_edge[0] ** 3) / 3.0 vol *= np.cos(ds.domain_left_edge[1]) - np.cos(ds.domain_right_edge[1]) vol *= ds.domain_right_edge[2].v - ds.domain_left_edge[2].v assert_allclose(dd.quantities.total_quantity(("gas", "cell_volume")), vol) def field_func(field): return dd[field] for field in _fields_disk: yield GenericArrayTest(ds, field_func, args=[field]) _fields_AM06 = ("temperature", "density", "velocity_magnitude", "magnetic_field_x") AM06 = "AM06/AM06.out1.00400.athdf" @requires_ds(AM06) def test_AM06(): ds = data_dir_load(AM06) assert_equal(str(ds), "AM06.out1.00400") for test in small_patch_amr(ds, _fields_AM06): test_AM06.__name__ = test.description yield test uo_AM06 = { "length_unit": (1.0, "kpc"), "mass_unit": (1.0, "Msun"), "time_unit": (1.0, "Myr"), } @requires_file(AM06) def test_AM06_override(): # verify that overriding units causes derived unit values to be updated. # see issue #1259 ds = load(AM06, units_override=uo_AM06) assert_equal(float(ds.magnetic_unit.in_units("gauss")), 9.01735778342523e-08) @requires_file(AM06) def test_units_override(): units_override_check(AM06) @requires_file(AM06) def test_AthenaPPDataset(): assert isinstance(data_dir_load(AM06), AthenaPPDataset) @requires_file(AM06) def test_magnetic_units(): ds = load(AM06, unit_system="code") assert ds.magnetic_unit.units.dimensions == dimensions.magnetic_field_cgs assert (ds.magnetic_unit**2).units.dimensions == dimensions.pressure @requires_file(AM06) @disable_dataset_cache def test_mag_factor(): ds1 = load(AM06, units_override=uo_AM06, magnetic_normalization="gaussian") assert ds1.magnetic_unit == np.sqrt( 4.0 * np.pi * ds1.mass_unit / (ds1.time_unit**2 * ds1.length_unit) ) sp1 = ds1.sphere("c", (100.0, "kpc")) pB1a = ( sp1["athena_pp", "Bcc1"] ** 2 + sp1["athena_pp", "Bcc2"] ** 2 + sp1["athena_pp", "Bcc3"] ** 2 ) / (8.0 * np.pi) pB1b = ( sp1["gas", "magnetic_field_x"] ** 2 + sp1["gas", "magnetic_field_y"] ** 2 + sp1["gas", "magnetic_field_z"] ** 2 ) / (8.0 * np.pi) pB1a.convert_to_units("dyn/cm**2") pB1b.convert_to_units("dyn/cm**2") assert_allclose_units(pB1a, pB1b) assert_allclose_units(pB1a, sp1["magnetic_pressure"]) ds2 = load(AM06, units_override=uo_AM06, magnetic_normalization="lorentz_heaviside") assert ds2.magnetic_unit == np.sqrt( ds2.mass_unit / (ds2.time_unit**2 * ds2.length_unit) ) sp2 = ds2.sphere("c", (100.0, "kpc")) pB2a = ( sp2["athena_pp", "Bcc1"] ** 2 + sp2["athena_pp", "Bcc2"] ** 2 + sp2["athena_pp", "Bcc3"] ** 2 ) / 2.0 pB2b = ( sp2["gas", "magnetic_field_x"] ** 2 + sp2["gas", "magnetic_field_y"] ** 2 + sp2["gas", "magnetic_field_z"] ** 2 ) / 2.0 pB2a.convert_to_units("dyn/cm**2") pB2b.convert_to_units("dyn/cm**2") assert_allclose_units(pB2a, pB2b) assert_allclose_units(pB2a, sp2["magnetic_pressure"]) assert_allclose_units(pB1a, pB2a) yt-project-yt-f043ac8/yt/frontends/boxlib/000077500000000000000000000000001510711153200205675ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/boxlib/__init__.py000066400000000000000000000000001510711153200226660ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/boxlib/_deprecation.py000066400000000000000000000007321510711153200235770ustar00rootroot00000000000000from yt._maintenance.deprecation import issue_deprecation_warning, warnings def boxlib_deprecation(): with warnings.catch_warnings(): warnings.simplefilter("always") issue_deprecation_warning( "The historic 'boxlib' frontend is \n" "deprecated as it has been renamed 'amrex'. " "Existing and future work should instead reference the 'amrex' frontend.", stacklevel=4, since="4.4.0", ) yt-project-yt-f043ac8/yt/frontends/boxlib/api.py000066400000000000000000000010051510711153200217060ustar00rootroot00000000000000from ..amrex import tests from ..amrex import data_structures from ..amrex.data_structures import ( AMReXDataset, AMReXHierarchy, BoxlibDataset, BoxlibGrid, BoxlibHierarchy, CastroDataset, MaestroDataset, NyxDataset, NyxHierarchy, OrionDataset, OrionHierarchy, WarpXDataset, WarpXHierarchy, ) from ..amrex.fields import ( BoxlibFieldInfo, CastroFieldInfo, MaestroFieldInfo, NyxFieldInfo, WarpXFieldInfo, ) from ..amrex.io import IOHandlerBoxlib yt-project-yt-f043ac8/yt/frontends/boxlib/data_structures/000077500000000000000000000000001510711153200240035ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/boxlib/data_structures/__init__.py000066400000000000000000000005401510711153200261130ustar00rootroot00000000000000from ...amrex.data_structures import ( AMReXDataset, AMReXHierarchy, BoxlibDataset, BoxlibGrid, BoxlibHierarchy, CastroDataset, MaestroDataset, NyxDataset, NyxHierarchy, OrionDataset, OrionHierarchy, WarpXDataset, WarpXHierarchy, ) from .._deprecation import boxlib_deprecation boxlib_deprecation() yt-project-yt-f043ac8/yt/frontends/boxlib/fields/000077500000000000000000000000001510711153200220355ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/boxlib/fields/__init__.py000066400000000000000000000003121510711153200241420ustar00rootroot00000000000000from ...amrex.fields import ( BoxlibFieldInfo, CastroFieldInfo, MaestroFieldInfo, NyxFieldInfo, WarpXFieldInfo, ) from .._deprecation import boxlib_deprecation boxlib_deprecation() yt-project-yt-f043ac8/yt/frontends/boxlib/io/000077500000000000000000000000001510711153200211765ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/boxlib/io/__init__.py000066400000000000000000000001541510711153200233070ustar00rootroot00000000000000from ...amrex.io import IOHandlerBoxlib from .._deprecation import boxlib_deprecation boxlib_deprecation() yt-project-yt-f043ac8/yt/frontends/boxlib/tests/000077500000000000000000000000001510711153200217315ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/boxlib/tests/__init__.py000066400000000000000000000000331510711153200240360ustar00rootroot00000000000000from ...amrex import tests yt-project-yt-f043ac8/yt/frontends/boxlib/tests/test_boxlib_deprecation.py000066400000000000000000000012561510711153200272020ustar00rootroot00000000000000from importlib import import_module, reload from yt._maintenance.deprecation import warnings def test_imports(): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") for index, mname in enumerate(["data_structures", "fields", "io"]): mod_name = import_module("yt.frontends.boxlib." + mname) if len(w) != index + 1: reload(mod_name) assert len(w) == 3 and all( [ issubclass(w[0].category, DeprecationWarning), issubclass(w[1].category, DeprecationWarning), issubclass(w[2].category, DeprecationWarning), ] ) yt-project-yt-f043ac8/yt/frontends/boxlib/tests/test_outputs.py000066400000000000000000000323611510711153200250720ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_allclose, assert_equal from yt.frontends.boxlib.api import ( AMReXDataset, CastroDataset, MaestroDataset, NyxDataset, OrionDataset, WarpXDataset, ) from yt.loaders import load from yt.testing import ( disable_dataset_cache, requires_file, units_override_check, ) from yt.utilities.answer_testing.framework import ( GridValuesTest, data_dir_load, requires_ds, small_patch_amr, ) # We don't do anything needing ghost zone generation right now, because these # are non-periodic datasets. _orion_fields = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ) _nyx_fields = ( ("boxlib", "Ne"), ("boxlib", "Temp"), ("boxlib", "particle_mass_density"), ) _warpx_fields = (("mesh", "Ex"), ("mesh", "By"), ("mesh", "jz")) _castro_fields = ( ("boxlib", "Temp"), ("gas", "density"), ("boxlib", "particle_count"), ) radadvect = "RadAdvect/plt00000" @requires_ds(radadvect) def test_radadvect(): ds = data_dir_load(radadvect) assert_equal(str(ds), "plt00000") for test in small_patch_amr(ds, _orion_fields): test_radadvect.__name__ = test.description yield test rt = "RadTube/plt00500" @requires_ds(rt) def test_radtube(): ds = data_dir_load(rt) assert_equal(str(ds), "plt00500") for test in small_patch_amr(ds, _orion_fields): test_radtube.__name__ = test.description yield test star = "StarParticles/plrd01000" @requires_ds(star) def test_star(): ds = data_dir_load(star) assert_equal(str(ds), "plrd01000") for test in small_patch_amr(ds, _orion_fields): test_star.__name__ = test.description yield test LyA = "Nyx_LyA/plt00000" @requires_ds(LyA) def test_LyA(): ds = data_dir_load(LyA) assert_equal(str(ds), "plt00000") for test in small_patch_amr( ds, _nyx_fields, input_center="c", input_weight=("boxlib", "Ne") ): test_LyA.__name__ = test.description yield test @requires_file(LyA) def test_nyx_particle_io(): ds = data_dir_load(LyA) grid = ds.index.grids[0] npart_grid_0 = 7908 # read directly from the header assert_equal(grid["all", "particle_position_x"].size, npart_grid_0) assert_equal(grid["DM", "particle_position_y"].size, npart_grid_0) assert_equal(grid["all", "particle_position_z"].size, npart_grid_0) ad = ds.all_data() npart = 32768 # read directly from the header assert_equal(ad["all", "particle_velocity_x"].size, npart) assert_equal(ad["DM", "particle_velocity_y"].size, npart) assert_equal(ad["all", "particle_velocity_z"].size, npart) assert np.all(ad["all", "particle_mass"] == ad["all", "particle_mass"][0]) left_edge = ds.arr([0.0, 0.0, 0.0], "code_length") right_edge = ds.arr([4.0, 4.0, 4.0], "code_length") center = 0.5 * (left_edge + right_edge) reg = ds.region(center, left_edge, right_edge) assert np.all( np.logical_and( reg["all", "particle_position_x"] <= right_edge[0], reg["all", "particle_position_x"] >= left_edge[0], ) ) assert np.all( np.logical_and( reg["all", "particle_position_y"] <= right_edge[1], reg["all", "particle_position_y"] >= left_edge[1], ) ) assert np.all( np.logical_and( reg["all", "particle_position_z"] <= right_edge[2], reg["all", "particle_position_z"] >= left_edge[2], ) ) RT_particles = "RT_particles/plt00050" @requires_ds(RT_particles) def test_RT_particles(): ds = data_dir_load(RT_particles) assert_equal(str(ds), "plt00050") for test in small_patch_amr(ds, _castro_fields): test_RT_particles.__name__ = test.description yield test @requires_file(RT_particles) def test_castro_particle_io(): ds = data_dir_load(RT_particles) grid = ds.index.grids[2] npart_grid_2 = 49 # read directly from the header assert_equal(grid["all", "particle_position_x"].size, npart_grid_2) assert_equal(grid["Tracer", "particle_position_y"].size, npart_grid_2) assert_equal(grid["all", "particle_position_y"].size, npart_grid_2) ad = ds.all_data() npart = 49 # read directly from the header assert_equal(ad["all", "particle_velocity_x"].size, npart) assert_equal(ad["Tracer", "particle_velocity_y"].size, npart) assert_equal(ad["all", "particle_velocity_y"].size, npart) left_edge = ds.arr([0.0, 0.0, 0.0], "code_length") right_edge = ds.arr([0.25, 1.0, 1.0], "code_length") center = 0.5 * (left_edge + right_edge) reg = ds.region(center, left_edge, right_edge) assert np.all( np.logical_and( reg["all", "particle_position_x"] <= right_edge[0], reg["all", "particle_position_x"] >= left_edge[0], ) ) assert np.all( np.logical_and( reg["all", "particle_position_y"] <= right_edge[1], reg["all", "particle_position_y"] >= left_edge[1], ) ) langmuir = "LangmuirWave/plt00020_v2" @requires_ds(langmuir) def test_langmuir(): ds = data_dir_load(langmuir) assert_equal(str(ds), "plt00020_v2") for test in small_patch_amr( ds, _warpx_fields, input_center="c", input_weight=("mesh", "Ex") ): test_langmuir.__name__ = test.description yield test plasma = "PlasmaAcceleration/plt00030_v2" @requires_ds(plasma) def test_plasma(): ds = data_dir_load(plasma) assert_equal(str(ds), "plt00030_v2") for test in small_patch_amr( ds, _warpx_fields, input_center="c", input_weight=("mesh", "Ex") ): test_plasma.__name__ = test.description yield test beam = "GaussianBeam/plt03008" @requires_ds(beam) def test_beam(): ds = data_dir_load(beam) assert_equal(str(ds), "plt03008") for param in ("number of boxes", "maximum zones"): # PR 2807 # these parameters are only populated if the config file attached to this # dataset is read correctly assert param in ds.parameters for test in small_patch_amr( ds, _warpx_fields, input_center="c", input_weight=("mesh", "Ex") ): test_beam.__name__ = test.description yield test @requires_file(plasma) def test_warpx_particle_io(): ds = data_dir_load(plasma) grid = ds.index.grids[0] # read directly from the header npart0_grid_0 = 344 npart1_grid_0 = 69632 assert_equal(grid["particle0", "particle_position_x"].size, npart0_grid_0) assert_equal(grid["particle1", "particle_position_y"].size, npart1_grid_0) assert_equal(grid["all", "particle_position_z"].size, npart0_grid_0 + npart1_grid_0) # read directly from the header npart0 = 1360 npart1 = 802816 ad = ds.all_data() assert_equal(ad["particle0", "particle_velocity_x"].size, npart0) assert_equal(ad["particle1", "particle_velocity_y"].size, npart1) assert_equal(ad["all", "particle_velocity_z"].size, npart0 + npart1) np.all(ad["particle1", "particle_mass"] == ad["particle1", "particle_mass"][0]) np.all(ad["particle0", "particle_mass"] == ad["particle0", "particle_mass"][0]) left_edge = ds.arr([-7.5e-5, -7.5e-5, -7.5e-5], "code_length") right_edge = ds.arr([2.5e-5, 2.5e-5, 2.5e-5], "code_length") center = 0.5 * (left_edge + right_edge) reg = ds.region(center, left_edge, right_edge) assert np.all( np.logical_and( reg["all", "particle_position_x"] <= right_edge[0], reg["all", "particle_position_x"] >= left_edge[0], ) ) assert np.all( np.logical_and( reg["all", "particle_position_y"] <= right_edge[1], reg["all", "particle_position_y"] >= left_edge[1], ) ) assert np.all( np.logical_and( reg["all", "particle_position_z"] <= right_edge[2], reg["all", "particle_position_z"] >= left_edge[2], ) ) _raw_fields = [("raw", "Bx"), ("raw", "Ey"), ("raw", "jz")] laser = "Laser/plt00015" @requires_ds(laser) def test_raw_fields(): for field in _raw_fields: yield GridValuesTest(laser, field) @requires_file(rt) def test_OrionDataset(): assert isinstance(data_dir_load(rt), OrionDataset) @requires_file(LyA) def test_NyxDataset(): assert isinstance(data_dir_load(LyA), NyxDataset) @requires_file("nyx_small/nyx_small_00000") def test_NyxDataset_2(): assert isinstance(data_dir_load("nyx_small/nyx_small_00000"), NyxDataset) @requires_file(RT_particles) def test_CastroDataset(): assert isinstance(data_dir_load(RT_particles), CastroDataset) @requires_file("castro_sod_x_plt00036") def test_CastroDataset_2(): assert isinstance(data_dir_load("castro_sod_x_plt00036"), CastroDataset) @requires_file("castro_sedov_1d_cyl_plt00150") def test_CastroDataset_3(): assert isinstance(data_dir_load("castro_sedov_1d_cyl_plt00150"), CastroDataset) @requires_file(plasma) def test_WarpXDataset(): assert isinstance(data_dir_load(plasma), WarpXDataset) @disable_dataset_cache @requires_file(plasma) def test_magnetic_units(): ds1 = load(plasma) assert_allclose(ds1.magnetic_unit.value, 1.0) assert str(ds1.magnetic_unit.units) == "T" mag_unit1 = ds1.magnetic_unit.to("code_magnetic") assert_allclose(mag_unit1.value, 1.0) assert str(mag_unit1.units) == "code_magnetic" ds2 = load(plasma, unit_system="cgs") assert_allclose(ds2.magnetic_unit.value, 1.0e4) assert str(ds2.magnetic_unit.units) == "G" mag_unit2 = ds2.magnetic_unit.to("code_magnetic") assert_allclose(mag_unit2.value, 1.0) assert str(mag_unit2.units) == "code_magnetic" @requires_ds(laser) def test_WarpXDataset_2(): assert isinstance(data_dir_load(laser), WarpXDataset) @requires_file("plt.Cavity00010") def test_AMReXDataset(): ds = data_dir_load("plt.Cavity00010", kwargs={"cparam_filename": "inputs"}) assert isinstance(ds, AMReXDataset) @requires_file(rt) def test_units_override(): units_override_check(rt) nyx_no_particles = "nyx_sedov_plt00086" @requires_file(nyx_no_particles) def test_nyx_no_part(): assert isinstance(data_dir_load(nyx_no_particles), NyxDataset) fields = sorted( [ ("boxlib", "H"), ("boxlib", "He"), ("boxlib", "MachNumber"), ("boxlib", "Ne"), ("boxlib", "Rank"), ("boxlib", "StateErr"), ("boxlib", "Temp"), ("boxlib", "X(H)"), ("boxlib", "X(He)"), ("boxlib", "density"), ("boxlib", "divu"), ("boxlib", "eint_E"), ("boxlib", "eint_e"), ("boxlib", "entropy"), ("boxlib", "forcex"), ("boxlib", "forcey"), ("boxlib", "forcez"), ("boxlib", "kineng"), ("boxlib", "logden"), ("boxlib", "magmom"), ("boxlib", "magvel"), ("boxlib", "magvort"), ("boxlib", "pressure"), ("boxlib", "rho_E"), ("boxlib", "rho_H"), ("boxlib", "rho_He"), ("boxlib", "rho_e"), ("boxlib", "soundspeed"), ("boxlib", "x_velocity"), ("boxlib", "xmom"), ("boxlib", "y_velocity"), ("boxlib", "ymom"), ("boxlib", "z_velocity"), ("boxlib", "zmom"), ] ) ds = data_dir_load(nyx_no_particles) assert_equal(sorted(ds.field_list), fields) msubch = "maestro_subCh_plt00248" @requires_file(msubch) def test_maestro_parameters(): assert isinstance(data_dir_load(msubch), MaestroDataset) ds = data_dir_load(msubch) # Check a string parameter assert ds.parameters["plot_base_name"] == "subCh_hot_baserun_plt" assert type(ds.parameters["plot_base_name"]) is str # noqa: E721 # Check boolean parameters: T or F assert not ds.parameters["use_thermal_diffusion"] assert type(ds.parameters["use_thermal_diffusion"]) is bool # noqa: E721 assert ds.parameters["do_burning"] assert type(ds.parameters["do_burning"]) is bool # noqa: E721 # Check a float parameter with a decimal point assert ds.parameters["sponge_kappa"] == float("10.00000000") assert type(ds.parameters["sponge_kappa"]) is float # noqa: E721 # Check a float parameter with E exponent notation assert ds.parameters["small_dt"] == float("0.1000000000E-09") # Check an int parameter assert ds.parameters["s0_interp_type"] == 3 assert type(ds.parameters["s0_interp_type"]) is int # noqa: E721 castro_1d_cyl = "castro_sedov_1d_cyl_plt00150" @requires_file(castro_1d_cyl) def test_castro_parameters(): ds = data_dir_load(castro_1d_cyl) assert isinstance(ds, CastroDataset) # Modified from default (leading [*]) assert ds.parameters["castro.do_hydro"] == 1 assert ds.parameters["castro.cfl"] == 0.5 assert ds.parameters["problem.p_ambient"] == float("1e-06") # Leading [*] should be removed from the parameter name assert "[*] castro.do_hydro" not in ds.parameters # Not modified from default assert ds.parameters["castro.pslope_cutoff_density"] == float("-1e+20") assert ds.parameters["castro.do_sponge"] == 0 assert ds.parameters["problem.dens_ambient"] == 1 assert ds.parameters["eos.eos_assume_neutral"] == 1 # Empty string value assert ds.parameters["castro.stopping_criterion_field"] is None yt-project-yt-f043ac8/yt/frontends/cf_radial/000077500000000000000000000000001510711153200212145ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cf_radial/__init__.py000066400000000000000000000000521510711153200233220ustar00rootroot00000000000000""" API for yt.frontends.cf_radial """ yt-project-yt-f043ac8/yt/frontends/cf_radial/api.py000066400000000000000000000003011510711153200223310ustar00rootroot00000000000000""" API for yt.frontends.cf_radial """ from .data_structures import CFRadialDataset, CFRadialGrid, CFRadialHierarchy from .fields import CFRadialFieldInfo from .io import CFRadialIOHandler yt-project-yt-f043ac8/yt/frontends/cf_radial/data_structures.py000066400000000000000000000323331510711153200250060ustar00rootroot00000000000000""" CF Radial data structures """ import contextlib import os import weakref import numpy as np from unyt import unyt_array from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.funcs import mylog from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.file_handler import NetCDF4FileHandler, valid_netcdf_signature from yt.utilities.on_demand_imports import _xarray as xr from .fields import CFRadialFieldInfo class CFRadialGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level, dimensions): super().__init__(id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level self.ActiveDimensions = dimensions class CFRadialHierarchy(GridIndex): grid = CFRadialGrid def __init__(self, ds, dataset_type="cf_radial"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) # our index file is the dataset itself: self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) # float type for the simulation edges and must be float64 now self.float_type = np.float64 super().__init__(ds, dataset_type) def _detect_output_fields(self): # This sets self.field_list, containing all the available on-disk fields and # records the units for each field. self.field_list = [] units = {} with self.ds._handle() as xr_ds_handle: for key in xr_ds_handle.variables.keys(): if all(x in xr_ds_handle[key].dims for x in ["time", "z", "y", "x"]): fld = ("cf_radial", key) self.field_list.append(fld) units[fld] = xr_ds_handle[key].units self.ds.field_units.update(units) def _count_grids(self): self.num_grids = 1 def _parse_index(self): self.grid_left_edge[0][:] = self.ds.domain_left_edge[:] self.grid_right_edge[0][:] = self.ds.domain_right_edge[:] self.grid_dimensions[0][:] = self.ds.domain_dimensions[:] self.grid_particle_count[0][0] = 0 self.grid_levels[0][0] = 0 self.max_level = 0 def _populate_grid_objects(self): # only a single grid, no need to loop g = self.grid(0, self, self.grid_levels.flat[0], self.grid_dimensions[0]) g._prepare_grid() g._setup_dx() self.grids = np.array([g], dtype="object") class CFRadialDataset(Dataset): _load_requirements = ["xarray", "pyart"] _index_class = CFRadialHierarchy _field_info_class = CFRadialFieldInfo def __init__( self, filename, dataset_type="cf_radial", storage_filename=None, storage_overwrite: bool = False, grid_shape: tuple[int, int, int] | None = None, grid_limit_x: tuple[float, float] | None = None, grid_limit_y: tuple[float, float] | None = None, grid_limit_z: tuple[float, float] | None = None, units_override=None, ): """ Parameters ---------- filename dataset_type storage_filename: Optional[str] the filename to store gridded file to if necessary. If not provided, the string "_yt_grid" will be appended to the dataset filename. storage_overwrite: bool if True and if any gridding parameters are set, then the storage_filename will be over-written if it exists. Default is False. grid_shape : Optional[Tuple[int, int, int]] when gridding to cartesian, grid_shape is the number of cells in the z, y, x coordinates. If not provided, yt attempts to calculate a reasonable shape based on the resolution of the original cfradial grid grid_limit_x : Optional[Tuple[float, float]] The x range of the cartesian-gridded data in the form (xmin, xmax) with x in the native radar range units grid_limit_y : Optional[Tuple[float, float]] The y range of the cartesian-gridded data in the form (ymin, ymax) with y in the native radar range units grid_limit_z : Optional[Tuple[float, float]] The z range of the cartesian-gridded data in the form (zmin, zmax) with z in the native radar range units units_override """ self.fluid_types += ("cf_radial",) with self._handle(filename=filename) as xr_ds_handle: if "x" not in xr_ds_handle.coords: if storage_filename is None: f_base, f_ext = os.path.splitext(filename) storage_filename = f_base + "_yt_grid" + f_ext regrid = True if os.path.exists(storage_filename): # pyart grid.write will error if the filename exists, so this logic # forces some explicit behavior to minimize confusion and avoid # overwriting or deleting without explicit user consent. if storage_overwrite: os.remove(storage_filename) elif any([grid_shape, grid_limit_x, grid_limit_y, grid_limit_z]): mylog.warning( "Ignoring provided grid parameters because %s exists.", storage_filename, ) mylog.warning( "To re-grid, either provide a unique storage_filename or set " "storage_overwrite to True to overwrite %s.", storage_filename, ) regrid = False else: mylog.info( "loading existing re-gridded file: %s", storage_filename ) regrid = False if regrid: mylog.info("Building cfradial grid") from yt.utilities.on_demand_imports import _pyart as pyart radar = pyart.io.read_cfradial(filename) grid_limit_z = self._validate_grid_dim(radar, "z", grid_limit_z) grid_limit_x = self._validate_grid_dim(radar, "x", grid_limit_x) grid_limit_y = self._validate_grid_dim(radar, "y", grid_limit_y) grid_limits = (grid_limit_z, grid_limit_y, grid_limit_x) grid_shape = self._validate_grid_shape(grid_shape) # note: grid_shape must be in (z, y, x) order. self.grid_shape = grid_shape self.grid_limits = grid_limits mylog.info("Calling pyart.map.grid_from_radars ... ") # this is fairly slow grid = pyart.map.grid_from_radars( (radar,), grid_shape=self.grid_shape, grid_limits=self.grid_limits, ) mylog.info( "Successfully built cfradial grid, writing to %s", storage_filename, ) mylog.info( "Subsequent loads of %s will load the gridded file by default", filename, ) grid.write(storage_filename) filename = storage_filename super().__init__(filename, dataset_type, units_override=units_override) self.storage_filename = storage_filename self.refine_by = 2 # refinement factor between a grid and its subgrid @contextlib.contextmanager def _handle(self, filename: str | None = None): if filename is None: if hasattr(self, "filename"): filename = self.filename else: raise RuntimeError("Dataset has no filename yet.") with xr.open_dataset(filename) as xrds: yield xrds def _validate_grid_dim( self, radar, dim: str, grid_limit: tuple[float, float] | None = None ) -> tuple[float, float]: if grid_limit is None: if dim.lower() == "z": gate_alt = radar.gate_altitude["data"] gate_alt_units = radar.gate_altitude["units"] grid_limit = (gate_alt.min(), gate_alt.max()) grid_limit = self._round_grid_guess(grid_limit, gate_alt_units) mylog.info( "grid_limit_z not provided, using max height range in data: (%f, %f)", *grid_limit, ) else: max_range = radar.range["data"].max() grid_limit = self._round_grid_guess( (-max_range, max_range), radar.range["units"] ) mylog.info( "grid_limit_%s not provided, using max horizontal range in data: (%f, %f)", dim, *grid_limit, ) if len(grid_limit) != 2: raise ValueError( f"grid_limit_{dim} must have 2 dimensions, but it has {len(grid_limit)}" ) return grid_limit def _validate_grid_shape( self, grid_shape: tuple[int, int, int] | None = None ) -> tuple[int, int, int]: if grid_shape is None: grid_shape = (100, 100, 100) mylog.info( "grid_shape not provided, using (nz, ny, nx) = (%i, %i, %i)", *grid_shape, ) if len(grid_shape) != 3: raise ValueError( f"grid_shape must have 3 dimensions, but it has {len(grid_shape)}" ) return grid_shape def _round_grid_guess(self, bounds: tuple[float, float], unit_str: str): # rounds the bounds to the closest 10 km increment that still contains # the grid_limit for findstr, repstr in self._field_info_class.unit_subs: unit_str = unit_str.replace(findstr, repstr) limits = unyt_array(bounds, unit_str).to("km") limits[0] = np.floor(limits[0] / 10.0) * 10.0 limits[1] = np.ceil(limits[1] / 10.0) * 10.0 return tuple(limits.to(unit_str).tolist()) def _set_code_unit_attributes(self): with self._handle() as xr_ds_handle: length_unit = xr_ds_handle.variables["x"].attrs["units"] self.length_unit = self.quan(1.0, length_unit) self.mass_unit = self.quan(1.0, "kg") self.time_unit = self.quan(1.0, "s") def _parse_parameter_file(self): self.parameters = {} with self._handle() as xr_ds_handle: x, y, z = (xr_ds_handle.coords[d] for d in "xyz") self.domain_left_edge = np.array([x.min(), y.min(), z.min()]) self.domain_right_edge = np.array([x.max(), y.max(), z.max()]) self.dimensionality = 3 dims = [xr_ds_handle.sizes[d] for d in "xyz"] self.domain_dimensions = np.array(dims, dtype="int64") self._periodicity = (False, False, False) # note: origin_latitude and origin_longitude arrays will have time # as a dimension and the initial implementation here only handles # the first index. Also, the time array may have a datetime dtype, # so cast to float. self.origin_latitude = xr_ds_handle.origin_latitude[0] self.origin_longitude = xr_ds_handle.origin_longitude[0] self.current_time = float(xr_ds_handle.time.values[0]) # Cosmological information set to zero (not in space). self.cosmological_simulation = 0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # This accepts a filename or a set of arguments and returns True or # False depending on if the file is of the type requested. if not valid_netcdf_signature(filename): return False if cls._missing_load_requirements(): return False is_cfrad = False try: # note that we use the NetCDF4FileHandler here to avoid some # issues with xarray opening datasets it cannot handle. Once # a dataset is as identified as a CFRadialDataset, xarray is used # for opening. See https://github.com/yt-project/yt/issues/3987 nc4_file = NetCDF4FileHandler(filename) with nc4_file.open_ds(keepweakref=True) as ds: con = "Conventions" # the attribute to check for file conventions # note that the attributes here are potentially space- or # comma-delimited strings, so we concatenate a single string # to search for a substring. cons = "" # the value of the Conventions attribute for c in [con, con.lower(), "Sub_" + con.lower()]: if hasattr(ds, c): cons += getattr(ds, c) is_cfrad = "CF/Radial" in cons or "CF-Radial" in cons except (OSError, AttributeError, ImportError): return False return is_cfrad yt-project-yt-f043ac8/yt/frontends/cf_radial/fields.py000066400000000000000000000022151510711153200230340ustar00rootroot00000000000000""" CF-radial-specific fields """ from yt.fields.field_info_container import FieldInfoContainer class CFRadialFieldInfo(FieldInfoContainer): known_other_fields = () # fields are set dynamically known_particle_fields = () units_to_ignore = ("dBz", "dBZ", "ratio") # set as nondimensional if found field_units_ignored: list[str] = [] # fields for which units have been ignored # (find, replace) pairs for sanitizing: unit_subs = (("degrees", "degree"), ("meters", "m"), ("_per_", "/")) def setup_fluid_fields(self): # Here we dynamically add fields available in our netcdf file for to the # FieldInfoContainer with sanitized units. for field in self.field_list: # field here is ('fluid_type', 'field') tuple units = self.ds.field_units.get(field, "") # sanitization of the units if units in self.units_to_ignore: self.field_units_ignored.append(field) units = "" for findstr, repstr in self.unit_subs: units = units.replace(findstr, repstr) self.add_output_field(field, "cell", units=units) yt-project-yt-f043ac8/yt/frontends/cf_radial/io.py000066400000000000000000000021111510711153200221700ustar00rootroot00000000000000""" CF-Radial-specific IO functions """ import numpy as np from yt.utilities.io_handler import BaseIOHandler class CFRadialIOHandler(BaseIOHandler): _particle_reader = False _dataset_type = "cf_radial" def _read_fluid_selection(self, chunks, selector, fields, size): # This needs to allocate a set of arrays inside a dictionary, where the # keys are the (ftype, fname) tuples and the values are arrays that # have been masked using whatever selector method is appropriate. The # dict gets returned at the end and it should be flat, with selected # data. rv = {field: np.empty(size, dtype="float64") for field in fields} offset = 0 with self.ds._handle() as xr_ds_handle: for field in fields: for chunk in chunks: for grid in chunk.objs: variable = xr_ds_handle.variables[field[1]] data = variable.values[0, ...].T offset += grid.select(selector, data, rv[field], offset) return rv yt-project-yt-f043ac8/yt/frontends/cf_radial/tests/000077500000000000000000000000001510711153200223565ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cf_radial/tests/__init__.py000066400000000000000000000000001510711153200244550ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cf_radial/tests/test_cf_radial_pytest.py000066400000000000000000000032271510711153200273070ustar00rootroot00000000000000from pathlib import Path import numpy as np import yt from yt.frontends.cf_radial.api import CFRadialDataset from yt.testing import requires_module_pytest as requires_module def create_cf_radial_mock_gridded_ds(savedir: Path) -> Path: # a minimal dataset that yt should pick up as a CfRadial dataset import xarray as xr file_to_save = savedir / "mock_gridded_cfradial.nc" shp = (16, 16, 16) xyz = {dim: np.linspace(0.0, 1.0, shp[idim]) for idim, dim in enumerate("xyz")} da = xr.DataArray(data=np.ones(shp), coords=xyz, name="reflectivity") ds_xr = da.to_dataset() ds_xr.attrs["conventions"] = "CF/Radial" ds_xr.x.attrs["units"] = "m" ds_xr.y.attrs["units"] = "m" ds_xr.z.attrs["units"] = "m" ds_xr.reflectivity.attrs["units"] = "" times = np.array(["2017-05-19T01", "2017-05-19T01:01"], dtype="datetime64[ns]") ds_xr = ds_xr.assign_coords({"time": times}) ds_xr["origin_latitude"] = xr.DataArray(np.zeros_like(times), dims=("time",)) ds_xr["origin_longitude"] = xr.DataArray(np.zeros_like(times), dims=("time",)) ds_xr.to_netcdf(file_to_save, engine="netcdf4") return file_to_save @requires_module("xarray", "netCDF4", "pyart") def test_load_mock_gridded_cf_radial(tmp_path): import xarray as xr test_file = create_cf_radial_mock_gridded_ds(tmp_path) assert test_file.exists() # make sure that the mock dataset is valid and can be re-loaded with xarray with xr.open_dataset(test_file) as ds_xr: assert "CF/Radial" in ds_xr.conventions test_file = create_cf_radial_mock_gridded_ds(tmp_path) ds_yt = yt.load(test_file) assert isinstance(ds_yt, CFRadialDataset) yt-project-yt-f043ac8/yt/frontends/cf_radial/tests/test_outputs.py000066400000000000000000000130431510711153200255130ustar00rootroot00000000000000""" CF-Radial frontend tests """ import os import shutil import tempfile import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.frontends.cf_radial.data_structures import CFRadialDataset from yt.testing import ( requires_file, requires_module, units_override_check, ) from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) cf = "CfRadialGrid/grid1.nc" # an already gridded cfradial file cf_nongridded = ( "CfRadialGrid/swx_20120520_0641.nc" # cfradial file without cartesian grid ) _fields_cfradial = ["reflectivity", "velocity", "gate_id", "differential_phase", "ROI"] _fields_units = { "reflectivity": "dimensionless", "velocity": "m/s", "differential_phase": "degree", "gate_id": "dimensionless", "ROI": "m", } @requires_module("xarray") @requires_file(cf) def test_units_override(): units_override_check(cf) @requires_module("xarray") @requires_file(cf) def test_cf_radial_gridded(): ds = data_dir_load(cf) assert isinstance(ds, CFRadialDataset) check_domain(ds) check_origin_latitude_longitude(ds) ad = ds.all_data() for field in _fields_cfradial: check_fields(ds, field) check_field_units(ad, field, _fields_units[field]) def check_fields(ds, field): assert ("cf_radial", field) in ds.field_list with ds._handle() as xr_ds_handle: assert field in xr_ds_handle.variables.keys() def check_field_units(ad, field, value): assert str(ad["cf_radial", field].units) == value def check_origin_latitude_longitude(ds): assert_almost_equal(ds.origin_latitude.values, 36.49120001) assert_almost_equal(ds.origin_longitude.values, -97.5939) def check_domain(ds): domain_dim_array = [251, 251, 46] assert_equal(ds.domain_dimensions, domain_dim_array) domain_center_array = [0.0, 0.0, 7500.0] assert_equal(ds.domain_center, domain_center_array) domain_left_array = [-50000.0, -50000.0, 0.0] assert_equal(ds.domain_left_edge, domain_left_array) domain_right_array = [50000.0, 50000.0, 15000.0] assert_equal(ds.domain_right_edge, domain_right_array) @requires_module("xarray") @requires_file(cf_nongridded) def test_auto_gridding(): # loads up a radial dataset, which triggers the gridding. # create temporary directory and grid file tempdir = tempfile.mkdtemp() grid_file = os.path.join(tempdir, "temp_grid.nc") # this load will trigger the re-gridding and write out the gridded file # from which data will be loaded. With default gridding params, this takes # on the order of 10s, but since we are not testing actual output here, we # can decrease the resolution to speed it up. grid_shape = (10, 10, 10) ds = data_dir_load( cf_nongridded, kwargs={"storage_filename": grid_file, "grid_shape": grid_shape} ) assert os.path.exists(grid_file) # check that the cartesian fields exist now with ds._handle() as xr_ds_handle: on_disk_fields = xr_ds_handle.variables.keys() for field in ["x", "y", "z"]: assert field in on_disk_fields assert all(ds.domain_dimensions == grid_shape) # check that we can load the gridded file too ds = data_dir_load(grid_file) assert isinstance(ds, CFRadialDataset) shutil.rmtree(tempdir) @requires_module("xarray") @requires_file(cf_nongridded) def test_grid_parameters(): # checks that the gridding parameters are used and that conflicts in parameters # are resolved as expected. tempdir = tempfile.mkdtemp() grid_file = os.path.join(tempdir, "temp_grid_params.nc") # check that the grid parameters work cfkwargs = { "storage_filename": grid_file, "grid_shape": (10, 10, 10), "grid_limit_x": (-10000, 10000), "grid_limit_y": (-10000, 10000), "grid_limit_z": (500, 20000), } ds = data_dir_load(cf_nongridded, kwargs=cfkwargs) expected_width = [] for dim in "xyz": minval, maxval = cfkwargs[f"grid_limit_{dim}"] expected_width.append(maxval - minval) expected_width = np.array(expected_width) actual_width = ds.domain_width.to_value("m") assert all(expected_width == actual_width) assert all(ds.domain_dimensions == cfkwargs["grid_shape"]) # check the grid parameter conflicts # on re-load with default grid params it will reload storage_filename if # it exists. Just checking that this runs... _ = data_dir_load(cf_nongridded, kwargs={"storage_filename": grid_file}) # if storage_filename exists, grid parameters are ignored (with a warning) # and the domain_dimensions will match the original new_kwargs = {"storage_filename": grid_file, "grid_shape": (15, 15, 15)} ds = data_dir_load(cf_nongridded, kwargs=new_kwargs) assert all(ds.domain_dimensions == cfkwargs["grid_shape"]) # if we overwrite, the regridding should run and the dimensions should match # the desired dimensions new_kwargs["storage_overwrite"] = True ds = data_dir_load(cf_nongridded, kwargs=new_kwargs) assert all(ds.domain_dimensions == new_kwargs["grid_shape"]) shutil.rmtree(tempdir) @requires_module("xarray") @requires_ds(cf) def test_cfradial_grid_field_values(): ds = data_dir_load(cf) fields_to_check = [("cf_radial", field) for field in _fields_cfradial] wtfield = ("cf_radial", "reflectivity") for test in small_patch_amr( ds, fields_to_check, input_center=ds.domain_center, input_weight=wtfield ): test_cfradial_grid_field_values.__name__ = test.description yield test yt-project-yt-f043ac8/yt/frontends/chimera/000077500000000000000000000000001510711153200207205ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chimera/__init__.py000066400000000000000000000000501510711153200230240ustar00rootroot00000000000000""" API for yt.frontends.chimera """ yt-project-yt-f043ac8/yt/frontends/chimera/api.py000066400000000000000000000002331510711153200220410ustar00rootroot00000000000000""" API for yt.frontends.chimera """ from .data_structures import ChimeraDataset from .fields import ChimeraFieldInfo from .io import ChimeraIOHandler yt-project-yt-f043ac8/yt/frontends/chimera/data_structures.py000066400000000000000000000260101510711153200245050ustar00rootroot00000000000000""" Chimera data structures """ import os import re import numpy as np from yt.data_objects.index_subobjects.unstructured_mesh import SemiStructuredMesh from yt.data_objects.static_output import Dataset from yt.geometry.api import Geometry from yt.geometry.geometry_handler import YTDataChunk from yt.geometry.unstructured_mesh_handler import UnstructuredIndex from yt.utilities.file_handler import HDF5FileHandler from yt.utilities.io_handler import io_registry from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import ChimeraFieldInfo class ChimeraMesh(SemiStructuredMesh): _index_offset = 0 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _find_files(filename_c): # Returns a list of all files that share a frame number with the input dirname, file = os.path.split(filename_c) match = re.match(r"chimera_\d+_grid", file) if match is None: raise RuntimeError( rf"Expected filename to be of form 'chimera_\d+_grid_*', got {file!r}" ) prefix = match.group() frames = [f for f in os.listdir(dirname) if f.startswith(prefix)] index_filenames = [os.path.join(dirname, f) for f in sorted(frames)] return index_filenames class ChimeraUNSIndex(UnstructuredIndex): def __init__(self, ds, dataset_type="chimera"): self._handle = ds._handle super().__init__(ds, dataset_type) self.directory = os.path.dirname(self.dataset.filename) self.dataset_type = dataset_type def _initialize_mesh(self): self.meshes = [] index_filenames = _find_files( self.dataset.filename ) # Retrieves list of all datafiles with the same frame number # Detects Yin-Yang data format yy = any("grid_2" in file for file in index_filenames) for n, file in enumerate(index_filenames): with h5py.File(file, "r") as f: nmx, nmy, nmz = tuple(f["mesh"]["array_dimensions"][:]) l = ( int(file[-5:-3]) - 1 ) # Pulls the subgrid number from the data file name if nmz > 2: k = f["fluid"]["entropy"].shape[0] r = f["mesh"]["x_ef"][:-2] theta = f["mesh"]["y_ef"][:] phi = f["mesh"]["z_ef"][ k * l : k * (l + 1) + 1 ] # Pulls only the individual subgrid's band of phi values elif f["mesh"]["z_ef"][-1] == f["mesh"]["z_ef"][0]: r = f["mesh"]["x_ef"][ : f["mesh"]["radial_index_bound"][1] - f["mesh"]["x_ef"].shape[0] ] theta = f["mesh"]["y_ef"][:] phi = np.array([f["mesh"]["z_ef"][0], 2 * np.pi]) else: r = f["mesh"]["x_ef"][ : f["mesh"]["radial_index_bound"][1] - f["mesh"]["x_ef"].shape[0] ] theta = f["mesh"]["y_ef"][:] phi = f["mesh"]["z_ef"][:] # Creates variables to hold the size of dimensions nxd = r.size nyd = theta.size nzd = phi.size nyzd = nyd * nzd nyzd_ = (nyd - 1) * (nzd - 1) # Generates and fills coordinate array coords = np.zeros((nxd, nyd, nzd, 3), dtype="float64", order="C") coords[:, :, :, 0] = r[:, None, None] coords[:, :, :, 1] = theta[None, :, None] coords[:, :, :, 2] = phi[None, None, :] if yy: mylog.warning( "Yin-Yang File Detected; This data is not currently supported." ) coords = coords.reshape(nxd * nyd * nzd, 3) # Connectivity is an array of rows, each of which corresponds to a grid cell. # The 8 elements of each row are integers representing the cell vertices. # These integers reference the numerical index of the element of the # "coords" array which corresponds to the spatial coordinate. connectivity = np.zeros( ((nyd - 1) * (nxd - 1) * (nzd - 1), 8), dtype="int64", order="C" ) # Creates scaffold array connectivity[0] = [ 0, 1, nzd, (nzd + 1), (nyzd), (nyzd + 1), (nyzd + nzd), (nyzd + nzd + 1), ] # Manually defines first coordinate set for p in range( nzd - 1 ): # Increments first row around phi to define an arc of cells if p > 0: connectivity[p] = connectivity[p - 1] + 1 for t in range( nyd - 1 ): # Increments this arc around theta to define a shell if t > 0: connectivity[t * (nzd - 1) : (t + 1) * (nzd - 1)] = ( connectivity[(t - 1) * (nzd - 1) : t * (nzd - 1)] + nzd ) for r in range( nxd - 1 ): # Increments this shell along r to define a sphere if r > 0: connectivity[r * (nyzd_) : (r + 1) * (nyzd_)] = ( connectivity[(r - 1) * (nyzd_) : r * (nyzd_)] + nyzd ) mesh = ChimeraMesh( n, self.index_filename, connectivity, coords, self ) # Creates a mesh object if "grid_" in file: mylog.info("Mesh %s generated", (n + 1) / len(index_filenames)) self.meshes.append( mesh ) # Adds new mesh to the list of generated meshes def _detect_output_fields(self): # Reads in the available data fields with h5py.File(self.index_filename, "r") as f: fluids = [ ("chimera", i) for i in f["fluid"] if np.shape(f["fluid"][i]) == np.shape(f["fluid"]["rho_c"]) ] abundance = [ ("chimera", i) for i in f["abundance"] if np.shape(f["abundance"][i]) == np.shape(f["fluid"]["rho_c"]) ] e_rms = [("chimera", f"e_rms_{i+1}") for i in range(4)] lumin = [("chimera", f"lumin_{i+1}") for i in range(4)] num_lumin = [("chimera", f"num_lumin_{i+1}") for i in range(4)] a_name = [ ("chimera", i.decode("utf-8").strip()) for i in f["abundance"]["a_name"] ] self.field_list = ( fluids + abundance + e_rms + lumin + num_lumin + [("chimera", "abar")] + a_name ) if np.shape(f["abundance"]["nse_c"]) != np.shape(f["fluid"]["rho_c"]): self.field_list += [("chimera", "nse_c")] def _chunk_io(self, dobj, cache=True, local_only=False): # Creates Data chunk gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for subset in gobjs: yield YTDataChunk( dobj, "io", [subset], self._count_selection(dobj, [subset]), cache=cache ) def _setup_data_io(self): self.io = io_registry[self.dataset_type](self.dataset) class ChimeraDataset(Dataset): _load_requirements = ["h5py"] _index_class = ChimeraUNSIndex # ChimeraHierarchy _field_info_class = ChimeraFieldInfo def __init__( self, filename, dataset_type="chimera", storage_filename=None, units_override=None, ): # refinement factor between a grid and its subgrid self.refine_by = 1 # Somewhat superfluous for Chimera, but left to avoid errors self.fluid_types += ("chimera",) super().__init__(filename, dataset_type, units_override=units_override) self.storage_filename = storage_filename self._handle = HDF5FileHandler(filename) def _set_code_unit_attributes(self): # This is where quantities are created that represent the various # on-disk units. These are the currently available quantities which # should be set, along with examples of how to set them to standard # values. # self.length_unit = self.quan(1.0, "cm") self.mass_unit = self.quan(1.0, "g") self.time_unit = self.quan(1.0, "s") self.time_unit = self.quan(1.0, "s") self.velocity_unit = self.quan(1.0, "cm/s") self.magnetic_unit = self.quan(1.0, "gauss") def _parse_parameter_file(self): with h5py.File(self.parameter_filename, "r") as f: # Reads in simulation time, number of dimensions and shape self.current_time = f["mesh"]["time"][()] self.dimensionality = 3 self.domain_dimensions = f["mesh"]["array_dimensions"][()] self.geometry = Geometry.SPHERICAL # Uses default spherical geometry self._periodicity = (False, False, True) dle = [ f["mesh"]["x_ef"][0], f["mesh"]["y_ef"][0], f["mesh"]["z_ef"][0], ] if ( self.domain_dimensions[2] <= 2 and f["mesh"]["z_ef"][-1] == f["mesh"]["z_ef"][0] ): dre = [ f["mesh"]["x_ef"][ f["mesh"]["radial_index_bound"][1] - f["mesh"]["x_ef"].shape[0] ], f["mesh"]["y_ef"][-1], 2 * np.pi, ] else: dre = [ f["mesh"]["x_ef"][ f["mesh"]["radial_index_bound"][1] - f["mesh"]["x_ef"].shape[0] ], f["mesh"]["y_ef"][-1], f["mesh"]["z_ef"][-1], ] # Sets left and right bounds based on earlier definitions self.domain_right_edge = np.array(dre) self.domain_left_edge = np.array(dle) self.cosmological_simulation = 0 # Chimera is not a cosmological simulation @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # This accepts a filename or a set of arguments and returns True or # False depending on if the file is of the type requested. if cls._missing_load_requirements(): return False try: fileh = HDF5FileHandler(filename) if ( "fluid" in fileh and "agr_c" in fileh["fluid"].keys() and "grav_x_c" in fileh["fluid"].keys() ): return True # Numpy bless except OSError: pass return False yt-project-yt-f043ac8/yt/frontends/chimera/definitions.py000066400000000000000000000001141510711153200236010ustar00rootroot00000000000000# This file is often empty. It can hold definitions related to a frontend. yt-project-yt-f043ac8/yt/frontends/chimera/fields.py000066400000000000000000000045271510711153200225500ustar00rootroot00000000000000""" Chimera-specific fields """ from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer # We need to specify which fields we might have in our dataset. The field info # container subclass here will define which fields it knows about. There are # optionally methods on it that get called which can be subclassed. class ChimeraFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("e_int", ("erg", ["Internal Energy"], "Internal Energy")), ("entropy", ("", ["Entropy"], None)), ("rho_c", ("g/cm**3", ["density", "Density"], "Density")), ("dudt_nu", ("erg/s", [], None)), ("dudt_nuc", ("erg/s", [], None)), ("grav_x_c", ("cm/s**2", [], None)), ("grav_y_c", ("cm/s**2", [], None)), ("grav_z_c", ("cm/s**2", [], None)), ("press", ("erg/cm**3", ["pressure"], "Pressure")), ("t_c", ("K", ["temperature"], "Temperature")), ("u_c", ("cm/s", ["v_radial"], "Radial Velocity")), ("v_c", ("cm/s", ["v_theta"], "Theta Velocity")), ("v_csound", ("", [], None)), ("wBVMD", ("1/s", [], "BruntViasala_freq")), ("w_c", ("cm/s", ["v_phi"], "Phi Velocity")), ("ye_c", ("", [], None)), ("ylep", ("", [], None)), ("a_nuc_rep_c", ("", [], None)), ("be_nuc_rep_c", ("", [], None)), ("e_book", ("", [], None)), ("nse_c", ("", [], None)), ("z_nuc_rep_c", ("", [], None)), ) # Each entry here is of the form # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), known_particle_fields = ( # Identical form to above ) def __init__(self, ds, field_list): super().__init__(ds, field_list) # If you want, you can check self.field_list def setup_fluid_fields(self): # Here we do anything that might need info about the dataset. # You can use self.alias, self.add_output_field (for on-disk fields) # and self.add_field (for derived fields). def _test(field, data): return data["chimera", "rho_c"] self.add_field( ("chimera", "test"), sampling_type="cell", function=_test, units="g/cm**3" ) def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) # This will get called for every particle type. yt-project-yt-f043ac8/yt/frontends/chimera/io.py000066400000000000000000000232141510711153200217030ustar00rootroot00000000000000""" Chimera-specific IO functions """ import numpy as np import unyt as un from yt.frontends.chimera.data_structures import _find_files from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py class ChimeraIOHandler(BaseIOHandler): _particle_reader = False _dataset_type = "chimera" def __init__(self, ds): super().__init__(ds) self._handle = ds._handle self.filename = ds.filename def _read_particle_coords(self, chunks, ptf): # This needs to *yield* a series of tuples of (ptype, (x, y, z)). # chunks is a list of chunks, and ptf is a dict where the keys are # ptypes and the values are lists of fields. pass def _read_particle_fields(self, chunks, ptf, selector): # This gets called after the arrays have been allocated. It needs to # yield ((ptype, field), data) where data is the masked results of # reading ptype, field and applying the selector to the data read in. # Selector objects have a .select_points(x,y,z) that returns a mask, so # you need to do your masking here. pass def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} nodal_fields = [] for field in fields: finfo = self.ds.field_info[field] nodal_flag = finfo.nodal_flag if np.any(nodal_flag): num_nodes = 2 ** sum(nodal_flag) rv[field] = np.empty((size, num_nodes), dtype="=f8") nodal_fields.append(field) else: rv[field] = np.empty(size, dtype="=f8") ind = 0 for field, mesh, data in self.io_iter(chunks, fields): if data is None: continue else: ind += mesh.select(selector, data.flatten(), rv[field], ind) # caches return rv def io_iter(self, chunks, fields): for n, chunk in enumerate(chunks): file = _find_files(self.filename) with h5py.File(file[n], "r") as f: # Generates mask according to the "ongrid_mask" variable m = int(file[n][-5:-3]) - 1 k = f["fluid"]["entropy"].shape[0] mask_0 = f["mesh"]["ongrid_mask"][k * m : k * (m + 1), :] if f["mesh"]["array_dimensions"][2] > 1: nrd = f["mesh"]["array_dimensions"][0] - 2 else: nrd = f["mesh"]["array_dimensions"][0] mask = np.repeat(mask_0[:, :, np.newaxis], nrd, axis=2).transpose() specials = ( "abar", "e_rms_1", "e_rms_2", "e_rms_3", "e_rms_4", "lumin_1", "lumin_2", "lumin_3", "lumin_4", "num_lumin_1", "num_lumin_2", "num_lumin_3", "num_lumin_4", "shock", "nse_c", ) for field in fields: # Reads data by locating subheading ftype, fname = field a_name_2 = [i.decode("utf-8") for i in f["abundance"]["a_name"]] a_name_dict = {name.strip(): name for name in a_name_2} if fname not in specials: if fname in f["fluid"]: ds = f["fluid"][f"{fname}"] elif fname in f["abundance"]: ds = f["abundance"][f"{fname}"] elif fname in a_name_dict: ind_xn = a_name_2.index(a_name_dict[fname]) ds = f["abundance"]["xn_c"][:, :, :, ind_xn] else: mylog.warning("Invalid field name %s", fname) dat_1 = ds[:, :, :].transpose() elif fname == "nse_c": if np.shape(f["abundance"]["nse_c"]) != np.shape( f["fluid"]["rho_c"] ): ds = f["abundance"]["nse_c"][:, :, 1:] else: ds = f["abundance"]["nse_c"] dat_1 = ds[:, :, :].transpose() elif fname == "abar": xn_c = np.array(f["abundance"]["xn_c"]) a_nuc_rep_c = np.array(f["abundance"]["a_nuc_rep_c"]) a_nuc = np.array(f["abundance"]["a_nuc"]) a_nuc_tile = np.tile( a_nuc, (xn_c.shape[0], xn_c.shape[1], xn_c.shape[2], 1) ) yn_c = np.empty(xn_c.shape) yn_c[:, :, :, :-1] = xn_c[:, :, :, :-1] / a_nuc_tile[:, :, :, :] yn_c[:, :, :, -1] = xn_c[:, :, :, -1] / a_nuc_rep_c[:, :, :] ytot = np.sum(yn_c, axis=3) atot = np.sum(xn_c, axis=3) abar = np.divide(atot, ytot) dat_1 = abar[:, :, :].transpose() elif fname in ("e_rms_1", "e_rms_2", "e_rms_3", "e_rms_4"): dims = f["mesh"]["array_dimensions"] n_groups = f["radiation"]["raddim"][0] n_species = f["radiation"]["raddim"][1] n_hyperslabs = f["mesh"]["nz_hyperslabs"][()] energy_edge = f["radiation"]["unubi"][()] energy_center = f["radiation"]["unui"][()] d_energy = [] for i in range(0, n_groups): d_energy.append(energy_edge[i + 1] - energy_edge[i]) d_energy = np.array(d_energy) e3de = energy_center**3 * d_energy e5de = energy_center**5 * d_energy psi0_c = f["radiation"]["psi0_c"][:] row = np.empty( (n_species, int(dims[2] / n_hyperslabs), dims[1], dims[0]) ) for n in range(0, n_species): numerator = np.sum(psi0_c[:, :, :, n] * e5de, axis=3) denominator = np.sum(psi0_c[:, :, :, n] * e3de, axis=3) row[n][:][:][:] = np.sqrt( numerator / (denominator + 1e-100) ) species = int(fname[-1]) - 1 dat_1 = row[species, :, :, :].transpose() elif fname in ( "lumin_1", "lumin_2", "lumin_3", "lumin_4", "num_lumin_1", "num_lumin_2", "num_lumin_3", "num_lumin_4", ): dims = f["mesh"]["array_dimensions"] n_groups = f["radiation"]["raddim"][0] n_hyperslabs = f["mesh"]["nz_hyperslabs"][()] ergmev = float((1 * un.MeV) / (1 * un.erg)) cvel = float(un.c.to("cm/s")) h = float(un.h.to("MeV * s")) ecoef = 4.0 * np.pi * ergmev / (h * cvel) ** 3 radius = f["mesh"]["x_ef"][()] agr_e = f["fluid"]["agr_e"][()] cell_area_GRcorrected = 4 * np.pi * radius**2 / agr_e**4 psi1_e = f["radiation"]["psi1_e"] energy_edge = f["radiation"]["unubi"][()] energy_center = f["radiation"]["unui"][()] d_energy = [] for i in range(0, n_groups): d_energy.append(energy_edge[i + 1] - energy_edge[i]) d_energy = np.array(d_energy) species = int(fname[-1]) - 1 if fname in ("lumin_1", "lumin_2", "lumin_3", "lumin_4"): eNde = energy_center**3 * d_energy else: eNde = energy_center**2 * d_energy lumin = ( np.sum(psi1_e[:, :, :, species] * eNde, axis=3) * np.tile( cell_area_GRcorrected[1 : dims[0] + 1], (int(dims[2] / n_hyperslabs), dims[1], 1), ) * (cvel * ecoef * 1e-51) ) dat_1 = lumin[:, :, :].transpose() if f["mesh"]["array_dimensions"][2] > 1: data = dat_1[:-2, :, :] # Clips off ghost zones for 3D else: data = dat_1[:, :, :] data = np.ma.masked_where(mask == 0.0, data) # Masks data = np.ma.filled( data, fill_value=0.0 ) # Replaces masked value with 0 yield field, chunk.objs[0], data pass def _read_chunk_data(self, chunk, fields): # This reads the data from a single chunk without doing any selection, # and is only used for caching data that might be used by multiple # different selectors later. For instance, this can speed up ghost zone # computation. pass yt-project-yt-f043ac8/yt/frontends/chimera/misc.py000066400000000000000000000000001510711153200222130ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chimera/tests/000077500000000000000000000000001510711153200220625ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chimera/tests/__init__.py000066400000000000000000000000001510711153200241610ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chimera/tests/test_outputs.py000066400000000000000000000141401510711153200252160ustar00rootroot00000000000000""" Chimera frontend tests """ import numpy as np from numpy.testing import assert_almost_equal, assert_array_equal, assert_equal from yt.testing import requires_file, requires_module from yt.utilities.answer_testing.framework import ( GenericArrayTest, data_dir_load, requires_ds, ) Two_D = "F37_80/chimera_00001_grid_1_01.h5" @requires_module("h5py") @requires_ds(Two_D) def test_2D(): ds = data_dir_load(Two_D) _fields = [ ("chimera", "a_nuc_rep_c"), ("chimera", "abar"), ("chimera", "ar36"), ("chimera", "be_nuc_rep_c"), ("chimera", "c12"), ("chimera", "ca40"), ("chimera", "cr48"), ("chimera", "dudt_nu"), ("chimera", "dudt_nuc"), ("chimera", "e_book"), ("chimera", "e_int"), ("chimera", "e_rms_1"), ("chimera", "e_rms_2"), ("chimera", "e_rms_3"), ("chimera", "e_rms_4"), ("chimera", "entropy"), ("chimera", "fe52"), ("chimera", "fe56"), ("chimera", "grav_x_c"), ("chimera", "grav_y_c"), ("chimera", "grav_z_c"), ("chimera", "he4"), ("chimera", "lumin_1"), ("chimera", "lumin_2"), ("chimera", "lumin_3"), ("chimera", "lumin_4"), ("chimera", "mg24"), ("chimera", "n"), ("chimera", "ne20"), ("chimera", "ni56"), ("chimera", "nse_c"), ("chimera", "num_lumin_1"), ("chimera", "num_lumin_2"), ("chimera", "num_lumin_3"), ("chimera", "num_lumin_4"), ("chimera", "o16"), ("chimera", "p"), ("chimera", "press"), ("chimera", "rho_c"), ("chimera", "s32"), ("chimera", "si28"), ("chimera", "t_c"), ("chimera", "ti44"), ("chimera", "u_c"), ("chimera", "v_c"), ("chimera", "v_csound"), ("chimera", "wBVMD"), ("chimera", "w_c"), ("chimera", "ye_c"), ("chimera", "ylep"), ("chimera", "z_nuc_rep_c"), ("chimera", "zn60"), ] assert_equal(str(ds), "chimera_00001_grid_1_01.h5") assert_equal(str(ds.geometry), "spherical") # Geometry assert_almost_equal( ds.domain_right_edge, ds.arr([1.0116509e10 + 100, 3.14159265e00, 6.28318531e00], "code_length"), ) # domain edge assert_array_equal( ds.domain_left_edge, ds.arr([0.0, 0.0, 0.0], "code_length") ) # domain edge assert_array_equal(ds.domain_dimensions, np.array([722, 240, 1])) # Dimensions assert_array_equal(ds.field_list, _fields) def field_func(field): min = dd[field].min() max = dd[field].max() avg = np.mean(dd[field]) size = dd[field].size return [min, max, avg, size] dd = ds.all_data() for field in _fields: if field != ("chimera", "shock"): yield GenericArrayTest(ds, field_func, args=[field]) Three_D = "C15-3D-3deg/chimera_002715000_grid_1_01.h5" @requires_module("h5py") @requires_ds(Three_D) def test_3D(): ds = data_dir_load(Three_D) _fields = [ ("chimera", "a_nuc_rep_c"), ("chimera", "abar"), ("chimera", "ar36"), ("chimera", "be_nuc_rep_c"), ("chimera", "c12"), ("chimera", "ca40"), ("chimera", "cr48"), ("chimera", "dudt_nu"), ("chimera", "dudt_nuc"), ("chimera", "e_book"), ("chimera", "e_int"), ("chimera", "e_rms_1"), ("chimera", "e_rms_2"), ("chimera", "e_rms_3"), ("chimera", "e_rms_4"), ("chimera", "entropy"), ("chimera", "fe52"), ("chimera", "grav_x_c"), ("chimera", "grav_y_c"), ("chimera", "grav_z_c"), ("chimera", "he4"), ("chimera", "lumin_1"), ("chimera", "lumin_2"), ("chimera", "lumin_3"), ("chimera", "lumin_4"), ("chimera", "mg24"), ("chimera", "n"), ("chimera", "ne20"), ("chimera", "ni56"), ("chimera", "nse_c"), ("chimera", "num_lumin_1"), ("chimera", "num_lumin_2"), ("chimera", "num_lumin_3"), ("chimera", "num_lumin_4"), ("chimera", "o16"), ("chimera", "p"), ("chimera", "press"), ("chimera", "rho_c"), ("chimera", "s32"), ("chimera", "si28"), ("chimera", "t_c"), ("chimera", "ti44"), ("chimera", "u_c"), ("chimera", "v_c"), ("chimera", "v_csound"), ("chimera", "wBVMD"), ("chimera", "w_c"), ("chimera", "ye_c"), ("chimera", "ylep"), ("chimera", "z_nuc_rep_c"), ("chimera", "zn60"), ] assert_equal(str(ds), "chimera_002715000_grid_1_01.h5") assert_equal(str(ds.geometry), "spherical") # Geometry assert_almost_equal( ds.domain_right_edge, ds.arr( [1.06500257e09 - 1.03818333, 3.14159265e00, 6.2831853e00], "code_length" ), ) # Domain edge assert_array_equal(ds.domain_left_edge, [0.0, 0.0, 0.0]) # Domain edge assert_array_equal(ds.domain_dimensions, [542, 60, 135]) # Dimensions assert_array_equal(ds.field_list, _fields) def field_func(field): min = dd[field].min() max = dd[field].max() avg = np.mean(dd[field]) size = dd[field].size return [min, max, avg, size] dd = ds.all_data() for field in _fields: if field != ("chimera", "shock"): yield GenericArrayTest(ds, field_func, args=[field]) @requires_file(Three_D) def test_multimesh(): # Tests that the multimesh system for 3D data has been created correctly ds = data_dir_load(Three_D) assert_equal(len(ds.index.meshes), 45) for i in range(44): assert_almost_equal( ds.index.meshes[i + 1].connectivity_coords - ds.index.meshes[i].connectivity_coords, np.tile([0.0, 0.0, 0.13962634015954636], (132004, 1)), ) # Tests that each mesh is an identically shaped wedge, incrememnted in Phi. assert_array_equal( ds.index.meshes[i + 1].connectivity_indices, ds.index.meshes[i].connectivity_indices, ) # Checks Connectivity array is identical for all meshes. yt-project-yt-f043ac8/yt/frontends/cholla/000077500000000000000000000000001510711153200205525ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cholla/__init__.py000066400000000000000000000000001510711153200226510ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cholla/api.py000066400000000000000000000002141510711153200216720ustar00rootroot00000000000000from .data_structures import ChollaDataset, ChollaGrid, ChollaHierarchy from .fields import ChollaFieldInfo from .io import ChollaIOHandler yt-project-yt-f043ac8/yt/frontends/cholla/data_structures.py000066400000000000000000000156071510711153200243510ustar00rootroot00000000000000import os import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.funcs import setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import ChollaFieldInfo class ChollaGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level, dims): super().__init__(id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level self.ActiveDimensions = dims class ChollaHierarchy(GridIndex): grid = ChollaGrid def __init__(self, ds, dataset_type="cholla"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) # for now, the index file is the dataset! self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) # float type for the simulation edges and must be float64 now self.float_type = np.float64 super().__init__(ds, dataset_type) def _detect_output_fields(self): with h5py.File(self.index_filename, mode="r") as h5f: self.field_list = [("cholla", k) for k in h5f.keys()] def _count_grids(self): self.num_grids = 1 def _parse_index(self): self.grid_left_edge[0][:] = self.ds.domain_left_edge[:] self.grid_right_edge[0][:] = self.ds.domain_right_edge[:] self.grid_dimensions[0][:] = self.ds.domain_dimensions[:] self.grid_particle_count[0][0] = 0 self.grid_levels[0][0] = 0 self.max_level = 0 def _populate_grid_objects(self): self.grids = np.empty(self.num_grids, dtype="object") for i in range(self.num_grids): g = self.grid(i, self, self.grid_levels.flat[i], self.grid_dimensions[i]) g._prepare_grid() g._setup_dx() self.grids[i] = g class ChollaDataset(Dataset): _load_requirements = ["h5py"] _index_class = ChollaHierarchy _field_info_class = ChollaFieldInfo def __init__( self, filename, dataset_type="cholla", storage_filename=None, units_override=None, unit_system="cgs", ): self.fluid_types += ("cholla",) super().__init__(filename, dataset_type, units_override=units_override) self.storage_filename = storage_filename def _set_code_unit_attributes(self): # This is where quantities are created that represent the various # on-disk units. These are the defaults, but if they are listed # in the HDF5 attributes for a file, which is loaded first, then those are # used instead. # if not self.length_unit: self.length_unit = self.quan(1.0, "pc") if not self.mass_unit: self.mass_unit = self.quan(1.0, "Msun") if not self.time_unit: self.time_unit = self.quan(1000, "yr") if not self.velocity_unit: self.velocity_unit = self.quan(1.0, "cm/s") if not self.magnetic_unit: self.magnetic_unit = self.quan(1.0, "gauss") for key, unit in self.__class__.default_units.items(): setdefaultattr(self, key, self.quan(1, unit)) def _parse_parameter_file(self): with h5py.File(self.parameter_filename, mode="r") as h5f: attrs = h5f.attrs self.parameters = dict(attrs.items()) self.domain_left_edge = attrs["bounds"][:].astype("=f8") self.domain_right_edge = self.domain_left_edge + attrs["domain"][:].astype( "=f8" ) self.dimensionality = len(attrs["dims"][:]) self.domain_dimensions = attrs["dims"][:].astype("=f8") self.current_time = attrs["t"][:] self._periodicity = tuple(attrs.get("periodicity", (False, False, False))) self.gamma = attrs.get("gamma", 5.0 / 3.0) if (self.default_species_fields is not None) and "mu" in attrs: raise ValueError( 'default_species_fields must be None when "mu" is an hdf5 attribute' ) elif "mu" in attrs: self.mu = attrs["mu"] elif self.default_species_fields is None: # other yt-machinery can't handle ds.mu == None, so we simply # avoid defining the mu attribute if we don't know its value mylog.info( 'add the "mu" hdf5 attribute OR use the default_species_fields kwarg ' "to compute temperature" ) self.refine_by = 1 # If header specifies code units, default to those (in CGS) length_unit = attrs.get("length_unit", None) mass_unit = attrs.get("mass_unit", None) time_unit = attrs.get("time_unit", None) velocity_unit = attrs.get("velocity_unit", None) magnetic_unit = attrs.get("magnetic_unit", None) if length_unit: self.length_unit = self.quan(length_unit[0], "cm") if mass_unit: self.mass_unit = self.quan(mass_unit[0], "g") if time_unit: self.time_unit = self.quan(time_unit[0], "s") if velocity_unit: self.velocity_unit = self.quan(velocity_unit[0], "cm/s") if magnetic_unit: self.magnetic_unit = self.quan(magnetic_unit[0], "gauss") # this minimalistic implementation fills the requirements for # this frontend to run, change it to make it run _correctly_ ! for key, unit in self.__class__.default_units.items(): setdefaultattr(self, key, self.quan(1, unit)) # CHOLLA cannot yet be run as a cosmological simulation self.cosmological_simulation = 0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 # CHOLLA datasets are always unigrid cartesian self.geometry = Geometry.CARTESIAN @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # This accepts a filename or a set of arguments and returns True or # False depending on if the file is of the type requested. if cls._missing_load_requirements(): return False try: fileh = h5py.File(filename, mode="r") except OSError: return False try: attrs = fileh.attrs except AttributeError: return False else: return ( "bounds" in attrs and "domain" in attrs and attrs.get("data_type") != "yt_light_ray" ) finally: fileh.close() yt-project-yt-f043ac8/yt/frontends/cholla/definitions.py000066400000000000000000000001141510711153200234330ustar00rootroot00000000000000# This file is often empty. It can hold definitions related to a frontend. yt-project-yt-f043ac8/yt/frontends/cholla/fields.py000066400000000000000000000114211510711153200223710ustar00rootroot00000000000000import numpy as np from unyt import Zsun from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.utilities.physical_constants import kboltz, mh # Copied from Athena frontend pres_units = "code_pressure" erg_units = "code_mass * (code_length/code_time)**2" rho_units = "code_mass / code_length**3" mom_units = "code_mass / code_length**2 / code_time" def velocity_field(comp): def _velocity(field, data): return data["cholla", f"momentum_{comp}"] / data["cholla", "density"] return _velocity class ChollaFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( # Each entry here is of the form # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ("density", (rho_units, ["density"], None)), ("momentum_x", (mom_units, ["momentum_x"], None)), ("momentum_y", (mom_units, ["momentum_y"], None)), ("momentum_z", (mom_units, ["momentum_z"], None)), ("Energy", ("code_pressure", ["total_energy_density"], None)), ("scalar0", (rho_units, [], None)), ("metal_density", (rho_units, ["metal_density"], None)), ) known_particle_fields = () # In Cholla, conservative variables are written out. def setup_fluid_fields(self): unit_system = self.ds.unit_system # Add velocity fields for comp in "xyz": self.add_field( ("gas", f"velocity_{comp}"), sampling_type="cell", function=velocity_field(comp), units=unit_system["velocity"], ) # Add pressure field if ("cholla", "GasEnergy") in self.field_list: self.add_output_field( ("cholla", "GasEnergy"), sampling_type="cell", units=pres_units ) self.alias( ("gas", "thermal_energy"), ("cholla", "GasEnergy"), units=unit_system["pressure"], ) def _pressure(field, data): return (data.ds.gamma - 1.0) * data["cholla", "GasEnergy"] else: def _pressure(field, data): return (data.ds.gamma - 1.0) * ( data["cholla", "Energy"] - data["gas", "kinetic_energy_density"] ) self.add_field( ("gas", "pressure"), sampling_type="cell", function=_pressure, units=unit_system["pressure"], ) def _specific_total_energy(field, data): return data["cholla", "Energy"] / data["cholla", "density"] self.add_field( ("gas", "specific_total_energy"), sampling_type="cell", function=_specific_total_energy, units=unit_system["specific_energy"], ) # Add temperature field if hasattr(self.ds, "mu"): def _temperature(field, data): return ( data.ds.mu * data["gas", "pressure"] / data["gas", "density"] * mh / kboltz ) self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) # Add color field if present (scalar0 / density) if ("cholla", "scalar0") in self.field_list: self.add_output_field( ("cholla", "scalar0"), sampling_type="cell", units=rho_units, ) def _color(field, data): return data["cholla", "scalar0"] / data["cholla", "density"] self.add_field( ("cholla", "color"), sampling_type="cell", function=_color, units="", ) self.alias( ("gas", "color"), ("cholla", "color"), units="", ) # Using color field to define metallicity field, where a color of 1 # indicates solar metallicity def _metallicity(field, data): # Ensuring that there are no negative metallicities return np.clip(data["cholla", "color"], 0, np.inf) * Zsun self.add_field( ("cholla", "metallicity"), sampling_type="cell", function=_metallicity, units="Zsun", ) self.alias( ("gas", "metallicity"), ("cholla", "metallicity"), units="Zsun", ) def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) # This will get called for every particle type. yt-project-yt-f043ac8/yt/frontends/cholla/io.py000066400000000000000000000021371510711153200215360ustar00rootroot00000000000000import numpy as np from yt.utilities.io_handler import BaseIOHandler from yt.utilities.on_demand_imports import _h5py as h5py class ChollaIOHandler(BaseIOHandler): _particle_reader = False _dataset_type = "cholla" def _read_particle_coords(self, chunks, ptf): raise NotImplementedError def _read_particle_fields(self, chunks, ptf, selector): raise NotImplementedError def _read_fluid_selection(self, chunks, selector, fields, size): data = {} for field in fields: data[field] = np.empty(size, dtype="float64") with h5py.File(self.ds.parameter_filename, "r") as fh: ind = 0 for chunk in chunks: for grid in chunk.objs: nd = 0 for field in fields: ftype, fname = field values = fh[fname][:].astype("=f8") nd = grid.select(selector, values, data[field], ind) ind += nd return data def _read_chunk_data(self, chunk, fields): raise NotImplementedError yt-project-yt-f043ac8/yt/frontends/cholla/misc.py000066400000000000000000000000001510711153200220450ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cholla/tests/000077500000000000000000000000001510711153200217145ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cholla/tests/__init__.py000066400000000000000000000000001510711153200240130ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/cholla/tests/test_outputs.py000066400000000000000000000040321510711153200250470ustar00rootroot00000000000000from numpy.testing import assert_equal import yt from yt.frontends.cholla.api import ChollaDataset from yt.testing import requires_file, requires_module from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) _fields = ( ("gas", "temperature"), ("gas", "density"), ) ChollaSimple = "ChollaSimple/0.h5" @requires_module("h5py") @requires_file(ChollaSimple) def test_ChollaDataset(): assert isinstance(data_dir_load(ChollaSimple), ChollaDataset) @requires_module("h5py") @requires_file(ChollaSimple) def test_ChollaSimple_fields(): expected_fields = [ "Energy", "GasEnergy", "density", "momentum_x", "momentum_y", "momentum_z", "scalar0", ] ds = yt.load(ChollaSimple) assert_equal(str(ds), "0.h5") ad = ds.all_data() # Check all the expected fields exist and can be accessed for field in expected_fields: assert ("cholla", field) in ds.field_list # test that field access works ad["cholla", field] @requires_module("h5py") @requires_file(ChollaSimple) def test_ChollaSimple_derived_fields(): expected_derived_fields = [ "density", "momentum_x", "momentum_y", "momentum_z", "metallicity", ] ds = yt.load(ChollaSimple) ad = ds.all_data() # Check all the expected fields exist and can be accessed for field in expected_derived_fields: assert ("gas", field) in ds.derived_field_list # test that field access works ad["gas", field] _fields_chollasimple = ( ("cholla", "GasEnergy"), ("gas", "temperature"), ("gas", "density"), ("gas", "metallicity"), ) @requires_module("h5py") @requires_ds(ChollaSimple) def test_cholla_data(): ds = data_dir_load(ChollaSimple) assert_equal(str(ds), "0.h5") for test in small_patch_amr( ds, _fields_chollasimple, input_center="c", input_weight="ones" ): test_cholla_data.__name__ = test.description yield test yt-project-yt-f043ac8/yt/frontends/chombo/000077500000000000000000000000001510711153200205575ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chombo/__init__.py000066400000000000000000000000001510711153200226560ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chombo/api.py000066400000000000000000000006611510711153200217050ustar00rootroot00000000000000from . import tests from .data_structures import ( ChomboDataset, ChomboGrid, ChomboHierarchy, ChomboPICDataset, ChomboPICHierarchy, Orion2Dataset, Orion2Hierarchy, PlutoDataset, PlutoHierarchy, ) from .fields import ( ChomboFieldInfo, ChomboPICFieldInfo1D, ChomboPICFieldInfo2D, ChomboPICFieldInfo3D, Orion2FieldInfo, PlutoFieldInfo, ) from .io import IOHandlerChomboHDF5 yt-project-yt-f043ac8/yt/frontends/chombo/data_structures.py000066400000000000000000000735221510711153200243560ustar00rootroot00000000000000import os import re import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.field_info_container import FieldInfoContainer from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.file_handler import HDF5FileHandler, valid_hdf5_signature from yt.utilities.lib.misc_utilities import get_box_grids_level from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_root_only from .fields import ( ChomboFieldInfo, ChomboPICFieldInfo1D, ChomboPICFieldInfo2D, ChomboPICFieldInfo3D, Orion2FieldInfo, PlutoFieldInfo, ) def is_chombo_hdf5(fn): try: with h5py.File(fn, mode="r") as fileh: valid = "Chombo_global" in fileh["/"] except (KeyError, OSError, ImportError): return False return valid class ChomboGrid(AMRGridPatch): _id_offset = 0 __slots__ = ["_level_id", "stop_index"] def __init__(self, id, index, level, start, stop): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self._parent_id = [] self._children_ids = [] self.Level = level self.ActiveDimensions = stop - start + 1 def get_global_startindex(self): """ Return the integer starting index for each dimension at the current level. """ if self.start_index is not None: return self.start_index if self.Parent is None: iLE = self.LeftEdge - self.ds.domain_left_edge start_index = iLE / self.dds return np.rint(start_index).astype("int64").ravel() pdx = self.Parent[0].dds start_index = (self.Parent[0].get_global_startindex()) + np.rint( (self.LeftEdge - self.Parent[0].LeftEdge) / pdx ) self.start_index = (start_index * self.ds.refine_by).astype("int64").ravel() return self.start_index def _setup_dx(self): # has already been read in and stored in index self.dds = self.ds.arr(self.index.dds_list[self.Level], "code_length") @property def Parent(self): if len(self._parent_id) == 0: return None return [self.index.grids[pid - self._id_offset] for pid in self._parent_id] @property def Children(self): return [self.index.grids[cid - self._id_offset] for cid in self._children_ids] class ChomboHierarchy(GridIndex): grid = ChomboGrid _data_file = None def __init__(self, ds, dataset_type="chombo_hdf5"): self.domain_left_edge = ds.domain_left_edge self.domain_right_edge = ds.domain_right_edge self.dataset_type = dataset_type self.field_indexes = {} self.dataset = weakref.proxy(ds) # for now, the index file is the dataset! self.index_filename = os.path.abspath(self.dataset.parameter_filename) self.directory = ds.directory self._handle = ds._handle self._levels = [key for key in self._handle.keys() if key.startswith("level")] GridIndex.__init__(self, ds, dataset_type) self._read_particles() def _read_particles(self): # only do anything if the dataset contains particles if not any(f[1].startswith("particle_") for f in self.field_list): return self.num_particles = 0 particles_per_grid = [] for key, val in self._handle.items(): if key.startswith("level"): level_particles = val["particles:offsets"][:] self.num_particles += level_particles.sum() particles_per_grid = np.concatenate( (particles_per_grid, level_particles) ) for i, _grid in enumerate(self.grids): self.grids[i].NumberOfParticles = particles_per_grid[i] self.grid_particle_count[i] = particles_per_grid[i] assert self.num_particles == self.grid_particle_count.sum() # Chombo datasets, by themselves, have no "known" fields. However, # we will look for "fluid" fields by finding the string "component" in # the output file, and "particle" fields by finding the string "particle". def _detect_output_fields(self): # look for fluid fields output_fields = [] for key, val in self._handle.attrs.items(): if key.startswith("component"): output_fields.append(val.decode("ascii")) self.field_list = [("chombo", c) for c in output_fields] # look for particle fields particle_fields = [] for key, val in self._handle.attrs.items(): if key.startswith("particle"): particle_fields.append(val.decode("ascii")) self.field_list.extend([("io", c) for c in particle_fields]) def _count_grids(self): self.num_grids = 0 for lev in self._levels: d = self._handle[lev] if "Processors" in d: self.num_grids += d["Processors"].len() elif "boxes" in d: self.num_grids += d["boxes"].len() else: raise RuntimeError("Unknown file specification") def _parse_index(self): f = self._handle # shortcut self.max_level = f.attrs["num_levels"] - 1 grids = [] self.dds_list = [] i = 0 D = self.dataset.dimensionality for lev_index, lev in enumerate(self._levels): level_number = int(re.match(r"level_(\d+)", lev).groups()[0]) try: boxes = f[lev]["boxes"][()] except KeyError: boxes = f[lev]["particles:boxes"][()] dx = f[lev].attrs["dx"] self.dds_list.append(dx * np.ones(3)) if D == 1: self.dds_list[lev_index][1] = 1.0 self.dds_list[lev_index][2] = 1.0 if D == 2: self.dds_list[lev_index][2] = 1.0 for level_id, box in enumerate(boxes): si = np.array([box[f"lo_{ax}"] for ax in "ijk"[:D]]) ei = np.array([box[f"hi_{ax}"] for ax in "ijk"[:D]]) if D == 1: si = np.concatenate((si, [0.0, 0.0])) ei = np.concatenate((ei, [0.0, 0.0])) if D == 2: si = np.concatenate((si, [0.0])) ei = np.concatenate((ei, [0.0])) pg = self.grid(len(grids), self, level=level_number, start=si, stop=ei) grids.append(pg) grids[-1]._level_id = level_id self.grid_levels[i] = level_number self.grid_left_edge[i] = self.dds_list[lev_index] * si.astype( self.float_type ) self.grid_right_edge[i] = self.dds_list[lev_index] * ( ei.astype(self.float_type) + 1 ) self.grid_particle_count[i] = 0 self.grid_dimensions[i] = ei - si + 1 i += 1 self.grids = np.empty(len(grids), dtype="object") for gi, g in enumerate(grids): self.grids[gi] = g def _populate_grid_objects(self): self._reconstruct_parent_child() for g in self.grids: g._prepare_grid() g._setup_dx() def _setup_derived_fields(self): self.derived_field_list = [] def _reconstruct_parent_child(self): mask = np.empty(len(self.grids), dtype="int32") mylog.debug("First pass; identifying child grids") for i, grid in enumerate(self.grids): get_box_grids_level( self.grid_left_edge[i, :], self.grid_right_edge[i, :], self.grid_levels[i].item() + 1, self.grid_left_edge, self.grid_right_edge, self.grid_levels, mask, ) ids = np.where(mask.astype("bool")) # where is a tuple grid._children_ids = ids[0] + grid._id_offset mylog.debug("Second pass; identifying parents") for i, grid in enumerate(self.grids): # Second pass for child in grid.Children: child._parent_id.append(i + grid._id_offset) class ChomboDataset(Dataset): _load_requirements = ["h5py"] _index_class = ChomboHierarchy _field_info_class: type[FieldInfoContainer] = ChomboFieldInfo def __init__( self, filename, dataset_type="chombo_hdf5", storage_filename=None, ini_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): self.fluid_types += ("chombo",) self._handle = HDF5FileHandler(filename) self.dataset_type = dataset_type self.geometry = Geometry.CARTESIAN self.ini_filename = ini_filename self.fullplotdir = os.path.abspath(filename) Dataset.__init__( self, filename, self.dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename self.cosmological_simulation = False # These are parameters that I very much wish to get rid of. self.parameters["HydroMethod"] = "chombo" self.parameters["DualEnergyFormalism"] = 0 self.parameters["EOSType"] = -1 # default def _set_code_unit_attributes(self): if not hasattr(self, "length_unit"): mylog.warning("Setting code length unit to be 1.0 cm") if not hasattr(self, "mass_unit"): mylog.warning("Setting code mass unit to be 1.0 g") if not hasattr(self, "time_unit"): mylog.warning("Setting code time unit to be 1.0 s") setdefaultattr(self, "length_unit", self.quan(1.0, "cm")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "magnetic_unit", self.quan(np.sqrt(4.0 * np.pi), "gauss")) setdefaultattr(self, "velocity_unit", self.length_unit / self.time_unit) def _localize(self, f, default): if f is None: return os.path.join(self.directory, default) return f def _parse_parameter_file(self): self.dimensionality = self._handle["Chombo_global/"].attrs["SpaceDim"] self.domain_left_edge = self._calc_left_edge() self.domain_right_edge = self._calc_right_edge() self.domain_dimensions = self._calc_domain_dimensions() # if a lower-dimensional dataset, set up pseudo-3D stuff here. if self.dimensionality == 1: self.domain_left_edge = np.concatenate((self.domain_left_edge, [0.0, 0.0])) self.domain_right_edge = np.concatenate( (self.domain_right_edge, [1.0, 1.0]) ) self.domain_dimensions = np.concatenate((self.domain_dimensions, [1, 1])) if self.dimensionality == 2: self.domain_left_edge = np.concatenate((self.domain_left_edge, [0.0])) self.domain_right_edge = np.concatenate((self.domain_right_edge, [1.0])) self.domain_dimensions = np.concatenate((self.domain_dimensions, [1])) self.refine_by = self._handle["/level_0"].attrs["ref_ratio"] self._determine_periodic() self._determine_current_time() def _determine_current_time(self): # some datasets will not be time-dependent, and to make # matters worse, the simulation time is not always # stored in the same place in the hdf file! Make # sure we handle that here. try: self.current_time = self._handle.attrs["time"] except KeyError: try: self.current_time = self._handle["/level_0"].attrs["time"] except KeyError: self.current_time = 0.0 def _determine_periodic(self): # we default to true unless the HDF5 file says otherwise is_periodic = np.array([True, True, True]) for dir in range(self.dimensionality): try: is_periodic[dir] = self._handle["/level_0"].attrs[ "is_periodic_%d" % dir ] except KeyError: is_periodic[dir] = True self._periodicity = tuple(is_periodic) def _calc_left_edge(self): fileh = self._handle dx0 = fileh["/level_0"].attrs["dx"] D = self.dimensionality LE = dx0 * ((np.array(list(fileh["/level_0"].attrs["prob_domain"])))[0:D]) return LE def _calc_right_edge(self): fileh = self._handle dx0 = fileh["/level_0"].attrs["dx"] D = self.dimensionality RE = dx0 * ((np.array(list(fileh["/level_0"].attrs["prob_domain"])))[D:] + 1) return RE def _calc_domain_dimensions(self): fileh = self._handle D = self.dimensionality L_index = (np.array(list(fileh["/level_0"].attrs["prob_domain"])))[0:D] R_index = (np.array(list(fileh["/level_0"].attrs["prob_domain"])))[D:] + 1 return R_index - L_index @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False if not is_chombo_hdf5(filename): return False pluto_ini_file_exists = False orion2_ini_file_exists = False if isinstance(filename, str): dir_name = os.path.dirname(os.path.abspath(filename)) pluto_ini_filename = os.path.join(dir_name, "pluto.ini") orion2_ini_filename = os.path.join(dir_name, "orion2.ini") pluto_ini_file_exists = os.path.isfile(pluto_ini_filename) orion2_ini_file_exists = os.path.isfile(orion2_ini_filename) if not (pluto_ini_file_exists or orion2_ini_file_exists): try: fileh = h5py.File(filename, mode="r") valid = "Chombo_global" in fileh["/"] # ORION2 simulations should always have this: valid = valid and "CeilVA_mass" not in fileh.attrs.keys() valid = valid and "Charm_global" not in fileh.keys() fileh.close() return valid except Exception: pass return False @parallel_root_only def print_key_parameters(self): for a in [ "current_time", "domain_dimensions", "domain_left_edge", "domain_right_edge", ]: if not hasattr(self, a): mylog.error("Missing %s in parameter file definition!", a) continue v = getattr(self, a) mylog.info("Parameters: %-25s = %s", a, v) class PlutoHierarchy(ChomboHierarchy): def __init__(self, ds, dataset_type="chombo_hdf5"): ChomboHierarchy.__init__(self, ds, dataset_type) def _parse_index(self): f = self._handle # shortcut self.max_level = f.attrs["num_levels"] - 1 grids = [] self.dds_list = [] i = 0 D = self.dataset.dimensionality for lev_index, lev in enumerate(self._levels): level_number = int(re.match(r"level_(\d+)", lev).groups()[0]) try: boxes = f[lev]["boxes"][()] except KeyError: boxes = f[lev]["particles:boxes"][()] dx = f[lev].attrs["dx"] self.dds_list.append(dx * np.ones(3)) if D == 1: self.dds_list[lev_index][1] = 1.0 self.dds_list[lev_index][2] = 1.0 if D == 2: self.dds_list[lev_index][2] = 1.0 for level_id, box in enumerate(boxes): si = np.array([box[f"lo_{ax}"] for ax in "ijk"[:D]]) ei = np.array([box[f"hi_{ax}"] for ax in "ijk"[:D]]) if D == 1: si = np.concatenate((si, [0.0, 0.0])) ei = np.concatenate((ei, [0.0, 0.0])) if D == 2: si = np.concatenate((si, [0.0])) ei = np.concatenate((ei, [0.0])) pg = self.grid(len(grids), self, level=level_number, start=si, stop=ei) grids.append(pg) grids[-1]._level_id = level_id self.grid_levels[i] = level_number self.grid_left_edge[i] = ( self.dds_list[lev_index] * si.astype(self.float_type) + self.domain_left_edge.value ) self.grid_right_edge[i] = ( self.dds_list[lev_index] * (ei.astype(self.float_type) + 1) + self.domain_left_edge.value ) self.grid_particle_count[i] = 0 self.grid_dimensions[i] = ei - si + 1 i += 1 self.grids = np.empty(len(grids), dtype="object") for gi, g in enumerate(grids): self.grids[gi] = g class PlutoDataset(ChomboDataset): _index_class = PlutoHierarchy _field_info_class = PlutoFieldInfo def __init__( self, filename, dataset_type="chombo_hdf5", storage_filename=None, ini_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): ChomboDataset.__init__( self, filename, dataset_type, storage_filename, ini_filename, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) def _parse_parameter_file(self): """ Check to see whether a 'pluto.ini' file exists in the plot file directory. If one does, attempt to parse it. Otherwise grab the dimensions from the hdf5 file. """ pluto_ini_file_exists = False dir_name = os.path.dirname(os.path.abspath(self.fullplotdir)) pluto_ini_filename = os.path.join(dir_name, "pluto.ini") pluto_ini_file_exists = os.path.isfile(pluto_ini_filename) self.dimensionality = self._handle["Chombo_global/"].attrs["SpaceDim"] self.domain_dimensions = self._calc_domain_dimensions() self.refine_by = self._handle["/level_0"].attrs["ref_ratio"] if pluto_ini_file_exists: lines = [line.strip() for line in open(pluto_ini_filename)] domain_left_edge = np.zeros(self.dimensionality) domain_right_edge = np.zeros(self.dimensionality) for il, ll in enumerate( lines[ lines.index("[Grid]") + 2 : lines.index("[Grid]") + 2 + self.dimensionality ] ): domain_left_edge[il] = float(ll.split()[2]) domain_right_edge[il] = float(ll.split()[-1]) self._periodicity = [0] * 3 for il, ll in enumerate( lines[ lines.index("[Boundary]") + 2 : lines.index("[Boundary]") + 2 + 6 : 2 ] ): self.periodicity[il] = ll.split()[1] == "periodic" self._periodicity = tuple(self.periodicity) for ll in lines[lines.index("[Parameters]") + 2 :]: if ll.split()[0] == "GAMMA": self.gamma = float(ll.split()[1]) self.domain_left_edge = domain_left_edge self.domain_right_edge = domain_right_edge else: self.domain_left_edge = self._calc_left_edge() self.domain_right_edge = self._calc_right_edge() self._periodicity = (True, True, True) # if a lower-dimensional dataset, set up pseudo-3D stuff here. if self.dimensionality == 1: self.domain_left_edge = np.concatenate((self.domain_left_edge, [0.0, 0.0])) self.domain_right_edge = np.concatenate( (self.domain_right_edge, [1.0, 1.0]) ) self.domain_dimensions = np.concatenate((self.domain_dimensions, [1, 1])) if self.dimensionality == 2: self.domain_left_edge = np.concatenate((self.domain_left_edge, [0.0])) self.domain_right_edge = np.concatenate((self.domain_right_edge, [1.0])) self.domain_dimensions = np.concatenate((self.domain_dimensions, [1])) self._determine_current_time() @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False if not is_chombo_hdf5(filename): return False pluto_ini_file_exists = False if isinstance(filename, str): dir_name = os.path.dirname(os.path.abspath(filename)) pluto_ini_filename = os.path.join(dir_name, "pluto.ini") pluto_ini_file_exists = os.path.isfile(pluto_ini_filename) if pluto_ini_file_exists: return True return False class Orion2Hierarchy(ChomboHierarchy): def __init__(self, ds, dataset_type="orion_chombo_native"): ChomboHierarchy.__init__(self, ds, dataset_type) def _detect_output_fields(self): # look for fluid fields output_fields = [] for key, val in self._handle.attrs.items(): if key.startswith("component"): output_fields.append(val.decode("ascii")) self.field_list = [("chombo", c) for c in output_fields] # look for particle fields self.particle_filename = self.index_filename[:-4] + "sink" if not os.path.exists(self.particle_filename): return pfield_list = [("io", str(c)) for c in self.io.particle_field_index.keys()] self.field_list.extend(pfield_list) def _read_particles(self): if not os.path.exists(self.particle_filename): return with open(self.particle_filename) as f: lines = f.readlines() self.num_stars = int(lines[0].strip().split(" ")[0]) for num, line in enumerate(lines[1:]): particle_position_x = float(line.split(" ")[1]) particle_position_y = float(line.split(" ")[2]) particle_position_z = float(line.split(" ")[3]) coord = [particle_position_x, particle_position_y, particle_position_z] # for each particle, determine which grids contain it # copied from object_finding_mixin.py mask = np.ones(self.num_grids) for i in range(len(coord)): np.choose( np.greater(self.grid_left_edge.d[:, i], coord[i]), (mask, 0), mask, ) np.choose( np.greater(self.grid_right_edge.d[:, i], coord[i]), (0, mask), mask, ) ind = np.where(mask == 1) selected_grids = self.grids[ind] # in orion, particles always live on the finest level. # so, we want to assign the particle to the finest of # the grids we just found if len(selected_grids) != 0: grid = sorted(selected_grids, key=lambda grid: grid.Level)[-1] ind = np.where(self.grids == grid)[0][0] self.grid_particle_count[ind] += 1 self.grids[ind].NumberOfParticles += 1 # store the position in the *.sink file for fast access. try: self.grids[ind]._particle_line_numbers.append(num + 1) except AttributeError: self.grids[ind]._particle_line_numbers = [num + 1] class Orion2Dataset(ChomboDataset): _load_requirements = ["h5py"] _index_class = Orion2Hierarchy _field_info_class = Orion2FieldInfo def __init__( self, filename, dataset_type="orion_chombo_native", storage_filename=None, ini_filename=None, units_override=None, default_species_fields=None, ): ChomboDataset.__init__( self, filename, dataset_type, storage_filename, ini_filename, units_override=units_override, default_species_fields=default_species_fields, ) def _parse_parameter_file(self): """ Check to see whether an 'orion2.ini' file exists in the plot file directory. If one does, attempt to parse it. Otherwise grab the dimensions from the hdf5 file. """ orion2_ini_file_exists = False dir_name = os.path.dirname(os.path.abspath(self.fullplotdir)) orion2_ini_filename = os.path.join(dir_name, "orion2.ini") orion2_ini_file_exists = os.path.isfile(orion2_ini_filename) if orion2_ini_file_exists: self._parse_inputs_file("orion2.ini") self.dimensionality = 3 self.domain_left_edge = self._calc_left_edge() self.domain_right_edge = self._calc_right_edge() self.domain_dimensions = self._calc_domain_dimensions() self.refine_by = self._handle["/level_0"].attrs["ref_ratio"] self._determine_periodic() self._determine_current_time() def _parse_inputs_file(self, ini_filename): self.fullplotdir = os.path.abspath(self.parameter_filename) self.ini_filename = self._localize(self.ini_filename, ini_filename) lines = open(self.ini_filename).readlines() # read the file line by line, storing important parameters for line in lines: try: param, sep, vals = line.partition("=") if not sep: # No = sign present, so split by space instead param, sep, vals = line.partition(" ") param = param.strip() vals = vals.strip() if not param: # skip blank lines continue if param[0] == "#": # skip comment lines continue if param[0] == "[": # skip stanza headers continue vals = vals.partition("#")[0] # strip trailing comments try: self.parameters[param] = np.int64(vals) except ValueError: try: self.parameters[param] = np.float64(vals) except ValueError: self.parameters[param] = vals except ValueError: mylog.error("ValueError: '%s'", line) if param == "GAMMA": self.gamma = np.float64(vals) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False if not is_chombo_hdf5(filename): return False pluto_ini_file_exists = False orion2_ini_file_exists = False if isinstance(filename, str): dir_name = os.path.dirname(os.path.abspath(filename)) pluto_ini_filename = os.path.join(dir_name, "pluto.ini") orion2_ini_filename = os.path.join(dir_name, "orion2.ini") pluto_ini_file_exists = os.path.isfile(pluto_ini_filename) orion2_ini_file_exists = os.path.isfile(orion2_ini_filename) if orion2_ini_file_exists: return True if not pluto_ini_file_exists: try: fileh = h5py.File(filename, mode="r") valid = "CeilVA_mass" in fileh.attrs.keys() valid = ( "Chombo_global" in fileh["/"] and "Charm_global" not in fileh["/"] ) valid = valid and "CeilVA_mass" in fileh.attrs.keys() fileh.close() return valid except Exception: pass return False class ChomboPICHierarchy(ChomboHierarchy): def __init__(self, ds, dataset_type="chombo_hdf5"): ChomboHierarchy.__init__(self, ds, dataset_type) class ChomboPICDataset(ChomboDataset): _load_requirements = ["h5py"] _index_class = ChomboPICHierarchy _field_info_class = ChomboPICFieldInfo3D def __init__( self, filename, dataset_type="chombo_hdf5", storage_filename=None, ini_filename=None, units_override=None, ): ChomboDataset.__init__( self, filename, dataset_type, storage_filename, ini_filename, units_override=units_override, ) if self.dimensionality == 1: self._field_info_class = ChomboPICFieldInfo1D if self.dimensionality == 2: self._field_info_class = ChomboPICFieldInfo2D @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not valid_hdf5_signature(filename): return False if cls._missing_load_requirements(): return False if not is_chombo_hdf5(filename): return False pluto_ini_file_exists = False orion2_ini_file_exists = False if isinstance(filename, str): dir_name = os.path.dirname(os.path.abspath(filename)) pluto_ini_filename = os.path.join(dir_name, "pluto.ini") orion2_ini_filename = os.path.join(dir_name, "orion2.ini") pluto_ini_file_exists = os.path.isfile(pluto_ini_filename) orion2_ini_file_exists = os.path.isfile(orion2_ini_filename) if orion2_ini_file_exists: return False if pluto_ini_file_exists: return False try: fileh = h5py.File(filename, mode="r") valid = "Charm_global" in fileh["/"] fileh.close() return valid except Exception: pass return False yt-project-yt-f043ac8/yt/frontends/chombo/definitions.py000066400000000000000000000000001510711153200234320ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chombo/fields.py000066400000000000000000000361441510711153200224070ustar00rootroot00000000000000import numpy as np from yt._typing import KnownFieldsT from yt.fields.field_info_container import ( FieldInfoContainer, particle_deposition_functions, particle_vector_functions, standard_particle_fields, ) from yt.units.unit_object import Unit # type: ignore from yt.utilities.exceptions import YTFieldNotFound rho_units = "code_mass / code_length**3" mom_units = "code_mass / (code_time * code_length**2)" eden_units = "code_mass / (code_time**2 * code_length)" # erg / cm^3 vel_units = "code_length / code_time" b_units = "code_magnetic" class ChomboFieldInfo(FieldInfoContainer): # no custom behaviour is needed yet pass # Orion 2 Fields # We duplicate everything here from Boxlib, because we want to be able to # subclass it and that can be somewhat tricky. class Orion2FieldInfo(ChomboFieldInfo): known_other_fields: KnownFieldsT = ( ("density", (rho_units, ["density"], None)), ("energy-density", (eden_units, ["total_energy_density"], None)), ("radiation-energy-density", (eden_units, ["radiation_energy_density"], None)), ("X-momentum", (mom_units, ["momentum_density_x"], None)), ("Y-momentum", (mom_units, ["momentum_density_y"], None)), ("Z-momentum", (mom_units, ["momentum_density_z"], None)), ("temperature", ("K", ["temperature"], None)), ("X-magnfield", (b_units, [], None)), ("Y-magnfield", (b_units, [], None)), ("Z-magnfield", (b_units, [], None)), ("directrad-dedt-density", (eden_units, ["directrad-dedt-density"], None)), ("directrad-dpxdt-density", (mom_units, ["directrad-dpxdt-density"], None)), ("directrad-dpydt-density", (mom_units, ["directrad-dpydt-density"], None)), ("directrad-dpzdt-density", (mom_units, ["directrad-dpzdt-density"], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_mass", ("code_mass", [], None)), ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_momentum_x", ("code_mass*code_length/code_time", [], None)), ("particle_momentum_y", ("code_mass*code_length/code_time", [], None)), ("particle_momentum_z", ("code_mass*code_length/code_time", [], None)), # Note that these are *internal* agmomen ("particle_angmomen_x", ("code_length**2/code_time", [], None)), ("particle_angmomen_y", ("code_length**2/code_time", [], None)), ("particle_angmomen_z", ("code_length**2/code_time", [], None)), ("particle_mlast", ("code_mass", [], None)), ("particle_r", ("code_length", [], None)), ("particle_mdeut", ("code_mass", [], None)), ("particle_n", ("", [], None)), ("particle_mdot", ("code_mass/code_time", [], None)), ("particle_burnstate", ("", [], None)), ("particle_luminosity", ("", [], None)), ("particle_id", ("", ["particle_index"], None)), ) def setup_particle_fields(self, ptype): def _get_vel(axis): def velocity(field, data): return ( data[ptype, f"particle_momentum_{axis}"] / data[ptype, "particle_mass"] ) return velocity for ax in "xyz": self.add_field( (ptype, f"particle_velocity_{ax}"), sampling_type="particle", function=_get_vel(ax), units="code_length/code_time", ) super().setup_particle_fields(ptype) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases unit_system = self.ds.unit_system def _thermal_energy_density(field, data): try: return ( data["chombo", "energy-density"] - data["gas", "kinetic_energy_density"] - data["gas", "magnetic_energy_density"] ) except YTFieldNotFound: return ( data["chombo", "energy-density"] - data["gas", "kinetic_energy_density"] ) def _specific_thermal_energy(field, data): return data["gas", "thermal_energy_density"] / data["gas", "density"] def _magnetic_energy_density(field, data): ret = data["chombo", "X-magnfield"] ** 2 if data.ds.dimensionality > 1: ret = ret + data["chombo", "Y-magnfield"] ** 2 if data.ds.dimensionality > 2: ret = ret + data["chombo", "Z-magnfield"] ** 2 return ret / 8.0 / np.pi def _specific_magnetic_energy(field, data): return data["gas", "specific_magnetic_energy"] / data["gas", "density"] def _kinetic_energy_density(field, data): p2 = data["chombo", "X-momentum"] ** 2 if data.ds.dimensionality > 1: p2 = p2 + data["chombo", "Y-momentum"] ** 2 if data.ds.dimensionality > 2: p2 = p2 + data["chombo", "Z-momentum"] ** 2 return 0.5 * p2 / data["gas", "density"] def _specific_kinetic_energy(field, data): return data["gas", "kinetic_energy_density"] / data["gas", "density"] def _temperature(field, data): c_v = data.ds.quan(data.ds.parameters["radiation.const_cv"], "erg/g/K") return data["gas", "specific_thermal_energy"] / c_v def _get_vel(axis): def velocity(field, data): return data["gas", f"momentum_density_{axis}"] / data["gas", "density"] return velocity for ax in "xyz": self.add_field( ("gas", f"velocity_{ax}"), sampling_type="cell", function=_get_vel(ax), units=unit_system["velocity"], ) self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) self.add_field( ("gas", "thermal_energy_density"), sampling_type="cell", function=_thermal_energy_density, units=unit_system["pressure"], ) self.add_field( ("gas", "kinetic_energy_density"), sampling_type="cell", function=_kinetic_energy_density, units=unit_system["pressure"], ) self.add_field( ("gas", "specific_kinetic_energy"), sampling_type="cell", function=_specific_kinetic_energy, units=unit_system["specific_energy"], ) self.add_field( ("gas", "magnetic_energy_density"), sampling_type="cell", function=_magnetic_energy_density, units=unit_system["pressure"], ) self.add_field( ("gas", "specific_magnetic_energy"), sampling_type="cell", function=_specific_magnetic_energy, units=unit_system["specific_energy"], ) self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) setup_magnetic_field_aliases( self, "chombo", [f"{ax}-magnfield" for ax in "XYZ"] ) class ChomboPICFieldInfo3D(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", (rho_units, ["density", "Density"], None)), ( "potential", ("code_length**2 / code_time**2", ["potential", "Potential"], None), ), ("gravitational_field_x", ("code_length / code_time**2", [], None)), ("gravitational_field_y", ("code_length / code_time**2", [], None)), ("gravitational_field_z", ("code_length / code_time**2", [], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_mass", ("code_mass", [], None)), ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_velocity_x", ("code_length / code_time", [], None)), ("particle_velocity_y", ("code_length / code_time", [], None)), ("particle_velocity_z", ("code_length / code_time", [], None)), ) # I am re-implementing this here to override a few default behaviors: # I don't want to skip output units for code_length and I do want # particle_fields to default to take_log = False. def setup_particle_fields(self, ptype, ftype="gas", num_neighbors=64): skip_output_units = () for f, (units, aliases, dn) in sorted(self.known_particle_fields): units = self.ds.field_units.get((ptype, f), units) if ( f in aliases or ptype not in self.ds.particle_types_raw ) and units not in skip_output_units: u = Unit(units, registry=self.ds.unit_registry) output_units = str(u.get_cgs_equivalent()) else: output_units = units if (ptype, f) not in self.field_list: continue self.add_output_field( (ptype, f), sampling_type="particle", units=units, display_name=dn, output_units=output_units, take_log=False, ) for alias in aliases: self.alias((ptype, alias), (ptype, f), units=output_units) ppos_fields = [f"particle_position_{ax}" for ax in "xyz"] pvel_fields = [f"particle_velocity_{ax}" for ax in "xyz"] particle_vector_functions(ptype, ppos_fields, pvel_fields, self) particle_deposition_functions(ptype, "particle_position", "particle_mass", self) standard_particle_fields(self, ptype) # Now we check for any leftover particle fields for field in sorted(self.field_list): if field in self: continue if not isinstance(field, tuple): raise RuntimeError if field[0] not in self.ds.particle_types: continue self.add_output_field( field, sampling_type="particle", units=self.ds.field_units.get(field, ""), ) self.setup_smoothed_fields(ptype, num_neighbors=num_neighbors, ftype=ftype) def _dummy_position(field, data): return 0.5 * np.ones_like(data["all", "particle_position_x"]) def _dummy_velocity(field, data): return np.zeros_like(data["all", "particle_velocity_x"]) def _dummy_field(field, data): return 0.0 * data["chombo", "gravitational_field_x"] fluid_field_types = ["chombo", "gas"] particle_field_types = ["io", "all"] class ChomboPICFieldInfo2D(ChomboPICFieldInfo3D): known_other_fields: KnownFieldsT = ( ("density", (rho_units, ["density", "Density"], None)), ( "potential", ("code_length**2 / code_time**2", ["potential", "Potential"], None), ), ("gravitational_field_x", ("code_length / code_time**2", [], None)), ("gravitational_field_y", ("code_length / code_time**2", [], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_mass", ("code_mass", [], None)), ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_velocity_x", ("code_length / code_time", [], None)), ("particle_velocity_y", ("code_length / code_time", [], None)), ) def __init__(self, ds, field_list): super().__init__(ds, field_list) for ftype in fluid_field_types: self.add_field( (ftype, "gravitational_field_z"), sampling_type="cell", function=_dummy_field, units="code_length / code_time**2", ) for ptype in particle_field_types: self.add_field( (ptype, "particle_position_z"), sampling_type="particle", function=_dummy_position, units="code_length", ) self.add_field( (ptype, "particle_velocity_z"), sampling_type="particle", function=_dummy_velocity, units="code_length / code_time", ) class ChomboPICFieldInfo1D(ChomboPICFieldInfo3D): known_other_fields: KnownFieldsT = ( ("density", (rho_units, ["density", "Density"], None)), ( "potential", ("code_length**2 / code_time**2", ["potential", "Potential"], None), ), ("gravitational_field_x", ("code_length / code_time**2", [], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_mass", ("code_mass", [], None)), ("particle_position_x", ("code_length", [], None)), ("particle_velocity_x", ("code_length / code_time", [], None)), ) def __init__(self, ds, field_list): super().__init__(ds, field_list) for ftype in fluid_field_types: self.add_field( (ftype, "gravitational_field_y"), sampling_type="cell", function=_dummy_field, units="code_length / code_time**2", ) self.add_field( (ftype, "gravitational_field_z"), sampling_type="cell", function=_dummy_field, units="code_length / code_time**2", ) for ptype in particle_field_types: self.add_field( (ptype, "particle_position_y"), sampling_type="particle", function=_dummy_position, units="code_length", ) self.add_field( (ptype, "particle_position_z"), sampling_type="particle", function=_dummy_position, units="code_length", ) self.add_field( (ptype, "particle_velocity_y"), sampling_type="particle", function=_dummy_velocity, units="code_length / code_time", ) self.add_field( (ptype, "particle_velocity_z"), sampling_type="particle", function=_dummy_velocity, units="code_length / code_time", ) class PlutoFieldInfo(ChomboFieldInfo): known_other_fields: KnownFieldsT = ( ("rho", (rho_units, ["density"], None)), ("prs", ("code_mass / (code_length * code_time**2)", ["pressure"], None)), ("vx1", (vel_units, ["velocity_x"], None)), ("vx2", (vel_units, ["velocity_y"], None)), ("vx3", (vel_units, ["velocity_z"], None)), ("bx1", (b_units, [], None)), ("bx2", (b_units, [], None)), ("bx3", (b_units, [], None)), ) known_particle_fields = () def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases setup_magnetic_field_aliases(self, "chombo", [f"bx{ax}" for ax in [1, 2, 3]]) yt-project-yt-f043ac8/yt/frontends/chombo/io.py000066400000000000000000000241541510711153200215460ustar00rootroot00000000000000import re import numpy as np from yt.geometry.selection_routines import GridSelector from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog class IOHandlerChomboHDF5(BaseIOHandler): _dataset_type = "chombo_hdf5" _offset_string = "data:offsets=0" _data_string = "data:datatype=0" _offsets = None def __init__(self, ds, *args, **kwargs): BaseIOHandler.__init__(self, ds, *args, **kwargs) self.ds = ds self._handle = ds._handle self.dim = self._handle["Chombo_global/"].attrs["SpaceDim"] self._read_ghost_info() if self._offset_string not in self._handle["level_0"]: self._calculate_offsets() def _calculate_offsets(self): def box_size(corners): size = 1 for idim in range(self.dim): size *= corners[idim + self.dim] - corners[idim] + 1 return size self._offsets = {} num_comp = self._handle.attrs["num_components"] level = 0 while True: lname = "level_%i" % level if lname not in self._handle: break boxes = self._handle["level_0"]["boxes"][()] box_sizes = np.array([box_size(box) for box in boxes]) offsets = np.cumsum(box_sizes * num_comp, dtype="int64") offsets -= offsets[0] self._offsets[level] = offsets level += 1 def _read_ghost_info(self): try: self.ghost = tuple( self._handle["level_0/data_attributes"].attrs["outputGhost"] ) # pad with zeros if the dataset is low-dimensional self.ghost += (3 - self.dim) * (0,) self.ghost = np.array(self.ghost) except KeyError: # assume zero ghosts if outputGhosts not present self.ghost = np.zeros(self.dim, "int64") _field_dict = None @property def field_dict(self): if self._field_dict is not None: return self._field_dict field_dict = {} for key, val in self._handle.attrs.items(): if key.startswith("component_"): comp_number = int(re.match(r"component_(\d+)", key).groups()[0]) field_dict[val.decode("utf-8")] = comp_number self._field_dict = field_dict return self._field_dict _particle_field_index = None @property def particle_field_index(self): if self._particle_field_index is not None: return self._particle_field_index field_dict = {} for key, val in self._handle.attrs.items(): if key.startswith("particle_"): comp_number = int( re.match(r"particle_component_(\d+)", key).groups()[0] ) field_dict[val.decode("ascii")] = comp_number self._particle_field_index = field_dict return self._particle_field_index def _read_data(self, grid, field): lstring = "level_%i" % grid.Level lev = self._handle[lstring] dims = grid.ActiveDimensions shape = dims + 2 * self.ghost boxsize = shape.prod() if self._offsets is not None: grid_offset = self._offsets[grid.Level][grid._level_id] else: grid_offset = lev[self._offset_string][grid._level_id] start = grid_offset + self.field_dict[field] * boxsize stop = start + boxsize data = lev[self._data_string][start:stop] data_no_ghost = data.reshape(shape, order="F") ghost_slice = tuple( slice(g, g + d) for g, d in zip(self.ghost, dims, strict=True) ) ghost_slice = ghost_slice[0 : self.dim] return data_no_ghost[ghost_slice] def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} chunks = list(chunks) fields.sort(key=lambda a: self.field_dict[a[1]]) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError grid = chunks[0].objs[0] for ftype, fname in fields: rv[ftype, fname] = self._read_data(grid, fname) return rv if size is None: size = sum(g.count(selector) for chunk in chunks for g in chunk.objs) for field in fields: ftype, fname = field fsize = size rv[field] = np.empty(fsize, dtype="float64") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) ind = 0 for chunk in chunks: for g in chunk.objs: nd = 0 for field in fields: ftype, fname = field data = self._read_data(g, fname) nd = g.select(selector, data, rv[field], ind) # caches ind += nd return rv def _read_particle_selection(self, chunks, selector, fields): rv = {} chunks = list(chunks) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError grid = chunks[0].objs[0] for ftype, fname in fields: rv[ftype, fname] = self._read_particles(grid, fname) return rv rv = {f: np.array([]) for f in fields} for chunk in chunks: for grid in chunk.objs: for ftype, fname in fields: data = self._read_particles(grid, fname) rv[ftype, fname] = np.concatenate((data, rv[ftype, fname])) return rv def _read_particles(self, grid, name): field_index = self.particle_field_index[name] lev = f"level_{grid.Level}" particles_per_grid = self._handle[lev]["particles:offsets"][()] items_per_particle = len(self._particle_field_index) # compute global offset position offsets = items_per_particle * np.cumsum(particles_per_grid) offsets = np.append(np.array([0]), offsets) offsets = np.array(offsets, dtype=np.int64) # convert between the global grid id and the id on this level grid_levels = np.array([g.Level for g in self.ds.index.grids]) grid_ids = np.array([g.id for g in self.ds.index.grids]) grid_level_offset = grid_ids[np.where(grid_levels == grid.Level)[0][0]] lo = grid.id - grid_level_offset hi = lo + 1 # handle the case where this grid has no particles if offsets[lo] == offsets[hi]: return np.array([], dtype=np.float64) data = self._handle[lev]["particles:data"][offsets[lo] : offsets[hi]] return np.asarray( data[field_index::items_per_particle], dtype=np.float64, order="F" ) def parse_orion_sinks(fn): r""" Orion sink particles are stored in text files. This function is for figuring what particle fields are present based on the number of entries per line in the \*.sink file. """ # Figure out the format of the particle file with open(fn) as f: lines = f.readlines() try: line = lines[1] except IndexError: # a particle file exists, but there is only one line, # so no sinks have been created yet. index = {} return index # The basic fields that all sink particles have index = { "particle_mass": 0, "particle_position_x": 1, "particle_position_y": 2, "particle_position_z": 3, "particle_momentum_x": 4, "particle_momentum_y": 5, "particle_momentum_z": 6, "particle_angmomen_x": 7, "particle_angmomen_y": 8, "particle_angmomen_z": 9, "particle_id": -1, } if len(line.strip().split()) == 11: # these are vanilla sinks, do nothing pass elif len(line.strip().split()) == 17: # these are old-style stars, add stellar model parameters index["particle_mlast"] = 10 index["particle_r"] = 11 index["particle_mdeut"] = 12 index["particle_n"] = 13 index["particle_mdot"] = 14 index["particle_burnstate"] = 15 elif len(line.strip().split()) == 18 or len(line.strip().split()) == 19: # these are the newer style, add luminosity as well index["particle_mlast"] = 10 index["particle_r"] = 11 index["particle_mdeut"] = 12 index["particle_n"] = 13 index["particle_mdot"] = 14 index["particle_burnstate"] = 15 index["particle_luminosity"] = 16 else: # give a warning if none of the above apply: mylog.warning("Warning - could not figure out particle output file") mylog.warning("These results could be nonsense!") return index class IOHandlerOrion2HDF5(IOHandlerChomboHDF5): _dataset_type = "orion_chombo_native" _particle_field_index = None @property def particle_field_index(self): fn = self.ds.fullplotdir[:-4] + "sink" index = parse_orion_sinks(fn) self._particle_field_index = index return self._particle_field_index def _read_particles(self, grid, field): """ parses the Orion Star Particle text files """ particles = [] if grid.NumberOfParticles == 0: return np.array(particles) def read(line, field): entry = line.strip().split(" ")[self.particle_field_index[field]] return float(entry) try: lines = self._cached_lines for num in grid._particle_line_numbers: line = lines[num] particles.append(read(line, field)) return np.array(particles) except AttributeError: fn = grid.ds.fullplotdir[:-4] + "sink" with open(fn) as f: lines = f.readlines() self._cached_lines = lines for num in grid._particle_line_numbers: line = lines[num] particles.append(read(line, field)) return np.array(particles) yt-project-yt-f043ac8/yt/frontends/chombo/misc.py000066400000000000000000000000001510711153200220520ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chombo/tests/000077500000000000000000000000001510711153200217215ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chombo/tests/__init__.py000066400000000000000000000000001510711153200240200ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/chombo/tests/test_outputs.py000066400000000000000000000050511510711153200250560ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.frontends.chombo.api import ChomboDataset, Orion2Dataset, PlutoDataset from yt.testing import requires_file, requires_module, units_override_check from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) _fields = ( ("gas", "density"), ("gas", "velocity_magnitude"), ("gas", "magnetic_field_x"), ) gc = "GaussianCloud/data.0077.3d.hdf5" @requires_ds(gc) def test_gc(): ds = data_dir_load(gc) assert_equal(str(ds), "data.0077.3d.hdf5") for test in small_patch_amr(ds, _fields): test_gc.__name__ = test.description yield test tb = "TurbBoxLowRes/data.0005.3d.hdf5" @requires_module("h5py") @requires_ds(tb) def test_tb(): ds = data_dir_load(tb) assert_equal(str(ds), "data.0005.3d.hdf5") for test in small_patch_amr(ds, _fields): test_tb.__name__ = test.description yield test iso = "IsothermalSphere/data.0000.3d.hdf5" @requires_module("h5py") @requires_ds(iso) def test_iso(): ds = data_dir_load(iso) assert_equal(str(ds), "data.0000.3d.hdf5") for test in small_patch_amr(ds, _fields): test_iso.__name__ = test.description yield test _zp_fields = (("chombo", "rhs"), ("chombo", "phi")) zp = "ZeldovichPancake/plt32.2d.hdf5" @requires_module("h5py") @requires_ds(zp) def test_zp(): ds = data_dir_load(zp) assert_equal(str(ds), "plt32.2d.hdf5") for test in small_patch_amr(ds, _zp_fields, input_center="c", input_weight="rhs"): test_zp.__name__ = test.description yield test kho = "KelvinHelmholtz/data.0004.hdf5" @requires_module("h5py") @requires_ds(kho) def test_kho(): ds = data_dir_load(kho) assert_equal(str(ds), "data.0004.hdf5") for test in small_patch_amr(ds, _fields): test_kho.__name__ = test.description yield test @requires_module("h5py") @requires_file(zp) def test_ChomboDataset(): assert isinstance(data_dir_load(zp), ChomboDataset) @requires_module("h5py") @requires_file(gc) def test_Orion2Dataset(): assert isinstance(data_dir_load(gc), Orion2Dataset) @requires_module("h5py") @requires_file(kho) def test_PlutoDataset(): assert isinstance(data_dir_load(kho), PlutoDataset) @requires_module("h5py") @requires_file(zp) def test_units_override_zp(): units_override_check(zp) @requires_module("h5py") @requires_file(gc) def test_units_override_gc(): units_override_check(gc) @requires_module("h5py") @requires_file(kho) def test_units_override_kho(): units_override_check(kho) yt-project-yt-f043ac8/yt/frontends/eagle/000077500000000000000000000000001510711153200203655ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/eagle/__init__.py000066400000000000000000000000001510711153200224640ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/eagle/api.py000066400000000000000000000002431510711153200215070ustar00rootroot00000000000000from . import tests from .data_structures import EagleDataset, EagleNetworkDataset from .fields import EagleNetworkFieldInfo from .io import IOHandlerEagleNetwork yt-project-yt-f043ac8/yt/frontends/eagle/data_structures.py000066400000000000000000000060021510711153200241510ustar00rootroot00000000000000import numpy as np import yt.units from yt.fields.field_info_container import FieldInfoContainer from yt.frontends.gadget.data_structures import GadgetHDF5Dataset from yt.frontends.owls.fields import OWLSFieldInfo from yt.utilities.on_demand_imports import _h5py as h5py from .fields import EagleNetworkFieldInfo class EagleDataset(GadgetHDF5Dataset): _load_requirements = ["h5py"] _particle_mass_name = "Mass" _field_info_class: type[FieldInfoContainer] = OWLSFieldInfo _time_readin_ = "Time" def _parse_parameter_file(self): # read values from header hvals = self._get_hvals() self.parameters = hvals # set features common to OWLS and Eagle self._set_owls_eagle() # Set time from analytic solution for flat LCDM universe a = hvals["ExpansionFactor"] H0 = hvals["H(z)"] / hvals["E(z)"] a_eq = (self.omega_matter / self.omega_lambda) ** (1.0 / 3) t1 = 2.0 / (3.0 * np.sqrt(self.omega_lambda)) t2 = (a / a_eq) ** (3.0 / 2) t3 = np.sqrt(1.0 + (a / a_eq) ** 3) t = t1 * np.log(t2 + t3) / H0 self.current_time = t * yt.units.s def _set_code_unit_attributes(self): self._set_owls_eagle_units() @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = [ "Config", "Constants", "HashTable", "Header", "Parameters", "RuntimePars", "Units", ] veto_groups = [ "SUBFIND", "PartType0/ChemistryAbundances", "PartType0/ChemicalAbundances", ] valid = True try: fileh = h5py.File(filename, mode="r") for ng in need_groups: if ng not in fileh["/"]: valid = False for vg in veto_groups: if vg in fileh["/"]: valid = False fileh.close() except Exception: valid = False pass return valid class EagleNetworkDataset(EagleDataset): _load_requirements = ["h5py"] _particle_mass_name = "Mass" _field_info_class = EagleNetworkFieldInfo _time_readin = "Time" @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: fileh = h5py.File(filename, mode="r") if ( "Constants" in fileh["/"].keys() and "Header" in fileh["/"].keys() and "SUBFIND" not in fileh["/"].keys() and ( "ChemistryAbundances" in fileh["PartType0"].keys() or "ChemicalAbundances" in fileh["PartType0"].keys() ) ): fileh.close() return True fileh.close() except Exception: pass return False yt-project-yt-f043ac8/yt/frontends/eagle/definitions.py000066400000000000000000000027041510711153200232550ustar00rootroot00000000000000eaglenetwork_ions = ( "electron", "H1", "H2", "H_m", "He1", "He2", "He3", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C_m", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", "O_m", "Ne1", "Ne2", "Ne3", "Ne4", "Ne5", "Ne6", "Ne7", "Ne8", "Ne9", "Ne10", "Ne11", "Mg1", "Mg2", "Mg3", "Mg4", "Mg5", "Mg6", "Mg7", "Mg8", "Mg9", "Mg10", "Mg11", "Mg12", "Mg13", "Si1", "Si2", "Si3", "Si4", "Si5", "Si6", "Si7", "Si8", "Si9", "Si10", "Si11", "Si12", "Si13", "Si14", "Si15", "Si16", "Si17", "Ca1", "Ca2", "Ca3", "Ca4", "Ca5", "Ca6", "Ca7", "Ca8", "Ca9", "Ca10", "Ca11", "Ca12", "Ca13", "Ca14", "Ca15", "Ca16", "Ca17", "Ca18", "Ca19", "Ca20", "Ca21", "Fe1", "Fe2", "Fe3", "Fe4", "Fe5", "Fe6", "Fe7", "Fe8", "Fe9", "Fe10", "Fe11", "Fe12", "Fe13", "Fe14", "Fe15", "Fe16", "Fe17", "Fe18", "Fe19", "Fe20", "Fe21", "Fe22", "Fe23", "Fe24", "Fe25", "Fe25", "Fe27", ) eaglenetwork_ion_lookup = {ion: index for index, ion in enumerate(eaglenetwork_ions)} yt-project-yt-f043ac8/yt/frontends/eagle/fields.py000066400000000000000000000056751510711153200222220ustar00rootroot00000000000000from yt.frontends.eagle.definitions import eaglenetwork_ion_lookup from yt.frontends.owls.fields import OWLSFieldInfo from yt.units.yt_array import YTQuantity from yt.utilities.periodic_table import periodic_table class EagleNetworkFieldInfo(OWLSFieldInfo): _ions = ( "H1", "H2", "He1", "He2", "He3", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", "Ne1", "Ne2", "Ne3", "Ne4", "Ne5", "Ne6", "Ne7", "Ne8", "Ne9", "Ne10", "Ne11", "Mg1", "Mg2", "Mg3", "Mg4", "Mg5", "Mg6", "Mg7", "Mg8", "Mg9", "Mg10", "Mg11", "Mg12", "Mg13", "Si1", "Si2", "Si3", "Si4", "Si5", "Si6", "Si7", "Si8", "Si9", "Si10", "Si11", "Si12", "Si13", "Si14", "Si15", "Si16", "Si17", "Ca1", "Ca2", "Ca3", "Ca4", "Ca5", "Ca6", "Ca7", "Ca8", "Ca9", "Ca10", "Ca11", "Ca12", "Ca13", "Ca14", "Ca15", "Ca16", "Ca17", "Ca18", "Ca19", "Ca20", "Ca21", "Fe1", "Fe2", "Fe3", "Fe4", "Fe5", "Fe6", "Fe7", "Fe8", "Fe9", "Fe10", "Fe11", "Fe12", "Fe13", "Fe14", "Fe15", "Fe16", "Fe17", "Fe18", "Fe19", "Fe20", "Fe21", "Fe22", "Fe23", "Fe24", "Fe25", "Fe25", "Fe27", ) def __init__(self, ds, field_list, slice_info=None): super().__init__(ds, field_list, slice_info=slice_info) def _create_ion_density_func(self, ftype, ion): """returns a function that calculates the ion density of a particle.""" def _ion_density(field, data): # Lookup the index of the ion index = eaglenetwork_ion_lookup[ion] # Ion to hydrogen number density ratio ion_chem = data[ftype, "Chemistry_%03i" % index] # Mass of a single ion if ion[0:2].isalpha(): symbol = ion[0:2].capitalize() else: symbol = ion[0:1].capitalize() m_ion = YTQuantity(periodic_table.elements_by_symbol[symbol].weight, "amu") # hydrogen number density n_H = data["PartType0", "H_number_density"] return m_ion * ion_chem * n_H return _ion_density yt-project-yt-f043ac8/yt/frontends/eagle/io.py000066400000000000000000000002011510711153200213370ustar00rootroot00000000000000from yt.frontends.owls.io import IOHandlerOWLS class IOHandlerEagleNetwork(IOHandlerOWLS): _dataset_type = "eagle_network" yt-project-yt-f043ac8/yt/frontends/eagle/tests/000077500000000000000000000000001510711153200215275ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/eagle/tests/__init__.py000066400000000000000000000000001510711153200236260ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/eagle/tests/test_outputs.py000066400000000000000000000013151510711153200246630ustar00rootroot00000000000000from yt.frontends.eagle.api import EagleDataset from yt.testing import ParticleSelectionComparison, requires_file, requires_module from yt.utilities.answer_testing.framework import data_dir_load s28 = "snapshot_028_z000p000/snap_028_z000p000.0.hdf5" @requires_module("h5py") @requires_file(s28) def test_EagleDataset(): ds = data_dir_load(s28) assert isinstance(ds, EagleDataset) psc = ParticleSelectionComparison(ds) psc.run_defaults() s399 = "snipshot_399_z000p000/snip_399_z000p000.0.hdf5" @requires_module("h5py") @requires_file(s399) def test_Snipshot(): ds = data_dir_load(s399) assert isinstance(ds, EagleDataset) psc = ParticleSelectionComparison(ds) psc.run_defaults() yt-project-yt-f043ac8/yt/frontends/enzo/000077500000000000000000000000001510711153200202635ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo/__init__.py000066400000000000000000000000001510711153200223620ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo/answer_testing_support.py000066400000000000000000000072521510711153200254730ustar00rootroot00000000000000import os from functools import wraps import numpy as np from yt.config import ytcfg from yt.loaders import load from yt.testing import assert_allclose from yt.utilities.answer_testing.framework import ( AnswerTestingTest, FieldValuesTest, GridValuesTest, ProjectionValuesTest, can_run_ds, temp_cwd, ) class AssertWrapper: """ Used to wrap a numpy testing assertion, in order to provide a useful name for a given assertion test. """ def __init__(self, description, *args): # The key here is to add a description attribute, which nose will pick # up. self.args = args self.description = description def __call__(self): self.args[0](*self.args[1:]) def requires_outputlog(path=".", prefix=""): from nose import SkipTest def ffalse(func): @wraps(func) def fskip(*args, **kwargs): raise SkipTest return fskip def ftrue(func): @wraps(func) def fyielder(*args, **kwargs): with temp_cwd(path): for t in func(*args, **kwargs): if isinstance(t, AnswerTestingTest): t.prefix = prefix yield t return fyielder if os.path.exists("OutputLog"): return ftrue with temp_cwd(path): if os.path.exists("OutputLog"): return ftrue return ffalse def standard_small_simulation(ds_fn, fields): if not can_run_ds(ds_fn): return dso = [None] tolerance = ytcfg.get("yt", "answer_testing_tolerance") bitwise = ytcfg.get("yt", "answer_testing_bitwise") for field in fields: if bitwise: yield GridValuesTest(ds_fn, field) if "particle" in field: continue for dobj_name in dso: for axis in [0, 1, 2]: for weight_field in [None, ("gas", "density")]: yield ProjectionValuesTest( ds_fn, axis, field, weight_field, dobj_name, decimals=tolerance ) yield FieldValuesTest(ds_fn, field, dobj_name, decimals=tolerance) class ShockTubeTest: def __init__( self, data_file, solution_file, fields, left_edges, right_edges, rtol, atol ): self.solution_file = solution_file self.data_file = data_file self.fields = fields self.left_edges = left_edges self.right_edges = right_edges self.rtol = rtol self.atol = atol def __call__(self): # Read in the ds ds = load(self.data_file) exact = self.get_analytical_solution() ad = ds.all_data() position = ad["index", "x"] for k in self.fields: field = ad[k].d for xmin, xmax in zip(self.left_edges, self.right_edges, strict=True): mask = (position >= xmin) * (position <= xmax) exact_field = np.interp(position[mask].ndview, exact["pos"], exact[k]) myname = f"ShockTubeTest_{k}" # yield test vs analytical solution yield AssertWrapper( myname, assert_allclose, field[mask], exact_field, self.rtol, self.atol, ) def get_analytical_solution(self): # Reads in from file pos, dens, vel, pres, inte = np.loadtxt(self.solution_file, unpack=True) exact = {} exact["pos"] = pos exact["gas", "density"] = dens exact["gas", "velocity_x"] = vel exact["gas", "pressure"] = pres exact["gas", "specific_thermal_energy"] = inte return exact yt-project-yt-f043ac8/yt/frontends/enzo/api.py000066400000000000000000000007111510711153200214050ustar00rootroot00000000000000from . import tests from .data_structures import ( EnzoDataset, EnzoDatasetInMemory, EnzoGrid, EnzoGridInMemory, EnzoHierarchy, EnzoHierarchy1D, EnzoHierarchy2D, EnzoHierarchyInMemory, ) from .fields import EnzoFieldInfo from .io import ( IOHandlerInMemory, IOHandlerPacked1D, IOHandlerPacked2D, IOHandlerPackedHDF5, ) from .simulation_handling import EnzoSimulation add_enzo_field = EnzoFieldInfo.add_field yt-project-yt-f043ac8/yt/frontends/enzo/data_structures.py000066400000000000000000001306561510711153200240640ustar00rootroot00000000000000import os import re import string import time import weakref from collections import defaultdict from functools import cached_property import numpy as np from more_itertools import always_iterable from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.field_info_container import NullFunc from yt.frontends.enzo.misc import cosmology_get_units from yt.funcs import get_pbar, iter_fields, setdefaultattr from yt.geometry.geometry_handler import YTDataChunk from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py, _libconf as libconf from .fields import EnzoFieldInfo class EnzoGrid(AMRGridPatch): """ Class representing a single Enzo Grid instance. """ def __init__(self, id, index): """ Returns an instance of EnzoGrid with *id*, associated with *filename* and *index*. """ # All of the field parameters will be passed to us as needed. AMRGridPatch.__init__(self, id, filename=None, index=index) self._children_ids = [] self._parent_id = -1 self.Level = -1 def set_filename(self, filename): """ Intelligently set the filename. """ if filename is None: self.filename = filename return if self.index._strip_path: self.filename = os.path.join( self.index.directory, os.path.basename(filename) ) elif filename[0] == os.path.sep: self.filename = filename else: self.filename = os.path.join(self.index.directory, filename) return @property def Parent(self): if self._parent_id == -1: return None return self.index.grids[self._parent_id - self._id_offset] @property def Children(self): return [self.index.grids[cid - self._id_offset] for cid in self._children_ids] @property def NumberOfActiveParticles(self): if not hasattr(self.index, "grid_active_particle_count"): return {} id = self.id - self._id_offset nap = { ptype: self.index.grid_active_particle_count[ptype][id] for ptype in self.index.grid_active_particle_count } return nap class EnzoGridInMemory(EnzoGrid): __slots__ = ["proc_num"] def set_filename(self, filename): pass class EnzoGridGZ(EnzoGrid): __slots__ = () def retrieve_ghost_zones(self, n_zones, fields, all_levels=False, smoothed=False): NGZ = self.ds.parameters.get("NumberOfGhostZones", 3) if n_zones > NGZ: return EnzoGrid.retrieve_ghost_zones( self, n_zones, fields, all_levels, smoothed ) # ----- Below is mostly the original code, except we remove the field # ----- access section # We will attempt this by creating a datacube that is exactly bigger # than the grid by nZones*dx in each direction nl = self.get_global_startindex() - n_zones new_left_edge = nl * self.dds + self.ds.domain_left_edge # Something different needs to be done for the root grid, though level = self.Level kwargs = { "dims": self.ActiveDimensions + 2 * n_zones, "num_ghost_zones": n_zones, "use_pbar": False, } # This should update the arguments to set the field parameters to be # those of this grid. kwargs.update(self.field_parameters) if smoothed: # cube = self.index.smoothed_covering_grid( # level, new_left_edge, new_right_edge, **kwargs) cube = self.index.smoothed_covering_grid(level, new_left_edge, **kwargs) else: cube = self.index.covering_grid(level, new_left_edge, **kwargs) # ----- This is EnzoGrid.get_data, duplicated here mostly for # ---- efficiency's sake. start_zone = NGZ - n_zones if start_zone == 0: end_zone = None else: end_zone = -(NGZ - n_zones) sl = tuple(slice(start_zone, end_zone) for i in range(3)) if fields is None: return cube for field in iter_fields(fields): if field in self.field_list: conv_factor = 1.0 if field in self.ds.field_info: conv_factor = self.ds.field_info[field]._convert_function(self) if self.ds.field_info[field].sampling_type == "particle": continue temp = self.index.io._read_raw_data_set(self, field) temp = temp.swapaxes(0, 2) cube.field_data[field] = np.multiply(temp, conv_factor, temp)[sl] return cube class EnzoHierarchy(GridIndex): _strip_path = False grid = EnzoGrid _preload_implemented = True def __init__(self, ds, dataset_type): self.dataset_type = dataset_type self.index_filename = os.path.abspath(f"{ds.parameter_filename}.hierarchy") if os.path.getsize(self.index_filename) == 0: raise OSError(-1, "File empty", self.index_filename) self.directory = os.path.dirname(self.index_filename) # For some reason, r8 seems to want Float64 if "CompilerPrecision" in ds and ds["CompilerPrecision"] == "r4": self.float_type = "float32" else: self.float_type = "float64" GridIndex.__init__(self, ds, dataset_type) # sync it back self.dataset.dataset_type = self.dataset_type def _count_grids(self): self.num_grids = None test_grid = test_grid_id = None self.num_stars = 0 for line in rlines(open(self.index_filename, "rb")): if ( line.startswith("BaryonFileName") or line.startswith("ParticleFileName") or line.startswith("FileName ") ): test_grid = line.split("=")[-1].strip().rstrip() if line.startswith("NumberOfStarParticles"): self.num_stars = int(line.split("=")[-1]) if line.startswith("Grid "): if self.num_grids is None: self.num_grids = int(line.split("=")[-1]) test_grid_id = int(line.split("=")[-1]) if test_grid is not None: break self._guess_dataset_type(self.ds.dimensionality, test_grid, test_grid_id) def _guess_dataset_type(self, rank, test_grid, test_grid_id): if test_grid[0] != os.path.sep: test_grid = os.path.join(self.directory, test_grid) if not os.path.exists(test_grid): test_grid = os.path.join(self.directory, os.path.basename(test_grid)) mylog.debug("Your data uses the annoying hardcoded path.") self._strip_path = True if self.dataset_type is not None: return if rank == 3: mylog.debug("Detected packed HDF5") if self.parameters.get("WriteGhostZones", 0) == 1: self.dataset_type = "enzo_packed_3d_gz" self.grid = EnzoGridGZ else: self.dataset_type = "enzo_packed_3d" elif rank == 2: mylog.debug("Detect packed 2D") self.dataset_type = "enzo_packed_2d" elif rank == 1: mylog.debug("Detect packed 1D") self.dataset_type = "enzo_packed_1d" else: raise NotImplementedError # Sets are sorted, so that won't work! def _parse_index(self): def _next_token_line(token, f): for line in f: if line.startswith(token): return line.split()[2:] pattern = r"Pointer: Grid\[(\d*)\]->NextGrid(Next|This)Level = (\d*)\s+$" patt = re.compile(pattern) f = open(self.index_filename) self.grids = [self.grid(1, self)] self.grids[0].Level = 0 si, ei, LE, RE, fn, npart = [], [], [], [], [], [] pbar = get_pbar("Parsing Hierarchy ", self.num_grids) version = self.dataset.parameters.get("VersionNumber", None) params = self.dataset.parameters if version is None and "Internal" in params: version = float(params["Internal"]["Provenance"]["VersionNumber"]) if version >= 3.0: active_particles = True nap = { ap_type: [] for ap_type in params["Physics"]["ActiveParticles"][ "ActiveParticlesEnabled" ] } else: if "AppendActiveParticleType" in self.parameters: nap = {} active_particles = True for type in self.parameters.get("AppendActiveParticleType", []): nap[type] = [] else: nap = None active_particles = False for grid_id in range(self.num_grids): pbar.update(grid_id + 1) # We will unroll this list si.append(_next_token_line("GridStartIndex", f)) ei.append(_next_token_line("GridEndIndex", f)) LE.append(_next_token_line("GridLeftEdge", f)) RE.append(_next_token_line("GridRightEdge", f)) nb = int(_next_token_line("NumberOfBaryonFields", f)[0]) fn.append([None]) if nb > 0: fn[-1] = _next_token_line("BaryonFileName", f) npart.append(int(_next_token_line("NumberOfParticles", f)[0])) # Below we find out what active particles exist in this grid, # and add their counts individually. if active_particles: ptypes = _next_token_line("PresentParticleTypes", f) counts = [int(c) for c in _next_token_line("ParticleTypeCounts", f)] for ptype in self.parameters.get("AppendActiveParticleType", []): if ptype in ptypes: nap[ptype].append(counts[ptypes.index(ptype)]) else: nap[ptype].append(0) if nb == 0 and npart[-1] > 0: fn[-1] = _next_token_line("ParticleFileName", f) for line in f: if len(line) < 2: break if line.startswith("Pointer:"): vv = patt.findall(line)[0] self.__pointer_handler(vv) pbar.finish() self._fill_arrays(ei, si, LE, RE, npart, nap) temp_grids = np.empty(self.num_grids, dtype="object") temp_grids[:] = self.grids self.grids = temp_grids self.filenames = fn def _initialize_grid_arrays(self): super()._initialize_grid_arrays() if "AppendActiveParticleType" in self.parameters.keys() and len( self.parameters["AppendActiveParticleType"] ): gac = { ptype: np.zeros(self.num_grids, dtype="i4") for ptype in self.parameters["AppendActiveParticleType"] } self.grid_active_particle_count = gac def _fill_arrays(self, ei, si, LE, RE, npart, nap): self.grid_dimensions.flat[:] = ei self.grid_dimensions -= np.array(si, dtype="i4") self.grid_dimensions += 1 self.grid_left_edge.flat[:] = LE self.grid_right_edge.flat[:] = RE self.grid_particle_count.flat[:] = npart if nap is not None: for ptype in nap: self.grid_active_particle_count[ptype].flat[:] = nap[ptype] def __pointer_handler(self, m): sgi = int(m[2]) - 1 if sgi == -1: return # if it's 0, then we're done with that lineage # Okay, so, we have a pointer. We make a new grid, with an id of the length+1 # (recall, Enzo grids are 1-indexed) self.grids.append(self.grid(len(self.grids) + 1, self)) # We'll just go ahead and make a weakref to cache second_grid = self.grids[sgi] # zero-indexed already first_grid = self.grids[int(m[0]) - 1] if m[1] == "Next": first_grid._children_ids.append(second_grid.id) second_grid._parent_id = first_grid.id second_grid.Level = first_grid.Level + 1 elif m[1] == "This": if first_grid.Parent is not None: first_grid.Parent._children_ids.append(second_grid.id) second_grid._parent_id = first_grid._parent_id second_grid.Level = first_grid.Level self.grid_levels[sgi] = second_grid.Level def _rebuild_top_grids(self, level=0): mylog.info("Rebuilding grids on level %s", level) cmask = self.grid_levels.flat == (level + 1) cmsum = cmask.sum() mask = np.zeros(self.num_grids, dtype="bool") for grid in self.select_grids(level): mask[:] = 0 LE = self.grid_left_edge[grid.id - grid._id_offset] RE = self.grid_right_edge[grid.id - grid._id_offset] grids, grid_i = self.get_box_grids(LE, RE) mask[grid_i] = 1 grid._children_ids = [] cgrids = self.grids[(mask * cmask).astype("bool")] mylog.info("%s: %s / %s", grid, len(cgrids), cmsum) for cgrid in cgrids: grid._children_ids.append(cgrid.id) cgrid._parent_id = grid.id mylog.info("Finished rebuilding") def _populate_grid_objects(self): for g, f in zip(self.grids, self.filenames, strict=True): g._prepare_grid() g._setup_dx() g.set_filename(f[0]) del self.filenames # No longer needed. self.max_level = self.grid_levels.max() def _detect_active_particle_fields(self): ap_list = self.dataset["AppendActiveParticleType"] _fields = {ap: [] for ap in ap_list} fields = [] for ptype in self.dataset["AppendActiveParticleType"]: select_grids = self.grid_active_particle_count[ptype].flat if not np.any(select_grids): current_ptypes = self.dataset.particle_types new_ptypes = [p for p in current_ptypes if p != ptype] self.dataset.particle_types = new_ptypes self.dataset.particle_types_raw = new_ptypes continue if ptype != "DarkMatter": gs = self.grids[select_grids > 0] g = gs[0] handle = h5py.File(g.filename, "r") grid_group = handle[f"/Grid{g.id:08d}"] for pname in ["Active Particles", "Particles"]: if pname in grid_group: break else: raise RuntimeError("Could not find active particle group in data.") node = grid_group[pname] for ptype in (str(p) for p in node): if ptype not in _fields: continue for field in (str(f) for f in node[ptype]): _fields[ptype].append(field) if node[ptype][field].ndim > 1: self.io._array_fields[field] = ( node[ptype][field].shape[1:], ) fields += [(ptype, field) for field in _fields.pop(ptype)] handle.close() return set(fields) def _setup_derived_fields(self): super()._setup_derived_fields() aps = self.dataset.parameters.get("AppendActiveParticleType", []) for fname, field in self.ds.field_info.items(): if not field.sampling_type == "particle": continue if isinstance(fname, tuple): continue if field._function is NullFunc: continue for apt in aps: dd = field._copy_def() dd.pop("name") self.ds.field_info.add_field((apt, fname), sampling_type="cell", **dd) def _detect_output_fields(self): self.field_list = [] # Do this only on the root processor to save disk work. if self.comm.rank in (0, None): mylog.info("Gathering a field list (this may take a moment.)") field_list = set() random_sample = self._generate_random_grids() for grid in random_sample: if not hasattr(grid, "filename"): continue try: gf = self.io._read_field_names(grid) except self.io._read_exception as e: raise OSError("Grid %s is a bit funky?", grid.id) from e mylog.debug("Grid %s has: %s", grid.id, gf) field_list = field_list.union(gf) if "AppendActiveParticleType" in self.dataset.parameters: ap_fields = self._detect_active_particle_fields() field_list = list(set(field_list).union(ap_fields)) if not any(f[0] == "io" for f in field_list): if "io" in self.dataset.particle_types_raw: ptypes_raw = list(self.dataset.particle_types_raw) ptypes_raw.remove("io") self.dataset.particle_types_raw = tuple(ptypes_raw) if "io" in self.dataset.particle_types: ptypes = list(self.dataset.particle_types) ptypes.remove("io") self.dataset.particle_types = tuple(ptypes) ptypes = self.dataset.particle_types ptypes_raw = self.dataset.particle_types_raw else: field_list = None ptypes = None ptypes_raw = None self.field_list = list(self.comm.mpi_bcast(field_list)) self.dataset.particle_types = list(self.comm.mpi_bcast(ptypes)) self.dataset.particle_types_raw = list(self.comm.mpi_bcast(ptypes_raw)) def _generate_random_grids(self): if self.num_grids > 40: rng = np.random.default_rng() starter = rng.integers(0, 20) random_sample = np.mgrid[starter : len(self.grids) - 1 : 20j].astype( "int32" ) # We also add in a bit to make sure that some of the grids have # particles gwp = self.grid_particle_count > 0 if np.any(gwp) and not np.any(gwp[random_sample,]): # We just add one grid. This is not terribly efficient. first_grid = np.where(gwp)[0][0] random_sample.resize((21,)) random_sample[-1] = first_grid mylog.debug("Added additional grid %s", first_grid) mylog.debug("Checking grids: %s", random_sample.tolist()) else: random_sample = np.mgrid[0 : max(len(self.grids), 1)].astype("int32") return self.grids[random_sample,] def _get_particle_type_counts(self): try: ret = {} for ptype in self.grid_active_particle_count: ret[ptype] = self.grid_active_particle_count[ptype].sum() return ret except AttributeError: return super()._get_particle_type_counts() def find_particles_by_type(self, ptype, max_num=None, additional_fields=None): """ Returns a structure of arrays with all of the particles' positions, velocities, masses, types, IDs, and attributes for a particle type **ptype** for a maximum of **max_num** particles. If non-default particle fields are used, provide them in **additional_fields**. """ # Not sure whether this routine should be in the general HierarchyType. if self.grid_particle_count.sum() == 0: mylog.info("Data contains no particles.") return None if additional_fields is None: additional_fields = [ "metallicity_fraction", "creation_time", "dynamical_time", ] pfields = [f for f in self.field_list if f.startswith("particle_")] nattr = self.dataset["NumberOfParticleAttributes"] if nattr > 0: pfields += additional_fields[:nattr] # Find where the particles reside and count them if max_num is None: max_num = 1e100 total = 0 pstore = [] for level in range(self.max_level, -1, -1): for grid in self.select_grids(level): index = np.where(grid["particle_type"] == ptype)[0] total += len(index) pstore.append(index) if total >= max_num: break if total >= max_num: break result = None if total > 0: result = {} for p in pfields: result[p] = np.zeros(total, "float64") # Now we retrieve data for each field ig = count = 0 for level in range(self.max_level, -1, -1): for grid in self.select_grids(level): nidx = len(pstore[ig]) if nidx > 0: for p in pfields: result[p][count : count + nidx] = grid[p][pstore[ig]] count += nidx ig += 1 if count >= total: break if count >= total: break # Crop data if retrieved more than max_num if count > max_num: for p in pfields: result[p] = result[p][0:max_num] return result class EnzoHierarchyInMemory(EnzoHierarchy): grid = EnzoGridInMemory @cached_property def enzo(self): import enzo return enzo def __init__(self, ds, dataset_type=None): self.dataset_type = dataset_type self.float_type = "float64" self.dataset = weakref.proxy(ds) # for _obtain_enzo self.float_type = self.enzo.hierarchy_information["GridLeftEdge"].dtype self.directory = os.getcwd() GridIndex.__init__(self, ds, dataset_type) def _initialize_data_storage(self): pass def _count_grids(self): self.num_grids = self.enzo.hierarchy_information["GridDimensions"].shape[0] def _parse_index(self): self._copy_index_structure() mylog.debug("Copying reverse tree") reverse_tree = self.enzo.hierarchy_information["GridParentIDs"].ravel().tolist() # Initial setup: mylog.debug("Reconstructing parent-child relationships") grids = [] # We enumerate, so it's 0-indexed id and 1-indexed pid self.filenames = ["-1"] * self.num_grids for id, pid in enumerate(reverse_tree): grids.append(self.grid(id + 1, self)) grids[-1].Level = self.grid_levels[id, 0] if pid > 0: grids[-1]._parent_id = pid grids[pid - 1]._children_ids.append(grids[-1].id) self.max_level = self.grid_levels.max() mylog.debug("Preparing grids") self.grids = np.empty(len(grids), dtype="object") for i, grid in enumerate(grids): if (i % 1e4) == 0: mylog.debug("Prepared % 7i / % 7i grids", i, self.num_grids) grid.filename = "Inline_processor_%07i" % (self.grid_procs[i, 0]) grid._prepare_grid() grid._setup_dx() grid.proc_num = self.grid_procs[i, 0] self.grids[i] = grid mylog.debug("Prepared") def _initialize_grid_arrays(self): EnzoHierarchy._initialize_grid_arrays(self) self.grid_procs = np.zeros((self.num_grids, 1), "int32") def _copy_index_structure(self): # Dimensions are important! self.grid_dimensions[:] = self.enzo.hierarchy_information["GridEndIndices"][:] self.grid_dimensions -= self.enzo.hierarchy_information["GridStartIndices"][:] self.grid_dimensions += 1 self.grid_left_edge[:] = self.enzo.hierarchy_information["GridLeftEdge"][:] self.grid_right_edge[:] = self.enzo.hierarchy_information["GridRightEdge"][:] self.grid_levels[:] = self.enzo.hierarchy_information["GridLevels"][:] self.grid_procs = self.enzo.hierarchy_information["GridProcs"].copy() self.grid_particle_count[:] = self.enzo.hierarchy_information[ "GridNumberOfParticles" ][:] def save_data(self, *args, **kwargs): pass _cached_field_list = None _cached_derived_field_list = None def _generate_random_grids(self): my_rank = self.comm.rank my_grids = self.grids[self.grid_procs.ravel() == my_rank] if len(my_grids) > 40: rng = np.random.default_rng() starter = rng.integers(0, 20) random_sample = np.mgrid[starter : len(my_grids) - 1 : 20j].astype("int32") mylog.debug("Checking grids: %s", random_sample.tolist()) else: random_sample = np.mgrid[0 : max(len(my_grids) - 1, 1)].astype("int32") return my_grids[random_sample,] def _chunk_io(self, dobj, cache=True, local_only=False): gfiles = defaultdict(list) gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for g in gobjs: gfiles[g.filename].append(g) for fn in sorted(gfiles): if local_only: gobjs = [g for g in gfiles[fn] if g.proc_num == self.comm.rank] gfiles[fn] = gobjs gs = gfiles[fn] count = self._count_selection(dobj, gs) yield YTDataChunk(dobj, "io", gs, count, cache=cache) class EnzoHierarchy1D(EnzoHierarchy): def _fill_arrays(self, ei, si, LE, RE, npart, nap): self.grid_dimensions[:, :1] = ei self.grid_dimensions[:, :1] -= np.array(si, dtype="i4") self.grid_dimensions += 1 self.grid_left_edge[:, :1] = LE self.grid_right_edge[:, :1] = RE self.grid_particle_count.flat[:] = npart self.grid_left_edge[:, 1:] = 0.0 self.grid_right_edge[:, 1:] = 1.0 self.grid_dimensions[:, 1:] = 1 if nap is not None: raise NotImplementedError class EnzoHierarchy2D(EnzoHierarchy): def _fill_arrays(self, ei, si, LE, RE, npart, nap): self.grid_dimensions[:, :2] = ei self.grid_dimensions[:, :2] -= np.array(si, dtype="i4") self.grid_dimensions += 1 self.grid_left_edge[:, :2] = LE self.grid_right_edge[:, :2] = RE self.grid_particle_count.flat[:] = npart self.grid_left_edge[:, 2] = 0.0 self.grid_right_edge[:, 2] = 1.0 self.grid_dimensions[:, 2] = 1 if nap is not None: raise NotImplementedError class EnzoDataset(Dataset): """ Enzo-specific output, set at a fixed time. """ _load_requirements = ["h5py"] _index_class = EnzoHierarchy _field_info_class = EnzoFieldInfo def __init__( self, filename, dataset_type=None, parameter_override=None, conversion_override=None, storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): """ This class is a stripped down class that simply reads and parses *filename* without looking at the index. *dataset_type* gets passed to the index to pre-determine the style of data-output. However, it is not strictly necessary. Optionally you may specify a *parameter_override* dictionary that will override anything in the parameter file and a *conversion_override* dictionary that consists of {fieldname : conversion_to_cgs} that will override the #DataCGS. """ self.fluid_types += ("enzo",) if filename.endswith(".hierarchy"): filename = filename[:-10] if parameter_override is None: parameter_override = {} self._parameter_override = parameter_override if conversion_override is None: conversion_override = {} self._conversion_override = conversion_override self.storage_filename = storage_filename Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) def _setup_1d(self): self._index_class = EnzoHierarchy1D self.domain_left_edge = np.concatenate([[self.domain_left_edge], [0.0, 0.0]]) self.domain_right_edge = np.concatenate([[self.domain_right_edge], [1.0, 1.0]]) def _setup_2d(self): self._index_class = EnzoHierarchy2D self.domain_left_edge = np.concatenate([self.domain_left_edge, [0.0]]) self.domain_right_edge = np.concatenate([self.domain_right_edge, [1.0]]) def get_parameter(self, parameter, type=None): """ Gets a parameter not in the parameterDict. """ if parameter in self.parameters: return self.parameters[parameter] for line in open(self.parameter_filename): if line.find("#") >= 1: # Keep the commented lines line = line[: line.find("#")] line = line.strip().rstrip() if len(line) < 2: continue try: param, vals = map(string.strip, map(string.rstrip, line.split("="))) except ValueError: mylog.error("ValueError: '%s'", line) if parameter == param: if type is None: t = vals.split() else: t = map(type, vals.split()) if len(t) == 1: self.parameters[param] = t[0] else: self.parameters[param] = t if param.endswith("Units") and not param.startswith("Temperature"): dataType = param[:-5] self.conversion_factors[dataType] = self.parameters[param] return self.parameters[parameter] return "" @cached_property def unique_identifier(self) -> str: if "CurrentTimeIdentifier" in self.parameters: # enzo2 return str(self.parameters["CurrentTimeIdentifier"]) elif "MetaDataDatasetUUID" in self.parameters: # enzo2 return str(self.parameters["MetaDataDatasetUUID"]) elif "Internal" in self.parameters: # enzo3 return str( self.parameters["Internal"]["Provenance"]["CurrentTimeIdentidier"] ) else: return super().unique_identifier def _parse_parameter_file(self): """ Parses the parameter file and establishes the various dictionaries. """ # Let's read the file with open(self.parameter_filename) as f: line = f.readline().strip() f.seek(0) if line == "Internal:": self._parse_enzo3_parameter_file(f) else: self._parse_enzo2_parameter_file(f) def _parse_enzo3_parameter_file(self, f): self.parameters = p = libconf.load(f) sim = p["SimulationControl"] internal = p["Internal"] phys = p["Physics"] self.refine_by = sim["AMR"]["RefineBy"] self._periodicity = tuple( a == 3 for a in sim["Domain"]["LeftFaceBoundaryCondition"] ) self.dimensionality = sim["Domain"]["TopGridRank"] self.domain_dimensions = np.array( sim["Domain"]["TopGridDimensions"], dtype="int64" ) self.domain_left_edge = np.array( sim["Domain"]["DomainLeftEdge"], dtype="float64" ) self.domain_right_edge = np.array( sim["Domain"]["DomainRightEdge"], dtype="float64" ) self.gamma = phys["Hydro"]["Gamma"] self.current_time = internal["InitialTime"] self.cosmological_simulation = phys["Cosmology"]["ComovingCoordinates"] if self.cosmological_simulation == 1: cosmo = phys["Cosmology"] self.current_redshift = internal["CosmologyCurrentRedshift"] self.omega_lambda = cosmo["OmegaLambdaNow"] self.omega_matter = cosmo["OmegaMatterNow"] self.hubble_constant = cosmo["HubbleConstantNow"] else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 self.particle_types = ["DarkMatter"] + phys["ActiveParticles"][ "ActiveParticlesEnabled" ] self.particle_types = tuple(self.particle_types) self.particle_types_raw = self.particle_types if self.dimensionality == 1: self._setup_1d() elif self.dimensionality == 2: self._setup_2d() def _parse_enzo2_parameter_file(self, f): for line in (l.strip() for l in f): if (len(line) < 2) or ("=" not in line): continue param, vals = (i.strip() for i in line.split("=", 1)) # First we try to decipher what type of value it is. vals = vals.split() # Special case approaching. if "(do" in vals: vals = vals[:1] if len(vals) == 0: pcast = str # Assume NULL output else: v = vals[0] # Figure out if it's castable to floating point: try: float(v) except ValueError: pcast = str else: if any("." in v or "e+" in v or "e-" in v for v in vals): pcast = float elif v == "inf": pcast = str else: pcast = int # Now we figure out what to do with it. if len(vals) == 0: vals = "" elif len(vals) == 1: vals = pcast(vals[0]) else: vals = np.array([pcast(i) for i in vals if i != "-99999"]) if param.startswith("Append"): if param not in self.parameters: self.parameters[param] = [] self.parameters[param].append(vals) else: self.parameters[param] = vals self.refine_by = self.parameters["RefineBy"] _periodicity = tuple( always_iterable(self.parameters["LeftFaceBoundaryCondition"] == 3) ) self.dimensionality = self.parameters["TopGridRank"] if self.dimensionality > 1: self.domain_dimensions = self.parameters["TopGridDimensions"] if len(self.domain_dimensions) < 3: tmp = self.domain_dimensions.tolist() tmp.append(1) self.domain_dimensions = np.array(tmp) _periodicity += (False,) self.domain_left_edge = np.array( self.parameters["DomainLeftEdge"], "float64" ).copy() self.domain_right_edge = np.array( self.parameters["DomainRightEdge"], "float64" ).copy() else: self.domain_left_edge = np.array( self.parameters["DomainLeftEdge"], "float64" ) self.domain_right_edge = np.array( self.parameters["DomainRightEdge"], "float64" ) self.domain_dimensions = np.array( [self.parameters["TopGridDimensions"], 1, 1] ) _periodicity += (False, False) assert len(_periodicity) == 3 self._periodicity = _periodicity self.gamma = self.parameters["Gamma"] if self.parameters["ComovingCoordinates"]: self.cosmological_simulation = 1 self.current_redshift = self.parameters["CosmologyCurrentRedshift"] self.omega_lambda = self.parameters["CosmologyOmegaLambdaNow"] self.omega_matter = self.parameters["CosmologyOmegaMatterNow"] self.omega_radiation = self.parameters.get( "CosmologyOmegaRadiationNow", 0.0 ) self.hubble_constant = self.parameters["CosmologyHubbleConstantNow"] else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 self.particle_types = [] self.current_time = self.parameters["InitialTime"] if ( self.parameters["NumberOfParticles"] > 0 and "AppendActiveParticleType" in self.parameters.keys() ): # If this is the case, then we know we should have a DarkMatter # particle type, and we don't need the "io" type. self.parameters["AppendActiveParticleType"].append("DarkMatter") else: # We do not have an "io" type for Enzo particles if the # ActiveParticle machinery is on, as we simply will ignore any of # the non-DarkMatter particles in that case. However, for older # datasets, we call this particle type "io". self.particle_types = ["io"] for ptype in self.parameters.get("AppendActiveParticleType", []): self.particle_types.append(ptype) self.particle_types = tuple(self.particle_types) self.particle_types_raw = self.particle_types if self.dimensionality == 1: self._setup_1d() elif self.dimensionality == 2: self._setup_2d() def _set_code_unit_attributes(self): if self.cosmological_simulation: k = cosmology_get_units( self.hubble_constant, self.omega_matter, self.parameters["CosmologyComovingBoxSize"], self.parameters["CosmologyInitialRedshift"], self.current_redshift, ) # Now some CGS values box_size = self.parameters["CosmologyComovingBoxSize"] setdefaultattr(self, "length_unit", self.quan(box_size, "Mpccm/h")) setdefaultattr( self, "mass_unit", self.quan(k["urho"], "g/cm**3") * (self.length_unit.in_cgs()) ** 3, ) setdefaultattr(self, "time_unit", self.quan(k["utim"], "s")) setdefaultattr(self, "velocity_unit", self.quan(k["uvel"], "cm/s")) else: if "LengthUnits" in self.parameters: length_unit = self.parameters["LengthUnits"] mass_unit = self.parameters["DensityUnits"] * length_unit**3 time_unit = self.parameters["TimeUnits"] elif "SimulationControl" in self.parameters: units = self.parameters["SimulationControl"]["Units"] length_unit = units["Length"] mass_unit = units["Density"] * length_unit**3 time_unit = units["Time"] else: mylog.warning("Setting 1.0 in code units to be 1.0 cm") mylog.warning("Setting 1.0 in code units to be 1.0 s") length_unit = mass_unit = time_unit = 1.0 setdefaultattr(self, "length_unit", self.quan(length_unit, "cm")) setdefaultattr(self, "mass_unit", self.quan(mass_unit, "g")) setdefaultattr(self, "time_unit", self.quan(time_unit, "s")) setdefaultattr(self, "velocity_unit", self.length_unit / self.time_unit) density_unit = self.mass_unit / self.length_unit**3 magnetic_unit = np.sqrt(4 * np.pi * density_unit) * self.velocity_unit magnetic_unit = np.float64(magnetic_unit.in_cgs()) setdefaultattr(self, "magnetic_unit", self.quan(magnetic_unit, "gauss")) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return filename.endswith(".hierarchy") or os.path.exists( f"{filename}.hierarchy" ) @classmethod def _guess_candidates(cls, base, directories, files): candidates = [ _ for _ in files if _.endswith(".hierarchy") and os.path.exists(os.path.join(base, _.rsplit(".", 1)[0])) ] # Typically, Enzo won't have nested outputs. return candidates, (len(candidates) == 0) class EnzoDatasetInMemory(EnzoDataset): _index_class = EnzoHierarchyInMemory _dataset_type = "enzo_inline" def __init__(self, parameter_override=None, conversion_override=None): self.fluid_types += ("enzo",) if parameter_override is None: parameter_override = {} self._parameter_override = parameter_override if conversion_override is None: conversion_override = {} self._conversion_override = conversion_override Dataset.__init__(self, "InMemoryParameterFile", self._dataset_type) def _parse_parameter_file(self): enzo = self._obtain_enzo() ncalls = enzo.yt_parameter_file["NumberOfPythonCalls"] self._input_filename = f"cycle{ncalls:08d}" self.parameters["CurrentTimeIdentifier"] = time.time() self.parameters.update(enzo.yt_parameter_file) self.conversion_factors.update(enzo.conversion_factors) for i in self.parameters: if isinstance(self.parameters[i], tuple): self.parameters[i] = np.array(self.parameters[i]) if i.endswith("Units") and not i.startswith("Temperature"): dataType = i[:-5] self.conversion_factors[dataType] = self.parameters[i] self.domain_left_edge = self.parameters["DomainLeftEdge"].copy() self.domain_right_edge = self.parameters["DomainRightEdge"].copy() for i in self.conversion_factors: if isinstance(self.conversion_factors[i], tuple): self.conversion_factors[i] = np.array(self.conversion_factors[i]) for p, v in self._parameter_override.items(): self.parameters[p] = v for p, v in self._conversion_override.items(): self.conversion_factors[p] = v self.refine_by = self.parameters["RefineBy"] self._periodicity = tuple( always_iterable(self.parameters["LeftFaceBoundaryCondition"] == 3) ) self.dimensionality = self.parameters["TopGridRank"] self.domain_dimensions = self.parameters["TopGridDimensions"] self.current_time = self.parameters["InitialTime"] if self.parameters["ComovingCoordinates"]: self.cosmological_simulation = 1 self.current_redshift = self.parameters["CosmologyCurrentRedshift"] self.omega_lambda = self.parameters["CosmologyOmegaLambdaNow"] self.omega_matter = self.parameters["CosmologyOmegaMatterNow"] self.hubble_constant = self.parameters["CosmologyHubbleConstantNow"] else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 def _obtain_enzo(self): import enzo return enzo @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return False # These next two functions are taken from # http://www.reddit.com/r/Python/comments/6hj75/reverse_file_iterator/c03vms4 # Credit goes to "Brian" on Reddit def rblocks(f, blocksize=4096): """Read file as series of blocks from end of file to start. The data itself is in normal order, only the order of the blocks is reversed. ie. "hello world" -> ["ld","wor", "lo ", "hel"] Note that the file must be opened in binary mode. """ if "b" not in f.mode.lower(): raise Exception("File must be opened using binary mode.") size = os.stat(f.name).st_size fullblocks, lastblock = divmod(size, blocksize) # The first(end of file) block will be short, since this leaves # the rest aligned on a blocksize boundary. This may be more # efficient than having the last (first in file) block be short f.seek(-lastblock, 2) yield f.read(lastblock).decode("ascii") for i in range(fullblocks - 1, -1, -1): f.seek(i * blocksize) yield f.read(blocksize).decode("ascii") def rlines(f, keepends=False): """Iterate through the lines of a file in reverse order. If keepends is true, line endings are kept as part of the line. """ buf = "" for block in rblocks(f): buf = block + buf lines = buf.splitlines(keepends) # Return all lines except the first (since may be partial) if lines: lines.reverse() buf = lines.pop() # Last line becomes end of new first line. yield from lines yield buf # First line. yt-project-yt-f043ac8/yt/frontends/enzo/definitions.py000066400000000000000000000000001510711153200231360ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo/fields.py000066400000000000000000000314611510711153200221100ustar00rootroot00000000000000import numpy as np from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.utilities.physical_constants import me, mp b_units = "code_magnetic" e_units = "code_magnetic/c" ra_units = "code_length / code_time**2" rho_units = "code_mass / code_length**3" vel_units = "code_velocity" known_species_names = { "HI": "H_p0", "HII": "H_p1", "HeI": "He_p0", "HeII": "He_p1", "HeIII": "He_p2", "H2I": "H2_p0", "H2II": "H2_p1", "HM": "H_m1", "HeH": "HeH_p0", "DI": "D_p0", "DII": "D_p1", "HDI": "HD_p0", "Electron": "El", "OI": "O_p0", "OII": "O_p1", "OIII": "O_p2", "OIV": "O_p3", "OV": "O_p4", "OVI": "O_p5", "OVII": "O_p6", "OVIII": "O_p7", "OIX": "O_p8", } NODAL_FLAGS = { "BxF": [1, 0, 0], "ByF": [0, 1, 0], "BzF": [0, 0, 1], "Ex": [0, 1, 1], "Ey": [1, 0, 1], "Ez": [1, 1, 0], "AvgElec0": [0, 1, 1], "AvgElec1": [1, 0, 1], "AvgElec2": [1, 1, 0], } class EnzoFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("Cooling_Time", ("s", ["cooling_time"], None)), ("Dengo_Cooling_Rate", ("erg/g/s", [], None)), ("Grackle_Cooling_Rate", ("erg/s/cm**3", [], None)), ("HI_kph", ("1/code_time", ["H_p0_ionization_rate"], None)), ("HeI_kph", ("1/code_time", ["He_p0_ionization_rate"], None)), ("HeII_kph", ("1/code_time", ["He_p1_ionization_rate"], None)), ("H2I_kdiss", ("1/code_time", ["H2_p0_dissociation_rate"], None)), ("HM_kph", ("1/code_time", ["H_m1_ionization_rate"], None)), ("H2II_kdiss", ("1/code_time", ["H2_p1_dissociation_rate"], None)), ("Bx", (b_units, [], None)), ("By", (b_units, [], None)), ("Bz", (b_units, [], None)), ("BxF", (b_units, [], None)), ("ByF", (b_units, [], None)), ("BzF", (b_units, [], None)), ("Ex", (e_units, [], None)), ("Ey", (e_units, [], None)), ("Ez", (e_units, [], None)), ("AvgElec0", (e_units, [], None)), ("AvgElec1", (e_units, [], None)), ("AvgElec2", (e_units, [], None)), ("RadAccel1", (ra_units, ["radiation_acceleration_x"], None)), ("RadAccel2", (ra_units, ["radiation_acceleration_y"], None)), ("RadAccel3", (ra_units, ["radiation_acceleration_z"], None)), ("Dark_Matter_Density", (rho_units, ["dark_matter_density"], None)), ("Temperature", ("K", ["temperature"], None)), ("Dust_Temperature", ("K", ["dust_temperature"], None)), ("x-velocity", (vel_units, ["velocity_x"], None)), ("y-velocity", (vel_units, ["velocity_y"], None)), ("z-velocity", (vel_units, ["velocity_z"], None)), ("RaySegments", ("", ["ray_segments"], None)), ("PhotoGamma", ("eV/code_time", ["photo_gamma"], None)), ("PotentialField", ("code_velocity**2", ["gravitational_potential"], None)), ("Density", (rho_units, ["density"], None)), ("Metal_Density", (rho_units, ["metal_density"], None)), ("SN_Colour", (rho_units, [], None)), # Note: we do not alias Electron_Density to anything ("Electron_Density", (rho_units, [], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_velocity_x", (vel_units, [], None)), ("particle_velocity_y", (vel_units, [], None)), ("particle_velocity_z", (vel_units, [], None)), ("creation_time", ("code_time", [], None)), ("dynamical_time", ("code_time", [], None)), ("metallicity_fraction", ("code_metallicity", [], None)), ("metallicity", ("", [], None)), ("particle_type", ("", [], None)), ("particle_index", ("", [], None)), ("particle_mass", ("code_mass", [], None)), ("GridID", ("", [], None)), ("identifier", ("", ["particle_index"], None)), ("level", ("", [], None)), ("AccretionRate", ("code_mass/code_time", [], None)), ("AccretionRateTime", ("code_time", [], None)), ("AccretionRadius", ("code_length", [], None)), ("RadiationLifetime", ("code_time", [], None)), ) def __init__(self, ds, field_list): hydro_method = ds.parameters.get("HydroMethod", None) if hydro_method is None: hydro_method = ds.parameters["Physics"]["Hydro"]["HydroMethod"] if hydro_method == 2: sl_left = slice(None, -2, None) sl_right = slice(1, -1, None) div_fac = 1.0 else: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 slice_info = (sl_left, sl_right, div_fac) super().__init__(ds, field_list, slice_info) # setup nodal flag information for field in NODAL_FLAGS: if ("enzo", field) in self: finfo = self["enzo", field] finfo.nodal_flag = np.array(NODAL_FLAGS[field]) def add_species_field(self, species): # This is currently specific to Enzo. Hopefully in the future we will # have deeper integration with other systems, such as Dengo, to provide # better understanding of ionization and molecular states. # # We have several fields to add based on a given species field. First # off, we add the species field itself. Then we'll add a few more # items... # self.add_output_field( ("enzo", f"{species}_Density"), sampling_type="cell", take_log=True, units="code_mass/code_length**3", ) yt_name = known_species_names[species] # don't alias electron density since mass is wrong if species != "Electron": self.alias(("gas", f"{yt_name}_density"), ("enzo", f"{species}_Density")) def setup_species_fields(self): species_names = [ fn.rsplit("_Density")[0] for ft, fn in self.field_list if fn.endswith("_Density") ] species_names = [sp for sp in species_names if sp in known_species_names] def _electron_density(field, data): return data["enzo", "Electron_Density"] * (me / mp) self.add_field( ("gas", "El_density"), sampling_type="cell", function=_electron_density, units=self.ds.unit_system["density"], ) for sp in species_names: self.add_species_field(sp) self.species_names.append(known_species_names[sp]) self.species_names.sort() # bb #1059 def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases # Now we conditionally load a few other things. params = self.ds.parameters multi_species = params.get("MultiSpecies", None) dengo = params.get("DengoChemistryModel", 0) if multi_species is None: multi_species = params["Physics"]["AtomicPhysics"]["MultiSpecies"] if multi_species > 0 or dengo == 1: self.setup_species_fields() self.setup_energy_field() setup_magnetic_field_aliases(self, "enzo", [f"B{ax}" for ax in "xyz"]) def setup_energy_field(self): unit_system = self.ds.unit_system # We check which type of field we need, and then we add it. ge_name = None te_name = None params = self.ds.parameters multi_species = params.get("MultiSpecies", None) if multi_species is None: multi_species = params["Physics"]["AtomicPhysics"]["MultiSpecies"] hydro_method = params.get("HydroMethod", None) if hydro_method is None: hydro_method = params["Physics"]["Hydro"]["HydroMethod"] dual_energy = params.get("DualEnergyFormalism", None) if dual_energy is None: dual_energy = params["Physics"]["Hydro"]["DualEnergyFormalism"] if ("enzo", "Gas_Energy") in self.field_list: ge_name = "Gas_Energy" elif ("enzo", "GasEnergy") in self.field_list: ge_name = "GasEnergy" if ("enzo", "Total_Energy") in self.field_list: te_name = "Total_Energy" elif ("enzo", "TotalEnergy") in self.field_list: te_name = "TotalEnergy" if hydro_method == 2 and te_name is not None: self.add_output_field( ("enzo", te_name), sampling_type="cell", units="code_velocity**2" ) self.alias(("gas", "specific_thermal_energy"), ("enzo", te_name)) def _ge_plus_kin(field, data): ret = data["enzo", te_name] + 0.5 * data["gas", "velocity_x"] ** 2.0 if data.ds.dimensionality > 1: ret += 0.5 * data["gas", "velocity_y"] ** 2.0 if data.ds.dimensionality > 2: ret += 0.5 * data["gas", "velocity_z"] ** 2.0 return ret self.add_field( ("gas", "specific_total_energy"), sampling_type="cell", function=_ge_plus_kin, units=unit_system["specific_energy"], ) elif dual_energy == 1: if te_name is not None: self.add_output_field( ("enzo", te_name), sampling_type="cell", units="code_velocity**2" ) self.alias( ("gas", "specific_total_energy"), ("enzo", te_name), units=unit_system["specific_energy"], ) if ge_name is not None: self.add_output_field( ("enzo", ge_name), sampling_type="cell", units="code_velocity**2" ) self.alias( ("gas", "specific_thermal_energy"), ("enzo", ge_name), units=unit_system["specific_energy"], ) elif hydro_method in (4, 6) and te_name is not None: self.add_output_field( ("enzo", te_name), sampling_type="cell", units="code_velocity**2" ) # Subtract off B-field energy def _sub_b(field, data): ret = data["enzo", te_name] - 0.5 * data["gas", "velocity_x"] ** 2.0 if data.ds.dimensionality > 1: ret -= 0.5 * data["gas", "velocity_y"] ** 2.0 if data.ds.dimensionality > 2: ret -= 0.5 * data["gas", "velocity_z"] ** 2.0 ret -= data["gas", "magnetic_energy_density"] / data["gas", "density"] return ret self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_sub_b, units=unit_system["specific_energy"], ) elif te_name is not None: # Otherwise, we assume TotalEnergy is kinetic+thermal self.add_output_field( ("enzo", te_name), sampling_type="cell", units="code_velocity**2" ) self.alias( ("gas", "specific_total_energy"), ("enzo", te_name), units=unit_system["specific_energy"], ) def _tot_minus_kin(field, data): ret = data["enzo", te_name] - 0.5 * data["gas", "velocity_x"] ** 2.0 if data.ds.dimensionality > 1: ret -= 0.5 * data["gas", "velocity_y"] ** 2.0 if data.ds.dimensionality > 2: ret -= 0.5 * data["gas", "velocity_z"] ** 2.0 return ret self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_tot_minus_kin, units=unit_system["specific_energy"], ) if multi_species == 0 and "Mu" in params: def _mean_molecular_weight(field, data): return params["Mu"] * data["index", "ones"] self.add_field( ("gas", "mean_molecular_weight"), sampling_type="cell", function=_mean_molecular_weight, units="", ) def _number_density(field, data): return data["gas", "density"] / (mp * params["Mu"]) self.add_field( ("gas", "number_density"), sampling_type="cell", function=_number_density, units=unit_system["number_density"], ) def setup_particle_fields(self, ptype): def _age(field, data): return data.ds.current_time - data["all", "creation_time"] self.add_field( (ptype, "age"), sampling_type="particle", function=_age, units="yr" ) super().setup_particle_fields(ptype) yt-project-yt-f043ac8/yt/frontends/enzo/io.py000066400000000000000000000342511510711153200212510ustar00rootroot00000000000000import numpy as np from yt.geometry.selection_routines import GridSelector from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py _convert_mass = ("particle_mass", "mass") _particle_position_names: dict[str, str] = {} class IOHandlerPackedHDF5(BaseIOHandler): _dataset_type = "enzo_packed_3d" _base = slice(None) _field_dtype = "float64" def _read_field_names(self, grid): if grid.filename is None: return [] f = h5py.File(grid.filename, mode="r") try: group = f["/Grid%08i" % grid.id] except KeyError: group = f fields = [] dtypes = set() add_io = "io" in grid.ds.particle_types add_dm = "DarkMatter" in grid.ds.particle_types for name, v in group.items(): # NOTE: This won't work with 1D datasets or references. # For all versions of Enzo I know about, we can assume all floats # are of the same size. So, let's grab one. if not hasattr(v, "shape") or v.dtype == "O": continue elif len(v.dims) == 1: if grid.ds.dimensionality == 1: fields.append(("enzo", str(name))) elif add_io: fields.append(("io", str(name))) elif add_dm: fields.append(("DarkMatter", str(name))) else: fields.append(("enzo", str(name))) dtypes.add(v.dtype) if len(dtypes) == 1: # Now, if everything we saw was the same dtype, we can go ahead and # set it here. We do this because it is a HUGE savings for 32 bit # floats, since our numpy copying/casting is way faster than # h5py's, for some reason I don't understand. This does *not* need # to be correct -- it will get fixed later -- it just needs to be # okay for now. self._field_dtype = list(dtypes)[0] f.close() return fields @property def _read_exception(self): return (KeyError,) def _read_particle_coords(self, chunks, ptf): yield from ( (ptype, xyz, 0.0) for ptype, xyz in self._read_particle_fields(chunks, ptf, None) ) def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) for chunk in chunks: # These should be organized by grid filename f = None for g in chunk.objs: if g.filename is None: continue if f is None: # print("Opening (read) %s" % g.filename) f = h5py.File(g.filename, mode="r") nap = sum(g.NumberOfActiveParticles.values()) if g.NumberOfParticles == 0 and nap == 0: continue ds = f.get("/Grid%08i" % g.id) for ptype, field_list in sorted(ptf.items()): if ptype == "io": if g.NumberOfParticles == 0: continue pds = ds elif ptype == "DarkMatter": if g.NumberOfActiveParticles[ptype] == 0: continue pds = ds elif not g.NumberOfActiveParticles[ptype]: continue else: for pname in ["Active Particles", "Particles"]: pds = ds.get(f"{pname}/{ptype}") if pds is not None: break else: raise RuntimeError( "Could not find active particle group in data." ) pn = _particle_position_names.get(ptype, r"particle_position_%s") x, y, z = ( np.asarray(pds.get(pn % ax)[()], dtype="=f8") for ax in "xyz" ) if selector is None: # This only ever happens if the call is made from # _read_particle_coords. yield ptype, (x, y, z) continue mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: data = np.asarray(pds.get(field)[()], "=f8") if field in _convert_mass: data *= g.dds.prod(dtype="f8") yield (ptype, field), data[mask] if f: f.close() def io_iter(self, chunks, fields): h5_dtype = self._field_dtype for chunk in chunks: fid = None filename = -1 for obj in chunk.objs: if obj.filename is None: continue if obj.filename != filename: # Note one really important thing here: even if we do # implement LRU caching in the _read_obj_field function, # we'll still be doing file opening and whatnot. This is a # problem, but one we can return to. if fid is not None: fid.close() fid = h5py.h5f.open( obj.filename.encode("latin-1"), h5py.h5f.ACC_RDONLY ) filename = obj.filename for field in fields: nodal_flag = self.ds.field_info[field].nodal_flag dims = obj.ActiveDimensions[::-1] + nodal_flag[::-1] data = np.empty(dims, dtype=h5_dtype) yield field, obj, self._read_obj_field(obj, field, (fid, data)) if fid is not None: fid.close() def _read_obj_field(self, obj, field, fid_data): if fid_data is None: fid_data = (None, None) fid, data = fid_data if fid is None: close = True fid = h5py.h5f.open(obj.filename.encode("latin-1"), h5py.h5f.ACC_RDONLY) else: close = False if data is None: data = np.empty(obj.ActiveDimensions[::-1], dtype=self._field_dtype) ftype, fname = field try: node = "/Grid%08i/%s" % (obj.id, fname) dg = h5py.h5d.open(fid, node.encode("latin-1")) except KeyError: if fname == "Dark_Matter_Density": data[:] = 0 return data.T raise dg.read(h5py.h5s.ALL, h5py.h5s.ALL, data) # I don't know why, but on some installations of h5py this works, but # on others, nope. Doesn't seem to be a version thing. # dg.close() if close: fid.close() return data.T class IOHandlerPackedHDF5GhostZones(IOHandlerPackedHDF5): _dataset_type = "enzo_packed_3d_gz" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) NGZ = self.ds.parameters.get("NumberOfGhostZones", 3) self._base = (slice(NGZ, -NGZ), slice(NGZ, -NGZ), slice(NGZ, -NGZ)) def _read_obj_field(self, *args, **kwargs): return super()._read_obj_field(*args, **kwargs)[self._base] class IOHandlerInMemory(BaseIOHandler): _dataset_type = "enzo_inline" def __init__(self, ds, ghost_zones=3): self.ds = ds import enzo self.enzo = enzo self.grids_in_memory = enzo.grid_data self.old_grids_in_memory = enzo.old_grid_data self.my_slice = ( slice(ghost_zones, -ghost_zones), slice(ghost_zones, -ghost_zones), slice(ghost_zones, -ghost_zones), ) BaseIOHandler.__init__(self, ds) def _read_field_names(self, grid): fields = [] add_io = "io" in grid.ds.particle_types for name, v in self.grids_in_memory[grid.id].items(): # NOTE: This won't work with 1D datasets or references. if not hasattr(v, "shape") or v.dtype == "O": continue elif v.ndim == 1: if grid.ds.dimensionality == 1: fields.append(("enzo", str(name))) elif add_io: fields.append(("io", str(name))) else: fields.append(("enzo", str(name))) return fields def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} # Now we have to do something unpleasant chunks = list(chunks) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError g = chunks[0].objs[0] for ftype, fname in fields: rv[ftype, fname] = self.grids_in_memory[g.id][fname].swapaxes(0, 2) return rv if size is None: size = sum(g.count(selector) for chunk in chunks for g in chunk.objs) for field in fields: ftype, fname = field fsize = size rv[field] = np.empty(fsize, dtype="float64") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) ind = 0 for chunk in chunks: for g in chunk.objs: # We want a *hard error* here. # if g.id not in self.grids_in_memory: continue for field in fields: ftype, fname = field data_view = self.grids_in_memory[g.id][fname][ self.my_slice ].swapaxes(0, 2) nd = g.select(selector, data_view, rv[field], ind) ind += nd assert ind == fsize return rv def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) for chunk in chunks: # These should be organized by grid filename for g in chunk.objs: if g.id not in self.grids_in_memory: continue nap = sum(g.NumberOfActiveParticles.values()) if g.NumberOfParticles == 0 and nap == 0: continue for ptype in sorted(ptf): x, y, z = ( self.grids_in_memory[g.id]["particle_position_x"], self.grids_in_memory[g.id]["particle_position_y"], self.grids_in_memory[g.id]["particle_position_z"], ) yield ptype, (x, y, z), 0.0 def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) for chunk in chunks: # These should be organized by grid filename for g in chunk.objs: if g.id not in self.grids_in_memory: continue nap = sum(g.NumberOfActiveParticles.values()) if g.NumberOfParticles == 0 and nap == 0: continue for ptype, field_list in sorted(ptf.items()): x, y, z = ( self.grids_in_memory[g.id]["particle_position_x"], self.grids_in_memory[g.id]["particle_position_y"], self.grids_in_memory[g.id]["particle_position_z"], ) mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: data = self.grids_in_memory[g.id][field] if field in _convert_mass: data = data * g.dds.prod(dtype="f8") yield (ptype, field), data[mask] class IOHandlerPacked2D(IOHandlerPackedHDF5): _dataset_type = "enzo_packed_2d" _particle_reader = False def _read_data_set(self, grid, field): f = h5py.File(grid.filename, mode="r") ds = f["/Grid%08i/%s" % (grid.id, field)][:] f.close() return ds.transpose()[:, :, None] def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} # Now we have to do something unpleasant chunks = list(chunks) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError g = chunks[0].objs[0] f = h5py.File(g.filename, mode="r") gds = f.get("/Grid%08i" % g.id) for ftype, fname in fields: rv[ftype, fname] = np.atleast_3d(gds.get(fname)[()].transpose()) f.close() return rv if size is None: size = sum(g.count(selector) for chunk in chunks for g in chunk.objs) for field in fields: ftype, fname = field fsize = size rv[field] = np.empty(fsize, dtype="float64") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) ind = 0 for chunk in chunks: f = None for g in chunk.objs: if f is None: # print("Opening (count) %s" % g.filename) f = h5py.File(g.filename, mode="r") gds = f.get("/Grid%08i" % g.id) if gds is None: gds = f for field in fields: ftype, fname = field ds = np.atleast_3d(gds.get(fname)[()].transpose()) nd = g.select(selector, ds, rv[field], ind) # caches ind += nd f.close() return rv class IOHandlerPacked1D(IOHandlerPackedHDF5): _dataset_type = "enzo_packed_1d" _particle_reader = False def _read_data_set(self, grid, field): f = h5py.File(grid.filename, mode="r") ds = f["/Grid%08i/%s" % (grid.id, field)][:] f.close() return ds.transpose()[:, None, None] yt-project-yt-f043ac8/yt/frontends/enzo/misc.py000066400000000000000000000022601510711153200215700ustar00rootroot00000000000000import numpy as np from yt.utilities.physical_ratios import ( boltzmann_constant_erg_per_K, cm_per_mpc, mass_hydrogen_grams, newton_cgs, rho_crit_g_cm3_h2, ) def cosmology_get_units( hubble_constant, omega_matter, box_size, initial_redshift, current_redshift ): """ Return a dict of Enzo cosmological unit conversions. """ zp1 = 1.0 + current_redshift zip1 = 1.0 + initial_redshift k = {} # For better agreement with values calculated by Enzo, # adopt the exact constants that are used there. time_scaling = np.sqrt(1 / (4 * np.pi * newton_cgs * rho_crit_g_cm3_h2)) vel_scaling = cm_per_mpc / time_scaling temp_scaling = mass_hydrogen_grams / boltzmann_constant_erg_per_K * vel_scaling**2 k["utim"] = time_scaling / np.sqrt(omega_matter) / hubble_constant / zip1**1.5 k["urho"] = rho_crit_g_cm3_h2 * omega_matter * hubble_constant**2 * zp1**3 k["uxyz"] = cm_per_mpc * box_size / hubble_constant / zp1 k["uaye"] = 1.0 / zip1 k["uvel"] = vel_scaling * box_size * np.sqrt(omega_matter) * np.sqrt(zip1) k["utem"] = temp_scaling * (box_size**2) * omega_matter * zip1 k["aye"] = zip1 / zp1 return k yt-project-yt-f043ac8/yt/frontends/enzo/simulation_handling.py000066400000000000000000000704751510711153200247020ustar00rootroot00000000000000import glob import os import numpy as np from unyt import dimensions, unyt_array from unyt.unit_registry import UnitRegistry from yt.data_objects.time_series import DatasetSeries, SimulationTimeSeries from yt.funcs import only_on_root from yt.loaders import load from yt.utilities.cosmology import Cosmology from yt.utilities.exceptions import ( InvalidSimulationTimeSeries, MissingParameter, NoStoppingCondition, YTUnidentifiedDataType, ) from yt.utilities.logger import ytLogger as mylog from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_objects class EnzoSimulation(SimulationTimeSeries): r""" Initialize an Enzo Simulation object. Upon creation, the parameter file is parsed and the time and redshift are calculated and stored in all_outputs. A time units dictionary is instantiated to allow for time outputs to be requested with physical time units. The get_time_series can be used to generate a DatasetSeries object. parameter_filename : str The simulation parameter file. find_outputs : bool If True, subdirectories within the GlobalDir directory are searched one by one for datasets. Time and redshift information are gathered by temporarily instantiating each dataset. This can be used when simulation data was created in a non-standard way, making it difficult to guess the corresponding time and redshift information. Default: False. Examples -------- >>> import yt >>> es = yt.load_simulation("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo") >>> es.get_time_series() >>> for ds in es: ... print(ds.current_time) """ def __init__(self, parameter_filename, find_outputs=False): self.simulation_type = "grid" self.key_parameters = ["stop_cycle"] SimulationTimeSeries.__init__( self, parameter_filename, find_outputs=find_outputs ) def _set_units(self): self.unit_registry = UnitRegistry() self.unit_registry.add("code_time", 1.0, dimensions.time) self.unit_registry.add("code_length", 1.0, dimensions.length) if self.cosmological_simulation: # Instantiate EnzoCosmology object for units and time conversions. self.cosmology = EnzoCosmology( self.parameters["CosmologyHubbleConstantNow"], self.parameters["CosmologyOmegaMatterNow"], self.parameters["CosmologyOmegaLambdaNow"], self.parameters.get("CosmologyOmegaRadiationNow", 0.0), 0.0, self.parameters["CosmologyInitialRedshift"], unit_registry=self.unit_registry, ) self.time_unit = self.cosmology.time_unit.in_units("s") if "h" in self.unit_registry: self.unit_registry.modify("h", self.hubble_constant) else: self.unit_registry.add( "h", self.hubble_constant, dimensions.dimensionless ) # Comoving lengths for my_unit in ["m", "pc", "AU"]: new_unit = f"{my_unit}cm" # technically not true, but should be ok self.unit_registry.add( new_unit, self.unit_registry.lut[my_unit][0], dimensions.length, f"\\rm{{{my_unit}}}/(1+z)", prefixable=True, ) self.length_unit = self.quan( self.box_size, "Mpccm / h", registry=self.unit_registry ) else: self.time_unit = self.quan(self.parameters["TimeUnits"], "s") self.length_unit = self.quan(self.parameters["LengthUnits"], "cm") self.box_size = self.length_unit self.domain_left_edge = self.domain_left_edge * self.length_unit self.domain_right_edge = self.domain_right_edge * self.length_unit self.unit_registry.modify("code_time", self.time_unit) self.unit_registry.modify("code_length", self.length_unit) self.unit_registry.add( "unitary", float(self.box_size.in_base()), self.length_unit.units.dimensions ) def get_time_series( self, time_data=True, redshift_data=True, initial_time=None, final_time=None, initial_redshift=None, final_redshift=None, initial_cycle=None, final_cycle=None, times=None, redshifts=None, tolerance=None, parallel=True, setup_function=None, ): """ Instantiate a DatasetSeries object for a set of outputs. If no additional keywords given, a DatasetSeries object will be created with all potential datasets created by the simulation. Outputs can be gather by specifying a time or redshift range (or combination of time and redshift), with a specific list of times or redshifts, a range of cycle numbers (for cycle based output), or by simply searching all subdirectories within the simulation directory. time_data : bool Whether or not to include time outputs when gathering datasets for time series. Default: True. redshift_data : bool Whether or not to include redshift outputs when gathering datasets for time series. Default: True. initial_time : tuple of type (float, str) The earliest time for outputs to be included. This should be given as the value and the string representation of the units. For example, (5.0, "Gyr"). If None, the initial time of the simulation is used. This can be used in combination with either final_time or final_redshift. Default: None. final_time : tuple of type (float, str) The latest time for outputs to be included. This should be given as the value and the string representation of the units. For example, (13.7, "Gyr"). If None, the final time of the simulation is used. This can be used in combination with either initial_time or initial_redshift. Default: None. times : tuple of type (float array, str) A list of times for which outputs will be found and the units of those values. For example, ([0, 1, 2, 3], "s"). Default: None. initial_redshift : float The earliest redshift for outputs to be included. If None, the initial redshift of the simulation is used. This can be used in combination with either final_time or final_redshift. Default: None. final_redshift : float The latest redshift for outputs to be included. If None, the final redshift of the simulation is used. This can be used in combination with either initial_time or initial_redshift. Default: None. redshifts : array_like A list of redshifts for which outputs will be found. Default: None. initial_cycle : float The earliest cycle for outputs to be included. If None, the initial cycle of the simulation is used. This can only be used with final_cycle. Default: None. final_cycle : float The latest cycle for outputs to be included. If None, the final cycle of the simulation is used. This can only be used in combination with initial_cycle. Default: None. tolerance : float Used in combination with "times" or "redshifts" keywords, this is the tolerance within which outputs are accepted given the requested times or redshifts. If None, the nearest output is always taken. Default: None. parallel : bool/int If True, the generated DatasetSeries will divide the work such that a single processor works on each dataset. If an integer is supplied, the work will be divided into that number of jobs. Default: True. setup_function : callable, accepts a ds This function will be called whenever a dataset is loaded. Examples -------- >>> import yt >>> es = yt.load_simulation("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo") >>> es.get_time_series( ... initial_redshift=10, final_time=(13.7, "Gyr"), redshift_data=False ... ) >>> for ds in es: ... print(ds.current_time) >>> es.get_time_series(redshifts=[3, 2, 1, 0]) >>> for ds in es: ... print(ds.current_time) """ if ( initial_redshift is not None or final_redshift is not None ) and not self.cosmological_simulation: raise InvalidSimulationTimeSeries( "An initial or final redshift has been given for a " + "noncosmological simulation." ) if time_data and redshift_data: my_all_outputs = self.all_outputs elif time_data: my_all_outputs = self.all_time_outputs elif redshift_data: my_all_outputs = self.all_redshift_outputs else: raise InvalidSimulationTimeSeries( "Both time_data and redshift_data are False." ) if not my_all_outputs: DatasetSeries.__init__(self, outputs=[], parallel=parallel) mylog.info("0 outputs loaded into time series.") return # Apply selection criteria to the set. if times is not None: my_outputs = self._get_outputs_by_key( "time", times, tolerance=tolerance, outputs=my_all_outputs ) elif redshifts is not None: my_outputs = self._get_outputs_by_key( "redshift", redshifts, tolerance=tolerance, outputs=my_all_outputs ) elif initial_cycle is not None or final_cycle is not None: if initial_cycle is None: initial_cycle = 0 else: initial_cycle = max(initial_cycle, 0) if final_cycle is None: final_cycle = self.parameters["StopCycle"] else: final_cycle = min(final_cycle, self.parameters["StopCycle"]) my_outputs = my_all_outputs[ int( np.ceil(float(initial_cycle) / self.parameters["CycleSkipDataDump"]) ) : (final_cycle / self.parameters["CycleSkipDataDump"]) + 1 ] else: if initial_time is not None: if isinstance(initial_time, float): my_initial_time = self.quan(initial_time, "code_time") elif isinstance(initial_time, tuple) and len(initial_time) == 2: my_initial_time = self.quan(*initial_time) elif not isinstance(initial_time, unyt_array): raise RuntimeError( "Error: initial_time must be given as a float or " + "tuple of (value, units)." ) elif initial_redshift is not None: my_initial_time = self.cosmology.t_from_z(initial_redshift) else: my_initial_time = self.initial_time if final_time is not None: if isinstance(final_time, float): my_final_time = self.quan(final_time, "code_time") elif isinstance(final_time, tuple) and len(final_time) == 2: my_final_time = self.quan(*final_time) elif not isinstance(final_time, unyt_array): raise RuntimeError( "Error: final_time must be given as a float or " + "tuple of (value, units)." ) elif final_redshift is not None: my_final_time = self.cosmology.t_from_z(final_redshift) else: my_final_time = self.final_time my_initial_time.convert_to_units("s") my_final_time.convert_to_units("s") my_times = np.array([a["time"] for a in my_all_outputs]) my_indices = np.digitize([my_initial_time, my_final_time], my_times) if my_initial_time == my_times[my_indices[0] - 1]: my_indices[0] -= 1 my_outputs = my_all_outputs[my_indices[0] : my_indices[1]] init_outputs = [] for output in my_outputs: if os.path.exists(output["filename"]): init_outputs.append(output["filename"]) DatasetSeries.__init__( self, outputs=init_outputs, parallel=parallel, setup_function=setup_function ) mylog.info("%d outputs loaded into time series.", len(init_outputs)) def _parse_parameter_file(self): """ Parses the parameter file and establishes the various dictionaries. """ self.conversion_factors = {} redshift_outputs = [] # Let's read the file lines = open(self.parameter_filename).readlines() for line in (l.strip() for l in lines): if "#" in line: line = line[0 : line.find("#")] if "//" in line: line = line[0 : line.find("//")] if len(line) < 2: continue param, vals = (i.strip() for i in line.split("=", 1)) # First we try to decipher what type of value it is. vals = vals.split() # Special case approaching. if "(do" in vals: vals = vals[:1] if len(vals) == 0: pcast = str # Assume NULL output else: v = vals[0] # Figure out if it's castable to floating point: try: float(v) except ValueError: pcast = str else: if any("." in v or "e" in v for v in vals): pcast = float elif v == "inf": pcast = str else: pcast = int # Now we figure out what to do with it. if param.endswith("Units") and not param.startswith("Temperature"): dataType = param[:-5] # This one better be a float. self.conversion_factors[dataType] = float(vals[0]) if param.startswith("CosmologyOutputRedshift["): index = param[param.find("[") + 1 : param.find("]")] redshift_outputs.append( {"index": int(index), "redshift": float(vals[0])} ) elif len(vals) == 0: vals = "" elif len(vals) == 1: vals = pcast(vals[0]) else: vals = np.array([pcast(i) for i in vals if i != "-99999"]) self.parameters[param] = vals self.refine_by = self.parameters["RefineBy"] self.dimensionality = self.parameters["TopGridRank"] if self.dimensionality > 1: self.domain_dimensions = self.parameters["TopGridDimensions"] if len(self.domain_dimensions) < 3: tmp = self.domain_dimensions.tolist() tmp.append(1) self.domain_dimensions = np.array(tmp) self.domain_left_edge = np.array( self.parameters["DomainLeftEdge"], "float64" ).copy() self.domain_right_edge = np.array( self.parameters["DomainRightEdge"], "float64" ).copy() else: self.domain_left_edge = np.array( self.parameters["DomainLeftEdge"], "float64" ) self.domain_right_edge = np.array( self.parameters["DomainRightEdge"], "float64" ) self.domain_dimensions = np.array( [self.parameters["TopGridDimensions"], 1, 1] ) if self.parameters["ComovingCoordinates"]: cosmo_attr = { "box_size": "CosmologyComovingBoxSize", "omega_lambda": "CosmologyOmegaLambdaNow", "omega_matter": "CosmologyOmegaMatterNow", "omega_radiation": "CosmologyOmegaRadiationNow", "hubble_constant": "CosmologyHubbleConstantNow", "initial_redshift": "CosmologyInitialRedshift", "final_redshift": "CosmologyFinalRedshift", } self.cosmological_simulation = 1 for a, v in cosmo_attr.items(): if v not in self.parameters: raise MissingParameter(self.parameter_filename, v) setattr(self, a, self.parameters[v]) else: self.cosmological_simulation = 0 self.omega_lambda = self.omega_matter = self.hubble_constant = 0.0 # make list of redshift outputs self.all_redshift_outputs = [] if not self.cosmological_simulation: return for output in redshift_outputs: output["filename"] = os.path.join( self.parameters["GlobalDir"], "%s%04d" % (self.parameters["RedshiftDumpDir"], output["index"]), "%s%04d" % (self.parameters["RedshiftDumpName"], output["index"]), ) del output["index"] self.all_redshift_outputs = redshift_outputs def _calculate_time_outputs(self): """ Calculate time outputs and their redshifts if cosmological. """ self.all_time_outputs = [] if ( self.final_time is None or "dtDataDump" not in self.parameters or self.parameters["dtDataDump"] <= 0.0 ): return [] index = 0 current_time = self.initial_time.copy() dt_datadump = self.quan(self.parameters["dtDataDump"], "code_time") while current_time <= self.final_time + dt_datadump: filename = os.path.join( self.parameters["GlobalDir"], "%s%04d" % (self.parameters["DataDumpDir"], index), "%s%04d" % (self.parameters["DataDumpName"], index), ) output = {"index": index, "filename": filename, "time": current_time.copy()} output["time"] = min(output["time"], self.final_time) if self.cosmological_simulation: output["redshift"] = self.cosmology.z_from_t(current_time) self.all_time_outputs.append(output) if np.abs(self.final_time - current_time) / self.final_time < 1e-4: break current_time += dt_datadump index += 1 def _calculate_cycle_outputs(self): """ Calculate cycle outputs. """ mylog.warning("Calculating cycle outputs. Dataset times will be unavailable.") if ( self.stop_cycle is None or "CycleSkipDataDump" not in self.parameters or self.parameters["CycleSkipDataDump"] <= 0.0 ): return [] self.all_time_outputs = [] index = 0 for cycle in range( 0, self.stop_cycle + 1, self.parameters["CycleSkipDataDump"] ): filename = os.path.join( self.parameters["GlobalDir"], "%s%04d" % (self.parameters["DataDumpDir"], index), "%s%04d" % (self.parameters["DataDumpName"], index), ) output = {"index": index, "filename": filename, "cycle": cycle} self.all_time_outputs.append(output) index += 1 def _get_all_outputs(self, *, find_outputs=False): """ Get all potential datasets and combine into a time-sorted list. """ # Create the set of outputs from which further selection will be done. if find_outputs: self._find_outputs() elif ( self.parameters["dtDataDump"] > 0 and self.parameters["CycleSkipDataDump"] > 0 ): mylog.info( "Simulation %s has both dtDataDump and CycleSkipDataDump set.", self.parameter_filename, ) mylog.info( " Unable to calculate datasets. " "Attempting to search in the current directory" ) self._find_outputs() else: # Get all time or cycle outputs. if self.parameters["CycleSkipDataDump"] > 0: self._calculate_cycle_outputs() else: self._calculate_time_outputs() # Calculate times for redshift outputs. if self.cosmological_simulation: for output in self.all_redshift_outputs: output["time"] = self.cosmology.t_from_z(output["redshift"]) self.all_redshift_outputs.sort(key=lambda obj: obj["time"]) self.all_outputs = self.all_time_outputs + self.all_redshift_outputs if self.parameters["CycleSkipDataDump"] <= 0: self.all_outputs.sort(key=lambda obj: obj["time"].to_ndarray()) def _calculate_simulation_bounds(self): """ Figure out the starting and stopping time and redshift for the simulation. """ if "StopCycle" in self.parameters: self.stop_cycle = self.parameters["StopCycle"] # Convert initial/final redshifts to times. if self.cosmological_simulation: self.initial_time = self.cosmology.t_from_z(self.initial_redshift) self.initial_time.units.registry = self.unit_registry self.final_time = self.cosmology.t_from_z(self.final_redshift) self.final_time.units.registry = self.unit_registry # If not a cosmology simulation, figure out the stopping criteria. else: if "InitialTime" in self.parameters: self.initial_time = self.quan( self.parameters["InitialTime"], "code_time" ) else: self.initial_time = self.quan(0.0, "code_time") if "StopTime" in self.parameters: self.final_time = self.quan(self.parameters["StopTime"], "code_time") else: self.final_time = None if not ("StopTime" in self.parameters or "StopCycle" in self.parameters): raise NoStoppingCondition(self.parameter_filename) if self.final_time is None: mylog.warning( "Simulation %s has no stop time set, stopping condition " "will be based only on cycles.", self.parameter_filename, ) def _set_parameter_defaults(self): """ Set some default parameters to avoid problems if they are not in the parameter file. """ self.parameters["GlobalDir"] = self.directory self.parameters["DataDumpName"] = "data" self.parameters["DataDumpDir"] = "DD" self.parameters["RedshiftDumpName"] = "RedshiftOutput" self.parameters["RedshiftDumpDir"] = "RD" self.parameters["ComovingCoordinates"] = 0 self.parameters["TopGridRank"] = 3 self.parameters["DomainLeftEdge"] = np.zeros(self.parameters["TopGridRank"]) self.parameters["DomainRightEdge"] = np.ones(self.parameters["TopGridRank"]) self.parameters["RefineBy"] = 2 # technically not the enzo default self.parameters["StopCycle"] = 100000 self.parameters["dtDataDump"] = 0.0 self.parameters["CycleSkipDataDump"] = 0.0 self.parameters["LengthUnits"] = 1.0 self.parameters["TimeUnits"] = 1.0 self.parameters["CosmologyOmegaRadiationNow"] = 0.0 def _find_outputs(self): """ Search for directories matching the data dump keywords. If found, get dataset times py opening the ds. """ # look for time outputs. potential_time_outputs = glob.glob( os.path.join( self.parameters["GlobalDir"], f"{self.parameters['DataDumpDir']}*" ) ) self.all_time_outputs = self._check_for_outputs(potential_time_outputs) self.all_time_outputs.sort(key=lambda obj: obj["time"]) # look for redshift outputs. potential_redshift_outputs = glob.glob( os.path.join( self.parameters["GlobalDir"], f"{self.parameters['RedshiftDumpDir']}*" ) ) self.all_redshift_outputs = self._check_for_outputs(potential_redshift_outputs) self.all_redshift_outputs.sort(key=lambda obj: obj["time"]) self.all_outputs = self.all_time_outputs + self.all_redshift_outputs self.all_outputs.sort(key=lambda obj: obj["time"]) only_on_root(mylog.info, "Located %d total outputs.", len(self.all_outputs)) # manually set final time and redshift with last output if self.all_outputs: self.final_time = self.all_outputs[-1]["time"] if self.cosmological_simulation: self.final_redshift = self.all_outputs[-1]["redshift"] def _check_for_outputs(self, potential_outputs): """ Check a list of files to see if they are valid datasets. """ only_on_root( mylog.info, "Checking %d potential outputs.", len(potential_outputs) ) my_outputs = {} llevel = mylog.level # suppress logging as we load every dataset, unless set to debug if llevel > 10 and llevel < 40: mylog.setLevel(40) for my_storage, output in parallel_objects( potential_outputs, storage=my_outputs ): if self.parameters["DataDumpDir"] in output: dir_key = self.parameters["DataDumpDir"] output_key = self.parameters["DataDumpName"] else: dir_key = self.parameters["RedshiftDumpDir"] output_key = self.parameters["RedshiftDumpName"] index = output[output.find(dir_key) + len(dir_key) :] filename = os.path.join( self.parameters["GlobalDir"], f"{dir_key}{index}", f"{output_key}{index}", ) try: ds = load(filename) except (FileNotFoundError, YTUnidentifiedDataType): mylog.error("Failed to load %s", filename) continue my_storage.result = { "filename": filename, "time": ds.current_time.in_units("s"), } if ds.cosmological_simulation: my_storage.result["redshift"] = ds.current_redshift mylog.setLevel(llevel) my_outputs = [ my_output for my_output in my_outputs.values() if my_output is not None ] return my_outputs def _write_cosmology_outputs(self, filename, outputs, start_index, decimals=3): """ Write cosmology output parameters for a cosmology splice. """ mylog.info("Writing redshift output list to %s.", filename) f = open(filename, "w") for q, output in enumerate(outputs): f.write( (f"CosmologyOutputRedshift[%d] = %.{decimals}f\n") % ((q + start_index), output["redshift"]) ) f.close() class EnzoCosmology(Cosmology): def __init__( self, hubble_constant, omega_matter, omega_lambda, omega_radiation, omega_curvature, initial_redshift, unit_registry=None, ): Cosmology.__init__( self, hubble_constant=hubble_constant, omega_matter=omega_matter, omega_lambda=omega_lambda, omega_radiation=omega_radiation, omega_curvature=omega_curvature, unit_registry=unit_registry, ) self.initial_redshift = initial_redshift self.initial_time = self.t_from_z(self.initial_redshift) # time units = 1 / sqrt(4 * pi * G rho_0 * (1 + z_i)**3), # rho_0 = (3 * Omega_m * h**2) / (8 * pi * G) self.time_unit = ( ( 1.5 * self.omega_matter * self.hubble_constant**2 * (1 + self.initial_redshift) ** 3 ) ** -0.5 ).in_units("s") self.time_unit.units.registry = self.unit_registry yt-project-yt-f043ac8/yt/frontends/enzo/tests/000077500000000000000000000000001510711153200214255ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo/tests/__init__.py000066400000000000000000000000001510711153200235240ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo/tests/test_outputs.py000066400000000000000000000214761510711153200245730ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_array_equal, assert_equal from yt.frontends.enzo.api import EnzoDataset from yt.frontends.enzo.fields import NODAL_FLAGS from yt.testing import ( assert_allclose_units, requires_file, requires_module, units_override_check, ) from yt.utilities.answer_testing.framework import ( big_patch_amr, data_dir_load, requires_ds, small_patch_amr, ) from yt.visualization.plot_window import SlicePlot _fields = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ("gas", "velocity_divergence"), ) two_sphere_test = "ActiveParticleTwoSphere/DD0011/DD0011" active_particle_cosmology = "ActiveParticleCosmology/DD0046/DD0046" ecp = "enzo_cosmology_plus/DD0046/DD0046" g30 = "IsolatedGalaxy/galaxy0030/galaxy0030" enzotiny = "enzo_tiny_cosmology/DD0046/DD0046" toro1d = "ToroShockTube/DD0001/data0001" kh2d = "EnzoKelvinHelmholtz/DD0011/DD0011" mhdctot = "MHDCTOrszagTang/DD0004/data0004" dnz = "DeeplyNestedZoom/DD0025/data0025" p3mini = "PopIII_mini/DD0034/DD0034" def color_conservation(ds): species_names = ds.field_info.species_names dd = ds.all_data() dens_yt = dd["gas", "density"].copy() # Enumerate our species here for s in sorted(species_names): if s == "El": continue dens_yt -= dd[f"{s}_density"] dens_yt -= dd["enzo", "Metal_Density"] delta_yt = np.abs(dens_yt / dd["gas", "density"]) # Now we compare color conservation to Enzo's color conservation dd = ds.all_data() dens_enzo = dd["enzo", "Density"].copy() for f in sorted(ds.field_list): ff = f[1] if not ff.endswith("_Density"): continue start_strings = [ "Electron_", "SFR_", "Forming_Stellar_", "Dark_Matter", "Star_Particle_", ] if any(ff.startswith(ss) for ss in start_strings): continue dens_enzo -= dd[f] delta_enzo = np.abs(dens_enzo / dd["enzo", "Density"]) np.testing.assert_almost_equal(delta_yt, delta_enzo) def check_color_conservation(ds): species_names = ds.field_info.species_names dd = ds.all_data() dens_yt = dd["gas", "density"].copy() # Enumerate our species here for s in sorted(species_names): if s == "El": continue dens_yt -= dd[f"{s}_density"] dens_yt -= dd["enzo", "Metal_Density"] delta_yt = np.abs(dens_yt / dd["gas", "density"]) # Now we compare color conservation to Enzo's color conservation dd = ds.all_data() dens_enzo = dd["enzo", "Density"].copy() for f in sorted(ds.field_list): ff = f[1] if not ff.endswith("_Density"): continue start_strings = [ "Electron_", "SFR_", "Forming_Stellar_", "Dark_Matter", "Star_Particle_", ] if any(ff.startswith(ss) for ss in start_strings): continue dens_enzo -= dd[f] delta_enzo = np.abs(dens_enzo / dd["enzo", "Density"]) return assert_almost_equal, delta_yt, delta_enzo m7 = "DD0010/moving7_0010" @requires_module("h5py") @requires_ds(m7) def test_moving7(): ds = data_dir_load(m7) assert_equal(str(ds), "moving7_0010") for test in small_patch_amr(m7, _fields): test_moving7.__name__ = test.description yield test @requires_module("h5py") @requires_ds(g30, big_data=True) def test_galaxy0030(): ds = data_dir_load(g30) yield check_color_conservation(ds) assert_equal(str(ds), "galaxy0030") for test in big_patch_amr(ds, _fields): test_galaxy0030.__name__ = test.description yield test assert_equal(ds.particle_type_counts, {"io": 1124453}) @requires_module("h5py") @requires_ds(toro1d) def test_toro1d(): ds = data_dir_load(toro1d) assert_equal(str(ds), "data0001") for test in small_patch_amr(ds, ds.field_list): test_toro1d.__name__ = test.description yield test @requires_module("h5py") @requires_ds(kh2d) def test_kh2d(): ds = data_dir_load(kh2d) assert_equal(str(ds), "DD0011") for test in small_patch_amr(ds, ds.field_list): test_kh2d.__name__ = test.description yield test @requires_module("h5py") @requires_ds(ecp, big_data=True) def test_ecp(): ds = data_dir_load(ecp) # Now we test our species fields yield check_color_conservation(ds) @requires_module("h5py") @requires_file(enzotiny) def test_units_override(): units_override_check(enzotiny) @requires_module("h5py") @requires_ds(ecp, big_data=True) def test_nuclei_density_fields(): ds = data_dir_load(ecp) ad = ds.all_data() assert_array_equal( ad["gas", "H_nuclei_density"], (ad["gas", "H_p0_number_density"] + ad["gas", "H_p1_number_density"]), ) assert_array_equal( ad["gas", "He_nuclei_density"], ( ad["gas", "He_p0_number_density"] + ad["gas", "He_p1_number_density"] + ad["gas", "He_p2_number_density"] ), ) @requires_module("h5py") @requires_file(enzotiny) def test_EnzoDataset(): assert isinstance(data_dir_load(enzotiny), EnzoDataset) @requires_module("h5py") @requires_file(two_sphere_test) @requires_file(active_particle_cosmology) def test_active_particle_datasets(): two_sph = data_dir_load(two_sphere_test) assert "AccretingParticle" in two_sph.particle_types_raw assert "io" not in two_sph.particle_types_raw assert "all" in two_sph.particle_types assert "nbody" in two_sph.particle_types assert_equal(len(two_sph.particle_unions), 2) pfields = [ "GridID", "creation_time", "dynamical_time", "identifier", "level", "metallicity", "particle_mass", ] pfields += [f"particle_position_{d}" for d in "xyz"] pfields += [f"particle_velocity_{d}" for d in "xyz"] acc_part_fields = [("AccretingParticle", pf) for pf in ["AccretionRate"] + pfields] real_acc_part_fields = sorted( f for f in two_sph.field_list if f[0] == "AccretingParticle" ) assert_equal(acc_part_fields, real_acc_part_fields) apcos = data_dir_load(active_particle_cosmology) assert_equal(["CenOstriker", "DarkMatter"], apcos.particle_types_raw) assert "all" in apcos.particle_unions assert "nbody" in apcos.particle_unions apcos_fields = [("CenOstriker", pf) for pf in pfields] real_apcos_fields = sorted(f for f in apcos.field_list if f[0] == "CenOstriker") assert_equal(apcos_fields, real_apcos_fields) assert_equal( apcos.particle_type_counts, {"CenOstriker": 899755, "DarkMatter": 32768} ) @requires_module("h5py") @requires_file(mhdctot) def test_face_centered_mhdct_fields(): ds = data_dir_load(mhdctot) ad = ds.all_data() grid = ds.index.grids[0] for field, flag in NODAL_FLAGS.items(): dims = ds.domain_dimensions assert_equal(ad[field].shape, (dims.prod(), 2 * sum(flag))) assert_equal(grid[field].shape, tuple(dims) + (2 * sum(flag),)) # Average of face-centered fields should be the same as cell-centered field assert (ad["enzo", "BxF"].sum(axis=-1) / 2 == ad["enzo", "Bx"]).all() assert (ad["enzo", "ByF"].sum(axis=-1) / 2 == ad["enzo", "By"]).all() assert (ad["enzo", "BzF"].sum(axis=-1) / 2 == ad["enzo", "Bz"]).all() @requires_module("h5py") @requires_file(dnz) def test_deeply_nested_zoom(): ds = data_dir_load(dnz) # carefully chosen to just barely miss a grid in the middle of the image center = [0.4915073260199302, 0.5052605316800006, 0.4905805557500548] plot = SlicePlot(ds, "z", "density", width=(0.001, "pc"), center=center) image = plot.frb["gas", "density"] assert (image > 0).all() v, c = ds.find_max(("gas", "density")) assert_allclose_units(v, ds.quan(0.005878286377124154, "g/cm**3")) c_actual = [0.49150732540021, 0.505260532936791, 0.49058055816398] c_actual = ds.arr(c_actual, "code_length") assert_allclose_units(c, c_actual) assert_equal(max(g["gas", "density"].max() for g in ds.index.grids), v) @requires_module("h5py") @requires_file(kh2d) def test_2d_grid_shape(): # see issue #1601 # we want to make sure that accessing data on a grid object # returns a 3D array with a dummy dimension. ds = data_dir_load(kh2d) g = ds.index.grids[1] assert g["gas", "density"].shape == (128, 100, 1) @requires_module("h5py") @requires_file(p3mini) def test_nonzero_omega_radiation(): """ Test support for non-zero omega_radiation cosmologies. """ ds = data_dir_load(p3mini) assert_equal(ds.omega_radiation, ds.cosmology.omega_radiation) tratio = ds.current_time / ds.cosmology.t_from_z(ds.current_redshift) assert_almost_equal( tratio, 1, 4, err_msg="Simulation time not consistent with cosmology calculator.", ) yt-project-yt-f043ac8/yt/frontends/enzo_e/000077500000000000000000000000001510711153200205675ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo_e/__init__.py000066400000000000000000000000001510711153200226660ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo_e/api.py000066400000000000000000000003071510711153200217120ustar00rootroot00000000000000from . import tests from .data_structures import EnzoEDataset, EnzoEGrid, EnzoEHierarchy from .fields import EnzoEFieldInfo from .io import EnzoEIOHandler add_enzoe_field = EnzoEFieldInfo.add_field yt-project-yt-f043ac8/yt/frontends/enzo_e/data_structures.py000066400000000000000000000460321510711153200243620ustar00rootroot00000000000000import os from functools import cached_property import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.field_info_container import NullFunc from yt.frontends.enzo.misc import cosmology_get_units from yt.frontends.enzo_e.fields import EnzoEFieldInfo from yt.frontends.enzo_e.misc import ( get_block_info, get_child_index, get_listed_subparam, get_root_block_id, get_root_blocks, is_parent, nested_dict_get, ) from yt.funcs import get_pbar, setdefaultattr from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.cosmology import Cosmology from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py, _libconf as libconf class EnzoEGrid(AMRGridPatch): """ Class representing a single EnzoE Grid instance. """ _id_offset = 0 _refine_by = 2 def __init__(self, id, index, block_name, filename=None): """ Returns an instance of EnzoEGrid with *id*, associated with *filename* and *index*. """ # All of the field parameters will be passed to us as needed. AMRGridPatch.__init__(self, id, filename=filename, index=index) self.block_name = block_name self._children_ids = None self._parent_id = -1 self.Level = -1 def __repr__(self): return "EnzoEGrid_%04d" % self.id def _prepare_grid(self): """Copies all the appropriate attributes from the index.""" h = self.index # cache it my_ind = self.id - self._id_offset self.ActiveDimensions = h.grid_dimensions[my_ind] self.LeftEdge = h.grid_left_edge[my_ind] self.RightEdge = h.grid_right_edge[my_ind] def get_parent_id(self, desc_block_name): if self.block_name == desc_block_name: raise RuntimeError("Child and parent are the same!") dim = self.ds.dimensionality d_block = desc_block_name[1:].replace(":", "") parent = self while True: a_block = parent.block_name[1:].replace(":", "") gengap = (len(d_block) - len(a_block)) / dim if gengap <= 1: return parent.id cid = get_child_index(a_block, d_block) parent = self.index.grids[parent._children_ids[cid]] def add_child(self, child): if self._children_ids is None: self._children_ids = -1 * np.ones( self._refine_by**self.ds.dimensionality, dtype=np.int64 ) a_block = self.block_name[1:].replace(":", "") d_block = child.block_name[1:].replace(":", "") cid = get_child_index(a_block, d_block) self._children_ids[cid] = child.id @cached_property def particle_count(self): with h5py.File(self.filename, mode="r") as f: fnstr = "{}/{}".format( self.block_name, self.ds.index.io._sep.join(["particle", "%s", "%s"]), ) return { ptype: f.get(fnstr % (ptype, pfield)).size for ptype, pfield in self.ds.index.io.sample_pfields.items() } @cached_property def total_particles(self) -> int: return sum(self.particle_count.values()) @property def Parent(self): if self._parent_id == -1: return None return self.index.grids[self._parent_id] @property def Children(self): if self._children_ids is None: return [] return [self.index.grids[cid] for cid in self._children_ids] class EnzoEHierarchy(GridIndex): _strip_path = False grid = EnzoEGrid _preload_implemented = True def __init__(self, ds, dataset_type): self.dataset_type = dataset_type self.directory = os.path.dirname(ds.parameter_filename) self.index_filename = ds.parameter_filename if os.path.getsize(self.index_filename) == 0: raise OSError(-1, "File empty", self.index_filename) GridIndex.__init__(self, ds, dataset_type) self.dataset.dataset_type = self.dataset_type def _count_grids(self): fblock_size = 32768 f = open(self.ds.parameter_filename) f.seek(0, 2) file_size = f.tell() nblocks = np.ceil(float(file_size) / fblock_size).astype(np.int64) f.seek(0) offset = f.tell() ngrids = 0 for _ in range(nblocks): my_block = min(fblock_size, file_size - offset) buff = f.read(my_block) ngrids += buff.count("\n") offset += my_block f.close() self.num_grids = ngrids self.dataset_type = "enzo_e" def _parse_index(self): self.grids = np.empty(self.num_grids, dtype="object") c = 1 pbar = get_pbar("Parsing Hierarchy", self.num_grids) f = open(self.ds.parameter_filename) fblock_size = 32768 f.seek(0, 2) file_size = f.tell() nblocks = np.ceil(float(file_size) / fblock_size).astype(np.int64) f.seek(0) offset = f.tell() lstr = "" # place child blocks after the root blocks rbdim = self.ds.root_block_dimensions nroot_blocks = rbdim.prod() child_id = nroot_blocks last_pid = None for _ib in range(nblocks): fblock = min(fblock_size, file_size - offset) buff = lstr + f.read(fblock) bnl = 0 for _inl in range(buff.count("\n")): nnl = buff.find("\n", bnl) line = buff[bnl:nnl] block_name, block_file = line.split() # Handling of the B, B_, and B__ blocks is consistent with # other unrefined blocks level, left, right = get_block_info(block_name) rbindex = get_root_block_id(block_name) rbid = ( rbindex[0] * rbdim[1:].prod() + rbindex[1] * rbdim[2:].prod() + rbindex[2] ) # There are also blocks at lower level than the # real root blocks. These can be ignored. if level == 0: check_root = get_root_blocks(block_name).prod() if check_root < nroot_blocks: level = -1 if level == -1: grid_id = child_id parent_id = -1 child_id += 1 elif level == 0: grid_id = rbid parent_id = -1 else: grid_id = child_id # Try the last parent_id first if last_pid is not None and is_parent( self.grids[last_pid].block_name, block_name ): parent_id = last_pid else: parent_id = self.grids[rbid].get_parent_id(block_name) last_pid = parent_id child_id += 1 my_grid = self.grid( grid_id, self, block_name, filename=os.path.join(self.directory, block_file), ) my_grid.Level = level my_grid._parent_id = parent_id self.grids[grid_id] = my_grid self.grid_levels[grid_id] = level self.grid_left_edge[grid_id] = left self.grid_right_edge[grid_id] = right self.grid_dimensions[grid_id] = self.ds.active_grid_dimensions if level > 0: self.grids[parent_id].add_child(my_grid) bnl = nnl + 1 pbar.update(c) c += 1 lstr = buff[bnl:] offset += fblock f.close() pbar.finish() slope = self.ds.domain_width / self.ds.arr(np.ones(3), "code_length") self.grid_left_edge = self.grid_left_edge * slope + self.ds.domain_left_edge self.grid_right_edge = self.grid_right_edge * slope + self.ds.domain_left_edge def _populate_grid_objects(self): for g in self.grids: g._prepare_grid() g._setup_dx() self.max_level = self.grid_levels.max() def _setup_derived_fields(self): super()._setup_derived_fields() for fname, field in self.ds.field_info.items(): if not field.particle_type: continue if isinstance(fname, tuple): continue if field._function is NullFunc: continue def _get_particle_type_counts(self): return { ptype: sum(g.particle_count[ptype] for g in self.grids) for ptype in self.ds.particle_types_raw } def _detect_output_fields(self): self.field_list = [] # Do this only on the root processor to save disk work. if self.comm.rank in (0, None): # Just check the first grid. grid = self.grids[0] field_list, ptypes = self.io._read_field_names(grid) mylog.debug("Grid %s has: %s", grid.id, field_list) sample_pfields = self.io.sample_pfields else: field_list = None ptypes = None sample_pfields = None self.field_list = list(self.comm.mpi_bcast(field_list)) self.dataset.particle_types = list(self.comm.mpi_bcast(ptypes)) self.dataset.particle_types_raw = self.dataset.particle_types[:] self.io.sample_pfields = self.comm.mpi_bcast(sample_pfields) class EnzoEDataset(Dataset): """ Enzo-E-specific output, set at a fixed time. """ _load_requirements = ["h5py", "libconf"] refine_by = 2 _index_class = EnzoEHierarchy _field_info_class = EnzoEFieldInfo _suffix = ".block_list" particle_types: tuple[str, ...] = () particle_types_raw = None def __init__( self, filename, dataset_type=None, parameter_override=None, conversion_override=None, storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): """ This class is a stripped down class that simply reads and parses *filename* without looking at the index. *dataset_type* gets passed to the index to pre-determine the style of data-output. However, it is not strictly necessary. Optionally you may specify a *parameter_override* dictionary that will override anything in the parameter file and a *conversion_override* dictionary that consists of {fieldname : conversion_to_cgs} that will override the #DataCGS. """ self.fluid_types += ("enzoe",) if parameter_override is None: parameter_override = {} self._parameter_override = parameter_override if conversion_override is None: conversion_override = {} self._conversion_override = conversion_override self.storage_filename = storage_filename Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) def _parse_parameter_file(self): """ Parses the parameter file and establishes the various dictionaries. """ f = open(self.parameter_filename) # get dimension from first block name b0, fn0 = f.readline().strip().split() level0, left0, right0 = get_block_info(b0, min_dim=0) root_blocks = get_root_blocks(b0) f.close() self.dimensionality = left0.size self._periodicity = tuple(np.ones(self.dimensionality, dtype=bool)) lcfn = self.parameter_filename[: -len(self._suffix)] + ".libconfig" if os.path.exists(lcfn): with open(lcfn) as lf: self.parameters = libconf.load(lf) # Enzo-E ignores all cosmology parameters if "cosmology" is not in # the Physics:list parameter physics_list = nested_dict_get( self.parameters, ("Physics", "list"), default=[] ) if "cosmology" in physics_list: self.cosmological_simulation = 1 co_pars = [ "hubble_constant_now", "omega_matter_now", "omega_lambda_now", "comoving_box_size", "initial_redshift", ] co_dict = { attr: nested_dict_get( self.parameters, ("Physics", "cosmology", attr) ) for attr in co_pars } for attr in ["hubble_constant", "omega_matter", "omega_lambda"]: setattr(self, attr, co_dict[f"{attr}_now"]) # Current redshift is not stored, so it's not possible # to set all cosmological units yet. # Get the time units and use that to figure out redshift. k = cosmology_get_units( self.hubble_constant, self.omega_matter, co_dict["comoving_box_size"], co_dict["initial_redshift"], 0, ) setdefaultattr(self, "time_unit", self.quan(k["utim"], "s")) co = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) else: self.cosmological_simulation = 0 else: self.cosmological_simulation = 0 fh = h5py.File(os.path.join(self.directory, fn0), "r") self.domain_left_edge = fh.attrs["lower"] self.domain_right_edge = fh.attrs["upper"] if "version" in fh.attrs: version = fh.attrs.get("version").tobytes().decode("ascii") else: version = None # earliest recorded version is '0.9.0' self.parameters["version"] = version # all blocks are the same size ablock = fh[list(fh.keys())[0]] self.current_time = ablock.attrs["time"][0] self.parameters["current_cycle"] = ablock.attrs["cycle"][0] gsi = ablock.attrs["enzo_GridStartIndex"] gei = ablock.attrs["enzo_GridEndIndex"] assert len(gsi) == len(gei) == 3 # sanity check # Enzo-E technically allows each axis to have different ghost zone # depths (this feature is not really used in practice) self.ghost_zones = gsi assert (self.ghost_zones[self.dimensionality :] == 0).all() # sanity check self.root_block_dimensions = root_blocks self.active_grid_dimensions = gei - gsi + 1 self.grid_dimensions = ablock.attrs["enzo_GridDimension"] self.domain_dimensions = root_blocks * self.active_grid_dimensions fh.close() if self.cosmological_simulation: self.current_redshift = co.z_from_t(self.current_time * self.time_unit) self._periodicity += (False,) * (3 - self.dimensionality) self._parse_fluid_prop_params() def _parse_fluid_prop_params(self): """ Parse the fluid properties. """ fp_params = nested_dict_get( self.parameters, ("Physics", "fluid_props"), default=None ) if fp_params is not None: # in newer versions of enzo-e, this data is specified in a # centralized parameter group called Physics:fluid_props # - for internal reasons related to backwards compatibility, # treatment of this physics-group is somewhat special (compared # to the cosmology group). The parameters in this group are # honored even if Physics:list does not include "fluid_props" self.gamma = nested_dict_get(fp_params, ("eos", "gamma")) de_type = nested_dict_get( fp_params, ("dual_energy", "type"), default="disabled" ) uses_de = de_type != "disabled" else: # in older versions, these parameters were more scattered self.gamma = nested_dict_get(self.parameters, ("Field", "gamma")) uses_de = False for method in ("ppm", "mhd_vlct"): subparams = get_listed_subparam( self.parameters, "Method", method, default=None ) if subparams is not None: uses_de = subparams.get("dual_energy", False) self.parameters["uses_dual_energy"] = uses_de def _set_code_unit_attributes(self): if self.cosmological_simulation: box_size = self.parameters["Physics"]["cosmology"]["comoving_box_size"] k = cosmology_get_units( self.hubble_constant, self.omega_matter, box_size, self.parameters["Physics"]["cosmology"]["initial_redshift"], self.current_redshift, ) # Now some CGS values setdefaultattr(self, "length_unit", self.quan(box_size, "Mpccm/h")) setdefaultattr( self, "mass_unit", self.quan(k["urho"], "g/cm**3") * (self.length_unit.in_cgs()) ** 3, ) setdefaultattr(self, "velocity_unit", self.quan(k["uvel"], "cm/s")) else: p = self.parameters for d, u in [("length", "cm"), ("time", "s")]: val = nested_dict_get(p, ("Units", d), default=1) setdefaultattr(self, f"{d}_unit", self.quan(val, u)) mass = nested_dict_get(p, ("Units", "mass")) if mass is None: density = nested_dict_get(p, ("Units", "density")) if density is not None: mass = density * self.length_unit**3 else: mass = 1 setdefaultattr(self, "mass_unit", self.quan(mass, "g")) setdefaultattr(self, "velocity_unit", self.length_unit / self.time_unit) magnetic_unit = np.sqrt( 4 * np.pi * self.mass_unit / (self.time_unit**2 * self.length_unit) ) magnetic_unit = np.float64(magnetic_unit.in_cgs()) setdefaultattr(self, "magnetic_unit", self.quan(magnetic_unit, "gauss")) def __str__(self): return self.basename[: -len(self._suffix)] @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: ddir = os.path.dirname(filename) if not filename.endswith(cls._suffix): return False if cls._missing_load_requirements(): return False try: with open(filename) as f: block, block_file = f.readline().strip().split() get_block_info(block) if not os.path.exists(os.path.join(ddir, block_file)): return False except Exception: return False return True yt-project-yt-f043ac8/yt/frontends/enzo_e/definitions.py000066400000000000000000000000001510711153200234420ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo_e/fields.py000066400000000000000000000137701510711153200224170ustar00rootroot00000000000000import numpy as np from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.fields.particle_fields import add_union_field from yt.frontends.enzo_e.misc import ( get_listed_subparam, get_particle_mass_correction, nested_dict_get, ) rho_units = "code_mass / code_length**3" vel_units = "code_velocity" acc_units = "code_velocity / code_time" energy_units = "code_velocity**2" b_units = "code_magnetic" NODAL_FLAGS = { "bfieldi_x": [1, 0, 0], "bfieldi_y": [0, 1, 0], "bfieldi_z": [0, 0, 1], } class EnzoEFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("velocity_x", (vel_units, ["velocity_x"], None)), ("velocity_y", (vel_units, ["velocity_y"], None)), ("velocity_z", (vel_units, ["velocity_z"], None)), ("acceleration_x", (acc_units, ["acceleration_x"], None)), ("acceleration_y", (acc_units, ["acceleration_y"], None)), ("acceleration_z", (acc_units, ["acceleration_z"], None)), ("density", (rho_units, ["density"], None)), ("density_total", (rho_units, ["total_density"], None)), ("total_energy", (energy_units, ["specific_total_energy"], None)), ("internal_energy", (energy_units, ["specific_thermal_energy"], None)), ("bfield_x", (b_units, [], None)), ("bfield_y", (b_units, [], None)), ("bfield_z", (b_units, [], None)), ("bfieldi_x", (b_units, [], None)), ("bfieldi_y", (b_units, [], None)), ("bfieldi_z", (b_units, [], None)), ) known_particle_fields: KnownFieldsT = ( ("x", ("code_length", ["particle_position_x"], None)), ("y", ("code_length", ["particle_position_y"], None)), ("z", ("code_length", ["particle_position_z"], None)), ("vx", (vel_units, ["particle_velocity_x"], None)), ("vy", (vel_units, ["particle_velocity_y"], None)), ("vz", (vel_units, ["particle_velocity_z"], None)), ("ax", (acc_units, ["particle_acceleration_x"], None)), ("ay", (acc_units, ["particle_acceleration_y"], None)), ("az", (acc_units, ["particle_acceleration_z"], None)), ("mass", ("code_mass", ["particle_mass"], None)), ) def __init__(self, ds, field_list, slice_info=None): super().__init__(ds, field_list, slice_info=slice_info) # setup nodal flag information for field, arr in NODAL_FLAGS.items(): if ("enzoe", field) in self: finfo = self["enzoe", field] finfo.nodal_flag = np.array(arr) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases self.setup_energy_field() setup_magnetic_field_aliases(self, "enzoe", [f"bfield_{ax}" for ax in "xyz"]) def setup_energy_field(self): unit_system = self.ds.unit_system # check if we have a field for internal energy has_ie_field = ("enzoe", "internal_energy") in self.field_list # check if we need to account for magnetic energy vlct_params = get_listed_subparam(self.ds.parameters, "Method", "mhd_vlct", {}) has_magnetic = "no_bfield" != vlct_params.get("mhd_choice", "no_bfield") # define the ("gas", "specific_thermal_energy") field # - this is already done for us if the simulation used the dual-energy # formalism AND ("enzoe", "internal_energy") was saved to disk if not (self.ds.parameters["uses_dual_energy"] and has_ie_field): def _tot_minus_kin(field, data): return ( data["enzoe", "total_energy"] - 0.5 * data["gas", "velocity_magnitude"] ** 2.0 ) if not has_magnetic: # thermal energy = total energy - kinetic energy self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_tot_minus_kin, units=unit_system["specific_energy"], ) else: # thermal energy = total energy - kinetic energy - magnetic energy def _sub_b(field, data): return ( _tot_minus_kin(field, data) - data["gas", "magnetic_energy_density"] / data["gas", "density"] ) self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_sub_b, units=unit_system["specific_energy"], ) def setup_particle_fields(self, ptype, ftype="gas", num_neighbors=64): super().setup_particle_fields(ptype, ftype=ftype, num_neighbors=num_neighbors) self.setup_particle_mass_field(ptype) def setup_particle_mass_field(self, ptype): fname = "particle_mass" if ptype in self.ds.particle_unions: add_union_field(self, ptype, fname, "code_mass") return pdict = self.ds.parameters.get("Particle", None) if pdict is None: return constants = nested_dict_get(pdict, (ptype, "constants"), default=()) if not constants: return # constants should be a tuple consisting of multiple tuples of (name, type, value). # When there is only one entry, the enclosing tuple gets stripped, so we put it back. if not isinstance(constants[0], tuple): constants = (constants,) names = [c[0] for c in constants] if "mass" not in names: return val = constants[names.index("mass")][2] * self.ds.mass_unit if not self.ds.index.io._particle_mass_is_mass: val = val * get_particle_mass_correction(self.ds) def _pmass(field, data): return val * data[ptype, "particle_ones"] self.add_field( (ptype, fname), function=_pmass, units="code_mass", sampling_type="particle", ) yt-project-yt-f043ac8/yt/frontends/enzo_e/io.py000066400000000000000000000210661510711153200215550ustar00rootroot00000000000000import numpy as np from yt.frontends.enzo_e.misc import get_particle_mass_correction, nested_dict_get from yt.utilities.exceptions import YTException from yt.utilities.io_handler import BaseIOHandler from yt.utilities.on_demand_imports import _h5py as h5py class EnzoEIOHandler(BaseIOHandler): _dataset_type = "enzo_e" _base = slice(None) _field_dtype = "float64" _sep = "_" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # precompute the indexing specifying each field's active zone # -> this assumes that each field in Enzo-E shares the same number of # ghost-zones. Technically, Enzo-E allows each field to have a # different number of ghost zones (but this feature isn't currently # used & it currently doesn't record this information) # -> our usage of a negative stop value ensures compatability with # both cell-centered and face-centered fields self._activezone_idx = tuple( slice(num_zones, -num_zones) if num_zones > 0 else slice(None) for num_zones in self.ds.ghost_zones[: self.ds.dimensionality] ) # Determine if particle masses are actually masses or densities. if self.ds.parameters["version"] is not None: # they're masses for enzo-e versions that record a version string mass_flag = True else: # in earlier versions: query the existence of the "mass_is_mass" # particle parameter mass_flag = nested_dict_get( self.ds.parameters, ("Particle", "mass_is_mass"), default=None ) # the historic approach for initializing the value of "mass_is_mass" # was unsound (and could yield a random value). Thus we should only # check for the parameter's existence and not its value self._particle_mass_is_mass = mass_flag is not None def _read_field_names(self, grid): if grid.filename is None: return [] f = h5py.File(grid.filename, mode="r") try: group = f[grid.block_name] except KeyError as e: raise YTException( message=f"Grid {grid.block_name} is missing from data file {grid.filename}.", ds=self.ds, ) from e fields = [] ptypes = set() dtypes = set() # keep one field for each particle type so we can count later sample_pfields = {} for name, v in group.items(): if not hasattr(v, "shape") or v.dtype == "O": continue # mesh fields are "field " if name.startswith("field"): _, fname = name.split(self._sep, 1) fields.append(("enzoe", fname)) dtypes.add(v.dtype) # particle fields are "particle " else: _, ftype, fname = name.split(self._sep, 2) fields.append((ftype, fname)) ptypes.add(ftype) dtypes.add(v.dtype) if ftype not in sample_pfields: sample_pfields[ftype] = fname self.sample_pfields = sample_pfields if len(dtypes) == 1: # Now, if everything we saw was the same dtype, we can go ahead and # set it here. We do this because it is a HUGE savings for 32 bit # floats, since our numpy copying/casting is way faster than # h5py's, for some reason I don't understand. This does *not* need # to be correct -- it will get fixed later -- it just needs to be # okay for now. self._field_dtype = list(dtypes)[0] f.close() return fields, ptypes def _read_particle_coords(self, chunks, ptf): yield from ( (ptype, xyz, 0.0) for ptype, xyz in self._read_particle_fields(chunks, ptf, None) ) def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) dc = self.ds.domain_center.in_units("code_length").d for chunk in chunks: # These should be organized by grid filename f = None for g in chunk.objs: if g.filename is None: continue if f is None: f = h5py.File(g.filename, mode="r") if g.particle_count is None: fnstr = "{}/{}".format( g.block_name, self._sep.join(["particle", "%s", "%s"]), ) g.particle_count = { ptype: f.get(fnstr % (ptype, self.sample_pfields[ptype])).size for ptype in self.sample_pfields } g.total_particles = sum(g.particle_count.values()) if g.total_particles == 0: continue group = f.get(g.block_name) for ptype, field_list in sorted(ptf.items()): pn = self._sep.join(["particle", ptype, "%s"]) if g.particle_count[ptype] == 0: continue coords = tuple( np.asarray(group.get(pn % ax)[()], dtype="=f8") for ax in "xyz"[: self.ds.dimensionality] ) for i in range(self.ds.dimensionality, 3): coords += ( dc[i] * np.ones(g.particle_count[ptype], dtype="f8"), ) if selector is None: # This only ever happens if the call is made from # _read_particle_coords. yield ptype, coords continue coords += (0.0,) mask = selector.select_points(*coords) if mask is None: continue for field in field_list: data = np.asarray(group.get(pn % field)[()], "=f8") if field == "mass" and not self._particle_mass_is_mass: data[mask] *= get_particle_mass_correction(self.ds) yield (ptype, field), data[mask] if f: f.close() def io_iter(self, chunks, fields): for chunk in chunks: fid = None filename = -1 for obj in chunk.objs: if obj.filename is None: continue if obj.filename != filename: # Note one really important thing here: even if we do # implement LRU caching in the _read_obj_field function, # we'll still be doing file opening and whatnot. This is a # problem, but one we can return to. if fid is not None: fid.close() fid = h5py.h5f.open( obj.filename.encode("latin-1"), h5py.h5f.ACC_RDONLY ) filename = obj.filename for field in fields: grid_dim = self.ds.grid_dimensions nodal_flag = self.ds.field_info[field].nodal_flag dims = ( grid_dim[: self.ds.dimensionality][::-1] + nodal_flag[: self.ds.dimensionality][::-1] ) data = np.empty(dims, dtype=self._field_dtype) yield field, obj, self._read_obj_field(obj, field, (fid, data)) if fid is not None: fid.close() def _read_obj_field(self, obj, field, fid_data): if fid_data is None: fid_data = (None, None) fid, rdata = fid_data if fid is None: close = True fid = h5py.h5f.open(obj.filename.encode("latin-1"), h5py.h5f.ACC_RDONLY) else: close = False ftype, fname = field node = f"/{obj.block_name}/field{self._sep}{fname}" dg = h5py.h5d.open(fid, node.encode("latin-1")) if rdata is None: rdata = np.empty( self.ds.grid_dimensions[: self.ds.dimensionality][::-1], dtype=self._field_dtype, ) dg.read(h5py.h5s.ALL, h5py.h5s.ALL, rdata) if close: fid.close() data = rdata[self._activezone_idx].T if self.ds.dimensionality < 3: nshape = data.shape + (1,) * (3 - self.ds.dimensionality) data = np.reshape(data, nshape) return data yt-project-yt-f043ac8/yt/frontends/enzo_e/misc.py000066400000000000000000000102711510711153200220750ustar00rootroot00000000000000import numpy as np from more_itertools import always_iterable def bdecode(block): """ Decode a block descriptor to get its left and right sides and level. A block string consisting of (0, 1), with optionally one colon. The number of digits after the colon is the refinement level. The combined digits denote the binary representation of the left edge. """ if ":" in block: level = len(block) - block.find(":") - 1 else: level = 0 bst = block.replace(":", "") d = float(2 ** len(bst)) left = int(bst, 2) right = left + 1 left /= d right /= d return level, left, right def get_block_string_and_dim(block, min_dim=3): mybs = block[1:].split("_") dim = max(len(mybs), min_dim) return mybs, dim def get_block_level(block): if ":" in block: l = block.find(":") else: l = len(block) return l def get_block_info(block, min_dim=3): """Decode a block name to get its left and right sides and level. Given a block name, this function returns the locations of the block's left and right edges (measured as binary fractions of the domain along each axis) and level. Unrefined blocks in the root array (which can each hold an of octree) have a refinement level of 0 while their ancestors (used internally by Enzo-E's solvers - they don't actually hold meaningful data) have negative levels. Because identification of negative refinement levels requires knowledge of the root array shape (the 'root_blocks' value specified in the parameter file), all unrefined blocks are assumed to have a level of 0. """ mybs, dim = get_block_string_and_dim(block, min_dim=min_dim) left = np.zeros(dim) right = np.ones(dim) level = 0 for i, myb in enumerate(mybs): if myb == "": continue level, left[i], right[i] = bdecode(myb) return level, left, right def get_root_blocks(block, min_dim=3): mybs, dim = get_block_string_and_dim(block, min_dim=min_dim) nb = np.ones(dim, dtype="int64") for i, myb in enumerate(mybs): if myb == "": continue s = get_block_level(myb) nb[i] = 2**s return nb def get_root_block_id(block, min_dim=3): mybs, dim = get_block_string_and_dim(block, min_dim=min_dim) rbid = np.zeros(dim, dtype="int64") for i, myb in enumerate(mybs): if myb == "": continue s = get_block_level(myb) if s == 0: continue rbid[i] = int(myb[:s], 2) return rbid def get_child_index(anc_id, desc_id): cid = "" for aind, dind in zip(anc_id.split("_"), desc_id.split("_"), strict=True): cid += dind[len(aind)] cid = int(cid, 2) return cid def is_parent(anc_block, desc_block): dim = anc_block.count("_") + 1 if (len(desc_block.replace(":", "")) - len(anc_block.replace(":", ""))) / dim != 1: return False for aind, dind in zip(anc_block.split("_"), desc_block.split("_"), strict=True): if not dind.startswith(aind): return False return True def nested_dict_get(pdict, keys, default=None): """ Retrieve a value from a nested dict using a tuple of keys. If a is a dict, and a['b'] = {'c': 'd'}, then nested_dict_get(a, ('b', 'c')) returns 'd'. """ val = pdict for key in always_iterable(keys): try: val = val[key] except KeyError: return default return val def get_listed_subparam(pdict, parent_param, subparam, default=None): """ Returns nested_dict_get(pdict, (parent_param,subparam), default) if subparam is an entry in nested_dict_get(pdict, (parent_param, 'list'), []) This is a common idiom in Enzo-E's parameter parsing """ if subparam in nested_dict_get(pdict, (parent_param, "list"), []): return nested_dict_get(pdict, (parent_param, subparam), default) return default def get_particle_mass_correction(ds): """ Normalize particle masses by the root grid cell volume. This correction is used for Enzo-E datasets where particle masses are stored as densities. """ return (ds.domain_width / ds.domain_dimensions).prod() / ds.length_unit**3 yt-project-yt-f043ac8/yt/frontends/enzo_e/tests/000077500000000000000000000000001510711153200217315ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo_e/tests/__init__.py000066400000000000000000000000001510711153200240300ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/enzo_e/tests/test_misc.py000066400000000000000000000115741510711153200243050ustar00rootroot00000000000000import numpy as np from yt.frontends.enzo_e.misc import ( get_block_info, get_root_block_id, get_root_blocks, is_parent, nested_dict_get, ) def get_random_block_string(max_n=64, random_state=None, level=None): if max_n == 1: assert level is None or level == 0 return 0, 0, "B" elif max_n < 1: raise ValueError("max_n must be a positive integer") if random_state is None: random_state = np.random.RandomState() max_l = int(np.log2(max_n)) form = f"%0{max_l}d" num10 = random_state.randint(0, high=max_n) num2 = form % int(bin(num10)[2:]) # the slice clips the '0b' prefix if level is None: level = random_state.randint(0, high=max_l) if level > 0: my_block = f"{num2[:-level]}:{num2[-level:]}" else: my_block = num2 my_block = "B" + my_block return num10, level, my_block def flip_random_block_bit(block, rs): """ Flips a bit string in one of the block descriptors in a given block name """ # split block into descriptors for each dimension descriptors = block[1:].split("_") # choose which descriptor to modify flippable = [i for i, descr in enumerate(descriptors) if len(descr) > 0] if len(flippable) == 0: # when block in ['B', 'B_', 'B__'] raise ValueError(f"{block} has no bits that can be flipped") descr_index = flippable[rs.randint(0, len(flippable))] # split block descriptor into left and right parts parts = descriptors[descr_index].split(":") # select the part to be modified if len(parts) == 1: # block is unrefined part_index = 0 elif len(parts[0]) == 0: # The root block index can't be modified part_index = 1 else: part_index = rs.randint(0, high=2) modify_part = parts[part_index] # flip a bit in modify_part, and return the new block name with this change flip_index = rs.randint(0, high=len(modify_part)) parts[part_index] = "%s%d%s" % ( modify_part[:flip_index], (int(modify_part[flip_index]) + 1) % 2, modify_part[flip_index + 1 :], ) descriptors[descr_index] = ":".join(parts) return "B" + "_".join(descriptors) def test_get_block_info(): rs = np.random.RandomState(45047) max_n = 64 for _ in range(10): n, l, b = get_random_block_string(max_n=max_n, random_state=rs) level, left, right = get_block_info(b, min_dim=1) assert level == l assert left == float(n) / max_n assert right == float(n + 1) / max_n for block in ["B", "B_", "B__"]: level, left, right = get_block_info(block) assert level == 0 assert (left == 0.0).all() assert (right == 1.0).all() def test_root_blocks(): rs = np.random.RandomState(45652) for i in range(6): max_n = 2**i n1, l1, b1 = get_random_block_string(max_n=max_n, random_state=rs, level=0) n2, l2, b2 = get_random_block_string(max_n=32, random_state=rs, level=0) block = f"{b1}:{b2[1:]}" nrb = get_root_blocks(block, min_dim=1) assert nrb == max_n rbid = get_root_block_id(block, min_dim=1) assert rbid == n1 def test_is_parent(): rs = np.random.RandomState(45652) for dim in [1, 2, 3]: for i in range(6): max_n = 2**i descriptors = [] for _ in range(dim): n1, l1, b1 = get_random_block_string( max_n=max_n, random_state=rs, level=0 ) n2, l2, b2 = get_random_block_string(max_n=32, random_state=rs, level=0) descriptors.append(f"{b1[1:]}:{b2[1:]}") block = "B" + "_".join(descriptors) # since b2 is computed with max_n=32 in the for-loop, block always # has a refined great-great-grandparent parent = "B" + "_".join(elem[:-1] for elem in descriptors) grandparent = "B" + "_".join(elem[:-2] for elem in descriptors) assert is_parent(parent, block) assert is_parent(grandparent, parent) assert not is_parent(grandparent, block) assert not is_parent(block, parent) assert not is_parent(flip_random_block_bit(parent, rs), block) def test_nested_dict_get(): rs = np.random.RandomState(47988) keys = [] my_dict = None for _ in range(5): k = str(rs.randint(0, high=1000000)) if my_dict is None: v = str(rs.randint(0, high=1000000)) keys.append(v) my_dict = {k: v} else: my_dict = {k: my_dict} keys.append(k) keys.reverse() assert nested_dict_get(my_dict, keys[:-1]) == keys[-1] my_def = "devron" assert nested_dict_get(my_dict, "system", default=my_def) == my_def def test_nested_dict_get_real_none(): my_dict = {"a": None} response = nested_dict_get(my_dict, "a", default="fail") assert response is None yt-project-yt-f043ac8/yt/frontends/enzo_e/tests/test_outputs.py000066400000000000000000000107661510711153200250770ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal, assert_equal from yt.frontends.enzo_e.api import EnzoEDataset from yt.frontends.enzo_e.fields import NODAL_FLAGS from yt.testing import requires_file, requires_module from yt.utilities.answer_testing.framework import ( FieldValuesTest, PixelizedProjectionValuesTest, create_obj, data_dir_load, requires_ds, ) from yt.utilities.on_demand_imports import _h5py as h5py _fields = ( ("gas", "density"), ("gas", "specific_total_energy"), ("gas", "velocity_x"), ("gas", "velocity_y"), ) _pfields = ( ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_velocity_z"), ) hello_world = "hello-0210/hello-0210.block_list" ep_cosmo = "ENZOP_DD0140/ENZOP_DD0140.block_list" orszag_tang = "ENZOE_orszag-tang_0.5/ENZOE_orszag-tang_0.5.block_list" @requires_module("h5py") @requires_file(hello_world) def test_EnzoEDataset(): assert isinstance(data_dir_load(hello_world), EnzoEDataset) @requires_module("h5py") @requires_ds(hello_world) def test_hello_world(): ds = data_dir_load(hello_world) dso = [None, ("sphere", ("max", (0.25, "unitary")))] for dobj_name in dso: for field in _fields: for axis in [0, 1, 2]: for weight_field in [None, ("gas", "density")]: yield PixelizedProjectionValuesTest( hello_world, axis, field, weight_field, dobj_name ) yield FieldValuesTest(hello_world, field, dobj_name) dobj = create_obj(ds, dobj_name) s1 = dobj["index", "ones"].sum() s2 = sum(mask.sum() for block, mask in dobj.blocks) assert_equal(s1, s2) @requires_module("h5py") @requires_ds(ep_cosmo) def test_particle_fields(): ds = data_dir_load(ep_cosmo) dso = [None, ("sphere", ("max", (0.1, "unitary")))] for dobj_name in dso: for field in _pfields: yield FieldValuesTest(ep_cosmo, field, dobj_name, particle_type=True) dobj = create_obj(ds, dobj_name) s1 = dobj["index", "ones"].sum() s2 = sum(mask.sum() for block, mask in dobj.blocks) assert_equal(s1, s2) @requires_module("h5py") @requires_file(hello_world) def test_hierarchy(): ds = data_dir_load(hello_world) fh = h5py.File(ds.index.grids[0].filename, mode="r") for grid in ds.index.grids: assert_array_equal( grid.LeftEdge.d, fh[grid.block_name].attrs["enzo_GridLeftEdge"] ) assert_array_equal(ds.index.grid_left_edge[grid.id], grid.LeftEdge) assert_array_equal(ds.index.grid_right_edge[grid.id], grid.RightEdge) for child in grid.Children: assert (child.LeftEdge >= grid.LeftEdge).all() assert (child.RightEdge <= grid.RightEdge).all() assert_equal(child.Parent.id, grid.id) fh.close() @requires_module("h5py") @requires_file(ep_cosmo) def test_critical_density(): ds = data_dir_load(ep_cosmo) c1 = ( (ds.r["dark", "particle_mass"].sum() + ds.r["gas", "cell_mass"].sum()) / ds.domain_width.prod() / ds.critical_density ) c2 = ( ds.omega_matter * (1 + ds.current_redshift) ** 3 / (ds.omega_matter * (1 + ds.current_redshift) ** 3 + ds.omega_lambda) ) assert np.abs(c1 - c2) / max(c1, c2) < 1e-3 @requires_module("h5py") @requires_file(orszag_tang) def test_face_centered_bfields(): # this is based on the enzo frontend test, test_face_centered_mhdct_fields ds = data_dir_load(orszag_tang) ad = ds.all_data() assert len(ds.index.grids) == 4 for field, flag in NODAL_FLAGS.items(): dims = ds.domain_dimensions assert_equal(ad[field].shape, (dims.prod(), 2 * sum(flag))) # the x and y domains are each split across 2 blocks. The z domain # is all located on a single block block_dims = (dims[0] // 2, dims[1] // 2, dims[2]) for grid in ds.index.grids: assert_equal(grid[field].shape, block_dims + (2 * sum(flag),)) # Average of face-centered fields should be the same as cell-centered field assert_array_equal( ad["enzoe", "bfieldi_x"].sum(axis=-1) / 2, ad["enzoe", "bfield_x"] ) assert_array_equal( ad["enzoe", "bfieldi_y"].sum(axis=-1) / 2, ad["enzoe", "bfield_y"] ) assert_array_equal( ad["enzoe", "bfieldi_z"].sum(axis=-1) / 2, ad["enzoe", "bfield_z"] ) yt-project-yt-f043ac8/yt/frontends/exodus_ii/000077500000000000000000000000001510711153200213005ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/exodus_ii/__init__.py000066400000000000000000000000001510711153200233770ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/exodus_ii/api.py000066400000000000000000000004031510711153200224200ustar00rootroot00000000000000from . import tests from .data_structures import ( ExodusIIDataset, ExodusIIUnstructuredIndex, ExodusIIUnstructuredMesh, ) from .fields import ExodusIIFieldInfo from .io import IOHandlerExodusII from .simulation_handling import ExodusIISimulation yt-project-yt-f043ac8/yt/frontends/exodus_ii/data_structures.py000066400000000000000000000351621510711153200250750ustar00rootroot00000000000000import numpy as np from yt.data_objects.index_subobjects.unstructured_mesh import UnstructuredMesh from yt.data_objects.static_output import Dataset from yt.data_objects.unions import MeshUnion from yt.funcs import setdefaultattr from yt.geometry.unstructured_mesh_handler import UnstructuredIndex from yt.utilities.file_handler import NetCDF4FileHandler, valid_netcdf_signature from yt.utilities.logger import ytLogger as mylog from .fields import ExodusIIFieldInfo from .util import get_num_pseudo_dims, load_info_records, sanitize_string class ExodusIIUnstructuredMesh(UnstructuredMesh): _index_offset = 1 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) class ExodusIIUnstructuredIndex(UnstructuredIndex): def __init__(self, ds, dataset_type="exodus_ii"): super().__init__(ds, dataset_type) def _initialize_mesh(self): coords = self.ds._read_coordinates() connectivity = self.ds._read_connectivity() self.meshes = [] for mesh_id, conn_ind in enumerate(connectivity): displaced_coords = self.ds._apply_displacement(coords, mesh_id) mesh = ExodusIIUnstructuredMesh( mesh_id, self.index_filename, conn_ind, displaced_coords, self ) self.meshes.append(mesh) self.mesh_union = MeshUnion("mesh_union", self.meshes) def _detect_output_fields(self): elem_names = self.dataset.parameters["elem_names"] node_names = self.dataset.parameters["nod_names"] fnames = elem_names + node_names self.field_list = [] for i in range(1, len(self.meshes) + 1): self.field_list += [("connect%d" % i, fname) for fname in fnames] self.field_list += [("all", fname) for fname in fnames] class ExodusIIDataset(Dataset): _load_requirements = ["netCDF4"] _index_class = ExodusIIUnstructuredIndex _field_info_class = ExodusIIFieldInfo def __init__( self, filename, step=0, displacements=None, dataset_type="exodus_ii", storage_filename=None, units_override=None, ): """ A class used to represent an on-disk ExodusII dataset. The initializer takes two extra optional parameters, "step" and "displacements." Parameters ---------- step : integer The step tells which time index to slice at. It throws an Error if the index is larger than the number of time outputs in the ExodusII file. Passing step=-1 picks out the last dataframe. Default is 0. displacements : dictionary of tuples This is a dictionary that controls whether or not displacement fields will be used with the meshes in this dataset. The keys of the displacements dictionary should the names of meshes in the file (e.g., "connect1", "connect2", etc... ), while the values should be tuples of the form (scale, offset), where "scale" is a floating point value and "offset" is an array-like with one component for each spatial dimension in the dataset. When the displacements for a given mesh are turned on, the coordinates of the vertices in that mesh get transformed as: vertex_x = vertex_x + disp_x*scale + offset_x vertex_y = vertex_y + disp_y*scale + offset_y vertex_z = vertex_z + disp_z*scale + offset_z If no displacement fields (assumed to be named 'disp_x', 'disp_y', etc... ) are detected in the output file, then this dictionary is ignored. Examples -------- This will load the Dataset at time index '0' with displacements turned off. >>> import yt >>> ds = yt.load("MOOSE_sample_data/mps_out.e") This will load the Dataset at the final index with displacements turned off. >>> import yt >>> ds = yt.load("MOOSE_sample_data/mps_out.e", step=-1) This will load the Dataset at index 10, turning on displacement fields for the 2nd mesh without applying any scale or offset: >>> import yt >>> ds = yt.load( ... "MOOSE_sample_data/mps_out.e", ... step=10, ... displacements={"connect2": (1.0, [0.0, 0.0, 0.0])}, ... ) This will load the Dataset at index 10, scaling the displacements in the 2nd mesh by a factor of 5 while not applying an offset: >>> import yt >>> ds = yt.load( ... "MOOSE_sample_data/mps_out.e", ... step=10, ... displacements={"connect2": (5.0, [0.0, 0.0, 0.0])}, ... ) This will load the Dataset at index 10, scaling the displacements for the 2nd mesh by a factor of 5.0 and shifting all the vertices in the first mesh by 1.0 unit in the z direction. >>> import yt >>> ds = yt.load( ... "MOOSE_sample_data/mps_out.e", ... step=10, ... displacements={ ... "connect1": (0.0, [0.0, 0.0, 1.0]), ... "connect2": (5.0, [0.0, 0.0, 0.0]), ... }, ... ) """ self.step = step if displacements is None: self.displacements = {} else: self.displacements = displacements self.storage_filename = storage_filename super().__init__(filename, dataset_type, units_override=units_override) self.fluid_types += self._get_fluid_types() self.default_field = [f for f in self.field_list if f[0] == "connect1"][-1] @property def index_filename(self): # historic alias return self.filename def _set_code_unit_attributes(self): # This is where quantities are created that represent the various # on-disk units. These are the currently available quantities which # should be set, along with examples of how to set them to standard # values. # setdefaultattr(self, "length_unit", self.quan(1.0, "cm")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) # # These can also be set: # self.velocity_unit = self.quan(1.0, "cm/s") # self.magnetic_unit = self.quan(1.0, "gauss") def _parse_parameter_file(self): self._handle = NetCDF4FileHandler(self.parameter_filename) with self._handle.open_ds() as ds: self._read_glo_var() self.dimensionality = ds.variables["coor_names"].shape[0] self.parameters["info_records"] = self._load_info_records() self.num_steps = len(ds.variables["time_whole"]) self.current_time = self._get_current_time() self.parameters["num_meshes"] = ds.variables["eb_status"].shape[0] self.parameters["elem_names"] = self._get_elem_names() self.parameters["nod_names"] = self._get_nod_names() self.domain_left_edge, self.domain_right_edge = self._load_domain_edge() self._periodicity = (False, False, False) # These attributes don't really make sense for unstructured # mesh data, but yt warns if they are not present, so we set # them to dummy values here. self.domain_dimensions = np.ones(3, "int32") self.cosmological_simulation = 0 self.current_redshift = 0 self.omega_lambda = 0 self.omega_matter = 0 self.hubble_constant = 0 self.refine_by = 0 def _get_fluid_types(self): with NetCDF4FileHandler(self.parameter_filename).open_ds() as ds: fluid_types = () i = 1 while True: ftype = "connect%d" % i if ftype in ds.variables: fluid_types += (ftype,) i += 1 else: break fluid_types += ("all",) return fluid_types def _read_glo_var(self): """ Adds each global variable to the dict of parameters """ names = self._get_glo_names() if not names: return with self._handle.open_ds() as ds: values = ds.variables["vals_glo_var"][:].transpose() for name, value in zip(names, values, strict=True): self.parameters[name] = value def _load_info_records(self): """ Returns parsed version of the info_records. """ with self._handle.open_ds() as ds: try: return load_info_records(ds.variables["info_records"]) except (KeyError, TypeError): mylog.warning("No info_records found") return [] def _get_current_time(self): with self._handle.open_ds() as ds: try: return ds.variables["time_whole"][self.step] except IndexError as e: raise RuntimeError( "Invalid step number, max is %d" % (self.num_steps - 1) ) from e except (KeyError, TypeError): return 0.0 def _get_glo_names(self): """ Returns the names of the global vars, if available. """ with self._handle.open_ds() as ds: if "name_glo_var" not in ds.variables: mylog.warning("name_glo_var not found") return [] else: return [ sanitize_string(v.tobytes()) for v in ds.variables["name_glo_var"] ] def _get_elem_names(self): """ Returns the names of the element vars, if available. """ with self._handle.open_ds() as ds: if "name_elem_var" not in ds.variables: mylog.warning("name_elem_var not found") return [] else: return [ sanitize_string(v.tobytes()) for v in ds.variables["name_elem_var"] ] def _get_nod_names(self): """ Returns the names of the node vars, if available """ with self._handle.open_ds() as ds: if "name_nod_var" not in ds.variables: mylog.warning("name_nod_var not found") return [] else: return [ sanitize_string(v.tobytes()) for v in ds.variables["name_nod_var"] ] def _read_coordinates(self): """ Loads the coordinates for the mesh """ coord_axes = "xyz"[: self.dimensionality] mylog.info("Loading coordinates") with self._handle.open_ds() as ds: if "coord" not in ds.variables: coords = ( np.array([ds.variables[f"coord{ax}"][:] for ax in coord_axes]) .transpose() .astype("f8") ) else: coords = ( np.array(list(ds.variables["coord"][:])).transpose().astype("f8") ) return coords def _apply_displacement(self, coords, mesh_id): mesh_name = "connect%d" % (mesh_id + 1) new_coords = coords.copy() if mesh_name not in self.displacements: return new_coords fac = self.displacements[mesh_name][0] offset = self.displacements[mesh_name][1] coord_axes = "xyz"[: self.dimensionality] with self._handle.open_ds() as ds: for i, ax in enumerate(coord_axes): if f"disp_{ax}" in self.parameters["nod_names"]: ind = self.parameters["nod_names"].index(f"disp_{ax}") disp = ds.variables["vals_nod_var%d" % (ind + 1)][self.step] new_coords[:, i] = coords[:, i] + fac * disp + offset[i] return new_coords def _read_connectivity(self): """ Loads the connectivity data for the mesh """ mylog.info("Loading connectivity") connectivity = [] with self._handle.open_ds() as ds: for i in range(self.parameters["num_meshes"]): var = ds.variables["connect%d" % (i + 1)][:].astype("i8") try: elem_type = var.elem_type.lower() if elem_type == "nfaced": raise NotImplementedError( "3D arbitrary polyhedra are not implemented yet" ) arbitrary_polyhedron = elem_type == "nsided" except AttributeError: arbitrary_polyhedron = False conn = var[:] if arbitrary_polyhedron: nodes_per_element = ds.variables[f"ebepecnt{i + 1}"] npe = nodes_per_element[0] if np.any(nodes_per_element != npe): raise NotImplementedError("only equal-size polyhedra supported") q, r = np.divmod(len(conn), npe) assert r == 0 conn = conn.reshape(q, npe) connectivity.append(conn) return connectivity def _load_domain_edge(self): """ Loads the boundaries for the domain edge """ coords = self._read_coordinates() connectivity = self._read_connectivity() mi = 1e300 ma = -1e300 for mesh_id, _ in enumerate(connectivity): displaced_coords = self._apply_displacement(coords, mesh_id) mi = np.minimum(displaced_coords.min(axis=0), mi) ma = np.maximum(displaced_coords.max(axis=0), ma) # pad domain boundaries width = ma - mi mi -= 0.1 * width ma += 0.1 * width # set up pseudo-3D for lodim datasets here for _ in range(self.dimensionality, 3): mi = np.append(mi, 0.0) ma = np.append(ma, 1.0) num_pseudo_dims = get_num_pseudo_dims(coords) self.dimensionality -= num_pseudo_dims for i in range(self.dimensionality, 3): mi[i] = 0.0 ma[i] = 1.0 return mi, ma @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not valid_netcdf_signature(filename): return False if cls._missing_load_requirements(): return False try: from netCDF4 import Dataset # We use keepweakref here to avoid holding onto the file handle # which can interfere with other is_valid calls. with Dataset(filename, keepweakref=True) as f: f.variables["connect1"] return True except Exception: return False yt-project-yt-f043ac8/yt/frontends/exodus_ii/definitions.py000066400000000000000000000001141510711153200241610ustar00rootroot00000000000000# This file is often empty. It can hold definitions related to a frontend. yt-project-yt-f043ac8/yt/frontends/exodus_ii/fields.py000066400000000000000000000021711510711153200231210ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer # We need to specify which fields we might have in our dataset. The field info # container subclass here will define which fields it knows about. There are # optionally methods on it that get called which can be subclassed. class ExodusIIFieldInfo(FieldInfoContainer): known_other_fields = ( # Each entry here is of the form # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ) known_particle_fields = ( # Identical form to above # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ) def __init__(self, ds, field_list): super().__init__(ds, field_list) for name in self: self[name].take_log = False # If you want, you can check self.field_list def setup_fluid_fields(self): # Here we do anything that might need info about the dataset. # You can use self.alias, self.add_output_field and self.add_field . pass def setup_particle_fields(self, ptype): # This will get called for every particle type. pass yt-project-yt-f043ac8/yt/frontends/exodus_ii/io.py000066400000000000000000000071021510711153200222610ustar00rootroot00000000000000import numpy as np from yt.utilities.file_handler import NetCDF4FileHandler from yt.utilities.io_handler import BaseIOHandler class IOHandlerExodusII(BaseIOHandler): _particle_reader = False _dataset_type = "exodus_ii" _INDEX_OFFSET = 1 def __init__(self, ds): self.filename = ds.index_filename exodus_ii_handler = NetCDF4FileHandler(self.filename) self.handler = exodus_ii_handler super().__init__(ds) self.node_fields = ds._get_nod_names() self.elem_fields = ds._get_elem_names() def _read_particle_coords(self, chunks, ptf): pass def _read_particle_fields(self, chunks, ptf, selector): pass def _read_fluid_selection(self, chunks, selector, fields, size): # This needs to allocate a set of arrays inside a dictionary, where the # keys are the (ftype, fname) tuples and the values are arrays that # have been masked using whatever selector method is appropriate. The # dict gets returned at the end and it should be flat, with selected # data. Note that if you're reading grid data, you might need to # special-case a grid selector object. with self.handler.open_ds() as ds: chunks = list(chunks) rv = {} for field in fields: ftype, fname = field if ftype == "all": ci = np.concatenate( [ mesh.connectivity_indices - self._INDEX_OFFSET for mesh in self.ds.index.mesh_union ] ) else: ci = ds.variables[ftype][:] - self._INDEX_OFFSET num_elem = ci.shape[0] if fname in self.node_fields: nodes_per_element = ci.shape[1] rv[field] = np.zeros((num_elem, nodes_per_element), dtype="float64") elif fname in self.elem_fields: rv[field] = np.zeros(num_elem, dtype="float64") for field in fields: ind = 0 ftype, fname = field if ftype == "all": mesh_ids = [mesh.mesh_id + 1 for mesh in self.ds.index.mesh_union] objs = list(self.ds.index.mesh_union) else: mesh_ids = [int(ftype.replace("connect", ""))] chunk = chunks[mesh_ids[0] - 1] objs = chunk.objs if fname in self.node_fields: field_ind = self.node_fields.index(fname) fdata = ds.variables["vals_nod_var%d" % (field_ind + 1)] for g in objs: ci = g.connectivity_indices - self._INDEX_OFFSET data = fdata[self.ds.step][ci] ind += g.select(selector, data, rv[field], ind) # caches if fname in self.elem_fields: field_ind = self.elem_fields.index(fname) for g, mesh_id in zip(objs, mesh_ids, strict=True): fdata = ds.variables[ "vals_elem_var%deb%s" % (field_ind + 1, mesh_id) ][:] data = fdata[self.ds.step, :] ind += g.select(selector, data, rv[field], ind) # caches rv[field] = rv[field][:ind] return rv def _read_chunk_data(self, chunk, fields): # This reads the data from a single chunk, and is only used for # caching. pass yt-project-yt-f043ac8/yt/frontends/exodus_ii/misc.py000066400000000000000000000000001510711153200225730ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/exodus_ii/simulation_handling.py000066400000000000000000000064431510711153200257110ustar00rootroot00000000000000import glob from yt.data_objects.time_series import DatasetSeries from yt.funcs import only_on_root from yt.loaders import load from yt.utilities.exceptions import YTUnidentifiedDataType from yt.utilities.logger import ytLogger as mylog from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_objects class ExodusIISimulation(DatasetSeries): r""" Initialize an ExodusII Simulation object. Upon creation, the input directory is searched for valid ExodusIIDatasets. The get_time_series can be used to generate a DatasetSeries object. simulation_directory : str The directory that contain the simulation data. Examples -------- >>> import yt >>> sim = yt.load_simulation("demo_second", "ExodusII") >>> sim.get_time_series() >>> for ds in sim: ... print(ds.current_time) """ def __init__(self, simulation_directory, find_outputs=False): self.simulation_directory = simulation_directory fn_pattern = f"{self.simulation_directory}/*" potential_outputs = glob.glob(fn_pattern) self.all_outputs = self._check_for_outputs(potential_outputs) self.all_outputs.sort(key=lambda obj: obj["filename"]) def __iter__(self): for o in self._pre_outputs: fn, step = o ds = load(fn, step=step) self._setup_function(ds) yield ds def __getitem__(self, key): if isinstance(key, slice): if isinstance(key.start, float): return self.get_range(key.start, key.stop) # This will return a sliced up object! return DatasetSeries(self._pre_outputs[key], self.parallel) o = self._pre_outputs[key] fn, step = o o = load(fn, step=step) self._setup_function(o) return o def get_time_series(self, parallel=False, setup_function=None): r""" Instantiate a DatasetSeries object for a set of outputs. If no additional keywords given, a DatasetSeries object will be created with all potential datasets created by the simulation. Fine-level filtering is currently not implemented. """ all_outputs = self.all_outputs ds_list = [] for output in all_outputs: num_steps = output["num_steps"] fn = output["filename"] for step in range(num_steps): ds_list.append((fn, step)) super().__init__(ds_list, parallel=parallel, setup_function=setup_function) def _check_for_outputs(self, potential_outputs): r""" Check a list of files to see if they are valid datasets. """ only_on_root( mylog.info, "Checking %d potential outputs.", len(potential_outputs) ) my_outputs = {} for my_storage, output in parallel_objects( potential_outputs, storage=my_outputs ): try: ds = load(output) except (FileNotFoundError, YTUnidentifiedDataType): mylog.error("Failed to load %s", output) continue my_storage.result = {"filename": output, "num_steps": ds.num_steps} my_outputs = [ my_output for my_output in my_outputs.values() if my_output is not None ] return my_outputs yt-project-yt-f043ac8/yt/frontends/exodus_ii/tests/000077500000000000000000000000001510711153200224425ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/exodus_ii/tests/__init__.py000066400000000000000000000000001510711153200245410ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/exodus_ii/tests/test_outputs.py000066400000000000000000000047541510711153200256100ustar00rootroot00000000000000from numpy.testing import assert_array_equal, assert_equal from yt.testing import requires_file from yt.utilities.answer_testing.framework import ( GenericArrayTest, data_dir_load, requires_ds, ) out = "ExodusII/out.e" @requires_file(out) def test_out(): ds = data_dir_load(out) field_list = [ ("all", "conv_indicator"), ("all", "conv_marker"), ("all", "convected"), ("all", "diffused"), ("connect1", "conv_indicator"), ("connect1", "conv_marker"), ("connect1", "convected"), ("connect1", "diffused"), ("connect2", "conv_indicator"), ("connect2", "conv_marker"), ("connect2", "convected"), ("connect2", "diffused"), ] assert_equal(str(ds), "out.e") assert_equal(ds.dimensionality, 3) assert_equal(ds.current_time, 0.0) assert_array_equal(ds.parameters["nod_names"], ["convected", "diffused"]) assert_equal(ds.parameters["num_meshes"], 2) assert_array_equal(ds.field_list, field_list) out_s002 = "ExodusII/out.e-s002" @requires_file(out_s002) def test_out002(): ds = data_dir_load(out_s002) field_list = [ ("all", "conv_indicator"), ("all", "conv_marker"), ("all", "convected"), ("all", "diffused"), ("connect1", "conv_indicator"), ("connect1", "conv_marker"), ("connect1", "convected"), ("connect1", "diffused"), ("connect2", "conv_indicator"), ("connect2", "conv_marker"), ("connect2", "convected"), ("connect2", "diffused"), ] assert_equal(str(ds), "out.e-s002") assert_equal(ds.dimensionality, 3) assert_equal(ds.current_time, 2.0) assert_array_equal(ds.field_list, field_list) gold = "ExodusII/gold.e" @requires_file(gold) def test_gold(): ds = data_dir_load(gold) field_list = [("all", "forced"), ("connect1", "forced")] assert_equal(str(ds), "gold.e") assert_array_equal(ds.field_list, field_list) big_data = "MOOSE_sample_data/mps_out.e" @requires_ds(big_data) def test_displacement_fields(): displacement_dicts = [ {"connect2": (5.0, [0.0, 0.0, 0.0])}, {"connect1": (1.0, [1.0, 2.0, 3.0]), "connect2": (0.0, [0.0, 0.0, 0.0])}, ] for disp in displacement_dicts: ds = data_dir_load(big_data, displacements=disp) for mesh in ds.index.meshes: def array_func(): return mesh.connectivity_coords # noqa: B023 yield GenericArrayTest(ds, array_func, 12) yt-project-yt-f043ac8/yt/frontends/exodus_ii/util.py000066400000000000000000000035151510711153200226330ustar00rootroot00000000000000import re import string from collections import OrderedDict from itertools import takewhile import numpy as np def get_num_pseudo_dims(coords): D = coords.shape[1] return sum(np.all(coords[:, dim] == 0.0) for dim in range(D)) def sanitize_string(s): _printable = {ord(_) for _ in string.printable} return "".join(chr(_) for _ in takewhile(lambda a: a in _printable, s)) def load_info_records(info_records): info_records_parsed = [sanitize_string(line_chars) for line_chars in info_records] return group_by_sections(info_records_parsed) def group_by_sections(info_records): # 1. Split by top groupings top_levels = get_top_levels(info_records) # 2. Determine if in section by index number grouped = OrderedDict() for tidx, top_level in enumerate(top_levels): grouped[top_level[1]] = [] try: next_idx = top_levels[tidx + 1][0] except IndexError: next_idx = len(info_records) - 1 for idx in range(top_level[0], next_idx): if idx == top_level[0]: continue grouped[top_level[1]].append(info_records[idx]) if "Version Info" in grouped.keys(): version_info = OrderedDict() for line in grouped["Version Info"]: split_line = line.split(":") key = split_line[0] val = ":".join(split_line[1:]).lstrip().rstrip() if key != "": version_info[key] = val grouped["Version Info"] = version_info return grouped def get_top_levels(info_records): top_levels = [] for idx, line in enumerate(info_records): pattern = re.compile(r"###[a-zA-Z\s]+") if pattern.match(line): clean_line = re.sub(r"[^\w\s]", "", line).lstrip().rstrip() top_levels.append([idx, clean_line]) return top_levels yt-project-yt-f043ac8/yt/frontends/fits/000077500000000000000000000000001510711153200202555ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/fits/__init__.py000066400000000000000000000000001510711153200223540ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/fits/api.py000066400000000000000000000004701510711153200214010ustar00rootroot00000000000000from . import tests from .data_structures import ( EventsFITSDataset, FITSDataset, FITSGrid, FITSHierarchy, SkyDataFITSDataset, SpectralCubeFITSDataset, SpectralCubeFITSHierarchy, ) from .fields import FITSFieldInfo from .io import IOHandlerFITS from .misc import setup_counts_fields yt-project-yt-f043ac8/yt/frontends/fits/data_structures.py000066400000000000000000001056351510711153200240550ustar00rootroot00000000000000import os import time import uuid import warnings import weakref from collections import defaultdict from functools import cached_property import numpy as np from more_itertools import always_iterable from yt.config import ytcfg from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.field_info_container import FieldInfoContainer from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.geometry_handler import YTDataChunk from yt.geometry.grid_geometry_handler import GridIndex from yt.units import dimensions from yt.units.unit_lookup_table import ( # type: ignore default_unit_symbol_lut, unit_prefixes, ) from yt.units.unit_object import UnitParseError # type: ignore from yt.units.yt_array import YTQuantity from yt.utilities.decompose import decompose_array, get_psize from yt.utilities.file_handler import FITSFileHandler from yt.utilities.io_handler import io_registry from yt.utilities.on_demand_imports import NotAModule, _astropy from .fields import FITSFieldInfo, WCSFITSFieldInfo, YTFITSFieldInfo lon_prefixes = ["X", "RA", "GLON", "LINEAR"] lat_prefixes = ["Y", "DEC", "GLAT", "LINEAR"] spec_names = {"V": "Velocity", "F": "Frequency", "E": "Energy", "W": "Wavelength"} space_prefixes = list(set(lon_prefixes + lat_prefixes)) unique_sky_prefixes = set(space_prefixes) unique_sky_prefixes.difference_update({"X", "Y", "LINEAR"}) sky_prefixes = list(unique_sky_prefixes) spec_prefixes = list(spec_names.keys()) class FITSGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = 0 class FITSHierarchy(GridIndex): grid = FITSGrid def __init__(self, ds, dataset_type="fits"): self.dataset_type = dataset_type self.field_indexes = {} self.dataset = weakref.proxy(ds) # for now, the index file is the dataset self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self._handle = ds._handle self.float_type = np.float64 GridIndex.__init__(self, ds, dataset_type) def _initialize_data_storage(self): pass def _guess_name_from_units(self, units): field_from_unit = {"Jy": "intensity", "K": "temperature"} for k, v in field_from_unit.items(): if k in units: mylog.warning( "Guessing this is a %s field based on its units of %s.", v, k ) return v return None def _determine_image_units(self, bunit): try: try: # First let AstroPy attempt to figure the unit out u = 1.0 * _astropy.units.Unit(bunit, format="fits") u = YTQuantity.from_astropy(u).units except ValueError: try: # Let yt try it by itself u = self.ds.quan(1.0, bunit).units except UnitParseError: return "dimensionless" return str(u) except KeyError: return "dimensionless" def _ensure_same_dims(self, hdu): ds = self.dataset conditions = [hdu.header["naxis"] != ds.primary_header["naxis"]] for i in range(ds.naxis): nax = "naxis%d" % (i + 1) conditions.append(hdu.header[nax] != ds.primary_header[nax]) if np.any(conditions): return False else: return True def _detect_output_fields(self): self.field_list = [] self._axis_map = {} self._file_map = {} self._ext_map = {} self._scale_map = {} dup_field_index = {} # Since FITS header keywords are case-insensitive, we only pick a subset of # prefixes, ones that we expect to end up in headers. known_units = {unit.lower(): unit for unit in self.ds.unit_registry.lut} for unit in list(known_units.values()): if unit in self.ds.unit_registry.prefixable_units: for p in ["n", "u", "m", "c", "k"]: known_units[(p + unit).lower()] = p + unit # We create a field from each slice on the 4th axis if self.dataset.naxis == 4: naxis4 = self.dataset.primary_header["naxis4"] else: naxis4 = 1 for i, fits_file in enumerate(self.dataset._handle._fits_files): for j, hdu in enumerate(fits_file): if ( isinstance(hdu, _astropy.pyfits.BinTableHDU) or hdu.header["naxis"] == 0 ): continue if self._ensure_same_dims(hdu): units = self._determine_image_units(hdu.header["bunit"]) try: # Grab field name from btype fname = hdu.header["btype"] except KeyError: # Try to guess the name from the units fname = self._guess_name_from_units(units) # When all else fails if fname is None: fname = "image_%d" % (j) if self.ds.num_files > 1 and fname.startswith("image"): fname += "_file_%d" % (i) if ("fits", fname) in self.field_list: if fname in dup_field_index: dup_field_index[fname] += 1 else: dup_field_index[fname] = 1 mylog.warning( "This field has the same name as a previously loaded " "field. Changing the name from %s to %s_%d. To avoid " "this, change one of the BTYPE header keywords.", fname, fname, dup_field_index[fname], ) fname += "_%d" % (dup_field_index[fname]) for k in range(naxis4): if naxis4 > 1: fname += "_%s_%d" % (hdu.header["CTYPE4"], k + 1) self._axis_map[fname] = k self._file_map[fname] = fits_file self._ext_map[fname] = j self._scale_map[fname] = [0.0, 1.0] if "bzero" in hdu.header: self._scale_map[fname][0] = hdu.header["bzero"] if "bscale" in hdu.header: self._scale_map[fname][1] = hdu.header["bscale"] self.field_list.append(("fits", fname)) self.dataset.field_units[fname] = units mylog.info("Adding field %s to the list of fields.", fname) if units == "dimensionless": mylog.warning( "Could not determine dimensions for field %s, " "setting to dimensionless.", fname, ) else: mylog.warning( "Image block %s does not have the same dimensions " "as the primary and will not be available as a field.", hdu.name.lower(), ) def _count_grids(self): self.num_grids = self.ds.parameters["nprocs"] def _parse_index(self): ds = self.dataset # If nprocs > 1, decompose the domain into virtual grids if self.num_grids > 1: self._domain_decomp() else: self.grid_left_edge[0, :] = ds.domain_left_edge self.grid_right_edge[0, :] = ds.domain_right_edge self.grid_dimensions[0] = ds.domain_dimensions self.grid_levels.flat[:] = 0 self.grids = np.empty(self.num_grids, dtype="object") for i in range(self.num_grids): self.grids[i] = self.grid(i, self, self.grid_levels[i, 0]) def _domain_decomp(self): bbox = np.array( [self.ds.domain_left_edge, self.ds.domain_right_edge] ).transpose() dims = self.ds.domain_dimensions psize = get_psize(dims, self.num_grids) gle, gre, shapes, slices, _ = decompose_array(dims, psize, bbox) self.grid_left_edge = self.ds.arr(gle, "code_length") self.grid_right_edge = self.ds.arr(gre, "code_length") self.grid_dimensions = np.array(shapes, dtype="int32") def _populate_grid_objects(self): for i in range(self.num_grids): self.grids[i]._prepare_grid() self.grids[i]._setup_dx() self.max_level = 0 def _setup_derived_fields(self): super()._setup_derived_fields() [self.dataset.conversion_factors[field] for field in self.field_list] for field in self.field_list: if field not in self.derived_field_list: self.derived_field_list.append(field) for field in self.derived_field_list: f = self.dataset.field_info[field] if f.is_alias: # Translating an already-converted field self.dataset.conversion_factors[field] = 1.0 def _setup_data_io(self): self.io = io_registry[self.dataset_type](self.dataset) def _chunk_io(self, dobj, cache=True, local_only=False): # local_only is only useful for inline datasets and requires # implementation by subclasses. gfiles = defaultdict(list) gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for g in gobjs: gfiles[g.id].append(g) for fn in sorted(gfiles): gs = gfiles[fn] yield YTDataChunk( dobj, "io", gs, self._count_selection(dobj, gs), cache=cache ) def find_primary_header(fileh): # Sometimes the primary hdu doesn't have an image if len(fileh) > 1 and fileh[0].header["naxis"] == 0: first_image = 1 else: first_image = 0 header = fileh[first_image].header return header, first_image def check_fits_valid(filename): ext = filename.rsplit(".", 1)[-1] if ext.upper() in ("GZ", "FZ"): # We don't know for sure that there will be > 1 ext = filename.rsplit(".", 1)[0].rsplit(".", 1)[-1] if ext.upper() not in ("FITS", "FTS"): return None elif isinstance(_astropy.pyfits, NotAModule): raise RuntimeError( "This appears to be a FITS file, but AstroPy is not installed." ) try: with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=UserWarning, append=True) fileh = _astropy.pyfits.open(filename) header, _ = find_primary_header(fileh) if header["naxis"] >= 2: return fileh else: fileh.close() except Exception: pass return None def check_sky_coords(filename, ndim): fileh = check_fits_valid(filename) if fileh is not None: try: if len(fileh) > 1 and fileh[1].name == "EVENTS" and ndim == 2: fileh.close() return True else: header, _ = find_primary_header(fileh) if header["naxis"] < ndim: return False axis_names = [ header.get("ctype%d" % (i + 1), "") for i in range(header["naxis"]) ] if len(axis_names) == 3 and axis_names.count("LINEAR") == 2: return any(a[0] in spec_prefixes for a in axis_names) x = find_axes(axis_names, sky_prefixes + spec_prefixes) fileh.close() return x >= ndim except Exception: pass return False class FITSDataset(Dataset): _load_requirements = ["astropy"] _index_class = FITSHierarchy _field_info_class: type[FieldInfoContainer] = FITSFieldInfo _dataset_type = "fits" _handle = None def __init__( self, filename, dataset_type="fits", auxiliary_files=None, nprocs=None, storage_filename=None, nan_mask=None, suppress_astropy_warnings=True, parameters=None, units_override=None, unit_system="cgs", ): if parameters is None: parameters = {} parameters["nprocs"] = nprocs self.specified_parameters = parameters if suppress_astropy_warnings: warnings.filterwarnings("ignore", module="astropy", append=True) self.filenames = [filename] + list(always_iterable(auxiliary_files)) self.num_files = len(self.filenames) self.fluid_types += ("fits",) if nan_mask is None: self.nan_mask = {} elif isinstance(nan_mask, float): self.nan_mask = {"all": nan_mask} elif isinstance(nan_mask, dict): self.nan_mask = nan_mask self._handle = FITSFileHandler(self.filenames[0]) if isinstance( self.filenames[0], _astropy.pyfits.hdu.image._ImageBaseHDU ) or isinstance(self.filenames[0], _astropy.pyfits.HDUList): fn = f"InMemoryFITSFile_{uuid.uuid4().hex}" else: fn = self.filenames[0] self._handle._fits_files.append(self._handle) if self.num_files > 1: for fits_file in auxiliary_files: if isinstance(fits_file, _astropy.pyfits.hdu.image._ImageBaseHDU): f = _astropy.pyfits.HDUList([fits_file]) elif isinstance(fits_file, _astropy.pyfits.HDUList): f = fits_file else: if os.path.exists(fits_file): fn = fits_file else: fn = os.path.join(ytcfg.get("yt", "test_data_dir"), fits_file) f = _astropy.pyfits.open( fn, memmap=True, do_not_scale_image_data=True, ignore_blank=True ) self._handle._fits_files.append(f) self.refine_by = 2 Dataset.__init__( self, fn, dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ if getattr(self, "length_unit", None) is None: default_length_units = [ u for u, v in default_unit_symbol_lut.items() if str(v[1]) == "(length)" ] more_length_units = [] for unit in default_length_units: if unit in self.unit_registry.prefixable_units: more_length_units += [prefix + unit for prefix in unit_prefixes] default_length_units += more_length_units file_units = [] cunits = [self.wcs.wcs.cunit[i] for i in range(self.dimensionality)] for unit in (_.to_string() for _ in cunits): if unit in default_length_units: file_units.append(unit) if len(set(file_units)) == 1: length_factor = self.wcs.wcs.cdelt[0] length_unit = str(file_units[0]) mylog.info("Found length units of %s.", length_unit) else: self.no_cgs_equiv_length = True mylog.warning("No length conversion provided. Assuming 1 = 1 cm.") length_factor = 1.0 length_unit = "cm" setdefaultattr(self, "length_unit", self.quan(length_factor, length_unit)) for unit, cgs in [("time", "s"), ("mass", "g")]: # We set these to cgs for now, but they may have been overridden if getattr(self, unit + "_unit", None) is not None: continue mylog.warning("Assuming 1.0 = 1.0 %s", cgs) setdefaultattr(self, f"{unit}_unit", self.quan(1.0, cgs)) self.magnetic_unit = np.sqrt( 4 * np.pi * self.mass_unit / (self.time_unit**2 * self.length_unit) ) self.magnetic_unit.convert_to_units("gauss") self.velocity_unit = self.length_unit / self.time_unit @property def filename(self) -> str: if self._input_filename.startswith("InMemory"): return self._input_filename else: return super().filename @cached_property def unique_identifier(self) -> str: if self.filename.startswith("InMemory"): return str(time.time()) else: return super().unique_identifier def _parse_parameter_file(self): self._determine_structure() self._determine_axes() # Determine dimensionality self.dimensionality = self.naxis self.geometry = Geometry.CARTESIAN # Sometimes a FITS file has a 4D datacube, in which case # we take the 4th axis and assume it consists of different fields. if self.dimensionality == 4: self.dimensionality = 3 self._determine_wcs() self.current_time = 0.0 self.domain_dimensions = np.array(self.dims)[: self.dimensionality] if self.dimensionality == 2: self.domain_dimensions = np.append(self.domain_dimensions, [1]) self._determine_bbox() # Get the simulation time try: self.current_time = self.parameters["time"] except Exception: mylog.warning("Cannot find time") self.current_time = 0.0 pass # For now we'll ignore these self._periodicity = (False,) * 3 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 self._determine_nprocs() # Now we can set up some of our parameters for convenience. for k, v in self.primary_header.items(): self.parameters[k] = v # Remove potential default keys self.parameters.pop("", None) def _determine_nprocs(self): # If nprocs is None, do some automatic decomposition of the domain if self.specified_parameters["nprocs"] is None: nprocs = np.around( np.prod(self.domain_dimensions) / 32**self.dimensionality ).astype("int64") self.parameters["nprocs"] = max(min(nprocs, 512), 1) else: self.parameters["nprocs"] = self.specified_parameters["nprocs"] def _determine_structure(self): self.primary_header, self.first_image = find_primary_header(self._handle) self.naxis = self.primary_header["naxis"] self.axis_names = [ self.primary_header.get("ctype%d" % (i + 1), "LINEAR") for i in range(self.naxis) ] self.dims = [ self.primary_header["naxis%d" % (i + 1)] for i in range(self.naxis) ] def _determine_wcs(self): wcs = _astropy.pywcs.WCS(header=self.primary_header) if self.naxis == 4: self.wcs = _astropy.pywcs.WCS(naxis=3) self.wcs.wcs.crpix = wcs.wcs.crpix[:3] self.wcs.wcs.cdelt = wcs.wcs.cdelt[:3] self.wcs.wcs.crval = wcs.wcs.crval[:3] self.wcs.wcs.cunit = [str(unit) for unit in wcs.wcs.cunit][:3] self.wcs.wcs.ctype = list(wcs.wcs.ctype)[:3] else: self.wcs = wcs def _determine_bbox(self): domain_left_edge = np.array([0.5] * 3) domain_right_edge = np.array( [float(dim) + 0.5 for dim in self.domain_dimensions] ) if self.dimensionality == 2: domain_left_edge[-1] = 0.5 domain_right_edge[-1] = 1.5 self.domain_left_edge = domain_left_edge self.domain_right_edge = domain_right_edge def _determine_axes(self): self.lat_axis = 1 self.lon_axis = 0 self.lat_name = "Y" self.lon_name = "X" @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: fileh = check_fits_valid(filename) except Exception: return False if fileh is None: return False else: fileh.close() return True @classmethod def _guess_candidates(cls, base, directories, files): candidates = [] for fn, fnl in ((_, _.lower()) for _ in files): if ( fnl.endswith(".fits") or fnl.endswith(".fits.gz") or fnl.endswith(".fits.fz") ): candidates.append(fn) # FITS files don't preclude subdirectories return candidates, True def close(self): self._handle.close() def find_axes(axis_names, prefixes): x = 0 for p in prefixes: y = np.char.startswith(axis_names, p) x += np.any(y) return x class YTFITSDataset(FITSDataset): _load_requirements = ["astropy"] _field_info_class = YTFITSFieldInfo def _parse_parameter_file(self): super()._parse_parameter_file() # Get the current time if "time" in self.primary_header: self.current_time = self.primary_header["time"] def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ for unit, cgs in [ ("length", "cm"), ("time", "s"), ("mass", "g"), ("velocity", "cm/s"), ("magnetic", "gauss"), ]: if unit == "magnetic": short_unit = "bfunit" else: short_unit = f"{unit[0]}unit" if short_unit in self.primary_header: # units should now be in header u = self.quan( self.primary_header[short_unit], self.primary_header.comments[short_unit].strip("[]"), ) mylog.info("Found %s units of %s.", unit, u) else: if unit == "length": # Falling back to old way of getting units for length # in old files u = self.quan(1.0, str(self.wcs.wcs.cunit[0])) mylog.info("Found %s units of %s.", unit, u) else: # Give up otherwise u = self.quan(1.0, cgs) mylog.warning( "No unit for %s found. Assuming 1.0 code_%s = 1.0 %s", unit, unit, cgs, ) setdefaultattr(self, f"{unit}_unit", u) def _determine_bbox(self): dx = np.zeros(3) dx[: self.dimensionality] = self.wcs.wcs.cdelt domain_left_edge = np.zeros(3) domain_left_edge[: self.dimensionality] = self.wcs.wcs.crval - dx[ : self.dimensionality ] * (self.wcs.wcs.crpix - 0.5) domain_right_edge = domain_left_edge + dx * self.domain_dimensions if self.dimensionality == 2: domain_left_edge[-1] = 0.0 domain_right_edge[-1] = dx[0] self.domain_left_edge = domain_left_edge self.domain_right_edge = domain_right_edge @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: fileh = check_fits_valid(filename) except Exception: return False if fileh is None: return False else: if "WCSNAME" in fileh[0].header: isyt = fileh[0].header["WCSNAME"].strip() == "yt" else: isyt = False fileh.close() return isyt class SkyDataFITSDataset(FITSDataset): _load_requirements = ["astropy"] _field_info_class = WCSFITSFieldInfo def _determine_wcs(self): super()._determine_wcs() end = min(self.dimensionality + 1, 4) self.ctypes = np.array( [self.primary_header["CTYPE%d" % (i)] for i in range(1, end)] ) self.wcs_2d = self.wcs def _parse_parameter_file(self): super()._parse_parameter_file() end = min(self.dimensionality + 1, 4) self.geometry = Geometry.SPECTRAL_CUBE log_str = "Detected these axes: " + "%s " * len(self.ctypes) mylog.info(log_str, *self.ctypes) self.lat_axis = np.zeros((end - 1), dtype="bool") for p in lat_prefixes: self.lat_axis += np.char.startswith(self.ctypes, p) self.lat_axis = np.where(self.lat_axis)[0][0] self.lat_name = self.ctypes[self.lat_axis].split("-")[0].lower() self.lon_axis = np.zeros((end - 1), dtype="bool") for p in lon_prefixes: self.lon_axis += np.char.startswith(self.ctypes, p) self.lon_axis = np.where(self.lon_axis)[0][0] self.lon_name = self.ctypes[self.lon_axis].split("-")[0].lower() if self.lat_axis == self.lon_axis and self.lat_name == self.lon_name: self.lat_axis = 1 self.lon_axis = 0 self.lat_name = "Y" self.lon_name = "X" self.spec_axis = 2 self.spec_name = "z" self.spec_unit = "" def _set_code_unit_attributes(self): super()._set_code_unit_attributes() units = self.wcs_2d.wcs.cunit[0] if units == "deg": units = "degree" if units == "rad": units = "radian" pixel_area = np.prod(np.abs(self.wcs_2d.wcs.cdelt)) pixel_area = self.quan(pixel_area, f"{units}**2").in_cgs() pixel_dims = pixel_area.units.dimensions self.unit_registry.add("pixel", float(pixel_area.value), dimensions=pixel_dims) if "beam_size" in self.specified_parameters: beam_size = self.specified_parameters["beam_size"] beam_size = self.quan(beam_size[0], beam_size[1]).in_cgs().value self.unit_registry.add("beam", beam_size, dimensions=dimensions.solid_angle) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: return check_sky_coords(filename, ndim=2) except Exception: return False class SpectralCubeFITSHierarchy(FITSHierarchy): def _domain_decomp(self): dz = self.ds.quan(1.0, "code_length") * self.ds.spectral_factor self.grid_dimensions[:, 2] = np.around( float(self.ds.domain_dimensions[2]) / self.num_grids ).astype("int64") self.grid_dimensions[-1, 2] += self.ds.domain_dimensions[2] % self.num_grids self.grid_left_edge[0, 2] = self.ds.domain_left_edge[2] self.grid_left_edge[1:, 2] = ( self.ds.domain_left_edge[2] + np.cumsum(self.grid_dimensions[:-1, 2]) * dz ) self.grid_right_edge[:, 2] = ( self.grid_left_edge[:, 2] + self.grid_dimensions[:, 2] * dz ) self.grid_left_edge[:, :2] = self.ds.domain_left_edge[:2] self.grid_right_edge[:, :2] = self.ds.domain_right_edge[:2] self.grid_dimensions[:, :2] = self.ds.domain_dimensions[:2] class SpectralCubeFITSDataset(SkyDataFITSDataset): _load_requirements = ["astropy"] _index_class = SpectralCubeFITSHierarchy def __init__( self, filename, auxiliary_files=None, nprocs=None, storage_filename=None, nan_mask=None, spectral_factor=1.0, suppress_astropy_warnings=True, parameters=None, units_override=None, unit_system="cgs", ): if auxiliary_files is None: auxiliary_files = [] self.spectral_factor = spectral_factor super().__init__( filename, nprocs=nprocs, auxiliary_files=auxiliary_files, storage_filename=storage_filename, suppress_astropy_warnings=suppress_astropy_warnings, nan_mask=nan_mask, parameters=parameters, units_override=units_override, unit_system=unit_system, ) def _parse_parameter_file(self): super()._parse_parameter_file() self.geometry = Geometry.SPECTRAL_CUBE end = min(self.dimensionality + 1, 4) self.spec_axis = np.zeros(end - 1, dtype="bool") for p in spec_names.keys(): self.spec_axis += np.char.startswith(self.ctypes, p) self.spec_axis = np.where(self.spec_axis)[0][0] self.spec_name = spec_names[self.ctypes[self.spec_axis].split("-")[0][0]] # Extract a subimage from a WCS object self.wcs_2d = self.wcs.sub(["longitude", "latitude"]) self._p0 = self.wcs.wcs.crpix[self.spec_axis] self._dz = self.wcs.wcs.cdelt[self.spec_axis] self._z0 = self.wcs.wcs.crval[self.spec_axis] self.spec_unit = str(self.wcs.wcs.cunit[self.spec_axis]) if self.spectral_factor == "auto": self.spectral_factor = float( max(self.domain_dimensions[[self.lon_axis, self.lat_axis]]) ) self.spectral_factor /= self.domain_dimensions[self.spec_axis] mylog.info("Setting the spectral factor to %f", self.spectral_factor) Dz = ( self.domain_right_edge[self.spec_axis] - self.domain_left_edge[self.spec_axis] ) dre = self.domain_right_edge.copy() dre[self.spec_axis] = ( self.domain_left_edge[self.spec_axis] + self.spectral_factor * Dz ) self.domain_right_edge = dre self._dz /= self.spectral_factor self._p0 = (self._p0 - 0.5) * self.spectral_factor + 0.5 def _determine_nprocs(self): # If nprocs is None, do some automatic decomposition of the domain if self.specified_parameters["nprocs"] is None: nprocs = np.around(self.domain_dimensions[2] / 8).astype("int64") self.parameters["nprocs"] = max(min(nprocs, 512), 1) else: self.parameters["nprocs"] = self.specified_parameters["nprocs"] def spec2pixel(self, spec_value): sv = self.arr(spec_value).in_units(self.spec_unit) return self.arr((sv.v - self._z0) / self._dz + self._p0, "code_length") def pixel2spec(self, pixel_value): pv = self.arr(pixel_value, "code_length") return self.arr((pv.v - self._p0) * self._dz + self._z0, self.spec_unit) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: return check_sky_coords(filename, ndim=3) except Exception: return False class EventsFITSHierarchy(FITSHierarchy): def _detect_output_fields(self): ds = self.dataset self.field_list = [] for k, v in ds.events_info.items(): fname = "event_" + k mylog.info("Adding field %s to the list of fields.", fname) self.field_list.append(("io", fname)) if k in ["x", "y"]: field_unit = "code_length" else: field_unit = v self.dataset.field_units["io", fname] = field_unit return def _parse_index(self): super()._parse_index() try: self.grid_particle_count[:] = self.dataset.primary_header["naxis2"] except KeyError: self.grid_particle_count[:] = 0.0 self._particle_indices = np.zeros(self.num_grids + 1, dtype="int64") self._particle_indices[1] = self.grid_particle_count.squeeze() class EventsFITSDataset(SkyDataFITSDataset): _load_requirements = ["astropy"] _index_class = EventsFITSHierarchy def __init__( self, filename, storage_filename=None, suppress_astropy_warnings=True, reblock=1, parameters=None, units_override=None, unit_system="cgs", ): self.reblock = reblock super().__init__( filename, nprocs=1, storage_filename=storage_filename, parameters=parameters, suppress_astropy_warnings=suppress_astropy_warnings, units_override=units_override, unit_system=unit_system, ) def _determine_structure(self): self.first_image = 1 self.primary_header = self._handle[self.first_image].header self.naxis = 2 def _determine_wcs(self): self.wcs = _astropy.pywcs.WCS(naxis=2) self.events_info = {} for k, v in self.primary_header.items(): if k.startswith("TTYP"): if v.lower() in ["x", "y"]: num = k.replace("TTYPE", "") self.events_info[v.lower()] = ( self.primary_header["TLMIN" + num], self.primary_header["TLMAX" + num], self.primary_header["TCTYP" + num], self.primary_header["TCRVL" + num], self.primary_header["TCDLT" + num], self.primary_header["TCRPX" + num], ) elif v.lower() in ["energy", "time"]: num = k.replace("TTYPE", "") unit = self.primary_header["TUNIT" + num].lower() if unit.endswith("ev"): unit = unit.replace("ev", "eV") self.events_info[v.lower()] = unit self.axis_names = [self.events_info[ax][2] for ax in ["x", "y"]] self.wcs.wcs.cdelt = [ self.events_info["x"][4] * self.reblock, self.events_info["y"][4] * self.reblock, ] self.wcs.wcs.crpix = [ (self.events_info["x"][5] - 0.5) / self.reblock + 0.5, (self.events_info["y"][5] - 0.5) / self.reblock + 0.5, ] self.wcs.wcs.ctype = [self.events_info["x"][2], self.events_info["y"][2]] self.wcs.wcs.cunit = ["deg", "deg"] self.wcs.wcs.crval = [self.events_info["x"][3], self.events_info["y"][3]] self.dims = [ (self.events_info["x"][1] - self.events_info["x"][0]) / self.reblock, (self.events_info["y"][1] - self.events_info["y"][0]) / self.reblock, ] self.ctypes = self.axis_names self.wcs_2d = self.wcs @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: fileh = check_fits_valid(filename) except Exception: return False if fileh is not None: try: valid = fileh[1].name == "EVENTS" fileh.close() return valid except Exception: pass return False yt-project-yt-f043ac8/yt/frontends/fits/definitions.py000066400000000000000000000000001510711153200231300ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/fits/fields.py000066400000000000000000000102751510711153200221020ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer class FITSFieldInfo(FieldInfoContainer): known_other_fields = () def __init__(self, ds, field_list, slice_info=None): super().__init__(ds, field_list, slice_info=slice_info) for field in ds.field_list: if field[0] == "fits": self[field].take_log = False class YTFITSFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", ("code_mass/code_length**3", ["density"], None)), ( "dark_matter_density", ("code_mass/code_length**3", ["dark_matter_density"], None), ), ("number_density", ("1/code_length**3", ["number_density"], None)), ("pressure", ("dyne/code_length**2", ["pressure"], None)), ("thermal_energy", ("erg / g", ["specific_thermal_energy"], None)), ("temperature", ("K", ["temperature"], None)), ("velocity_x", ("code_length/code_time", ["velocity_x"], None)), ("velocity_y", ("code_length/code_time", ["velocity_y"], None)), ("velocity_z", ("code_length/code_time", ["velocity_z"], None)), ("magnetic_field_x", ("gauss", [], None)), ("magnetic_field_y", ("gauss", [], None)), ("magnetic_field_z", ("gauss", [], None)), ("metallicity", ("Zsun", ["metallicity"], None)), # We need to have a bunch of species fields here, too ("metal_density", ("code_mass/code_length**3", ["metal_density"], None)), ("hi_density", ("code_mass/code_length**3", ["hi_density"], None)), ("hii_density", ("code_mass/code_length**3", ["hii_density"], None)), ("h2i_density", ("code_mass/code_length**3", ["h2i_density"], None)), ("h2ii_density", ("code_mass/code_length**3", ["h2ii_density"], None)), ("h2m_density", ("code_mass/code_length**3", ["h2m_density"], None)), ("hei_density", ("code_mass/code_length**3", ["hei_density"], None)), ("heii_density", ("code_mass/code_length**3", ["heii_density"], None)), ("heiii_density", ("code_mass/code_length**3", ["heiii_density"], None)), ("hdi_density", ("code_mass/code_length**3", ["hdi_density"], None)), ("di_density", ("code_mass/code_length**3", ["di_density"], None)), ("dii_density", ("code_mass/code_length**3", ["dii_density"], None)), ) def __init__(self, ds, field_list, slice_info=None): super().__init__(ds, field_list, slice_info=slice_info) class WCSFITSFieldInfo(FITSFieldInfo): def setup_fluid_fields(self): wcs_2d = getattr(self.ds, "wcs_2d", self.ds.wcs) def _pixel(field, data): return data.ds.arr(data["index", "ones"], "pixel") self.add_field( ("fits", "pixel"), sampling_type="cell", function=_pixel, units="pixel" ) def _get_2d_wcs(data, axis): w_coords = wcs_2d.wcs_pix2world(data["index", "x"], data["index", "y"], 1) return w_coords[axis] def world_f(axis, unit): def _world_f(field, data): return data.ds.arr(_get_2d_wcs(data, axis), unit) return _world_f for i, axis, name in [ (0, self.ds.lon_axis, self.ds.lon_name), (1, self.ds.lat_axis, self.ds.lat_name), ]: unit = str(wcs_2d.wcs.cunit[i]) if unit.lower() == "deg": unit = "degree" if unit.lower() == "rad": unit = "radian" self.add_field( ("fits", name), sampling_type="cell", function=world_f(axis, unit), units=unit, ) if self.ds.dimensionality == 3: def _spec(field, data): axis = "xyz"[data.ds.spec_axis] sp = ( data["fits", axis].ndarray_view() - self.ds._p0 ) * self.ds._dz + self.ds._z0 return data.ds.arr(sp, data.ds.spec_unit) self.add_field( ("fits", "spectral"), sampling_type="cell", function=_spec, units=self.ds.spec_unit, display_name=self.ds.spec_name, ) yt-project-yt-f043ac8/yt/frontends/fits/io.py000066400000000000000000000073231510711153200212430ustar00rootroot00000000000000import numpy as np from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog class IOHandlerFITS(BaseIOHandler): _particle_reader = False _dataset_type = "fits" def __init__(self, ds): super().__init__(ds) self.ds = ds self._handle = ds._handle def _read_particles( self, fields_to_read, type, args, grid_list, count_list, conv_factors ): pass def _read_particle_coords(self, chunks, ptf): pdata = self.ds._handle[self.ds.first_image].data assert len(ptf) == 1 ptype = list(ptf.keys())[0] x = np.asarray(pdata.field("X"), dtype="=f8") y = np.asarray(pdata.field("Y"), dtype="=f8") z = np.ones(x.shape) x = (x - 0.5) / self.ds.reblock + 0.5 y = (y - 0.5) / self.ds.reblock + 0.5 yield ptype, (x, y, z), 0.0 def _read_particle_fields(self, chunks, ptf, selector): pdata = self.ds._handle[self.ds.first_image].data assert len(ptf) == 1 ptype = list(ptf.keys())[0] field_list = ptf[ptype] x = np.asarray(pdata.field("X"), dtype="=f8") y = np.asarray(pdata.field("Y"), dtype="=f8") z = np.ones(x.shape) x = (x - 0.5) / self.ds.reblock + 0.5 y = (y - 0.5) / self.ds.reblock + 0.5 mask = selector.select_points(x, y, z, 0.0) if mask is None: return for field in field_list: fd = field.split("_")[-1] data = pdata.field(fd.upper()) if fd in ["x", "y"]: data = (data.copy() - 0.5) / self.ds.reblock + 0.5 yield (ptype, field), data[mask] def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) if any((ftype != "fits" for ftype, fname in fields)): raise NotImplementedError rv = {} dt = "float64" for field in fields: rv[field] = np.empty(size, dtype=dt) ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) dx = self.ds.domain_width / self.ds.domain_dimensions for field in fields: ftype, fname = field f = self.ds.index._file_map[fname] ds = f[self.ds.index._ext_map[fname]] bzero, bscale = self.ds.index._scale_map[fname] ind = 0 for chunk in chunks: for g in chunk.objs: start = ((g.LeftEdge - self.ds.domain_left_edge) / dx).d.astype( "int" ) end = start + g.ActiveDimensions slices = [slice(start[i], end[i]) for i in range(3)] if self.ds.dimensionality == 2: nx, ny = g.ActiveDimensions[:2] nz = 1 data = np.zeros((nx, ny, nz)) data[:, :, 0] = ds.data[slices[1], slices[0]].T elif self.ds.naxis == 4: idx = self.ds.index._axis_map[fname] data = ds.data[idx, slices[2], slices[1], slices[0]].T else: data = ds.data[slices[2], slices[1], slices[0]].T if fname in self.ds.nan_mask: data[np.isnan(data)] = self.ds.nan_mask[fname] elif "all" in self.ds.nan_mask: data[np.isnan(data)] = self.ds.nan_mask["all"] data = bzero + bscale * data ind += g.select(selector, data.astype("float64"), rv[field], ind) return rv yt-project-yt-f043ac8/yt/frontends/fits/misc.py000066400000000000000000000240431510711153200215650ustar00rootroot00000000000000import base64 import os from io import BytesIO import numpy as np from yt.fields.derived_field import ValidateSpatial from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _astropy def _make_counts(emin, emax): def _counts(field, data): e = data["all", "event_energy"].in_units("keV") mask = np.logical_and(e >= emin, e < emax) x = data["all", "event_x"][mask] y = data["all", "event_y"][mask] z = np.ones(x.shape) pos = np.array([x, y, z]).transpose() img = data.deposit(pos, method="count") if data.has_field_parameter("sigma"): sigma = data.get_field_parameter("sigma") else: sigma = None if sigma is not None and sigma > 0.0: kern = _astropy.conv.Gaussian2DKernel(x_stddev=sigma) img[:, :, 0] = _astropy.conv.convolve(img[:, :, 0], kern) return data.ds.arr(img, "counts/pixel") return _counts def setup_counts_fields(ds, ebounds, ftype="gas"): r""" Create deposited image fields from X-ray count data in energy bands. Parameters ---------- ds : ~yt.data_objects.static_output.Dataset The FITS events file dataset to add the counts fields to. ebounds : list of tuples A list of tuples, one for each field, with (emin, emax) as the energy bounds for the image. ftype : string, optional The field type of the resulting field. Defaults to "gas". Examples -------- >>> ds = yt.load("evt.fits") >>> ebounds = [(0.1, 2.0), (2.0, 3.0)] >>> setup_counts_fields(ds, ebounds) """ for emin, emax in ebounds: cfunc = _make_counts(emin, emax) fname = f"counts_{emin}-{emax}" mylog.info("Creating counts field %s.", fname) ds.add_field( (ftype, fname), sampling_type="cell", function=cfunc, units="counts/pixel", validators=[ValidateSpatial()], display_name=f"Counts ({emin}-{emax} keV)", ) def create_spectral_slabs(filename, slab_centers, slab_width, **kwargs): r""" Given a dictionary of spectral slab centers and a width in spectral units, extract data from a spectral cube at these slab centers and return a `FITSDataset` instance containing the different slabs as separate yt fields. Useful for extracting individual lines from a spectral cube and separating them out as different fields. Requires the SpectralCube (https://spectral-cube.readthedocs.io/en/latest/) library. All keyword arguments will be passed on to the `FITSDataset` constructor. Parameters ---------- filename : string The spectral cube FITS file to extract the data from. slab_centers : dict of (float, string) tuples or YTQuantities The centers of the slabs, where the keys are the names of the new fields and the values are (float, string) tuples or YTQuantities, specifying a value for each center and its unit. slab_width : YTQuantity or (float, string) tuple The width of the slab along the spectral axis. Examples -------- >>> slab_centers = { ... "13CN": (218.03117, "GHz"), ... "CH3CH2CHO": (218.284256, "GHz"), ... "CH3NH2": (218.40956, "GHz"), ... } >>> slab_width = (0.05, "GHz") >>> ds = create_spectral_slabs( ... "intensity_cube.fits", slab_centers, slab_width, nan_mask=0.0 ... ) """ from spectral_cube import SpectralCube from yt.frontends.fits.api import FITSDataset from yt.visualization.fits_image import FITSImageData cube = SpectralCube.read(filename) if not isinstance(slab_width, YTQuantity): slab_width = YTQuantity(slab_width[0], slab_width[1]) slab_data = {} field_units = cube.header.get("bunit", "dimensionless") for k, v in slab_centers.items(): if not isinstance(v, YTQuantity): slab_center = YTQuantity(v[0], v[1]) else: slab_center = v mylog.info("Adding slab field %s at %g %s", k, slab_center.v, slab_center.units) slab_lo = (slab_center - 0.5 * slab_width).to_astropy() slab_hi = (slab_center + 0.5 * slab_width).to_astropy() subcube = cube.spectral_slab(slab_lo, slab_hi) slab_data[k] = YTArray(subcube.filled_data[:, :, :], field_units) width = subcube.header["naxis3"] * cube.header["cdelt3"] w = subcube.wcs.copy() w.wcs.crpix[-1] = 0.5 w.wcs.crval[-1] = -0.5 * width fid = FITSImageData(slab_data, wcs=w) for hdu in fid: hdu.header.pop("RESTFREQ", None) hdu.header.pop("RESTFRQ", None) ds = FITSDataset(fid, **kwargs) return ds def ds9_region(ds, reg, obj=None, field_parameters=None): r""" Create a data container from a ds9 region file. Requires the regions package (https://astropy-regions.readthedocs.io/) to be installed. Parameters ---------- ds : FITSDataset The Dataset to create the region from. reg : string The filename of the ds9 region, or a region string to be parsed. obj : data container, optional The data container that will be used to create the new region. Defaults to ds.all_data. field_parameters : dictionary, optional A set of field parameters to apply to the region. Examples -------- >>> ds = yt.load("m33_hi.fits") >>> circle_region = ds9_region(ds, "circle.reg") >>> print(circle_region.quantities.extrema("flux")) """ from yt.utilities.on_demand_imports import _astropy, _regions Regions = _regions.Regions WCS = _astropy.WCS from yt.frontends.fits.api import EventsFITSDataset if os.path.exists(reg): method = Regions.read else: method = Regions.parse r = method(reg, format="ds9").regions[0] reg_name = reg header = ds.wcs_2d.to_header() # The FITS header only contains WCS-related keywords header["NAXIS1"] = ds.domain_dimensions[ds.lon_axis] header["NAXIS2"] = ds.domain_dimensions[ds.lat_axis] pixreg = r.to_pixel(WCS(header)) mask = pixreg.to_mask().to_image((header["NAXIS1"], header["NAXIS2"])).astype(bool) if isinstance(ds, EventsFITSDataset): prefix = "event_" else: prefix = "" def _reg_field(field, data): i = data[prefix + "xyz"[ds.lon_axis]].d.astype("int64") - 1 j = data[prefix + "xyz"[ds.lat_axis]].d.astype("int64") - 1 new_mask = mask[i, j] ret = np.zeros(data[prefix + "x"].shape) ret[new_mask] = 1.0 return ret ds.add_field(("gas", reg_name), sampling_type="cell", function=_reg_field) if obj is None: obj = ds.all_data() if field_parameters is not None: for k, v in field_parameters.items(): obj.set_field_parameter(k, v) return obj.cut_region([f"obj['{reg_name}'] > 0"]) class PlotWindowWCS: r""" Use AstroPy's WCSAxes class to plot celestial coordinates on the axes of a on-axis PlotWindow plot. See http://docs.astropy.org/en/stable/visualization/wcsaxes/ for more details on how it works under the hood. This functionality requires a version of AstroPy >= 1.3. Parameters ---------- pw : on-axis PlotWindow instance The PlotWindow instance to add celestial axes to. """ def __init__(self, pw): WCSAxes = _astropy.wcsaxes.WCSAxes if pw.oblique: raise NotImplementedError("WCS axes are not implemented for oblique plots.") if not hasattr(pw.ds, "wcs_2d"): raise NotImplementedError("WCS axes are not implemented for this dataset.") if pw.data_source.axis != pw.ds.spec_axis: raise NotImplementedError("WCS axes are not implemented for this axis.") self.plots = {} self.pw = pw for f in pw.plots: rect = pw.plots[f]._get_best_layout()[1] fig = pw.plots[f].figure ax = fig.axes[0] wcs_ax = WCSAxes(fig, rect, wcs=pw.ds.wcs_2d, frameon=False) fig.add_axes(wcs_ax) wcs = pw.ds.wcs_2d.wcs xax = pw.ds.coordinates.x_axis[pw.data_source.axis] yax = pw.ds.coordinates.y_axis[pw.data_source.axis] xlabel = f"{wcs.ctype[xax].split('-')[0]} ({wcs.cunit[xax]})" ylabel = f"{wcs.ctype[yax].split('-')[0]} ({wcs.cunit[yax]})" fp = pw._font_properties wcs_ax.coords[0].set_axislabel(xlabel, fontproperties=fp, minpad=0.5) wcs_ax.coords[1].set_axislabel(ylabel, fontproperties=fp, minpad=0.4) wcs_ax.coords[0].ticklabels.set_fontproperties(fp) wcs_ax.coords[1].ticklabels.set_fontproperties(fp) ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) wcs_ax.set_xlim(pw.xlim[0].value, pw.xlim[1].value) wcs_ax.set_ylim(pw.ylim[0].value, pw.ylim[1].value) wcs_ax.coords.frame._update_cache = [] ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) self.plots[f] = fig def keys(self): return self.plots.keys() def values(self): return self.plots.values() def items(self): return self.plots.items() def __getitem__(self, key): for k in self.keys(): if k[1] == key: return self.plots[k] def show(self): return self def save(self, name=None, mpl_kwargs=None): if mpl_kwargs is None: mpl_kwargs = {} mpl_kwargs["bbox_inches"] = "tight" self.pw.save(name=name, mpl_kwargs=mpl_kwargs) def _repr_html_(self): from matplotlib.backends.backend_agg import FigureCanvasAgg ret = "" for v in self.plots.values(): canvas = FigureCanvasAgg(v) f = BytesIO() canvas.print_figure(f) f.seek(0) img = base64.b64encode(f.read()).decode() ret += ( r'
' ) return ret yt-project-yt-f043ac8/yt/frontends/fits/tests/000077500000000000000000000000001510711153200214175ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/fits/tests/__init__.py000066400000000000000000000000001510711153200235160ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/fits/tests/test_outputs.py000066400000000000000000000053461510711153200245630ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.testing import requires_file, requires_module, units_override_check from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) from ..data_structures import ( EventsFITSDataset, FITSDataset, SkyDataFITSDataset, SpectralCubeFITSDataset, ) _fields_grs = (("fits", "temperature"),) grs = "radio_fits/grs-50-cube.fits" @requires_ds(grs) def test_grs(): ds = data_dir_load(grs, cls=SpectralCubeFITSDataset, kwargs={"nan_mask": 0.0}) assert_equal(str(ds), "grs-50-cube.fits") for test in small_patch_amr(ds, _fields_grs, input_center="c", input_weight="ones"): test_grs.__name__ = test.description yield test _fields_vels = (("fits", "velocity_x"), ("fits", "velocity_y"), ("fits", "velocity_z")) vf = "UnigridData/velocity_field_20.fits" @requires_module("astropy") @requires_ds(vf) def test_velocity_field(): ds = data_dir_load(vf, cls=FITSDataset) assert_equal(str(ds), "velocity_field_20.fits") for test in small_patch_amr( ds, _fields_vels, input_center="c", input_weight="ones" ): test_velocity_field.__name__ = test.description yield test acis = "xray_fits/acisf05356N003_evt2.fits.gz" _fields_acis = (("gas", "counts_0.1-2.0"), ("gas", "counts_2.0-5.0")) @requires_ds(acis) def test_acis(): from yt.frontends.fits.misc import setup_counts_fields ds = data_dir_load(acis, cls=EventsFITSDataset) ebounds = [(0.1, 2.0), (2.0, 5.0)] setup_counts_fields(ds, ebounds) assert_equal(str(ds), "acisf05356N003_evt2.fits.gz") for test in small_patch_amr( ds, _fields_acis, input_center="c", input_weight="ones" ): test_acis.__name__ = test.description yield test A2052 = "xray_fits/A2052_merged_0.3-2_match-core_tmap_bgecorr.fits" _fields_A2052 = (("fits", "flux"),) @requires_ds(A2052) def test_A2052(): ds = data_dir_load(A2052, cls=SkyDataFITSDataset) assert_equal(str(ds), "A2052_merged_0.3-2_match-core_tmap_bgecorr.fits") for test in small_patch_amr( ds, _fields_A2052, input_center="c", input_weight="ones" ): test_A2052.__name__ = test.description yield test @requires_file(vf) def test_units_override(): units_override_check(vf) @requires_file(vf) def test_FITSDataset(): assert isinstance(data_dir_load(vf), FITSDataset) @requires_file(grs) def test_SpectralCubeFITSDataset(): assert isinstance(data_dir_load(grs), SpectralCubeFITSDataset) @requires_file(acis) def test_EventsFITSDataset(): assert isinstance(data_dir_load(acis), EventsFITSDataset) @requires_file(A2052) def test_SkyDataFITSDataset(): assert isinstance(data_dir_load(A2052), SkyDataFITSDataset) yt-project-yt-f043ac8/yt/frontends/flash/000077500000000000000000000000001510711153200204055ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/flash/__init__.py000066400000000000000000000000001510711153200225040ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/flash/api.py000066400000000000000000000003361510711153200215320ustar00rootroot00000000000000from . import tests from .data_structures import ( FLASHDataset, FLASHGrid, FLASHHierarchy, FLASHParticleDataset, ) from .fields import FLASHFieldInfo from .io import IOHandlerFLASH, IOHandlerFLASHParticle yt-project-yt-f043ac8/yt/frontends/flash/data_structures.py000066400000000000000000000526371510711153200242100ustar00rootroot00000000000000import os import weakref from pathlib import Path import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset, ParticleFile from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.geometry_handler import Index from yt.geometry.grid_geometry_handler import GridIndex from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.file_handler import HDF5FileHandler, valid_hdf5_signature from yt.utilities.physical_ratios import cm_per_mpc from .fields import FLASHFieldInfo class FLASHGrid(AMRGridPatch): _id_offset = 1 # __slots__ = ["_level_id", "stop_index"] def __init__(self, id, index, level): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level class FLASHHierarchy(GridIndex): grid = FLASHGrid _preload_implemented = True def __init__(self, ds, dataset_type="flash_hdf5"): self.dataset_type = dataset_type self.field_indexes = {} self.dataset = weakref.proxy(ds) # for now, the index file is the dataset! self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self._handle = ds._handle self._particle_handle = ds._particle_handle self.float_type = np.float64 GridIndex.__init__(self, ds, dataset_type) def _initialize_data_storage(self): pass def _detect_output_fields(self): self.field_list = [ ("flash", s.decode("ascii", "ignore")) for s in self._handle["/unknown names"][:].flat ] if "/particle names" in self._particle_handle: self.field_list += [ ("io", "particle_" + s[0].decode("ascii", "ignore").strip()) for s in self._particle_handle["/particle names"][:] ] def _count_grids(self): try: self.num_grids = self.dataset._find_parameter( "integer", "globalnumblocks", True ) except KeyError: try: self.num_grids = self._handle["simulation parameters"]["total blocks"][ 0 ] except KeyError: self.num_grids = self._handle["/simulation parameters"][0][0] def _parse_index(self): f = self._handle # shortcut ds = self.dataset # shortcut f_part = self._particle_handle # shortcut # Initialize to the domain left / domain right ND = self.dataset.dimensionality DLE = self.dataset.domain_left_edge DRE = self.dataset.domain_right_edge for i in range(3): self.grid_left_edge[:, i] = DLE[i] self.grid_right_edge[:, i] = DRE[i] # We only go up to ND for 2D datasets self.grid_left_edge[:, :ND] = f["/bounding box"][:, :ND, 0] self.grid_right_edge[:, :ND] = f["/bounding box"][:, :ND, 1] # Move this to the parameter file try: nxb = ds.parameters["nxb"] nyb = ds.parameters["nyb"] nzb = ds.parameters["nzb"] except KeyError: nxb, nyb, nzb = ( int(f["/simulation parameters"][f"n{ax}b"]) for ax in "xyz" ) self.grid_dimensions[:] *= (nxb, nyb, nzb) try: self.grid_particle_count[:] = f_part["/localnp"][:][:, None] self._blockless_particle_count = ( f_part["/tracer particles"].shape[0] - self.grid_particle_count.sum() ) except KeyError: self.grid_particle_count[:] = 0.0 self._particle_indices = np.zeros(self.num_grids + 1, dtype="int64") if self.num_grids > 1: np.add.accumulate( self.grid_particle_count.squeeze(), out=self._particle_indices[1:] ) else: self._particle_indices[1] = self.grid_particle_count.squeeze() # This will become redundant, as _prepare_grid will reset it to its # current value. Note that FLASH uses 1-based indexing for refinement # levels, but we do not, so we reduce the level by 1. self.grid_levels.flat[:] = f["/refine level"][:][:] - 1 self.grids = np.empty(self.num_grids, dtype="object") for i in range(self.num_grids): self.grids[i] = self.grid(i + 1, self, self.grid_levels[i, 0]) # This is a possibly slow and verbose fix, and should be re-examined! rdx = self.dataset.domain_width / self.dataset.domain_dimensions nlevels = self.grid_levels.max() dxs = np.ones((nlevels + 1, 3), dtype="float64") for i in range(nlevels + 1): dxs[i, :ND] = rdx[:ND] / self.dataset.refine_by**i if ND < 3: dxs[:, ND:] = rdx[ND:] # Because we don't care about units, we're going to operate on views. gle = self.grid_left_edge.ndarray_view() gre = self.grid_right_edge.ndarray_view() geom = self.dataset.geometry if geom != "cartesian" and ND < 3: if geom == "spherical" and ND < 2: gle[:, 1] = 0.0 gre[:, 1] = np.pi gle[:, 2] = 0.0 gre[:, 2] = 2.0 * np.pi return def _populate_grid_objects(self): ii = np.argsort(self.grid_levels.flat) gid = self._handle["/gid"][:] first_ind = -(self.dataset.refine_by**self.dataset.dimensionality) for g in self.grids[ii].flat: gi = g.id - g._id_offset # FLASH uses 1-indexed group info g.Children = [self.grids[i - 1] for i in gid[gi, first_ind:] if i > -1] for g1 in g.Children: g1.Parent = g g._prepare_grid() g._setup_dx() if self.dataset.dimensionality < 3: DD = self.dataset.domain_right_edge[2] - self.dataset.domain_left_edge[2] for g in self.grids: g.dds[2] = DD if self.dataset.dimensionality < 2: DD = self.dataset.domain_right_edge[1] - self.dataset.domain_left_edge[1] for g in self.grids: g.dds[1] = DD self.max_level = self.grid_levels.max() class FLASHDataset(Dataset): _load_requirements = ["h5py"] _index_class: type[Index] = FLASHHierarchy _field_info_class = FLASHFieldInfo _handle = None def __init__( self, filename, dataset_type="flash_hdf5", storage_filename=None, particle_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): self.fluid_types += ("flash",) if self._handle is not None: return self._handle = HDF5FileHandler(filename) self.particle_filename = particle_filename filepath = Path(filename) if self.particle_filename is None: # try to guess the particle filename if "hdf5_plt_cnt" in filepath.name: # We have a plotfile, look for the particle file try: pfn = str( filepath.parent.resolve() / filepath.name.replace("plt_cnt", "part") ) self._particle_handle = HDF5FileHandler(pfn) self.particle_filename = pfn mylog.info( "Particle file found: %s", os.path.basename(self.particle_filename), ) except OSError: self._particle_handle = self._handle elif "hdf5_chk" in filepath.name: # This is a checkpoint file, should have the particles in it self._particle_handle = self._handle else: # particle_filename is specified by user self._particle_handle = HDF5FileHandler(self.particle_filename) # Check if the particle file has the same time if self._particle_handle != self._handle: plot_time = self._handle.handle.get("real scalars") if (part_time := self._particle_handle.handle.get("real scalars")) is None: raise RuntimeError("FLASH 2.x particle files are not supported!") if not np.isclose(part_time[0][1], plot_time[0][1]): self._particle_handle = self._handle mylog.warning( "%s and %s are not at the same time. " "This particle file will not be used.", self.particle_filename, filename, ) # These should be explicitly obtained from the file, but for now that # will wait until a reorganization of the source tree and better # generalization. self.refine_by = 2 Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename self.parameters["HydroMethod"] = "flash" # always PPM DE self.parameters["Time"] = 1.0 # default unit is 1... def _set_code_unit_attributes(self): if "unitsystem" in self.parameters: # Some versions of FLASH inject quotes in the runtime parameters # See issue #1721 us = self["unitsystem"].replace("'", "").replace('"', "").lower() if us == "cgs": b_factor = 1.0 elif us == "si": b_factor = np.sqrt(4 * np.pi / 1e7) elif us == "none": b_factor = np.sqrt(4 * np.pi) else: raise RuntimeError( "Runtime parameter unitsystem with " f"value {self['unitsystem']} is unrecognized" ) else: b_factor = 1.0 if self.cosmological_simulation == 1: length_factor = 1.0 / (1.0 + self.current_redshift) temperature_factor = 1.0 / (1.0 + self.current_redshift) ** 2 else: length_factor = 1.0 temperature_factor = 1.0 setdefaultattr(self, "magnetic_unit", self.quan(b_factor, "gauss")) setdefaultattr(self, "length_unit", self.quan(length_factor, "cm")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "cm/s")) setdefaultattr(self, "temperature_unit", self.quan(temperature_factor, "K")) def set_code_units(self): super().set_code_units() def _find_parameter(self, ptype, pname, scalar=False): nn = "/{} {}".format( ptype, {False: "runtime parameters", True: "scalars"}[scalar] ) if nn not in self._handle: raise KeyError(nn) for tpname, pval in zip( self._handle[nn][:, "name"], self._handle[nn][:, "value"], strict=True, ): if tpname.decode("ascii", "ignore").strip() == pname: if hasattr(pval, "decode"): pval = pval.decode("ascii", "ignore") if ptype == "string": return pval.strip() else: return pval raise KeyError(pname) def _parse_parameter_file(self): if "file format version" in self._handle: self._flash_version = self._handle["file format version"][:].item() elif "sim info" in self._handle: self._flash_version = self._handle["sim info"][:][ "file format version" ].item() else: raise RuntimeError("Can't figure out FLASH file version.") # First we load all of the parameters hns = ["simulation parameters"] # note the ordering here is important: runtime parameters should # overwrite scalars with the same name. for ptype in ["scalars", "runtime parameters"]: for vtype in ["integer", "real", "logical", "string"]: hns.append(f"{vtype} {ptype}") if self._flash_version > 7: for hn in hns: if hn not in self._handle: continue for varname, val in zip( self._handle[hn][:, "name"], self._handle[hn][:, "value"], strict=True, ): vn = varname.strip() if hn.startswith("string"): pval = val.strip() else: pval = val if vn in self.parameters and self.parameters[vn] != pval: mylog.info( "%s %s overwrites a simulation scalar of the same name", hn[:-1], vn, ) if hasattr(pval, "decode"): pval = pval.decode("ascii", "ignore") self.parameters[vn.decode("ascii", "ignore")] = pval if self._flash_version == 7: for hn in hns: if hn not in self._handle: continue if hn == "simulation parameters": zipover = ( (name, self._handle[hn][name][0]) for name in self._handle[hn].dtype.names ) else: zipover = zip( self._handle[hn][:, "name"], self._handle[hn][:, "value"], strict=True, ) for varname, val in zipover: vn = varname.strip() if hasattr(vn, "decode"): vn = vn.decode("ascii", "ignore") if hn.startswith("string"): pval = val.strip() else: pval = val if vn in self.parameters and self.parameters[vn] != pval: mylog.info( "%s %s overwrites a simulation scalar of the same name", hn[:-1], vn, ) if hasattr(pval, "decode"): pval = pval.decode("ascii", "ignore") self.parameters[vn] = pval # Determine block size try: nxb = self.parameters["nxb"] nyb = self.parameters["nyb"] nzb = self.parameters["nzb"] except KeyError: nxb, nyb, nzb = ( int(self._handle["/simulation parameters"][f"n{ax}b"]) for ax in "xyz" ) # FLASH2 only! # Determine dimensionality try: dimensionality = self.parameters["dimensionality"] except KeyError: dimensionality = 3 if nzb == 1: dimensionality = 2 if nyb == 1: dimensionality = 1 if dimensionality < 3: mylog.warning("Guessing dimensionality as %s", dimensionality) self.dimensionality = dimensionality self.geometry = Geometry(self.parameters["geometry"]) # Determine base grid parameters if "lrefine_min" in self.parameters.keys(): # PARAMESH nblockx = self.parameters["nblockx"] nblocky = self.parameters["nblocky"] nblockz = self.parameters["nblockz"] elif self.parameters["globalnumblocks"] == 1: # non-fixed block size UG nblockx = 1 nblocky = 1 nblockz = 1 else: # Uniform Grid nblockx = self.parameters["iprocs"] nblocky = self.parameters["jprocs"] nblockz = self.parameters["kprocs"] # In case the user wasn't careful if self.dimensionality <= 2: nblockz = 1 if self.dimensionality == 1: nblocky = 1 # Determine domain boundaries dle = np.array([self.parameters[f"{ax}min"] for ax in "xyz"]).astype("float64") dre = np.array([self.parameters[f"{ax}max"] for ax in "xyz"]).astype("float64") if self.dimensionality < 3: for d in range(self.dimensionality, 3): if dle[d] == dre[d]: mylog.warning( "Identical domain left edge and right edges " "along dummy dimension (%i), attempting to read anyway", d, ) dre[d] = dle[d] + 1.0 if self.dimensionality < 3 and self.geometry == "cylindrical": mylog.warning("Extending theta dimension to 2PI + left edge.") dre[2] = dle[2] + 2 * np.pi elif self.dimensionality < 3 and self.geometry == "polar": mylog.warning("Extending theta dimension to 2PI + left edge.") dre[1] = dle[1] + 2 * np.pi elif self.dimensionality < 3 and self.geometry == "spherical": mylog.warning("Extending phi dimension to 2PI + left edge.") dre[2] = dle[2] + 2 * np.pi if self.dimensionality == 1 and self.geometry == "spherical": mylog.warning("Extending theta dimension to PI + left edge.") dre[1] = dle[1] + np.pi self.domain_left_edge = dle self.domain_right_edge = dre self.domain_dimensions = np.array([nblockx * nxb, nblocky * nyb, nblockz * nzb]) # Try to determine Gamma try: self.gamma = self.parameters["gamma"] except Exception: mylog.info("Cannot find Gamma") pass # Get the simulation time self.current_time = self.parameters["time"] # Determine if this is a periodic box p = [ self.parameters.get(f"{ax}l_boundary_type", None) == "periodic" for ax in "xyz" ] self._periodicity = tuple(p) # Determine cosmological parameters. try: self.parameters["usecosmology"] self.cosmological_simulation = 1 self.current_redshift = 1.0 / self.parameters["scalefactor"] - 1.0 self.omega_lambda = self.parameters["cosmologicalconstant"] self.omega_matter = self.parameters["omegamatter"] self.hubble_constant = self.parameters["hubbleconstant"] self.hubble_constant *= cm_per_mpc * 1.0e-5 * 1.0e-2 # convert to 'h' except Exception: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: fileh = HDF5FileHandler(filename) if "bounding box" in fileh["/"].keys(): return True except OSError: pass return False @classmethod def _guess_candidates(cls, base, directories, files): candidates = [ _ for _ in files if ("_hdf5_plt_cnt_" in _) or ("_hdf5_chk_" in _) ] # Typically, Flash won't have nested outputs. return candidates, (len(candidates) == 0) def close(self): self._handle.close() class FLASHParticleFile(ParticleFile): pass class FLASHParticleDataset(FLASHDataset): _load_requirements = ["h5py"] _index_class = ParticleIndex filter_bbox = False _file_class = FLASHParticleFile def __init__( self, filename, dataset_type="flash_particle_hdf5", storage_filename=None, units_override=None, index_order=None, index_filename=None, unit_system="cgs", ): self.index_order = index_order self.index_filename = index_filename if self._handle is not None: return self._handle = HDF5FileHandler(filename) self.refine_by = 2 Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename def _parse_parameter_file(self): # Let the superclass do all the work but then # fix the domain dimensions super()._parse_parameter_file() domain_dimensions = np.zeros(3, "int32") domain_dimensions[: self.dimensionality] = 1 self.domain_dimensions = domain_dimensions self.filename_template = self.parameter_filename self.file_count = 1 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not valid_hdf5_signature(filename): return False if cls._missing_load_requirements(): return False try: fileh = HDF5FileHandler(filename) if ( "bounding box" not in fileh["/"].keys() and "localnp" in fileh["/"].keys() ): return True except OSError: pass return False @classmethod def _guess_candidates(cls, base, directories, files): candidates = [_ for _ in files if "_hdf5_part_" in _] # Typically, Flash won't have nested outputs. return candidates, (len(candidates) == 0) yt-project-yt-f043ac8/yt/frontends/flash/definitions.py000066400000000000000000000000001510711153200232600ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/flash/fields.py000066400000000000000000000211161510711153200222260ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer # Common fields in FLASH: (Thanks to John ZuHone for this list) # # dens gas mass density (g/cc) -- # eint internal energy (ergs/g) -- # ener total energy (ergs/g), with 0.5*v^2 -- # gamc gamma defined as ratio of specific heats, no units # game gamma defined as in , no units # gpol gravitational potential from the last timestep (ergs/g) # gpot gravitational potential from the current timestep (ergs/g) # grac gravitational acceleration from the current timestep (cm s^-2) # pden particle mass density (usually dark matter) (g/cc) # pres pressure (erg/cc) # temp temperature (K) -- # velx velocity x (cm/s) -- # vely velocity y (cm/s) -- # velz velocity z (cm/s) -- b_units = "code_magnetic" pres_units = "code_mass/(code_length*code_time**2)" en_units = "code_mass * (code_length/code_time)**2" rho_units = "code_mass / code_length**3" class FLASHFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("velx", ("code_length/code_time", ["velocity_x"], None)), ("vely", ("code_length/code_time", ["velocity_y"], None)), ("velz", ("code_length/code_time", ["velocity_z"], None)), ("dens", ("code_mass/code_length**3", ["density"], None)), ("temp", ("code_temperature", ["temperature"], None)), ("pres", (pres_units, ["pressure"], None)), ("gpot", ("code_length**2/code_time**2", ["gravitational_potential"], None)), ("gpol", ("code_length**2/code_time**2", [], None)), ("tion", ("code_temperature", [], None)), ("tele", ("code_temperature", [], None)), ("trad", ("code_temperature", [], None)), ("pion", (pres_units, [], None)), ("pele", (pres_units, [], "Electron Pressure, P_e")), ("prad", (pres_units, [], "Radiation Pressure")), ("eion", (en_units, [], "Ion Internal Specific Energy")), ("eele", (en_units, [], "Electron Internal Specific Energy")), ("erad", (en_units, [], "Radiation Internal Specific Energy")), ("pden", (rho_units, [], "Particle Mass Density")), ("depo", ("code_length**2/code_time**2", [], None)), ("ye", ("", [], "Y_e")), ("magp", (pres_units, [], None)), ("divb", ("code_magnetic/code_length", [], None)), ("game", ("", [], r"\gamma_e\ \rm{(ratio\ of\ specific\ heats)}")), ("gamc", ("", [], r"\gamma_c\ \rm{(ratio\ of\ specific\ heats)}")), ("flam", ("", [], None)), ("absr", ("", [], "Absorption Coefficient")), ("emis", ("", [], "Emissivity")), ("cond", ("", [], "Conductivity")), ("dfcf", ("", [], "Diffusion Equation Scalar")), ("fllm", ("", [], "Flux Limit")), ("pipe", ("", [], "P_i/P_e")), ("tite", ("", [], "T_i/T_e")), ("dbgs", ("", [], "Debug for Shocks")), ("cham", ("", [], "Chamber Material Fraction")), ("targ", ("", [], "Target Material Fraction")), ("sumy", ("", [], None)), ("mgdc", ("", [], "Emission Minus Absorption Diffusion Terms")), ("magx", (b_units, [], "B_x")), ("magy", (b_units, [], "B_y")), ("magz", (b_units, [], "B_z")), ) known_particle_fields = ( ("particle_posx", ("code_length", ["particle_position_x"], None)), ("particle_posy", ("code_length", ["particle_position_y"], None)), ("particle_posz", ("code_length", ["particle_position_z"], None)), ("particle_velx", ("code_length/code_time", ["particle_velocity_x"], None)), ("particle_vely", ("code_length/code_time", ["particle_velocity_y"], None)), ("particle_velz", ("code_length/code_time", ["particle_velocity_z"], None)), ("particle_tag", ("", ["particle_index"], None)), ("particle_mass", ("code_mass", ["particle_mass"], None)), ( "particle_gpot", ("code_length**2/code_time**2", ["particle_gravitational_potential"], None), ), ) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases unit_system = self.ds.unit_system # Adopt FLASH 4.6 value for Na Na = self.ds.quan(6.022140857e23, "g**-1") for i in range(1, 1000): self.add_output_field( ("flash", f"r{i:03}"), sampling_type="cell", units="", display_name=f"Energy Group {i}", ) # Add energy fields def ekin(data): ek = data["flash", "velx"] ** 2 if data.ds.dimensionality >= 2: ek += data["flash", "vely"] ** 2 if data.ds.dimensionality == 3: ek += data["flash", "velz"] ** 2 return 0.5 * ek if ("flash", "ener") in self.field_list: self.add_output_field( ("flash", "ener"), sampling_type="cell", units="code_length**2/code_time**2", ) self.alias( ("gas", "specific_total_energy"), ("flash", "ener"), units=unit_system["specific_energy"], ) else: def _ener(field, data): ener = data["flash", "eint"] + ekin(data) try: ener += data["flash", "magp"] / data["flash", "dens"] except Exception: pass return ener self.add_field( ("gas", "specific_total_energy"), sampling_type="cell", function=_ener, units=unit_system["specific_energy"], ) if ("flash", "eint") in self.field_list: self.add_output_field( ("flash", "eint"), sampling_type="cell", units="code_length**2/code_time**2", ) self.alias( ("gas", "specific_thermal_energy"), ("flash", "eint"), units=unit_system["specific_energy"], ) else: def _eint(field, data): eint = data["flash", "ener"] - ekin(data) try: eint -= data["flash", "magp"] / data["flash", "dens"] except Exception: pass return eint self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_eint, units=unit_system["specific_energy"], ) ## Derived FLASH Fields if ("flash", "abar") in self.field_list: self.alias(("gas", "mean_molecular_weight"), ("flash", "abar")) elif ("flash", "sumy") in self.field_list: def _abar(field, data): return 1.0 / data["flash", "sumy"] self.add_field( ("gas", "mean_molecular_weight"), sampling_type="cell", function=_abar, units="", ) elif "eos_singlespeciesa" in self.ds.parameters: def _abar(field, data): return data.ds.parameters["eos_singlespeciesa"] * data["index", "ones"] self.add_field( ("gas", "mean_molecular_weight"), sampling_type="cell", function=_abar, units="", ) if ("flash", "sumy") in self.field_list: def _nele(field, data): return data["flash", "dens"] * data["flash", "ye"] * Na self.add_field( ("gas", "El_number_density"), sampling_type="cell", function=_nele, units=unit_system["number_density"], ) def _nion(field, data): return data["flash", "dens"] * data["flash", "sumy"] * Na self.add_field( ("gas", "ion_number_density"), sampling_type="cell", function=_nion, units=unit_system["number_density"], ) def _number_density(field, data): return ( data["gas", "El_number_density"] + data["gas", "ion_number_density"] ) else: def _number_density(field, data): return data["flash", "dens"] * Na / data["gas", "mean_molecular_weight"] self.add_field( ("gas", "number_density"), sampling_type="cell", function=_number_density, units=unit_system["number_density"], ) setup_magnetic_field_aliases(self, "flash", [f"mag{ax}" for ax in "xyz"]) yt-project-yt-f043ac8/yt/frontends/flash/io.py000066400000000000000000000271461510711153200214000ustar00rootroot00000000000000from functools import cached_property from itertools import groupby import numpy as np from yt.geometry.selection_routines import AlwaysSelector from yt.utilities.io_handler import BaseIOHandler # http://stackoverflow.com/questions/2361945/detecting-consecutive-integers-in-a-list def particle_sequences(grids): g_iter = sorted(grids, key=lambda g: g.id) for _k, g in groupby(enumerate(g_iter), lambda i_x: i_x[0] - i_x[1].id): seq = [v[1] for v in g] yield seq[0], seq[-1] def grid_sequences(grids): g_iter = sorted(grids, key=lambda g: g.id) for _k, g in groupby(enumerate(g_iter), lambda i_x1: i_x1[0] - i_x1[1].id): seq = [v[1] for v in g] yield seq def determine_particle_fields(handle): try: particle_fields = [ s[0].decode("ascii", "ignore").strip() for s in handle["/particle names"][:] ] _particle_fields = {"particle_" + s: i for i, s in enumerate(particle_fields)} except KeyError: _particle_fields = {} return _particle_fields def _conditionally_split_arrays(inp_arrays, condition): output_true = [] output_false = [] for arr in inp_arrays: output_true.append(arr[condition]) output_false.append(arr[~condition]) return output_true, output_false class IOHandlerFLASH(BaseIOHandler): _particle_reader = False _dataset_type = "flash_hdf5" def __init__(self, ds): super().__init__(ds) # Now we cache the particle fields self._handle = ds._handle self._particle_handle = ds._particle_handle self._particle_fields = determine_particle_fields(self._particle_handle) def _read_particles( self, fields_to_read, type, args, grid_list, count_list, conv_factors ): pass def io_iter(self, chunks, fields): f = self._handle for chunk in chunks: for field in fields: # Note that we *prefer* to iterate over the fields on the # outside; here, though, we're iterating over them on the # inside because we may exhaust our chunks. ftype, fname = field ds = f[f"/{fname}"] for gs in grid_sequences(chunk.objs): start = gs[0].id - gs[0]._id_offset end = gs[-1].id - gs[-1]._id_offset + 1 data = ds[start:end, :, :, :] for i, g in enumerate(gs): yield field, g, self._read_obj_field(g, field, (data, i)) def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) f_part = self._particle_handle p_ind = self.ds.index._particle_indices px, py, pz = (self._particle_fields[f"particle_pos{ax}"] for ax in "xyz") pblk = self._particle_fields["particle_blk"] blockless_buffer = self.ds.index._blockless_particle_count p_fields = f_part["/tracer particles"] assert len(ptf) == 1 ptype = list(ptf.keys())[0] bxyz = [] # We need to track all the particles that don't have blocks and make # sure they get yielded too. But, we also want our particles to largely # line up with the grids they are resident in. So we keep a buffer of # particles. That buffer is checked for any "blockless" particles, # which get yielded at the end. for chunk in chunks: start = end = None for g1, g2 in particle_sequences(chunk.objs): start = p_ind[g1.id - g1._id_offset] end_nobuff = p_ind[g2.id - g2._id_offset + 1] end = end_nobuff + blockless_buffer maxlen = end_nobuff - start blk = np.asarray(p_fields[start:end, pblk], dtype="=i8") >= 0 # Can we use something like np.choose here? xyz = [ np.asarray(p_fields[start:end, _], dtype="=f8") for _ in (px, py, pz) ] (x, y, z), _xyz = _conditionally_split_arrays(xyz, blk) if _xyz[0].size > 0: bxyz.append(_xyz) yield ptype, (x[:maxlen], y[:maxlen], z[:maxlen]), 0.0 if len(bxyz) == 0: return bxyz = np.concatenate(bxyz, axis=-1) yield ptype, (bxyz[0, :], bxyz[1, :], bxyz[2, :]), 0.0 def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) f_part = self._particle_handle p_ind = self.ds.index._particle_indices px, py, pz = (self._particle_fields[f"particle_pos{ax}"] for ax in "xyz") pblk = self._particle_fields["particle_blk"] blockless_buffer = self.ds.index._blockless_particle_count p_fields = f_part["/tracer particles"] assert len(ptf) == 1 ptype = list(ptf.keys())[0] field_list = ptf[ptype] bxyz = [] bfields = {(ptype, field): [] for field in field_list} for chunk in chunks: for g1, g2 in particle_sequences(chunk.objs): start = p_ind[g1.id - g1._id_offset] end_nobuff = p_ind[g2.id - g2._id_offset + 1] end = end_nobuff + blockless_buffer maxlen = end_nobuff - start blk = np.asarray(p_fields[start:end, pblk], dtype="=i8") >= 0 xyz = [ np.asarray(p_fields[start:end, _], dtype="=f8") for _ in (px, py, pz) ] (x, y, z), _xyz = _conditionally_split_arrays(xyz, blk) x, y, z = (_[:maxlen] for _ in (x, y, z)) if _xyz[0].size > 0: bxyz.append(_xyz) mask = selector.select_points(x, y, z, 0.0) blockless = (_xyz[0] > 0).any() # This checks if none of the particles within these blocks are # included in the mask We need to also allow for blockless # particles to be selected. if mask is None and not blockless: continue for field in field_list: fi = self._particle_fields[field] (data,), (bdata,) = _conditionally_split_arrays( [p_fields[start:end, fi]], blk ) # Note that we have to apply mask to the split array, since # we select on the split array. if mask is not None: # Be sure to truncate off the buffer length!! yield (ptype, field), data[:maxlen][mask] if blockless: bfields[ptype, field].append(bdata) if len(bxyz) == 0: return bxyz = np.concatenate(bxyz, axis=-1) mask = selector.select_points(bxyz[0, :], bxyz[1, :], bxyz[2, :], 0.0) if mask is None: return for field in field_list: data_field = np.concatenate(bfields[ptype, field], axis=-1) yield (ptype, field), data_field[mask] def _read_obj_field(self, obj, field, ds_offset=None): if ds_offset is None: ds_offset = (None, -1) ds, offset = ds_offset # our context here includes datasets and whatnot that are opened in the # hdf5 file if ds is None: ds = self._handle[f"/{field[1]}"] if offset == -1: data = ds[obj.id - obj._id_offset, :, :, :].transpose() else: data = ds[offset, :, :, :].transpose() return data.astype("=f8") def _read_chunk_data(self, chunk, fields): f = self._handle rv = {} for g in chunk.objs: rv[g.id] = {} # Split into particles and non-particles fluid_fields, particle_fields = [], [] for ftype, fname in fields: if ftype in self.ds.particle_types: particle_fields.append((ftype, fname)) else: fluid_fields.append((ftype, fname)) if len(particle_fields) > 0: selector = AlwaysSelector(self.ds) rv.update(self._read_particle_selection([chunk], selector, particle_fields)) if len(fluid_fields) == 0: return rv for field in fluid_fields: ftype, fname = field ds = f[f"/{fname}"] for gs in grid_sequences(chunk.objs): start = gs[0].id - gs[0]._id_offset end = gs[-1].id - gs[-1]._id_offset + 1 data = ds[start:end, :, :, :].transpose() for i, g in enumerate(gs): rv[g.id][field] = np.asarray(data[..., i], "=f8") return rv class IOHandlerFLASHParticle(BaseIOHandler): _particle_reader = True _dataset_type = "flash_particle_hdf5" def __init__(self, ds): super().__init__(ds) # Now we cache the particle fields self._handle = ds._handle self._particle_fields = determine_particle_fields(self._handle) self._position_fields = [ self._particle_fields[f"particle_pos{ax}"] for ax in "xyz" ] @property def chunksize(self): return 32**3 def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) data_files = set() assert len(ptf) == 1 for chunk in chunks: for obj in chunk.objs: data_files.update(obj.data_files) px, py, pz = self._position_fields p_fields = self._handle["/tracer particles"] for data_file in sorted(data_files, key=lambda x: (x.filename, x.start)): pxyz = np.asarray( p_fields[data_file.start : data_file.end, [px, py, pz]], dtype="=f8" ) yield "io", pxyz.T, 0.0 def _yield_coordinates(self, data_file, needed_ptype=None): px, py, pz = self._position_fields p_fields = self._handle["/tracer particles"] pxyz = np.asarray( p_fields[data_file.start : data_file.end, [px, py, pz]], dtype="=f8" ) yield ("io", pxyz) def _read_particle_data_file(self, data_file, ptf, selector=None): px, py, pz = self._position_fields p_fields = self._handle["/tracer particles"] si, ei = data_file.start, data_file.end data_return = {} # This should just be a single item for ptype, field_list in sorted(ptf.items()): x = np.asarray(p_fields[si:ei, px], dtype="=f8") y = np.asarray(p_fields[si:ei, py], dtype="=f8") z = np.asarray(p_fields[si:ei, pz], dtype="=f8") if selector: mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: fi = self._particle_fields[field] data = p_fields[si:ei, fi] if selector: data = data[mask] data_return[ptype, field] = data return data_return def _read_particle_fields(self, chunks, ptf, selector): assert len(ptf) == 1 yield from super()._read_particle_fields(chunks, ptf, selector) @cached_property def _pcount(self): return self._handle["/localnp"][:].sum() def _count_particles(self, data_file): si, ei = data_file.start, data_file.end pcount = self._pcount if None not in (si, ei): pcount = np.clip(pcount - si, 0, ei - si) return {"io": pcount} def _identify_fields(self, data_file): fields = [("io", field) for field in self._particle_fields] return fields, {} yt-project-yt-f043ac8/yt/frontends/flash/misc.py000066400000000000000000000000001510711153200217000ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/flash/tests/000077500000000000000000000000001510711153200215475ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/flash/tests/__init__.py000066400000000000000000000000001510711153200236460ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/flash/tests/test_outputs.py000066400000000000000000000103661510711153200247110ustar00rootroot00000000000000from collections import OrderedDict import numpy as np from numpy.testing import assert_allclose, assert_equal from yt.frontends.flash.api import FLASHDataset, FLASHParticleDataset from yt.loaders import load from yt.testing import ( ParticleSelectionComparison, disable_dataset_cache, requires_file, requires_module, units_override_check, ) from yt.utilities.answer_testing.framework import ( data_dir_load, nbody_answer, requires_ds, small_patch_amr, ) _fields = (("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude")) sloshing = "GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0300" @requires_ds(sloshing, big_data=True) def test_sloshing(): ds = data_dir_load(sloshing) assert_equal(str(ds), "sloshing_low_res_hdf5_plt_cnt_0300") for test in small_patch_amr(ds, _fields): test_sloshing.__name__ = test.description yield test _fields_2d = (("gas", "temperature"), ("gas", "density")) wt = "WindTunnel/windtunnel_4lev_hdf5_plt_cnt_0030" @requires_module("h5py") @requires_ds(wt) def test_wind_tunnel(): ds = data_dir_load(wt) assert_equal(str(ds), "windtunnel_4lev_hdf5_plt_cnt_0030") for test in small_patch_amr(ds, _fields_2d): test_wind_tunnel.__name__ = test.description yield test @requires_module("h5py") @requires_file(wt) def test_FLASHDataset(): assert isinstance(data_dir_load(wt), FLASHDataset) @requires_module("h5py") @requires_file(sloshing) def test_units_override(): units_override_check(sloshing) @disable_dataset_cache @requires_module("h5py") @requires_file(sloshing) def test_magnetic_units(): ds1 = load(sloshing) assert_allclose(ds1.magnetic_unit.value, np.sqrt(4.0 * np.pi)) assert str(ds1.magnetic_unit.units) == "G" mag_unit1 = ds1.magnetic_unit.to("code_magnetic") assert_allclose(mag_unit1.value, 1.0) assert str(mag_unit1.units) == "code_magnetic" ds2 = load(sloshing, unit_system="mks") assert_allclose(ds2.magnetic_unit.value, np.sqrt(4.0 * np.pi) * 1.0e-4) assert str(ds2.magnetic_unit.units) == "T" mag_unit2 = ds2.magnetic_unit.to("code_magnetic") assert_allclose(mag_unit2.value, 1.0) assert str(mag_unit2.units) == "code_magnetic" @requires_module("h5py") @requires_file(sloshing) def test_mu(): ds = data_dir_load(sloshing) sp = ds.sphere("c", (0.1, "unitary")) assert np.all( sp["gas", "mean_molecular_weight"] == ds.parameters["eos_singlespeciesa"] ) fid_1to3_b1 = "fiducial_1to3_b1/fiducial_1to3_b1_hdf5_part_0080" fid_1to3_b1_fields = OrderedDict( [ (("all", "particle_mass"), None), (("all", "particle_ones"), None), (("all", "particle_velocity_x"), ("all", "particle_mass")), (("all", "particle_velocity_y"), ("all", "particle_mass")), (("all", "particle_velocity_z"), ("all", "particle_mass")), ] ) @requires_module("h5py") @requires_file(fid_1to3_b1) def test_FLASHParticleDataset(): assert isinstance(data_dir_load(fid_1to3_b1), FLASHParticleDataset) @requires_module("h5py") @requires_file(fid_1to3_b1) def test_FLASHParticleDataset_selection(): ds = data_dir_load(fid_1to3_b1) psc = ParticleSelectionComparison(ds) psc.run_defaults() dens_turb_mag = "DensTurbMag/DensTurbMag_hdf5_plt_cnt_0015" @requires_module("h5py") @requires_file(dens_turb_mag) def test_FLASH25_dataset(): ds = data_dir_load(dens_turb_mag) assert_equal(ds.parameters["time"], 751000000000.0) assert_equal(ds.domain_dimensions, np.array([8, 8, 8])) assert_equal(ds.domain_left_edge, ds.arr([-2e18, -2e18, -2e18], "code_length")) assert_equal(ds.index.num_grids, 73) dd = ds.all_data() dd["gas", "density"] @requires_module("h5py") @requires_ds(fid_1to3_b1, big_data=True) def test_fid_1to3_b1(): ds = data_dir_load(fid_1to3_b1) for test in nbody_answer( ds, "fiducial_1to3_b1_hdf5_part_0080", 6684119, fid_1to3_b1_fields ): test_fid_1to3_b1.__name__ = test.description yield test loc_bub_dust = "LocBub_dust/LocBub_dust_hdf5_plt_cnt_0220" @requires_module("h5py") @requires_file(loc_bub_dust) def test_blockless_particles(): ds = data_dir_load(loc_bub_dust) dd = ds.all_data() pos = dd["all", "particle_position"] assert_equal(pos.shape, (2239, 3)) yt-project-yt-f043ac8/yt/frontends/gadget/000077500000000000000000000000001510711153200205435ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget/__init__.py000066400000000000000000000000001510711153200226420ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget/api.py000066400000000000000000000003201510711153200216610ustar00rootroot00000000000000from . import tests from .data_structures import GadgetDataset, GadgetFieldInfo, GadgetHDF5Dataset from .io import IOHandlerGadgetBinary, IOHandlerGadgetHDF5 from .simulation_handling import GadgetSimulation yt-project-yt-f043ac8/yt/frontends/gadget/data_structures.py000066400000000000000000000651341510711153200243420ustar00rootroot00000000000000import os import struct import numpy as np from yt.data_objects.static_output import ParticleFile from yt.fields.field_info_container import FieldInfoContainer from yt.frontends.sph.data_structures import SPHDataset, SPHParticleIndex from yt.funcs import only_on_root from yt.geometry.geometry_handler import Index from yt.utilities.chemical_formulas import compute_mu from yt.utilities.cosmology import Cosmology from yt.utilities.fortran_utils import read_record from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .definitions import ( SNAP_FORMAT_2_OFFSET, gadget_field_specs, gadget_header_specs, gadget_ptype_specs, ) from .fields import GadgetFieldInfo def _fix_unit_ordering(unit): if isinstance(unit[0], str): unit = unit[1], unit[0] return unit def _byte_swap_32(x): return struct.unpack(">I", struct.pack("" # Format 2? elif rhead == 8: return 2, "<" elif rhead == _byte_swap_32(8): return 2, ">" else: raise RuntimeError( "Gadget snapshot file is likely corrupted! " f"The first 4 bytes represent {rhead} (as little endian int32). " f"But we are looking for {first_header_size} (for format 1) " "or 8 (for format 2)." ) @property def position_offset(self): """Offset to the position block.""" n_header = len(self.size) offset = 8 * n_header + sum(self.size) if self.gadget_format[0] == 2: offset += SNAP_FORMAT_2_OFFSET * (n_header + 1) return offset @property def size(self): """Header size in bytes.""" def single_header_size(single_header_spec): return sum( field[1] * np.dtype(field[2]).itemsize for field in single_header_spec ) return [single_header_size(spec) for spec in self.spec] @property def value(self): """Header values as a dictionary.""" # The entries in this header are capitalized and named to match Table 4 # in the GADGET-2 user guide. gformat, endianswap = self.gadget_format # Read header with self.open() as f: hvals = {} for spec in self.spec: if gformat == 2: f.seek(f.tell() + SNAP_FORMAT_2_OFFSET) hvals_new = read_record(f, spec, endian=endianswap) hvals.update(hvals_new) # Remove placeholder keys for key in self._placeholder_keys: if key in hvals: del hvals[key] # Convert length 1 list to scalar value for i in hvals: if len(hvals[i]) == 1: hvals[i] = hvals[i][0] return hvals def open(self): """Open snapshot file.""" for filename in [self.filename, self.filename + ".0"]: if os.path.exists(filename): return open(filename, "rb") raise FileNotFoundError(f"Snapshot file {self.filename} does not exist.") def validate(self): """Validate data integrity.""" try: self.open().close() self.gadget_format self.float_type except Exception: return False return True class GadgetBinaryFile(ParticleFile): def __init__(self, ds, io, filename, file_id, range=None): header = GadgetBinaryHeader(filename, ds._header.spec) self.header = header.value self._position_offset = header.position_offset with header.open() as f: self._file_size = f.seek(0, os.SEEK_END) super().__init__(ds, io, filename, file_id, range) def _calculate_offsets(self, field_list, pcounts): # Note that we ignore pcounts here because it's the global count. We # just want the local count, which we store here. self.field_offsets = self.io._calculate_field_offsets( field_list, self.header["Npart"].copy(), self._position_offset, self.start, self._file_size, ) class GadgetBinaryIndex(SPHParticleIndex): def __init__(self, ds, dataset_type): super().__init__(ds, dataset_type) self._initialize_index() def _initialize_index(self): # Normally this function is called during field detection. We call it # here because we need to know which fields exist on-disk so that we can # read in the smoothing lengths for SPH data before we construct the # Morton bitmaps. self._detect_output_fields() super()._initialize_index() def _initialize_frontend_specific(self): super()._initialize_frontend_specific() self.io._float_type = self.ds._header.float_type class GadgetDataset(SPHDataset): _index_class: type[Index] = GadgetBinaryIndex _file_class: type[ParticleFile] = GadgetBinaryFile _field_info_class: type[FieldInfoContainer] = GadgetFieldInfo _particle_mass_name = "Mass" _particle_coordinates_name = "Coordinates" _particle_velocity_name = "Velocities" _sph_ptypes = ("Gas",) _suffix = "" def __init__( self, filename, dataset_type="gadget_binary", additional_fields=(), unit_base=None, index_order=None, index_filename=None, kdtree_filename=None, kernel_name=None, bounding_box=None, header_spec="default", field_spec="default", ptype_spec="default", long_ids=False, units_override=None, mean_molecular_weight=None, header_offset=0, unit_system="cgs", use_dark_factor=False, w_0=-1.0, w_a=0.0, default_species_fields=None, ): if self._instantiated: return # Check if filename is a directory if os.path.isdir(filename): # Get the .0 snapshot file. We know there's only 1 and it's valid since we # came through _is_valid in load() for f in os.listdir(filename): fname = os.path.join(filename, f) fext = os.path.splitext(fname)[-1] if ( (".0" in f) and (fext not in {".ewah", ".kdtree"}) and os.path.isfile(fname) ): filename = os.path.join(filename, f) break self._header = GadgetBinaryHeader(filename, header_spec) header_size = self._header.size if header_size != [256]: only_on_root( mylog.warning, "Non-standard header size is detected! " "Gadget-2 standard header is 256 bytes, but yours is %s. " "Make sure a non-standard header is actually expected. " "Otherwise something is wrong, " "and you might want to check how the dataset is loaded. " "Further information about header specification can be found in " "https://yt-project.org/docs/dev/examining/loading_data.html#header-specification.", header_size, ) self._field_spec = self._setup_binary_spec(field_spec, gadget_field_specs) self._ptype_spec = self._setup_binary_spec(ptype_spec, gadget_ptype_specs) self.storage_filename = None if long_ids: self._id_dtype = "u8" else: self._id_dtype = "u4" self.long_ids = long_ids self.header_offset = header_offset if unit_base is not None and "UnitLength_in_cm" in unit_base: # We assume this is comoving, because in the absence of comoving # integration the redshift will be zero. unit_base["cmcm"] = 1.0 / unit_base["UnitLength_in_cm"] self._unit_base = unit_base if bounding_box is not None: # This ensures that we know a bounding box has been applied self._domain_override = True bbox = np.array(bounding_box, dtype="float64") if bbox.shape == (2, 3): bbox = bbox.transpose() self.domain_left_edge = bbox[:, 0] self.domain_right_edge = bbox[:, 1] else: self.domain_left_edge = self.domain_right_edge = None if units_override is not None: raise RuntimeError( "units_override is not supported for GadgetDataset. " + "Use unit_base instead." ) # Set dark energy parameters before cosmology object is created self.use_dark_factor = use_dark_factor self.w_0 = w_0 self.w_a = w_a super().__init__( filename, dataset_type=dataset_type, unit_system=unit_system, index_order=index_order, index_filename=index_filename, kdtree_filename=kdtree_filename, kernel_name=kernel_name, default_species_fields=default_species_fields, ) if self.cosmological_simulation: self.time_unit.convert_to_units("s/h") self.length_unit.convert_to_units("kpccm/h") self.mass_unit.convert_to_units("g/h") else: self.time_unit.convert_to_units("s") self.length_unit.convert_to_units("kpc") self.mass_unit.convert_to_units("Msun") if mean_molecular_weight is None: self.mu = compute_mu(self.default_species_fields) else: self.mu = mean_molecular_weight @classmethod def _setup_binary_spec(cls, spec, spec_dict): if isinstance(spec, str): _hs = () for hs in spec.split("+"): _hs += spec_dict[hs] spec = _hs return spec def __str__(self): return os.path.basename(self.parameter_filename).split(".")[0] def _get_hvals(self): self.gen_hsmls = False return self._header.value def _parse_parameter_file(self): hvals = self._get_hvals() self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "sph" # Set standard values # We may have an overridden bounding box. if self.domain_left_edge is None and hvals["BoxSize"] != 0: self.domain_left_edge = np.zeros(3, "float64") self.domain_right_edge = np.ones(3, "float64") * hvals["BoxSize"] self.domain_dimensions = np.ones(3, "int32") self._periodicity = (True, True, True) self.current_redshift = hvals.get("Redshift", 0.0) if "Redshift" not in hvals: mylog.info("Redshift is not set in Header. Assuming z=0.") if "OmegaLambda" in hvals: self.omega_lambda = hvals["OmegaLambda"] self.omega_matter = hvals["Omega0"] self.hubble_constant = hvals["HubbleParam"] self.cosmological_simulation = self.omega_lambda != 0.0 else: # If these are not set it is definitely not a cosmological dataset. self.omega_lambda = 0.0 self.omega_matter = 0.0 # Just in case somebody asks for it. # omega_matter has been changed to 0.0 for consistency with # non-cosmological frontends self.cosmological_simulation = 0 # Hubble is set below for Omega Lambda = 0. # According to the Gadget manual, OmegaLambda will be zero for # non-cosmological datasets. However, it may be the case that # individuals are running cosmological simulations *without* Lambda, in # which case we may be doing something incorrect here. # It may be possible to deduce whether ComovingIntegration is on # somehow, but opinions on this vary. if not self.cosmological_simulation: mylog.info("Omega Lambda is 0.0, so we are turning off Cosmology.") self.hubble_constant = 1.0 # So that scaling comes out correct self.current_redshift = 0.0 # This may not be correct. self.current_time = hvals["Time"] else: # Now we calculate our time based on the cosmology, because in # ComovingIntegration hvals["Time"] will in fact be the expansion # factor, not the actual integration time, so we re-calculate # global time from our Cosmology. cosmo = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmo.lookback_time(self.current_redshift, 1e6) mylog.info( "Calculating time from %0.3e to be %0.3e seconds", hvals["Time"], self.current_time, ) self.parameters = hvals basename, _, _ = self.basename.partition(".") prefix = os.path.join(self.directory, basename) if hvals["NumFiles"] > 1: for t in ( f"{prefix}.%(num)s{self._suffix}", f"{prefix}.gad.%(num)s{self._suffix}", ): if os.path.isfile(t % {"num": 0}): self.filename_template = t break else: raise RuntimeError("Could not determine correct data file template.") else: self.filename_template = self.parameter_filename self.file_count = hvals["NumFiles"] def _set_code_unit_attributes(self): # If no units passed in by user, set a sane default (Gadget-2 users # guide). if self._unit_base is None: if self.cosmological_simulation == 1: only_on_root( mylog.info, "Assuming length units are in kpc/h (comoving)" ) self._unit_base = {"length": (1.0, "kpccm/h")} else: only_on_root(mylog.info, "Assuming length units are in kpc (physical)") self._unit_base = {"length": (1.0, "kpc")} # If units passed in by user, decide what to do about # co-moving and factors of h unit_base = self._unit_base or {} if "length" in unit_base: length_unit = unit_base["length"] elif "UnitLength_in_cm" in unit_base: if self.cosmological_simulation == 0: length_unit = (unit_base["UnitLength_in_cm"], "cm") else: length_unit = (unit_base["UnitLength_in_cm"], "cmcm/h") else: raise RuntimeError length_unit = _fix_unit_ordering(length_unit) self.length_unit = self.quan(length_unit[0], length_unit[1]) unit_base = self._unit_base or {} if self.cosmological_simulation: # see http://www.mpa-garching.mpg.de/gadget/gadget-list/0113.html # for why we need to include a factor of square root of the # scale factor vel_units = "cm/s * sqrt(a)" else: vel_units = "cm/s" if "velocity" in unit_base: velocity_unit = unit_base["velocity"] elif "UnitVelocity_in_cm_per_s" in unit_base: velocity_unit = (unit_base["UnitVelocity_in_cm_per_s"], vel_units) else: velocity_unit = (1e5, vel_units) velocity_unit = _fix_unit_ordering(velocity_unit) self.velocity_unit = self.quan(velocity_unit[0], velocity_unit[1]) # We set hubble_constant = 1.0 for non-cosmology, so this is safe. # Default to 1e10 Msun/h if mass is not specified. if "mass" in unit_base: mass_unit = unit_base["mass"] elif "UnitMass_in_g" in unit_base: if self.cosmological_simulation == 0: mass_unit = (unit_base["UnitMass_in_g"], "g") else: mass_unit = (unit_base["UnitMass_in_g"], "g/h") else: # Sane default mass_unit = (1e10, "Msun/h") mass_unit = _fix_unit_ordering(mass_unit) self.mass_unit = self.quan(mass_unit[0], mass_unit[1]) if self.cosmological_simulation: # self.velocity_unit is the unit to rescale on-disk velocities, The # actual internal velocity unit is really in comoving units # since the time unit is derived from the internal velocity unit, we # infer the internal velocity unit here and name it vel_unit # # see http://www.mpa-garching.mpg.de/gadget/gadget-list/0113.html if "velocity" in unit_base: vel_unit = unit_base["velocity"] elif "UnitVelocity_in_cm_per_s" in unit_base: vel_unit = (unit_base["UnitVelocity_in_cm_per_s"], "cmcm/s") else: vel_unit = (1, "kmcm/s") vel_unit = self.quan(*vel_unit) else: vel_unit = self.velocity_unit self.time_unit = self.length_unit / vel_unit if "specific_energy" in unit_base: specific_energy_unit = unit_base["specific_energy"] elif "UnitEnergy_in_cgs" in unit_base and "UnitMass_in_g" in unit_base: specific_energy_unit = ( unit_base["UnitEnergy_in_cgs"] / unit_base["UnitMass_in_g"] ) specific_energy_unit = (specific_energy_unit, "(cm/s)**2") else: # Sane default specific_energy_unit = (1, "(km/s) ** 2") specific_energy_unit = _fix_unit_ordering(specific_energy_unit) self.specific_energy_unit = self.quan(*specific_energy_unit) if "magnetic" in unit_base: magnetic_unit = unit_base["magnetic"] elif "UnitMagneticField_in_gauss" in unit_base: magnetic_unit = (unit_base["UnitMagneticField_in_gauss"], "gauss") else: # Sane default magnetic_unit = (1.0, "gauss") magnetic_unit = _fix_unit_ordering(magnetic_unit) self.magnetic_unit = self.quan(*magnetic_unit) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: header_spec = kwargs.get("header_spec", "default") # Check to see if passed filename is a directory. If so, use it to get # the .0 snapshot file. Make sure there's only one such file, otherwise # there's an ambiguity about which file the user wants. Ignore ewah files if os.path.isdir(filename): valid_files = [] for f in os.listdir(filename): fname = os.path.join(filename, f) if (".0" in f) and (".ewah" not in f) and os.path.isfile(fname): valid_files.append(f) if len(valid_files) == 0: return False elif len(valid_files) > 1: return False else: validated_file = os.path.join(filename, valid_files[0]) else: validated_file = filename header = GadgetBinaryHeader(validated_file, header_spec) return header.validate() class GadgetHDF5File(ParticleFile): pass class GadgetHDF5Dataset(GadgetDataset): _load_requirements = ["h5py"] _file_class = GadgetHDF5File _index_class = SPHParticleIndex _field_info_class: type[FieldInfoContainer] = GadgetFieldInfo _particle_mass_name = "Masses" _sph_ptypes = ("PartType0",) _suffix = ".hdf5" def __init__( self, filename, dataset_type="gadget_hdf5", unit_base=None, index_order=None, index_filename=None, kernel_name=None, bounding_box=None, units_override=None, unit_system="cgs", default_species_fields=None, ): self.storage_filename = None filename = os.path.abspath(filename) if units_override is not None: raise RuntimeError( "units_override is not supported for GadgetHDF5Dataset. " "Use unit_base instead." ) super().__init__( filename, dataset_type, unit_base=unit_base, index_order=index_order, index_filename=index_filename, kernel_name=kernel_name, bounding_box=bounding_box, unit_system=unit_system, default_species_fields=default_species_fields, ) def _get_hvals(self): handle = h5py.File(self.parameter_filename, mode="r") hvals = {} hvals.update((str(k), v) for k, v in handle["/Header"].attrs.items()) # Compat reasons. hvals["NumFiles"] = hvals["NumFilesPerSnapshot"] hvals["Massarr"] = hvals["MassTable"] sph_ptypes = [ptype for ptype in self._sph_ptypes if ptype in handle] if sph_ptypes: self.gen_hsmls = "SmoothingLength" not in handle[sph_ptypes[0]] else: self.gen_hsmls = False # Later versions of Gadget and its derivatives have a "Parameters" # group in the HDF5 file. if "Parameters" in handle: hvals.update((str(k), v) for k, v in handle["/Parameters"].attrs.items()) handle.close() # ensure that 1-element arrays are reduced to scalars updated_hvals = {} for hvalname, value in hvals.items(): if isinstance(value, np.ndarray) and value.size == 1: mylog.info("Reducing single-element array %s to scalar.", hvalname) updated_hvals[hvalname] = value.item() hvals.update(updated_hvals) return hvals def _get_uvals(self): handle = h5py.File(self.parameter_filename, mode="r") uvals = {} uvals.update((str(k), v) for k, v in handle["/Units"].attrs.items()) handle.close() return uvals def _set_owls_eagle(self): self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "sph" self._unit_base = self._get_uvals() self._unit_base["cmcm"] = 1.0 / self._unit_base["UnitLength_in_cm"] self.current_redshift = self.parameters["Redshift"] self.omega_lambda = self.parameters["OmegaLambda"] self.omega_matter = self.parameters["Omega0"] self.hubble_constant = self.parameters["HubbleParam"] if self.domain_left_edge is None and self.parameters["BoxSize"] != 0: self.domain_left_edge = np.zeros(3, "float64") self.domain_right_edge = np.ones(3, "float64") * self.parameters["BoxSize"] self.domain_dimensions = np.ones(3, "int32") self.cosmological_simulation = 1 self._periodicity = (True, True, True) prefix = os.path.abspath( os.path.join( os.path.dirname(self.parameter_filename), os.path.basename(self.parameter_filename).split(".", 1)[0], ) ) suffix = self.parameter_filename.rsplit(".", 1)[-1] if self.parameters["NumFiles"] > 1: self.filename_template = f"{prefix}.%(num)i.{suffix}" else: self.filename_template = self.parameter_filename self.file_count = self.parameters["NumFilesPerSnapshot"] def _set_owls_eagle_units(self): # note the contents of the HDF5 Units group are in _unit_base # note the velocity stored on disk is sqrt(a) dx/dt # physical velocity [cm/s] = a dx/dt # = sqrt(a) * velocity_on_disk * UnitVelocity_in_cm_per_s self.length_unit = self.quan(self._unit_base["UnitLength_in_cm"], "cmcm/h") self.mass_unit = self.quan(self._unit_base["UnitMass_in_g"], "g/h") self.velocity_unit = self.quan( self._unit_base["UnitVelocity_in_cm_per_s"], "cm/s * sqrt(a)" ) self.time_unit = self.quan(self._unit_base["UnitTime_in_s"], "s/h") specific_energy_unit_cgs = ( self._unit_base["UnitEnergy_in_cgs"] / self._unit_base["UnitMass_in_g"] ) self.specific_energy_unit = self.quan(specific_energy_unit_cgs, "(cm/s)**2") @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = ["Header"] veto_groups = ["FOF", "Group", "Subhalo"] valid = True try: fh = h5py.File(filename, mode="r") valid = all(ng in fh["/"] for ng in need_groups) and not any( vg in fh["/"] for vg in veto_groups ) fh.close() except Exception: valid = False pass try: fh = h5py.File(filename, mode="r") valid = fh["Header"].attrs["Code"].decode("utf-8") != "SWIFT" fh.close() except (OSError, KeyError): pass return valid yt-project-yt-f043ac8/yt/frontends/gadget/definitions.py000066400000000000000000000063771510711153200234450ustar00rootroot00000000000000gadget_header_specs = { "default": ( ("Npart", 6, "i"), ("Massarr", 6, "d"), ("Time", 1, "d"), ("Redshift", 1, "d"), ("FlagSfr", 1, "i"), ("FlagFeedback", 1, "i"), ("Nall", 6, "i"), ("FlagCooling", 1, "i"), ("NumFiles", 1, "i"), ("BoxSize", 1, "d"), ("Omega0", 1, "d"), ("OmegaLambda", 1, "d"), ("HubbleParam", 1, "d"), ("FlagAge", 1, "i"), ("FlagMetals", 1, "i"), ("NallHW", 6, "i"), ("unused", 16, "i"), ), "pad32": (("empty", 32, "c"),), "pad64": (("empty", 64, "c"),), "pad128": (("empty", 128, "c"),), "pad256": (("empty", 256, "c"),), } gadget_ptype_specs = {"default": ("Gas", "Halo", "Disk", "Bulge", "Stars", "Bndry")} gadget_field_specs = { "default": ( "Coordinates", "Velocities", "ParticleIDs", "Mass", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("SmoothingLength", "Gas"), ), "agora_unlv": ( "Coordinates", "Velocities", "ParticleIDs", "Mass", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("Electron_Number_Density", "Gas"), ("HI_NumberDensity", "Gas"), ("SmoothingLength", "Gas"), ), "group0000": ( "Coordinates", "Velocities", "ParticleIDs", "Mass", "Potential", ("Temperature", "Gas"), ("Density", "Gas"), ("ElectronNumberDensity", "Gas"), ("HI_NumberDensity", "Gas"), ("SmoothingLength", "Gas"), ("StarFormationRate", "Gas"), ("DelayTime", "Gas"), ("FourMetalFractions", ("Gas", "Stars")), ("MaxTemperature", ("Gas", "Stars")), ("NStarsSpawned", ("Gas", "Stars")), ("StellarAge", "Stars"), ), "magneticum_box2_hr": ( "Coordinates", "Velocities", "ParticleIDs", "Mass", ("InternalEnergy", "Gas"), ("Density", "Gas"), ("SmoothingLength", "Gas"), ("ColdFraction", "Gas"), ("Temperature", "Gas"), ("StellarAge", ("Stars", "Bndry")), "Potential", ("InitialMass", "Stars"), ("ElevenMetalMasses", ("Gas", "Stars")), ("StarFormationRate", "Gas"), ("TrueMass", "Bndry"), ("AccretionRate", "Bndry"), ), } gadget_hdf5_ptypes = ( "PartType0", "PartType1", "PartType2", "PartType3", "PartType4", "PartType5", ) SNAP_FORMAT_2_OFFSET = 16 """ Here we have a dictionary of possible element species defined in Gadget datasets, keyed by the number of elements. In some cases, these are mass fractions, in others, they are metals--the context for the dataset will determine this. The "Ej" key is for the total mass of all elements that are not explicitly listed. """ elem_names_opts = { 4: ["C", "O", "Si", "Fe"], 7: ["C", "N", "O", "Mg", "Si", "Fe", "Ej"], 8: ["He", "C", "O", "Mg", "S", "Si", "Fe", "Ej"], 11: ["He", "C", "Ca", "O", "N", "Ne", "Mg", "S", "Si", "Fe", "Ej"], 15: [ "He", "C", "Ca", "O", "N", "Ne", "Mg", "S", "Si", "Fe", "Na", "Al", "Ar", "Ni", "Ej", ], } yt-project-yt-f043ac8/yt/frontends/gadget/fields.py000066400000000000000000000237671510711153200224020ustar00rootroot00000000000000from functools import partial from yt.fields.particle_fields import sph_whitelist_fields from yt.frontends.gadget.definitions import elem_names_opts from yt.frontends.sph.fields import SPHFieldInfo from yt.utilities.periodic_table import periodic_table from yt.utilities.physical_constants import kb, mp from yt.utilities.physical_ratios import _primordial_mass_fraction class GadgetFieldInfo(SPHFieldInfo): def __init__(self, ds, field_list, slice_info=None): if ds.gen_hsmls: hsml = (("smoothing_length", ("code_length", [], None)),) self.known_particle_fields += hsml for field in field_list: if field[1].startswith("MetalMasses"): mm = ((field[1], ("code_mass", [], None)),) self.known_particle_fields += mm super().__init__(ds, field_list, slice_info=slice_info) def setup_particle_fields(self, ptype, *args, **kwargs): # setup some special fields that only make sense for SPH particles if (ptype, "FourMetalFractions") in self.ds.field_list: self.species_names = self._setup_four_metal_fractions(ptype) elif (ptype, "ElevenMetalMasses") in self.ds.field_list: self.species_names = self._setup_metal_masses(ptype, "ElevenMetalMasses") elif (ptype, "MetalMasses_00") in self.ds.field_list: self.species_names = self._setup_metal_masses(ptype, "MetalMasses") if len(self.species_names) == 0: self.species_names = self._check_whitelist_species_fields(ptype) super().setup_particle_fields(ptype, *args, **kwargs) if ptype in ("PartType0", "Gas"): self.setup_gas_particle_fields(ptype) def _setup_four_metal_fractions(self, ptype): """ This function breaks the FourMetalFractions field (if present) into its four component metal fraction fields and adds corresponding metal density fields which will later get smoothed This gets used with the Gadget group0000 format as defined in the gadget_field_specs in frontends/gadget/definitions.py """ metal_names = elem_names_opts[4] def _fraction(field, data, i: int): return data[ptype, "FourMetalFractions"][:, i] def _metal_density(field, data, i: int): return data[ptype, "FourMetalFractions"][:, i] * data[ptype, "density"] for i, metal_name in enumerate(metal_names): # add the metal fraction fields self.add_field( (ptype, metal_name + "_fraction"), sampling_type="particle", function=partial(_fraction, i=i), units="", ) # add the metal density fields self.add_field( (ptype, metal_name + "_density"), sampling_type="particle", function=partial(_metal_density, i=i), units=self.ds.unit_system["density"], ) return metal_names def _make_fraction_functions(self, ptype, fname): if fname == "ElevenMetalMasses": def _fraction(field, data, i: int): return ( data[ptype, "ElevenMetalMasses"][:, i] / data[ptype, "particle_mass"] ) def _metallicity(field, data): ret = ( data[ptype, "ElevenMetalMasses"][:, 1].sum(axis=1) / data[ptype, "particle_mass"] ) return ret def _h_fraction(field, data): ret = ( data[ptype, "ElevenMetalMasses"].sum(axis=1) / data[ptype, "particle_mass"] ) return 1.0 - ret elem_names = elem_names_opts[11] elif fname == "MetalMasses": n_elem = len( [ fd for fd in self.ds.field_list if fd[0] == ptype and fd[1].startswith("MetalMasses") ] ) elem_names = elem_names_opts[n_elem] no_He = "He" not in elem_names def _fraction(field, data, i: int): return ( data[ptype, f"MetalMasses_{i:02d}"] / data[ptype, "particle_mass"] ) def _metallicity(field, data): mass = 0.0 start_idx = int(not no_He) for i in range(start_idx, n_elem): mass += data[ptype, f"MetalMasses_{i:02d}"] ret = mass / data[ptype, "particle_mass"] return ret if no_He: _h_fraction = None else: def _h_fraction(field, data): mass = 0.0 for i in range(n_elem): mass += data[ptype, f"MetalMasses_{i:02d}"] ret = mass / data[ptype, "particle_mass"] return 1.0 - ret else: raise KeyError( f"Making element fraction fields from '{ptype}','{fname}' not possible!" ) return _fraction, _h_fraction, _metallicity, elem_names def _setup_metal_masses(self, ptype, fname): """ This function breaks the ElevenMetalMasses field (if present) into its eleven component metal fraction fields and adds corresponding metal density fields which will later get smoothed This gets used with the magneticum_box2_hr format as defined in the gadget_field_specs in frontends/gadget/definitions.py """ sampling_type = "local" if ptype in self.ds._sph_ptypes else "particle" ( _fraction, _h_fraction, _metallicity, elem_names, ) = self._make_fraction_functions(ptype, fname) def _metal_density(field, data, elem_name: str): return data[ptype, f"{elem_name}_fraction"] * data[ptype, "density"] for i, elem_name in enumerate(elem_names): # add the element fraction fields self.add_field( (ptype, f"{elem_name}_fraction"), sampling_type=sampling_type, function=partial(_fraction, i=i), units="", ) # add the element density fields self.add_field( (ptype, f"{elem_name}_density"), sampling_type=sampling_type, function=partial(_metal_density, elem_name=elem_name), units=self.ds.unit_system["density"], ) # metallicity self.add_field( (ptype, "metallicity"), sampling_type=sampling_type, function=_metallicity, units="", ) if _h_fraction is None: # no helium, so can't compute hydrogen species_names = elem_names[-1] else: # hydrogen fraction and density self.add_field( (ptype, "H_fraction"), sampling_type=sampling_type, function=_h_fraction, units="", ) def _h_density(field, data): return data[ptype, "H_fraction"] * data[ptype, "density"] self.add_field( (ptype, "H_density"), sampling_type=sampling_type, function=_h_density, units=self.ds.unit_system["density"], ) species_names = ["H"] + elem_names[:-1] if "Ej" in elem_names: def _ej_mass(field, data): return data[ptype, "Ej_fraction"] * data[ptype, "particle_mass"] self.add_field( (ptype, "Ej_mass"), sampling_type=sampling_type, function=_ej_mass, units=self.ds.unit_system["mass"], ) if sampling_type == "local": self.alias(("gas", "Ej_mass"), (ptype, "Ej_mass")) return species_names def _check_whitelist_species_fields(self, ptype): species_names = [] for field in self.ds.field_list: if ( field[0] == ptype and field[1].endswith(("_fraction", "_density")) and field[1] in sph_whitelist_fields ): symbol, _, _ = field[1].partition("_") species_names.append(symbol) return sorted(species_names, key=lambda symbol: periodic_table[symbol].num) def setup_gas_particle_fields(self, ptype): if (ptype, "Temperature") not in self.ds.field_list: if (ptype, "ElectronAbundance") in self.ds.field_list: def _temperature(field, data): # Assume cosmic abundances x_H = _primordial_mass_fraction["H"] gamma = 5.0 / 3.0 a_e = data[ptype, "ElectronAbundance"] mu = 4.0 / (3.0 * x_H + 1.0 + 4.0 * x_H * a_e) ret = data[ptype, "InternalEnergy"] * (gamma - 1) * mu * mp / kb return ret.in_units(self.ds.unit_system["temperature"]) else: def _temperature(field, data): gamma = 5.0 / 3.0 ret = ( data[ptype, "InternalEnergy"] * (gamma - 1) * data.ds.mu * mp / kb ) return ret.in_units(self.ds.unit_system["temperature"]) self.add_field( (ptype, "Temperature"), sampling_type="particle", function=_temperature, units=self.ds.unit_system["temperature"], ) self.alias((ptype, "temperature"), (ptype, "Temperature")) # need to do this manually since that automatic aliasing that happens # in the FieldInfoContainer base class has already happened at this # point self.alias(("gas", "temperature"), (ptype, "Temperature")) yt-project-yt-f043ac8/yt/frontends/gadget/io.py000066400000000000000000000553571510711153200215430ustar00rootroot00000000000000import os from collections import defaultdict from functools import cached_property import numpy as np from yt.frontends.sph.io import IOHandlerSPH from yt.units._numpy_wrapper_functions import uconcatenate from yt.utilities.lib.particle_kdtree_tools import generate_smoothing_length from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .definitions import SNAP_FORMAT_2_OFFSET, gadget_hdf5_ptypes class IOHandlerGadgetHDF5(IOHandlerSPH): _dataset_type = "gadget_hdf5" _vector_fields = { "Coordinates": 3, "Velocity": 3, "Velocities": 3, "MagneticField": 3, } _known_ptypes = gadget_hdf5_ptypes _element_names = ( "Hydrogen", "Helium", "Carbon", "Nitrogen", "Oxygen", "Neon", "Magnesium", "Silicon", "Iron", ) _coord_name = "Coordinates" @cached_property def var_mass(self) -> tuple[str, ...]: vm = [] for i, v in enumerate(self.ds["Massarr"]): if v == 0: vm.append(self._known_ptypes[i]) return tuple(vm) def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): for data_file in self._sorted_chunk_iterator(chunks): si, ei = data_file.start, data_file.end f = h5py.File(data_file.filename, mode="r") # This double-reads for ptype in sorted(ptf): if data_file.total_particles[ptype] == 0: continue c = f[f"/{ptype}/{self._coord_name}"][si:ei, :].astype("float64") x, y, z = (np.squeeze(_) for _ in np.split(c, 3, axis=1)) if ptype == self.ds._sph_ptypes[0]: pdtype = c.dtype pshape = c.shape hsml = self._get_smoothing_length(data_file, pdtype, pshape) else: hsml = 0.0 yield ptype, (x, y, z), hsml f.close() def _yield_coordinates(self, data_file, needed_ptype=None): si, ei = data_file.start, data_file.end f = h5py.File(data_file.filename, mode="r") pcount = f["/Header"].attrs["NumPart_ThisFile"][:].astype("int64") np.clip(pcount - si, 0, ei - si, out=pcount) pcount = pcount.sum() for key in f.keys(): if not key.startswith("PartType"): continue if "Coordinates" not in f[key]: continue if needed_ptype and key != needed_ptype: continue ds = f[key]["Coordinates"][si:ei, ...] dt = ds.dtype.newbyteorder("N") # Native pos = np.empty(ds.shape, dtype=dt) pos[:] = ds yield key, pos f.close() def _generate_smoothing_length(self, index): data_files = index.data_files if not self.ds.gen_hsmls: return hsml_fn = data_files[0].filename.replace(".hdf5", ".hsml.hdf5") if os.path.exists(hsml_fn): with h5py.File(hsml_fn, mode="r") as f: file_hash = f.attrs["q"] if file_hash != self.ds._file_hash: mylog.warning("Replacing hsml files.") for data_file in data_files: hfn = data_file.filename.replace(".hdf5", ".hsml.hdf5") os.remove(hfn) else: return positions = [] counts = defaultdict(int) for data_file in data_files: for _, ppos in self._yield_coordinates( data_file, needed_ptype=self.ds._sph_ptypes[0] ): counts[data_file.filename] += ppos.shape[0] positions.append(ppos) if not positions: return offsets = {} offset = 0 for fn, count in counts.items(): offsets[fn] = offset offset += count kdtree = index.kdtree positions = uconcatenate(positions)[kdtree.idx] hsml = generate_smoothing_length( positions.astype("float64"), kdtree, self.ds._num_neighbors ) dtype = positions.dtype hsml = hsml[np.argsort(kdtree.idx)].astype(dtype) mylog.warning("Writing smoothing lengths to hsml files.") for i, data_file in enumerate(data_files): si, ei = data_file.start, data_file.end fn = data_file.filename hsml_fn = data_file.filename.replace(".hdf5", ".hsml.hdf5") with h5py.File(hsml_fn, mode="a") as f: if i == 0: f.attrs["q"] = self.ds._file_hash g = f.require_group(self.ds._sph_ptypes[0]) d = g.require_dataset( "SmoothingLength", dtype=dtype, shape=(counts[fn],) ) begin = si + offsets[fn] end = min(ei, d.size) + offsets[fn] d[si:ei] = hsml[begin:end] def _get_smoothing_length(self, data_file, position_dtype, position_shape): ptype = self.ds._sph_ptypes[0] si, ei = data_file.start, data_file.end if self.ds.gen_hsmls: fn = data_file.filename.replace(".hdf5", ".hsml.hdf5") else: fn = data_file.filename with h5py.File(fn, mode="r") as f: ds = f[ptype]["SmoothingLength"][si:ei, ...] dt = ds.dtype.newbyteorder("N") # Native if position_dtype is not None and dt < position_dtype: # Sometimes positions are stored in double precision # but smoothing lengths are stored in single precision. # In these cases upcast smoothing length to double precision # to avoid ValueErrors when we pass these arrays to Cython. dt = position_dtype hsml = np.empty(ds.shape, dtype=dt) hsml[:] = ds return hsml def _read_particle_data_file(self, data_file, ptf, selector=None): si, ei = data_file.start, data_file.end data_return = {} f = h5py.File(data_file.filename, mode="r") for ptype, field_list in sorted(ptf.items()): if data_file.total_particles[ptype] == 0: continue g = f[f"/{ptype}"] if selector is None or getattr(selector, "is_all_data", False): mask = slice(None, None, None) mask_sum = data_file.total_particles[ptype] hsmls = None else: coords = g["Coordinates"][si:ei].astype("float64") if ptype == "PartType0": hsmls = self._get_smoothing_length( data_file, g["Coordinates"].dtype, g["Coordinates"].shape ).astype("float64") else: hsmls = 0.0 mask = selector.select_points( coords[:, 0], coords[:, 1], coords[:, 2], hsmls ) if mask is not None: mask_sum = mask.sum() del coords if mask is None: continue for field in field_list: if field in ("Mass", "Masses") and ptype not in self.var_mass: data = np.empty(mask_sum, dtype="float64") ind = self._known_ptypes.index(ptype) data[:] = self.ds["Massarr"][ind] elif field in self._element_names: rfield = "ElementAbundance/" + field data = g[rfield][si:ei][mask, ...] elif field.startswith("Metallicity_"): col = int(field.rsplit("_", 1)[-1]) data = g["Metallicity"][si:ei, col][mask] elif field.startswith("GFM_Metals_"): col = int(field.rsplit("_", 1)[-1]) data = g["GFM_Metals"][si:ei, col][mask] elif field.startswith("Chemistry_"): col = int(field.rsplit("_", 1)[-1]) data = g["ChemistryAbundances"][si:ei, col][mask] elif field.startswith("PassiveScalars_"): col = int(field.rsplit("_", 1)[-1]) data = g["PassiveScalars"][si:ei, col][mask] elif field.startswith("GFM_StellarPhotometrics_"): col = int(field.rsplit("_", 1)[-1]) data = g["GFM_StellarPhotometrics"][si:ei, col][mask] elif field.startswith("MetalMasses_"): col = int(field.rsplit("_", 1)[-1]) data = g["Mass of Metals"][si:ei, col][mask] elif field == "smoothing_length": # This is for frontends which do not store # the smoothing length on-disk, so we do not # attempt to read them, but instead assume # that they are calculated in _get_smoothing_length. if hsmls is None: hsmls = self._get_smoothing_length( data_file, g["Coordinates"].dtype, g["Coordinates"].shape, ).astype("float64") data = hsmls[mask] else: data = g[field][si:ei][mask, ...] data_return[ptype, field] = data f.close() return data_return def _count_particles(self, data_file): si, ei = data_file.start, data_file.end f = h5py.File(data_file.filename, mode="r") pcount = f["/Header"].attrs["NumPart_ThisFile"][:].astype("int64") f.close() if None not in (si, ei): np.clip(pcount - si, 0, ei - si, out=pcount) npart = {f"PartType{i}": v for i, v in enumerate(pcount)} return npart def _identify_fields(self, data_file): f = h5py.File(data_file.filename, mode="r") fields = [] cname = self.ds._particle_coordinates_name # Coordinates mname = self.ds._particle_mass_name # Mass # loop over all keys in OWLS hdf5 file # -------------------------------------------------- for key in f.keys(): # only want particle data # -------------------------------------- if not key.startswith("PartType"): continue # particle data group # -------------------------------------- g = f[key] if cname not in g: continue # note str => not unicode! ptype = str(key) if ptype not in self.var_mass: fields.append((ptype, mname)) # loop over all keys in PartTypeX group # ---------------------------------------- for k in g.keys(): if k == "ElementAbundance": gp = g[k] for j in gp.keys(): kk = j fields.append((ptype, str(kk))) elif ( k in ( "Metallicity", "GFM_Metals", "PassiveScalars", "GFM_StellarPhotometrics", "Mass of Metals", ) and len(g[k].shape) > 1 ): # Vector of metallicity or passive scalar for i in range(g[k].shape[1]): key = "MetalMasses" if k == "Mass of Metals" else k fields.append((ptype, "%s_%02i" % (key, i))) elif k == "ChemistryAbundances" and len(g[k].shape) > 1: for i in range(g[k].shape[1]): fields.append((ptype, "Chemistry_%03i" % i)) else: kk = k if not hasattr(g[kk], "shape"): continue if len(g[kk].shape) > 1: self._vector_fields[kk] = g[kk].shape[1] fields.append((ptype, str(kk))) f.close() if self.ds.gen_hsmls: fields.append(("PartType0", "smoothing_length")) return fields, {} ZeroMass = object() class IOHandlerGadgetBinary(IOHandlerSPH): _dataset_type = "gadget_binary" _vector_fields = { "Coordinates": 3, "Velocity": 3, "Velocities": 3, "MagneticField": 3, "FourMetalFractions": 4, "ElevenMetalMasses": 11, } # Particle types (Table 3 in GADGET-2 user guide) # # Blocks in the file: # HEAD # POS # VEL # ID # MASS (variable mass only) # U (gas only) # RHO (gas only) # HSML (gas only) # POT (only if enabled in makefile) # ACCE (only if enabled in makefile) # ENDT (only if enabled in makefile) # TSTP (only if enabled in makefile) _format = None def __init__(self, ds, *args, **kwargs): self._fields = ds._field_spec self._ptypes = ds._ptype_spec self.data_files = set() gformat, endianswap = ds._header.gadget_format # gadget format 1 original, 2 with block name self._format = gformat self._endian = endianswap super().__init__(ds, *args, **kwargs) @cached_property def var_mass(self) -> tuple[str, ...]: vm = [] for i, v in enumerate(self.ds["Massarr"]): if v == 0: vm.append(self._ptypes[i]) return tuple(vm) def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): data_files = set() for chunk in chunks: for obj in chunk.objs: data_files.update(obj.data_files) for data_file in sorted(data_files, key=lambda x: (x.filename, x.start)): poff = data_file.field_offsets tp = data_file.total_particles f = open(data_file.filename, "rb") for ptype in ptf: if tp[ptype] == 0: # skip if there are no particles continue f.seek(poff[ptype, "Coordinates"], os.SEEK_SET) pos = self._read_field_from_file(f, tp[ptype], "Coordinates") if ptype == self.ds._sph_ptypes[0]: f.seek(poff[ptype, "SmoothingLength"], os.SEEK_SET) hsml = self._read_field_from_file(f, tp[ptype], "SmoothingLength") else: hsml = 0.0 yield ptype, (pos[:, 0], pos[:, 1], pos[:, 2]), hsml f.close() def _read_particle_data_file(self, data_file, ptf, selector=None): return_data = {} poff = data_file.field_offsets tp = data_file.total_particles f = open(data_file.filename, "rb") for ptype, field_list in sorted(ptf.items()): if tp[ptype] == 0: continue if selector is None or getattr(selector, "is_all_data", False): mask = slice(None, None, None) else: f.seek(poff[ptype, "Coordinates"], os.SEEK_SET) pos = self._read_field_from_file(f, tp[ptype], "Coordinates") if ptype == self.ds._sph_ptypes[0]: f.seek(poff[ptype, "SmoothingLength"], os.SEEK_SET) hsml = self._read_field_from_file(f, tp[ptype], "SmoothingLength") else: hsml = 0.0 mask = selector.select_points(pos[:, 0], pos[:, 1], pos[:, 2], hsml) del pos del hsml if mask is None: continue for field in field_list: if field == "Mass" and ptype not in self.var_mass: if getattr(selector, "is_all_data", False): size = data_file.total_particles[ptype] else: size = mask.sum() data = np.empty(size, dtype="float64") m = self.ds.parameters["Massarr"][self._ptypes.index(ptype)] data[:] = m else: f.seek(poff[ptype, field], os.SEEK_SET) data = self._read_field_from_file(f, tp[ptype], field) data = data[mask, ...] return_data[ptype, field] = data f.close() return return_data def _read_field_from_file(self, f, count, name): if count == 0: return if name == "ParticleIDs": dt = self._endian + self.ds._id_dtype else: dt = self._endian + self._float_type dt = np.dtype(dt) if name in self._vector_fields: count *= self._vector_fields[name] arr = np.fromfile(f, dtype=dt, count=count) # ensure data are in native endianness to avoid errors # when field data are passed to cython dt = dt.newbyteorder("N") arr = arr.astype(dt) if name in self._vector_fields: factor = self._vector_fields[name] arr = arr.reshape((count // factor, factor), order="C") return arr def _yield_coordinates(self, data_file, needed_ptype=None): self._float_type = data_file.ds._header.float_type self._field_size = np.dtype(self._float_type).itemsize dt = np.dtype(self._endian + self._float_type) dt_native = dt.newbyteorder("N") with open(data_file.filename, "rb") as f: # We add on an additionally 4 for the first record. f.seek(data_file._position_offset + 4) for ptype, count in data_file.total_particles.items(): if count == 0: continue if needed_ptype is not None and ptype != needed_ptype: continue # The first total_particles * 3 values are positions pp = ( np.fromfile(f, dtype=dt, count=count * 3) .reshape(count, 3) .astype(dt_native, copy=False) ) yield ptype, pp def _get_smoothing_length(self, data_file, position_dtype, position_shape): ret = self._get_field(data_file, "SmoothingLength", "Gas") if position_dtype is not None and ret.dtype != position_dtype: # Sometimes positions are stored in double precision # but smoothing lengths are stored in single precision. # In these cases upcast smoothing length to double precision # to avoid ValueErrors when we pass these arrays to Cython. ret = ret.astype(position_dtype) return ret def _get_field(self, data_file, field, ptype): poff = data_file.field_offsets tp = data_file.total_particles with open(data_file.filename, "rb") as f: f.seek(poff[ptype, field], os.SEEK_SET) pp = self._read_field_from_file(f, tp[ptype], field) return pp def _count_particles(self, data_file): si, ei = data_file.start, data_file.end pcount = np.array(data_file.header["Npart"]) if None not in (si, ei): np.clip(pcount - si, 0, ei - si, out=pcount) npart = {self._ptypes[i]: v for i, v in enumerate(pcount)} return npart # header is 256, but we have 4 at beginning and end for ints _field_size = 4 def _calculate_field_offsets( self, field_list, pcount, offset, df_start, file_size=None ): # field_list is (ftype, fname) but the blocks are ordered # (fname, ftype) in the file. if self._format == 2: # Need to subtract offset due to extra header block pos = offset - SNAP_FORMAT_2_OFFSET else: pos = offset fs = self._field_size offsets = {} pcount = dict(zip(self._ptypes, pcount, strict=True)) for field in self._fields: if field == "ParticleIDs" and self.ds.long_ids: fs = 8 else: fs = 4 if not isinstance(field, str): field = field[0] if not any((ptype, field) in field_list for ptype in self._ptypes): continue if self._format == 2: pos += 20 # skip block header elif self._format == 1: pos += 4 else: raise RuntimeError(f"incorrect Gadget format {str(self._format)}!") any_ptypes = False for ptype in self._ptypes: if field == "Mass" and ptype not in self.var_mass: continue if (ptype, field) not in field_list: continue start_offset = df_start * fs if field in self._vector_fields: start_offset *= self._vector_fields[field] pos += start_offset offsets[ptype, field] = pos any_ptypes = True remain_offset = (pcount[ptype] - df_start) * fs if field in self._vector_fields: remain_offset *= self._vector_fields[field] pos += remain_offset pos += 4 if not any_ptypes: pos -= 8 if file_size is not None: if (file_size != pos) & (self._format == 1): # ignore the rest of format 2 diff = file_size - pos possible = [] for ptype, psize in sorted(pcount.items()): if psize == 0: continue if float(diff) / psize == int(float(diff) / psize): possible.append(ptype) mylog.warning( "Your Gadget-2 file may have extra " "columns or different precision! " "(%s diff => %s?)", diff, possible, ) return offsets def _identify_fields(self, domain): # We can just look at the particle counts. field_list = [] tp = domain.total_particles for i, ptype in enumerate(self._ptypes): count = tp[ptype] if count == 0: continue m = domain.header["Massarr"][i] for field in self._fields: if isinstance(field, tuple): field, req = field if req is ZeroMass: if m > 0.0: continue elif isinstance(req, tuple) and ptype in req: pass elif req != ptype: continue field_list.append((ptype, field)) return field_list, {} yt-project-yt-f043ac8/yt/frontends/gadget/simulation_handling.py000066400000000000000000000521271510711153200251540ustar00rootroot00000000000000import glob import os import numpy as np from unyt import dimensions, unyt_array from unyt.unit_registry import UnitRegistry from yt.data_objects.time_series import DatasetSeries, SimulationTimeSeries from yt.funcs import only_on_root from yt.loaders import load from yt.utilities.cosmology import Cosmology from yt.utilities.exceptions import ( InvalidSimulationTimeSeries, MissingParameter, NoStoppingCondition, YTUnidentifiedDataType, ) from yt.utilities.logger import ytLogger as mylog from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_objects class GadgetSimulation(SimulationTimeSeries): r""" Initialize an Gadget Simulation object. Upon creation, the parameter file is parsed and the time and redshift are calculated and stored in all_outputs. A time units dictionary is instantiated to allow for time outputs to be requested with physical time units. The get_time_series can be used to generate a DatasetSeries object. parameter_filename : str The simulation parameter file. find_outputs : bool If True, the OutputDir directory is searched for datasets. Time and redshift information are gathered by temporarily instantiating each dataset. This can be used when simulation data was created in a non-standard way, making it difficult to guess the corresponding time and redshift information. Default: False. Examples -------- >>> import yt >>> gs = yt.load_simulation("my_simulation.par", "Gadget") >>> gs.get_time_series() >>> for ds in gs: ... print(ds.current_time) """ def __init__(self, parameter_filename, find_outputs=False): self.simulation_type = "particle" self.dimensionality = 3 SimulationTimeSeries.__init__( self, parameter_filename, find_outputs=find_outputs ) def _set_units(self): self.unit_registry = UnitRegistry() self.time_unit = self.quan(1.0, "s") if self.cosmological_simulation: # Instantiate Cosmology object for units and time conversions. self.cosmology = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, unit_registry=self.unit_registry, ) if "h" in self.unit_registry: self.unit_registry.modify("h", self.hubble_constant) else: self.unit_registry.add( "h", self.hubble_constant, dimensions.dimensionless ) # Comoving lengths for my_unit in ["m", "pc", "AU"]: new_unit = f"{my_unit}cm" # technically not true, but should be ok self.unit_registry.add( new_unit, self.unit_registry.lut[my_unit][0], dimensions.length, f"\\rm{{{my_unit}}}/(1+z)", prefixable=True, ) self.length_unit = self.quan( self.unit_base["UnitLength_in_cm"], "cmcm / h", registry=self.unit_registry, ) self.mass_unit = self.quan( self.unit_base["UnitMass_in_g"], "g / h", registry=self.unit_registry ) self.box_size = self.box_size * self.length_unit self.domain_left_edge = self.domain_left_edge * self.length_unit self.domain_right_edge = self.domain_right_edge * self.length_unit self.unit_registry.add( "unitary", float(self.box_size.in_base()), self.length_unit.units.dimensions, ) else: # Read time from file for non-cosmological sim self.time_unit = self.quan( self.unit_base["UnitLength_in_cm"] / self.unit_base["UnitVelocity_in_cm_per_s"], "s", ) self.unit_registry.add("code_time", 1.0, dimensions.time) self.unit_registry.modify("code_time", self.time_unit) # Length self.length_unit = self.quan(self.unit_base["UnitLength_in_cm"], "cm") self.unit_registry.add("code_length", 1.0, dimensions.length) self.unit_registry.modify("code_length", self.length_unit) def get_time_series( self, initial_time=None, final_time=None, initial_redshift=None, final_redshift=None, times=None, redshifts=None, tolerance=None, parallel=True, setup_function=None, ): """ Instantiate a DatasetSeries object for a set of outputs. If no additional keywords given, a DatasetSeries object will be created with all potential datasets created by the simulation. Outputs can be gather by specifying a time or redshift range (or combination of time and redshift), with a specific list of times or redshifts), or by simply searching all subdirectories within the simulation directory. initial_time : tuple of type (float, str) The earliest time for outputs to be included. This should be given as the value and the string representation of the units. For example, (5.0, "Gyr"). If None, the initial time of the simulation is used. This can be used in combination with either final_time or final_redshift. Default: None. final_time : tuple of type (float, str) The latest time for outputs to be included. This should be given as the value and the string representation of the units. For example, (13.7, "Gyr"). If None, the final time of the simulation is used. This can be used in combination with either initial_time or initial_redshift. Default: None. times : tuple of type (float array, str) A list of times for which outputs will be found and the units of those values. For example, ([0, 1, 2, 3], "s"). Default: None. initial_redshift : float The earliest redshift for outputs to be included. If None, the initial redshift of the simulation is used. This can be used in combination with either final_time or final_redshift. Default: None. final_redshift : float The latest redshift for outputs to be included. If None, the final redshift of the simulation is used. This can be used in combination with either initial_time or initial_redshift. Default: None. redshifts : array_like A list of redshifts for which outputs will be found. Default: None. tolerance : float Used in combination with "times" or "redshifts" keywords, this is the tolerance within which outputs are accepted given the requested times or redshifts. If None, the nearest output is always taken. Default: None. parallel : bool/int If True, the generated DatasetSeries will divide the work such that a single processor works on each dataset. If an integer is supplied, the work will be divided into that number of jobs. Default: True. setup_function : callable, accepts a ds This function will be called whenever a dataset is loaded. Examples -------- >>> import yt >>> gs = yt.load_simulation("my_simulation.par", "Gadget") >>> gs.get_time_series(initial_redshift=10, final_time=(13.7, "Gyr")) >>> gs.get_time_series(redshifts=[3, 2, 1, 0]) >>> # after calling get_time_series >>> for ds in gs.piter(): ... p = ProjectionPlot(ds, "x", ("gas", "density")) ... p.save() >>> # An example using the setup_function keyword >>> def print_time(ds): ... print(ds.current_time) >>> gs.get_time_series(setup_function=print_time) >>> for ds in gs: ... SlicePlot(ds, "x", "Density").save() """ if ( initial_redshift is not None or final_redshift is not None ) and not self.cosmological_simulation: raise InvalidSimulationTimeSeries( "An initial or final redshift has been given for a " + "noncosmological simulation." ) my_all_outputs = self.all_outputs if not my_all_outputs: DatasetSeries.__init__( self, outputs=[], parallel=parallel, unit_base=self.unit_base ) mylog.info("0 outputs loaded into time series.") return # Apply selection criteria to the set. if times is not None: my_outputs = self._get_outputs_by_key( "time", times, tolerance=tolerance, outputs=my_all_outputs ) elif redshifts is not None: my_outputs = self._get_outputs_by_key( "redshift", redshifts, tolerance=tolerance, outputs=my_all_outputs ) else: if initial_time is not None: if isinstance(initial_time, float): initial_time = self.quan(initial_time, "code_time") elif isinstance(initial_time, tuple) and len(initial_time) == 2: initial_time = self.quan(*initial_time) elif not isinstance(initial_time, unyt_array): raise RuntimeError( "Error: initial_time must be given as a float or " + "tuple of (value, units)." ) elif initial_redshift is not None: my_initial_time = self.cosmology.t_from_z(initial_redshift) else: my_initial_time = self.initial_time if final_time is not None: if isinstance(final_time, float): final_time = self.quan(final_time, "code_time") elif isinstance(final_time, tuple) and len(final_time) == 2: final_time = self.quan(*final_time) elif not isinstance(final_time, unyt_array): raise RuntimeError( "Error: final_time must be given as a float or " + "tuple of (value, units)." ) my_final_time = final_time.in_units("s") elif final_redshift is not None: my_final_time = self.cosmology.t_from_z(final_redshift) else: my_final_time = self.final_time my_initial_time.convert_to_units("s") my_final_time.convert_to_units("s") my_times = np.array([a["time"] for a in my_all_outputs]) my_indices = np.digitize([my_initial_time, my_final_time], my_times) if my_initial_time == my_times[my_indices[0] - 1]: my_indices[0] -= 1 my_outputs = my_all_outputs[my_indices[0] : my_indices[1]] init_outputs = [] for output in my_outputs: if os.path.exists(output["filename"]): init_outputs.append(output["filename"]) if len(init_outputs) == 0 and len(my_outputs) > 0: mylog.warning( "Could not find any datasets. " "Check the value of OutputDir in your parameter file." ) DatasetSeries.__init__( self, outputs=init_outputs, parallel=parallel, setup_function=setup_function, unit_base=self.unit_base, ) mylog.info("%d outputs loaded into time series.", len(init_outputs)) def _parse_parameter_file(self): """ Parses the parameter file and establishes the various dictionaries. """ self.unit_base = {} # Let's read the file lines = open(self.parameter_filename).readlines() comments = ["%", ";"] for line in (l.strip() for l in lines): for comment in comments: if comment in line: line = line[0 : line.find(comment)] if len(line) < 2: continue param, vals = (i.strip() for i in line.split(None, 1)) # First we try to decipher what type of value it is. vals = vals.split() # Special case approaching. if "(do" in vals: vals = vals[:1] if len(vals) == 0: pcast = str # Assume NULL output else: v = vals[0] # Figure out if it's castable to floating point: try: float(v) except ValueError: pcast = str else: if any("." in v or "e" in v for v in vals): pcast = float elif v == "inf": pcast = str else: pcast = int # Now we figure out what to do with it. if param.startswith("Unit"): self.unit_base[param] = float(vals[0]) if len(vals) == 0: vals = "" elif len(vals) == 1: vals = pcast(vals[0]) else: vals = np.array([pcast(i) for i in vals]) self.parameters[param] = vals # Domain dimensions for Gadget datasets are always 2x2x2 for octree self.domain_dimensions = np.array([2, 2, 2]) if self.parameters["ComovingIntegrationOn"]: cosmo_attr = { "box_size": "BoxSize", "omega_lambda": "OmegaLambda", "omega_matter": "Omega0", "hubble_constant": "HubbleParam", } self.initial_redshift = 1.0 / self.parameters["TimeBegin"] - 1.0 self.final_redshift = 1.0 / self.parameters["TimeMax"] - 1.0 self.cosmological_simulation = 1 for a, v in cosmo_attr.items(): if v not in self.parameters: raise MissingParameter(self.parameter_filename, v) setattr(self, a, self.parameters[v]) self.domain_left_edge = np.array([0.0, 0.0, 0.0]) self.domain_right_edge = ( np.array([1.0, 1.0, 1.0]) * self.parameters["BoxSize"] ) else: self.cosmological_simulation = 0 self.omega_lambda = self.omega_matter = self.hubble_constant = 0.0 def _find_data_dir(self): """ Find proper location for datasets. First look where parameter file points, but if this doesn't exist then default to the current directory. """ if self.parameters["OutputDir"].startswith("/"): data_dir = self.parameters["OutputDir"] else: data_dir = os.path.join(self.directory, self.parameters["OutputDir"]) if not os.path.exists(data_dir): mylog.info( "OutputDir not found at %s, instead using %s.", data_dir, self.directory ) data_dir = self.directory self.data_dir = data_dir def _snapshot_format(self, index=None): """ The snapshot filename for a given index. Modify this for different naming conventions. """ if self.parameters["NumFilesPerSnapshot"] > 1: suffix = ".0" else: suffix = "" if self.parameters["SnapFormat"] == 3: suffix += ".hdf5" if index is None: count = "*" else: count = "%03d" % index filename = f"{self.parameters['SnapshotFileBase']}_{count}{suffix}" return os.path.join(self.data_dir, filename) def _get_all_outputs(self, *, find_outputs=False): """ Get all potential datasets and combine into a time-sorted list. """ # Find the data directory where the outputs are self._find_data_dir() # Create the set of outputs from which further selection will be done. if find_outputs: self._find_outputs() else: if self.parameters["OutputListOn"]: a_values = [ float(a) for a in open( os.path.join( self.data_dir, self.parameters["OutputListFilename"] ), ).readlines() ] else: a_values = [float(self.parameters["TimeOfFirstSnapshot"])] time_max = float(self.parameters["TimeMax"]) while a_values[-1] < time_max: if self.cosmological_simulation: a_values.append( a_values[-1] * self.parameters["TimeBetSnapshot"] ) else: a_values.append( a_values[-1] + self.parameters["TimeBetSnapshot"] ) if a_values[-1] > time_max: a_values[-1] = time_max if self.cosmological_simulation: self.all_outputs = [ {"filename": self._snapshot_format(i), "redshift": (1.0 / a - 1)} for i, a in enumerate(a_values) ] # Calculate times for redshift outputs. for output in self.all_outputs: output["time"] = self.cosmology.t_from_z(output["redshift"]) else: self.all_outputs = [ { "filename": self._snapshot_format(i), "time": self.quan(a, "code_time"), } for i, a in enumerate(a_values) ] self.all_outputs.sort(key=lambda obj: obj["time"].to_ndarray()) def _calculate_simulation_bounds(self): """ Figure out the starting and stopping time and redshift for the simulation. """ # Convert initial/final redshifts to times. if self.cosmological_simulation: self.initial_time = self.cosmology.t_from_z(self.initial_redshift) self.initial_time.units.registry = self.unit_registry self.final_time = self.cosmology.t_from_z(self.final_redshift) self.final_time.units.registry = self.unit_registry # If not a cosmology simulation, figure out the stopping criteria. else: if "TimeBegin" in self.parameters: self.initial_time = self.quan(self.parameters["TimeBegin"], "code_time") else: self.initial_time = self.quan(0.0, "code_time") if "TimeMax" in self.parameters: self.final_time = self.quan(self.parameters["TimeMax"], "code_time") else: self.final_time = None if "TimeMax" not in self.parameters: raise NoStoppingCondition(self.parameter_filename) def _find_outputs(self): """ Search for directories matching the data dump keywords. If found, get dataset times py opening the ds. """ potential_outputs = glob.glob(self._snapshot_format()) self.all_outputs = self._check_for_outputs(potential_outputs) self.all_outputs.sort(key=lambda obj: obj["time"]) only_on_root(mylog.info, "Located %d total outputs.", len(self.all_outputs)) # manually set final time and redshift with last output if self.all_outputs: self.final_time = self.all_outputs[-1]["time"] if self.cosmological_simulation: self.final_redshift = self.all_outputs[-1]["redshift"] def _check_for_outputs(self, potential_outputs): r""" Check a list of files to see if they are valid datasets. """ only_on_root( mylog.info, "Checking %d potential outputs.", len(potential_outputs) ) my_outputs = {} for my_storage, output in parallel_objects( potential_outputs, storage=my_outputs ): try: ds = load(output) except (FileNotFoundError, YTUnidentifiedDataType): mylog.error("Failed to load %s", output) continue my_storage.result = { "filename": output, "time": ds.current_time.in_units("s"), } if ds.cosmological_simulation: my_storage.result["redshift"] = ds.current_redshift my_outputs = [ my_output for my_output in my_outputs.values() if my_output is not None ] return my_outputs def _write_cosmology_outputs(self, filename, outputs, start_index, decimals=3): r""" Write cosmology output parameters for a cosmology splice. """ mylog.info("Writing redshift output list to %s.", filename) f = open(filename, "w") for output in outputs: f.write(f"{1.0 / (1.0 + output['redshift']):f}\n") f.close() yt-project-yt-f043ac8/yt/frontends/gadget/testing.py000066400000000000000000000070731510711153200226010ustar00rootroot00000000000000import numpy as np from .data_structures import GadgetBinaryHeader, GadgetDataset from .definitions import gadget_field_specs, gadget_ptype_specs from .io import IOHandlerGadgetBinary vector_fields = dict(IOHandlerGadgetBinary._vector_fields) block_ids = { "Coordinates": "POS", "Velocities": "VEL", "ParticleIDs": "ID", "Mass": "MASS", "InternalEnergy": "U", "Density": "RHO", "SmoothingLength": "HSML", } def write_record(fp, data, endian): dtype = endian + "i4" size = np.array(data.nbytes, dtype=dtype) fp.write(size.tobytes()) fp.write(data.tobytes()) fp.write(size.tobytes()) def write_block(fp, data, endian, fmt, block_id): assert fmt in [1, 2] block_id = "%-4s" % block_id if fmt == 2: block_id_dtype = np.dtype([("id", "S", 4), ("offset", endian + "i4")]) block_id_data = np.zeros(1, dtype=block_id_dtype) block_id_data["id"] = block_id block_id_data["offset"] = data.nbytes + 8 write_record(fp, block_id_data, endian) write_record(fp, data, endian) def fake_gadget_binary( filename="fake_gadget_binary", npart=(100, 100, 100, 0, 100, 0), header_spec="default", field_spec="default", ptype_spec="default", endian="", fmt=2, ): """Generate a fake Gadget binary snapshot.""" header = GadgetBinaryHeader(filename, header_spec) field_spec = GadgetDataset._setup_binary_spec(field_spec, gadget_field_specs) ptype_spec = GadgetDataset._setup_binary_spec(ptype_spec, gadget_ptype_specs) with open(filename, "wb") as fp: # Generate and write header blocks for i_header, header_spec in enumerate(header.spec): specs = [] for name, dim, dtype in header_spec: # workaround a FutureWarning in numpy where np.dtype(name, type, 1) # will change meaning in a future version so name_dtype = [name, endian + dtype, dim] if dim == 1: name_dtype.pop() specs.append(tuple(name_dtype)) header_dtype = np.dtype(specs) header = np.zeros(1, dtype=header_dtype) if i_header == 0: header["Npart"] = npart header["Nall"] = npart header["NumFiles"] = 1 header["BoxSize"] = 1 header["HubbleParam"] = 1 write_block(fp, header, endian, fmt, "HEAD") npart = dict(zip(ptype_spec, npart, strict=True)) for fs in field_spec: # Parse field name and particle type if isinstance(fs, str): field = fs ptype = ptype_spec else: field, ptype = fs if isinstance(ptype, str): ptype = (ptype,) # Determine field dimension if field in vector_fields: dim = vector_fields[field] else: dim = 1 # Determine dtype (in numpy convention) if field == "ParticleIDs": dtype = "u4" else: dtype = "f4" dtype = endian + dtype # Generate and write field block data = [] rng = np.random.default_rng() for pt in ptype: data += [rng.random((npart[pt], dim))] data = np.concatenate(data).astype(dtype) if field in block_ids: block_id = block_ids[field] else: block_id = "" write_block(fp, data, endian, fmt, block_id) return filename yt-project-yt-f043ac8/yt/frontends/gadget/tests/000077500000000000000000000000001510711153200217055ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget/tests/__init__.py000066400000000000000000000000001510711153200240040ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget/tests/test_gadget_pytest.py000066400000000000000000000023731510711153200261660ustar00rootroot00000000000000import numpy as np import yt from yt.testing import requires_file, requires_module from yt.utilities.on_demand_imports import _h5py as h5py @requires_file("snapshot_033/snap_033.0.hdf5") @requires_module("h5py") def test_gadget_header_array_reduction(tmp_path): # first get a real header ds = yt.load("snapshot_033/snap_033.0.hdf5") hvals = ds._get_hvals() hvals_orig = hvals.copy() # wrap some of the scalar values in nested arrays hvals["Redshift"] = np.array([hvals["Redshift"]]) hvals["Omega0"] = np.array([[hvals["Omega0"]]]) # drop those header values into a fake header-only file tmp_snpshot_dir = tmp_path / "snapshot_033" tmp_snpshot_dir.mkdir() tmp_header_only_file = str(tmp_snpshot_dir / "fake_gadget_header.hdf5") with h5py.File(tmp_header_only_file, mode="w") as f: headergrp = f.create_group("Header") for field, val in hvals.items(): headergrp.attrs[field] = val # trick the dataset into using the header file and make sure the # arrays are reduced ds._input_filename = tmp_header_only_file hvals = ds._get_hvals() for attr in ("Redshift", "Omega0"): assert hvals[attr] == hvals_orig[attr] assert isinstance(hvals[attr], np.ndarray) is False yt-project-yt-f043ac8/yt/frontends/gadget/tests/test_outputs.py000066400000000000000000000132111510711153200250370ustar00rootroot00000000000000import os import shutil import tempfile from collections import OrderedDict from itertools import product import yt from yt.frontends.gadget.api import GadgetDataset, GadgetHDF5Dataset from yt.frontends.gadget.testing import fake_gadget_binary from yt.testing import ( ParticleSelectionComparison, assert_allclose_units, requires_file, requires_module, ) from yt.utilities.answer_testing.framework import data_dir_load, requires_ds, sph_answer isothermal_h5 = "IsothermalCollapse/snap_505.hdf5" isothermal_bin = "IsothermalCollapse/snap_505" BE_Gadget = "BigEndianGadgetBinary/BigEndianGadgetBinary" LE_SnapFormat2 = "Gadget3-snap-format2/Gadget3-snap-format2" keplerian_ring = "KeplerianRing/keplerian_ring_0020.hdf5" snap_33 = "snapshot_033/snap_033.0.hdf5" snap_33_dir = "snapshot_033/" magneticum = "MagneticumCluster/snap_132" magneticum_camels = "magneticum_camels/snap_small_086.hdf5" # This maps from field names to weight field names to use for projections iso_fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "velocity_magnitude"), None), ] ) iso_kwargs = {"bounding_box": [[-3, 3], [-3, 3], [-3, 3]]} @requires_module("h5py") def test_gadget_binary(): header_specs = ["default", "default+pad32", ["default", "pad32"]] curdir = os.getcwd() tmpdir = tempfile.mkdtemp() for header_spec, endian, fmt in product(header_specs, "<>", [1, 2]): try: fake_snap = fake_gadget_binary( header_spec=header_spec, endian=endian, fmt=fmt ) except FileNotFoundError: # sometimes this happens for mysterious reasons pass ds = yt.load(fake_snap, header_spec=header_spec) assert isinstance(ds, GadgetDataset) ds.field_list try: os.remove(fake_snap) except FileNotFoundError: # sometimes this happens for mysterious reasons pass os.chdir(curdir) shutil.rmtree(tmpdir) @requires_module("h5py") @requires_file(isothermal_h5) def test_gadget_hdf5(): assert isinstance( data_dir_load(isothermal_h5, kwargs=iso_kwargs), GadgetHDF5Dataset ) @requires_file(keplerian_ring) def test_non_cosmo_dataset(): """ Non-cosmological datasets may not have the cosmological parameters in the Header. The code should fall back gracefully when they are not present, with the Redshift set to 0. """ data = data_dir_load(keplerian_ring) assert data.current_redshift == 0.0 assert data.cosmological_simulation == 0 @requires_ds(isothermal_h5) def test_iso_collapse(): ds = data_dir_load(isothermal_h5, kwargs=iso_kwargs) for test in sph_answer(ds, "snap_505", 2**17, iso_fields): test_iso_collapse.__name__ = test.description yield test @requires_ds(LE_SnapFormat2) def test_pid_uniqueness(): """ ParticleIDs should be unique. """ ds = data_dir_load(LE_SnapFormat2) ad = ds.all_data() pid = ad["all", "ParticleIDs"] assert len(pid) == len(set(pid.v)) @requires_file(snap_33) @requires_file(snap_33_dir) def test_multifile_read(): """ Tests to make sure multi-file gadget snapshot can be loaded by passing '.0' file or by passing the directory containing the multi-file snapshot. """ assert isinstance(data_dir_load(snap_33), GadgetDataset) assert isinstance(data_dir_load(snap_33_dir), GadgetDataset) @requires_file(snap_33) def test_particle_subselection(): # This checks that we correctly subselect from a dataset, first by making # sure we get all the particles, then by comparing manual selections against # them. ds = data_dir_load(snap_33) psc = ParticleSelectionComparison(ds) psc.run_defaults() @requires_ds(BE_Gadget) def test_bigendian_field_access(): ds = data_dir_load(BE_Gadget) data = ds.all_data() data["Halo", "Velocities"] mag_fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "velocity_magnitude"), None), (("gas", "H_fraction"), None), (("gas", "C_fraction"), None), ] ) mag_kwargs = { "long_ids": True, "field_spec": "magneticum_box2_hr", } @requires_ds(magneticum) def test_magneticum(): ds = data_dir_load(magneticum, kwargs=mag_kwargs) for test in sph_answer(ds, "snap_132", 3718111, mag_fields, center="max"): test_magneticum.__name__ = test.description yield test camels_kwargs = { "bounding_box": [[8126.0, 22126.0], [5140.0, 19140.0], [5500.0, 19500.0]] } @requires_module("h5py") @requires_file(magneticum_camels) def test_magneticum_camels(): # In this test, we're only checking the metal fields since this # is a dataset with special metal handling ds = data_dir_load(magneticum_camels, kwargs=camels_kwargs) dd = ds.all_data() elems = [ "He", "C", "Ca", "O", "N", "Ne", "Mg", "S", "Si", "Fe", "Na", "Al", "Ar", "Ni", "Ej", ] metl = 0.0 heavy_mass = 0.0 for i, elem in enumerate(elems): assert_allclose_units( dd["gas", f"{elem}_mass"], dd["PartType0", f"MetalMasses_{i:02d}"] ) heavy_mass += dd["PartType0", f"MetalMasses_{i:02d}"] if i > 0: metl += dd["PartType0", f"MetalMasses_{i:02d}"] / dd["PartType0", "Masses"] assert_allclose_units(dd["gas", "metallicity"], metl) assert_allclose_units(dd["gas", "H_mass"], dd["PartType0", "Masses"] - heavy_mass) yt-project-yt-f043ac8/yt/frontends/gadget_fof/000077500000000000000000000000001510711153200213755ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget_fof/__init__.py000066400000000000000000000000521510711153200235030ustar00rootroot00000000000000""" API for HaloCatalog frontend. """ yt-project-yt-f043ac8/yt/frontends/gadget_fof/api.py000066400000000000000000000005261510711153200225230ustar00rootroot00000000000000from . import tests from .data_structures import ( GadgetFOFDataset, GadgetFOFHaloContainer, GadgetFOFHaloDataset, GadgetFOFHaloParticleIndex, GadgetFOFHDF5File, GadgetFOFParticleIndex, ) from .fields import GadgetFOFFieldInfo, GadgetFOFHaloFieldInfo from .io import IOHandlerGadgetFOFHaloHDF5, IOHandlerGadgetFOFHDF5 yt-project-yt-f043ac8/yt/frontends/gadget_fof/data_structures.py000066400000000000000000000577351510711153200252040ustar00rootroot00000000000000import os import weakref from collections import defaultdict from functools import cached_property, partial import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.data_objects.static_output import ParticleDataset from yt.frontends.gadget.data_structures import _fix_unit_ordering from yt.frontends.gadget_fof.fields import GadgetFOFFieldInfo, GadgetFOFHaloFieldInfo from yt.frontends.halo_catalog.data_structures import HaloCatalogFile, HaloDataset from yt.funcs import only_on_root, setdefaultattr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.cosmology import Cosmology from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py class GadgetFOFParticleIndex(ParticleIndex): def _calculate_particle_count(self): """ Calculate the total number of each type of particle. """ self.particle_count = { ptype: sum(d.total_particles[ptype] for d in self.data_files) for ptype in self.ds.particle_types_raw } def _calculate_particle_index_starts(self): # Halo indices are not saved in the file, so we must count by hand. # File 0 has halos 0 to N_0 - 1, file 1 has halos N_0 to N_0 + N_1 - 1, etc. particle_count = defaultdict(int) offset_count = 0 for data_file in self.data_files: data_file.index_start = { ptype: particle_count[ptype] for ptype in data_file.total_particles } data_file.offset_start = offset_count for ptype in data_file.total_particles: particle_count[ptype] += data_file.total_particles[ptype] offset_count += data_file.total_offset self._halo_index_start = { ptype: np.array( [data_file.index_start[ptype] for data_file in self.data_files] ) for ptype in self.ds.particle_types_raw } def _calculate_file_offset_map(self): # After the FOF is performed, a load-balancing step redistributes halos # and then writes more fields. Here, for each file, we create a list of # files which contain the rest of the redistributed particles. ifof = np.array( [data_file.total_particles["Group"] for data_file in self.data_files] ) isub = np.array([data_file.total_offset for data_file in self.data_files]) subend = isub.cumsum() fofend = ifof.cumsum() istart = np.digitize(fofend - ifof, subend - isub) - 1 iend = np.clip(np.digitize(fofend, subend), 0, ifof.size - 2) for i, data_file in enumerate(self.data_files): data_file.offset_files = self.data_files[istart[i] : iend[i] + 1] def _detect_output_fields(self): field_list = [] units = {} found_fields = { ptype: False for ptype, pnum in self.particle_count.items() if pnum > 0 } for data_file in self.data_files: fl, _units = self.io._identify_fields(data_file) units.update(_units) field_list.extend([f for f in fl if f not in field_list]) for ptype in found_fields: found_fields[ptype] |= data_file.total_particles[ptype] if all(found_fields.values()): break self.field_list = field_list ds = self.dataset ds.particle_types = tuple({pt for pt, ds in field_list}) ds.field_units.update(units) ds.particle_types_raw = ds.particle_types def _setup_filenames(self): template = self.ds.filename_template ndoms = self.ds.file_count cls = self.ds._file_class self.data_files = [ cls(self.ds, self.io, template % {"num": i}, i, frange=None) for i in range(ndoms) ] def _setup_data_io(self): super()._setup_data_io() self._calculate_particle_count() self._calculate_particle_index_starts() self._calculate_file_offset_map() class GadgetFOFHDF5File(HaloCatalogFile): def __init__(self, ds, io, filename, file_id, frange): with h5py.File(filename, mode="r") as f: self.header = {str(field): val for field, val in f["Header"].attrs.items()} self.group_length_sum = ( f["Group/GroupLen"][()].sum() if "Group/GroupLen" in f else 0 ) self.group_subs_sum = ( f["Group/GroupNsubs"][()].sum() if "Group/GroupNsubs" in f else 0 ) self.total_ids = self.header["Nids_ThisFile"] self.total_offset = 0 super().__init__(ds, io, filename, file_id, frange) def _read_particle_positions(self, ptype, f=None): """ Read all particle positions in this file. """ if f is None: close = True f = h5py.File(self.filename, mode="r") else: close = False pos = f[ptype][f"{ptype}Pos"][()].astype("float64") if close: f.close() return pos class GadgetFOFDataset(ParticleDataset): _load_requirements = ["h5py"] _index_class = GadgetFOFParticleIndex _file_class = GadgetFOFHDF5File _field_info_class = GadgetFOFFieldInfo def __init__( self, filename, dataset_type="gadget_fof_hdf5", index_order=None, index_filename=None, unit_base=None, units_override=None, unit_system="cgs", ): if unit_base is not None and "UnitLength_in_cm" in unit_base: # We assume this is comoving, because in the absence of comoving # integration the redshift will be zero. unit_base["cmcm"] = 1.0 / unit_base["UnitLength_in_cm"] self._unit_base = unit_base if units_override is not None: raise RuntimeError( "units_override is not supported for GadgetFOFDataset. " + "Use unit_base instead." ) super().__init__( filename, dataset_type, units_override=units_override, index_order=index_order, index_filename=index_filename, unit_system=unit_system, ) def add_field(self, *args, **kwargs): super().add_field(*args, **kwargs) self._halos_ds.add_field(*args, **kwargs) @property def halos_field_list(self): return self._halos_ds.field_list @property def halos_derived_field_list(self): return self._halos_ds.derived_field_list @cached_property def _halos_ds(self): return GadgetFOFHaloDataset(self) def _setup_classes(self): super()._setup_classes() self.halo = partial(GadgetFOFHaloContainer, ds=self._halos_ds) def _parse_parameter_file(self): with h5py.File(self.parameter_filename, mode="r") as f: self.parameters = { str(field): val for field, val in f["Header"].attrs.items() } self.dimensionality = 3 self.refine_by = 2 # Set standard values self.domain_left_edge = np.zeros(3, "float64") self.domain_right_edge = np.ones(3, "float64") * self.parameters["BoxSize"] self.domain_dimensions = np.ones(3, "int32") self.cosmological_simulation = 1 self._periodicity = (True, True, True) self.current_redshift = self.parameters["Redshift"] self.omega_lambda = self.parameters["OmegaLambda"] self.omega_matter = self.parameters["Omega0"] self.hubble_constant = self.parameters["HubbleParam"] cosmology = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmology.t_from_z(self.current_redshift) prefix = os.path.abspath( os.path.join( os.path.dirname(self.parameter_filename), os.path.basename(self.parameter_filename).split(".", 1)[0], ) ) suffix = self.parameter_filename.rsplit(".", 1)[-1] self.filename_template = f"{prefix}.%(num)i.{suffix}" self.file_count = self.parameters["NumFiles"] self.particle_types = ("Group", "Subhalo") self.particle_types_raw = ("Group", "Subhalo") def _set_code_unit_attributes(self): # Set a sane default for cosmological simulations. if self._unit_base is None and self.cosmological_simulation == 1: only_on_root(mylog.info, "Assuming length units are in Mpc/h (comoving)") self._unit_base = {"length": (1.0, "Mpccm/h")} # The other same defaults we will use from the standard Gadget # defaults. unit_base = self._unit_base or {} if "length" in unit_base: length_unit = unit_base["length"] elif "UnitLength_in_cm" in unit_base: if self.cosmological_simulation == 0: length_unit = (unit_base["UnitLength_in_cm"], "cm") else: length_unit = (unit_base["UnitLength_in_cm"], "cmcm/h") else: raise RuntimeError length_unit = _fix_unit_ordering(length_unit) setdefaultattr(self, "length_unit", self.quan(length_unit[0], length_unit[1])) if "velocity" in unit_base: velocity_unit = unit_base["velocity"] elif "UnitVelocity_in_cm_per_s" in unit_base: velocity_unit = (unit_base["UnitVelocity_in_cm_per_s"], "cm/s") else: if self.cosmological_simulation == 0: velocity_unit = (1e5, "cm/s") else: velocity_unit = (1e5, "cm/s * sqrt(a)") velocity_unit = _fix_unit_ordering(velocity_unit) setdefaultattr( self, "velocity_unit", self.quan(velocity_unit[0], velocity_unit[1]) ) # We set hubble_constant = 1.0 for non-cosmology, so this is safe. # Default to 1e10 Msun/h if mass is not specified. if "mass" in unit_base: mass_unit = unit_base["mass"] elif "UnitMass_in_g" in unit_base: if self.cosmological_simulation == 0: mass_unit = (unit_base["UnitMass_in_g"], "g") else: mass_unit = (unit_base["UnitMass_in_g"], "g/h") else: # Sane default mass_unit = (1.0, "1e10*Msun/h") mass_unit = _fix_unit_ordering(mass_unit) setdefaultattr(self, "mass_unit", self.quan(mass_unit[0], mass_unit[1])) if "time" in unit_base: time_unit = unit_base["time"] elif "UnitTime_in_s" in unit_base: time_unit = (unit_base["UnitTime_in_s"], "s") else: tu = (self.length_unit / self.velocity_unit).to("yr/h") time_unit = (tu.d, tu.units) setdefaultattr(self, "time_unit", self.quan(time_unit[0], time_unit[1])) def __str__(self): return self.basename.split(".", 1)[0] @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = ["Group", "Header", "Subhalo"] veto_groups = ["FOF"] valid = True try: fh = h5py.File(filename, mode="r") valid = all(ng in fh["/"] for ng in need_groups) and not any( vg in fh["/"] for vg in veto_groups ) fh.close() except Exception: valid = False pass return valid class GadgetFOFHaloParticleIndex(GadgetFOFParticleIndex): def __init__(self, ds, dataset_type): self.real_ds = weakref.proxy(ds.real_ds) super().__init__(ds, dataset_type) def _create_halo_id_table(self): """ Create a list of halo start ids so we know which file contains particles for a given halo. Note, the halo ids are distributed over all files and so the ids for a given halo are likely stored in a different file than the halo itself. """ self._halo_id_number = np.array( [data_file.total_ids for data_file in self.data_files] ) self._halo_id_end = self._halo_id_number.cumsum() self._halo_id_start = self._halo_id_end - self._halo_id_number self._group_length_sum = np.array( [data_file.group_length_sum for data_file in self.data_files] ) def _detect_output_fields(self): field_list = [] scalar_field_list = [] units = {} found_fields = { ptype: False for ptype, pnum in self.particle_count.items() if pnum > 0 } has_ids = False for data_file in self.data_files: fl, sl, idl, _units = self.io._identify_fields(data_file) units.update(_units) field_list.extend([f for f in fl if f not in field_list]) scalar_field_list.extend([f for f in sl if f not in scalar_field_list]) for ptype in found_fields: found_fields[ptype] |= data_file.total_particles[ptype] has_ids |= len(idl) > 0 if all(found_fields.values()) and has_ids: break self.field_list = field_list self.scalar_field_list = scalar_field_list ds = self.dataset ds.scalar_field_list = scalar_field_list ds.particle_types = tuple({pt for pt, ds in field_list}) ds.field_units.update(units) ds.particle_types_raw = ds.particle_types def _identify_base_chunk(self, dobj): pass def _read_particle_fields(self, fields, dobj, chunk=None): if len(fields) == 0: return {}, [] fields_to_read, fields_to_generate = self._split_fields(fields) if len(fields_to_read) == 0: return {}, fields_to_generate fields_to_return = self.io._read_particle_selection(dobj, fields_to_read) return fields_to_return, fields_to_generate def _get_halo_file_indices(self, ptype, identifiers): return np.digitize(identifiers, self._halo_index_start[ptype], right=False) - 1 def _get_halo_scalar_index(self, ptype, identifier): i_scalar = self._get_halo_file_indices(ptype, [identifier])[0] scalar_index = identifier - self._halo_index_start[ptype][i_scalar] return scalar_index def _get_halo_values(self, ptype, identifiers, fields, f=None): """ Get field values for halos. IDs are likely to be sequential (or at least monotonic), but not necessarily all within the same file. This does not do much to minimize file i/o, but with halos randomly distributed across files, there's not much more we can do. """ # if a file is already open, don't open it again filename = None if f is None else f.filename data = defaultdict(lambda: np.empty(identifiers.size)) i_scalars = self._get_halo_file_indices(ptype, identifiers) for i_scalar in np.unique(i_scalars): target = i_scalars == i_scalar scalar_indices = identifiers - self._halo_index_start[ptype][i_scalar] # only open file if it's not already open my_f = ( f if self.data_files[i_scalar].filename == filename else h5py.File(self.data_files[i_scalar].filename, mode="r") ) for field in fields: data[field][target] = my_f[os.path.join(ptype, field)][()][ scalar_indices[target] ] if self.data_files[i_scalar].filename != filename: my_f.close() return data def _setup_data_io(self): super()._setup_data_io() self._create_halo_id_table() class GadgetFOFHaloDataset(HaloDataset): _index_class = GadgetFOFHaloParticleIndex _file_class = GadgetFOFHDF5File _field_info_class = GadgetFOFHaloFieldInfo def __init__(self, ds, dataset_type="gadget_fof_halo_hdf5"): super().__init__(ds, dataset_type) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # This class is not meant to be instantiated by yt.load() return False class GadgetFOFHaloContainer(YTSelectionContainer): """ Create a data container to get member particles and individual values from halos and subhalos. Halo mass, position, and velocity are set as attributes. Halo IDs are accessible through the field, "member_ids". Other fields that are one value per halo are accessible as normal. The field list for halo objects can be seen in `ds.halos_field_list`. Parameters ---------- ptype : string The type of halo, either "Group" for the main halo or "Subhalo" for subhalos. particle_identifier : int or tuple of ints The halo or subhalo id. If requesting a subhalo, the id can also be given as a tuple of the main halo id and subgroup id, such as (1, 4) for subgroup 4 of halo 1. Attributes ---------- particle_identifier : int The id of the halo or subhalo. group_identifier : int For subhalos, the id of the enclosing halo. subgroup_identifier : int For subhalos, the relative id of the subhalo within the enclosing halo. particle_number : int Number of particles in the halo. mass : float Halo mass. position : array of floats Halo position. velocity : array of floats Halo velocity. Note ---- Relevant Fields: * particle_number - number of particles * subhalo_number - number of subhalos * group_identifier - id of parent group for subhalos Examples -------- >>> import yt >>> ds = yt.load("gadget_halos/data/groups_298/fof_subhalo_tab_298.0.hdf5") >>> halo = ds.halo("Group", 0) >>> print(halo.mass) 13256.5517578 code_mass >>> print(halo.position) [ 16.18603706 6.95965052 12.52694607] code_length >>> print(halo.velocity) [ 6943694.22793569 -762788.90647454 -794749.63819757] cm/s >>> print(halo["Group_R_Crit200"]) [ 0.79668683] code_length >>> # particle ids for this halo >>> print(halo["member_ids"]) [ 723631. 690744. 854212. ..., 608589. 905551. 1147449.] dimensionless >>> # get the first subhalo of this halo >>> subhalo = ds.halo("Subhalo", (0, 0)) >>> print(subhalo["member_ids"]) [ 723631. 690744. 854212. ..., 808362. 956359. 1248821.] dimensionless """ _type_name = "halo" _con_args = ("ptype", "particle_identifier") _spatial = False # Do not register it to prevent .halo from being attached to all datasets _skip_add = True def __init__(self, ptype, particle_identifier, ds=None): if ptype not in ds.particle_types_raw: raise RuntimeError( f'Possible halo types are {ds.particle_types_raw}, supplied "{ptype}".' ) self.ptype = ptype self._current_particle_type = ptype super().__init__(ds, {}) if ptype == "Subhalo" and isinstance(particle_identifier, tuple): self.group_identifier, self.subgroup_identifier = particle_identifier my_data = self.index._get_halo_values( "Group", np.array([self.group_identifier]), ["GroupFirstSub"] ) self.particle_identifier = np.int64( my_data["GroupFirstSub"][0] + self.subgroup_identifier ) else: self.particle_identifier = particle_identifier if self.particle_identifier >= self.index.particle_count[ptype]: raise RuntimeError( "%s %d requested, but only %d %s objects exist." % (ptype, particle_identifier, self.index.particle_count[ptype], ptype) ) # Find the file that has the scalar values for this halo. i_scalar = self.index._get_halo_file_indices(ptype, [self.particle_identifier])[ 0 ] self.scalar_data_file = self.index.data_files[i_scalar] # index within halo arrays that corresponds to this halo self.scalar_index = self.index._get_halo_scalar_index( ptype, self.particle_identifier ) halo_fields = [f"{ptype}Len"] if ptype == "Subhalo": halo_fields.append("SubhaloGrNr") my_data = self.index._get_halo_values( ptype, np.array([self.particle_identifier]), halo_fields ) self.particle_number = np.int64(my_data[f"{ptype}Len"][0]) if ptype == "Group": self.group_identifier = self.particle_identifier id_offset = 0 # index of file that has scalar values for the group g_scalar = i_scalar group_index = self.scalar_index # If a subhalo, find the index of the parent. elif ptype == "Subhalo": self.group_identifier = np.int64(my_data["SubhaloGrNr"][0]) # Find the file that has the scalar values for the parent group. g_scalar = self.index._get_halo_file_indices( "Group", [self.group_identifier] )[0] # index within halo arrays that corresponds to the paent group group_index = self.index._get_halo_scalar_index( "Group", self.group_identifier ) my_data = self.index._get_halo_values( "Group", np.array([self.group_identifier]), ["GroupNsubs", "GroupFirstSub"], ) self.subgroup_identifier = self.particle_identifier - np.int64( my_data["GroupFirstSub"][0] ) parent_subhalos = my_data["GroupNsubs"][0] mylog.debug( "Subhalo %d is subgroup %s of %d in group %d.", self.particle_identifier, self.subgroup_identifier, parent_subhalos, self.group_identifier, ) # ids of the sibling subhalos that come before this one if self.subgroup_identifier > 0: sub_ids = np.arange( self.particle_identifier - self.subgroup_identifier, self.particle_identifier, ) my_data = self.index._get_halo_values( "Subhalo", sub_ids, ["SubhaloLen"] ) id_offset = my_data["SubhaloLen"].sum(dtype=np.int64) else: id_offset = 0 # Calculate the starting index for the member particles. # First, add up all the particles in the earlier files. all_id_start = self.index._group_length_sum[:g_scalar].sum(dtype=np.int64) # Now add the halos in this file that come before. with h5py.File(self.index.data_files[g_scalar].filename, mode="r") as f: all_id_start += f["Group"]["GroupLen"][:group_index].sum(dtype=np.int64) # Add the subhalo offset. all_id_start += id_offset # indices of first and last files containing member particles i_start = ( np.digitize([all_id_start], self.index._halo_id_start, right=False)[0] - 1 ) i_end = np.digitize( [all_id_start + self.particle_number], self.index._halo_id_end, right=True )[0] self.field_data_files = self.index.data_files[i_start : i_end + 1] # starting and ending indices for each file containing particles self.field_data_start = ( all_id_start - self.index._halo_id_start[i_start : i_end + 1] ).clip(min=0) self.field_data_start = self.field_data_start.astype(np.int64) self.field_data_end = ( all_id_start + self.particle_number - self.index._halo_id_start[i_start : i_end + 1] ).clip(max=self.index._halo_id_number[i_start : i_end + 1]) self.field_data_end = self.field_data_end.astype(np.int64) for attr in ["mass", "position", "velocity"]: setattr(self, attr, self[self.ptype, f"particle_{attr}"][0]) def __repr__(self): return "%s_%s_%09d" % (self.ds, self.ptype, self.particle_identifier) yt-project-yt-f043ac8/yt/frontends/gadget_fof/fields.py000066400000000000000000000076161510711153200232270ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer m_units = "code_mass" p_units = "code_length" v_units = "code_velocity" _pnums = 6 _type_fields: KnownFieldsT = tuple( ("%s%sType_%d" % (ptype, field, pnum), (units, [], None)) for pnum in range(_pnums) for field, units in (("Mass", m_units), ("Len", p_units)) for ptype in ("Group", "Subhalo") ) _sub_type_fields: KnownFieldsT = tuple( ("Subhalo%sType_%d" % (field, pnum), (units, [], None)) for pnum in range(_pnums) for field, units in ( ("HalfmassRad", p_units), ("MassInHalfRad", m_units), ("MassInMaxRad", m_units), ("MassInRad", m_units), ) ) _particle_fields: KnownFieldsT = ( ("GroupPos_0", (p_units, ["Group", "particle_position_x"], None)), ("GroupPos_1", (p_units, ["Group", "particle_position_y"], None)), ("GroupPos_2", (p_units, ["Group", "particle_position_z"], None)), ("GroupVel_0", (v_units, ["Group", "particle_velocity_x"], None)), ("GroupVel_1", (v_units, ["Group", "particle_velocity_y"], None)), ("GroupVel_2", (v_units, ["Group", "particle_velocity_z"], None)), ("GroupMass", (m_units, ["Group", "particle_mass"], None)), ("GroupLen", ("", ["Group", "particle_number"], None)), ("GroupNsubs", ("", ["Group", "subhalo_number"], None)), ("GroupFirstSub", ("", [], None)), ("Group_M_Crit200", (m_units, [], None)), ("Group_M_Crit500", (m_units, [], None)), ("Group_M_Mean200", (m_units, [], None)), ("Group_M_TopHat200", (m_units, [], None)), ("Group_R_Crit200", (p_units, [], None)), ("Group_R_Crit500", (p_units, [], None)), ("Group_R_Mean200", (p_units, [], None)), ("Group_R_TopHat200", (p_units, [], None)), ("SubhaloPos_0", (p_units, ["Subhalo", "particle_position_x"], None)), ("SubhaloPos_1", (p_units, ["Subhalo", "particle_position_y"], None)), ("SubhaloPos_2", (p_units, ["Subhalo", "particle_position_z"], None)), ("SubhaloVel_0", (v_units, ["Subhalo", "particle_velocity_x"], None)), ("SubhaloVel_1", (v_units, ["Subhalo", "particle_velocity_y"], None)), ("SubhaloVel_2", (v_units, ["Subhalo", "particle_velocity_z"], None)), ("SubhaloMass", (m_units, ["Subhalo", "particle_mass"], None)), ("SubhaloLen", ("", ["Subhalo", "particle_number"], None)), ("SubhaloCM_0", (p_units, ["Subhalo", "center_of_mass_x"], None)), ("SubhaloCM_1", (p_units, ["Subhalo", "center_of_mass_y"], None)), ("SubhaloCM_2", (p_units, ["Subhalo", "center_of_mass_z"], None)), ("SubhaloSpin_0", ("", ["Subhalo", "spin_x"], None)), ("SubhaloSpin_1", ("", ["Subhalo", "spin_y"], None)), ("SubhaloSpin_2", ("", ["Subhalo", "spin_z"], None)), ("SubhaloGrNr", ("", ["Subhalo", "group_identifier"], None)), ("SubhaloHalfmassRad", (p_units, [], None)), ("SubhaloIDMostbound", ("", [], None)), ("SubhaloMassInHalfRad", (m_units, [], None)), ("SubhaloMassInMaxRad", (m_units, [], None)), ("SubhaloMassInRad", (m_units, [], None)), ("SubhaloParent", ("", [], None)), ("SubhaloVelDisp", (v_units, ["Subhalo", "velocity_dispersion"], None)), ("SubhaloVmax", (v_units, [], None)), ("SubhaloVmaxRad", (p_units, [], None)), *_type_fields, *_sub_type_fields, ) class GadgetFOFFieldInfo(FieldInfoContainer): known_particle_fields = _particle_fields # these are extra fields to be created for the "all" particle type extra_union_fields = ( (p_units, "particle_position_x"), (p_units, "particle_position_y"), (p_units, "particle_position_z"), (v_units, "particle_velocity_x"), (v_units, "particle_velocity_y"), (v_units, "particle_velocity_z"), (m_units, "particle_mass"), ("", "particle_number"), ("", "particle_ones"), ) class GadgetFOFHaloFieldInfo(FieldInfoContainer): known_particle_fields = _particle_fields + (("ID", ("", ["member_ids"], None)),) yt-project-yt-f043ac8/yt/frontends/gadget_fof/io.py000066400000000000000000000342431510711153200223640ustar00rootroot00000000000000from collections import defaultdict import numpy as np from yt.funcs import mylog from yt.utilities.io_handler import BaseParticleIOHandler from yt.utilities.on_demand_imports import _h5py as h5py class IOHandlerGadgetFOFHDF5(BaseParticleIOHandler): _dataset_type = "gadget_fof_hdf5" def __init__(self, ds): super().__init__(ds) self.offset_fields = set() def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError( "IOHandlerGadgetFOFHDF5 _read_fluid_selection not implemented yet" ) def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. for data_file in self._sorted_chunk_iterator(chunks): with h5py.File(data_file.filename, mode="r") as f: for ptype in sorted(ptf): coords = data_file._get_particle_positions(ptype, f=f) if coords is None: continue x = coords[:, 0] y = coords[:, 1] z = coords[:, 2] yield ptype, (x, y, z), 0.0 def _yield_coordinates(self, data_file): ptypes = self.ds.particle_types_raw with h5py.File(data_file.filename, mode="r") as f: for ptype in sorted(ptypes): pcount = data_file.total_particles[ptype] if pcount == 0: continue coords = f[ptype][f"{ptype}Pos"][()].astype("float64") coords = np.resize(coords, (pcount, 3)) yield ptype, coords def _read_offset_particle_field(self, field, data_file, fh): field_data = np.empty(data_file.total_particles["Group"], dtype="float64") fofindex = ( np.arange(data_file.total_particles["Group"]) + data_file.index_start["Group"] ) for offset_file in data_file.offset_files: if fh.filename == offset_file.filename: ofh = fh else: ofh = h5py.File(offset_file.filename, mode="r") subindex = np.arange(offset_file.total_offset) + offset_file.offset_start substart = max(fofindex[0] - subindex[0], 0) subend = min(fofindex[-1] - subindex[0], subindex.size - 1) fofstart = substart + subindex[0] - fofindex[0] fofend = subend + subindex[0] - fofindex[0] field_data[fofstart : fofend + 1] = ofh["Subhalo"][field][ substart : subend + 1 ] return field_data def _read_particle_fields(self, chunks, ptf, selector): # Now we have all the sizes, and we can allocate for data_file in self._sorted_chunk_iterator(chunks): si, ei = data_file.start, data_file.end with h5py.File(data_file.filename, mode="r") as f: for ptype, field_list in sorted(ptf.items()): pcount = data_file.total_particles[ptype] if pcount == 0: continue coords = data_file._get_particle_positions(ptype, f=f) x = coords[:, 0] y = coords[:, 1] z = coords[:, 2] mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: if field in self.offset_fields: field_data = self._read_offset_particle_field( field, data_file, f ) else: if field == "particle_identifier": field_data = ( np.arange(data_file.total_particles[ptype]) + data_file.index_start[ptype] ) elif field in f[ptype]: field_data = f[ptype][field][()].astype("float64") else: fname = field[: field.rfind("_")] field_data = f[ptype][fname][()].astype("float64") my_div = field_data.size / pcount if my_div > 1: findex = int(field[field.rfind("_") + 1 :]) field_data = field_data[:, findex] data = field_data[si:ei][mask] yield (ptype, field), data def _count_particles(self, data_file): si, ei = data_file.start, data_file.end pcount = { "Group": data_file.header["Ngroups_ThisFile"], "Subhalo": data_file.header["Nsubgroups_ThisFile"], } if None not in (si, ei): for ptype in pcount: pcount[ptype] = np.clip(pcount[ptype] - si, 0, ei - si) return pcount def _identify_fields(self, data_file): fields = [] pcount = data_file.total_particles if sum(pcount.values()) == 0: return fields, {} with h5py.File(data_file.filename, mode="r") as f: for ptype in self.ds.particle_types_raw: if data_file.total_particles[ptype] == 0: continue fields.append((ptype, "particle_identifier")) my_fields, my_offset_fields = subfind_field_list( f[ptype], ptype, data_file.total_particles ) fields.extend(my_fields) self.offset_fields = self.offset_fields.union(set(my_offset_fields)) return fields, {} class IOHandlerGadgetFOFHaloHDF5(IOHandlerGadgetFOFHDF5): _dataset_type = "gadget_fof_halo_hdf5" def _read_particle_coords(self, chunks, ptf): pass def _read_particle_selection(self, dobj, fields): rv = {} ind = {} # We first need a set of masks for each particle type ptf = defaultdict(list) # ON-DISK TO READ fsize = defaultdict(lambda: 0) # COUNT RV field_maps = defaultdict(list) # ptypes -> fields unions = self.ds.particle_unions # What we need is a mapping from particle types to return types for field in fields: ftype, fname = field fsize[field] = 0 # We should add a check for p.fparticle_unions or something here if ftype in unions: for pt in unions[ftype]: ptf[pt].append(fname) field_maps[pt, fname].append(field) else: ptf[ftype].append(fname) field_maps[field].append(field) # Now we allocate psize = {dobj.ptype: dobj.particle_number} for field in fields: if field[0] in unions: for pt in unions[field[0]]: fsize[field] += psize.get(pt, 0) else: fsize[field] += psize.get(field[0], 0) for field in fields: if field[1] in self._vector_fields: shape = (fsize[field], self._vector_fields[field[1]]) elif field[1] in self._array_fields: shape = (fsize[field],) + self._array_fields[field[1]] elif field in self.ds.scalar_field_list: shape = (1,) else: shape = (fsize[field],) rv[field] = np.empty(shape, dtype="float64") ind[field] = 0 # Now we read. for field_r, vals in self._read_particle_fields(dobj, ptf): # Note that we now need to check the mappings for field_f in field_maps[field_r]: my_ind = ind[field_f] rv[field_f][my_ind : my_ind + vals.shape[0], ...] = vals ind[field_f] += vals.shape[0] # Now we need to truncate all our fields, since we allow for # over-estimating. for field_f in ind: rv[field_f] = rv[field_f][: ind[field_f]] return rv def _read_scalar_fields(self, dobj, scalar_fields): all_data = {} if not scalar_fields: return all_data pcount = 1 with h5py.File(dobj.scalar_data_file.filename, mode="r") as f: for ptype, field_list in sorted(scalar_fields.items()): for field in field_list: if field == "particle_identifier": field_data = ( np.arange(dobj.scalar_data_file.total_particles[ptype]) + dobj.scalar_data_file.index_start[ptype] ) elif field in f[ptype]: field_data = f[ptype][field][()].astype("float64") else: fname = field[: field.rfind("_")] field_data = f[ptype][fname][()].astype("float64") my_div = field_data.size / pcount if my_div > 1: findex = int(field[field.rfind("_") + 1 :]) field_data = field_data[:, findex] data = np.array([field_data[dobj.scalar_index]]) all_data[ptype, field] = data return all_data def _read_member_fields(self, dobj, member_fields): all_data = defaultdict(lambda: np.empty(dobj.particle_number, dtype=np.float64)) if not member_fields: return all_data field_start = 0 for i, data_file in enumerate(dobj.field_data_files): start_index = dobj.field_data_start[i] end_index = dobj.field_data_end[i] pcount = end_index - start_index if pcount == 0: continue field_end = field_start + end_index - start_index with h5py.File(data_file.filename, mode="r") as f: for ptype, field_list in sorted(member_fields.items()): for field in field_list: field_data = all_data[ptype, field] if field in f["IDs"]: my_data = f["IDs"][field][start_index:end_index].astype( "float64" ) else: fname = field[: field.rfind("_")] my_data = f["IDs"][fname][start_index:end_index].astype( "float64" ) my_div = my_data.size / pcount if my_div > 1: findex = int(field[field.rfind("_") + 1 :]) my_data = my_data[:, findex] field_data[field_start:field_end] = my_data field_start = field_end return all_data def _read_particle_fields(self, dobj, ptf): # separate member particle fields from scalar fields scalar_fields = defaultdict(list) member_fields = defaultdict(list) for ptype, field_list in sorted(ptf.items()): for field in field_list: if (ptype, field) in self.ds.scalar_field_list: scalar_fields[ptype].append(field) else: member_fields[ptype].append(field) all_data = self._read_scalar_fields(dobj, scalar_fields) all_data.update(self._read_member_fields(dobj, member_fields)) for field, field_data in all_data.items(): yield field, field_data def _identify_fields(self, data_file): fields = [] scalar_fields = [] id_fields = {} with h5py.File(data_file.filename, mode="r") as f: for ptype in self.ds.particle_types_raw: fields.append((ptype, "particle_identifier")) scalar_fields.append((ptype, "particle_identifier")) my_fields, my_offset_fields = subfind_field_list( f[ptype], ptype, data_file.total_particles ) fields.extend(my_fields) scalar_fields.extend(my_fields) if "IDs" not in f: continue id_fields = [(ptype, field) for field in f["IDs"]] fields.extend(id_fields) return fields, scalar_fields, id_fields, {} def subfind_field_list(fh, ptype, pcount): fields = [] offset_fields = [] for field in fh.keys(): if isinstance(fh[field], h5py.Group): my_fields, my_offset_fields = subfind_field_list(fh[field], ptype, pcount) fields.extend(my_fields) my_offset_fields.extend(offset_fields) else: if not fh[field].size % pcount[ptype]: my_div = fh[field].size / pcount[ptype] fname = fh[field].name[fh[field].name.find(ptype) + len(ptype) + 1 :] if my_div > 1: for i in range(int(my_div)): fields.append((ptype, "%s_%d" % (fname, i))) else: fields.append((ptype, fname)) elif ( ptype == "Subhalo" and not fh[field].size % fh["/Subhalo"].attrs["Number_of_groups"] ): # These are actually Group fields, but they were written after # a load balancing step moved halos around and thus they do not # correspond to the halos stored in the Group group. my_div = fh[field].size / fh["/Subhalo"].attrs["Number_of_groups"] fname = fh[field].name[fh[field].name.find(ptype) + len(ptype) + 1 :] if my_div > 1: for i in range(int(my_div)): fields.append(("Group", "%s_%d" % (fname, i))) else: fields.append(("Group", fname)) offset_fields.append(fname) else: mylog.warning( "Cannot add field (%s, %s) with size %d.", ptype, fh[field].name, fh[field].size, ) continue return fields, offset_fields yt-project-yt-f043ac8/yt/frontends/gadget_fof/tests/000077500000000000000000000000001510711153200225375ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget_fof/tests/__init__.py000066400000000000000000000000001510711153200246360ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gadget_fof/tests/test_outputs.py000066400000000000000000000073251510711153200257020ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal, assert_equal from yt.frontends.gadget_fof.api import GadgetFOFDataset from yt.testing import ParticleSelectionComparison, requires_file, requires_module from yt.utilities.answer_testing.framework import ( FieldValuesTest, data_dir_load, requires_ds, ) _fields = ( ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), ("all", "particle_velocity_z"), ("all", "particle_mass"), ("all", "particle_identifier"), ) # a dataset with empty files g5 = "gadget_fof_halos/groups_005/fof_subhalo_tab_005.0.hdf5" g42 = "gadget_fof_halos/groups_042/fof_subhalo_tab_042.0.hdf5" @requires_module("h5py") @requires_ds(g5) def test_fields_g5(): for field in _fields: yield FieldValuesTest(g5, field, particle_type=True) @requires_module("h5py") @requires_ds(g42) def test_fields_g42(): for field in _fields: yield FieldValuesTest(g42, field, particle_type=True) @requires_module("h5py") @requires_file(g42) def test_GadgetFOFDataset(): assert isinstance(data_dir_load(g42), GadgetFOFDataset) # fof/subhalo catalog with member particles g298 = "gadget_halos/data/groups_298/fof_subhalo_tab_298.0.hdf5" @requires_module("h5py") @requires_file(g298) def test_particle_selection(): ds = data_dir_load(g298) psc = ParticleSelectionComparison(ds) psc.run_defaults() @requires_module("h5py") @requires_file(g298) def test_subhalos(): ds = data_dir_load(g298) total_sub = 0 total_int = 0 for hid in range(0, ds.index.particle_count["Group"]): my_h = ds.halo("Group", hid) h_ids = my_h["Group", "ID"] for sid in range(int(my_h["Group", "subhalo_number"][0])): my_s = ds.halo("Subhalo", (my_h.particle_identifier, sid)) total_sub += my_s["Subhalo", "ID"].size total_int += np.intersect1d(h_ids, my_s["Subhalo", "ID"]).size # Test that all subhalo particles are contained within # their parent group. assert_equal(total_sub, total_int) @requires_module("h5py") @requires_file(g298) def test_halo_masses(): ds = data_dir_load(g298) ad = ds.all_data() for ptype in ["Group", "Subhalo"]: nhalos = ds.index.particle_count[ptype] mass = ds.arr(np.zeros(nhalos), "code_mass") for i in range(nhalos): halo = ds.halo(ptype, i) mass[i] = halo.mass # Check that masses from halo containers are the same # as the array of all masses. This will test getting # scalar fields for halos correctly. assert_array_equal(ad[ptype, "particle_mass"], mass) # fof/subhalo catalog with no member ids in first file g56 = "gadget_halos/data/groups_056/fof_subhalo_tab_056.0.hdf5" # This dataset has halos in one file and ids in another, # which can confuse the field detection. @requires_module("h5py") @requires_file(g56) def test_unbalanced_dataset(): ds = data_dir_load(g56) halo = ds.halo("Group", 0) assert_equal(len(halo["Group", "member_ids"]), 33) assert_equal(halo["Group", "member_ids"].min().d, 723254.0) assert_equal(halo["Group", "member_ids"].max().d, 772662.0) # fof/subhalo catalog with no member ids in first file g76 = "gadget_halos/data/groups_076/fof_subhalo_tab_076.0.hdf5" # This dataset has one halo with particles distributed over 3 files # with the 2nd file being empty. @requires_module("h5py") @requires_file(g76) def test_3file_halo(): ds = data_dir_load(g76) # this halo's particles are distributed over 3 files with the # middle file being empty halo = ds.halo("Group", 6) halo["Group", "member_ids"] assert True yt-project-yt-f043ac8/yt/frontends/gamer/000077500000000000000000000000001510711153200204035ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gamer/__init__.py000066400000000000000000000000001510711153200225020ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gamer/api.py000066400000000000000000000002641510711153200215300ustar00rootroot00000000000000from .data_structures import GAMERDataset, GAMERGrid, GAMERHierarchy from .fields import GAMERFieldInfo from .io import IOHandlerGAMER ### NOT SUPPORTED YET # from . import tests yt-project-yt-f043ac8/yt/frontends/gamer/cfields.pyx000066400000000000000000000134361510711153200225650ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS cimport cython cimport libc.math as math cimport numpy as np import numpy as np cdef np.float64_t gamma_eos(np.float64_t kT, np.float64_t g) noexcept nogil: return g cdef np.float64_t gamma_eos_tb(np.float64_t kT, np.float64_t g) noexcept nogil: cdef np.float64_t x, c_p, c_v x = 2.25 * kT / ( math.sqrt(2.25 * kT * kT + 1.0) + 1.0 ) c_p = 2.5 + x c_v = 1.5 + x return c_p / c_v cdef np.float64_t cs_eos_tb(np.float64_t kT, np.float64_t h, np.float64_t g) noexcept nogil: cdef np.float64_t hp, cs2 hp = h + 1.0 cs2 = kT / (3.0 * hp) cs2 *= (5.0 * hp - 8.0 * kT) / (hp - kT) return math.sqrt(cs2) cdef np.float64_t cs_eos(np.float64_t kT, np.float64_t h, np.float64_t g) noexcept nogil: cdef np.float64_t hp, cs2 hp = h + 1.0 cs2 = g / hp * kT return math.sqrt(cs2) ctypedef np.float64_t (*f2_type)(np.float64_t, np.float64_t) noexcept nogil ctypedef np.float64_t (*f3_type)(np.float64_t, np.float64_t, np.float64_t) noexcept nogil cdef class SRHDFields: cdef f2_type gamma cdef f3_type cs cdef np.float64_t _gamma def __init__(self, int eos, np.float64_t gamma): self._gamma = gamma # Select aux functions based on eos no. if eos == 1: self.gamma = gamma_eos self.cs = cs_eos else: self.gamma = gamma_eos_tb self.cs = cs_eos_tb @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def gamma_field(self, temp): cdef np.float64_t[:] kT = temp.ravel() out = np.empty_like(kT) cdef np.float64_t[:] outp = out.ravel() cdef int i for i in range(outp.shape[0]): outp[i] = self.gamma(kT[i], self._gamma) return out cdef np.float64_t _lorentz_factor( self, np.float64_t rho, np.float64_t mx, np.float64_t my, np.float64_t mz, np.float64_t h, ) noexcept nogil: cdef np.float64_t u2 cdef np.float64_t fac fac = (1.0 / (rho * (h + 1.0))) ** 2 u2 = (mx * mx + my * my + mz * mz) * fac return math.sqrt(1.0 + u2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def lorentz_factor(self, dens, momx, momy, momz, enth): cdef np.float64_t[:] rho = dens.ravel() cdef np.float64_t[:] mx = momx.ravel() cdef np.float64_t[:] my = momy.ravel() cdef np.float64_t[:] mz = momz.ravel() cdef np.float64_t[:] h = enth.ravel() out = np.empty_like(dens) cdef np.float64_t[:] outp = out.ravel() cdef int i for i in range(outp.shape[0]): outp[i] = self._lorentz_factor(rho[i], mx[i], my[i], mz[i], h[i]) return out @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def sound_speed(self, temp, enth): cdef np.float64_t[:] kT = temp.ravel() cdef np.float64_t[:] h = enth.ravel() out = np.empty_like(kT) cdef np.float64_t[:] outp = out.ravel() cdef int i for i in range(outp.shape[0]): outp[i] = self.cs(kT[i], h[i], self._gamma) return out cdef np.float64_t _four_vel( self, np.float64_t rho, np.float64_t mi, np.float64_t h, ) noexcept nogil: return mi / (rho * (h + 1.0)) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def four_velocity_xyz(self, dens, mom, enth): cdef np.float64_t[:] rho = dens.ravel() cdef np.float64_t[:] mi = mom.ravel() cdef np.float64_t[:] h = enth.ravel() out = np.empty_like(dens) cdef np.float64_t[:] outp = out.ravel() cdef int i for i in range(outp.shape[0]): outp[i] = self._four_vel(rho[i], mi[i], h[i]) return out @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def kinetic_energy_density(self, dens, momx, momy, momz, temp, enth): cdef np.float64_t[:] rho = dens.ravel() cdef np.float64_t[:] mx = momx.ravel() cdef np.float64_t[:] my = momy.ravel() cdef np.float64_t[:] mz = momz.ravel() cdef np.float64_t[:] kT = temp.ravel() cdef np.float64_t[:] h = enth.ravel() out = np.empty_like(dens) cdef np.float64_t[:] outp = out.ravel() cdef np.float64_t lf, u2, ux, uy, uz cdef int i for i in range(outp.shape[0]): ux = self._four_vel(rho[i], mx[i], h[i]) uy = self._four_vel(rho[i], my[i], h[i]) uz = self._four_vel(rho[i], mz[i], h[i]) u2 = ux**2 + uy**2 + uz**2 lf = self._lorentz_factor(rho[i], mx[i], my[i], mz[i], h[i]) gm1 = u2 / (lf + 1.0) p = rho[i] / lf * kT[i] outp[i] = gm1 * (rho[i] * (h[i] + 1.0) + p) return out @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def mach_number(self, dens, momx, momy, momz, temp, enth): cdef np.float64_t[:] rho = dens.ravel() cdef np.float64_t[:] mx = momx.ravel() cdef np.float64_t[:] my = momy.ravel() cdef np.float64_t[:] mz = momz.ravel() cdef np.float64_t[:] kT = temp.ravel() cdef np.float64_t[:] h = enth.ravel() out = np.empty_like(dens) cdef np.float64_t[:] outp = out.ravel() cdef np.float64_t cs, us, u cdef int i for i in range(outp.shape[0]): cs = self.cs(kT[i], h[i], self._gamma) us = cs / math.sqrt(1.0 - cs**2) u = math.sqrt(mx[i]**2 + my[i]**2 + mz[i]**2) / (rho[i] * (h[i] + 1.0)) outp[i] = u / us return out yt-project-yt-f043ac8/yt/frontends/gamer/data_structures.py000066400000000000000000000343551510711153200242030ustar00rootroot00000000000000import os import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.cosmology import Cosmology from yt.utilities.file_handler import HDF5FileHandler from .definitions import geometry_parameters from .fields import GAMERFieldInfo class GAMERGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = None # do NOT initialize Parent as [] self.Children = [] self.Level = level class GAMERHierarchy(GridIndex): grid = GAMERGrid _preload_implemented = True # since gamer defines "_read_chunk_data" in io.py def __init__(self, ds, dataset_type="gamer"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self._handle = ds._handle self._group_grid = ds._group_grid self._group_particle = ds._group_particle self.float_type = "float64" # fixed even when FLOAT8 is off self._particle_handle = ds._particle_handle self.refine_by = ds.refine_by self.pgroup = self.refine_by**3 # number of patches in a patch group GridIndex.__init__(self, ds, dataset_type) def _detect_output_fields(self): # find all field names in the current dataset # grid fields self.field_list = [("gamer", v) for v in self._group_grid.keys()] # particle fields if self._group_particle is not None: self.field_list += [("io", v) for v in self._group_particle.keys()] def _count_grids(self): # count the total number of patches at all levels self.num_grids = self.dataset.parameters["NPatch"].sum() // self.pgroup def _parse_index(self): parameters = self.dataset.parameters gid0 = 0 grid_corner = self._handle["Tree/Corner"][()][:: self.pgroup] convert2physical = self._handle["Tree/Corner"].attrs["Cvt2Phy"] self.grid_dimensions[:] = parameters["PatchSize"] * self.refine_by for lv in range(0, parameters["NLevel"]): num_grids_level = parameters["NPatch"][lv] // self.pgroup if num_grids_level == 0: break patch_scale = ( parameters["PatchSize"] * parameters["CellScale"][lv] * self.refine_by ) # set the level and edge of each grid # (left/right_edge are YT arrays in code units) self.grid_levels.flat[gid0 : gid0 + num_grids_level] = lv self.grid_left_edge[gid0 : gid0 + num_grids_level] = ( grid_corner[gid0 : gid0 + num_grids_level] * convert2physical ) self.grid_right_edge[gid0 : gid0 + num_grids_level] = ( grid_corner[gid0 : gid0 + num_grids_level] + patch_scale ) * convert2physical gid0 += num_grids_level self.grid_left_edge += self.dataset.domain_left_edge self.grid_right_edge += self.dataset.domain_left_edge # allocate all grid objects self.grids = np.empty(self.num_grids, dtype="object") for i in range(self.num_grids): self.grids[i] = self.grid(i, self, self.grid_levels.flat[i]) # maximum level with patches (which can be lower than MAX_LEVEL) self.max_level = self.grid_levels.max() # number of particles in each grid try: self.grid_particle_count[:] = np.sum( self._handle["Tree/NPar"][()].reshape(-1, self.pgroup), axis=1 )[:, None] except KeyError: self.grid_particle_count[:] = 0.0 # calculate the starting particle indices for each grid (starting from 0) # --> note that the last element must store the total number of particles # (see _read_particle_coords and _read_particle_fields in io.py) self._particle_indices = np.zeros(self.num_grids + 1, dtype="int64") np.add.accumulate( self.grid_particle_count.squeeze(), out=self._particle_indices[1:] ) def _populate_grid_objects(self): son_list = self._handle["Tree/Son"][()] for gid in range(self.num_grids): grid = self.grids[gid] son_gid0 = ( son_list[gid * self.pgroup : (gid + 1) * self.pgroup] // self.pgroup ) # set up the parent-children relationship grid.Children = [self.grids[t] for t in son_gid0[son_gid0 >= 0]] for son_grid in grid.Children: son_grid.Parent = grid # set up other grid attributes grid._prepare_grid() grid._setup_dx() # validate the parent-children relationship in the debug mode if self.dataset._debug: self._validate_parent_children_relationship() # for _debug mode only def _validate_parent_children_relationship(self): mylog.info("Validating the parent-children relationship ...") father_list = self._handle["Tree/Father"][()] for grid in self.grids: # parent->children == itself if grid.Parent is not None: assert grid in grid.Parent.Children, ( f"Grid {grid.id}, Parent {grid.Parent.id}, " f"Parent->Children[0] {grid.Parent.Children[0].id}" ) # children->parent == itself for c in grid.Children: assert c.Parent is grid, ( f"Grid {grid.id}, Children {c.id}, " f"Children->Parent {c.Parent.id}" ) # all refinement grids should have parent if grid.Level > 0: assert grid.Parent is not None and grid.Parent.id >= 0, ( f"Grid {grid.id}, Level {grid.Level}, " f"Parent {grid.Parent.id if grid.Parent is not None else -999}" ) # parent index is consistent with the loaded dataset if grid.Level > 0: father_gid = father_list[grid.id * self.pgroup] // self.pgroup assert father_gid == grid.Parent.id, ( f"Grid {grid.id}, Level {grid.Level}, " f"Parent_Found {grid.Parent.id}, Parent_Expect {father_gid}" ) # edges between children and parent for c in grid.Children: for d in range(0, 3): msgL = ( "Grid %d, Child %d, Grid->EdgeL %14.7e, Children->EdgeL %14.7e" % (grid.id, c.id, grid.LeftEdge[d], c.LeftEdge[d]) ) msgR = ( "Grid %d, Child %d, Grid->EdgeR %14.7e, Children->EdgeR %14.7e" % (grid.id, c.id, grid.RightEdge[d], c.RightEdge[d]) ) if not grid.LeftEdge[d] <= c.LeftEdge[d]: raise ValueError(msgL) if not grid.RightEdge[d] >= c.RightEdge[d]: raise ValueError(msgR) mylog.info("Check passed") class GAMERDataset(Dataset): _load_requirements = ["h5py"] _index_class = GAMERHierarchy _field_info_class = GAMERFieldInfo _handle = None _group_grid = None _group_particle = None _debug = False # debug mode for the GAMER frontend def __init__( self, filename, dataset_type="gamer", storage_filename=None, particle_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): if self._handle is not None: return self.fluid_types += ("gamer",) self._handle = HDF5FileHandler(filename) self.particle_filename = particle_filename # to catch both the new and old data formats for the grid data try: self._group_grid = self._handle["GridData"] except KeyError: self._group_grid = self._handle["Data"] if "Particle" in self._handle: self._group_particle = self._handle["Particle"] if self.particle_filename is None: self._particle_handle = self._handle else: self._particle_handle = HDF5FileHandler(self.particle_filename) # currently GAMER only supports refinement by a factor of 2 self.refine_by = 2 Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename def _set_code_unit_attributes(self): if self.opt_unit: # GAMER units are always in CGS setdefaultattr( self, "length_unit", self.quan(self.parameters["Unit_L"], "cm") ) setdefaultattr(self, "mass_unit", self.quan(self.parameters["Unit_M"], "g")) setdefaultattr(self, "time_unit", self.quan(self.parameters["Unit_T"], "s")) if self.mhd: setdefaultattr( self, "magnetic_unit", self.quan(self.parameters["Unit_B"], "gauss") ) else: if len(self.units_override) == 0: mylog.warning( "Cannot determine code units ==> " "Use units_override to specify the units" ) for unit, value, cgs in [ ("length", 1.0, "cm"), ("time", 1.0, "s"), ("mass", 1.0, "g"), ("magnetic", np.sqrt(4.0 * np.pi), "gauss"), ]: setdefaultattr(self, f"{unit}_unit", self.quan(value, cgs)) if len(self.units_override) == 0: mylog.warning("Assuming %8s unit = %f %s", unit, value, cgs) def _parse_parameter_file(self): # code-specific parameters for t in self._handle["Info"]: info_category = self._handle["Info"][t] for v in info_category.dtype.names: self.parameters[v] = info_category[v] # shortcut for self.parameters parameters = self.parameters # reset 'Model' to be more readable # (no longer regard MHD as a separate model) if parameters["Model"] == 1: parameters["Model"] = "Hydro" elif parameters["Model"] == 3: parameters["Model"] = "ELBDM" else: parameters["Model"] = "Unknown" # simulation time and domain self.dimensionality = 3 # always 3D self.domain_left_edge = parameters.get( "BoxEdgeL", np.array([0.0, 0.0, 0.0]) ).astype("f8") self.domain_right_edge = parameters.get( "BoxEdgeR", parameters["BoxSize"] ).astype("f8") self.domain_dimensions = parameters["NX0"].astype("int64") # periodicity if parameters["FormatVersion"] >= 2106: periodic_bc = 1 else: periodic_bc = 0 self._periodicity = ( bool(parameters["Opt__BC_Flu"][0] == periodic_bc), bool(parameters["Opt__BC_Flu"][2] == periodic_bc), bool(parameters["Opt__BC_Flu"][4] == periodic_bc), ) # cosmological parameters if parameters["Comoving"]: self.cosmological_simulation = 1 # here parameters["Time"][0] is the scale factor a at certain redshift self.current_redshift = 1.0 / parameters["Time"][0] - 1.0 self.omega_matter = parameters["OmegaM0"] self.omega_lambda = 1.0 - self.omega_matter # default to 0.7 for old data format self.hubble_constant = parameters.get("Hubble0", 0.7) # use the cosmological age computed by the given cosmological parameters as the current time when COMOVING is on; cosmological age is computed by subtracting the lookback time at self.current_redshift from that at z = 1e6 (i.e., very early universe) cosmo = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmo.lookback_time(self.current_redshift, 1e6) else: self.cosmological_simulation = 0 self.current_redshift = 0.0 self.omega_matter = 0.0 self.omega_lambda = 0.0 self.hubble_constant = 0.0 # use parameters["Time"][0] as current time when COMOVING is off self.current_time = parameters["Time"][0] # make aliases to some frequently used variables if parameters["Model"] == "Hydro": self.gamma = parameters["Gamma"] self.gamma_cr = self.parameters.get("CR_Gamma", None) self.eos = parameters.get("EoS", 1) # Assume gamma-law by default # default to 0.6 for old data format self.mu = parameters.get( "MolecularWeight", 0.6 ) # Assume ionized primordial by default self.mhd = parameters.get("Magnetohydrodynamics", 0) self.srhd = parameters.get("SRHydrodynamics", 0) else: self.mhd = 0 self.srhd = 0 # set dummy value of mu here to avoid more complicated workarounds later self.mu = 1.0 # old data format (version < 2210) did not contain any information of code units self.opt_unit = self.parameters.get("Opt__Unit", 0) self.geometry = Geometry(geometry_parameters[parameters.get("Coordinate", 1)]) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: # define a unique way to identify GAMER datasets f = HDF5FileHandler(filename) if "Info" in f["/"].keys() and "KeyInfo" in f["/Info"].keys(): return True except Exception: pass return False yt-project-yt-f043ac8/yt/frontends/gamer/definitions.py000066400000000000000000000002101510711153200232610ustar00rootroot00000000000000geometry_parameters = { 1: "cartesian", 2: ("cylindrical", ("r", "theta", "z")), 3: ("spherical", ("r", "theta", "phi")), } yt-project-yt-f043ac8/yt/frontends/gamer/fields.py000066400000000000000000000402121510711153200222220ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.fields.tensor_fields import setup_stress_energy_ideal from yt.funcs import mylog from .cfields import SRHDFields b_units = "code_magnetic" pre_units = "code_mass / (code_length*code_time**2)" erg_units = "code_mass / (code_length*code_time**2)" rho_units = "code_mass / code_length**3" mom_units = "code_mass / (code_length**2*code_time)" vel_units = "code_velocity" pot_units = "code_length**2/code_time**2" psi_units = "code_mass**(1/2) / code_length**(3/2)" class GAMERFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( # hydro fields on disk (GAMER outputs conservative variables) ("Dens", (rho_units, [], None)), ("MomX", (mom_units, ["momentum_density_x"], None)), ("MomY", (mom_units, ["momentum_density_y"], None)), ("MomZ", (mom_units, ["momentum_density_z"], None)), ("Engy", (erg_units, [], None)), ("CRay", (erg_units, ["cosmic_ray_energy_density"], None)), ("Pote", (pot_units, ["gravitational_potential"], None)), ("Pres", (pre_units, ["pressure"], None)), ("Temp", ("code_temperature", ["temperature"], None)), ("Enth", (pot_units, ["specific_reduced_enthalpy"], None)), ("Mach", ("dimensionless", ["mach_number"], None)), ("Cs", (vel_units, ["sound_speed"], None)), ("DivVel", ("1/code_time", ["velocity_divergence"], None)), # MHD fields on disk (CC=cell-centered) ("CCMagX", (b_units, [], "B_x")), ("CCMagY", (b_units, [], "B_y")), ("CCMagZ", (b_units, [], "B_z")), # psiDM fields on disk ("Real", (psi_units, ["psidm_real_part"], None)), ("Imag", (psi_units, ["psidm_imaginary_part"], None)), # particle fields on disk (deposited onto grids) ("ParDens", (rho_units, ["particle_density_on_grid"], None)), ("TotalDens", (rho_units, ["total_density_on_grid"], None)), ) known_particle_fields: KnownFieldsT = ( ("ParMass", ("code_mass", ["particle_mass"], None)), ("ParPosX", ("code_length", ["particle_position_x"], None)), ("ParPosY", ("code_length", ["particle_position_y"], None)), ("ParPosZ", ("code_length", ["particle_position_z"], None)), ("ParVelX", ("code_velocity", ["particle_velocity_x"], None)), ("ParVelY", ("code_velocity", ["particle_velocity_y"], None)), ("ParVelZ", ("code_velocity", ["particle_velocity_z"], None)), ("ParCreTime", ("code_time", ["particle_creation_time"], None)), ) def __init__(self, ds, field_list): super().__init__(ds, field_list) # add primitive and other derived variables def setup_fluid_fields(self): pc = self.ds.units.physical_constants from yt.fields.magnetic_field import setup_magnetic_field_aliases unit_system = self.ds.unit_system unit_system.registry = self.ds.unit_registry # TODO: Why do I need this?! if self.ds.opt_unit: temp_conv = pc.kb / (self.ds.mu * pc.mh) else: temp_conv = ( self.ds.arr(1.0, "code_velocity**2/code_temperature") / self.ds.mu ) if self.ds.srhd: if self.ds.opt_unit: c2 = pc.clight * pc.clight else: c2 = self.ds.arr(1.0, "code_velocity**2") invc2 = 1.0 / c2 if ("gamer", "Temp") not in self.field_list: mylog.warning( 'The temperature field "Temp" is not present in the dataset. Most ' 'SRHD fields will not be available!! Please set "OPT__OUTPUT_TEMP ' '= 1" in Input__Parameter and re-run the simulation.' ) if ("gamer", "Enth") not in self.field_list: mylog.warning( 'The reduced enthalpy field "Enth" is not present in the dataset. ' "Most SRHD fields will not be available!! Please set " '"OPT__OUTPUT_ENTHALPY = 1" in Input__Parameter and re-run the ' "simulation." ) # EOS functions gamma = self.ds.gamma if self.ds.eos == 1 else 0.0 fgen = SRHDFields(self.ds.eos, gamma) # temperature fraction (kT/mc^2) def _temp_fraction(field, data): return data["gamer", "Temp"] * temp_conv * invc2 self.add_field( ("gas", "temp_fraction"), function=_temp_fraction, sampling_type="cell", units="", ) # specific enthalpy def _specific_enthalpy(field, data): return data["gas", "specific_reduced_enthalpy"] + c2 self.add_field( ("gas", "specific_enthalpy"), function=_specific_enthalpy, sampling_type="cell", units=unit_system["specific_energy"], ) # sound speed if ("gamer", "Cs") not in self.field_list: def _sound_speed(field, data): out = fgen.sound_speed( data["gas", "temp_fraction"].d, data["gamer", "Enth"].d, ) return data.ds.arr(out, "code_velocity").to(unit_system["velocity"]) self.add_field( ("gas", "sound_speed"), sampling_type="cell", function=_sound_speed, units=unit_system["velocity"], ) # ratio of specific heats (gamma) def _gamma(field, data): out = fgen.gamma_field(data["gas", "temp_fraction"].d) return data.ds.arr(out, "dimensionless") self.add_field( ("gas", "gamma"), sampling_type="cell", function=_gamma, units="" ) # reduced total energy density self.alias( ("gas", "reduced_total_energy_density"), ("gamer", "Engy"), units=unit_system["pressure"], ) # total energy density def _total_energy_density(field, data): return data["gamer", "Engy"] + data["gamer", "Dens"] * c2 self.add_field( ("gas", "total_energy_density"), sampling_type="cell", function=_total_energy_density, units=unit_system["pressure"], ) # coordinate frame density self.alias( ("gas", "frame_density"), ("gamer", "Dens"), units=unit_system["density"], ) # 4-velocity spatial components def four_velocity_xyz(u): def _four_velocity(field, data): out = fgen.four_velocity_xyz( data["gamer", "Dens"].d, data["gamer", f"Mom{u.upper()}"].d, data["gamer", "Enth"].d, ) return data.ds.arr(out, "code_velocity").to(unit_system["velocity"]) return _four_velocity for u in "xyz": self.add_field( ("gas", f"four_velocity_{u}"), sampling_type="cell", function=four_velocity_xyz(u), units=unit_system["velocity"], ) # lorentz factor if ("gamer", "Lrtz") in self.field_list: def _lorentz_factor(field, data): return data["gamer", "Lrtz"] else: def _lorentz_factor(field, data): out = fgen.lorentz_factor( data["gamer", "Dens"].d, data["gamer", "MomX"].d, data["gamer", "MomY"].d, data["gamer", "MomZ"].d, data["gamer", "Enth"].d, ) return data.ds.arr(out, "dimensionless") self.add_field( ("gas", "lorentz_factor"), sampling_type="cell", function=_lorentz_factor, units="", ) # density def _density(field, data): return data["gamer", "Dens"] / data["gas", "lorentz_factor"] self.add_field( ("gas", "density"), sampling_type="cell", function=_density, units=unit_system["density"], ) # pressure def _pressure(field, data): p = data["gas", "density"] * data["gas", "temp_fraction"] return p * c2 # thermal energy per mass (i.e., specific) def _specific_thermal_energy(field, data): return ( data["gas", "specific_reduced_enthalpy"] - c2 * data["gas", "temp_fraction"] ) # total energy per mass def _specific_total_energy(field, data): return data["gas", "total_energy_density"] / data["gas", "density"] # kinetic energy density def _kinetic_energy_density(field, data): out = fgen.kinetic_energy_density( data["gamer", "Dens"].d, data["gamer", "MomX"].d, data["gamer", "MomY"].d, data["gamer", "MomZ"].d, data["gas", "temp_fraction"].d, data["gamer", "Enth"].d, ) return data.ds.arr(out, erg_units).to(unit_system["pressure"]) self.add_field( ("gas", "kinetic_energy_density"), sampling_type="cell", function=_kinetic_energy_density, units=unit_system["pressure"], ) # Mach number if ("gamer", "Mach") not in self.field_list: def _mach_number(field, data): out = fgen.mach_number( data["gamer", "Dens"].d, data["gamer", "MomX"].d, data["gamer", "MomY"].d, data["gamer", "MomZ"].d, data["gas", "temp_fraction"].d, data["gamer", "Enth"].d, ) return data.ds.arr(out, "dimensionless") self.add_field( ("gas", "mach_number"), sampling_type="cell", function=_mach_number, units="", ) setup_stress_energy_ideal(self) else: # not RHD # density self.alias( ("gas", "density"), ("gamer", "Dens"), units=unit_system["density"] ) self.alias( ("gas", "total_energy_density"), ("gamer", "Engy"), units=unit_system["pressure"], ) # ==================================================== # note that yt internal fields assume # [specific_thermal_energy] = [energy per mass] # [kinetic_energy_density] = [energy per volume] # [magnetic_energy_density] = [energy per volume] # and we further adopt # [specific_total_energy] = [energy per mass] # [total_energy_density] = [energy per volume] # ==================================================== # thermal energy per volume def et(data): ek = ( 0.5 * ( data["gamer", "MomX"] ** 2 + data["gamer", "MomY"] ** 2 + data["gamer", "MomZ"] ** 2 ) / data["gamer", "Dens"] ) Et = data["gamer", "Engy"] - ek if self.ds.mhd: # magnetic_energy is a yt internal field Et -= data["gas", "magnetic_energy_density"] if getattr(self.ds, "gamma_cr", None): # cosmic rays are included in this dataset Et -= data["gas", "cosmic_ray_energy_density"] return Et # thermal energy per mass (i.e., specific) def _specific_thermal_energy(field, data): return et(data) / data["gamer", "Dens"] # total energy per mass def _specific_total_energy(field, data): return data["gamer", "Engy"] / data["gamer", "Dens"] # pressure def _pressure(field, data): return et(data) * (data.ds.gamma - 1.0) # velocity def velocity_xyz(v): if ("gamer", f"Vel{v.upper()}") in self.field_list: def _velocity(field, data): return data.ds.arr( data["gamer", f"Vel{v.upper()}"].d, "code_velocity" ).to(unit_system["velocity"]) elif self.ds.srhd: def _velocity(field, data): return ( data["gas", f"four_velocity_{v}"] / data["gas", "lorentz_factor"] ) else: def _velocity(field, data): return data["gas", f"momentum_density_{v}"] / data["gas", "density"] return _velocity for v in "xyz": self.add_field( ("gas", f"velocity_{v}"), sampling_type="cell", function=velocity_xyz(v), units=unit_system["velocity"], ) if ("gamer", "Pres") not in self.field_list: self.add_field( ("gas", "pressure"), sampling_type="cell", function=_pressure, units=unit_system["pressure"], ) self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) def _thermal_energy_density(field, data): return data["gas", "density"] * data["gas", "specific_thermal_energy"] self.add_field( ("gas", "thermal_energy_density"), sampling_type="cell", function=_thermal_energy_density, units=unit_system["pressure"], ) self.add_field( ("gas", "specific_total_energy"), sampling_type="cell", function=_specific_total_energy, units=unit_system["specific_energy"], ) if getattr(self.ds, "gamma_cr", None): def _cr_pressure(field, data): return (data.ds.gamma_cr - 1.0) * data[ "gas", "cosmic_ray_energy_density" ] self.add_field( ("gas", "cosmic_ray_pressure"), _cr_pressure, sampling_type="cell", units=self.ds.unit_system["pressure"], ) # mean molecular weight if hasattr(self.ds, "mu"): def _mu(field, data): return data.ds.mu * data["index", "ones"] self.add_field( ("gas", "mean_molecular_weight"), sampling_type="cell", function=_mu, units="", ) # temperature if ("gamer", "Temp") not in self.field_list: def _temperature(field, data): return data["gas", "pressure"] / (data["gas", "density"] * temp_conv) self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) # magnetic field aliases --> magnetic_field_x/y/z if self.ds.mhd: setup_magnetic_field_aliases(self, "gamer", [f"CCMag{v}" for v in "XYZ"]) def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) yt-project-yt-f043ac8/yt/frontends/gamer/io.py000066400000000000000000000167171510711153200214000ustar00rootroot00000000000000from itertools import groupby import numpy as np from yt.geometry.selection_routines import AlwaysSelector from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog # ----------------------------------------------------------------------------- # GAMER shares a similar HDF5 format, and thus io.py as well, with FLASH # ----------------------------------------------------------------------------- # group grids with consecutive indices together to improve the I/O performance # --> grids are assumed to be sorted into ascending numerical order already def grid_sequences(grids): for _k, g in groupby(enumerate(grids), lambda i_x: i_x[0] - i_x[1].id): seq = [v[1] for v in g] yield seq def particle_sequences(grids): for _k, g in groupby(enumerate(grids), lambda i_x: i_x[0] - i_x[1].id): seq = [v[1] for v in g] yield seq[0], seq[-1] class IOHandlerGAMER(BaseIOHandler): _particle_reader = False _dataset_type = "gamer" def __init__(self, ds): super().__init__(ds) self._handle = ds._handle self._group_grid = ds._group_grid self._group_particle = ds._group_particle self._field_dtype = "float64" # fixed even when FLOAT8 is off self._particle_handle = ds._particle_handle self.patch_size = ds.parameters["PatchSize"] * ds.refine_by self.pgroup = ds.refine_by**3 # number of patches in a patch group def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) # generator --> list p_idx = self.ds.index._particle_indices # shortcuts par_posx = self._group_particle["ParPosX"] par_posy = self._group_particle["ParPosY"] par_posz = self._group_particle["ParPosZ"] # currently GAMER does not support multiple particle types assert len(ptf) == 1 ptype = list(ptf.keys())[0] for chunk in chunks: for g1, g2 in particle_sequences(chunk.objs): start = p_idx[g1.id] end = p_idx[g2.id + 1] x = np.asarray(par_posx[start:end], dtype=self._field_dtype) y = np.asarray(par_posy[start:end], dtype=self._field_dtype) z = np.asarray(par_posz[start:end], dtype=self._field_dtype) yield ptype, (x, y, z), 0.0 def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) # generator --> list p_idx = self.ds.index._particle_indices # shortcuts par_posx = self._group_particle["ParPosX"] par_posy = self._group_particle["ParPosY"] par_posz = self._group_particle["ParPosZ"] # currently GAMER does not support multiple particle types assert len(ptf) == 1 ptype = list(ptf.keys())[0] pfields = ptf[ptype] for chunk in chunks: for g1, g2 in particle_sequences(chunk.objs): start = p_idx[g1.id] end = p_idx[g2.id + 1] x = np.asarray(par_posx[start:end], dtype=self._field_dtype) y = np.asarray(par_posy[start:end], dtype=self._field_dtype) z = np.asarray(par_posz[start:end], dtype=self._field_dtype) mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in pfields: data = self._group_particle[field][start:end] yield (ptype, field), data[mask] def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) # generator --> list if any((ftype != "gamer" for ftype, fname in fields)): raise NotImplementedError rv = {} for field in fields: rv[field] = np.empty(size, dtype=self._field_dtype) ng = sum(len(c.objs) for c in chunks) # c.objs is a list of grids mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) # shortcuts ps2 = self.patch_size ps1 = ps2 // 2 for field in fields: ds = self._group_grid[field[1]] offset = 0 for chunk in chunks: for gs in grid_sequences(chunk.objs): start = (gs[0].id) * self.pgroup end = (gs[-1].id + 1) * self.pgroup buf = ds[start:end, :, :, :] ngrid = len(gs) data = np.empty((ngrid, ps2, ps2, ps2), dtype=self._field_dtype) for g in range(ngrid): pid0 = g * self.pgroup data[g, 0:ps1, 0:ps1, 0:ps1] = buf[pid0 + 0, :, :, :] data[g, 0:ps1, 0:ps1, ps1:ps2] = buf[pid0 + 1, :, :, :] data[g, 0:ps1, ps1:ps2, 0:ps1] = buf[pid0 + 2, :, :, :] data[g, ps1:ps2, 0:ps1, 0:ps1] = buf[pid0 + 3, :, :, :] data[g, 0:ps1, ps1:ps2, ps1:ps2] = buf[pid0 + 4, :, :, :] data[g, ps1:ps2, ps1:ps2, 0:ps1] = buf[pid0 + 5, :, :, :] data[g, ps1:ps2, 0:ps1, ps1:ps2] = buf[pid0 + 6, :, :, :] data[g, ps1:ps2, ps1:ps2, ps1:ps2] = buf[pid0 + 7, :, :, :] data = data.transpose() for i, g in enumerate(gs): offset += g.select(selector, data[..., i], rv[field], offset) return rv def _read_chunk_data(self, chunk, fields): rv = {} if len(chunk.objs) == 0: return rv for g in chunk.objs: rv[g.id] = {} # Split into particles and non-particles fluid_fields, particle_fields = [], [] for ftype, fname in fields: if ftype in self.ds.particle_types: particle_fields.append((ftype, fname)) else: fluid_fields.append((ftype, fname)) # particles if len(particle_fields) > 0: selector = AlwaysSelector(self.ds) rv.update(self._read_particle_selection([chunk], selector, particle_fields)) # fluid if len(fluid_fields) == 0: return rv ps2 = self.patch_size ps1 = ps2 // 2 for field in fluid_fields: ds = self._group_grid[field[1]] for gs in grid_sequences(chunk.objs): start = (gs[0].id) * self.pgroup end = (gs[-1].id + 1) * self.pgroup buf = ds[start:end, :, :, :] ngrid = len(gs) data = np.empty((ngrid, ps2, ps2, ps2), dtype=self._field_dtype) for g in range(ngrid): pid0 = g * self.pgroup data[g, 0:ps1, 0:ps1, 0:ps1] = buf[pid0 + 0, :, :, :] data[g, 0:ps1, 0:ps1, ps1:ps2] = buf[pid0 + 1, :, :, :] data[g, 0:ps1, ps1:ps2, 0:ps1] = buf[pid0 + 2, :, :, :] data[g, ps1:ps2, 0:ps1, 0:ps1] = buf[pid0 + 3, :, :, :] data[g, 0:ps1, ps1:ps2, ps1:ps2] = buf[pid0 + 4, :, :, :] data[g, ps1:ps2, ps1:ps2, 0:ps1] = buf[pid0 + 5, :, :, :] data[g, ps1:ps2, 0:ps1, ps1:ps2] = buf[pid0 + 6, :, :, :] data[g, ps1:ps2, ps1:ps2, ps1:ps2] = buf[pid0 + 7, :, :, :] data = data.transpose() for i, g in enumerate(gs): rv[g.id][field] = data[..., i] return rv yt-project-yt-f043ac8/yt/frontends/gamer/misc.py000066400000000000000000000000001510711153200216760ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gamer/tests/000077500000000000000000000000001510711153200215455ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gamer/tests/__init__.py000066400000000000000000000000001510711153200236440ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gamer/tests/test_outputs.py000066400000000000000000000075721510711153200247140ustar00rootroot00000000000000from numpy.testing import assert_array_almost_equal, assert_equal from yt.frontends.gamer.api import GAMERDataset from yt.testing import requires_file, units_override_check from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) jet = "InteractingJets/jet_000002" _fields_jet = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ) jet_units = { "length_unit": (1.0, "kpc"), "time_unit": (3.08567758096e13, "s"), "mass_unit": (1.4690033e36, "g"), } @requires_ds(jet, big_data=True) def test_jet(): ds = data_dir_load(jet, kwargs={"units_override": jet_units}) assert_equal(str(ds), "jet_000002") for test in small_patch_amr(ds, _fields_jet): test_jet.__name__ = test.description yield test psiDM = "WaveDarkMatter/psiDM_000020" _fields_psiDM = ("Dens", "Real", "Imag") @requires_ds(psiDM, big_data=True) def test_psiDM(): ds = data_dir_load(psiDM) assert_equal(str(ds), "psiDM_000020") for test in small_patch_amr(ds, _fields_psiDM): test_psiDM.__name__ = test.description yield test plummer = "Plummer/plummer_000000" _fields_plummer = (("gamer", "ParDens"), ("deposit", "io_cic")) @requires_ds(plummer, big_data=True) def test_plummer(): ds = data_dir_load(plummer) assert_equal(str(ds), "plummer_000000") for test in small_patch_amr(ds, _fields_plummer): test_plummer.__name__ = test.description yield test mhdvortex = "MHDOrszagTangVortex/Data_000018" _fields_mhdvortex = ( ("gamer", "CCMagX"), ("gamer", "CCMagY"), ("gas", "magnetic_energy_density"), ) @requires_ds(mhdvortex, big_data=True) def test_mhdvortex(): ds = data_dir_load(mhdvortex) assert_equal(str(ds), "Data_000018") for test in small_patch_amr(ds, _fields_mhdvortex): test_mhdvortex.__name__ = test.description yield test @requires_file(psiDM) def test_GAMERDataset(): assert isinstance(data_dir_load(psiDM), GAMERDataset) @requires_file(jet) def test_units_override(): units_override_check(jet) jiw = "JetICMWall/Data_000060" _fields_jiw = ( ("gas", "four_velocity_magnitude"), ("gas", "density"), ("gas", "gamma"), ("gas", "temperature"), ) @requires_ds(jiw, big_data=True) def test_jiw(): ds = data_dir_load(jiw) assert_equal(str(ds), "Data_000060") for test in small_patch_amr(ds, _fields_jiw): test_jiw.__name__ = test.description yield test @requires_ds(jiw, big_data=True) def test_stress_energy(): axes = "txyz" ds = data_dir_load(jiw) center = ds.arr([3.0, 10.0, 10.0], "kpc") sp = ds.sphere(center, (1.0, "kpc")) c2 = ds.units.clight**2 inv_c2 = 1.0 / c2 rho = sp["gas", "density"] p = sp["gas", "pressure"] e = sp["gas", "thermal_energy_density"] h = rho + (e + p) * inv_c2 for mu in range(4): for nu in range(4): # matrix is symmetric so only do the upper-right part if nu >= mu: Umu = sp[f"four_velocity_{axes[mu]}"] Unu = sp[f"four_velocity_{axes[nu]}"] Tmunu = h * Umu * Unu if mu != nu: assert_array_almost_equal(sp[f"T{mu}{nu}"], sp[f"T{nu}{mu}"]) else: Tmunu += p assert_array_almost_equal(sp[f"T{mu}{nu}"], Tmunu) cr_shock = "CRShockTube/Data_000005" @requires_ds(cr_shock) def test_cosmic_rays(): ds = data_dir_load(cr_shock) assert_array_almost_equal(ds.gamma_cr, 4.0 / 3.0) ad = ds.all_data() p_cr = ad["gas", "cosmic_ray_pressure"] e_cr = ad["gas", "cosmic_ray_energy_density"] assert_array_almost_equal(p_cr, e_cr / 3.0) e_kin = ad["gas", "kinetic_energy_density"] e_int = ad["gas", "thermal_energy_density"] e_tot = ad["gas", "total_energy_density"] assert_array_almost_equal(e_tot, e_kin + e_int + e_cr) yt-project-yt-f043ac8/yt/frontends/gdf/000077500000000000000000000000001510711153200200505ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gdf/__init__.py000066400000000000000000000000001510711153200221470ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gdf/api.py000066400000000000000000000002751510711153200211770ustar00rootroot00000000000000from . import tests from .data_structures import GDFDataset, GDFGrid, GDFHierarchy from .fields import GDFFieldInfo from .io import IOHandlerGDFHDF5 add_gdf_field = GDFFieldInfo.add_field yt-project-yt-f043ac8/yt/frontends/gdf/data_structures.py000066400000000000000000000273751510711153200236540ustar00rootroot00000000000000import os import weakref from functools import cached_property import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.funcs import just_one, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.units.dimensions import dimensionless as sympy_one # type: ignore from yt.units.unit_object import Unit # type: ignore from yt.units.unit_systems import unit_system_registry # type: ignore from yt.utilities.exceptions import YTGDFUnknownGeometry from yt.utilities.lib.misc_utilities import get_box_grids_level from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import GDFFieldInfo GEOMETRY_TRANS = { 0: "cartesian", 1: "polar", 2: "cylindrical", 3: "spherical", } class GDFGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level, start, dimensions): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = [] self.Children = [] self.Level = level self.start_index = start.copy() self.stop_index = self.start_index + dimensions self.ActiveDimensions = dimensions.copy() def _setup_dx(self): # So first we figure out what the index is. We don't assume # that dx=dy=dz , at least here. We probably do elsewhere. id = self.id - self._id_offset if len(self.Parent) > 0: self.dds = self.Parent[0].dds / self.ds.refine_by else: LE, RE = self.index.grid_left_edge[id, :], self.index.grid_right_edge[id, :] self.dds = np.array((RE - LE) / self.ActiveDimensions) self.field_data["dx"], self.field_data["dy"], self.field_data["dz"] = self.dds self.dds = self.ds.arr(self.dds, "code_length") class GDFHierarchy(GridIndex): grid = GDFGrid def __init__(self, ds, dataset_type="grid_data_format"): self.dataset = weakref.proxy(ds) self.index_filename = self.dataset.parameter_filename h5f = h5py.File(self.index_filename, mode="r") self.dataset_type = dataset_type GridIndex.__init__(self, ds, dataset_type) self.directory = os.path.dirname(self.index_filename) h5f.close() def _detect_output_fields(self): h5f = h5py.File(self.index_filename, mode="r") self.field_list = [("gdf", str(f)) for f in h5f["field_types"].keys()] h5f.close() def _count_grids(self): h5f = h5py.File(self.index_filename, mode="r") self.num_grids = h5f["/grid_parent_id"].shape[0] h5f.close() def _parse_index(self): h5f = h5py.File(self.index_filename, mode="r") dxs = [] self.grids = np.empty(self.num_grids, dtype="object") levels = (h5f["grid_level"][:]).copy() glis = (h5f["grid_left_index"][:]).copy() gdims = (h5f["grid_dimensions"][:]).copy() active_dims = ~( (np.max(gdims, axis=0) == 1) & (self.dataset.domain_dimensions == 1) ) for i in range(levels.shape[0]): self.grids[i] = self.grid(i, self, levels[i], glis[i], gdims[i]) self.grids[i]._level_id = levels[i] dx = ( self.dataset.domain_right_edge - self.dataset.domain_left_edge ) / self.dataset.domain_dimensions dx[active_dims] /= self.dataset.refine_by ** levels[i] dxs.append(dx.in_units("code_length")) dx = self.dataset.arr(dxs, units="code_length") self.grid_left_edge = self.dataset.domain_left_edge + dx * glis self.grid_dimensions = gdims.astype("int32") self.grid_right_edge = self.grid_left_edge + dx * self.grid_dimensions self.grid_particle_count = h5f["grid_particle_count"][:] del levels, glis, gdims h5f.close() def _populate_grid_objects(self): mask = np.empty(self.grids.size, dtype="int32") for g in self.grids: g._prepare_grid() g._setup_dx() for gi, g in enumerate(self.grids): g.Children = self._get_grid_children(g) for g1 in g.Children: g1.Parent.append(g) get_box_grids_level( self.grid_left_edge[gi, :], self.grid_right_edge[gi, :], self.grid_levels[gi].item(), self.grid_left_edge, self.grid_right_edge, self.grid_levels, mask, ) m = mask.astype("bool") m[gi] = False siblings = self.grids[gi:][m[gi:]] if len(siblings) > 0: g.OverlappingSiblings = siblings.tolist() self.max_level = self.grid_levels.max() def _get_box_grids(self, left_edge, right_edge): """ Gets back all the grids between a left edge and right edge """ eps = np.finfo(np.float64).eps grid_i = np.where( np.all((self.grid_right_edge - left_edge) > eps, axis=1) & np.all((right_edge - self.grid_left_edge) > eps, axis=1) ) return self.grids[grid_i], grid_i def _get_grid_children(self, grid): mask = np.zeros(self.num_grids, dtype="bool") grids, grid_ind = self._get_box_grids(grid.LeftEdge, grid.RightEdge) mask[grid_ind] = True return [g for g in self.grids[mask] if g.Level == grid.Level + 1] class GDFDataset(Dataset): _load_requirements = ["h5py"] _index_class = GDFHierarchy _field_info_class = GDFFieldInfo def __init__( self, filename, dataset_type="grid_data_format", storage_filename=None, geometry=None, units_override=None, unit_system="cgs", default_species_fields=None, ): self.geometry = geometry self.fluid_types += ("gdf",) Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ # This should be improved. h5f = h5py.File(self.parameter_filename, mode="r") for field_name in h5f["/field_types"]: current_field = h5f[f"/field_types/{field_name}"] if "field_to_cgs" in current_field.attrs: field_conv = current_field.attrs["field_to_cgs"] self.field_units[field_name] = just_one(field_conv) elif "field_units" in current_field.attrs: field_units = current_field.attrs["field_units"] if isinstance(field_units, str): current_field_units = current_field.attrs["field_units"] else: current_field_units = just_one(current_field.attrs["field_units"]) self.field_units[field_name] = current_field_units.decode("utf8") else: self.field_units[field_name] = "" if "dataset_units" in h5f: for unit_name in h5f["/dataset_units"]: current_unit = h5f[f"/dataset_units/{unit_name}"] value = current_unit[()] unit = current_unit.attrs["unit"] # need to convert to a Unit object and check dimensions # because unit can be things like # 'dimensionless/dimensionless**3' so naive string # comparisons are insufficient unit = Unit(unit, registry=self.unit_registry) if unit_name.endswith("_unit") and unit.dimensions is sympy_one: # Catch code units and if they are dimensionless, # assign CGS units. setdefaultattr will catch code units # which have already been set via units_override. un = unit_name[:-5] un = un.replace("magnetic", "magnetic_field_cgs", 1) unit = unit_system_registry["cgs"][un] setdefaultattr(self, unit_name, self.quan(value, unit)) setdefaultattr(self, unit_name, self.quan(value, unit)) if unit_name in h5f["/field_types"]: if unit_name in self.field_units: mylog.warning( "'field_units' was overridden by 'dataset_units/%s'", unit_name, ) self.field_units[unit_name] = str(unit) else: setdefaultattr(self, "length_unit", self.quan(1.0, "cm")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) h5f.close() @cached_property def unique_identifier(self) -> str: with h5py.File(self.parameter_filename, mode="r") as handle: return str(handle["/simulation_parameters"].attrs["unique_identifier"]) def _parse_parameter_file(self): self._handle = h5py.File(self.parameter_filename, mode="r") if "data_software" in self._handle["gridded_data_format"].attrs: self.data_software = self._handle["gridded_data_format"].attrs[ "data_software" ] else: self.data_software = "unknown" sp = self._handle["/simulation_parameters"].attrs if self.geometry is None: geometry = just_one(sp.get("geometry", 0)) try: self.geometry = Geometry(GEOMETRY_TRANS[geometry]) except KeyError as e: raise YTGDFUnknownGeometry(geometry) from e self.parameters.update(sp) self.domain_left_edge = sp["domain_left_edge"][:] self.domain_right_edge = sp["domain_right_edge"][:] self.domain_dimensions = sp["domain_dimensions"][:] refine_by = sp["refine_by"] if refine_by is None: refine_by = 2 self.refine_by = refine_by self.dimensionality = sp["dimensionality"] self.current_time = sp["current_time"] self.cosmological_simulation = sp["cosmological_simulation"] if sp["num_ghost_zones"] != 0: raise RuntimeError self.num_ghost_zones = sp["num_ghost_zones"] self.field_ordering = sp["field_ordering"] self.boundary_conditions = sp["boundary_conditions"][:] self._periodicity = tuple(bnd == 0 for bnd in self.boundary_conditions[::2]) if self.cosmological_simulation: self.current_redshift = sp["current_redshift"] self.omega_lambda = sp["omega_lambda"] self.omega_matter = sp["omega_matter"] self.hubble_constant = sp["hubble_constant"] else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 # Hardcode time conversion for now. self.parameters["Time"] = 1.0 # Hardcode for now until field staggering is supported. self.parameters["HydroMethod"] = 0 self._handle.close() del self._handle @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False try: fileh = h5py.File(filename, mode="r") if "gridded_data_format" in fileh: fileh.close() return True fileh.close() except Exception: pass return False def __str__(self): return self.basename.rsplit(".", 1)[0] yt-project-yt-f043ac8/yt/frontends/gdf/definitions.py000066400000000000000000000001061510711153200227320ustar00rootroot00000000000000""" Various definitions for various other modules and routines """ yt-project-yt-f043ac8/yt/frontends/gdf/fields.py000066400000000000000000000021271510711153200216720ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer # The nice thing about GDF is that for the most part, everything is in CGS, # with potentially a scalar modification. class GDFFieldInfo(FieldInfoContainer): known_other_fields = ( ("density", ("g/cm**3", ["density"], None)), ("specific_energy", ("erg/g", ["specific_thermal_energy"], None)), ("pressure", ("erg/cm**3", ["pressure"], None)), ("temperature", ("K", ["temperature"], None)), ("velocity_x", ("cm/s", ["velocity_x"], None)), ("velocity_y", ("cm/s", ["velocity_y"], None)), ("velocity_z", ("cm/s", ["velocity_z"], None)), ("mag_field_x", ("gauss", ["magnetic_field_x"], None)), ("mag_field_y", ("gauss", ["magnetic_field_y"], None)), ("mag_field_z", ("gauss", ["magnetic_field_z"], None)), ) known_particle_fields = () def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases setup_magnetic_field_aliases( self, "gdf", [f"magnetic_field_{ax}" for ax in "xyz"] ) yt-project-yt-f043ac8/yt/frontends/gdf/io.py000066400000000000000000000061101510711153200210270ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.geometry.selection_routines import GridSelector from yt.utilities.io_handler import BaseParticleIOHandler from yt.utilities.on_demand_imports import _h5py as h5py def _grid_dname(grid_id): return "/data/grid_%010i" % grid_id def _field_dname(grid_id, field_name): return f"{_grid_dname(grid_id)}/{field_name}" # TODO all particle bits were removed class IOHandlerGDFHDF5(BaseParticleIOHandler): _dataset_type = "grid_data_format" _offset_string = "data:offsets=0" _data_string = "data:datatype=0" def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} chunks = list(chunks) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError grid = chunks[0].objs[0] h5f = h5py.File(grid.filename, mode="r") gds = h5f.get(_grid_dname(grid.id)) for ftype, fname in fields: if self.ds.field_ordering == 1: rv[ftype, fname] = gds.get(fname)[()].swapaxes(0, 2) else: rv[ftype, fname] = gds.get(fname)[()] h5f.close() return rv if size is None: size = sum(grid.count(selector) for chunk in chunks for grid in chunk.objs) if any((ftype != "gdf" for ftype, fname in fields)): raise NotImplementedError for field in fields: ftype, fname = field fsize = size # check the dtype instead rv[field] = np.empty(fsize, dtype="float64") ngrids = sum(len(chunk.objs) for chunk in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [fn for ft, fn in fields], ngrids, ) ind = 0 for chunk in chunks: fid = None for grid in chunk.objs: if grid.filename is None: continue if fid is None: fid = h5py.h5f.open( bytes(grid.filename, "utf-8"), h5py.h5f.ACC_RDONLY ) if self.ds.field_ordering == 1: # check the dtype instead data = np.empty(grid.ActiveDimensions[::-1], dtype="float64") data_view = data.swapaxes(0, 2) else: # check the dtype instead data_view = data = np.empty(grid.ActiveDimensions, dtype="float64") for field in fields: ftype, fname = field dg = h5py.h5d.open( fid, bytes(_field_dname(grid.id, fname), "utf-8") ) dg.read(h5py.h5s.ALL, h5py.h5s.ALL, data) # caches nd = grid.select(selector, data_view, rv[field], ind) ind += nd # I don't get that part, only last nd is added if fid is not None: fid.close() return rv yt-project-yt-f043ac8/yt/frontends/gdf/misc.py000066400000000000000000000000001510711153200213430ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gdf/tests/000077500000000000000000000000001510711153200212125ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gdf/tests/__init__.py000066400000000000000000000000001510711153200233110ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gdf/tests/conftest.py000066400000000000000000000016221510711153200234120ustar00rootroot00000000000000""" Title: conftest.py Purpose: Contains fixtures for loading data. """ # Test data sedov = "sedov/sedov_tst_0004.h5" # Test parameters. Format: # {test1: {param1 : [(val1, val2,...), (id1, id2,...)], param2 : ...}, test2: ...} test_params = { "test_sedov_tunnel": { "axis": [(0, 1, 2), ("0", "1", "2")], "dobj": [(None, ("sphere", ("max", (0.1, "unitary")))), ("None", "sphere")], "weight": [(None, ("gas", "density")), ("None", "density")], "field": [(("gas", "density"), ("gas", "velocity_x")), ("density", "vx")], } } def pytest_generate_tests(metafunc): # Loop over each test in test_params for test_name, params in test_params.items(): if metafunc.function.__name__ == test_name: # Parametrize for param_name, param_vals in params.items(): metafunc.parametrize(param_name, param_vals[0], ids=param_vals[1]) yt-project-yt-f043ac8/yt/frontends/gdf/tests/test_outputs.py000066400000000000000000000015461510711153200243540ustar00rootroot00000000000000import pytest from yt.frontends.gdf.api import GDFDataset from yt.testing import requires_file, requires_module, units_override_check from yt.utilities.answer_testing.answer_tests import small_patch_amr # Test data sedov = "sedov/sedov_tst_0004.h5" @pytest.mark.answer_test class TestGDF: answer_file = None saved_hashes = None answer_version = "000" @pytest.mark.usefixtures("hashing") @pytest.mark.parametrize("ds", [sedov], indirect=True) def test_sedov_tunnel(self, axis, dobj, weight, field, ds): self.hashes.update(small_patch_amr(ds, field, weight, axis, dobj)) @pytest.mark.parametrize("ds", [sedov], indirect=True) def test_GDFDataset(self, ds): assert isinstance(ds, GDFDataset) @requires_module("h5py") @requires_file(sedov) def test_units_override(self): units_override_check(sedov) yt-project-yt-f043ac8/yt/frontends/gdf/tests/test_outputs_nose.py000066400000000000000000000015051510711153200253730ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.frontends.gdf.api import GDFDataset from yt.testing import requires_file, requires_module, units_override_check from yt.utilities.answer_testing.framework import ( data_dir_load, requires_ds, small_patch_amr, ) _fields = [("gas", "density"), ("gas", "velocity_x")] sedov = "sedov/sedov_tst_0004.h5" @requires_ds(sedov) def test_sedov_tunnel(): ds = data_dir_load(sedov) assert_equal(str(ds), "sedov_tst_0004") for test in small_patch_amr(ds, _fields): test_sedov_tunnel.__name__ = test.description yield test @requires_module("h5py") @requires_file(sedov) def test_GDFDataset(): assert isinstance(data_dir_load(sedov), GDFDataset) @requires_module("h5py") @requires_file(sedov) def test_units_override(): units_override_check(sedov) yt-project-yt-f043ac8/yt/frontends/gizmo/000077500000000000000000000000001510711153200204355ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gizmo/__init__.py000066400000000000000000000000001510711153200225340ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gizmo/api.py000066400000000000000000000001151510711153200215550ustar00rootroot00000000000000from .data_structures import GizmoDataset from .fields import GizmoFieldInfo yt-project-yt-f043ac8/yt/frontends/gizmo/data_structures.py000066400000000000000000000142271510711153200242310ustar00rootroot00000000000000import os import numpy as np from yt.frontends.gadget.data_structures import GadgetHDF5Dataset from yt.utilities.cosmology import Cosmology from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import GizmoFieldInfo class GizmoDataset(GadgetHDF5Dataset): _load_requirements = ["h5py"] _field_info_class = GizmoFieldInfo @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = ["Header"] veto_groups = ["Config", "Constants", "FOF", "Group", "Subhalo"] valid = True valid_fname = filename # If passed arg is a directory, look for the .0 file in that dir if os.path.isdir(filename): valid_files = [] for f in os.listdir(filename): fname = os.path.join(filename, f) fext = os.path.splitext(fname)[-1] if ( (".0" in f) and (fext not in {".ewah", ".kdtree"}) and os.path.isfile(fname) ): valid_files.append(fname) if len(valid_files) == 0: valid = False elif len(valid_files) > 1: valid = False else: valid_fname = valid_files[0] try: fh = h5py.File(valid_fname, mode="r") valid = all(ng in fh["/"] for ng in need_groups) and not any( vg in fh["/"] for vg in veto_groups ) # From Apr 2021, 7f1f06f, public gizmo includes a header variable # GIZMO_version, which is set to the year of the most recent commit # We should prefer this to checking the metallicity, which might # not exist if "GIZMO_version" not in fh["/Header"].attrs: dmetal = "/PartType0/Metallicity" if dmetal not in fh or ( fh[dmetal].ndim > 1 and fh[dmetal].shape[1] < 11 ): valid = False fh.close() except Exception: valid = False return valid def _set_code_unit_attributes(self): super()._set_code_unit_attributes() def _parse_parameter_file(self): hvals = self._get_hvals() self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "sph" # Set standard values # We may have an overridden bounding box. if self.domain_left_edge is None and hvals["BoxSize"] != 0: self.domain_left_edge = np.zeros(3, "float64") self.domain_right_edge = np.ones(3, "float64") * hvals["BoxSize"] self.domain_dimensions = np.ones(3, "int32") self._periodicity = (True, True, True) self.cosmological_simulation = 1 self.current_redshift = hvals.get("Redshift", 0.0) if "Redshift" not in hvals: mylog.info("Redshift is not set in Header. Assuming z=0.") if "ComovingIntegrationOn" in hvals: # In 1d8479, Nov 2020, public GIZMO updated the names of the Omegas # to include an _, added baryons and radiation and added the # ComovingIntegrationOn field. ComovingIntegrationOn is always set, # but the Omega's are only included if ComovingIntegrationOn is true mylog.debug("Reading cosmological parameters using post-1d8479 format") self.cosmological_simulation = hvals["ComovingIntegrationOn"] if self.cosmological_simulation: self.omega_lambda = hvals["Omega_Lambda"] self.omega_matter = hvals["Omega_Matter"] self.omega_baryon = hvals["Omega_Baryon"] self.omega_radiation = hvals["Omega_Radiation"] self.hubble_constant = hvals["HubbleParam"] elif "OmegaLambda" in hvals: # Should still support GIZMO versions prior to 1d8479 too mylog.info( "ComovingIntegrationOn does not exist, falling back to OmegaLambda", ) self.omega_lambda = hvals["OmegaLambda"] self.omega_matter = hvals["Omega0"] self.hubble_constant = hvals["HubbleParam"] self.cosmological_simulation = self.omega_lambda != 0.0 else: # If these are not set it is definitely not a cosmological dataset. mylog.debug("No cosmological information found, assuming defaults") self.omega_lambda = 0.0 self.omega_matter = 0.0 # Just in case somebody asks for it. self.cosmological_simulation = 0 # Hubble is set below for Omega Lambda = 0. if not self.cosmological_simulation: mylog.info( "ComovingIntegrationOn != 1 or (not found " "and OmegaLambda is 0.0), so we are turning off Cosmology.", ) self.hubble_constant = 1.0 # So that scaling comes out correct self.current_redshift = 0.0 # This may not be correct. self.current_time = hvals["Time"] else: # Now we calculate our time based on the cosmology, because in # ComovingIntegration hvals["Time"] will in fact be the expansion # factor, not the actual integration time, so we re-calculate # global time from our Cosmology. cosmo = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmo.lookback_time(self.current_redshift, 1e6) mylog.info( "Calculating time from %0.3e to be %0.3e seconds", hvals["Time"], self.current_time, ) self.parameters = hvals prefix = os.path.join(self.directory, self.basename.split(".", 1)[0]) if hvals["NumFiles"] > 1: self.filename_template = f"{prefix}.%(num)s{self._suffix}" else: self.filename_template = self.parameter_filename self.file_count = hvals["NumFiles"] yt-project-yt-f043ac8/yt/frontends/gizmo/fields.py000066400000000000000000000163141510711153200222620ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.fields.species_fields import add_species_field_by_density, setup_species_fields from yt.frontends.gadget.fields import GadgetFieldInfo from yt.frontends.sph.fields import SPHFieldInfo metal_elements = ["He", "C", "N", "O", "Ne", "Mg", "Si", "S", "Ca", "Fe"] class GizmoFieldInfo(GadgetFieldInfo): # The known fields list is according to the GIZMO User Guide. See # http://www.tapir.caltech.edu/~phopkins/Site/GIZMO_files/gizmo_documentation.html#snaps-reading known_particle_fields: KnownFieldsT = ( ("Coordinates", ("code_length", ["particle_position"], None)), ("Velocities", ("code_velocity", ["particle_velocity"], None)), ("ParticleIDs", ("", ["particle_index"], None)), ("Masses", ("code_mass", ["particle_mass"], None)), ("InternalEnergy", ("code_specific_energy", ["specific_thermal_energy"], None)), ("Density", ("code_mass / code_length**3", ["density"], None)), ("SmoothingLength", ("code_length", ["smoothing_length"], None)), ("ElectronAbundance", ("", [], None)), ("NeutralHydrogenAbundance", ("", [], None)), ("StarFormationRate", ("Msun / yr", ["star_formation_rate"], None)), ("Metallicity", ("code_metallicity", ["metallicity"], None)), ("Metallicity_00", ("", ["metallicity"], None)), ("Metallicity_01", ("", ["He_metallicity"], None)), ("Metallicity_02", ("", ["C_metallicity"], None)), ("Metallicity_03", ("", ["N_metallicity"], None)), ("Metallicity_04", ("", ["O_metallicity"], None)), ("Metallicity_05", ("", ["Ne_metallicity"], None)), ("Metallicity_06", ("", ["Mg_metallicity"], None)), ("Metallicity_07", ("", ["Si_metallicity"], None)), ("Metallicity_08", ("", ["S_metallicity"], None)), ("Metallicity_09", ("", ["Ca_metallicity"], None)), ("Metallicity_10", ("", ["Fe_metallicity"], None)), ("ArtificialViscosity", ("", [], None)), ("MagneticField", ("code_magnetic", ["particle_magnetic_field"], None)), ("DivergenceOfMagneticField", ("code_magnetic / code_length", [], None)), ("StellarFormationTime", ("", [], None)), # "StellarFormationTime" has different meanings in (non-)cosmological # runs, so units are left blank here. ("BH_Mass", ("code_mass", [], None)), ("BH_Mdot", ("code_mass / code_time", [], None)), ("BH_Mass_AlphaDisk", ("code_mass", [], None)), ) def __init__(self, *args, **kwargs): super(SPHFieldInfo, self).__init__(*args, **kwargs) if ("PartType0", "Metallicity_00") in self.field_list: self.nuclei_names = metal_elements self.species_names = ["H_p0", "H_p1"] + metal_elements else: self.nuclei_names = [] def setup_particle_fields(self, ptype): FieldInfoContainer.setup_particle_fields(self, ptype) if ptype in ("PartType0",): self.setup_gas_particle_fields(ptype) setup_species_fields(self, ptype) if ptype in ("PartType4",): self.setup_star_particle_fields(ptype) def setup_gas_particle_fields(self, ptype): from yt.fields.magnetic_field import setup_magnetic_field_aliases super().setup_gas_particle_fields(ptype) def _h_p0_density(field, data): x_H = 1.0 - data[ptype, "He_metallicity"] - data[ptype, "metallicity"] return ( x_H * data[ptype, "density"] * data[ptype, "NeutralHydrogenAbundance"] ) self.add_field( (ptype, "H_p0_density"), sampling_type="particle", function=_h_p0_density, units=self.ds.unit_system["density"], ) add_species_field_by_density(self, ptype, "H") def _h_p1_density(field, data): x_H = 1.0 - data[ptype, "He_metallicity"] - data[ptype, "metallicity"] return ( x_H * data[ptype, "density"] * (1.0 - data[ptype, "NeutralHydrogenAbundance"]) ) self.add_field( (ptype, "H_p1_density"), sampling_type="particle", function=_h_p1_density, units=self.ds.unit_system["density"], ) add_species_field_by_density(self, ptype, "H_p1") def _nuclei_mass_density_field(field, data): species = field.name[1][: field.name[1].find("_")] return data[ptype, "density"] * data[ptype, f"{species}_metallicity"] for species in ["H", "H_p0", "H_p1"]: for suf in ["_density", "_number_density"]: field = f"{species}{suf}" self.alias(("gas", field), (ptype, field)) if (ptype, "ElectronAbundance") in self.field_list: def _el_number_density(field, data): return ( data[ptype, "ElectronAbundance"] * data[ptype, "H_nuclei_density"] ) self.add_field( (ptype, "El_number_density"), sampling_type="particle", function=_el_number_density, units=self.ds.unit_system["number_density"], ) self.alias(("gas", "El_number_density"), (ptype, "El_number_density")) for species in self.nuclei_names: self.add_field( (ptype, f"{species}_nuclei_mass_density"), sampling_type="particle", function=_nuclei_mass_density_field, units=self.ds.unit_system["density"], ) for suf in ["_nuclei_mass_density", "_metallicity"]: field = f"{species}{suf}" self.alias(("gas", field), (ptype, field)) def _metal_density_field(field, data): return data[ptype, "metallicity"] * data[ptype, "density"] self.add_field( (ptype, "metal_density"), sampling_type="local", function=_metal_density_field, units=self.ds.unit_system["density"], ) self.alias(("gas", "metal_density"), (ptype, "metal_density")) magnetic_field = "MagneticField" if (ptype, magnetic_field) in self.field_list: setup_magnetic_field_aliases(self, ptype, magnetic_field) def setup_star_particle_fields(self, ptype): def _creation_time(field, data): if data.ds.cosmological_simulation: a_form = data[ptype, "StellarFormationTime"] z_form = 1 / a_form - 1 creation_time = data.ds.cosmology.t_from_z(z_form) else: t_form = data[ptype, "StellarFormationTime"] creation_time = data.ds.arr(t_form, "code_time") return creation_time self.add_field( (ptype, "creation_time"), sampling_type="particle", function=_creation_time, units=self.ds.unit_system["time"], ) def _age(field, data): return data.ds.current_time - data[ptype, "creation_time"] self.add_field( (ptype, "age"), sampling_type="particle", function=_age, units=self.ds.unit_system["time"], ) yt-project-yt-f043ac8/yt/frontends/gizmo/tests/000077500000000000000000000000001510711153200215775ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gizmo/tests/__init__.py000066400000000000000000000000001510711153200236760ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/gizmo/tests/test_outputs.py000066400000000000000000000105211510711153200247320ustar00rootroot00000000000000from collections import OrderedDict import yt from yt.frontends.gizmo.api import GizmoDataset from yt.frontends.gizmo.fields import metal_elements from yt.testing import assert_allclose_units, requires_file, requires_module from yt.units import Myr from yt.utilities.answer_testing.framework import requires_ds, sph_answer # This maps from field names to weight field names to use for projections fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "metallicity"), ("gas", "density")), (("gas", "O_metallicity"), ("gas", "density")), (("gas", "velocity_magnitude"), None), ] ) g64 = "gizmo_64/output/snap_N64L16_135.hdf5" gmhd = "gizmo_mhd_mwdisk/gizmo_mhd_mwdisk.hdf5" gmhd_bbox = [[-400, 400]] * 3 zeld_wg = "gizmo_zeldovich/snapshot_076_wi_gizver.hdf5" zeld_ng = "gizmo_zeldovich/snapshot_076_no_gizver.hdf5" @requires_module("h5py") @requires_ds(g64, big_data=True) def test_gizmo_64(): ds = yt.load(g64) assert isinstance(ds, GizmoDataset) for test in sph_answer(ds, "snap_N64L16_135", 524288, fields): test_gizmo_64.__name__ = test.description yield test @requires_module("h5py") @requires_file(zeld_wg) @requires_file(zeld_ng) def test_gizmo_zeldovich(): """ Test loading a recent gizmo snapshot that doesn't have cooling/metallicity The gizmo_zeldovich file has no metallicity field on the gas particles but is a cosmological dataset run using GIZMO_version=2022. There are two versions of the file, with GIZMO_version (_wg) and without GIZMO_version (_ng). Check that both load as gizmo datasets and correctly pull the cosmological variables. This test should get simpler when the file switches to pytest. """ for fn in [zeld_wg, zeld_ng]: ds = yt.load(fn) assert isinstance(ds, GizmoDataset) assert ds.cosmological_simulation assert ds.omega_matter == 1.0 assert ds.omega_lambda == 0.0 # current_time is calculated from the cosmology so this checks if that # was calculated correctly assert_allclose_units(ds.current_time, 1672.0678 * Myr) @requires_module("h5py") @requires_file(gmhd) def test_gizmo_mhd(): """ Magnetic fields should be loaded correctly when they are present. """ ds = yt.load(gmhd, bounding_box=gmhd_bbox, unit_system="code") ad = ds.all_data() ptype = "PartType0" # Test vector magnetic field fmag = "particle_magnetic_field" f = ad[ptype, fmag] assert str(f.units) == "code_magnetic" assert f.shape == (409013, 3) # Test component magnetic fields for axis in "xyz": f = ad[ptype, fmag + "_" + axis] assert str(f.units) == "code_magnetic" assert f.shape == (409013,) @requires_module("h5py") @requires_file(gmhd) def test_gas_particle_fields(): """ Test fields set up in GizmoFieldInfo.setup_gas_particle_fields. """ ds = yt.load(gmhd, bounding_box=gmhd_bbox) ptype = "PartType0" derived_fields = [] # Add species fields for species in ["H_p0", "H_p1"]: for suffix in ["density", "fraction", "mass", "number_density"]: derived_fields += [f"{species}_{suffix}"] for species in metal_elements: derived_fields += [f"{species}_nuclei_mass_density"] # Add magnetic fields derived_fields += [f"particle_magnetic_field_{axis}" for axis in "xyz"] # Check for field in derived_fields: assert (ptype, field) in ds.derived_field_list ptype = "gas" derived_fields = [] for species in ["H_p0", "H_p1"]: for suffix in ["density", "number_density"]: derived_fields += [f"{species}_{suffix}"] for species in metal_elements: for suffix in ["nuclei_mass_density", "metallicity"]: derived_fields += [f"{species}_{suffix}"] derived_fields += [f"magnetic_field_{axis}" for axis in "xyz"] for field in derived_fields: assert (ptype, field) in ds.derived_field_list @requires_module("h5py") @requires_file(gmhd) def test_star_particle_fields(): """ Test fields set up in GizmoFieldInfo.setup_star_particle_fields. """ ds = yt.load(gmhd, bounding_box=gmhd_bbox) ptype = "PartType4" derived_fields = ["creation_time", "age"] for field in derived_fields: assert (ptype, field) in ds.derived_field_list yt-project-yt-f043ac8/yt/frontends/halo_catalog/000077500000000000000000000000001510711153200217255ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/halo_catalog/__init__.py000066400000000000000000000000521510711153200240330ustar00rootroot00000000000000""" API for HaloCatalog frontend. """ yt-project-yt-f043ac8/yt/frontends/halo_catalog/api.py000066400000000000000000000002041510711153200230440ustar00rootroot00000000000000from .data_structures import YTHaloCatalogDataset from .fields import YTHaloCatalogFieldInfo from .io import IOHandlerYTHaloCatalog yt-project-yt-f043ac8/yt/frontends/halo_catalog/data_structures.py000066400000000000000000000404111510711153200255130ustar00rootroot00000000000000import glob import weakref from collections import defaultdict from functools import cached_property, partial import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer, ) from yt.data_objects.static_output import ( ParticleDataset, ParticleFile, ) from yt.frontends.ytdata.data_structures import SavedDataset from yt.funcs import parse_h5_attr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.on_demand_imports import _h5py as h5py from .fields import YTHaloCatalogFieldInfo, YTHaloCatalogHaloFieldInfo class HaloCatalogFile(ParticleFile): """ Base class for data files of halo catalog datasets. This is mainly here to correct for periodicity when reading particle positions. """ def __init__(self, ds, io, filename, file_id, frange): super().__init__(ds, io, filename, file_id, frange) def _read_particle_positions(self, ptype, f=None): raise NotImplementedError def _get_particle_positions(self, ptype, f=None): pcount = self.total_particles[ptype] if pcount == 0: return None # Correct for periodicity. dle = self.ds.domain_left_edge.to("code_length").v dw = self.ds.domain_width.to("code_length").v pos = self._read_particle_positions(ptype, f=f) si, ei = self.start, self.end if None not in (si, ei): pos = pos[si:ei] np.subtract(pos, dle, out=pos) np.mod(pos, dw, out=pos) np.add(pos, dle, out=pos) return pos class YTHaloCatalogFile(HaloCatalogFile): """ Data file class for the YTHaloCatalogDataset. """ def __init__(self, ds, io, filename, file_id, frange): with h5py.File(filename, mode="r") as f: self.header = {field: parse_h5_attr(f, field) for field in f.attrs.keys()} pids = f.get("particles/ids") self.total_ids = 0 if pids is None else pids.size self.group_length_sum = self.total_ids super().__init__(ds, io, filename, file_id, frange) def _read_particle_positions(self, ptype, f=None): """ Read all particle positions in this file. """ if f is None: close = True f = h5py.File(self.filename, mode="r") else: close = False pcount = self.header["num_halos"] pos = np.empty((pcount, 3), dtype="float64") for i, ax in enumerate("xyz"): pos[:, i] = f[f"particle_position_{ax}"][()] if close: f.close() return pos class YTHaloCatalogDataset(SavedDataset): """ Dataset class for halo catalogs made with yt. This covers yt FoF/HoP halo finders and the halo analysis in yt_astro_analysis. """ _load_requirements = ["h5py"] _index_class = ParticleIndex _file_class = YTHaloCatalogFile _field_info_class = YTHaloCatalogFieldInfo _suffix = ".h5" _con_attrs = ( "cosmological_simulation", "current_time", "current_redshift", "hubble_constant", "omega_matter", "omega_lambda", "domain_left_edge", "domain_right_edge", ) def __init__( self, filename, dataset_type="ythalocatalog", index_order=None, units_override=None, unit_system="cgs", ): self.index_order = index_order super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, ) def add_field(self, *args, **kwargs): super().add_field(*args, **kwargs) self._halos_ds.add_field(*args, **kwargs) @property def halos_field_list(self): return self._halos_ds.field_list @property def halos_derived_field_list(self): return self._halos_ds.derived_field_list @cached_property def _halos_ds(self): return YTHaloDataset(self) def _setup_classes(self): super()._setup_classes() self.halo = partial(YTHaloCatalogHaloContainer, ds=self._halos_ds) self.halo.__doc__ = YTHaloCatalogHaloContainer.__doc__ def _parse_parameter_file(self): self.refine_by = 2 self.dimensionality = 3 self.domain_dimensions = np.ones(self.dimensionality, "int32") self._periodicity = (True, True, True) prefix = ".".join(self.parameter_filename.rsplit(".", 2)[:-2]) self.filename_template = f"{prefix}.%(num)s{self._suffix}" self.file_count = len(glob.glob(prefix + "*" + self._suffix)) self.particle_types = ("halos",) self.particle_types_raw = ("halos",) super()._parse_parameter_file() @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: if ( "data_type" in f.attrs and parse_h5_attr(f, "data_type") == "halo_catalog" ): return True return False class YTHaloParticleIndex(ParticleIndex): """ Particle index for getting halo particles from YTHaloCatalogDatasets. """ def __init__(self, ds, dataset_type): self.real_ds = weakref.proxy(ds.real_ds) super().__init__(ds, dataset_type) def _calculate_particle_index_starts(self): """ Create a dict of halo id offsets for each file. """ particle_count = defaultdict(int) offset_count = 0 for data_file in self.data_files: data_file.index_start = { ptype: particle_count[ptype] for ptype in data_file.total_particles } data_file.offset_start = offset_count for ptype in data_file.total_particles: particle_count[ptype] += data_file.total_particles[ptype] offset_count += getattr(data_file, "total_offset", 0) self._halo_index_start = {} for ptype in self.ds.particle_types_raw: d = [data_file.index_start[ptype] for data_file in self.data_files] self._halo_index_start.update({ptype: np.array(d)}) def _detect_output_fields(self): field_list = [] scalar_field_list = [] units = {} pc = {} for ptype in self.ds.particle_types_raw: d = [df.total_particles[ptype] for df in self.data_files] pc.update({ptype: sum(d)}) found_fields = {ptype: False for ptype, pnum in pc.items() if pnum > 0} has_ids = False for data_file in self.data_files: fl, sl, idl, _units = self.io._identify_fields(data_file) units.update(_units) field_list.extend([f for f in fl if f not in field_list]) scalar_field_list.extend([f for f in sl if f not in scalar_field_list]) for ptype in found_fields: found_fields[ptype] |= data_file.total_particles[ptype] has_ids |= len(idl) > 0 if all(found_fields.values()) and has_ids: break self.field_list = field_list self.scalar_field_list = scalar_field_list ds = self.dataset ds.scalar_field_list = scalar_field_list ds.particle_types = tuple({pt for pt, ds in field_list}) ds.field_units.update(units) ds.particle_types_raw = ds.particle_types def _get_halo_file_indices(self, ptype, identifiers): """ Get the index of the data file list where this halo lives. Digitize returns i such that bins[i-1] <= x < bins[i], so we subtract one because we will open data file i. """ return np.digitize(identifiers, self._halo_index_start[ptype], right=False) - 1 def _get_halo_scalar_index(self, ptype, identifier): i_scalar = self._get_halo_file_indices(ptype, [identifier])[0] scalar_index = identifier - self._halo_index_start[ptype][i_scalar] return scalar_index def _get_halo_values(self, ptype, identifiers, fields, f=None): """ Get field values for halo data containers. """ # if a file is already open, don't open it again filename = None if f is None else f.filename data = defaultdict(lambda: np.empty(identifiers.size)) i_scalars = self._get_halo_file_indices(ptype, identifiers) for i_scalar in np.unique(i_scalars): # mask array to get field data for this halo target = i_scalars == i_scalar scalar_indices = identifiers - self._halo_index_start[ptype][i_scalar] # only open file if it's not already open my_f = ( f if self.data_files[i_scalar].filename == filename else h5py.File(self.data_files[i_scalar].filename, mode="r") ) for field in fields: data[field][target] = self._read_halo_particle_field( my_f, ptype, field, scalar_indices[target] ) if self.data_files[i_scalar].filename != filename: my_f.close() return data def _identify_base_chunk(self, dobj): pass def _read_halo_particle_field(self, fh, ptype, field, indices): return fh[field][indices] def _read_particle_fields(self, fields, dobj, chunk=None): if not fields: return {}, [] fields_to_read, fields_to_generate = self._split_fields(fields) if not fields_to_read: return {}, fields_to_generate fields_to_return = self.io._read_particle_selection(dobj, fields_to_read) return fields_to_return, fields_to_generate def _setup_data_io(self): super()._setup_data_io() if self.real_ds._instantiated_index is None: self.real_ds.index self.real_ds.index # inherit some things from parent index self._data_files = self.real_ds.index.data_files self._total_particles = self.real_ds.index.total_particles self._calculate_particle_index_starts() class HaloDataset(ParticleDataset): """ Base class for dataset accessing particles from halo catalogs. """ def __init__(self, ds, dataset_type): self.real_ds = ds for attr in [ "filename_template", "file_count", "particle_types_raw", "particle_types", "_periodicity", ]: setattr(self, attr, getattr(self.real_ds, attr)) super().__init__(self.real_ds.parameter_filename, dataset_type) def print_key_parameters(self): pass def _set_derived_attrs(self): pass def _parse_parameter_file(self): for attr in [ "cosmological_simulation", "cosmology", "current_redshift", "current_time", "dimensionality", "domain_dimensions", "domain_left_edge", "domain_right_edge", "domain_width", "hubble_constant", "omega_lambda", "omega_matter", "unique_identifier", ]: setattr(self, attr, getattr(self.real_ds, attr, None)) def set_code_units(self): self._set_code_unit_attributes() self.unit_registry = self.real_ds.unit_registry def _set_code_unit_attributes(self): for unit in ["length", "time", "mass", "velocity", "magnetic", "temperature"]: my_unit = f"{unit}_unit" setattr(self, my_unit, getattr(self.real_ds, my_unit, None)) def __str__(self): return f"{self.real_ds}" def _setup_classes(self): self.objects = [] class YTHaloDataset(HaloDataset): """ Dataset used for accessing member particles from YTHaloCatalogDatasets. """ _index_class = YTHaloParticleIndex _file_class = YTHaloCatalogFile _field_info_class = YTHaloCatalogHaloFieldInfo def __init__(self, ds, dataset_type="ythalo"): super().__init__(ds, dataset_type) def _set_code_unit_attributes(self): pass @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # We don't ever want this to be loaded by yt.load. return False class HaloContainer(YTSelectionContainer): """ Base class for data containers providing halo particles. """ _type_name = "halo" _con_args = ("ptype", "particle_identifier") _skip_add = True _spatial = False def __init__(self, ptype, particle_identifier, ds=None): if ptype not in ds.particle_types_raw: raise RuntimeError( f'Possible halo types are {ds.particle_types_raw}, supplied "{ptype}".' ) self.ptype = ptype self._current_particle_type = ptype super().__init__(ds, {}) self._set_identifiers(particle_identifier) # Find the file that has the scalar values for this halo. i_scalar = self.index._get_halo_file_indices(ptype, [self.particle_identifier])[ 0 ] self.i_scalar = i_scalar self.scalar_data_file = self.index.data_files[i_scalar] # Data files containing particles belonging to this halo. self.field_data_files = [self.index.data_files[i_scalar]] # index within halo arrays that corresponds to this halo self.scalar_index = self.index._get_halo_scalar_index( ptype, self.particle_identifier ) self._set_io_data() self.particle_number = self._get_particle_number() # starting and ending indices for each file containing particles self._set_field_indices() @cached_property def mass(self): return self[self.ptype, "particle_mass"][0] @cached_property def radius(self): return self[self.ptype, "virial_radius"][0] @cached_property def position(self): return self[self.ptype, "particle_position"][0] @cached_property def velocity(self): return self[self.ptype, "particle_velocity"][0] def _set_io_data(self): halo_fields = self._get_member_fieldnames() my_data = self.index._get_halo_values( self.ptype, np.array([self.particle_identifier]), halo_fields ) self._io_data = {field: np.int64(val[0]) for field, val in my_data.items()} def __repr__(self): return f"{self.ds}_{self.ptype}_{self.particle_identifier:09d}" class YTHaloCatalogHaloContainer(HaloContainer): """ Data container for accessing particles from a halo. Create a data container to get member particles and individual values from halos and subhalos. Halo mass, radius, position, and velocity are set as attributes. Halo IDs are accessible through the field, "member_ids". Other fields that are one value per halo are accessible as normal. The field list for halo objects can be seen in `ds.halos_field_list`. Parameters ---------- ptype : string The type of halo. Possible options can be found by inspecting the value of ds.particle_types_raw. particle_identifier : int The halo id. Examples -------- >>> import yt >>> ds = yt.load("tiny_fof_halos/DD0046/DD0046.0.h5") >>> halo = ds.halo("halos", 0) >>> print(halo.particle_identifier) 0 >>> print(halo.mass) 8724990744704.453 Msun >>> print(halo.radius) 658.8140635766607 kpc >>> print(halo.position) [0.05496909 0.19451951 0.04056824] code_length >>> print(halo.velocity) [7034181.07118151 5323471.09102874 3234522.50495914] cm/s >>> # particle ids for this halo >>> print(halo["member_ids"]) [ 1248. 129. 128. 31999. 31969. 31933. 31934. 159. 31903. 31841. ... 2241. 2240. 2239. 2177. 2209. 2207. 2208.] dimensionless """ def _get_member_fieldnames(self): return ["particle_number", "particle_index_start"] def _get_particle_number(self): return self._io_data["particle_number"] def _set_field_indices(self): self.field_data_start = [self._io_data["particle_index_start"]] self.field_data_end = [self.field_data_start[0] + self.particle_number] def _set_identifiers(self, particle_identifier): self.particle_identifier = particle_identifier self.group_identifier = self.particle_identifier yt-project-yt-f043ac8/yt/frontends/halo_catalog/fields.py000066400000000000000000000016661510711153200235560ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer m_units = "g" p_units = "cm" v_units = "cm / s" r_units = "cm" _particle_fields: KnownFieldsT = ( ("particle_identifier", ("", [], None)), ("particle_position_x", (p_units, [], None)), ("particle_position_y", (p_units, [], None)), ("particle_position_z", (p_units, [], None)), ("particle_velocity_x", (v_units, [], None)), ("particle_velocity_y", (v_units, [], None)), ("particle_velocity_z", (v_units, [], None)), ("particle_mass", (m_units, [], "Virial Mass")), ("virial_radius", (r_units, [], "Virial Radius")), ) class YTHaloCatalogFieldInfo(FieldInfoContainer): known_other_fields = () known_particle_fields = _particle_fields class YTHaloCatalogHaloFieldInfo(FieldInfoContainer): known_other_fields = () known_particle_fields = _particle_fields + (("ids", ("", ["member_ids"], None)),) yt-project-yt-f043ac8/yt/frontends/halo_catalog/io.py000066400000000000000000000151261510711153200227130ustar00rootroot00000000000000from collections import defaultdict import numpy as np from yt.frontends.gadget_fof.io import IOHandlerGadgetFOFHaloHDF5 from yt.funcs import parse_h5_attr from yt.units._numpy_wrapper_functions import uvstack from yt.utilities.io_handler import BaseParticleIOHandler from yt.utilities.on_demand_imports import _h5py as h5py class IOHandlerYTHaloCatalog(BaseParticleIOHandler): _dataset_type = "ythalocatalog" def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" ptype = "halos" pn = "particle_position_%s" for data_file in self._sorted_chunk_iterator(chunks): with h5py.File(data_file.filename, mode="r") as f: units = parse_h5_attr(f[pn % "x"], "units") pos = data_file._get_particle_positions(ptype, f=f) x, y, z = (self.ds.arr(pos[:, i], units) for i in range(3)) yield "halos", (x, y, z), 0.0 def _yield_coordinates(self, data_file): pn = "particle_position_%s" with h5py.File(data_file.filename, mode="r") as f: units = parse_h5_attr(f[pn % "x"], "units") x, y, z = ( self.ds.arr(f[pn % ax][()].astype("float64"), units) for ax in "xyz" ) pos = uvstack([x, y, z]).T pos.convert_to_units("code_length") yield "halos", pos def _read_particle_fields(self, chunks, ptf, selector): # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" pn = "particle_position_%s" for data_file in self._sorted_chunk_iterator(chunks): si, ei = data_file.start, data_file.end with h5py.File(data_file.filename, mode="r") as f: for ptype, field_list in sorted(ptf.items()): units = parse_h5_attr(f[pn % "x"], "units") pos = data_file._get_particle_positions(ptype, f=f) x, y, z = (self.ds.arr(pos[:, i], units) for i in range(3)) mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: data = f[field][si:ei][mask].astype("float64") yield (ptype, field), data def _count_particles(self, data_file): si, ei = data_file.start, data_file.end nhalos = data_file.header["num_halos"] if None not in (si, ei): nhalos = np.clip(nhalos - si, 0, ei - si) return {"halos": nhalos} def _identify_fields(self, data_file): with h5py.File(data_file.filename, mode="r") as f: fields = [ ("halos", field) for field in f if not isinstance(f[field], h5py.Group) ] units = {("halos", field): parse_h5_attr(f[field], "units") for field in f} return fields, units class HaloDatasetIOHandler: """ Base class for io handlers to load halo member particles. """ def _read_particle_coords(self, chunks, ptf): pass def _read_particle_fields(self, dobj, ptf): # separate member particle fields from scalar fields scalar_fields = defaultdict(list) member_fields = defaultdict(list) for ptype, field_list in sorted(ptf.items()): for field in field_list: if (ptype, field) in self.ds.scalar_field_list: scalar_fields[ptype].append(field) else: member_fields[ptype].append(field) all_data = self._read_scalar_fields(dobj, scalar_fields) all_data.update(self._read_member_fields(dobj, member_fields)) for field, field_data in all_data.items(): yield field, field_data # This will be refactored. _read_particle_selection = IOHandlerGadgetFOFHaloHDF5._read_particle_selection # ignoring type in this mixing to circumvent this error from mypy # Definition of "_read_particle_fields" in base class "HaloDatasetIOHandler" # is incompatible with definition in base class "IOHandlerYTHaloCatalog" # # it may not be possible to refactor out of this situation without breaking downstream class IOHandlerYTHalo(HaloDatasetIOHandler, IOHandlerYTHaloCatalog): # type: ignore _dataset_type = "ythalo" def _identify_fields(self, data_file): with h5py.File(data_file.filename, mode="r") as f: scalar_fields = [ ("halos", field) for field in f if not isinstance(f[field], h5py.Group) ] units = {("halos", field): parse_h5_attr(f[field], "units") for field in f} if "particles" in f: id_fields = [("halos", field) for field in f["particles"]] else: id_fields = [] return scalar_fields + id_fields, scalar_fields, id_fields, units def _read_member_fields(self, dobj, member_fields): all_data = defaultdict(lambda: np.empty(dobj.particle_number, dtype=np.float64)) if not member_fields: return all_data field_start = 0 for i, data_file in enumerate(dobj.field_data_files): start_index = dobj.field_data_start[i] end_index = dobj.field_data_end[i] pcount = end_index - start_index if pcount == 0: continue field_end = field_start + end_index - start_index with h5py.File(data_file.filename, mode="r") as f: for ptype, field_list in sorted(member_fields.items()): for field in field_list: field_data = all_data[ptype, field] my_data = f["particles"][field][start_index:end_index].astype( "float64" ) field_data[field_start:field_end] = my_data field_start = field_end return all_data def _read_scalar_fields(self, dobj, scalar_fields): all_data = {} if not scalar_fields: return all_data with h5py.File(dobj.scalar_data_file.filename, mode="r") as f: for ptype, field_list in sorted(scalar_fields.items()): for field in field_list: data = np.array([f[field][dobj.scalar_index]]).astype("float64") all_data[ptype, field] = data return all_data yt-project-yt-f043ac8/yt/frontends/halo_catalog/tests/000077500000000000000000000000001510711153200230675ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/halo_catalog/tests/__init__.py000066400000000000000000000000001510711153200251660ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/halo_catalog/tests/test_outputs.py000066400000000000000000000072111510711153200262240ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal, assert_equal from yt.frontends.halo_catalog.data_structures import YTHaloCatalogDataset from yt.frontends.ytdata.utilities import save_as_dataset from yt.loaders import load as yt_load from yt.testing import TempDirTest, requires_file, requires_module from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.answer_testing.framework import data_dir_load def fake_halo_catalog(data): filename = "catalog.0.h5" ftypes = {field: "." for field in data} extra_attrs = {"data_type": "halo_catalog", "num_halos": data["particle_mass"].size} ds = { "cosmological_simulation": 1, "omega_lambda": 0.7, "omega_matter": 0.3, "hubble_constant": 0.7, "current_redshift": 0, "current_time": YTQuantity(1, "yr"), "domain_left_edge": YTArray(np.zeros(3), "cm"), "domain_right_edge": YTArray(np.ones(3), "cm"), } save_as_dataset(ds, filename, data, field_types=ftypes, extra_attrs=extra_attrs) return filename class HaloCatalogTest(TempDirTest): @requires_module("h5py") def test_halo_catalog(self): rs = np.random.RandomState(3670474) n_halos = 100 fields = ["particle_mass"] + [f"particle_position_{ax}" for ax in "xyz"] units = ["g"] + ["cm"] * 3 data = { field: YTArray(rs.random_sample(n_halos), unit) for field, unit in zip(fields, units, strict=True) } fn = fake_halo_catalog(data) ds = yt_load(fn) assert type(ds) is YTHaloCatalogDataset for field in fields: f1 = data[field].in_base() f1.sort() f2 = ds.r["all", field].in_base() f2.sort() assert_array_equal(f1, f2) @requires_module("h5py") def test_halo_catalog_boundary_particles(self): rs = np.random.RandomState(3670474) n_halos = 100 fields = ["particle_mass"] + [f"particle_position_{ax}" for ax in "xyz"] units = ["g"] + ["cm"] * 3 data = { field: YTArray(rs.random_sample(n_halos), unit) for field, unit in zip(fields, units, strict=True) } data["particle_position_x"][0] = 1.0 data["particle_position_x"][1] = 0.0 data["particle_position_y"][2] = 1.0 data["particle_position_y"][3] = 0.0 data["particle_position_z"][4] = 1.0 data["particle_position_z"][5] = 0.0 fn = fake_halo_catalog(data) ds = yt_load(fn) assert type(ds) is YTHaloCatalogDataset for field in fields: f1 = data[field].in_base() f1.sort() f2 = ds.r["all", field].in_base() f2.sort() assert_array_equal(f1, f2) t46 = "tiny_fof_halos/DD0046/DD0046.0.h5" @requires_file(t46) @requires_module("h5py") def test_halo_quantities(): ds = data_dir_load(t46) ad = ds.all_data() for i in range(ds.index.total_particles): hid = int(ad["halos", "particle_identifier"][i]) halo = ds.halo("halos", hid) for field in ["mass", "position", "velocity"]: v1 = ad["halos", f"particle_{field}"][i] v2 = getattr(halo, field) assert_equal(v1, v2, err_msg=f"Halo {hid} {field} field mismatch.") @requires_file(t46) @requires_module("h5py") def test_halo_particles(): ds = data_dir_load(t46) i = ds.r["halos", "particle_mass"].argmax() hid = int(ds.r["halos", "particle_identifier"][i]) halo = ds.halo("halos", hid) ids = halo["halos", "member_ids"] assert_equal(ids.size, 420) assert_equal(ids.min(), 19478.0) assert_equal(ids.max(), 31669.0) yt-project-yt-f043ac8/yt/frontends/http_stream/000077500000000000000000000000001510711153200216425ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/http_stream/__init__.py000066400000000000000000000000001510711153200237410ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/http_stream/api.py000066400000000000000000000001231510711153200227610ustar00rootroot00000000000000from .data_structures import HTTPStreamDataset from .io import IOHandlerHTTPStream yt-project-yt-f043ac8/yt/frontends/http_stream/data_structures.py000066400000000000000000000066421510711153200254400ustar00rootroot00000000000000import json import time from functools import cached_property import numpy as np from yt.data_objects.static_output import ParticleDataset, ParticleFile from yt.frontends.sph.fields import SPHFieldInfo from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.on_demand_imports import _requests as requests class HTTPParticleFile(ParticleFile): pass class HTTPStreamDataset(ParticleDataset): _load_requirements = ["requests"] _index_class = ParticleIndex _file_class = HTTPParticleFile _field_info_class = SPHFieldInfo _particle_mass_name = "Mass" _particle_coordinates_name = "Coordinates" _particle_velocity_name = "Velocities" filename_template = "" def __init__( self, base_url, dataset_type="http_particle_stream", unit_system="cgs", index_order=None, index_filename=None, ): self.base_url = base_url super().__init__( "", dataset_type=dataset_type, unit_system=unit_system, index_order=index_order, index_filename=index_filename, ) def __str__(self): return self.base_url @cached_property def unique_identifier(self) -> str: return str(self.parameters.get("unique_identifier", time.time())) def _parse_parameter_file(self): self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "sph" # Here's where we're going to grab the JSON index file hreq = requests.get(self.base_url + "/yt_index.json") if hreq.status_code != 200: raise RuntimeError header = json.loads(hreq.content) header["particle_count"] = { int(k): header["particle_count"][k] for k in header["particle_count"] } self.parameters = header # Now we get what we need self.domain_left_edge = np.array(header["domain_left_edge"], "float64") self.domain_right_edge = np.array(header["domain_right_edge"], "float64") self.domain_dimensions = np.ones(3, "int32") self._periodicity = (True, True, True) self.current_time = header["current_time"] self.cosmological_simulation = int(header["cosmological_simulation"]) for attr in ( "current_redshift", "omega_lambda", "omega_matter", "hubble_constant", ): setattr(self, attr, float(header[attr])) self.file_count = header["num_files"] def _set_units(self): length_unit = float(self.parameters["units"]["length"]) time_unit = float(self.parameters["units"]["time"]) mass_unit = float(self.parameters["units"]["mass"]) density_unit = mass_unit / length_unit**3 velocity_unit = length_unit / time_unit self._unit_base = {} self._unit_base["cm"] = 1.0 / length_unit self._unit_base["s"] = 1.0 / time_unit super()._set_units() self.conversion_factors["velocity"] = velocity_unit self.conversion_factors["mass"] = mass_unit self.conversion_factors["density"] = density_unit @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.startswith("http://"): return False if cls._missing_load_requirements(): return False return requests.get(filename + "/yt_index.json").status_code == 200 yt-project-yt-f043ac8/yt/frontends/http_stream/io.py000066400000000000000000000047411510711153200226310ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.utilities.io_handler import BaseParticleIOHandler from yt.utilities.on_demand_imports import _requests as requests class IOHandlerHTTPStream(BaseParticleIOHandler): _dataset_type = "http_particle_stream" _vector_fields = {"Coordinates": 3, "Velocity": 3, "Velocities": 3} def __init__(self, ds): self._url = ds.base_url # This should eventually manage the IO and cache it self.total_bytes = 0 super().__init__(ds) def _open_stream(self, data_file, field): # This does not actually stream yet! ftype, fname = field s = f"{self._url}/{data_file.file_id}/{ftype}/{fname}" mylog.info("Loading URL %s", s) resp = requests.get(s) if resp.status_code != 200: raise RuntimeError self.total_bytes += len(resp.content) return resp.content def _identify_fields(self, data_file): f = [] for ftype, fname in self.ds.parameters["field_list"]: f.append((str(ftype), str(fname))) return f, {} def _read_particle_coords(self, chunks, ptf): for data_file in self._sorted_chunk_iterator(chunks): for ptype in ptf: s = self._open_stream(data_file, (ptype, "Coordinates")) c = np.frombuffer(s, dtype="float64") c = c.reshape(c.size // 3, 3) yield ptype, (c[:, 0], c[:, 1], c[:, 2]), 0.0 def _read_particle_fields(self, chunks, ptf, selector): # Now we have all the sizes, and we can allocate for data_file in self._sorted_chunk_iterator(chunks): for ptype, field_list in sorted(ptf.items()): s = self._open_stream(data_file, (ptype, "Coordinates")) c = np.frombuffer(s, dtype="float64") c = c.reshape(c.size // 3, 3) mask = selector.select_points(c[:, 0], c[:, 1], c[:, 2], 0.0) del c if mask is None: continue for field in field_list: s = self._open_stream(data_file, (ptype, field)) c = np.frombuffer(s, dtype="float64") if field in self._vector_fields: c = c.reshape(c.size // 3, 3) data = c[mask, ...] yield (ptype, field), data def _count_particles(self, data_file): return self.ds.parameters["particle_count"][data_file.file_id] yt-project-yt-f043ac8/yt/frontends/moab/000077500000000000000000000000001510711153200202265ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/moab/__init__.py000066400000000000000000000000431510711153200223340ustar00rootroot00000000000000""" Empty __init__.py file. """ yt-project-yt-f043ac8/yt/frontends/moab/api.py000066400000000000000000000003421510711153200213500ustar00rootroot00000000000000from . import tests from .data_structures import ( MoabHex8Dataset, MoabHex8Hierarchy, MoabHex8Mesh, PyneMoabHex8Dataset, ) from .fields import MoabFieldInfo, PyneFieldInfo from .io import IOHandlerMoabH5MHex8 yt-project-yt-f043ac8/yt/frontends/moab/data_structures.py000066400000000000000000000157441510711153200240270ustar00rootroot00000000000000import os import weakref from functools import cached_property import numpy as np from yt.data_objects.index_subobjects.unstructured_mesh import SemiStructuredMesh from yt.data_objects.static_output import Dataset from yt.funcs import setdefaultattr from yt.geometry.unstructured_mesh_handler import UnstructuredIndex from yt.utilities.file_handler import HDF5FileHandler from yt.utilities.on_demand_imports import _h5py as h5py from .fields import MoabFieldInfo, PyneFieldInfo class MoabHex8Mesh(SemiStructuredMesh): _connectivity_length = 8 _index_offset = 1 class MoabHex8Hierarchy(UnstructuredIndex): def __init__(self, ds, dataset_type="h5m"): self.dataset = weakref.proxy(ds) self.dataset_type = dataset_type self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self._fhandle = h5py.File(self.index_filename, mode="r") UnstructuredIndex.__init__(self, ds, dataset_type) self._fhandle.close() def _initialize_mesh(self): con = self._fhandle["/tstt/elements/Hex8/connectivity"][:] con = np.asarray(con, dtype="int64") coords = self._fhandle["/tstt/nodes/coordinates"][:] coords = np.asarray(coords, dtype="float64") self.meshes = [MoabHex8Mesh(0, self.index_filename, con, coords, self)] def _detect_output_fields(self): self.field_list = [ ("moab", f) for f in self._fhandle["/tstt/elements/Hex8/tags"].keys() ] def _count_grids(self): self.num_grids = 1 class MoabHex8Dataset(Dataset): _load_requirements = ["h5py"] _index_class = MoabHex8Hierarchy _field_info_class = MoabFieldInfo periodicity = (False, False, False) def __init__( self, filename, dataset_type="moab_hex8", storage_filename=None, units_override=None, unit_system="cgs", ): self.fluid_types += ("moab",) Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename self._handle = HDF5FileHandler(filename) def _set_code_unit_attributes(self): # Almost everything is regarded as dimensionless in MOAB, so these will # not be used very much or at all. setdefaultattr(self, "length_unit", self.quan(1.0, "cm")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) def _parse_parameter_file(self): self._handle = h5py.File(self.parameter_filename, mode="r") coords = self._handle["/tstt/nodes/coordinates"] self.domain_left_edge = coords[0] self.domain_right_edge = coords[-1] self.domain_dimensions = self.domain_right_edge - self.domain_left_edge self.refine_by = 2 self.dimensionality = len(self.domain_dimensions) self.current_time = 0.0 self.cosmological_simulation = False self.num_ghost_zones = 0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return filename.endswith(".h5m") and not cls._missing_load_requirements() def __str__(self): return self.basename.rsplit(".", 1)[0] class PyneHex8Mesh(SemiStructuredMesh): _connectivity_length = 8 _index_offset = 0 class PyneMeshHex8Hierarchy(UnstructuredIndex): def __init__(self, ds, dataset_type="moab_hex8_pyne"): self.dataset = weakref.proxy(ds) self.dataset_type = dataset_type self.index_filename = self.dataset.parameter_filename self.directory = os.getcwd() self.pyne_mesh = ds.pyne_mesh super().__init__(ds, dataset_type) def _initialize_mesh(self): from pymoab import types ents = list(self.pyne_mesh.structured_iterate_vertex()) coords = self.pyne_mesh.mesh.get_coords(ents).astype("float64") coords = coords.reshape(len(coords) // 3, 3) hexes = self.pyne_mesh.mesh.get_entities_by_type(0, types.MBHEX) vind = [] for h in hexes: vind.append( self.pyne_mesh.mesh.get_adjacencies( h, 0, create_if_missing=True, op_type=types.UNION ) ) vind = np.asarray(vind, dtype=np.int64) if vind.ndim == 1: vind = vind.reshape(len(vind) // 8, 8) assert vind.ndim == 2 and vind.shape[1] == 8 self.meshes = [PyneHex8Mesh(0, self.index_filename, vind, coords, self)] def _detect_output_fields(self): self.field_list = [("pyne", f) for f in self.pyne_mesh.tags.keys()] def _count_grids(self): self.num_grids = 1 class PyneMoabHex8Dataset(Dataset): _index_class = PyneMeshHex8Hierarchy _fieldinfo_fallback = MoabFieldInfo _field_info_class = PyneFieldInfo periodicity = (False, False, False) def __init__( self, pyne_mesh, dataset_type="moab_hex8_pyne", storage_filename=None, units_override=None, unit_system="cgs", ): self.fluid_types += ("pyne",) filename = f"pyne_mesh_{id(pyne_mesh)}" self.pyne_mesh = pyne_mesh Dataset.__init__( self, str(filename), dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename @property def filename(self) -> str: return self._input_filename @cached_property def unique_identifier(self) -> str: return self.filename def _set_code_unit_attributes(self): # Almost everything is regarded as dimensionless in MOAB, so these will # not be used very much or at all. setdefaultattr(self, "length_unit", self.quan(1.0, "cm")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "mass_unit", self.quan(1.0, "g")) def _parse_parameter_file(self): ents = list(self.pyne_mesh.structured_iterate_vertex()) coords = self.pyne_mesh.mesh.get_coords(ents) self.domain_left_edge = coords[0:3] self.domain_right_edge = coords[-3:] self.domain_dimensions = self.domain_right_edge - self.domain_left_edge self.refine_by = 2 self.dimensionality = len(self.domain_dimensions) self.current_time = 0.0 self.cosmological_simulation = False self.num_ghost_zones = 0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return False def __str__(self): return self.basename.rsplit(".", 1)[0] yt-project-yt-f043ac8/yt/frontends/moab/definitions.py000066400000000000000000000000001510711153200231010ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/moab/fields.py000066400000000000000000000002461510711153200220500ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer class MoabFieldInfo(FieldInfoContainer): pass class PyneFieldInfo(FieldInfoContainer): pass yt-project-yt-f043ac8/yt/frontends/moab/io.py000066400000000000000000000046401510711153200212130ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.utilities.io_handler import BaseIOHandler def field_dname(field_name): return f"/tstt/elements/Hex8/tags/{field_name}" # TODO all particle bits were removed class IOHandlerMoabH5MHex8(BaseIOHandler): _dataset_type = "moab_hex8" def __init__(self, ds): super().__init__(ds) self._handle = ds._handle def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) assert len(chunks) == 1 fhandle = self._handle rv = {} for field in fields: ftype, fname = field rv[field] = np.empty(size, dtype=fhandle[field_dname(fname)].dtype) ngrids = sum(len(chunk.objs) for chunk in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [fname for ft, fn in fields], ngrids, ) for field in fields: ftype, fname = field ds = np.array(fhandle[field_dname(fname)][:], dtype="float64") ind = 0 for chunk in chunks: for g in chunk.objs: ind += g.select(selector, ds, rv[field], ind) # caches return rv class IOHandlerMoabPyneHex8(BaseIOHandler): _dataset_type = "moab_hex8_pyne" def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) assert len(chunks) == 1 rv = {} pyne_mesh = self.ds.pyne_mesh for field in fields: rv[field] = np.empty(size, dtype="float64") ngrids = sum(len(chunk.objs) for chunk in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [fname for ftype, fname in fields], ngrids, ) for field in fields: ftype, fname = field if pyne_mesh.structured: tag = pyne_mesh.mesh.tag_get_handle("idx") hex_list = list(pyne_mesh.structured_iterate_hex()) indices = pyne_mesh.mesh.tag_get_data(tag, hex_list).flatten() else: indices = slice(None) ds = np.asarray(getattr(pyne_mesh, fname)[indices], "float64") ind = 0 for chunk in chunks: for g in chunk.objs: ind += g.select(selector, ds, rv[field], ind) # caches return rv yt-project-yt-f043ac8/yt/frontends/moab/misc.py000066400000000000000000000000001510711153200215210ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/moab/tests/000077500000000000000000000000001510711153200213705ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/moab/tests/__init__.py000066400000000000000000000000001510711153200234670ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/moab/tests/test_c5.py000066400000000000000000000033361510711153200233150ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.frontends.moab.api import MoabHex8Dataset from yt.testing import requires_file, requires_module, units_override_check from yt.utilities.answer_testing.framework import ( FieldValuesTest, data_dir_load, requires_ds, ) _fields = (("moab", "flux"),) c5 = "c5/c5.h5m" @requires_module("h5py") @requires_ds(c5) def test_cantor_5(): np.random.seed(0x4D3D3D3) ds = data_dir_load(c5) assert_equal(str(ds), "c5") dso = [ None, ("sphere", ("c", (0.1, "unitary"))), ("sphere", ("c", (0.2, "unitary"))), ] dd = ds.all_data() assert_almost_equal(ds.index.get_smallest_dx(), 0.00411522633744843, 10) assert_equal(dd["gas", "x"].shape[0], 63 * 63 * 63) assert_almost_equal( dd["index", "cell_volume"].in_units("code_length**3").sum(dtype="float64").d, 1.0, 10, ) for offset_1 in [1e-9, 1e-4, 0.1]: for offset_2 in [1e-9, 1e-4, 0.1]: DLE = ds.domain_left_edge DRE = ds.domain_right_edge ray = ds.ray(DLE + offset_1 * DLE.uq, DRE - offset_2 * DRE.uq) assert_almost_equal(ray["dts"].sum(dtype="float64"), 1.0, 8) for p1 in np.random.random((5, 3)): for p2 in np.random.random((5, 3)): ray = ds.ray(p1, p2) assert_almost_equal(ray["dts"].sum(dtype="float64"), 1.0, 8) for field in _fields: for dobj_name in dso: yield FieldValuesTest(c5, field, dobj_name) @requires_module("h5py") @requires_file(c5) def test_MoabHex8Dataset(): assert isinstance(data_dir_load(c5), MoabHex8Dataset) @requires_file(c5) def test_units_override(): units_override_check(c5) yt-project-yt-f043ac8/yt/frontends/nc4_cm1/000077500000000000000000000000001510711153200205345ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/nc4_cm1/__init__.py000066400000000000000000000000001510711153200226330ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/nc4_cm1/api.py000066400000000000000000000002461510711153200216610ustar00rootroot00000000000000""" API for yt.frontends.nc4_cm1 """ from .data_structures import CM1Dataset, CM1Grid, CM1Hierarchy from .fields import CM1FieldInfo from .io import CM1IOHandler yt-project-yt-f043ac8/yt/frontends/nc4_cm1/data_structures.py000066400000000000000000000221121510711153200243200ustar00rootroot00000000000000import os import weakref from collections import OrderedDict import numpy as np from yt._typing import AxisOrder from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.file_handler import NetCDF4FileHandler, valid_netcdf_signature from yt.utilities.logger import ytLogger as mylog from .fields import CM1FieldInfo class CM1Grid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level, dimensions): super().__init__(id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level self.ActiveDimensions = dimensions class CM1Hierarchy(GridIndex): grid = CM1Grid def __init__(self, ds, dataset_type="cm1"): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) # for now, the index file is the dataset! self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) # float type for the simulation edges and must be float64 now self.float_type = np.float64 super().__init__(ds, dataset_type) def _detect_output_fields(self): # build list of on-disk fields for dataset_type 'cm1' vnames = self.dataset.parameters["variable_names"] self.field_list = [("cm1", vname) for vname in vnames] def _count_grids(self): # This needs to set self.num_grids self.num_grids = 1 def _parse_index(self): self.grid_left_edge[0][:] = self.ds.domain_left_edge[:] self.grid_right_edge[0][:] = self.ds.domain_right_edge[:] self.grid_dimensions[0][:] = self.ds.domain_dimensions[:] self.grid_particle_count[0][0] = 0 self.grid_levels[0][0] = 0 self.max_level = 0 def _populate_grid_objects(self): self.grids = np.empty(self.num_grids, dtype="object") for i in range(self.num_grids): g = self.grid(i, self, self.grid_levels.flat[i], self.grid_dimensions[i]) g._prepare_grid() g._setup_dx() self.grids[i] = g class CM1Dataset(Dataset): _load_requirements = ["netCDF4"] _index_class = CM1Hierarchy _field_info_class = CM1FieldInfo def __init__( self, filename, dataset_type="cm1", storage_filename=None, units_override=None, unit_system="mks", ): self.fluid_types += ("cm1",) self._handle = NetCDF4FileHandler(filename) # refinement factor between a grid and its subgrid. self.refine_by = 1 super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename def _setup_coordinate_handler(self, axis_order: AxisOrder | None) -> None: # ensure correct ordering of axes so plots aren't rotated (z should always be # on the vertical axis). super()._setup_coordinate_handler(axis_order) # type checking is deactivated in the following two lines because changing them is not # within the scope of the PR that _enabled_ typechecking here (#4244), but it'd be worth # having a careful look at *why* these warnings appear, as they may point to rotten code self.coordinates._x_pairs = (("x", "y"), ("y", "x"), ("z", "x")) # type: ignore [union-attr] self.coordinates._y_pairs = (("x", "z"), ("y", "z"), ("z", "y")) # type: ignore [union-attr] def _set_code_unit_attributes(self): # This is where quantities are created that represent the various # on-disk units. These are the currently available quantities which # should be set, along with examples of how to set them to standard # values. with self._handle.open_ds() as _handle: length_unit = _handle.variables["xh"].units self.length_unit = self.quan(1.0, length_unit) self.mass_unit = self.quan(1.0, "kg") self.time_unit = self.quan(1.0, "s") self.velocity_unit = self.quan(1.0, "m/s") self.time_unit = self.quan(1.0, "s") def _parse_parameter_file(self): # This needs to set up the following items. Note that these are all # assumed to be in code units; domain_left_edge and domain_right_edge # will be converted to YTArray automatically at a later time. # This includes the cosmological parameters. self.parameters = {} # code-specific items with self._handle.open_ds() as _handle: # _handle here is a netcdf Dataset object, we need to parse some metadata # for constructing our yt ds. # TO DO: generalize this to be coordinate variable name agnostic in order to # make useful for WRF or climate data. For now, we're hard coding for CM1 # specifically and have named the classes appropriately. Additionally, we # are only handling the cell-centered grid ("xh","yh","zh") at present. # The cell-centered grid contains scalar fields and interpolated velocities. dims = [_handle.dimensions[i].size for i in ["xh", "yh", "zh"]] xh, yh, zh = (_handle.variables[i][:] for i in ["xh", "yh", "zh"]) self.domain_left_edge = np.array( [xh.min(), yh.min(), zh.min()], dtype="float64" ) self.domain_right_edge = np.array( [xh.max(), yh.max(), zh.max()], dtype="float64" ) # loop over the variable names in the netCDF file, record only those on the # "zh","yh","xh" grid. varnames = [] for key, var in _handle.variables.items(): if all(x in var.dimensions for x in ["time", "zh", "yh", "xh"]): varnames.append(key) self.parameters["variable_names"] = varnames self.parameters["lofs_version"] = _handle.cm1_lofs_version self.parameters["is_uniform"] = _handle.uniform_mesh self.current_time = _handle.variables["time"][:][0] # record the dimension metadata: __handle.dimensions contains netcdf # objects so we need to manually copy over attributes. dim_info = OrderedDict() for dim, meta in _handle.dimensions.items(): dim_info[dim] = meta.size self.parameters["dimensions"] = dim_info self.dimensionality = 3 self.domain_dimensions = np.array(dims, dtype="int64") self._periodicity = (False, False, False) # Set cosmological information to zero for non-cosmological. self.cosmological_simulation = 0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: # This accepts a filename or a set of arguments and returns True or # False depending on if the file is of the type requested. if not valid_netcdf_signature(filename): return False if cls._missing_load_requirements(): return False try: nc4_file = NetCDF4FileHandler(filename) with nc4_file.open_ds(keepweakref=True) as _handle: is_cm1_lofs = hasattr(_handle, "cm1_lofs_version") is_cm1 = hasattr(_handle, "cm1 version") # not a typo, it is a space... # ensure coordinates of each variable array exists in the dataset coords = _handle.dimensions # get the dataset wide coordinates failed_vars = [] # list of failed variables for var in _handle.variables: # iterate over the variables vcoords = _handle[var].dimensions # get the dims for the variable ncoords = len(vcoords) # number of coordinates in variable # number of coordinates that pass for a variable coordspassed = sum(vc in coords for vc in vcoords) if coordspassed != ncoords: failed_vars.append(var) if failed_vars: mylog.warning( "Trying to load a cm1_lofs netcdf file but the " "coordinates of the following fields do not match the " "coordinates of the dataset: %s", failed_vars, ) return False if not is_cm1_lofs: if is_cm1: mylog.warning( "It looks like you are trying to load a cm1 netcdf file, " "but at present yt only supports cm1_lofs output. Until" " support is added, you can likely use" " yt.load_uniform_grid() to load your cm1 file manually." ) return False except (OSError, AttributeError, ImportError): return False return True yt-project-yt-f043ac8/yt/frontends/nc4_cm1/fields.py000066400000000000000000000063021510711153200223550ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer # We need to specify which fields we might have in our dataset. The field info # container subclass here will define which fields it knows about. There are # optionally methods on it that get called which can be subclassed. class CM1FieldInfo(FieldInfoContainer): known_other_fields = ( # Each entry here is of the form # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ("uinterp", ("m/s", ["velocity_x"], None)), ("vinterp", ("m/s", ["velocity_y"], None)), ("winterp", ("m/s", ["velocity_z"], None)), ("u", ("m/s", ["velocity_x"], None)), ("v", ("m/s", ["velocity_y"], None)), ("w", ("m/s", ["velocity_z"], None)), ("hwin_sr", ("m/s", ["storm_relative_horizontal_wind_speed"], None)), ("windmag_sr", ("m/s", ["storm_relative_3D_wind_speed"], None)), ("hwin_gr", ("m/s", ["ground_relative_horizontal_wind_speed"], None)), ("thpert", ("K", ["potential_temperature_perturbation"], None)), ("thrhopert", ("K", ["density_potential_temperature_perturbation"], None)), ("prespert", ("hPa", ["presure_perturbation"], None)), ("rhopert", ("kg/m**3", ["density_perturbation"], None)), ("dbz", ("dB", ["simulated_reflectivity"], None)), ("qvpert", ("g/kg", ["water_vapor_mixing_ratio_perturbation"], None)), ("qc", ("g/kg", ["cloud_liquid_water_mixing_ratio"], None)), ("qr", ("g/kg", ["rain_mixing_ratio"], None)), ("qi", ("g/kg", ["cloud_ice_mixing_ratio"], None)), ("qs", ("g/kg", ["snow_mixing_ratio"], None)), ("qg", ("g/kg", ["graupel_or_hail_mixing_ratio"], None)), ("qcloud", ("g/kg", ["sum_of_cloud_water_and_cloud_ice_mixing_ratios"], None)), ("qprecip", ("g/kg", ["sum_of_rain_graupel_snow_mixing_ratios"], None)), ("nci", ("1/cm**3", ["number_concerntration_of_cloud_ice"], None)), ("ncr", ("1/cm**3", ["number_concentration_of_rain"], None)), ("ncs", ("1/cm**3", ["number_concentration_of_snow"], None)), ("ncg", ("1/cm**3", ["number_concentration_of_graupel_or_hail"], None)), ("xvort", ("1/s", ["vorticity_x"], None)), ("yvort", ("1/s", ["vorticity_y"], None)), ("zvort", ("1/s", ["vorticity_z"], None)), ("hvort", ("1/s", ["horizontal_vorticity_magnitude"], None)), ("vortmag", ("1/s", ["vorticity_magnitude"], None)), ("streamvort", ("1/s", ["streamwise_vorticity"], None)), ("khh", ("m**2/s", ["khh"], None)), ("khv", ("m**2/s", ["khv"], None)), ("kmh", ("m**2/s", ["kmh"], None)), ("kmv", ("m**2/s", ["kmv"], None)), ) known_particle_fields = ( # Identical form to above # ( "name", ("units", ["fields", "to", "alias"], # "display_name")), ) def setup_fluid_fields(self): # Here we do anything that might need info about the dataset. # You can use self.alias, self.add_output_field (for on-disk fields) # and self.add_field (for derived fields). pass def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) # This will get called for every particle type. yt-project-yt-f043ac8/yt/frontends/nc4_cm1/io.py000066400000000000000000000055541510711153200215260ustar00rootroot00000000000000import numpy as np from yt.utilities.file_handler import NetCDF4FileHandler from yt.utilities.io_handler import BaseIOHandler class CM1IOHandler(BaseIOHandler): _particle_reader = False _dataset_type = "cm1" def __init__(self, ds): self.filename = ds.filename self._handle = NetCDF4FileHandler(self.filename) super().__init__(ds) def _read_particle_coords(self, chunks, ptf): # This needs to *yield* a series of tuples of (ptype, (x, y, z)). # chunks is a list of chunks, and ptf is a dict where the keys are # ptypes and the values are lists of fields. raise NotImplementedError def _read_particle_fields(self, chunks, ptf, selector): # This gets called after the arrays have been allocated. It needs to # yield ((ptype, field), data) where data is the masked results of # reading ptype, field and applying the selector to the data read in. # Selector objects have a .select_points(x,y,z) that returns a mask, so # you need to do your masking here. raise NotImplementedError def _read_fluid_selection(self, chunks, selector, fields, size): # This needs to allocate a set of arrays inside a dictionary, where the # keys are the (ftype, fname) tuples and the values are arrays that # have been masked using whatever selector method is appropriate. The # dict gets returned at the end and it should be flat, with selected # data. Note that if you're reading grid data, you might need to # special-case a grid selector object. # Also note that "chunks" is a generator for multiple chunks, each of # which contains a list of grids. The returned numpy arrays should be # in 64-bit float and contiguous along the z direction. Therefore, for # a C-like input array with the dimension [x][y][z] or a # Fortran-like input array with the dimension (z,y,x), a matrix # transpose is required (e.g., using np_array.transpose() or # np_array.swapaxes(0,2)). data = {} chunks = list(chunks) with self._handle.open_ds() as ds: for field in fields: data[field] = np.empty(size, dtype="float64") offset = 0 for chunk in chunks: for grid in chunk.objs: variable = ds.variables[field[1]][:][0] values = np.squeeze(variable.T) offset += grid.select(selector, values, data[field], offset) return data def _read_chunk_data(self, chunk, fields): # This reads the data from a single chunk without doing any selection, # and is only used for caching data that might be used by multiple # different selectors later. For instance, this can speed up ghost zone # computation. pass yt-project-yt-f043ac8/yt/frontends/nc4_cm1/tests/000077500000000000000000000000001510711153200216765ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/nc4_cm1/tests/__init__.py000066400000000000000000000000001510711153200237750ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/nc4_cm1/tests/test_outputs.py000066400000000000000000000040731510711153200250360ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.frontends.nc4_cm1.api import CM1Dataset from yt.testing import requires_file, units_override_check from yt.utilities.answer_testing.framework import ( FieldValuesTest, GridValuesTest, can_run_ds, data_dir_load, requires_ds, small_patch_amr, ) _fields = (("cm1", "thrhopert"), ("cm1", "zvort")) cm1sim = "cm1_tornado_lofs/nc4_cm1_lofs_tornado_test.nc" @requires_ds(cm1sim) def test_cm1_mesh_fields(): ds = data_dir_load(cm1sim) assert_equal(str(ds), "nc4_cm1_lofs_tornado_test.nc") # run the small_patch_amr tests on safe fields ic = ds.domain_center for test in small_patch_amr(ds, _fields, input_center=ic, input_weight=None): test_cm1_mesh_fields.__name__ = test.description yield test # manually run the Grid and Field Values tests on dbz (do not want to run the # ProjectionValuesTest for this field) if can_run_ds(ds): dso = [None, ("sphere", (ic, (0.1, "unitary")))] for field in [("cm1", "dbz")]: yield GridValuesTest(ds, field) for dobj_name in dso: yield FieldValuesTest(ds, field, dobj_name) @requires_file(cm1sim) def test_CM1Dataset(): assert isinstance(data_dir_load(cm1sim), CM1Dataset) @requires_file(cm1sim) def test_units_override(): units_override_check(cm1sim) @requires_file(cm1sim) def test_dims_and_meta(): ds = data_dir_load(cm1sim) known_dims = ["time", "zf", "zh", "yf", "yh", "xf", "xh"] dims = ds.parameters["dimensions"] ## check the file for 2 grids and a time dimension - ## (time, xf, xh, yf, yh, zf, zh). The dimensions ending in ## f are the staggered velocity grid components and the ## dimensions ending in h are the scalar grid components assert_equal(len(dims), len(known_dims)) for kdim in known_dims: assert kdim in dims ## check the simulation time assert_equal(ds.current_time, 5500.0) @requires_file(cm1sim) def test_if_cm1(): ds = data_dir_load(cm1sim) assert float(ds.parameters["lofs_version"]) >= 1.0 yt-project-yt-f043ac8/yt/frontends/open_pmd/000077500000000000000000000000001510711153200211115ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/open_pmd/__init__.py000066400000000000000000000000001510711153200232100ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/open_pmd/api.py000066400000000000000000000002511510711153200222320ustar00rootroot00000000000000from . import tests from .data_structures import OpenPMDDataset, OpenPMDGrid, OpenPMDHierarchy from .fields import OpenPMDFieldInfo from .io import IOHandlerOpenPMDHDF5 yt-project-yt-f043ac8/yt/frontends/open_pmd/data_structures.py000066400000000000000000000657611510711153200247160ustar00rootroot00000000000000from functools import reduce from operator import mul from os import listdir, path from re import match import numpy as np from packaging.version import Version from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.data_objects.time_series import DatasetSeries from yt.frontends.open_pmd.fields import OpenPMDFieldInfo from yt.frontends.open_pmd.misc import get_component, is_const_component from yt.funcs import setdefaultattr from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.file_handler import HDF5FileHandler, valid_hdf5_signature from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py ompd_known_versions = [Version(_) for _ in ("1.0.0", "1.0.1", "1.1.0")] opmd_required_attributes = ["openPMD", "basePath"] class OpenPMDGrid(AMRGridPatch): """Represents chunk of data on-disk. This defines the index and offset for every mesh and particle type. It also defines parents and children grids. Since openPMD does not have multiple levels of refinement there are no parents or children for any grid. """ _id_offset = 0 __slots__ = ["_level_id"] # Every particle species and mesh might have different hdf5-indices and offsets ftypes: list[str] | None = [] ptypes: list[str] | None = [] findex = 0 foffset = 0 pindex = 0 poffset = 0 def __init__(self, gid, index, level=-1, fi=0, fo=0, pi=0, po=0, ft=None, pt=None): AMRGridPatch.__init__(self, gid, filename=index.index_filename, index=index) if ft is None: ft = [] if pt is None: pt = [] self.findex = fi self.foffset = fo self.pindex = pi self.poffset = po self.ftypes = ft self.ptypes = pt self.Parent = None self.Children = [] self.Level = level def __str__(self): return "OpenPMDGrid_%04i (%s)" % (self.id, self.ActiveDimensions) class OpenPMDHierarchy(GridIndex): """Defines which fields and particles are created and read from disk. Furthermore it defines the characteristics of the grids. """ grid = OpenPMDGrid def __init__(self, ds, dataset_type="openPMD"): self.dataset_type = dataset_type self.dataset = ds self.index_filename = ds.parameter_filename self.directory = path.dirname(self.index_filename) GridIndex.__init__(self, ds, dataset_type) def _get_particle_type_counts(self): """Reads the active number of particles for every species. Returns ------- dict keys are ptypes values are integer counts of the ptype """ result = {} f = self.dataset._handle bp = self.dataset.base_path pp = self.dataset.particles_path try: for ptype in self.ds.particle_types_raw: if str(ptype) == "io": spec = list(f[bp + pp].keys())[0] else: spec = ptype axis = list(f[bp + pp + "/" + spec + "/position"].keys())[0] pos = f[bp + pp + "/" + spec + "/position/" + axis] if is_const_component(pos): result[ptype] = pos.attrs["shape"] else: result[ptype] = pos.len() except KeyError: result["io"] = 0 return result def _detect_output_fields(self): """Populates ``self.field_list`` with native fields (mesh and particle) on disk. Each entry is a tuple of two strings. The first element is the on-disk fluid type or particle type. The second element is the name of the field in yt. This string is later used for accessing the data. Convention suggests that the on-disk fluid type should be "openPMD", the on-disk particle type (for a single species of particles) is "io" or (for multiple species of particles) the particle name on-disk. """ f = self.dataset._handle bp = self.dataset.base_path mp = self.dataset.meshes_path pp = self.dataset.particles_path mesh_fields = [] try: meshes = f[bp + mp] for mname in meshes.keys(): try: mesh = meshes[mname] for axis in mesh.keys(): mesh_fields.append(mname.replace("_", "-") + "_" + axis) except AttributeError: # This is a h5py.Dataset (i.e. no axes) mesh_fields.append(mname.replace("_", "-")) except (KeyError, TypeError, AttributeError): pass self.field_list = [("openPMD", str(field)) for field in mesh_fields] particle_fields = [] try: particles = f[bp + pp] for pname in particles.keys(): species = particles[pname] for recname in species.keys(): record = species[recname] if is_const_component(record): # Record itself (e.g. particle_mass) is constant particle_fields.append( pname.replace("_", "-") + "_" + recname.replace("_", "-") ) elif "particlePatches" not in recname: try: # Create a field for every axis (x,y,z) of every # property (position) of every species (electrons) axes = list(record.keys()) if str(recname) == "position": recname = "positionCoarse" for axis in axes: particle_fields.append( pname.replace("_", "-") + "_" + recname.replace("_", "-") + "_" + axis ) except AttributeError: # Record is a dataset, does not have axes (e.g. weighting) particle_fields.append( pname.replace("_", "-") + "_" + recname.replace("_", "-") ) pass else: pass if len(list(particles.keys())) > 1: # There is more than one particle species, # use the specific names as field types self.field_list.extend( [ ( str(field).split("_")[0], ("particle_" + "_".join(str(field).split("_")[1:])), ) for field in particle_fields ] ) else: # Only one particle species, fall back to "io" self.field_list.extend( [ ("io", ("particle_" + "_".join(str(field).split("_")[1:]))) for field in particle_fields ] ) except (KeyError, TypeError, AttributeError): pass def _count_grids(self): """Sets ``self.num_grids`` to be the total number of grids in the simulation. The number of grids is determined by their respective memory footprint. """ f = self.dataset._handle bp = self.dataset.base_path mp = self.dataset.meshes_path pp = self.dataset.particles_path self.meshshapes = {} self.numparts = {} self.num_grids = 0 try: meshes = f[bp + mp] for mname in meshes.keys(): mesh = meshes[mname] if isinstance(mesh, h5py.Group): shape = mesh[list(mesh.keys())[0]].shape else: shape = mesh.shape spacing = tuple(mesh.attrs["gridSpacing"]) offset = tuple(mesh.attrs["gridGlobalOffset"]) unit_si = mesh.attrs["gridUnitSI"] self.meshshapes[mname] = (shape, spacing, offset, unit_si) except (KeyError, TypeError, AttributeError): pass try: particles = f[bp + pp] for pname in particles.keys(): species = particles[pname] if "particlePatches" in species.keys(): for patch, size in enumerate( species["/particlePatches/numParticles"] ): self.numparts[f"{pname}#{patch}"] = size else: axis = list(species["/position"].keys())[0] if is_const_component(species["/position/" + axis]): self.numparts[pname] = species["/position/" + axis].attrs[ "shape" ] else: self.numparts[pname] = species["/position/" + axis].len() except (KeyError, TypeError, AttributeError): pass # Limit values per grid by resulting memory footprint self.vpg = int(self.dataset.gridsize / 4) # 4Byte per value (f32) # Meshes of the same size do not need separate chunks for shape, *_ in set(self.meshshapes.values()): self.num_grids += min( shape[0], int(np.ceil(reduce(mul, shape) * self.vpg**-1)) ) # Same goes for particle chunks if they are not inside particlePatches patches = {} no_patches = {} for k, v in self.numparts.items(): if "#" in k: patches[k] = v else: no_patches[k] = v for size in set(no_patches.values()): self.num_grids += int(np.ceil(size * self.vpg**-1)) for size in patches.values(): self.num_grids += int(np.ceil(size * self.vpg**-1)) def _parse_index(self): """Fills each grid with appropriate properties (extent, dimensions, ...) This calculates the properties of every OpenPMDGrid based on the total number of grids in the simulation. The domain is divided into ``self.num_grids`` (roughly) equally sized chunks along the x-axis. ``grid_levels`` is always equal to 0 since we only have one level of refinement in openPMD. Notes ----- ``self.grid_dimensions`` is rounded to the nearest integer. Grid edges are calculated from this dimension. Grids with dimensions [0, 0, 0] are particle only. The others do not have any particles affiliated with them. """ f = self.dataset._handle bp = self.dataset.base_path pp = self.dataset.particles_path self.grid_levels.flat[:] = 0 self.grids = np.empty(self.num_grids, dtype="object") grid_index_total = 0 # Mesh grids for mesh in set(self.meshshapes.values()): (shape, spacing, offset, unit_si) = mesh shape = np.asarray(shape) spacing = np.asarray(spacing) offset = np.asarray(offset) # Total dimension of this grid domain_dimension = np.asarray(shape, dtype=np.int32) domain_dimension = np.append( domain_dimension, np.ones(3 - len(domain_dimension)) ) # Number of grids of this shape num_grids = min(shape[0], int(np.ceil(reduce(mul, shape) * self.vpg**-1))) gle = offset * unit_si # self.dataset.domain_left_edge gre = ( domain_dimension[: spacing.size] * unit_si * spacing + gle ) # self.dataset.domain_right_edge gle = np.append(gle, np.zeros(3 - len(gle))) gre = np.append(gre, np.ones(3 - len(gre))) grid_dim_offset = np.linspace( 0, domain_dimension[0], num_grids + 1, dtype=np.int32 ) grid_edge_offset = ( grid_dim_offset * float(domain_dimension[0]) ** -1 * (gre[0] - gle[0]) + gle[0] ) mesh_names = [] for mname, mdata in self.meshshapes.items(): if mesh == mdata: mesh_names.append(str(mname)) prev = 0 for grid in np.arange(num_grids): self.grid_dimensions[grid_index_total] = domain_dimension self.grid_dimensions[grid_index_total][0] = ( grid_dim_offset[grid + 1] - grid_dim_offset[grid] ) self.grid_left_edge[grid_index_total] = gle self.grid_left_edge[grid_index_total][0] = grid_edge_offset[grid] self.grid_right_edge[grid_index_total] = gre self.grid_right_edge[grid_index_total][0] = grid_edge_offset[grid + 1] self.grid_particle_count[grid_index_total] = 0 self.grids[grid_index_total] = self.grid( grid_index_total, self, 0, fi=prev, fo=self.grid_dimensions[grid_index_total][0], ft=mesh_names, ) prev += self.grid_dimensions[grid_index_total][0] grid_index_total += 1 handled_ptypes = [] # Particle grids for species, count in self.numparts.items(): if "#" in species: # This is a particlePatch spec = species.split("#") patch = f[bp + pp + "/" + spec[0] + "/particlePatches"] domain_dimension = np.ones(3, dtype=np.int32) for ind, axis in enumerate(list(patch["extent"].keys())): domain_dimension[ind] = patch["extent/" + axis][()][int(spec[1])] num_grids = int(np.ceil(count * self.vpg**-1)) gle = [] for axis in patch["offset"].keys(): gle.append( get_component(patch, "offset/" + axis, int(spec[1]), 1)[0] ) gle = np.asarray(gle) gle = np.append(gle, np.zeros(3 - len(gle))) gre = [] for axis in patch["extent"].keys(): gre.append( get_component(patch, "extent/" + axis, int(spec[1]), 1)[0] ) gre = np.asarray(gre) gre = np.append(gre, np.ones(3 - len(gre))) np.add(gle, gre, gre) npo = patch["numParticlesOffset"][()].item(int(spec[1])) particle_count = np.linspace( npo, npo + count, num_grids + 1, dtype=np.int32 ) particle_names = [str(spec[0])] elif str(species) not in handled_ptypes: domain_dimension = self.dataset.domain_dimensions num_grids = int(np.ceil(count * self.vpg**-1)) gle = self.dataset.domain_left_edge gre = self.dataset.domain_right_edge particle_count = np.linspace(0, count, num_grids + 1, dtype=np.int32) particle_names = [] for pname, size in self.numparts.items(): if size == count: # Since this is not part of a particlePatch, # we can include multiple same-sized ptypes particle_names.append(str(pname)) handled_ptypes.append(str(pname)) else: # A grid with this exact particle count has already been created continue for grid in np.arange(num_grids): self.grid_dimensions[grid_index_total] = domain_dimension self.grid_left_edge[grid_index_total] = gle self.grid_right_edge[grid_index_total] = gre self.grid_particle_count[grid_index_total] = ( particle_count[grid + 1] - particle_count[grid] ) * len(particle_names) self.grids[grid_index_total] = self.grid( grid_index_total, self, 0, pi=particle_count[grid], po=particle_count[grid + 1] - particle_count[grid], pt=particle_names, ) grid_index_total += 1 def _populate_grid_objects(self): """This initializes all grids. Additionally, it should set up Children and Parent lists on each grid object. openPMD is not adaptive and thus there are no Children and Parents for any grid. """ for i in np.arange(self.num_grids): self.grids[i]._prepare_grid() self.grids[i]._setup_dx() self.max_level = 0 class OpenPMDDataset(Dataset): """Contains all the required information of a single iteration of the simulation. Notes ----- It is assumed that - all meshes cover the same region. Their resolution can be different. - all particles reside in this same region exclusively. - particle and mesh positions are *absolute* with respect to the simulation origin. """ _load_requirements = ["h5py"] _index_class = OpenPMDHierarchy _field_info_class = OpenPMDFieldInfo def __init__( self, filename, dataset_type="openPMD", storage_filename=None, units_override=None, unit_system="mks", **kwargs, ): self._handle = HDF5FileHandler(filename) self.gridsize = kwargs.pop("open_pmd_virtual_gridsize", 10**9) self.standard_version = Version(self._handle.attrs["openPMD"].decode()) self.iteration = kwargs.pop("iteration", None) self._set_paths(self._handle, path.dirname(filename), self.iteration) Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, ) self.storage_filename = storage_filename self.fluid_types += ("openPMD",) try: particles = tuple( str(c) for c in self._handle[self.base_path + self.particles_path].keys() ) if len(particles) > 1: # Only use on-disk particle names if there is more than one species self.particle_types = particles mylog.debug("self.particle_types: %s", self.particle_types) self.particle_types_raw = self.particle_types self.particle_types = tuple(self.particle_types) except (KeyError, TypeError, AttributeError): pass def _set_paths(self, handle, path, iteration): """Parses relevant hdf5-paths out of ``handle``. Parameters ---------- handle : h5py.File path : str (absolute) filepath for current hdf5 container """ iterations = [] if iteration is None: iteration = list(handle["/data"].keys())[0] encoding = handle.attrs["iterationEncoding"].decode() if "groupBased" in encoding: iterations = list(handle["/data"].keys()) mylog.info("Found %s iterations in file", len(iterations)) elif "fileBased" in encoding: itformat = handle.attrs["iterationFormat"].decode().split("/")[-1] regex = "^" + itformat.replace("%T", "[0-9]+") + "$" if path == "": mylog.warning( "For file based iterations, please use absolute file paths!" ) pass for filename in listdir(path): if match(regex, filename): iterations.append(filename) mylog.info("Found %s iterations in directory", len(iterations)) if len(iterations) == 0: mylog.warning("No iterations found!") if "groupBased" in encoding and len(iterations) > 1: mylog.warning("Only chose to load one iteration (%s)", iteration) self.base_path = f"/data/{iteration}/" try: self.meshes_path = self._handle["/"].attrs["meshesPath"].decode() handle[self.base_path + self.meshes_path] except KeyError: if self.standard_version <= Version("1.1.0"): mylog.info( "meshesPath not present in file. " "Assuming file contains no meshes and has a domain extent of 1m^3!" ) self.meshes_path = None else: raise try: self.particles_path = self._handle["/"].attrs["particlesPath"].decode() handle[self.base_path + self.particles_path] except KeyError: if self.standard_version <= Version("1.1.0"): mylog.info( "particlesPath not present in file." " Assuming file contains no particles!" ) self.particles_path = None else: raise def _set_code_unit_attributes(self): """Handle conversion between different physical units and the code units. Every dataset in openPMD can have different code <-> physical scaling. The individual factor is obtained by multiplying with "unitSI" reading getting data from disk. """ setdefaultattr(self, "length_unit", self.quan(1.0, "m")) setdefaultattr(self, "mass_unit", self.quan(1.0, "kg")) setdefaultattr(self, "time_unit", self.quan(1.0, "s")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "m/s")) setdefaultattr(self, "magnetic_unit", self.quan(1.0, "T")) def _parse_parameter_file(self): """Read in metadata describing the overall data on-disk.""" f = self._handle bp = self.base_path mp = self.meshes_path self.parameters = 0 self._periodicity = np.zeros(3, dtype="bool") self.refine_by = 1 self.cosmological_simulation = 0 try: shapes = {} left_edges = {} right_edges = {} meshes = f[bp + mp] for mname in meshes.keys(): mesh = meshes[mname] if isinstance(mesh, h5py.Group): shape = np.asarray(mesh[list(mesh.keys())[0]].shape) else: shape = np.asarray(mesh.shape) spacing = np.asarray(mesh.attrs["gridSpacing"]) offset = np.asarray(mesh.attrs["gridGlobalOffset"]) unit_si = np.asarray(mesh.attrs["gridUnitSI"]) le = offset * unit_si re = le + shape * unit_si * spacing shapes[mname] = shape left_edges[mname] = le right_edges[mname] = re lowest_dim = np.min([len(i) for i in shapes.values()]) shapes = np.asarray([i[:lowest_dim] for i in shapes.values()]) left_edges = np.asarray([i[:lowest_dim] for i in left_edges.values()]) right_edges = np.asarray([i[:lowest_dim] for i in right_edges.values()]) fs = [] dle = [] dre = [] for i in np.arange(lowest_dim): fs.append(np.max(shapes.transpose()[i])) dle.append(np.min(left_edges.transpose()[i])) dre.append(np.min(right_edges.transpose()[i])) self.dimensionality = len(fs) self.domain_dimensions = np.append(fs, np.ones(3 - self.dimensionality)) self.domain_left_edge = np.append(dle, np.zeros(3 - len(dle))) self.domain_right_edge = np.append(dre, np.ones(3 - len(dre))) except (KeyError, TypeError, AttributeError): if self.standard_version <= Version("1.1.0"): self.dimensionality = 3 self.domain_dimensions = np.ones(3, dtype=np.float64) self.domain_left_edge = np.zeros(3, dtype=np.float64) self.domain_right_edge = np.ones(3, dtype=np.float64) else: raise self.current_time = f[bp].attrs["time"] * f[bp].attrs["timeUnitSI"] @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: """Checks whether the supplied file can be read by this frontend.""" if not valid_hdf5_signature(filename): return False if cls._missing_load_requirements(): return False try: with h5py.File(filename, mode="r") as f: attrs = list(f["/"].attrs.keys()) for i in opmd_required_attributes: if i not in attrs: return False if Version(f.attrs["openPMD"].decode()) not in ompd_known_versions: return False if f.attrs["iterationEncoding"].decode() == "fileBased": return True return False except OSError: return False class OpenPMDDatasetSeries(DatasetSeries): _pre_outputs = () _dataset_cls = OpenPMDDataset parallel = True setup_function = None mixed_dataset_types = False def __init__(self, filename): super().__init__([]) self.handle = h5py.File(filename, mode="r") self.filename = filename self._pre_outputs = sorted( np.asarray(list(self.handle["/data"].keys()), dtype="int64") ) def __iter__(self): for it in self._pre_outputs: ds = self._load(it, **self.kwargs) self._setup_function(ds) yield ds def __getitem__(self, key): if isinstance(key, int): o = self._load(key) self._setup_function(o) return o else: raise KeyError(f"Unknown iteration {key}") def _load(self, it, **kwargs): return OpenPMDDataset(self.filename, iteration=it) class OpenPMDGroupBasedDataset(Dataset): _load_requirements = ["h5py"] _index_class = OpenPMDHierarchy _field_info_class = OpenPMDFieldInfo def __new__(cls, filename, *args, **kwargs): ret = object.__new__(OpenPMDDatasetSeries) ret.__init__(filename) return ret @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not valid_hdf5_signature(filename): return False if cls._missing_load_requirements(): return False try: with h5py.File(filename, mode="r") as f: attrs = list(f["/"].attrs.keys()) for i in opmd_required_attributes: if i not in attrs: return False if Version(f.attrs["openPMD"].decode()) not in ompd_known_versions: return False if f.attrs["iterationEncoding"].decode() == "groupBased": return True return False except OSError: return False yt-project-yt-f043ac8/yt/frontends/open_pmd/definitions.py000066400000000000000000000000001510711153200237640ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/open_pmd/fields.py000066400000000000000000000237071510711153200227420ustar00rootroot00000000000000import numpy as np from yt.fields.field_info_container import FieldInfoContainer from yt.fields.magnetic_field import setup_magnetic_field_aliases from yt.frontends.open_pmd.misc import is_const_component, parse_unit_dimension from yt.units.yt_array import YTQuantity from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.physical_constants import mu_0, speed_of_light def setup_poynting_vector(self): def _get_poyn(axis): def poynting(field, data): u = mu_0**-1 if axis in "x": return u * ( data["openPMD", "E_y"] * data["gas", "magnetic_field_z"] - data["openPMD", "E_z"] * data["gas", "magnetic_field_y"] ) elif axis in "y": return u * ( data["openPMD", "E_z"] * data["gas", "magnetic_field_x"] - data["openPMD", "E_x"] * data["gas", "magnetic_field_z"] ) elif axis in "z": return u * ( data["openPMD", "E_x"] * data["gas", "magnetic_field_y"] - data["openPMD", "E_y"] * data["gas", "magnetic_field_x"] ) return poynting for ax in "xyz": self.add_field( ("openPMD", f"poynting_vector_{ax}"), sampling_type="cell", function=_get_poyn(ax), units="W/m**2", ) def setup_kinetic_energy(self, ptype): def _kin_en(field, data): p2 = ( data[ptype, "particle_momentum_x"] ** 2 + data[ptype, "particle_momentum_y"] ** 2 + data[ptype, "particle_momentum_z"] ** 2 ) mass = data[ptype, "particle_mass"] * data[ptype, "particle_weighting"] return ( speed_of_light * np.sqrt(p2 + mass**2 * speed_of_light**2) - mass * speed_of_light**2 ) self.add_field( (ptype, "particle_kinetic_energy"), sampling_type="particle", function=_kin_en, units="kg*m**2/s**2", ) def setup_velocity(self, ptype): def _get_vel(axis): def velocity(field, data): c = speed_of_light momentum = data[ptype, f"particle_momentum_{axis}"] mass = data[ptype, "particle_mass"] weighting = data[ptype, "particle_weighting"] return momentum / np.sqrt((mass * weighting) ** 2 + (momentum**2) / (c**2)) return velocity for ax in "xyz": self.add_field( (ptype, f"particle_velocity_{ax}"), sampling_type="particle", function=_get_vel(ax), units="m/s", ) def setup_absolute_positions(self, ptype): def _abs_pos(axis): def ap(field, data): return np.add( data[ptype, f"particle_positionCoarse_{axis}"], data[ptype, f"particle_positionOffset_{axis}"], ) return ap for ax in "xyz": self.add_field( (ptype, f"particle_position_{ax}"), sampling_type="particle", function=_abs_pos(ax), units="m", ) class OpenPMDFieldInfo(FieldInfoContainer): r"""Specifies which fields from the dataset yt should know about. ``self.known_other_fields`` and ``self.known_particle_fields`` must be populated. Entries for both of these lists must be tuples of the form ("name", ("units", ["fields", "to", "alias"], "display_name")) These fields will be represented and handled in yt in the way you define them here. The fields defined in both ``self.known_other_fields`` and ``self.known_particle_fields`` will only be added to a dataset (with units, aliases, etc), if they match any entry in the ``OpenPMDHierarchy``'s ``self.field_list``. Notes ----- Contrary to many other frontends, we dynamically obtain the known fields from the simulation output. The openPMD markup is extremely flexible - names, dimensions and the number of individual datasets can (and very likely will) vary. openPMD states that record names and their components are only allowed to contain * characters a-Z, * the numbers 0-9 * and the underscore _ * (equivalently, the regex \w). Since yt widely uses the underscore in field names, openPMD's underscores (_) are replaced by hyphen (-). Derived fields will automatically be set up, if names and units of your known on-disk (or manually derived) fields match the ones in [1]. References ---------- * http://yt-project.org/docs/dev/analyzing/fields.html * http://yt-project.org/docs/dev/developing/creating_frontend.html#data-meaning-structures * https://github.com/openPMD/openPMD-standard/blob/latest/STANDARD.md * [1] http://yt-project.org/docs/dev/reference/field_list.html#universal-fields """ _mag_fields: list[str] = [] def __init__(self, ds, field_list): f = ds._handle bp = ds.base_path mp = ds.meshes_path pp = ds.particles_path try: fields = f[bp + mp] for fname in fields.keys(): field = fields[fname] if isinstance(field, h5py.Dataset) or is_const_component(field): # Don't consider axes. # This appears to be a vector field of single dimensionality ytname = str("_".join([fname.replace("_", "-")])) parsed = parse_unit_dimension( np.asarray(field.attrs["unitDimension"], dtype="int64") ) unit = str(YTQuantity(1, parsed).units) aliases = [] # Save a list of magnetic fields for aliasing later on # We can not reasonably infer field type/unit by name in openPMD if unit == "T" or unit == "kg/(A*s**2)": self._mag_fields.append(ytname) self.known_other_fields += ((ytname, (unit, aliases, None)),) else: for axis in field.keys(): ytname = str("_".join([fname.replace("_", "-"), axis])) parsed = parse_unit_dimension( np.asarray(field.attrs["unitDimension"], dtype="int64") ) unit = str(YTQuantity(1, parsed).units) aliases = [] # Save a list of magnetic fields for aliasing later on # We can not reasonably infer field type by name in openPMD if unit == "T" or unit == "kg/(A*s**2)": self._mag_fields.append(ytname) self.known_other_fields += ((ytname, (unit, aliases, None)),) for i in self.known_other_fields: mylog.debug("open_pmd - known_other_fields - %s", i) except (KeyError, TypeError, AttributeError): pass try: particles = f[bp + pp] for pname in particles.keys(): species = particles[pname] for recname in species.keys(): try: record = species[recname] parsed = parse_unit_dimension(record.attrs["unitDimension"]) unit = str(YTQuantity(1, parsed).units) ytattrib = str(recname).replace("_", "-") if ytattrib == "position": # Symbolically rename position to preserve yt's # interpretation of the pfield particle_position is later # derived in setup_absolute_positions in the way yt expects ytattrib = "positionCoarse" if isinstance(record, h5py.Dataset) or is_const_component( record ): name = ["particle", ytattrib] self.known_particle_fields += ( (str("_".join(name)), (unit, [], None)), ) else: for axis in record.keys(): aliases = [] name = ["particle", ytattrib, axis] ytname = str("_".join(name)) self.known_particle_fields += ( (ytname, (unit, aliases, None)), ) except KeyError: if recname != "particlePatches": mylog.info( "open_pmd - %s_%s does not seem to have " "unitDimension", pname, recname, ) for i in self.known_particle_fields: mylog.debug("open_pmd - known_particle_fields - %s", i) except (KeyError, TypeError, AttributeError): pass super().__init__(ds, field_list) def setup_fluid_fields(self): """Defines which derived mesh fields to create. If a field can not be calculated, it will simply be skipped. """ # Set up aliases first so the setup for poynting can use them if len(self._mag_fields) > 0: setup_magnetic_field_aliases(self, "openPMD", self._mag_fields) setup_poynting_vector(self) def setup_particle_fields(self, ptype): """Defines which derived particle fields to create. This will be called for every entry in `OpenPMDDataset``'s ``self.particle_types``. If a field can not be calculated, it will simply be skipped. """ setup_absolute_positions(self, ptype) setup_kinetic_energy(self, ptype) setup_velocity(self, ptype) super().setup_particle_fields(ptype) yt-project-yt-f043ac8/yt/frontends/open_pmd/io.py000066400000000000000000000172531510711153200221020ustar00rootroot00000000000000from collections import defaultdict import numpy as np from yt.frontends.open_pmd.misc import get_component, is_const_component from yt.geometry.selection_routines import GridSelector from yt.utilities.io_handler import BaseIOHandler class IOHandlerOpenPMDHDF5(BaseIOHandler): _field_dtype = "float32" _dataset_type = "openPMD" def __init__(self, ds, *args, **kwargs): self.ds = ds self._handle = ds._handle self.base_path = ds.base_path self.meshes_path = ds.meshes_path self.particles_path = ds.particles_path self._array_fields = {} self._cached_ptype = "" def _fill_cache(self, ptype, index=0, offset=None): """Fills the particle position cache for the ``ptype``. Parameters ---------- ptype : str The on-disk name of the particle species index : int, optional offset : int, optional """ if str((ptype, index, offset)) not in self._cached_ptype: self._cached_ptype = str((ptype, index, offset)) pds = self._handle[self.base_path + self.particles_path + "/" + ptype] axes = list(pds["position"].keys()) if offset is None: if is_const_component(pds["position/" + axes[0]]): offset = pds["position/" + axes[0]].attrs["shape"] else: offset = pds["position/" + axes[0]].len() self.cache = np.empty((3, offset), dtype=np.float64) for i in np.arange(3): ax = "xyz"[i] if ax in axes: np.add( get_component(pds, "position/" + ax, index, offset), get_component(pds, "positionOffset/" + ax, index, offset), self.cache[i], ) else: # Pad accordingly with zeros to make 1D/2D datasets compatible # These have to be the same shape as the existing axes since that # equals the number of particles self.cache[i] = np.zeros(offset) def _read_particle_selection(self, chunks, selector, fields): """Read particle fields for particle species masked by a selection. Parameters ---------- chunks A list of chunks A chunk is a list of grids selector A region (inside your domain) specifying which parts of the field you want to read. See [1] and [2] fields : array_like Tuples (ptype, pfield) representing a field Returns ------- dict keys are tuples (ptype, pfield) representing a field values are (N,) ndarrays with data from that field """ f = self._handle bp = self.base_path pp = self.particles_path ds = f[bp + pp] unions = self.ds.particle_unions chunks = list(chunks) # chunks is a generator rv = {} ind = {} particle_count = {} ptf = defaultdict(list) # ParticleTypes&Fields rfm = defaultdict(list) # RequestFieldMapping for ptype, pname in fields: pfield = (ptype, pname) # Overestimate the size of all pfields so they include all particles # and shrink it later particle_count[pfield] = 0 if ptype in unions: for pt in unions[ptype]: particle_count[pfield] += self.ds.particle_type_counts[pt] ptf[pt].append(pname) rfm[pt, pname].append(pfield) else: particle_count[pfield] = self.ds.particle_type_counts[ptype] ptf[ptype].append(pname) rfm[pfield].append(pfield) rv[pfield] = np.empty((particle_count[pfield],), dtype=np.float64) ind[pfield] = 0 for ptype in ptf: for chunk in chunks: for grid in chunk.objs: if str(ptype) == "io": species = list(ds.keys())[0] else: species = ptype if species not in grid.ptypes: continue # read particle coords into cache self._fill_cache(species, grid.pindex, grid.poffset) mask = selector.select_points( self.cache[0], self.cache[1], self.cache[2], 0.0 ) if mask is None: continue pds = ds[species] for field in ptf[ptype]: component = "/".join(field.split("_")[1:]) component = component.replace("positionCoarse", "position") component = component.replace("-", "_") data = get_component(pds, component, grid.pindex, grid.poffset)[ mask ] for request_field in rfm[ptype, field]: rv[request_field][ ind[request_field] : ind[request_field] + data.shape[0] ] = data ind[request_field] += data.shape[0] for field in fields: rv[field] = rv[field][: ind[field]] return rv def _read_fluid_selection(self, chunks, selector, fields, size): """Reads given fields masked by a given selection. Parameters ---------- chunks A list of chunks A chunk is a list of grids selector A region (inside your domain) specifying which parts of the field you want to read. See [1] and [2] fields : array_like Tuples (fname, ftype) representing a field size : int Size of the data to read Returns ------- dict keys are tuples (ftype, fname) representing a field values are flat (``size``,) ndarrays with data from that field """ f = self._handle bp = self.base_path mp = self.meshes_path ds = f[bp + mp] chunks = list(chunks) rv = {} ind = {} if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError if size is None: size = sum(g.count(selector) for chunk in chunks for g in chunk.objs) for field in fields: rv[field] = np.empty(size, dtype=np.float64) ind[field] = 0 for ftype, fname in fields: field = (ftype, fname) for chunk in chunks: for grid in chunk.objs: mask = grid._get_selector_mask(selector) if mask is None: continue component = fname.replace("_", "/").replace("-", "_") if component.split("/")[0] not in grid.ftypes: data = np.full_like(mask, 0) else: data = get_component(ds, component, grid.findex, grid.foffset) # Workaround - casts a 2D (x,y) array to 3D (x,y,1) data = data.reshape(mask.shape) # The following is a modified AMRGridPatch.select(...) count = grid.count(selector) rv[field][ind[field] : ind[field] + count] = data[mask] ind[field] += count for field in fields: rv[field] = rv[field][: ind[field]] rv[field].flatten() return rv yt-project-yt-f043ac8/yt/frontends/open_pmd/misc.py000066400000000000000000000061431510711153200224220ustar00rootroot00000000000000import numpy as np from yt.utilities.logger import ytLogger as mylog def parse_unit_dimension(unit_dimension): r"""Transforms an openPMD unitDimension into a string. Parameters ---------- unit_dimension : array_like integer array of length 7 with one entry for the dimensional component of every SI unit [0] length L, [1] mass M, [2] time T, [3] electric current I, [4] thermodynamic temperature theta, [5] amount of substance N, [6] luminous intensity J References ---------- https://github.com/openPMD/openPMD-standard/blob/latest/STANDARD.md#unit-systems-and-dimensionality Returns ------- str Examples -------- >>> velocity = [1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0] >>> print(parse_unit_dimension(velocity)) 'm**1*s**-1' >>> magnetic_field = [0.0, 1.0, -2.0, -1.0, 0.0, 0.0, 0.0] >>> print(parse_unit_dimension(magnetic_field)) 'kg**1*s**-2*A**-1' """ if len(unit_dimension) != 7: mylog.error("SI must have 7 base dimensions!") unit_dimension = np.asarray(unit_dimension, dtype="int64") dim = [] si = ["m", "kg", "s", "A", "C", "mol", "cd"] for i in np.arange(7): if unit_dimension[i] != 0: dim.append(f"{si[i]}**{unit_dimension[i]}") return "*".join(dim) def is_const_component(record_component): """Determines whether a group or dataset in the HDF5 file is constant. Parameters ---------- record_component : h5py.Group or h5py.Dataset Returns ------- bool True if constant, False otherwise References ---------- .. https://github.com/openPMD/openPMD-standard/blob/latest/STANDARD.md, section 'Constant Record Components' """ return "value" in record_component.attrs.keys() def get_component(group, component_name, index=0, offset=None): """Grabs a dataset component from a group as a whole or sliced. Parameters ---------- group : h5py.Group component_name : str relative path of the component in the group index : int, optional first entry along the first axis to read offset : int, optional number of entries to read if not supplied, every entry after index is returned Notes ----- This scales every entry of the component with the respective "unitSI". Returns ------- ndarray (N,) 1D in case of particle data (O,P,Q) 1D/2D/3D in case of mesh data """ record_component = group[component_name] unit_si = record_component.attrs["unitSI"] if is_const_component(record_component): shape = np.asarray(record_component.attrs["shape"]) if offset is None: shape[0] -= index else: shape[0] = offset # component is constant, craft an array by hand return np.full(shape, record_component.attrs["value"] * unit_si) else: if offset is not None: offset += index # component is a dataset, return it (possibly masked) return np.multiply(record_component[index:offset], unit_si) yt-project-yt-f043ac8/yt/frontends/open_pmd/tests/000077500000000000000000000000001510711153200222535ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/open_pmd/tests/__init__.py000066400000000000000000000000001510711153200243520ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/open_pmd/tests/test_outputs.py000066400000000000000000000140441510711153200254120ustar00rootroot00000000000000from itertools import product import numpy as np from numpy.testing import assert_almost_equal, assert_array_equal, assert_equal from yt.frontends.open_pmd.data_structures import OpenPMDDataset from yt.loaders import load from yt.testing import requires_file, requires_module from yt.utilities.answer_testing.framework import data_dir_load twoD = "example-2d/hdf5/data00000100.h5" threeD = "example-3d/hdf5/data00000100.h5" noFields = "no_fields/data00000400.h5" noParticles = "no_particles/data00000400.h5" groupBased = "singleParticle/simData.h5" particle_fields = [ "particle_charge", "particle_mass", "particle_momentum_x", "particle_momentum_y", "particle_momentum_z", "particle_positionCoarse_x", "particle_positionCoarse_y", "particle_positionCoarse_z", "particle_positionOffset_x", "particle_positionOffset_y", "particle_positionOffset_z", "particle_weighting", ] @requires_module("h5py") @requires_file(threeD) def test_3d_out(): ds = data_dir_load(threeD) particle_types = ["all", "io", "nbody"] field_list = list(product(particle_types, particle_fields)) field_list += list(product(("openPMD",), ("E_x", "E_y", "E_z", "rho"))) domain_dimensions = [26, 26, 201] * np.ones_like(ds.domain_dimensions) domain_width = [2.08e-05, 2.08e-05, 2.01e-05] * np.ones_like(ds.domain_left_edge) assert isinstance(ds, OpenPMDDataset) assert_equal(str(ds), "data00000100.h5") assert_equal(ds.dimensionality, 3) assert_equal(ds.particle_types_raw, ("io",)) assert "all" in ds.particle_unions assert_array_equal(ds.field_list, field_list) assert_array_equal(ds.domain_dimensions, domain_dimensions) assert_almost_equal( ds.current_time, 3.28471214521e-14 * np.ones_like(ds.current_time) ) assert_almost_equal(ds.domain_right_edge - ds.domain_left_edge, domain_width) @requires_module("h5py") @requires_file(twoD) def test_2d_out(): ds = data_dir_load(twoD) particle_types = ("Hydrogen1+", "all", "electrons", "nbody") field_list = list(product(particle_types, particle_fields)) field_list += [ ("openPMD", "B_x"), ("openPMD", "B_y"), ("openPMD", "B_z"), ("openPMD", "E_x"), ("openPMD", "E_y"), ("openPMD", "E_z"), ("openPMD", "J_x"), ("openPMD", "J_y"), ("openPMD", "J_z"), ("openPMD", "rho"), ] domain_dimensions = [51, 201, 1] * np.ones_like(ds.domain_dimensions) domain_width = [3.06e-05, 2.01e-05, 1e0] * np.ones_like(ds.domain_left_edge) assert isinstance(ds, OpenPMDDataset) assert_equal(str(ds), "data00000100.h5") assert_equal(ds.dimensionality, 2) assert_equal(ds.particle_types_raw, ("Hydrogen1+", "electrons")) assert "all" in ds.particle_unions assert_array_equal(ds.field_list, field_list) assert_array_equal(ds.domain_dimensions, domain_dimensions) assert_almost_equal( ds.current_time, 3.29025596712e-14 * np.ones_like(ds.current_time) ) assert_almost_equal(ds.domain_right_edge - ds.domain_left_edge, domain_width) @requires_module("h5py") @requires_file(noFields) def test_no_fields_out(): ds = data_dir_load(noFields) particle_types = ("all", "io", "nbody") no_fields_pfields = sorted(particle_fields + ["particle_id"]) field_list = list(product(particle_types, no_fields_pfields)) domain_dimensions = [1, 1, 1] * np.ones_like(ds.domain_dimensions) domain_width = [1, 1, 1] * np.ones_like(ds.domain_left_edge) assert isinstance(ds, OpenPMDDataset) assert_equal(str(ds), "data00000400.h5") assert_equal(ds.dimensionality, 3) assert_equal(ds.particle_types_raw, ("io",)) assert "all" in ds.particle_unions assert_array_equal(ds.field_list, field_list) assert_array_equal(ds.domain_dimensions, domain_dimensions) assert_almost_equal( ds.current_time, 1.3161023868481013e-13 * np.ones_like(ds.current_time) ) assert_almost_equal(ds.domain_right_edge - ds.domain_left_edge, domain_width) @requires_module("h5py") @requires_file(noParticles) def test_no_particles_out(): ds = data_dir_load(noParticles) field_list = [ ("openPMD", "E_x"), ("openPMD", "E_y"), ("openPMD", "E_z"), ("openPMD", "rho"), ] domain_dimensions = [51, 201, 1] * np.ones_like(ds.domain_dimensions) domain_width = [3.06e-05, 2.01e-05, 1e0] * np.ones_like(ds.domain_left_edge) assert isinstance(ds, OpenPMDDataset) assert_equal(str(ds), "data00000400.h5") assert_equal(ds.dimensionality, 2) assert_equal(ds.particle_types_raw, ("io",)) assert "all" not in ds.particle_unions assert_array_equal(ds.field_list, field_list) assert_array_equal(ds.domain_dimensions, domain_dimensions) assert_almost_equal( ds.current_time, 1.3161023868481013e-13 * np.ones_like(ds.current_time) ) assert_almost_equal(ds.domain_right_edge - ds.domain_left_edge, domain_width) @requires_module("h5py") @requires_file(groupBased) def test_groupBased_out(): dss = load(groupBased) particle_types = ("all", "io", "nbody") field_list = list(product(particle_types, particle_fields)) field_list += [ ("openPMD", "J_x"), ("openPMD", "J_y"), ("openPMD", "J_z"), ("openPMD", "e-chargeDensity"), ] domain_dimensions = [32, 64, 64] * np.ones_like(dss[0].domain_dimensions) domain_width = [0.0002752, 0.0005504, 0.0005504] * np.ones_like( dss[0].domain_left_edge ) assert_equal(len(dss), 101) for i in range(0, len(dss), 20): # Test only every 20th ds out of the series ds = dss[i] assert_equal(str(ds), "simData.h5") assert_equal(ds.dimensionality, 3) assert_equal(ds.particle_types_raw, ("io",)) assert_array_equal(ds.field_list, field_list) assert_array_equal(ds.domain_dimensions, domain_dimensions) assert ds.current_time >= np.zeros_like(ds.current_time) assert ds.current_time <= 1.6499999999999998e-12 * np.ones_like(ds.current_time) assert_almost_equal(ds.domain_right_edge - ds.domain_left_edge, domain_width) yt-project-yt-f043ac8/yt/frontends/owls/000077500000000000000000000000001510711153200202745ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls/__init__.py000066400000000000000000000000001510711153200223730ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls/api.py000066400000000000000000000002551510711153200214210ustar00rootroot00000000000000from . import tests from .data_structures import OWLSDataset from .fields import OWLSFieldInfo from .io import IOHandlerOWLS from .simulation_handling import OWLSSimulation yt-project-yt-f043ac8/yt/frontends/owls/data_structures.py000066400000000000000000000046111510711153200240640ustar00rootroot00000000000000import os import yt.units from yt.frontends.gadget.data_structures import GadgetHDF5Dataset from yt.utilities.definitions import sec_conversion from yt.utilities.on_demand_imports import _h5py as h5py from .fields import OWLSFieldInfo class OWLSDataset(GadgetHDF5Dataset): _load_requirements = ["h5py"] _particle_mass_name = "Mass" _field_info_class = OWLSFieldInfo _time_readin = "Time_GYR" def _parse_parameter_file(self): # read values from header hvals = self._get_hvals() self.parameters = hvals # set features common to OWLS and Eagle self._set_owls_eagle() # Set time from value in header self.current_time = ( hvals[self._time_readin] * sec_conversion["Gyr"] * yt.units.s ) def _set_code_unit_attributes(self): self._set_owls_eagle_units() @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = ["Constants", "Header", "Parameters", "Units"] veto_groups = [ "SUBFIND", "FOF", "PartType0/ChemistryAbundances", "PartType0/ChemicalAbundances", "RuntimePars", "HashTable", ] valid = True valid_fname = filename # If passed arg is a directory, look for the .0 file in that dir if os.path.isdir(filename): valid_files = [] for f in os.listdir(filename): fname = os.path.join(filename, f) fext = os.path.splitext(fname)[-1] if ( (".0" in f) and (fext not in {".ewah", ".kdtree"}) and os.path.isfile(fname) ): valid_files.append(fname) if len(valid_files) == 0: valid = False elif len(valid_files) > 1: valid = False else: valid_fname = valid_files[0] try: fileh = h5py.File(valid_fname, mode="r") for ng in need_groups: if ng not in fileh["/"]: valid = False for vg in veto_groups: if vg in fileh["/"]: valid = False fileh.close() except Exception: valid = False return valid yt-project-yt-f043ac8/yt/frontends/owls/fields.py000066400000000000000000000302441510711153200221170ustar00rootroot00000000000000import os import numpy as np from yt.config import ytcfg from yt.fields.species_fields import ( add_species_field_by_density, add_species_field_by_fraction, ) from yt.frontends.sph.fields import SPHFieldInfo from yt.funcs import download_file, mylog from . import owls_ion_tables as oit def _get_ion_mass_frac(ion, ftype, itab, data): # get element symbol from ion string. ion string will # be a member of the tuple _ions (i.e. si13) # -------------------------------------------------------- if ion[0:2].isalpha(): symbol = ion[0:2].capitalize() else: symbol = ion[0:1].capitalize() # mass fraction for the element # -------------------------------------------------------- m_frac = data[ftype, symbol + "_fraction"] # get nH and T for lookup # -------------------------------------------------------- log_nH = np.log10(data["PartType0", "H_number_density"]) log_T = np.log10(data["PartType0", "Temperature"]) # get name of owls_ion_file for given ion # -------------------------------------------------------- itab.set_iz(data.ds.current_redshift) # find ion balance using log nH and log T # -------------------------------------------------------- i_frac = itab.interp(log_nH, log_T) return i_frac, m_frac class OWLSFieldInfo(SPHFieldInfo): _ions: tuple[str, ...] = ( "c1", "c2", "c3", "c4", "c5", "c6", "fe2", "fe17", "h1", "he1", "he2", "mg1", "mg2", "n2", "n3", "n4", "n5", "n6", "n7", "ne8", "ne9", "ne10", "o1", "o6", "o7", "o8", "si2", "si3", "si4", "si13", ) _elements = ("H", "He", "C", "N", "O", "Ne", "Mg", "Si", "Fe") _num_neighbors = 48 _add_elements = ("PartType0", "PartType4") _add_ions = "PartType0" def __init__(self, ds, field_list, slice_info=None): new_particle_fields = ( ("Hydrogen", ("", ["H_fraction"], None)), ("Helium", ("", ["He_fraction"], None)), ("Carbon", ("", ["C_fraction"], None)), ("Nitrogen", ("", ["N_fraction"], None)), ("Oxygen", ("", ["O_fraction"], None)), ("Neon", ("", ["Ne_fraction"], None)), ("Magnesium", ("", ["Mg_fraction"], None)), ("Silicon", ("", ["Si_fraction"], None)), ("Iron", ("", ["Fe_fraction"], None)), ) if ds.gen_hsmls: new_particle_fields += (("smoothing_length", ("code_length", [], None)),) self.known_particle_fields += new_particle_fields super().__init__(ds, field_list, slice_info=slice_info) # This enables the machinery in yt.fields.species_fields self.species_names += list(self._elements) def setup_particle_fields(self, ptype): """additional particle fields derived from those in snapshot. we also need to add the smoothed fields here b/c setup_fluid_fields is called before setup_particle_fields.""" smoothed_suffixes = ("_number_density", "_density", "_mass") # we add particle element fields for stars and gas # ----------------------------------------------------- if ptype in self._add_elements: # this adds the particle element fields # X_density, X_mass, and X_number_density # where X is an item of self._elements. # X_fraction are defined in snapshot # ----------------------------------------------- for s in self._elements: field_names = add_species_field_by_fraction(self, ptype, s) if ptype == self.ds._sph_ptypes[0]: for fn in field_names: self.alias(("gas", fn[1]), fn) # this needs to be called after the call to # add_species_field_by_fraction for some reason ... # not sure why yet. # ------------------------------------------------------- if ptype == "PartType0": ftype = "gas" else: ftype = ptype super().setup_particle_fields( ptype, num_neighbors=self._num_neighbors, ftype=ftype ) # and now we add the smoothed versions for PartType0 # ----------------------------------------------------- if ptype == "PartType0": # we only add ion fields for gas. this takes some # time as the ion abundances have to be interpolated # from cloudy tables (optically thin) # ----------------------------------------------------- # this defines the ion density on particles # X_density for all items in self._ions # ----------------------------------------------- self.setup_gas_ion_particle_fields(ptype) # this adds the rest of the ion particle fields # X_fraction, X_mass, X_number_density # ----------------------------------------------- for ion in self._ions: # construct yt name for ion # --------------------------------------------------- if ion[0:2].isalpha(): symbol = ion[0:2].capitalize() roman = int(ion[2:]) else: symbol = ion[0:1].capitalize() roman = int(ion[1:]) if (ptype, symbol + "_fraction") not in self.field_aliases: continue pstr = f"_p{roman - 1}" yt_ion = symbol + pstr # add particle field # --------------------------------------------------- add_species_field_by_density(self, ptype, yt_ion) def _h_p1_density(field, data): return data[ptype, "H_density"] - data[ptype, "H_p0_density"] self.add_field( (ptype, "H_p1_density"), sampling_type="particle", function=_h_p1_density, units=self.ds.unit_system["density"], ) add_species_field_by_density(self, ptype, "H_p1") for sfx in ["mass", "density", "number_density"]: fname = "H_p1_" + sfx self.alias(("gas", fname), (ptype, fname)) def _el_number_density(field, data): n_e = data[ptype, "H_p1_number_density"] n_e += data[ptype, "He_p1_number_density"] n_e += 2.0 * data[ptype, "He_p2_number_density"] return n_e self.add_field( (ptype, "El_number_density"), sampling_type="particle", function=_el_number_density, units=self.ds.unit_system["number_density"], ) self.alias(("gas", "El_number_density"), (ptype, "El_number_density")) # alias ion fields # ----------------------------------------------- for ion in self._ions: # construct yt name for ion # --------------------------------------------------- if ion[0:2].isalpha(): symbol = ion[0:2].capitalize() roman = int(ion[2:]) else: symbol = ion[0:1].capitalize() roman = int(ion[1:]) if (ptype, symbol + "_fraction") not in self.field_aliases: continue pstr = f"_p{roman - 1}" yt_ion = symbol + pstr for sfx in smoothed_suffixes: fname = yt_ion + sfx self.alias(("gas", fname), (ptype, fname)) def setup_gas_ion_particle_fields(self, ptype): """Sets up particle fields for gas ion densities.""" # loop over all ions and make fields # ---------------------------------------------- for ion in self._ions: # construct yt name for ion # --------------------------------------------------- if ion[0:2].isalpha(): symbol = ion[0:2].capitalize() roman = int(ion[2:]) else: symbol = ion[0:1].capitalize() roman = int(ion[1:]) if (ptype, symbol + "_fraction") not in self.field_aliases: continue pstr = f"_p{roman - 1}" yt_ion = symbol + pstr ftype = ptype # add ion density and mass field for this species # ------------------------------------------------ fname = yt_ion + "_density" dens_func = self._create_ion_density_func(ftype, ion) self.add_field( (ftype, fname), sampling_type="particle", function=dens_func, units=self.ds.unit_system["density"], ) self._show_field_errors.append((ftype, fname)) fname = yt_ion + "_mass" mass_func = self._create_ion_mass_func(ftype, ion) self.add_field( (ftype, fname), sampling_type="particle", function=mass_func, units=self.ds.unit_system["mass"], ) self._show_field_errors.append((ftype, fname)) def _create_ion_density_func(self, ftype, ion): """returns a function that calculates the ion density of a particle.""" def get_owls_ion_density_field(ion, ftype, itab): def _func(field, data): m_frac, i_frac = _get_ion_mass_frac(ion, ftype, itab, data) return data[ftype, "Density"] * m_frac * i_frac return _func ion_path = self._get_owls_ion_data_dir() fname = os.path.join(ion_path, ion + ".hdf5") itab = oit.IonTableOWLS(fname) return get_owls_ion_density_field(ion, ftype, itab) def _create_ion_mass_func(self, ftype, ion): """returns a function that calculates the ion mass of a particle""" def get_owls_ion_mass_field(ion, ftype, itab): def _func(field, data): m_frac, i_frac = _get_ion_mass_frac(ion, ftype, itab, data) return data[ftype, "particle_mass"] * m_frac * i_frac return _func ion_path = self._get_owls_ion_data_dir() fname = os.path.join(ion_path, ion + ".hdf5") itab = oit.IonTableOWLS(fname) return get_owls_ion_mass_field(ion, ftype, itab) # this function sets up the X_mass, X_density, X_fraction, and # X_number_density fields where X is the name of an OWLS element. # ------------------------------------------------------------- def setup_fluid_fields(self): return # this function returns the owls_ion_data directory. if it doesn't # exist it will download the data from http://yt-project.org/data # ------------------------------------------------------------- def _get_owls_ion_data_dir(self): txt = "Attempting to download ~ 30 Mb of owls ion data from %s to %s." data_file = "owls_ion_data.tar.gz" data_url = "http://yt-project.org/data" # get test_data_dir from yt config (ytcgf) # ---------------------------------------------- tdir = ytcfg.get("yt", "test_data_dir") # set download destination to tdir or ./ if tdir isn't defined # ---------------------------------------------- if tdir == "/does/not/exist": data_dir = "./" else: data_dir = tdir # check for owls_ion_data directory in data_dir # if not there download the tarball and untar it # ---------------------------------------------- owls_ion_path = os.path.join(data_dir, "owls_ion_data") if not os.path.exists(owls_ion_path): mylog.info(txt, data_url, data_dir) fname = os.path.join(data_dir, data_file) download_file(os.path.join(data_url, data_file), fname) cmnd = f"cd {data_dir}; tar xf {data_file}" os.system(cmnd) if not os.path.exists(owls_ion_path): raise RuntimeError("Failed to download owls ion data.") return owls_ion_path yt-project-yt-f043ac8/yt/frontends/owls/io.py000066400000000000000000000001761510711153200212610ustar00rootroot00000000000000from yt.frontends.gadget.io import IOHandlerGadgetHDF5 class IOHandlerOWLS(IOHandlerGadgetHDF5): _dataset_type = "OWLS" yt-project-yt-f043ac8/yt/frontends/owls/owls_ion_tables.py000066400000000000000000000134701510711153200240360ustar00rootroot00000000000000import numpy as np from yt.utilities.on_demand_imports import _h5py as h5py def h5rd(fname, path, dtype=None): """Read Data. Return a dataset located at in file as a numpy array. e.g. rd( fname, '/PartType0/Coordinates' ).""" data = None fid = h5py.h5f.open(fname.encode("latin-1"), h5py.h5f.ACC_RDONLY) dg = h5py.h5d.open(fid, path.encode("ascii")) if dtype is None: dtype = dg.dtype data = np.zeros(dg.shape, dtype=dtype) dg.read(h5py.h5s.ALL, h5py.h5s.ALL, data) fid.close() return data class IonTableSpectrum: """A class to handle the HM01 spectra in the OWLS ionization tables.""" def __init__(self, ion_file): where = "/header/spectrum/gammahi" self.GH1 = h5rd(ion_file, where) # GH1[1/s] where = "/header/spectrum/logenergy_ryd" self.logryd = h5rd(ion_file, where) # E[ryd] where = "/header/spectrum/logflux" self.logflux = h5rd(ion_file, where) # J[ergs/s/Hz/Sr/cm^2] where = "/header/spectrum/redshift" self.z = h5rd(ion_file, where) # z def return_table_GH1_at_z(self, z): # find redshift indices # ----------------------------------------------------------------- i_zlo = np.argmin(np.abs(self.z - z)) if self.z[i_zlo] < z: i_zhi = i_zlo + 1 else: i_zhi = i_zlo i_zlo = i_zlo - 1 z_frac = (z - self.z[i_zlo]) / (self.z[i_zhi] - self.z[i_zlo]) # find GH1 from table # ----------------------------------------------------------------- logGH1_all = np.log10(self.GH1) dlog_GH1 = logGH1_all[i_zhi] - logGH1_all[i_zlo] logGH1_table = logGH1_all[i_zlo] + z_frac * dlog_GH1 GH1_table = 10.0**logGH1_table return GH1_table class IonTableOWLS: """A class to handle OWLS ionization tables.""" DELTA_nH = 0.25 DELTA_T = 0.1 def __init__(self, ion_file): self.ion_file = ion_file # ionbal is indexed like [nH, T, z] # nH and T are log quantities # --------------------------------------------------------------- self.nH = h5rd(ion_file, "/logd") # log nH [cm^-3] self.T = h5rd(ion_file, "/logt") # log T [K] self.z = h5rd(ion_file, "/redshift") # z # read the ionization fractions # linear values stored in file so take log here # ionbal is the ionization balance (i.e. fraction) # --------------------------------------------------------------- self.ionbal = h5rd(ion_file, "/ionbal").astype(np.float64) self.ionbal_orig = self.ionbal.copy() ipositive = self.ionbal > 0.0 izero = np.logical_not(ipositive) self.ionbal[izero] = self.ionbal[ipositive].min() self.ionbal = np.log10(self.ionbal) # load in background spectrum # --------------------------------------------------------------- self.spectrum = IonTableSpectrum(ion_file) # calculate the spacing along each dimension # --------------------------------------------------------------- self.dnH = self.nH[1:] - self.nH[0:-1] self.dT = self.T[1:] - self.T[0:-1] self.dz = self.z[1:] - self.z[0:-1] self.order_str = "[log nH, log T, z]" # sets iz and fz # ----------------------------------------------------- def set_iz(self, z): if z <= self.z[0]: self.iz = 0 self.fz = 0.0 elif z >= self.z[-1]: self.iz = len(self.z) - 2 self.fz = 1.0 else: for iz in range(len(self.z) - 1): if z < self.z[iz + 1]: self.iz = iz self.fz = (z - self.z[iz]) / self.dz[iz] break # interpolate the table at a fixed redshift for the input # values of nH and T ( input should be log ). A simple # tri-linear interpolation is used. # ----------------------------------------------------- def interp(self, nH, T): nH = np.array(nH) T = np.array(T) if nH.size != T.size: raise ValueError(" owls_ion_tables: array size mismatch !!! ") # field discovery will have nH.size == 1 and T.size == 1 # in that case we simply return 1.0 if nH.size == 1 and T.size == 1: ionfrac = 1.0 return ionfrac # find inH and fnH # ----------------------------------------------------- x_nH = (nH - self.nH[0]) / self.DELTA_nH x_nH_clip = np.clip(x_nH, 0.0, self.nH.size - 1.001) fnH, inH = np.modf(x_nH_clip) inH = inH.astype(np.int32) # find iT and fT # ----------------------------------------------------- x_T = (T - self.T[0]) / self.DELTA_T x_T_clip = np.clip(x_T, 0.0, self.T.size - 1.001) fT, iT = np.modf(x_T_clip) iT = iT.astype(np.int32) # short names for previously calculated iz and fz # ----------------------------------------------------- iz = self.iz fz = self.fz # calculate interpolated value # use tri-linear interpolation on the log values # ----------------------------------------------------- ionfrac = ( self.ionbal[inH, iT, iz] * (1 - fnH) * (1 - fT) * (1 - fz) + self.ionbal[inH + 1, iT, iz] * (fnH) * (1 - fT) * (1 - fz) + self.ionbal[inH, iT + 1, iz] * (1 - fnH) * (fT) * (1 - fz) + self.ionbal[inH, iT, iz + 1] * (1 - fnH) * (1 - fT) * (fz) + self.ionbal[inH + 1, iT, iz + 1] * (fnH) * (1 - fT) * (fz) + self.ionbal[inH, iT + 1, iz + 1] * (1 - fnH) * (fT) * (fz) + self.ionbal[inH + 1, iT + 1, iz] * (fnH) * (fT) * (1 - fz) + self.ionbal[inH + 1, iT + 1, iz + 1] * (fnH) * (fT) * (fz) ) return 10**ionfrac yt-project-yt-f043ac8/yt/frontends/owls/simulation_handling.py000066400000000000000000000041371510711153200247030ustar00rootroot00000000000000import os from yt.frontends.gadget.simulation_handling import GadgetSimulation class OWLSSimulation(GadgetSimulation): r""" Initialize an OWLS Simulation object. Upon creation, the parameter file is parsed and the time and redshift are calculated and stored in all_outputs. A time units dictionary is instantiated to allow for time outputs to be requested with physical time units. The get_time_series can be used to generate a DatasetSeries object. parameter_filename : str The simulation parameter file. find_outputs : bool If True, the OutputDir directory is searched for datasets. Time and redshift information are gathered by temporarily instantiating each dataset. This can be used when simulation data was created in a non-standard way, making it difficult to guess the corresponding time and redshift information. Default: False. Examples -------- >>> import yt >>> es = yt.load_simulation("my_simulation.par", "OWLS") >>> es.get_time_series() >>> for ds in es: ... print(ds.current_time) """ def __init__(self, parameter_filename, find_outputs=False): GadgetSimulation.__init__(self, parameter_filename, find_outputs=find_outputs) def _snapshot_format(self, index=None): """ The snapshot filename for a given index. Modify this for different naming conventions. """ if self.parameters["OutputDir"].startswith("/"): data_dir = self.parameters["OutputDir"] else: data_dir = os.path.join(self.directory, self.parameters["OutputDir"]) if self.parameters["NumFilesPerSnapshot"] > 1: suffix = ".0" else: suffix = "" if self.parameters["SnapFormat"] == 3: suffix += ".hdf5" if index is None: count = "*" else: count = "%03d" % index keyword = f"{self.parameters['SnapshotFileBase']}_{count}" filename = os.path.join(keyword, f"{keyword}{suffix}") return os.path.join(data_dir, filename) yt-project-yt-f043ac8/yt/frontends/owls/tests/000077500000000000000000000000001510711153200214365ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls/tests/__init__.py000066400000000000000000000000001510711153200235350ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls/tests/test_outputs.py000066400000000000000000000034061510711153200245750ustar00rootroot00000000000000from collections import OrderedDict from yt.data_objects.particle_filters import add_particle_filter from yt.frontends.owls.api import OWLSDataset from yt.testing import ParticleSelectionComparison, requires_file, requires_module from yt.utilities.answer_testing.framework import data_dir_load, requires_ds, sph_answer os33 = "snapshot_033/snap_033.0.hdf5" # This maps from field names to weight field names to use for projections _fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "He_p0_number_density"), None), (("gas", "velocity_magnitude"), None), ] ) @requires_module("h5py") @requires_ds(os33, big_data=True) def test_snapshot_033(): ds = data_dir_load(os33) psc = ParticleSelectionComparison(ds) psc.run_defaults() for test in sph_answer(ds, "snap_033", 2 * 128**3, _fields): test_snapshot_033.__name__ = test.description yield test @requires_module("h5py") @requires_file(os33) def test_OWLSDataset(): assert isinstance(data_dir_load(os33), OWLSDataset) @requires_module("h5py") @requires_ds(os33) def test_OWLS_particlefilter(): ds = data_dir_load(os33) ad = ds.all_data() def cold_gas(pfilter, data): temperature = data[pfilter.filtered_type, "Temperature"] filter = temperature.in_units("K") <= 1e5 return filter add_particle_filter( "gas_cold", function=cold_gas, filtered_type="PartType0", requires=["Temperature"], ) ds.add_particle_filter("gas_cold") mask = ad["PartType0", "Temperature"] <= 1e5 assert ( ad["PartType0", "Temperature"][mask].shape == ad["gas_cold", "Temperature"].shape ) yt-project-yt-f043ac8/yt/frontends/owls_subfind/000077500000000000000000000000001510711153200220065ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls_subfind/__init__.py000066400000000000000000000000521510711153200241140ustar00rootroot00000000000000""" API for HaloCatalog frontend. """ yt-project-yt-f043ac8/yt/frontends/owls_subfind/api.py000066400000000000000000000002261510711153200231310ustar00rootroot00000000000000from . import tests from .data_structures import OWLSSubfindDataset from .fields import OWLSSubfindFieldInfo from .io import IOHandlerOWLSSubfindHDF5 yt-project-yt-f043ac8/yt/frontends/owls_subfind/data_structures.py000066400000000000000000000206501510711153200255770ustar00rootroot00000000000000import glob import os from collections import defaultdict import numpy as np from yt.data_objects.static_output import ParticleDataset, ParticleFile from yt.frontends.gadget.data_structures import _fix_unit_ordering from yt.funcs import only_on_root, setdefaultattr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.exceptions import YTException from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import OWLSSubfindFieldInfo class OWLSSubfindParticleIndex(ParticleIndex): chunksize = -1 def __init__(self, ds, dataset_type): super().__init__(ds, dataset_type) def _calculate_particle_index_starts(self): # Halo indices are not saved in the file, so we must count by hand. # File 0 has halos 0 to N_0 - 1, file 1 has halos N_0 to N_0 + N_1 - 1, etc. particle_count = defaultdict(int) offset_count = 0 for data_file in self.data_files: data_file.index_start = { ptype: particle_count[ptype] for ptype in data_file.total_particles } data_file.offset_start = offset_count for ptype in data_file.total_particles: particle_count[ptype] += data_file.total_particles[ptype] offset_count += data_file.total_offset def _calculate_file_offset_map(self): # After the FOF is performed, a load-balancing step redistributes halos # and then writes more fields. Here, for each file, we create a list of # files which contain the rest of the redistributed particles. ifof = np.array( [data_file.total_particles["FOF"] for data_file in self.data_files] ) isub = np.array([data_file.total_offset for data_file in self.data_files]) subend = isub.cumsum() fofend = ifof.cumsum() istart = np.digitize(fofend - ifof, subend - isub) - 1 iend = np.clip(np.digitize(fofend, subend), 0, ifof.size - 2) for i, data_file in enumerate(self.data_files): data_file.offset_files = self.data_files[istart[i] : iend[i] + 1] def _detect_output_fields(self): # TODO: Add additional fields self._calculate_particle_index_starts() self._calculate_file_offset_map() dsl = [] units = {} for dom in self.data_files: fl, _units = self.io._identify_fields(dom) units.update(_units) for f in fl: if f not in dsl: dsl.append(f) self.field_list = dsl ds = self.dataset ds.particle_types = tuple({pt for pt, ds in dsl}) # This is an attribute that means these particle types *actually* # exist. As in, they are real, in the dataset. ds.field_units.update(units) ds.particle_types_raw = ds.particle_types class OWLSSubfindHDF5File(ParticleFile): def __init__(self, ds, io, filename, file_id, bounds): super().__init__(ds, io, filename, file_id, bounds) with h5py.File(filename, mode="r") as f: self.header = {field: f.attrs[field] for field in f.attrs.keys()} class OWLSSubfindDataset(ParticleDataset): _load_requirements = ["h5py"] _index_class = OWLSSubfindParticleIndex _file_class = OWLSSubfindHDF5File _field_info_class = OWLSSubfindFieldInfo _suffix = ".hdf5" def __init__( self, filename, dataset_type="subfind_hdf5", index_order=None, index_filename=None, units_override=None, unit_system="cgs", ): super().__init__( filename, dataset_type, index_order=index_order, index_filename=index_filename, units_override=units_override, unit_system=unit_system, ) def _parse_parameter_file(self): handle = h5py.File(self.parameter_filename, mode="r") hvals = {} hvals.update((str(k), v) for k, v in handle["/Header"].attrs.items()) hvals["NumFiles"] = hvals["NumFilesPerSnapshot"] hvals["Massarr"] = hvals["MassTable"] self.dimensionality = 3 self.refine_by = 2 # Set standard values if "Time_GYR" in hvals: self.current_time = self.quan(hvals["Time_GYR"], "Gyr") self.domain_left_edge = np.zeros(3, "float64") self.domain_right_edge = np.ones(3, "float64") * hvals["BoxSize"] self.domain_dimensions = np.ones(3, "int32") self.cosmological_simulation = 1 self._periodicity = (True, True, True) self.current_redshift = hvals["Redshift"] self.omega_lambda = hvals["OmegaLambda"] self.omega_matter = hvals["Omega0"] self.hubble_constant = hvals["HubbleParam"] self.parameters = hvals prefix = os.path.abspath( os.path.join( os.path.dirname(self.parameter_filename), os.path.basename(self.parameter_filename).split(".", 1)[0], ) ) suffix = self.parameter_filename.rsplit(".", 1)[-1] self.filename_template = f"{prefix}.%(num)i.{suffix}" self.file_count = len(glob.glob(prefix + "*" + self._suffix)) if self.file_count == 0: raise YTException(message="No data files found.", ds=self) self.particle_types = ("FOF", "SUBFIND") self.particle_types_raw = ("FOF", "SUBFIND") # To avoid having to open files twice self._unit_base = {} self._unit_base.update((str(k), v) for k, v in handle["/Units"].attrs.items()) handle.close() def _set_code_unit_attributes(self): # Set a sane default for cosmological simulations. if self._unit_base is None and self.cosmological_simulation == 1: only_on_root(mylog.info, "Assuming length units are in Mpc/h (comoving)") self._unit_base = {"length": (1.0, "Mpccm/h")} # The other same defaults we will use from the standard Gadget # defaults. unit_base = self._unit_base or {} if "length" in unit_base: length_unit = unit_base["length"] elif "UnitLength_in_cm" in unit_base: if self.cosmological_simulation == 0: length_unit = (unit_base["UnitLength_in_cm"], "cm") else: length_unit = (unit_base["UnitLength_in_cm"], "cmcm/h") else: raise RuntimeError length_unit = _fix_unit_ordering(length_unit) setdefaultattr(self, "length_unit", self.quan(length_unit[0], length_unit[1])) if "velocity" in unit_base: velocity_unit = unit_base["velocity"] elif "UnitVelocity_in_cm_per_s" in unit_base: velocity_unit = (unit_base["UnitVelocity_in_cm_per_s"], "cm/s") else: velocity_unit = (1e5, "cm/s * sqrt(a)") velocity_unit = _fix_unit_ordering(velocity_unit) setdefaultattr( self, "velocity_unit", self.quan(velocity_unit[0], velocity_unit[1]) ) # We set hubble_constant = 1.0 for non-cosmology, so this is safe. # Default to 1e10 Msun/h if mass is not specified. if "mass" in unit_base: mass_unit = unit_base["mass"] elif "UnitMass_in_g" in unit_base: if self.cosmological_simulation == 0: mass_unit = (unit_base["UnitMass_in_g"], "g") else: mass_unit = (unit_base["UnitMass_in_g"], "g/h") else: # Sane default mass_unit = (1.0, "1e10*Msun/h") mass_unit = _fix_unit_ordering(mass_unit) setdefaultattr(self, "mass_unit", self.quan(mass_unit[0], mass_unit[1])) if "time" in unit_base: time_unit = unit_base["time"] elif "UnitTime_in_s" in unit_base: time_unit = (unit_base["UnitTime_in_s"], "s") else: tu = (self.length_unit / self.velocity_unit).to("yr/h") time_unit = (tu.d, tu.units) setdefaultattr(self, "time_unit", self.quan(time_unit[0], time_unit[1])) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False need_groups = ["Constants", "Header", "Parameters", "Units", "FOF"] valid = True try: fh = h5py.File(filename, mode="r") valid = all(ng in fh["/"] for ng in need_groups) fh.close() except Exception: valid = False return valid yt-project-yt-f043ac8/yt/frontends/owls_subfind/fields.py000066400000000000000000000041101510711153200236220ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer m_units = "code_mass" mdot_units = "code_mass / code_time" p_units = "Mpccm/h" v_units = "1e5 * cmcm / s" class OWLSSubfindFieldInfo(FieldInfoContainer): known_particle_fields: KnownFieldsT = ( ("CenterOfMass_0", (p_units, ["particle_position_x"], None)), ("CenterOfMass_1", (p_units, ["particle_position_y"], None)), ("CenterOfMass_2", (p_units, ["particle_position_z"], None)), ("CentreOfMass_0", (p_units, ["particle_position_x"], None)), ("CentreOfMass_1", (p_units, ["particle_position_y"], None)), ("CentreOfMass_2", (p_units, ["particle_position_z"], None)), ("CenterOfMassVelocity_0", (v_units, ["particle_velocity_x"], None)), ("CenterOfMassVelocity_1", (v_units, ["particle_velocity_y"], None)), ("CenterOfMassVelocity_2", (v_units, ["particle_velocity_z"], None)), ("Velocity_0", (v_units, ["particle_velocity_x"], None)), ("Velocity_1", (v_units, ["particle_velocity_y"], None)), ("Velocity_2", (v_units, ["particle_velocity_z"], None)), ("Mass", (m_units, ["particle_mass"], None)), ("Halo_M_Crit200", (m_units, ["Virial Mass"], None)), ("Halo_M_Crit2500", (m_units, [], None)), ("Halo_M_Crit500", (m_units, [], None)), ("Halo_M_Mean200", (m_units, [], None)), ("Halo_M_Mean2500", (m_units, [], None)), ("Halo_M_Mean500", (m_units, [], None)), ("Halo_M_TopHat200", (m_units, [], None)), ("Halo_R_Crit200", (p_units, ["Virial Radius"], None)), ("Halo_R_Crit2500", (p_units, [], None)), ("Halo_R_Crit500", (p_units, [], None)), ("Halo_R_Mean200", (p_units, [], None)), ("Halo_R_Mean2500", (p_units, [], None)), ("Halo_R_Mean500", (p_units, [], None)), ("Halo_R_TopHat200", (p_units, [], None)), ("BH_Mass", (m_units, [], None)), ("Stars/Mass", (m_units, [], None)), ("BH_Mdot", (mdot_units, [], None)), ("StarFormationRate", (mdot_units, [], None)), ) yt-project-yt-f043ac8/yt/frontends/owls_subfind/io.py000066400000000000000000000213351510711153200227730ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.utilities.io_handler import BaseParticleIOHandler from yt.utilities.on_demand_imports import _h5py as h5py _pos_names = ["CenterOfMass", "CentreOfMass"] class IOHandlerOWLSSubfindHDF5(BaseParticleIOHandler): _dataset_type = "subfind_hdf5" _position_name = None def __init__(self, ds): super().__init__(ds) self.offset_fields = set() def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. for data_file in self._sorted_chunk_iterator(chunks): with h5py.File(data_file.filename, mode="r") as f: for ptype in sorted(ptf): pcount = data_file.total_particles[ptype] coords = f[ptype][self._position_name][()].astype("float64") coords = np.resize(coords, (pcount, 3)) x = coords[:, 0] y = coords[:, 1] z = coords[:, 2] yield ptype, (x, y, z), 0.0 def _yield_coordinates(self, data_file): ptypes = self.ds.particle_types_raw with h5py.File(data_file.filename, mode="r") as f: for ptype in sorted(ptypes): pcount = data_file.total_particles[ptype] coords = f[ptype][self._position_name][()].astype("float64") coords = np.resize(coords, (pcount, 3)) yield ptype, coords def _read_offset_particle_field(self, field, data_file, fh): field_data = np.empty(data_file.total_particles["FOF"], dtype="float64") fofindex = ( np.arange(data_file.total_particles["FOF"]) + data_file.index_start["FOF"] ) for offset_file in data_file.offset_files: if fh.filename == offset_file.filename: ofh = fh else: ofh = h5py.File(offset_file.filename, mode="r") subindex = np.arange(offset_file.total_offset) + offset_file.offset_start substart = max(fofindex[0] - subindex[0], 0) subend = min(fofindex[-1] - subindex[0], subindex.size - 1) fofstart = substart + subindex[0] - fofindex[0] fofend = subend + subindex[0] - fofindex[0] field_data[fofstart : fofend + 1] = ofh["SUBFIND"][field][ substart : subend + 1 ] return field_data def _read_particle_fields(self, chunks, ptf, selector): # Now we have all the sizes, and we can allocate for data_file in self._sorted_chunk_iterator(chunks): with h5py.File(data_file.filename, mode="r") as f: for ptype, field_list in sorted(ptf.items()): pcount = data_file.total_particles[ptype] if pcount == 0: continue coords = f[ptype][self._position_name][()].astype("float64") coords = np.resize(coords, (pcount, 3)) x = coords[:, 0] y = coords[:, 1] z = coords[:, 2] mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: if field in self.offset_fields: field_data = self._read_offset_particle_field( field, data_file, f ) else: if field == "particle_identifier": field_data = ( np.arange(data_file.total_particles[ptype]) + data_file.index_start[ptype] ) elif field in f[ptype]: field_data = f[ptype][field][()].astype("float64") else: fname = field[: field.rfind("_")] field_data = f[ptype][fname][()].astype("float64") my_div = field_data.size / pcount if my_div > 1: field_data = np.resize( field_data, (int(pcount), int(my_div)) ) findex = int(field[field.rfind("_") + 1 :]) field_data = field_data[:, findex] data = field_data[mask] yield (ptype, field), data def _count_particles(self, data_file): with h5py.File(data_file.filename, mode="r") as f: pcount = {"FOF": get_one_attr(f["FOF"], ["Number_of_groups", "Ngroups"])} if "SUBFIND" in f: # We need this to figure out where the offset fields are stored. data_file.total_offset = get_one_attr( f["SUBFIND"], ["Number_of_groups", "Ngroups"] ) pcount["SUBFIND"] = get_one_attr( f["FOF"], ["Number_of_subgroups", "Nsubgroups"] ) else: data_file.total_offset = 0 pcount["SUBFIND"] = 0 return pcount def _identify_fields(self, data_file): fields = [] pcount = data_file.total_particles if sum(pcount.values()) == 0: return fields, {} with h5py.File(data_file.filename, mode="r") as f: for ptype in self.ds.particle_types_raw: if data_file.total_particles[ptype] == 0: continue self._identify_position_name(f[ptype]) fields.append((ptype, "particle_identifier")) my_fields, my_offset_fields = subfind_field_list( f[ptype], ptype, data_file.total_particles ) fields.extend(my_fields) self.offset_fields = self.offset_fields.union(set(my_offset_fields)) return fields, {} def _identify_position_name(self, fh): if self._position_name is not None: return for pname in _pos_names: if pname in fh: self._position_name = pname return def get_one_attr(fh, attrs, default=None, error=True): """ Try getting from a list of attrs. Return the first one that exists. """ for attr in attrs: if attr in fh.attrs: return fh.attrs[attr] if error: raise RuntimeError( f"Could not find any of these attributes: {attrs}. " f"Available attributes: {fh.attrs.keys()}" ) return default def subfind_field_list(fh, ptype, pcount): fields = [] offset_fields = [] for field in fh.keys(): if "PartType" in field: # These are halo member particles continue elif isinstance(fh[field], h5py.Group): my_fields, my_offset_fields = subfind_field_list(fh[field], ptype, pcount) fields.extend(my_fields) my_offset_fields.extend(offset_fields) else: if not fh[field].size % pcount[ptype]: my_div = fh[field].size / pcount[ptype] fname = fh[field].name[fh[field].name.find(ptype) + len(ptype) + 1 :] if my_div > 1: for i in range(int(my_div)): fields.append((ptype, "%s_%d" % (fname, i))) else: fields.append((ptype, fname)) elif ( ptype == "SUBFIND" and not fh[field].size % fh["/SUBFIND"].attrs["Number_of_groups"] ): # These are actually FOF fields, but they were written after # a load balancing step moved halos around and thus they do not # correspond to the halos stored in the FOF group. my_div = fh[field].size / fh["/SUBFIND"].attrs["Number_of_groups"] fname = fh[field].name[fh[field].name.find(ptype) + len(ptype) + 1 :] if my_div > 1: for i in range(int(my_div)): fields.append(("FOF", "%s_%d" % (fname, i))) else: fields.append(("FOF", fname)) offset_fields.append(fname) else: mylog.warning( "Cannot add field (%s, %s) with size %d.", ptype, fh[field].name, fh[field].size, ) continue return fields, offset_fields yt-project-yt-f043ac8/yt/frontends/owls_subfind/tests/000077500000000000000000000000001510711153200231505ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls_subfind/tests/__init__.py000066400000000000000000000000001510711153200252470ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/owls_subfind/tests/test_outputs.py000066400000000000000000000017611510711153200263110ustar00rootroot00000000000000import os.path from numpy.testing import assert_equal from yt.testing import requires_module from yt.utilities.answer_testing.framework import ( FieldValuesTest, data_dir_load, requires_ds, ) # from yt.frontends.owls_subfind.api import OWLSSubfindDataset _fields = ( ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ("all", "particle_mass"), ) # a dataset with empty files g1 = "owls_fof_halos/groups_001/group_001.0.hdf5" g8 = "owls_fof_halos/groups_008/group_008.0.hdf5" @requires_module("h5py") @requires_ds(g8) def test_fields_g8(): ds = data_dir_load(g8) assert_equal(str(ds), os.path.basename(g8)) for field in _fields: yield FieldValuesTest(g8, field, particle_type=True) @requires_module("h5py") @requires_ds(g1) def test_fields_g1(): ds = data_dir_load(g1) assert_equal(str(ds), os.path.basename(g1)) for field in _fields: yield FieldValuesTest(g1, field, particle_type=True) yt-project-yt-f043ac8/yt/frontends/parthenon/000077500000000000000000000000001510711153200213065ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/parthenon/__init__.py000066400000000000000000000000001510711153200234050ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/parthenon/api.py000066400000000000000000000002571510711153200224350ustar00rootroot00000000000000from . import tests from .data_structures import ParthenonDataset, ParthenonGrid, ParthenonHierarchy from .fields import ParthenonFieldInfo from .io import IOHandlerParthenon yt-project-yt-f043ac8/yt/frontends/parthenon/data_structures.py000066400000000000000000000267201510711153200251030ustar00rootroot00000000000000import os import warnings import weakref import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.static_output import Dataset from yt.fields.magnetic_field import get_magnetic_normalization from yt.funcs import mylog, setdefaultattr from yt.geometry.api import Geometry from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.chemical_formulas import compute_mu from yt.utilities.file_handler import HDF5FileHandler from .fields import ParthenonFieldInfo _geom_map = { "UniformCartesian": Geometry.CARTESIAN, "UniformCylindrical": Geometry.CYLINDRICAL, "UniformSpherical": Geometry.SPHERICAL, } # fmt: off _cis = np.array( [ [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1], ], dtype="int64", ) # fmt: on class ParthenonGrid(AMRGridPatch): _id_offset = 0 def __init__(self, id, index, level): AMRGridPatch.__init__(self, id, filename=index.index_filename, index=index) self.Parent = None self.Children = [] self.Level = level def _setup_dx(self): # So first we figure out what the index is. We don't assume # that dx=dy=dz , at least here. We probably do elsewhere. id = self.id - self._id_offset LE, RE = self.index.grid_left_edge[id, :], self.index.grid_right_edge[id, :] self.dds = self.ds.arr((RE - LE) / self.ActiveDimensions, "code_length") self.field_data["dx"], self.field_data["dy"], self.field_data["dz"] = self.dds def retrieve_ghost_zones(self, n_zones, fields, all_levels=False, smoothed=False): if smoothed: warnings.warn( "ghost-zones interpolation/smoothing is not " "currently supported for Parthenon data.", category=RuntimeWarning, stacklevel=2, ) smoothed = False return super().retrieve_ghost_zones( n_zones, fields, all_levels=all_levels, smoothed=smoothed ) class ParthenonHierarchy(GridIndex): grid = ParthenonGrid _dataset_type = "parthenon" _data_file = None def __init__(self, ds, dataset_type="parthenon"): self.dataset = weakref.proxy(ds) self.directory = os.path.dirname(self.dataset.filename) self.dataset_type = dataset_type # for now, the index file is the dataset! self.index_filename = self.dataset.filename self._handle = ds._handle GridIndex.__init__(self, ds, dataset_type) def _detect_output_fields(self): self.field_list = [("parthenon", k) for k in self.dataset._field_map] def _count_grids(self): self.num_grids = self._handle["Info"].attrs["NumMeshBlocks"] def _parse_index(self): num_grids = self._handle["Info"].attrs["NumMeshBlocks"] # TODO: In an unlikely case this would use too much memory, implement # chunked read along 1 dim x = self._handle["Locations"]["x"][:, :] y = self._handle["Locations"]["y"][:, :] z = self._handle["Locations"]["z"][:, :] mesh_block_size = self._handle["Info"].attrs["MeshBlockSize"] self.grids = np.empty(self.num_grids, dtype="object") levels = self._handle["Levels"][:] for i in range(num_grids): self.grid_left_edge[i] = np.array( [x[i, 0], y[i, 0], z[i, 0]], dtype="float64" ) self.grid_right_edge[i] = np.array( [x[i, -1], y[i, -1], z[i, -1]], dtype="float64" ) self.grid_dimensions[i] = mesh_block_size self.grids[i] = self.grid(i, self, levels[i]) if self.dataset.dimensionality <= 2: self.grid_right_edge[:, 2] = self.dataset.domain_right_edge[2] if self.dataset.dimensionality == 1: self.grid_right_edge[:, 1:] = self.dataset.domain_right_edge[1:] self.grid_particle_count = np.zeros([self.num_grids, 1], dtype="int64") def _populate_grid_objects(self): for g in self.grids: g._prepare_grid() g._setup_dx() self.max_level = self._handle["Info"].attrs["MaxLevel"] class ParthenonDataset(Dataset): _load_requirements = ["h5py"] _field_info_class = ParthenonFieldInfo _dataset_type = "parthenon" _index_class = ParthenonHierarchy def __init__( self, filename, dataset_type="parthenon", storage_filename=None, parameters=None, units_override=None, unit_system="cgs", default_species_fields=None, magnetic_normalization="gaussian", ): self.fluid_types += ("parthenon",) if parameters is None: parameters = {} self.specified_parameters = parameters if units_override is None: units_override = {} self._handle = HDF5FileHandler(filename) xrat = self._handle["Info"].attrs["RootGridDomain"][2] yrat = self._handle["Info"].attrs["RootGridDomain"][5] zrat = self._handle["Info"].attrs["RootGridDomain"][8] if xrat != 1.0 or yrat != 1.0 or zrat != 1.0: raise NotImplementedError( "Logarithmic grids not yet supported/tested in Parthenon frontend." ) self._magnetic_factor = get_magnetic_normalization(magnetic_normalization) self.geometry = _geom_map[self._handle["Info"].attrs["Coordinates"]] Dataset.__init__( self, filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) if storage_filename is None: storage_filename = self.basename + ".yt" self.storage_filename = storage_filename def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ for unit, cgs in [ ("length", "cm"), ("time", "s"), ("mass", "g"), ]: unit_param = f"Hydro/code_{unit}_cgs" # Use units, if provided in output if unit_param in self.parameters: setdefaultattr( self, f"{unit}_unit", self.quan(self.parameters[unit_param], cgs) ) # otherwise use code = cgs else: mylog.warning(f"Assuming 1.0 code_{unit} = 1.0 {cgs}") setdefaultattr(self, f"{unit}_unit", self.quan(1.0, cgs)) self.magnetic_unit = np.sqrt( self._magnetic_factor * self.mass_unit / (self.time_unit**2 * self.length_unit) ) self.magnetic_unit.convert_to_units("gauss") self.velocity_unit = self.length_unit / self.time_unit def _parse_parameter_file(self): self.parameters.update(self.specified_parameters) for key, val in self._handle["Params"].attrs.items(): if key in self.parameters.keys(): mylog.warning( f"Overriding existing {key!r} key in ds.parameters from data 'Params'" ) self.parameters[key] = val xmin, xmax = self._handle["Info"].attrs["RootGridDomain"][0:2] ymin, ymax = self._handle["Info"].attrs["RootGridDomain"][3:5] zmin, zmax = self._handle["Info"].attrs["RootGridDomain"][6:8] self.domain_left_edge = np.array([xmin, ymin, zmin], dtype="float64") self.domain_right_edge = np.array([xmax, ymax, zmax], dtype="float64") self.domain_width = self.domain_right_edge - self.domain_left_edge self.domain_dimensions = self._handle["Info"].attrs["RootGridSize"] self._field_map = {} k = 0 dnames = self._handle["Info"].attrs["OutputDatasetNames"] num_components = self._handle["Info"].attrs["NumComponents"] if "OutputFormatVersion" in self._handle["Info"].attrs.keys(): self.output_format_version = self._handle["Info"].attrs[ "OutputFormatVersion" ] else: raise NotImplementedError("Could not determine OutputFormatVersion.") # For a single variable, we need to convert it to a list for the following # zip to work. if isinstance(num_components, np.uint64): dnames = (dnames,) num_components = (num_components,) component_name_offset = 0 for dname, num_component in zip(dnames, num_components, strict=False): for j in range(num_component): fname = self._handle["Info"].attrs["ComponentNames"][ j + component_name_offset ] self._field_map[fname] = (dname, j) k += 1 component_name_offset = int(component_name_offset + num_component) self.refine_by = 2 dimensionality = 3 if self.domain_dimensions[2] == 1: dimensionality = 2 if self.domain_dimensions[1] == 1: dimensionality = 1 self.dimensionality = dimensionality self.current_time = self._handle["Info"].attrs["Time"] self.num_ghost_zones = 0 self.field_ordering = "fortran" self.boundary_conditions = [1] * 6 self.cosmological_simulation = False if "periodicity" in self.parameters: self._periodicity = tuple(self.parameters["periodicity"]) else: boundary_conditions = self._handle["Info"].attrs["BoundaryConditions"] inner_bcs = boundary_conditions[::2] # outer_bcs = boundary_conditions[1::2] ##Check self consistency # for inner_bc,outer_bc in zip(inner_bcs,outer_bcs): # if( (inner_bc == "periodicity" or outer_bc == "periodic") and inner_bc != outer_bc ): # raise Exception("Inconsistent periodicity in boundary conditions") self._periodicity = tuple(bc == "periodic" for bc in inner_bcs) if "gamma" in self.parameters: self.gamma = float(self.parameters["gamma"]) elif "Hydro/AdiabaticIndex" in self.parameters: self.gamma = self.parameters["Hydro/AdiabaticIndex"] else: mylog.warning( "Adiabatic index gamma could not be determined. Falling back to 5/3." ) self.gamma = 5.0 / 3.0 if "mu" in self.parameters: self.mu = self.parameters["mu"] elif "Hydro/mu" in self.parameters: self.mu = self.parameters["Hydro/mu"] # Legacy He_mass_fraction parameter implemented in AthenaPK elif "Hydro/He_mass_fraction" in self.parameters: He_mass_fraction = self.parameters["Hydro/He_mass_fraction"] self.mu = 1 / (He_mass_fraction * 3.0 / 4.0 + (1 - He_mass_fraction) * 2) # Fallback to primorial gas composition (and show warning) else: mylog.warning( "Plasma composition could not be determined in data file. Falling back to fully ionized primodial composition." ) self.mu = self.parameters.get("mu", compute_mu(self.default_species_fields)) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False return filename.endswith((".phdf", ".rhdf")) @property def _skip_cache(self): return True def __str__(self): return self.basename.rsplit(".", 1)[0] yt-project-yt-f043ac8/yt/frontends/parthenon/definitions.py000066400000000000000000000001061510711153200241700ustar00rootroot00000000000000""" Various definitions for various other modules and routines """ yt-project-yt-f043ac8/yt/frontends/parthenon/fields.py000066400000000000000000000161541510711153200231350ustar00rootroot00000000000000import numpy as np from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.funcs import mylog from yt.utilities.physical_constants import kboltz, mh mag_units = "code_magnetic" pres_units = "code_mass/(code_length*code_time**2)" rho_units = "code_mass / code_length**3" vel_units = "code_length / code_time" mom_units = "code_mass / code_length**2 / code_time" eng_units = "code_mass / code_length / code_time**2" def velocity_field(mom_field): def _velocity(field, data): return data[mom_field] / data["gas", "density"] return _velocity def _cooling_time_field(field, data): cooling_time = ( data["gas", "density"] * data["gas", "specific_thermal_energy"] / data["gas", "cooling_rate"] ) # Set cooling time where Cooling_Rate==0 to infinity inf_ct_mask = data["cooling_rate"] == 0 cooling_time[inf_ct_mask] = data.ds.quan(np.inf, "s") return cooling_time class ParthenonFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( # Need to provide info for both primitive and conserved variable as they # can be written indepdendently (or even in the same output file). # New field naming (i.e., "variable_component") of primitive variables ("prim_density", (rho_units, ["density"], None)), ("prim_velocity_1", (vel_units, ["velocity_x"], None)), ("prim_velocity_2", (vel_units, ["velocity_y"], None)), ("prim_velocity_3", (vel_units, ["velocity_z"], None)), ("prim_pressure", (pres_units, ["pressure"], None)), # Magnetic fields carry units of 1/sqrt(pi) so we cannot directly forward # and need to setup aliases below. ("prim_magnetic_field_1", (mag_units, [], None)), ("prim_magnetic_field_2", (mag_units, [], None)), ("prim_magnetic_field_3", (mag_units, [], None)), # New field naming (i.e., "variable_component") of conserved variables ("cons_density", (rho_units, ["density"], None)), ("cons_momentum_density_1", (mom_units, ["momentum_density_x"], None)), ("cons_momentum_density_2", (mom_units, ["momentum_density_y"], None)), ("cons_momentum_density_3", (mom_units, ["momentum_density_z"], None)), ("cons_total_energy_density", (eng_units, ["total_energy_density"], None)), # Magnetic fields carry units of 1/sqrt(pi) so we cannot directly forward # and need to setup aliases below. ("cons_magnetic_field_1", (mag_units, [], None)), ("cons_magnetic_field_2", (mag_units, [], None)), ("cons_magnetic_field_3", (mag_units, [], None)), # Legacy naming. Given that there's no conflict with the names above, # we can just define those here so that the frontend works with older data. ("Density", (rho_units, ["density"], None)), ("Velocity1", (mom_units, ["velocity_x"], None)), ("Velocity2", (mom_units, ["velocity_y"], None)), ("Velocity3", (mom_units, ["velocity_z"], None)), ("Pressure", (pres_units, ["pressure"], None)), ("MagneticField1", (mag_units, [], None)), ("MagneticField2", (mag_units, [], None)), ("MagneticField3", (mag_units, [], None)), ("MomentumDensity1", (mom_units, ["momentum_density_x"], None)), ("MomentumDensity2", (mom_units, ["momentum_density_y"], None)), ("MomentumDensity3", (mom_units, ["momentum_density_z"], None)), ("TotalEnergyDensity", (eng_units, ["total_energy_density"], None)), ) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases unit_system = self.ds.unit_system # Add velocity fields (if only momemtum densities are given) for i, comp in enumerate(self.ds.coordinates.axis_order): # Support both current and legacy scheme for mom_field_name in ["MomentumDensity", "cons_momentum_density_"]: mom_field = ("parthenon", f"{mom_field_name}{i+1}") if mom_field in self.field_list: self.add_field( ("gas", f"velocity_{comp}"), sampling_type="cell", function=velocity_field(mom_field), units=unit_system["velocity"], ) # Figure out thermal energy field if ("parthenon", "Pressure") in self.field_list or ( "parthenon", "prim_pressure", ) in self.field_list: # only show warning for non-AthenaPK codes if "Hydro/AdiabaticIndex" not in self.ds.parameters: mylog.warning( f"Adding a specific thermal energy field assuming an ideal gas with an " f"adiabatic index of {self.ds.gamma}" ) def _specific_thermal_energy(field, data): return ( data["gas", "pressure"] / (data.ds.gamma - 1.0) / data["gas", "density"] ) self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) elif ("parthenon", "TotalEnergyDensity") in self.field_list or ( "parthenon", "cons_total_energy_density", ) in self.field_list: def _specific_thermal_energy(field, data): eint = ( data["gas", "total_energy_density"] - data["gas", "kinetic_energy_density"] ) if ( ("parthenon", "MagneticField1") in self.field_list or ("parthenon", "prim_magnetic_field_1") in self.field_list or ("parthenon", "cons_magnetic_field_1") in self.field_list ): eint -= data["gas", "magnetic_energy_density"] return eint / data["gas", "density"] self.add_field( ("gas", "specific_thermal_energy"), sampling_type="cell", function=_specific_thermal_energy, units=unit_system["specific_energy"], ) # Add temperature field def _temperature(field, data): return ( (data["gas", "pressure"] / data["gas", "density"]) * data.ds.mu * mh / kboltz ) self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=unit_system["temperature"], ) # We can simply all all variants as only fields present will be added setup_magnetic_field_aliases( self, "parthenon", ["MagneticField%d" % ax for ax in (1, 2, 3)] ) setup_magnetic_field_aliases( self, "parthenon", ["prim_magnetic_field_%d" % ax for ax in (1, 2, 3)] ) setup_magnetic_field_aliases( self, "parthenon", ["cons_magnetic_field_%d" % ax for ax in (1, 2, 3)] ) yt-project-yt-f043ac8/yt/frontends/parthenon/io.py000066400000000000000000000054331510711153200222740ustar00rootroot00000000000000from itertools import groupby import numpy as np from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog # http://stackoverflow.com/questions/2361945/detecting-consecutive-integers-in-a-list def grid_sequences(grids): g_iter = sorted(grids, key=lambda g: g.id) for _, g in groupby(enumerate(g_iter), lambda i_x1: i_x1[0] - i_x1[1].id): seq = [v[1] for v in g] yield seq ii = [0, 1, 0, 1, 0, 1, 0, 1] jj = [0, 0, 1, 1, 0, 0, 1, 1] kk = [0, 0, 0, 0, 1, 1, 1, 1] class IOHandlerParthenon(BaseIOHandler): _particle_reader = False _dataset_type = "parthenon" def __init__(self, ds): super().__init__(ds) self._handle = ds._handle def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) f = self._handle rv = {} for field in fields: # Always use *native* 64-bit float. rv[field] = np.empty(size, dtype="=f8") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [f2 for f1, f2 in fields], ng, ) last_dname = None for field in fields: ftype, fname = field dname, fdi = self.ds._field_map[fname] if dname != last_dname: ds = f[f"/{dname}"] ind = 0 for chunk in chunks: for gs in grid_sequences(chunk.objs): start = gs[0].id - gs[0]._id_offset end = gs[-1].id - gs[-1]._id_offset + 1 if len(ds.shape) == 4: data = ds[start:end, :, :, :].transpose() else: data = ds[start:end, fdi, :, :, :].transpose() for i, g in enumerate(gs): ind += g.select(selector, data[..., i], rv[field], ind) last_dname = dname return rv def _read_chunk_data(self, chunk, fields): f = self._handle rv = {} for g in chunk.objs: rv[g.id] = {} if len(fields) == 0: return rv for field in fields: ftype, fname = field dname, fdi = self.ds._field_map[fname] ds = f[f"/{dname}"] for gs in grid_sequences(chunk.objs): start = gs[0].id - gs[0]._id_offset end = gs[-1].id - gs[-1]._id_offset + 1 if len(ds.shape) == 4: data = ds[start:end, :, :, :].transpose() else: data = ds[start:end, fdi, :, :, :].transpose() for i, g in enumerate(gs): rv[g.id][field] = np.asarray(data[..., i], "=f8") return rv yt-project-yt-f043ac8/yt/frontends/parthenon/misc.py000066400000000000000000000000001510711153200226010ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/parthenon/tests/000077500000000000000000000000001510711153200224505ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/parthenon/tests/__init__.py000066400000000000000000000000001510711153200245470ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/parthenon/tests/test_outputs.py000066400000000000000000000152001510711153200256020ustar00rootroot00000000000000import numpy as np from yt.frontends.parthenon.api import ParthenonDataset from yt.loaders import load from yt.testing import ( assert_allclose, assert_equal, assert_true, requires_file, ) from yt.utilities.answer_testing.framework import ( GenericArrayTest, data_dir_load, requires_ds, small_patch_amr, ) _fields_parthenon_advection = ( ("parthenon", "advected_0_0"), ("parthenon", "one_minus_advected"), ("parthenon", "one_minus_advected_sq"), ("parthenon", "one_minus_sqrt_one_minus_advected_sq_12"), ("parthenon", "one_minus_sqrt_one_minus_advected_sq_37"), ) # Simple 2D test (advected spherical blob) with AMR from the main Parthenon test suite # adjusted so that x1 != x2. # Ran with `./example/advection/advection-example -i ../tst/regression/test_suites/output_hdf5/parthinput.advection parthenon/mesh/nx1=128 parthenon/mesh/x1min=-1.0 parthenon/mesh/x1max=1.0 Advection/vx=2` # on changeset e5059ad parthenon_advection = "parthenon_advection/advection_2d.out0.final.phdf" @requires_ds(parthenon_advection) def test_loading_data(): ds = data_dir_load(parthenon_advection) assert_equal(str(ds), "advection_2d.out0.final") dd = ds.all_data() # test mesh dims vol = np.prod(ds.domain_right_edge - ds.domain_left_edge) assert_equal(vol, ds.quan(2.0, "code_length**3")) assert_allclose(dd.quantities.total_quantity("cell_volume"), vol) # test data for field in _fields_parthenon_advection: def field_func(name): return dd[name] yield GenericArrayTest(ds, field_func, args=[field]) # reading data of two fields and compare against each other (data is squared in output) ad = ds.all_data() assert_allclose( ad[("parthenon", "one_minus_advected")] ** 2.0, ad[("parthenon", "one_minus_advected_sq")], ) # check if the peak is in the domain center (and at the highest refinement level) dist_of_max_from_center = np.linalg.norm( ad.quantities.max_location(("parthenon", "advected_0_0"))[1:] - ds.domain_center ) dx_min, dx_max = ad.quantities.extrema(("index", "dx")) dy_min, dy_max = ad.quantities.extrema(("index", "dy")) assert_true(dist_of_max_from_center < np.min((dx_min, dy_min))) # 3D magnetized cluster center from downstream Parthenon code AthenaPK (Restart, Conserveds) athenapk_cluster = "athenapk_cluster/athenapk_cluster.restart.00000.rhdf" # Keplerian disk in 2D cylindrical from downstream Parthenon code AthenaPK (Data, Primitives) athenapk_disk = "athenapk_disk/athenapk_disk.prim.00000.phdf" @requires_file(athenapk_cluster) def test_AthenaPK_rhdf(): # Test that a downstream AthenaPK data set can be loaded with this Parthenon # frontend ds = data_dir_load(athenapk_cluster) assert isinstance(ds, ParthenonDataset) assert_equal(ds.domain_left_edge.in_units("code_length").v, (-0.15, -0.18, -0.2)) assert_equal(ds.domain_right_edge.in_units("code_length").v, (0.15, 0.18, 0.2)) @requires_file(athenapk_disk) def test_AthenaPK_phdf(): # Test that a downstream AthenaPK data set can be loaded with this Parthenon # frontend assert isinstance(data_dir_load(athenapk_disk), ParthenonDataset) _fields_derived = ( ("gas", "temperature"), ("gas", "specific_thermal_energy"), ) _fields_derived_cluster = (("gas", "magnetic_field_strength"),) @requires_ds(athenapk_cluster) def test_cluster(): ds = data_dir_load(athenapk_cluster) assert_equal(str(ds), "athenapk_cluster.restart.00000") for test in small_patch_amr(ds, _fields_derived + _fields_derived_cluster): test_cluster.__name__ = test.description yield test @requires_ds(athenapk_disk) @requires_ds(athenapk_cluster) def test_derived_fields(): # Check that derived fields like temperature are present in downstream # which define them # Check temperature and specific thermal energy becomes defined from primitives ds = data_dir_load(athenapk_disk) dd = ds.all_data() for field in _fields_derived: def field_func(name): return dd[name] yield GenericArrayTest(ds, field_func, args=[field]) # Check hydro, magnetic, and cooling fields defined from conserveds ds = data_dir_load(athenapk_cluster) dd = ds.all_data() for field in _fields_derived + _fields_derived_cluster: def field_func(name): return dd[name] yield GenericArrayTest(ds, field_func, args=[field]) @requires_file(athenapk_cluster) @requires_file(athenapk_disk) def test_adiabatic_index(): # Read adiabiatic index from dataset parameters ds = data_dir_load(athenapk_cluster) assert_allclose(ds.gamma, 5.0 / 3.0, rtol=1e-12) ds = data_dir_load(athenapk_disk) assert_allclose(ds.gamma, 4.0 / 3.0, rtol=1e-12) # Change adiabatic index from dataset parameters ds = load(athenapk_disk, parameters={"gamma": 9.0 / 8.0}) assert_allclose(ds.gamma, 9.0 / 8.0, rtol=1e-12) @requires_file(athenapk_cluster) def test_molecular_mass(): # Read mu from dataset parameters ds = data_dir_load(athenapk_cluster) assert_allclose(float(ds.mu), 0.5925925925925926, rtol=1e-12) # Change He mass fraction from dataset parameters ds = load(athenapk_disk, parameters={"mu": 137}) assert_equal(ds.mu, 137) @requires_file(athenapk_cluster) def test_units(): # Check units in dataset are loaded correctly ds = data_dir_load(athenapk_cluster) assert_allclose(float(ds.quan(1, "code_time").in_units("Gyr")), 1, rtol=1e-12) assert_allclose(float(ds.quan(1, "code_length").in_units("Mpc")), 1, rtol=1e-12) assert_allclose(float(ds.quan(1, "code_mass").in_units("msun")), 1e14, rtol=1e-12) @requires_file(athenapk_disk) def test_load_cylindrical(): # Load a cylindrical dataset of a full disk ds = data_dir_load(athenapk_disk) # Check that the domain edges match r in [0.5,2.0], theta in [0, 2pi] assert_equal(ds.domain_left_edge.in_units("code_length").v[:2], (0.5, 0)) assert_equal(ds.domain_right_edge.in_units("code_length").v[:2], (2.0, 2 * np.pi)) # Sedov blast wave with curvlinear coords run with RIOT riot_sedov_curvlinear = "riot_sedov_curvlinear/sedov.out1.final.phdf" @requires_file(riot_sedov_curvlinear) def test_load_riot_curvilinear(): # Load a cylindrical dataset of a full disk ds = data_dir_load(riot_sedov_curvlinear) assert ("parthenon", "c.c.bulk.pressure") in ds.field_list ad = ds.all_data() dth = ad["index", "dtheta"] vol = ad["index", "cell_volume"] rho = ad["parthenon", "c.c.bulk.rho"] assert np.all(np.abs(dth - 2 * np.pi) <= 1e-12) total_mass = (rho * vol).sum() assert np.all(np.abs(total_mass.value - 0.169646) <= 1e-8) yt-project-yt-f043ac8/yt/frontends/ramses/000077500000000000000000000000001510711153200206025ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ramses/__init__.py000066400000000000000000000000001510711153200227010ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ramses/api.py000066400000000000000000000002521510711153200217240ustar00rootroot00000000000000from . import tests from .data_structures import RAMSESDataset from .definitions import field_aliases from .fields import RAMSESFieldInfo from .io import IOHandlerRAMSES yt-project-yt-f043ac8/yt/frontends/ramses/data_structures.py000066400000000000000000001203401510711153200243700ustar00rootroot00000000000000import os import weakref from collections import defaultdict from functools import cached_property from itertools import product from pathlib import Path import numpy as np from yt.arraytypes import blankRecordArray from yt.data_objects.index_subobjects.octree_subset import OctreeSubset from yt.data_objects.particle_filters import add_particle_filter from yt.data_objects.static_output import Dataset from yt.funcs import mylog, setdefaultattr from yt.geometry.geometry_handler import YTDataChunk from yt.geometry.oct_container import RAMSESOctreeContainer from yt.geometry.oct_geometry_handler import OctreeIndex from yt.utilities.cython_fortran_utils import FortranFile as fpu from yt.utilities.lib.cosmology_time import t_frw, tau_frw from yt.utilities.on_demand_imports import _f90nml as f90nml from yt.utilities.physical_constants import kb, mp from .definitions import ( OUTPUT_DIR_EXP, OUTPUT_DIR_RE, STANDARD_FILE_RE, field_aliases, particle_families, ramses_header, ) from .field_handlers import get_field_handlers from .fields import RAMSESFieldInfo from .hilbert import get_intersecting_cpus from .io_utils import fill_hydro, read_amr from .particle_handlers import get_particle_handlers class RAMSESFileSanitizer: """A class to handle the different files that can be passed and associated safely to a RAMSES output.""" root_folder = None # Path | None: path to the root folder info_fname = None # Path | None: path to the info file group_name = None # str | None: name of the first group folder (if any) def __init__(self, filename): # Make the resolve optional, so that it works with symlinks paths_to_try = (Path(filename), Path(filename).resolve()) self.original_filename = filename self.output_dir = None self.info_fname = None check_functions = (self.test_with_standard_file, self.test_with_folder_name) # Loop on both the functions and the tested paths for path, check_fun in product(paths_to_try, check_functions): ok, output_dir, group_dir, info_fname = check_fun(path) if ok: break # Early exit if the ok flag is False if not ok: return self.root_folder = output_dir self.group_name = group_dir.name if group_dir else None self.info_fname = info_fname def validate(self) -> None: # raise a TypeError if self.original_filename is not a valid path # we also want to expand '$USER' and '~' because os.path.exists('~') is always False filename: str = os.path.expanduser(self.original_filename) if not os.path.exists(filename): raise FileNotFoundError(rf"No such file or directory '{filename!s}'") if self.root_folder is None: raise ValueError( f"Could not determine output directory from '{filename!s}'\n" f"Expected a directory name of form {OUTPUT_DIR_EXP!r} " "containing an info_*.txt file and amr_* files." ) # This last case is (erroneously ?) marked as unreachable by mypy # If/when this bug is fixed upstream, mypy will warn that the unused # 'type: ignore' comment can be removed if self.info_fname is None: # type: ignore [unreachable] raise ValueError(f"Failed to detect info file from '{filename!s}'") @property def is_valid(self) -> bool: try: self.validate() except (TypeError, FileNotFoundError, ValueError): return False else: return True @staticmethod def check_standard_files(folder, iout): """Return True if the folder contains an amr file and the info file.""" # Check that the "amr_" and "info_" files exist ok = (folder / f"amr_{iout}.out00001").is_file() ok &= (folder / f"info_{iout}.txt").is_file() return ok @staticmethod def _match_output_and_group( path: Path, ) -> tuple[Path, Path | None, str | None]: # Make sure we work with a directory of the form `output_XXXXX` for p in (path, path.parent): match = OUTPUT_DIR_RE.match(p.name) if match: path = p break if match is None: return path, None, None iout = match.group(1) # See whether a folder named `group_YYYYY` exists group_dir = path / "group_00001" if group_dir.is_dir(): return path, group_dir, iout else: return path, None, iout @classmethod def test_with_folder_name( cls, output_dir: Path ) -> tuple[bool, Path | None, Path | None, Path | None]: output_dir, group_dir, iout = cls._match_output_and_group(output_dir) ok = output_dir.is_dir() and iout is not None info_fname: Path | None if ok: parent_dir = group_dir or output_dir ok &= cls.check_standard_files(parent_dir, iout) info_fname = parent_dir / f"info_{iout}.txt" else: info_fname = None return ok, output_dir, group_dir, info_fname @classmethod def test_with_standard_file( cls, filename: Path ) -> tuple[bool, Path | None, Path | None, Path | None]: output_dir, group_dir, iout = cls._match_output_and_group(filename.parent) ok = ( filename.is_file() and STANDARD_FILE_RE.match(filename.name) is not None and iout is not None ) info_fname: Path | None if ok: parent_dir = group_dir or output_dir ok &= cls.check_standard_files(parent_dir, iout) info_fname = parent_dir / f"info_{iout}.txt" else: info_fname = None return ok, output_dir, group_dir, info_fname class RAMSESDomainFile: _last_mask = None _last_selector_id = None _hydro_offset = None _level_count = None _oct_handler_initialized = False _amr_header_initialized = False def __init__(self, ds, domain_id): self.ds = ds self.domain_id = domain_id num = os.path.basename(ds.parameter_filename).split(".")[0].split("_")[1] rootdir = ds.root_folder basedir = os.path.abspath(os.path.dirname(ds.parameter_filename)) basename = "%s/%%s_%s.out%05i" % (basedir, num, domain_id) part_file_descriptor = f"{basedir}/part_file_descriptor.txt" if ds.num_groups > 0: igroup = ((domain_id - 1) // ds.group_size) + 1 basename = "%s/group_%05i/%%s_%s.out%05i" % ( rootdir, igroup, num, domain_id, ) else: basename = "%s/%%s_%s.out%05i" % (basedir, num, domain_id) for t in ["grav", "amr"]: setattr(self, f"{t}_fn", basename % t) self._part_file_descriptor = part_file_descriptor self.max_level = self.ds.parameters["levelmax"] - self.ds.parameters["levelmin"] # Autodetect field files field_handlers = [FH(self) for FH in get_field_handlers() if FH.any_exist(ds)] self.field_handlers = field_handlers for fh in field_handlers: mylog.debug("Detected fluid type %s in domain_id=%s", fh.ftype, domain_id) fh.detect_fields(ds) # self._add_ftype(fh.ftype) # Autodetect particle files particle_handlers = [ PH(self) for PH in get_particle_handlers() if PH.any_exist(ds) ] self.particle_handlers = particle_handlers def __repr__(self): return "RAMSESDomainFile: %i" % self.domain_id @property def level_count(self): lvl_count = None for fh in self.field_handlers: fh.offset if lvl_count is None: lvl_count = fh.level_count.copy() else: lvl_count += fh._level_count return lvl_count @property def amr_file(self): if hasattr(self, "_amr_file") and not self._amr_file.close: self._amr_file.seek(0) return self._amr_file f = fpu(self.amr_fn) self._amr_file = f f.seek(0) return f def _read_amr_header(self): if self._amr_header_initialized: return hvals = {} with self.amr_file as f: f.seek(0) for header in ramses_header(hvals): hvals.update(f.read_attrs(header)) # For speedup, skip reading of 'headl' and 'taill' f.skip(2) hvals["numbl"] = f.read_vector("i") # That's the header, now we skip a few. hvals["numbl"] = np.array(hvals["numbl"]).reshape( (hvals["nlevelmax"], hvals["ncpu"]) ) f.skip() if hvals["nboundary"] > 0: f.skip(2) self._ngridbound = f.read_vector("i").astype("int64") else: self._ngridbound = np.zeros(hvals["nlevelmax"], dtype="int64") _free_mem = f.read_attrs((("free_mem", 5, "i"),)) _ordering = f.read_vector("c") f.skip(4) # Now we're at the tree itself # Now we iterate over each level and each CPU. position = f.tell() self._amr_header = hvals self._amr_offset = position # The maximum effective level is the deepest level # that has a non-zero number of octs nocts_to_this_level = hvals["numbl"].sum(axis=1).cumsum() self._max_level = ( np.argwhere(nocts_to_this_level == nocts_to_this_level[-1])[0][0] - self.ds.parameters["levelmin"] + 1 ) # update levelmax force_max_level, convention = self.ds._force_max_level if convention == "yt": force_max_level += self.ds.min_level + 1 self._amr_header["nlevelmax"] = min( force_max_level, self._amr_header["nlevelmax"] ) self._local_oct_count = hvals["numbl"][ self.ds.min_level :, self.domain_id - 1 ].sum() imin, imax = self.ds.min_level, self._amr_header["nlevelmax"] self._total_oct_count = hvals["numbl"][imin:imax, :].sum(axis=0) self._amr_header_initialized = True @property def ngridbound(self): self._read_amr_header() return self._ngridbound @property def amr_offset(self): self._read_amr_header() return self._amr_offset @property def max_level(self): self._read_amr_header() return self._max_level @max_level.setter def max_level(self, value): self._max_level = value @property def total_oct_count(self): self._read_amr_header() return self._total_oct_count @property def local_oct_count(self): self._read_amr_header() return self._local_oct_count @property def amr_header(self): self._read_amr_header() return self._amr_header @cached_property def oct_handler(self): """Open the oct file, read in octs level-by-level. For each oct, only the position, index, level and domain are needed - its position in the octree is found automatically. The most important is finding all the information to feed oct_handler.add """ self._read_amr_header() oct_handler = RAMSESOctreeContainer( self.ds.domain_dimensions / 2, self.ds.domain_left_edge, self.ds.domain_right_edge, ) root_nodes = self.amr_header["numbl"][self.ds.min_level, :].sum() oct_handler.allocate_domains(self.total_oct_count, root_nodes) mylog.debug( "Reading domain AMR % 4i (%0.3e, %0.3e)", self.domain_id, self.total_oct_count.sum(), self.ngridbound.sum(), ) with self.amr_file as f: f.seek(self.amr_offset) min_level = self.ds.min_level max_level = read_amr( f, self.amr_header, self.ngridbound, min_level, oct_handler ) oct_handler.finalize() new_max_level = max_level if new_max_level > self.max_level: raise RuntimeError( f"The maximum level detected in the AMR file ({new_max_level}) " f" does not match the expected number {self.max_level}." ) self.max_level = new_max_level self._oct_handler_initialized = True return oct_handler def included(self, selector): if getattr(selector, "domain_id", None) is not None: return selector.domain_id == self.domain_id domain_ids = self.oct_handler.domain_identify(selector) return self.domain_id in domain_ids class RAMSESDomainSubset(OctreeSubset): _domain_offset = 1 _block_order = "F" _base_domain = None def __init__( self, base_region, domain, ds, num_zones=2, num_ghost_zones=0, base_grid=None, ): super().__init__(base_region, domain, ds, num_zones, num_ghost_zones) self._base_grid = base_grid if num_ghost_zones > 0: if not all(ds.periodicity): mylog.warning( "Ghost zones will wrongly assume the domain to be periodic." ) # Create a base domain *with no self._base_domain.fwidth base_domain = RAMSESDomainSubset(ds.all_data(), domain, ds, num_zones) self._base_domain = base_domain elif num_ghost_zones < 0: raise RuntimeError( "Cannot initialize a domain subset with a negative number " f"of ghost zones, was called with {num_ghost_zones=}" ) @property def oct_handler(self): return self.domain.oct_handler def _fill_no_ghostzones(self, fd, fields, selector, file_handler): ndim = self.ds.dimensionality # Here we get a copy of the file, which we skip through and read the # bits we want. oct_handler = self.oct_handler all_fields = [f for ft, f in file_handler.field_list] fields = [f for ft, f in fields] data = {} cell_count = selector.count_oct_cells(self.oct_handler, self.domain_id) # Initializing data container for field in fields: data[field] = np.zeros(cell_count, "float64") # Do an early exit if the cell count is null if cell_count == 0: return data level_inds, cell_inds, file_inds = self.oct_handler.file_index_octs( selector, self.domain_id, cell_count ) cpu_list = [self.domain_id - 1] fill_hydro( fd, file_handler.offset, file_handler.level_count, cpu_list, level_inds, cell_inds, file_inds, ndim, all_fields, fields, data, oct_handler, ) return data def _fill_with_ghostzones( self, fd, fields, selector, file_handler, num_ghost_zones ): ndim = self.ds.dimensionality ncpu = self.ds.parameters["ncpu"] # Here we get a copy of the file, which we skip through and read the # bits we want. oct_handler = self.oct_handler all_fields = [f for ft, f in file_handler.field_list] fields = [f for ft, f in fields] tr = {} cell_count = ( selector.count_octs(self.oct_handler, self.domain_id) * self.nz**ndim ) # Initializing data container for field in fields: tr[field] = np.zeros(cell_count, "float64") # Do an early exit if the cell count is null if cell_count == 0: return tr gz_cache = getattr(self, "_ghost_zone_cache", None) if gz_cache: level_inds, cell_inds, file_inds, domain_inds = gz_cache else: gz_cache = ( level_inds, cell_inds, file_inds, domain_inds, ) = self.oct_handler.file_index_octs_with_ghost_zones( selector, self.domain_id, cell_count, self._num_ghost_zones ) self._ghost_zone_cache = gz_cache cpu_list = list(range(ncpu)) fill_hydro( fd, file_handler.offset, file_handler.level_count, cpu_list, level_inds, cell_inds, file_inds, ndim, all_fields, fields, tr, oct_handler, domain_inds=domain_inds, ) return tr @property def fwidth(self): fwidth = super().fwidth if self._num_ghost_zones > 0: fwidth = fwidth.reshape(-1, 8, 3) n_oct = fwidth.shape[0] # new_fwidth contains the fwidth of the oct+ghost zones # this is a constant array in each oct, so we simply copy # the oct value using numpy fancy-indexing new_fwidth = np.zeros((n_oct, self.nz**3, 3), dtype=fwidth.dtype) new_fwidth[:, :, :] = fwidth[:, 0:1, :] fwidth = new_fwidth.reshape(-1, 3) return fwidth @property def fcoords(self): num_ghost_zones = self._num_ghost_zones if num_ghost_zones == 0: return super().fcoords oh = self.oct_handler indices = oh.fill_index(self.selector).reshape(-1, 8) oct_inds, cell_inds = oh.fill_octcellindex_neighbours( self.selector, self._num_ghost_zones ) N_per_oct = self.nz**3 oct_inds = oct_inds.reshape(-1, N_per_oct) cell_inds = cell_inds.reshape(-1, N_per_oct) inds = indices[oct_inds, cell_inds] fcoords = self.ds.arr(oh.fcoords(self.selector)[inds].reshape(-1, 3), "unitary") return fcoords def fill(self, fd, fields, selector, file_handler): if self._num_ghost_zones == 0: return self._fill_no_ghostzones(fd, fields, selector, file_handler) else: return self._fill_with_ghostzones( fd, fields, selector, file_handler, self._num_ghost_zones ) def retrieve_ghost_zones(self, ngz, fields, smoothed=False): if smoothed: mylog.warning( "%s.retrieve_ghost_zones was called with the " "`smoothed` argument set to True. This is not supported, " "ignoring it.", self, ) smoothed = False _subset_with_gz = getattr(self, "_subset_with_gz", {}) try: new_subset = _subset_with_gz[ngz] mylog.debug( "Reusing previous subset with %s ghost zones for domain %s", ngz, self.domain_id, ) except KeyError: new_subset = RAMSESDomainSubset( self.base_region, self.domain, self.ds, num_ghost_zones=ngz, base_grid=self, ) _subset_with_gz[ngz] = new_subset # Cache the fields new_subset.get_data(fields) self._subset_with_gz = _subset_with_gz return new_subset class RAMSESIndex(OctreeIndex): def __init__(self, ds, dataset_type="ramses"): self.fluid_field_list = ds._fields_in_file self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self.float_type = np.float64 super().__init__(ds, dataset_type) def _initialize_oct_handler(self): if self.ds._bbox is not None: cpu_list = get_intersecting_cpus(self.dataset, self.dataset._bbox) else: cpu_list = range(self.dataset["ncpu"]) self.domains = [RAMSESDomainFile(self.dataset, i + 1) for i in cpu_list] @cached_property def max_level(self): force_max_level, convention = self.ds._force_max_level if convention == "yt": force_max_level += self.ds.min_level + 1 return min(force_max_level, max(dom.max_level for dom in self.domains)) @cached_property def num_grids(self): return sum( dom.local_oct_count for dom in self.domains # + dom.ngridbound.sum() ) def _detect_output_fields(self): dsl = set() # Get the detected particle fields for ph in self.domains[0].particle_handlers: dsl.update(set(ph.field_offsets.keys())) self.particle_field_list = list(dsl) # Get the detected fields dsl = set() for fh in self.domains[0].field_handlers: dsl.update(set(fh.field_list)) self.fluid_field_list = list(dsl) self.field_list = self.particle_field_list + self.fluid_field_list def _identify_base_chunk(self, dobj): use_fast_hilbert = ( hasattr(dobj, "get_bbox") and self.ds.parameters["ordering type"] == "hilbert" ) if getattr(dobj, "_chunk_info", None) is None: if use_fast_hilbert: idoms = { idom + 1 for idom in get_intersecting_cpus(self.ds, dobj, factor=3) } # If the oct handler has been initialized, use it domains = [] for dom in self.domains: # Hilbert indexing is conservative, so reject all those that # aren't in the bbox if dom.domain_id not in idoms: continue # If the domain has its oct handler, refine the selection if dom._oct_handler_initialized and not dom.included(dobj.selector): continue mylog.debug("Identified domain %s", dom.domain_id) domains.append(dom) if len(domains) >= 1: mylog.info( "Identified % 5d/% 5d intersecting domains (% 5d through hilbert key indexing)", len(domains), len(self.domains), len(idoms), ) else: domains = [dom for dom in self.domains if dom.included(dobj.selector)] if len(domains) >= 1: mylog.info("Identified %s intersecting domains", len(domains)) base_region = getattr(dobj, "base_region", dobj) subsets = [ RAMSESDomainSubset( base_region, domain, self.dataset, num_ghost_zones=dobj._num_ghost_zones, ) for domain in domains ] dobj._chunk_info = subsets dobj._current_chunk = list(self._chunk_all(dobj))[0] def _chunk_all(self, dobj): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) yield YTDataChunk(dobj, "all", oobjs, None) def _chunk_spatial(self, dobj, ngz, sort=None, preload_fields=None): sobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for og in sobjs: if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og yield YTDataChunk(dobj, "spatial", [g], None) def _chunk_io(self, dobj, cache=True, local_only=False): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for subset in oobjs: yield YTDataChunk(dobj, "io", [subset], None, cache=cache) def _initialize_level_stats(self): levels = sum(dom.level_count for dom in self.domains) desc = {"names": ["numcells", "level"], "formats": ["int64"] * 2} max_level = self.dataset.min_level + self.dataset.max_level + 2 self.level_stats = blankRecordArray(desc, max_level) self.level_stats["level"] = list(range(max_level)) self.level_stats["numcells"] = [0 for i in range(max_level)] for level in range(self.dataset.min_level + 1): self.level_stats[level + 1]["numcells"] = 2 ** ( level * self.dataset.dimensionality ) for level in range(levels.shape[1]): ncell = levels[:, level].sum() self.level_stats[level + self.dataset.min_level + 1]["numcells"] = ncell def _get_particle_type_counts(self): npart = 0 npart = {k: 0 for k in self.ds.particle_types if k != "all"} for dom in self.domains: for fh in dom.particle_handlers: count = fh.local_particle_count npart[fh.ptype] += count return npart def print_stats(self): """ Prints out (stdout) relevant information about the simulation This function prints information based on the fluid on the grids, and therefore does not work for DM only runs. """ if not self.fluid_field_list: print("This function is not implemented for DM only runs") return self._initialize_level_stats() header = "{:>3}\t{:>14}\t{:>14}".format("level", "# cells", "# cells^3") print(header) print(f"{len(header.expandtabs()) * '-'}") for level in range(self.dataset.min_level + self.dataset.max_level + 2): print( "% 3i\t% 14i\t% 14i" % ( level, self.level_stats["numcells"][level], np.ceil(self.level_stats["numcells"][level] ** (1.0 / 3)), ) ) print("-" * 46) print(" \t% 14i" % (self.level_stats["numcells"].sum())) print("\n") dx = self.get_smallest_dx() try: print(f"z = {self.dataset.current_redshift:0.8f}") except Exception: pass print( "t = {:0.8e} = {:0.8e} = {:0.8e}".format( self.ds.current_time.in_units("code_time"), self.ds.current_time.in_units("s"), self.ds.current_time.in_units("yr"), ) ) print("\nSmallest Cell:") for item in ("Mpc", "pc", "AU", "cm"): print(f"\tWidth: {dx.in_units(item):0.3e}") class RAMSESDataset(Dataset): _index_class = RAMSESIndex _field_info_class = RAMSESFieldInfo gamma = 1.4 # This will get replaced on hydro_fn open # RAMSES-specific parameters force_cosmological: bool | None _force_max_level: tuple[int, str] _bbox: list[list[float]] | None _self_shielding: bool | None = None def __init__( self, filename, dataset_type="ramses", fields=None, storage_filename=None, units_override=None, unit_system="cgs", extra_particle_fields=None, cosmological=None, bbox=None, max_level=None, max_level_convention=None, default_species_fields=None, self_shielding=None, use_conformal_time=None, ): # Here we want to initiate a traceback, if the reader is not built. if isinstance(fields, str): fields = field_aliases[fields] """ fields: An array of hydro variable fields in order of position in the hydro_XXXXX.outYYYYY file. If set to None, will try a default set of fields. extra_particle_fields: An array of extra particle variables in order of position in the particle_XXXXX.outYYYYY file. cosmological: If set to None, automatically detect cosmological simulation. If a boolean, force its value. self_shielding: If set to True, assume gas is self-shielded above 0.01 mp/cm^3. This affects the fields related to cooling and the mean molecular weight. """ self._fields_in_file = fields # By default, extra fields have not triggered a warning self._warned_extra_fields = defaultdict(lambda: False) self._extra_particle_fields = extra_particle_fields self.force_cosmological = cosmological self._bbox = bbox self._force_max_level = self._sanitize_max_level( max_level, max_level_convention ) file_handler = RAMSESFileSanitizer(filename) # ensure validation happens even if the class is instantiated # directly rather than from yt.load file_handler.validate() # Sanitize the filename info_fname = file_handler.info_fname if file_handler.group_name is not None: self.num_groups = len( [_ for _ in file_handler.root_folder.glob("group_?????") if _.is_dir()] ) else: self.num_groups = 0 self.root_folder = file_handler.root_folder Dataset.__init__( self, info_fname, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) # Add the particle types ptypes = [] for PH in get_particle_handlers(): if PH.any_exist(self): ptypes.append(PH.ptype) ptypes = tuple(ptypes) self.particle_types = self.particle_types_raw = ptypes # Add the fluid types for FH in get_field_handlers(): FH.purge_detected_fields(self) if FH.any_exist(self): self.fluid_types += (FH.ftype,) if use_conformal_time is not None: self.use_conformal_time = use_conformal_time elif self.cosmological_simulation: if "rt" in self.fluid_types: self.use_conformal_time = False else: self.use_conformal_time = True else: self.use_conformal_time = False self.storage_filename = storage_filename self.self_shielding = self_shielding @property def self_shielding(self) -> bool: if self._self_shielding is not None: return self._self_shielding # Read namelist.txt file (if any) has_namelist = self.read_namelist() if not has_namelist: self._self_shielding = False return self._self_shielding nml = self.parameters["namelist"] # "self_shielding" is stored in physics_params in older versions of the code physics_params = nml.get("physics_params", default={}) # and in "cooling_params" in more recent ones cooling_params = nml.get("cooling_params", default={}) self_shielding = physics_params.get("self_shielding", False) self_shielding |= cooling_params.get("self_shielding", False) self._self_shielding = self_shielding return self_shielding @self_shielding.setter def self_shielding(self, value): self._self_shielding = value @staticmethod def _sanitize_max_level(max_level, max_level_convention): # NOTE: the initialisation of the dataset class sets # self.min_level _and_ requires force_max_level # to be set, so we cannot convert from to yt/ramses # conventions if max_level is None and max_level_convention is None: return (2**999, "yt") # Check max_level is a valid, positive integer if not isinstance(max_level, (int, np.integer)): raise TypeError( f"Expected `max_level` to be a positive integer, got {max_level} " f"with type {type(max_level)} instead." ) if max_level < 0: raise ValueError( f"Expected `max_level` to be a positive integer, got {max_level} " "instead." ) # Check max_level_convention is set and acceptable if max_level_convention is None: raise ValueError( f"Received `max_level`={max_level}, but no `max_level_convention`. " "Valid conventions are 'yt' and 'ramses'." ) if max_level_convention not in ("ramses", "yt"): raise ValueError( f"Invalid convention {max_level_convention}. " "Valid choices are 'yt' and 'ramses'." ) return (max_level, max_level_convention) def create_field_info(self, *args, **kwa): """Extend create_field_info to add the particles types.""" super().create_field_info(*args, **kwa) # Register particle filters if ("io", "particle_family") in self.field_list: for fname, value in particle_families.items(): def loc(val): def closure(pfilter, data): filter = data[pfilter.filtered_type, "particle_family"] == val return filter return closure add_particle_filter( fname, loc(value), filtered_type="io", requires=["particle_family"] ) for k in particle_families.keys(): mylog.info("Adding particle_type: %s", k) self.add_particle_filter(f"{k}") def __str__(self): return self.basename.rsplit(".", 1)[0] def _set_code_unit_attributes(self): """ Generates the conversion to various physical _units based on the parameter file """ # loading the units from the info file boxlen = self.parameters["boxlen"] length_unit = self.parameters["unit_l"] density_unit = self.parameters["unit_d"] time_unit = self.parameters["unit_t"] # calculating derived units (except velocity and temperature, done below) mass_unit = density_unit * length_unit**3 magnetic_unit = np.sqrt(4 * np.pi * mass_unit / (time_unit**2 * length_unit)) pressure_unit = density_unit * (length_unit / time_unit) ** 2 setdefaultattr(self, "density_unit", self.quan(density_unit, "g/cm**3")) setdefaultattr(self, "magnetic_unit", self.quan(magnetic_unit, "gauss")) setdefaultattr(self, "pressure_unit", self.quan(pressure_unit, "dyne/cm**2")) setdefaultattr(self, "time_unit", self.quan(time_unit, "s")) setdefaultattr(self, "mass_unit", self.quan(mass_unit, "g")) setdefaultattr( self, "velocity_unit", self.quan(length_unit, "cm") / self.time_unit ) temperature_unit = self.velocity_unit**2 * mp / kb setdefaultattr(self, "temperature_unit", temperature_unit.in_units("K")) # Only the length unit get scales by a factor of boxlen setdefaultattr(self, "length_unit", self.quan(length_unit * boxlen, "cm")) def _parse_parameter_file(self): # hardcoded for now # These should be explicitly obtained from the file, but for now that # will wait until a reorganization of the source tree and better # generalization. self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "ramses" self.parameters["Time"] = 1.0 # default unit is 1... # We now execute the same logic Oliver's code does rheader = {} def read_rhs(f, cast): line = f.readline().strip() if line and "=" in line: key, val = (_.strip() for _ in line.split("=")) rheader[key] = cast(val) return key else: return None def cast_a_else_b(cast_a, cast_b): def caster(val): try: return cast_a(val) except ValueError: return cast_b(val) return caster with open(self.parameter_filename) as f: # Standard: first six are ncpu, ndim, levelmin, levelmax, ngridmax, nstep_coarse for _ in range(6): read_rhs(f, int) f.readline() # Standard: next 11 are boxlen, time, aexp, h0, omega_m, omega_l, omega_k, omega_b, unit_l, unit_d, unit_t for _ in range(11): key = read_rhs(f, float) # Read non standard extra fields until hitting the ordering type while key != "ordering type": key = read_rhs(f, cast_a_else_b(float, str)) # This next line deserves some comment. We specify a min_level that # corresponds to the minimum level in the RAMSES simulation. RAMSES is # one-indexed, but it also does refer to the *oct* dimensions -- so # this means that a levelmin of 1 would have *1* oct in it. So a # levelmin of 2 would have 8 octs at the root mesh level. self.min_level = rheader["levelmin"] - 1 # Now we read the hilbert indices self.hilbert_indices = {} if rheader["ordering type"] == "hilbert": f.readline() # header for _ in range(rheader["ncpu"]): dom, mi, ma = f.readline().split() self.hilbert_indices[int(dom)] = (float(mi), float(ma)) if rheader["ordering type"] != "hilbert" and self._bbox is not None: raise NotImplementedError( f"The ordering {rheader['ordering type']} " "is not compatible with the `bbox` argument." ) self.parameters.update(rheader) self.domain_left_edge = np.zeros(3, dtype="float64") self.domain_dimensions = np.ones(3, dtype="int32") * 2 ** (self.min_level + 1) self.domain_right_edge = np.ones(3, dtype="float64") # This is likely not true, but it's not clear # how to determine the boundary conditions self._periodicity = (True, True, True) if self.force_cosmological is not None: is_cosmological = self.force_cosmological else: # These conditions seem to always be true for non-cosmological datasets is_cosmological = not ( rheader["time"] >= 0 and rheader["H0"] == 1 and rheader["aexp"] == 1 ) if not is_cosmological: self.cosmological_simulation = False self.current_redshift = 0 self.hubble_constant = 0 self.omega_matter = 0 self.omega_lambda = 0 else: self.cosmological_simulation = True self.current_redshift = (1.0 / rheader["aexp"]) - 1.0 self.omega_lambda = rheader["omega_l"] self.omega_matter = rheader["omega_m"] self.hubble_constant = rheader["H0"] / 100.0 # This is H100 force_max_level, convention = self._force_max_level if convention == "yt": force_max_level += self.min_level + 1 self.max_level = min(force_max_level, rheader["levelmax"]) - self.min_level - 1 if not self.cosmological_simulation: self.current_time = self.parameters["time"] else: aexp_grid = np.geomspace(1e-3, 1, 2_000, endpoint=False) z_grid = 1 / aexp_grid - 1 self.tau_frw = tau_frw(self, z_grid) self.t_frw = t_frw(self, z_grid) self.current_time = t_frw(self, self.current_redshift).to("Gyr") if self.num_groups > 0: self.group_size = rheader["ncpu"] // self.num_groups self.read_namelist() def read_namelist(self) -> bool: """Read the namelist.txt file in the output folder, if present""" namelist_file = os.path.join(self.root_folder, "namelist.txt") if not os.path.exists(namelist_file): return False try: with open(namelist_file) as f: nml = f90nml.read(f) except ImportError as err: mylog.warning( "`namelist.txt` file found but missing package f90nml to read it:", exc_info=err, ) return False except (ValueError, StopIteration, AssertionError) as err: # Note: f90nml may raise a StopIteration, a ValueError or an AssertionError if # the namelist is not valid. mylog.warning( "Could not parse `namelist.txt` file as it was malformed:", exc_info=err, ) return False self.parameters["namelist"] = nml return True @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False return RAMSESFileSanitizer(filename).is_valid yt-project-yt-f043ac8/yt/frontends/ramses/definitions.py000066400000000000000000000043411510711153200234710ustar00rootroot00000000000000# These functions are RAMSES-specific import re from yt.funcs import mylog from yt.utilities.configure import YTConfig, configuration_callbacks def ramses_header(hvals): header = ( ("ncpu", 1, "i"), ("ndim", 1, "i"), ("nx", 3, "i"), ("nlevelmax", 1, "i"), ("ngridmax", 1, "i"), ("nboundary", 1, "i"), ("ngrid_current", 1, "i"), ("boxlen", 1, "d"), ("nout", 3, "i"), ) yield header # TODO: REMOVE noutput, iout, ifout = hvals["nout"] next_set = ( ("tout", noutput, "d"), ("aout", noutput, "d"), ("t", 1, "d"), ("dtold", hvals["nlevelmax"], "d"), ("dtnew", hvals["nlevelmax"], "d"), ("nstep", 2, "i"), ("stat", 3, "d"), ("cosm", 7, "d"), ("timing", 5, "d"), ("mass_sph", 1, "d", True), ) yield next_set field_aliases = { "standard_five": ("Density", "x-velocity", "y-velocity", "z-velocity", "Pressure"), "standard_six": ( "Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "Metallicity", ), } ## Regular expressions used to parse file descriptors VERSION_RE = re.compile(r"# version: *(\d+)") # This will match comma-separated strings, discarding whitespaces # on the left hand side VAR_DESC_RE = re.compile(r"\s*([^\s]+),\s*([^\s]+),\s*([^\s]+)") OUTPUT_DIR_EXP = r"output_(\d{5})" OUTPUT_DIR_RE = re.compile(OUTPUT_DIR_EXP) STANDARD_FILE_RE = re.compile(r"((amr|hydro|part|grav)_\d{5}\.out\d{5}|info_\d{5}.txt)") ## Configure family mapping particle_families = { "DM": 1, "star": 2, "cloud": 3, "dust": 4, "star_tracer": -2, "cloud_tracer": -3, "dust_tracer": -4, "gas_tracer": 0, } def _setup_ramses_particle_families(ytcfg: YTConfig) -> None: if not ytcfg.has_section("ramses-families"): return for key in particle_families.keys(): val = ytcfg.get("ramses-families", key, callback=None) if val is not None: mylog.info( "Changing family %s from %s to %s", key, particle_families[key], val ) particle_families[key] = val configuration_callbacks.append(_setup_ramses_particle_families) yt-project-yt-f043ac8/yt/frontends/ramses/field_handlers.py000066400000000000000000000510251510711153200241220ustar00rootroot00000000000000import abc import glob import os from functools import cached_property from yt.config import ytcfg from yt.funcs import mylog from yt.utilities.cython_fortran_utils import FortranFile from .io import _read_fluid_file_descriptor from .io_utils import read_offset FIELD_HANDLERS: set[type["FieldFileHandler"]] = set() def get_field_handlers(): return FIELD_HANDLERS def register_field_handler(ph): FIELD_HANDLERS.add(ph) DETECTED_FIELDS = {} # type: ignore class HandlerMixin: """This contains all the shared methods to handle RAMSES files. This is not supposed to be user-facing. """ def setup_handler(self, domain): """ Initialize an instance of the class. This automatically sets the full path to the file. This is not intended to be overridden in most cases. If you need more flexibility, rewrite this function to your need in the inherited class. """ self.ds = ds = domain.ds self.domain = domain self.domain_id = domain.domain_id basename = os.path.abspath(ds.root_folder) iout = int(os.path.basename(ds.parameter_filename).split(".")[0].split("_")[1]) if ds.num_groups > 0: igroup = ((domain.domain_id - 1) // ds.group_size) + 1 full_path = os.path.join( basename, f"group_{igroup:05d}", self.fname.format(iout=iout, icpu=domain.domain_id), ) else: full_path = os.path.join( basename, self.fname.format(iout=iout, icpu=domain.domain_id) ) if os.path.exists(full_path): self.fname = full_path else: raise FileNotFoundError( f"Could not find {self._file_type} file (type: {self.ftype}). " f"Tried {full_path}" ) if self.file_descriptor is not None: if ds.num_groups > 0: # The particle file descriptor is *only* in the first group self.file_descriptor = os.path.join( basename, "group_00001", self.file_descriptor ) else: self.file_descriptor = os.path.join(basename, self.file_descriptor) @property def exists(self): """ This function should return True if the *file* the instance exists. It is called for each file of the type found on the disk. By default, it just returns whether the file exists. Override it for more complex cases. """ return os.path.exists(self.fname) @property def has_descriptor(self): """ This function should return True if a *file descriptor* exists. By default, it just returns whether the file exists. Override it for more complex cases. """ return os.path.exists(self.file_descriptor) @classmethod def any_exist(cls, ds): """ This function should return True if the kind of particle represented by the class exists in the dataset. It takes as argument the class itself -not an instance- and a dataset. Arguments --------- ds : a Ramses Dataset Note ---- This function is usually called once at the initialization of the RAMSES Dataset structure to determine if the particle type (e.g. regular particles) exists. """ if ds.unique_identifier in cls._unique_registry: return cls._unique_registry[ds.unique_identifier] iout = int(os.path.basename(ds.parameter_filename).split(".")[0].split("_")[1]) fname = os.path.join( os.path.split(ds.parameter_filename)[0], cls.fname.format(iout=iout, icpu=1) ) exists = os.path.exists(fname) cls._unique_registry[ds.unique_identifier] = exists return exists class FieldFileHandler(abc.ABC, HandlerMixin): """ Abstract class to handle particles in RAMSES. Each instance represents a single file (one domain). To add support to a new particle file, inherit from this class and implement all functions containing a `NotImplementedError`. See `SinkParticleFileHandler` for an example implementation.""" _file_type = "field" # These properties are static properties ftype: str | None = None # The name to give to the field type fname: str | None = None # The name of the file(s) # The attributes of the header attrs: tuple[tuple[str, int, str], ...] | None = None known_fields = None # A list of tuple containing the field name and its type config_field: str | None = None # Name of the config section (if any) file_descriptor: str | None = None # The name of the file descriptor (if any) # These properties are computed dynamically field_offsets = None # Mapping from field to offset in file field_types = ( None # Mapping from field to the type of the data (float, integer, ...) ) def __init_subclass__(cls, *args, **kwargs): """ Registers subclasses at creation. """ super().__init_subclass__(*args, **kwargs) if cls.ftype is not None: register_field_handler(cls) cls._unique_registry = {} cls.parameters = {} cls.rt_parameters = {} cls._detected_field_list = {} return cls def __init__(self, domain): self.setup_handler(domain) @classmethod @abc.abstractmethod def detect_fields(cls, ds): """ Called once to setup the fields of this type It should set the following static variables: * parameters: dictionary Dictionary containing the variables. The keys should match those of `cls.attrs` * field_list: list of (ftype, fname) The list of the field present in the file """ pass @classmethod def get_detected_fields(cls, ds): """ Get the detected fields from the registry. """ if ds.unique_identifier in DETECTED_FIELDS: d = DETECTED_FIELDS[ds.unique_identifier] if cls.ftype in d: return d[cls.ftype] return None @classmethod def set_detected_fields(cls, ds, fields): """ Store the detected fields into the registry. """ if ds.unique_identifier not in DETECTED_FIELDS: DETECTED_FIELDS[ds.unique_identifier] = {} DETECTED_FIELDS[ds.unique_identifier].update({cls.ftype: fields}) @classmethod def purge_detected_fields(cls, ds): """ Purge the registry. This should be called on dataset creation to force the field detection to be called. """ if ds.unique_identifier in DETECTED_FIELDS: DETECTED_FIELDS.pop(ds.unique_identifier) @property def level_count(self): """ Return the number of cells per level. """ if getattr(self, "_level_count", None) is not None: return self._level_count self.offset return self._level_count @property def field_list(self): return self._detected_field_list[self.ds.unique_identifier] @cached_property def offset(self): """ Compute the offsets of the fields. By default, it skips the header (as defined by `cls.attrs`) and computes the offset at each level. It should be generic enough for most of the cases, but if the *structure* of your fluid file is non-canonical, change this. """ nvars = len(self._detected_field_list[self.ds.unique_identifier]) with FortranFile(self.fname) as fd: # Skip headers nskip = len(self.attrs) fd.skip(nskip) min_level = self.domain.ds.min_level # The file is as follows: # > headers # loop over levels # loop over cpu domains # > : current level # > : number of octs in level, domain # loop over variables (positions, velocities, density, ...) # loop over <2*2*2> cells in each oct # > with shape (nocts, ) # # So there are 8 * nvars records each with length (nocts, ) # at each (level, cpus) offset, level_count = read_offset( fd, min_level, self.domain.domain_id, self.parameters[self.ds.unique_identifier]["nvar"], self.domain.amr_header, Nskip=nvars * 8, ) self._level_count = level_count return offset @classmethod def load_fields_from_yt_config(cls) -> list[str]: if cls.config_field and ytcfg.has_section(cls.config_field): cfg = ytcfg.get(cls.config_field, "fields") fields = [_.strip() for _ in cfg if _.strip() != ""] return fields return [] class HydroFieldFileHandler(FieldFileHandler): ftype = "ramses" fname = "hydro_{iout:05d}.out{icpu:05d}" file_descriptor = "hydro_file_descriptor.txt" config_field = "ramses-hydro" attrs = ( ("ncpu", 1, "i"), ("nvar", 1, "i"), ("ndim", 1, "i"), ("nlevelmax", 1, "i"), ("nboundary", 1, "i"), ("gamma", 1, "d"), ) @classmethod def detect_fields(cls, ds): # Try to get the detected fields detected_fields = cls.get_detected_fields(ds) if detected_fields: return detected_fields num = os.path.basename(ds.parameter_filename).split(".")[0].split("_")[1] testdomain = 1 # Just pick the first domain file to read basepath = os.path.abspath(os.path.dirname(ds.parameter_filename)) basename = "%s/%%s_%s.out%05i" % (basepath, num, testdomain) fname = basename % "hydro" fname_desc = os.path.join(basepath, cls.file_descriptor) attrs = cls.attrs with FortranFile(fname) as fd: hvals = fd.read_attrs(attrs) cls.parameters[ds.unique_identifier] = hvals # Store some metadata ds.gamma = hvals["gamma"] nvar = hvals["nvar"] ok = False if ds._fields_in_file is not None: # Case 1: fields are provided by users on construction of dataset fields = list(ds._fields_in_file) ok = True else: # Case 2: fields are provided by users in the config fields = cls.load_fields_from_yt_config() ok = len(fields) > 0 if not ok and os.path.exists(fname_desc): # Case 3: there is a file descriptor # Or there is an hydro file descriptor mylog.debug("Reading hydro file descriptor.") # For now, we can only read double precision fields fields = [ e[0] for e in _read_fluid_file_descriptor(fname_desc, prefix="hydro") ] # We get no fields for old-style hydro file descriptor ok = len(fields) > 0 if not ok: # Case 4: attempt autodetection with usual fields foldername = os.path.abspath(os.path.dirname(ds.parameter_filename)) rt_flag = any(glob.glob(os.sep.join([foldername, "info_rt_*.txt"]))) if rt_flag: # rt run if nvar < 10: mylog.info("Detected RAMSES-RT file WITHOUT IR trapping.") fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "Metallicity", "HII", "HeII", "HeIII", ] else: mylog.info("Detected RAMSES-RT file WITH IR trapping.") fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pres_IR", "Pressure", "Metallicity", "HII", "HeII", "HeIII", ] else: if nvar < 5: mylog.debug( "nvar=%s is too small! YT doesn't currently " "support 1D/2D runs in RAMSES %s" ) raise ValueError # Basic hydro runs if nvar == 5: fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", ] if nvar > 5 and nvar < 11: fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "Metallicity", ] # MHD runs - NOTE: # THE MHD MODULE WILL SILENTLY ADD 3 TO THE NVAR IN THE MAKEFILE if nvar == 11: fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "B_x_left", "B_y_left", "B_z_left", "B_x_right", "B_y_right", "B_z_right", "Pressure", ] if nvar > 11: fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "B_x_left", "B_y_left", "B_z_left", "B_x_right", "B_y_right", "B_z_right", "Pressure", "Metallicity", ] mylog.debug( "No fields specified by user; automatically setting fields array to %s", fields, ) # Allow some wiggle room for users to add too many variables count_extra = 0 while len(fields) < nvar: fields.append(f"var_{len(fields)}") count_extra += 1 if count_extra > 0: mylog.debug("Detected %s extra fluid fields.", count_extra) cls._detected_field_list[ds.unique_identifier] = [ (cls.ftype, e) for e in fields ] cls.set_detected_fields(ds, fields) return fields class GravFieldFileHandler(FieldFileHandler): ftype = "gravity" fname = "grav_{iout:05d}.out{icpu:05d}" config_field = "ramses-grav" attrs = ( ("ncpu", 1, "i"), ("nvar", 1, "i"), ("nlevelmax", 1, "i"), ("nboundary", 1, "i"), ) @classmethod def detect_fields(cls, ds): # Try to get the detected fields detected_fields = cls.get_detected_fields(ds) if detected_fields: return detected_fields ndim = ds.dimensionality iout = int(str(ds).split("_")[1]) basedir = os.path.split(ds.parameter_filename)[0] fname = os.path.join(basedir, cls.fname.format(iout=iout, icpu=1)) with FortranFile(fname) as fd: cls.parameters[ds.unique_identifier] = fd.read_attrs(cls.attrs) nvar = cls.parameters[ds.unique_identifier]["nvar"] ndim = ds.dimensionality fields = cls.load_fields_from_yt_config() if not fields: if nvar == ndim + 1: fields = ["Potential"] + [f"{k}-acceleration" for k in "xyz"[:ndim]] else: fields = [f"{k}-acceleration" for k in "xyz"[:ndim]] ndetected = len(fields) if ndetected != nvar and not ds._warned_extra_fields["gravity"]: mylog.info("Detected %s extra gravity fields.", nvar - ndetected) ds._warned_extra_fields["gravity"] = True for i in range(nvar - ndetected): fields.append(f"var{i}") cls._detected_field_list[ds.unique_identifier] = [ (cls.ftype, e) for e in fields ] cls.set_detected_fields(ds, fields) return fields class RTFieldFileHandler(FieldFileHandler): ftype = "ramses-rt" fname = "rt_{iout:05d}.out{icpu:05d}" file_descriptor = "rt_file_descriptor.txt" config_field = "ramses-rt" attrs = ( ("ncpu", 1, "i"), ("nvar", 1, "i"), ("ndim", 1, "i"), ("nlevelmax", 1, "i"), ("nboundary", 1, "i"), ("gamma", 1, "d"), ) @classmethod def detect_fields(cls, ds): # Try to get the detected fields detected_fields = cls.get_detected_fields(ds) if detected_fields: return detected_fields fname = ds.parameter_filename.replace("info_", "info_rt_") rheader = {} def read_rhs(cast): line = f.readline() p, v = line.split("=") rheader[p.strip()] = cast(v) with open(fname) as f: # Read nRTvar, nions, ngroups, iions for _ in range(4): read_rhs(int) # Try to read rtprecision. # Either it is present or the line is simply blank, so # we try to parse the line as an int, and if it fails, # we simply ignore it. try: read_rhs(int) f.readline() except ValueError: pass # Read X and Y fractions for _ in range(2): read_rhs(float) f.readline() # Reat unit_np, unit_pfd for _ in range(2): read_rhs(float) # Read rt_c_frac # Note: when using variable speed of light, this line will contain multiple # values corresponding the the velocity at each level read_rhs(lambda line: [float(e) for e in line.split()]) f.readline() # Read n star, t2star, g_star for _ in range(3): read_rhs(float) # Touchy part, we have to read the photon group properties mylog.debug("Not reading photon group properties") cls.rt_parameters[ds.unique_identifier] = rheader ngroups = rheader["nGroups"] iout = int(str(ds).split("_")[1]) basedir = os.path.split(ds.parameter_filename)[0] fname = os.path.join(basedir, cls.fname.format(iout=iout, icpu=1)) fname_desc = os.path.join(basedir, cls.file_descriptor) with FortranFile(fname) as fd: cls.parameters[ds.unique_identifier] = fd.read_attrs(cls.attrs) ok = False if ds._fields_in_file is not None: # Case 1: fields are provided by users on construction of dataset fields = list(ds._fields_in_file) ok = True else: # Case 2: fields are provided by users in the config fields = cls.load_fields_from_yt_config() ok = len(fields) > 0 if not ok and os.path.exists(fname_desc): # Case 3: there is a file descriptor # Or there is an hydro file descriptor mylog.debug("Reading rt file descriptor.") # For now, we can only read double precision fields fields = [ e[0] for e in _read_fluid_file_descriptor(fname_desc, prefix="rt") ] ok = len(fields) > 0 if not ok: fields = [] tmp = [ "Photon_density_%s", "Photon_flux_x_%s", "Photon_flux_y_%s", "Photon_flux_z_%s", ] for ng in range(ngroups): fields.extend([t % (ng + 1) for t in tmp]) cls._detected_field_list[ds.unique_identifier] = [ (cls.ftype, e) for e in fields ] cls.set_detected_fields(ds, fields) return fields @classmethod def get_rt_parameters(cls, ds): if cls.rt_parameters[ds.unique_identifier]: return cls.rt_parameters[ds.unique_identifier] # Call detect fields to get the rt_parameters cls.detect_fields(ds) return cls.rt_parameters[ds.unique_identifier] yt-project-yt-f043ac8/yt/frontends/ramses/fields.py000066400000000000000000000520501510711153200224240ustar00rootroot00000000000000import os import warnings from functools import partial import numpy as np from yt import units from yt._typing import KnownFieldsT from yt.fields.field_detector import FieldDetector from yt.fields.field_info_container import FieldInfoContainer from yt.frontends.ramses.io import convert_ramses_conformal_time_to_physical_time from yt.utilities.cython_fortran_utils import FortranFile from yt.utilities.lib.cosmology_time import t_frw from yt.utilities.linear_interpolators import BilinearFieldInterpolator from yt.utilities.logger import ytLogger as mylog from yt.utilities.physical_constants import ( boltzmann_constant_cgs, mass_hydrogen_cgs, mh, mp, ) from .field_handlers import RTFieldFileHandler b_units = "code_magnetic" ra_units = "code_length / code_time**2" rho_units = "code_density" vel_units = "code_velocity" pressure_units = "code_pressure" ener_units = "code_mass * code_velocity**2" specific_ener_units = "code_velocity**2" ang_mom_units = "code_mass * code_velocity * code_length" cooling_function_units = " erg * cm**3 /s" cooling_function_prime_units = " erg * cm**3 /s/K" flux_unit = "1 / code_length**2 / code_time" number_density_unit = "1 / code_length**3" known_species_masses = { sp: mh * v for sp, v in [ ("HI", 1.0), ("HII", 1.0), ("Electron", 1.0), ("HeI", 4.0), ("HeII", 4.0), ("HeIII", 4.0), ("H2I", 2.0), ("H2II", 2.0), ("HM", 1.0), ("DI", 2.0), ("DII", 2.0), ("HDI", 3.0), ] } known_species_names = { "HI": "H_p0", "HII": "H_p1", "Electron": "El", "HeI": "He_p0", "HeII": "He_p1", "HeIII": "He_p2", "H2I": "H2_p0", "H2II": "H2_p1", "HM": "H_m1", "DI": "D_p0", "DII": "D_p1", "HDI": "HD_p0", } _cool_axes = ("lognH", "logT") # , "logTeq") _cool_arrs = ( ("cooling_primordial", cooling_function_units), ("heating_primordial", cooling_function_units), ("cooling_compton", cooling_function_units), ("heating_compton", cooling_function_units), ("cooling_metal", cooling_function_units), ("cooling_primordial_prime", cooling_function_prime_units), ("heating_primordial_prime", cooling_function_prime_units), ("cooling_compton_prime", cooling_function_prime_units), ("heating_compton_prime", cooling_function_prime_units), ("cooling_metal_prime", cooling_function_prime_units), ("mu", None), ("abundances", None), ) _cool_species = ( "Electron_number_density", "HI_number_density", "HII_number_density", "HeI_number_density", "HeII_number_density", "HeIII_number_density", ) _X = 0.76 # H fraction, hardcoded _Y = 0.24 # He fraction, hardcoded class RAMSESFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("Density", (rho_units, ["density"], None)), ("x-velocity", (vel_units, ["velocity_x"], None)), ("y-velocity", (vel_units, ["velocity_y"], None)), ("z-velocity", (vel_units, ["velocity_z"], None)), ("Pres_IR", (pressure_units, ["pres_IR", "pressure_IR"], None)), ("Pressure", (pressure_units, ["pressure"], None)), ("Metallicity", ("", ["metallicity"], None)), ("HII", ("", ["H_p1_fraction"], None)), ("HeII", ("", ["He_p1_fraction"], None)), ("HeIII", ("", ["He_p2_fraction"], None)), ("x-acceleration", (ra_units, ["acceleration_x"], None)), ("y-acceleration", (ra_units, ["acceleration_y"], None)), ("z-acceleration", (ra_units, ["acceleration_z"], None)), ("Potential", (specific_ener_units, ["potential"], None)), ("B_x_left", (b_units, ["magnetic_field_x_left"], None)), ("B_x_right", (b_units, ["magnetic_field_x_right"], None)), ("B_y_left", (b_units, ["magnetic_field_y_left"], None)), ("B_y_right", (b_units, ["magnetic_field_y_right"], None)), ("B_z_left", (b_units, ["magnetic_field_z_left"], None)), ("B_z_right", (b_units, ["magnetic_field_z_right"], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_velocity_x", (vel_units, [], None)), ("particle_velocity_y", (vel_units, [], None)), ("particle_velocity_z", (vel_units, [], None)), ("particle_mass", ("code_mass", [], None)), ("particle_identity", ("", ["particle_index"], None)), ("particle_refinement_level", ("", [], None)), ("particle_birth_time", ("code_time", ["age"], None)), ("conformal_birth_time", ("", [], None)), ("particle_metallicity", ("", [], None)), ("particle_family", ("", [], None)), ("particle_tag", ("", [], None)), # sink field parameters ("particle_mass", ("code_mass", [], None)), ("particle_angular_momentum_x", (ang_mom_units, [], None)), ("particle_angular_momentum_y", (ang_mom_units, [], None)), ("particle_angular_momentum_z", (ang_mom_units, [], None)), ("particle_formation_time", ("code_time", [], None)), ("particle_accretion_rate", ("code_mass/code_time", [], None)), ("particle_delta_mass", ("code_mass", [], None)), ("particle_rho_gas", (rho_units, [], None)), ("particle_cs**2", (vel_units, [], None)), ("particle_etherm", (ener_units, [], None)), ("particle_velocity_x_gas", (vel_units, [], None)), ("particle_velocity_y_gas", (vel_units, [], None)), ("particle_velocity_z_gas", (vel_units, [], None)), ("particle_mass_bh", ("code_mass", [], None)), ("particle_level", ("", [], None)), ("particle_radius_star", ("code_length", [], None)), ) known_sink_fields: KnownFieldsT = ( ("particle_position_x", ("code_length", [], None)), ("particle_position_y", ("code_length", [], None)), ("particle_position_z", ("code_length", [], None)), ("particle_velocity_x", (vel_units, [], None)), ("particle_velocity_y", (vel_units, [], None)), ("particle_velocity_z", (vel_units, [], None)), ("particle_mass", ("code_mass", [], None)), ("particle_identifier", ("", ["particle_index"], None)), ("particle_birth_time", ("code_time", ["age"], None)), ("BH_real_accretion", ("code_mass/code_time", [], None)), ("BH_bondi_accretion", ("code_mass/code_time", [], None)), ("BH_eddington_accretion", ("code_mass/code_time", [], None)), ("BH_esave", (ener_units, [], None)), ("gas_spin_x", (ang_mom_units, [], None)), ("gas_spin_y", (ang_mom_units, [], None)), ("gas_spin_z", (ang_mom_units, [], None)), ("BH_spin_x", ("", [], None)), ("BH_spin_y", ("", [], None)), ("BH_spin_z", ("", [], None)), ("BH_spin", (ang_mom_units, [], None)), ("BH_efficiency", ("", [], None)), ) def setup_particle_fields(self, ptype): super().setup_particle_fields(ptype) def star_age_from_conformal_cosmo(field, data): conformal_age = data[ptype, "conformal_birth_time"] birth_time = convert_ramses_conformal_time_to_physical_time( data.ds, conformal_age ) return data.ds.current_time - birth_time def star_age_from_physical_cosmo(field, data): H0 = float( data.ds.quan(data.ds.hubble_constant * 100, "km/s/Mpc").to("1/Gyr") ) times = data[ptype, "conformal_birth_time"].value time_tot = float(t_frw(data.ds, 0) * H0) birth_time = (time_tot + times) / H0 t_out = float(data.ds.current_time.to("Gyr")) return data.apply_units(t_out - birth_time, "Gyr") def star_age(field, data): formation_time = data[ptype, "particle_birth_time"] return data.ds.current_time - formation_time if self.ds.cosmological_simulation and self.ds.use_conformal_time: fun = star_age_from_conformal_cosmo elif self.ds.cosmological_simulation: fun = star_age_from_physical_cosmo else: fun = star_age self.add_field( (ptype, "star_age"), sampling_type="particle", function=fun, units=self.ds.unit_system["time"], ) def setup_fluid_fields(self): def _temperature_over_mu(field, data): rv = data["gas", "pressure"] / data["gas", "density"] rv *= mass_hydrogen_cgs / boltzmann_constant_cgs return rv self.add_field( ("gas", "temperature_over_mu"), sampling_type="cell", function=_temperature_over_mu, units=self.ds.unit_system["temperature"], ) found_cooling_fields = self.create_cooling_fields() if found_cooling_fields: def _temperature(field, data): return data["gas", "temperature_over_mu"] * data["gas", "mu"] else: def _temperature(field, data): if not isinstance(data, FieldDetector): warnings.warn( "Trying to calculate temperature but the cooling tables " "couldn't be found or read. yt will return T/µ instead of " "T — this is equivalent to assuming µ=1.0. To suppress this, " "derive the temperature from temperature_over_mu with " "some values for mu.", category=RuntimeWarning, stacklevel=1, ) return data["gas", "temperature_over_mu"] self.add_field( ("gas", "temperature"), sampling_type="cell", function=_temperature, units=self.ds.unit_system["temperature"], ) self.species_names = [ known_species_names[fn] for ft, fn in self.field_list if fn in known_species_names ] # See if we need to load the rt fields rt_flag = RTFieldFileHandler.any_exist(self.ds) if rt_flag: # rt run self.create_rt_fields() # Load magnetic fields if ("gas", "magnetic_field_x_left") in self: self.create_magnetic_fields() # Potential field if ("gravity", "Potential") in self: self.create_gravity_fields() def create_gravity_fields(self): def potential_energy(field, data): return data["gas", "potential"] * data["gas", "cell_mass"] self.add_field( ("gas", "potential_energy"), sampling_type="cell", function=potential_energy, units=self.ds.unit_system["energy"], ) def create_magnetic_fields(self): # Calculate cell-centred magnetic fields from face-centred def mag_field(ax): def _mag_field(field, data): return ( data["gas", f"magnetic_field_{ax}_left"] + data["gas", f"magnetic_field_{ax}_right"] ) / 2 return _mag_field for ax in self.ds.coordinates.axis_order: self.add_field( ("gas", f"magnetic_field_{ax}"), sampling_type="cell", function=mag_field(ax), units=self.ds.unit_system["magnetic_field_cgs"], ) def _divB(field, data): """Calculate magnetic field divergence""" out = np.zeros_like(data["gas", "magnetic_field_x_right"]) for ax in data.ds.coordinates.axis_order: out += ( data["gas", f"magnetic_field_{ax}_right"] - data["gas", f"magnetic_field_{ax}_left"] ) return out / data["gas", "dx"] self.add_field( ("gas", "magnetic_field_divergence"), sampling_type="cell", function=_divB, units=self.ds.unit_system["magnetic_field_cgs"] / self.ds.unit_system["length"], ) def create_rt_fields(self): self.ds.fluid_types += ("rt",) p = RTFieldFileHandler.get_rt_parameters(self.ds).copy() p.update(self.ds.parameters) ngroups = p["nGroups"] # Make sure rt_c_frac is at least as long as the number of levels in # the simulation. Pad with either 1 (default) when using level-dependent # reduced speed of light, otherwise pad with a constant value if len(p["rt_c_frac"]) == 1: pad_value = p["rt_c_frac"][0] else: pad_value = 1 rt_c_frac = np.pad( p["rt_c_frac"], (0, max(0, self.ds.max_level - len(["rt_c_frac"]) + 1)), constant_values=pad_value, ) rt_c = rt_c_frac * units.c / (p["unit_l"] / p["unit_t"]) dens_conv = (p["unit_np"] / rt_c).value / units.cm**3 ######################################## # Adding the fields in the hydro_* files def _temp_IR(field, data): rv = data["gas", "pres_IR"] / data["gas", "density"] rv *= mass_hydrogen_cgs / boltzmann_constant_cgs return rv self.add_field( ("gas", "temp_IR"), sampling_type="cell", function=_temp_IR, units=self.ds.unit_system["temperature"], ) def _species_density(field, data, species: str): return data["gas", f"{species}_fraction"] * data["gas", "density"] def _species_mass(field, data, species: str): return data["gas", f"{species}_density"] * data["index", "cell_volume"] for species in ["H_p1", "He_p1", "He_p2"]: self.add_field( ("gas", species + "_density"), sampling_type="cell", function=partial(_species_density, species=species), units=self.ds.unit_system["density"], ) self.add_field( ("gas", species + "_mass"), sampling_type="cell", function=partial(_species_mass, species=species), units=self.ds.unit_system["mass"], ) ######################################## # Adding the fields in the rt_ files def gen_pdens(igroup): def _photon_density(field, data): # The photon density depends on the possibly level-dependent conversion factor. ilvl = data["index", "grid_level"].astype("int64") dc = dens_conv[ilvl] rv = data["ramses-rt", f"Photon_density_{igroup + 1}"] * dc return rv return _photon_density for igroup in range(ngroups): self.add_field( ("rt", f"photon_density_{igroup + 1}"), sampling_type="cell", function=gen_pdens(igroup), units=self.ds.unit_system["number_density"], ) flux_conv = p["unit_pf"] / units.cm**2 / units.s flux_unit = ( 1 / self.ds.unit_system["time"] / self.ds.unit_system["length"] ** 2 ).units def gen_flux(key, igroup): def _photon_flux(field, data): rv = data["ramses-rt", f"Photon_flux_{key}_{igroup + 1}"] * flux_conv return rv return _photon_flux for key in "xyz": for igroup in range(ngroups): self.add_field( ("rt", f"photon_flux_{key}_{igroup + 1}"), sampling_type="cell", function=gen_flux(key, igroup), units=flux_unit, ) def create_cooling_fields(self) -> bool: "Create cooling fields from the cooling files. Return True if successful." num = os.path.basename(self.ds.parameter_filename).split(".")[0].split("_")[1] filename = "%s/cooling_%05i.out" % ( os.path.dirname(self.ds.parameter_filename), int(num), ) if not os.path.exists(filename): mylog.warning("This output has no cooling fields") return False # Function to create the cooling fields def _create_field(name, interp_object, unit): def _func(field, data): shape = data["gas", "temperature_over_mu"].shape # Ramses assumes a fraction X of Hydrogen within the non-metal gas. # It has to be corrected by metallicity. Z = data["gas", "metallicity"] nH = ((1 - _Y) * (1 - Z) * data["gas", "density"] / mh).to("cm**-3") if data.ds.self_shielding: boost = np.maximum(np.exp(-nH / 0.01), 1e-20) else: boost = 1 d = { "lognH": np.log10(nH / boost).ravel(), "logT": np.log10(data["gas", "temperature_over_mu"]).ravel(), } rv = interp_object(d).reshape(shape) if name[-1] != "mu": rv = 10 ** interp_object(d).reshape(shape) cool = data.ds.arr(rv, unit) if "metal" in name[-1].split("_"): cool = ( cool * data["gas", "metallicity"] / 0.02 ) # Ramses uses Zsolar=0.02 elif "compton" in name[-1].split("_"): cool = data.ds.arr(rv, unit + "/cm**3") cool = ( cool / data["gas", "number_density"] ) # Compton cooling/heating is written to file in erg/s return cool self.add_field(name=name, sampling_type="cell", function=_func, units=unit) # Load cooling files avals = {} tvals = {} with FortranFile(filename) as fd: n1, n2 = fd.read_vector("i") for ax in _cool_axes: avals[ax] = fd.read_vector("d") for i, (tname, unit) in enumerate(_cool_arrs): var = fd.read_vector("d") if var.size == n1 and i == 0: # If this case occurs, the cooling files were produced pre-2010 in # a format that is no longer supported mylog.warning( "This cooling file format is no longer supported. " "Cooling field loading skipped." ) return False if var.size == n1 * n2: tvals[tname] = { "data": var.reshape((n1, n2), order="F"), "unit": unit, } else: var = var.reshape((n1, n2, var.size // (n1 * n2)), order="F") for i in range(var.shape[-1]): tvals[_cool_species[i]] = { "data": var[:, :, i], "unit": "1/cm**3", } # Add the mu field first, as it is needed for the number density interp = BilinearFieldInterpolator( tvals["mu"]["data"], (avals["lognH"], avals["logT"]), ["lognH", "logT"], truncate=True, ) _create_field(("gas", "mu"), interp, "dimensionless") # Add the number density field, based on mu def _number_density(field, data): return data["gas", "density"] / mp / data["gas", "mu"] self.add_field( name=("gas", "number_density"), sampling_type="cell", function=_number_density, units=number_density_unit, ) # Add the cooling and heating fields, which need the number density field for key in tvals: if key != "mu": interp = BilinearFieldInterpolator( tvals[key]["data"], (avals["lognH"], avals["logT"]), ["lognH", "logT"], truncate=True, ) _create_field(("gas", key), interp, tvals[key]["unit"]) # Add total cooling and heating fields def _all_cool(field, data): return ( data["gas", "cooling_primordial"] + data["gas", "cooling_metal"] + data["gas", "cooling_compton"] ) def _all_heat(field, data): return data["gas", "heating_primordial"] + data["gas", "heating_compton"] self.add_field( name=("gas", "cooling_total"), sampling_type="cell", function=_all_cool, units=cooling_function_units, ) self.add_field( name=("gas", "heating_total"), sampling_type="cell", function=_all_heat, units=cooling_function_units, ) # Add net cooling fields def _net_cool(field, data): return data["gas", "cooling_total"] - data["gas", "heating_total"] self.add_field( name=("gas", "cooling_net"), sampling_type="cell", function=_net_cool, units=cooling_function_units, ) return True yt-project-yt-f043ac8/yt/frontends/ramses/hilbert.py000066400000000000000000000140041510711153200226040ustar00rootroot00000000000000from typing import Any, Optional import numpy as np from yt.data_objects.selection_objects.region import YTRegion from yt.geometry.selection_routines import ( bbox_intersects, fully_contains, ) from yt.utilities.lib.geometry_utils import get_hilbert_indices # State diagram to compute the hilbert curve _STATE_DIAGRAM = np.array( [ [ [1, 2, 0, 6, 11, 4, 5, 6, 10, 4, 7, 10], [0, 0, 0, 2, 4, 6, 4, 6, 2, 2, 4, 6], ], [ [2, 6, 9, 0, 11, 4, 7, 1, 3, 4, 2, 3], [1, 7, 3, 3, 3, 5, 7, 7, 5, 1, 5, 1], ], [ [3, 0, 10, 6, 0, 8, 5, 6, 1, 8, 11, 2], [3, 1, 7, 1, 5, 1, 3, 5, 3, 5, 7, 7], ], [ [2, 7, 9, 11, 7, 8, 3, 10, 1, 8, 2, 6], [2, 6, 4, 0, 2, 2, 0, 4, 4, 6, 6, 0], ], [ [4, 8, 1, 9, 5, 0, 1, 9, 10, 2, 7, 10], [7, 3, 1, 5, 7, 7, 5, 1, 1, 3, 3, 5], ], [ [5, 8, 1, 0, 9, 6, 1, 4, 3, 7, 5, 3], [6, 4, 2, 4, 0, 4, 6, 0, 6, 0, 2, 2], ], [ [3, 0, 11, 9, 0, 10, 11, 9, 5, 2, 8, 4], [4, 2, 6, 6, 6, 0, 2, 2, 0, 4, 0, 4], ], [ [5, 7, 11, 8, 7, 6, 11, 10, 9, 3, 5, 4], [5, 5, 5, 7, 1, 3, 1, 3, 7, 7, 1, 3], ], ] ) def hilbert3d( ijk: "np.ndarray[Any, np.dtype[np.int64]]", bit_length: int ) -> "np.ndarray[Any, np.dtype[np.float64]]": """Compute the order using Hilbert indexing. Arguments --------- ijk : (N, ndim) integer array The positions bit_length : integer The bit_length for the indexing. """ ijk = np.atleast_2d(ijk) # A note here: there is a freedom in the way hilbert indices are # being computed (should it be xyz or yzx or zxy etc.) # and the yt convention is not the same as the RAMSES one. return get_hilbert_indices(bit_length, ijk[:, [1, 2, 0]].astype(np.int64)) def get_intersecting_cpus( ds, region: YTRegion, LE: Optional["np.ndarray[Any, np.dtype[np.float64]]"] = None, dx: float = 1.0, dx_cond: float | None = None, factor: float = 4.0, bound_keys: Optional["np.ndarray[Any, np.dtype[np.float64]]"] = None, ) -> set[int]: """ Find the subset of CPUs that intersect the bbox in a recursive fashion. """ if LE is None: LE = np.array([0, 0, 0], dtype="d") if dx_cond is None: bbox = region.get_bbox() dx_cond = float((bbox[1] - bbox[0]).min().to("code_length")) if bound_keys is None: ncpu = ds.parameters["ncpu"] bound_keys = np.empty(ncpu + 1, dtype="float64") bound_keys[:ncpu] = [ds.hilbert_indices[icpu + 1][0] for icpu in range(ncpu)] bound_keys[ncpu] = ds.hilbert_indices[ncpu][1] # If the current dx is smaller than the smallest size of the bbox if dx < dx_cond / factor: # Finish recursion return get_cpu_list_cuboid(ds, np.asarray([LE, LE + dx]), bound_keys) # If the current cell is fully within the selected region, stop recursion if fully_contains(region.selector, LE, dx): return get_cpu_list_cuboid(ds, np.asarray([LE, LE + dx]), bound_keys) dx /= 2 ret = set() # Compute intersection of the eight subcubes with the bbox and recurse. for i in range(2): for j in range(2): for k in range(2): LE_new = LE + np.array([i, j, k], dtype="d") * dx if bbox_intersects(region.selector, LE_new, dx): ret.update( get_intersecting_cpus( ds, region, LE_new, dx, dx_cond, factor, bound_keys ) ) return ret def get_cpu_list_cuboid( ds, X: "np.ndarray[Any, np.dtype[np.float64]]", bound_keys: "np.ndarray[Any, np.dtype[np.float64]]", ) -> set[int]: """ Return the list of the CPU intersecting with the cuboid containing the positions. Note that it will be 0-indexed. Parameters ---------- ds : Dataset The dataset containing the information X : (N, ndim) float array An array containing positions. They should be between 0 and 1. """ X = np.atleast_2d(X) if X.shape[1] != 3: raise NotImplementedError("This function is only implemented in 3D.") levelmax = ds.parameters["levelmax"] ndim = ds.parameters["ndim"] xmin, ymin, zmin = X.min(axis=0) xmax, ymax, zmax = X.max(axis=0) dmax = max(xmax - xmin, ymax - ymin, zmax - zmin) ilevel = int(np.ceil(-np.log2(dmax))) lmin = ilevel bit_length = lmin - 1 maxdom = 2**bit_length imin, imax, jmin, jmax, kmin, kmax = 0, 0, 0, 0, 0, 0 if bit_length > 0: imin = int(xmin * maxdom) imax = imin + 1 jmin = int(ymin * maxdom) jmax = jmin + 1 kmin = int(zmin * maxdom) kmax = kmin + 1 dkey = (2 ** (levelmax + 1) / maxdom) ** ndim ndom = 1 if bit_length > 0: ndom = 8 ijkdom = idom, jdom, kdom = np.empty((3, 8), dtype=np.int64) idom[0], idom[1] = imin, imax idom[2], idom[3] = imin, imax idom[4], idom[5] = imin, imax idom[6], idom[7] = imin, imax jdom[0], jdom[1] = jmin, jmin jdom[2], jdom[3] = jmax, jmax jdom[4], jdom[5] = jmin, jmin jdom[6], jdom[7] = jmax, jmax kdom[0], kdom[1] = kmin, kmin kdom[2], kdom[3] = kmin, kmin kdom[4], kdom[5] = kmax, kmax kdom[6], kdom[7] = kmax, kmax bounding_min, bounding_max = np.zeros(ndom), np.zeros(ndom) if bit_length > 0: order_min = hilbert3d(ijkdom.T, bit_length) for i in range(ndom): if bit_length > 0: omin = order_min[i] else: omin = 0 bounding_min[i] = omin * dkey bounding_max[i] = (omin + 1) * dkey cpu_min = np.searchsorted(bound_keys, bounding_min, side="right") - 1 cpu_max = np.searchsorted(bound_keys, bounding_max, side="right") cpu_read: set[int] = set() for i in range(ndom): cpu_read.update(range(cpu_min[i], cpu_max[i])) return cpu_read yt-project-yt-f043ac8/yt/frontends/ramses/io.py000066400000000000000000000372611510711153200215740ustar00rootroot00000000000000from collections import defaultdict from functools import lru_cache from typing import TYPE_CHECKING, Union import numpy as np from unyt import unyt_array from yt._maintenance.deprecation import issue_deprecation_warning from yt.frontends.ramses.definitions import VAR_DESC_RE, VERSION_RE from yt.utilities.cython_fortran_utils import FortranFile from yt.utilities.exceptions import ( YTFieldTypeNotFound, YTFileNotParseable, YTParticleOutputFormatNotImplemented, ) from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog if TYPE_CHECKING: import os def convert_ramses_ages(ds, conformal_ages): issue_deprecation_warning( msg=( "The `convert_ramses_ages' function is deprecated. It should be replaced " "by the `convert_ramses_conformal_time_to_physical_age' function." ), stacklevel=3, since="4.0.3", ) return convert_ramses_conformal_time_to_physical_time(ds, conformal_ages) def convert_ramses_conformal_time_to_physical_time( ds, conformal_time: np.ndarray ) -> unyt_array: """ Convert conformal times (as defined in RAMSES) to physical times. Arguments --------- ds : RAMSESDataset The RAMSES dataset to use for the conversion conformal_time : np.ndarray The conformal time as read from disk Returns ------- physical_age : np.ndarray The physical age in code units """ h0 = ds.hubble_constant tau_bins = ds.tau_frw * h0 t_bins = ds.t_frw min_time = 0 max_time = ds.current_time.to(t_bins.units) return ds.arr( np.clip( np.interp( conformal_time, tau_bins, t_bins.value, right=max_time, left=min_time, ), min_time, max_time.value, ), t_bins.units, ) def _ramses_particle_binary_file_handler(particle_handler, subset, fields, count): """General file handler for binary file, called by _read_particle_subset Parameters ---------- particle : ``ParticleFileHandler`` the particle class we want to read subset: ``RAMSESDomainSubset`` A RAMSES domain subset object fields: list of tuple The fields to read count: integer The number of elements to count """ tr = {} ds = subset.domain.ds foffsets = particle_handler.field_offsets fname = particle_handler.fname data_types = particle_handler.field_types with FortranFile(fname) as fd: # We do *all* conversion into boxlen here. # This means that no other conversions need to be applied to convert # positions into the same domain as the octs themselves. for field in sorted(fields, key=lambda a: foffsets[a]): if count == 0: tr[field] = np.empty(0, dtype=data_types[field]) continue # Sentinel value: -1 means we don't have this field if foffsets[field] == -1: tr[field] = np.empty(count, dtype=data_types[field]) else: fd.seek(foffsets[field]) dt = data_types[field] tr[field] = fd.read_vector(dt) if field[1].startswith("particle_position"): np.divide(tr[field], ds["boxlen"], tr[field]) # Hand over to field handler for special cases, like particle_birth_times particle_handler.handle_field(field, tr) return tr def _ramses_particle_csv_file_handler(particle_handler, subset, fields, count): """General file handler for csv file, called by _read_particle_subset Parameters ---------- particle: ``ParticleFileHandler`` the particle class we want to read subset: ``RAMSESDomainSubset`` A RAMSES domain subset object fields: list of tuple The fields to read count: integer The number of elements to count """ from yt.utilities.on_demand_imports import _pandas as pd tr = {} ds = subset.domain.ds foffsets = particle_handler.field_offsets fname = particle_handler.fname list_field_ind = [ (field, foffsets[field]) for field in sorted(fields, key=lambda a: foffsets[a]) ] # read only selected fields dat = pd.read_csv( fname, delimiter=",", usecols=[ind for _field, ind in list_field_ind], skiprows=2, header=None, ) for field, ind in list_field_ind: tr[field] = dat[ind].to_numpy() if field[1].startswith("particle_position"): np.divide(tr[field], ds["boxlen"], tr[field]) particle_handler.handle_field(field, tr) return tr class IOHandlerRAMSES(BaseIOHandler): _dataset_type = "ramses" def _read_fluid_selection(self, chunks, selector, fields, size): tr = defaultdict(list) # Set of field types ftypes = {f[0] for f in fields} for chunk in chunks: # Gather fields by type to minimize i/o operations for ft in ftypes: # Get all the fields of the same type field_subs = list(filter(lambda f, ft=ft: f[0] == ft, fields)) # Loop over subsets for subset in chunk.objs: fname = None for fh in subset.domain.field_handlers: if fh.ftype == ft: file_handler = fh fname = fh.fname break if fname is None: raise YTFieldTypeNotFound(ft) # Now we read the entire thing with FortranFile(fname) as fd: # This contains the boundary information, so we skim through # and pick off the right vectors rv = subset.fill(fd, field_subs, selector, file_handler) for ft, f in field_subs: d = rv.pop(f) if d.size == 0: continue mylog.debug( "Filling %s with %s (%0.3e %0.3e) (%s zones)", f, d.size, d.min(), d.max(), d.size, ) tr[ft, f].append(d) d = {} for field in fields: tmp = tr.pop(field, None) d[field] = np.concatenate(tmp) if tmp else np.empty(0, dtype="d") return d def _read_particle_coords(self, chunks, ptf): pn = "particle_position_%s" fields = [ (ptype, f"particle_position_{ax}") for ptype, field_list in ptf.items() for ax in "xyz" ] for chunk in chunks: for subset in chunk.objs: rv = self._read_particle_subset(subset, fields) for ptype in sorted(ptf): yield ( ptype, ( rv[ptype, pn % "x"], rv[ptype, pn % "y"], rv[ptype, pn % "z"], ), 0.0, ) def _read_particle_fields(self, chunks, ptf, selector): pn = "particle_position_%s" chunks = list(chunks) fields = [ (ptype, fname) for ptype, field_list in ptf.items() for fname in field_list ] for ptype, field_list in sorted(ptf.items()): for ax in "xyz": if pn % ax not in field_list: fields.append((ptype, pn % ax)) if ptype == "sink_csv": subset = chunks[0].objs[0] rv = self._read_particle_subset(subset, fields) for ptype, field_list in sorted(ptf.items()): x, y, z = (np.asarray(rv[ptype, pn % ax], "=f8") for ax in "xyz") mask = selector.select_points(x, y, z, 0.0) if mask is None: mask = [] for field in field_list: data = np.asarray(rv.pop((ptype, field))[mask], "=f8") yield (ptype, field), data else: for chunk in chunks: for subset in chunk.objs: rv = self._read_particle_subset(subset, fields) for ptype, field_list in sorted(ptf.items()): x, y, z = ( np.asarray(rv[ptype, pn % ax], "=f8") for ax in "xyz" ) mask = selector.select_points(x, y, z, 0.0) if mask is None: mask = [] for field in field_list: data = np.asarray(rv.pop((ptype, field))[mask], "=f8") yield (ptype, field), data def _read_particle_subset(self, subset, fields): """Read the particle files.""" tr = {} # Sequential read depending on particle type for ptype in {f[0] for f in fields}: # Select relevant files subs_fields = filter(lambda f, ptype=ptype: f[0] == ptype, fields) ok = False for ph in subset.domain.particle_handlers: if ph.ptype == ptype: ok = True count = ph.local_particle_count break if not ok: raise YTFieldTypeNotFound(ptype) tr.update(ph.reader(subset, subs_fields, count)) return tr @lru_cache def _read_part_binary_file_descriptor(fname: Union[str, "os.PathLike[str]"]): """ Read a file descriptor and returns the array of the fields found. """ # Mapping mapping_list = [ ("position_x", "particle_position_x"), ("position_y", "particle_position_y"), ("position_z", "particle_position_z"), ("velocity_x", "particle_velocity_x"), ("velocity_y", "particle_velocity_y"), ("velocity_z", "particle_velocity_z"), ("mass", "particle_mass"), ("identity", "particle_identity"), ("levelp", "particle_level"), ("family", "particle_family"), ("tag", "particle_tag"), ] # Convert to dictionary mapping = dict(mapping_list) with open(fname) as f: line = f.readline() tmp = VERSION_RE.match(line) mylog.debug("Reading part file descriptor %s.", fname) if not tmp: raise YTParticleOutputFormatNotImplemented() version = int(tmp.group(1)) if version == 1: # Skip one line (containing the headers) line = f.readline() fields = [] for i, line in enumerate(f.readlines()): tmp = VAR_DESC_RE.match(line) if not tmp: raise YTFileNotParseable(fname, i + 1) # ivar = tmp.group(1) varname = tmp.group(2) dtype = tmp.group(3) if varname in mapping: varname = mapping[varname] else: varname = f"particle_{varname}" fields.append((varname, dtype)) else: raise YTParticleOutputFormatNotImplemented() return fields @lru_cache def _read_part_csv_file_descriptor(fname: Union[str, "os.PathLike[str]"]): """ Read the file from the csv sink particles output. """ from yt.utilities.on_demand_imports import _pandas as pd # Fields name from the default csv RAMSES sink algorithm in the yt default convention mapping = { " # id": "particle_identifier", "msink": "particle_mass", "x": "particle_position_x", "y": "particle_position_y", "z": "particle_position_z", "vx": "particle_velocity_x", "vy": "particle_velocity_y", "vz": "particle_velocity_z", "lx": "particle_angular_momentum_x", "ly": "particle_angular_momentum_y", "lz": "particle_angular_momentum_z", "tform": "particle_formation_time", "acc_rate": "particle_accretion_rate", "del_mass": "particle_delta_mass", "rho_gas": "particle_rho_gas", "cs**2": "particle_sound_speed", "etherm": "particle_etherm", "vx_gas": "particle_velocity_x_gas", "vy_gas": "particle_velocity_y_gas", "vz_gas": "particle_velocity_z_gas", "mbh": "particle_mass_bh", "level": "particle_level", "rsink_star": "particle_radius_star", } # read the all file to get the number of particle dat = pd.read_csv(fname, delimiter=",") fields = [] local_particle_count = len(dat) for varname in dat.columns: if varname in mapping: varname = mapping[varname] else: varname = f"particle_{varname}" fields.append(varname) return fields, local_particle_count @lru_cache def _read_fluid_file_descriptor(fname: Union[str, "os.PathLike[str]"], *, prefix: str): """ Read a file descriptor and returns the array of the fields found. """ # Mapping mapping_list = [ ("density", "Density"), ("velocity_x", "x-velocity"), ("velocity_y", "y-velocity"), ("velocity_z", "z-velocity"), ("pressure", "Pressure"), ("metallicity", "Metallicity"), # Add mapping for ionized species # Note: we expect internally that these names use the HII, HeII, # HeIII, ... convention for historical reasons. So we need to map # the names read from `hydro_file_descriptor.txt` to this # convention. # This will create fields like ("ramses", "HII") which are mapped # to ("gas", "H_p1_fraction") in fields.py ("H_p1_fraction", "HII"), ("He_p1_fraction", "HeII"), ("He_p2_fraction", "HeIII"), # Photon fluxes / densities are stored as `photon_density_XX`, so # only 100 photon bands can be stored with this format. Let's be # conservative and support up to 100 bands. *[(f"photon_density_{i:02d}", f"Photon_density_{i:d}") for i in range(100)], *[ (f"photon_flux_{i:02d}_{dim}", f"Photon_flux_{dim}_{i:d}") for i in range(100) for dim in "xyz" ], ] # Add mapping for magnetic fields mapping_list += [ (key, key) for key in ( f"B_{dim}_{side}" for side in ["left", "right"] for dim in ["x", "y", "z"] ) ] # Convert to dictionary mapping = dict(mapping_list) with open(fname) as f: line = f.readline() tmp = VERSION_RE.match(line) mylog.debug("Reading fluid file descriptor %s.", fname) if not tmp: return [] version = int(tmp.group(1)) if version == 1: # Skip one line (containing the headers) line = f.readline() fields = [] for i, line in enumerate(f.readlines()): tmp = VAR_DESC_RE.match(line) if not tmp: raise YTFileNotParseable(fname, i + 1) # ivar = tmp.group(1) varname = tmp.group(2) dtype = tmp.group(3) if varname in mapping: varname = mapping[varname] else: varname = f"{prefix}_{varname}" fields.append((varname, dtype)) else: mylog.error("Version %s", version) raise YTParticleOutputFormatNotImplemented() return fields yt-project-yt-f043ac8/yt/frontends/ramses/io_utils.pyx000066400000000000000000000221051510711153200231730ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: include_dirs = LIB_DIR cimport cython from libc.stdio cimport SEEK_CUR, SEEK_SET cimport numpy as np import numpy as np from yt.geometry.oct_container cimport RAMSESOctreeContainer from yt.utilities.cython_fortran_utils cimport FortranFile from yt.utilities.exceptions import YTIllDefinedAMRData ctypedef np.int32_t INT32_t ctypedef np.int64_t INT64_t ctypedef np.float64_t DOUBLE_t cdef int INT32_SIZE = sizeof(np.int32_t) cdef int INT64_SIZE = sizeof(np.int64_t) cdef int DOUBLE_SIZE = sizeof(np.float64_t) cdef inline int skip_len(int Nskip, int record_len) noexcept nogil: return Nskip * (record_len * DOUBLE_SIZE + INT64_SIZE) @cython.cpow(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.nonecheck(False) def read_amr(FortranFile f, dict headers, np.ndarray[np.int64_t, ndim=1] ngridbound, INT64_t min_level, RAMSESOctreeContainer oct_handler): cdef INT64_t ncpu, nboundary, max_level, nlevelmax, ncpu_and_bound cdef DOUBLE_t nx, ny, nz cdef INT64_t ilevel, icpu, n, ndim, jump_len cdef INT32_t ng cdef np.ndarray[np.int32_t, ndim=2] numbl cdef np.ndarray[np.float64_t, ndim=2] pos cdef int i # The ordering is very important here, as we'll write directly into the memory # address the content of the files. cdef np.float64_t[::1, :] pos_view ndim = headers['ndim'] numbl = headers['numbl'] nboundary = headers['nboundary'] nx, ny, nz = (((i-1.0)/2.0) for i in headers['nx']) nlevelmax = headers['nlevelmax'] ncpu = headers['ncpu'] ncpu_and_bound = nboundary + ncpu # Allocate more memory if required pos = np.empty((max(numbl.max(), ngridbound.max()), 3), dtype="d", order="F") pos_view = pos # Compute number of fields to skip. This should be 31 in 3 dimensions jump_len = (1 # father index + 2*ndim # neighbor index + 2**ndim # son index + 2**ndim # cpu map + 2**ndim # refinement map ) # Initialize values max_level = 0 cdef int record_len for ilevel in range(nlevelmax): for icpu in range(ncpu_and_bound): if icpu < ncpu: ng = numbl[ilevel, icpu] else: ng = ngridbound[icpu - ncpu + nboundary*ilevel] if ng == 0: continue # Skip grid index, 'next' and 'prev' arrays record_len = (ng * INT32_SIZE + INT64_SIZE) f.seek(record_len * 3, SEEK_CUR) f.read_vector_inplace("d", &pos_view[0, 0]) f.read_vector_inplace("d", &pos_view[0, 1]) f.read_vector_inplace("d", &pos_view[0, 2]) for i in range(ng): pos_view[i, 0] -= nx for i in range(ng): pos_view[i, 1] -= ny for i in range(ng): pos_view[i, 2] -= nz # Skip father, neighbor, son, cpu map and refinement map f.seek(record_len * jump_len, SEEK_CUR) # Note that we're adding *grids*, not individual cells. if ilevel >= min_level: n = oct_handler.add(icpu + 1, ilevel - min_level, pos[:ng, :], count_boundary = 1) if n > 0: max_level = max(ilevel - min_level, max_level) return max_level @cython.cpow(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.nonecheck(False) cpdef read_offset(FortranFile f, INT64_t min_level, INT64_t domain_id, INT64_t nvar, dict headers, int Nskip): cdef np.ndarray[np.int64_t, ndim=2] offset, level_count cdef INT64_t ndim, twotondim, nlevelmax, n_levels, nboundary, ncpu, ncpu_and_bound cdef INT64_t ilevel, icpu cdef INT32_t file_ilevel, file_ncache ndim = headers['ndim'] nboundary = headers['nboundary'] nlevelmax = headers['nlevelmax'] n_levels = nlevelmax - min_level ncpu = headers['ncpu'] ncpu_and_bound = nboundary + ncpu twotondim = 2**ndim if Nskip == -1: Nskip = twotondim * nvar # It goes: level, CPU, 8-variable (1 oct) offset = np.full((ncpu_and_bound, n_levels), -1, dtype=np.int64) level_count = np.zeros((ncpu_and_bound, n_levels), dtype=np.int64) cdef np.int64_t[:, ::1] level_count_view = level_count cdef np.int64_t[:, ::1] offset_view = offset for ilevel in range(nlevelmax): for icpu in range(ncpu_and_bound): file_ilevel = f.read_int() file_ncache = f.read_int() if file_ncache == 0: continue if file_ilevel != ilevel+1: raise YTIllDefinedAMRData( 'Cannot read offsets in file %s. The level read ' 'from data (%s) is not coherent with the expected (%s)', f.name, file_ilevel, ilevel) if ilevel >= min_level: offset_view[icpu, ilevel - min_level] = f.tell() level_count_view[icpu, ilevel - min_level] = file_ncache f.seek(skip_len(Nskip, file_ncache), SEEK_CUR) return offset, level_count @cython.cpow(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.nonecheck(False) def fill_hydro(FortranFile f, np.ndarray[np.int64_t, ndim=2] offsets, np.ndarray[np.int64_t, ndim=2] level_count, list cpu_enumerator, np.ndarray[np.uint8_t, ndim=1] level_inds, np.ndarray[np.uint8_t, ndim=1] cell_inds, np.ndarray[np.int64_t, ndim=1] file_inds, INT64_t ndim, list all_fields, list fields, dict tr, RAMSESOctreeContainer oct_handler, np.ndarray[np.int32_t, ndim=1] domain_inds=np.array([], dtype='int32')): cdef INT64_t offset cdef dict tmp cdef str field cdef INT64_t twotondim cdef int ilevel, icpu, nlevels, nc, ncpu_selected, nfields_selected cdef int i, j, ii twotondim = 2**ndim nfields_selected = len(fields) nlevels = offsets.shape[1] ncpu_selected = len(cpu_enumerator) cdef np.int64_t[::1] cpu_list = np.asarray(cpu_enumerator, dtype=np.int64) cdef np.int64_t[::1] jumps = np.zeros(nfields_selected + 1, dtype=np.int64) cdef int jump_len, Ncells cdef np.uint8_t[::1] mask_level = np.zeros(nlevels, dtype=np.uint8) # First, make sure fields are in the same order fields = sorted(fields, key=lambda f: all_fields.index(f)) # The ordering is very important here, as we'll write directly into the memory # address the content of the files. cdef np.float64_t[::1, :, :] buffer jump_len = 0 j = 0 for i, field in enumerate(all_fields): if field in fields: jumps[j] = jump_len j += 1 jump_len = 0 else: jump_len += 1 jumps[j] = jump_len cdef int first_field_index = jumps[0] buffer = np.empty((level_count.max(), twotondim, nfields_selected), dtype="float64", order='F') # Precompute which levels we need to read Ncells = len(level_inds) for i in range(Ncells): mask_level[level_inds[i]] |= 1 # Loop over levels for ilevel in range(nlevels): if mask_level[ilevel] == 0: continue # Loop over cpu domains. In most cases, we'll only read the CPU corresponding to the domain. for ii in range(ncpu_selected): icpu = cpu_list[ii] nc = level_count[icpu, ilevel] if nc == 0: continue offset = offsets[icpu, ilevel] if offset == -1: continue f.seek(offset + skip_len(first_field_index, nc), SEEK_SET) # We have already skipped the first fields (if any) # so we "rewind" (this will cancel the first seek) jump_len = -first_field_index for i in range(twotondim): # Read the selected fields for j in range(nfields_selected): jump_len += jumps[j] if jump_len > 0: f.seek(skip_len(jump_len, nc), SEEK_CUR) jump_len = 0 f.read_vector_inplace('d', &buffer[0, i, j]) jump_len += jumps[nfields_selected] # In principle, we may be left with some fields to skip # but since we're doing an absolute seek at the beginning of # the loop on CPUs, we can spare one seek here ## if jump_len > 0: ## f.seek(skip_len(jump_len, nc), SEEK_CUR) # Alias buffer into dictionary tmp = {} for i, field in enumerate(fields): tmp[field] = buffer[:, :, i] if ncpu_selected > 1: oct_handler.fill_level_with_domain( ilevel, level_inds, cell_inds, file_inds, domain_inds, tr, tmp, domain=icpu+1) else: oct_handler.fill_level( ilevel, level_inds, cell_inds, file_inds, tr, tmp) yt-project-yt-f043ac8/yt/frontends/ramses/particle_handlers.py000066400000000000000000000404041510711153200246410ustar00rootroot00000000000000import abc import os import struct from collections.abc import Callable from functools import cached_property from itertools import chain, count from typing import TYPE_CHECKING, Any import numpy as np from yt._typing import FieldKey from yt.config import ytcfg from yt.funcs import mylog from yt.utilities.cython_fortran_utils import FortranFile from .field_handlers import HandlerMixin from .io import ( _ramses_particle_binary_file_handler, _ramses_particle_csv_file_handler, _read_part_binary_file_descriptor, _read_part_csv_file_descriptor, convert_ramses_conformal_time_to_physical_time, ) if TYPE_CHECKING: from yt.frontends.ramses.data_structures import RAMSESDomainSubset PARTICLE_HANDLERS: set[type["ParticleFileHandler"]] = set() def get_particle_handlers(): return PARTICLE_HANDLERS def register_particle_handler(ph): PARTICLE_HANDLERS.add(ph) class ParticleFileHandler(abc.ABC, HandlerMixin): """ Abstract class to handle particles in RAMSES. Each instance represents a single file (one domain). To add support to a new particle file, inherit from this class and implement all functions containing a `NotImplementedError`. See `SinkParticleFileHandler` for an example implementation.""" _file_type = "particle" ## These properties are static # The name to give to the particle type ptype: str # The name of the file(s). fname: str # The name of the file descriptor (if any) file_descriptor: str | None = None # The attributes of the header attrs: tuple[tuple[str, int, str], ...] # A list of tuple containing the field name and its type known_fields: list[FieldKey] # The function to employ to read the file reader: Callable[ ["ParticleFileHandler", "RAMSESDomainSubset", list[tuple[str, str]], int], dict[tuple[str, str], np.ndarray], ] # Name of the config section (if any) config_field: str | None = None ## These properties are computed dynamically # Mapping from field to offset in file _field_offsets: dict[tuple[str, str], int] # Mapping from field to the type of the data (float, integer, ...) _field_types: dict[tuple[str, str], str] # Number of particle in the domain _local_particle_count: int # The header of the file _header: dict[str, Any] def __init_subclass__(cls, *args, **kwargs): """ Registers subclasses at creation. """ super().__init_subclass__(*args, **kwargs) if cls.ptype is not None: register_particle_handler(cls) cls._unique_registry = {} return cls def __init__(self, domain): self.setup_handler(domain) # Attempt to read the list of fields from the config file if self.config_field and ytcfg.has_section(self.config_field): cfg = ytcfg.get(self.config_field, "fields") known_fields = [] for c in (_.strip() for _ in cfg.split("\n") if _.strip() != ""): field, field_type = (_.strip() for _ in c.split(",")) known_fields.append((field, field_type)) self.known_fields = known_fields @abc.abstractmethod def read_header(self): """ This function is called once per file. It should: * read the header of the file and store any relevant information * detect the fields in the file * compute the offsets (location in the file) of each field It is in charge of setting `self.field_offsets` and `self.field_types`. * `_field_offsets`: dictionary: tuple -> integer A dictionary that maps `(type, field_name)` to their location in the file (integer) * `_field_types`: dictionary: tuple -> character A dictionary that maps `(type, field_name)` to their type (character), following Python's struct convention. """ pass @property def field_offsets(self) -> dict[tuple[str, str], int]: if hasattr(self, "_field_offsets"): return self._field_offsets self.read_header() return self._field_offsets @property def field_types(self) -> dict[tuple[str, str], str]: if hasattr(self, "_field_types"): return self._field_types self.read_header() return self._field_types @property def local_particle_count(self) -> int: if hasattr(self, "_local_particle_count"): return self._local_particle_count self.read_header() return self._local_particle_count @property def header(self) -> dict[str, Any]: if hasattr(self, "_header"): return self._header self.read_header() return self._header def handle_field( self, field: tuple[str, str], data_dict: dict[tuple[str, str], np.ndarray] ): """ This function allows custom code to be called to handle special cases, such as the particle birth time. It updates the `data_dict` dictionary with the new data. Parameters ---------- field : tuple[str, str] The field name. data_dict : dict[tuple[str, str], np.ndarray] A dictionary containing the data. By default, this function does nothing. """ pass _default_dtypes: dict[int, str] = { 1: "c", # char 2: "h", # short, 4: "f", # float 8: "d", # double } class DefaultParticleFileHandler(ParticleFileHandler): ptype = "io" fname = "part_{iout:05d}.out{icpu:05d}" file_descriptor = "part_file_descriptor.txt" config_field = "ramses-particles" reader = _ramses_particle_binary_file_handler attrs = ( ("ncpu", 1, "i"), ("ndim", 1, "i"), ("npart", 1, "i"), ("localseed", -1, "i"), ("nstar_tot", 1, "i"), ("mstar_tot", 1, "d"), ("mstar_lost", 1, "d"), ("nsink", 1, "i"), ) known_fields = [ ("particle_position_x", "d"), ("particle_position_y", "d"), ("particle_position_z", "d"), ("particle_velocity_x", "d"), ("particle_velocity_y", "d"), ("particle_velocity_z", "d"), ("particle_mass", "d"), ("particle_identity", "i"), ("particle_refinement_level", "i"), ] def read_header(self): if not self.exists: self._field_offsets = {} self._field_types = {} self._local_particle_count = 0 self._header = {} return flen = os.path.getsize(self.fname) with FortranFile(self.fname) as fd: hvals = dict(fd.read_attrs(self.attrs)) particle_field_pos = fd.tell() self._header = hvals self._local_particle_count = hvals["npart"] extra_particle_fields = self.ds._extra_particle_fields if self.has_descriptor: particle_fields = _read_part_binary_file_descriptor(self.file_descriptor) else: particle_fields = list(self.known_fields) if extra_particle_fields is not None: particle_fields += extra_particle_fields if ( hvals["nstar_tot"] > 0 and extra_particle_fields is not None and ("particle_birth_time", "d") not in particle_fields ): particle_fields += [ ("particle_birth_time", "d"), ("particle_metallicity", "d"), ] def build_iterator(): return chain( particle_fields, ((f"particle_extra_field_{i}", "d") for i in count(1)), ) field_offsets = {} _pfields = {} ptype = self.ptype blockLen = struct.calcsize("i") * 2 particle_fields_iterator = build_iterator() ipos = particle_field_pos while ipos < flen: field, vtype = next(particle_fields_iterator) field_offsets[ptype, field] = ipos _pfields[ptype, field] = vtype ipos += blockLen + struct.calcsize(vtype) * hvals["npart"] if ipos != flen: particle_fields_iterator = build_iterator() with FortranFile(self.fname) as fd: fd.seek(particle_field_pos) ipos = particle_field_pos while ipos < flen: field, vtype = next(particle_fields_iterator) old_pos = fd.tell() field_offsets[ptype, field] = old_pos fd.skip(1) ipos = fd.tell() record_len = ipos - old_pos - blockLen exp_len = struct.calcsize(vtype) * hvals["npart"] if record_len != exp_len: # Guess record vtype from length nbytes = record_len // hvals["npart"] # NOTE: in some simulations (e.g. New Horizon), the record length is not # a multiple of 1, 2, 4 or 8. In this case, fallback onto assuming # double precision. vtype = _default_dtypes.get(nbytes, "d") mylog.warning( "Supposed that `%s` has type %s given record size", field, np.dtype(vtype), ) _pfields[ptype, field] = vtype if field.startswith("particle_extra_field_"): iextra = int(field.split("_")[-1]) else: iextra = 0 if iextra > 0 and not self.ds._warned_extra_fields["io"]: mylog.warning( "Detected %s extra particle fields assuming kind " "`double`. Consider using the `extra_particle_fields` " "keyword argument if you have unexpected behavior.", iextra, ) self.ds._warned_extra_fields["io"] = True if ( self.ds.use_conformal_time and (ptype, "particle_birth_time") in field_offsets ): field_offsets[ptype, "conformal_birth_time"] = field_offsets[ ptype, "particle_birth_time" ] _pfields[ptype, "conformal_birth_time"] = _pfields[ ptype, "particle_birth_time" ] self._field_offsets = field_offsets self._field_types = _pfields @property def birth_file_fname(self): basename = os.path.abspath(self.ds.root_folder) iout = int( os.path.basename(self.ds.parameter_filename).split(".")[0].split("_")[1] ) icpu = self.domain_id fname = os.path.join(basename, f"birth_{iout:05d}.out{icpu:05d}") return fname @cached_property def has_birth_file(self): return os.path.exists(self.birth_file_fname) def handle_field( self, field: tuple[str, str], data_dict: dict[tuple[str, str], np.ndarray] ): _ptype, fname = field if not (fname == "particle_birth_time" and self.ds.cosmological_simulation): return # If the birth files exist, read from them if self.has_birth_file: with FortranFile(self.birth_file_fname) as fd: # Note: values are written in Gyr, so we need to convert back to code_time data_dict[field] = ( self.ds.arr(fd.read_vector("d"), "Gyr").to("code_time").v ) return # Otherwise, convert conformal time to physical age ds = self.ds conformal_time = data_dict[field] physical_time = ( convert_ramses_conformal_time_to_physical_time(ds, conformal_time) .to("code_time") .v ) # arbitrarily set particles with zero conformal_age to zero # particle_age. This corresponds to DM particles. data_dict[field] = np.where(conformal_time != 0, physical_time, 0) class SinkParticleFileHandler(ParticleFileHandler): """Handle sink files""" ptype = "sink" fname = "sink_{iout:05d}.out{icpu:05d}" file_descriptor = "sink_file_descriptor.txt" config_field = "ramses-sink-particles" reader = _ramses_particle_binary_file_handler attrs = (("nsink", 1, "i"), ("nindsink", 1, "i")) known_fields = [ ("particle_identifier", "i"), ("particle_mass", "d"), ("particle_position_x", "d"), ("particle_position_y", "d"), ("particle_position_z", "d"), ("particle_velocity_x", "d"), ("particle_velocity_y", "d"), ("particle_velocity_z", "d"), ("particle_birth_time", "d"), ("BH_real_accretion", "d"), ("BH_bondi_accretion", "d"), ("BH_eddington_accretion", "d"), ("BH_esave", "d"), ("gas_spin_x", "d"), ("gas_spin_y", "d"), ("gas_spin_z", "d"), ("BH_spin_x", "d"), ("BH_spin_y", "d"), ("BH_spin_z", "d"), ("BH_spin", "d"), ("BH_efficiency", "d"), ] def read_header(self): if not self.exists: self._field_offsets = {} self._field_types = {} self._local_particle_count = 0 self._header = {} return fd = FortranFile(self.fname) flen = os.path.getsize(self.fname) hvals = {} # Read the header of the file attrs = self.attrs hvals.update(fd.read_attrs(attrs)) self._header = hvals # This is somehow a trick here: we only want one domain to # be read, as ramses writes all the sinks in all the # domains. Here, we set the local_particle_count to 0 except # for the first domain to be red. if getattr(self.ds, "_sink_file_flag", False): self._local_particle_count = 0 else: self.ds._sink_file_flag = True self._local_particle_count = hvals["nsink"] # Read the fields + add the sink properties if self.has_descriptor: fields = _read_part_binary_file_descriptor(self.file_descriptor) else: fields = list(self.known_fields) # Note: this follows RAMSES convention. for i in range(self.ds.dimensionality * 2 + 1): for ilvl in range(self.ds.max_level + 1): fields.append((f"particle_prop_{ilvl}_{i}", "d")) field_offsets = {} _pfields = {} # Fill the fields, offsets and types self.fields = [] for field, vtype in fields: self.fields.append(field) if fd.tell() >= flen: break field_offsets[self.ptype, field] = fd.tell() _pfields[self.ptype, field] = vtype fd.skip(1) self._field_offsets = field_offsets self._field_types = _pfields fd.close() class SinkParticleFileHandlerCsv(ParticleFileHandler): """Handle sink files from a csv file, the format from the sink particle in ramses""" ptype = "sink_csv" fname = "sink_{iout:05d}.csv" file_descriptor = None config_field = "ramses-sink-particles" reader = _ramses_particle_csv_file_handler attrs = (("nsink", 1, "i"), ("nindsink", 1, "i")) def read_header(self): if not self.exists: self._field_offsets = {} self._field_types = {} self._local_particle_count = 0 self._header = {} return field_offsets = {} _pfields = {} fields, self._local_particle_count = _read_part_csv_file_descriptor(self.fname) for ind, field in enumerate(fields): field_offsets[self.ptype, field] = ind _pfields[self.ptype, field] = "d" self._field_offsets = field_offsets self._field_types = _pfields def handle_field( self, field: tuple[str, str], data_dict: dict[tuple[str, str], np.ndarray] ): _ptype, fname = field if not (fname == "particle_birth_time" and self.ds.cosmological_simulation): return # convert conformal time to physical age ds = self.ds conformal_time = data_dict[field] physical_time = convert_ramses_conformal_time_to_physical_time( ds, conformal_time ) # arbitrarily set particles with zero conformal_age to zero # particle_age. This corresponds to DM particles. data_dict[field] = np.where(conformal_time > 0, physical_time, 0) yt-project-yt-f043ac8/yt/frontends/ramses/tests/000077500000000000000000000000001510711153200217445ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ramses/tests/__init__.py000066400000000000000000000000001510711153200240430ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ramses/tests/test_file_sanitizer.py000066400000000000000000000067161510711153200263760ustar00rootroot00000000000000import re import tempfile from collections import namedtuple from itertools import chain from pathlib import Path import pytest from yt.frontends.ramses.data_structures import RAMSESFileSanitizer PathTuple = namedtuple( "PathTuple", ("output_dir", "group_dir_name", "info_file", "paths_to_try") ) def generate_paths(create): with tempfile.TemporaryDirectory() as tmpdir: output_dir = Path(tmpdir) / "output_00123" output_dir.mkdir() amr_file = output_dir / "amr_00123.out00001" if create: amr_file.touch() info_file = output_dir / "info_00123.txt" if create: info_file.touch() # Test with regular structure output_dir2 = Path(tmpdir) / "output_00124" output_dir2.mkdir() group_dir2 = output_dir2 / "group_00001" group_dir2.mkdir() info_file2 = group_dir2 / "info_00124.txt" if create: info_file2.touch() amr_file2 = group_dir2 / "amr_00124.out00001" if create: amr_file2.touch() yield ( PathTuple( output_dir=output_dir, group_dir_name=None, info_file=info_file, paths_to_try=(output_dir, info_file, amr_file), ), PathTuple( output_dir=output_dir2, group_dir_name=group_dir2.name, info_file=info_file2, paths_to_try=(output_dir2, info_file2, group_dir2, amr_file2), ), ) @pytest.fixture def valid_path_tuples(): yield from generate_paths(create=True) @pytest.fixture def invalid_path_tuples(): yield from generate_paths(create=False) def test_valid_sanitizing(valid_path_tuples): for answer in valid_path_tuples: for path in answer.paths_to_try: sanitizer = RAMSESFileSanitizer(path) sanitizer.validate() assert sanitizer.root_folder == answer.output_dir assert sanitizer.group_name == answer.group_dir_name assert sanitizer.info_fname == answer.info_file def test_invalid_sanitizing(valid_path_tuples, invalid_path_tuples): for path in chain(*(pt.paths_to_try for pt in invalid_path_tuples)): sanitizer = RAMSESFileSanitizer(path) if path.exists(): expected_error = ValueError expected_error_message = ( "Could not determine output directory from '.*'\n" "Expected a directory name of form .* " "containing an info_\\*.txt file and amr_\\* files." ) else: expected_error = FileNotFoundError expected_error_message = re.escape( f"No such file or directory '{str(path)}'" ) with pytest.raises(expected_error, match=expected_error_message): sanitizer.validate() for path in chain(*(pt.paths_to_try for pt in valid_path_tuples)): expected_error_message = re.escape( f"No such file or directory '{str(path/'does_not_exist.txt')}'" ) sanitizer = RAMSESFileSanitizer(path / "does_not_exist.txt") with pytest.raises(FileNotFoundError, match=expected_error_message): sanitizer.validate() expected_error_message = "No such file or directory '.*'" sanitizer = RAMSESFileSanitizer(Path("this") / "does" / "not" / "exist") with pytest.raises(FileNotFoundError, match=expected_error_message): sanitizer.validate() yt-project-yt-f043ac8/yt/frontends/ramses/tests/test_hilbert.py000066400000000000000000000033671510711153200250170ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal import yt from yt.frontends.ramses.hilbert import get_cpu_list_cuboid, hilbert3d from yt.testing import requires_file def test_hilbert3d(): # 8 different cases, checked against RAMSES' own implementation inputs = [ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1], ] outputs = [0, 1, 7, 6, 3, 2, 4, 5] for i, o in zip(inputs, outputs, strict=True): assert_equal(hilbert3d(i, 3).item(), o) output_00080 = "output_00080/info_00080.txt" @requires_file(output_00080) def test_get_cpu_list(): ds = yt.load(output_00080) np.random.seed(16091992) # These are randomly generated outputs, checked against RAMSES' own implementation inputs = ( [[0.27747276, 0.30018937, 0.17916189], [0.42656026, 0.40509483, 0.29927838]], [[0.90660856, 0.44201328, 0.22770587], [1.09175462, 0.58017918, 0.2836648]], [[0.98542323, 0.58543376, 0.45858327], [1.04441105, 0.62079207, 0.58919283]], [[0.42274841, 0.44887745, 0.87793679], [0.52066634, 0.58936331, 1.00666222]], [[0.69964803, 0.65893669, 0.03660775], [0.80565696, 0.67409752, 0.11434604]], ) outputs = ([0, 15], [0, 15], [0, 1, 15], [0, 13, 14, 15], [0]) ncpu = ds.parameters["ncpu"] bound_keys = np.array( [ds.hilbert_indices[icpu][0] for icpu in range(1, ncpu + 1)] + [ds.hilbert_indices[ds.parameters["ncpu"]][1]], dtype="float64", ) for i, o in zip(inputs, outputs, strict=True): bbox = i ls = list(get_cpu_list_cuboid(ds, bbox, bound_keys=bound_keys)) assert len(ls) > 0 assert all(np.array(o) == np.array(ls)) yt-project-yt-f043ac8/yt/frontends/ramses/tests/test_outputs.py000066400000000000000000000615611510711153200251110ustar00rootroot00000000000000import os from contextlib import contextmanager from pathlib import Path from shutil import copytree from tempfile import TemporaryDirectory import numpy as np from numpy.testing import assert_equal, assert_raises import yt from yt.config import ytcfg from yt.fields.field_detector import FieldDetector from yt.frontends.ramses.api import RAMSESDataset from yt.frontends.ramses.field_handlers import DETECTED_FIELDS, HydroFieldFileHandler from yt.testing import requires_file, requires_module, units_override_check from yt.utilities.answer_testing.framework import ( FieldValuesTest, PixelizedProjectionValuesTest, create_obj, data_dir_load, requires_ds, ) from yt.utilities.on_demand_imports import _f90nml as f90nml _fields = ( ("gas", "temperature"), ("gas", "density"), ("gas", "velocity_magnitude"), ("gas", "pressure_gradient_magnitude"), ) output_00080 = "output_00080/info_00080.txt" @requires_ds(output_00080) def test_output_00080(): ds = data_dir_load(output_00080) assert_equal(str(ds), "info_00080") assert_equal(ds.particle_type_counts, {"io": 1090895, "nbody": 0}) dso = [None, ("sphere", ("max", (0.1, "unitary")))] for dobj_name in dso: for field in _fields: for axis in [0, 1, 2]: for weight_field in [None, ("gas", "density")]: yield PixelizedProjectionValuesTest( output_00080, axis, field, weight_field, dobj_name ) yield FieldValuesTest(output_00080, field, dobj_name) dobj = create_obj(ds, dobj_name) s1 = dobj["index", "ones"].sum() s2 = sum(mask.sum() for block, mask in dobj.blocks) assert_equal(s1, s2) @requires_file(output_00080) def test_RAMSESDataset(): assert isinstance(data_dir_load(output_00080), RAMSESDataset) @requires_file(output_00080) def test_RAMSES_alternative_load(): # Test that we can load a RAMSES dataset by giving it the folder name, # the info file name or an amr file name base_dir, info_file_fname = os.path.split(output_00080) for fname in (base_dir, os.path.join(base_dir, "amr_00080.out00001")): assert isinstance(data_dir_load(fname), RAMSESDataset) @requires_file(output_00080) def test_units_override(): units_override_check(output_00080) ramsesNonCosmo = "DICEGalaxyDisk_nonCosmological/output_00002/info_00002.txt" @requires_file(ramsesNonCosmo) def test_non_cosmo_detection(): ds = yt.load(ramsesNonCosmo, cosmological=False) assert_equal(ds.cosmological_simulation, 0) ds = yt.load(ramsesNonCosmo, cosmological=None) assert_equal(ds.cosmological_simulation, 0) ds = yt.load(ramsesNonCosmo) assert_equal(ds.cosmological_simulation, 0) @requires_file(ramsesNonCosmo) def test_unit_non_cosmo(): for force_cosmo in [False, None]: ds = yt.load(ramsesNonCosmo, cosmological=force_cosmo) expected_raw_time = 0.0299468077820411 # in ramses unit assert_equal(ds.current_time.value, expected_raw_time) expected_time = 14087886140997.336 # in seconds assert_equal(ds.current_time.in_units("s").value, expected_time) ramsesCosmo = "output_00080/info_00080.txt" @requires_file(ramsesCosmo) def test_cosmo_detection(): ds = yt.load(ramsesCosmo, cosmological=True) assert_equal(ds.cosmological_simulation, 1) ds = yt.load(ramsesCosmo, cosmological=None) assert_equal(ds.cosmological_simulation, 1) ds = yt.load(ramsesCosmo) assert_equal(ds.cosmological_simulation, 1) @requires_file(ramsesCosmo) def test_unit_cosmo(): for force_cosmo in [True, None]: ds = yt.load(ramsesCosmo, cosmological=force_cosmo) # NOTE: these are the old test values, which used 3.08e24 as # the Mpc to cm conversion factor # expected_raw_time = 1.119216564055017 # in ramses unit # expected_time = 3.756241729312462e17 # in seconds expected_raw_time = 1.1213297131656712 # in ramses unit np.testing.assert_equal( ds.current_time.to("code_time").value, expected_raw_time ) expected_time = 3.7633337427123904e17 # in seconds np.testing.assert_equal(ds.current_time.to("s").value, expected_time) ramsesExtraFieldsSmall = "ramses_extra_fields_small/output_00001" @requires_file(ramsesExtraFieldsSmall) def test_extra_fields(): extra_fields = [("particle_family", "I"), ("particle_pointer", "I")] ds = yt.load( os.path.join(ramsesExtraFieldsSmall, "info_00001.txt"), extra_particle_fields=extra_fields, ) # the dataset should contain the fields for field, _ in extra_fields: assert ("all", field) in ds.field_list # Check the family (they should equal 100, for tracer particles) dd = ds.all_data() families = dd["all", "particle_family"] assert all(families == 100) @requires_file(ramsesExtraFieldsSmall) def test_extra_fields_2(): extra_fields = [f"particle_extra_field_{i + 1}" for i in range(2)] ds = yt.load(os.path.join(ramsesExtraFieldsSmall, "info_00001.txt")) # When migrating to pytest, uncomment this # with pytest.warns( # UserWarning, # match=r"Field (.*) has a length \d+, but expected a length of \d+.()", # ): # ds.index # the dataset should contain the fields for field in extra_fields: assert ("io", field) in ds.field_list # In the dataset, the fields are integers, so we cannot test # that they are accessed correctly. ramses_rt = "ramses_rt_00088/output_00088/info_00088.txt" @requires_file(ramses_rt) def test_ramses_rt(): ds = yt.load(ramses_rt) ad = ds.all_data() expected_fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pres_IR", "Pressure", "Metallicity", "HII", "HeII", "HeIII", ] for field in expected_fields: assert ("ramses", field) in ds.field_list # test that field access works ad["ramses", field] # test that special derived fields for RT datasets work special_fields = [("gas", "temp_IR")] species = ["H_p1", "He_p1", "He_p2"] for specie in species: special_fields.extend( [ ("gas", f"{specie}_fraction"), ("gas", f"{specie}_density"), ("gas", f"{specie}_mass"), ] ) for field in special_fields: assert field in ds.derived_field_list ad[field] # Test the creation of rt fields rt_fields = [("rt", "photon_density_1")] + [ ("rt", f"photon_flux_{d}_1") for d in "xyz" ] for field in rt_fields: assert field in ds.derived_field_list ad[field] ramses_sink = "ramses_sink_00016/output_00016/info_00016.txt" @requires_file(ramses_sink) def test_ramses_sink(): expected_fields = [ "BH_bondi_accretion", "BH_eddington_accretion", "BH_efficiency", "BH_esave", "BH_real_accretion", "BH_spin", "BH_spin_x", "BH_spin_y", "BH_spin_z", "gas_spin_x", "gas_spin_y", "gas_spin_z", "particle_birth_time", "particle_identifier", "particle_mass", "particle_position_x", "particle_position_y", "particle_position_z", "particle_prop_0_0", "particle_prop_0_1", "particle_prop_0_2", "particle_prop_0_3", "particle_prop_0_4", "particle_prop_0_5", "particle_prop_0_6", "particle_velocity_x", "particle_velocity_y", "particle_velocity_z", ] # Check that sinks are autodetected ds = yt.load(ramses_sink) ad = ds.all_data() for field in expected_fields: assert ("sink", field) in ds.field_list # test that field access works ad["sink", field] # Checking that sinks are autodetected ds = yt.load(ramsesCosmo) ad = ds.all_data() for field in expected_fields: assert ("sink", field) not in ds.field_list ramses_new_format = "ramses_new_format/output_00002/info_00002.txt" @requires_file(ramses_new_format) def test_new_format(): expected_particle_fields = [ ("star", "particle_identity"), ("star", "particle_level"), ("star", "particle_mass"), ("star", "particle_metallicity"), ("star", "particle_position_x"), ("star", "particle_position_y"), ("star", "particle_position_z"), ("star", "particle_tag"), ("star", "particle_velocity_x"), ("star", "particle_velocity_y"), ("star", "particle_velocity_z"), ] ds = yt.load(ramses_new_format) ad = ds.all_data() # Check all the expected fields exist and can be accessed for f in expected_particle_fields: assert f in ds.derived_field_list ad[f] # Check there is only stars with tag 0 (it should be right) assert all(ad["star", "particle_family"] == 2) assert all(ad["star", "particle_tag"] == 0) assert len(ad["star", "particle_tag"]) == 600 @requires_file(ramses_sink) def test_ramses_part_count(): ds = yt.load(ramses_sink) pcount = ds.particle_type_counts assert_equal(pcount["io"], 17132, err_msg="Got wrong number of io particle") assert_equal(pcount["sink"], 8, err_msg="Got wrong number of sink particle") @requires_file(ramsesCosmo) def test_custom_particle_def(): ytcfg.add_section("ramses-particles") ytcfg["ramses-particles", "fields"] = """particle_position_x, d particle_position_y, d particle_position_z, d particle_velocity_x, d particle_velocity_y, d particle_velocity_z, d particle_mass, d particle_identifier, i particle_refinement_level, I particle_birth_time, d particle_foobar, d """ ds = yt.load(ramsesCosmo) def check_unit(array, unit): assert str(array.in_cgs().units) == unit try: assert ("io", "particle_birth_time") in ds.derived_field_list assert ("io", "particle_foobar") in ds.derived_field_list check_unit(ds.r["io", "particle_birth_time"], "s") check_unit(ds.r["io", "particle_foobar"], "dimensionless") finally: ytcfg.remove_section("ramses-particles") @requires_file(output_00080) @requires_file(ramses_sink) def test_grav_detection(): for path, has_potential in ((output_00080, False), (ramses_sink, True)): ds = yt.load(path) # Test detection for k in "xyz": assert ("gravity", f"{k}-acceleration") in ds.field_list assert ("gas", f"acceleration_{k}") in ds.derived_field_list if has_potential: assert ("gravity", "Potential") in ds.field_list assert ("gas", "potential") in ds.derived_field_list assert ("gas", "potential_energy") in ds.derived_field_list # Test access for k in "xyz": ds.r["gas", f"acceleration_{k}"].to("m/s**2") if has_potential: ds.r["gas", "potential"].to("m**2/s**2") ds.r["gas", "potential_energy"].to("kg*m**2/s**2") @requires_file(ramses_sink) @requires_file(output_00080) def test_ramses_field_detection(): ds1 = yt.load(ramses_rt) assert "ramses" not in DETECTED_FIELDS # Now they are detected ! ds1.index P1 = HydroFieldFileHandler.parameters assert DETECTED_FIELDS[ds1.unique_identifier]["ramses"] is not None fields_1 = set(DETECTED_FIELDS[ds1.unique_identifier]["ramses"]) # Check the right number of variables has been loaded assert P1[ds1.unique_identifier]["nvar"] == 10 assert len(fields_1) == P1[ds1.unique_identifier]["nvar"] # Now load another dataset ds2 = yt.load(output_00080) ds2.index P2 = HydroFieldFileHandler.parameters fields_2 = set(DETECTED_FIELDS[ds2.unique_identifier]["ramses"]) # Check the right number of variables has been loaded assert P2[ds2.unique_identifier]["nvar"] == 6 assert len(fields_2) == P2[ds2.unique_identifier]["nvar"] @requires_file(ramses_new_format) @requires_file(ramsesCosmo) @requires_file(ramsesNonCosmo) def test_formation_time(): extra_particle_fields = [ ("particle_birth_time", "d"), ("particle_metallicity", "d"), ] # test semantics for cosmological dataset ds = yt.load(ramsesCosmo, extra_particle_fields=extra_particle_fields) assert ("io", "particle_birth_time") in ds.field_list assert ("io", "conformal_birth_time") in ds.field_list assert ("io", "particle_metallicity") in ds.field_list ad = ds.all_data() whstars = ad["io", "conformal_birth_time"] != 0 assert np.all(ad["io", "star_age"][whstars] > 0) # test semantics for non-cosmological new-style output format ds = yt.load(ramses_new_format) ad = ds.all_data() assert ("io", "particle_birth_time") in ds.field_list assert np.all(ad["io", "particle_birth_time"] > 0) # test semantics for non-cosmological old-style output format ds = yt.load(ramsesNonCosmo, extra_particle_fields=extra_particle_fields) ad = ds.all_data() assert ("io", "particle_birth_time") in ds.field_list # the dataset only includes particles with arbitrarily old ages # and particles that formed in the very first timestep assert np.all(ad["io", "particle_birth_time"] <= 0) whdynstars = ad["io", "particle_birth_time"] == 0 assert np.all(ad["io", "star_age"][whdynstars] == ds.current_time) @requires_file(ramses_new_format) def test_cooling_fields(): # Test the field is being loaded correctly ds = yt.load(ramses_new_format) # Derived cooling fields assert ("gas", "cooling_net") in ds.derived_field_list assert ("gas", "cooling_total") in ds.derived_field_list assert ("gas", "heating_total") in ds.derived_field_list assert ("gas", "number_density") in ds.derived_field_list # Original cooling fields assert ("gas", "cooling_primordial") in ds.derived_field_list assert ("gas", "cooling_compton") in ds.derived_field_list assert ("gas", "cooling_metal") in ds.derived_field_list assert ("gas", "heating_primordial") in ds.derived_field_list assert ("gas", "heating_compton") in ds.derived_field_list assert ("gas", "cooling_primordial_prime") in ds.derived_field_list assert ("gas", "cooling_compton_prime") in ds.derived_field_list assert ("gas", "cooling_metal_prime") in ds.derived_field_list assert ("gas", "heating_primordial_prime") in ds.derived_field_list assert ("gas", "heating_compton_prime") in ds.derived_field_list assert ("gas", "mu") in ds.derived_field_list # Abundances assert ("gas", "Electron_number_density") in ds.derived_field_list assert ("gas", "HI_number_density") in ds.derived_field_list assert ("gas", "HII_number_density") in ds.derived_field_list assert ("gas", "HeI_number_density") in ds.derived_field_list assert ("gas", "HeII_number_density") in ds.derived_field_list assert ("gas", "HeIII_number_density") in ds.derived_field_list def check_unit(array, unit): assert str(array.in_cgs().units) == unit check_unit(ds.r["gas", "cooling_total"], "cm**5*g/s**3") check_unit(ds.r["gas", "cooling_primordial_prime"], "cm**5*g/(K*s**3)") check_unit(ds.r["gas", "number_density"], "cm**(-3)") check_unit(ds.r["gas", "mu"], "dimensionless") check_unit(ds.r["gas", "Electron_number_density"], "cm**(-3)") @requires_file(ramses_rt) def test_ramses_mixed_files(): # Test that one can use derived fields that depend on different # files (here hydro and rt files) ds = yt.load(ramses_rt) def _mixed_field(field, data): return data["rt", "photon_density_1"] / data["gas", "H_nuclei_density"] ds.add_field(("gas", "mixed_files"), function=_mixed_field, sampling_type="cell") # Access the field ds.r["gas", "mixed_files"] ramses_empty_record = "ramses_empty_record/output_00003/info_00003.txt" @requires_file(ramses_empty_record) def test_ramses_empty_record(): # Test that yt can load datasets with empty records ds = yt.load(ramses_empty_record) # This should not fail ds.index # Access some field ds.r["gas", "density"] @requires_file(ramses_new_format) @requires_module("f90nml") def test_namelist_reading(): ds = data_dir_load(ramses_new_format) namelist_fname = os.path.join(ds.directory, "namelist.txt") with open(namelist_fname) as f: ref = f90nml.read(f) nml = ds.parameters["namelist"] assert nml == ref @requires_file(ramses_empty_record) @requires_file(output_00080) @requires_module("f90nml") def test_namelist_reading_should_not_fail(): for ds_name in (ramses_empty_record, output_00080): # Test that the reading does not fail for malformed namelist.txt files ds = data_dir_load(ds_name) ds.index # should work ramses_mhd_128 = "ramses_mhd_128/output_00027/info_00027.txt" @requires_file(ramses_mhd_128) def test_magnetic_field_aliasing(): # Test if RAMSES magnetic fields are correctly aliased to yt magnetic fields and if # derived magnetic quantities are calculated ds = data_dir_load(ramses_mhd_128) ad = ds.all_data() for field in [ "magnetic_field_x", "magnetic_field_magnitude", "alfven_speed", "magnetic_field_divergence", ]: assert ("gas", field) in ds.derived_field_list ad["gas", field] @requires_file(output_00080) def test_field_accession(): ds = yt.load(output_00080) fields = [ ("gas", "density"), # basic ones ("gas", "pressure"), ("gas", "pressure_gradient_magnitude"), # requires ghost zones ] # Check accessing gradient works for a variety of spatial domains for reg in ( ds.all_data(), ds.sphere([0.1] * 3, 0.01), ds.sphere([0.5] * 3, 0.05), ds.box([0.1] * 3, [0.2] * 3), ): for field in fields: reg[field] @requires_file(output_00080) def test_ghost_zones(): ds = yt.load(output_00080) def gen_dummy(ngz): def dummy(field, data): if not isinstance(data, FieldDetector): shape = data["gas", "mach_number"].shape[:3] np.testing.assert_equal(shape, (2 + 2 * ngz, 2 + 2 * ngz, 2 + 2 * ngz)) return data["gas", "mach_number"] return dummy fields = [] for ngz in (1, 2, 3): fname = ("gas", f"density_ghost_zone_{ngz}") ds.add_field( fname, gen_dummy(ngz), sampling_type="cell", units="", validators=[yt.ValidateSpatial(ghost_zones=ngz)], ) fields.append(fname) box = ds.box([0, 0, 0], [0.1, 0.1, 0.1]) for f in fields: print("Getting ", f) box[f] @requires_file(output_00080) def test_max_level(): ds = yt.load(output_00080) assert any(ds.r["index", "grid_level"] > 2) # Should work for ds in ( yt.load(output_00080, max_level=2, max_level_convention="yt"), yt.load(output_00080, max_level=8, max_level_convention="ramses"), ): assert all(ds.r["index", "grid_level"] <= 2) assert any(ds.r["index", "grid_level"] == 2) @requires_file(output_00080) def test_invalid_max_level(): invalid_value_args = ( (1, None), (1, "foo"), (1, "bar"), (-1, "yt"), ) for lvl, convention in invalid_value_args: with assert_raises(ValueError): yt.load(output_00080, max_level=lvl, max_level_convention=convention) invalid_type_args = ( (1.0, "yt"), # not an int ("invalid", "yt"), ) # Should fail with value errors for lvl, convention in invalid_type_args: with assert_raises(TypeError): yt.load(output_00080, max_level=lvl, max_level_convention=convention) @requires_file(ramses_new_format) def test_print_stats(): ds = yt.load(ramses_new_format) # Should work ds.print_stats() # FIXME #3197: use `capsys` with pytest to make sure the print_stats function works as intended @requires_file(output_00080) def test_reading_order(): # This checks the bug unvovered in #4880 # This checks that the result of field accession doesn't # depend on the order def _dummy_field(field, data): # Note: this is a dummy field # that doesn't really have any physical meaning # but may trigger some bug in the field # handling. T = data["gas", "temperature"] Z = data["gas", "metallicity"] return T * 1**Z fields = [ "Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "Metallicity", ] ds = yt.load(output_00080, fields=fields) ds.add_field( ("gas", "test"), function=_dummy_field, units=None, sampling_type="cell" ) ad = ds.all_data() v0 = ad["gas", "test"] ad = ds.all_data() ad["gas", "temperature"] v1 = ad["gas", "test"] np.testing.assert_allclose(v0, v1) # Simple context manager that overrides the value of # the self_shielding flag in the namelist.txt file @contextmanager def override_self_shielding(root: Path, section: str, val: bool): # Copy content of root in a temporary folder with TemporaryDirectory() as tmp: tmpdir = Path(tmp) / root.name tmpdir.mkdir() # Copy content of `root` in `tmpdir` recursively copytree(root, tmpdir, dirs_exist_ok=True) fname = Path(tmpdir) / "namelist.txt" with open(fname) as f: namelist_data = f90nml.read(f).todict() sec = namelist_data.get(section, {}) sec["self_shielding"] = val namelist_data[section] = sec with open(fname, "w") as f: new_nml = f90nml.Namelist(namelist_data) new_nml.write(f) yield tmpdir @requires_file(ramses_new_format) @requires_module("f90nml") def test_self_shielding_logic(): ################################################## # Check that the self_shielding flag is correctly read ds = yt.load(ramses_new_format) assert ds.parameters["namelist"] is not None assert ds.self_shielding is False ################################################## # Modify the namelist in-situ, reload and check root_folder = Path(ds.root_folder) for section in ("physics_params", "cooling_params"): for val in (True, False): with override_self_shielding(root_folder, section, val) as tmp_output: # Autodetection should work ds = yt.load(tmp_output) assert ds.parameters["namelist"] is not None assert ds.self_shielding is val # Manually set should ignore the namelist ds = yt.load(tmp_output, self_shielding=True) assert ds.self_shielding is True ds = yt.load(tmp_output, self_shielding=False) assert ds.self_shielding is False ################################################## # Manually set should work, even if namelist does not contain the flag ds = yt.load(ramses_new_format, self_shielding=True) assert ds.self_shielding is True ds = yt.load(ramses_new_format, self_shielding=False) assert ds.self_shielding is False ramses_star_formation = "ramses_star_formation/output_00013" @requires_file(ramses_star_formation) @requires_module("f90nml") def test_self_shielding_loading(): ds_off = yt.load(ramses_star_formation, self_shielding=False) ds_on = yt.load(ramses_star_formation, self_shielding=True) # We do not expect any significant change at densities << 1e-2 reg_on = ds_on.all_data().cut_region( ["obj['gas', 'density'].to('mp/cm**3') < 1e-6"] ) reg_off = ds_off.all_data().cut_region( ["obj['gas', 'density'].to('mp/cm**3') < 1e-6"] ) np.testing.assert_allclose( reg_on["gas", "cooling_total"], reg_off["gas", "cooling_total"], rtol=1e-5, ) # We expect large differences from 1e-2 mp/cc reg_on = ds_on.all_data().cut_region( ["obj['gas', 'density'].to('mp/cm**3') > 1e-2"] ) reg_off = ds_off.all_data().cut_region( ["obj['gas', 'density'].to('mp/cm**3') > 1e-2"] ) # Primordial cooling decreases with density (except at really high temperature) # so we expect the boosted version to cool *less* efficiently diff = ( reg_on["gas", "cooling_primordial"] - reg_off["gas", "cooling_primordial"] ) / reg_off["gas", "cooling_primordial"] assert (np.isclose(diff, 0, atol=1e-5) | (diff < 0)).all() # Also make sure the difference is large for some cells assert (np.abs(diff) > 0.1).any() @requires_file(output_00080) @requires_file(ramses_mhd_128) def test_order_does_not_matter(): for order in (1, 2): ds0 = yt.load(output_00080) ds1 = yt.load(ramses_mhd_128) # This should not raise any exception if order == 1: _sp1 = ds1.all_data() sp0 = ds0.all_data() else: sp0 = ds0.all_data() _sp1 = ds1.all_data() sp0["gas", "velocity_x"].max().to("km/s") yt-project-yt-f043ac8/yt/frontends/ramses/tests/test_outputs_pytest.py000066400000000000000000000040631510711153200265130ustar00rootroot00000000000000from copy import deepcopy import pytest import yt from yt.config import ytcfg from yt.testing import requires_file output_00080 = "output_00080/info_00080.txt" ramses_new_format = "ramses_new_format/output_00002/info_00002.txt" custom_hydro = [ "my-Density", "my-x-velocity", "my-y-velocity", "my-z-velocity", "my-Pressure", "my-Metallicity", ] custom_grav = [ "my-x-acceleration", "my-y-acceleration", "my-z-acceleration", ] @pytest.fixture() def custom_ramses_fields_conf(): old_config = deepcopy(ytcfg.config_root.as_dict()) ytcfg.add_section("ramses-hydro") ytcfg.add_section("ramses-grav") ytcfg.set("ramses-hydro", "fields", custom_hydro) ytcfg.set("ramses-grav", "fields", custom_grav) yield ytcfg.remove_section("ramses-hydro") ytcfg.remove_section("ramses-grav") ytcfg.update(old_config) @requires_file(output_00080) def test_field_config_1(custom_ramses_fields_conf): ds = yt.load(output_00080) for f in custom_hydro: assert ("ramses", f) in ds.field_list for f in custom_grav: assert ("gravity", f) in ds.field_list @requires_file(ramses_new_format) def test_field_config_2(custom_ramses_fields_conf): ds = yt.load(ramses_new_format) for f in custom_hydro: assert ("ramses", f) in ds.field_list for f in custom_grav: assert ("gravity", f) in ds.field_list @requires_file(output_00080) @requires_file(ramses_new_format) def test_warning_T2(): ds1 = yt.load(output_00080) ds2 = yt.load(ramses_new_format) # Should not raise warnings ds1.r["gas", "temperature_over_mu"] ds2.r["gas", "temperature_over_mu"] # We cannot read the cooling tables of output_00080 # so this should raise a warning with pytest.warns( RuntimeWarning, match=( "Trying to calculate temperature but the cooling tables couldn't be " r"found or read\. yt will return T/µ instead of T.*" ), ): ds1.r["gas", "temperature"] # But this one should not ds2.r["gas", "temperature"] yt-project-yt-f043ac8/yt/frontends/rockstar/000077500000000000000000000000001510711153200211405ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/rockstar/__init__.py000066400000000000000000000000471510711153200232520ustar00rootroot00000000000000""" API for Rockstar frontend. """ yt-project-yt-f043ac8/yt/frontends/rockstar/api.py000066400000000000000000000002171510711153200222630ustar00rootroot00000000000000from . import tests from .data_structures import RockstarDataset from .fields import RockstarFieldInfo from .io import IOHandlerRockstarBinary yt-project-yt-f043ac8/yt/frontends/rockstar/data_structures.py000066400000000000000000000167701510711153200247410ustar00rootroot00000000000000import glob import os from functools import cached_property from typing import Any, Optional import numpy as np from yt.data_objects.static_output import ParticleDataset from yt.frontends.halo_catalog.data_structures import HaloCatalogFile from yt.funcs import setdefaultattr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities import fortran_utils as fpu from yt.utilities.cosmology import Cosmology from yt.utilities.exceptions import YTFieldNotFound from .definitions import header_dt from .fields import RockstarFieldInfo class RockstarBinaryFile(HaloCatalogFile): header: dict _position_offset: int _member_offset: int _Npart: "np.ndarray[Any, np.dtype[np.int64]]" _ids_halos: list[int] _file_size: int def __init__(self, ds, io, filename, file_id, range): with open(filename, "rb") as f: self.header = fpu.read_cattrs(f, header_dt, "=") self._position_offset = f.tell() pcount = self.header["num_halos"] halos = np.fromfile(f, dtype=io._halo_dt, count=pcount) self._member_offset = f.tell() self._ids_halos = list(halos["particle_identifier"]) self._Npart = halos["num_p"] f.seek(0, os.SEEK_END) self._file_size = f.tell() expected_end = self._member_offset + 8 * self._Npart.sum() if expected_end != self._file_size: raise RuntimeError( f"File size {self._file_size} does not match expected size {expected_end}." ) super().__init__(ds, io, filename, file_id, range) def _read_member( self, ihalo: int ) -> Optional["np.ndarray[Any, np.dtype[np.int64]]"]: if ihalo not in self._ids_halos: return None ind_halo = self._ids_halos.index(ihalo) ipos = self._member_offset + 8 * self._Npart[:ind_halo].sum() with open(self.filename, "rb") as f: f.seek(ipos, os.SEEK_SET) ids = np.fromfile(f, dtype=np.int64, count=self._Npart[ind_halo]) return ids def _read_particle_positions(self, ptype: str, f=None): """ Read all particle positions in this file. """ if f is None: close = True f = open(self.filename, "rb") else: close = False pcount = self.header["num_halos"] pos = np.empty((pcount, 3), dtype="float64") f.seek(self._position_offset, os.SEEK_SET) halos = np.fromfile(f, dtype=self.io._halo_dt, count=pcount) for i, ax in enumerate("xyz"): pos[:, i] = halos[f"particle_position_{ax}"].astype("float64") if close: f.close() return pos class RockstarIndex(ParticleIndex): def get_member(self, ihalo: int): for df in self.data_files: members = df._read_member(ihalo) if members is not None: return members raise RuntimeError(f"Could not find halo {ihalo} in any data file.") class RockstarDataset(ParticleDataset): _index_class = RockstarIndex _file_class = RockstarBinaryFile _field_info_class = RockstarFieldInfo _suffix = ".bin" def __init__( self, filename, dataset_type="rockstar_binary", units_override=None, unit_system="cgs", index_order=None, index_filename=None, ): super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, ) def _parse_parameter_file(self): with open(self.parameter_filename, "rb") as f: hvals = fpu.read_cattrs(f, header_dt) hvals.pop("unused") self.dimensionality = 3 self.refine_by = 2 prefix = ".".join(self.parameter_filename.rsplit(".", 2)[:-2]) self.filename_template = f"{prefix}.%(num)s{self._suffix}" self.file_count = len(glob.glob(prefix + ".*" + self._suffix)) # Now we can set up things we already know. self.cosmological_simulation = 1 self.current_redshift = (1.0 / hvals["scale"]) - 1.0 self.hubble_constant = hvals["h0"] self.omega_lambda = hvals["Ol"] self.omega_matter = hvals["Om"] cosmo = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmo.lookback_time(self.current_redshift, 1e6).in_units( "s" ) self._periodicity = (True, True, True) self.particle_types = "halos" self.particle_types_raw = "halos" self.domain_left_edge = np.array([0.0, 0.0, 0.0]) self.domain_right_edge = np.array([hvals["box_size"]] * 3) self.domain_dimensions = np.ones(3, "int32") self.parameters.update(hvals) def _set_code_unit_attributes(self): z = self.current_redshift setdefaultattr(self, "length_unit", self.quan(1.0 / (1.0 + z), "Mpc / h")) setdefaultattr(self, "mass_unit", self.quan(1.0, "Msun / h")) setdefaultattr(self, "velocity_unit", self.quan(1.0, "km / s")) setdefaultattr(self, "time_unit", self.length_unit / self.velocity_unit) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".bin"): return False try: with open(filename, mode="rb") as f: header = fpu.read_cattrs(f, header_dt) except OSError: return False else: return header["magic"] == 18077126535843729616 def halo(self, ptype, particle_identifier): return RockstarHaloContainer( ptype, particle_identifier, parent_ds=None, halo_ds=self, ) class RockstarHaloContainer: def __init__(self, ptype, particle_identifier, *, parent_ds, halo_ds): if ptype not in halo_ds.particle_types_raw: raise RuntimeError( f'Possible halo types are {halo_ds.particle_types_raw}, supplied "{ptype}".' ) self.ds = parent_ds self.halo_ds = halo_ds self.ptype = ptype self.particle_identifier = particle_identifier def __repr__(self): return f"{self.halo_ds}_{self.ptype}_{self.particle_identifier:09d}" def __getitem__(self, key): if isinstance(key, tuple): ptype, field = key else: ptype = self.ptype field = key data = { "mass": self.mass, "position": self.position, "velocity": self.velocity, "member_ids": self.member_ids, } if ptype == "halos" and field in data: return data[field] raise YTFieldNotFound((ptype, field), dataset=self.ds) @cached_property def ihalo(self): halo_id = self.particle_identifier halo_ids = list(self.halo_ds.r["halos", "particle_identifier"].astype("i8")) ihalo = halo_ids.index(halo_id) assert halo_ids[ihalo] == halo_id return ihalo @property def mass(self): return self.halo_ds.r["halos", "particle_mass"][self.ihalo] @property def position(self): return self.halo_ds.r["halos", "particle_position"][self.ihalo] @property def velocity(self): return self.halo_ds.r["halos", "particle_velocity"][self.ihalo] @property def member_ids(self): return self.halo_ds.index.get_member(self.particle_identifier) yt-project-yt-f043ac8/yt/frontends/rockstar/definitions.py000066400000000000000000000076641510711153200240420ustar00rootroot00000000000000from typing import Any import numpy as np BINARY_HEADER_SIZE = 256 header_dt = ( ("magic", 1, "Q"), ("snap", 1, "q"), ("chunk", 1, "q"), ("scale", 1, "f"), ("Om", 1, "f"), ("Ol", 1, "f"), ("h0", 1, "f"), ("bounds", 6, "f"), ("num_halos", 1, "q"), ("num_particles", 1, "q"), ("box_size", 1, "f"), ("particle_mass", 1, "f"), ("particle_type", 1, "q"), ("format_revision", 1, "i"), ("version", 12, "c"), ("unused", BINARY_HEADER_SIZE - 4 * 12 - 4 - 8 * 6 - 12, "c"), ) # Note the final field here, which is a field for min/max format revision in # which the field appears. KNOWN_REVISIONS: list[int] = [0, 1, 2] # using typing.Any here in lieu of numpy.typing.DTypeLike (should be backported for numpy < 1.20) HaloDataType = tuple[str, Any] | tuple[str, Any, tuple[int, int]] halo_dt: list[HaloDataType] = [ ("particle_identifier", np.int64), ("particle_position_x", np.float32), ("particle_position_y", np.float32), ("particle_position_z", np.float32), ("particle_mposition_x", np.float32, (0, 0)), ("particle_mposition_y", np.float32, (0, 0)), ("particle_mposition_z", np.float32, (0, 0)), ("particle_velocity_x", np.float32), ("particle_velocity_y", np.float32), ("particle_velocity_z", np.float32), ("particle_corevel_x", np.float32, (1, 100)), ("particle_corevel_y", np.float32, (1, 100)), ("particle_corevel_z", np.float32, (1, 100)), ("particle_bulkvel_x", np.float32), ("particle_bulkvel_y", np.float32), ("particle_bulkvel_z", np.float32), ("particle_mass", np.float32), ("virial_radius", np.float32), ("child_r", np.float32), ("vmax_r", np.float32), ("mgrav", np.float32), ("vmax", np.float32), ("rvmax", np.float32), ("rs", np.float32), ("klypin_rs", np.float32), ("vrms", np.float32), ("Jx", np.float32), ("Jy", np.float32), ("Jz", np.float32), ("energy", np.float32), ("spin", np.float32), ("alt_m1", np.float32), ("alt_m2", np.float32), ("alt_m3", np.float32), ("alt_m4", np.float32), ("Xoff", np.float32), ("Voff", np.float32), ("b_to_a", np.float32), ("c_to_a", np.float32), ("Ax", np.float32), ("Ay", np.float32), ("Az", np.float32), ("b_to_a2", np.float32, (1, 100)), ("c_to_a2", np.float32, (1, 100)), ("A2x", np.float32, (1, 100)), ("A2y", np.float32, (1, 100)), ("A2z", np.float32, (1, 100)), ("bullock_spin", np.float32), ("kin_to_pot", np.float32), ("m_pe_b", np.float32, (1, 100)), ("m_pe_d", np.float32, (1, 100)), ("num_p", np.int64), ("num_child_particles", np.int64), ("p_start", np.int64), ("desc", np.int64), ("flags", np.int64), ("n_core", np.int64), ("min_pos_err", np.float32), ("min_vel_err", np.float32), ("min_bulkvel_err", np.float32), ("type", np.int32, (2, 100)), ("sm", np.float32, (2, 100)), ("gas", np.float32, (2, 100)), ("bh", np.float32, (2, 100)), ("peak_density", np.float32, (2, 100)), ("av_density", np.float32, (2, 100)), ] # using typing.Any here in lieu of numpy.typing.DTypeLike (should be backported for numpy < 1.20) halo_dts_tmp: dict[int, list[HaloDataType]] = {} halo_dts: dict[int, np.dtype] = {} for rev in KNOWN_REVISIONS: halo_dts_tmp[rev] = [] for item in halo_dt: if len(item) == 2: halo_dts_tmp[rev].append(item) elif len(item) == 3: mi, ma = item[2] if (mi <= rev) and (rev <= ma): halo_dts_tmp[rev].append(item[:2]) halo_dts[rev] = np.dtype(halo_dts_tmp[rev], align=True) del halo_dts_tmp particle_dt = np.dtype( [ ("particle_identifier", np.int64), ("particle_position_x", np.float32), ("particle_position_y", np.float32), ("particle_position_z", np.float32), ("particle_velocity_x", np.float32), ("particle_velocity_y", np.float32), ("particle_velocity_z", np.float32), ] ) yt-project-yt-f043ac8/yt/frontends/rockstar/fields.py000066400000000000000000000055571510711153200227740ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer m_units = "Msun / h" # Msun / h p_units = "Mpccm / h" # Mpc / h comoving v_units = "km / s" # km /s phys, peculiar r_units = "kpccm / h" # kpc / h comoving class RockstarFieldInfo(FieldInfoContainer): known_particle_fields: KnownFieldsT = ( ("particle_identifier", ("", [], None)), ("particle_position_x", (p_units, [], None)), ("particle_position_y", (p_units, [], None)), ("particle_position_z", (p_units, [], None)), ("particle_velocity_x", (v_units, [], None)), ("particle_velocity_y", (v_units, [], None)), ("particle_velocity_z", (v_units, [], None)), ("particle_corevel_x", (v_units, [], None)), ("particle_corevel_y", (v_units, [], None)), ("particle_corevel_z", (v_units, [], None)), ("particle_bulkvel_x", (v_units, [], None)), ("particle_bulkvel_y", (v_units, [], None)), ("particle_bulkvel_z", (v_units, [], None)), ("particle_mass", (m_units, [], "Mass")), ("virial_radius", (r_units, [], "Radius")), ("child_r", (r_units, [], None)), ("vmax_r", (v_units, [], None)), # These fields I don't have good definitions for yet. ("mgrav", ("", [], None)), ("vmax", (v_units, [], "V_{max}")), ("rvmax", (v_units, [], None)), ("rs", (r_units, [], "R_s")), ("klypin_rs", (r_units, [], "Klypin R_s")), ("vrms", (v_units, [], "V_{rms}")), ("Jx", ("", [], "J_x")), ("Jy", ("", [], "J_y")), ("Jz", ("", [], "J_z")), ("energy", ("", [], None)), ("spin", ("", [], "Spin Parameter")), ("alt_m1", (m_units, [], None)), ("alt_m2", (m_units, [], None)), ("alt_m3", (m_units, [], None)), ("alt_m4", (m_units, [], None)), ("Xoff", ("", [], None)), ("Voff", ("", [], None)), ("b_to_a", ("", [], "Ellipsoidal b to a")), ("c_to_a", ("", [], "Ellipsoidal c to a")), ("Ax", ("", [], "A_x")), ("Ay", ("", [], "A_y")), ("Az", ("", [], "A_z")), ("b_to_a2", ("", [], None)), ("c_to_a2", ("", [], None)), ("A2x", ("", [], "A2_x")), ("A2y", ("", [], "A2_y")), ("A2z", ("", [], "A2_z")), ("bullock_spin", ("", [], "Bullock Spin Parameter")), ("kin_to_pot", ("", [], "Kinetic to Potential")), ("m_pe_b", ("", [], None)), ("m_pe_d", ("", [], None)), ("num_p", ("", [], "Number of Particles")), ("num_child_particles", ("", [], "Number of Child Particles")), ("p_start", ("", [], None)), ("desc", ("", [], None)), ("flags", ("", [], None)), ("n_core", ("", [], None)), ("min_pos_err", ("", [], None)), ("min_vel_err", ("", [], None)), ("min_bulkvel_err", ("", [], None)), ) yt-project-yt-f043ac8/yt/frontends/rockstar/io.py000066400000000000000000000061501510711153200221230ustar00rootroot00000000000000import os import numpy as np from yt.utilities.io_handler import BaseParticleIOHandler from .definitions import halo_dts class IOHandlerRockstarBinary(BaseParticleIOHandler): _dataset_type = "rockstar_binary" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._halo_dt = halo_dts[self.ds.parameters["format_revision"]] def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" ptype = "halos" for data_file in self._sorted_chunk_iterator(chunks): pcount = data_file.header["num_halos"] if pcount == 0: continue with open(data_file.filename, "rb") as f: pos = data_file._get_particle_positions(ptype, f=f) yield "halos", (pos[:, i] for i in range(3)), 0.0 def _read_particle_fields(self, chunks, ptf, selector): # Only support halo reading for now. assert len(ptf) == 1 assert list(ptf.keys())[0] == "halos" for data_file in self._sorted_chunk_iterator(chunks): si, ei = data_file.start, data_file.end pcount = data_file.header["num_halos"] if pcount == 0: continue with open(data_file.filename, "rb") as f: for ptype, field_list in sorted(ptf.items()): pos = data_file._get_particle_positions(ptype, f=f) x, y, z = (pos[:, i] for i in range(3)) mask = selector.select_points(x, y, z, 0.0) del x, y, z f.seek(data_file._position_offset, os.SEEK_SET) halos = np.fromfile(f, dtype=self._halo_dt, count=pcount) if mask is None: continue for field in field_list: data = halos[field][si:ei][mask].astype("float64") yield (ptype, field), data def _yield_coordinates(self, data_file): # Just does halos pcount = data_file.header["num_halos"] with open(data_file.filename, "rb") as f: f.seek(data_file._position_offset, os.SEEK_SET) halos = np.fromfile(f, dtype=self._halo_dt, count=pcount) pos = np.empty((halos.size, 3), dtype="float64") pos[:, 0] = halos["particle_position_x"] pos[:, 1] = halos["particle_position_y"] pos[:, 2] = halos["particle_position_z"] yield "halos", pos def _count_particles(self, data_file): nhalos = data_file.header["num_halos"] si, ei = data_file.start, data_file.end if None not in (si, ei): nhalos = np.clip(nhalos - si, 0, ei - si) return {"halos": nhalos} def _identify_fields(self, data_file): fields = [("halos", f) for f in self._halo_dt.fields if "padding" not in f] return fields, {} yt-project-yt-f043ac8/yt/frontends/rockstar/tests/000077500000000000000000000000001510711153200223025ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/rockstar/tests/__init__.py000066400000000000000000000000001510711153200244010ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/rockstar/tests/test_outputs.py000066400000000000000000000026141510711153200254410ustar00rootroot00000000000000import os.path from numpy.testing import assert_equal from yt.frontends.rockstar.api import RockstarDataset from yt.testing import ParticleSelectionComparison, requires_file from yt.utilities.answer_testing.framework import ( FieldValuesTest, data_dir_load, requires_ds, ) _fields = ( ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_position_z"), ("all", "particle_mass"), ) r1 = "rockstar_halos/halos_0.0.bin" @requires_ds(r1) def test_fields_r1(): ds = data_dir_load(r1) assert_equal(str(ds), os.path.basename(r1)) for field in _fields: yield FieldValuesTest(r1, field, particle_type=True) @requires_file(r1) def test_RockstarDataset(): assert isinstance(data_dir_load(r1), RockstarDataset) @requires_file(r1) def test_particle_selection(): ds = data_dir_load(r1) psc = ParticleSelectionComparison(ds) psc.run_defaults() @requires_file(r1) def test_halo_loading(): ds = data_dir_load(r1) for halo_id, Npart in zip( ds.r["halos", "particle_identifier"], ds.r["halos", "num_p"], strict=False, ): halo = ds.halo("halos", halo_id) assert halo is not None # Try accessing properties halo.position halo.velocity halo.mass # Make sure we can access the member particles assert_equal(len(halo.member_ids), Npart) yt-project-yt-f043ac8/yt/frontends/sdf/000077500000000000000000000000001510711153200200645ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/sdf/__init__.py000066400000000000000000000000521510711153200221720ustar00rootroot00000000000000""" __init__ for yt.frontends.sdf """ yt-project-yt-f043ac8/yt/frontends/sdf/api.py000066400000000000000000000001461510711153200212100ustar00rootroot00000000000000from .data_structures import SDFDataset from .fields import SDFFieldInfo from .io import IOHandlerSDF yt-project-yt-f043ac8/yt/frontends/sdf/data_structures.py000066400000000000000000000154571510711153200236660ustar00rootroot00000000000000import os from functools import cached_property import numpy as np from yt.data_objects.static_output import ParticleDataset, ParticleFile from yt.funcs import setdefaultattr from yt.geometry.particle_geometry_handler import ParticleIndex from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _requests as requests from yt.utilities.sdf import HTTPSDFRead, SDFIndex, SDFRead from .fields import SDFFieldInfo # currently specified by units_2HOT == 2 in header # in future will read directly from file units_2HOT_v2_length = 3.08567802e21 units_2HOT_v2_mass = 1.98892e43 units_2HOT_v2_time = 3.1558149984e16 class SDFFile(ParticleFile): pass class SDFDataset(ParticleDataset): _load_requirements = ["requests"] _index_class = ParticleIndex _file_class = SDFFile _field_info_class = SDFFieldInfo _particle_mass_name = None _particle_coordinates_name = None _particle_velocity_name = None _skip_cache = True _subspace = False def __init__( self, filename, dataset_type="sdf_particles", index_order=None, index_filename=None, bounding_box=None, sdf_header=None, midx_filename=None, midx_header=None, midx_level=None, field_map=None, units_override=None, unit_system="cgs", ): if bounding_box is not None: # This ensures that we know a bounding box has been applied self._domain_override = True self._subspace = True bbox = np.array(bounding_box, dtype="float64") if bbox.shape == (2, 3): bbox = bbox.transpose() self.domain_left_edge = bbox[:, 0] self.domain_right_edge = bbox[:, 1] else: self.domain_left_edge = self.domain_right_edge = None self.sdf_header = sdf_header self.midx_filename = midx_filename self.midx_header = midx_header self.midx_level = midx_level if field_map is None: field_map = {} self._field_map = field_map prefix = "" if self.midx_filename is not None: prefix += "midx_" if filename.startswith("http"): prefix += "http_" dataset_type = prefix + "sdf_particles" super().__init__( filename, dataset_type=dataset_type, units_override=units_override, unit_system=unit_system, index_order=index_order, index_filename=index_filename, ) def _parse_parameter_file(self): if self.parameter_filename.startswith("http"): sdf_class = HTTPSDFRead else: sdf_class = SDFRead self.sdf_container = sdf_class(self.parameter_filename, header=self.sdf_header) # Reference self.parameters = self.sdf_container.parameters self.dimensionality = 3 self.refine_by = 2 if self.domain_left_edge is None or self.domain_right_edge is None: R0 = self.parameters["R0"] if "offset_center" in self.parameters and self.parameters["offset_center"]: self.domain_left_edge = np.array([0, 0, 0], dtype=np.float64) self.domain_right_edge = np.array( [2.0 * self.parameters.get(f"R{ax}", R0) for ax in "xyz"], dtype=np.float64, ) else: self.domain_left_edge = np.array( [-self.parameters.get(f"R{ax}", R0) for ax in "xyz"], dtype=np.float64, ) self.domain_right_edge = np.array( [+self.parameters.get(f"R{ax}", R0) for ax in "xyz"], dtype=np.float64, ) self.domain_left_edge *= self.parameters.get("a", 1.0) self.domain_right_edge *= self.parameters.get("a", 1.0) self.domain_dimensions = np.ones(3, "int32") if self.parameters.get("do_periodic", False): self._periodicity = (True, True, True) else: self._periodicity = (False, False, False) self.cosmological_simulation = 1 self.current_redshift = self.parameters.get("redshift", 0.0) self.omega_lambda = self.parameters["Omega0_lambda"] self.omega_matter = self.parameters["Omega0_m"] if "Omega0_fld" in self.parameters: self.omega_lambda += self.parameters["Omega0_fld"] if "Omega0_r" in self.parameters: # not correct, but most codes can't handle Omega0_r self.omega_matter += self.parameters["Omega0_r"] self.hubble_constant = self.parameters["h_100"] self.current_time = units_2HOT_v2_time * self.parameters.get("tpos", 0.0) mylog.info("Calculating time to be %0.3e seconds", self.current_time) self.filename_template = self.parameter_filename self.file_count = 1 @cached_property def midx(self): if self.midx_filename is None: raise RuntimeError("SDF index0 file not supplied in load.") if "http" in self.midx_filename: sdf_class = HTTPSDFRead else: sdf_class = SDFRead indexdata = sdf_class(self.midx_filename, header=self.midx_header) return SDFIndex(self.sdf_container, indexdata, level=self.midx_level) def _set_code_unit_attributes(self): setdefaultattr( self, "length_unit", self.quan(1.0, self.parameters.get("length_unit", "kpc")), ) setdefaultattr( self, "velocity_unit", self.quan(1.0, self.parameters.get("velocity_unit", "kpc/Gyr")), ) setdefaultattr( self, "time_unit", self.quan(1.0, self.parameters.get("time_unit", "Gyr")) ) mass_unit = self.parameters.get("mass_unit", "1e10 Msun") if " " in mass_unit: factor, unit = mass_unit.split(" ") else: factor = 1.0 unit = mass_unit setdefaultattr(self, "mass_unit", self.quan(float(factor), unit)) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if cls._missing_load_requirements(): return False sdf_header = kwargs.get("sdf_header", filename) if sdf_header.startswith("http"): hreq = requests.get(sdf_header, stream=True) if hreq.status_code != 200: return False # Grab a whole 4k page. line = next(hreq.iter_content(4096)) elif os.path.isfile(sdf_header): try: with open(sdf_header, encoding="ISO-8859-1") as f: line = f.read(10).strip() except PermissionError: return False else: return False return line.startswith("# SDF") yt-project-yt-f043ac8/yt/frontends/sdf/definitions.py000066400000000000000000000000001510711153200227370ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/sdf/fields.py000066400000000000000000000031541510711153200217070ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer class SDFFieldInfo(FieldInfoContainer): known_other_fields = () known_particle_fields = () _mass_field = None def __init__(self, ds, field_list): if "mass" in field_list: self.known_particle_fields.append( ("mass", "code_mass", ["particle_mass"], None) ) possible_masses = ["mass", "m200b", "mvir"] mnf = "mass" for mn in possible_masses: if mn in ds.sdf_container.keys(): mnf = self._mass_field = mn break idf = ds._field_map.get("particle_index", "ident") xf = ds._field_map.get("particle_position_x", "x") yf = ds._field_map.get("particle_position_y", "y") zf = ds._field_map.get("particle_position_z", "z") vxf = ds._field_map.get("particle_velocity_x", "vx") vyf = ds._field_map.get("particle_velocity_z", "vy") vzf = ds._field_map.get("particle_velocity_z", "vz") self.known_particle_fields = ( (idf, ("dimensionless", ["particle_index"], None)), (xf, ("code_length", ["particle_position_x"], None)), (yf, ("code_length", ["particle_position_y"], None)), (zf, ("code_length", ["particle_position_z"], None)), (vxf, ("code_velocity", ["particle_velocity_x"], None)), (vyf, ("code_velocity", ["particle_velocity_y"], None)), (vzf, ("code_velocity", ["particle_velocity_z"], None)), (mnf, ("code_mass", ["particle_mass"], None)), ) super().__init__(ds, field_list) yt-project-yt-f043ac8/yt/frontends/sdf/io.py000066400000000000000000000160671510711153200210570ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.utilities.io_handler import BaseParticleIOHandler class IOHandlerSDF(BaseParticleIOHandler): _dataset_type = "sdf_particles" @property def _handle(self): return self.ds.sdf_container def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _read_particle_coords(self, chunks, ptf): assert len(ptf) == 1 assert ptf.keys()[0] == "dark_matter" data_files = self._get_data_files(chunks) assert len(data_files) == 1 for _data_file in sorted(data_files, key=lambda x: (x.filename, x.start)): yield ( "dark_matter", ( self._handle["x"], self._handle["y"], self._handle["z"], ), 0.0, ) def _read_particle_fields(self, chunks, ptf, selector): assert len(ptf) == 1 assert ptf.keys()[0] == "dark_matter" data_files = self._get_data_files(chunks) assert len(data_files) == 1 for _data_file in sorted(data_files, key=lambda x: (x.filename, x.start)): for ptype, field_list in sorted(ptf.items()): x = self._handle["x"] y = self._handle["y"] z = self._handle["z"] mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: if field == "mass": data = np.ones(mask.sum(), dtype="float64") data *= self.ds.parameters["particle_mass"] else: data = self._handle[field][mask] yield (ptype, field), data def _identify_fields(self, data_file): fields = [("dark_matter", v) for v in self._handle.keys()] fields.append(("dark_matter", "mass")) return fields, {} def _count_particles(self, data_file): pcount = self._handle["x"].size if pcount > 1e9: mylog.warning( "About to load %i particles into memory. " "You may want to consider a midx-enabled load", pcount, ) return {"dark_matter": pcount} class IOHandlerHTTPSDF(IOHandlerSDF): _dataset_type = "http_sdf_particles" def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) data_files = set() assert len(ptf) == 1 assert ptf.keys()[0] == "dark_matter" for chunk in chunks: for obj in chunk.objs: data_files.update(obj.data_files) assert len(data_files) == 1 for _data_file in data_files: pcount = self._handle["x"].size yield ( "dark_matter", ( self._handle["x"][:pcount], self._handle["y"][:pcount], self._handle["z"][:pcount], ), 0.0, ) def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) data_files = set() assert len(ptf) == 1 assert ptf.keys()[0] == "dark_matter" for chunk in chunks: for obj in chunk.objs: data_files.update(obj.data_files) assert len(data_files) == 1 for _data_file in data_files: pcount = self._handle["x"].size for ptype, field_list in sorted(ptf.items()): x = self._handle["x"][:pcount] y = self._handle["y"][:pcount] z = self._handle["z"][:pcount] mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: if field == "mass": if self.ds.field_info._mass_field is None: pm = 1.0 if "particle_mass" in self.ds.parameters: pm = self.ds.parameters["particle_mass"] else: raise RuntimeError data = pm * np.ones(mask.sum(), dtype="float64") else: data = self._handle[self.ds.field_info._mass_field][:][mask] else: data = self._handle[field][:][mask] yield (ptype, field), data def _count_particles(self, data_file): return {"dark_matter": self._handle["x"].http_array.shape} class IOHandlerSIndexSDF(IOHandlerSDF): _dataset_type = "midx_sdf_particles" def _read_particle_coords(self, chunks, ptf): dle = self.ds.domain_left_edge.in_units("code_length").d dre = self.ds.domain_right_edge.in_units("code_length").d for dd in self.ds.midx.iter_bbox_data(dle, dre, ["x", "y", "z"]): yield "dark_matter", (dd["x"], dd["y"], dd["z"]), 0.0 def _read_particle_fields(self, chunks, ptf, selector): dle = self.ds.domain_left_edge.in_units("code_length").d dre = self.ds.domain_right_edge.in_units("code_length").d required_fields = [] for field_list in sorted(ptf.values()): for field in field_list: if field == "mass": continue required_fields.append(field) for dd in self.ds.midx.iter_bbox_data(dle, dre, required_fields): for ptype, field_list in sorted(ptf.items()): x = dd["x"] y = dd["y"] z = dd["z"] mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: if field == "mass": data = np.ones(mask.sum(), dtype="float64") data *= self.ds.parameters["particle_mass"] else: data = dd[field][mask] yield (ptype, field), data def _count_particles(self, data_file): dle = self.ds.domain_left_edge.in_units("code_length").d dre = self.ds.domain_right_edge.in_units("code_length").d pcount_estimate = self.ds.midx.get_nparticles_bbox(dle, dre) if pcount_estimate > 1e9: mylog.warning( "Filtering %i particles to find total. " "You may want to reconsider your bounding box.", pcount_estimate, ) pcount = 0 for dd in self.ds.midx.iter_bbox_data(dle, dre, ["x"]): pcount += dd["x"].size return {"dark_matter": pcount} def _identify_fields(self, data_file): fields = [("dark_matter", v) for v in self._handle.keys()] fields.append(("dark_matter", "mass")) return fields, {} class IOHandlerSIndexHTTPSDF(IOHandlerSIndexSDF): _dataset_type = "midx_http_sdf_particles" yt-project-yt-f043ac8/yt/frontends/sdf/misc.py000066400000000000000000000000001510711153200213570ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/sph/000077500000000000000000000000001510711153200201025ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/sph/__init__.py000066400000000000000000000000001510711153200222010ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/sph/api.py000066400000000000000000000000631510711153200212240ustar00rootroot00000000000000""" API for general SPH frontend machinery """ yt-project-yt-f043ac8/yt/frontends/sph/data_structures.py000066400000000000000000000106531510711153200236750ustar00rootroot00000000000000import os import numpy as np from yt.data_objects.static_output import ParticleDataset from yt.funcs import mylog from yt.geometry.particle_geometry_handler import ParticleIndex class SPHDataset(ParticleDataset): default_kernel_name = "cubic" _sph_smoothing_styles = ["scatter", "gather"] _sph_smoothing_style = "scatter" _num_neighbors = 32 _use_sph_normalization = True def __init__( self, filename, dataset_type=None, units_override=None, unit_system="cgs", index_order=None, index_filename=None, kdtree_filename=None, kernel_name=None, default_species_fields=None, ): if kernel_name is None: self.kernel_name = self.default_kernel_name else: self.kernel_name = kernel_name self.kdtree_filename = kdtree_filename super().__init__( filename, dataset_type=dataset_type, units_override=units_override, unit_system=unit_system, index_order=index_order, index_filename=index_filename, default_species_fields=default_species_fields, ) @property def num_neighbors(self): return self._num_neighbors @num_neighbors.setter def num_neighbors(self, value): if value < 0: raise ValueError(f"Negative value not allowed: {value}") self._num_neighbors = value @property def sph_smoothing_style(self): return self._sph_smoothing_style @sph_smoothing_style.setter def sph_smoothing_style(self, value): if value not in self._sph_smoothing_styles: raise ValueError( f"Smoothing style not implemented: {value}, " "please select one of the following: ", self._sph_smoothing_styles, ) self._sph_smoothing_style = value @property def use_sph_normalization(self): return self._use_sph_normalization @use_sph_normalization.setter def use_sph_normalization(self, value): if value is not True and value is not False: raise ValueError("SPH normalization needs to be True or False!") self._use_sph_normalization = value class SPHParticleIndex(ParticleIndex): def _initialize_index(self): ds = self.dataset ds._file_hash = self._generate_hash() if hasattr(self.io, "_generate_smoothing_length"): self.io._generate_smoothing_length(self) super()._initialize_index() def _generate_kdtree(self, fname): from yt.utilities.lib.cykdtree import PyKDTree if fname is not None: if os.path.exists(fname): mylog.info("Loading KDTree from %s", os.path.basename(fname)) kdtree = PyKDTree.from_file(fname) if kdtree.data_version != self.ds._file_hash: mylog.info("Detected hash mismatch, regenerating KDTree") else: self._kdtree = kdtree return positions = [] for data_file in self.data_files: for _, ppos in self.io._yield_coordinates( data_file, needed_ptype=self.ds._sph_ptypes[0] ): positions.append(ppos) if positions == []: self._kdtree = None return positions = np.concatenate(positions) mylog.info("Allocating KDTree for %s particles", positions.shape[0]) num_neighbors = getattr(self.ds, "num_neighbors", 32) self._kdtree = PyKDTree( positions.astype("float64"), left_edge=self.ds.domain_left_edge, right_edge=self.ds.domain_right_edge, periodic=np.array(self.ds.periodicity), leafsize=2 * int(num_neighbors), data_version=self.ds._file_hash, ) if fname is not None: self._kdtree.save(fname) @property def kdtree(self): if hasattr(self, "_kdtree"): return self._kdtree ds = self.ds if getattr(ds, "kdtree_filename", None) is None: if os.path.exists(ds.parameter_filename): fname = ds.parameter_filename + ".kdtree" else: # we don't want to write to disk for in-memory data fname = None else: fname = ds.kdtree_filename self._generate_kdtree(fname) return self._kdtree yt-project-yt-f043ac8/yt/frontends/sph/fields.py000066400000000000000000000041221510711153200217210ustar00rootroot00000000000000from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer from yt.fields.species_fields import setup_species_fields class SPHFieldInfo(FieldInfoContainer): known_particle_fields: KnownFieldsT = ( ("Mass", ("code_mass", ["particle_mass"], None)), ("Masses", ("code_mass", ["particle_mass"], None)), ("Coordinates", ("code_length", ["particle_position"], None)), ("Velocity", ("code_velocity", ["particle_velocity"], None)), ("Velocities", ("code_velocity", ["particle_velocity"], None)), ("ParticleIDs", ("", ["particle_index"], None)), ("InternalEnergy", ("code_specific_energy", ["specific_thermal_energy"], None)), ("SmoothingLength", ("code_length", ["smoothing_length"], None)), ("Density", ("code_mass / code_length**3", ["density"], None)), ("MaximumTemperature", ("K", [], None)), ("Temperature", ("K", ["temperature"], None)), ("Epsilon", ("code_length", [], None)), ("Metals", ("code_metallicity", ["metallicity"], None)), ("Metallicity", ("code_metallicity", ["metallicity"], None)), ("Phi", ("code_length", [], None)), ("Potential", ("code_velocity**2", ["gravitational_potential"], None)), ("StarFormationRate", ("Msun / yr", ["star_formation_rate"], None)), ("FormationTime", ("code_time", ["creation_time"], None)), ("Metallicity_00", ("", ["metallicity"], None)), ("InitialMass", ("code_mass", [], None)), ("TrueMass", ("code_mass", [], None)), ("ElevenMetalMasses", ("code_mass", [], None)), ("ColdFraction", ("", ["cold_fraction"], None)), ("HotTemperature", ("code_temperature", ["hot_temperature"], None)), ("CloudFraction", ("", ["cold_fraction"], None)), ("HotPhaseTemperature", ("code_temperature", ["hot_temperature"], None)), ) def setup_particle_fields(self, ptype, *args, **kwargs): super().setup_particle_fields(ptype, *args, **kwargs) setup_species_fields(self, ptype) def setup_fluid_index_fields(self): pass yt-project-yt-f043ac8/yt/frontends/sph/io.py000066400000000000000000000007161510711153200210670ustar00rootroot00000000000000""" Generic file-handing functions for SPH data """ from yt.utilities.io_handler import BaseParticleIOHandler class IOHandlerSPH(BaseParticleIOHandler): """IOHandler implementation specifically for SPH data This exists to handle particles with smoothing lengths, which require us to read in smoothing lengths along with the the particle coordinates to determine particle extents. At present this is non-functional. """ pass yt-project-yt-f043ac8/yt/frontends/stream/000077500000000000000000000000001510711153200206035ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/stream/__init__.py000066400000000000000000000000001510711153200227020ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/stream/api.py000066400000000000000000000003561510711153200217320ustar00rootroot00000000000000from . import sample_data, tests from .data_structures import ( StreamDataset, StreamGrid, StreamHandler, StreamHierarchy, hexahedral_connectivity, ) from .fields import StreamFieldInfo from .io import IOHandlerStream yt-project-yt-f043ac8/yt/frontends/stream/data_structures.py000066400000000000000000001052111510711153200243710ustar00rootroot00000000000000import os import time import uuid import weakref from collections import UserDict from functools import cached_property from itertools import chain, repeat from numbers import Number as numeric_type import numpy as np from more_itertools import always_iterable from yt._typing import AxisOrder, FieldKey from yt.data_objects.field_data import YTFieldData from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.index_subobjects.octree_subset import OctreeSubset from yt.data_objects.index_subobjects.stretched_grid import StretchedGrid from yt.data_objects.index_subobjects.unstructured_mesh import ( SemiStructuredMesh, UnstructuredMesh, ) from yt.data_objects.static_output import Dataset, ParticleFile from yt.data_objects.unions import MeshUnion, ParticleUnion from yt.frontends.sph.data_structures import SPHParticleIndex from yt.funcs import setdefaultattr from yt.geometry.api import Geometry from yt.geometry.geometry_handler import Index, YTDataChunk from yt.geometry.grid_geometry_handler import GridIndex from yt.geometry.oct_container import OctreeContainer from yt.geometry.oct_geometry_handler import OctreeIndex from yt.geometry.unstructured_mesh_handler import UnstructuredIndex from yt.units import YTQuantity from yt.utilities.io_handler import io_registry from yt.utilities.lib.cykdtree import PyKDTree from yt.utilities.lib.misc_utilities import ( _obtain_coords_and_widths, get_box_grids_level, ) from yt.utilities.lib.particle_kdtree_tools import ( estimate_density, generate_smoothing_length, ) from yt.utilities.logger import ytLogger as mylog from .definitions import process_data, set_particle_types from .fields import StreamFieldInfo class StreamGrid(AMRGridPatch): """ Class representing a single In-memory Grid instance. """ __slots__ = ["proc_num"] _id_offset = 0 def __init__(self, id, index): """ Returns an instance of StreamGrid with *id*, associated with *filename* and *index*. """ # All of the field parameters will be passed to us as needed. AMRGridPatch.__init__(self, id, filename=None, index=index) self._children_ids = [] self._parent_id = -1 self.Level = -1 def set_filename(self, filename): pass @property def Parent(self): if self._parent_id == -1: return None return self.index.grids[self._parent_id - self._id_offset] @property def Children(self): return [self.index.grids[cid - self._id_offset] for cid in self._children_ids] class StreamStretchedGrid(StretchedGrid): _id_offset = 0 def __init__(self, id, index): cell_widths = index.grid_cell_widths[id - self._id_offset] super().__init__(id, cell_widths, index=index) self._children_ids = [] self._parent_id = -1 self.Level = -1 @property def Parent(self): if self._parent_id == -1: return None return self.index.grids[self._parent_id - self._id_offset] @property def Children(self): return [self.index.grids[cid - self._id_offset] for cid in self._children_ids] class StreamHandler: def __init__( self, left_edges, right_edges, dimensions, levels, parent_ids, particle_count, processor_ids, fields, field_units, code_units, io=None, particle_types=None, periodicity=(True, True, True), *, cell_widths=None, parameters=None, ): if particle_types is None: particle_types = {} self.left_edges = np.array(left_edges) self.right_edges = np.array(right_edges) self.dimensions = dimensions self.levels = levels self.parent_ids = parent_ids self.particle_count = particle_count self.processor_ids = processor_ids self.num_grids = self.levels.size self.fields = fields self.field_units = field_units self.code_units = code_units self.io = io self.particle_types = particle_types self.periodicity = periodicity self.cell_widths = cell_widths if parameters is None: self.parameters = {} else: self.parameters = parameters.copy() def get_fields(self): return self.fields.all_fields def get_particle_type(self, field): if field in self.particle_types: return self.particle_types[field] else: return False class StreamHierarchy(GridIndex): grid = StreamGrid def __init__(self, ds, dataset_type=None): self.dataset_type = dataset_type self.float_type = "float64" self.dataset = weakref.proxy(ds) # for _obtain_enzo self.stream_handler = ds.stream_handler self.float_type = "float64" self.directory = os.getcwd() GridIndex.__init__(self, ds, dataset_type) def _count_grids(self): self.num_grids = self.stream_handler.num_grids def _icoords_to_fcoords(self, icoords, ires, axes=None): """ We check here that we have cell_widths, and if we do, we will provide them. """ if self.grid_cell_widths is None: return super()._icoords_to_fcoords(icoords, ires, axes) if axes is None: axes = [0, 1, 2] # Transpose these by reversing the shape coords = np.empty(icoords.shape, dtype="f8") cell_widths = np.empty(icoords.shape, dtype="f8") for i, ax in enumerate(axes): coords[:, i], cell_widths[:, i] = _obtain_coords_and_widths( icoords[:, i], ires, self.grid_cell_widths[0][ax], self.ds.domain_left_edge[ax].d, ) return coords, cell_widths def _parse_index(self): self.grid_dimensions = self.stream_handler.dimensions self.grid_left_edge[:] = self.stream_handler.left_edges self.grid_right_edge[:] = self.stream_handler.right_edges self.grid_levels[:] = self.stream_handler.levels self.min_level = self.grid_levels.min() self.grid_procs = self.stream_handler.processor_ids self.grid_particle_count[:] = self.stream_handler.particle_count if self.stream_handler.cell_widths is not None: self.grid_cell_widths = self.stream_handler.cell_widths[:] self.grid = StreamStretchedGrid else: self.grid_cell_widths = None mylog.debug("Copying reverse tree") self.grids = [] # We enumerate, so it's 0-indexed id and 1-indexed pid for id in range(self.num_grids): self.grids.append(self.grid(id, self)) self.grids[id].Level = self.grid_levels[id, 0] parent_ids = self.stream_handler.parent_ids if parent_ids is not None: reverse_tree = self.stream_handler.parent_ids.tolist() # Initial setup: for gid, pid in enumerate(reverse_tree): if pid >= 0: self.grids[gid]._parent_id = pid self.grids[pid]._children_ids.append(self.grids[gid].id) else: mylog.debug("Reconstructing parent-child relationships") self._reconstruct_parent_child() self.max_level = self.grid_levels.max() mylog.debug("Preparing grids") temp_grids = np.empty(self.num_grids, dtype="object") for i, grid in enumerate(self.grids): if (i % 1e4) == 0: mylog.debug("Prepared % 7i / % 7i grids", i, self.num_grids) grid.filename = None grid._prepare_grid() grid._setup_dx() grid.proc_num = self.grid_procs[i] temp_grids[i] = grid self.grids = temp_grids mylog.debug("Prepared") def _reconstruct_parent_child(self): mask = np.empty(len(self.grids), dtype="int32") mylog.debug("First pass; identifying child grids") for i, grid in enumerate(self.grids): get_box_grids_level( self.grid_left_edge[i, :], self.grid_right_edge[i, :], self.grid_levels[i].item() + 1, self.grid_left_edge, self.grid_right_edge, self.grid_levels, mask, ) ids = np.where(mask.astype("bool")) grid._children_ids = ids[0] # where is a tuple mylog.debug("Second pass; identifying parents") self.stream_handler.parent_ids = ( np.zeros(self.stream_handler.num_grids, "int64") - 1 ) for i, grid in enumerate(self.grids): # Second pass for child in grid.Children: child._parent_id = i # _id_offset = 0 self.stream_handler.parent_ids[child.id] = i def _initialize_grid_arrays(self): GridIndex._initialize_grid_arrays(self) self.grid_procs = np.zeros((self.num_grids, 1), "int32") def _detect_output_fields(self): # NOTE: Because particle unions add to the actual field list, without # having the keys in the field list itself, we need to double check # here. fl = set(self.stream_handler.get_fields()) fl.update(set(getattr(self, "field_list", []))) self.field_list = list(fl) def _populate_grid_objects(self): for g in self.grids: g._setup_dx() self.max_level = self.grid_levels.max() def _setup_data_io(self): if self.stream_handler.io is not None: self.io = self.stream_handler.io else: self.io = io_registry[self.dataset_type](self.ds) def _reset_particle_count(self): self.grid_particle_count[:] = self.stream_handler.particle_count for i, grid in enumerate(self.grids): grid.NumberOfParticles = self.grid_particle_count[i, 0] def update_data(self, data): """ Update the stream data with a new data dict. If fields already exist, they will be replaced, but if they do not, they will be added. Fields already in the stream but not part of the data dict will be left alone. """ particle_types = set_particle_types(data[0]) self.stream_handler.particle_types.update(particle_types) self.ds._find_particle_types() for i, grid in enumerate(self.grids): field_units, gdata, number_of_particles = process_data(data[i]) self.stream_handler.particle_count[i] = number_of_particles self.stream_handler.field_units.update(field_units) for field in gdata: if field in grid.field_data: grid.field_data.pop(field, None) self.stream_handler.fields[grid.id][field] = gdata[field] self._reset_particle_count() # We only want to create a superset of fields here. for field in self.ds.field_list: if field[0] == "all": self.ds.field_list.remove(field) self._detect_output_fields() self.ds.create_field_info() mylog.debug("Creating Particle Union 'all'") pu = ParticleUnion("all", list(self.ds.particle_types_raw)) self.ds.add_particle_union(pu) self.ds.particle_types = tuple(set(self.ds.particle_types)) class StreamDataset(Dataset): _index_class: type[Index] = StreamHierarchy _field_info_class = StreamFieldInfo _dataset_type = "stream" def __init__( self, stream_handler, storage_filename=None, geometry="cartesian", unit_system="cgs", default_species_fields=None, *, axis_order: AxisOrder | None = None, ): self.fluid_types += ("stream",) self.geometry = Geometry(geometry) self.stream_handler = stream_handler self._find_particle_types() name = f"InMemoryParameterFile_{uuid.uuid4().hex}" from yt.data_objects.static_output import _cached_datasets if geometry == "spectral_cube": # mimic FITSDataset specific interface to allow testing with # fake, in memory data setdefaultattr(self, "lon_axis", 0) setdefaultattr(self, "lat_axis", 1) setdefaultattr(self, "spec_axis", 2) setdefaultattr(self, "lon_name", "X") setdefaultattr(self, "lat_name", "Y") setdefaultattr(self, "spec_name", "z") setdefaultattr(self, "spec_unit", "") setdefaultattr( self, "pixel2spec", lambda pixel_value: self.arr(pixel_value, self.spec_unit), # type: ignore [attr-defined] ) setdefaultattr( self, "spec2pixel", lambda spec_value: self.arr(spec_value, "code_length"), ) _cached_datasets[name] = self Dataset.__init__( self, name, self._dataset_type, unit_system=unit_system, default_species_fields=default_species_fields, axis_order=axis_order, ) @property def filename(self): return self.stream_handler.name @cached_property def unique_identifier(self) -> str: return str(self.parameters["CurrentTimeIdentifier"]) def _parse_parameter_file(self): self.parameters["CurrentTimeIdentifier"] = time.time() self.domain_left_edge = self.stream_handler.domain_left_edge.copy() self.domain_right_edge = self.stream_handler.domain_right_edge.copy() self.refine_by = self.stream_handler.refine_by self.dimensionality = self.stream_handler.dimensionality self._periodicity = self.stream_handler.periodicity self.domain_dimensions = self.stream_handler.domain_dimensions self.current_time = self.stream_handler.simulation_time self.gamma = 5.0 / 3.0 self.parameters["EOSType"] = -1 self.parameters["CosmologyHubbleConstantNow"] = 1.0 self.parameters["CosmologyCurrentRedshift"] = 1.0 self.parameters["HydroMethod"] = -1 self.parameters.update(self.stream_handler.parameters) if self.stream_handler.cosmology_simulation: self.cosmological_simulation = 1 self.current_redshift = self.stream_handler.current_redshift self.omega_lambda = self.stream_handler.omega_lambda self.omega_matter = self.stream_handler.omega_matter self.hubble_constant = self.stream_handler.hubble_constant else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 self.cosmological_simulation = 0 def _set_units(self): self.field_units = self.stream_handler.field_units def _set_code_unit_attributes(self): base_units = self.stream_handler.code_units attrs = ( "length_unit", "mass_unit", "time_unit", "velocity_unit", "magnetic_unit", ) cgs_units = ("cm", "g", "s", "cm/s", "gauss") for unit, attr, cgs_unit in zip(base_units, attrs, cgs_units, strict=True): if isinstance(unit, str): if unit == "code_magnetic": # If no magnetic unit was explicitly specified # we skip it now and take care of it at the bottom continue else: uq = self.quan(1.0, unit) elif isinstance(unit, numeric_type): uq = self.quan(unit, cgs_unit) elif isinstance(unit, YTQuantity): uq = unit elif isinstance(unit, tuple): uq = self.quan(unit[0], unit[1]) else: raise RuntimeError(f"{attr} ({unit}) is invalid.") setattr(self, attr, uq) if not hasattr(self, "magnetic_unit"): self.magnetic_unit = np.sqrt( 4 * np.pi * self.mass_unit / (self.time_unit**2 * self.length_unit) ) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return False @property def _skip_cache(self): return True def _find_particle_types(self): particle_types = set() for k, v in self.stream_handler.particle_types.items(): if v: particle_types.add(k[0]) self.particle_types = tuple(particle_types) self.particle_types_raw = self.particle_types class StreamDictFieldHandler(UserDict): _additional_fields: tuple[FieldKey, ...] = () @property def all_fields(self): self_fields = chain.from_iterable(s.keys() for s in self.values()) self_fields = list(set(self_fields)) fields = list(self._additional_fields) + self_fields fields = list(set(fields)) return fields class StreamParticleIndex(SPHParticleIndex): def __init__(self, ds, dataset_type=None): self.stream_handler = ds.stream_handler super().__init__(ds, dataset_type) def _setup_data_io(self): if self.stream_handler.io is not None: self.io = self.stream_handler.io else: self.io = io_registry[self.dataset_type](self.ds) def update_data(self, data): """ Update the stream data with a new data dict. If fields already exist, they will be replaced, but if they do not, they will be added. Fields already in the stream but not part of the data dict will be left alone. """ # Alias ds = self.ds handler = ds.stream_handler # Preprocess field_units, data, _ = process_data(data) pdata = {} for key in data.keys(): if not isinstance(key, tuple): field = ("io", key) mylog.debug("Reassigning '%s' to '%s'", key, field) else: field = key pdata[field] = data[key] data = pdata # Drop reference count particle_types = set_particle_types(data) # Update particle types handler.particle_types.update(particle_types) ds._find_particle_types() # Update fields handler.field_units.update(field_units) fields = handler.fields for field in data.keys(): if field not in fields._additional_fields: fields._additional_fields += (field,) fields["stream_file"].update(data) # Update field list for field in self.ds.field_list: if field[0] in ["all", "nbody"]: self.ds.field_list.remove(field) self._detect_output_fields() self.ds.create_field_info() class StreamParticleFile(ParticleFile): pass class StreamParticlesDataset(StreamDataset): _index_class = StreamParticleIndex _file_class = StreamParticleFile _field_info_class = StreamFieldInfo _dataset_type = "stream_particles" file_count = 1 filename_template = "stream_file" _proj_type = "particle_proj" def __init__( self, stream_handler, storage_filename=None, geometry="cartesian", unit_system="cgs", default_species_fields=None, *, axis_order: AxisOrder | None = None, ): super().__init__( stream_handler, storage_filename=storage_filename, geometry=geometry, unit_system=unit_system, default_species_fields=default_species_fields, axis_order=axis_order, ) fields = list(stream_handler.fields["stream_file"].keys()) sph_ptypes = [] for ptype in self.particle_types: if (ptype, "density") in fields and (ptype, "smoothing_length") in fields: sph_ptypes.append(ptype) if len(sph_ptypes) == 1: self._sph_ptypes = tuple(sph_ptypes) elif len(sph_ptypes) > 1: raise ValueError("Multiple SPH particle types are currently not supported!") def add_sph_fields(self, n_neighbors=32, kernel="cubic", sph_ptype="io"): """Add SPH fields for the specified particle type. For a particle type with "particle_position" and "particle_mass" already defined, this method adds the "smoothing_length" and "density" fields. "smoothing_length" is computed as the distance to the nth nearest neighbor. "density" is computed as the SPH (gather) smoothed mass. The SPH fields are added only if they don't already exist. Parameters ---------- n_neighbors : int The number of neighbors to use in smoothing length computation. kernel : str The kernel function to use in density estimation. sph_ptype : str The SPH particle type. Each dataset has one sph_ptype only. This method will overwrite existing sph_ptype of the dataset. """ mylog.info("Generating SPH fields") # Unify units l_unit = "code_length" m_unit = "code_mass" d_unit = "code_mass / code_length**3" # Read basic fields ad = self.all_data() pos = ad[sph_ptype, "particle_position"].to(l_unit).d mass = ad[sph_ptype, "particle_mass"].to(m_unit).d # Construct k-d tree kdtree = PyKDTree( pos.astype("float64"), left_edge=self.domain_left_edge.to_value(l_unit), right_edge=self.domain_right_edge.to_value(l_unit), periodic=self.periodicity, leafsize=2 * int(n_neighbors), ) order = np.argsort(kdtree.idx) def exists(fname): if (sph_ptype, fname) in self.derived_field_list: mylog.info( "Field ('%s','%s') already exists. Skipping", sph_ptype, fname ) return True else: mylog.info("Generating field ('%s','%s')", sph_ptype, fname) return False data = {} # Add smoothing length field fname = "smoothing_length" if not exists(fname): hsml = generate_smoothing_length(pos[kdtree.idx], kdtree, n_neighbors) hsml = hsml[order] data[sph_ptype, "smoothing_length"] = (hsml, l_unit) else: hsml = ad[sph_ptype, fname].to(l_unit).d # Add density field fname = "density" if not exists(fname): dens = estimate_density( pos[kdtree.idx], mass[kdtree.idx], hsml[kdtree.idx], kdtree, kernel_name=kernel, ) dens = dens[order] data[sph_ptype, "density"] = (dens, d_unit) # Add fields self._sph_ptypes = (sph_ptype,) self.index.update_data(data) self.num_neighbors = n_neighbors _cis = np.array( [ [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1], ], dtype="int64", ) def hexahedral_connectivity(xgrid, ygrid, zgrid): r"""Define the cell coordinates and cell neighbors of a hexahedral mesh for a semistructured grid. Used to specify the connectivity and coordinates parameters used in :func:`~yt.frontends.stream.data_structures.load_hexahedral_mesh`. Parameters ---------- xgrid : array_like x-coordinates of boundaries of the hexahedral cells. Should be a one-dimensional array. ygrid : array_like y-coordinates of boundaries of the hexahedral cells. Should be a one-dimensional array. zgrid : array_like z-coordinates of boundaries of the hexahedral cells. Should be a one-dimensional array. Returns ------- coords : array_like The list of (x,y,z) coordinates of the vertices of the mesh. Is of size (M,3) where M is the number of vertices. connectivity : array_like For each hexahedron h in the mesh, gives the index of each of h's neighbors. Is of size (N,8), where N is the number of hexahedra. Examples -------- >>> xgrid = np.array([-1, -0.25, 0, 0.25, 1]) >>> coords, conn = hexahedral_connectivity(xgrid, xgrid, xgrid) >>> coords array([[-1. , -1. , -1. ], [-1. , -1. , -0.25], [-1. , -1. , 0. ], ..., [ 1. , 1. , 0. ], [ 1. , 1. , 0.25], [ 1. , 1. , 1. ]]) >>> conn array([[ 0, 1, 5, 6, 25, 26, 30, 31], [ 1, 2, 6, 7, 26, 27, 31, 32], [ 2, 3, 7, 8, 27, 28, 32, 33], ..., [ 91, 92, 96, 97, 116, 117, 121, 122], [ 92, 93, 97, 98, 117, 118, 122, 123], [ 93, 94, 98, 99, 118, 119, 123, 124]]) """ nx = len(xgrid) ny = len(ygrid) nz = len(zgrid) coords = np.zeros((nx, ny, nz, 3), dtype="float64", order="C") coords[:, :, :, 0] = xgrid[:, None, None] coords[:, :, :, 1] = ygrid[None, :, None] coords[:, :, :, 2] = zgrid[None, None, :] coords = coords.reshape(nx * ny * nz, 3) cycle = np.rollaxis(np.indices((nx - 1, ny - 1, nz - 1)), 0, 4).reshape( (nx - 1) * (ny - 1) * (nz - 1), 3 ) off = _cis + cycle[:, np.newaxis] connectivity = np.array( ((off[:, :, 0] * ny) + off[:, :, 1]) * nz + off[:, :, 2], order="C" ) return coords, connectivity class StreamHexahedralMesh(SemiStructuredMesh): _connectivity_length = 8 _index_offset = 0 class StreamHexahedralHierarchy(UnstructuredIndex): def __init__(self, ds, dataset_type=None): self.stream_handler = ds.stream_handler super().__init__(ds, dataset_type) def _initialize_mesh(self): coords = self.stream_handler.fields.pop("coordinates") connect = self.stream_handler.fields.pop("connectivity") self.meshes = [ StreamHexahedralMesh(0, self.index_filename, connect, coords, self) ] def _setup_data_io(self): if self.stream_handler.io is not None: self.io = self.stream_handler.io else: self.io = io_registry[self.dataset_type](self.ds) def _detect_output_fields(self): self.field_list = list(set(self.stream_handler.get_fields())) class StreamHexahedralDataset(StreamDataset): _index_class = StreamHexahedralHierarchy _field_info_class = StreamFieldInfo _dataset_type = "stream_hexahedral" class StreamOctreeSubset(OctreeSubset): domain_id = 1 _domain_offset = 1 def __init__(self, base_region, ds, oct_handler, num_zones=2, num_ghost_zones=0): self._num_zones = num_zones self.field_data = YTFieldData() self.field_parameters = {} self.ds = ds self._oct_handler = oct_handler self._last_mask = None self._last_selector_id = None self._current_particle_type = "io" self._current_fluid_type = self.ds.default_fluid_type self.base_region = base_region self.base_selector = base_region.selector self._num_ghost_zones = num_ghost_zones if num_ghost_zones > 0: if not all(ds.periodicity): mylog.warning( "Ghost zones will wrongly assume the domain to be periodic." ) base_grid = StreamOctreeSubset(base_region, ds, oct_handler, num_zones) self._base_grid = base_grid @property def oct_handler(self): return self._oct_handler def retrieve_ghost_zones(self, ngz, fields, smoothed=False): try: new_subset = self._subset_with_gz mylog.debug("Reusing previous subset with ghost zone.") except AttributeError: new_subset = StreamOctreeSubset( self.base_region, self.ds, self.oct_handler, self._num_zones, num_ghost_zones=ngz, ) self._subset_with_gz = new_subset return new_subset def _fill_no_ghostzones(self, content, dest, selector, offset): # Here we get a copy of the file, which we skip through and read the # bits we want. oct_handler = self.oct_handler cell_count = selector.count_oct_cells(self.oct_handler, self.domain_id) levels, cell_inds, file_inds = self.oct_handler.file_index_octs( selector, self.domain_id, cell_count ) levels[:] = 0 dest.update((field, np.empty(cell_count, dtype="float64")) for field in content) # Make references ... count = oct_handler.fill_level( 0, levels, cell_inds, file_inds, dest, content, offset ) return count def _fill_with_ghostzones(self, content, dest, selector, offset): oct_handler = self.oct_handler ndim = self.ds.dimensionality cell_count = ( selector.count_octs(self.oct_handler, self.domain_id) * self.nz**ndim ) gz_cache = getattr(self, "_ghost_zone_cache", None) if gz_cache: levels, cell_inds, file_inds, domains = gz_cache else: gz_cache = ( levels, cell_inds, file_inds, domains, ) = oct_handler.file_index_octs_with_ghost_zones( selector, self.domain_id, cell_count ) self._ghost_zone_cache = gz_cache levels[:] = 0 dest.update((field, np.empty(cell_count, dtype="float64")) for field in content) # Make references ... oct_handler.fill_level(0, levels, cell_inds, file_inds, dest, content, offset) def fill(self, content, dest, selector, offset): if self._num_ghost_zones == 0: return self._fill_no_ghostzones(content, dest, selector, offset) else: return self._fill_with_ghostzones(content, dest, selector, offset) class StreamOctreeHandler(OctreeIndex): def __init__(self, ds, dataset_type=None): self.stream_handler = ds.stream_handler self.dataset_type = dataset_type super().__init__(ds, dataset_type) def _setup_data_io(self): if self.stream_handler.io is not None: self.io = self.stream_handler.io else: self.io = io_registry[self.dataset_type](self.ds) def _initialize_oct_handler(self): header = { "dims": self.ds.domain_dimensions // self.ds.num_zones, "left_edge": self.ds.domain_left_edge, "right_edge": self.ds.domain_right_edge, "octree": self.ds.octree_mask, "num_zones": self.ds.num_zones, "partial_coverage": self.ds.partial_coverage, } self.oct_handler = OctreeContainer.load_octree(header) # We do now need to get the maximum level set, as well. self.ds.max_level = self.oct_handler.max_level def _identify_base_chunk(self, dobj): if getattr(dobj, "_chunk_info", None) is None: base_region = getattr(dobj, "base_region", dobj) subset = [ StreamOctreeSubset( base_region, self.dataset, self.oct_handler, self.ds.num_zones, ) ] dobj._chunk_info = subset dobj._current_chunk = list(self._chunk_all(dobj))[0] def _chunk_all(self, dobj): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) yield YTDataChunk(dobj, "all", oobjs, None) def _chunk_spatial(self, dobj, ngz, sort=None, preload_fields=None): sobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) # This is where we will perform cutting of the Octree and # load-balancing. That may require a specialized selector object to # cut based on some space-filling curve index. for og in sobjs: if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og yield YTDataChunk(dobj, "spatial", [g]) def _chunk_io(self, dobj, cache=True, local_only=False): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for subset in oobjs: yield YTDataChunk(dobj, "io", [subset], None, cache=cache) def _setup_classes(self): dd = self._get_data_reader_dict() super()._setup_classes(dd) def _detect_output_fields(self): # NOTE: Because particle unions add to the actual field list, without # having the keys in the field list itself, we need to double check # here. fl = set(self.stream_handler.get_fields()) fl.update(set(getattr(self, "field_list", []))) self.field_list = list(fl) class StreamOctreeDataset(StreamDataset): _index_class = StreamOctreeHandler _field_info_class = StreamFieldInfo _dataset_type = "stream_octree" levelmax = None def __init__( self, stream_handler, storage_filename=None, geometry="cartesian", unit_system="cgs", default_species_fields=None, ): super().__init__( stream_handler, storage_filename, geometry, unit_system, default_species_fields=default_species_fields, ) # Set up levelmax self.max_level = stream_handler.levels.max() self.min_level = stream_handler.levels.min() class StreamUnstructuredMesh(UnstructuredMesh): _index_offset = 0 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._connectivity_length = self.connectivity_indices.shape[1] class StreamUnstructuredIndex(UnstructuredIndex): def __init__(self, ds, dataset_type=None): self.stream_handler = ds.stream_handler super().__init__(ds, dataset_type) def _initialize_mesh(self): coords = self.stream_handler.fields.pop("coordinates") connect = always_iterable(self.stream_handler.fields.pop("connectivity")) self.meshes = [ StreamUnstructuredMesh(i, self.index_filename, c1, c2, self) for i, (c1, c2) in enumerate(zip(connect, repeat(coords))) ] self.mesh_union = MeshUnion("mesh_union", self.meshes) def _setup_data_io(self): if self.stream_handler.io is not None: self.io = self.stream_handler.io else: self.io = io_registry[self.dataset_type](self.ds) def _detect_output_fields(self): self.field_list = list(set(self.stream_handler.get_fields())) fnames = list({fn for ft, fn in self.field_list}) self.field_list += [("all", fname) for fname in fnames] class StreamUnstructuredMeshDataset(StreamDataset): _index_class = StreamUnstructuredIndex _field_info_class = StreamFieldInfo _dataset_type = "stream_unstructured" def _find_particle_types(self): pass yt-project-yt-f043ac8/yt/frontends/stream/definitions.py000066400000000000000000000244631510711153200235010ustar00rootroot00000000000000from collections import defaultdict import numpy as np from yt.funcs import is_sequence from yt.geometry.grid_container import GridTree, MatchPointsToGrids from yt.utilities.exceptions import ( YTInconsistentGridFieldShape, YTInconsistentGridFieldShapeGridDims, YTInconsistentParticleFieldShape, ) from yt.utilities.logger import ytLogger as mylog from .fields import StreamFieldInfo def assign_particle_data(ds, pdata, bbox): """ Assign particle data to the grids using MatchPointsToGrids. This will overwrite any existing particle data, so be careful! """ particle_index_fields = [ f"particle_position_{ax}" for ax in ds.coordinates.axis_order ] for ptype in ds.particle_types_raw: check_fields = [(ptype, pi_field) for pi_field in particle_index_fields] check_fields.append((ptype, "particle_position")) if all(f not in pdata for f in check_fields): pdata_ftype = {} for f in sorted(pdata): if not hasattr(pdata[f], "shape"): continue if f == "number_of_particles": continue mylog.debug("Reassigning '%s' to ('%s','%s')", f, ptype, f) pdata_ftype[ptype, f] = pdata.pop(f) pdata_ftype.update(pdata) pdata = pdata_ftype # Note: what we need to do here is a bit tricky. Because occasionally this # gets called before we property handle the field detection, we cannot use # any information about the index. Fortunately for us, we can generate # most of the GridTree utilizing information we already have from the # stream handler. if len(ds.stream_handler.fields) > 1: pdata.pop("number_of_particles", None) num_grids = len(ds.stream_handler.fields) parent_ids = ds.stream_handler.parent_ids num_children = np.zeros(num_grids, dtype="int64") # We're going to do this the slow way mask = np.empty(num_grids, dtype="bool") for i in range(num_grids): np.equal(parent_ids, i, mask) num_children[i] = mask.sum() levels = ds.stream_handler.levels.astype("int64").ravel() grid_tree = GridTree( num_grids, ds.stream_handler.left_edges, ds.stream_handler.right_edges, ds.stream_handler.dimensions, ds.stream_handler.parent_ids, levels, num_children, ) grid_pdata = [] for _ in range(num_grids): grid = {"number_of_particles": 0} grid_pdata.append(grid) particle_index_fields = [ f"particle_position_{ax}" for ax in ds.coordinates.axis_order ] for ptype in ds.particle_types_raw: if (ptype, "particle_position_x") in pdata: # we call them x, y, z even though they may be different field names x, y, z = (pdata[ptype, pi_field] for pi_field in particle_index_fields) elif (ptype, "particle_position") in pdata: x, y, z = pdata[ptype, "particle_position"].T else: raise KeyError( "Cannot decompose particle data without position fields!" ) pts = MatchPointsToGrids(grid_tree, len(x), x, y, z) particle_grid_inds = pts.find_points_in_tree() (assigned_particles,) = (particle_grid_inds >= 0).nonzero() num_particles = particle_grid_inds.size num_unassigned = num_particles - assigned_particles.size if num_unassigned > 0: eps = np.finfo(x.dtype).eps s = np.array( [ [x.min() - eps, x.max() + eps], [y.min() - eps, y.max() + eps], [z.min() - eps, z.max() + eps], ] ) sug_bbox = [ [min(bbox[0, 0], s[0, 0]), max(bbox[0, 1], s[0, 1])], [min(bbox[1, 0], s[1, 0]), max(bbox[1, 1], s[1, 1])], [min(bbox[2, 0], s[2, 0]), max(bbox[2, 1], s[2, 1])], ] mylog.warning( "Discarding %s particles (out of %s) that are outside " "bounding box. Set bbox=%s to avoid this in the future.", num_unassigned, num_particles, sug_bbox, ) particle_grid_inds = particle_grid_inds[assigned_particles] x = x[assigned_particles] y = y[assigned_particles] z = z[assigned_particles] idxs = np.argsort(particle_grid_inds) particle_grid_count = np.bincount( particle_grid_inds.astype("intp"), minlength=num_grids ) particle_indices = np.zeros(num_grids + 1, dtype="int64") if num_grids > 1: np.add.accumulate( particle_grid_count.squeeze(), out=particle_indices[1:] ) else: particle_indices[1] = particle_grid_count.squeeze() for i, pcount in enumerate(particle_grid_count): grid_pdata[i]["number_of_particles"] += pcount start = particle_indices[i] end = particle_indices[i + 1] for key in pdata.keys(): if key[0] == ptype: grid_pdata[i][key] = pdata[key][idxs][start:end] else: grid_pdata = [pdata] for pd, gi in zip(grid_pdata, sorted(ds.stream_handler.fields), strict=True): ds.stream_handler.fields[gi].update(pd) ds.stream_handler.particle_types.update(set_particle_types(pd)) npart = ds.stream_handler.fields[gi].pop("number_of_particles", 0) ds.stream_handler.particle_count[gi] = npart def process_data(data, grid_dims=None, allow_callables=True): new_data, field_units = {}, {} for field, val in data.items(): # val is a data array if isinstance(val, np.ndarray): # val is a YTArray if hasattr(val, "units"): field_units[field] = val.units new_data[field] = val.copy().d # val is a numpy array else: field_units[field] = "" new_data[field] = val.copy() # val is a tuple of (data, units) elif isinstance(val, tuple) and len(val) == 2: valid_data = isinstance(val[0], np.ndarray) if allow_callables: valid_data = valid_data or callable(val[0]) if not isinstance(field, (str, tuple)): raise TypeError("Field name is not a string!") if not valid_data: raise TypeError( "Field data is not an ndarray or callable (with nproc == 1)!" ) if not isinstance(val[1], str): raise TypeError("Unit specification is not a string!") field_units[field] = val[1] new_data[field] = val[0] # val is a list of data to be turned into an array elif is_sequence(val): field_units[field] = "" new_data[field] = np.asarray(val) elif callable(val): if not allow_callables: raise RuntimeError( "Callable functions can not be specified " "in conjunction with nprocs > 1." ) field_units[field] = "" new_data[field] = val else: raise RuntimeError( "The data dict appears to be invalid. " "The data dictionary must map from field " "names to (numpy array, unit spec) tuples. " ) data = new_data # At this point, we have arrays for all our fields new_data = {} for field in data: n_shape = 3 if not callable(data[field]): n_shape = len(data[field].shape) if isinstance(field, tuple): new_field = field elif n_shape in (1, 2): new_field = ("io", field) elif n_shape == 3: new_field = ("stream", field) else: raise RuntimeError new_data[new_field] = data[field] field_units[new_field] = field_units.pop(field) known_fields = ( StreamFieldInfo.known_particle_fields + StreamFieldInfo.known_other_fields ) # We do not want to override any of the known ones, if it's not # overridden here. if ( any(f[0] == new_field[1] for f in known_fields) and field_units[new_field] == "" ): field_units.pop(new_field) data = new_data # Sanity checking that all fields have the same dimensions. g_shapes = [] p_shapes = defaultdict(list) for field in data: if callable(data[field]): continue f_shape = data[field].shape n_shape = len(f_shape) if n_shape in (1, 2): p_shapes[field[0]].append((field[1], f_shape[0])) elif n_shape == 3: g_shapes.append((field, f_shape)) if len(g_shapes) > 0: g_s = np.array([s[1] for s in g_shapes]) if not np.all(g_s == g_s[0]): raise YTInconsistentGridFieldShape(g_shapes) if grid_dims is not None: if not np.all(g_s == grid_dims): raise YTInconsistentGridFieldShapeGridDims(g_shapes, grid_dims) if len(p_shapes) > 0: for ptype, p_shape in p_shapes.items(): p_s = np.array([s[1] for s in p_shape]) if not np.all(p_s == p_s[0]): raise YTInconsistentParticleFieldShape(ptype, p_shape) # Now that we know the particle fields are consistent, determine the number # of particles. if len(p_shapes) > 0: number_of_particles = np.sum([s[0][1] for s in p_shapes.values()]) else: number_of_particles = 0 return field_units, data, number_of_particles def set_particle_types(data): particle_types = {} for key in data.keys(): if key == "number_of_particles": continue elif callable(data[key]): particle_types[key] = False elif len(data[key].shape) == 1: particle_types[key] = True else: particle_types[key] = False return particle_types yt-project-yt-f043ac8/yt/frontends/stream/fields.py000066400000000000000000000153241510711153200224300ustar00rootroot00000000000000import re from yt._typing import KnownFieldsT from yt.fields.field_info_container import FieldInfoContainer class StreamFieldInfo(FieldInfoContainer): known_other_fields: KnownFieldsT = ( ("density", ("code_mass/code_length**3", ["density"], None)), ( "dark_matter_density", ("code_mass/code_length**3", ["dark_matter_density"], None), ), ("number_density", ("1/code_length**3", ["number_density"], None)), ("pressure", ("dyne/code_length**2", ["pressure"], None)), ("specific_thermal_energy", ("erg / g", ["specific_thermal_energy"], None)), ("temperature", ("K", ["temperature"], None)), ("velocity_x", ("code_length/code_time", ["velocity_x"], None)), ("velocity_y", ("code_length/code_time", ["velocity_y"], None)), ("velocity_z", ("code_length/code_time", ["velocity_z"], None)), ("magnetic_field_x", ("gauss", [], None)), ("magnetic_field_y", ("gauss", [], None)), ("magnetic_field_z", ("gauss", [], None)), ("velocity_r", ("code_length/code_time", ["velocity_r"], None)), ("velocity_theta", ("code_length/code_time", ["velocity_theta"], None)), ("velocity_phi", ("code_length/code_time", ["velocity_phi"], None)), ("magnetic_field_r", ("gauss", [], None)), ("magnetic_field_theta", ("gauss", [], None)), ("magnetic_field_phi", ("gauss", [], None)), ( "radiation_acceleration_x", ("code_length/code_time**2", ["radiation_acceleration_x"], None), ), ( "radiation_acceleration_y", ("code_length/code_time**2", ["radiation_acceleration_y"], None), ), ( "radiation_acceleration_z", ("code_length/code_time**2", ["radiation_acceleration_z"], None), ), ("metallicity", ("Zsun", ["metallicity"], None)), # We need to have a bunch of species fields here, too ("metal_density", ("code_mass/code_length**3", ["metal_density"], None)), ("hi_density", ("code_mass/code_length**3", ["hi_density"], None)), ("hii_density", ("code_mass/code_length**3", ["hii_density"], None)), ("h2i_density", ("code_mass/code_length**3", ["h2i_density"], None)), ("h2ii_density", ("code_mass/code_length**3", ["h2ii_density"], None)), ("h2m_density", ("code_mass/code_length**3", ["h2m_density"], None)), ("hei_density", ("code_mass/code_length**3", ["hei_density"], None)), ("heii_density", ("code_mass/code_length**3", ["heii_density"], None)), ("heiii_density", ("code_mass/code_length**3", ["heiii_density"], None)), ("hdi_density", ("code_mass/code_length**3", ["hdi_density"], None)), ("di_density", ("code_mass/code_length**3", ["di_density"], None)), ("dii_density", ("code_mass/code_length**3", ["dii_density"], None)), ) known_particle_fields: KnownFieldsT = ( ("particle_position", ("code_length", ["particle_position"], None)), ("particle_position_x", ("code_length", ["particle_position_x"], None)), ("particle_position_y", ("code_length", ["particle_position_y"], None)), ("particle_position_z", ("code_length", ["particle_position_z"], None)), ("particle_velocity", ("code_length/code_time", ["particle_velocity"], None)), ( "particle_velocity_x", ("code_length/code_time", ["particle_velocity_x"], None), ), ( "particle_velocity_y", ("code_length/code_time", ["particle_velocity_y"], None), ), ( "particle_velocity_z", ("code_length/code_time", ["particle_velocity_z"], None), ), ("particle_index", ("", ["particle_index"], None)), ( "particle_gas_density", ("code_mass/code_length**3", ["particle_gas_density"], None), ), ("particle_gas_temperature", ("K", ["particle_gas_temperature"], None)), ("particle_mass", ("code_mass", ["particle_mass"], None)), ("smoothing_length", ("code_length", ["smoothing_length"], None)), ("density", ("code_mass/code_length**3", ["density"], None)), ("temperature", ("code_temperature", ["temperature"], None)), ("creation_time", ("code_time", ["creation_time"], None)), ("age", ("code_time", [], None)), ) def setup_fluid_fields(self): from yt.fields.magnetic_field import setup_magnetic_field_aliases from yt.fields.species_fields import setup_species_fields from yt.utilities.periodic_table import periodic_table # First grab all the element symbols from the periodic table # (this includes the electron and deuterium) symbols = list(periodic_table.elements_by_symbol) # Now add some common molecules symbols += ["H2", "CO"] species_names = [] for field in self.ds.stream_handler.field_units: if field[0] in self.ds.particle_types: continue units = self.ds.stream_handler.field_units[field] if units != "": self.add_output_field(field, sampling_type="cell", units=units) # Check to see if this could be a species fraction field if field[1].endswith("_fraction"): sp = field[1].rsplit("_fraction")[0] parts = sp.split("_") # parts is now either an element or molecule symbol # by itself: valid = parts[0] in symbols # or it may also have an ionization state after it if valid and len(parts) > 1 and parts[0] != "El": # Note that this doesn't catch invalid ionization states, # which would indicate more electron states empty than actually # exist, but we'll leave that to the user to do correctly. valid &= re.match("^[pm](0|[1-9][0-9]*)$", parts[1]) is not None if valid: # Add the species name to the list species_names.append(sp) # Alias the field self.alias(("gas", field[1]), ("stream", field[1])) self.species_names = sorted(species_names) setup_magnetic_field_aliases( self, "stream", [f"magnetic_field_{ax}" for ax in self.ds.coordinates.axis_order], ) setup_species_fields(self) def add_output_field(self, name, sampling_type, **kwargs): if name in self.ds.stream_handler.field_units: kwargs["units"] = self.ds.stream_handler.field_units[name] super().add_output_field(name, sampling_type, **kwargs) yt-project-yt-f043ac8/yt/frontends/stream/io.py000066400000000000000000000257011510711153200215710ustar00rootroot00000000000000import numpy as np from yt.utilities.exceptions import YTDomainOverflow from yt.utilities.io_handler import BaseIOHandler, BaseParticleIOHandler from yt.utilities.logger import ytLogger as mylog class IOHandlerStream(BaseIOHandler): _dataset_type = "stream" _vector_fields = {"particle_velocity": 3, "particle_position": 3} def __init__(self, ds): self.fields = ds.stream_handler.fields self.field_units = ds.stream_handler.field_units super().__init__(ds) def _read_data_set(self, grid, field): # This is where we implement processor-locking tr = self.fields[grid.id][field] if callable(tr): tr = tr(grid, field) # If it's particles, we copy. if len(tr.shape) == 1: return tr.copy() # New in-place unit conversion breaks if we don't copy first return tr def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) if any((ftype not in self.ds.fluid_types for ftype, fname in fields)): raise NotImplementedError rv = {} for field in fields: rv[field] = self.ds.arr(np.empty(size, dtype="float64")) ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [f2 for f1, f2 in fields], ng, ) for field in fields: ftype, fname = field ind = 0 for chunk in chunks: for g in chunk.objs: ds = self.fields[g.id][ftype, fname] if callable(ds): ds = ds(g, field) ind += g.select(selector, ds, rv[field], ind) # caches return rv def _read_particle_coords(self, chunks, ptf): chunks = list(chunks) for chunk in chunks: for g in chunk.objs: if g.NumberOfParticles == 0: continue gf = self.fields[g.id] for ptype in sorted(ptf): if (ptype, "particle_position") in gf: x, y, z = gf[ptype, "particle_position"].T else: x, y, z = ( gf[ptype, f"particle_position_{ax}"] for ax in self.ds.coordinates.axis_order ) yield ptype, (x, y, z), 0.0 def _read_particle_fields(self, chunks, ptf, selector): chunks = list(chunks) for chunk in chunks: for g in chunk.objs: if g.NumberOfParticles == 0: continue gf = self.fields[g.id] for ptype, field_list in sorted(ptf.items()): if (ptype, "particle_position") in gf: x, y, z = gf[ptype, "particle_position"].T else: x, y, z = ( gf[ptype, f"particle_position_{ax}"] for ax in self.ds.coordinates.axis_order ) mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: data = np.asarray(gf[ptype, field]) yield (ptype, field), data[mask] @property def _read_exception(self): return KeyError class StreamParticleIOHandler(BaseParticleIOHandler): _dataset_type = "stream_particles" _vector_fields = {"particle_velocity": 3, "particle_position": 3} def __init__(self, ds): self.fields = ds.stream_handler.fields super().__init__(ds) def _read_particle_coords(self, chunks, ptf): for data_file in self._sorted_chunk_iterator(chunks): f = self.fields[data_file.filename] # This double-reads for ptype in sorted(ptf): yield ( ptype, ( f[ptype, "particle_position_x"], f[ptype, "particle_position_y"], f[ptype, "particle_position_z"], ), 0.0, ) def _read_smoothing_length(self, chunks, ptf, ptype): for data_file in self._sorted_chunk_iterator(chunks): f = self.fields[data_file.filename] return f[ptype, "smoothing_length"] def _read_particle_data_file(self, data_file, ptf, selector=None): return_data = {} f = self.fields[data_file.filename] for ptype, field_list in sorted(ptf.items()): if (ptype, "particle_position") in f: ppos = f[ptype, "particle_position"] x = ppos[:, 0] y = ppos[:, 1] z = ppos[:, 2] else: x, y, z = (f[ptype, f"particle_position_{ax}"] for ax in "xyz") if (ptype, "smoothing_length") in self.ds.field_list: hsml = f[ptype, "smoothing_length"] else: hsml = 0.0 if selector: mask = selector.select_points(x, y, z, hsml) if mask is None: continue for field in field_list: data = f[ptype, field] if selector: data = data[mask] return_data[ptype, field] = data return return_data def _yield_coordinates(self, data_file, needed_ptype=None): # self.fields[g.id][fname] is the pattern here for ptype in self.ds.particle_types_raw: if needed_ptype is not None and needed_ptype is not ptype: continue try: pos = np.column_stack( [ self.fields[data_file.filename][ (ptype, f"particle_position_{ax}") ] for ax in "xyz" ] ) except KeyError: pos = self.fields[data_file.filename][ptype, "particle_position"] if np.any(pos.min(axis=0) < data_file.ds.domain_left_edge) or np.any( pos.max(axis=0) > data_file.ds.domain_right_edge ): raise YTDomainOverflow( pos.min(axis=0), pos.max(axis=0), data_file.ds.domain_left_edge, data_file.ds.domain_right_edge, ) yield ptype, pos def _get_smoothing_length(self, data_file, dtype, shape): ptype = self.ds._sph_ptypes[0] return self.fields[data_file.filename][ptype, "smoothing_length"] def _count_particles(self, data_file): pcount = {} for ptype in self.ds.particle_types_raw: pcount[ptype] = 0 # stream datasets only have one "file" if data_file.file_id > 0: return pcount for ptype in self.ds.particle_types_raw: d = self.fields[data_file.filename] try: pcount[ptype] = d[ptype, "particle_position_x"].size except KeyError: pcount[ptype] = d[ptype, "particle_position"].shape[0] return pcount def _identify_fields(self, data_file): return self.fields[data_file.filename].keys(), {} class IOHandlerStreamHexahedral(BaseIOHandler): _dataset_type = "stream_hexahedral" _vector_fields = {"particle_velocity": 3, "particle_position": 3} def __init__(self, ds): self.fields = ds.stream_handler.fields super().__init__(ds) def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) assert len(chunks) == 1 chunk = chunks[0] rv = {} for field in fields: ftype, fname = field rv[field] = np.empty(size, dtype="float64") ngrids = sum(len(chunk.objs) for chunk in chunks) mylog.debug( "Reading %s cells of %s fields in %s blocks", size, [fn for ft, fn in fields], ngrids, ) for field in fields: ind = 0 ftype, fname = field for chunk in chunks: for g in chunk.objs: ds = self.fields[g.mesh_id].get(field, None) if ds is None: ds = self.fields[g.mesh_id][fname] ind += g.select(selector, ds, rv[field], ind) # caches return rv class IOHandlerStreamOctree(BaseIOHandler): _dataset_type = "stream_octree" _vector_fields = {"particle_velocity": 3, "particle_position": 3} def __init__(self, ds): self.fields = ds.stream_handler.fields super().__init__(ds) def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} ind = 0 chunks = list(chunks) assert len(chunks) == 1 for chunk in chunks: assert len(chunk.objs) == 1 for subset in chunk.objs: field_vals = {} for field in fields: field_vals[field] = self.fields[ subset.domain_id - subset._domain_offset ][field] subset.fill(field_vals, rv, selector, ind) return rv class IOHandlerStreamUnstructured(BaseIOHandler): _dataset_type = "stream_unstructured" def __init__(self, ds): self.fields = ds.stream_handler.fields super().__init__(ds) def _read_fluid_selection(self, chunks, selector, fields, size): chunks = list(chunks) rv = {} for field in fields: ftype, fname = field if ftype == "all": ci = np.concatenate( [mesh.connectivity_indices for mesh in self.ds.index.mesh_union] ) else: mesh_id = int(ftype[-1]) - 1 m = self.ds.index.meshes[mesh_id] ci = m.connectivity_indices num_elem = ci.shape[0] if fname in self.ds._node_fields: nodes_per_element = ci.shape[1] rv[field] = np.empty((num_elem, nodes_per_element), dtype="float64") else: rv[field] = np.empty(num_elem, dtype="float64") for field in fields: ind = 0 ftype, fname = field if ftype == "all": objs = list(self.ds.index.mesh_union) else: mesh_ids = [int(ftype[-1])] chunk = chunks[mesh_ids[0] - 1] objs = chunk.objs for g in objs: ds = self.fields[g.mesh_id].get(field, None) if ds is None: f = ("connect%d" % (g.mesh_id + 1), fname) ds = self.fields[g.mesh_id][f] ind += g.select(selector, ds, rv[field], ind) # caches rv[field] = rv[field][:ind] return rv yt-project-yt-f043ac8/yt/frontends/stream/misc.py000066400000000000000000000012301510711153200221040ustar00rootroot00000000000000import numpy as np from yt._typing import DomainDimensions def _validate_cell_widths( cell_widths: list[np.ndarray], domain_dimensions: DomainDimensions, ) -> list[np.ndarray]: # check dimensionality if (nwids := len(cell_widths)) != (ndims := len(domain_dimensions)): raise ValueError( f"The number of elements in cell_widths ({nwids}) " f"must match the number of dimensions ({ndims})." ) # check the dtypes for each dimension, upcast to float64 if needed for idim in range(len(cell_widths)): cell_widths[idim] = cell_widths[idim].astype(np.float64, copy=False) return cell_widths yt-project-yt-f043ac8/yt/frontends/stream/sample_data/000077500000000000000000000000001510711153200230555ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/stream/sample_data/__init__.py000066400000000000000000000000001510711153200251540ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/stream/sample_data/hexahedral_mesh.py000066400000000000000000013310661510711153200265620ustar00rootroot00000000000000import numpy as np # This mesh can be found at # https://github.com/idaholab/moose/tree/master/test/tests/bcs/nodal_normals/cylinder-hexes.e _coordinates = np.array([ [ 1.00000000e+00, 0.00000000e+00, 3.57142857e-01], [ 9.86361303e-01, 1.64594590e-01, 3.57142857e-01], [ 9.04164528e-01, 1.50878374e-01, 3.57142857e-01], [ 9.16666667e-01, 2.24432411e-17, 3.57142857e-01], [ 1.00000000e+00, 0.00000000e+00, 5.00000000e-01], [ 9.86361303e-01, 1.64594590e-01, 5.00000000e-01], [ 9.04164528e-01, 1.50878374e-01, 5.00000000e-01], [ 9.16666667e-01, -4.50419429e-18, 5.00000000e-01], [ 9.45817242e-01, 3.24699469e-01, 3.57142857e-01], [ 8.66999138e-01, 2.97641180e-01, 3.57142857e-01], [ 9.45817242e-01, 3.24699469e-01, 5.00000000e-01], [ 8.66999138e-01, 2.97641180e-01, 5.00000000e-01], [ 8.79473751e-01, 4.75947393e-01, 3.57142857e-01], [ 8.06184272e-01, 4.36285110e-01, 3.57142857e-01], [ 8.79473751e-01, 4.75947393e-01, 5.00000000e-01], [ 8.06184272e-01, 4.36285110e-01, 5.00000000e-01], [ 7.89140509e-01, 6.14212713e-01, 3.57142857e-01], [ 7.23378800e-01, 5.63028320e-01, 3.57142857e-01], [ 7.89140509e-01, 6.14212713e-01, 5.00000000e-01], [ 7.23378800e-01, 5.63028320e-01, 5.00000000e-01], [ 6.77281572e-01, 7.35723911e-01, 3.57142857e-01], [ 6.20841441e-01, 6.74413585e-01, 3.57142857e-01], [ 6.77281572e-01, 7.35723911e-01, 5.00000000e-01], [ 6.20841441e-01, 6.74413585e-01, 5.00000000e-01], [ 5.46948158e-01, 8.37166478e-01, 3.57142857e-01], [ 5.01369145e-01, 7.67402605e-01, 3.57142857e-01], [ 5.46948158e-01, 8.37166478e-01, 5.00000000e-01], [ 5.01369145e-01, 7.67402605e-01, 5.00000000e-01], [ 4.01695425e-01, 9.15773327e-01, 3.57142857e-01], [ 3.68220806e-01, 8.39458883e-01, 3.57142857e-01], [ 4.01695425e-01, 9.15773327e-01, 5.00000000e-01], [ 3.68220806e-01, 8.39458883e-01, 5.00000000e-01], [ 8.21967753e-01, 1.37162159e-01, 3.57142857e-01], [ 8.33333333e-01, 3.56043101e-17, 3.57142857e-01], [ 8.21967753e-01, 1.37162159e-01, 5.00000000e-01], [ 8.33333333e-01, -9.00838858e-18, 5.00000000e-01], [ 7.88181035e-01, 2.70582891e-01, 3.57142857e-01], [ 7.88181035e-01, 2.70582891e-01, 5.00000000e-01], [ 7.32894793e-01, 3.96622828e-01, 3.57142857e-01], [ 7.32894793e-01, 3.96622828e-01, 5.00000000e-01], [ 6.57617091e-01, 5.11843927e-01, 3.57142857e-01], [ 6.57617091e-01, 5.11843927e-01, 5.00000000e-01], [ 5.64401310e-01, 6.13103259e-01, 3.57142857e-01], [ 5.64401310e-01, 6.13103259e-01, 5.00000000e-01], [ 4.55790132e-01, 6.97638732e-01, 3.57142857e-01], [ 4.55790132e-01, 6.97638732e-01, 5.00000000e-01], [ 3.34746187e-01, 7.63144439e-01, 3.57142857e-01], [ 3.34746187e-01, 7.63144439e-01, 5.00000000e-01], [ 7.39770978e-01, 1.23445943e-01, 3.57142857e-01], [ 7.50000000e-01, 3.32197375e-17, 3.57142857e-01], [ 7.39770978e-01, 1.23445943e-01, 5.00000000e-01], [ 7.50000000e-01, -1.35125829e-17, 5.00000000e-01], [ 7.09362931e-01, 2.43524602e-01, 3.57142857e-01], [ 7.09362931e-01, 2.43524602e-01, 5.00000000e-01], [ 6.59605313e-01, 3.56960545e-01, 3.57142857e-01], [ 6.59605313e-01, 3.56960545e-01, 5.00000000e-01], [ 5.91855382e-01, 4.60659535e-01, 3.57142857e-01], [ 5.91855382e-01, 4.60659535e-01, 5.00000000e-01], [ 5.07961179e-01, 5.51792933e-01, 3.57142857e-01], [ 5.07961179e-01, 5.51792933e-01, 5.00000000e-01], [ 4.10211119e-01, 6.27874859e-01, 3.57142857e-01], [ 4.10211119e-01, 6.27874859e-01, 5.00000000e-01], [ 3.01271568e-01, 6.86829995e-01, 3.57142857e-01], [ 3.01271568e-01, 6.86829995e-01, 5.00000000e-01], [ 6.57574202e-01, 1.09729727e-01, 3.57142857e-01], [ 6.66666667e-01, 2.67569398e-17, 3.57142857e-01], [ 6.57574202e-01, 1.09729727e-01, 5.00000000e-01], [ 6.66666667e-01, -1.80167772e-17, 5.00000000e-01], [ 6.30544828e-01, 2.16466313e-01, 3.57142857e-01], [ 6.30544828e-01, 2.16466313e-01, 5.00000000e-01], [ 5.86315834e-01, 3.17298262e-01, 3.57142857e-01], [ 5.86315834e-01, 3.17298262e-01, 5.00000000e-01], [ 5.26093673e-01, 4.09475142e-01, 3.57142857e-01], [ 5.26093673e-01, 4.09475142e-01, 5.00000000e-01], [ 4.51521048e-01, 4.90482607e-01, 3.57142857e-01], [ 4.51521048e-01, 4.90482607e-01, 5.00000000e-01], [ 3.64632105e-01, 5.58110986e-01, 3.57142857e-01], [ 3.64632105e-01, 5.58110986e-01, 5.00000000e-01], [ 2.67796950e-01, 6.10515551e-01, 3.57142857e-01], [ 2.67796950e-01, 6.10515551e-01, 5.00000000e-01], [ 5.75377427e-01, 9.60135110e-02, 3.57142857e-01], [ 5.83333333e-01, 1.89153508e-17, 3.57142857e-01], [ 5.75377427e-01, 9.60135110e-02, 5.00000000e-01], [ 5.83333333e-01, -2.25209714e-17, 5.00000000e-01], [ 5.51726724e-01, 1.89408024e-01, 3.57142857e-01], [ 5.51726724e-01, 1.89408024e-01, 5.00000000e-01], [ 5.13026355e-01, 2.77635979e-01, 3.57142857e-01], [ 5.13026355e-01, 2.77635979e-01, 5.00000000e-01], [ 4.60331964e-01, 3.58290749e-01, 3.57142857e-01], [ 4.60331964e-01, 3.58290749e-01, 5.00000000e-01], [ 3.95080917e-01, 4.29172281e-01, 3.57142857e-01], [ 3.95080917e-01, 4.29172281e-01, 5.00000000e-01], [ 3.19053092e-01, 4.88347112e-01, 3.57142857e-01], [ 3.19053092e-01, 4.88347112e-01, 5.00000000e-01], [ 2.34322331e-01, 5.34201107e-01, 3.57142857e-01], [ 2.34322331e-01, 5.34201107e-01, 5.00000000e-01], [ 4.93180652e-01, 8.22972951e-02, 3.57142857e-01], [ 5.00000000e-01, 1.04459875e-17, 3.57142857e-01], [ 4.93180652e-01, 8.22972951e-02, 5.00000000e-01], [ 5.00000000e-01, -2.70251657e-17, 5.00000000e-01], [ 4.72908621e-01, 1.62349735e-01, 3.57142857e-01], [ 4.72908621e-01, 1.62349735e-01, 5.00000000e-01], [ 4.39736876e-01, 2.37973697e-01, 3.57142857e-01], [ 4.39736876e-01, 2.37973697e-01, 5.00000000e-01], [ 3.94570255e-01, 3.07106356e-01, 3.57142857e-01], [ 3.94570255e-01, 3.07106356e-01, 5.00000000e-01], [ 3.38640786e-01, 3.67861955e-01, 3.57142857e-01], [ 3.38640786e-01, 3.67861955e-01, 5.00000000e-01], [ 2.73474079e-01, 4.18583239e-01, 3.57142857e-01], [ 2.73474079e-01, 4.18583239e-01, 5.00000000e-01], [ 2.00847712e-01, 4.57886663e-01, 3.57142857e-01], [ 2.00847712e-01, 4.57886663e-01, 5.00000000e-01], [ 2.92606991e-01, 3.16445261e-01, 3.57142857e-01], [ 3.44188184e-01, 2.67935253e-01, 3.57142857e-01], [ 2.92606991e-01, 3.16445261e-01, 5.00000000e-01], [ 3.44188184e-01, 2.67935253e-01, 5.00000000e-01], [ 2.34867640e-01, 3.58265725e-01, 3.57142857e-01], [ 2.34867640e-01, 3.58265725e-01, 5.00000000e-01], [ 1.72155182e-01, 3.92474283e-01, 3.57142857e-01], [ 1.72155182e-01, 3.92474283e-01, 5.00000000e-01], [ 2.46573197e-01, 2.65028567e-01, 3.57142857e-01], [ 2.93806114e-01, 2.28764151e-01, 3.57142857e-01], [ 2.46573197e-01, 2.65028567e-01, 5.00000000e-01], [ 2.93806114e-01, 2.28764151e-01, 5.00000000e-01], [ 1.96261201e-01, 2.97948211e-01, 3.57142857e-01], [ 1.96261201e-01, 2.97948211e-01, 5.00000000e-01], [ 1.43462652e-01, 3.27061902e-01, 3.57142857e-01], [ 1.43462652e-01, 3.27061902e-01, 5.00000000e-01], [ 2.00539403e-01, 2.13611872e-01, 3.57142857e-01], [ 2.43424043e-01, 1.89593048e-01, 3.57142857e-01], [ 2.00539403e-01, 2.13611872e-01, 5.00000000e-01], [ 2.43424043e-01, 1.89593048e-01, 5.00000000e-01], [ 1.57654762e-01, 2.37630697e-01, 3.57142857e-01], [ 1.57654762e-01, 2.37630697e-01, 5.00000000e-01], [ 1.14770121e-01, 2.61649522e-01, 3.57142857e-01], [ 1.14770121e-01, 2.61649522e-01, 5.00000000e-01], [ 8.60775910e-02, 1.96237141e-01, 3.57142857e-01], [ 1.39074405e-01, 1.78223023e-01, 3.57142857e-01], [ 8.60775910e-02, 1.96237141e-01, 5.00000000e-01], [ 1.39074405e-01, 1.78223023e-01, 5.00000000e-01], [ 5.73850607e-02, 1.30824761e-01, 3.57142857e-01], [ 1.20494048e-01, 1.18815349e-01, 3.57142857e-01], [ 5.73850607e-02, 1.30824761e-01, 5.00000000e-01], [ 1.20494048e-01, 1.18815349e-01, 5.00000000e-01], [ 2.86925303e-02, 6.54123805e-02, 3.57142857e-01], [ 1.01913690e-01, 5.94076743e-02, 3.57142857e-01], [ 2.86925303e-02, 6.54123805e-02, 5.00000000e-01], [ 1.01913690e-01, 5.94076743e-02, 5.00000000e-01], [ -1.23126238e-17, -4.32625932e-17, 3.57142857e-01], [ 8.33333333e-02, -3.43565351e-17, 3.57142857e-01], [ -5.84327908e-18, -5.40503315e-17, 5.00000000e-01], [ 8.33333333e-02, -4.95461372e-17, 5.00000000e-01], [ 1.92071219e-01, 1.60208904e-01, 3.57142857e-01], [ 1.92071219e-01, 1.60208904e-01, 5.00000000e-01], [ 1.83603035e-01, 1.06805936e-01, 3.57142857e-01], [ 1.83603035e-01, 1.06805936e-01, 5.00000000e-01], [ 1.75134851e-01, 5.34029681e-02, 3.57142857e-01], [ 1.75134851e-01, 5.34029681e-02, 5.00000000e-01], [ 1.66666667e-01, -2.53770275e-17, 3.57142857e-01], [ 1.66666667e-01, -4.50419429e-17, 5.00000000e-01], [ 2.45068032e-01, 1.42194786e-01, 3.57142857e-01], [ 2.45068032e-01, 1.42194786e-01, 5.00000000e-01], [ 2.46712022e-01, 9.47965238e-02, 3.57142857e-01], [ 2.46712022e-01, 9.47965238e-02, 5.00000000e-01], [ 2.48356011e-01, 4.73982619e-02, 3.57142857e-01], [ 2.48356011e-01, 4.73982619e-02, 5.00000000e-01], [ 2.50000000e-01, -1.63501274e-17, 3.57142857e-01], [ 2.50000000e-01, -4.05377486e-17, 5.00000000e-01], [ 3.33333333e-01, -7.31934467e-18, 3.57142857e-01], [ 3.29964224e-01, 5.90312730e-02, 3.57142857e-01], [ 3.33333333e-01, -3.60335543e-17, 5.00000000e-01], [ 3.29964224e-01, 5.90312730e-02, 5.00000000e-01], [ 4.16666667e-01, 1.64710272e-18, 3.57142857e-01], [ 4.11572438e-01, 7.06642841e-02, 3.57142857e-01], [ 4.16666667e-01, -3.15293600e-17, 5.00000000e-01], [ 4.11572438e-01, 7.06642841e-02, 5.00000000e-01], [ 3.22110888e-01, 1.17314261e-01, 3.57142857e-01], [ 3.22110888e-01, 1.17314261e-01, 5.00000000e-01], [ 3.97509754e-01, 1.39831998e-01, 3.57142857e-01], [ 3.97509754e-01, 1.39831998e-01, 5.00000000e-01], [ 3.09957647e-01, 1.74121089e-01, 3.57142857e-01], [ 3.09957647e-01, 1.74121089e-01, 5.00000000e-01], [ 3.74847261e-01, 2.06047393e-01, 3.57142857e-01], [ 3.74847261e-01, 2.06047393e-01, 5.00000000e-01], [ 2.45485487e-01, 9.69400266e-01, 3.57142857e-01], [ 2.25028363e-01, 8.88616910e-01, 3.57142857e-01], [ 2.45485487e-01, 9.69400266e-01, 5.00000000e-01], [ 2.25028363e-01, 8.88616910e-01, 5.00000000e-01], [ 8.25793455e-02, 9.96584493e-01, 3.57142857e-01], [ 7.56977333e-02, 9.13535785e-01, 3.57142857e-01], [ 8.25793455e-02, 9.96584493e-01, 5.00000000e-01], [ 7.56977333e-02, 9.13535785e-01, 5.00000000e-01], [ -8.25793455e-02, 9.96584493e-01, 3.57142857e-01], [ -7.56977333e-02, 9.13535785e-01, 3.57142857e-01], [ -8.25793455e-02, 9.96584493e-01, 5.00000000e-01], [ -7.56977333e-02, 9.13535785e-01, 5.00000000e-01], [ -2.45485487e-01, 9.69400266e-01, 3.57142857e-01], [ -2.25028363e-01, 8.88616910e-01, 3.57142857e-01], [ -2.45485487e-01, 9.69400266e-01, 5.00000000e-01], [ -2.25028363e-01, 8.88616910e-01, 5.00000000e-01], [ -4.01695425e-01, 9.15773327e-01, 3.57142857e-01], [ -3.68220806e-01, 8.39458883e-01, 3.57142857e-01], [ -4.01695425e-01, 9.15773327e-01, 5.00000000e-01], [ -3.68220806e-01, 8.39458883e-01, 5.00000000e-01], [ -5.46948158e-01, 8.37166478e-01, 3.57142857e-01], [ -5.01369145e-01, 7.67402605e-01, 3.57142857e-01], [ -5.46948158e-01, 8.37166478e-01, 5.00000000e-01], [ -5.01369145e-01, 7.67402605e-01, 5.00000000e-01], [ -6.77281572e-01, 7.35723911e-01, 3.57142857e-01], [ -6.20841441e-01, 6.74413585e-01, 3.57142857e-01], [ -6.77281572e-01, 7.35723911e-01, 5.00000000e-01], [ -6.20841441e-01, 6.74413585e-01, 5.00000000e-01], [ 2.04571239e-01, 8.07833555e-01, 3.57142857e-01], [ 2.04571239e-01, 8.07833555e-01, 5.00000000e-01], [ 6.88161212e-02, 8.30487078e-01, 3.57142857e-01], [ 6.88161212e-02, 8.30487078e-01, 5.00000000e-01], [ -6.88161212e-02, 8.30487078e-01, 3.57142857e-01], [ -6.88161212e-02, 8.30487078e-01, 5.00000000e-01], [ -2.04571239e-01, 8.07833555e-01, 3.57142857e-01], [ -2.04571239e-01, 8.07833555e-01, 5.00000000e-01], [ -3.34746187e-01, 7.63144439e-01, 3.57142857e-01], [ -3.34746187e-01, 7.63144439e-01, 5.00000000e-01], [ -4.55790132e-01, 6.97638732e-01, 3.57142857e-01], [ -4.55790132e-01, 6.97638732e-01, 5.00000000e-01], [ -5.64401310e-01, 6.13103259e-01, 3.57142857e-01], [ -5.64401310e-01, 6.13103259e-01, 5.00000000e-01], [ 1.84114115e-01, 7.27050199e-01, 3.57142857e-01], [ 1.84114115e-01, 7.27050199e-01, 5.00000000e-01], [ 6.19345091e-02, 7.47438370e-01, 3.57142857e-01], [ 6.19345091e-02, 7.47438370e-01, 5.00000000e-01], [ -6.19345091e-02, 7.47438370e-01, 3.57142857e-01], [ -6.19345091e-02, 7.47438370e-01, 5.00000000e-01], [ -1.84114115e-01, 7.27050199e-01, 3.57142857e-01], [ -1.84114115e-01, 7.27050199e-01, 5.00000000e-01], [ -3.01271568e-01, 6.86829995e-01, 3.57142857e-01], [ -3.01271568e-01, 6.86829995e-01, 5.00000000e-01], [ -4.10211119e-01, 6.27874859e-01, 3.57142857e-01], [ -4.10211119e-01, 6.27874859e-01, 5.00000000e-01], [ -5.07961179e-01, 5.51792933e-01, 3.57142857e-01], [ -5.07961179e-01, 5.51792933e-01, 5.00000000e-01], [ 1.63656991e-01, 6.46266844e-01, 3.57142857e-01], [ 1.63656991e-01, 6.46266844e-01, 5.00000000e-01], [ 5.50528970e-02, 6.64389662e-01, 3.57142857e-01], [ 5.50528970e-02, 6.64389662e-01, 5.00000000e-01], [ -5.50528970e-02, 6.64389662e-01, 3.57142857e-01], [ -5.50528970e-02, 6.64389662e-01, 5.00000000e-01], [ -1.63656991e-01, 6.46266844e-01, 3.57142857e-01], [ -1.63656991e-01, 6.46266844e-01, 5.00000000e-01], [ -2.67796950e-01, 6.10515551e-01, 3.57142857e-01], [ -2.67796950e-01, 6.10515551e-01, 5.00000000e-01], [ -3.64632105e-01, 5.58110986e-01, 3.57142857e-01], [ -3.64632105e-01, 5.58110986e-01, 5.00000000e-01], [ -4.51521048e-01, 4.90482607e-01, 3.57142857e-01], [ -4.51521048e-01, 4.90482607e-01, 5.00000000e-01], [ 1.43199867e-01, 5.65483488e-01, 3.57142857e-01], [ 1.43199867e-01, 5.65483488e-01, 5.00000000e-01], [ 4.81712849e-02, 5.81340954e-01, 3.57142857e-01], [ 4.81712849e-02, 5.81340954e-01, 5.00000000e-01], [ -4.81712849e-02, 5.81340954e-01, 3.57142857e-01], [ -4.81712849e-02, 5.81340954e-01, 5.00000000e-01], [ -1.43199867e-01, 5.65483488e-01, 3.57142857e-01], [ -1.43199867e-01, 5.65483488e-01, 5.00000000e-01], [ -2.34322331e-01, 5.34201107e-01, 3.57142857e-01], [ -2.34322331e-01, 5.34201107e-01, 5.00000000e-01], [ -3.19053092e-01, 4.88347112e-01, 3.57142857e-01], [ -3.19053092e-01, 4.88347112e-01, 5.00000000e-01], [ -3.95080917e-01, 4.29172281e-01, 3.57142857e-01], [ -3.95080917e-01, 4.29172281e-01, 5.00000000e-01], [ 1.22742744e-01, 4.84700133e-01, 3.57142857e-01], [ 1.22742744e-01, 4.84700133e-01, 5.00000000e-01], [ 4.12896727e-02, 4.98292247e-01, 3.57142857e-01], [ 4.12896727e-02, 4.98292247e-01, 5.00000000e-01], [ -4.12896727e-02, 4.98292247e-01, 3.57142857e-01], [ -4.12896727e-02, 4.98292247e-01, 5.00000000e-01], [ -1.22742744e-01, 4.84700133e-01, 3.57142857e-01], [ -1.22742744e-01, 4.84700133e-01, 5.00000000e-01], [ -2.00847712e-01, 4.57886663e-01, 3.57142857e-01], [ -2.00847712e-01, 4.57886663e-01, 5.00000000e-01], [ -2.73474079e-01, 4.18583239e-01, 3.57142857e-01], [ -2.73474079e-01, 4.18583239e-01, 5.00000000e-01], [ -3.38640786e-01, 3.67861955e-01, 3.57142857e-01], [ -3.38640786e-01, 3.67861955e-01, 5.00000000e-01], [ -1.02283149e-01, 4.15336195e-01, 3.57142857e-01], [ -3.59859419e-02, 4.34695086e-01, 3.57142857e-01], [ -1.02283149e-01, 4.15336195e-01, 5.00000000e-01], [ -3.59859419e-02, 4.34695086e-01, 5.00000000e-01], [ -1.66348287e-01, 3.87163066e-01, 3.57142857e-01], [ -1.66348287e-01, 3.87163066e-01, 5.00000000e-01], [ -2.26761024e-01, 3.50663301e-01, 3.57142857e-01], [ -2.26761024e-01, 3.50663301e-01, 5.00000000e-01], [ -2.82200655e-01, 3.06551629e-01, 3.57142857e-01], [ -2.82200655e-01, 3.06551629e-01, 5.00000000e-01], [ -8.18235534e-02, 3.45972257e-01, 3.57142857e-01], [ -3.06822110e-02, 3.71097926e-01, 3.57142857e-01], [ -8.18235534e-02, 3.45972257e-01, 5.00000000e-01], [ -3.06822110e-02, 3.71097926e-01, 5.00000000e-01], [ -1.31848862e-01, 3.16439469e-01, 3.57142857e-01], [ -1.31848862e-01, 3.16439469e-01, 5.00000000e-01], [ -1.80047970e-01, 2.82743363e-01, 3.57142857e-01], [ -1.80047970e-01, 2.82743363e-01, 5.00000000e-01], [ -2.25760524e-01, 2.45241304e-01, 3.57142857e-01], [ -2.25760524e-01, 2.45241304e-01, 5.00000000e-01], [ -6.13639584e-02, 2.76608319e-01, 3.57142857e-01], [ -2.53784802e-02, 3.07500766e-01, 3.57142857e-01], [ -6.13639584e-02, 2.76608319e-01, 5.00000000e-01], [ -2.53784802e-02, 3.07500766e-01, 5.00000000e-01], [ -9.73494365e-02, 2.45715872e-01, 3.57142857e-01], [ -9.73494365e-02, 2.45715872e-01, 5.00000000e-01], [ -1.33334915e-01, 2.14823425e-01, 3.57142857e-01], [ -1.33334915e-01, 2.14823425e-01, 5.00000000e-01], [ -1.69320393e-01, 1.83930978e-01, 3.57142857e-01], [ -1.69320393e-01, 1.83930978e-01, 5.00000000e-01], [ -1.12880262e-01, 1.22620652e-01, 3.57142857e-01], [ -7.93257664e-02, 1.65019743e-01, 3.57142857e-01], [ -1.12880262e-01, 1.22620652e-01, 5.00000000e-01], [ -7.93257664e-02, 1.65019743e-01, 5.00000000e-01], [ -5.64401310e-02, 6.13103259e-02, 3.57142857e-01], [ -2.53166180e-02, 1.15216062e-01, 3.57142857e-01], [ -5.64401310e-02, 6.13103259e-02, 5.00000000e-01], [ -2.53166180e-02, 1.15216062e-01, 5.00000000e-01], [ -4.57712708e-02, 2.07418835e-01, 3.57142857e-01], [ -4.57712708e-02, 2.07418835e-01, 5.00000000e-01], [ 5.80689493e-03, 1.69121798e-01, 3.57142857e-01], [ 5.80689493e-03, 1.69121798e-01, 5.00000000e-01], [ -1.22167752e-02, 2.49817927e-01, 3.57142857e-01], [ -1.22167752e-02, 2.49817927e-01, 5.00000000e-01], [ 3.69304079e-02, 2.23027534e-01, 3.57142857e-01], [ 3.69304079e-02, 2.23027534e-01, 5.00000000e-01], [ 2.13377203e-02, 2.92217018e-01, 3.57142857e-01], [ 2.13377203e-02, 2.92217018e-01, 5.00000000e-01], [ 6.80539208e-02, 2.76933270e-01, 3.57142857e-01], [ 6.80539208e-02, 2.76933270e-01, 5.00000000e-01], [ 8.62835284e-02, 3.46188891e-01, 3.57142857e-01], [ 8.62835284e-02, 3.46188891e-01, 5.00000000e-01], [ 1.04513136e-01, 4.15444512e-01, 3.57142857e-01], [ 1.04513136e-01, 4.15444512e-01, 5.00000000e-01], [ 2.79883711e-02, 3.60908761e-01, 3.57142857e-01], [ 2.79883711e-02, 3.60908761e-01, 5.00000000e-01], [ 3.46390219e-02, 4.29600504e-01, 3.57142857e-01], [ 3.46390219e-02, 4.29600504e-01, 5.00000000e-01], [ -7.89140509e-01, 6.14212713e-01, 3.57142857e-01], [ -7.23378800e-01, 5.63028320e-01, 3.57142857e-01], [ -7.89140509e-01, 6.14212713e-01, 5.00000000e-01], [ -7.23378800e-01, 5.63028320e-01, 5.00000000e-01], [ -8.79473751e-01, 4.75947393e-01, 3.57142857e-01], [ -8.06184272e-01, 4.36285110e-01, 3.57142857e-01], [ -8.79473751e-01, 4.75947393e-01, 5.00000000e-01], [ -8.06184272e-01, 4.36285110e-01, 5.00000000e-01], [ -9.45817242e-01, 3.24699469e-01, 3.57142857e-01], [ -8.66999138e-01, 2.97641180e-01, 3.57142857e-01], [ -9.45817242e-01, 3.24699469e-01, 5.00000000e-01], [ -8.66999138e-01, 2.97641180e-01, 5.00000000e-01], [ -9.86361303e-01, 1.64594590e-01, 3.57142857e-01], [ -9.04164528e-01, 1.50878374e-01, 3.57142857e-01], [ -9.86361303e-01, 1.64594590e-01, 5.00000000e-01], [ -9.04164528e-01, 1.50878374e-01, 5.00000000e-01], [ -1.00000000e+00, 8.74747714e-17, 3.57142857e-01], [ -9.16666667e-01, 7.64102399e-17, 3.57142857e-01], [ -1.00000000e+00, 1.22464680e-16, 5.00000000e-01], [ -9.16666667e-01, 1.07755096e-16, 5.00000000e-01], [ -9.86361303e-01, -1.64594590e-01, 3.57142857e-01], [ -9.04164528e-01, -1.50878374e-01, 3.57142857e-01], [ -9.86361303e-01, -1.64594590e-01, 5.00000000e-01], [ -9.04164528e-01, -1.50878374e-01, 5.00000000e-01], [ -6.57617091e-01, 5.11843927e-01, 3.57142857e-01], [ -6.57617091e-01, 5.11843927e-01, 5.00000000e-01], [ -7.32894793e-01, 3.96622828e-01, 3.57142857e-01], [ -7.32894793e-01, 3.96622828e-01, 5.00000000e-01], [ -7.88181035e-01, 2.70582891e-01, 3.57142857e-01], [ -7.88181035e-01, 2.70582891e-01, 5.00000000e-01], [ -8.21967753e-01, 1.37162159e-01, 3.57142857e-01], [ -8.21967753e-01, 1.37162159e-01, 5.00000000e-01], [ -8.33333333e-01, 6.54603924e-17, 3.57142857e-01], [ -8.33333333e-01, 9.30455114e-17, 5.00000000e-01], [ -8.21967753e-01, -1.37162159e-01, 3.57142857e-01], [ -8.21967753e-01, -1.37162159e-01, 5.00000000e-01], [ -5.91855382e-01, 4.60659535e-01, 3.57142857e-01], [ -5.91855382e-01, 4.60659535e-01, 5.00000000e-01], [ -6.59605313e-01, 3.56960545e-01, 3.57142857e-01], [ -6.59605313e-01, 3.56960545e-01, 5.00000000e-01], [ -7.09362931e-01, 2.43524602e-01, 3.57142857e-01], [ -7.09362931e-01, 2.43524602e-01, 5.00000000e-01], [ -7.39770978e-01, 1.23445943e-01, 3.57142857e-01], [ -7.39770978e-01, 1.23445943e-01, 5.00000000e-01], [ -7.50000000e-01, 5.42913931e-17, 3.57142857e-01], [ -7.50000000e-01, 7.83359271e-17, 5.00000000e-01], [ -7.39770978e-01, -1.23445943e-01, 3.57142857e-01], [ -7.39770978e-01, -1.23445943e-01, 5.00000000e-01], [ -5.26093673e-01, 4.09475142e-01, 3.57142857e-01], [ -5.26093673e-01, 4.09475142e-01, 5.00000000e-01], [ -5.86315834e-01, 3.17298262e-01, 3.57142857e-01], [ -5.86315834e-01, 3.17298262e-01, 5.00000000e-01], [ -6.30544828e-01, 2.16466313e-01, 3.57142857e-01], [ -6.30544828e-01, 2.16466313e-01, 5.00000000e-01], [ -6.57574202e-01, 1.09729727e-01, 3.57142857e-01], [ -6.57574202e-01, 1.09729727e-01, 5.00000000e-01], [ -6.66666667e-01, 4.30651628e-17, 3.57142857e-01], [ -6.66666667e-01, 6.36263428e-17, 5.00000000e-01], [ -6.57574202e-01, -1.09729727e-01, 3.57142857e-01], [ -6.57574202e-01, -1.09729727e-01, 5.00000000e-01], [ -4.60331964e-01, 3.58290749e-01, 3.57142857e-01], [ -4.60331964e-01, 3.58290749e-01, 5.00000000e-01], [ -5.13026355e-01, 2.77635979e-01, 3.57142857e-01], [ -5.13026355e-01, 2.77635979e-01, 5.00000000e-01], [ -5.51726724e-01, 1.89408024e-01, 3.57142857e-01], [ -5.51726724e-01, 1.89408024e-01, 5.00000000e-01], [ -5.75377427e-01, 9.60135110e-02, 3.57142857e-01], [ -5.75377427e-01, 9.60135110e-02, 5.00000000e-01], [ -5.83333333e-01, 3.18758200e-17, 3.57142857e-01], [ -5.83333333e-01, 4.89167585e-17, 5.00000000e-01], [ -5.75377427e-01, -9.60135110e-02, 3.57142857e-01], [ -5.75377427e-01, -9.60135110e-02, 5.00000000e-01], [ -3.94570255e-01, 3.07106356e-01, 3.57142857e-01], [ -3.94570255e-01, 3.07106356e-01, 5.00000000e-01], [ -4.39736876e-01, 2.37973697e-01, 3.57142857e-01], [ -4.39736876e-01, 2.37973697e-01, 5.00000000e-01], [ -4.72908621e-01, 1.62349735e-01, 3.57142857e-01], [ -4.72908621e-01, 1.62349735e-01, 5.00000000e-01], [ -4.93180652e-01, 8.22972951e-02, 3.57142857e-01], [ -4.93180652e-01, 8.22972951e-02, 5.00000000e-01], [ -5.00000000e-01, 2.07780968e-17, 3.57142857e-01], [ -5.00000000e-01, 3.42071742e-17, 5.00000000e-01], [ -4.93180652e-01, -8.22972951e-02, 3.57142857e-01], [ -4.93180652e-01, -8.22972951e-02, 5.00000000e-01], [ -4.20156583e-01, 7.22539112e-02, 3.57142857e-01], [ -4.11228248e-01, 1.41174836e-01, 3.57142857e-01], [ -4.20156583e-01, 7.22539112e-02, 5.00000000e-01], [ -4.11228248e-01, 1.41174836e-01, 5.00000000e-01], [ -4.20116462e-01, 1.83641595e-03, 3.57142857e-01], [ -4.20116462e-01, 1.83641595e-03, 5.00000000e-01], [ -4.10983876e-01, -6.85810793e-02, 3.57142857e-01], [ -4.10983876e-01, -6.85810793e-02, 5.00000000e-01], [ -3.47132513e-01, 6.22105272e-02, 3.57142857e-01], [ -3.49547876e-01, 1.19999937e-01, 3.57142857e-01], [ -3.47132513e-01, 6.22105272e-02, 5.00000000e-01], [ -3.49547876e-01, 1.19999937e-01, 5.00000000e-01], [ -3.40232923e-01, 3.67283191e-03, 3.57142857e-01], [ -3.40232923e-01, 3.67283191e-03, 5.00000000e-01], [ -3.28787101e-01, -5.48648634e-02, 3.57142857e-01], [ -3.28787101e-01, -5.48648634e-02, 5.00000000e-01], [ -2.74108444e-01, 5.21671433e-02, 3.57142857e-01], [ -2.87867503e-01, 9.88250387e-02, 3.57142857e-01], [ -2.74108444e-01, 5.21671433e-02, 5.00000000e-01], [ -2.87867503e-01, 9.88250387e-02, 5.00000000e-01], [ -2.60349385e-01, 5.50924786e-03, 3.57142857e-01], [ -2.60349385e-01, 5.50924786e-03, 5.00000000e-01], [ -2.46590326e-01, -4.11486476e-02, 3.57142857e-01], [ -2.46590326e-01, -4.11486476e-02, 5.00000000e-01], [ -1.64393551e-01, -2.74324317e-02, 3.57142857e-01], [ -1.92379634e-01, 2.41096072e-02, 3.57142857e-01], [ -1.64393551e-01, -2.74324317e-02, 5.00000000e-01], [ -1.92379634e-01, 2.41096072e-02, 5.00000000e-01], [ -8.21967753e-02, -1.37162159e-02, 3.57142857e-01], [ -1.24409882e-01, 4.27099665e-02, 3.57142857e-01], [ -8.21967753e-02, -1.37162159e-02, 5.00000000e-01], [ -1.24409882e-01, 4.27099665e-02, 5.00000000e-01], [ -2.20365717e-01, 7.56516461e-02, 3.57142857e-01], [ -2.20365717e-01, 7.56516461e-02, 5.00000000e-01], [ -1.66622989e-01, 9.91361489e-02, 3.57142857e-01], [ -1.66622989e-01, 9.91361489e-02, 5.00000000e-01], [ -2.48351800e-01, 1.27193685e-01, 3.57142857e-01], [ -2.48351800e-01, 1.27193685e-01, 5.00000000e-01], [ -2.08836096e-01, 1.55562331e-01, 3.57142857e-01], [ -2.08836096e-01, 1.55562331e-01, 5.00000000e-01], [ -2.70747482e-01, 2.06077006e-01, 3.57142857e-01], [ -2.70747482e-01, 2.06077006e-01, 5.00000000e-01], [ -3.32658869e-01, 2.56591681e-01, 3.57142857e-01], [ -3.32658869e-01, 2.56591681e-01, 5.00000000e-01], [ -3.12146825e-01, 1.64120356e-01, 3.57142857e-01], [ -3.12146825e-01, 1.64120356e-01, 5.00000000e-01], [ -3.75941850e-01, 2.01047026e-01, 3.57142857e-01], [ -3.75941850e-01, 2.01047026e-01, 5.00000000e-01], [ -9.45817242e-01, -3.24699469e-01, 3.57142857e-01], [ -8.66999138e-01, -2.97641180e-01, 3.57142857e-01], [ -9.45817242e-01, -3.24699469e-01, 5.00000000e-01], [ -8.66999138e-01, -2.97641180e-01, 5.00000000e-01], [ -8.79473751e-01, -4.75947393e-01, 3.57142857e-01], [ -8.06184272e-01, -4.36285110e-01, 3.57142857e-01], [ -8.79473751e-01, -4.75947393e-01, 5.00000000e-01], [ -8.06184272e-01, -4.36285110e-01, 5.00000000e-01], [ -7.89140509e-01, -6.14212713e-01, 3.57142857e-01], [ -7.23378800e-01, -5.63028320e-01, 3.57142857e-01], [ -7.89140509e-01, -6.14212713e-01, 5.00000000e-01], [ -7.23378800e-01, -5.63028320e-01, 5.00000000e-01], [ -6.77281572e-01, -7.35723911e-01, 3.57142857e-01], [ -6.20841441e-01, -6.74413585e-01, 3.57142857e-01], [ -6.77281572e-01, -7.35723911e-01, 5.00000000e-01], [ -6.20841441e-01, -6.74413585e-01, 5.00000000e-01], [ -5.46948158e-01, -8.37166478e-01, 3.57142857e-01], [ -5.01369145e-01, -7.67402605e-01, 3.57142857e-01], [ -5.46948158e-01, -8.37166478e-01, 5.00000000e-01], [ -5.01369145e-01, -7.67402605e-01, 5.00000000e-01], [ -4.01695425e-01, -9.15773327e-01, 3.57142857e-01], [ -3.68220806e-01, -8.39458883e-01, 3.57142857e-01], [ -4.01695425e-01, -9.15773327e-01, 5.00000000e-01], [ -3.68220806e-01, -8.39458883e-01, 5.00000000e-01], [ -7.88181035e-01, -2.70582891e-01, 3.57142857e-01], [ -7.88181035e-01, -2.70582891e-01, 5.00000000e-01], [ -7.32894793e-01, -3.96622828e-01, 3.57142857e-01], [ -7.32894793e-01, -3.96622828e-01, 5.00000000e-01], [ -6.57617091e-01, -5.11843927e-01, 3.57142857e-01], [ -6.57617091e-01, -5.11843927e-01, 5.00000000e-01], [ -5.64401310e-01, -6.13103259e-01, 3.57142857e-01], [ -5.64401310e-01, -6.13103259e-01, 5.00000000e-01], [ -4.55790132e-01, -6.97638732e-01, 3.57142857e-01], [ -4.55790132e-01, -6.97638732e-01, 5.00000000e-01], [ -3.34746187e-01, -7.63144439e-01, 3.57142857e-01], [ -3.34746187e-01, -7.63144439e-01, 5.00000000e-01], [ -7.09362931e-01, -2.43524602e-01, 3.57142857e-01], [ -7.09362931e-01, -2.43524602e-01, 5.00000000e-01], [ -6.59605313e-01, -3.56960545e-01, 3.57142857e-01], [ -6.59605313e-01, -3.56960545e-01, 5.00000000e-01], [ -5.91855382e-01, -4.60659535e-01, 3.57142857e-01], [ -5.91855382e-01, -4.60659535e-01, 5.00000000e-01], [ -5.07961179e-01, -5.51792933e-01, 3.57142857e-01], [ -5.07961179e-01, -5.51792933e-01, 5.00000000e-01], [ -4.10211119e-01, -6.27874859e-01, 3.57142857e-01], [ -4.10211119e-01, -6.27874859e-01, 5.00000000e-01], [ -3.01271568e-01, -6.86829995e-01, 3.57142857e-01], [ -3.01271568e-01, -6.86829995e-01, 5.00000000e-01], [ -6.30544828e-01, -2.16466313e-01, 3.57142857e-01], [ -6.30544828e-01, -2.16466313e-01, 5.00000000e-01], [ -5.86315834e-01, -3.17298262e-01, 3.57142857e-01], [ -5.86315834e-01, -3.17298262e-01, 5.00000000e-01], [ -5.26093673e-01, -4.09475142e-01, 3.57142857e-01], [ -5.26093673e-01, -4.09475142e-01, 5.00000000e-01], [ -4.51521048e-01, -4.90482607e-01, 3.57142857e-01], [ -4.51521048e-01, -4.90482607e-01, 5.00000000e-01], [ -3.64632105e-01, -5.58110986e-01, 3.57142857e-01], [ -3.64632105e-01, -5.58110986e-01, 5.00000000e-01], [ -2.67796950e-01, -6.10515551e-01, 3.57142857e-01], [ -2.67796950e-01, -6.10515551e-01, 5.00000000e-01], [ -5.51726724e-01, -1.89408024e-01, 3.57142857e-01], [ -5.51726724e-01, -1.89408024e-01, 5.00000000e-01], [ -5.13026355e-01, -2.77635979e-01, 3.57142857e-01], [ -5.13026355e-01, -2.77635979e-01, 5.00000000e-01], [ -4.60331964e-01, -3.58290749e-01, 3.57142857e-01], [ -4.60331964e-01, -3.58290749e-01, 5.00000000e-01], [ -3.95080917e-01, -4.29172281e-01, 3.57142857e-01], [ -3.95080917e-01, -4.29172281e-01, 5.00000000e-01], [ -3.19053092e-01, -4.88347112e-01, 3.57142857e-01], [ -3.19053092e-01, -4.88347112e-01, 5.00000000e-01], [ -2.34322331e-01, -5.34201107e-01, 3.57142857e-01], [ -2.34322331e-01, -5.34201107e-01, 5.00000000e-01], [ -4.72908621e-01, -1.62349735e-01, 3.57142857e-01], [ -4.72908621e-01, -1.62349735e-01, 5.00000000e-01], [ -4.39736876e-01, -2.37973697e-01, 3.57142857e-01], [ -4.39736876e-01, -2.37973697e-01, 5.00000000e-01], [ -3.94570255e-01, -3.07106356e-01, 3.57142857e-01], [ -3.94570255e-01, -3.07106356e-01, 5.00000000e-01], [ -3.38640786e-01, -3.67861955e-01, 3.57142857e-01], [ -3.38640786e-01, -3.67861955e-01, 5.00000000e-01], [ -2.73474079e-01, -4.18583239e-01, 3.57142857e-01], [ -2.73474079e-01, -4.18583239e-01, 5.00000000e-01], [ -2.00847712e-01, -4.57886663e-01, 3.57142857e-01], [ -2.00847712e-01, -4.57886663e-01, 5.00000000e-01], [ -2.90292421e-01, -3.12221863e-01, 3.57142857e-01], [ -3.43107373e-01, -2.67051188e-01, 3.57142857e-01], [ -2.90292421e-01, -3.12221863e-01, 5.00000000e-01], [ -3.43107373e-01, -2.67051188e-01, 5.00000000e-01], [ -2.31319311e-01, -3.50702994e-01, 3.57142857e-01], [ -2.31319311e-01, -3.50702994e-01, 5.00000000e-01], [ -1.67373094e-01, -3.81572219e-01, 3.57142857e-01], [ -1.67373094e-01, -3.81572219e-01, 5.00000000e-01], [ -2.41944057e-01, -2.56581770e-01, 3.57142857e-01], [ -2.91644492e-01, -2.26996019e-01, 3.57142857e-01], [ -2.41944057e-01, -2.56581770e-01, 5.00000000e-01], [ -2.91644492e-01, -2.26996019e-01, 5.00000000e-01], [ -1.89164543e-01, -2.82822750e-01, 3.57142857e-01], [ -1.89164543e-01, -2.82822750e-01, 5.00000000e-01], [ -1.33898475e-01, -3.05257776e-01, 3.57142857e-01], [ -1.33898475e-01, -3.05257776e-01, 5.00000000e-01], [ -1.93595692e-01, -2.00941678e-01, 3.57142857e-01], [ -2.40181610e-01, -1.86940851e-01, 3.57142857e-01], [ -1.93595692e-01, -2.00941678e-01, 5.00000000e-01], [ -2.40181610e-01, -1.86940851e-01, 5.00000000e-01], [ -1.47009774e-01, -2.14942505e-01, 3.57142857e-01], [ -1.47009774e-01, -2.14942505e-01, 5.00000000e-01], [ -1.00423856e-01, -2.28943332e-01, 3.57142857e-01], [ -1.00423856e-01, -2.28943332e-01, 5.00000000e-01], [ -6.69492374e-02, -1.52628888e-01, 3.57142857e-01], [ -1.25405441e-01, -1.47867075e-01, 3.57142857e-01], [ -6.69492374e-02, -1.52628888e-01, 5.00000000e-01], [ -1.25405441e-01, -1.47867075e-01, 5.00000000e-01], [ -3.34746187e-02, -7.63144439e-02, 3.57142857e-01], [ -1.03801108e-01, -8.07916455e-02, 3.57142857e-01], [ -3.34746187e-02, -7.63144439e-02, 5.00000000e-01], [ -1.03801108e-01, -8.07916455e-02, 5.00000000e-01], [ -1.83861645e-01, -1.43105263e-01, 3.57142857e-01], [ -1.83861645e-01, -1.43105263e-01, 5.00000000e-01], [ -1.74127598e-01, -8.52688471e-02, 3.57142857e-01], [ -1.74127598e-01, -8.52688471e-02, 5.00000000e-01], [ -2.42317849e-01, -1.38343450e-01, 3.57142857e-01], [ -2.42317849e-01, -1.38343450e-01, 5.00000000e-01], [ -2.44454087e-01, -8.97460487e-02, 3.57142857e-01], [ -2.44454087e-01, -8.97460487e-02, 5.00000000e-01], [ -3.20605599e-01, -1.13947277e-01, 3.57142857e-01], [ -3.20605599e-01, -1.13947277e-01, 5.00000000e-01], [ -3.96757110e-01, -1.38148506e-01, 3.57142857e-01], [ -3.96757110e-01, -1.38148506e-01, 5.00000000e-01], [ -3.08124191e-01, -1.71553532e-01, 3.57142857e-01], [ -3.08124191e-01, -1.71553532e-01, 5.00000000e-01], [ -3.73930533e-01, -2.04763614e-01, 3.57142857e-01], [ -3.73930533e-01, -2.04763614e-01, 5.00000000e-01], [ -2.45485487e-01, -9.69400266e-01, 3.57142857e-01], [ -2.25028363e-01, -8.88616910e-01, 3.57142857e-01], [ -2.45485487e-01, -9.69400266e-01, 5.00000000e-01], [ -2.25028363e-01, -8.88616910e-01, 5.00000000e-01], [ -8.25793455e-02, -9.96584493e-01, 3.57142857e-01], [ -7.56977333e-02, -9.13535785e-01, 3.57142857e-01], [ -8.25793455e-02, -9.96584493e-01, 5.00000000e-01], [ -7.56977333e-02, -9.13535785e-01, 5.00000000e-01], [ 8.25793455e-02, -9.96584493e-01, 3.57142857e-01], [ 7.56977333e-02, -9.13535785e-01, 3.57142857e-01], [ 8.25793455e-02, -9.96584493e-01, 5.00000000e-01], [ 7.56977333e-02, -9.13535785e-01, 5.00000000e-01], [ 2.45485487e-01, -9.69400266e-01, 3.57142857e-01], [ 2.25028363e-01, -8.88616910e-01, 3.57142857e-01], [ 2.45485487e-01, -9.69400266e-01, 5.00000000e-01], [ 2.25028363e-01, -8.88616910e-01, 5.00000000e-01], [ 4.01695425e-01, -9.15773327e-01, 3.57142857e-01], [ 3.68220806e-01, -8.39458883e-01, 3.57142857e-01], [ 4.01695425e-01, -9.15773327e-01, 5.00000000e-01], [ 3.68220806e-01, -8.39458883e-01, 5.00000000e-01], [ 5.46948158e-01, -8.37166478e-01, 3.57142857e-01], [ 5.01369145e-01, -7.67402605e-01, 3.57142857e-01], [ 5.46948158e-01, -8.37166478e-01, 5.00000000e-01], [ 5.01369145e-01, -7.67402605e-01, 5.00000000e-01], [ -2.04571239e-01, -8.07833555e-01, 3.57142857e-01], [ -2.04571239e-01, -8.07833555e-01, 5.00000000e-01], [ -6.88161212e-02, -8.30487078e-01, 3.57142857e-01], [ -6.88161212e-02, -8.30487078e-01, 5.00000000e-01], [ 6.88161212e-02, -8.30487078e-01, 3.57142857e-01], [ 6.88161212e-02, -8.30487078e-01, 5.00000000e-01], [ 2.04571239e-01, -8.07833555e-01, 3.57142857e-01], [ 2.04571239e-01, -8.07833555e-01, 5.00000000e-01], [ 3.34746187e-01, -7.63144439e-01, 3.57142857e-01], [ 3.34746187e-01, -7.63144439e-01, 5.00000000e-01], [ 4.55790132e-01, -6.97638732e-01, 3.57142857e-01], [ 4.55790132e-01, -6.97638732e-01, 5.00000000e-01], [ -1.84114115e-01, -7.27050199e-01, 3.57142857e-01], [ -1.84114115e-01, -7.27050199e-01, 5.00000000e-01], [ -6.19345091e-02, -7.47438370e-01, 3.57142857e-01], [ -6.19345091e-02, -7.47438370e-01, 5.00000000e-01], [ 6.19345091e-02, -7.47438370e-01, 3.57142857e-01], [ 6.19345091e-02, -7.47438370e-01, 5.00000000e-01], [ 1.84114115e-01, -7.27050199e-01, 3.57142857e-01], [ 1.84114115e-01, -7.27050199e-01, 5.00000000e-01], [ 3.01271568e-01, -6.86829995e-01, 3.57142857e-01], [ 3.01271568e-01, -6.86829995e-01, 5.00000000e-01], [ 4.10211119e-01, -6.27874859e-01, 3.57142857e-01], [ 4.10211119e-01, -6.27874859e-01, 5.00000000e-01], [ -1.63656991e-01, -6.46266844e-01, 3.57142857e-01], [ -1.63656991e-01, -6.46266844e-01, 5.00000000e-01], [ -5.50528970e-02, -6.64389662e-01, 3.57142857e-01], [ -5.50528970e-02, -6.64389662e-01, 5.00000000e-01], [ 5.50528970e-02, -6.64389662e-01, 3.57142857e-01], [ 5.50528970e-02, -6.64389662e-01, 5.00000000e-01], [ 1.63656991e-01, -6.46266844e-01, 3.57142857e-01], [ 1.63656991e-01, -6.46266844e-01, 5.00000000e-01], [ 2.67796950e-01, -6.10515551e-01, 3.57142857e-01], [ 2.67796950e-01, -6.10515551e-01, 5.00000000e-01], [ 3.64632105e-01, -5.58110986e-01, 3.57142857e-01], [ 3.64632105e-01, -5.58110986e-01, 5.00000000e-01], [ -1.43199867e-01, -5.65483488e-01, 3.57142857e-01], [ -1.43199867e-01, -5.65483488e-01, 5.00000000e-01], [ -4.81712849e-02, -5.81340954e-01, 3.57142857e-01], [ -4.81712849e-02, -5.81340954e-01, 5.00000000e-01], [ 4.81712849e-02, -5.81340954e-01, 3.57142857e-01], [ 4.81712849e-02, -5.81340954e-01, 5.00000000e-01], [ 1.43199867e-01, -5.65483488e-01, 3.57142857e-01], [ 1.43199867e-01, -5.65483488e-01, 5.00000000e-01], [ 2.34322331e-01, -5.34201107e-01, 3.57142857e-01], [ 2.34322331e-01, -5.34201107e-01, 5.00000000e-01], [ 3.19053092e-01, -4.88347112e-01, 3.57142857e-01], [ 3.19053092e-01, -4.88347112e-01, 5.00000000e-01], [ -1.22742744e-01, -4.84700133e-01, 3.57142857e-01], [ -1.22742744e-01, -4.84700133e-01, 5.00000000e-01], [ -4.12896727e-02, -4.98292247e-01, 3.57142857e-01], [ -4.12896727e-02, -4.98292247e-01, 5.00000000e-01], [ 4.12896727e-02, -4.98292247e-01, 3.57142857e-01], [ 4.12896727e-02, -4.98292247e-01, 5.00000000e-01], [ 1.22742744e-01, -4.84700133e-01, 3.57142857e-01], [ 1.22742744e-01, -4.84700133e-01, 5.00000000e-01], [ 2.00847712e-01, -4.57886663e-01, 3.57142857e-01], [ 2.00847712e-01, -4.57886663e-01, 5.00000000e-01], [ 2.73474079e-01, -4.18583239e-01, 3.57142857e-01], [ 2.73474079e-01, -4.18583239e-01, 5.00000000e-01], [ 1.02606772e-01, -4.13792257e-01, 3.57142857e-01], [ 3.59043567e-02, -4.33301147e-01, 3.57142857e-01], [ 1.02606772e-01, -4.13792257e-01, 5.00000000e-01], [ 3.59043567e-02, -4.33301147e-01, 5.00000000e-01], [ 1.67077120e-01, -3.85469130e-01, 3.57142857e-01], [ 1.67077120e-01, -3.85469130e-01, 5.00000000e-01], [ 2.27895066e-01, -3.48819366e-01, 3.57142857e-01], [ 2.27895066e-01, -3.48819366e-01, 5.00000000e-01], [ 8.24708009e-02, -3.42884381e-01, 3.57142857e-01], [ 3.05190406e-02, -3.68310047e-01, 3.57142857e-01], [ 8.24708009e-02, -3.42884381e-01, 5.00000000e-01], [ 3.05190406e-02, -3.68310047e-01, 5.00000000e-01], [ 1.33306527e-01, -3.13051596e-01, 3.57142857e-01], [ 1.33306527e-01, -3.13051596e-01, 5.00000000e-01], [ 1.82316053e-01, -2.79055493e-01, 3.57142857e-01], [ 1.82316053e-01, -2.79055493e-01, 5.00000000e-01], [ 6.23348295e-02, -2.71976505e-01, 3.57142857e-01], [ 2.51337245e-02, -3.03318947e-01, 3.57142857e-01], [ 6.23348295e-02, -2.71976505e-01, 5.00000000e-01], [ 2.51337245e-02, -3.03318947e-01, 5.00000000e-01], [ 9.95359345e-02, -2.40634062e-01, 3.57142857e-01], [ 9.95359345e-02, -2.40634062e-01, 5.00000000e-01], [ 1.36737040e-01, -2.09291620e-01, 3.57142857e-01], [ 1.36737040e-01, -2.09291620e-01, 5.00000000e-01], [ 9.11580264e-02, -1.39527746e-01, 3.57142857e-01], [ 5.51990834e-02, -1.85860856e-01, 3.57142857e-01], [ 9.11580264e-02, -1.39527746e-01, 5.00000000e-01], [ 5.51990834e-02, -1.85860856e-01, 5.00000000e-01], [ 4.55790132e-02, -6.97638732e-02, 3.57142857e-01], [ 1.08622324e-02, -1.31087650e-01, 3.57142857e-01], [ 4.55790132e-02, -6.97638732e-02, 5.00000000e-01], [ 1.08622324e-02, -1.31087650e-01, 5.00000000e-01], [ 1.92401405e-02, -2.32193966e-01, 3.57142857e-01], [ 1.92401405e-02, -2.32193966e-01, 5.00000000e-01], [ -2.38545485e-02, -1.92411427e-01, 3.57142857e-01], [ -2.38545485e-02, -1.92411427e-01, 5.00000000e-01], [ -1.67188024e-02, -2.78527075e-01, 3.57142857e-01], [ -1.67188024e-02, -2.78527075e-01, 5.00000000e-01], [ -5.85713293e-02, -2.53735203e-01, 3.57142857e-01], [ -5.85713293e-02, -2.53735203e-01, 5.00000000e-01], [ -7.99618007e-02, -3.30723513e-01, 3.57142857e-01], [ -7.99618007e-02, -3.30723513e-01, 5.00000000e-01], [ -1.01352272e-01, -4.07711823e-01, 3.57142857e-01], [ -1.01352272e-01, -4.07711823e-01, 5.00000000e-01], [ -2.49090925e-02, -3.51782132e-01, 3.57142857e-01], [ -2.49090925e-02, -3.51782132e-01, 5.00000000e-01], [ -3.30993826e-02, -4.25037189e-01, 3.57142857e-01], [ -3.30993826e-02, -4.25037189e-01, 5.00000000e-01], [ 6.77281572e-01, -7.35723911e-01, 3.57142857e-01], [ 6.20841441e-01, -6.74413585e-01, 3.57142857e-01], [ 6.77281572e-01, -7.35723911e-01, 5.00000000e-01], [ 6.20841441e-01, -6.74413585e-01, 5.00000000e-01], [ 7.89140509e-01, -6.14212713e-01, 3.57142857e-01], [ 7.23378800e-01, -5.63028320e-01, 3.57142857e-01], [ 7.89140509e-01, -6.14212713e-01, 5.00000000e-01], [ 7.23378800e-01, -5.63028320e-01, 5.00000000e-01], [ 8.79473751e-01, -4.75947393e-01, 3.57142857e-01], [ 8.06184272e-01, -4.36285110e-01, 3.57142857e-01], [ 8.79473751e-01, -4.75947393e-01, 5.00000000e-01], [ 8.06184272e-01, -4.36285110e-01, 5.00000000e-01], [ 9.45817242e-01, -3.24699469e-01, 3.57142857e-01], [ 8.66999138e-01, -2.97641180e-01, 3.57142857e-01], [ 9.45817242e-01, -3.24699469e-01, 5.00000000e-01], [ 8.66999138e-01, -2.97641180e-01, 5.00000000e-01], [ 9.86361303e-01, -1.64594590e-01, 3.57142857e-01], [ 9.04164528e-01, -1.50878374e-01, 3.57142857e-01], [ 9.86361303e-01, -1.64594590e-01, 5.00000000e-01], [ 9.04164528e-01, -1.50878374e-01, 5.00000000e-01], [ 5.64401310e-01, -6.13103259e-01, 3.57142857e-01], [ 5.64401310e-01, -6.13103259e-01, 5.00000000e-01], [ 6.57617091e-01, -5.11843927e-01, 3.57142857e-01], [ 6.57617091e-01, -5.11843927e-01, 5.00000000e-01], [ 7.32894793e-01, -3.96622828e-01, 3.57142857e-01], [ 7.32894793e-01, -3.96622828e-01, 5.00000000e-01], [ 7.88181035e-01, -2.70582891e-01, 3.57142857e-01], [ 7.88181035e-01, -2.70582891e-01, 5.00000000e-01], [ 8.21967753e-01, -1.37162159e-01, 3.57142857e-01], [ 8.21967753e-01, -1.37162159e-01, 5.00000000e-01], [ 5.07961179e-01, -5.51792933e-01, 3.57142857e-01], [ 5.07961179e-01, -5.51792933e-01, 5.00000000e-01], [ 5.91855382e-01, -4.60659535e-01, 3.57142857e-01], [ 5.91855382e-01, -4.60659535e-01, 5.00000000e-01], [ 6.59605313e-01, -3.56960545e-01, 3.57142857e-01], [ 6.59605313e-01, -3.56960545e-01, 5.00000000e-01], [ 7.09362931e-01, -2.43524602e-01, 3.57142857e-01], [ 7.09362931e-01, -2.43524602e-01, 5.00000000e-01], [ 7.39770978e-01, -1.23445943e-01, 3.57142857e-01], [ 7.39770978e-01, -1.23445943e-01, 5.00000000e-01], [ 4.51521048e-01, -4.90482607e-01, 3.57142857e-01], [ 4.51521048e-01, -4.90482607e-01, 5.00000000e-01], [ 5.26093673e-01, -4.09475142e-01, 3.57142857e-01], [ 5.26093673e-01, -4.09475142e-01, 5.00000000e-01], [ 5.86315834e-01, -3.17298262e-01, 3.57142857e-01], [ 5.86315834e-01, -3.17298262e-01, 5.00000000e-01], [ 6.30544828e-01, -2.16466313e-01, 3.57142857e-01], [ 6.30544828e-01, -2.16466313e-01, 5.00000000e-01], [ 6.57574202e-01, -1.09729727e-01, 3.57142857e-01], [ 6.57574202e-01, -1.09729727e-01, 5.00000000e-01], [ 3.95080917e-01, -4.29172281e-01, 3.57142857e-01], [ 3.95080917e-01, -4.29172281e-01, 5.00000000e-01], [ 4.60331964e-01, -3.58290749e-01, 3.57142857e-01], [ 4.60331964e-01, -3.58290749e-01, 5.00000000e-01], [ 5.13026355e-01, -2.77635979e-01, 3.57142857e-01], [ 5.13026355e-01, -2.77635979e-01, 5.00000000e-01], [ 5.51726724e-01, -1.89408024e-01, 3.57142857e-01], [ 5.51726724e-01, -1.89408024e-01, 5.00000000e-01], [ 5.75377427e-01, -9.60135110e-02, 3.57142857e-01], [ 5.75377427e-01, -9.60135110e-02, 5.00000000e-01], [ 3.38640786e-01, -3.67861955e-01, 3.57142857e-01], [ 3.38640786e-01, -3.67861955e-01, 5.00000000e-01], [ 3.94570255e-01, -3.07106356e-01, 3.57142857e-01], [ 3.94570255e-01, -3.07106356e-01, 5.00000000e-01], [ 4.39736876e-01, -2.37973697e-01, 3.57142857e-01], [ 4.39736876e-01, -2.37973697e-01, 5.00000000e-01], [ 4.72908621e-01, -1.62349735e-01, 3.57142857e-01], [ 4.72908621e-01, -1.62349735e-01, 5.00000000e-01], [ 4.93180652e-01, -8.22972951e-02, 3.57142857e-01], [ 4.93180652e-01, -8.22972951e-02, 5.00000000e-01], [ 4.02533591e-01, -1.40423963e-01, 3.57142857e-01], [ 3.82383017e-01, -2.06935340e-01, 3.57142857e-01], [ 4.02533591e-01, -1.40423963e-01, 5.00000000e-01], [ 3.82383017e-01, -2.06935340e-01, 5.00000000e-01], [ 4.14084357e-01, -7.09602665e-02, 3.57142857e-01], [ 4.14084357e-01, -7.09602665e-02, 5.00000000e-01], [ 3.32158562e-01, -1.18498191e-01, 3.57142857e-01], [ 3.25029158e-01, -1.75896984e-01, 3.57142857e-01], [ 3.32158562e-01, -1.18498191e-01, 5.00000000e-01], [ 3.25029158e-01, -1.75896984e-01, 5.00000000e-01], [ 3.34988061e-01, -5.96232379e-02, 3.57142857e-01], [ 3.34988061e-01, -5.96232379e-02, 5.00000000e-01], [ 2.61783533e-01, -9.65724185e-02, 3.57142857e-01], [ 2.67675299e-01, -1.44858628e-01, 3.57142857e-01], [ 2.61783533e-01, -9.65724185e-02, 5.00000000e-01], [ 2.67675299e-01, -1.44858628e-01, 5.00000000e-01], [ 2.55891766e-01, -4.82862093e-02, 3.57142857e-01], [ 2.55891766e-01, -4.82862093e-02, 5.00000000e-01], [ 1.85787515e-01, -5.54454306e-02, 3.57142857e-01], [ 1.85787515e-01, -5.54454306e-02, 5.00000000e-01], [ 1.15683264e-01, -6.26046519e-02, 3.57142857e-01], [ 1.15683264e-01, -6.26046519e-02, 5.00000000e-01], [ 2.04908364e-01, -1.10890861e-01, 3.57142857e-01], [ 2.04908364e-01, -1.10890861e-01, 5.00000000e-01], [ 1.48033195e-01, -1.25209304e-01, 3.57142857e-01], [ 1.48033195e-01, -1.25209304e-01, 5.00000000e-01], [ 2.24029213e-01, -1.66336292e-01, 3.57142857e-01], [ 2.24029213e-01, -1.66336292e-01, 5.00000000e-01], [ 1.80383126e-01, -1.87813956e-01, 3.57142857e-01], [ 1.80383126e-01, -1.87813956e-01, 5.00000000e-01], [ 2.33135679e-01, -2.47829956e-01, 3.57142857e-01], [ 2.33135679e-01, -2.47829956e-01, 5.00000000e-01], [ 2.85888233e-01, -3.07845955e-01, 3.57142857e-01], [ 2.85888233e-01, -3.07845955e-01, 5.00000000e-01], [ 2.80876227e-01, -2.13259647e-01, 3.57142857e-01], [ 2.80876227e-01, -2.13259647e-01, 5.00000000e-01], [ 3.37723241e-01, -2.60183001e-01, 3.57142857e-01], [ 3.37723241e-01, -2.60183001e-01, 5.00000000e-01], [ 1.00000000e+00, 0.00000000e+00, 2.14285714e-01], [ 9.86361303e-01, 1.64594590e-01, 2.14285714e-01], [ 9.04164528e-01, 1.50878374e-01, 2.14285714e-01], [ 9.16666667e-01, 7.92157468e-17, 2.14285714e-01], [ 9.45817242e-01, 3.24699469e-01, 2.14285714e-01], [ 8.66999138e-01, 2.97641180e-01, 2.14285714e-01], [ 8.79473751e-01, 4.75947393e-01, 2.14285714e-01], [ 8.06184272e-01, 4.36285110e-01, 2.14285714e-01], [ 7.89140509e-01, 6.14212713e-01, 2.14285714e-01], [ 7.23378800e-01, 5.63028320e-01, 2.14285714e-01], [ 6.77281572e-01, 7.35723911e-01, 2.14285714e-01], [ 6.20841441e-01, 6.74413585e-01, 2.14285714e-01], [ 5.46948158e-01, 8.37166478e-01, 2.14285714e-01], [ 5.01369145e-01, 7.67402605e-01, 2.14285714e-01], [ 4.01695425e-01, 9.15773327e-01, 2.14285714e-01], [ 3.68220806e-01, 8.39458883e-01, 2.14285714e-01], [ 8.21967753e-01, 1.37162159e-01, 2.14285714e-01], [ 8.33333333e-01, 1.09189934e-16, 2.14285714e-01], [ 7.88181035e-01, 2.70582891e-01, 2.14285714e-01], [ 7.32894793e-01, 3.96622828e-01, 2.14285714e-01], [ 6.57617091e-01, 5.11843927e-01, 2.14285714e-01], [ 5.64401310e-01, 6.13103259e-01, 2.14285714e-01], [ 4.55790132e-01, 6.97638732e-01, 2.14285714e-01], [ 3.34746187e-01, 7.63144439e-01, 2.14285714e-01], [ 7.39770978e-01, 1.23445943e-01, 2.14285714e-01], [ 7.50000000e-01, 1.08072838e-16, 2.14285714e-01], [ 7.09362931e-01, 2.43524602e-01, 2.14285714e-01], [ 6.59605313e-01, 3.56960545e-01, 2.14285714e-01], [ 5.91855382e-01, 4.60659535e-01, 2.14285714e-01], [ 5.07961179e-01, 5.51792933e-01, 2.14285714e-01], [ 4.10211119e-01, 6.27874859e-01, 2.14285714e-01], [ 3.01271568e-01, 6.86829995e-01, 2.14285714e-01], [ 6.57574202e-01, 1.09729727e-01, 2.14285714e-01], [ 6.66666667e-01, 9.87992924e-17, 2.14285714e-01], [ 6.30544828e-01, 2.16466313e-01, 2.14285714e-01], [ 5.86315834e-01, 3.17298262e-01, 2.14285714e-01], [ 5.26093673e-01, 4.09475142e-01, 2.14285714e-01], [ 4.51521048e-01, 4.90482607e-01, 2.14285714e-01], [ 3.64632105e-01, 5.58110986e-01, 2.14285714e-01], [ 2.67796950e-01, 6.10515551e-01, 2.14285714e-01], [ 5.75377427e-01, 9.60135110e-02, 2.14285714e-01], [ 5.83333333e-01, 8.67681639e-17, 2.14285714e-01], [ 5.51726724e-01, 1.89408024e-01, 2.14285714e-01], [ 5.13026355e-01, 2.77635979e-01, 2.14285714e-01], [ 4.60331964e-01, 3.58290749e-01, 2.14285714e-01], [ 3.95080917e-01, 4.29172281e-01, 2.14285714e-01], [ 3.19053092e-01, 4.88347112e-01, 2.14285714e-01], [ 2.34322331e-01, 5.34201107e-01, 2.14285714e-01], [ 4.93180652e-01, 8.22972951e-02, 2.14285714e-01], [ 5.00000000e-01, 7.34814867e-17, 2.14285714e-01], [ 4.72908621e-01, 1.62349735e-01, 2.14285714e-01], [ 4.39736876e-01, 2.37973697e-01, 2.14285714e-01], [ 3.94570255e-01, 3.07106356e-01, 2.14285714e-01], [ 3.38640786e-01, 3.67861955e-01, 2.14285714e-01], [ 2.73474079e-01, 4.18583239e-01, 2.14285714e-01], [ 2.00847712e-01, 4.57886663e-01, 2.14285714e-01], [ 2.92606991e-01, 3.16445261e-01, 2.14285714e-01], [ 3.44188184e-01, 2.67935253e-01, 2.14285714e-01], [ 2.34867640e-01, 3.58265725e-01, 2.14285714e-01], [ 1.72155182e-01, 3.92474283e-01, 2.14285714e-01], [ 2.46573197e-01, 2.65028567e-01, 2.14285714e-01], [ 2.93806114e-01, 2.28764151e-01, 2.14285714e-01], [ 1.96261201e-01, 2.97948211e-01, 2.14285714e-01], [ 1.43462652e-01, 3.27061902e-01, 2.14285714e-01], [ 2.00539403e-01, 2.13611872e-01, 2.14285714e-01], [ 2.43424043e-01, 1.89593048e-01, 2.14285714e-01], [ 1.57654762e-01, 2.37630697e-01, 2.14285714e-01], [ 1.14770121e-01, 2.61649522e-01, 2.14285714e-01], [ 8.60775910e-02, 1.96237141e-01, 2.14285714e-01], [ 1.39074405e-01, 1.78223023e-01, 2.14285714e-01], [ 5.73850607e-02, 1.30824761e-01, 2.14285714e-01], [ 1.20494048e-01, 1.18815349e-01, 2.14285714e-01], [ 2.86925303e-02, 6.54123805e-02, 2.14285714e-01], [ 1.01913690e-01, 5.94076743e-02, 2.14285714e-01], [ -2.17036080e-17, -1.20233781e-17, 2.14285714e-01], [ 8.33333333e-02, 2.13668869e-18, 2.14285714e-01], [ 1.92071219e-01, 1.60208904e-01, 2.14285714e-01], [ 1.83603035e-01, 1.06805936e-01, 2.14285714e-01], [ 1.75134851e-01, 5.34029681e-02, 2.14285714e-01], [ 1.66666667e-01, 1.64436543e-17, 2.14285714e-01], [ 2.45068032e-01, 1.42194786e-01, 2.14285714e-01], [ 2.46712022e-01, 9.47965238e-02, 2.14285714e-01], [ 2.48356011e-01, 4.73982619e-02, 2.14285714e-01], [ 2.50000000e-01, 3.08454053e-17, 2.14285714e-01], [ 3.33333333e-01, 4.52549212e-17, 2.14285714e-01], [ 3.29964224e-01, 5.90312730e-02, 2.14285714e-01], [ 4.16666667e-01, 5.95357666e-17, 2.14285714e-01], [ 4.11572438e-01, 7.06642841e-02, 2.14285714e-01], [ 3.22110888e-01, 1.17314261e-01, 2.14285714e-01], [ 3.97509754e-01, 1.39831998e-01, 2.14285714e-01], [ 3.09957647e-01, 1.74121089e-01, 2.14285714e-01], [ 3.74847261e-01, 2.06047393e-01, 2.14285714e-01], [ 2.45485487e-01, 9.69400266e-01, 2.14285714e-01], [ 2.25028363e-01, 8.88616910e-01, 2.14285714e-01], [ 8.25793455e-02, 9.96584493e-01, 2.14285714e-01], [ 7.56977333e-02, 9.13535785e-01, 2.14285714e-01], [ -8.25793455e-02, 9.96584493e-01, 2.14285714e-01], [ -7.56977333e-02, 9.13535785e-01, 2.14285714e-01], [ -2.45485487e-01, 9.69400266e-01, 2.14285714e-01], [ -2.25028363e-01, 8.88616910e-01, 2.14285714e-01], [ -4.01695425e-01, 9.15773327e-01, 2.14285714e-01], [ -3.68220806e-01, 8.39458883e-01, 2.14285714e-01], [ -5.46948158e-01, 8.37166478e-01, 2.14285714e-01], [ -5.01369145e-01, 7.67402605e-01, 2.14285714e-01], [ -6.77281572e-01, 7.35723911e-01, 2.14285714e-01], [ -6.20841441e-01, 6.74413585e-01, 2.14285714e-01], [ 2.04571239e-01, 8.07833555e-01, 2.14285714e-01], [ 6.88161212e-02, 8.30487078e-01, 2.14285714e-01], [ -6.88161212e-02, 8.30487078e-01, 2.14285714e-01], [ -2.04571239e-01, 8.07833555e-01, 2.14285714e-01], [ -3.34746187e-01, 7.63144439e-01, 2.14285714e-01], [ -4.55790132e-01, 6.97638732e-01, 2.14285714e-01], [ -5.64401310e-01, 6.13103259e-01, 2.14285714e-01], [ 1.84114115e-01, 7.27050199e-01, 2.14285714e-01], [ 6.19345091e-02, 7.47438370e-01, 2.14285714e-01], [ -6.19345091e-02, 7.47438370e-01, 2.14285714e-01], [ -1.84114115e-01, 7.27050199e-01, 2.14285714e-01], [ -3.01271568e-01, 6.86829995e-01, 2.14285714e-01], [ -4.10211119e-01, 6.27874859e-01, 2.14285714e-01], [ -5.07961179e-01, 5.51792933e-01, 2.14285714e-01], [ 1.63656991e-01, 6.46266844e-01, 2.14285714e-01], [ 5.50528970e-02, 6.64389662e-01, 2.14285714e-01], [ -5.50528970e-02, 6.64389662e-01, 2.14285714e-01], [ -1.63656991e-01, 6.46266844e-01, 2.14285714e-01], [ -2.67796950e-01, 6.10515551e-01, 2.14285714e-01], [ -3.64632105e-01, 5.58110986e-01, 2.14285714e-01], [ -4.51521048e-01, 4.90482607e-01, 2.14285714e-01], [ 1.43199867e-01, 5.65483488e-01, 2.14285714e-01], [ 4.81712849e-02, 5.81340954e-01, 2.14285714e-01], [ -4.81712849e-02, 5.81340954e-01, 2.14285714e-01], [ -1.43199867e-01, 5.65483488e-01, 2.14285714e-01], [ -2.34322331e-01, 5.34201107e-01, 2.14285714e-01], [ -3.19053092e-01, 4.88347112e-01, 2.14285714e-01], [ -3.95080917e-01, 4.29172281e-01, 2.14285714e-01], [ 1.22742744e-01, 4.84700133e-01, 2.14285714e-01], [ 4.12896727e-02, 4.98292247e-01, 2.14285714e-01], [ -4.12896727e-02, 4.98292247e-01, 2.14285714e-01], [ -1.22742744e-01, 4.84700133e-01, 2.14285714e-01], [ -2.00847712e-01, 4.57886663e-01, 2.14285714e-01], [ -2.73474079e-01, 4.18583239e-01, 2.14285714e-01], [ -3.38640786e-01, 3.67861955e-01, 2.14285714e-01], [ -1.02283149e-01, 4.15336195e-01, 2.14285714e-01], [ -3.59859419e-02, 4.34695086e-01, 2.14285714e-01], [ -1.66348287e-01, 3.87163066e-01, 2.14285714e-01], [ -2.26761024e-01, 3.50663301e-01, 2.14285714e-01], [ -2.82200655e-01, 3.06551629e-01, 2.14285714e-01], [ -8.18235534e-02, 3.45972257e-01, 2.14285714e-01], [ -3.06822110e-02, 3.71097926e-01, 2.14285714e-01], [ -1.31848862e-01, 3.16439469e-01, 2.14285714e-01], [ -1.80047970e-01, 2.82743363e-01, 2.14285714e-01], [ -2.25760524e-01, 2.45241304e-01, 2.14285714e-01], [ -6.13639584e-02, 2.76608319e-01, 2.14285714e-01], [ -2.53784802e-02, 3.07500766e-01, 2.14285714e-01], [ -9.73494365e-02, 2.45715872e-01, 2.14285714e-01], [ -1.33334915e-01, 2.14823425e-01, 2.14285714e-01], [ -1.69320393e-01, 1.83930978e-01, 2.14285714e-01], [ -1.12880262e-01, 1.22620652e-01, 2.14285714e-01], [ -7.93257664e-02, 1.65019743e-01, 2.14285714e-01], [ -5.64401310e-02, 6.13103259e-02, 2.14285714e-01], [ -2.53166180e-02, 1.15216062e-01, 2.14285714e-01], [ -4.57712708e-02, 2.07418835e-01, 2.14285714e-01], [ 5.80689493e-03, 1.69121798e-01, 2.14285714e-01], [ -1.22167752e-02, 2.49817927e-01, 2.14285714e-01], [ 3.69304079e-02, 2.23027534e-01, 2.14285714e-01], [ 2.13377203e-02, 2.92217018e-01, 2.14285714e-01], [ 6.80539208e-02, 2.76933270e-01, 2.14285714e-01], [ 8.62835284e-02, 3.46188891e-01, 2.14285714e-01], [ 1.04513136e-01, 4.15444512e-01, 2.14285714e-01], [ 2.79883711e-02, 3.60908761e-01, 2.14285714e-01], [ 3.46390219e-02, 4.29600504e-01, 2.14285714e-01], [ -7.89140509e-01, 6.14212713e-01, 2.14285714e-01], [ -7.23378800e-01, 5.63028320e-01, 2.14285714e-01], [ -8.79473751e-01, 4.75947393e-01, 2.14285714e-01], [ -8.06184272e-01, 4.36285110e-01, 2.14285714e-01], [ -9.45817242e-01, 3.24699469e-01, 2.14285714e-01], [ -8.66999138e-01, 2.97641180e-01, 2.14285714e-01], [ -9.86361303e-01, 1.64594590e-01, 2.14285714e-01], [ -9.04164528e-01, 1.50878374e-01, 2.14285714e-01], [ -1.00000000e+00, 5.24848628e-17, 2.14285714e-01], [ -9.16666667e-01, 5.61432674e-17, 2.14285714e-01], [ -9.86361303e-01, -1.64594590e-01, 2.14285714e-01], [ -9.04164528e-01, -1.50878374e-01, 2.14285714e-01], [ -6.57617091e-01, 5.11843927e-01, 2.14285714e-01], [ -7.32894793e-01, 3.96622828e-01, 2.14285714e-01], [ -7.88181035e-01, 2.70582891e-01, 2.14285714e-01], [ -8.21967753e-01, 1.37162159e-01, 2.14285714e-01], [ -8.33333333e-01, 4.98053016e-17, 2.14285714e-01], [ -8.21967753e-01, -1.37162159e-01, 2.14285714e-01], [ -5.91855382e-01, 4.60659535e-01, 2.14285714e-01], [ -6.59605313e-01, 3.56960545e-01, 2.14285714e-01], [ -7.09362931e-01, 2.43524602e-01, 2.14285714e-01], [ -7.39770978e-01, 1.23445943e-01, 2.14285714e-01], [ -7.50000000e-01, 4.30290320e-17, 2.14285714e-01], [ -7.39770978e-01, -1.23445943e-01, 2.14285714e-01], [ -5.26093673e-01, 4.09475142e-01, 2.14285714e-01], [ -5.86315834e-01, 3.17298262e-01, 2.14285714e-01], [ -6.30544828e-01, 2.16466313e-01, 2.14285714e-01], [ -6.57574202e-01, 1.09729727e-01, 2.14285714e-01], [ -6.66666667e-01, 3.61383006e-17, 2.14285714e-01], [ -6.57574202e-01, -1.09729727e-01, 2.14285714e-01], [ -4.60331964e-01, 3.58290749e-01, 2.14285714e-01], [ -5.13026355e-01, 2.77635979e-01, 2.14285714e-01], [ -5.51726724e-01, 1.89408024e-01, 2.14285714e-01], [ -5.75377427e-01, 9.60135110e-02, 2.14285714e-01], [ -5.83333333e-01, 2.93213442e-17, 2.14285714e-01], [ -5.75377427e-01, -9.60135110e-02, 2.14285714e-01], [ -3.94570255e-01, 3.07106356e-01, 2.14285714e-01], [ -4.39736876e-01, 2.37973697e-01, 2.14285714e-01], [ -4.72908621e-01, 1.62349735e-01, 2.14285714e-01], [ -4.93180652e-01, 8.22972951e-02, 2.14285714e-01], [ -5.00000000e-01, 2.26876270e-17, 2.14285714e-01], [ -4.93180652e-01, -8.22972951e-02, 2.14285714e-01], [ -4.20156583e-01, 7.22539112e-02, 2.14285714e-01], [ -4.11228248e-01, 1.41174836e-01, 2.14285714e-01], [ -4.20116462e-01, 1.83641595e-03, 2.14285714e-01], [ -4.10983876e-01, -6.85810793e-02, 2.14285714e-01], [ -3.47132513e-01, 6.22105272e-02, 2.14285714e-01], [ -3.49547876e-01, 1.19999937e-01, 2.14285714e-01], [ -3.40232923e-01, 3.67283191e-03, 2.14285714e-01], [ -3.28787101e-01, -5.48648634e-02, 2.14285714e-01], [ -2.74108444e-01, 5.21671433e-02, 2.14285714e-01], [ -2.87867503e-01, 9.88250387e-02, 2.14285714e-01], [ -2.60349385e-01, 5.50924786e-03, 2.14285714e-01], [ -2.46590326e-01, -4.11486476e-02, 2.14285714e-01], [ -1.64393551e-01, -2.74324317e-02, 2.14285714e-01], [ -1.92379634e-01, 2.41096072e-02, 2.14285714e-01], [ -8.21967753e-02, -1.37162159e-02, 2.14285714e-01], [ -1.24409882e-01, 4.27099665e-02, 2.14285714e-01], [ -2.20365717e-01, 7.56516461e-02, 2.14285714e-01], [ -1.66622989e-01, 9.91361489e-02, 2.14285714e-01], [ -2.48351800e-01, 1.27193685e-01, 2.14285714e-01], [ -2.08836096e-01, 1.55562331e-01, 2.14285714e-01], [ -2.70747482e-01, 2.06077006e-01, 2.14285714e-01], [ -3.32658869e-01, 2.56591681e-01, 2.14285714e-01], [ -3.12146825e-01, 1.64120356e-01, 2.14285714e-01], [ -3.75941850e-01, 2.01047026e-01, 2.14285714e-01], [ -9.45817242e-01, -3.24699469e-01, 2.14285714e-01], [ -8.66999138e-01, -2.97641180e-01, 2.14285714e-01], [ -8.79473751e-01, -4.75947393e-01, 2.14285714e-01], [ -8.06184272e-01, -4.36285110e-01, 2.14285714e-01], [ -7.89140509e-01, -6.14212713e-01, 2.14285714e-01], [ -7.23378800e-01, -5.63028320e-01, 2.14285714e-01], [ -6.77281572e-01, -7.35723911e-01, 2.14285714e-01], [ -6.20841441e-01, -6.74413585e-01, 2.14285714e-01], [ -5.46948158e-01, -8.37166478e-01, 2.14285714e-01], [ -5.01369145e-01, -7.67402605e-01, 2.14285714e-01], [ -4.01695425e-01, -9.15773327e-01, 2.14285714e-01], [ -3.68220806e-01, -8.39458883e-01, 2.14285714e-01], [ -7.88181035e-01, -2.70582891e-01, 2.14285714e-01], [ -7.32894793e-01, -3.96622828e-01, 2.14285714e-01], [ -6.57617091e-01, -5.11843927e-01, 2.14285714e-01], [ -5.64401310e-01, -6.13103259e-01, 2.14285714e-01], [ -4.55790132e-01, -6.97638732e-01, 2.14285714e-01], [ -3.34746187e-01, -7.63144439e-01, 2.14285714e-01], [ -7.09362931e-01, -2.43524602e-01, 2.14285714e-01], [ -6.59605313e-01, -3.56960545e-01, 2.14285714e-01], [ -5.91855382e-01, -4.60659535e-01, 2.14285714e-01], [ -5.07961179e-01, -5.51792933e-01, 2.14285714e-01], [ -4.10211119e-01, -6.27874859e-01, 2.14285714e-01], [ -3.01271568e-01, -6.86829995e-01, 2.14285714e-01], [ -6.30544828e-01, -2.16466313e-01, 2.14285714e-01], [ -5.86315834e-01, -3.17298262e-01, 2.14285714e-01], [ -5.26093673e-01, -4.09475142e-01, 2.14285714e-01], [ -4.51521048e-01, -4.90482607e-01, 2.14285714e-01], [ -3.64632105e-01, -5.58110986e-01, 2.14285714e-01], [ -2.67796950e-01, -6.10515551e-01, 2.14285714e-01], [ -5.51726724e-01, -1.89408024e-01, 2.14285714e-01], [ -5.13026355e-01, -2.77635979e-01, 2.14285714e-01], [ -4.60331964e-01, -3.58290749e-01, 2.14285714e-01], [ -3.95080917e-01, -4.29172281e-01, 2.14285714e-01], [ -3.19053092e-01, -4.88347112e-01, 2.14285714e-01], [ -2.34322331e-01, -5.34201107e-01, 2.14285714e-01], [ -4.72908621e-01, -1.62349735e-01, 2.14285714e-01], [ -4.39736876e-01, -2.37973697e-01, 2.14285714e-01], [ -3.94570255e-01, -3.07106356e-01, 2.14285714e-01], [ -3.38640786e-01, -3.67861955e-01, 2.14285714e-01], [ -2.73474079e-01, -4.18583239e-01, 2.14285714e-01], [ -2.00847712e-01, -4.57886663e-01, 2.14285714e-01], [ -2.90292421e-01, -3.12221863e-01, 2.14285714e-01], [ -3.43107373e-01, -2.67051188e-01, 2.14285714e-01], [ -2.31319311e-01, -3.50702994e-01, 2.14285714e-01], [ -1.67373094e-01, -3.81572219e-01, 2.14285714e-01], [ -2.41944057e-01, -2.56581770e-01, 2.14285714e-01], [ -2.91644492e-01, -2.26996019e-01, 2.14285714e-01], [ -1.89164543e-01, -2.82822750e-01, 2.14285714e-01], [ -1.33898475e-01, -3.05257776e-01, 2.14285714e-01], [ -1.93595692e-01, -2.00941678e-01, 2.14285714e-01], [ -2.40181610e-01, -1.86940851e-01, 2.14285714e-01], [ -1.47009774e-01, -2.14942505e-01, 2.14285714e-01], [ -1.00423856e-01, -2.28943332e-01, 2.14285714e-01], [ -6.69492374e-02, -1.52628888e-01, 2.14285714e-01], [ -1.25405441e-01, -1.47867075e-01, 2.14285714e-01], [ -3.34746187e-02, -7.63144439e-02, 2.14285714e-01], [ -1.03801108e-01, -8.07916455e-02, 2.14285714e-01], [ -1.83861645e-01, -1.43105263e-01, 2.14285714e-01], [ -1.74127598e-01, -8.52688471e-02, 2.14285714e-01], [ -2.42317849e-01, -1.38343450e-01, 2.14285714e-01], [ -2.44454087e-01, -8.97460487e-02, 2.14285714e-01], [ -3.20605599e-01, -1.13947277e-01, 2.14285714e-01], [ -3.96757110e-01, -1.38148506e-01, 2.14285714e-01], [ -3.08124191e-01, -1.71553532e-01, 2.14285714e-01], [ -3.73930533e-01, -2.04763614e-01, 2.14285714e-01], [ -2.45485487e-01, -9.69400266e-01, 2.14285714e-01], [ -2.25028363e-01, -8.88616910e-01, 2.14285714e-01], [ -8.25793455e-02, -9.96584493e-01, 2.14285714e-01], [ -7.56977333e-02, -9.13535785e-01, 2.14285714e-01], [ 8.25793455e-02, -9.96584493e-01, 2.14285714e-01], [ 7.56977333e-02, -9.13535785e-01, 2.14285714e-01], [ 2.45485487e-01, -9.69400266e-01, 2.14285714e-01], [ 2.25028363e-01, -8.88616910e-01, 2.14285714e-01], [ 4.01695425e-01, -9.15773327e-01, 2.14285714e-01], [ 3.68220806e-01, -8.39458883e-01, 2.14285714e-01], [ 5.46948158e-01, -8.37166478e-01, 2.14285714e-01], [ 5.01369145e-01, -7.67402605e-01, 2.14285714e-01], [ -2.04571239e-01, -8.07833555e-01, 2.14285714e-01], [ -6.88161212e-02, -8.30487078e-01, 2.14285714e-01], [ 6.88161212e-02, -8.30487078e-01, 2.14285714e-01], [ 2.04571239e-01, -8.07833555e-01, 2.14285714e-01], [ 3.34746187e-01, -7.63144439e-01, 2.14285714e-01], [ 4.55790132e-01, -6.97638732e-01, 2.14285714e-01], [ -1.84114115e-01, -7.27050199e-01, 2.14285714e-01], [ -6.19345091e-02, -7.47438370e-01, 2.14285714e-01], [ 6.19345091e-02, -7.47438370e-01, 2.14285714e-01], [ 1.84114115e-01, -7.27050199e-01, 2.14285714e-01], [ 3.01271568e-01, -6.86829995e-01, 2.14285714e-01], [ 4.10211119e-01, -6.27874859e-01, 2.14285714e-01], [ -1.63656991e-01, -6.46266844e-01, 2.14285714e-01], [ -5.50528970e-02, -6.64389662e-01, 2.14285714e-01], [ 5.50528970e-02, -6.64389662e-01, 2.14285714e-01], [ 1.63656991e-01, -6.46266844e-01, 2.14285714e-01], [ 2.67796950e-01, -6.10515551e-01, 2.14285714e-01], [ 3.64632105e-01, -5.58110986e-01, 2.14285714e-01], [ -1.43199867e-01, -5.65483488e-01, 2.14285714e-01], [ -4.81712849e-02, -5.81340954e-01, 2.14285714e-01], [ 4.81712849e-02, -5.81340954e-01, 2.14285714e-01], [ 1.43199867e-01, -5.65483488e-01, 2.14285714e-01], [ 2.34322331e-01, -5.34201107e-01, 2.14285714e-01], [ 3.19053092e-01, -4.88347112e-01, 2.14285714e-01], [ -1.22742744e-01, -4.84700133e-01, 2.14285714e-01], [ -4.12896727e-02, -4.98292247e-01, 2.14285714e-01], [ 4.12896727e-02, -4.98292247e-01, 2.14285714e-01], [ 1.22742744e-01, -4.84700133e-01, 2.14285714e-01], [ 2.00847712e-01, -4.57886663e-01, 2.14285714e-01], [ 2.73474079e-01, -4.18583239e-01, 2.14285714e-01], [ 1.02606772e-01, -4.13792257e-01, 2.14285714e-01], [ 3.59043567e-02, -4.33301147e-01, 2.14285714e-01], [ 1.67077120e-01, -3.85469130e-01, 2.14285714e-01], [ 2.27895066e-01, -3.48819366e-01, 2.14285714e-01], [ 8.24708009e-02, -3.42884381e-01, 2.14285714e-01], [ 3.05190406e-02, -3.68310047e-01, 2.14285714e-01], [ 1.33306527e-01, -3.13051596e-01, 2.14285714e-01], [ 1.82316053e-01, -2.79055493e-01, 2.14285714e-01], [ 6.23348295e-02, -2.71976505e-01, 2.14285714e-01], [ 2.51337245e-02, -3.03318947e-01, 2.14285714e-01], [ 9.95359345e-02, -2.40634062e-01, 2.14285714e-01], [ 1.36737040e-01, -2.09291620e-01, 2.14285714e-01], [ 9.11580264e-02, -1.39527746e-01, 2.14285714e-01], [ 5.51990834e-02, -1.85860856e-01, 2.14285714e-01], [ 4.55790132e-02, -6.97638732e-02, 2.14285714e-01], [ 1.08622324e-02, -1.31087650e-01, 2.14285714e-01], [ 1.92401405e-02, -2.32193966e-01, 2.14285714e-01], [ -2.38545485e-02, -1.92411427e-01, 2.14285714e-01], [ -1.67188024e-02, -2.78527075e-01, 2.14285714e-01], [ -5.85713293e-02, -2.53735203e-01, 2.14285714e-01], [ -7.99618007e-02, -3.30723513e-01, 2.14285714e-01], [ -1.01352272e-01, -4.07711823e-01, 2.14285714e-01], [ -2.49090925e-02, -3.51782132e-01, 2.14285714e-01], [ -3.30993826e-02, -4.25037189e-01, 2.14285714e-01], [ 6.77281572e-01, -7.35723911e-01, 2.14285714e-01], [ 6.20841441e-01, -6.74413585e-01, 2.14285714e-01], [ 7.89140509e-01, -6.14212713e-01, 2.14285714e-01], [ 7.23378800e-01, -5.63028320e-01, 2.14285714e-01], [ 8.79473751e-01, -4.75947393e-01, 2.14285714e-01], [ 8.06184272e-01, -4.36285110e-01, 2.14285714e-01], [ 9.45817242e-01, -3.24699469e-01, 2.14285714e-01], [ 8.66999138e-01, -2.97641180e-01, 2.14285714e-01], [ 9.86361303e-01, -1.64594590e-01, 2.14285714e-01], [ 9.04164528e-01, -1.50878374e-01, 2.14285714e-01], [ 5.64401310e-01, -6.13103259e-01, 2.14285714e-01], [ 6.57617091e-01, -5.11843927e-01, 2.14285714e-01], [ 7.32894793e-01, -3.96622828e-01, 2.14285714e-01], [ 7.88181035e-01, -2.70582891e-01, 2.14285714e-01], [ 8.21967753e-01, -1.37162159e-01, 2.14285714e-01], [ 5.07961179e-01, -5.51792933e-01, 2.14285714e-01], [ 5.91855382e-01, -4.60659535e-01, 2.14285714e-01], [ 6.59605313e-01, -3.56960545e-01, 2.14285714e-01], [ 7.09362931e-01, -2.43524602e-01, 2.14285714e-01], [ 7.39770978e-01, -1.23445943e-01, 2.14285714e-01], [ 4.51521048e-01, -4.90482607e-01, 2.14285714e-01], [ 5.26093673e-01, -4.09475142e-01, 2.14285714e-01], [ 5.86315834e-01, -3.17298262e-01, 2.14285714e-01], [ 6.30544828e-01, -2.16466313e-01, 2.14285714e-01], [ 6.57574202e-01, -1.09729727e-01, 2.14285714e-01], [ 3.95080917e-01, -4.29172281e-01, 2.14285714e-01], [ 4.60331964e-01, -3.58290749e-01, 2.14285714e-01], [ 5.13026355e-01, -2.77635979e-01, 2.14285714e-01], [ 5.51726724e-01, -1.89408024e-01, 2.14285714e-01], [ 5.75377427e-01, -9.60135110e-02, 2.14285714e-01], [ 3.38640786e-01, -3.67861955e-01, 2.14285714e-01], [ 3.94570255e-01, -3.07106356e-01, 2.14285714e-01], [ 4.39736876e-01, -2.37973697e-01, 2.14285714e-01], [ 4.72908621e-01, -1.62349735e-01, 2.14285714e-01], [ 4.93180652e-01, -8.22972951e-02, 2.14285714e-01], [ 4.02533591e-01, -1.40423963e-01, 2.14285714e-01], [ 3.82383017e-01, -2.06935340e-01, 2.14285714e-01], [ 4.14084357e-01, -7.09602665e-02, 2.14285714e-01], [ 3.32158562e-01, -1.18498191e-01, 2.14285714e-01], [ 3.25029158e-01, -1.75896984e-01, 2.14285714e-01], [ 3.34988061e-01, -5.96232379e-02, 2.14285714e-01], [ 2.61783533e-01, -9.65724185e-02, 2.14285714e-01], [ 2.67675299e-01, -1.44858628e-01, 2.14285714e-01], [ 2.55891766e-01, -4.82862093e-02, 2.14285714e-01], [ 1.85787515e-01, -5.54454306e-02, 2.14285714e-01], [ 1.15683264e-01, -6.26046519e-02, 2.14285714e-01], [ 2.04908364e-01, -1.10890861e-01, 2.14285714e-01], [ 1.48033195e-01, -1.25209304e-01, 2.14285714e-01], [ 2.24029213e-01, -1.66336292e-01, 2.14285714e-01], [ 1.80383126e-01, -1.87813956e-01, 2.14285714e-01], [ 2.33135679e-01, -2.47829956e-01, 2.14285714e-01], [ 2.85888233e-01, -3.07845955e-01, 2.14285714e-01], [ 2.80876227e-01, -2.13259647e-01, 2.14285714e-01], [ 3.37723241e-01, -2.60183001e-01, 2.14285714e-01], [ 1.00000000e+00, 0.00000000e+00, 7.14285714e-02], [ 9.86361303e-01, 1.64594590e-01, 7.14285714e-02], [ 9.04164528e-01, 1.50878374e-01, 7.14285714e-02], [ 9.16666667e-01, 6.89122780e-17, 7.14285714e-02], [ 9.45817242e-01, 3.24699469e-01, 7.14285714e-02], [ 8.66999138e-01, 2.97641180e-01, 7.14285714e-02], [ 8.79473751e-01, 4.75947393e-01, 7.14285714e-02], [ 8.06184272e-01, 4.36285110e-01, 7.14285714e-02], [ 7.89140509e-01, 6.14212713e-01, 7.14285714e-02], [ 7.23378800e-01, 5.63028320e-01, 7.14285714e-02], [ 6.77281572e-01, 7.35723911e-01, 7.14285714e-02], [ 6.20841441e-01, 6.74413585e-01, 7.14285714e-02], [ 5.46948158e-01, 8.37166478e-01, 7.14285714e-02], [ 5.01369145e-01, 7.67402605e-01, 7.14285714e-02], [ 4.01695425e-01, 9.15773327e-01, 7.14285714e-02], [ 3.68220806e-01, 8.39458883e-01, 7.14285714e-02], [ 8.21967753e-01, 1.37162159e-01, 7.14285714e-02], [ 8.33333333e-01, 1.18742958e-16, 7.14285714e-02], [ 7.88181035e-01, 2.70582891e-01, 7.14285714e-02], [ 7.32894793e-01, 3.96622828e-01, 7.14285714e-02], [ 6.57617091e-01, 5.11843927e-01, 7.14285714e-02], [ 5.64401310e-01, 6.13103259e-01, 7.14285714e-02], [ 4.55790132e-01, 6.97638732e-01, 7.14285714e-02], [ 3.34746187e-01, 7.63144439e-01, 7.14285714e-02], [ 7.39770978e-01, 1.23445943e-01, 7.14285714e-02], [ 7.50000000e-01, 1.21936714e-16, 7.14285714e-02], [ 7.09362931e-01, 2.43524602e-01, 7.14285714e-02], [ 6.59605313e-01, 3.56960545e-01, 7.14285714e-02], [ 5.91855382e-01, 4.60659535e-01, 7.14285714e-02], [ 5.07961179e-01, 5.51792933e-01, 7.14285714e-02], [ 4.10211119e-01, 6.27874859e-01, 7.14285714e-02], [ 3.01271568e-01, 6.86829995e-01, 7.14285714e-02], [ 6.57574202e-01, 1.09729727e-01, 7.14285714e-02], [ 6.66666667e-01, 1.12895794e-16, 7.14285714e-02], [ 6.30544828e-01, 2.16466313e-01, 7.14285714e-02], [ 5.86315834e-01, 3.17298262e-01, 7.14285714e-02], [ 5.26093673e-01, 4.09475142e-01, 7.14285714e-02], [ 4.51521048e-01, 4.90482607e-01, 7.14285714e-02], [ 3.64632105e-01, 5.58110986e-01, 7.14285714e-02], [ 2.67796950e-01, 6.10515551e-01, 7.14285714e-02], [ 5.75377427e-01, 9.60135110e-02, 7.14285714e-02], [ 5.83333333e-01, 9.97185006e-17, 7.14285714e-02], [ 5.51726724e-01, 1.89408024e-01, 7.14285714e-02], [ 5.13026355e-01, 2.77635979e-01, 7.14285714e-02], [ 4.60331964e-01, 3.58290749e-01, 7.14285714e-02], [ 3.95080917e-01, 4.29172281e-01, 7.14285714e-02], [ 3.19053092e-01, 4.88347112e-01, 7.14285714e-02], [ 2.34322331e-01, 5.34201107e-01, 7.14285714e-02], [ 4.93180652e-01, 8.22972951e-02, 7.14285714e-02], [ 5.00000000e-01, 8.46578841e-17, 7.14285714e-02], [ 4.72908621e-01, 1.62349735e-01, 7.14285714e-02], [ 4.39736876e-01, 2.37973697e-01, 7.14285714e-02], [ 3.94570255e-01, 3.07106356e-01, 7.14285714e-02], [ 3.38640786e-01, 3.67861955e-01, 7.14285714e-02], [ 2.73474079e-01, 4.18583239e-01, 7.14285714e-02], [ 2.00847712e-01, 4.57886663e-01, 7.14285714e-02], [ 2.92606991e-01, 3.16445261e-01, 7.14285714e-02], [ 3.44188184e-01, 2.67935253e-01, 7.14285714e-02], [ 2.34867640e-01, 3.58265725e-01, 7.14285714e-02], [ 1.72155182e-01, 3.92474283e-01, 7.14285714e-02], [ 2.46573197e-01, 2.65028567e-01, 7.14285714e-02], [ 2.93806114e-01, 2.28764151e-01, 7.14285714e-02], [ 1.96261201e-01, 2.97948211e-01, 7.14285714e-02], [ 1.43462652e-01, 3.27061902e-01, 7.14285714e-02], [ 2.00539403e-01, 2.13611872e-01, 7.14285714e-02], [ 2.43424043e-01, 1.89593048e-01, 7.14285714e-02], [ 1.57654762e-01, 2.37630697e-01, 7.14285714e-02], [ 1.14770121e-01, 2.61649522e-01, 7.14285714e-02], [ 8.60775910e-02, 1.96237141e-01, 7.14285714e-02], [ 1.39074405e-01, 1.78223023e-01, 7.14285714e-02], [ 5.73850607e-02, 1.30824761e-01, 7.14285714e-02], [ 1.20494048e-01, 1.18815349e-01, 7.14285714e-02], [ 2.86925303e-02, 6.54123805e-02, 7.14285714e-02], [ 1.01913690e-01, 5.94076743e-02, 7.14285714e-02], [ -1.35647550e-17, -1.43830177e-17, 7.14285714e-02], [ 8.33333333e-02, 1.98768323e-18, 7.14285714e-02], [ 1.92071219e-01, 1.60208904e-01, 7.14285714e-02], [ 1.83603035e-01, 1.06805936e-01, 7.14285714e-02], [ 1.75134851e-01, 5.34029681e-02, 7.14285714e-02], [ 1.66666667e-01, 1.85787325e-17, 7.14285714e-02], [ 2.45068032e-01, 1.42194786e-01, 7.14285714e-02], [ 2.46712022e-01, 9.47965238e-02, 7.14285714e-02], [ 2.48356011e-01, 4.73982619e-02, 7.14285714e-02], [ 2.50000000e-01, 3.53119596e-17, 7.14285714e-02], [ 3.33333333e-01, 5.20568343e-17, 7.14285714e-02], [ 3.29964224e-01, 5.90312730e-02, 7.14285714e-02], [ 4.16666667e-01, 6.86087031e-17, 7.14285714e-02], [ 4.11572438e-01, 7.06642841e-02, 7.14285714e-02], [ 3.22110888e-01, 1.17314261e-01, 7.14285714e-02], [ 3.97509754e-01, 1.39831998e-01, 7.14285714e-02], [ 3.09957647e-01, 1.74121089e-01, 7.14285714e-02], [ 3.74847261e-01, 2.06047393e-01, 7.14285714e-02], [ 2.45485487e-01, 9.69400266e-01, 7.14285714e-02], [ 2.25028363e-01, 8.88616910e-01, 7.14285714e-02], [ 8.25793455e-02, 9.96584493e-01, 7.14285714e-02], [ 7.56977333e-02, 9.13535785e-01, 7.14285714e-02], [ -8.25793455e-02, 9.96584493e-01, 7.14285714e-02], [ -7.56977333e-02, 9.13535785e-01, 7.14285714e-02], [ -2.45485487e-01, 9.69400266e-01, 7.14285714e-02], [ -2.25028363e-01, 8.88616910e-01, 7.14285714e-02], [ -4.01695425e-01, 9.15773327e-01, 7.14285714e-02], [ -3.68220806e-01, 8.39458883e-01, 7.14285714e-02], [ -5.46948158e-01, 8.37166478e-01, 7.14285714e-02], [ -5.01369145e-01, 7.67402605e-01, 7.14285714e-02], [ -6.77281572e-01, 7.35723911e-01, 7.14285714e-02], [ -6.20841441e-01, 6.74413585e-01, 7.14285714e-02], [ 2.04571239e-01, 8.07833555e-01, 7.14285714e-02], [ 6.88161212e-02, 8.30487078e-01, 7.14285714e-02], [ -6.88161212e-02, 8.30487078e-01, 7.14285714e-02], [ -2.04571239e-01, 8.07833555e-01, 7.14285714e-02], [ -3.34746187e-01, 7.63144439e-01, 7.14285714e-02], [ -4.55790132e-01, 6.97638732e-01, 7.14285714e-02], [ -5.64401310e-01, 6.13103259e-01, 7.14285714e-02], [ 1.84114115e-01, 7.27050199e-01, 7.14285714e-02], [ 6.19345091e-02, 7.47438370e-01, 7.14285714e-02], [ -6.19345091e-02, 7.47438370e-01, 7.14285714e-02], [ -1.84114115e-01, 7.27050199e-01, 7.14285714e-02], [ -3.01271568e-01, 6.86829995e-01, 7.14285714e-02], [ -4.10211119e-01, 6.27874859e-01, 7.14285714e-02], [ -5.07961179e-01, 5.51792933e-01, 7.14285714e-02], [ 1.63656991e-01, 6.46266844e-01, 7.14285714e-02], [ 5.50528970e-02, 6.64389662e-01, 7.14285714e-02], [ -5.50528970e-02, 6.64389662e-01, 7.14285714e-02], [ -1.63656991e-01, 6.46266844e-01, 7.14285714e-02], [ -2.67796950e-01, 6.10515551e-01, 7.14285714e-02], [ -3.64632105e-01, 5.58110986e-01, 7.14285714e-02], [ -4.51521048e-01, 4.90482607e-01, 7.14285714e-02], [ 1.43199867e-01, 5.65483488e-01, 7.14285714e-02], [ 4.81712849e-02, 5.81340954e-01, 7.14285714e-02], [ -4.81712849e-02, 5.81340954e-01, 7.14285714e-02], [ -1.43199867e-01, 5.65483488e-01, 7.14285714e-02], [ -2.34322331e-01, 5.34201107e-01, 7.14285714e-02], [ -3.19053092e-01, 4.88347112e-01, 7.14285714e-02], [ -3.95080917e-01, 4.29172281e-01, 7.14285714e-02], [ 1.22742744e-01, 4.84700133e-01, 7.14285714e-02], [ 4.12896727e-02, 4.98292247e-01, 7.14285714e-02], [ -4.12896727e-02, 4.98292247e-01, 7.14285714e-02], [ -1.22742744e-01, 4.84700133e-01, 7.14285714e-02], [ -2.00847712e-01, 4.57886663e-01, 7.14285714e-02], [ -2.73474079e-01, 4.18583239e-01, 7.14285714e-02], [ -3.38640786e-01, 3.67861955e-01, 7.14285714e-02], [ -1.02283149e-01, 4.15336195e-01, 7.14285714e-02], [ -3.59859419e-02, 4.34695086e-01, 7.14285714e-02], [ -1.66348287e-01, 3.87163066e-01, 7.14285714e-02], [ -2.26761024e-01, 3.50663301e-01, 7.14285714e-02], [ -2.82200655e-01, 3.06551629e-01, 7.14285714e-02], [ -8.18235534e-02, 3.45972257e-01, 7.14285714e-02], [ -3.06822110e-02, 3.71097926e-01, 7.14285714e-02], [ -1.31848862e-01, 3.16439469e-01, 7.14285714e-02], [ -1.80047970e-01, 2.82743363e-01, 7.14285714e-02], [ -2.25760524e-01, 2.45241304e-01, 7.14285714e-02], [ -6.13639584e-02, 2.76608319e-01, 7.14285714e-02], [ -2.53784802e-02, 3.07500766e-01, 7.14285714e-02], [ -9.73494365e-02, 2.45715872e-01, 7.14285714e-02], [ -1.33334915e-01, 2.14823425e-01, 7.14285714e-02], [ -1.69320393e-01, 1.83930978e-01, 7.14285714e-02], [ -1.12880262e-01, 1.22620652e-01, 7.14285714e-02], [ -7.93257664e-02, 1.65019743e-01, 7.14285714e-02], [ -5.64401310e-02, 6.13103259e-02, 7.14285714e-02], [ -2.53166180e-02, 1.15216062e-01, 7.14285714e-02], [ -4.57712708e-02, 2.07418835e-01, 7.14285714e-02], [ 5.80689493e-03, 1.69121798e-01, 7.14285714e-02], [ -1.22167752e-02, 2.49817927e-01, 7.14285714e-02], [ 3.69304079e-02, 2.23027534e-01, 7.14285714e-02], [ 2.13377203e-02, 2.92217018e-01, 7.14285714e-02], [ 6.80539208e-02, 2.76933270e-01, 7.14285714e-02], [ 8.62835284e-02, 3.46188891e-01, 7.14285714e-02], [ 1.04513136e-01, 4.15444512e-01, 7.14285714e-02], [ 2.79883711e-02, 3.60908761e-01, 7.14285714e-02], [ 3.46390219e-02, 4.29600504e-01, 7.14285714e-02], [ -7.89140509e-01, 6.14212713e-01, 7.14285714e-02], [ -7.23378800e-01, 5.63028320e-01, 7.14285714e-02], [ -8.79473751e-01, 4.75947393e-01, 7.14285714e-02], [ -8.06184272e-01, 4.36285110e-01, 7.14285714e-02], [ -9.45817242e-01, 3.24699469e-01, 7.14285714e-02], [ -8.66999138e-01, 2.97641180e-01, 7.14285714e-02], [ -9.86361303e-01, 1.64594590e-01, 7.14285714e-02], [ -9.04164528e-01, 1.50878374e-01, 7.14285714e-02], [ -1.00000000e+00, 1.74949543e-17, 7.14285714e-02], [ -9.16666667e-01, 3.57545599e-17, 7.14285714e-02], [ -9.86361303e-01, -1.64594590e-01, 7.14285714e-02], [ -9.04164528e-01, -1.50878374e-01, 7.14285714e-02], [ -6.57617091e-01, 5.11843927e-01, 7.14285714e-02], [ -7.32894793e-01, 3.96622828e-01, 7.14285714e-02], [ -7.88181035e-01, 2.70582891e-01, 7.14285714e-02], [ -8.21967753e-01, 1.37162159e-01, 7.14285714e-02], [ -8.33333333e-01, 3.09851013e-17, 7.14285714e-02], [ -8.21967753e-01, -1.37162159e-01, 7.14285714e-02], [ -5.91855382e-01, 4.60659535e-01, 7.14285714e-02], [ -6.59605313e-01, 3.56960545e-01, 7.14285714e-02], [ -7.09362931e-01, 2.43524602e-01, 7.14285714e-02], [ -7.39770978e-01, 1.23445943e-01, 7.14285714e-02], [ -7.50000000e-01, 2.55581870e-17, 7.14285714e-02], [ -7.39770978e-01, -1.23445943e-01, 7.14285714e-02], [ -5.26093673e-01, 4.09475142e-01, 7.14285714e-02], [ -5.86315834e-01, 3.17298262e-01, 7.14285714e-02], [ -6.30544828e-01, 2.16466313e-01, 7.14285714e-02], [ -6.57574202e-01, 1.09729727e-01, 7.14285714e-02], [ -6.66666667e-01, 1.99595799e-17, 7.14285714e-02], [ -6.57574202e-01, -1.09729727e-01, 7.14285714e-02], [ -4.60331964e-01, 3.58290749e-01, 7.14285714e-02], [ -5.13026355e-01, 2.77635979e-01, 7.14285714e-02], [ -5.51726724e-01, 1.89408024e-01, 7.14285714e-02], [ -5.75377427e-01, 9.60135110e-02, 7.14285714e-02], [ -5.83333333e-01, 1.44716353e-17, 7.14285714e-02], [ -5.75377427e-01, -9.60135110e-02, 7.14285714e-02], [ -3.94570255e-01, 3.07106356e-01, 7.14285714e-02], [ -4.39736876e-01, 2.37973697e-01, 7.14285714e-02], [ -4.72908621e-01, 1.62349735e-01, 7.14285714e-02], [ -4.93180652e-01, 8.22972951e-02, 7.14285714e-02], [ -5.00000000e-01, 9.25854954e-18, 7.14285714e-02], [ -4.93180652e-01, -8.22972951e-02, 7.14285714e-02], [ -4.20156583e-01, 7.22539112e-02, 7.14285714e-02], [ -4.11228248e-01, 1.41174836e-01, 7.14285714e-02], [ -4.20116462e-01, 1.83641595e-03, 7.14285714e-02], [ -4.10983876e-01, -6.85810793e-02, 7.14285714e-02], [ -3.47132513e-01, 6.22105272e-02, 7.14285714e-02], [ -3.49547876e-01, 1.19999937e-01, 7.14285714e-02], [ -3.40232923e-01, 3.67283191e-03, 7.14285714e-02], [ -3.28787101e-01, -5.48648634e-02, 7.14285714e-02], [ -2.74108444e-01, 5.21671433e-02, 7.14285714e-02], [ -2.87867503e-01, 9.88250387e-02, 7.14285714e-02], [ -2.60349385e-01, 5.50924786e-03, 7.14285714e-02], [ -2.46590326e-01, -4.11486476e-02, 7.14285714e-02], [ -1.64393551e-01, -2.74324317e-02, 7.14285714e-02], [ -1.92379634e-01, 2.41096072e-02, 7.14285714e-02], [ -8.21967753e-02, -1.37162159e-02, 7.14285714e-02], [ -1.24409882e-01, 4.27099665e-02, 7.14285714e-02], [ -2.20365717e-01, 7.56516461e-02, 7.14285714e-02], [ -1.66622989e-01, 9.91361489e-02, 7.14285714e-02], [ -2.48351800e-01, 1.27193685e-01, 7.14285714e-02], [ -2.08836096e-01, 1.55562331e-01, 7.14285714e-02], [ -2.70747482e-01, 2.06077006e-01, 7.14285714e-02], [ -3.32658869e-01, 2.56591681e-01, 7.14285714e-02], [ -3.12146825e-01, 1.64120356e-01, 7.14285714e-02], [ -3.75941850e-01, 2.01047026e-01, 7.14285714e-02], [ -9.45817242e-01, -3.24699469e-01, 7.14285714e-02], [ -8.66999138e-01, -2.97641180e-01, 7.14285714e-02], [ -8.79473751e-01, -4.75947393e-01, 7.14285714e-02], [ -8.06184272e-01, -4.36285110e-01, 7.14285714e-02], [ -7.89140509e-01, -6.14212713e-01, 7.14285714e-02], [ -7.23378800e-01, -5.63028320e-01, 7.14285714e-02], [ -6.77281572e-01, -7.35723911e-01, 7.14285714e-02], [ -6.20841441e-01, -6.74413585e-01, 7.14285714e-02], [ -5.46948158e-01, -8.37166478e-01, 7.14285714e-02], [ -5.01369145e-01, -7.67402605e-01, 7.14285714e-02], [ -4.01695425e-01, -9.15773327e-01, 7.14285714e-02], [ -3.68220806e-01, -8.39458883e-01, 7.14285714e-02], [ -7.88181035e-01, -2.70582891e-01, 7.14285714e-02], [ -7.32894793e-01, -3.96622828e-01, 7.14285714e-02], [ -6.57617091e-01, -5.11843927e-01, 7.14285714e-02], [ -5.64401310e-01, -6.13103259e-01, 7.14285714e-02], [ -4.55790132e-01, -6.97638732e-01, 7.14285714e-02], [ -3.34746187e-01, -7.63144439e-01, 7.14285714e-02], [ -7.09362931e-01, -2.43524602e-01, 7.14285714e-02], [ -6.59605313e-01, -3.56960545e-01, 7.14285714e-02], [ -5.91855382e-01, -4.60659535e-01, 7.14285714e-02], [ -5.07961179e-01, -5.51792933e-01, 7.14285714e-02], [ -4.10211119e-01, -6.27874859e-01, 7.14285714e-02], [ -3.01271568e-01, -6.86829995e-01, 7.14285714e-02], [ -6.30544828e-01, -2.16466313e-01, 7.14285714e-02], [ -5.86315834e-01, -3.17298262e-01, 7.14285714e-02], [ -5.26093673e-01, -4.09475142e-01, 7.14285714e-02], [ -4.51521048e-01, -4.90482607e-01, 7.14285714e-02], [ -3.64632105e-01, -5.58110986e-01, 7.14285714e-02], [ -2.67796950e-01, -6.10515551e-01, 7.14285714e-02], [ -5.51726724e-01, -1.89408024e-01, 7.14285714e-02], [ -5.13026355e-01, -2.77635979e-01, 7.14285714e-02], [ -4.60331964e-01, -3.58290749e-01, 7.14285714e-02], [ -3.95080917e-01, -4.29172281e-01, 7.14285714e-02], [ -3.19053092e-01, -4.88347112e-01, 7.14285714e-02], [ -2.34322331e-01, -5.34201107e-01, 7.14285714e-02], [ -4.72908621e-01, -1.62349735e-01, 7.14285714e-02], [ -4.39736876e-01, -2.37973697e-01, 7.14285714e-02], [ -3.94570255e-01, -3.07106356e-01, 7.14285714e-02], [ -3.38640786e-01, -3.67861955e-01, 7.14285714e-02], [ -2.73474079e-01, -4.18583239e-01, 7.14285714e-02], [ -2.00847712e-01, -4.57886663e-01, 7.14285714e-02], [ -2.90292421e-01, -3.12221863e-01, 7.14285714e-02], [ -3.43107373e-01, -2.67051188e-01, 7.14285714e-02], [ -2.31319311e-01, -3.50702994e-01, 7.14285714e-02], [ -1.67373094e-01, -3.81572219e-01, 7.14285714e-02], [ -2.41944057e-01, -2.56581770e-01, 7.14285714e-02], [ -2.91644492e-01, -2.26996019e-01, 7.14285714e-02], [ -1.89164543e-01, -2.82822750e-01, 7.14285714e-02], [ -1.33898475e-01, -3.05257776e-01, 7.14285714e-02], [ -1.93595692e-01, -2.00941678e-01, 7.14285714e-02], [ -2.40181610e-01, -1.86940851e-01, 7.14285714e-02], [ -1.47009774e-01, -2.14942505e-01, 7.14285714e-02], [ -1.00423856e-01, -2.28943332e-01, 7.14285714e-02], [ -6.69492374e-02, -1.52628888e-01, 7.14285714e-02], [ -1.25405441e-01, -1.47867075e-01, 7.14285714e-02], [ -3.34746187e-02, -7.63144439e-02, 7.14285714e-02], [ -1.03801108e-01, -8.07916455e-02, 7.14285714e-02], [ -1.83861645e-01, -1.43105263e-01, 7.14285714e-02], [ -1.74127598e-01, -8.52688471e-02, 7.14285714e-02], [ -2.42317849e-01, -1.38343450e-01, 7.14285714e-02], [ -2.44454087e-01, -8.97460487e-02, 7.14285714e-02], [ -3.20605599e-01, -1.13947277e-01, 7.14285714e-02], [ -3.96757110e-01, -1.38148506e-01, 7.14285714e-02], [ -3.08124191e-01, -1.71553532e-01, 7.14285714e-02], [ -3.73930533e-01, -2.04763614e-01, 7.14285714e-02], [ -2.45485487e-01, -9.69400266e-01, 7.14285714e-02], [ -2.25028363e-01, -8.88616910e-01, 7.14285714e-02], [ -8.25793455e-02, -9.96584493e-01, 7.14285714e-02], [ -7.56977333e-02, -9.13535785e-01, 7.14285714e-02], [ 8.25793455e-02, -9.96584493e-01, 7.14285714e-02], [ 7.56977333e-02, -9.13535785e-01, 7.14285714e-02], [ 2.45485487e-01, -9.69400266e-01, 7.14285714e-02], [ 2.25028363e-01, -8.88616910e-01, 7.14285714e-02], [ 4.01695425e-01, -9.15773327e-01, 7.14285714e-02], [ 3.68220806e-01, -8.39458883e-01, 7.14285714e-02], [ 5.46948158e-01, -8.37166478e-01, 7.14285714e-02], [ 5.01369145e-01, -7.67402605e-01, 7.14285714e-02], [ -2.04571239e-01, -8.07833555e-01, 7.14285714e-02], [ -6.88161212e-02, -8.30487078e-01, 7.14285714e-02], [ 6.88161212e-02, -8.30487078e-01, 7.14285714e-02], [ 2.04571239e-01, -8.07833555e-01, 7.14285714e-02], [ 3.34746187e-01, -7.63144439e-01, 7.14285714e-02], [ 4.55790132e-01, -6.97638732e-01, 7.14285714e-02], [ -1.84114115e-01, -7.27050199e-01, 7.14285714e-02], [ -6.19345091e-02, -7.47438370e-01, 7.14285714e-02], [ 6.19345091e-02, -7.47438370e-01, 7.14285714e-02], [ 1.84114115e-01, -7.27050199e-01, 7.14285714e-02], [ 3.01271568e-01, -6.86829995e-01, 7.14285714e-02], [ 4.10211119e-01, -6.27874859e-01, 7.14285714e-02], [ -1.63656991e-01, -6.46266844e-01, 7.14285714e-02], [ -5.50528970e-02, -6.64389662e-01, 7.14285714e-02], [ 5.50528970e-02, -6.64389662e-01, 7.14285714e-02], [ 1.63656991e-01, -6.46266844e-01, 7.14285714e-02], [ 2.67796950e-01, -6.10515551e-01, 7.14285714e-02], [ 3.64632105e-01, -5.58110986e-01, 7.14285714e-02], [ -1.43199867e-01, -5.65483488e-01, 7.14285714e-02], [ -4.81712849e-02, -5.81340954e-01, 7.14285714e-02], [ 4.81712849e-02, -5.81340954e-01, 7.14285714e-02], [ 1.43199867e-01, -5.65483488e-01, 7.14285714e-02], [ 2.34322331e-01, -5.34201107e-01, 7.14285714e-02], [ 3.19053092e-01, -4.88347112e-01, 7.14285714e-02], [ -1.22742744e-01, -4.84700133e-01, 7.14285714e-02], [ -4.12896727e-02, -4.98292247e-01, 7.14285714e-02], [ 4.12896727e-02, -4.98292247e-01, 7.14285714e-02], [ 1.22742744e-01, -4.84700133e-01, 7.14285714e-02], [ 2.00847712e-01, -4.57886663e-01, 7.14285714e-02], [ 2.73474079e-01, -4.18583239e-01, 7.14285714e-02], [ 1.02606772e-01, -4.13792257e-01, 7.14285714e-02], [ 3.59043567e-02, -4.33301147e-01, 7.14285714e-02], [ 1.67077120e-01, -3.85469130e-01, 7.14285714e-02], [ 2.27895066e-01, -3.48819366e-01, 7.14285714e-02], [ 8.24708009e-02, -3.42884381e-01, 7.14285714e-02], [ 3.05190406e-02, -3.68310047e-01, 7.14285714e-02], [ 1.33306527e-01, -3.13051596e-01, 7.14285714e-02], [ 1.82316053e-01, -2.79055493e-01, 7.14285714e-02], [ 6.23348295e-02, -2.71976505e-01, 7.14285714e-02], [ 2.51337245e-02, -3.03318947e-01, 7.14285714e-02], [ 9.95359345e-02, -2.40634062e-01, 7.14285714e-02], [ 1.36737040e-01, -2.09291620e-01, 7.14285714e-02], [ 9.11580264e-02, -1.39527746e-01, 7.14285714e-02], [ 5.51990834e-02, -1.85860856e-01, 7.14285714e-02], [ 4.55790132e-02, -6.97638732e-02, 7.14285714e-02], [ 1.08622324e-02, -1.31087650e-01, 7.14285714e-02], [ 1.92401405e-02, -2.32193966e-01, 7.14285714e-02], [ -2.38545485e-02, -1.92411427e-01, 7.14285714e-02], [ -1.67188024e-02, -2.78527075e-01, 7.14285714e-02], [ -5.85713293e-02, -2.53735203e-01, 7.14285714e-02], [ -7.99618007e-02, -3.30723513e-01, 7.14285714e-02], [ -1.01352272e-01, -4.07711823e-01, 7.14285714e-02], [ -2.49090925e-02, -3.51782132e-01, 7.14285714e-02], [ -3.30993826e-02, -4.25037189e-01, 7.14285714e-02], [ 6.77281572e-01, -7.35723911e-01, 7.14285714e-02], [ 6.20841441e-01, -6.74413585e-01, 7.14285714e-02], [ 7.89140509e-01, -6.14212713e-01, 7.14285714e-02], [ 7.23378800e-01, -5.63028320e-01, 7.14285714e-02], [ 8.79473751e-01, -4.75947393e-01, 7.14285714e-02], [ 8.06184272e-01, -4.36285110e-01, 7.14285714e-02], [ 9.45817242e-01, -3.24699469e-01, 7.14285714e-02], [ 8.66999138e-01, -2.97641180e-01, 7.14285714e-02], [ 9.86361303e-01, -1.64594590e-01, 7.14285714e-02], [ 9.04164528e-01, -1.50878374e-01, 7.14285714e-02], [ 5.64401310e-01, -6.13103259e-01, 7.14285714e-02], [ 6.57617091e-01, -5.11843927e-01, 7.14285714e-02], [ 7.32894793e-01, -3.96622828e-01, 7.14285714e-02], [ 7.88181035e-01, -2.70582891e-01, 7.14285714e-02], [ 8.21967753e-01, -1.37162159e-01, 7.14285714e-02], [ 5.07961179e-01, -5.51792933e-01, 7.14285714e-02], [ 5.91855382e-01, -4.60659535e-01, 7.14285714e-02], [ 6.59605313e-01, -3.56960545e-01, 7.14285714e-02], [ 7.09362931e-01, -2.43524602e-01, 7.14285714e-02], [ 7.39770978e-01, -1.23445943e-01, 7.14285714e-02], [ 4.51521048e-01, -4.90482607e-01, 7.14285714e-02], [ 5.26093673e-01, -4.09475142e-01, 7.14285714e-02], [ 5.86315834e-01, -3.17298262e-01, 7.14285714e-02], [ 6.30544828e-01, -2.16466313e-01, 7.14285714e-02], [ 6.57574202e-01, -1.09729727e-01, 7.14285714e-02], [ 3.95080917e-01, -4.29172281e-01, 7.14285714e-02], [ 4.60331964e-01, -3.58290749e-01, 7.14285714e-02], [ 5.13026355e-01, -2.77635979e-01, 7.14285714e-02], [ 5.51726724e-01, -1.89408024e-01, 7.14285714e-02], [ 5.75377427e-01, -9.60135110e-02, 7.14285714e-02], [ 3.38640786e-01, -3.67861955e-01, 7.14285714e-02], [ 3.94570255e-01, -3.07106356e-01, 7.14285714e-02], [ 4.39736876e-01, -2.37973697e-01, 7.14285714e-02], [ 4.72908621e-01, -1.62349735e-01, 7.14285714e-02], [ 4.93180652e-01, -8.22972951e-02, 7.14285714e-02], [ 4.02533591e-01, -1.40423963e-01, 7.14285714e-02], [ 3.82383017e-01, -2.06935340e-01, 7.14285714e-02], [ 4.14084357e-01, -7.09602665e-02, 7.14285714e-02], [ 3.32158562e-01, -1.18498191e-01, 7.14285714e-02], [ 3.25029158e-01, -1.75896984e-01, 7.14285714e-02], [ 3.34988061e-01, -5.96232379e-02, 7.14285714e-02], [ 2.61783533e-01, -9.65724185e-02, 7.14285714e-02], [ 2.67675299e-01, -1.44858628e-01, 7.14285714e-02], [ 2.55891766e-01, -4.82862093e-02, 7.14285714e-02], [ 1.85787515e-01, -5.54454306e-02, 7.14285714e-02], [ 1.15683264e-01, -6.26046519e-02, 7.14285714e-02], [ 2.04908364e-01, -1.10890861e-01, 7.14285714e-02], [ 1.48033195e-01, -1.25209304e-01, 7.14285714e-02], [ 2.24029213e-01, -1.66336292e-01, 7.14285714e-02], [ 1.80383126e-01, -1.87813956e-01, 7.14285714e-02], [ 2.33135679e-01, -2.47829956e-01, 7.14285714e-02], [ 2.85888233e-01, -3.07845955e-01, 7.14285714e-02], [ 2.80876227e-01, -2.13259647e-01, 7.14285714e-02], [ 3.37723241e-01, -2.60183001e-01, 7.14285714e-02], [ 1.00000000e+00, 0.00000000e+00, -7.14285714e-02], [ 9.86361303e-01, 1.64594590e-01, -7.14285714e-02], [ 9.04164528e-01, 1.50878374e-01, -7.14285714e-02], [ 9.16666667e-01, 1.66466002e-16, -7.14285714e-02], [ 9.45817242e-01, 3.24699469e-01, -7.14285714e-02], [ 8.66999138e-01, 2.97641180e-01, -7.14285714e-02], [ 8.79473751e-01, 4.75947393e-01, -7.14285714e-02], [ 8.06184272e-01, 4.36285110e-01, -7.14285714e-02], [ 7.89140509e-01, 6.14212713e-01, -7.14285714e-02], [ 7.23378800e-01, 5.63028320e-01, -7.14285714e-02], [ 6.77281572e-01, 7.35723911e-01, -7.14285714e-02], [ 6.20841441e-01, 6.74413585e-01, -7.14285714e-02], [ 5.46948158e-01, 8.37166478e-01, -7.14285714e-02], [ 5.01369145e-01, 7.67402605e-01, -7.14285714e-02], [ 4.01695425e-01, 9.15773327e-01, -7.14285714e-02], [ 3.68220806e-01, 8.39458883e-01, -7.14285714e-02], [ 8.21967753e-01, 1.37162159e-01, -7.14285714e-02], [ 8.33333333e-01, 2.29336017e-16, -7.14285714e-02], [ 7.88181035e-01, 2.70582891e-01, -7.14285714e-02], [ 7.32894793e-01, 3.96622828e-01, -7.14285714e-02], [ 6.57617091e-01, 5.11843927e-01, -7.14285714e-02], [ 5.64401310e-01, 6.13103259e-01, -7.14285714e-02], [ 4.55790132e-01, 6.97638732e-01, -7.14285714e-02], [ 3.34746187e-01, 7.63144439e-01, -7.14285714e-02], [ 7.39770978e-01, 1.23445943e-01, -7.14285714e-02], [ 7.50000000e-01, 2.30023465e-16, -7.14285714e-02], [ 7.09362931e-01, 2.43524602e-01, -7.14285714e-02], [ 6.59605313e-01, 3.56960545e-01, -7.14285714e-02], [ 5.91855382e-01, 4.60659535e-01, -7.14285714e-02], [ 5.07961179e-01, 5.51792933e-01, -7.14285714e-02], [ 4.10211119e-01, 6.27874859e-01, -7.14285714e-02], [ 3.01271568e-01, 6.86829995e-01, -7.14285714e-02], [ 6.57574202e-01, 1.09729727e-01, -7.14285714e-02], [ 6.66666667e-01, 2.14398012e-16, -7.14285714e-02], [ 6.30544828e-01, 2.16466313e-01, -7.14285714e-02], [ 5.86315834e-01, 3.17298262e-01, -7.14285714e-02], [ 5.26093673e-01, 4.09475142e-01, -7.14285714e-02], [ 4.51521048e-01, 4.90482607e-01, -7.14285714e-02], [ 3.64632105e-01, 5.58110986e-01, -7.14285714e-02], [ 2.67796950e-01, 6.10515551e-01, -7.14285714e-02], [ 5.75377427e-01, 9.60135110e-02, -7.14285714e-02], [ 5.83333333e-01, 1.93257395e-16, -7.14285714e-02], [ 5.51726724e-01, 1.89408024e-01, -7.14285714e-02], [ 5.13026355e-01, 2.77635979e-01, -7.14285714e-02], [ 4.60331964e-01, 3.58290749e-01, -7.14285714e-02], [ 3.95080917e-01, 4.29172281e-01, -7.14285714e-02], [ 3.19053092e-01, 4.88347112e-01, -7.14285714e-02], [ 2.34322331e-01, 5.34201107e-01, -7.14285714e-02], [ 4.93180652e-01, 8.22972951e-02, -7.14285714e-02], [ 5.00000000e-01, 1.69605680e-16, -7.14285714e-02], [ 4.72908621e-01, 1.62349735e-01, -7.14285714e-02], [ 4.39736876e-01, 2.37973697e-01, -7.14285714e-02], [ 3.94570255e-01, 3.07106356e-01, -7.14285714e-02], [ 3.38640786e-01, 3.67861955e-01, -7.14285714e-02], [ 2.73474079e-01, 4.18583239e-01, -7.14285714e-02], [ 2.00847712e-01, 4.57886663e-01, -7.14285714e-02], [ 2.92606991e-01, 3.16445261e-01, -7.14285714e-02], [ 3.44188184e-01, 2.67935253e-01, -7.14285714e-02], [ 2.34867640e-01, 3.58265725e-01, -7.14285714e-02], [ 1.72155182e-01, 3.92474283e-01, -7.14285714e-02], [ 2.46573197e-01, 2.65028567e-01, -7.14285714e-02], [ 2.93806114e-01, 2.28764151e-01, -7.14285714e-02], [ 1.96261201e-01, 2.97948211e-01, -7.14285714e-02], [ 1.43462652e-01, 3.27061902e-01, -7.14285714e-02], [ 2.00539403e-01, 2.13611872e-01, -7.14285714e-02], [ 2.43424043e-01, 1.89593048e-01, -7.14285714e-02], [ 1.57654762e-01, 2.37630697e-01, -7.14285714e-02], [ 1.14770121e-01, 2.61649522e-01, -7.14285714e-02], [ 8.60775910e-02, 1.96237141e-01, -7.14285714e-02], [ 1.39074405e-01, 1.78223023e-01, -7.14285714e-02], [ 5.73850607e-02, 1.30824761e-01, -7.14285714e-02], [ 1.20494048e-01, 1.18815349e-01, -7.14285714e-02], [ 2.86925303e-02, 6.54123805e-02, -7.14285714e-02], [ 1.01913690e-01, 5.94076743e-02, -7.14285714e-02], [ -7.26236114e-17, 1.61257875e-17, -7.14285714e-02], [ 8.33333333e-02, 4.15242815e-17, -7.14285714e-02], [ 1.92071219e-01, 1.60208904e-01, -7.14285714e-02], [ 1.83603035e-01, 1.06805936e-01, -7.14285714e-02], [ 1.75134851e-01, 5.34029681e-02, -7.14285714e-02], [ 1.66666667e-01, 6.72165733e-17, -7.14285714e-02], [ 2.45068032e-01, 1.42194786e-01, -7.14285714e-02], [ 2.46712022e-01, 9.47965238e-02, -7.14285714e-02], [ 2.48356011e-01, 4.73982619e-02, -7.14285714e-02], [ 2.50000000e-01, 9.30984356e-17, -7.14285714e-02], [ 3.33333333e-01, 1.18995828e-16, -7.14285714e-02], [ 3.29964224e-01, 5.90312730e-02, -7.14285714e-02], [ 4.16666667e-01, 1.44635879e-16, -7.14285714e-02], [ 4.11572438e-01, 7.06642841e-02, -7.14285714e-02], [ 3.22110888e-01, 1.17314261e-01, -7.14285714e-02], [ 3.97509754e-01, 1.39831998e-01, -7.14285714e-02], [ 3.09957647e-01, 1.74121089e-01, -7.14285714e-02], [ 3.74847261e-01, 2.06047393e-01, -7.14285714e-02], [ 2.45485487e-01, 9.69400266e-01, -7.14285714e-02], [ 2.25028363e-01, 8.88616910e-01, -7.14285714e-02], [ 8.25793455e-02, 9.96584493e-01, -7.14285714e-02], [ 7.56977333e-02, 9.13535785e-01, -7.14285714e-02], [ -8.25793455e-02, 9.96584493e-01, -7.14285714e-02], [ -7.56977333e-02, 9.13535785e-01, -7.14285714e-02], [ -2.45485487e-01, 9.69400266e-01, -7.14285714e-02], [ -2.25028363e-01, 8.88616910e-01, -7.14285714e-02], [ -4.01695425e-01, 9.15773327e-01, -7.14285714e-02], [ -3.68220806e-01, 8.39458883e-01, -7.14285714e-02], [ -5.46948158e-01, 8.37166478e-01, -7.14285714e-02], [ -5.01369145e-01, 7.67402605e-01, -7.14285714e-02], [ -6.77281572e-01, 7.35723911e-01, -7.14285714e-02], [ -6.20841441e-01, 6.74413585e-01, -7.14285714e-02], [ 2.04571239e-01, 8.07833555e-01, -7.14285714e-02], [ 6.88161212e-02, 8.30487078e-01, -7.14285714e-02], [ -6.88161212e-02, 8.30487078e-01, -7.14285714e-02], [ -2.04571239e-01, 8.07833555e-01, -7.14285714e-02], [ -3.34746187e-01, 7.63144439e-01, -7.14285714e-02], [ -4.55790132e-01, 6.97638732e-01, -7.14285714e-02], [ -5.64401310e-01, 6.13103259e-01, -7.14285714e-02], [ 1.84114115e-01, 7.27050199e-01, -7.14285714e-02], [ 6.19345091e-02, 7.47438370e-01, -7.14285714e-02], [ -6.19345091e-02, 7.47438370e-01, -7.14285714e-02], [ -1.84114115e-01, 7.27050199e-01, -7.14285714e-02], [ -3.01271568e-01, 6.86829995e-01, -7.14285714e-02], [ -4.10211119e-01, 6.27874859e-01, -7.14285714e-02], [ -5.07961179e-01, 5.51792933e-01, -7.14285714e-02], [ 1.63656991e-01, 6.46266844e-01, -7.14285714e-02], [ 5.50528970e-02, 6.64389662e-01, -7.14285714e-02], [ -5.50528970e-02, 6.64389662e-01, -7.14285714e-02], [ -1.63656991e-01, 6.46266844e-01, -7.14285714e-02], [ -2.67796950e-01, 6.10515551e-01, -7.14285714e-02], [ -3.64632105e-01, 5.58110986e-01, -7.14285714e-02], [ -4.51521048e-01, 4.90482607e-01, -7.14285714e-02], [ 1.43199867e-01, 5.65483488e-01, -7.14285714e-02], [ 4.81712849e-02, 5.81340954e-01, -7.14285714e-02], [ -4.81712849e-02, 5.81340954e-01, -7.14285714e-02], [ -1.43199867e-01, 5.65483488e-01, -7.14285714e-02], [ -2.34322331e-01, 5.34201107e-01, -7.14285714e-02], [ -3.19053092e-01, 4.88347112e-01, -7.14285714e-02], [ -3.95080917e-01, 4.29172281e-01, -7.14285714e-02], [ 1.22742744e-01, 4.84700133e-01, -7.14285714e-02], [ 4.12896727e-02, 4.98292247e-01, -7.14285714e-02], [ -4.12896727e-02, 4.98292247e-01, -7.14285714e-02], [ -1.22742744e-01, 4.84700133e-01, -7.14285714e-02], [ -2.00847712e-01, 4.57886663e-01, -7.14285714e-02], [ -2.73474079e-01, 4.18583239e-01, -7.14285714e-02], [ -3.38640786e-01, 3.67861955e-01, -7.14285714e-02], [ -1.02283149e-01, 4.15336195e-01, -7.14285714e-02], [ -3.59859419e-02, 4.34695086e-01, -7.14285714e-02], [ -1.66348287e-01, 3.87163066e-01, -7.14285714e-02], [ -2.26761024e-01, 3.50663301e-01, -7.14285714e-02], [ -2.82200655e-01, 3.06551629e-01, -7.14285714e-02], [ -8.18235534e-02, 3.45972257e-01, -7.14285714e-02], [ -3.06822110e-02, 3.71097926e-01, -7.14285714e-02], [ -1.31848862e-01, 3.16439469e-01, -7.14285714e-02], [ -1.80047970e-01, 2.82743363e-01, -7.14285714e-02], [ -2.25760524e-01, 2.45241304e-01, -7.14285714e-02], [ -6.13639584e-02, 2.76608319e-01, -7.14285714e-02], [ -2.53784802e-02, 3.07500766e-01, -7.14285714e-02], [ -9.73494365e-02, 2.45715872e-01, -7.14285714e-02], [ -1.33334915e-01, 2.14823425e-01, -7.14285714e-02], [ -1.69320393e-01, 1.83930978e-01, -7.14285714e-02], [ -1.12880262e-01, 1.22620652e-01, -7.14285714e-02], [ -7.93257664e-02, 1.65019743e-01, -7.14285714e-02], [ -5.64401310e-02, 6.13103259e-02, -7.14285714e-02], [ -2.53166180e-02, 1.15216062e-01, -7.14285714e-02], [ -4.57712708e-02, 2.07418835e-01, -7.14285714e-02], [ 5.80689493e-03, 1.69121798e-01, -7.14285714e-02], [ -1.22167752e-02, 2.49817927e-01, -7.14285714e-02], [ 3.69304079e-02, 2.23027534e-01, -7.14285714e-02], [ 2.13377203e-02, 2.92217018e-01, -7.14285714e-02], [ 6.80539208e-02, 2.76933270e-01, -7.14285714e-02], [ 8.62835284e-02, 3.46188891e-01, -7.14285714e-02], [ 1.04513136e-01, 4.15444512e-01, -7.14285714e-02], [ 2.79883711e-02, 3.60908761e-01, -7.14285714e-02], [ 3.46390219e-02, 4.29600504e-01, -7.14285714e-02], [ -7.89140509e-01, 6.14212713e-01, -7.14285714e-02], [ -7.23378800e-01, 5.63028320e-01, -7.14285714e-02], [ -8.79473751e-01, 4.75947393e-01, -7.14285714e-02], [ -8.06184272e-01, 4.36285110e-01, -7.14285714e-02], [ -9.45817242e-01, 3.24699469e-01, -7.14285714e-02], [ -8.66999138e-01, 2.97641180e-01, -7.14285714e-02], [ -9.86361303e-01, 1.64594590e-01, -7.14285714e-02], [ -9.04164528e-01, 1.50878374e-01, -7.14285714e-02], [ -1.00000000e+00, -1.74949543e-17, -7.14285714e-02], [ -9.16666667e-01, -2.67544510e-17, -7.14285714e-02], [ -9.86361303e-01, -1.64594590e-01, -7.14285714e-02], [ -9.04164528e-01, -1.50878374e-01, -7.14285714e-02], [ -6.57617091e-01, 5.11843927e-01, -7.14285714e-02], [ -7.32894793e-01, 3.96622828e-01, -7.14285714e-02], [ -7.88181035e-01, 2.70582891e-01, -7.14285714e-02], [ -8.21967753e-01, 1.37162159e-01, -7.14285714e-02], [ -8.33333333e-01, -2.31382435e-17, -7.14285714e-02], [ -8.21967753e-01, -1.37162159e-01, -7.14285714e-02], [ -5.91855382e-01, 4.60659535e-01, -7.14285714e-02], [ -6.59605313e-01, 3.56960545e-01, -7.14285714e-02], [ -7.09362931e-01, 2.43524602e-01, -7.14285714e-02], [ -7.39770978e-01, 1.23445943e-01, -7.14285714e-02], [ -7.50000000e-01, -2.03986436e-17, -7.14285714e-02], [ -7.39770978e-01, -1.23445943e-01, -7.14285714e-02], [ -5.26093673e-01, 4.09475142e-01, -7.14285714e-02], [ -5.86315834e-01, 3.17298262e-01, -7.14285714e-02], [ -6.30544828e-01, 2.16466313e-01, -7.14285714e-02], [ -6.57574202e-01, 1.09729727e-01, -7.14285714e-02], [ -6.66666667e-01, -1.78879673e-17, -7.14285714e-02], [ -6.57574202e-01, -1.09729727e-01, -7.14285714e-02], [ -4.60331964e-01, 3.58290749e-01, -7.14285714e-02], [ -5.13026355e-01, 2.77635979e-01, -7.14285714e-02], [ -5.51726724e-01, 1.89408024e-01, -7.14285714e-02], [ -5.75377427e-01, 9.60135110e-02, -7.14285714e-02], [ -5.83333333e-01, -1.52297412e-17, -7.14285714e-02], [ -5.75377427e-01, -9.60135110e-02, -7.14285714e-02], [ -3.94570255e-01, 3.07106356e-01, -7.14285714e-02], [ -4.39736876e-01, 2.37973697e-01, -7.14285714e-02], [ -4.72908621e-01, 1.62349735e-01, -7.14285714e-02], [ -4.93180652e-01, 8.22972951e-02, -7.14285714e-02], [ -5.00000000e-01, -1.22050366e-17, -7.14285714e-02], [ -4.93180652e-01, -8.22972951e-02, -7.14285714e-02], [ -4.20156583e-01, 7.22539112e-02, -7.14285714e-02], [ -4.11228248e-01, 1.41174836e-01, -7.14285714e-02], [ -4.20116462e-01, 1.83641595e-03, -7.14285714e-02], [ -4.10983876e-01, -6.85810793e-02, -7.14285714e-02], [ -3.47132513e-01, 6.22105272e-02, -7.14285714e-02], [ -3.49547876e-01, 1.19999937e-01, -7.14285714e-02], [ -3.40232923e-01, 3.67283191e-03, -7.14285714e-02], [ -3.28787101e-01, -5.48648634e-02, -7.14285714e-02], [ -2.74108444e-01, 5.21671433e-02, -7.14285714e-02], [ -2.87867503e-01, 9.88250387e-02, -7.14285714e-02], [ -2.60349385e-01, 5.50924786e-03, -7.14285714e-02], [ -2.46590326e-01, -4.11486476e-02, -7.14285714e-02], [ -1.64393551e-01, -2.74324317e-02, -7.14285714e-02], [ -1.92379634e-01, 2.41096072e-02, -7.14285714e-02], [ -8.21967753e-02, -1.37162159e-02, -7.14285714e-02], [ -1.24409882e-01, 4.27099665e-02, -7.14285714e-02], [ -2.20365717e-01, 7.56516461e-02, -7.14285714e-02], [ -1.66622989e-01, 9.91361489e-02, -7.14285714e-02], [ -2.48351800e-01, 1.27193685e-01, -7.14285714e-02], [ -2.08836096e-01, 1.55562331e-01, -7.14285714e-02], [ -2.70747482e-01, 2.06077006e-01, -7.14285714e-02], [ -3.32658869e-01, 2.56591681e-01, -7.14285714e-02], [ -3.12146825e-01, 1.64120356e-01, -7.14285714e-02], [ -3.75941850e-01, 2.01047026e-01, -7.14285714e-02], [ -9.45817242e-01, -3.24699469e-01, -7.14285714e-02], [ -8.66999138e-01, -2.97641180e-01, -7.14285714e-02], [ -8.79473751e-01, -4.75947393e-01, -7.14285714e-02], [ -8.06184272e-01, -4.36285110e-01, -7.14285714e-02], [ -7.89140509e-01, -6.14212713e-01, -7.14285714e-02], [ -7.23378800e-01, -5.63028320e-01, -7.14285714e-02], [ -6.77281572e-01, -7.35723911e-01, -7.14285714e-02], [ -6.20841441e-01, -6.74413585e-01, -7.14285714e-02], [ -5.46948158e-01, -8.37166478e-01, -7.14285714e-02], [ -5.01369145e-01, -7.67402605e-01, -7.14285714e-02], [ -4.01695425e-01, -9.15773327e-01, -7.14285714e-02], [ -3.68220806e-01, -8.39458883e-01, -7.14285714e-02], [ -7.88181035e-01, -2.70582891e-01, -7.14285714e-02], [ -7.32894793e-01, -3.96622828e-01, -7.14285714e-02], [ -6.57617091e-01, -5.11843927e-01, -7.14285714e-02], [ -5.64401310e-01, -6.13103259e-01, -7.14285714e-02], [ -4.55790132e-01, -6.97638732e-01, -7.14285714e-02], [ -3.34746187e-01, -7.63144439e-01, -7.14285714e-02], [ -7.09362931e-01, -2.43524602e-01, -7.14285714e-02], [ -6.59605313e-01, -3.56960545e-01, -7.14285714e-02], [ -5.91855382e-01, -4.60659535e-01, -7.14285714e-02], [ -5.07961179e-01, -5.51792933e-01, -7.14285714e-02], [ -4.10211119e-01, -6.27874859e-01, -7.14285714e-02], [ -3.01271568e-01, -6.86829995e-01, -7.14285714e-02], [ -6.30544828e-01, -2.16466313e-01, -7.14285714e-02], [ -5.86315834e-01, -3.17298262e-01, -7.14285714e-02], [ -5.26093673e-01, -4.09475142e-01, -7.14285714e-02], [ -4.51521048e-01, -4.90482607e-01, -7.14285714e-02], [ -3.64632105e-01, -5.58110986e-01, -7.14285714e-02], [ -2.67796950e-01, -6.10515551e-01, -7.14285714e-02], [ -5.51726724e-01, -1.89408024e-01, -7.14285714e-02], [ -5.13026355e-01, -2.77635979e-01, -7.14285714e-02], [ -4.60331964e-01, -3.58290749e-01, -7.14285714e-02], [ -3.95080917e-01, -4.29172281e-01, -7.14285714e-02], [ -3.19053092e-01, -4.88347112e-01, -7.14285714e-02], [ -2.34322331e-01, -5.34201107e-01, -7.14285714e-02], [ -4.72908621e-01, -1.62349735e-01, -7.14285714e-02], [ -4.39736876e-01, -2.37973697e-01, -7.14285714e-02], [ -3.94570255e-01, -3.07106356e-01, -7.14285714e-02], [ -3.38640786e-01, -3.67861955e-01, -7.14285714e-02], [ -2.73474079e-01, -4.18583239e-01, -7.14285714e-02], [ -2.00847712e-01, -4.57886663e-01, -7.14285714e-02], [ -2.90292421e-01, -3.12221863e-01, -7.14285714e-02], [ -3.43107373e-01, -2.67051188e-01, -7.14285714e-02], [ -2.31319311e-01, -3.50702994e-01, -7.14285714e-02], [ -1.67373094e-01, -3.81572219e-01, -7.14285714e-02], [ -2.41944057e-01, -2.56581770e-01, -7.14285714e-02], [ -2.91644492e-01, -2.26996019e-01, -7.14285714e-02], [ -1.89164543e-01, -2.82822750e-01, -7.14285714e-02], [ -1.33898475e-01, -3.05257776e-01, -7.14285714e-02], [ -1.93595692e-01, -2.00941678e-01, -7.14285714e-02], [ -2.40181610e-01, -1.86940851e-01, -7.14285714e-02], [ -1.47009774e-01, -2.14942505e-01, -7.14285714e-02], [ -1.00423856e-01, -2.28943332e-01, -7.14285714e-02], [ -6.69492374e-02, -1.52628888e-01, -7.14285714e-02], [ -1.25405441e-01, -1.47867075e-01, -7.14285714e-02], [ -3.34746187e-02, -7.63144439e-02, -7.14285714e-02], [ -1.03801108e-01, -8.07916455e-02, -7.14285714e-02], [ -1.83861645e-01, -1.43105263e-01, -7.14285714e-02], [ -1.74127598e-01, -8.52688471e-02, -7.14285714e-02], [ -2.42317849e-01, -1.38343450e-01, -7.14285714e-02], [ -2.44454087e-01, -8.97460487e-02, -7.14285714e-02], [ -3.20605599e-01, -1.13947277e-01, -7.14285714e-02], [ -3.96757110e-01, -1.38148506e-01, -7.14285714e-02], [ -3.08124191e-01, -1.71553532e-01, -7.14285714e-02], [ -3.73930533e-01, -2.04763614e-01, -7.14285714e-02], [ -2.45485487e-01, -9.69400266e-01, -7.14285714e-02], [ -2.25028363e-01, -8.88616910e-01, -7.14285714e-02], [ -8.25793455e-02, -9.96584493e-01, -7.14285714e-02], [ -7.56977333e-02, -9.13535785e-01, -7.14285714e-02], [ 8.25793455e-02, -9.96584493e-01, -7.14285714e-02], [ 7.56977333e-02, -9.13535785e-01, -7.14285714e-02], [ 2.45485487e-01, -9.69400266e-01, -7.14285714e-02], [ 2.25028363e-01, -8.88616910e-01, -7.14285714e-02], [ 4.01695425e-01, -9.15773327e-01, -7.14285714e-02], [ 3.68220806e-01, -8.39458883e-01, -7.14285714e-02], [ 5.46948158e-01, -8.37166478e-01, -7.14285714e-02], [ 5.01369145e-01, -7.67402605e-01, -7.14285714e-02], [ -2.04571239e-01, -8.07833555e-01, -7.14285714e-02], [ -6.88161212e-02, -8.30487078e-01, -7.14285714e-02], [ 6.88161212e-02, -8.30487078e-01, -7.14285714e-02], [ 2.04571239e-01, -8.07833555e-01, -7.14285714e-02], [ 3.34746187e-01, -7.63144439e-01, -7.14285714e-02], [ 4.55790132e-01, -6.97638732e-01, -7.14285714e-02], [ -1.84114115e-01, -7.27050199e-01, -7.14285714e-02], [ -6.19345091e-02, -7.47438370e-01, -7.14285714e-02], [ 6.19345091e-02, -7.47438370e-01, -7.14285714e-02], [ 1.84114115e-01, -7.27050199e-01, -7.14285714e-02], [ 3.01271568e-01, -6.86829995e-01, -7.14285714e-02], [ 4.10211119e-01, -6.27874859e-01, -7.14285714e-02], [ -1.63656991e-01, -6.46266844e-01, -7.14285714e-02], [ -5.50528970e-02, -6.64389662e-01, -7.14285714e-02], [ 5.50528970e-02, -6.64389662e-01, -7.14285714e-02], [ 1.63656991e-01, -6.46266844e-01, -7.14285714e-02], [ 2.67796950e-01, -6.10515551e-01, -7.14285714e-02], [ 3.64632105e-01, -5.58110986e-01, -7.14285714e-02], [ -1.43199867e-01, -5.65483488e-01, -7.14285714e-02], [ -4.81712849e-02, -5.81340954e-01, -7.14285714e-02], [ 4.81712849e-02, -5.81340954e-01, -7.14285714e-02], [ 1.43199867e-01, -5.65483488e-01, -7.14285714e-02], [ 2.34322331e-01, -5.34201107e-01, -7.14285714e-02], [ 3.19053092e-01, -4.88347112e-01, -7.14285714e-02], [ -1.22742744e-01, -4.84700133e-01, -7.14285714e-02], [ -4.12896727e-02, -4.98292247e-01, -7.14285714e-02], [ 4.12896727e-02, -4.98292247e-01, -7.14285714e-02], [ 1.22742744e-01, -4.84700133e-01, -7.14285714e-02], [ 2.00847712e-01, -4.57886663e-01, -7.14285714e-02], [ 2.73474079e-01, -4.18583239e-01, -7.14285714e-02], [ 1.02606772e-01, -4.13792257e-01, -7.14285714e-02], [ 3.59043567e-02, -4.33301147e-01, -7.14285714e-02], [ 1.67077120e-01, -3.85469130e-01, -7.14285714e-02], [ 2.27895066e-01, -3.48819366e-01, -7.14285714e-02], [ 8.24708009e-02, -3.42884381e-01, -7.14285714e-02], [ 3.05190406e-02, -3.68310047e-01, -7.14285714e-02], [ 1.33306527e-01, -3.13051596e-01, -7.14285714e-02], [ 1.82316053e-01, -2.79055493e-01, -7.14285714e-02], [ 6.23348295e-02, -2.71976505e-01, -7.14285714e-02], [ 2.51337245e-02, -3.03318947e-01, -7.14285714e-02], [ 9.95359345e-02, -2.40634062e-01, -7.14285714e-02], [ 1.36737040e-01, -2.09291620e-01, -7.14285714e-02], [ 9.11580264e-02, -1.39527746e-01, -7.14285714e-02], [ 5.51990834e-02, -1.85860856e-01, -7.14285714e-02], [ 4.55790132e-02, -6.97638732e-02, -7.14285714e-02], [ 1.08622324e-02, -1.31087650e-01, -7.14285714e-02], [ 1.92401405e-02, -2.32193966e-01, -7.14285714e-02], [ -2.38545485e-02, -1.92411427e-01, -7.14285714e-02], [ -1.67188024e-02, -2.78527075e-01, -7.14285714e-02], [ -5.85713293e-02, -2.53735203e-01, -7.14285714e-02], [ -7.99618007e-02, -3.30723513e-01, -7.14285714e-02], [ -1.01352272e-01, -4.07711823e-01, -7.14285714e-02], [ -2.49090925e-02, -3.51782132e-01, -7.14285714e-02], [ -3.30993826e-02, -4.25037189e-01, -7.14285714e-02], [ 6.77281572e-01, -7.35723911e-01, -7.14285714e-02], [ 6.20841441e-01, -6.74413585e-01, -7.14285714e-02], [ 7.89140509e-01, -6.14212713e-01, -7.14285714e-02], [ 7.23378800e-01, -5.63028320e-01, -7.14285714e-02], [ 8.79473751e-01, -4.75947393e-01, -7.14285714e-02], [ 8.06184272e-01, -4.36285110e-01, -7.14285714e-02], [ 9.45817242e-01, -3.24699469e-01, -7.14285714e-02], [ 8.66999138e-01, -2.97641180e-01, -7.14285714e-02], [ 9.86361303e-01, -1.64594590e-01, -7.14285714e-02], [ 9.04164528e-01, -1.50878374e-01, -7.14285714e-02], [ 5.64401310e-01, -6.13103259e-01, -7.14285714e-02], [ 6.57617091e-01, -5.11843927e-01, -7.14285714e-02], [ 7.32894793e-01, -3.96622828e-01, -7.14285714e-02], [ 7.88181035e-01, -2.70582891e-01, -7.14285714e-02], [ 8.21967753e-01, -1.37162159e-01, -7.14285714e-02], [ 5.07961179e-01, -5.51792933e-01, -7.14285714e-02], [ 5.91855382e-01, -4.60659535e-01, -7.14285714e-02], [ 6.59605313e-01, -3.56960545e-01, -7.14285714e-02], [ 7.09362931e-01, -2.43524602e-01, -7.14285714e-02], [ 7.39770978e-01, -1.23445943e-01, -7.14285714e-02], [ 4.51521048e-01, -4.90482607e-01, -7.14285714e-02], [ 5.26093673e-01, -4.09475142e-01, -7.14285714e-02], [ 5.86315834e-01, -3.17298262e-01, -7.14285714e-02], [ 6.30544828e-01, -2.16466313e-01, -7.14285714e-02], [ 6.57574202e-01, -1.09729727e-01, -7.14285714e-02], [ 3.95080917e-01, -4.29172281e-01, -7.14285714e-02], [ 4.60331964e-01, -3.58290749e-01, -7.14285714e-02], [ 5.13026355e-01, -2.77635979e-01, -7.14285714e-02], [ 5.51726724e-01, -1.89408024e-01, -7.14285714e-02], [ 5.75377427e-01, -9.60135110e-02, -7.14285714e-02], [ 3.38640786e-01, -3.67861955e-01, -7.14285714e-02], [ 3.94570255e-01, -3.07106356e-01, -7.14285714e-02], [ 4.39736876e-01, -2.37973697e-01, -7.14285714e-02], [ 4.72908621e-01, -1.62349735e-01, -7.14285714e-02], [ 4.93180652e-01, -8.22972951e-02, -7.14285714e-02], [ 4.02533591e-01, -1.40423963e-01, -7.14285714e-02], [ 3.82383017e-01, -2.06935340e-01, -7.14285714e-02], [ 4.14084357e-01, -7.09602665e-02, -7.14285714e-02], [ 3.32158562e-01, -1.18498191e-01, -7.14285714e-02], [ 3.25029158e-01, -1.75896984e-01, -7.14285714e-02], [ 3.34988061e-01, -5.96232379e-02, -7.14285714e-02], [ 2.61783533e-01, -9.65724185e-02, -7.14285714e-02], [ 2.67675299e-01, -1.44858628e-01, -7.14285714e-02], [ 2.55891766e-01, -4.82862093e-02, -7.14285714e-02], [ 1.85787515e-01, -5.54454306e-02, -7.14285714e-02], [ 1.15683264e-01, -6.26046519e-02, -7.14285714e-02], [ 2.04908364e-01, -1.10890861e-01, -7.14285714e-02], [ 1.48033195e-01, -1.25209304e-01, -7.14285714e-02], [ 2.24029213e-01, -1.66336292e-01, -7.14285714e-02], [ 1.80383126e-01, -1.87813956e-01, -7.14285714e-02], [ 2.33135679e-01, -2.47829956e-01, -7.14285714e-02], [ 2.85888233e-01, -3.07845955e-01, -7.14285714e-02], [ 2.80876227e-01, -2.13259647e-01, -7.14285714e-02], [ 3.37723241e-01, -2.60183001e-01, -7.14285714e-02], [ 1.00000000e+00, 0.00000000e+00, -2.14285714e-01], [ 9.86361303e-01, 1.64594590e-01, -2.14285714e-01], [ 9.04164528e-01, 1.50878374e-01, -2.14285714e-01], [ 9.16666667e-01, 1.69309911e-16, -2.14285714e-01], [ 9.45817242e-01, 3.24699469e-01, -2.14285714e-01], [ 8.66999138e-01, 2.97641180e-01, -2.14285714e-01], [ 8.79473751e-01, 4.75947393e-01, -2.14285714e-01], [ 8.06184272e-01, 4.36285110e-01, -2.14285714e-01], [ 7.89140509e-01, 6.14212713e-01, -2.14285714e-01], [ 7.23378800e-01, 5.63028320e-01, -2.14285714e-01], [ 6.77281572e-01, 7.35723911e-01, -2.14285714e-01], [ 6.20841441e-01, 6.74413585e-01, -2.14285714e-01], [ 5.46948158e-01, 8.37166478e-01, -2.14285714e-01], [ 5.01369145e-01, 7.67402605e-01, -2.14285714e-01], [ 4.01695425e-01, 9.15773327e-01, -2.14285714e-01], [ 3.68220806e-01, 8.39458883e-01, -2.14285714e-01], [ 8.21967753e-01, 1.37162159e-01, -2.14285714e-01], [ 8.33333333e-01, 2.52036419e-16, -2.14285714e-01], [ 7.88181035e-01, 2.70582891e-01, -2.14285714e-01], [ 7.32894793e-01, 3.96622828e-01, -2.14285714e-01], [ 6.57617091e-01, 5.11843927e-01, -2.14285714e-01], [ 5.64401310e-01, 6.13103259e-01, -2.14285714e-01], [ 4.55790132e-01, 6.97638732e-01, -2.14285714e-01], [ 3.34746187e-01, 7.63144439e-01, -2.14285714e-01], [ 7.39770978e-01, 1.23445943e-01, -2.14285714e-01], [ 7.50000000e-01, 2.57034718e-16, -2.14285714e-01], [ 7.09362931e-01, 2.43524602e-01, -2.14285714e-01], [ 6.59605313e-01, 3.56960545e-01, -2.14285714e-01], [ 5.91855382e-01, 4.60659535e-01, -2.14285714e-01], [ 5.07961179e-01, 5.51792933e-01, -2.14285714e-01], [ 4.10211119e-01, 6.27874859e-01, -2.14285714e-01], [ 3.01271568e-01, 6.86829995e-01, -2.14285714e-01], [ 6.57574202e-01, 1.09729727e-01, -2.14285714e-01], [ 6.66666667e-01, 2.41641892e-16, -2.14285714e-01], [ 6.30544828e-01, 2.16466313e-01, -2.14285714e-01], [ 5.86315834e-01, 3.17298262e-01, -2.14285714e-01], [ 5.26093673e-01, 4.09475142e-01, -2.14285714e-01], [ 4.51521048e-01, 4.90482607e-01, -2.14285714e-01], [ 3.64632105e-01, 5.58110986e-01, -2.14285714e-01], [ 2.67796950e-01, 6.10515551e-01, -2.14285714e-01], [ 5.75377427e-01, 9.60135110e-02, -2.14285714e-01], [ 5.83333333e-01, 2.19355109e-16, -2.14285714e-01], [ 5.51726724e-01, 1.89408024e-01, -2.14285714e-01], [ 5.13026355e-01, 2.77635979e-01, -2.14285714e-01], [ 4.60331964e-01, 3.58290749e-01, -2.14285714e-01], [ 3.95080917e-01, 4.29172281e-01, -2.14285714e-01], [ 3.19053092e-01, 4.88347112e-01, -2.14285714e-01], [ 2.34322331e-01, 5.34201107e-01, -2.14285714e-01], [ 4.93180652e-01, 8.22972951e-02, -2.14285714e-01], [ 5.00000000e-01, 1.93929455e-16, -2.14285714e-01], [ 4.72908621e-01, 1.62349735e-01, -2.14285714e-01], [ 4.39736876e-01, 2.37973697e-01, -2.14285714e-01], [ 3.94570255e-01, 3.07106356e-01, -2.14285714e-01], [ 3.38640786e-01, 3.67861955e-01, -2.14285714e-01], [ 2.73474079e-01, 4.18583239e-01, -2.14285714e-01], [ 2.00847712e-01, 4.57886663e-01, -2.14285714e-01], [ 2.92606991e-01, 3.16445261e-01, -2.14285714e-01], [ 3.44188184e-01, 2.67935253e-01, -2.14285714e-01], [ 2.34867640e-01, 3.58265725e-01, -2.14285714e-01], [ 1.72155182e-01, 3.92474283e-01, -2.14285714e-01], [ 2.46573197e-01, 2.65028567e-01, -2.14285714e-01], [ 2.93806114e-01, 2.28764151e-01, -2.14285714e-01], [ 1.96261201e-01, 2.97948211e-01, -2.14285714e-01], [ 1.43462652e-01, 3.27061902e-01, -2.14285714e-01], [ 2.00539403e-01, 2.13611872e-01, -2.14285714e-01], [ 2.43424043e-01, 1.89593048e-01, -2.14285714e-01], [ 1.57654762e-01, 2.37630697e-01, -2.14285714e-01], [ 1.14770121e-01, 2.61649522e-01, -2.14285714e-01], [ 8.60775910e-02, 1.96237141e-01, -2.14285714e-01], [ 1.39074405e-01, 1.78223023e-01, -2.14285714e-01], [ 5.73850607e-02, 1.30824761e-01, -2.14285714e-01], [ 1.20494048e-01, 1.18815349e-01, -2.14285714e-01], [ 2.86925303e-02, 6.54123805e-02, -2.14285714e-01], [ 1.01913690e-01, 5.94076743e-02, -2.14285714e-01], [ 1.14778696e-17, 2.69135258e-17, -2.14285714e-01], [ 8.33333333e-02, 5.45226540e-17, -2.14285714e-01], [ 1.92071219e-01, 1.60208904e-01, -2.14285714e-01], [ 1.83603035e-01, 1.06805936e-01, -2.14285714e-01], [ 1.75134851e-01, 5.34029681e-02, -2.14285714e-01], [ 1.66666667e-01, 8.24990293e-17, -2.14285714e-01], [ 2.45068032e-01, 1.42194786e-01, -2.14285714e-01], [ 2.46712022e-01, 9.47965238e-02, -2.14285714e-01], [ 2.48356011e-01, 4.73982619e-02, -2.14285714e-01], [ 2.50000000e-01, 1.10712368e-16, -2.14285714e-01], [ 3.33333333e-01, 1.38945119e-16, -2.14285714e-01], [ 3.29964224e-01, 5.90312730e-02, -2.14285714e-01], [ 4.16666667e-01, 1.66856194e-16, -2.14285714e-01], [ 4.11572438e-01, 7.06642841e-02, -2.14285714e-01], [ 3.22110888e-01, 1.17314261e-01, -2.14285714e-01], [ 3.97509754e-01, 1.39831998e-01, -2.14285714e-01], [ 3.09957647e-01, 1.74121089e-01, -2.14285714e-01], [ 3.74847261e-01, 2.06047393e-01, -2.14285714e-01], [ 2.45485487e-01, 9.69400266e-01, -2.14285714e-01], [ 2.25028363e-01, 8.88616910e-01, -2.14285714e-01], [ 8.25793455e-02, 9.96584493e-01, -2.14285714e-01], [ 7.56977333e-02, 9.13535785e-01, -2.14285714e-01], [ -8.25793455e-02, 9.96584493e-01, -2.14285714e-01], [ -7.56977333e-02, 9.13535785e-01, -2.14285714e-01], [ -2.45485487e-01, 9.69400266e-01, -2.14285714e-01], [ -2.25028363e-01, 8.88616910e-01, -2.14285714e-01], [ -4.01695425e-01, 9.15773327e-01, -2.14285714e-01], [ -3.68220806e-01, 8.39458883e-01, -2.14285714e-01], [ -5.46948158e-01, 8.37166478e-01, -2.14285714e-01], [ -5.01369145e-01, 7.67402605e-01, -2.14285714e-01], [ -6.77281572e-01, 7.35723911e-01, -2.14285714e-01], [ -6.20841441e-01, 6.74413585e-01, -2.14285714e-01], [ 2.04571239e-01, 8.07833555e-01, -2.14285714e-01], [ 6.88161212e-02, 8.30487078e-01, -2.14285714e-01], [ -6.88161212e-02, 8.30487078e-01, -2.14285714e-01], [ -2.04571239e-01, 8.07833555e-01, -2.14285714e-01], [ -3.34746187e-01, 7.63144439e-01, -2.14285714e-01], [ -4.55790132e-01, 6.97638732e-01, -2.14285714e-01], [ -5.64401310e-01, 6.13103259e-01, -2.14285714e-01], [ 1.84114115e-01, 7.27050199e-01, -2.14285714e-01], [ 6.19345091e-02, 7.47438370e-01, -2.14285714e-01], [ -6.19345091e-02, 7.47438370e-01, -2.14285714e-01], [ -1.84114115e-01, 7.27050199e-01, -2.14285714e-01], [ -3.01271568e-01, 6.86829995e-01, -2.14285714e-01], [ -4.10211119e-01, 6.27874859e-01, -2.14285714e-01], [ -5.07961179e-01, 5.51792933e-01, -2.14285714e-01], [ 1.63656991e-01, 6.46266844e-01, -2.14285714e-01], [ 5.50528970e-02, 6.64389662e-01, -2.14285714e-01], [ -5.50528970e-02, 6.64389662e-01, -2.14285714e-01], [ -1.63656991e-01, 6.46266844e-01, -2.14285714e-01], [ -2.67796950e-01, 6.10515551e-01, -2.14285714e-01], [ -3.64632105e-01, 5.58110986e-01, -2.14285714e-01], [ -4.51521048e-01, 4.90482607e-01, -2.14285714e-01], [ 1.43199867e-01, 5.65483488e-01, -2.14285714e-01], [ 4.81712849e-02, 5.81340954e-01, -2.14285714e-01], [ -4.81712849e-02, 5.81340954e-01, -2.14285714e-01], [ -1.43199867e-01, 5.65483488e-01, -2.14285714e-01], [ -2.34322331e-01, 5.34201107e-01, -2.14285714e-01], [ -3.19053092e-01, 4.88347112e-01, -2.14285714e-01], [ -3.95080917e-01, 4.29172281e-01, -2.14285714e-01], [ 1.22742744e-01, 4.84700133e-01, -2.14285714e-01], [ 4.12896727e-02, 4.98292247e-01, -2.14285714e-01], [ -4.12896727e-02, 4.98292247e-01, -2.14285714e-01], [ -1.22742744e-01, 4.84700133e-01, -2.14285714e-01], [ -2.00847712e-01, 4.57886663e-01, -2.14285714e-01], [ -2.73474079e-01, 4.18583239e-01, -2.14285714e-01], [ -3.38640786e-01, 3.67861955e-01, -2.14285714e-01], [ -1.02283149e-01, 4.15336195e-01, -2.14285714e-01], [ -3.59859419e-02, 4.34695086e-01, -2.14285714e-01], [ -1.66348287e-01, 3.87163066e-01, -2.14285714e-01], [ -2.26761024e-01, 3.50663301e-01, -2.14285714e-01], [ -2.82200655e-01, 3.06551629e-01, -2.14285714e-01], [ -8.18235534e-02, 3.45972257e-01, -2.14285714e-01], [ -3.06822110e-02, 3.71097926e-01, -2.14285714e-01], [ -1.31848862e-01, 3.16439469e-01, -2.14285714e-01], [ -1.80047970e-01, 2.82743363e-01, -2.14285714e-01], [ -2.25760524e-01, 2.45241304e-01, -2.14285714e-01], [ -6.13639584e-02, 2.76608319e-01, -2.14285714e-01], [ -2.53784802e-02, 3.07500766e-01, -2.14285714e-01], [ -9.73494365e-02, 2.45715872e-01, -2.14285714e-01], [ -1.33334915e-01, 2.14823425e-01, -2.14285714e-01], [ -1.69320393e-01, 1.83930978e-01, -2.14285714e-01], [ -1.12880262e-01, 1.22620652e-01, -2.14285714e-01], [ -7.93257664e-02, 1.65019743e-01, -2.14285714e-01], [ -5.64401310e-02, 6.13103259e-02, -2.14285714e-01], [ -2.53166180e-02, 1.15216062e-01, -2.14285714e-01], [ -4.57712708e-02, 2.07418835e-01, -2.14285714e-01], [ 5.80689493e-03, 1.69121798e-01, -2.14285714e-01], [ -1.22167752e-02, 2.49817927e-01, -2.14285714e-01], [ 3.69304079e-02, 2.23027534e-01, -2.14285714e-01], [ 2.13377203e-02, 2.92217018e-01, -2.14285714e-01], [ 6.80539208e-02, 2.76933270e-01, -2.14285714e-01], [ 8.62835284e-02, 3.46188891e-01, -2.14285714e-01], [ 1.04513136e-01, 4.15444512e-01, -2.14285714e-01], [ 2.79883711e-02, 3.60908761e-01, -2.14285714e-01], [ 3.46390219e-02, 4.29600504e-01, -2.14285714e-01], [ -7.89140509e-01, 6.14212713e-01, -2.14285714e-01], [ -7.23378800e-01, 5.63028320e-01, -2.14285714e-01], [ -8.79473751e-01, 4.75947393e-01, -2.14285714e-01], [ -8.06184272e-01, 4.36285110e-01, -2.14285714e-01], [ -9.45817242e-01, 3.24699469e-01, -2.14285714e-01], [ -8.66999138e-01, 2.97641180e-01, -2.14285714e-01], [ -9.86361303e-01, 1.64594590e-01, -2.14285714e-01], [ -9.04164528e-01, 1.50878374e-01, -2.14285714e-01], [ -1.00000000e+00, -5.24848628e-17, -2.14285714e-01], [ -9.16666667e-01, -3.39957805e-17, -2.14285714e-01], [ -9.86361303e-01, -1.64594590e-01, -2.14285714e-01], [ -9.04164528e-01, -1.50878374e-01, -2.14285714e-01], [ -6.57617091e-01, 5.11843927e-01, -2.14285714e-01], [ -7.32894793e-01, 3.96622828e-01, -2.14285714e-01], [ -7.88181035e-01, 2.70582891e-01, -2.14285714e-01], [ -8.21967753e-01, 1.37162159e-01, -2.14285714e-01], [ -8.33333333e-01, -2.88110659e-17, -2.14285714e-01], [ -8.21967753e-01, -1.37162159e-01, -2.14285714e-01], [ -5.91855382e-01, 4.60659535e-01, -2.14285714e-01], [ -6.59605313e-01, 3.56960545e-01, -2.14285714e-01], [ -7.09362931e-01, 2.43524602e-01, -2.14285714e-01], [ -7.39770978e-01, 1.23445943e-01, -2.14285714e-01], [ -7.50000000e-01, -2.47221107e-17, -2.14285714e-01], [ -7.39770978e-01, -1.23445943e-01, -2.14285714e-01], [ -5.26093673e-01, 4.09475142e-01, -2.14285714e-01], [ -5.86315834e-01, 3.17298262e-01, -2.14285714e-01], [ -6.30544828e-01, 2.16466313e-01, -2.14285714e-01], [ -6.57574202e-01, 1.09729727e-01, -2.14285714e-01], [ -6.66666667e-01, -2.09193101e-17, -2.14285714e-01], [ -6.57574202e-01, -1.09729727e-01, -2.14285714e-01], [ -4.60331964e-01, 3.58290749e-01, -2.14285714e-01], [ -5.13026355e-01, 2.77635979e-01, -2.14285714e-01], [ -5.51726724e-01, 1.89408024e-01, -2.14285714e-01], [ -5.75377427e-01, 9.60135110e-02, -2.14285714e-01], [ -5.83333333e-01, -1.69320721e-17, -2.14285714e-01], [ -5.75377427e-01, -9.60135110e-02, -2.14285714e-01], [ -3.94570255e-01, 3.07106356e-01, -2.14285714e-01], [ -4.39736876e-01, 2.37973697e-01, -2.14285714e-01], [ -4.72908621e-01, 1.62349735e-01, -2.14285714e-01], [ -4.93180652e-01, 8.22972951e-02, -2.14285714e-01], [ -5.00000000e-01, -1.24867361e-17, -2.14285714e-01], [ -4.93180652e-01, -8.22972951e-02, -2.14285714e-01], [ -4.20156583e-01, 7.22539112e-02, -2.14285714e-01], [ -4.11228248e-01, 1.41174836e-01, -2.14285714e-01], [ -4.20116462e-01, 1.83641595e-03, -2.14285714e-01], [ -4.10983876e-01, -6.85810793e-02, -2.14285714e-01], [ -3.47132513e-01, 6.22105272e-02, -2.14285714e-01], [ -3.49547876e-01, 1.19999937e-01, -2.14285714e-01], [ -3.40232923e-01, 3.67283191e-03, -2.14285714e-01], [ -3.28787101e-01, -5.48648634e-02, -2.14285714e-01], [ -2.74108444e-01, 5.21671433e-02, -2.14285714e-01], [ -2.87867503e-01, 9.88250387e-02, -2.14285714e-01], [ -2.60349385e-01, 5.50924786e-03, -2.14285714e-01], [ -2.46590326e-01, -4.11486476e-02, -2.14285714e-01], [ -1.64393551e-01, -2.74324317e-02, -2.14285714e-01], [ -1.92379634e-01, 2.41096072e-02, -2.14285714e-01], [ -8.21967753e-02, -1.37162159e-02, -2.14285714e-01], [ -1.24409882e-01, 4.27099665e-02, -2.14285714e-01], [ -2.20365717e-01, 7.56516461e-02, -2.14285714e-01], [ -1.66622989e-01, 9.91361489e-02, -2.14285714e-01], [ -2.48351800e-01, 1.27193685e-01, -2.14285714e-01], [ -2.08836096e-01, 1.55562331e-01, -2.14285714e-01], [ -2.70747482e-01, 2.06077006e-01, -2.14285714e-01], [ -3.32658869e-01, 2.56591681e-01, -2.14285714e-01], [ -3.12146825e-01, 1.64120356e-01, -2.14285714e-01], [ -3.75941850e-01, 2.01047026e-01, -2.14285714e-01], [ -9.45817242e-01, -3.24699469e-01, -2.14285714e-01], [ -8.66999138e-01, -2.97641180e-01, -2.14285714e-01], [ -8.79473751e-01, -4.75947393e-01, -2.14285714e-01], [ -8.06184272e-01, -4.36285110e-01, -2.14285714e-01], [ -7.89140509e-01, -6.14212713e-01, -2.14285714e-01], [ -7.23378800e-01, -5.63028320e-01, -2.14285714e-01], [ -6.77281572e-01, -7.35723911e-01, -2.14285714e-01], [ -6.20841441e-01, -6.74413585e-01, -2.14285714e-01], [ -5.46948158e-01, -8.37166478e-01, -2.14285714e-01], [ -5.01369145e-01, -7.67402605e-01, -2.14285714e-01], [ -4.01695425e-01, -9.15773327e-01, -2.14285714e-01], [ -3.68220806e-01, -8.39458883e-01, -2.14285714e-01], [ -7.88181035e-01, -2.70582891e-01, -2.14285714e-01], [ -7.32894793e-01, -3.96622828e-01, -2.14285714e-01], [ -6.57617091e-01, -5.11843927e-01, -2.14285714e-01], [ -5.64401310e-01, -6.13103259e-01, -2.14285714e-01], [ -4.55790132e-01, -6.97638732e-01, -2.14285714e-01], [ -3.34746187e-01, -7.63144439e-01, -2.14285714e-01], [ -7.09362931e-01, -2.43524602e-01, -2.14285714e-01], [ -6.59605313e-01, -3.56960545e-01, -2.14285714e-01], [ -5.91855382e-01, -4.60659535e-01, -2.14285714e-01], [ -5.07961179e-01, -5.51792933e-01, -2.14285714e-01], [ -4.10211119e-01, -6.27874859e-01, -2.14285714e-01], [ -3.01271568e-01, -6.86829995e-01, -2.14285714e-01], [ -6.30544828e-01, -2.16466313e-01, -2.14285714e-01], [ -5.86315834e-01, -3.17298262e-01, -2.14285714e-01], [ -5.26093673e-01, -4.09475142e-01, -2.14285714e-01], [ -4.51521048e-01, -4.90482607e-01, -2.14285714e-01], [ -3.64632105e-01, -5.58110986e-01, -2.14285714e-01], [ -2.67796950e-01, -6.10515551e-01, -2.14285714e-01], [ -5.51726724e-01, -1.89408024e-01, -2.14285714e-01], [ -5.13026355e-01, -2.77635979e-01, -2.14285714e-01], [ -4.60331964e-01, -3.58290749e-01, -2.14285714e-01], [ -3.95080917e-01, -4.29172281e-01, -2.14285714e-01], [ -3.19053092e-01, -4.88347112e-01, -2.14285714e-01], [ -2.34322331e-01, -5.34201107e-01, -2.14285714e-01], [ -4.72908621e-01, -1.62349735e-01, -2.14285714e-01], [ -4.39736876e-01, -2.37973697e-01, -2.14285714e-01], [ -3.94570255e-01, -3.07106356e-01, -2.14285714e-01], [ -3.38640786e-01, -3.67861955e-01, -2.14285714e-01], [ -2.73474079e-01, -4.18583239e-01, -2.14285714e-01], [ -2.00847712e-01, -4.57886663e-01, -2.14285714e-01], [ -2.90292421e-01, -3.12221863e-01, -2.14285714e-01], [ -3.43107373e-01, -2.67051188e-01, -2.14285714e-01], [ -2.31319311e-01, -3.50702994e-01, -2.14285714e-01], [ -1.67373094e-01, -3.81572219e-01, -2.14285714e-01], [ -2.41944057e-01, -2.56581770e-01, -2.14285714e-01], [ -2.91644492e-01, -2.26996019e-01, -2.14285714e-01], [ -1.89164543e-01, -2.82822750e-01, -2.14285714e-01], [ -1.33898475e-01, -3.05257776e-01, -2.14285714e-01], [ -1.93595692e-01, -2.00941678e-01, -2.14285714e-01], [ -2.40181610e-01, -1.86940851e-01, -2.14285714e-01], [ -1.47009774e-01, -2.14942505e-01, -2.14285714e-01], [ -1.00423856e-01, -2.28943332e-01, -2.14285714e-01], [ -6.69492374e-02, -1.52628888e-01, -2.14285714e-01], [ -1.25405441e-01, -1.47867075e-01, -2.14285714e-01], [ -3.34746187e-02, -7.63144439e-02, -2.14285714e-01], [ -1.03801108e-01, -8.07916455e-02, -2.14285714e-01], [ -1.83861645e-01, -1.43105263e-01, -2.14285714e-01], [ -1.74127598e-01, -8.52688471e-02, -2.14285714e-01], [ -2.42317849e-01, -1.38343450e-01, -2.14285714e-01], [ -2.44454087e-01, -8.97460487e-02, -2.14285714e-01], [ -3.20605599e-01, -1.13947277e-01, -2.14285714e-01], [ -3.96757110e-01, -1.38148506e-01, -2.14285714e-01], [ -3.08124191e-01, -1.71553532e-01, -2.14285714e-01], [ -3.73930533e-01, -2.04763614e-01, -2.14285714e-01], [ -2.45485487e-01, -9.69400266e-01, -2.14285714e-01], [ -2.25028363e-01, -8.88616910e-01, -2.14285714e-01], [ -8.25793455e-02, -9.96584493e-01, -2.14285714e-01], [ -7.56977333e-02, -9.13535785e-01, -2.14285714e-01], [ 8.25793455e-02, -9.96584493e-01, -2.14285714e-01], [ 7.56977333e-02, -9.13535785e-01, -2.14285714e-01], [ 2.45485487e-01, -9.69400266e-01, -2.14285714e-01], [ 2.25028363e-01, -8.88616910e-01, -2.14285714e-01], [ 4.01695425e-01, -9.15773327e-01, -2.14285714e-01], [ 3.68220806e-01, -8.39458883e-01, -2.14285714e-01], [ 5.46948158e-01, -8.37166478e-01, -2.14285714e-01], [ 5.01369145e-01, -7.67402605e-01, -2.14285714e-01], [ -2.04571239e-01, -8.07833555e-01, -2.14285714e-01], [ -6.88161212e-02, -8.30487078e-01, -2.14285714e-01], [ 6.88161212e-02, -8.30487078e-01, -2.14285714e-01], [ 2.04571239e-01, -8.07833555e-01, -2.14285714e-01], [ 3.34746187e-01, -7.63144439e-01, -2.14285714e-01], [ 4.55790132e-01, -6.97638732e-01, -2.14285714e-01], [ -1.84114115e-01, -7.27050199e-01, -2.14285714e-01], [ -6.19345091e-02, -7.47438370e-01, -2.14285714e-01], [ 6.19345091e-02, -7.47438370e-01, -2.14285714e-01], [ 1.84114115e-01, -7.27050199e-01, -2.14285714e-01], [ 3.01271568e-01, -6.86829995e-01, -2.14285714e-01], [ 4.10211119e-01, -6.27874859e-01, -2.14285714e-01], [ -1.63656991e-01, -6.46266844e-01, -2.14285714e-01], [ -5.50528970e-02, -6.64389662e-01, -2.14285714e-01], [ 5.50528970e-02, -6.64389662e-01, -2.14285714e-01], [ 1.63656991e-01, -6.46266844e-01, -2.14285714e-01], [ 2.67796950e-01, -6.10515551e-01, -2.14285714e-01], [ 3.64632105e-01, -5.58110986e-01, -2.14285714e-01], [ -1.43199867e-01, -5.65483488e-01, -2.14285714e-01], [ -4.81712849e-02, -5.81340954e-01, -2.14285714e-01], [ 4.81712849e-02, -5.81340954e-01, -2.14285714e-01], [ 1.43199867e-01, -5.65483488e-01, -2.14285714e-01], [ 2.34322331e-01, -5.34201107e-01, -2.14285714e-01], [ 3.19053092e-01, -4.88347112e-01, -2.14285714e-01], [ -1.22742744e-01, -4.84700133e-01, -2.14285714e-01], [ -4.12896727e-02, -4.98292247e-01, -2.14285714e-01], [ 4.12896727e-02, -4.98292247e-01, -2.14285714e-01], [ 1.22742744e-01, -4.84700133e-01, -2.14285714e-01], [ 2.00847712e-01, -4.57886663e-01, -2.14285714e-01], [ 2.73474079e-01, -4.18583239e-01, -2.14285714e-01], [ 1.02606772e-01, -4.13792257e-01, -2.14285714e-01], [ 3.59043567e-02, -4.33301147e-01, -2.14285714e-01], [ 1.67077120e-01, -3.85469130e-01, -2.14285714e-01], [ 2.27895066e-01, -3.48819366e-01, -2.14285714e-01], [ 8.24708009e-02, -3.42884381e-01, -2.14285714e-01], [ 3.05190406e-02, -3.68310047e-01, -2.14285714e-01], [ 1.33306527e-01, -3.13051596e-01, -2.14285714e-01], [ 1.82316053e-01, -2.79055493e-01, -2.14285714e-01], [ 6.23348295e-02, -2.71976505e-01, -2.14285714e-01], [ 2.51337245e-02, -3.03318947e-01, -2.14285714e-01], [ 9.95359345e-02, -2.40634062e-01, -2.14285714e-01], [ 1.36737040e-01, -2.09291620e-01, -2.14285714e-01], [ 9.11580264e-02, -1.39527746e-01, -2.14285714e-01], [ 5.51990834e-02, -1.85860856e-01, -2.14285714e-01], [ 4.55790132e-02, -6.97638732e-02, -2.14285714e-01], [ 1.08622324e-02, -1.31087650e-01, -2.14285714e-01], [ 1.92401405e-02, -2.32193966e-01, -2.14285714e-01], [ -2.38545485e-02, -1.92411427e-01, -2.14285714e-01], [ -1.67188024e-02, -2.78527075e-01, -2.14285714e-01], [ -5.85713293e-02, -2.53735203e-01, -2.14285714e-01], [ -7.99618007e-02, -3.30723513e-01, -2.14285714e-01], [ -1.01352272e-01, -4.07711823e-01, -2.14285714e-01], [ -2.49090925e-02, -3.51782132e-01, -2.14285714e-01], [ -3.30993826e-02, -4.25037189e-01, -2.14285714e-01], [ 6.77281572e-01, -7.35723911e-01, -2.14285714e-01], [ 6.20841441e-01, -6.74413585e-01, -2.14285714e-01], [ 7.89140509e-01, -6.14212713e-01, -2.14285714e-01], [ 7.23378800e-01, -5.63028320e-01, -2.14285714e-01], [ 8.79473751e-01, -4.75947393e-01, -2.14285714e-01], [ 8.06184272e-01, -4.36285110e-01, -2.14285714e-01], [ 9.45817242e-01, -3.24699469e-01, -2.14285714e-01], [ 8.66999138e-01, -2.97641180e-01, -2.14285714e-01], [ 9.86361303e-01, -1.64594590e-01, -2.14285714e-01], [ 9.04164528e-01, -1.50878374e-01, -2.14285714e-01], [ 5.64401310e-01, -6.13103259e-01, -2.14285714e-01], [ 6.57617091e-01, -5.11843927e-01, -2.14285714e-01], [ 7.32894793e-01, -3.96622828e-01, -2.14285714e-01], [ 7.88181035e-01, -2.70582891e-01, -2.14285714e-01], [ 8.21967753e-01, -1.37162159e-01, -2.14285714e-01], [ 5.07961179e-01, -5.51792933e-01, -2.14285714e-01], [ 5.91855382e-01, -4.60659535e-01, -2.14285714e-01], [ 6.59605313e-01, -3.56960545e-01, -2.14285714e-01], [ 7.09362931e-01, -2.43524602e-01, -2.14285714e-01], [ 7.39770978e-01, -1.23445943e-01, -2.14285714e-01], [ 4.51521048e-01, -4.90482607e-01, -2.14285714e-01], [ 5.26093673e-01, -4.09475142e-01, -2.14285714e-01], [ 5.86315834e-01, -3.17298262e-01, -2.14285714e-01], [ 6.30544828e-01, -2.16466313e-01, -2.14285714e-01], [ 6.57574202e-01, -1.09729727e-01, -2.14285714e-01], [ 3.95080917e-01, -4.29172281e-01, -2.14285714e-01], [ 4.60331964e-01, -3.58290749e-01, -2.14285714e-01], [ 5.13026355e-01, -2.77635979e-01, -2.14285714e-01], [ 5.51726724e-01, -1.89408024e-01, -2.14285714e-01], [ 5.75377427e-01, -9.60135110e-02, -2.14285714e-01], [ 3.38640786e-01, -3.67861955e-01, -2.14285714e-01], [ 3.94570255e-01, -3.07106356e-01, -2.14285714e-01], [ 4.39736876e-01, -2.37973697e-01, -2.14285714e-01], [ 4.72908621e-01, -1.62349735e-01, -2.14285714e-01], [ 4.93180652e-01, -8.22972951e-02, -2.14285714e-01], [ 4.02533591e-01, -1.40423963e-01, -2.14285714e-01], [ 3.82383017e-01, -2.06935340e-01, -2.14285714e-01], [ 4.14084357e-01, -7.09602665e-02, -2.14285714e-01], [ 3.32158562e-01, -1.18498191e-01, -2.14285714e-01], [ 3.25029158e-01, -1.75896984e-01, -2.14285714e-01], [ 3.34988061e-01, -5.96232379e-02, -2.14285714e-01], [ 2.61783533e-01, -9.65724185e-02, -2.14285714e-01], [ 2.67675299e-01, -1.44858628e-01, -2.14285714e-01], [ 2.55891766e-01, -4.82862093e-02, -2.14285714e-01], [ 1.85787515e-01, -5.54454306e-02, -2.14285714e-01], [ 1.15683264e-01, -6.26046519e-02, -2.14285714e-01], [ 2.04908364e-01, -1.10890861e-01, -2.14285714e-01], [ 1.48033195e-01, -1.25209304e-01, -2.14285714e-01], [ 2.24029213e-01, -1.66336292e-01, -2.14285714e-01], [ 1.80383126e-01, -1.87813956e-01, -2.14285714e-01], [ 2.33135679e-01, -2.47829956e-01, -2.14285714e-01], [ 2.85888233e-01, -3.07845955e-01, -2.14285714e-01], [ 2.80876227e-01, -2.13259647e-01, -2.14285714e-01], [ 3.37723241e-01, -2.60183001e-01, -2.14285714e-01], [ 1.00000000e+00, 0.00000000e+00, -3.57142857e-01], [ 9.86361303e-01, 1.64594590e-01, -3.57142857e-01], [ 9.04164528e-01, 1.50878374e-01, -3.57142857e-01], [ 9.16666667e-01, 1.88101103e-16, -3.57142857e-01], [ 9.45817242e-01, 3.24699469e-01, -3.57142857e-01], [ 8.66999138e-01, 2.97641180e-01, -3.57142857e-01], [ 8.79473751e-01, 4.75947393e-01, -3.57142857e-01], [ 8.06184272e-01, 4.36285110e-01, -3.57142857e-01], [ 7.89140509e-01, 6.14212713e-01, -3.57142857e-01], [ 7.23378800e-01, 5.63028320e-01, -3.57142857e-01], [ 6.77281572e-01, 7.35723911e-01, -3.57142857e-01], [ 6.20841441e-01, 6.74413585e-01, -3.57142857e-01], [ 5.46948158e-01, 8.37166478e-01, -3.57142857e-01], [ 5.01369145e-01, 7.67402605e-01, -3.57142857e-01], [ 4.01695425e-01, 9.15773327e-01, -3.57142857e-01], [ 3.68220806e-01, 8.39458883e-01, -3.57142857e-01], [ 8.21967753e-01, 1.37162159e-01, -3.57142857e-01], [ 8.33333333e-01, 2.89101549e-16, -3.57142857e-01], [ 7.88181035e-01, 2.70582891e-01, -3.57142857e-01], [ 7.32894793e-01, 3.96622828e-01, -3.57142857e-01], [ 6.57617091e-01, 5.11843927e-01, -3.57142857e-01], [ 5.64401310e-01, 6.13103259e-01, -3.57142857e-01], [ 4.55790132e-01, 6.97638732e-01, -3.57142857e-01], [ 3.34746187e-01, 7.63144439e-01, -3.57142857e-01], [ 7.39770978e-01, 1.23445943e-01, -3.57142857e-01], [ 7.50000000e-01, 2.96828145e-16, -3.57142857e-01], [ 7.09362931e-01, 2.43524602e-01, -3.57142857e-01], [ 6.59605313e-01, 3.56960545e-01, -3.57142857e-01], [ 5.91855382e-01, 4.60659535e-01, -3.57142857e-01], [ 5.07961179e-01, 5.51792933e-01, -3.57142857e-01], [ 4.10211119e-01, 6.27874859e-01, -3.57142857e-01], [ 3.01271568e-01, 6.86829995e-01, -3.57142857e-01], [ 6.57574202e-01, 1.09729727e-01, -3.57142857e-01], [ 6.66666667e-01, 2.80085390e-16, -3.57142857e-01], [ 6.30544828e-01, 2.16466313e-01, -3.57142857e-01], [ 5.86315834e-01, 3.17298262e-01, -3.57142857e-01], [ 5.26093673e-01, 4.09475142e-01, -3.57142857e-01], [ 4.51521048e-01, 4.90482607e-01, -3.57142857e-01], [ 3.64632105e-01, 5.58110986e-01, -3.57142857e-01], [ 2.67796950e-01, 6.10515551e-01, -3.57142857e-01], [ 5.75377427e-01, 9.60135110e-02, -3.57142857e-01], [ 5.83333333e-01, 2.55069887e-16, -3.57142857e-01], [ 5.51726724e-01, 1.89408024e-01, -3.57142857e-01], [ 5.13026355e-01, 2.77635979e-01, -3.57142857e-01], [ 4.60331964e-01, 3.58290749e-01, -3.57142857e-01], [ 3.95080917e-01, 4.29172281e-01, -3.57142857e-01], [ 3.19053092e-01, 4.88347112e-01, -3.57142857e-01], [ 2.34322331e-01, 5.34201107e-01, -3.57142857e-01], [ 4.93180652e-01, 8.22972951e-02, -3.57142857e-01], [ 5.00000000e-01, 2.26287739e-16, -3.57142857e-01], [ 4.72908621e-01, 1.62349735e-01, -3.57142857e-01], [ 4.39736876e-01, 2.37973697e-01, -3.57142857e-01], [ 3.94570255e-01, 3.07106356e-01, -3.57142857e-01], [ 3.38640786e-01, 3.67861955e-01, -3.57142857e-01], [ 2.73474079e-01, 4.18583239e-01, -3.57142857e-01], [ 2.00847712e-01, 4.57886663e-01, -3.57142857e-01], [ 2.92606991e-01, 3.16445261e-01, -3.57142857e-01], [ 3.44188184e-01, 2.67935253e-01, -3.57142857e-01], [ 2.34867640e-01, 3.58265725e-01, -3.57142857e-01], [ 1.72155182e-01, 3.92474283e-01, -3.57142857e-01], [ 2.46573197e-01, 2.65028567e-01, -3.57142857e-01], [ 2.93806114e-01, 2.28764151e-01, -3.57142857e-01], [ 1.96261201e-01, 2.97948211e-01, -3.57142857e-01], [ 1.43462652e-01, 3.27061902e-01, -3.57142857e-01], [ 2.00539403e-01, 2.13611872e-01, -3.57142857e-01], [ 2.43424043e-01, 1.89593048e-01, -3.57142857e-01], [ 1.57654762e-01, 2.37630697e-01, -3.57142857e-01], [ 1.14770121e-01, 2.61649522e-01, -3.57142857e-01], [ 8.60775910e-02, 1.96237141e-01, -3.57142857e-01], [ 1.39074405e-01, 1.78223023e-01, -3.57142857e-01], [ 5.73850607e-02, 1.30824761e-01, -3.57142857e-01], [ 1.20494048e-01, 1.18815349e-01, -3.57142857e-01], [ 2.86925303e-02, 6.54123805e-02, -3.57142857e-01], [ 1.01913690e-01, 5.94076743e-02, -3.57142857e-01], [ -6.67803323e-18, 3.62404443e-17, -3.57142857e-01], [ 8.33333333e-02, 6.76427614e-17, -3.57142857e-01], [ 1.92071219e-01, 1.60208904e-01, -3.57142857e-01], [ 1.83603035e-01, 1.06805936e-01, -3.57142857e-01], [ 1.75134851e-01, 5.34029681e-02, -3.57142857e-01], [ 1.66666667e-01, 9.94857751e-17, -3.57142857e-01], [ 2.45068032e-01, 1.42194786e-01, -3.57142857e-01], [ 2.46712022e-01, 9.47965238e-02, -3.57142857e-01], [ 2.48356011e-01, 4.73982619e-02, -3.57142857e-01], [ 2.50000000e-01, 1.31613145e-16, -3.57142857e-01], [ 3.33333333e-01, 1.63763809e-16, -3.57142857e-01], [ 3.29964224e-01, 5.90312730e-02, -3.57142857e-01], [ 4.16666667e-01, 1.95528462e-16, -3.57142857e-01], [ 4.11572438e-01, 7.06642841e-02, -3.57142857e-01], [ 3.22110888e-01, 1.17314261e-01, -3.57142857e-01], [ 3.97509754e-01, 1.39831998e-01, -3.57142857e-01], [ 3.09957647e-01, 1.74121089e-01, -3.57142857e-01], [ 3.74847261e-01, 2.06047393e-01, -3.57142857e-01], [ 2.45485487e-01, 9.69400266e-01, -3.57142857e-01], [ 2.25028363e-01, 8.88616910e-01, -3.57142857e-01], [ 8.25793455e-02, 9.96584493e-01, -3.57142857e-01], [ 7.56977333e-02, 9.13535785e-01, -3.57142857e-01], [ -8.25793455e-02, 9.96584493e-01, -3.57142857e-01], [ -7.56977333e-02, 9.13535785e-01, -3.57142857e-01], [ -2.45485487e-01, 9.69400266e-01, -3.57142857e-01], [ -2.25028363e-01, 8.88616910e-01, -3.57142857e-01], [ -4.01695425e-01, 9.15773327e-01, -3.57142857e-01], [ -3.68220806e-01, 8.39458883e-01, -3.57142857e-01], [ -5.46948158e-01, 8.37166478e-01, -3.57142857e-01], [ -5.01369145e-01, 7.67402605e-01, -3.57142857e-01], [ -6.77281572e-01, 7.35723911e-01, -3.57142857e-01], [ -6.20841441e-01, 6.74413585e-01, -3.57142857e-01], [ 2.04571239e-01, 8.07833555e-01, -3.57142857e-01], [ 6.88161212e-02, 8.30487078e-01, -3.57142857e-01], [ -6.88161212e-02, 8.30487078e-01, -3.57142857e-01], [ -2.04571239e-01, 8.07833555e-01, -3.57142857e-01], [ -3.34746187e-01, 7.63144439e-01, -3.57142857e-01], [ -4.55790132e-01, 6.97638732e-01, -3.57142857e-01], [ -5.64401310e-01, 6.13103259e-01, -3.57142857e-01], [ 1.84114115e-01, 7.27050199e-01, -3.57142857e-01], [ 6.19345091e-02, 7.47438370e-01, -3.57142857e-01], [ -6.19345091e-02, 7.47438370e-01, -3.57142857e-01], [ -1.84114115e-01, 7.27050199e-01, -3.57142857e-01], [ -3.01271568e-01, 6.86829995e-01, -3.57142857e-01], [ -4.10211119e-01, 6.27874859e-01, -3.57142857e-01], [ -5.07961179e-01, 5.51792933e-01, -3.57142857e-01], [ 1.63656991e-01, 6.46266844e-01, -3.57142857e-01], [ 5.50528970e-02, 6.64389662e-01, -3.57142857e-01], [ -5.50528970e-02, 6.64389662e-01, -3.57142857e-01], [ -1.63656991e-01, 6.46266844e-01, -3.57142857e-01], [ -2.67796950e-01, 6.10515551e-01, -3.57142857e-01], [ -3.64632105e-01, 5.58110986e-01, -3.57142857e-01], [ -4.51521048e-01, 4.90482607e-01, -3.57142857e-01], [ 1.43199867e-01, 5.65483488e-01, -3.57142857e-01], [ 4.81712849e-02, 5.81340954e-01, -3.57142857e-01], [ -4.81712849e-02, 5.81340954e-01, -3.57142857e-01], [ -1.43199867e-01, 5.65483488e-01, -3.57142857e-01], [ -2.34322331e-01, 5.34201107e-01, -3.57142857e-01], [ -3.19053092e-01, 4.88347112e-01, -3.57142857e-01], [ -3.95080917e-01, 4.29172281e-01, -3.57142857e-01], [ 1.22742744e-01, 4.84700133e-01, -3.57142857e-01], [ 4.12896727e-02, 4.98292247e-01, -3.57142857e-01], [ -4.12896727e-02, 4.98292247e-01, -3.57142857e-01], [ -1.22742744e-01, 4.84700133e-01, -3.57142857e-01], [ -2.00847712e-01, 4.57886663e-01, -3.57142857e-01], [ -2.73474079e-01, 4.18583239e-01, -3.57142857e-01], [ -3.38640786e-01, 3.67861955e-01, -3.57142857e-01], [ -1.02283149e-01, 4.15336195e-01, -3.57142857e-01], [ -3.59859419e-02, 4.34695086e-01, -3.57142857e-01], [ -1.66348287e-01, 3.87163066e-01, -3.57142857e-01], [ -2.26761024e-01, 3.50663301e-01, -3.57142857e-01], [ -2.82200655e-01, 3.06551629e-01, -3.57142857e-01], [ -8.18235534e-02, 3.45972257e-01, -3.57142857e-01], [ -3.06822110e-02, 3.71097926e-01, -3.57142857e-01], [ -1.31848862e-01, 3.16439469e-01, -3.57142857e-01], [ -1.80047970e-01, 2.82743363e-01, -3.57142857e-01], [ -2.25760524e-01, 2.45241304e-01, -3.57142857e-01], [ -6.13639584e-02, 2.76608319e-01, -3.57142857e-01], [ -2.53784802e-02, 3.07500766e-01, -3.57142857e-01], [ -9.73494365e-02, 2.45715872e-01, -3.57142857e-01], [ -1.33334915e-01, 2.14823425e-01, -3.57142857e-01], [ -1.69320393e-01, 1.83930978e-01, -3.57142857e-01], [ -1.12880262e-01, 1.22620652e-01, -3.57142857e-01], [ -7.93257664e-02, 1.65019743e-01, -3.57142857e-01], [ -5.64401310e-02, 6.13103259e-02, -3.57142857e-01], [ -2.53166180e-02, 1.15216062e-01, -3.57142857e-01], [ -4.57712708e-02, 2.07418835e-01, -3.57142857e-01], [ 5.80689493e-03, 1.69121798e-01, -3.57142857e-01], [ -1.22167752e-02, 2.49817927e-01, -3.57142857e-01], [ 3.69304079e-02, 2.23027534e-01, -3.57142857e-01], [ 2.13377203e-02, 2.92217018e-01, -3.57142857e-01], [ 6.80539208e-02, 2.76933270e-01, -3.57142857e-01], [ 8.62835284e-02, 3.46188891e-01, -3.57142857e-01], [ 1.04513136e-01, 4.15444512e-01, -3.57142857e-01], [ 2.79883711e-02, 3.60908761e-01, -3.57142857e-01], [ 3.46390219e-02, 4.29600504e-01, -3.57142857e-01], [ -7.89140509e-01, 6.14212713e-01, -3.57142857e-01], [ -7.23378800e-01, 5.63028320e-01, -3.57142857e-01], [ -8.79473751e-01, 4.75947393e-01, -3.57142857e-01], [ -8.06184272e-01, 4.36285110e-01, -3.57142857e-01], [ -9.45817242e-01, 3.24699469e-01, -3.57142857e-01], [ -8.66999138e-01, 2.97641180e-01, -3.57142857e-01], [ -9.86361303e-01, 1.64594590e-01, -3.57142857e-01], [ -9.04164528e-01, 1.50878374e-01, -3.57142857e-01], [ -1.00000000e+00, -8.74747714e-17, -3.57142857e-01], [ -9.16666667e-01, -6.01060321e-17, -3.57142857e-01], [ -9.86361303e-01, -1.64594590e-01, -3.57142857e-01], [ -9.04164528e-01, -1.50878374e-01, -3.57142857e-01], [ -6.57617091e-01, 5.11843927e-01, -3.57142857e-01], [ -7.32894793e-01, 3.96622828e-01, -3.57142857e-01], [ -7.88181035e-01, 2.70582891e-01, -3.57142857e-01], [ -8.21967753e-01, 1.37162159e-01, -3.57142857e-01], [ -8.33333333e-01, -5.17702556e-17, -3.57142857e-01], [ -8.21967753e-01, -1.37162159e-01, -3.57142857e-01], [ -5.91855382e-01, 4.60659535e-01, -3.57142857e-01], [ -6.59605313e-01, 3.56960545e-01, -3.57142857e-01], [ -7.09362931e-01, 2.43524602e-01, -3.57142857e-01], [ -7.39770978e-01, 1.23445943e-01, -3.57142857e-01], [ -7.50000000e-01, -4.47493904e-17, -3.57142857e-01], [ -7.39770978e-01, -1.23445943e-01, -3.57142857e-01], [ -5.26093673e-01, 4.09475142e-01, -3.57142857e-01], [ -5.86315834e-01, 3.17298262e-01, -3.57142857e-01], [ -6.30544828e-01, 2.16466313e-01, -3.57142857e-01], [ -6.57574202e-01, 1.09729727e-01, -3.57142857e-01], [ -6.66666667e-01, -3.80719106e-17, -3.57142857e-01], [ -6.57574202e-01, -1.09729727e-01, -3.57142857e-01], [ -4.60331964e-01, 3.58290749e-01, -3.57142857e-01], [ -5.13026355e-01, 2.77635979e-01, -3.57142857e-01], [ -5.51726724e-01, 1.89408024e-01, -3.57142857e-01], [ -5.75377427e-01, 9.60135110e-02, -3.57142857e-01], [ -5.83333333e-01, -3.11731060e-17, -3.57142857e-01], [ -5.75377427e-01, -9.60135110e-02, -3.57142857e-01], [ -3.94570255e-01, 3.07106356e-01, -3.57142857e-01], [ -4.39736876e-01, 2.37973697e-01, -3.57142857e-01], [ -4.72908621e-01, 1.62349735e-01, -3.57142857e-01], [ -4.93180652e-01, 8.22972951e-02, -3.57142857e-01], [ -5.00000000e-01, -2.37245839e-17, -3.57142857e-01], [ -4.93180652e-01, -8.22972951e-02, -3.57142857e-01], [ -4.20156583e-01, 7.22539112e-02, -3.57142857e-01], [ -4.11228248e-01, 1.41174836e-01, -3.57142857e-01], [ -4.20116462e-01, 1.83641595e-03, -3.57142857e-01], [ -4.10983876e-01, -6.85810793e-02, -3.57142857e-01], [ -3.47132513e-01, 6.22105272e-02, -3.57142857e-01], [ -3.49547876e-01, 1.19999937e-01, -3.57142857e-01], [ -3.40232923e-01, 3.67283191e-03, -3.57142857e-01], [ -3.28787101e-01, -5.48648634e-02, -3.57142857e-01], [ -2.74108444e-01, 5.21671433e-02, -3.57142857e-01], [ -2.87867503e-01, 9.88250387e-02, -3.57142857e-01], [ -2.60349385e-01, 5.50924786e-03, -3.57142857e-01], [ -2.46590326e-01, -4.11486476e-02, -3.57142857e-01], [ -1.64393551e-01, -2.74324317e-02, -3.57142857e-01], [ -1.92379634e-01, 2.41096072e-02, -3.57142857e-01], [ -8.21967753e-02, -1.37162159e-02, -3.57142857e-01], [ -1.24409882e-01, 4.27099665e-02, -3.57142857e-01], [ -2.20365717e-01, 7.56516461e-02, -3.57142857e-01], [ -1.66622989e-01, 9.91361489e-02, -3.57142857e-01], [ -2.48351800e-01, 1.27193685e-01, -3.57142857e-01], [ -2.08836096e-01, 1.55562331e-01, -3.57142857e-01], [ -2.70747482e-01, 2.06077006e-01, -3.57142857e-01], [ -3.32658869e-01, 2.56591681e-01, -3.57142857e-01], [ -3.12146825e-01, 1.64120356e-01, -3.57142857e-01], [ -3.75941850e-01, 2.01047026e-01, -3.57142857e-01], [ -9.45817242e-01, -3.24699469e-01, -3.57142857e-01], [ -8.66999138e-01, -2.97641180e-01, -3.57142857e-01], [ -8.79473751e-01, -4.75947393e-01, -3.57142857e-01], [ -8.06184272e-01, -4.36285110e-01, -3.57142857e-01], [ -7.89140509e-01, -6.14212713e-01, -3.57142857e-01], [ -7.23378800e-01, -5.63028320e-01, -3.57142857e-01], [ -6.77281572e-01, -7.35723911e-01, -3.57142857e-01], [ -6.20841441e-01, -6.74413585e-01, -3.57142857e-01], [ -5.46948158e-01, -8.37166478e-01, -3.57142857e-01], [ -5.01369145e-01, -7.67402605e-01, -3.57142857e-01], [ -4.01695425e-01, -9.15773327e-01, -3.57142857e-01], [ -3.68220806e-01, -8.39458883e-01, -3.57142857e-01], [ -7.88181035e-01, -2.70582891e-01, -3.57142857e-01], [ -7.32894793e-01, -3.96622828e-01, -3.57142857e-01], [ -6.57617091e-01, -5.11843927e-01, -3.57142857e-01], [ -5.64401310e-01, -6.13103259e-01, -3.57142857e-01], [ -4.55790132e-01, -6.97638732e-01, -3.57142857e-01], [ -3.34746187e-01, -7.63144439e-01, -3.57142857e-01], [ -7.09362931e-01, -2.43524602e-01, -3.57142857e-01], [ -6.59605313e-01, -3.56960545e-01, -3.57142857e-01], [ -5.91855382e-01, -4.60659535e-01, -3.57142857e-01], [ -5.07961179e-01, -5.51792933e-01, -3.57142857e-01], [ -4.10211119e-01, -6.27874859e-01, -3.57142857e-01], [ -3.01271568e-01, -6.86829995e-01, -3.57142857e-01], [ -6.30544828e-01, -2.16466313e-01, -3.57142857e-01], [ -5.86315834e-01, -3.17298262e-01, -3.57142857e-01], [ -5.26093673e-01, -4.09475142e-01, -3.57142857e-01], [ -4.51521048e-01, -4.90482607e-01, -3.57142857e-01], [ -3.64632105e-01, -5.58110986e-01, -3.57142857e-01], [ -2.67796950e-01, -6.10515551e-01, -3.57142857e-01], [ -5.51726724e-01, -1.89408024e-01, -3.57142857e-01], [ -5.13026355e-01, -2.77635979e-01, -3.57142857e-01], [ -4.60331964e-01, -3.58290749e-01, -3.57142857e-01], [ -3.95080917e-01, -4.29172281e-01, -3.57142857e-01], [ -3.19053092e-01, -4.88347112e-01, -3.57142857e-01], [ -2.34322331e-01, -5.34201107e-01, -3.57142857e-01], [ -4.72908621e-01, -1.62349735e-01, -3.57142857e-01], [ -4.39736876e-01, -2.37973697e-01, -3.57142857e-01], [ -3.94570255e-01, -3.07106356e-01, -3.57142857e-01], [ -3.38640786e-01, -3.67861955e-01, -3.57142857e-01], [ -2.73474079e-01, -4.18583239e-01, -3.57142857e-01], [ -2.00847712e-01, -4.57886663e-01, -3.57142857e-01], [ -2.90292421e-01, -3.12221863e-01, -3.57142857e-01], [ -3.43107373e-01, -2.67051188e-01, -3.57142857e-01], [ -2.31319311e-01, -3.50702994e-01, -3.57142857e-01], [ -1.67373094e-01, -3.81572219e-01, -3.57142857e-01], [ -2.41944057e-01, -2.56581770e-01, -3.57142857e-01], [ -2.91644492e-01, -2.26996019e-01, -3.57142857e-01], [ -1.89164543e-01, -2.82822750e-01, -3.57142857e-01], [ -1.33898475e-01, -3.05257776e-01, -3.57142857e-01], [ -1.93595692e-01, -2.00941678e-01, -3.57142857e-01], [ -2.40181610e-01, -1.86940851e-01, -3.57142857e-01], [ -1.47009774e-01, -2.14942505e-01, -3.57142857e-01], [ -1.00423856e-01, -2.28943332e-01, -3.57142857e-01], [ -6.69492374e-02, -1.52628888e-01, -3.57142857e-01], [ -1.25405441e-01, -1.47867075e-01, -3.57142857e-01], [ -3.34746187e-02, -7.63144439e-02, -3.57142857e-01], [ -1.03801108e-01, -8.07916455e-02, -3.57142857e-01], [ -1.83861645e-01, -1.43105263e-01, -3.57142857e-01], [ -1.74127598e-01, -8.52688471e-02, -3.57142857e-01], [ -2.42317849e-01, -1.38343450e-01, -3.57142857e-01], [ -2.44454087e-01, -8.97460487e-02, -3.57142857e-01], [ -3.20605599e-01, -1.13947277e-01, -3.57142857e-01], [ -3.96757110e-01, -1.38148506e-01, -3.57142857e-01], [ -3.08124191e-01, -1.71553532e-01, -3.57142857e-01], [ -3.73930533e-01, -2.04763614e-01, -3.57142857e-01], [ -2.45485487e-01, -9.69400266e-01, -3.57142857e-01], [ -2.25028363e-01, -8.88616910e-01, -3.57142857e-01], [ -8.25793455e-02, -9.96584493e-01, -3.57142857e-01], [ -7.56977333e-02, -9.13535785e-01, -3.57142857e-01], [ 8.25793455e-02, -9.96584493e-01, -3.57142857e-01], [ 7.56977333e-02, -9.13535785e-01, -3.57142857e-01], [ 2.45485487e-01, -9.69400266e-01, -3.57142857e-01], [ 2.25028363e-01, -8.88616910e-01, -3.57142857e-01], [ 4.01695425e-01, -9.15773327e-01, -3.57142857e-01], [ 3.68220806e-01, -8.39458883e-01, -3.57142857e-01], [ 5.46948158e-01, -8.37166478e-01, -3.57142857e-01], [ 5.01369145e-01, -7.67402605e-01, -3.57142857e-01], [ -2.04571239e-01, -8.07833555e-01, -3.57142857e-01], [ -6.88161212e-02, -8.30487078e-01, -3.57142857e-01], [ 6.88161212e-02, -8.30487078e-01, -3.57142857e-01], [ 2.04571239e-01, -8.07833555e-01, -3.57142857e-01], [ 3.34746187e-01, -7.63144439e-01, -3.57142857e-01], [ 4.55790132e-01, -6.97638732e-01, -3.57142857e-01], [ -1.84114115e-01, -7.27050199e-01, -3.57142857e-01], [ -6.19345091e-02, -7.47438370e-01, -3.57142857e-01], [ 6.19345091e-02, -7.47438370e-01, -3.57142857e-01], [ 1.84114115e-01, -7.27050199e-01, -3.57142857e-01], [ 3.01271568e-01, -6.86829995e-01, -3.57142857e-01], [ 4.10211119e-01, -6.27874859e-01, -3.57142857e-01], [ -1.63656991e-01, -6.46266844e-01, -3.57142857e-01], [ -5.50528970e-02, -6.64389662e-01, -3.57142857e-01], [ 5.50528970e-02, -6.64389662e-01, -3.57142857e-01], [ 1.63656991e-01, -6.46266844e-01, -3.57142857e-01], [ 2.67796950e-01, -6.10515551e-01, -3.57142857e-01], [ 3.64632105e-01, -5.58110986e-01, -3.57142857e-01], [ -1.43199867e-01, -5.65483488e-01, -3.57142857e-01], [ -4.81712849e-02, -5.81340954e-01, -3.57142857e-01], [ 4.81712849e-02, -5.81340954e-01, -3.57142857e-01], [ 1.43199867e-01, -5.65483488e-01, -3.57142857e-01], [ 2.34322331e-01, -5.34201107e-01, -3.57142857e-01], [ 3.19053092e-01, -4.88347112e-01, -3.57142857e-01], [ -1.22742744e-01, -4.84700133e-01, -3.57142857e-01], [ -4.12896727e-02, -4.98292247e-01, -3.57142857e-01], [ 4.12896727e-02, -4.98292247e-01, -3.57142857e-01], [ 1.22742744e-01, -4.84700133e-01, -3.57142857e-01], [ 2.00847712e-01, -4.57886663e-01, -3.57142857e-01], [ 2.73474079e-01, -4.18583239e-01, -3.57142857e-01], [ 1.02606772e-01, -4.13792257e-01, -3.57142857e-01], [ 3.59043567e-02, -4.33301147e-01, -3.57142857e-01], [ 1.67077120e-01, -3.85469130e-01, -3.57142857e-01], [ 2.27895066e-01, -3.48819366e-01, -3.57142857e-01], [ 8.24708009e-02, -3.42884381e-01, -3.57142857e-01], [ 3.05190406e-02, -3.68310047e-01, -3.57142857e-01], [ 1.33306527e-01, -3.13051596e-01, -3.57142857e-01], [ 1.82316053e-01, -2.79055493e-01, -3.57142857e-01], [ 6.23348295e-02, -2.71976505e-01, -3.57142857e-01], [ 2.51337245e-02, -3.03318947e-01, -3.57142857e-01], [ 9.95359345e-02, -2.40634062e-01, -3.57142857e-01], [ 1.36737040e-01, -2.09291620e-01, -3.57142857e-01], [ 9.11580264e-02, -1.39527746e-01, -3.57142857e-01], [ 5.51990834e-02, -1.85860856e-01, -3.57142857e-01], [ 4.55790132e-02, -6.97638732e-02, -3.57142857e-01], [ 1.08622324e-02, -1.31087650e-01, -3.57142857e-01], [ 1.92401405e-02, -2.32193966e-01, -3.57142857e-01], [ -2.38545485e-02, -1.92411427e-01, -3.57142857e-01], [ -1.67188024e-02, -2.78527075e-01, -3.57142857e-01], [ -5.85713293e-02, -2.53735203e-01, -3.57142857e-01], [ -7.99618007e-02, -3.30723513e-01, -3.57142857e-01], [ -1.01352272e-01, -4.07711823e-01, -3.57142857e-01], [ -2.49090925e-02, -3.51782132e-01, -3.57142857e-01], [ -3.30993826e-02, -4.25037189e-01, -3.57142857e-01], [ 6.77281572e-01, -7.35723911e-01, -3.57142857e-01], [ 6.20841441e-01, -6.74413585e-01, -3.57142857e-01], [ 7.89140509e-01, -6.14212713e-01, -3.57142857e-01], [ 7.23378800e-01, -5.63028320e-01, -3.57142857e-01], [ 8.79473751e-01, -4.75947393e-01, -3.57142857e-01], [ 8.06184272e-01, -4.36285110e-01, -3.57142857e-01], [ 9.45817242e-01, -3.24699469e-01, -3.57142857e-01], [ 8.66999138e-01, -2.97641180e-01, -3.57142857e-01], [ 9.86361303e-01, -1.64594590e-01, -3.57142857e-01], [ 9.04164528e-01, -1.50878374e-01, -3.57142857e-01], [ 5.64401310e-01, -6.13103259e-01, -3.57142857e-01], [ 6.57617091e-01, -5.11843927e-01, -3.57142857e-01], [ 7.32894793e-01, -3.96622828e-01, -3.57142857e-01], [ 7.88181035e-01, -2.70582891e-01, -3.57142857e-01], [ 8.21967753e-01, -1.37162159e-01, -3.57142857e-01], [ 5.07961179e-01, -5.51792933e-01, -3.57142857e-01], [ 5.91855382e-01, -4.60659535e-01, -3.57142857e-01], [ 6.59605313e-01, -3.56960545e-01, -3.57142857e-01], [ 7.09362931e-01, -2.43524602e-01, -3.57142857e-01], [ 7.39770978e-01, -1.23445943e-01, -3.57142857e-01], [ 4.51521048e-01, -4.90482607e-01, -3.57142857e-01], [ 5.26093673e-01, -4.09475142e-01, -3.57142857e-01], [ 5.86315834e-01, -3.17298262e-01, -3.57142857e-01], [ 6.30544828e-01, -2.16466313e-01, -3.57142857e-01], [ 6.57574202e-01, -1.09729727e-01, -3.57142857e-01], [ 3.95080917e-01, -4.29172281e-01, -3.57142857e-01], [ 4.60331964e-01, -3.58290749e-01, -3.57142857e-01], [ 5.13026355e-01, -2.77635979e-01, -3.57142857e-01], [ 5.51726724e-01, -1.89408024e-01, -3.57142857e-01], [ 5.75377427e-01, -9.60135110e-02, -3.57142857e-01], [ 3.38640786e-01, -3.67861955e-01, -3.57142857e-01], [ 3.94570255e-01, -3.07106356e-01, -3.57142857e-01], [ 4.39736876e-01, -2.37973697e-01, -3.57142857e-01], [ 4.72908621e-01, -1.62349735e-01, -3.57142857e-01], [ 4.93180652e-01, -8.22972951e-02, -3.57142857e-01], [ 4.02533591e-01, -1.40423963e-01, -3.57142857e-01], [ 3.82383017e-01, -2.06935340e-01, -3.57142857e-01], [ 4.14084357e-01, -7.09602665e-02, -3.57142857e-01], [ 3.32158562e-01, -1.18498191e-01, -3.57142857e-01], [ 3.25029158e-01, -1.75896984e-01, -3.57142857e-01], [ 3.34988061e-01, -5.96232379e-02, -3.57142857e-01], [ 2.61783533e-01, -9.65724185e-02, -3.57142857e-01], [ 2.67675299e-01, -1.44858628e-01, -3.57142857e-01], [ 2.55891766e-01, -4.82862093e-02, -3.57142857e-01], [ 1.85787515e-01, -5.54454306e-02, -3.57142857e-01], [ 1.15683264e-01, -6.26046519e-02, -3.57142857e-01], [ 2.04908364e-01, -1.10890861e-01, -3.57142857e-01], [ 1.48033195e-01, -1.25209304e-01, -3.57142857e-01], [ 2.24029213e-01, -1.66336292e-01, -3.57142857e-01], [ 1.80383126e-01, -1.87813956e-01, -3.57142857e-01], [ 2.33135679e-01, -2.47829956e-01, -3.57142857e-01], [ 2.85888233e-01, -3.07845955e-01, -3.57142857e-01], [ 2.80876227e-01, -2.13259647e-01, -3.57142857e-01], [ 3.37723241e-01, -2.60183001e-01, -3.57142857e-01], [ 1.00000000e+00, 0.00000000e+00, -5.00000000e-01], [ 9.86361303e-01, 1.64594590e-01, -5.00000000e-01], [ 8.85385799e-01, 1.47859195e-01, -5.00000000e-01], [ 8.98367845e-01, 5.09955106e-05, -5.00000000e-01], [ 9.45817242e-01, 3.24699469e-01, -5.00000000e-01], [ 8.50003984e-01, 2.91939989e-01, -5.00000000e-01], [ 8.79473751e-01, 4.75947393e-01, -5.00000000e-01], [ 7.90244661e-01, 4.27798478e-01, -5.00000000e-01], [ 7.89140509e-01, 6.14212713e-01, -5.00000000e-01], [ 7.09163527e-01, 5.52078099e-01, -5.00000000e-01], [ 6.77281572e-01, 7.35723911e-01, -5.00000000e-01], [ 6.10767838e-01, 6.63497160e-01, -5.00000000e-01], [ 5.46948158e-01, 8.37166478e-01, -5.00000000e-01], [ 4.93229607e-01, 7.54957609e-01, -5.00000000e-01], [ 4.01695425e-01, 9.15773327e-01, -5.00000000e-01], [ 3.62235106e-01, 8.25828858e-01, -5.00000000e-01], [ 7.94125993e-01, 1.32740732e-01, -5.00000000e-01], [ 8.00800813e-01, 1.88651800e-04, -5.00000000e-01], [ 7.57532995e-01, 2.60531900e-01, -5.00000000e-01], [ 7.05311522e-01, 3.82151873e-01, -5.00000000e-01], [ 6.33176967e-01, 4.93188206e-01, -5.00000000e-01], [ 5.44359740e-01, 5.91554707e-01, -5.00000000e-01], [ 4.39850704e-01, 6.73367897e-01, -5.00000000e-01], [ 3.23078188e-01, 7.36661594e-01, -5.00000000e-01], [ 7.01128052e-01, 1.17677998e-01, -5.00000000e-01], [ 7.11649572e-01, 3.33951524e-04, -5.00000000e-01], [ 6.72966890e-01, 2.31894729e-01, -5.00000000e-01], [ 6.25669634e-01, 3.39513212e-01, -5.00000000e-01], [ 5.64027416e-01, 4.39616287e-01, -5.00000000e-01], [ 4.84508482e-01, 5.26668700e-01, -5.00000000e-01], [ 3.91560912e-01, 5.99504467e-01, -5.00000000e-01], [ 2.87602059e-01, 6.55840087e-01, -5.00000000e-01], [ 6.17706136e-01, 1.04228490e-01, -5.00000000e-01], [ 6.24422079e-01, 6.32285718e-04, -5.00000000e-01], [ 5.91740630e-01, 2.04740807e-01, -5.00000000e-01], [ 5.53662436e-01, 3.01062778e-01, -5.00000000e-01], [ 4.96990333e-01, 3.87922085e-01, -5.00000000e-01], [ 4.26905739e-01, 4.64324206e-01, -5.00000000e-01], [ 3.44867105e-01, 5.28103963e-01, -5.00000000e-01], [ 2.53361833e-01, 5.77868140e-01, -5.00000000e-01], [ 5.38389125e-01, 9.17206205e-02, -5.00000000e-01], [ 5.45454368e-01, 8.70266955e-04, -5.00000000e-01], [ 5.17997560e-01, 1.80324497e-01, -5.00000000e-01], [ 4.84931719e-01, 2.64732797e-01, -5.00000000e-01], [ 4.36383812e-01, 3.41220989e-01, -5.00000000e-01], [ 3.74978458e-01, 4.07950157e-01, -5.00000000e-01], [ 3.02772277e-01, 4.63502415e-01, -5.00000000e-01], [ 2.22468936e-01, 5.07437291e-01, -5.00000000e-01], [ 4.64471094e-01, 8.04373661e-02, -5.00000000e-01], [ 4.73417894e-01, 1.03377535e-03, -5.00000000e-01], [ 4.49550108e-01, 1.58190845e-01, -5.00000000e-01], [ 4.20123744e-01, 2.31037319e-01, -5.00000000e-01], [ 3.77857876e-01, 2.96451484e-01, -5.00000000e-01], [ 3.26056063e-01, 3.54556465e-01, -5.00000000e-01], [ 2.63231766e-01, 4.02405818e-01, -5.00000000e-01], [ 1.93502599e-01, 4.41318122e-01, -5.00000000e-01], [ 2.80697996e-01, 3.04138803e-01, -5.00000000e-01], [ 3.26828978e-01, 2.57477836e-01, -5.00000000e-01], [ 2.27278087e-01, 3.45852853e-01, -5.00000000e-01], [ 1.66385745e-01, 3.79356622e-01, -5.00000000e-01], [ 2.41953376e-01, 2.58408137e-01, -5.00000000e-01], [ 2.80636530e-01, 2.22682050e-01, -5.00000000e-01], [ 1.94021253e-01, 2.91186055e-01, -5.00000000e-01], [ 1.39733872e-01, 3.17911899e-01, -5.00000000e-01], [ 2.12482604e-01, 2.15267429e-01, -5.00000000e-01], [ 2.47964916e-01, 2.00029985e-01, -5.00000000e-01], [ 1.65163116e-01, 2.36189539e-01, -5.00000000e-01], [ 1.14601980e-01, 2.59689793e-01, -5.00000000e-01], [ 8.97246141e-02, 2.03521438e-01, -5.00000000e-01], [ 1.45294294e-01, 1.83523348e-01, -5.00000000e-01], [ 6.53411100e-02, 1.48104882e-01, -5.00000000e-01], [ 1.29690654e-01, 1.28069210e-01, -5.00000000e-01], [ 3.66825649e-02, 8.40224902e-02, -5.00000000e-01], [ 1.15178059e-01, 6.50962363e-02, -5.00000000e-01], [ -6.90931371e-03, -1.43821650e-02, -5.00000000e-01], [ 1.13891956e-01, -7.46169753e-03, -5.00000000e-01], [ 2.00104806e-01, 1.68875989e-01, -5.00000000e-01], [ 1.92634871e-01, 1.16656531e-01, -5.00000000e-01], [ 1.88903990e-01, 5.89872913e-02, -5.00000000e-01], [ 1.90411041e-01, -2.07447445e-03, -5.00000000e-01], [ 2.50775614e-01, 1.62140475e-01, -5.00000000e-01], [ 2.53396140e-01, 1.13391119e-01, -5.00000000e-01], [ 2.55727085e-01, 5.80397186e-02, -5.00000000e-01], [ 2.60266073e-01, 6.10581640e-04, -5.00000000e-01], [ 3.30524944e-01, 1.08041301e-03, -5.00000000e-01], [ 3.24967172e-01, 6.21259202e-02, -5.00000000e-01], [ 4.00230719e-01, 1.27521573e-03, -5.00000000e-01], [ 3.95498789e-01, 7.04582546e-02, -5.00000000e-01], [ 3.15952931e-01, 1.20992370e-01, -5.00000000e-01], [ 3.81156850e-01, 1.37671789e-01, -5.00000000e-01], [ 3.01812446e-01, 1.76311672e-01, -5.00000000e-01], [ 3.60992327e-01, 2.01144550e-01, -5.00000000e-01], [ 2.45485487e-01, 9.69400266e-01, -5.00000000e-01], [ 2.21362099e-01, 8.74184899e-01, -5.00000000e-01], [ 8.25793455e-02, 9.96584493e-01, -5.00000000e-01], [ 7.44564944e-02, 8.98711097e-01, -5.00000000e-01], [ -8.25793455e-02, 9.96584493e-01, -5.00000000e-01], [ -7.44723590e-02, 8.98720257e-01, -5.00000000e-01], [ -2.45485487e-01, 9.69400266e-01, -5.00000000e-01], [ -2.20691802e-01, 8.71719923e-01, -5.00000000e-01], [ -4.01695425e-01, 9.15773327e-01, -5.00000000e-01], [ -3.60903765e-01, 8.22973696e-01, -5.00000000e-01], [ -5.46948158e-01, 8.37166478e-01, -5.00000000e-01], [ -4.91337090e-01, 7.52168583e-01, -5.00000000e-01], [ -6.77281572e-01, 7.35723911e-01, -5.00000000e-01], [ -6.08422426e-01, 6.60965795e-01, -5.00000000e-01], [ 1.97440834e-01, 7.79839076e-01, -5.00000000e-01], [ 6.64059355e-02, 8.01246618e-01, -5.00000000e-01], [ -6.62645469e-02, 8.01029588e-01, -5.00000000e-01], [ -1.96854349e-01, 7.78286921e-01, -5.00000000e-01], [ -3.21946321e-01, 7.34659580e-01, -5.00000000e-01], [ -4.36798391e-01, 6.69120390e-01, -5.00000000e-01], [ -5.40612618e-01, 5.87506757e-01, -5.00000000e-01], [ 1.75775694e-01, 6.94385920e-01, -5.00000000e-01], [ 5.91673686e-02, 7.13777718e-01, -5.00000000e-01], [ -5.88119880e-02, 7.12616793e-01, -5.00000000e-01], [ -1.74101170e-01, 6.89878982e-01, -5.00000000e-01], [ -2.84169826e-01, 6.49644503e-01, -5.00000000e-01], [ -3.86858078e-01, 5.93199666e-01, -5.00000000e-01], [ -4.79144350e-01, 5.20924297e-01, -5.00000000e-01], [ 1.54837478e-01, 6.11910244e-01, -5.00000000e-01], [ 5.21907761e-02, 6.29193467e-01, -5.00000000e-01], [ -5.12184367e-02, 6.25994011e-01, -5.00000000e-01], [ -1.53060733e-01, 6.08704209e-01, -5.00000000e-01], [ -2.50417746e-01, 5.73884869e-01, -5.00000000e-01], [ -3.39314709e-01, 5.21301649e-01, -5.00000000e-01], [ -4.20125339e-01, 4.57152827e-01, -5.00000000e-01], [ 1.35887547e-01, 5.37522969e-01, -5.00000000e-01], [ 4.57175793e-02, 5.49353160e-01, -5.00000000e-01], [ -4.41750548e-02, 5.47282691e-01, -5.00000000e-01], [ -1.32346943e-01, 5.31088099e-01, -5.00000000e-01], [ -2.17069383e-01, 5.00276789e-01, -5.00000000e-01], [ -2.95441098e-01, 4.55297464e-01, -5.00000000e-01], [ -3.66529630e-01, 3.99203743e-01, -5.00000000e-01], [ 1.17538501e-01, 4.65928006e-01, -5.00000000e-01], [ 3.96664619e-02, 4.79172226e-01, -5.00000000e-01], [ -3.75472975e-02, 4.75808900e-01, -5.00000000e-01], [ -1.13238534e-01, 4.61485351e-01, -5.00000000e-01], [ -1.87421164e-01, 4.35519238e-01, -5.00000000e-01], [ -2.54075366e-01, 3.93685438e-01, -5.00000000e-01], [ -3.15875504e-01, 3.44415828e-01, -5.00000000e-01], [ -9.42722138e-02, 3.97659750e-01, -5.00000000e-01], [ -3.16082042e-02, 4.12808353e-01, -5.00000000e-01], [ -1.57313402e-01, 3.72682840e-01, -5.00000000e-01], [ -2.13902082e-01, 3.35146284e-01, -5.00000000e-01], [ -2.67798778e-01, 2.92234617e-01, -5.00000000e-01], [ -7.38121164e-02, 3.40587375e-01, -5.00000000e-01], [ -2.54537279e-02, 3.56949863e-01, -5.00000000e-01], [ -1.23885635e-01, 3.12926202e-01, -5.00000000e-01], [ -1.73736614e-01, 2.79179204e-01, -5.00000000e-01], [ -2.22166180e-01, 2.41993646e-01, -5.00000000e-01], [ -4.95025577e-02, 2.94314076e-01, -5.00000000e-01], [ -1.98454920e-02, 3.18279461e-01, -5.00000000e-01], [ -8.72841201e-02, 2.62569352e-01, -5.00000000e-01], [ -1.29852384e-01, 2.26256159e-01, -5.00000000e-01], [ -1.74652198e-01, 1.90687531e-01, -5.00000000e-01], [ -1.29778666e-01, 1.38814436e-01, -5.00000000e-01], [ -8.36228415e-02, 1.77780710e-01, -5.00000000e-01], [ -8.16435423e-02, 7.97110013e-02, -5.00000000e-01], [ -3.02608410e-02, 1.29131270e-01, -5.00000000e-01], [ -4.40670309e-02, 2.18498201e-01, -5.00000000e-01], [ 7.24710626e-03, 1.80262002e-01, -5.00000000e-01], [ -1.03471103e-02, 2.62032046e-01, -5.00000000e-01], [ 3.73861516e-02, 2.31491239e-01, -5.00000000e-01], [ 1.54198095e-02, 3.03574443e-01, -5.00000000e-01], [ 6.32129499e-02, 2.82903258e-01, -5.00000000e-01], [ 8.26525900e-02, 3.39405613e-01, -5.00000000e-01], [ 1.00909910e-01, 4.03015805e-01, -5.00000000e-01], [ 2.64893934e-02, 3.53483382e-01, -5.00000000e-01], [ 3.36435314e-02, 4.12308571e-01, -5.00000000e-01], [ -7.89140509e-01, 6.14212713e-01, -5.00000000e-01], [ -7.08964474e-01, 5.51797869e-01, -5.00000000e-01], [ -8.79473751e-01, 4.75947393e-01, -5.00000000e-01], [ -7.90197698e-01, 4.27606295e-01, -5.00000000e-01], [ -9.45817242e-01, 3.24699469e-01, -5.00000000e-01], [ -8.49856882e-01, 2.91743625e-01, -5.00000000e-01], [ -9.86361303e-01, 1.64594590e-01, -5.00000000e-01], [ -8.86275569e-01, 1.47901385e-01, -5.00000000e-01], [ -1.00000000e+00, -1.22464680e-16, -5.00000000e-01], [ -8.98473805e-01, 1.15381832e-05, -5.00000000e-01], [ -9.86361303e-01, -1.64594590e-01, -5.00000000e-01], [ -8.86174824e-01, -1.47883246e-01, -5.00000000e-01], [ -6.30001456e-01, 4.90383785e-01, -5.00000000e-01], [ -7.02273443e-01, 3.80015358e-01, -5.00000000e-01], [ -7.55313724e-01, 2.59296912e-01, -5.00000000e-01], [ -7.87658470e-01, 1.31485016e-01, -5.00000000e-01], [ -7.98470908e-01, 4.55107425e-05, -5.00000000e-01], [ -7.87494160e-01, -1.31426735e-01, -5.00000000e-01], [ -5.58627682e-01, 4.34835928e-01, -5.00000000e-01], [ -6.22870183e-01, 3.37003542e-01, -5.00000000e-01], [ -6.69946331e-01, 2.29998633e-01, -5.00000000e-01], [ -6.98601469e-01, 1.16688217e-01, -5.00000000e-01], [ -7.08116470e-01, 1.05809214e-04, -5.00000000e-01], [ -6.98261773e-01, -1.16539499e-01, -5.00000000e-01], [ -4.90039236e-01, 3.81447281e-01, -5.00000000e-01], [ -5.46674699e-01, 2.95688707e-01, -5.00000000e-01], [ -5.88083370e-01, 2.01927138e-01, -5.00000000e-01], [ -6.13127891e-01, 1.02579208e-01, -5.00000000e-01], [ -6.21326911e-01, 2.49679885e-04, -5.00000000e-01], [ -6.12659159e-01, -1.02238675e-01, -5.00000000e-01], [ -4.28201630e-01, 3.33102542e-01, -5.00000000e-01], [ -4.78292897e-01, 2.58422199e-01, -5.00000000e-01], [ -5.14588632e-01, 1.76707278e-01, -5.00000000e-01], [ -5.36209382e-01, 9.00148497e-02, -5.00000000e-01], [ -5.43295990e-01, 5.06463641e-04, -5.00000000e-01], [ -5.35354600e-01, -8.93132854e-02, -5.00000000e-01], [ -3.70108778e-01, 2.87293073e-01, -5.00000000e-01], [ -4.14823967e-01, 2.23456652e-01, -5.00000000e-01], [ -4.46176995e-01, 1.53226227e-01, -5.00000000e-01], [ -4.64366203e-01, 7.86053306e-02, -5.00000000e-01], [ -4.70979406e-01, 1.02297782e-03, -5.00000000e-01], [ -4.62590465e-01, -7.71173269e-02, -5.00000000e-01], [ -3.98997832e-01, 6.89882561e-02, -5.00000000e-01], [ -3.84007718e-01, 1.31869003e-01, -5.00000000e-01], [ -4.02767806e-01, 2.14475434e-03, -5.00000000e-01], [ -3.93927304e-01, -6.55498719e-02, -5.00000000e-01], [ -3.37106322e-01, 6.21262340e-02, -5.00000000e-01], [ -3.30209654e-01, 1.13360219e-01, -5.00000000e-01], [ -3.35031657e-01, 5.42266022e-03, -5.00000000e-01], [ -3.27721161e-01, -5.42580733e-02, -5.00000000e-01], [ -2.83447084e-01, 6.21745720e-02, -5.00000000e-01], [ -2.92001062e-01, 1.00268454e-01, -5.00000000e-01], [ -2.73720464e-01, 1.23812993e-02, -5.00000000e-01], [ -2.63751777e-01, -4.33840760e-02, -5.00000000e-01], [ -1.98646355e-01, -3.27574407e-02, -5.00000000e-01], [ -2.13311244e-01, 2.71005688e-02, -5.00000000e-01], [ -1.25363626e-01, -2.31240997e-02, -5.00000000e-01], [ -1.50402001e-01, 4.80422198e-02, -5.00000000e-01], [ -2.35376990e-01, 8.01922238e-02, -5.00000000e-01], [ -1.81647343e-01, 1.06212482e-01, -5.00000000e-01], [ -2.61648693e-01, 1.25022130e-01, -5.00000000e-01], [ -2.20441300e-01, 1.56334892e-01, -5.00000000e-01], [ -2.65822474e-01, 2.01206908e-01, -5.00000000e-01], [ -3.15933525e-01, 2.43651325e-01, -5.00000000e-01], [ -3.03727391e-01, 1.58021732e-01, -5.00000000e-01], [ -3.58146942e-01, 1.91331513e-01, -5.00000000e-01], [ -9.45817242e-01, -3.24699469e-01, -5.00000000e-01], [ -8.49758028e-01, -2.91753303e-01, -5.00000000e-01], [ -8.79473751e-01, -4.75947393e-01, -5.00000000e-01], [ -7.90200242e-01, -4.27674318e-01, -5.00000000e-01], [ -7.89140509e-01, -6.14212713e-01, -5.00000000e-01], [ -7.09077565e-01, -5.51918815e-01, -5.00000000e-01], [ -6.77281572e-01, -7.35723911e-01, -5.00000000e-01], [ -6.08569412e-01, -6.61074211e-01, -5.00000000e-01], [ -5.46948158e-01, -8.37166478e-01, -5.00000000e-01], [ -4.91428805e-01, -7.52168929e-01, -5.00000000e-01], [ -4.01695425e-01, -9.15773327e-01, -5.00000000e-01], [ -3.60888838e-01, -8.22761670e-01, -5.00000000e-01], [ -7.55188846e-01, -2.59345642e-01, -5.00000000e-01], [ -7.02337606e-01, -3.80192065e-01, -5.00000000e-01], [ -6.32857312e-01, -4.92618433e-01, -5.00000000e-01], [ -5.43151014e-01, -5.89986003e-01, -5.00000000e-01], [ -4.37118826e-01, -6.68993686e-01, -5.00000000e-01], [ -3.20766354e-01, -7.31326287e-01, -5.00000000e-01], [ -6.69734997e-01, -2.30080512e-01, -5.00000000e-01], [ -6.22993936e-01, -3.37335567e-01, -5.00000000e-01], [ -5.59194551e-01, -4.35298937e-01, -5.00000000e-01], [ -4.79828309e-01, -5.21111684e-01, -5.00000000e-01], [ -3.87501919e-01, -5.92949943e-01, -5.00000000e-01], [ -2.84426825e-01, -6.48495027e-01, -5.00000000e-01], [ -5.87684351e-01, -2.02039854e-01, -5.00000000e-01], [ -5.46979032e-01, -2.96347299e-01, -5.00000000e-01], [ -4.93900401e-01, -3.84490387e-01, -5.00000000e-01], [ -4.23918729e-01, -4.60219985e-01, -5.00000000e-01], [ -3.40561104e-01, -5.20838061e-01, -5.00000000e-01], [ -2.49643040e-01, -5.69140160e-01, -5.00000000e-01], [ -5.13658677e-01, -1.76864123e-01, -5.00000000e-01], [ -4.78660176e-01, -2.59652919e-01, -5.00000000e-01], [ -4.29973501e-01, -3.34721367e-01, -5.00000000e-01], [ -3.69196403e-01, -4.00374180e-01, -5.00000000e-01], [ -2.97634891e-01, -4.54622635e-01, -5.00000000e-01], [ -2.18156550e-01, -4.97266712e-01, -5.00000000e-01], [ -4.43955972e-01, -1.53433052e-01, -5.00000000e-01], [ -4.14976040e-01, -2.25780487e-01, -5.00000000e-01], [ -3.73212995e-01, -2.90523705e-01, -5.00000000e-01], [ -3.21264722e-01, -3.47542486e-01, -5.00000000e-01], [ -2.57973317e-01, -3.92813300e-01, -5.00000000e-01], [ -1.88659023e-01, -4.29830508e-01, -5.00000000e-01], [ -2.76959851e-01, -2.97681175e-01, -5.00000000e-01], [ -3.22068080e-01, -2.50675254e-01, -5.00000000e-01], [ -2.21359240e-01, -3.34292006e-01, -5.00000000e-01], [ -1.61403511e-01, -3.67396927e-01, -5.00000000e-01], [ -2.36755182e-01, -2.48291400e-01, -5.00000000e-01], [ -2.76170203e-01, -2.14736566e-01, -5.00000000e-01], [ -1.88145259e-01, -2.77458362e-01, -5.00000000e-01], [ -1.33977883e-01, -3.03968290e-01, -5.00000000e-01], [ -2.07536544e-01, -2.03087349e-01, -5.00000000e-01], [ -2.44322863e-01, -1.89623778e-01, -5.00000000e-01], [ -1.60891409e-01, -2.22173809e-01, -5.00000000e-01], [ -1.08132613e-01, -2.43259688e-01, -5.00000000e-01], [ -8.28302097e-02, -1.83773619e-01, -5.00000000e-01], [ -1.41840587e-01, -1.63689740e-01, -5.00000000e-01], [ -5.28309761e-02, -1.16741673e-01, -5.00000000e-01], [ -1.27483869e-01, -9.76683493e-02, -5.00000000e-01], [ -1.97979852e-01, -1.52716684e-01, -5.00000000e-01], [ -1.94512087e-01, -9.49401261e-02, -5.00000000e-01], [ -2.49063661e-01, -1.50709568e-01, -5.00000000e-01], [ -2.55937772e-01, -1.00112636e-01, -5.00000000e-01], [ -3.15961679e-01, -1.13123573e-01, -5.00000000e-01], [ -3.78395823e-01, -1.32030107e-01, -5.00000000e-01], [ -2.99920321e-01, -1.68386817e-01, -5.00000000e-01], [ -3.56808685e-01, -1.95651482e-01, -5.00000000e-01], [ -2.45485487e-01, -9.69400266e-01, -5.00000000e-01], [ -2.20527452e-01, -8.70963424e-01, -5.00000000e-01], [ -8.25793455e-02, -9.96584493e-01, -5.00000000e-01], [ -7.41639184e-02, -8.95445060e-01, -5.00000000e-01], [ 8.25793455e-02, -9.96584493e-01, -5.00000000e-01], [ 7.42192906e-02, -8.95487034e-01, -5.00000000e-01], [ 2.45485487e-01, -9.69400266e-01, -5.00000000e-01], [ 2.20573235e-01, -8.71047175e-01, -5.00000000e-01], [ 4.01695425e-01, -9.15773327e-01, -5.00000000e-01], [ 3.60903494e-01, -8.22802941e-01, -5.00000000e-01], [ 5.46948158e-01, -8.37166478e-01, -5.00000000e-01], [ 4.91381119e-01, -7.52102570e-01, -5.00000000e-01], [ -1.95947086e-01, -7.74127277e-01, -5.00000000e-01], [ -6.58591700e-02, -7.95934883e-01, -5.00000000e-01], [ 6.59950160e-02, -7.96009686e-01, -5.00000000e-01], [ 1.96033858e-01, -7.74239363e-01, -5.00000000e-01], [ 3.20688739e-01, -7.31187985e-01, -5.00000000e-01], [ 4.36565689e-01, -6.68176192e-01, -5.00000000e-01], [ -1.73710483e-01, -6.86594431e-01, -5.00000000e-01], [ -5.83403733e-02, -7.06068825e-01, -5.00000000e-01], [ 5.85663516e-02, -7.06222728e-01, -5.00000000e-01], [ 1.73845433e-01, -6.86833114e-01, -5.00000000e-01], [ 2.84310702e-01, -6.48392566e-01, -5.00000000e-01], [ 3.87077327e-01, -5.92417398e-01, -5.00000000e-01], [ -1.52324550e-01, -6.02613585e-01, -5.00000000e-01], [ -5.10827736e-02, -6.20032612e-01, -5.00000000e-01], [ 5.14407375e-02, -6.20224621e-01, -5.00000000e-01], [ 1.52492169e-01, -6.03046508e-01, -5.00000000e-01], [ 2.49356849e-01, -5.69065471e-01, -5.00000000e-01], [ 3.39522909e-01, -5.19665127e-01, -5.00000000e-01], [ -1.32913591e-01, -5.26851074e-01, -5.00000000e-01], [ -4.44373752e-02, -5.42756409e-01, -5.00000000e-01], [ 4.50390459e-02, -5.43028575e-01, -5.00000000e-01], [ 1.33228954e-01, -5.27995406e-01, -5.00000000e-01], [ 2.17713616e-01, -4.97612407e-01, -5.00000000e-01], [ 2.96632422e-01, -4.54081308e-01, -5.00000000e-01], [ -1.14420539e-01, -4.55704117e-01, -5.00000000e-01], [ -3.79657707e-02, -4.70906926e-01, -5.00000000e-01], [ 3.90798645e-02, -4.71331248e-01, -5.00000000e-01], [ 1.15221008e-01, -4.58994868e-01, -5.00000000e-01], [ 1.87693599e-01, -4.30660195e-01, -5.00000000e-01], [ 2.56191789e-01, -3.92327216e-01, -5.00000000e-01], [ 9.77205966e-02, -3.94673911e-01, -5.00000000e-01], [ 3.36893742e-02, -4.06727121e-01, -5.00000000e-01], [ 1.58803158e-01, -3.68132920e-01, -5.00000000e-01], [ 2.17966632e-01, -3.34109450e-01, -5.00000000e-01], [ 7.83407097e-02, -3.33990665e-01, -5.00000000e-01], [ 2.86928174e-02, -3.48638215e-01, -5.00000000e-01], [ 1.29463668e-01, -3.09306875e-01, -5.00000000e-01], [ 1.81227986e-01, -2.78393827e-01, -5.00000000e-01], [ 5.64523840e-02, -2.84774155e-01, -5.00000000e-01], [ 2.50675824e-02, -3.08228657e-01, -5.00000000e-01], [ 9.81604613e-02, -2.56263578e-01, -5.00000000e-01], [ 1.45747568e-01, -2.24725813e-01, -5.00000000e-01], [ 1.08776353e-01, -1.69704406e-01, -5.00000000e-01], [ 5.96672230e-02, -2.08411981e-01, -5.00000000e-01], [ 6.52344214e-02, -1.07234898e-01, -5.00000000e-01], [ 1.12299685e-02, -1.59491418e-01, -5.00000000e-01], [ 1.94489428e-02, -2.49212860e-01, -5.00000000e-01], [ -2.75711446e-02, -2.14453527e-01, -5.00000000e-01], [ -1.01419274e-02, -2.90912747e-01, -5.00000000e-01], [ -5.63990856e-02, -2.68644868e-01, -5.00000000e-01], [ -7.81326349e-02, -3.26377817e-01, -5.00000000e-01], [ -9.65225590e-02, -3.89249328e-01, -5.00000000e-01], [ -2.31032455e-02, -3.43177165e-01, -5.00000000e-01], [ -3.13773433e-02, -4.05719997e-01, -5.00000000e-01], [ 6.77281572e-01, -7.35723911e-01, -5.00000000e-01], [ 6.08411375e-01, -6.60869436e-01, -5.00000000e-01], [ 7.89140509e-01, -6.14212713e-01, -5.00000000e-01], [ 7.08543237e-01, -5.51440045e-01, -5.00000000e-01], [ 8.79473751e-01, -4.75947393e-01, -5.00000000e-01], [ 7.88509514e-01, -4.26698136e-01, -5.00000000e-01], [ 9.45817242e-01, -3.24699469e-01, -5.00000000e-01], [ 8.49108858e-01, -2.91497351e-01, -5.00000000e-01], [ 9.86361303e-01, -1.64594590e-01, -5.00000000e-01], [ 8.85646606e-01, -1.47773471e-01, -5.00000000e-01], [ 5.40399291e-01, -5.86902930e-01, -5.00000000e-01], [ 6.31661212e-01, -4.91529504e-01, -5.00000000e-01], [ 7.06154712e-01, -3.82110644e-01, -5.00000000e-01], [ 7.57207596e-01, -2.59933027e-01, -5.00000000e-01], [ 7.90560453e-01, -1.31868883e-01, -5.00000000e-01], [ 4.79134872e-01, -5.20246274e-01, -5.00000000e-01], [ 5.57358575e-01, -4.33573270e-01, -5.00000000e-01], [ 6.22649467e-01, -3.36858332e-01, -5.00000000e-01], [ 6.70334308e-01, -2.30102238e-01, -5.00000000e-01], [ 7.00296083e-01, -1.16745852e-01, -5.00000000e-01], [ 4.20218711e-01, -4.56063930e-01, -5.00000000e-01], [ 4.90701218e-01, -3.81516225e-01, -5.00000000e-01], [ 5.48809022e-01, -2.96865907e-01, -5.00000000e-01], [ 5.88345346e-01, -2.02001938e-01, -5.00000000e-01], [ 6.14676740e-01, -1.02418815e-01, -5.00000000e-01], [ 3.67922567e-01, -3.98927381e-01, -5.00000000e-01], [ 4.28983572e-01, -3.33156683e-01, -5.00000000e-01], [ 4.78469364e-01, -2.58774492e-01, -5.00000000e-01], [ 5.14798664e-01, -1.76976670e-01, -5.00000000e-01], [ 5.37552876e-01, -8.96851608e-02, -5.00000000e-01], [ 3.18991616e-01, -3.45088841e-01, -5.00000000e-01], [ 3.72496945e-01, -2.88521239e-01, -5.00000000e-01], [ 4.17437712e-01, -2.25772747e-01, -5.00000000e-01], [ 4.46270753e-01, -1.54015799e-01, -5.00000000e-01], [ 4.65793651e-01, -7.81566215e-02, -5.00000000e-01], [ 3.83645678e-01, -1.33842084e-01, -5.00000000e-01], [ 3.57498091e-01, -1.93408546e-01, -5.00000000e-01], [ 3.94991079e-01, -6.75531524e-02, -5.00000000e-01], [ 3.23108985e-01, -1.16767010e-01, -5.00000000e-01], [ 3.07405476e-01, -1.66484070e-01, -5.00000000e-01], [ 3.28629393e-01, -5.96308040e-02, -5.00000000e-01], [ 2.69488721e-01, -1.08491179e-01, -5.00000000e-01], [ 2.71915330e-01, -1.47551966e-01, -5.00000000e-01], [ 2.65001259e-01, -5.64391238e-02, -5.00000000e-01], [ 2.02706372e-01, -6.38527005e-02, -5.00000000e-01], [ 1.35742212e-01, -7.83273135e-02, -5.00000000e-01], [ 2.18918251e-01, -1.20081827e-01, -5.00000000e-01], [ 1.63747295e-01, -1.39842812e-01, -5.00000000e-01], [ 2.38577514e-01, -1.67677403e-01, -5.00000000e-01], [ 1.94295104e-01, -1.94156801e-01, -5.00000000e-01], [ 2.30940050e-01, -2.44114153e-01, -5.00000000e-01], [ 2.74443311e-01, -2.95203178e-01, -5.00000000e-01], [ 2.74528072e-01, -2.06666991e-01, -5.00000000e-01], [ 3.20977283e-01, -2.46876061e-01, -5.00000000e-01] ]) _connectivity = np.array([ [ 1, 2, 3, 4, 5, 6, 7, 8], [ 2, 9, 10, 3, 6, 11, 12, 7], [ 9, 13, 14, 10, 11, 15, 16, 12], [ 13, 17, 18, 14, 15, 19, 20, 16], [ 17, 21, 22, 18, 19, 23, 24, 20], [ 21, 25, 26, 22, 23, 27, 28, 24], [ 25, 29, 30, 26, 27, 31, 32, 28], [ 4, 3, 33, 34, 8, 7, 35, 36], [ 3, 10, 37, 33, 7, 12, 38, 35], [ 10, 14, 39, 37, 12, 16, 40, 38], [ 14, 18, 41, 39, 16, 20, 42, 40], [ 18, 22, 43, 41, 20, 24, 44, 42], [ 22, 26, 45, 43, 24, 28, 46, 44], [ 26, 30, 47, 45, 28, 32, 48, 46], [ 34, 33, 49, 50, 36, 35, 51, 52], [ 33, 37, 53, 49, 35, 38, 54, 51], [ 37, 39, 55, 53, 38, 40, 56, 54], [ 39, 41, 57, 55, 40, 42, 58, 56], [ 41, 43, 59, 57, 42, 44, 60, 58], [ 43, 45, 61, 59, 44, 46, 62, 60], [ 45, 47, 63, 61, 46, 48, 64, 62], [ 50, 49, 65, 66, 52, 51, 67, 68], [ 49, 53, 69, 65, 51, 54, 70, 67], [ 53, 55, 71, 69, 54, 56, 72, 70], [ 55, 57, 73, 71, 56, 58, 74, 72], [ 57, 59, 75, 73, 58, 60, 76, 74], [ 59, 61, 77, 75, 60, 62, 78, 76], [ 61, 63, 79, 77, 62, 64, 80, 78], [ 66, 65, 81, 82, 68, 67, 83, 84], [ 65, 69, 85, 81, 67, 70, 86, 83], [ 69, 71, 87, 85, 70, 72, 88, 86], [ 71, 73, 89, 87, 72, 74, 90, 88], [ 73, 75, 91, 89, 74, 76, 92, 90], [ 75, 77, 93, 91, 76, 78, 94, 92], [ 77, 79, 95, 93, 78, 80, 96, 94], [ 82, 81, 97, 98, 84, 83, 99, 100], [ 81, 85, 101, 97, 83, 86, 102, 99], [ 85, 87, 103, 101, 86, 88, 104, 102], [ 87, 89, 105, 103, 88, 90, 106, 104], [ 89, 91, 107, 105, 90, 92, 108, 106], [ 91, 93, 109, 107, 92, 94, 110, 108], [ 93, 95, 111, 109, 94, 96, 112, 110], [ 105, 107, 113, 114, 106, 108, 115, 116], [ 107, 109, 117, 113, 108, 110, 118, 115], [ 109, 111, 119, 117, 110, 112, 120, 118], [ 114, 113, 121, 122, 116, 115, 123, 124], [ 113, 117, 125, 121, 115, 118, 126, 123], [ 117, 119, 127, 125, 118, 120, 128, 126], [ 122, 121, 129, 130, 124, 123, 131, 132], [ 121, 125, 133, 129, 123, 126, 134, 131], [ 125, 127, 135, 133, 126, 128, 136, 134], [ 135, 137, 138, 133, 136, 139, 140, 134], [ 137, 141, 142, 138, 139, 143, 144, 140], [ 141, 145, 146, 142, 143, 147, 148, 144], [ 145, 149, 150, 146, 147, 151, 152, 148], [ 133, 138, 153, 129, 134, 140, 154, 131], [ 138, 142, 155, 153, 140, 144, 156, 154], [ 142, 146, 157, 155, 144, 148, 158, 156], [ 146, 150, 159, 157, 148, 152, 160, 158], [ 129, 153, 161, 130, 131, 154, 162, 132], [ 153, 155, 163, 161, 154, 156, 164, 162], [ 155, 157, 165, 163, 156, 158, 166, 164], [ 157, 159, 167, 165, 158, 160, 168, 166], [ 167, 169, 170, 165, 168, 171, 172, 166], [ 169, 173, 174, 170, 171, 175, 176, 172], [ 173, 98, 97, 174, 175, 100, 99, 176], [ 165, 170, 177, 163, 166, 172, 178, 164], [ 170, 174, 179, 177, 172, 176, 180, 178], [ 174, 97, 101, 179, 176, 99, 102, 180], [ 163, 177, 181, 161, 164, 178, 182, 162], [ 177, 179, 183, 181, 178, 180, 184, 182], [ 179, 101, 103, 183, 180, 102, 104, 184], [ 161, 181, 122, 130, 162, 182, 124, 132], [ 181, 183, 114, 122, 182, 184, 116, 124], [ 183, 103, 105, 114, 184, 104, 106, 116], [ 29, 185, 186, 30, 31, 187, 188, 32], [ 185, 189, 190, 186, 187, 191, 192, 188], [ 189, 193, 194, 190, 191, 195, 196, 192], [ 193, 197, 198, 194, 195, 199, 200, 196], [ 197, 201, 202, 198, 199, 203, 204, 200], [ 201, 205, 206, 202, 203, 207, 208, 204], [ 205, 209, 210, 206, 207, 211, 212, 208], [ 30, 186, 213, 47, 32, 188, 214, 48], [ 186, 190, 215, 213, 188, 192, 216, 214], [ 190, 194, 217, 215, 192, 196, 218, 216], [ 194, 198, 219, 217, 196, 200, 220, 218], [ 198, 202, 221, 219, 200, 204, 222, 220], [ 202, 206, 223, 221, 204, 208, 224, 222], [ 206, 210, 225, 223, 208, 212, 226, 224], [ 47, 213, 227, 63, 48, 214, 228, 64], [ 213, 215, 229, 227, 214, 216, 230, 228], [ 215, 217, 231, 229, 216, 218, 232, 230], [ 217, 219, 233, 231, 218, 220, 234, 232], [ 219, 221, 235, 233, 220, 222, 236, 234], [ 221, 223, 237, 235, 222, 224, 238, 236], [ 223, 225, 239, 237, 224, 226, 240, 238], [ 63, 227, 241, 79, 64, 228, 242, 80], [ 227, 229, 243, 241, 228, 230, 244, 242], [ 229, 231, 245, 243, 230, 232, 246, 244], [ 231, 233, 247, 245, 232, 234, 248, 246], [ 233, 235, 249, 247, 234, 236, 250, 248], [ 235, 237, 251, 249, 236, 238, 252, 250], [ 237, 239, 253, 251, 238, 240, 254, 252], [ 79, 241, 255, 95, 80, 242, 256, 96], [ 241, 243, 257, 255, 242, 244, 258, 256], [ 243, 245, 259, 257, 244, 246, 260, 258], [ 245, 247, 261, 259, 246, 248, 262, 260], [ 247, 249, 263, 261, 248, 250, 264, 262], [ 249, 251, 265, 263, 250, 252, 266, 264], [ 251, 253, 267, 265, 252, 254, 268, 266], [ 95, 255, 269, 111, 96, 256, 270, 112], [ 255, 257, 271, 269, 256, 258, 272, 270], [ 257, 259, 273, 271, 258, 260, 274, 272], [ 259, 261, 275, 273, 260, 262, 276, 274], [ 261, 263, 277, 275, 262, 264, 278, 276], [ 263, 265, 279, 277, 264, 266, 280, 278], [ 265, 267, 281, 279, 266, 268, 282, 280], [ 273, 275, 283, 284, 274, 276, 285, 286], [ 275, 277, 287, 283, 276, 278, 288, 285], [ 277, 279, 289, 287, 278, 280, 290, 288], [ 279, 281, 291, 289, 280, 282, 292, 290], [ 284, 283, 293, 294, 286, 285, 295, 296], [ 283, 287, 297, 293, 285, 288, 298, 295], [ 287, 289, 299, 297, 288, 290, 300, 298], [ 289, 291, 301, 299, 290, 292, 302, 300], [ 294, 293, 303, 304, 296, 295, 305, 306], [ 293, 297, 307, 303, 295, 298, 308, 305], [ 297, 299, 309, 307, 298, 300, 310, 308], [ 299, 301, 311, 309, 300, 302, 312, 310], [ 311, 313, 314, 309, 312, 315, 316, 310], [ 313, 317, 318, 314, 315, 319, 320, 316], [ 317, 149, 145, 318, 319, 151, 147, 320], [ 309, 314, 321, 307, 310, 316, 322, 308], [ 314, 318, 323, 321, 316, 320, 324, 322], [ 318, 145, 141, 323, 320, 147, 143, 324], [ 307, 321, 325, 303, 308, 322, 326, 305], [ 321, 323, 327, 325, 322, 324, 328, 326], [ 323, 141, 137, 327, 324, 143, 139, 328], [ 303, 325, 329, 304, 305, 326, 330, 306], [ 325, 327, 331, 329, 326, 328, 332, 330], [ 327, 137, 135, 331, 328, 139, 136, 332], [ 135, 127, 333, 331, 136, 128, 334, 332], [ 127, 119, 335, 333, 128, 120, 336, 334], [ 119, 111, 269, 335, 120, 112, 270, 336], [ 331, 333, 337, 329, 332, 334, 338, 330], [ 333, 335, 339, 337, 334, 336, 340, 338], [ 335, 269, 271, 339, 336, 270, 272, 340], [ 329, 337, 294, 304, 330, 338, 296, 306], [ 337, 339, 284, 294, 338, 340, 286, 296], [ 339, 271, 273, 284, 340, 272, 274, 286], [ 209, 341, 342, 210, 211, 343, 344, 212], [ 341, 345, 346, 342, 343, 347, 348, 344], [ 345, 349, 350, 346, 347, 351, 352, 348], [ 349, 353, 354, 350, 351, 355, 356, 352], [ 353, 357, 358, 354, 355, 359, 360, 356], [ 357, 361, 362, 358, 359, 363, 364, 360], [ 210, 342, 365, 225, 212, 344, 366, 226], [ 342, 346, 367, 365, 344, 348, 368, 366], [ 346, 350, 369, 367, 348, 352, 370, 368], [ 350, 354, 371, 369, 352, 356, 372, 370], [ 354, 358, 373, 371, 356, 360, 374, 372], [ 358, 362, 375, 373, 360, 364, 376, 374], [ 225, 365, 377, 239, 226, 366, 378, 240], [ 365, 367, 379, 377, 366, 368, 380, 378], [ 367, 369, 381, 379, 368, 370, 382, 380], [ 369, 371, 383, 381, 370, 372, 384, 382], [ 371, 373, 385, 383, 372, 374, 386, 384], [ 373, 375, 387, 385, 374, 376, 388, 386], [ 239, 377, 389, 253, 240, 378, 390, 254], [ 377, 379, 391, 389, 378, 380, 392, 390], [ 379, 381, 393, 391, 380, 382, 394, 392], [ 381, 383, 395, 393, 382, 384, 396, 394], [ 383, 385, 397, 395, 384, 386, 398, 396], [ 385, 387, 399, 397, 386, 388, 400, 398], [ 253, 389, 401, 267, 254, 390, 402, 268], [ 389, 391, 403, 401, 390, 392, 404, 402], [ 391, 393, 405, 403, 392, 394, 406, 404], [ 393, 395, 407, 405, 394, 396, 408, 406], [ 395, 397, 409, 407, 396, 398, 410, 408], [ 397, 399, 411, 409, 398, 400, 412, 410], [ 267, 401, 413, 281, 268, 402, 414, 282], [ 401, 403, 415, 413, 402, 404, 416, 414], [ 403, 405, 417, 415, 404, 406, 418, 416], [ 405, 407, 419, 417, 406, 408, 420, 418], [ 407, 409, 421, 419, 408, 410, 422, 420], [ 409, 411, 423, 421, 410, 412, 424, 422], [ 417, 419, 425, 426, 418, 420, 427, 428], [ 419, 421, 429, 425, 420, 422, 430, 427], [ 421, 423, 431, 429, 422, 424, 432, 430], [ 426, 425, 433, 434, 428, 427, 435, 436], [ 425, 429, 437, 433, 427, 430, 438, 435], [ 429, 431, 439, 437, 430, 432, 440, 438], [ 434, 433, 441, 442, 436, 435, 443, 444], [ 433, 437, 445, 441, 435, 438, 446, 443], [ 437, 439, 447, 445, 438, 440, 448, 446], [ 447, 449, 450, 445, 448, 451, 452, 446], [ 449, 453, 454, 450, 451, 455, 456, 452], [ 453, 149, 317, 454, 455, 151, 319, 456], [ 445, 450, 457, 441, 446, 452, 458, 443], [ 450, 454, 459, 457, 452, 456, 460, 458], [ 454, 317, 313, 459, 456, 319, 315, 460], [ 441, 457, 461, 442, 443, 458, 462, 444], [ 457, 459, 463, 461, 458, 460, 464, 462], [ 459, 313, 311, 463, 460, 315, 312, 464], [ 311, 301, 465, 463, 312, 302, 466, 464], [ 301, 291, 467, 465, 302, 292, 468, 466], [ 291, 281, 413, 467, 292, 282, 414, 468], [ 463, 465, 469, 461, 464, 466, 470, 462], [ 465, 467, 471, 469, 466, 468, 472, 470], [ 467, 413, 415, 471, 468, 414, 416, 472], [ 461, 469, 434, 442, 462, 470, 436, 444], [ 469, 471, 426, 434, 470, 472, 428, 436], [ 471, 415, 417, 426, 472, 416, 418, 428], [ 361, 473, 474, 362, 363, 475, 476, 364], [ 473, 477, 478, 474, 475, 479, 480, 476], [ 477, 481, 482, 478, 479, 483, 484, 480], [ 481, 485, 486, 482, 483, 487, 488, 484], [ 485, 489, 490, 486, 487, 491, 492, 488], [ 489, 493, 494, 490, 491, 495, 496, 492], [ 362, 474, 497, 375, 364, 476, 498, 376], [ 474, 478, 499, 497, 476, 480, 500, 498], [ 478, 482, 501, 499, 480, 484, 502, 500], [ 482, 486, 503, 501, 484, 488, 504, 502], [ 486, 490, 505, 503, 488, 492, 506, 504], [ 490, 494, 507, 505, 492, 496, 508, 506], [ 375, 497, 509, 387, 376, 498, 510, 388], [ 497, 499, 511, 509, 498, 500, 512, 510], [ 499, 501, 513, 511, 500, 502, 514, 512], [ 501, 503, 515, 513, 502, 504, 516, 514], [ 503, 505, 517, 515, 504, 506, 518, 516], [ 505, 507, 519, 517, 506, 508, 520, 518], [ 387, 509, 521, 399, 388, 510, 522, 400], [ 509, 511, 523, 521, 510, 512, 524, 522], [ 511, 513, 525, 523, 512, 514, 526, 524], [ 513, 515, 527, 525, 514, 516, 528, 526], [ 515, 517, 529, 527, 516, 518, 530, 528], [ 517, 519, 531, 529, 518, 520, 532, 530], [ 399, 521, 533, 411, 400, 522, 534, 412], [ 521, 523, 535, 533, 522, 524, 536, 534], [ 523, 525, 537, 535, 524, 526, 538, 536], [ 525, 527, 539, 537, 526, 528, 540, 538], [ 527, 529, 541, 539, 528, 530, 542, 540], [ 529, 531, 543, 541, 530, 532, 544, 542], [ 411, 533, 545, 423, 412, 534, 546, 424], [ 533, 535, 547, 545, 534, 536, 548, 546], [ 535, 537, 549, 547, 536, 538, 550, 548], [ 537, 539, 551, 549, 538, 540, 552, 550], [ 539, 541, 553, 551, 540, 542, 554, 552], [ 541, 543, 555, 553, 542, 544, 556, 554], [ 549, 551, 557, 558, 550, 552, 559, 560], [ 551, 553, 561, 557, 552, 554, 562, 559], [ 553, 555, 563, 561, 554, 556, 564, 562], [ 558, 557, 565, 566, 560, 559, 567, 568], [ 557, 561, 569, 565, 559, 562, 570, 567], [ 561, 563, 571, 569, 562, 564, 572, 570], [ 566, 565, 573, 574, 568, 567, 575, 576], [ 565, 569, 577, 573, 567, 570, 578, 575], [ 569, 571, 579, 577, 570, 572, 580, 578], [ 579, 581, 582, 577, 580, 583, 584, 578], [ 581, 585, 586, 582, 583, 587, 588, 584], [ 585, 149, 453, 586, 587, 151, 455, 588], [ 577, 582, 589, 573, 578, 584, 590, 575], [ 582, 586, 591, 589, 584, 588, 592, 590], [ 586, 453, 449, 591, 588, 455, 451, 592], [ 573, 589, 593, 574, 575, 590, 594, 576], [ 589, 591, 595, 593, 590, 592, 596, 594], [ 591, 449, 447, 595, 592, 451, 448, 596], [ 447, 439, 597, 595, 448, 440, 598, 596], [ 439, 431, 599, 597, 440, 432, 600, 598], [ 431, 423, 545, 599, 432, 424, 546, 600], [ 595, 597, 601, 593, 596, 598, 602, 594], [ 597, 599, 603, 601, 598, 600, 604, 602], [ 599, 545, 547, 603, 600, 546, 548, 604], [ 593, 601, 566, 574, 594, 602, 568, 576], [ 601, 603, 558, 566, 602, 604, 560, 568], [ 603, 547, 549, 558, 604, 548, 550, 560], [ 493, 605, 606, 494, 495, 607, 608, 496], [ 605, 609, 610, 606, 607, 611, 612, 608], [ 609, 613, 614, 610, 611, 615, 616, 612], [ 613, 617, 618, 614, 615, 619, 620, 616], [ 617, 621, 622, 618, 619, 623, 624, 620], [ 621, 625, 626, 622, 623, 627, 628, 624], [ 494, 606, 629, 507, 496, 608, 630, 508], [ 606, 610, 631, 629, 608, 612, 632, 630], [ 610, 614, 633, 631, 612, 616, 634, 632], [ 614, 618, 635, 633, 616, 620, 636, 634], [ 618, 622, 637, 635, 620, 624, 638, 636], [ 622, 626, 639, 637, 624, 628, 640, 638], [ 507, 629, 641, 519, 508, 630, 642, 520], [ 629, 631, 643, 641, 630, 632, 644, 642], [ 631, 633, 645, 643, 632, 634, 646, 644], [ 633, 635, 647, 645, 634, 636, 648, 646], [ 635, 637, 649, 647, 636, 638, 650, 648], [ 637, 639, 651, 649, 638, 640, 652, 650], [ 519, 641, 653, 531, 520, 642, 654, 532], [ 641, 643, 655, 653, 642, 644, 656, 654], [ 643, 645, 657, 655, 644, 646, 658, 656], [ 645, 647, 659, 657, 646, 648, 660, 658], [ 647, 649, 661, 659, 648, 650, 662, 660], [ 649, 651, 663, 661, 650, 652, 664, 662], [ 531, 653, 665, 543, 532, 654, 666, 544], [ 653, 655, 667, 665, 654, 656, 668, 666], [ 655, 657, 669, 667, 656, 658, 670, 668], [ 657, 659, 671, 669, 658, 660, 672, 670], [ 659, 661, 673, 671, 660, 662, 674, 672], [ 661, 663, 675, 673, 662, 664, 676, 674], [ 543, 665, 677, 555, 544, 666, 678, 556], [ 665, 667, 679, 677, 666, 668, 680, 678], [ 667, 669, 681, 679, 668, 670, 682, 680], [ 669, 671, 683, 681, 670, 672, 684, 682], [ 671, 673, 685, 683, 672, 674, 686, 684], [ 673, 675, 687, 685, 674, 676, 688, 686], [ 681, 683, 689, 690, 682, 684, 691, 692], [ 683, 685, 693, 689, 684, 686, 694, 691], [ 685, 687, 695, 693, 686, 688, 696, 694], [ 690, 689, 697, 698, 692, 691, 699, 700], [ 689, 693, 701, 697, 691, 694, 702, 699], [ 693, 695, 703, 701, 694, 696, 704, 702], [ 698, 697, 705, 706, 700, 699, 707, 708], [ 697, 701, 709, 705, 699, 702, 710, 707], [ 701, 703, 711, 709, 702, 704, 712, 710], [ 711, 713, 714, 709, 712, 715, 716, 710], [ 713, 717, 718, 714, 715, 719, 720, 716], [ 717, 149, 585, 718, 719, 151, 587, 720], [ 709, 714, 721, 705, 710, 716, 722, 707], [ 714, 718, 723, 721, 716, 720, 724, 722], [ 718, 585, 581, 723, 720, 587, 583, 724], [ 705, 721, 725, 706, 707, 722, 726, 708], [ 721, 723, 727, 725, 722, 724, 728, 726], [ 723, 581, 579, 727, 724, 583, 580, 728], [ 579, 571, 729, 727, 580, 572, 730, 728], [ 571, 563, 731, 729, 572, 564, 732, 730], [ 563, 555, 677, 731, 564, 556, 678, 732], [ 727, 729, 733, 725, 728, 730, 734, 726], [ 729, 731, 735, 733, 730, 732, 736, 734], [ 731, 677, 679, 735, 732, 678, 680, 736], [ 725, 733, 698, 706, 726, 734, 700, 708], [ 733, 735, 690, 698, 734, 736, 692, 700], [ 735, 679, 681, 690, 736, 680, 682, 692], [ 625, 737, 738, 626, 627, 739, 740, 628], [ 737, 741, 742, 738, 739, 743, 744, 740], [ 741, 745, 746, 742, 743, 747, 748, 744], [ 745, 749, 750, 746, 747, 751, 752, 748], [ 749, 753, 754, 750, 751, 755, 756, 752], [ 753, 1, 4, 754, 755, 5, 8, 756], [ 626, 738, 757, 639, 628, 740, 758, 640], [ 738, 742, 759, 757, 740, 744, 760, 758], [ 742, 746, 761, 759, 744, 748, 762, 760], [ 746, 750, 763, 761, 748, 752, 764, 762], [ 750, 754, 765, 763, 752, 756, 766, 764], [ 754, 4, 34, 765, 756, 8, 36, 766], [ 639, 757, 767, 651, 640, 758, 768, 652], [ 757, 759, 769, 767, 758, 760, 770, 768], [ 759, 761, 771, 769, 760, 762, 772, 770], [ 761, 763, 773, 771, 762, 764, 774, 772], [ 763, 765, 775, 773, 764, 766, 776, 774], [ 765, 34, 50, 775, 766, 36, 52, 776], [ 651, 767, 777, 663, 652, 768, 778, 664], [ 767, 769, 779, 777, 768, 770, 780, 778], [ 769, 771, 781, 779, 770, 772, 782, 780], [ 771, 773, 783, 781, 772, 774, 784, 782], [ 773, 775, 785, 783, 774, 776, 786, 784], [ 775, 50, 66, 785, 776, 52, 68, 786], [ 663, 777, 787, 675, 664, 778, 788, 676], [ 777, 779, 789, 787, 778, 780, 790, 788], [ 779, 781, 791, 789, 780, 782, 792, 790], [ 781, 783, 793, 791, 782, 784, 794, 792], [ 783, 785, 795, 793, 784, 786, 796, 794], [ 785, 66, 82, 795, 786, 68, 84, 796], [ 675, 787, 797, 687, 676, 788, 798, 688], [ 787, 789, 799, 797, 788, 790, 800, 798], [ 789, 791, 801, 799, 790, 792, 802, 800], [ 791, 793, 803, 801, 792, 794, 804, 802], [ 793, 795, 805, 803, 794, 796, 806, 804], [ 795, 82, 98, 805, 796, 84, 100, 806], [ 801, 803, 807, 808, 802, 804, 809, 810], [ 803, 805, 811, 807, 804, 806, 812, 809], [ 805, 98, 173, 811, 806, 100, 175, 812], [ 808, 807, 813, 814, 810, 809, 815, 816], [ 807, 811, 817, 813, 809, 812, 818, 815], [ 811, 173, 169, 817, 812, 175, 171, 818], [ 814, 813, 819, 820, 816, 815, 821, 822], [ 813, 817, 823, 819, 815, 818, 824, 821], [ 817, 169, 167, 823, 818, 171, 168, 824], [ 167, 159, 825, 823, 168, 160, 826, 824], [ 159, 150, 827, 825, 160, 152, 828, 826], [ 150, 149, 717, 827, 152, 151, 719, 828], [ 823, 825, 829, 819, 824, 826, 830, 821], [ 825, 827, 831, 829, 826, 828, 832, 830], [ 827, 717, 713, 831, 828, 719, 715, 832], [ 819, 829, 833, 820, 821, 830, 834, 822], [ 829, 831, 835, 833, 830, 832, 836, 834], [ 831, 713, 711, 835, 832, 715, 712, 836], [ 711, 703, 837, 835, 712, 704, 838, 836], [ 703, 695, 839, 837, 704, 696, 840, 838], [ 695, 687, 797, 839, 696, 688, 798, 840], [ 835, 837, 841, 833, 836, 838, 842, 834], [ 837, 839, 843, 841, 838, 840, 844, 842], [ 839, 797, 799, 843, 840, 798, 800, 844], [ 833, 841, 814, 820, 834, 842, 816, 822], [ 841, 843, 808, 814, 842, 844, 810, 816], [ 843, 799, 801, 808, 844, 800, 802, 810], [ 845, 846, 847, 848, 1, 2, 3, 4], [ 846, 849, 850, 847, 2, 9, 10, 3], [ 849, 851, 852, 850, 9, 13, 14, 10], [ 851, 853, 854, 852, 13, 17, 18, 14], [ 853, 855, 856, 854, 17, 21, 22, 18], [ 855, 857, 858, 856, 21, 25, 26, 22], [ 857, 859, 860, 858, 25, 29, 30, 26], [ 848, 847, 861, 862, 4, 3, 33, 34], [ 847, 850, 863, 861, 3, 10, 37, 33], [ 850, 852, 864, 863, 10, 14, 39, 37], [ 852, 854, 865, 864, 14, 18, 41, 39], [ 854, 856, 866, 865, 18, 22, 43, 41], [ 856, 858, 867, 866, 22, 26, 45, 43], [ 858, 860, 868, 867, 26, 30, 47, 45], [ 862, 861, 869, 870, 34, 33, 49, 50], [ 861, 863, 871, 869, 33, 37, 53, 49], [ 863, 864, 872, 871, 37, 39, 55, 53], [ 864, 865, 873, 872, 39, 41, 57, 55], [ 865, 866, 874, 873, 41, 43, 59, 57], [ 866, 867, 875, 874, 43, 45, 61, 59], [ 867, 868, 876, 875, 45, 47, 63, 61], [ 870, 869, 877, 878, 50, 49, 65, 66], [ 869, 871, 879, 877, 49, 53, 69, 65], [ 871, 872, 880, 879, 53, 55, 71, 69], [ 872, 873, 881, 880, 55, 57, 73, 71], [ 873, 874, 882, 881, 57, 59, 75, 73], [ 874, 875, 883, 882, 59, 61, 77, 75], [ 875, 876, 884, 883, 61, 63, 79, 77], [ 878, 877, 885, 886, 66, 65, 81, 82], [ 877, 879, 887, 885, 65, 69, 85, 81], [ 879, 880, 888, 887, 69, 71, 87, 85], [ 880, 881, 889, 888, 71, 73, 89, 87], [ 881, 882, 890, 889, 73, 75, 91, 89], [ 882, 883, 891, 890, 75, 77, 93, 91], [ 883, 884, 892, 891, 77, 79, 95, 93], [ 886, 885, 893, 894, 82, 81, 97, 98], [ 885, 887, 895, 893, 81, 85, 101, 97], [ 887, 888, 896, 895, 85, 87, 103, 101], [ 888, 889, 897, 896, 87, 89, 105, 103], [ 889, 890, 898, 897, 89, 91, 107, 105], [ 890, 891, 899, 898, 91, 93, 109, 107], [ 891, 892, 900, 899, 93, 95, 111, 109], [ 897, 898, 901, 902, 105, 107, 113, 114], [ 898, 899, 903, 901, 107, 109, 117, 113], [ 899, 900, 904, 903, 109, 111, 119, 117], [ 902, 901, 905, 906, 114, 113, 121, 122], [ 901, 903, 907, 905, 113, 117, 125, 121], [ 903, 904, 908, 907, 117, 119, 127, 125], [ 906, 905, 909, 910, 122, 121, 129, 130], [ 905, 907, 911, 909, 121, 125, 133, 129], [ 907, 908, 912, 911, 125, 127, 135, 133], [ 912, 913, 914, 911, 135, 137, 138, 133], [ 913, 915, 916, 914, 137, 141, 142, 138], [ 915, 917, 918, 916, 141, 145, 146, 142], [ 917, 919, 920, 918, 145, 149, 150, 146], [ 911, 914, 921, 909, 133, 138, 153, 129], [ 914, 916, 922, 921, 138, 142, 155, 153], [ 916, 918, 923, 922, 142, 146, 157, 155], [ 918, 920, 924, 923, 146, 150, 159, 157], [ 909, 921, 925, 910, 129, 153, 161, 130], [ 921, 922, 926, 925, 153, 155, 163, 161], [ 922, 923, 927, 926, 155, 157, 165, 163], [ 923, 924, 928, 927, 157, 159, 167, 165], [ 928, 929, 930, 927, 167, 169, 170, 165], [ 929, 931, 932, 930, 169, 173, 174, 170], [ 931, 894, 893, 932, 173, 98, 97, 174], [ 927, 930, 933, 926, 165, 170, 177, 163], [ 930, 932, 934, 933, 170, 174, 179, 177], [ 932, 893, 895, 934, 174, 97, 101, 179], [ 926, 933, 935, 925, 163, 177, 181, 161], [ 933, 934, 936, 935, 177, 179, 183, 181], [ 934, 895, 896, 936, 179, 101, 103, 183], [ 925, 935, 906, 910, 161, 181, 122, 130], [ 935, 936, 902, 906, 181, 183, 114, 122], [ 936, 896, 897, 902, 183, 103, 105, 114], [ 859, 937, 938, 860, 29, 185, 186, 30], [ 937, 939, 940, 938, 185, 189, 190, 186], [ 939, 941, 942, 940, 189, 193, 194, 190], [ 941, 943, 944, 942, 193, 197, 198, 194], [ 943, 945, 946, 944, 197, 201, 202, 198], [ 945, 947, 948, 946, 201, 205, 206, 202], [ 947, 949, 950, 948, 205, 209, 210, 206], [ 860, 938, 951, 868, 30, 186, 213, 47], [ 938, 940, 952, 951, 186, 190, 215, 213], [ 940, 942, 953, 952, 190, 194, 217, 215], [ 942, 944, 954, 953, 194, 198, 219, 217], [ 944, 946, 955, 954, 198, 202, 221, 219], [ 946, 948, 956, 955, 202, 206, 223, 221], [ 948, 950, 957, 956, 206, 210, 225, 223], [ 868, 951, 958, 876, 47, 213, 227, 63], [ 951, 952, 959, 958, 213, 215, 229, 227], [ 952, 953, 960, 959, 215, 217, 231, 229], [ 953, 954, 961, 960, 217, 219, 233, 231], [ 954, 955, 962, 961, 219, 221, 235, 233], [ 955, 956, 963, 962, 221, 223, 237, 235], [ 956, 957, 964, 963, 223, 225, 239, 237], [ 876, 958, 965, 884, 63, 227, 241, 79], [ 958, 959, 966, 965, 227, 229, 243, 241], [ 959, 960, 967, 966, 229, 231, 245, 243], [ 960, 961, 968, 967, 231, 233, 247, 245], [ 961, 962, 969, 968, 233, 235, 249, 247], [ 962, 963, 970, 969, 235, 237, 251, 249], [ 963, 964, 971, 970, 237, 239, 253, 251], [ 884, 965, 972, 892, 79, 241, 255, 95], [ 965, 966, 973, 972, 241, 243, 257, 255], [ 966, 967, 974, 973, 243, 245, 259, 257], [ 967, 968, 975, 974, 245, 247, 261, 259], [ 968, 969, 976, 975, 247, 249, 263, 261], [ 969, 970, 977, 976, 249, 251, 265, 263], [ 970, 971, 978, 977, 251, 253, 267, 265], [ 892, 972, 979, 900, 95, 255, 269, 111], [ 972, 973, 980, 979, 255, 257, 271, 269], [ 973, 974, 981, 980, 257, 259, 273, 271], [ 974, 975, 982, 981, 259, 261, 275, 273], [ 975, 976, 983, 982, 261, 263, 277, 275], [ 976, 977, 984, 983, 263, 265, 279, 277], [ 977, 978, 985, 984, 265, 267, 281, 279], [ 981, 982, 986, 987, 273, 275, 283, 284], [ 982, 983, 988, 986, 275, 277, 287, 283], [ 983, 984, 989, 988, 277, 279, 289, 287], [ 984, 985, 990, 989, 279, 281, 291, 289], [ 987, 986, 991, 992, 284, 283, 293, 294], [ 986, 988, 993, 991, 283, 287, 297, 293], [ 988, 989, 994, 993, 287, 289, 299, 297], [ 989, 990, 995, 994, 289, 291, 301, 299], [ 992, 991, 996, 997, 294, 293, 303, 304], [ 991, 993, 998, 996, 293, 297, 307, 303], [ 993, 994, 999, 998, 297, 299, 309, 307], [ 994, 995, 1000, 999, 299, 301, 311, 309], [1000, 1001, 1002, 999, 311, 313, 314, 309], [1001, 1003, 1004, 1002, 313, 317, 318, 314], [1003, 919, 917, 1004, 317, 149, 145, 318], [ 999, 1002, 1005, 998, 309, 314, 321, 307], [1002, 1004, 1006, 1005, 314, 318, 323, 321], [1004, 917, 915, 1006, 318, 145, 141, 323], [ 998, 1005, 1007, 996, 307, 321, 325, 303], [1005, 1006, 1008, 1007, 321, 323, 327, 325], [1006, 915, 913, 1008, 323, 141, 137, 327], [ 996, 1007, 1009, 997, 303, 325, 329, 304], [1007, 1008, 1010, 1009, 325, 327, 331, 329], [1008, 913, 912, 1010, 327, 137, 135, 331], [ 912, 908, 1011, 1010, 135, 127, 333, 331], [ 908, 904, 1012, 1011, 127, 119, 335, 333], [ 904, 900, 979, 1012, 119, 111, 269, 335], [1010, 1011, 1013, 1009, 331, 333, 337, 329], [1011, 1012, 1014, 1013, 333, 335, 339, 337], [1012, 979, 980, 1014, 335, 269, 271, 339], [1009, 1013, 992, 997, 329, 337, 294, 304], [1013, 1014, 987, 992, 337, 339, 284, 294], [1014, 980, 981, 987, 339, 271, 273, 284], [ 949, 1015, 1016, 950, 209, 341, 342, 210], [1015, 1017, 1018, 1016, 341, 345, 346, 342], [1017, 1019, 1020, 1018, 345, 349, 350, 346], [1019, 1021, 1022, 1020, 349, 353, 354, 350], [1021, 1023, 1024, 1022, 353, 357, 358, 354], [1023, 1025, 1026, 1024, 357, 361, 362, 358], [ 950, 1016, 1027, 957, 210, 342, 365, 225], [1016, 1018, 1028, 1027, 342, 346, 367, 365], [1018, 1020, 1029, 1028, 346, 350, 369, 367], [1020, 1022, 1030, 1029, 350, 354, 371, 369], [1022, 1024, 1031, 1030, 354, 358, 373, 371], [1024, 1026, 1032, 1031, 358, 362, 375, 373], [ 957, 1027, 1033, 964, 225, 365, 377, 239], [1027, 1028, 1034, 1033, 365, 367, 379, 377], [1028, 1029, 1035, 1034, 367, 369, 381, 379], [1029, 1030, 1036, 1035, 369, 371, 383, 381], [1030, 1031, 1037, 1036, 371, 373, 385, 383], [1031, 1032, 1038, 1037, 373, 375, 387, 385], [ 964, 1033, 1039, 971, 239, 377, 389, 253], [1033, 1034, 1040, 1039, 377, 379, 391, 389], [1034, 1035, 1041, 1040, 379, 381, 393, 391], [1035, 1036, 1042, 1041, 381, 383, 395, 393], [1036, 1037, 1043, 1042, 383, 385, 397, 395], [1037, 1038, 1044, 1043, 385, 387, 399, 397], [ 971, 1039, 1045, 978, 253, 389, 401, 267], [1039, 1040, 1046, 1045, 389, 391, 403, 401], [1040, 1041, 1047, 1046, 391, 393, 405, 403], [1041, 1042, 1048, 1047, 393, 395, 407, 405], [1042, 1043, 1049, 1048, 395, 397, 409, 407], [1043, 1044, 1050, 1049, 397, 399, 411, 409], [ 978, 1045, 1051, 985, 267, 401, 413, 281], [1045, 1046, 1052, 1051, 401, 403, 415, 413], [1046, 1047, 1053, 1052, 403, 405, 417, 415], [1047, 1048, 1054, 1053, 405, 407, 419, 417], [1048, 1049, 1055, 1054, 407, 409, 421, 419], [1049, 1050, 1056, 1055, 409, 411, 423, 421], [1053, 1054, 1057, 1058, 417, 419, 425, 426], [1054, 1055, 1059, 1057, 419, 421, 429, 425], [1055, 1056, 1060, 1059, 421, 423, 431, 429], [1058, 1057, 1061, 1062, 426, 425, 433, 434], [1057, 1059, 1063, 1061, 425, 429, 437, 433], [1059, 1060, 1064, 1063, 429, 431, 439, 437], [1062, 1061, 1065, 1066, 434, 433, 441, 442], [1061, 1063, 1067, 1065, 433, 437, 445, 441], [1063, 1064, 1068, 1067, 437, 439, 447, 445], [1068, 1069, 1070, 1067, 447, 449, 450, 445], [1069, 1071, 1072, 1070, 449, 453, 454, 450], [1071, 919, 1003, 1072, 453, 149, 317, 454], [1067, 1070, 1073, 1065, 445, 450, 457, 441], [1070, 1072, 1074, 1073, 450, 454, 459, 457], [1072, 1003, 1001, 1074, 454, 317, 313, 459], [1065, 1073, 1075, 1066, 441, 457, 461, 442], [1073, 1074, 1076, 1075, 457, 459, 463, 461], [1074, 1001, 1000, 1076, 459, 313, 311, 463], [1000, 995, 1077, 1076, 311, 301, 465, 463], [ 995, 990, 1078, 1077, 301, 291, 467, 465], [ 990, 985, 1051, 1078, 291, 281, 413, 467], [1076, 1077, 1079, 1075, 463, 465, 469, 461], [1077, 1078, 1080, 1079, 465, 467, 471, 469], [1078, 1051, 1052, 1080, 467, 413, 415, 471], [1075, 1079, 1062, 1066, 461, 469, 434, 442], [1079, 1080, 1058, 1062, 469, 471, 426, 434], [1080, 1052, 1053, 1058, 471, 415, 417, 426], [1025, 1081, 1082, 1026, 361, 473, 474, 362], [1081, 1083, 1084, 1082, 473, 477, 478, 474], [1083, 1085, 1086, 1084, 477, 481, 482, 478], [1085, 1087, 1088, 1086, 481, 485, 486, 482], [1087, 1089, 1090, 1088, 485, 489, 490, 486], [1089, 1091, 1092, 1090, 489, 493, 494, 490], [1026, 1082, 1093, 1032, 362, 474, 497, 375], [1082, 1084, 1094, 1093, 474, 478, 499, 497], [1084, 1086, 1095, 1094, 478, 482, 501, 499], [1086, 1088, 1096, 1095, 482, 486, 503, 501], [1088, 1090, 1097, 1096, 486, 490, 505, 503], [1090, 1092, 1098, 1097, 490, 494, 507, 505], [1032, 1093, 1099, 1038, 375, 497, 509, 387], [1093, 1094, 1100, 1099, 497, 499, 511, 509], [1094, 1095, 1101, 1100, 499, 501, 513, 511], [1095, 1096, 1102, 1101, 501, 503, 515, 513], [1096, 1097, 1103, 1102, 503, 505, 517, 515], [1097, 1098, 1104, 1103, 505, 507, 519, 517], [1038, 1099, 1105, 1044, 387, 509, 521, 399], [1099, 1100, 1106, 1105, 509, 511, 523, 521], [1100, 1101, 1107, 1106, 511, 513, 525, 523], [1101, 1102, 1108, 1107, 513, 515, 527, 525], [1102, 1103, 1109, 1108, 515, 517, 529, 527], [1103, 1104, 1110, 1109, 517, 519, 531, 529], [1044, 1105, 1111, 1050, 399, 521, 533, 411], [1105, 1106, 1112, 1111, 521, 523, 535, 533], [1106, 1107, 1113, 1112, 523, 525, 537, 535], [1107, 1108, 1114, 1113, 525, 527, 539, 537], [1108, 1109, 1115, 1114, 527, 529, 541, 539], [1109, 1110, 1116, 1115, 529, 531, 543, 541], [1050, 1111, 1117, 1056, 411, 533, 545, 423], [1111, 1112, 1118, 1117, 533, 535, 547, 545], [1112, 1113, 1119, 1118, 535, 537, 549, 547], [1113, 1114, 1120, 1119, 537, 539, 551, 549], [1114, 1115, 1121, 1120, 539, 541, 553, 551], [1115, 1116, 1122, 1121, 541, 543, 555, 553], [1119, 1120, 1123, 1124, 549, 551, 557, 558], [1120, 1121, 1125, 1123, 551, 553, 561, 557], [1121, 1122, 1126, 1125, 553, 555, 563, 561], [1124, 1123, 1127, 1128, 558, 557, 565, 566], [1123, 1125, 1129, 1127, 557, 561, 569, 565], [1125, 1126, 1130, 1129, 561, 563, 571, 569], [1128, 1127, 1131, 1132, 566, 565, 573, 574], [1127, 1129, 1133, 1131, 565, 569, 577, 573], [1129, 1130, 1134, 1133, 569, 571, 579, 577], [1134, 1135, 1136, 1133, 579, 581, 582, 577], [1135, 1137, 1138, 1136, 581, 585, 586, 582], [1137, 919, 1071, 1138, 585, 149, 453, 586], [1133, 1136, 1139, 1131, 577, 582, 589, 573], [1136, 1138, 1140, 1139, 582, 586, 591, 589], [1138, 1071, 1069, 1140, 586, 453, 449, 591], [1131, 1139, 1141, 1132, 573, 589, 593, 574], [1139, 1140, 1142, 1141, 589, 591, 595, 593], [1140, 1069, 1068, 1142, 591, 449, 447, 595], [1068, 1064, 1143, 1142, 447, 439, 597, 595], [1064, 1060, 1144, 1143, 439, 431, 599, 597], [1060, 1056, 1117, 1144, 431, 423, 545, 599], [1142, 1143, 1145, 1141, 595, 597, 601, 593], [1143, 1144, 1146, 1145, 597, 599, 603, 601], [1144, 1117, 1118, 1146, 599, 545, 547, 603], [1141, 1145, 1128, 1132, 593, 601, 566, 574], [1145, 1146, 1124, 1128, 601, 603, 558, 566], [1146, 1118, 1119, 1124, 603, 547, 549, 558], [1091, 1147, 1148, 1092, 493, 605, 606, 494], [1147, 1149, 1150, 1148, 605, 609, 610, 606], [1149, 1151, 1152, 1150, 609, 613, 614, 610], [1151, 1153, 1154, 1152, 613, 617, 618, 614], [1153, 1155, 1156, 1154, 617, 621, 622, 618], [1155, 1157, 1158, 1156, 621, 625, 626, 622], [1092, 1148, 1159, 1098, 494, 606, 629, 507], [1148, 1150, 1160, 1159, 606, 610, 631, 629], [1150, 1152, 1161, 1160, 610, 614, 633, 631], [1152, 1154, 1162, 1161, 614, 618, 635, 633], [1154, 1156, 1163, 1162, 618, 622, 637, 635], [1156, 1158, 1164, 1163, 622, 626, 639, 637], [1098, 1159, 1165, 1104, 507, 629, 641, 519], [1159, 1160, 1166, 1165, 629, 631, 643, 641], [1160, 1161, 1167, 1166, 631, 633, 645, 643], [1161, 1162, 1168, 1167, 633, 635, 647, 645], [1162, 1163, 1169, 1168, 635, 637, 649, 647], [1163, 1164, 1170, 1169, 637, 639, 651, 649], [1104, 1165, 1171, 1110, 519, 641, 653, 531], [1165, 1166, 1172, 1171, 641, 643, 655, 653], [1166, 1167, 1173, 1172, 643, 645, 657, 655], [1167, 1168, 1174, 1173, 645, 647, 659, 657], [1168, 1169, 1175, 1174, 647, 649, 661, 659], [1169, 1170, 1176, 1175, 649, 651, 663, 661], [1110, 1171, 1177, 1116, 531, 653, 665, 543], [1171, 1172, 1178, 1177, 653, 655, 667, 665], [1172, 1173, 1179, 1178, 655, 657, 669, 667], [1173, 1174, 1180, 1179, 657, 659, 671, 669], [1174, 1175, 1181, 1180, 659, 661, 673, 671], [1175, 1176, 1182, 1181, 661, 663, 675, 673], [1116, 1177, 1183, 1122, 543, 665, 677, 555], [1177, 1178, 1184, 1183, 665, 667, 679, 677], [1178, 1179, 1185, 1184, 667, 669, 681, 679], [1179, 1180, 1186, 1185, 669, 671, 683, 681], [1180, 1181, 1187, 1186, 671, 673, 685, 683], [1181, 1182, 1188, 1187, 673, 675, 687, 685], [1185, 1186, 1189, 1190, 681, 683, 689, 690], [1186, 1187, 1191, 1189, 683, 685, 693, 689], [1187, 1188, 1192, 1191, 685, 687, 695, 693], [1190, 1189, 1193, 1194, 690, 689, 697, 698], [1189, 1191, 1195, 1193, 689, 693, 701, 697], [1191, 1192, 1196, 1195, 693, 695, 703, 701], [1194, 1193, 1197, 1198, 698, 697, 705, 706], [1193, 1195, 1199, 1197, 697, 701, 709, 705], [1195, 1196, 1200, 1199, 701, 703, 711, 709], [1200, 1201, 1202, 1199, 711, 713, 714, 709], [1201, 1203, 1204, 1202, 713, 717, 718, 714], [1203, 919, 1137, 1204, 717, 149, 585, 718], [1199, 1202, 1205, 1197, 709, 714, 721, 705], [1202, 1204, 1206, 1205, 714, 718, 723, 721], [1204, 1137, 1135, 1206, 718, 585, 581, 723], [1197, 1205, 1207, 1198, 705, 721, 725, 706], [1205, 1206, 1208, 1207, 721, 723, 727, 725], [1206, 1135, 1134, 1208, 723, 581, 579, 727], [1134, 1130, 1209, 1208, 579, 571, 729, 727], [1130, 1126, 1210, 1209, 571, 563, 731, 729], [1126, 1122, 1183, 1210, 563, 555, 677, 731], [1208, 1209, 1211, 1207, 727, 729, 733, 725], [1209, 1210, 1212, 1211, 729, 731, 735, 733], [1210, 1183, 1184, 1212, 731, 677, 679, 735], [1207, 1211, 1194, 1198, 725, 733, 698, 706], [1211, 1212, 1190, 1194, 733, 735, 690, 698], [1212, 1184, 1185, 1190, 735, 679, 681, 690], [1157, 1213, 1214, 1158, 625, 737, 738, 626], [1213, 1215, 1216, 1214, 737, 741, 742, 738], [1215, 1217, 1218, 1216, 741, 745, 746, 742], [1217, 1219, 1220, 1218, 745, 749, 750, 746], [1219, 1221, 1222, 1220, 749, 753, 754, 750], [1221, 845, 848, 1222, 753, 1, 4, 754], [1158, 1214, 1223, 1164, 626, 738, 757, 639], [1214, 1216, 1224, 1223, 738, 742, 759, 757], [1216, 1218, 1225, 1224, 742, 746, 761, 759], [1218, 1220, 1226, 1225, 746, 750, 763, 761], [1220, 1222, 1227, 1226, 750, 754, 765, 763], [1222, 848, 862, 1227, 754, 4, 34, 765], [1164, 1223, 1228, 1170, 639, 757, 767, 651], [1223, 1224, 1229, 1228, 757, 759, 769, 767], [1224, 1225, 1230, 1229, 759, 761, 771, 769], [1225, 1226, 1231, 1230, 761, 763, 773, 771], [1226, 1227, 1232, 1231, 763, 765, 775, 773], [1227, 862, 870, 1232, 765, 34, 50, 775], [1170, 1228, 1233, 1176, 651, 767, 777, 663], [1228, 1229, 1234, 1233, 767, 769, 779, 777], [1229, 1230, 1235, 1234, 769, 771, 781, 779], [1230, 1231, 1236, 1235, 771, 773, 783, 781], [1231, 1232, 1237, 1236, 773, 775, 785, 783], [1232, 870, 878, 1237, 775, 50, 66, 785], [1176, 1233, 1238, 1182, 663, 777, 787, 675], [1233, 1234, 1239, 1238, 777, 779, 789, 787], [1234, 1235, 1240, 1239, 779, 781, 791, 789], [1235, 1236, 1241, 1240, 781, 783, 793, 791], [1236, 1237, 1242, 1241, 783, 785, 795, 793], [1237, 878, 886, 1242, 785, 66, 82, 795], [1182, 1238, 1243, 1188, 675, 787, 797, 687], [1238, 1239, 1244, 1243, 787, 789, 799, 797], [1239, 1240, 1245, 1244, 789, 791, 801, 799], [1240, 1241, 1246, 1245, 791, 793, 803, 801], [1241, 1242, 1247, 1246, 793, 795, 805, 803], [1242, 886, 894, 1247, 795, 82, 98, 805], [1245, 1246, 1248, 1249, 801, 803, 807, 808], [1246, 1247, 1250, 1248, 803, 805, 811, 807], [1247, 894, 931, 1250, 805, 98, 173, 811], [1249, 1248, 1251, 1252, 808, 807, 813, 814], [1248, 1250, 1253, 1251, 807, 811, 817, 813], [1250, 931, 929, 1253, 811, 173, 169, 817], [1252, 1251, 1254, 1255, 814, 813, 819, 820], [1251, 1253, 1256, 1254, 813, 817, 823, 819], [1253, 929, 928, 1256, 817, 169, 167, 823], [ 928, 924, 1257, 1256, 167, 159, 825, 823], [ 924, 920, 1258, 1257, 159, 150, 827, 825], [ 920, 919, 1203, 1258, 150, 149, 717, 827], [1256, 1257, 1259, 1254, 823, 825, 829, 819], [1257, 1258, 1260, 1259, 825, 827, 831, 829], [1258, 1203, 1201, 1260, 827, 717, 713, 831], [1254, 1259, 1261, 1255, 819, 829, 833, 820], [1259, 1260, 1262, 1261, 829, 831, 835, 833], [1260, 1201, 1200, 1262, 831, 713, 711, 835], [1200, 1196, 1263, 1262, 711, 703, 837, 835], [1196, 1192, 1264, 1263, 703, 695, 839, 837], [1192, 1188, 1243, 1264, 695, 687, 797, 839], [1262, 1263, 1265, 1261, 835, 837, 841, 833], [1263, 1264, 1266, 1265, 837, 839, 843, 841], [1264, 1243, 1244, 1266, 839, 797, 799, 843], [1261, 1265, 1252, 1255, 833, 841, 814, 820], [1265, 1266, 1249, 1252, 841, 843, 808, 814], [1266, 1244, 1245, 1249, 843, 799, 801, 808], [1267, 1268, 1269, 1270, 845, 846, 847, 848], [1268, 1271, 1272, 1269, 846, 849, 850, 847], [1271, 1273, 1274, 1272, 849, 851, 852, 850], [1273, 1275, 1276, 1274, 851, 853, 854, 852], [1275, 1277, 1278, 1276, 853, 855, 856, 854], [1277, 1279, 1280, 1278, 855, 857, 858, 856], [1279, 1281, 1282, 1280, 857, 859, 860, 858], [1270, 1269, 1283, 1284, 848, 847, 861, 862], [1269, 1272, 1285, 1283, 847, 850, 863, 861], [1272, 1274, 1286, 1285, 850, 852, 864, 863], [1274, 1276, 1287, 1286, 852, 854, 865, 864], [1276, 1278, 1288, 1287, 854, 856, 866, 865], [1278, 1280, 1289, 1288, 856, 858, 867, 866], [1280, 1282, 1290, 1289, 858, 860, 868, 867], [1284, 1283, 1291, 1292, 862, 861, 869, 870], [1283, 1285, 1293, 1291, 861, 863, 871, 869], [1285, 1286, 1294, 1293, 863, 864, 872, 871], [1286, 1287, 1295, 1294, 864, 865, 873, 872], [1287, 1288, 1296, 1295, 865, 866, 874, 873], [1288, 1289, 1297, 1296, 866, 867, 875, 874], [1289, 1290, 1298, 1297, 867, 868, 876, 875], [1292, 1291, 1299, 1300, 870, 869, 877, 878], [1291, 1293, 1301, 1299, 869, 871, 879, 877], [1293, 1294, 1302, 1301, 871, 872, 880, 879], [1294, 1295, 1303, 1302, 872, 873, 881, 880], [1295, 1296, 1304, 1303, 873, 874, 882, 881], [1296, 1297, 1305, 1304, 874, 875, 883, 882], [1297, 1298, 1306, 1305, 875, 876, 884, 883], [1300, 1299, 1307, 1308, 878, 877, 885, 886], [1299, 1301, 1309, 1307, 877, 879, 887, 885], [1301, 1302, 1310, 1309, 879, 880, 888, 887], [1302, 1303, 1311, 1310, 880, 881, 889, 888], [1303, 1304, 1312, 1311, 881, 882, 890, 889], [1304, 1305, 1313, 1312, 882, 883, 891, 890], [1305, 1306, 1314, 1313, 883, 884, 892, 891], [1308, 1307, 1315, 1316, 886, 885, 893, 894], [1307, 1309, 1317, 1315, 885, 887, 895, 893], [1309, 1310, 1318, 1317, 887, 888, 896, 895], [1310, 1311, 1319, 1318, 888, 889, 897, 896], [1311, 1312, 1320, 1319, 889, 890, 898, 897], [1312, 1313, 1321, 1320, 890, 891, 899, 898], [1313, 1314, 1322, 1321, 891, 892, 900, 899], [1319, 1320, 1323, 1324, 897, 898, 901, 902], [1320, 1321, 1325, 1323, 898, 899, 903, 901], [1321, 1322, 1326, 1325, 899, 900, 904, 903], [1324, 1323, 1327, 1328, 902, 901, 905, 906], [1323, 1325, 1329, 1327, 901, 903, 907, 905], [1325, 1326, 1330, 1329, 903, 904, 908, 907], [1328, 1327, 1331, 1332, 906, 905, 909, 910], [1327, 1329, 1333, 1331, 905, 907, 911, 909], [1329, 1330, 1334, 1333, 907, 908, 912, 911], [1334, 1335, 1336, 1333, 912, 913, 914, 911], [1335, 1337, 1338, 1336, 913, 915, 916, 914], [1337, 1339, 1340, 1338, 915, 917, 918, 916], [1339, 1341, 1342, 1340, 917, 919, 920, 918], [1333, 1336, 1343, 1331, 911, 914, 921, 909], [1336, 1338, 1344, 1343, 914, 916, 922, 921], [1338, 1340, 1345, 1344, 916, 918, 923, 922], [1340, 1342, 1346, 1345, 918, 920, 924, 923], [1331, 1343, 1347, 1332, 909, 921, 925, 910], [1343, 1344, 1348, 1347, 921, 922, 926, 925], [1344, 1345, 1349, 1348, 922, 923, 927, 926], [1345, 1346, 1350, 1349, 923, 924, 928, 927], [1350, 1351, 1352, 1349, 928, 929, 930, 927], [1351, 1353, 1354, 1352, 929, 931, 932, 930], [1353, 1316, 1315, 1354, 931, 894, 893, 932], [1349, 1352, 1355, 1348, 927, 930, 933, 926], [1352, 1354, 1356, 1355, 930, 932, 934, 933], [1354, 1315, 1317, 1356, 932, 893, 895, 934], [1348, 1355, 1357, 1347, 926, 933, 935, 925], [1355, 1356, 1358, 1357, 933, 934, 936, 935], [1356, 1317, 1318, 1358, 934, 895, 896, 936], [1347, 1357, 1328, 1332, 925, 935, 906, 910], [1357, 1358, 1324, 1328, 935, 936, 902, 906], [1358, 1318, 1319, 1324, 936, 896, 897, 902], [1281, 1359, 1360, 1282, 859, 937, 938, 860], [1359, 1361, 1362, 1360, 937, 939, 940, 938], [1361, 1363, 1364, 1362, 939, 941, 942, 940], [1363, 1365, 1366, 1364, 941, 943, 944, 942], [1365, 1367, 1368, 1366, 943, 945, 946, 944], [1367, 1369, 1370, 1368, 945, 947, 948, 946], [1369, 1371, 1372, 1370, 947, 949, 950, 948], [1282, 1360, 1373, 1290, 860, 938, 951, 868], [1360, 1362, 1374, 1373, 938, 940, 952, 951], [1362, 1364, 1375, 1374, 940, 942, 953, 952], [1364, 1366, 1376, 1375, 942, 944, 954, 953], [1366, 1368, 1377, 1376, 944, 946, 955, 954], [1368, 1370, 1378, 1377, 946, 948, 956, 955], [1370, 1372, 1379, 1378, 948, 950, 957, 956], [1290, 1373, 1380, 1298, 868, 951, 958, 876], [1373, 1374, 1381, 1380, 951, 952, 959, 958], [1374, 1375, 1382, 1381, 952, 953, 960, 959], [1375, 1376, 1383, 1382, 953, 954, 961, 960], [1376, 1377, 1384, 1383, 954, 955, 962, 961], [1377, 1378, 1385, 1384, 955, 956, 963, 962], [1378, 1379, 1386, 1385, 956, 957, 964, 963], [1298, 1380, 1387, 1306, 876, 958, 965, 884], [1380, 1381, 1388, 1387, 958, 959, 966, 965], [1381, 1382, 1389, 1388, 959, 960, 967, 966], [1382, 1383, 1390, 1389, 960, 961, 968, 967], [1383, 1384, 1391, 1390, 961, 962, 969, 968], [1384, 1385, 1392, 1391, 962, 963, 970, 969], [1385, 1386, 1393, 1392, 963, 964, 971, 970], [1306, 1387, 1394, 1314, 884, 965, 972, 892], [1387, 1388, 1395, 1394, 965, 966, 973, 972], [1388, 1389, 1396, 1395, 966, 967, 974, 973], [1389, 1390, 1397, 1396, 967, 968, 975, 974], [1390, 1391, 1398, 1397, 968, 969, 976, 975], [1391, 1392, 1399, 1398, 969, 970, 977, 976], [1392, 1393, 1400, 1399, 970, 971, 978, 977], [1314, 1394, 1401, 1322, 892, 972, 979, 900], [1394, 1395, 1402, 1401, 972, 973, 980, 979], [1395, 1396, 1403, 1402, 973, 974, 981, 980], [1396, 1397, 1404, 1403, 974, 975, 982, 981], [1397, 1398, 1405, 1404, 975, 976, 983, 982], [1398, 1399, 1406, 1405, 976, 977, 984, 983], [1399, 1400, 1407, 1406, 977, 978, 985, 984], [1403, 1404, 1408, 1409, 981, 982, 986, 987], [1404, 1405, 1410, 1408, 982, 983, 988, 986], [1405, 1406, 1411, 1410, 983, 984, 989, 988], [1406, 1407, 1412, 1411, 984, 985, 990, 989], [1409, 1408, 1413, 1414, 987, 986, 991, 992], [1408, 1410, 1415, 1413, 986, 988, 993, 991], [1410, 1411, 1416, 1415, 988, 989, 994, 993], [1411, 1412, 1417, 1416, 989, 990, 995, 994], [1414, 1413, 1418, 1419, 992, 991, 996, 997], [1413, 1415, 1420, 1418, 991, 993, 998, 996], [1415, 1416, 1421, 1420, 993, 994, 999, 998], [1416, 1417, 1422, 1421, 994, 995, 1000, 999], [1422, 1423, 1424, 1421, 1000, 1001, 1002, 999], [1423, 1425, 1426, 1424, 1001, 1003, 1004, 1002], [1425, 1341, 1339, 1426, 1003, 919, 917, 1004], [1421, 1424, 1427, 1420, 999, 1002, 1005, 998], [1424, 1426, 1428, 1427, 1002, 1004, 1006, 1005], [1426, 1339, 1337, 1428, 1004, 917, 915, 1006], [1420, 1427, 1429, 1418, 998, 1005, 1007, 996], [1427, 1428, 1430, 1429, 1005, 1006, 1008, 1007], [1428, 1337, 1335, 1430, 1006, 915, 913, 1008], [1418, 1429, 1431, 1419, 996, 1007, 1009, 997], [1429, 1430, 1432, 1431, 1007, 1008, 1010, 1009], [1430, 1335, 1334, 1432, 1008, 913, 912, 1010], [1334, 1330, 1433, 1432, 912, 908, 1011, 1010], [1330, 1326, 1434, 1433, 908, 904, 1012, 1011], [1326, 1322, 1401, 1434, 904, 900, 979, 1012], [1432, 1433, 1435, 1431, 1010, 1011, 1013, 1009], [1433, 1434, 1436, 1435, 1011, 1012, 1014, 1013], [1434, 1401, 1402, 1436, 1012, 979, 980, 1014], [1431, 1435, 1414, 1419, 1009, 1013, 992, 997], [1435, 1436, 1409, 1414, 1013, 1014, 987, 992], [1436, 1402, 1403, 1409, 1014, 980, 981, 987], [1371, 1437, 1438, 1372, 949, 1015, 1016, 950], [1437, 1439, 1440, 1438, 1015, 1017, 1018, 1016], [1439, 1441, 1442, 1440, 1017, 1019, 1020, 1018], [1441, 1443, 1444, 1442, 1019, 1021, 1022, 1020], [1443, 1445, 1446, 1444, 1021, 1023, 1024, 1022], [1445, 1447, 1448, 1446, 1023, 1025, 1026, 1024], [1372, 1438, 1449, 1379, 950, 1016, 1027, 957], [1438, 1440, 1450, 1449, 1016, 1018, 1028, 1027], [1440, 1442, 1451, 1450, 1018, 1020, 1029, 1028], [1442, 1444, 1452, 1451, 1020, 1022, 1030, 1029], [1444, 1446, 1453, 1452, 1022, 1024, 1031, 1030], [1446, 1448, 1454, 1453, 1024, 1026, 1032, 1031], [1379, 1449, 1455, 1386, 957, 1027, 1033, 964], [1449, 1450, 1456, 1455, 1027, 1028, 1034, 1033], [1450, 1451, 1457, 1456, 1028, 1029, 1035, 1034], [1451, 1452, 1458, 1457, 1029, 1030, 1036, 1035], [1452, 1453, 1459, 1458, 1030, 1031, 1037, 1036], [1453, 1454, 1460, 1459, 1031, 1032, 1038, 1037], [1386, 1455, 1461, 1393, 964, 1033, 1039, 971], [1455, 1456, 1462, 1461, 1033, 1034, 1040, 1039], [1456, 1457, 1463, 1462, 1034, 1035, 1041, 1040], [1457, 1458, 1464, 1463, 1035, 1036, 1042, 1041], [1458, 1459, 1465, 1464, 1036, 1037, 1043, 1042], [1459, 1460, 1466, 1465, 1037, 1038, 1044, 1043], [1393, 1461, 1467, 1400, 971, 1039, 1045, 978], [1461, 1462, 1468, 1467, 1039, 1040, 1046, 1045], [1462, 1463, 1469, 1468, 1040, 1041, 1047, 1046], [1463, 1464, 1470, 1469, 1041, 1042, 1048, 1047], [1464, 1465, 1471, 1470, 1042, 1043, 1049, 1048], [1465, 1466, 1472, 1471, 1043, 1044, 1050, 1049], [1400, 1467, 1473, 1407, 978, 1045, 1051, 985], [1467, 1468, 1474, 1473, 1045, 1046, 1052, 1051], [1468, 1469, 1475, 1474, 1046, 1047, 1053, 1052], [1469, 1470, 1476, 1475, 1047, 1048, 1054, 1053], [1470, 1471, 1477, 1476, 1048, 1049, 1055, 1054], [1471, 1472, 1478, 1477, 1049, 1050, 1056, 1055], [1475, 1476, 1479, 1480, 1053, 1054, 1057, 1058], [1476, 1477, 1481, 1479, 1054, 1055, 1059, 1057], [1477, 1478, 1482, 1481, 1055, 1056, 1060, 1059], [1480, 1479, 1483, 1484, 1058, 1057, 1061, 1062], [1479, 1481, 1485, 1483, 1057, 1059, 1063, 1061], [1481, 1482, 1486, 1485, 1059, 1060, 1064, 1063], [1484, 1483, 1487, 1488, 1062, 1061, 1065, 1066], [1483, 1485, 1489, 1487, 1061, 1063, 1067, 1065], [1485, 1486, 1490, 1489, 1063, 1064, 1068, 1067], [1490, 1491, 1492, 1489, 1068, 1069, 1070, 1067], [1491, 1493, 1494, 1492, 1069, 1071, 1072, 1070], [1493, 1341, 1425, 1494, 1071, 919, 1003, 1072], [1489, 1492, 1495, 1487, 1067, 1070, 1073, 1065], [1492, 1494, 1496, 1495, 1070, 1072, 1074, 1073], [1494, 1425, 1423, 1496, 1072, 1003, 1001, 1074], [1487, 1495, 1497, 1488, 1065, 1073, 1075, 1066], [1495, 1496, 1498, 1497, 1073, 1074, 1076, 1075], [1496, 1423, 1422, 1498, 1074, 1001, 1000, 1076], [1422, 1417, 1499, 1498, 1000, 995, 1077, 1076], [1417, 1412, 1500, 1499, 995, 990, 1078, 1077], [1412, 1407, 1473, 1500, 990, 985, 1051, 1078], [1498, 1499, 1501, 1497, 1076, 1077, 1079, 1075], [1499, 1500, 1502, 1501, 1077, 1078, 1080, 1079], [1500, 1473, 1474, 1502, 1078, 1051, 1052, 1080], [1497, 1501, 1484, 1488, 1075, 1079, 1062, 1066], [1501, 1502, 1480, 1484, 1079, 1080, 1058, 1062], [1502, 1474, 1475, 1480, 1080, 1052, 1053, 1058], [1447, 1503, 1504, 1448, 1025, 1081, 1082, 1026], [1503, 1505, 1506, 1504, 1081, 1083, 1084, 1082], [1505, 1507, 1508, 1506, 1083, 1085, 1086, 1084], [1507, 1509, 1510, 1508, 1085, 1087, 1088, 1086], [1509, 1511, 1512, 1510, 1087, 1089, 1090, 1088], [1511, 1513, 1514, 1512, 1089, 1091, 1092, 1090], [1448, 1504, 1515, 1454, 1026, 1082, 1093, 1032], [1504, 1506, 1516, 1515, 1082, 1084, 1094, 1093], [1506, 1508, 1517, 1516, 1084, 1086, 1095, 1094], [1508, 1510, 1518, 1517, 1086, 1088, 1096, 1095], [1510, 1512, 1519, 1518, 1088, 1090, 1097, 1096], [1512, 1514, 1520, 1519, 1090, 1092, 1098, 1097], [1454, 1515, 1521, 1460, 1032, 1093, 1099, 1038], [1515, 1516, 1522, 1521, 1093, 1094, 1100, 1099], [1516, 1517, 1523, 1522, 1094, 1095, 1101, 1100], [1517, 1518, 1524, 1523, 1095, 1096, 1102, 1101], [1518, 1519, 1525, 1524, 1096, 1097, 1103, 1102], [1519, 1520, 1526, 1525, 1097, 1098, 1104, 1103], [1460, 1521, 1527, 1466, 1038, 1099, 1105, 1044], [1521, 1522, 1528, 1527, 1099, 1100, 1106, 1105], [1522, 1523, 1529, 1528, 1100, 1101, 1107, 1106], [1523, 1524, 1530, 1529, 1101, 1102, 1108, 1107], [1524, 1525, 1531, 1530, 1102, 1103, 1109, 1108], [1525, 1526, 1532, 1531, 1103, 1104, 1110, 1109], [1466, 1527, 1533, 1472, 1044, 1105, 1111, 1050], [1527, 1528, 1534, 1533, 1105, 1106, 1112, 1111], [1528, 1529, 1535, 1534, 1106, 1107, 1113, 1112], [1529, 1530, 1536, 1535, 1107, 1108, 1114, 1113], [1530, 1531, 1537, 1536, 1108, 1109, 1115, 1114], [1531, 1532, 1538, 1537, 1109, 1110, 1116, 1115], [1472, 1533, 1539, 1478, 1050, 1111, 1117, 1056], [1533, 1534, 1540, 1539, 1111, 1112, 1118, 1117], [1534, 1535, 1541, 1540, 1112, 1113, 1119, 1118], [1535, 1536, 1542, 1541, 1113, 1114, 1120, 1119], [1536, 1537, 1543, 1542, 1114, 1115, 1121, 1120], [1537, 1538, 1544, 1543, 1115, 1116, 1122, 1121], [1541, 1542, 1545, 1546, 1119, 1120, 1123, 1124], [1542, 1543, 1547, 1545, 1120, 1121, 1125, 1123], [1543, 1544, 1548, 1547, 1121, 1122, 1126, 1125], [1546, 1545, 1549, 1550, 1124, 1123, 1127, 1128], [1545, 1547, 1551, 1549, 1123, 1125, 1129, 1127], [1547, 1548, 1552, 1551, 1125, 1126, 1130, 1129], [1550, 1549, 1553, 1554, 1128, 1127, 1131, 1132], [1549, 1551, 1555, 1553, 1127, 1129, 1133, 1131], [1551, 1552, 1556, 1555, 1129, 1130, 1134, 1133], [1556, 1557, 1558, 1555, 1134, 1135, 1136, 1133], [1557, 1559, 1560, 1558, 1135, 1137, 1138, 1136], [1559, 1341, 1493, 1560, 1137, 919, 1071, 1138], [1555, 1558, 1561, 1553, 1133, 1136, 1139, 1131], [1558, 1560, 1562, 1561, 1136, 1138, 1140, 1139], [1560, 1493, 1491, 1562, 1138, 1071, 1069, 1140], [1553, 1561, 1563, 1554, 1131, 1139, 1141, 1132], [1561, 1562, 1564, 1563, 1139, 1140, 1142, 1141], [1562, 1491, 1490, 1564, 1140, 1069, 1068, 1142], [1490, 1486, 1565, 1564, 1068, 1064, 1143, 1142], [1486, 1482, 1566, 1565, 1064, 1060, 1144, 1143], [1482, 1478, 1539, 1566, 1060, 1056, 1117, 1144], [1564, 1565, 1567, 1563, 1142, 1143, 1145, 1141], [1565, 1566, 1568, 1567, 1143, 1144, 1146, 1145], [1566, 1539, 1540, 1568, 1144, 1117, 1118, 1146], [1563, 1567, 1550, 1554, 1141, 1145, 1128, 1132], [1567, 1568, 1546, 1550, 1145, 1146, 1124, 1128], [1568, 1540, 1541, 1546, 1146, 1118, 1119, 1124], [1513, 1569, 1570, 1514, 1091, 1147, 1148, 1092], [1569, 1571, 1572, 1570, 1147, 1149, 1150, 1148], [1571, 1573, 1574, 1572, 1149, 1151, 1152, 1150], [1573, 1575, 1576, 1574, 1151, 1153, 1154, 1152], [1575, 1577, 1578, 1576, 1153, 1155, 1156, 1154], [1577, 1579, 1580, 1578, 1155, 1157, 1158, 1156], [1514, 1570, 1581, 1520, 1092, 1148, 1159, 1098], [1570, 1572, 1582, 1581, 1148, 1150, 1160, 1159], [1572, 1574, 1583, 1582, 1150, 1152, 1161, 1160], [1574, 1576, 1584, 1583, 1152, 1154, 1162, 1161], [1576, 1578, 1585, 1584, 1154, 1156, 1163, 1162], [1578, 1580, 1586, 1585, 1156, 1158, 1164, 1163], [1520, 1581, 1587, 1526, 1098, 1159, 1165, 1104], [1581, 1582, 1588, 1587, 1159, 1160, 1166, 1165], [1582, 1583, 1589, 1588, 1160, 1161, 1167, 1166], [1583, 1584, 1590, 1589, 1161, 1162, 1168, 1167], [1584, 1585, 1591, 1590, 1162, 1163, 1169, 1168], [1585, 1586, 1592, 1591, 1163, 1164, 1170, 1169], [1526, 1587, 1593, 1532, 1104, 1165, 1171, 1110], [1587, 1588, 1594, 1593, 1165, 1166, 1172, 1171], [1588, 1589, 1595, 1594, 1166, 1167, 1173, 1172], [1589, 1590, 1596, 1595, 1167, 1168, 1174, 1173], [1590, 1591, 1597, 1596, 1168, 1169, 1175, 1174], [1591, 1592, 1598, 1597, 1169, 1170, 1176, 1175], [1532, 1593, 1599, 1538, 1110, 1171, 1177, 1116], [1593, 1594, 1600, 1599, 1171, 1172, 1178, 1177], [1594, 1595, 1601, 1600, 1172, 1173, 1179, 1178], [1595, 1596, 1602, 1601, 1173, 1174, 1180, 1179], [1596, 1597, 1603, 1602, 1174, 1175, 1181, 1180], [1597, 1598, 1604, 1603, 1175, 1176, 1182, 1181], [1538, 1599, 1605, 1544, 1116, 1177, 1183, 1122], [1599, 1600, 1606, 1605, 1177, 1178, 1184, 1183], [1600, 1601, 1607, 1606, 1178, 1179, 1185, 1184], [1601, 1602, 1608, 1607, 1179, 1180, 1186, 1185], [1602, 1603, 1609, 1608, 1180, 1181, 1187, 1186], [1603, 1604, 1610, 1609, 1181, 1182, 1188, 1187], [1607, 1608, 1611, 1612, 1185, 1186, 1189, 1190], [1608, 1609, 1613, 1611, 1186, 1187, 1191, 1189], [1609, 1610, 1614, 1613, 1187, 1188, 1192, 1191], [1612, 1611, 1615, 1616, 1190, 1189, 1193, 1194], [1611, 1613, 1617, 1615, 1189, 1191, 1195, 1193], [1613, 1614, 1618, 1617, 1191, 1192, 1196, 1195], [1616, 1615, 1619, 1620, 1194, 1193, 1197, 1198], [1615, 1617, 1621, 1619, 1193, 1195, 1199, 1197], [1617, 1618, 1622, 1621, 1195, 1196, 1200, 1199], [1622, 1623, 1624, 1621, 1200, 1201, 1202, 1199], [1623, 1625, 1626, 1624, 1201, 1203, 1204, 1202], [1625, 1341, 1559, 1626, 1203, 919, 1137, 1204], [1621, 1624, 1627, 1619, 1199, 1202, 1205, 1197], [1624, 1626, 1628, 1627, 1202, 1204, 1206, 1205], [1626, 1559, 1557, 1628, 1204, 1137, 1135, 1206], [1619, 1627, 1629, 1620, 1197, 1205, 1207, 1198], [1627, 1628, 1630, 1629, 1205, 1206, 1208, 1207], [1628, 1557, 1556, 1630, 1206, 1135, 1134, 1208], [1556, 1552, 1631, 1630, 1134, 1130, 1209, 1208], [1552, 1548, 1632, 1631, 1130, 1126, 1210, 1209], [1548, 1544, 1605, 1632, 1126, 1122, 1183, 1210], [1630, 1631, 1633, 1629, 1208, 1209, 1211, 1207], [1631, 1632, 1634, 1633, 1209, 1210, 1212, 1211], [1632, 1605, 1606, 1634, 1210, 1183, 1184, 1212], [1629, 1633, 1616, 1620, 1207, 1211, 1194, 1198], [1633, 1634, 1612, 1616, 1211, 1212, 1190, 1194], [1634, 1606, 1607, 1612, 1212, 1184, 1185, 1190], [1579, 1635, 1636, 1580, 1157, 1213, 1214, 1158], [1635, 1637, 1638, 1636, 1213, 1215, 1216, 1214], [1637, 1639, 1640, 1638, 1215, 1217, 1218, 1216], [1639, 1641, 1642, 1640, 1217, 1219, 1220, 1218], [1641, 1643, 1644, 1642, 1219, 1221, 1222, 1220], [1643, 1267, 1270, 1644, 1221, 845, 848, 1222], [1580, 1636, 1645, 1586, 1158, 1214, 1223, 1164], [1636, 1638, 1646, 1645, 1214, 1216, 1224, 1223], [1638, 1640, 1647, 1646, 1216, 1218, 1225, 1224], [1640, 1642, 1648, 1647, 1218, 1220, 1226, 1225], [1642, 1644, 1649, 1648, 1220, 1222, 1227, 1226], [1644, 1270, 1284, 1649, 1222, 848, 862, 1227], [1586, 1645, 1650, 1592, 1164, 1223, 1228, 1170], [1645, 1646, 1651, 1650, 1223, 1224, 1229, 1228], [1646, 1647, 1652, 1651, 1224, 1225, 1230, 1229], [1647, 1648, 1653, 1652, 1225, 1226, 1231, 1230], [1648, 1649, 1654, 1653, 1226, 1227, 1232, 1231], [1649, 1284, 1292, 1654, 1227, 862, 870, 1232], [1592, 1650, 1655, 1598, 1170, 1228, 1233, 1176], [1650, 1651, 1656, 1655, 1228, 1229, 1234, 1233], [1651, 1652, 1657, 1656, 1229, 1230, 1235, 1234], [1652, 1653, 1658, 1657, 1230, 1231, 1236, 1235], [1653, 1654, 1659, 1658, 1231, 1232, 1237, 1236], [1654, 1292, 1300, 1659, 1232, 870, 878, 1237], [1598, 1655, 1660, 1604, 1176, 1233, 1238, 1182], [1655, 1656, 1661, 1660, 1233, 1234, 1239, 1238], [1656, 1657, 1662, 1661, 1234, 1235, 1240, 1239], [1657, 1658, 1663, 1662, 1235, 1236, 1241, 1240], [1658, 1659, 1664, 1663, 1236, 1237, 1242, 1241], [1659, 1300, 1308, 1664, 1237, 878, 886, 1242], [1604, 1660, 1665, 1610, 1182, 1238, 1243, 1188], [1660, 1661, 1666, 1665, 1238, 1239, 1244, 1243], [1661, 1662, 1667, 1666, 1239, 1240, 1245, 1244], [1662, 1663, 1668, 1667, 1240, 1241, 1246, 1245], [1663, 1664, 1669, 1668, 1241, 1242, 1247, 1246], [1664, 1308, 1316, 1669, 1242, 886, 894, 1247], [1667, 1668, 1670, 1671, 1245, 1246, 1248, 1249], [1668, 1669, 1672, 1670, 1246, 1247, 1250, 1248], [1669, 1316, 1353, 1672, 1247, 894, 931, 1250], [1671, 1670, 1673, 1674, 1249, 1248, 1251, 1252], [1670, 1672, 1675, 1673, 1248, 1250, 1253, 1251], [1672, 1353, 1351, 1675, 1250, 931, 929, 1253], [1674, 1673, 1676, 1677, 1252, 1251, 1254, 1255], [1673, 1675, 1678, 1676, 1251, 1253, 1256, 1254], [1675, 1351, 1350, 1678, 1253, 929, 928, 1256], [1350, 1346, 1679, 1678, 928, 924, 1257, 1256], [1346, 1342, 1680, 1679, 924, 920, 1258, 1257], [1342, 1341, 1625, 1680, 920, 919, 1203, 1258], [1678, 1679, 1681, 1676, 1256, 1257, 1259, 1254], [1679, 1680, 1682, 1681, 1257, 1258, 1260, 1259], [1680, 1625, 1623, 1682, 1258, 1203, 1201, 1260], [1676, 1681, 1683, 1677, 1254, 1259, 1261, 1255], [1681, 1682, 1684, 1683, 1259, 1260, 1262, 1261], [1682, 1623, 1622, 1684, 1260, 1201, 1200, 1262], [1622, 1618, 1685, 1684, 1200, 1196, 1263, 1262], [1618, 1614, 1686, 1685, 1196, 1192, 1264, 1263], [1614, 1610, 1665, 1686, 1192, 1188, 1243, 1264], [1684, 1685, 1687, 1683, 1262, 1263, 1265, 1261], [1685, 1686, 1688, 1687, 1263, 1264, 1266, 1265], [1686, 1665, 1666, 1688, 1264, 1243, 1244, 1266], [1683, 1687, 1674, 1677, 1261, 1265, 1252, 1255], [1687, 1688, 1671, 1674, 1265, 1266, 1249, 1252], [1688, 1666, 1667, 1671, 1266, 1244, 1245, 1249], [1689, 1690, 1691, 1692, 1267, 1268, 1269, 1270], [1690, 1693, 1694, 1691, 1268, 1271, 1272, 1269], [1693, 1695, 1696, 1694, 1271, 1273, 1274, 1272], [1695, 1697, 1698, 1696, 1273, 1275, 1276, 1274], [1697, 1699, 1700, 1698, 1275, 1277, 1278, 1276], [1699, 1701, 1702, 1700, 1277, 1279, 1280, 1278], [1701, 1703, 1704, 1702, 1279, 1281, 1282, 1280], [1692, 1691, 1705, 1706, 1270, 1269, 1283, 1284], [1691, 1694, 1707, 1705, 1269, 1272, 1285, 1283], [1694, 1696, 1708, 1707, 1272, 1274, 1286, 1285], [1696, 1698, 1709, 1708, 1274, 1276, 1287, 1286], [1698, 1700, 1710, 1709, 1276, 1278, 1288, 1287], [1700, 1702, 1711, 1710, 1278, 1280, 1289, 1288], [1702, 1704, 1712, 1711, 1280, 1282, 1290, 1289], [1706, 1705, 1713, 1714, 1284, 1283, 1291, 1292], [1705, 1707, 1715, 1713, 1283, 1285, 1293, 1291], [1707, 1708, 1716, 1715, 1285, 1286, 1294, 1293], [1708, 1709, 1717, 1716, 1286, 1287, 1295, 1294], [1709, 1710, 1718, 1717, 1287, 1288, 1296, 1295], [1710, 1711, 1719, 1718, 1288, 1289, 1297, 1296], [1711, 1712, 1720, 1719, 1289, 1290, 1298, 1297], [1714, 1713, 1721, 1722, 1292, 1291, 1299, 1300], [1713, 1715, 1723, 1721, 1291, 1293, 1301, 1299], [1715, 1716, 1724, 1723, 1293, 1294, 1302, 1301], [1716, 1717, 1725, 1724, 1294, 1295, 1303, 1302], [1717, 1718, 1726, 1725, 1295, 1296, 1304, 1303], [1718, 1719, 1727, 1726, 1296, 1297, 1305, 1304], [1719, 1720, 1728, 1727, 1297, 1298, 1306, 1305], [1722, 1721, 1729, 1730, 1300, 1299, 1307, 1308], [1721, 1723, 1731, 1729, 1299, 1301, 1309, 1307], [1723, 1724, 1732, 1731, 1301, 1302, 1310, 1309], [1724, 1725, 1733, 1732, 1302, 1303, 1311, 1310], [1725, 1726, 1734, 1733, 1303, 1304, 1312, 1311], [1726, 1727, 1735, 1734, 1304, 1305, 1313, 1312], [1727, 1728, 1736, 1735, 1305, 1306, 1314, 1313], [1730, 1729, 1737, 1738, 1308, 1307, 1315, 1316], [1729, 1731, 1739, 1737, 1307, 1309, 1317, 1315], [1731, 1732, 1740, 1739, 1309, 1310, 1318, 1317], [1732, 1733, 1741, 1740, 1310, 1311, 1319, 1318], [1733, 1734, 1742, 1741, 1311, 1312, 1320, 1319], [1734, 1735, 1743, 1742, 1312, 1313, 1321, 1320], [1735, 1736, 1744, 1743, 1313, 1314, 1322, 1321], [1741, 1742, 1745, 1746, 1319, 1320, 1323, 1324], [1742, 1743, 1747, 1745, 1320, 1321, 1325, 1323], [1743, 1744, 1748, 1747, 1321, 1322, 1326, 1325], [1746, 1745, 1749, 1750, 1324, 1323, 1327, 1328], [1745, 1747, 1751, 1749, 1323, 1325, 1329, 1327], [1747, 1748, 1752, 1751, 1325, 1326, 1330, 1329], [1750, 1749, 1753, 1754, 1328, 1327, 1331, 1332], [1749, 1751, 1755, 1753, 1327, 1329, 1333, 1331], [1751, 1752, 1756, 1755, 1329, 1330, 1334, 1333], [1756, 1757, 1758, 1755, 1334, 1335, 1336, 1333], [1757, 1759, 1760, 1758, 1335, 1337, 1338, 1336], [1759, 1761, 1762, 1760, 1337, 1339, 1340, 1338], [1761, 1763, 1764, 1762, 1339, 1341, 1342, 1340], [1755, 1758, 1765, 1753, 1333, 1336, 1343, 1331], [1758, 1760, 1766, 1765, 1336, 1338, 1344, 1343], [1760, 1762, 1767, 1766, 1338, 1340, 1345, 1344], [1762, 1764, 1768, 1767, 1340, 1342, 1346, 1345], [1753, 1765, 1769, 1754, 1331, 1343, 1347, 1332], [1765, 1766, 1770, 1769, 1343, 1344, 1348, 1347], [1766, 1767, 1771, 1770, 1344, 1345, 1349, 1348], [1767, 1768, 1772, 1771, 1345, 1346, 1350, 1349], [1772, 1773, 1774, 1771, 1350, 1351, 1352, 1349], [1773, 1775, 1776, 1774, 1351, 1353, 1354, 1352], [1775, 1738, 1737, 1776, 1353, 1316, 1315, 1354], [1771, 1774, 1777, 1770, 1349, 1352, 1355, 1348], [1774, 1776, 1778, 1777, 1352, 1354, 1356, 1355], [1776, 1737, 1739, 1778, 1354, 1315, 1317, 1356], [1770, 1777, 1779, 1769, 1348, 1355, 1357, 1347], [1777, 1778, 1780, 1779, 1355, 1356, 1358, 1357], [1778, 1739, 1740, 1780, 1356, 1317, 1318, 1358], [1769, 1779, 1750, 1754, 1347, 1357, 1328, 1332], [1779, 1780, 1746, 1750, 1357, 1358, 1324, 1328], [1780, 1740, 1741, 1746, 1358, 1318, 1319, 1324], [1703, 1781, 1782, 1704, 1281, 1359, 1360, 1282], [1781, 1783, 1784, 1782, 1359, 1361, 1362, 1360], [1783, 1785, 1786, 1784, 1361, 1363, 1364, 1362], [1785, 1787, 1788, 1786, 1363, 1365, 1366, 1364], [1787, 1789, 1790, 1788, 1365, 1367, 1368, 1366], [1789, 1791, 1792, 1790, 1367, 1369, 1370, 1368], [1791, 1793, 1794, 1792, 1369, 1371, 1372, 1370], [1704, 1782, 1795, 1712, 1282, 1360, 1373, 1290], [1782, 1784, 1796, 1795, 1360, 1362, 1374, 1373], [1784, 1786, 1797, 1796, 1362, 1364, 1375, 1374], [1786, 1788, 1798, 1797, 1364, 1366, 1376, 1375], [1788, 1790, 1799, 1798, 1366, 1368, 1377, 1376], [1790, 1792, 1800, 1799, 1368, 1370, 1378, 1377], [1792, 1794, 1801, 1800, 1370, 1372, 1379, 1378], [1712, 1795, 1802, 1720, 1290, 1373, 1380, 1298], [1795, 1796, 1803, 1802, 1373, 1374, 1381, 1380], [1796, 1797, 1804, 1803, 1374, 1375, 1382, 1381], [1797, 1798, 1805, 1804, 1375, 1376, 1383, 1382], [1798, 1799, 1806, 1805, 1376, 1377, 1384, 1383], [1799, 1800, 1807, 1806, 1377, 1378, 1385, 1384], [1800, 1801, 1808, 1807, 1378, 1379, 1386, 1385], [1720, 1802, 1809, 1728, 1298, 1380, 1387, 1306], [1802, 1803, 1810, 1809, 1380, 1381, 1388, 1387], [1803, 1804, 1811, 1810, 1381, 1382, 1389, 1388], [1804, 1805, 1812, 1811, 1382, 1383, 1390, 1389], [1805, 1806, 1813, 1812, 1383, 1384, 1391, 1390], [1806, 1807, 1814, 1813, 1384, 1385, 1392, 1391], [1807, 1808, 1815, 1814, 1385, 1386, 1393, 1392], [1728, 1809, 1816, 1736, 1306, 1387, 1394, 1314], [1809, 1810, 1817, 1816, 1387, 1388, 1395, 1394], [1810, 1811, 1818, 1817, 1388, 1389, 1396, 1395], [1811, 1812, 1819, 1818, 1389, 1390, 1397, 1396], [1812, 1813, 1820, 1819, 1390, 1391, 1398, 1397], [1813, 1814, 1821, 1820, 1391, 1392, 1399, 1398], [1814, 1815, 1822, 1821, 1392, 1393, 1400, 1399], [1736, 1816, 1823, 1744, 1314, 1394, 1401, 1322], [1816, 1817, 1824, 1823, 1394, 1395, 1402, 1401], [1817, 1818, 1825, 1824, 1395, 1396, 1403, 1402], [1818, 1819, 1826, 1825, 1396, 1397, 1404, 1403], [1819, 1820, 1827, 1826, 1397, 1398, 1405, 1404], [1820, 1821, 1828, 1827, 1398, 1399, 1406, 1405], [1821, 1822, 1829, 1828, 1399, 1400, 1407, 1406], [1825, 1826, 1830, 1831, 1403, 1404, 1408, 1409], [1826, 1827, 1832, 1830, 1404, 1405, 1410, 1408], [1827, 1828, 1833, 1832, 1405, 1406, 1411, 1410], [1828, 1829, 1834, 1833, 1406, 1407, 1412, 1411], [1831, 1830, 1835, 1836, 1409, 1408, 1413, 1414], [1830, 1832, 1837, 1835, 1408, 1410, 1415, 1413], [1832, 1833, 1838, 1837, 1410, 1411, 1416, 1415], [1833, 1834, 1839, 1838, 1411, 1412, 1417, 1416], [1836, 1835, 1840, 1841, 1414, 1413, 1418, 1419], [1835, 1837, 1842, 1840, 1413, 1415, 1420, 1418], [1837, 1838, 1843, 1842, 1415, 1416, 1421, 1420], [1838, 1839, 1844, 1843, 1416, 1417, 1422, 1421], [1844, 1845, 1846, 1843, 1422, 1423, 1424, 1421], [1845, 1847, 1848, 1846, 1423, 1425, 1426, 1424], [1847, 1763, 1761, 1848, 1425, 1341, 1339, 1426], [1843, 1846, 1849, 1842, 1421, 1424, 1427, 1420], [1846, 1848, 1850, 1849, 1424, 1426, 1428, 1427], [1848, 1761, 1759, 1850, 1426, 1339, 1337, 1428], [1842, 1849, 1851, 1840, 1420, 1427, 1429, 1418], [1849, 1850, 1852, 1851, 1427, 1428, 1430, 1429], [1850, 1759, 1757, 1852, 1428, 1337, 1335, 1430], [1840, 1851, 1853, 1841, 1418, 1429, 1431, 1419], [1851, 1852, 1854, 1853, 1429, 1430, 1432, 1431], [1852, 1757, 1756, 1854, 1430, 1335, 1334, 1432], [1756, 1752, 1855, 1854, 1334, 1330, 1433, 1432], [1752, 1748, 1856, 1855, 1330, 1326, 1434, 1433], [1748, 1744, 1823, 1856, 1326, 1322, 1401, 1434], [1854, 1855, 1857, 1853, 1432, 1433, 1435, 1431], [1855, 1856, 1858, 1857, 1433, 1434, 1436, 1435], [1856, 1823, 1824, 1858, 1434, 1401, 1402, 1436], [1853, 1857, 1836, 1841, 1431, 1435, 1414, 1419], [1857, 1858, 1831, 1836, 1435, 1436, 1409, 1414], [1858, 1824, 1825, 1831, 1436, 1402, 1403, 1409], [1793, 1859, 1860, 1794, 1371, 1437, 1438, 1372], [1859, 1861, 1862, 1860, 1437, 1439, 1440, 1438], [1861, 1863, 1864, 1862, 1439, 1441, 1442, 1440], [1863, 1865, 1866, 1864, 1441, 1443, 1444, 1442], [1865, 1867, 1868, 1866, 1443, 1445, 1446, 1444], [1867, 1869, 1870, 1868, 1445, 1447, 1448, 1446], [1794, 1860, 1871, 1801, 1372, 1438, 1449, 1379], [1860, 1862, 1872, 1871, 1438, 1440, 1450, 1449], [1862, 1864, 1873, 1872, 1440, 1442, 1451, 1450], [1864, 1866, 1874, 1873, 1442, 1444, 1452, 1451], [1866, 1868, 1875, 1874, 1444, 1446, 1453, 1452], [1868, 1870, 1876, 1875, 1446, 1448, 1454, 1453], [1801, 1871, 1877, 1808, 1379, 1449, 1455, 1386], [1871, 1872, 1878, 1877, 1449, 1450, 1456, 1455], [1872, 1873, 1879, 1878, 1450, 1451, 1457, 1456], [1873, 1874, 1880, 1879, 1451, 1452, 1458, 1457], [1874, 1875, 1881, 1880, 1452, 1453, 1459, 1458], [1875, 1876, 1882, 1881, 1453, 1454, 1460, 1459], [1808, 1877, 1883, 1815, 1386, 1455, 1461, 1393], [1877, 1878, 1884, 1883, 1455, 1456, 1462, 1461], [1878, 1879, 1885, 1884, 1456, 1457, 1463, 1462], [1879, 1880, 1886, 1885, 1457, 1458, 1464, 1463], [1880, 1881, 1887, 1886, 1458, 1459, 1465, 1464], [1881, 1882, 1888, 1887, 1459, 1460, 1466, 1465], [1815, 1883, 1889, 1822, 1393, 1461, 1467, 1400], [1883, 1884, 1890, 1889, 1461, 1462, 1468, 1467], [1884, 1885, 1891, 1890, 1462, 1463, 1469, 1468], [1885, 1886, 1892, 1891, 1463, 1464, 1470, 1469], [1886, 1887, 1893, 1892, 1464, 1465, 1471, 1470], [1887, 1888, 1894, 1893, 1465, 1466, 1472, 1471], [1822, 1889, 1895, 1829, 1400, 1467, 1473, 1407], [1889, 1890, 1896, 1895, 1467, 1468, 1474, 1473], [1890, 1891, 1897, 1896, 1468, 1469, 1475, 1474], [1891, 1892, 1898, 1897, 1469, 1470, 1476, 1475], [1892, 1893, 1899, 1898, 1470, 1471, 1477, 1476], [1893, 1894, 1900, 1899, 1471, 1472, 1478, 1477], [1897, 1898, 1901, 1902, 1475, 1476, 1479, 1480], [1898, 1899, 1903, 1901, 1476, 1477, 1481, 1479], [1899, 1900, 1904, 1903, 1477, 1478, 1482, 1481], [1902, 1901, 1905, 1906, 1480, 1479, 1483, 1484], [1901, 1903, 1907, 1905, 1479, 1481, 1485, 1483], [1903, 1904, 1908, 1907, 1481, 1482, 1486, 1485], [1906, 1905, 1909, 1910, 1484, 1483, 1487, 1488], [1905, 1907, 1911, 1909, 1483, 1485, 1489, 1487], [1907, 1908, 1912, 1911, 1485, 1486, 1490, 1489], [1912, 1913, 1914, 1911, 1490, 1491, 1492, 1489], [1913, 1915, 1916, 1914, 1491, 1493, 1494, 1492], [1915, 1763, 1847, 1916, 1493, 1341, 1425, 1494], [1911, 1914, 1917, 1909, 1489, 1492, 1495, 1487], [1914, 1916, 1918, 1917, 1492, 1494, 1496, 1495], [1916, 1847, 1845, 1918, 1494, 1425, 1423, 1496], [1909, 1917, 1919, 1910, 1487, 1495, 1497, 1488], [1917, 1918, 1920, 1919, 1495, 1496, 1498, 1497], [1918, 1845, 1844, 1920, 1496, 1423, 1422, 1498], [1844, 1839, 1921, 1920, 1422, 1417, 1499, 1498], [1839, 1834, 1922, 1921, 1417, 1412, 1500, 1499], [1834, 1829, 1895, 1922, 1412, 1407, 1473, 1500], [1920, 1921, 1923, 1919, 1498, 1499, 1501, 1497], [1921, 1922, 1924, 1923, 1499, 1500, 1502, 1501], [1922, 1895, 1896, 1924, 1500, 1473, 1474, 1502], [1919, 1923, 1906, 1910, 1497, 1501, 1484, 1488], [1923, 1924, 1902, 1906, 1501, 1502, 1480, 1484], [1924, 1896, 1897, 1902, 1502, 1474, 1475, 1480], [1869, 1925, 1926, 1870, 1447, 1503, 1504, 1448], [1925, 1927, 1928, 1926, 1503, 1505, 1506, 1504], [1927, 1929, 1930, 1928, 1505, 1507, 1508, 1506], [1929, 1931, 1932, 1930, 1507, 1509, 1510, 1508], [1931, 1933, 1934, 1932, 1509, 1511, 1512, 1510], [1933, 1935, 1936, 1934, 1511, 1513, 1514, 1512], [1870, 1926, 1937, 1876, 1448, 1504, 1515, 1454], [1926, 1928, 1938, 1937, 1504, 1506, 1516, 1515], [1928, 1930, 1939, 1938, 1506, 1508, 1517, 1516], [1930, 1932, 1940, 1939, 1508, 1510, 1518, 1517], [1932, 1934, 1941, 1940, 1510, 1512, 1519, 1518], [1934, 1936, 1942, 1941, 1512, 1514, 1520, 1519], [1876, 1937, 1943, 1882, 1454, 1515, 1521, 1460], [1937, 1938, 1944, 1943, 1515, 1516, 1522, 1521], [1938, 1939, 1945, 1944, 1516, 1517, 1523, 1522], [1939, 1940, 1946, 1945, 1517, 1518, 1524, 1523], [1940, 1941, 1947, 1946, 1518, 1519, 1525, 1524], [1941, 1942, 1948, 1947, 1519, 1520, 1526, 1525], [1882, 1943, 1949, 1888, 1460, 1521, 1527, 1466], [1943, 1944, 1950, 1949, 1521, 1522, 1528, 1527], [1944, 1945, 1951, 1950, 1522, 1523, 1529, 1528], [1945, 1946, 1952, 1951, 1523, 1524, 1530, 1529], [1946, 1947, 1953, 1952, 1524, 1525, 1531, 1530], [1947, 1948, 1954, 1953, 1525, 1526, 1532, 1531], [1888, 1949, 1955, 1894, 1466, 1527, 1533, 1472], [1949, 1950, 1956, 1955, 1527, 1528, 1534, 1533], [1950, 1951, 1957, 1956, 1528, 1529, 1535, 1534], [1951, 1952, 1958, 1957, 1529, 1530, 1536, 1535], [1952, 1953, 1959, 1958, 1530, 1531, 1537, 1536], [1953, 1954, 1960, 1959, 1531, 1532, 1538, 1537], [1894, 1955, 1961, 1900, 1472, 1533, 1539, 1478], [1955, 1956, 1962, 1961, 1533, 1534, 1540, 1539], [1956, 1957, 1963, 1962, 1534, 1535, 1541, 1540], [1957, 1958, 1964, 1963, 1535, 1536, 1542, 1541], [1958, 1959, 1965, 1964, 1536, 1537, 1543, 1542], [1959, 1960, 1966, 1965, 1537, 1538, 1544, 1543], [1963, 1964, 1967, 1968, 1541, 1542, 1545, 1546], [1964, 1965, 1969, 1967, 1542, 1543, 1547, 1545], [1965, 1966, 1970, 1969, 1543, 1544, 1548, 1547], [1968, 1967, 1971, 1972, 1546, 1545, 1549, 1550], [1967, 1969, 1973, 1971, 1545, 1547, 1551, 1549], [1969, 1970, 1974, 1973, 1547, 1548, 1552, 1551], [1972, 1971, 1975, 1976, 1550, 1549, 1553, 1554], [1971, 1973, 1977, 1975, 1549, 1551, 1555, 1553], [1973, 1974, 1978, 1977, 1551, 1552, 1556, 1555], [1978, 1979, 1980, 1977, 1556, 1557, 1558, 1555], [1979, 1981, 1982, 1980, 1557, 1559, 1560, 1558], [1981, 1763, 1915, 1982, 1559, 1341, 1493, 1560], [1977, 1980, 1983, 1975, 1555, 1558, 1561, 1553], [1980, 1982, 1984, 1983, 1558, 1560, 1562, 1561], [1982, 1915, 1913, 1984, 1560, 1493, 1491, 1562], [1975, 1983, 1985, 1976, 1553, 1561, 1563, 1554], [1983, 1984, 1986, 1985, 1561, 1562, 1564, 1563], [1984, 1913, 1912, 1986, 1562, 1491, 1490, 1564], [1912, 1908, 1987, 1986, 1490, 1486, 1565, 1564], [1908, 1904, 1988, 1987, 1486, 1482, 1566, 1565], [1904, 1900, 1961, 1988, 1482, 1478, 1539, 1566], [1986, 1987, 1989, 1985, 1564, 1565, 1567, 1563], [1987, 1988, 1990, 1989, 1565, 1566, 1568, 1567], [1988, 1961, 1962, 1990, 1566, 1539, 1540, 1568], [1985, 1989, 1972, 1976, 1563, 1567, 1550, 1554], [1989, 1990, 1968, 1972, 1567, 1568, 1546, 1550], [1990, 1962, 1963, 1968, 1568, 1540, 1541, 1546], [1935, 1991, 1992, 1936, 1513, 1569, 1570, 1514], [1991, 1993, 1994, 1992, 1569, 1571, 1572, 1570], [1993, 1995, 1996, 1994, 1571, 1573, 1574, 1572], [1995, 1997, 1998, 1996, 1573, 1575, 1576, 1574], [1997, 1999, 2000, 1998, 1575, 1577, 1578, 1576], [1999, 2001, 2002, 2000, 1577, 1579, 1580, 1578], [1936, 1992, 2003, 1942, 1514, 1570, 1581, 1520], [1992, 1994, 2004, 2003, 1570, 1572, 1582, 1581], [1994, 1996, 2005, 2004, 1572, 1574, 1583, 1582], [1996, 1998, 2006, 2005, 1574, 1576, 1584, 1583], [1998, 2000, 2007, 2006, 1576, 1578, 1585, 1584], [2000, 2002, 2008, 2007, 1578, 1580, 1586, 1585], [1942, 2003, 2009, 1948, 1520, 1581, 1587, 1526], [2003, 2004, 2010, 2009, 1581, 1582, 1588, 1587], [2004, 2005, 2011, 2010, 1582, 1583, 1589, 1588], [2005, 2006, 2012, 2011, 1583, 1584, 1590, 1589], [2006, 2007, 2013, 2012, 1584, 1585, 1591, 1590], [2007, 2008, 2014, 2013, 1585, 1586, 1592, 1591], [1948, 2009, 2015, 1954, 1526, 1587, 1593, 1532], [2009, 2010, 2016, 2015, 1587, 1588, 1594, 1593], [2010, 2011, 2017, 2016, 1588, 1589, 1595, 1594], [2011, 2012, 2018, 2017, 1589, 1590, 1596, 1595], [2012, 2013, 2019, 2018, 1590, 1591, 1597, 1596], [2013, 2014, 2020, 2019, 1591, 1592, 1598, 1597], [1954, 2015, 2021, 1960, 1532, 1593, 1599, 1538], [2015, 2016, 2022, 2021, 1593, 1594, 1600, 1599], [2016, 2017, 2023, 2022, 1594, 1595, 1601, 1600], [2017, 2018, 2024, 2023, 1595, 1596, 1602, 1601], [2018, 2019, 2025, 2024, 1596, 1597, 1603, 1602], [2019, 2020, 2026, 2025, 1597, 1598, 1604, 1603], [1960, 2021, 2027, 1966, 1538, 1599, 1605, 1544], [2021, 2022, 2028, 2027, 1599, 1600, 1606, 1605], [2022, 2023, 2029, 2028, 1600, 1601, 1607, 1606], [2023, 2024, 2030, 2029, 1601, 1602, 1608, 1607], [2024, 2025, 2031, 2030, 1602, 1603, 1609, 1608], [2025, 2026, 2032, 2031, 1603, 1604, 1610, 1609], [2029, 2030, 2033, 2034, 1607, 1608, 1611, 1612], [2030, 2031, 2035, 2033, 1608, 1609, 1613, 1611], [2031, 2032, 2036, 2035, 1609, 1610, 1614, 1613], [2034, 2033, 2037, 2038, 1612, 1611, 1615, 1616], [2033, 2035, 2039, 2037, 1611, 1613, 1617, 1615], [2035, 2036, 2040, 2039, 1613, 1614, 1618, 1617], [2038, 2037, 2041, 2042, 1616, 1615, 1619, 1620], [2037, 2039, 2043, 2041, 1615, 1617, 1621, 1619], [2039, 2040, 2044, 2043, 1617, 1618, 1622, 1621], [2044, 2045, 2046, 2043, 1622, 1623, 1624, 1621], [2045, 2047, 2048, 2046, 1623, 1625, 1626, 1624], [2047, 1763, 1981, 2048, 1625, 1341, 1559, 1626], [2043, 2046, 2049, 2041, 1621, 1624, 1627, 1619], [2046, 2048, 2050, 2049, 1624, 1626, 1628, 1627], [2048, 1981, 1979, 2050, 1626, 1559, 1557, 1628], [2041, 2049, 2051, 2042, 1619, 1627, 1629, 1620], [2049, 2050, 2052, 2051, 1627, 1628, 1630, 1629], [2050, 1979, 1978, 2052, 1628, 1557, 1556, 1630], [1978, 1974, 2053, 2052, 1556, 1552, 1631, 1630], [1974, 1970, 2054, 2053, 1552, 1548, 1632, 1631], [1970, 1966, 2027, 2054, 1548, 1544, 1605, 1632], [2052, 2053, 2055, 2051, 1630, 1631, 1633, 1629], [2053, 2054, 2056, 2055, 1631, 1632, 1634, 1633], [2054, 2027, 2028, 2056, 1632, 1605, 1606, 1634], [2051, 2055, 2038, 2042, 1629, 1633, 1616, 1620], [2055, 2056, 2034, 2038, 1633, 1634, 1612, 1616], [2056, 2028, 2029, 2034, 1634, 1606, 1607, 1612], [2001, 2057, 2058, 2002, 1579, 1635, 1636, 1580], [2057, 2059, 2060, 2058, 1635, 1637, 1638, 1636], [2059, 2061, 2062, 2060, 1637, 1639, 1640, 1638], [2061, 2063, 2064, 2062, 1639, 1641, 1642, 1640], [2063, 2065, 2066, 2064, 1641, 1643, 1644, 1642], [2065, 1689, 1692, 2066, 1643, 1267, 1270, 1644], [2002, 2058, 2067, 2008, 1580, 1636, 1645, 1586], [2058, 2060, 2068, 2067, 1636, 1638, 1646, 1645], [2060, 2062, 2069, 2068, 1638, 1640, 1647, 1646], [2062, 2064, 2070, 2069, 1640, 1642, 1648, 1647], [2064, 2066, 2071, 2070, 1642, 1644, 1649, 1648], [2066, 1692, 1706, 2071, 1644, 1270, 1284, 1649], [2008, 2067, 2072, 2014, 1586, 1645, 1650, 1592], [2067, 2068, 2073, 2072, 1645, 1646, 1651, 1650], [2068, 2069, 2074, 2073, 1646, 1647, 1652, 1651], [2069, 2070, 2075, 2074, 1647, 1648, 1653, 1652], [2070, 2071, 2076, 2075, 1648, 1649, 1654, 1653], [2071, 1706, 1714, 2076, 1649, 1284, 1292, 1654], [2014, 2072, 2077, 2020, 1592, 1650, 1655, 1598], [2072, 2073, 2078, 2077, 1650, 1651, 1656, 1655], [2073, 2074, 2079, 2078, 1651, 1652, 1657, 1656], [2074, 2075, 2080, 2079, 1652, 1653, 1658, 1657], [2075, 2076, 2081, 2080, 1653, 1654, 1659, 1658], [2076, 1714, 1722, 2081, 1654, 1292, 1300, 1659], [2020, 2077, 2082, 2026, 1598, 1655, 1660, 1604], [2077, 2078, 2083, 2082, 1655, 1656, 1661, 1660], [2078, 2079, 2084, 2083, 1656, 1657, 1662, 1661], [2079, 2080, 2085, 2084, 1657, 1658, 1663, 1662], [2080, 2081, 2086, 2085, 1658, 1659, 1664, 1663], [2081, 1722, 1730, 2086, 1659, 1300, 1308, 1664], [2026, 2082, 2087, 2032, 1604, 1660, 1665, 1610], [2082, 2083, 2088, 2087, 1660, 1661, 1666, 1665], [2083, 2084, 2089, 2088, 1661, 1662, 1667, 1666], [2084, 2085, 2090, 2089, 1662, 1663, 1668, 1667], [2085, 2086, 2091, 2090, 1663, 1664, 1669, 1668], [2086, 1730, 1738, 2091, 1664, 1308, 1316, 1669], [2089, 2090, 2092, 2093, 1667, 1668, 1670, 1671], [2090, 2091, 2094, 2092, 1668, 1669, 1672, 1670], [2091, 1738, 1775, 2094, 1669, 1316, 1353, 1672], [2093, 2092, 2095, 2096, 1671, 1670, 1673, 1674], [2092, 2094, 2097, 2095, 1670, 1672, 1675, 1673], [2094, 1775, 1773, 2097, 1672, 1353, 1351, 1675], [2096, 2095, 2098, 2099, 1674, 1673, 1676, 1677], [2095, 2097, 2100, 2098, 1673, 1675, 1678, 1676], [2097, 1773, 1772, 2100, 1675, 1351, 1350, 1678], [1772, 1768, 2101, 2100, 1350, 1346, 1679, 1678], [1768, 1764, 2102, 2101, 1346, 1342, 1680, 1679], [1764, 1763, 2047, 2102, 1342, 1341, 1625, 1680], [2100, 2101, 2103, 2098, 1678, 1679, 1681, 1676], [2101, 2102, 2104, 2103, 1679, 1680, 1682, 1681], [2102, 2047, 2045, 2104, 1680, 1625, 1623, 1682], [2098, 2103, 2105, 2099, 1676, 1681, 1683, 1677], [2103, 2104, 2106, 2105, 1681, 1682, 1684, 1683], [2104, 2045, 2044, 2106, 1682, 1623, 1622, 1684], [2044, 2040, 2107, 2106, 1622, 1618, 1685, 1684], [2040, 2036, 2108, 2107, 1618, 1614, 1686, 1685], [2036, 2032, 2087, 2108, 1614, 1610, 1665, 1686], [2106, 2107, 2109, 2105, 1684, 1685, 1687, 1683], [2107, 2108, 2110, 2109, 1685, 1686, 1688, 1687], [2108, 2087, 2088, 2110, 1686, 1665, 1666, 1688], [2105, 2109, 2096, 2099, 1683, 1687, 1674, 1677], [2109, 2110, 2093, 2096, 1687, 1688, 1671, 1674], [2110, 2088, 2089, 2093, 1688, 1666, 1667, 1671], [2111, 2112, 2113, 2114, 1689, 1690, 1691, 1692], [2112, 2115, 2116, 2113, 1690, 1693, 1694, 1691], [2115, 2117, 2118, 2116, 1693, 1695, 1696, 1694], [2117, 2119, 2120, 2118, 1695, 1697, 1698, 1696], [2119, 2121, 2122, 2120, 1697, 1699, 1700, 1698], [2121, 2123, 2124, 2122, 1699, 1701, 1702, 1700], [2123, 2125, 2126, 2124, 1701, 1703, 1704, 1702], [2114, 2113, 2127, 2128, 1692, 1691, 1705, 1706], [2113, 2116, 2129, 2127, 1691, 1694, 1707, 1705], [2116, 2118, 2130, 2129, 1694, 1696, 1708, 1707], [2118, 2120, 2131, 2130, 1696, 1698, 1709, 1708], [2120, 2122, 2132, 2131, 1698, 1700, 1710, 1709], [2122, 2124, 2133, 2132, 1700, 1702, 1711, 1710], [2124, 2126, 2134, 2133, 1702, 1704, 1712, 1711], [2128, 2127, 2135, 2136, 1706, 1705, 1713, 1714], [2127, 2129, 2137, 2135, 1705, 1707, 1715, 1713], [2129, 2130, 2138, 2137, 1707, 1708, 1716, 1715], [2130, 2131, 2139, 2138, 1708, 1709, 1717, 1716], [2131, 2132, 2140, 2139, 1709, 1710, 1718, 1717], [2132, 2133, 2141, 2140, 1710, 1711, 1719, 1718], [2133, 2134, 2142, 2141, 1711, 1712, 1720, 1719], [2136, 2135, 2143, 2144, 1714, 1713, 1721, 1722], [2135, 2137, 2145, 2143, 1713, 1715, 1723, 1721], [2137, 2138, 2146, 2145, 1715, 1716, 1724, 1723], [2138, 2139, 2147, 2146, 1716, 1717, 1725, 1724], [2139, 2140, 2148, 2147, 1717, 1718, 1726, 1725], [2140, 2141, 2149, 2148, 1718, 1719, 1727, 1726], [2141, 2142, 2150, 2149, 1719, 1720, 1728, 1727], [2144, 2143, 2151, 2152, 1722, 1721, 1729, 1730], [2143, 2145, 2153, 2151, 1721, 1723, 1731, 1729], [2145, 2146, 2154, 2153, 1723, 1724, 1732, 1731], [2146, 2147, 2155, 2154, 1724, 1725, 1733, 1732], [2147, 2148, 2156, 2155, 1725, 1726, 1734, 1733], [2148, 2149, 2157, 2156, 1726, 1727, 1735, 1734], [2149, 2150, 2158, 2157, 1727, 1728, 1736, 1735], [2152, 2151, 2159, 2160, 1730, 1729, 1737, 1738], [2151, 2153, 2161, 2159, 1729, 1731, 1739, 1737], [2153, 2154, 2162, 2161, 1731, 1732, 1740, 1739], [2154, 2155, 2163, 2162, 1732, 1733, 1741, 1740], [2155, 2156, 2164, 2163, 1733, 1734, 1742, 1741], [2156, 2157, 2165, 2164, 1734, 1735, 1743, 1742], [2157, 2158, 2166, 2165, 1735, 1736, 1744, 1743], [2163, 2164, 2167, 2168, 1741, 1742, 1745, 1746], [2164, 2165, 2169, 2167, 1742, 1743, 1747, 1745], [2165, 2166, 2170, 2169, 1743, 1744, 1748, 1747], [2168, 2167, 2171, 2172, 1746, 1745, 1749, 1750], [2167, 2169, 2173, 2171, 1745, 1747, 1751, 1749], [2169, 2170, 2174, 2173, 1747, 1748, 1752, 1751], [2172, 2171, 2175, 2176, 1750, 1749, 1753, 1754], [2171, 2173, 2177, 2175, 1749, 1751, 1755, 1753], [2173, 2174, 2178, 2177, 1751, 1752, 1756, 1755], [2178, 2179, 2180, 2177, 1756, 1757, 1758, 1755], [2179, 2181, 2182, 2180, 1757, 1759, 1760, 1758], [2181, 2183, 2184, 2182, 1759, 1761, 1762, 1760], [2183, 2185, 2186, 2184, 1761, 1763, 1764, 1762], [2177, 2180, 2187, 2175, 1755, 1758, 1765, 1753], [2180, 2182, 2188, 2187, 1758, 1760, 1766, 1765], [2182, 2184, 2189, 2188, 1760, 1762, 1767, 1766], [2184, 2186, 2190, 2189, 1762, 1764, 1768, 1767], [2175, 2187, 2191, 2176, 1753, 1765, 1769, 1754], [2187, 2188, 2192, 2191, 1765, 1766, 1770, 1769], [2188, 2189, 2193, 2192, 1766, 1767, 1771, 1770], [2189, 2190, 2194, 2193, 1767, 1768, 1772, 1771], [2194, 2195, 2196, 2193, 1772, 1773, 1774, 1771], [2195, 2197, 2198, 2196, 1773, 1775, 1776, 1774], [2197, 2160, 2159, 2198, 1775, 1738, 1737, 1776], [2193, 2196, 2199, 2192, 1771, 1774, 1777, 1770], [2196, 2198, 2200, 2199, 1774, 1776, 1778, 1777], [2198, 2159, 2161, 2200, 1776, 1737, 1739, 1778], [2192, 2199, 2201, 2191, 1770, 1777, 1779, 1769], [2199, 2200, 2202, 2201, 1777, 1778, 1780, 1779], [2200, 2161, 2162, 2202, 1778, 1739, 1740, 1780], [2191, 2201, 2172, 2176, 1769, 1779, 1750, 1754], [2201, 2202, 2168, 2172, 1779, 1780, 1746, 1750], [2202, 2162, 2163, 2168, 1780, 1740, 1741, 1746], [2125, 2203, 2204, 2126, 1703, 1781, 1782, 1704], [2203, 2205, 2206, 2204, 1781, 1783, 1784, 1782], [2205, 2207, 2208, 2206, 1783, 1785, 1786, 1784], [2207, 2209, 2210, 2208, 1785, 1787, 1788, 1786], [2209, 2211, 2212, 2210, 1787, 1789, 1790, 1788], [2211, 2213, 2214, 2212, 1789, 1791, 1792, 1790], [2213, 2215, 2216, 2214, 1791, 1793, 1794, 1792], [2126, 2204, 2217, 2134, 1704, 1782, 1795, 1712], [2204, 2206, 2218, 2217, 1782, 1784, 1796, 1795], [2206, 2208, 2219, 2218, 1784, 1786, 1797, 1796], [2208, 2210, 2220, 2219, 1786, 1788, 1798, 1797], [2210, 2212, 2221, 2220, 1788, 1790, 1799, 1798], [2212, 2214, 2222, 2221, 1790, 1792, 1800, 1799], [2214, 2216, 2223, 2222, 1792, 1794, 1801, 1800], [2134, 2217, 2224, 2142, 1712, 1795, 1802, 1720], [2217, 2218, 2225, 2224, 1795, 1796, 1803, 1802], [2218, 2219, 2226, 2225, 1796, 1797, 1804, 1803], [2219, 2220, 2227, 2226, 1797, 1798, 1805, 1804], [2220, 2221, 2228, 2227, 1798, 1799, 1806, 1805], [2221, 2222, 2229, 2228, 1799, 1800, 1807, 1806], [2222, 2223, 2230, 2229, 1800, 1801, 1808, 1807], [2142, 2224, 2231, 2150, 1720, 1802, 1809, 1728], [2224, 2225, 2232, 2231, 1802, 1803, 1810, 1809], [2225, 2226, 2233, 2232, 1803, 1804, 1811, 1810], [2226, 2227, 2234, 2233, 1804, 1805, 1812, 1811], [2227, 2228, 2235, 2234, 1805, 1806, 1813, 1812], [2228, 2229, 2236, 2235, 1806, 1807, 1814, 1813], [2229, 2230, 2237, 2236, 1807, 1808, 1815, 1814], [2150, 2231, 2238, 2158, 1728, 1809, 1816, 1736], [2231, 2232, 2239, 2238, 1809, 1810, 1817, 1816], [2232, 2233, 2240, 2239, 1810, 1811, 1818, 1817], [2233, 2234, 2241, 2240, 1811, 1812, 1819, 1818], [2234, 2235, 2242, 2241, 1812, 1813, 1820, 1819], [2235, 2236, 2243, 2242, 1813, 1814, 1821, 1820], [2236, 2237, 2244, 2243, 1814, 1815, 1822, 1821], [2158, 2238, 2245, 2166, 1736, 1816, 1823, 1744], [2238, 2239, 2246, 2245, 1816, 1817, 1824, 1823], [2239, 2240, 2247, 2246, 1817, 1818, 1825, 1824], [2240, 2241, 2248, 2247, 1818, 1819, 1826, 1825], [2241, 2242, 2249, 2248, 1819, 1820, 1827, 1826], [2242, 2243, 2250, 2249, 1820, 1821, 1828, 1827], [2243, 2244, 2251, 2250, 1821, 1822, 1829, 1828], [2247, 2248, 2252, 2253, 1825, 1826, 1830, 1831], [2248, 2249, 2254, 2252, 1826, 1827, 1832, 1830], [2249, 2250, 2255, 2254, 1827, 1828, 1833, 1832], [2250, 2251, 2256, 2255, 1828, 1829, 1834, 1833], [2253, 2252, 2257, 2258, 1831, 1830, 1835, 1836], [2252, 2254, 2259, 2257, 1830, 1832, 1837, 1835], [2254, 2255, 2260, 2259, 1832, 1833, 1838, 1837], [2255, 2256, 2261, 2260, 1833, 1834, 1839, 1838], [2258, 2257, 2262, 2263, 1836, 1835, 1840, 1841], [2257, 2259, 2264, 2262, 1835, 1837, 1842, 1840], [2259, 2260, 2265, 2264, 1837, 1838, 1843, 1842], [2260, 2261, 2266, 2265, 1838, 1839, 1844, 1843], [2266, 2267, 2268, 2265, 1844, 1845, 1846, 1843], [2267, 2269, 2270, 2268, 1845, 1847, 1848, 1846], [2269, 2185, 2183, 2270, 1847, 1763, 1761, 1848], [2265, 2268, 2271, 2264, 1843, 1846, 1849, 1842], [2268, 2270, 2272, 2271, 1846, 1848, 1850, 1849], [2270, 2183, 2181, 2272, 1848, 1761, 1759, 1850], [2264, 2271, 2273, 2262, 1842, 1849, 1851, 1840], [2271, 2272, 2274, 2273, 1849, 1850, 1852, 1851], [2272, 2181, 2179, 2274, 1850, 1759, 1757, 1852], [2262, 2273, 2275, 2263, 1840, 1851, 1853, 1841], [2273, 2274, 2276, 2275, 1851, 1852, 1854, 1853], [2274, 2179, 2178, 2276, 1852, 1757, 1756, 1854], [2178, 2174, 2277, 2276, 1756, 1752, 1855, 1854], [2174, 2170, 2278, 2277, 1752, 1748, 1856, 1855], [2170, 2166, 2245, 2278, 1748, 1744, 1823, 1856], [2276, 2277, 2279, 2275, 1854, 1855, 1857, 1853], [2277, 2278, 2280, 2279, 1855, 1856, 1858, 1857], [2278, 2245, 2246, 2280, 1856, 1823, 1824, 1858], [2275, 2279, 2258, 2263, 1853, 1857, 1836, 1841], [2279, 2280, 2253, 2258, 1857, 1858, 1831, 1836], [2280, 2246, 2247, 2253, 1858, 1824, 1825, 1831], [2215, 2281, 2282, 2216, 1793, 1859, 1860, 1794], [2281, 2283, 2284, 2282, 1859, 1861, 1862, 1860], [2283, 2285, 2286, 2284, 1861, 1863, 1864, 1862], [2285, 2287, 2288, 2286, 1863, 1865, 1866, 1864], [2287, 2289, 2290, 2288, 1865, 1867, 1868, 1866], [2289, 2291, 2292, 2290, 1867, 1869, 1870, 1868], [2216, 2282, 2293, 2223, 1794, 1860, 1871, 1801], [2282, 2284, 2294, 2293, 1860, 1862, 1872, 1871], [2284, 2286, 2295, 2294, 1862, 1864, 1873, 1872], [2286, 2288, 2296, 2295, 1864, 1866, 1874, 1873], [2288, 2290, 2297, 2296, 1866, 1868, 1875, 1874], [2290, 2292, 2298, 2297, 1868, 1870, 1876, 1875], [2223, 2293, 2299, 2230, 1801, 1871, 1877, 1808], [2293, 2294, 2300, 2299, 1871, 1872, 1878, 1877], [2294, 2295, 2301, 2300, 1872, 1873, 1879, 1878], [2295, 2296, 2302, 2301, 1873, 1874, 1880, 1879], [2296, 2297, 2303, 2302, 1874, 1875, 1881, 1880], [2297, 2298, 2304, 2303, 1875, 1876, 1882, 1881], [2230, 2299, 2305, 2237, 1808, 1877, 1883, 1815], [2299, 2300, 2306, 2305, 1877, 1878, 1884, 1883], [2300, 2301, 2307, 2306, 1878, 1879, 1885, 1884], [2301, 2302, 2308, 2307, 1879, 1880, 1886, 1885], [2302, 2303, 2309, 2308, 1880, 1881, 1887, 1886], [2303, 2304, 2310, 2309, 1881, 1882, 1888, 1887], [2237, 2305, 2311, 2244, 1815, 1883, 1889, 1822], [2305, 2306, 2312, 2311, 1883, 1884, 1890, 1889], [2306, 2307, 2313, 2312, 1884, 1885, 1891, 1890], [2307, 2308, 2314, 2313, 1885, 1886, 1892, 1891], [2308, 2309, 2315, 2314, 1886, 1887, 1893, 1892], [2309, 2310, 2316, 2315, 1887, 1888, 1894, 1893], [2244, 2311, 2317, 2251, 1822, 1889, 1895, 1829], [2311, 2312, 2318, 2317, 1889, 1890, 1896, 1895], [2312, 2313, 2319, 2318, 1890, 1891, 1897, 1896], [2313, 2314, 2320, 2319, 1891, 1892, 1898, 1897], [2314, 2315, 2321, 2320, 1892, 1893, 1899, 1898], [2315, 2316, 2322, 2321, 1893, 1894, 1900, 1899], [2319, 2320, 2323, 2324, 1897, 1898, 1901, 1902], [2320, 2321, 2325, 2323, 1898, 1899, 1903, 1901], [2321, 2322, 2326, 2325, 1899, 1900, 1904, 1903], [2324, 2323, 2327, 2328, 1902, 1901, 1905, 1906], [2323, 2325, 2329, 2327, 1901, 1903, 1907, 1905], [2325, 2326, 2330, 2329, 1903, 1904, 1908, 1907], [2328, 2327, 2331, 2332, 1906, 1905, 1909, 1910], [2327, 2329, 2333, 2331, 1905, 1907, 1911, 1909], [2329, 2330, 2334, 2333, 1907, 1908, 1912, 1911], [2334, 2335, 2336, 2333, 1912, 1913, 1914, 1911], [2335, 2337, 2338, 2336, 1913, 1915, 1916, 1914], [2337, 2185, 2269, 2338, 1915, 1763, 1847, 1916], [2333, 2336, 2339, 2331, 1911, 1914, 1917, 1909], [2336, 2338, 2340, 2339, 1914, 1916, 1918, 1917], [2338, 2269, 2267, 2340, 1916, 1847, 1845, 1918], [2331, 2339, 2341, 2332, 1909, 1917, 1919, 1910], [2339, 2340, 2342, 2341, 1917, 1918, 1920, 1919], [2340, 2267, 2266, 2342, 1918, 1845, 1844, 1920], [2266, 2261, 2343, 2342, 1844, 1839, 1921, 1920], [2261, 2256, 2344, 2343, 1839, 1834, 1922, 1921], [2256, 2251, 2317, 2344, 1834, 1829, 1895, 1922], [2342, 2343, 2345, 2341, 1920, 1921, 1923, 1919], [2343, 2344, 2346, 2345, 1921, 1922, 1924, 1923], [2344, 2317, 2318, 2346, 1922, 1895, 1896, 1924], [2341, 2345, 2328, 2332, 1919, 1923, 1906, 1910], [2345, 2346, 2324, 2328, 1923, 1924, 1902, 1906], [2346, 2318, 2319, 2324, 1924, 1896, 1897, 1902], [2291, 2347, 2348, 2292, 1869, 1925, 1926, 1870], [2347, 2349, 2350, 2348, 1925, 1927, 1928, 1926], [2349, 2351, 2352, 2350, 1927, 1929, 1930, 1928], [2351, 2353, 2354, 2352, 1929, 1931, 1932, 1930], [2353, 2355, 2356, 2354, 1931, 1933, 1934, 1932], [2355, 2357, 2358, 2356, 1933, 1935, 1936, 1934], [2292, 2348, 2359, 2298, 1870, 1926, 1937, 1876], [2348, 2350, 2360, 2359, 1926, 1928, 1938, 1937], [2350, 2352, 2361, 2360, 1928, 1930, 1939, 1938], [2352, 2354, 2362, 2361, 1930, 1932, 1940, 1939], [2354, 2356, 2363, 2362, 1932, 1934, 1941, 1940], [2356, 2358, 2364, 2363, 1934, 1936, 1942, 1941], [2298, 2359, 2365, 2304, 1876, 1937, 1943, 1882], [2359, 2360, 2366, 2365, 1937, 1938, 1944, 1943], [2360, 2361, 2367, 2366, 1938, 1939, 1945, 1944], [2361, 2362, 2368, 2367, 1939, 1940, 1946, 1945], [2362, 2363, 2369, 2368, 1940, 1941, 1947, 1946], [2363, 2364, 2370, 2369, 1941, 1942, 1948, 1947], [2304, 2365, 2371, 2310, 1882, 1943, 1949, 1888], [2365, 2366, 2372, 2371, 1943, 1944, 1950, 1949], [2366, 2367, 2373, 2372, 1944, 1945, 1951, 1950], [2367, 2368, 2374, 2373, 1945, 1946, 1952, 1951], [2368, 2369, 2375, 2374, 1946, 1947, 1953, 1952], [2369, 2370, 2376, 2375, 1947, 1948, 1954, 1953], [2310, 2371, 2377, 2316, 1888, 1949, 1955, 1894], [2371, 2372, 2378, 2377, 1949, 1950, 1956, 1955], [2372, 2373, 2379, 2378, 1950, 1951, 1957, 1956], [2373, 2374, 2380, 2379, 1951, 1952, 1958, 1957], [2374, 2375, 2381, 2380, 1952, 1953, 1959, 1958], [2375, 2376, 2382, 2381, 1953, 1954, 1960, 1959], [2316, 2377, 2383, 2322, 1894, 1955, 1961, 1900], [2377, 2378, 2384, 2383, 1955, 1956, 1962, 1961], [2378, 2379, 2385, 2384, 1956, 1957, 1963, 1962], [2379, 2380, 2386, 2385, 1957, 1958, 1964, 1963], [2380, 2381, 2387, 2386, 1958, 1959, 1965, 1964], [2381, 2382, 2388, 2387, 1959, 1960, 1966, 1965], [2385, 2386, 2389, 2390, 1963, 1964, 1967, 1968], [2386, 2387, 2391, 2389, 1964, 1965, 1969, 1967], [2387, 2388, 2392, 2391, 1965, 1966, 1970, 1969], [2390, 2389, 2393, 2394, 1968, 1967, 1971, 1972], [2389, 2391, 2395, 2393, 1967, 1969, 1973, 1971], [2391, 2392, 2396, 2395, 1969, 1970, 1974, 1973], [2394, 2393, 2397, 2398, 1972, 1971, 1975, 1976], [2393, 2395, 2399, 2397, 1971, 1973, 1977, 1975], [2395, 2396, 2400, 2399, 1973, 1974, 1978, 1977], [2400, 2401, 2402, 2399, 1978, 1979, 1980, 1977], [2401, 2403, 2404, 2402, 1979, 1981, 1982, 1980], [2403, 2185, 2337, 2404, 1981, 1763, 1915, 1982], [2399, 2402, 2405, 2397, 1977, 1980, 1983, 1975], [2402, 2404, 2406, 2405, 1980, 1982, 1984, 1983], [2404, 2337, 2335, 2406, 1982, 1915, 1913, 1984], [2397, 2405, 2407, 2398, 1975, 1983, 1985, 1976], [2405, 2406, 2408, 2407, 1983, 1984, 1986, 1985], [2406, 2335, 2334, 2408, 1984, 1913, 1912, 1986], [2334, 2330, 2409, 2408, 1912, 1908, 1987, 1986], [2330, 2326, 2410, 2409, 1908, 1904, 1988, 1987], [2326, 2322, 2383, 2410, 1904, 1900, 1961, 1988], [2408, 2409, 2411, 2407, 1986, 1987, 1989, 1985], [2409, 2410, 2412, 2411, 1987, 1988, 1990, 1989], [2410, 2383, 2384, 2412, 1988, 1961, 1962, 1990], [2407, 2411, 2394, 2398, 1985, 1989, 1972, 1976], [2411, 2412, 2390, 2394, 1989, 1990, 1968, 1972], [2412, 2384, 2385, 2390, 1990, 1962, 1963, 1968], [2357, 2413, 2414, 2358, 1935, 1991, 1992, 1936], [2413, 2415, 2416, 2414, 1991, 1993, 1994, 1992], [2415, 2417, 2418, 2416, 1993, 1995, 1996, 1994], [2417, 2419, 2420, 2418, 1995, 1997, 1998, 1996], [2419, 2421, 2422, 2420, 1997, 1999, 2000, 1998], [2421, 2423, 2424, 2422, 1999, 2001, 2002, 2000], [2358, 2414, 2425, 2364, 1936, 1992, 2003, 1942], [2414, 2416, 2426, 2425, 1992, 1994, 2004, 2003], [2416, 2418, 2427, 2426, 1994, 1996, 2005, 2004], [2418, 2420, 2428, 2427, 1996, 1998, 2006, 2005], [2420, 2422, 2429, 2428, 1998, 2000, 2007, 2006], [2422, 2424, 2430, 2429, 2000, 2002, 2008, 2007], [2364, 2425, 2431, 2370, 1942, 2003, 2009, 1948], [2425, 2426, 2432, 2431, 2003, 2004, 2010, 2009], [2426, 2427, 2433, 2432, 2004, 2005, 2011, 2010], [2427, 2428, 2434, 2433, 2005, 2006, 2012, 2011], [2428, 2429, 2435, 2434, 2006, 2007, 2013, 2012], [2429, 2430, 2436, 2435, 2007, 2008, 2014, 2013], [2370, 2431, 2437, 2376, 1948, 2009, 2015, 1954], [2431, 2432, 2438, 2437, 2009, 2010, 2016, 2015], [2432, 2433, 2439, 2438, 2010, 2011, 2017, 2016], [2433, 2434, 2440, 2439, 2011, 2012, 2018, 2017], [2434, 2435, 2441, 2440, 2012, 2013, 2019, 2018], [2435, 2436, 2442, 2441, 2013, 2014, 2020, 2019], [2376, 2437, 2443, 2382, 1954, 2015, 2021, 1960], [2437, 2438, 2444, 2443, 2015, 2016, 2022, 2021], [2438, 2439, 2445, 2444, 2016, 2017, 2023, 2022], [2439, 2440, 2446, 2445, 2017, 2018, 2024, 2023], [2440, 2441, 2447, 2446, 2018, 2019, 2025, 2024], [2441, 2442, 2448, 2447, 2019, 2020, 2026, 2025], [2382, 2443, 2449, 2388, 1960, 2021, 2027, 1966], [2443, 2444, 2450, 2449, 2021, 2022, 2028, 2027], [2444, 2445, 2451, 2450, 2022, 2023, 2029, 2028], [2445, 2446, 2452, 2451, 2023, 2024, 2030, 2029], [2446, 2447, 2453, 2452, 2024, 2025, 2031, 2030], [2447, 2448, 2454, 2453, 2025, 2026, 2032, 2031], [2451, 2452, 2455, 2456, 2029, 2030, 2033, 2034], [2452, 2453, 2457, 2455, 2030, 2031, 2035, 2033], [2453, 2454, 2458, 2457, 2031, 2032, 2036, 2035], [2456, 2455, 2459, 2460, 2034, 2033, 2037, 2038], [2455, 2457, 2461, 2459, 2033, 2035, 2039, 2037], [2457, 2458, 2462, 2461, 2035, 2036, 2040, 2039], [2460, 2459, 2463, 2464, 2038, 2037, 2041, 2042], [2459, 2461, 2465, 2463, 2037, 2039, 2043, 2041], [2461, 2462, 2466, 2465, 2039, 2040, 2044, 2043], [2466, 2467, 2468, 2465, 2044, 2045, 2046, 2043], [2467, 2469, 2470, 2468, 2045, 2047, 2048, 2046], [2469, 2185, 2403, 2470, 2047, 1763, 1981, 2048], [2465, 2468, 2471, 2463, 2043, 2046, 2049, 2041], [2468, 2470, 2472, 2471, 2046, 2048, 2050, 2049], [2470, 2403, 2401, 2472, 2048, 1981, 1979, 2050], [2463, 2471, 2473, 2464, 2041, 2049, 2051, 2042], [2471, 2472, 2474, 2473, 2049, 2050, 2052, 2051], [2472, 2401, 2400, 2474, 2050, 1979, 1978, 2052], [2400, 2396, 2475, 2474, 1978, 1974, 2053, 2052], [2396, 2392, 2476, 2475, 1974, 1970, 2054, 2053], [2392, 2388, 2449, 2476, 1970, 1966, 2027, 2054], [2474, 2475, 2477, 2473, 2052, 2053, 2055, 2051], [2475, 2476, 2478, 2477, 2053, 2054, 2056, 2055], [2476, 2449, 2450, 2478, 2054, 2027, 2028, 2056], [2473, 2477, 2460, 2464, 2051, 2055, 2038, 2042], [2477, 2478, 2456, 2460, 2055, 2056, 2034, 2038], [2478, 2450, 2451, 2456, 2056, 2028, 2029, 2034], [2423, 2479, 2480, 2424, 2001, 2057, 2058, 2002], [2479, 2481, 2482, 2480, 2057, 2059, 2060, 2058], [2481, 2483, 2484, 2482, 2059, 2061, 2062, 2060], [2483, 2485, 2486, 2484, 2061, 2063, 2064, 2062], [2485, 2487, 2488, 2486, 2063, 2065, 2066, 2064], [2487, 2111, 2114, 2488, 2065, 1689, 1692, 2066], [2424, 2480, 2489, 2430, 2002, 2058, 2067, 2008], [2480, 2482, 2490, 2489, 2058, 2060, 2068, 2067], [2482, 2484, 2491, 2490, 2060, 2062, 2069, 2068], [2484, 2486, 2492, 2491, 2062, 2064, 2070, 2069], [2486, 2488, 2493, 2492, 2064, 2066, 2071, 2070], [2488, 2114, 2128, 2493, 2066, 1692, 1706, 2071], [2430, 2489, 2494, 2436, 2008, 2067, 2072, 2014], [2489, 2490, 2495, 2494, 2067, 2068, 2073, 2072], [2490, 2491, 2496, 2495, 2068, 2069, 2074, 2073], [2491, 2492, 2497, 2496, 2069, 2070, 2075, 2074], [2492, 2493, 2498, 2497, 2070, 2071, 2076, 2075], [2493, 2128, 2136, 2498, 2071, 1706, 1714, 2076], [2436, 2494, 2499, 2442, 2014, 2072, 2077, 2020], [2494, 2495, 2500, 2499, 2072, 2073, 2078, 2077], [2495, 2496, 2501, 2500, 2073, 2074, 2079, 2078], [2496, 2497, 2502, 2501, 2074, 2075, 2080, 2079], [2497, 2498, 2503, 2502, 2075, 2076, 2081, 2080], [2498, 2136, 2144, 2503, 2076, 1714, 1722, 2081], [2442, 2499, 2504, 2448, 2020, 2077, 2082, 2026], [2499, 2500, 2505, 2504, 2077, 2078, 2083, 2082], [2500, 2501, 2506, 2505, 2078, 2079, 2084, 2083], [2501, 2502, 2507, 2506, 2079, 2080, 2085, 2084], [2502, 2503, 2508, 2507, 2080, 2081, 2086, 2085], [2503, 2144, 2152, 2508, 2081, 1722, 1730, 2086], [2448, 2504, 2509, 2454, 2026, 2082, 2087, 2032], [2504, 2505, 2510, 2509, 2082, 2083, 2088, 2087], [2505, 2506, 2511, 2510, 2083, 2084, 2089, 2088], [2506, 2507, 2512, 2511, 2084, 2085, 2090, 2089], [2507, 2508, 2513, 2512, 2085, 2086, 2091, 2090], [2508, 2152, 2160, 2513, 2086, 1730, 1738, 2091], [2511, 2512, 2514, 2515, 2089, 2090, 2092, 2093], [2512, 2513, 2516, 2514, 2090, 2091, 2094, 2092], [2513, 2160, 2197, 2516, 2091, 1738, 1775, 2094], [2515, 2514, 2517, 2518, 2093, 2092, 2095, 2096], [2514, 2516, 2519, 2517, 2092, 2094, 2097, 2095], [2516, 2197, 2195, 2519, 2094, 1775, 1773, 2097], [2518, 2517, 2520, 2521, 2096, 2095, 2098, 2099], [2517, 2519, 2522, 2520, 2095, 2097, 2100, 2098], [2519, 2195, 2194, 2522, 2097, 1773, 1772, 2100], [2194, 2190, 2523, 2522, 1772, 1768, 2101, 2100], [2190, 2186, 2524, 2523, 1768, 1764, 2102, 2101], [2186, 2185, 2469, 2524, 1764, 1763, 2047, 2102], [2522, 2523, 2525, 2520, 2100, 2101, 2103, 2098], [2523, 2524, 2526, 2525, 2101, 2102, 2104, 2103], [2524, 2469, 2467, 2526, 2102, 2047, 2045, 2104], [2520, 2525, 2527, 2521, 2098, 2103, 2105, 2099], [2525, 2526, 2528, 2527, 2103, 2104, 2106, 2105], [2526, 2467, 2466, 2528, 2104, 2045, 2044, 2106], [2466, 2462, 2529, 2528, 2044, 2040, 2107, 2106], [2462, 2458, 2530, 2529, 2040, 2036, 2108, 2107], [2458, 2454, 2509, 2530, 2036, 2032, 2087, 2108], [2528, 2529, 2531, 2527, 2106, 2107, 2109, 2105], [2529, 2530, 2532, 2531, 2107, 2108, 2110, 2109], [2530, 2509, 2510, 2532, 2108, 2087, 2088, 2110], [2527, 2531, 2518, 2521, 2105, 2109, 2096, 2099], [2531, 2532, 2515, 2518, 2109, 2110, 2093, 2096], [2532, 2510, 2511, 2515, 2110, 2088, 2089, 2093], [2533, 2534, 2535, 2536, 2111, 2112, 2113, 2114], [2534, 2537, 2538, 2535, 2112, 2115, 2116, 2113], [2537, 2539, 2540, 2538, 2115, 2117, 2118, 2116], [2539, 2541, 2542, 2540, 2117, 2119, 2120, 2118], [2541, 2543, 2544, 2542, 2119, 2121, 2122, 2120], [2543, 2545, 2546, 2544, 2121, 2123, 2124, 2122], [2545, 2547, 2548, 2546, 2123, 2125, 2126, 2124], [2536, 2535, 2549, 2550, 2114, 2113, 2127, 2128], [2535, 2538, 2551, 2549, 2113, 2116, 2129, 2127], [2538, 2540, 2552, 2551, 2116, 2118, 2130, 2129], [2540, 2542, 2553, 2552, 2118, 2120, 2131, 2130], [2542, 2544, 2554, 2553, 2120, 2122, 2132, 2131], [2544, 2546, 2555, 2554, 2122, 2124, 2133, 2132], [2546, 2548, 2556, 2555, 2124, 2126, 2134, 2133], [2550, 2549, 2557, 2558, 2128, 2127, 2135, 2136], [2549, 2551, 2559, 2557, 2127, 2129, 2137, 2135], [2551, 2552, 2560, 2559, 2129, 2130, 2138, 2137], [2552, 2553, 2561, 2560, 2130, 2131, 2139, 2138], [2553, 2554, 2562, 2561, 2131, 2132, 2140, 2139], [2554, 2555, 2563, 2562, 2132, 2133, 2141, 2140], [2555, 2556, 2564, 2563, 2133, 2134, 2142, 2141], [2558, 2557, 2565, 2566, 2136, 2135, 2143, 2144], [2557, 2559, 2567, 2565, 2135, 2137, 2145, 2143], [2559, 2560, 2568, 2567, 2137, 2138, 2146, 2145], [2560, 2561, 2569, 2568, 2138, 2139, 2147, 2146], [2561, 2562, 2570, 2569, 2139, 2140, 2148, 2147], [2562, 2563, 2571, 2570, 2140, 2141, 2149, 2148], [2563, 2564, 2572, 2571, 2141, 2142, 2150, 2149], [2566, 2565, 2573, 2574, 2144, 2143, 2151, 2152], [2565, 2567, 2575, 2573, 2143, 2145, 2153, 2151], [2567, 2568, 2576, 2575, 2145, 2146, 2154, 2153], [2568, 2569, 2577, 2576, 2146, 2147, 2155, 2154], [2569, 2570, 2578, 2577, 2147, 2148, 2156, 2155], [2570, 2571, 2579, 2578, 2148, 2149, 2157, 2156], [2571, 2572, 2580, 2579, 2149, 2150, 2158, 2157], [2574, 2573, 2581, 2582, 2152, 2151, 2159, 2160], [2573, 2575, 2583, 2581, 2151, 2153, 2161, 2159], [2575, 2576, 2584, 2583, 2153, 2154, 2162, 2161], [2576, 2577, 2585, 2584, 2154, 2155, 2163, 2162], [2577, 2578, 2586, 2585, 2155, 2156, 2164, 2163], [2578, 2579, 2587, 2586, 2156, 2157, 2165, 2164], [2579, 2580, 2588, 2587, 2157, 2158, 2166, 2165], [2585, 2586, 2589, 2590, 2163, 2164, 2167, 2168], [2586, 2587, 2591, 2589, 2164, 2165, 2169, 2167], [2587, 2588, 2592, 2591, 2165, 2166, 2170, 2169], [2590, 2589, 2593, 2594, 2168, 2167, 2171, 2172], [2589, 2591, 2595, 2593, 2167, 2169, 2173, 2171], [2591, 2592, 2596, 2595, 2169, 2170, 2174, 2173], [2594, 2593, 2597, 2598, 2172, 2171, 2175, 2176], [2593, 2595, 2599, 2597, 2171, 2173, 2177, 2175], [2595, 2596, 2600, 2599, 2173, 2174, 2178, 2177], [2600, 2601, 2602, 2599, 2178, 2179, 2180, 2177], [2601, 2603, 2604, 2602, 2179, 2181, 2182, 2180], [2603, 2605, 2606, 2604, 2181, 2183, 2184, 2182], [2605, 2607, 2608, 2606, 2183, 2185, 2186, 2184], [2599, 2602, 2609, 2597, 2177, 2180, 2187, 2175], [2602, 2604, 2610, 2609, 2180, 2182, 2188, 2187], [2604, 2606, 2611, 2610, 2182, 2184, 2189, 2188], [2606, 2608, 2612, 2611, 2184, 2186, 2190, 2189], [2597, 2609, 2613, 2598, 2175, 2187, 2191, 2176], [2609, 2610, 2614, 2613, 2187, 2188, 2192, 2191], [2610, 2611, 2615, 2614, 2188, 2189, 2193, 2192], [2611, 2612, 2616, 2615, 2189, 2190, 2194, 2193], [2616, 2617, 2618, 2615, 2194, 2195, 2196, 2193], [2617, 2619, 2620, 2618, 2195, 2197, 2198, 2196], [2619, 2582, 2581, 2620, 2197, 2160, 2159, 2198], [2615, 2618, 2621, 2614, 2193, 2196, 2199, 2192], [2618, 2620, 2622, 2621, 2196, 2198, 2200, 2199], [2620, 2581, 2583, 2622, 2198, 2159, 2161, 2200], [2614, 2621, 2623, 2613, 2192, 2199, 2201, 2191], [2621, 2622, 2624, 2623, 2199, 2200, 2202, 2201], [2622, 2583, 2584, 2624, 2200, 2161, 2162, 2202], [2613, 2623, 2594, 2598, 2191, 2201, 2172, 2176], [2623, 2624, 2590, 2594, 2201, 2202, 2168, 2172], [2624, 2584, 2585, 2590, 2202, 2162, 2163, 2168], [2547, 2625, 2626, 2548, 2125, 2203, 2204, 2126], [2625, 2627, 2628, 2626, 2203, 2205, 2206, 2204], [2627, 2629, 2630, 2628, 2205, 2207, 2208, 2206], [2629, 2631, 2632, 2630, 2207, 2209, 2210, 2208], [2631, 2633, 2634, 2632, 2209, 2211, 2212, 2210], [2633, 2635, 2636, 2634, 2211, 2213, 2214, 2212], [2635, 2637, 2638, 2636, 2213, 2215, 2216, 2214], [2548, 2626, 2639, 2556, 2126, 2204, 2217, 2134], [2626, 2628, 2640, 2639, 2204, 2206, 2218, 2217], [2628, 2630, 2641, 2640, 2206, 2208, 2219, 2218], [2630, 2632, 2642, 2641, 2208, 2210, 2220, 2219], [2632, 2634, 2643, 2642, 2210, 2212, 2221, 2220], [2634, 2636, 2644, 2643, 2212, 2214, 2222, 2221], [2636, 2638, 2645, 2644, 2214, 2216, 2223, 2222], [2556, 2639, 2646, 2564, 2134, 2217, 2224, 2142], [2639, 2640, 2647, 2646, 2217, 2218, 2225, 2224], [2640, 2641, 2648, 2647, 2218, 2219, 2226, 2225], [2641, 2642, 2649, 2648, 2219, 2220, 2227, 2226], [2642, 2643, 2650, 2649, 2220, 2221, 2228, 2227], [2643, 2644, 2651, 2650, 2221, 2222, 2229, 2228], [2644, 2645, 2652, 2651, 2222, 2223, 2230, 2229], [2564, 2646, 2653, 2572, 2142, 2224, 2231, 2150], [2646, 2647, 2654, 2653, 2224, 2225, 2232, 2231], [2647, 2648, 2655, 2654, 2225, 2226, 2233, 2232], [2648, 2649, 2656, 2655, 2226, 2227, 2234, 2233], [2649, 2650, 2657, 2656, 2227, 2228, 2235, 2234], [2650, 2651, 2658, 2657, 2228, 2229, 2236, 2235], [2651, 2652, 2659, 2658, 2229, 2230, 2237, 2236], [2572, 2653, 2660, 2580, 2150, 2231, 2238, 2158], [2653, 2654, 2661, 2660, 2231, 2232, 2239, 2238], [2654, 2655, 2662, 2661, 2232, 2233, 2240, 2239], [2655, 2656, 2663, 2662, 2233, 2234, 2241, 2240], [2656, 2657, 2664, 2663, 2234, 2235, 2242, 2241], [2657, 2658, 2665, 2664, 2235, 2236, 2243, 2242], [2658, 2659, 2666, 2665, 2236, 2237, 2244, 2243], [2580, 2660, 2667, 2588, 2158, 2238, 2245, 2166], [2660, 2661, 2668, 2667, 2238, 2239, 2246, 2245], [2661, 2662, 2669, 2668, 2239, 2240, 2247, 2246], [2662, 2663, 2670, 2669, 2240, 2241, 2248, 2247], [2663, 2664, 2671, 2670, 2241, 2242, 2249, 2248], [2664, 2665, 2672, 2671, 2242, 2243, 2250, 2249], [2665, 2666, 2673, 2672, 2243, 2244, 2251, 2250], [2669, 2670, 2674, 2675, 2247, 2248, 2252, 2253], [2670, 2671, 2676, 2674, 2248, 2249, 2254, 2252], [2671, 2672, 2677, 2676, 2249, 2250, 2255, 2254], [2672, 2673, 2678, 2677, 2250, 2251, 2256, 2255], [2675, 2674, 2679, 2680, 2253, 2252, 2257, 2258], [2674, 2676, 2681, 2679, 2252, 2254, 2259, 2257], [2676, 2677, 2682, 2681, 2254, 2255, 2260, 2259], [2677, 2678, 2683, 2682, 2255, 2256, 2261, 2260], [2680, 2679, 2684, 2685, 2258, 2257, 2262, 2263], [2679, 2681, 2686, 2684, 2257, 2259, 2264, 2262], [2681, 2682, 2687, 2686, 2259, 2260, 2265, 2264], [2682, 2683, 2688, 2687, 2260, 2261, 2266, 2265], [2688, 2689, 2690, 2687, 2266, 2267, 2268, 2265], [2689, 2691, 2692, 2690, 2267, 2269, 2270, 2268], [2691, 2607, 2605, 2692, 2269, 2185, 2183, 2270], [2687, 2690, 2693, 2686, 2265, 2268, 2271, 2264], [2690, 2692, 2694, 2693, 2268, 2270, 2272, 2271], [2692, 2605, 2603, 2694, 2270, 2183, 2181, 2272], [2686, 2693, 2695, 2684, 2264, 2271, 2273, 2262], [2693, 2694, 2696, 2695, 2271, 2272, 2274, 2273], [2694, 2603, 2601, 2696, 2272, 2181, 2179, 2274], [2684, 2695, 2697, 2685, 2262, 2273, 2275, 2263], [2695, 2696, 2698, 2697, 2273, 2274, 2276, 2275], [2696, 2601, 2600, 2698, 2274, 2179, 2178, 2276], [2600, 2596, 2699, 2698, 2178, 2174, 2277, 2276], [2596, 2592, 2700, 2699, 2174, 2170, 2278, 2277], [2592, 2588, 2667, 2700, 2170, 2166, 2245, 2278], [2698, 2699, 2701, 2697, 2276, 2277, 2279, 2275], [2699, 2700, 2702, 2701, 2277, 2278, 2280, 2279], [2700, 2667, 2668, 2702, 2278, 2245, 2246, 2280], [2697, 2701, 2680, 2685, 2275, 2279, 2258, 2263], [2701, 2702, 2675, 2680, 2279, 2280, 2253, 2258], [2702, 2668, 2669, 2675, 2280, 2246, 2247, 2253], [2637, 2703, 2704, 2638, 2215, 2281, 2282, 2216], [2703, 2705, 2706, 2704, 2281, 2283, 2284, 2282], [2705, 2707, 2708, 2706, 2283, 2285, 2286, 2284], [2707, 2709, 2710, 2708, 2285, 2287, 2288, 2286], [2709, 2711, 2712, 2710, 2287, 2289, 2290, 2288], [2711, 2713, 2714, 2712, 2289, 2291, 2292, 2290], [2638, 2704, 2715, 2645, 2216, 2282, 2293, 2223], [2704, 2706, 2716, 2715, 2282, 2284, 2294, 2293], [2706, 2708, 2717, 2716, 2284, 2286, 2295, 2294], [2708, 2710, 2718, 2717, 2286, 2288, 2296, 2295], [2710, 2712, 2719, 2718, 2288, 2290, 2297, 2296], [2712, 2714, 2720, 2719, 2290, 2292, 2298, 2297], [2645, 2715, 2721, 2652, 2223, 2293, 2299, 2230], [2715, 2716, 2722, 2721, 2293, 2294, 2300, 2299], [2716, 2717, 2723, 2722, 2294, 2295, 2301, 2300], [2717, 2718, 2724, 2723, 2295, 2296, 2302, 2301], [2718, 2719, 2725, 2724, 2296, 2297, 2303, 2302], [2719, 2720, 2726, 2725, 2297, 2298, 2304, 2303], [2652, 2721, 2727, 2659, 2230, 2299, 2305, 2237], [2721, 2722, 2728, 2727, 2299, 2300, 2306, 2305], [2722, 2723, 2729, 2728, 2300, 2301, 2307, 2306], [2723, 2724, 2730, 2729, 2301, 2302, 2308, 2307], [2724, 2725, 2731, 2730, 2302, 2303, 2309, 2308], [2725, 2726, 2732, 2731, 2303, 2304, 2310, 2309], [2659, 2727, 2733, 2666, 2237, 2305, 2311, 2244], [2727, 2728, 2734, 2733, 2305, 2306, 2312, 2311], [2728, 2729, 2735, 2734, 2306, 2307, 2313, 2312], [2729, 2730, 2736, 2735, 2307, 2308, 2314, 2313], [2730, 2731, 2737, 2736, 2308, 2309, 2315, 2314], [2731, 2732, 2738, 2737, 2309, 2310, 2316, 2315], [2666, 2733, 2739, 2673, 2244, 2311, 2317, 2251], [2733, 2734, 2740, 2739, 2311, 2312, 2318, 2317], [2734, 2735, 2741, 2740, 2312, 2313, 2319, 2318], [2735, 2736, 2742, 2741, 2313, 2314, 2320, 2319], [2736, 2737, 2743, 2742, 2314, 2315, 2321, 2320], [2737, 2738, 2744, 2743, 2315, 2316, 2322, 2321], [2741, 2742, 2745, 2746, 2319, 2320, 2323, 2324], [2742, 2743, 2747, 2745, 2320, 2321, 2325, 2323], [2743, 2744, 2748, 2747, 2321, 2322, 2326, 2325], [2746, 2745, 2749, 2750, 2324, 2323, 2327, 2328], [2745, 2747, 2751, 2749, 2323, 2325, 2329, 2327], [2747, 2748, 2752, 2751, 2325, 2326, 2330, 2329], [2750, 2749, 2753, 2754, 2328, 2327, 2331, 2332], [2749, 2751, 2755, 2753, 2327, 2329, 2333, 2331], [2751, 2752, 2756, 2755, 2329, 2330, 2334, 2333], [2756, 2757, 2758, 2755, 2334, 2335, 2336, 2333], [2757, 2759, 2760, 2758, 2335, 2337, 2338, 2336], [2759, 2607, 2691, 2760, 2337, 2185, 2269, 2338], [2755, 2758, 2761, 2753, 2333, 2336, 2339, 2331], [2758, 2760, 2762, 2761, 2336, 2338, 2340, 2339], [2760, 2691, 2689, 2762, 2338, 2269, 2267, 2340], [2753, 2761, 2763, 2754, 2331, 2339, 2341, 2332], [2761, 2762, 2764, 2763, 2339, 2340, 2342, 2341], [2762, 2689, 2688, 2764, 2340, 2267, 2266, 2342], [2688, 2683, 2765, 2764, 2266, 2261, 2343, 2342], [2683, 2678, 2766, 2765, 2261, 2256, 2344, 2343], [2678, 2673, 2739, 2766, 2256, 2251, 2317, 2344], [2764, 2765, 2767, 2763, 2342, 2343, 2345, 2341], [2765, 2766, 2768, 2767, 2343, 2344, 2346, 2345], [2766, 2739, 2740, 2768, 2344, 2317, 2318, 2346], [2763, 2767, 2750, 2754, 2341, 2345, 2328, 2332], [2767, 2768, 2746, 2750, 2345, 2346, 2324, 2328], [2768, 2740, 2741, 2746, 2346, 2318, 2319, 2324], [2713, 2769, 2770, 2714, 2291, 2347, 2348, 2292], [2769, 2771, 2772, 2770, 2347, 2349, 2350, 2348], [2771, 2773, 2774, 2772, 2349, 2351, 2352, 2350], [2773, 2775, 2776, 2774, 2351, 2353, 2354, 2352], [2775, 2777, 2778, 2776, 2353, 2355, 2356, 2354], [2777, 2779, 2780, 2778, 2355, 2357, 2358, 2356], [2714, 2770, 2781, 2720, 2292, 2348, 2359, 2298], [2770, 2772, 2782, 2781, 2348, 2350, 2360, 2359], [2772, 2774, 2783, 2782, 2350, 2352, 2361, 2360], [2774, 2776, 2784, 2783, 2352, 2354, 2362, 2361], [2776, 2778, 2785, 2784, 2354, 2356, 2363, 2362], [2778, 2780, 2786, 2785, 2356, 2358, 2364, 2363], [2720, 2781, 2787, 2726, 2298, 2359, 2365, 2304], [2781, 2782, 2788, 2787, 2359, 2360, 2366, 2365], [2782, 2783, 2789, 2788, 2360, 2361, 2367, 2366], [2783, 2784, 2790, 2789, 2361, 2362, 2368, 2367], [2784, 2785, 2791, 2790, 2362, 2363, 2369, 2368], [2785, 2786, 2792, 2791, 2363, 2364, 2370, 2369], [2726, 2787, 2793, 2732, 2304, 2365, 2371, 2310], [2787, 2788, 2794, 2793, 2365, 2366, 2372, 2371], [2788, 2789, 2795, 2794, 2366, 2367, 2373, 2372], [2789, 2790, 2796, 2795, 2367, 2368, 2374, 2373], [2790, 2791, 2797, 2796, 2368, 2369, 2375, 2374], [2791, 2792, 2798, 2797, 2369, 2370, 2376, 2375], [2732, 2793, 2799, 2738, 2310, 2371, 2377, 2316], [2793, 2794, 2800, 2799, 2371, 2372, 2378, 2377], [2794, 2795, 2801, 2800, 2372, 2373, 2379, 2378], [2795, 2796, 2802, 2801, 2373, 2374, 2380, 2379], [2796, 2797, 2803, 2802, 2374, 2375, 2381, 2380], [2797, 2798, 2804, 2803, 2375, 2376, 2382, 2381], [2738, 2799, 2805, 2744, 2316, 2377, 2383, 2322], [2799, 2800, 2806, 2805, 2377, 2378, 2384, 2383], [2800, 2801, 2807, 2806, 2378, 2379, 2385, 2384], [2801, 2802, 2808, 2807, 2379, 2380, 2386, 2385], [2802, 2803, 2809, 2808, 2380, 2381, 2387, 2386], [2803, 2804, 2810, 2809, 2381, 2382, 2388, 2387], [2807, 2808, 2811, 2812, 2385, 2386, 2389, 2390], [2808, 2809, 2813, 2811, 2386, 2387, 2391, 2389], [2809, 2810, 2814, 2813, 2387, 2388, 2392, 2391], [2812, 2811, 2815, 2816, 2390, 2389, 2393, 2394], [2811, 2813, 2817, 2815, 2389, 2391, 2395, 2393], [2813, 2814, 2818, 2817, 2391, 2392, 2396, 2395], [2816, 2815, 2819, 2820, 2394, 2393, 2397, 2398], [2815, 2817, 2821, 2819, 2393, 2395, 2399, 2397], [2817, 2818, 2822, 2821, 2395, 2396, 2400, 2399], [2822, 2823, 2824, 2821, 2400, 2401, 2402, 2399], [2823, 2825, 2826, 2824, 2401, 2403, 2404, 2402], [2825, 2607, 2759, 2826, 2403, 2185, 2337, 2404], [2821, 2824, 2827, 2819, 2399, 2402, 2405, 2397], [2824, 2826, 2828, 2827, 2402, 2404, 2406, 2405], [2826, 2759, 2757, 2828, 2404, 2337, 2335, 2406], [2819, 2827, 2829, 2820, 2397, 2405, 2407, 2398], [2827, 2828, 2830, 2829, 2405, 2406, 2408, 2407], [2828, 2757, 2756, 2830, 2406, 2335, 2334, 2408], [2756, 2752, 2831, 2830, 2334, 2330, 2409, 2408], [2752, 2748, 2832, 2831, 2330, 2326, 2410, 2409], [2748, 2744, 2805, 2832, 2326, 2322, 2383, 2410], [2830, 2831, 2833, 2829, 2408, 2409, 2411, 2407], [2831, 2832, 2834, 2833, 2409, 2410, 2412, 2411], [2832, 2805, 2806, 2834, 2410, 2383, 2384, 2412], [2829, 2833, 2816, 2820, 2407, 2411, 2394, 2398], [2833, 2834, 2812, 2816, 2411, 2412, 2390, 2394], [2834, 2806, 2807, 2812, 2412, 2384, 2385, 2390], [2779, 2835, 2836, 2780, 2357, 2413, 2414, 2358], [2835, 2837, 2838, 2836, 2413, 2415, 2416, 2414], [2837, 2839, 2840, 2838, 2415, 2417, 2418, 2416], [2839, 2841, 2842, 2840, 2417, 2419, 2420, 2418], [2841, 2843, 2844, 2842, 2419, 2421, 2422, 2420], [2843, 2845, 2846, 2844, 2421, 2423, 2424, 2422], [2780, 2836, 2847, 2786, 2358, 2414, 2425, 2364], [2836, 2838, 2848, 2847, 2414, 2416, 2426, 2425], [2838, 2840, 2849, 2848, 2416, 2418, 2427, 2426], [2840, 2842, 2850, 2849, 2418, 2420, 2428, 2427], [2842, 2844, 2851, 2850, 2420, 2422, 2429, 2428], [2844, 2846, 2852, 2851, 2422, 2424, 2430, 2429], [2786, 2847, 2853, 2792, 2364, 2425, 2431, 2370], [2847, 2848, 2854, 2853, 2425, 2426, 2432, 2431], [2848, 2849, 2855, 2854, 2426, 2427, 2433, 2432], [2849, 2850, 2856, 2855, 2427, 2428, 2434, 2433], [2850, 2851, 2857, 2856, 2428, 2429, 2435, 2434], [2851, 2852, 2858, 2857, 2429, 2430, 2436, 2435], [2792, 2853, 2859, 2798, 2370, 2431, 2437, 2376], [2853, 2854, 2860, 2859, 2431, 2432, 2438, 2437], [2854, 2855, 2861, 2860, 2432, 2433, 2439, 2438], [2855, 2856, 2862, 2861, 2433, 2434, 2440, 2439], [2856, 2857, 2863, 2862, 2434, 2435, 2441, 2440], [2857, 2858, 2864, 2863, 2435, 2436, 2442, 2441], [2798, 2859, 2865, 2804, 2376, 2437, 2443, 2382], [2859, 2860, 2866, 2865, 2437, 2438, 2444, 2443], [2860, 2861, 2867, 2866, 2438, 2439, 2445, 2444], [2861, 2862, 2868, 2867, 2439, 2440, 2446, 2445], [2862, 2863, 2869, 2868, 2440, 2441, 2447, 2446], [2863, 2864, 2870, 2869, 2441, 2442, 2448, 2447], [2804, 2865, 2871, 2810, 2382, 2443, 2449, 2388], [2865, 2866, 2872, 2871, 2443, 2444, 2450, 2449], [2866, 2867, 2873, 2872, 2444, 2445, 2451, 2450], [2867, 2868, 2874, 2873, 2445, 2446, 2452, 2451], [2868, 2869, 2875, 2874, 2446, 2447, 2453, 2452], [2869, 2870, 2876, 2875, 2447, 2448, 2454, 2453], [2873, 2874, 2877, 2878, 2451, 2452, 2455, 2456], [2874, 2875, 2879, 2877, 2452, 2453, 2457, 2455], [2875, 2876, 2880, 2879, 2453, 2454, 2458, 2457], [2878, 2877, 2881, 2882, 2456, 2455, 2459, 2460], [2877, 2879, 2883, 2881, 2455, 2457, 2461, 2459], [2879, 2880, 2884, 2883, 2457, 2458, 2462, 2461], [2882, 2881, 2885, 2886, 2460, 2459, 2463, 2464], [2881, 2883, 2887, 2885, 2459, 2461, 2465, 2463], [2883, 2884, 2888, 2887, 2461, 2462, 2466, 2465], [2888, 2889, 2890, 2887, 2466, 2467, 2468, 2465], [2889, 2891, 2892, 2890, 2467, 2469, 2470, 2468], [2891, 2607, 2825, 2892, 2469, 2185, 2403, 2470], [2887, 2890, 2893, 2885, 2465, 2468, 2471, 2463], [2890, 2892, 2894, 2893, 2468, 2470, 2472, 2471], [2892, 2825, 2823, 2894, 2470, 2403, 2401, 2472], [2885, 2893, 2895, 2886, 2463, 2471, 2473, 2464], [2893, 2894, 2896, 2895, 2471, 2472, 2474, 2473], [2894, 2823, 2822, 2896, 2472, 2401, 2400, 2474], [2822, 2818, 2897, 2896, 2400, 2396, 2475, 2474], [2818, 2814, 2898, 2897, 2396, 2392, 2476, 2475], [2814, 2810, 2871, 2898, 2392, 2388, 2449, 2476], [2896, 2897, 2899, 2895, 2474, 2475, 2477, 2473], [2897, 2898, 2900, 2899, 2475, 2476, 2478, 2477], [2898, 2871, 2872, 2900, 2476, 2449, 2450, 2478], [2895, 2899, 2882, 2886, 2473, 2477, 2460, 2464], [2899, 2900, 2878, 2882, 2477, 2478, 2456, 2460], [2900, 2872, 2873, 2878, 2478, 2450, 2451, 2456], [2845, 2901, 2902, 2846, 2423, 2479, 2480, 2424], [2901, 2903, 2904, 2902, 2479, 2481, 2482, 2480], [2903, 2905, 2906, 2904, 2481, 2483, 2484, 2482], [2905, 2907, 2908, 2906, 2483, 2485, 2486, 2484], [2907, 2909, 2910, 2908, 2485, 2487, 2488, 2486], [2909, 2533, 2536, 2910, 2487, 2111, 2114, 2488], [2846, 2902, 2911, 2852, 2424, 2480, 2489, 2430], [2902, 2904, 2912, 2911, 2480, 2482, 2490, 2489], [2904, 2906, 2913, 2912, 2482, 2484, 2491, 2490], [2906, 2908, 2914, 2913, 2484, 2486, 2492, 2491], [2908, 2910, 2915, 2914, 2486, 2488, 2493, 2492], [2910, 2536, 2550, 2915, 2488, 2114, 2128, 2493], [2852, 2911, 2916, 2858, 2430, 2489, 2494, 2436], [2911, 2912, 2917, 2916, 2489, 2490, 2495, 2494], [2912, 2913, 2918, 2917, 2490, 2491, 2496, 2495], [2913, 2914, 2919, 2918, 2491, 2492, 2497, 2496], [2914, 2915, 2920, 2919, 2492, 2493, 2498, 2497], [2915, 2550, 2558, 2920, 2493, 2128, 2136, 2498], [2858, 2916, 2921, 2864, 2436, 2494, 2499, 2442], [2916, 2917, 2922, 2921, 2494, 2495, 2500, 2499], [2917, 2918, 2923, 2922, 2495, 2496, 2501, 2500], [2918, 2919, 2924, 2923, 2496, 2497, 2502, 2501], [2919, 2920, 2925, 2924, 2497, 2498, 2503, 2502], [2920, 2558, 2566, 2925, 2498, 2136, 2144, 2503], [2864, 2921, 2926, 2870, 2442, 2499, 2504, 2448], [2921, 2922, 2927, 2926, 2499, 2500, 2505, 2504], [2922, 2923, 2928, 2927, 2500, 2501, 2506, 2505], [2923, 2924, 2929, 2928, 2501, 2502, 2507, 2506], [2924, 2925, 2930, 2929, 2502, 2503, 2508, 2507], [2925, 2566, 2574, 2930, 2503, 2144, 2152, 2508], [2870, 2926, 2931, 2876, 2448, 2504, 2509, 2454], [2926, 2927, 2932, 2931, 2504, 2505, 2510, 2509], [2927, 2928, 2933, 2932, 2505, 2506, 2511, 2510], [2928, 2929, 2934, 2933, 2506, 2507, 2512, 2511], [2929, 2930, 2935, 2934, 2507, 2508, 2513, 2512], [2930, 2574, 2582, 2935, 2508, 2152, 2160, 2513], [2933, 2934, 2936, 2937, 2511, 2512, 2514, 2515], [2934, 2935, 2938, 2936, 2512, 2513, 2516, 2514], [2935, 2582, 2619, 2938, 2513, 2160, 2197, 2516], [2937, 2936, 2939, 2940, 2515, 2514, 2517, 2518], [2936, 2938, 2941, 2939, 2514, 2516, 2519, 2517], [2938, 2619, 2617, 2941, 2516, 2197, 2195, 2519], [2940, 2939, 2942, 2943, 2518, 2517, 2520, 2521], [2939, 2941, 2944, 2942, 2517, 2519, 2522, 2520], [2941, 2617, 2616, 2944, 2519, 2195, 2194, 2522], [2616, 2612, 2945, 2944, 2194, 2190, 2523, 2522], [2612, 2608, 2946, 2945, 2190, 2186, 2524, 2523], [2608, 2607, 2891, 2946, 2186, 2185, 2469, 2524], [2944, 2945, 2947, 2942, 2522, 2523, 2525, 2520], [2945, 2946, 2948, 2947, 2523, 2524, 2526, 2525], [2946, 2891, 2889, 2948, 2524, 2469, 2467, 2526], [2942, 2947, 2949, 2943, 2520, 2525, 2527, 2521], [2947, 2948, 2950, 2949, 2525, 2526, 2528, 2527], [2948, 2889, 2888, 2950, 2526, 2467, 2466, 2528], [2888, 2884, 2951, 2950, 2466, 2462, 2529, 2528], [2884, 2880, 2952, 2951, 2462, 2458, 2530, 2529], [2880, 2876, 2931, 2952, 2458, 2454, 2509, 2530], [2950, 2951, 2953, 2949, 2528, 2529, 2531, 2527], [2951, 2952, 2954, 2953, 2529, 2530, 2532, 2531], [2952, 2931, 2932, 2954, 2530, 2509, 2510, 2532], [2949, 2953, 2940, 2943, 2527, 2531, 2518, 2521], [2953, 2954, 2937, 2940, 2531, 2532, 2515, 2518], [2954, 2932, 2933, 2937, 2532, 2510, 2511, 2515], [2955, 2956, 2957, 2958, 2533, 2534, 2535, 2536], [2956, 2959, 2960, 2957, 2534, 2537, 2538, 2535], [2959, 2961, 2962, 2960, 2537, 2539, 2540, 2538], [2961, 2963, 2964, 2962, 2539, 2541, 2542, 2540], [2963, 2965, 2966, 2964, 2541, 2543, 2544, 2542], [2965, 2967, 2968, 2966, 2543, 2545, 2546, 2544], [2967, 2969, 2970, 2968, 2545, 2547, 2548, 2546], [2958, 2957, 2971, 2972, 2536, 2535, 2549, 2550], [2957, 2960, 2973, 2971, 2535, 2538, 2551, 2549], [2960, 2962, 2974, 2973, 2538, 2540, 2552, 2551], [2962, 2964, 2975, 2974, 2540, 2542, 2553, 2552], [2964, 2966, 2976, 2975, 2542, 2544, 2554, 2553], [2966, 2968, 2977, 2976, 2544, 2546, 2555, 2554], [2968, 2970, 2978, 2977, 2546, 2548, 2556, 2555], [2972, 2971, 2979, 2980, 2550, 2549, 2557, 2558], [2971, 2973, 2981, 2979, 2549, 2551, 2559, 2557], [2973, 2974, 2982, 2981, 2551, 2552, 2560, 2559], [2974, 2975, 2983, 2982, 2552, 2553, 2561, 2560], [2975, 2976, 2984, 2983, 2553, 2554, 2562, 2561], [2976, 2977, 2985, 2984, 2554, 2555, 2563, 2562], [2977, 2978, 2986, 2985, 2555, 2556, 2564, 2563], [2980, 2979, 2987, 2988, 2558, 2557, 2565, 2566], [2979, 2981, 2989, 2987, 2557, 2559, 2567, 2565], [2981, 2982, 2990, 2989, 2559, 2560, 2568, 2567], [2982, 2983, 2991, 2990, 2560, 2561, 2569, 2568], [2983, 2984, 2992, 2991, 2561, 2562, 2570, 2569], [2984, 2985, 2993, 2992, 2562, 2563, 2571, 2570], [2985, 2986, 2994, 2993, 2563, 2564, 2572, 2571], [2988, 2987, 2995, 2996, 2566, 2565, 2573, 2574], [2987, 2989, 2997, 2995, 2565, 2567, 2575, 2573], [2989, 2990, 2998, 2997, 2567, 2568, 2576, 2575], [2990, 2991, 2999, 2998, 2568, 2569, 2577, 2576], [2991, 2992, 3000, 2999, 2569, 2570, 2578, 2577], [2992, 2993, 3001, 3000, 2570, 2571, 2579, 2578], [2993, 2994, 3002, 3001, 2571, 2572, 2580, 2579], [2996, 2995, 3003, 3004, 2574, 2573, 2581, 2582], [2995, 2997, 3005, 3003, 2573, 2575, 2583, 2581], [2997, 2998, 3006, 3005, 2575, 2576, 2584, 2583], [2998, 2999, 3007, 3006, 2576, 2577, 2585, 2584], [2999, 3000, 3008, 3007, 2577, 2578, 2586, 2585], [3000, 3001, 3009, 3008, 2578, 2579, 2587, 2586], [3001, 3002, 3010, 3009, 2579, 2580, 2588, 2587], [3007, 3008, 3011, 3012, 2585, 2586, 2589, 2590], [3008, 3009, 3013, 3011, 2586, 2587, 2591, 2589], [3009, 3010, 3014, 3013, 2587, 2588, 2592, 2591], [3012, 3011, 3015, 3016, 2590, 2589, 2593, 2594], [3011, 3013, 3017, 3015, 2589, 2591, 2595, 2593], [3013, 3014, 3018, 3017, 2591, 2592, 2596, 2595], [3016, 3015, 3019, 3020, 2594, 2593, 2597, 2598], [3015, 3017, 3021, 3019, 2593, 2595, 2599, 2597], [3017, 3018, 3022, 3021, 2595, 2596, 2600, 2599], [3022, 3023, 3024, 3021, 2600, 2601, 2602, 2599], [3023, 3025, 3026, 3024, 2601, 2603, 2604, 2602], [3025, 3027, 3028, 3026, 2603, 2605, 2606, 2604], [3027, 3029, 3030, 3028, 2605, 2607, 2608, 2606], [3021, 3024, 3031, 3019, 2599, 2602, 2609, 2597], [3024, 3026, 3032, 3031, 2602, 2604, 2610, 2609], [3026, 3028, 3033, 3032, 2604, 2606, 2611, 2610], [3028, 3030, 3034, 3033, 2606, 2608, 2612, 2611], [3019, 3031, 3035, 3020, 2597, 2609, 2613, 2598], [3031, 3032, 3036, 3035, 2609, 2610, 2614, 2613], [3032, 3033, 3037, 3036, 2610, 2611, 2615, 2614], [3033, 3034, 3038, 3037, 2611, 2612, 2616, 2615], [3038, 3039, 3040, 3037, 2616, 2617, 2618, 2615], [3039, 3041, 3042, 3040, 2617, 2619, 2620, 2618], [3041, 3004, 3003, 3042, 2619, 2582, 2581, 2620], [3037, 3040, 3043, 3036, 2615, 2618, 2621, 2614], [3040, 3042, 3044, 3043, 2618, 2620, 2622, 2621], [3042, 3003, 3005, 3044, 2620, 2581, 2583, 2622], [3036, 3043, 3045, 3035, 2614, 2621, 2623, 2613], [3043, 3044, 3046, 3045, 2621, 2622, 2624, 2623], [3044, 3005, 3006, 3046, 2622, 2583, 2584, 2624], [3035, 3045, 3016, 3020, 2613, 2623, 2594, 2598], [3045, 3046, 3012, 3016, 2623, 2624, 2590, 2594], [3046, 3006, 3007, 3012, 2624, 2584, 2585, 2590], [2969, 3047, 3048, 2970, 2547, 2625, 2626, 2548], [3047, 3049, 3050, 3048, 2625, 2627, 2628, 2626], [3049, 3051, 3052, 3050, 2627, 2629, 2630, 2628], [3051, 3053, 3054, 3052, 2629, 2631, 2632, 2630], [3053, 3055, 3056, 3054, 2631, 2633, 2634, 2632], [3055, 3057, 3058, 3056, 2633, 2635, 2636, 2634], [3057, 3059, 3060, 3058, 2635, 2637, 2638, 2636], [2970, 3048, 3061, 2978, 2548, 2626, 2639, 2556], [3048, 3050, 3062, 3061, 2626, 2628, 2640, 2639], [3050, 3052, 3063, 3062, 2628, 2630, 2641, 2640], [3052, 3054, 3064, 3063, 2630, 2632, 2642, 2641], [3054, 3056, 3065, 3064, 2632, 2634, 2643, 2642], [3056, 3058, 3066, 3065, 2634, 2636, 2644, 2643], [3058, 3060, 3067, 3066, 2636, 2638, 2645, 2644], [2978, 3061, 3068, 2986, 2556, 2639, 2646, 2564], [3061, 3062, 3069, 3068, 2639, 2640, 2647, 2646], [3062, 3063, 3070, 3069, 2640, 2641, 2648, 2647], [3063, 3064, 3071, 3070, 2641, 2642, 2649, 2648], [3064, 3065, 3072, 3071, 2642, 2643, 2650, 2649], [3065, 3066, 3073, 3072, 2643, 2644, 2651, 2650], [3066, 3067, 3074, 3073, 2644, 2645, 2652, 2651], [2986, 3068, 3075, 2994, 2564, 2646, 2653, 2572], [3068, 3069, 3076, 3075, 2646, 2647, 2654, 2653], [3069, 3070, 3077, 3076, 2647, 2648, 2655, 2654], [3070, 3071, 3078, 3077, 2648, 2649, 2656, 2655], [3071, 3072, 3079, 3078, 2649, 2650, 2657, 2656], [3072, 3073, 3080, 3079, 2650, 2651, 2658, 2657], [3073, 3074, 3081, 3080, 2651, 2652, 2659, 2658], [2994, 3075, 3082, 3002, 2572, 2653, 2660, 2580], [3075, 3076, 3083, 3082, 2653, 2654, 2661, 2660], [3076, 3077, 3084, 3083, 2654, 2655, 2662, 2661], [3077, 3078, 3085, 3084, 2655, 2656, 2663, 2662], [3078, 3079, 3086, 3085, 2656, 2657, 2664, 2663], [3079, 3080, 3087, 3086, 2657, 2658, 2665, 2664], [3080, 3081, 3088, 3087, 2658, 2659, 2666, 2665], [3002, 3082, 3089, 3010, 2580, 2660, 2667, 2588], [3082, 3083, 3090, 3089, 2660, 2661, 2668, 2667], [3083, 3084, 3091, 3090, 2661, 2662, 2669, 2668], [3084, 3085, 3092, 3091, 2662, 2663, 2670, 2669], [3085, 3086, 3093, 3092, 2663, 2664, 2671, 2670], [3086, 3087, 3094, 3093, 2664, 2665, 2672, 2671], [3087, 3088, 3095, 3094, 2665, 2666, 2673, 2672], [3091, 3092, 3096, 3097, 2669, 2670, 2674, 2675], [3092, 3093, 3098, 3096, 2670, 2671, 2676, 2674], [3093, 3094, 3099, 3098, 2671, 2672, 2677, 2676], [3094, 3095, 3100, 3099, 2672, 2673, 2678, 2677], [3097, 3096, 3101, 3102, 2675, 2674, 2679, 2680], [3096, 3098, 3103, 3101, 2674, 2676, 2681, 2679], [3098, 3099, 3104, 3103, 2676, 2677, 2682, 2681], [3099, 3100, 3105, 3104, 2677, 2678, 2683, 2682], [3102, 3101, 3106, 3107, 2680, 2679, 2684, 2685], [3101, 3103, 3108, 3106, 2679, 2681, 2686, 2684], [3103, 3104, 3109, 3108, 2681, 2682, 2687, 2686], [3104, 3105, 3110, 3109, 2682, 2683, 2688, 2687], [3110, 3111, 3112, 3109, 2688, 2689, 2690, 2687], [3111, 3113, 3114, 3112, 2689, 2691, 2692, 2690], [3113, 3029, 3027, 3114, 2691, 2607, 2605, 2692], [3109, 3112, 3115, 3108, 2687, 2690, 2693, 2686], [3112, 3114, 3116, 3115, 2690, 2692, 2694, 2693], [3114, 3027, 3025, 3116, 2692, 2605, 2603, 2694], [3108, 3115, 3117, 3106, 2686, 2693, 2695, 2684], [3115, 3116, 3118, 3117, 2693, 2694, 2696, 2695], [3116, 3025, 3023, 3118, 2694, 2603, 2601, 2696], [3106, 3117, 3119, 3107, 2684, 2695, 2697, 2685], [3117, 3118, 3120, 3119, 2695, 2696, 2698, 2697], [3118, 3023, 3022, 3120, 2696, 2601, 2600, 2698], [3022, 3018, 3121, 3120, 2600, 2596, 2699, 2698], [3018, 3014, 3122, 3121, 2596, 2592, 2700, 2699], [3014, 3010, 3089, 3122, 2592, 2588, 2667, 2700], [3120, 3121, 3123, 3119, 2698, 2699, 2701, 2697], [3121, 3122, 3124, 3123, 2699, 2700, 2702, 2701], [3122, 3089, 3090, 3124, 2700, 2667, 2668, 2702], [3119, 3123, 3102, 3107, 2697, 2701, 2680, 2685], [3123, 3124, 3097, 3102, 2701, 2702, 2675, 2680], [3124, 3090, 3091, 3097, 2702, 2668, 2669, 2675], [3059, 3125, 3126, 3060, 2637, 2703, 2704, 2638], [3125, 3127, 3128, 3126, 2703, 2705, 2706, 2704], [3127, 3129, 3130, 3128, 2705, 2707, 2708, 2706], [3129, 3131, 3132, 3130, 2707, 2709, 2710, 2708], [3131, 3133, 3134, 3132, 2709, 2711, 2712, 2710], [3133, 3135, 3136, 3134, 2711, 2713, 2714, 2712], [3060, 3126, 3137, 3067, 2638, 2704, 2715, 2645], [3126, 3128, 3138, 3137, 2704, 2706, 2716, 2715], [3128, 3130, 3139, 3138, 2706, 2708, 2717, 2716], [3130, 3132, 3140, 3139, 2708, 2710, 2718, 2717], [3132, 3134, 3141, 3140, 2710, 2712, 2719, 2718], [3134, 3136, 3142, 3141, 2712, 2714, 2720, 2719], [3067, 3137, 3143, 3074, 2645, 2715, 2721, 2652], [3137, 3138, 3144, 3143, 2715, 2716, 2722, 2721], [3138, 3139, 3145, 3144, 2716, 2717, 2723, 2722], [3139, 3140, 3146, 3145, 2717, 2718, 2724, 2723], [3140, 3141, 3147, 3146, 2718, 2719, 2725, 2724], [3141, 3142, 3148, 3147, 2719, 2720, 2726, 2725], [3074, 3143, 3149, 3081, 2652, 2721, 2727, 2659], [3143, 3144, 3150, 3149, 2721, 2722, 2728, 2727], [3144, 3145, 3151, 3150, 2722, 2723, 2729, 2728], [3145, 3146, 3152, 3151, 2723, 2724, 2730, 2729], [3146, 3147, 3153, 3152, 2724, 2725, 2731, 2730], [3147, 3148, 3154, 3153, 2725, 2726, 2732, 2731], [3081, 3149, 3155, 3088, 2659, 2727, 2733, 2666], [3149, 3150, 3156, 3155, 2727, 2728, 2734, 2733], [3150, 3151, 3157, 3156, 2728, 2729, 2735, 2734], [3151, 3152, 3158, 3157, 2729, 2730, 2736, 2735], [3152, 3153, 3159, 3158, 2730, 2731, 2737, 2736], [3153, 3154, 3160, 3159, 2731, 2732, 2738, 2737], [3088, 3155, 3161, 3095, 2666, 2733, 2739, 2673], [3155, 3156, 3162, 3161, 2733, 2734, 2740, 2739], [3156, 3157, 3163, 3162, 2734, 2735, 2741, 2740], [3157, 3158, 3164, 3163, 2735, 2736, 2742, 2741], [3158, 3159, 3165, 3164, 2736, 2737, 2743, 2742], [3159, 3160, 3166, 3165, 2737, 2738, 2744, 2743], [3163, 3164, 3167, 3168, 2741, 2742, 2745, 2746], [3164, 3165, 3169, 3167, 2742, 2743, 2747, 2745], [3165, 3166, 3170, 3169, 2743, 2744, 2748, 2747], [3168, 3167, 3171, 3172, 2746, 2745, 2749, 2750], [3167, 3169, 3173, 3171, 2745, 2747, 2751, 2749], [3169, 3170, 3174, 3173, 2747, 2748, 2752, 2751], [3172, 3171, 3175, 3176, 2750, 2749, 2753, 2754], [3171, 3173, 3177, 3175, 2749, 2751, 2755, 2753], [3173, 3174, 3178, 3177, 2751, 2752, 2756, 2755], [3178, 3179, 3180, 3177, 2756, 2757, 2758, 2755], [3179, 3181, 3182, 3180, 2757, 2759, 2760, 2758], [3181, 3029, 3113, 3182, 2759, 2607, 2691, 2760], [3177, 3180, 3183, 3175, 2755, 2758, 2761, 2753], [3180, 3182, 3184, 3183, 2758, 2760, 2762, 2761], [3182, 3113, 3111, 3184, 2760, 2691, 2689, 2762], [3175, 3183, 3185, 3176, 2753, 2761, 2763, 2754], [3183, 3184, 3186, 3185, 2761, 2762, 2764, 2763], [3184, 3111, 3110, 3186, 2762, 2689, 2688, 2764], [3110, 3105, 3187, 3186, 2688, 2683, 2765, 2764], [3105, 3100, 3188, 3187, 2683, 2678, 2766, 2765], [3100, 3095, 3161, 3188, 2678, 2673, 2739, 2766], [3186, 3187, 3189, 3185, 2764, 2765, 2767, 2763], [3187, 3188, 3190, 3189, 2765, 2766, 2768, 2767], [3188, 3161, 3162, 3190, 2766, 2739, 2740, 2768], [3185, 3189, 3172, 3176, 2763, 2767, 2750, 2754], [3189, 3190, 3168, 3172, 2767, 2768, 2746, 2750], [3190, 3162, 3163, 3168, 2768, 2740, 2741, 2746], [3135, 3191, 3192, 3136, 2713, 2769, 2770, 2714], [3191, 3193, 3194, 3192, 2769, 2771, 2772, 2770], [3193, 3195, 3196, 3194, 2771, 2773, 2774, 2772], [3195, 3197, 3198, 3196, 2773, 2775, 2776, 2774], [3197, 3199, 3200, 3198, 2775, 2777, 2778, 2776], [3199, 3201, 3202, 3200, 2777, 2779, 2780, 2778], [3136, 3192, 3203, 3142, 2714, 2770, 2781, 2720], [3192, 3194, 3204, 3203, 2770, 2772, 2782, 2781], [3194, 3196, 3205, 3204, 2772, 2774, 2783, 2782], [3196, 3198, 3206, 3205, 2774, 2776, 2784, 2783], [3198, 3200, 3207, 3206, 2776, 2778, 2785, 2784], [3200, 3202, 3208, 3207, 2778, 2780, 2786, 2785], [3142, 3203, 3209, 3148, 2720, 2781, 2787, 2726], [3203, 3204, 3210, 3209, 2781, 2782, 2788, 2787], [3204, 3205, 3211, 3210, 2782, 2783, 2789, 2788], [3205, 3206, 3212, 3211, 2783, 2784, 2790, 2789], [3206, 3207, 3213, 3212, 2784, 2785, 2791, 2790], [3207, 3208, 3214, 3213, 2785, 2786, 2792, 2791], [3148, 3209, 3215, 3154, 2726, 2787, 2793, 2732], [3209, 3210, 3216, 3215, 2787, 2788, 2794, 2793], [3210, 3211, 3217, 3216, 2788, 2789, 2795, 2794], [3211, 3212, 3218, 3217, 2789, 2790, 2796, 2795], [3212, 3213, 3219, 3218, 2790, 2791, 2797, 2796], [3213, 3214, 3220, 3219, 2791, 2792, 2798, 2797], [3154, 3215, 3221, 3160, 2732, 2793, 2799, 2738], [3215, 3216, 3222, 3221, 2793, 2794, 2800, 2799], [3216, 3217, 3223, 3222, 2794, 2795, 2801, 2800], [3217, 3218, 3224, 3223, 2795, 2796, 2802, 2801], [3218, 3219, 3225, 3224, 2796, 2797, 2803, 2802], [3219, 3220, 3226, 3225, 2797, 2798, 2804, 2803], [3160, 3221, 3227, 3166, 2738, 2799, 2805, 2744], [3221, 3222, 3228, 3227, 2799, 2800, 2806, 2805], [3222, 3223, 3229, 3228, 2800, 2801, 2807, 2806], [3223, 3224, 3230, 3229, 2801, 2802, 2808, 2807], [3224, 3225, 3231, 3230, 2802, 2803, 2809, 2808], [3225, 3226, 3232, 3231, 2803, 2804, 2810, 2809], [3229, 3230, 3233, 3234, 2807, 2808, 2811, 2812], [3230, 3231, 3235, 3233, 2808, 2809, 2813, 2811], [3231, 3232, 3236, 3235, 2809, 2810, 2814, 2813], [3234, 3233, 3237, 3238, 2812, 2811, 2815, 2816], [3233, 3235, 3239, 3237, 2811, 2813, 2817, 2815], [3235, 3236, 3240, 3239, 2813, 2814, 2818, 2817], [3238, 3237, 3241, 3242, 2816, 2815, 2819, 2820], [3237, 3239, 3243, 3241, 2815, 2817, 2821, 2819], [3239, 3240, 3244, 3243, 2817, 2818, 2822, 2821], [3244, 3245, 3246, 3243, 2822, 2823, 2824, 2821], [3245, 3247, 3248, 3246, 2823, 2825, 2826, 2824], [3247, 3029, 3181, 3248, 2825, 2607, 2759, 2826], [3243, 3246, 3249, 3241, 2821, 2824, 2827, 2819], [3246, 3248, 3250, 3249, 2824, 2826, 2828, 2827], [3248, 3181, 3179, 3250, 2826, 2759, 2757, 2828], [3241, 3249, 3251, 3242, 2819, 2827, 2829, 2820], [3249, 3250, 3252, 3251, 2827, 2828, 2830, 2829], [3250, 3179, 3178, 3252, 2828, 2757, 2756, 2830], [3178, 3174, 3253, 3252, 2756, 2752, 2831, 2830], [3174, 3170, 3254, 3253, 2752, 2748, 2832, 2831], [3170, 3166, 3227, 3254, 2748, 2744, 2805, 2832], [3252, 3253, 3255, 3251, 2830, 2831, 2833, 2829], [3253, 3254, 3256, 3255, 2831, 2832, 2834, 2833], [3254, 3227, 3228, 3256, 2832, 2805, 2806, 2834], [3251, 3255, 3238, 3242, 2829, 2833, 2816, 2820], [3255, 3256, 3234, 3238, 2833, 2834, 2812, 2816], [3256, 3228, 3229, 3234, 2834, 2806, 2807, 2812], [3201, 3257, 3258, 3202, 2779, 2835, 2836, 2780], [3257, 3259, 3260, 3258, 2835, 2837, 2838, 2836], [3259, 3261, 3262, 3260, 2837, 2839, 2840, 2838], [3261, 3263, 3264, 3262, 2839, 2841, 2842, 2840], [3263, 3265, 3266, 3264, 2841, 2843, 2844, 2842], [3265, 3267, 3268, 3266, 2843, 2845, 2846, 2844], [3202, 3258, 3269, 3208, 2780, 2836, 2847, 2786], [3258, 3260, 3270, 3269, 2836, 2838, 2848, 2847], [3260, 3262, 3271, 3270, 2838, 2840, 2849, 2848], [3262, 3264, 3272, 3271, 2840, 2842, 2850, 2849], [3264, 3266, 3273, 3272, 2842, 2844, 2851, 2850], [3266, 3268, 3274, 3273, 2844, 2846, 2852, 2851], [3208, 3269, 3275, 3214, 2786, 2847, 2853, 2792], [3269, 3270, 3276, 3275, 2847, 2848, 2854, 2853], [3270, 3271, 3277, 3276, 2848, 2849, 2855, 2854], [3271, 3272, 3278, 3277, 2849, 2850, 2856, 2855], [3272, 3273, 3279, 3278, 2850, 2851, 2857, 2856], [3273, 3274, 3280, 3279, 2851, 2852, 2858, 2857], [3214, 3275, 3281, 3220, 2792, 2853, 2859, 2798], [3275, 3276, 3282, 3281, 2853, 2854, 2860, 2859], [3276, 3277, 3283, 3282, 2854, 2855, 2861, 2860], [3277, 3278, 3284, 3283, 2855, 2856, 2862, 2861], [3278, 3279, 3285, 3284, 2856, 2857, 2863, 2862], [3279, 3280, 3286, 3285, 2857, 2858, 2864, 2863], [3220, 3281, 3287, 3226, 2798, 2859, 2865, 2804], [3281, 3282, 3288, 3287, 2859, 2860, 2866, 2865], [3282, 3283, 3289, 3288, 2860, 2861, 2867, 2866], [3283, 3284, 3290, 3289, 2861, 2862, 2868, 2867], [3284, 3285, 3291, 3290, 2862, 2863, 2869, 2868], [3285, 3286, 3292, 3291, 2863, 2864, 2870, 2869], [3226, 3287, 3293, 3232, 2804, 2865, 2871, 2810], [3287, 3288, 3294, 3293, 2865, 2866, 2872, 2871], [3288, 3289, 3295, 3294, 2866, 2867, 2873, 2872], [3289, 3290, 3296, 3295, 2867, 2868, 2874, 2873], [3290, 3291, 3297, 3296, 2868, 2869, 2875, 2874], [3291, 3292, 3298, 3297, 2869, 2870, 2876, 2875], [3295, 3296, 3299, 3300, 2873, 2874, 2877, 2878], [3296, 3297, 3301, 3299, 2874, 2875, 2879, 2877], [3297, 3298, 3302, 3301, 2875, 2876, 2880, 2879], [3300, 3299, 3303, 3304, 2878, 2877, 2881, 2882], [3299, 3301, 3305, 3303, 2877, 2879, 2883, 2881], [3301, 3302, 3306, 3305, 2879, 2880, 2884, 2883], [3304, 3303, 3307, 3308, 2882, 2881, 2885, 2886], [3303, 3305, 3309, 3307, 2881, 2883, 2887, 2885], [3305, 3306, 3310, 3309, 2883, 2884, 2888, 2887], [3310, 3311, 3312, 3309, 2888, 2889, 2890, 2887], [3311, 3313, 3314, 3312, 2889, 2891, 2892, 2890], [3313, 3029, 3247, 3314, 2891, 2607, 2825, 2892], [3309, 3312, 3315, 3307, 2887, 2890, 2893, 2885], [3312, 3314, 3316, 3315, 2890, 2892, 2894, 2893], [3314, 3247, 3245, 3316, 2892, 2825, 2823, 2894], [3307, 3315, 3317, 3308, 2885, 2893, 2895, 2886], [3315, 3316, 3318, 3317, 2893, 2894, 2896, 2895], [3316, 3245, 3244, 3318, 2894, 2823, 2822, 2896], [3244, 3240, 3319, 3318, 2822, 2818, 2897, 2896], [3240, 3236, 3320, 3319, 2818, 2814, 2898, 2897], [3236, 3232, 3293, 3320, 2814, 2810, 2871, 2898], [3318, 3319, 3321, 3317, 2896, 2897, 2899, 2895], [3319, 3320, 3322, 3321, 2897, 2898, 2900, 2899], [3320, 3293, 3294, 3322, 2898, 2871, 2872, 2900], [3317, 3321, 3304, 3308, 2895, 2899, 2882, 2886], [3321, 3322, 3300, 3304, 2899, 2900, 2878, 2882], [3322, 3294, 3295, 3300, 2900, 2872, 2873, 2878], [3267, 3323, 3324, 3268, 2845, 2901, 2902, 2846], [3323, 3325, 3326, 3324, 2901, 2903, 2904, 2902], [3325, 3327, 3328, 3326, 2903, 2905, 2906, 2904], [3327, 3329, 3330, 3328, 2905, 2907, 2908, 2906], [3329, 3331, 3332, 3330, 2907, 2909, 2910, 2908], [3331, 2955, 2958, 3332, 2909, 2533, 2536, 2910], [3268, 3324, 3333, 3274, 2846, 2902, 2911, 2852], [3324, 3326, 3334, 3333, 2902, 2904, 2912, 2911], [3326, 3328, 3335, 3334, 2904, 2906, 2913, 2912], [3328, 3330, 3336, 3335, 2906, 2908, 2914, 2913], [3330, 3332, 3337, 3336, 2908, 2910, 2915, 2914], [3332, 2958, 2972, 3337, 2910, 2536, 2550, 2915], [3274, 3333, 3338, 3280, 2852, 2911, 2916, 2858], [3333, 3334, 3339, 3338, 2911, 2912, 2917, 2916], [3334, 3335, 3340, 3339, 2912, 2913, 2918, 2917], [3335, 3336, 3341, 3340, 2913, 2914, 2919, 2918], [3336, 3337, 3342, 3341, 2914, 2915, 2920, 2919], [3337, 2972, 2980, 3342, 2915, 2550, 2558, 2920], [3280, 3338, 3343, 3286, 2858, 2916, 2921, 2864], [3338, 3339, 3344, 3343, 2916, 2917, 2922, 2921], [3339, 3340, 3345, 3344, 2917, 2918, 2923, 2922], [3340, 3341, 3346, 3345, 2918, 2919, 2924, 2923], [3341, 3342, 3347, 3346, 2919, 2920, 2925, 2924], [3342, 2980, 2988, 3347, 2920, 2558, 2566, 2925], [3286, 3343, 3348, 3292, 2864, 2921, 2926, 2870], [3343, 3344, 3349, 3348, 2921, 2922, 2927, 2926], [3344, 3345, 3350, 3349, 2922, 2923, 2928, 2927], [3345, 3346, 3351, 3350, 2923, 2924, 2929, 2928], [3346, 3347, 3352, 3351, 2924, 2925, 2930, 2929], [3347, 2988, 2996, 3352, 2925, 2566, 2574, 2930], [3292, 3348, 3353, 3298, 2870, 2926, 2931, 2876], [3348, 3349, 3354, 3353, 2926, 2927, 2932, 2931], [3349, 3350, 3355, 3354, 2927, 2928, 2933, 2932], [3350, 3351, 3356, 3355, 2928, 2929, 2934, 2933], [3351, 3352, 3357, 3356, 2929, 2930, 2935, 2934], [3352, 2996, 3004, 3357, 2930, 2574, 2582, 2935], [3355, 3356, 3358, 3359, 2933, 2934, 2936, 2937], [3356, 3357, 3360, 3358, 2934, 2935, 2938, 2936], [3357, 3004, 3041, 3360, 2935, 2582, 2619, 2938], [3359, 3358, 3361, 3362, 2937, 2936, 2939, 2940], [3358, 3360, 3363, 3361, 2936, 2938, 2941, 2939], [3360, 3041, 3039, 3363, 2938, 2619, 2617, 2941], [3362, 3361, 3364, 3365, 2940, 2939, 2942, 2943], [3361, 3363, 3366, 3364, 2939, 2941, 2944, 2942], [3363, 3039, 3038, 3366, 2941, 2617, 2616, 2944], [3038, 3034, 3367, 3366, 2616, 2612, 2945, 2944], [3034, 3030, 3368, 3367, 2612, 2608, 2946, 2945], [3030, 3029, 3313, 3368, 2608, 2607, 2891, 2946], [3366, 3367, 3369, 3364, 2944, 2945, 2947, 2942], [3367, 3368, 3370, 3369, 2945, 2946, 2948, 2947], [3368, 3313, 3311, 3370, 2946, 2891, 2889, 2948], [3364, 3369, 3371, 3365, 2942, 2947, 2949, 2943], [3369, 3370, 3372, 3371, 2947, 2948, 2950, 2949], [3370, 3311, 3310, 3372, 2948, 2889, 2888, 2950], [3310, 3306, 3373, 3372, 2888, 2884, 2951, 2950], [3306, 3302, 3374, 3373, 2884, 2880, 2952, 2951], [3302, 3298, 3353, 3374, 2880, 2876, 2931, 2952], [3372, 3373, 3375, 3371, 2950, 2951, 2953, 2949], [3373, 3374, 3376, 3375, 2951, 2952, 2954, 2953], [3374, 3353, 3354, 3376, 2952, 2931, 2932, 2954], [3371, 3375, 3362, 3365, 2949, 2953, 2940, 2943], [3375, 3376, 3359, 3362, 2953, 2954, 2937, 2940], [3376, 3354, 3355, 3359, 2954, 2932, 2933, 2937] ], dtype='int64') yt-project-yt-f043ac8/yt/frontends/stream/sample_data/tetrahedral_mesh.py000066400000000000000000003155501510711153200267530ustar00rootroot00000000000000import numpy as np # This is just an export from `pydistmesh` using the '3D Unit Ball' example: # https://github.com/bfroehle/pydistmesh _coordinates = np.array([ [ -9.62967621e-01, -2.68325669e-01, -2.63570990e-02], [ -9.23197206e-01, 6.00592270e-02, -3.79604808e-01], [ -9.99398840e-01, -3.01322522e-02, 1.71466163e-02], [ -9.23424144e-01, -7.55355896e-02, 3.76274134e-01], [ -9.76172900e-01, 2.13522703e-01, 3.86590740e-02], [ -7.73044456e-01, -5.39692475e-01, -3.33368116e-01], [ -7.87313888e-01, -5.98131409e-01, -1.49584956e-01], [ -7.74823044e-01, -6.03944560e-01, 1.86816007e-01], [ -8.02947422e-01, -3.76286648e-01, -4.62259448e-01], [ -9.01768086e-01, -3.68179163e-01, -2.26403233e-01], [ -8.75147083e-01, -4.83776676e-01, -8.81535636e-03], [ -9.02155201e-01, -3.89290298e-01, 1.85927562e-01], [ -7.89270641e-01, -4.87597303e-01, 3.73230123e-01], [ -7.52138168e-01, -2.22375528e-01, -6.20352562e-01], [ -8.95457206e-01, -1.92141852e-01, -4.01544395e-01], [ -9.71942256e-01, -1.23239288e-01, -2.00350515e-01], [ -8.05378938e-01, -2.23546612e-01, -1.51473404e-04], [ -9.66811578e-01, -1.69359673e-01, 1.91292119e-01], [ -8.70654663e-01, -2.82390171e-01, 4.02760786e-01], [ -7.11079737e-01, -4.28959611e-01, 5.57098968e-01], [ -7.88471049e-01, 1.78042094e-01, -5.88739685e-01], [ -8.39017123e-01, -2.86996919e-02, -5.43347582e-01], [ -7.35821715e-01, -2.11538194e-01, -2.33136439e-01], [ -7.85079983e-01, -4.35631443e-03, -7.29027161e-02], [ -7.92742715e-01, 5.30843614e-04, 1.49140404e-01], [ -8.17004797e-01, -5.47499959e-02, 5.74025783e-01], [ -7.04701668e-01, -2.24209163e-01, 6.73146203e-01], [ -7.70999764e-01, 3.71216019e-01, -5.17453410e-01], [ -8.90385164e-01, 2.68446994e-01, -3.67628170e-01], [ -9.79246646e-01, 1.15973457e-01, -1.66211200e-01], [ -7.72969304e-01, 2.21158909e-01, 4.35889641e-02], [ -9.70179581e-01, 8.97731254e-02, 2.25149656e-01], [ -8.86033779e-01, 1.44311425e-01, 4.40588645e-01], [ -7.57393407e-01, 1.57682547e-01, 6.33633524e-01], [ -8.20053173e-01, 4.86337456e-01, -3.01643287e-01], [ -9.21552627e-01, 3.65067689e-01, -1.32160273e-01], [ -8.88239706e-01, 4.53748187e-01, 7.17133752e-02], [ -9.12745738e-01, 3.24240717e-01, 2.48521981e-01], [ -8.08848385e-01, 3.57206222e-01, 4.67084580e-01], [ -8.02123077e-01, 5.90021489e-01, -9.20500458e-02], [ -7.45948377e-01, 6.57205497e-01, 1.07897884e-01], [ -7.98204994e-01, 5.25058631e-01, 2.95266357e-01], [ -6.30887505e-01, -7.39668679e-01, -2.34246028e-01], [ -7.15329115e-01, -6.98453892e-01, 2.15967334e-02], [ -5.77957725e-01, -7.92693457e-01, 1.93912223e-01], [ -6.42665949e-01, -5.94884286e-01, -4.82797228e-01], [ -5.46038557e-01, -5.44985033e-01, -2.71787655e-01], [ -5.54213927e-01, -5.84648463e-01, -5.10845856e-02], [ -5.45487961e-01, -5.77578168e-01, 2.00918083e-01], [ -6.46377284e-01, -6.53904664e-01, 3.93198547e-01], [ -6.38955694e-01, -4.18351960e-01, -6.45536411e-01], [ -5.99144091e-01, -3.63886456e-01, -4.05441933e-01], [ -6.82849084e-01, -3.86014000e-01, -1.63498157e-01], [ -6.68766881e-01, -4.19225500e-01, 5.42136051e-02], [ -7.30857178e-01, -2.03227666e-01, 2.06228100e-01], [ -5.43618891e-01, -4.30741083e-01, 4.45877772e-01], [ -5.58595071e-01, -4.21540710e-01, 7.14335339e-01], [ -5.82838566e-01, -2.03229852e-01, -7.86763518e-01], [ -5.09947711e-01, -2.26802295e-01, -5.80406785e-01], [ -6.57701812e-01, -1.38424779e-01, -4.27940410e-01], [ -6.07756412e-01, -1.72786293e-01, -3.54305445e-02], [ -5.06397150e-01, -2.73398545e-01, 1.14366953e-01], [ -6.62607234e-01, -3.85526543e-01, 2.68375874e-01], [ -7.05010419e-01, -2.21273740e-01, 4.52510033e-01], [ -4.93572234e-01, -2.48290925e-01, 5.93171798e-01], [ -5.30304677e-01, -1.91881131e-01, 8.25807836e-01], [ -6.95576819e-01, -1.18317597e-02, -7.18354299e-01], [ -5.87300531e-01, 6.42382159e-03, -5.97639073e-01], [ -6.73330053e-01, 1.22449487e-01, -4.02731110e-01], [ -5.59319757e-01, -1.73769322e-02, -2.09348254e-01], [ -5.95036090e-01, 6.63685740e-02, 3.03841620e-02], [ -5.57266536e-01, -5.60387817e-02, 1.93042065e-01], [ -7.00883535e-01, -6.04503771e-03, 3.64291599e-01], [ -5.78057568e-01, -3.71186623e-02, 5.64052628e-01], [ -6.47137348e-01, -2.91689448e-03, 7.62367854e-01], [ -6.17566835e-01, 2.14112793e-01, -7.56813660e-01], [ -5.39967650e-01, 2.26181937e-01, -5.56616845e-01], [ -7.72970264e-01, -6.67507940e-03, -2.79806814e-01], [ -6.13953196e-01, 1.78370120e-01, -1.61353511e-01], [ -5.29572241e-01, 2.85092966e-01, 5.07316309e-02], [ -5.74738903e-01, 1.74657307e-01, 2.42139593e-01], [ -7.56747405e-01, 2.10707702e-01, 2.69976416e-01], [ -6.31176071e-01, 1.83653152e-01, 4.84414633e-01], [ -5.87915999e-01, 2.24217289e-01, 7.77226727e-01], [ -6.28395851e-01, 4.19919168e-01, -6.54817950e-01], [ -6.42450027e-01, 3.31162532e-01, -3.28240646e-01], [ -8.01427779e-01, 2.08645036e-01, -1.92090617e-01], [ -6.90906413e-01, 3.97333130e-01, -9.61360295e-02], [ -6.72545573e-01, 4.10691484e-01, 1.45013988e-01], [ -6.04539604e-01, 3.87066026e-01, 3.57575500e-01], [ -6.68054353e-01, 3.84236522e-01, 6.37232827e-01], [ -6.75857115e-01, 5.72589964e-01, -4.64066691e-01], [ -5.59143406e-01, 5.23445316e-01, -2.54861670e-01], [ -5.63538680e-01, 5.59721915e-01, -3.14218279e-02], [ -6.39318728e-01, 6.99649299e-01, 3.19002231e-01], [ -6.64343258e-01, 5.64739973e-01, 4.89608822e-01], [ -6.86462131e-01, 6.83192131e-01, -2.49034648e-01], [ -6.20846808e-01, 7.80473026e-01, -7.35601686e-02], [ -5.64621392e-01, 8.16515778e-01, 1.20435325e-01], [ -4.95021894e-01, -7.59837405e-01, -4.21426672e-01], [ -4.11090255e-01, -8.80338747e-01, -2.36661137e-01], [ -5.47447534e-01, -8.36158957e-01, -3.37549521e-02], [ -3.66130310e-01, -9.09648278e-01, 1.96185134e-01], [ -4.62833735e-01, -7.92039672e-01, 3.98067949e-01], [ -4.89583007e-01, -6.02925047e-01, -6.29912587e-01], [ -3.80304973e-01, -6.03973036e-01, -4.02431008e-01], [ -3.91842972e-01, -7.11773257e-01, -1.85685509e-01], [ -3.84974499e-01, -7.08582426e-01, 3.83824107e-02], [ -3.40651461e-01, -6.72213220e-01, 2.28912670e-01], [ -3.84521145e-01, -5.86140658e-01, 4.14820856e-01], [ -5.31073959e-01, -6.17424650e-01, 5.80299277e-01], [ -4.59909273e-01, -3.95671027e-01, -7.94938928e-01], [ -3.98102385e-01, -4.19059333e-01, -5.61501051e-01], [ -3.81193299e-01, -3.93630978e-01, -3.56732489e-01], [ -4.77511655e-01, -3.75863014e-01, -1.26017536e-01], [ -3.87190527e-01, -4.74981994e-01, 5.57554008e-02], [ -4.21088239e-01, -3.96851005e-01, 2.59967897e-01], [ -3.08478859e-01, -3.41393062e-01, 4.32292613e-01], [ -3.52402545e-01, -4.36693481e-01, 5.99333058e-01], [ -3.88440519e-01, -3.70654859e-01, 8.43640290e-01], [ -3.89098848e-01, -1.77734623e-01, -9.03887433e-01], [ -3.06623610e-01, -2.35268920e-01, -7.00243202e-01], [ -3.41856207e-01, -1.73691252e-01, -4.53096469e-01], [ -4.95495891e-01, -2.09375496e-01, -2.66566326e-01], [ -3.58979116e-01, -2.38478707e-01, -6.00078514e-02], [ -3.61083064e-01, -1.51054689e-01, 2.47282244e-01], [ -5.22145140e-01, -2.02932177e-01, 3.68602647e-01], [ -2.88279144e-01, -2.58872203e-01, 7.15313501e-01], [ -3.30873242e-01, -1.47363870e-01, 9.32098057e-01], [ -5.21050518e-01, 2.37374153e-02, -8.53195695e-01], [ -3.96061295e-01, -3.80024812e-02, -6.94021762e-01], [ -4.64462964e-01, -1.24913007e-02, -4.30642267e-01], [ -3.26163230e-01, -3.91595358e-02, -2.32146758e-01], [ -4.06814958e-01, -5.71682613e-02, 2.94472862e-03], [ -3.74040953e-01, 1.08402262e-01, 1.74064052e-01], [ -4.51503692e-01, 2.09979658e-02, 4.07300559e-01], [ -3.92321513e-01, -4.58352294e-02, 7.04774801e-01], [ -4.56241586e-01, 5.07189584e-02, 8.88409366e-01], [ -4.25440817e-01, 2.32028056e-01, -8.74736014e-01], [ -2.99098712e-01, 2.14664654e-01, -7.35610261e-01], [ -3.56902556e-01, 1.27886858e-01, -5.76114828e-01], [ -4.38206907e-01, 1.84178546e-01, -3.37738082e-01], [ -4.01961192e-01, 1.38677100e-01, -9.82255735e-02], [ -4.11546738e-01, 3.58182028e-01, 2.47789091e-01], [ -4.07517555e-01, 2.08108781e-01, 4.18341689e-01], [ -4.51178528e-01, 1.49551301e-01, 6.51618844e-01], [ -3.72680102e-01, 2.63356951e-01, 8.89804843e-01], [ -4.64223868e-01, 4.23342694e-01, -7.77995607e-01], [ -3.64395973e-01, 4.03004411e-01, -6.01158644e-01], [ -5.12348000e-01, 4.07384566e-01, -4.58573380e-01], [ -4.44985738e-01, 3.51724152e-01, -1.50316028e-01], [ -3.86770739e-01, 4.64454002e-01, 5.02311117e-02], [ -5.32930370e-01, 5.75775886e-01, 2.03141645e-01], [ -2.66781216e-01, 4.13898117e-01, 4.30887385e-01], [ -4.85656110e-01, 3.71762124e-01, 5.38458720e-01], [ -4.68107747e-01, 4.18318830e-01, 7.78385826e-01], [ -4.97986898e-01, 6.13339175e-01, -6.13044946e-01], [ -3.86761727e-01, 5.93271383e-01, -4.03701051e-01], [ -4.45885002e-01, 7.19487703e-01, -1.83540323e-01], [ -3.94993635e-01, 7.04316770e-01, 5.57745148e-02], [ -4.34407638e-01, 8.12572050e-01, 2.97930026e-01], [ -4.34808796e-01, 5.61352642e-01, 4.02806351e-01], [ -5.03093415e-01, 5.81725526e-01, 6.39134124e-01], [ -5.36185759e-01, 7.42670990e-01, -4.01179053e-01], [ -3.78389380e-01, 8.72517067e-01, -3.09088084e-01], [ -4.08836317e-01, 9.09543790e-01, -7.47192069e-02], [ -3.45264752e-01, 9.26749050e-01, 1.48082571e-01], [ -4.65253738e-01, 7.44368236e-01, 4.79014498e-01], [ -3.49013019e-01, -9.36842287e-01, -2.27253547e-02], [ -3.14733849e-01, -7.41414880e-01, -5.92660594e-01], [ -2.74412512e-01, -8.66322161e-01, -4.17353194e-01], [ -1.92620923e-01, -9.60021017e-01, -2.03117769e-01], [ -1.82051552e-01, -7.61556292e-01, -8.36865303e-02], [ -1.51505552e-01, -9.87579969e-01, 4.16157811e-02], [ -2.54046269e-01, -8.75951727e-01, 4.10084217e-01], [ -3.15836068e-01, -7.42333328e-01, 5.90921999e-01], [ -3.16779695e-01, -5.65032826e-01, -7.61832353e-01], [ -1.99380736e-01, -5.83498966e-01, -5.17971409e-01], [ -1.93074469e-01, -7.27602781e-01, -3.13324024e-01], [ -3.07440769e-01, -5.34538758e-01, -1.59248646e-01], [ -1.83589817e-01, -5.63598366e-01, 3.99710449e-02], [ -1.72637366e-01, -7.87780365e-01, 1.41563414e-01], [ -1.66748215e-01, -7.11019457e-01, 3.88397712e-01], [ -1.79936519e-01, -5.56267314e-01, 5.46800560e-01], [ -3.58182261e-01, -5.78742564e-01, 7.32640780e-01], [ -2.39113604e-01, -3.63405744e-01, -9.00422651e-01], [ -1.88667420e-01, -4.25310126e-01, -6.68457429e-01], [ -1.64032518e-01, -4.92768758e-01, -3.23434511e-01], [ -1.42286771e-01, -3.78405111e-01, -8.11029046e-02], [ -2.44971665e-01, -3.13503503e-01, 1.06085864e-01], [ -2.28610813e-01, -4.85183904e-01, 2.66130473e-01], [ -1.28977866e-01, -3.13541733e-01, 5.70211381e-01], [ -1.78031279e-01, -4.65204651e-01, 7.92395828e-01], [ -1.72505832e-01, -3.19168435e-01, 9.31865467e-01], [ -1.71515483e-01, -1.59643018e-01, -9.72160762e-01], [ -1.77643190e-01, -3.20496856e-01, -4.92602235e-01], [ -1.14229852e-01, -1.49074465e-01, -3.42747649e-01], [ -2.52795680e-01, -2.86475470e-01, -2.49110854e-01], [ -1.74693523e-01, -1.40098697e-01, -8.60454071e-02], [ -1.66194986e-01, -2.46030595e-01, 3.10073251e-01], [ -3.13732125e-01, -1.40675034e-01, 5.06100644e-01], [ -1.74134649e-01, -1.01886625e-01, 7.32556325e-01], [ -9.95450127e-02, -1.14585341e-01, 9.88413370e-01], [ -3.18739089e-01, 4.00091666e-02, -9.46997709e-01], [ -1.93708219e-01, 1.00722710e-02, -7.78634557e-01], [ -1.82613620e-01, -7.75287419e-02, -5.72815481e-01], [ -2.28815875e-01, 6.25534005e-02, -4.05248137e-01], [ -9.94383439e-02, 2.68669966e-02, -2.20864387e-01], [ -2.07094208e-01, 9.82448007e-02, -3.77870585e-02], [ -2.15852843e-01, -8.17490291e-02, 1.15598164e-01], [ -2.30191010e-01, -1.75544371e-04, 3.44118680e-01], [ -2.35116145e-01, 7.71682150e-02, 5.52860241e-01], [ -2.08185096e-01, 1.20081075e-01, 7.63698137e-01], [ -2.40651767e-01, 5.78343201e-02, 9.68886948e-01], [ -1.93731688e-01, 2.45702312e-01, -9.49788612e-01], [ -1.12016933e-01, 1.63531041e-01, -5.86901497e-01], [ -2.34602187e-01, 2.94459202e-01, -4.73486651e-01], [ -2.26208974e-01, 2.23166687e-01, -2.41358207e-01], [ -2.85908624e-01, 2.71530866e-01, 5.11117105e-02], [ -1.34482864e-01, 1.24402789e-01, 1.86580767e-01], [ -2.19932943e-01, 2.26626105e-01, 3.55951824e-01], [ -3.01290816e-01, 3.03227979e-01, 6.38886854e-01], [ -1.64692179e-01, 2.77015285e-01, 9.46646195e-01], [ -2.62930799e-01, 4.35815525e-01, -8.60774199e-01], [ -1.45572649e-01, 3.98542626e-01, -6.84093735e-01], [ -2.07918055e-01, 5.55368124e-01, -5.18712780e-01], [ -3.42891725e-01, 4.03334468e-01, -3.28874903e-01], [ -1.88513526e-01, 3.73927074e-01, -1.06486483e-01], [ -1.73582211e-01, 3.83676674e-01, 1.80808854e-01], [ -1.14880644e-01, 4.96441733e-01, 5.98104183e-01], [ -1.47311184e-01, 3.54067258e-01, 7.64393318e-01], [ -2.55934339e-01, 4.92690346e-01, 8.31717402e-01], [ -3.03477171e-01, 6.16396048e-01, -7.26606853e-01], [ -3.37997757e-01, 7.73558160e-01, -5.36064633e-01], [ -2.26044087e-01, 7.08169734e-01, -3.04701855e-01], [ -3.23533824e-01, 5.57868526e-01, -1.56651270e-01], [ -1.95521147e-01, 5.66863051e-01, 3.43378062e-02], [ -2.94013176e-01, 5.82311319e-01, 2.45762618e-01], [ -1.97076732e-01, 6.60820318e-01, 4.16455346e-01], [ -3.15119586e-01, 5.41216314e-01, 6.21821689e-01], [ -1.35031842e-01, 6.70227569e-01, 7.29768050e-01], [ -1.30922603e-01, 7.63763696e-01, -6.32079337e-01], [ -1.69367064e-01, 8.85500714e-01, -4.32669948e-01], [ -1.88284600e-01, 9.58082704e-01, -2.15931571e-01], [ -2.15357169e-01, 7.70995955e-01, -7.15664769e-02], [ -1.19113529e-01, 9.69485318e-01, 2.14266624e-01], [ -2.50862111e-01, 8.84373013e-01, 3.93640159e-01], [ -3.03000063e-01, 7.42360357e-01, 5.97571806e-01], [ -1.87291251e-01, 9.82304419e-01, -1.28846379e-04], [ -3.84532991e-02, -9.31861848e-01, -3.60769787e-01], [ 1.44520011e-02, -9.93720630e-01, -1.10952461e-01], [ -1.26363945e-01, -9.56072126e-01, 2.64496206e-01], [ -1.19844694e-01, -6.78852423e-01, -7.24428491e-01], [ -8.69666571e-02, -8.24194281e-01, -5.59589661e-01], [ 1.92508773e-02, -7.86178086e-01, -1.99792242e-01], [ 2.59736870e-02, -8.16027980e-01, 3.21822222e-02], [ 5.78263480e-02, -7.77061434e-01, 2.69277400e-01], [ 8.48713263e-03, -9.03365175e-01, 4.28788212e-01], [ -8.12034498e-02, -8.01090245e-01, 5.93009629e-01], [ -9.44555528e-02, -5.11948311e-01, -8.53807400e-01], [ 1.22227825e-02, -5.84779056e-01, -6.17069967e-01], [ 1.14282616e-02, -6.77557927e-01, -4.03044568e-01], [ -4.94206399e-02, -5.79359611e-01, -1.75221047e-01], [ 3.54180438e-02, -6.16893928e-01, 5.76597508e-02], [ -7.35585843e-02, -6.27256660e-01, 2.41870856e-01], [ 4.69704885e-02, -6.49539057e-01, 4.72840371e-01], [ 9.18934804e-02, -6.86719418e-01, 7.21090861e-01], [ -1.25360666e-01, -6.52218717e-01, 7.47593104e-01], [ 1.10570869e-01, -5.47391887e-01, -8.29539755e-01], [ 3.39626961e-03, -3.66635006e-01, -6.93343934e-01], [ 1.25738074e-02, -4.53904798e-01, -4.69638034e-01], [ 7.03185078e-02, -4.26921601e-01, -6.79651552e-02], [ -2.81962041e-02, -4.08640743e-01, 1.57174041e-01], [ 1.29299577e-01, -5.28129998e-01, 2.68603445e-01], [ -6.51159350e-02, -4.55882802e-01, 4.04884494e-01], [ 2.36123834e-02, -4.72139675e-01, 6.39529107e-01], [ 2.46523286e-02, -4.80995012e-01, 8.76376666e-01], [ 4.97649308e-02, -1.37259058e-01, -9.89284288e-01], [ 2.42859800e-02, -3.39666980e-01, -9.40232171e-01], [ -9.69668126e-02, -1.99263737e-01, -7.68274355e-01], [ 2.08525613e-02, -2.14097485e-01, -5.31519023e-01], [ -1.07013915e-03, -3.39815934e-01, -2.93721956e-01], [ 1.57139516e-02, -1.92449444e-01, -1.42390379e-01], [ -2.04197400e-02, -2.22306197e-01, 9.04305303e-02], [ 3.87266848e-02, -2.76080684e-01, 3.60076717e-01], [ 9.31376446e-02, -2.13251477e-01, 5.74822958e-01], [ -2.06942363e-02, -2.61315125e-01, 7.64083562e-01], [ 5.35699517e-02, -2.57293791e-01, 9.64847224e-01], [ -8.66763821e-02, 5.63452713e-02, -9.94641853e-01], [ 4.50561348e-02, -7.41066799e-03, -8.47406496e-01], [ 3.33458782e-02, -1.14143341e-02, -6.60235249e-01], [ 6.84339708e-03, 8.90210227e-03, -4.41613112e-01], [ 9.49578088e-02, 4.46302002e-02, -2.10962018e-01], [ -6.47323444e-03, 4.23441467e-03, 1.11262491e-02], [ -2.20508593e-02, -7.44769302e-02, 2.50509251e-01], [ -8.08824268e-02, -1.12803625e-01, 4.93814502e-01], [ 4.31118364e-03, 2.66036252e-02, 6.39348740e-01], [ -1.51603188e-03, -1.98988230e-02, 8.37604363e-01], [ -9.23935066e-03, 1.09286102e-01, 9.93967395e-01], [ 5.63301619e-02, 2.24876724e-01, -9.72757613e-01], [ -4.61848355e-02, 2.04133531e-01, -7.79695854e-01], [ -3.96552100e-03, 3.80190538e-01, -4.97582997e-01], [ -1.18507316e-02, 2.17673333e-01, -3.61334139e-01], [ -6.02985784e-03, 2.21576339e-01, -1.22196991e-01], [ -2.32241591e-02, 2.50647992e-01, 6.38152715e-02], [ -6.63051411e-03, 2.68706951e-01, 3.21898185e-01], [ -8.58651811e-03, 8.43630474e-02, 4.22202755e-01], [ -8.42376448e-02, 2.59602722e-01, 5.58129856e-01], [ 2.57323327e-02, 2.12104235e-01, 7.70285538e-01], [ 3.52746047e-02, 3.45035878e-01, 9.37926407e-01], [ -2.97723438e-02, 4.24183729e-01, -9.05086610e-01], [ 9.29712760e-02, 3.76707117e-01, -7.06489067e-01], [ -1.40981487e-01, 4.61266202e-01, -3.29987153e-01], [ 4.92055611e-02, 3.91387717e-01, -2.35631121e-01], [ -1.97462635e-04, 4.51544969e-01, 6.47824769e-03], [ 1.23653883e-01, 3.67736045e-01, 2.05364992e-01], [ -7.63551949e-02, 4.59397098e-01, 3.72865094e-01], [ 6.82176296e-02, 4.37151216e-01, 6.84436118e-01], [ 2.66618242e-04, 5.35613256e-01, 8.44463362e-01], [ -8.06612085e-02, 6.06532435e-01, -7.90956493e-01], [ 7.59431774e-03, 5.76466083e-01, -5.91739541e-01], [ -4.20662344e-02, 6.92842103e-01, -4.04895589e-01], [ -7.22163505e-02, 5.86341525e-01, -1.76197982e-01], [ -2.82929006e-03, 6.85092763e-01, 3.33712489e-02], [ -3.54501018e-02, 5.73604344e-01, 2.16391791e-01], [ 8.87977875e-03, 7.54111200e-01, 3.05826038e-01], [ 2.83834106e-02, 6.24014787e-01, 5.06040338e-01], [ 9.15391011e-02, 7.01376458e-01, 7.06888717e-01], [ 1.19416186e-01, 7.35331387e-01, -6.67103835e-01], [ 5.68688791e-02, 8.54595574e-01, -5.16170841e-01], [ -1.16699743e-02, 7.89916321e-01, -1.85148337e-01], [ -6.01400665e-03, 8.69990193e-01, 6.23534747e-02], [ -1.95727679e-01, 7.64511507e-01, 1.81280060e-01], [ 1.20789453e-01, 8.40490066e-01, 5.28191591e-01], [ -9.39626086e-02, 8.20506401e-01, 5.63861931e-01], [ 2.81148104e-02, 9.43729028e-01, -3.29522502e-01], [ 3.41125444e-02, 9.95068116e-01, -9.31438613e-02], [ -2.14483362e-03, 9.25392070e-01, 3.79005168e-01], [ 2.18112689e-01, -9.74821369e-01, -4.63697576e-02], [ 1.23853444e-01, -7.28502092e-01, -6.73754426e-01], [ 1.44704613e-01, -8.58537380e-01, -4.91908673e-01], [ 1.87781455e-01, -9.41233073e-01, -2.80746195e-01], [ 2.31410729e-01, -8.07540207e-01, -8.25767126e-02], [ 6.97170007e-02, -9.84450226e-01, 1.61236758e-01], [ 2.30393023e-01, -9.04286027e-01, 3.59424311e-01], [ 1.89807547e-01, -8.03635809e-01, 5.64041295e-01], [ 3.22755516e-01, -5.86935920e-01, -7.42519430e-01], [ 3.31597344e-01, -7.46740775e-01, -5.76559985e-01], [ 2.27776250e-01, -7.23370447e-01, -3.10605531e-01], [ 1.63795538e-01, -5.17366379e-01, -2.81379193e-01], [ 1.78823602e-01, -6.28896291e-01, -8.64287065e-02], [ 2.31860993e-01, -7.39739895e-01, 1.49715345e-01], [ 2.69030761e-01, -6.65754968e-01, 3.56967997e-01], [ 2.50134311e-01, -5.68320717e-01, 5.65130746e-01], [ 2.21765258e-01, -5.48596353e-01, 8.06140318e-01], [ 2.66915440e-01, -3.83657939e-01, -8.84060368e-01], [ 2.01519428e-01, -4.30434869e-01, -6.53813689e-01], [ 2.27114000e-01, -5.81622952e-01, -4.95126812e-01], [ 2.26971538e-01, -3.11986319e-01, -2.06952935e-01], [ 2.66317379e-01, -5.03016563e-01, 1.08925528e-01], [ 4.45366001e-01, -6.19390316e-01, 2.11676959e-01], [ 1.75225358e-01, -4.29561039e-01, 4.56919428e-01], [ 2.04330462e-01, -3.60931919e-01, 6.92428501e-01], [ 2.28824041e-01, -3.32760745e-01, 9.14827768e-01], [ 2.48765341e-01, -1.65057236e-01, -9.54396099e-01], [ 1.39387281e-01, -2.14073764e-01, -7.55554463e-01], [ 1.97279259e-01, -3.34597766e-01, -4.57795165e-01], [ 1.55494033e-01, -1.36566595e-01, -3.29815444e-01], [ 1.69940802e-01, -3.12986488e-01, 2.72903460e-02], [ 1.88913617e-01, -2.97118516e-01, 2.31933019e-01], [ 2.83041605e-01, -2.43544619e-01, 4.58124190e-01], [ 1.86156324e-01, -1.28793574e-01, 7.73894896e-01], [ 1.72667984e-01, -7.32071974e-02, 9.82255808e-01], [ 4.12455593e-01, 1.47398934e-02, -9.10858452e-01], [ 2.69311185e-01, -2.87406987e-02, -7.57067959e-01], [ 2.19557754e-01, -8.59316259e-02, -5.45649375e-01], [ 2.16518363e-01, 8.98099440e-02, -3.96642521e-01], [ 1.94983213e-01, -1.03663895e-01, -8.13730923e-02], [ 1.83294039e-01, -8.80651206e-02, 1.23569725e-01], [ 1.07922312e-01, 1.15031462e-01, 1.92321541e-01], [ 1.65118717e-01, -7.17364718e-02, 3.60784649e-01], [ 2.09732400e-01, -1.20997873e-02, 5.59122842e-01], [ 2.18098013e-01, 9.38478667e-02, 7.75124598e-01], [ 3.73543318e-01, 5.17586579e-03, 9.27598297e-01], [ 2.05717715e-01, 7.53314927e-02, -9.75707634e-01], [ 1.17345035e-01, 1.84580292e-01, -5.76077495e-01], [ 2.25102402e-01, 3.27630607e-01, -4.35006763e-01], [ 2.22753607e-01, 2.46290020e-01, -2.26657613e-01], [ 1.97348236e-01, 1.22204372e-01, -5.27994103e-03], [ 3.15010615e-01, 2.58654930e-01, 1.42312814e-01], [ 2.09783256e-01, 1.67156009e-01, 3.59093347e-01], [ 1.63202711e-01, 2.05291525e-01, 5.64763439e-01], [ 1.97878214e-01, 1.73604975e-01, 9.64730805e-01], [ 2.05583723e-01, 3.68622696e-01, -9.06560886e-01], [ 1.99505685e-01, 1.68317631e-01, -7.68578105e-01], [ 1.02120411e-01, 5.44777800e-01, -3.74107242e-01], [ 2.91304671e-01, 4.63829103e-01, -2.65149160e-01], [ 1.92393602e-01, 3.56306387e-01, -3.61651193e-02], [ 1.66221667e-01, 5.40222401e-01, 3.16116237e-01], [ 1.03192068e-01, 3.84500538e-01, 4.76252225e-01], [ 2.40927417e-01, 3.27308102e-01, 7.05589375e-01], [ 2.22868825e-01, 4.06677398e-01, 8.85970079e-01], [ 1.51856985e-01, 5.73133196e-01, -8.05268773e-01], [ 2.24221571e-01, 5.13573162e-01, -5.71311460e-01], [ 1.76465420e-01, 7.38746010e-01, -3.11570971e-01], [ 1.57586884e-01, 5.83668501e-01, -1.32718527e-01], [ 1.93245363e-01, 5.67332754e-01, 9.12964174e-02], [ 1.82472617e-01, 7.64831299e-01, 1.71349495e-01], [ 2.37303191e-01, 6.80016753e-01, 4.01108365e-01], [ 2.50808507e-01, 5.30577516e-01, 5.61711743e-01], [ 2.60139071e-01, 5.85769469e-01, 7.67594812e-01], [ 3.37133701e-01, 6.84372138e-01, -6.46510358e-01], [ 2.67125222e-01, 8.34839493e-01, -4.81338901e-01], [ 2.24773284e-01, 9.44894725e-01, -2.38014561e-01], [ 1.93469895e-01, 7.85372670e-01, -6.87807929e-02], [ 1.28005405e-01, 9.72510108e-01, 1.94521731e-01], [ 2.59586513e-01, 8.95602508e-01, 3.61263047e-01], [ 3.03673017e-01, 7.51297047e-01, 5.85948332e-01], [ 2.38520764e-01, 9.71027131e-01, 1.46341398e-02], [ 3.84074854e-01, -8.31551082e-01, -4.01259647e-01], [ 4.13923711e-01, -8.89137254e-01, -1.95197607e-01], [ 4.53213495e-01, -8.91167829e-01, 2.04310780e-02], [ 2.81856189e-01, -9.48065186e-01, 1.47409267e-01], [ 4.10774439e-01, -7.72051309e-01, 4.84975398e-01], [ 5.07299892e-01, -6.19496981e-01, -5.99057852e-01], [ 4.31433334e-01, -5.95575682e-01, -3.92692715e-01], [ 4.08083088e-01, -6.51386299e-01, -1.87980140e-01], [ 4.02323355e-01, -7.02571172e-01, 1.91533619e-02], [ 4.44254565e-01, -8.56088417e-01, 2.64103208e-01], [ 4.65500718e-01, -5.51798409e-01, 4.34918703e-01], [ 3.82917700e-01, -6.42536357e-01, 6.63717609e-01], [ 4.83513927e-01, -4.23010976e-01, -7.66339348e-01], [ 4.17627918e-01, -4.27370036e-01, -5.50514685e-01], [ 3.77960799e-01, -4.21545760e-01, -3.12935201e-01], [ 5.01986263e-01, -3.72801757e-01, -1.34702273e-01], [ 3.35515185e-01, -4.74157052e-01, -7.82543895e-02], [ 4.86118021e-01, -3.67092357e-01, 1.16359356e-01], [ 3.61077213e-01, -4.21637977e-01, 3.08204120e-01], [ 4.16969173e-01, -3.85761421e-01, 5.58811541e-01], [ 4.16529017e-01, -4.37785083e-01, 7.96773368e-01], [ 4.48997766e-01, -2.11045782e-01, -8.68251510e-01], [ 3.35477205e-01, -2.61430553e-01, -6.74970497e-01], [ 3.66362329e-01, -2.22560833e-01, -4.34081998e-01], [ 3.74108151e-01, -1.71647694e-01, -2.03504673e-01], [ 3.57819189e-01, -2.49803706e-01, 2.19688702e-02], [ 3.61600941e-01, -1.66483260e-01, 2.28496360e-01], [ 5.05691199e-01, -2.54501047e-01, 3.39829791e-01], [ 3.62776630e-01, -2.09215076e-01, 6.75053746e-01], [ 3.95673292e-01, -2.08362685e-01, 8.94442640e-01], [ 6.03722991e-01, -2.78474122e-02, -7.96707646e-01], [ 4.40265013e-01, -7.86809664e-02, -6.55722319e-01], [ 4.39918700e-01, -2.76851361e-02, -4.13347503e-01], [ 3.29102633e-01, 4.00043008e-02, -2.11496211e-01], [ 4.05774419e-01, -2.14033991e-02, 4.17895530e-03], [ 3.43817328e-01, 5.31430401e-02, 2.18404731e-01], [ 3.96753464e-01, -6.61189080e-02, 4.47365482e-01], [ 4.06048426e-01, -1.47617094e-02, 6.86995863e-01], [ 5.44822069e-01, 1.06719344e-01, 8.31733066e-01], [ 3.75600036e-01, 2.54440923e-01, -8.91170258e-01], [ 4.63684484e-01, 1.47006457e-01, -7.51528889e-01], [ 3.45634061e-01, 1.25786727e-01, -5.64749471e-01], [ 4.33771745e-01, 2.04274120e-01, -3.49729146e-01], [ 3.89430106e-01, 2.08809630e-01, -6.30142458e-02], [ 4.89357046e-01, 2.47087213e-01, 2.84235835e-01], [ 3.90697387e-01, 1.41030133e-01, 4.63836968e-01], [ 4.12857161e-01, 1.92822910e-01, 6.59096000e-01], [ 3.96276758e-01, 2.53444744e-01, 8.82457077e-01], [ 3.61150908e-01, 4.97243556e-01, -7.88871895e-01], [ 3.26489787e-01, 3.30981779e-01, -6.53409650e-01], [ 4.51588202e-01, 4.29381991e-01, -4.71925699e-01], [ 4.56266502e-01, 3.74805015e-01, -2.06872348e-01], [ 3.68187497e-01, 4.73294103e-01, -2.70325319e-02], [ 3.71244032e-01, 4.52825679e-01, 2.00185303e-01], [ 3.04632697e-01, 3.52977527e-01, 3.88234862e-01], [ 4.14725316e-01, 3.85903492e-01, 5.70635473e-01], [ 4.45573712e-01, 4.55986081e-01, 7.70415968e-01], [ 5.29294460e-01, 5.68004268e-01, -6.30252748e-01], [ 3.36555342e-01, 6.17472970e-01, -4.19001181e-01], [ 3.79508522e-01, 6.72901111e-01, -1.89083677e-01], [ 3.79210104e-01, 7.03099639e-01, 2.89663510e-02], [ 3.71716774e-01, 6.80534148e-01, 2.44314680e-01], [ 4.55066493e-01, 5.31958561e-01, 4.03363561e-01], [ 4.74304364e-01, 6.32321249e-01, 6.12539966e-01], [ 5.09305522e-01, 7.26531737e-01, -4.61258627e-01], [ 4.14769083e-01, 8.52822936e-01, -3.17269046e-01], [ 4.18775018e-01, 9.02581503e-01, -9.98704997e-02], [ 3.94632869e-01, 9.03066896e-01, 1.69514249e-01], [ 4.61852907e-01, 7.86451245e-01, 4.10105270e-01], [ 5.78765894e-01, -7.48034418e-01, -3.24768457e-01], [ 6.22385024e-01, -7.74420626e-01, -1.13620313e-01], [ 6.24238372e-01, -7.71683267e-01, 1.21784198e-01], [ 6.66471014e-01, -5.96428244e-01, -4.47313912e-01], [ 6.01827871e-01, -5.26836192e-01, -2.07474643e-01], [ 5.59753905e-01, -5.67596319e-01, 1.41632354e-02], [ 6.15626467e-01, -4.74253980e-01, 2.38852068e-01], [ 5.96741406e-01, -7.18050370e-01, 3.58194584e-01], [ 6.64541504e-01, -4.34981561e-01, -6.07598249e-01], [ 5.94037691e-01, -3.84380270e-01, -3.94164624e-01], [ 7.37730601e-01, -3.09096499e-01, -2.03560626e-01], [ 7.00473230e-01, -3.99470459e-01, 7.71399518e-03], [ 7.25721545e-01, -2.69961817e-01, 2.12956514e-01], [ 6.25103528e-01, -3.50378404e-01, 4.31830925e-01], [ 5.75160121e-01, -5.28839768e-01, 6.24114841e-01], [ 6.37504952e-01, -2.38247168e-01, -7.32683917e-01], [ 5.56308954e-01, -2.19302319e-01, -5.54146677e-01], [ 6.91335007e-01, -1.31863121e-01, -4.04903216e-01], [ 5.50450679e-01, -2.16461343e-01, -2.95913339e-01], [ 5.76819434e-01, -1.89855295e-01, -6.06792724e-02], [ 5.68002996e-01, -1.30984565e-01, 1.46840964e-01], [ 7.05672069e-01, -1.11125915e-01, 3.88609961e-01], [ 5.53006773e-01, -1.71911867e-01, 5.61461544e-01], [ 5.86529417e-01, -3.17786124e-01, 7.44980015e-01], [ 6.75938804e-01, 1.80420223e-01, -7.14531509e-01], [ 5.94310637e-01, 3.60783005e-02, -5.54992825e-01], [ 6.74403271e-01, 9.99821774e-02, -3.80833274e-01], [ 5.47740687e-01, 2.16242697e-03, -2.17769258e-01], [ 6.52781177e-01, 1.98788373e-02, -2.04197681e-02], [ 5.43580756e-01, 6.80793881e-03, 3.12214905e-01], [ 5.94453493e-01, 5.82306725e-02, 5.52769782e-01], [ 7.05676016e-01, 8.06852878e-02, 7.03925597e-01], [ 5.80128414e-01, -1.09106402e-01, 8.07184499e-01], [ 5.42599917e-01, 3.68797555e-01, -7.54701063e-01], [ 5.43213272e-01, 2.48965238e-01, -5.43315884e-01], [ 7.72945716e-01, 1.57594270e-01, -2.09995542e-01], [ 5.70040110e-01, 1.93374118e-01, -1.49210043e-01], [ 5.37514570e-01, 3.43582877e-01, 5.02783860e-02], [ 5.49305756e-01, 1.26433159e-01, 1.10900907e-01], [ 7.04438426e-01, 1.39590407e-01, 3.72156642e-01], [ 5.76275599e-01, 2.97362798e-01, 4.81580372e-01], [ 6.07520197e-01, 3.02951102e-01, 7.34261425e-01], [ 6.99631576e-01, 4.05751597e-01, -5.88116740e-01], [ 6.49674674e-01, 3.24746847e-01, -3.61182477e-01], [ 6.90837315e-01, 3.92507432e-01, -1.56065623e-01], [ 7.07297492e-01, 4.39430221e-01, 7.45724157e-02], [ 7.34624861e-01, 2.33302182e-01, 1.97568405e-01], [ 6.20925127e-01, 4.17105125e-01, 2.91445572e-01], [ 6.39732628e-01, 4.85274250e-01, 5.96029419e-01], [ 6.84957031e-01, 5.81126866e-01, -4.39460386e-01], [ 5.42010952e-01, 5.44683144e-01, -3.00708180e-01], [ 5.65520586e-01, 5.71231502e-01, -7.77148654e-02], [ 5.42658377e-01, 5.80522401e-01, 1.50091398e-01], [ 6.45989977e-01, 6.39765095e-01, 4.16410344e-01], [ 6.11253189e-01, 7.58595339e-01, -2.25616158e-01], [ 5.69866140e-01, 8.21707530e-01, 7.02263904e-03], [ 5.96124408e-01, 7.72064856e-01, 2.20344163e-01], [ 7.67799046e-01, -5.95832390e-01, -2.35517280e-01], [ 7.81073661e-01, -6.23989069e-01, -2.36976405e-02], [ 7.54107633e-01, -6.22427589e-01, 2.09536569e-01], [ 8.15394602e-01, -4.29633749e-01, -3.88003204e-01], [ 8.95007919e-01, -4.24199922e-01, -1.37895800e-01], [ 8.74435840e-01, -4.68344016e-01, 1.26553719e-01], [ 8.44167782e-01, -4.13217508e-01, 3.41514343e-01], [ 7.04143218e-01, -5.56758821e-01, 4.40683494e-01], [ 7.99734351e-01, -2.56440289e-01, -5.42829021e-01], [ 9.12597668e-01, -2.34115328e-01, -3.35194734e-01], [ 9.73305104e-01, -1.91998325e-01, -1.25753000e-01], [ 7.84069900e-01, -1.86036000e-01, 1.64746740e-02], [ 9.45975754e-01, -2.08962721e-01, 2.47920258e-01], [ 8.65952407e-01, -2.10822307e-01, 4.53519992e-01], [ 7.36999230e-01, -3.54489940e-01, 5.75472865e-01], [ 7.69123269e-01, -4.39753477e-02, -6.37585733e-01], [ 8.89753514e-01, -2.12586220e-02, -4.55946002e-01], [ 7.61925948e-01, -8.17295281e-02, -2.12584521e-01], [ 8.66579005e-01, 1.98271619e-02, -2.65740411e-02], [ 7.71523404e-01, -7.95716890e-03, 1.88624841e-01], [ 8.49571672e-01, 3.23679601e-02, 5.26479144e-01], [ 7.49529893e-01, -1.37902806e-01, 6.47447107e-01], [ 8.13235532e-01, 2.00148039e-01, -5.46432733e-01], [ 9.17920585e-01, 1.93823705e-01, -3.46199610e-01], [ 9.83931066e-01, 1.52608447e-01, -9.26839775e-02], [ 7.63532804e-01, 2.26521101e-01, 5.58254364e-04], [ 9.73502133e-01, 1.58275744e-01, 1.65052678e-01], [ 8.97439314e-01, 2.27709113e-01, 3.77824349e-01], [ 7.73925499e-01, 2.60018372e-01, 5.77433777e-01], [ 8.30247868e-01, 4.04004588e-01, -3.84016628e-01], [ 9.13121193e-01, 3.66274908e-01, -1.79031780e-01], [ 8.53887365e-01, 5.20387043e-01, -8.58448400e-03], [ 8.84701271e-01, 4.04948543e-01, 2.30911967e-01], [ 7.88854593e-01, 4.39735361e-01, 4.29349790e-01], [ 7.78387183e-01, 5.87137721e-01, -2.22222165e-01], [ 7.24110054e-01, 6.89672471e-01, 4.06359182e-03], [ 7.67279635e-01, 6.00429434e-01, 2.25314127e-01], [ 9.50421204e-01, -3.05112721e-01, 6.00480158e-02], [ 9.68789916e-01, -7.78690666e-03, -2.47760899e-01], [ 9.94841187e-01, -5.83791196e-02, 8.29632029e-02], [ 9.43181204e-01, -1.36099283e-03, 3.32276037e-01], [ 9.43413869e-01, 3.28538485e-01, 4.50858739e-02] ]) _connectivity = np.array([ [ 95, 162, 167, 161], [239, 162, 167, 161], [342, 420, 427, 421], [273, 264, 272, 263], [180, 264, 272, 263], [243, 164, 234, 242], [243, 330, 234, 242], [190, 180, 264, 272], [238, 160, 167, 161], [522, 521, 530, 469], [522, 521, 468, 469], [ 94, 95, 167, 161], [ 94, 160, 167, 161], [ 94, 160, 98, 167], [466, 529, 465, 457], [466, 529, 465, 475], [474, 529, 465, 475], [474, 464, 465, 391], [398, 408, 409, 481], [398, 408, 409, 326], [398, 325, 408, 326], [153, 238, 239, 161], [ 1, 77, 15, 14], [ 1, 21, 77, 14], [335, 243, 330, 242], [335, 330, 413, 404], [335, 330, 413, 336], [335, 243, 330, 336], [148, 156, 147, 232], [476, 521, 530, 469], [476, 521, 468, 469], [476, 477, 483, 469], [476, 477, 483, 411], [585, 527, 564, 572], [574, 579, 532, 575], [412, 484, 413, 404], [412, 335, 413, 404], [412, 477, 484, 404], [412, 477, 483, 411], [412, 477, 484, 483], [ 92, 93, 235, 158], [ 92, 93, 235, 150], [ 92, 87, 93, 150], [ 92, 87, 93, 39], [244, 243, 330, 234], [244, 235, 234, 158], [244, 243, 164, 234], [244, 165, 243, 164], [244, 164, 234, 158], [244, 165, 164, 158], [ 89, 94, 41, 95], [237, 238, 160, 161], [237, 153, 238, 161], [237, 153, 228, 316], [237, 153, 238, 316], [ 35, 87, 39, 36], [ 30, 35, 4, 36], [ 30, 35, 87, 36], [ 30, 78, 23, 70], [ 96, 92, 163, 91], [ 96, 92, 163, 158], [ 96, 92, 93, 158], [ 96, 92, 93, 39], [233, 164, 234, 242], [254, 262, 263, 255], [248, 244, 165, 243], [248, 330, 331, 336], [248, 244, 330, 331], [248, 243, 330, 336], [248, 244, 243, 330], [536, 474, 529, 475], [334, 238, 325, 326], [460, 522, 468, 469], [534, 527, 578, 572], [534, 527, 564, 572], [534, 527, 564, 526], [534, 564, 526, 516], [573, 527, 578, 572], [381, 464, 390, 391], [381, 464, 465, 391], [381, 382, 465, 391], [306, 381, 390, 391], [528, 536, 474, 481], [528, 573, 527, 578], [528, 573, 536, 578], [528, 474, 464, 465], [528, 536, 474, 529], [528, 573, 536, 529], [528, 474, 529, 465], [399, 398, 326, 316], [399, 398, 409, 326], [399, 317, 307, 391], [399, 317, 409, 326], [221, 231, 230, 239], [359, 273, 272, 263], [359, 369, 273, 272], [ 16, 23, 15, 2], [123, 197, 122, 132], [ 76, 148, 216, 149], [ 76, 148, 216, 140], [ 84, 76, 27, 149], [ 84, 27, 91, 149], [ 84, 156, 91, 149], [ 84, 148, 156, 149], [ 84, 148, 156, 147], [ 84, 76, 148, 149], [ 84, 76, 148, 147], [ 84, 76, 147, 75], [ 84, 76, 20, 75], [ 84, 76, 20, 27], [223, 148, 147, 232], [329, 412, 328, 404], [329, 412, 335, 404], [467, 476, 521, 468], [467, 521, 458, 468], [467, 393, 458, 468], [299, 300, 214, 288], [386, 477, 396, 469], [386, 313, 387, 302], [386, 313, 387, 396], [386, 460, 468, 469], [ 58, 122, 121, 112], [215, 206, 216, 140], [215, 206, 216, 302], [403, 467, 476, 411], [403, 476, 468, 469], [403, 467, 476, 468], [403, 386, 468, 469], [403, 386, 477, 469], [403, 412, 328, 411], [403, 412, 477, 411], [403, 476, 477, 469], [403, 476, 477, 411], [403, 386, 477, 396], [142, 134, 133, 70], [571, 585, 564, 584], [571, 585, 564, 572], [571, 534, 564, 572], [557, 585, 564, 584], [557, 556, 564, 584], [582, 557, 556, 584], [489, 420, 427, 421], [146, 221, 231, 230], [338, 342, 420, 341], [338, 342, 420, 421], [340, 249, 254, 341], [424, 425, 491, 488], [424, 425, 419, 488], [204, 194, 203, 288], [204, 214, 203, 288], [204, 300, 214, 288], [204, 194, 120, 203], [204, 194, 121, 120], [494, 495, 547, 552], [350, 254, 262, 263], [350, 254, 263, 255], [350, 254, 342, 255], [348, 342, 420, 341], [348, 419, 420, 341], [348, 254, 342, 341], [348, 350, 254, 342], [348, 340, 419, 341], [348, 340, 254, 341], [ 8, 5, 52, 9], [ 10, 16, 52, 9], [115, 107, 179, 47], [115, 107, 180, 179], [415, 418, 331, 336], [415, 337, 325, 416], [414, 330, 413, 336], [414, 418, 413, 336], [414, 330, 331, 336], [414, 418, 331, 336], [414, 330, 413, 404], [414, 418, 485, 413], [531, 567, 574, 568], [531, 574, 579, 532], [531, 522, 530, 469], [531, 567, 522, 530], [531, 567, 574, 530], [538, 477, 483, 469], [538, 477, 396, 469], [538, 531, 579, 532], [303, 313, 387, 302], [303, 388, 304, 293], [322, 244, 330, 234], [322, 244, 235, 234], [322, 312, 235, 234], [ 34, 92, 87, 39], [ 34, 35, 87, 39], [ 34, 96, 92, 91], [ 34, 96, 92, 39], [166, 248, 244, 165], [166, 160, 98, 167], [159, 93, 235, 158], [159, 244, 235, 158], [159, 244, 165, 158], [159, 166, 165, 98], [159, 166, 244, 165], [159, 166, 160, 98], [143, 237, 153, 161], [143, 237, 153, 228], [332, 248, 244, 331], [332, 166, 248, 244], [332, 159, 166, 244], [332, 159, 237, 160], [332, 159, 166, 160], [332, 237, 238, 160], [ 29, 4, 23, 2], [ 29, 30, 4, 23], [ 29, 23, 15, 2], [ 29, 23, 77, 15], [ 29, 1, 77, 15], [ 79, 87, 93, 150], [ 79, 30, 78, 70], [ 79, 30, 78, 87], [ 79, 78, 87, 150], [ 79, 143, 218, 134], [ 79, 142, 78, 70], [ 79, 142, 78, 150], [ 79, 142, 218, 150], [ 79, 142, 134, 70], [ 79, 142, 218, 134], [ 97, 165, 164, 158], [ 97, 163, 164, 158], [ 97, 96, 163, 158], [ 97, 159, 165, 158], [ 97, 159, 165, 98], [ 97, 96, 93, 158], [ 97, 159, 93, 158], [ 97, 159, 93, 98], [ 97, 40, 93, 98], [ 97, 96, 93, 39], [ 97, 40, 93, 39], [226, 148, 216, 149], [226, 92, 235, 150], [225, 148, 156, 232], [225, 233, 156, 232], [225, 233, 241, 232], [225, 226, 148, 216], [225, 226, 312, 216], [279, 204, 194, 121], [279, 277, 194, 278], [172, 254, 262, 255], [172, 262, 263, 255], [172, 262, 180, 263], [172, 262, 180, 179], [172, 107, 180, 179], [482, 474, 409, 481], [482, 474, 409, 475], [482, 536, 474, 481], [482, 536, 474, 475], [533, 581, 540, 580], [541, 482, 487, 481], [541, 482, 536, 481], [480, 487, 408, 481], [480, 487, 408, 416], [480, 486, 487, 416], [480, 398, 408, 481], [480, 541, 487, 481], [462, 389, 525, 471], [462, 389, 525, 526], [462, 524, 525, 526], [462, 388, 387, 452], [462, 454, 389, 526], [523, 531, 524, 532], [523, 574, 532, 575], [523, 531, 574, 532], [523, 574, 568, 575], [523, 531, 574, 568], [523, 583, 568, 561], [470, 524, 525, 532], [470, 462, 525, 471], [470, 462, 524, 525], [470, 531, 524, 532], [470, 538, 531, 532], [470, 538, 396, 469], [470, 538, 531, 469], [246, 334, 337, 325], [246, 334, 238, 325], [246, 332, 238, 325], [246, 332, 238, 160], [246, 238, 160, 167], [246, 332, 166, 160], [246, 166, 160, 167], [417, 408, 409, 326], [417, 408, 409, 481], [417, 482, 409, 481], [417, 487, 408, 481], [417, 482, 487, 481], [417, 487, 408, 416], [308, 317, 307, 391], [308, 317, 230, 307], [229, 317, 230, 307], [229, 153, 307, 316], [229, 221, 230, 239], [229, 221, 153, 239], [229, 221, 230, 307], [229, 221, 153, 307], [229, 399, 307, 316], [229, 399, 317, 307], [229, 231, 230, 239], [229, 240, 231, 239], [229, 317, 230, 318], [229, 240, 317, 318], [229, 153, 238, 316], [229, 153, 238, 239], [229, 238, 326, 316], [229, 399, 326, 316], [229, 399, 317, 326], [229, 231, 230, 318], [229, 240, 231, 318], [229, 334, 238, 326], [229, 240, 334, 326], [229, 240, 334, 238], [400, 399, 317, 391], [400, 399, 317, 409], [400, 474, 465, 391], [400, 308, 317, 391], [400, 401, 308, 317], [400, 474, 409, 475], [400, 382, 465, 391], [400, 474, 465, 475], [400, 308, 382, 391], [400, 382, 466, 465], [400, 466, 465, 475], [400, 401, 466, 475], [400, 308, 382, 392], [400, 401, 308, 392], [400, 382, 466, 392], [400, 401, 466, 392], [ 71, 134, 133, 70], [ 71, 134, 125, 133], [ 71, 60, 133, 70], [208, 142, 134, 133], [208, 142, 218, 134], [208, 142, 133, 132], [208, 303, 304, 293], [503, 504, 496, 553], [503, 504, 496, 431], [560, 503, 504, 553], [560, 503, 504, 449], [569, 523, 568, 575], [569, 523, 583, 568], [459, 521, 458, 468], [459, 522, 521, 468], [459, 460, 522, 468], [383, 466, 465, 457], [383, 382, 466, 465], [383, 382, 466, 392], [518, 528, 529, 465], [518, 528, 573, 529], [518, 528, 573, 527], [518, 528, 464, 465], [518, 528, 464, 527], [565, 585, 527, 572], [565, 573, 527, 572], [565, 518, 573, 527], [219, 218, 304, 228], [219, 208, 218, 304], [219, 208, 218, 134], [219, 208, 304, 293], [380, 306, 381, 390], [315, 399, 398, 316], [535, 534, 527, 578], [535, 528, 527, 578], [535, 533, 581, 540], [535, 533, 525, 540], [535, 533, 534, 525], [535, 541, 540, 481], [535, 528, 536, 481], [535, 528, 536, 578], [535, 541, 581, 578], [535, 541, 581, 540], [535, 541, 536, 481], [535, 541, 536, 578], [473, 474, 409, 481], [473, 399, 390, 391], [473, 528, 474, 481], [473, 528, 474, 464], [473, 464, 390, 391], [473, 474, 464, 391], [473, 398, 409, 481], [473, 399, 398, 409], [473, 315, 399, 390], [473, 315, 399, 398], [473, 400, 474, 409], [473, 400, 399, 409], [473, 400, 474, 391], [473, 400, 399, 391], [473, 315, 389, 390], [368, 359, 369, 272], [124, 123, 60, 133], [124, 123, 133, 132], [124, 123, 197, 132], [113, 105, 46, 179], [113, 123, 197, 122], [113, 58, 122, 112], [ 68, 1, 21, 77], [ 68, 20, 1, 21], [ 68, 20, 1, 28], [ 68, 20, 27, 28], [ 68, 76, 20, 27], [ 67, 66, 20, 75], [ 67, 76, 20, 75], [ 67, 66, 129, 75], [ 67, 68, 76, 20], [ 67, 76, 140, 75], [ 67, 66, 20, 21], [ 67, 68, 20, 21], [ 67, 66, 129, 57], [ 67, 66, 13, 21], [ 67, 66, 13, 57], [ 67, 58, 13, 57], [139, 76, 148, 147], [139, 223, 148, 147], [139, 223, 138, 147], [139, 138, 147, 75], [139, 76, 147, 75], [139, 76, 148, 140], [139, 76, 140, 75], [139, 129, 140, 75], [139, 138, 129, 75], [139, 223, 138, 214], [139, 148, 216, 140], [139, 215, 216, 140], [139, 204, 215, 140], [139, 204, 215, 300], [139, 204, 300, 214], [139, 138, 129, 203], [139, 138, 214, 203], [139, 204, 214, 203], [310, 299, 300, 214], [301, 215, 216, 302], [301, 386, 313, 302], [301, 312, 313, 302], [301, 312, 216, 302], [301, 225, 312, 216], [365, 355, 364, 278], [365, 277, 364, 278], [365, 279, 277, 278], [185, 279, 194, 278], [185, 279, 259, 278], [185, 194, 121, 120], [185, 279, 194, 121], [551, 494, 547, 552], [551, 494, 547, 550], [551, 494, 501, 552], [509, 557, 585, 564], [509, 585, 527, 564], [509, 565, 585, 527], [509, 446, 510, 501], [509, 565, 518, 527], [509, 518, 510, 566], [509, 565, 518, 566], [438, 446, 510, 501], [438, 511, 510, 501], [438, 511, 502, 439], [555, 582, 556, 549], [555, 582, 556, 584], [222, 146, 231, 230], [222, 231, 230, 318], [212, 146, 221, 145], [212, 137, 146, 145], [212, 146, 221, 230], [212, 222, 146, 230], [212, 221, 230, 307], [212, 308, 230, 307], [212, 222, 308, 230], [212, 222, 308, 298], [155, 146, 221, 231], [155, 146, 221, 145], [155, 231, 239, 162], [155, 221, 231, 239], [430, 438, 502, 439], [422, 338, 343, 255], [422, 338, 342, 421], [422, 342, 427, 421], [422, 428, 427, 421], [256, 344, 343, 257], [256, 422, 344, 343], [256, 264, 263, 255], [256, 273, 264, 263], [292, 303, 387, 302], [292, 388, 377, 452], [292, 282, 377, 293], [292, 388, 387, 452], [292, 303, 388, 387], [292, 388, 377, 293], [292, 303, 388, 293], [205, 279, 204, 121], [205, 215, 206, 140], [205, 204, 215, 140], [437, 369, 273, 361], [437, 359, 369, 273], [437, 359, 369, 436], [437, 494, 446, 436], [437, 494, 446, 501], [437, 438, 446, 501], [426, 493, 489, 427], [426, 489, 420, 488], [426, 489, 420, 427], [426, 419, 420, 488], [426, 425, 419, 488], [426, 348, 419, 420], [426, 348, 425, 419], [426, 342, 420, 427], [426, 348, 342, 420], [426, 350, 342, 427], [426, 348, 350, 342], [499, 494, 547, 550], [499, 493, 494, 547], [499, 546, 547, 550], [499, 546, 493, 547], [499, 493, 494, 436], [499, 546, 549, 550], [499, 582, 549, 550], [499, 582, 556, 549], [490, 489, 427, 421], [490, 493, 489, 427], [490, 546, 493, 547], [490, 546, 493, 489], [490, 428, 427, 421], [261, 348, 340, 254], [261, 340, 253, 339], [261, 340, 249, 254], [261, 340, 249, 253], [ 6, 42, 43, 47], [ 6, 42, 46, 47], [ 6, 42, 5, 46], [ 6, 5, 52, 9], [ 6, 10, 52, 9], [ 6, 52, 46, 47], [ 6, 5, 52, 46], [106, 107, 179, 47], [106, 46, 179, 47], [106, 42, 46, 47], [106, 172, 107, 179], [106, 105, 46, 179], [106, 168, 172, 107], [106, 105, 99, 46], [106, 42, 99, 46], [ 50, 58, 13, 57], [ 22, 8, 9, 14], [ 22, 8, 52, 9], [ 22, 123, 60, 52], [ 22, 77, 15, 14], [ 22, 9, 15, 14], [ 22, 16, 52, 9], [ 22, 16, 60, 52], [ 22, 16, 60, 23], [ 22, 23, 77, 15], [ 22, 16, 23, 15], [ 0, 10, 16, 9], [ 0, 22, 9, 15], [ 0, 22, 16, 15], [ 0, 22, 16, 9], [ 0, 16, 15, 2], [ 0, 17, 16, 2], [ 0, 10, 16, 11], [ 0, 17, 16, 11], [ 53, 10, 16, 52], [ 53, 16, 60, 52], [ 53, 6, 52, 47], [ 53, 6, 10, 52], [ 53, 6, 43, 47], [ 53, 6, 10, 43], [ 53, 10, 43, 7], [ 53, 10, 11, 7], [ 53, 10, 16, 11], [ 48, 116, 109, 55], [ 48, 115, 107, 47], [ 48, 53, 115, 47], [ 48, 53, 43, 47], [ 48, 53, 43, 7], [245, 332, 166, 248], [245, 248, 331, 336], [245, 415, 331, 336], [245, 332, 248, 331], [245, 415, 325, 331], [245, 415, 337, 325], [245, 246, 332, 166], [245, 332, 325, 331], [245, 246, 337, 325], [245, 246, 332, 325], [479, 543, 486, 485], [479, 486, 418, 485], [479, 414, 418, 485], [407, 415, 486, 418], [407, 479, 486, 418], [407, 479, 414, 418], [407, 415, 418, 331], [407, 414, 418, 331], [407, 479, 480, 486], [407, 415, 325, 331], [407, 415, 486, 416], [407, 480, 486, 416], [407, 415, 325, 416], [407, 325, 408, 416], [407, 480, 408, 416], [407, 398, 325, 408], [407, 480, 398, 408], [539, 542, 579, 580], [539, 538, 542, 579], [539, 538, 579, 532], [539, 533, 540, 580], [539, 470, 538, 532], [539, 479, 540, 471], [539, 543, 542, 580], [539, 470, 525, 471], [539, 470, 525, 532], [539, 543, 540, 580], [539, 479, 543, 540], [539, 525, 540, 471], [539, 533, 525, 540], [539, 533, 525, 532], [537, 531, 574, 579], [537, 538, 531, 579], [537, 531, 574, 530], [537, 531, 530, 469], [537, 538, 531, 469], [537, 538, 542, 483], [537, 538, 542, 579], [537, 476, 530, 469], [537, 476, 483, 469], [537, 538, 483, 469], [544, 480, 486, 487], [544, 541, 581, 540], [544, 480, 541, 487], [544, 479, 543, 540], [544, 479, 480, 540], [544, 479, 543, 486], [544, 479, 480, 486], [544, 581, 540, 580], [544, 543, 540, 580], [544, 541, 540, 481], [544, 480, 540, 481], [544, 480, 541, 481], [314, 315, 304, 228], [323, 414, 330, 331], [323, 244, 330, 331], [323, 322, 244, 330], [323, 407, 414, 331], [323, 332, 244, 331], [323, 407, 325, 331], [323, 332, 325, 331], [ 86, 34, 35, 28], [ 86, 34, 35, 87], [ 86, 29, 35, 28], [ 86, 30, 78, 87], [ 86, 30, 35, 87], [ 86, 68, 78, 77], [ 86, 68, 1, 77], [ 86, 68, 1, 28], [ 86, 29, 1, 77], [ 86, 29, 1, 28], [ 86, 78, 23, 77], [ 86, 29, 23, 77], [ 86, 30, 78, 23], [ 86, 29, 30, 23], [ 86, 30, 35, 4], [ 86, 29, 35, 4], [ 86, 29, 30, 4], [151, 159, 93, 235], [151, 93, 235, 150], [151, 79, 93, 150], [151, 79, 218, 150], [151, 79, 143, 218], [151, 143, 237, 228], [151, 143, 218, 228], [152, 159, 93, 98], [152, 40, 93, 98], [152, 40, 94, 98], [152, 94, 160, 98], [152, 159, 160, 98], [152, 151, 159, 93], [152, 159, 237, 160], [152, 151, 159, 237], [152, 89, 94, 41], [152, 40, 94, 41], [152, 94, 160, 161], [152, 237, 160, 161], [152, 143, 89, 161], [152, 143, 237, 161], [152, 151, 143, 237], [152, 94, 95, 161], [152, 89, 95, 161], [152, 89, 94, 95], [ 80, 79, 30, 70], [ 80, 79, 143, 134], [ 80, 144, 143, 89], [ 80, 144, 143, 134], [ 80, 79, 134, 70], [ 80, 71, 134, 70], [227, 226, 235, 150], [227, 151, 235, 150], [227, 151, 218, 150], [227, 226, 312, 235], [227, 142, 218, 150], [227, 208, 218, 304], [227, 208, 303, 304], [227, 322, 312, 235], [227, 218, 304, 228], [227, 314, 304, 228], [227, 314, 303, 304], [227, 322, 312, 313], [227, 314, 322, 313], [227, 314, 303, 313], [ 85, 86, 68, 78], [ 85, 76, 27, 149], [ 85, 68, 76, 27], [ 85, 226, 92, 149], [ 85, 226, 92, 150], [ 85, 78, 87, 150], [ 85, 92, 87, 150], [ 85, 86, 78, 87], [ 85, 86, 34, 87], [ 85, 27, 91, 149], [ 85, 34, 27, 91], [ 85, 92, 91, 149], [ 85, 34, 92, 91], [ 85, 34, 92, 87], [ 85, 34, 27, 28], [ 85, 86, 34, 28], [ 85, 68, 27, 28], [ 85, 86, 68, 28], [157, 233, 156, 163], [157, 225, 233, 156], [157, 225, 233, 234], [157, 156, 91, 149], [157, 156, 163, 91], [157, 148, 156, 149], [157, 225, 148, 156], [157, 233, 164, 234], [157, 233, 163, 164], [157, 92, 91, 149], [157, 92, 163, 91], [157, 226, 92, 149], [157, 226, 148, 149], [157, 225, 226, 148], [157, 164, 234, 158], [157, 163, 164, 158], [157, 225, 312, 234], [157, 225, 226, 312], [157, 92, 163, 158], [157, 226, 92, 235], [157, 312, 235, 234], [157, 226, 312, 235], [157, 235, 234, 158], [157, 92, 235, 158], [321, 233, 241, 242], [321, 225, 233, 241], [321, 233, 234, 242], [321, 225, 233, 234], [321, 329, 241, 242], [321, 330, 234, 242], [321, 335, 330, 242], [321, 329, 335, 242], [321, 225, 312, 234], [321, 335, 330, 404], [321, 329, 335, 404], [321, 322, 330, 404], [321, 322, 330, 234], [321, 322, 312, 234], [576, 533, 581, 580], [576, 539, 579, 580], [576, 539, 533, 580], [576, 539, 579, 532], [576, 539, 533, 532], [576, 579, 532, 575], [577, 533, 534, 586], [577, 576, 533, 586], [577, 576, 533, 581], [577, 534, 578, 572], [577, 535, 581, 578], [577, 535, 533, 581], [577, 535, 534, 578], [577, 535, 533, 534], [577, 571, 534, 572], [577, 571, 534, 586], [472, 473, 315, 398], [472, 473, 315, 389], [472, 479, 540, 471], [472, 479, 480, 540], [472, 525, 540, 471], [472, 389, 525, 471], [472, 480, 398, 481], [472, 473, 398, 481], [472, 535, 525, 540], [472, 480, 540, 481], [472, 535, 540, 481], [570, 523, 524, 532], [570, 524, 525, 532], [570, 523, 524, 516], [570, 533, 525, 532], [570, 533, 534, 525], [570, 524, 525, 526], [570, 534, 525, 526], [570, 533, 534, 586], [570, 523, 532, 575], [570, 524, 526, 516], [570, 534, 526, 516], [570, 576, 533, 532], [570, 576, 533, 586], [570, 576, 532, 575], [570, 576, 586, 575], [570, 569, 586, 575], [570, 569, 523, 575], [570, 571, 534, 586], [570, 569, 571, 586], [570, 534, 564, 516], [570, 571, 534, 564], [376, 292, 387, 452], [376, 386, 387, 302], [376, 292, 387, 302], [247, 240, 334, 238], [247, 229, 238, 239], [247, 229, 240, 239], [247, 229, 240, 238], [247, 239, 167, 161], [247, 238, 167, 161], [247, 238, 239, 161], [247, 246, 238, 167], [247, 246, 334, 238], [247, 239, 162, 167], [247, 231, 239, 162], [247, 240, 231, 239], [410, 401, 317, 318], [410, 400, 317, 409], [410, 400, 401, 317], [410, 482, 409, 475], [410, 417, 482, 409], [410, 400, 409, 475], [410, 400, 401, 475], [327, 240, 334, 326], [327, 417, 409, 326], [327, 410, 417, 409], [327, 240, 317, 318], [327, 410, 317, 318], [327, 317, 409, 326], [327, 410, 317, 409], [327, 229, 317, 326], [327, 229, 240, 326], [327, 229, 240, 317], [333, 417, 408, 326], [333, 325, 408, 326], [333, 417, 408, 416], [333, 334, 325, 326], [333, 334, 337, 325], [333, 325, 408, 416], [333, 337, 325, 416], [333, 327, 334, 326], [333, 327, 417, 326], [309, 317, 230, 318], [309, 308, 317, 230], [309, 222, 230, 318], [309, 222, 308, 230], [309, 401, 317, 318], [309, 401, 308, 317], [309, 401, 308, 392], [309, 308, 392, 298], [309, 222, 308, 298], [297, 212, 308, 298], [297, 308, 392, 298], [297, 308, 382, 392], [154, 144, 221, 145], [154, 155, 221, 145], [154, 144, 221, 153], [154, 155, 239, 162], [154, 155, 221, 239], [154, 95, 162, 161], [154, 89, 95, 161], [154, 239, 162, 161], [154, 143, 153, 161], [154, 143, 89, 161], [154, 144, 143, 153], [154, 144, 143, 89], [154, 153, 239, 161], [154, 221, 153, 239], [ 61, 71, 125, 133], [ 61, 71, 60, 133], [ 61, 126, 116, 125], [ 61, 126, 71, 125], [ 61, 124, 125, 133], [ 61, 124, 60, 133], [ 61, 48, 116, 115], [ 61, 48, 53, 115], [ 24, 60, 23, 70], [ 24, 71, 60, 70], [ 24, 80, 71, 70], [ 24, 80, 71, 72], [ 24, 30, 23, 70], [ 24, 16, 60, 23], [ 24, 80, 30, 70], [ 24, 31, 17, 2], [ 24, 31, 3, 72], [ 24, 31, 17, 3], [ 24, 16, 23, 2], [ 24, 17, 16, 2], [ 24, 4, 23, 2], [ 24, 30, 4, 23], [ 24, 31, 4, 2], [ 24, 31, 30, 4], [512, 522, 521, 530], [512, 567, 522, 530], [512, 459, 521, 458], [512, 459, 522, 521], [512, 459, 373, 458], [512, 459, 373, 449], [497, 496, 553, 548], [497, 504, 496, 553], [497, 496, 491, 548], [456, 518, 465, 457], [456, 383, 465, 457], [456, 381, 464, 465], [456, 518, 464, 465], [456, 383, 448, 371], [456, 381, 382, 465], [456, 383, 382, 465], [456, 381, 382, 371], [456, 383, 382, 371], [136, 212, 137, 145], [558, 509, 557, 585], [558, 509, 565, 585], [558, 509, 565, 566], [519, 565, 518, 573], [519, 518, 573, 529], [519, 565, 518, 566], [519, 529, 465, 457], [519, 518, 465, 457], [519, 518, 529, 465], [519, 456, 518, 457], [220, 144, 143, 134], [220, 153, 307, 316], [220, 221, 153, 307], [220, 144, 221, 153], [220, 153, 228, 316], [220, 143, 153, 228], [220, 144, 143, 153], [220, 143, 218, 228], [220, 143, 218, 134], [220, 219, 218, 228], [220, 219, 218, 134], [445, 437, 369, 436], [445, 437, 446, 436], [305, 315, 399, 390], [305, 306, 390, 391], [305, 399, 390, 391], [305, 315, 399, 316], [305, 306, 307, 391], [305, 399, 307, 391], [305, 220, 219, 306], [305, 399, 307, 316], [305, 219, 304, 228], [305, 315, 304, 228], [305, 220, 219, 228], [305, 220, 307, 316], [305, 220, 306, 307], [305, 315, 228, 316], [305, 220, 228, 316], [463, 535, 528, 527], [463, 528, 464, 527], [463, 534, 527, 526], [463, 535, 534, 527], [463, 534, 525, 526], [463, 535, 534, 525], [463, 389, 525, 526], [463, 454, 389, 526], [463, 473, 528, 464], [463, 472, 389, 525], [463, 472, 535, 525], [463, 472, 473, 389], [463, 535, 528, 481], [463, 473, 528, 481], [463, 454, 464, 390], [463, 454, 389, 390], [463, 473, 464, 390], [463, 473, 389, 390], [463, 472, 535, 481], [463, 472, 473, 481], [271, 359, 272, 263], [271, 368, 359, 272], [271, 350, 359, 263], [271, 180, 272, 263], [271, 262, 180, 263], [271, 350, 262, 263], [198, 124, 197, 132], [198, 208, 133, 132], [198, 124, 133, 132], [189, 116, 115, 190], [189, 190, 180, 272], [189, 115, 190, 180], [189, 61, 116, 125], [189, 61, 124, 125], [189, 61, 116, 115], [189, 61, 124, 115], [114, 113, 46, 179], [114, 46, 179, 47], [114, 124, 197, 179], [114, 113, 197, 179], [114, 115, 179, 47], [114, 124, 115, 179], [114, 52, 46, 47], [114, 124, 123, 197], [114, 113, 123, 197], [114, 123, 60, 52], [114, 124, 123, 60], [114, 53, 52, 47], [114, 53, 115, 47], [114, 61, 124, 115], [114, 61, 124, 60], [114, 61, 53, 115], [114, 53, 60, 52], [114, 61, 53, 60], [ 69, 68, 78, 77], [ 69, 78, 23, 77], [ 69, 22, 123, 60], [ 69, 123, 60, 133], [ 69, 123, 133, 132], [ 69, 22, 23, 77], [ 69, 22, 60, 23], [ 69, 78, 23, 70], [ 69, 60, 23, 70], [ 69, 60, 133, 70], [ 69, 142, 133, 132], [ 69, 142, 133, 70], [ 69, 142, 78, 70], [130, 67, 129, 57], [130, 67, 58, 57], [130, 58, 121, 57], [130, 120, 129, 57], [130, 121, 120, 57], [130, 204, 121, 120], [130, 129, 140, 75], [130, 67, 140, 75], [130, 67, 129, 75], [130, 58, 122, 121], [130, 120, 129, 203], [130, 204, 120, 203], [130, 139, 129, 140], [130, 139, 204, 140], [130, 205, 204, 140], [130, 205, 204, 121], [130, 205, 122, 140], [130, 205, 122, 121], [130, 139, 129, 203], [130, 139, 204, 203], [ 59, 67, 13, 21], [ 59, 68, 21, 77], [ 59, 67, 68, 21], [ 59, 21, 77, 14], [ 59, 22, 77, 14], [ 59, 13, 21, 14], [ 59, 69, 68, 77], [ 59, 67, 58, 13], [ 59, 8, 13, 14], [ 59, 22, 8, 14], [ 59, 69, 22, 77], [ 59, 69, 22, 123], [ 59, 58, 123, 122], [402, 403, 328, 411], [402, 403, 467, 411], [224, 223, 148, 232], [224, 225, 148, 232], [224, 225, 148, 216], [224, 301, 225, 216], [224, 310, 300, 214], [224, 310, 223, 214], [224, 139, 223, 148], [224, 139, 148, 216], [224, 139, 300, 214], [224, 139, 223, 214], [224, 139, 215, 300], [224, 139, 215, 216], [224, 301, 215, 216], [385, 386, 460, 468], [385, 301, 386, 302], [385, 301, 215, 302], [385, 376, 386, 460], [385, 376, 386, 302], [395, 301, 386, 313], [395, 301, 403, 386], [395, 386, 313, 396], [395, 403, 386, 396], [395, 301, 312, 313], [395, 477, 396, 404], [395, 403, 477, 396], [395, 321, 322, 404], [395, 322, 312, 313], [395, 321, 322, 312], [395, 412, 477, 404], [395, 403, 412, 477], [395, 412, 328, 404], [395, 403, 412, 328], [395, 329, 328, 404], [395, 321, 329, 404], [395, 321, 329, 328], [347, 424, 425, 419], [347, 348, 340, 419], [347, 348, 425, 419], [177, 252, 169, 176], [177, 113, 105, 112], [177, 252, 169, 253], [500, 551, 494, 501], [500, 494, 446, 501], [500, 509, 446, 501], [500, 558, 551, 501], [500, 558, 509, 501], [500, 558, 551, 557], [500, 558, 509, 557], [500, 494, 446, 436], [500, 499, 494, 436], [500, 551, 494, 550], [500, 499, 494, 550], [500, 551, 557, 550], [500, 557, 556, 564], [500, 509, 557, 564], [500, 507, 499, 556], [500, 499, 582, 550], [500, 499, 582, 556], [500, 582, 557, 550], [500, 582, 557, 556], [517, 518, 464, 527], [517, 509, 518, 527], [517, 509, 446, 510], [517, 509, 518, 510], [517, 463, 464, 527], [517, 463, 454, 464], [517, 463, 527, 526], [517, 463, 454, 526], [517, 527, 564, 526], [517, 509, 527, 564], [447, 438, 511, 510], [447, 511, 448, 439], [447, 438, 511, 439], [447, 438, 362, 439], [447, 456, 448, 371], [447, 363, 448, 439], [447, 363, 362, 439], [447, 456, 381, 371], [447, 363, 448, 371], [447, 363, 362, 371], [447, 285, 381, 371], [447, 285, 362, 371], [545, 499, 546, 549], [108, 190, 180, 264], [108, 115, 190, 180], [108, 115, 107, 180], [108, 48, 115, 107], [108, 116, 109, 190], [108, 48, 116, 109], [108, 116, 115, 190], [108, 48, 116, 115], [295, 294, 380, 306], [295, 380, 306, 381], [295, 285, 380, 381], [ 37, 30, 4, 36], [ 37, 31, 30, 4], [ 54, 18, 17, 3], [ 54, 24, 3, 72], [ 54, 24, 17, 3], [ 54, 24, 71, 72], [ 54, 24, 17, 16], [ 54, 126, 71, 72], [ 54, 17, 16, 11], [ 54, 18, 17, 11], [ 54, 61, 126, 71], [ 54, 24, 16, 60], [ 54, 24, 71, 60], [ 54, 61, 71, 60], [ 54, 53, 16, 11], [ 54, 53, 16, 60], [ 54, 61, 53, 60], [345, 256, 344, 257], [354, 363, 362, 439], [353, 438, 362, 439], [353, 430, 438, 439], [353, 354, 362, 439], [353, 354, 430, 439], [353, 438, 362, 361], [353, 354, 430, 266], [353, 345, 430, 266], [353, 345, 430, 423], [267, 183, 258, 175], [372, 383, 448, 371], [372, 363, 448, 371], [372, 202, 297, 298], [372, 383, 382, 392], [372, 383, 382, 371], [372, 297, 382, 392], [372, 297, 382, 371], [372, 297, 392, 298], [182, 258, 174, 175], [182, 183, 258, 175], [182, 183, 175, 109], [182, 258, 174, 257], [182, 183, 109, 190], [182, 108, 190, 264], [182, 108, 109, 190], [351, 422, 428, 344], [351, 256, 422, 344], [351, 422, 428, 427], [351, 422, 342, 427], [351, 359, 273, 263], [351, 256, 273, 263], [351, 350, 359, 263], [351, 350, 359, 427], [351, 350, 342, 427], [351, 256, 263, 255], [351, 338, 342, 255], [351, 422, 338, 255], [351, 422, 338, 342], [351, 422, 343, 255], [351, 256, 343, 255], [351, 256, 422, 343], [351, 350, 263, 255], [351, 350, 342, 255], [207, 292, 282, 293], [207, 198, 282, 293], [207, 292, 303, 302], [207, 198, 208, 132], [207, 198, 208, 293], [207, 208, 303, 293], [207, 292, 303, 293], [360, 493, 494, 547], [360, 490, 493, 547], [360, 494, 495, 547], [360, 490, 495, 547], [360, 490, 493, 427], [360, 493, 359, 427], [360, 490, 428, 427], [360, 490, 428, 495], [360, 493, 359, 436], [360, 493, 494, 436], [360, 351, 359, 427], [360, 437, 359, 436], [360, 437, 494, 436], [360, 351, 428, 427], [360, 428, 495, 423], [178, 172, 254, 262], [178, 261, 254, 262], [178, 261, 249, 254], [178, 172, 262, 179], [178, 106, 172, 179], [178, 106, 105, 179], [178, 261, 249, 253], [178, 177, 261, 253], [178, 177, 169, 105], [100, 106, 42, 99], [100, 106, 168, 172], [100, 178, 106, 172], [250, 338, 343, 255], [250, 249, 254, 341], [250, 172, 254, 255], [250, 254, 342, 341], [250, 338, 342, 341], [250, 254, 342, 255], [250, 338, 342, 255], [251, 256, 343, 257], [251, 256, 343, 255], [251, 182, 174, 257], [251, 182, 256, 257], [ 44, 48, 43, 7], [ 44, 108, 48, 107], [ 44, 48, 43, 47], [ 44, 48, 107, 47], [111, 50, 58, 112], [111, 50, 58, 57], [111, 121, 120, 57], [111, 185, 121, 120], [111, 58, 121, 112], [111, 58, 121, 57], [104, 169, 105, 99], [104, 111, 176, 112], [104, 111, 50, 112], [104, 177, 176, 112], [104, 177, 169, 176], [104, 177, 105, 112], [104, 177, 169, 105], [ 45, 104, 50, 112], [ 45, 42, 99, 46], [ 45, 42, 5, 46], [ 45, 105, 99, 46], [ 45, 104, 105, 99], [ 45, 104, 105, 112], [478, 539, 538, 542], [478, 538, 477, 396], [478, 542, 484, 483], [478, 538, 542, 483], [478, 542, 484, 485], [478, 539, 543, 542], [478, 539, 479, 543], [478, 539, 479, 471], [478, 477, 396, 404], [478, 477, 484, 404], [478, 477, 484, 483], [478, 538, 477, 483], [478, 479, 414, 485], [478, 543, 542, 485], [478, 479, 543, 485], [478, 470, 396, 471], [478, 470, 538, 396], [478, 539, 470, 471], [478, 539, 470, 538], [478, 484, 413, 404], [478, 414, 413, 404], [478, 484, 485, 413], [478, 414, 485, 413], [397, 315, 388, 304], [397, 314, 315, 304], [397, 303, 388, 304], [397, 314, 303, 304], [397, 315, 388, 389], [397, 303, 388, 387], [397, 303, 313, 387], [397, 314, 303, 313], [397, 462, 389, 471], [397, 462, 388, 389], [397, 472, 389, 471], [397, 472, 315, 389], [397, 462, 388, 387], [397, 470, 462, 471], [397, 470, 462, 387], [397, 313, 387, 396], [397, 470, 396, 471], [397, 470, 387, 396], [405, 323, 414, 330], [405, 323, 322, 330], [405, 414, 330, 404], [405, 322, 330, 404], [405, 323, 314, 322], [405, 478, 396, 404], [405, 478, 414, 404], [405, 395, 396, 404], [405, 395, 322, 404], [405, 314, 322, 313], [405, 478, 396, 471], [405, 395, 313, 396], [405, 395, 322, 313], [405, 397, 396, 471], [405, 397, 313, 396], [405, 397, 314, 313], [405, 478, 479, 471], [405, 478, 479, 414], [236, 323, 314, 322], [236, 227, 322, 235], [236, 227, 314, 322], [236, 322, 244, 235], [236, 323, 322, 244], [236, 227, 151, 235], [236, 227, 314, 228], [236, 159, 244, 235], [236, 151, 159, 235], [236, 151, 237, 228], [236, 151, 218, 228], [236, 227, 218, 228], [236, 227, 151, 218], [236, 151, 159, 237], [236, 323, 332, 244], [236, 332, 159, 244], [236, 332, 159, 237], [ 88, 152, 143, 89], [ 88, 80, 143, 89], [ 88, 80, 79, 143], [ 88, 152, 89, 41], [ 88, 151, 79, 143], [ 88, 152, 151, 143], [ 88, 80, 79, 30], [ 88, 151, 79, 93], [ 88, 152, 151, 93], [ 88, 40, 41, 36], [ 88, 152, 40, 41], [ 88, 79, 87, 93], [ 88, 37, 41, 36], [ 88, 37, 30, 36], [ 88, 30, 87, 36], [ 88, 79, 30, 87], [ 88, 152, 40, 93], [ 88, 87, 39, 36], [ 88, 40, 39, 36], [ 88, 87, 93, 39], [ 88, 40, 93, 39], [217, 227, 142, 150], [217, 227, 226, 150], [217, 207, 206, 132], [217, 207, 206, 302], [217, 206, 216, 302], [217, 226, 312, 216], [217, 227, 226, 312], [217, 208, 142, 132], [217, 207, 208, 132], [217, 207, 303, 302], [217, 208, 142, 218], [217, 227, 142, 218], [217, 227, 208, 218], [217, 227, 208, 303], [217, 207, 208, 303], [217, 312, 216, 302], [217, 303, 313, 302], [217, 227, 303, 313], [217, 312, 313, 302], [217, 227, 312, 313], [406, 472, 315, 398], [406, 397, 472, 471], [406, 397, 472, 315], [406, 472, 479, 471], [406, 407, 480, 398], [406, 472, 480, 398], [406, 405, 397, 471], [406, 405, 479, 471], [406, 407, 479, 480], [406, 472, 479, 480], [406, 397, 314, 315], [406, 405, 397, 314], [406, 407, 479, 414], [406, 405, 479, 414], [406, 405, 323, 314], [406, 323, 407, 414], [406, 405, 323, 414], [563, 570, 523, 516], [563, 570, 569, 523], [563, 569, 523, 583], [563, 570, 564, 516], [563, 569, 571, 584], [563, 570, 569, 571], [563, 569, 583, 584], [563, 556, 564, 584], [563, 556, 564, 516], [563, 571, 564, 584], [563, 570, 571, 564], [563, 555, 556, 584], [563, 555, 583, 584], [117, 126, 116, 125], [117, 126, 200, 125], [117, 126, 116, 55], [117, 116, 109, 190], [117, 116, 109, 55], [117, 126, 64, 55], [117, 126, 200, 64], [117, 183, 109, 190], [394, 459, 373, 458], [394, 384, 373, 458], [394, 459, 458, 468], [394, 459, 460, 468], [394, 385, 460, 468], [394, 384, 393, 458], [394, 393, 458, 468], [394, 384, 299, 393], [450, 503, 504, 449], [450, 440, 503, 449], [450, 459, 373, 449], [450, 440, 373, 449], [562, 507, 556, 516], [562, 563, 523, 516], [562, 563, 523, 583], [562, 523, 583, 561], [562, 563, 556, 516], [562, 583, 554, 561], [562, 563, 555, 556], [562, 563, 555, 583], [562, 555, 583, 554], [515, 562, 507, 516], [515, 562, 507, 506], [515, 462, 524, 452], [515, 523, 524, 516], [515, 562, 523, 516], [434, 499, 493, 436], [434, 507, 499, 436], [434, 433, 497, 506], [432, 424, 496, 491], [432, 497, 496, 491], [432, 424, 425, 491], [432, 497, 425, 491], [432, 424, 496, 431], [432, 433, 497, 425], [432, 504, 496, 431], [432, 497, 504, 496], [432, 346, 424, 431], [367, 451, 376, 452], [367, 292, 377, 452], [367, 376, 292, 452], [367, 292, 282, 377], [520, 511, 510, 566], [520, 447, 511, 510], [520, 447, 456, 510], [520, 518, 510, 566], [520, 519, 518, 566], [520, 519, 456, 457], [520, 456, 518, 510], [520, 519, 456, 518], [520, 447, 511, 448], [520, 447, 456, 448], [520, 456, 383, 457], [520, 456, 383, 448], [455, 456, 518, 464], [455, 456, 518, 510], [455, 517, 518, 464], [455, 517, 518, 510], [455, 456, 381, 464], [455, 447, 456, 510], [455, 447, 456, 381], [455, 517, 454, 464], [455, 517, 445, 454], [455, 517, 446, 510], [455, 517, 445, 446], [455, 381, 464, 390], [455, 380, 381, 390], [455, 445, 380, 454], [455, 454, 464, 390], [455, 380, 454, 390], [135, 126, 200, 125], [135, 126, 71, 72], [135, 126, 71, 125], [135, 80, 71, 72], [135, 71, 134, 125], [135, 80, 144, 134], [135, 80, 71, 134], [211, 136, 200, 201], [211, 136, 212, 201], [211, 295, 200, 201], [211, 136, 212, 145], [211, 135, 136, 145], [211, 135, 136, 200], [211, 135, 144, 145], [211, 144, 221, 145], [211, 212, 221, 145], [211, 212, 221, 307], [211, 220, 221, 307], [211, 220, 306, 307], [211, 220, 144, 221], [559, 551, 501, 552], [559, 558, 551, 501], [559, 502, 501, 552], [559, 438, 502, 501], [559, 438, 511, 501], [559, 438, 511, 502], [559, 511, 510, 566], [559, 511, 510, 501], [559, 558, 509, 566], [559, 558, 509, 501], [559, 509, 510, 566], [559, 509, 510, 501], [379, 380, 454, 390], [379, 380, 306, 390], [379, 294, 380, 306], [379, 388, 304, 293], [379, 454, 389, 390], [379, 454, 388, 389], [379, 305, 306, 390], [379, 219, 304, 293], [379, 294, 219, 293], [379, 315, 389, 390], [379, 315, 388, 389], [379, 315, 388, 304], [379, 294, 219, 306], [379, 305, 219, 306], [379, 305, 315, 390], [379, 305, 315, 304], [379, 305, 219, 304], [283, 282, 377, 293], [283, 368, 282, 377], [283, 368, 369, 272], [283, 198, 282, 293], [283, 271, 368, 272], [283, 271, 368, 282], [296, 285, 381, 371], [296, 381, 382, 371], [296, 297, 382, 371], [296, 295, 285, 381], [296, 297, 308, 382], [296, 295, 306, 381], [296, 295, 285, 201], [296, 381, 382, 391], [296, 308, 382, 391], [296, 211, 295, 306], [296, 211, 295, 201], [296, 306, 381, 391], [296, 297, 212, 201], [296, 211, 212, 201], [296, 306, 307, 391], [296, 308, 307, 391], [296, 211, 306, 307], [296, 297, 212, 308], [296, 212, 308, 307], [296, 211, 212, 307], [284, 295, 285, 380], [284, 294, 380, 369], [284, 295, 294, 380], [284, 369, 273, 361], [284, 369, 273, 272], [284, 283, 369, 272], [284, 283, 294, 369], [378, 283, 368, 369], [378, 283, 368, 377], [378, 283, 294, 369], [378, 294, 380, 369], [378, 445, 380, 369], [378, 283, 377, 293], [378, 283, 294, 293], [378, 379, 294, 380], [378, 379, 294, 293], [378, 445, 380, 454], [378, 379, 380, 454], [378, 388, 377, 293], [378, 379, 388, 293], [378, 379, 454, 388], [444, 434, 507, 436], [444, 445, 369, 436], [444, 359, 369, 436], [444, 368, 359, 369], [444, 378, 368, 369], [444, 378, 445, 369], [444, 378, 368, 377], [209, 294, 219, 293], [209, 219, 208, 293], [209, 198, 208, 293], [209, 283, 294, 293], [209, 283, 198, 293], [209, 198, 208, 133], [209, 208, 134, 133], [209, 219, 208, 134], [209, 283, 189, 198], [209, 134, 125, 133], [209, 124, 125, 133], [209, 189, 124, 125], [209, 198, 124, 133], [209, 189, 198, 124], [141, 69, 142, 132], [141, 217, 206, 132], [141, 217, 142, 132], [141, 69, 142, 78], [141, 69, 68, 78], [141, 206, 216, 140], [141, 217, 206, 216], [141, 217, 226, 216], [141, 76, 216, 140], [141, 85, 68, 78], [141, 85, 68, 76], [141, 76, 216, 149], [141, 226, 216, 149], [141, 142, 78, 150], [141, 217, 142, 150], [141, 217, 226, 150], [141, 85, 76, 149], [141, 85, 226, 149], [141, 85, 78, 150], [141, 85, 226, 150], [131, 59, 67, 68], [131, 59, 69, 68], [131, 141, 69, 68], [131, 67, 68, 76], [131, 141, 68, 76], [131, 59, 58, 122], [131, 59, 67, 58], [131, 59, 123, 122], [131, 59, 69, 123], [131, 67, 76, 140], [131, 141, 76, 140], [131, 141, 69, 132], [131, 130, 58, 122], [131, 130, 67, 58], [131, 123, 122, 132], [131, 69, 123, 132], [131, 130, 122, 140], [131, 130, 67, 140], [131, 141, 206, 140], [131, 206, 122, 132], [131, 141, 206, 132], [131, 205, 122, 140], [131, 205, 206, 140], [131, 205, 206, 122], [ 51, 59, 22, 123], [ 51, 59, 22, 8], [ 51, 22, 123, 52], [ 51, 22, 8, 52], [ 51, 59, 58, 123], [ 51, 114, 52, 46], [ 51, 114, 123, 52], [ 51, 5, 52, 46], [ 51, 8, 5, 52], [ 51, 114, 113, 46], [ 51, 114, 113, 123], [ 51, 58, 123, 122], [ 51, 113, 123, 122], [ 51, 113, 58, 122], [ 51, 59, 58, 13], [ 51, 59, 8, 13], [ 51, 45, 5, 46], [ 51, 45, 8, 5], [ 51, 113, 105, 46], [ 51, 45, 105, 46], [ 51, 45, 50, 8], [ 51, 50, 58, 13], [ 51, 50, 8, 13], [ 51, 113, 58, 112], [ 51, 50, 58, 112], [ 51, 113, 105, 112], [ 51, 45, 105, 112], [ 51, 45, 50, 112], [319, 224, 223, 232], [319, 224, 310, 223], [319, 225, 241, 232], [319, 224, 225, 232], [311, 385, 215, 300], [311, 385, 301, 215], [311, 224, 215, 300], [311, 224, 301, 215], [311, 394, 385, 300], [311, 224, 310, 300], [311, 402, 310, 393], [311, 394, 299, 393], [311, 394, 299, 300], [311, 394, 393, 468], [311, 394, 385, 468], [311, 319, 402, 310], [311, 319, 224, 310], [311, 310, 299, 393], [311, 310, 299, 300], [311, 301, 403, 386], [311, 385, 301, 386], [311, 403, 386, 468], [311, 385, 386, 468], [311, 467, 393, 468], [311, 402, 467, 393], [311, 403, 467, 468], [311, 402, 403, 467], [269, 365, 355, 278], [269, 268, 355, 278], [269, 268, 259, 278], [269, 279, 259, 278], [269, 365, 279, 278], [269, 185, 279, 259], [260, 252, 253, 339], [260, 261, 253, 339], [260, 177, 270, 261], [260, 177, 252, 253], [260, 177, 261, 253], [260, 268, 252, 339], [260, 268, 252, 259], [260, 269, 268, 259], [187, 113, 197, 179], [187, 113, 105, 179], [187, 177, 113, 105], [187, 270, 261, 262], [187, 177, 270, 261], [187, 178, 262, 179], [187, 178, 105, 179], [187, 178, 177, 105], [187, 178, 261, 262], [187, 178, 177, 261], [508, 500, 509, 446], [508, 517, 509, 446], [508, 517, 445, 446], [508, 500, 509, 564], [508, 517, 509, 564], [508, 445, 446, 436], [508, 500, 446, 436], [508, 500, 556, 564], [508, 500, 507, 556], [508, 444, 507, 436], [508, 444, 445, 436], [508, 507, 499, 436], [508, 500, 499, 436], [508, 500, 507, 499], [508, 517, 454, 526], [508, 517, 445, 454], [508, 556, 564, 516], [508, 507, 556, 516], [508, 564, 526, 516], [508, 517, 564, 526], [118, 183, 175, 109], [118, 117, 183, 109], [118, 110, 109, 55], [118, 117, 109, 55], [118, 117, 64, 55], [275, 267, 192, 183], [275, 353, 362, 361], [275, 285, 362, 361], [275, 353, 354, 266], [275, 353, 354, 362], [275, 284, 285, 361], [286, 363, 362, 371], [286, 285, 362, 371], [286, 275, 285, 362], [286, 202, 297, 201], [286, 295, 285, 201], [286, 296, 297, 371], [286, 296, 285, 371], [286, 296, 297, 201], [286, 296, 285, 201], [ 82, 80, 144, 89], [ 82, 154, 144, 89], [ 82, 135, 80, 72], [ 82, 135, 80, 144], [ 82, 154, 144, 145], [ 82, 135, 144, 145], [ 62, 61, 126, 116], [ 62, 54, 61, 126], [ 62, 126, 116, 55], [ 62, 54, 61, 53], [ 62, 61, 48, 116], [ 62, 61, 48, 53], [ 62, 48, 116, 55], [ 62, 54, 53, 11], [ 62, 12, 19, 55], [ 62, 18, 12, 11], [ 62, 54, 18, 11], [ 62, 48, 12, 7], [ 62, 48, 53, 7], [ 62, 12, 11, 7], [ 62, 53, 11, 7], [ 63, 25, 3, 72], [ 63, 25, 18, 3], [ 63, 54, 3, 72], [ 63, 54, 18, 3], [ 63, 54, 126, 72], [ 63, 25, 26, 18], [ 63, 62, 54, 18], [ 63, 62, 54, 126], [ 63, 26, 18, 19], [ 63, 26, 19, 64], [ 63, 18, 12, 19], [ 63, 62, 12, 19], [ 63, 62, 18, 12], [ 63, 62, 19, 55], [ 63, 62, 126, 55], [ 63, 19, 64, 55], [ 63, 126, 64, 55], [ 74, 136, 137, 145], [ 90, 154, 89, 95], [ 90, 82, 154, 89], [ 90, 154, 95, 162], [ 90, 154, 155, 162], [ 90, 82, 154, 145], [ 32, 31, 3, 72], [ 32, 25, 3, 72], [352, 345, 344, 423], [352, 353, 345, 423], [352, 345, 256, 344], [352, 351, 256, 273], [352, 351, 256, 344], [352, 428, 344, 423], [352, 351, 428, 344], [352, 351, 359, 273], [352, 360, 351, 359], [352, 360, 428, 423], [352, 360, 351, 428], [352, 437, 273, 361], [352, 353, 437, 361], [352, 437, 359, 273], [352, 360, 437, 359], [184, 110, 175, 109], [184, 118, 175, 109], [184, 118, 110, 109], [184, 267, 183, 175], [184, 118, 183, 175], [184, 267, 192, 183], [184, 118, 192, 183], [213, 212, 222, 298], [213, 297, 212, 298], [213, 202, 297, 298], [213, 297, 212, 201], [213, 202, 297, 201], [213, 212, 137, 146], [213, 212, 222, 146], [213, 136, 212, 201], [213, 136, 212, 137], [276, 275, 267, 192], [276, 354, 363, 362], [276, 286, 363, 362], [276, 286, 275, 192], [276, 275, 354, 266], [276, 275, 267, 266], [276, 275, 354, 362], [276, 286, 275, 362], [ 65, 74, 136, 137], [196, 198, 197, 132], [196, 207, 198, 132], [196, 207, 206, 132], [196, 198, 282, 197], [196, 207, 198, 282], [196, 206, 122, 132], [196, 197, 122, 132], [196, 205, 206, 122], [196, 207, 292, 282], [196, 367, 292, 282], [349, 426, 348, 425], [349, 433, 426, 425], [349, 426, 348, 350], [349, 270, 261, 262], [349, 350, 254, 262], [349, 348, 350, 254], [349, 271, 350, 262], [349, 261, 254, 262], [349, 261, 348, 254], [170, 178, 249, 253], [170, 177, 169, 253], [170, 178, 177, 253], [170, 178, 177, 169], [170, 169, 105, 99], [170, 178, 169, 105], [170, 100, 106, 99], [170, 100, 178, 106], [170, 106, 105, 99], [170, 178, 106, 105], [171, 100, 168, 172], [171, 250, 249, 254], [171, 250, 172, 254], [171, 178, 249, 254], [171, 178, 172, 254], [171, 170, 178, 249], [171, 100, 178, 172], [171, 170, 100, 178], [173, 171, 168, 172], [173, 171, 250, 172], [173, 250, 343, 255], [173, 251, 343, 255], [173, 250, 172, 255], [181, 182, 108, 174], [181, 251, 182, 174], [181, 182, 108, 264], [181, 182, 256, 264], [181, 251, 182, 256], [181, 168, 172, 107], [181, 173, 168, 172], [181, 108, 180, 264], [181, 256, 264, 255], [181, 251, 256, 255], [181, 172, 107, 180], [181, 108, 107, 180], [181, 173, 172, 255], [181, 173, 251, 255], [181, 264, 263, 255], [181, 180, 264, 263], [181, 172, 263, 255], [181, 172, 180, 263], [101, 44, 43, 47], [101, 44, 107, 47], [101, 42, 43, 47], [101, 100, 106, 42], [101, 106, 168, 107], [101, 100, 106, 168], [101, 106, 107, 47], [101, 106, 42, 47], [102, 181, 251, 174], [102, 101, 168, 107], [102, 101, 44, 107], [102, 181, 108, 174], [102, 44, 108, 107], [102, 181, 173, 168], [102, 181, 173, 251], [102, 181, 168, 107], [102, 181, 108, 107], [103, 110, 175, 109], [103, 102, 108, 174], [103, 102, 44, 108], [103, 182, 175, 109], [103, 182, 174, 175], [103, 182, 108, 109], [103, 182, 108, 174], [103, 108, 48, 109], [103, 44, 108, 48], [324, 237, 228, 316], [324, 236, 237, 228], [324, 236, 314, 228], [324, 236, 323, 314], [324, 315, 228, 316], [324, 314, 315, 228], [324, 236, 332, 237], [324, 323, 332, 325], [324, 236, 323, 332], [324, 315, 398, 316], [324, 406, 323, 314], [324, 398, 326, 316], [324, 398, 325, 326], [324, 406, 315, 398], [324, 406, 314, 315], [324, 237, 238, 316], [324, 332, 238, 325], [324, 332, 237, 238], [324, 238, 326, 316], [324, 238, 325, 326], [324, 407, 398, 325], [324, 406, 407, 398], [324, 323, 407, 325], [324, 406, 323, 407], [199, 189, 116, 125], [199, 117, 116, 125], [199, 189, 116, 190], [199, 117, 116, 190], [199, 209, 189, 125], [199, 284, 283, 294], [199, 284, 295, 294], [199, 189, 190, 272], [199, 209, 283, 294], [199, 209, 283, 189], [199, 283, 189, 272], [199, 284, 283, 272], [199, 117, 200, 125], [289, 365, 277, 364], [289, 384, 277, 364], [289, 384, 277, 288], [289, 394, 299, 300], [289, 394, 384, 299], [289, 204, 300, 288], [289, 365, 279, 277], [289, 299, 300, 288], [289, 384, 299, 288], [289, 277, 194, 288], [289, 279, 277, 194], [289, 204, 194, 288], [289, 279, 204, 194], [441, 450, 503, 504], [441, 450, 440, 503], [441, 503, 504, 431], [441, 440, 503, 431], [441, 432, 504, 431], [441, 440, 355, 431], [441, 365, 355, 364], [441, 440, 355, 364], [374, 394, 459, 373], [374, 450, 459, 373], [374, 394, 459, 460], [374, 450, 459, 460], [374, 384, 373, 364], [374, 394, 384, 373], [374, 289, 365, 364], [374, 440, 373, 364], [374, 450, 440, 373], [374, 441, 365, 364], [374, 394, 385, 460], [374, 289, 384, 364], [374, 289, 394, 384], [374, 441, 440, 364], [374, 441, 450, 440], [498, 507, 499, 556], [498, 562, 507, 556], [498, 499, 556, 549], [498, 555, 556, 549], [498, 562, 555, 556], [498, 562, 507, 506], [498, 555, 549, 554], [498, 562, 555, 554], [498, 434, 507, 506], [498, 434, 507, 499], [498, 434, 497, 506], [498, 549, 554, 548], [498, 545, 549, 548], [514, 562, 523, 561], [514, 515, 562, 523], [514, 567, 568, 561], [514, 523, 568, 561], [514, 531, 567, 568], [514, 523, 531, 568], [514, 560, 567, 561], [514, 531, 567, 522], [514, 523, 531, 524], [514, 515, 523, 524], [357, 347, 346, 424], [357, 432, 346, 424], [357, 347, 424, 425], [357, 432, 424, 425], [357, 347, 346, 339], [357, 347, 348, 425], [357, 349, 348, 425], [357, 432, 433, 425], [357, 349, 433, 425], [357, 260, 261, 339], [357, 349, 261, 348], [357, 261, 340, 339], [357, 347, 340, 339], [357, 260, 270, 261], [357, 349, 270, 261], [357, 261, 348, 340], [357, 347, 348, 340], [442, 451, 506, 504], [442, 497, 506, 504], [442, 433, 497, 506], [442, 432, 497, 504], [442, 432, 433, 497], [442, 441, 432, 504], [442, 450, 451, 504], [442, 441, 450, 504], [370, 447, 285, 381], [370, 455, 447, 381], [370, 285, 380, 381], [370, 455, 380, 381], [370, 447, 438, 362], [370, 447, 285, 362], [370, 447, 438, 510], [370, 455, 447, 510], [370, 284, 285, 361], [370, 284, 285, 380], [370, 438, 362, 361], [370, 285, 362, 361], [370, 437, 438, 361], [370, 438, 446, 510], [370, 455, 446, 510], [370, 284, 369, 361], [370, 284, 380, 369], [370, 437, 369, 361], [370, 445, 380, 369], [370, 455, 445, 380], [370, 437, 438, 446], [370, 445, 437, 369], [370, 445, 437, 446], [370, 455, 445, 446], [210, 211, 295, 200], [210, 199, 200, 125], [210, 199, 295, 200], [210, 135, 200, 125], [210, 211, 135, 200], [210, 199, 295, 294], [210, 211, 295, 306], [210, 199, 209, 125], [210, 199, 209, 294], [210, 211, 220, 144], [210, 211, 135, 144], [210, 209, 294, 219], [210, 294, 219, 306], [210, 295, 294, 306], [210, 220, 219, 306], [210, 211, 220, 306], [210, 135, 134, 125], [210, 220, 219, 134], [210, 220, 144, 134], [210, 135, 144, 134], [210, 209, 134, 125], [210, 209, 219, 134], [188, 283, 189, 198], [188, 283, 271, 282], [188, 283, 198, 282], [188, 283, 271, 272], [188, 283, 189, 272], [188, 198, 282, 197], [188, 198, 124, 197], [188, 189, 198, 124], [188, 271, 180, 272], [188, 189, 180, 272], [188, 271, 262, 180], [188, 124, 197, 179], [188, 262, 180, 179], [188, 115, 180, 179], [188, 189, 115, 180], [188, 124, 115, 179], [188, 189, 124, 115], [188, 187, 262, 179], [188, 187, 197, 179], [274, 284, 273, 361], [274, 275, 284, 361], [274, 273, 264, 272], [274, 284, 273, 272], [274, 182, 190, 264], [274, 182, 183, 190], [274, 190, 264, 272], [274, 117, 183, 190], [274, 199, 117, 190], [274, 275, 284, 285], [274, 199, 190, 272], [274, 199, 284, 272], [435, 444, 368, 359], [435, 434, 433, 426], [435, 444, 359, 436], [435, 444, 434, 436], [435, 271, 350, 359], [435, 271, 368, 359], [435, 349, 271, 350], [435, 349, 426, 350], [435, 349, 433, 426], [435, 493, 359, 436], [435, 434, 493, 436], [435, 350, 359, 427], [435, 426, 350, 427], [435, 493, 359, 427], [435, 426, 493, 427], [443, 434, 507, 506], [443, 444, 434, 507], [443, 515, 507, 506], [443, 434, 433, 506], [443, 515, 451, 452], [443, 515, 451, 506], [443, 442, 433, 506], [443, 442, 451, 506], [443, 367, 377, 452], [443, 367, 451, 452], [443, 442, 367, 451], [320, 319, 241, 328], [320, 319, 225, 241], [320, 321, 225, 241], [320, 329, 241, 328], [320, 321, 329, 328], [320, 321, 329, 241], [320, 224, 301, 225], [320, 319, 224, 225], [320, 301, 225, 312], [320, 321, 225, 312], [320, 311, 301, 403], [320, 311, 224, 301], [320, 311, 319, 224], [320, 395, 403, 328], [320, 395, 321, 328], [320, 395, 301, 403], [320, 395, 301, 312], [320, 395, 321, 312], [320, 402, 403, 328], [320, 311, 402, 403], [320, 319, 402, 328], [320, 311, 319, 402], [290, 289, 365, 279], [290, 374, 289, 365], [290, 205, 279, 204], [290, 289, 279, 204], [290, 374, 394, 385], [290, 374, 289, 394], [290, 205, 204, 215], [290, 385, 215, 300], [290, 394, 385, 300], [290, 289, 394, 300], [290, 204, 215, 300], [290, 289, 204, 300], [195, 122, 121, 112], [195, 187, 177, 270], [195, 205, 122, 121], [195, 205, 279, 121], [195, 177, 113, 112], [195, 187, 177, 113], [195, 113, 122, 112], [195, 196, 205, 122], [195, 113, 197, 122], [195, 187, 113, 197], [195, 196, 197, 122], [186, 260, 269, 259], [186, 260, 252, 259], [186, 260, 177, 252], [186, 260, 177, 270], [186, 260, 269, 270], [186, 252, 259, 176], [186, 177, 252, 176], [186, 185, 259, 176], [186, 269, 185, 259], [186, 195, 177, 270], [186, 195, 269, 270], [186, 177, 176, 112], [186, 195, 177, 112], [186, 111, 185, 176], [186, 111, 185, 121], [186, 185, 279, 121], [186, 269, 185, 279], [186, 195, 121, 112], [186, 195, 279, 121], [186, 195, 269, 279], [186, 111, 176, 112], [186, 111, 121, 112], [453, 508, 444, 507], [453, 508, 526, 516], [453, 508, 507, 516], [453, 508, 454, 526], [453, 443, 444, 507], [453, 508, 445, 454], [453, 508, 444, 445], [453, 515, 507, 516], [453, 462, 454, 526], [453, 443, 515, 452], [453, 443, 515, 507], [453, 443, 377, 452], [453, 443, 444, 377], [453, 378, 445, 454], [453, 444, 378, 445], [453, 524, 526, 516], [453, 462, 524, 526], [453, 515, 462, 452], [453, 462, 388, 452], [453, 388, 377, 452], [453, 444, 378, 377], [453, 515, 524, 516], [453, 515, 462, 524], [453, 454, 388, 389], [453, 462, 388, 389], [453, 462, 454, 389], [453, 378, 388, 377], [453, 378, 454, 388], [191, 118, 192, 183], [191, 275, 192, 183], [191, 118, 117, 183], [191, 286, 275, 192], [191, 274, 117, 183], [191, 274, 275, 183], [191, 274, 199, 117], [191, 274, 275, 285], [191, 286, 295, 285], [191, 286, 275, 285], [191, 199, 295, 200], [191, 199, 117, 200], [191, 199, 284, 295], [191, 274, 199, 284], [191, 284, 295, 285], [191, 274, 284, 285], [191, 295, 200, 201], [191, 286, 295, 201], [ 73, 74, 25, 26], [ 73, 63, 26, 64], [ 73, 63, 25, 26], [ 73, 65, 26, 64], [ 73, 65, 74, 26], [ 73, 65, 136, 64], [ 73, 65, 74, 136], [ 73, 63, 126, 64], [ 73, 63, 25, 72], [ 73, 136, 200, 64], [ 73, 135, 136, 200], [ 73, 135, 136, 145], [ 73, 74, 136, 145], [ 73, 82, 25, 72], [ 73, 82, 135, 72], [ 73, 126, 200, 64], [ 73, 135, 126, 200], [ 73, 135, 126, 72], [ 73, 63, 126, 72], [ 73, 82, 135, 145], [ 38, 89, 41, 95], [ 38, 90, 89, 95], [ 38, 90, 82, 89], [ 33, 73, 82, 25], [ 33, 73, 74, 25], [ 33, 82, 25, 72], [ 33, 32, 25, 72], [ 33, 32, 82, 72], [ 33, 38, 90, 82], [ 33, 38, 32, 82], [ 33, 73, 82, 145], [ 33, 73, 74, 145], [429, 352, 360, 423], [429, 352, 360, 437], [429, 360, 495, 423], [429, 495, 502, 423], [429, 360, 494, 495], [429, 360, 437, 494], [429, 352, 353, 423], [429, 352, 353, 437], [429, 495, 502, 552], [429, 494, 495, 552], [429, 430, 502, 423], [429, 430, 438, 502], [429, 353, 430, 423], [429, 353, 430, 438], [429, 437, 438, 361], [429, 353, 438, 361], [429, 353, 437, 361], [429, 494, 501, 552], [429, 437, 494, 501], [429, 502, 501, 552], [429, 438, 502, 501], [429, 437, 438, 501], [ 56, 184, 118, 110], [ 56, 110, 19, 55], [ 56, 118, 110, 55], [ 56, 26, 19, 64], [ 56, 65, 26, 64], [ 56, 19, 64, 55], [ 56, 118, 64, 55], [287, 276, 286, 363], [287, 372, 363, 371], [287, 286, 363, 371], [287, 372, 202, 297], [287, 286, 202, 297], [287, 372, 297, 371], [287, 286, 297, 371], [193, 184, 267, 192], [193, 276, 267, 192], [193, 287, 286, 202], [193, 276, 286, 192], [193, 287, 276, 286], [193, 286, 202, 201], [291, 367, 376, 292], [291, 196, 367, 292], [291, 376, 292, 302], [291, 207, 292, 302], [291, 196, 207, 292], [291, 207, 206, 302], [291, 196, 207, 206], [291, 385, 376, 302], [291, 385, 215, 302], [291, 290, 385, 215], [291, 215, 206, 302], [291, 196, 205, 206], [291, 205, 215, 206], [291, 290, 205, 215], [281, 196, 367, 282], [281, 187, 270, 262], [281, 349, 270, 262], [281, 196, 282, 197], [281, 195, 187, 270], [281, 188, 271, 282], [281, 349, 271, 262], [281, 188, 282, 197], [281, 188, 187, 197], [281, 195, 187, 197], [281, 195, 196, 197], [281, 188, 271, 262], [281, 188, 187, 262], [ 49, 12, 19, 55], [ 49, 110, 19, 55], [ 49, 103, 44, 48], [ 49, 103, 110, 109], [ 49, 103, 48, 109], [ 49, 48, 12, 7], [ 49, 44, 48, 7], [ 49, 62, 12, 55], [ 49, 62, 48, 55], [ 49, 62, 48, 12], [ 49, 110, 109, 55], [ 49, 48, 109, 55], [492, 498, 434, 499], [492, 434, 499, 493], [492, 545, 499, 549], [492, 498, 499, 549], [492, 498, 545, 549], [492, 498, 434, 497], [492, 499, 546, 493], [492, 545, 499, 546], [492, 498, 497, 548], [492, 498, 545, 548], [492, 546, 493, 489], [492, 545, 546, 489], [492, 426, 493, 489], [492, 435, 426, 493], [492, 435, 434, 493], [492, 435, 434, 426], [492, 497, 425, 491], [492, 433, 497, 425], [492, 434, 433, 497], [492, 497, 491, 548], [492, 545, 491, 548], [492, 426, 489, 488], [492, 545, 489, 488], [492, 426, 425, 488], [492, 433, 426, 425], [492, 434, 433, 426], [492, 425, 491, 488], [492, 545, 491, 488], [505, 514, 562, 561], [505, 515, 562, 506], [505, 514, 515, 562], [505, 554, 553, 561], [505, 562, 554, 561], [505, 515, 451, 506], [505, 514, 515, 451], [505, 498, 562, 554], [505, 498, 562, 506], [505, 560, 553, 561], [505, 560, 504, 553], [505, 514, 560, 561], [505, 497, 504, 553], [505, 497, 506, 504], [505, 451, 506, 504], [505, 554, 553, 548], [505, 498, 554, 548], [505, 498, 497, 506], [505, 497, 553, 548], [505, 498, 497, 548], [461, 514, 531, 522], [461, 514, 531, 524], [461, 470, 531, 524], [461, 531, 522, 469], [461, 470, 531, 469], [461, 514, 515, 524], [461, 514, 515, 451], [461, 515, 524, 452], [461, 515, 451, 452], [461, 462, 524, 452], [461, 470, 462, 524], [461, 460, 522, 469], [461, 386, 460, 469], [461, 386, 396, 469], [461, 470, 396, 469], [461, 376, 387, 452], [461, 451, 376, 452], [461, 451, 376, 460], [461, 462, 387, 452], [461, 470, 462, 387], [461, 386, 387, 396], [461, 470, 387, 396], [461, 376, 386, 460], [461, 376, 386, 387], [513, 512, 567, 522], [513, 514, 567, 522], [513, 512, 560, 567], [513, 514, 560, 567], [513, 512, 459, 522], [513, 512, 560, 449], [513, 459, 460, 522], [513, 450, 459, 460], [513, 512, 459, 449], [513, 450, 459, 449], [513, 560, 504, 449], [513, 450, 504, 449], [513, 450, 451, 460], [513, 461, 514, 522], [513, 461, 514, 451], [513, 505, 514, 560], [513, 505, 514, 451], [513, 505, 560, 504], [513, 461, 460, 522], [513, 461, 451, 460], [513, 450, 451, 504], [513, 505, 451, 504], [375, 367, 451, 376], [375, 442, 367, 451], [375, 451, 376, 460], [375, 450, 451, 460], [375, 442, 450, 451], [375, 374, 450, 460], [375, 291, 367, 376], [375, 385, 376, 460], [375, 374, 385, 460], [375, 290, 374, 385], [375, 374, 441, 450], [375, 442, 441, 450], [375, 291, 385, 376], [375, 291, 290, 385], [375, 374, 441, 365], [375, 290, 374, 365], [366, 442, 441, 432], [366, 375, 442, 441], [366, 357, 432, 433], [366, 442, 432, 433], [366, 375, 442, 367], [366, 357, 349, 270], [366, 357, 349, 433], [366, 375, 441, 365], [366, 281, 349, 270], [265, 353, 345, 266], [265, 275, 353, 266], [265, 275, 353, 361], [265, 274, 275, 361], [265, 345, 258, 266], [265, 352, 345, 256], [265, 352, 353, 345], [265, 274, 275, 183], [265, 274, 273, 361], [265, 267, 258, 266], [265, 275, 267, 266], [265, 352, 256, 273], [265, 182, 256, 264], [265, 274, 182, 264], [265, 256, 273, 264], [265, 274, 273, 264], [265, 182, 183, 258], [265, 274, 182, 183], [265, 267, 183, 258], [265, 275, 267, 183], [265, 352, 273, 361], [265, 352, 353, 361], [265, 345, 258, 257], [265, 345, 256, 257], [265, 182, 258, 257], [265, 182, 256, 257], [358, 435, 444, 434], [358, 443, 444, 434], [358, 435, 434, 433], [358, 443, 434, 433], [358, 435, 444, 368], [358, 435, 349, 433], [358, 444, 368, 377], [358, 443, 444, 377], [358, 435, 271, 368], [358, 435, 349, 271], [358, 368, 282, 377], [358, 271, 368, 282], [358, 366, 349, 433], [358, 367, 282, 377], [358, 443, 367, 377], [358, 366, 281, 367], [358, 366, 281, 349], [358, 443, 442, 433], [358, 366, 442, 433], [358, 443, 442, 367], [358, 366, 442, 367], [358, 281, 349, 271], [358, 281, 271, 282], [358, 281, 367, 282], [280, 269, 365, 279], [280, 290, 365, 279], [280, 290, 205, 279], [280, 375, 290, 365], [280, 195, 205, 279], [280, 195, 269, 279], [280, 366, 375, 365], [280, 375, 291, 290], [280, 291, 290, 205], [280, 195, 196, 205], [280, 291, 196, 205], [280, 375, 291, 367], [280, 366, 375, 367], [280, 195, 269, 270], [280, 366, 269, 270], [280, 291, 196, 367], [280, 281, 195, 196], [280, 281, 195, 270], [280, 366, 281, 270], [280, 281, 196, 367], [280, 366, 281, 367], [127, 191, 118, 192], [127, 191, 286, 201], [127, 191, 286, 192], [127, 191, 200, 201], [127, 193, 286, 201], [127, 193, 286, 192], [127, 191, 117, 200], [127, 191, 118, 117], [127, 136, 200, 64], [127, 136, 200, 201], [127, 117, 200, 64], [127, 118, 117, 64], [127, 65, 136, 64], [ 81, 38, 32, 37], [ 81, 38, 32, 82], [ 81, 24, 80, 30], [ 81, 24, 80, 72], [ 81, 32, 37, 31], [ 81, 88, 80, 89], [ 81, 82, 80, 89], [ 81, 38, 82, 89], [ 81, 38, 89, 41], [ 81, 38, 37, 41], [ 81, 82, 80, 72], [ 81, 32, 82, 72], [ 81, 88, 80, 30], [ 81, 88, 37, 30], [ 81, 24, 31, 30], [ 81, 37, 31, 30], [ 81, 24, 31, 72], [ 81, 32, 31, 72], [ 81, 88, 89, 41], [ 81, 88, 37, 41], [ 83, 33, 74, 145], [ 83, 90, 82, 145], [ 83, 33, 82, 145], [ 83, 33, 90, 82], [ 83, 74, 137, 145], [ 83, 154, 155, 145], [ 83, 90, 154, 145], [ 83, 90, 154, 155], [ 83, 137, 146, 145], [ 83, 155, 146, 145], [128, 213, 202, 201], [128, 193, 202, 201], [128, 127, 193, 201], [128, 213, 136, 137], [128, 65, 136, 137], [128, 127, 65, 136], [128, 213, 136, 201], [128, 127, 136, 201], [119, 128, 127, 65], [119, 128, 127, 193], [119, 56, 65, 64], [119, 127, 65, 64], [119, 56, 184, 118], [119, 193, 184, 192], [119, 127, 193, 192], [119, 56, 118, 64], [119, 127, 118, 64], [119, 184, 118, 192], [119, 127, 118, 192], [356, 366, 441, 432], [356, 366, 357, 432], [356, 357, 432, 346], [356, 432, 346, 431], [356, 441, 432, 431], [356, 346, 355, 431], [356, 441, 355, 431], [356, 366, 441, 365], [356, 366, 269, 270], [356, 366, 357, 270], [356, 268, 346, 355], [356, 269, 268, 355], [356, 269, 365, 355], [356, 441, 365, 355], [356, 280, 269, 365], [356, 280, 366, 365], [356, 280, 366, 269], [356, 260, 269, 268], [356, 260, 269, 270], [356, 357, 260, 270], [356, 268, 346, 339], [356, 260, 268, 339], [356, 357, 346, 339], [356, 357, 260, 339] ], dtype='int64') yt-project-yt-f043ac8/yt/frontends/stream/tests/000077500000000000000000000000001510711153200217455ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/stream/tests/__init__.py000066400000000000000000000000001510711153200240440ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/stream/tests/test_callable_grids.py000066400000000000000000000105401510711153200263050ustar00rootroot00000000000000import numpy as np import pytest import unyt from numpy.testing import assert_almost_equal, assert_equal from yt import load_amr_grids, load_hdf5_file, load_uniform_grid from yt.testing import _amr_grid_index, requires_file, requires_module turb_vels = "UnigridData/turb_vels.h5" _existing_fields = ( "Bx", "By", "Bz", "Density", "MagneticEnergy", "Temperature", "turb_x-velocity", "turb_y-velocity", "turb_z-velocity", "x-velocity", "y-velocity", "z-velocity", ) @requires_file(turb_vels) @requires_module("h5py") def test_load_hdf5_file(): ds1 = load_hdf5_file(turb_vels) assert_equal(ds1.domain_dimensions, [256, 256, 256]) for field_name in _existing_fields: assert ("stream", field_name) in ds1.field_list assert_equal(ds1.r[:]["ones"].size, 256 * 256 * 256) assert_equal(ds1.r[:]["Density"].size, 256 * 256 * 256) # Now we test that we get the same results regardless of our decomp ds2 = load_hdf5_file(turb_vels, nchunks=19) assert_equal(ds2.domain_dimensions, [256, 256, 256]) assert_equal(ds2.r[:]["ones"].size, 256 * 256 * 256) assert_equal(ds2.r[:]["Density"].size, 256 * 256 * 256) assert_almost_equal(ds2.r[:]["Density"].min(), ds1.r[:]["Density"].min()) assert_almost_equal(ds2.r[:]["Density"].max(), ds1.r[:]["Density"].max()) assert_almost_equal(ds2.r[:]["Density"].std(), ds1.r[:]["Density"].std()) # test that we can load this dataset with a different bounding box and length units ds3 = load_hdf5_file( turb_vels, bbox=np.array([[-1.0, 1.0], [-1.0, 1.0], [-1.0, 1.0]]), dataset_arguments={"length_unit": (1.0, "kpc")}, ) assert_almost_equal(ds3.domain_width, ds3.arr([2, 2, 2], "kpc")) _x_coefficients = (100, 50, 30, 10, 20) _y_coefficients = (20, 90, 80, 30, 30) _z_coefficients = (50, 10, 90, 40, 40) def _grid_data_function(grid, field_name): # We want N points from the cell-center to the cell-center on the other side x, y, z = ( np.linspace( grid.LeftEdge[i] + grid.dds[i] / 2, grid.RightEdge[i] - grid.dds[i] / 2, grid.ActiveDimensions[i], ) for i in (0, 1, 2) ) r = np.sqrt( ((x.d - 0.5) ** 2)[:, None, None] + ((y.d - 0.5) ** 2)[None, :, None] + ((z.d - 0.5) ** 2)[None, None, :] ) atten = np.exp(-20 * (1.1 * r**2)) xv = sum( c * np.sin(2 ** (1 + i) * (x.d * np.pi * 2)) for i, c in enumerate(_x_coefficients) ) yv = sum( c * np.sin(2 ** (1 + i) * (y.d * np.pi * 2)) for i, c in enumerate(_y_coefficients) ) zv = sum( c * np.sin(2 ** (1 + i) * (z.d * np.pi * 2)) for i, c in enumerate(_z_coefficients) ) return atten * (xv[:, None, None] * yv[None, :, None] * zv[None, None, :]) def test_load_callable(): grid_data = [] for level, le, re, dims in _amr_grid_index: grid_data.append( { "level": level, "left_edge": le, "right_edge": re, "dimensions": dims, "density": _grid_data_function, } ) ds = load_amr_grids( grid_data, [32, 32, 32], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) ) assert_equal(ds.r[:].sum("cell_volume"), ds.domain_width.prod()) assert_almost_equal(ds.r[:].max("density").d, 2660218.62833899) assert_almost_equal(ds.r[:].min("density").d, -2660218.62833899) def test_load_uniform_grid_callable(): data = {"density": _grid_data_function, "my_temp": (_grid_data_function, "K")} ds = load_uniform_grid( data, [32, 32, 32], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) ) assert_equal(ds.r[:].sum("cell_volume"), ds.domain_width.prod()) # note: the following min/max values differ from test_load_callable because # the grid here is coarser and the min/max values of the function are not # well-sampled. assert_almost_equal(ds.r[:].max("density").d, 1559160.37194738) assert_almost_equal(ds.r[:].min("density").d, -1559160.37194738) assert ds.r[:].min("my_temp").units == unyt.K with pytest.raises(RuntimeError, match="Callable functions can not be specified"): _ = load_uniform_grid( data, [32, 32, 32], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), nprocs=16, ) yt-project-yt-f043ac8/yt/frontends/stream/tests/test_outputs.py000066400000000000000000000046511510711153200251070ustar00rootroot00000000000000import os import shutil import tempfile import unittest import numpy as np from numpy.testing import assert_equal, assert_raises from yt.loaders import load_particles, load_uniform_grid from yt.utilities.exceptions import ( YTInconsistentGridFieldShape, YTInconsistentGridFieldShapeGridDims, YTInconsistentParticleFieldShape, ) class TestEmptyLoad(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) # create 0 byte file open("empty_file", "a") # create empty directory os.makedirs("empty_directory") def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_dimensionless_field_units(): Z = np.random.uniform(size=(32, 32, 32)) d = np.random.uniform(size=(32, 32, 32)) data = {"density": d, "metallicity": Z} ds = load_uniform_grid(data, (32, 32, 32)) dd = ds.all_data() assert_equal(Z.max(), float(dd["stream", "metallicity"].max())) def test_inconsistent_field_shape(): def load_field_field_mismatch(): d = np.random.uniform(size=(32, 32, 32)) t = np.random.uniform(size=(32, 64, 32)) data = {"density": d, "temperature": t} load_uniform_grid(data, (32, 32, 32)) assert_raises(YTInconsistentGridFieldShape, load_field_field_mismatch) def load_field_grid_mismatch(): d = np.random.uniform(size=(32, 32, 32)) t = np.random.uniform(size=(32, 32, 32)) data = {"density": d, "temperature": t} load_uniform_grid(data, (32, 64, 32)) assert_raises(YTInconsistentGridFieldShapeGridDims, load_field_grid_mismatch) def load_particle_fields_mismatch(): x = np.random.uniform(size=100) y = np.random.uniform(size=100) z = np.random.uniform(size=200) data = { "particle_position_x": x, "particle_position_y": y, "particle_position_z": z, } load_particles(data) assert_raises(YTInconsistentParticleFieldShape, load_particle_fields_mismatch) def test_parameters(): # simple test to check that we can pass in parameters Z = np.random.uniform(size=(32, 32, 32)) d = np.random.uniform(size=(32, 32, 32)) data = {"density": d, "metallicity": Z} ds = load_uniform_grid(data, (32, 32, 32), parameters={"metadata_is_nice": True}) assert ds.parameters["metadata_is_nice"] yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_amrgrids.py000066400000000000000000000040461510711153200265450ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_raises from yt import ProjectionPlot, load_amr_grids from yt.utilities.exceptions import YTIllDefinedAMR, YTIntDomainOverflow def test_qt_overflow(): grid_data = [] grid_dict = {} grid_dict["left_edge"] = [-1.0, -1.0, -1.0] grid_dict["right_edge"] = [1.0, 1.0, 1.0] grid_dict["dimensions"] = [8, 8, 8] grid_dict["level"] = 0 grid_dict["density"] = np.ones((8, 8, 8)) grid_data.append(grid_dict) domain_dimensions = np.array([8, 8, 8]) spf = load_amr_grids(grid_data, domain_dimensions) def make_proj(): p = ProjectionPlot(spf, "x", [("gas", "density")], center="c", origin="native") return p assert_raises(YTIntDomainOverflow, make_proj) def test_refine_by(): grid_data = [] ref_by = 4 lo = 0.0 hi = 1.0 fine_grid_width = (hi - lo) / ref_by for level in range(2): grid_dict = {} grid_dict["left_edge"] = [0.0 + 0.5 * fine_grid_width * level] * 3 grid_dict["right_edge"] = [1.0 - 0.5 * fine_grid_width * level] * 3 grid_dict["dimensions"] = [8, 8, 8] grid_dict["level"] = level grid_dict["density"] = np.ones((8, 8, 8)) grid_data.append(grid_dict) domain_dimensions = np.array([8, 8, 8]) load_amr_grids(grid_data, domain_dimensions, refine_by=ref_by) def test_validation(): dims = np.array([4, 2, 4]) grid_data = [ { "left_edge": [0.0, 0.0, 0.0], "right_edge": [1.0, 1.0, 1.0], "level": 0, "dimensions": dims, }, { "left_edge": [0.25, 0.25, 0.25], "right_edge": [0.75, 0.75, 0.75], "level": 1, "dimensions": dims, }, ] bbox = np.array([[0, 1], [0, 1], [0, 1]]) def load_grids(): load_amr_grids( grid_data, dims, bbox=bbox, periodicity=(0, 0, 0), length_unit=1.0, refine_by=2, ) assert_raises(YTIllDefinedAMR, load_grids) yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_hexahedral.py000066400000000000000000000042311510711153200270360ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt import SlicePlot from yt.frontends.stream.data_structures import hexahedral_connectivity from yt.loaders import load_hexahedral_mesh # Field information def test_stream_hexahedral(): np.random.seed(0x4D3D3D3) Nx, Ny, Nz = 32, 18, 24 # Note what we're doing here -- we are creating a randomly spaced mesh, but # because of how the accumulate operation works, we also reset the leftmost # cell boundary to 0.0. cell_x = np.random.random(Nx + 1) cell_x /= cell_x.sum() cell_x = np.add.accumulate(cell_x) cell_x[0] = 0.0 cell_y = np.random.random(Ny + 1) cell_y /= cell_y.sum() cell_y = np.add.accumulate(cell_y) cell_y[0] = 0.0 cell_z = np.random.random(Nz + 1) cell_z /= cell_z.sum() cell_z = np.add.accumulate(cell_z) cell_z[0] = 0.0 coords, conn = hexahedral_connectivity(cell_x, cell_y, cell_z) data = {"random_field": np.random.random((Nx, Ny, Nz))} bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) ds = load_hexahedral_mesh(data, conn, coords, bbox=bbox) dd = ds.all_data() # raise RuntimeError assert_almost_equal(float(dd["gas", "cell_volume"].sum(dtype="float64")), 1.0) assert_equal(dd["index", "ones"].size, Nx * Ny * Nz) # Now we try it with a standard mesh cell_x = np.linspace(0.0, 1.0, Nx + 1) cell_y = np.linspace(0.0, 1.0, Ny + 1) cell_z = np.linspace(0.0, 1.0, Nz + 1) coords, conn = hexahedral_connectivity(cell_x, cell_y, cell_z) data = {"random_field": np.random.random((Nx, Ny, Nz))} bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) ds = load_hexahedral_mesh(data, conn, coords, bbox=bbox) dd = ds.all_data() assert_almost_equal(float(dd["gas", "cell_volume"].sum(dtype="float64")), 1.0) assert_equal(dd["index", "ones"].size, Nx * Ny * Nz) assert_almost_equal(dd["index", "dx"].to_ndarray(), 1.0 / Nx) assert_almost_equal(dd["index", "dy"].to_ndarray(), 1.0 / Ny) assert_almost_equal(dd["index", "dz"].to_ndarray(), 1.0 / Nz) s = SlicePlot(ds, "x", "random_field") s.render() s.frb["stream", "random_field"] yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_octree.py000066400000000000000000000016501510711153200262140ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal import yt OCT_MASK_LIST = [ 8, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, ] def test_octree(): # See Issue #1272 octree_mask = np.array(OCT_MASK_LIST, dtype=np.uint8) quantities = {} quantities["gas", "density"] = np.random.random((22, 1)) bbox = np.array([[-10.0, 10.0], [-10.0, 10.0], [-10.0, 10.0]]) ds = yt.load_octree( octree_mask=octree_mask, data=quantities, bbox=bbox, num_zones=1, partial_coverage=0, ) proj = ds.proj(("gas", "density"), "x") proj["gas", "density"] assert_equal(ds.r[:]["ones"].size, 22) rho1 = quantities["gas", "density"].ravel() rho2 = ds.r[:]["density"].copy() rho1.sort() rho2.sort() assert_equal(rho1, rho2) yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_particles.py000066400000000000000000000351521510711153200267250ustar00rootroot00000000000000import numpy as np import pytest from numpy.testing import assert_equal import yt.utilities.initial_conditions as ic from yt.loaders import load_amr_grids, load_particles, load_uniform_grid from yt.testing import fake_particle_ds, fake_sph_orientation_ds # Field information def test_stream_particles(): num_particles = 100000 domain_dims = (64, 64, 64) dens = np.random.random(domain_dims) x = np.random.uniform(size=num_particles) y = np.random.uniform(size=num_particles) z = np.random.uniform(size=num_particles) m = np.ones(num_particles) # Field operators and cell flagging methods fo = [] fo.append(ic.TopHatSphere(0.1, [0.2, 0.3, 0.4], {"density": 2.0})) fo.append(ic.TopHatSphere(0.05, [0.7, 0.4, 0.75], {"density": 20.0})) # Add particles fields1 = { "density": dens, "particle_position_x": x, "particle_position_y": y, "particle_position_z": z, "particle_mass": m, } fields2 = fields1.copy() ug1 = load_uniform_grid(fields1, domain_dims, 1.0) ug2 = load_uniform_grid(fields2, domain_dims, 1.0, nprocs=8) # Check to make sure the number of particles is the same number_of_particles1 = np.sum([grid.NumberOfParticles for grid in ug1.index.grids]) number_of_particles2 = np.sum([grid.NumberOfParticles for grid in ug2.index.grids]) assert_equal(number_of_particles1, num_particles) assert_equal(number_of_particles1, number_of_particles2) for grid in ug2.index.grids: tot_parts = grid["io", "particle_position_x"].size tot_all_parts = grid["all", "particle_position_x"].size assert tot_parts == grid.NumberOfParticles assert tot_all_parts == grid.NumberOfParticles # Check to make sure the fields have been defined correctly for ptype in ("all", "io"): assert ( ug1._get_field_info((ptype, "particle_position_x")).sampling_type == "particle" ) assert ( ug1._get_field_info((ptype, "particle_position_y")).sampling_type == "particle" ) assert ( ug1._get_field_info((ptype, "particle_position_z")).sampling_type == "particle" ) assert ug1._get_field_info((ptype, "particle_mass")).sampling_type == "particle" assert not ug1._get_field_info(("gas", "density")).sampling_type == "particle" for ptype in ("all", "io"): assert ( ug2._get_field_info((ptype, "particle_position_x")).sampling_type == "particle" ) assert ( ug2._get_field_info((ptype, "particle_position_y")).sampling_type == "particle" ) assert ( ug2._get_field_info((ptype, "particle_position_z")).sampling_type == "particle" ) assert ug2._get_field_info((ptype, "particle_mass")).sampling_type == "particle" assert not ug2._get_field_info(("gas", "density")).sampling_type == "particle" # Now perform similar checks, but with multiple particle types num_dm_particles = 30000 xd = np.random.uniform(size=num_dm_particles) yd = np.random.uniform(size=num_dm_particles) zd = np.random.uniform(size=num_dm_particles) md = np.ones(num_dm_particles) num_star_particles = 20000 xs = np.random.uniform(size=num_star_particles) ys = np.random.uniform(size=num_star_particles) zs = np.random.uniform(size=num_star_particles) ms = 2.0 * np.ones(num_star_particles) dens = np.random.random(domain_dims) fields3 = { "density": dens, ("dm", "particle_position_x"): xd, ("dm", "particle_position_y"): yd, ("dm", "particle_position_z"): zd, ("dm", "particle_mass"): md, ("star", "particle_position_x"): xs, ("star", "particle_position_y"): ys, ("star", "particle_position_z"): zs, ("star", "particle_mass"): ms, } fields4 = fields3.copy() ug3 = load_uniform_grid(fields3, domain_dims, 1.0) ug4 = load_uniform_grid(fields4, domain_dims, 1.0, nprocs=8) # Check to make sure the number of particles is the same number_of_particles3 = np.sum([grid.NumberOfParticles for grid in ug3.index.grids]) number_of_particles4 = np.sum([grid.NumberOfParticles for grid in ug4.index.grids]) assert_equal(number_of_particles3, num_dm_particles + num_star_particles) assert_equal(number_of_particles3, number_of_particles4) for grid in ug4.index.grids: tot_parts = grid["dm", "particle_position_x"].size tot_parts += grid["star", "particle_position_x"].size tot_all_parts = grid["all", "particle_position_x"].size assert tot_parts == grid.NumberOfParticles assert tot_all_parts == grid.NumberOfParticles # Check to make sure the fields have been defined correctly for ptype in ("dm", "star"): assert ( ug3._get_field_info((ptype, "particle_position_x")).sampling_type == "particle" ) assert ( ug3._get_field_info((ptype, "particle_position_y")).sampling_type == "particle" ) assert ( ug3._get_field_info((ptype, "particle_position_z")).sampling_type == "particle" ) assert ug3._get_field_info((ptype, "particle_mass")).sampling_type == "particle" assert ( ug4._get_field_info((ptype, "particle_position_x")).sampling_type == "particle" ) assert ( ug4._get_field_info((ptype, "particle_position_y")).sampling_type == "particle" ) assert ( ug4._get_field_info((ptype, "particle_position_z")).sampling_type == "particle" ) assert ug4._get_field_info((ptype, "particle_mass")).sampling_type == "particle" def test_load_particles_types(): num_particles = 10000 data1 = { "particle_position_x": np.random.random(size=num_particles), "particle_position_y": np.random.random(size=num_particles), "particle_position_z": np.random.random(size=num_particles), "particle_mass": np.ones(num_particles), } ds1 = load_particles(data1) ds1.index assert set(ds1.particle_types) == {"all", "io", "nbody"} dd = ds1.all_data() for ax in "xyz": assert dd["io", f"particle_position_{ax}"].size == num_particles assert dd["all", f"particle_position_{ax}"].size == num_particles assert dd["nbody", f"particle_position_{ax}"].size == num_particles num_dm_particles = 10000 num_star_particles = 50000 num_tot_particles = num_dm_particles + num_star_particles data2 = { ("dm", "particle_position_x"): np.random.random(size=num_dm_particles), ("dm", "particle_position_y"): np.random.random(size=num_dm_particles), ("dm", "particle_position_z"): np.random.random(size=num_dm_particles), ("dm", "particle_mass"): np.ones(num_dm_particles), ("star", "particle_position_x"): np.random.random(size=num_star_particles), ("star", "particle_position_y"): np.random.random(size=num_star_particles), ("star", "particle_position_z"): np.random.random(size=num_star_particles), ("star", "particle_mass"): 2.0 * np.ones(num_star_particles), } ds2 = load_particles(data2) ds2.index # We use set here because we don't care about the order and we just need # the elements to be correct assert set(ds2.particle_types) == {"all", "star", "dm", "nbody"} dd = ds2.all_data() for ax in "xyz": npart = 0 for ptype in ds2.particle_types_raw: npart += dd[ptype, f"particle_position_{ax}"].size assert npart == num_tot_particles assert dd["all", f"particle_position_{ax}"].size == num_tot_particles def test_load_particles_sph_types(): num_particles = 10000 data = { ("gas", "particle_position_x"): np.random.random(size=num_particles), ("gas", "particle_position_y"): np.random.random(size=num_particles), ("gas", "particle_position_z"): np.random.random(size=num_particles), ("gas", "particle_velocity_x"): np.random.random(size=num_particles), ("gas", "particle_velocity_y"): np.random.random(size=num_particles), ("gas", "particle_velocity_z"): np.random.random(size=num_particles), ("gas", "particle_mass"): np.ones(num_particles), ("gas", "density"): np.ones(num_particles), ("gas", "smoothing_length"): np.ones(num_particles), ("dm", "particle_position_x"): np.random.random(size=num_particles), ("dm", "particle_position_y"): np.random.random(size=num_particles), ("dm", "particle_position_z"): np.random.random(size=num_particles), ("dm", "particle_velocity_x"): np.random.random(size=num_particles), ("dm", "particle_velocity_y"): np.random.random(size=num_particles), ("dm", "particle_velocity_z"): np.random.random(size=num_particles), ("dm", "particle_mass"): np.ones(num_particles), } ds = load_particles(data) assert set(ds.particle_types) == {"gas", "dm"} assert ds._sph_ptypes == ("gas",) data.update( { ("cr_gas", "particle_position_x"): np.random.random(size=num_particles), ("cr_gas", "particle_position_y"): np.random.random(size=num_particles), ("cr_gas", "particle_position_z"): np.random.random(size=num_particles), ("cr_gas", "particle_velocity_x"): np.random.random(size=num_particles), ("cr_gas", "particle_velocity_y"): np.random.random(size=num_particles), ("cr_gas", "particle_velocity_z"): np.random.random(size=num_particles), ("cr_gas", "particle_mass"): np.ones(num_particles), ("cr_gas", "density"): np.ones(num_particles), ("cr_gas", "smoothing_length"): np.ones(num_particles), } ) with pytest.raises( ValueError, match="Multiple SPH particle types are currently not supported!" ): load_particles(data) def test_load_particles_with_data_source(): ds1 = fake_particle_ds() # Load from dataset ad = ds1.all_data() fields = [("all", "particle_mass")] fields += [("all", f"particle_position_{ax}") for ax in "xyz"] data = {field: ad[field] for field in fields} ds2 = load_particles(data, data_source=ad) def in_cgs(quan): return quan.in_cgs().v # Test bbox is parsed correctly for attr in ["domain_left_edge", "domain_right_edge"]: assert np.allclose(in_cgs(getattr(ds1, attr)), in_cgs(getattr(ds2, attr))) # Test sim_time is parsed correctly assert in_cgs(ds1.current_time) == in_cgs(ds2.current_time) # Test code units are parsed correctly def get_cu(ds, dim): return ds.quan(1, "code_" + dim) for dim in ["length", "mass", "time", "velocity", "magnetic"]: assert in_cgs(get_cu(ds1, dim)) == in_cgs(get_cu(ds2, dim)) def test_add_sph_fields(): ds = fake_particle_ds() ds.index assert set(ds.particle_types) == {"io", "all", "nbody"} ds.add_sph_fields() assert set(ds.particle_types) == {"io", "all"} assert ("io", "smoothing_length") in ds.field_list assert ("io", "density") in ds.field_list def test_particles_outside_domain(): np.random.seed(0x4D3D3D3) posx_arr = np.random.uniform(low=-1.6, high=1.5, size=1000) posy_arr = np.random.uniform(low=-1.5, high=1.5, size=1000) posz_arr = np.random.uniform(low=-1.5, high=1.5, size=1000) dens_arr = np.random.random((16, 16, 16)) data = { "density": dens_arr, "particle_position_x": posx_arr, "particle_position_y": posy_arr, "particle_position_z": posz_arr, } bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]]) ds = load_uniform_grid(data, (16, 16, 16), bbox=bbox, nprocs=4) wh = (posx_arr < bbox[0, 0]).nonzero()[0] assert wh.size == 1000 - ds.particle_type_counts["io"] ad = ds.all_data() assert ds.particle_type_counts["io"] == ad["all", "particle_position_x"].size def test_stream_sph_projection(): ds = fake_sph_orientation_ds() proj = ds.proj(("gas", "density"), 2) frb = proj.to_frb(ds.domain_width[0], (256, 256)) image = frb["gas", "density"] assert image.max() > 0 assert image.shape == (256, 256) @pytest.mark.parametrize("loader", (load_uniform_grid, load_amr_grids)) def test_stream_non_cartesian_particles(loader): eps = 1e-6 r, theta, phi = np.mgrid[ 0.0 : 1.0 - eps : 64j, 0.0 : np.pi - eps : 64j, 0.0 : 2.0 * np.pi - eps : 64j ] np.random.seed(0x4D3D3D3) ind = np.random.randint(0, 64 * 64 * 64, size=1000) particle_position_r = r.ravel()[ind] particle_position_theta = theta.ravel()[ind] particle_position_phi = phi.ravel()[ind] ds = load_uniform_grid( { "density": r, "temperature": phi, "entropy": phi, "particle_position_r": particle_position_r, "particle_position_theta": particle_position_theta, "particle_position_phi": particle_position_phi, }, (64, 64, 64), bbox=np.array([[0.0, 1.0], [0.0, np.pi], [0.0, 2.0 * np.pi]]), geometry="spherical", ) dd = ds.all_data() assert_equal(dd["all", "particle_position_r"].v, particle_position_r) assert_equal(dd["all", "particle_position_phi"].v, particle_position_phi) assert_equal(dd["all", "particle_position_theta"].v, particle_position_theta) def test_stream_non_cartesian_particles_amr(): eps = 1e-6 r, theta, phi = np.mgrid[ 0.0 : 1.0 - eps : 64j, 0.0 : np.pi - eps : 64j, 0.0 : 2.0 * np.pi - eps : 64j ] np.random.seed(0x4D3D3D3) ind = np.random.randint(0, 64 * 64 * 64, size=1000) particle_position_r = r.ravel()[ind] particle_position_theta = theta.ravel()[ind] particle_position_phi = phi.ravel()[ind] ds = load_amr_grids( [ { "density": r, "temperature": phi, "entropy": phi, "particle_position_r": particle_position_r, "particle_position_theta": particle_position_theta, "particle_position_phi": particle_position_phi, "dimensions": [64, 64, 64], "level": 0, "left_edge": [0.0, 0.0, 0.0], "right_edge": [1.0, np.pi, 2.0 * np.pi], } ], (64, 64, 64), bbox=np.array([[0.0, 1.0], [0.0, np.pi], [0.0, 2.0 * np.pi]]), geometry="spherical", ) dd = ds.all_data() assert_equal(dd["all", "particle_position_r"].v, particle_position_r) assert_equal(dd["all", "particle_position_phi"].v, particle_position_phi) assert_equal(dd["all", "particle_position_theta"].v, particle_position_theta) yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_species.py000066400000000000000000000021561510711153200263700ustar00rootroot00000000000000import numpy as np from yt.loaders import load_uniform_grid from yt.testing import assert_allclose_units def test_stream_species(): prng = np.random.default_rng(seed=42) arr = prng.uniform(size=(32, 32, 32)) data = { "density": (arr, "g/cm**3"), "H_p0_fraction": (0.37 * np.ones_like(arr), "dimensionless"), "H_p1_fraction": (0.37 * np.ones_like(arr), "dimensionless"), "He_fraction": (0.24 * np.ones_like(arr), "dimensionless"), "CO_fraction": (0.02 * np.ones_like(arr), "dimensionless"), } bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]]) ds = load_uniform_grid(data, arr.shape, length_unit="Mpc", bbox=bbox, nprocs=64) assert ("gas", "CO_density") in ds.derived_field_list assert ("gas", "H_nuclei_density") in ds.derived_field_list assert ("gas", "H_p0_number_density") in ds.derived_field_list dd = ds.all_data() assert_allclose_units(dd["gas", "CO_density"], 0.02 * dd["gas", "density"]) all_H = dd["gas", "H_p0_number_density"] + dd["gas", "H_p1_number_density"] assert_allclose_units(all_H, dd["gas", "H_nuclei_density"]) yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_stretched.py000066400000000000000000000103511510711153200267160ustar00rootroot00000000000000import numpy as np import pytest from numpy.testing import assert_almost_equal, assert_equal from yt import load_uniform_grid def test_variable_dx(): np.random.seed(0x4D3D3D3) data = {"density": np.random.random((128, 128, 128))} cell_widths = [] for _ in range(3): cw = np.random.random(128) cw /= cw.sum() cell_widths.append(cw) ds = load_uniform_grid( data, [128, 128, 128], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=cell_widths, ) # We now check that we get all of our original cell widths back out, and # only those cell widths assert_equal(np.unique(ds.index.grids[0]["index", "dx"]).size, 128) assert_equal(ds.index.grids[0]["index", "dx"][:, 0, 0], cell_widths[0]) assert_equal(np.unique(ds.index.grids[0]["index", "dx"]).size, 128) assert_equal(ds.index.grids[0]["index", "dy"][0, :, 0], cell_widths[1]) assert_equal(np.unique(ds.index.grids[0]["index", "dx"]).size, 128) assert_equal(ds.index.grids[0]["index", "dz"][0, 0, :], cell_widths[2]) assert_equal(np.unique(ds.index.grids[0]["index", "x"]).size, 128) center_x = np.add.accumulate(cell_widths[0]) - 0.5 * cell_widths[0] assert_equal(center_x, ds.index.grids[0]["index", "x"][:, 0, 0]) assert_equal(np.unique(ds.index.grids[0]["index", "y"]).size, 128) center_y = np.add.accumulate(cell_widths[1]) - 0.5 * cell_widths[1] assert_equal(center_y, ds.index.grids[0]["index", "y"][0, :, 0]) assert_equal(np.unique(ds.index.grids[0]["index", "z"]).size, 128) center_z = np.add.accumulate(cell_widths[2]) - 0.5 * cell_widths[2] assert_equal(center_z, ds.index.grids[0]["index", "z"][0, 0, :]) assert_almost_equal(ds.r[:].sum(("index", "cell_volume")), ds.domain_width.prod()) for ax in "xyz": dd = ds.all_data() p = dd.integrate("ones", axis=ax) assert_almost_equal(p["index", "ones"].max().d, 1.0) assert_almost_equal(p["index", "ones"].min().d, 1.0) @pytest.fixture def data_cell_widths_N16(): np.random.seed(0x4D3D3D3) N = 16 data = { "density": np.random.random((N, N, N)), "temperature": np.random.random((N, N, N)), } cell_widths = [] for _ in range(3): cw = np.random.random(N) cw /= cw.sum() cell_widths.append(cw) return (data, cell_widths) def test_cell_width_type(data_cell_widths_N16): # checks that cell widths are properly upcast to float64 (this errors # if that is not the case). data, cell_widths = data_cell_widths_N16 cell_widths = [cw.astype(np.float32) for cw in cell_widths] ds = load_uniform_grid( data, data["density"].shape, bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=cell_widths, ) _ = ds.slice(0, ds.domain_center[0])[("stream", "density")] def test_cell_width_dimensionality(data_cell_widths_N16): data, cell_widths = data_cell_widths_N16 # single np array in list should error with pytest.raises(ValueError, match="The number of elements in cell_widths"): _ = load_uniform_grid( data, data["density"].shape, bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=[cell_widths[0]], ) # mismatched shapes should error with pytest.raises(ValueError, match="The number of elements in cell_widths"): _ = load_uniform_grid( data, data["density"].shape, bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=[cell_widths[1:]], ) def test_cell_width_with_nproc(data_cell_widths_N16): data, cell_widths = data_cell_widths_N16 ds = load_uniform_grid( data, data["density"].shape, bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=cell_widths, nprocs=4, ) assert ds.index.num_grids == 4 # check that it successfully decomposed grid = ds.index.grids[0] n_cells = np.prod(grid.shape) assert n_cells == data["density"].size / 4 # and try a selection c = (grid.RightEdge + grid.LeftEdge) / 2.0 reg = ds.region(c, grid.LeftEdge, grid.RightEdge) assert reg["gas", "density"].size == n_cells yt-project-yt-f043ac8/yt/frontends/stream/tests/test_stream_unstructured.py000066400000000000000000000047071510711153200275100ustar00rootroot00000000000000import numpy as np from yt import SlicePlot, load_unstructured_mesh def test_multi_mesh(): coordsMulti = np.array( [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], dtype=np.float64 ) connect1 = np.array( [ [0, 1, 3], ], dtype=np.int64, ) connect2 = np.array( [ [1, 2, 3], ], dtype=np.int64, ) data1 = {} data2 = {} data1["connect1", "test"] = np.array( [ [0.0, 1.0, 3.0], ], dtype=np.float64, ) data2["connect2", "test"] = np.array( [ [1.0, 2.0, 3.0], ], dtype=np.float64, ) connectList = [connect1, connect2] dataList = [data1, data2] ds = load_unstructured_mesh(connectList, coordsMulti, dataList) sl = SlicePlot(ds, "z", ("connect1", "test")) assert sl.data_source.field_data["connect1", "test"].shape == (1, 3) sl = SlicePlot(ds, "z", ("connect2", "test")) assert sl.data_source.field_data["connect2", "test"].shape == (1, 3) sl = SlicePlot(ds, "z", ("all", "test")) assert sl.data_source.field_data["all", "test"].shape == (2, 3) sl.annotate_mesh_lines() def test_multi_field(): coords = np.array( [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], dtype=np.float64 ) connect = np.array([[0, 1, 3], [1, 2, 3]], dtype=np.int64) data = {} data["connect1", "test"] = np.array( [[0.0, 1.0, 3.0], [1.0, 2.0, 3.0]], dtype=np.float64 ) data["connect1", "testAgain"] = np.array( [[0.0, 1.0, 3.0], [1.0, 2.0, 3.0]], dtype=np.float64 ) ds = load_unstructured_mesh(connect, coords, data) sl = SlicePlot(ds, "z", ("connect1", "test")) sl.annotate_mesh_lines() sl = SlicePlot(ds, "z", ("connect1", "testAgain")) sl.annotate_mesh_lines() def test_units(): coords = np.array( [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], dtype=np.float64 ) connect = np.array([[0, 1, 3], [1, 2, 3]], dtype=np.int64) data = {} data["connect1", "density"] = ( np.array([[0.0, 1.0, 3.0], [1.0, 2.0, 3.0]], dtype=np.float64), "mp/cm**3", ) data["connect1", "testAgain"] = np.array( [[0.0, 1.0, 3.0], [1.0, 2.0, 3.0]], dtype=np.float64 ) ds = load_unstructured_mesh(connect, coords, data) ad = ds.all_data() ad["connect1", "density"].to("kg/m**3") # should work ad["connect1", "testAgain"].to("1") # should work yt-project-yt-f043ac8/yt/frontends/stream/tests/test_update_data.py000066400000000000000000000015561510711153200256400ustar00rootroot00000000000000import numpy as np from yt.data_objects.profiles import create_profile from yt.testing import fake_particle_ds, fake_random_ds def test_update_data_grid(): ds = fake_random_ds(64, nprocs=8) ds.index dims = (32, 32, 32) grid_data = [ {"temperature": np.random.uniform(size=dims)} for i in range(ds.index.num_grids) ] ds.index.update_data(grid_data) prj = ds.proj(("gas", "temperature"), 2) prj["gas", "temperature"] dd = ds.all_data() profile = create_profile(dd, ("gas", "density"), ("gas", "temperature"), 10) profile["gas", "temperature"] def test_update_data_particle(): npart = 100 ds = fake_particle_ds(npart=npart) part_data = {"temperature": np.random.rand(npart)} ds.index.update_data(part_data) assert ("io", "temperature") in ds.field_list dd = ds.all_data() dd["io", "temperature"] yt-project-yt-f043ac8/yt/frontends/swift/000077500000000000000000000000001510711153200204445ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/swift/__init__.py000066400000000000000000000000001510711153200225430ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/swift/api.py000066400000000000000000000002171510711153200215670ustar00rootroot00000000000000from yt.frontends.sph.fields import SPHFieldInfo from . import tests from .data_structures import SwiftDataset from .io import IOHandlerSwift yt-project-yt-f043ac8/yt/frontends/swift/data_structures.py000066400000000000000000000170541510711153200242410ustar00rootroot00000000000000import numpy as np from yt.data_objects.static_output import ParticleFile from yt.frontends.sph.data_structures import SPHDataset, SPHParticleIndex from yt.funcs import only_on_root from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from .fields import SwiftFieldInfo class SwiftParticleFile(ParticleFile): pass class SwiftDataset(SPHDataset): _load_requirements = ["h5py"] _index_class = SPHParticleIndex _field_info_class = SwiftFieldInfo _file_class = SwiftParticleFile _particle_mass_name = "Masses" _particle_coordinates_name = "Coordinates" _particle_velocity_name = "Velocities" _sph_ptypes = ("PartType0",) _suffix = ".hdf5" def __init__( self, filename, dataset_type="swift", storage_filename=None, units_override=None, unit_system="cgs", default_species_fields=None, ): super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, default_species_fields=default_species_fields, ) self.storage_filename = storage_filename self.refine_by = 1 def _set_code_unit_attributes(self): """ Sets the units from the SWIFT internal unit system. Currently sets length, mass, time, and temperature. SWIFT uses comoving coordinates without the usual h-factors. """ units = self._get_info_attributes("Units") if self.cosmological_simulation == 1: msg = "Assuming length units are in comoving centimetres" only_on_root(mylog.info, msg) self.length_unit = self.quan( float(units["Unit length in cgs (U_L)"]), "cmcm" ) else: msg = "Assuming length units are in physical centimetres" only_on_root(mylog.info, msg) self.length_unit = self.quan(float(units["Unit length in cgs (U_L)"]), "cm") self.mass_unit = self.quan(float(units["Unit mass in cgs (U_M)"]), "g") self.time_unit = self.quan(float(units["Unit time in cgs (U_t)"]), "s") self.temperature_unit = self.quan( float(units["Unit temperature in cgs (U_T)"]), "K" ) return def _get_info_attributes(self, dataset): """ Gets the information from a header-style dataset and returns it as a python dictionary. Example: self._get_info_attributes(header) returns a dictionary of all of the information in the Header.attrs. """ with h5py.File(self.filename, mode="r") as handle: header = dict(handle[dataset].attrs) return header def _parse_parameter_file(self): """ Parse the SWIFT "parameter file" -- really this actually reads info from the main HDF5 file as everything is replicated there and usually parameterfiles are not transported. The header information from the HDF5 file is stored in an un-parsed format in self.parameters should users wish to use it. """ # Read from the HDF5 file, this gives us all the info we need. The rest # of this function is just parsing. header = self._get_info_attributes("Header") # RuntimePars were removed from snapshots at SWIFT commit 6271388 # between SWIFT versions 0.8.5 and 0.9.0 with h5py.File(self.filename, mode="r") as handle: has_runtime_pars = "RuntimePars" in handle.keys() if has_runtime_pars: runtime_parameters = self._get_info_attributes("RuntimePars") else: runtime_parameters = {} policy = self._get_info_attributes("Policy") # These are the parameterfile parameters from *.yml at runtime parameters = self._get_info_attributes("Parameters") # Not used in this function, but passed to parameters hydro = self._get_info_attributes("HydroScheme") subgrid = self._get_info_attributes("SubgridScheme") self.domain_right_edge = header["BoxSize"] self.domain_left_edge = np.zeros_like(self.domain_right_edge) self.dimensionality = int(header["Dimension"]) # SWIFT is either all periodic, or not periodic at all if has_runtime_pars: periodic = int(runtime_parameters["PeriodicBoundariesOn"]) else: periodic = int(parameters["InitialConditions:periodic"]) if periodic: self._periodicity = [True] * self.dimensionality else: self._periodicity = [False] * self.dimensionality # Units get attached to this self.current_time = float(header["Time"]) # Now cosmology enters the fray, as a runtime parameter. self.cosmological_simulation = int(policy["cosmological integration"]) if self.cosmological_simulation: try: self.current_redshift = float(header["Redshift"]) # These won't be present if self.cosmological_simulation is false self.omega_lambda = float(parameters["Cosmology:Omega_lambda"]) # Cosmology:Omega_m parameter deprecated at SWIFT commit d2783c2 # Between SWIFT versions 0.9.0 and 1.0.0 if "Cosmology:Omega_cdm" in parameters: self.omega_matter = float(parameters["Cosmology:Omega_b"]) + float( parameters["Cosmology:Omega_cdm"] ) else: self.omega_matter = float(parameters["Cosmology:Omega_m"]) # This is "little h" self.hubble_constant = float(parameters["Cosmology:h"]) except KeyError: mylog.warning( "Could not find cosmology information in Parameters, " "despite having ran with -c signifying a cosmological " "run." ) mylog.info("Setting up as a non-cosmological run. Check this!") self.cosmological_simulation = 0 self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 else: self.current_redshift = 0.0 self.omega_lambda = 0.0 self.omega_matter = 0.0 self.hubble_constant = 0.0 # Store the un-parsed information should people want it. self.parameters = { "header": header, "policy": policy, "parameters": parameters, # NOTE: runtime_parameters may be empty "runtime_parameters": runtime_parameters, "hydro": hydro, "subgrid": subgrid, } # SWIFT never has multi file snapshots self.file_count = 1 self.filename_template = self.parameter_filename return @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: """ Checks to see if the file is a valid output from SWIFT. This requires the file to have the Code attribute set in the Header dataset to "SWIFT". """ if cls._missing_load_requirements(): return False valid = True # Attempt to open the file, if it's not a hdf5 then this will fail: try: handle = h5py.File(filename, mode="r") valid = handle["Header"].attrs["Code"].decode("utf-8") == "SWIFT" handle.close() except (OSError, KeyError): valid = False return valid yt-project-yt-f043ac8/yt/frontends/swift/fields.py000066400000000000000000000020401510711153200222600ustar00rootroot00000000000000from yt.frontends.sph.fields import SPHFieldInfo class SwiftFieldInfo(SPHFieldInfo): def __init__(self, ds, field_list, slice_info=None): self.known_particle_fields += ( ( "InternalEnergies", ("code_specific_energy", ["specific_thermal_energy"], None), ), ("Densities", ("code_mass / code_length**3", ["density"], None)), ("SmoothingLengths", ("code_length", ["smoothing_length"], None)), ) super().__init__(ds, field_list, slice_info) def setup_particle_fields(self, ptype, *args, **kwargs): super().setup_particle_fields(ptype, *args, **kwargs) if ptype in ("PartType0", "Gas"): self.setup_gas_particle_fields(ptype) def setup_gas_particle_fields(self, ptype): self.alias((ptype, "temperature"), (ptype, "Temperatures")) self.alias(("gas", "temperature"), (ptype, "Temperatures")) for ax in ("x", "y", "z"): self.alias((ptype, ax), (ptype, "particle_position_" + ax)) yt-project-yt-f043ac8/yt/frontends/swift/io.py000066400000000000000000000141601510711153200214270ustar00rootroot00000000000000import numpy as np from yt.frontends.sph.io import IOHandlerSPH from yt.utilities.on_demand_imports import _h5py as h5py class IOHandlerSwift(IOHandlerSPH): _dataset_type = "swift" def __init__(self, ds, *args, **kwargs): super().__init__(ds, *args, **kwargs) def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError # NOTE: we refer to sub_files in the next sections, these sub_files may # actually be full data_files. # In the event data_files are too big, yt breaks them up into sub_files and # we sort of treat them as files in the chunking system def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. # yt has the concept of sub_files, i.e, we break up big files into # virtual sub_files to deal with the chunking system for sub_file in self._sorted_chunk_iterator(chunks): si, ei = sub_file.start, sub_file.end f = h5py.File(sub_file.filename, mode="r") # This double-reads for ptype in sorted(ptf): if sub_file.total_particles[ptype] == 0: continue pos = f[f"/{ptype}/Coordinates"][si:ei, :] pos = pos.astype("float64", copy=False) if ptype == self.ds._sph_ptypes[0]: hsml = self._get_smoothing_length(sub_file) else: hsml = 0.0 yield ptype, (pos[:, 0], pos[:, 1], pos[:, 2]), hsml f.close() def _yield_coordinates(self, sub_file, needed_ptype=None): si, ei = sub_file.start, sub_file.end f = h5py.File(sub_file.filename, mode="r") pcount = f["/Header"].attrs["NumPart_ThisFile"][:].astype("int64") np.clip(pcount - si, 0, ei - si, out=pcount) pcount = pcount.sum() for key in f.keys(): if ( not key.startswith("PartType") or "Coordinates" not in f[key] or needed_ptype and key != needed_ptype ): continue pos = f[key]["Coordinates"][si:ei, ...] pos = pos.astype("float64", copy=False) yield key, pos f.close() def _get_smoothing_length(self, sub_file, pdtype=None, pshape=None): # We do not need the pdtype and the pshape, but some frontends do so we # accept them and then just ignore them ptype = self.ds._sph_ptypes[0] ind = int(ptype[-1]) si, ei = sub_file.start, sub_file.end with h5py.File(sub_file.filename, mode="r") as f: pcount = f["/Header"].attrs["NumPart_ThisFile"][ind].astype("int64") pcount = np.clip(pcount - si, 0, ei - si) keys = f[ptype].keys() # SWIFT commit a94cc81 changed from "SmoothingLength" to "SmoothingLengths" # between SWIFT versions 0.8.2 and 0.8.3 if "SmoothingLengths" in keys: hsml = f[ptype]["SmoothingLengths"][si:ei, ...] else: hsml = f[ptype]["SmoothingLength"][si:ei, ...] # we upscale to float64 hsml = hsml.astype("float64", copy=False) return hsml def _read_particle_data_file(self, sub_file, ptf, selector=None): # note: this frontend uses the variable name and terminology sub_file. # other frontends use data_file with the understanding that it may # actually be a sub_file, hence the super()._read_datafile is called # ._read_datafile instead of ._read_subfile return_data = {} si, ei = sub_file.start, sub_file.end f = h5py.File(sub_file.filename, mode="r") for ptype, field_list in sorted(ptf.items()): if sub_file.total_particles[ptype] == 0: continue g = f[f"/{ptype}"] # this should load as float64 coords = g["Coordinates"][si:ei] if ptype == "PartType0": hsmls = self._get_smoothing_length(sub_file) else: hsmls = 0.0 if selector: mask = selector.select_points( coords[:, 0], coords[:, 1], coords[:, 2], hsmls ) del coords if selector and mask is None: continue for field in field_list: if field in ("Mass", "Masses"): data = g[self.ds._particle_mass_name][si:ei] else: data = g[field][si:ei] if selector: data = data[mask, ...] data.astype("float64", copy=False) return_data[ptype, field] = data f.close() return return_data def _count_particles(self, data_file): si, ei = data_file.start, data_file.end f = h5py.File(data_file.filename, mode="r") pcount = f["/Header"].attrs["NumPart_ThisFile"][:].astype("int64") f.close() # if this data_file was a sub_file, then we just extract the region # defined by the subfile if None not in (si, ei): np.clip(pcount - si, 0, ei - si, out=pcount) npart = {f"PartType{i}": v for i, v in enumerate(pcount)} return npart def _identify_fields(self, data_file): f = h5py.File(data_file.filename, mode="r") fields = [] cname = self.ds._particle_coordinates_name # Coordinates mname = self.ds._particle_mass_name # Coordinates for key in f.keys(): if not key.startswith("PartType"): continue g = f[key] if cname not in g: continue ptype = str(key) for k in g.keys(): kk = k if str(kk) == mname: fields.append((ptype, "Mass")) continue if not hasattr(g[kk], "shape"): continue if len(g[kk].shape) > 1: self._vector_fields[kk] = g[kk].shape[1] fields.append((ptype, str(kk))) f.close() return fields, {} yt-project-yt-f043ac8/yt/frontends/swift/tests/000077500000000000000000000000001510711153200216065ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/swift/tests/__init__.py000066400000000000000000000000001510711153200237050ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/swift/tests/test_outputs.py000066400000000000000000000073551510711153200247540ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal from yt import load from yt.frontends.swift.api import SwiftDataset from yt.testing import ParticleSelectionComparison, requires_file, requires_module from yt.utilities.on_demand_imports import _h5py as h5py keplerian_ring = "KeplerianRing/keplerian_ring_0020.hdf5" EAGLE_6 = "EAGLE_6/eagle_0005.hdf5" # Combined the tests for loading a file and ensuring the units have been # implemented correctly to save time on re-loading a dataset @requires_module("h5py") @requires_file(keplerian_ring) def test_non_cosmo_dataset(): ds = load(keplerian_ring) assert type(ds) is SwiftDataset field = ("gas", "density") ad = ds.all_data() yt_density = ad[field] yt_coords = ad[field[0], "position"] # load some data the old fashioned way fh = h5py.File(ds.parameter_filename, mode="r") part_data = fh["PartType0"] # set up a conversion factor by loading the unit mas and unit length in cm, # and then converting to proper coordinates units = fh["Units"] units = dict(units.attrs) density_factor = float(units["Unit mass in cgs (U_M)"]) density_factor /= float(units["Unit length in cgs (U_L)"]) ** 3 # now load the raw density and coordinates raw_density = part_data["Density"][:].astype("float64") * density_factor raw_coords = part_data["Coordinates"][:].astype("float64") fh.close() # sort by the positions - yt often loads in a different order ind_raw = np.lexsort((raw_coords[:, 2], raw_coords[:, 1], raw_coords[:, 0])) ind_yt = np.lexsort((yt_coords[:, 2], yt_coords[:, 1], yt_coords[:, 0])) raw_density = raw_density[ind_raw] yt_density = yt_density[ind_yt] # make sure we are comparing fair units assert str(yt_density.units) == "g/cm**3" # make sure the actual values are the same assert_almost_equal(yt_density.d, raw_density) @requires_module("h5py") @requires_file(keplerian_ring) def test_non_cosmo_dataset_selection(): ds = load(keplerian_ring) psc = ParticleSelectionComparison(ds) psc.run_defaults() @requires_module("h5py") @requires_file(EAGLE_6) def test_cosmo_dataset(): ds = load(EAGLE_6) assert type(ds) is SwiftDataset field = ("gas", "density") ad = ds.all_data() yt_density = ad[field] yt_coords = ad[field[0], "position"] # load some data the old fashioned way fh = h5py.File(ds.parameter_filename, mode="r") part_data = fh["PartType0"] # set up a conversion factor by loading the unit mas and unit length in cm, # and then converting to proper coordinates units = fh["Units"] units = dict(units.attrs) density_factor = float(units["Unit mass in cgs (U_M)"]) density_factor /= float(units["Unit length in cgs (U_L)"]) ** 3 # add the redshift factor header = fh["Header"] header = dict(header.attrs) density_factor *= (1.0 + float(header["Redshift"])) ** 3 # now load the raw density and coordinates raw_density = part_data["Density"][:].astype("float64") * density_factor raw_coords = part_data["Coordinates"][:].astype("float64") fh.close() # sort by the positions - yt often loads in a different order ind_raw = np.lexsort((raw_coords[:, 2], raw_coords[:, 1], raw_coords[:, 0])) ind_yt = np.lexsort((yt_coords[:, 2], yt_coords[:, 1], yt_coords[:, 0])) raw_density = raw_density[ind_raw] yt_density = yt_density[ind_yt] # make sure we are comparing fair units assert str(yt_density.units) == "g/cm**3" # make sure the actual values are the same assert_almost_equal(yt_density.d, raw_density) @requires_module("h5py") @requires_file(EAGLE_6) def test_cosmo_dataset_selection(): ds = load(EAGLE_6) psc = ParticleSelectionComparison(ds) psc.run_defaults() yt-project-yt-f043ac8/yt/frontends/tipsy/000077500000000000000000000000001510711153200204605ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/tipsy/__init__.py000066400000000000000000000000001510711153200225570ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/tipsy/api.py000066400000000000000000000002061510711153200216010ustar00rootroot00000000000000from . import tests from .data_structures import TipsyDataset from .fields import TipsyFieldInfo from .io import IOHandlerTipsyBinary yt-project-yt-f043ac8/yt/frontends/tipsy/data_structures.py000066400000000000000000000320501510711153200242460ustar00rootroot00000000000000import glob import os import struct import numpy as np from yt.data_objects.static_output import ParticleFile from yt.frontends.sph.data_structures import SPHDataset, SPHParticleIndex from yt.utilities.cosmology import Cosmology from yt.utilities.physical_constants import G from yt.utilities.physical_ratios import cm_per_kpc from .fields import TipsyFieldInfo class TipsyFile(ParticleFile): def __init__(self, ds, io, filename, file_id, range=None): super().__init__(ds, io, filename, file_id, range) if not hasattr(io, "_field_list"): io._create_dtypes(self) # Check automatically what the domain size is io._update_domain(self) self._calculate_offsets(io._field_list) def _calculate_offsets(self, field_list, pcounts=None): self.field_offsets = self.io._calculate_particle_offsets(self, None) class TipsyDataset(SPHDataset): _index_class = SPHParticleIndex _file_class = TipsyFile _field_info_class = TipsyFieldInfo _particle_mass_name = "Mass" _particle_coordinates_name = "Coordinates" _sph_ptypes = ("Gas",) _header_spec = ( ("time", "d"), ("nbodies", "i"), ("ndim", "i"), ("nsph", "i"), ("ndark", "i"), ("nstar", "i"), ("dummy", "i"), ) def __init__( self, filename, dataset_type="tipsy", field_dtypes=None, unit_base=None, parameter_file=None, cosmology_parameters=None, index_order=None, index_filename=None, kdtree_filename=None, kernel_name=None, bounding_box=None, units_override=None, unit_system="cgs", default_species_fields=None, ): # Because Tipsy outputs don't have a fixed domain boundary, one can # specify a bounding box which effectively gives a domain_left_edge # and domain_right_edge self.bounding_box = bounding_box self.filter_bbox = bounding_box is not None if field_dtypes is None: field_dtypes = {} success, self.endian = self._validate_header(filename) if not success: print("SOMETHING HAS GONE WRONG. NBODIES != SUM PARTICLES.") print( "{} != (sum == {} + {} + {})".format( self.parameters["nbodies"], self.parameters["nsph"], self.parameters["ndark"], self.parameters["nstar"], ) ) print("Often this can be fixed by changing the 'endian' parameter.") print("This defaults to '>' but may in fact be '<'.") raise RuntimeError self.storage_filename = None # My understanding is that dtypes are set on a field by field basis, # not on a (particle type, field) basis self._field_dtypes = field_dtypes self._unit_base = unit_base or {} self._cosmology_parameters = cosmology_parameters if parameter_file is not None: parameter_file = os.path.abspath(parameter_file) self._param_file = parameter_file filename = os.path.abspath(filename) if units_override is not None: raise RuntimeError( "units_override is not supported for TipsyDataset. " + "Use unit_base instead." ) super().__init__( filename, dataset_type=dataset_type, unit_system=unit_system, index_order=index_order, index_filename=index_filename, kdtree_filename=kdtree_filename, kernel_name=kernel_name, default_species_fields=default_species_fields, ) def __str__(self): return os.path.basename(self.parameter_filename) def _parse_parameter_file(self): # Parsing the header of the tipsy file, from this we obtain # the snapshot time and particle counts. f = open(self.parameter_filename, "rb") hh = self.endian + "".join(str(b) for a, b in self._header_spec) hvals = { a: c for (a, b), c in zip( self._header_spec, struct.unpack(hh, f.read(struct.calcsize(hh))), strict=True, ) } self.parameters.update(hvals) self._header_offset = f.tell() # These are always true, for now. self.dimensionality = 3 self.refine_by = 2 self.parameters["HydroMethod"] = "sph" # Read in parameter file, if available. if self._param_file is None: pfn = glob.glob(os.path.join(self.directory, "*.param")) assert len(pfn) < 2, "More than one param file is in the data directory" if pfn == []: pfn = None else: pfn = pfn[0] else: pfn = self._param_file if pfn is not None: for line in (l.strip() for l in open(pfn)): # skip comment lines and blank lines l = line.strip() if l.startswith("#") or l == "": continue # parse parameters according to tipsy parameter type param, val = (i.strip() for i in line.split("=", 1)) val = val.split("#")[0] if param.startswith("n") or param.startswith("i"): val = int(val) elif param.startswith("d"): val = float(val) elif param.startswith("b"): val = bool(float(val)) self.parameters[param] = val self.current_time = hvals["time"] self.domain_dimensions = np.ones(3, "int32") periodic = self.parameters.get("bPeriodic", True) period = self.parameters.get("dPeriod", None) self._periodicity = (periodic, periodic, periodic) self.cosmological_simulation = float( self.parameters.get("bComove", self._cosmology_parameters is not None) ) if self.cosmological_simulation and period is None: period = 1.0 if self.bounding_box is None: if periodic and period is not None: # If we are periodic, that sets our domain width to # either 1 or dPeriod. self.domain_left_edge = np.zeros(3, "float64") - 0.5 * period self.domain_right_edge = np.zeros(3, "float64") + 0.5 * period else: self.domain_left_edge = None self.domain_right_edge = None else: # This ensures that we know a bounding box has been applied self._domain_override = True bbox = np.array(self.bounding_box, dtype="float64") if bbox.shape == (2, 3): bbox = bbox.transpose() self.domain_left_edge = bbox[:, 0] self.domain_right_edge = bbox[:, 1] # If the cosmology parameters dictionary got set when data is # loaded, we can assume it's a cosmological data set if self.cosmological_simulation == 1.0: cosm = self._cosmology_parameters or {} # In comoving simulations, time stores the scale factor a self.scale_factor = hvals["time"] dcosm = { "current_redshift": (1.0 / self.scale_factor) - 1.0, "omega_lambda": self.parameters.get( "dLambda", cosm.get("omega_lambda", 0.0) ), "omega_matter": self.parameters.get( "dOmega0", cosm.get("omega_matter", 0.0) ), "hubble_constant": self.parameters.get( "dHubble0", cosm.get("hubble_constant", 1.0) ), } for param in dcosm.keys(): pval = dcosm[param] setattr(self, param, pval) else: kpc_unit = self.parameters.get("dKpcUnit", 1.0) self._unit_base["cm"] = 1.0 / (kpc_unit * cm_per_kpc) self.filename_template = self.parameter_filename self.file_count = 1 f.close() def _set_derived_attrs(self): if self.bounding_box is None and ( self.domain_left_edge is None or self.domain_right_edge is None ): self.domain_left_edge = np.array([np.nan, np.nan, np.nan]) self.domain_right_edge = np.array([np.nan, np.nan, np.nan]) self.index super()._set_derived_attrs() def _set_code_unit_attributes(self): # First try to set units based on parameter file if self.cosmological_simulation: mu = self.parameters.get("dMsolUnit", 1.0) self.mass_unit = self.quan(mu, "Msun") lu = self.parameters.get("dKpcUnit", 1000.0) # In cosmological runs, lengths are stored as length*scale_factor self.length_unit = self.quan(lu, "kpc") * self.scale_factor density_unit = self.mass_unit / (self.length_unit / self.scale_factor) ** 3 if "dHubble0" in self.parameters: # Gasoline's internal hubble constant, dHubble0, is stored in # units of proper code time self.hubble_constant *= np.sqrt(G * density_unit) # Finally, we scale the hubble constant by 100 km/s/Mpc self.hubble_constant /= self.quan(100, "km/s/Mpc") # If we leave it as a YTQuantity, the cosmology object # used below will add units back on. self.hubble_constant = self.hubble_constant.to_value("") else: mu = self.parameters.get("dMsolUnit", 1.0) self.mass_unit = self.quan(mu, "Msun") lu = self.parameters.get("dKpcUnit", 1.0) self.length_unit = self.quan(lu, "kpc") # If unit base is defined by the user, override all relevant units if self._unit_base is not None: for my_unit in ["length", "mass", "time"]: if my_unit in self._unit_base: my_val = self._unit_base[my_unit] my_val = ( self.quan(*my_val) if isinstance(my_val, tuple) else self.quan(my_val) ) setattr(self, f"{my_unit}_unit", my_val) # Finally, set the dependent units if self.cosmological_simulation: cosmo = Cosmology( hubble_constant=self.hubble_constant, omega_matter=self.omega_matter, omega_lambda=self.omega_lambda, ) self.current_time = cosmo.lookback_time(self.current_redshift, 1e6) # mass units are rho_crit(z=0) * domain volume mu = ( cosmo.critical_density(0.0) * (1 + self.current_redshift) ** 3 * self.length_unit**3 ) self.mass_unit = self.quan(mu.in_units("Msun"), "Msun") density_unit = self.mass_unit / (self.length_unit / self.scale_factor) ** 3 # need to do this again because we've modified the hubble constant self.unit_registry.modify("h", self.hubble_constant) else: density_unit = self.mass_unit / self.length_unit**3 if not hasattr(self, "time_unit"): self.time_unit = 1.0 / np.sqrt(density_unit * G) @staticmethod def _validate_header(filename): """ This method automatically detects whether the tipsy file is big/little endian and is not corrupt/invalid. It returns a tuple of (Valid, endianswap) where Valid is a boolean that is true if the file is a tipsy file, and endianswap is the endianness character '>' or '<'. """ try: f = open(filename, "rb") except Exception: return False, 1 try: f.seek(0, os.SEEK_END) fs = f.tell() f.seek(0, os.SEEK_SET) # Read in the header t, n, ndim, ng, nd, ns = struct.unpack(" 3: endianswap = ">" f.seek(0) t, n, ndim, ng, nd, ns = struct.unpack(">diiiii", f.read(28)) # File is borked if this is true. The header is 28 bytes, and may # Be followed by a 4 byte pad. Next comes gas particles, which use # 48 bytes, followed by 36 bytes per dark matter particle, and 44 bytes # per star particle. If positions are stored as doubles, each of these # sizes is increased by 12 bytes. if ( fs != 28 + 48 * ng + 36 * nd + 44 * ns and fs != 28 + 60 * ng + 48 * nd + 56 * ns and fs != 32 + 48 * ng + 36 * nd + 44 * ns and fs != 32 + 60 * ng + 48 * nd + 56 * ns ): f.close() return False, 0 f.close() return True, endianswap @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: return TipsyDataset._validate_header(filename)[0] yt-project-yt-f043ac8/yt/frontends/tipsy/definitions.py000066400000000000000000000001111510711153200233360ustar00rootroot00000000000000npart_mapping = {"Gas": "nsph", "DarkMatter": "ndark", "Stars": "nstar"} yt-project-yt-f043ac8/yt/frontends/tipsy/fields.py000066400000000000000000000037341510711153200223070ustar00rootroot00000000000000from yt.frontends.sph.fields import SPHFieldInfo class TipsyFieldInfo(SPHFieldInfo): known_particle_fields = SPHFieldInfo.known_particle_fields + ( ("smoothing_length", ("code_length", [], None)), ) aux_particle_fields = { "uDotFB": ("uDotFB", ("code_mass * code_velocity**2", [""], None)), "uDotAV": ("uDotAV", ("code_mass * code_velocity**2", [""], None)), "uDotPdV": ("uDotPdV", ("code_mass * code_velocity**2", [""], None)), "uDotHydro": ("uDotHydro", ("code_mass * code_velocity**2", [""], None)), "uDotDiff": ("uDotDiff", ("code_mass * code_velocity**2", [""], None)), "uDot": ("uDot", ("code_mass * code_velocity**2", [""], None)), "coolontime": ("coolontime", ("code_time", [""], None)), "timeform": ("timeform", ("code_time", [""], None)), "massform": ("massform", ("code_mass", [""], None)), "HI": ("HI", ("dimensionless", ["H_fraction"], None)), "HII": ("HII", ("dimensionless", ["H_p1_fraction"], None)), "HeI": ("HeI", ("dimensionless", ["He_fraction"], None)), "HeII": ("HeII", ("dimensionless", ["He_p2_fraction"], None)), "OxMassFrac": ("OxMassFrac", ("dimensionless", ["O_fraction"], None)), "FeMassFrac": ("FeMassFrac", ("dimensionless", ["Fe_fraction"], None)), "c": ("c", ("code_velocity", [""], None)), "acc": ("acc", ("code_velocity / code_time", [""], None)), "accg": ("accg", ("code_velocity / code_time", [""], None)), "smoothlength": ("smoothlength", ("code_length", ["smoothing_length"], None)), } def __init__(self, ds, field_list, slice_info=None): for field in field_list: if ( field[1] in self.aux_particle_fields.keys() and self.aux_particle_fields[field[1]] not in self.known_particle_fields ): self.known_particle_fields += (self.aux_particle_fields[field[1]],) super().__init__(ds, field_list, slice_info) yt-project-yt-f043ac8/yt/frontends/tipsy/io.py000066400000000000000000000470571510711153200214560ustar00rootroot00000000000000import glob import os import struct import numpy as np from yt.frontends.sph.io import IOHandlerSPH from yt.frontends.tipsy.definitions import npart_mapping from yt.utilities.lib.particle_kdtree_tools import generate_smoothing_length from yt.utilities.logger import ytLogger as mylog class IOHandlerTipsyBinary(IOHandlerSPH): _dataset_type = "tipsy" _vector_fields = {"Coordinates": 3, "Velocity": 3, "Velocities": 3} _pdtypes = None # dtypes, to be filled in later _aux_pdtypes = None # auxiliary files' dtypes _ptypes = ("Gas", "DarkMatter", "Stars") _aux_fields = None _fields = ( ("Gas", "Mass"), ("Gas", "Coordinates"), ("Gas", "Velocities"), ("Gas", "Density"), ("Gas", "Temperature"), ("Gas", "Epsilon"), ("Gas", "Metals"), ("Gas", "Phi"), ("DarkMatter", "Mass"), ("DarkMatter", "Coordinates"), ("DarkMatter", "Velocities"), ("DarkMatter", "Epsilon"), ("DarkMatter", "Phi"), ("Stars", "Mass"), ("Stars", "Coordinates"), ("Stars", "Velocities"), ("Stars", "Metals"), ("Stars", "FormationTime"), ("Stars", "Epsilon"), ("Stars", "Phi"), ) def __init__(self, *args, **kwargs): self._aux_fields = [] super().__init__(*args, **kwargs) def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _fill_fields(self, fields, vals, hsml, mask, data_file): if mask is None: size = 0 elif isinstance(mask, slice): if fields[0] == "smoothing_length": size = hsml.size else: size = vals[fields[0]].size else: size = mask.sum() rv = {} for field in fields: mylog.debug("Allocating %s values for %s", size, field) if field in self._vector_fields: rv[field] = np.empty((size, 3), dtype="float64") if size == 0: continue rv[field][:, 0] = vals[field]["x"][mask] rv[field][:, 1] = vals[field]["y"][mask] rv[field][:, 2] = vals[field]["z"][mask] elif field == "smoothing_length": rv[field] = hsml[mask] else: rv[field] = np.empty(size, dtype="float64") if size == 0: continue rv[field][:] = vals[field][mask] if field == "Coordinates": eps = np.finfo(rv[field].dtype).eps for i in range(3): rv[field][:, i] = np.clip( rv[field][:, i], self.ds.domain_left_edge[i].v + eps, self.ds.domain_right_edge[i].v - eps, ) return rv def _read_particle_coords(self, chunks, ptf): chunksize = self.ds.index.chunksize for data_file in self._sorted_chunk_iterator(chunks): poff = data_file.field_offsets tp = data_file.total_particles f = open(data_file.filename, "rb") for ptype in sorted(ptf, key=lambda a, poff=poff: poff.get(a, -1)): if data_file.total_particles[ptype] == 0: continue f.seek(poff[ptype]) total = 0 while total < tp[ptype]: count = min(chunksize, tp[ptype] - total) p = np.fromfile(f, self._pdtypes[ptype], count=count) total += p.size d = [p["Coordinates"][ax].astype("float64") for ax in "xyz"] del p if ptype == self.ds._sph_ptypes[0]: hsml = self._read_smoothing_length(data_file, count) else: hsml = 0.0 yield ptype, d, hsml @property def hsml_filename(self): return f"{self.ds.parameter_filename}-{'hsml'}" def _generate_smoothing_length(self, index): if os.path.exists(self.hsml_filename): with open(self.hsml_filename, "rb") as f: file_hash = struct.unpack("q", f.read(struct.calcsize("q")))[0] if file_hash != self.ds._file_hash: os.remove(self.hsml_filename) else: return positions = [] for data_file in index.data_files: for _, ppos in self._yield_coordinates( data_file, needed_ptype=self.ds._sph_ptypes[0] ): positions.append(ppos) if positions == []: return kdtree = index.kdtree positions = np.concatenate(positions)[kdtree.idx] hsml = generate_smoothing_length(positions, kdtree, self.ds._num_neighbors) hsml = hsml[np.argsort(kdtree.idx)] dtype = self._pdtypes["Gas"]["Coordinates"][0] with open(self.hsml_filename, "wb") as f: f.write(struct.pack("q", self.ds._file_hash)) f.write(hsml.astype(dtype).tobytes()) def _read_smoothing_length(self, data_file, count): dtype = self._pdtypes["Gas"]["Coordinates"][0] with open(self.hsml_filename, "rb") as f: f.seek(struct.calcsize("q") + data_file.start * dtype.itemsize) hsmls = np.fromfile(f, dtype, count=count) return hsmls.astype("float64") def _get_smoothing_length(self, data_file, dtype, shape): return self._read_smoothing_length(data_file, shape[0]) def _read_particle_data_file(self, data_file, ptf, selector=None): from numpy.lib.recfunctions import append_fields return_data = {} poff = data_file.field_offsets aux_fields_offsets = self._calculate_particle_offsets_aux(data_file) tp = data_file.total_particles f = open(data_file.filename, "rb") # we need to open all aux files for chunking to work _aux_fh = {} def aux_fh(afield): if afield not in _aux_fh: _aux_fh[afield] = open(data_file.filename + "." + afield, "rb") return _aux_fh[afield] for ptype, field_list in sorted(ptf.items(), key=lambda a: poff.get(a[0], -1)): if data_file.total_particles[ptype] == 0: continue f.seek(poff[ptype]) afields = list(set(field_list).intersection(self._aux_fields)) count = min(self.ds.index.chunksize, tp[ptype]) p = np.fromfile(f, self._pdtypes[ptype], count=count) auxdata = [] for afield in afields: aux_fh(afield).seek(aux_fields_offsets[afield][ptype]) if isinstance(self._aux_pdtypes[afield], np.dtype): auxdata.append( np.fromfile( aux_fh(afield), self._aux_pdtypes[afield], count=count ) ) else: aux_fh(afield).seek(0) sh = aux_fields_offsets[afield][ptype] if tp[ptype] > 0: aux = np.genfromtxt( aux_fh(afield), skip_header=sh, max_rows=count ) if aux.ndim < 1: aux = np.array([aux]) auxdata.append(aux) if afields: p = append_fields(p, afields, auxdata) if ptype == "Gas": hsml = self._read_smoothing_length(data_file, count) else: hsml = 0.0 if selector is None or getattr(selector, "is_all_data", False): mask = slice(None, None, None) else: x = p["Coordinates"]["x"].astype("float64") y = p["Coordinates"]["y"].astype("float64") z = p["Coordinates"]["z"].astype("float64") mask = selector.select_points(x, y, z, hsml) del x, y, z if mask is None: continue tf = self._fill_fields(field_list, p, hsml, mask, data_file) for field in field_list: return_data[ptype, field] = tf.pop(field) # close all file handles f.close() for fh in _aux_fh.values(): fh.close() return return_data def _update_domain(self, data_file): """ This method is used to determine the size needed for a box that will bound the particles. It simply finds the largest position of the whole set of particles, and sets the domain to +/- that value. """ ds = data_file.ds ind = 0 # NOTE: # We hardcode this value here because otherwise we get into a # situation where we require the existence of index before we # can successfully instantiate it, or where we are calling it # from within its instantiation. # # Because this value is not propagated later on, and does not # impact the construction of the bitmap indices, it should be # acceptable to just use a reasonable number here. chunksize = 64**3 # Check to make sure that the domain hasn't already been set # by the parameter file if np.all(np.isfinite(ds.domain_left_edge)) and np.all( np.isfinite(ds.domain_right_edge) ): return with open(data_file.filename, "rb") as f: ds.domain_left_edge = 0 ds.domain_right_edge = 0 f.seek(ds._header_offset) mi = np.array([1e30, 1e30, 1e30], dtype="float64") ma = -np.array([1e30, 1e30, 1e30], dtype="float64") for ptype in self._ptypes: # We'll just add the individual types separately count = data_file.total_particles[ptype] if count == 0: continue stop = ind + count while ind < stop: c = min(chunksize, stop - ind) pp = np.fromfile(f, dtype=self._pdtypes[ptype], count=c) np.minimum( mi, [ pp["Coordinates"]["x"].min(), pp["Coordinates"]["y"].min(), pp["Coordinates"]["z"].min(), ], out=mi, ) np.maximum( ma, [ pp["Coordinates"]["x"].max(), pp["Coordinates"]["y"].max(), pp["Coordinates"]["z"].max(), ], out=ma, ) ind += c # We extend by 1%. DW = ma - mi mi -= 0.01 * DW ma += 0.01 * DW ds.domain_left_edge = ds.arr(mi, "code_length") ds.domain_right_edge = ds.arr(ma, "code_length") ds.domain_width = DW = ds.domain_right_edge - ds.domain_left_edge ds.unit_registry.add( "unitary", float(DW.max() * DW.units.base_value), DW.units.dimensions ) def _yield_coordinates(self, data_file, needed_ptype=None): with open(data_file.filename, "rb") as f: poff = data_file.field_offsets for ptype in self._ptypes: if ptype not in poff: continue f.seek(poff[ptype]) if needed_ptype is not None and ptype != needed_ptype: continue # We'll just add the individual types separately count = data_file.total_particles[ptype] if count == 0: continue pp = np.fromfile(f, dtype=self._pdtypes[ptype], count=count) mis = np.empty(3, dtype="float64") mas = np.empty(3, dtype="float64") for axi, ax in enumerate("xyz"): mi = pp["Coordinates"][ax].min() ma = pp["Coordinates"][ax].max() mylog.debug("Spanning: %0.3e .. %0.3e in %s", mi, ma, ax) mis[axi] = mi mas[axi] = ma pos = np.empty((pp.size, 3), dtype="float64") for i, ax in enumerate("xyz"): pos[:, i] = pp["Coordinates"][ax] yield ptype, pos def _count_particles(self, data_file): pcount = np.array( [ data_file.ds.parameters["nsph"], data_file.ds.parameters["nstar"], data_file.ds.parameters["ndark"], ] ) si, ei = data_file.start, data_file.end if None not in (si, ei): np.clip(pcount - si, 0, ei - si, out=pcount) ptypes = ["Gas", "Stars", "DarkMatter"] npart = dict(zip(ptypes, pcount, strict=True)) return npart @classmethod def _compute_dtypes(cls, field_dtypes, endian="<"): pds = {} for ptype, field in cls._fields: dtbase = field_dtypes.get(field, "f") ff = f"{endian}{dtbase}" if field in cls._vector_fields: dt = (field, [("x", ff), ("y", ff), ("z", ff)]) else: dt = (field, ff) pds.setdefault(ptype, []).append(dt) pdtypes = {} for ptype in pds: pdtypes[ptype] = np.dtype(pds[ptype]) return pdtypes def _create_dtypes(self, data_file): # We can just look at the particle counts. self._header_offset = data_file.ds._header_offset self._pdtypes = self._compute_dtypes( data_file.ds._field_dtypes, data_file.ds.endian ) self._field_list = [] for ptype, field in self._fields: if data_file.total_particles[ptype] == 0: # We do not want out _pdtypes to have empty particles. self._pdtypes.pop(ptype, None) continue self._field_list.append((ptype, field)) if "Gas" in self._pdtypes.keys(): self._field_list.append(("Gas", "smoothing_length")) # Find out which auxiliaries we have and what is their format tot_parts = np.sum( [ data_file.ds.parameters["nsph"], data_file.ds.parameters["nstar"], data_file.ds.parameters["ndark"], ] ) endian = data_file.ds.endian self._aux_pdtypes = {} self._aux_fields = [] for f in glob.glob(data_file.filename + ".*"): afield = f.rsplit(".")[-1] filename = data_file.filename + "." + afield if not os.path.exists(filename): continue if afield in ["log", "parameter", "kdtree"]: # Amiga halo finder makes files like this we need to ignore continue self._aux_fields.append(afield) skip_afields = [] for afield in self._aux_fields: filename = data_file.filename + "." + afield # We need to do some fairly ugly detection to see what format the # auxiliary files are in. They can be either ascii or binary, and # the binary files can be either floats, ints, or doubles. We're # going to use a try-catch cascade to determine the format. filesize = os.stat(filename).st_size dtype = np.dtype(endian + "i4") tot_parts_from_file = np.fromfile(filename, dtype, count=1) if tot_parts_from_file != tot_parts: with open(filename, "rb") as f: header_nparts = f.readline() try: header_nparts = int(header_nparts) except ValueError: skip_afields.append(afield) continue if int(header_nparts) != tot_parts: raise RuntimeError self._aux_pdtypes[afield] = "ascii" elif (filesize - 4) / 8 == tot_parts: self._aux_pdtypes[afield] = np.dtype([("aux", endian + "d")]) elif (filesize - 4) / 4 == tot_parts: if afield.startswith("i"): self._aux_pdtypes[afield] = np.dtype([("aux", endian + "i")]) else: self._aux_pdtypes[afield] = np.dtype([("aux", endian + "f")]) else: skip_afields.append(afield) for afield in skip_afields: self._aux_fields.remove(afield) # Add the auxiliary fields to each ptype we have for ptype in self._ptypes: if any(ptype == field[0] for field in self._field_list): self._field_list += [(ptype, afield) for afield in self._aux_fields] return self._field_list def _identify_fields(self, data_file): return self._field_list, {} def _calculate_particle_offsets(self, data_file, pcounts): # This computes the offsets for each particle type into a "data_file." # Note that the term "data_file" here is a bit overloaded, and also refers to a # "chunk" of particles inside a data file. # data_file.start represents the *particle count* that we should start at. # # At this point, data_file will have the total number of particles # that this chunk represents located in the property total_particles. # Because in tipsy files the particles are stored sequentially, we can # figure out where each one starts. # We first figure out the global offsets, then offset them by the count # and size of each individual particle type. field_offsets = {} # Initialize pos to the point the first particle type would start pos = data_file.ds._header_offset global_offsets = {} field_offsets = {} for ptype in self._ptypes: if ptype not in self._pdtypes: # This means we don't have any, I think, and so we shouldn't # stick it in the offsets. continue # Note that much of this will be computed redundantly; future # refactorings could fix this. global_offsets[ptype] = pos size = self._pdtypes[ptype].itemsize npart = self.ds.parameters[npart_mapping[ptype]] # Get the offset into just this particle type, and start at data_file.start if npart > data_file.start: field_offsets[ptype] = pos + size * data_file.start pos += npart * size return field_offsets def _calculate_particle_offsets_aux(self, data_file): aux_fields_offsets = {} params = self.ds.parameters for afield in self._aux_fields: aux_fields_offsets[afield] = {} if isinstance(self._aux_pdtypes[afield], np.dtype): pos = 4 # i4 size = np.dtype(self._aux_pdtypes[afield]).itemsize else: pos = 1 size = 1 for i, ptype in enumerate(self._ptypes): if data_file.total_particles[ptype] == 0: continue elif params[npart_mapping[ptype]] > self.ds.index.chunksize: for j in range(i): npart = params[npart_mapping[self._ptypes[j]]] if npart > self.ds.index.chunksize: pos += npart * size pos += data_file.start * size aux_fields_offsets[afield][ptype] = pos pos += data_file.total_particles[ptype] * size return aux_fields_offsets yt-project-yt-f043ac8/yt/frontends/tipsy/tests/000077500000000000000000000000001510711153200216225ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/tipsy/tests/__init__.py000066400000000000000000000000001510711153200237210ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/tipsy/tests/test_outputs.py000066400000000000000000000067311510711153200247650ustar00rootroot00000000000000from collections import OrderedDict from yt.frontends.tipsy.api import TipsyDataset from yt.testing import ParticleSelectionComparison, requires_file from yt.utilities.answer_testing.framework import ( data_dir_load, nbody_answer, requires_ds, sph_answer, ) _fields = OrderedDict( [ (("all", "particle_mass"), None), (("all", "particle_ones"), None), (("all", "particle_velocity_x"), ("all", "particle_mass")), (("all", "particle_velocity_y"), ("all", "particle_mass")), (("all", "particle_velocity_z"), ("all", "particle_mass")), ] ) pkdgrav = "halo1e11_run1.00400/halo1e11_run1.00400" pkdgrav_cosmology_parameters = { "current_redshift": 0.0, "omega_lambda": 0.728, "omega_matter": 0.272, "hubble_constant": 0.702, } pkdgrav_kwargs = { "field_dtypes": {"Coordinates": "d"}, "cosmology_parameters": pkdgrav_cosmology_parameters, "unit_base": {"length": (60.0, "Mpccm/h")}, } @requires_ds(pkdgrav, big_data=True, file_check=True) def test_pkdgrav(): ds = data_dir_load(pkdgrav, TipsyDataset, (), kwargs=pkdgrav_kwargs) yield from nbody_answer(ds, "halo1e11_run1.00400", 26847360, _fields) psc = ParticleSelectionComparison(ds) psc.run_defaults() gasoline_dmonly = "agora_1e11.00400/agora_1e11.00400" @requires_ds(gasoline_dmonly, big_data=True, file_check=True) def test_gasoline_dmonly(): cosmology_parameters = { "current_redshift": 0.0, "omega_lambda": 0.728, "omega_matter": 0.272, "hubble_constant": 0.702, } kwargs = { "cosmology_parameters": cosmology_parameters, "unit_base": {"length": (60.0, "Mpccm/h")}, } ds = data_dir_load(gasoline_dmonly, TipsyDataset, (), kwargs) yield from nbody_answer(ds, "agora_1e11.00400", 10550576, _fields) psc = ParticleSelectionComparison(ds) psc.run_defaults() tg_sph_fields = OrderedDict( [ (("gas", "density"), None), (("gas", "temperature"), None), (("gas", "temperature"), ("gas", "density")), (("gas", "velocity_magnitude"), None), (("gas", "Fe_fraction"), None), ] ) tg_nbody_fields = OrderedDict( [ (("Stars", "Metals"), None), ] ) tipsy_gal = "TipsyGalaxy/galaxy.00300" @requires_ds(tipsy_gal) def test_tipsy_galaxy(): ds = data_dir_load( tipsy_gal, kwargs={"bounding_box": [[-2000, 2000], [-2000, 2000], [-2000, 2000]]}, ) # These tests should be re-enabled. But the holdup is that the region # selector does not offset by domain_left_edge, and we have inelegant # selection using bboxes. # psc = ParticleSelectionComparison(ds) # psc.run_defaults() for test in sph_answer(ds, "galaxy.00300", 315372, tg_sph_fields): test_tipsy_galaxy.__name__ = test.description yield test for test in nbody_answer(ds, "galaxy.00300", 315372, tg_nbody_fields): test_tipsy_galaxy.__name__ = test.description yield test @requires_file(gasoline_dmonly) @requires_file(pkdgrav) def test_TipsyDataset(): assert isinstance(data_dir_load(pkdgrav, kwargs=pkdgrav_kwargs), TipsyDataset) assert isinstance(data_dir_load(gasoline_dmonly), TipsyDataset) @requires_file(tipsy_gal) def test_tipsy_index(): ds = data_dir_load(tipsy_gal) sl = ds.slice("z", 0.0) assert sl["gas", "density"].shape[0] != 0 @requires_file(tipsy_gal) def test_tipsy_smoothing_length(): ds = data_dir_load(tipsy_gal) _ = ds.all_data()["Gas", "smoothing_length"] yt-project-yt-f043ac8/yt/frontends/ytdata/000077500000000000000000000000001510711153200205765ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ytdata/__init__.py000066400000000000000000000000451510711153200227060ustar00rootroot00000000000000""" API for ytData frontend. """ yt-project-yt-f043ac8/yt/frontends/ytdata/api.py000066400000000000000000000010401510711153200217140ustar00rootroot00000000000000from . import tests from .data_structures import ( YTClumpContainer, YTClumpTreeDataset, YTDataContainerDataset, YTGrid, YTGridDataset, YTGridHierarchy, YTNonspatialDataset, YTNonspatialGrid, YTNonspatialHierarchy, YTProfileDataset, YTSpatialPlotDataset, ) from .fields import YTDataContainerFieldInfo, YTGridFieldInfo from .io import ( IOHandlerYTDataContainerHDF5, IOHandlerYTGridHDF5, IOHandlerYTNonspatialhdf5, IOHandlerYTSpatialPlotHDF5, ) from .utilities import save_as_dataset yt-project-yt-f043ac8/yt/frontends/ytdata/data_structures.py000066400000000000000000001072741510711153200243770ustar00rootroot00000000000000import os import weakref from collections import defaultdict from functools import cached_property from numbers import Number as numeric_type import numpy as np from yt.data_objects.index_subobjects.grid_patch import AMRGridPatch from yt.data_objects.profiles import ( Profile1DFromDataset, Profile2DFromDataset, Profile3DFromDataset, ) from yt.data_objects.static_output import Dataset, ParticleFile from yt.data_objects.unions import ParticleUnion from yt.fields.field_exceptions import NeedsGridType from yt.fields.field_info_container import FieldInfoContainer from yt.funcs import is_root, parse_h5_attr from yt.geometry.api import Geometry from yt.geometry.geometry_handler import Index from yt.geometry.grid_geometry_handler import GridIndex from yt.geometry.particle_geometry_handler import ParticleIndex from yt.units import dimensions from yt.units._numpy_wrapper_functions import uconcatenate from yt.units.unit_registry import UnitRegistry # type: ignore from yt.units.yt_array import YTQuantity from yt.utilities.exceptions import GenerationInProgress, YTFieldTypeNotFound from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_root_only from yt.utilities.tree_container import TreeContainer from .fields import YTDataContainerFieldInfo, YTGridFieldInfo _grid_data_containers = ["arbitrary_grid", "covering_grid", "smoothed_covering_grid"] _set_attrs = {"periodicity": "_periodicity"} class SavedDataset(Dataset): """ Base dataset class for products of calling save_as_dataset. """ geometry = Geometry.CARTESIAN _con_attrs: tuple[str, ...] = () def _parse_parameter_file(self): self.refine_by = 2 with h5py.File(self.parameter_filename, mode="r") as f: for key in f.attrs.keys(): v = parse_h5_attr(f, key) if key == "con_args": try: v = eval(v) except ValueError: # support older ytdata outputs v = v.astype("str") except NameError: # This is the most common error we expect, and it # results from having the eval do a concatenated decoded # set of the values. v = [_.decode("utf8") for _ in v] self.parameters[key] = v self._with_parameter_file_open(f) # if saved, restore unit registry from the json string if "unit_registry_json" in self.parameters: self.unit_registry = UnitRegistry.from_json( self.parameters["unit_registry_json"] ) # reset self.arr and self.quan to use new unit_registry self._arr = None self._quan = None for dim in [ "length", "mass", "pressure", "temperature", "time", "velocity", ]: cu = "code_" + dim if cu not in self.unit_registry: self.unit_registry.add(cu, 1.0, getattr(dimensions, dim)) if "code_magnetic" not in self.unit_registry: self.unit_registry.add( "code_magnetic", 0.1**0.5, dimensions.magnetic_field_cgs ) # if saved, set unit system if "unit_system_name" in self.parameters: unit_system = self.parameters["unit_system_name"] del self.parameters["unit_system_name"] else: unit_system = "cgs" # reset unit system since we may have a new unit registry self._assign_unit_system(unit_system) # assign units to parameters that have associated unit string del_pars = [] for par in self.parameters: ustr = f"{par}_units" if ustr in self.parameters: if isinstance(self.parameters[par], np.ndarray): to_u = self.arr else: to_u = self.quan self.parameters[par] = to_u(self.parameters[par], self.parameters[ustr]) del_pars.append(ustr) for par in del_pars: del self.parameters[par] for attr in self._con_attrs: sattr = _set_attrs.get(attr, attr) if sattr == "geometry": if "geometry" in self.parameters: self.geometry = Geometry(self.parameters["geometry"]) continue try: setattr(self, sattr, self.parameters.get(attr)) except TypeError: # some Dataset attributes are properties with setters # which may not accept None as an input pass def _with_parameter_file_open(self, f): # This allows subclasses to access the parameter file # while it's still open to get additional information. pass def set_units(self): if "unit_registry_json" in self.parameters: self._set_code_unit_attributes() del self.parameters["unit_registry_json"] else: super().set_units() def _set_code_unit_attributes(self): attrs = ( "length_unit", "mass_unit", "time_unit", "velocity_unit", "magnetic_unit", ) cgs_units = ("cm", "g", "s", "cm/s", "gauss") base_units = np.ones(len(attrs)) for unit, attr, cgs_unit in zip(base_units, attrs, cgs_units, strict=True): if attr in self.parameters and isinstance( self.parameters[attr], YTQuantity ): uq = self.parameters[attr] elif attr in self.parameters and f"{attr}_units" in self.parameters: uq = self.quan(self.parameters[attr], self.parameters[f"{attr}_units"]) del self.parameters[attr] del self.parameters[f"{attr}_units"] elif isinstance(unit, str): uq = self.quan(1.0, unit) elif isinstance(unit, numeric_type): uq = self.quan(unit, cgs_unit) elif isinstance(unit, YTQuantity): uq = unit elif isinstance(unit, tuple): uq = self.quan(unit[0], unit[1]) else: raise RuntimeError(f"{attr} ({unit}) is invalid.") setattr(self, attr, uq) class YTDataset(SavedDataset): """Base dataset class for all ytdata datasets.""" _con_attrs = ( "cosmological_simulation", "current_time", "current_redshift", "hubble_constant", "omega_matter", "omega_lambda", "dimensionality", "domain_dimensions", "geometry", "periodicity", "domain_left_edge", "domain_right_edge", "container_type", "data_type", ) def _with_parameter_file_open(self, f): self.num_particles = { group: parse_h5_attr(f[group], "num_elements") for group in f if group != self.default_fluid_type } def create_field_info(self): self.field_dependencies = {} self.derived_field_list = [] self.filtered_particle_types = [] self.field_info = self._field_info_class(self, self.field_list) self.coordinates.setup_fields(self.field_info) self.field_info.setup_fluid_fields() for ptype in self.particle_types: self.field_info.setup_particle_fields(ptype) self._setup_gas_alias() self.field_info.setup_fluid_index_fields() if "all" not in self.particle_types: mylog.debug("Creating Particle Union 'all'") pu = ParticleUnion("all", list(self.particle_types_raw)) self.add_particle_union(pu) self.field_info.setup_extra_union_fields() mylog.debug("Loading field plugins.") self.field_info.load_all_plugins() deps, unloaded = self.field_info.check_derived_fields() self.field_dependencies.update(deps) def _setup_gas_alias(self): pass def _setup_override_fields(self): pass class YTDataHDF5File(ParticleFile): def __init__(self, ds, io, filename, file_id, range): with h5py.File(filename, mode="r") as f: self.header = {field: parse_h5_attr(f, field) for field in f.attrs.keys()} super().__init__(ds, io, filename, file_id, range) class YTDataContainerDataset(YTDataset): """Dataset for saved geometric data containers.""" _load_requirements = ["h5py"] _index_class = ParticleIndex _file_class = YTDataHDF5File _field_info_class: type[FieldInfoContainer] = YTDataContainerFieldInfo _suffix = ".h5" fluid_types = ("grid", "gas", "deposit", "index") def __init__( self, filename, dataset_type="ytdatacontainer_hdf5", index_order=None, index_filename=None, units_override=None, unit_system="cgs", ): self.index_order = index_order self.index_filename = index_filename super().__init__( filename, dataset_type, units_override=units_override, unit_system=unit_system, ) def _parse_parameter_file(self): super()._parse_parameter_file() self.particle_types_raw = tuple(self.num_particles.keys()) self.particle_types = self.particle_types_raw self.filename_template = self.parameter_filename self.file_count = 1 self.domain_dimensions = np.ones(3, "int32") def _setup_gas_alias(self): "Alias the grid type to gas by making a particle union." if "grid" in self.particle_types and "gas" not in self.particle_types: pu = ParticleUnion("gas", ["grid"]) self.add_particle_union(pu) # We have to alias this because particle unions only # cover the field_list. self.field_info.alias(("gas", "cell_volume"), ("grid", "cell_volume")) @cached_property def data(self): """ Return a data container configured like the original used to create this dataset. """ # Some data containers can't be reconstructed in the same way # since this is now particle-like data. data_type = self.parameters.get("data_type") container_type = self.parameters.get("container_type") ex_container_type = ["cutting", "quad_proj", "ray", "slice", "cut_region"] if data_type == "yt_light_ray" or container_type in ex_container_type: mylog.info("Returning an all_data data container.") return self.all_data() my_obj = getattr(self, self.parameters["container_type"]) my_args = [self.parameters[con_arg] for con_arg in self.parameters["con_args"]] return my_obj(*my_args) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") cont_type = parse_h5_attr(f, "container_type") if data_type is None: return False if ( data_type == "yt_data_container" and cont_type not in _grid_data_containers ): return True return False class YTDataLightRayDataset(YTDataContainerDataset): """Dataset for saved LightRay objects.""" _load_requirements = ["h5py"] def _parse_parameter_file(self): super()._parse_parameter_file() self._restore_light_ray_solution() def _restore_light_ray_solution(self): """ Restore all information associate with the light ray solution to its original form. """ key = "light_ray_solution" self.light_ray_solution = [] lrs_fields = [ par for par in self.parameters if key in par and not par.endswith("_units") ] if len(lrs_fields) == 0: return self.light_ray_solution = [{} for val in self.parameters[lrs_fields[0]]] for sp3 in ["unique_identifier", "filename"]: ksp3 = f"{key}_{sp3}" if ksp3 not in lrs_fields: continue self.parameters[ksp3] = self.parameters[ksp3].astype(str) for field in lrs_fields: field_name = field[len(key) + 1 :] for i in range(self.parameters[field].shape[0]): self.light_ray_solution[i][field_name] = self.parameters[field][i] @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") if data_type in ["yt_light_ray"]: return True return False class YTSpatialPlotDataset(YTDataContainerDataset): """Dataset for saved slices and projections.""" _load_requirements = ["h5py"] _field_info_class = YTGridFieldInfo def __init__(self, *args, **kwargs): super().__init__(*args, dataset_type="ytspatialplot_hdf5", **kwargs) def _parse_parameter_file(self): super()._parse_parameter_file() if self.parameters["container_type"] == "proj": if ( isinstance(self.parameters["weight_field"], str) and self.parameters["weight_field"] == "None" ): self.parameters["weight_field"] = None elif isinstance(self.parameters["weight_field"], np.ndarray): self.parameters["weight_field"] = tuple(self.parameters["weight_field"]) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") cont_type = parse_h5_attr(f, "container_type") if data_type == "yt_data_container" and cont_type in [ "cutting", "proj", "slice", "quad_proj", ]: return True return False class YTGrid(AMRGridPatch): _id_offset = 0 def __init__(self, gid, index, filename=None): AMRGridPatch.__init__(self, gid, filename=filename, index=index) self._children_ids = [] self._parent_id = -1 self.Level = 0 self.LeftEdge = self.index.ds.domain_left_edge self.RightEdge = self.index.ds.domain_right_edge def __getitem__(self, key): tr = super(AMRGridPatch, self).__getitem__(key) try: fields = self._determine_fields(key) except YTFieldTypeNotFound: return tr finfo = self.ds._get_field_info(fields[0]) if not finfo.sampling_type == "particle": return tr.reshape(self.ActiveDimensions[: self.ds.dimensionality]) return tr @property def Parent(self): return None @property def Children(self): return [] class YTDataHierarchy(GridIndex): def __init__(self, ds, dataset_type=None): self.dataset_type = dataset_type self.float_type = "float64" self.dataset = weakref.proxy(ds) self.directory = os.getcwd() super().__init__(ds, dataset_type) def _count_grids(self): self.num_grids = 1 def _parse_index(self): self.grid_dimensions[:] = self.ds.domain_dimensions self.grid_left_edge[:] = self.ds.domain_left_edge self.grid_right_edge[:] = self.ds.domain_right_edge self.grid_levels[:] = np.zeros(self.num_grids) self.grid_procs = np.zeros(self.num_grids) self.grid_particle_count[:] = sum(self.ds.num_particles.values()) self.grids = [] for gid in range(self.num_grids): self.grids.append(self.grid(gid, self)) self.grids[gid].Level = self.grid_levels[gid, 0] self.max_level = self.grid_levels.max() temp_grids = np.empty(self.num_grids, dtype="object") for i, grid in enumerate(self.grids): grid.filename = self.ds.parameter_filename grid._prepare_grid() grid.proc_num = self.grid_procs[i] temp_grids[i] = grid self.grids = temp_grids def _detect_output_fields(self): self.field_list = [] self.ds.field_units = self.ds.field_units or {} with h5py.File(self.ds.parameter_filename, mode="r") as f: for group in f: for field in f[group]: field_name = (str(group), str(field)) self.field_list.append(field_name) self.ds.field_units[field_name] = parse_h5_attr( f[group][field], "units" ) class YTGridHierarchy(YTDataHierarchy): grid = YTGrid def _populate_grid_objects(self): for g in self.grids: g._setup_dx() self.max_level = self.grid_levels.max() class YTGridDataset(YTDataset): """Dataset for saved covering grids, arbitrary grids, and FRBs.""" _load_requirements = ["h5py"] _index_class: type[Index] = YTGridHierarchy _field_info_class = YTGridFieldInfo _dataset_type = "ytgridhdf5" geometry = Geometry.CARTESIAN default_fluid_type = "grid" fluid_types: tuple[str, ...] = ("grid", "gas", "deposit", "index") def __init__(self, filename, unit_system="cgs"): super().__init__(filename, self._dataset_type, unit_system=unit_system) self.data = self.index.grids[0] def _parse_parameter_file(self): super()._parse_parameter_file() self.num_particles.pop(self.default_fluid_type, None) self.particle_types_raw = tuple(self.num_particles.keys()) self.particle_types = self.particle_types_raw # correct domain dimensions for the covering grid dimension self.base_domain_left_edge = self.domain_left_edge self.base_domain_right_edge = self.domain_right_edge self.base_domain_dimensions = self.domain_dimensions if self.container_type in _grid_data_containers: self.domain_left_edge = self.parameters["left_edge"] if "level" in self.parameters["con_args"]: dx = (self.base_domain_right_edge - self.base_domain_left_edge) / ( self.domain_dimensions * self.refine_by ** self.parameters["level"] ) self.domain_right_edge = ( self.domain_left_edge + self.parameters["ActiveDimensions"] * dx ) self.domain_dimensions = ( (self.domain_right_edge - self.domain_left_edge) / dx ).astype("int64") else: self.domain_right_edge = self.parameters["right_edge"] self.domain_dimensions = self.parameters["ActiveDimensions"] dx = ( self.domain_right_edge - self.domain_left_edge ) / self.domain_dimensions periodicity = ( np.abs(self.domain_left_edge - self.base_domain_left_edge) < 0.5 * dx ) periodicity &= ( np.abs(self.domain_right_edge - self.base_domain_right_edge) < 0.5 * dx ) self._periodicity = periodicity elif self.data_type == "yt_frb": dle = self.domain_left_edge self.domain_left_edge = uconcatenate( [self.parameters["left_edge"].to(dle.units), [0] * dle.uq] ) dre = self.domain_right_edge self.domain_right_edge = uconcatenate( [self.parameters["right_edge"].to(dre.units), [1] * dre.uq] ) self.domain_dimensions = np.concatenate( [self.parameters["ActiveDimensions"], [1]] ) def _setup_gas_alias(self): "Alias the grid type to gas with a field alias." for ftype, field in self.field_list: if ftype == "grid": self.field_info.alias(("gas", field), ("grid", field)) @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") cont_type = parse_h5_attr(f, "container_type") if data_type == "yt_frb": return True if data_type == "yt_data_container" and cont_type in _grid_data_containers: return True return False class YTNonspatialGrid(AMRGridPatch): _id_offset = 0 def __init__(self, gid, index, filename=None): super().__init__(gid, filename=filename, index=index) self._children_ids = [] self._parent_id = -1 self.Level = 0 self.LeftEdge = self.index.ds.domain_left_edge self.RightEdge = self.index.ds.domain_right_edge def __getitem__(self, key): tr = super(AMRGridPatch, self).__getitem__(key) try: fields = self._determine_fields(key) except YTFieldTypeNotFound: return tr self.ds._get_field_info(fields[0]) return tr def get_data(self, fields=None): if fields is None: return nfields = [] apply_fields = defaultdict(list) for field in self._determine_fields(fields): if field[0] in self.ds.filtered_particle_types: f = self.ds.known_filters[field[0]] apply_fields[field[0]].append((f.filtered_type, field[1])) else: nfields.append(field) for filter_type in apply_fields: f = self.ds.known_filters[filter_type] with f.apply(self): self.get_data(apply_fields[filter_type]) fields = nfields if len(fields) == 0: return # Now we collect all our fields # Here is where we need to perform a validation step, so that if we # have a field requested that we actually *can't* yet get, we put it # off until the end. This prevents double-reading fields that will # need to be used in spatial fields later on. fields_to_get = [] # This will be pre-populated with spatial fields fields_to_generate = [] for field in self._determine_fields(fields): if field in self.field_data: continue finfo = self.ds._get_field_info(field) try: finfo.check_available(self) except NeedsGridType: fields_to_generate.append(field) continue fields_to_get.append(field) if len(fields_to_get) == 0 and len(fields_to_generate) == 0: return elif self._locked: raise GenerationInProgress(fields) # Track which ones we want in the end ofields = set(list(self.field_data.keys()) + fields_to_get + fields_to_generate) # At this point, we want to figure out *all* our dependencies. fields_to_get = self._identify_dependencies(fields_to_get, self._spatial) # We now split up into readers for the types of fields fluids, particles = [], [] finfos = {} for field_key in fields_to_get: finfo = self.ds._get_field_info(field_key) finfos[field_key] = finfo if finfo.sampling_type == "particle": particles.append(field_key) elif field_key not in fluids: fluids.append(field_key) # The _read method will figure out which fields it needs to get from # disk, and return a dict of those fields along with the fields that # need to be generated. read_fluids, gen_fluids = self.index._read_fluid_fields( fluids, self, self._current_chunk ) for f, v in read_fluids.items(): convert = True if v.dtype != np.float64: if finfos[f].units == "": self.field_data[f] = v convert = False else: v = v.astype(np.float64) if convert: self.field_data[f] = self.ds.arr(v, units=finfos[f].units) self.field_data[f].convert_to_units(finfos[f].output_units) read_particles, gen_particles = self.index._read_fluid_fields( particles, self, self._current_chunk ) for f, v in read_particles.items(): convert = True if v.dtype != np.float64: if finfos[f].units == "": self.field_data[f] = v convert = False else: v = v.astype(np.float64) if convert: self.field_data[f] = self.ds.arr(v, units=finfos[f].units) self.field_data[f].convert_to_units(finfos[f].output_units) fields_to_generate += gen_fluids + gen_particles self._generate_fields(fields_to_generate) for field in list(self.field_data.keys()): if field not in ofields: self.field_data.pop(field) @property def Parent(self): return None @property def Children(self): return [] class YTNonspatialHierarchy(YTDataHierarchy): grid = YTNonspatialGrid def _populate_grid_objects(self): for g in self.grids: g._setup_dx() # this is non-spatial, so remove the code_length units g.dds = self.ds.arr(g.dds.d, "") g.ActiveDimensions = self.ds.domain_dimensions self.max_level = self.grid_levels.max() def _read_fluid_fields(self, fields, dobj, chunk=None): if len(fields) == 0: return {}, [] fields_to_read, fields_to_generate = self._split_fields(fields) if len(fields_to_read) == 0: return {}, fields_to_generate selector = dobj.selector fields_to_return = self.io._read_fluid_selection(dobj, selector, fields_to_read) return fields_to_return, fields_to_generate class YTNonspatialDataset(YTGridDataset): """Dataset for general array data.""" _load_requirements = ["h5py"] _index_class = YTNonspatialHierarchy _field_info_class = YTGridFieldInfo _dataset_type = "ytnonspatialhdf5" geometry = Geometry.CARTESIAN default_fluid_type = "data" fluid_types: tuple[str, ...] = ("data", "gas") def _parse_parameter_file(self): super(YTGridDataset, self)._parse_parameter_file() self.num_particles.pop(self.default_fluid_type, None) self.particle_types_raw = tuple(self.num_particles.keys()) self.particle_types = self.particle_types_raw def _set_derived_attrs(self): # set some defaults just to make things go default_attrs = { "dimensionality": 3, "domain_dimensions": np.ones(3, dtype="int64"), "domain_left_edge": np.zeros(3), "domain_right_edge": np.ones(3), "_periodicity": np.ones(3, dtype="bool"), } for att, val in default_attrs.items(): if getattr(self, att, None) is None: setattr(self, att, val) def _setup_classes(self): # We don't allow geometric selection for non-spatial datasets self.objects = [] @parallel_root_only def print_key_parameters(self): for a in [ "current_time", "domain_dimensions", "domain_left_edge", "domain_right_edge", "cosmological_simulation", ]: v = getattr(self, a) if v is not None: mylog.info("Parameters: %-25s = %s", a, v) if hasattr(self, "cosmological_simulation") and self.cosmological_simulation: for a in [ "current_redshift", "omega_lambda", "omega_matter", "hubble_constant", ]: v = getattr(self, a) if v is not None: mylog.info("Parameters: %-25s = %s", a, v) mylog.warning("Geometric data selection not available for this dataset type.") @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") if data_type == "yt_array_data": return True return False class YTProfileDataset(YTNonspatialDataset): """Dataset for saved profile objects.""" _load_requirements = ["h5py"] fluid_types = ("data", "gas", "standard_deviation") def __init__(self, filename, unit_system="cgs"): super().__init__(filename, unit_system=unit_system) _profile = None @property def profile(self): if self._profile is not None: return self._profile if self.dimensionality == 1: self._profile = Profile1DFromDataset(self) elif self.dimensionality == 2: self._profile = Profile2DFromDataset(self) elif self.dimensionality == 3: self._profile = Profile3DFromDataset(self) else: self._profile = None return self._profile def _parse_parameter_file(self): super(YTGridDataset, self)._parse_parameter_file() if ( isinstance(self.parameters["weight_field"], str) and self.parameters["weight_field"] == "None" ): self.parameters["weight_field"] = None elif isinstance(self.parameters["weight_field"], np.ndarray): self.parameters["weight_field"] = tuple( self.parameters["weight_field"].astype(str) ) for a in ["profile_dimensions"] + [ f"{ax}_{attr}" for ax in "xyz"[: self.dimensionality] for attr in ["log"] ]: setattr(self, a, self.parameters[a]) self.base_domain_left_edge = self.domain_left_edge self.base_domain_right_edge = self.domain_right_edge self.base_domain_dimensions = self.domain_dimensions domain_dimensions = np.ones(3, dtype="int64") domain_dimensions[: self.dimensionality] = self.profile_dimensions self.domain_dimensions = domain_dimensions domain_left_edge = np.zeros(3) domain_right_edge = np.ones(3) for i, ax in enumerate("xyz"[: self.dimensionality]): range_name = f"{ax}_range" my_range = self.parameters[range_name] if getattr(self, f"{ax}_log", False): my_range = np.log10(my_range) domain_left_edge[i] = my_range[0] domain_right_edge[i] = my_range[1] setattr(self, range_name, self.parameters[range_name]) bin_field = f"{ax}_field" if ( isinstance(self.parameters[bin_field], str) and self.parameters[bin_field] == "None" ): self.parameters[bin_field] = None elif isinstance(self.parameters[bin_field], np.ndarray): self.parameters[bin_field] = ( "data", self.parameters[bin_field].astype(str)[1], ) setattr(self, bin_field, self.parameters[bin_field]) self.domain_left_edge = domain_left_edge self.domain_right_edge = domain_right_edge def _setup_gas_alias(self): "Alias the grid type to gas with a field alias." for ftype, field in self.field_list: if ftype == "data": self.field_info.alias(("gas", field), (ftype, field)) def create_field_info(self): super().create_field_info() if self.parameters["weight_field"] is not None: self.field_info.alias( self.parameters["weight_field"], (self.default_fluid_type, "weight") ) def _set_derived_attrs(self): self.domain_center = 0.5 * (self.domain_right_edge + self.domain_left_edge) self.domain_width = self.domain_right_edge - self.domain_left_edge def print_key_parameters(self): if is_root(): mylog.info("YTProfileDataset") for a in ["dimensionality", "profile_dimensions"] + [ f"{ax}_{attr}" for ax in "xyz"[: self.dimensionality] for attr in ["field", "range", "log"] ]: v = getattr(self, a) mylog.info("Parameters: %-25s = %s", a, v) super().print_key_parameters() @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") if data_type == "yt_profile": return True return False class YTClumpContainer(TreeContainer): def __init__( self, clump_id, global_id, parent_id, contour_key, contour_id, ds=None ): self.clump_id = clump_id self.global_id = global_id self.parent_id = parent_id self.contour_key = contour_key self.contour_id = contour_id self.parent = None self.ds = ds TreeContainer.__init__(self) def add_child(self, child): if self.children is None: self.children = [] self.children.append(child) child.parent = self def __repr__(self): return "Clump[%d]" % self.clump_id def __getitem__(self, field): g = self.ds.data f = g._determine_fields(field)[0] if f[0] == "clump": return g[f][self.global_id] if self.contour_id == -1: return g[f] cfield = (f[0], f"contours_{self.contour_key.decode('utf-8')}") if f[0] == "grid": return g[f][g[cfield] == self.contour_id] return self.parent[f][g[cfield] == self.contour_id] class YTClumpTreeDataset(YTNonspatialDataset): """Dataset for saved clump-finder data.""" _load_requirements = ["h5py"] def __init__(self, filename, unit_system="cgs"): super().__init__(filename, unit_system=unit_system) self._load_tree() def _load_tree(self): my_tree = {} for i, clump_id in enumerate(self.data["clump", "clump_id"]): my_tree[clump_id] = YTClumpContainer( clump_id, i, self.data["clump", "parent_id"][i], self.data["clump", "contour_key"][i], self.data["clump", "contour_id"][i], self, ) for clump in my_tree.values(): if clump.parent_id == -1: self.tree = clump else: parent = my_tree[clump.parent_id] parent.add_child(clump) @cached_property def leaves(self): return [clump for clump in self.tree if clump.children is None] @classmethod def _is_valid(cls, filename: str, *args, **kwargs) -> bool: if not filename.endswith(".h5"): return False if cls._missing_load_requirements(): return False with h5py.File(filename, mode="r") as f: data_type = parse_h5_attr(f, "data_type") if data_type is None: return False if data_type == "yt_clump_tree": return True return False yt-project-yt-f043ac8/yt/frontends/ytdata/fields.py000066400000000000000000000023141510711153200224160ustar00rootroot00000000000000from yt.fields.field_info_container import FieldInfoContainer m_units = "g" p_units = "cm" v_units = "cm / s" r_units = "cm" class YTDataContainerFieldInfo(FieldInfoContainer): known_other_fields = () known_particle_fields = () def __init__(self, ds, field_list): super().__init__(ds, field_list) self.add_fake_grid_fields() def add_fake_grid_fields(self): """ Add cell volume and mass fields that use the dx, dy, and dz fields that come with the dataset instead of the index fields which correspond to the oct tree. We need to do this for now since we're treating the grid data like particles until we implement exporting AMR hierarchies. """ if ("grid", "cell_volume") not in self.field_list: def _cell_volume(field, data): return data["grid", "dx"] * data["grid", "dy"] * data["grid", "dz"] self.add_field( ("grid", "cell_volume"), sampling_type="particle", function=_cell_volume, units="cm**3", ) class YTGridFieldInfo(FieldInfoContainer): known_other_fields = () known_particle_fields = () yt-project-yt-f043ac8/yt/frontends/ytdata/io.py000066400000000000000000000302171510711153200215620ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog, parse_h5_attr from yt.geometry.selection_routines import GridSelector from yt.units._numpy_wrapper_functions import uvstack from yt.utilities.io_handler import BaseIOHandler from yt.utilities.on_demand_imports import _h5py as h5py class IOHandlerYTNonspatialhdf5(BaseIOHandler): _dataset_type = "ytnonspatialhdf5" _base = slice(None) _field_dtype = "float64" def _read_fluid_selection(self, g, selector, fields): rv = {} if isinstance(selector, GridSelector): if g.id in self._cached_fields: gf = self._cached_fields[g.id] rv.update(gf) if len(rv) == len(fields): return rv f = h5py.File(g.filename, mode="r") for field in fields: if field in rv: self._hits += 1 continue self._misses += 1 ftype, fname = field rv[ftype, fname] = f[ftype][fname][()] if self._cache_on: for gid in rv: self._cached_fields.setdefault(gid, {}) self._cached_fields[gid].update(rv[gid]) f.close() return rv else: raise RuntimeError( "Geometric selection not supported for non-spatial datasets." ) class IOHandlerYTGridHDF5(BaseIOHandler): _dataset_type = "ytgridhdf5" _base = slice(None) _field_dtype = "float64" def _read_fluid_selection(self, chunks, selector, fields, size): rv = {} # Now we have to do something unpleasant chunks = list(chunks) if isinstance(selector, GridSelector): if not (len(chunks) == len(chunks[0].objs) == 1): raise RuntimeError g = chunks[0].objs[0] if g.id in self._cached_fields: gf = self._cached_fields[g.id] rv.update(gf) if len(rv) == len(fields): return rv f = h5py.File(g.filename, mode="r") gds = f[self.ds.default_fluid_type] for field in fields: if field in rv: self._hits += 1 continue self._misses += 1 ftype, fname = field rv[ftype, fname] = gds[fname][()] if self._cache_on: for gid in rv: self._cached_fields.setdefault(gid, {}) self._cached_fields[gid].update(rv[gid]) f.close() return rv if size is None: size = sum(g.count(selector) for chunk in chunks for g in chunk.objs) for field in fields: ftype, fname = field fsize = size rv[field] = np.empty(fsize, dtype="float64") ng = sum(len(c.objs) for c in chunks) mylog.debug( "Reading %s cells of %s fields in %s grids", size, [f2 for f1, f2 in fields], ng, ) ind = 0 for chunk in chunks: f = None for g in chunk.objs: if g.filename is None: continue if f is None: f = h5py.File(g.filename, mode="r") gf = self._cached_fields.get(g.id, {}) nd = 0 for field in fields: if field in gf: nd = g.select(selector, gf[field], rv[field], ind) self._hits += 1 continue self._misses += 1 ftype, fname = field # add extra dimensions to make data 3D data = f[ftype][fname][()].astype(self._field_dtype) for dim in range(len(data.shape), 3): data = np.expand_dims(data, dim) if self._cache_on: self._cached_fields.setdefault(g.id, {}) self._cached_fields[g.id][field] = data nd = g.select(selector, data, rv[field], ind) # caches ind += nd if f: f.close() return rv def _read_particle_coords(self, chunks, ptf): pn = "particle_position_%s" chunks = list(chunks) for chunk in chunks: f = None for g in chunk.objs: if g.filename is None: continue if f is None: f = h5py.File(g.filename, mode="r") if g.NumberOfParticles == 0: continue for ptype, field_list in sorted(ptf.items()): units = parse_h5_attr(f[ptype][pn % "x"], "units") x, y, z = ( self.ds.arr(f[ptype][pn % ax][()].astype("float64"), units) for ax in "xyz" ) for field in field_list: if np.asarray(f[ptype][field]).ndim > 1: self._array_fields[field] = f[ptype][field].shape[1:] yield ptype, (x, y, z), 0.0 if f: f.close() def _read_particle_fields(self, chunks, ptf, selector): pn = "particle_position_%s" chunks = list(chunks) for chunk in chunks: # These should be organized by grid filename f = None for g in chunk.objs: if g.filename is None: continue if f is None: f = h5py.File(g.filename, mode="r") if g.NumberOfParticles == 0: continue for ptype, field_list in sorted(ptf.items()): units = parse_h5_attr(f[ptype][pn % "x"], "units") x, y, z = ( self.ds.arr(f[ptype][pn % ax][()].astype("float64"), units) for ax in "xyz" ) mask = selector.select_points(x, y, z, 0.0) if mask is None: continue for field in field_list: data = np.asarray(f[ptype][field][()], "=f8") yield (ptype, field), data[mask] if f: f.close() class IOHandlerYTDataContainerHDF5(BaseIOHandler): _dataset_type = "ytdatacontainer_hdf5" def _read_fluid_selection(self, chunks, selector, fields, size): raise NotImplementedError def _yield_coordinates(self, data_file): with h5py.File(data_file.filename, mode="r") as f: for ptype in f.keys(): if "x" not in f[ptype].keys(): continue units = _get_position_array_units(ptype, f, "x") x, y, z = ( self.ds.arr(_get_position_array(ptype, f, ax), units) for ax in "xyz" ) pos = uvstack([x, y, z]).T pos.convert_to_units("code_length") yield ptype, pos def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. for data_file in self._sorted_chunk_iterator(chunks): index_mask = slice(data_file.start, data_file.end) with h5py.File(data_file.filename, mode="r") as f: for ptype in sorted(ptf): pcount = data_file.total_particles[ptype] if pcount == 0: continue units = _get_position_array_units(ptype, f, "x") x, y, z = ( self.ds.arr( _get_position_array(ptype, f, ax, index_mask=index_mask), units, ) for ax in "xyz" ) yield ptype, (x, y, z), 0.0 def _read_particle_data_file(self, data_file, ptf, selector): data_return = {} with h5py.File(data_file.filename, mode="r") as f: index_mask = slice(data_file.start, data_file.end) for ptype, field_list in sorted(ptf.items()): if selector is None or getattr(selector, "is_all_data", False): mask = index_mask else: units = _get_position_array_units(ptype, f, "x") x, y, z = ( self.ds.arr( _get_position_array(ptype, f, ax, index_mask=index_mask), units, ) for ax in "xyz" ) mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: data = f[ptype][field][mask].astype("float64", copy=False) data_return[ptype, field] = data return data_return def _count_particles(self, data_file): si, ei = data_file.start, data_file.end if None not in (si, ei): pcount = {} for ptype, npart in self.ds.num_particles.items(): pcount[ptype] = np.clip(npart - si, 0, ei - si) else: pcount = self.ds.num_particles return pcount def _identify_fields(self, data_file): fields = [] units = {} with h5py.File(data_file.filename, mode="r") as f: for ptype in f: fields.extend([(ptype, str(field)) for field in f[ptype]]) units.update( { (ptype, str(field)): parse_h5_attr(f[ptype][field], "units") for field in f[ptype] } ) return fields, units class IOHandlerYTSpatialPlotHDF5(IOHandlerYTDataContainerHDF5): _dataset_type = "ytspatialplot_hdf5" def _read_particle_coords(self, chunks, ptf): # This will read chunks and yield the results. for data_file in self._sorted_chunk_iterator(chunks): with h5py.File(data_file.filename, mode="r") as f: for ptype in sorted(ptf): pcount = data_file.total_particles[ptype] if pcount == 0: continue x = _get_position_array(ptype, f, "px") y = _get_position_array(ptype, f, "py") z = ( np.zeros(x.size, dtype="float64") + self.ds.domain_left_edge[2].to("code_length").d ) yield ptype, (x, y, z), 0.0 def _read_particle_fields(self, chunks, ptf, selector): # Now we have all the sizes, and we can allocate for data_file in self._sorted_chunk_iterator(chunks): all_count = self._count_particles(data_file) with h5py.File(data_file.filename, mode="r") as f: for ptype, field_list in sorted(ptf.items()): x = _get_position_array(ptype, f, "px") y = _get_position_array(ptype, f, "py") z = ( np.zeros(all_count[ptype], dtype="float64") + self.ds.domain_left_edge[2].to("code_length").d ) mask = selector.select_points(x, y, z, 0.0) del x, y, z if mask is None: continue for field in field_list: data = f[ptype][field][mask].astype("float64") yield (ptype, field), data def _get_position_array(ptype, f, ax, index_mask=None): if index_mask is None: index_mask = slice(None, None, None) if ptype == "grid": pos_name = "" else: pos_name = "particle_position_" return f[ptype][pos_name + ax][index_mask].astype("float64") def _get_position_array_units(ptype, f, ax): if ptype == "grid": pos_name = "" else: pos_name = "particle_position_" return parse_h5_attr(f[ptype][pos_name + ax], "units") yt-project-yt-f043ac8/yt/frontends/ytdata/tests/000077500000000000000000000000001510711153200217405ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ytdata/tests/__init__.py000066400000000000000000000000001510711153200240370ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/frontends/ytdata/tests/test_data_reload.py000066400000000000000000000017621510711153200256160ustar00rootroot00000000000000import numpy as np from yt.loaders import load_uniform_grid from yt.testing import requires_module_pytest from yt.utilities.on_demand_imports import _h5py as h5py @requires_module_pytest("h5py") def test_save_as_data_unit_system(tmp_path): # This test checks that the file saved with calling save_as_dataset # for a ds with a "code" unit system contains the proper "unit_system_name". # It checks the hdf5 file directly rather than using yt.load(), because # https://github.com/yt-project/yt/issues/4315 only manifested restarting # the python kernel (because the unit registry is state dependent). fi = tmp_path / "output_data.h5" shp = (4, 4, 4) data = {"density": np.random.random(shp)} ds = load_uniform_grid(data, shp, unit_system="code") assert "code" in ds._unit_system_name sp = ds.sphere(ds.domain_center, ds.domain_width[0] / 2.0) sp.save_as_dataset(fi) with h5py.File(fi, mode="r") as f: assert f.attrs["unit_system_name"] == "code" yt-project-yt-f043ac8/yt/frontends/ytdata/tests/test_old_outputs.py000066400000000000000000000157351510711153200257450ustar00rootroot00000000000000""" ytdata frontend tests using enzo_tiny_cosmology """ import os import shutil import tempfile import numpy as np from yt.data_objects.api import create_profile from yt.frontends.ytdata.api import ( YTDataContainerDataset, YTGridDataset, YTNonspatialDataset, YTProfileDataset, YTSpatialPlotDataset, ) from yt.frontends.ytdata.tests.test_outputs import ( YTDataFieldTest, compare_unit_attributes, ) from yt.testing import ( assert_allclose_units, assert_array_equal, requires_file, requires_module, skip, ) from yt.units.yt_array import YTArray from yt.utilities.answer_testing.framework import data_dir_load, requires_ds from yt.visualization.profile_plotter import PhasePlot, ProfilePlot enzotiny = "enzo_tiny_cosmology/DD0046/DD0046" ytdata_dir = "ytdata_test" @skip(reason="See https://github.com/yt-project/yt/issues/3909") @requires_module("h5py") @requires_ds(enzotiny) @requires_file(os.path.join(ytdata_dir, "DD0046_sphere.h5")) @requires_file(os.path.join(ytdata_dir, "DD0046_cut_region.h5")) def test_old_datacontainer_data(): ds = data_dir_load(enzotiny) sphere = ds.sphere(ds.domain_center, (10, "Mpc")) fn = "DD0046_sphere.h5" full_fn = os.path.join(ytdata_dir, fn) sphere_ds = data_dir_load(full_fn) compare_unit_attributes(ds, sphere_ds) assert isinstance(sphere_ds, YTDataContainerDataset) yield YTDataFieldTest(full_fn, ("grid", "density")) yield YTDataFieldTest(full_fn, ("all", "particle_mass")) cr = ds.cut_region(sphere, ['obj["gas", "temperature"] > 1e4']) fn = "DD0046_cut_region.h5" full_fn = os.path.join(ytdata_dir, fn) cr_ds = data_dir_load(full_fn) assert isinstance(cr_ds, YTDataContainerDataset) assert (cr["gas", "temperature"] == cr_ds.data["gas", "temperature"]).all() @skip(reason="See https://github.com/yt-project/yt/issues/3909") @requires_module("h5py") @requires_ds(enzotiny) @requires_file(os.path.join(ytdata_dir, "DD0046_covering_grid.h5")) @requires_file(os.path.join(ytdata_dir, "DD0046_arbitrary_grid.h5")) @requires_file(os.path.join(ytdata_dir, "DD0046_proj_frb.h5")) def test_old_grid_datacontainer_data(): ds = data_dir_load(enzotiny) fn = "DD0046_covering_grid.h5" full_fn = os.path.join(ytdata_dir, fn) cg_ds = data_dir_load(full_fn) compare_unit_attributes(ds, cg_ds) assert isinstance(cg_ds, YTGridDataset) yield YTDataFieldTest(full_fn, ("grid", "density")) yield YTDataFieldTest(full_fn, ("all", "particle_mass")) fn = "DD0046_arbitrary_grid.h5" full_fn = os.path.join(ytdata_dir, fn) ag_ds = data_dir_load(full_fn) compare_unit_attributes(ds, ag_ds) assert isinstance(ag_ds, YTGridDataset) yield YTDataFieldTest(full_fn, ("grid", "density")) yield YTDataFieldTest(full_fn, ("all", "particle_mass")) my_proj = ds.proj("density", "x", weight_field="density") frb = my_proj.to_frb(1.0, (800, 800)) fn = "DD0046_proj_frb.h5" full_fn = os.path.join(ytdata_dir, fn) frb_ds = data_dir_load(full_fn) assert_allclose_units(frb["gas", "density"], frb_ds.data["gas", "density"], 1e-7) compare_unit_attributes(ds, frb_ds) assert isinstance(frb_ds, YTGridDataset) yield YTDataFieldTest(full_fn, ("gas", "density"), geometric=False) @skip(reason="See https://github.com/yt-project/yt/issues/3909") @requires_module("h5py") @requires_ds(enzotiny) @requires_file(os.path.join(ytdata_dir, "DD0046_proj.h5")) def test_old_spatial_data(): ds = data_dir_load(enzotiny) fn = "DD0046_proj.h5" full_fn = os.path.join(ytdata_dir, fn) proj_ds = data_dir_load(full_fn) compare_unit_attributes(ds, proj_ds) assert isinstance(proj_ds, YTSpatialPlotDataset) yield YTDataFieldTest(full_fn, ("gas", "density"), geometric=False) @skip(reason="See https://github.com/yt-project/yt/issues/3909") @requires_module("h5py") @requires_ds(enzotiny) @requires_file(os.path.join(ytdata_dir, "DD0046_Profile1D.h5")) @requires_file(os.path.join(ytdata_dir, "DD0046_Profile2D.h5")) def test_old_profile_data(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(enzotiny) ad = ds.all_data() profile_1d = create_profile( ad, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "cell_mass"), ) fn = "DD0046_Profile1D.h5" full_fn = os.path.join(ytdata_dir, fn) prof_1d_ds = data_dir_load(full_fn) compare_unit_attributes(ds, prof_1d_ds) assert isinstance(prof_1d_ds, YTProfileDataset) for field in profile_1d.standard_deviation: assert_array_equal( profile_1d.standard_deviation[field], prof_1d_ds.profile.standard_deviation["data", field[1]], ) p1 = ProfilePlot( prof_1d_ds.data, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "cell_mass"), ) p1.save() yield YTDataFieldTest(full_fn, ("gas", "temperature"), geometric=False) yield YTDataFieldTest(full_fn, ("index", "x"), geometric=False) yield YTDataFieldTest(full_fn, ("gas", "density"), geometric=False) fn = "DD0046_Profile2D.h5" full_fn = os.path.join(ytdata_dir, fn) prof_2d_ds = data_dir_load(full_fn) compare_unit_attributes(ds, prof_2d_ds) assert isinstance(prof_2d_ds, YTProfileDataset) p2 = PhasePlot( prof_2d_ds.data, ("gas", "density"), ("gas", "temperature"), ("gas", "cell_mass"), weight_field=None, ) p2.save() yield YTDataFieldTest(full_fn, ("gas", "density"), geometric=False) yield YTDataFieldTest(full_fn, ("index", "x"), geometric=False) yield YTDataFieldTest(full_fn, ("gas", "temperature"), geometric=False) yield YTDataFieldTest(full_fn, ("index", "y"), geometric=False) yield YTDataFieldTest(full_fn, ("gas", "cell_mass"), geometric=False) os.chdir(curdir) shutil.rmtree(tmpdir) @skip(reason="See https://github.com/yt-project/yt/issues/3909") @requires_module("h5py") @requires_ds(enzotiny) @requires_file(os.path.join(ytdata_dir, "test_data.h5")) @requires_file(os.path.join(ytdata_dir, "random_data.h5")) def test_old_nonspatial_data(): ds = data_dir_load(enzotiny) region = ds.box([0.25] * 3, [0.75] * 3) sphere = ds.sphere(ds.domain_center, (10, "Mpc")) my_data = {} my_data["region_density"] = region["gas", "density"] my_data["sphere_density"] = sphere["gas", "density"] fn = "test_data.h5" full_fn = os.path.join(ytdata_dir, fn) array_ds = data_dir_load(full_fn) compare_unit_attributes(ds, array_ds) assert isinstance(array_ds, YTNonspatialDataset) yield YTDataFieldTest(full_fn, "region_density", geometric=False) yield YTDataFieldTest(full_fn, "sphere_density", geometric=False) my_data = {"density": YTArray(np.linspace(1.0, 20.0, 10), "g/cm**3")} fn = "random_data.h5" full_fn = os.path.join(ytdata_dir, fn) new_ds = data_dir_load(full_fn) assert isinstance(new_ds, YTNonspatialDataset) yield YTDataFieldTest(full_fn, ("gas", "density"), geometric=False) yt-project-yt-f043ac8/yt/frontends/ytdata/tests/test_outputs.py000066400000000000000000000215211510711153200250750ustar00rootroot00000000000000import os import shutil import tempfile import numpy as np from numpy.testing import assert_array_equal, assert_equal from yt.data_objects.api import create_profile from yt.frontends.ytdata.api import ( YTDataContainerDataset, YTGridDataset, YTNonspatialDataset, YTProfileDataset, YTSpatialPlotDataset, save_as_dataset, ) from yt.loaders import load from yt.testing import assert_allclose_units from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.answer_testing.framework import ( AnswerTestingTest, data_dir_load, requires_ds, ) from yt.visualization.profile_plotter import PhasePlot, ProfilePlot def make_tempdir(): if int(os.environ.get("GENERATE_YTDATA", 0)): return "." else: return tempfile.mkdtemp() def compare_unit_attributes(ds1, ds2): r""" Checks to make sure that the length, mass, time, velocity, and magnetic units are the same for two different dataset objects. """ attrs = ("length_unit", "mass_unit", "time_unit", "velocity_unit", "magnetic_unit") for attr in attrs: u1 = getattr(ds1, attr, None) u2 = getattr(ds2, attr, None) assert u1 == u2 class YTDataFieldTest(AnswerTestingTest): _type_name = "YTDataTest" _attrs = ("field_name",) def __init__(self, ds_fn, field, decimals=10, geometric=True): super().__init__(ds_fn) self.field = field if isinstance(field, tuple) and len(field) == 2: self.field_name = field[1] else: self.field_name = field self.decimals = decimals self.geometric = geometric def run(self): if self.geometric: obj = self.ds.all_data() else: obj = self.ds.data num_e = obj[self.field].size avg = obj[self.field].mean() return np.array([num_e, avg]) def compare(self, new_result, old_result): err_msg = f"YTData field values for {self.field} not equal." if self.decimals is None: assert_equal(new_result, old_result, err_msg=err_msg, verbose=True) else: assert_allclose_units( new_result, old_result, 10.0 ** (-self.decimals), err_msg=err_msg, verbose=True, ) enzotiny = "enzo_tiny_cosmology/DD0046/DD0046" @requires_ds(enzotiny) def test_datacontainer_data(): tmpdir = make_tempdir() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(enzotiny) sphere = ds.sphere(ds.domain_center, (10, "Mpc")) fn = sphere.save_as_dataset(fields=[("gas", "density"), ("all", "particle_mass")]) full_fn = os.path.join(tmpdir, fn) sphere_ds = load(full_fn) compare_unit_attributes(ds, sphere_ds) assert isinstance(sphere_ds, YTDataContainerDataset) yield YTDataFieldTest(full_fn, ("grid", "density")) yield YTDataFieldTest(full_fn, ("all", "particle_mass")) cr = ds.cut_region(sphere, ['obj["gas", "temperature"] > 1e4']) fn = cr.save_as_dataset(fields=[("gas", "temperature")]) full_fn = os.path.join(tmpdir, fn) cr_ds = load(full_fn) assert isinstance(cr_ds, YTDataContainerDataset) assert (cr["gas", "temperature"] == cr_ds.data["gas", "temperature"]).all() os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) @requires_ds(enzotiny) def test_grid_datacontainer_data(): tmpdir = make_tempdir() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(enzotiny) cg = ds.covering_grid(level=0, left_edge=[0.25] * 3, dims=[16] * 3) fn = cg.save_as_dataset( fields=[ ("gas", "density"), ("all", "particle_mass"), ("all", "particle_position"), ] ) full_fn = os.path.join(tmpdir, fn) cg_ds = load(full_fn) compare_unit_attributes(ds, cg_ds) assert isinstance(cg_ds, YTGridDataset) assert ( cg["all", "particle_position"].shape == cg_ds.r["all", "particle_position"].shape ) yield YTDataFieldTest(full_fn, ("grid", "density")) yield YTDataFieldTest(full_fn, ("all", "particle_mass")) ag = ds.arbitrary_grid(left_edge=[0.25] * 3, right_edge=[0.75] * 3, dims=[16] * 3) fn = ag.save_as_dataset(fields=[("gas", "density"), ("all", "particle_mass")]) full_fn = os.path.join(tmpdir, fn) ag_ds = load(full_fn) compare_unit_attributes(ds, ag_ds) assert isinstance(ag_ds, YTGridDataset) yield YTDataFieldTest(full_fn, ("grid", "density")) yield YTDataFieldTest(full_fn, ("all", "particle_mass")) my_proj = ds.proj(("gas", "density"), "x", weight_field=("gas", "density")) frb = my_proj.to_frb(1.0, (800, 800)) fn = frb.save_as_dataset(fields=[("gas", "density")]) frb_ds = load(fn) assert_array_equal(frb["gas", "density"], frb_ds.data["gas", "density"]) compare_unit_attributes(ds, frb_ds) assert isinstance(frb_ds, YTGridDataset) yield YTDataFieldTest(full_fn, ("grid", "density"), geometric=False) os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) @requires_ds(enzotiny) def test_spatial_data(): tmpdir = make_tempdir() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(enzotiny) proj = ds.proj(("gas", "density"), "x", weight_field=("gas", "density")) fn = proj.save_as_dataset() full_fn = os.path.join(tmpdir, fn) proj_ds = load(full_fn) compare_unit_attributes(ds, proj_ds) assert isinstance(proj_ds, YTSpatialPlotDataset) yield YTDataFieldTest(full_fn, ("grid", "density"), geometric=False) os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) @requires_ds(enzotiny) def test_profile_data(): tmpdir = make_tempdir() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(enzotiny) ad = ds.all_data() profile_1d = create_profile( ad, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "cell_mass"), ) fn = profile_1d.save_as_dataset() full_fn = os.path.join(tmpdir, fn) prof_1d_ds = load(full_fn) compare_unit_attributes(ds, prof_1d_ds) assert isinstance(prof_1d_ds, YTProfileDataset) for field in profile_1d.standard_deviation: assert_array_equal( profile_1d.standard_deviation[field], prof_1d_ds.profile.standard_deviation["data", field[1]], ) p1 = ProfilePlot( prof_1d_ds.data, ("gas", "density"), ("gas", "temperature"), weight_field=("gas", "cell_mass"), ) p1.save() yield YTDataFieldTest(full_fn, ("data", "temperature"), geometric=False) yield YTDataFieldTest(full_fn, ("data", "x"), geometric=False) yield YTDataFieldTest(full_fn, ("data", "density"), geometric=False) profile_2d = create_profile( ad, [("gas", "density"), ("gas", "temperature")], ("gas", "cell_mass"), weight_field=None, n_bins=(128, 128), ) fn = profile_2d.save_as_dataset() full_fn = os.path.join(tmpdir, fn) prof_2d_ds = load(full_fn) compare_unit_attributes(ds, prof_2d_ds) assert isinstance(prof_2d_ds, YTProfileDataset) p2 = PhasePlot( prof_2d_ds.data, ("gas", "density"), ("gas", "temperature"), ("gas", "cell_mass"), weight_field=None, ) p2.save() yield YTDataFieldTest(full_fn, ("data", "density"), geometric=False) yield YTDataFieldTest(full_fn, ("data", "x"), geometric=False) yield YTDataFieldTest(full_fn, ("data", "temperature"), geometric=False) yield YTDataFieldTest(full_fn, ("data", "y"), geometric=False) yield YTDataFieldTest(full_fn, ("data", "cell_mass"), geometric=False) os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) @requires_ds(enzotiny) def test_nonspatial_data(): tmpdir = make_tempdir() curdir = os.getcwd() os.chdir(tmpdir) ds = data_dir_load(enzotiny) region = ds.box([0.25] * 3, [0.75] * 3) sphere = ds.sphere(ds.domain_center, (10, "Mpc")) my_data = {} my_data["region_density"] = region["gas", "density"] my_data["sphere_density"] = sphere["gas", "density"] fn = "test_data.h5" save_as_dataset(ds, fn, my_data) full_fn = os.path.join(tmpdir, fn) array_ds = load(full_fn) compare_unit_attributes(ds, array_ds) assert isinstance(array_ds, YTNonspatialDataset) yield YTDataFieldTest(full_fn, "region_density", geometric=False) yield YTDataFieldTest(full_fn, "sphere_density", geometric=False) my_data = {"density": YTArray(np.linspace(1.0, 20.0, 10), "g/cm**3")} fake_ds = {"current_time": YTQuantity(10, "Myr")} fn = "random_data.h5" save_as_dataset(fake_ds, fn, my_data) full_fn = os.path.join(tmpdir, fn) new_ds = load(full_fn) assert isinstance(new_ds, YTNonspatialDataset) yield YTDataFieldTest(full_fn, ("data", "density"), geometric=False) os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) yt-project-yt-f043ac8/yt/frontends/ytdata/tests/test_unit.py000066400000000000000000000075011510711153200243330ustar00rootroot00000000000000import os import shutil import tempfile import numpy as np from numpy.testing import assert_array_equal from yt.loaders import load, load_uniform_grid from yt.testing import ( assert_fname, fake_random_ds, requires_file, requires_module, ) from yt.utilities.answer_testing.framework import data_dir_load from yt.visualization.plot_window import ProjectionPlot, SlicePlot ytdata_dir = "ytdata_test" @requires_module("h5py") @requires_file(os.path.join(ytdata_dir, "slice.h5")) @requires_file(os.path.join(ytdata_dir, "proj.h5")) @requires_file(os.path.join(ytdata_dir, "oas.h5")) def test_old_plot_data(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) fn = "slice.h5" full_fn = os.path.join(ytdata_dir, fn) ds_slice = data_dir_load(full_fn) p = SlicePlot(ds_slice, "z", ("gas", "density")) fn = p.save() assert_fname(fn[0]) fn = "proj.h5" full_fn = os.path.join(ytdata_dir, fn) ds_proj = data_dir_load(full_fn) p = ProjectionPlot(ds_proj, "z", ("gas", "density")) fn = p.save() assert_fname(fn[0]) fn = "oas.h5" full_fn = os.path.join(ytdata_dir, fn) ds_oas = data_dir_load(full_fn) p = SlicePlot(ds_oas, [1, 1, 1], ("gas", "density")) fn = p.save() assert_fname(fn[0]) os.chdir(curdir) shutil.rmtree(tmpdir) @requires_module("h5py") def test_plot_data(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) ds = fake_random_ds(16) plot = SlicePlot(ds, "z", ("gas", "density")) fn = plot.data_source.save_as_dataset("slice.h5") ds_slice = load(fn) p = SlicePlot(ds_slice, "z", ("gas", "density")) fn = p.save() assert_fname(fn[0]) plot = ProjectionPlot(ds, "z", ("gas", "density")) fn = plot.data_source.save_as_dataset("proj.h5") ds_proj = load(fn) p = ProjectionPlot(ds_proj, "z", ("gas", "density")) fn = p.save() assert_fname(fn[0]) plot = SlicePlot(ds, [1, 1, 1], ("gas", "density")) fn = plot.data_source.save_as_dataset("oas.h5") ds_oas = load(fn) p = SlicePlot(ds_oas, [1, 1, 1], ("gas", "density")) fn = p.save() assert_fname(fn[0]) os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) @requires_module("h5py") def test_non_square_frb(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) # construct an arbitrary dataset arr = np.arange(8.0 * 9.0 * 10.0).reshape((8, 9, 10)) data = {"density": (arr, "g/cm**3")} bbox = np.array([[-4, 4.0], [-4.5, 4.5], [-5.0, 5]]) ds = load_uniform_grid( data, arr.shape, length_unit="Mpc", bbox=bbox, periodicity=(False, False, False) ) # make a slice slc = ds.slice(axis="z", coord=ds.quan(0.0, "code_length")) # make a frb and save it to disk center = (ds.quan(0.0, "code_length"), ds.quan(0.0, "code_length")) xax, yax = ds.coordinates.x_axis[slc.axis], ds.coordinates.y_axis[slc.axis] res = [ds.domain_dimensions[xax], ds.domain_dimensions[yax]] # = [8,9] width = ds.domain_right_edge[xax] - ds.domain_left_edge[xax] # = 8 code_length height = ds.domain_right_edge[yax] - ds.domain_left_edge[yax] # = 9 code_length frb = slc.to_frb(width=width, height=height, resolution=res, center=center) fname = "test_frb_roundtrip.h5" frb.save_as_dataset(fname, fields=[("gas", "density")]) expected_vals = arr[:, :, 5].T print( "\nConfirmation that initial frb results are expected:", (expected_vals == frb["gas", "density"].v).all(), "\n", ) # yt-reload: reloaded_ds = load(fname) assert_array_equal( frb["gas", "density"].shape, reloaded_ds.data["gas", "density"].shape ) assert_array_equal(frb["gas", "density"], reloaded_ds.data["gas", "density"]) os.chdir(curdir) if tmpdir != ".": shutil.rmtree(tmpdir) yt-project-yt-f043ac8/yt/frontends/ytdata/utilities.py000066400000000000000000000166511510711153200231740ustar00rootroot00000000000000from yt.units.yt_array import YTArray from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py def save_as_dataset(ds, filename, data, field_types=None, extra_attrs=None): r"""Export a set of field arrays to a reloadable yt dataset. This function can be used to create a yt loadable dataset from a set of arrays. The field arrays can either be associated with a loaded dataset or, if not, a dictionary of dataset attributes can be provided that will be used as metadata for the new dataset. The resulting dataset can be reloaded as a yt dataset. Parameters ---------- ds : dataset or dict The dataset associated with the fields or a dictionary of parameters. filename : str The name of the file to be written. data : dict A dictionary of field arrays to be saved. field_types: dict, optional A dictionary denoting the group name to which each field is to be saved. When the resulting dataset is reloaded, this will be the field type for this field. If not given, "data" will be used. extra_attrs: dict, optional A dictionary of additional attributes to be saved. Returns ------- filename : str The name of the file that has been created. Examples -------- >>> import numpy as np >>> import yt >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") >>> sphere = ds.sphere([0.5] * 3, (10, "Mpc")) >>> sphere_density = sphere["gas", "density"] >>> region = ds.box([0.0] * 3, [0.25] * 3) >>> region_density = region["gas", "density"] >>> data = {} >>> data["sphere_density"] = sphere_density >>> data["region_density"] = region_density >>> yt.save_as_dataset(ds, "density_data.h5", data) >>> new_ds = yt.load("density_data.h5") >>> print(new_ds.data["region_density"]) [ 7.47650434e-32 7.70370740e-32 9.74692941e-32 ..., 1.22384547e-27 5.13889063e-28 2.91811974e-28] g/cm**3 >>> print(new_ds.data["sphere_density"]) [ 4.46237613e-32 4.86830178e-32 4.46335118e-32 ..., 6.43956165e-30 3.57339907e-30 2.83150720e-30] g/cm**3 >>> data = { ... "density": yt.YTArray(1e-24 * np.ones(10), "g/cm**3"), ... "temperature": yt.YTArray(1000.0 * np.ones(10), "K"), ... } >>> ds_data = {"current_time": yt.YTQuantity(10, "Myr")} >>> yt.save_as_dataset(ds_data, "random_data.h5", data) >>> new_ds = yt.load("random_data.h5") >>> print(new_ds.data["gas", "temperature"]) [ 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000.] K """ mylog.info("Saving field data to yt dataset: %s.", filename) if extra_attrs is None: extra_attrs = {} base_attrs = [ "dimensionality", "domain_left_edge", "domain_right_edge", "current_redshift", "current_time", "domain_dimensions", "geometry", "periodicity", "cosmological_simulation", "omega_lambda", "omega_matter", "hubble_constant", "length_unit", "mass_unit", "time_unit", "velocity_unit", "magnetic_unit", ] fh = h5py.File(filename, mode="w") if ds is None: ds = {} if hasattr(ds, "parameters") and isinstance(ds.parameters, dict): for attr, val in ds.parameters.items(): _yt_array_hdf5_attr(fh, attr, val) if hasattr(ds, "unit_registry"): _yt_array_hdf5_attr(fh, "unit_registry_json", ds.unit_registry.to_json()) if hasattr(ds, "unit_system"): # Note: ds._unit_system_name is written here rather than # ds.unit_system.name because for a 'code' unit system, ds.unit_system.name # is a hash, not a unit system. And on re-load, we want to designate # a unit system not a hash value. # See https://github.com/yt-project/yt/issues/4315 for more background. _yt_array_hdf5_attr(fh, "unit_system_name", ds._unit_system_name) for attr in base_attrs: if isinstance(ds, dict): my_val = ds.get(attr, None) else: my_val = getattr(ds, attr, None) if my_val is None: continue _yt_array_hdf5_attr(fh, attr, my_val) for attr in extra_attrs: my_val = extra_attrs[attr] _yt_array_hdf5_attr(fh, attr, my_val) if "data_type" not in extra_attrs: fh.attrs["data_type"] = "yt_array_data" for field in data: if field_types is None: field_type = "data" else: field_type = field_types[field] if field_type not in fh: fh.create_group(field_type) if isinstance(field, tuple): field_name = field[1] else: field_name = field # for python3 if data[field].dtype.kind == "U": data[field] = data[field].astype("|S") _yt_array_hdf5(fh[field_type], field_name, data[field]) if "num_elements" not in fh[field_type].attrs: fh[field_type].attrs["num_elements"] = data[field].size fh.close() return filename def _hdf5_yt_array(fh, field, ds=None): r"""Load an hdf5 dataset as a YTArray. Reads in a dataset from an open hdf5 file or group and uses the "units" attribute, if it exists, to apply units. Parameters ---------- fh : an open hdf5 file or hdf5 group The hdf5 file or group in which the dataset exists. field : str The name of the field to be loaded. ds : yt Dataset If not None, the unit_registry of the dataset is used to apply units. Returns ------- A YTArray of the requested field. """ if ds is None: new_arr = YTArray else: new_arr = ds.arr units = "" if "units" in fh[field].attrs: units = fh[field].attrs["units"] if units == "dimensionless": units = "" return new_arr(fh[field][()], units) def _yt_array_hdf5(fh, field, data): r"""Save a YTArray to an open hdf5 file or group. Save a YTArray to an open hdf5 file or group, and save the units to a "units" attribute. Parameters ---------- fh : an open hdf5 file or hdf5 group The hdf5 file or group to which the data will be written. field : str The name of the field to be saved. data : YTArray The data array to be saved. Returns ------- dataset : hdf5 dataset The created hdf5 dataset. """ dataset = fh.create_dataset(str(field), data=data) units = "" if isinstance(data, YTArray): units = str(data.units) dataset.attrs["units"] = units return dataset def _yt_array_hdf5_attr(fh, attr, val): r"""Save a YTArray or YTQuantity as an hdf5 attribute. Save an hdf5 attribute. If it has units, save an additional attribute with the units. Parameters ---------- fh : an open hdf5 file, group, or dataset The hdf5 file, group, or dataset to which the attribute will be written. attr : str The name of the attribute to be saved. val : anything The value to be saved. """ if val is None: val = "None" if hasattr(val, "units"): fh.attrs[f"{attr}_units"] = str(val.units) try: fh.attrs[str(attr)] = val # This is raised if no HDF5 equivalent exists. # In that case, save its string representation. except TypeError: fh.attrs[str(attr)] = str(val) yt-project-yt-f043ac8/yt/funcs.py000066400000000000000000001255761510711153200170160ustar00rootroot00000000000000import base64 import contextlib import copy import errno import glob import inspect import itertools import os import re import struct import subprocess import sys import time import traceback from collections import UserDict from collections.abc import Callable from copy import deepcopy from functools import lru_cache, wraps from numbers import Number as numeric_type from typing import Any import numpy as np from more_itertools import always_iterable, collapse, first from yt._maintenance.deprecation import issue_deprecation_warning from yt._maintenance.ipython_compat import IS_IPYTHON from yt.config import ytcfg from yt.units import YTArray, YTQuantity from yt.utilities.exceptions import YTFieldNotFound, YTInvalidWidthError from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _requests as requests # Some functions for handling sequences and other types def is_sequence(obj): """ Grabbed from Python Cookbook / matplotlib.cbook. Returns true/false for Parameters ---------- obj : iterable """ try: len(obj) return True except TypeError: return False def iter_fields(field_or_fields): """ Create an iterator for field names, specified as single strings or tuples(fname, ftype) alike. This can safely be used in places where we accept a single field or a list as input. Parameters ---------- field_or_fields: str, tuple(str, str), or any iterable of the previous types. Examples -------- >>> fields = ("gas", "density") >>> for field in iter_fields(fields): ... print(field) density >>> fields = ("gas", "density") >>> for field in iter_fields(fields): ... print(field) ('gas', 'density') >>> fields = [("gas", "density"), ("gas", "temperature"), ("index", "dx")] >>> for field in iter_fields(fields): ... print(field) density temperature ('index', 'dx') """ return always_iterable(field_or_fields, base_type=(tuple, str, bytes)) def ensure_numpy_array(obj): """ This function ensures that *obj* is a numpy array. Typically used to convert scalar, list or tuple argument passed to functions using Cython. """ if isinstance(obj, np.ndarray): if obj.shape == (): return np.array([obj]) # We cast to ndarray to catch ndarray subclasses return np.array(obj) elif isinstance(obj, (list, tuple)): return np.asarray(obj) else: return np.asarray([obj]) def read_struct(f, fmt): """ This reads a struct, and only that struct, from an open file. """ s = f.read(struct.calcsize(fmt)) return struct.unpack(fmt, s) def just_one(obj): # If we have an iterable, sometimes we only want one item return first(collapse(obj)) def compare_dicts(dict1, dict2): if not set(dict1) <= set(dict2): return False for key in dict1.keys(): if dict1[key] is not None and dict2[key] is not None: if isinstance(dict1[key], dict): if compare_dicts(dict1[key], dict2[key]): continue else: return False try: comparison = np.array_equal(dict1[key], dict2[key]) except TypeError: comparison = dict1[key] == dict2[key] if not comparison: return False return True # Taken from # http://www.goldb.org/goldblog/2008/02/06/PythonConvertSecsIntoHumanReadableTimeStringHHMMSS.aspx def humanize_time(secs): """ Takes *secs* and returns a nicely formatted string """ mins, secs = divmod(secs, 60) hours, mins = divmod(mins, 60) return "%02d:%02d:%02d" % (hours, mins, secs) # # Some function wrappers that come in handy once in a while # def get_memory_usage(subtract_share=False): """ Returning resident size in megabytes """ pid = os.getpid() # we use the resource module to get the memory page size try: import resource except ImportError: return -1024 else: pagesize = resource.getpagesize() status_file = f"/proc/{pid}/statm" if not os.path.isfile(status_file): return -1024 with open(status_file) as fh: line = fh.read() size, resident, share, text, library, data, dt = (int(i) for i in line.split()) if subtract_share: resident -= share return resident * pagesize / (1024 * 1024) # return in megs def time_execution(func): r""" Decorator for seeing how long a given function takes, depending on whether or not the global 'yt.time_functions' config parameter is set. """ @wraps(func) def wrapper(*arg, **kw): t1 = time.time() res = func(*arg, **kw) t2 = time.time() mylog.debug("%s took %0.3f s", func.__name__, (t2 - t1)) return res if ytcfg.get("yt", "time_functions"): return wrapper else: return func def print_tb(func): """ This function is used as a decorate on a function to have the calling stack printed whenever that function is entered. This can be used like so: >>> @print_tb ... def some_deeply_nested_function(*args, **kwargs): ... ... """ @wraps(func) def run_func(*args, **kwargs): traceback.print_stack() return func(*args, **kwargs) return run_func def rootonly(func): """ This is a decorator that, when used, will only call the function on the root processor. This can be used like so: .. code-block:: python @rootonly def some_root_only_function(*args, **kwargs): ... """ @wraps(func) def check_parallel_rank(*args, **kwargs): if ytcfg.get("yt", "internals", "topcomm_parallel_rank") > 0: return return func(*args, **kwargs) return check_parallel_rank def pdb_run(func): """ This decorator inserts a pdb session on top of the call-stack into a function. This can be used like so: >>> @pdb_run ... def some_function_to_debug(*args, **kwargs): ... ... """ import pdb @wraps(func) def wrapper(*args, **kw): pdb.runcall(func, *args, **kw) return wrapper __header = """ == Welcome to the embedded IPython Shell == You are currently inside the function: %(fname)s Defined in: %(filename)s:%(lineno)s """ def insert_ipython(num_up=1): """ Placed inside a function, this will insert an IPython interpreter at that current location. This will enabled detailed inspection of the current execution environment, as well as (optional) modification of that environment. *num_up* refers to how many frames of the stack get stripped off, and defaults to 1 so that this function itself is stripped off. """ import IPython from IPython.terminal.embed import InteractiveShellEmbed try: from traitlets.config.loader import Config except ImportError: from IPython.config.loader import Config frame = inspect.stack()[num_up] loc = frame[0].f_locals.copy() glo = frame[0].f_globals dd = {"fname": frame[3], "filename": frame[1], "lineno": frame[2]} cfg = Config() cfg.InteractiveShellEmbed.local_ns = loc cfg.InteractiveShellEmbed.global_ns = glo IPython.embed(config=cfg, banner2=__header % dd) ipshell = InteractiveShellEmbed(config=cfg) del ipshell # # Our progress bar types and how to get one # class TqdmProgressBar: # This is a drop in replacement for pbar # called tqdm def __init__(self, title, maxval): from tqdm import tqdm self._pbar = tqdm(leave=True, total=maxval, desc=title) self.i = 0 def update(self, i=None): if i is None: i = self.i + 1 n = i - self.i self.i = i self._pbar.update(n) def finish(self): self._pbar.close() class DummyProgressBar: # This progressbar gets handed if we don't # want ANY output def __init__(self, *args, **kwargs): return def update(self, *args, **kwargs): return def finish(self, *args, **kwargs): return def get_pbar(title, maxval): """ This returns a progressbar of the most appropriate type, given a *title* and a *maxval*. """ maxval = max(maxval, 1) if ( ytcfg.get("yt", "suppress_stream_logging") or ytcfg.get("yt", "internals", "within_testing") or maxval == 1 or not is_root() ): return DummyProgressBar() return TqdmProgressBar(title, maxval) def only_on_root(func, *args, **kwargs): """ This function accepts a *func*, a set of *args* and *kwargs* and then only on the root processor calls the function. All other processors get "None" handed back. """ if kwargs.pop("global_rootonly", False): cfg_option = "global_parallel_rank" else: cfg_option = "topcomm_parallel_rank" if not ytcfg.get("yt", "internals", "parallel"): return func(*args, **kwargs) if ytcfg.get("yt", "internals", cfg_option) > 0: return return func(*args, **kwargs) def is_root(): """ This function returns True if it is on the root processor of the topcomm and False otherwise. """ if not ytcfg.get("yt", "internals", "parallel"): return True return ytcfg.get("yt", "internals", "topcomm_parallel_rank") == 0 # # Our signal and traceback handling functions # def signal_print_traceback(signo, frame): print(traceback.print_stack(frame)) def signal_problem(signo, frame): raise RuntimeError() def signal_ipython(signo, frame): insert_ipython(2) def paste_traceback(exc_type, exc, tb): """ This is a traceback handler that knows how to paste to the pastebin. Should only be used in sys.excepthook. """ sys.__excepthook__(exc_type, exc, tb) import xmlrpc.client from io import StringIO p = xmlrpc.client.ServerProxy( "http://paste.yt-project.org/xmlrpc/", allow_none=True ) s = StringIO() traceback.print_exception(exc_type, exc, tb, file=s) s = s.getvalue() ret = p.pastes.newPaste("pytb", s, None, "", "", True) print() print(f"Traceback pasted to http://paste.yt-project.org/show/{ret}") print() def paste_traceback_detailed(exc_type, exc, tb): """ This is a traceback handler that knows how to paste to the pastebin. Should only be used in sys.excepthook. """ import cgitb import xmlrpc.client from io import StringIO s = StringIO() handler = cgitb.Hook(format="text", file=s) handler(exc_type, exc, tb) s = s.getvalue() print(s) p = xmlrpc.client.ServerProxy( "http://paste.yt-project.org/xmlrpc/", allow_none=True ) ret = p.pastes.newPaste("text", s, None, "", "", True) print() print(f"Traceback pasted to http://paste.yt-project.org/show/{ret}") print() _ss = "fURbBUUBE0cLXgETJnZgJRMXVhVGUQpQAUBuehQMUhJWRFFRAV1ERAtBXw1dAxMLXT4zXBFfABNN\nC0ZEXw1YUURHCxMXVlFERwxWCQw=\n" def _rdbeta(key): enc_s = base64.decodestring(_ss) dec_s = "".join(chr(ord(a) ^ ord(b)) for a, b in zip(enc_s, itertools.cycle(key))) print(dec_s) # # Some exceptions # class NoCUDAException(Exception): pass class YTEmptyClass: pass def update_git(path): try: import git except ImportError: print("Updating and precise version information requires ") print("gitpython to be installed.") print("Try: python -m pip install gitpython") return -1 with open(os.path.join(path, "yt_updater.log"), "a") as f: repo = git.Repo(path) if repo.is_dirty(untracked_files=True): print("Changes have been made to the yt source code so I won't ") print("update the code. You will have to do this yourself.") print("Here's a set of sample commands:") print("") print(f" $ cd {path}") print(" $ git stash") print(" $ git checkout main") print(" $ git pull") print(" $ git stash pop") print(f" $ {sys.executable} setup.py develop") print("") return 1 if repo.active_branch.name != "main": print("yt repository is not tracking the main branch so I won't ") print("update the code. You will have to do this yourself.") print("Here's a set of sample commands:") print("") print(f" $ cd {path}") print(" $ git checkout main") print(" $ git pull") print(f" $ {sys.executable} setup.py develop") print("") return 1 print("Updating the repository") f.write("Updating the repository\n\n") old_version = repo.git.rev_parse("HEAD", short=12) try: remote = repo.remotes.yt_upstream except AttributeError: remote = repo.create_remote( "yt_upstream", url="https://github.com/yt-project/yt" ) remote.fetch() main = repo.heads.main main.set_tracking_branch(remote.refs.main) main.checkout() remote.pull() new_version = repo.git.rev_parse("HEAD", short=12) f.write(f"Updated from {old_version} to {new_version}\n\n") rebuild_modules(path, f) print("Updated successfully") def rebuild_modules(path, f): f.write("Rebuilding modules\n\n") p = subprocess.Popen( [sys.executable, "setup.py", "build_clib", "build_ext", "-i"], cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout, stderr = p.communicate() f.write(stdout.decode("utf-8")) f.write("\n\n") if p.returncode: print(f"BROKEN: See {os.path.join(path, 'yt_updater.log')}") sys.exit(1) f.write("Successful!\n") def get_git_version(path): try: import git except ImportError: print("Updating and precise version information requires ") print("gitpython to be installed.") print("Try: python -m pip install gitpython") return None try: repo = git.Repo(path) return repo.git.rev_parse("HEAD", short=12) except git.InvalidGitRepositoryError: # path is not a git repository return None def get_yt_version(): import importlib.resources as importlib_resources version = get_git_version(os.path.dirname(importlib_resources.files("yt"))) if version is None: return version else: v_str = version[:12].strip() if hasattr(v_str, "decode"): v_str = v_str.decode("utf-8") return v_str def get_version_stack(): import matplotlib from yt._version import __version__ as yt_version version_info = {} version_info["yt"] = yt_version version_info["numpy"] = np.version.version version_info["matplotlib"] = matplotlib.__version__ return version_info def get_script_contents(): top_frame = inspect.stack()[-1] finfo = inspect.getframeinfo(top_frame[0]) if finfo[2] != "": return None if not os.path.exists(finfo[0]): return None try: contents = open(finfo[0]).read() except Exception: contents = None return contents def download_file(url, filename): try: return fancy_download_file(url, filename, requests) except ImportError: # fancy_download_file requires requests return simple_download_file(url, filename) def fancy_download_file(url, filename, requests=None): response = requests.get(url, stream=True) total_length = response.headers.get("content-length") with open(filename, "wb") as fh: if total_length is None: fh.write(response.content) else: blocksize = 4 * 1024**2 iterations = int(float(total_length) / float(blocksize)) pbar = get_pbar( "Downloading {} to {} ".format(*os.path.split(filename)[::-1]), iterations, ) iteration = 0 for chunk in response.iter_content(chunk_size=blocksize): fh.write(chunk) iteration += 1 pbar.update(iteration) pbar.finish() return filename def simple_download_file(url, filename): import urllib.error import urllib.request try: fn, h = urllib.request.urlretrieve(url, filename) except urllib.error.HTTPError as err: raise RuntimeError( f"Attempt to download file from {url} failed with error {err.code}: {err.msg}." ) from None return fn # This code snippet is modified from Georg Brandl def bb_apicall(endpoint, data, use_pass=True): import getpass import urllib.parse import urllib.request uri = f"https://api.bitbucket.org/1.0/{endpoint}/" # since bitbucket doesn't return the required WWW-Authenticate header when # making a request without Authorization, we cannot use the standard urllib2 # auth handlers; we have to add the requisite header from the start if data is not None: data = urllib.parse.urlencode(data) req = urllib.request.Request(uri, data) if use_pass: username = input("Bitbucket Username? ") password = getpass.getpass() upw = f"{username}:{password}" req.add_header("Authorization", f"Basic {base64.b64encode(upw).strip()}") return urllib.request.urlopen(req).read() def fix_length(length, ds): registry = ds.unit_registry if isinstance(length, YTArray): if registry is not None: length.units.registry = registry return length.in_units("code_length") if isinstance(length, numeric_type): return YTArray(length, "code_length", registry=registry) length_valid_tuple = isinstance(length, (list, tuple)) and len(length) == 2 unit_is_string = isinstance(length[1], str) length_is_number = isinstance(length[0], numeric_type) and not isinstance( length[0], YTArray ) if length_valid_tuple and unit_is_string and length_is_number: return YTArray(*length, registry=registry) else: raise RuntimeError(f"Length {str(length)} is invalid") @contextlib.contextmanager def parallel_profile(prefix): r"""A context manager for profiling parallel code execution using cProfile This is a simple context manager that automatically profiles the execution of a snippet of code. Parameters ---------- prefix : string A string name to prefix outputs with. Examples -------- >>> from yt import PhasePlot >>> from yt.testing import fake_random_ds >>> fields = ("density", "temperature", "cell_mass") >>> units = ("g/cm**3", "K", "g") >>> ds = fake_random_ds(16, fields=fields, units=units) >>> with parallel_profile("my_profile"): ... plot = PhasePlot(ds.all_data(), *fields) """ import cProfile fn = "%s_%04i_%04i.cprof" % ( prefix, ytcfg.get("yt", "internals", "topcomm_parallel_size"), ytcfg.get("yt", "internals", "topcomm_parallel_rank"), ) p = cProfile.Profile() p.enable() yield fn p.disable() p.dump_stats(fn) def get_num_threads(): from .config import ytcfg nt = ytcfg.get("yt", "num_threads") if nt < 0: return os.environ.get("OMP_NUM_THREADS", 0) return nt def fix_axis(axis, ds): return ds.coordinates.axis_id.get(axis, axis) def get_output_filename(name, keyword, suffix): r"""Return an appropriate filename for output. With a name provided by the user, this will decide how to appropriately name the output file by the following rules: 1. if name is None, the filename will be the keyword plus the suffix. 2. if name ends with "/" (resp "\" on Windows), assume name is a directory and the file will be named name/(keyword+suffix). If the directory does not exist, first try to create it and raise an exception if an error occurs. 3. if name does not end in the suffix, add the suffix. Parameters ---------- name : str A filename given by the user. keyword : str A default filename prefix if name is None. suffix : str Suffix that must appear at end of the filename. This will be added if not present. Examples -------- >>> get_output_filename(None, "Projection_x", ".png") 'Projection_x.png' >>> get_output_filename("my_file", "Projection_x", ".png") 'my_file.png' >>> get_output_filename("my_dir/", "Projection_x", ".png") 'my_dir/Projection_x.png' """ if name is None: name = keyword name = os.path.expanduser(name) if name.endswith(os.sep) and not os.path.isdir(name): ensure_dir(name) if os.path.isdir(name): name = os.path.join(name, keyword) if not name.endswith(suffix): name += suffix return name def ensure_dir_exists(path): r"""Create all directories in path recursively in a parallel safe manner""" my_dir = os.path.dirname(path) # If path is a file in the current directory, like "test.txt", then my_dir # would be an empty string, resulting in FileNotFoundError when passed to # ensure_dir. Let's avoid that. if my_dir: ensure_dir(my_dir) def ensure_dir(path): r"""Parallel safe directory maker.""" if os.path.exists(path): return path try: os.makedirs(path) except OSError as e: if e.errno == errno.EEXIST: pass else: raise return path def validate_width_tuple(width): if not is_sequence(width) or len(width) != 2: raise YTInvalidWidthError(f"width ({width}) is not a two element tuple") is_numeric = isinstance(width[0], numeric_type) length_has_units = isinstance(width[0], YTArray) unit_is_string = isinstance(width[1], str) if not is_numeric or length_has_units and unit_is_string: msg = f"width ({str(width)}) is invalid. " msg += "Valid widths look like this: (12, 'au')" raise YTInvalidWidthError(msg) _first_cap_re = re.compile("(.)([A-Z][a-z]+)") _all_cap_re = re.compile("([a-z0-9])([A-Z])") @lru_cache(maxsize=128, typed=False) def camelcase_to_underscore(name): s1 = _first_cap_re.sub(r"\1_\2", name) return _all_cap_re.sub(r"\1_\2", s1).lower() def set_intersection(some_list): if len(some_list) == 0: return set() # This accepts a list of iterables, which we get the intersection of. s = set(some_list[0]) for l in some_list[1:]: s.intersection_update(l) return s @contextlib.contextmanager def memory_checker(interval=15, dest=None): r"""This is a context manager that monitors memory usage. Parameters ---------- interval : int The number of seconds between printing the current memory usage in gigabytes of the current Python interpreter. Examples -------- >>> with memory_checker(10): ... arr = np.zeros(1024 * 1024 * 1024, dtype="float64") ... time.sleep(15) ... del arr MEMORY: -1.000e+00 gb """ import threading if dest is None: dest = sys.stdout class MemoryChecker(threading.Thread): def __init__(self, event, interval): self.event = event self.interval = interval threading.Thread.__init__(self) def run(self): while not self.event.wait(self.interval): print(f"MEMORY: {get_memory_usage() / 1024.0:0.3e} gb", file=dest) e = threading.Event() mem_check = MemoryChecker(e, interval) mem_check.start() try: yield finally: e.set() def enable_plugins(plugin_filename=None): """Forces a plugin file to be parsed. A plugin file is a means of creating custom fields, quantities, data objects, colormaps, and other code classes and objects to be used in yt scripts without modifying the yt source directly. If ``plugin_filename`` is omitted, this function will look for a plugin file at ``$HOME/.config/yt/my_plugins.py``, which is the preferred behaviour for a system-level configuration. Warning: a script using this function will only be reproducible if your plugin file is shared with it. """ import yt from yt.config import config_dir, ytcfg from yt.fields.my_plugin_fields import my_plugins_fields if plugin_filename is not None: _fn = plugin_filename if not os.path.isfile(_fn): raise FileNotFoundError(_fn) else: # Determine global plugin location. By decreasing priority order: # - absolute path # - CONFIG_DIR # - obsolete config dir. my_plugin_name = ytcfg.get("yt", "plugin_filename") for base_prefix in ("", config_dir()): if os.path.isfile(os.path.join(base_prefix, my_plugin_name)): _fn = os.path.join(base_prefix, my_plugin_name) break else: raise FileNotFoundError("Could not find a global system plugin file.") mylog.info("Loading plugins from %s", _fn) ytdict = yt.__dict__ execdict = ytdict.copy() execdict["add_field"] = my_plugins_fields.add_field with open(_fn) as f: code = compile(f.read(), _fn, "exec") exec(code, execdict, execdict) ytnamespace = list(ytdict.keys()) for k in execdict.keys(): if k not in ytnamespace: if callable(execdict[k]): setattr(yt, k, execdict[k]) def subchunk_count(n_total, chunk_size): handled = 0 while handled < n_total: tr = min(n_total - handled, chunk_size) yield tr handled += tr def fix_unitary(u): if u == "1": return "unitary" else: return u def get_hash(infile, algorithm="md5", BLOCKSIZE=65536): """Generate file hash without reading in the entire file at once. Original code licensed under MIT. Source: https://www.pythoncentral.io/hashing-files-with-python/ Parameters ---------- infile : str File of interest (including the path). algorithm : str (optional) Hash algorithm of choice. Defaults to 'md5'. BLOCKSIZE : int (optional) How much data in bytes to read in at once. Returns ------- hash : str The hash of the file. Examples -------- >>> from tempfile import NamedTemporaryFile >>> with NamedTemporaryFile() as file: ... get_hash(file.name) 'd41d8cd98f00b204e9800998ecf8427e' """ import hashlib try: hasher = getattr(hashlib, algorithm)() except AttributeError as e: raise NotImplementedError( f"'{algorithm}' not available! Available algorithms: {hashlib.algorithms}" ) from e filesize = os.path.getsize(infile) iterations = int(float(filesize) / float(BLOCKSIZE)) pbar = get_pbar(f"Generating {algorithm} hash", iterations) iter = 0 with open(infile, "rb") as f: buf = f.read(BLOCKSIZE) while len(buf) > 0: hasher.update(buf) buf = f.read(BLOCKSIZE) iter += 1 pbar.update(iter) pbar.finish() return hasher.hexdigest() def get_brewer_cmap(cmap): """Returns a colorbrewer colormap from palettable""" try: import palettable except ImportError as exc: raise RuntimeError( "Please install palettable to use colorbrewer colormaps" ) from exc bmap = palettable.colorbrewer.get_map(*cmap) return bmap.get_mpl_colormap(N=cmap[2]) def matplotlib_style_context(style="yt.default", after_reset=False): """Returns a context manager for controlling matplotlib style. Arguments are passed to matplotlib.style.context() if specified. Defaults to setting yt's "yt.default" style, after resetting to the default config parameters. """ # FUTURE: this function should be deprecated in favour of matplotlib.style.context # after support for matplotlib 3.6 and older versions is dropped. import importlib.resources as importlib_resources import matplotlib as mpl import matplotlib.style if style == "yt.default" and mpl.__version_info__ < (3, 7): style = importlib_resources.files("yt") / "default.mplstyle" return matplotlib.style.context(style, after_reset=after_reset) interactivity = False """Sets the condition that interactive backends can be used.""" def toggle_interactivity(): global interactivity interactivity = not interactivity if interactivity: if IS_IPYTHON: import IPython shell = IPython.get_ipython() shell.magic("matplotlib") else: import matplotlib matplotlib.interactive(True) def get_interactivity(): return interactivity def setdefaultattr(obj, name, value): """Set attribute with *name* on *obj* with *value* if it doesn't exist yet Analogous to dict.setdefault """ if not hasattr(obj, name): setattr(obj, name, value) return getattr(obj, name) def parse_h5_attr(f, attr): """A Python3-safe function for getting hdf5 attributes. If an attribute is supposed to be a string, this will return it as such. """ val = f.attrs.get(attr, None) if isinstance(val, bytes): return val.decode("utf8") else: return val def obj_length(v): if is_sequence(v): return len(v) else: # If something isn't iterable, we return 0 # to signify zero length (aka a scalar). return 0 def array_like_field(data, x, field): field = data._determine_fields(field)[0] finfo = data.ds._get_field_info(field) if finfo.sampling_type == "particle": units = finfo.output_units else: units = finfo.units if isinstance(x, YTArray): arr = copy.deepcopy(x) arr.convert_to_units(units) return arr if isinstance(x, np.ndarray): return data.ds.arr(x, units) else: return data.ds.quan(x, units) def _full_type_name(obj: object = None, /, *, cls: type | None = None) -> str: if cls is not None and obj is not None: raise TypeError("_full_type_name takes an object or a class, but not both") if cls is None: cls = obj.__class__ prefix = f"{cls.__module__}." if cls.__module__ != "builtins" else "" return f"{prefix}{cls.__name__}" def validate_3d_array(obj): if not is_sequence(obj) or len(obj) != 3: raise TypeError( f"Expected an array of size (3,), " f"received {_full_type_name(obj)!r} of length {len(obj)}" ) def validate_float(obj): """Validates if the passed argument is a float value. Raises an exception if `obj` is not a single float value or a YTQuantity of size 1. Parameters ---------- obj : Any Any argument which needs to be checked for a single float value. Raises ------ TypeError Raised if `obj` is not a single float value or YTQunatity Examples -------- >>> validate_float(1) >>> validate_float(1.50) >>> validate_float(YTQuantity(1, "cm")) >>> validate_float((1, "cm")) >>> validate_float([1, 1, 1]) Traceback (most recent call last): ... TypeError: Expected a numeric value (or size-1 array), received 'list' of length 3 >>> validate_float([YTQuantity(1, "cm"), YTQuantity(2, "cm")]) Traceback (most recent call last): ... TypeError: Expected a numeric value (or size-1 array), received 'list' of length 2 """ if isinstance(obj, tuple): if ( len(obj) != 2 or not isinstance(obj[0], numeric_type) or not isinstance(obj[1], str) ): raise TypeError( "Expected a numeric value (or tuple of format " f"(float, String)), received an inconsistent tuple {str(obj)!r}." ) else: return if is_sequence(obj) and (len(obj) != 1 or not isinstance(obj[0], numeric_type)): raise TypeError( "Expected a numeric value (or size-1 array), " f"received {_full_type_name(obj)!r} of length {len(obj)}" ) def validate_sequence(obj): if obj is not None and not is_sequence(obj): raise TypeError( "Expected an iterable object, " f"received {_full_type_name(obj)!r}" ) def validate_field_key(key): if ( isinstance(key, tuple) and len(key) == 2 and all(isinstance(_, str) for _ in key) ): return raise TypeError( "Expected a 2-tuple of strings formatted as\n" "(field or particle type, field name)\n" f"Received invalid field key: {key}, with type {type(key)}" ) def is_valid_field_key(key): try: validate_field_key(key) except TypeError: return False else: return True def validate_object(obj, data_type): if obj is not None and not isinstance(obj, data_type): raise TypeError( f"Expected an object of {_full_type_name(cls=data_type)!r} type, " f"received {_full_type_name(obj)!r}" ) def validate_axis(ds, axis): if ds is not None: valid_axis = sorted( ds.coordinates.axis_name.keys(), key=lambda k: str(k).swapcase() ) else: valid_axis = [0, 1, 2, "x", "y", "z", "X", "Y", "Z"] if axis not in valid_axis: raise TypeError(f"Expected axis to be any of {valid_axis}, received {axis!r}") def validate_center(center): if isinstance(center, str): c = center.lower() if ( c not in ["c", "center", "m", "max", "min"] and not c.startswith("max_") and not c.startswith("min_") ): raise TypeError( "Expected 'center' to be in ['c', 'center', " "'m', 'max', 'min'] or the prefix to be " f"'max_'/'min_', received {center!r}." ) elif not isinstance(center, (numeric_type, YTQuantity)) and not is_sequence(center): raise TypeError( "Expected 'center' to be a numeric object of type " "list/tuple/np.ndarray/YTArray/YTQuantity, " f"received {_full_type_name(center)}." ) def parse_center_array(center, ds, axis: int | None = None): known_shortnames = {"m": "max", "c": "center", "l": "left", "r": "right"} valid_single_str_values = ("center", "left", "right") valid_field_loc_str_values = ("min", "max") valid_str_values = valid_single_str_values + valid_field_loc_str_values default_error_message = ( "Expected any of the following\n" "- 'c', 'center', 'l', 'left', 'r', 'right', 'm', 'max', or 'min'\n" "- a 2 element tuple with 'min' or 'max' as the first element, followed by a field identifier\n" "- a 3 element array-like: for a unyt_array, expects length dimensions, otherwise code_lenght is assumed" ) # store an unmodified copy of user input to be inserted in error messages center_input = deepcopy(center) if isinstance(center, str): centerl = center.lower() if centerl in known_shortnames: centerl = known_shortnames[centerl] match = re.match(r"^(?P(min|max))(_(?P[\w_]+))?", centerl) if match is not None: if match["field"] is not None: for ftype, fname in ds.derived_field_list: # noqa: B007 if fname == match["field"]: break else: raise YTFieldNotFound(match["field"], ds) else: ftype, fname = ("gas", "density") center = (match["extremum"], (ftype, fname)) elif centerl in ("center", "left", "right"): # domain_left_edge and domain_right_edge might not be # initialized until we create the index, so create it ds.index center = ds.domain_center.copy() if centerl in ("left", "right") and axis is None: raise ValueError(f"center={center!r} is not valid with axis=None") if centerl == "left": center = ds.domain_center.copy() center[axis] = ds.domain_left_edge[axis] elif centerl == "right": # note that the right edge of a grid is excluded by slice selector # which is why we offset the region center by the smallest distance possible center = ds.domain_center.copy() center[axis] = ( ds.domain_right_edge[axis] - center.uq * np.finfo(center.dtype).eps ) elif centerl not in valid_str_values: raise ValueError( f"Received unknown center single string value {center!r}. " + default_error_message ) if is_sequence(center): if ( len(center) == 2 and isinstance(center[0], str) and (is_valid_field_key(center[1]) or isinstance(center[1], str)) ): center0l = center[0].lower() if center0l not in valid_str_values: raise ValueError( f"Received unknown string value {center[0]!r}. " f"Expected one of {valid_field_loc_str_values} (case insensitive)" ) field_key = center[1] if center0l == "min": v, center = ds.find_min(field_key) else: assert center0l == "max" v, center = ds.find_max(field_key) center = ds.arr(center, "code_length") elif len(center) == 2 and is_sequence(center[0]) and isinstance(center[1], str): center = ds.arr(center[0], center[1]) elif len(center) == 3 and all(isinstance(_, YTQuantity) for _ in center): center = ds.arr([c.copy() for c in center], dtype="float64") elif len(center) == 3: center = ds.arr(center, "code_length") if isinstance(center, np.ndarray) and center.ndim > 1: mylog.debug("Removing singleton dimensions from 'center'.") center = np.squeeze(center) if not isinstance(center, YTArray): raise TypeError( f"Received {center_input!r}, but failed to transform to a unyt_array (obtained {center!r}).\n" + default_error_message + "\n" "If you supplied an expected type, consider filing a bug report" ) if center.shape != (3,): raise TypeError( f"Received {center_input!r} and obtained {center!r} after sanitizing.\n" + default_error_message + "\n" "If you supplied an expected type, consider filing a bug report" ) # make sure the return value shares all # unit symbols with ds.unit_registry # we rely on unyt to invalidate unit dimensionality here center = ds.arr(center).in_units("code_length") if not ds._is_within_domain(center): mylog.warning( "Requested center at %s is outside of data domain with " "left edge = %s, " "right edge = %s, " "periodicity = %s", center, ds.domain_left_edge, ds.domain_right_edge, ds.periodicity, ) return center.astype("float64") def sglob(pattern): """ Return the results of a glob through the sorted() function. """ return sorted(glob.glob(pattern)) def dictWithFactory(factory: Callable[[Any], Any]) -> type: """ Create a dictionary class with a default factory function. Contrary to `collections.defaultdict`, the factory takes the missing key as input parameter. Parameters ---------- factory : callable(key) -> value The factory to call when hitting a missing key Returns ------- DictWithFactory class A class to create new dictionaries handling missing keys. """ issue_deprecation_warning( "yt.funcs.dictWithFactory will be removed in a future version of yt, please do not rely on it. " "If you need it, copy paste this function from yt's source code", stacklevel=3, since="4.1", ) class DictWithFactory(UserDict): def __init__(self, *args, **kwargs): self.factory = factory super().__init__(*args, **kwargs) def __missing__(self, key): val = self.factory(key) self[key] = val return val return DictWithFactory def levenshtein_distance(seq1, seq2, max_dist=None): """ Compute the levenshtein distance between seq1 and seq2. From https://stackabuse.com/levenshtein-distance-and-text-similarity-in-python/ Parameters ---------- seq1 : str seq2 : str The strings to compute the distance between max_dist : integer If not None, maximum distance returned (see notes). Returns ------- The Levenshtein distance as an integer. Notes ----- This computes the Levenshtein distance, i.e. the number of edits to change seq1 into seq2. If a maximum distance is passed, the algorithm will stop as soon as the number of edits goes above the value. This allows for an earlier break and speeds calculations up. """ size_x = len(seq1) + 1 size_y = len(seq2) + 1 if max_dist is None: max_dist = max(size_x, size_y) if abs(size_x - size_y) > max_dist: return max_dist + 1 matrix = np.zeros((size_x, size_y), dtype=int) for x in range(size_x): matrix[x, 0] = x for y in range(size_y): matrix[0, y] = y for x in range(1, size_x): for y in range(1, size_y): if seq1[x - 1] == seq2[y - 1]: matrix[x, y] = min( matrix[x - 1, y] + 1, matrix[x - 1, y - 1], matrix[x, y - 1] + 1 ) else: matrix[x, y] = min( matrix[x - 1, y] + 1, matrix[x - 1, y - 1] + 1, matrix[x, y - 1] + 1 ) # Early break: the minimum distance is already larger than # maximum allow value, can return safely. if matrix[x].min() > max_dist: return max_dist + 1 return matrix[size_x - 1, size_y - 1] def validate_moment(moment, weight_field): if moment == 2 and weight_field is None: raise ValueError( "Cannot compute the second moment of a projection if weight_field=None!" ) if moment not in [1, 2]: raise ValueError( "Weighted projections can only be made of averages " "(moment = 1) or standard deviations (moment = 2)!" ) def setdefault_mpl_metadata(save_kwargs: dict[str, Any], name: str) -> None: """ Set a default Software metadata entry for use with Matplotlib outputs. """ _, ext = os.path.splitext(name.lower()) if ext in (".eps", ".ps", ".svg", ".pdf"): key = "Creator" elif ext == ".png": key = "Software" else: return default_software = ( "Matplotlib version{matplotlib}, https://matplotlib.org|NumPy-{numpy}|yt-{yt}" ).format(**get_version_stack()) if "metadata" in save_kwargs: save_kwargs["metadata"].setdefault(key, default_software) else: save_kwargs["metadata"] = {key: default_software} yt-project-yt-f043ac8/yt/geometry/000077500000000000000000000000001510711153200171415ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/__init__.py000066400000000000000000000000001510711153200212400ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/_selection_routines/000077500000000000000000000000001510711153200232155ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/_selection_routines/always_selector.pxi000066400000000000000000000030361510711153200271410ustar00rootroot00000000000000cdef class AlwaysSelector(SelectorObject): def __init__(self, dobj): self.overlap_cells = 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): cdef int ng = left_edges.shape[0] cdef np.ndarray[np.uint8_t, ndim=1] gridi = np.ones(ng, dtype='uint8') return gridi.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return 1 cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: return 1 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: return 1 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: return 1 cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: return 1 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: return 1 def _hash_vals(self): return ("always", 1,) always_selector = AlwaysSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/boolean_selectors.pxi000066400000000000000000000410251510711153200274430ustar00rootroot00000000000000 cdef class BooleanSelector(SelectorObject): def __init__(self, dobj): # Note that this has a different API than the other selector objects, # so will not work as a traditional data selector. if not hasattr(dobj.dobj1, "selector"): self.sel1 = dobj.dobj1 else: self.sel1 = dobj.dobj1.selector if not hasattr(dobj.dobj2, "selector"): self.sel2 = dobj.dobj2 else: self.sel2 = dobj.dobj2.selector cdef class BooleanANDSelector(BooleanSelector): cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.sel1.select_bbox(left_edge, right_edge) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_bbox(left_edge, right_edge) if rv2 == 0: return 0 return 1 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.sel1.select_bbox_edge(left_edge, right_edge) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_bbox_edge(left_edge, right_edge) if rv2 == 0: return 0 return max(rv1, rv2) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: cdef int rv1 = self.sel1.select_grid(left_edge, right_edge, level, o) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_grid(left_edge, right_edge, level, o) if rv2 == 0: return 0 return 1 cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef int rv1 = self.sel1.select_cell(pos, dds) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_cell(pos, dds) if rv2 == 0: return 0 return 1 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int rv1 = self.sel1.select_point(pos) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_point(pos) if rv2 == 0: return 0 return 1 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int rv1 = self.sel1.select_sphere(pos, radius) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_sphere(pos, radius) if rv2 == 0: return 0 return 1 def _hash_vals(self): return (self.sel1._hash_vals() + ("and",) + self.sel2._hash_vals()) cdef class BooleanORSelector(BooleanSelector): cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.sel1.select_bbox(left_edge, right_edge) if rv1 == 1: return 1 cdef int rv2 = self.sel2.select_bbox(left_edge, right_edge) if rv2 == 1: return 1 return 0 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.sel1.select_bbox_edge(left_edge, right_edge) if rv1 == 1: return 1 cdef int rv2 = self.sel2.select_bbox_edge(left_edge, right_edge) if rv2 == 1: return 1 return max(rv1, rv2) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: cdef int rv1 = self.sel1.select_grid(left_edge, right_edge, level, o) if rv1 == 1: return 1 cdef int rv2 = self.sel2.select_grid(left_edge, right_edge, level, o) if rv2 == 1: return 1 if (rv1 == 2) or (rv2 == 2): return 2 return 0 cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef int rv1 = self.sel1.select_cell(pos, dds) if rv1 == 1: return 1 cdef int rv2 = self.sel2.select_cell(pos, dds) if rv2 == 1: return 1 return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int rv1 = self.sel1.select_point(pos) if rv1 == 1: return 1 cdef int rv2 = self.sel2.select_point(pos) if rv2 == 1: return 1 return 0 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int rv1 = self.sel1.select_sphere(pos, radius) if rv1 == 1: return 1 cdef int rv2 = self.sel2.select_sphere(pos, radius) if rv2 == 1: return 1 return 0 def _hash_vals(self): return (self.sel1._hash_vals() + ("or",) + self.sel2._hash_vals()) cdef class BooleanNOTSelector(BooleanSelector): cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # We always return True here, because we don't have a "fully included" # check anywhere else. return 1 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.sel1.select_bbox_edge(left_edge, right_edge) if rv1 == 0: return 1 elif rv1 == 1: return 0 return 2 cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: return 1 cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef int rv1 = self.sel1.select_cell(pos, dds) if rv1 == 0: return 1 return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int rv1 = self.sel1.select_point(pos) if rv1 == 0: return 1 return 0 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int rv1 = self.sel1.select_sphere(pos, radius) if rv1 == 0: return 1 return 0 def _hash_vals(self): return (self.sel1._hash_vals() + ("not",)) cdef class BooleanXORSelector(BooleanSelector): cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # We always return True here, because we don't have a "fully included" # check anywhere else. return 1 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # Return 2 in cases where one or both selectors partially overlap since # part of the bounding box could satisfy the condition unless the # selectors are identical. cdef int rv1 = self.sel1.select_bbox_edge(left_edge, right_edge) cdef int rv2 = self.sel2.select_bbox_edge(left_edge, right_edge) if rv1 == rv2: if rv1 == 2: # If not identical, part of the bbox will be touched by one # selector and not the other. # if self.sel1 == self.sel2: return 0 # requires gil return 2 return 0 if rv1 == 0: return rv2 if rv2 == 0: return rv1 return 2 # part of bbox only touched by selector fully covering bbox cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: return 1 cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef int rv1 = self.sel1.select_cell(pos, dds) cdef int rv2 = self.sel2.select_cell(pos, dds) if rv1 == rv2: return 0 return 1 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int rv1 = self.sel1.select_point(pos) cdef int rv2 = self.sel2.select_point(pos) if rv1 == rv2: return 0 return 1 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int rv1 = self.sel1.select_sphere(pos, radius) cdef int rv2 = self.sel2.select_sphere(pos, radius) if rv1 == rv2: return 0 return 1 def _hash_vals(self): return (self.sel1._hash_vals() + ("xor",) + self.sel2._hash_vals()) cdef class BooleanNEGSelector(BooleanSelector): cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # We always return True here, because we don't have a "fully included" # check anywhere else. return self.sel1.select_bbox(left_edge, right_edge) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.sel1.select_bbox_edge(left_edge, right_edge) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_bbox_edge(left_edge, right_edge) if rv2 == 1: return 0 elif rv2 == 0: return rv1 # If sel2 is partial, then sel1 - sel2 will be partial as long # as sel1 != sel2 # if self.sel1 == self.sel2: return 0 # requires gil return 2 cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: return self.sel1.select_grid(left_edge, right_edge, level, o) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef int rv1 = self.sel1.select_cell(pos, dds) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_cell(pos, dds) if rv2 == 1: return 0 return 1 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int rv1 = self.sel1.select_point(pos) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_point(pos) if rv2 == 1: return 0 return 1 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int rv1 = self.sel1.select_sphere(pos, radius) if rv1 == 0: return 0 cdef int rv2 = self.sel2.select_sphere(pos, radius) if rv2 == 1: return 0 return 1 def _hash_vals(self): return (self.sel1._hash_vals() + ("neg",) + self.sel2._hash_vals()) cdef class ChainedBooleanSelector(SelectorObject): cdef int n_obj cdef np.ndarray selectors def __init__(self, dobj): # These are data objects, not selectors self.n_obj = len(dobj.data_objects) self.selectors = np.empty(self.n_obj, dtype="object") for i in range(self.n_obj): self.selectors[i] = dobj.data_objects[i].selector cdef class ChainedBooleanANDSelector(ChainedBooleanSelector): @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_bbox( left_edge, right_edge) == 0: return 0 return 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int selected = 1 cdef int ret with gil: for i in range(self.n_obj): ret = (self.selectors[i]).select_bbox_edge( left_edge, right_edge) if ret == 0: return 0 elif ret == 2: selected = 2 return selected @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_grid( left_edge, right_edge, level, o) == 0: return 0 return 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_cell( pos, dds) == 0: return 0 return 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_point(pos) == 0: return 0 return 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_sphere( pos, radius) == 0: return 0 return 1 def _hash_vals(self): v = ("chained_and",) for s in self.selectors: v += s._hash_vals() return v intersection_selector = ChainedBooleanANDSelector cdef class ChainedBooleanORSelector(ChainedBooleanSelector): @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_bbox( left_edge, right_edge) == 1: return 1 return 0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int selected = 0 cdef int ret with gil: for i in range(self.n_obj): ret = (self.selectors[i]).select_bbox_edge( left_edge, right_edge) if ret == 1: return 1 elif ret == 2: selected = 2 return selected @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_grid( left_edge, right_edge, level, o) == 1: return 1 return 0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_cell( pos, dds) == 1: return 1 return 0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_point(pos) == 1: return 1 return 0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: with gil: for i in range(self.n_obj): if (self.selectors[i]).select_sphere( pos, radius) == 1: return 1 return 0 def _hash_vals(self): v = ("chained_or",) for s in self.selectors: v += s._hash_vals() return v union_selector = ChainedBooleanORSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/compose_selector.pxi000066400000000000000000000053141510711153200273070ustar00rootroot00000000000000cdef class ComposeSelector(SelectorObject): cdef SelectorObject selector1 cdef SelectorObject selector2 def __init__(self, dobj, selector1, selector2): self.selector1 = selector1 self.selector2 = selector2 self.min_level = max(selector1.min_level, selector2.min_level) self.max_level = min(selector1.max_level, selector2.max_level) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): return np.logical_or( self.selector1.select_grids(left_edges, right_edges, levels), self.selector2.select_grids(left_edges, right_edges, levels)) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: if self.selector1.select_cell(pos, dds) and \ self.selector2.select_cell(pos, dds): return 1 else: return 0 cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: if self.selector1.select_grid(left_edge, right_edge, level, o) or \ self.selector2.select_grid(left_edge, right_edge, level, o): return 1 else: return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: if self.selector1.select_point(pos) and \ self.selector2.select_point(pos): return 1 else: return 0 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: if self.selector1.select_sphere(pos, radius) and \ self.selector2.select_sphere(pos, radius): return 1 else: return 0 cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: if self.selector1.select_bbox(left_edge, right_edge) and \ self.selector2.select_bbox(left_edge, right_edge): return 1 else: return 0 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int rv1 = self.selector1.select_bbox_edge(left_edge, right_edge) if rv1 == 0: return 0 cdef int rv2 = self.selector2.select_bbox_edge(left_edge, right_edge) if rv2 == 0: return 0 return max(rv1, rv2) def _hash_vals(self): return (hash(self.selector1), hash(self.selector2)) compose_selector = ComposeSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/cut_region_selector.pxi000066400000000000000000000026141510711153200300000ustar00rootroot00000000000000cdef class CutRegionSelector(SelectorObject): cdef set _positions cdef tuple _conditionals def __init__(self, dobj): axis_name = dobj.ds.coordinates.axis_name positions = np.array([dobj['index', axis_name[0]], dobj['index', axis_name[1]], dobj['index', axis_name[2]]]).T self._conditionals = tuple(dobj.conditionals) self._positions = set(tuple(position) for position in positions) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: return 1 cdef int select_bbox_dge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: return 1 cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: with gil: if (pos[0], pos[1], pos[2]) in self._positions: return 1 else: return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: return 1 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: return 1 def _hash_vals(self): t = () for i, c in enumerate(self._conditionals): t += ("conditional[%s]" % i, c) return ("conditionals", t) cut_region_selector = CutRegionSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/cutting_plane_selector.pxi000066400000000000000000000102251510711153200304730ustar00rootroot00000000000000cdef class CuttingPlaneSelector(SelectorObject): cdef public np.float64_t norm_vec[3] cdef public np.float64_t d def __init__(self, dobj): cdef int i for i in range(3): self.norm_vec[i] = dobj._norm_vec[i] self.d = _ensure_code(dobj._d) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef int i for i in range(3): left_edge[i] = pos[i] - 0.5*dds[i] right_edge[i] = pos[i] + 0.5*dds[i] return self.select_bbox(left_edge, right_edge) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: # two 0-volume constructs don't intersect return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int i cdef np.float64_t height = self.d for i in range(3) : height += pos[i] * self.norm_vec[i] if height*height <= radius*radius : return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int i, j, k, n cdef np.float64_t *arr[2] cdef np.float64_t pos[3] cdef np.float64_t gd arr[0] = left_edge arr[1] = right_edge all_under = 1 all_over = 1 # Check each corner for i in range(2): pos[0] = arr[i][0] for j in range(2): pos[1] = arr[j][1] for k in range(2): pos[2] = arr[k][2] gd = self.d for n in range(3): gd += pos[n] * self.norm_vec[n] # this allows corners and faces on the low-end to # collide, while not selecting cells on the high-side if i == 0 and j == 0 and k == 0 : if gd <= 0: all_over = 0 if gd >= 0: all_under = 0 else : if gd < 0: all_over = 0 if gd > 0: all_under = 0 if all_over == 1 or all_under == 1: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int i, j, k, n cdef np.float64_t *arr[2] cdef np.float64_t pos[3] cdef np.float64_t gd arr[0] = left_edge arr[1] = right_edge all_under = 1 all_over = 1 # Check each corner for i in range(2): pos[0] = arr[i][0] for j in range(2): pos[1] = arr[j][1] for k in range(2): pos[2] = arr[k][2] gd = self.d for n in range(3): gd += pos[n] * self.norm_vec[n] # this allows corners and faces on the low-end to # collide, while not selecting cells on the high-side if i == 0 and j == 0 and k == 0 : if gd <= 0: all_over = 0 if gd >= 0: all_under = 0 else : if gd < 0: all_over = 0 if gd > 0: all_under = 0 if all_over == 1 or all_under == 1: return 0 return 2 # a box of non-zeros volume can't be inside a plane def _hash_vals(self): return (("norm_vec[0]", self.norm_vec[0]), ("norm_vec[1]", self.norm_vec[1]), ("norm_vec[2]", self.norm_vec[2]), ("d", self.d)) def _get_state_attnames(self): return ("d", "norm_vec") cutting_selector = CuttingPlaneSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/data_collection_selector.pxi000066400000000000000000000024011510711153200307600ustar00rootroot00000000000000cdef class DataCollectionSelector(SelectorObject): cdef object obj_ids cdef np.int64_t nids def __init__(self, dobj): self.obj_ids = dobj._obj_ids self.nids = self.obj_ids.shape[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): cdef int n cdef int ng = left_edges.shape[0] cdef np.ndarray[np.uint8_t, ndim=1] gridi = np.zeros(ng, dtype='uint8') cdef np.ndarray[np.int64_t, ndim=1] oids = self.obj_ids with nogil: for n in range(self.nids): gridi[oids[n]] = 1 return gridi.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask_regular_grid(self, gobj): cdef np.ndarray[np.uint8_t, ndim=3] mask mask = np.ones(gobj.ActiveDimensions, dtype='uint8') return mask.astype("bool"), mask.size def _hash_vals(self): return (hash(self.obj_ids.tobytes()), self.nids) data_collection_selector = DataCollectionSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/disk_selector.pxi000066400000000000000000000150751510711153200266010ustar00rootroot00000000000000cdef class DiskSelector(SelectorObject): cdef public np.float64_t norm_vec[3] cdef public np.float64_t center[3] cdef public np.float64_t radius, radius2 cdef public np.float64_t height def __init__(self, dobj): cdef int i for i in range(3): self.norm_vec[i] = dobj._norm_vec[i] self.center[i] = _ensure_code(dobj.center[i]) self.radius = _ensure_code(dobj.radius) self.radius2 = self.radius * self.radius self.height = _ensure_code(dobj.height) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return self.select_point(pos) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef np.float64_t h, d, r2, temp cdef int i h = d = 0 for i in range(3): temp = self.periodic_difference(pos[i], self.center[i], i) h += temp * self.norm_vec[i] d += temp*temp r2 = (d - h*h) if fabs(h) <= self.height and r2 <= self.radius2: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef np.float64_t h, d, r2, temp cdef int i h = d = 0 for i in range(3): temp = self.periodic_difference(pos[i], self.center[i], i) h += temp * self.norm_vec[i] d += temp*temp r2 = (d - h*h) d = self.radius+radius if fabs(h) <= self.height+radius and r2 <= d*d: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # Until we can get our OBB/OBB intersection correct, disable this. return 1 # cdef np.float64_t *arr[2] # cdef np.float64_t pos[3] # cdef np.float64_t H, D, R2, temp # cdef int i, j, k, n # cdef int all_under = 1 # cdef int all_over = 1 # cdef int any_radius = 0 # # A moment of explanation (revised): # # The disk and bounding box collide if any of the following are true: # # 1) the center of the disk is inside the bounding box # # 2) any corner of the box lies inside the disk # # 3) the box spans the plane (!all_under and !all_over) and at least # # one corner is within the cylindrical radius # # check if disk center lies inside bbox # if left_edge[0] <= self.center[0] <= right_edge[0] and \ # left_edge[1] <= self.center[1] <= right_edge[1] and \ # left_edge[2] <= self.center[2] <= right_edge[2] : # return 1 # # check all corners # arr[0] = left_edge # arr[1] = right_edge # for i in range(2): # pos[0] = arr[i][0] # for j in range(2): # pos[1] = arr[j][1] # for k in range(2): # pos[2] = arr[k][2] # H = D = 0 # for n in range(3): # temp = self.difference(pos[n], self.center[n], n) # H += (temp * self.norm_vec[n]) # D += temp*temp # R2 = (D - H*H) # if R2 < self.radius2 : # any_radius = 1 # if fabs(H) < self.height: return 1 # if H < 0: all_over = 0 # if H > 0: all_under = 0 # if all_over == 0 and all_under == 0 and any_radius == 1: return 1 # return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # Until we can get our OBB/OBB intersection correct, disable this. return 2 # cdef np.float64_t *arr[2] # cdef np.float64_t pos[3], H, D, R2, temp # cdef int i, j, k, n # cdef int all_under = 1 # cdef int all_over = 1 # cdef int any_radius = 0 # # A moment of explanation (revised): # # The disk and bounding box collide if any of the following are true: # # 1) the center of the disk is inside the bounding box # # 2) any corner of the box lies inside the disk # # 3) the box spans the plane (!all_under and !all_over) and at least # # one corner is within the cylindrical radius # # check if disk center lies inside bbox # if left_edge[0] <= self.center[0] <= right_edge[0] and \ # left_edge[1] <= self.center[1] <= right_edge[1] and \ # left_edge[2] <= self.center[2] <= right_edge[2] : # return 1 # # check all corners # arr[0] = left_edge # arr[1] = right_edge # for i in range(2): # pos[0] = arr[i][0] # for j in range(2): # pos[1] = arr[j][1] # for k in range(2): # pos[2] = arr[k][2] # H = D = 0 # for n in range(3): # temp = self.periodic_difference( # pos[n], self.center[n], n) # H += (temp * self.norm_vec[n]) # D += temp*temp # R2 = (D - H*H) # if R2 < self.radius2 : # any_radius = 1 # if fabs(H) < self.height: return 1 # if H < 0: all_over = 0 # if H > 0: all_under = 0 # if all_over == 0 and all_under == 0 and any_radius == 1: return 1 # return 0 def _hash_vals(self): return (("norm_vec[0]", self.norm_vec[0]), ("norm_vec[1]", self.norm_vec[1]), ("norm_vec[2]", self.norm_vec[2]), ("center[0]", self.center[0]), ("center[1]", self.center[1]), ("center[2]", self.center[2]), ("radius", self.radius), ("radius2", self.radius2), ("height", self.height)) def _get_state_attnames(self): return ("radius", "radius2", "height", "norm_vec", "center") disk_selector = DiskSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/ellipsoid_selector.pxi000066400000000000000000000133771510711153200276360ustar00rootroot00000000000000cdef class EllipsoidSelector(SelectorObject): cdef public np.float64_t vec[3][3] cdef public np.float64_t mag[3] cdef public np.float64_t center[3] def __init__(self, dobj): cdef int i _ensure_code(dobj.center) _ensure_code(dobj._e0) _ensure_code(dobj._e1) _ensure_code(dobj._e2) _ensure_code(dobj._A) _ensure_code(dobj._B) _ensure_code(dobj._C) for i in range(3): self.center[i] = dobj.center[i] self.vec[0][i] = dobj._e0[i] self.vec[1][i] = dobj._e1[i] self.vec[2][i] = dobj._e2[i] self.mag[0] = dobj._A self.mag[1] = dobj._B self.mag[2] = dobj._C @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return self.select_point(pos) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef np.float64_t dot_evec[3] cdef np.float64_t dist cdef int i, j dot_evec[0] = dot_evec[1] = dot_evec[2] = 0 # Calculate the rotated dot product for i in range(3): # axis dist = self.periodic_difference(pos[i], self.center[i], i) for j in range(3): dot_evec[j] += dist * self.vec[j][i] dist = 0.0 for i in range(3): dist += (dot_evec[i] * dot_evec[i])/(self.mag[i] * self.mag[i]) if dist <= 1.0: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: # this is the sphere selection cdef int i cdef np.float64_t dist, dist2_max, dist2 = 0 for i in range(3): dist = self.periodic_difference(pos[i], self.center[i], i) dist2 += dist * dist dist2_max = (self.mag[0] + radius) * (self.mag[0] + radius) if dist2 <= dist2_max: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # This is the sphere selection cdef int i cdef np.float64_t box_center, relcenter, closest, dist, edge, dist_max if left_edge[0] <= self.center[0] <= right_edge[0] and \ left_edge[1] <= self.center[1] <= right_edge[1] and \ left_edge[2] <= self.center[2] <= right_edge[2]: return 1 # http://www.gamedev.net/topic/335465-is-this-the-simplest-sphere-aabb-collision-test/ dist = 0 for i in range(3): box_center = (right_edge[i] + left_edge[i])/2.0 relcenter = self.periodic_difference(box_center, self.center[i], i) edge = right_edge[i] - left_edge[i] closest = relcenter - fclip(relcenter, -edge/2.0, edge/2.0) dist += closest * closest dist_max = self.mag[0] * self.mag[0] if dist <= dist_max: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # This is the sphere selection cdef int i cdef np.float64_t box_center, relcenter, closest, farthest, cdist, fdist, edge if left_edge[0] <= self.center[0] <= right_edge[0] and \ left_edge[1] <= self.center[1] <= right_edge[1] and \ left_edge[2] <= self.center[2] <= right_edge[2]: fdist = 0 for i in range(3): edge = right_edge[i] - left_edge[i] box_center = (right_edge[i] + left_edge[i])/2.0 relcenter = self.periodic_difference( box_center, self.center[i], i) farthest = relcenter + fclip(relcenter, -edge/2.0, edge/2.0) fdist += farthest*farthest if fdist >= self.mag[0]**2: return 2 return 1 # http://www.gamedev.net/topic/335465-is-this-the-simplest-sphere-aabb-collision-test/ cdist = 0 fdist = 0 for i in range(3): box_center = (right_edge[i] + left_edge[i])/2.0 relcenter = self.periodic_difference(box_center, self.center[i], i) edge = right_edge[i] - left_edge[i] closest = relcenter - fclip(relcenter, -edge/2.0, edge/2.0) farthest = relcenter + fclip(relcenter, -edge/2.0, edge/2.0) cdist += closest * closest fdist += farthest * farthest if cdist > self.mag[0]**2: return 0 if fdist < self.mag[0]**2: return 1 else: return 2 def _hash_vals(self): return (("vec[0][0]", self.vec[0][0]), ("vec[0][1]", self.vec[0][1]), ("vec[0][2]", self.vec[0][2]), ("vec[1][0]", self.vec[1][0]), ("vec[1][1]", self.vec[1][1]), ("vec[1][2]", self.vec[1][2]), ("vec[2][0]", self.vec[2][0]), ("vec[2][1]", self.vec[2][1]), ("vec[2][2]", self.vec[2][2]), ("mag[0]", self.mag[0]), ("mag[1]", self.mag[1]), ("mag[2]", self.mag[2]), ("center[0]", self.center[0]), ("center[1]", self.center[1]), ("center[2]", self.center[2])) def _get_state_attnames(self): return ("mag", "center", "vec") ellipsoid_selector = EllipsoidSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/grid_selector.pxi000066400000000000000000000024271510711153200265710ustar00rootroot00000000000000cdef class GridSelector(SelectorObject): cdef object ind def __init__(self, dobj): self.ind = dobj.id - dobj._id_offset @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): cdef int ng = left_edges.shape[0] cdef np.ndarray[np.uint8_t, ndim=1] gridi = np.zeros(ng, dtype='uint8') gridi[self.ind] = 1 return gridi.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask_regular_grid(self, gobj): mask = np.ones(gobj.ActiveDimensions, dtype='bool') return mask, mask.size @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return 1 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: # we apparently don't check if the point actually lies in the grid.. return 1 def _hash_vals(self): return (self.ind,) grid_selector = GridSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/halo_particles_selector.pxi000066400000000000000000000007321510711153200306320ustar00rootroot00000000000000cdef class HaloParticlesSelector(SelectorObject): cdef public object base_source cdef SelectorObject base_selector cdef object pind cdef public np.int64_t halo_id def __init__(self, dobj): self.base_source = dobj.base_source self.base_selector = self.base_source.selector self.pind = dobj.particle_indices def _hash_vals(self): return ("halo_particles", self.halo_id) halo_particles_selector = HaloParticlesSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/indexed_octree_subset_selector.pxi000066400000000000000000000052361510711153200322130ustar00rootroot00000000000000cdef class IndexedOctreeSubsetSelector(SelectorObject): # This is a numpy array, which will be a bool of ndim 1 cdef np.uint64_t min_ind cdef np.uint64_t max_ind cdef public SelectorObject base_selector cdef int filter_bbox cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] def __init__(self, dobj): self.min_ind = dobj.min_ind self.max_ind = dobj.max_ind self.base_selector = dobj.base_selector self.min_level = self.base_selector.min_level self.max_level = self.base_selector.max_level self.filter_bbox = 0 if getattr(dobj.ds, "filter_bbox", False): self.filter_bbox = 1 for i in range(3): self.DLE[i] = dobj.ds.domain_left_edge[i] self.DRE[i] = dobj.ds.domain_right_edge[i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): raise RuntimeError @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int i if self.filter_bbox == 0: return 1 for i in range(3): if pos[i] < self.DLE[i] or pos[i] > self.DRE[i]: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: return self.base_selector.select_bbox(left_edge, right_edge) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: # Because visitors now use select_grid, we should be explicitly # checking this. return self.base_selector.select_grid(left_edge, right_edge, level, o) def _hash_vals(self): return (hash(self.base_selector), self.min_ind, self.max_ind) indexed_octree_subset_selector = IndexedOctreeSubsetSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/octree_subset_selector.pxi000066400000000000000000000045051510711153200305110ustar00rootroot00000000000000cdef class OctreeSubsetSelector(SelectorObject): def __init__(self, dobj): self.base_selector = dobj.base_selector self.min_level = self.base_selector.min_level self.max_level = self.base_selector.max_level self.domain_id = dobj.domain_id self.overlap_cells = getattr(dobj.oct_handler, 'overlap_cells', 1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): raise RuntimeError @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: return self.base_selector.select_point(pos) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # return 1 return self.base_selector.select_bbox(left_edge, right_edge) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: # Because visitors now use select_grid, we should be explicitly # checking this. cdef int res res = self.base_selector.select_grid(left_edge, right_edge, level, o) if self.domain_id == -1: return res elif res == 1 and o != NULL and o.domain != self.domain_id: return -1 return res def _hash_vals(self): return (hash(self.base_selector), self.domain_id) octree_subset_selector = OctreeSubsetSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/ortho_ray_selector.pxi000066400000000000000000000105231510711153200276460ustar00rootroot00000000000000cdef class OrthoRaySelector(SelectorObject): cdef public np.uint8_t px_ax cdef public np.uint8_t py_ax cdef public np.float64_t px cdef public np.float64_t py cdef public int axis def __init__(self, dobj): self.axis = dobj.axis self.px_ax = dobj.px_ax self.py_ax = dobj.py_ax self.px = dobj.px self.py = dobj.py @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask_regular_grid(self, gobj): cdef np.ndarray[np.uint8_t, ndim=3] mask cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask cdef int i, j, k cdef int total = 0 cdef int this_level = 0 cdef int ind[3][2] cdef np.int32_t level = gobj.Level _ensure_code(gobj.LeftEdge) _ensure_code(gobj.RightEdge) _ensure_code(gobj.dds) if level < self.min_level or level > self.max_level: return None else: child_mask = gobj.child_mask mask = np.zeros(gobj.ActiveDimensions, dtype=np.uint8) if level == self.max_level: this_level = 1 ind[self.axis][0] = 0 ind[self.axis][1] = gobj.ActiveDimensions[self.axis] ind[self.px_ax][0] = \ ((self.px - (gobj.LeftEdge).to_ndarray()[self.px_ax]) / gobj.dds[self.px_ax]) ind[self.px_ax][1] = ind[self.px_ax][0] + 1 ind[self.py_ax][0] = \ ((self.py - (gobj.LeftEdge).to_ndarray()[self.py_ax]) / gobj.dds[self.py_ax]) ind[self.py_ax][1] = ind[self.py_ax][0] + 1 with nogil: for i in range(ind[0][0], ind[0][1]): for j in range(ind[1][0], ind[1][1]): for k in range(ind[2][0], ind[2][1]): if this_level == 1 or child_mask[i, j, k]: mask[i, j, k] = 1 total += 1 if total == 0: return None, 0 return mask.astype("bool"), total @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: if self.px >= pos[self.px_ax] - 0.5*dds[self.px_ax] and \ self.px < pos[self.px_ax] + 0.5*dds[self.px_ax] and \ self.py >= pos[self.py_ax] - 0.5*dds[self.py_ax] and \ self.py < pos[self.py_ax] + 0.5*dds[self.py_ax]: return 1 return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: # two 0-volume constructs don't intersect return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef np.float64_t dx = self.periodic_difference( pos[self.px_ax], self.px, self.px_ax) cdef np.float64_t dy = self.periodic_difference( pos[self.py_ax], self.py, self.py_ax) if dx*dx + dy*dy < radius*radius: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: if left_edge[self.px_ax] <= self.px < right_edge[self.px_ax] and \ left_edge[self.py_ax] <= self.py < right_edge[self.py_ax] : return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: if left_edge[self.px_ax] <= self.px < right_edge[self.px_ax] and \ left_edge[self.py_ax] <= self.py < right_edge[self.py_ax] : return 2 # a box of non-zero volume can't be inside a ray return 0 def _hash_vals(self): return (("px_ax", self.px_ax), ("py_ax", self.py_ax), ("px", self.px), ("py", self.py), ("axis", self.axis)) def _get_state_attnames(self): return ("px_ax", "py_ax", "px", "py", "axis") ortho_ray_selector = OrthoRaySelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/point_selector.pxi000066400000000000000000000054621510711153200267770ustar00rootroot00000000000000cdef class PointSelector(SelectorObject): cdef public np.float64_t p[3] def __init__(self, dobj): cdef const np.float64_t[:] DLE = dobj.ds.domain_left_edge cdef const np.float64_t[:] DRE = dobj.ds.domain_right_edge for i in range(3): self.p[i] = _ensure_code(dobj.p[i]) # ensure the point lies in the domain if self.periodicity[i]: self.p[i] = np.fmod(self.p[i], self.domain_width[i]) if self.p[i] < DLE[i]: self.p[i] += self.domain_width[i] elif self.p[i] >= DRE[i]: self.p[i] -= self.domain_width[i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: if (pos[0] - 0.5*dds[0] <= self.p[0] < pos[0]+0.5*dds[0] and pos[1] - 0.5*dds[1] <= self.p[1] < pos[1]+0.5*dds[1] and pos[2] - 0.5*dds[2] <= self.p[2] < pos[2]+0.5*dds[2]): return 1 else: return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int i cdef np.float64_t dist, dist2 = 0 for i in range(3): dist = self.periodic_difference(pos[i], self.p[i], i) dist2 += dist*dist if dist2 <= radius*radius: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # point definitely can only be in one cell if (left_edge[0] <= self.p[0] < right_edge[0] and left_edge[1] <= self.p[1] < right_edge[1] and left_edge[2] <= self.p[2] < right_edge[2]): return 1 else: return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: # point definitely can only be in one cell # Return 2 in all cases to indicate that the point only overlaps # portion of box if (left_edge[0] <= self.p[0] <= right_edge[0] and left_edge[1] <= self.p[1] <= right_edge[1] and left_edge[2] <= self.p[2] <= right_edge[2]): return 2 else: return 0 def _hash_vals(self): return (("p[0]", self.p[0]), ("p[1]", self.p[1]), ("p[2]", self.p[2])) def _get_state_attnames(self): return ('p', ) point_selector = PointSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/ray_selector.pxi000066400000000000000000000244211510711153200264350ustar00rootroot00000000000000cdef struct IntegrationAccumulator: np.float64_t *t np.float64_t *dt np.uint8_t *child_mask int hits cdef void dt_sampler( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil: cdef IntegrationAccumulator *am = data cdef int di = (index[0]*vc.dims[1]+index[1])*vc.dims[2]+index[2] if am.child_mask[di] == 0 or enter_t == exit_t: return am.hits += 1 am.t[di] = enter_t am.dt[di] = (exit_t - enter_t) cdef class RaySelector(SelectorObject): cdef public np.float64_t p1[3] cdef public np.float64_t p2[3] cdef public np.float64_t vec[3] def __init__(self, dobj): cdef int i _ensure_code(dobj.start_point) _ensure_code(dobj.end_point) for i in range(3): self.vec[i] = dobj.vec[i] self.p1[i] = dobj.start_point[i] self.p2[i] = dobj.end_point[i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask_regular_grid(self, gobj): cdef np.ndarray[np.float64_t, ndim=3] t, dt cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask cdef int i cdef int total = 0 cdef IntegrationAccumulator *ia ia = malloc(sizeof(IntegrationAccumulator)) cdef VolumeContainer vc mask = np.zeros(gobj.ActiveDimensions, dtype='uint8') t = np.zeros(gobj.ActiveDimensions, dtype="float64") dt = np.zeros(gobj.ActiveDimensions, dtype="float64") - 1 child_mask = gobj.child_mask ia.t = t.data ia.dt = dt.data ia.child_mask = child_mask.data ia.hits = 0 _ensure_code(gobj.LeftEdge) _ensure_code(gobj.RightEdge) _ensure_code(gobj.dds) for i in range(3): vc.left_edge[i] = gobj.LeftEdge[i] vc.right_edge[i] = gobj.RightEdge[i] vc.dds[i] = gobj.dds[i] vc.idds[i] = 1.0/gobj.dds[i] vc.dims[i] = dt.shape[i] walk_volume(&vc, self.p1, self.vec, dt_sampler, ia) for i in range(dt.shape[0]): for j in range(dt.shape[1]): for k in range(dt.shape[2]): if dt[i, j, k] >= 0: mask[i, j, k] = 1 total += 1 free(ia) if total == 0: return None, 0 return mask.astype("bool"), total @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_dt(self, gobj): cdef np.ndarray[np.float64_t, ndim=3] t, dt cdef np.ndarray[np.float64_t, ndim=1] tr, dtr cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask cdef int i, j, k, ni cdef IntegrationAccumulator *ia ia = malloc(sizeof(IntegrationAccumulator)) cdef VolumeContainer vc t = np.zeros(gobj.ActiveDimensions, dtype="float64") dt = np.zeros(gobj.ActiveDimensions, dtype="float64") - 1 child_mask = gobj.child_mask ia.t = t.data ia.dt = dt.data ia.child_mask = child_mask.data ia.hits = 0 _ensure_code(gobj.LeftEdge) _ensure_code(gobj.RightEdge) _ensure_code(gobj.dds) for i in range(3): vc.left_edge[i] = gobj.LeftEdge[i] vc.right_edge[i] = gobj.RightEdge[i] vc.dds[i] = gobj.dds[i] vc.idds[i] = 1.0/gobj.dds[i] vc.dims[i] = dt.shape[i] walk_volume(&vc, self.p1, self.vec, dt_sampler, ia) tr = np.zeros(ia.hits, dtype="float64") dtr = np.zeros(ia.hits, dtype="float64") ni = 0 for i in range(dt.shape[0]): for j in range(dt.shape[1]): for k in range(dt.shape[2]): if dt[i, j, k] >= 0: tr[ni] = t[i, j, k] dtr[ni] = dt[i, j, k] ni += 1 if not (ni == ia.hits): print(ni, ia.hits) free(ia) return dtr, tr @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_dt_mesh(self, mesh, nz, int offset): cdef np.ndarray[np.float64_t, ndim=3] t, dt cdef np.ndarray[np.float64_t, ndim=1] tr, dtr cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask cdef int i, j, k, ni cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef np.float64_t pos cdef IntegrationAccumulator *ia ia = malloc(sizeof(IntegrationAccumulator)) cdef np.ndarray[np.float64_t, ndim=2] coords cdef np.ndarray[np.int64_t, ndim=2] indices indices = mesh.connectivity_indices coords = _ensure_code(mesh.connectivity_coords) cdef int nc = indices.shape[0] cdef int nv = indices.shape[1] if nv != 8: raise NotImplementedError cdef VolumeContainer vc child_mask = np.ones((1,1,1), dtype="uint8") t = np.zeros((1,1,1), dtype="float64") dt = np.zeros((1,1,1), dtype="float64") - 1 tr = np.zeros(nz, dtype="float64") dtr = np.zeros(nz, dtype="float64") ia.t = t.data ia.dt = dt.data ia.child_mask = child_mask.data ia.hits = 0 ni = 0 for i in range(nc): for j in range(3): LE[j] = 1e60 RE[j] = -1e60 for j in range(nv): for k in range(3): pos = coords[indices[i, j] - offset, k] LE[k] = fmin(pos, LE[k]) RE[k] = fmax(pos, RE[k]) for j in range(3): vc.left_edge[j] = LE[j] vc.right_edge[j] = RE[j] vc.dds[j] = RE[j] - LE[j] vc.idds[j] = 1.0/vc.dds[j] vc.dims[j] = 1 t[0,0,0] = dt[0,0,0] = -1 walk_volume(&vc, self.p1, self.vec, dt_sampler, ia) if dt[0,0,0] >= 0: tr[ni] = t[0,0,0] dtr[ni] = dt[0,0,0] ni += 1 free(ia) return dtr, tr cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: # two 0-volume constructs don't intersect return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int i cdef np.float64_t length = norm(self.vec) cdef np.float64_t r[3] for i in range(3): r[i] = pos[i] - self.p1[i] # the projected position of the sphere along the ray cdef np.float64_t l = dot(r, self.vec) / length # the square of the impact parameter cdef np.float64_t b_sqr = dot(r, r) - l*l # only accept spheres with radii larger than the impact parameter and # with a projected position along the ray no more than a radius away # from the ray if -radius < l and l < (length+radius) and b_sqr < radius*radius: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int i, rv cdef VolumeContainer vc cdef IntegrationAccumulator *ia ia = malloc(sizeof(IntegrationAccumulator)) cdef np.float64_t dt[1] cdef np.float64_t t[1] cdef np.uint8_t cm[1] for i in range(3): vc.left_edge[i] = left_edge[i] vc.right_edge[i] = right_edge[i] vc.dds[i] = right_edge[i] - left_edge[i] vc.idds[i] = 1.0/vc.dds[i] vc.dims[i] = 1 t[0] = dt[0] = 0.0 cm[0] = 1 ia.t = t ia.dt = dt ia.child_mask = cm ia.hits = 0 walk_volume(&vc, self.p1, self.vec, dt_sampler, ia) rv = 0 if ia.hits > 0: rv = 1 free(ia) return rv @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int i cdef np.uint8_t cm = 1 cdef VolumeContainer vc cdef IntegrationAccumulator ia cdef np.float64_t dt, t for i in range(3): vc.left_edge[i] = left_edge[i] vc.right_edge[i] = right_edge[i] vc.dds[i] = right_edge[i] - left_edge[i] vc.idds[i] = 1.0/vc.dds[i] vc.dims[i] = 1 t = dt = 0.0 ia.t = &t ia.dt = &dt ia.child_mask = &cm ia.hits = 0 walk_volume(&vc, self.p1, self.vec, dt_sampler, &ia) if ia.hits > 0: return 2 # a box of non-zero volume cannot be inside a ray return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: # This is terribly inefficient for Octrees. For grids, it will never # get called. cdef int i cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] for i in range(3): left_edge[i] = pos[i] - dds[i]/2.0 right_edge[i] = pos[i] + dds[i]/2.0 return self.select_bbox(left_edge, right_edge) def _hash_vals(self): return (("p1[0]", self.p1[0]), ("p1[1]", self.p1[1]), ("p1[2]", self.p1[2]), ("p2[0]", self.p2[0]), ("p2[1]", self.p2[1]), ("p2[2]", self.p2[2]), ("vec[0]", self.vec[0]), ("vec[1]", self.vec[1]), ("vec[2]", self.vec[2])) def _get_state_attnames(self): return ("p1", "p2", "vec") ray_selector = RaySelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/region_selector.pxi000066400000000000000000000216611510711153200271300ustar00rootroot00000000000000cdef class RegionSelector(SelectorObject): cdef public np.float64_t left_edge[3] cdef public np.float64_t right_edge[3] cdef public np.float64_t right_edge_shift[3] cdef public bint is_all_data cdef public bint loose_selection cdef public bint check_period[3] @cython.boundscheck(False) @cython.wraparound(False) def __init__(self, dobj): cdef int i # We are modifying dobj.left_edge and dobj.right_edge , so here we will # do an in-place conversion of those arrays. cdef np.float64_t[:] RE = dobj.right_edge.copy() cdef np.float64_t[:] LE = dobj.left_edge.copy() cdef const np.float64_t[:] DW = dobj.ds.domain_width cdef const np.float64_t[:] DLE = dobj.ds.domain_left_edge cdef const np.float64_t[:] DRE = dobj.ds.domain_right_edge le_all = (np.array(LE) == dobj.ds.domain_left_edge).all() re_all = (np.array(RE) == dobj.ds.domain_right_edge).all() # If we have a bounding box, then we should *not* revert to all data domain_override = getattr(dobj.ds, '_domain_override', False) if le_all and re_all and not domain_override: self.is_all_data = True else: self.is_all_data = False cdef np.float64_t region_width[3] cdef bint p[3] # This is for if we want to include zones that overlap and whose # centers are not strictly included. self.loose_selection = getattr(dobj, "loose_selection", False) for i in range(3): self.check_period[i] = False region_width[i] = RE[i] - LE[i] p[i] = dobj.ds.periodicity[i] if region_width[i] <= 0: raise RuntimeError( "Region right edge[%s] < left edge: width = %s" % ( i, region_width[i])) for i in range(3): if p[i]: # First, we check if any criteria requires a period check, # without any adjustments. This is for short-circuiting the # short-circuit of the loop down below in mask filling. if LE[i] < DLE[i] or LE[i] > DRE[i] or RE[i] > DRE[i]: self.check_period[i] = True # shift so left_edge guaranteed in domain if LE[i] < DLE[i]: LE[i] += DW[i] RE[i] += DW[i] elif LE[i] > DRE[i]: LE[i] -= DW[i] RE[i] -= DW[i] else: if LE[i] < DLE[i] or RE[i] > DRE[i]: raise RuntimeError( "yt attempted to read outside the boundaries of " "a non-periodic domain along dimension %s.\n" "Region left edge = %s, Region right edge = %s\n" "Dataset left edge = %s, Dataset right edge = %s\n\n" "This commonly happens when trying to compute ghost cells " "up to the domain boundary. Two possible solutions are to " "select a smaller region that does not border domain edge " "(see https://yt-project.org/docs/analyzing/objects.html?highlight=region)\n" "or override the periodicity with\n" "ds.force_periodicity()" % \ (i, dobj.left_edge[i], dobj.right_edge[i], dobj.ds.domain_left_edge[i], dobj.ds.domain_right_edge[i]) ) # Already ensured in code self.left_edge[i] = LE[i] self.right_edge[i] = RE[i] self.right_edge_shift[i] = RE[i] - DW[i] if not self.periodicity[i]: self.right_edge_shift[i] = -np.inf @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int i for i in range(3): if (right_edge[i] < self.left_edge[i] and \ left_edge[i] >= self.right_edge_shift[i]) or \ left_edge[i] >= self.right_edge[i]: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef int i for i in range(3): if (right_edge[i] < self.left_edge[i] and \ left_edge[i] >= self.right_edge_shift[i]) or \ left_edge[i] >= self.right_edge[i]: return 0 for i in range(3): if left_edge[i] < self.right_edge_shift[i]: if right_edge[i] >= self.right_edge_shift[i]: return 2 elif left_edge[i] < self.left_edge[i] or \ right_edge[i] >= self.right_edge[i]: return 2 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef int i if self.loose_selection: for i in range(3): left_edge[i] = pos[i] - dds[i]*0.5 right_edge[i] = pos[i] + dds[i]*0.5 return self.select_bbox(left_edge, right_edge) return self.select_point(pos) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int i for i in range(3): if (self.right_edge_shift[i] <= pos[i] < self.left_edge[i]) or \ pos[i] >= self.right_edge[i]: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: # adapted from http://stackoverflow.com/a/4579192/1382869 cdef int i cdef np.float64_t p cdef np.float64_t r2 = radius**2 cdef np.float64_t dmin = 0 cdef np.float64_t d = 0 for i in range(3): if (pos[i]+radius < self.left_edge[i] and \ pos[i]-radius >= self.right_edge_shift[i]): d = self.periodic_difference(pos[i], self.left_edge[i], i) elif pos[i]-radius > self.right_edge[i]: d = self.periodic_difference(pos[i], self.right_edge[i], i) dmin += d*d return int(dmin <= r2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int fill_mask_selector_regular_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.float64_t dds[3], int dim[3], np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask, np.ndarray[np.uint8_t, ndim=3] mask, int level): cdef int i, j, k cdef int total = 0, this_level = 0 cdef np.float64_t pos[3] if level < self.min_level or level > self.max_level: return 0 if level == self.max_level: this_level = 1 cdef np.int64_t si[3] cdef np.int64_t ei[3] for i in range(3): if not self.check_period[i]: si[i] = ((self.left_edge[i] - left_edge[i])/dds[i]) ei[i] = ((self.right_edge[i] - left_edge[i])/dds[i]) si[i] = iclip(si[i] - 1, 0, dim[i]) ei[i] = iclip(ei[i] + 1, 0, dim[i]) else: si[i] = 0 ei[i] = dim[i] with nogil: for i in range(si[0], ei[0]): pos[0] = left_edge[0] + (i + 0.5) * dds[0] for j in range(si[1], ei[1]): pos[1] = left_edge[1] + (j + 0.5) * dds[1] for k in range(si[2], ei[2]): pos[2] = left_edge[2] + (k + 0.5) * dds[2] if child_mask[i, j, k] == 1 or this_level == 1: mask[i, j, k] = self.select_cell(pos, dds) total += mask[i, j, k] return total def _hash_vals(self): return (("left_edge[0]", self.left_edge[0]), ("left_edge[1]", self.left_edge[1]), ("left_edge[2]", self.left_edge[2]), ("right_edge[0]", self.right_edge[0]), ("right_edge[1]", self.right_edge[1]), ("right_edge[2]", self.right_edge[2])) def _get_state_attnames(self): return ('left_edge', 'right_edge', 'right_edge_shift', 'check_period', 'is_all_data', 'loose_selection') region_selector = RegionSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/selector_object.pxi000066400000000000000000000722651510711153200271210ustar00rootroot00000000000000from .oct_visitors cimport CountTotalCells, CountTotalOcts cdef class SelectorObject: def __cinit__(self, dobj, *args): self._hash_initialized = 0 cdef const np.float64_t [:] DLE cdef const np.float64_t [:] DRE min_level = getattr(dobj, "min_level", None) max_level = getattr(dobj, "max_level", None) if min_level is None: min_level = 0 if max_level is None: max_level = 99 self.min_level = min_level self.max_level = max_level self.overlap_cells = 0 ds = getattr(dobj, 'ds', None) if ds is None: for i in range(3): # NOTE that this is not universal. self.domain_width[i] = 1.0 self.periodicity[i] = False else: DLE = ds.domain_left_edge DRE = ds.domain_right_edge for i in range(3): self.domain_width[i] = DRE[i] - DLE[i] self.domain_center[i] = DLE[i] + 0.5 * self.domain_width[i] self.periodicity[i] = ds.periodicity[i] def get_periodicity(self): cdef int i cdef np.ndarray[np.uint8_t, ndim=1] periodicity periodicity = np.zeros(3, dtype='uint8') for i in range(3): periodicity[i] = self.periodicity[i] return periodicity @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_grids(self, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels): cdef int i, n cdef int ng = left_edges.shape[0] cdef np.ndarray[np.uint8_t, ndim=1] gridi = np.zeros(ng, dtype='uint8') cdef np.float64_t LE[3] cdef np.float64_t RE[3] _ensure_code(left_edges) _ensure_code(right_edges) with nogil: for n in range(ng): # Call our selector function # Check if the sphere is inside the grid for i in range(3): LE[i] = left_edges[n, i] RE[i] = right_edges[n, i] gridi[n] = self.select_grid(LE, RE, levels[n, 0]) return gridi.astype("bool") def count_octs(self, OctreeContainer octree, int domain_id = -1): cdef CountTotalOcts visitor visitor = CountTotalOcts(octree, domain_id) octree.visit_all_octs(self, visitor) return visitor.index def count_oct_cells(self, OctreeContainer octree, int domain_id = -1): cdef CountTotalCells visitor visitor = CountTotalCells(octree, domain_id) octree.visit_all_octs(self, visitor) return visitor.index @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void recursively_visit_octs(self, Oct *root, np.float64_t pos[3], np.float64_t dds[3], int level, OctVisitor visitor, int visit_covered = 0): # visit_covered tells us whether this octree supports partial # refinement. If it does, we need to handle this specially -- first # we visit *this* oct, then we make a second pass to check any child # octs. cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef np.float64_t sdds[3] cdef np.float64_t spos[3] cdef int i, j, k, res cdef Oct *ch # Remember that pos is the *center* of the oct, and dds is the oct # width. So to get to the edges, we add/subtract half of dds. for i in range(3): # sdds is the cell width sdds[i] = dds[i]/2.0 LE[i] = pos[i] - dds[i]/2.0 RE[i] = pos[i] + dds[i]/2.0 #print(LE[0], RE[0], LE[1], RE[1], LE[2], RE[2]) res = self.select_grid(LE, RE, level, root) if res == 1 and visitor.domain > 0 and root.domain != visitor.domain: res = -1 cdef int increment = 1 cdef int next_level, this_level # next_level: an int that says whether or not we can progress to children # this_level: an int that says whether or not we can select from this # level next_level = this_level = 1 if res == -1: # This happens when we do domain selection but the oct has # children. This would allow an oct to pass to its children but # not get accessed itself. next_level = 1 this_level = 0 elif level == self.max_level: next_level = 0 elif level < self.min_level or level > self.max_level: this_level = 0 if res == 0 and this_level == 1: return # Now we visit all our children. We subtract off sdds for the first # pass because we center it on the first cell. cdef int iter = 1 - visit_covered # 2 if 1, 1 if 0. # So the order here goes like so. If visit_covered is 1, which usually # comes from "partial_coverage", we visit the components of a zone even # if it has children. But in general, the first iteration through, we # visit each cell. This means that only if visit_covered is true do we # visit potentially covered cells. The next time through, we visit # child cells. while iter < 2: spos[0] = pos[0] - sdds[0]/2.0 for i in range(2): spos[1] = pos[1] - sdds[1]/2.0 for j in range(2): spos[2] = pos[2] - sdds[2]/2.0 for k in range(2): ch = NULL # We only supply a child if we are actually going to # look at the next level. if root.children != NULL and next_level == 1: ch = root.children[cind(i, j, k)] if iter == 1 and next_level == 1 and ch != NULL: # Note that visitor.pos is always going to be the # position of the Oct -- it is *not* always going # to be the same as the position of the cell under # investigation. visitor.pos[0] = (visitor.pos[0] << 1) + i visitor.pos[1] = (visitor.pos[1] << 1) + j visitor.pos[2] = (visitor.pos[2] << 1) + k visitor.level += 1 self.recursively_visit_octs( ch, spos, sdds, level + 1, visitor, visit_covered) visitor.pos[0] = (visitor.pos[0] >> 1) visitor.pos[1] = (visitor.pos[1] >> 1) visitor.pos[2] = (visitor.pos[2] >> 1) visitor.level -= 1 elif this_level == 1 and visitor.nz > 1: visitor.global_index += increment increment = 0 self.visit_oct_cells(root, ch, spos, sdds, visitor, i, j, k) elif this_level == 1 and increment == 1: visitor.global_index += increment increment = 0 visitor.ind[0] = visitor.ind[1] = visitor.ind[2] = 0 visitor.visit(root, 1) spos[2] += sdds[2] spos[1] += sdds[1] spos[0] += sdds[0] this_level = 0 # We turn this off for the second pass. iter += 1 cdef void visit_oct_cells(self, Oct *root, Oct *ch, np.float64_t spos[3], np.float64_t sdds[3], OctVisitor visitor, int i, int j, int k): # We can short-circuit the whole process if data.nz == 2. # This saves us some funny-business. cdef int selected if visitor.nz == 2: selected = self.select_cell(spos, sdds) if ch != NULL: selected *= self.overlap_cells # visitor.ind refers to the cell, not to the oct. visitor.ind[0] = i visitor.ind[1] = j visitor.ind[2] = k visitor.visit(root, selected) return # Okay, now that we've got that out of the way, we have to do some # other checks here. In this case, spos[] is the position of the # center of a *possible* oct child, which means it is the center of a # cluster of cells. That cluster might have 1, 8, 64, ... cells in it. # But, we can figure it out by calculating the cell dds. cdef np.float64_t dds[3] cdef np.float64_t pos[3] cdef int ci, cj, ck cdef int nr = (visitor.nz >> 1) for ci in range(3): dds[ci] = sdds[ci] / nr # Boot strap at the first index. pos[0] = (spos[0] - sdds[0]/2.0) + dds[0] * 0.5 for ci in range(nr): pos[1] = (spos[1] - sdds[1]/2.0) + dds[1] * 0.5 for cj in range(nr): pos[2] = (spos[2] - sdds[2]/2.0) + dds[2] * 0.5 for ck in range(nr): selected = self.select_cell(pos, dds) if ch != NULL: selected *= self.overlap_cells visitor.ind[0] = ci + i * nr visitor.ind[1] = cj + j * nr visitor.ind[2] = ck + k * nr visitor.visit(root, selected) pos[2] += dds[2] pos[1] += dds[1] pos[0] += dds[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: if level < self.min_level or level > self.max_level: return 0 return self.select_bbox(left_edge, right_edge) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_grid_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = NULL) noexcept nogil: if level < self.min_level or level > self.max_level: return 0 return self.select_bbox_edge(left_edge, right_edge) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: return 0 cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: return 0 cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: """ Returns: 0: If the selector does not touch the bounding box. 1: If the selector overlaps the bounding box anywhere. """ return 0 cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: """ Returns: 0: If the selector does not touch the bounding box. 1: If the selector contains the entire bounding box. 2: If the selector contains part of the bounding box. """ return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.float64_t periodic_difference(self, np.float64_t x1, np.float64_t x2, int d) noexcept nogil: # domain_width is already in code units, and we assume what is fed in # is too. cdef np.float64_t rel = x1 - x2 if self.periodicity[d]: if rel > self.domain_width[d] * 0.5: rel -= self.domain_width[d] elif rel < -self.domain_width[d] * 0.5: rel += self.domain_width[d] return rel @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mesh_mask(self, mesh): cdef np.float64_t pos[3] cdef np.ndarray[np.int64_t, ndim=2] indices cdef np.ndarray[np.float64_t, ndim=2] coords cdef np.ndarray[np.uint8_t, ndim=1] mask cdef int i, j, k, selected cdef int npoints, nv = mesh._connectivity_length cdef int total = 0 cdef int offset = mesh._index_offset coords = _ensure_code(mesh.connectivity_coords) indices = mesh.connectivity_indices npoints = indices.shape[0] mask = np.zeros(npoints, dtype='uint8') for i in range(npoints): selected = 0 for j in range(nv): for k in range(3): pos[k] = coords[indices[i, j] - offset, k] selected = self.select_point(pos) if selected == 1: break total += selected mask[i] = selected if total == 0: return None return mask.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mesh_cell_mask(self, mesh): cdef np.float64_t pos cdef np.float64_t le[3] cdef np.float64_t re[3] cdef np.ndarray[np.int64_t, ndim=2] indices cdef np.ndarray[np.float64_t, ndim=2] coords cdef np.ndarray[np.uint8_t, ndim=1] mask cdef int i, j, k, selected cdef int npoints, nv = mesh._connectivity_length cdef int ndim = mesh.connectivity_coords.shape[1] cdef int total = 0 cdef int offset = mesh._index_offset coords = _ensure_code(mesh.connectivity_coords) indices = mesh.connectivity_indices npoints = indices.shape[0] mask = np.zeros(npoints, dtype='uint8') for i in range(npoints): selected = 0 for k in range(3): le[k] = 1e60 re[k] = -1e60 for j in range(nv): for k in range(ndim): pos = coords[indices[i, j] - offset, k] le[k] = fmin(pos, le[k]) re[k] = fmax(pos, re[k]) for k in range(2, ndim - 1, -1): le[k] = self.domain_center[k] re[k] = self.domain_center[k] selected = self.select_bbox(le, re) total += selected mask[i] = selected if total == 0: return None return mask.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask_regular_grid(self, gobj): cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask child_mask = gobj.child_mask cdef np.ndarray[np.uint8_t, ndim=3] mask cdef int dim[3] _ensure_code(gobj.dds) _ensure_code(gobj.LeftEdge) _ensure_code(gobj.RightEdge) cdef np.ndarray[np.float64_t, ndim=1] odds = gobj.dds.d cdef np.ndarray[np.float64_t, ndim=1] oleft_edge = gobj.LeftEdge.d cdef np.ndarray[np.float64_t, ndim=1] oright_edge = gobj.RightEdge.d cdef int i cdef np.float64_t dds[3] cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] for i in range(3): dds[i] = odds[i] dim[i] = gobj.ActiveDimensions[i] left_edge[i] = oleft_edge[i] right_edge[i] = oright_edge[i] mask = np.zeros(gobj.ActiveDimensions, dtype='uint8') # Check for the level bounds cdef np.int32_t level = gobj.Level # We set this to 1 if we ignore child_mask cdef int total total = self.fill_mask_selector_regular_grid(left_edge, right_edge, dds, dim, child_mask, mask, level) if total == 0: return None, 0 return mask.astype("bool"), total @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int fill_mask_selector_regular_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.float64_t dds[3], int dim[3], np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask, np.ndarray[np.uint8_t, ndim=3] mask, int level): cdef int i, j, k cdef int total = 0, this_level = 0 cdef np.float64_t pos[3] if level < self.min_level or level > self.max_level: return 0 if level == self.max_level: this_level = 1 with nogil: for i in range(dim[0]): pos[0] = left_edge[0] + (i + 0.5) * dds[0] for j in range(dim[1]): pos[1] = left_edge[1] + (j + 0.5) * dds[1] for k in range(dim[2]): pos[2] = left_edge[2] + (k + 0.5) * dds[2] if child_mask[i, j, k] == 1 or this_level == 1: mask[i, j, k] = self.select_cell(pos, dds) total += mask[i, j, k] return total @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask(self, gobj): # This is for an irregular grid. We make no assumptions about the # shape of the dds values, which are supplied as differing-length # arrays. cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask child_mask = gobj.child_mask cdef np.ndarray[np.uint8_t, ndim=3] mask cdef int dim[3] _ensure_code(gobj.cell_widths[0]) _ensure_code(gobj.cell_widths[1]) _ensure_code(gobj.cell_widths[2]) _ensure_code(gobj.LeftEdge) _ensure_code(gobj.RightEdge) cdef np.ndarray[np.float64_t, ndim=1] oleft_edge = gobj.LeftEdge.d cdef np.ndarray[np.float64_t, ndim=1] oright_edge = gobj.RightEdge.d cdef np.ndarray[np.float64_t, ndim=1] ocell_width cdef int i, n = 0 cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef np.float64_t **dds dds = malloc(sizeof(np.float64_t*)*3) for i in range(3): dim[i] = gobj.ActiveDimensions[i] left_edge[i] = oleft_edge[i] right_edge[i] = oright_edge[i] dds[i] = malloc(sizeof(np.float64_t) * dim[i]) ocell_width = gobj.cell_widths[i] for j in range(dim[i]): dds[i][j] = ocell_width[j] mask = np.zeros(gobj.ActiveDimensions, dtype='uint8') # Check for the level bounds cdef np.int32_t level = gobj.Level # We set this to 1 if we ignore child_mask cdef int total total = self.fill_mask_selector(left_edge, right_edge, dds, dim, child_mask, mask, level) for i in range(3): free(dds[i]) free(dds) if total == 0: return None return mask.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int fill_mask_selector(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.float64_t **dds, int dim[3], np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask, np.ndarray[np.uint8_t, ndim=3] mask, int level): cdef int i, j, k cdef int total = 0, this_level = 0 cdef np.float64_t pos[3] if level < self.min_level or level > self.max_level: return 0 if level == self.max_level: this_level = 1 cdef np.float64_t *offsets[3] cdef np.float64_t c = 0.0 cdef np.float64_t tdds[3] for i in range(3): offsets[i] = malloc(dim[i] * sizeof(np.float64_t)) c = left_edge[i] for j in range(dim[i]): offsets[i][j] = c c += dds[i][j] with nogil: # We need to keep in mind that it is entirely possible to # accumulate round-off error by doing lots of additions, etc. # That's one of the reasons we construct (ahead of time) the edge # array. I mean, we don't necessarily *have* to do that, but it # seems OK. for i in range(dim[0]): tdds[0] = dds[0][i] pos[0] = offsets[0][i] + 0.5 * tdds[0] for j in range(dim[1]): tdds[1] = dds[1][j] pos[1] = offsets[1][j] + 0.5 * tdds[1] for k in range(dim[2]): tdds[2] = dds[2][k] pos[2] = offsets[2][k] + 0.5 * tdds[2] if child_mask[i, j, k] == 1 or this_level == 1: mask[i, j, k] = self.select_cell(pos, tdds) total += mask[i, j, k] free(offsets[0]) free(offsets[1]) free(offsets[2]) return total @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void visit_grid_cells(self, GridVisitorData *data, grid_visitor_function *func, np.uint8_t *cached_mask = NULL): # This function accepts a grid visitor function, the data that # corresponds to the current grid being examined (the most important # aspect of which is the .grid attribute, along with index values and # void* pointers to arrays) and a possibly-pre-generated cached mask. # Each cell is visited with the grid visitor function. cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef np.float64_t dds[3] cdef int dim[3] cdef int this_level = 0, level, i cdef np.float64_t pos[3] level = data.grid.level if level < self.min_level or level > self.max_level: return if level == self.max_level: this_level = 1 cdef np.uint8_t child_masked, selected for i in range(3): left_edge[i] = data.grid.left_edge[i] right_edge[i] = data.grid.right_edge[i] dds[i] = (right_edge[i] - left_edge[i])/data.grid.dims[i] dim[i] = data.grid.dims[i] with nogil: pos[0] = left_edge[0] + dds[0] * 0.5 data.pos[0] = 0 for i in range(dim[0]): pos[1] = left_edge[1] + dds[1] * 0.5 data.pos[1] = 0 for j in range(dim[1]): pos[2] = left_edge[2] + dds[2] * 0.5 data.pos[2] = 0 for k in range(dim[2]): # We short-circuit if we have a cache; if we don't, we # only set selected to true if it's *not* masked by a # child and it *is* selected. if cached_mask != NULL: selected = ba_get_value(cached_mask, data.global_index) else: if this_level == 1: child_masked = 0 else: child_masked = check_child_masked(data) if child_masked == 0: selected = self.select_cell(pos, dds) else: selected = 0 func(data, selected) data.global_index += 1 pos[2] += dds[2] data.pos[2] += 1 pos[1] += dds[1] data.pos[1] += 1 pos[0] += dds[0] data.pos[0] += 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def count_points(self, np.ndarray[cython.floating, ndim=1] x, np.ndarray[cython.floating, ndim=1] y, np.ndarray[cython.floating, ndim=1] z, radii): cdef int count = 0 cdef int i cdef np.float64_t pos[3] cdef np.float64_t radius cdef np.float64_t[:] _radii if radii is not None: _radii = np.atleast_1d(np.array(radii, dtype='float64')) else: _radii = np.array([0.0], dtype='float64') _ensure_code(x) _ensure_code(y) _ensure_code(z) with nogil: for i in range(x.shape[0]): pos[0] = x[i] pos[1] = y[i] pos[2] = z[i] if _radii.shape[0] == 1: radius = _radii[0] else: radius = _radii[i] if radius == 0: count += self.select_point(pos) else: count += self.select_sphere(pos, radius) return count @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def select_points(self, np.ndarray[cython.floating, ndim=1] x, np.ndarray[cython.floating, ndim=1] y, np.ndarray[cython.floating, ndim=1] z, radii): cdef int count = 0 cdef int i cdef np.float64_t pos[3] cdef np.float64_t radius cdef np.ndarray[np.uint8_t, ndim=1] mask cdef np.float64_t[:] _radii if radii is not None: _radii = np.atleast_1d(np.array(radii, dtype='float64')) else: _radii = np.array([0.0], dtype='float64') mask = np.empty(x.shape[0], dtype='uint8') _ensure_code(x) _ensure_code(y) _ensure_code(z) # this is to allow selectors to optimize the point vs # 0-radius sphere case. These two may have different # effects for 0-volume selectors, however (collision # between a ray and a point is null, while ray and a # sphere is allowed) with nogil: for i in range(x.shape[0]) : pos[0] = x[i] pos[1] = y[i] pos[2] = z[i] if _radii.shape[0] == 1: radius = 0 else: radius = _radii[i] if radius == 0: mask[i] = self.select_point(pos) else: mask[i] = self.select_sphere(pos, radius) count += mask[i] if count == 0: return None return mask.view("bool") def __hash__(self): # convert data to be hashed to a byte array, which FNV algorithm expects if self._hash_initialized == 1: return self._hash hash_data = bytearray() for v in self._hash_vals() + self._base_hash(): if isinstance(v, tuple): hash_data.extend(v[0].encode('ascii')) hash_data.extend(repr(v[1]).encode('ascii')) else: hash_data.extend(repr(v).encode('ascii')) cdef np.int64_t hash_value = fnv_hash(hash_data) self._hash = hash_value self._hash_initialized = 1 return hash_value def _hash_vals(self): raise NotImplementedError def _base_hash(self): return (("min_level", self.min_level), ("max_level", self.max_level), ("overlap_cells", self.overlap_cells), ("periodicity[0]", self.periodicity[0]), ("periodicity[1]", self.periodicity[1]), ("periodicity[2]", self.periodicity[2]), ("domain_width[0]", self.domain_width[0]), ("domain_width[1]", self.domain_width[1]), ("domain_width[2]", self.domain_width[2])) def _get_state_attnames(self): # return a tupe of attr names for __setstate__: implement for each subclass raise NotImplementedError def __getstate__(self): # returns a tuple containing (attribute name, attribute value) tuples needed to # rebuild the state: base_atts = ("min_level", "max_level", "overlap_cells", "periodicity", "domain_width", "domain_center") child_atts = self._get_state_attnames() # assemble the state_tuple (('a1', a1val), ('a2', a2val),...) state_tuple = () for fld in base_atts + child_atts: state_tuple += ((fld, getattr(self, fld)), ) return state_tuple def __getnewargs__(self): # __setstate__ will always call __cinit__, this pickle hook returns arguments # to __cinit__. We will give it None so we dont error then set attributes in # __setstate__ Note that we could avoid this by making dobj an optional argument # to __cinit__ return (None, ) def __setstate__(self, state_tuple): # parse and set attributes from the state_tuple: (('a1',a1val),('a2',a2val),...) for attr in state_tuple: setattr(self, attr[0], attr[1]) yt-project-yt-f043ac8/yt/geometry/_selection_routines/slice_selector.pxi000066400000000000000000000110731510711153200267400ustar00rootroot00000000000000cdef class SliceSelector(SelectorObject): cdef public int axis cdef public np.float64_t coord cdef public int ax, ay cdef public int reduced_dimensionality def __init__(self, dobj): self.axis = dobj.axis self.coord = _ensure_code(dobj.coord) # If we have a reduced dimensionality dataset, we want to avoid any # checks against it in the axes that are beyond its dimensionality. # This means that if we have a 2D dataset, *all* slices along z will # select all the zones. if self.axis >= dobj.ds.dimensionality: self.reduced_dimensionality = 1 else: self.reduced_dimensionality = 0 self.ax = (self.axis+1) % 3 self.ay = (self.axis+2) % 3 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_mask_regular_grid(self, gobj): cdef np.ndarray[np.uint8_t, ndim=3] mask cdef np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask cdef int i, j, k cdef int total = 0 cdef int this_level = 0 cdef int ind[3][2] cdef np.uint64_t icoord cdef np.int32_t level = gobj.Level _ensure_code(gobj.LeftEdge) _ensure_code(gobj.dds) if level < self.min_level or level > self.max_level: return None else: child_mask = gobj.child_mask mask = np.zeros(gobj.ActiveDimensions, dtype=np.uint8) if level == self.max_level: this_level = 1 for i in range(3): if i == self.axis: icoord = ( (self.coord - gobj.LeftEdge.d[i])/gobj.dds[i]) # clip coordinate to avoid seg fault below if we're # exactly at a grid boundary ind[i][0] = iclip( icoord, 0, gobj.ActiveDimensions[i]-1) ind[i][1] = ind[i][0] + 1 else: ind[i][0] = 0 ind[i][1] = gobj.ActiveDimensions[i] with nogil: for i in range(ind[0][0], ind[0][1]): for j in range(ind[1][0], ind[1][1]): for k in range(ind[2][0], ind[2][1]): if this_level == 1 or child_mask[i, j, k]: mask[i, j, k] = 1 total += 1 if total == 0: return None, 0 return mask.astype("bool"), total @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: if self.reduced_dimensionality == 1: return 1 if pos[self.axis] + 0.5*dds[self.axis] > self.coord \ and pos[self.axis] - 0.5*dds[self.axis] - grid_eps <= self.coord: return 1 return 0 cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: # two 0-volume constructs don't intersect return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: if self.reduced_dimensionality == 1: return 1 cdef np.float64_t dist = self.periodic_difference( pos[self.axis], self.coord, self.axis) if dist*dist < radius*radius: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: if self.reduced_dimensionality == 1: return 1 if left_edge[self.axis] - grid_eps <= self.coord < right_edge[self.axis]: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: if self.reduced_dimensionality == 1: return 2 if left_edge[self.axis] - grid_eps <= self.coord < right_edge[self.axis]: return 2 # a box with non-zero volume can't be inside a plane return 0 def _hash_vals(self): return (("axis", self.axis), ("coord", self.coord)) def _get_state_attnames(self): return ("axis", "coord", "ax", "ay", "reduced_dimensionality") slice_selector = SliceSelector yt-project-yt-f043ac8/yt/geometry/_selection_routines/sphere_selector.pxi000066400000000000000000000160041510711153200271260ustar00rootroot00000000000000cdef class SphereSelector(SelectorObject): cdef public np.float64_t radius cdef public np.float64_t radius2 cdef public np.float64_t center[3] cdef np.float64_t bbox[3][2] cdef public bint check_box[3] def __init__(self, dobj): for i in range(3): self.center[i] = _ensure_code(dobj.center[i]) self.radius = _ensure_code(dobj.radius) self.radius2 = self.radius * self.radius self.set_bbox(_ensure_code(dobj.center)) for i in range(3): if self.bbox[i][0] < dobj.ds.domain_left_edge[i]: self.check_box[i] = False elif self.bbox[i][1] > dobj.ds.domain_right_edge[i]: self.check_box[i] = False else: self.check_box[i] = True def set_bbox(self, center): for i in range(3): self.center[i] = center[i] self.bbox[i][0] = self.center[i] - self.radius self.bbox[i][1] = self.center[i] + self.radius @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil: # sphere center either inside cell or center of cell lies inside sphere if (pos[0] - 0.5*dds[0] <= self.center[0] <= pos[0]+0.5*dds[0] and pos[1] - 0.5*dds[1] <= self.center[1] <= pos[1]+0.5*dds[1] and pos[2] - 0.5*dds[2] <= self.center[2] <= pos[2]+0.5*dds[2]): return 1 return self.select_point(pos) # # langmm: added to allow sphere to intersect edge/corner of cell # cdef np.float64_t LE[3] # cdef np.float64_t RE[3] # cdef int i # for i in range(3): # LE[i] = pos[i] - 0.5*dds[i] # RE[i] = pos[i] + 0.5*dds[i] # return self.select_bbox(LE, RE) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_point(self, np.float64_t pos[3]) noexcept nogil: cdef int i cdef np.float64_t dist, dist2 = 0 for i in range(3): if self.check_box[i] and \ (pos[i] < self.bbox[i][0] or pos[i] > self.bbox[i][1]): return 0 dist = _periodic_dist(pos[i], self.center[i], self.domain_width[i], self.periodicity[i]) dist2 += dist*dist if dist2 > self.radius2: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil: cdef int i cdef np.float64_t dist, dist2 = 0 for i in range(3): dist = self.periodic_difference(pos[i], self.center[i], i) dist2 += dist*dist dist = self.radius+radius if dist2 <= dist*dist: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef np.float64_t box_center, relcenter, closest, dist, edge cdef int i if (left_edge[0] <= self.center[0] < right_edge[0] and left_edge[1] <= self.center[1] < right_edge[1] and left_edge[2] <= self.center[2] < right_edge[2]): return 1 for i in range(3): if not self.check_box[i]: continue if right_edge[i] < self.bbox[i][0] or \ left_edge[i] > self.bbox[i][1]: return 0 # http://www.gamedev.net/topic/335465-is-this-the-simplest-sphere-aabb-collision-test/ dist = 0 for i in range(3): # Early terminate box_center = (right_edge[i] + left_edge[i])/2.0 relcenter = self.periodic_difference(box_center, self.center[i], i) edge = right_edge[i] - left_edge[i] closest = relcenter - fclip(relcenter, -edge/2.0, edge/2.0) dist += closest*closest if dist > self.radius2: return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil: cdef np.float64_t box_center, relcenter, closest, farthest, cdist, fdist, edge cdef int i if (left_edge[0] <= self.center[0] <= right_edge[0] and left_edge[1] <= self.center[1] <= right_edge[1] and left_edge[2] <= self.center[2] <= right_edge[2]): fdist = 0 for i in range(3): edge = right_edge[i] - left_edge[i] box_center = (right_edge[i] + left_edge[i])/2.0 relcenter = self.periodic_difference( box_center, self.center[i], i) if relcenter >= 0: farthest = relcenter + edge/2.0 else: farthest = relcenter - edge/2.0 # farthest = relcenter + fclip(relcenter, -edge/2.0, edge/2.0) fdist += farthest*farthest if fdist >= self.radius2: return 2 # Box extends outside sphere return 1 # Box entirely inside sphere for i in range(3): if not self.check_box[i]: continue if right_edge[i] < self.bbox[i][0] or \ left_edge[i] > self.bbox[i][1]: return 0 # Box outside sphere bounding box # http://www.gamedev.net/topic/335465-is-this-the-simplest-sphere-aabb-collision-test/ cdist = 0 fdist = 0 for i in range(3): # Early terminate box_center = (right_edge[i] + left_edge[i])/2.0 relcenter = self.periodic_difference(box_center, self.center[i], i) edge = right_edge[i] - left_edge[i] closest = relcenter - fclip(relcenter, -edge/2.0, edge/2.0) if relcenter >= 0: farthest = relcenter + edge/2.0 else: farthest = relcenter - edge/2.0 #farthest = relcenter + fclip(relcenter, -edge/2.0, edge/2.0) cdist += closest*closest fdist += farthest*farthest if cdist > self.radius2: return 0 # Box does not overlap sphere if fdist < self.radius2: return 1 # Sphere extends to entirely contain box else: return 2 # Sphere only partially overlaps box def _hash_vals(self): return (("radius", self.radius), ("radius2", self.radius2), ("center[0]", self.center[0]), ("center[1]", self.center[1]), ("center[2]", self.center[2])) def _get_state_attnames(self): return ("radius", "radius2", "center", "check_box") def __setstate__(self, hashes): super(SphereSelector, self).__setstate__(hashes) self.set_bbox(self.center) sphere_selector = SphereSelector yt-project-yt-f043ac8/yt/geometry/api.py000066400000000000000000000001651510711153200202660ustar00rootroot00000000000000from .geometry_enum import Geometry from .geometry_handler import Index from .grid_geometry_handler import GridIndex yt-project-yt-f043ac8/yt/geometry/coordinates/000077500000000000000000000000001510711153200214535ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/coordinates/__init__.py000066400000000000000000000000001510711153200235520ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/coordinates/api.py000066400000000000000000000007311510711153200225770ustar00rootroot00000000000000from .cartesian_coordinates import CartesianCoordinateHandler from .coordinate_handler import CoordinateHandler from .cylindrical_coordinates import CylindricalCoordinateHandler from .geographic_coordinates import ( GeographicCoordinateHandler, InternalGeographicCoordinateHandler, ) from .polar_coordinates import PolarCoordinateHandler from .spec_cube_coordinates import SpectralCubeCoordinateHandler from .spherical_coordinates import SphericalCoordinateHandler yt-project-yt-f043ac8/yt/geometry/coordinates/cartesian_coordinates.py000066400000000000000000001035041510711153200263730ustar00rootroot00000000000000import numpy as np from yt.data_objects.index_subobjects.unstructured_mesh import SemiStructuredMesh from yt.funcs import mylog from yt.units._numpy_wrapper_functions import uconcatenate, uvstack from yt.units.yt_array import YTArray from yt.utilities.lib.pixelization_routines import ( interpolate_sph_grid_gather, normalization_2d_utility, pixelize_cartesian, pixelize_cartesian_nodal, pixelize_element_mesh, pixelize_element_mesh_line, pixelize_off_axis_cartesian, pixelize_sph_kernel_cutting, pixelize_sph_kernel_projection, pixelize_sph_kernel_slice, ) from yt.utilities.math_utils import compute_stddev_image from yt.utilities.nodal_data_utils import get_nodal_data from .coordinate_handler import ( CoordinateHandler, _get_coord_fields, _get_vert_fields, cartesian_to_cylindrical, cylindrical_to_cartesian, ) def _sample_ray(ray, npoints, field): """ Private function that uses a ray object for calculating the field values that will be the y-axis values in a LinePlot object. Parameters ---------- ray : YTOrthoRay, YTRay, or LightRay Ray object from which to sample field values npoints : int The number of points to sample field : str or field tuple The name of the field to sample """ start_point = ray.start_point end_point = ray.end_point sample_dr = (end_point - start_point) / (npoints - 1) sample_points = [np.arange(npoints) * sample_dr[i] for i in range(3)] sample_points = uvstack(sample_points).T + start_point ray_coordinates = uvstack([ray["index", d] for d in "xyz"]).T ray_dds = uvstack([ray["index", f"d{d}"] for d in "xyz"]).T ray_field = ray[field] field_values = ray.ds.arr(np.zeros(npoints), ray_field.units) for i, sample_point in enumerate(sample_points): ray_contains = (sample_point >= (ray_coordinates - ray_dds / 2)) & ( sample_point <= (ray_coordinates + ray_dds / 2) ) ray_contains = ray_contains.all(axis=-1) # use argmax to find the first nonzero index, sometimes there # are two indices if the sampling point happens to fall exactly at # a cell boundary field_values[i] = ray_field[np.argmax(ray_contains)] dr = np.sqrt((sample_dr**2).sum()) x = np.arange(npoints) / (npoints - 1) * (dr * npoints) return x, field_values def all_data(data, ptype, fields, kdtree=False): field_data = {} fields = set(fields) for field in fields: field_data[field] = [] for chunk in data.all_data().chunks([], "io"): for field in fields: field_data[field].append(chunk[ptype, field].in_base("code")) for field in fields: field_data[field] = uconcatenate(field_data[field]) if kdtree is True: kdtree = data.index.kdtree for field in fields: if len(field_data[field].shape) == 1: field_data[field] = field_data[field][kdtree.idx] else: field_data[field] = field_data[field][kdtree.idx, :] return field_data class CartesianCoordinateHandler(CoordinateHandler): name = "cartesian" _default_axis_order = ("x", "y", "z") def setup_fields(self, registry): for axi, ax in enumerate(self.axis_order): f1, f2 = _get_coord_fields(axi) registry.add_field( ("index", f"d{ax}"), sampling_type="cell", function=f1, display_field=False, units="code_length", ) registry.add_field( ("index", f"path_element_{ax}"), sampling_type="cell", function=f1, display_field=False, units="code_length", ) registry.add_field( ("index", f"{ax}"), sampling_type="cell", function=f2, display_field=False, units="code_length", ) f3 = _get_vert_fields(axi) registry.add_field( ("index", f"vertex_{ax}"), sampling_type="cell", function=f3, display_field=False, units="code_length", ) self._register_volume(registry) self._check_fields(registry) def _register_volume(self, registry): def _cell_volume(field, data): rv = data["index", "dx"].copy(order="K") rv *= data["index", "dy"] rv *= data["index", "dz"] return rv registry.add_field( ("index", "cell_volume"), sampling_type="cell", function=_cell_volume, display_field=False, units="code_length**3", ) registry.alias(("index", "volume"), ("index", "cell_volume")) def _check_fields(self, registry): registry.check_derived_fields( [ ("index", "dx"), ("index", "dy"), ("index", "dz"), ("index", "x"), ("index", "y"), ("index", "z"), ("index", "cell_volume"), ] ) def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=True, *, return_mask=False, ): """ Method for pixelizing datasets in preparation for two-dimensional image plots. Relies on several sampling routines written in cython """ index = data_source.ds.index if hasattr(index, "meshes") and not isinstance( index.meshes[0], SemiStructuredMesh ): ftype, fname = field if ftype == "all": mesh_id = 0 indices = np.concatenate( [mesh.connectivity_indices for mesh in index.mesh_union] ) else: mesh_id = int(ftype[-1]) - 1 indices = index.meshes[mesh_id].connectivity_indices coords = index.meshes[mesh_id].connectivity_coords offset = index.meshes[mesh_id]._index_offset ad = data_source.ds.all_data() field_data = ad[field] buff_size = size[0:dimension] + (1,) + size[dimension:] ax = data_source.axis xax = self.x_axis[ax] yax = self.y_axis[ax] c = np.float64(data_source.center[dimension].d) extents = np.zeros((3, 2)) extents[ax] = np.array([c, c]) extents[xax] = bounds[0:2] extents[yax] = bounds[2:4] # if this is an element field, promote to 2D here if len(field_data.shape) == 1: field_data = np.expand_dims(field_data, 1) # if this is a higher-order element, we demote to 1st order # here, for now. elif field_data.shape[1] == 27: # hexahedral mylog.warning( "High order elements not yet supported, dropping to 1st order." ) field_data = field_data[:, 0:8] indices = indices[:, 0:8] buff, mask = pixelize_element_mesh( coords, indices, buff_size, field_data, extents, index_offset=offset, return_mask=True, ) buff = np.squeeze(np.transpose(buff, (yax, xax, ax))) mask = np.squeeze(np.transpose(mask, (yax, xax, ax))) elif self.axis_id.get(dimension, dimension) is not None: buff, mask = self._ortho_pixelize( data_source, field, bounds, size, antialias, dimension, periodic ) else: buff, mask = self._oblique_pixelize( data_source, field, bounds, size, antialias ) if return_mask: assert mask is None or mask.dtype == bool return buff, mask else: return buff def pixelize_line(self, field, start_point, end_point, npoints): """ Method for sampling datasets along a line in preparation for one-dimensional line plots. For UnstructuredMesh, relies on a sampling routine written in cython """ if npoints < 2: raise ValueError( "Must have at least two sample points in order to draw a line plot." ) index = self.ds.index if hasattr(index, "meshes") and not isinstance( index.meshes[0], SemiStructuredMesh ): ftype, fname = field if ftype == "all": mesh_id = 0 indices = np.concatenate( [mesh.connectivity_indices for mesh in index.mesh_union] ) else: mesh_id = int(ftype[-1]) - 1 indices = index.meshes[mesh_id].connectivity_indices coords = index.meshes[mesh_id].connectivity_coords if coords.shape[1] != end_point.size != start_point.size: raise ValueError( "The coordinate dimension doesn't match the " "start and end point dimensions." ) offset = index.meshes[mesh_id]._index_offset ad = self.ds.all_data() field_data = ad[field] # if this is an element field, promote to 2D here if len(field_data.shape) == 1: field_data = np.expand_dims(field_data, 1) # if this is a higher-order element, we demote to 1st order # here, for now. elif field_data.shape[1] == 27: # hexahedral mylog.warning( "High order elements not yet supported, dropping to 1st order." ) field_data = field_data[:, 0:8] indices = indices[:, 0:8] arc_length, plot_values = pixelize_element_mesh_line( coords, indices, start_point, end_point, npoints, field_data, index_offset=offset, ) arc_length = YTArray(arc_length, start_point.units) plot_values = YTArray(plot_values, field_data.units) else: ray = self.ds.ray(start_point, end_point) arc_length, plot_values = _sample_ray(ray, npoints, field) return arc_length, plot_values def _ortho_pixelize( self, data_source, field, bounds, size, antialias, dim, periodic ): from yt.data_objects.construction_data_containers import YTParticleProj from yt.data_objects.selection_objects.slices import YTSlice from yt.frontends.sph.data_structures import ParticleDataset from yt.frontends.stream.data_structures import StreamParticlesDataset # We should be using fcoords field = data_source._determine_fields(field)[0] finfo = data_source.ds.field_info[field] # some coordinate handlers use only projection-plane periods, # others need all box periods. period2 = self.period[:2].copy() # dummy here period2[0] = self.period[self.x_axis[dim]] period2[1] = self.period[self.y_axis[dim]] period3 = self.period[:].copy() # dummy here period3[0] = self.period[self.x_axis[dim]] period3[1] = self.period[self.y_axis[dim]] zax = list({0, 1, 2} - {self.x_axis[dim], self.y_axis[dim]})[0] period3[2] = self.period[zax] if hasattr(period2, "in_units"): period2 = period2.in_units("code_length").d if hasattr(period3, "in_units"): period3 = period3.in_units("code_length").d buff = np.full((size[1], size[0]), np.nan, dtype="float64") particle_datasets = (ParticleDataset, StreamParticlesDataset) is_sph_field = finfo.is_sph_field finfo = self.ds._get_field_info(field) if np.any(finfo.nodal_flag): nodal_data = get_nodal_data(data_source, field) coord = data_source.coord.d mask = pixelize_cartesian_nodal( buff, data_source["px"], data_source["py"], data_source["pz"], data_source["pdx"], data_source["pdy"], data_source["pdz"], nodal_data, coord, bounds, int(antialias), period2, int(periodic), return_mask=True, ) elif isinstance(data_source.ds, particle_datasets) and is_sph_field: # SPH handling ptype = field[0] if ptype == "gas": ptype = data_source.ds._sph_ptypes[0] px_name = self.axis_name[self.x_axis[dim]] py_name = self.axis_name[self.y_axis[dim]] # need z coordinates for depth, # but name isn't saved in the handler -> use the 'other one' pz_name = list(set(self.axis_order) - {px_name, py_name})[0] # ignore default True periodic argument # (not actually supplied by a call from # FixedResolutionBuffer), and use the dataset periodicity # instead xa = self.x_axis[dim] ya = self.y_axis[dim] # axorder = data_source.ds.coordinates.axis_order za = list({0, 1, 2} - {xa, ya})[0] ds_periodic = data_source.ds.periodicity _periodic = np.array(ds_periodic) _periodic[0] = ds_periodic[xa] _periodic[1] = ds_periodic[ya] _periodic[2] = ds_periodic[za] ounits = data_source.ds.field_info[field].output_units bnds = data_source.ds.arr(bounds, "code_length").tolist() kernel_name = None if hasattr(data_source.ds, "kernel_name"): kernel_name = data_source.ds.kernel_name if kernel_name is None: kernel_name = "cubic" if isinstance(data_source, YTParticleProj): # projection weight = data_source.weight_field moment = data_source.moment le, re = data_source.data_source.get_bbox() # If we're not periodic, we need to clip to the boundary edges # or we get errors about extending off the edge of the region. # (depth/z range is handled by region setting) if not self.ds.periodicity[xa]: le[xa] = max(bounds[0], self.ds.domain_left_edge[xa]) re[xa] = min(bounds[1], self.ds.domain_right_edge[xa]) else: le[xa] = bounds[0] re[xa] = bounds[1] if not self.ds.periodicity[ya]: le[ya] = max(bounds[2], self.ds.domain_left_edge[ya]) re[ya] = min(bounds[3], self.ds.domain_right_edge[ya]) else: le[ya] = bounds[2] re[ya] = bounds[3] # We actually need to clip these proj_reg = data_source.ds.region( left_edge=le, right_edge=re, center=data_source.center, data_source=data_source.data_source, ) proj_reg.set_field_parameter("axis", data_source.axis) # need some z bounds for SPH projection # -> use source bounds bnds3 = bnds + [le[za], re[za]] buff = np.zeros(size, dtype="float64") mask_uint8 = np.zeros_like(buff, dtype="uint8") if weight is None: for chunk in proj_reg.chunks([], "io"): data_source._initialize_projected_units([field], chunk) pixelize_sph_kernel_projection( buff, mask_uint8, chunk[ptype, px_name].to("code_length"), chunk[ptype, py_name].to("code_length"), chunk[ptype, pz_name].to("code_length"), chunk[ptype, "smoothing_length"].to("code_length"), chunk[ptype, "mass"].to("code_mass"), chunk[ptype, "density"].to("code_density"), chunk[field].in_units(ounits), bnds3, _check_period=_periodic.astype("int"), period=period3, kernel_name=kernel_name, ) # We use code length here, but to get the path length right # we need to multiply by the conversion factor between # code length and the unit system's length unit default_path_length_unit = data_source.ds.unit_system["length"] dl_conv = data_source.ds.quan(1.0, "code_length").to( default_path_length_unit ) buff *= dl_conv.v # if there is a weight field, take two projections: # one of field*weight, the other of just weight, and divide them else: weight_buff = np.zeros(size, dtype="float64") buff = np.zeros(size, dtype="float64") mask_uint8 = np.zeros_like(buff, dtype="uint8") wounits = data_source.ds.field_info[weight].output_units for chunk in proj_reg.chunks([], "io"): data_source._initialize_projected_units([field], chunk) data_source._initialize_projected_units([weight], chunk) pixelize_sph_kernel_projection( buff, mask_uint8, chunk[ptype, px_name].to("code_length"), chunk[ptype, py_name].to("code_length"), chunk[ptype, pz_name].to("code_length"), chunk[ptype, "smoothing_length"].to("code_length"), chunk[ptype, "mass"].to("code_mass"), chunk[ptype, "density"].to("code_density"), chunk[field].in_units(ounits), bnds3, _check_period=_periodic.astype("int"), period=period3, weight_field=chunk[weight].in_units(wounits), kernel_name=kernel_name, ) mylog.info( "Making a fixed resolution buffer of (%s) %d by %d", weight, size[0], size[1], ) for chunk in proj_reg.chunks([], "io"): data_source._initialize_projected_units([weight], chunk) pixelize_sph_kernel_projection( weight_buff, mask_uint8, chunk[ptype, px_name].to("code_length"), chunk[ptype, py_name].to("code_length"), chunk[ptype, pz_name].to("code_length"), chunk[ptype, "smoothing_length"].to("code_length"), chunk[ptype, "mass"].to("code_mass"), chunk[ptype, "density"].to("code_density"), chunk[weight].in_units(wounits), bnds3, _check_period=_periodic.astype("int"), period=period3, kernel_name=kernel_name, ) normalization_2d_utility(buff, weight_buff) if moment == 2: buff2 = np.zeros(size, dtype="float64") for chunk in proj_reg.chunks([], "io"): data_source._initialize_projected_units([field], chunk) data_source._initialize_projected_units([weight], chunk) pixelize_sph_kernel_projection( buff2, mask_uint8, chunk[ptype, px_name].to("code_length"), chunk[ptype, py_name].to("code_length"), chunk[ptype, pz_name].to("code_length"), chunk[ptype, "smoothing_length"].to("code_length"), chunk[ptype, "mass"].to("code_mass"), chunk[ptype, "density"].to("code_density"), chunk[field].in_units(ounits) ** 2, bnds3, _check_period=_periodic.astype("int"), period=period3, weight_field=chunk[weight].in_units(wounits), kernel_name=kernel_name, ) normalization_2d_utility(buff2, weight_buff) buff = compute_stddev_image(buff2, buff) mask = mask_uint8.astype("bool") elif isinstance(data_source, YTSlice): smoothing_style = getattr(self.ds, "sph_smoothing_style", "scatter") normalize = getattr(self.ds, "use_sph_normalization", True) if smoothing_style == "scatter": buff = np.zeros(size, dtype="float64") mask_uint8 = np.zeros_like(buff, dtype="uint8") if normalize: buff_den = np.zeros(size, dtype="float64") for chunk in data_source.chunks([], "io"): hsmlname = "smoothing_length" pixelize_sph_kernel_slice( buff, mask_uint8, chunk[ptype, px_name].to("code_length").v, chunk[ptype, py_name].to("code_length").v, chunk[ptype, pz_name].to("code_length").v, chunk[ptype, hsmlname].to("code_length").v, chunk[ptype, "mass"].to("code_mass").v, chunk[ptype, "density"].to("code_density").v, chunk[field].in_units(ounits).v, bnds, data_source.coord.to("code_length").v, _check_period=_periodic.astype("int"), period=period3, kernel_name=kernel_name, ) if normalize: pixelize_sph_kernel_slice( buff_den, mask_uint8, chunk[ptype, px_name].to("code_length").v, chunk[ptype, py_name].to("code_length").v, chunk[ptype, pz_name].to("code_length").v, chunk[ptype, hsmlname].to("code_length").v, chunk[ptype, "mass"].to("code_mass").v, chunk[ptype, "density"].to("code_density").v, np.ones(chunk[ptype, "density"].shape[0]), bnds, data_source.coord.to("code_length").v, _check_period=_periodic.astype("int"), period=period3, kernel_name=kernel_name, ) if normalize: normalization_2d_utility(buff, buff_den) mask = mask_uint8.astype("bool", copy=False) if smoothing_style == "gather": # Here we find out which axis are going to be the "x" and # "y" axis for the actual visualisation and then we set the # buffer size and bounds to match. The z axis of the plot # is the axis we slice over and the buffer will be of size 1 # in that dimension x, y, z = self.x_axis[dim], self.y_axis[dim], dim buff_size = np.zeros(3, dtype="int64") buff_size[x] = size[0] buff_size[y] = size[1] buff_size[z] = 1 buff_bounds = np.zeros(6, dtype="float64") buff_bounds[2 * x : 2 * x + 2] = bounds[0:2] buff_bounds[2 * y : 2 * y + 2] = bounds[2:4] buff_bounds[2 * z] = data_source.coord buff_bounds[2 * z + 1] = data_source.coord # then we do the interpolation buff_temp = np.zeros(buff_size, dtype="float64") fields_to_get = [ "particle_position", "density", "mass", "smoothing_length", field[1], ] all_fields = all_data(self.ds, ptype, fields_to_get, kdtree=True) num_neighbors = getattr(self.ds, "num_neighbors", 32) mask_temp = interpolate_sph_grid_gather( buff_temp, all_fields["particle_position"].to("code_length"), buff_bounds, all_fields["smoothing_length"].to("code_length"), all_fields["mass"].to("code_mass"), all_fields["density"].to("code_density"), all_fields[field[1]].in_units(ounits), self.ds.index.kdtree, num_neigh=num_neighbors, use_normalization=normalize, return_mask=True, ) # We swap the axes back so the axis which was sliced over # is the last axis, as this is the "z" axis of the plots. if z != 2: buff_temp = buff_temp.swapaxes(2, z) mask_temp = mask_temp.swapaxes(2, z) if x == 2: x = z else: y = z buff = buff_temp[:, :, 0] mask = mask_temp[:, :, 0] # Then we just transpose if the buffer x and y are # different than the plot x and y if y < x: buff = buff.transpose() mask = mask.transpose() else: raise NotImplementedError( "A pixelization routine has not been implemented for " f"{type(data_source)} data objects" ) buff = buff.transpose() mask = mask.transpose() else: mask = pixelize_cartesian( buff, data_source["px"], data_source["py"], data_source["pdx"], data_source["pdy"], data_source[field], bounds, int(antialias), period2, int(periodic), return_mask=True, ) assert mask is None or mask.dtype == bool return buff, mask def _oblique_pixelize(self, data_source, field, bounds, size, antialias): from yt.data_objects.selection_objects.slices import YTCuttingPlane from yt.frontends.sph.data_structures import ParticleDataset from yt.frontends.stream.data_structures import StreamParticlesDataset from yt.frontends.ytdata.data_structures import YTSpatialPlotDataset # Determine what sort of data we're dealing with # -> what backend to use # copied from the _ortho_pixelize method field = data_source._determine_fields(field)[0] _finfo = data_source.ds.field_info[field] is_sph_field = _finfo.is_sph_field particle_datasets = (ParticleDataset, StreamParticlesDataset) # finfo = self.ds._get_field_info(field) # SPH data # only for slices: a function in off_axis_projection.py # handles projections if ( isinstance(data_source.ds, particle_datasets) and is_sph_field and isinstance(data_source, YTCuttingPlane) ): normalize = getattr(self.ds, "use_sph_normalization", True) le = data_source.ds.domain_left_edge.to("code_length") re = data_source.ds.domain_right_edge.to("code_length") boxbounds = np.array([le[0], re[0], le[1], re[1], le[2], re[2]]) periodic = data_source.ds.periodicity ptype = field[0] if ptype == "gas": ptype = data_source.ds._sph_ptypes[0] axorder = data_source.ds.coordinates.axis_order ounits = data_source.ds.field_info[field].output_units # input bounds are in code length units already widthxy = np.array((bounds[1] - bounds[0], bounds[3] - bounds[2])) kernel_name = None if hasattr(data_source.ds, "kernel_name"): kernel_name = data_source.ds.kernel_name if kernel_name is None: kernel_name = "cubic" # data_source should be a YTCuttingPlane object # dimensionless unyt normal/north # -> numpy array cython can deal with normal_vector = data_source.normal.v north_vector = data_source._y_vec.v center = data_source.center.to("code_length") buff = np.zeros(size, dtype="float64") mask_uint8 = np.zeros_like(buff, dtype="uint8") if normalize: buff_den = np.zeros(size, dtype="float64") for chunk in data_source.chunks([], "io"): pixelize_sph_kernel_cutting( buff, mask_uint8, chunk[ptype, axorder[0]].to("code_length").v, chunk[ptype, axorder[1]].to("code_length").v, chunk[ptype, axorder[2]].to("code_length").v, chunk[ptype, "smoothing_length"].to("code_length").v, chunk[ptype, "mass"].to("code_mass"), chunk[ptype, "density"].to("code_density"), chunk[field].in_units(ounits), center, widthxy, normal_vector, north_vector, boxbounds, periodic, kernel_name=kernel_name, check_period=1, ) if normalize: pixelize_sph_kernel_cutting( buff_den, mask_uint8, chunk[ptype, axorder[0]].to("code_length"), chunk[ptype, axorder[1]].to("code_length"), chunk[ptype, axorder[2]].to("code_length"), chunk[ptype, "smoothing_length"].to("code_length"), chunk[ptype, "mass"].to("code_mass"), chunk[ptype, "density"].to("code_density"), np.ones(chunk[ptype, "density"].shape[0]), center, widthxy, normal_vector, north_vector, boxbounds, periodic, kernel_name=kernel_name, check_period=1, ) if normalize: normalization_2d_utility(buff, buff_den) mask = mask_uint8.astype("bool", copy=False) # swap axes for image plotting mask = mask.swapaxes(0, 1) buff = buff.swapaxes(0, 1) # whatever other data this code could handle before the # SPH option was added else: indices = np.argsort(data_source["pdx"])[::-1].astype("int64", copy=False) buff = np.full((size[1], size[0]), np.nan, dtype="float64") ftype = "index" if isinstance(data_source.ds, YTSpatialPlotDataset): ftype = "gas" mask = pixelize_off_axis_cartesian( buff, data_source[ftype, "x"], data_source[ftype, "y"], data_source[ftype, "z"], data_source["px"], data_source["py"], data_source["pdx"], data_source["pdy"], data_source["pdz"], data_source.center, data_source._inv_mat, indices, data_source[field], bounds, ) return buff, mask def convert_from_cartesian(self, coord): return coord def convert_to_cartesian(self, coord): return coord def convert_to_cylindrical(self, coord): center = self.ds.domain_center return cartesian_to_cylindrical(coord, center) def convert_from_cylindrical(self, coord): center = self.ds.domain_center return cylindrical_to_cartesian(coord, center) def convert_to_spherical(self, coord): raise NotImplementedError def convert_from_spherical(self, coord): raise NotImplementedError _x_pairs = (("x", "y"), ("y", "z"), ("z", "x")) _y_pairs = (("x", "z"), ("y", "x"), ("z", "y")) @property def period(self): return self.ds.domain_width yt-project-yt-f043ac8/yt/geometry/coordinates/coordinate_handler.py000066400000000000000000000261201510711153200256520ustar00rootroot00000000000000import abc import weakref from functools import cached_property from numbers import Number from typing import Any, Literal, overload import numpy as np from yt._typing import AxisOrder from yt.funcs import fix_unitary, is_sequence, parse_center_array, validate_width_tuple from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.exceptions import YTCoordinateNotImplemented, YTInvalidWidthError def _unknown_coord(field, data): raise YTCoordinateNotImplemented def _get_coord_fields(axi, units="code_length"): def _dds(field, data): rv = data.ds.arr(data.fwidth[..., axi].copy(), units) return data._reshape_vals(rv) def _coords(field, data): rv = data.ds.arr(data.fcoords[..., axi].copy(), units) return data._reshape_vals(rv) return _dds, _coords def _get_vert_fields(axi, units="code_length"): def _vert(field, data): rv = data.ds.arr(data.fcoords_vertex[..., axi].copy(), units) return rv return _vert def _setup_dummy_cartesian_coords_and_widths(registry, axes: tuple[str]): for ax in axes: registry.add_field( ("index", f"d{ax}"), sampling_type="cell", function=_unknown_coord ) registry.add_field(("index", ax), sampling_type="cell", function=_unknown_coord) def _setup_polar_coordinates(registry, axis_id): f1, f2 = _get_coord_fields(axis_id["r"]) registry.add_field( ("index", "dr"), sampling_type="cell", function=f1, display_field=False, units="code_length", ) registry.add_field( ("index", "r"), sampling_type="cell", function=f2, display_field=False, units="code_length", ) f1, f2 = _get_coord_fields(axis_id["theta"], "dimensionless") registry.add_field( ("index", "dtheta"), sampling_type="cell", function=f1, display_field=False, units="dimensionless", ) registry.add_field( ("index", "theta"), sampling_type="cell", function=f2, display_field=False, units="dimensionless", ) def _path_r(field, data): return data["index", "dr"] registry.add_field( ("index", "path_element_r"), sampling_type="cell", function=_path_r, units="code_length", ) def _path_theta(field, data): # Note: this already assumes cell-centered return data["index", "r"] * data["index", "dtheta"] registry.add_field( ("index", "path_element_theta"), sampling_type="cell", function=_path_theta, units="code_length", ) def validate_sequence_width(width, ds, unit=None): if isinstance(width[0], tuple) and isinstance(width[1], tuple): validate_width_tuple(width[0]) validate_width_tuple(width[1]) return ( ds.quan(width[0][0], fix_unitary(width[0][1])), ds.quan(width[1][0], fix_unitary(width[1][1])), ) elif isinstance(width[0], Number) and isinstance(width[1], Number): return (ds.quan(width[0], "code_length"), ds.quan(width[1], "code_length")) elif isinstance(width[0], YTQuantity) and isinstance(width[1], YTQuantity): return (ds.quan(width[0]), ds.quan(width[1])) else: validate_width_tuple(width) # If width and unit are both valid width tuples, we # assume width controls x and unit controls y try: validate_width_tuple(unit) return ( ds.quan(width[0], fix_unitary(width[1])), ds.quan(unit[0], fix_unitary(unit[1])), ) except YTInvalidWidthError: return ( ds.quan(width[0], fix_unitary(width[1])), ds.quan(width[0], fix_unitary(width[1])), ) class CoordinateHandler(abc.ABC): name: str _default_axis_order: AxisOrder def __init__(self, ds, ordering: AxisOrder | None = None): self.ds = weakref.proxy(ds) if ordering is not None: self.axis_order = ordering else: self.axis_order = self._default_axis_order @abc.abstractmethod def setup_fields(self): # This should return field definitions for x, y, z, r, theta, phi pass @overload def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=True, *, return_mask: Literal[False], ) -> "np.ndarray[Any, np.dtype[np.float64]]": ... @overload def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=True, *, return_mask: Literal[True], ) -> tuple[ "np.ndarray[Any, np.dtype[np.float64]]", "np.ndarray[Any, np.dtype[np.bool_]]" ]: ... @abc.abstractmethod def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=True, *, return_mask=False, ): # This should *actually* be a pixelize call, not just returning the # pixelizer pass @abc.abstractmethod def pixelize_line(self, field, start_point, end_point, npoints): pass def distance(self, start, end): p1 = self.convert_to_cartesian(start) p2 = self.convert_to_cartesian(end) return np.sqrt(((p1 - p2) ** 2.0).sum()) @abc.abstractmethod def convert_from_cartesian(self, coord): pass @abc.abstractmethod def convert_to_cartesian(self, coord): pass @abc.abstractmethod def convert_to_cylindrical(self, coord): pass @abc.abstractmethod def convert_from_cylindrical(self, coord): pass @abc.abstractmethod def convert_to_spherical(self, coord): pass @abc.abstractmethod def convert_from_spherical(self, coord): pass @cached_property def data_projection(self): return {ax: None for ax in self.axis_order} @cached_property def data_transform(self): return {ax: None for ax in self.axis_order} @cached_property def axis_name(self): an = {} for axi, ax in enumerate(self.axis_order): an[axi] = ax an[ax] = ax an[ax.capitalize()] = ax return an @cached_property def axis_id(self): ai = {} for axi, ax in enumerate(self.axis_order): ai[ax] = ai[axi] = axi return ai @property def image_axis_name(self): rv = {} for i in range(3): rv[i] = (self.axis_name[self.x_axis[i]], self.axis_name[self.y_axis[i]]) rv[self.axis_name[i]] = rv[i] rv[self.axis_name[i].capitalize()] = rv[i] return rv @cached_property def x_axis(self): ai = self.axis_id xa = {} for a1, a2 in self._x_pairs: xa[a1] = xa[ai[a1]] = ai[a2] return xa @cached_property def y_axis(self): ai = self.axis_id ya = {} for a1, a2 in self._y_pairs: ya[a1] = ya[ai[a1]] = ai[a2] return ya @property @abc.abstractmethod def period(self): pass def sanitize_depth(self, depth): if is_sequence(depth): validate_width_tuple(depth) depth = (self.ds.quan(depth[0], fix_unitary(depth[1])),) elif isinstance(depth, Number): depth = ( self.ds.quan(depth, "code_length", registry=self.ds.unit_registry), ) elif isinstance(depth, YTQuantity): depth = (depth,) else: raise YTInvalidWidthError(depth) return depth def sanitize_width(self, axis, width, depth): if width is None: # initialize the index if it is not already initialized self.ds.index # Default to code units if not is_sequence(axis): xax = self.x_axis[axis] yax = self.y_axis[axis] w = self.ds.domain_width[np.array([xax, yax])] else: # axis is actually the normal vector # for an off-axis data object. mi = np.argmin(self.ds.domain_width) w = self.ds.domain_width[np.array((mi, mi))] width = (w[0], w[1]) elif is_sequence(width): width = validate_sequence_width(width, self.ds) elif isinstance(width, YTQuantity): width = (width, width) elif isinstance(width, Number): width = ( self.ds.quan(width, "code_length"), self.ds.quan(width, "code_length"), ) else: raise YTInvalidWidthError(width) if depth is not None: depth = self.sanitize_depth(depth) return width + depth return width def sanitize_center(self, center, axis): center = parse_center_array(center, ds=self.ds, axis=axis) # This has to return both a center and a display_center display_center = self.convert_to_cartesian(center) return center, display_center def cartesian_to_cylindrical(coord, center=(0, 0, 0)): c2 = np.zeros_like(coord) if not isinstance(center, YTArray): center = center * coord.uq c2[..., 0] = ( (coord[..., 0] - center[0]) ** 2.0 + (coord[..., 1] - center[1]) ** 2.0 ) ** 0.5 c2[..., 1] = coord[..., 2] # rzt c2[..., 2] = np.arctan2(coord[..., 1] - center[1], coord[..., 0] - center[0]) return c2 def cylindrical_to_cartesian(coord, center=(0, 0, 0)): c2 = np.zeros_like(coord) if not isinstance(center, YTArray): center = center * coord.uq c2[..., 0] = np.cos(coord[..., 0]) * coord[..., 1] + center[0] c2[..., 1] = np.sin(coord[..., 0]) * coord[..., 1] + center[1] c2[..., 2] = coord[..., 2] return c2 def _get_polar_bounds(self: CoordinateHandler, axes: tuple[str, str]): # a small helper function that is needed by two unrelated classes ri = self.axis_id[axes[0]] pi = self.axis_id[axes[1]] rmin = self.ds.domain_left_edge[ri] rmax = self.ds.domain_right_edge[ri] phimin = self.ds.domain_left_edge[pi] phimax = self.ds.domain_right_edge[pi] corners = [ (rmin, phimin), (rmin, phimax), (rmax, phimin), (rmax, phimax), ] def to_polar_plane(r, phi): x = r * np.cos(phi) y = r * np.sin(phi) return x, y conic_corner_coords = [to_polar_plane(*corner) for corner in corners] phimin = phimin.d phimax = phimax.d if phimin <= np.pi <= phimax: xxmin = -rmax else: xxmin = min(xx for xx, yy in conic_corner_coords) if phimin <= 0 <= phimax: xxmax = rmax else: xxmax = max(xx for xx, yy in conic_corner_coords) if phimin <= 3 * np.pi / 2 <= phimax: yymin = -rmax else: yymin = min(yy for xx, yy in conic_corner_coords) if phimin <= np.pi / 2 <= phimax: yymax = rmax else: yymax = max(yy for xx, yy in conic_corner_coords) return xxmin, xxmax, yymin, yymax yt-project-yt-f043ac8/yt/geometry/coordinates/cylindrical_coordinates.py000066400000000000000000000204711510711153200267200ustar00rootroot00000000000000from functools import cached_property import numpy as np from yt.utilities.lib.pixelization_routines import pixelize_cartesian, pixelize_cylinder from .coordinate_handler import ( CoordinateHandler, _get_coord_fields, _get_polar_bounds, _setup_dummy_cartesian_coords_and_widths, _setup_polar_coordinates, cartesian_to_cylindrical, cylindrical_to_cartesian, ) # # Cylindrical fields # class CylindricalCoordinateHandler(CoordinateHandler): name = "cylindrical" _default_axis_order = ("r", "z", "theta") def __init__(self, ds, ordering=None): super().__init__(ds, ordering) self.image_units = {} self.image_units[self.axis_id["r"]] = ("rad", None) self.image_units[self.axis_id["theta"]] = (None, None) self.image_units[self.axis_id["z"]] = (None, None) def setup_fields(self, registry): # Missing implementation for x and y coordinates. _setup_dummy_cartesian_coords_and_widths(registry, axes=("x", "y")) _setup_polar_coordinates(registry, self.axis_id) f1, f2 = _get_coord_fields(self.axis_id["z"]) registry.add_field( ("index", "dz"), sampling_type="cell", function=f1, display_field=False, units="code_length", ) registry.add_field( ("index", "z"), sampling_type="cell", function=f2, display_field=False, units="code_length", ) def _CylindricalVolume(field, data): r = data["index", "r"] dr = data["index", "dr"] vol = 0.5 * ((r + 0.5 * dr) ** 2 - (r - 0.5 * dr) ** 2) vol *= data["index", "dtheta"] vol *= data["index", "dz"] return vol registry.add_field( ("index", "cell_volume"), sampling_type="cell", function=_CylindricalVolume, units="code_length**3", ) registry.alias(("index", "volume"), ("index", "cell_volume")) def _path_z(field, data): return data["index", "dz"] registry.add_field( ("index", "path_element_z"), sampling_type="cell", function=_path_z, units="code_length", ) def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=False, *, return_mask=False, ): # Note that above, we set periodic by default to be *false*. This is # because our pixelizers, at present, do not handle periodicity # correctly, and if you change the "width" of a cylindrical plot, it # double-counts in the edge buffers. See, for instance, issue 1669. ax_name = self.axis_name[dimension] if ax_name in ("r", "theta"): buff, mask = self._ortho_pixelize( data_source, field, bounds, size, antialias, dimension, periodic ) elif ax_name == "z": # This is admittedly a very hacky way to resolve a bug # it's very likely that the *right* fix would have to # be applied upstream of this function, *but* this case # has never worked properly so maybe it's still preferable to # not having a solution ? # see https://github.com/yt-project/yt/pull/3533 bounds = (*bounds[2:4], *bounds[:2]) buff, mask = self._cyl_pixelize(data_source, field, bounds, size, antialias) else: # Pixelizing along a cylindrical surface is a bit tricky raise NotImplementedError if return_mask: assert mask is None or mask.dtype == bool return buff, mask else: return buff def pixelize_line(self, field, start_point, end_point, npoints): raise NotImplementedError def _ortho_pixelize( self, data_source, field, bounds, size, antialias, dim, periodic ): period = self.period[:2].copy() # dummy here period[0] = self.period[self.x_axis[dim]] period[1] = self.period[self.y_axis[dim]] if hasattr(period, "in_units"): period = period.in_units("code_length").d buff = np.full(size, np.nan, dtype="float64") mask = pixelize_cartesian( buff, data_source["px"], data_source["py"], data_source["pdx"], data_source["pdy"], data_source[field], bounds, int(antialias), period, int(periodic), ) return buff, mask def _cyl_pixelize(self, data_source, field, bounds, size, antialias): buff = np.full((size[1], size[0]), np.nan, dtype="f8") mask = pixelize_cylinder( buff, data_source["px"], data_source["pdx"], data_source["py"], data_source["pdy"], data_source[field], bounds, return_mask=True, ) return buff, mask _x_pairs = (("r", "theta"), ("z", "r"), ("theta", "r")) _y_pairs = (("r", "z"), ("z", "theta"), ("theta", "z")) _image_axis_name = None @property def image_axis_name(self): if self._image_axis_name is not None: return self._image_axis_name # This is the x and y axes labels that get displayed. For # non-Cartesian coordinates, we usually want to override these for # Cartesian coordinates, since we transform them. rv = { self.axis_id["r"]: ("\\theta", "z"), self.axis_id["z"]: ("x", "y"), self.axis_id["theta"]: ("r", "z"), } for i in list(rv.keys()): rv[self.axis_name[i]] = rv[i] rv[self.axis_name[i].upper()] = rv[i] self._image_axis_name = rv return rv def convert_from_cartesian(self, coord): return cartesian_to_cylindrical(coord) def convert_to_cartesian(self, coord): return cylindrical_to_cartesian(coord) def convert_to_cylindrical(self, coord): return coord def convert_from_cylindrical(self, coord): return coord def convert_to_spherical(self, coord): raise NotImplementedError def convert_from_spherical(self, coord): raise NotImplementedError @property def period(self): return np.array([0.0, 0.0, 2.0 * np.pi]) @cached_property def _polar_bounds(self): return _get_polar_bounds(self, axes=("r", "theta")) def sanitize_center(self, center, axis): center, display_center = super().sanitize_center(center, axis) display_center = [ 0.0 * display_center[0], 0.0 * display_center[1], 0.0 * display_center[2], ] ax_name = self.axis_name[axis] r_ax = self.axis_id["r"] theta_ax = self.axis_id["theta"] z_ax = self.axis_id["z"] if ax_name == "r": display_center[theta_ax] = self.ds.domain_center[theta_ax] display_center[z_ax] = self.ds.domain_center[z_ax] elif ax_name == "theta": # use existing center value for idx in (r_ax, z_ax): display_center[idx] = center[idx] elif ax_name == "z": xxmin, xxmax, yymin, yymax = self._polar_bounds xc = (xxmin + xxmax) / 2 yc = (yymin + yymax) / 2 display_center = (xc, yc, 0 * xc) return center, display_center def sanitize_width(self, axis, width, depth): name = self.axis_name[axis] r_ax, theta_ax, z_ax = ( self.ds.coordinates.axis_id[ax] for ax in ("r", "theta", "z") ) if width is not None: width = super().sanitize_width(axis, width, depth) # Note: regardless of axes, these are set up to give consistent plots # when plotted, which is not strictly a "right hand rule" for axes. elif name == "r": # soup can label width = [self.ds.domain_width[theta_ax], self.ds.domain_width[z_ax]] elif name == "theta": width = [self.ds.domain_width[r_ax], self.ds.domain_width[z_ax]] elif name == "z": xxmin, xxmax, yymin, yymax = self._polar_bounds xw = xxmax - xxmin yw = yymax - yymin width = [xw, yw] return width yt-project-yt-f043ac8/yt/geometry/coordinates/geographic_coordinates.py000066400000000000000000000501661510711153200265370ustar00rootroot00000000000000import numpy as np import unyt from yt.utilities.lib.pixelization_routines import pixelize_cartesian, pixelize_cylinder from .coordinate_handler import ( CoordinateHandler, _get_coord_fields, _setup_dummy_cartesian_coords_and_widths, ) class GeographicCoordinateHandler(CoordinateHandler): radial_axis = "altitude" name = "geographic" def __init__(self, ds, ordering=None): if ordering is None: ordering = ("latitude", "longitude", self.radial_axis) super().__init__(ds, ordering) self.image_units = {} self.image_units[self.axis_id["latitude"]] = (None, None) self.image_units[self.axis_id["longitude"]] = (None, None) self.image_units[self.axis_id[self.radial_axis]] = ("deg", "deg") def setup_fields(self, registry): # Missing implementation for x, y and z coordinates. _setup_dummy_cartesian_coords_and_widths(registry, axes=("x", "y", "z")) f1, f2 = _get_coord_fields(self.axis_id["latitude"], "") registry.add_field( ("index", "dlatitude"), sampling_type="cell", function=f1, display_field=False, units="", ) registry.add_field( ("index", "latitude"), sampling_type="cell", function=f2, display_field=False, units="", ) f1, f2 = _get_coord_fields(self.axis_id["longitude"], "") registry.add_field( ("index", "dlongitude"), sampling_type="cell", function=f1, display_field=False, units="", ) registry.add_field( ("index", "longitude"), sampling_type="cell", function=f2, display_field=False, units="", ) f1, f2 = _get_coord_fields(self.axis_id[self.radial_axis]) registry.add_field( ("index", f"d{self.radial_axis}"), sampling_type="cell", function=f1, display_field=False, units="code_length", ) registry.add_field( ("index", self.radial_axis), sampling_type="cell", function=f2, display_field=False, units="code_length", ) def _SphericalVolume(field, data): # We can use the transformed coordinates here. # Here we compute the spherical volume element exactly r = data["index", "r"] dr = data["index", "dr"] theta = data["index", "theta"] dtheta = data["index", "dtheta"] vol = ((r + 0.5 * dr) ** 3 - (r - 0.5 * dr) ** 3) / 3.0 vol *= np.cos(theta - 0.5 * dtheta) - np.cos(theta + 0.5 * dtheta) vol *= data["index", "dphi"] return vol registry.add_field( ("index", "cell_volume"), sampling_type="cell", function=_SphericalVolume, units="code_length**3", ) registry.alias(("index", "volume"), ("index", "cell_volume")) def _path_radial_axis(field, data): return data["index", f"d{self.radial_axis}"] registry.add_field( ("index", f"path_element_{self.radial_axis}"), sampling_type="cell", function=_path_radial_axis, units="code_length", ) def _path_latitude(field, data): # We use r here explicitly return data["index", "r"] * data["index", "dlatitude"] * np.pi / 180.0 registry.add_field( ("index", "path_element_latitude"), sampling_type="cell", function=_path_latitude, units="code_length", ) def _path_longitude(field, data): # We use r here explicitly return ( data["index", "r"] * data["index", "dlongitude"] * np.pi / 180.0 * np.sin((90 - data["index", "latitude"]) * np.pi / 180.0) ) registry.add_field( ("index", "path_element_longitude"), sampling_type="cell", function=_path_longitude, units="code_length", ) def _latitude_to_theta(field, data): # latitude runs from -90 to 90 # theta = 0 at +90 deg, np.pi at -90 return (90.0 - data["index", "latitude"]) * np.pi / 180.0 registry.add_field( ("index", "theta"), sampling_type="cell", function=_latitude_to_theta, units="", ) def _dlatitude_to_dtheta(field, data): return data["index", "dlatitude"] * np.pi / 180.0 registry.add_field( ("index", "dtheta"), sampling_type="cell", function=_dlatitude_to_dtheta, units="", ) def _longitude_to_phi(field, data): # longitude runs from -180 to 180 lonvals = data[("index", "longitude")] neglons = lonvals < 0.0 if np.any(neglons): lonvals[neglons] = lonvals[neglons] + 360.0 return lonvals * np.pi / 180.0 registry.add_field( ("index", "phi"), sampling_type="cell", function=_longitude_to_phi, units="" ) def _dlongitude_to_dphi(field, data): return data["index", "dlongitude"] * np.pi / 180.0 registry.add_field( ("index", "dphi"), sampling_type="cell", function=_dlongitude_to_dphi, units="", ) self._setup_radial_fields(registry) def _setup_radial_fields(self, registry): # This stays here because we don't want to risk the field detector not # properly getting the data_source, etc. def _altitude_to_radius(field, data): surface_height = data.get_field_parameter("surface_height") if surface_height is None: if hasattr(data.ds, "surface_height"): surface_height = data.ds.surface_height else: surface_height = data.ds.quan(0.0, "code_length") return data["index", "altitude"] + surface_height registry.add_field( ("index", "r"), sampling_type="cell", function=_altitude_to_radius, units="code_length", ) registry.alias(("index", "dr"), ("index", "daltitude")) def _retrieve_radial_offset(self, data_source=None): # This returns the factor by which the radial field is multiplied and # the scalar its offset by. Typically the "factor" will *only* be # either 1.0 or -1.0. The order will be factor * r + offset. # Altitude is the radius from the central zone minus the radius of the # surface. Depth to radius is negative value of depth plus the # outermost radius. surface_height = None if data_source is not None: surface_height = data_source.get_field_parameter("surface_height") if surface_height is None: if hasattr(self.ds, "surface_height"): surface_height = self.ds.surface_height else: surface_height = self.ds.quan(0.0, "code_length") return surface_height, 1.0 def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=True, *, return_mask=False, ): if self.axis_name[dimension] in ("latitude", "longitude"): buff, mask = self._cyl_pixelize( data_source, field, bounds, size, antialias, dimension ) elif self.axis_name[dimension] == self.radial_axis: buff, mask = self._ortho_pixelize( data_source, field, bounds, size, antialias, dimension, periodic ) else: raise NotImplementedError if return_mask: assert mask is None or mask.dtype == bool return buff, mask else: return buff def pixelize_line(self, field, start_point, end_point, npoints): raise NotImplementedError def _ortho_pixelize( self, data_source, field, bounds, size, antialias, dimension, periodic ): period = self.period[:2].copy() period[0] = self.period[self.x_axis[dimension]] period[1] = self.period[self.y_axis[dimension]] if hasattr(period, "in_units"): period = period.in_units("code_length").d # For a radial axis, px will correspond to longitude and py will # correspond to latitude. px = data_source["px"] pdx = data_source["pdx"] py = data_source["py"] pdy = data_source["pdy"] buff = np.full((size[1], size[0]), np.nan, dtype="float64") mask = pixelize_cartesian( buff, px, py, pdx, pdy, data_source[field], bounds, int(antialias), period, int(periodic), ) return buff, mask def _cyl_pixelize(self, data_source, field, bounds, size, antialias, dimension): offset, factor = self._retrieve_radial_offset(data_source) r = factor * data_source["py"] + offset # Because of the axis-ordering, dimensions 0 and 1 both have r as py # and the angular coordinate as px. But we need to figure out how to # convert our coordinate back to an actual angle, based on which # dimension we're in. pdx = data_source["pdx"].d * np.pi / 180 if self.axis_name[self.x_axis[dimension]] == "latitude": px = (data_source["px"].d + 90) * np.pi / 180 do_transpose = True elif self.axis_name[self.x_axis[dimension]] == "longitude": px = (data_source["px"].d + 180) * np.pi / 180 do_transpose = False else: # We should never get here! raise NotImplementedError buff = np.full((size[1], size[0]), np.nan, dtype="f8") mask = pixelize_cylinder( buff, r, data_source["pdy"], px, pdx, data_source[field], bounds, return_mask=True, ) if do_transpose: buff = buff.transpose() mask = mask.transpose() return buff, mask def convert_from_cartesian(self, coord): raise NotImplementedError def convert_to_cartesian(self, coord): offset, factor = self._retrieve_radial_offset() if isinstance(coord, np.ndarray) and len(coord.shape) > 1: rad = self.axis_id[self.radial_axis] lon = self.axis_id["longitude"] lat = self.axis_id["latitude"] r = factor * coord[:, rad] + offset colatitude = _latitude_to_colatitude(coord[:, lat]) phi = coord[:, lon] * np.pi / 180 nc = np.zeros_like(coord) # r, theta, phi nc[:, lat] = np.cos(phi) * np.sin(colatitude) * r nc[:, lon] = np.sin(phi) * np.sin(colatitude) * r nc[:, rad] = np.cos(colatitude) * r else: a, b, c = coord colatitude = _latitude_to_colatitude(b) phi = a * np.pi / 180 r = factor * c + offset nc = ( np.cos(phi) * np.sin(colatitude) * r, np.sin(phi) * np.sin(colatitude) * r, np.cos(colatitude) * r, ) return nc def convert_to_cylindrical(self, coord): raise NotImplementedError def convert_from_cylindrical(self, coord): raise NotImplementedError def convert_to_spherical(self, coord): raise NotImplementedError def convert_from_spherical(self, coord): raise NotImplementedError _image_axis_name = None @property def image_axis_name(self): if self._image_axis_name is not None: return self._image_axis_name # This is the x and y axes labels that get displayed. For # non-Cartesian coordinates, we usually want to override these for # Cartesian coordinates, since we transform them. rv = { self.axis_id["latitude"]: ( "x / \\sin(\\mathrm{latitude})", "y / \\sin(\\mathrm{latitude})", ), self.axis_id["longitude"]: ("R", "z"), self.axis_id[self.radial_axis]: ("longitude", "latitude"), } for i in list(rv.keys()): rv[self.axis_name[i]] = rv[i] rv[self.axis_name[i].capitalize()] = rv[i] self._image_axis_name = rv return rv _x_pairs = ( ("latitude", "longitude"), ("longitude", "latitude"), ("altitude", "longitude"), ) _y_pairs = ( ("latitude", "altitude"), ("longitude", "altitude"), ("altitude", "latitude"), ) _data_projection = None @property def data_projection(self): # this will control the default projection to use when displaying data if self._data_projection is not None: return self._data_projection dpj = {} for ax in self.axis_order: if ax == self.radial_axis: dpj[ax] = "Mollweide" else: dpj[ax] = None self._data_projection = dpj return dpj _data_transform = None @property def data_transform(self): # this is the coordinate system on which the data is defined (the crs). if self._data_transform is not None: return self._data_transform dtx = {} for ax in self.axis_order: if ax == self.radial_axis: dtx[ax] = "PlateCarree" else: dtx[ax] = None self._data_transform = dtx return dtx @property def period(self): return self.ds.domain_width def sanitize_center(self, center, axis): center, display_center = super().sanitize_center(center, axis) name = self.axis_name[axis] if name == self.radial_axis: display_center = center elif name == "latitude": display_center = ( 0.0 * display_center[0], 0.0 * display_center[1], 0.0 * display_center[2], ) elif name == "longitude": ri = self.axis_id[self.radial_axis] c = (self.ds.domain_right_edge[ri] + self.ds.domain_left_edge[ri]) / 2.0 display_center = [ 0.0 * display_center[0], 0.0 * display_center[1], 0.0 * display_center[2], ] display_center[self.axis_id["latitude"]] = c return center, display_center def sanitize_width(self, axis, width, depth): name = self.axis_name[axis] if width is not None: width = super().sanitize_width(axis, width, depth) elif name == self.radial_axis: rax = self.radial_axis width = [ self.ds.domain_width[self.x_axis[rax]], self.ds.domain_width[self.y_axis[rax]], ] elif name == "latitude": ri = self.axis_id[self.radial_axis] # Remember, in spherical coordinates when we cut in theta, # we create a conic section width = [2.0 * self.ds.domain_width[ri], 2.0 * self.ds.domain_width[ri]] elif name == "longitude": ri = self.axis_id[self.radial_axis] width = [self.ds.domain_width[ri], 2.0 * self.ds.domain_width[ri]] return width class InternalGeographicCoordinateHandler(GeographicCoordinateHandler): radial_axis = "depth" name = "internal_geographic" def _setup_radial_fields(self, registry): # Altitude is the radius from the central zone minus the radius of the # surface. def _depth_to_radius(field, data): outer_radius = data.get_field_parameter("outer_radius") if outer_radius is None: if hasattr(data.ds, "outer_radius"): outer_radius = data.ds.outer_radius else: # Otherwise, we assume that the depth goes to full depth, # so we can look at the domain right edge in depth. rax = self.axis_id[self.radial_axis] outer_radius = data.ds.domain_right_edge[rax] return -1.0 * data["index", "depth"] + outer_radius registry.add_field( ("index", "r"), sampling_type="cell", function=_depth_to_radius, units="code_length", ) registry.alias(("index", "dr"), ("index", "ddepth")) def _retrieve_radial_offset(self, data_source=None): # Depth means switching sign and adding to full radius outer_radius = None if data_source is not None: outer_radius = data_source.get_field_parameter("outer_radius") if outer_radius is None: if hasattr(self.ds, "outer_radius"): outer_radius = self.ds.outer_radius else: # Otherwise, we assume that the depth goes to full depth, # so we can look at the domain right edge in depth. rax = self.axis_id[self.radial_axis] outer_radius = self.ds.domain_right_edge[rax] return outer_radius, -1.0 _x_pairs = ( ("latitude", "longitude"), ("longitude", "latitude"), ("depth", "longitude"), ) _y_pairs = (("latitude", "depth"), ("longitude", "depth"), ("depth", "latitude")) def sanitize_center(self, center, axis): center, display_center = super( GeographicCoordinateHandler, self ).sanitize_center(center, axis) name = self.axis_name[axis] if name == self.radial_axis: display_center = center elif name == "latitude": display_center = ( 0.0 * display_center[0], 0.0 * display_center[1], 0.0 * display_center[2], ) elif name == "longitude": ri = self.axis_id[self.radial_axis] offset, factor = self._retrieve_radial_offset() outermost = factor * self.ds.domain_left_edge[ri] + offset display_center = [ 0.0 * display_center[0], 0.0 * display_center[1], 0.0 * display_center[2], ] display_center[self.axis_id["latitude"]] = outermost / 2.0 return center, display_center def sanitize_width(self, axis, width, depth): name = self.axis_name[axis] if width is not None: width = super(GeographicCoordinateHandler, self).sanitize_width( axis, width, depth ) elif name == self.radial_axis: rax = self.radial_axis width = [ self.ds.domain_width[self.x_axis[rax]], self.ds.domain_width[self.y_axis[rax]], ] elif name == "latitude": ri = self.axis_id[self.radial_axis] # Remember, in spherical coordinates when we cut in theta, # we create a conic section offset, factor = self._retrieve_radial_offset() outermost = factor * self.ds.domain_left_edge[ri] + offset width = [2.0 * outermost, 2.0 * outermost] elif name == "longitude": ri = self.axis_id[self.radial_axis] offset, factor = self._retrieve_radial_offset() outermost = factor * self.ds.domain_left_edge[ri] + offset width = [outermost, 2.0 * outermost] return width def _latitude_to_colatitude(lat_vals): # convert latitude to theta, accounting for units, # including the case where the units are code_length # due to how yt stores the domain_center units for # geographic coordinates. if isinstance(lat_vals, unyt.unyt_array): if lat_vals.units.dimensions == unyt.dimensions.length: return (90.0 - lat_vals.d) * np.pi / 180.0 ninety = unyt.unyt_quantity(90.0, "degree") return (ninety - lat_vals).to("radian") return (90 - lat_vals) * np.pi / 180.0 yt-project-yt-f043ac8/yt/geometry/coordinates/polar_coordinates.py000066400000000000000000000003011510711153200255260ustar00rootroot00000000000000from .cylindrical_coordinates import CylindricalCoordinateHandler class PolarCoordinateHandler(CylindricalCoordinateHandler): name = "polar" _default_axis_order = ("r", "theta", "z") yt-project-yt-f043ac8/yt/geometry/coordinates/spec_cube_coordinates.py000066400000000000000000000060511510711153200263510ustar00rootroot00000000000000from .cartesian_coordinates import CartesianCoordinateHandler from .coordinate_handler import _get_coord_fields class SpectralCubeCoordinateHandler(CartesianCoordinateHandler): name = "spectral_cube" def __init__(self, ds, ordering=None): if ordering is None: ordering = tuple( "xyz"[axis] for axis in (ds.lon_axis, ds.lat_axis, ds.spec_axis) ) super().__init__(ds, ordering) self.default_unit_label = {} names = {} if ds.lon_name != "X" or ds.lat_name != "Y": names["x"] = r"Image\ x" names["y"] = r"Image\ y" # We can just use ds.lon_axis here self.default_unit_label[ds.lon_axis] = "pixel" self.default_unit_label[ds.lat_axis] = "pixel" names["z"] = ds.spec_name # Again, can use spec_axis here self.default_unit_label[ds.spec_axis] = ds.spec_unit self._image_axis_name = ian = {} for ax in "xyz": axi = self.axis_id[ax] xax = self.axis_name[self.x_axis[ax]] yax = self.axis_name[self.y_axis[ax]] ian[axi] = ian[ax] = ian[ax.upper()] = ( names.get(xax, xax), names.get(yax, yax), ) def _spec_axis(ax, x, y): p = (x, y)[ax] return [self.ds.pixel2spec(pp).v for pp in p] self.axis_field = {} self.axis_field[self.ds.spec_axis] = _spec_axis def setup_fields(self, registry): if not self.ds.no_cgs_equiv_length: return super().setup_fields(registry) for axi, ax in enumerate("xyz"): f1, f2 = _get_coord_fields(axi) def _get_length_func(): def _length_func(field, data): # Just use axis 0 rv = data.ds.arr(data.fcoords[..., 0].copy(), field.units) rv[:] = 1.0 return rv return _length_func registry.add_field( ("index", f"d{ax}"), sampling_type="cell", function=f1, display_field=False, units="code_length", ) registry.add_field( ("index", f"path_element_{ax}"), sampling_type="cell", function=_get_length_func(), display_field=False, units="", ) registry.add_field( ("index", f"{ax}"), sampling_type="cell", function=f2, display_field=False, units="code_length", ) self._register_volume(registry) self._check_fields(registry) _x_pairs = (("x", "y"), ("y", "x"), ("z", "x")) _y_pairs = (("x", "z"), ("y", "z"), ("z", "y")) def convert_to_cylindrical(self, coord): raise NotImplementedError def convert_from_cylindrical(self, coord): raise NotImplementedError @property def image_axis_name(self): return self._image_axis_name yt-project-yt-f043ac8/yt/geometry/coordinates/spherical_coordinates.py000066400000000000000000000331471510711153200264010ustar00rootroot00000000000000from functools import cached_property import numpy as np from yt.utilities.lib.pixelization_routines import pixelize_aitoff, pixelize_cylinder from .coordinate_handler import ( CoordinateHandler, _get_coord_fields, _get_polar_bounds, _setup_dummy_cartesian_coords_and_widths, _setup_polar_coordinates, ) class SphericalCoordinateHandler(CoordinateHandler): name = "spherical" _default_axis_order = ("r", "theta", "phi") def __init__(self, ds, ordering=None): super().__init__(ds, ordering) # Generate self.image_units = {} self.image_units[self.axis_id["r"]] = (1, 1) self.image_units[self.axis_id["theta"]] = (None, None) self.image_units[self.axis_id["phi"]] = (None, None) def setup_fields(self, registry): # Missing implementation for x, y and z coordinates. _setup_dummy_cartesian_coords_and_widths(registry, axes=("x", "y", "z")) _setup_polar_coordinates(registry, self.axis_id) f1, f2 = _get_coord_fields(self.axis_id["phi"], "dimensionless") registry.add_field( ("index", "dphi"), sampling_type="cell", function=f1, display_field=False, units="dimensionless", ) registry.add_field( ("index", "phi"), sampling_type="cell", function=f2, display_field=False, units="dimensionless", ) def _SphericalVolume(field, data): # Here we compute the spherical volume element exactly r = data["index", "r"] dr = data["index", "dr"] theta = data["index", "theta"] dtheta = data["index", "dtheta"] vol = ((r + 0.5 * dr) ** 3 - (r - 0.5 * dr) ** 3) / 3.0 vol *= np.cos(theta - 0.5 * dtheta) - np.cos(theta + 0.5 * dtheta) vol *= data["index", "dphi"] return vol registry.add_field( ("index", "cell_volume"), sampling_type="cell", function=_SphericalVolume, units="code_length**3", ) registry.alias(("index", "volume"), ("index", "cell_volume")) def _path_phi(field, data): # Note: this already assumes cell-centered return ( data["index", "r"] * data["index", "dphi"] * np.sin(data["index", "theta"]) ) registry.add_field( ("index", "path_element_phi"), sampling_type="cell", function=_path_phi, units="code_length", ) def pixelize( self, dimension, data_source, field, bounds, size, antialias=True, periodic=True, *, return_mask=False, ): self.period name = self.axis_name[dimension] if name == "r": buff, mask = self._ortho_pixelize( data_source, field, bounds, size, antialias, dimension, periodic ) elif name in ("theta", "phi"): if name == "theta": # This is admittedly a very hacky way to resolve a bug # it's very likely that the *right* fix would have to # be applied upstream of this function, *but* this case # has never worked properly so maybe it's still preferable to # not having a solution ? # see https://github.com/yt-project/yt/pull/3533 bounds = (*bounds[2:4], *bounds[:2]) buff, mask = self._cyl_pixelize( data_source, field, bounds, size, antialias, dimension ) else: raise NotImplementedError if return_mask: assert mask is None or mask.dtype == bool return buff, mask else: return buff def pixelize_line(self, field, start_point, end_point, npoints): raise NotImplementedError def _ortho_pixelize( self, data_source, field, bounds, size, antialias, dim, periodic ): # use Aitoff projection # http://paulbourke.net/geometry/transformationprojection/ bounds = tuple(_.ndview for _ in self._aitoff_bounds) buff, mask = pixelize_aitoff( azimuth=data_source["py"], dazimuth=data_source["pdy"], colatitude=data_source["px"], dcolatitude=data_source["pdx"], buff_size=size, field=data_source[field], bounds=bounds, input_img=None, azimuth_offset=0, colatitude_offset=0, return_mask=True, ) return buff.T, mask.T def _cyl_pixelize(self, data_source, field, bounds, size, antialias, dimension): name = self.axis_name[dimension] buff = np.full((size[1], size[0]), np.nan, dtype="f8") if name == "theta": mask = pixelize_cylinder( buff, data_source["px"], data_source["pdx"], data_source["py"], data_source["pdy"], data_source[field], bounds, return_mask=True, ) elif name == "phi": # Note that we feed in buff.T here mask = pixelize_cylinder( buff.T, data_source["px"], data_source["pdx"], data_source["py"], data_source["pdy"], data_source[field], bounds, return_mask=True, ).T else: raise RuntimeError return buff, mask def convert_from_cartesian(self, coord): raise NotImplementedError def convert_to_cartesian(self, coord): if isinstance(coord, np.ndarray) and len(coord.shape) > 1: ri = self.axis_id["r"] thetai = self.axis_id["theta"] phii = self.axis_id["phi"] r = coord[:, ri] theta = coord[:, thetai] phi = coord[:, phii] nc = np.zeros_like(coord) # r, theta, phi nc[:, ri] = np.cos(phi) * np.sin(theta) * r nc[:, thetai] = np.sin(phi) * np.sin(theta) * r nc[:, phii] = np.cos(theta) * r else: r, theta, phi = coord nc = ( np.cos(phi) * np.sin(theta) * r, np.sin(phi) * np.sin(theta) * r, np.cos(theta) * r, ) return nc def convert_to_cylindrical(self, coord): raise NotImplementedError def convert_from_cylindrical(self, coord): raise NotImplementedError def convert_to_spherical(self, coord): raise NotImplementedError def convert_from_spherical(self, coord): raise NotImplementedError _image_axis_name = None @property def image_axis_name(self): if self._image_axis_name is not None: return self._image_axis_name # This is the x and y axes labels that get displayed. For # non-Cartesian coordinates, we usually want to override these for # Cartesian coordinates, since we transform them. rv = { self.axis_id["r"]: ( # these are the Hammer-Aitoff normalized coordinates # conventions: # - theta is the colatitude, from 0 to PI # - bartheta is the latitude, from -PI/2 to +PI/2 (bartheta = PI/2 - theta) # - phi is the azimuth, from 0 to 2PI # - lambda is the longitude, from -PI to PI (lambda = phi - PI) r"\frac{2\cos(\mathrm{\bar{\theta}})\sin(\lambda/2)}{\sqrt{1 + \cos(\bar{\theta}) \cos(\lambda/2)}}", r"\frac{sin(\bar{\theta})}{\sqrt{1 + \cos(\bar{\theta}) \cos(\lambda/2)}}", "\\theta", ), self.axis_id["theta"]: ("x / \\sin(\\theta)", "y / \\sin(\\theta)"), self.axis_id["phi"]: ("R", "z"), } for i in list(rv.keys()): rv[self.axis_name[i]] = rv[i] rv[self.axis_name[i].capitalize()] = rv[i] self._image_axis_name = rv return rv _x_pairs = (("r", "theta"), ("theta", "r"), ("phi", "r")) _y_pairs = (("r", "phi"), ("theta", "phi"), ("phi", "theta")) @property def period(self): return self.ds.domain_width @cached_property def _poloidal_bounds(self): ri = self.axis_id["r"] ti = self.axis_id["theta"] rmin = self.ds.domain_left_edge[ri] rmax = self.ds.domain_right_edge[ri] thetamin = self.ds.domain_left_edge[ti] thetamax = self.ds.domain_right_edge[ti] corners = [ (rmin, thetamin), (rmin, thetamax), (rmax, thetamin), (rmax, thetamax), ] def to_poloidal_plane(r, theta): # take a r, theta position and return # cylindrical R, z coordinates R = r * np.sin(theta) z = r * np.cos(theta) return R, z cylindrical_corner_coords = [to_poloidal_plane(*corner) for corner in corners] thetamin = thetamin.d thetamax = thetamax.d Rmin = min(R for R, z in cylindrical_corner_coords) if thetamin <= np.pi / 2 <= thetamax: Rmax = rmax else: Rmax = max(R for R, z in cylindrical_corner_coords) zmin = min(z for R, z in cylindrical_corner_coords) zmax = max(z for R, z in cylindrical_corner_coords) return Rmin, Rmax, zmin, zmax @cached_property def _conic_bounds(self): return _get_polar_bounds(self, axes=("r", "phi")) @cached_property def _aitoff_bounds(self): # at the time of writing this function, yt's support for curvilinear # coordinates is a bit hacky, as many components of the system still # expect to receive coordinates with a length dimension. Ultimately # this is not needed but calls for a large refactor. ONE = self.ds.quan(1, "code_length") # colatitude ti = self.axis_id["theta"] thetamin = self.ds.domain_left_edge[ti] thetamax = self.ds.domain_right_edge[ti] # latitude latmin = ONE * np.pi / 2 - thetamax latmax = ONE * np.pi / 2 - thetamin # azimuth pi = self.axis_id["phi"] phimin = self.ds.domain_left_edge[pi] phimax = self.ds.domain_right_edge[pi] # longitude lonmin = phimin - ONE * np.pi lonmax = phimax - ONE * np.pi corners = [ (latmin, lonmin), (latmin, lonmax), (latmax, lonmin), (latmax, lonmax), ] def aitoff_z(latitude, longitude): return np.sqrt(1 + np.cos(latitude) * np.cos(longitude / 2)) def aitoff_x(latitude, longitude): return ( 2 * np.cos(latitude) * np.sin(longitude / 2) / aitoff_z(latitude, longitude) ) def aitoff_y(latitude, longitude): return np.sin(latitude) / aitoff_z(latitude, longitude) def to_aitoff_plane(latitude, longitude): return aitoff_x(latitude, longitude), aitoff_y(latitude, longitude) aitoff_corner_coords = [to_aitoff_plane(*corner) for corner in corners] xmin = ONE * min(x for x, y in aitoff_corner_coords) xmax = ONE * max(x for x, y in aitoff_corner_coords) # theta is the colatitude # What this branch is meant to do is check whether the equator (latitude = 0) # is included in the domain. if latmin < 0 < latmax: xmin = min(xmin, ONE * aitoff_x(0, lonmin)) xmax = max(xmax, ONE * aitoff_x(0, lonmax)) # the y direction is more straightforward because aitoff-projected parallels (y) # draw a convex shape, while aitoff-projected meridians (x) draw a concave shape ymin = ONE * min(y for x, y in aitoff_corner_coords) ymax = ONE * max(y for x, y in aitoff_corner_coords) return xmin, xmax, ymin, ymax def sanitize_center(self, center, axis): center, display_center = super().sanitize_center(center, axis) name = self.axis_name[axis] if name == "r": xxmin, xxmax, yymin, yymax = self._aitoff_bounds xc = (xxmin + xxmax) / 2 yc = (yymin + yymax) / 2 display_center = (0 * xc, xc, yc) elif name == "theta": xxmin, xxmax, yymin, yymax = self._conic_bounds xc = (xxmin + xxmax) / 2 yc = (yymin + yymax) / 2 display_center = (xc, 0 * xc, yc) elif name == "phi": Rmin, Rmax, zmin, zmax = self._poloidal_bounds xc = (Rmin + Rmax) / 2 yc = (zmin + zmax) / 2 display_center = (xc, yc) return center, display_center def sanitize_width(self, axis, width, depth): name = self.axis_name[axis] if width is not None: width = super().sanitize_width(axis, width, depth) elif name == "r": xxmin, xxmax, yymin, yymax = self._aitoff_bounds xw = xxmax - xxmin yw = yymax - yymin width = [xw, yw] elif name == "theta": # Remember, in spherical coordinates when we cut in theta, # we create a conic section xxmin, xxmax, yymin, yymax = self._conic_bounds xw = xxmax - xxmin yw = yymax - yymin width = [xw, yw] elif name == "phi": Rmin, Rmax, zmin, zmax = self._poloidal_bounds xw = Rmax - Rmin yw = zmax - zmin width = [xw, yw] return width yt-project-yt-f043ac8/yt/geometry/coordinates/tests/000077500000000000000000000000001510711153200226155ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/coordinates/tests/__init__.py000066400000000000000000000000001510711153200247140ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_axial_pixelization.py000066400000000000000000000007541510711153200301310ustar00rootroot00000000000000from yt.testing import _geom_transforms, fake_amr_ds from yt.utilities.answer_testing.framework import AxialPixelizationTest def test_axial_pixelization(): for geom in sorted(_geom_transforms): if geom == "spectral_cube": # skip this case as it was added much later and we don't want to keep # adding yield-based tests during the nose->pytest migration continue ds = fake_amr_ds(geometry=geom) yield AxialPixelizationTest(ds) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_cartesian_coordinates.py000066400000000000000000000022101510711153200305640ustar00rootroot00000000000000# Some tests for the Cartesian coordinates handler import numpy as np from numpy.testing import assert_equal from yt.testing import fake_amr_ds # Our canonical tests are that we can access all of our fields and we can # compute our volume correctly. def test_cartesian_coordinates(): # We're going to load up a simple AMR grid and check its volume # calculations and path length calculations. ds = fake_amr_ds() axes = sorted(set(ds.coordinates.axis_name.values())) for i, axis in enumerate(axes): dd = ds.all_data() fi = ("index", axis) fd = ("index", f"d{axis}") fp = ("index", f"path_element_{axis}") ma = np.argmax(dd[fi]) assert_equal(dd[fi][ma] + dd[fd][ma] / 2.0, ds.domain_right_edge[i]) mi = np.argmin(dd[fi]) assert_equal(dd[fi][mi] - dd[fd][mi] / 2.0, ds.domain_left_edge[i]) assert_equal(dd[fd].min(), ds.index.get_smallest_dx()) assert_equal(dd[fd].max(), (ds.domain_width / ds.domain_dimensions)[i]) assert_equal(dd[fd], dd[fp]) assert_equal( dd["index", "cell_volume"].sum(dtype="float64"), ds.domain_width.prod() ) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_cylindrical_coordinates.py000066400000000000000000000024571510711153200311250ustar00rootroot00000000000000# Some tests for the Cylindrical coordinates handler import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.testing import fake_amr_ds # Our canonical tests are that we can access all of our fields and we can # compute our volume correctly. def test_cylindrical_coordinates(): # We're going to load up a simple AMR grid and check its volume # calculations and path length calculations. ds = fake_amr_ds(geometry="cylindrical") axes = ["r", "z", "theta"] for i, axis in enumerate(axes): dd = ds.all_data() fi = ("index", axis) fd = ("index", f"d{axis}") ma = np.argmax(dd[fi]) assert_equal(dd[fi][ma] + dd[fd][ma] / 2.0, ds.domain_right_edge[i].d) mi = np.argmin(dd[fi]) assert_equal(dd[fi][mi] - dd[fd][mi] / 2.0, ds.domain_left_edge[i].d) assert_equal(dd[fd].max(), (ds.domain_width / ds.domain_dimensions)[i].d) assert_almost_equal( dd["index", "cell_volume"].sum(dtype="float64"), np.pi * ds.domain_width[0] ** 2 * ds.domain_width[1], ) assert_equal(dd["index", "path_element_r"], dd["index", "dr"]) assert_equal(dd["index", "path_element_z"], dd["index", "dz"]) assert_equal( dd["index", "path_element_theta"], dd["index", "r"] * dd["index", "dtheta"] ) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_geographic_coordinates.py000066400000000000000000000131431510711153200307320ustar00rootroot00000000000000# Some tests for the geographic coordinates handler import numpy as np import pytest import unyt from numpy.testing import assert_equal from yt.testing import assert_rel_equal, fake_amr_ds # Our canonical tests are that we can access all of our fields and we can # compute our volume correctly. @pytest.mark.parametrize("geometry", ("geographic", "internal_geographic")) def test_geographic_coordinates(geometry): # We're going to load up a simple AMR grid and check its volume # calculations and path length calculations. # Note that we are setting it up to have an altitude of 1000 maximum, which # means our volume will be that of a shell 1000 wide, starting at r of # whatever our surface_height is set to. ds = fake_amr_ds(geometry=geometry) if geometry == "geographic": ds.surface_height = ds.quan(5000.0, "code_length") inner_r = ds.surface_height outer_r = ds.surface_height + ds.domain_width[2] else: ds.outer_radius = ds.quan(5000.0, "code_length") inner_r = ds.outer_radius - ds.domain_right_edge[2] outer_r = ds.outer_radius radial_axis = ds.coordinates.radial_axis axes = ds.coordinates.axis_order for i, axis in enumerate(axes): dd = ds.all_data() fi = ("index", axis) fd = ("index", f"d{axis}") ma = np.argmax(dd[fi]) assert_equal(dd[fi][ma] + dd[fd][ma] / 2.0, ds.domain_right_edge[i].d) mi = np.argmin(dd[fi]) assert_equal(dd[fi][mi] - dd[fd][mi] / 2.0, ds.domain_left_edge[i].d) assert_equal(dd[fd].max(), (ds.domain_width / ds.domain_dimensions)[i].d) assert_equal(dd["index", "dtheta"], dd["index", "dlatitude"] * np.pi / 180.0) assert_equal(dd["index", "dphi"], dd["index", "dlongitude"] * np.pi / 180.0) # Note our terrible agreement here. assert_rel_equal( dd["index", "cell_volume"].sum(dtype="float64"), (4.0 / 3.0) * np.pi * (outer_r**3 - inner_r**3), 14, ) assert_equal( dd["index", f"path_element_{radial_axis}"], dd["index", f"d{radial_axis}"] ) assert_equal(dd["index", f"path_element_{radial_axis}"], dd["index", "dr"]) # Note that latitude corresponds to theta, longitude to phi assert_equal( dd["index", "path_element_latitude"], dd["index", "r"] * dd["index", "dlatitude"] * np.pi / 180.0, ) assert_equal( dd["index", "path_element_longitude"], ( dd["index", "r"] * dd["index", "dlongitude"] * np.pi / 180.0 * np.sin((90 - dd["index", "latitude"]) * np.pi / 180.0) ), ) # We also want to check that our radius is correct offset, factor = ds.coordinates._retrieve_radial_offset() radius = factor * dd["index", radial_axis] + offset assert_equal(dd["index", "r"], radius) @pytest.mark.parametrize("geometry", ("geographic", "internal_geographic")) def test_geographic_conversions(geometry): ds = fake_amr_ds(geometry=geometry) ad = ds.all_data() lats = ad["index", "latitude"] dlats = ad["index", "dlatitude"] theta = ad["index", "theta"] dtheta = ad["index", "dtheta"] # check that theta = 0, pi at latitudes of 90, -90 south_pole_id = np.where(lats == np.min(lats))[0][0] north_pole_id = np.where(lats == np.max(lats))[0][0] # check that we do in fact have -90, 90 exactly assert lats[south_pole_id] - dlats[south_pole_id] / 2.0 == -90.0 assert lats[north_pole_id] + dlats[north_pole_id] / 2.0 == 90.0 # check that theta=0 at the north pole, np.pi at the south assert theta[north_pole_id] - dtheta[north_pole_id] / 2 == 0.0 assert theta[south_pole_id] + dtheta[south_pole_id] / 2 == np.pi # check that longitude-phi conversions phi = ad["index", "phi"] dphi = ad["index", "dphi"] lons = ad["index", "longitude"] dlon = ad["index", "dlongitude"] lon_180 = np.where(lons == np.max(lons))[0][0] lon_neg180 = np.where(lons == np.min(lons))[0][0] # check we have -180, 180 exactly assert lons[lon_neg180] - dlon[lon_neg180] / 2.0 == -180.0 assert lons[lon_180] + dlon[lon_180] / 2.0 == 180.0 # check that those both convert to phi = np.pi assert phi[lon_neg180] - dphi[lon_neg180] / 2.0 == np.pi assert phi[lon_180] + dphi[lon_180] / 2.0 == np.pi # check that z = +/- radius at +/-90 # default expected axis order: lat, lon, radial axis r_val = ds.coordinates._retrieve_radial_offset()[0] coords = np.zeros((2, 3)) coords[0, 0] = 90.0 coords[1, 0] = -90.0 xyz = ds.coordinates.convert_to_cartesian(coords) z = xyz[:, 2] assert z[0] == r_val assert z[1] == -r_val @pytest.mark.parametrize("geometry", ("geographic", "internal_geographic")) def test_geographic_conversions_with_units(geometry): ds = fake_amr_ds(geometry=geometry) # _sanitize_center will give all values in 'code_length' coords = ds.arr(np.zeros((2, 3)), "code_length") xyz_u = ds.coordinates.convert_to_cartesian(coords) xyz = ds.coordinates.convert_to_cartesian(coords.d) assert_equal(xyz, xyz_u) coords = ds.arr(np.zeros((3,)), "code_length") xyz_u = ds.coordinates.convert_to_cartesian(coords) xyz = ds.coordinates.convert_to_cartesian(coords.d) assert_equal(xyz, xyz_u) # also check that if correct units are supplied, the # result has dimensions of length. coords = [ ds.arr(np.zeros((10,)), "degree"), ds.arr(np.zeros((10,)), "degree"), ds.arr(np.linspace(0, 100, 10), "code_length"), ] xyz = ds.coordinates.convert_to_cartesian(coords) for dim in xyz: assert dim.units.dimensions == unyt.dimensions.length yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_polar_coordinates.py000066400000000000000000000025351510711153200277420ustar00rootroot00000000000000# Some tests for the polar coordinates handler # (Pretty similar to cylindrical, but different ordering) import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.testing import fake_amr_ds # Our canonical tests are that we can access all of our fields and we can # compute our volume correctly. def test_cylindrical_coordinates(): # We're going to load up a simple AMR grid and check its volume # calculations and path length calculations. ds = fake_amr_ds(geometry="polar") axes = ["r", "theta", "z"] for i, axis in enumerate(axes): dd = ds.all_data() fi = ("index", axis) fd = ("index", f"d{axis}") ma = np.argmax(dd[fi]) assert_equal(dd[fi][ma] + dd[fd][ma] / 2.0, ds.domain_right_edge[i].d) mi = np.argmin(dd[fi]) assert_equal(dd[fi][mi] - dd[fd][mi] / 2.0, ds.domain_left_edge[i].d) assert_equal(dd[fd].max(), (ds.domain_width / ds.domain_dimensions)[i].d) assert_almost_equal( dd["index", "cell_volume"].sum(dtype="float64"), np.pi * ds.domain_width[0] ** 2 * ds.domain_width[2], ) assert_equal(dd["index", "path_element_r"], dd["index", "dr"]) assert_equal(dd["index", "path_element_z"], dd["index", "dz"]) assert_equal( dd["index", "path_element_theta"], dd["index", "r"] * dd["index", "dtheta"] ) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_sanitize_center.py000066400000000000000000000076201510711153200274210ustar00rootroot00000000000000import re import pytest from unyt import unyt_array from unyt.exceptions import UnitConversionError from yt.testing import fake_amr_ds @pytest.fixture(scope="module") def reusable_fake_dataset(): ds = fake_amr_ds( fields=[("gas", "density")], units=["g/cm**3"], ) return ds valid_single_str_values = ("center",) valid_field_loc_str_values = ("min", "max") DEFAUT_ERROR_MESSAGE = ( "Expected any of the following\n" "- 'c', 'center', 'l', 'left', 'r', 'right', 'm', 'max', or 'min'\n" "- a 2 element tuple with 'min' or 'max' as the first element, followed by a field identifier\n" "- a 3 element array-like: for a unyt_array, expects length dimensions, otherwise code_lenght is assumed" ) @pytest.mark.parametrize( "user_input", ( # second element can be a single str or a field tuple (2 str), but not three (("max", ("not", "a", "field"))), # a 1-tuple is also not a valid field key (("max", ("notafield",))), # both elements need to be str (("max", (0, "invalid_field_type"))), (("max", ("invalid_field_type", 1))), ), ) def test_invalid_center_type_default_error(reusable_fake_dataset, user_input): ds = reusable_fake_dataset with pytest.raises( TypeError, match=re.escape(f"Received {user_input!r}, ") + r"but failed to transform to a unyt_array \(obtained .+\)\.", ): # at the time of writing `axis` is an unused parameter of the base # sanitize center method, which is used directly for cartesian coordinate handlers # this probably hints that a refactor would make sense to separaet center sanitizing # and display_center calculation ds.coordinates.sanitize_center(user_input, axis=None) @pytest.mark.parametrize( "user_input, error_type, error_message", ( ( "bad_str", ValueError, re.escape( "Received unknown center single string value 'bad_str'. " + DEFAUT_ERROR_MESSAGE ), ), ( ("bad_str", ("gas", "density")), ValueError, re.escape( "Received unknown string value 'bad_str'. " f"Expected one of {valid_field_loc_str_values} (case insensitive)" ), ), ( ("bad_str", "density"), ValueError, re.escape( "Received unknown string value 'bad_str'. " "Expected one of ('min', 'max') (case insensitive)" ), ), # even with exactly three elements, the dimension should be length ( unyt_array([0.5] * 3, "kg"), UnitConversionError, "...", # don't match the exact error message since it's unyt's responsibility ), # only validate 3 elements unyt_arrays ( unyt_array([0.5] * 2, "cm"), TypeError, re.escape("Received unyt_array([0.5, 0.5], 'cm')"), ), ( unyt_array([0.5] * 4, "cm"), TypeError, # don't attempt to match error message as details of how # a unyt array with more than a couple elements is displayed are out of our control "...", ), ( # check that the whole shape is used in validation, not just the length (number of rows) unyt_array([0.5] * 6, "cm").reshape(3, 2), TypeError, # don't attempt to match error message as details of how # a unyt array with more than a couple elements is displayed are out of our control "...", ), ), ) def test_invalid_center_special_cases( reusable_fake_dataset, user_input, error_type, error_message ): ds = reusable_fake_dataset with pytest.raises(error_type, match=error_message): ds.coordinates.sanitize_center(user_input, axis=None) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_sph_pixelization.py000066400000000000000000000144001510711153200276160ustar00rootroot00000000000000import numpy as np import yt from yt.testing import ( assert_rel_equal, cubicspline_python, fake_sph_flexible_grid_ds, integrate_kernel, requires_file, ) from yt.utilities.math_utils import compute_stddev_image ## off-axis projection tests for SPH data are in ## yt/visualization/tests/test_offaxisprojection.py magneticum = "MagneticumCluster/snap_132" mag_kwargs = { "long_ids": True, "field_spec": "magneticum_box2_hr", } @requires_file(magneticum) def test_sph_moment(): ds = yt.load(magneticum, **mag_kwargs) def _vysq(field, data): return data["gas", "velocity_y"] ** 2 ds.add_field(("gas", "vysq"), _vysq, sampling_type="local", units="cm**2/s**2") prj1 = yt.ProjectionPlot( ds, "y", [("gas", "velocity_y"), ("gas", "vysq")], weight_field=("gas", "density"), moment=1, buff_size=(400, 400), ) prj2 = yt.ProjectionPlot( ds, "y", ("gas", "velocity_y"), moment=2, weight_field=("gas", "density"), buff_size=(400, 400), ) sigy = compute_stddev_image(prj1.frb["gas", "vysq"], prj1.frb["gas", "velocity_y"]) assert_rel_equal(sigy, prj2.frb["gas", "velocity_y"].d, 10) def test_sph_projection_basic1(): """ small, uniform grid: expected values for given dl? pixel centers at 0.5, 1., 1.5, 2., 2.5 particles at 0.5, 1.5, 2.5 """ bbox = np.array([[0.0, 3.0]] * 3) ds = fake_sph_flexible_grid_ds(hsml_factor=1.0, nperside=3, bbox=bbox) # works, but no depth control (at least without specific filters) proj = ds.proj(("gas", "density"), 2) frb = proj.to_frb( width=(2.5, "cm"), resolution=(5, 5), height=(2.5, "cm"), center=np.array([1.5, 1.5, 1.5]), periodic=False, ) out = frb.get_image(("gas", "density")) expected_out = np.zeros((5, 5), dtype=np.float64) dl_1part = integrate_kernel(cubicspline_python, 0.0, 0.5) linedens_1part = dl_1part * 1.0 # unit mass, density linedens = 3.0 * linedens_1part expected_out[::2, ::2] = linedens assert_rel_equal(expected_out, out.v, 5) # return out def test_sph_projection_basic2(): """ small, uniform grid: expected values for given dl? pixel centers at 0.5, 1., 1.5, 2., 2.5 particles at 0.5, 1.5, 2.5 but hsml radii are 0.25 -> try non-zero impact parameters, other pixels are still zero. """ bbox = np.array([[0.0, 3.0]] * 3) ds = fake_sph_flexible_grid_ds(hsml_factor=0.5, nperside=3, bbox=bbox) proj = ds.proj(("gas", "density"), 2) frb = proj.to_frb( width=(2.5, "cm"), resolution=(5, 5), height=(2.5, "cm"), center=np.array([1.375, 1.375, 1.5]), periodic=False, ) out = frb.get_image(("gas", "density")) expected_out = np.zeros((5, 5), dtype=np.float64) dl_1part = integrate_kernel(cubicspline_python, np.sqrt(2) * 0.125, 0.25) linedens_1part = dl_1part * 1.0 # unit mass, density linedens = 3.0 * linedens_1part expected_out[::2, ::2] = linedens # print(expected_out) # print(out.v) assert_rel_equal(expected_out, out.v, 4) # return out def get_dataset_sphrefine(reflevel: int = 1): """ constant density particle grid, with increasing particle sampling """ lenfact = (1.0 / 3.0) ** (reflevel - 1) massfact = lenfact**3 nperside = 3**reflevel e1hat = np.array([lenfact, 0, 0]) e2hat = np.array([0, lenfact, 0]) e3hat = np.array([0, 0, lenfact]) hsml_factor = lenfact bbox = np.array([[0.0, 3.0]] * 3) offsets = np.ones(3, dtype=np.float64) * 0.5 # in units of ehat def refmass(i: int, j: int, k: int) -> float: return massfact unitrho = 1.0 / massfact # want density 1 for decreasing mass ds = fake_sph_flexible_grid_ds( hsml_factor=hsml_factor, nperside=nperside, periodic=True, e1hat=e1hat, e2hat=e2hat, e3hat=e3hat, offsets=offsets, massgenerator=refmass, unitrho=unitrho, bbox=bbox, ) return ds def getdata_test_gridproj2(): # initial pixel centers at 0.5, 1., 1.5, 2., 2.5 # particles at 0.5, 1.5, 2.5 # refine particle grid, check if pixel values remain the # same in the pixels passing through initial particle centers outlist = [] dss = [] for rl in range(1, 4): ds = get_dataset_sphrefine(reflevel=rl) proj = ds.proj(("gas", "density"), 2) frb = proj.to_frb( width=(2.5, "cm"), resolution=(5, 5), height=(2.5, "cm"), center=np.array([1.5, 1.5, 1.5]), periodic=False, ) out = frb.get_image(("gas", "density")) outlist.append(out) dss.append(ds) return outlist, dss def test_sph_gridproj_reseffect1(): """ Comparing same pixel centers with higher particle resolution. The pixel centers are at x/y coordinates [0.5, 1., 1.5, 2., 2.5] at the first level, the spacing halves at each level. Checking the pixels at [0.5, 1.5, 2.5], which should have the same values at each resolution. """ imgs, _ = getdata_test_gridproj2() ref = imgs[-1] for img in imgs: assert_rel_equal( img[:: img.shape[0] // 2, :: img.shape[1] // 2], ref[:: ref.shape[0] // 2, :: ref.shape[1] // 2], 4, ) def test_sph_gridproj_reseffect2(): """ refine the pixel grid instead of the particle grid """ ds = get_dataset_sphrefine(reflevel=2) proj = ds.proj(("gas", "density"), 2) imgs = {} maxrl = 5 for rl in range(1, maxrl + 1): npix = 1 + 2 ** (rl + 1) margin = 0.5 - 0.5 ** (rl + 1) frb = proj.to_frb( width=(3.0 - 2.0 * margin, "cm"), resolution=(npix, npix), height=(3.0 - 2.0 * margin, "cm"), center=np.array([1.5, 1.5, 1.5]), periodic=False, ) out = frb.get_image(("gas", "density")) imgs[rl] = out ref = imgs[maxrl] pixspace_ref = 2 ** (maxrl) for rl in imgs: img = imgs[rl] pixspace = 2 ** (rl) # print(f'Grid refinement level {rl}:') assert_rel_equal( img[::pixspace, ::pixspace], ref[::pixspace_ref, ::pixspace_ref], 4 ) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_sph_pixelization_pytestonly.py000066400000000000000000000423141510711153200321350ustar00rootroot00000000000000import numpy as np import pytest import unyt import yt from yt.data_objects.selection_objects.region import YTRegion from yt.testing import ( assert_rel_equal, cubicspline_python, distancematrix, fake_random_sph_ds, fake_sph_flexible_grid_ds, integrate_kernel, ) @pytest.mark.parametrize("weighted", [True, False]) @pytest.mark.parametrize("periodic", [True, False]) @pytest.mark.parametrize("depth", [None, (1.0, "cm")]) @pytest.mark.parametrize("shiftcenter", [False, True]) @pytest.mark.parametrize("axis", [0, 1, 2]) def test_sph_proj_general_alongaxes( axis: int, shiftcenter: bool, depth: float | None, periodic: bool, weighted: bool, ) -> None: """ The previous projection tests were for a specific issue. Here, we test more functionality of the projections. We just send lines of sight through pixel centers for convenience. Particles at [0.5, 1.5, 2.5] (in each coordinate) smoothing lengths 0.25 all particles have mass 1., density 1.5, except the single center particle, with mass 2., density 3. Parameters: ----------- axis: {0, 1, 2} projection axis (aligned with sim. axis) shiftcenter: bool shift the coordinates to center the projection on. (The grid is offset to this same center) depth: float or None depth of the projection slice periodic: bool assume periodic boundary conditions, or not weighted: bool make a weighted projection (density-weighted density), or not Returns: -------- None """ if shiftcenter: center = unyt.unyt_array(np.array((0.625, 0.625, 0.625)), "cm") else: center = unyt.unyt_array(np.array((1.5, 1.5, 1.5)), "cm") bbox = unyt.unyt_array(np.array([[0.0, 3.0], [0.0, 3.0], [0.0, 3.0]]), "cm") hsml_factor = 0.5 unitrho = 1.5 # test correct centering, particle selection def makemasses(i, j, k): if i == j == k == 1: return 2.0 else: return 1.0 # m / rho, factor 1. / hsml**2 is included in the kernel integral # (density is adjusted, so same for center particle) prefactor = 1.0 / unitrho # / (0.5 * 0.5)**2 dl_cen = integrate_kernel(cubicspline_python, 0.0, 0.25) # result shouldn't depend explicitly on the center if we re-center # the data, unless we get cut-offs in the non-periodic case ds = fake_sph_flexible_grid_ds( hsml_factor=hsml_factor, nperside=3, periodic=periodic, offsets=np.full(3, 0.5), massgenerator=makemasses, unitrho=unitrho, bbox=bbox.v, recenter=center.v, ) if depth is None: source = ds.all_data() else: depth = unyt.unyt_quantity(*depth) le = np.array(ds.domain_left_edge) re = np.array(ds.domain_right_edge) le[axis] = center[axis] - 0.5 * depth re[axis] = center[axis] + 0.5 * depth cen = 0.5 * (le + re) reg = YTRegion(center=cen, left_edge=le, right_edge=re, ds=ds) source = reg # we don't actually want a plot, it's just a straightforward, # common way to get an frb / image array if weighted: toweight_field = ("gas", "density") else: toweight_field = None prj = yt.ProjectionPlot( ds, axis, ("gas", "density"), width=(2.5, "cm"), weight_field=toweight_field, buff_size=(5, 5), center=center, data_source=source, ) img = prj.frb.data[("gas", "density")] if weighted: expected_out = np.zeros( ( 5, 5, ), dtype=img.v.dtype, ) expected_out[::2, ::2] = unitrho if depth is None: ## during shift, particle coords do wrap around edges # if (not periodic) and shiftcenter: # # weight 1. for unitrho, 2. for 2. * untrho # expected_out[2, 2] *= 5. / 3. # else: # weight (2 * 1.) for unitrho, (1 * 2.) for 2. * unitrho expected_out[2, 2] *= 1.5 else: # only 2 * unitrho element included expected_out[2, 2] *= 2.0 else: expected_out = np.zeros( ( 5, 5, ), dtype=img.v.dtype, ) expected_out[::2, ::2] = dl_cen * prefactor * unitrho if depth is None: # 3 particles per l.o.s., including the denser one expected_out *= 3.0 expected_out[2, 2] *= 4.0 / 3.0 else: # 1 particle per l.o.s., including the denser one expected_out[2, 2] *= 2.0 # grid is shifted to the left -> 'missing' stuff at the left if (not periodic) and shiftcenter: expected_out[:1, :] = 0.0 expected_out[:, :1] = 0.0 # print(axis, shiftcenter, depth, periodic, weighted) # print(expected_out) # print(img.v) assert_rel_equal(expected_out, img.v, 5) @pytest.mark.parametrize("periodic", [True, False]) @pytest.mark.parametrize("shiftcenter", [False, True]) @pytest.mark.parametrize("zoff", [0.0, 0.1, 0.5, 1.0]) @pytest.mark.parametrize("axis", [0, 1, 2]) def test_sph_slice_general_alongaxes( axis: int, shiftcenter: bool, periodic: bool, zoff: float, ) -> None: """ Particles at [0.5, 1.5, 2.5] (in each coordinate) smoothing lengths 0.25 all particles have mass 1., density 1.5, except the single center particle, with mass 2., density 3. Parameters: ----------- axis: {0, 1, 2} projection axis (aligned with sim. axis) northvector: tuple y-axis direction in the final plot (direction vector) shiftcenter: bool shift the coordinates to center the projection on. (The grid is offset to this same center) zoff: float offset of the slice plane from the SPH particle center plane periodic: bool assume periodic boundary conditions, or not Returns: -------- None """ if shiftcenter: center = unyt.unyt_array(np.array((0.625, 0.625, 0.625)), "cm") else: center = unyt.unyt_array(np.array((1.5, 1.5, 1.5)), "cm") bbox = unyt.unyt_array(np.array([[0.0, 3.0], [0.0, 3.0], [0.0, 3.0]]), "cm") hsml_factor = 0.5 unitrho = 1.5 # test correct centering, particle selection def makemasses(i, j, k): if i == j == k == 1: return 2.0 elif i == j == k == 2: return 3.0 else: return 1.0 # result shouldn't depend explicitly on the center if we re-center # the data, unless we get cut-offs in the non-periodic case ds = fake_sph_flexible_grid_ds( hsml_factor=hsml_factor, nperside=3, periodic=periodic, offsets=np.full(3, 0.5), massgenerator=makemasses, unitrho=unitrho, bbox=bbox.v, recenter=center.v, ) ad = ds.all_data() # print(ad[('gas', 'position')]) outgridsize = 10 width = 2.5 _center = center.to("cm").v.copy() _center[axis] += zoff # we don't actually want a plot, it's just a straightforward, # common way to get an frb / image array slc = yt.SlicePlot( ds, axis, ("gas", "density"), width=(width, "cm"), buff_size=(outgridsize,) * 2, center=(_center, "cm"), ) img = slc.frb.data[("gas", "density")] # center is same in non-projection coords if axis == 0: ci = 1 else: ci = 0 gridcens = ( _center[ci] - 0.5 * width + 0.5 * width / outgridsize + np.arange(outgridsize) * width / outgridsize ) xgrid = np.repeat(gridcens, outgridsize) ygrid = np.tile(gridcens, outgridsize) zgrid = np.full(outgridsize**2, _center[axis]) gridcoords = np.empty((outgridsize**2, 3), dtype=xgrid.dtype) if axis == 2: gridcoords[:, 0] = xgrid gridcoords[:, 1] = ygrid gridcoords[:, 2] = zgrid elif axis == 0: gridcoords[:, 0] = zgrid gridcoords[:, 1] = xgrid gridcoords[:, 2] = ygrid elif axis == 1: gridcoords[:, 0] = ygrid gridcoords[:, 1] = zgrid gridcoords[:, 2] = xgrid ad = ds.all_data() sphcoords = np.array( [ (ad[("gas", "x")]).to("cm"), (ad[("gas", "y")]).to("cm"), (ad[("gas", "z")]).to("cm"), ] ).T # print("sphcoords:") # print(sphcoords) # print("gridcoords:") # print(gridcoords) dists = distancematrix( gridcoords, sphcoords, periodic=(periodic,) * 3, periods=np.array([3.0, 3.0, 3.0]), ) # print("dists <= 1:") # print(dists <= 1) sml = (ad[("gas", "smoothing_length")]).to("cm") normkern = cubicspline_python(dists / sml.v[np.newaxis, :]) sphcontr = normkern / sml[np.newaxis, :] ** 3 * ad[("gas", "mass")] contsum = np.sum(sphcontr, axis=1) sphweights = ( normkern / sml[np.newaxis, :] ** 3 * ad[("gas", "mass")] / ad[("gas", "density")] ) weights = np.sum(sphweights, axis=1) nzeromask = np.logical_not(weights == 0) expected = np.zeros(weights.shape, weights.dtype) expected[nzeromask] = contsum[nzeromask] / weights[nzeromask] expected = expected.reshape((outgridsize, outgridsize)) # expected[np.isnan(expected)] = 0.0 # convention in the slices # print("expected:\n", expected) # print("recovered:\n", img.v) assert_rel_equal(expected, img.v, 5) @pytest.mark.parametrize("periodic", [True, False]) @pytest.mark.parametrize("shiftcenter", [False, True]) @pytest.mark.parametrize("northvector", [None, (1.0e-4, 1.0, 0.0)]) @pytest.mark.parametrize("zoff", [0.0, 0.1, 0.5, 1.0]) def test_sph_slice_general_offaxis( northvector: tuple[float, float, float] | None, shiftcenter: bool, zoff: float, periodic: bool, ) -> None: """ Same as the on-axis slices, but we rotate the basis vectors to test whether roations are handled ok. the rotation is chosen to be small so that in/exclusion of particles within bboxes, etc. works out the same way. Particles at [0.5, 1.5, 2.5] (in each coordinate) smoothing lengths 0.25 all particles have mass 1., density 1.5, except the single center particle, with mass 2., density 3. Parameters: ----------- northvector: tuple y-axis direction in the final plot (direction vector) shiftcenter: bool shift the coordinates to center the projection on. (The grid is offset to this same center) zoff: float offset of the slice plane from the SPH particle center plane periodic: bool assume periodic boundary conditions, or not Returns: -------- None """ if shiftcenter: center = np.array((0.625, 0.625, 0.625)) # cm else: center = np.array((1.5, 1.5, 1.5)) # cm bbox = unyt.unyt_array(np.array([[0.0, 3.0], [0.0, 3.0], [0.0, 3.0]]), "cm") hsml_factor = 0.5 unitrho = 1.5 # test correct centering, particle selection def makemasses(i, j, k): if i == j == k == 1: return 2.0 else: return 1.0 # try to make sure dl differences from periodic wrapping are small epsilon = 1e-4 projaxis = np.array([epsilon, 0.00, np.sqrt(1.0 - epsilon**2)]) e1dir = projaxis / np.sqrt(np.sum(projaxis**2)) if northvector is None: e2dir = np.array([0.0, 1.0, 0.0]) else: e2dir = np.asarray(northvector) e2dir = e2dir - np.sum(e1dir * e2dir) * e2dir # orthonormalize e2dir /= np.sqrt(np.sum(e2dir**2)) e3dir = np.cross(e2dir, e1dir) outgridsize = 10 width = 2.5 _center = center.copy() _center += zoff * e1dir ds = fake_sph_flexible_grid_ds( hsml_factor=hsml_factor, nperside=3, periodic=periodic, offsets=np.full(3, 0.5), massgenerator=makemasses, unitrho=unitrho, bbox=bbox.v, recenter=center, e1hat=e1dir, e2hat=e2dir, e3hat=e3dir, ) # source = ds.all_data() # couple to dataset -> right unit registry center = ds.arr(center, "cm") # print("position:\n", source["gas", "position"]) slc = yt.SlicePlot( ds, e1dir, ("gas", "density"), width=(width, "cm"), buff_size=(outgridsize,) * 2, center=(_center, "cm"), north_vector=e2dir, ) img = slc.frb.data[("gas", "density")] # center is same in x/y (e3dir/e2dir) gridcenx = ( np.dot(_center, e3dir) - 0.5 * width + 0.5 * width / outgridsize + np.arange(outgridsize) * width / outgridsize ) gridceny = ( np.dot(_center, e2dir) - 0.5 * width + 0.5 * width / outgridsize + np.arange(outgridsize) * width / outgridsize ) xgrid = np.repeat(gridcenx, outgridsize) ygrid = np.tile(gridceny, outgridsize) zgrid = np.full(outgridsize**2, np.dot(_center, e1dir)) gridcoords = ( xgrid[:, np.newaxis] * e3dir[np.newaxis, :] + ygrid[:, np.newaxis] * e2dir[np.newaxis, :] + zgrid[:, np.newaxis] * e1dir[np.newaxis, :] ) # print("gridcoords:") # print(gridcoords) ad = ds.all_data() sphcoords = np.array( [ (ad[("gas", "x")]).to("cm"), (ad[("gas", "y")]).to("cm"), (ad[("gas", "z")]).to("cm"), ] ).T dists = distancematrix( gridcoords, sphcoords, periodic=(periodic,) * 3, periods=np.array([3.0, 3.0, 3.0]), ) sml = (ad[("gas", "smoothing_length")]).to("cm") normkern = cubicspline_python(dists / sml.v[np.newaxis, :]) sphcontr = normkern / sml[np.newaxis, :] ** 3 * ad[("gas", "mass")] contsum = np.sum(sphcontr, axis=1) sphweights = ( normkern / sml[np.newaxis, :] ** 3 * ad[("gas", "mass")] / ad[("gas", "density")] ) weights = np.sum(sphweights, axis=1) nzeromask = np.logical_not(weights == 0) expected = np.zeros(weights.shape, weights.dtype) expected[nzeromask] = contsum[nzeromask] / weights[nzeromask] expected = expected.reshape((outgridsize, outgridsize)) expected = expected.T # transposed for image plotting # expected[np.isnan(expected)] = 0.0 # convention in the slices # print(axis, shiftcenter, depth, periodic, weighted) # print("expected:\n", expected) # print("recovered:\n", img.v) assert_rel_equal(expected, img.v, 4) # only axis-aligned; testing YTArbitraryGrid, YTCoveringGrid @pytest.mark.parametrize("periodic", [True, False, (True, True, False)]) @pytest.mark.parametrize("wholebox", [True, False]) def test_sph_grid( periodic: bool | tuple[bool, bool, bool], wholebox: bool, ) -> None: bbox = np.array([[-1.0, 3.0], [1.0, 5.2], [-1.0, 3.0]]) ds = fake_random_sph_ds(50, bbox, periodic=periodic) if not hasattr(periodic, "__len__"): periodic = (periodic,) * 3 if wholebox: left = bbox[:, 0].copy() level = 2 ncells = np.array([2**level] * 3) # print("left: ", left) # print("ncells: ", ncells) resgrid = ds.covering_grid(level, tuple(left), ncells) right = bbox[:, 1].copy() xedges = np.linspace(left[0], right[0], ncells[0] + 1) yedges = np.linspace(left[1], right[1], ncells[1] + 1) zedges = np.linspace(left[2], right[2], ncells[2] + 1) else: left = np.array([-1.0, 1.8, -1.0]) right = np.array([2.5, 5.2, 2.5]) ncells = np.array([3, 4, 4]) resgrid = ds.arbitrary_grid(left, right, dims=ncells) xedges = np.linspace(left[0], right[0], ncells[0] + 1) yedges = np.linspace(left[1], right[1], ncells[1] + 1) zedges = np.linspace(left[2], right[2], ncells[2] + 1) res = resgrid["gas", "density"] xcens = 0.5 * (xedges[:-1] + xedges[1:]) ycens = 0.5 * (yedges[:-1] + yedges[1:]) zcens = 0.5 * (zedges[:-1] + zedges[1:]) ad = ds.all_data() sphcoords = np.array( [ (ad[("gas", "x")]).to("cm"), (ad[("gas", "y")]).to("cm"), (ad[("gas", "z")]).to("cm"), ] ).T gridx, gridy, gridz = np.meshgrid(xcens, ycens, zcens, indexing="ij") outshape = gridx.shape gridx = gridx.flatten() gridy = gridy.flatten() gridz = gridz.flatten() gridcoords = np.array([gridx, gridy, gridz]).T periods = bbox[:, 1] - bbox[:, 0] dists = distancematrix(gridcoords, sphcoords, periodic=periodic, periods=periods) sml = (ad[("gas", "smoothing_length")]).to("cm") normkern = cubicspline_python(dists / sml.v[np.newaxis, :]) sphcontr = normkern / sml[np.newaxis, :] ** 3 * ad[("gas", "mass")] contsum = np.sum(sphcontr, axis=1) sphweights = ( normkern / sml[np.newaxis, :] ** 3 * ad[("gas", "mass")] / ad[("gas", "density")] ) weights = np.sum(sphweights, axis=1) nzeromask = np.logical_not(weights == 0) expected = np.zeros(weights.shape, weights.dtype) expected[nzeromask] = contsum[nzeromask] / weights[nzeromask] expected = expected.reshape(outshape) # expected[np.isnan(expected)] = 0.0 # convention in the slices # print(axis, shiftcenter, depth, periodic, weighted) # print("expected:\n", expected) # print("recovered:\n", res.v) assert_rel_equal(expected, res.v, 4) yt-project-yt-f043ac8/yt/geometry/coordinates/tests/test_spherical_coordinates.py000066400000000000000000000032371510711153200305770ustar00rootroot00000000000000# Some tests for the Spherical coordinates handler import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.testing import fake_amr_ds # Our canonical tests are that we can access all of our fields and we can # compute our volume correctly. def test_spherical_coordinates(): # We're going to load up a simple AMR grid and check its volume # calculations and path length calculations. ds = fake_amr_ds(geometry="spherical") axes = ["r", "theta", "phi"] for i, axis in enumerate(axes): dd = ds.all_data() fi = ("index", axis) fd = ("index", f"d{axis}") ma = np.argmax(dd[fi]) assert_equal(dd[fi][ma] + dd[fd][ma] / 2.0, ds.domain_right_edge[i].d) mi = np.argmin(dd[fi]) assert_equal(dd[fi][mi] - dd[fd][mi] / 2.0, ds.domain_left_edge[i].d) assert_equal(dd[fd].max(), (ds.domain_width / ds.domain_dimensions)[i].d) # Note that we're using a lot of funny transforms to get to this, so we do # not expect to get actual agreement. This is a bit of a shame, but I # don't think it is avoidable as of right now. Real datasets will almost # certainly be correct, if this is correct to 3 decimel places. assert_almost_equal( dd["index", "cell_volume"].sum(dtype="float64"), (4.0 / 3.0) * np.pi * ds.domain_width[0] ** 3, ) assert_equal(dd["index", "path_element_r"], dd["index", "dr"]) assert_equal( dd["index", "path_element_theta"], dd["index", "r"] * dd["index", "dtheta"] ) assert_equal( dd["index", "path_element_phi"], (dd["index", "r"] * dd["index", "dphi"] * np.sin(dd["index", "theta"])), ) yt-project-yt-f043ac8/yt/geometry/fake_octree.pyx000066400000000000000000000050711510711153200221550ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Make a fake octree, deposit particle at every leaf """ cimport numpy as np from libc.stdlib cimport RAND_MAX, rand from .oct_visitors cimport cind import numpy as np from .oct_container cimport Oct, SparseOctreeContainer # Create a balanced octree by a random walk that recursively # subdivides def create_fake_octree(SparseOctreeContainer oct_handler, long max_noct, long max_level, np.ndarray[np.int32_t, ndim=1] ndd, np.ndarray[np.float64_t, ndim=1] dle, np.ndarray[np.float64_t, ndim=1] dre, float fsubdivide): cdef int[3] dd #hold the octant index cdef int[3] ind #hold the octant index cdef long i cdef long cur_leaf = 0 cdef np.ndarray[np.uint8_t, ndim=2] mask for i in range(3): ind[i] = 0 dd[i] = ndd[i] oct_handler.allocate_domains([max_noct]) parent = oct_handler.next_root(1, ind) parent.domain = 1 cur_leaf = 8 #we've added one parent... mask = np.ones((max_noct,8),dtype='uint8') while oct_handler.domains[0].n_assigned < max_noct: print("root: nocts ", oct_handler.domains[0].n_assigned) cur_leaf = subdivide(oct_handler, parent, ind, dd, cur_leaf, 0, max_noct, max_level, fsubdivide, mask) return cur_leaf cdef long subdivide(SparseOctreeContainer oct_handler, Oct *parent, int ind[3], int dd[3], long cur_leaf, long cur_level, long max_noct, long max_level, float fsubdivide, np.ndarray[np.uint8_t, ndim=2] mask): print("child", parent.file_ind, ind[0], ind[1], ind[2], cur_leaf, cur_level) cdef int ddr[3] cdef int ii cdef long i cdef float rf #random float from 0-1 if cur_level >= max_level: return cur_leaf if oct_handler.domains[0].n_assigned >= max_noct: return cur_leaf for i in range(3): ind[i] = ((rand() * 1.0 / RAND_MAX) * dd[i]) ddr[i] = 2 rf = rand() * 1.0 / RAND_MAX if rf > fsubdivide: ii = cind(ind[0], ind[1], ind[2]) if parent.children[ii] == NULL: cur_leaf += 7 oct = oct_handler.next_child(1, ind, parent) oct.domain = 1 cur_leaf = subdivide(oct_handler, oct, ind, ddr, cur_leaf, cur_level + 1, max_noct, max_level, fsubdivide, mask) return cur_leaf yt-project-yt-f043ac8/yt/geometry/geometry_enum.py000066400000000000000000000011121510711153200223650ustar00rootroot00000000000000import sys from enum import auto if sys.version_info >= (3, 11): from enum import StrEnum else: from yt._maintenance.backports import StrEnum # register all valid geometries class Geometry(StrEnum): CARTESIAN = auto() CYLINDRICAL = auto() POLAR = auto() SPHERICAL = auto() GEOGRAPHIC = auto() INTERNAL_GEOGRAPHIC = auto() SPECTRAL_CUBE = auto() def __str__(self): # Implemented for backward compatibility. if sys.version_info >= (3, 11): return super().__str__() else: return self.name.lower() yt-project-yt-f043ac8/yt/geometry/geometry_handler.py000066400000000000000000000410371510711153200230500ustar00rootroot00000000000000import abc import os import weakref import numpy as np from yt._maintenance.deprecation import issue_deprecation_warning from yt.config import ytcfg from yt.units._numpy_wrapper_functions import uconcatenate from yt.units.yt_array import YTArray from yt.utilities.exceptions import YTFieldNotFound from yt.utilities.io_handler import io_registry from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, parallel_root_only, ) class Index(ParallelAnalysisInterface, abc.ABC): """The base index class""" _unsupported_objects: tuple[str, ...] = () _index_properties: tuple[str, ...] = () def __init__(self, ds, dataset_type): ParallelAnalysisInterface.__init__(self) self.dataset = weakref.proxy(ds) self.ds = self.dataset self._initialize_state_variables() mylog.debug("Initializing data storage.") self._initialize_data_storage() mylog.debug("Setting up domain geometry.") self._setup_geometry() mylog.debug("Initializing data grid data IO") self._setup_data_io() # Note that this falls under the "geometry" object since it's # potentially quite expensive, and should be done with the indexing. mylog.debug("Detecting fields.") self._detect_output_fields() @abc.abstractmethod def _detect_output_fields(self): pass def _icoords_to_fcoords( self, icoords: np.ndarray, ires: np.ndarray, axes: tuple[int, ...] | None = None, ) -> tuple[np.ndarray, np.ndarray]: # What's the use of raising NotImplementedError for this, when it's an # abstract base class? Well, only *some* of the subclasses have it -- # and for those that *don't*, we should not be calling it -- and since # it's a semi-private method, it shouldn't be called outside of yt # machinery. So we shouldn't ever get here! raise NotImplementedError def _initialize_state_variables(self): self._parallel_locking = False self._data_file = None self._data_mode = None self.num_grids = None def _initialize_data_storage(self): if not ytcfg.get("yt", "serialize"): return fn = self.ds.storage_filename if fn is None: if os.path.isfile( os.path.join(self.directory, f"{self.ds.unique_identifier}.yt") ): fn = os.path.join(self.directory, f"{self.ds.unique_identifier}.yt") else: fn = os.path.join(self.directory, f"{self.dataset.basename}.yt") dir_to_check = os.path.dirname(fn) if dir_to_check == "": dir_to_check = "." # We have four options: # Writeable, does not exist : create, open as append # Writeable, does exist : open as append # Not writeable, does not exist : do not attempt to open # Not writeable, does exist : open as read-only exists = os.path.isfile(fn) if not exists: writeable = os.access(dir_to_check, os.W_OK) else: writeable = os.access(fn, os.W_OK) writeable = writeable and not ytcfg.get("yt", "only_deserialize") # We now have our conditional stuff self.comm.barrier() if not writeable and not exists: return if writeable: try: if not exists: self.__create_data_file(fn) self._data_mode = "a" except OSError: self._data_mode = None return else: self._data_mode = "r" self.__data_filename = fn self._data_file = h5py.File(fn, mode=self._data_mode) def __create_data_file(self, fn): # Note that this used to be parallel_root_only; it no longer is, # because we have better logic to decide who owns the file. f = h5py.File(fn, mode="a") f.close() def _setup_data_io(self): if getattr(self, "io", None) is not None: return self.io = io_registry[self.dataset_type](self.dataset) @parallel_root_only def print_stats(self): raise NotImplementedError(f"{type(self)} has no print_stats method.") @parallel_root_only def save_data( self, array, node, name, set_attr=None, force=False, passthrough=False ): """ Arbitrary numpy data will be saved to the region in the datafile described by *node* and *name*. If data file does not exist, it throws no error and simply does not save. """ if self._data_mode != "a": return try: node_loc = self._data_file[node] if name in node_loc and force: mylog.info("Overwriting node %s/%s", node, name) del self._data_file[node][name] elif name in node_loc and passthrough: return except Exception: pass myGroup = self._data_file["/"] for q in node.split("/"): if q: myGroup = myGroup.require_group(q) arr = myGroup.create_dataset(name, data=array) if set_attr is not None: for i, j in set_attr.items(): arr.attrs[i] = j self._data_file.flush() def _reload_data_file(self, *args, **kwargs): if self._data_file is None: return self._data_file.close() del self._data_file self._data_file = h5py.File(self.__data_filename, mode=self._data_mode) def get_data(self, node, name): """ Return the dataset with a given *name* located at *node* in the datafile. """ if self._data_file is None: return None if node[0] != "/": node = f"/{node}" myGroup = self._data_file["/"] for group in node.split("/"): if group: if group not in myGroup: return None myGroup = myGroup[group] if name not in myGroup: return None full_name = f"{node}/{name}" try: return self._data_file[full_name][:] except TypeError: return self._data_file[full_name] def _get_particle_type_counts(self): # this is implemented by subclasses raise NotImplementedError def _close_data_file(self): if self._data_file: self._data_file.close() del self._data_file self._data_file = None def _split_fields(self, fields): # This will split fields into either generated or read fields fields_to_read, fields_to_generate = [], [] for ftype, fname in fields: if fname in self.field_list or (ftype, fname) in self.field_list: fields_to_read.append((ftype, fname)) elif ( fname in self.ds.derived_field_list or (ftype, fname) in self.ds.derived_field_list ): fields_to_generate.append((ftype, fname)) else: raise YTFieldNotFound((ftype, fname), self.ds) return fields_to_read, fields_to_generate def _read_particle_fields(self, fields, dobj, chunk=None): if len(fields) == 0: return {}, [] fields_to_read, fields_to_generate = self._split_fields(fields) if len(fields_to_read) == 0: return {}, fields_to_generate selector = dobj.selector if chunk is None: self._identify_base_chunk(dobj) chunks = self._chunk_io(dobj, cache=False) fields_to_return = self.io._read_particle_selection( chunks, selector, fields_to_read ) return fields_to_return, fields_to_generate def _read_fluid_fields(self, fields, dobj, chunk=None): if len(fields) == 0: return {}, [] fields_to_read, fields_to_generate = self._split_fields(fields) if len(fields_to_read) == 0: return {}, fields_to_generate selector = dobj.selector if chunk is None: self._identify_base_chunk(dobj) chunk_size = dobj.size else: chunk_size = chunk.data_size fields_to_return = self.io._read_fluid_selection( self._chunk_io(dobj), selector, fields_to_read, chunk_size ) return fields_to_return, fields_to_generate def _chunk(self, dobj, chunking_style, ngz=0, **kwargs): # A chunk is either None or (grids, size) if dobj._current_chunk is None: self._identify_base_chunk(dobj) if ngz != 0 and chunking_style != "spatial": raise NotImplementedError if chunking_style == "all": return self._chunk_all(dobj, **kwargs) elif chunking_style == "spatial": return self._chunk_spatial(dobj, ngz, **kwargs) elif chunking_style == "io": return self._chunk_io(dobj, **kwargs) else: raise NotImplementedError def cacheable_property(func): # not quite equivalent to functools.cached_property # this decorator allows cached to be disabled via a self._cache flag attribute n = f"_{func.__name__}" @property def cacheable_func(self): if self._cache and getattr(self, n, None) is not None: return getattr(self, n) if self.data_size is None: tr = self._accumulate_values(n[1:]) else: tr = func(self) if self._cache: setattr(self, n, tr) return tr return cacheable_func class YTDataChunk: def __init__( self, dobj, chunk_type, objs, data_size=None, field_type=None, cache=False, fast_index=None, ): self.dobj = dobj self.chunk_type = chunk_type self.objs = objs self.data_size = data_size self._field_type = field_type self._cache = cache self._fast_index = fast_index def _accumulate_values(self, method): # We call this generically. It's somewhat slower, since we're doing # costly getattr functions, but this allows us to generalize. mname = f"select_{method}" arrs = [] for obj in self._fast_index or self.objs: f = getattr(obj, mname) arrs.append(f(self.dobj)) if method == "dtcoords": arrs = [arr[0] for arr in arrs] elif method == "tcoords": arrs = [arr[1] for arr in arrs] if len(arrs) == 0: self.data_size = 0 return np.empty((0, 3), dtype="float64") arrs = uconcatenate(arrs) self.data_size = arrs.shape[0] return arrs @cacheable_property def fcoords(self): if self._fast_index is not None: ci = self._fast_index.select_fcoords(self.dobj.selector, self.data_size) ci = YTArray(ci, units="code_length", registry=self.dobj.ds.unit_registry) return ci ci = np.empty((self.data_size, 3), dtype="float64") ci = YTArray(ci, units="code_length", registry=self.dobj.ds.unit_registry) if self.data_size == 0: return ci ind = 0 for obj in self._fast_index or self.objs: c = obj.select_fcoords(self.dobj) if c.shape[0] == 0: continue ci.d[ind : ind + c.shape[0], :] = c ind += c.shape[0] return ci @cacheable_property def icoords(self): if self._fast_index is not None: ci = self._fast_index.select_icoords(self.dobj.selector, self.data_size) return ci ci = np.empty((self.data_size, 3), dtype="int64") if self.data_size == 0: return ci ind = 0 for obj in self._fast_index or self.objs: c = obj.select_icoords(self.dobj) if c.shape[0] == 0: continue ci[ind : ind + c.shape[0], :] = c ind += c.shape[0] return ci @cacheable_property def fwidth(self): if self._fast_index is not None: ci = self._fast_index.select_fwidth(self.dobj.selector, self.data_size) ci = YTArray(ci, units="code_length", registry=self.dobj.ds.unit_registry) return ci ci = np.empty((self.data_size, 3), dtype="float64") ci = YTArray(ci, units="code_length", registry=self.dobj.ds.unit_registry) if self.data_size == 0: return ci ind = 0 for obj in self._fast_index or self.objs: c = obj.select_fwidth(self.dobj) if c.shape[0] == 0: continue ci.d[ind : ind + c.shape[0], :] = c ind += c.shape[0] return ci @cacheable_property def ires(self): if self._fast_index is not None: ci = self._fast_index.select_ires(self.dobj.selector, self.data_size) return ci ci = np.empty(self.data_size, dtype="int64") if self.data_size == 0: return ci ind = 0 for obj in self._fast_index or self.objs: c = obj.select_ires(self.dobj) if c.shape == 0: continue ci[ind : ind + c.size] = c ind += c.size return ci @cacheable_property def tcoords(self): self.dtcoords return self._tcoords @cacheable_property def dtcoords(self): ct = np.empty(self.data_size, dtype="float64") cdt = np.empty(self.data_size, dtype="float64") self._tcoords = ct # Se this for tcoords if self.data_size == 0: return cdt ind = 0 for obj in self._fast_index or self.objs: gdt, gt = obj.select_tcoords(self.dobj) if gt.size == 0: continue ct[ind : ind + gt.size] = gt cdt[ind : ind + gdt.size] = gdt ind += gt.size return cdt @cacheable_property def fcoords_vertex(self): nodes_per_elem = self.dobj.index.meshes[0].connectivity_indices.shape[1] dim = self.dobj.ds.dimensionality ci = np.empty((self.data_size, nodes_per_elem, dim), dtype="float64") ci = YTArray(ci, units="code_length", registry=self.dobj.ds.unit_registry) if self.data_size == 0: return ci ind = 0 for obj in self.objs: c = obj.select_fcoords_vertex(self.dobj) if c.shape[0] == 0: continue ci.d[ind : ind + c.shape[0], :, :] = c ind += c.shape[0] return ci class ChunkDataCache: def __init__(self, base_iter, preload_fields, geometry_handler, max_length=256): # At some point, max_length should instead become a heuristic function, # potentially looking at estimated memory usage. Note that this never # initializes the iterator; it assumes the iterator is already created, # and it calls next() on it. self.base_iter = base_iter.__iter__() self.queue = [] self.max_length = max_length self.preload_fields = preload_fields self.geometry_handler = geometry_handler self.cache = {} def __iter__(self): return self def __next__(self): if len(self.queue) == 0: for _ in range(self.max_length): try: self.queue.append(next(self.base_iter)) except StopIteration: break # If it's still zero ... if len(self.queue) == 0: raise StopIteration chunk = YTDataChunk(None, "cache", self.queue, cache=False) self.cache = ( self.geometry_handler.io._read_chunk_data(chunk, self.preload_fields) or {} ) g = self.queue.pop(0) g._initialize_cache(self.cache.pop(g.id, {})) return g def is_curvilinear(geo): # tell geometry is curvilinear or not issue_deprecation_warning( "the is_curvilear() function is deprecated. " "Instead, compare the geometry object directly with yt.geometry.geometry_enum.Geometry " "enum members, as for instance:\n" "if is_curvilinear(geometry):\n ...\n" "should be rewritten as:" "if geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL:\n ...", stacklevel=3, since="4.2", ) if geo in ["polar", "cylindrical", "spherical"]: return True else: return False yt-project-yt-f043ac8/yt/geometry/grid_container.pxd000066400000000000000000000035141510711153200226500ustar00rootroot00000000000000""" Matching points on the grid to specific grids """ import numpy as np cimport cython cimport numpy as np from libc.stdlib cimport free, malloc from yt.geometry cimport grid_visitors from yt.geometry.grid_visitors cimport ( GridTreeNode, GridTreeNodePadded, GridVisitorData, grid_visitor_function, ) from yt.utilities.lib.bitarray cimport bitarray from yt.utilities.lib.fp_utils cimport iclip from .selection_routines cimport SelectorObject, _ensure_code cdef class GridTree: cdef GridTreeNode *grids cdef GridTreeNode *root_grids cdef int num_grids cdef int num_root_grids cdef int num_leaf_grids cdef public bitarray mask cdef void setup_data(self, GridVisitorData *data) cdef void visit_grids(self, GridVisitorData *data, grid_visitor_function *func, SelectorObject selector) cdef void recursively_visit_grid(self, GridVisitorData *data, grid_visitor_function *func, SelectorObject selector, GridTreeNode *grid, np.uint8_t *buf = ?) cdef class MatchPointsToGrids: cdef int num_points cdef np.float64_t *xp cdef np.float64_t *yp cdef np.float64_t *zp cdef GridTree tree cdef np.int64_t *point_grids cdef np.uint8_t check_position(self, np.int64_t pt_index, np.float64_t x, np.float64_t y, np.float64_t z, GridTreeNode *grid) cdef np.uint8_t is_in_grid(self, np.float64_t x, np.float64_t y, np.float64_t z, GridTreeNode *grid) cdef extern from "platform_dep.h" nogil: double rint(double x) yt-project-yt-f043ac8/yt/geometry/grid_container.pyx000066400000000000000000000311771510711153200227030ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Matching points on the grid to specific grids """ import numpy as np cimport cython cimport numpy as np from yt.utilities.lib.bitarray cimport bitarray @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef GridTreeNode Grid_initialize(np.ndarray[np.float64_t, ndim=1] le, np.ndarray[np.float64_t, ndim=1] re, np.ndarray[np.int32_t, ndim=1] dims, int num_children, int level, int index): cdef GridTreeNode node cdef int i node.index = index node.level = level for i in range(3): node.left_edge[i] = le[i] node.right_edge[i] = re[i] node.dims[i] = dims[i] node.dds[i] = (re[i] - le[i])/dims[i] node.start_index[i] = rint(le[i] / node.dds[i]) node.num_children = num_children if num_children <= 0: node.children = NULL return node node.children = malloc( sizeof(GridTreeNode *) * num_children) for i in range(num_children): node.children[i] = NULL return node cdef class GridTree: @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __cinit__(self, int num_grids, np.ndarray[np.float64_t, ndim=2] left_edge, np.ndarray[np.float64_t, ndim=2] right_edge, np.ndarray[np.int32_t, ndim=2] dimensions, np.ndarray[np.int64_t, ndim=1] parent_ind, np.ndarray[np.int64_t, ndim=1] level, np.ndarray[np.int64_t, ndim=1] num_children): cdef int i, j, k cdef np.ndarray[np.int64_t, ndim=1] child_ptr child_ptr = np.zeros(num_grids, dtype='int64') self.num_grids = num_grids self.num_root_grids = 0 self.num_leaf_grids = 0 self.grids = malloc( sizeof(GridTreeNode) * num_grids) for i in range(num_grids): self.grids[i] = Grid_initialize(left_edge[i,:], right_edge[i,:], dimensions[i,:], num_children[i], level[i], i) if level[i] == 0: self.num_root_grids += 1 if num_children[i] == 0: self.num_leaf_grids += 1 self.root_grids = malloc( sizeof(GridTreeNode) * self.num_root_grids) k = 0 for i in range(num_grids): j = parent_ind[i] if j >= 0: self.grids[j].children[child_ptr[j]] = &self.grids[i] child_ptr[j] += 1 else: if k >= self.num_root_grids: raise RuntimeError self.root_grids[k] = self.grids[i] k = k + 1 def __init__(self, *args, **kwargs): self.mask = None def __iter__(self): yield self @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def return_tree_info(self): cdef int i, j levels = [] indices = [] nchild = [] children = [] for i in range(self.num_grids): childs = [] levels.append(self.grids[i].level) indices.append(self.grids[i].index) nchild.append(self.grids[i].num_children) for j in range(self.grids[i].num_children): childs.append(self.grids[i].children[j].index) children.append(childs) return indices, levels, nchild, children @property def grid_arrays(self): cdef GridTreeNodePadded[:] grids grids = \ ( self.grids) grids_basic = np.asarray(grids) # This next bit is necessary because as of 0.23.4, Cython can't make # nested dtypes automatically where you have a property that is # something like float[3]. So we unroll all of those, then re-roll # them in a new dtype. dtn = {} dt = grids_basic.dtype for name in dt.names: d, o = dt.fields[name] n = name if name.endswith("_x"): f = (d.char, 3) n = name[:-2] elif name.endswith("_y") or name.endswith("_z"): continue else: f = d.char dtn[n] = (f, o) return grids_basic.view(dtype=np.dtype(dtn)) cdef void setup_data(self, GridVisitorData *data): # Being handed a new GVD object, we initialize it to sane defaults. data.index = 0 data.global_index = 0 data.n_tuples = 0 data.child_tuples = NULL data.array = NULL data.ref_factor = 2 #### FIX THIS cdef void visit_grids(self, GridVisitorData *data, grid_visitor_function *func, SelectorObject selector): # This iterates over all root grids, given a selector+data, and then # visits each one and its children. cdef int i # Because of confusion about mapping of children to parents, we are # going to do this the stupid way for now. cdef GridTreeNode *grid cdef np.uint8_t *buf = NULL if self.mask is not None: buf = self.mask.buf for i in range(self.num_root_grids): grid = &self.root_grids[i] self.recursively_visit_grid(data, func, selector, grid, buf) grid_visitors.free_tuples(data) cdef void recursively_visit_grid(self, GridVisitorData *data, grid_visitor_function *func, SelectorObject selector, GridTreeNode *grid, np.uint8_t *buf = NULL): # Visit this grid and all of its child grids, with a given grid visitor # function. We early terminate if we are not selected by the selector. cdef int i data.grid = grid if selector.select_bbox(grid.left_edge, grid.right_edge) == 0: # Note that this does not increment the global_index. return grid_visitors.setup_tuples(data) selector.visit_grid_cells(data, func, buf) for i in range(grid.num_children): self.recursively_visit_grid(data, func, selector, grid.children[i], buf) def count(self, SelectorObject selector): # Use the counting grid visitor cdef GridVisitorData data self.setup_data(&data) cdef np.uint64_t size = 0 cdef int i for i in range(self.num_grids): size += (self.grids[i].dims[0] * self.grids[i].dims[1] * self.grids[i].dims[2]) cdef bitarray mask = bitarray(size) data.array = mask.buf self.visit_grids(&data, grid_visitors.mask_cells, selector) self.mask = mask size = 0 self.setup_data(&data) data.array = (&size) self.visit_grids(&data, grid_visitors.count_cells, selector) return size def select_icoords(self, SelectorObject selector, np.uint64_t size = -1): # Fill icoords with a selector cdef GridVisitorData data self.setup_data(&data) if size == -1: size = 0 data.array = (&size) self.visit_grids(&data, grid_visitors.count_cells, selector) cdef np.ndarray[np.int64_t, ndim=2] icoords icoords = np.empty((size, 3), dtype="int64") data.array = icoords.data self.visit_grids(&data, grid_visitors.icoords_cells, selector) return icoords def select_ires(self, SelectorObject selector, np.uint64_t size = -1): # Fill ires with a selector cdef GridVisitorData data self.setup_data(&data) if size == -1: size = 0 data.array = (&size) self.visit_grids(&data, grid_visitors.count_cells, selector) cdef np.ndarray[np.int64_t, ndim=1] ires ires = np.empty(size, dtype="int64") data.array = ires.data self.visit_grids(&data, grid_visitors.ires_cells, selector) return ires def select_fcoords(self, SelectorObject selector, np.uint64_t size = -1): # Fill fcoords with a selector cdef GridVisitorData data self.setup_data(&data) if size == -1: size = 0 data.array = (&size) self.visit_grids(&data, grid_visitors.count_cells, selector) cdef np.ndarray[np.float64_t, ndim=2] fcoords fcoords = np.empty((size, 3), dtype="float64") data.array = fcoords.data self.visit_grids(&data, grid_visitors.fcoords_cells, selector) return fcoords def select_fwidth(self, SelectorObject selector, np.uint64_t size = -1): # Fill fwidth with a selector cdef GridVisitorData data self.setup_data(&data) if size == -1: size = 0 data.array = (&size) self.visit_grids(&data, grid_visitors.count_cells, selector) cdef np.ndarray[np.float64_t, ndim=2] fwidth fwidth = np.empty((size, 3), dtype="float64") data.array = fwidth.data self.visit_grids(&data, grid_visitors.fwidth_cells, selector) return fwidth cdef class MatchPointsToGrids: @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __cinit__(self, GridTree tree, int num_points, np.ndarray[np.float64_t, ndim=1] x, np.ndarray[np.float64_t, ndim=1] y, np.ndarray[np.float64_t, ndim=1] z): cdef int i self.num_points = num_points self.xp = malloc( sizeof(np.float64_t) * num_points) self.yp = malloc( sizeof(np.float64_t) * num_points) self.zp = malloc( sizeof(np.float64_t) * num_points) self.point_grids = malloc( sizeof(np.int64_t) * num_points) for i in range(num_points): self.xp[i] = x[i] self.yp[i] = y[i] self.zp[i] = z[i] self.point_grids[i] = -1 self.tree = tree @cython.boundscheck(False) @cython.wraparound(False) def find_points_in_tree(self): cdef np.ndarray[np.int64_t, ndim=1] pt_grids cdef int i, j cdef np.uint8_t in_grid pt_grids = np.zeros(self.num_points, dtype='int64') for i in range(self.num_points): in_grid = 0 for j in range(self.tree.num_root_grids): if not in_grid: in_grid = self.check_position(i, self.xp[i], self.yp[i], self.zp[i], &self.tree.root_grids[j]) for i in range(self.num_points): pt_grids[i] = self.point_grids[i] return pt_grids @cython.boundscheck(False) @cython.wraparound(False) cdef np.uint8_t check_position(self, np.int64_t pt_index, np.float64_t x, np.float64_t y, np.float64_t z, GridTreeNode * grid): cdef int i cdef np.uint8_t in_grid in_grid = self.is_in_grid(x, y, z, grid) if in_grid: if grid.num_children > 0: in_grid = 0 for i in range(grid.num_children): if not in_grid: in_grid = self.check_position(pt_index, x, y, z, grid.children[i]) if not in_grid: self.point_grids[pt_index] = grid.index in_grid = 1 else: self.point_grids[pt_index] = grid.index in_grid = 1 return in_grid @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.uint8_t is_in_grid(self, np.float64_t x, np.float64_t y, np.float64_t z, GridTreeNode * grid): if x >= grid.right_edge[0]: return 0 if y >= grid.right_edge[1]: return 0 if z >= grid.right_edge[2]: return 0 if x < grid.left_edge[0]: return 0 if y < grid.left_edge[1]: return 0 if z < grid.left_edge[2]: return 0 return 1 yt-project-yt-f043ac8/yt/geometry/grid_geometry_handler.py000066400000000000000000000443001510711153200240510ustar00rootroot00000000000000import abc import weakref from collections import defaultdict import numpy as np from yt.arraytypes import blankRecordArray from yt.config import ytcfg from yt.fields.derived_field import ValidateSpatial from yt.fields.field_detector import FieldDetector from yt.funcs import ensure_numpy_array, iter_fields from yt.geometry.geometry_handler import ChunkDataCache, Index, YTDataChunk from yt.utilities.definitions import MAXLEVEL from yt.utilities.logger import ytLogger as mylog from .grid_container import GridTree, MatchPointsToGrids class GridIndex(Index, abc.ABC): """The index class for patch and block AMR datasets.""" float_type = "float64" _preload_implemented = False _index_properties = ( "grid_left_edge", "grid_right_edge", "grid_levels", "grid_particle_count", "grid_dimensions", ) def _setup_geometry(self): mylog.debug("Counting grids.") self._count_grids() mylog.debug("Initializing grid arrays.") self._initialize_grid_arrays() mylog.debug("Parsing index.") self._parse_index() mylog.debug("Constructing grid objects.") self._populate_grid_objects() mylog.debug("Re-examining index") self._initialize_level_stats() @abc.abstractmethod def _count_grids(self): pass @abc.abstractmethod def _parse_index(self): pass @abc.abstractmethod def _populate_grid_objects(self): pass def __del__(self): del self.grid_dimensions del self.grid_left_edge del self.grid_right_edge del self.grid_levels del self.grid_particle_count del self.grids @property def parameters(self): return self.dataset.parameters def _detect_output_fields_backup(self): # grab fields from backup file as well, if present return def select_grids(self, level): """ Returns an array of grids at *level*. """ return self.grids[self.grid_levels.flat == level] def get_levels(self): for level in range(self.max_level + 1): yield self.select_grids(level) def _initialize_grid_arrays(self): mylog.debug("Allocating arrays for %s grids", self.num_grids) self.grid_dimensions = np.ones((self.num_grids, 3), "int32") self.grid_left_edge = self.ds.arr( np.zeros((self.num_grids, 3), self.float_type), "code_length" ) self.grid_right_edge = self.ds.arr( np.ones((self.num_grids, 3), self.float_type), "code_length" ) self.grid_levels = np.zeros((self.num_grids, 1), "int32") self.grid_particle_count = np.zeros((self.num_grids, 1), "int32") def clear_all_data(self): """ This routine clears all the data currently being held onto by the grids and the data io handler. """ for g in self.grids: g.clear_data() self.io.queue.clear() def get_smallest_dx(self): """ Returns (in code units) the smallest cell size in the simulation. """ return self.select_grids(self.grid_levels.max())[0].dds[:].min() def _get_particle_type_counts(self): return {self.ds.particle_types_raw[0]: self.grid_particle_count.sum()} def _initialize_level_stats(self): # Now some statistics: # 0 = number of grids # 1 = number of cells # 2 = blank desc = {"names": ["numgrids", "numcells", "level"], "formats": ["int64"] * 3} self.level_stats = blankRecordArray(desc, MAXLEVEL) self.level_stats["level"] = list(range(MAXLEVEL)) self.level_stats["numgrids"] = [0 for i in range(MAXLEVEL)] self.level_stats["numcells"] = [0 for i in range(MAXLEVEL)] for level in range(self.max_level + 1): self.level_stats[level]["numgrids"] = np.sum(self.grid_levels == level) li = self.grid_levels[:, 0] == level self.level_stats[level]["numcells"] = ( self.grid_dimensions[li, :].prod(axis=1).sum() ) @property def grid_corners(self): return np.array( [ [ self.grid_left_edge[:, 0], self.grid_left_edge[:, 1], self.grid_left_edge[:, 2], ], [ self.grid_right_edge[:, 0], self.grid_left_edge[:, 1], self.grid_left_edge[:, 2], ], [ self.grid_right_edge[:, 0], self.grid_right_edge[:, 1], self.grid_left_edge[:, 2], ], [ self.grid_left_edge[:, 0], self.grid_right_edge[:, 1], self.grid_left_edge[:, 2], ], [ self.grid_left_edge[:, 0], self.grid_left_edge[:, 1], self.grid_right_edge[:, 2], ], [ self.grid_right_edge[:, 0], self.grid_left_edge[:, 1], self.grid_right_edge[:, 2], ], [ self.grid_right_edge[:, 0], self.grid_right_edge[:, 1], self.grid_right_edge[:, 2], ], [ self.grid_left_edge[:, 0], self.grid_right_edge[:, 1], self.grid_right_edge[:, 2], ], ], dtype="float64", ) def lock_grids_to_parents(self): r"""This function locks grid edges to their parents. This is useful in cases where the grid structure may be somewhat irregular, or where setting the left and right edges is a lossy process. It is designed to correct situations where left/right edges may be set slightly incorrectly, resulting in discontinuities in images and the like. """ mylog.info("Locking grids to parents.") for i, g in enumerate(self.grids): si = g.get_global_startindex() g.LeftEdge = self.ds.domain_left_edge + g.dds * si g.RightEdge = g.LeftEdge + g.ActiveDimensions * g.dds self.grid_left_edge[i, :] = g.LeftEdge self.grid_right_edge[i, :] = g.RightEdge def print_stats(self): """ Prints out (stdout) relevant information about the simulation """ header = "{:>3}\t{:>6}\t{:>14}\t{:>14}".format( "level", "# grids", "# cells", "# cells^3" ) print(header) print(f"{len(header.expandtabs()) * '-'}") for level in range(MAXLEVEL): if (self.level_stats["numgrids"][level]) == 0: continue print( "% 3i\t% 6i\t% 14i\t% 14i" % ( level, self.level_stats["numgrids"][level], self.level_stats["numcells"][level], np.ceil(self.level_stats["numcells"][level] ** (1.0 / 3)), ) ) dx = self.select_grids(level)[0].dds[0] print("-" * 46) print( " \t% 6i\t% 14i" % (self.level_stats["numgrids"].sum(), self.level_stats["numcells"].sum()) ) print("\n") try: print(f"z = {self['CosmologyCurrentRedshift']:0.8f}") except Exception: pass print( "t = {:0.8e} = {:0.8e} = {:0.8e}".format( self.ds.current_time.in_units("code_time"), self.ds.current_time.in_units("s"), self.ds.current_time.in_units("yr"), ) ) print("\nSmallest Cell:") for item in ("Mpc", "pc", "AU", "cm"): print(f"\tWidth: {dx.in_units(item):0.3e}") def _find_field_values_at_points(self, fields, coords): r"""Find the value of fields at a set of coordinates. Returns the values [field1, field2,...] of the fields at the given (x, y, z) points. Returns a numpy array of field values cross coords """ coords = self.ds.arr(ensure_numpy_array(coords), "code_length") grids = self._find_points(coords[:, 0], coords[:, 1], coords[:, 2])[0] fields = list(iter_fields(fields)) mark = np.zeros(3, dtype="int64") out = [] # create point -> grid mapping grid_index = {} for coord_index, grid in enumerate(grids): if grid not in grid_index: grid_index[grid] = [] grid_index[grid].append(coord_index) out = [] for field in fields: funit = self.ds._get_field_info(field).units out.append(self.ds.arr(np.empty(len(coords)), funit)) for grid in grid_index: cellwidth = (grid.RightEdge - grid.LeftEdge) / grid.ActiveDimensions for field_index, field in enumerate(fields): for coord_index in grid_index[grid]: mark = (coords[coord_index, :] - grid.LeftEdge) / cellwidth mark = np.array(mark, dtype="int64") out[field_index][coord_index] = grid[field][ mark[0], mark[1], mark[2] ] if len(fields) == 1: return out[0] return out def _find_points(self, x, y, z): """ Returns the (objects, indices) of leaf grids containing a number of (x,y,z) points """ x = ensure_numpy_array(x) y = ensure_numpy_array(y) z = ensure_numpy_array(z) if not len(x) == len(y) == len(z): raise ValueError("Arrays of indices must be of the same size") grid_tree = self._get_grid_tree() pts = MatchPointsToGrids(grid_tree, len(x), x, y, z) ind = pts.find_points_in_tree() return self.grids[ind], ind def _get_grid_tree(self): left_edge = self.ds.arr(np.zeros((self.num_grids, 3)), "code_length") right_edge = self.ds.arr(np.zeros((self.num_grids, 3)), "code_length") level = np.zeros((self.num_grids), dtype="int64") parent_ind = np.zeros((self.num_grids), dtype="int64") num_children = np.zeros((self.num_grids), dtype="int64") dimensions = np.zeros((self.num_grids, 3), dtype="int32") for i, grid in enumerate(self.grids): left_edge[i, :] = grid.LeftEdge right_edge[i, :] = grid.RightEdge level[i] = grid.Level if grid.Parent is None: parent_ind[i] = -1 else: parent_ind[i] = grid.Parent.id - grid.Parent._id_offset num_children[i] = np.int64(len(grid.Children)) dimensions[i, :] = grid.ActiveDimensions return GridTree( self.num_grids, left_edge, right_edge, dimensions, parent_ind, level, num_children, ) def convert(self, unit): return self.dataset.conversion_factors[unit] def _identify_base_chunk(self, dobj): fast_index = None if dobj._type_name == "grid": dobj._chunk_info = np.empty(1, dtype="object") dobj._chunk_info[0] = weakref.proxy(dobj) elif getattr(dobj, "_grids", None) is None: gi = dobj.selector.select_grids( self.grid_left_edge, self.grid_right_edge, self.grid_levels ) if any(g.filename is not None for g in self.grids[gi]): _gsort = _grid_sort_mixed else: _gsort = _grid_sort_id grids = sorted(self.grids[gi], key=_gsort) dobj._chunk_info = np.empty(len(grids), dtype="object") for i, g in enumerate(grids): dobj._chunk_info[i] = g # These next two lines, when uncommented, turn "on" the fast index. # if dobj._type_name != "grid": # fast_index = self._get_grid_tree() if getattr(dobj, "size", None) is None: dobj.size = self._count_selection(dobj, fast_index=fast_index) if getattr(dobj, "shape", None) is None: dobj.shape = (dobj.size,) dobj._current_chunk = list( self._chunk_all(dobj, cache=False, fast_index=fast_index) )[0] def _count_selection(self, dobj, grids=None, fast_index=None): if fast_index is not None: return fast_index.count(dobj.selector) if grids is None: grids = dobj._chunk_info count = sum(g.count(dobj.selector) for g in grids) return count def _chunk_all(self, dobj, cache=True, fast_index=None): gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) fast_index = fast_index or getattr(dobj._current_chunk, "_fast_index", None) yield YTDataChunk(dobj, "all", gobjs, dobj.size, cache, fast_index=fast_index) def _chunk_spatial(self, dobj, ngz, sort=None, preload_fields=None): gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) if sort in ("+level", "level"): giter = sorted(gobjs, key=lambda g: g.Level) elif sort == "-level": giter = sorted(gobjs, key=lambda g: -g.Level) elif sort is None: giter = gobjs if preload_fields is None: preload_fields = [] preload_fields, _ = self._split_fields(preload_fields) if self._preload_implemented and len(preload_fields) > 0 and ngz == 0: giter = ChunkDataCache(list(giter), preload_fields, self) for og in giter: if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og size = self._count_selection(dobj, [og]) if size == 0: continue # We don't want to cache any of the masks or icoords or fcoords for # individual grids. yield YTDataChunk(dobj, "spatial", [g], size, cache=False) _grid_chunksize = 1000 def _chunk_io( self, dobj, cache=True, local_only=False, preload_fields=None, chunk_sizing="auto", ): # local_only is only useful for inline datasets and requires # implementation by subclasses. if preload_fields is None: preload_fields = [] preload_fields, _ = self._split_fields(preload_fields) gfiles = defaultdict(list) gobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) fast_index = dobj._current_chunk._fast_index for g in gobjs: # Force to be a string because sometimes g.filename is None. gfiles[str(g.filename)].append(g) # We can apply a heuristic here to make sure we aren't loading too # many grids all at once. if chunk_sizing == "auto": chunk_ngrids = len(gobjs) if chunk_ngrids > 0: nproc = int(ytcfg.get("yt", "internals", "global_parallel_size")) chunking_factor = np.int64( np.ceil(self._grid_chunksize * nproc / chunk_ngrids) ) size = max(self._grid_chunksize // chunking_factor, 1) else: size = self._grid_chunksize elif chunk_sizing == "config_file": size = ytcfg.get("yt", "chunk_size") elif chunk_sizing == "just_one": size = 1 elif chunk_sizing == "old": size = self._grid_chunksize else: raise RuntimeError( f"{chunk_sizing} is an invalid value for the 'chunk_sizing' argument." ) for fn in sorted(gfiles): gs = gfiles[fn] for grids in (gs[pos : pos + size] for pos in range(0, len(gs), size)): dc = YTDataChunk( dobj, "io", grids, self._count_selection(dobj, grids), cache=cache, fast_index=fast_index, ) # We allow four full chunks to be included. with self.io.preload(dc, preload_fields, 4.0 * size): yield dc def _icoords_to_fcoords( self, icoords: np.ndarray, ires: np.ndarray, axes: tuple[int, ...] | None = None, ) -> tuple[np.ndarray, np.ndarray]: """ Accepts icoords and ires and returns appropriate fcoords and fwidth. Mostly useful for cases where we have irregularly spaced or structured grids. """ dds = self.ds.domain_width[axes,] / ( self.ds.domain_dimensions[axes,] * self.ds.refine_by ** ires[:, None] ) pos = (0.5 + icoords) * dds + self.ds.domain_left_edge[axes,] return pos, dds def _add_mesh_sampling_particle_field(self, deposit_field, ftype, ptype): units = self.ds.field_info[ftype, deposit_field].units take_log = self.ds.field_info[ftype, deposit_field].take_log field_name = f"cell_{ftype}_{deposit_field}" def _mesh_sampling_particle_field(field, data): pos = data[ptype, "particle_position"] field_values = data[ftype, deposit_field] if isinstance(data, FieldDetector): return np.zeros(pos.shape[0]) i, j, k = np.floor((pos - data.LeftEdge) / data.dds).astype("int64").T # Make sure all particles are within the current grid, otherwise return nan maxi, maxj, maxk = field_values.shape mask = (i < maxi) & (j < maxj) & (k < maxk) mask &= (i >= 0) & (j >= 0) & (k >= 0) result = np.full(len(pos), np.nan, dtype="float64") if result.shape[0] > 0: result[mask] = field_values[i[mask], j[mask], k[mask]] return data.ds.arr(result, field_values.units) self.ds.add_field( (ptype, field_name), function=_mesh_sampling_particle_field, sampling_type="particle", units=units, take_log=take_log, validators=[ValidateSpatial()], ) def _grid_sort_id(g): return g.id def _grid_sort_mixed(g): if g.filename is None: return str(g.id) return g.filename yt-project-yt-f043ac8/yt/geometry/grid_visitors.pxd000066400000000000000000000046501510711153200225520ustar00rootroot00000000000000""" Grid visitor definitions file """ cimport numpy as np cdef struct GridTreeNode: np.int32_t num_children np.int32_t level np.int64_t index np.float64_t left_edge[3] np.float64_t right_edge[3] GridTreeNode **children np.int64_t start_index[3] np.int32_t dims[3] np.float64_t dds[3] cdef struct GridTreeNodePadded: np.int32_t num_children np.int32_t level np.int64_t index np.float64_t left_edge_x np.float64_t left_edge_y np.float64_t left_edge_z np.float64_t right_edge_x np.float64_t right_edge_y np.float64_t right_edge_z np.int64_t children_pointers np.int64_t start_index_x np.int64_t start_index_y np.int64_t start_index_z np.int32_t dims_x np.int32_t dims_y np.int32_t dims_z np.float64_t dds_x np.float64_t dds_y np.float64_t dds_z cdef struct GridVisitorData: GridTreeNode *grid np.uint64_t index np.uint64_t global_index np.int64_t pos[3] # position in ints int n_tuples int **child_tuples # [N_child][6], where 0-1 are x_start, x_end, etc. void *array int ref_factor # This may change on a grid-by-grid basis # It is the number of cells a child grid has per dimension # in a cell of this grid. cdef void free_tuples(GridVisitorData *data) nogil cdef void setup_tuples(GridVisitorData *data) nogil cdef np.uint8_t check_child_masked(GridVisitorData *data) nogil ctypedef void grid_visitor_function(GridVisitorData *data, np.uint8_t selected) nogil # This is similar in spirit to the way oct visitor functions work. However, # there are a few important differences. Because the grid objects are expected # to be bigger, we don't need to pass them along -- we will not be recursively # visiting. So the GridVisitorData will be updated in between grids. # Furthermore, we're only going to use them for a much smaller subset of # operations. All child mask evaluation is going to be conducted inside the # outermost level of the visitor function, and visitor functions will receive # information about whether they have been selected and whether they are # covered by child cells. cdef grid_visitor_function count_cells cdef grid_visitor_function mask_cells cdef grid_visitor_function icoords_cells cdef grid_visitor_function ires_cells cdef grid_visitor_function fcoords_cells cdef grid_visitor_function fwidth_cells yt-project-yt-f043ac8/yt/geometry/grid_visitors.pyx000066400000000000000000000117221510711153200225750ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Grid visitor functions """ cimport cython cimport numpy as np from libc.stdlib cimport free, malloc from yt.utilities.lib.bitarray cimport ba_set_value from yt.utilities.lib.fp_utils cimport iclip cdef void free_tuples(GridVisitorData *data) noexcept nogil: # This wipes out the tuples, which is necessary since they are # heap-allocated cdef int i if data.child_tuples == NULL: return for i in range(data.n_tuples): free(data.child_tuples[i]) free(data.child_tuples) data.child_tuples = NULL data.n_tuples = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void setup_tuples(GridVisitorData *data) noexcept nogil: # This sets up child-mask tuples. Rather than a single mask that covers # everything, we instead allocate pairs of integers that are start/stop # positions for child masks. This may not be considerably more efficient # memory-wise, but it is easier to keep and save when going through # multiple grids and selectors. cdef int i, j cdef np.int64_t si, ei cdef GridTreeNode *g cdef GridTreeNode *c free_tuples(data) g = data.grid data.child_tuples = malloc(sizeof(int*) * g.num_children) for i in range(g.num_children): c = g.children[i] data.child_tuples[i] = malloc(sizeof(int) * 6) # Now we fill them in for j in range(3): si = (c.start_index[j] / data.ref_factor) - g.start_index[j] ei = si + c.dims[j]/data.ref_factor - 1 data.child_tuples[i][j*2+0] = iclip(si, 0, g.dims[j] - 1) data.child_tuples[i][j*2+1] = iclip(ei, 0, g.dims[j] - 1) data.n_tuples = g.num_children @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.uint8_t check_child_masked(GridVisitorData *data) noexcept nogil: # This simply checks if we're inside any of the tuples. Probably not the # most efficient way, but the GVD* passed in has a position affiliated with # it, and we can very easily look for that inside here. cdef int i, j, k cdef int *tup for i in range(data.n_tuples): # k is if we're inside a given child tuple. We check each one # individually, and invalidate if we're outside. k = 1 tup = data.child_tuples[i] for j in range(3): # Check if pos is outside in any of the three dimensions if data.pos[j] < tup[j*2+0] or data.pos[j] > tup[j*2+1]: k = 0 break if k == 1: return 1 # Return 1 for child masked return 0 # Only return 0 if it doesn't match any of the children @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void count_cells(GridVisitorData *data, np.uint8_t selected) noexcept nogil: # Simply increment for each one, if we've selected it. if selected == 0: return cdef np.uint64_t *count = data.array count[0] += 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void mask_cells(GridVisitorData *data, np.uint8_t selected) noexcept nogil: # Set our bitarray -- we're creating a mask -- if we are selected. if selected == 0: return cdef np.uint8_t *mask = data.array ba_set_value(mask, data.global_index, 1) # No need to increment anything. @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void icoords_cells(GridVisitorData *data, np.uint8_t selected) noexcept nogil: # Nice and easy icoord setter. if selected == 0: return cdef int i cdef np.int64_t *icoords = data.array for i in range(3): icoords[data.index * 3 + i] = data.pos[i] + data.grid.start_index[i] data.index += 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void ires_cells(GridVisitorData *data, np.uint8_t selected) noexcept nogil: # Fill with the level value. if selected == 0: return cdef np.int64_t *ires = data.array ires[data.index] = data.grid.level data.index += 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void fwidth_cells(GridVisitorData *data, np.uint8_t selected) noexcept nogil: # Fill with our dds. if selected == 0: return cdef int i cdef np.float64_t *fwidth = data.array for i in range(3): fwidth[data.index * 3 + i] = data.grid.dds[i] data.index += 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void fcoords_cells(GridVisitorData *data, np.uint8_t selected) noexcept nogil: # Simple cell-centered position filling. if selected == 0: return cdef int i cdef np.float64_t *fcoords = data.array for i in range(3): fcoords[data.index * 3 + i] = data.grid.left_edge[i] + \ (0.5 + data.pos[i])*data.grid.dds[i] data.index += 1 yt-project-yt-f043ac8/yt/geometry/oct_container.pxd000066400000000000000000000076651510711153200225230ustar00rootroot00000000000000""" Oct definitions file """ cimport cython cimport numpy as np from libc.math cimport floor from libc.stdlib cimport bsearch, free, malloc, qsort, realloc from yt.geometry cimport oct_visitors, selection_routines from yt.utilities.lib.allocation_container cimport AllocationContainer, ObjectPool from yt.utilities.lib.fp_utils cimport * from .oct_visitors cimport Oct, OctInfo, OctVisitor, cind cdef int ORDER_MAX cdef struct OctKey: np.int64_t key Oct *node # These next two are for particle sparse octrees. np.int64_t *indices np.int64_t pcount cdef struct OctList cdef struct OctList: OctList *next Oct *o # NOTE: This object *has* to be the same size as the AllocationContainer # object. There's an assert in the __cinit__ function. cdef struct OctAllocationContainer: np.uint64_t n np.uint64_t n_assigned np.uint64_t offset np.int64_t con_id # container id Oct *my_objs cdef class OctObjectPool(ObjectPool): cdef inline OctAllocationContainer *get_cont(self, int i): return (&self.containers[i]) cdef OctList *OctList_append(OctList *list, Oct *o) cdef int OctList_count(OctList *list) cdef void OctList_delete(OctList *list) cdef class OctreeContainer: cdef public OctObjectPool domains cdef Oct ****root_mesh cdef int partial_coverage cdef int level_offset cdef int nn[3] cdef np.uint8_t nz cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] cdef public np.int64_t nocts cdef public int num_domains cdef Oct *get(self, np.float64_t ppos[3], OctInfo *oinfo = ?, int max_level = ?) noexcept nogil cdef int get_root(self, int ind[3], Oct **o) noexcept nogil cdef Oct **neighbors(self, OctInfo *oinfo, np.int64_t *nneighbors, Oct *o, bint periodicity[3]) # This function must return the offset from global-to-local domains; i.e., # AllocationContainer.offset if such a thing exists. cdef np.int64_t get_domain_offset(self, int domain_id) cdef void visit_all_octs(self, selection_routines.SelectorObject selector, OctVisitor visitor, int vc = ?, np.int64_t *indices = ?) cdef Oct *next_root(self, int domain_id, int ind[3]) cdef Oct *next_child(self, int domain_id, int ind[3], Oct *parent) except? NULL cdef void append_domain(self, np.int64_t domain_count) # The fill_style is the ordering, C or F, of the octs in the file. "o" # corresponds to C, and "r" is for Fortran. cdef public object fill_style cdef public int max_level cpdef void fill_level( self, const int level, const np.uint8_t[::1] level_inds, const np.uint8_t[::1] cell_inds, const np.int64_t[::1] file_inds, dict dest_fields, dict source_fields, np.int64_t offset = ? ) cpdef int fill_level_with_domain( self, const int level, const np.uint8_t[::1] level_inds, const np.uint8_t[::1] cell_inds, const np.int64_t[::1] file_inds, const np.int32_t[::1] domain_inds, dict dest_fields, dict source_fields, const np.int32_t domain, np.int64_t offset = ? ) cdef class SparseOctreeContainer(OctreeContainer): cdef OctKey *root_nodes cdef void *tree_root cdef int num_root cdef int max_root cdef void key_to_ipos(self, np.int64_t key, np.int64_t pos[3]) cdef np.int64_t ipos_to_key(self, int pos[3]) noexcept nogil cdef class RAMSESOctreeContainer(SparseOctreeContainer): pass cdef extern from "tsearch.h" nogil: void *tsearch(const void *key, void **rootp, int (*compar)(const void *, const void *)) void *tfind(const void *key, const void **rootp, int (*compar)(const void *, const void *)) void *tdelete(const void *key, void **rootp, int (*compar)(const void *, const void *)) yt-project-yt-f043ac8/yt/geometry/oct_container.pyx000066400000000000000000001346211510711153200225410ustar00rootroot00000000000000# distutils: sources = yt/utilities/lib/tsearch.c # distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Oct container """ cimport cython cimport numpy as np import numpy as np from libc.math cimport floor from .oct_visitors cimport ( NeighbourCellIndexVisitor, NeighbourCellVisitor, OctPadded, StoreIndex, ) from .selection_routines cimport AlwaysSelector, SelectorObject ORDER_MAX = 20 _ORDER_MAX = ORDER_MAX cdef extern from "stdlib.h": # NOTE that size_t might not be int void *alloca(int) # Here is the strategy for RAMSES containers: # * Read each domain individually, creating *all* octs found in that domain # file, even if they reside on other CPUs. # * Only allocate octs that reside on >= domain # * For all octs, insert into tree, which may require traversing existing # octs # * Note that this does not allow one component of an ObjectPool (an # AllocationContainer) to exactly be a chunk, but it is close. For IO # chunking, we can theoretically examine those octs that live inside a # given allocator. cdef class OctreeContainer: def __init__(self, oct_domain_dimensions, domain_left_edge, domain_right_edge, partial_coverage = 0, num_zones = 2): # This will just initialize the root mesh octs self.nz = num_zones self.partial_coverage = partial_coverage cdef int i for i in range(3): self.nn[i] = oct_domain_dimensions[i] self.num_domains = 0 self.level_offset = 0 self.domains = OctObjectPool() self.nocts = 0 # Increment when initialized for i in range(3): self.DLE[i] = domain_left_edge[i] #0 self.DRE[i] = domain_right_edge[i] #num_grid self._initialize_root_mesh() self.fill_style = "o" def _initialize_root_mesh(self): self.root_mesh = malloc(sizeof(void*) * self.nn[0]) for i in range(self.nn[0]): self.root_mesh[i] = malloc(sizeof(void*) * self.nn[1]) for j in range(self.nn[1]): self.root_mesh[i][j] = malloc(sizeof(void*) * self.nn[2]) for k in range(self.nn[2]): self.root_mesh[i][j][k] = NULL @property def oct_arrays(self): return self.domains.to_arrays() @classmethod def load_octree(cls, header): cdef np.ndarray[np.uint8_t, ndim=1] ref_mask ref_mask = header['octree'] cdef OctreeContainer obj = cls(header['dims'], header['left_edge'], header['right_edge'], num_zones = header['num_zones'], partial_coverage = header['partial_coverage']) # NOTE: We do not allow domain/file indices to be specified. cdef SelectorObject selector = AlwaysSelector(None) cdef oct_visitors.LoadOctree visitor visitor = oct_visitors.LoadOctree(obj, -1) cdef int i, j, k visitor.global_index = -1 visitor.level = 0 visitor.nz = visitor.nzones = 1 visitor.max_level = 0 assert(ref_mask.shape[0] / float(visitor.nzones) == (ref_mask.shape[0]/float(visitor.nzones))) obj.allocate_domains([ref_mask.shape[0] / visitor.nzones]) cdef np.float64_t pos[3] cdef np.float64_t dds[3] # This dds is the oct-width for i in range(3): dds[i] = (obj.DRE[i] - obj.DLE[i]) / obj.nn[i] # Pos is the center of the octs cdef OctAllocationContainer *cur = obj.domains.get_cont(0) cdef Oct *o cdef np.uint64_t nfinest = 0 visitor.ref_mask = ref_mask visitor.octs = cur.my_objs visitor.nocts = &cur.n_assigned visitor.nfinest = &nfinest pos[0] = obj.DLE[0] + dds[0]/2.0 for i in range(obj.nn[0]): pos[1] = obj.DLE[1] + dds[1]/2.0 for j in range(obj.nn[1]): pos[2] = obj.DLE[2] + dds[2]/2.0 for k in range(obj.nn[2]): if obj.root_mesh[i][j][k] != NULL: raise RuntimeError o = &cur.my_objs[cur.n_assigned] o.domain_ind = o.file_ind = 0 o.domain = 1 obj.root_mesh[i][j][k] = o cur.n_assigned += 1 visitor.pos[0] = i visitor.pos[1] = j visitor.pos[2] = k # Always visit covered selector.recursively_visit_octs( obj.root_mesh[i][j][k], pos, dds, 0, visitor, 1) pos[2] += dds[2] pos[1] += dds[1] pos[0] += dds[0] obj.nocts = cur.n_assigned if obj.nocts * visitor.nz != ref_mask.size: raise KeyError(ref_mask.size, obj.nocts, obj.nz, obj.partial_coverage, visitor.nzones) obj.max_level = visitor.max_level return obj def __dealloc__(self): if self.root_mesh == NULL: return for i in range(self.nn[0]): if self.root_mesh[i] == NULL: continue for j in range(self.nn[1]): if self.root_mesh[i][j] == NULL: continue free(self.root_mesh[i][j]) if self.root_mesh[i] == NULL: continue free(self.root_mesh[i]) free(self.root_mesh) @cython.cdivision(True) cdef void visit_all_octs(self, SelectorObject selector, OctVisitor visitor, int vc = -1, np.int64_t *indices = NULL): cdef int i, j, k if vc == -1: vc = self.partial_coverage visitor.global_index = -1 visitor.level = 0 cdef np.float64_t pos[3] cdef np.float64_t dds[3] # This dds is the oct-width for i in range(3): dds[i] = (self.DRE[i] - self.DLE[i]) / self.nn[i] # Pos is the center of the octs pos[0] = self.DLE[0] + dds[0]/2.0 for i in range(self.nn[0]): pos[1] = self.DLE[1] + dds[1]/2.0 for j in range(self.nn[1]): pos[2] = self.DLE[2] + dds[2]/2.0 for k in range(self.nn[2]): if self.root_mesh[i][j][k] == NULL: raise KeyError(i,j,k) visitor.pos[0] = i visitor.pos[1] = j visitor.pos[2] = k selector.recursively_visit_octs( self.root_mesh[i][j][k], pos, dds, 0, visitor, vc) pos[2] += dds[2] pos[1] += dds[1] pos[0] += dds[0] cdef np.int64_t get_domain_offset(self, int domain_id): return 0 def _check_root_mesh(self): cdef count = 0 for i in range(self.nn[0]): for j in range(self.nn[1]): for k in range(self.nn[2]): if self.root_mesh[i][j][k] == NULL: print("Missing ", i, j, k) count += 1 print("Missing total of %s out of %s" % (count, self.nn[0] * self.nn[1] * self.nn[2])) cdef int get_root(self, int ind[3], Oct **o) noexcept nogil: cdef int i for i in range(3): if ind[i] < 0 or ind[i] >= self.nn[i]: o[0] = NULL return 1 o[0] = self.root_mesh[ind[0]][ind[1]][ind[2]] return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef Oct *get(self, np.float64_t ppos[3], OctInfo *oinfo = NULL, int max_level = 99) noexcept nogil: #Given a floating point position, retrieve the most #refined oct at that time cdef int ind32[3] cdef np.int64_t ipos[3] cdef np.float64_t dds[3] cdef np.float64_t cp[3] cdef Oct *cur cdef Oct *next cdef int i cur = next = NULL cdef np.int64_t ind[3] cdef np.int64_t level = -1 for i in range(3): dds[i] = (self.DRE[i] - self.DLE[i])/self.nn[i] ind[i] = (floor((ppos[i] - self.DLE[i])/dds[i])) cp[i] = (ind[i] + 0.5) * dds[i] + self.DLE[i] ipos[i] = 0 # We add this to ind later, so it should be zero. ind32[i] = ind[i] self.get_root(ind32, &next) # We want to stop recursing when there's nowhere else to go while next != NULL and level < max_level: level += 1 for i in range(3): ipos[i] = (ipos[i] << 1) + ind[i] cur = next for i in range(3): dds[i] = dds[i] / 2.0 if cp[i] > ppos[i]: ind[i] = 0 cp[i] -= dds[i] / 2.0 else: ind[i] = 1 cp[i] += dds[i]/2.0 if cur.children != NULL: next = cur.children[cind(ind[0],ind[1],ind[2])] else: next = NULL if oinfo == NULL: return cur cdef np.float64_t factor = 1.0 / self.nz * 2 for i in range(3): # We don't normally need to change dds[i] as it has been halved # from the oct width, thus making it already the cell width. # But, since not everything has the cell width equal to have the # width of the oct, we need to apply "factor". oinfo.dds[i] = dds[i] * factor # Cell width oinfo.ipos[i] = ipos[i] oinfo.left_edge[i] = oinfo.ipos[i] * (oinfo.dds[i] * self.nz) + self.DLE[i] oinfo.level = level return cur def locate_positions(self, np.float64_t[:,:] positions): """ This routine, meant to be called by other internal routines, returns a list of oct IDs and a dictionary of Oct info for all the positions supplied. Positions must be in code_length. """ cdef np.float64_t factor = self.nz cdef dict all_octs = {} cdef OctInfo oi cdef Oct* o = NULL cdef np.float64_t pos[3] cdef np.ndarray[np.uint8_t, ndim=1] recorded cdef np.ndarray[np.int64_t, ndim=1] oct_id oct_id = np.ones(positions.shape[0], dtype="int64") * -1 recorded = np.zeros(self.nocts, dtype="uint8") cdef np.int64_t i, j for i in range(positions.shape[0]): for j in range(3): pos[j] = positions[i,j] o = self.get(pos, &oi) if o == NULL: raise RuntimeError if recorded[o.domain_ind] == 0: left_edge = np.asarray(oi.left_edge).copy() dds = np.asarray(oi.dds).copy() right_edge = left_edge + dds*factor all_octs[o.domain_ind] = dict( left_edge = left_edge, right_edge = right_edge, level = oi.level ) recorded[o.domain_ind] = 1 oct_id[i] = o.domain_ind return oct_id, all_octs def domain_identify(self, SelectorObject selector): cdef np.ndarray[np.uint8_t, ndim=1] domain_mask domain_mask = np.zeros(self.num_domains, dtype="uint8") cdef oct_visitors.IdentifyOcts visitor visitor = oct_visitors.IdentifyOcts(self) visitor.domain_mask = domain_mask self.visit_all_octs(selector, visitor) cdef int i domain_ids = [] for i in range(self.num_domains): if domain_mask[i] == 1: domain_ids.append(i+1) return domain_ids @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef Oct** neighbors(self, OctInfo *oi, np.int64_t *nneighbors, Oct *o, bint periodicity[3]): # We are going to do a brute-force search here. # This is not the most efficient -- in fact, it's relatively bad. But # we will attempt to improve it in a future iteration, where we will # grow a stack of parent Octs. # Note that in the first iteration, we will just find the up-to-27 # neighbors, including the main oct. cdef np.int64_t i, j, k, n, level, ii, dlevel cdef int ind[3] cdef OctList *olist cdef OctList *my_list my_list = olist = NULL cdef Oct *cand cdef np.int64_t npos[3] cdef np.int64_t ndim[3] # Now we get our boundaries for this level, so that we can wrap around # if need be. # ndim is the oct dimensions of the level, not the cell dimensions. for i in range(3): ndim[i] = ((self.DRE[i] - self.DLE[i]) / oi.dds[i]) # Here we adjust for oi.dds meaning *cell* width. ndim[i] = (ndim[i] / self.nz) my_list = olist = OctList_append(NULL, o) for i in range(3): npos[0] = (oi.ipos[0] + (1 - i)) if not periodicity[0] and not \ (0 <= npos[0] < ndim[0]): continue elif npos[0] < 0: npos[0] += ndim[0] elif npos[0] >= ndim[0]: npos[0] -= ndim[0] for j in range(3): npos[1] = (oi.ipos[1] + (1 - j)) if not periodicity[1] and not \ (0 <= npos[1] < ndim[1]): continue elif npos[1] < 0: npos[1] += ndim[1] elif npos[1] >= ndim[1]: npos[1] -= ndim[1] for k in range(3): npos[2] = (oi.ipos[2] + (1 - k)) if not periodicity[2] and not \ (0 <= npos[2] < ndim[2]): continue if npos[2] < 0: npos[2] += ndim[2] if npos[2] >= ndim[2]: npos[2] -= ndim[2] # Now we have our npos, which we just need to find. # Level 0 gets bootstrapped for n in range(3): ind[n] = ((npos[n] >> (oi.level)) & 1) cand = NULL self.get_root(ind, &cand) # We should not get a NULL if we handle periodicity # correctly, but we might. if cand == NULL: continue for level in range(1, oi.level+1): dlevel = oi.level - level if cand.children == NULL: break for n in range(3): ind[n] = (npos[n] >> dlevel) & 1 ii = cind(ind[0],ind[1],ind[2]) if cand.children[ii] == NULL: break cand = cand.children[ii] if cand.children != NULL: olist = OctList_subneighbor_find( olist, cand, i, j, k) else: olist = OctList_append(olist, cand) olist = my_list cdef int noct = OctList_count(olist) cdef Oct **neighbors neighbors = malloc(sizeof(Oct*)*noct) for i in range(noct): neighbors[i] = olist.o olist = olist.next OctList_delete(my_list) nneighbors[0] = noct return neighbors @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def mask(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): if num_cells == -1: num_cells = selector.count_octs(self, domain_id) cdef np.ndarray[np.uint8_t, ndim=4] mask cdef oct_visitors.MaskOcts visitor visitor = oct_visitors.MaskOcts(self, domain_id) cdef int ns = self.nz mask = np.zeros((num_cells, ns, ns, ns), dtype="uint8") visitor.mask = mask self.visit_all_octs(selector, visitor) return mask.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def icoords(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): if num_cells == -1: num_cells = selector.count_oct_cells(self, domain_id) cdef oct_visitors.ICoordsOcts visitor visitor = oct_visitors.ICoordsOcts(self, domain_id) cdef np.ndarray[np.int64_t, ndim=2] coords coords = np.empty((num_cells, 3), dtype="int64") visitor.icoords = coords self.visit_all_octs(selector, visitor) return coords @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def ires(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): cdef int i if num_cells == -1: num_cells = selector.count_oct_cells(self, domain_id) cdef oct_visitors.IResOcts visitor visitor = oct_visitors.IResOcts(self, domain_id) #Return the 'resolution' of each cell; ie the level cdef np.ndarray[np.int64_t, ndim=1] res res = np.empty(num_cells, dtype="int64") visitor.ires = res self.visit_all_octs(selector, visitor) if self.level_offset > 0: for i in range(num_cells): res[i] += self.level_offset return res @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fwidth(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): if num_cells == -1: num_cells = selector.count_oct_cells(self, domain_id) cdef oct_visitors.FWidthOcts visitor visitor = oct_visitors.FWidthOcts(self, domain_id) cdef np.ndarray[np.float64_t, ndim=2] fwidth fwidth = np.empty((num_cells, 3), dtype="float64") visitor.fwidth = fwidth self.visit_all_octs(selector, visitor) cdef np.float64_t base_dx for i in range(3): base_dx = (self.DRE[i] - self.DLE[i])/self.nn[i] fwidth[:,i] *= base_dx return fwidth @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fcoords(self, SelectorObject selector, np.int64_t num_cells = -1, int domain_id = -1): if num_cells == -1: num_cells = selector.count_oct_cells(self, domain_id) cdef oct_visitors.FCoordsOcts visitor visitor = oct_visitors.FCoordsOcts(self, domain_id) #Return the floating point unitary position of every cell cdef np.ndarray[np.float64_t, ndim=2] coords coords = np.empty((num_cells, 3), dtype="float64") visitor.fcoords = coords self.visit_all_octs(selector, visitor) cdef int i cdef np.float64_t base_dx for i in range(3): base_dx = (self.DRE[i] - self.DLE[i])/self.nn[i] coords[:,i] *= base_dx coords[:,i] += self.DLE[i] return coords def save_octree(self): # Get the header header = dict(dims = (self.nn[0], self.nn[1], self.nn[2]), left_edge = (self.DLE[0], self.DLE[1], self.DLE[2]), right_edge = (self.DRE[0], self.DRE[1], self.DRE[2]), num_zones = self.nz, partial_coverage = self.partial_coverage) cdef SelectorObject selector = AlwaysSelector(None) # domain_id = -1 here, because we want *every* oct cdef oct_visitors.StoreOctree visitor visitor = oct_visitors.StoreOctree(self, -1) visitor.nz = 1 cdef np.ndarray[np.uint8_t, ndim=1] ref_mask ref_mask = np.zeros(self.nocts * visitor.nzones, dtype="uint8") - 1 visitor.ref_mask = ref_mask # Enforce partial_coverage here self.visit_all_octs(selector, visitor, 1) header['octree'] = ref_mask return header def selector_fill(self, SelectorObject selector, np.ndarray source, np.ndarray dest = None, np.int64_t offset = 0, int dims = 1, int domain_id = -1): # This is actually not correct. The hard part is that we need to # iterate the same way visit_all_octs does, but we need to track the # number of octs total visited. cdef np.int64_t num_cells = -1 if dest is None: # Note that RAMSES can have partial refinement inside an Oct. This # means we actually do want the number of Octs, not the number of # cells. num_cells = selector.count_oct_cells(self, domain_id) dest = np.zeros((num_cells, dims), dtype=source.dtype, order='C') if dims != 1: raise RuntimeError # Just make sure that we're in the right shape. Ideally this will not # duplicate memory. Since we're in Cython, we want to avoid modifying # the .shape attributes directly. dest = dest.reshape((num_cells, 1)) source = source.reshape((source.shape[0], source.shape[1], source.shape[2], source.shape[3], dims)) cdef OctVisitor visitor cdef oct_visitors.CopyArrayI64 visitor_i64 cdef oct_visitors.CopyArrayF64 visitor_f64 if source.dtype != dest.dtype: raise RuntimeError if source.dtype == np.int64: visitor_i64 = oct_visitors.CopyArrayI64(self, domain_id) visitor_i64.source = source visitor_i64.dest = dest visitor = visitor_i64 elif source.dtype == np.float64: visitor_f64 = oct_visitors.CopyArrayF64(self, domain_id) visitor_f64.source = source visitor_f64.dest = dest visitor = visitor_f64 else: raise NotImplementedError visitor.index = offset # We only need this so we can continue calculating the offset visitor.dims = dims self.visit_all_octs(selector, visitor) if (visitor.global_index + 1) * visitor.nzones * visitor.dims > source.size: print("GLOBAL INDEX RAN AHEAD.",) print (visitor.global_index + 1) * visitor.nzones * visitor.dims - source.size print(dest.size, source.size, num_cells) raise RuntimeError if visitor.index > dest.size: print("DEST INDEX RAN AHEAD.",) print(visitor.index - dest.size) print (visitor.global_index + 1) * visitor.nzones * visitor.dims, source.size print(num_cells) raise RuntimeError if num_cells >= 0: return dest return visitor.index - offset def domain_ind(self, selector, int domain_id = -1): cdef np.ndarray[np.int64_t, ndim=1] ind # Here's where we grab the masked items. ind = np.full(self.nocts, -1, 'int64') cdef oct_visitors.IndexOcts visitor visitor = oct_visitors.IndexOcts(self, domain_id) visitor.oct_index = ind self.visit_all_octs(selector, visitor) return ind @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def add(self, int curdom, int curlevel, np.ndarray[np.float64_t, ndim=2] pos, int skip_boundary = 1, int count_boundary = 0, np.ndarray[np.uint64_t, ndim=1, cast=True] levels = None ): # In this function, if we specify curlevel = -1, then we query the # (optional) levels array for the oct level. cdef int no, p, i cdef int ind[3] cdef int nb = 0 cdef Oct *cur cdef np.float64_t pp[3] cdef np.float64_t cp[3] cdef np.float64_t dds[3] no = pos.shape[0] #number of octs if curdom > self.num_domains: return 0 cdef OctAllocationContainer *cont = self.domains.get_cont(curdom - 1) cdef int initial = cont.n_assigned cdef int in_boundary = 0 # How do we bootstrap ourselves? for p in range(no): # We allow specifying curlevel = -1 to query from the levels array # instead. if curlevel == -1: this_level = levels[p] else: this_level = curlevel #for every oct we're trying to add find the #floating point unitary position on this level in_boundary = 0 for i in range(3): pp[i] = pos[p, i] dds[i] = (self.DRE[i] - self.DLE[i])/self.nn[i] ind[i] = ((pp[i] - self.DLE[i])/dds[i]) cp[i] = (ind[i] + 0.5) * dds[i] + self.DLE[i] if ind[i] < 0 or ind[i] >= self.nn[i]: in_boundary = 1 if skip_boundary == in_boundary == 1: nb += count_boundary continue cur = self.next_root(curdom, ind) if cur == NULL: raise RuntimeError # Now we find the location we want # Note that RAMSES I think 1-findiceses levels, but we don't. for _ in range(this_level): # At every level, find the cell this oct # lives inside for i in range(3): #as we get deeper, oct size halves dds[i] = dds[i] / 2.0 if cp[i] > pp[i]: ind[i] = 0 cp[i] -= dds[i]/2.0 else: ind[i] = 1 cp[i] += dds[i]/2.0 # Check if it has not been allocated cur = self.next_child(curdom, ind, cur) # Now we should be at the right level cur.domain = curdom cur.file_ind = p return cont.n_assigned - initial + nb def allocate_domains(self, domain_counts): cdef int count, i self.num_domains = len(domain_counts) # 1-indexed for i, count in enumerate(domain_counts): self.domains.append(count) cdef void append_domain(self, np.int64_t domain_count): self.num_domains += 1 self.domains.append(domain_count) cdef Oct* next_root(self, int domain_id, int ind[3]): cdef Oct *next = self.root_mesh[ind[0]][ind[1]][ind[2]] if next != NULL: return next cdef OctAllocationContainer *cont = self.domains.get_cont(domain_id - 1) if cont.n_assigned >= cont.n: raise RuntimeError next = &cont.my_objs[cont.n_assigned] cont.n_assigned += 1 self.root_mesh[ind[0]][ind[1]][ind[2]] = next self.nocts += 1 return next cdef Oct* next_child(self, int domain_id, int ind[3], Oct *parent) except? NULL: cdef int i cdef Oct *next = NULL if parent.children != NULL: next = parent.children[cind(ind[0],ind[1],ind[2])] else: # This *8 does NOT need to be made generic. parent.children = malloc(sizeof(Oct *) * 8) for i in range(8): parent.children[i] = NULL if next != NULL: return next cdef OctAllocationContainer *cont = self.domains.get_cont(domain_id - 1) if cont.n_assigned >= cont.n: raise RuntimeError next = &cont.my_objs[cont.n_assigned] cont.n_assigned += 1 parent.children[cind(ind[0],ind[1],ind[2])] = next self.nocts += 1 return next def file_index_octs(self, SelectorObject selector, int domain_id, num_cells = -1): # We create oct arrays of the correct size cdef np.ndarray[np.uint8_t, ndim=1] levels cdef np.ndarray[np.uint8_t, ndim=1] cell_inds cdef np.ndarray[np.int64_t, ndim=1] file_inds if num_cells < 0: num_cells = selector.count_oct_cells(self, domain_id) # Initialize variables with dummy values levels = np.full(num_cells, 255, dtype="uint8") file_inds = np.full(num_cells, -1, dtype="int64") cell_inds = np.full(num_cells, 8, dtype="uint8") cdef oct_visitors.FillFileIndicesO visitor_o cdef oct_visitors.FillFileIndicesR visitor_r if self.fill_style == "r": visitor_r = oct_visitors.FillFileIndicesR(self, domain_id) visitor_r.levels = levels visitor_r.file_inds = file_inds visitor_r.cell_inds = cell_inds visitor = visitor_r elif self.fill_style == "o": visitor_o = oct_visitors.FillFileIndicesO(self, domain_id) visitor_o.levels = levels visitor_o.file_inds = file_inds visitor_o.cell_inds = cell_inds visitor = visitor_o else: raise RuntimeError self.visit_all_octs(selector, visitor) return levels, cell_inds, file_inds def morton_index_octs(self, SelectorObject selector, int domain_id, num_cells = -1): cdef np.int64_t i cdef np.uint8_t[:] levels cdef np.uint64_t[:] morton_inds if num_cells < 0: num_cells = selector.count_oct_cells(self, domain_id) levels = np.zeros(num_cells, dtype="uint8") morton_inds = np.zeros(num_cells, dtype="uint64") for i in range(num_cells): levels[i] = 100 morton_inds[i] = 0 cdef oct_visitors.MortonIndexOcts visitor visitor = oct_visitors.MortonIndexOcts(self, domain_id) visitor.level_arr = levels visitor.morton_ind = morton_inds self.visit_all_octs(selector, visitor) return levels, morton_inds def domain_count(self, SelectorObject selector): # We create oct arrays of the correct size cdef np.ndarray[np.int64_t, ndim=1] domain_counts domain_counts = np.zeros(self.num_domains, dtype="int64") cdef oct_visitors.CountByDomain visitor visitor = oct_visitors.CountByDomain(self, -1) visitor.domain_counts = domain_counts self.visit_all_octs(selector, visitor) return domain_counts @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cpdef void fill_level( self, const int level, const np.uint8_t[::1] levels, const np.uint8_t[::1] cell_inds, const np.int64_t[::1] file_inds, dict dest_fields, dict source_fields, np.int64_t offset = 0 ): cdef np.float64_t[:, :] source cdef np.float64_t[::1] dest cdef int i, lvl for key in dest_fields: dest = dest_fields[key] source = source_fields[key] for i in range(levels.shape[0]): lvl = levels[i] if lvl != level: continue if file_inds[i] < 0: dest[i + offset] = np.nan else: dest[i + offset] = source[file_inds[i], cell_inds[i]] def fill_index(self, SelectorObject selector = AlwaysSelector(None)): """Get the on-file index of each cell""" cdef StoreIndex visitor cdef np.int64_t[:, :, :, :] cell_inds cell_inds = np.full((self.nocts, 2, 2, 2), -1, dtype=np.int64) visitor = StoreIndex(self, -1) visitor.cell_inds = cell_inds self.visit_all_octs(selector, visitor) return np.asarray(cell_inds) def fill_octcellindex_neighbours(self, SelectorObject selector, int num_octs=-1, int domain_id=-1, int n_ghost_zones=1): """Compute the oct and cell indices of all the cells within all selected octs, extended by one cell in all directions (for ghost zones computations). Parameters ---------- selector : SelectorObject Selector for the octs to compute neighbour of num_octs : int, optional The number of octs to read in domain_id : int, optional The domain to perform the selection over Returns ------- oct_inds : int64 ndarray (nocts*8, ) The on-domain index of the octs containing each cell cell_inds : uint8 ndarray (nocts*8, ) The index of the cell in its parent oct Note ---- oct_inds/cell_inds """ if num_octs == -1: num_octs = selector.count_octs(self, domain_id) cdef NeighbourCellIndexVisitor visitor cdef np.uint8_t[::1] cell_inds cdef np.int64_t[::1] oct_inds cell_inds = np.full(num_octs*4**3, 8, dtype=np.uint8) oct_inds = np.full(num_octs*4**3, -1, dtype=np.int64) visitor = NeighbourCellIndexVisitor(self, -1, n_ghost_zones) visitor.cell_inds = cell_inds visitor.domain_inds = oct_inds self.visit_all_octs(selector, visitor) return np.asarray(oct_inds), np.asarray(cell_inds) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cpdef int fill_level_with_domain( self, const int level, const np.uint8_t[::1] level_inds, const np.uint8_t[::1] cell_inds, const np.int64_t[::1] file_inds, const np.int32_t[::1] domain_inds, dict dest_fields, dict source_fields, const np.int32_t domain, np.int64_t offset = 0 ): """Similar to fill_level but accepts a domain argument. This is particularly useful for frontends that have buffer zones at CPU boundaries. These buffer oct cells have a different domain than the local one and are usually not read, but one has to read them e.g. to compute ghost zones. """ cdef np.float64_t[:, :] source cdef np.float64_t[::1] dest cdef int i, count, lev cdef np.int32_t dom for key in dest_fields: dest = dest_fields[key] source = source_fields[key] count = 0 for i in range(level_inds.shape[0]): lev = level_inds[i] dom = domain_inds[i] if lev != level or dom != domain: continue count += 1 if file_inds[i] < 0: dest[i + offset] = np.nan else: dest[i + offset] = source[file_inds[i], cell_inds[i]] return count @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def file_index_octs_with_ghost_zones( self, SelectorObject selector, int domain_id, int num_cells=1, int n_ghost_zones=1): """Similar as file_index_octs, but returns the level, cell index, file index and domain of the neighbouring cells as well. Arguments --------- selector : SelectorObject The selector object. It is expected to select all cells for a given oct. domain_id : int The domain to select. Set to -1 to select all domains. num_cells : int, optional The total number of cells (accounting for a 1-cell thick ghost zone layer). Returns ------- levels : uint8, shape (num_cells,) The level of each cell of the super oct cell_inds : uint8, shape (num_cells, ) The index of each cell of the super oct within its own oct file_inds : int64, shape (num_cells, ) The on-file position of the cell. See notes below. domains : int32, shape (num_cells) The domain to which the cells belongs. See notes below. Notes ----- The algorithm constructs a "super-oct" around each oct (see sketch below, where the original oct cells are marked with an x). Note that for sparse octrees (such as RAMSES'), the neighbouring cells may belong to another domain (this is stored in `domains`). If the dataset provides buffer zones between domains (such as RAMSES), this may be stored locally and can be accessed directly. +---+---+---+---+ | | | | | |---+---+---+---| | | x | x | | |---+---+---+---| | | x | x | | |---+---+---+---| | | | | | +---+---+---+---+ """ cdef int num_octs if num_cells < 0: num_octs = selector.count_octs(self, domain_id) num_cells = num_octs * 4**3 cdef NeighbourCellVisitor visitor cdef np.ndarray[np.uint8_t, ndim=1] levels cdef np.ndarray[np.uint8_t, ndim=1] cell_inds cdef np.ndarray[np.int64_t, ndim=1] file_inds cdef np.ndarray[np.int32_t, ndim=1] domains levels = np.full(num_cells, 255, dtype="uint8") file_inds = np.full(num_cells, -1, dtype="int64") cell_inds = np.full(num_cells, 8, dtype="uint8") domains = np.full(num_cells, -1, dtype="int32") visitor = NeighbourCellVisitor(self, -1, n_ghost_zones) # output: level, file_ind and cell_ind of the neighbouring cells visitor.levels = levels visitor.file_inds = file_inds visitor.cell_inds = cell_inds visitor.domains = domains # direction to explore and extra parameters of the visitor visitor.octree = self visitor.last = -1 # Compute indices self.visit_all_octs(selector, visitor) return levels, cell_inds, file_inds, domains def finalize(self): cdef SelectorObject selector = AlwaysSelector(None) cdef oct_visitors.AssignDomainInd visitor visitor = oct_visitors.AssignDomainInd(self, 1) self.visit_all_octs(selector, visitor) assert ((visitor.global_index+1)*visitor.nzones == visitor.index) cdef int root_node_compare(const void *a, const void *b) noexcept nogil: cdef OctKey *ao cdef OctKey *bo ao = a bo = b if ao.key < bo.key: return -1 elif ao.key == bo.key: return 0 else: return 1 cdef class SparseOctreeContainer(OctreeContainer): def __init__(self, domain_dimensions, domain_left_edge, domain_right_edge, num_zones = 2): cdef int i self.partial_coverage = 1 self.nz = num_zones for i in range(3): self.nn[i] = domain_dimensions[i] self.domains = OctObjectPool() self.num_domains = 0 self.level_offset = 0 self.nocts = 0 # Increment when initialized self.root_mesh = NULL self.root_nodes = NULL self.tree_root = NULL self.num_root = 0 self.max_root = 0 # We don't initialize the octs yet for i in range(3): self.DLE[i] = domain_left_edge[i] #0 self.DRE[i] = domain_right_edge[i] #num_grid self.fill_style = "r" @classmethod def load_octree(cls, header): raise NotImplementedError def save_octree(self): raise NotImplementedError cdef int get_root(self, int ind[3], Oct **o) noexcept nogil: o[0] = NULL cdef np.int64_t key = self.ipos_to_key(ind) cdef OctKey okey cdef OctKey **oresult = NULL okey.key = key okey.node = NULL oresult = tfind(&okey, &self.tree_root, root_node_compare) if oresult != NULL: o[0] = oresult[0].node return 1 return 0 cdef void key_to_ipos(self, np.int64_t key, np.int64_t pos[3]): # Note: this is the result of doing # for i in range(20): # ukey |= (1 << i) cdef np.int64_t ukey = 1048575 cdef int j for j in range(3): pos[2 - j] = ((key & ukey)) key = key >> 20 cdef np.int64_t ipos_to_key(self, int pos[3]) noexcept nogil: # We (hope) that 20 bits is enough for each index. cdef int i cdef np.int64_t key = 0 for i in range(3): # Note the casting here. Bitshifting can cause issues otherwise. key |= ((pos[i]) << 20 * (2 - i)) return key @cython.cdivision(True) cdef void visit_all_octs(self, SelectorObject selector, OctVisitor visitor, int vc = -1, np.int64_t *indices = NULL): cdef int i, j cdef np.int64_t key visitor.global_index = -1 visitor.level = 0 if vc == -1: vc = self.partial_coverage cdef np.float64_t pos[3] cdef np.float64_t dds[3] # This dds is the oct-width for i in range(3): dds[i] = (self.DRE[i] - self.DLE[i]) / self.nn[i] # Pos is the center of the octs cdef Oct *o for i in range(self.num_root): o = self.root_nodes[i].node key = self.root_nodes[i].key self.key_to_ipos(key, visitor.pos) for j in range(3): pos[j] = self.DLE[j] + (visitor.pos[j] + 0.5) * dds[j] selector.recursively_visit_octs( o, pos, dds, 0, visitor, vc) if indices != NULL: indices[i] = visitor.index cdef np.int64_t get_domain_offset(self, int domain_id): return 0 # We no longer have a domain offset. cdef Oct* next_root(self, int domain_id, int ind[3]): cdef Oct *next = NULL self.get_root(ind, &next) if next != NULL: return next cdef OctAllocationContainer *cont = self.domains.get_cont(domain_id - 1) if cont.n_assigned >= cont.n: print("Too many assigned.") return NULL if self.num_root >= self.max_root: print("Too many roots.") return NULL next = &cont.my_objs[cont.n_assigned] cont.n_assigned += 1 cdef np.int64_t key = 0 cdef OctKey *ikey = &self.root_nodes[self.num_root] key = self.ipos_to_key(ind) self.root_nodes[self.num_root].key = key self.root_nodes[self.num_root].node = next tsearch(ikey, &self.tree_root, root_node_compare) self.num_root += 1 self.nocts += 1 return next def allocate_domains(self, domain_counts, int root_nodes): OctreeContainer.allocate_domains(self, domain_counts) self.root_nodes = malloc(sizeof(OctKey) * root_nodes) self.max_root = root_nodes for i in range(root_nodes): self.root_nodes[i].key = -1 self.root_nodes[i].node = NULL def __dealloc__(self): # This gets called BEFORE the superclass deallocation. But, both get # called. cdef OctKey *ikey for i in range(self.num_root): ikey = &self.root_nodes[i] tdelete(ikey, &self.tree_root, root_node_compare) if self.root_nodes != NULL: free(self.root_nodes) cdef class ARTOctreeContainer(OctreeContainer): def __init__(self, oct_domain_dimensions, domain_left_edge, domain_right_edge, partial_coverage = 0, num_zones = 2): OctreeContainer.__init__(self, oct_domain_dimensions, domain_left_edge, domain_right_edge, partial_coverage, num_zones) self.fill_style = "r" cdef OctList *OctList_subneighbor_find(OctList *olist, Oct *top, int i, int j, int k): if top.children == NULL: return olist # The i, j, k here are the offsets of "top" with respect to # the oct for whose neighbors we are searching. # Note that this will be recursively called. We will evaluate either 1, 2, # or 4 octs for children and potentially adding them. In fact, this will # be 2**(num_zero) where num_zero is the number of indices that are equal # to zero; i.e., the number of dimensions along which we are aligned. # For now, we assume we will not be doing this along all three zeros, # because that would be pretty tricky. if i == j == k == 1: return olist cdef np.int64_t n[3] cdef np.int64_t ind[3] cdef np.int64_t off[3][2] cdef np.int64_t ii, ij, ik, ci ind[0] = 1 - i ind[1] = 1 - j ind[2] = 1 - k for ii in range(3): if ind[ii] == 0: n[ii] = 2 off[ii][0] = 0 off[ii][1] = 1 elif ind[ii] == -1: n[ii] = 1 off[ii][0] = 1 elif ind[ii] == 1: n[ii] = 1 off[ii][0] = 0 for ii in range(n[0]): for ij in range(n[1]): for ik in range(n[2]): ci = cind(off[0][ii], off[1][ij], off[2][ik]) cand = top.children[ci] if cand.children != NULL: olist = OctList_subneighbor_find(olist, cand, i, j, k) else: olist = OctList_append(olist, cand) return olist cdef OctList *OctList_append(OctList *olist, Oct *o): cdef OctList *this = olist if this == NULL: this = malloc(sizeof(OctList)) this.next = NULL this.o = o return this while this.next != NULL: this = this.next this.next = malloc(sizeof(OctList)) this = this.next this.o = o this.next = NULL return this cdef int OctList_count(OctList *olist): cdef OctList *this = olist cdef int i = 0 # Count the list while this != NULL: i += 1 this = this.next return i cdef void OctList_delete(OctList *olist): cdef OctList *next cdef OctList *this = olist while this != NULL: next = this.next free(this) this = next cdef class OctObjectPool(ObjectPool): # This is an inherited version of the ObjectPool that provides setup and # teardown functions for the individually allocated objects. These allow # us to initialize the Octs to default values, and we can also free any # allocated memory in them. Implementing _con_to_array also provides the # opportunity to supply views of the octs in Python code. def __cinit__(self): # Base class will ALSO be called self.itemsize = sizeof(Oct) assert(sizeof(OctAllocationContainer) == sizeof(AllocationContainer)) cdef void setup_objs(self, void *obj, np.uint64_t n, np.uint64_t offset, np.int64_t con_id): cdef Oct* octs = obj for n in range(n): octs[n].file_ind = octs[n].domain = - 1 octs[n].domain_ind = n + offset octs[n].children = NULL cdef void teardown_objs(self, void *obj, np.uint64_t n, np.uint64_t offset, np.int64_t con_id): cdef np.uint64_t i cdef Oct *my_octs = obj for i in range(n): if my_octs[i].children != NULL: free(my_octs[i].children) free(obj) def _con_to_array(self, int i): cdef AllocationContainer *obj = &self.containers[i] if obj.n_assigned == 0: return None cdef OctPadded[:] mm = ( obj.my_objs) rv = np.asarray(mm) return rv yt-project-yt-f043ac8/yt/geometry/oct_geometry_handler.py000066400000000000000000000111111510711153200237030ustar00rootroot00000000000000import numpy as np from yt.fields.field_detector import FieldDetector from yt.geometry.geometry_handler import Index from yt.utilities.logger import ytLogger as mylog class OctreeIndex(Index): """The Index subclass for oct AMR datasets""" def _setup_geometry(self): mylog.debug("Initializing Octree Geometry Handler.") self._initialize_oct_handler() def get_smallest_dx(self): """ Returns (in code units) the smallest cell size in the simulation. """ return ( self.dataset.domain_width / (self.dataset.domain_dimensions * 2 ** (self.max_level)) ).min() def convert(self, unit): return self.dataset.conversion_factors[unit] def _add_mesh_sampling_particle_field(self, deposit_field, ftype, ptype): units = self.ds.field_info[ftype, deposit_field].units take_log = self.ds.field_info[ftype, deposit_field].take_log field_name = f"cell_{ftype}_{deposit_field}" def _cell_index(field, data): # Get the position of the particles pos = data[ptype, "particle_position"] Npart = pos.shape[0] ret = np.zeros(Npart, dtype="float64") tmp = np.zeros(Npart, dtype="float64") if isinstance(data, FieldDetector): return ret remaining = np.ones(Npart, dtype=bool) Nremaining = Npart Nobjs = len(data._current_chunk.objs) Nbits = int(np.ceil(np.log2(Nobjs))) # Sort objs by decreasing number of octs enumerated_objs = sorted( enumerate(data._current_chunk.objs), key=lambda arg: arg[1].oct_handler.nocts, reverse=True, ) for i, obj in enumerated_objs: if Nremaining == 0: break icell = ( obj["index", "ones"].T.reshape(-1).astype(np.int64).cumsum().value - 1 ) mesh_data = ((icell << Nbits) + i).astype(np.float64) # Access the mesh data and attach them to their particles tmp[:Nremaining] = obj.mesh_sampling_particle_field( pos[remaining], mesh_data ) ret[remaining] = tmp[:Nremaining] remaining[remaining] = np.isnan(tmp[:Nremaining]) Nremaining = remaining.sum() return data.ds.arr(ret, units="1") def _mesh_sampling_particle_field(field, data): """ Create a grid field for particle quantities using given method. """ ones = data[ptype, "particle_ones"] # Access "cell_index" field Npart = ones.shape[0] ret = np.zeros(Npart) cell_index = np.array(data[ptype, "cell_index"], np.int64) if isinstance(data, FieldDetector): return ret # The index of the obj is stored on the first bits Nobjs = len(data._current_chunk.objs) Nbits = int(np.ceil(np.log2(Nobjs))) icell = cell_index >> Nbits iobj = cell_index - (icell << Nbits) for i, subset in enumerate(data._current_chunk.objs): mask = iobj == i subset.field_parameters = data.field_parameters cell_data = subset[ftype, deposit_field].T.reshape(-1) ret[mask] = cell_data[icell[mask]] return data.ds.arr(ret, units=cell_data.units) if (ptype, "cell_index") not in self.ds.derived_field_list: self.ds.add_field( (ptype, "cell_index"), function=_cell_index, sampling_type="particle", units="1", ) self.ds.add_field( (ptype, field_name), function=_mesh_sampling_particle_field, sampling_type="particle", units=units, take_log=take_log, ) def _icoords_to_fcoords( self, icoords: np.ndarray, ires: np.ndarray, axes: tuple[int, ...] | None = None, ) -> tuple[np.ndarray, np.ndarray]: """ Accepts icoords and ires and returns appropriate fcoords and fwidth. Mostly useful for cases where we have irregularly spaced or structured grids. """ dds = self.ds.domain_width[axes,] / ( self.ds.domain_dimensions[axes,] * self.ds.refine_by ** ires[:, None] ) pos = (0.5 + icoords) * dds + self.ds.domain_left_edge[axes,] return pos, dds yt-project-yt-f043ac8/yt/geometry/oct_visitors.pxd000066400000000000000000000110571510711153200224110ustar00rootroot00000000000000""" Oct visitor definitions file """ cimport numpy as np cdef struct Oct cdef struct Oct: np.int64_t file_ind # index with respect to the order in which it was # added np.int64_t domain_ind # index within the global set of domains np.int64_t domain # (opt) addl int index Oct **children # Up to 8 long cdef struct OctInfo: np.float64_t left_edge[3] np.float64_t dds[3] np.int64_t ipos[3] np.int32_t level cdef struct OctPadded: np.int64_t file_ind np.int64_t domain_ind np.int64_t domain np.int64_t padding cdef class OctVisitor: cdef np.uint64_t index cdef np.uint64_t last cdef np.int64_t global_index cdef np.int64_t pos[3] # position in ints cdef np.uint8_t ind[3] # cell position cdef int dims cdef np.int32_t domain cdef np.int8_t level cdef np.int8_t nz # This is number of zones along each dimension. 1 => 1 zones, 2 => 8, etc. # To calculate nzones, nz**3 cdef np.int32_t nzones # There will also be overrides for the memoryviews associated with the # specific instance. cdef void visit(self, Oct*, np.uint8_t selected) cdef inline int oind(self): cdef int d = self.nz return (((self.ind[0]*d)+self.ind[1])*d+self.ind[2]) cdef inline int rind(self): cdef int d = self.nz return (((self.ind[2]*d)+self.ind[1])*d+self.ind[0]) cdef class CountTotalOcts(OctVisitor): pass cdef class CountTotalCells(OctVisitor): pass cdef class MarkOcts(OctVisitor): # Unused cdef np.uint8_t[:,:,:,:] mark cdef class MaskOcts(OctVisitor): cdef np.uint8_t[:,:,:,:] mask cdef class IndexOcts(OctVisitor): cdef np.int64_t[:] oct_index cdef class MaskedIndexOcts(OctVisitor): cdef np.int64_t[:] oct_index cdef np.uint8_t[:] oct_mask cdef class IndexMaskMapOcts(OctVisitor): cdef np.int64_t[:] oct_index cdef np.uint8_t[:] oct_mask cdef np.int64_t[:] map_domain_ind cdef np.uint64_t map_index cdef class ICoordsOcts(OctVisitor): cdef np.int64_t[:,:] icoords cdef class IResOcts(OctVisitor): cdef np.int64_t[:] ires cdef class FCoordsOcts(OctVisitor): cdef np.float64_t[:,:] fcoords cdef class FWidthOcts(OctVisitor): cdef np.float64_t[:,:] fwidth cdef class CopyArrayI64(OctVisitor): cdef np.int64_t[:,:,:,:,:,:] source cdef np.int64_t[:,:] dest cdef class CopyArrayF64(OctVisitor): cdef np.float64_t[:,:,:,:,:] source cdef np.float64_t[:,:] dest cdef class CopyFileIndArrayI8(OctVisitor): cdef np.int64_t root cdef np.uint8_t[:] source cdef np.uint8_t[:] dest cdef class IdentifyOcts(OctVisitor): cdef np.uint8_t[:] domain_mask cdef class AssignDomainInd(OctVisitor): pass cdef class FillFileIndicesO(OctVisitor): cdef np.uint8_t[:] levels cdef np.int64_t[:] file_inds cdef np.uint8_t[:] cell_inds cdef class FillFileIndicesR(OctVisitor): cdef np.uint8_t[:] levels cdef np.int64_t[:] file_inds cdef np.uint8_t[:] cell_inds cdef class CountByDomain(OctVisitor): cdef np.int64_t[:] domain_counts cdef class StoreOctree(OctVisitor): cdef np.uint8_t[:] ref_mask cdef class LoadOctree(OctVisitor): cdef np.uint8_t[:] ref_mask cdef Oct* octs cdef np.uint64_t *nocts cdef np.uint64_t *nfinest cdef np.uint64_t max_level cdef class MortonIndexOcts(OctVisitor): cdef np.uint8_t[:] level_arr cdef np.uint64_t[:] morton_ind cdef inline int cind(int i, int j, int k) noexcept nogil: # THIS ONLY WORKS FOR CHILDREN. It is not general for zones. return (((i*2)+j)*2+k) from .oct_container cimport OctreeContainer cdef class StoreIndex(OctVisitor): cdef np.int64_t[:,:,:,:] cell_inds # cimport oct_container cdef class BaseNeighbourVisitor(OctVisitor): cdef int idim # 0,1,2 for x,y,z cdef int direction # +1 for +x, -1 for -x cdef np.uint8_t neigh_ind[3] cdef bint other_oct cdef Oct *neighbour cdef OctreeContainer octree cdef OctInfo oi cdef int n_ghost_zones cdef void set_neighbour_info(self, Oct *o, int ishift[3]) cdef inline np.uint8_t neighbour_rind(self): cdef int d = self.nz return (((self.neigh_ind[2]*d)+self.neigh_ind[1])*d+self.neigh_ind[0]) cdef class NeighbourCellIndexVisitor(BaseNeighbourVisitor): cdef np.uint8_t[::1] cell_inds cdef np.int64_t[::1] domain_inds cdef class NeighbourCellVisitor(BaseNeighbourVisitor): cdef np.uint8_t[::1] levels cdef np.int64_t[::1] file_inds cdef np.uint8_t[::1] cell_inds cdef np.int32_t[::1] domains yt-project-yt-f043ac8/yt/geometry/oct_visitors.pyx000066400000000000000000000504361510711153200224420ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Oct visitor functions """ cimport cython cimport numpy as np import numpy as np from libc.stdlib cimport malloc from yt.utilities.lib.fp_utils cimport * from yt.utilities.lib.geometry_utils cimport encode_morton_64bit from .oct_container cimport OctreeContainer # Now some visitor functions cdef class OctVisitor: def __init__(self, OctreeContainer octree, int domain_id = -1): cdef int i self.index = 0 self.last = -1 self.global_index = -1 for i in range(3): self.pos[i] = -1 self.ind[i] = -1 self.dims = 0 self.domain = domain_id self.level = -1 self.nz = octree.nz self.nzones = self.nz**3 cdef void visit(self, Oct* o, np.uint8_t selected): raise NotImplementedError # This copies an integer array from the source to the destination, based on the # selection criteria. cdef class CopyArrayI64(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # We should always have global_index less than our source. # "last" here tells us the dimensionality of the array. if selected == 0: return # There are this many records between "octs" self.dest[self.index, :] = self.source[ self.ind[2], self.ind[1], self.ind[0], self.global_index, :] self.index += 1 # This copies a floating point array from the source to the destination, based # on the selection criteria. cdef class CopyArrayF64(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # We should always have global_index less than our source. # "last" here tells us the dimensionality of the array. if selected == 0: return # There are this many records between "octs" self.dest[self.index, :] = self.source[ self.ind[2], self.ind[1], self.ind[0], self.global_index, :] self.index += 1 # This copies a bit array from source to the destination, based on file_ind cdef class CopyFileIndArrayI8(OctVisitor): def __init__(self, OctreeContainer octree, int domain_id = -1): super(CopyFileIndArrayI8, self).__init__(octree, domain_id) self.root = -1 @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if self.level == 0: self.root += 1 if self.last != o.domain_ind: self.last = o.domain_ind self.dest[o.domain_ind] = self.source[self.root] self.index += 1 # This counts the number of octs, selected or not, that the selector hits. # Note that the selector will not recursively visit unselected octs, so this is # still useful. cdef class CountTotalOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # Count even if not selected. # Number of *octs* visited. if self.last != o.domain_ind: self.index += 1 self.last = o.domain_ind # This counts the number of selected cells. cdef class CountTotalCells(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # Number of *cells* visited and selected. self.index += selected # Every time a cell is visited, mark it. This will be for all visited octs. cdef class MarkOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # We mark them even if they are not selected if self.last != o.domain_ind: self.last = o.domain_ind self.index += 1 self.mark[self.index, self.ind[2], self.ind[1], self.ind[0]] = 1 # Mask all the selected cells. cdef class MaskOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if selected == 0: return self.mask[self.global_index, self.ind[2], self.ind[1], self.ind[0]] = 1 # Compute a mapping from domain_ind to flattened index. cdef class IndexOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # Note that we provide an index even if the cell is not selected. if self.last != o.domain_ind: self.last = o.domain_ind self.oct_index[o.domain_ind] = self.index self.index += 1 # Compute a mapping from domain_ind to flattened index with some octs masked. cdef class MaskedIndexOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # Note that we provide an index even if the cell is not selected. if self.last != o.domain_ind: self.last = o.domain_ind if self.oct_mask[o.domain_ind] == 1: self.oct_index[o.domain_ind] = self.index self.index += 1 # Compute a mapping from domain_ind to flattened index checking mask. cdef class IndexMaskMapOcts(OctVisitor): def __init__(self, OctreeContainer octree, int domain_id = -1): super(IndexMaskMapOcts, self).__init__(octree, domain_id) self.map_index = 0 @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if self.last != o.domain_ind: self.last = o.domain_ind if self.oct_mask[o.domain_ind] == 1: if self.map_domain_ind[self.map_index] >= 0: self.oct_index[self.map_domain_ind[self.map_index]] = self.index self.map_index += 1 self.index += 1 # Integer coordinates cdef class ICoordsOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if selected == 0: return cdef int i for i in range(3): self.icoords[self.index,i] = (self.pos[i] * self.nz) + self.ind[i] self.index += 1 # Level cdef class IResOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if selected == 0: return self.ires[self.index] = self.level self.index += 1 # Floating point coordinates cdef class FCoordsOcts(OctVisitor): @cython.cdivision(True) @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # Note that this does not actually give the correct floating point # coordinates. It gives them in some unit system where the domain is 1.0 # in all directions, and assumes that they will be scaled later. if selected == 0: return cdef int i cdef np.float64_t c, dx dx = 1.0 / ((self.nz) << self.level) for i in range(3): c = ((self.pos[i] * self.nz) + self.ind[i]) self.fcoords[self.index,i] = (c + 0.5) * dx self.index += 1 # Floating point widths; domain modifications are done later. cdef class FWidthOcts(OctVisitor): @cython.cdivision(True) @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # Note that this does not actually give the correct floating point # coordinates. It gives them in some unit system where the domain is 1.0 # in all directions, and assumes that they will be scaled later. if selected == 0: return cdef int i cdef np.float64_t dx dx = 1.0 / (self.nz << self.level) for i in range(3): self.fwidth[self.index,i] = dx self.index += 1 # Mark which domains are touched by a selector. cdef class IdentifyOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # We assume that our domain has *already* been selected by, which means # we'll get all cells within the domain for a by-domain selector and all # cells within the domain *and* selector for the selector itself. if selected == 0: return self.domain_mask[o.domain - 1] = 1 # Assign domain indices to octs cdef class AssignDomainInd(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): o.domain_ind = self.global_index self.index += 1 # From the file, fill in C order cdef class FillFileIndicesO(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # We fill these arrays, then inside the level filler we use these as # indices as we fill a second array from the self. if selected == 0: return self.levels[self.index] = self.level self.file_inds[self.index] = o.file_ind self.cell_inds[self.index] = self.oind() self.index +=1 # From the file, fill in F order cdef class FillFileIndicesR(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): # We fill these arrays, then inside the level filler we use these as # indices as we fill a second array from the self. if selected == 0: return self.levels[self.index] = self.level self.file_inds[self.index] = o.file_ind self.cell_inds[self.index] = self.rind() self.index +=1 # Count octs by domain cdef class CountByDomain(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if selected == 0: return # NOTE: We do this for every *cell*. self.domain_counts[o.domain - 1] += 1 # Store the refinement mapping of the octree to be loaded later cdef class StoreOctree(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if o.children == NULL: # Not refined. res = 0 else: res = 1 self.ref_mask[self.index] = res self.index += 1 # Go from a refinement mapping to a new octree cdef class LoadOctree(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): cdef int i, ii ii = cind(self.ind[0], self.ind[1], self.ind[2]) if self.level > self.max_level: self.max_level = self.level if self.ref_mask[self.index] == 0: # We only want to do this once. Otherwise we end up with way too many # nfinest for our tastes. if o.file_ind == -1: o.children = NULL o.file_ind = self.nfinest[0] o.domain = 1 self.nfinest[0] += 1 elif self.ref_mask[self.index] > 0: if self.ref_mask[self.index] != 1 and self.ref_mask[self.index] != 8: print("ARRAY CLUE: ", self.ref_mask[self.index], "UNKNOWN") raise RuntimeError if o.children == NULL: o.children = malloc(sizeof(Oct *) * 8) for i in range(8): o.children[i] = NULL for i in range(8): o.children[ii + i] = &self.octs[self.nocts[0]] o.children[ii + i].domain_ind = self.nocts[0] o.children[ii + i].file_ind = -1 o.children[ii + i].domain = -1 o.children[ii + i].children = NULL self.nocts[0] += 1 else: print("SOMETHING IS AMISS", self.index) raise RuntimeError self.index += 1 cdef class MortonIndexOcts(OctVisitor): @cython.boundscheck(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if selected == 0: return cdef np.int64_t coord[3] cdef int i for i in range(3): coord[i] = (self.pos[i] * self.nz) + self.ind[i] if (coord[i] < 0): raise RuntimeError("Oct coordinate in dimension {} is ".format(i)+ "negative. ({})".format(coord[i])) self.level_arr[self.index] = self.level self.morton_ind[self.index] = encode_morton_64bit( np.uint64(coord[0]), np.uint64(coord[1]), np.uint64(coord[2])) self.index += 1 # Store cell index cdef class StoreIndex(OctVisitor): @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): if not selected: return self.cell_inds[o.domain_ind, self.ind[2], self.ind[1], self.ind[0]] = self.index self.index += 1 cdef class BaseNeighbourVisitor(OctVisitor): def __init__(self, OctreeContainer octree, int domain_id=-1, int n_ghost_zones=1): self.octree = octree self.neigh_ind[0] = 0 self.neigh_ind[1] = 0 self.neigh_ind[2] = 0 self.n_ghost_zones = n_ghost_zones super(BaseNeighbourVisitor, self).__init__(octree, domain_id) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void set_neighbour_info(self, Oct *o, int ishift[3]): cdef int i cdef np.float64_t c, dx cdef np.int64_t ipos cdef np.float64_t fcoords[3] cdef Oct *neighbour cdef bint local_oct cdef bint other_oct dx = 1.0 / (self.nz << self.level) local_oct = True # Compute position of neighbouring cell for i in range(3): c = (self.pos[i] * self.nz) fcoords[i] = (c + 0.5 + ishift[i]) * dx / self.octree.nn[i] # Assuming periodicity if fcoords[i] < 0: fcoords[i] += 1 elif fcoords[i] > 1: fcoords[i] -= 1 local_oct &= (0 <= ishift[i] <= 1) other_oct = not local_oct # Use octree to find neighbour if other_oct: neighbour = self.octree.get(fcoords, &self.oi, max_level=self.level) else: neighbour = o self.oi.level = self.level for i in range(3): self.oi.ipos[i] = (self.pos[i] * self.nz) + ishift[i] # Extra step - compute cell position in neighbouring oct (and store in oi.ipos) if self.oi.level == self.level - 1: for i in range(3): ipos = (((self.pos[i] * self.nz) + ishift[i])) >> 1 if (self.oi.ipos[i] << 1) == ipos: self.oi.ipos[i] = 0 else: self.oi.ipos[i] = 1 self.neighbour = neighbour # Index of neighbouring cell within its oct for i in range(3): self.neigh_ind[i] = (ishift[i]) % 2 self.other_oct = other_oct if other_oct: if self.neighbour != NULL: if self.oi.level == self.level - 1: # Position within neighbouring oct is stored in oi.ipos for i in range(3): self.neigh_ind[i] = self.oi.ipos[i] elif self.oi.level != self.level: print('This should not happen! %s %s' % (self.oi.level, self.level)) self.neighbour = NULL # Store neighbouring cell index in current cell cdef class NeighbourCellIndexVisitor(BaseNeighbourVisitor): # This piece of code is very much optimizable. Here are possible routes to achieve # much better performance: # - Work oct by oct, which would reduce the number of neighbor lookup # from 4³=64 to 3³=27, # - Use faster neighbor lookup method(s). For now, all searches are started from # the root mesh down to leaf nodes, but we could instead go up the tree from the # central oct then down to find all neighbors (see e.g. # https://geidav.wordpress.com/2017/12/02/advanced-octrees-4-finding-neighbor-nodes/). # - Pre-compute the face-neighbors of all octs. # Note that for the last point, algorithms exist that generate the neighbors of all # octs in O(1) time (https://link.springer.com/article/10.1007/s13319-015-0060-9) # during the octree construction. # Another possible solution would be to keep a unordered hash map of all the octs # indexed by their (3-integers) position. With such structure, finding a neighbor # takes O(1) time. This could even come as a replacement of the current # pointer-based octree structure. @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): cdef int i, j, k cdef int ishift[3] cdef np.uint8_t neigh_cell_ind cdef np.int64_t neigh_domain_ind if selected == 0: return # Work at oct level if self.last == o.domain_ind: return self.last = o.domain_ind cdef int i0, i1 i0 = -self.n_ghost_zones i1 = 2 + self.n_ghost_zones # Loop over cells in and directly around oct for i in range(i0, i1): ishift[0] = i for j in range(i0, i1): ishift[1] = j for k in range(i0, i1): ishift[2] = k self.set_neighbour_info(o, ishift) if not self.other_oct: neigh_domain_ind = o.domain_ind neigh_cell_ind = self.neighbour_rind() elif self.neighbour != NULL: neigh_domain_ind = self.neighbour.domain_ind neigh_cell_ind = self.neighbour_rind() else: neigh_domain_ind = -1 neigh_cell_ind = 8 self.cell_inds[self.index] = neigh_cell_ind self.domain_inds[self.index] = neigh_domain_ind self.index += 1 # Store file position + cell of neighbouring cell in current cell cdef class NeighbourCellVisitor(BaseNeighbourVisitor): @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void visit(self, Oct* o, np.uint8_t selected): cdef int i, j, k cdef int ishift[3] cdef np.int64_t neigh_file_ind cdef np.uint8_t neigh_cell_ind cdef np.int32_t neigh_domain cdef np.uint8_t neigh_level if selected == 0: return # Work at oct level if self.last == o.domain_ind: return self.last = o.domain_ind cdef int i0, i1 i0 = -self.n_ghost_zones i1 = 2 + self.n_ghost_zones # Loop over cells in and directly around oct for i in range(i0, i1): ishift[0] = i for j in range(i0, i1): ishift[1] = j for k in range(i0, i1): ishift[2] = k self.set_neighbour_info(o, ishift) if not self.other_oct: neigh_level = self.level neigh_domain = o.domain neigh_file_ind = o.file_ind neigh_cell_ind = self.neighbour_rind() elif self.neighbour != NULL: neigh_level = self.oi.level neigh_domain = self.neighbour.domain neigh_file_ind = self.neighbour.file_ind neigh_cell_ind = self.neighbour_rind() else: neigh_level = 255 neigh_domain = -1 neigh_file_ind = -1 neigh_cell_ind = 8 self.levels[self.index] = neigh_level self.file_inds[self.index] = neigh_file_ind self.cell_inds[self.index] = neigh_cell_ind self.domains[self.index] = neigh_domain self.index += 1 yt-project-yt-f043ac8/yt/geometry/particle_deposit.pxd000066400000000000000000000107271510711153200232170ustar00rootroot00000000000000""" Particle Deposition onto Octs """ cimport numpy as np import numpy as np cimport cython from libc.math cimport sqrt from libc.stdlib cimport free, malloc from yt.utilities.lib.fp_utils cimport * from .oct_container cimport Oct, OctreeContainer cdef extern from "numpy/npy_math.h": double NPY_PI cdef extern from "platform_dep.h": void *alloca(int) cdef inline int gind(int i, int j, int k, int dims[3]): # The ordering is such that we want i to vary the slowest in this instance, # even though in other instances it varies the fastest. To see this in # action, try looking at the results of an n_ref=256 particle CIC plot, # which shows it the most clearly. return ((i*dims[1])+j)*dims[2]+k #################################################### # Standard SPH kernel for use with the Grid method # #################################################### cdef inline np.float64_t sph_kernel_cubic(np.float64_t x) noexcept nogil: cdef np.float64_t kernel # C is 8/pi cdef np.float64_t C = 2.5464790894703255 if x <= 0.5: kernel = 1.-6.*x*x*(1.-x) elif x>0.5 and x<=1.0: kernel = 2.*(1.-x)*(1.-x)*(1.-x) else: kernel = 0. return kernel * C ######################################################## # Alternative SPH kernels for use with the Grid method # ######################################################## # quartic spline cdef inline np.float64_t sph_kernel_quartic(np.float64_t x) noexcept nogil: cdef np.float64_t kernel cdef np.float64_t C = 9.71404681957369 # 5.**6/512/np.pi if x < 1: kernel = (1.-x)**4 if x < 3./5: kernel -= 5*(3./5-x)**4 if x < 1./5: kernel += 10*(1./5-x)**4 else: kernel = 0. return kernel * C # quintic spline cdef inline np.float64_t sph_kernel_quintic(np.float64_t x) noexcept nogil: cdef np.float64_t kernel cdef np.float64_t C = 17.403593027098754 # 3.**7/40/np.pi if x < 1: kernel = (1.-x)**5 if x < 2./3: kernel -= 6*(2./3-x)**5 if x < 1./3: kernel += 15*(1./3-x)**5 else: kernel = 0. return kernel * C # Wendland C2 cdef inline np.float64_t sph_kernel_wendland2(np.float64_t x) noexcept nogil: cdef np.float64_t kernel cdef np.float64_t C = 3.3422538049298023 # 21./2/np.pi if x < 1: kernel = (1.-x)**4 * (1+4*x) else: kernel = 0. return kernel * C # Wendland C4 cdef inline np.float64_t sph_kernel_wendland4(np.float64_t x) noexcept nogil: cdef np.float64_t kernel cdef np.float64_t C = 4.923856051905513 # 495./32/np.pi if x < 1: kernel = (1.-x)**6 * (1+6*x+35./3*x**2) else: kernel = 0. return kernel * C # Wendland C6 cdef inline np.float64_t sph_kernel_wendland6(np.float64_t x) noexcept nogil: cdef np.float64_t kernel cdef np.float64_t C = 6.78895304126366 # 1365./64/np.pi if x < 1: kernel = (1.-x)**8 * (1+8*x+25*x**2+32*x**3) else: kernel = 0. return kernel * C cdef inline np.float64_t sph_kernel_dummy(np.float64_t x) noexcept nogil: return 0 # I don't know the way to use a dict in a cdef class. # So in order to mimic a registry functionality, # I manually created a function to lookup the kernel functions. ctypedef np.float64_t (*kernel_func) (np.float64_t) noexcept nogil cdef inline kernel_func get_kernel_func(str kernel_name) noexcept nogil: with gil: if kernel_name == 'cubic': return sph_kernel_cubic elif kernel_name == 'quartic': return sph_kernel_quartic elif kernel_name == 'quintic': return sph_kernel_quintic elif kernel_name == 'wendland2': return sph_kernel_wendland2 elif kernel_name == 'wendland4': return sph_kernel_wendland4 elif kernel_name == 'wendland6': return sph_kernel_wendland6 elif kernel_name == 'none': return sph_kernel_dummy else: raise NotImplementedError cdef class ParticleDepositOperation: # We assume each will allocate and define their own temporary storage cdef kernel_func sph_kernel cdef public object nvals cdef public int update_values cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind) except -1 nogil yt-project-yt-f043ac8/yt/geometry/particle_deposit.pyx000066400000000000000000000503731510711153200232450ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Particle Deposition onto Cells """ cimport numpy as np import numpy as np cimport cython from libc.math cimport sqrt from yt.utilities.lib.fp_utils cimport * from .oct_container cimport Oct, OctInfo, OctreeContainer from yt.utilities.lib.misc_utilities import OnceIndirect cdef append_axes(np.ndarray arr, int naxes): if arr.ndim == naxes: return arr # Avoid copies arr2 = arr.view() arr2.shape = arr2.shape + (1,) * (naxes - arr2.ndim) return arr2 cdef class ParticleDepositOperation: def __init__(self, nvals, kernel_name): # nvals is a tuple containing the active dimensions of the # grid to deposit onto and the number of grids, # (nx, ny, nz, ngrids) self.nvals = nvals self.update_values = 0 # This is the default self.sph_kernel = get_kernel_func(kernel_name) def initialize(self, *args): raise NotImplementedError def finalize(self, *args): raise NotImplementedError @cython.boundscheck(False) @cython.wraparound(False) def process_octree(self, OctreeContainer octree, np.ndarray[np.int64_t, ndim=1] dom_ind, np.ndarray[np.float64_t, ndim=2] positions, fields = None, int domain_id = -1, int domain_offset = 0, lvlmax = None): cdef int nf, i, j if fields is None: fields = [] nf = len(fields) cdef np.float64_t[::cython.view.indirect, ::1] field_pointers if nf > 0: field_pointers = OnceIndirect(fields) cdef np.float64_t pos[3] cdef np.float64_t[:] field_vals = np.empty(nf, dtype="float64") cdef int dims[3] dims[0] = dims[1] = dims[2] = octree.nz cdef OctInfo oi cdef np.int64_t offset, moff cdef Oct *oct cdef np.int8_t use_lvlmax moff = octree.get_domain_offset(domain_id + domain_offset) if lvlmax is None: use_lvlmax = False lvlmax = [] else: use_lvlmax = True cdef np.ndarray[np.int32_t, ndim=1] lvlmaxval = np.asarray(lvlmax, dtype=np.int32) for i in range(positions.shape[0]): # We should check if particle remains inside the Oct here for j in range(nf): field_vals[j] = field_pointers[j,i] for j in range(3): pos[j] = positions[i, j] # This line should be modified to have it return the index into an # array based on whatever cutting of the domain we have done. This # may or may not include the domain indices that we have # previously generated. This way we can support not knowing the # full octree structure. All we *really* care about is some # arbitrary offset into a field value for deposition. if not use_lvlmax: oct = octree.get(pos, &oi) else: oct = octree.get(pos, &oi, max_level=lvlmaxval[i]) # This next line is unfortunate. Basically it says, sometimes we # might have particles that belong to octs outside our domain. # For the distributed-memory octrees, this will manifest as a NULL # oct. For the non-distributed memory octrees, we'll simply see # this as a domain_id that is not the current domain id. Note that # this relies on the idea that all the particles in a region are # all fed to sequential domain subsets, which will not be true with # RAMSES, where we *will* miss particles that live in ghost # regions on other processors. Addressing this is on the TODO # list. if oct == NULL or (domain_id > 0 and oct.domain != domain_id): continue # Note that this has to be our local index, not our in-file index. offset = dom_ind[oct.domain_ind - moff] if offset < 0: continue # Check that we found the oct ... self.process(dims, i, oi.left_edge, oi.dds, offset, pos, field_vals, oct.domain_ind) if self.update_values == 1: for j in range(nf): field_pointers[j][i] = field_vals[j] @cython.boundscheck(False) @cython.wraparound(False) def process_grid(self, gobj, np.ndarray[np.float64_t, ndim=2, cast=True] positions, fields = None): cdef int nf, i, j if fields is None: fields = [] if positions.shape[0] == 0: return nf = len(fields) cdef np.float64_t[:] field_vals = np.empty(nf, dtype="float64") cdef np.float64_t[::cython.view.indirect, ::1] field_pointers if nf > 0: field_pointers = OnceIndirect(fields) cdef np.float64_t pos[3] cdef np.int64_t gid = getattr(gobj, "id", -1) cdef np.float64_t dds[3] cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef int dims[3] for i in range(3): dds[i] = gobj.dds[i] left_edge[i] = gobj.LeftEdge[i] right_edge[i] = gobj.RightEdge[i] dims[i] = gobj.ActiveDimensions[i] for i in range(positions.shape[0]): # Now we process for j in range(nf): field_vals[j] = field_pointers[j,i] for j in range(3): pos[j] = positions[i, j] continue_loop = False for j in range(3): if pos[j] < left_edge[j] or pos[j] > right_edge[j]: continue_loop = True if continue_loop: continue self.process(dims, i, left_edge, dds, 0, pos, field_vals, gid) if self.update_values == 1: for j in range(nf): field_pointers[j][i] = field_vals[j] cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind) except -1 nogil: with gil: raise NotImplementedError cdef class CountParticles(ParticleDepositOperation): cdef np.int64_t[:,:,:,:] count def initialize(self): # Create a numpy array accessible to python self.count = append_axes( np.zeros(self.nvals, dtype="int64", order='F'), 4) @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, # offset into IO field np.float64_t ppos[3], # this particle's position np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: # here we do our thing; this is the kernel cdef int ii[3] cdef int i for i in range(3): ii[i] = ((ppos[i] - left_edge[i])/dds[i]) self.count[ii[2], ii[1], ii[0], offset] += 1 return 0 def finalize(self): arr = np.asarray(self.count) arr.shape = self.nvals return arr.astype("float64") deposit_count = CountParticles cdef class SimpleSmooth(ParticleDepositOperation): # Note that this does nothing at the edges. So it will give a poor # estimate there, and since Octrees are mostly edges, this will be a very # poor SPH kernel. cdef np.float64_t[:,:,:,:] data cdef np.float64_t[:,:,:,:] temp def initialize(self): self.data = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) self.temp = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: cdef int ii[3] cdef int ib0[3] cdef int ib1[3] cdef int i, j, k, half_len cdef np.float64_t idist[3] cdef np.float64_t kernel_sum, dist # Smoothing length is fields[0] kernel_sum = 0.0 for i in range(3): ii[i] = ((ppos[i] - left_edge[i])/dds[i]) half_len = (fields[0]/dds[i]) + 1 ib0[i] = ii[i] - half_len ib1[i] = ii[i] + half_len if ib0[i] >= dim[i] or ib1[i] <0: return 0 ib0[i] = iclip(ib0[i], 0, dim[i] - 1) ib1[i] = iclip(ib1[i], 0, dim[i] - 1) for i from ib0[0] <= i <= ib1[0]: idist[0] = (ii[0] - i) * dds[0] idist[0] *= idist[0] for j from ib0[1] <= j <= ib1[1]: idist[1] = (ii[1] - j) * dds[1] idist[1] *= idist[1] for k from ib0[2] <= k <= ib1[2]: idist[2] = (ii[2] - k) * dds[2] idist[2] *= idist[2] dist = idist[0] + idist[1] + idist[2] # Calculate distance in multiples of the smoothing length dist = sqrt(dist) / fields[0] with gil: self.temp[k,j,i,offset] = self.sph_kernel(dist) kernel_sum += self.temp[k,j,i,offset] # Having found the kernel, deposit accordingly into gdata for i from ib0[0] <= i <= ib1[0]: for j from ib0[1] <= j <= ib1[1]: for k from ib0[2] <= k <= ib1[2]: dist = self.temp[k,j,i,offset] / kernel_sum self.data[k,j,i,offset] += fields[1] * dist return 0 def finalize(self): return self.odata deposit_simple_smooth = SimpleSmooth cdef class SumParticleField(ParticleDepositOperation): cdef np.float64_t[:,:,:,:] sum def initialize(self): self.sum = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: cdef int ii[3] cdef int i for i in range(3): ii[i] = ((ppos[i] - left_edge[i]) / dds[i]) self.sum[ii[2], ii[1], ii[0], offset] += fields[0] return 0 def finalize(self): sum = np.asarray(self.sum) sum.shape = self.nvals return sum deposit_sum = SumParticleField cdef class StdParticleField(ParticleDepositOperation): # Thanks to Britton and MJ Turk for the link # to a single-pass STD # http://www.cs.berkeley.edu/~mhoemmen/cs194/Tutorials/variance.pdf cdef np.float64_t[:,:,:,:] mk cdef np.float64_t[:,:,:,:] qk cdef np.float64_t[:,:,:,:] i def initialize(self): # we do this in a single pass, but need two scalar # per cell, M_k, and Q_k and also the number of particles # deposited into each one # the M_k term self.mk = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) self.qk = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) self.i = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: cdef int ii[3] cdef int i cdef float k, mk, qk for i in range(3): ii[i] = ((ppos[i] - left_edge[i])/dds[i]) k = self.i[ii[2], ii[1], ii[0], offset] mk = self.mk[ii[2], ii[1], ii[0], offset] qk = self.qk[ii[2], ii[1], ii[0], offset] if k == 0.0: # Initialize cell values self.mk[ii[2], ii[1], ii[0], offset] = fields[0] else: self.mk[ii[2], ii[1], ii[0], offset] = mk + (fields[0] - mk) / k self.qk[ii[2], ii[1], ii[0], offset] = \ qk + (k - 1.0) * (fields[0] - mk) * (fields[0] - mk) / k self.i[ii[2], ii[1], ii[0], offset] += 1 return 0 def finalize(self): # This is the standard variance # if we want sample variance divide by (self.oi - 1.0) i = np.asarray(self.i) std2 = np.asarray(self.qk) / i std2[i == 0.0] = 0.0 std2.shape = self.nvals return np.sqrt(std2) deposit_std = StdParticleField cdef class CICDeposit(ParticleDepositOperation): cdef np.float64_t[:,:,:,:] field cdef public object ofield def initialize(self): if not all(_ > 1 for _ in self.nvals[:-1]): from yt.utilities.exceptions import YTBoundsDefinitionError raise YTBoundsDefinitionError( "CIC requires minimum of 2 zones in all spatial dimensions.", self.nvals[:-1]) self.field = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, # offset into IO field np.float64_t ppos[3], # this particle's position np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: cdef int i, j, k cdef int ind[3] cdef np.float64_t rpos[3] cdef np.float64_t rdds[3][2] # Compute the position of the central cell for i in range(3): rpos[i] = (ppos[i]-left_edge[i])/dds[i] rpos[i] = fclip(rpos[i], 0.5001, dim[i]-0.5001) ind[i] = (rpos[i] + 0.5) # Note these are 1, then 0 rdds[i][1] = ( ind[i]) + 0.5 - rpos[i] rdds[i][0] = 1.0 - rdds[i][1] for i in range(2): for j in range(2): for k in range(2): self.field[ind[2] - k, ind[1] - j, ind[0] - i, offset] += \ fields[0]*rdds[0][i]*rdds[1][j]*rdds[2][k] return 0 def finalize(self): rv = np.asarray(self.field) rv.shape = self.nvals return rv deposit_cic = CICDeposit cdef class WeightedMeanParticleField(ParticleDepositOperation): # Deposit both mass * field and mass into two scalars # then in finalize divide mass * field / mass cdef np.float64_t[:,:,:,:] wf cdef np.float64_t[:,:,:,:] w def initialize(self): self.wf = append_axes( np.zeros(self.nvals, dtype='float64', order='F'), 4) self.w = append_axes( np.zeros(self.nvals, dtype='float64', order='F'), 4) @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: cdef int ii[3] cdef int i for i in range(3): ii[i] = ((ppos[i] - left_edge[i]) / dds[i]) self.w[ii[2], ii[1], ii[0], offset] += fields[1] self.wf[ii[2], ii[1], ii[0], offset] += fields[0] * fields[1] return 0 def finalize(self): wf = np.asarray(self.wf) w = np.asarray(self.w) with np.errstate(divide='ignore', invalid='ignore'): rv = wf / w rv.shape = self.nvals return rv deposit_weighted_mean = WeightedMeanParticleField cdef class MeshIdentifier(ParticleDepositOperation): # This is a tricky one! What it does is put into the particle array the # value of the oct or block (grids will always be zero) identifier that a # given particle resides in def initialize(self): self.update_values = 1 @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: fields[0] = domain_ind return 0 def finalize(self): return deposit_mesh_id = MeshIdentifier cdef class CellIdentifier(ParticleDepositOperation): cdef np.int64_t[:] indexes, cell_index # This method stores the offset of the grid containing each particle # and compute the index of the cell in the oct. def initialize(self, int npart): self.indexes = np.zeros(npart, dtype=np.int64, order='F') - 1 self.cell_index = np.zeros(npart, dtype=np.int64, order='F') - 1 @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: cdef int i, icell self.indexes[ipart] = offset icell = 0 for i in range(3): if ppos[i] > left_edge[i] + dds[i]: icell |= 4 >> i # Compute cell index self.cell_index[ipart] = icell return 0 def finalize(self): return np.asarray(self.indexes), np.asarray(self.cell_index) deposit_cell_id = CellIdentifier cdef class NNParticleField(ParticleDepositOperation): cdef np.float64_t[:,:,:,:] nnfield cdef np.float64_t[:,:,:,:] distfield def initialize(self): self.nnfield = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) self.distfield = append_axes( np.zeros(self.nvals, dtype="float64", order='F'), 4) self.distfield[:] = np.inf @cython.cdivision(True) @cython.boundscheck(False) cdef int process(self, int dim[3], int ipart, np.float64_t left_edge[3], np.float64_t dds[3], np.int64_t offset, np.float64_t ppos[3], np.float64_t[:] fields, np.int64_t domain_ind ) except -1 nogil: # This one is a bit slow. Every grid cell is going to be iterated # over, and we're going to deposit particles in it. cdef int i, j, k cdef np.float64_t r2 cdef np.float64_t gpos[3] gpos[0] = left_edge[0] + 0.5 * dds[0] for i in range(dim[0]): gpos[1] = left_edge[1] + 0.5 * dds[1] for j in range(dim[1]): gpos[2] = left_edge[2] + 0.5 * dds[2] for k in range(dim[2]): r2 = ((ppos[0] - gpos[0])*(ppos[0] - gpos[0]) + (ppos[1] - gpos[1])*(ppos[1] - gpos[1]) + (ppos[2] - gpos[2])*(ppos[2] - gpos[2])) if r2 < self.distfield[k,j,i,offset]: self.distfield[k,j,i,offset] = r2 self.nnfield[k,j,i,offset] = fields[0] gpos[2] += dds[2] gpos[1] += dds[1] gpos[0] += dds[0] return 0 def finalize(self): nn = np.asarray(self.nnfield) nn.shape = self.nvals return nn deposit_nearest = NNParticleField yt-project-yt-f043ac8/yt/geometry/particle_geometry_handler.py000066400000000000000000000441431510711153200247340ustar00rootroot00000000000000import collections import errno import os import struct import weakref import numpy as np from ewah_bool_utils.ewah_bool_wrap import BoolArrayCollection from yt.data_objects.index_subobjects.particle_container import ParticleContainer from yt.funcs import get_pbar, is_sequence, only_on_root from yt.geometry.geometry_handler import Index, YTDataChunk from yt.geometry.particle_oct_container import ParticleBitmap from yt.utilities.lib.fnv_hash import fnv_hash from yt.utilities.logger import ytLogger as mylog from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_objects def validate_index_order(index_order): if index_order is None: index_order = (6, 2) elif not is_sequence(index_order): index_order = (int(index_order), 1) else: if len(index_order) != 2: raise RuntimeError( f"Tried to load a dataset with index_order={index_order}, but " "index_order\nmust be an integer or a two-element tuple of " "integers." ) index_order = tuple(int(o) for o in index_order) return index_order class ParticleIndexInfo: def __init__(self, order1, order2, filename, mutable_index): self._order1 = order1 self._order2 = order2 self._order2_orig = order2 self.filename = filename self.mutable_index = mutable_index self._is_loaded = False @property def order1(self): return self._order1 @property def order2(self): return self._order2 @order2.setter def order2(self, value): if value == self._order2: # do nothing if nothing changes return mylog.debug("Updating index_order2 from %s to %s", self._order2, value) self._order2 = value @property def order2_orig(self): return self._order2_orig class ParticleIndex(Index): """The Index subclass for particle datasets""" def __init__(self, ds, dataset_type): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) self.float_type = np.float64 super().__init__(ds, dataset_type) self._initialize_index() def _setup_geometry(self): self.regions = None def get_smallest_dx(self): """ Returns (in code units) the smallest cell size in the simulation. """ return self.ds.arr(0, "code_length") def _get_particle_type_counts(self): result = collections.defaultdict(lambda: 0) for df in self.data_files: for k in df.total_particles.keys(): result[k] += df.total_particles[k] return dict(result) def convert(self, unit): return self.dataset.conversion_factors[unit] @property def chunksize(self): # This can be overridden in subclasses return 64**3 _data_files = None @property def data_files(self): if self._data_files is not None: return self._data_files self._setup_filenames() return self._data_files @data_files.setter def data_files(self, value): self._data_files = value _total_particles = None @property def total_particles(self): if self._total_particles is not None: return self._total_particles self._total_particles = sum( sum(d.total_particles.values()) for d in self.data_files ) return self._total_particles def _setup_filenames(self): template = self.dataset.filename_template ndoms = self.dataset.file_count cls = self.dataset._file_class self.data_files = [] fi = 0 for i in range(int(ndoms)): start = 0 if self.chunksize > 0: end = start + self.chunksize else: end = None while True: try: _filename = template % {"num": i} df = cls(self.dataset, self.io, _filename, fi, (start, end)) except FileNotFoundError: mylog.warning( "Failed to load '%s' (missing file or directory)", _filename ) break if max(df.total_particles.values()) == 0: break fi += 1 self.data_files.append(df) if end is None: break start = end end += self.chunksize def _initialize_index(self): ds = self.dataset only_on_root( mylog.info, "Allocating for %0.4g particles", self.total_particles, global_rootonly=True, ) # if we have not yet set domain_left_edge and domain_right_edge then do # an I/O pass over the particle coordinates to determine a bounding box if self.ds.domain_left_edge is None: min_ppos = np.empty(3, dtype="float64") min_ppos[:] = np.nan max_ppos = np.empty(3, dtype="float64") max_ppos[:] = np.nan only_on_root( mylog.info, "Bounding box cannot be inferred from metadata, reading " "particle positions to infer bounding box", ) for df in self.data_files: for _, ppos in self.io._yield_coordinates(df): min_ppos = np.nanmin(np.vstack([min_ppos, ppos]), axis=0) max_ppos = np.nanmax(np.vstack([max_ppos, ppos]), axis=0) only_on_root( mylog.info, f"Load this dataset with bounding_box=[{min_ppos}, {max_ppos}] " "to avoid I/O overhead from inferring bounding_box.", ) ds.domain_left_edge = ds.arr(1.05 * min_ppos, "code_length") ds.domain_right_edge = ds.arr(1.05 * max_ppos, "code_length") ds.domain_width = ds.domain_right_edge - ds.domain_left_edge # use a trivial morton index for datasets containing a single chunk if len(self.data_files) == 1: order1 = 1 order2 = 1 mutable_index = False else: mutable_index = ds.index_order is None index_order = validate_index_order(ds.index_order) order1 = index_order[0] order2 = index_order[1] if order1 == 1 and order2 == 1: dont_cache = True else: dont_cache = False if not hasattr(self.ds, "_file_hash"): self.ds._file_hash = self._generate_hash() # Load Morton index from file if provided fname = getattr(ds, "index_filename", None) or f"{ds.parameter_filename}.ewah" self.pii = ParticleIndexInfo(order1, order2, fname, mutable_index) self.regions = ParticleBitmap( ds.domain_left_edge, ds.domain_right_edge, ds.periodicity, self.ds._file_hash, len(self.data_files), index_order1=self.pii.order1, index_order2=self.pii.order2_orig, ) dont_load = dont_cache and not hasattr(ds, "index_filename") try: if dont_load: raise OSError rflag, max_hsml = self.regions.load_bitmasks(fname) if max_hsml > 0.0 and self.pii.mutable_index: self._order2_update(max_hsml) rflag = self.regions.check_bitmasks() self._initialize_frontend_specific() if rflag == 0: raise OSError self.pii._is_loaded = True except (OSError, struct.error): self.regions.reset_bitmasks() max_hsml = self._initialize_coarse_index() self._initialize_refined_index() wdir = os.path.dirname(fname) if not dont_cache and os.access(wdir, os.W_OK): # Sometimes os mis-reports whether a directory is writable, # So pass if writing the bitmask file fails. try: self.regions.save_bitmasks(fname, max_hsml) except OSError: pass rflag = self.regions.check_bitmasks() self.ds.index_order = (self.pii.order1, self.pii.order2) def _order2_update(self, max_hsml): # By passing this in, we only allow index_order2 to be increased by # two at most, never increased. One place this becomes particularly # useful is in the case of an extremely small section of gas # particles embedded in a much much larger domain. The max # smoothing length will be quite small, so based on the larger # domain, it will correspond to a very very high index order, which # is a large amount of memory! Having multiple indexes, one for # each particle type, would fix this. new_order2 = self.regions.update_mi2(max_hsml, self.pii.order2 + 2) self.pii.order2 = new_order2 def _initialize_coarse_index(self): max_hsml = 0.0 pb = get_pbar("Initializing coarse index ", len(self.data_files)) for i, data_file in parallel_objects(enumerate(self.data_files)): pb.update(i + 1) for ptype, pos in self.io._yield_coordinates(data_file): ds = self.ds if hasattr(ds, "_sph_ptypes") and ptype == ds._sph_ptypes[0]: hsml = self.io._get_smoothing_length( data_file, pos.dtype, pos.shape ) if hsml is not None and hsml.size > 0.0: max_hsml = max(max_hsml, hsml.max()) else: hsml = None self.regions._coarse_index_data_file(pos, hsml, data_file.file_id) pb.finish() self.regions.masks = self.comm.mpi_allreduce(self.regions.masks, op="sum") self.regions.particle_counts = self.comm.mpi_allreduce( self.regions.particle_counts, op="sum" ) for data_file in self.data_files: self.regions._set_coarse_index_data_file(data_file.file_id) self.regions.find_collisions_coarse() if max_hsml > 0.0 and self.pii.mutable_index: self._order2_update(max_hsml) return max_hsml def _initialize_refined_index(self): mask = self.regions.masks.sum(axis=1).astype("uint8") max_npart = max(sum(d.total_particles.values()) for d in self.data_files) * 28 sub_mi1 = np.zeros(max_npart, "uint64") sub_mi2 = np.zeros(max_npart, "uint64") pb = get_pbar("Initializing refined index", len(self.data_files)) mask_threshold = getattr(self, "_index_mask_threshold", 2) count_threshold = getattr(self, "_index_count_threshold", 256) mylog.debug( "Using estimated thresholds of %s and %s for refinement", mask_threshold, count_threshold, ) total_refined = 0 total_coarse_refined = ( (mask >= 2) & (self.regions.particle_counts > count_threshold) ).sum() mylog.debug( "This should produce roughly %s zones, for %s of the domain", total_coarse_refined, 100 * total_coarse_refined / mask.size, ) storage = {} for sto, (i, data_file) in parallel_objects( enumerate(self.data_files), storage=storage ): coll = None pb.update(i + 1) nsub_mi = 0 for ptype, pos in self.io._yield_coordinates(data_file): if pos.size == 0: continue if hasattr(self.ds, "_sph_ptypes") and ptype == self.ds._sph_ptypes[0]: hsml = self.io._get_smoothing_length( data_file, pos.dtype, pos.shape ) else: hsml = None nsub_mi, coll = self.regions._refined_index_data_file( coll, pos, hsml, mask, sub_mi1, sub_mi2, data_file.file_id, nsub_mi, count_threshold=count_threshold, mask_threshold=mask_threshold, ) total_refined += nsub_mi sto.result_id = i if coll is None: coll_str = b"" else: coll_str = coll.dumps() sto.result = (data_file.file_id, coll_str) pb.finish() for i in sorted(storage): file_id, coll_str = storage[i] coll = BoolArrayCollection() coll.loads(coll_str) self.regions.bitmasks.append(file_id, coll) self.regions.find_collisions_refined() def _detect_output_fields(self): # TODO: Add additional fields dsl = [] units = {} pcounts = self._get_particle_type_counts() field_cache = {} for dom in self.data_files: if dom.filename in field_cache: fl, _units = field_cache[dom.filename] else: fl, _units = self.io._identify_fields(dom) field_cache[dom.filename] = fl, _units units.update(_units) dom._calculate_offsets(fl, pcounts) for f in fl: if f not in dsl: dsl.append(f) self.field_list = dsl ds = self.dataset ds.particle_types = tuple({pt for pt, ds in dsl}) # This is an attribute that means these particle types *actually* # exist. As in, they are real, in the dataset. ds.field_units.update(units) ds.particle_types_raw = ds.particle_types def _identify_base_chunk(self, dobj): # Must check that chunk_info contains the right number of ghost zones if getattr(dobj, "_chunk_info", None) is None: if isinstance(dobj, ParticleContainer): dobj._chunk_info = [dobj] else: # TODO: only return files if getattr(dobj.selector, "is_all_data", False): nfiles = self.regions.nfiles dfi = np.arange(nfiles) else: dfi, file_masks, addfi = self.regions.identify_file_masks( dobj.selector ) nfiles = len(file_masks) dobj._chunk_info = [None for _ in range(nfiles)] # The following was moved here from ParticleContainer in order # to make the ParticleContainer object pickleable. By having # the base_selector as its own argument, we avoid having to # rebuild the index on unpickling a ParticleContainer. if hasattr(dobj, "base_selector"): base_selector = dobj.base_selector base_region = dobj.base_region else: base_region = dobj base_selector = dobj.selector for i in range(nfiles): domain_id = i + 1 dobj._chunk_info[i] = ParticleContainer( base_region, base_selector, [self.data_files[dfi[i]]], domain_id=domain_id, ) # NOTE: One fun thing about the way IO works is that it # consolidates things quite nicely. So we should feel free to # create as many objects as part of the chunk as we want, since # it'll take the set() of them. So if we break stuff up like # this here, we end up in a situation where we have the ability # to break things down further later on for buffer zones and the # like. (dobj._current_chunk,) = self._chunk_all(dobj) def _chunk_all(self, dobj): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) yield YTDataChunk(dobj, "all", oobjs, None) def _chunk_spatial(self, dobj, ngz, sort=None, preload_fields=None): sobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for og in sobjs: with og._expand_data_files(): if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og yield YTDataChunk(dobj, "spatial", [g]) def _chunk_io(self, dobj, cache=True, local_only=False): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for container in oobjs: yield YTDataChunk(dobj, "io", [container], None, cache=cache) def _generate_hash(self): # Generate an FNV hash by creating a byte array containing the # modification time of as well as the first and last 1 MB of data in # every output file ret = bytearray() for pfile in self.data_files: # only look at "real" files, not "fake" files generated by the # chunking system if pfile.start not in (0, None): continue try: mtime = os.path.getmtime(pfile.filename) except OSError as e: if e.errno == errno.ENOENT: # this is an in-memory file so we return with a dummy # value return -1 else: raise ret.extend(str(mtime).encode("utf-8")) size = os.path.getsize(pfile.filename) if size > 1e6: size = int(1e6) with open(pfile.filename, "rb") as fh: # read in first and last 1 MB of data data = fh.read(size) fh.seek(-size, os.SEEK_END) data = fh.read(size) ret.extend(data) return fnv_hash(ret) def _initialize_frontend_specific(self): """This is for frontend-specific initialization code If there are frontend-specific things that need to be set while creating the index, this function forces these operations to happen in cases where we are reloading the index from a sidecar file. """ pass yt-project-yt-f043ac8/yt/geometry/particle_oct_container.pyx000066400000000000000000002756431510711153200244360ustar00rootroot00000000000000# distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG # distutils: include_dirs = LIB_DIR # distutils: libraries = EWAH_LIBS """ Oct container tuned for Particles """ from ewah_bool_utils.ewah_bool_array cimport ( bool_array, ewah_bool_array, ewah_bool_iterator, ewah_word_type, ) from libc.math cimport ceil, log2 from libc.stdlib cimport free, malloc from libcpp.map cimport map as cmap from libcpp.vector cimport vector import numpy as np cimport cython cimport numpy as np from cpython.exc cimport PyErr_CheckSignals from cython.operator cimport dereference, preincrement from yt.geometry cimport oct_visitors from yt.utilities.lib.fnv_hash cimport c_fnv_hash as fnv_hash from yt.utilities.lib.fp_utils cimport * from yt.utilities.lib.geometry_utils cimport ( bounded_morton, bounded_morton_dds, bounded_morton_split_dds, bounded_morton_split_relative_dds, decode_morton_64bit, encode_morton_64bit, morton_neighbors_coarse, morton_neighbors_refined, ) from .oct_container cimport ( ORDER_MAX, Oct, OctKey, OctreeContainer, SparseOctreeContainer, ) from .oct_visitors cimport cind from .selection_routines cimport AlwaysSelector, SelectorObject from yt.funcs import get_pbar from ewah_bool_utils.ewah_bool_wrap cimport BoolArrayCollection import os from ewah_bool_utils.ewah_bool_wrap cimport ( BoolArrayCollectionUncompressed as BoolArrayColl, FileBitmasks, SparseUnorderedRefinedBitmaskSet as SparseUnorderedRefinedBitmask, ) _bitmask_version = np.uint64(5) ctypedef cmap[np.uint64_t, bool_array] CoarseRefinedSets cdef class ParticleOctreeContainer(OctreeContainer): cdef Oct** oct_list #The starting oct index of each domain cdef np.int64_t *dom_offsets #How many particles do we keep before refining cdef public int n_ref def allocate_root(self): cdef int i, j, k cdef Oct *cur for i in range(self.nn[0]): for j in range(self.nn[1]): for k in range(self.nn[2]): cur = self.allocate_oct() self.root_mesh[i][j][k] = cur def __dealloc__(self): #Call the freemem ops on every ocy #of the root mesh recursively cdef int i, j, k if self.root_mesh == NULL: return for i in range(self.nn[0]): if self.root_mesh[i] == NULL: continue for j in range(self.nn[1]): if self.root_mesh[i][j] == NULL: continue for k in range(self.nn[2]): if self.root_mesh[i][j][k] == NULL: continue self.visit_free(self.root_mesh[i][j][k]) free(self.oct_list) free(self.dom_offsets) cdef void visit_free(self, Oct *o): #Free the memory for this oct recursively cdef int i, j, k for i in range(2): for j in range(2): for k in range(2): if o.children != NULL \ and o.children[cind(i,j,k)] != NULL: self.visit_free(o.children[cind(i,j,k)]) free(o.children) free(o) def clear_fileind(self): cdef int i, j, k for i in range(self.nn[0]): for j in range(self.nn[1]): for k in range(self.nn[2]): self.visit_clear(self.root_mesh[i][j][k]) cdef void visit_clear(self, Oct *o): #Free the memory for this oct recursively cdef int i, j, k o.file_ind = 0 for i in range(2): for j in range(2): for k in range(2): if o.children != NULL \ and o.children[cind(i,j,k)] != NULL: self.visit_clear(o.children[cind(i,j,k)]) def __iter__(self): #Get the next oct, will traverse domains #Note that oct containers can be sorted #so that consecutive octs are on the same domain cdef int oi cdef Oct *o for oi in range(self.nocts): o = self.oct_list[oi] yield (o.file_ind, o.domain_ind, o.domain) def allocate_domains(self, domain_counts): pass def finalize(self, int domain_id = 0): #This will sort the octs in the oct list #so that domains appear consecutively #And then find the oct index/offset for #every domain cdef int max_level = 0 self.oct_list = malloc(sizeof(Oct*)*self.nocts) cdef np.int64_t i = 0, lpos = 0 # Note that we now assign them in the same order they will be visited # by recursive visitors. for i in range(self.nn[0]): for j in range(self.nn[1]): for k in range(self.nn[2]): self.visit_assign(self.root_mesh[i][j][k], &lpos, 0, &max_level) assert(lpos == self.nocts) for i in range(self.nocts): self.oct_list[i].domain_ind = i self.oct_list[i].domain = domain_id self.max_level = max_level cdef visit_assign(self, Oct *o, np.int64_t *lpos, int level, int *max_level): cdef int i, j, k self.oct_list[lpos[0]] = o lpos[0] += 1 max_level[0] = imax(max_level[0], level) for i in range(2): for j in range(2): for k in range(2): if o.children != NULL \ and o.children[cind(i,j,k)] != NULL: self.visit_assign(o.children[cind(i,j,k)], lpos, level + 1, max_level) return cdef np.int64_t get_domain_offset(self, int domain_id): return 0 cdef Oct* allocate_oct(self): #Allocate the memory, set to NULL or -1 #We reserve space for n_ref particles, but keep #track of how many are used with np initially 0 self.nocts += 1 cdef Oct *my_oct = malloc(sizeof(Oct)) my_oct.domain = -1 my_oct.file_ind = 0 my_oct.domain_ind = self.nocts - 1 my_oct.children = NULL return my_oct @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def add(self, np.ndarray[np.uint64_t, ndim=1] indices, np.uint8_t order = ORDER_MAX): #Add this particle to the root oct #Then if that oct has children, add it to them recursively #If the child needs to be refined because of max particles, do so cdef np.int64_t no = indices.shape[0], p cdef np.uint64_t index cdef int i, level cdef int ind[3] if self.root_mesh[0][0][0] == NULL: self.allocate_root() cdef np.uint64_t *data = indices.data for p in range(no): # We have morton indices, which means we choose left and right by # looking at (MAX_ORDER - level) & with the values 1, 2, 4. level = 0 index = indices[p] if index == FLAG: # This is a marker for the index not being inside the domain # we're interested in. continue # Convert morton index to 3D index of octree root for i in range(3): ind[i] = (index >> ((order - level)*3 + (2 - i))) & 1 cur = self.root_mesh[ind[0]][ind[1]][ind[2]] if cur == NULL: raise RuntimeError # Continue refining the octree until you reach the level of the # morton indexing order. Along the way, use prefix to count # previous indices at levels in the octree? while (cur.file_ind + 1) > self.n_ref: if level >= order: break # Just dump it here. level += 1 for i in range(3): ind[i] = (index >> ((order - level)*3 + (2 - i))) & 1 if cur.children == NULL or \ cur.children[cind(ind[0],ind[1],ind[2])] == NULL: cur = self.refine_oct(cur, index, level, order) self.filter_particles(cur, data, p, level, order) else: cur = cur.children[cind(ind[0],ind[1],ind[2])] # If our n_ref is 1, we are always refining, which means we're an # index octree. In this case, we should store the index for fast # lookup later on when we find neighbors and the like. if self.n_ref == 1: cur.file_ind = index else: cur.file_ind += 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef Oct *refine_oct(self, Oct *o, np.uint64_t index, int level, np.uint8_t order): #Allocate and initialize child octs #Attach particles to child octs #Remove particles from this oct entirely cdef int i, j, k cdef int ind[3] cdef Oct *noct # TODO: This does not need to be changed. o.children = malloc(sizeof(Oct *)*8) for i in range(2): for j in range(2): for k in range(2): noct = self.allocate_oct() noct.domain = o.domain noct.file_ind = 0 o.children[cind(i,j,k)] = noct o.file_ind = self.n_ref + 1 for i in range(3): ind[i] = (index >> ((order - level)*3 + (2 - i))) & 1 noct = o.children[cind(ind[0],ind[1],ind[2])] return noct cdef void filter_particles(self, Oct *o, np.uint64_t *data, np.int64_t p, int level, np.uint8_t order): # Now we look at the last nref particles to decide where they go. # If p: Loops over all previous morton indices # If n_ref: Loops over n_ref previous morton indices cdef int n = imin(p, self.n_ref) cdef np.uint64_t *arr = data + imax(p - self.n_ref, 0) cdef np.uint64_t prefix1, prefix2 # Now we figure out our prefix, which is the oct address at this level. # As long as we're actually in Morton order, we do not need to worry # about *any* of the other children of the oct. prefix1 = data[p] >> (order - level)*3 for i in range(n): prefix2 = arr[i] >> (order - level)*3 if (prefix1 == prefix2): o.file_ind += 1 # Says how many morton indices are in this octant? def recursively_count(self): #Visit every cell, accumulate the # of cells per level cdef int i, j, k cdef np.int64_t counts[128] for i in range(128): counts[i] = 0 for i in range(self.nn[0]): for j in range(self.nn[1]): for k in range(self.nn[2]): if self.root_mesh[i][j][k] != NULL: self.visit(self.root_mesh[i][j][k], counts) level_counts = {} for i in range(128): if counts[i] == 0: break level_counts[i] = counts[i] return level_counts cdef visit(self, Oct *o, np.int64_t *counts, level = 0): cdef int i, j, k counts[level] += 1 for i in range(2): for j in range(2): for k in range(2): if o.children != NULL \ and o.children[cind(i,j,k)] != NULL: self.visit(o.children[cind(i,j,k)], counts, level + 1) return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef Oct *get_from_index(self, np.uint64_t mi, np.uint8_t order = ORDER_MAX, int max_level = 99): cdef Oct *cur cdef Oct *next cur = next = NULL cdef int i cdef np.int64_t level = -1 cdef int ind32[3] cdef np.uint64_t ind[3] cdef np.uint64_t index # Get level offset cdef int level_offset[3] for i in range(3): level_offset[i] = np.log2(self.nn[i]) if (1 << level_offset[i]) != self.nn[i]: raise Exception("Octree does not have roots along dimension {} in a power of 2 ".format(i)) for i in range(2,3): if level_offset[i] != level_offset[0]: raise Exception("Octree must have the same number of roots in each dimension for this.") # Get root for index index = (mi >> ((order - level_offset[0])*3)) decode_morton_64bit(index, ind) for i in range(3): ind32[i] = ind[i] self.get_root(ind32, &next) # We want to stop recursing when there's nowhere else to go level = level_offset[0] max_level = min(max_level, order) while next != NULL and level <= max_level: level += 1 for i in range(3): ind[i] = (mi >> ((order - level)*3 + (2 - i))) & 1 cur = next if cur.children != NULL: next = cur.children[cind(ind[0],ind[1],ind[2])] else: next = NULL return cur def apply_domain(self, int domain_id, BoolArrayCollection mask, int masklevel): cdef SelectorObject selector = AlwaysSelector(None) ind = self.domain_ind(selector, mask = mask, masklevel = masklevel) for i in range(self.nocts): if ind[i] < 0: continue self.oct_list[i].domain = domain_id super(ParticleOctreeContainer,self).domain_ind(selector, domain_id = domain_id) def domain_ind(self, selector, int domain_id = -1, BoolArrayCollection mask = None, int masklevel = 99): if mask is None: return super(ParticleOctreeContainer,self).domain_ind(selector, domain_id = domain_id) # Create mask for octs that are touched by the mask cdef ewah_bool_array *ewah_slct = mask.ewah_keys cdef ewah_bool_iterator *iter_set = new ewah_bool_iterator(ewah_slct[0].begin()) cdef ewah_bool_iterator *iter_end = new ewah_bool_iterator(ewah_slct[0].end()) cdef np.ndarray[np.uint8_t, ndim=1] oct_mask oct_mask = np.zeros(self.nocts, 'uint8') cdef Oct *o cdef int coct, cmi coct = cmi = 0 while iter_set[0] != iter_end[0]: mi = dereference(iter_set[0]) o = self.get_from_index(mi, order = masklevel) if o != NULL: _mask_children(oct_mask, o) coct += 1 cmi += 1 preincrement(iter_set[0]) # Get domain ind cdef np.ndarray[np.int64_t, ndim=1] ind ind = np.zeros(self.nocts, 'int64') - 1 cdef oct_visitors.MaskedIndexOcts visitor visitor = oct_visitors.MaskedIndexOcts(self, domain_id) visitor.oct_index = ind visitor.oct_mask = oct_mask self.visit_all_octs(selector, visitor) return ind cdef void _mask_children(np.ndarray[np.uint8_t] mask, Oct *cur): cdef int i, j, k if cur == NULL: return mask[cur.domain_ind] = 1 if cur.children == NULL: return for i in range(2): for j in range(2): for k in range(2): _mask_children(mask, cur.children[cind(i,j,k)]) cdef np.uint64_t ONEBIT=1 cdef np.uint64_t FLAG = ~(0) cdef class ParticleBitmap: cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef np.uint8_t periodicity[3] cdef np.float64_t dds[3] cdef np.float64_t dds_mi1[3] cdef np.float64_t dds_mi2[3] cdef np.float64_t idds[3] cdef np.int32_t dims[3] cdef np.int64_t file_hash cdef np.uint64_t directional_max2[3] cdef np.int64_t hash_value cdef public np.uint64_t nfiles cdef public np.int32_t index_order1 cdef public np.int32_t index_order2 cdef public object masks cdef public object particle_counts cdef public object counts cdef public object max_count cdef public object _last_selector cdef public object _last_return_values cdef public object _cached_octrees cdef public object _last_octree_subset cdef public object _last_oct_handler cdef public object _prev_octree_subset cdef public object _prev_oct_handler cdef np.uint32_t *file_markers cdef np.uint64_t n_file_markers cdef np.uint64_t file_marker_i cdef public FileBitmasks bitmasks cdef public BoolArrayCollection collisions cdef public int _used_mi2 def __init__(self, left_edge, right_edge, periodicity, file_hash, nfiles, index_order1, index_order2): # TODO: Set limit on maximum orders? cdef int i self._cached_octrees = {} self._last_selector = None self._last_return_values = None self._last_octree_subset = None self._last_oct_handler = None self._prev_octree_subset = None self._prev_oct_handler = None self.file_hash = file_hash self.nfiles = nfiles for i in range(3): self.left_edge[i] = left_edge[i] self.right_edge[i] = right_edge[i] self.periodicity[i] = periodicity[i] self.dims[i] = (1< 0: return self.index_order2 cdef np.uint64_t index_order2 = 2 for i in range(3): # Note we're casting to signed here, to avoid negative issues. if self.dds_mi1[i] < characteristic_size: continue index_order2 = max(index_order2, ceil(log2(self.dds_mi1[i] / characteristic_size))) index_order2 = i64min(max_index_order2, index_order2) self._update_mi2(index_order2) return self.index_order2 cdef void _update_mi2(self, np.uint64_t index_order2): self.index_order2 = index_order2 mi2_max = (1 << self.index_order2) - 1 self.directional_max2[0] = encode_morton_64bit(mi2_max, 0, 0) self.directional_max2[1] = encode_morton_64bit(0, mi2_max, 0) self.directional_max2[2] = encode_morton_64bit(0, 0, mi2_max) for i in range(3): self.dds_mi2[i] = self.dds_mi1[i] / (1< RE[i]: axiter[i][1] = -1 axiterv[i][1] = -DW[i] for xi in range(2): if axiter[0][xi] == 999: continue s_ppos[0] = ppos[0] + axiterv[0][xi] for yi in range(2): if axiter[1][yi] == 999: continue s_ppos[1] = ppos[1] + axiterv[1][yi] for zi in range(2): if axiter[2][zi] == 999: continue s_ppos[2] = ppos[2] + axiterv[2][zi] # OK, now we compute the left and right edges for this shift. for i in range(3): clip_pos_l[i] = fmax(s_ppos[i] - radius, LE[i] + dds[i]/10) clip_pos_r[i] = fmin(s_ppos[i] + radius, RE[i] - dds[i]/10) bounded_morton_split_dds(clip_pos_l[0], clip_pos_l[1], clip_pos_l[2], LE, dds, bounds[0]) bounded_morton_split_dds(clip_pos_r[0], clip_pos_r[1], clip_pos_r[2], LE, dds, bounds[1]) # We go to the upper bound plus one so that we have *inclusive* loops -- the upper bound # is the cell *index*, so we want to make sure we include that cell. This is also why # we don't need to worry about mi_max being the max index rather than the cell count. for xex in range(bounds[0][0], bounds[1][0] + 1): for yex in range(bounds[0][1], bounds[1][1] + 1): for zex in range(bounds[0][2], bounds[1][2] + 1): miex = encode_morton_64bit(xex, yex, zex) mask[miex] = 1 particle_counts[miex] += 1 if miex >= msize: raise IndexError( "Index for a softening region " f"({miex}) exceeds " f"max ({msize})") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def _set_coarse_index_data_file(self, np.uint64_t file_id): return self.__set_coarse_index_data_file(file_id) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void __set_coarse_index_data_file(self, np.uint64_t file_id): cdef np.int64_t i cdef FileBitmasks bitmasks = self.bitmasks cdef np.ndarray[np.uint8_t, ndim=1] mask = self.masks[:,file_id] # Add in order for i in range(mask.shape[0]): if mask[i] == 1: bitmasks._set_coarse(file_id, i) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def _refined_index_data_file(self, BoolArrayCollection in_collection, np.ndarray[cython.floating, ndim=2] pos, np.ndarray[cython.floating, ndim=1] hsml, np.ndarray[np.uint8_t, ndim=1] mask, np.ndarray[np.uint64_t, ndim=1] sub_mi1, np.ndarray[np.uint64_t, ndim=1] sub_mi2, np.uint64_t file_id, np.int64_t nsub_mi, np.uint64_t count_threshold = 128, np.uint8_t mask_threshold = 2): self._used_mi2 = 1 if in_collection is None: in_collection = BoolArrayCollection() cdef BoolArrayCollection _in_coll = in_collection out_collection = self.__refined_index_data_file(_in_coll, pos, hsml, mask, count_threshold, mask_threshold) return 0, out_collection @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef BoolArrayCollection __refined_index_data_file( self, BoolArrayCollection in_collection, np.ndarray[cython.floating, ndim=2] pos, np.ndarray[cython.floating, ndim=1] hsml, np.ndarray[np.uint8_t, ndim=1] mask, np.uint64_t count_threshold, np.uint8_t mask_threshold ): # Initialize cdef np.int64_t p, sorted_ind cdef np.uint64_t i cdef np.uint64_t mi1, mi2 cdef np.float64_t ppos[3] cdef np.float64_t s_ppos[3] # shifted ppos cdef int skip cdef BoolArrayCollection this_collection, out_collection cdef np.uint64_t bounds[2][3] cdef np.uint8_t fully_enclosed cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef np.float64_t DW[3] cdef np.uint8_t PER[3] cdef np.float64_t dds1[3] cdef np.float64_t dds2[3] cdef np.float64_t radius cdef np.uint64_t mi_split1[3] cdef np.uint64_t mi_split2[3] cdef np.uint64_t miex1 cdef np.uint64_t[:] particle_counts = self.particle_counts cdef np.uint64_t xex, yex, zex cdef np.float64_t clip_pos_l[3] cdef np.float64_t clip_pos_r[3] cdef int axiter[3][2] cdef np.float64_t axiterv[3][2] cdef CoarseRefinedSets coarse_refined_map cdef np.uint64_t nfully_enclosed = 0, n_calls = 0 cdef np.uint64_t max_mi1_elements = 1 << (3*self.index_order1) cdef np.uint64_t max_mi2_elements = 1 << (3*self.index_order2) cdef np.ndarray[np.uint64_t, ndim=1] refined_count = np.zeros(max_mi1_elements, dtype="uint64") # Copy things from structure (type cast) for i in range(3): LE[i] = self.left_edge[i] RE[i] = self.right_edge[i] PER[i] = self.periodicity[i] dds1[i] = self.dds_mi1[i] dds2[i] = self.dds_mi2[i] DW[i] = RE[i] - LE[i] axiter[i][0] = 0 # We always do an offset of 0 axiterv[i][0] = 0.0 cdef np.ndarray[np.uint64_t, ndim=1] morton_indices = np.empty(pos.shape[0], dtype="u8") for p in range(pos.shape[0]): morton_indices[p] = bounded_morton(pos[p, 0], pos[p, 1], pos[p, 2], LE, RE, self.index_order1) # Loop over positions skipping those outside the domain cdef np.ndarray[np.uint64_t, ndim=1, cast=True] sorted_order if hsml is None: # casting to uint64 for compatibility with 32 bits systems # see https://github.com/yt-project/yt/issues/3656 sorted_order = np.argsort(morton_indices).astype(np.uint64, copy=False) else: sorted_order = np.argsort(hsml)[::-1].astype(np.uint64, copy=False) for sorted_ind in range(sorted_order.shape[0]): p = sorted_order[sorted_ind] skip = 0 for i in range(3): axiter[i][1] = 999 if not (LE[i] <= pos[p, i] < RE[i]): skip = 1 break ppos[i] = pos[p,i] if skip == 1: continue # Only look if collision at coarse index mi1 = bounded_morton_split_dds(ppos[0], ppos[1], ppos[2], LE, dds1, mi_split1) if hsml is None: if mask[mi1] < mask_threshold \ or particle_counts[mi1] < count_threshold: continue # Determine sub index within cell of primary index mi2 = bounded_morton_split_relative_dds( ppos[0], ppos[1], ppos[2], LE, dds1, dds2, mi_split2) if refined_count[mi1] == 0: coarse_refined_map[mi1].padWithZeroes(max_mi2_elements) if not coarse_refined_map[mi1].get(mi2): coarse_refined_map[mi1].set(mi2) refined_count[mi1] += 1 else: # only hit if we have smoothing lengths. # We have to do essentially the identical process to in the coarse indexing, # except here we need to fill in all the subranges as well as the coarse ranges # Note that we are also doing the null case, where we do no shifting radius = hsml[p] #if mask[mi1] <= 4: # only one thing in this area # continue for i in range(3): if PER[i] and ppos[i] - radius < LE[i]: axiter[i][1] = +1 axiterv[i][1] = DW[i] elif PER[i] and ppos[i] + radius > RE[i]: axiter[i][1] = -1 axiterv[i][1] = -DW[i] for xi in range(2): if axiter[0][xi] == 999: continue s_ppos[0] = ppos[0] + axiterv[0][xi] for yi in range(2): if axiter[1][yi] == 999: continue s_ppos[1] = ppos[1] + axiterv[1][yi] for zi in range(2): if axiter[2][zi] == 999: continue s_ppos[2] = ppos[2] + axiterv[2][zi] # OK, now we compute the left and right edges for this shift. for i in range(3): # casting to int64 is not nice but is so we can have negative values we clip clip_pos_l[i] = fmax(s_ppos[i] - radius, LE[i] + dds1[i]/10) clip_pos_r[i] = fmin(s_ppos[i] + radius, RE[i] - dds1[i]/10) bounded_morton_split_dds(clip_pos_l[0], clip_pos_l[1], clip_pos_l[2], LE, dds1, bounds[0]) bounded_morton_split_dds(clip_pos_r[0], clip_pos_r[1], clip_pos_r[2], LE, dds1, bounds[1]) # We go to the upper bound plus one so that we have *inclusive* loops -- the upper bound # is the cell *index*, so we want to make sure we include that cell. This is also why # we don't need to worry about mi_max being the max index rather than the cell count. # One additional thing to note is that for all of # the *internal* cells, i.e., those that are both # greater than the left edge and less than the # right edge, we are fully enclosed. for xex in range(bounds[0][0], bounds[1][0] + 1): for yex in range(bounds[0][1], bounds[1][1] + 1): for zex in range(bounds[0][2], bounds[1][2] + 1): miex1 = encode_morton_64bit(xex, yex, zex) if mask[miex1] < mask_threshold or \ particle_counts[miex1] < count_threshold: continue # this explicitly requires that it be *between* # them, not overlapping if xex > bounds[0][0] and xex < bounds[1][0] and \ yex > bounds[0][1] and yex < bounds[1][1] and \ zex > bounds[0][2] and zex < bounds[1][2]: fully_enclosed = 1 else: fully_enclosed = 0 # Now we need to fill our sub-range if refined_count[miex1] == 0: coarse_refined_map[miex1].padWithZeroes(max_mi2_elements) elif refined_count[miex1] >= max_mi2_elements: continue if fully_enclosed == 1: nfully_enclosed += 1 coarse_refined_map[miex1].inplace_logicalxor( coarse_refined_map[miex1]) coarse_refined_map[miex1].inplace_logicalnot() refined_count[miex1] = max_mi2_elements continue n_calls += 1 refined_count[miex1] += self.__fill_refined_ranges(s_ppos, radius, LE, RE, dds1, xex, yex, zex, dds2, coarse_refined_map[miex1]) cdef np.uint64_t vec_i cdef bool_array *buf = NULL cdef ewah_word_type w this_collection = BoolArrayCollection() cdef ewah_bool_array *refined_arr = NULL for it1 in coarse_refined_map: mi1 = it1.first refined_arr = &this_collection.ewah_coll[0][mi1] this_collection.ewah_keys[0].set(mi1) this_collection.ewah_refn[0].set(mi1) buf = &it1.second for vec_i in range(buf.sizeInBytes() / sizeof(ewah_word_type)): w = buf.getWord(vec_i) refined_arr.addWord(w) out_collection = BoolArrayCollection() in_collection._logicalor(this_collection, out_collection) return out_collection @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef np.int64_t __fill_refined_ranges(self, np.float64_t s_ppos[3], np.float64_t radius, np.float64_t LE[3], np.float64_t RE[3], np.float64_t dds1[3], np.uint64_t xex, np.uint64_t yex, np.uint64_t zex, np.float64_t dds2[3], bool_array &refined_set) except -1: cdef int i cdef np.uint64_t bounds_l[3] cdef np.uint64_t bounds_r[3] cdef np.uint64_t miex2, miex2_min, miex2_max cdef np.float64_t clip_pos_l[3] cdef np.float64_t clip_pos_r[3] cdef np.float64_t cell_edge_l, cell_edge_r cdef np.uint64_t ex1[3] cdef np.uint64_t xiex_min, yiex_min, ziex_min cdef np.uint64_t xiex_max, yiex_max, ziex_max cdef np.uint64_t old_nsub = refined_set.numberOfOnes() ex1[0] = xex ex1[1] = yex ex1[2] = zex # Check a few special cases for i in range(3): # Figure out our bounds inside our coarse cell, in the space of the # full domain cell_edge_l = ex1[i] * dds1[i] + LE[i] cell_edge_r = cell_edge_l + dds1[i] if s_ppos[i] + radius < cell_edge_l or s_ppos[i] - radius > cell_edge_r: return 0 clip_pos_l[i] = fmax(s_ppos[i] - radius, cell_edge_l + dds2[i]/2.0) clip_pos_r[i] = fmin(s_ppos[i] + radius, cell_edge_r - dds2[i]/2.0) miex2_min = bounded_morton_split_relative_dds(clip_pos_l[0], clip_pos_l[1], clip_pos_l[2], LE, dds1, dds2, bounds_l) miex2_max = bounded_morton_split_relative_dds(clip_pos_r[0], clip_pos_r[1], clip_pos_r[2], LE, dds1, dds2, bounds_r) xex_max = self.directional_max2[0] yex_max = self.directional_max2[1] zex_max = self.directional_max2[2] xiex_min = miex2_min & xex_max yiex_min = miex2_min & yex_max ziex_min = miex2_min & zex_max xiex_max = miex2_max & xex_max yiex_max = miex2_max & yex_max ziex_max = miex2_max & zex_max # This could *probably* be sped up by iterating over words. for miex2 in range(miex2_min, miex2_max + 1): #miex2 = encode_morton_64bit(xex2, yex2, zex2) #decode_morton_64bit(miex2, ex2) # Let's check all our cases here if (miex2 & xex_max) < (xiex_min): continue if (miex2 & xex_max) > (xiex_max): continue if (miex2 & yex_max) < (yiex_min): continue if (miex2 & yex_max) > (yiex_max): continue if (miex2 & zex_max) < (ziex_min): continue if (miex2 & zex_max) > (ziex_max): continue refined_set.set(miex2) return refined_set.numberOfOnes() - old_nsub @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def _set_refined_index_data_file(self, np.ndarray[np.uint64_t, ndim=1] sub_mi1, np.ndarray[np.uint64_t, ndim=1] sub_mi2, np.uint64_t file_id, np.int64_t nsub_mi): return self.__set_refined_index_data_file(sub_mi1, sub_mi2, file_id, nsub_mi) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void __set_refined_index_data_file(self, np.ndarray[np.uint64_t, ndim=1] sub_mi1, np.ndarray[np.uint64_t, ndim=1] sub_mi2, np.uint64_t file_id, np.int64_t nsub_mi): cdef FileBitmasks bitmasks = self.bitmasks bitmasks._set_refined_index_array(file_id, nsub_mi, sub_mi1, sub_mi2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def find_collisions(self, verbose=False): cdef tuple cc, rc cc, rc = self.bitmasks._find_collisions(self.collisions,verbose) return cc, rc @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def find_collisions_coarse(self, verbose=False, file_list = None): cdef int nc, nm nc, nm = self.bitmasks._find_collisions_coarse(self.collisions, verbose, file_list) return nc, nm @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def find_uncontaminated(self, np.uint32_t ifile, BoolArrayCollection mask, BoolArrayCollection mask2 = None): cdef np.ndarray[np.uint8_t, ndim=1] arr = np.zeros((1 << (self.index_order1 * 3)),'uint8') cdef np.uint8_t[:] arr_view = arr self.bitmasks._select_uncontaminated(ifile, mask, arr_view, mask2) return arr @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def find_contaminated(self, np.uint32_t ifile, BoolArrayCollection mask, BoolArrayCollection mask2 = None): cdef np.ndarray[np.uint8_t, ndim=1] arr = np.zeros((1 << (self.index_order1 * 3)),'uint8') cdef np.uint8_t[:] arr_view = arr cdef np.ndarray[np.uint8_t, ndim=1] sfiles = np.zeros(self.nfiles,'uint8') cdef np.uint8_t[:] sfiles_view = sfiles self.bitmasks._select_contaminated(ifile, mask, arr_view, sfiles_view, mask2) return arr, np.where(sfiles)[0].astype('uint32') @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) def find_collisions_refined(self, verbose=False): cdef np.int32_t nc, nm nc, nm = self.bitmasks._find_collisions_refined(self.collisions,verbose) return nc, nm def get_bitmasks(self): return self.bitmasks def iseq_bitmask(self, solf): return self.bitmasks._iseq(solf.get_bitmasks()) def save_bitmasks(self, fname, max_hsml): import h5py cdef bytes serial_BAC cdef np.uint64_t ifile with h5py.File(fname, mode="a") as fp: try: grp = fp[str(self.hash_value)] grp.clear() except KeyError: grp = fp.create_group(str(self.hash_value)) grp.attrs["bitmask_version"] = _bitmask_version grp.attrs["nfiles"] = self.nfiles grp.attrs["max_hsml"] = max_hsml # Add some attrs for convenience. They're not read back. grp.attrs["file_hash"] = self.file_hash grp.attrs["left_edge"] = self.left_edge grp.attrs["right_edge"] = self.right_edge grp.attrs["periodicity"] = self.periodicity grp.attrs["index_order1"] = self.index_order1 grp.attrs["index_order2"] = self.index_order2 for ifile in range(self.nfiles): serial_BAC = self.bitmasks._dumps(ifile) grp.create_dataset(f"nfile_{ifile:05}", data=np.void(serial_BAC)) serial_BAC = self.collisions._dumps() grp.create_dataset("collisions", data=np.void(serial_BAC)) def check_bitmasks(self): return self.bitmasks._check() def reset_bitmasks(self): self.bitmasks._reset() def load_bitmasks(self, fname): import h5py cdef bint read_flag = 1 cdef bint irflag cdef np.uint64_t ver cdef bint overwrite = 0 # Verify that file is correct version if not os.path.isfile(fname): raise OSError with h5py.File(fname, mode="r") as fp: try: grp = fp[str(self.hash_value)] except KeyError: raise OSError(f"Index not found in the {fname}") ver = grp.attrs["bitmask_version"] try: max_hsml = grp.attrs["max_hsml"] except KeyError: raise OSError(f"'max_hsml' not found in the {fname}") if ver == self.nfiles and ver != _bitmask_version: overwrite = 1 ver = 0 # Original bitmaps had number of files first if ver != _bitmask_version: raise OSError("The file format of the index has changed since " "this file was created. It will be replaced with an " "updated version.") # Read bitmap for each file pb = get_pbar("Loading particle index", self.nfiles) for ifile in range(self.nfiles): pb.update(ifile+1) irflag = self.bitmasks._loads(ifile, grp[f"nfile_{ifile:05}"][...].tobytes()) if irflag == 0: read_flag = 0 pb.finish() # Collisions irflag = self.collisions._loads(grp["collisions"][...].tobytes()) if irflag == 0: read_flag = 0 # Save in correct format if overwrite == 1: self.save_bitmasks(fname, max_hsml) return read_flag, max_hsml def print_info(self): cdef np.uint64_t ifile for ifile in range(self.nfiles): self.bitmasks.print_info(ifile, "File: %03d" % ifile) def count_coarse(self, ifile): r"""Get the number of coarse cells set for a file.""" return self.bitmasks.count_coarse(ifile) def count_refined(self, ifile): r"""Get the number of cells refined for a file.""" return self.bitmasks.count_refined(ifile) def count_total(self, ifile): r"""Get the total number of cells set for a file.""" return self.bitmasks.count_total(ifile) def check(self): cdef np.uint64_t mi1 cdef ewah_bool_array arr_totref, arr_tottwo cdef ewah_bool_array arr, arr_any, arr_two, arr_swap cdef vector[size_t] vec_totref cdef vector[size_t].iterator it_mi1 cdef int nm = 0, nc = 0 cdef np.uint64_t ifile, nbitmasks nbitmasks = len(self.bitmasks) # Locate all indices with second level refinement for ifile in range(self.nfiles): arr = ( self.bitmasks.ewah_refn)[ifile][0] arr_totref.logicalor(arr,arr_totref) # Count collections & second level indices vec_totref = arr_totref.toArray() it_mi1 = vec_totref.begin() while it_mi1 != vec_totref.end(): mi1 = dereference(it_mi1) arr_any.reset() arr_two.reset() for ifile in range(nbitmasks): if self.bitmasks._isref(ifile, mi1) == 1: arr = ( self.bitmasks.ewah_coll)[ifile][0][mi1] arr_any.logicaland(arr, arr_two) # Indices in previous files arr_any.logicalor(arr, arr_swap) # All second level indices arr_any = arr_swap arr_two.logicalor(arr_tottwo,arr_tottwo) nc += arr_tottwo.numberOfOnes() nm += arr_any.numberOfOnes() preincrement(it_mi1) # nc: total number of second level morton indices that are repeated # nm: total number of second level morton indices print("Total of %s / %s collisions (% 3.5f%%)" % (nc, nm, 100.0*float(nc)/nm)) def primary_indices(self): mi = ( self.collisions.ewah_keys)[0].toArray() return np.array(mi,'uint64') def file_ownership_mask(self, fid): cdef BoolArrayCollection out out = self.bitmasks._get_bitmask( fid) return out def finalize(self): return # self.index_octree = ParticleOctreeContainer([1,1,1], # [self.left_edge[0], self.left_edge[1], self.left_edge[2]], # [self.right_edge[0], self.right_edge[1], self.right_edge[2]], # num_zones = 1 # ) # self.index_octree.n_ref = 1 # mi = ( self.collisions.ewah_keys)[0].toArray() # Change from vector to numpy # mi = mi.astype("uint64") # self.index_octree.add(mi, self.index_order1) # self.index_octree.finalize() def get_DLE(self): cdef int i cdef np.ndarray[np.float64_t, ndim=1] DLE DLE = np.zeros(3, dtype='float64') for i in range(3): DLE[i] = self.left_edge[i] return DLE def get_DRE(self): cdef int i cdef np.ndarray[np.float64_t, ndim=1] DRE DRE = np.zeros(3, dtype='float64') for i in range(3): DRE[i] = self.right_edge[i] return DRE @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_ghost_zones(self, SelectorObject selector, int ngz, BoolArrayCollection dmask = None, bint coarse_ghosts = False): cdef BoolArrayCollection gmask, gmask2, out cdef np.ndarray[np.uint8_t, ndim=1] periodic = selector.get_periodicity() cdef bint periodicity[3] cdef int i for i in range(3): periodicity[i] = periodic[i] if dmask is None: dmask = BoolArrayCollection() gmask2 = BoolArrayCollection() morton_selector = ParticleBitmapSelector(selector,self,ngz=0) morton_selector.fill_masks(dmask, gmask2) gmask = BoolArrayCollection() dmask._get_ghost_zones(ngz, self.index_order1, self.index_order2, periodicity, gmask, coarse_ghosts) _dfiles, gfiles = self.masks_to_files(dmask, gmask) out = BoolArrayCollection() gmask._logicalor(dmask, out) return gfiles, out @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def selector2mask(self, SelectorObject selector): cdef BoolArrayCollection cmask = BoolArrayCollection() cdef ParticleBitmapSelector morton_selector morton_selector = ParticleBitmapSelector(selector,self,ngz=0) morton_selector.fill_masks(cmask) return cmask @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def mask2files(self, BoolArrayCollection cmask): cdef np.ndarray[np.uint32_t, ndim=1] file_idx file_idx = self.mask_to_files(cmask) return file_idx @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def mask2filemasks(self, BoolArrayCollection cmask, np.ndarray[np.uint32_t, ndim=1] file_idx): cdef BoolArrayCollection fmask cdef np.int32_t fid cdef np.ndarray[object, ndim=1] file_masks cdef int i # Get bitmasks for parts of files touching the selector file_masks = np.array([BoolArrayCollection() for i in range(len(file_idx))], dtype="object") for i, (fid, fmask) in enumerate(zip(file_idx,file_masks)): self.bitmasks._logicaland( fid, cmask, fmask) return file_masks @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def filemasks2addfiles(self, np.ndarray[object, ndim=1] file_masks): cdef list addfile_idx addfile_idx = len(file_masks)*[None] for i, fmask in enumerate(file_masks): addfile_idx[i] = self.mask_to_files(fmask).astype('uint32') return addfile_idx @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def identify_file_masks(self, SelectorObject selector): cdef BoolArrayCollection cmask = BoolArrayCollection() cdef BoolArrayCollection fmask cdef np.int32_t fid cdef np.ndarray[object, ndim=1] file_masks cdef np.ndarray[np.uint32_t, ndim=1] file_idx cdef list addfile_idx # Get bitmask for selector cdef ParticleBitmapSelector morton_selector morton_selector = ParticleBitmapSelector(selector, self, ngz=0) morton_selector.fill_masks(cmask) # Get bitmasks for parts of files touching the selector file_idx = self.mask_to_files(cmask) file_masks = np.array([BoolArrayCollection() for i in range(len(file_idx))], dtype="object") addfile_idx = len(file_idx)*[None] for i, (fid, fmask) in enumerate(zip(file_idx,file_masks)): self.bitmasks._logicaland( fid, cmask, fmask) addfile_idx[i] = self.mask_to_files(fmask).astype('uint32') return file_idx.astype('uint32'), file_masks, addfile_idx @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def identify_data_files(self, SelectorObject selector, int ngz = 0): cdef BoolArrayCollection cmask_s = BoolArrayCollection() cdef BoolArrayCollection cmask_g = BoolArrayCollection() # Find mask of selected morton indices cdef ParticleBitmapSelector morton_selector morton_selector = ParticleBitmapSelector(selector, self, ngz=ngz) morton_selector.fill_masks(cmask_s, cmask_g) return self.masks_to_files(cmask_s, cmask_g), (cmask_s, cmask_g) def mask_to_files(self, BoolArrayCollection mm_s): cdef FileBitmasks mm_d = self.bitmasks cdef np.uint32_t ifile cdef np.ndarray[np.uint8_t, ndim=1] file_mask_p file_mask_p = np.zeros(self.nfiles, dtype="uint8") # Compare with mask of particles for ifile in range(self.nfiles): # Only continue if the file is not already selected if file_mask_p[ifile] == 0: if mm_d._intersects(ifile, mm_s): file_mask_p[ifile] = 1 cdef np.ndarray[np.int32_t, ndim=1] file_idx_p file_idx_p = np.where(file_mask_p)[0].astype('int32') return file_idx_p.astype('uint32') def masks_to_files(self, BoolArrayCollection mm_s, BoolArrayCollection mm_g): cdef FileBitmasks mm_d = self.bitmasks cdef np.uint32_t ifile cdef np.ndarray[np.uint8_t, ndim=1] file_mask_p cdef np.ndarray[np.uint8_t, ndim=1] file_mask_g file_mask_p = np.zeros(self.nfiles, dtype="uint8") file_mask_g = np.zeros(self.nfiles, dtype="uint8") # Compare with mask of particles for ifile in range(self.nfiles): # Only continue if the file is not already selected if file_mask_p[ifile] == 0: if mm_d._intersects(ifile, mm_s): file_mask_p[ifile] = 1 file_mask_g[ifile] = 0 # No intersection elif mm_d._intersects(ifile, mm_g): file_mask_g[ifile] = 1 cdef np.ndarray[np.int32_t, ndim=1] file_idx_p cdef np.ndarray[np.int32_t, ndim=1] file_idx_g file_idx_p = np.where(file_mask_p)[0].astype('int32') file_idx_g = np.where(file_mask_g)[0].astype('int32') return file_idx_p.astype('uint32'), file_idx_g.astype('uint32') @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def construct_octree(self, index, io_handler, data_files, num_zones, BoolArrayCollection selector_mask, BoolArrayCollection base_mask = None): cdef np.uint64_t total_pcount cdef np.uint64_t i, j, k cdef int ind[3] cdef np.uint64_t ind64[3] cdef ParticleBitmapOctreeContainer octree cdef np.uint64_t mi, mi_root cdef np.ndarray pos cdef np.ndarray[np.float32_t, ndim=2] pos32 cdef np.ndarray[np.float64_t, ndim=2] pos64 cdef np.float64_t ppos[3] cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] cdef int bitsize = 0 for i in range(3): DLE[i] = self.left_edge[i] DRE[i] = self.right_edge[i] cdef np.ndarray[np.uint64_t, ndim=1] morton_ind # Determine cells that need to be added to the octree cdef np.uint64_t nroot = selector_mask._count_total() # Now we can actually create a sparse octree. octree = ParticleBitmapOctreeContainer( (self.dims[0], self.dims[1], self.dims[2]), (self.left_edge[0], self.left_edge[1], self.left_edge[2]), (self.right_edge[0], self.right_edge[1], self.right_edge[2]), nroot, num_zones) octree.n_ref = index.dataset.n_ref octree.level_offset = self.index_order1 octree.allocate_domains() # Add roots based on the mask cdef np.uint64_t croot = 0 cdef ewah_bool_array *ewah_slct = selector_mask.ewah_keys cdef ewah_bool_array *ewah_base if base_mask is not None: ewah_base = base_mask.ewah_keys else: ewah_base = NULL cdef ewah_bool_iterator *iter_set = new ewah_bool_iterator(ewah_slct[0].begin()) cdef ewah_bool_iterator *iter_end = new ewah_bool_iterator(ewah_slct[0].end()) cdef np.ndarray[np.uint8_t, ndim=1] slct_arr slct_arr = np.zeros((1 << (self.index_order1 * 3)),'uint8') while iter_set[0] != iter_end[0]: mi = dereference(iter_set[0]) if ewah_base != NULL and ewah_base[0].get(mi) == 0: octree._index_base_roots[croot] = 0 slct_arr[mi] = 2 else: slct_arr[mi] = 1 decode_morton_64bit(mi, ind64) for j in range(3): ind[j] = ind64[j] octree.next_root(1, ind) croot += 1 preincrement(iter_set[0]) assert(croot == nroot) if ewah_base != NULL: assert(np.sum(octree._index_base_roots) == ewah_base[0].numberOfOnes()) # Get morton indices for all particles in this file and those # contaminating cells it has majority control of. files_touched = data_files #+ buffer_files # datafile object from ID goes here total_pcount = 0 for data_file in files_touched: total_pcount += sum(data_file.total_particles.values()) morton_ind = np.empty(total_pcount, dtype='uint64') total_pcount = 0 cdef np.uint64_t base_pcount = 0 for data_file in files_touched: # We now get our particle positions for pos in io_handler._yield_coordinates(data_file): pos32 = pos64 = None bitsize = 0 if pos.dtype == np.float32: pos32 = pos bitsize = 32 for j in range(pos.shape[0]): for k in range(3): ppos[k] = pos32[j,k] mi = bounded_morton(ppos[0], ppos[1], ppos[2], DLE, DRE, ORDER_MAX) mi_root = mi >> (3*(ORDER_MAX-self.index_order1)) if slct_arr[mi_root] > 0: morton_ind[total_pcount] = mi total_pcount += 1 if slct_arr[mi_root] == 1: base_pcount += 1 elif pos.dtype == np.float64: pos64 = pos bitsize = 64 for j in range(pos.shape[0]): for k in range(3): ppos[k] = pos64[j,k] mi = bounded_morton(ppos[0], ppos[1], ppos[2], DLE, DRE, ORDER_MAX) mi_root = mi >> (3*(ORDER_MAX-self.index_order1)) if slct_arr[mi_root] > 0: morton_ind[total_pcount] = mi total_pcount += 1 if slct_arr[mi_root] == 1: base_pcount += 1 else: raise RuntimeError morton_ind = morton_ind[:total_pcount] morton_ind.sort() octree.add(morton_ind, self.index_order1) octree.finalize() return octree cdef class ParticleBitmapSelector: cdef SelectorObject selector cdef ParticleBitmap bitmap cdef np.uint32_t ngz cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] cdef bint periodicity[3] cdef np.uint32_t order1 cdef np.uint32_t order2 cdef np.uint64_t max_index1 cdef np.uint64_t max_index2 cdef np.uint64_t s1 cdef np.uint64_t s2 cdef void* pointers[11] cdef np.uint64_t[:,:] ind1_n cdef np.uint64_t[:,:] ind2_n cdef np.uint32_t[:,:] neighbors cdef np.uint64_t[:] neighbor_list1 cdef np.uint64_t[:] neighbor_list2 cdef np.uint32_t nfiles cdef np.uint8_t[:] file_mask_p cdef np.uint8_t[:] file_mask_g # Uncompressed boolean cdef np.uint8_t[:] refined_select_bool cdef np.uint8_t[:] refined_ghosts_bool cdef np.uint8_t[:] coarse_select_bool cdef np.uint8_t[:] coarse_ghosts_bool cdef SparseUnorderedRefinedBitmask refined_ghosts_list cdef BoolArrayColl select_ewah cdef BoolArrayColl ghosts_ewah def __cinit__(self, selector, bitmap, ngz=0): cdef int i cdef np.ndarray[np.uint8_t, ndim=1] periodicity = np.zeros(3, dtype='uint8') cdef np.ndarray[np.float64_t, ndim=1] DLE = np.zeros(3, dtype='float64') cdef np.ndarray[np.float64_t, ndim=1] DRE = np.zeros(3, dtype='float64') self.selector = selector self.bitmap = bitmap self.ngz = ngz # Things from the bitmap & selector periodicity = selector.get_periodicity() DLE = bitmap.get_DLE() DRE = bitmap.get_DRE() for i in range(3): self.DLE[i] = DLE[i] self.DRE[i] = DRE[i] self.periodicity[i] = periodicity[i] self.order1 = bitmap.index_order1 self.order2 = bitmap.index_order2 self.nfiles = bitmap.nfiles self.max_index1 = (1 << self.order1) self.max_index2 = (1 << self.order2) self.s1 = (1 << (self.order1*3)) self.s2 = (1 << (self.order2*3)) self.neighbors = np.zeros((2*ngz+1, 3), dtype='uint32') self.ind1_n = np.zeros((2*ngz+1, 3), dtype='uint64') self.ind2_n = np.zeros((2*ngz+1, 3), dtype='uint64') self.neighbor_list1 = np.zeros((2*ngz+1)**3, dtype='uint64') self.neighbor_list2 = np.zeros((2*ngz+1)**3, dtype='uint64') self.file_mask_p = np.zeros(bitmap.nfiles, dtype='uint8') self.file_mask_g = np.zeros(bitmap.nfiles, dtype='uint8') self.refined_select_bool = np.zeros(self.s2, 'uint8') self.refined_ghosts_bool = np.zeros(self.s2, 'uint8') self.coarse_select_bool = np.zeros(self.s1, 'uint8') self.coarse_ghosts_bool = np.zeros(self.s1, 'uint8') self.refined_ghosts_list = SparseUnorderedRefinedBitmask() self.select_ewah = BoolArrayColl(self.s1, self.s2) self.ghosts_ewah = BoolArrayColl(self.s1, self.s2) def fill_masks(self, BoolArrayCollection mm_s, BoolArrayCollection mm_g = None): # Normal variables cdef int i cdef np.int32_t level = 0 cdef np.uint64_t mi1 mi1 = ~(0) cdef np.float64_t pos[3] cdef np.float64_t dds[3] cdef np.uint64_t cur_ind[3] for i in range(3): cur_ind[i] = 0 pos[i] = self.DLE[i] dds[i] = self.DRE[i] - self.DLE[i] if mm_g is None: mm_g = BoolArrayCollection() # Uncompressed version cdef BoolArrayColl mm_s0 cdef BoolArrayColl mm_g0 mm_s0 = BoolArrayColl(self.s1, self.s2) mm_g0 = BoolArrayColl(self.s1, self.s2) # Recurse cdef np.float64_t rpos[3] for i in range(3): rpos[i] = self.DRE[i] - self.bitmap.dds_mi2[i]/2.0 sbbox = self.selector.select_bbox_edge(pos, rpos) if sbbox == 1: for mi1 in range(self.s1): mm_s0._set_coarse(mi1) mm_s0._compress(mm_s) return else: self.recursive_morton_mask(level, pos, dds, mi1, cur_ind) # Set coarse morton indices in order self.set_coarse_bool(mm_s0, mm_g0) self.set_refined_list(mm_s0, mm_g0) self.set_refined_bool(mm_s0, mm_g0) # Compress mm_s0._compress(mm_s) mm_g0._compress(mm_g) def find_files(self, np.ndarray[np.uint8_t, ndim=1] file_mask_p, np.ndarray[np.uint8_t, ndim=1] file_mask_g): cdef np.uint64_t i cdef np.int32_t level = 0 cdef np.uint64_t mi1 mi1 = ~(0) cdef np.float64_t pos[3] cdef np.float64_t dds[3] for i in range(3): pos[i] = self.DLE[i] dds[i] = self.DRE[i] - self.DLE[i] # Fill with input for i in range(self.nfiles): self.file_mask_p[i] = file_mask_p[i] self.file_mask_g[i] = file_mask_g[i] # Recurse self.recursive_morton_files(level, pos, dds, mi1) # Fill with results for i in range(self.nfiles): file_mask_p[i] = self.file_mask_p[i] if file_mask_p[i]: file_mask_g[i] = 0 else: file_mask_g[i] = self.file_mask_g[i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef bint is_refined(self, np.uint64_t mi1): return self.bitmap.collisions._isref(mi1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef bint is_refined_files(self, np.uint64_t mi1): cdef np.uint64_t i if self.bitmap.collisions._isref(mi1): # Don't refine if files all selected already for i in range(self.nfiles): if self.file_mask_p[i] == 0: if self.bitmap.bitmasks._isref(i, mi1) == 1: return 1 return 0 else: return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void add_coarse(self, np.uint64_t mi1, int bbox = 2): self.coarse_select_bool[mi1] = 1 # Neighbors if (self.ngz > 0) and (bbox == 2): if not self.is_refined(mi1): self.add_neighbors_coarse(mi1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void set_files_coarse(self, np.uint64_t mi1): cdef np.uint64_t i cdef bint flag_ref = self.is_refined(mi1) # Flag files at coarse level if flag_ref == 0: for i in range(self.nfiles): if self.file_mask_p[i] == 0: if self.bitmap.bitmasks._get_coarse(i, mi1) == 1: self.file_mask_p[i] = 1 # Neighbors if (flag_ref == 0) and (self.ngz > 0): self.set_files_neighbors_coarse(mi1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int add_refined(self, np.uint64_t mi1, np.uint64_t mi2, int bbox = 2) except -1: self.refined_select_bool[mi2] = 1 # Neighbors if (self.ngz > 0) and (bbox == 2): self.add_neighbors_refined(mi1, mi2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void set_files_refined(self, np.uint64_t mi1, np.uint64_t mi2): cdef np.uint64_t i # Flag files for i in range(self.nfiles): if self.file_mask_p[i] == 0: if self.bitmap.bitmasks._get(i, mi1, mi2): self.file_mask_p[i] = 1 # Neighbors if (self.ngz > 0): self.set_files_neighbors_refined(mi1, mi2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void add_neighbors_coarse(self, np.uint64_t mi1): cdef np.uint64_t m cdef np.uint32_t ntot cdef np.uint64_t mi1_n ntot = morton_neighbors_coarse(mi1, self.max_index1, self.periodicity, self.ngz, self.neighbors, self.ind1_n, self.neighbor_list1) for m in range(ntot): mi1_n = self.neighbor_list1[m] self.coarse_ghosts_bool[mi1_n] = 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void set_files_neighbors_coarse(self, np.uint64_t mi1): cdef np.uint64_t i, m cdef np.uint32_t ntot cdef np.uint64_t mi1_n ntot = morton_neighbors_coarse(mi1, self.max_index1, self.periodicity, self.ngz, self.neighbors, self.ind1_n, self.neighbor_list1) for m in range(ntot): mi1_n = self.neighbor_list1[m] for i in range(self.nfiles): if self.file_mask_g[i] == 0: if self.bitmap.bitmasks._get_coarse(i, mi1_n): self.file_mask_g[i] = 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void add_neighbors_refined(self, np.uint64_t mi1, np.uint64_t mi2): cdef int m cdef np.uint32_t ntot cdef np.uint64_t mi1_n, mi2_n ntot = morton_neighbors_refined(mi1, mi2, self.max_index1, self.max_index2, self.periodicity, self.ngz, self.neighbors, self.ind1_n, self.ind2_n, self.neighbor_list1, self.neighbor_list2) for m in range(ntot): mi1_n = self.neighbor_list1[m] mi2_n = self.neighbor_list2[m] self.coarse_ghosts_bool[mi1_n] = 1 # Ghost cells are added at the refined level regardless of if the # coarse cell containing it is refined in the selector. if mi1_n == mi1: self.refined_ghosts_bool[mi2_n] = 1 else: self.refined_ghosts_list._set(mi1_n, mi2_n) # alternative implementation by Meagan Lang # see ed95b1ac2f7105092b1116f9c76568ae27024751 # Ghost cells are only added at the refined level if the coarse # index for the ghost cell is refined in the selector. #if mi1_n == mi1: # self.refined_ghosts_bool[mi2_n] = 1 #elif self.is_refined(mi1_n) == 1: # self.refined_ghosts_list._set(mi1_n, mi2_n) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void set_files_neighbors_refined(self, np.uint64_t mi1, np.uint64_t mi2): cdef int i, m cdef np.uint32_t ntot cdef np.uint64_t mi1_n, mi2_n ntot = morton_neighbors_refined(mi1, mi2, self.max_index1, self.max_index2, self.periodicity, self.ngz, self.neighbors, self.ind1_n, self.ind2_n, self.neighbor_list1, self.neighbor_list2) for m in range(ntot): mi1_n = self.neighbor_list1[m] mi2_n = self.neighbor_list2[m] if self.is_refined(mi1_n) == 1: for i in range(self.nfiles): if self.file_mask_g[i] == 0: if self.bitmap.bitmasks._get(i, mi1_n, mi2_n) == 1: self.file_mask_g[i] = 1 else: for i in range(self.nfiles): if self.file_mask_g[i] == 0: if self.bitmap.bitmasks._get_coarse(i, mi1_n) == 1: self.file_mask_g[i] = 1 break # If not refined, only one file should be selected @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void set_coarse_list(self, BoolArrayColl mm_s, BoolArrayColl mm_g): self.coarse_select_list._fill_bool(mm_s) self.coarse_ghosts_list._fill_bool(mm_g) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void set_refined_list(self, BoolArrayColl mm_s, BoolArrayColl mm_g): self.refined_ghosts_list._fill_bool(mm_g) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void set_coarse_bool(self, BoolArrayColl mm_s, BoolArrayColl mm_g): cdef np.uint64_t mi1 mm_s._set_coarse_array_ptr(&self.coarse_select_bool[0]) for mi1 in range(self.s1): self.coarse_select_bool[mi1] = 0 mm_g._set_coarse_array_ptr(&self.coarse_ghosts_bool[0]) for mi1 in range(self.s1): self.coarse_ghosts_bool[mi1] = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void set_refined_bool(self, BoolArrayColl mm_s, BoolArrayColl mm_g): mm_s._append(self.select_ewah) mm_g._append(self.ghosts_ewah) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef void push_refined_bool(self, np.uint64_t mi1): cdef np.uint64_t mi2 self.select_ewah._set_refined_array_ptr(mi1, &self.refined_select_bool[0]) for mi2 in range(self.s2): self.refined_select_bool[mi2] = 0 self.ghosts_ewah._set_refined_array_ptr(mi1, &self.refined_ghosts_bool[0]) for mi2 in range(self.s2): self.refined_ghosts_bool[mi2] = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void add_ghost_zones(self, BoolArrayColl mm_s, BoolArrayColl mm_g): cdef np.uint64_t mi1, mi2 # Get ghost zones, unordered for mi1 in range(self.s1): if mm_s._get_coarse(mi1): if self.is_refined(mi1): for mi2 in range(self.s2): if mm_s._get(mi1, mi2): self.add_neighbors_refined(mi1, mi2) # self.push_refined_bool(mi1) self.ghosts_ewah._set_refined_array_ptr(mi1, &self.refined_ghosts_bool[0]) for mi2 in range(self.s2): self.refined_ghosts_bool[mi2] = 0 else: self.add_neighbors_coarse(mi1) # Add ghost zones to bool array in order mm_g._set_coarse_array_ptr(&self.coarse_ghosts_bool[0]) for mi1 in range(self.s1): self.coarse_ghosts_bool[mi1] = 0 self.refined_ghosts_list._fill_bool(mm_g) mm_g._append(self.ghosts_ewah) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int fill_subcells_mi1(self, np.uint64_t nlevel, np.uint64_t ind1[3]) except -1: cdef np.uint64_t imi, fmi cdef np.uint64_t mi cdef np.uint64_t shift_by = 3 * (self.bitmap.index_order1 - nlevel) imi = encode_morton_64bit(ind1[0], ind1[1], ind1[2]) << shift_by fmi = imi + (1 << shift_by) for mi in range(imi, fmi): self.add_coarse(mi, 1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int fill_subcells_mi2(self, np.uint64_t nlevel, np.uint64_t mi1, np.uint64_t ind2[3]) except -1: cdef np.uint64_t imi, fmi cdef np.uint64_t shift_by = 3 * ((self.bitmap.index_order2 + self.bitmap.index_order1) - nlevel) imi = encode_morton_64bit(ind2[0], ind2[1], ind2[2]) << shift_by fmi = imi + (1 << shift_by) for mi2 in range(imi, fmi): self.add_refined(mi1, mi2, 1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int recursive_morton_mask( self, np.int32_t level, np.float64_t pos[3], np.float64_t dds[3], np.uint64_t mi1, np.uint64_t cur_ind[3]) except -1: cdef np.uint64_t mi2 cdef np.float64_t npos[3] cdef np.float64_t rpos[3] cdef np.float64_t ndds[3] cdef np.uint64_t nlevel cdef np.uint64_t ncur_ind[3] cdef np.uint64_t* zeros = [0, 0, 0] cdef int i, j, k, sbbox PyErr_CheckSignals() for i in range(3): ndds[i] = dds[i]/2 nlevel = level + 1 # Loop over octs for i in range(2): npos[0] = pos[0] + i*ndds[0] rpos[0] = npos[0] + ndds[0] ncur_ind[0] = (cur_ind[0] << 1) + i for j in range(2): npos[1] = pos[1] + j*ndds[1] rpos[1] = npos[1] + ndds[1] ncur_ind[1] = (cur_ind[1] << 1) + j for k in range(2): npos[2] = pos[2] + k*ndds[2] rpos[2] = npos[2] + ndds[2] ncur_ind[2] = (cur_ind[2] << 1) + k # Only recurse into selected cells sbbox = self.selector.select_bbox_edge(npos, rpos) if sbbox == 0: continue if nlevel < self.order1: if sbbox == 1: self.fill_subcells_mi1(nlevel, ncur_ind) else: self.recursive_morton_mask( nlevel, npos, ndds, mi1, ncur_ind) elif nlevel == self.order1: mi1 = encode_morton_64bit( ncur_ind[0], ncur_ind[1], ncur_ind[2]) if sbbox == 2: # an edge cell if self.is_refined(mi1) == 1: # note we pass zeros here in the last argument # this is because we now need to generate # *refined* indices above order1 so we need to # start a new running count of refined indices. # # note that recursive_morton_mask does not # mutate the last argument (a new index is # calculated in each stack frame) so this is # safe self.recursive_morton_mask( nlevel, npos, ndds, mi1, zeros) self.add_coarse(mi1, sbbox) self.push_refined_bool(mi1) elif nlevel < (self.order1 + self.order2): if sbbox == 1: self.fill_subcells_mi2(nlevel, mi1, ncur_ind) else: self.recursive_morton_mask( nlevel, npos, ndds, mi1, ncur_ind) elif nlevel == (self.order1 + self.order2): mi2 = encode_morton_64bit( ncur_ind[0], ncur_ind[1], ncur_ind[2]) self.add_refined(mi1, mi2, sbbox) return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void recursive_morton_files(self, np.int32_t level, np.float64_t pos[3], np.float64_t dds[3], np.uint64_t mi1): cdef np.uint64_t mi2 cdef np.float64_t npos[3] cdef np.float64_t rpos[3] cdef np.float64_t ndds[3] cdef np.uint64_t nlevel cdef np.float64_t DLE[3] cdef np.uint64_t ind1[3] cdef int i, j, k, m for i in range(3): ndds[i] = dds[i]/2 nlevel = level + 1 # Loop over octs for i in range(2): npos[0] = pos[0] + i*ndds[0] rpos[0] = npos[0] + ndds[0] for j in range(2): npos[1] = pos[1] + j*ndds[1] rpos[1] = npos[1] + ndds[1] for k in range(2): npos[2] = pos[2] + k*ndds[2] rpos[2] = npos[2] + ndds[2] # Only recurse into selected cells if not self.selector.select_bbox(npos, rpos): continue if nlevel < self.order1: self.recursive_morton_files(nlevel, npos, ndds, mi1) elif nlevel == self.order1: mi1 = bounded_morton_dds(npos[0], npos[1], npos[2], self.DLE, ndds) if self.is_refined_files(mi1): self.recursive_morton_files(nlevel, npos, ndds, mi1) self.set_files_coarse(mi1) elif nlevel < (self.order1 + self.order2): self.recursive_morton_files(nlevel, npos, ndds, mi1) elif nlevel == (self.order1 + self.order2): decode_morton_64bit(mi1,ind1) for m in range(3): DLE[m] = self.DLE[m] + ndds[m]*ind1[m]*self.max_index2 mi2 = bounded_morton_dds(npos[0], npos[1], npos[2], DLE, ndds) self.set_files_refined(mi1,mi2) cdef class ParticleBitmapOctreeContainer(SparseOctreeContainer): cdef Oct** oct_list cdef public int n_ref cdef int loaded # Loaded with load_octree? cdef np.uint8_t* _ptr_index_base_roots cdef np.uint8_t* _ptr_index_base_octs cdef np.uint64_t* _ptr_octs_per_root cdef public np.uint8_t[:] _index_base_roots cdef public np.uint8_t[:] _index_base_octs cdef np.uint64_t[:] _octs_per_root cdef public int overlap_cells def __init__(self, domain_dimensions, domain_left_edge, domain_right_edge, int num_root, num_zones = 2): super(ParticleBitmapOctreeContainer, self).__init__( domain_dimensions, domain_left_edge, domain_right_edge, num_zones) self.loaded = 0 self.fill_style = "o" self.partial_coverage = 2 self.overlap_cells = 0 # Now the overrides self.max_level = -1 self.max_root = num_root self.root_nodes = malloc(sizeof(OctKey) * num_root) self._ptr_index_base_roots = malloc(sizeof(np.uint8_t) * num_root) self._ptr_octs_per_root = malloc(sizeof(np.uint64_t) * num_root) for i in range(num_root): self.root_nodes[i].key = -1 self.root_nodes[i].node = NULL self._ptr_index_base_roots[i] = 1 self._ptr_octs_per_root[i] = 0 self._index_base_roots = self._ptr_index_base_roots self._octs_per_root = self._ptr_octs_per_root def allocate_domains(self, counts = None): if counts is None: counts = [self.max_root] OctreeContainer.allocate_domains(self, counts) def finalize(self): # Assign domain ind cdef SelectorObject selector = AlwaysSelector(None) selector.overlap_cells = self.overlap_cells cdef oct_visitors.AssignDomainInd visitor visitor = oct_visitors.AssignDomainInd(self) self.visit_all_octs(selector, visitor) assert ((visitor.global_index+1)*visitor.nz == visitor.index) # Copy indexes self._ptr_index_base_octs = malloc(sizeof(np.uint8_t)*self.nocts) self._index_base_octs = self._ptr_index_base_octs cdef np.int64_t nprev_octs = 0 cdef int i for i in range(self.num_root): self._index_base_octs[nprev_octs:(nprev_octs+self._octs_per_root[i])] = self._index_base_roots[i] nprev_octs += self._octs_per_root[i] cdef visit_assign(self, Oct *o, np.int64_t *lpos, int level, int *max_level, np.int64_t index_root): cdef int i, j, k if o.children == NULL: self.oct_list[lpos[0]] = o self._index_base_octs[lpos[0]] = self._index_base_roots[index_root] lpos[0] += 1 max_level[0] = imax(max_level[0], level) for i in range(2): for j in range(2): for k in range(2): if o.children != NULL \ and o.children[cind(i,j,k)] != NULL: self.visit_assign(o.children[cind(i,j,k)], lpos, level + 1, max_level, index_root) return cdef Oct* allocate_oct(self): #Allocate the memory, set to NULL or -1 #We reserve space for n_ref particles, but keep #track of how many are used with np initially 0 self.nocts += 1 cdef Oct *my_oct = malloc(sizeof(Oct)) my_oct.domain = -1 my_oct.file_ind = 0 my_oct.domain_ind = self.nocts - 1 my_oct.children = NULL return my_oct def get_index_base_octs(self, np.int64_t[:] domain_ind): cdef np.int64_t ndst = np.max(domain_ind) + 1 ind = np.zeros(ndst, 'int64') - 1 self._get_index_base_octs(ind, domain_ind) return ind[ind >= 0] cdef void _get_index_base_octs(self, np.int64_t[:] ind, np.int64_t[:] domain_ind): cdef SelectorObject selector = AlwaysSelector(None) selector.overlap_cells = self.overlap_cells cdef oct_visitors.IndexMaskMapOcts visitor visitor = oct_visitors.IndexMaskMapOcts(self) visitor.oct_mask = self._index_base_octs visitor.oct_index = ind visitor.map_domain_ind = domain_ind self.visit_all_octs(selector, visitor) def __dealloc__(self): #Call the freemem ops on every ocy #of the root mesh recursively cdef int i if self.root_nodes== NULL: return if self.loaded == 0: for i in range(self.max_root): if self.root_nodes[i].node == NULL: continue self.visit_free(&self.root_nodes.node[i], 0) self.root_nodes = NULL free(self.oct_list) free(self._ptr_index_base_roots) free(self._ptr_index_base_octs) free(self._ptr_octs_per_root) self.oct_list = NULL cdef void visit_free(self, Oct *o, int free_this): #Free the memory for this oct recursively cdef int i, j, k for i in range(2): for j in range(2): for k in range(2): if o.children != NULL \ and o.children[cind(i,j,k)] != NULL: self.visit_free(o.children[cind(i,j,k)], 1) if o.children != NULL: free(o.children) if free_this == 1: free(o) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void recursive_add(self, Oct *o, np.ndarray[np.uint64_t, ndim=1] indices, int level, int *max_level, int domain_id, int *count): cdef np.int64_t no = indices.shape[0], beg, end, nind cdef np.int64_t index cdef int i, j, k cdef int ind[3] cdef Oct *noct beg = end = 0 if level > max_level[0]: max_level[0] = level # Initialize children if o.children == NULL: o.children = malloc(sizeof(Oct *)*8) for i in range(2): for j in range(2): for k in range(2): o.children[cind(i,j,k)] = NULL # noct = self.allocate_oct() # noct.domain = o.domain # noct.file_ind = 0 # o.children[cind(i,j,k)] = noct # Loop through sets of particles with matching prefix at this level while end < no: beg = end index = (indices[beg] >> ((ORDER_MAX - level)*3)) while (end < no) and (index == (indices[end] >> ((ORDER_MAX - level)*3))): end += 1 nind = (end - beg) # Add oct for i in range(3): ind[i] = ((index >> (2 - i)) & 1) # noct = o.children[cind(ind[0],ind[1],ind[2])] if o.children[cind(ind[0],ind[1],ind[2])] != NULL: raise Exception('Child was already initialized...') noct = self.allocate_oct() noct.domain = o.domain o.children[cind(ind[0],ind[1],ind[2])] = noct # Don't add it to the list if it will be refined if nind > self.n_ref and level < ORDER_MAX: self.nocts -= 1 noct.domain_ind = -1 # overwritten by finalize else: count[0] += 1 noct.file_ind = o.file_ind # noct.file_ind = nind # o.file_ind = self.n_ref + 1 # Refine oct or add its children if nind > self.n_ref and level < ORDER_MAX: self.recursive_add(noct, indices[beg:end], level+1, max_level, domain_id, count) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def add(self, np.ndarray[np.uint64_t, ndim=1] indices, np.uint64_t order1, int domain_id = -1): #Add this particle to the root oct #Then if that oct has children, add it to them recursively #If the child needs to be refined because of max particles, do so cdef Oct *root = NULL cdef np.int64_t no = indices.shape[0], beg, end, index cdef int i cdef int ind[3] cdef np.uint64_t ind64[3] cdef int max_level = self.max_level # Note what we're doing here: we have decided the root will always be # zero, since we're in a forest of octrees, where the root_mesh node is # the level 0. This means our morton indices should be made with # respect to that, which means we need to keep a few different arrays # of them. cdef np.int64_t index_root = 0 cdef int root_count beg = end = 0 self._octs_per_root[:] = 1 # Roots count regardless while end < no: # Determine number of octs with this prefix beg = end index = (indices[beg] >> ((ORDER_MAX - self.level_offset)*3)) while (end < no) and (index == (indices[end] >> ((ORDER_MAX - self.level_offset)*3))): end += 1 # Find root for prefix decode_morton_64bit(index, ind64) for i in range(3): ind[i] = ind64[i] while (index_root < self.num_root) and \ (self.ipos_to_key(ind) != self.root_nodes[index_root].key): index_root += 1 if index_root >= self.num_root: raise Exception('No root found for {},{},{}'.format(ind[0],ind[1],ind[2])) root = self.root_nodes[index_root].node # self.get_root(ind, &root) # if root == NULL: # raise Exception('No root found for {},{},{}'.format(ind[0],ind[1],ind[2])) root.file_ind = index_root # Refine root as necessary if (end - beg) > self.n_ref: root_count = 0 self.nocts -= 1 self.recursive_add(root, indices[beg:end], self.level_offset+1, &max_level, domain_id, &root_count) self._octs_per_root[index_root] = root_count self.max_level = max_level assert(self.nocts == np.sum(self._octs_per_root)) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef Oct *refine_oct(self, Oct *o, np.uint64_t index, int level): #Allocate and initialize child octs #Attach particles to child octs #Remove particles from this oct entirely cdef int i, j, k cdef int ind[3] cdef Oct *noct # Initialize empty children if o.children == NULL: o.children = malloc(sizeof(Oct *)*8) # This version can be used to just add the child containing the index # for i in range(2): # for j in range(2): # for k in range(2): # o.children[cind(i,j,k)] = NULL # # Only allocate and count the indexed oct # for i in range(3): # ind[i] = (index >> ((ORDER_MAX - level)*3 + (2 - i))) & 1 # noct = self.allocate_oct() # noct.domain = o.domain # noct.file_ind = 0 # o.children[cind(ind[0],ind[1],ind[2])] = noct # o.file_ind = self.n_ref + 1 for i in range(2): for j in range(2): for k in range(2): noct = self.allocate_oct() noct.domain = o.domain noct.file_ind = 0 o.children[cind(i,j,k)] = noct o.file_ind = self.n_ref + 1 for i in range(3): ind[i] = (index >> ((ORDER_MAX - level)*3 + (2 - i))) & 1 noct = o.children[cind(ind[0],ind[1],ind[2])] return noct cdef void filter_particles(self, Oct *o, np.uint64_t *data, np.int64_t p, int level): # Now we look at the last nref particles to decide where they go. cdef int n = imin(p, self.n_ref) cdef np.uint64_t *arr = data + imax(p - self.n_ref, 0) # Now we figure out our prefix, which is the oct address at this level. # As long as we're actually in Morton order, we do not need to worry # about *any* of the other children of the oct. prefix1 = data[p] >> (ORDER_MAX - level)*3 for i in range(n): prefix2 = arr[i] >> (ORDER_MAX - level)*3 if (prefix1 == prefix2): o.file_ind += 1 yt-project-yt-f043ac8/yt/geometry/particle_smooth.pxd000066400000000000000000000061351510711153200230570ustar00rootroot00000000000000""" Particle Deposition onto Octs """ cimport numpy as np import numpy as np cimport cython from libc.math cimport sqrt from libc.stdlib cimport free, malloc, qsort from yt.utilities.lib.distance_queue cimport ( DistanceQueue, Neighbor_compare, NeighborList, r2dist, ) from yt.utilities.lib.fp_utils cimport * from .oct_container cimport Oct, OctreeContainer from .particle_deposit cimport get_kernel_func, gind, kernel_func cdef extern from "platform_dep.h": void *alloca(int) cdef class ParticleSmoothOperation: # We assume each will allocate and define their own temporary storage cdef kernel_func sph_kernel cdef public object nvals cdef np.float64_t DW[3] cdef int nfields cdef int maxn cdef bint periodicity[3] # Note that we are preallocating here, so this is *not* threadsafe. cdef void (*pos_setup)(np.float64_t ipos[3], np.float64_t opos[3]) cdef void neighbor_process(self, int dim[3], np.float64_t left_edge[3], np.float64_t dds[3], np.float64_t[:,:] ppos, np.float64_t **fields, np.int64_t[:] doffs, np.int64_t **nind, np.int64_t[:] pinds, np.int64_t[:] pcounts, np.int64_t offset, np.float64_t **index_fields, OctreeContainer octree, np.int64_t domain_id, int *nsize, np.float64_t[:,:] oct_left_edges, np.float64_t[:,:] oct_dds, DistanceQueue dq) cdef int neighbor_search(self, np.float64_t pos[3], OctreeContainer octree, np.int64_t **nind, int *nsize, np.int64_t nneighbors, np.int64_t domain_id, Oct **oct = ?, int extra_layer = ?) cdef void neighbor_process_particle(self, np.float64_t cpos[3], np.float64_t[:,:] ppos, np.float64_t **fields, np.int64_t[:] doffs, np.int64_t **nind, np.int64_t[:] pinds, np.int64_t[:] pcounts, np.int64_t offset, np.float64_t **index_fields, OctreeContainer octree, np.int64_t domain_id, int *nsize, DistanceQueue dq) cdef void neighbor_find(self, np.int64_t nneighbors, np.int64_t *nind, np.int64_t[:] doffs, np.int64_t[:] pcounts, np.int64_t[:] pinds, np.float64_t[:,:] ppos, np.float64_t cpos[3], np.float64_t[:,:] oct_left_edges, np.float64_t[:,:] oct_dds, DistanceQueue dq) cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **index_fields, DistanceQueue dq) yt-project-yt-f043ac8/yt/geometry/particle_smooth.pyx000066400000000000000000001017131510711153200231020ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Particle smoothing in cells """ cimport numpy as np import numpy as np cimport cython from cpython.exc cimport PyErr_CheckSignals from libc.math cimport cos, sin, sqrt from libc.stdlib cimport free, malloc, realloc from .oct_container cimport Oct, OctInfo, OctreeContainer cdef void spherical_coord_setup(np.float64_t ipos[3], np.float64_t opos[3]): opos[0] = ipos[0] * sin(ipos[1]) * cos(ipos[2]) opos[1] = ipos[0] * sin(ipos[1]) * sin(ipos[2]) opos[2] = ipos[0] * cos(ipos[1]) cdef void cart_coord_setup(np.float64_t ipos[3], np.float64_t opos[3]): opos[0] = ipos[0] opos[1] = ipos[1] opos[2] = ipos[2] cdef class ParticleSmoothOperation: def __init__(self, nvals, nfields, max_neighbors, kernel_name): # This is the set of cells, in grids, blocks or octs, we are handling. self.nvals = nvals self.nfields = nfields self.maxn = max_neighbors self.sph_kernel = get_kernel_func(kernel_name) def initialize(self, *args): raise NotImplementedError def finalize(self, *args): raise NotImplementedError @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) def process_octree(self, OctreeContainer mesh_octree, np.int64_t [:] mdom_ind, np.float64_t[:,:] positions, np.float64_t[:,:] oct_positions, fields = None, int domain_id = -1, int domain_offset = 0, periodicity = (True, True, True), index_fields = None, OctreeContainer particle_octree = None, np.int64_t [:] pdom_ind = None, geometry = "cartesian"): # This will be a several-step operation. # # We first take all of our particles and assign them to Octs. If they # are not in an Oct, we will assume they are out of bounds. Note that # this means that if we have loaded neighbor particles for which an Oct # does not exist, we are going to be discarding them -- so sparse # octrees will need to ensure that neighbor octs *exist*. Particles # will be assigned in a new NumPy array. Note that this incurs # overhead, but reduces complexity as we will now be able to use # argsort. # # After the particles have been assigned to Octs, we process each Oct # individually. We will do this by calling "get" for the *first* # particle in each set of Octs in the sorted list. After this, we get # neighbors for each Oct. # # Now, with the set of neighbors (and thus their indices) we allocate # an array of particles and their fields, fill these in, and call our # process function. # # This is not terribly efficient -- for starters, the neighbor function # is not the most efficient yet. We will also need to handle some # mechanism of an expandable array for holding pointers to Octs, so # that we can deal with >27 neighbors. if particle_octree is None: particle_octree = mesh_octree pdom_ind = mdom_ind cdef int nf, i, j cdef int dims[3] cdef np.float64_t **field_pointers cdef np.float64_t pos[3] cdef int nsize = 0 cdef np.int64_t *nind = NULL cdef OctInfo moi cdef Oct *oct cdef np.int64_t offset, poff cdef np.int64_t moff_p, moff_m cdef np.int64_t[:] pind, doff, pdoms, pcount cdef np.ndarray[np.float64_t, ndim=1] tarr cdef np.ndarray[np.float64_t, ndim=4] iarr cdef np.float64_t[:,:] cart_positions cdef np.float64_t[:,:] oct_left_edges, oct_dds cdef OctInfo oinfo if geometry == "cartesian": self.pos_setup = cart_coord_setup cart_positions = positions elif geometry == "spherical": self.pos_setup = spherical_coord_setup cart_positions = np.empty((positions.shape[0], 3), dtype="float64") cart_positions[:,0] = positions[:,0] * \ np.sin(positions[:,1]) * \ np.cos(positions[:,2]) cart_positions[:,1] = positions[:,0] * \ np.sin(positions[:,1]) * \ np.sin(positions[:,2]) cart_positions[:,2] = positions[:,0] * \ np.cos(positions[:,1]) periodicity = (False, False, False) else: raise NotImplementedError dims[0] = dims[1] = dims[2] = mesh_octree.nz cdef int nz = dims[0] * dims[1] * dims[2] # pcount is the number of particles per oct. pcount = np.zeros_like(pdom_ind) oct_left_edges = np.zeros((pdom_ind.shape[0], 3), dtype='float64') oct_dds = np.zeros_like(oct_left_edges) # doff is the offset to a given oct in the sorted particles. doff = np.zeros_like(pdom_ind) - 1 moff_p = particle_octree.get_domain_offset(domain_id + domain_offset) moff_m = mesh_octree.get_domain_offset(domain_id + domain_offset) # pdoms points particles at their octs. So the value in this array, for # a given index, is the local oct index. pdoms = np.zeros(positions.shape[0], dtype="int64") - 1 nf = len(fields) if fields is None: fields = [] field_pointers = alloca(sizeof(np.float64_t *) * nf) for i in range(nf): tarr = fields[i] field_pointers[i] = tarr.data if index_fields is None: index_fields = [] nf = len(index_fields) index_field_pointers = alloca(sizeof(np.float64_t *) * nf) for i in range(nf): iarr = index_fields[i] index_field_pointers[i] = iarr.data for i in range(3): self.DW[i] = (mesh_octree.DRE[i] - mesh_octree.DLE[i]) self.periodicity[i] = periodicity[i] cdef np.float64_t factor = particle_octree.nz for i in range(positions.shape[0]): for j in range(3): pos[j] = positions[i, j] oct = particle_octree.get(pos, &oinfo) if oct == NULL or (domain_id > 0 and oct.domain != domain_id): continue # Note that this has to be our local index, not our in-file index. # This is the particle count, which we'll use once we have sorted # the particles to calculate the offsets into each oct's particles. offset = oct.domain_ind - moff_p pcount[offset] += 1 pdoms[i] = offset # We store the *actual* offset. # store oct positions and dds to avoid searching for neighbors # in octs that we know are too far away for j in range(3): oct_left_edges[offset, j] = oinfo.left_edge[j] oct_dds[offset, j] = oinfo.dds[j] * factor # Now we have oct assignments. Let's sort them. # Note that what we will be providing to our processing functions will # actually be indirectly-sorted fields. This preserves memory at the # expense of additional pointer lookups. pind = np.asarray(np.argsort(pdoms), dtype='int64', order='C') # So what this means is that we now have all the oct-0 particle indices # in order, then the oct-1, etc etc. # This now gives us the indices to the particles for each domain. for i in range(positions.shape[0]): # This value, poff, is the index of the particle in the *unsorted* # arrays. poff = pind[i] offset = pdoms[poff] # If we have yet to assign the starting index to this oct, we do so # now. if doff[offset] < 0: doff[offset] = i #print(domain_id, domain_offset, moff_p, moff_m) #raise RuntimeError # Now doff is full of offsets to the first entry in the pind that # refers to that oct's particles. cdef np.ndarray[np.uint8_t, ndim=1] visited visited = np.zeros(mdom_ind.shape[0], dtype="uint8") cdef int nproc = 0 # This should be thread-private if we ever go to OpenMP cdef DistanceQueue dist_queue = DistanceQueue(self.maxn) dist_queue._setup(self.DW, self.periodicity) for i in range(oct_positions.shape[0]): if (i % 10000) == 0: PyErr_CheckSignals() for j in range(3): pos[j] = oct_positions[i, j] oct = mesh_octree.get(pos, &moi) offset = mdom_ind[oct.domain_ind - moff_m] * nz if visited[oct.domain_ind - moff_m] == 1: continue visited[oct.domain_ind - moff_m] = 1 if offset < 0: continue nproc += 1 self.neighbor_process( dims, moi.left_edge, moi.dds, cart_positions, field_pointers, doff, &nind, pind, pcount, offset, index_field_pointers, particle_octree, domain_id, &nsize, oct_left_edges, oct_dds, dist_queue) #print("VISITED", visited.sum(), visited.size,) #print(100.0*float(visited.sum())/visited.size) if nind != NULL: free(nind) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) def process_particles(self, OctreeContainer particle_octree, np.ndarray[np.int64_t, ndim=1] pdom_ind, np.ndarray[np.float64_t, ndim=2] positions, fields = None, int domain_id = -1, int domain_offset = 0, periodicity = (True, True, True), geometry = "cartesian"): # The other functions in this base class process particles in a way # that results in a modification to the *mesh*. This function is # designed to process neighboring particles in such a way that a new # *particle* field is defined -- this means that new particle # attributes (*not* mesh attributes) can be created that rely on the # values of nearby particles. For instance, a smoothing kernel, or a # nearest-neighbor field. cdef int nf, i, j, k cdef np.float64_t **field_pointers cdef np.float64_t pos[3] cdef int nsize = 0 cdef np.int64_t *nind = NULL cdef Oct *oct cdef np.int64_t offset cdef np.int64_t moff_p, pind0, poff cdef np.int64_t[:] pind, doff, pdoms, pcount cdef np.ndarray[np.float64_t, ndim=1] tarr cdef np.ndarray[np.float64_t, ndim=2] cart_positions if geometry == "cartesian": self.pos_setup = cart_coord_setup cart_positions = positions elif geometry == "spherical": self.pos_setup = spherical_coord_setup cart_positions = np.empty((positions.shape[0], 3), dtype="float64") cart_positions[:,0] = positions[:,0] * \ np.sin(positions[:,1]) * \ np.cos(positions[:,2]) cart_positions[:,1] = positions[:,0] * \ np.sin(positions[:,1]) * \ np.sin(positions[:,2]) cart_positions[:,2] = positions[:,0] * \ np.cos(positions[:,1]) periodicity = (False, False, False) else: raise NotImplementedError pcount = np.zeros_like(pdom_ind) doff = np.zeros_like(pdom_ind) - 1 moff_p = particle_octree.get_domain_offset(domain_id + domain_offset) pdoms = np.zeros(positions.shape[0], dtype="int64") - 1 nf = len(fields) if fields is None: fields = [] field_pointers = alloca(sizeof(np.float64_t *) * nf) for i in range(nf): tarr = fields[i] field_pointers[i] = tarr.data for i in range(3): self.DW[i] = (particle_octree.DRE[i] - particle_octree.DLE[i]) self.periodicity[i] = periodicity[i] for i in range(positions.shape[0]): for j in range(3): pos[j] = positions[i, j] oct = particle_octree.get(pos) if oct == NULL or (domain_id > 0 and oct.domain != domain_id): continue # Note that this has to be our local index, not our in-file index. # This is the particle count, which we'll use once we have sorted # the particles to calculate the offsets into each oct's particles. offset = oct.domain_ind - moff_p pcount[offset] += 1 pdoms[i] = offset # We store the *actual* offset. # Now we have oct assignments. Let's sort them. # Note that what we will be providing to our processing functions will # actually be indirectly-sorted fields. This preserves memory at the # expense of additional pointer lookups. pind = np.asarray(np.argsort(pdoms), dtype='int64', order='C') # So what this means is that we now have all the oct-0 particle indices # in order, then the oct-1, etc etc. # This now gives us the indices to the particles for each domain. for i in range(positions.shape[0]): # This value, poff, is the index of the particle in the *unsorted* # arrays. poff = pind[i] offset = pdoms[poff] # If we have yet to assign the starting index to this oct, we do so # now. if doff[offset] < 0: doff[offset] = i #print(domain_id, domain_offset, moff_p, moff_m) #raise RuntimeError # Now doff is full of offsets to the first entry in the pind that # refers to that oct's particles. # This should be thread-private if we ever go to OpenMP cdef DistanceQueue dist_queue = DistanceQueue(self.maxn) dist_queue._setup(self.DW, self.periodicity) for i in range(doff.shape[0]): if doff[i] < 0: continue offset = pind[doff[i]] for j in range(3): pos[j] = positions[offset, j] for j in range(pcount[i]): pind0 = pind[doff[i] + j] for k in range(3): pos[k] = positions[pind0, k] self.neighbor_process_particle(pos, cart_positions, field_pointers, doff, &nind, pind, pcount, pind0, NULL, particle_octree, domain_id, &nsize, dist_queue) #print("VISITED", visited.sum(), visited.size,) #print(100.0*float(visited.sum())/visited.size) if nind != NULL: free(nind) cdef int neighbor_search(self, np.float64_t pos[3], OctreeContainer octree, np.int64_t **nind, int *nsize, np.int64_t nneighbors, np.int64_t domain_id, Oct **oct = NULL, int extra_layer = 0): cdef OctInfo oi cdef Oct *ooct cdef Oct **neighbors cdef Oct **first_layer cdef int j, total_neighbors = 0, initial_layer = 0 cdef int layer_ind = 0 cdef np.int64_t moff = octree.get_domain_offset(domain_id) ooct = octree.get(pos, &oi) if oct != NULL and ooct == oct[0]: return nneighbors oct[0] = ooct if nind[0] == NULL: nsize[0] = 27 nind[0] = malloc(sizeof(np.int64_t)*nsize[0]) # This is our "seed" set of neighbors. If we are asked to, we will # create a master list of neighbors that is much bigger and includes # everything. layer_ind = 0 first_layer = NULL while 1: neighbors = octree.neighbors(&oi, &nneighbors, ooct, self.periodicity) # Now we have all our neighbors. And, we should be set for what # else we need to do. if total_neighbors + nneighbors > nsize[0]: nind[0] = realloc( nind[0], sizeof(np.int64_t)*(nneighbors + total_neighbors)) nsize[0] = nneighbors + total_neighbors for j in range(nneighbors): # Particle octree neighbor indices nind[0][j + total_neighbors] = neighbors[j].domain_ind - moff total_neighbors += nneighbors if extra_layer == 0: # Not adding on any additional layers here. free(neighbors) neighbors = NULL break if initial_layer == 0: initial_layer = nneighbors first_layer = neighbors else: # Allocated internally; we free this in the loops if we aren't # tracking it free(neighbors) neighbors = NULL ooct = first_layer[layer_ind] layer_ind += 1 if layer_ind == initial_layer: neighbors break for j in range(total_neighbors): # Particle octree neighbor indices if nind[0][j] == -1: continue for n in range(j): if nind[0][j] == nind[0][n]: nind[0][j] = -1 # This is allocated by the neighbors function, so we deallocate it. if first_layer != NULL: free(first_layer) return total_neighbors @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) def process_grid(self, gobj, np.ndarray[np.float64_t, ndim=2] positions, fields = None): raise NotImplementedError cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **ifields, DistanceQueue dq): raise NotImplementedError @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void neighbor_find(self, np.int64_t nneighbors, np.int64_t *nind, np.int64_t[:] doffs, np.int64_t[:] pcounts, np.int64_t[:] pinds, np.float64_t[:,:] ppos, np.float64_t cpos[3], np.float64_t[:,:] oct_left_edges, np.float64_t[:,:] oct_dds, DistanceQueue dq ): # We are now given the number of neighbors, the indices into the # domains for them, and the number of particles for each. cdef int ni, i, j, k cdef np.int64_t offset, pn, pc cdef np.float64_t pos[3] cdef np.float64_t ex[2] cdef np.float64_t DR[2] cdef np.float64_t r2_trunc, r2, dist dq.neighbor_reset() for ni in range(nneighbors): if nind[ni] == -1: continue # terminate early if all 8 corners of oct are farther away than # most distant currently known neighbor if oct_left_edges != None and dq.curn == dq.maxn: r2_trunc = dq.neighbors[dq.curn - 1].r2 # iterate over each dimension in the outer loop so we can # consolidate temporary storage # What this next bit does is figure out which component is the # closest, of each possible permutation. # k here is the dimension r2 = 0.0 for k in range(3): # We start at left edge, then do halfway, then right edge. ex[0] = oct_left_edges[nind[ni], k] ex[1] = ex[0] + oct_dds[nind[ni], k] # There are three possibilities; we are between, left-of, # or right-of the extrema. Thanks to # http://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point # for some help. This has been modified to account for # periodicity. dist = 0.0 DR[0] = (ex[0] - cpos[k]) DR[1] = (cpos[k] - ex[1]) for j in range(2): if not self.periodicity[k]: pass elif (DR[j] > self.DW[k]/2.0): DR[j] -= self.DW[k] elif (DR[j] < -self.DW[k]/2.0): DR[j] += self.DW[k] dist = fmax(dist, DR[j]) r2 += dist*dist if r2 > r2_trunc: continue offset = doffs[nind[ni]] pc = pcounts[nind[ni]] for i in range(pc): pn = pinds[offset + i] for j in range(3): pos[j] = ppos[pn, j] dq.neighbor_eval(pn, pos, cpos) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void neighbor_process(self, int dim[3], np.float64_t left_edge[3], np.float64_t dds[3], np.float64_t[:,:] ppos, np.float64_t **fields, np.int64_t [:] doffs, np.int64_t **nind, np.int64_t [:] pinds, np.int64_t[:] pcounts, np.int64_t offset, np.float64_t **index_fields, OctreeContainer octree, np.int64_t domain_id, int *nsize, np.float64_t[:,:] oct_left_edges, np.float64_t[:,:] oct_dds, DistanceQueue dq): # Note that we assume that fields[0] == smoothing length in the native # units supplied. We can now iterate over every cell in the block and # every particle to find the nearest. We will use a priority heap. cdef int i, j, k, ntot, nntot, m, nneighbors cdef np.float64_t cpos[3] cdef np.float64_t opos[3] cdef Oct* oct = NULL cpos[0] = left_edge[0] + 0.5*dds[0] for i in range(dim[0]): cpos[1] = left_edge[1] + 0.5*dds[1] for j in range(dim[1]): cpos[2] = left_edge[2] + 0.5*dds[2] for k in range(dim[2]): self.pos_setup(cpos, opos) nneighbors = self.neighbor_search(opos, octree, nind, nsize, nneighbors, domain_id, &oct, 0) self.neighbor_find(nneighbors, nind[0], doffs, pcounts, pinds, ppos, opos, oct_left_edges, oct_dds, dq) # Now we have all our neighbors in our neighbor list. if dq.curn <-1*dq.maxn: ntot = nntot = 0 for m in range(nneighbors): if nind[0][m] < 0: continue nntot += 1 ntot += pcounts[nind[0][m]] print("SOMETHING WRONG", dq.curn, nneighbors, ntot, nntot) self.process(offset, i, j, k, dim, opos, fields, index_fields, dq) cpos[2] += dds[2] cpos[1] += dds[1] cpos[0] += dds[0] @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void neighbor_process_particle(self, np.float64_t cpos[3], np.float64_t[:,:] ppos, np.float64_t **fields, np.int64_t[:] doffs, np.int64_t **nind, np.int64_t[:] pinds, np.int64_t[:] pcounts, np.int64_t offset, np.float64_t **index_fields, OctreeContainer octree, np.int64_t domain_id, int *nsize, DistanceQueue dq): # Note that we assume that fields[0] == smoothing length in the native # units supplied. We can now iterate over every cell in the block and # every particle to find the nearest. We will use a priority heap. cdef int i, j, k cdef int dim[3] cdef Oct *oct = NULL cdef np.int64_t nneighbors = 0 i = j = k = 0 dim[0] = dim[1] = dim[2] = 1 cdef np.float64_t opos[3] self.pos_setup(cpos, opos) nneighbors = self.neighbor_search(opos, octree, nind, nsize, nneighbors, domain_id, &oct, 0) self.neighbor_find(nneighbors, nind[0], doffs, pcounts, pinds, ppos, opos, None, None, dq) self.process(offset, i, j, k, dim, opos, fields, index_fields, dq) cdef class VolumeWeightedSmooth(ParticleSmoothOperation): # This smoothing function evaluates the field, *without* normalization, at # every point in the *mesh*. Applying a normalization results in # non-conservation of mass when smoothing density; to avoid this, we do not # apply this normalization factor. The SPLASH paper # (http://arxiv.org/abs/0709.0832v1) discusses this in detail; what we are # applying here is equation 6, with variable smoothing lengths (eq 13). cdef np.float64_t **fp cdef public object vals def initialize(self): cdef int i if self.nfields < 4: # We need four fields -- the mass should be the first, then the # smoothing length for particles, the normalization factor to # ensure mass conservation, then the field we're smoothing. raise RuntimeError cdef np.ndarray tarr self.fp = malloc( sizeof(np.float64_t *) * (self.nfields - 3)) self.vals = [] # We usually only allocate one field; if we are doing multiple field, # single-pass smoothing, then we might have more. for i in range(self.nfields - 3): tarr = np.zeros(self.nvals, dtype="float64", order="F") self.vals.append(tarr) self.fp[i] = tarr.data def finalize(self): free(self.fp) return self.vals @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **index_fields, DistanceQueue dq): # We have our i, j, k for our cell, as well as the cell position. # We also have a list of neighboring particles with particle numbers. cdef int n, fi cdef np.float64_t weight, r2, val, hsml, dens, mass, max_r cdef np.float64_t max_hsml, ihsml, ihsml3, kern cdef np.int64_t pn # We get back our mass # rho_i = sum(j = 1 .. n) m_j * W_ij max_r = sqrt(dq.neighbors[dq.curn-1].r2) max_hsml = index_fields[0][gind(i,j,k,dim) + offset] for n in range(dq.curn): # No normalization for the moment. # fields[0] is the smoothing length. r2 = dq.neighbors[n].r2 pn = dq.neighbors[n].pn # Smoothing kernel weight function mass = fields[0][pn] hsml = fields[1][pn] dens = fields[2][pn] if hsml < 0: hsml = max_r if hsml == 0: continue ihsml = 1.0/hsml hsml = fmax(max_hsml/2.0, hsml) ihsml3 = ihsml*ihsml*ihsml # Usually this density has been computed if dens == 0.0: continue weight = (mass / dens) * ihsml3 kern = self.sph_kernel(sqrt(r2) * ihsml) weight *= kern # Mass of the particle times the value for fi in range(self.nfields - 3): val = fields[fi + 3][pn] self.fp[fi][gind(i,j,k,dim) + offset] += val * weight return volume_weighted_smooth = VolumeWeightedSmooth cdef class NearestNeighborSmooth(ParticleSmoothOperation): cdef np.float64_t *fp cdef public object vals def initialize(self): cdef np.ndarray tarr assert(self.nfields == 1) tarr = np.zeros(self.nvals, dtype="float64", order="F") self.vals = tarr self.fp = tarr.data def finalize(self): return self.vals @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **index_fields, DistanceQueue dq): # We have our i, j, k for our cell, as well as the cell position. # We also have a list of neighboring particles with particle numbers. cdef np.int64_t pn # We get back our mass # rho_i = sum(j = 1 .. n) m_j * W_ij pn = dq.neighbors[0].pn self.fp[gind(i,j,k,dim) + offset] = fields[0][pn] #self.fp[gind(i,j,k,dim) + offset] = dq.neighbors[0].r2 return nearest_smooth = NearestNeighborSmooth cdef class IDWInterpolationSmooth(ParticleSmoothOperation): cdef np.float64_t *fp cdef public int p2 cdef public object vals def initialize(self): cdef np.ndarray tarr assert(self.nfields == 1) tarr = np.zeros(self.nvals, dtype="float64", order="F") self.vals = tarr self.fp = tarr.data self.p2 = 2 # Power, for IDW, in units of 2. So we only do even p's. def finalize(self): return self.vals @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **index_fields, DistanceQueue dq): # We have our i, j, k for our cell, as well as the cell position. # We also have a list of neighboring particles with particle numbers. cdef np.int64_t pn, ni, di cdef np.float64_t total_weight = 0.0, total_value = 0.0, r2, val, w # We're going to do a very simple IDW average if dq.neighbors[0].r2 == 0.0: pn = dq.neighbors[0].pn self.fp[gind(i,j,k,dim) + offset] = fields[0][pn] for ni in range(dq.curn): r2 = dq.neighbors[ni].r2 val = fields[0][dq.neighbors[ni].pn] w = r2 for di in range(self.p2 - 1): w *= r2 total_value += w * val total_weight += w self.fp[gind(i,j,k,dim) + offset] = total_value / total_weight return idw_smooth = IDWInterpolationSmooth cdef class NthNeighborDistanceSmooth(ParticleSmoothOperation): def initialize(self): return def finalize(self): return @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **index_fields, DistanceQueue dq): cdef np.float64_t max_r # We assume "offset" here is the particle index. max_r = sqrt(dq.neighbors[dq.curn-1].r2) fields[0][offset] = max_r nth_neighbor_smooth = NthNeighborDistanceSmooth cdef class SmoothedDensityEstimate(ParticleSmoothOperation): def initialize(self): return def finalize(self): return @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef void process(self, np.int64_t offset, int i, int j, int k, int dim[3], np.float64_t cpos[3], np.float64_t **fields, np.float64_t **index_fields, DistanceQueue dq): cdef np.float64_t r2, hsml, dens, mass, weight, lw cdef int pn # We assume "offset" here is the particle index. hsml = sqrt(dq.neighbors[dq.curn-1].r2) dens = 0.0 weight = 0.0 for pn in range(dq.curn): mass = fields[0][dq.neighbors[pn].pn] r2 = dq.neighbors[pn].r2 lw = self.sph_kernel(sqrt(r2) / hsml) dens += mass * lw weight = (4.0/3.0) * 3.1415926 * hsml**3 fields[1][offset] = dens/weight density_smooth = SmoothedDensityEstimate yt-project-yt-f043ac8/yt/geometry/selection_routines.pxd000066400000000000000000000075351510711153200236050ustar00rootroot00000000000000""" Geometry selection routine imports. """ cimport numpy as np from yt.geometry.grid_visitors cimport ( GridTreeNode, GridVisitorData, check_child_masked, grid_visitor_function, ) from yt.utilities.lib.fp_utils cimport _ensure_code from yt.utilities.lib.geometry_utils cimport decode_morton_64bit from .oct_container cimport OctreeContainer from .oct_visitors cimport Oct, OctVisitor cdef class SelectorObject: cdef public np.int32_t min_level cdef public np.int32_t max_level cdef public int overlap_cells cdef public np.float64_t domain_width[3] cdef public np.float64_t domain_center[3] cdef public bint periodicity[3] cdef bint _hash_initialized cdef np.int64_t _hash cdef void recursively_visit_octs(self, Oct *root, np.float64_t pos[3], np.float64_t dds[3], int level, OctVisitor visitor, int visit_covered = ?) cdef void visit_oct_cells(self, Oct *root, Oct *ch, np.float64_t spos[3], np.float64_t sdds[3], OctVisitor visitor, int i, int j, int k) cdef int select_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = ?) noexcept nogil cdef int select_grid_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.int32_t level, Oct *o = ?) noexcept nogil cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) noexcept nogil cdef int select_point(self, np.float64_t pos[3]) noexcept nogil cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) noexcept nogil cdef int select_bbox(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil cdef int select_bbox_edge(self, np.float64_t left_edge[3], np.float64_t right_edge[3]) noexcept nogil cdef int fill_mask_selector_regular_grid(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.float64_t dds[3], int dim[3], np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask, np.ndarray[np.uint8_t, ndim=3] mask, int level) cdef int fill_mask_selector(self, np.float64_t left_edge[3], np.float64_t right_edge[3], np.float64_t **dds, int dim[3], np.ndarray[np.uint8_t, ndim=3, cast=True] child_mask, np.ndarray[np.uint8_t, ndim=3] mask, int level) cdef void visit_grid_cells(self, GridVisitorData *data, grid_visitor_function *func, np.uint8_t *cached_mask = ?) # compute periodic distance (if periodicity set) # assuming 0->domain_width[d] coordinates cdef np.float64_t periodic_difference( self, np.float64_t x1, np.float64_t x2, int d) noexcept nogil cdef class AlwaysSelector(SelectorObject): pass cdef class OctreeSubsetSelector(SelectorObject): cdef public SelectorObject base_selector cdef public np.int64_t domain_id cdef class BooleanSelector(SelectorObject): cdef public SelectorObject sel1 cdef public SelectorObject sel2 cdef inline np.float64_t _periodic_dist(np.float64_t x1, np.float64_t x2, np.float64_t dw, bint periodic) noexcept nogil: cdef np.float64_t rel = x1 - x2 if not periodic: return rel if rel > dw * 0.5: rel -= dw elif rel < -dw * 0.5: rel += dw return rel yt-project-yt-f043ac8/yt/geometry/selection_routines.pyx000066400000000000000000000147021510711153200236240ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS """ Geometry selection routines. """ import numpy as np cimport cython cimport numpy as np from libc.math cimport sqrt from libc.stdlib cimport free, malloc from yt.utilities.lib.bitarray cimport ba_get_value from yt.utilities.lib.fnv_hash cimport c_fnv_hash as fnv_hash from yt.utilities.lib.fp_utils cimport fclip, fmax, fmin, iclip from yt.utilities.lib.grid_traversal cimport walk_volume from yt.utilities.lib.volume_container cimport VolumeContainer from .oct_container cimport Oct, OctreeContainer from .oct_visitors cimport cind cdef extern from "math.h": double exp(double x) noexcept nogil float expf(float x) noexcept nogil long double expl(long double x) noexcept nogil double floor(double x) noexcept nogil double ceil(double x) noexcept nogil double fmod(double x, double y) noexcept nogil double log2(double x) noexcept nogil long int lrint(double x) noexcept nogil double fabs(double x) noexcept nogil # use this as an epsilon test for grids aligned with selector # define here to avoid the gil later cdef np.float64_t grid_eps = np.finfo(np.float64).eps grid_eps = 0.0 cdef inline np.float64_t dot(np.float64_t* v1, np.float64_t* v2) noexcept nogil: return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] cdef inline np.float64_t norm(np.float64_t* v) noexcept nogil: return sqrt(dot(v, v)) # These routines are separated into a couple different categories: # # * Routines for identifying intersections of an object with a bounding box # * Routines for identifying cells/points inside a bounding box that # intersect with an object # * Routines that speed up some type of geometric calculation # First, bounding box / object intersection routines. # These all respect the interface "dobj" and a set of left_edges, right_edges, # sometimes also accepting level and mask information. @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def convert_mask_to_indices(np.ndarray[np.uint8_t, ndim=3, cast=True] mask, int count, int transpose = 0): cdef int i, j, k, cpos cdef np.ndarray[np.int64_t, ndim=2] indices indices = np.zeros((count, 3), dtype='int64') cpos = 0 for i in range(mask.shape[0]): for j in range(mask.shape[1]): for k in range(mask.shape[2]): if mask[i, j, k] == 1: if transpose == 1: indices[cpos, 0] = k indices[cpos, 1] = j indices[cpos, 2] = i else: indices[cpos, 0] = i indices[cpos, 1] = j indices[cpos, 2] = k cpos += 1 return indices @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef _mask_fill(np.ndarray[np.float64_t, ndim=1] out, np.int64_t offset, np.ndarray[np.uint8_t, ndim=3, cast=True] mask, np.ndarray[cython.floating, ndim=3] vals): cdef np.int64_t count = 0 cdef int i, j, k for i in range(mask.shape[0]): for j in range(mask.shape[1]): for k in range(mask.shape[2]): if mask[i, j, k] == 1: out[offset + count] = vals[i, j, k] count += 1 return count def mask_fill(np.ndarray[np.float64_t, ndim=1] out, np.int64_t offset, np.ndarray[np.uint8_t, ndim=3, cast=True] mask, np.ndarray vals): if vals.dtype == np.float32: return _mask_fill[np.float32_t](out, offset, mask, vals) elif vals.dtype == np.float64: return _mask_fill[np.float64_t](out, offset, mask, vals) else: raise RuntimeError @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def points_in_cells( np.float64_t[:] cx, np.float64_t[:] cy, np.float64_t[:] cz, np.float64_t[:] dx, np.float64_t[:] dy, np.float64_t[:] dz, np.float64_t[:] px, np.float64_t[:] py, np.float64_t[:] pz): # Take a list of cells and particles and calculate which particles # are enclosed within one of the cells. This is used for querying # particle fields on clump/contour objects. # We use brute force since the cells are a relatively unordered collection. cdef int p, c, n_p, n_c cdef np.ndarray[np.uint8_t, ndim=1, cast=True] mask n_p = px.size n_c = cx.size mask = np.zeros(n_p, dtype="bool") for p in range(n_p): for c in range(n_c): if (fabs(px[p] - cx[c]) <= 0.5 * dx[c] and fabs(py[p] - cy[c]) <= 0.5 * dy[c] and fabs(pz[p] - cz[c]) <= 0.5 * dz[c]): mask[p] = True break return mask def bbox_intersects( SelectorObject selector, np.float64_t[::1] left_edges, np.float64_t dx ): cdef np.float64_t[3] right_edges right_edges[0] = left_edges[0] + dx right_edges[1] = left_edges[1] + dx right_edges[2] = left_edges[2] + dx return selector.select_bbox(&left_edges[0], right_edges) == 1 def fully_contains( SelectorObject selector, np.float64_t[::1] left_edges, np.float64_t dx, ): cdef np.float64_t[3] right_edges right_edges[0] = left_edges[0] + dx right_edges[1] = left_edges[1] + dx right_edges[2] = left_edges[2] + dx return selector.select_bbox_edge(&left_edges[0], right_edges) == 1 include "_selection_routines/selector_object.pxi" include "_selection_routines/point_selector.pxi" include "_selection_routines/sphere_selector.pxi" include "_selection_routines/region_selector.pxi" include "_selection_routines/cut_region_selector.pxi" include "_selection_routines/disk_selector.pxi" include "_selection_routines/cutting_plane_selector.pxi" include "_selection_routines/slice_selector.pxi" include "_selection_routines/ortho_ray_selector.pxi" include "_selection_routines/ray_selector.pxi" include "_selection_routines/data_collection_selector.pxi" include "_selection_routines/ellipsoid_selector.pxi" include "_selection_routines/grid_selector.pxi" include "_selection_routines/octree_subset_selector.pxi" include "_selection_routines/indexed_octree_subset_selector.pxi" include "_selection_routines/always_selector.pxi" include "_selection_routines/compose_selector.pxi" include "_selection_routines/halo_particles_selector.pxi" include "_selection_routines/boolean_selectors.pxi" yt-project-yt-f043ac8/yt/geometry/tests/000077500000000000000000000000001510711153200203035ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/tests/__init__.py000066400000000000000000000000001510711153200224020ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/geometry/tests/fake_octree.py000066400000000000000000000027251510711153200231320ustar00rootroot00000000000000import numpy as np from yt.geometry.fake_octree import create_fake_octree from yt.geometry.oct_container import ParticleOctreeContainer, RAMSESOctreeContainer nocts = 3 max_level = 12 dn = 2 dd = np.ones(3, dtype="i4") * dn dle = np.ones(3, dtype="f8") * 0.0 dre = np.ones(3, dtype="f8") fsub = 0.25 domain = 1 oct_handler = RAMSESOctreeContainer(dd, dle, dre) leaves = create_fake_octree(oct_handler, nocts, max_level, dd, dle, dre, fsub) mask = np.ones((nocts, 8), dtype="bool") cell_count = nocts * 8 oct_counts = oct_handler.count_levels(max_level, 1, mask) level_counts = np.concatenate( ( [ 0, ], np.cumsum(oct_counts), ) ) fc = oct_handler.fcoords(domain, mask, cell_count, level_counts.copy()) leavesb = oct_handler.count_leaves(mask) assert leaves == leavesb # Now take the fcoords, call them particles and recreate the same octree print("particle-based recreate") oct_handler2 = ParticleOctreeContainer(dd, dle, dre) oct_handler2.allocate_domains([nocts]) oct_handler2.n_ref = 1 # specifically make a maximum of 1 particle per oct oct_handler2.add(fc, 1) print("added particles") cell_count2 = nocts * 8 oct_counts2 = oct_handler.count_levels(max_level, 1, mask) level_counts2 = np.concatenate( ( [ 0, ], np.cumsum(oct_counts), ) ) fc2 = oct_handler.fcoords(domain, mask, cell_count, level_counts.copy()) leaves2 = oct_handler2.count_leaves(mask) assert leaves == leaves2 print("success") yt-project-yt-f043ac8/yt/geometry/tests/test_ewah_write_load.py000066400000000000000000000021101510711153200250430ustar00rootroot00000000000000from yt.loaders import load from yt.sample_data.api import _get_test_data_dir_path from yt.testing import assert_array_equal, requires_file @requires_file("TNGHalo/halo_59.hdf5") def test_ewah_write_load(tmp_path): mock_file = tmp_path / "halo_59.hdf5" mock_file.symlink_to(_get_test_data_dir_path() / "TNGHalo" / "halo_59.hdf5") masses = [] opts = [ (None, True, (6, 2, 4), False), (None, True, (6, 2, 4), True), ((5, 3), False, (5, 3, 3), False), ((5, 3), False, (5, 3, 3), True), ] for opt in opts: ds = load(mock_file, index_order=opt[0]) _, c = ds.find_max(("gas", "density")) assert ds.index.pii.mutable_index is opt[1] assert ds.index.pii.order1 == opt[2][0] assert ds.index.pii.order2_orig == opt[2][1] assert ds.index.pii.order2 == opt[2][2] assert ds.index.pii._is_loaded is opt[3] c += ds.quan(0.5, "Mpc") sp = ds.sphere(c, (20.0, "kpc")) mass = sp.sum(("gas", "mass")) masses.append(mass) assert_array_equal(masses[0], masses[1:]) yt-project-yt-f043ac8/yt/geometry/tests/test_geometries.py000066400000000000000000000017101510711153200240560ustar00rootroot00000000000000from importlib.util import find_spec import pytest import yt from yt.testing import fake_amr_ds @pytest.mark.parametrize( "geometry", [ "cartesian", "polar", "cylindrical", "spherical", "geographic", "internal_geographic", "spectral_cube", ], ) def test_testable_geometries(geometry): # check that initializing a simple fake dataset works in any geometry ds = fake_amr_ds(fields=[("gas", "density")], units=["g/cm**3"], geometry=geometry) # make sure basic plotting works for axis in range(3): if "geographic" in geometry and axis == 2 and find_spec("cartopy") is None: pytest.skip( reason=( "cannot test this case with vanilla yt (requires cartopy) " "see https://github.com/yt-project/yt/issues/4182" ) ) yt.SlicePlot(ds, axis, ("gas", "density"), buff_size=(8, 8)) yt-project-yt-f043ac8/yt/geometry/tests/test_grid_container.py000066400000000000000000000106351510711153200247100ustar00rootroot00000000000000import random import numpy as np from numpy.testing import assert_equal, assert_raises from yt.loaders import load_amr_grids def setup_test_ds(): """Prepare setup specific environment""" grid_data = [ { "left_edge": [0.0, 0.0, 0.0], "right_edge": [1.0, 1.0, 1.0], "level": 0, "dimensions": [16, 16, 16], }, { "left_edge": [0.25, 0.25, 0.25], "right_edge": [0.75, 0.75, 0.75], "level": 1, "dimensions": [16, 16, 16], }, { "left_edge": [0.25, 0.25, 0.375], "right_edge": [0.5, 0.5, 0.625], "level": 2, "dimensions": [16, 16, 16], }, { "left_edge": [0.5, 0.5, 0.375], "right_edge": [0.75, 0.75, 0.625], "level": 2, "dimensions": [16, 16, 16], }, { "left_edge": [0.3125, 0.3125, 0.4375], "right_edge": [0.4375, 0.4375, 0.5625], "level": 3, "dimensions": [16, 16, 16], }, { "left_edge": [0.5625, 0.5625, 0.4375], "right_edge": [0.6875, 0.6875, 0.5625], "level": 3, "dimensions": [16, 16, 16], }, ] for grid in grid_data: grid["density"] = ( np.random.random(grid["dimensions"]) * 2 ** grid["level"], "g/cm**3", ) return load_amr_grids(grid_data, [16, 16, 16]) def test_grid_tree(): """Main test suite for GridTree""" test_ds = setup_test_ds() grid_tree = test_ds.index._get_grid_tree() indices, levels, nchild, children = grid_tree.return_tree_info() grid_levels = [grid.Level for grid in test_ds.index.grids] grid_indices = [grid.id - grid._id_offset for grid in test_ds.index.grids] grid_nchild = [len(grid.Children) for grid in test_ds.index.grids] assert_equal(levels, grid_levels) assert_equal(indices, grid_indices) assert_equal(nchild, grid_nchild) for i, grid in enumerate(test_ds.index.grids): if grid_nchild[i] > 0: grid_children = np.array( [child.id - child._id_offset for child in grid.Children] ) assert_equal(grid_children, children[i]) def test_find_points(): """Main test suite for MatchPoints""" num_points = 100 test_ds = setup_test_ds() randx = np.random.uniform( low=test_ds.domain_left_edge[0], high=test_ds.domain_right_edge[0], size=num_points, ) randy = np.random.uniform( low=test_ds.domain_left_edge[1], high=test_ds.domain_right_edge[1], size=num_points, ) randz = np.random.uniform( low=test_ds.domain_left_edge[2], high=test_ds.domain_right_edge[2], size=num_points, ) point_grids, point_grid_inds = test_ds.index._find_points(randx, randy, randz) grid_inds = np.zeros((num_points), dtype="int64") for ind, ixx, iyy, izz in zip(range(num_points), randx, randy, randz, strict=True): pos = np.array([ixx, iyy, izz]) pt_level = -1 for grid in test_ds.index.grids: if ( np.all(pos >= grid.LeftEdge) and np.all(pos <= grid.RightEdge) and grid.Level > pt_level ): pt_level = grid.Level grid_inds[ind] = grid.id - grid._id_offset assert_equal(point_grid_inds, grid_inds) # Test whether find_points works for lists point_grids, point_grid_inds = test_ds.index._find_points( randx.tolist(), randy.tolist(), randz.tolist() ) assert_equal(point_grid_inds, grid_inds) # Test if find_points works for scalar ind = random.randint(0, num_points - 1) point_grids, point_grid_inds = test_ds.index._find_points( randx[ind], randy[ind], randz[ind] ) assert_equal(point_grid_inds, grid_inds[ind]) # Test if find_points fails properly for non equal indices' array sizes assert_raises(ValueError, test_ds.index._find_points, [0], 1.0, [2, 3]) def test_grid_arrays_view(): ds = setup_test_ds() tree = ds.index._get_grid_tree() grid_arr = tree.grid_arrays assert_equal(grid_arr["left_edge"], ds.index.grid_left_edge) assert_equal(grid_arr["right_edge"], ds.index.grid_right_edge) assert_equal(grid_arr["dims"], ds.index.grid_dimensions) assert_equal(grid_arr["level"], ds.index.grid_levels[:, 0]) yt-project-yt-f043ac8/yt/geometry/tests/test_grid_index.py000066400000000000000000000015161510711153200240330ustar00rootroot00000000000000from yt.testing import assert_allclose_units, fake_amr_ds def test_icoords_to_ires(): for geometry in ("cartesian", "spherical", "cylindrical"): ds = fake_amr_ds(geometry=geometry) dd = ds.r[:] icoords = dd.icoords ires = dd.ires fcoords, fwidth = ds.index._icoords_to_fcoords(icoords, ires) assert_allclose_units(fcoords, dd.fcoords, rtol=1e-14) assert_allclose_units(fwidth, dd.fwidth, rtol=1e-14) fcoords_xz, fwidth_xz = ds.index._icoords_to_fcoords( icoords[:, (0, 2)], ires, axes=(0, 2) ) assert_allclose_units(fcoords_xz[:, 0], dd.fcoords[:, 0]) assert_allclose_units(fcoords_xz[:, 1], dd.fcoords[:, 2]) assert_allclose_units(fwidth_xz[:, 0], dd.fwidth[:, 0]) assert_allclose_units(fwidth_xz[:, 1], dd.fwidth[:, 2]) yt-project-yt-f043ac8/yt/geometry/tests/test_particle_deposit.py000066400000000000000000000071001510711153200252440ustar00rootroot00000000000000from numpy.testing import assert_allclose, assert_array_less, assert_raises import yt from yt.loaders import load from yt.testing import fake_random_ds, requires_file, requires_module from yt.utilities.exceptions import YTBoundsDefinitionError def test_cic_deposit(): ds = fake_random_ds(64, nprocs=8, particles=64**3) my_reg = ds.arbitrary_grid( ds.domain_left_edge, ds.domain_right_edge, dims=[1, 800, 800] ) f = ("deposit", "all_cic") assert_raises(YTBoundsDefinitionError, my_reg.__getitem__, f) RAMSES = "output_00080/info_00080.txt" RAMSES_small = "ramses_new_format/output_00002/info_00002.txt" ISOGAL = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_file(RAMSES) def test_one_zone_octree_deposit(): ds = load(RAMSES) # Get a sphere centred on the main halo hpos = ds.arr( [0.5215110772898429, 0.5215110772898429, 0.5215110772898429], "code_length" ) hrvir = ds.quan(0.042307235300540924, "Mpc") sp = ds.sphere(hpos, hrvir * 10) assert sp["deposit", "io_cic"].shape == (1,) @requires_module("h5py") @requires_file(RAMSES) @requires_file(ISOGAL) def test_mesh_sampling(): for fn in (RAMSES, ISOGAL): ds = yt.load(fn) ds.add_mesh_sampling_particle_field(("index", "x"), ptype="all") ds.add_mesh_sampling_particle_field(("index", "dx"), ptype="all") dx = ds.r["all", "cell_index_dx"] xc = ds.r["all", "cell_index_x"] xp = ds.r["all", "particle_position_x"] dist = xp - xc assert_array_less(dist, dx) assert_array_less(-dist, dx) @requires_module("h5py") @requires_file(RAMSES) @requires_file(ISOGAL) def test_mesh_sampling_for_filtered_particles(): for fn in (RAMSES, ISOGAL): ds = yt.load(fn) @yt.particle_filter(requires=["particle_position_x"], filtered_type="io") def left(pfilter, data): return ( data[pfilter.filtered_type, "particle_position_x"].to("code_length") < 0.5 ) ds.add_particle_filter("left") for f in (("index", "x"), ("index", "dx"), ("gas", "density")): ds.add_mesh_sampling_particle_field(f, ptype="io") ds.add_mesh_sampling_particle_field(f, ptype="left") data_sources = (ds.all_data(), ds.box([0] * 3, [0.1] * 3)) def test_source(ptype, src): # Test accessing src[ptype, "cell_index_x"] src[ptype, "cell_index_dx"] src[ptype, "cell_gas_density"] for ptype in ("io", "left"): for src in data_sources: test_source(ptype, src) @requires_file(RAMSES) def test_mesh_sampling_with_indexing(): # Access with index caching ds = yt.load(RAMSES) ds.add_mesh_sampling_particle_field(("gas", "density"), ptype="all") ad = ds.all_data() ad["all", "cell_index"] v1 = ad["all", "cell_gas_density"] # Access with no index caching ds = yt.load(RAMSES) ds.add_mesh_sampling_particle_field(("gas", "density"), ptype="all") ad = ds.all_data() v2 = ad["all", "cell_gas_density"] # Check same answer is returned assert_allclose(v1, v2) @requires_file(RAMSES_small) def test_mesh_sampling_vs_field_value_at_point(): all_ds = (fake_random_ds(ndims=3, particles=500), yt.load(RAMSES_small)) for ds in all_ds: ds.add_mesh_sampling_particle_field(("gas", "density"), ptype="all") val = ds.r["all", "cell_gas_density"] ref = ds.find_field_values_at_points( ("gas", "density"), ds.r["all", "particle_position"] ) assert_allclose(val, ref) yt-project-yt-f043ac8/yt/geometry/tests/test_particle_octree.py000066400000000000000000000643071510711153200250720ustar00rootroot00000000000000import os import numpy as np from numpy.testing import assert_array_equal, assert_equal import yt.units.dimensions as dimensions from yt.geometry.oct_container import _ORDER_MAX from yt.geometry.particle_oct_container import ParticleBitmap, ParticleOctreeContainer from yt.geometry.selection_routines import RegionSelector from yt.testing import requires_module from yt.units.unit_registry import UnitRegistry from yt.units.yt_array import YTArray from yt.utilities.lib.geometry_utils import ( get_hilbert_indices, get_hilbert_points, get_morton_indices, get_morton_points, ) NPART = 32**3 DLE = np.array([0.0, 0.0, 0.0]) DRE = np.array([10.0, 10.0, 10.0]) DW = DRE - DLE PER = np.array([0, 0, 0], "bool") dx = DW / (2**_ORDER_MAX) def test_add_particles_random(): np.random.seed(0x4D3D3D3) pos = np.random.normal(0.5, scale=0.05, size=(NPART, 3)) * (DRE - DLE) + DLE # Now convert to integers for i in range(3): np.clip(pos[:, i], DLE[i], DRE[i], pos[:, i]) # Convert to integers pos = np.floor((pos - DLE) / dx).astype("uint64") morton = get_morton_indices(pos) morton.sort() for ndom in [1, 2, 4, 8]: octree = ParticleOctreeContainer((1, 1, 1), DLE, DRE) octree.n_ref = 32 for split in np.array_split(morton, ndom): octree.add(split) octree.finalize() # This visits every oct. tc = octree.recursively_count() total_count = np.zeros(len(tc), dtype="int32") for i in sorted(tc): total_count[i] = tc[i] assert_equal(octree.nocts, total_count.sum()) # This visits every cell -- including those covered by octs. # for dom in range(ndom): # level_count += octree.count_levels(total_count.size-1, dom, mask) assert_equal(total_count, [1, 8, 64, 64, 256, 536, 1856, 1672]) class FakeDS: domain_left_edge = None domain_right_edge = None domain_width = None unit_registry = UnitRegistry() unit_registry.add("code_length", 1.0, dimensions.length) periodicity = (False, False, False) class FakeRegion: def __init__(self, nfiles, periodic=False): self.ds = FakeDS() self.ds.domain_left_edge = YTArray( [0.0, 0.0, 0.0], "code_length", registry=self.ds.unit_registry ) self.ds.domain_right_edge = YTArray( [nfiles, nfiles, nfiles], "code_length", registry=self.ds.unit_registry, dtype="float64", ) self.ds.domain_width = self.ds.domain_right_edge - self.ds.domain_left_edge self.ds.periodicity = (periodic, periodic, periodic) self.nfiles = nfiles def set_edges(self, file_id, dx=0.1): self.left_edge = YTArray( [file_id + dx, 0.0, 0.0], "code_length", registry=self.ds.unit_registry ) self.right_edge = YTArray( [file_id + 1 - dx, self.nfiles, self.nfiles], "code_length", registry=self.ds.unit_registry, ) class FakeBoxRegion: def __init__(self, nfiles, left_edge, right_edge): self.ds = FakeDS() self.ds.domain_left_edge = YTArray( left_edge, "code_length", registry=self.ds.unit_registry ) self.ds.domain_right_edge = YTArray( right_edge, "code_length", registry=self.ds.unit_registry ) self.ds.domain_width = self.ds.domain_right_edge - self.ds.domain_left_edge self.nfiles = nfiles def set_edges(self, center, width): self.left_edge = self.ds.domain_left_edge + self.ds.domain_width * ( center - width / 2 ) self.right_edge = self.ds.domain_left_edge + self.ds.domain_width * ( center + width / 2 ) def FakeBitmap( npart, nfiles, order1, order2, left_edge=None, right_edge=None, periodicity=None, decomp="sliced", buff=0.1, distrib="uniform", fname=None, ): if left_edge is None: left_edge = np.array([0.0, 0.0, 0.0]) if right_edge is None: right_edge = np.array([1.0, 1.0, 1.0]) if periodicity is None: periodicity = np.array([0, 0, 0], "bool") reg = ParticleBitmap( left_edge, right_edge, periodicity, 12345, nfiles, order1, order2 ) # Load from file if it exists if isinstance(fname, str) and os.path.isfile(fname): reg.load_bitmasks(fname) else: # Create positions for each file posgen = yield_fake_decomp( decomp, npart, nfiles, left_edge, right_edge, buff=buff, distrib=distrib ) # Coarse index max_npart = 0 for i, (pos, hsml) in enumerate(posgen): max_npart = max(max_npart, pos.shape[0]) reg._coarse_index_data_file(pos, hsml, i) reg._set_coarse_index_data_file(i) if i != (nfiles - 1): raise RuntimeError( f"There are positions for {i + 1} files, but there should be {nfiles}." ) # Refined index mask = reg.masks.sum(axis=1).astype("uint8") sub_mi1 = np.zeros(max_npart, "uint64") sub_mi2 = np.zeros(max_npart, "uint64") posgen = yield_fake_decomp( decomp, npart, nfiles, left_edge, right_edge, buff=buff, distrib=distrib ) coll = None for i, (pos, hsml) in enumerate(posgen): nsub_mi, coll = reg._refined_index_data_file( coll, pos, hsml, mask, sub_mi1, sub_mi2, i, 0, count_threshold=1, mask_threshold=2, ) reg.bitmasks.append(i, coll) # Save if file name provided if isinstance(fname, str): reg.save_bitmasks(fname) return reg def test_bitmap_no_collisions(): # Test init for slabs of points in x left_edge = np.array([0.0, 0.0, 0.0]) right_edge = np.array([1.0, 1.0, 1.0]) periodicity = np.array([0, 0, 0], "bool") npart = 100 nfiles = 2 file_hash = 12345 order1 = 2 order2 = 2 reg = ParticleBitmap( left_edge, right_edge, periodicity, file_hash, nfiles, order1, order2 ) # Coarse index posgen = yield_fake_decomp("sliced", npart, nfiles, left_edge, right_edge) max_npart = 0 for i, (pos, hsml) in enumerate(posgen): reg._coarse_index_data_file(pos, hsml, i) max_npart = max(max_npart, pos.shape[0]) reg._set_coarse_index_data_file(i) assert_equal(reg.count_total(i), np.sum(reg.masks[:, i])) mask = reg.masks.sum(axis=1).astype("uint8") ncoll = np.sum(mask > 1) nc, nm = reg.find_collisions_coarse() assert_equal(nc, 0, "%d coarse collisions" % nc) assert_equal(ncoll, nc, "%d in mask, %d in bitmap" % (ncoll, nc)) # Refined index sub_mi1 = np.zeros(max_npart, "uint64") sub_mi2 = np.zeros(max_npart, "uint64") posgen = yield_fake_decomp("sliced", npart, nfiles, left_edge, right_edge) coll = None for i, (pos, hsml) in enumerate(posgen): nsub_mi, coll = reg._refined_index_data_file( coll, pos, hsml, mask, sub_mi1, sub_mi2, i, 0, count_threshold=1, mask_threshold=2, ) reg.bitmasks.append(i, coll) assert_equal(reg.count_refined(i), 0) nr, nm = reg.find_collisions_refined() assert_equal(nr, 0, "%d collisions" % nr) def test_bitmap_collisions(): # Test init for slabs of points in x left_edge = np.array([0.0, 0.0, 0.0]) right_edge = np.array([1.0, 1.0, 1.0]) periodicity = np.array([0, 0, 0], "bool") nfiles = 2 file_hash = 12345 order1 = 2 order2 = 2 reg = ParticleBitmap( left_edge, right_edge, periodicity, file_hash, nfiles, order1, order2 ) # Use same points for all files to force collisions pos = cell_centers(order1 + order2, left_edge, right_edge) hsml = None # Coarse index max_npart = 0 for i in range(nfiles): reg._coarse_index_data_file(pos, hsml, i) max_npart = max(max_npart, pos.shape[0]) reg._set_coarse_index_data_file(i) assert_equal(reg.count_total(i), np.sum(reg.masks[:, i])) mask = reg.masks.sum(axis=1).astype("uint8") ncoll = np.sum(mask > 1) nc, nm = reg.find_collisions_coarse() assert_equal(ncoll, nc, "%d in mask, %d in bitmap" % (ncoll, nc)) assert_equal(nc, 2 ** (3 * order1), "%d coarse collisions" % nc) # Refined index sub_mi1 = np.zeros(max_npart, "uint64") sub_mi2 = np.zeros(max_npart, "uint64") for i in range(nfiles): nsub_mi, coll = reg._refined_index_data_file( None, pos, hsml, mask, sub_mi1, sub_mi2, i, 0, count_threshold=1, mask_threshold=2, ) reg.bitmasks.append(i, coll) assert_equal(reg.count_refined(i), ncoll) nr, nm = reg.find_collisions_refined() assert_equal(nr, 2 ** (3 * (order1 + order2)), "%d collisions" % nr) @requires_module("h5py") def test_bitmap_save_load(): # Test init for slabs of points in x left_edge = np.array([0.0, 0.0, 0.0]) right_edge = np.array([1.0, 1.0, 1.0]) periodicity = np.array([0, 0, 0], "bool") npart = NPART file_hash = 12345 nfiles = 32 order1 = 2 order2 = 2 fname_fmt = "temp_bitmasks{}.dat" i = 0 fname = fname_fmt.format(i) while os.path.isfile(fname): i += 1 fname = fname_fmt.format(i) # Create bitmap and save to file reg0 = FakeBitmap(npart, nfiles, order1, order2, left_edge, right_edge, periodicity) reg0.save_bitmasks(fname, 0.0) # Attempt to load bitmap reg1 = ParticleBitmap( left_edge, right_edge, periodicity, file_hash, nfiles, order1, order2 ) reg1.load_bitmasks(fname) assert reg0.iseq_bitmask(reg1) # Remove file os.remove(fname) def test_bitmap_select(): np.random.seed(0x4D3D3D3) dx = 0.1 for periodic in [False, True]: for nfiles in [2, 15, 31, 32, 33]: # Now we create particles # Note: we set order1 to log2(nfiles) here for testing purposes to # ensure no collisions order1 = int(np.ceil(np.log2(nfiles))) # Ensures zero collisions order2 = 2 # No overlap for N = nfiles exact_division = nfiles == (1 << order1) div = float(nfiles) / float(1 << order1) reg = FakeBitmap( nfiles**3, nfiles, order1, order2, decomp="grid", left_edge=np.array([0.0, 0.0, 0.0]), right_edge=np.array([nfiles, nfiles, nfiles]), periodicity=np.array([periodic, periodic, periodic]), ) # Loop over regions selecting single files fr = FakeRegion(nfiles, periodic=periodic) for i in range(nfiles): fr.set_edges(i, dx) selector = RegionSelector(fr) (df, gf), (dmask, gmask) = reg.identify_data_files(selector, ngz=1) if exact_division: assert_equal(len(df), 1, f"selector {i}, number of files") assert_equal(df[0], i, f"selector {i}, file selected") if periodic and (nfiles != 2): ans_gf = sorted([(i - 1) % nfiles, (i + 1) % nfiles]) elif i == 0: ans_gf = [i + 1] elif i == (nfiles - 1): ans_gf = [i - 1] else: ans_gf = [i - 1, i + 1] assert_equal( len(gf), len(ans_gf), f"selector {i}, number of ghost files", ) for i in range(len(gf)): assert_equal(gf[i], ans_gf[i], f"selector {i}, ghost files") else: lf_frac = np.floor(float(fr.left_edge[0]) / div) * div rf_frac = np.floor(float(fr.right_edge[0]) / div) * div # Selected files lf = int( np.floor(lf_frac) if ((lf_frac % 0.5) == 0) else np.round(lf_frac) ) rf = int( np.floor(rf_frac) if ((rf_frac % 0.5) == 0) else np.round(rf_frac) ) if (rf + 0.5) >= (rf_frac + div): rf -= 1 if (lf + 0.5) <= (lf_frac - div): lf += 1 df_ans = np.arange(max(lf, 0), min(rf + 1, nfiles)) assert_array_equal(df, df_ans, f"selector {i}, file array") # Ghost zones selected files lf_ghost = int( np.floor(lf_frac - div) if (((lf_frac - div) % 0.5) == 0) else np.round(lf_frac - div) ) rf_ghost = int( np.floor(rf_frac + div) if (((rf_frac + div) % 0.5) == 0) else np.round(rf_frac + div) ) if not periodic: lf_ghost = max(lf_ghost, 0) rf_ghost = min(rf_ghost, nfiles - 1) if (rf_ghost + 0.5) >= (rf_frac + 2 * div): rf_ghost -= 1 gf_ans = [] if lf_ghost < lf: gf_ans.append(lf_ghost % nfiles) if rf_ghost > rf: gf_ans.append(rf_ghost % nfiles) gf_ans = np.array(sorted(gf_ans)) assert_array_equal(gf, gf_ans, f"selector {i}, ghost file array") def cell_centers(order, left_edge, right_edge): ndim = left_edge.size ncells = 2**order dx = (right_edge - left_edge) / (2 * ncells) d = [ np.linspace(left_edge[i] + dx[i], right_edge[i] - dx[i], ncells) for i in range(ndim) ] dd = np.meshgrid(*d) return np.vstack([x.flatten() for x in dd]).T def fake_decomp_random(npart, nfiles, ifile, DLE, DRE, buff=0.0): np.random.seed(0x4D3D3D3 + ifile) nPF = int(npart / nfiles) nR = npart % nfiles if ifile == 0: nPF += nR pos = np.empty((nPF, 3), "float64") for i in range(3): pos[:, i] = np.random.uniform(DLE[i], DRE[i], nPF) return pos def fake_decomp_sliced(npart, nfiles, ifile, DLE, DRE, buff=0.0): np.random.seed(0x4D3D3D3 + ifile) DW = DRE - DLE div = DW / nfiles nPF = int(npart / nfiles) nR = npart % nfiles inp = nPF if ifile == 0: inp += nR iLE = DLE[0] + ifile * div[0] iRE = iLE + div[0] if ifile != 0: iLE -= buff * div[0] if ifile != (nfiles - 1): iRE += buff * div[0] pos = np.empty((inp, 3), dtype="float") pos[:, 0] = np.random.uniform(iLE, iRE, inp) for i in range(1, 3): pos[:, i] = np.random.uniform(DLE[i], DRE[i], inp) return pos def makeall_decomp_hilbert_gaussian( npart, nfiles, DLE, DRE, buff=0.0, order=6, verbose=False, fname_base=None, nchunk=10, width=None, center=None, frac_random=0.1, ): import pickle np.random.seed(0x4D3D3D3) DW = DRE - DLE if fname_base is None: fname_base = f"hilbert{order}_gaussian_np{npart}_nf{nfiles}_" if width is None: width = 0.1 * DW if center is None: center = DLE + 0.5 * DW def load_pos(file_id): filename = fname_base + f"file{file_id}" if os.path.isfile(filename): fd = open(filename, "rb") positions = pickle.load(fd) fd.close() else: positions = np.empty((0, 3), dtype="float64") return positions def save_pos(file_id, positions): filename = fname_base + f"file{file_id}" fd = open(filename, "wb") pickle.dump(positions, fd) fd.close() npart_rnd = int(frac_random * npart) npart_gau = npart - npart_rnd dim_hilbert = 1 << order nH = dim_hilbert**3 if nH < nfiles: raise ValueError("Fewer hilbert cells than files.") nHPF = nH / nfiles rHPF = nH % nfiles for ichunk in range(nchunk): inp = npart_gau / nchunk if ichunk == 0: inp += npart_gau % nchunk pos = np.empty((inp, 3), dtype="float64") ind = np.empty((inp, 3), dtype="int64") for k in range(3): pos[:, k] = np.clip( np.random.normal(center[k], width[k], inp), DLE[k], DRE[k] - (1.0e-9) * DW[k], ) ind[:, k] = (pos[:, k] - DLE[k]) / (DW[k] / dim_hilbert) harr = get_hilbert_indices(order, ind) farr = (harr - rHPF) / nHPF for ifile in range(nfiles): ipos = load_pos(ifile) if ifile == 0: idx = farr <= ifile # Put remainders in first file else: idx = farr == ifile ipos = np.concatenate((ipos, pos[idx, :]), axis=0) save_pos(ifile, ipos) # Random for ifile in range(nfiles): ipos = load_pos(ifile) ipos_rnd = fake_decomp_hilbert_uniform( npart_rnd, nfiles, ifile, DLE, DRE, buff=buff, order=order, verbose=verbose ) ipos = np.concatenate((ipos, ipos_rnd), axis=0) save_pos(ifile, ipos) def fake_decomp_hilbert_gaussian( npart, nfiles, ifile, DLE, DRE, buff=0.0, order=6, verbose=False, fname=None ): np.random.seed(0x4D3D3D3) DW = DRE - DLE dim_hilbert = 1 << order nH = dim_hilbert**3 if nH < nfiles: raise Exception("Fewer hilbert cells than files.") nHPF = nH / nfiles rHPF = nH % nfiles hdiv = DW / dim_hilbert if ifile == 0: hlist = np.arange(0, nHPF + rHPF, dtype="int64") else: hlist = np.arange(ifile * nHPF + rHPF, (ifile + 1) * nHPF + rHPF, dtype="int64") hpos = get_hilbert_points(order, hlist) iLE = np.empty((len(hlist), 3), dtype="float") iRE = np.empty((len(hlist), 3), dtype="float") count = np.zeros(3, dtype="int64") pos = np.empty((npart, 3), dtype="float") for k in range(3): iLE[:, k] = DLE[k] + hdiv[k] * hpos[:, k] iRE[:, k] = iLE[:, k] + hdiv[k] iLE[hpos[:, k] != 0, k] -= buff * hdiv[k] iRE[hpos[:, k] != (dim_hilbert - 1), k] += buff * hdiv[k] gpos = np.clip( np.random.normal(DLE[k] + DW[k] / 2.0, DW[k] / 10.0, npart), DLE[k], DRE[k] ) for ipos in gpos: for i in range(len(hlist)): if iLE[i, k] <= ipos < iRE[i, k]: pos[count[k], k] = ipos count[k] += 1 break return pos[: count.min(), :] def fake_decomp_hilbert_uniform( npart, nfiles, ifile, DLE, DRE, buff=0.0, order=6, verbose=False ): np.random.seed(0x4D3D3D3 + ifile) DW = DRE - DLE dim_hilbert = 1 << order nH = dim_hilbert**3 if nH < nfiles: raise Exception("Fewer hilbert cells than files.") nHPF = nH / nfiles rHPF = nH % nfiles nPH = npart / nH nRH = npart % nH hind = np.arange(nH, dtype="int64") hpos = get_hilbert_points(order, hind) hdiv = DW / dim_hilbert if ifile == 0: hlist = range(0, nHPF + rHPF) nptot = nPH * len(hlist) + nRH else: hlist = range(ifile * nHPF + rHPF, (ifile + 1) * nHPF + rHPF) nptot = nPH * len(hlist) pos = np.empty((nptot, 3), dtype="float") pc = 0 for i in hlist: iLE = DLE + hdiv * hpos[i, :] iRE = iLE + hdiv for k in range(3): # Don't add buffer past domain bounds if hpos[i, k] != 0: iLE[k] -= buff * hdiv[k] if hpos[i, k] != (dim_hilbert - 1): iRE[k] += buff * hdiv[k] inp = nPH if (ifile == 0) and (i == 0): inp += nRH for k in range(3): pos[pc : (pc + inp), k] = np.random.uniform(iLE[k], iRE[k], inp) pc += inp return pos def fake_decomp_morton( npart, nfiles, ifile, DLE, DRE, buff=0.0, order=6, verbose=False ): np.random.seed(0x4D3D3D3 + ifile) DW = DRE - DLE dim_morton = 1 << order nH = dim_morton**3 if nH < nfiles: raise Exception("Fewer morton cells than files.") nHPF = nH / nfiles rHPF = nH % nfiles nPH = npart / nH nRH = npart % nH hind = np.arange(nH, dtype="uint64") hpos = get_morton_points(hind) hdiv = DW / dim_morton if ifile == 0: hlist = range(0, nHPF + rHPF) nptot = nPH * len(hlist) + nRH else: hlist = range(ifile * nHPF + rHPF, (ifile + 1) * nHPF + rHPF) nptot = nPH * len(hlist) pos = np.empty((nptot, 3), dtype="float") pc = 0 for i in hlist: iLE = DLE + hdiv * hpos[i, :] iRE = iLE + hdiv for k in range(3): # Don't add buffer past domain bounds if hpos[i, k] != 0: iLE[k] -= buff * hdiv[k] if hpos[i, k] != (dim_morton - 1): iRE[k] += buff * hdiv[k] inp = nPH if (ifile == 0) and (i == 0): inp += nRH for k in range(3): pos[pc : (pc + inp), k] = np.random.uniform(iLE[k], iRE[k], inp) pc += inp return pos def fake_decomp_grid(npart, nfiles, ifile, DLE, DRE, buff=0.0, verbose=False): # TODO: handle 'remainder' particles np.random.seed(0x4D3D3D3 + ifile) DW = DRE - DLE nYZ = int(np.sqrt(npart / nfiles)) div = DW / nYZ Y, Z = np.mgrid[ DLE[1] + 0.1 * div[1] : DRE[1] - 0.1 * div[1] : nYZ * 1j, DLE[2] + 0.1 * div[2] : DRE[2] - 0.1 * div[2] : nYZ * 1j, ] X = 0.5 * div[0] * np.ones(Y.shape, dtype="float64") + div[0] * ifile pos = np.array([X.ravel(), Y.ravel(), Z.ravel()], dtype="float64").transpose() return pos def yield_fake_decomp(decomp, npart, nfiles, DLE, DRE, **kws): hsml = None for ifile in range(nfiles): yield fake_decomp(decomp, npart, nfiles, ifile, DLE, DRE, **kws), hsml def fake_decomp( decomp, npart, nfiles, ifile, DLE, DRE, distrib="uniform", fname=None, **kws ): import pickle if fname is None and distrib == "gaussian": fname = f"{decomp}6_{distrib}_np{npart}_nf{nfiles}_file{ifile}" if fname is not None and os.path.isfile(fname): fd = open(fname, "rb") pos = pickle.load(fd) fd.close() return pos if decomp.startswith("zoom_"): zoom_factor = 5 decomp_zoom = decomp.split("zoom_")[-1] zoom_npart = npart / 2 zoom_rem = npart % 2 pos1 = fake_decomp( decomp_zoom, zoom_npart + zoom_rem, nfiles, ifile, DLE, DRE, distrib=distrib, **kws, ) DLE_zoom = DLE + 0.5 * DW * (1.0 - 1.0 / float(zoom_factor)) DRE_zoom = DLE_zoom + DW / zoom_factor pos2 = fake_decomp( decomp_zoom, zoom_npart, nfiles, ifile, DLE_zoom, DRE_zoom, distrib=distrib, **kws, ) pos = np.concatenate((pos1, pos2), axis=0) elif "_" in decomp: decomp_list = decomp.split("_") decomp_np = npart / len(decomp_list) decomp_nr = npart % len(decomp_list) pos = np.empty((0, 3), dtype="float") for i, idecomp in enumerate(decomp_list): inp = decomp_np if i == 0: inp += decomp_nr ipos = fake_decomp( idecomp, inp, nfiles, ifile, DLE, DRE, distrib=distrib, **kws ) pos = np.concatenate((pos, ipos), axis=0) # A perfect grid, no overlap between files elif decomp == "grid": pos = fake_decomp_grid(npart, nfiles, ifile, DLE, DRE, **kws) # Completely random data set elif decomp == "random": if distrib == "uniform": pos = fake_decomp_random(npart, nfiles, ifile, DLE, DRE, **kws) else: raise ValueError( f"Unsupported value {distrib} for input parameter 'distrib'" ) # Each file contains a slab (part of x domain, all of y/z domain) elif decomp == "sliced": if distrib == "uniform": pos = fake_decomp_sliced(npart, nfiles, ifile, DLE, DRE, **kws) else: raise ValueError( f"Unsupported value {distrib} for input parameter 'distrib'" ) # Particles are assigned to files based on their location on a # Peano-Hilbert curve of order 6 elif decomp.startswith("hilbert"): if decomp == "hilbert": kws["order"] = 6 else: kws["order"] = int(decomp.split("hilbert")[-1]) if distrib == "uniform": pos = fake_decomp_hilbert_uniform(npart, nfiles, ifile, DLE, DRE, **kws) elif distrib == "gaussian": makeall_decomp_hilbert_gaussian( npart, nfiles, DLE, DRE, fname_base=fname.split("file")[0], **kws ) pos = fake_decomp( decomp, npart, nfiles, ifile, DLE, DRE, distrib=distrib, fname=fname, **kws, ) else: raise ValueError( f"Unsupported value {distrib} for input parameter 'distrib'" ) # Particles are assigned to files based on their location on a # Morton ordered Z-curve of order 6 elif decomp.startswith("morton"): if decomp == "morton": kws["order"] = 6 else: kws["order"] = int(decomp.split("morton")[-1]) if distrib == "uniform": pos = fake_decomp_morton(npart, nfiles, ifile, DLE, DRE, **kws) else: raise ValueError( f"Unsupported value {distrib} for input parameter 'distrib'" ) else: raise ValueError(f"Unsupported value {decomp} for input parameter 'decomp'") # Save if fname is not None: fd = open(fname, "wb") pickle.dump(pos, fd) fd.close() return pos yt-project-yt-f043ac8/yt/geometry/tests/test_pickleable_selections.py000066400000000000000000000022071510711153200262400ustar00rootroot00000000000000import pickle import numpy as np from numpy.testing import assert_equal from yt.testing import fake_particle_ds def test_pickleability(): # tests the pickleability of the selection objects. def pickle_test(sel_obj): assert_fields = sel_obj._get_state_attnames() # the attrs used in get/set state new_sel_obj = pickle.loads(pickle.dumps(sel_obj)) for attr in assert_fields: assert_equal(getattr(new_sel_obj, attr), getattr(sel_obj, attr)) # list of selection types and argument tuples for each selection type c = np.array([0.5, 0.5, 0.5]) sargs = ( ("point", (c,)), ("sphere", (c, 0.25)), ("box", (c - 0.3, c)), ("ellipsoid", (c, 0.3, 0.2, 0.1, c - 0.4, 0.2)), ("disk", (c, [1, 0, 0], 0.2, 0.2)), ("cutting", ([0.1, 0.2, -0.9], [0.5, 0.42, 0.6])), ("ortho_ray", ("z", c)), ("ray", (c, [0.1, 0.1, 0.1])), ) # load fake data ds = fake_particle_ds() for sel_type, args in sargs: sel = getattr(ds, sel_type)(*args) # instantiate this selection type pickle_test(sel.selector) # make sure it (un)pickles yt-project-yt-f043ac8/yt/geometry/unstructured_mesh_handler.py000066400000000000000000000056541510711153200250050ustar00rootroot00000000000000import os import weakref import numpy as np from yt.geometry.geometry_handler import Index, YTDataChunk from yt.utilities.lib.mesh_utilities import smallest_fwidth from yt.utilities.logger import ytLogger as mylog class UnstructuredIndex(Index): """The Index subclass for unstructured and hexahedral mesh datasets.""" _unsupported_objects = ("proj", "covering_grid", "smoothed_covering_grid") def __init__(self, ds, dataset_type): self.dataset_type = dataset_type self.dataset = weakref.proxy(ds) self.index_filename = self.dataset.parameter_filename self.directory = os.path.dirname(self.index_filename) self.float_type = np.float64 super().__init__(ds, dataset_type) def _setup_geometry(self): mylog.debug("Initializing Unstructured Mesh Geometry Handler.") self._initialize_mesh() def get_smallest_dx(self): """ Returns (in code units) the smallest cell size in the simulation. """ dx = min( smallest_fwidth( mesh.connectivity_coords, mesh.connectivity_indices, mesh._index_offset ) for mesh in self.meshes ) return dx def convert(self, unit): return self.dataset.conversion_factors[unit] def _initialize_mesh(self): raise NotImplementedError def _identify_base_chunk(self, dobj): if getattr(dobj, "_chunk_info", None) is None: dobj._chunk_info = self.meshes if getattr(dobj, "size", None) is None: dobj.size = self._count_selection(dobj) dobj._current_chunk = list(self._chunk_all(dobj))[0] def _count_selection(self, dobj, meshes=None): if meshes is None: meshes = dobj._chunk_info count = sum(m.count(dobj.selector) for m in meshes) return count def _chunk_all(self, dobj, cache=True): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) yield YTDataChunk(dobj, "all", oobjs, dobj.size, cache) def _chunk_spatial(self, dobj, ngz, sort=None, preload_fields=None): sobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) # This is where we will perform cutting of the Octree and # load-balancing. That may require a specialized selector object to # cut based on some space-filling curve index. for og in sobjs: if ngz > 0: g = og.retrieve_ghost_zones(ngz, [], smoothed=True) else: g = og size = self._count_selection(dobj, [og]) if size == 0: continue yield YTDataChunk(dobj, "spatial", [g], size) def _chunk_io(self, dobj, cache=True, local_only=False): oobjs = getattr(dobj._current_chunk, "objs", dobj._chunk_info) for subset in oobjs: s = self._count_selection(dobj, oobjs) yield YTDataChunk(dobj, "io", [subset], s, cache=cache) yt-project-yt-f043ac8/yt/geometry/vectorized_ops.h000066400000000000000000000001271510711153200223510ustar00rootroot00000000000000typedef double v4df __attribute__ ((vector_size(32))); // vector of four double floats yt-project-yt-f043ac8/yt/loaders.py000066400000000000000000002151601510711153200173160ustar00rootroot00000000000000""" This module gathers all user-facing functions with a `load_` prefix. """ import atexit import os import sys import time import types import warnings from collections.abc import Mapping from pathlib import Path from typing import TYPE_CHECKING, Any, Union, cast from urllib.parse import urlsplit import numpy as np from more_itertools import always_iterable from yt._maintenance.deprecation import ( future_positional_only, issue_deprecation_warning, ) from yt._typing import AnyFieldKey, AxisOrder, FieldKey from yt.data_objects.static_output import Dataset from yt.funcs import levenshtein_distance from yt.sample_data.api import lookup_on_disk_data from yt.utilities.decompose import decompose_array, get_psize from yt.utilities.exceptions import ( MountError, YTAmbiguousDataType, YTIllDefinedAMR, YTSimulationNotIdentified, YTUnidentifiedDataType, ) from yt.utilities.hierarchy_inspection import find_lowest_subclasses from yt.utilities.lib.misc_utilities import get_box_grids_level from yt.utilities.logger import ytLogger as mylog from yt.utilities.object_registries import ( output_type_registry, simulation_time_series_registry, ) from yt.utilities.on_demand_imports import _pooch as pooch, _ratarmount as ratarmount from yt.utilities.parallel_tools.parallel_analysis_interface import ( parallel_root_only_then_broadcast, ) if TYPE_CHECKING: from multiprocessing.connection import Connection # --- Loaders for known data formats --- # FUTURE: embedded warnings need to have their stacklevel decremented when this decorator is removed @future_positional_only({0: "fn"}, since="4.2") def load(fn: Union[str, "os.PathLike[str]"], *args, hint: str | None = None, **kwargs): """ Load a Dataset or DatasetSeries object. The data format is automatically discovered, and the exact return type is the corresponding subclass of :class:`yt.data_objects.static_output.Dataset`. A :class:`yt.data_objects.time_series.DatasetSeries` is created if the first argument is a pattern. Parameters ---------- fn : str, os.Pathlike[str] A path to the data location. This can be a file name, directory name, a glob pattern, or a url (for data types that support it). hint : str, optional Only classes whose name include a hint are considered. If loading fails with a YTAmbiguousDataType exception, this argument can be used to lift ambiguity. Hints are case insensitive. Additional arguments, if any, are passed down to the return class. Returns ------- :class:`yt.data_objects.static_output.Dataset` object If fn is a single path, create a Dataset from the appropriate subclass. :class:`yt.data_objects.time_series.DatasetSeries` If fn is a glob pattern (i.e. containing wildcards '[]?!*'), create a series. Raises ------ FileNotFoundError If fn does not match any existing file or directory. yt.utilities.exceptions.YTUnidentifiedDataType If fn matches existing files or directories with undetermined format. yt.utilities.exceptions.YTAmbiguousDataType If the data format matches more than one class of similar specialization levels. """ from importlib.metadata import entry_points from yt.frontends import _all # type: ignore [attr-defined] # noqa _input_fn = fn fn = os.path.expanduser(fn) if any(wildcard in fn for wildcard in "[]?!*"): from yt.data_objects.time_series import DatasetSeries return DatasetSeries(fn, *args, hint=hint, **kwargs) # This will raise FileNotFoundError if the path isn't matched # either in the current dir or yt.config.ytcfg['data_dir_directory'] if not fn.startswith("http"): fn = str(lookup_on_disk_data(fn)) external_frontends = entry_points(group="yt.frontends") # Ensure that external frontends are loaded for entrypoint in external_frontends: entrypoint.load() candidates: list[type[Dataset]] = [] for cls in output_type_registry.values(): if cls._is_valid(fn, *args, **kwargs): candidates.append(cls) # Filter the candidates if a hint was given if hint is not None: candidates = [c for c in candidates if hint.lower() in c.__name__.lower()] # Find only the lowest subclasses, i.e. most specialised front ends candidates = find_lowest_subclasses(candidates) if len(candidates) == 1: cls = candidates[0] if missing := cls._missing_load_requirements(): warnings.warn( f"This dataset appears to be of type {cls.__name__}, " "but the following requirements are currently missing: " f"{', '.join(missing)}\n" "Please verify your installation.", stacklevel=3, ) return cls(fn, *args, **kwargs) if len(candidates) > 1: raise YTAmbiguousDataType(_input_fn, candidates) raise YTUnidentifiedDataType(_input_fn, *args, **kwargs) def load_simulation(fn, simulation_type, find_outputs=False): """ Load a simulation time series object of the specified simulation type. Parameters ---------- fn : str, os.Pathlike, or byte (types supported by os.path.expandusers) Name of the data file or directory. simulation_type : str E.g. 'Enzo' find_outputs : bool Defaults to False Raises ------ FileNotFoundError If fn is not found. yt.utilities.exceptions.YTSimulationNotIdentified If simulation_type is unknown. """ from yt.frontends import _all # noqa fn = str(lookup_on_disk_data(fn)) try: cls = simulation_time_series_registry[simulation_type] except KeyError as e: raise YTSimulationNotIdentified(simulation_type) from e return cls(fn, find_outputs=find_outputs) # --- Loaders for generic ("stream") data --- def _sanitize_axis_order_args( geometry: str | tuple[str, AxisOrder], axis_order: AxisOrder | None ) -> tuple[str, AxisOrder | None]: # this entire function should be removed at the end of its deprecation cycle geometry_str: str if isinstance(geometry, tuple): issue_deprecation_warning( f"Received a tuple as {geometry=}\n" "Use the `axis_order` argument instead.", since="4.2", stacklevel=4, ) geometry_str, axis_order = geometry else: geometry_str = geometry return geometry_str, axis_order def load_uniform_grid( data, domain_dimensions, length_unit=None, bbox=None, nprocs=1, sim_time=0.0, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(True, True, True), geometry="cartesian", unit_system="cgs", default_species_fields=None, *, axis_order: AxisOrder | None = None, cell_widths=None, parameters=None, dataset_name: str = "UniformGridData", ): r"""Load a uniform grid of data into yt as a :class:`~yt.frontends.stream.data_structures.StreamHandler`. This should allow a uniform grid of data to be loaded directly into yt and analyzed as would any others. This comes with several caveats: * Units will be incorrect unless the unit system is explicitly specified. * Some functions may behave oddly, and parallelism will be disappointing or non-existent in most cases. * Particles may be difficult to integrate. Particle fields are detected as one-dimensional fields. Parameters ---------- data : dict This is a dict of numpy arrays, (numpy array, unit spec) tuples. Functions may also be supplied in place of numpy arrays as long as the subsequent argument nprocs is not specified to be greater than 1. Supplied functions much accepts the arguments (grid_object, field_name) and return numpy arrays. The keys to the dict are the field names. domain_dimensions : array_like This is the domain dimensions of the grid length_unit : string Unit to use for lengths. Defaults to unitless. bbox : array_like (xdim:zdim, LE:RE), optional Size of computational domain in units specified by length_unit. Defaults to a cubic unit-length domain. nprocs: integer, optional If greater than 1, will create this number of subarrays out of data sim_time : float, optional The simulation time in seconds mass_unit : string Unit to use for masses. Defaults to unitless. time_unit : string Unit to use for times. Defaults to unitless. velocity_unit : string Unit to use for velocities. Defaults to unitless. magnetic_unit : string Unit to use for magnetic fields. Defaults to unitless. periodicity : tuple of booleans Determines whether the data will be treated as periodic along each axis geometry : string (or tuple, deprecated) "cartesian", "cylindrical", "polar", "spherical", "geographic" or "spectral_cube". [DEPRECATED]: Optionally, a tuple can be provided to specify the axis ordering. For instance, to specify that the axis ordering should be z, x, y, this would be: ("cartesian", ("z", "x", "y")). The same can be done for other coordinates, for instance: ("spherical", ("theta", "phi", "r")). default_species_fields : string, optional If set, default species fields are created for H and He which also determine the mean molecular weight. Options are "ionized" and "neutral". axis_order: tuple of three strings, optional Force axis ordering, e.g. ("z", "y", "x") with cartesian geometry Otherwise use geometry-specific default ordering. cell_widths: list, optional If set, cell_widths is a list of arrays with an array for each dimension, specificing the cell spacing in that dimension. Must be consistent with the domain_dimensions. parameters: dictionary, optional Optional dictionary used to populate the dataset parameters, useful for storing dataset metadata. dataset_name: string, optional Optional string used to assign a name to the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.) Examples -------- >>> np.random.seed(int(0x4D3D3D3)) >>> bbox = np.array([[0.0, 1.0], [-1.5, 1.5], [1.0, 2.5]]) >>> arr = np.random.random((128, 128, 128)) >>> data = dict(density=arr) >>> ds = load_uniform_grid(data, arr.shape, length_unit="cm", bbox=bbox, nprocs=12) >>> dd = ds.all_data() >>> dd["gas", "density"] unyt_array([0.76017901, 0.96855994, 0.49205428, ..., 0.78798258, 0.97569432, 0.99453904], 'g/cm**3') """ from yt.frontends.stream.data_structures import ( StreamDataset, StreamDictFieldHandler, StreamHandler, ) from yt.frontends.stream.definitions import ( assign_particle_data, process_data, set_particle_types, ) from yt.frontends.stream.misc import _validate_cell_widths geometry, axis_order = _sanitize_axis_order_args(geometry, axis_order) domain_dimensions = np.array(domain_dimensions) if bbox is None: bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]], "float64") domain_left_edge = np.array(bbox[:, 0], "float64") domain_right_edge = np.array(bbox[:, 1], "float64") grid_levels = np.zeros(nprocs, dtype="int32").reshape((nprocs, 1)) # First we fix our field names, apply units to data # and check for consistency of field shapes field_units, data, number_of_particles = process_data( data, grid_dims=tuple(domain_dimensions), allow_callables=nprocs == 1 ) sfh = StreamDictFieldHandler() if number_of_particles > 0: particle_types = set_particle_types(data) # Used much further below. pdata: dict[str | FieldKey, Any] = {"number_of_particles": number_of_particles} for key in list(data.keys()): if len(data[key].shape) == 1 or key[0] == "io": field: FieldKey if not isinstance(key, tuple): field = ("io", key) mylog.debug("Reassigning '%s' to '%s'", key, field) else: key = cast(FieldKey, key) field = key sfh._additional_fields += (field,) pdata[field] = data.pop(key) else: particle_types = {} if cell_widths is not None: cell_widths = _validate_cell_widths(cell_widths, domain_dimensions) if nprocs > 1: temp = {} new_data = {} # type: ignore [var-annotated] for key in data.keys(): psize = get_psize(np.array(data[key].shape), nprocs) ( grid_left_edges, grid_right_edges, shapes, slices, grid_cell_widths, ) = decompose_array( data[key].shape, psize, bbox, cell_widths=cell_widths, ) grid_dimensions = np.array(list(shapes), dtype="int32") temp[key] = [data[key][slice] for slice in slices] cell_widths = grid_cell_widths for gid in range(nprocs): new_data[gid] = {} for key in temp.keys(): new_data[gid].update({key: temp[key][gid]}) sfh.update(new_data) del new_data, temp else: sfh.update({0: data}) grid_left_edges = domain_left_edge grid_right_edges = domain_right_edge grid_dimensions = domain_dimensions.reshape(nprocs, 3).astype("int32") if cell_widths is not None: cell_widths = [ cell_widths, ] if length_unit is None: length_unit = "code_length" if mass_unit is None: mass_unit = "code_mass" if time_unit is None: time_unit = "code_time" if velocity_unit is None: velocity_unit = "code_velocity" if magnetic_unit is None: magnetic_unit = "code_magnetic" handler = StreamHandler( grid_left_edges, grid_right_edges, grid_dimensions, grid_levels, -np.ones(nprocs, dtype="int64"), np.zeros(nprocs, dtype="int64").reshape(nprocs, 1), # particle count np.zeros(nprocs).reshape((nprocs, 1)), sfh, field_units, (length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit), particle_types=particle_types, periodicity=periodicity, cell_widths=cell_widths, parameters=parameters, ) handler.name = dataset_name # type: ignore [attr-defined] handler.domain_left_edge = domain_left_edge # type: ignore [attr-defined] handler.domain_right_edge = domain_right_edge # type: ignore [attr-defined] handler.refine_by = 2 # type: ignore [attr-defined] if np.all(domain_dimensions[1:] == 1): dimensionality = 1 elif domain_dimensions[2] == 1: dimensionality = 2 else: dimensionality = 3 handler.dimensionality = dimensionality # type: ignore [attr-defined] handler.domain_dimensions = domain_dimensions # type: ignore [attr-defined] handler.simulation_time = sim_time # type: ignore [attr-defined] handler.cosmology_simulation = 0 # type: ignore [attr-defined] sds = StreamDataset( handler, geometry=geometry, axis_order=axis_order, unit_system=unit_system, default_species_fields=default_species_fields, ) # Now figure out where the particles go if number_of_particles > 0: # This will update the stream handler too assign_particle_data(sds, pdata, bbox) return sds def load_amr_grids( grid_data, domain_dimensions, bbox=None, sim_time=0.0, length_unit=None, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(True, True, True), geometry="cartesian", refine_by=2, unit_system="cgs", default_species_fields=None, *, parameters=None, dataset_name: str = "AMRGridData", axis_order: AxisOrder | None = None, ): r"""Load a set of grids of data into yt as a :class:`~yt.frontends.stream.data_structures.StreamHandler`. This should allow a sequence of grids of varying resolution of data to be loaded directly into yt and analyzed as would any others. This comes with several caveats: * Units will be incorrect unless the unit system is explicitly specified. * Some functions may behave oddly, and parallelism will be disappointing or non-existent in most cases. * Particles may be difficult to integrate. * No consistency checks are performed on the index Parameters ---------- grid_data : list of dicts This is a list of dicts. Each dict must have entries "left_edge", "right_edge", "dimensions", "level", and then any remaining entries are assumed to be fields. Field entries must map to an NDArray *or* a function with the signature (grid_object, field_name) -> NDArray. The grid_data may also include a particle count. If no particle count is supplied, the dataset is understood to contain no particles. The grid_data will be modified in place and can't be assumed to be static. Grid data may also be supplied as a tuple of (NDarray or function, unit string) to specify the units. domain_dimensions : array_like This is the domain dimensions of the grid length_unit : string or float Unit to use for lengths. Defaults to unitless. If set to be a string, the bbox dimensions are assumed to be in the corresponding units. If set to a float, the value is a assumed to be the conversion from bbox dimensions to centimeters. mass_unit : string or float Unit to use for masses. Defaults to unitless. time_unit : string or float Unit to use for times. Defaults to unitless. velocity_unit : string or float Unit to use for velocities. Defaults to unitless. magnetic_unit : string or float Unit to use for magnetic fields. Defaults to unitless. bbox : array_like (xdim:zdim, LE:RE), optional Size of computational domain in units specified by length_unit. Defaults to a cubic unit-length domain. sim_time : float, optional The simulation time in seconds periodicity : tuple of booleans Determines whether the data will be treated as periodic along each axis geometry : string (or tuple, deprecated) "cartesian", "cylindrical", "polar", "spherical", "geographic" or "spectral_cube". [DEPRECATED]: Optionally, a tuple can be provided to specify the axis ordering. For instance, to specify that the axis ordering should be z, x, y, this would be: ("cartesian", ("z", "x", "y")). The same can be done for other coordinates, for instance: ("spherical", ("theta", "phi", "r")). refine_by : integer or list/array of integers. Specifies the refinement ratio between levels. Defaults to 2. This can be an array, in which case it specifies for each dimension. For instance, this can be used to say that some datasets have refinement of 1 in one dimension, indicating that they span the full range in that dimension. default_species_fields : string, optional If set, default species fields are created for H and He which also determine the mean molecular weight. Options are "ionized" and "neutral". parameters: dictionary, optional Optional dictionary used to populate the dataset parameters, useful for storing dataset metadata. dataset_name: string, optional Optional string used to assign a name to the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.) axis_order: tuple of three strings, optional Force axis ordering, e.g. ("z", "y", "x") with cartesian geometry Otherwise use geometry-specific default ordering. Examples -------- >>> grid_data = [ ... dict( ... left_edge=[0.0, 0.0, 0.0], ... right_edge=[1.0, 1.0, 1.0], ... level=0, ... dimensions=[32, 32, 32], ... number_of_particles=0, ... ), ... dict( ... left_edge=[0.25, 0.25, 0.25], ... right_edge=[0.75, 0.75, 0.75], ... level=1, ... dimensions=[32, 32, 32], ... number_of_particles=0, ... ), ... ] ... >>> for g in grid_data: ... g["gas", "density"] = ( ... np.random.random(g["dimensions"]) * 2 ** g["level"], ... "g/cm**3", ... ) ... >>> ds = load_amr_grids(grid_data, [32, 32, 32], length_unit=1.0) """ from yt.frontends.stream.data_structures import ( StreamDataset, StreamDictFieldHandler, StreamHandler, ) from yt.frontends.stream.definitions import process_data, set_particle_types geometry, axis_order = _sanitize_axis_order_args(geometry, axis_order) domain_dimensions = np.array(domain_dimensions) ngrids = len(grid_data) if bbox is None: bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]], "float64") domain_left_edge = np.array(bbox[:, 0], "float64") domain_right_edge = np.array(bbox[:, 1], "float64") grid_levels = np.zeros((ngrids, 1), dtype="int32") grid_left_edges = np.zeros((ngrids, 3), dtype="float64") grid_right_edges = np.zeros((ngrids, 3), dtype="float64") grid_dimensions = np.zeros((ngrids, 3), dtype="int32") number_of_particles = np.zeros((ngrids, 1), dtype="int64") parent_ids = np.zeros(ngrids, dtype="int64") - 1 sfh = StreamDictFieldHandler() for i, g in enumerate(grid_data): grid_left_edges[i, :] = g.pop("left_edge") grid_right_edges[i, :] = g.pop("right_edge") grid_dimensions[i, :] = g.pop("dimensions") grid_levels[i, :] = g.pop("level") field_units, data, n_particles = process_data( g, grid_dims=tuple(grid_dimensions[i, :]) ) number_of_particles[i, :] = n_particles sfh[i] = data # We now reconstruct our parent ids, so that our particle assignment can # proceed. mask = np.empty(ngrids, dtype="int32") for gi in range(ngrids): get_box_grids_level( grid_left_edges[gi, :], grid_right_edges[gi, :], grid_levels[gi].item() + 1, grid_left_edges, grid_right_edges, grid_levels, mask, ) ids = np.where(mask.astype("bool")) for ci in ids: parent_ids[ci] = gi # Check if the grid structure is properly aligned (bug #1295) for lvl in range(grid_levels.min() + 1, grid_levels.max() + 1): idx = grid_levels.flatten() == lvl dims = domain_dimensions * refine_by ** (lvl - 1) for iax, ax in enumerate("xyz"): cell_edges = np.linspace( domain_left_edge[iax], domain_right_edge[iax], dims[iax], endpoint=False ) if set(grid_left_edges[idx, iax]) - set(cell_edges): raise YTIllDefinedAMR(lvl, ax) if length_unit is None: length_unit = "code_length" if mass_unit is None: mass_unit = "code_mass" if time_unit is None: time_unit = "code_time" if velocity_unit is None: velocity_unit = "code_velocity" if magnetic_unit is None: magnetic_unit = "code_magnetic" particle_types = {} for grid in sfh.values(): particle_types.update(set_particle_types(grid)) handler = StreamHandler( grid_left_edges, grid_right_edges, grid_dimensions, grid_levels, parent_ids, number_of_particles, np.zeros(ngrids).reshape((ngrids, 1)), sfh, field_units, (length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit), particle_types=particle_types, periodicity=periodicity, parameters=parameters, ) handler.name = dataset_name # type: ignore [attr-defined] handler.domain_left_edge = domain_left_edge # type: ignore [attr-defined] handler.domain_right_edge = domain_right_edge # type: ignore [attr-defined] handler.refine_by = refine_by # type: ignore [attr-defined] if np.all(domain_dimensions[1:] == 1): dimensionality = 1 elif domain_dimensions[2] == 1: dimensionality = 2 else: dimensionality = 3 handler.dimensionality = dimensionality # type: ignore [attr-defined] handler.domain_dimensions = domain_dimensions # type: ignore [attr-defined] handler.simulation_time = sim_time # type: ignore [attr-defined] handler.cosmology_simulation = 0 # type: ignore [attr-defined] sds = StreamDataset( handler, geometry=geometry, axis_order=axis_order, unit_system=unit_system, default_species_fields=default_species_fields, ) return sds def load_particles( data: Mapping[AnyFieldKey, np.ndarray | tuple[np.ndarray, str]], length_unit=None, bbox=None, sim_time=None, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(True, True, True), geometry="cartesian", unit_system="cgs", data_source=None, default_species_fields=None, *, axis_order: AxisOrder | None = None, parameters=None, dataset_name: str = "ParticleData", ): r"""Load a set of particles into yt as a :class:`~yt.frontends.stream.data_structures.StreamParticleHandler`. This will allow a collection of particle data to be loaded directly into yt and analyzed as would any others. This comes with several caveats: * There must be sufficient space in memory to contain all the particle data. * Parallelism will be disappointing or non-existent in most cases. * Fluid fields are not supported. Note: in order for the dataset to take advantage of SPH functionality, the following two fields must be provided: * ('io', 'density') * ('io', 'smoothing_length') Parameters ---------- data : dict This is a dict of numpy arrays or (numpy array, unit name) tuples, where the keys are the field names. Particles positions must be named "particle_position_x", "particle_position_y", and "particle_position_z". length_unit : float Conversion factor from simulation length units to centimeters bbox : array_like (xdim:zdim, LE:RE), optional Size of computational domain in units of the length_unit sim_time : float, optional The simulation time in seconds mass_unit : float Conversion factor from simulation mass units to grams time_unit : float Conversion factor from simulation time units to seconds velocity_unit : float Conversion factor from simulation velocity units to cm/s magnetic_unit : float Conversion factor from simulation magnetic units to gauss periodicity : tuple of booleans Determines whether the data will be treated as periodic along each axis geometry : string (or tuple, deprecated) "cartesian", "cylindrical", "polar", "spherical", "geographic" or "spectral_cube". data_source : YTSelectionContainer, optional If set, parameters like `bbox`, `sim_time`, and code units are derived from it. default_species_fields : string, optional If set, default species fields are created for H and He which also determine the mean molecular weight. Options are "ionized" and "neutral". axis_order: tuple of three strings, optional Force axis ordering, e.g. ("z", "y", "x") with cartesian geometry Otherwise use geometry-specific default ordering. parameters: dictionary, optional Optional dictionary used to populate the dataset parameters, useful for storing dataset metadata. dataset_name: string, optional Optional string used to assign a name to the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.) Examples -------- >>> pos = [np.random.random(128 * 128 * 128) for i in range(3)] >>> data = dict( ... particle_position_x=pos[0], ... particle_position_y=pos[1], ... particle_position_z=pos[2], ... ) >>> bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) >>> ds = load_particles(data, 3.08e24, bbox=bbox) """ from yt.frontends.stream.data_structures import ( StreamDictFieldHandler, StreamHandler, StreamParticlesDataset, ) from yt.frontends.stream.definitions import process_data, set_particle_types domain_dimensions = np.ones(3, "int32") nprocs = 1 # Parse bounding box if data_source is not None: le, re = data_source.get_bbox() le = le.to_value("code_length") re = re.to_value("code_length") bbox = list(zip(le, re, strict=True)) if bbox is None: bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]], "float64") else: bbox = np.array(bbox) domain_left_edge = np.array(bbox[:, 0], "float64") domain_right_edge = np.array(bbox[:, 1], "float64") grid_levels = np.zeros(nprocs, dtype="int32").reshape((nprocs, 1)) # Parse simulation time if data_source is not None: sim_time = data_source.ds.current_time if sim_time is None: sim_time = 0.0 else: sim_time = float(sim_time) # Parse units def parse_unit(unit, dimension): if unit is None: unit = "code_" + dimension if data_source is not None: unit = getattr(data_source.ds, dimension + "_unit", unit) return unit length_unit = parse_unit(length_unit, "length") mass_unit = parse_unit(mass_unit, "mass") time_unit = parse_unit(time_unit, "time") velocity_unit = parse_unit(velocity_unit, "velocity") magnetic_unit = parse_unit(magnetic_unit, "magnetic") # Preprocess data field_units, data, _ = process_data(data) sfh = StreamDictFieldHandler() pdata: dict[AnyFieldKey, np.ndarray | tuple[np.ndarray, str]] = {} for key in data.keys(): field: FieldKey if not isinstance(key, tuple): field = ("io", key) mylog.debug("Reassigning '%s' to '%s'", key, field) else: field = key pdata[field] = data[key] sfh._additional_fields += (field,) data = pdata # Drop reference count particle_types = set_particle_types(data) sfh.update({"stream_file": data}) grid_left_edges = domain_left_edge grid_right_edges = domain_right_edge grid_dimensions = domain_dimensions.reshape(nprocs, 3).astype("int32") # I'm not sure we need any of this. handler = StreamHandler( grid_left_edges, grid_right_edges, grid_dimensions, grid_levels, -np.ones(nprocs, dtype="int64"), np.zeros(nprocs, dtype="int64").reshape(nprocs, 1), # Temporary np.zeros(nprocs).reshape((nprocs, 1)), sfh, field_units, (length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit), particle_types=particle_types, periodicity=periodicity, parameters=parameters, ) handler.name = dataset_name # type: ignore [attr-defined] handler.domain_left_edge = domain_left_edge # type: ignore [attr-defined] handler.domain_right_edge = domain_right_edge # type: ignore [attr-defined] handler.refine_by = 2 # type: ignore [attr-defined] handler.dimensionality = 3 # type: ignore [attr-defined] handler.domain_dimensions = domain_dimensions # type: ignore [attr-defined] handler.simulation_time = sim_time # type: ignore [attr-defined] handler.cosmology_simulation = 0 # type: ignore [attr-defined] sds = StreamParticlesDataset( handler, geometry=geometry, unit_system=unit_system, default_species_fields=default_species_fields, axis_order=axis_order, ) return sds def load_hexahedral_mesh( data, connectivity, coordinates, length_unit=None, bbox=None, sim_time=0.0, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(True, True, True), geometry="cartesian", unit_system="cgs", *, axis_order: AxisOrder | None = None, parameters=None, dataset_name: str = "HexahedralMeshData", ): r"""Load a hexahedral mesh of data into yt as a :class:`~yt.frontends.stream.data_structures.StreamHandler`. This should allow a semistructured grid of data to be loaded directly into yt and analyzed as would any others. This comes with several caveats: * Units will be incorrect unless the data has already been converted to cgs. * Some functions may behave oddly, and parallelism will be disappointing or non-existent in most cases. * Particles may be difficult to integrate. Particle fields are detected as one-dimensional fields. The number of particles is set by the "number_of_particles" key in data. Parameters ---------- data : dict This is a dict of numpy arrays, where the keys are the field names. There must only be one. Note that the data in the numpy arrays should define the cell-averaged value for of the quantity in in the hexahedral cell. connectivity : array_like This should be of size (N,8) where N is the number of zones. coordinates : array_like This should be of size (M,3) where M is the number of vertices indicated in the connectivity matrix. bbox : array_like (xdim:zdim, LE:RE), optional Size of computational domain in units of the length unit. sim_time : float, optional The simulation time in seconds mass_unit : string Unit to use for masses. Defaults to unitless. time_unit : string Unit to use for times. Defaults to unitless. velocity_unit : string Unit to use for velocities. Defaults to unitless. magnetic_unit : string Unit to use for magnetic fields. Defaults to unitless. periodicity : tuple of booleans Determines whether the data will be treated as periodic along each axis geometry : string (or tuple, deprecated) "cartesian", "cylindrical", "polar", "spherical", "geographic" or "spectral_cube". [DEPRECATED]: Optionally, a tuple can be provided to specify the axis ordering. For instance, to specify that the axis ordering should be z, x, y, this would be: ("cartesian", ("z", "x", "y")). The same can be done for other coordinates, for instance: ("spherical", ("theta", "phi", "r")). axis_order: tuple of three strings, optional Force axis ordering, e.g. ("z", "y", "x") with cartesian geometry Otherwise use geometry-specific default ordering. parameters: dictionary, optional Optional dictionary used to populate the dataset parameters, useful for storing dataset metadata. dataset_name: string, optional Optional string used to assign a name to the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.) """ from yt.frontends.stream.data_structures import ( StreamDictFieldHandler, StreamHandler, StreamHexahedralDataset, ) from yt.frontends.stream.definitions import process_data, set_particle_types geometry, axis_order = _sanitize_axis_order_args(geometry, axis_order) domain_dimensions = np.ones(3, "int32") * 2 nprocs = 1 if bbox is None: bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]], "float64") domain_left_edge = np.array(bbox[:, 0], "float64") domain_right_edge = np.array(bbox[:, 1], "float64") grid_levels = np.zeros(nprocs, dtype="int32").reshape((nprocs, 1)) field_units, data, _ = process_data(data) sfh = StreamDictFieldHandler() particle_types = set_particle_types(data) sfh.update({"connectivity": connectivity, "coordinates": coordinates, 0: data}) # Simple check for axis length correctness if len(data) > 0: fn = sorted(data)[0] array_values = data[fn] if array_values.size != connectivity.shape[0]: mylog.error( "Dimensions of array must be one fewer than the coordinate set." ) raise RuntimeError grid_left_edges = domain_left_edge grid_right_edges = domain_right_edge grid_dimensions = domain_dimensions.reshape(nprocs, 3).astype("int32") if length_unit is None: length_unit = "code_length" if mass_unit is None: mass_unit = "code_mass" if time_unit is None: time_unit = "code_time" if velocity_unit is None: velocity_unit = "code_velocity" if magnetic_unit is None: magnetic_unit = "code_magnetic" # I'm not sure we need any of this. handler = StreamHandler( grid_left_edges, grid_right_edges, grid_dimensions, grid_levels, -np.ones(nprocs, dtype="int64"), np.zeros(nprocs, dtype="int64").reshape(nprocs, 1), # Temporary np.zeros(nprocs).reshape((nprocs, 1)), sfh, field_units, (length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit), particle_types=particle_types, periodicity=periodicity, parameters=parameters, ) handler.name = dataset_name # type: ignore [attr-defined] handler.domain_left_edge = domain_left_edge # type: ignore [attr-defined] handler.domain_right_edge = domain_right_edge # type: ignore [attr-defined] handler.refine_by = 2 # type: ignore [attr-defined] handler.dimensionality = 3 # type: ignore [attr-defined] handler.domain_dimensions = domain_dimensions # type: ignore [attr-defined] handler.simulation_time = sim_time # type: ignore [attr-defined] handler.cosmology_simulation = 0 # type: ignore [attr-defined] sds = StreamHexahedralDataset( handler, geometry=geometry, axis_order=axis_order, unit_system=unit_system ) return sds def load_octree( octree_mask, data, bbox=None, sim_time=0.0, length_unit=None, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(True, True, True), over_refine_factor=None, num_zones=2, partial_coverage=1, unit_system="cgs", default_species_fields=None, *, parameters=None, domain_dimensions=None, dataset_name: str = "OctreeData", ): r"""Load an octree mask into yt. Octrees can be saved out by calling save_octree on an OctreeContainer. This enables them to be loaded back in. This will initialize an Octree of data. Note that fluid fields will not work yet, or possibly ever. Parameters ---------- octree_mask : np.ndarray[uint8_t] This is a depth-first refinement mask for an Octree. It should be of size n_octs * 8 (but see note about the root oct below), where each item is 1 for an oct-cell being refined and 0 for it not being refined. For num_zones != 2, the children count will still be 8, so there will still be n_octs * 8 entries. Note that if the root oct is not refined, there will be only one entry for the root, so the size of the mask will be (n_octs - 1)*8 + 1. data : dict A dictionary of 1D arrays. Note that these must of the size of the number of "False" values in the ``octree_mask``. bbox : array_like (xdim:zdim, LE:RE), optional Size of computational domain in units of length sim_time : float, optional The simulation time in seconds length_unit : string Unit to use for lengths. Defaults to unitless. mass_unit : string Unit to use for masses. Defaults to unitless. time_unit : string Unit to use for times. Defaults to unitless. velocity_unit : string Unit to use for velocities. Defaults to unitless. magnetic_unit : string Unit to use for magnetic fields. Defaults to unitless. periodicity : tuple of booleans Determines whether the data will be treated as periodic along each axis partial_coverage : boolean Whether or not an oct can be refined cell-by-cell, or whether all 8 get refined. default_species_fields : string, optional If set, default species fields are created for H and He which also determine the mean molecular weight. Options are "ionized" and "neutral". num_zones : int The number of zones along each dimension in an oct parameters: dictionary, optional Optional dictionary used to populate the dataset parameters, useful for storing dataset metadata. domain_dimensions : 3 elements array-like, optional This is the domain dimensions of the root *mesh*, which can be used to specify (indirectly) the number of root oct nodes. dataset_name : string, optional Optional string used to assign a name to the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.) Example ------- >>> import numpy as np >>> oct_mask = np.zeros(33) # 5 refined values gives 7 * 4 + 5 octs to mask ... oct_mask[[0, 5, 7, 16]] = 8 >>> octree_mask = np.array(oct_mask, dtype=np.uint8) >>> quantities = {} >>> quantities["gas", "density"] = np.random.random((29, 1)) # num of false's >>> bbox = np.array([[-10.0, 10.0], [-10.0, 10.0], [-10.0, 10.0]]) >>> ds = load_octree( ... octree_mask=octree_mask, ... data=quantities, ... bbox=bbox, ... num_zones=1, ... partial_coverage=0, ... ) """ from yt.frontends.stream.data_structures import ( StreamDictFieldHandler, StreamHandler, StreamOctreeDataset, ) from yt.frontends.stream.definitions import process_data, set_particle_types if not isinstance(octree_mask, np.ndarray) or octree_mask.dtype != np.uint8: raise TypeError("octree_mask should be a Numpy array with type uint8") nz = num_zones # for compatibility if over_refine_factor is not None: nz = 1 << over_refine_factor if domain_dimensions is None: # We assume that if it isn't specified, it defaults to the number of # zones (i.e., a single root oct.) domain_dimensions = np.array([nz, nz, nz]) else: domain_dimensions = np.array(domain_dimensions) nprocs = 1 if bbox is None: bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]], "float64") domain_left_edge = np.array(bbox[:, 0], "float64") domain_right_edge = np.array(bbox[:, 1], "float64") grid_levels = np.zeros(nprocs, dtype="int32").reshape((nprocs, 1)) field_units, data, _ = process_data(data) sfh = StreamDictFieldHandler() particle_types = set_particle_types(data) sfh.update({0: data}) grid_left_edges = domain_left_edge grid_right_edges = domain_right_edge grid_dimensions = domain_dimensions.reshape(nprocs, 3).astype("int32") if length_unit is None: length_unit = "code_length" if mass_unit is None: mass_unit = "code_mass" if time_unit is None: time_unit = "code_time" if velocity_unit is None: velocity_unit = "code_velocity" if magnetic_unit is None: magnetic_unit = "code_magnetic" # I'm not sure we need any of this. handler = StreamHandler( grid_left_edges, grid_right_edges, grid_dimensions, grid_levels, -np.ones(nprocs, dtype="int64"), np.zeros(nprocs, dtype="int64").reshape(nprocs, 1), # Temporary np.zeros(nprocs).reshape((nprocs, 1)), sfh, field_units, (length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit), particle_types=particle_types, periodicity=periodicity, parameters=parameters, ) handler.name = dataset_name # type: ignore [attr-defined] handler.domain_left_edge = domain_left_edge # type: ignore [attr-defined] handler.domain_right_edge = domain_right_edge # type: ignore [attr-defined] handler.refine_by = 2 # type: ignore [attr-defined] handler.dimensionality = 3 # type: ignore [attr-defined] handler.domain_dimensions = domain_dimensions # type: ignore [attr-defined] handler.simulation_time = sim_time # type: ignore [attr-defined] handler.cosmology_simulation = 0 # type: ignore [attr-defined] sds = StreamOctreeDataset( handler, unit_system=unit_system, default_species_fields=default_species_fields ) sds.octree_mask = octree_mask # type: ignore [attr-defined] sds.partial_coverage = partial_coverage # type: ignore [attr-defined] sds.num_zones = num_zones # type: ignore [attr-defined] return sds def load_unstructured_mesh( connectivity, coordinates, node_data=None, elem_data=None, length_unit=None, bbox=None, sim_time=0.0, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(False, False, False), geometry="cartesian", unit_system="cgs", *, axis_order: AxisOrder | None = None, parameters=None, dataset_name: str = "UnstructuredMeshData", ): r"""Load an unstructured mesh of data into yt as a :class:`~yt.frontends.stream.data_structures.StreamHandler`. This should allow an unstructured mesh data to be loaded directly into yt and analyzed as would any others. Not all functionality for visualization will be present, and some analysis functions may not yet have been implemented. Particle fields are detected as one-dimensional fields. The number of particles is set by the "number_of_particles" key in data. In the parameter descriptions below, a "vertex" is a 3D point in space, an "element" is a single polyhedron whose location is defined by a set of vertices, and a "mesh" is a set of polyhedral elements, each with the same number of vertices. Parameters ---------- connectivity : list of array_like or array_like This should either be a single 2D array or list of 2D arrays. If this is a list, each element in the list corresponds to the connectivity information for a distinct mesh. Each array can have different connectivity length and should be of shape (N,M) where N is the number of elements and M is the number of vertices per element. coordinates : array_like The 3D coordinates of mesh vertices. This should be of size (L, D) where L is the number of vertices and D is the number of coordinates per vertex (the spatial dimensions of the dataset). Currently this must be either 2 or 3. When loading more than one mesh, the data for each mesh should be concatenated into a single coordinates array. node_data : dict or list of dicts For a single mesh, a dict mapping field names to 2D numpy arrays, representing data defined at element vertices. For multiple meshes, this must be a list of dicts. Note that these are not the values as a function of the coordinates, but of the connectivity. Their shape should be the same as the connectivity. This means that if the data is in the shape of the coordinates, you may need to reshape them using the `connectivity` array as an index. elem_data : dict or list of dicts For a single mesh, a dict mapping field names to 1D numpy arrays, where each array has a length equal to the number of elements. The data must be defined at the center of each mesh element and there must be only one data value for each element. For multiple meshes, this must be a list of dicts, with one dict for each mesh. bbox : array_like (xdim:zdim, LE:RE), optional Size of computational domain in units of the length unit. sim_time : float, optional The simulation time in seconds length_unit : string Unit to use for length. Defaults to unitless. mass_unit : string Unit to use for masses. Defaults to unitless. time_unit : string Unit to use for times. Defaults to unitless. velocity_unit : string Unit to use for velocities. Defaults to unitless. magnetic_unit : string Unit to use for magnetic fields. Defaults to unitless. periodicity : tuple of booleans Determines whether the data will be treated as periodic along each axis geometry : string (or tuple, deprecated) "cartesian", "cylindrical", "polar", "spherical", "geographic" or "spectral_cube". [DEPRECATED]: Optionally, a tuple can be provided to specify the axis ordering. For instance, to specify that the axis ordering should be z, x, y, this would be: ("cartesian", ("z", "x", "y")). The same can be done for other coordinates, for instance: ("spherical", ("theta", "phi", "r")). axis_order: tuple of three strings, optional Force axis ordering, e.g. ("z", "y", "x") with cartesian geometry Otherwise use geometry-specific default ordering. parameters: dictionary, optional Optional dictionary used to populate the dataset parameters, useful for storing dataset metadata. dataset_name: string, optional Optional string used to assign a name to the dataset. Stream datasets will use this value in place of a filename (in image prefixing, etc.) Examples -------- Load a simple mesh consisting of two tets. >>> # Coordinates for vertices of two tetrahedra >>> coordinates = np.array( ... [ ... [0.0, 0.0, 0.5], ... [0.0, 1.0, 0.5], ... [0.5, 1, 0.5], ... [0.5, 0.5, 0.0], ... [0.5, 0.5, 1.0], ... ] ... ) >>> # The indices in the coordinates array of mesh vertices. >>> # This mesh has two elements. >>> connectivity = np.array([[0, 1, 2, 4], [0, 1, 2, 3]]) >>> # Field data defined at the centers of the two mesh elements. >>> elem_data = {("connect1", "elem_field"): np.array([1, 2])} >>> # Field data defined at node vertices >>> node_data = { ... ("connect1", "node_field"): np.array( ... [[0.0, 1.0, 2.0, 4.0], [0.0, 1.0, 2.0, 3.0]] ... ) ... } >>> ds = load_unstructured_mesh( ... connectivity, coordinates, elem_data=elem_data, node_data=node_data ... ) """ from yt.frontends.exodus_ii.util import get_num_pseudo_dims from yt.frontends.stream.data_structures import ( StreamDictFieldHandler, StreamHandler, StreamUnstructuredMeshDataset, ) from yt.frontends.stream.definitions import process_data, set_particle_types geometry, axis_order = _sanitize_axis_order_args(geometry, axis_order) dimensionality = coordinates.shape[1] domain_dimensions = np.ones(3, "int32") * 2 nprocs = 1 if elem_data is None and node_data is None: raise RuntimeError("No data supplied in load_unstructured_mesh.") connectivity = list(always_iterable(connectivity, base_type=np.ndarray)) num_meshes = max(1, len(connectivity)) elem_data = list(always_iterable(elem_data, base_type=dict)) or [{}] * num_meshes node_data = list(always_iterable(node_data, base_type=dict)) or [{}] * num_meshes data = [{} for i in range(num_meshes)] # type: ignore [var-annotated] for elem_dict, data_dict in zip(elem_data, data, strict=True): for field, values in elem_dict.items(): data_dict[field] = values for node_dict, data_dict in zip(node_data, data, strict=True): for field, values in node_dict.items(): data_dict[field] = values if bbox is None: bbox = [ [ coordinates[:, i].min() - 0.1 * abs(coordinates[:, i].min()), coordinates[:, i].max() + 0.1 * abs(coordinates[:, i].max()), ] for i in range(dimensionality) ] if dimensionality < 3: bbox.append([0.0, 1.0]) if dimensionality < 2: bbox.append([0.0, 1.0]) # handle pseudo-dims here num_pseudo_dims = get_num_pseudo_dims(coordinates) dimensionality -= num_pseudo_dims for i in range(dimensionality, 3): bbox[i][0] = 0.0 bbox[i][1] = 1.0 bbox = np.array(bbox, dtype=np.float64) domain_left_edge = np.array(bbox[:, 0], "float64") domain_right_edge = np.array(bbox[:, 1], "float64") grid_levels = np.zeros(nprocs, dtype="int32").reshape((nprocs, 1)) field_units = {} particle_types = {} sfh = StreamDictFieldHandler() sfh.update({"connectivity": connectivity, "coordinates": coordinates}) for i, d in enumerate(data): _f_unit, _data, _ = process_data(d) field_units.update(_f_unit) sfh[i] = _data particle_types.update(set_particle_types(_data)) grid_left_edges = domain_left_edge grid_right_edges = domain_right_edge grid_dimensions = domain_dimensions.reshape(nprocs, 3).astype("int32") if length_unit is None: length_unit = "code_length" if mass_unit is None: mass_unit = "code_mass" if time_unit is None: time_unit = "code_time" if velocity_unit is None: velocity_unit = "code_velocity" if magnetic_unit is None: magnetic_unit = "code_magnetic" # I'm not sure we need any of this. handler = StreamHandler( grid_left_edges, grid_right_edges, grid_dimensions, grid_levels, -np.ones(nprocs, dtype="int64"), np.zeros(nprocs, dtype="int64").reshape(nprocs, 1), # Temporary np.zeros(nprocs).reshape((nprocs, 1)), sfh, field_units, (length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit), particle_types=particle_types, periodicity=periodicity, parameters=parameters, ) handler.name = dataset_name # type: ignore [attr-defined] handler.domain_left_edge = domain_left_edge # type: ignore [attr-defined] handler.domain_right_edge = domain_right_edge # type: ignore [attr-defined] handler.refine_by = 2 # type: ignore [attr-defined] handler.dimensionality = dimensionality # type: ignore [attr-defined] handler.domain_dimensions = domain_dimensions # type: ignore [attr-defined] handler.simulation_time = sim_time # type: ignore [attr-defined] handler.cosmology_simulation = 0 # type: ignore [attr-defined] sds = StreamUnstructuredMeshDataset( handler, geometry=geometry, axis_order=axis_order, unit_system=unit_system ) fluid_types = ["all"] for i in range(1, num_meshes + 1): fluid_types += ["connect%d" % i] sds.fluid_types = tuple(fluid_types) def flatten(l): return [item for sublist in l for item in sublist] sds._node_fields = flatten([[f[1] for f in m] for m in node_data if m]) # type: ignore [attr-defined] sds._elem_fields = flatten([[f[1] for f in m] for m in elem_data if m]) # type: ignore [attr-defined] sds.default_field = [f for f in sds.field_list if f[0] == "connect1"][-1] sds.default_fluid_type = sds.default_field[0] return sds # --- Loader for yt sample datasets --- @parallel_root_only_then_broadcast def _get_sample_data( fn: str | None = None, *, progressbar: bool = True, timeout=None, **kwargs ): # this isolates all the filename management and downloading so that it # can be restricted to a single process if running in parallel. Returns # the loadable_path as well as the kwargs dictionary, which is modified # by this function (note that the kwargs are returned explicitly rather than # relying on in-place modification so that the updated kwargs can be # broadcast to other processes during parallel execution). import tarfile from yt.sample_data.api import ( _download_sample_data_file, _get_test_data_dir_path, get_data_registry_table, get_download_cache_dir, ) pooch_logger = pooch.utils.get_logger() # normalize path for platform portability # for consistency with yt.load, we also convert to str explicitly, # which gives us support Path objects for free fn = str(fn).replace("/", os.path.sep) topdir, _, specific_file = fn.partition(os.path.sep) registry_table = get_data_registry_table() known_names: list[str] = registry_table.dropna()["filename"].to_list() if topdir not in known_names: msg = f"'{topdir}' is not an available dataset." lexical_distances: list[tuple[str, int]] = [ (name, levenshtein_distance(name, topdir)) for name in known_names ] suggestions: list[str] = [name for name, dist in lexical_distances if dist < 4] if len(suggestions) == 1: msg += f" Did you mean '{suggestions[0]}' ?" elif suggestions: msg += " Did you mean to type any of the following ?\n\n " msg += "\n ".join(f"'{_}'" for _ in suggestions) raise ValueError(msg) # PR 3089 # note: in the future the registry table should be reindexed # so that the following line can be replaced with # # specs = registry_table.loc[fn] # # however we don't want to do it right now because the "filename" column is # currently incomplete specs = registry_table.query(f"`filename` == '{topdir}'").iloc[0] load_name = specific_file or specs["load_name"] or "" if not isinstance(specs["load_kwargs"], dict): raise ValueError( "The requested dataset seems to be improperly registered.\n" "Tip: the entry in yt/sample_data_registry.json may be inconsistent with " "https://github.com/yt-project/website/blob/master/data/datafiles.json\n" "Please report this to https://github.com/yt-project/yt/issues/new" ) load_kwargs = {**specs["load_kwargs"], **kwargs} save_dir = _get_test_data_dir_path() data_path = save_dir.joinpath(fn) if save_dir.joinpath(topdir).exists(): # if the data is already available locally, `load_sample` # only acts as a thin wrapper around `load` if load_name and os.sep not in fn: data_path = data_path.joinpath(load_name) mylog.info("Sample dataset found in '%s'", data_path) if timeout is not None: mylog.info("Ignoring the `timeout` keyword argument received.") return data_path, load_kwargs mylog.info("'%s' is not available locally. Looking up online.", fn) # effectively silence the pooch's logger and create our own log instead pooch_logger.setLevel(100) mylog.info("Downloading from %s", specs["url"]) # downloading via a pooch.Pooch instance behind the scenes filename = urlsplit(specs["url"]).path.split("/")[-1] tmp_file = _download_sample_data_file( filename, progressbar=progressbar, timeout=timeout ) # pooch has functionalities to unpack downloaded archive files, # but it needs to be told in advance that we are downloading a tarball. # Since that information is not necessarily trivial to guess from the filename, # we rely on the standard library to perform a conditional unpacking instead. if tarfile.is_tarfile(tmp_file): mylog.info("Untaring downloaded file to '%s'", save_dir) with tarfile.open(tmp_file) as fh: def is_within_directory(directory, target): abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) prefix = os.path.commonprefix([abs_directory, abs_target]) return prefix == abs_directory def safe_extract(tar, path=".", members=None, *, numeric_owner=False): for member in tar.getmembers(): member_path = os.path.join(path, member.name) if not is_within_directory(path, member_path): raise Exception("Attempted Path Traversal in Tar File") if sys.version_info >= (3, 12): # the filter argument is new in Python 3.12, but not specifying it # explicitly raises a deprecation warning on 3.12 and 3.13 extractall_kwargs = {"filter": "data"} else: extractall_kwargs = {} tar.extractall( path, members, numeric_owner=numeric_owner, **extractall_kwargs ) safe_extract(fh, save_dir) os.remove(tmp_file) else: os.replace(tmp_file, os.path.join(save_dir, fn)) loadable_path = Path.joinpath(save_dir, fn) if load_name not in str(loadable_path): loadable_path = loadable_path.joinpath(load_name, specific_file) try: # clean cache dir get_download_cache_dir().rmdir() except OSError: # cache dir isn't empty pass return loadable_path, load_kwargs def load_sample( fn: str | None = None, *, progressbar: bool = True, timeout=None, **kwargs ): r""" Load sample data with yt. This is a simple wrapper around :func:`~yt.loaders.load` to include fetching data with pooch from remote source. The data registry table can be retrieved and visualized using :func:`~yt.sample_data.api.get_data_registry_table`. The `filename` column contains usable keys that can be passed as the first positional argument to load_sample. Some data samples contain series of datasets. It may be required to supply the relative path to a specific dataset. Parameters ---------- fn: str The `filename` of the dataset to load, as defined in the data registry table. progressbar: bool display a progress bar (tqdm). timeout: float or int (optional) Maximal waiting time, in seconds, after which download is aborted. `None` means "no limit". This parameter is directly passed to down to requests.get via pooch.HTTPDownloader Notes ----- - This function is experimental as of yt 4.0.0, do not rely on its exact behaviour. - Any additional keyword argument is passed down to :func:`~yt.loaders.load`. - In case of collision with predefined keyword arguments as set in the data registry, the ones passed to this function take priority. - Datasets with slashes '/' in their names can safely be used even on Windows. On the contrary, paths using backslashes '\' won't work outside of Windows, so it is recommended to favour the UNIX convention ('/') in scripts that are meant to be cross-platform. - This function requires pandas and pooch. - Corresponding sample data live at https://yt-project.org/data """ if fn is None: print( "One can see which sample datasets are available at: https://yt-project.org/data\n" "or alternatively by running: yt.sample_data.api.get_data_registry_table()", file=sys.stderr, ) return None loadable_path, load_kwargs = _get_sample_data( fn, progressbar=progressbar, timeout=timeout, **kwargs ) return load(loadable_path, **load_kwargs) def _mount_helper( archive: str, mountPoint: str, ratarmount_kwa: dict, conn: "Connection" ): try: fuseOperationsObject = ratarmount.TarMount( pathToMount=archive, mountPoint=mountPoint, lazyMounting=True, **ratarmount_kwa, ) fuseOperationsObject.use_ns = True conn.send(True) except Exception: conn.send(False) raise ratarmount.fuse.FUSE( operations=fuseOperationsObject, mountpoint=mountPoint, foreground=True, nothreads=True, ) # --- Loader for tar-based datasets --- def load_archive( fn: str | Path, path: str, ratarmount_kwa: dict | None = None, mount_timeout: float = 1.0, *args, **kwargs, ) -> Dataset: r""" Load archived data with yt. This is a wrapper around :func:`~yt.loaders.load` to include mounting and unmounting the archive as a read-only filesystem and load it. Parameters ---------- fn: str The `filename` of the archive containing the dataset. path: str The path to the dataset in the archive. ratarmount_kwa: dict, optional Optional parameters to pass to ratarmount to mount the archive. mount_timeout: float, optional The timeout to wait for ratarmount to mount the archive. Default is 1s. Notes ----- - The function is experimental and may work or not depending on your setup. - Any additional keyword argument is passed down to :func:`~yt.loaders.load`. - This function requires ratarmount to be installed. - This function does not work on Windows system. """ import tarfile from multiprocessing import Pipe, Process warnings.warn( "The 'load_archive' function is still experimental and may be unstable.", stacklevel=2, ) fn = os.path.expanduser(fn) # This will raise FileNotFoundError if the path isn't matched # either in the current dir or yt.config.ytcfg['data_dir_directory'] if not fn.startswith("http"): fn = str(lookup_on_disk_data(fn)) if ratarmount_kwa is None: ratarmount_kwa = {} try: tarfile.open(fn) except tarfile.ReadError as exc: raise YTUnidentifiedDataType(fn, *args, **kwargs) from exc # Note: the temporary directory will be created by ratarmount tempdir = fn + ".mount" tempdir_base = tempdir i = 0 while os.path.exists(tempdir): i += 1 tempdir = f"{tempdir_base}.{i}" parent_conn, child_conn = Pipe() proc = Process(target=_mount_helper, args=(fn, tempdir, ratarmount_kwa, child_conn)) proc.start() if not parent_conn.recv(): raise MountError(f"An error occurred while mounting {fn} in {tempdir}") # Note: the mounting needs to happen in another process which # needs be run in the foreground (otherwise it may # unmount). To prevent a race-condition here, we wait # for the folder to be mounted within a reasonable time. t = 0.0 while t < mount_timeout: if os.path.ismount(tempdir): break time.sleep(0.1) t += 0.1 else: raise MountError(f"Folder {tempdir} does not appear to be mounted") # We need to kill the process at exit (to force unmounting) def umount_callback(): proc.terminate() atexit.register(umount_callback) # Alternatively, can dismount manually def del_callback(self): proc.terminate() atexit.unregister(umount_callback) ds = load(os.path.join(tempdir, path), *args, **kwargs) ds.dismount = types.MethodType(del_callback, ds) return ds def load_hdf5_file( fn: Union[str, "os.PathLike[str]"], root_node: str | None = "/", fields: list[str] | None = None, bbox: np.ndarray | None = None, nchunks: int = 0, dataset_arguments: dict | None = None, ): """ Create a (grid-based) yt dataset given the path to an hdf5 file. This function accepts a filename, as well as (potentially) a bounding box, the root node where fields are stored, and the number of chunks to attempt to decompose the object into. This function will then introspect that HDF5 file, attempt to determine the available fields, and then supply a :class:`yt.data_objects.static_output.Dataset` object. However, unlike the other loaders, the data is *not* required to be preloaded into memory, and will only be loaded *on demand*. This does not yet work with particle-type datasets. Parameters ---------- fn : str, os.Pathlike[str] A path to the hdf5 file that contains the data. root_node: str, optional If the fields to be loaded are stored under an HDF5 group object, specify it here. Otherwise, the fields are assumed to be at the root level of the HDF5 file hierarchy. fields : list of str, optional The fields to be included as part of the dataset. If this is not included, all of the datasets under *root_node* will be included. If your file contains, for instance, a "parameters" node at the root level next to other fields, it would be (mistakenly) included. This allows you to specify only those that should be included. bbox : array_like (xdim:zdim, LE:RE), optional If supplied, this will be the bounding box for the dataset. If not supplied, it will be assumed to be from 0 to 1 in all dimensions. nchunks : int, optional How many chunks should this dataset be split into? If 0 or not supplied, yt will attempt to ensure that there is one chunk for every 64**3 zones in the dataset. dataset_arguments : dict, optional Any additional arguments that should be passed to :class:`yt.loaders.load_amr_grids`, including things like the unit length and the coordinates. Returns ------- :class:`yt.data_objects.static_output.Dataset` object This returns an instance of a dataset, created with `load_amr_grids`, that can read from the HDF5 file supplied to this function. An open handle to the HDF5 file is retained. Raises ------ FileNotFoundError If fn does not match any existing file or directory. """ from yt.utilities.on_demand_imports import _h5py as h5py dataset_arguments = dataset_arguments or {} if bbox is None: bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) mylog.info("Assuming unitary (0..1) bounding box.") def _read_data(handle, root_node): def _reader(grid, field_name): ftype, fname = field_name si = grid.get_global_startindex() ei = si + grid.ActiveDimensions return handle[root_node][fname][si[0] : ei[0], si[1] : ei[1], si[2] : ei[2]] return _reader fn = str(lookup_on_disk_data(fn)) handle = h5py.File(fn, "r") reader = _read_data(handle, root_node) if fields is None: fields = list(handle[root_node].keys()) mylog.debug("Identified fields %s", fields) shape = handle[root_node][fields[0]].shape if nchunks <= 0: # We apply a pretty simple heuristic here. We don't want more than # about 64^3 zones per chunk. So ... full_size = np.prod(shape) nchunks = full_size // (64**3) mylog.info("Auto-guessing %s chunks from a size of %s", nchunks, full_size) grid_data = [] psize = get_psize(np.array(shape), nchunks) left_edges, right_edges, shapes, _, _ = decompose_array(shape, psize, bbox) for le, re, s in zip(left_edges, right_edges, shapes, strict=True): data = {_: reader for _ in fields} data.update({"left_edge": le, "right_edge": re, "dimensions": s, "level": 0}) grid_data.append(data) return load_amr_grids(grid_data, shape, bbox=bbox, **dataset_arguments) yt-project-yt-f043ac8/yt/py.typed000066400000000000000000000000001510711153200167730ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/sample_data/000077500000000000000000000000001510711153200175605ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/sample_data/__init__.py000066400000000000000000000000001510711153200216570ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/sample_data/api.py000066400000000000000000000140511510711153200207040ustar00rootroot00000000000000""" This is a collection of helper functions to yt.load_sample """ import json import re import sys from functools import lru_cache from pathlib import Path from typing import Optional from warnings import warn from yt.config import ytcfg from yt.utilities.on_demand_imports import ( _pandas as pd, _pooch as pooch, _requests as requests, ) num_exp = re.compile(r"\d*(\.\d*)?") byte_unit_exp = re.compile(r"[KMGT]?B") def _parse_byte_size(s: str): """ Convert a string size specification to integer byte size. This function should be insensitive to case and whitespace. It doesn't always return an int, as a temporary measure to deal with missing or corrupted data in the registry. This should be fixed in the future. Examples -------- # most of the following examples are adapted from # https://stackoverflow.com/a/31631711/5489622 >>> _parse_byte_size(None) >>> from numpy import nan >>> _parse_byte_size(nan) >>> _parse_byte_size("1B") 1 >>> _parse_byte_size("1.00 KB") 1024 >>> _parse_byte_size("488.28 KB") 500000 >>> _parse_byte_size("1.00 MB") 1049000 >>> _parse_byte_size("47.68 MB") 50000000 >>> _parse_byte_size("1.00 GB") 1074000000 >>> _parse_byte_size("4.66 GB") 5004000000 >>> _parse_byte_size("1.00 TB") 1100000000000 >>> _parse_byte_size("4.55 TB") 5003000000000 """ try: s = s.upper() except AttributeError: # input is not a string (likely a np.nan) return pd.NA match = re.search(num_exp, s) if match is None: raise ValueError val = float(match.group()) match = re.search(byte_unit_exp, s) if match is None: raise ValueError unit = match.group() prefixes = ["B", "K", "M", "G", "T"] raw_res = val * 1024 ** prefixes.index(unit[0]) return int(float(f"{raw_res:.3e}")) def _get_sample_data_registry(): import importlib.resources as importlib_resources return json.loads( importlib_resources.files("yt") .joinpath("sample_data_registry.json") .read_bytes() ) @lru_cache(maxsize=128) def get_data_registry_table(): """ Load the sample data registry as a pandas.Dataframe instance. This function is considered experimental and is exposed for exploratory purposed. The output format is subject to change. The output of this function is cached so it will only generate one request per session. """ # it would be nicer to have an actual api on the yt website server, # but this will do for now api_url = "https://raw.githubusercontent.com/yt-project/website/master/data/datafiles.json" response = requests.get(api_url) if not response.ok: raise RuntimeError( "Could not retrieve registry data. Please check your network setup." ) website_json = response.json() # this dict follows this schema: {frontend_name: {flat dataframe-like}} columns = ["code", "filename", "size", "url", "description"] website_table = pd.concat(pd.DataFrame(d) for d in website_json.values())[columns] # add a int-type byte size column # note that we cast to pandas specific type "Int64" because we expect missing values # see https://pandas.pydata.org/pandas-docs/stable/user_guide/missing_data.html#integer-dtypes-and-missing-data website_table["byte_size"] = ( website_table["size"].apply(_parse_byte_size).astype("Int64") ) # normalize urls to match the local json website_table["url"] = website_table["url"].apply( lambda u: u.replace("http:", "https:") ) # load local data pooch_table = pd.DataFrame(_get_sample_data_registry().values()) # merge tables unified_table = website_table.merge(pooch_table, on="url", how="outer") # PR 3089 # ideally we should be able to do this, but it's not possible # at the time of writing because fhe "filename" is incomplete # see the companion comment in load_sample # unified_table.set_index("filename", inplace=True) # unified_table.index.rename("id", inplace=True) return unified_table def _get_test_data_dir_path(): p = Path(ytcfg.get("yt", "test_data_dir")) if p.is_dir(): return p warn( "Storage directory from yt config doesn't exist " f"(currently set to '{p}'). " "Current working directory will be used instead." ) return Path.cwd() def lookup_on_disk_data(fn) -> Path: """ Look for data file/dir on disk. Returns ------- pathlib.Path to a file/dir matching fn if any Raises ------ FileNotFoundError """ path = Path(fn).expanduser().resolve() if path.exists(): return path err_msg = f"No such file or directory: '{fn}'." test_data_dir = _get_test_data_dir_path() if not test_data_dir.is_dir(): raise FileNotFoundError(err_msg) alt_path = _get_test_data_dir_path().joinpath(fn).resolve() if alt_path != path: if alt_path.exists(): return alt_path err_msg += f"\n(Also tried '{alt_path}')." raise FileNotFoundError(err_msg) def get_download_cache_dir(): return _get_test_data_dir_path() / "yt_download_cache" _POOCHIE = None def _get_pooch_instance(): global _POOCHIE if _POOCHIE is None: data_registry = get_data_registry_table() cache_storage = get_download_cache_dir() registry = {k: v["hash"] for k, v in _get_sample_data_registry().items()} _POOCHIE = pooch.create( path=cache_storage, base_url="https://yt-project.org/data/", registry=registry, ) return _POOCHIE def _download_sample_data_file( filename, progressbar=True, timeout: Optional[int] = None ): """ Download a file by url. Returns ------- storage_filename : location of the downloaded file """ downloader = pooch.HTTPDownloader(progressbar=progressbar, timeout=timeout) poochie = _get_pooch_instance() poochie.fetch(filename, downloader=downloader) return poochie.path / filename yt-project-yt-f043ac8/yt/sample_data_registry.json000066400000000000000000001117261510711153200224130ustar00rootroot00000000000000{ "A2052.tar.gz": { "hash": "a6a427750945b36a7b7478e3e301e4aba75e95af11bdda2e9ef759c9bbcde5e5", "load_kwargs": {}, "load_name": "xray_fits/A2052_merged_0.3-2_match-core_tmap_bgecorr.fits", "url": "https://yt-project.org/data/A2052.tar.gz" }, "AM06.tar.gz": { "hash": "d95090c2c65725411ad5c11f1627c311ec144f93ed04a88a4a4e446aeb0420a6", "load_kwargs": {}, "load_name": "AM06.out1.00400.athdf", "url": "https://yt-project.org/data/AM06.tar.gz" }, "ActiveParticleCosmology.tar.gz": { "hash": "6a52b6539db12f29c4e4209705ffa823bb8027f65bf9431cf068d3ffdb28f55e", "load_kwargs": {}, "load_name": "DD0046/DD0046", "url": "https://yt-project.org/data/ActiveParticleCosmology.tar.gz" }, "ActiveParticleTwoSphere.tar.gz": { "hash": "7b056da9180417c9339bb5c3b98f5dfa5ac33f2f6c2a0d5b70c624a41dd60d4e", "load_kwargs": {}, "load_name": "DD0011/DD0011", "url": "https://yt-project.org/data/ActiveParticleTwoSphere.tar.gz" }, "ArepoBullet.tar.gz": { "hash": "17afd084a0e527e9dccf4cf0dc7e9296a61e44faec3bbc1f9efb3609d9df1025", "load_kwargs": {}, "load_name": "snapshot_150.hdf5", "url": "https://yt-project.org/data/ArepoBullet.tar.gz" }, "ArepoCosmicRays.tar.gz": { "hash": "e664ea7f634fff778511e9ae7b2e375ba9c5099e427f7c037d48bba9944388f4", "load_kwargs": {}, "load_name": "snapshot_039.hdf5", "url": "https://yt-project.org/data/ArepoCosmicRays.tar.gz" }, "BigEndianGadgetBinary.tar.gz": { "hash": "0ab8d69b7c0c1a74282c567823916cc947f9ead7ab54b749a38a17c158b371c9", "load_kwargs": {}, "load_name": "BigEndianGadgetBinary", "url": "https://yt-project.org/data/BigEndianGadgetBinary.tar.gz" }, "C15-3D-3deg.tar.gz": { "hash": "b59a4ea80b3e5bf0c8fc1ab960241b23e8cba6d7346cab25d071d9a63c3be2b7", "load_kwargs": {}, "load_name": "chimera_002715000_grid_1_01.h5", "url": "https://yt-project.org/data/C15-3D-3deg.tar.gz" }, "CfRadialGrid.tar.gz": { "hash": "22a45b322773d4b2864ccab93c521d8e3d637d81c8d8c0852e1a67c7fa883e0c", "load_kwargs": {}, "load_name": "grid1.nc", "url": "https://yt-project.org/data/CfRadialGrid.tar.gz" }, "ChollaSimple.tar.gz": { "hash": "aa277471f694f9de2e46c190efd8cd631c0694044aa29cb8431f127319774d73", "load_kwargs": {}, "load_name": "0.h5", "url": "https://yt-project.org/data/ChollaSimple.tar.gz" }, "ClusterMerger.tar.gz": { "hash": "862a537bdecb8de363f20abec751665fd2b9208c29ae66331105cf848e7a0033", "load_kwargs": {}, "load_name": "Data_000100", "url": "https://yt-project.org/data/ClusterMerger.tar.gz" }, "CompactObjects.tar.gz": { "hash": "e24f275d86c95900f62c640c8f5fcc4fec3310e09893b6ed6a9f1e7adc6b9d64", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/CompactObjects.tar.gz" }, "D9p_500.tar.gz": { "hash": "6d62244dbb4d0974f2e63dd191c3aaf65a8a5ebb90c202e40e871576c3267040", "load_kwargs": {}, "load_name": "10MpcBox_HartGal_csf_a0.500.d", "url": "https://yt-project.org/data/D9p_500.tar.gz" }, "DICEGalaxyDisk.tar.gz": { "hash": "e7cb5255be0b6e712620d337d019239980827ce458585fe5f3141d0f1d910721", "load_kwargs": {}, "load_name": "output_00001", "url": "https://yt-project.org/data/DICEGalaxyDisk.tar.gz" }, "DICEGalaxyDisk_nonCosmological.tar.gz": { "hash": "f95032fc9dc61e760e3919f2c3a59960684d21b9a44cc34cb1a15953de18f30d", "load_kwargs": {}, "load_name": "output_00002/info_00002.txt", "url": "https://yt-project.org/data/DICEGalaxyDisk_nonCosmological.tar.gz" }, "DMonly.tar.gz": { "hash": "45ad285c0df4d7c9e08f497047ac04da373580cd70a8e6fefbe824726235c5c7", "load_kwargs": {}, "load_name": "PMcrs0.0100.DAT", "url": "https://yt-project.org/data/DMonly.tar.gz" }, "DeeplyNestedZoom.tar.gz": { "hash": "cf6c33fc9d743624f9ce95b947df18bae6159afc1eb18e65aecc1b57cc9432ed", "load_kwargs": {}, "load_name": "DD0025/data0025", "url": "https://yt-project.org/data/DeeplyNestedZoom.tar.gz" }, "DensTurbMag.tar.gz": { "hash": "43f3e03d1065d233d315c620316ba8fd877a94bd368d911905dbf81861beb47f", "load_kwargs": {}, "load_name": "DensTurbMag_hdf5_plt_cnt_0015", "url": "https://yt-project.org/data/DensTurbMag.tar.gz" }, "EAGLE_6.tar.gz": { "hash": "234309547fdce3f68e0399bc6f565364ec3bdd1887ce4e414071e55654157652", "load_kwargs": {}, "load_name": "eagle_0005.hdf5", "url": "https://yt-project.org/data/EAGLE_6.tar.gz" }, "ENZOE_orszag-tang_0.5.tar.gz": { "hash": "dbd93068131d3d100c186adb801302a303ac2c5c54118ab5f9e19b409eee5efc", "load_kwargs": {}, "load_name": "ENZOE_orszag-tang_0.5.block_list", "url": "https://yt-project.org/data/ENZOE_orszag-tang_0.5.tar.gz" }, "ENZOP_DD0140.tar.gz": { "hash": "8df14326a82845479398447b8b55bd93d8eea1c5f31657536b390e6703458e0d", "load_kwargs": {}, "load_name": "ENZOP_DD0140.block_list", "url": "https://yt-project.org/data/ENZOP_DD0140.tar.gz" }, "EnzoKelvinHelmholtz.tar.gz": { "hash": "750dcd29a060aee7b5607afdb30a6e3c2b465be60fc5e022c19a78ab04b35a39", "load_kwargs": {}, "load_name": "DD0011/DD0011", "url": "https://yt-project.org/data/EnzoKelvinHelmholtz.tar.gz" }, "Enzo_64.tar.gz": { "hash": "2b17fffb6a2e8e471ef85dbdbfa5568f09f7bdb77715c69b40dfe48be8f71bc9", "load_kwargs": {}, "load_name": "DD0043/data0043", "url": "https://yt-project.org/data/Enzo_64.tar.gz" }, "ExodusII_tests.tar.gz": { "hash": "d21c5ef320f2b4705ec4cacf26488a3a59544fab8cc3a9726e8989ad1bd8d177", "load_kwargs": {}, "load_name": "ExodusII/gold.e", "url": "https://yt-project.org/data/ExodusII_tests.tar.gz" }, "F37_80.tar.gz": { "hash": "ff0eeb2690ec53f25def74a346fdef07878e46d16424ce4f5d31d8c841408a46", "load_kwargs": {}, "load_name": "chimera_00001_grid_1_01.h5", "url": "https://yt-project.org/data/F37_80.tar.gz" }, "FIRE_M12i_ref11.tar.gz": { "hash": "74488ca3158810ad76ed65bf43a5769b899b63d3985a9b43ff55bffd04bf3af2", "load_kwargs": {}, "load_name": "snapshot_600.hdf5", "url": "https://yt-project.org/data/FIRE_M12i_ref11.tar.gz" }, "GDFClumps.tar.gz": { "hash": "d9d81f934bdc8e1459bb04471fdcafcea2c2600d326e20a3d922b4c148f7783b", "load_kwargs": {}, "load_name": "clumps.h5", "url": "https://yt-project.org/data/GDFClumps.tar.gz" }, "Gadget3-snap-format2.tar.gz": { "hash": "bedc0f1979f0dd0cc9707c60a806171fe47b4bece0d29b474ff4030b560d5660", "load_kwargs": {}, "load_name": "Gadget3-snap-format2", "url": "https://yt-project.org/data/Gadget3-snap-format2.tar.gz" }, "GadgetDiskGalaxy.tar.gz": { "hash": "262bbd552786c133918a858c8ce09f5348583733fb906f6defbb6375b5a7951e", "load_kwargs": {}, "load_name": "snapshot_200.hdf5", "url": "https://yt-project.org/data/GadgetDiskGalaxy.tar.gz" }, "GalaxyClusterMerger.tar.gz": { "hash": "64d2909369fc0ca7a810734aaa53a6db2f526b101993c401e6b66fc0db597ed7", "load_kwargs": {}, "load_name": "fiducial_1to3_b0.273d_hdf5_plt_cnt_0175", "url": "https://yt-project.org/data/GalaxyClusterMerger.tar.gz" }, "GasSloshing.tar.gz": { "hash": "3e16c5975f310bafd1e859080310de7ca01d5aa055b3504c10138c93154202a4", "load_kwargs": {}, "load_name": "sloshing_nomag2_hdf5_plt_cnt_0150", "url": "https://yt-project.org/data/GasSloshing.tar.gz" }, "GasSloshingLowRes.tar.gz": { "hash": "1eb53d8c0b90e2ebb95aee129046bd230f5296347c90a44558b6f2bdb79951ed", "load_kwargs": {}, "load_name": "sloshing_low_res_hdf5_plt_cnt_0300", "url": "https://yt-project.org/data/GasSloshingLowRes.tar.gz" }, "GaussianBeam.tar.gz": { "hash": "5200448cee1cc03f8ba630c5f88c6a258f9d55cab5672ce8847035d701cfef15", "load_kwargs": {}, "load_name": "plt03008", "url": "https://yt-project.org/data/GaussianBeam.tar.gz" }, "GaussianCloud.tar.gz": { "hash": "edf869828cc14c2f47a389a62bf5a9bec22fba0de7e5c5380c6eb1acca9aa6a1", "load_kwargs": {}, "load_name": "data.0077.3d.hdf5", "url": "https://yt-project.org/data/GaussianCloud.tar.gz" }, "HiresIsolatedGalaxy.tar.gz": { "hash": "1b310a243158dbb9f42b9a71a1c71512a24f1e3cac906713b518555f8bc350c6", "load_kwargs": {}, "load_name": "DD0044/DD0044", "url": "https://yt-project.org/data/HiresIsolatedGalaxy.tar.gz" }, "InteractingJets.tar.gz": { "hash": "e6a0d55df9763842b94fdd453b69b1f2a392f19b4188a9d08c58bf63613c281f", "load_kwargs": {}, "load_name": "jet_000002", "url": "https://yt-project.org/data/InteractingJets.tar.gz" }, "IsolatedGalaxy.tar.gz": { "hash": "fc081bd4420efd02f7ba2db7eaf4bff0299d5cc4da93436be55a30c219daaf18", "load_kwargs": {}, "load_name": "galaxy0030/galaxy0030", "url": "https://yt-project.org/data/IsolatedGalaxy.tar.gz" }, "IsolatedGalaxy_Gravity.tar.gz": { "hash": "2b7faa6a2472d4afe526678775c208242605a8918ace99154bd4ecb6df266505", "load_kwargs": {}, "load_name": "galaxy0030/galaxy0030", "url": "https://yt-project.org/data/IsolatedGalaxy_Gravity.tar.gz" }, "IsothermalCollapse.tar.gz": { "hash": "1420074fdef1c00692bdd4dee31b5b92eaebd668a80510dc554942590b0dae47", "load_kwargs": { "bounding_box": [ [ -3, 3 ], [ -3, 3 ], [ -3, 3 ] ], "unit_base": { "UnitLength_in_cm": 5e+16, "UnitMass_in_g": 1.98992e+33, "UnitVelocity_in_cm_per_s": 46385.19 } }, "load_name": "snap_505.hdf5", "url": "https://yt-project.org/data/IsothermalCollapse.tar.gz" }, "IsothermalSphere.tar.gz": { "hash": "787b0aba3446c62d07caafd7db8e0f225d52b78ce2afe6d86c2535898fc7f4e5", "load_kwargs": {}, "load_name": "data.0000.3d.hdf5", "url": "https://yt-project.org/data/IsothermalSphere.tar.gz" }, "JetICMWall.tar.gz": { "hash": "4f156be55eb96d994001b64965017abf55728ce406bdbdbf5b0de3b3238b905a", "load_kwargs": {}, "load_name": "Data_000060", "url": "https://yt-project.org/data/JetICMWall.tar.gz" }, "KelvinHelmholtz.tar.gz": { "hash": "a70ccdab287e6b24553881d24a08a4025079b0e40eea9097c7ef686166ce571d", "load_kwargs": {}, "load_name": "data.0004.hdf5", "url": "https://yt-project.org/data/KelvinHelmholtz.tar.gz" }, "KeplerianDisk.tar.gz": { "hash": "488307807a1e53c404b316f28915d614aa6673defd7b2291e3f26b7fc7839506", "load_kwargs": {}, "load_name": "disk.out1.00000.athdf", "url": "https://yt-project.org/data/KeplerianDisk.tar.gz" }, "KeplerianRing.tar.gz": { "hash": "b2e26d9f98319289283e44e4303a3ac0bfc937931290d8aff86e437503634eab", "load_kwargs": {}, "load_name": "keplerian_ring_0020.hdf5", "url": "https://yt-project.org/data/KeplerianRing.tar.gz" }, "LangmuirWave_v2.tar.gz": { "hash": "27ab056b79b598b4b88e8aa4be042282c07899e85206dd42a506639a69e31270", "load_kwargs": {}, "load_name": "plt00020_v2", "url": "https://yt-project.org/data/LangmuirWave_v2.tar.gz" }, "Laser.tar.gz": { "hash": "7850a9f8cba1fcab4712b269fbea707dbfbaf611605e319cefe444bf9a1347f0", "load_kwargs": {}, "load_name": "plt00015", "url": "https://yt-project.org/data/Laser.tar.gz" }, "LocBub_dust.tar.gz": { "hash": "1ad9728ca982616686d9bd6c1c959fece3c067ecbbba64ff8c2c5bfcc1deed01", "load_kwargs": {}, "load_name": "LocBub_dust_hdf5_plt_cnt_0220", "url": "https://yt-project.org/data/LocBub_dust.tar.gz" }, "MagneticumCluster.tar.gz": { "hash": "403c2224c205a44ee92a5925053be8aef6e5d530d9297c18151cdb0f588c32a7", "load_kwargs": { "long_ids": true, "field_spec": "magneticum_box2_hr" }, "load_name": "snap_132", "url": "https://yt-project.org/data/MagneticumCluster.tar.gz" }, "MHDBlast.tar.gz": { "hash": "fee1139df19448bae966a5396775476935886e69e3898fd7cf71d54548c4e14b", "load_kwargs": {}, "load_name": "id0/Blast.0100.vtk", "url": "https://yt-project.org/data/MHDBlast.tar.gz" }, "MHDCTOrszagTang.tar.gz": { "hash": "90d69d9ed5488051fdbab2ebddf1b45647ab513b19ab0b04bb9ea65b0bbbab26", "load_kwargs": {}, "load_name": "DD0004/data0004", "url": "https://yt-project.org/data/MHDCTOrszagTang.tar.gz" }, "MHDOrszagTangVortex.tar.gz": { "hash": "24c97644ab8620333b0839ac69e0e4021820681eebafe96882fdf3b8fbec1270", "load_kwargs": {}, "load_name": "Data_000018", "url": "https://yt-project.org/data/MHDOrszagTangVortex.tar.gz" }, "MHDSloshing.tar.gz": { "hash": "f0d0b4e26145a2f7445c3afed8ed0294026a2209496c477cc0d1a7a0f022bf38", "load_kwargs": { "units_override": { "length_unit": [1.0, "Mpc"], "mass_unit": [100000000000000.0, "Msun"], "time_unit": [1.0, "Myr"] } }, "load_name": "virgo_low_res.0054.vtk", "url": "https://yt-project.org/data/MHDSloshing.tar.gz" }, "MHD_Cyl3d_hdf5_plt_cnt_0100.tar.gz": { "hash": "f3ad1a0d01e44617a2291733cdceb9b7a54a4f3551155e5ec1b14ee012357dc8", "load_kwargs": {}, "load_name": "MHD_Cyl3d_hdf5_plt_cnt_0100.hdf5", "url": "https://yt-project.org/data/MHD_Cyl3d_hdf5_plt_cnt_0100.tar.gz" }, "MOOSE_Sample_data.tar.gz": { "hash": "0a43e40931e25c4a0e4f205b9902666c2d7a89cd5959e2d5eb7819059cabe01f", "load_kwargs": {}, "load_name": "out.e", "url": "https://yt-project.org/data/MOOSE_Sample_data.tar.gz" }, "MoabTest.tar.gz": { "hash": "22609631c0e417964e0247b4ad62199a60c2d7979f38f8587960a9f8b2147fab", "load_kwargs": {}, "load_name": "fng_usrbin22.h5m", "url": "https://yt-project.org/data/MoabTest.tar.gz" }, "MultiRegion.tar.gz": { "hash": "6acf8e21f2b847102cfbb9f6e22b9b919a9b807e22b2176854fead43e3344e9d", "load_kwargs": {}, "load_name": "two_region_example_out.e", "url": "https://yt-project.org/data/MultiRegion.tar.gz" }, "Nyx_LyA.tar.gz": { "hash": "db9fff2e78ac326780d9ba4fcdb0f9fe30440dd9100b6c50cb1f97722f2c8ea8", "load_kwargs": {}, "load_name": "plt00000", "url": "https://yt-project.org/data/Nyx_LyA.tar.gz" }, "Orbit.tar.gz": { "hash": "3df7988b555c5ff17e73f4675167c78baa33993e0c321d56c8da542c5ae03a4b", "load_kwargs": {}, "load_name": "orbit_hdf5_chk_0014", "url": "https://yt-project.org/data/Orbit.tar.gz" }, "PlasmaAcceleration_v2.tar.gz": { "hash": "45eea2e3b1d9bd796816c1d300e5ccd4db57c9609ad8dc9b93a157a90c3a89a4", "load_kwargs": {}, "load_name": "plt00030_v2", "url": "https://yt-project.org/data/PlasmaAcceleration_v2.tar.gz" }, "Plummer.tar.gz": { "hash": "3ef04dad7aee393a9cb08cf6b8616f271aa733a53a07fdfad34b8effcb550998", "load_kwargs": {}, "load_name": "plummer_000000", "url": "https://yt-project.org/data/Plummer.tar.gz" }, "PopIII_mini.tar.gz": { "hash": "184dfeb09474fad8ec91474d44b75f6a7d33be99c021b76902d91baad98ddca6", "load_kwargs": {}, "load_name": "DD0034/DD0034", "url": "https://yt-project.org/data/PopIII_mini.tar.gz" }, "RT_particles.tar.gz": { "hash": "69f7a96dbc8fad60defc03f1a2e864de4ffbf5d881d7cac2c8727ae317e0a24f", "load_kwargs": {}, "load_name": "plt00050", "url": "https://yt-project.org/data/RT_particles.tar.gz" }, "RadAdvect.tar.gz": { "hash": "0511e8d45cdb9b268089bc4ef4608a58b7218cf0db9da8d83a14cbd34722784b", "load_kwargs": {}, "load_name": "plt00000", "url": "https://yt-project.org/data/RadAdvect.tar.gz" }, "RadTube.tar.gz": { "hash": "43a2f00c435cbaa4708d6e7e5e3ff0784699f5d28bc435ecbd677f64e6351837", "load_kwargs": {}, "load_name": "plt00500", "url": "https://yt-project.org/data/RadTube.tar.gz" }, "RamPressureStripping.tar.gz": { "hash": "4ef4f9f9127668699111091d31ece0fff6e7ab2e9e6af7c547810df31db4a40b", "load_kwargs": { "units_override": { "length_unit": 8.0236e+22, "mass_unit": 5.1649293e+39, "time_unit": 308600000000000.0 } }, "load_name": "id0/rps.0062.vtk", "url": "https://yt-project.org/data/RamPressureStripping.tar.gz" }, "SecondOrderQuads.tar.gz": { "hash": "b00000d4a6ec3907e2f22cbf58c7b582a9a63ca2792a0419fb56a8733b1ec904", "load_kwargs": {}, "load_name": "lid_driven_out.e", "url": "https://yt-project.org/data/SecondOrderQuads.tar.gz" }, "SecondOrderTets.tar.gz": { "hash": "73bbd2bf10d02287f15303215952d560969daab1919001969c29991561e26cb9", "load_kwargs": {}, "load_name": "tet10_unstructured_out.e", "url": "https://yt-project.org/data/SecondOrderTets.tar.gz" }, "SecondOrderTris.tar.gz": { "hash": "d5a4e05117ceb53b31e1ecef077355b05dbd8a43b6773751b48390ef80795165", "load_kwargs": {}, "load_name": "RZ_p_no_parts_do_nothing_bcs_cone_out.e", "url": "https://yt-project.org/data/SecondOrderTris.tar.gz" }, "Sedov3D.tar.gz": { "hash": "4e3220744024333293c707bafe64706d325e1f6ee0914bee4be78c3b48d3f97e", "load_kwargs": {}, "load_name": "Sedov_3d/sedov_hdf5_chk_0000", "url": "https://yt-project.org/data/Sedov3D.tar.gz" }, "ShockCloud.tar.gz": { "hash": "5a41fe85c293b5714fa3fb989c7d905ed8b6cc92276c524ec786933081176027", "load_kwargs": {}, "load_name": "id0/Cloud.0050.vtk", "url": "https://yt-project.org/data/ShockCloud.tar.gz" }, "SimbaExample.tar.gz": { "hash": "6770e4d5ead92cfc966bca5b8735903389809885f0f1b50523448f68bc53a5ec", "load_kwargs": {}, "load_name": "simba_example.hdf5", "url": "https://yt-project.org/data/SimbaExample.tar.gz" }, "StarParticles.tar.gz": { "hash": "83fbcb7d520cc49c25430003a9e1f1ca8ffed7ab9f83319aa91faa45494ed485", "load_kwargs": {}, "load_name": "plrd01000", "url": "https://yt-project.org/data/StarParticles.tar.gz" }, "TNGHalo.tar.gz": { "hash": "759b517500362d1ccd6286fe8854b1a303ecbce4678b9ff7ce541cfecda8a604", "load_kwargs": {}, "load_name": "halo_59.hdf5", "url": "https://yt-project.org/data/TNGHalo.tar.gz" }, "TipsyAuxiliary.tar.gz": { "hash": "c27205c3b5f673b9822c76212c59c9632027210a6ee5f3c2e9eac213b805a143", "load_kwargs": {}, "load_name": "ascii/onestar.00001", "url": "https://yt-project.org/data/TipsyAuxiliary.tar.gz" }, "TipsyGalaxy.tar.gz": { "hash": "34228eec5d43a8c0465b785efea39e4e963390730919e0bf819f8fca474d3341", "load_kwargs": {}, "load_name": "galaxy.00300", "url": "https://yt-project.org/data/TipsyGalaxy.tar.gz" }, "ToroShockTube.tar.gz": { "hash": "016541e0e600f77b820bfc7281bafc2e3c3a1bd6ae11567a0dce7247032a5f6a", "load_kwargs": {}, "load_name": "DD0001/data0001", "url": "https://yt-project.org/data/ToroShockTube.tar.gz" }, "TurbBoxLowRes.tar.gz": { "hash": "96b9e08dcf007bd4410ed98f9252aad33138515b8e83f4168f4f7bcda5e5bc27", "load_kwargs": {}, "load_name": "data.0005.3d.hdf5", "url": "https://yt-project.org/data/TurbBoxLowRes.tar.gz" }, "UnigridData.tar.gz": { "hash": "8efffc5377c99eaa45c086ac730d056b890c8358f17e8a2f7d8c281384fc2ab7", "load_kwargs": {}, "load_name": "velocity_field_20.fits", "url": "https://yt-project.org/data/UnigridData.tar.gz" }, "WDMerger_hdf5_chk_1000.tar.gz": { "hash": "a3806579f6e25b61ddc918dd6c3004d69cda88a94be3967111702cf7636ba158", "load_kwargs": {}, "load_name": "WDMerger_hdf5_chk_1000.hdf5", "url": "https://yt-project.org/data/WDMerger_hdf5_chk_1000.tar.gz" }, "WaveDarkMatter.tar.gz": { "hash": "4996fbf505ea56a196970d8920e9e46d0da70fecc792355dabaab6be8e5f0172", "load_kwargs": {}, "load_name": "psiDM_000020", "url": "https://yt-project.org/data/WaveDarkMatter.tar.gz" }, "WindTunnel.tar.gz": { "hash": "414b923d59f671196f2185c5216ae241156619b5042854df6236dea9fa10ee2b", "load_kwargs": {}, "load_name": "windtunnel_4lev_hdf5_plt_cnt_0030", "url": "https://yt-project.org/data/WindTunnel.tar.gz" }, "ZeldovichPancake.tar.gz": { "hash": "88467b9babe6886ce393ee9ee01ade9be3842a32cef9946863c68f765b8ebd32", "load_kwargs": {}, "load_name": "plt32.2d.hdf5", "url": "https://yt-project.org/data/ZeldovichPancake.tar.gz" }, "acisf05356N003_evt2.fits.gz": { "hash": "6fc406dd056b525592f704b202dadf52c41024660dbbd96cfbeb830d710a70d8", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/acisf05356N003_evt2.fits.gz" }, "agora_1e11.00400.tar.gz": { "hash": "3a8820735bcf5067dfbe0fcd90d79121a801ea1a34c7d6de7e09185d3c635e6a", "load_kwargs": {}, "load_name": "agora_1e11.00400", "url": "https://yt-project.org/data/agora_1e11.00400.tar.gz" }, "ahf_halos.tar.gz": { "hash": "66d60ab80520cdeb1510a9c7e48a214d79f2559f7808bc346722028b5b48f8f7", "load_kwargs": {}, "load_name": "snap_N64L16_068.parameter", "url": "https://yt-project.org/data/ahf_halos.tar.gz" }, "athenapk_cluster.tar.gz": { "hash": "95f641b31cca00a39fa728be367861db9abc75ec3e3adcd9103325f675d3e9c1", "load_kwargs": {}, "load_name": "athenapk_cluster.restart.00000.rhdf", "url": "https://yt-project.org/data/athenapk_cluster.tar.gz" }, "athenapk_disk.tar.gz": { "hash": "3bd59a494a10b28cd691d1f69700f9e100a6a3a32541b0e16348d81163404cd0", "load_kwargs": {}, "load_name": "athenapk_disk.prim.00000.phdf", "url": "https://yt-project.org/data/athenapk_disk.tar.gz" }, "bw_cartesian_3d.tar.gz": { "hash": "de8b3b4c2a8c93f857c6729a118960eb1bcf244e1de07aee231d6fcc589c5628", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/bw_cartesian_3d.tar.gz" }, "bw_cylindrical_3d.tar.gz": { "hash": "7392e60dabd7ef636ce98929c56d4d8ece89958716a67e32792dce5981d64709", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/bw_cylindrical_3d.tar.gz" }, "bw_polar_2d.tar.gz": { "hash": "85308582ab55049474a9a302d1e88459a89f4e8925c5b901fbf3c590b1f269ab", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/bw_polar_2d.tar.gz" }, "bw_spherical_2d.tar.gz": { "hash": "2407f8f3352d7f2687a608bdfb7143e892d54b983d5a0d1f869132f41c98dbcc", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/bw_spherical_2d.tar.gz" }, "kh2d.tar.gz": { "hash": "2ea94f4f24b3423dcb67dc1b1eb28c83a02b66b6c79b29d1703b7aa2aa76b71e", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/kh2d.tar.gz" }, "kh3d.tar.gz": { "hash": "ae354a29eb88e2cd6720efdb98de96a22b4988364cd65cf8f8dce071914430c3", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/kh3d.tar.gz" }, "mhd_jet.tar.gz": { "hash": "24b7d24ec10d876dcc508563d516e1a4fe323150a6d5aef2f4640b020da817d7", "load_kwargs": {}, "load_name": "Jet0003.dat", "url": "https://yt-project.org/data/mhd_jet.tar.gz" }, "riemann1d.tar.gz": { "hash": "377483c6b74c1826b5f7c5e2b52a5b8ffeb974515d62d6135208cc96f7f09ae4", "load_kwargs": {}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/riemann1d.tar.gz" }, "rmi_dust_2d.tar.gz": { "hash": "96fafc63755b065ffa17d63a7ab5f01d4183ed1b10728ce1c5a23281cb43f5a6", "load_kwargs": {"parfiles": ["rmi_dust_2d/amrvac.par"]}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/rmi_dust_2d.tar.gz" }, "solar_prom2d.tar.gz": { "hash": "a8ddb034c27badf8160af1c4622ec9e9835c4434a5556e55212df6803c5020dc", "load_kwargs": {"parfiles": ["solar_prom2d/amrvac.par"]}, "load_name": "output0001.dat", "url": "https://yt-project.org/data/solar_prom2d.tar.gz" }, "arbor.tar.gz": { "hash": "d6c89c6496acdd92ef086736b8644996de58b1f4292b67b01d5db94ce60210fb", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/arbor.tar.gz" }, "big_tipsy.tar.gz": { "hash": "fe7a5b1b7bb3d960a3ccb77457c039a010c05b5caa10b6891add3274fb9f7e36", "load_kwargs": {}, "load_name": "g1536.00256", "url": "https://yt-project.org/data/big_tipsy.tar.gz" }, "c5.tar.gz": { "hash": "db007304b128472d2439c946a8169f5cc00e0701bbcda18012e1beb8cd30dba6", "load_kwargs": {}, "load_name": "c5.h5m", "url": "https://yt-project.org/data/c5.tar.gz" }, "castro_sedov_1d_cyl_plt00150.tar.gz": { "hash": "c96a8fdf3cd43563a88e34569a6b37e57a0349692a9100ea65252506a167f08f", "load_kwargs": {}, "load_name": "", "url": "https://yt-project.org/data/castro_sedov_1d_cyl_plt00150.tar.gz" }, "castro_sedov_2d_cyl_in_cart_plt00150.tar.gz": { "hash": "8b73a37a0503dba593af65f8bf16dfcc11ac825d671f11e5a364a2dee63c9047", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/castro_sedov_2d_cyl_in_cart_plt00150.tar.gz" }, "castro_sedov_2d_sph_in_cyl_plt00130.tar.gz": { "hash": "ef4d081a2a2f8e10afe132768725c573631b82021e91f07782f1c1fbe043e2b5", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/castro_sedov_2d_sph_in_cyl_plt00130.tar.gz" }, "castro_sod_x_plt00036.tar.gz": { "hash": "3f0a586b41e7b54fa2b3cddd50f9384feb2efe1fe1a815e7348965ae7bf88f78", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/castro_sod_x_plt00036.tar.gz" }, "check_pooch.py": { "hash": "d73fa43c3d56b3487ba96d45c0e7198f3d5471475f19105ee4fcd6442e2a3024", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/check_pooch.py.tar.gz" }, "cm1_tornado_lofs.tar.gz": { "hash": "7a84f688b363b79d2ac7bf53d1a963f01d07926a0d268e4163ba87ea81699706", "load_kwargs": {}, "load_name": "nc4_cm1_lofs_tornado_test.nc", "url": "https://yt-project.org/data/cm1_tornado_lofs.tar.gz" }, "datafiles.json": { "hash": "56732241ddfe5770d0dacc52c1957a0c8b13ca095fd0d8f029dbd263be714838", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/datafiles.json.tar.gz" }, "enzo_cosmology_plus.tar.gz": { "hash": "dab1d5cdf30ec6a99b556fe20cc91ba9a3b37c079b608de7cb92a240b2a943c8", "load_kwargs": {}, "load_name": "DD0046/DD0046", "url": "https://yt-project.org/data/enzo_cosmology_plus.tar.gz" }, "enzo_tiny_cosmology.tar.gz": { "hash": "91b04e443dfdafc2de234d7bb6f7ab4a0a970ec43e6ff5c59e0c9ded8fa41fa9", "load_kwargs": {}, "load_name": "DD0046/DD0046", "url": "https://yt-project.org/data/enzo_tiny_cosmology.tar.gz" }, "example-2d.tar.gz": { "hash": "d9f93ec4c83bd2abe14ea7bac447d154f44b86087e62945de8ce8e295f0d231d", "load_kwargs": {}, "load_name": "hdf5/data00000100.h5", "url": "https://yt-project.org/data/example-2d.tar.gz" }, "example-3d.tar.gz": { "hash": "42f860e42e80885d80f4ecfa84f58a8b083bb69d6b9e3b1bae1517003cb163c3", "load_kwargs": {}, "load_name": "hdf5/data00000100.h5", "url": "https://yt-project.org/data/example-3d.tar.gz" }, "fiducial_1to1_b0.tar.gz": { "hash": "1af47a8230addb96055a5c73d1fab9b413f566df191d03c60f0f202df845e7ff", "load_kwargs": {}, "load_name": "fiducial_1to1_b0_hdf5_part_0004", "url": "https://yt-project.org/data/fiducial_1to1_b0.tar.gz" }, "fiducial_1to3_b1.tar.gz": { "hash": "23c9faaa1af084b186f4115662967473b1b5cfe76b9afe232be800b6bd7da941", "load_kwargs": {}, "load_name": "fiducial_1to3_b1_hdf5_part_0080", "url": "https://yt-project.org/data/fiducial_1to3_b1.tar.gz" }, "gadget_fof_halos.tar.gz": { "hash": "77430eaca62ccc0c476d863f0beff5b0c71fa827b7a2fc33aa9cf803e001cc90", "load_kwargs": {}, "load_name": "groups_042/fof_subhalo_tab_042.0.hdf5", "url": "https://yt-project.org/data/gadget_fof_halos.tar.gz" }, "gadget_halos.tar.gz": { "hash": "1eb2c77700fac6eb8f9435549e0613ce38035c9106b295b9d5f6c70c82542842", "load_kwargs": {}, "load_name": "data/groups_076/fof_subhalo_tab_076.0.hdf5", "url": "https://yt-project.org/data/gadget_halos.tar.gz" }, "geos.tar.gz": { "hash": "2fbf9537be8c4f5da84ea22ed0971ded1a9aa71140fddda92f00f20df6fc43d3", "load_kwargs": {}, "load_name": "GEOS.fp.asm.inst3_3d_aer_Nv.20180822_0900.V01.nc4", "url": "https://yt-project.org/data/geos.tar.gz" }, "gizmo_64.tar.gz": { "hash": "11b8f425832bc1579854e013390483c9131ef5f27e3a74c8685a6d6dfcb809e5", "load_kwargs": {}, "load_name": "output/snap_N64L16_135.hdf5", "url": "https://yt-project.org/data/gizmo_64.tar.gz" }, "gizmo_cosmology_plus.tar.gz": { "hash": "73062872b13c6a8c86ac134b18e177c3526250000cef5559ceb0b2383cbaba6b", "load_kwargs": {}, "load_name": "snap_N128L16_131.hdf5", "url": "https://yt-project.org/data/gizmo_cosmology_plus.tar.gz" }, "gizmo_mhd_mwdisk.tar.gz": { "hash": "6f64248af83f50543173ade9c2c3f33ef1eae762b8e97196da5caf79ba4c5651", "load_kwargs": {}, "load_name": "gizmo_mhd_mwdisk.hdf5", "url": "https://yt-project.org/data/gizmo_mhd_mwdisk.tar.gz" }, "gizmo_zeldovich.tar.gz": { "hash": "5f1a73ead736024ffb6c099f84e834ec927d2818c93d7c775d9ff625adaa7c51", "load_kwargs": {}, "load_name": "snapshot_076_wi_gizver.hdf5", "url": "https://yt-project.org/data/gizmo_zeldovich.tar.gz" }, "grs-50-cube.fits.gz": { "hash": "1aff152f616d2626c8515c1493e65f07843d9b5f2ba73b7e4271a2dc6a9e6da7", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/grs-50-cube.fits.gz" }, "halo1e11_run1.00400.tar.gz": { "hash": "414d05f9d5aa2dc2811e55fab59ad2c1426ec51675aaf41db773b24cd2f0aa5f", "load_kwargs": {}, "load_name": "halo1e11_run1.00400", "url": "https://yt-project.org/data/halo1e11_run1.00400.tar.gz" }, "hello-0210.tar.gz": { "hash": "d18964e339c8ddc2a56859edd2cd853648b914e63434a59d4a457a876d9496f3", "load_kwargs": {}, "load_name": "hello-0210.block_list", "url": "https://yt-project.org/data/hello-0210.tar.gz" }, "m33_hi.fits.gz": { "hash": "6a44b6c352a5c98c58f0b2b7498fd87e2430c530971eaff68b843146d82d6609", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/m33_hi.fits.gz" }, "maestro_subCh_plt00248.tar.gz": { "hash": "6de1b63b63ce9738e4596ccf4cc75087d5029c892c07f40c2c69887e76b8ec1a", "load_kwargs": {}, "load_name": "maestro_subCh_plt00248", "url": "https://yt-project.org/data/maestro_subCh_plt00248.tar.gz" }, "maestro_xrb_lores_23437.tar.gz": { "hash": "a17b63609a2c50a829e26420f093ee898237b1ef8256ef38ee8be56a6f824460", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/maestro_xrb_lores_23437.tar.gz" }, "medium_tipsy.tar.gz": { "hash": "b77c1802c5ea6cf3ab3a698a741fbfdbbcdb561bf57be2fe7a3c9d00980ff438", "load_kwargs": {}, "load_name": "g1536.00256", "url": "https://yt-project.org/data/medium_tipsy.tar.gz" }, "nyx_sedov_plt00086.tgz": { "hash": "308f6b2a81363c5aada35941c199f149d8688f8ce65803f33d6dedce43a57a1c", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/nyx_sedov_plt00086.tgz" }, "nyx_small.tar.gz": { "hash": "1eefb443c2de6d9c6f4546bb87e1bde89ae98adb97c860a6065ac3177ecf1127", "load_kwargs": {}, "load_name": "nyx_small_00000", "url": "https://yt-project.org/data/nyx_small.tar.gz" }, "output_00080.tar.gz": { "hash": "175b18fbf72246c36442d7886f04194e1ea4d9d0b2bac2d5568a887d5f51bf29", "load_kwargs": {}, "load_name": "info_00080.txt", "url": "https://yt-project.org/data/output_00080.tar.gz" }, "output_00080_halos.tar.gz": { "hash": "fb8f573ea7cb183e1531091b3fce91cf7015cfdf5ccbce9ef2742e3135b52d4c", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/output_00080_halos.tar.gz" }, "output_00101.tar.gz": { "hash": "7ff2287c225d4e68f7ec04efd183732bc7ae5a2686c0d7626b4f9f4b6361f6df", "load_kwargs": {}, "load_name": "info_00101.txt", "url": "https://yt-project.org/data/output_00101.tar.gz" }, "owls_fof_halos.tar.gz": { "hash": "7ed6bd5f2eab16e8614f30676d7a0129c5fda98f994c06000e9fae8275a14c90", "load_kwargs": {}, "load_name": "groups_008/group_008.0.hdf5", "url": "https://yt-project.org/data/owls_fof_halos.tar.gz" }, "parthenon_advection.tar.gz": { "hash": "93e9e747cc9c0f230f87ea960a116c7e8c117b348f1488d72402aaaa906e4e89", "load_kwargs": {}, "load_name": "advection_2d.out0.final.phdf", "url": "https://yt-project.org/data/parthenon_advection.tar.gz" }, "ramses_empty_record.tar.gz": { "hash": "1d119d8afa144abce5b980e5ba47c45bdfe5c851de8c7a43823b62524b0bd6e0", "load_kwargs": {}, "load_name": "output_00003/info_00003.txt", "url": "https://yt-project.org/data/ramses_empty_record.tar.gz" }, "ramses_extra_fields_small.tar.gz": { "hash": "8d2dde6e6150f0767ebe3ace84e2e790503154c5d90474008afdfecbac68ffed", "load_kwargs": {}, "load_name": "output_00001/info_00001.txt", "url": "https://yt-project.org/data/ramses_extra_fields_small.tar.gz" }, "ramses_mhd_128.tar.gz": { "hash": "9bb48f090e2fdb795b2be49f571edecdd944c389c62283e5357a226406504d76", "load_kwargs": {}, "load_name": "output_00027/info_00027.txt", "url": "https://yt-project.org/data/ramses_mhd_128.tar.gz" }, "ramses_mhd_amr.tar.gz": { "hash": "bd7e0a11f971e246a3393272be4e4b7e0d84e85351bdd2acac2bd8025cf00774", "load_kwargs": {}, "load_name": "output_00019/info_00019.txt", "url": "https://yt-project.org/data/ramses_mhd_amr.tar.gz" }, "ramses_new_format.tar.gz": { "hash": "6ef426587454846456473c2026229d620e588a1fa9cbe409a0eb0a11517958c6", "load_kwargs": {}, "load_name": "output_00002/info_00002.txt", "url": "https://yt-project.org/data/ramses_new_format.tar.gz" }, "ramses_rt_00088.tar.gz": { "hash": "bdeb1480aa3e669f972af982ad89cd17f45749a90e3ba74dc2370201bdda4b3c", "load_kwargs": {}, "load_name": "output_00088/info_00088.txt", "url": "https://yt-project.org/data/ramses_rt_00088.tar.gz" }, "ramses_sink_00016.tar.gz": { "hash": "e28306c6e6cfc84ac0fcc209bb04b9677efd1916a2a1bcd5cb0b3f1ec90ce83a", "load_kwargs": {}, "load_name": "output_00016/info_00016.txt", "url": "https://yt-project.org/data/ramses_sink_00016.tar.gz" }, "ramses_star_formation.tar.gz": { "hash": "7513bfe14e270de6f643623815d815cf9f4adf0bf7dfb33ff3bc20fcd3f965e6", "load_kwargs": {}, "load_name": "output_00013/info_00013.txt", "url": "https://yt-project.org/data/ramses_star_formation.tar.gz" }, "rockstar_halos.tar.gz": { "hash": "5090052faad3a397ab69d833a15b1779466e1893a024dca55de18bfd9aeae774", "load_kwargs": {}, "load_name": "halos_0.0.bin", "url": "https://yt-project.org/data/rockstar_halos.tar.gz" }, "sedov_1d_sph_plt00120.tar.gz": { "hash": "25e8316a64a747043c2c95bfaaf6092a5aa2748a8e31295fa86a0bb793e7c1af", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/sedov_1d_sph_plt00120.tar.gz" }, "sedov_tst_0004.tar.gz": { "hash": "f0c592efde0257efea77e743167439da6df44cde71a9917b305751a8818eabb5", "load_kwargs": {}, "load_name": "sedov/sedov_tst_0004.h5", "url": "https://yt-project.org/data/sedov_tst_0004.tar.gz" }, "sizmbhloz-clref04SNth-rs9_a0.9011.tar.gz": { "hash": "3fc14b8e48e9b80c4ea4b324c5815eccb4ca04fe90903ce77f1496838ad764d4", "load_kwargs": {}, "load_name": "sizmbhloz-clref04SNth-rs9_a0.9011.art", "url": "https://yt-project.org/data/sizmbhloz-clref04SNth-rs9_a0.9011.tar.gz" }, "SmartStars.tar.gz": { "hash": "990ff61aa85408d33c4c6be7181e1f64fcd738526dcf0e34be5605d8bb158605", "load_kwargs": {}, "load_name": "DD0100/output_0100", "url": "https://yt-project.org/data/SmartStars.tar.gz" }, "snapshot_010.tar.gz": { "hash": "c98fa744de250d02833b643e3250dce2cf2c7a622c42c96b201024ee28582ba9", "load_kwargs": {}, "load_name": "snapshot_010", "url": "https://yt-project.org/data/snapshot_010.tar.gz" }, "snapshot_028_z000p000.tar.gz": { "hash": "35b00963a0b34f315054a7b254854859f626155dc368ac2c83158c15ce73e6d1", "load_kwargs": {}, "load_name": "snap_028_z000p000.0.hdf5", "url": "https://yt-project.org/data/snapshot_028_z000p000.tar.gz" }, "snapshot_033.tar.gz": { "hash": "f15788bd298c1f292548d2773f8232ad1f2e41723e210d200b479cb97c4d21bd", "load_kwargs": {}, "load_name": "snap_033.0.hdf5", "url": "https://yt-project.org/data/snapshot_033.tar.gz" }, "snipshot_399_z000p000.tar.gz": { "hash": "a4009624898d9926a90dffa79f9eaf8fe3bde9ba5adaf40577bc36d932016672", "load_kwargs": {}, "load_name": "snip_399_z000p000.0.hdf5", "url": "https://yt-project.org/data/snipshot_399_z000p000.tar.gz" }, "test_outputs.tar.gz": { "hash": "f7f59ee7e8b9e45d42e005b8116f896ce85ea1ea4a7a1c9b44c6de5b2f09a625", "load_kwargs": {}, "load_name": "RadTube/plt00500", "url": "https://yt-project.org/data/test_outputs.tar.gz" }, "tiny_fof_halos.tar.gz": { "hash": "48555ba2c482be88d7944a79ecf27967b517366d4d1075a7a5639e6d2339d8f0", "load_kwargs": {}, "load_name": "DD0045/DD0045.0.h5", "url": "https://yt-project.org/data/tiny_fof_halos.tar.gz" }, "xrb_spherical_smallplt00010.tar.gz": { "hash": "27ede5ed03f7c89b2afac03a368beb56d5f25f0c7c95b81f14f071a54a795783", "load_kwargs": {}, "load_name": null, "url": "https://yt-project.org/data/xrb_spherical_smallplt00010.tar.gz" }, "ytdata_test.tar.gz": { "hash": "cafb2b06ab3190ba17909585b58a4724e25f27ac72f11d6dff1a482146eb8958", "load_kwargs": {}, "load_name": "slice.h5", "url": "https://yt-project.org/data/ytdata_test.tar.gz" } } yt-project-yt-f043ac8/yt/startup_tasks.py000066400000000000000000000124711510711153200205740ustar00rootroot00000000000000# This handles the command line. import argparse import os import signal import sys from yt.config import ytcfg from yt.funcs import ( mylog, paste_traceback, paste_traceback_detailed, signal_ipython, signal_print_traceback, ) from yt.utilities import rpdb exe_name = os.path.basename(sys.executable) # At import time, we determined whether or not we're being run in parallel. def turn_on_parallelism(): parallel_capable = False try: # we import this to check if mpi4py is installed from mpi4py import MPI # NOQA except ImportError as e: mylog.error( "Warning: Attempting to turn on parallelism, " "but mpi4py import failed. Try pip install mpi4py." ) raise e # Now we have to turn on the parallelism from the perspective of the # parallel_analysis_interface from yt.utilities.parallel_tools.parallel_analysis_interface import ( enable_parallelism, ) parallel_capable = enable_parallelism() return parallel_capable # This fallback is for Paraview: # We use two signals, SIGUSR1 and SIGUSR2. In a non-threaded environment, # we set up handlers to process these by printing the current stack and to # raise a RuntimeError. The latter can be used, inside pdb, to catch an error # and then examine the current stack. try: signal.signal(signal.SIGUSR1, signal_print_traceback) mylog.debug("SIGUSR1 registered for traceback printing") signal.signal(signal.SIGUSR2, signal_ipython) mylog.debug("SIGUSR2 registered for IPython Insertion") except (ValueError, RuntimeError, AttributeError): # Not in main thread pass class SetExceptionHandling(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): # If we recognize one of the arguments on the command line as indicating a # different mechanism for handling tracebacks, we attach one of those handlers # and remove the argument from sys.argv. # if self.dest == "paste": sys.excepthook = paste_traceback mylog.debug("Enabling traceback pasting") elif self.dest == "paste-detailed": sys.excepthook = paste_traceback_detailed mylog.debug("Enabling detailed traceback pasting") elif self.dest == "detailed": import cgitb cgitb.enable(format="text") mylog.debug("Enabling detailed traceback reporting") elif self.dest == "rpdb": sys.excepthook = rpdb.rpdb_excepthook mylog.debug("Enabling remote debugging") class SetConfigOption(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): param, val = values.split("=") mylog.debug("Overriding config: %s = %s", param, val) ytcfg["yt", param] = val if param == "log_level": # special case mylog.setLevel(int(val)) class YTParser(argparse.ArgumentParser): def error(self, message): """error(message: string) Prints a help message that is more detailed than the argparse default and then exits. """ self.print_help(sys.stderr) self.exit(2, f"{self.prog}: error: {message}\n") parser = YTParser(description="yt command line arguments") parser.add_argument( "--config", action=SetConfigOption, help="Set configuration option, in the form param=value", ) parser.add_argument( "--paste", action=SetExceptionHandling, help="Paste traceback to paste.yt-project.org", nargs=0, ) parser.add_argument( "--paste-detailed", action=SetExceptionHandling, help="Paste a detailed traceback with local variables to " + "paste.yt-project.org", nargs=0, ) parser.add_argument( "--detailed", action=SetExceptionHandling, help="Display detailed traceback.", nargs=0, ) parser.add_argument( "--rpdb", action=SetExceptionHandling, help="Enable remote pdb interaction (for parallel debugging).", nargs=0, ) parser.add_argument( "--parallel", action="store_true", default=False, dest="parallel", help="Run in MPI-parallel mode (must be launched as an MPI task)", ) if not hasattr(sys, "argv") or sys.argv is None: sys.argv = [] unparsed_args: list[str] = [] parallel_capable = False if not ytcfg.get("yt", "internals", "command_line"): opts, unparsed_args = parser.parse_known_args() # THIS IS NOT SUCH A GOOD IDEA: # sys.argv = [a for a in unparsed_args] if opts.parallel: parallel_capable = turn_on_parallelism() subparsers = parser.add_subparsers( title="subcommands", dest="subcommands", description="Valid subcommands", ) else: subparsers = parser.add_subparsers( title="subcommands", dest="subcommands", description="Valid subcommands", ) def print_help(*args, **kwargs): parser.print_help() help_parser = subparsers.add_parser("help", help="Print help message") help_parser.set_defaults(func=print_help) if parallel_capable: pass elif ( exe_name in ["mpi4py", "embed_enzo", "python{}.{}-mpi".format(*sys.version_info)] or "_parallel" in dir(sys) or any("ipengine" in arg for arg in sys.argv) or any("cluster-id" in arg for arg in sys.argv) ): parallel_capable = turn_on_parallelism() else: parallel_capable = False yt-project-yt-f043ac8/yt/testing.py000066400000000000000000001765031510711153200173510ustar00rootroot00000000000000import hashlib import itertools as it import os import pickle import shutil import sys import tempfile import unittest from collections.abc import Callable, Mapping from functools import wraps from importlib.util import find_spec from shutil import which from typing import TYPE_CHECKING, TypeVar from unittest import SkipTest import matplotlib import numpy as np import numpy.typing as npt from more_itertools import always_iterable from numpy.random import RandomState from unyt.exceptions import UnitOperationError from yt._maintenance.deprecation import issue_deprecation_warning from yt.config import ytcfg from yt.frontends.stream.data_structures import StreamParticlesDataset from yt.funcs import is_sequence from yt.loaders import load, load_particles from yt.units.yt_array import YTArray, YTQuantity if TYPE_CHECKING: from collections.abc import Mapping from yt._typing import AnyFieldKey ANSWER_TEST_TAG = "answer_test" # Expose assert_true and assert_less_equal from unittest.TestCase # this is adopted from nose. Doing this here allows us to avoid importing # nose at the top level. def _deprecated_assert_func(func): @wraps(func) def retf(*args, **kwargs): issue_deprecation_warning( f"yt.testing.{func.__name__} is deprecated", since="4.2", stacklevel=3, ) return func(*args, **kwargs) return retf class _Dummy(unittest.TestCase): def nop(self): pass _t = _Dummy("nop") assert_true = _deprecated_assert_func(_t.assertTrue) assert_less_equal = _deprecated_assert_func(_t.assertLessEqual) def assert_rel_equal(a1, a2, decimals, err_msg="", verbose=True): from numpy.testing import assert_almost_equal # We have nan checks in here because occasionally we have fields that get # weighted without non-zero weights. I'm looking at you, particle fields! if isinstance(a1, np.ndarray): assert a1.size == a2.size # Mask out NaNs assert (np.isnan(a1) == np.isnan(a2)).all() a1[np.isnan(a1)] = 1.0 a2[np.isnan(a2)] = 1.0 # Mask out 0 ind1 = np.array(np.abs(a1) < np.finfo(a1.dtype).eps) ind2 = np.array(np.abs(a2) < np.finfo(a2.dtype).eps) assert (ind1 == ind2).all() a1[ind1] = 1.0 a2[ind2] = 1.0 elif np.any(np.isnan(a1)) and np.any(np.isnan(a2)): return True if not isinstance(a1, np.ndarray) and a1 == a2 == 0.0: # NANS! a1 = a2 = 1.0 return assert_almost_equal( np.array(a1) / np.array(a2), 1.0, decimals, err_msg=err_msg, verbose=verbose ) # tested: volume integral is 1. def cubicspline_python( x: float | npt.NDArray[np.floating], ) -> npt.NDArray[np.floating]: """ cubic spline SPH kernel function for testing against more effiecient cython methods Parameters ---------- x: impact parameter / smoothing length [dimenionless] Returns ------- value of the kernel function """ # C is 8/pi _c = 8.0 / np.pi x = np.asarray(x) kernel = np.zeros(x.shape, dtype=x.dtype) half1 = np.where(np.logical_and(x >= 0.0, x <= 0.5)) kernel[half1] = 1.0 - 6.0 * x[half1] ** 2 * (1.0 - x[half1]) half2 = np.where(np.logical_and(x > 0.5, x <= 1.0)) kernel[half2] = 2.0 * (1.0 - x[half2]) ** 3 return kernel * _c def integrate_kernel( kernelfunc: Callable[ [float | npt.NDArray[np.floating]], float | npt.NDArray[np.floating] ], b: float | npt.NDArray[np.floating], hsml: float | npt.NDArray[np.floating], ) -> npt.NDArray[np.floating]: """ integrates a kernel function over a line passing entirely through it Parameters: ----------- kernelfunc: the kernel function to integrate b: impact parameter hsml: smoothing length [same units as impact parameter] Returns: -------- the integral of the SPH kernel function. units: 1 / units of b and hsml """ pre = 1.0 / hsml**2 x = b / hsml xmax = np.sqrt(1.0 - x**2) xmin = -1.0 * xmax xe = np.linspace(xmin, xmax, 500) # shape: 500, x.shape xc = 0.5 * (xe[:-1, ...] + xe[1:, ...]) dx = np.diff(xe, axis=0) spv = kernelfunc(np.sqrt(xc**2 + x**2)) integral = np.sum(spv * dx, axis=0) return np.atleast_1d(pre * integral) _zeroperiods = np.array([0.0, 0.0, 0.0]) _FloatingT = TypeVar("_FloatingT", bound=np.floating) def distancematrix( pos3_i0: npt.NDArray[_FloatingT], pos3_i1: npt.NDArray[_FloatingT], periodic: tuple[bool, bool, bool] = (True,) * 3, periods: npt.NDArray[_FloatingT] = _zeroperiods, ) -> npt.NDArray[_FloatingT]: """ Calculates the distances between two arrays of points. Parameters: ---------- pos3_i0: shape (first number of points, 3) positions of the first set of points. The second index is for positions along the different cartesian axes pos3_i1: shape (second number of points, 3) as pos3_i0, but for the second set of points periodic: are the positions along each axis periodic (True) or not periods: the periods along each axis. Ignored if positions in a given direction are not periodic. Returns: -------- a 2D-array of distances between postions `pos3_i0` (changes along index 0) and `pos3_i1` (changes along index 1) """ d2 = np.zeros((len(pos3_i0), len(pos3_i1)), dtype=pos3_i0.dtype) for ax in range(3): # 'center on' pos3_i1 _d = pos3_i0[:, ax, np.newaxis] - pos3_i1[np.newaxis, :, ax] if periodic[ax]: _period = periods[ax] _d += 0.5 * _period # center on half box size _d %= _period # wrap coordinate to 0 -- boxsize range _d -= 0.5 * _period # center back to zero d2 += _d**2 return np.sqrt(d2) def amrspace(extent, levels=7, cells=8): """Creates two numpy arrays representing the left and right bounds of an AMR grid as well as an array for the AMR level of each cell. Parameters ---------- extent : array-like This a sequence of length 2*ndims that is the bounds of each dimension. For example, the 2D unit square would be given by [0.0, 1.0, 0.0, 1.0]. A 3D cylindrical grid may look like [0.0, 2.0, -1.0, 1.0, 0.0, 2*np.pi]. levels : int or sequence of ints, optional This is the number of AMR refinement levels. If given as a sequence (of length ndims), then each dimension will be refined down to this level. All values in this array must be the same or zero. A zero valued dimension indicates that this dim should not be refined. Taking the 3D cylindrical example above if we don't want refine theta but want r and z at 5 we would set levels=(5, 5, 0). cells : int, optional This is the number of cells per refinement level. Returns ------- left : float ndarray, shape=(npoints, ndims) The left AMR grid points. right : float ndarray, shape=(npoints, ndims) The right AMR grid points. level : int ndarray, shape=(npoints,) The AMR level for each point. Examples -------- >>> l, r, lvl = amrspace([0.0, 2.0, 1.0, 2.0, 0.0, 3.14], levels=(3, 3, 0), cells=2) >>> print(l) [[ 0. 1. 0. ] [ 0.25 1. 0. ] [ 0. 1.125 0. ] [ 0.25 1.125 0. ] [ 0.5 1. 0. ] [ 0. 1.25 0. ] [ 0.5 1.25 0. ] [ 1. 1. 0. ] [ 0. 1.5 0. ] [ 1. 1.5 0. ]] """ extent = np.asarray(extent, dtype="f8") dextent = extent[1::2] - extent[::2] ndims = len(dextent) if isinstance(levels, int): minlvl = maxlvl = levels levels = np.array([levels] * ndims, dtype="int32") else: levels = np.asarray(levels, dtype="int32") minlvl = levels.min() maxlvl = levels.max() if minlvl != maxlvl and (minlvl != 0 or {minlvl, maxlvl} != set(levels)): raise ValueError("all levels must have the same value or zero.") dims_zero = levels == 0 dims_nonzero = ~dims_zero ndims_nonzero = dims_nonzero.sum() npoints = (cells**ndims_nonzero - 1) * maxlvl + 1 left = np.empty((npoints, ndims), dtype="float64") right = np.empty((npoints, ndims), dtype="float64") level = np.empty(npoints, dtype="int32") # fill zero dims left[:, dims_zero] = extent[::2][dims_zero] right[:, dims_zero] = extent[1::2][dims_zero] # fill non-zero dims dcell = 1.0 / cells left_slice = tuple( ( slice(extent[2 * n], extent[2 * n + 1], extent[2 * n + 1]) if dims_zero[n] else slice(0.0, 1.0, dcell) ) for n in range(ndims) ) right_slice = tuple( ( slice(extent[2 * n + 1], extent[2 * n], -extent[2 * n + 1]) if dims_zero[n] else slice(dcell, 1.0 + dcell, dcell) ) for n in range(ndims) ) left_norm_grid = np.reshape(np.mgrid[left_slice].T.flat[ndims:], (-1, ndims)) lng_zero = left_norm_grid[:, dims_zero] lng_nonzero = left_norm_grid[:, dims_nonzero] right_norm_grid = np.reshape(np.mgrid[right_slice].T.flat[ndims:], (-1, ndims)) rng_zero = right_norm_grid[:, dims_zero] rng_nonzero = right_norm_grid[:, dims_nonzero] level[0] = maxlvl left[0, :] = extent[::2] right[0, dims_zero] = extent[1::2][dims_zero] right[0, dims_nonzero] = (dcell**maxlvl) * dextent[dims_nonzero] + extent[::2][ dims_nonzero ] for i, lvl in enumerate(range(maxlvl, 0, -1)): start = (cells**ndims_nonzero - 1) * i + 1 stop = (cells**ndims_nonzero - 1) * (i + 1) + 1 dsize = dcell ** (lvl - 1) * dextent[dims_nonzero] level[start:stop] = lvl left[start:stop, dims_zero] = lng_zero left[start:stop, dims_nonzero] = lng_nonzero * dsize + extent[::2][dims_nonzero] right[start:stop, dims_zero] = rng_zero right[start:stop, dims_nonzero] = ( rng_nonzero * dsize + extent[::2][dims_nonzero] ) return left, right, level def _check_field_unit_args_helper(args: dict, default_args: dict): values = list(args.values()) keys = list(args.keys()) if all(v is None for v in values): for key in keys: args[key] = default_args[key] elif None in values: raise ValueError( "Error in creating a fake dataset:" f" either all or none of the following arguments need to specified: {keys}." ) elif any(len(v) != len(values[0]) for v in values): raise ValueError( "Error in creating a fake dataset:" f" all the following arguments must have the same length: {keys}." ) return list(args.values()) _fake_random_ds_default_fields = ("density", "velocity_x", "velocity_y", "velocity_z") _fake_random_ds_default_units = ("g/cm**3", "cm/s", "cm/s", "cm/s") _fake_random_ds_default_negative = (False, False, False, False) def fake_random_ds( ndims, peak_value=1.0, fields=None, units=None, particle_fields=None, particle_field_units=None, negative=False, nprocs=1, particles=0, length_unit=1.0, unit_system="cgs", bbox=None, default_species_fields=None, ): from yt.loaders import load_uniform_grid prng = RandomState(0x4D3D3D3) if not is_sequence(ndims): ndims = [ndims, ndims, ndims] else: assert len(ndims) == 3 if not is_sequence(negative): if fields: negative = [negative for f in fields] else: negative = None fields, units, negative = _check_field_unit_args_helper( { "fields": fields, "units": units, "negative": negative, }, { "fields": _fake_random_ds_default_fields, "units": _fake_random_ds_default_units, "negative": _fake_random_ds_default_negative, }, ) offsets = [] for n in negative: if n: offsets.append(0.5) else: offsets.append(0.0) data = {} for field, offset, u in zip(fields, offsets, units, strict=True): v = (prng.random_sample(ndims) - offset) * peak_value if field[0] == "all": v = v.ravel() data[field] = (v, u) if particles: if particle_fields is not None: for field, unit in zip(particle_fields, particle_field_units, strict=True): if field in ("particle_position", "particle_velocity"): data["io", field] = (prng.random_sample((int(particles), 3)), unit) else: data["io", field] = (prng.random_sample(size=int(particles)), unit) else: for f in (f"particle_position_{ax}" for ax in "xyz"): data["io", f] = (prng.random_sample(size=particles), "code_length") for f in (f"particle_velocity_{ax}" for ax in "xyz"): data["io", f] = (prng.random_sample(size=particles) - 0.5, "cm/s") data["io", "particle_mass"] = (prng.random_sample(particles), "g") ug = load_uniform_grid( data, ndims, length_unit=length_unit, nprocs=nprocs, unit_system=unit_system, bbox=bbox, default_species_fields=default_species_fields, ) return ug _geom_transforms = { # These are the bounds we want. Cartesian we just assume goes 0 .. 1. "cartesian": ((0.0, 0.0, 0.0), (1.0, 1.0, 1.0)), "spherical": ((0.0, 0.0, 0.0), (1.0, np.pi, 2 * np.pi)), "cylindrical": ((0.0, 0.0, 0.0), (1.0, 1.0, 2.0 * np.pi)), # rzt "polar": ((0.0, 0.0, 0.0), (1.0, 2.0 * np.pi, 1.0)), # rtz "geographic": ((-90.0, -180.0, 0.0), (90.0, 180.0, 1000.0)), # latlonalt "internal_geographic": ((-90.0, -180.0, 0.0), (90.0, 180.0, 1000.0)), # latlondep "spectral_cube": ((0.0, 0.0, 0.0), (1.0, 1.0, 1.0)), } _fake_amr_ds_default_fields = ("Density",) _fake_amr_ds_default_units = ("g/cm**3",) def fake_amr_ds( fields=None, units=None, geometry="cartesian", particles=0, length_unit=None, *, domain_left_edge=None, domain_right_edge=None, ): from yt.loaders import load_amr_grids fields, units = _check_field_unit_args_helper( { "fields": fields, "units": units, }, { "fields": _fake_amr_ds_default_fields, "units": _fake_amr_ds_default_units, }, ) prng = RandomState(0x4D3D3D3) default_LE, default_RE = _geom_transforms[geometry] LE = np.array(domain_left_edge or default_LE, dtype="float64") RE = np.array(domain_right_edge or default_RE, dtype="float64") data = [] for gspec in _amr_grid_index: level, left_edge, right_edge, dims = gspec left_edge = left_edge * (RE - LE) + LE right_edge = right_edge * (RE - LE) + LE gdata = { "level": level, "left_edge": left_edge, "right_edge": right_edge, "dimensions": dims, } for f, u in zip(fields, units, strict=True): gdata[f] = (prng.random_sample(dims), u) if particles: for i, f in enumerate(f"particle_position_{ax}" for ax in "xyz"): pdata = prng.random_sample(particles) pdata /= right_edge[i] - left_edge[i] pdata += left_edge[i] gdata["io", f] = (pdata, "code_length") for f in (f"particle_velocity_{ax}" for ax in "xyz"): gdata["io", f] = (prng.random_sample(particles) - 0.5, "cm/s") gdata["io", "particle_mass"] = (prng.random_sample(particles), "g") data.append(gdata) bbox = np.array([LE, RE]).T return load_amr_grids( data, [32, 32, 32], geometry=geometry, bbox=bbox, length_unit=length_unit ) _fake_particle_ds_default_fields = ( "particle_position_x", "particle_position_y", "particle_position_z", "particle_mass", "particle_velocity_x", "particle_velocity_y", "particle_velocity_z", ) _fake_particle_ds_default_units = ("cm", "cm", "cm", "g", "cm/s", "cm/s", "cm/s") _fake_particle_ds_default_negative = (False, False, False, False, True, True, True) def fake_particle_ds( fields=None, units=None, negative=None, npart=16**3, length_unit=1.0, data=None, ): from yt.loaders import load_particles prng = RandomState(0x4D3D3D3) if negative is not None and not is_sequence(negative): negative = [negative for f in fields] fields, units, negative = _check_field_unit_args_helper( { "fields": fields, "units": units, "negative": negative, }, { "fields": _fake_particle_ds_default_fields, "units": _fake_particle_ds_default_units, "negative": _fake_particle_ds_default_negative, }, ) offsets = [] for n in negative: if n: offsets.append(0.5) else: offsets.append(0.0) data = data if data else {} for field, offset, u in zip(fields, offsets, units, strict=True): if field in data: v = data[field] continue if "position" in field: v = prng.normal(loc=0.5, scale=0.25, size=npart) np.clip(v, 0.0, 1.0, v) v = prng.random_sample(npart) - offset data[field] = (v, u) bbox = np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]) ds = load_particles(data, 1.0, bbox=bbox) return ds def fake_tetrahedral_ds(): from yt.frontends.stream.sample_data.tetrahedral_mesh import ( _connectivity, _coordinates, ) from yt.loaders import load_unstructured_mesh prng = RandomState(0x4D3D3D3) # the distance from the origin node_data = {} dist = np.sum(_coordinates**2, 1) node_data["connect1", "test"] = dist[_connectivity] # each element gets a random number elem_data = {} elem_data["connect1", "elem"] = prng.rand(_connectivity.shape[0]) ds = load_unstructured_mesh( _connectivity, _coordinates, node_data=node_data, elem_data=elem_data ) return ds def fake_hexahedral_ds(fields=None): from yt.frontends.stream.sample_data.hexahedral_mesh import ( _connectivity, _coordinates, ) from yt.loaders import load_unstructured_mesh prng = RandomState(0x4D3D3D3) # the distance from the origin node_data = {} dist = np.sum(_coordinates**2, 1) node_data["connect1", "test"] = dist[_connectivity - 1] for field in always_iterable(fields): node_data["connect1", field] = dist[_connectivity - 1] # each element gets a random number elem_data = {} elem_data["connect1", "elem"] = prng.rand(_connectivity.shape[0]) ds = load_unstructured_mesh( _connectivity - 1, _coordinates, node_data=node_data, elem_data=elem_data ) return ds def small_fake_hexahedral_ds(): from yt.loaders import load_unstructured_mesh _coordinates = np.array( [ [-1.0, -1.0, -1.0], [0.0, -1.0, -1.0], [-0.0, 0.0, -1.0], [-1.0, -0.0, -1.0], [-1.0, -1.0, 0.0], [-0.0, -1.0, 0.0], [-0.0, 0.0, -0.0], [-1.0, 0.0, -0.0], ] ) _connectivity = np.array([[1, 2, 3, 4, 5, 6, 7, 8]]) # the distance from the origin node_data = {} dist = np.sum(_coordinates**2, 1) node_data["connect1", "test"] = dist[_connectivity - 1] ds = load_unstructured_mesh(_connectivity - 1, _coordinates, node_data=node_data) return ds def fake_stretched_ds(N=16): from yt.loaders import load_uniform_grid rng = np.random.default_rng(seed=0x4D3D3D3) data = {"density": rng.random((N, N, N))} cell_widths = [] for _ in range(3): cw = rng.random(N) cw /= cw.sum() cell_widths.append(cw) return load_uniform_grid( data, [N, N, N], bbox=np.array([[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]), cell_widths=cell_widths, ) def fake_vr_orientation_test_ds(N=96, scale=1): """ create a toy dataset that puts a sphere at (0,0,0), a single cube on +x, two cubes on +y, and three cubes on +z in a domain from [-1*scale,1*scale]**3. The lower planes (x = -1*scale, y = -1*scale, z = -1*scale) are also given non-zero values. This dataset allows you to easily explore orientations and handiness in VR and other renderings Parameters ---------- N : integer The number of cells along each direction scale : float A spatial scale, the domain boundaries will be multiplied by scale to test datasets that have spatial different scales (e.g. data in CGS units) """ from yt.loaders import load_uniform_grid xmin = ymin = zmin = -1.0 * scale xmax = ymax = zmax = 1.0 * scale dcoord = (xmax - xmin) / N arr = np.zeros((N, N, N), dtype=np.float64) arr[:, :, :] = 1.0e-4 bbox = np.array([[xmin, xmax], [ymin, ymax], [zmin, zmax]]) # coordinates -- in the notation data[i, j, k] x = (np.arange(N) + 0.5) * dcoord + xmin y = (np.arange(N) + 0.5) * dcoord + ymin z = (np.arange(N) + 0.5) * dcoord + zmin x3d, y3d, z3d = np.meshgrid(x, y, z, indexing="ij") # sphere at the origin c = np.array([0.5 * (xmin + xmax), 0.5 * (ymin + ymax), 0.5 * (zmin + zmax)]) r = np.sqrt((x3d - c[0]) ** 2 + (y3d - c[1]) ** 2 + (z3d - c[2]) ** 2) arr[r < 0.05] = 1.0 arr[abs(x3d - xmin) < 2 * dcoord] = 0.3 arr[abs(y3d - ymin) < 2 * dcoord] = 0.3 arr[abs(z3d - zmin) < 2 * dcoord] = 0.3 # single cube on +x xc = 0.75 * scale dx = 0.05 * scale idx = np.logical_and( np.logical_and(x3d > xc - dx, x3d < xc + dx), np.logical_and( np.logical_and(y3d > -dx, y3d < dx), np.logical_and(z3d > -dx, z3d < dx) ), ) arr[idx] = 1.0 # two cubes on +y dy = 0.05 * scale for yc in [0.65 * scale, 0.85 * scale]: idx = np.logical_and( np.logical_and(y3d > yc - dy, y3d < yc + dy), np.logical_and( np.logical_and(x3d > -dy, x3d < dy), np.logical_and(z3d > -dy, z3d < dy) ), ) arr[idx] = 0.8 # three cubes on +z dz = 0.05 * scale for zc in [0.5 * scale, 0.7 * scale, 0.9 * scale]: idx = np.logical_and( np.logical_and(z3d > zc - dz, z3d < zc + dz), np.logical_and( np.logical_and(x3d > -dz, x3d < dz), np.logical_and(y3d > -dz, y3d < dz) ), ) arr[idx] = 0.6 data = {"density": (arr, "g/cm**3")} ds = load_uniform_grid(data, arr.shape, bbox=bbox) return ds def fake_sph_orientation_ds(): """Returns an in-memory SPH dataset useful for testing This dataset should have one particle at the origin, one more particle along the x axis, two along y, and three along z. All particles will have non-overlapping smoothing regions with a radius of 0.25, masses of 1, and densities of 1, and zero velocity. """ from yt import load_particles npart = 7 # one particle at the origin, one particle along x-axis, two along y, # three along z data = { "particle_position_x": (np.array([0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), "cm"), "particle_position_y": (np.array([0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0]), "cm"), "particle_position_z": (np.array([0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0]), "cm"), "particle_mass": (np.ones(npart), "g"), "particle_velocity_x": (np.zeros(npart), "cm/s"), "particle_velocity_y": (np.zeros(npart), "cm/s"), "particle_velocity_z": (np.zeros(npart), "cm/s"), "smoothing_length": (0.25 * np.ones(npart), "cm"), "density": (np.ones(npart), "g/cm**3"), "temperature": (np.ones(npart), "K"), } bbox = np.array([[-4, 4], [-4, 4], [-4, 4]]) return load_particles(data=data, length_unit=1.0, bbox=bbox) def fake_sph_grid_ds(hsml_factor=1.0): """Returns an in-memory SPH dataset useful for testing This dataset should have 27 particles with the particles arranged uniformly on a 3D grid. The bottom left corner is (0.5,0.5,0.5) and the top right corner is (2.5,2.5,2.5). All particles will have non-overlapping smoothing regions with a radius of 0.05, masses of 1, and densities of 1, and zero velocity. """ from yt import load_particles npart = 27 x = np.empty(npart) y = np.empty(npart) z = np.empty(npart) tot = 0 for i in range(0, 3): for j in range(0, 3): for k in range(0, 3): x[tot] = i + 0.5 y[tot] = j + 0.5 z[tot] = k + 0.5 tot += 1 data = { "particle_position_x": (x, "cm"), "particle_position_y": (y, "cm"), "particle_position_z": (z, "cm"), "particle_mass": (np.ones(npart), "g"), "particle_velocity_x": (np.zeros(npart), "cm/s"), "particle_velocity_y": (np.zeros(npart), "cm/s"), "particle_velocity_z": (np.zeros(npart), "cm/s"), "smoothing_length": (0.05 * np.ones(npart) * hsml_factor, "cm"), "density": (np.ones(npart), "g/cm**3"), "temperature": (np.ones(npart), "K"), } bbox = np.array([[0, 3], [0, 3], [0, 3]]) return load_particles(data=data, length_unit=1.0, bbox=bbox) def constantmass(i: int, j: int, k: int) -> float: return 1.0 _xhat = np.array([1, 0, 0]) _yhat = np.array([0, 1, 0]) _zhat = np.array([0, 0, 1]) _floathalves = 0.5 * np.ones((3,), dtype=np.float64) def fake_sph_flexible_grid_ds( hsml_factor: float = 1.0, nperside: int = 3, periodic: bool = True, e1hat: np.ndarray = _xhat, e2hat: np.ndarray = _yhat, e3hat: np.ndarray = _zhat, offsets: np.ndarray = _floathalves, massgenerator: Callable[[int, int, int], float] = constantmass, unitrho: float = 1.0, bbox: np.ndarray | None = None, recenter: np.ndarray | None = None, ) -> StreamParticlesDataset: """Returns an in-memory SPH dataset useful for testing Parameters: ----------- hsml_factor: all particles have smoothing lengths of `hsml_factor` * 0.5 nperside: the dataset will have `nperside`**3 particles, arranged uniformly on a 3D grid periodic: are the positions taken to be periodic? (applies to all coordinate axes) e1hat: shape (3,) the first basis vector defining the 3D grid. If the basis vectors are not normalized to 1 or not orthogonal, the spacing or overlap between SPH particles will be affected, but this is allowed. e2hat: shape (3,) the second basis vector defining the 3D grid. (See `e1hat`.) e3hat: shape (3,) the third basis vector defining the 3D grid. (See `e1hat`.) offsets: shape (3,) the the zero point of the 3D grid along each coordinate axis massgenerator: a function assigning a mass to each particle, as a function of the e[1-3]hat indices, in order unitrho: defines the density for a particle with mass 1 ('g'), and the standard (uniform) grid `hsml_factor`. bbox: if np.ndarray, shape is (2, 3) the assumed enclosing volume of the particles. Should enclose all the coordinate values. If not specified, a bbox is defined which encloses all coordinates values with a margin. If `periodic`, the size of the `bbox` along each coordinate is also the period along that axis. recenter: if not `None`, after generating the grid, the positions are periodically shifted to move the old center to this positions. Useful for testing periodicity handling. This shift is relative to the halfway positions of the bbox edges. Returns: -------- A `StreamParticlesDataset` object with particle positions, masses, velocities (zero), smoothing lengths, and densities specified. Values are in cgs units. """ npart = nperside**3 pos = np.empty((npart, 3), dtype=np.float64) mass = np.empty((npart,), dtype=np.float64) for i in range(0, nperside): for j in range(0, nperside): for k in range(0, nperside): _pos = ( (offsets[0] + i) * e1hat + (offsets[1] + j) * e2hat + (offsets[2] + k) * e3hat ) ind = nperside**2 * i + nperside * j + k pos[ind, :] = _pos mass[ind] = massgenerator(i, j, k) rho = unitrho * mass if bbox is None: eps = 1e-3 margin = (1.0 + eps) * hsml_factor bbox = np.array( [ [np.min(pos[:, 0]) - margin, np.max(pos[:, 0]) + margin], [np.min(pos[:, 1]) - margin, np.max(pos[:, 1]) + margin], [np.min(pos[:, 2]) - margin, np.max(pos[:, 2]) + margin], ] ) if recenter is not None: periods = bbox[:, 1] - bbox[:, 0] # old center -> new position pos += -0.5 * periods[np.newaxis, :] + recenter[np.newaxis, :] # wrap coordinates -> all in [0, boxsize) range pos %= periods[np.newaxis, :] # shift back to original bbox range pos += (bbox[:, 0])[np.newaxis, :] if not periodic: # remove points outside bbox to avoid errors: okinds = np.ones(len(mass), dtype=bool) for ax in [0, 1, 2]: okinds &= pos[:, ax] < bbox[ax, 1] okinds &= pos[:, ax] >= bbox[ax, 0] npart = sum(okinds) else: okinds = np.ones((npart,), dtype=bool) data: Mapping[AnyFieldKey, tuple[np.ndarray, str]] = { "particle_position_x": (np.copy(pos[okinds, 0]), "cm"), "particle_position_y": (np.copy(pos[okinds, 1]), "cm"), "particle_position_z": (np.copy(pos[okinds, 2]), "cm"), "particle_mass": (np.copy(mass[okinds]), "g"), "particle_velocity_x": (np.zeros(npart), "cm/s"), "particle_velocity_y": (np.zeros(npart), "cm/s"), "particle_velocity_z": (np.zeros(npart), "cm/s"), "smoothing_length": (np.ones(npart) * 0.5 * hsml_factor, "cm"), "density": (np.copy(rho[okinds]), "g/cm**3"), } ds = load_particles( data=data, bbox=bbox, periodicity=(periodic,) * 3, length_unit=1.0, mass_unit=1.0, time_unit=1.0, velocity_unit=1.0, ) ds.kernel_name = "cubic" return ds def fake_random_sph_ds( npart: int, bbox: np.ndarray, periodic: bool | tuple[bool, bool, bool] = True, massrange: tuple[float, float] = (0.5, 2.0), hsmlrange: tuple[float, float] = (0.5, 2.0), unitrho: float = 1.0, ) -> StreamParticlesDataset: """Returns an in-memory SPH dataset useful for testing Parameters: ----------- npart: number of particles to generate bbox: shape: (3, 2), units: "cm" the assumed enclosing volume of the particles. Particle positions are drawn uniformly from these ranges. periodic: are the positions taken to be periodic? If a single value, that value is applied to all axes massrange: particle masses are drawn uniformly from this range (unit: "g") hsmlrange: units: "cm" particle smoothing lengths are drawn uniformly from this range unitrho: defines the density for a particle with mass 1 ("g"), and smoothing length 1 ("cm"). Returns: -------- A `StreamParticlesDataset` object with particle positions, masses, velocities (zero), smoothing lengths, and densities specified. Values are in cgs units. """ if not hasattr(periodic, "__len__"): periodic = (periodic,) * 3 gen = np.random.default_rng(seed=0) posx = gen.uniform(low=bbox[0][0], high=bbox[0][1], size=npart) posy = gen.uniform(low=bbox[1][0], high=bbox[1][1], size=npart) posz = gen.uniform(low=bbox[2][0], high=bbox[2][1], size=npart) mass = gen.uniform(low=massrange[0], high=massrange[1], size=npart) hsml = gen.uniform(low=hsmlrange[0], high=hsmlrange[1], size=npart) dens = mass / hsml**3 * unitrho data: Mapping[AnyFieldKey, tuple[np.ndarray, str]] = { "particle_position_x": (posx, "cm"), "particle_position_y": (posy, "cm"), "particle_position_z": (posz, "cm"), "particle_mass": (mass, "g"), "particle_velocity_x": (np.zeros(npart), "cm/s"), "particle_velocity_y": (np.zeros(npart), "cm/s"), "particle_velocity_z": (np.zeros(npart), "cm/s"), "smoothing_length": (hsml, "cm"), "density": (dens, "g/cm**3"), } ds = load_particles( data=data, bbox=bbox, periodicity=periodic, length_unit=1.0, mass_unit=1.0, time_unit=1.0, velocity_unit=1.0, ) ds.kernel_name = "cubic" return ds def construct_octree_mask(prng=RandomState(0x1D3D3D3), refined=None): # noqa B008 # Implementation taken from url: # http://docs.hyperion-rt.org/en/stable/advanced/indepth_oct.html if refined in (None, True): refined = [True] if not refined: refined = [False] return refined # Loop over subcells for _ in range(8): # Insert criterion for whether cell should be sub-divided. Here we # just use a random number to demonstrate. divide = prng.random_sample() < 0.12 # Append boolean to overall list refined.append(divide) # If the cell is sub-divided, recursively divide it further if divide: construct_octree_mask(prng, refined) return refined def fake_octree_ds( prng=RandomState(0x4D3D3D3), # noqa B008 refined=None, quantities=None, bbox=None, sim_time=0.0, length_unit=None, mass_unit=None, time_unit=None, velocity_unit=None, magnetic_unit=None, periodicity=(True, True, True), num_zones=2, partial_coverage=1, unit_system="cgs", ): from yt.loaders import load_octree octree_mask = np.asarray( construct_octree_mask(prng=prng, refined=refined), dtype=np.uint8 ) particles = np.sum(np.invert(octree_mask)) if quantities is None: quantities = {} quantities["gas", "density"] = prng.random_sample((particles, 1)) quantities["gas", "velocity_x"] = prng.random_sample((particles, 1)) quantities["gas", "velocity_y"] = prng.random_sample((particles, 1)) quantities["gas", "velocity_z"] = prng.random_sample((particles, 1)) ds = load_octree( octree_mask=octree_mask, data=quantities, bbox=bbox, sim_time=sim_time, length_unit=length_unit, mass_unit=mass_unit, time_unit=time_unit, velocity_unit=velocity_unit, magnetic_unit=magnetic_unit, periodicity=periodicity, partial_coverage=partial_coverage, num_zones=num_zones, unit_system=unit_system, ) return ds def add_noise_fields(ds): """Add 4 classes of noise fields to a dataset""" prng = RandomState(0x4D3D3D3) def _binary_noise(field, data): """random binary data""" return prng.randint(low=0, high=2, size=data.size).astype("float64") def _positive_noise(field, data): """random strictly positive data""" return prng.random_sample(data.size) + 1e-16 def _negative_noise(field, data): """random negative data""" return -prng.random_sample(data.size) def _even_noise(field, data): """random data with mixed signs""" return 2 * prng.random_sample(data.size) - 1 ds.add_field(("gas", "noise0"), _binary_noise, sampling_type="cell") ds.add_field(("gas", "noise1"), _positive_noise, sampling_type="cell") ds.add_field(("gas", "noise2"), _negative_noise, sampling_type="cell") ds.add_field(("gas", "noise3"), _even_noise, sampling_type="cell") def expand_keywords(keywords, full=False): """ expand_keywords is a means for testing all possible keyword arguments in the nosetests. Simply pass it a dictionary of all the keyword arguments and all of the values for these arguments that you want to test. It will return a list of kwargs dicts containing combinations of the various kwarg values you passed it. These can then be passed to the appropriate function in nosetests. If full=True, then every possible combination of keywords is produced, otherwise, every keyword option is included at least once in the output list. Be careful, by using full=True, you may be in for an exponentially larger number of tests! Parameters ---------- keywords : dict a dictionary where the keys are the keywords for the function, and the values of each key are the possible values that this key can take in the function full : bool if set to True, every possible combination of given keywords is returned Returns ------- array of dicts An array of dictionaries to be individually passed to the appropriate function matching these kwargs. Examples -------- >>> keywords = {} >>> keywords["dpi"] = (50, 100, 200) >>> keywords["cmap"] = ("cmyt.arbre", "cmyt.kelp") >>> list_of_kwargs = expand_keywords(keywords) >>> print(list_of_kwargs) array([{'cmap': 'cmyt.arbre', 'dpi': 50}, {'cmap': 'cmyt.kelp', 'dpi': 100}, {'cmap': 'cmyt.arbre', 'dpi': 200}], dtype=object) >>> list_of_kwargs = expand_keywords(keywords, full=True) >>> print(list_of_kwargs) array([{'cmap': 'cmyt.arbre', 'dpi': 50}, {'cmap': 'cmyt.arbre', 'dpi': 100}, {'cmap': 'cmyt.arbre', 'dpi': 200}, {'cmap': 'cmyt.kelp', 'dpi': 50}, {'cmap': 'cmyt.kelp', 'dpi': 100}, {'cmap': 'cmyt.kelp', 'dpi': 200}], dtype=object) >>> for kwargs in list_of_kwargs: ... write_projection(*args, **kwargs) """ issue_deprecation_warning( "yt.testing.expand_keywords is deprecated", since="4.2", stacklevel=3 ) # if we want every possible combination of keywords, use iter magic if full: keys = sorted(keywords) list_of_kwarg_dicts = np.array( [ dict(zip(keys, prod, strict=True)) for prod in it.product(*(keywords[key] for key in keys)) ] ) # if we just want to probe each keyword, but not necessarily every # combination else: # Determine the maximum number of values any of the keywords has num_lists = 0 for val in keywords.values(): if isinstance(val, str): num_lists = max(1.0, num_lists) else: num_lists = max(len(val), num_lists) # Construct array of kwargs dicts, each element of the list is a different # **kwargs dict. each kwargs dict gives a different combination of # the possible values of the kwargs # initialize array list_of_kwarg_dicts = np.array([{} for x in range(num_lists)]) # fill in array for i in np.arange(num_lists): list_of_kwarg_dicts[i] = {} for key in keywords.keys(): # if it's a string, use it (there's only one) if isinstance(keywords[key], str): list_of_kwarg_dicts[i][key] = keywords[key] # if there are more options, use the i'th val elif i < len(keywords[key]): list_of_kwarg_dicts[i][key] = keywords[key][i] # if there are not more options, use the 0'th val else: list_of_kwarg_dicts[i][key] = keywords[key][0] return list_of_kwarg_dicts def skip(reason: str): # a drop-in replacement for pytest.mark.skip decorator with nose-compatibility def dec(func): @wraps(func) def wrapper(*args, **kwargs): if os.getenv("PYTEST_VERSION") is not None: # this is the recommended way to detect a pytest session # https://docs.pytest.org/en/stable/reference/reference.html#envvar-PYTEST_VERSION import pytest pytest.skip(reason) else: # running from nose, or unittest raise SkipTest(reason) return wrapper return dec def skipif(condition: bool, reason: str): # a drop-in replacement for pytest.mark.skipif decorator with nose-compatibility def dec(func): if condition: return skip(reason)(func) else: return func return dec def requires_module(module): """ Decorator that takes a module name as an argument and tries to import it. If the module imports without issue, the function is returned, but if not, a null function is returned. This is so tests that depend on certain modules being imported will not fail if the module is not installed on the testing platform. """ return skipif(find_spec(module) is None, reason=f"Missing required module {module}") def requires_module_pytest(*module_names): """ This is a replacement for yt.testing.requires_module that's compatible with pytest, and accepts an arbitrary number of requirements to avoid stacking decorators Important: this is meant to decorate test functions only, it won't work as a decorator to fixture functions. It's meant to be imported as >>> from yt.testing import requires_module_pytest as requires_module So that it can be later renamed to `requires_module`. """ # note: import pytest here so that it is not a hard requirement for # importing yt.testing see https://github.com/yt-project/yt/issues/4507 import pytest def deco(func): missing = [name for name in module_names if find_spec(name) is None] # note that order between these two decorators matters @pytest.mark.skipif( missing, reason=f"missing requirement(s): {', '.join(missing)}", ) @wraps(func) def inner_func(*args, **kwargs): return func(*args, **kwargs) return inner_func return deco def requires_file(req_file): condition = ( not os.path.exists(req_file) and not os.path.exists(os.path.join(ytcfg.get("yt", "test_data_dir"), req_file)) and not ytcfg.get("yt", "internals", "strict_requires") ) return skipif(condition, reason=f"Missing required file {req_file}") def disable_dataset_cache(func): @wraps(func) def newfunc(*args, **kwargs): restore_cfg_state = False if not ytcfg.get("yt", "skip_dataset_cache"): ytcfg["yt", "skip_dataset_cache"] = True restore_cfg_state = True rv = func(*args, **kwargs) if restore_cfg_state: ytcfg["yt", "skip_dataset_cache"] = False return rv return newfunc @disable_dataset_cache def units_override_check(fn): from numpy.testing import assert_equal units_list = ["length", "time", "mass", "velocity", "magnetic", "temperature"] ds1 = load(fn) units_override = {} attrs1 = [] attrs2 = [] for u in units_list: unit_attr = getattr(ds1, f"{u}_unit", None) if unit_attr is not None: attrs1.append(unit_attr) units_override[f"{u}_unit"] = (unit_attr.v, unit_attr.units) del ds1 ds2 = load(fn, units_override=units_override) assert len(ds2.units_override) > 0 for u in units_list: unit_attr = getattr(ds2, f"{u}_unit", None) if unit_attr is not None: attrs2.append(unit_attr) assert_equal(attrs1, attrs2) # This is an export of the 40 grids in IsolatedGalaxy that are of level 4 or # lower. It's just designed to give a sample AMR index to deal with. _amr_grid_index = [ [0, [0.0, 0.0, 0.0], [1.0, 1.0, 1.0], [32, 32, 32]], [1, [0.25, 0.21875, 0.25], [0.5, 0.5, 0.5], [16, 18, 16]], [1, [0.5, 0.21875, 0.25], [0.75, 0.5, 0.5], [16, 18, 16]], [1, [0.21875, 0.5, 0.25], [0.5, 0.75, 0.5], [18, 16, 16]], [1, [0.5, 0.5, 0.25], [0.75, 0.75, 0.5], [16, 16, 16]], [1, [0.25, 0.25, 0.5], [0.5, 0.5, 0.75], [16, 16, 16]], [1, [0.5, 0.25, 0.5], [0.75, 0.5, 0.75], [16, 16, 16]], [1, [0.25, 0.5, 0.5], [0.5, 0.75, 0.75], [16, 16, 16]], [1, [0.5, 0.5, 0.5], [0.75, 0.75, 0.75], [16, 16, 16]], [2, [0.5, 0.5, 0.5], [0.71875, 0.71875, 0.71875], [28, 28, 28]], [3, [0.5, 0.5, 0.5], [0.6640625, 0.65625, 0.6796875], [42, 40, 46]], [4, [0.5, 0.5, 0.5], [0.59765625, 0.6015625, 0.6015625], [50, 52, 52]], [2, [0.28125, 0.5, 0.5], [0.5, 0.734375, 0.71875], [28, 30, 28]], [3, [0.3359375, 0.5, 0.5], [0.5, 0.671875, 0.6640625], [42, 44, 42]], [4, [0.40625, 0.5, 0.5], [0.5, 0.59765625, 0.59765625], [48, 50, 50]], [2, [0.5, 0.28125, 0.5], [0.71875, 0.5, 0.71875], [28, 28, 28]], [3, [0.5, 0.3359375, 0.5], [0.671875, 0.5, 0.6640625], [44, 42, 42]], [4, [0.5, 0.40625, 0.5], [0.6015625, 0.5, 0.59765625], [52, 48, 50]], [2, [0.28125, 0.28125, 0.5], [0.5, 0.5, 0.71875], [28, 28, 28]], [3, [0.3359375, 0.3359375, 0.5], [0.5, 0.5, 0.671875], [42, 42, 44]], [ 4, [0.46484375, 0.37890625, 0.50390625], [0.4765625, 0.390625, 0.515625], [6, 6, 6], ], [4, [0.40625, 0.40625, 0.5], [0.5, 0.5, 0.59765625], [48, 48, 50]], [2, [0.5, 0.5, 0.28125], [0.71875, 0.71875, 0.5], [28, 28, 28]], [3, [0.5, 0.5, 0.3359375], [0.6796875, 0.6953125, 0.5], [46, 50, 42]], [4, [0.5, 0.5, 0.40234375], [0.59375, 0.6015625, 0.5], [48, 52, 50]], [2, [0.265625, 0.5, 0.28125], [0.5, 0.71875, 0.5], [30, 28, 28]], [3, [0.3359375, 0.5, 0.328125], [0.5, 0.65625, 0.5], [42, 40, 44]], [4, [0.40234375, 0.5, 0.40625], [0.5, 0.60546875, 0.5], [50, 54, 48]], [2, [0.5, 0.265625, 0.28125], [0.71875, 0.5, 0.5], [28, 30, 28]], [3, [0.5, 0.3203125, 0.328125], [0.6640625, 0.5, 0.5], [42, 46, 44]], [4, [0.5, 0.3984375, 0.40625], [0.546875, 0.5, 0.5], [24, 52, 48]], [4, [0.546875, 0.41796875, 0.4453125], [0.5625, 0.4375, 0.5], [8, 10, 28]], [4, [0.546875, 0.453125, 0.41796875], [0.5546875, 0.48046875, 0.4375], [4, 14, 10]], [4, [0.546875, 0.4375, 0.4375], [0.609375, 0.5, 0.5], [32, 32, 32]], [4, [0.546875, 0.4921875, 0.41796875], [0.56640625, 0.5, 0.4375], [10, 4, 10]], [ 4, [0.546875, 0.48046875, 0.41796875], [0.5703125, 0.4921875, 0.4375], [12, 6, 10], ], [4, [0.55859375, 0.46875, 0.43359375], [0.5703125, 0.48046875, 0.4375], [6, 6, 2]], [2, [0.265625, 0.28125, 0.28125], [0.5, 0.5, 0.5], [30, 28, 28]], [3, [0.328125, 0.3359375, 0.328125], [0.5, 0.5, 0.5], [44, 42, 44]], [4, [0.4140625, 0.40625, 0.40625], [0.5, 0.5, 0.5], [44, 48, 48]], ] def check_results(func): r"""This is a decorator for a function to verify that the (numpy ndarray) result of a function is what it should be. This function is designed to be used for very light answer testing. Essentially, it wraps around a larger function that returns a numpy array, and that has results that should not change. It is not necessarily used inside the testing scripts themselves, but inside testing scripts written by developers during the testing of pull requests and new functionality. If a hash is specified, it "wins" and the others are ignored. Otherwise, tolerance is 1e-8 (just above single precision.) The correct results will be stored if the command line contains --answer-reference , and otherwise it will compare against the results on disk. The filename will be func_results_ref_FUNCNAME.cpkl where FUNCNAME is the name of the function being tested. If you would like more control over the name of the pickle file the results are stored in, you can pass the result_basename keyword argument to the function you are testing. The check_results decorator will use the value of the keyword to construct the filename of the results data file. If result_basename is not specified, the name of the testing function is used. This will raise an exception if the results are not correct. Examples -------- >>> @check_results ... def my_func(ds): ... return ds.domain_width >>> my_func(ds) >>> @check_results ... def field_checker(dd, field_name): ... return dd[field_name] >>> field_checker(ds.all_data(), "density", result_basename="density") """ def compute_results(func): @wraps(func) def _func(*args, **kwargs): name = kwargs.pop("result_basename", func.__name__) rv = func(*args, **kwargs) if hasattr(rv, "convert_to_base"): rv.convert_to_base() _rv = rv.ndarray_view() else: _rv = rv mi = _rv.min() ma = _rv.max() st = _rv.std(dtype="float64") su = _rv.sum(dtype="float64") si = _rv.size ha = hashlib.md5(_rv.tobytes()).hexdigest() fn = f"func_results_ref_{name}.cpkl" with open(fn, "wb") as f: pickle.dump((mi, ma, st, su, si, ha), f) return rv return _func import yt.startup_tasks as _startup_tasks unparsed_args = _startup_tasks.unparsed_args if "--answer-reference" in unparsed_args: return compute_results(func) def compare_results(func): @wraps(func) def _func(*args, **kwargs): from numpy.testing import assert_allclose, assert_equal name = kwargs.pop("result_basename", func.__name__) rv = func(*args, **kwargs) if hasattr(rv, "convert_to_base"): rv.convert_to_base() _rv = rv.ndarray_view() else: _rv = rv vals = ( _rv.min(), _rv.max(), _rv.std(dtype="float64"), _rv.sum(dtype="float64"), _rv.size, hashlib.md5(_rv.tobytes()).hexdigest(), ) fn = f"func_results_ref_{name}.cpkl" if not os.path.exists(fn): print("Answers need to be created with --answer-reference .") return False with open(fn, "rb") as f: ref = pickle.load(f) print(f"Sizes: {vals[4] == ref[4]} ({vals[4]}, {ref[4]})") assert_allclose(vals[0], ref[0], 1e-8, err_msg="min") assert_allclose(vals[1], ref[1], 1e-8, err_msg="max") assert_allclose(vals[2], ref[2], 1e-8, err_msg="std") assert_allclose(vals[3], ref[3], 1e-8, err_msg="sum") assert_equal(vals[4], ref[4]) print("Hashes equal: %s" % (vals[-1] == ref[-1])) return rv return _func return compare_results(func) def periodicity_cases(ds): # This is a generator that yields things near the corners. It's good for # getting different places to check periodicity. yield (ds.domain_left_edge + ds.domain_right_edge) / 2.0 dx = ds.domain_width / ds.domain_dimensions # We start one dx in, and only go to one in as well. for i in (1, ds.domain_dimensions[0] - 2): for j in (1, ds.domain_dimensions[1] - 2): for k in (1, ds.domain_dimensions[2] - 2): center = dx * np.array([i, j, k]) + ds.domain_left_edge yield center def run_nose( verbose=False, run_answer_tests=False, answer_big_data=False, call_pdb=False, module=None, ): issue_deprecation_warning( "yt.run_nose (aka yt.testing.run_nose) is deprecated. " "Please do not rely on this function as it will be removed " "in the process of migrating yt tests from nose to pytest.", stacklevel=3, since="4.1", ) from yt.utilities.logger import ytLogger as mylog from yt.utilities.on_demand_imports import _nose orig_level = mylog.getEffectiveLevel() mylog.setLevel(50) nose_argv = sys.argv nose_argv += ["--exclude=answer_testing", "--detailed-errors", "--exe"] if call_pdb: nose_argv += ["--pdb", "--pdb-failures"] if verbose: nose_argv.append("-v") if run_answer_tests: nose_argv.append("--with-answer-testing") if answer_big_data: nose_argv.append("--answer-big-data") if module: nose_argv.append(module) initial_dir = os.getcwd() yt_file = os.path.abspath(__file__) yt_dir = os.path.dirname(yt_file) if os.path.samefile(os.path.dirname(yt_dir), initial_dir): # Provide a nice error message to work around nose bug # see https://github.com/nose-devs/nose/issues/701 raise RuntimeError( """ The yt.run_nose function does not work correctly when invoked in the same directory as the installed yt package. Try starting a python session in a different directory before invoking yt.run_nose again. Alternatively, you can also run the "nosetests" executable in the current directory like so: $ nosetests """ ) os.chdir(yt_dir) try: _nose.run(argv=nose_argv) finally: os.chdir(initial_dir) mylog.setLevel(orig_level) def assert_allclose_units(actual, desired, rtol=1e-7, atol=0, **kwargs): """Raise an error if two objects are not equal up to desired tolerance This is a wrapper for :func:`numpy.testing.assert_allclose` that also verifies unit consistency Parameters ---------- actual : array-like Array obtained (possibly with attached units) desired : array-like Array to compare with (possibly with attached units) rtol : float, optional Relative tolerance, defaults to 1e-7 atol : float or quantity, optional Absolute tolerance. If units are attached, they must be consistent with the units of ``actual`` and ``desired``. If no units are attached, assumes the same units as ``desired``. Defaults to zero. Notes ----- Also accepts additional keyword arguments accepted by :func:`numpy.testing.assert_allclose`, see the documentation of that function for details. """ from numpy.testing import assert_allclose # Create a copy to ensure this function does not alter input arrays act = YTArray(actual) des = YTArray(desired) try: des = des.in_units(act.units) except UnitOperationError as e: raise AssertionError( f"Units of actual ({act.units}) and desired ({des.units}) " "do not have equivalent dimensions" ) from e rt = YTArray(rtol) if not rt.units.is_dimensionless: raise AssertionError(f"Units of rtol ({rt.units}) are not dimensionless") if not isinstance(atol, YTArray): at = YTQuantity(atol, des.units) try: at = at.in_units(act.units) except UnitOperationError as e: raise AssertionError( f"Units of atol ({at.units}) and actual ({act.units}) " "do not have equivalent dimensions" ) from e # units have been validated, so we strip units before calling numpy # to avoid spurious errors act = act.value des = des.value rt = rt.value at = at.value return assert_allclose(act, des, rt, at, **kwargs) def assert_fname(fname): """Function that checks file type using libmagic""" if fname is None: return with open(fname, "rb") as fimg: data = fimg.read() image_type = "" # see http://www.w3.org/TR/PNG/#5PNG-file-signature if data.startswith(b"\211PNG\r\n\032\n"): image_type = ".png" # see http://www.mathguide.de/info/tools/media-types/image/jpeg elif data.startswith(b"\377\330"): image_type = ".jpeg" elif data.startswith(b"%!PS-Adobe"): data_str = data.decode("utf-8", "ignore") if "EPSF" in data_str[: data_str.index("\n")]: image_type = ".eps" else: image_type = ".ps" elif data.startswith(b"%PDF"): image_type = ".pdf" extension = os.path.splitext(fname)[1] assert image_type == extension, ( f"Expected an image of type {extension!r} but {fname!r} " "is an image of type {image_type!r}" ) def requires_backend(backend): """Decorator to check for a specified matplotlib backend. This decorator returns the decorated function if the specified `backend` is same as of `matplotlib.get_backend()`, otherwise returns null function. It could be used to execute function only when a particular `backend` of matplotlib is being used. Parameters ---------- backend : String The value which is compared with the current matplotlib backend in use. """ return skipif( backend.lower() != matplotlib.get_backend().lower(), reason=f"'{backend}' backend not in use", ) def requires_external_executable(*names): missing = [name for name in names if which(name) is None] return skipif( len(missing) > 0, reason=f"missing external executable(s): {', '.join(missing)}" ) class TempDirTest(unittest.TestCase): """ A test class that runs in a temporary directory and removes it afterward. """ def setUp(self): self.curdir = os.getcwd() self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) class ParticleSelectionComparison: """ This is a test helper class that takes a particle dataset, caches the particles it has on disk (manually reading them using lower-level IO routines) and then received a data object that it compares against manually running the data object's selection routines. All supplied data objects must be created from the input dataset. """ def __init__(self, ds): self.ds = ds # Construct an index so that we get all the data_files ds.index particles = {} # hsml is the smoothing length we use for radial selection hsml = {} for data_file in ds.index.data_files: for ptype, pos_arr in ds.index.io._yield_coordinates(data_file): particles.setdefault(ptype, []).append(pos_arr) if ptype in getattr(ds, "_sph_ptypes", ()): hsml.setdefault(ptype, []).append( ds.index.io._get_smoothing_length( data_file, pos_arr.dtype, pos_arr.shape ) ) for ptype in particles: particles[ptype] = np.concatenate(particles[ptype]) if ptype in hsml: hsml[ptype] = np.concatenate(hsml[ptype]) self.particles = particles self.hsml = hsml def compare_dobj_selection(self, dobj): from numpy.testing import assert_array_almost_equal_nulp for ptype in sorted(self.particles): x, y, z = self.particles[ptype].T # Set our radii to zero for now, I guess? radii = self.hsml.get(ptype, 0.0) sel_index = dobj.selector.select_points(x, y, z, radii) if sel_index is None: sel_pos = np.empty((0, 3)) else: sel_pos = self.particles[ptype][sel_index, :] obj_results = [] for chunk in dobj.chunks([], "io"): obj_results.append(chunk[ptype, "particle_position"]) if any(_.size > 0 for _ in obj_results): obj_results = np.concatenate(obj_results, axis=0) else: obj_results = np.empty((0, 3)) # Sometimes we get unitary scaling or other floating point noise. 5 # NULP should be OK. This is mostly for stuff like Rockstar, where # the f32->f64 casting happens at different places depending on # which code path we use. assert_array_almost_equal_nulp( np.asarray(sel_pos), np.asarray(obj_results), 5 ) def run_defaults(self): """ This runs lots of samples that touch different types of wraparounds. Specifically, it does: * sphere in center with radius 0.1 unitary * sphere in center with radius 0.2 unitary * sphere in each of the eight corners of the domain with radius 0.1 unitary * sphere in center with radius 0.5 unitary * box that covers 0.1 .. 0.9 * box from 0.8 .. 0.85 * box from 0.3..0.6, 0.2..0.8, 0.0..0.1 """ sp1 = self.ds.sphere("c", (0.1, "unitary")) self.compare_dobj_selection(sp1) sp2 = self.ds.sphere("c", (0.2, "unitary")) self.compare_dobj_selection(sp2) centers = [ [0.04, 0.5, 0.5], [0.5, 0.04, 0.5], [0.5, 0.5, 0.04], [0.04, 0.04, 0.04], [0.96, 0.5, 0.5], [0.5, 0.96, 0.5], [0.5, 0.5, 0.96], [0.96, 0.96, 0.96], ] r = self.ds.quan(0.1, "unitary") for center in centers: c = self.ds.arr(center, "unitary") + self.ds.domain_left_edge.in_units( "unitary" ) if not all(self.ds.periodicity): # filter out the periodic bits for non-periodic datasets if any(c - r < self.ds.domain_left_edge) or any( c + r > self.ds.domain_right_edge ): continue sp = self.ds.sphere(c, (0.1, "unitary")) self.compare_dobj_selection(sp) sp = self.ds.sphere("c", (0.5, "unitary")) self.compare_dobj_selection(sp) dd = self.ds.all_data() self.compare_dobj_selection(dd) # This is in raw numbers, so we can offset for the left edge LE = self.ds.domain_left_edge.in_units("unitary").d reg1 = self.ds.r[ (0.1 + LE[0], "unitary") : (0.9 + LE[0], "unitary"), (0.1 + LE[1], "unitary") : (0.9 + LE[1], "unitary"), (0.1 + LE[2], "unitary") : (0.9 + LE[2], "unitary"), ] self.compare_dobj_selection(reg1) reg2 = self.ds.r[ (0.8 + LE[0], "unitary") : (0.85 + LE[0], "unitary"), (0.8 + LE[1], "unitary") : (0.85 + LE[1], "unitary"), (0.8 + LE[2], "unitary") : (0.85 + LE[2], "unitary"), ] self.compare_dobj_selection(reg2) reg3 = self.ds.r[ (0.3 + LE[0], "unitary") : (0.6 + LE[0], "unitary"), (0.2 + LE[1], "unitary") : (0.8 + LE[1], "unitary"), (0.0 + LE[2], "unitary") : (0.1 + LE[2], "unitary"), ] self.compare_dobj_selection(reg3) def _deprecated_numpy_testing_reexport(func): import numpy.testing as npt npt_func = getattr(npt, func.__name__) @wraps(npt_func) def retf(*args, **kwargs): __tracebackhide__ = True # Hide traceback for pytest issue_deprecation_warning( f"yt.testing.{func.__name__} is a pure re-export of " f"numpy.testing.{func.__name__}, it will stop working in the future. " "Please import this function directly from numpy instead.", since="4.2", stacklevel=3, ) return npt_func(*args, **kwargs) return retf @_deprecated_numpy_testing_reexport def assert_array_equal(): ... @_deprecated_numpy_testing_reexport def assert_almost_equal(): ... @_deprecated_numpy_testing_reexport def assert_equal(): ... @_deprecated_numpy_testing_reexport def assert_array_less(): ... @_deprecated_numpy_testing_reexport def assert_string_equal(): ... @_deprecated_numpy_testing_reexport def assert_array_almost_equal_nulp(): ... @_deprecated_numpy_testing_reexport def assert_allclose(): ... @_deprecated_numpy_testing_reexport def assert_raises(): ... @_deprecated_numpy_testing_reexport def assert_approx_equal(): ... @_deprecated_numpy_testing_reexport def assert_array_almost_equal(): ... yt-project-yt-f043ac8/yt/tests/000077500000000000000000000000001510711153200164505ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/tests/__init__.py000066400000000000000000000000001510711153200205470ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/tests/test_external_frontends.py000066400000000000000000000031251510711153200237660ustar00rootroot00000000000000import importlib.metadata import pytest import yt from yt.data_objects.static_output import Dataset from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.object_registries import output_type_registry class MockEntryPoint: @classmethod def load(cls): class MockHierarchy(GridIndex): grid = None class ExtDataset(Dataset): _index_class = MockHierarchy def _parse_parameter_file(self): self.current_time = 1.0 self.cosmological_simulation = 0 def _set_code_unit_attributes(self): self.length_unit = self.quan(1.0, "code_length") self.mass_unit = self.quan(1.0, "code_mass") self.time_unit = self.quan(1.0, "code_time") @classmethod def _is_valid(cls, filename, *args, **kwargs): return filename.endswith("mock") @pytest.fixture() def mock_external_frontend(monkeypatch): def mock_entry_points(group=None): return [MockEntryPoint] monkeypatch.setattr(importlib.metadata, "entry_points", mock_entry_points) assert "ExtDataset" not in output_type_registry yield assert "ExtDataset" in output_type_registry # teardown to avoid test pollution output_type_registry.pop("ExtDataset") @pytest.mark.usefixtures("mock_external_frontend") def test_external_frontend(tmp_path): test_file = tmp_path / "tmp.mock" test_file.write_text("") # create the file assert test_file.is_file() ds = yt.load(test_file) assert "ExtDataset" in ds.__class__.__name__ yt-project-yt-f043ac8/yt/tests/test_funcs.py000066400000000000000000000103271510711153200212020ustar00rootroot00000000000000import os from nose.tools import assert_raises from numpy.testing import assert_equal from yt.funcs import ( just_one, levenshtein_distance, simple_download_file, validate_axis, validate_center, ) from yt.testing import fake_amr_ds from yt.units import YTArray, YTQuantity def test_validate_axis(): validate_axis(None, 0) validate_axis(None, "X") ds = fake_amr_ds(geometry="cylindrical") ds.slice("Theta", 0.25) with assert_raises(TypeError) as ex: # default geometry is cartesian ds = fake_amr_ds() ds.slice("r", 0.25) desired = "Expected axis to be any of [0, 1, 2, 'x', 'y', 'z', 'X', 'Y', 'Z'], received 'r'" actual = str(ex.exception) assert actual == desired def test_validate_center(): validate_center("max") validate_center("MIN_") with assert_raises(TypeError) as ex: validate_center("avg") desired = ( "Expected 'center' to be in ['c', 'center', 'm', 'max', 'min'] " "or the prefix to be 'max_'/'min_', received 'avg'." ) assert_equal(str(ex.exception), desired) validate_center(YTQuantity(0.25, "cm")) validate_center([0.25, 0.25, 0.25]) class CustomCenter: def __init__(self, center): self.center = center with assert_raises(TypeError) as ex: validate_center(CustomCenter(10)) desired = ( "Expected 'center' to be a numeric object of type " "list/tuple/np.ndarray/YTArray/YTQuantity, received " "'yt.tests.test_funcs.test_validate_center.." "CustomCenter'." ) assert_equal(str(ex.exception)[:50], desired[:50]) def test_just_one(): # Check that behaviour of this function is consistent before and after refactor # PR 2893 for unit in ["mm", "cm", "km", "pc", "g", "kg", "M_sun"]: obj = YTArray([0.0, 1.0], unit) expected = YTQuantity(obj.flat[0], obj.units, registry=obj.units.registry) jo = just_one(obj) assert jo == expected def test_levenshtein(): assert_equal(levenshtein_distance("abcdef", "abcdef"), 0) # Deletions / additions assert_equal(levenshtein_distance("abcdef", "abcde"), 1) assert_equal(levenshtein_distance("abcdef", "abcd"), 2) assert_equal(levenshtein_distance("abcdef", "abc"), 3) assert_equal(levenshtein_distance("abcdf", "abcdef"), 1) assert_equal(levenshtein_distance("cdef", "abcdef"), 2) assert_equal(levenshtein_distance("bde", "abcdef"), 3) # Substitutions assert_equal(levenshtein_distance("abcd", "abc_"), 1) assert_equal(levenshtein_distance("abcd", "ab__"), 2) assert_equal(levenshtein_distance("abcd", "a___"), 3) assert_equal(levenshtein_distance("abcd", "____"), 4) # Deletion + Substitutions assert_equal(levenshtein_distance("abcd", "abc_z"), 2) assert_equal(levenshtein_distance("abcd", "ab__zz"), 4) assert_equal(levenshtein_distance("abcd", "a___zzz"), 6) assert_equal(levenshtein_distance("abcd", "____zzzz"), 8) # Max distance assert_equal(levenshtein_distance("abcd", "", max_dist=0), 1) assert_equal(levenshtein_distance("abcd", "", max_dist=3), 4) assert_equal(levenshtein_distance("abcd", "", max_dist=10), 4) assert_equal(levenshtein_distance("abcd", "", max_dist=1), 2) assert_equal(levenshtein_distance("abcd", "a", max_dist=2), 3) assert_equal(levenshtein_distance("abcd", "ad", max_dist=2), 2) assert_equal(levenshtein_distance("abcd", "abd", max_dist=2), 1) assert_equal(levenshtein_distance("abcd", "abcd", max_dist=2), 0) def test_simple_download_file(): fn = simple_download_file("http://yt-project.org", "simple-download-file") try: assert fn == "simple-download-file" assert os.path.exists("simple-download-file") finally: # Clean up after ourselves. try: os.unlink("simple-download-file") except FileNotFoundError: pass with assert_raises(RuntimeError) as ex: simple_download_file("http://yt-project.org/404", "simple-download-file") desired = "Attempt to download file from http://yt-project.org/404 failed with error 404: Not Found." actual = str(ex.exception) assert actual == desired assert not os.path.exists("simple-download-file") yt-project-yt-f043ac8/yt/tests/test_load_archive.py000066400000000000000000000073551510711153200225130ustar00rootroot00000000000000import os import sys import tarfile import time import pytest from yt.config import ytcfg from yt.loaders import load_archive from yt.sample_data.api import _download_sample_data_file, get_data_registry_table from yt.testing import requires_module_pytest from yt.utilities.exceptions import YTUnidentifiedDataType @pytest.fixture() def data_registry(): yield get_data_registry_table() @pytest.fixture() def tmp_data_dir(tmp_path): pre_test_data_dir = ytcfg["yt", "test_data_dir"] ytcfg.set("yt", "test_data_dir", str(tmp_path)) yield tmp_path ytcfg.set("yt", "test_data_dir", pre_test_data_dir) # Note: ratarmount cannot currently be installed on Windows as of v0.8.1 @pytest.mark.skipif( sys.platform.startswith("win"), reason="ratarmount cannot currently be installed on Windows as of v0.8.1", ) @pytest.mark.skipif( os.environ.get("JENKINS_HOME") is not None, reason="Archive mounting times out on Jenkins.", ) @requires_module_pytest("pooch", "ratarmount") @pytest.mark.parametrize( "fn, exact_loc, class_", [ ( "ToroShockTube.tar.gz", "ToroShockTube/DD0001/data0001", "EnzoDataset", ), ( "ramses_sink_00016.tar.gz", "ramses_sink_00016/output_00016", "RAMSESDataset", ), ], ) @pytest.mark.parametrize("archive_suffix", ["", ".gz"]) def test_load_archive( fn, exact_loc, class_: str, archive_suffix, tmp_data_dir, data_registry ): # Download the sample .tar.gz'd file targz_path = _download_sample_data_file(filename=fn) tar_path = targz_path.with_suffix(archive_suffix) if tar_path != targz_path: # Open the tarfile and uncompress it to .tar, .tar.gz, and .tar.bz2 files with tarfile.open(targz_path, mode="r:*") as targz: mode = "w" + archive_suffix.replace(".", ":") with tarfile.open(tar_path, mode=mode) as tar: for member in targz.getmembers(): content = targz.extractfile(member) tar.addfile(member, fileobj=content) # Now try to open the .tar.* files warn_msg = "The 'load_archive' function is still experimental and may be unstable." with pytest.warns(UserWarning, match=warn_msg): ds = load_archive(tar_path, exact_loc, mount_timeout=10) assert type(ds).__name__ == class_ # Make sure the index is readable ds.index # Check cleanup mount_path = tar_path.with_name(tar_path.name + ".mount") assert mount_path.is_mount() ## Manually dismount ds.dismount() ## The dismounting happens concurrently, wait a few sec. time.sleep(2) ## Mount path should not exist anymore *and* have been deleted assert not mount_path.is_mount() assert not mount_path.exists() @pytest.mark.skipif( sys.platform.startswith("win"), reason="ratarmount cannot currently be installed on Windows as of v0.8.1", ) @pytest.mark.skipif( os.environ.get("JENKINS_HOME") is not None, reason="Archive mounting times out on Jenkins.", ) @pytest.mark.filterwarnings( "ignore:The 'load_archive' function is still experimental and may be unstable." ) @requires_module_pytest("pooch", "ratarmount") def test_load_invalid_archive(tmp_data_dir, data_registry): # Archive does not exist with pytest.raises(FileNotFoundError): load_archive("this_file_does_not_exist.tar.gz", "invalid_location") targz_path = _download_sample_data_file(filename="ToroShockTube.tar.gz") # File does not exist with pytest.raises(FileNotFoundError): load_archive(targz_path, "invalid_location") # File exists but is not recognized with pytest.raises(YTUnidentifiedDataType): load_archive(targz_path, "ToroShockTube/DD0001/data0001.memorymap") yt-project-yt-f043ac8/yt/tests/test_load_errors.py000066400000000000000000000220301510711153200223710ustar00rootroot00000000000000import re import pytest from yt.data_objects.static_output import Dataset from yt.geometry.grid_geometry_handler import GridIndex from yt.loaders import load, load_simulation from yt.utilities.exceptions import ( YTAmbiguousDataType, YTSimulationNotIdentified, YTUnidentifiedDataType, ) from yt.utilities.object_registries import output_type_registry @pytest.fixture def tmp_data_dir(tmp_path): from yt.config import ytcfg pre_test_data_dir = ytcfg["yt", "test_data_dir"] ytcfg.set("yt", "test_data_dir", str(tmp_path)) yield tmp_path ytcfg.set("yt", "test_data_dir", pre_test_data_dir) @pytest.mark.usefixtures("tmp_data_dir") def test_load_not_a_file(tmp_path): with pytest.raises(FileNotFoundError): load(tmp_path / "not_a_file") @pytest.mark.parametrize("simtype", ["Enzo", "unregistered_simulation_type"]) @pytest.mark.usefixtures("tmp_data_dir") def test_load_simulation_not_a_file(simtype, tmp_path): # it is preferable to report the most important problem in an error message # (missing data is worse than a typo insimulation_type) # so we make sure the error raised is not YTSimulationNotIdentified, # even with an absurd simulation type with pytest.raises(FileNotFoundError): load_simulation(tmp_path / "not_a_file", simtype) @pytest.fixture() def tmp_path_with_empty_file(tmp_path): empty_file_path = tmp_path / "empty_file" empty_file_path.touch() return tmp_path, empty_file_path def test_load_unidentified_data_dir(tmp_path_with_empty_file): tmp_path, empty_file_path = tmp_path_with_empty_file with pytest.raises(YTUnidentifiedDataType): load(tmp_path) def test_load_unidentified_data_file(tmp_path_with_empty_file): tmp_path, empty_file_path = tmp_path_with_empty_file with pytest.raises(YTUnidentifiedDataType): load(empty_file_path) def test_load_simulation_unidentified_data_dir(tmp_path_with_empty_file): tmp_path, empty_file_path = tmp_path_with_empty_file with pytest.raises(YTSimulationNotIdentified): load_simulation(tmp_path, "unregistered_simulation_type") def test_load_simulation_unidentified_data_file(tmp_path_with_empty_file): tmp_path, empty_file_path = tmp_path_with_empty_file with pytest.raises(YTSimulationNotIdentified): load_simulation( empty_file_path, "unregistered_simulation_type", ) @pytest.fixture() def ambiguous_dataset_classes(): # We deliberately setup a situation where two Dataset subclasses # that aren't parents are consisdered valid. # We implement the bare minimum for these classes to be actually # loadable in order to test hints. class MockHierarchy(GridIndex): pass class MockDataset(Dataset): _index_class = MockHierarchy def _parse_parameter_file(self, *args, **kwargs): self.current_time = -1.0 self.cosmological_simulation = 0 def _set_code_unit_attributes(self, *args, **kwargs): self.length_unit = self.quan(1, "m") self.mass_unit = self.quan(1, "kg") self.time_unit = self.quan(1, "s") @classmethod def _is_valid(cls, *args, **kwargs): return True class AlphaDataset(MockDataset): @classmethod def _is_valid(cls, *args, **kwargs): return True class BetaDataset(MockDataset): @classmethod def _is_valid(cls, *args, **kwargs): return True yield # teardown to avoid possible breakage in following tests output_type_registry.pop("MockDataset") output_type_registry.pop("AlphaDataset") output_type_registry.pop("BetaDataset") @pytest.mark.usefixtures("ambiguous_dataset_classes") def test_load_ambiguous_data(tmp_path): with pytest.raises(YTAmbiguousDataType): load(tmp_path) file = tmp_path / "fake_datafile0011.dump" file.touch() pattern = str(tmp_path / "fake_datafile00??.dump") # loading a DatasetSeries should not crash until an item is retrieved ts = load(pattern) with pytest.raises(YTAmbiguousDataType): ts[0] @pytest.mark.parametrize( "hint, expected_type", [ ("alpha", "AlphaDataset"), ("al", "AlphaDataset"), ("ph", "AlphaDataset"), ("beta", "BetaDataset"), ("BeTA", "BetaDataset"), ("b", "BetaDataset"), ("mock", "MockDataset"), ("MockDataset", "MockDataset"), ], ) @pytest.mark.usefixtures("ambiguous_dataset_classes") def test_load_ambiguous_data_with_hint(hint, expected_type, tmp_path): ds = load(tmp_path, hint=hint) assert type(ds).__name__ == expected_type file1 = tmp_path / "fake_datafile0011.dump" file2 = tmp_path / "fake_datafile0022.dump" file1.touch() file2.touch() pattern = str(tmp_path / "fake_datafile00??.dump") ts = load(pattern, hint=hint) ds = ts[0] assert type(ds).__name__ == expected_type ds = ts[1] assert type(ds).__name__ == expected_type @pytest.fixture() def catchall_dataset_class(): # define a Dataset class matching any input, # just so that we don't have to require an actual # dataset for some tests class MockHierarchy(GridIndex): pass class MockDataset(Dataset): _index_class = MockHierarchy def _parse_parameter_file(self, *args, **kwargs): self.current_time = -1.0 self.cosmological_simulation = 0 def _set_code_unit_attributes(self, *args, **kwargs): self.length_unit = self.quan(1, "m") self.mass_unit = self.quan(1, "kg") self.time_unit = self.quan(1, "s") @classmethod def _is_valid(cls, *args, **kwargs): return True yield # teardown to avoid possible breakage in following tests output_type_registry.pop("MockDataset") @pytest.mark.usefixtures("catchall_dataset_class") def test_depr_load_keyword(tmp_path): with pytest.deprecated_call( match=r"Using the 'fn' argument as keyword \(on position 0\) is deprecated\.", ): load(fn=tmp_path) @pytest.fixture() def invalid_dataset_class_with_missing_requirements(): # define a Dataset class matching any input, # just so that we don't have to require an actual # dataset for some tests class MockHierarchy(GridIndex): pass class MockDataset(Dataset): _load_requirements = ["mock_package_name"] _index_class = MockHierarchy def _parse_parameter_file(self, *args, **kwargs): self.current_time = -1.0 self.cosmological_simulation = 0 def _set_code_unit_attributes(self, *args, **kwargs): self.length_unit = self.quan(1, "m") self.mass_unit = self.quan(1, "kg") self.time_unit = self.quan(1, "s") @classmethod def _is_valid(cls, *args, **kwargs): return False yield # teardown to avoid possible breakage in following tests output_type_registry.pop("MockDataset") @pytest.mark.usefixtures("invalid_dataset_class_with_missing_requirements") def test_all_invalid_with_missing_requirements(tmp_path): with pytest.raises( YTUnidentifiedDataType, match=re.compile( re.escape(f"Could not determine input format from {tmp_path!r}\n") + "The following types could not be thorougly checked against your data because " "their requirements are missing. " "You may want to inspect this list and check your installation:\n" r".*" r"- MockDataset \(requires: mock_package_name\)" r".*" "\n\n" "Please make sure you are running a sufficiently recent version of yt.", re.DOTALL, ), ): load(tmp_path) @pytest.fixture() def valid_dataset_class_with_missing_requirements(): # define a Dataset class matching any input, # just so that we don't have to require an actual # dataset for some tests class MockHierarchy(GridIndex): pass class MockDataset(Dataset): _load_requirements = ["mock_package_name"] _index_class = MockHierarchy def _parse_parameter_file(self, *args, **kwargs): self.current_time = -1.0 self.cosmological_simulation = 0 def _set_code_unit_attributes(self, *args, **kwargs): self.length_unit = self.quan(1, "m") self.mass_unit = self.quan(1, "kg") self.time_unit = self.quan(1, "s") @classmethod def _is_valid(cls, *args, **kwargs): return True yield # teardown to avoid possible breakage in following tests output_type_registry.pop("MockDataset") @pytest.mark.usefixtures("valid_dataset_class_with_missing_requirements") def test_single_valid_with_missing_requirements(tmp_path): with pytest.warns( UserWarning, match=( "This dataset appears to be of type MockDataset, " "but the following requirements are currently missing: mock_package_name\n" "Please verify your installation." ), ): load(tmp_path) yt-project-yt-f043ac8/yt/tests/test_load_sample.py000066400000000000000000000141511510711153200223430ustar00rootroot00000000000000import logging import os import re import sys import textwrap import numpy as np import pytest from yt.config import ytcfg from yt.loaders import load_sample from yt.sample_data.api import ( get_data_registry_table, get_download_cache_dir, ) from yt.testing import requires_module_pytest from yt.utilities.logger import ytLogger @pytest.fixture() def tmp_data_dir(tmp_path): pre_test_data_dir = ytcfg["yt", "test_data_dir"] ytcfg.set("yt", "test_data_dir", str(tmp_path)) yield tmp_path ytcfg.set("yt", "test_data_dir", pre_test_data_dir) @pytest.fixture() def capturable_logger(caplog): """ This set the minimal conditions to make pytest's caplog fixture usable. """ propagate = ytLogger.propagate ytLogger.propagate = True with caplog.at_level(logging.INFO, "yt"): yield ytLogger.propagate = propagate @requires_module_pytest("pandas", "pooch", "f90nml") @pytest.mark.usefixtures("capturable_logger") @pytest.mark.parametrize( "fn, archive, exact_loc, class_", [ ( "ToroShockTube", "ToroShockTube.tar.gz", "ToroShockTube/DD0001/data0001", "EnzoDataset", ), ( "KeplerianDisk", "KeplerianDisk.tar.gz", "KeplerianDisk/disk.out1.00000.athdf", "AthenaPPDataset", ), # trying with an exact name as well ( "KeplerianDisk/disk.out1.00000.athdf", "KeplerianDisk.tar.gz", "KeplerianDisk/disk.out1.00000.athdf", "AthenaPPDataset", ), # check this special case because it relies on implementations # details in the AMRVAC frontend (using parfiles) # and could easily fail to load. See GH PR #3343 ( "rmi_dust_2d", "rmi_dust_2d.tar.gz", "rmi_dust_2d/output0001.dat", "AMRVACDataset", ), ], ) def test_load_sample_small_dataset( fn, archive, exact_loc, class_: str, tmp_data_dir, caplog ): cache_path = get_download_cache_dir() assert not cache_path.exists() ds = load_sample(fn, progressbar=False, timeout=30) assert type(ds).__name__ == class_ assert not cache_path.exists() text = textwrap.dedent( f""" '{fn.replace('/', os.path.sep)}' is not available locally. Looking up online. Downloading from https://yt-project.org/data/{archive} Untaring downloaded file to '{str(tmp_data_dir)}' """ ).strip("\n") expected = [("yt", 20, message) for message in text.split("\n")] assert caplog.record_tuples[:3] == expected caplog.clear() # loading a second time should not result in a download request ds2 = load_sample(fn) assert type(ds2).__name__ == class_ assert caplog.record_tuples[0] == ( "yt", 20, f"Sample dataset found in '{os.path.join(str(tmp_data_dir), *exact_loc.split('/'))}'", ) @pytest.mark.skipif( sys.platform.startswith("win"), # flakyness is probably due to Windows' infamous lack of time resolution # overall, this test doesn't seem worth it. reason="This test is flaky on Windows", ) @requires_module_pytest("pandas", "pooch") @pytest.mark.usefixtures("capturable_logger") def test_load_sample_timeout(tmp_data_dir, caplog): from requests.exceptions import ConnectTimeout, ReadTimeout # note that requests is a direct dependency to pooch, # so we don't need to mark it in a decorator. with pytest.raises((ConnectTimeout, ReadTimeout)): load_sample("IsolatedGalaxy", progressbar=False, timeout=0.00001) @requires_module_pytest("pandas", "requests") @pytest.mark.xfail(reason="Registry is currently incomplete.") def test_registry_integrity(): reg = get_data_registry_table() assert not any(reg.isna()) @pytest.fixture() def sound_subreg(): # this selection is needed because the full dataset is incomplete # so we test only the values that can be parsed reg = get_data_registry_table() return reg[["size", "byte_size"]][reg["size"].notna()] @requires_module_pytest("pandas", "requests") def test_registry_byte_size_dtype(sound_subreg): from pandas import Int64Dtype assert sound_subreg["byte_size"].dtype == Int64Dtype() @requires_module_pytest("pandas", "requests") def test_registry_byte_size_sign(sound_subreg): np.testing.assert_array_less(0, sound_subreg["byte_size"]) @requires_module_pytest("pandas", "requests") def test_unknown_filename(): fake_name = "these_are_not_the_files_your_looking_for" with pytest.raises(ValueError, match=f"'{fake_name}' is not an available dataset."): load_sample(fake_name) @requires_module_pytest("pandas", "requests") def test_typo_filename(): wrong_name = "Isolatedgalaxy" with pytest.raises( ValueError, match=re.escape( f"'{wrong_name}' is not an available dataset. Did you mean 'IsolatedGalaxy' ?" ), ): load_sample(wrong_name, timeout=1) @pytest.fixture() def fake_data_dir_in_a_vaccum(tmp_path): pre_test_data_dir = ytcfg["yt", "test_data_dir"] ytcfg.set("yt", "test_data_dir", "/does/not/exist") origin = os.getcwd() os.chdir(tmp_path) yield ytcfg.set("yt", "test_data_dir", pre_test_data_dir) os.chdir(origin) @pytest.mark.skipif( sys.platform.startswith("win"), reason="can't figure out how to match the warning message in a cross-platform way", ) @requires_module_pytest("pandas", "pooch") @pytest.mark.usefixtures("fake_data_dir_in_a_vaccum") def test_data_dir_broken(): # check that load_sample still works if the test_data_dir # isn't properly set, in which case we should dl to the # cwd and issue a warning. msg = ( r"Storage directory from yt config doesn't exist " r"\(currently set to '/does/not/exist'\)\. " r"Current working directory will be used instead\." ) with pytest.warns(UserWarning, match=msg): load_sample("ToroShockTube") def test_filename_none(capsys): assert load_sample() is None captured = capsys.readouterr() assert "yt.sample_data.api.get_data_registry_table" in captured.err yt-project-yt-f043ac8/yt/tests/test_testing.py000066400000000000000000000014201510711153200215330ustar00rootroot00000000000000from unittest import SkipTest import matplotlib from yt.testing import requires_backend active_backend = matplotlib.get_backend() inactive_backend = ({"gtkagg", "macosx", "wx", "tkagg"} - {active_backend}).pop() def test_requires_inactive_backend(): @requires_backend(inactive_backend) def foo(): return try: foo() except SkipTest: pass else: raise AssertionError( "@requires_backend appears to be broken (skip was expected)" ) def test_requires_active_backend(): @requires_backend(active_backend) def foo(): return try: foo() except SkipTest: raise AssertionError( "@requires_backend appears to be broken (skip was not expected)" ) from None yt-project-yt-f043ac8/yt/tests/test_version.py000066400000000000000000000021551510711153200215510ustar00rootroot00000000000000import pytest import yt from yt._version import VersionTuple, _parse_to_version_info @pytest.mark.parametrize( "version_str, expected", ( ("4.1.0", VersionTuple(4, 1, 0, "final", 0)), ("6.2.5", VersionTuple(6, 2, 5, "final", 0)), ("4.1.dev0", VersionTuple(4, 1, 0, "alpha", 0)), ("4.1.0rc", VersionTuple(4, 1, 0, "candidate", 0)), ("4.1.0rc1", VersionTuple(4, 1, 0, "candidate", 1)), ("4.1.0rc2", VersionTuple(4, 1, 0, "candidate", 2)), ), ) def test_parse_version_info(version_str, expected): actual = _parse_to_version_info(version_str) assert actual == expected def test_version_tuple_comp(): # exercise comparison with builtin tuples # comparison results do not matter for this test yt.version_info > (4,) # noqa: B015 yt.version_info > (4, 1) # noqa: B015 yt.version_info > (4, 1, 0) # noqa: B015 yt.version_info < (4, 1, 0) # noqa: B015 yt.version_info <= (4, 1, 0) # noqa: B015 yt.version_info >= (4, 1, 0) # noqa: B015 yt.version_info == (4, 1, 0) # noqa: B015 assert isinstance(yt.version_info, tuple) yt-project-yt-f043ac8/yt/units/000077500000000000000000000000001510711153200164505ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/units/__init__.py000066400000000000000000000065141510711153200205670ustar00rootroot00000000000000from unyt.array import ( loadtxt, savetxt, unyt_array, unyt_quantity, ) from unyt.unit_object import Unit, define_unit # NOQA: F401 from unyt.unit_registry import UnitRegistry # NOQA: Ffg401 from unyt.unit_systems import UnitSystem # NOQA: F401 from yt.units.physical_constants import * from yt.units.physical_constants import _ConstantContainer from yt.units.unit_symbols import * from yt.units.unit_symbols import _SymbolContainer from yt.utilities.exceptions import YTArrayTooLargeToDisplay from yt.units._numpy_wrapper_functions import ( uconcatenate, ucross, udot, uhstack, uintersect1d, unorm, ustack, uunion1d, uvstack, ) YTArray = unyt_array YTQuantity = unyt_quantity class UnitContainer: """A container for units and constants to associate with a dataset This object is usually accessed on a Dataset instance via ``ds.units``. Parameters ---------- registry : UnitRegistry instance A unit registry to associate with units and constants accessed on this object. Example ------- >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> code_mass = ds.units.code_mass >>> (12 * code_mass).to("Msun") unyt_quantity(4.89719136e+11, 'Msun') >>> code_mass.registry is ds.unit_registry True >>> ds.units.newtons_constant unyt_quantity(6.67384e-08, 'cm**3/(g*s**2)') """ def __init__(self, registry): self.unit_symbols = _SymbolContainer(registry) self.physical_constants = _ConstantContainer(registry) def __dir__(self): all_dir = self.unit_symbols.__dir__() + self.physical_constants.__dir__() all_dir += object.__dir__(self) return list(set(all_dir)) def __getattr__(self, item): pc = self.physical_constants us = self.unit_symbols ret = getattr(us, item, None) or getattr(pc, item, None) if not ret: raise AttributeError(item) return ret def display_ytarray(arr): r""" Display a YTArray in a Jupyter widget that enables unit switching. The array returned by this function is read-only, and only works with arrays of size 3 or lower. Parameters ---------- arr : YTArray The Array to display; must be of size 3 or lower. Examples -------- >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> display_ytarray(ds.domain_width) """ if arr.size > 3: raise YTArrayTooLargeToDisplay(arr.size, 3) import ipywidgets unit_registry = arr.units.registry equiv = unit_registry.list_same_dimensions(arr.units) dropdown = ipywidgets.Dropdown(options=sorted(equiv), value=str(arr.units)) def arr_updater(arr, texts): def _value_updater(change): arr2 = arr.in_units(change["new"]) if arr2.shape == (): arr2 = [arr2] for v, t in zip(arr2, texts): t.value = str(v.value) return _value_updater if arr.shape == (): arr_iter = [arr] else: arr_iter = arr texts = [ipywidgets.Text(value=str(_.value), disabled=True) for _ in arr_iter] dropdown.observe(arr_updater(arr, texts), names="value") return ipywidgets.HBox(texts + [dropdown]) def _wrap_display_ytarray(arr): from IPython.display import display display(display_ytarray(arr)) yt-project-yt-f043ac8/yt/units/_numpy_wrapper_functions.py000066400000000000000000000130331510711153200241610ustar00rootroot00000000000000# This module is not part of the public namespace `yt.units` # It is home to wrapper functions that are directly copied from unyt 2.9.2 # We vendor them as a transition step towards unyt 3.0 (in development), # where these wrapper functions are deprecated and are should be replaced with vanilla numpy API # FUTURE: # - require unyt>=3.0 # - deprecate these functions in yt too from unyt import unyt_array, unyt_quantity import numpy as np def _validate_numpy_wrapper_units(v, arrs): if not any(isinstance(a, unyt_array) for a in arrs): return v if not all(isinstance(a, unyt_array) for a in arrs): raise RuntimeError("Not all of your arrays are unyt_arrays.") a1 = arrs[0] if not all(a.units == a1.units for a in arrs[1:]): raise RuntimeError("Your arrays must have identical units.") v.units = a1.units return v def uconcatenate(arrs, axis=0): """Concatenate a sequence of arrays. This wrapper around numpy.concatenate preserves units. All input arrays must have the same units. See the documentation of numpy.concatenate for full details. Examples -------- >>> from unyt import cm >>> A = [1, 2, 3]*cm >>> B = [2, 3, 4]*cm >>> uconcatenate((A, B)) unyt_array([1, 2, 3, 2, 3, 4], 'cm') """ v = np.concatenate(arrs, axis=axis) v = _validate_numpy_wrapper_units(v, arrs) return v def ucross(arr1, arr2, registry=None, axisa=-1, axisb=-1, axisc=-1, axis=None): """Applies the cross product to two YT arrays. This wrapper around numpy.cross preserves units. See the documentation of numpy.cross for full details. """ v = np.cross(arr1, arr2, axisa=axisa, axisb=axisb, axisc=axisc, axis=axis) units = arr1.units * arr2.units arr = unyt_array(v, units, registry=registry) return arr def uintersect1d(arr1, arr2, assume_unique=False): """Find the sorted unique elements of the two input arrays. A wrapper around numpy.intersect1d that preserves units. All input arrays must have the same units. See the documentation of numpy.intersect1d for full details. Examples -------- >>> from unyt import cm >>> A = [1, 2, 3]*cm >>> B = [2, 3, 4]*cm >>> uintersect1d(A, B) unyt_array([2, 3], 'cm') """ v = np.intersect1d(arr1, arr2, assume_unique=assume_unique) v = _validate_numpy_wrapper_units(v, [arr1, arr2]) return v def uunion1d(arr1, arr2): """Find the union of two arrays. A wrapper around numpy.intersect1d that preserves units. All input arrays must have the same units. See the documentation of numpy.intersect1d for full details. Examples -------- >>> from unyt import cm >>> A = [1, 2, 3]*cm >>> B = [2, 3, 4]*cm >>> uunion1d(A, B) unyt_array([1, 2, 3, 4], 'cm') """ v = np.union1d(arr1, arr2) v = _validate_numpy_wrapper_units(v, [arr1, arr2]) return v def unorm(data, ord=None, axis=None, keepdims=False): """Matrix or vector norm that preserves units This is a wrapper around np.linalg.norm that preserves units. See the documentation for that function for descriptions of the keyword arguments. Examples -------- >>> from unyt import km >>> data = [1, 2, 3]*km >>> print(unorm(data)) 3.7416573867739413 km """ norm = np.linalg.norm(data, ord=ord, axis=axis, keepdims=keepdims) if norm.shape == (): return unyt_quantity(norm, data.units) return unyt_array(norm, data.units) def udot(op1, op2): """Matrix or vector dot product that preserves units This is a wrapper around np.dot that preserves units. Examples -------- >>> from unyt import km, s >>> a = np.eye(2)*km >>> b = (np.ones((2, 2)) * 2)*s >>> print(udot(a, b)) [[2. 2.] [2. 2.]] km*s """ dot = np.dot(op1.d, op2.d) units = op1.units * op2.units if dot.shape == (): return unyt_quantity(dot, units) return unyt_array(dot, units) def uvstack(arrs): """Stack arrays in sequence vertically (row wise) while preserving units This is a wrapper around np.vstack that preserves units. Examples -------- >>> from unyt import km >>> a = [1, 2, 3]*km >>> b = [2, 3, 4]*km >>> print(uvstack([a, b])) [[1 2 3] [2 3 4]] km """ v = np.vstack(arrs) v = _validate_numpy_wrapper_units(v, arrs) return v def uhstack(arrs): """Stack arrays in sequence horizontally while preserving units This is a wrapper around np.hstack that preserves units. Examples -------- >>> from unyt import km >>> a = [1, 2, 3]*km >>> b = [2, 3, 4]*km >>> print(uhstack([a, b])) [1 2 3 2 3 4] km >>> a = [[1],[2],[3]]*km >>> b = [[2],[3],[4]]*km >>> print(uhstack([a, b])) [[1 2] [2 3] [3 4]] km """ v = np.hstack(arrs) v = _validate_numpy_wrapper_units(v, arrs) return v def ustack(arrs, axis=0): """Join a sequence of arrays along a new axis while preserving units The axis parameter specifies the index of the new axis in the dimensions of the result. For example, if ``axis=0`` it will be the first dimension and if ``axis=-1`` it will be the last dimension. This is a wrapper around np.stack that preserves units. See the documentation for np.stack for full details. Examples -------- >>> from unyt import km >>> a = [1, 2, 3]*km >>> b = [2, 3, 4]*km >>> print(ustack([a, b])) [[1 2 3] [2 3 4]] km """ v = np.stack(arrs, axis=axis) v = _validate_numpy_wrapper_units(v, arrs) return v yt-project-yt-f043ac8/yt/units/dimensions.py000066400000000000000000000001631510711153200211720ustar00rootroot00000000000000from unyt.dimensions import current_mks # explicit re-export to enable type checking from unyt.dimensions import * yt-project-yt-f043ac8/yt/units/equivalencies.py000066400000000000000000000000411510711153200216520ustar00rootroot00000000000000from unyt.equivalencies import * yt-project-yt-f043ac8/yt/units/physical_constants.py000066400000000000000000000027341510711153200227400ustar00rootroot00000000000000from unyt.array import unyt_quantity from unyt.unit_systems import add_constants from yt.units.unit_registry import default_unit_registry add_constants(globals(), registry=default_unit_registry) class _ConstantContainer: """A container for physical constants to associate with a dataset. This object is usually accessed on a Dataset instance via ``ds.units.physical_constants``. Parameters ---------- registry : UnitRegistry instance A unit registry to associate with units constants accessed on this object. Example ------- >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ds.units.physical_constants.newtons_constant unyt_quantity(6.67384e-08, 'cm**3/(g*s**2)') """ def __init__(self, registry): self._registry = registry self._cache = {} def __dir__(self): ret = [p for p in globals() if not p.startswith("_")] + object.__dir__(self) return list(set(ret)) def __getattr__(self, item): if item in self._cache: return self._cache[item] if item in globals(): const = globals()[item].copy() const.units.registry = self._registry const.convert_to_base(self._registry.unit_system) const_v, const_unit = const.v, const.units ret = unyt_quantity(const_v, const_unit, registry=self._registry) self._cache[item] = ret return ret raise AttributeError(item) yt-project-yt-f043ac8/yt/units/tests/000077500000000000000000000000001510711153200176125ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/units/tests/__init__.py000066400000000000000000000000001510711153200217110ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/units/tests/test_magnetic_code_units.py000066400000000000000000000043211510711153200252260ustar00rootroot00000000000000import numpy as np from yt.loaders import load_uniform_grid from numpy.testing import assert_allclose def test_magnetic_code_units(): sqrt4pi = np.sqrt(4.0 * np.pi) ddims = (16,) * 3 data = {"density": (np.random.uniform(size=ddims), "g/cm**3")} ds1 = load_uniform_grid( data, ddims, magnetic_unit=(sqrt4pi, "gauss"), unit_system="cgs" ) assert_allclose(ds1.magnetic_unit.value, sqrt4pi) assert str(ds1.magnetic_unit.units) == "G" mucu = ds1.magnetic_unit.to("code_magnetic") assert_allclose(mucu.value, 1.0) assert str(mucu.units) == "code_magnetic" ds2 = load_uniform_grid(data, ddims, magnetic_unit=(1.0, "T"), unit_system="cgs") assert_allclose(ds2.magnetic_unit.value, 10000.0) assert str(ds2.magnetic_unit.units) == "G" mucu = ds2.magnetic_unit.to("code_magnetic") assert_allclose(mucu.value, 1.0) assert str(mucu.units) == "code_magnetic" ds3 = load_uniform_grid(data, ddims, magnetic_unit=(1.0, "T"), unit_system="mks") assert_allclose(ds3.magnetic_unit.value, 1.0) assert str(ds3.magnetic_unit.units) == "T" mucu = ds3.magnetic_unit.to("code_magnetic") assert_allclose(mucu.value, 1.0) assert str(mucu.units) == "code_magnetic" ds4 = load_uniform_grid( data, ddims, magnetic_unit=(1.0, "gauss"), unit_system="mks" ) assert_allclose(ds4.magnetic_unit.value, 1.0e-4) assert str(ds4.magnetic_unit.units) == "T" mucu = ds4.magnetic_unit.to("code_magnetic") assert_allclose(mucu.value, 1.0) assert str(mucu.units) == "code_magnetic" ds5 = load_uniform_grid( data, ddims, magnetic_unit=(1.0, "uG"), unit_system="mks" ) assert_allclose(ds5.magnetic_unit.value, 1.0e-10) assert str(ds5.magnetic_unit.units) == "T" mucu = ds5.magnetic_unit.to("code_magnetic") assert_allclose(mucu.value, 1.0) assert str(mucu.units) == "code_magnetic" ds6 = load_uniform_grid( data, ddims, magnetic_unit=(1.0, "nT"), unit_system="cgs" ) assert_allclose(ds6.magnetic_unit.value, 1.0e-5) assert str(ds6.magnetic_unit.units) == "G" mucu = ds6.magnetic_unit.to("code_magnetic") assert_allclose(mucu.value, 1.0) assert str(mucu.units) == "code_magnetic" yt-project-yt-f043ac8/yt/units/unit_lookup_table.py000066400000000000000000000000461510711153200225410ustar00rootroot00000000000000from unyt._unit_lookup_table import * yt-project-yt-f043ac8/yt/units/unit_object.py000066400000000000000000000000371510711153200213270ustar00rootroot00000000000000from unyt.unit_object import * yt-project-yt-f043ac8/yt/units/unit_registry.py000066400000000000000000000003261510711153200217320ustar00rootroot00000000000000from unyt.dimensions import dimensionless from unyt.unit_registry import * default_unit_registry = UnitRegistry(unit_system="cgs") # type: ignore default_unit_registry.add("h", 1.0, dimensionless, tex_repr=r"h") yt-project-yt-f043ac8/yt/units/unit_symbols.py000066400000000000000000000026771510711153200215650ustar00rootroot00000000000000from unyt.unit_object import Unit from unyt.unit_systems import add_symbols from yt.units.unit_registry import default_unit_registry add_symbols(globals(), registry=default_unit_registry) class _SymbolContainer: """A container for units to associate with a dataset. This object is usually accessed on a Dataset instance via ``ds.units.unit_symbols``. Parameters ---------- registry : UnitRegistry instance A unit registry to associate with units accessed on this object. Example ------- >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> code_mass = ds.units.code_mass >>> (12 * code_mass).to("Msun") unyt_quantity(4.89719136e+11, 'Msun') >>> code_mass.registry is ds.unit_registry True """ def __init__(self, registry): self._registry = registry self._cache = {} def __dir__(self): ret = [u for u in globals() if not u.startswith("_")] ret += list(self._registry.keys()) ret += object.__dir__(self) return list(set(ret)) def __getattr__(self, item): if item in self._cache: return self._cache[item] if hasattr(globals(), item): ret = Unit(globals()[item].expr, registry=self._registry) elif item in self._registry: ret = Unit(item, registry=self._registry) else: raise AttributeError(item) self._cache[item] = ret return ret yt-project-yt-f043ac8/yt/units/unit_systems.py000066400000000000000000000012711510711153200215710ustar00rootroot00000000000000from unyt.unit_systems import * def create_code_unit_system(unit_registry, current_mks_unit=None): code_unit_system = UnitSystem( name=unit_registry.unit_system_id, length_unit="code_length", mass_unit="code_mass", time_unit="code_time", temperature_unit="code_temperature", current_mks_unit=current_mks_unit, registry=unit_registry, ) code_unit_system["velocity"] = "code_velocity" if current_mks_unit: code_unit_system["magnetic_field_mks"] = "code_magnetic" else: code_unit_system["magnetic_field_cgs"] = "code_magnetic" code_unit_system["pressure"] = "code_pressure" return code_unit_system yt-project-yt-f043ac8/yt/units/yt_array.py000066400000000000000000000002311510711153200206500ustar00rootroot00000000000000from unyt.array import * # NOQA: F403, F401 from yt.funcs import array_like_field # NOQA: F401 from yt.units import YTArray, YTQuantity # NOQA: F401 yt-project-yt-f043ac8/yt/utilities/000077500000000000000000000000001510711153200173215ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/__init__.py000066400000000000000000000000001510711153200214200ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/amr_kdtree/000077500000000000000000000000001510711153200214365ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/amr_kdtree/__init__.py000066400000000000000000000000371510711153200235470ustar00rootroot00000000000000""" Initialize amr_kdtree """ yt-project-yt-f043ac8/yt/utilities/amr_kdtree/amr_kdtools.py000066400000000000000000000025671510711153200243400ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog def receive_and_reduce(comm, incoming_rank, image, add_to_front, *, use_opacity=True): mylog.debug("Receiving image from %04i", incoming_rank) # mylog.debug( '%04i receiving image from %04i'%(self.comm.rank,back.owner)) arr2 = comm.recv_array(incoming_rank, incoming_rank).reshape( (image.shape[0], image.shape[1], image.shape[2]) ) if not use_opacity: np.add(image, arr2, image) return image if add_to_front: front = arr2 back = image else: front = image back = arr2 if image.shape[2] == 3: # Assume Projection Camera, Add np.add(image, front, image) return image ta = 1.0 - front[:, :, 3] np.maximum(ta, 0.0, out=ta) # This now does the following calculation, but in a memory # conservative fashion # image[:,:,i ] = front[:,:,i] + ta*back[:,:,i] image = back.copy() for i in range(4): np.multiply(image[:, :, i], ta, image[:, :, i]) np.add(image, front, image) return image def send_to_parent(comm, outgoing_rank, image): mylog.debug("Sending image to %04i", outgoing_rank) comm.send_array(image, outgoing_rank, tag=comm.rank) def scatter_image(comm, root, image): mylog.debug("Scattering from %04i", root) image = comm.mpi_bcast(image, root=root) return image yt-project-yt-f043ac8/yt/utilities/amr_kdtree/amr_kdtree.py000066400000000000000000000522551510711153200241360ustar00rootroot00000000000000import operator import numpy as np from yt.funcs import is_sequence, mylog from yt.geometry.grid_geometry_handler import GridIndex from yt.utilities.amr_kdtree.amr_kdtools import ( receive_and_reduce, scatter_image, send_to_parent, ) from yt.utilities.lib.amr_kdtools import Node from yt.utilities.lib.partitioned_grid import PartitionedGrid from yt.utilities.math_utils import periodic_position from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, ) steps = np.array( [ [-1, -1, -1], [-1, -1, 0], [-1, -1, 1], [-1, 0, -1], [-1, 0, 0], [-1, 0, 1], [-1, 1, -1], [-1, 1, 0], [-1, 1, 1], [0, -1, -1], [0, -1, 0], [0, -1, 1], [0, 0, -1], # [ 0, 0, 0], [0, 0, 1], [0, 1, -1], [0, 1, 0], [0, 1, 1], [1, -1, -1], [1, -1, 0], [1, -1, 1], [1, 0, -1], [1, 0, 0], [1, 0, 1], [1, 1, -1], [1, 1, 0], [1, 1, 1], ] ) def _apply_log(data, log_changed, log_new): """Helper used to set log10/10^ to data in AMRKDTree""" if not log_changed: return if log_new: np.log10(data, data) else: np.power(10.0, data, data) class Tree: def __init__( self, ds, comm_rank=0, comm_size=1, left=None, right=None, min_level=None, max_level=None, data_source=None, ): self.ds = ds try: self._id_offset = ds.index.grids[0]._id_offset except AttributeError: self._id_offset = 0 if data_source is None: data_source = ds.all_data() self.data_source = data_source if left is None: left = np.array([-np.inf] * 3) if right is None: right = np.array([np.inf] * 3) if min_level is None: min_level = 0 if max_level is None: max_level = ds.index.max_level self.min_level = min_level self.max_level = max_level self.comm_rank = comm_rank self.comm_size = comm_size self.trunk = Node(None, None, None, left, right, -1, 1) self.build() def add_grids(self, grids): gles = np.array([g.LeftEdge for g in grids]) gres = np.array([g.RightEdge for g in grids]) gids = np.array([g.id for g in grids], dtype="int64") self.trunk.add_grids( gids.size, gles, gres, gids, self.comm_rank, self.comm_size ) del gles, gres, gids, grids def build(self): lvl_range = range(self.min_level, self.max_level + 1) for lvl in lvl_range: # grids = self.data_source.select_grids(lvl) grids = np.array( [b for b, mask in self.data_source.blocks if b.Level == lvl] ) if len(grids) == 0: continue self.add_grids(grids) def check_tree(self): for node in self.trunk.depth_traverse(): if node.grid == -1: continue grid = self.ds.index.grids[node.grid - self._id_offset] dds = grid.dds gle = grid.LeftEdge nle = self.ds.arr(node.get_left_edge(), units="code_length") nre = self.ds.arr(node.get_right_edge(), units="code_length") li = np.rint((nle - gle) / dds).astype("int32") ri = np.rint((nre - gle) / dds).astype("int32") dims = ri - li assert np.all(grid.LeftEdge <= nle) assert np.all(grid.RightEdge >= nre) assert np.all(dims > 0) # print(grid, dims, li, ri) # Calculate the Volume vol = self.trunk.kd_sum_volume() mylog.debug("AMRKDTree volume = %e", vol) self.trunk.kd_node_check() def sum_cells(self, all_cells=False): cells = 0 for node in self.trunk.depth_traverse(): if node.grid == -1: continue if not all_cells and not node.kd_is_leaf(): continue grid = self.ds.index.grids[node.grid - self._id_offset] dds = grid.dds gle = grid.LeftEdge nle = self.ds.arr(node.get_left_edge(), units="code_length") nre = self.ds.arr(node.get_right_edge(), units="code_length") li = np.rint((nle - gle) / dds).astype("int32") ri = np.rint((nre - gle) / dds).astype("int32") dims = ri - li cells += np.prod(dims) return cells class AMRKDTree(ParallelAnalysisInterface): r"""A KDTree for AMR data. Not applicable to particle or octree-based datasets. """ fields = None log_fields = None no_ghost = True def __init__(self, ds, min_level=None, max_level=None, data_source=None): if not issubclass(ds.index.__class__, GridIndex): raise RuntimeError( "AMRKDTree does not support particle or octree-based data." ) ParallelAnalysisInterface.__init__(self) self.ds = ds self.current_vcds = [] self.current_saved_grids = [] self.bricks = [] self.brick_dimensions = [] self.sdx = ds.index.get_smallest_dx() self._initialized = False try: self._id_offset = ds.index.grids[0]._id_offset except AttributeError: self._id_offset = 0 if data_source is None: data_source = self.ds.all_data() self.data_source = data_source mylog.debug("Building AMRKDTree") self.tree = Tree( ds, self.comm.rank, self.comm.size, min_level=min_level, max_level=max_level, data_source=data_source, ) def set_fields(self, fields, log_fields, no_ghost, force=False): new_fields = self.data_source._determine_fields(fields) regenerate_data = ( self.fields is None or len(self.fields) != len(new_fields) or self.fields != new_fields or force ) if not is_sequence(log_fields): log_fields = [log_fields] new_log_fields = list(log_fields) self.tree.trunk.set_dirty(regenerate_data) self.fields = new_fields if self.log_fields is not None and not regenerate_data: flip_log = list(map(operator.ne, self.log_fields, new_log_fields)) else: flip_log = [False] * len(new_log_fields) self.log_fields = new_log_fields self.no_ghost = no_ghost del self.bricks, self.brick_dimensions self.brick_dimensions = [] bricks = [] for b in self.traverse(): list(map(_apply_log, b.my_data, flip_log, self.log_fields)) bricks.append(b) self.bricks = np.array(bricks) self.brick_dimensions = np.array(self.brick_dimensions) self._initialized = True def initialize_source(self, fields, log_fields, no_ghost): if ( fields == self.fields and log_fields == self.log_fields and no_ghost == self.no_ghost ): return self.set_fields(fields, log_fields, no_ghost) def traverse(self, viewpoint=None): for node in self.tree.trunk.kd_traverse(viewpoint=viewpoint): yield self.get_brick_data(node) def slice_traverse(self, viewpoint=None): if not hasattr(self.ds.index, "grid"): raise NotImplementedError for node in self.tree.trunk.kd_traverse(viewpoint=viewpoint): grid = self.ds.index.grids[node.grid - self._id_offset] dds = grid.dds gle = grid.LeftEdge.in_units("code_length").ndarray_view() nle = node.get_left_edge() nre = node.get_right_edge() li = np.rint((nle - gle) / dds).astype("int32") ri = np.rint((nre - gle) / dds).astype("int32") dims = ri - li sl = (slice(li[0], ri[0]), slice(li[1], ri[1]), slice(li[2], ri[2])) gi = grid.get_global_startindex() + li yield grid, node, (sl, dims, gi) def get_node(self, nodeid): path = np.binary_repr(nodeid) depth = 1 temp = self.tree.trunk for depth in range(1, len(path)): if path[depth] == "0": temp = temp.left else: temp = temp.right assert temp is not None return temp def locate_node(self, pos): return self.tree.trunk.find_node(pos) def get_reduce_owners(self): owners = {} for bottom_id in range(self.comm.size, 2 * self.comm.size): temp = self.get_node(bottom_id) owners[temp.node_id] = temp.node_id - self.comm.size while temp is not None: if temp.parent is None: break if temp == temp.parent.right: break temp = temp.parent owners[temp.node_id] = owners[temp.left.node_id] return owners def reduce_tree_images(self, image, viewpoint, *, use_opacity=True): if self.comm.size <= 1: return image myrank = self.comm.rank nprocs = self.comm.size owners = self.get_reduce_owners() node = self.get_node(nprocs + myrank) while owners[node.parent.node_id] == myrank: split_dim = node.parent.get_split_dim() split_pos = node.parent.get_split_pos() add_to_front = viewpoint[split_dim] >= split_pos image = receive_and_reduce( self.comm, owners[node.parent.right.node_id], image, add_to_front, use_opacity=use_opacity, ) if node.parent.node_id == 1: break else: node = node.parent else: send_to_parent(self.comm, owners[node.parent.node_id], image) return scatter_image(self.comm, owners[1], image) def get_brick_data(self, node): if node.data is not None and not node.dirty: return node.data grid = self.ds.index.grids[node.grid - self._id_offset] dds = grid.dds.ndarray_view() gle = grid.LeftEdge.ndarray_view() nle = node.get_left_edge() nre = node.get_right_edge() li = np.rint((nle - gle) / dds).astype("int32") ri = np.rint((nre - gle) / dds).astype("int32") dims = ri - li assert np.all(grid.LeftEdge <= nle) assert np.all(grid.RightEdge >= nre) if grid in self.current_saved_grids and not node.dirty: dds = self.current_vcds[self.current_saved_grids.index(grid)] else: dds = [] vcd = grid.get_vertex_centered_data( self.fields, smoothed=True, no_ghost=self.no_ghost ) for i, field in enumerate(self.fields): if self.log_fields[i]: v = vcd[field].astype("float64") v[v <= 0] = np.nan dds.append(np.log10(v)) else: dds.append(vcd[field].astype("float64")) self.current_saved_grids.append(grid) self.current_vcds.append(dds) if self.data_source.selector is None: mask = np.ones(dims, dtype="uint8") else: mask, _ = self.data_source.selector.fill_mask_regular_grid(grid) mask = mask[li[0] : ri[0], li[1] : ri[1], li[2] : ri[2]].astype("uint8") data = [ d[li[0] : ri[0] + 1, li[1] : ri[1] + 1, li[2] : ri[2] + 1].copy() for d in dds ] brick = PartitionedGrid( grid.id, data, mask, nle.copy(), nre.copy(), dims.astype("int64") ) node.data = brick node.dirty = False if not self._initialized: self.brick_dimensions.append(dims) return brick def locate_neighbors(self, grid, ci): r"""Given a grid and cell index, finds the 26 neighbor grids and cell indices. Parameters ---------- grid: Grid Object Grid containing the cell of interest ci: array-like The cell index of the cell of interest Returns ------- grids: Numpy array of Grid objects cis: List of neighbor cell index tuples Both of these are neighbors that, relative to the current cell index (i,j,k), are ordered as: (i-1, j-1, k-1), (i-1, j-1, k ), (i-1, j-1, k+1), ... (i-1, j , k-1), (i-1, j , k ), (i-1, j , k+1), ... (i+1, j+1, k-1), (i-1, j-1, k ), (i+1, j+1, k+1) That is they start from the lower left and proceed to upper right varying the third index most frequently. Note that the center cell (i,j,k) is omitted. """ ci = np.array(ci) center_dds = grid.dds position = grid.LeftEdge + (np.array(ci) + 0.5) * grid.dds grids = np.empty(26, dtype="object") cis = np.empty([26, 3], dtype="int64") offs = 0.5 * (center_dds + self.sdx) new_cis = ci + steps in_grid = np.all((new_cis >= 0) * (new_cis < grid.ActiveDimensions), axis=1) new_positions = position + steps * offs new_positions = [periodic_position(p, self.ds) for p in new_positions] grids[in_grid] = grid get_them = np.argwhere(in_grid).ravel() cis[in_grid] = new_cis[in_grid] if (in_grid).sum() > 0: grids[np.logical_not(in_grid)] = [ self.ds.index.grids[ self.locate_node(new_positions[i]).grid - self._id_offset ] for i in get_them ] cis[np.logical_not(in_grid)] = [ (new_positions[i] - grids[i].LeftEdge) / grids[i].dds for i in get_them ] cis = [tuple(_ci) for _ci in cis] return grids, cis def locate_neighbors_from_position(self, position): r"""Given a position, finds the 26 neighbor grids and cell indices. This is a mostly a wrapper for locate_neighbors. Parameters ---------- position: array-like Position of interest Returns ------- grids: Numpy array of Grid objects cis: List of neighbor cell index tuples Both of these are neighbors that, relative to the current cell index (i,j,k), are ordered as: (i-1, j-1, k-1), (i-1, j-1, k ), (i-1, j-1, k+1), ... (i-1, j , k-1), (i-1, j , k ), (i-1, j , k+1), ... (i+1, j+1, k-1), (i-1, j-1, k ), (i+1, j+1, k+1) That is they start from the lower left and proceed to upper right varying the third index most frequently. Note that the center cell (i,j,k) is omitted. """ position = np.array(position) grid = self.ds.index.grids[self.locate_node(position).grid - self._id_offset] ci = ((position - grid.LeftEdge) / grid.dds).astype("int64") return self.locate_neighbors(grid, ci) def store_kd_bricks(self, fn=None): if not self._initialized: self.initialize_source() if fn is None: fn = f"{self.ds}_kd_bricks.h5" if self.comm.rank != 0: self.comm.recv_array(self.comm.rank - 1, tag=self.comm.rank - 1) f = h5py.File(fn, mode="w") for node in self.tree.depth_traverse(): i = node.node_id if node.data is not None: for fi, field in enumerate(self.fields): try: f.create_dataset( f"/brick_{hex(i)}_{field}", data=node.data.my_data[fi].astype("float64"), ) except Exception: pass f.close() del f if self.comm.rank != (self.comm.size - 1): self.comm.send_array([0], self.comm.rank + 1, tag=self.comm.rank) def load_kd_bricks(self, fn=None): if fn is None: fn = f"{self.ds}_kd_bricks.h5" if self.comm.rank != 0: self.comm.recv_array(self.comm.rank - 1, tag=self.comm.rank - 1) try: f = h5py.File(fn, mode="a") for node in self.tree.depth_traverse(): i = node.node_id if node.grid != -1: data = [ f[f"brick_{hex(i)}_{field}"][:].astype("float64") for field in self.fields ] node.data = PartitionedGrid( node.grid.id, data, node.l_corner.copy(), node.r_corner.copy(), node.dims.astype("int64"), ) self.bricks.append(node.data) self.brick_dimensions.append(node.dims) self.bricks = np.array(self.bricks) self.brick_dimensions = np.array(self.brick_dimensions) self._initialized = True f.close() del f except Exception: pass if self.comm.rank != (self.comm.size - 1): self.comm.send_array([0], self.comm.rank + 1, tag=self.comm.rank) def join_parallel_trees(self): if self.comm.size == 0: return nid, pid, lid, rid, les, res, gid, splitdims, splitposs = self.get_node_arrays() nid = self.comm.par_combine_object(nid, "cat", "list") pid = self.comm.par_combine_object(pid, "cat", "list") lid = self.comm.par_combine_object(lid, "cat", "list") rid = self.comm.par_combine_object(rid, "cat", "list") gid = self.comm.par_combine_object(gid, "cat", "list") les = self.comm.par_combine_object(les, "cat", "list") res = self.comm.par_combine_object(res, "cat", "list") splitdims = self.comm.par_combine_object(splitdims, "cat", "list") splitposs = self.comm.par_combine_object(splitposs, "cat", "list") nid = np.array(nid) self.rebuild_tree_from_array( nid, pid, lid, rid, les, res, gid, splitdims, splitposs ) def get_node_arrays(self): nids = [] leftids = [] rightids = [] parentids = [] les = [] res = [] gridids = [] splitdims = [] splitposs = [] for node in self.tree.trunk.depth_first_touch(): nids.append(node.node_id) les.append(node.get_left_edge()) res.append(node.get_right_edge()) if node.left is None: leftids.append(-1) else: leftids.append(node.left.node_id) if node.right is None: rightids.append(-1) else: rightids.append(node.right.node_id) if node.parent is None: parentids.append(-1) else: parentids.append(node.parent.node_id) if node.grid is None: gridids.append(-1) else: gridids.append(node.grid) splitdims.append(node.get_split_dim()) splitposs.append(node.get_split_pos()) return ( nids, parentids, leftids, rightids, les, res, gridids, splitdims, splitposs, ) def rebuild_tree_from_array( self, nids, pids, lids, rids, les, res, gids, splitdims, splitposs ): del self.tree.trunk self.tree.trunk = Node(None, None, None, les[0], res[0], gids[0], nids[0]) N = nids.shape[0] for i in range(N): n = self.get_node(nids[i]) n.set_left_edge(les[i]) n.set_right_edge(res[i]) if lids[i] != -1 and n.left is None: n.left = Node( n, None, None, np.zeros(3, dtype="float64"), np.zeros(3, dtype="float64"), -1, lids[i], ) if rids[i] != -1 and n.right is None: n.right = Node( n, None, None, np.zeros(3, dtype="float64"), np.zeros(3, dtype="float64"), -1, rids[i], ) if gids[i] != -1: n.grid = gids[i] if splitdims[i] != -1: n.create_split(splitdims[i], splitposs[i]) mylog.info( "AMRKDTree rebuilt, Final Volume: %e", self.tree.trunk.kd_sum_volume() ) return self.tree.trunk def count_volume(self): return self.tree.trunk.kd_sum_volume() def count_cells(self): return self.tree.sum_cells() if __name__ == "__main__": from time import time import yt ds = yt.load("/Users/skillman/simulations/DD1717/DD1717") ds.index t1 = time() hv = AMRKDTree(ds) t2 = time() print(hv.tree.trunk.kd_sum_volume()) print(hv.tree.trunk.kd_node_check()) print(f"Time: {t2 - t1:e} seconds") yt-project-yt-f043ac8/yt/utilities/amr_kdtree/api.py000066400000000000000000000000421510711153200225550ustar00rootroot00000000000000from .amr_kdtree import AMRKDTree yt-project-yt-f043ac8/yt/utilities/answer_testing/000077500000000000000000000000001510711153200223555ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/answer_testing/__init__.py000066400000000000000000000000701510711153200244630ustar00rootroot00000000000000""" The components of the Enzo testing mechanism """ yt-project-yt-f043ac8/yt/utilities/answer_testing/answer_tests.py000066400000000000000000000244131510711153200254540ustar00rootroot00000000000000""" Title: answer_tests.py Purpose: Contains answer tests that are used by yt's various frontends """ import hashlib import os import tempfile import matplotlib.image as mpimg import numpy as np import yt.visualization.plot_window as pw from yt.utilities.answer_testing.framework import create_obj from yt.utilities.answer_testing.testing_utilities import ( _create_phase_plot_attribute_plot, _create_plot_window_attribute_plot, ) def grid_hierarchy(ds): result = {} result["grid_dimensions"] = ds.index.grid_dimensions result["grid_left_edge"] = ds.index.grid_left_edge result["grid_right_edge"] = ds.index.grid_right_edge result["grid_levels"] = ds.index.grid_levels result["grid_particle_count"] = ds.index.grid_particle_count return result def parentage_relationships(ds): parents = [] children = [] for g in ds.index.grids: p = g.Parent if p is None: parents.append(-1) elif hasattr(p, "id"): parents.append(p.id) else: parents = parents + [pg.id for pg in p] children = children + [c.id for c in g.Children] result = np.array(parents + children) return result def grid_values(ds, field): # The hashing is done here so that there is only one entry for # the test that contains info about all of the grids as opposed # to having a separate 'grid_id : grid_hash' pair for each grid # since that makes the answer file much larger result = None for g in ds.index.grids: if result is None: result = hashlib.md5(bytes(g.id) + g[field].tobytes()) else: result.update(bytes(g.id) + g[field].tobytes()) g.clear_data() return result.hexdigest() def projection_values(ds, axis, field, weight_field, dobj_type): if dobj_type is not None: dobj = create_obj(ds, dobj_type) else: dobj = None if ds.domain_dimensions[axis] == 1: # This originally returned None, but None can't be converted # to a bytes array (for hashing), so use -1 as a string, # since ints can't be converted to bytes either return bytes(str(-1).encode("utf-8")) proj = ds.proj(field, axis, weight_field=weight_field, data_source=dobj) # This is to try and remove python-specific anchors in the yaml # answer file. Also, using __repr__() results in weird strings # of strings that make comparison fail even though the data is # the same result = None for k, v in proj.field_data.items(): k = k.__repr__().encode("utf8") if result is None: result = hashlib.md5(k + v.tobytes()) else: result.update(k + v.tobytes()) return result.hexdigest() def field_values(ds, field, obj_type=None, particle_type=False): # If needed build an instance of the dataset type obj = create_obj(ds, obj_type) determined_field = obj._determine_fields(field)[0] fd = ds.field_info[determined_field] # Get the proper weight field depending on if we're looking at # particles or not if particle_type: weight_field = (determined_field[0], "particle_ones") elif fd.is_sph_field: weight_field = (determined_field[0], "ones") else: weight_field = ("index", "ones") # Get the average, min, and max avg = obj.quantities.weighted_average_quantity( determined_field, weight=weight_field ) minimum, maximum = obj.quantities.extrema(field) # Return as a hashable bytestring return np.array([avg, minimum, maximum]) def pixelized_projection_values(ds, axis, field, weight_field=None, dobj_type=None): if dobj_type is not None: obj = create_obj(ds, dobj_type) else: obj = None proj = ds.proj(field, axis, weight_field=weight_field, data_source=obj) frb = proj.to_frb((1.0, "unitary"), 256) frb.render(field) if weight_field is not None: frb.render(weight_field) d = frb.data for f in proj.field_data: # Sometimes f will be a tuple. d[f"{f}_sum"] = proj.field_data[f].sum(dtype="float64") # This is to try and remove python-specific anchors in the yaml # answer file. Also, using __repr__() results in weird strings # of strings that make comparison fail even though the data is # the same result = None for k, v in d.items(): k = k.__repr__().encode("utf8") if result is None: result = hashlib.md5(k + v.tobytes()) else: result.update(k + v.tobytes()) return result.hexdigest() def small_patch_amr(ds, field, weight, axis, ds_obj): hex_digests = {} # Grid hierarchy test gh_hd = grid_hierarchy(ds) hex_digests["grid_hierarchy"] = gh_hd # Parentage relationships test pr_hd = parentage_relationships(ds) hex_digests["parentage_relationships"] = pr_hd # Grid values, projection values, and field values tests gv_hd = grid_values(ds, field) hex_digests["grid_values"] = gv_hd fv_hd = field_values(ds, field, ds_obj) hex_digests["field_values"] = fv_hd pv_hd = projection_values(ds, axis, field, weight, ds_obj) hex_digests["projection_values"] = pv_hd return hex_digests def big_patch_amr(ds, field, weight, axis, ds_obj): hex_digests = {} # Grid hierarchy test gh_hd = grid_hierarchy(ds) hex_digests["grid_hierarchy"] = gh_hd # Parentage relationships test pr_hd = parentage_relationships(ds) hex_digests["parentage_relationships"] = pr_hd # Grid values, projection values, and field values tests gv_hd = grid_values(ds, field) hex_digests["grid_values"] = gv_hd ppv_hd = pixelized_projection_values(ds, axis, field, weight, ds_obj) hex_digests["pixelized_projection_values"] = ppv_hd return hex_digests def generic_array(func, args=None, kwargs=None): if args is None: args = [] if kwargs is None: kwargs = {} return func(*args, **kwargs) def sph_answer(ds, ds_str_repr, ds_nparticles, field, weight, ds_obj, axis): # Make sure we're dealing with the right dataset assert str(ds) == ds_str_repr # Set up keys of test names hex_digests = {} dd = ds.all_data() assert dd["particle_position"].shape == (ds_nparticles, 3) tot = sum( dd[ptype, "particle_position"].shape[0] for ptype in ds.particle_types if ptype != "all" ) # Check assert tot == ds_nparticles dobj = create_obj(ds, ds_obj) s1 = dobj["ones"].sum() s2 = sum(mask.sum() for block, mask in dobj.blocks) assert s1 == s2 if field[0] in ds.particle_types: particle_type = True else: particle_type = False if not particle_type: ppv_hd = pixelized_projection_values(ds, axis, field, weight, ds_obj) hex_digests["pixelized_projection_values"] = ppv_hd fv_hd = field_values(ds, field, ds_obj, particle_type=particle_type) hex_digests["field_values"] = fv_hd return hex_digests def get_field_size_and_mean(ds, field, geometric): if geometric: obj = ds.all_data() else: obj = ds.data return np.array([obj[field].size, obj[field].mean()]) def plot_window_attribute( ds, plot_field, plot_axis, attr_name, attr_args, plot_type="SlicePlot", callback_id="", callback_runners=None, ): if callback_runners is None: callback_runners = [] plot = _create_plot_window_attribute_plot(ds, plot_type, plot_field, plot_axis, {}) for r in callback_runners: r(plot_field, plot) attr = getattr(plot, attr_name) attr(*attr_args[0], **attr_args[1]) tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) plot.save(name=tmpname) image = mpimg.imread(tmpname) os.remove(tmpname) return image def phase_plot_attribute( ds_fn, x_field, y_field, z_field, attr_name, attr_args, plot_type="PhasePlot", plot_kwargs=None, ): if plot_kwargs is None: plot_kwargs = {} data_source = ds_fn.all_data() plot = _create_phase_plot_attribute_plot( data_source, x_field, y_field, z_field, plot_type, plot_kwargs ) attr = getattr(plot, attr_name) attr(*attr_args[0], **attr_args[1]) tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) plot.save(name=tmpname) image = mpimg.imread(tmpname) os.remove(tmpname) return image def generic_image(img_fname): from yt._maintenance.deprecation import issue_deprecation_warning issue_deprecation_warning( "yt.utilities.answer_testing.answer_tests.generic_image is deprecated " "and will be removed in a future version. Please use pytest-mpl instead", since="4.4", stacklevel=2, ) img_data = mpimg.imread(img_fname) return img_data def axial_pixelization(ds): r""" This test is typically used once per geometry or coordinates type. Feed it a dataset, and it checks that the results of basic pixelization don't change. """ for i, axis in enumerate(ds.coordinates.axis_order): (bounds, center, display_center) = pw.get_window_parameters( axis, ds.domain_center, None, ds ) slc = ds.slice(axis, center[i]) xax = ds.coordinates.axis_name[ds.coordinates.x_axis[axis]] yax = ds.coordinates.axis_name[ds.coordinates.y_axis[axis]] pix_x = ds.coordinates.pixelize(axis, slc, xax, bounds, (512, 512)) pix_y = ds.coordinates.pixelize(axis, slc, yax, bounds, (512, 512)) # Wipe out all NaNs pix_x[np.isnan(pix_x)] = 0.0 pix_y[np.isnan(pix_y)] = 0.0 pix_x pix_y return pix_x, pix_y def extract_connected_sets(ds_fn, data_source, field, num_levels, min_val, max_val): n, all_sets = data_source.extract_connected_sets( field, num_levels, min_val, max_val ) result = [] for level in all_sets: for set_id in all_sets[level]: result.append( [ all_sets[level][set_id]["cell_mass"].size, all_sets[level][set_id]["cell_mass"].sum(), ] ) result = np.array(result) return result def VR_image_comparison(scene): tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) scene.save(tmpname, sigma_clip=1.0) image = mpimg.imread(tmpname) os.remove(tmpname) return image yt-project-yt-f043ac8/yt/utilities/answer_testing/api.py000066400000000000000000000001001510711153200234670ustar00rootroot00000000000000from yt.utilities.answer_testing.framework import AnswerTesting yt-project-yt-f043ac8/yt/utilities/answer_testing/framework.py000066400000000000000000001162331510711153200247320ustar00rootroot00000000000000""" Title: framework.py Purpose: Contains answer tests that are used by yt's various frontends """ import contextlib import hashlib import logging import os import pickle import shelve import sys import tempfile import time import warnings import zlib from collections import defaultdict import numpy as np from matplotlib import image as mpimg from matplotlib.testing.compare import compare_images from nose.plugins import Plugin from numpy.testing import assert_almost_equal, assert_equal from yt.config import ytcfg from yt.data_objects.static_output import Dataset from yt.funcs import get_pbar, get_yt_version from yt.loaders import load, load_simulation from yt.testing import ( assert_allclose_units, assert_rel_equal, skipif, ) from yt.utilities.exceptions import ( YTAmbiguousDataType, YTCloudError, YTNoAnswerNameSpecified, YTNoOldAnswer, YTUnidentifiedDataType, ) from yt.utilities.logger import disable_stream_logging from yt.visualization import ( image_writer as image_writer, particle_plots as particle_plots, plot_window as pw, profile_plotter as profile_plotter, ) mylog = logging.getLogger("nose.plugins.answer-testing") run_big_data = False # Set the latest gold and local standard filenames _latest = ytcfg.get("yt", "gold_standard_filename") _latest_local = ytcfg.get("yt", "local_standard_filename") _url_path = ytcfg.get("yt", "answer_tests_url") class AnswerTesting(Plugin): name = "answer-testing" _my_version = None def options(self, parser, env=os.environ): super().options(parser, env=env) parser.add_option( "--answer-name", dest="answer_name", metavar="str", default=None, help="The name of the standard to store/compare against", ) parser.add_option( "--answer-store", dest="store_results", metavar="bool", default=False, action="store_true", help="Should we store this result instead of comparing?", ) parser.add_option( "--local", dest="local_results", default=False, action="store_true", help="Store/load reference results locally?", ) parser.add_option( "--answer-big-data", dest="big_data", default=False, help="Should we run against big data, too?", action="store_true", ) parser.add_option( "--local-dir", dest="output_dir", metavar="str", help="The name of the directory to store local results", ) @property def my_version(self, version=None): if self._my_version is not None: return self._my_version if version is None: try: version = get_yt_version() except Exception: version = f"UNKNOWN{time.time()}" self._my_version = version return self._my_version def configure(self, options, conf): super().configure(options, conf) if not self.enabled: return disable_stream_logging() # Parse through the storage flags to make sense of them # and use reasonable defaults # If we're storing the data, default storage name is local # latest version if options.store_results: if options.answer_name is None: self.store_name = _latest_local else: self.store_name = options.answer_name self.compare_name = None # if we're not storing, then we're comparing, and we want default # comparison name to be the latest gold standard # either on network or local else: if options.answer_name is None: if options.local_results: self.compare_name = _latest_local else: self.compare_name = _latest else: self.compare_name = options.answer_name self.store_name = self.my_version self.store_results = options.store_results ytcfg["yt", "internals", "within_testing"] = True AnswerTestingTest.result_storage = self.result_storage = defaultdict(dict) if self.compare_name == "SKIP": self.compare_name = None elif self.compare_name == "latest": self.compare_name = _latest # Local/Cloud storage if options.local_results: if options.output_dir is None: print("Please supply an output directory with the --local-dir option") sys.exit(1) storage_class = AnswerTestLocalStorage output_dir = os.path.realpath(options.output_dir) # Fix up filename for local storage if self.compare_name is not None: self.compare_name = os.path.join( output_dir, self.compare_name, self.compare_name ) # Create a local directory only when `options.answer_name` is # provided. If it is not provided then creating local directory # will depend on the `AnswerTestingTest.answer_name` value of the # test, this case is handled in AnswerTestingTest class. if options.store_results and options.answer_name is not None: name_dir_path = os.path.join(output_dir, self.store_name) if not os.path.isdir(name_dir_path): os.makedirs(name_dir_path) self.store_name = os.path.join(name_dir_path, self.store_name) else: storage_class = AnswerTestCloudStorage # Initialize answer/reference storage AnswerTestingTest.reference_storage = self.storage = storage_class( self.compare_name, self.store_name ) AnswerTestingTest.options = options self.local_results = options.local_results global run_big_data run_big_data = options.big_data def finalize(self, result=None): if not self.store_results: return self.storage.dump(self.result_storage) def help(self): return "yt answer testing support" class AnswerTestStorage: def __init__(self, reference_name=None, answer_name=None): self.reference_name = reference_name self.answer_name = answer_name self.cache = {} def dump(self, result_storage, result): raise NotImplementedError def get(self, ds_name, default=None): raise NotImplementedError class AnswerTestCloudStorage(AnswerTestStorage): def get(self, ds_name, default=None): import urllib.error import urllib.request if self.reference_name is None: return default if ds_name in self.cache: return self.cache[ds_name] url = _url_path.format(self.reference_name, ds_name) try: resp = urllib.request.urlopen(url) except urllib.error.HTTPError as exc: raise YTNoOldAnswer(url) from exc else: for _ in range(3): try: data = resp.read() except Exception: time.sleep(0.01) else: # We were successful break else: # Raise error if all tries were unsuccessful raise YTCloudError(url) # This is dangerous, but we have a controlled S3 environment rv = pickle.loads(data) self.cache[ds_name] = rv return rv def progress_callback(self, current, total): self.pbar.update(current) def dump(self, result_storage): if self.answer_name is None: return # This is where we dump our result storage up to Amazon, if we are able # to. import pyrax credentials = os.path.expanduser(os.path.join("~", ".yt", "rackspace")) pyrax.set_credential_file(credentials) cf = pyrax.cloudfiles c = cf.get_container("yt-answer-tests") pb = get_pbar("Storing results ", len(result_storage)) for i, ds_name in enumerate(result_storage): pb.update(i + 1) rs = pickle.dumps(result_storage[ds_name]) object_name = f"{self.answer_name}_{ds_name}" if object_name in c.get_object_names(): obj = c.get_object(object_name) c.delete_object(obj) c.store_object(object_name, rs) pb.finish() class AnswerTestLocalStorage(AnswerTestStorage): def dump(self, result_storage): # The 'tainted' attribute is automatically set to 'True' # if the dataset required for an answer test is missing # (see can_run_ds(). # This logic check prevents creating a shelve with empty answers. storage_is_tainted = result_storage.get("tainted", False) if self.answer_name is None or storage_is_tainted: return # Store data using shelve ds = shelve.open(self.answer_name, protocol=-1) for ds_name in result_storage: answer_name = f"{ds_name}" if answer_name in ds: mylog.info("Overwriting %s", answer_name) ds[answer_name] = result_storage[ds_name] ds.close() def get(self, ds_name, default=None): if self.reference_name is None: return default # Read data using shelve answer_name = f"{ds_name}" os.makedirs(os.path.dirname(self.reference_name), exist_ok=True) ds = shelve.open(self.reference_name, protocol=-1) try: result = ds[answer_name] except KeyError: result = default ds.close() return result @contextlib.contextmanager def temp_cwd(cwd): oldcwd = os.getcwd() os.chdir(cwd) yield os.chdir(oldcwd) def can_run_ds(ds_fn, file_check=False): result_storage = AnswerTestingTest.result_storage if isinstance(ds_fn, Dataset): return result_storage is not None path = ytcfg.get("yt", "test_data_dir") if not os.path.isdir(path): return False if file_check: return os.path.isfile(os.path.join(path, ds_fn)) and result_storage is not None try: load(ds_fn) except FileNotFoundError: if ytcfg.get("yt", "internals", "strict_requires"): if result_storage is not None: result_storage["tainted"] = True raise return False except (YTUnidentifiedDataType, YTAmbiguousDataType): return False return result_storage is not None def data_dir_load(ds_fn, cls=None, args=None, kwargs=None): args = args or () kwargs = kwargs or {} path = ytcfg.get("yt", "test_data_dir") if isinstance(ds_fn, Dataset): return ds_fn if not os.path.isdir(path): return False if cls is None: ds = load(ds_fn, *args, **kwargs) else: ds = cls(os.path.join(path, ds_fn), *args, **kwargs) ds.index return ds def data_dir_load_v2(fn, *args, **kwargs): # a version of data_dir_load without type flexibility # that is simpler to reason about path = os.path.join(ytcfg.get("yt", "test_data_dir"), fn) return load(path, *args, **kwargs) def sim_dir_load(sim_fn, path=None, sim_type="Enzo", find_outputs=False): if path is None and not os.path.exists(sim_fn): raise OSError if os.path.exists(sim_fn) or not path: path = "." return load_simulation( os.path.join(path, sim_fn), sim_type, find_outputs=find_outputs ) class AnswerTestingTest: reference_storage = None result_storage = None prefix = "" options = None # This variable should be set if we are not providing `--answer-name` as # command line parameter while running yt's answer testing using nosetests. answer_name = None def __init__(self, ds_fn): if ds_fn is None: self.ds = None elif isinstance(ds_fn, Dataset): self.ds = ds_fn else: self.ds = data_dir_load(ds_fn, kwargs={"unit_system": "code"}) def __call__(self): if AnswerTestingTest.result_storage is None: return nv = self.run() # Test answer name should be provided either as command line parameters # or by setting AnswerTestingTest.answer_name if self.options.answer_name is None and self.answer_name is None: raise YTNoAnswerNameSpecified() # This is for running answer test when `--answer-name` is not set in # nosetests command line arguments. In this case, set the answer_name # from the `answer_name` keyword in the test case if self.options.answer_name is None: pyver = f"py{sys.version_info.major}{sys.version_info.minor}" self.answer_name = f"{pyver}_{self.answer_name}" answer_store_dir = os.path.realpath(self.options.output_dir) ref_name = os.path.join( answer_store_dir, self.answer_name, self.answer_name ) self.reference_storage.reference_name = ref_name self.reference_storage.answer_name = ref_name # If we are generating golden answers (passed --answer-store arg): # - create the answer directory for this test # - self.reference_storage.answer_name will be path to answer files if self.options.store_results: answer_test_dir = os.path.join(answer_store_dir, self.answer_name) if not os.path.isdir(answer_test_dir): os.makedirs(answer_test_dir) self.reference_storage.reference_name = None if self.reference_storage.reference_name is not None: # Compare test generated values against the golden answer dd = self.reference_storage.get(self.storage_name) if dd is None or self.description not in dd: raise YTNoOldAnswer(f"{self.storage_name} : {self.description}") ov = dd[self.description] self.compare(nv, ov) else: # Store results, hence do nothing (in case of --answer-store arg) ov = None self.result_storage[self.storage_name][self.description] = nv @property def storage_name(self): if self.prefix != "": return f"{self.prefix}_{self.ds}" return str(self.ds) def compare(self, new_result, old_result): raise RuntimeError def create_plot(self, ds, plot_type, plot_field, plot_axis, plot_kwargs=None): # plot_type should be a string # plot_kwargs should be a dict if plot_type is None: raise RuntimeError("Must explicitly request a plot type") cls = getattr(pw, plot_type, None) if cls is None: cls = getattr(particle_plots, plot_type) plot = cls(*(ds, plot_axis, plot_field), **plot_kwargs) return plot @property def sim_center(self): """ This returns the center of the domain. """ return 0.5 * (self.ds.domain_right_edge + self.ds.domain_left_edge) @property def max_dens_location(self): """ This is a helper function to return the location of the most dense point. """ return self.ds.find_max(("gas", "density"))[1] @property def entire_simulation(self): """ Return an unsorted array of values that cover the entire domain. """ return self.ds.all_data() @property def description(self): obj_type = getattr(self, "obj_type", None) if obj_type is None: oname = "all" else: oname = "_".join(str(s) for s in obj_type) args = [self._type_name, str(self.ds), oname] args += [str(getattr(self, an)) for an in self._attrs] suffix = getattr(self, "suffix", None) if suffix: args.append(suffix) return "_".join(args).replace(".", "_") class FieldValuesTest(AnswerTestingTest): _type_name = "FieldValues" _attrs = ("field",) def __init__(self, ds_fn, field, obj_type=None, particle_type=False, decimals=10): super().__init__(ds_fn) self.obj_type = obj_type self.field = field self.particle_type = particle_type self.decimals = decimals def run(self): obj = create_obj(self.ds, self.obj_type) field = obj._determine_fields(self.field)[0] fd = self.ds.field_info[field] if self.particle_type: weight_field = (field[0], "particle_ones") elif fd.is_sph_field: weight_field = (field[0], "ones") else: weight_field = ("index", "ones") avg = obj.quantities.weighted_average_quantity(field, weight=weight_field) mi, ma = obj.quantities.extrema(self.field) return [avg, mi, ma] def compare(self, new_result, old_result): err_msg = f"Field values for {self.field} not equal." if hasattr(new_result, "d"): new_result = new_result.d if hasattr(old_result, "d"): old_result = old_result.d if self.decimals is None: assert_equal(new_result, old_result, err_msg=err_msg, verbose=True) else: # What we do here is check if the old_result has units; if not, we # assume they will be the same as the units of new_result. if isinstance(old_result, np.ndarray) and not hasattr( old_result, "in_units" ): # coerce it here to the same units old_result = old_result * new_result[0].uq assert_allclose_units( new_result, old_result, 10.0 ** (-self.decimals), err_msg=err_msg, verbose=True, ) class AllFieldValuesTest(AnswerTestingTest): _type_name = "AllFieldValues" _attrs = ("field",) def __init__(self, ds_fn, field, obj_type=None, decimals=None): super().__init__(ds_fn) self.obj_type = obj_type self.field = field self.decimals = decimals def run(self): obj = create_obj(self.ds, self.obj_type) return obj[self.field] def compare(self, new_result, old_result): err_msg = f"All field values for {self.field} not equal." if hasattr(new_result, "d"): new_result = new_result.d if hasattr(old_result, "d"): old_result = old_result.d if self.decimals is None: assert_equal(new_result, old_result, err_msg=err_msg, verbose=True) else: assert_rel_equal( new_result, old_result, self.decimals, err_msg=err_msg, verbose=True ) class ProjectionValuesTest(AnswerTestingTest): _type_name = "ProjectionValues" _attrs = ("field", "axis", "weight_field") def __init__( self, ds_fn, axis, field, weight_field=None, obj_type=None, decimals=10 ): super().__init__(ds_fn) self.axis = axis self.field = field self.weight_field = weight_field self.obj_type = obj_type self.decimals = decimals def run(self): if self.obj_type is not None: obj = create_obj(self.ds, self.obj_type) else: obj = None if self.ds.domain_dimensions[self.axis] == 1: return None proj = self.ds.proj( self.field, self.axis, weight_field=self.weight_field, data_source=obj ) return proj.field_data def compare(self, new_result, old_result): if new_result is None: return assert len(new_result) == len(old_result) nind, oind = None, None for k in new_result: assert k in old_result if oind is None: oind = np.array(np.isnan(old_result[k])) np.logical_or(oind, np.isnan(old_result[k]), oind) if nind is None: nind = np.array(np.isnan(new_result[k])) np.logical_or(nind, np.isnan(new_result[k]), nind) oind = ~oind nind = ~nind for k in new_result: err_msg = f"{k} values of {self.field} ({self.weight_field} weighted) projection (axis {self.axis}) not equal." if k == "weight_field": # Our weight_field can vary between unit systems, whereas we # can do a unitful comparison for the other fields. So we do # not do the test here. continue nres, ores = new_result[k][nind], old_result[k][oind] if hasattr(nres, "d"): nres = nres.d if hasattr(ores, "d"): ores = ores.d if self.decimals is None: assert_equal(nres, ores, err_msg=err_msg) else: assert_allclose_units( nres, ores, 10.0**-(self.decimals), err_msg=err_msg ) class PixelizedProjectionValuesTest(AnswerTestingTest): _type_name = "PixelizedProjectionValues" _attrs = ("field", "axis", "weight_field") def __init__(self, ds_fn, axis, field, weight_field=None, obj_type=None): super().__init__(ds_fn) self.axis = axis self.field = field self.weight_field = weight_field self.obj_type = obj_type def _get_frb(self, obj): proj = self.ds.proj( self.field, self.axis, weight_field=self.weight_field, data_source=obj ) frb = proj.to_frb((1.0, "unitary"), 256) return proj, frb def run(self): if self.obj_type is not None: obj = create_obj(self.ds, self.obj_type) else: obj = None proj, frb = self._get_frb(obj) frb.render(self.field) if self.weight_field is not None: frb.render(self.weight_field) d = frb.data for f in proj.field_data: # Sometimes f will be a tuple. d[f"{f}_sum"] = proj.field_data[f].sum(dtype="float64") return d def compare(self, new_result, old_result): assert len(new_result) == len(old_result) for k in new_result: assert k in old_result for k in new_result: # weight_field does not have units, so we do not directly compare them if k == "weight_field_sum": continue try: assert_allclose_units(new_result[k], old_result[k], 1e-10) except AssertionError: dump_images(new_result[k], old_result[k]) raise class PixelizedParticleProjectionValuesTest(PixelizedProjectionValuesTest): def _get_frb(self, obj): proj_plot = particle_plots.ParticleProjectionPlot( self.ds, self.axis, [self.field], weight_field=self.weight_field ) return proj_plot.data_source, proj_plot.frb class GridValuesTest(AnswerTestingTest): _type_name = "GridValues" _attrs = ("field",) def __init__(self, ds_fn, field): super().__init__(ds_fn) self.field = field def run(self): hashes = {} for g in self.ds.index.grids: hashes[g.id] = hashlib.md5(g[self.field].tobytes()).hexdigest() g.clear_data() return hashes def compare(self, new_result, old_result): assert len(new_result) == len(old_result) for k in new_result: assert k in old_result for k in new_result: if hasattr(new_result[k], "d"): new_result[k] = new_result[k].d if hasattr(old_result[k], "d"): old_result[k] = old_result[k].d assert_equal(new_result[k], old_result[k]) class VerifySimulationSameTest(AnswerTestingTest): _type_name = "VerifySimulationSame" _attrs = () def __init__(self, simulation_obj): self.ds = simulation_obj def run(self): result = [ds.current_time for ds in self.ds] return result def compare(self, new_result, old_result): assert_equal( len(new_result), len(old_result), err_msg="Number of outputs not equal.", verbose=True, ) for i in range(len(new_result)): assert_equal( new_result[i], old_result[i], err_msg="Output times not equal.", verbose=True, ) class GridHierarchyTest(AnswerTestingTest): _type_name = "GridHierarchy" _attrs = () def run(self): result = {} result["grid_dimensions"] = self.ds.index.grid_dimensions result["grid_left_edges"] = self.ds.index.grid_left_edge result["grid_right_edges"] = self.ds.index.grid_right_edge result["grid_levels"] = self.ds.index.grid_levels result["grid_particle_count"] = self.ds.index.grid_particle_count return result def compare(self, new_result, old_result): for k in new_result: if hasattr(new_result[k], "d"): new_result[k] = new_result[k].d if hasattr(old_result[k], "d"): old_result[k] = old_result[k].d assert_equal(new_result[k], old_result[k]) class ParentageRelationshipsTest(AnswerTestingTest): _type_name = "ParentageRelationships" _attrs = () def run(self): result = {} result["parents"] = [] result["children"] = [] for g in self.ds.index.grids: p = g.Parent if p is None: result["parents"].append(None) elif hasattr(p, "id"): result["parents"].append(p.id) else: result["parents"].append([pg.id for pg in p]) result["children"].append([c.id for c in g.Children]) return result def compare(self, new_result, old_result): for newp, oldp in zip( new_result["parents"], old_result["parents"], strict=True, ): assert newp == oldp for newc, oldc in zip( new_result["children"], old_result["children"], strict=True, ): assert newc == oldc def dump_images(new_result, old_result, decimals=10): tmpfd, old_image = tempfile.mkstemp(prefix="baseline_", suffix=".png") os.close(tmpfd) tmpfd, new_image = tempfile.mkstemp(prefix="thisPR_", suffix=".png") os.close(tmpfd) image_writer.write_projection(new_result, new_image) image_writer.write_projection(old_result, old_image) results = compare_images(old_image, new_image, 10 ** (-decimals)) if results is not None: tempfiles = [ line.strip() for line in results.split("\n") if line.endswith(".png") ] for fn in tempfiles: sys.stderr.write(f"\n[[ATTACHMENT|{fn}]]") sys.stderr.write("\n") def ensure_image_comparability(a, b): # pad nans to the right and the bottom of two images to make them comparable # via matplotlib if they do not have the same shape if a.shape == b.shape: return a, b assert a.shape[2:] == b.shape[2:] warnings.warn( f"Images have different shapes {a.shape} and {b.shape}. " "Padding nans to make them comparable.", stacklevel=2, ) smallest_containing_shape = ( max(a.shape[0], b.shape[0]), max(a.shape[1], b.shape[1]), *a.shape[2:], ) pa = np.full(smallest_containing_shape, np.nan) pa[: a.shape[0], : a.shape[1], ...] = a pb = np.full(smallest_containing_shape, np.nan) pb[: b.shape[0], : b.shape[1], ...] = b return pa, pb def compare_image_lists(new_result, old_result, decimals): fns = [] for _ in range(2): tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) fns.append(tmpname) num_images = len(old_result) assert num_images > 0 for i in range(num_images): expected = pickle.loads(zlib.decompress(old_result[i])) actual = pickle.loads(zlib.decompress(new_result[i])) expected_p, actual_p = ensure_image_comparability(expected, actual) mpimg.imsave(fns[0], expected_p) mpimg.imsave(fns[1], actual_p) results = compare_images(fns[0], fns[1], 10 ** (-decimals)) if results is not None: tempfiles = [ line.strip() for line in results.split("\n") if line.endswith(".png") ] for fn, img, padded in zip( tempfiles, (expected, actual), (expected_p, actual_p), strict=True, ): # padded images are convenient for comparison # but what we really want to store and upload # are the actual results if padded.shape != img.shape: mpimg.imsave(fn, img) if os.environ.get("JENKINS_HOME") is not None: for fn in tempfiles: sys.stderr.write(f"\n[[ATTACHMENT|{fn}]]") sys.stderr.write("\n") assert_equal(results, None, results) for fn in fns: os.remove(fn) class PlotWindowAttributeTest(AnswerTestingTest): _type_name = "PlotWindowAttribute" _attrs = ( "plot_type", "plot_field", "plot_axis", "attr_name", "attr_args", "callback_id", ) def __init__( self, ds_fn: str, plot_field: str, plot_axis: str, attr_name: str | None = None, attr_args: tuple | None = None, decimals: int | None = 12, plot_type: str | None = "SlicePlot", callback_id: str | None = "", callback_runners: tuple | None = None, ): super().__init__(ds_fn) self.plot_type = plot_type self.plot_field = plot_field self.plot_axis = plot_axis self.plot_kwargs = {} self.attr_name = attr_name self.attr_args = attr_args self.decimals = decimals # callback_id is so that we don't have to hash the actual callbacks # run, but instead we call them something self.callback_id = callback_id if callback_runners is None: callback_runners = () self.callback_runners = callback_runners def run(self): plot = self.create_plot( self.ds, self.plot_type, self.plot_field, self.plot_axis, self.plot_kwargs ) for r in self.callback_runners: r(self, plot) if self.attr_name and self.attr_args: attr = getattr(plot, self.attr_name) attr(*self.attr_args[0], **self.attr_args[1]) tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) plot.save(name=tmpname) image = mpimg.imread(tmpname) os.remove(tmpname) return [zlib.compress(image.dumps())] def compare(self, new_result, old_result): compare_image_lists(new_result, old_result, self.decimals) class PhasePlotAttributeTest(AnswerTestingTest): _type_name = "PhasePlotAttribute" _attrs = ("plot_type", "x_field", "y_field", "z_field", "attr_name", "attr_args") def __init__( self, ds_fn, x_field, y_field, z_field, attr_name, attr_args, decimals, plot_type="PhasePlot", ): super().__init__(ds_fn) self.data_source = self.ds.all_data() self.plot_type = plot_type self.x_field = x_field self.y_field = y_field self.z_field = z_field self.plot_kwargs = {} self.attr_name = attr_name self.attr_args = attr_args self.decimals = decimals def create_plot( self, data_source, x_field, y_field, z_field, plot_type, plot_kwargs=None ): # plot_type should be a string # plot_kwargs should be a dict if plot_type is None: raise RuntimeError("Must explicitly request a plot type") cls = getattr(profile_plotter, plot_type, None) if cls is None: cls = getattr(particle_plots, plot_type) plot = cls(*(data_source, x_field, y_field, z_field), **plot_kwargs) return plot def run(self): plot = self.create_plot( self.data_source, self.x_field, self.y_field, self.z_field, self.plot_type, self.plot_kwargs, ) attr = getattr(plot, self.attr_name) attr(*self.attr_args[0], **self.attr_args[1]) tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) plot.save(name=tmpname) image = mpimg.imread(tmpname) os.remove(tmpname) return [zlib.compress(image.dumps())] def compare(self, new_result, old_result): compare_image_lists(new_result, old_result, self.decimals) class GenericArrayTest(AnswerTestingTest): _type_name = "GenericArray" _attrs = ("array_func_name", "args", "kwargs") def __init__(self, ds_fn, array_func, args=None, kwargs=None, decimals=None): super().__init__(ds_fn) self.array_func = array_func self.array_func_name = array_func.__name__ self.args = args self.kwargs = kwargs self.decimals = decimals def run(self): if self.args is None: args = [] else: args = self.args if self.kwargs is None: kwargs = {} else: kwargs = self.kwargs return self.array_func(*args, **kwargs) def compare(self, new_result, old_result): if not isinstance(new_result, dict): new_result = {"answer": new_result} old_result = {"answer": old_result} assert_equal( len(new_result), len(old_result), err_msg="Number of outputs not equal.", verbose=True, ) for k in new_result: if hasattr(new_result[k], "d"): new_result[k] = new_result[k].d if hasattr(old_result[k], "d"): old_result[k] = old_result[k].d if self.decimals is None: assert_almost_equal(new_result[k], old_result[k]) else: assert_allclose_units( new_result[k], old_result[k], 10 ** (-self.decimals) ) class AxialPixelizationTest(AnswerTestingTest): # This test is typically used once per geometry or coordinates type. # Feed it a dataset, and it checks that the results of basic pixelization # don't change. _type_name = "AxialPixelization" _attrs = ("geometry",) def __init__(self, ds_fn, decimals=None): super().__init__(ds_fn) self.decimals = decimals self.geometry = self.ds.coordinates.name def run(self): rv = {} ds = self.ds for i, axis in enumerate(ds.coordinates.axis_order): (bounds, center, display_center) = pw.get_window_parameters( axis, ds.domain_center, None, ds ) slc = ds.slice(axis, center[i]) xax = ds.coordinates.axis_name[ds.coordinates.x_axis[axis]] yax = ds.coordinates.axis_name[ds.coordinates.y_axis[axis]] pix_x = ds.coordinates.pixelize(axis, slc, ("gas", xax), bounds, (512, 512)) pix_y = ds.coordinates.pixelize(axis, slc, ("gas", yax), bounds, (512, 512)) # Wipe out invalid values (fillers) pix_x[~np.isfinite(pix_x)] = 0.0 pix_y[~np.isfinite(pix_y)] = 0.0 rv[f"{axis}_x"] = pix_x rv[f"{axis}_y"] = pix_y return rv def compare(self, new_result, old_result): assert_equal( len(new_result), len(old_result), err_msg="Number of outputs not equal.", verbose=True, ) for k in new_result: if hasattr(new_result[k], "d"): new_result[k] = new_result[k].d if hasattr(old_result[k], "d"): old_result[k] = old_result[k].d if self.decimals is None: assert_almost_equal(new_result[k], old_result[k]) else: assert_allclose_units( new_result[k], old_result[k], 10 ** (-self.decimals) ) def requires_answer_testing(): return skipif( AnswerTestingTest.result_storage is None, reason="answer testing storage is not properly setup", ) def requires_ds(ds_fn, big_data=False, file_check=False): condition = (big_data and not run_big_data) or not can_run_ds(ds_fn, file_check) return skipif(condition, reason=f"cannot load dataset {ds_fn}") def small_patch_amr(ds_fn, fields, input_center="max", input_weight=("gas", "density")): if not can_run_ds(ds_fn): return dso = [None, ("sphere", (input_center, (0.1, "unitary")))] yield GridHierarchyTest(ds_fn) yield ParentageRelationshipsTest(ds_fn) for field in fields: yield GridValuesTest(ds_fn, field) for dobj_name in dso: for axis in [0, 1, 2]: for weight_field in [None, input_weight]: yield ProjectionValuesTest( ds_fn, axis, field, weight_field, dobj_name ) yield FieldValuesTest(ds_fn, field, dobj_name) def big_patch_amr(ds_fn, fields, input_center="max", input_weight=("gas", "density")): if not can_run_ds(ds_fn): return dso = [None, ("sphere", (input_center, (0.1, "unitary")))] yield GridHierarchyTest(ds_fn) yield ParentageRelationshipsTest(ds_fn) for field in fields: yield GridValuesTest(ds_fn, field) for axis in [0, 1, 2]: for dobj_name in dso: for weight_field in [None, input_weight]: yield PixelizedProjectionValuesTest( ds_fn, axis, field, weight_field, dobj_name ) def _particle_answers( ds, ds_str_repr, ds_nparticles, fields, proj_test_class, center="c" ): if not can_run_ds(ds): return assert_equal(str(ds), ds_str_repr) dso = [None, ("sphere", (center, (0.1, "unitary")))] dd = ds.all_data() # this needs to explicitly be "all" assert_equal(dd["all", "particle_position"].shape, (ds_nparticles, 3)) tot = sum( dd[ptype, "particle_position"].shape[0] for ptype in ds.particle_types_raw ) assert_equal(tot, ds_nparticles) for dobj_name in dso: for field, weight_field in fields.items(): particle_type = field[0] in ds.particle_types for axis in [0, 1, 2]: if not particle_type: yield proj_test_class(ds, axis, field, weight_field, dobj_name) yield FieldValuesTest(ds, field, dobj_name, particle_type=particle_type) def nbody_answer(ds, ds_str_repr, ds_nparticles, fields, center="c"): return _particle_answers( ds, ds_str_repr, ds_nparticles, fields, PixelizedParticleProjectionValuesTest, center=center, ) def sph_answer(ds, ds_str_repr, ds_nparticles, fields, center="c"): return _particle_answers( ds, ds_str_repr, ds_nparticles, fields, PixelizedProjectionValuesTest, center=center, ) def create_obj(ds, obj_type): # obj_type should be tuple of # ( obj_name, ( args ) ) if obj_type is None: return ds.all_data() cls = getattr(ds, obj_type[0]) obj = cls(*obj_type[1]) return obj yt-project-yt-f043ac8/yt/utilities/answer_testing/level_sets_tests.py000066400000000000000000000023611510711153200263200ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.utilities.answer_testing.framework import AnswerTestingTest class ExtractConnectedSetsTest(AnswerTestingTest): _type_name = "ExtractConnectedSets" _attrs = () def __init__(self, ds_fn, data_source, field, num_levels, min_val, max_val): super().__init__(ds_fn) self.data_source = data_source self.field = field self.num_levels = num_levels self.min_val = min_val self.max_val = max_val def run(self): n, all_sets = self.data_source.extract_connected_sets( self.field, self.num_levels, self.min_val, self.max_val ) result = [] for level in all_sets: for set_id in all_sets[level]: result.append( [ all_sets[level][set_id]["cell_mass"].size, all_sets[level][set_id]["cell_mass"].sum(), ] ) result = np.array(result) return result def compare(self, new_result, old_result): err_msg = f"Size and/or mass of connected sets do not agree for {self.ds_fn}." assert_equal(new_result, old_result, err_msg=err_msg, verbose=True) yt-project-yt-f043ac8/yt/utilities/answer_testing/testing_utilities.py000066400000000000000000000352571510711153200265130ustar00rootroot00000000000000import functools import hashlib import inspect import os import numpy as np import pytest import yaml import yt.visualization.particle_plots as particle_plots import yt.visualization.plot_window as pw import yt.visualization.profile_plotter as profile_plotter from yt.config import ytcfg from yt.data_objects.selection_objects.region import YTRegion from yt.data_objects.static_output import Dataset from yt.loaders import load, load_simulation from yt.utilities.on_demand_imports import _h5py as h5py from yt.visualization.volume_rendering.scene import Scene def _streamline_for_io(params): r""" Put test results in a more io-friendly format. Many of yt's tests use objects such as tuples as test parameters (fields, for instance), but when these objects are written to a yaml file, yaml includes python specific anchors that make the file harder to read and less portable. The goal of this function is to convert these objects to strings (using __repr__() has it's own issues) in order to solve this problem. Parameters ---------- params : dict The dictionary of test parameters in the form {param_name : param_value}. Returns ------- streamlined_params : dict The dictionary of parsed and converted {param_name : param_value} pairs. """ streamlined_params = {} for key, value in params.items(): # Check for user-defined functions if inspect.isfunction(key): key = key.__name__ if inspect.isfunction(value): value = value.__name__ # The key can be nested iterables, e.g., # d = [None, ('sphere', (center, (0.1, 'unitary')))] so we need # to use recursion if not isinstance(key, str) and hasattr(key, "__iter__"): key = _iterable_to_string(key) # The value can also be nested iterables if not isinstance(value, str) and hasattr(value, "__iter__"): value = _iterable_to_string(value) # Scene objects need special treatment to make them more IO friendly if isinstance(value, Scene): value = "Scene" elif isinstance(value, YTRegion): value = "Region" streamlined_params[key] = value return streamlined_params def _iterable_to_string(iterable): r""" An extension of streamline_for_io that does the work of making an iterable more io-friendly. Parameters ---------- iterable : python iterable The object to be parsed and converted. Returns ------- result : str The io-friendly version of the passed iterable. """ result = iterable.__class__.__name__ for elem in iterable: # Check for user-defined functions if inspect.isfunction(elem): result += "_" + elem.__name__ # Non-string iterables (e.g., lists, tuples, etc.) elif not isinstance(elem, str) and hasattr(elem, "__iter__"): result += "_" + _iterable_to_string(elem) # Non-string non-iterables (ints, floats, etc.) elif not isinstance(elem, str) and not hasattr(elem, "__iter__"): result += "_" + str(elem) # Strings elif isinstance(elem, str): result += "_" + elem return result def _hash_results(results): r""" Driver function for hashing the test result. Parameters ---------- results : dict Dictionary of {test_name : test_result} pairs. Returns ------- results : dict Same as the passed results, but the test_results are now hex digests of the hashed test_result. """ # Here, results should be comprised of only the tests, not the test # parameters # Use a new dictionary so as to not overwrite the non-hashed test # results in case those are to be saved hashed_results = {} for test_name, test_value in results.items(): hashed_results[test_name] = generate_hash(test_value) return hashed_results def _hash_dict(data): r""" Specifically handles hashing a dictionary object. Parameters ---------- data : dict The dictionary to be hashed. Returns ------- hd.hexdigest : str The hex digest of the hashed dictionary. """ hd = None for key, value in data.items(): # Some keys are tuples, not strings if not isinstance(key, str): key = key.__repr__() # Test suites can return values that are dictionaries of other tests if isinstance(value, dict): hashed_data = _hash_dict(value) else: hashed_data = bytearray(key.encode("utf8")) + bytearray(value) # If we're returning from a recursive call (and therefore hashed_data # is a hex digest), we need to encode the string before it can be # hashed if isinstance(hashed_data, str): hashed_data = hashed_data.encode("utf8") if hd is None: hd = hashlib.md5(hashed_data) else: hd.update(hashed_data) return hd.hexdigest() def generate_hash(data): r""" Actually performs the hash operation. Parameters ---------- data : python object Data to be hashed. Returns ------- hd : str Hex digest of the hashed data. """ # Sometimes md5 complains that the data is not contiguous, so we # make it so here if isinstance(data, np.ndarray): data = np.ascontiguousarray(data) elif isinstance(data, str): data = data.encode("utf-8") # Try to hash. Some tests return hashable types (like ndarrays) and # others don't (such as dictionaries) try: hd = hashlib.md5(data).hexdigest() # Handle those tests that return non-hashable types. This is done # here instead of in the tests themselves to try and reduce boilerplate # and provide a central location where all of this is done in case it needs # to be changed except TypeError: if isinstance(data, dict): hd = _hash_dict(data) elif data is None: hd = hashlib.md5(bytes(str(-1).encode("utf-8"))).hexdigest() else: raise return hd def _save_result(data, output_file): r""" Saves the test results to the desired answer file. Parameters ---------- data : dict Test results to be saved. output_file : str Name of file to save results to. """ with open(output_file, "a") as f: yaml.dump(data, f) def _save_raw_arrays(arrays, answer_file, func_name): r""" Saves the raw arrays produced from answer tests to a file. The structure of `answer_file` is: each test function (e.g., test_toro1d[0-None-None-0]) forms a group. Within each group is a hdf5 dataset named after the test (e.g., field_values). The value stored in each dataset is the raw array corresponding to that test and function. Parameters ---------- arrays : dict Keys are the test name (e.g. field_values) and the values are the actual answer arrays produced by the test. answer_file : str The name of the file to save the answers to, in hdf5 format. func_name : str The name of the function (possibly augmented by pytest with test parameters) that called the test functions (e.g, test_toro1d). """ with h5py.File(answer_file, "a") as f: grp = f.create_group(func_name) for test_name, test_data in arrays.items(): # Some answer tests (e.g., grid_values, projection_values) # return a dictionary, which cannot be handled by h5py if isinstance(test_data, dict): sub_grp = grp.create_group(test_name) _parse_raw_answer_dict(test_data, sub_grp) else: # Some tests return None, which hdf5 can't handle, and there is # no proxy, so we have to make one ourselves. Using -1 if test_data is None: test_data = -1 grp.create_dataset(test_name, data=test_data) def _parse_raw_answer_dict(d, h5grp): for k, v in d.items(): if isinstance(v, dict): h5_sub_grp = h5grp.create_group(k) _parse_raw_answer_dict(v, h5_sub_grp) else: k = str(k) h5grp.create_dataset(k, data=v) def _compare_raw_arrays(arrays, answer_file, func_name): r""" Reads in previously saved raw array data and compares the current results with the old ones. The structure of `answer_file` is: each test function (e.g., test_toro1d[0-None-None-0]) forms a group. Within each group is a hdf5 dataset named after the test (e.g., field_values). The value stored in each dataset is the raw array corresponding to that test and function. Parameters ---------- arrays : dict Keys are the test name (e.g. field_values) and the values are the actual answer arrays produced by the test. answer_file : str The name of the file to load the answers from, in hdf5 format. func_name : str The name of the function (possibly augmented by pytest with test parameters) that called the test functions (e.g, test_toro1d). """ with h5py.File(answer_file, "r") as f: for test_name, new_answer in arrays.items(): np.testing.assert_array_equal(f[func_name][test_name][:], new_answer) def can_run_ds(ds_fn, file_check=False): r""" Validates whether or not a given input can be loaded and used as a Dataset object. """ if isinstance(ds_fn, Dataset): return True path = ytcfg.get("yt", "test_data_dir") if not os.path.isdir(path): return False if file_check: return os.path.isfile(os.path.join(path, ds_fn)) try: load(ds_fn) return True except FileNotFoundError: return False def can_run_sim(sim_fn, sim_type, file_check=False): r""" Validates whether or not a given input can be used as a simulation time-series object. """ path = ytcfg.get("yt", "test_data_dir") if not os.path.isdir(path): return False if file_check: return os.path.isfile(os.path.join(path, sim_fn)) try: load_simulation(sim_fn, sim_type) except FileNotFoundError: return False return True def data_dir_load(ds_fn, cls=None, args=None, kwargs=None): r""" Loads a sample dataset from the designated test_data_dir for use in testing. """ args = args or () kwargs = kwargs or {} path = ytcfg.get("yt", "test_data_dir") # Some frontends require their field_lists during test parameterization. # If the data isn't found, the parameterizing functions return None, since # pytest.skip cannot be called outside of a test or fixture. if ds_fn is None: raise FileNotFoundError if not os.path.isdir(path): raise FileNotFoundError if isinstance(ds_fn, Dataset): return ds_fn if cls is None: ds = load(ds_fn, *args, **kwargs) else: ds = cls(os.path.join(path, ds_fn), *args, **kwargs) ds.index return ds def requires_ds(ds_fn, file_check=False): r""" Meta-wrapper for specifying required data for a test and checking if said data exists. """ def ffalse(func): @functools.wraps(func) def skip(*args, **kwargs): msg = f"{ds_fn} not found, skipping {func.__name__}." pytest.skip(msg) return skip def ftrue(func): @functools.wraps(func) def true_wrapper(*args, **kwargs): return func(*args, **kwargs) return true_wrapper if not can_run_ds(ds_fn, file_check): return ffalse else: return ftrue def requires_sim(sim_fn, sim_type, file_check=False): r""" Meta-wrapper for specifying a required simulation for a test and checking if said simulation exists. """ def ffalse(func): @functools.wraps(func) def skip(*args, **kwargs): msg = f"{sim_fn} not found, skipping {func.__name__}." pytest.skip(msg) return skip def ftrue(func): @functools.wraps(func) def true_wrapper(*args, **kwargs): return func return true_wrapper if not can_run_sim(sim_fn, sim_type, file_check): return ffalse else: return ftrue def _create_plot_window_attribute_plot(ds, plot_type, field, axis, pkwargs=None): r""" Convenience function used in plot_window_attribute_test. Parameters ---------- ds : Dataset The Dataset object from which the plotting data is taken. plot_type : string Type of plot to make (e.g., SlicePlot). field : yt field The field (e.g, density) to plot. axis : int The plot axis to plot or project along. pkwargs : dict Any keywords to be passed when creating the plot. """ if plot_type is None: raise RuntimeError("Must explicitly request a plot type") cls = getattr(pw, plot_type, None) if cls is None: cls = getattr(particle_plots, plot_type) plot = cls(ds, axis, field, **pkwargs) return plot def _create_phase_plot_attribute_plot( data_source, x_field, y_field, z_field, plot_type, plot_kwargs=None ): r""" Convenience function used in phase_plot_attribute_test. Parameters ---------- data_source : Dataset object The Dataset object from which the plotting data is taken. x_field : yt field Field to plot on x-axis. y_field : yt field Field to plot on y-axis. z_field : yt field Field to plot on z-axis. plot_type : string Type of plot to make (e.g., SlicePlot). plot_kwargs : dict Any keywords to be passed when creating the plot. """ if plot_type is None: raise RuntimeError("Must explicitly request a plot type") cls = getattr(profile_plotter, plot_type, None) if cls is None: cls = getattr(particle_plots, plot_type) plot = cls(*(data_source, x_field, y_field, z_field), **plot_kwargs) return plot def get_parameterization(fname): """ Returns a dataset's field list to make test parameterizationn easier. Some tests (such as those that use the toro1d dataset in enzo) check every field in a dataset. In order to parametrize the tests without having to hardcode a list of every field, this function is used. Additionally, if the dataset cannot be found, this function enables pytest to mark the test as failed without the whole test run crashing, since the parameterization happens at import time. """ try: ds = data_dir_load(fname) return ds.field_list except FileNotFoundError: return [ None, ] yt-project-yt-f043ac8/yt/utilities/api.py000066400000000000000000000000401510711153200204360ustar00rootroot00000000000000""" API for yt.utilities """ yt-project-yt-f043ac8/yt/utilities/chemical_formulas.py000066400000000000000000000033321510711153200233510ustar00rootroot00000000000000import re from .periodic_table import periodic_table from .physical_ratios import _primordial_mass_fraction class ChemicalFormula: def __init__(self, formula_string): # See YTEP-0003 for information on the format. self.formula_string = formula_string self.elements = [] if "_" in self.formula_string: molecule, ionization = self.formula_string.split("_") if ionization[0] == "p": charge = int(ionization[1:]) elif ionization[0] == "m": charge = -int(ionization[1:]) else: raise NotImplementedError elif self.formula_string.startswith("El"): molecule = self.formula_string charge = -1 else: molecule = self.formula_string charge = 0 self.charge = charge for element, count in re.findall(r"([A-Z][a-z]*)(\d*)", molecule): if count == "": count = 1 self.elements.append((periodic_table[element], int(count))) self.weight = sum(n * e.weight for e, n in self.elements) def __repr__(self): return self.formula_string def compute_mu(ion_state): if ion_state == "ionized" or ion_state is None: # full ionization n_H = 2.0 # fully ionized hydrogen gives two particles n_He = 3.0 # fully ionized helium gives three particles elif ion_state == "neutral": n_H = 1.0 # neutral hydrogen gives one particle n_He = 1.0 # neutral helium gives one particle muinv = n_H * _primordial_mass_fraction["H"] / ChemicalFormula("H").weight muinv += n_He * _primordial_mass_fraction["He"] / ChemicalFormula("He").weight return 1.0 / muinv yt-project-yt-f043ac8/yt/utilities/command_line.py000066400000000000000000001304121510711153200223210ustar00rootroot00000000000000import argparse import base64 import json import os import pprint import sys import textwrap from typing import Any import numpy as np from more_itertools import always_iterable from yt.config import ytcfg from yt.funcs import ( download_file, enable_plugins, ensure_dir, ensure_dir_exists, get_git_version, mylog, update_git, ) from yt.loaders import load from yt.utilities.exceptions import YTFieldNotParseable, YTUnidentifiedDataType from yt.utilities.metadata import get_metadata from yt.visualization.plot_window import ProjectionPlot, SlicePlot # isort: off # This needs to be set before importing startup_tasks ytcfg["yt", "internals", "command_line"] = True # isort: skip from yt.startup_tasks import parser, subparsers # isort: skip # noqa: E402 # isort: on # loading field plugins for backward compatibility, since this module # used to do "from yt.mods import *" try: enable_plugins() except FileNotFoundError: pass _default_colormap = ytcfg.get("yt", "default_colormap") def _fix_ds(arg, *args, **kwargs): if os.path.isdir(f"{arg}") and os.path.exists(f"{arg}/{arg}"): ds = load(f"{arg}/{arg}", *args, **kwargs) elif os.path.isdir(f"{arg}.dir") and os.path.exists(f"{arg}.dir/{arg}"): ds = load(f"{arg}.dir/{arg}", *args, **kwargs) elif arg.endswith(".index"): ds = load(arg[:-10], *args, **kwargs) else: ds = load(arg, *args, **kwargs) return ds def _add_arg(sc, arg): if isinstance(arg, str): arg = _common_options[arg].copy() elif isinstance(arg, tuple): exclusive, *args = arg if exclusive: grp = sc.add_mutually_exclusive_group() else: grp = sc.add_argument_group() for arg in args: _add_arg(grp, arg) return argc = dict(arg.items()) argnames = [] if "short" in argc: argnames.append(argc.pop("short")) if "longname" in argc: argnames.append(argc.pop("longname")) sc.add_argument(*argnames, **argc) def _print_failed_source_update(reinstall=False): print() print("The yt package is not installed from a git repository,") print("so you must update this installation manually.") if "Continuum Analytics" in sys.version or "Anaconda" in sys.version: # see http://stackoverflow.com/a/21318941/1382869 for why we need # to check both Continuum *and* Anaconda print() print("Since it looks like you are using a python installation") print("that is managed by conda, you may want to do:") print() print(" $ conda update yt") print() print("to update your yt installation.") if reinstall: print() print("To update all of your packages, you can do:") print() print(" $ conda update --all") else: print("If you manage your python dependencies with pip, you may") print("want to do:") print() print(" $ python -m pip install -U yt") print() print("to update your yt installation.") def _print_installation_information(path): import yt print() print("yt module located at:") print(f" {path}") if "YT_DEST" in os.environ: spath = os.path.join(os.environ["YT_DEST"], "src", "yt-supplemental") if os.path.isdir(spath): print("The supplemental repositories are located at:") print(f" {spath}") print() print("The current version of yt is:") print() print("---") print(f"Version = {yt.__version__}") vstring = get_git_version(path) if vstring is not None: print(f"Changeset = {vstring.strip()}") print("---") return vstring class FileStreamer: final_size = None next_sent = 0 chunksize = 100 * 1024 def __init__(self, f, final_size=None): location = f.tell() f.seek(0, os.SEEK_END) self.final_size = f.tell() - location f.seek(location) self.f = f def __iter__(self): from tqdm import tqdm with tqdm( total=self.final_size, desc="Uploading file", unit="B", unit_scale=True ) as pbar: while self.f.tell() < self.final_size: yield self.f.read(self.chunksize) pbar.update(self.chunksize) _subparsers = {None: subparsers} _subparsers_description = { "config": "Get and set configuration values for yt", } class YTCommandSubtype(type): def __init__(cls, name, b, d): type.__init__(cls, name, b, d) if cls.name is None: return if cls.subparser not in _subparsers: try: description = _subparsers_description[cls.subparser] except KeyError: description = cls.subparser parent_parser = argparse.ArgumentParser(add_help=False) p = subparsers.add_parser( cls.subparser, help=description, description=description, parents=[parent_parser], ) _subparsers[cls.subparser] = p.add_subparsers( title=cls.subparser, dest=cls.subparser ) sp = _subparsers[cls.subparser] for name in always_iterable(cls.name): sc = sp.add_parser(name, description=cls.description, help=cls.description) sc.set_defaults(func=cls.run) for arg in cls.args: _add_arg(sc, arg) class YTCommand(metaclass=YTCommandSubtype): args: tuple[str | dict[str, Any], ...] = () name: str | list[str] | None = None description: str = "" aliases = () ndatasets: int = 1 subparser: str | None = None @classmethod def run(cls, args): self = cls() # Check for some things we know; for instance, comma separated # field names should be parsed as tuples. if getattr(args, "field", None) is not None and "," in args.field: if args.field.count(",") > 1: raise YTFieldNotParseable(args.field) args.field = tuple(_.strip() for _ in args.field.split(",")) if getattr(args, "weight", None) is not None and "," in args.weight: if args.weight.count(",") > 1: raise YTFieldNotParseable(args.weight) args.weight = tuple(_.strip() for _ in args.weight.split(",")) # Some commands need to be run repeatedly on datasets # In fact, this is the rule and the opposite is the exception # BUT, we only want to parse the arguments once. if cls.ndatasets > 1: self(args) else: ds_args = getattr(args, "ds", []) if len(ds_args) > 1: datasets = args.ds for ds in datasets: args.ds = ds self(args) elif len(ds_args) == 0: datasets = [] self(args) else: args.ds = getattr(args, "ds", [None])[0] self(args) class GetParameterFiles(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): if len(values) == 1: datasets = values elif len(values) == 2 and namespace.basename is not None: datasets = [ "%s%04i" % (namespace.basename, r) for r in range(int(values[0]), int(values[1]), namespace.skip) ] else: datasets = values namespace.ds = [_fix_ds(ds) for ds in datasets] _common_options = { "all": { "longname": "--all", "dest": "reinstall", "default": False, "action": "store_true", "help": ( "Reinstall the full yt stack in the current location. " "This option has been deprecated and will not have any " "effect." ), }, "ds": { "short": "ds", "action": GetParameterFiles, "nargs": "+", "help": "datasets to run on", }, "ods": { "action": GetParameterFiles, "dest": "ds", "nargs": "*", "help": "(Optional) datasets to run on", }, "axis": { "short": "-a", "longname": "--axis", "action": "store", "type": int, "dest": "axis", "default": 4, "help": "Axis (4 for all three)", }, "log": { "short": "-l", "longname": "--log", "action": "store_true", "dest": "takelog", "default": True, "help": "Use logarithmic scale for image", }, "linear": { "longname": "--linear", "action": "store_false", "dest": "takelog", "help": "Use linear scale for image", }, "text": { "short": "-t", "longname": "--text", "action": "store", "type": str, "dest": "text", "default": None, "help": "Textual annotation", }, "field": { "short": "-f", "longname": "--field", "action": "store", "type": str, "dest": "field", "default": "density", "help": ("Field to color by, use a comma to separate field tuple values"), }, "weight": { "short": "-g", "longname": "--weight", "action": "store", "type": str, "dest": "weight", "default": None, "help": ( "Field to weight projections with, " "use a comma to separate field tuple values" ), }, "cmap": { "longname": "--colormap", "action": "store", "type": str, "dest": "cmap", "default": _default_colormap, "help": "Colormap name", }, "zlim": { "short": "-z", "longname": "--zlim", "action": "store", "type": float, "dest": "zlim", "default": None, "nargs": 2, "help": "Color limits (min, max)", }, "dex": { "longname": "--dex", "action": "store", "type": float, "dest": "dex", "default": None, "nargs": 1, "help": "Number of dex above min to display", }, "width": { "short": "-w", "longname": "--width", "action": "store", "type": float, "dest": "width", "default": None, "help": "Width in specified units", }, "unit": { "short": "-u", "longname": "--unit", "action": "store", "type": str, "dest": "unit", "default": "1", "help": "Desired axes units", }, "center": { "short": "-c", "longname": "--center", "action": "store", "type": float, "dest": "center", "default": None, "nargs": 3, "help": "Center, space separated (-1 -1 -1 for max)", }, "max": { "short": "-m", "longname": "--max", "action": "store_true", "dest": "max", "default": False, "help": "Center the plot on the density maximum", }, "bn": { "short": "-b", "longname": "--basename", "action": "store", "type": str, "dest": "basename", "default": None, "help": "Basename of datasets", }, "output": { "short": "-o", "longname": "--output", "action": "store", "type": str, "dest": "output", "default": "frames/", "help": "Folder in which to place output images", }, "outputfn": { "short": "-o", "longname": "--output", "action": "store", "type": str, "dest": "output", "default": None, "help": "File in which to place output", }, "skip": { "short": "-s", "longname": "--skip", "action": "store", "type": int, "dest": "skip", "default": 1, "help": "Skip factor for outputs", }, "proj": { "short": "-p", "longname": "--projection", "action": "store_true", "dest": "projection", "default": False, "help": "Use a projection rather than a slice", }, "maxw": { "longname": "--max-width", "action": "store", "type": float, "dest": "max_width", "default": 1.0, "help": "Maximum width in code units", }, "minw": { "longname": "--min-width", "action": "store", "type": float, "dest": "min_width", "default": 50, "help": "Minimum width in units of smallest dx (default: 50)", }, "nframes": { "short": "-n", "longname": "--nframes", "action": "store", "type": int, "dest": "nframes", "default": 100, "help": "Number of frames to generate", }, "slabw": { "longname": "--slab-width", "action": "store", "type": float, "dest": "slab_width", "default": 1.0, "help": "Slab width in specified units", }, "slabu": { "short": "-g", "longname": "--slab-unit", "action": "store", "type": str, "dest": "slab_unit", "default": "1", "help": "Desired units for the slab", }, "ptype": { "longname": "--particle-type", "action": "store", "type": int, "dest": "ptype", "default": 2, "help": "Particle type to select", }, "agecut": { "longname": "--age-cut", "action": "store", "type": float, "dest": "age_filter", "default": None, "nargs": 2, "help": "Bounds for the field to select", }, "uboxes": { "longname": "--unit-boxes", "action": "store_true", "dest": "unit_boxes", "help": "Display heldsul unit boxes", }, "thresh": { "longname": "--threshold", "action": "store", "type": float, "dest": "threshold", "default": None, "help": "Density threshold", }, "dm_only": { "longname": "--all-particles", "action": "store_false", "dest": "dm_only", "default": True, "help": "Use all particles", }, "grids": { "longname": "--show-grids", "action": "store_true", "dest": "grids", "default": False, "help": "Show the grid boundaries", }, "time": { "longname": "--time", "action": "store_true", "dest": "time", "default": False, "help": "Print time in years on image", }, "contours": { "longname": "--contours", "action": "store", "type": int, "dest": "contours", "default": None, "help": "Number of Contours for Rendering", }, "contour_width": { "longname": "--contour_width", "action": "store", "type": float, "dest": "contour_width", "default": None, "help": "Width of gaussians used for rendering.", }, "enhance": { "longname": "--enhance", "action": "store_true", "dest": "enhance", "default": False, "help": "Enhance!", }, "valrange": { "short": "-r", "longname": "--range", "action": "store", "type": float, "dest": "valrange", "default": None, "nargs": 2, "help": "Range, space separated", }, "up": { "longname": "--up", "action": "store", "type": float, "dest": "up", "default": None, "nargs": 3, "help": "Up, space separated", }, "viewpoint": { "longname": "--viewpoint", "action": "store", "type": float, "dest": "viewpoint", "default": [1.0, 1.0, 1.0], "nargs": 3, "help": "Viewpoint, space separated", }, "pixels": { "longname": "--pixels", "action": "store", "type": int, "dest": "pixels", "default": None, "help": "Number of Pixels for Rendering", }, "halos": { "longname": "--halos", "action": "store", "type": str, "dest": "halos", "default": "multiple", "help": "Run halo profiler on a 'single' halo or 'multiple' halos.", }, "halo_radius": { "longname": "--halo_radius", "action": "store", "type": float, "dest": "halo_radius", "default": 0.1, "help": "Constant radius for profiling halos if using hop output files with no " + "radius entry. Default: 0.1.", }, "halo_radius_units": { "longname": "--halo_radius_units", "action": "store", "type": str, "dest": "halo_radius_units", "default": "1", "help": "Units for radius used with --halo_radius flag. " + "Default: '1' (code units).", }, "halo_hop_style": { "longname": "--halo_hop_style", "action": "store", "type": str, "dest": "halo_hop_style", "default": "new", "help": "Style of hop output file. " + "'new' for yt_hop files and 'old' for enzo_hop files.", }, "halo_dataset": { "longname": "--halo_dataset", "action": "store", "type": str, "dest": "halo_dataset", "default": None, "help": "HaloProfiler dataset.", }, "make_profiles": { "longname": "--make_profiles", "action": "store_true", "default": False, "help": "Make profiles with halo profiler.", }, "make_projections": { "longname": "--make_projections", "action": "store_true", "default": False, "help": "Make projections with halo profiler.", }, } class YTInstInfoCmd(YTCommand): name = ["instinfo", "version"] args = ( { "short": "-u", "longname": "--update-source", "action": "store_true", "default": False, "help": "Update the yt installation, if able", }, { "short": "-o", "longname": "--output-version", "action": "store", "default": None, "dest": "outputfile", "help": "File into which the current revision number will be stored", }, ) description = """ Get some information about the yt installation """ def __call__(self, opts): import importlib.resources as importlib_resources path = os.path.dirname(importlib_resources.files("yt")) vstring = _print_installation_information(path) if vstring is not None: print("This installation CAN be automatically updated.") if opts.update_source: update_git(path) elif opts.update_source: _print_failed_source_update() if vstring is not None and opts.outputfile is not None: open(opts.outputfile, "w").write(vstring) class YTLoadCmd(YTCommand): name = "load" description = """ Load a single dataset into an IPython instance """ args = ("ds",) def __call__(self, args): if args.ds is None: print("Could not load file.") sys.exit() import IPython import yt local_ns = {} local_ns["ds"] = args.ds local_ns["pf"] = args.ds local_ns["yt"] = yt try: from traitlets.config.loader import Config except ImportError: from IPython.config.loader import Config cfg = Config() # prepend sys.path with current working directory sys.path.insert(0, "") IPython.embed(config=cfg, user_ns=local_ns) class YTMapserverCmd(YTCommand): args = ( "proj", "field", "weight", "linear", "center", "width", "cmap", { "short": "-a", "longname": "--axis", "action": "store", "type": int, "dest": "axis", "default": 0, "help": "Axis", }, { "short": "-o", "longname": "--host", "action": "store", "type": str, "dest": "host", "default": None, "help": "IP Address to bind on", }, {"short": "ds", "nargs": 1, "type": str, "help": "The dataset to load."}, ) name = "mapserver" description = """ Serve a plot in a GMaps-style interface """ def __call__(self, args): from yt.frontends.ramses.data_structures import RAMSESDataset from yt.visualization.mapserver.pannable_map import PannableMapServer # For RAMSES datasets, use the bbox feature to make the dataset load faster if RAMSESDataset._is_valid(args.ds) and args.center and args.width: kwa = { "bbox": [ [c - args.width / 2 for c in args.center], [c + args.width / 2 for c in args.center], ] } else: kwa = {} ds = _fix_ds(args.ds, **kwa) if args.center and args.width: center = args.center width = args.width ad = ds.box( left_edge=[c - args.width / 2 for c in args.center], right_edge=[c + args.width / 2 for c in args.center], ) else: center = [0.5] * 3 width = 1.0 ad = ds.all_data() if args.axis >= 4: print("Doesn't work with multiple axes!") return if args.projection: p = ProjectionPlot( ds, args.axis, args.field, weight_field=args.weight, data_source=ad, center=center, width=width, ) else: p = SlicePlot( ds, args.axis, args.field, data_source=ad, center=center, width=width ) p.set_log("all", args.takelog) p.set_cmap("all", args.cmap) PannableMapServer(p.data_source, args.field, args.takelog, args.cmap) try: import bottle except ImportError as e: raise ImportError( "The mapserver functionality requires the bottle " "package to be installed. Please install using `pip " "install bottle`." ) from e bottle.debug(True) if args.host is not None: colonpl = args.host.find(":") if colonpl >= 0: port = int(args.host.split(":")[-1]) args.host = args.host[:colonpl] else: port = 8080 bottle.run(server="auto", host=args.host, port=port) else: bottle.run(server="auto") class YTPastebinCmd(YTCommand): name = "pastebin" args = ( { "short": "-l", "longname": "--language", "action": "store", "default": None, "dest": "language", "help": "Use syntax highlighter for the file in language", }, { "short": "-L", "longname": "--languages", "action": "store_true", "default": False, "dest": "languages", "help": "Retrieve a list of supported languages", }, { "short": "-e", "longname": "--encoding", "action": "store", "default": "utf-8", "dest": "encoding", "help": "Specify the encoding of a file (default is " "utf-8 or guessing if available)", }, { "short": "-b", "longname": "--open-browser", "action": "store_true", "default": False, "dest": "open_browser", "help": "Open the paste in a web browser", }, { "short": "-p", "longname": "--private", "action": "store_true", "default": False, "dest": "private", "help": "Paste as private", }, { "short": "-c", "longname": "--clipboard", "action": "store_true", "default": False, "dest": "clipboard", "help": "File to output to; else, print.", }, {"short": "file", "type": str}, ) description = """ Post a script to an anonymous pastebin """ def __call__(self, args): from yt.utilities import lodgeit as lo lo.main( args.file, languages=args.languages, language=args.language, encoding=args.encoding, open_browser=args.open_browser, private=args.private, clipboard=args.clipboard, ) class YTPastebinGrabCmd(YTCommand): args = ({"short": "number", "type": str},) name = "pastebin_grab" description = """ Print an online pastebin to STDOUT for local use. """ def __call__(self, args): from yt.utilities import lodgeit as lo lo.main(None, download=args.number) class YTPlotCmd(YTCommand): args = ( "width", "unit", "bn", "proj", "center", "zlim", "axis", "field", "weight", "skip", "cmap", "output", "grids", "time", "ds", "max", "log", "linear", { "short": "-fu", "longname": "--field-unit", "action": "store", "type": str, "dest": "field_unit", "default": None, "help": "Desired field units", }, { "longname": "--show-scale-bar", "action": "store_true", "help": "Annotate the plot with the scale", }, ) name = "plot" description = """ Create a set of images """ def __call__(self, args): ds = args.ds center = args.center if args.center == (-1, -1, -1): mylog.info("No center fed in; seeking.") v, center = ds.find_max("density") if args.max: v, center = ds.find_max("density") elif args.center is None: center = 0.5 * (ds.domain_left_edge + ds.domain_right_edge) center = np.array(center) if ds.dimensionality < 3: dummy_dimensions = np.nonzero(ds.index.grids[0].ActiveDimensions <= 1) axes = dummy_dimensions[0][0] elif args.axis == 4: axes = range(3) else: axes = args.axis unit = args.unit if unit is None: unit = "unitary" if args.width is None: width = None else: width = (args.width, args.unit) for ax in always_iterable(axes): mylog.info("Adding plot for axis %i", ax) if args.projection: plt = ProjectionPlot( ds, ax, args.field, center=center, width=width, weight_field=args.weight, ) else: plt = SlicePlot(ds, ax, args.field, center=center, width=width) if args.grids: plt.annotate_grids() if args.time: plt.annotate_timestamp() if args.show_scale_bar: plt.annotate_scale() if args.field_unit: plt.set_unit(args.field, args.field_unit) plt.set_cmap(args.field, args.cmap) plt.set_log(args.field, args.takelog) if args.zlim: plt.set_zlim(args.field, *args.zlim) ensure_dir_exists(args.output) plt.save(os.path.join(args.output, f"{ds}")) class YTRPDBCmd(YTCommand): name = "rpdb" description = """ Connect to a currently running (on localhost) rpd session. Commands run with --rpdb will trigger an rpdb session with any uncaught exceptions. """ args = ( { "short": "-t", "longname": "--task", "action": "store", "default": 0, "dest": "task", "help": "Open a web browser.", }, ) def __call__(self, args): from . import rpdb rpdb.run_rpdb(int(args.task)) class YTStatsCmd(YTCommand): args = ( "outputfn", "bn", "skip", "ds", "field", { "longname": "--max", "action": "store_true", "default": False, "dest": "max", "help": "Display maximum of field requested through -f option.", }, { "longname": "--min", "action": "store_true", "default": False, "dest": "min", "help": "Display minimum of field requested through -f option.", }, ) name = "stats" description = """ Print stats and max/min value of a given field (if requested), for one or more datasets (default field is density) """ def __call__(self, args): ds = args.ds ds.print_stats() vals = {} field = ds._get_field_info(args.field) if args.max: vals["max"] = ds.find_max(field) print(f"Maximum {field.name}: {vals['max'][0]:0.5e} at {vals['max'][1]}") if args.min: vals["min"] = ds.find_min(field) print(f"Minimum {field.name}: {vals['min'][0]:0.5e} at {vals['min'][1]}") if args.output is not None: t = ds.current_time.to("yr") with open(args.output, "a") as f: f.write(f"{ds} ({t:0.5e})\n") if "min" in vals: f.write( f"Minimum {field.name} is {vals['min'][0]:0.5e} at {vals['min'][1]}\n" ) if "max" in vals: f.write( f"Maximum {field.name} is {vals['max'][0]:0.5e} at {vals['max'][1]}\n" ) class YTUpdateCmd(YTCommand): args = ("all",) name = "update" description = """ Update the yt installation to the most recent version """ def __call__(self, opts): import importlib.resources as importlib_resources path = os.path.dirname(importlib_resources.files("yt")) vstring = _print_installation_information(path) if vstring is not None: print() print("This installation CAN be automatically updated.") update_git(path) else: _print_failed_source_update(opts.reinstall) class YTDeleteImageCmd(YTCommand): args = ({"short": "delete_hash", "type": str},) description = """ Delete image from imgur.com. """ name = "delete_image" def __call__(self, args): import urllib.error import urllib.request headers = {"Authorization": f"Client-ID {ytcfg.get('yt', 'imagebin_api_key')}"} delete_url = ytcfg.get("yt", "imagebin_delete_url") req = urllib.request.Request( delete_url.format(delete_hash=args.delete_hash), headers=headers, method="DELETE", ) try: response = urllib.request.urlopen(req).read().decode() except urllib.error.HTTPError as e: print("ERROR", e) return {"deleted": False} rv = json.loads(response) if "success" in rv and rv["success"]: print("\nImage successfully deleted!\n") else: print() print("Something has gone wrong! Here is the server response:") print() pprint.pprint(rv) class YTUploadImageCmd(YTCommand): args = ({"short": "file", "type": str},) description = """ Upload an image to imgur.com. Must be PNG. """ name = "upload_image" def __call__(self, args): import urllib.error import urllib.parse import urllib.request filename = args.file if not filename.endswith(".png"): print("File must be a PNG file!") return 1 headers = {"Authorization": f"Client-ID {ytcfg.get('yt', 'imagebin_api_key')}"} image_data = base64.b64encode(open(filename, "rb").read()) parameters = { "image": image_data, type: "base64", "name": filename, "title": f"{filename} uploaded by yt", } data = urllib.parse.urlencode(parameters).encode("utf-8") req = urllib.request.Request( ytcfg.get("yt", "imagebin_upload_url"), data=data, headers=headers ) try: response = urllib.request.urlopen(req).read().decode() except urllib.error.HTTPError as e: print("ERROR", e) return {"uploaded": False} rv = json.loads(response) if "data" in rv and "link" in rv["data"]: print() print("Image successfully uploaded! You can find it at:") print(f" {rv['data']['link']}") print() print("If you'd like to delete it, use the following") print(f" yt delete_image {rv['data']['deletehash']}") print() else: print() print("Something has gone wrong! Here is the server response:") print() pprint.pprint(rv) class YTUploadFileCmd(YTCommand): args = ({"short": "file", "type": str},) description = """ Upload a file to yt's curldrop. """ name = "upload" def __call__(self, args): from yt.utilities.on_demand_imports import _requests as requests fs = iter(FileStreamer(open(args.file, "rb"))) upload_url = ytcfg.get("yt", "curldrop_upload_url") r = requests.put(upload_url + "/" + os.path.basename(args.file), data=fs) print() print(r.text) class YTConfigLocalConfigHandler: def load_config(self, args) -> None: import os from yt.config import YTConfig from yt.utilities.configure import CONFIG local_config_file = YTConfig.get_local_config_file() global_config_file = YTConfig.get_global_config_file() local_exists = os.path.exists(local_config_file) global_exists = os.path.exists(global_config_file) local_arg_exists = hasattr(args, "local") global_arg_exists = hasattr(args, "global") config_file: str | None = None if getattr(args, "local", False): config_file = local_config_file elif getattr(args, "global", False): config_file = global_config_file else: if local_exists and global_exists: s = ( "Yt detected a local and a global configuration file, refusing " "to proceed.\n" f"Local config file: {local_config_file}\n" f"Global config file: {global_config_file}" ) # Only print the info about "--global" and "--local" if they exist if local_arg_exists and global_arg_exists: s += ( "\n" # missing eol from previous string "Specify which one you want to use using the `--local` or the " "`--global` flags." ) sys.exit(s) elif local_exists: config_file = local_config_file elif global_exists: config_file = global_config_file if config_file is None: print("WARNING: no configuration file installed.", file=sys.stderr) else: print( f"INFO: reading configuration file: {config_file}", file=sys.stderr ) CONFIG.read(config_file) self.config_file = config_file _global_local_args = ( { "short": "--local", "action": "store_true", "help": "Store the configuration in the local configuration file.", }, { "short": "--global", "action": "store_true", "help": "Store the configuration in the global configuration file.", }, ) class YTConfigGetCmd(YTCommand, YTConfigLocalConfigHandler): subparser = "config" name = "get" description = "get a config value" args = ( {"short": "section", "help": "The section containing the option."}, {"short": "option", "help": "The option to retrieve."}, *_global_local_args, ) def __call__(self, args): from yt.utilities.configure import get_config self.load_config(args) print(get_config(args.section, args.option)) class YTConfigSetCmd(YTCommand, YTConfigLocalConfigHandler): subparser = "config" name = "set" description = "set a config value" args = ( {"short": "section", "help": "The section containing the option."}, {"short": "option", "help": "The option to set."}, {"short": "value", "help": "The value to set the option to."}, *_global_local_args, ) def __call__(self, args): from yt.utilities.configure import set_config self.load_config(args) if self.config_file is None: self.config_file = os.path.join(os.getcwd(), "yt.toml") print( f"INFO: configuration will be written to {self.config_file}", file=sys.stderr, ) set_config(args.section, args.option, args.value, self.config_file) class YTConfigRemoveCmd(YTCommand, YTConfigLocalConfigHandler): subparser = "config" name = "rm" description = "remove a config option" args = ( {"short": "section", "help": "The section containing the option."}, {"short": "option", "help": "The option to remove."}, *_global_local_args, ) def __call__(self, args): from yt.utilities.configure import rm_config self.load_config(args) rm_config(args.section, args.option, self.config_file) class YTConfigListCmd(YTCommand, YTConfigLocalConfigHandler): subparser = "config" name = "list" description = "show the config content" args = _global_local_args def __call__(self, args): from yt.utilities.configure import write_config self.load_config(args) write_config(sys.stdout) class YTConfigPrintPath(YTCommand, YTConfigLocalConfigHandler): subparser = "config" name = "print-path" description = "show path to the config file" args = _global_local_args def __call__(self, args): self.load_config(args) print(self.config_file) class YTSearchCmd(YTCommand): args = ( { "short": "-o", "longname": "--output", "action": "store", "type": str, "dest": "output", "default": "yt_index.json", "help": "File in which to place output", }, { "longname": "--check-all", "short": "-a", "help": "Attempt to load every file", "action": "store_true", "default": False, "dest": "check_all", }, { "longname": "--full", "short": "-f", "help": "Output full contents of parameter file", "action": "store_true", "default": False, "dest": "full_output", }, ) description = """ Attempt to find outputs that yt can recognize in directories. """ name = "search" def __call__(self, args): from yt.utilities.object_registries import output_type_registry candidates = [] for base, dirs, files in os.walk(".", followlinks=True): print("(% 10i candidates) Examining %s" % (len(candidates), base)) recurse = [] if args.check_all: candidates.extend([os.path.join(base, _) for _ in files]) for _, otr in sorted(output_type_registry.items()): c, r = otr._guess_candidates(base, dirs, files) candidates.extend([os.path.join(base, _) for _ in c]) recurse.append(r) if len(recurse) > 0 and not all(recurse): del dirs[:] # Now we have a ton of candidates. We're going to do something crazy # and try to load each one. records = [] for i, c in enumerate(sorted(candidates)): print("(% 10i/% 10i) Evaluating %s" % (i, len(candidates), c)) try: record = get_metadata(c, args.full_output) except YTUnidentifiedDataType: continue records.append(record) with open(args.output, "w") as f: json.dump(records, f, indent=4) print(f"Identified {len(records)} records output to {args.output}") class YTDownloadData(YTCommand): args = ( { "short": "filename", "action": "store", "type": str, "help": "The name of the file to download", "nargs": "?", "default": "", }, { "short": "location", "action": "store", "type": str, "nargs": "?", "help": "The location in which to place the file, can be " '"supp_data_dir", "test_data_dir", or any valid ' "path on disk. ", "default": "", }, { "longname": "--overwrite", "short": "-c", "help": "Overwrite existing file.", "action": "store_true", "default": False, }, { "longname": "--list", "short": "-l", "help": "Display all available files.", "action": "store_true", "default": False, }, ) description = """ Download a file from http://yt-project.org/data and save it to a particular location. Files can be saved to the locations provided by the "test_data_dir" or "supp_data_dir" configuration entries, or any valid path to a location on disk. """ name = "download" def __call__(self, args): if args.list: self.get_list() return if not args.filename: raise RuntimeError( "You need to provide a filename. See --help " "for details or use --list to get available " "datasets." ) elif not args.location: raise RuntimeError( "You need to specify download location. See --help for details." ) data_url = f"http://yt-project.org/data/{args.filename}" if args.location in ["test_data_dir", "supp_data_dir"]: data_dir = ytcfg.get("yt", args.location) if data_dir == "/does/not/exist": raise RuntimeError(f"'{args.location}' is not configured!") else: data_dir = args.location if not os.path.exists(data_dir): print(f"The directory '{data_dir}' does not exist. Creating...") ensure_dir(data_dir) data_file = os.path.join(data_dir, args.filename) if os.path.exists(data_file) and not args.overwrite: raise OSError(f"File '{data_file}' exists and overwrite=False!") print(f"Attempting to download file: {args.filename}") fn = download_file(data_url, data_file) if not os.path.exists(fn): raise OSError(f"The file '{args.filename}' did not download!!") print(f"File: {args.filename} downloaded successfully to {data_file}") def get_list(self): import urllib.request data = ( urllib.request.urlopen("http://yt-project.org/data/datafiles.json") .read() .decode("utf8") ) data = json.loads(data) for key in data: for ds in data[key]: ds["fullname"] = ds["url"].replace("http://yt-project.org/data/", "") print("{fullname} ({size}) type: {code}".format(**ds)) for line in textwrap.wrap(ds["description"]): print("\t", line) def run_main(): args = parser.parse_args() # The following is a workaround for a nasty Python 3 bug: # http://bugs.python.org/issue16308 # http://bugs.python.org/issue9253 try: args.func except AttributeError: parser.print_help() sys.exit(0) args.func(args) if __name__ == "__main__": run_main() yt-project-yt-f043ac8/yt/utilities/configuration_tree.py000066400000000000000000000141731510711153200235670ustar00rootroot00000000000000class ConfigNode: def __init__(self, key, parent=None): self.key = key self.children = {} self.parent = parent def add(self, key, child): self.children[key] = child child.parent = self def update(self, other, extra_data=None): def _recursive_upsert(other_dict, keys): for key, val in other_dict.items(): new_keys = keys + [key] if isinstance(val, dict): _recursive_upsert(val, new_keys) else: self.upsert_from_list(new_keys, val, extra_data) _recursive_upsert(other, keys=[]) def get_child(self, key, constructor=None): if key in self.children: child = self.children[key] elif constructor is not None: child = self.children[key] = constructor() else: raise KeyError(f"Cannot get key {key}") return child def add_child(self, key): self.get_child(key, lambda: ConfigNode(key, parent=self)) def remove_child(self, key): self.children.pop(key) def upsert_from_list(self, keys, value, extra_data=None): key, *next_keys = keys if len(next_keys) == 0: # reach the end of the upsert leaf = self.get_child( key, lambda: ConfigLeaf( key, parent=self, value=value, extra_data=extra_data ), ) leaf.value = value leaf.extra_data = extra_data if not isinstance(leaf, ConfigLeaf): raise RuntimeError(f"Expected a ConfigLeaf, got {leaf}!") else: next_node = self.get_child(key, lambda: ConfigNode(key, parent=self)) if not isinstance(next_node, ConfigNode): raise RuntimeError(f"Expected a ConfigNode, got {next_node}!") next_node.upsert_from_list(next_keys, value, extra_data) def get_from_list(self, key_list): next, *key_list_remainder = key_list child = self.get_child(next) if len(key_list_remainder) == 0: return child else: return child.get_from_list(key_list_remainder) def get(self, *keys): return self.get_from_list(keys) def get_leaf(self, *keys, callback=lambda leaf: leaf.value): leaf = self.get_from_list(keys) return callback(leaf) def pop_leaf(self, keys): *node_keys, leaf_key = keys node = self.get_from_list(node_keys) node.children.pop(leaf_key) def get_deepest_leaf(self, *keys, callback=lambda leaf: leaf.value): root_key, *keys, leaf_key = keys root_node = self.get_child(root_key) node_list = [root_node] node = root_node # Traverse the tree down following the keys for k in keys: try: node = node.get_child(k) node_list.append(node) except KeyError: break # For each node, starting from the deepest, try to find the leaf for node in reversed(node_list): try: leaf = node.get_child(leaf_key) if not isinstance(leaf, ConfigLeaf): raise RuntimeError(f"Expected a ConfigLeaf, got {leaf}!") return callback(leaf) except KeyError: continue raise KeyError(f"Cannot any node that contains the leaf {leaf_key}.") def serialize(self): retval = {} for key, child in self.children.items(): retval[key] = child.serialize() return retval @staticmethod def from_dict(other, parent=None, **kwa): me = ConfigNode(None, parent=parent) for key, val in other.items(): if isinstance(val, dict): me.add(key, ConfigNode.from_dict(val, parent=me, **kwa)) else: me.add(key, ConfigLeaf(key, parent=me, value=val, **kwa)) return me def _as_dict_with_count(self, callback): data = {} total_count = 0 for key, child in self.children.items(): if isinstance(child, ConfigLeaf): total_count += 1 data[key] = callback(child) elif isinstance(child, ConfigNode): child_data, count = child._as_dict_with_count(callback) total_count += count if count > 0: data[key] = child_data return data, total_count def as_dict(self, callback=lambda child: child.value): data, _ = self._as_dict_with_count(callback) return data def __repr__(self): return f"" def __contains__(self, item): return item in self.children # Add support for IPython rich display # see https://ipython.readthedocs.io/en/stable/config/integrating.html def _repr_json_(self): return self.as_dict() class ConfigLeaf: def __init__(self, key, parent: ConfigNode, value, extra_data=None): self.key = key # the name of the config leaf self._value = value self.parent = parent self.extra_data = extra_data def serialize(self): return self.value def get_tree(self): node = self parents = [] while node is not None: parents.append(node) node = node.parent return reversed(parents) @property def value(self): return self._value @value.setter def value(self, new_value): if type(self.value) is type(new_value): self._value = new_value else: tree = self.get_tree() tree_str = ".".join(node.key for node in tree if node.key) msg = f"Error when setting {tree_str}.\n" msg += ( "Tried to assign a value of type " f"{type(new_value)}, expected type {type(self.value)}." ) source = self.extra_data.get("source", None) if source: msg += f"\nThis entry was last modified in {source}." raise TypeError(msg) def __repr__(self): return f"" yt-project-yt-f043ac8/yt/utilities/configure.py000066400000000000000000000136451510711153200216650ustar00rootroot00000000000000import os import sys import warnings from collections.abc import Callable from pathlib import Path from more_itertools import always_iterable from yt.utilities.configuration_tree import ConfigLeaf, ConfigNode configuration_callbacks: list[Callable[["YTConfig"], None]] = [] def config_dir(): config_root = os.environ.get( "XDG_CONFIG_HOME", os.path.join(os.path.expanduser("~"), ".config") ) conf_dir = os.path.join(config_root, "yt") return conf_dir class YTConfig: def __init__(self, defaults=None): if defaults is None: defaults = {} self.config_root = ConfigNode(None) def get(self, section, *keys, callback=None): node_or_leaf = self.config_root.get(section, *keys) if isinstance(node_or_leaf, ConfigLeaf): if callback is not None: return callback(node_or_leaf) return node_or_leaf.value return node_or_leaf def get_most_specific(self, section, *keys, **kwargs): use_fallback = "fallback" in kwargs fallback = kwargs.pop("fallback", None) try: return self.config_root.get_deepest_leaf(section, *keys) except KeyError as err: if use_fallback: return fallback else: raise err def update(self, new_values, metadata=None): if metadata is None: metadata = {} self.config_root.update(new_values, metadata) def has_section(self, section): try: self.config_root.get_child(section) return True except KeyError: return False def add_section(self, section): self.config_root.add_child(section) def remove_section(self, section): if self.has_section(section): self.config_root.remove_child(section) return True else: return False def set(self, *args, metadata=None): section, *keys, value = args if metadata is None: metadata = {"source": "runtime"} self.config_root.upsert_from_list( [section] + list(keys), value, extra_data=metadata ) def remove(self, *args): self.config_root.pop_leaf(args) def read(self, file_names): file_names_read = [] for fname in always_iterable(file_names): if not os.path.exists(fname): continue metadata = {"source": f"file: {fname}"} if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib try: with open(fname, "rb") as fh: data = tomllib.load(fh) except tomllib.TOMLDecodeError as exc: warnings.warn( f"Could not load configuration file {fname} (invalid TOML: {exc})", stacklevel=2, ) else: self.update(data, metadata=metadata) file_names_read.append(fname) return file_names_read def write(self, file_handler): import tomli_w value = self.config_root.as_dict() config_as_str = tomli_w.dumps(value) try: file_path = Path(file_handler) except TypeError: if not hasattr(file_handler, "write"): raise TypeError( f"Expected a path to a file, or a writable object, got {file_handler}" ) from None file_handler.write(config_as_str) else: pdir = file_path.parent if not pdir.exists(): warnings.warn( f"{pdir!s} does not exist, creating it (recursively)", stacklevel=2 ) os.makedirs(pdir) file_path.write_text(config_as_str) @staticmethod def get_global_config_file(): return os.path.join(config_dir(), "yt.toml") @staticmethod def get_local_config_file(): path = Path.cwd() while path.parent is not path: candidate = path.joinpath("yt.toml") if candidate.is_file(): return os.path.abspath(candidate) else: path = path.parent return os.path.join(os.path.abspath(os.curdir), "yt.toml") def __setitem__(self, args, value): section, *keys = always_iterable(args) self.set(section, *keys, value, metadata=None) def __getitem__(self, key): section, *keys = always_iterable(key) return self.get(section, *keys) def __contains__(self, item): return item in self.config_root # Add support for IPython rich display # see https://ipython.readthedocs.io/en/stable/config/integrating.html def _repr_json_(self): return self.config_root._repr_json_() CONFIG = YTConfig() def _cast_bool_helper(value): if value in ("true", "True", True): return True elif value in ("false", "False", False): return False else: raise ValueError("Cannot safely cast to bool") def _expand_all(s): return os.path.expandvars(os.path.expanduser(s)) def _cast_value_helper(value, types=(_cast_bool_helper, int, float, _expand_all)): for t in types: try: retval = t(value) return retval except ValueError: pass def get_config(section, option): *option_path, option_name = option.split(".") return CONFIG.get(section, *option_path, option_name) def set_config(section, option, value, config_file): if not CONFIG.has_section(section): CONFIG.add_section(section) option_path = option.split(".") CONFIG.set(section, *option_path, _cast_value_helper(value)) write_config(config_file) def write_config(config_file): CONFIG.write(config_file) def rm_config(section, option, config_file): option_path = option.split(".") CONFIG.remove(section, *option_path) write_config(config_file) yt-project-yt-f043ac8/yt/utilities/cosmology.py000066400000000000000000000515411510711153200217140ustar00rootroot00000000000000import functools import numpy as np from yt.units import dimensions from yt.units.unit_object import Unit # type: ignore from yt.units.unit_registry import UnitRegistry # type: ignore from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.physical_constants import ( gravitational_constant_cgs as G, speed_of_light_cgs, ) class Cosmology: r""" Create a cosmology calculator to compute cosmological distances and times. For an explanation of the various cosmological measures, see, for example Hogg (1999, https://arxiv.org/abs/astro-ph/9905116). WARNING: Cosmological distance calculations return values that are either in the comoving or proper frame, depending on the specific quantity. For simplicity, the proper and comoving frames are set equal to each other within the cosmology calculator. This means that for some distance value, x, x.to("Mpc") and x.to("Mpccm") will be the same. The user should take care to understand which reference frame is correct for the given calculation. Parameters ---------- hubble_constant : float The Hubble parameter at redshift zero in units of 100 km/s/Mpc. Default: 0.71. omega_matter : the fraction of the energy density of the Universe in matter at redshift zero. Default: 0.27. omega_lambda : the fraction of the energy density of the Universe in a cosmological constant. Default: 0.73. omega_radiation : the fraction of the energy density of the Universe in relativistic matter at redshift zero. omega_curvature : the fraction of the energy density of the Universe in curvature. Default: 0.0. unit_system : :class:`yt.units.unit_systems.UnitSystem`, optional The units system to use when making calculations. If not specified, cgs units are assumed. use_dark_factor: Bool, optional The flag to either use the cosmological constant (False, default) or to use the parameterization of w(a) as given in Linder 2002. This, along with w_0 and w_a, only matters in the function expansion_factor. w_0 : float, optional The Linder 2002 parameterization of w(a) is: w(a) = w_0 + w_a(1 - a). w_0 is w(a = 1). Only matters if use_dark_factor = True. Default is None. Cosmological constant case corresponds to w_0 = -1. w_a : float, optional See w_0. w_a is the derivative of w(a) evaluated at a = 1. Cosmological constant case corresponds to w_a = 0. Default is None. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.t_from_z(0.0).in_units("Gyr")) """ def __init__( self, hubble_constant=0.71, omega_matter=0.27, omega_lambda=0.73, omega_radiation=0.0, omega_curvature=0.0, unit_registry=None, unit_system="cgs", use_dark_factor=False, w_0=-1.0, w_a=0.0, ): self.omega_matter = float(omega_matter) self.omega_radiation = float(omega_radiation) self.omega_lambda = float(omega_lambda) self.omega_curvature = float(omega_curvature) hubble_constant = float(hubble_constant) if unit_registry is None: unit_registry = UnitRegistry(unit_system=unit_system) unit_registry.add("h", hubble_constant, dimensions.dimensionless, r"h") for my_unit in ["m", "pc", "AU", "au"]: new_unit = f"{my_unit}cm" my_u = Unit(my_unit, registry=unit_registry) # technically not true, but distances here are actually comoving unit_registry.add( new_unit, my_u.base_value, dimensions.length, f"\\rm{{{my_unit}}}/(1+z)", prefixable=True, ) self.unit_registry = unit_registry self.hubble_constant = self.quan(hubble_constant, "100*km/s/Mpc") self.unit_system = unit_system # For non-standard dark energy. If false, use default cosmological constant # This only affects the expansion_factor function. self.use_dark_factor = use_dark_factor self.w_0 = w_0 self.w_a = w_a def hubble_distance(self): r""" The distance corresponding to c / h, where c is the speed of light and h is the Hubble parameter in units of 1 / time. """ return self.quan(speed_of_light_cgs / self.hubble_constant).in_base( self.unit_system ) def comoving_radial_distance(self, z_i, z_f): r""" The comoving distance along the line of sight to on object at redshift, z_f, viewed at a redshift, z_i. Parameters ---------- z_i : float The redshift of the observer. z_f : float The redshift of the observed object. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.comoving_radial_distance(0.0, 1.0).in_units("Mpccm")) """ return ( self.hubble_distance() * trapezoid_int(self.inverse_expansion_factor, z_i, z_f) ).in_base(self.unit_system) def comoving_transverse_distance(self, z_i, z_f): r""" When multiplied by some angle, the distance between two objects observed at redshift, z_f, with an angular separation given by that angle, viewed by an observer at redshift, z_i (Hogg 1999). Parameters ---------- z_i : float The redshift of the observer. z_f : float The redshift of the observed object. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.comoving_transverse_distance(0.0, 1.0).in_units("Mpccm")) """ if self.omega_curvature > 0: return ( self.hubble_distance() / np.sqrt(self.omega_curvature) * np.sinh( np.sqrt(self.omega_curvature) * self.comoving_radial_distance(z_i, z_f) / self.hubble_distance() ) ).in_base(self.unit_system) elif self.omega_curvature < 0: return ( self.hubble_distance() / np.sqrt(np.fabs(self.omega_curvature)) * np.sin( np.sqrt(np.fabs(self.omega_curvature)) * self.comoving_radial_distance(z_i, z_f) / self.hubble_distance() ) ).in_base(self.unit_system) else: return self.comoving_radial_distance(z_i, z_f) def comoving_volume(self, z_i, z_f): r""" "The comoving volume is the volume measure in which number densities of non-evolving objects locked into Hubble flow are constant with redshift." -- Hogg (1999) Parameters ---------- z_i : float The lower redshift of the interval. z_f : float The higher redshift of the interval. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.comoving_volume(0.0, 1.0).in_units("Gpccm**3")) """ if self.omega_curvature > 1e-10: return ( 2 * np.pi * np.power(self.hubble_distance(), 3) / self.omega_curvature * ( self.comoving_transverse_distance(z_i, z_f) / self.hubble_distance() * np.sqrt( 1 + self.omega_curvature * np.sqrt( self.comoving_transverse_distance(z_i, z_f) / self.hubble_distance() ) ) - np.sinh( np.fabs(self.omega_curvature) * self.comoving_transverse_distance(z_i, z_f) / self.hubble_distance() ) / np.sqrt(self.omega_curvature) ) ).in_base(self.unit_system) elif self.omega_curvature < -1e-10: return ( 2 * np.pi * np.power(self.hubble_distance(), 3) / np.fabs(self.omega_curvature) * ( self.comoving_transverse_distance(z_i, z_f) / self.hubble_distance() * np.sqrt( 1 + self.omega_curvature * np.sqrt( self.comoving_transverse_distance(z_i, z_f) / self.hubble_distance() ) ) - np.arcsin( np.fabs(self.omega_curvature) * self.comoving_transverse_distance(z_i, z_f) / self.hubble_distance() ) / np.sqrt(np.fabs(self.omega_curvature)) ) ).in_base(self.unit_system) else: return ( 4 * np.pi * np.power(self.comoving_transverse_distance(z_i, z_f), 3) / 3 ).in_base(self.unit_system) def angular_diameter_distance(self, z_i, z_f): r""" Following Hogg (1999), the angular diameter distance is 'the ratio of an object's physical transverse size to its angular size in radians.' Parameters ---------- z_i : float The redshift of the observer. z_f : float The redshift of the observed object. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.angular_diameter_distance(0.0, 1.0).in_units("Mpc")) """ return ( self.comoving_transverse_distance(0, z_f) / (1 + z_f) - self.comoving_transverse_distance(0, z_i) / (1 + z_i) ).in_base(self.unit_system) def angular_scale(self, z_i, z_f): r""" The proper transverse distance between two points at redshift z_f observed at redshift z_i per unit of angular separation. Parameters ---------- z_i : float The redshift of the observer. z_f : float The redshift of the observed object. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.angular_scale(0.0, 1.0).in_units("kpc / arcsec")) """ scale = self.angular_diameter_distance(z_i, z_f) / self.quan(1, "radian") return scale.in_base(self.unit_system) def luminosity_distance(self, z_i, z_f): r""" The distance that would be inferred from the inverse-square law of light and the measured flux and luminosity of the observed object. Parameters ---------- z_i : float The redshift of the observer. z_f : float The redshift of the observed object. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.luminosity_distance(0.0, 1.0).in_units("Mpc")) """ return ( self.comoving_transverse_distance(0, z_f) * (1 + z_f) - self.comoving_transverse_distance(0, z_i) * (1 + z_i) ).in_base(self.unit_system) def lookback_time(self, z_i, z_f): r""" The difference in the age of the Universe between the redshift interval z_i to z_f. Parameters ---------- z_i : float The lower redshift of the interval. z_f : float The higher redshift of the interval. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.lookback_time(0.0, 1.0).in_units("Gyr")) """ return ( trapezoid_int(self.age_integrand, z_i, z_f) / self.hubble_constant ).in_base(self.unit_system) def critical_density(self, z): r""" The density required for closure of the Universe at a given redshift in the proper frame. Parameters ---------- z : float Redshift. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.critical_density(0.0).in_units("g/cm**3")) >>> print(co.critical_density(0).in_units("Msun/Mpc**3")) """ return (3.0 * self.hubble_parameter(z) ** 2 / 8.0 / np.pi / G).in_base( self.unit_system ) def hubble_parameter(self, z): r""" The value of the Hubble parameter at a given redshift. Parameters ---------- z: float Redshift. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.hubble_parameter(1.0).in_units("km/s/Mpc")) """ return self.hubble_constant.in_base(self.unit_system) * self.expansion_factor(z) def age_integrand(self, z): return 1.0 / (z + 1) / self.expansion_factor(z) def expansion_factor(self, z): r""" The ratio between the Hubble parameter at a given redshift and redshift zero. This is also the primary function integrated to calculate the cosmological distances. """ # Use non-standard dark energy if self.use_dark_factor: dark_factor = self.get_dark_factor(z) # Use default cosmological constant else: dark_factor = 1.0 zp1 = 1 + z return np.sqrt( self.omega_matter * zp1**3 + self.omega_curvature * zp1**2 + self.omega_radiation * zp1**4 + self.omega_lambda * dark_factor ) def inverse_expansion_factor(self, z): return 1.0 / self.expansion_factor(z) def path_length_function(self, z): return ((1 + z) ** 2) * self.inverse_expansion_factor(z) def path_length(self, z_i, z_f): return trapezoid_int(self.path_length_function, z_i, z_f) def t_from_a(self, a): """ Compute the age of the Universe for a given scale factor. Parameters ---------- a : float Scale factor. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.t_from_a(1.0).in_units("Gyr")) """ # Interpolate from a table of log(a) vs. log(t) la = np.log10(a) la_i = min(-6, np.asarray(la).min() - 3) la_f = np.asarray(la).max() bins_per_dex = 1000 n_bins = int((la_f - la_i) * bins_per_dex + 1) la_bins = np.linspace(la_i, la_f, n_bins) z_bins = 1.0 / np.power(10, la_bins) - 1 # Integrate in redshift. lt = trapezoid_cumulative_integral(self.age_integrand, z_bins) # Add a minus sign because we've switched the integration limits. table = InterpTable(la_bins[1:], np.log10(-lt)) t = np.power(10, table(la)) return (t / self.hubble_constant).in_base(self.unit_system) def t_from_z(self, z): """ Compute the age of the Universe for a given redshift. Parameters ---------- z : float Redshift. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.t_from_z(0.0).in_units("Gyr")) """ return self.t_from_a(1.0 / (1.0 + z)) def a_from_t(self, t): """ Compute the scale factor for a given age of the Universe. Parameters ---------- t : YTQuantity or float Time since the Big Bang. If a float is given, units are assumed to be seconds. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.a_from_t(4.0e17)) """ if not isinstance(t, YTArray): t = self.arr(t, "s") lt = np.log10((t * self.hubble_constant).to("")) # Interpolate from a table of log(a) vs. log(t) # Make initial guess for bounds and widen if necessary. la_i = -6 la_f = 6 bins_per_dex = 1000 iter = 0 while True: good = True n_bins = int((la_f - la_i) * bins_per_dex + 1) la_bins = np.linspace(la_i, la_f, n_bins) z_bins = 1.0 / np.power(10, la_bins) - 1 # Integrate in redshift. lt_bins = trapezoid_cumulative_integral(self.age_integrand, z_bins) # Add a minus sign because we've switched the integration limits. table = InterpTable(np.log10(-lt_bins), la_bins[1:]) la = table(lt) # We want to have the la_bins lower bound be decently # below the minimum calculated la values. laa = np.asarray(la) if laa.min() < la_i + 2: la_i -= 3 good = False if laa.max() > la_f: la_f = laa.max() + 1 good = False if good: break iter += 1 if iter > 10: raise RuntimeError("a_from_t calculation did not converge!") a = np.power(10, table(lt)) return a def z_from_t(self, t): """ Compute the redshift for a given age of the Universe. Parameters ---------- t : YTQuantity or float Time since the Big Bang. If a float is given, units are assumed to be seconds. Examples -------- >>> from yt.utilities.cosmology import Cosmology >>> co = Cosmology() >>> print(co.z_from_t(4.0e17)) """ a = self.a_from_t(t) return 1.0 / a - 1.0 def get_dark_factor(self, z): """ This function computes the additional term that enters the expansion factor when using non-standard dark energy. See Dolag et al 2004 eq. 7 for ref (but note that there's a typo in his eq. There should be no negative sign). At the moment, this only works using the parameterization given in Linder 2002 eq. 7: w(a) = w0 + wa(1 - a) = w0 + wa * z / (1+z). This gives rise to an analytic expression. It is also only functional for Gadget simulations, at the moment. Parameters ---------- z: float Redshift """ # Get value of scale factor a corresponding to redshift z scale_factor = 1.0 / (1.0 + z) # Evaluate exponential using Linder02 parameterization dark_factor = np.power( scale_factor, -3.0 * (1.0 + self.w_0 + self.w_a) ) * np.exp(-3.0 * self.w_a * (1.0 - scale_factor)) return dark_factor _arr = None @property def arr(self): if self._arr is not None: return self._arr self._arr = functools.partial(YTArray, registry=self.unit_registry) return self._arr _quan = None @property def quan(self): if self._quan is not None: return self._quan self._quan = functools.partial(YTQuantity, registry=self.unit_registry) return self._quan def trapzint(f, a, b, bins=10000): from yt._maintenance.deprecation import issue_deprecation_warning issue_deprecation_warning( "yt.utilities.cosmology.trapzint is an alias " "to yt.utilities.cosmology.trapezoid_int, " "and will be removed in a future version. " "Please use yt.utilities.cosmology.trapezoid_int directly.", since="4.3.0", stacklevel=3, ) return trapezoid_int(f, a, b, bins) def trapezoid_int(f, a, b, bins=10000): from yt._maintenance.numpy2_compat import trapezoid zbins = np.logspace(np.log10(a + 1), np.log10(b + 1), bins) - 1 return trapezoid(f(zbins[:-1]), x=zbins[:-1], dx=np.diff(zbins)) def trapezoid_cumulative_integral(f, x): """ Perform cumulative integration using the trapezoid rule. """ fy = f(x) return (0.5 * (fy[:-1] + fy[1:]) * np.diff(x)).cumsum() class InterpTable: """ Generate a function to linearly interpolate from provided arrays. """ def __init__(self, x, y): self.x = x self.y = y def __call__(self, val): i = np.clip(np.digitize(val, self.x) - 1, 0, self.x.size - 2) slope = (self.y[i + 1] - self.y[i]) / (self.x[i + 1] - self.x[i]) return slope * (val - self.x[i]) + self.y[i] yt-project-yt-f043ac8/yt/utilities/cython_fortran_utils.pxd000066400000000000000000000011211510711153200243100ustar00rootroot00000000000000cimport numpy as np from libc.stdio cimport FILE ctypedef np.int32_t INT32_t ctypedef np.int64_t INT64_t ctypedef np.float64_t DOUBLE_t cdef class FortranFile: cdef FILE* cfile cdef bint _closed cpdef INT64_t skip(self, INT64_t n=*) except -1 cdef INT64_t get_size(self, str dtype) cpdef INT32_t read_int(self) except? -1 cpdef np.ndarray read_vector(self, str dtype) cdef int read_vector_inplace(self, str dtype, void *data) cpdef INT64_t tell(self) except -1 cpdef INT64_t seek(self, INT64_t pos, INT64_t whence=*) except -1 cpdef void close(self) yt-project-yt-f043ac8/yt/utilities/cython_fortran_utils.pyx000066400000000000000000000246651510711153200243570ustar00rootroot00000000000000# distutils: libraries = STD_LIBS cimport numpy as np import numpy as np from libc.stdio cimport * cdef INT32_SIZE = sizeof(np.int32_t) cdef DOUBLE_SIZE = sizeof(np.float64_t) cdef class FortranFile: """This class provides facilities to interact with files written in fortran-record format. Since this is a non-standard file format, whose contents depend on the compiler and the endianness of the machine, caution is advised. This code will assume that the record header is written as a 32bit (4byte) signed integer. The code also assumes that the records use the system's local endianness. Notes ----- Since the assumed record header is an signed integer on 32bit, it will overflow at 2**31=2147483648 elements. This module has been inspired by scipy's FortranFile, especially the docstrings. """ def __cinit__(self, str fname): self.cfile = fopen(fname.encode('utf-8'), 'rb') self._closed = False if self.cfile is NULL: self._closed = True raise FileNotFoundError(fname.encode('utf-8')) def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() cpdef INT64_t skip(self, INT64_t n=1) except -1: """Skip records. Parameters ---------- - n : integer The number of records to skip Returns ------- value : int Returns 0 on success. """ cdef INT32_t s1, s2, i if self._closed: raise ValueError("Read of closed file.") for i in range(n): fread(&s1, INT32_SIZE, 1, self.cfile) fseek(self.cfile, s1, SEEK_CUR) fread(&s2, INT32_SIZE, 1, self.cfile) if s1 != s2: raise IOError('Sizes do not agree in the header and footer for ' 'this record - check header dtype. Got %s and %s' % (s1, s2)) return 0 cdef INT64_t get_size(self, str dtype): """Return the size of an element given its datatype. Parameters ---------- dtype : str The dtype, see note for details about the values of dtype. Returns ------- size : int The size in byte of the dtype Note: ----- See https://docs.python.org/3.5/library/struct.html#format-characters for details about the formatting characters. """ if dtype == 'i': return 4 elif dtype == 'd': return 8 elif dtype == 'f': return 4 elif dtype == 'l': return 8 else: # Fallback to (slow) numpy-based to compute the size return np.dtype(dtype).itemsize cpdef np.ndarray read_vector(self, str dtype): """Reads a record from the file and return it as numpy array. Parameters ---------- d : data type This is the datatype (from the struct module) that we should read. Returns ------- tr : numpy.ndarray This is the vector of values read from the file. Examples -------- >>> f = FortranFile("fort.3") >>> rv = f.read_vector("d") # Read a float64 array >>> rv = f.read_vector("i") # Read an int32 array """ cdef INT32_t s1, s2, size cdef np.ndarray data if self._closed: raise ValueError("I/O operation on closed file.") size = self.get_size(dtype) fread(&s1, INT32_SIZE, 1, self.cfile) # Check record is compatible with data type if s1 % size != 0: raise ValueError('Size obtained (%s) does not match with the expected ' 'size (%s) of multi-item record' % (s1, size)) data = np.empty(s1 // size, dtype=dtype) fread(data.data, size, s1 // size, self.cfile) fread(&s2, INT32_SIZE, 1, self.cfile) if s1 != s2: raise IOError('Sizes do not agree in the header and footer for ' 'this record - check header dtype') return data cdef int read_vector_inplace(self, str dtype, void *data): """Reads a record from the file. Parameters ---------- d : data type This is the datatype (from the struct module) that we should read. data : void* The pointer where to store the data. It should be preallocated and have the correct size. """ cdef INT32_t s1, s2, size if self._closed: raise ValueError("I/O operation on closed file.") size = self.get_size(dtype) fread(&s1, INT32_SIZE, 1, self.cfile) # Check record is compatible with data type if s1 % size != 0: raise ValueError('Size obtained (%s) does not match with the expected ' 'size (%s) of multi-item record' % (s1, size)) fread(data, size, s1 // size, self.cfile) fread(&s2, INT32_SIZE, 1, self.cfile) if s1 != s2: raise IOError('Sizes do not agree in the header and footer for ' 'this record - check header dtype') cpdef INT32_t read_int(self) except? -1: """Reads a single int32 from the file and return it. Returns ------- data : int32 The value. Examples -------- >>> f = FortranFile("fort.3") >>> rv = f.read_vector("d") # Read a float64 array >>> rv = f.read_vector("i") # Read an int32 array """ cdef INT32_t s1, s2 cdef INT32_t data if self._closed: raise ValueError("I/O operation on closed file.") fread(&s1, INT32_SIZE, 1, self.cfile) if s1 != INT32_SIZE != 0: raise ValueError('Size obtained (%s) does not match with the expected ' 'size (%s) of record' % (s1, INT32_SIZE)) fread(&data, INT32_SIZE, s1 // INT32_SIZE, self.cfile) fread(&s2, INT32_SIZE, 1, self.cfile) if s1 != s2: raise IOError('Sizes do not agree in the header and footer for ' 'this record - check header dtype') return data def read_attrs(self, object attrs): """This function reads from that file according to a definition of attributes, returning a dictionary. Fortran unformatted files provide total bytesize at the beginning and end of a record. By correlating the components of that record with attribute names, we construct a dictionary that gets returned. Note that this function is used for reading sequentially-written records. If you have many written that were written simultaneously. Parameters ---------- attrs : iterable of iterables This object should be an iterable of one of the formats: [ (attr_name, count, struct type), ... ]. [ ((name1,name2,name3), count, vector type] [ ((name1,name2,name3), count, 'type type type'] [ (attr_name, count, struct type, optional)] `optional` : boolean. If True, the attribute can be stored as an empty Fortran record. Returns ------- values : dict This will return a dict of iterables of the components of the values in the file. Examples -------- >>> header = [ ("ncpu", 1, "i"), ("nfiles", 2, "i") ] >>> f = FortranFile("fort.3") >>> rv = f.read_attrs(header) """ cdef str dtype cdef int n cdef dict data cdef key cdef np.ndarray tmp cdef bint optional if self._closed: raise ValueError("I/O operation on closed file.") data = {} for a in attrs: if len(a) == 3: key, n, dtype = a optional = False else: key, n, dtype, optional = a if n == 1: tmp = self.read_vector(dtype) if len(tmp) == 0 and optional: continue elif (len(tmp) == 1) or (n == -1): data[key] = tmp[0] else: raise ValueError("Expected a record of length %s, got %s (%s)" % (n, len(tmp), key)) else: tmp = self.read_vector(dtype) if (len(tmp) == 0 and optional): continue elif (len(tmp) != n) and (n != -1): raise ValueError("Expected a record of length %s, got %s (%s)" % (n, len(tmp), key)) if isinstance(key, tuple): # There are multiple keys for ikey in range(n): data[key[ikey]] = tmp[ikey] else: data[key] = tmp return data cpdef INT64_t tell(self) except -1: """Return current stream position.""" cdef INT64_t pos if self._closed: raise ValueError("I/O operation on closed file.") pos = ftell(self.cfile) return pos cpdef INT64_t seek(self, INT64_t pos, INT64_t whence=SEEK_SET) except -1: """Change stream position. Parameters ---------- pos : int Change the stream position to the given byte offset. The offset is interpreted relative to the position indicated by whence. whence : int Determine how pos is interpreted. Can by any of * 0 -- start of stream (the default); offset should be zero or positive * 1 -- current stream position; offset may be negative * 2 -- end of stream; offset is usually negative Returns ------- pos : int The new absolute position. """ if self._closed: raise ValueError("I/O operation on closed file.") if whence < 0 or whence > 2: raise ValueError("whence argument can be 0, 1, or 2. Got %s" % whence) fseek(self.cfile, pos, whence) return self.tell() cpdef void close(self): """Close the file descriptor. This method has no effect if the file is already closed. """ if self._closed: return fclose(self.cfile) self._closed = True def __dealloc__(self): if not self._closed: self.close() yt-project-yt-f043ac8/yt/utilities/decompose.py000066400000000000000000000116631510711153200216600ustar00rootroot00000000000000import numpy as np def SIEVE_PRIMES(x): return x and x[:1] + SIEVE_PRIMES([n for n in x if n % x[0]]) def decompose_to_primes(max_prime): """Decompose number into the primes""" for prime in SIEVE_PRIMES(list(range(2, max_prime))): if prime * prime > max_prime: break while max_prime % prime == 0: yield prime max_prime //= prime if max_prime > 1: yield max_prime def decompose_array(shape, psize, bbox, *, cell_widths=None): """Calculate list of product(psize) subarrays of arr, along with their left and right edges """ return split_array(bbox[:, 0], bbox[:, 1], shape, psize, cell_widths=cell_widths) def evaluate_domain_decomposition(n_d, pieces, ldom): """Evaluate longest to shortest edge ratio BEWARE: lot's of magic here""" eff_dim = (n_d > 1).sum() exp = float(eff_dim - 1) / float(eff_dim) ideal_bsize = eff_dim * pieces ** (1.0 / eff_dim) * np.prod(n_d) ** exp mask = np.where(n_d > 1) nd_arr = np.array(n_d, dtype=np.float64)[mask] bsize = int(np.sum(ldom[mask] / nd_arr * np.prod(nd_arr))) load_balance = float(np.prod(n_d)) / ( float(pieces) * np.prod((n_d - 1) // ldom + 1) ) # 0.25 is magic number quality = load_balance / (1 + 0.25 * (bsize / ideal_bsize - 1.0)) # \todo add a factor that estimates lower cost when x-direction is # not chopped too much # \deprecated estimate these magic numbers quality *= 1.0 - (0.001 * ldom[0] + 0.0001 * ldom[1]) / pieces if np.any(ldom > n_d): quality = 0 return quality def factorize_number(pieces): """Return array consisting of prime, its power and number of different decompositions in three dimensions for this prime """ factors = list(decompose_to_primes(pieces)) temp = np.bincount(factors) return np.array( [ (prime, temp[prime], (temp[prime] + 1) * (temp[prime] + 2) // 2) for prime in np.unique(factors) ], dtype="int64", ) def get_psize(n_d, pieces): """Calculate the best division of array into px*py*pz subarrays. The goal is to minimize the ratio of longest to shortest edge to minimize the amount of inter-process communication. """ fac = factorize_number(pieces) nfactors = len(fac[:, 2]) best = 0.0 p_size = np.ones(3, dtype=np.int64) if pieces == 1: return p_size while np.all(fac[:, 2] > 0): ldom = np.ones(3, dtype=np.int64) for nfac in range(nfactors): i = int(np.sqrt(0.25 + 2 * (fac[nfac, 2] - 1)) - 0.5) k = fac[nfac, 2] - (1 + i * (i + 1) // 2) i = fac[nfac, 1] - i j = fac[nfac, 1] - (i + k) ldom *= fac[nfac, 0] ** np.array([i, j, k]) quality = evaluate_domain_decomposition(n_d, pieces, ldom) if quality > best: best = quality p_size = ldom # search for next unique combination for j in range(nfactors): if fac[j, 2] > 1: fac[j, 2] -= 1 break else: if j < nfactors - 1: fac[j, 2] = int((fac[j, 1] + 1) * (fac[j, 1] + 2) / 2) else: fac[:, 2] = 0 # no more combinations to try return p_size def split_array(gle, gre, shape, psize, *, cell_widths=None): """Split array into px*py*pz subarrays.""" n_d = np.array(shape, dtype=np.int64) dds = (gre - gle) / shape left_edges = [] right_edges = [] shapes = [] slices = [] if cell_widths is None: cell_widths_by_grid = None else: cell_widths_by_grid = [] for i in range(psize[0]): for j in range(psize[1]): for k in range(psize[2]): piece = np.array((i, j, k), dtype=np.int64) lei = n_d * piece // psize rei = n_d * (piece + np.ones(3, dtype=np.int64)) // psize if cell_widths is not None: cws = [] offset_le = [] offset_re = [] for idim in range(3): cws.append(cell_widths[idim][lei[idim] : rei[idim]]) offset_le.append(np.sum(cell_widths[idim][0 : lei[idim]])) offset_re.append(offset_le[idim] + np.sum(cws[idim])) cell_widths_by_grid.append(cws) offset_re = np.array(offset_re) offset_le = np.array(offset_le) else: offset_le = lei * dds offset_re = rei * dds lle = gle + offset_le lre = gle + offset_re left_edges.append(lle) right_edges.append(lre) shapes.append(rei - lei) slices.append(np.s_[lei[0] : rei[0], lei[1] : rei[1], lei[2] : rei[2]]) return left_edges, right_edges, shapes, slices, cell_widths_by_grid yt-project-yt-f043ac8/yt/utilities/definitions.py000066400000000000000000000016011510711153200222040ustar00rootroot00000000000000from .physical_ratios import ( au_per_mpc, cm_per_mpc, km_per_mpc, kpc_per_mpc, miles_per_mpc, mpc_per_mpc, pc_per_mpc, rsun_per_mpc, sec_per_day, sec_per_Gyr, sec_per_Myr, sec_per_year, ) # The number of levels we expect to have at most MAXLEVEL = 48 # How many of each thing are in an Mpc mpc_conversion = { "Mpc": mpc_per_mpc, "mpc": mpc_per_mpc, "kpc": kpc_per_mpc, "pc": pc_per_mpc, "au": au_per_mpc, "rsun": rsun_per_mpc, "miles": miles_per_mpc, "km": km_per_mpc, "cm": cm_per_mpc, } # Nicely formatted versions of common length units formatted_length_unit_names = { "au": "AU", "rsun": r"R_\odot", "code_length": r"code\ length", } # How many seconds are in each thing sec_conversion = { "Gyr": sec_per_Gyr, "Myr": sec_per_Myr, "years": sec_per_year, "days": sec_per_day, } yt-project-yt-f043ac8/yt/utilities/exceptions.py000066400000000000000000000657111510711153200220660ustar00rootroot00000000000000# We don't need to import 'exceptions' import os.path from unyt.exceptions import UnitOperationError from yt._typing import FieldKey class YTException(Exception): pass # Data access exceptions: class YTUnidentifiedDataType(YTException): def __init__(self, filename, *args, **kwargs): self.filename = filename self.args = args self.kwargs = kwargs def __str__(self): from yt.utilities.hierarchy_inspection import ( get_classes_with_missing_requirements, ) msg = f"Could not determine input format from {self.filename!r}" if self.args: msg += ", " + (", ".join(f"{a!r}" for a in self.args)) if self.kwargs: msg += ", " + (", ".join(f"{k}={v!r}" for k, v in self.kwargs.items())) msg += "\n" if len(unusable_classes := get_classes_with_missing_requirements()) > 0: msg += ( "The following types could not be thorougly checked against your data because " "their requirements are missing. " "You may want to inspect this list and check your installation:" ) for cls, missing in unusable_classes.items(): requirements_str = ", ".join(missing) msg += f"\n- {cls.__name__} (requires: {requirements_str})" msg += "\n\n" msg += "Please make sure you are running a sufficiently recent version of yt." return msg class YTAmbiguousDataType(YTUnidentifiedDataType): def __init__(self, filename, candidates): self.filename = filename self.candidates = candidates def __str__(self): msg = f"Multiple data type candidates for {self.filename}\n" msg += "The following independent classes were detected as valid :\n" for c in self.candidates: msg += f"{c}\n" msg += ( "This degeneracy can be lifted using the `hint` keyword argument in yt.load" ) return msg class YTSphereTooSmall(YTException): def __init__(self, ds, radius, smallest_cell): self.ds = ds self.radius = radius self.smallest_cell = smallest_cell def __str__(self): return f"{self.radius:0.5e} < {self.smallest_cell:0.5e}" class YTAxesNotOrthogonalError(YTException): def __init__(self, axes): self.axes = axes def __str__(self): return f"The supplied axes are not orthogonal. {self.axes}" class YTNoDataInObjectError(YTException): def __init__(self, obj): self.obj_type = getattr(obj, "_type_name", "") def __str__(self): s = "The object requested has no data included in it." if self.obj_type == "slice": s += " It may lie on a grid face. Try offsetting slightly." return s class YTFieldNotFound(YTException): def __init__(self, field, ds): self.field = field self.ds = ds def _get_suggestions(self) -> list[FieldKey]: from yt.funcs import levenshtein_distance field = self.field ds = self.ds suggestions = {} if not isinstance(field, tuple): ftype, fname = None, field elif field[1] is None: ftype, fname = None, field[0] else: ftype, fname = field # Limit the suggestions to a distance of 3 (at most 3 edits) # This is very arbitrary, but is picked so that... # - small typos lead to meaningful suggestions (e.g. `densty` -> `density`) # - we don't suggest unrelated things (e.g. `pressure` -> `density` has a distance # of 6, we definitely do not want it) # A threshold of 3 seems like a good middle point. max_distance = 3 # Suggest (ftype, fname), with alternative ftype for ft, fn in ds.derived_field_list: if fn.lower() == fname.lower() and ( ftype is None or ft.lower() != ftype.lower() ): suggestions[ft, fn] = 0 if ftype is not None: # Suggest close matches using levenshtein distance fields_str = {_: str(_).lower() for _ in ds.derived_field_list} field_str = str(field).lower() for (ft, fn), fs in fields_str.items(): distance = levenshtein_distance(field_str, fs, max_dist=max_distance) if distance < max_distance: if (ft, fn) in suggestions: continue suggestions[ft, fn] = distance # Return suggestions sorted by increasing distance (first are most likely) return [ (ft, fn) for (ft, fn), distance in sorted(suggestions.items(), key=lambda v: v[1]) ] def __str__(self): msg = f"Could not find field {self.field!r} in {self.ds}." try: suggestions = self._get_suggestions() except AttributeError: # This may happen if passing a field that is e.g. an Ellipsis # e.g. when using ds.r[...] suggestions = [] if suggestions: msg += "\nDid you mean:\n\t" msg += "\n\t".join(str(_) for _ in suggestions) return msg class YTParticleTypeNotFound(YTException): def __init__(self, fname, ds): self.fname = fname self.ds = ds def __str__(self): return f"Could not find particle_type {self.fname!r} in {self.ds}." class YTSceneFieldNotFound(YTException): pass class YTCouldNotGenerateField(YTFieldNotFound): def __str__(self): return f"Could field '{self.fname}' in {self.ds} could not be generated." class YTFieldTypeNotFound(YTException): def __init__(self, ftype, ds=None): self.ftype = ftype self.ds = ds def __str__(self): if self.ds is not None and self.ftype in self.ds.particle_types: return ( f"Could not find field type {self.ftype!r}. " "This field type is a known particle type for this dataset. " "Try adding this field with sampling_type='particle'." ) else: return f"Could not find field type {self.ftype!r}." class YTSimulationNotIdentified(YTException): def __init__(self, sim_type): self.sim_type = sim_type def __str__(self): from yt.utilities.object_registries import simulation_time_series_registry return ( f"Simulation time-series type {self.sim_type!r} not defined. " f"Supported types are {list(simulation_time_series_registry)}" ) class YTCannotParseFieldDisplayName(YTException): def __init__(self, field_name, display_name, mathtext_error): self.field_name = field_name self.display_name = display_name self.mathtext_error = mathtext_error def __str__(self): return ( f"The display name {self.display_name!r} of the derived field {self.field_name!r} " f"contains the following LaTeX parser errors:\n{self.mathtext_error}" ) class YTCannotParseUnitDisplayName(YTException): def __init__(self, field_name, unit_name, mathtext_error): self.field_name = field_name self.unit_name = unit_name self.mathtext_error = mathtext_error def __str__(self): return ( f"The unit display name {self.unit_name!r} of the derived field {self.field_name!r} " f"contains the following LaTeX parser errors:\n{self.mathtext_error}" ) class InvalidSimulationTimeSeries(YTException): def __init__(self, message): self.message = message def __str__(self): return self.message class MissingParameter(YTException): def __init__(self, ds, parameter): self.ds = ds self.parameter = parameter def __str__(self): return f"dataset {self.ds} is missing {self.parameter} parameter." class NoStoppingCondition(YTException): def __init__(self, ds): self.ds = ds def __str__(self): return f"Simulation {self.ds} has no stopping condition. StopTime or StopCycle should be set." class YTNotInsideNotebook(YTException): def __str__(self): return "This function only works from within an IPython Notebook." class YTCoordinateNotImplemented(YTException): def __str__(self): return "This coordinate is not implemented for this geometry type." # define for back compat reasons for code written before yt 4.0 YTUnitOperationError = UnitOperationError class YTUnitNotRecognized(YTException): def __init__(self, unit): self.unit = unit def __str__(self): return f"This dataset doesn't recognize {self.unit!r}" class YTFieldUnitError(YTException): def __init__(self, field_info, returned_units): self.msg = ( f"The field function associated with the field {field_info.name!r} returned " f"data with units {returned_units!r} but was defined with units {field_info.units!r}." ) def __str__(self): return self.msg class YTFieldUnitParseError(YTException): def __init__(self, field_info): self.msg = ( f"The field {field_info.name!r} has unparsable units {field_info.units!r}." ) def __str__(self): return self.msg class YTSpatialFieldUnitError(YTException): def __init__(self, field): self.msg = ( f"Field {field!r} is a spatial field but has unknown units but " "spatial fields must have explicitly defined units. Add the " "field with explicit 'units' to clear this error." ) def __str__(self): return self.msg class YTHubRegisterError(YTException): def __str__(self): return ( "You must create an API key before uploading. See " "https://data.yt-project.org/getting_started.html" ) class YTNoFilenamesMatchPattern(YTException): def __init__(self, pattern): self.pattern = pattern def __str__(self): return f"No filenames were found to match the pattern: {self.pattern!r}" class YTNoOldAnswer(YTException): def __init__(self, path): self.path = path def __str__(self): return f"There is no old answer available.\n{self.path!r}" class YTNoAnswerNameSpecified(YTException): def __init__(self, message=None): if message is None or message == "": message = ( "Answer name not provided for the answer testing test." "\n Please specify --answer-name= in" " command line mode or in AnswerTestingTest.answer_name" " variable." ) self.message = message def __str__(self): return str(self.message) class YTCloudError(YTException): def __init__(self, path): self.path = path def __str__(self): return ( f"Failed to retrieve cloud data. Connection may be broken.\n {self.path!r}" ) class YTEllipsoidOrdering(YTException): def __init__(self, ds, A, B, C): self.ds = ds self._A = A self._B = B self._C = C def __str__(self): return "Must have A>=B>=C" class EnzoTestOutputFileNonExistent(YTException): def __init__(self, filename): self.filename = filename self.testname = os.path.basename(os.path.dirname(filename)) def __str__(self): return ( f"Enzo test output file (OutputLog) not generated for: {self.testname!r}.\n" "Test did not complete." ) class YTNoAPIKey(YTException): def __init__(self, service, config_name): self.service = service self.config_name = config_name def __str__(self): from yt.config import config_dir try: conf = os.path.join(config_dir(), "yt", "yt.toml") except Exception: # this is really not a good time to raise another exception conf = "yt's configuration file" return f"You need to set an API key for {self.service!r} in {conf} as {self.config_name!r}" class YTTooManyVertices(YTException): def __init__(self, nv, fn): self.nv = nv self.fn = fn def __str__(self): s = f"There are too many vertices ({self.nv}) to upload to Sketchfab. " s += f"Your model has been saved as {self.fn} . You should upload manually." return s class YTInvalidWidthError(YTException): def __init__(self, width): self.error = f"width ({str(width)}) is invalid" def __str__(self): return str(self.error) class YTFieldNotParseable(YTException): def __init__(self, field): self.field = field def __str__(self): return f"Cannot identify field {self.field!r}" class YTDataSelectorNotImplemented(YTException): def __init__(self, class_name): self.class_name = class_name def __str__(self): return f"Data selector {self.class_name!r} not implemented." class YTParticleDepositionNotImplemented(YTException): def __init__(self, class_name): self.class_name = class_name def __str__(self): return f"Particle deposition method {self.class_name!r} not implemented." class YTDomainOverflow(YTException): def __init__(self, mi, ma, dle, dre): self.mi = mi self.ma = ma self.dle = dle self.dre = dre def __str__(self): return ( f"Particle bounds {self.mi} and {self.ma} " f"exceed domain bounds {self.dle} and {self.dre}" ) class YTIntDomainOverflow(YTException): def __init__(self, dims, dd): self.dims = dims self.dd = dd def __str__(self): return f"Integer domain overflow: {self.dims} in {self.dd}" class YTIllDefinedFilter(YTException): def __init__(self, filter, s1, s2): self.filter = filter self.s1 = s1 self.s2 = s2 def __str__(self): return ( f"Filter {self.filter!r} ill-defined. " f"Applied to shape {self.s1} but is shape {self.s2}." ) class YTIllDefinedParticleFilter(YTException): def __init__(self, filter, missing): self.filter = filter self.missing = missing def __str__(self): msg = ( '\nThe fields\n\t{},\nrequired by the "{}" particle filter, ' "are not defined for this dataset." ) f = self.filter return msg.format("\n".join(str(m) for m in self.missing), f.name) class YTIllDefinedBounds(YTException): def __init__(self, lb, ub): self.lb = lb self.ub = ub def __str__(self): v = f"The bounds {self.lb:0.3e} and {self.ub:0.3e} are ill-defined. " v += "Typically this happens when a log binning is specified " v += "and zero or negative values are given for the bounds." return v class YTObjectNotImplemented(YTException): def __init__(self, ds, obj_name): self.ds = ds self.obj_name = obj_name def __str__(self): return f"The object type {self.obj_name!r} is not implemented for the dataset {self.ds!s}" class YTParticleOutputFormatNotImplemented(YTException): def __str__(self): return "The particle output format is not supported." class YTFileNotParseable(YTException): def __init__(self, fname, line): self.fname = fname self.line = line def __str__(self): return f"Error while parsing file {self.fname!r} at line {self.line}" class YTRockstarMultiMassNotSupported(YTException): def __init__(self, mi, ma, ptype): self.mi = mi self.ma = ma self.ptype = ptype def __str__(self): v = f"Particle type '{self.ptype}' has minimum mass {self.mi:0.3e} and maximum " v += f"mass {self.ma:0.3e}. Multi-mass particles are not currently supported." return v class YTTooParallel(YTException): def __str__(self): return "You've used too many processors for this dataset." class YTElementTypeNotRecognized(YTException): def __init__(self, dim, num_nodes): self.dim = dim self.num_nodes = num_nodes def __str__(self): return f"Element type not recognized - dim = {self.dim}, num_nodes = {self.num_nodes}" class YTDuplicateFieldInProfile(YTException): def __init__(self, field, new_spec, old_spec): self.field = field self.new_spec = new_spec self.old_spec = old_spec def __str__(self): r = f"""Field {self.field} already exists with field spec: {self.old_spec} But being asked to add it with: {self.new_spec}""" return r class YTInvalidPositionArray(YTException): def __init__(self, shape, dimensions): self.shape = shape self.dimensions = dimensions def __str__(self): r = f"""Position arrays must be length and shape (N,3). But this one has {self.dimensions} and {self.shape}.""" return r class YTIllDefinedCutRegion(YTException): def __init__(self, conditions): self.conditions = conditions def __str__(self): r = ( "Can't mix particle/discrete and fluid/mesh conditions or quantities. " "Conditions specified:\n" ) r += "\n".join(c for c in self.conditions) return r class YTMixedCutRegion(YTException): def __init__(self, conditions, field): self.conditions = conditions self.field = field def __str__(self): r = f"""Can't mix particle/discrete and fluid/mesh conditions or quantities. Field: {self.field} and Conditions specified: """ r += "\n".join(c for c in self.conditions) return r class YTGDFAlreadyExists(YTException): def __init__(self, filename): self.filename = filename def __str__(self): return f"A file already exists at {self.filename} and overwrite=False." class YTNonIndexedDataContainer(YTException): def __init__(self, cont): self.cont = cont def __str__(self): class_name = self.cont.__class__.__name__ return ( f"The data container type ({class_name}) is an unindexed type. " "Operations such as ires, icoords, fcoords and fwidth will not work on it.\n" "Did you just attempt to perform an off-axis operation ? " "Be sure to consult the latest documentation to see whether the operation " "you tried is actually supported for your data type." ) class YTGDFUnknownGeometry(YTException): def __init__(self, geometry): self.geometry = geometry def __str__(self): return ( """Unknown geometry %i. Please refer to GDF standard for more information""" % self.geometry ) class YTInvalidUnitEquivalence(YTException): def __init__(self, equiv, unit1, unit2): self.equiv = equiv self.unit1 = unit1 self.unit2 = unit2 def __str__(self): return f"The unit equivalence {self.equiv!r} does not exist for the units {self.unit1!r} and {self.unit2!r}." class YTPlotCallbackError(YTException): def __init__(self, callback): self.callback = "annotate_" + callback def __str__(self): return f"{self.callback} callback failed" class YTUnsupportedPlotCallback(YTPlotCallbackError): def __init__(self, callback: str, plot_type: str) -> None: super().__init__(callback) self.plot_type = plot_type def __str__(self): return f"The `{self.plot_type}` class currently doesn't support the `{self.callback}` method." class YTPixelizeError(YTException): def __init__(self, message): self.message = message def __str__(self): return self.message class YTDimensionalityError(YTException): def __init__(self, wrong, right): self.wrong = wrong self.right = right def __str__(self): return f"Dimensionality specified was {self.wrong} but we need {self.right}" class YTInvalidShaderType(YTException): def __init__(self, source): self.source = source def __str__(self): return f"Can't identify shader_type for file {self.source!r}" class YTInvalidFieldType(YTException): def __init__(self, fields): self.fields = fields def __str__(self): return ( "\nSlicePlot, ProjectionPlot, and OffAxisProjectionPlot can " "only plot fields that\n" "are defined on a mesh or for SPH particles, but received the " "following N-body\n" "particle fields:\n\n" f" {self.fields!r}\n\n" "Did you mean to use ParticlePlot or plot a deposited particle " "field instead?" ) class YTUnknownUniformKind(YTException): def __init__(self, kind): self.kind = kind def __str__(self): return f"Can't determine kind specification for {self.kind!r}" class YTUnknownUniformSize(YTException): def __init__(self, size_spec): self.size_spec = size_spec def __str__(self): return f"Can't determine size specification for {self.size_spec!r}" class YTDataTypeUnsupported(YTException): def __init__(self, this, supported): self.supported = supported self.this = this def __str__(self): v = f"This operation is not supported for data of geometry {self.this!r}; " v += f"It supports data of geometries {self.supported!r}" return v class YTBoundsDefinitionError(YTException): def __init__(self, message, bounds): self.bounds = bounds self.message = message def __str__(self): v = f"This operation has encountered a bounds error: {self.message} " v += f"\nSpecified bounds are {self.bounds!r}." return v def screen_one_element_list(lis): if len(lis) == 1: return lis[0] return lis class YTIllDefinedProfile(YTException): def __init__(self, bin_fields, fields, weight_field, is_pfield): nbin = len(bin_fields) nfields = len(fields) self.bin_fields = screen_one_element_list(bin_fields) self.bin_fields_ptype = screen_one_element_list(is_pfield[:nbin]) self.fields = screen_one_element_list(fields) self.fields_ptype = screen_one_element_list(is_pfield[nbin : nbin + nfields]) self.weight_field = weight_field if self.weight_field is not None: self.weight_field_ptype = is_pfield[-1] def __str__(self): msg = ( "\nCannot create a profile object that mixes particle and mesh " "fields.\n\n" "Received the following bin_fields:\n\n" " %s, particle_type = %s\n\n" "Profile fields:\n\n" " %s, particle_type = %s\n" ) msg = msg % ( self.bin_fields, self.bin_fields_ptype, self.fields, self.fields_ptype, ) if self.weight_field is not None: weight_msg = "\nAnd weight field:\n\n %s, particle_type = %s\n" weight_msg = weight_msg % (self.weight_field, self.weight_field_ptype) else: weight_msg = "" return msg + weight_msg class YTProfileDataShape(YTException): def __init__(self, field1, shape1, field2, shape2): self.field1 = field1 self.shape1 = shape1 self.field2 = field2 self.shape2 = shape2 def __str__(self): return ( "Profile fields must have same shape: {self.field1!r} has " f"shape {self.shape1} and {self.field2!r} has shape {self.shape2}." ) class YTBooleanObjectError(YTException): def __init__(self, bad_object): self.bad_object = bad_object def __str__(self): v = f"Supplied:\n{self.bad_object}\nto a boolean operation" v += " but it is not a YTSelectionContainer3D object." return v class YTBooleanObjectsWrongDataset(YTException): def __init__(self): pass def __str__(self): return "Boolean data objects must share a common dataset object." class YTIllDefinedAMR(YTException): def __init__(self, level, axis): self.level = level self.axis = axis def __str__(self): return ( f"Grids on the level {self.level} are not properly aligned with cell edges " f"on the parent level ({self.axis!r} axis)" ) class YTIllDefinedParticleData(YTException): pass class YTIllDefinedAMRData(YTException): pass class YTInconsistentGridFieldShape(YTException): def __init__(self, shapes): self.shapes = shapes def __str__(self): msg = "Not all grid-based fields have the same shape!\n" for name, shape in self.shapes: msg += f" Field {name!r} has shape {shape}.\n" return msg class YTInconsistentParticleFieldShape(YTException): def __init__(self, ptype, shapes): self.ptype = ptype self.shapes = shapes def __str__(self): msg = "Not all fields with field type {self.ptype!r} have the same shape!\n" for name, shape in self.shapes: field = (self.ptype, name) msg += f" Field {field} has shape {shape}.\n" return msg class YTInconsistentGridFieldShapeGridDims(YTException): def __init__(self, shapes, grid_dims): self.shapes = shapes self.grid_dims = grid_dims def __str__(self): msg = "Not all grid-based fields match the grid dimensions! " msg += f"Grid dims are {self.grid_dims}, " msg += "and the following fields have shapes that do not match them:\n" for name, shape in self.shapes: if shape != self.grid_dims: msg += f" Field {name} has shape {shape}.\n" return msg class YTCommandRequiresModule(YTException): def __init__(self, module: str): self.module = module def __str__(self): msg = f"This command requires {self.module!r} to be installed.\n\n" msg += f"Please install {self.module!r} with the package manager " msg += "appropriate for your python environment, e.g.:\n" msg += f" conda install {self.module}\n" msg += "or:\n" msg += f" python -m pip install {self.module}\n" return msg class YTModuleRemoved(YTException): def __init__(self, name, new_home=None, info=None): message = f"The {name} module has been removed from yt." if new_home is not None: message += f"\nIt has been moved to {new_home}." if info is not None: message += f"\nFor more information, see {info}." super().__init__(message) class YTArrayTooLargeToDisplay(YTException): def __init__(self, size, max_size): self.size = size self.max_size = max_size def __str__(self): msg = f"The requested array is of size {self.size}.\n" msg += "We do not support displaying arrays larger\n" msg += f"than size {self.max_size}." return msg class YTConfigurationError(YTException): pass class GenerationInProgress(Exception): def __init__(self, fields): self.fields = fields class MountError(Exception): def __init__(self, message): self.message = message yt-project-yt-f043ac8/yt/utilities/file_handler.py000066400000000000000000000076301510711153200223150ustar00rootroot00000000000000from contextlib import contextmanager from yt._maintenance.deprecation import issue_deprecation_warning from yt.utilities.on_demand_imports import NotAModule, _h5py as h5py def valid_hdf5_signature(fn: str, /) -> bool: signature = b"\x89HDF\r\n\x1a\n" try: with open(fn, "rb") as f: header = f.read(8) return header == signature except Exception: return False def warn_h5py(fn): issue_deprecation_warning( "warn_h5py is not used within yt any more and is deprecated.", since="4.3" ) needs_h5py = valid_hdf5_signature(fn) if needs_h5py and isinstance(h5py.File, NotAModule): raise RuntimeError( "This appears to be an HDF5 file, but h5py is not installed." ) class HDF5FileHandler: handle = None def __init__(self, filename): self.handle = h5py.File(filename, mode="r") def __getitem__(self, key): return self.handle[key] def __contains__(self, item): return item in self.handle def __len__(self): return len(self.handle) @property def attrs(self): return self.handle.attrs def keys(self): return list(self.handle.keys()) def items(self): return list(self.handle.items()) def close(self): if self.handle is not None: self.handle.close() class FITSFileHandler(HDF5FileHandler): def __init__(self, filename): from yt.utilities.on_demand_imports import _astropy if isinstance(filename, _astropy.pyfits.hdu.image._ImageBaseHDU): self.handle = _astropy.pyfits.HDUList(filename) elif isinstance(filename, _astropy.pyfits.HDUList): self.handle = filename else: self.handle = _astropy.pyfits.open( filename, memmap=True, do_not_scale_image_data=True, ignore_blank=True ) self._fits_files = [] def __del__(self): for f in self._fits_files: f.close() del self._fits_files del self.handle self.handle = None def close(self): self.handle.close() def valid_netcdf_signature(fn: str, /) -> bool: try: with open(fn, "rb") as f: header = f.read(4) except Exception: return False else: return header in (b"CDF\x01", b"CDF\x02") or ( fn.endswith((".nc", ".nc4")) and valid_hdf5_signature(fn) ) def valid_netcdf_classic_signature(filename): issue_deprecation_warning( "valid_netcdf_classic_signature is not used within yt any more and is deprecated.", since="4.3", ) signature_v1 = b"CDF\x01" signature_v2 = b"CDF\x02" try: with open(filename, "rb") as f: header = f.read(4) return header == signature_v1 or header == signature_v2 except Exception: return False def warn_netcdf(fn): # There are a few variants of the netCDF format. issue_deprecation_warning( "warn_netcdf is not used within yt any more and is deprecated.", since="4.3" ) classic = valid_netcdf_classic_signature(fn) # NetCDF-4 Classic files are HDF5 files constrained to the Classic # data model used by netCDF-3. netcdf4_classic = valid_hdf5_signature(fn) and fn.endswith((".nc", ".nc4")) needs_netcdf = classic or netcdf4_classic from yt.utilities.on_demand_imports import _netCDF4 as netCDF4 if needs_netcdf and isinstance(netCDF4.Dataset, NotAModule): raise RuntimeError( "This appears to be a netCDF file, but the " "python bindings for netCDF4 are not installed." ) class NetCDF4FileHandler: def __init__(self, filename): self.filename = filename @contextmanager def open_ds(self, **kwargs): from yt.utilities.on_demand_imports import _netCDF4 as netCDF4 ds = netCDF4.Dataset(self.filename, mode="r", **kwargs) yield ds ds.close() yt-project-yt-f043ac8/yt/utilities/flagging_methods.py000066400000000000000000000135641510711153200232050ustar00rootroot00000000000000import numpy as np # For modern purposes from yt.utilities.lib.misc_utilities import grow_flagging_field flagging_method_registry = {} class RegisteredFlaggingMethod(type): def __init__(cls, name, b, d): type.__init__(cls, name, b, d) class FlaggingMethod: _skip_add = False def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) if hasattr(cls, "_type_name") and not cls._skip_add: flagging_method_registry[cls._type_name] = cls class OverDensity(FlaggingMethod): _type_name = "overdensity" def __init__(self, over_density): self.over_density = over_density def __call__(self, grid): rho = grid["gas", "density"] / (grid.ds.refine_by**grid.Level) return rho > self.over_density class FlaggingGrid: def __init__(self, grid, methods): self.grid = grid flagged = np.zeros(grid.ActiveDimensions, dtype="bool") for method in methods: flagged |= method(self.grid) self.flagged = grow_flagging_field(flagged) self.subgrids = [] self.left_index = grid.get_global_startindex() self.dimensions = grid.ActiveDimensions.copy() def find_subgrids(self): if not np.any(self.flagged): return [] psg = ProtoSubgrid(self.flagged, self.left_index, self.dimensions) sgl = [psg] index = 0 while index < len(sgl): psg = sgl[index] psg.shrink() if psg.dimensions.prod() == 0: sgl[index] = None continue while not psg.acceptable: new_psgs = [] for dim in np.argsort(psg.dimensions)[::-1]: new_psgs = psg.find_by_zero_signature(dim) if len(new_psgs) > 1: break if len(new_psgs) <= 1: new_psgs = psg.find_by_second_derivative() psg = new_psgs[0] sgl[index] = psg sgl.extend(new_psgs[1:]) psg.shrink() index += 1 return sgl # Much or most of this is directly translated from Enzo class ProtoSubgrid: def __init__(self, flagged_base, left_index, dimensions, offset=(0, 0, 0)): self.left_index = left_index.copy() self.dimensions = dimensions.copy() self.flagged = flagged_base[ offset[0] : offset[0] + dimensions[0], offset[1] : offset[1] + dimensions[1], offset[2] : offset[2] + dimensions[2], ] self.compute_signatures() def compute_signatures(self): self.sigs = [] for dim in range(3): d1 = (dim + 1) % 3 d2 = int(dim == 0) self.sigs.append(self.flagged.sum(axis=d1).sum(axis=d2)) @property def acceptable(self): return float(self.flagged.sum()) / self.flagged.size > 0.2 def shrink(self): new_ind = [] for dim in range(3): sig = self.sigs[dim] new_start = 0 while sig[new_start] == 0: new_start += 1 new_end = sig.size while sig[new_end - 1] == 0: new_end -= 1 self.dimensions[dim] = new_end - new_start self.left_index[dim] += new_start new_ind.append((new_start, new_end)) self.flagged = self.flagged[ new_ind[0][0] : new_ind[0][1], new_ind[1][0] : new_ind[1][1], new_ind[2][0] : new_ind[2][1], ] self.compute_signatures() def find_by_zero_signature(self, dim): sig = self.sigs[dim] grid_ends = np.zeros((sig.size, 2), dtype="int64") ng = 0 i = 0 while i < sig.size: if sig[i] != 0: grid_ends[ng, 0] = i while i < sig.size and sig[i] != 0: i += 1 grid_ends[ng, 1] = i - 1 ng += 1 i += 1 new_grids = [] for si, ei in grid_ends[:ng, :]: li = self.left_index.copy() dims = self.dimensions.copy() li[dim] += si dims[dim] = ei - si offset = [0, 0, 0] offset[dim] = si new_grids.append(ProtoSubgrid(self.flagged, li, dims, offset)) return new_grids def find_by_second_derivative(self): max_strength = 0 max_axis = -1 for dim in range(3): sig = self.sigs[dim] sd = sig[:-2] - 2.0 * sig[1:-1] + sig[2:] center = int((self.flagged.shape[dim] - 1) / 2) strength = zero_strength = zero_cross = 0 for i in range(1, sig.size - 2): # Note that sd is offset by one if sd[i - 1] * sd[i] < 0: strength = np.abs(sd[i - 1] - sd[i]) # TODO this differs from what I could find in ENZO # there's |center - i| < |center - zero_cross| instead # additionally zero_cross is undefined in first pass if strength > zero_strength or ( strength == zero_strength and np.abs(center - i) < np.abs(zero_cross - i) ): zero_strength = strength zero_cross = i if zero_strength > max_strength: max_axis = dim dims = self.dimensions.copy() li = self.left_index.copy() dims[max_axis] = zero_cross psg1 = ProtoSubgrid(self.flagged, li, dims) li[max_axis] += zero_cross dims[max_axis] = self.dimensions[max_axis] - zero_cross offset = np.zeros(3) offset[max_axis] = zero_cross psg2 = ProtoSubgrid(self.flagged, li, dims, offset) return [psg1, psg2] def __str__(self): return f"LI: ({self.left_index}) DIMS: ({self.dimensions})" yt-project-yt-f043ac8/yt/utilities/fortran_utils.py000066400000000000000000000232511510711153200225710ustar00rootroot00000000000000import io import os import struct import numpy as np def read_attrs(f, attrs, endian="="): r"""This function accepts a file pointer and reads from that file pointer according to a definition of attributes, returning a dictionary. Fortran unformatted files provide total bytesize at the beginning and end of a record. By correlating the components of that record with attribute names, we construct a dictionary that gets returned. Note that this function is used for reading sequentially-written records. If you have many written that were written simultaneously, see read_record. Parameters ---------- f : File object An open file object. Should have been opened in mode rb. attrs : iterable of iterables This object should be an iterable of one of the formats: [ (attr_name, count, struct type), ... ]. [ ((name1,name2,name3),count, vector type] [ ((name1,name2,name3),count, 'type type type'] endian : str '=' is native, '>' is big, '<' is little endian Returns ------- values : dict This will return a dict of iterables of the components of the values in the file. Examples -------- >>> header = [("ncpu", 1, "i"), ("nfiles", 2, "i")] >>> f = open("fort.3", "rb") >>> rv = read_attrs(f, header) """ vv = {} net_format = endian for _a, n, t in attrs: for end in "@=<>": t = t.replace(end, "") net_format += "".join(["I"] + ([t] * n) + ["I"]) size = struct.calcsize(net_format) vals = list(struct.unpack(net_format, f.read(size))) vv = {} for a, n, t in attrs: for end in "@=<>": t = t.replace(end, "") if isinstance(a, tuple): n = len(a) s1 = vals.pop(0) v = [vals.pop(0) for i in range(n)] s2 = vals.pop(0) if s1 != s2: size = struct.calcsize(endian + "I" + "".join(n * [t]) + "I") raise OSError( "An error occurred while reading a Fortran record. " "Got a different size at the beginning and at the " "end of the record: %s %s", s1, s2, ) if n == 1: v = v[0] if isinstance(a, tuple): if len(a) != len(v): raise OSError( "An error occurred while reading a Fortran " "record. Record length is not equal to expected " f"length: {len(a)} {len(v)}" ) for k, val in zip(a, v, strict=True): vv[k] = val else: vv[a] = v return vv def read_cattrs(f, attrs, endian="="): r"""This function accepts a file pointer to a C-binary file and reads from that file pointer according to a definition of attributes, returning a dictionary. This function performs very similarly to read_attrs, except it does not add on any record padding. It is thus useful for using the same header types as in read_attrs, but for C files rather than Fortran. Parameters ---------- f : File object An open file object. Should have been opened in mode rb. attrs : iterable of iterables This object should be an iterable of one of the formats: [ (attr_name, count, struct type), ... ]. [ ((name1,name2,name3),count, vector type] [ ((name1,name2,name3),count, 'type type type'] endian : str '=' is native, '>' is big, '<' is little endian Returns ------- values : dict This will return a dict of iterables of the components of the values in the file. Examples -------- >>> header = [("ncpu", 1, "i"), ("nfiles", 2, "i")] >>> f = open("cdata.bin", "rb") >>> rv = read_cattrs(f, header) """ vv = {} net_format = endian for _a, n, t in attrs: for end in "@=<>": t = t.replace(end, "") net_format += "".join([t] * n) size = struct.calcsize(net_format) vals = list(struct.unpack(net_format, f.read(size))) vv = {} for a, n, t in attrs: for end in "@=<>": t = t.replace(end, "") if isinstance(a, tuple): n = len(a) v = [vals.pop(0) for i in range(n)] if n == 1: v = v[0] if isinstance(a, tuple): if len(a) != len(v): raise OSError( "An error occurred while reading a Fortran " "record. Record length is not equal to expected " f"length: {len(a)} {len(v)}" ) for k, val in zip(a, v, strict=True): vv[k] = val else: vv[a] = v return vv def read_vector(f, d, endian="="): r"""This function accepts a file pointer and reads from that file pointer a vector of values. Parameters ---------- f : File object An open file object. Should have been opened in mode rb. d : data type This is the datatype (from the struct module) that we should read. endian : str '=' is native, '>' is big, '<' is little endian Returns ------- tr : numpy.ndarray This is the vector of values read from the file. Examples -------- >>> f = open("fort.3", "rb") >>> rv = read_vector(f, "d") """ pad_fmt = f"{endian}I" pad_size = struct.calcsize(pad_fmt) vec_len = struct.unpack(pad_fmt, f.read(pad_size))[0] # bytes vec_fmt = f"{endian}{d}" vec_size = struct.calcsize(vec_fmt) if vec_len % vec_size != 0: raise OSError( "An error occurred while reading a Fortran record. " "Vector length is not compatible with data type: %s %s", vec_len, vec_size, ) vec_num = int(vec_len / vec_size) if isinstance(f, io.IOBase): tr = np.frombuffer(f.read(vec_len), vec_fmt, count=vec_num) else: tr = np.frombuffer(f, vec_fmt, count=vec_num) vec_len2 = struct.unpack(pad_fmt, f.read(pad_size))[0] if vec_len != vec_len2: raise OSError( "An error occurred while reading a Fortran record. " "Got a different size at the beginning and at the " "end of the record: %s %s", vec_len, vec_len2, ) return tr def skip(f, n=1, endian="="): r"""This function accepts a file pointer and skips a Fortran unformatted record. Optionally check that the skip was done correctly by checking the pad bytes. Parameters ---------- f : File object An open file object. Should have been opened in mode rb. n : int Number of records to skip. endian : str '=' is native, '>' is big, '<' is little endian Returns ------- skipped: The number of elements in the skipped array Examples -------- >>> f = open("fort.3", "rb") >>> skip(f, 3) """ skipped = np.zeros(n, dtype=np.int32) fmt = endian + "I" fmt_size = struct.calcsize(fmt) for i in range(n): size = f.read(fmt_size) s1 = struct.unpack(fmt, size)[0] f.seek(s1 + fmt_size, os.SEEK_CUR) s2 = struct.unpack(fmt, size)[0] if s1 != s2: raise OSError( "An error occurred while reading a Fortran record. " "Got a different size at the beginning and at the " "end of the record: %s %s", s1, s2, ) skipped[i] = s1 / fmt_size return skipped def peek_record_size(f, endian="="): r"""This function accept the file handle and returns the size of the next record and then rewinds the file to the previous position. Parameters ---------- f : File object An open file object. Should have been opened in mode rb. endian : str '=' is native, '>' is big, '<' is little endian Returns ------- Number of bytes in the next record """ pos = f.tell() s = struct.unpack(">i", f.read(struct.calcsize(">i"))) f.seek(pos) return s[0] def read_record(f, rspec, endian="="): r"""This function accepts a file pointer and reads from that file pointer a single "record" with different components. Fortran unformatted files provide total bytesize at the beginning and end of a record. By correlating the components of that record with attribute names, we construct a dictionary that gets returned. Parameters ---------- f : File object An open file object. Should have been opened in mode rb. rspec : iterable of iterables This object should be an iterable of the format [ (attr_name, count, struct type), ... ]. endian : str '=' is native, '>' is big, '<' is little endian Returns ------- values : dict This will return a dict of iterables of the components of the values in the file. Examples -------- >>> header = [("ncpu", 1, "i"), ("nfiles", 2, "i")] >>> f = open("fort.3", "rb") >>> rv = read_record(f, header) """ vv = {} net_format = endian + "I" for _a, n, t in rspec: t = t if len(t) == 1 else t[-1] net_format += f"{n}{t}" net_format += "I" size = struct.calcsize(net_format) vals = list(struct.unpack(net_format, f.read(size))) s1, s2 = vals.pop(0), vals.pop(-1) if s1 != s2: raise OSError( "An error occurred while reading a Fortran record. Got " "a different size at the beginning and at the end of " "the record: %s %s", s1, s2, ) pos = 0 for a, n, _t in rspec: vv[a] = vals[pos : pos + n] pos += n return vv yt-project-yt-f043ac8/yt/utilities/grid_data_format/000077500000000000000000000000001510711153200226075ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/grid_data_format/__init__.py000066400000000000000000000000321510711153200247130ustar00rootroot00000000000000from .conversion import * yt-project-yt-f043ac8/yt/utilities/grid_data_format/conversion/000077500000000000000000000000001510711153200247745ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/grid_data_format/conversion/__init__.py000066400000000000000000000001611510711153200271030ustar00rootroot00000000000000from .conversion_abc import Converter from .conversion_athena import AthenaConverter, AthenaDistributedConverter yt-project-yt-f043ac8/yt/utilities/grid_data_format/conversion/conversion_abc.py000066400000000000000000000002461510711153200303420ustar00rootroot00000000000000class Converter: def __init__(self, basename, outname=None): self.basename = basename self.outname = outname def convert(self): pass yt-project-yt-f043ac8/yt/utilities/grid_data_format/conversion/conversion_athena.py000066400000000000000000000464011510711153200310600ustar00rootroot00000000000000import os from glob import glob import numpy as np from yt.utilities.grid_data_format.conversion.conversion_abc import Converter from yt.utilities.on_demand_imports import _h5py as h5py translation_dict = {} translation_dict["density"] = "density" translation_dict["total_energy"] = "specific_energy" translation_dict["velocity_x"] = "velocity_x" translation_dict["velocity_y"] = "velocity_y" translation_dict["velocity_z"] = "velocity_z" translation_dict["cell_centered_B_x"] = "mag_field_x" translation_dict["cell_centered_B_y"] = "mag_field_y" translation_dict["cell_centered_B_z"] = "mag_field_z" class AthenaDistributedConverter(Converter): def __init__(self, basename, outname=None, source_dir=None, field_conversions=None): self.fields = [] self.current_time = 0.0 name = basename.split(".") self.ddn = int(name[1]) if source_dir is None: source_dir = "./" self.source_dir = source_dir self.basename = name[0] if outname is None: outname = self.basename + ".%04i" % self.ddn + ".gdf" self.outname = outname if field_conversions is None: field_conversions = {} self.field_conversions = field_conversions self.handle = None def parse_line(self, line, grid): # grid is a dictionary splitup = line.strip().split() if "vtk" in splitup: grid["vtk_version"] = splitup[-1] elif "Really" in splitup: grid["time"] = splitup[-1] self.current_time = grid["time"] elif "PRIMITIVE" in splitup: grid["time"] = float(splitup[4].rstrip(",")) grid["level"] = int(splitup[6].rstrip(",")) grid["domain"] = int(splitup[8].rstrip(",")) self.current_time = grid["time"] elif "DIMENSIONS" in splitup: grid["dimensions"] = np.array(splitup[-3:], dtype="int64") elif "ORIGIN" in splitup: grid["left_edge"] = np.array(splitup[-3:], dtype="float64") elif "SPACING" in splitup: grid["dds"] = np.array(splitup[-3:], dtype="float64") elif "CELL_DATA" in splitup: grid["ncells"] = int(splitup[-1]) elif "SCALARS" in splitup: field = splitup[1] grid["read_field"] = field grid["read_type"] = "scalar" elif "VECTORS" in splitup: field = splitup[1] grid["read_field"] = field grid["read_type"] = "vector" def write_gdf_field(self, fn, grid_number, field, data): f = self.handle ## --------- Store Grid Data --------- ## if "grid_%010i" % grid_number not in f["data"].keys(): g = f["data"].create_group("grid_%010i" % grid_number) else: g = f["data"]["grid_%010i" % grid_number] name = field try: name = translation_dict[name] except Exception: pass # print 'Writing %s' % name if name not in g.keys(): g.create_dataset(name, data=data) def read_and_write_index(self, basename, ddn, gdf_name): """Read Athena legacy vtk file from multiple cpus""" proc_names = glob(os.path.join(self.source_dir, "id*")) # print('Reading a dataset from %i Processor Files' % len(proc_names)) N = len(proc_names) grid_dims = np.empty([N, 3], dtype="int64") grid_left_edges = np.empty([N, 3], dtype="float64") grid_dds = np.empty([N, 3], dtype="float64") grid_levels = np.zeros(N, dtype="int64") grid_parent_ids = -1 * np.ones(N, dtype="int64") grid_particle_counts = np.zeros([N, 1], dtype="int64") for i in range(N): if i == 0: fn = os.path.join( self.source_dir, f"id{i}", basename + f".{ddn:04d}.vtk" ) else: fn = os.path.join( self.source_dir, f"id{i}", basename + f"-id{i}.{ddn:04d}.vtk" ) print(f"Reading file {fn}") f = open(fn, "rb") grid = {} grid["read_field"] = None grid["read_type"] = None line = f.readline() while grid["read_field"] is None: self.parse_line(line, grid) if "SCALAR" in line.strip().split(): break if "VECTOR" in line.strip().split(): break if "TABLE" in line.strip().split(): break if len(line) == 0: break del line line = f.readline() if len(line) == 0: break if np.prod(grid["dimensions"]) != grid["ncells"]: grid["dimensions"] -= 1 grid["dimensions"][grid["dimensions"] == 0] = 1 if np.prod(grid["dimensions"]) != grid["ncells"]: print( "product of dimensions %i not equal to number of cells %i" % (np.prod(grid["dimensions"]), grid["ncells"]) ) raise TypeError # Append all hierarchy info before reading this grid's data grid_dims[i] = grid["dimensions"] grid_left_edges[i] = grid["left_edge"] grid_dds[i] = grid["dds"] # grid_ncells[i]=grid['ncells'] del grid f.close() del f f = self.handle ## --------- Begin level nodes --------- ## g = f.create_group("gridded_data_format") g.attrs["format_version"] = np.float32(1.0) g.attrs["data_software"] = "athena" f.create_group("data") f.create_group("field_types") f.create_group("particle_types") pars_g = f.create_group("simulation_parameters") gles = grid_left_edges gdims = grid_dims dle = np.min(gles, axis=0) dre = np.max(gles + grid_dims * grid_dds, axis=0) glis = ((gles - dle) / grid_dds).astype("int64") ddims = (dre - dle) / grid_dds[0] # grid_left_index f.create_dataset("grid_left_index", data=glis) # grid_dimensions f.create_dataset("grid_dimensions", data=gdims) # grid_level f.create_dataset("grid_level", data=grid_levels) ## ----------QUESTIONABLE NEXT LINE--------- ## # This data needs two dimensions for now. f.create_dataset("grid_particle_count", data=grid_particle_counts) # grid_parent_id f.create_dataset("grid_parent_id", data=grid_parent_ids) ## --------- Done with top level nodes --------- ## pars_g.attrs["refine_by"] = np.int64(1) pars_g.attrs["dimensionality"] = np.int64(3) pars_g.attrs["domain_dimensions"] = ddims pars_g.attrs["current_time"] = self.current_time pars_g.attrs["domain_left_edge"] = dle pars_g.attrs["domain_right_edge"] = dre pars_g.attrs["unique_identifier"] = "athenatest" pars_g.attrs["cosmological_simulation"] = np.int64(0) pars_g.attrs["num_ghost_zones"] = np.int64(0) pars_g.attrs["field_ordering"] = np.int64(1) pars_g.attrs["boundary_conditions"] = np.int64([0] * 6) # For Now # Extra pars: # pars_g.attrs['n_cells'] = grid['ncells'] pars_g.attrs["vtk_version"] = 1.0 # Add particle types # Nothing to do here # Add particle field attributes # f.close() def read_and_write_data(self, basename, ddn, gdf_name): proc_names = glob(os.path.join(self.source_dir, "id*")) # print('Reading a dataset from %i Processor Files' % len(proc_names)) N = len(proc_names) for i in range(N): if i == 0: fn = os.path.join( self.source_dir, f"id{i}", basename + f".{ddn:04d}.vtk" ) else: fn = os.path.join( self.source_dir, +f"id{i}", basename + f"-id{i}.{ddn:04d}.vtk" ) f = open(fn, "rb") # print('Reading data from %s' % fn) line = f.readline() while line != "": if len(line) == 0: break splitup = line.strip().split() if "DIMENSIONS" in splitup: grid_dims = np.array(splitup[-3:], dtype="int64") line = f.readline() continue elif "CELL_DATA" in splitup: grid_ncells = int(splitup[-1]) line = f.readline() if np.prod(grid_dims) != grid_ncells: grid_dims -= 1 grid_dims[grid_dims == 0] = 1 if np.prod(grid_dims) != grid_ncells: print( "product of dimensions %i not equal to number of cells %i" % (np.prod(grid_dims), grid_ncells) ) raise TypeError break else: del line line = f.readline() read_table = False while line != "": if len(line) == 0: break splitup = line.strip().split() if "SCALARS" in splitup: field = splitup[1] if not read_table: line = f.readline() # Read the lookup table line read_table = True data = np.fromfile(f, dtype=">f4", count=grid_ncells).reshape( grid_dims, order="F" ) if i == 0: self.fields.append(field) # print('writing field %s' % field) self.write_gdf_field(gdf_name, i, field, data) read_table = False elif "VECTORS" in splitup: field = splitup[1] data = np.fromfile(f, dtype=">f4", count=3 * grid_ncells) data_x = data[0::3].reshape(grid_dims, order="F") data_y = data[1::3].reshape(grid_dims, order="F") data_z = data[2::3].reshape(grid_dims, order="F") if i == 0: self.fields.append(field + "_x") self.fields.append(field + "_y") self.fields.append(field + "_z") # print('writing field %s' % field) self.write_gdf_field(gdf_name, i, field + "_x", data_x) self.write_gdf_field(gdf_name, i, field + "_y", data_y) self.write_gdf_field(gdf_name, i, field + "_z", data_z) del data, data_x, data_y, data_z del line line = f.readline() f.close() del f f = self.handle field_g = f["field_types"] # Add Field Attributes for name in self.fields: tname = name try: tname = translation_dict[name] except Exception: pass this_field = field_g.create_group(tname) if name in self.field_conversions.keys(): this_field.attrs["field_to_cgs"] = self.field_conversions[name] else: this_field.attrs["field_to_cgs"] = np.float64("1.0") # For Now def convert(self, index=True, data=True): self.handle = h5py.File(self.outname, mode="a") if index: self.read_and_write_index(self.basename, self.ddn, self.outname) if data: self.read_and_write_data(self.basename, self.ddn, self.outname) self.handle.close() class AthenaConverter(Converter): def __init__(self, basename, outname=None, field_conversions=None): self.fields = [] self.basename = basename name = basename.split(".") fn = "%s.%04i" % (name[0], int(name[1])) self.ddn = int(name[1]) self.basename = fn if outname is None: outname = fn + ".gdf" self.outname = outname if field_conversions is None: field_conversions = {} self.field_conversions = field_conversions def parse_line(self, line, grid): # grid is a dictionary splitup = line.strip().split() if "vtk" in splitup: grid["vtk_version"] = splitup[-1] elif "Really" in splitup: grid["time"] = splitup[-1] elif "DIMENSIONS" in splitup: grid["dimensions"] = np.array(splitup[-3:], dtype="int64") elif "ORIGIN" in splitup: grid["left_edge"] = np.array(splitup[-3:], dtype="float64") elif "SPACING" in splitup: grid["dds"] = np.array(splitup[-3:], dtype="float64") elif "CELL_DATA" in splitup: grid["ncells"] = int(splitup[-1]) elif "SCALARS" in splitup: field = splitup[1] grid["read_field"] = field grid["read_type"] = "scalar" elif "VECTORS" in splitup: field = splitup[1] grid["read_field"] = field grid["read_type"] = "vector" def read_grid(self, filename): """Read Athena legacy vtk file from single cpu""" f = open(filename, "rb") # print('Reading from %s'%filename) grid = {} grid["read_field"] = None grid["read_type"] = None table_read = False line = f.readline() while line != "": while grid["read_field"] is None: self.parse_line(line, grid) if grid["read_type"] == "vector": break if not table_read: line = f.readline() if "TABLE" in line.strip().split(): table_read = True if len(line) == 0: break if len(line) == 0: break if np.prod(grid["dimensions"]) != grid["ncells"]: grid["dimensions"] -= 1 if np.prod(grid["dimensions"]) != grid["ncells"]: print( "product of dimensions %i not equal to number of cells %i" % (np.prod(grid["dimensions"]), grid["ncells"]) ) raise TypeError if grid["read_type"] == "scalar": grid[grid["read_field"]] = np.fromfile( f, dtype=">f4", count=grid["ncells"] ).reshape(grid["dimensions"], order="F") self.fields.append(grid["read_field"]) elif grid["read_type"] == "vector": data = np.fromfile(f, dtype=">f4", count=3 * grid["ncells"]) grid[grid["read_field"] + "_x"] = data[0::3].reshape( grid["dimensions"], order="F" ) grid[grid["read_field"] + "_y"] = data[1::3].reshape( grid["dimensions"], order="F" ) grid[grid["read_field"] + "_z"] = data[2::3].reshape( grid["dimensions"], order="F" ) self.fields.append(grid["read_field"] + "_x") self.fields.append(grid["read_field"] + "_y") self.fields.append(grid["read_field"] + "_z") else: raise TypeError grid["read_field"] = None grid["read_type"] = None line = f.readline() if len(line) == 0: break grid["right_edge"] = grid["left_edge"] + grid["dds"] * (grid["dimensions"]) return grid def write_to_gdf(self, fn, grid): f = h5py.File(fn, mode="a") ## --------- Begin level nodes --------- ## g = f.create_group("gridded_data_format") g.attrs["format_version"] = np.float32(1.0) g.attrs["data_software"] = "athena" data_g = f.create_group("data") field_g = f.create_group("field_types") f.create_group("particle_types") pars_g = f.create_group("simulation_parameters") dle = grid["left_edge"] # True only in this case of one grid for the domain gles = np.array([grid["left_edge"]]) gdims = np.array([grid["dimensions"]]) glis = ((gles - dle) / grid["dds"]).astype("int64") # grid_left_index f.create_dataset("grid_left_index", data=glis) # grid_dimensions f.create_dataset("grid_dimensions", data=gdims) levels = np.array([0], dtype="int64") # unigrid example # grid_level f.create_dataset("grid_level", data=levels) ## ----------QUESTIONABLE NEXT LINE--------- ## # This data needs two dimensions for now. n_particles = np.array([[0]], dtype="int64") # grid_particle_count f.create_dataset("grid_particle_count", data=n_particles) # Assume -1 means no parent. parent_ids = np.array([-1], dtype="int64") # grid_parent_id f.create_dataset("grid_parent_id", data=parent_ids) ## --------- Done with top level nodes --------- ## f.create_group("index") ## --------- Store Grid Data --------- ## g0 = data_g.create_group("grid_%010i" % 0) for field in self.fields: name = field if field in translation_dict.keys(): name = translation_dict[name] if name not in g0.keys(): g0.create_dataset(name, data=grid[field]) ## --------- Store Particle Data --------- ## # Nothing to do ## --------- Attribute Tables --------- ## pars_g.attrs["refine_by"] = np.int64(1) pars_g.attrs["dimensionality"] = np.int64(3) pars_g.attrs["domain_dimensions"] = grid["dimensions"] try: pars_g.attrs["current_time"] = grid["time"] except Exception: pars_g.attrs["current_time"] = 0.0 pars_g.attrs["domain_left_edge"] = grid["left_edge"] # For Now pars_g.attrs["domain_right_edge"] = grid["right_edge"] # For Now pars_g.attrs["unique_identifier"] = "athenatest" pars_g.attrs["cosmological_simulation"] = np.int64(0) pars_g.attrs["num_ghost_zones"] = np.int64(0) pars_g.attrs["field_ordering"] = np.int64(0) pars_g.attrs["boundary_conditions"] = np.int64([0] * 6) # For Now # Extra pars: pars_g.attrs["n_cells"] = grid["ncells"] pars_g.attrs["vtk_version"] = grid["vtk_version"] # Add Field Attributes for name in g0.keys(): tname = name try: tname = translation_dict[name] except Exception: pass this_field = field_g.create_group(tname) if name in self.field_conversions.keys(): this_field.attrs["field_to_cgs"] = self.field_conversions[name] else: this_field.attrs["field_to_cgs"] = np.float64("1.0") # For Now # Add particle types # Nothing to do here # Add particle field attributes f.close() def convert(self): grid = self.read_grid(self.basename + ".vtk") self.write_to_gdf(self.outname, grid) # import sys # if __name__ == '__main__': # n = sys.argv[-1] # n = n.split('.') # fn = '%s.%04i'%(n[0],int(n[1])) # grid = read_grid(fn+'.vtk') # write_to_hdf5(fn+'.gdf',grid) yt-project-yt-f043ac8/yt/utilities/grid_data_format/docs/000077500000000000000000000000001510711153200235375ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/grid_data_format/docs/IRATE_notes.txt000066400000000000000000000035051510711153200263570ustar00rootroot00000000000000Here is info from Erik Tollerud about the IRATE data format. The bitbucket project is at https://bitbucket.org/eteq/irate-format and I've posted a copy of the docs at http://www.physics.uci.edu/~etolleru/irate-docs/ , in particular http://www.physics.uci.edu/~etolleru/irate-docs/formatspec.html , which details the actual requirements for data to fit in the format. As far as I can tell, the following steps are needed to make GDF fit inside the IRATE format: *move everything except "/simulation_parameters" into a group named "/GridData" *rename "/simulation_parameters" to "SimulationParameters" *remove the 'field_types' group (this is not absolutely necessary, but the convention we had in mind for IRATE is that the dataset names themselves (e.g. the datasets like /data/gridxxxxxx/density) serve as the field definitions. * The unit information that's in 'field_types' should then be attributes in either "/GridData" or "/GridData/data" following the naming scheme e.g. "densityunitcgs" following the unit form given in the IRATE doc and an additional attribute e.g. "densityunitname" should be added with the human-readable name of the unit. This unit information can also live at the dataset level, but it probably makes more sense to put it instead at the higher level (IRATE supports both ways of doing it) * The Cosmology group (as defined in the IRATE specification) must be added - for simulations that are not technically "cosmological", you can just use one of the default cosmologies (WMAP7 is a reasonable choice - there's a function in the IRATE tools that automatically takes care of all the details for this). * optional: redo all the group names to follow the CamelCase convention - that's what we've been using elsewhere in IRATE. This is an arbitrary choice, but it would be nice for it to be consistent throughout the format. yt-project-yt-f043ac8/yt/utilities/grid_data_format/docs/gdf_specification.txt000066400000000000000000000255261510711153200277520ustar00rootroot00000000000000Gridded Data Format =================== This is a pre-release of version 1.0 of this format. Lots of formats have come before, but this one is simple and will work with yt; the idea is to create an import and export function in yt that will read this, so that other codes (such as ZEUS-MP) can export directly to it or convert their data to it, and so that yt can export to it from any format it recognizes and reads. Caveats and Notes ----------------- #. We avoid having many attributes on many nodes, as access can be quite slow #. Cartesian data only for now #. All grids must have the same number of ghost zones. #. If "/grid_parent" does not exist, parentage relationships will be reconstructed and assumed to allow multiple grids #. No parentage can skip levels #. All grids are at the same time #. This format is designed for single-fluid calculations (with color fields) but it should be viewed as extensible to multiple-fluids. #. All fluid quantities are assumed to be in every grid, filling every zone. Inside a given grid, for a given particle type, all the affiliated fields must be the same length. (i.e., dark matter's velocity must be the same in all dimensions.) #. Everything is in a single file; for extremely large datasets, the user may utilize HDF5 external links to link to files other than the primary. (This enables, for instance, Enzo datasets to have only a thin wrapper that creates this format.) #. All fluid fields in this version of the format are assumed to have the dimensionality of the grid they reside in plus any ghost zones, plus any additionally dimensionality required by the staggering property. #. Particles may have dataspaces affiliated with them. (See Enzo's OutputParticleTypeGrouping for more information.) This enables a light wrapper around data formats with interspersed particle types. #. Boundary conditions are very simply specified -- future revisions will feature more complicated and rich specifications for the boundary. Furthermore, we make a distinction between fluid quantities and particle quantities. Particles remain affiliated with grid nodes. Positions of particles are global, but this will change with future versions of this document. Format Declaration ------------------ The file type is HDF5. We require version 1.8 or greater. At the root level, this group must exist: :: /gridded_data_format This must contain the (float) attribute ``format_version``. This document describes version 1.0. Optional attributes may exist: ``data_software`` string, references the application creating the file, not the author of the data ``data_software_version`` string, should reference a unique version number ``data_author`` string, references the person or persons who created the data, should include an email address ``data_comment`` string, anything about the data Top Level Nodes --------------- At least five top-level groups must exist, although some may be empty. :: /gridded_data_format /data /simulation_parameters /field_types /particle_types Additionally, the grid structure elements must exist. The 0-indexed index into this array defines a unique "Grid ID". ``/grid_left_index`` (int64, Nx3): global, relative to current level, and only the active region ``/grid_dimensions`` (int64, Nx3): only the active regions ``/grid_level`` (int64, N): level, indexed by zero ``/grid_particle_count`` (int64, N): total number of particles. (May change in subsequent versions.) ``/grid_parent_id`` (int64, N): optional, may only reference a single parent Grid Fields ----------- Underneath ``/data/`` there must be entries for every grid, of the format ``/data/grid_%010i``. These grids need no attributes, and underneath them datasets live. Fluid Fields ++++++++++++ For every grid we then define ``/data/grid_%010i/%(field)s``. Where ``%(field)s`` draws from all of the fields defined. We define no standard for which fields must be present, only the names and units. Units should always be ''proper'' cgs (or conversion factors should be supplied, below), and field names should be drawn from this list, with these names. Not all fields must be represented. Field must extend beyond the active region if ghost zones are included. All pre-defined fields are assumed to be cell-centered unless this is overridden in ``field_types``. * ``density`` (g/cc) * ``temperature`` (K) * ``specific_thermal_energy`` (erg/g) * ``specific_energy`` (erg/g, includes kinetic and magnetic) * ``magnetic_energy`` (erg/g) * ``velocity_x`` (cm/s) * ``velocity_y`` (cm/s) * ``velocity_z`` (cm/s) * ``species_density_%s`` (g/cc) where %s is the species name including ionization state, such as H2I, HI, HII, CO, "elec" for electron * ``mag_field_x`` * ``mag_field_y`` * ``mag_field_z`` Particle Fields +++++++++++++++ Particles are more expensive to sort and identify based on "type" -- for instance, dark matter versus star particles. The particles should be separated based on type, under the group ``/data/grid_%010i/particles/``. The particles group will have sub-groups, each of which will be named after the type of particle it represents. We only specify "dark_matter" as a type; anything else must be specified as described below. Each node, for instance ``/data/grid_%010i/particles/dark_matter/``, must contain the following fields: * ``mass`` (g) * ``id`` * ``position_x`` (in physical units) * ``position_y`` (in physical units) * ``position_z`` (in physical units) * ``velocity_x`` (cm/s) * ``velocity_y`` (cm/s) * ``velocity_z`` (cm/s) * ``dataspace`` (optional) an HDF5 dataspace to be used when opening all affiliated fields. If this is to be used, it must be appropriately set in the particle type definition. This is of type ``H5T_STD_REF_DSETREG``. (See Enzo's OutputParticleTypeGrouping for an example.) Additional Fields +++++++++++++++++ Any additional fields from the data can be added, but must have a corresponding entry in the root field table (described below.) The naming scheme is to be as explicit as possible, with units in cgs (or a conversion factor to the standard cgs unit, in the field table.) Attribute Table --------------- In the root node, we define several groups which contain attributes. Simulation Parameters +++++++++++++++++++++ These attributes will all be associated with ``/simulation_parameters``. ``refine_by`` relative global refinement ``dimensionality`` 1-, 2- or 3-D data ``domain_dimensions`` dimensions in the top grid ``current_time`` current time in simulation, in seconds, from "start" of simulation ``domain_left_edge`` the left edge of the domain, in cm ``domain_right_edge`` the right edge of the domain, in cm ``unique_identifier`` regarded as a string, but can be anything ``cosmological_simulation`` 0 or 1 ``num_ghost_zones`` integer ``field_ordering`` integer: 0 for C, 1 for Fortran ``boundary_conditions`` integer (6): 0 for periodic, 1 for mirrored, 2 for outflow. Needs one for each face of the cube. Any past the dimensionality should be set to -1. The order of specification goes left in 0th dimension, right in 0th dimension, left in 1st dimension, right in 1st dimensions, left in 2nd dimension, right in 2nd dimension. Note also that yt does not currently support non-periodic boundary conditions, and that the assumption of periodicity shows up primarily in plots and covering grids. Optionally, attributes for cosmological simulations can be provided, if cosmological_simulation above is set to 1: * current_redshift * omega_matter (at z=0) * omega_lambda (at z=0) * hubble_constant (h100) Fluid Field Attributes ++++++++++++++++++++++ Every field that is included that is not both in CGS already and in the list above requires parameters. If a field is in the above list but is not in CGS, only the field_to_cgs attribute is necessary. These will be stored under ``/field_types`` and each must possess the following attributes: ``field_name`` a string that will be used to describe the field; can contain spaces. ``field_to_cgs`` a float that will be used to convert the field to cgs units, if necessary. Set to 1.0 if no conversion necessary. Note that if non-CGS units are desired this field should simply be viewed as the value by which field values are multiplied to get to some internally consistent unit system. ``field_units`` a string that names the units. ``staggering`` an integer: 0 for cell-centered, 1 for face-centered, 2 for vertex-centered. Non-cellcentered data will be linearly-interpolated; more complicated reconstruction will be defined in a future version of this standard; for 1.0 we only allow for simple definitions. Particle Types ++++++++++++++ Every particle type that is not recognized (i.e., all non-Dark Matter types) needs to have an entry under ``/particle_types``. Each entry must possess the following attributes: ``particle_type_name`` a string that will be used to describe the field; can contain spaces. ``particle_use_dataspace`` (optional) if 1, the dataspace (see particle field definition above) will be used for all particle fields for this type of particle. Useful if a given type of particle is embedded inside a larger list of different types of particle. ``particle_type_num`` an integer giving the total number of particles of this type. For instance, to define a particle of type ``accreting_black_hole``, the file must contain ``/particle_types/accreting_black_hole``, with the ``particle_type_name`` attribute of "Accreting Black Hole". Particle Field Attributes +++++++++++++++++++++++++ Every particle type that contains a new field (for instance, ``accretion_rate``) needs to have an entry under ``/particle_types/{particle_type_name}/{field_name}`` containing the following attributes: ``field_name`` a string that will be used to describe the field; can contain spaces. ``field_to_cgs`` a float that will be used to convert the field to cgs units, if necessary. Set to 1.0 if no conversion necessary. ``field_units`` a string that names the units. Role of YT ---------- yt will provide a reader for this data, so that any data in this format can be used by the code. Additionally, the names and specifications in this code reflect the internal yt data structures. yt will also provide a writer for this data, which will operate on any existing data format. Provided that a simulation code can read this data, this will enable cross-platform comparison. Furthermore, any external piece of software (i.e., Stranger) that implements reading this format will be able to read any format of data that yt understands. Example File ------------ An example file constructed from the ``RD0005-mine`` dataset is available at http://yt.enzotools.org/files/RD0005.gdf . It is not yet a complete conversion, but it is a working proof of concept. Readers and writers are forthcoming. yt-project-yt-f043ac8/yt/utilities/grid_data_format/scripts/000077500000000000000000000000001510711153200242765ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/grid_data_format/scripts/convert_distributed_athena.py000066400000000000000000000014041510711153200322510ustar00rootroot00000000000000import sys from grid_data_format import AthenaDistributedConverter # Assumes that last input is the basename for the athena dataset. # i.e. kh_3d_mhd_hlld_128_beta5000_sub_tanhd.0030 basename = sys.argv[-1] converter = AthenaDistributedConverter(basename) converter.convert() # If you have units information, set up a conversion dictionary for # each field. Each key is the name of the field that Athena uses. # Each value is what you have to multiply the raw output from Athena # by to get cgs units. # code_to_cgs = {'density':1.0e3, # 'total_energy':1.0e-3, # 'velocity_x':1.2345, # 'velocity_y':1.2345, # 'velocity_z':1.2345} # converter = AthenaDistributedConverter(basename, field_conversions=code_to_cgs) # converter.convert() yt-project-yt-f043ac8/yt/utilities/grid_data_format/scripts/convert_single_athena.py000066400000000000000000000013561510711153200312160ustar00rootroot00000000000000import sys from grid_data_format import AthenaConverter # Assumes that last input is the basename for the athena dataset. # i.e. kh_3d_mhd_hlld_128_beta5000_sub_tanhd.0030 basename = sys.argv[-1] converter = AthenaConverter(basename) converter.convert() # If you have units information, set up a conversion dictionary for # each field. Each key is the name of the field that Athena uses. # Each value is what you have to multiply the raw output from Athena # by to get cgs units. # code_to_cgs = {'density':1.0e3, # 'total_energy':1.0e-3, # 'velocity_x':1.2345, # 'velocity_y':1.2345, # 'velocity_z':1.2345} # converter = AthenaDistributedConverter(basename, field_conversions=code_to_cgs) # converter.convert() yt-project-yt-f043ac8/yt/utilities/grid_data_format/tests/000077500000000000000000000000001510711153200237515ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/grid_data_format/tests/__init__.py000066400000000000000000000000001510711153200260500ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/grid_data_format/tests/test_writer.py000066400000000000000000000023021510711153200266730ustar00rootroot00000000000000import os import shutil import tempfile from numpy.testing import assert_equal from yt.frontends.gdf.data_structures import GDFDataset from yt.loaders import load from yt.testing import fake_random_ds, requires_module from yt.utilities.grid_data_format.writer import write_to_gdf from yt.utilities.on_demand_imports import _h5py as h5py TEST_AUTHOR = "yt test runner" TEST_COMMENT = "Testing write_to_gdf" def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True @requires_module("h5py") def test_write_gdf(): """Main test suite for write_gdf""" tmpdir = tempfile.mkdtemp() tmpfile = os.path.join(tmpdir, "test_gdf.h5") try: test_ds = fake_random_ds(64) write_to_gdf( test_ds, tmpfile, data_author=TEST_AUTHOR, data_comment=TEST_COMMENT ) del test_ds assert isinstance(load(tmpfile), GDFDataset) h5f = h5py.File(tmpfile, mode="r") gdf = h5f["gridded_data_format"].attrs assert_equal(gdf["data_author"], TEST_AUTHOR) assert_equal(gdf["data_comment"], TEST_COMMENT) h5f.close() finally: shutil.rmtree(tmpdir) yt-project-yt-f043ac8/yt/utilities/grid_data_format/writer.py000066400000000000000000000270641510711153200245060ustar00rootroot00000000000000import os import sys from contextlib import contextmanager import numpy as np from yt import __version__ as yt_version from yt.funcs import iter_fields from yt.utilities.exceptions import YTGDFAlreadyExists from yt.utilities.on_demand_imports import _h5py as h5py from yt.utilities.parallel_tools.parallel_analysis_interface import ( communication_system, parallel_objects, ) def write_to_gdf( ds, gdf_path, fields=None, data_author=None, data_comment=None, dataset_units=None, particle_type_name="dark_matter", overwrite=False, **kwargs, ): """ Write a dataset to the given path in the Grid Data Format. Parameters ---------- ds : Dataset object The yt data to write out. gdf_path : string The path of the file to output. fields The field or list of fields to write out. If None, defaults to ds.field_list. data_author : string, optional The name of the author who wrote the data. Default: None. data_comment : string, optional A descriptive comment. Default: None. dataset_units : dictionary, optional A dictionary of (value, unit) tuples to set the default units of the dataset. Keys can be: * "length_unit" * "time_unit" * "mass_unit" * "velocity_unit" * "magnetic_unit" If not specified, these will carry over from the parent dataset. particle_type_name : string, optional The particle type of the particles in the dataset. Default: "dark_matter" overwrite : boolean, optional Whether or not to overwrite an already existing file. If False, attempting to overwrite an existing file will result in an exception. Examples -------- >>> dataset_units = {"length_unit": (1.0, "Mpc"), "time_unit": (1.0, "Myr")} >>> write_to_gdf( ... ds, ... "clumps.h5", ... data_author="John ZuHone", ... dataset_units=dataset_units, ... data_comment="My Really Cool Dataset", ... overwrite=True, ... ) """ if fields is None: fields = ds.field_list fields = list(iter_fields(fields)) with _create_new_gdf( ds, gdf_path, data_author, data_comment, dataset_units=dataset_units, particle_type_name=particle_type_name, overwrite=overwrite, ) as f: # now add the fields one-by-one _write_fields_to_gdf(ds, f, fields, particle_type_name) def save_field(ds, fields, field_parameters=None): """ Write a single field associated with the dataset ds to the backup file. Parameters ---------- ds : Dataset object The yt dataset that the field is associated with. fields : field of list of fields The name(s) of the field(s) to save. field_parameters : dictionary A dictionary of field parameters to set. """ fields = list(iter_fields(fields)) for field_name in fields: if isinstance(field_name, tuple): field_name = field_name[1] field_obj = ds._get_field_info(field_name) if field_obj.sampling_type == "particle": print("Saving particle fields currently not supported.") return with _get_backup_file(ds) as f: # now save the field _write_fields_to_gdf( ds, f, fields, particle_type_name="dark_matter", field_parameters=field_parameters, ) def _write_fields_to_gdf( ds, fhandle, fields, particle_type_name, field_parameters=None ): for field in fields: # add field info to field_types group g = fhandle["field_types"] # create the subgroup with the field's name fi = ds._get_field_info(field) ftype, fname = fi.name try: sg = g.create_group(fname) except ValueError: print("Error - File already contains field called " + field) sys.exit(1) # grab the display name and units from the field info container. display_name = fi.display_name units = fi.units # check that they actually contain something... if display_name: sg.attrs["field_name"] = np.bytes_(display_name) else: sg.attrs["field_name"] = np.bytes_(str(field)) if units: sg.attrs["field_units"] = np.bytes_(units) else: sg.attrs["field_units"] = np.bytes_("None") # @todo: is this always true? sg.attrs["staggering"] = 0 # first we must create the datasets on all processes. g = fhandle["data"] for grid in ds.index.grids: for field in fields: # sanitize get the field info object fi = ds._get_field_info(field) ftype, fname = fi.name grid_group = g["grid_%010i" % (grid.id - grid._id_offset)] particles_group = grid_group["particles"] pt_group = particles_group[particle_type_name] if fi.sampling_type == "particle": # particle data pt_group.create_dataset(fname, grid.ActiveDimensions, dtype="float64") else: # a field grid_group.create_dataset(fname, grid.ActiveDimensions, dtype="float64") # now add the actual data, grid by grid g = fhandle["data"] data_source = ds.all_data() citer = data_source.chunks([], "io", local_only=True) for region in parallel_objects(citer): # is there a better way to the get the grids on each chunk? for chunk in ds.index._chunk_io(region): for grid in chunk.objs: for field in fields: # sanitize and get the field info object fi = ds._get_field_info(field) ftype, fname = fi.name # set field parameters, if specified if field_parameters is not None: for k, v in field_parameters.items(): grid.set_field_parameter(k, v) grid_group = g["grid_%010i" % (grid.id - grid._id_offset)] particles_group = grid_group["particles"] pt_group = particles_group[particle_type_name] # add the field data to the grid group # Check if this is a real field or particle data. grid.get_data(field) units = fhandle["field_types"][fname].attrs["field_units"] if fi.sampling_type == "particle": # particle data dset = pt_group[fname] dset[:] = grid[field].in_units(units) else: # a field dset = grid_group[fname] dset[:] = grid[field].in_units(units) @contextmanager def _get_backup_file(ds): backup_filename = ds.backup_filename if os.path.exists(backup_filename): # backup file already exists, open it. We use parallel # h5py if it is available if communication_system.communicators[-1].size > 1 and h5py.get_config().mpi: mpi4py_communicator = communication_system.communicators[-1].comm f = h5py.File( backup_filename, mode="r+", driver="mpio", comm=mpi4py_communicator ) else: f = h5py.File(backup_filename, mode="r+") yield f f.close() else: # backup file does not exist, create it with _create_new_gdf( ds, backup_filename, data_author=None, data_comment=None, particle_type_name="dark_matter", ) as f: yield f @contextmanager def _create_new_gdf( ds, gdf_path, data_author=None, data_comment=None, dataset_units=None, particle_type_name="dark_matter", overwrite=False, **kwargs, ): # Make sure we have the absolute path to the file first gdf_path = os.path.abspath(gdf_path) # Is the file already there? If so, are we allowing # overwriting? if os.path.exists(gdf_path) and not overwrite: raise YTGDFAlreadyExists(gdf_path) ### # Create and open the file with h5py. We use parallel # h5py if it is available. ### if communication_system.communicators[-1].size > 1 and h5py.get_config().mpi: mpi4py_communicator = communication_system.communicators[-1].comm f = h5py.File(gdf_path, mode="w", driver="mpio", comm=mpi4py_communicator) else: f = h5py.File(gdf_path, mode="w") ### # "gridded_data_format" group ### g = f.create_group("gridded_data_format") g.attrs["data_software"] = "yt" g.attrs["data_software_version"] = yt_version if data_author is not None: g.attrs["data_author"] = data_author if data_comment is not None: g.attrs["data_comment"] = data_comment ### # "simulation_parameters" group ### g = f.create_group("simulation_parameters") g.attrs["refine_by"] = ds.refine_by g.attrs["dimensionality"] = ds.dimensionality g.attrs["domain_dimensions"] = ds.domain_dimensions g.attrs["current_time"] = ds.current_time g.attrs["domain_left_edge"] = ds.domain_left_edge g.attrs["domain_right_edge"] = ds.domain_right_edge g.attrs["unique_identifier"] = ds.unique_identifier g.attrs["cosmological_simulation"] = ds.cosmological_simulation # @todo: Where is this in the yt API? g.attrs["num_ghost_zones"] = 0 # @todo: Where is this in the yt API? g.attrs["field_ordering"] = 0 # @todo: not yet supported by yt. g.attrs["boundary_conditions"] = np.array([0, 0, 0, 0, 0, 0], "int32") if ds.cosmological_simulation: g.attrs["current_redshift"] = ds.current_redshift g.attrs["omega_matter"] = ds.omega_matter g.attrs["omega_lambda"] = ds.omega_lambda g.attrs["hubble_constant"] = ds.hubble_constant if dataset_units is None: dataset_units = {} g = f.create_group("dataset_units") for u in ["length", "time", "mass", "velocity", "magnetic"]: unit_name = u + "_unit" if unit_name in dataset_units: value, units = dataset_units[unit_name] else: attr = getattr(ds, unit_name) value = float(attr) units = str(attr.units) d = g.create_dataset(unit_name, data=value) d.attrs["unit"] = units ### # "field_types" group ### g = f.create_group("field_types") ### # "particle_types" group ### g = f.create_group("particle_types") # @todo: Particle type iterator sg = g.create_group(particle_type_name) sg["particle_type_name"] = np.bytes_(particle_type_name) ### # root datasets -- info about the grids ### f["grid_dimensions"] = ds.index.grid_dimensions f["grid_left_index"] = np.array( [grid.get_global_startindex() for grid in ds.index.grids] ).reshape(ds.index.grid_dimensions.shape[0], 3) f["grid_level"] = ds.index.grid_levels.flat # @todo: Fill with proper values f["grid_parent_id"] = -np.ones(ds.index.grid_dimensions.shape[0]) f["grid_particle_count"] = ds.index.grid_particle_count ### # "data" group -- where we should spend the most time ### g = f.create_group("data") for grid in ds.index.grids: # add group for this grid grid_group = g.create_group("grid_%010i" % (grid.id - grid._id_offset)) # add group for the particles on this grid particles_group = grid_group.create_group("particles") particles_group.create_group(particle_type_name) yield f # close the file when done f.close() yt-project-yt-f043ac8/yt/utilities/hierarchy_inspection.py000066400000000000000000000026541510711153200241130ustar00rootroot00000000000000import inspect from collections import Counter from typing import TypeVar from more_itertools import flatten from yt.data_objects.static_output import Dataset from yt.utilities.object_registries import output_type_registry T = TypeVar("T") def find_lowest_subclasses(candidates: list[type[T]]) -> list[type[T]]: """ This function takes a list of classes, and returns only the ones that are are not super classes of any others in the list. i.e. the ones that are at the bottom of the specified mro of classes. Parameters ---------- candidates : Iterable An iterable object that is a collection of classes to find the lowest subclass of. Returns ------- result : list A list of classes which are not super classes for any others in candidates. """ count = Counter(flatten(inspect.getmro(c) for c in candidates)) return [x for x in candidates if count[x] == 1] def get_classes_with_missing_requirements() -> dict[type[Dataset], list[str]]: # We need a function here rather than an global constant registry because: # - computation should be delayed until needed so that the result is independent of import order # - tests might (temporarily) mutate output_type_registry return { cls: missing for cls in sorted(output_type_registry.values(), key=lambda c: c.__name__) if (missing := cls._missing_load_requirements()) } yt-project-yt-f043ac8/yt/utilities/initial_conditions.py000066400000000000000000000067021510711153200235620ustar00rootroot00000000000000import numpy as np class FluidOperator: def apply(self, ds): for g in ds.index.grids: self(g) class TopHatSphere(FluidOperator): def __init__(self, radius, center, fields): self.radius = radius self.center = center self.fields = fields def __call__(self, grid, sub_select=None): r = np.zeros(grid.ActiveDimensions, dtype="float64") for i, ax in enumerate("xyz"): np.add(r, (grid[ax].to_ndarray() - self.center[i]) ** 2.0, r) np.sqrt(r, r) ind = r <= self.radius if sub_select is not None: ind &= sub_select for field, val in self.fields.items(): grid[field][ind] = val class CoredSphere(FluidOperator): def __init__(self, core_radius, radius, center, fields): self.radius = radius self.center = center self.fields = fields self.core_radius = core_radius def __call__(self, grid, sub_select=None): r = np.zeros(grid.ActiveDimensions, dtype="float64") r2 = self.radius**2 cr2 = self.core_radius**2 for i, ax in enumerate("xyz"): np.add(r, (grid[ax] - self.center[i]) ** 2.0, r) np.maximum(r, cr2, out=r) ind = r <= r2 if sub_select is not None: ind &= sub_select for field, (outer_val, inner_val) in self.fields.items(): val = ((r[ind] - cr2) / (r2 - cr2)) ** 0.5 * (outer_val - inner_val) grid[field][ind] = val + inner_val class BetaModelSphere(FluidOperator): def __init__(self, beta, core_radius, radius, center, fields): self.radius = radius self.center = center self.fields = fields self.core_radius = core_radius self.beta = beta def __call__(self, grid, sub_select=None): r = np.zeros(grid.ActiveDimensions, dtype="float64") r2 = self.radius**2 cr2 = self.core_radius**2 for i, ax in enumerate("xyz"): np.add(r, (grid[ax].ndarray_view() - self.center[i]) ** 2.0, r) ind = r <= r2 if sub_select is not None: ind &= sub_select for field, core_val in self.fields.items(): val = core_val * (1.0 + r[ind] / cr2) ** (-1.5 * self.beta) grid[field][ind] = val class NFWModelSphere(FluidOperator): def __init__(self, scale_radius, radius, center, fields): self.radius = radius self.center = center self.fields = fields self.scale_radius = scale_radius # unitless def __call__(self, grid, sub_select=None): r = np.zeros(grid.ActiveDimensions, dtype="float64") for i, ax in enumerate("xyz"): np.add(r, (grid[ax].ndarray_view() - self.center[i]) ** 2.0, r) np.sqrt(r, r) ind = r <= self.radius r /= self.scale_radius if sub_select is not None: ind &= sub_select for field, scale_val in self.fields.items(): val = scale_val / (r[ind] * (1.0 + r[ind]) ** 2) grid[field][ind] = val class RandomFluctuation(FluidOperator): def __init__(self, fields): self.fields = fields def __call__(self, grid, sub_select=None): if sub_select is None: sub_select = Ellipsis rng = np.random.default_rng() for field, mag in self.fields.items(): vals = grid[field][sub_select] rc = 1.0 + (rng.random(vals.shape) - 0.5) * mag grid[field][sub_select] *= rc yt-project-yt-f043ac8/yt/utilities/io_handler.py000066400000000000000000000234621510711153200220060ustar00rootroot00000000000000import os from collections import defaultdict from collections.abc import Iterator, Mapping from contextlib import contextmanager from functools import _make_key, lru_cache import numpy as np from yt._typing import FieldKey, ParticleCoordinateTuple from yt.geometry.selection_routines import GridSelector from yt.utilities.on_demand_imports import _h5py as h5py io_registry = {} use_caching = 0 def _make_io_key(args, *_args, **kwargs): self, obj, field, ctx = args # Ignore self because we have a self-specific cache return _make_key((obj.id, field), *_args, **kwargs) class BaseIOHandler: _vector_fields: dict[str, int] = {} _dataset_type: str _particle_reader = False _cache_on = False _misses = 0 _hits = 0 def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) if hasattr(cls, "_dataset_type"): io_registry[cls._dataset_type] = cls if use_caching and hasattr(cls, "_read_obj_field"): cls._read_obj_field = lru_cache( maxsize=use_caching, typed=True, make_key=_make_io_key )(cls._read_obj_field) def __init__(self, ds): self.queue = defaultdict(dict) self.ds = ds self._last_selector_id = None self._last_selector_counts = None self._array_fields = {} self._cached_fields = {} # We need a function for reading a list of sets # and a function for *popping* from a queue all the appropriate sets @contextmanager def preload(self, chunk, fields: list[FieldKey], max_size): yield self def peek(self, grid, field): return self.queue[grid.id].get(field, None) def push(self, grid, field, data): if grid.id in self.queue and field in self.queue[grid.id]: raise ValueError self.queue[grid][field] = data def _field_in_backup(self, grid, backup_file, field_name): if os.path.exists(backup_file): fhandle = h5py.File(backup_file, mode="r") g = fhandle["data"] grid_group = g["grid_%010i" % (grid.id - grid._id_offset)] if field_name in grid_group: return_val = True else: return_val = False fhandle.close() return return_val else: return False def _read_data_set(self, grid, field): # check backup file first. if field not found, # call frontend-specific io method backup_filename = grid.ds.backup_filename if not os.path.exists(backup_filename): return self._read_data(grid, field) elif self._field_in_backup(grid, backup_filename, field): fhandle = h5py.File(backup_filename, mode="r") g = fhandle["data"] grid_group = g["grid_%010i" % (grid.id - grid._id_offset)] data = grid_group[field][:] fhandle.close() return data else: return self._read_data(grid, field) # Now we define our interface def _read_data(self, grid, field): pass def _read_fluid_selection( self, chunks, selector, fields: list[FieldKey], size ) -> Mapping[FieldKey, np.ndarray]: # This function has an interesting history. It previously was mandate # to be defined by all of the subclasses. But, to avoid having to # rewrite a whole bunch of IO handlers all at once, and to allow a # better abstraction for grid-based frontends, we're now defining it in # the base class. rv = {} nodal_fields = [] for field in fields: finfo = self.ds.field_info[field] nodal_flag = finfo.nodal_flag if np.any(nodal_flag): num_nodes = 2 ** sum(nodal_flag) rv[field] = np.empty((size, num_nodes), dtype="=f8") nodal_fields.append(field) else: rv[field] = np.empty(size, dtype="=f8") ind = {field: 0 for field in fields} for field, obj, data in self.io_iter(chunks, fields): if data is None: continue if isinstance(selector, GridSelector) and field not in nodal_fields: ind[field] += data.size rv[field] = data.copy() else: ind[field] += obj.select(selector, data, rv[field], ind[field]) return rv def io_iter(self, chunks, fields: list[FieldKey]): raise NotImplementedError( "subclassing Dataset.io_iter this is required in order to use the default " "implementation of Dataset._read_fluid_selection. " "Custom implementations of the latter may not rely on this method." ) def _read_data_slice(self, grid, field, axis, coord): sl = [slice(None), slice(None), slice(None)] sl[axis] = slice(coord, coord + 1) tr = self._read_data_set(grid, field)[tuple(sl)] if tr.dtype == "float32": tr = tr.astype("float64") return tr def _read_field_names(self, grid): pass @property def _read_exception(self): return None def _read_chunk_data(self, chunk, fields): return {} def _read_particle_coords( self, chunks, ptf: defaultdict[str, list[str]] ) -> Iterator[ParticleCoordinateTuple]: # An iterator that yields particle coordinates for each chunk by particle # type. Must be implemented by each frontend. Must yield a tuple of # (particle type, xyz, hsml) by chunk. If the frontend does not have # a smoothing length, yield (particle type, xyz, 0.0) raise NotImplementedError def _read_particle_data_file(self, data_file, ptf, selector=None): # each frontend needs to implement this: read from a data_file object # and return a dict of fields for that data_file raise NotImplementedError def _read_particle_selection( self, chunks, selector, fields: list[FieldKey] ) -> dict[FieldKey, np.ndarray]: data: dict[FieldKey, list[np.ndarray]] = {} # Initialize containers for tracking particle, field information # ptf (particle field types) maps particle type to list of on-disk fields to read # field_maps stores fields, accounting for field unions ptf: defaultdict[str, list[str]] = defaultdict(list) field_maps: defaultdict[FieldKey, list[FieldKey]] = defaultdict(list) # We first need a set of masks for each particle type chunks = list(chunks) unions = self.ds.particle_unions # What we need is a mapping from particle types to return types for field in fields: ftype, fname = field # We should add a check for p.fparticle_unions or something here if ftype in unions: for pt in unions[ftype]: ptf[pt].append(fname) field_maps[pt, fname].append(field) else: ptf[ftype].append(fname) field_maps[field].append(field) data[field] = [] # Now we read. for field_r, vals in self._read_particle_fields(chunks, ptf, selector): # Note that we now need to check the mappings for field_f in field_maps[field_r]: data[field_f].append(vals) rv: dict[FieldKey, np.ndarray] = {} # the return dictionary fields = list(data.keys()) for field_f in fields: # We need to ensure the arrays have the right shape if there are no # particles in them. total = sum(_.size for _ in data[field_f]) if total > 0: vals = data.pop(field_f) # note: numpy.concatenate has a dtype argument that would avoid # a copy using .astype(...), available in numpy>=1.20 rv[field_f] = np.concatenate(vals, axis=0).astype("float64") else: shape = [0] if field_f[1] in self._vector_fields: shape.append(self._vector_fields[field_f[1]]) elif field_f[1] in self._array_fields: shape.append(self._array_fields[field_f[1]]) rv[field_f] = np.empty(shape, dtype="float64") return rv def _read_particle_fields(self, chunks, ptf, selector): # Now we have all the sizes, and we can allocate for data_file in self._sorted_chunk_iterator(chunks): data_file_data = self._read_particle_data_file(data_file, ptf, selector) # temporary trickery so it's still an iterator, need to adjust # the io_handler.BaseIOHandler.read_particle_selection() method # to not use an iterator. yield from data_file_data.items() @staticmethod def _get_data_files(chunks): chunks = list(chunks) data_files = set() for chunk in chunks: for obj in chunk.objs: data_files.update(obj.data_files) return data_files def _sorted_chunk_iterator(self, chunks): data_files = self._get_data_files(chunks) yield from sorted(data_files, key=lambda x: (x.filename, x.start)) # As a note: we don't *actually* want this to be how it is forever. There's no # reason we need to have the fluid and particle IO handlers separated. But, # for keeping track of which frontend is which, this is a useful abstraction. class BaseParticleIOHandler(BaseIOHandler): pass class IOHandlerExtracted(BaseIOHandler): _dataset_type = "extracted" def _read_data_set(self, grid, field): return grid.base_grid[field] / grid.base_grid.convert(field) def _read_data_slice(self, grid, field, axis, coord): sl = [slice(None), slice(None), slice(None)] sl[axis] = slice(coord, coord + 1) return grid.base_grid[field][tuple(sl)] / grid.base_grid.convert(field) yt-project-yt-f043ac8/yt/utilities/lib/000077500000000000000000000000001510711153200200675ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/__init__.py000066400000000000000000000000251510711153200221750ustar00rootroot00000000000000"""Blank __init__""" yt-project-yt-f043ac8/yt/utilities/lib/_octree_raytracing.hpp000066400000000000000000000300511510711153200244420ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include typedef double F; const int Ndim = 3; /* A simple node struct that contains a key and a fixed number of children, typically Nchildren = 2**Ndim */ template struct GenericNode { // Tree data GenericNode** children = nullptr; GenericNode* parent = nullptr; // Node data keyType key; int level = 0; bool terminal = false; int index = -1; }; template struct RayInfo { std::vector keys; std::vector t; RayInfo() {}; RayInfo(int N) { if (N > 0) { keys.reserve(N); t.reserve(2*N); } } }; struct Ray { std::array o; // Origin std::array d; // Direction F tmin = -1e99; F tmax = 1e99; Ray(const F* _o, const F* _d, const F _tmin, const F _tmax) : tmin(_tmin), tmax(_tmax) { for (auto idim = 0; idim < Ndim; ++idim) { o[idim] = _o[idim]; } F dd = 0; for (auto idim = 0; idim < Ndim; ++idim) { dd += _d[idim] * _d[idim]; } dd = sqrt(dd); for (auto idim = 0; idim < Ndim; ++idim) { d[idim] = _d[idim] / dd; } }; Ray() {}; }; /* Converts an array of integer position into a flattened index. The fast varying index is the last one. */ inline unsigned char ijk2iflat(const std::array ijk) { unsigned char iflat = 0; for (auto i : ijk) { iflat += i; iflat <<= 1; } return iflat >> 1; }; /* Converts a flattened index into an array of integer position. The fast varying index is the last one. */ inline std::array iflat2ijk(unsigned char iflat) { std::array ijk; for (auto idim = Ndim-1; idim >= 0; --idim) { ijk[idim] = iflat & 0b1; iflat >>= 1; } return ijk; }; /* A class to build an octree and cast rays through it. */ template class Octree { typedef GenericNode Node; typedef std::vector keyVector; typedef std::array Pos; typedef std::array iPos; typedef std::array ucPos; private: const unsigned char twotondim; const int maxDepth; Pos size; Pos DLE; // Domain left edge Pos DRE; // Domain right edge Node* root; int global_index = 0; public: Octree(int _maxDepth, F* _DLE, F* _DRE) : twotondim (1<children = (Node**) malloc(sizeof(Node*)*twotondim); for (auto i = 0; i < twotondim; ++i) root->children = nullptr; } ~Octree() { recursive_remove_node(root); }; /* Insert a new node in the tree. */ Node* insert_node(const iPos ipos, const int lvl, keyType key) { assert(lvl <= maxDepth); // std::cerr << "Inserting at level: " << lvl << "/" << maxDepth << std::endl; // this is 0b100..., where the 1 is at position maxDepth uint64_t mask = 1<<(maxDepth - 1); iPos ijk = ipos; std::array bitMask; Node* node = root; Node* child = nullptr; // Go down the tree for (auto ibit = maxDepth-1; ibit >= maxDepth - lvl; --ibit) { // Find children based on bits for (auto idim = 0; idim < Ndim; ++idim) { bitMask[idim] = ijk[idim] & mask; } mask >>= 1; auto iflat = ijk2iflat(bitMask); // Create child if it does not exist yet child = create_get_node(node, iflat); node = child; } // Mark last node as terminal node->terminal = true; node->key = key; return node; } Node* insert_node(const int* ipos, const int lvl, keyType key) { std::array ipos_as_arr; for (auto idim = 0; idim < Ndim; ++idim) ipos_as_arr[idim] = ipos[idim]; return insert_node(ipos_as_arr, lvl, key); } void insert_node_no_ret(const int* ipos, const int lvl, keyType key) { insert_node(ipos, lvl, key); } // Perform multiple ray cast RayInfo** cast_rays(const F *origins, const F *directions, const int Nrays) { RayInfo **ray_infos = (RayInfo**) malloc(sizeof(RayInfo*)*Nrays); int Nfound = 0; #pragma omp parallel for shared(ray_infos, Nfound) schedule(static, 100) for (auto i = 0; i < Nrays; ++i) { std::vector tList; ray_infos[i] = new RayInfo(Nfound); auto ri = ray_infos[i]; Ray r(&origins[3*i], &directions[3*i], -1e99, 1e99); cast_ray(&r, ri->keys, ri->t); // Keep track of the number of cells hit to preallocate the next ray info container Nfound = std::max(Nfound, (int) ri->keys.size()); } return ray_infos; } // Perform single ray tracing void cast_ray(Ray *r, keyVector &keyList, std::vector &tList) { // Boolean mask for direction unsigned char a = 0; unsigned char bmask = twotondim >> 1; // Put ray in positive direction and store info in bitmask "a" for (auto idim = 0; idim < Ndim; ++idim) { if (r->d[idim] < 0.0) { r->o[idim] = size[idim]-r->o[idim]; r->d[idim] = -r->d[idim]; a |= bmask; } bmask >>= 1; } // Compute intersection points Pos t0, t1; for (auto idim = 0; idim < Ndim; ++idim){ t0[idim] = (DLE[idim] - r->o[idim]) / r->d[idim]; t1[idim] = (DRE[idim] - r->o[idim]) / r->d[idim]; } // If entry point is smaller than exit point, find path in octree if (*std::max_element(t0.begin(), t0.end()) < *std::min_element(t1.begin(), t1.end())) proc_subtree(t0[0], t0[1], t0[2], t1[0], t1[1], t1[2], root, a, keyList, tList); } void cast_ray(double* o, double* d, keyVector &keyList, std::vector &tList) { Ray r(o, d, -1e99, 1e99); cast_ray(&r, keyList, tList); } private: /* Upsert a node as a child of another. This will create a new node as a child of the current one, or return an existing one if it already exists */ Node* create_get_node(Node* parent, const int iflat) { // Create children if not already existing if (parent->children == nullptr) { parent->children = (Node**) malloc(sizeof(Node*)*twotondim); for (auto i = 0; i < twotondim; ++i) parent->children[i] = nullptr; } if (parent->children[iflat] == nullptr) { Node* node = new Node(); node->level = parent->level + 1; node->index = global_index; node->parent = parent; ++global_index; parent->children[iflat] = node; } return parent->children[iflat]; } /* Recursively free memory. */ void recursive_remove_node(Node* node) { if (node->children) { for (auto i = 0; i < twotondim; ++i) { auto child = node->children[i]; if (child) { recursive_remove_node(child); } delete child; } free(node->children); } } /* Traverse the tree, assuming that the ray intersects From http://wscg.zcu.cz/wscg2000/Papers_2000/X31.pdf */ void proc_subtree(const F tx0, const F ty0, const F tz0, const F tx1, const F ty1, const F tz1, const Node *n, const unsigned char a, keyVector &keyList, std::vector &tList, int lvl=0) { // Check if exit face is not in our back if (tx1 < 0 || ty1 < 0 || tz1 < 0) return; // Exit if the node is null (happens if it hasn't been added to the tree) if (!n) return; // Process leaf node if (n->terminal) { keyList.push_back(n->key); // Push entry & exit t tList.push_back(std::max(std::max(tx0, ty0), tz0)); tList.push_back(std::min(std::min(tx1, ty1), tz1)); assert(n->children == nullptr); return; } // Early break for leafs without children that aren't terminal if (n->children == nullptr) return; // Compute middle intersection F txm, tym, tzm; txm = (tx0 + tx1) * 0.5; tym = (ty0 + ty1) * 0.5; tzm = (tz0 + tz1) * 0.5; unsigned char iNode = first_node(tx0, ty0, tz0, txm, tym, tzm); // Iterate over children do { switch (iNode) { case 0: proc_subtree(tx0, ty0, tz0, txm, tym, tzm, n->children[a], a, keyList, tList, lvl+1); iNode = next_node(txm, tym, tzm, 4, 2, 1); break; case 1: proc_subtree(tx0, ty0, tzm, txm, tym, tz1, n->children[1^a], a, keyList, tList, lvl+1); iNode = next_node(txm, tym, tz1, 5, 3, 8); break; case 2: proc_subtree(tx0, tym, tz0, txm, ty1, tzm, n->children[2^a], a, keyList, tList, lvl+1); iNode = next_node(txm, ty1, tzm, 6, 8, 3); break; case 3: proc_subtree(tx0, tym, tzm, txm, ty1, tz1, n->children[3^a], a, keyList, tList, lvl+1); iNode = next_node(txm, ty1, tz1, 7, 8, 8); break; case 4: proc_subtree(txm, ty0, tz0, tx1, tym, tzm, n->children[4^a], a, keyList, tList, lvl+1); iNode = next_node(tx1, tym, tzm, 8, 6, 5); break; case 5: proc_subtree(txm, ty0, tzm, tx1, tym, tz1, n->children[5^a], a, keyList, tList, lvl+1); iNode = next_node(tx1, tym, tz1, 8, 7, 8); break; case 6: proc_subtree(txm, tym, tz0, tx1, ty1, tzm, n->children[6^a], a, keyList, tList, lvl+1); iNode = next_node(tx1, ty1, tzm, 8, 8, 7); break; case 7: proc_subtree(txm, tym, tzm, tx1, ty1, tz1, n->children[7^a], a, keyList, tList, lvl+1); iNode = 8; break; } } while (iNode < twotondim); } // From "An Efficient Parametric Algorithm for Octree Traversal" by Revelles, Urena, & Lastra inline unsigned char first_node(const F tx0, const F ty0, const F tz0, const F txm, const F tym, const F tzm) { unsigned char index = 0; if (tx0 >= std::max(ty0, tz0)) { // enters YZ plane if (tym < tx0) index |= 0b010; if (tzm < tx0) index |= 0b001; } else if (ty0 >= std::max(tx0, tz0)) { // enters XZ plane if (txm < ty0) index |= 0b100; if (tzm < ty0) index |= 0b001; } else { // enters XY plane if (txm < tz0) index |= 0b100; if (tym < tz0) index |= 0b010; } return index; } // From "An Efficient Parametric Algorithm for Octree Traversal" by Revelles, Urena, & Lastra inline unsigned char next_node(const F tx, const F ty, const F tz, const uint8_t ix, const uint8_t iy, const uint8_t iz) { if(tx < std::min(ty, tz)) { // YZ plane return ix; } else if (ty < std::min(tx, tz)) { // XZ plane return iy; } else { // XY plane return iz; } } }; yt-project-yt-f043ac8/yt/utilities/lib/_octree_raytracing.pxd000066400000000000000000000017411510711153200244520ustar00rootroot00000000000000"""This is a wrapper around the C++ class to efficiently cast rays into an octree. It relies on the seminal paper by J. Revelles,, C.Ureña and M.Lastra. """ cimport numpy as np import numpy as np cimport cython from libcpp.vector cimport vector from cython.parallel import parallel, prange from libc.stdlib cimport free, malloc from .grid_traversal cimport sampler_function from .image_samplers cimport ImageAccumulator, ImageSampler from .partitioned_grid cimport PartitionedGrid from .volume_container cimport VolumeContainer cdef extern from "_octree_raytracing.hpp": cdef cppclass RayInfo[T]: vector[T] keys vector[double] t cdef cppclass Octree[T] nogil: Octree(int depth, double* LE, double* RE) void insert_node_no_ret(const int* ipos, const int lvl, T key) void cast_ray(double* origins, double* directions, vector[T] keyList, vector[double] tList) cdef class _OctreeRayTracing: cdef Octree[int]* oct cdef int depth yt-project-yt-f043ac8/yt/utilities/lib/_octree_raytracing.pyx000066400000000000000000000017721510711153200245030ustar00rootroot00000000000000# distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG OMP_ARGS """This is a wrapper around the C++ class to efficiently cast rays into an octree. It relies on the seminal paper by J. Revelles,, C.Ureña and M.Lastra. """ cimport numpy as np import numpy as np cimport cython cdef class _OctreeRayTracing: def __init__(self, np.ndarray LE, np.ndarray RE, int depth): cdef double* LE_ptr = LE.data cdef double* RE_ptr = RE.data self.oct = new Octree[int](depth, LE_ptr, RE_ptr) self.depth = depth @cython.boundscheck(False) @cython.wraparound(False) def add_nodes(self, int[:, :] ipos_view, int[:] lvl_view, int[:] key): cdef int i cdef int ii[3] for i in range(len(key)): ii[0] = ipos_view[i, 0] ii[1] = ipos_view[i, 1] ii[2] = ipos_view[i, 2] self.oct.insert_node_no_ret(ii, lvl_view[i], key[i]) def __dealloc__(self): del self.oct yt-project-yt-f043ac8/yt/utilities/lib/allocation_container.pxd000066400000000000000000000013401510711153200247710ustar00rootroot00000000000000""" An allocation container and memory pool """ cimport numpy as np from libc.stdlib cimport free, malloc, realloc cdef struct AllocationContainer: np.uint64_t n np.uint64_t n_assigned np.uint64_t offset np.int64_t con_id # container id void *my_objs cdef class ObjectPool: cdef public np.uint64_t itemsize cdef np.uint64_t n_con cdef AllocationContainer* containers cdef void allocate_objs(self, int n_objs, np.int64_t con_id = ?) except * cdef void setup_objs(self, void *obj, np.uint64_t count, np.uint64_t offset, np.int64_t con_id) cdef void teardown_objs(self, void *obj, np.uint64_t n, np.uint64_t offset, np.int64_t con_id) yt-project-yt-f043ac8/yt/utilities/lib/allocation_container.pyx000066400000000000000000000102611510711153200250200ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ An allocation container and memory pool """ cimport numpy as np import numpy as np cdef class ObjectPool: def __cinit__(self): """This class is *not* meant to be initialized directly, but instead through subclasses. Those subclasses need to implement at a minimum the setting of itemsize, but can optionally also implement setup_objs and teardown_objs to either set default values or initialize additional pointers and values, and then free them.""" self.containers = NULL self.n_con = 0 self.itemsize = -1 # Never use the base class def __dealloc__(self): cdef int i cdef AllocationContainer *obj for i in range(self.n_con): obj = &self.containers[i] self.teardown_objs(obj.my_objs, obj.n, obj.offset, obj.con_id) if self.containers != NULL: free(self.containers) def __getitem__(self, int i): """This returns an array view (if possible and implemented in a subclass) on the pool of objects specified by i.""" return self._con_to_array(i) def __len__(self): return self.n_con def append(self, int n_objs, np.int64_t con_id = -1): """This allocates a new batch of n_objs objects, with the container id specified as con_id. Return value is a view on them.""" self.allocate_objs(n_objs, con_id) return self[self.n_con - 1] cdef void allocate_objs(self, int n_objs, np.int64_t con_id = -1) except *: cdef AllocationContainer *n_cont cdef AllocationContainer *prev self.containers = realloc( self.containers, sizeof(AllocationContainer) * (self.n_con + 1)) n_cont = &self.containers[self.n_con] if self.n_con == 0: n_cont.offset = 0 else: prev = &self.containers[self.n_con - 1] n_cont.offset = prev.offset + prev.n self.n_con += 1 n_cont.my_objs = malloc(self.itemsize * n_objs) if n_cont.my_objs == NULL: raise MemoryError n_cont.n = n_objs n_cont.n_assigned = 0 n_cont.con_id = con_id self.setup_objs(n_cont.my_objs, n_objs, n_cont.offset, n_cont.con_id) cdef void setup_objs(self, void *obj, np.uint64_t count, np.uint64_t offset, np.int64_t con_id): """This can be overridden in the subclass, where it can initialize any of the allocated objects.""" pass cdef void teardown_objs(self, void *obj, np.uint64_t n, np.uint64_t offset, np.int64_t con_id): # We assume that these are all allocated and have no sub-allocations """If overridden, additional behavior can be provided during teardown of allocations. For instance, if you allocate some memory on each of the allocated objects.""" if obj != NULL: free(obj) def to_arrays(self): rv = [] cdef int i for i in range(self.n_con): rv.append(self._con_to_array(i)) return rv def _con_to_array(self, int i): """This has to be implemented in a subclass, and should return an appropriate np.asarray() of a memoryview of self.my_objs.""" raise NotImplementedError cdef class BitmaskPool(ObjectPool): def __cinit__(self): """This is an example implementation of object pools for bitmasks (uint8) arrays. It lets you reasonably quickly allocate a set of uint8 arrays which can be accessed and modified, and then virtually append to that.""" # Base class will ALSO be called self.itemsize = sizeof(np.uint8_t) cdef void setup_objs(self, void *obj, np.uint64_t n, np.uint64_t offset, np.int64_t con_id): cdef np.uint64_t i cdef np.uint8_t *mask = obj for i in range(n): mask[i] = 0 def _con_to_array(self, int i): cdef AllocationContainer *obj = &self.containers[i] cdef np.uint8_t[:] o = ( obj.my_objs) rv = np.asarray(o) return rv yt-project-yt-f043ac8/yt/utilities/lib/alt_ray_tracers.pyx000066400000000000000000000200471510711153200240120ustar00rootroot00000000000000# distutils: libraries = STD_LIBS """ """ import numpy as np cimport cython cimport libc.math as math cimport numpy as np @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def _cyl2cart(np.ndarray[np.float64_t, ndim=2] x): """Converts points in cylindrical coordinates to cartesian points.""" # NOTE this should be removed once the coord interface comes online cdef int i, I cdef np.ndarray[np.float64_t, ndim=2] xcart I = x.shape[0] xcart = np.empty((I, x.shape[1]), dtype='float64') for i in range(I): xcart[i,0] = x[i,0] * math.cos(x[i,2]) xcart[i,1] = x[i,0] * math.sin(x[i,2]) xcart[i,2] = x[i,1] return xcart @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def _cart_intersect(np.ndarray[np.float64_t, ndim=1] a, np.ndarray[np.float64_t, ndim=1] b, np.ndarray[np.float64_t, ndim=2] c, np.ndarray[np.float64_t, ndim=2] d): """Finds the times and locations of the lines defined by a->b and c->d in cartesian space. a and b must be 1d points. c and d must be 2d arrays of equal shape whose second dim is the same length as a and b. """ cdef int i, I cdef np.ndarray[np.float64_t, ndim=1] r, s, t cdef np.ndarray[np.float64_t, ndim=2] loc I = c.shape[0] shape = (I, c.shape[1]) t = np.empty(I, dtype='float64') loc = np.empty(shape, dtype='float64') r = b - a for i in range(I): s = d[i] - c[i] t[i] = (np.cross(c[i] - a, s).sum()) / (np.cross(r, s).sum()) loc[i] = a + t[i]*r return t, loc @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def cylindrical_ray_trace(np.ndarray[np.float64_t, ndim=1] p1, np.ndarray[np.float64_t, ndim=1] p2, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges): """Computes straight (cartesian) rays being traced through a cylindrical geometry. Parameters ---------- p1 : length 3 float ndarray start point for ray p2 : length 3 float ndarray stop point for ray left_edges : 2d ndarray left edges of grid cells right_edges : 2d ndarray right edges of grid cells Returns ------- t : 1d float ndarray ray parametric time on range [0,1] s : 1d float ndarray ray parametric distance on range [0,len(ray)] rztheta : 2d float ndarray ray grid cell intersections in cylindrical coordinates inds : 1d int ndarray indexes into the grid cells which the ray crosses in order. """ cdef np.int64_t i, I cdef np.float64_t a, b, bsqrd, twoa cdef np.ndarray[np.float64_t, ndim=1] p1cart, p2cart, dpcart, t, s, \ rleft, rright, zleft, zright, \ cleft, cright, thetaleft, thetaright, \ tmleft, tpleft, tmright, tpright, tsect cdef np.ndarray[np.int64_t, ndim=1, cast=True] inds, tinds, sinds cdef np.ndarray[np.float64_t, ndim=2] xyz, rztheta, ptemp, b1, b2, dsect # set up points ptemp = np.array([p1, p2]) ptemp = _cyl2cart(ptemp) p1cart = ptemp[0] p2cart = ptemp[1] dpcart = p2cart - p1cart # set up components rleft = left_edges[:,0] rright = right_edges[:,0] zleft = left_edges[:,1] zright = right_edges[:,1] a = dpcart[0] * dpcart[0] + dpcart[1] * dpcart[1] b = 2 * dpcart[0] * p1cart[0] + 2 * dpcart[1] * p1cart[1] cleft = p1cart[0] * p1cart[0] + p1cart[1] * p1cart[1] - rleft * rleft cright = p1cart[0] * p1cart[0] + p1cart[1] * p1cart[1] - rright * rright twoa = 2 * a bsqrd = b * b # Compute positive and negative times and associated masks I = np.intp(left_edges.shape[0]) tmleft = np.empty(I, dtype='float64') tpleft = np.empty(I, dtype='float64') tmright = np.empty(I, dtype='float64') tpright = np.empty(I, dtype='float64') for i in range(I): tmleft[i] = (-b - math.sqrt(bsqrd - 4*a*cleft[i])) / twoa tpleft[i] = (-b + math.sqrt(bsqrd - 4*a*cleft[i])) / twoa tmright[i] = (-b - math.sqrt(bsqrd - 4*a*cright[i])) / twoa tpright[i] = (-b + math.sqrt(bsqrd - 4*a*cright[i])) / twoa tmmright = np.logical_and(~np.isnan(tmright), rright <= p1[0]) tpmright = np.logical_and(~np.isnan(tpright), rright <= p2[0]) tmmleft = np.logical_and(~np.isnan(tmleft), rleft <= p1[0]) tpmleft = np.logical_and(~np.isnan(tpleft), rleft <= p2[0]) # compute first cut of indexes and thetas, which # have been filtered by those values for which intersection # times are impossible (see above masks). Note that this is # still independent of z. inds = np.unique(np.concatenate([np.argwhere(tmmleft).flat, np.argwhere(tpmleft).flat, np.argwhere(tmmright).flat, np.argwhere(tpmright).flat,])) if 0 == inds.shape[0]: inds = np.arange(np.intp(I)) thetaleft = np.empty(I) thetaleft.fill(p1[2]) thetaright = np.empty(I) thetaright.fill(p2[2]) else: rleft = rleft[inds] rright = rright[inds] zleft = zleft[inds] zright = zright[inds] thetaleft = np.arctan2((p1cart[1] + tmleft[inds]*dpcart[1]), (p1cart[0] + tmleft[inds]*dpcart[0])) nans = np.isnan(thetaleft) thetaleft[nans] = np.arctan2((p1cart[1] + tpleft[inds[nans]]*dpcart[1]), (p1cart[0] + tpleft[inds[nans]]*dpcart[0])) thetaright = np.arctan2((p1cart[1] + tmright[inds]*dpcart[1]), (p1cart[0] + tmright[inds]*dpcart[0])) nans = np.isnan(thetaright) thetaright[nans] = np.arctan2((p1cart[1] + tpright[inds[nans]]*dpcart[1]), (p1cart[0] + tpright[inds[nans]]*dpcart[0])) # Set up the cell boundary arrays b1 = np.concatenate([[rleft, zright, thetaleft], [rleft, zleft, thetaleft], [rleft, zleft, thetaleft], [rright, zleft, thetaright], [rleft, zleft, thetaleft], [rright, zright, thetaleft], [rleft, zright, thetaleft], [rright, zleft, thetaleft], ], axis=1).T b2 = np.concatenate([[rright, zright, thetaright], [rright, zleft, thetaright], [rleft, zright, thetaleft], [rright, zright, thetaright], [rleft, zleft, thetaright], [rright, zright, thetaright], [rleft, zright, thetaright], [rright, zleft, thetaright], ], axis=1).T inds = np.concatenate([inds, inds, inds, inds, inds, inds, inds, inds]) # find intersections and compute return values tsect, dsect = _cart_intersect(p1cart, p2cart, _cyl2cart(b1), _cyl2cart(b2)) tmask = np.logical_and(0.0<=tsect, tsect<=1.0) ret = np.unique(tsect[tmask], return_index=True) tsect, tinds = ret[0], ret[1].astype('int64') inds = inds[tmask][tinds] xyz = dsect[tmask][tinds] s = np.sqrt(((xyz - p1cart) * (xyz - p1cart)).sum(axis=1)) ret = np.unique(s, return_index=True) s, sinds = ret[0], ret[1].astype('int64') inds = inds[sinds] xyz = xyz[sinds] t = s/np.sqrt((dpcart*dpcart).sum()) sinds = s.argsort() s = s[sinds] t = t[sinds] inds = inds[sinds] xyz = xyz[sinds] rztheta = np.concatenate([np.sqrt(xyz[:,0] * xyz[:,0] + xyz[:,1] * xyz[:,1])[:,np.newaxis], xyz[:,2:3], np.arctan2(xyz[:,1], xyz[:,0])[:,np.newaxis]], axis=1) return t, s, rztheta, inds #rztheta[:,2] = 0.0 + (rztheta[:,2] - np.pi*3/2)%(2*np.pi) yt-project-yt-f043ac8/yt/utilities/lib/amr_kdtools.pxd000066400000000000000000000044151510711153200231260ustar00rootroot00000000000000""" AMR kD-Tree Cython Tools """ cimport numpy as np cdef struct Split: int dim np.float64_t pos cdef class Node: cdef public Node left cdef public Node right cdef public Node parent cdef public int grid cdef public bint dirty cdef public np.int64_t node_id cdef public np.int64_t node_ind cdef np.float64_t left_edge[3] cdef np.float64_t right_edge[3] cdef public data cdef Split * split cdef int level cdef int point_in_node(self, np.float64_t[:] point) cdef Node _find_node(self, np.float64_t[:] point) cdef int _kd_is_leaf(self) cdef add_grid(self, np.float64_t[:] gle, np.float64_t[:] gre, int gid, int rank, int size) cdef insert_grid(self, np.float64_t[:] gle, np.float64_t[:] gre, int grid_id, int rank, int size) cpdef add_grids(self, int ngrids, np.float64_t[:,:] gles, np.float64_t[:,:] gres, np.int64_t[:] gids, int rank, int size) cdef int should_i_split(self, int rank, int size) cdef void insert_grids(self, int ngrids, np.float64_t[:,:] gles, np.float64_t[:,:] gres, np.int64_t[:] gids, int rank, int size) cdef split_grid(self, np.float64_t[:] gle, np.float64_t[:] gre, int gid, int rank, int size) cdef int split_grids(self, int ngrids, np.float64_t[:,:] gles, np.float64_t[:,:] gres, np.int64_t[:] gids, int rank, int size) cdef geo_split(self, np.float64_t[:] gle, np.float64_t[:] gre, int grid_id, int rank, int size) cdef void divide(self, Split * split) yt-project-yt-f043ac8/yt/utilities/lib/amr_kdtools.pyx000066400000000000000000000647571510711153200231720ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ AMR kD-Tree Cython Tools """ import numpy as np cimport cython cimport numpy as np from cython.view cimport array as cvarray from libc.stdlib cimport free, malloc @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef class Node: def __cinit__(self, Node parent, Node left, Node right, np.float64_t[:] left_edge, np.float64_t[:] right_edge, int grid, np.int64_t node_id): self.dirty = False self.left = left self.right = right self.parent = parent cdef int i for i in range(3): self.left_edge[i] = left_edge[i] self.right_edge[i] = right_edge[i] self.grid = grid self.node_id = node_id self.split == NULL def print_me(self): print('Node %i' % self.node_id) print('\t le: %e %e %e' % (self.left_edge[0], self.left_edge[1], self.left_edge[2])) print('\t re: %e %e %e' % (self.right_edge[0], self.right_edge[1], self.right_edge[2])) print('\t grid: %i' % self.grid) def get_split_dim(self): if self.split != NULL: return self.split.dim else: return -1 def get_split_pos(self): if self.split != NULL: return self.split.pos else: return np.nan def set_left_edge(self, np.float64_t[:] left_edge): cdef int i for i in range(3): self.left_edge[i] = left_edge[i] def set_right_edge(self, np.float64_t[:] right_edge): cdef int i for i in range(3): self.right_edge[i] = right_edge[i] def create_split(self, dim, pos): split = malloc(sizeof(Split)) split.dim = dim split.pos = pos self.split = split def __dealloc__(self): if self.split != NULL: free(self.split) # Begin input of converted methods def get_left_edge(self): le = np.empty(3, dtype='float64') for i in range(3): le[i] = self.left_edge[i] return le def get_right_edge(self): re = np.empty(3, dtype='float64') for i in range(3): re[i] = self.right_edge[i] return re def set_dirty(self, bint state): cdef Node node for node in self.depth_traverse(): node.dirty = state def kd_traverse(self, viewpoint=None): cdef Node node if viewpoint is None: for node in self.depth_traverse(): if node._kd_is_leaf() == 1 and node.grid != -1: yield node else: for node in self.viewpoint_traverse(viewpoint): if node._kd_is_leaf() == 1 and node.grid != -1: yield node def add_pygrid(self, np.float64_t[:] gle, np.float64_t[:] gre, int gid, int rank, int size): """ The entire purpose of this function is to move everything from ndarrays to internal C pointers. """ cdef np.float64_t[:] pgles = cvarray(format="d", shape=(3,), itemsize=sizeof(np.float64_t)) cdef np.float64_t[:] pgres = cvarray(format="d", shape=(3,), itemsize=sizeof(np.float64_t)) cdef int j for j in range(3): pgles[j] = gle[j] pgres[j] = gre[j] self.add_grid(pgles, pgres, gid, rank, size) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef add_grid(self, np.float64_t[:] gle, np.float64_t[:] gre, int gid, int rank, int size): if not should_i_build(self, rank, size): return if self._kd_is_leaf() == 1: self.insert_grid(gle, gre, gid, rank, size) else: less_id = gle[self.split.dim] < self.split.pos if less_id: self.left.add_grid(gle, gre, gid, rank, size) greater_id = gre[self.split.dim] > self.split.pos if greater_id: self.right.add_grid(gle, gre, gid, rank, size) return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef insert_grid(self, np.float64_t[:] gle, np.float64_t[:] gre, int grid_id, int rank, int size): if not should_i_build(self, rank, size): return # If we should continue to split based on parallelism, do so! if self.should_i_split(rank, size): self.geo_split(gle, gre, grid_id, rank, size) return cdef int contained = 1 for i in range(3): if gle[i] > self.left_edge[i] or\ gre[i] < self.right_edge[i]: contained *= 0 if contained == 1: self.grid = grid_id assert(self.grid != -1) return # Split the grid cdef int check = self.split_grid(gle, gre, grid_id, rank, size) # If check is -1, then we have found a place where there are no choices. # Exit out and set the node to None. if check == -1: self.grid = -1 return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cpdef add_grids(self, int ngrids, np.float64_t[:,:] gles, np.float64_t[:,:] gres, np.int64_t[:] gids, int rank, int size): cdef int i, j, nless, ngreater, index cdef np.float64_t[:,:] less_gles, less_gres, greater_gles, greater_gres cdef np.int64_t[:] l_ids, g_ids if not should_i_build(self, rank, size): return if self._kd_is_leaf() == 1: self.insert_grids(ngrids, gles, gres, gids, rank, size) return less_ids = cvarray(format="q", shape=(ngrids,), itemsize=sizeof(np.int64_t)) greater_ids = cvarray(format="q", shape=(ngrids,), itemsize=sizeof(np.int64_t)) nless = 0 ngreater = 0 for i in range(ngrids): if gles[i, self.split.dim] < self.split.pos: less_ids[nless] = i nless += 1 if gres[i, self.split.dim] > self.split.pos: greater_ids[ngreater] = i ngreater += 1 #print('nless: %i' % nless) #print('ngreater: %i' % ngreater) if nless > 0: less_gles = cvarray(format="d", shape=(nless,3), itemsize=sizeof(np.float64_t)) less_gres = cvarray(format="d", shape=(nless,3), itemsize=sizeof(np.float64_t)) l_ids = cvarray(format="q", shape=(nless,), itemsize=sizeof(np.int64_t)) for i in range(nless): index = less_ids[i] l_ids[i] = gids[index] for j in range(3): less_gles[i,j] = gles[index,j] less_gres[i,j] = gres[index,j] self.left.add_grids(nless, less_gles, less_gres, l_ids, rank, size) if ngreater > 0: greater_gles = cvarray(format="d", shape=(ngreater,3), itemsize=sizeof(np.float64_t)) greater_gres = cvarray(format="d", shape=(ngreater,3), itemsize=sizeof(np.float64_t)) g_ids = cvarray(format="q", shape=(ngreater,), itemsize=sizeof(np.int64_t)) for i in range(ngreater): index = greater_ids[i] g_ids[i] = gids[index] for j in range(3): greater_gles[i,j] = gles[index,j] greater_gres[i,j] = gres[index,j] self.right.add_grids(ngreater, greater_gles, greater_gres, g_ids, rank, size) return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int should_i_split(self, int rank, int size): if self.node_id < size and self.node_id > 0: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void insert_grids(self, int ngrids, np.float64_t[:,:] gles, np.float64_t[:,:] gres, np.int64_t[:] gids, int rank, int size): if not should_i_build(self, rank, size) or ngrids == 0: return cdef int contained = 1 cdef int check if ngrids == 1: # If we should continue to split based on parallelism, do so! if self.should_i_split(rank, size): self.geo_split(gles[0,:], gres[0,:], gids[0], rank, size) return for i in range(3): contained *= gles[0,i] <= self.left_edge[i] contained *= gres[0,i] >= self.right_edge[i] if contained == 1: # print('Node fully contained, setting to grid: %i' % gids[0]) self.grid = gids[0] assert(self.grid != -1) return # Split the grids check = self.split_grids(ngrids, gles, gres, gids, rank, size) # If check is -1, then we have found a place where there are no choices. # Exit out and set the node to None. if check == -1: self.grid = -1 return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef split_grid(self, np.float64_t[:] gle, np.float64_t[:] gre, int gid, int rank, int size): cdef int j cdef np.uint8_t[:] less_ids, greater_ids data = cvarray(format="d", shape=(1,2,3), itemsize=sizeof(np.float64_t)) for j in range(3): data[0,0,j] = gle[j] data[0,1,j] = gre[j] less_ids = cvarray(format="B", shape=(1,), itemsize=sizeof(np.uint8_t)) greater_ids = cvarray(format="B", shape=(1,), itemsize=sizeof(np.uint8_t)) best_dim, split_pos, nless, ngreater = \ kdtree_get_choices(1, data, self.left_edge, self.right_edge, less_ids, greater_ids) # If best_dim is -1, then we have found a place where there are no choices. # Exit out and set the node to None. if best_dim == -1: return -1 split = malloc(sizeof(Split)) split.dim = best_dim split.pos = split_pos # Create a Split self.divide(split) # Populate Left Node #print('Inserting left node', self.left_edge, self.right_edge) if nless == 1: self.left.insert_grid(gle, gre, gid, rank, size) # Populate Right Node #print('Inserting right node', self.left_edge, self.right_edge) if ngreater == 1: self.right.insert_grid(gle, gre, gid, rank, size) return 0 #@cython.boundscheck(False) #@cython.wraparound(False) #@cython.cdivision(True) cdef int split_grids(self, int ngrids, np.float64_t[:,:] gles, np.float64_t[:,:] gres, np.int64_t[:] gids, int rank, int size): # Find a Split cdef int i, j, index cdef np.float64_t[:,:] less_gles, less_gres, greater_gles, greater_gres cdef np.int64_t[:] l_ids, g_ids if ngrids == 0: return 0 data = cvarray(format="d", shape=(ngrids,2,3), itemsize=sizeof(np.float64_t)) for i in range(ngrids): for j in range(3): data[i,0,j] = gles[i,j] data[i,1,j] = gres[i,j] less_ids = cvarray(format="B", shape=(ngrids,), itemsize=sizeof(np.uint8_t)) greater_ids = cvarray(format="B", shape=(ngrids,), itemsize=sizeof(np.uint8_t)) best_dim, split_pos, nless, ngreater = \ kdtree_get_choices(ngrids, data, self.left_edge, self.right_edge, less_ids, greater_ids) # If best_dim is -1, then we have found a place where there are no choices. # Exit out and set the node to None. if best_dim == -1: print('Failed to split grids.') return -1 split = malloc(sizeof(Split)) split.dim = best_dim split.pos = split_pos # Create a Split self.divide(split) less_index = cvarray(format="q", shape=(ngrids,), itemsize=sizeof(np.int64_t)) greater_index = cvarray(format="q", shape=(ngrids,), itemsize=sizeof(np.int64_t)) nless = 0 ngreater = 0 for i in range(ngrids): if less_ids[i] == 1: less_index[nless] = i nless += 1 if greater_ids[i] == 1: greater_index[ngreater] = i ngreater += 1 if nless > 0: less_gles = cvarray(format="d", shape=(nless,3), itemsize=sizeof(np.float64_t)) less_gres = cvarray(format="d", shape=(nless,3), itemsize=sizeof(np.float64_t)) l_ids = cvarray(format="q", shape=(nless,), itemsize=sizeof(np.int64_t)) for i in range(nless): index = less_index[i] l_ids[i] = gids[index] for j in range(3): less_gles[i,j] = gles[index,j] less_gres[i,j] = gres[index,j] # Populate Left Node #print('Inserting left node', self.left_edge, self.right_edge) self.left.insert_grids(nless, less_gles, less_gres, l_ids, rank, size) if ngreater > 0: greater_gles = cvarray(format="d", shape=(ngreater,3), itemsize=sizeof(np.float64_t)) greater_gres = cvarray(format="d", shape=(ngreater,3), itemsize=sizeof(np.float64_t)) g_ids = cvarray(format="q", shape=(ngreater,), itemsize=sizeof(np.int64_t)) for i in range(ngreater): index = greater_index[i] g_ids[i] = gids[index] for j in range(3): greater_gles[i,j] = gles[index,j] greater_gres[i,j] = gres[index,j] # Populate Right Node #print('Inserting right node', self.left_edge, self.right_edge) self.right.insert_grids(ngreater, greater_gles, greater_gres, g_ids, rank, size) return 0 cdef geo_split(self, np.float64_t[:] gle, np.float64_t[:] gre, int grid_id, int rank, int size): cdef int big_dim = 0 cdef int i cdef np.float64_t v, my_max = 0.0 for i in range(3): v = gre[i] - gle[i] if v > my_max: my_max = v big_dim = i new_pos = (gre[big_dim] + gle[big_dim])/2. lnew_gle = cvarray(format="d", shape=(3,), itemsize=sizeof(np.float64_t)) lnew_gre = cvarray(format="d", shape=(3,), itemsize=sizeof(np.float64_t)) rnew_gle = cvarray(format="d", shape=(3,), itemsize=sizeof(np.float64_t)) rnew_gre = cvarray(format="d", shape=(3,), itemsize=sizeof(np.float64_t)) for j in range(3): lnew_gle[j] = gle[j] lnew_gre[j] = gre[j] rnew_gle[j] = gle[j] rnew_gre[j] = gre[j] split = malloc(sizeof(Split)) split.dim = big_dim split.pos = new_pos # Create a Split self.divide(split) #lnew_gre[big_dim] = new_pos # Populate Left Node #print('Inserting left node', self.left_edge, self.right_edge) self.left.insert_grid(lnew_gle, lnew_gre, grid_id, rank, size) #rnew_gle[big_dim] = new_pos # Populate Right Node #print('Inserting right node', self.left_edge, self.right_edge) self.right.insert_grid(rnew_gle, rnew_gre, grid_id, rank, size) return cdef void divide(self, Split * split): # Create a Split self.split = split cdef np.float64_t[:] le = np.empty(3, dtype='float64') cdef np.float64_t[:] re = np.empty(3, dtype='float64') cdef int i for i in range(3): le[i] = self.left_edge[i] re[i] = self.right_edge[i] re[split.dim] = split.pos self.left = Node(self, None, None, le, re, self.grid, _lchild_id(self.node_id)) re[split.dim] = self.right_edge[split.dim] le[split.dim] = split.pos self.right = Node(self, None, None, le, re, self.grid, _rchild_id(self.node_id)) return # def kd_sum_volume(self): cdef np.float64_t vol = 1.0 if (self.left is None) and (self.right is None): if self.grid == -1: return 0.0 for i in range(3): vol *= self.right_edge[i] - self.left_edge[i] return vol else: return self.left.kd_sum_volume() + self.right.kd_sum_volume() def kd_node_check(self): assert (self.left is None) == (self.right is None) if (self.left is None) and (self.right is None): if self.grid != -1: return np.prod(self.right_edge - self.left_edge) else: return 0.0 else: return self.left.kd_node_check()+self.right.kd_node_check() def kd_is_leaf(self): cdef int has_l_child = self.left == None cdef int has_r_child = self.right == None assert has_l_child == has_r_child return has_l_child cdef int _kd_is_leaf(self): if self.left is None or self.right is None: return 1 return 0 def depth_traverse(self, max_node=None): ''' Yields a depth-first traversal of the kd tree always going to the left child before the right. ''' current = self previous = None if max_node is None: max_node = np.inf while current is not None: yield current current, previous = step_depth(current, previous) if current is None: break if current.node_id >= max_node: current = current.parent previous = current.right def depth_first_touch(self, max_node=None): ''' Yields a depth-first traversal of the kd tree always going to the left child before the right. ''' current = self previous = None if max_node is None: max_node = np.inf while current is not None: if previous is None or previous.parent != current: yield current current, previous = step_depth(current, previous) if current is None: break if current.node_id >= max_node: current = current.parent previous = current.right def breadth_traverse(self): ''' Yields a breadth-first traversal of the kd tree always going to the left child before the right. ''' current = self previous = None while current is not None: yield current current, previous = step_depth(current, previous) def viewpoint_traverse(self, viewpoint): ''' Yields a viewpoint dependent traversal of the kd-tree. Starts with nodes furthest away from viewpoint. ''' current = self previous = None while current is not None: yield current current, previous = step_viewpoint(current, previous, viewpoint) cdef int point_in_node(self, np.float64_t[:] point): cdef int i cdef int inside = 1 for i in range(3): inside *= self.left_edge[i] <= point[i] inside *= self.right_edge[i] > point[i] return inside cdef Node _find_node(self, np.float64_t[:] point): while self._kd_is_leaf() == 0: if point[self.split.dim] < self.split.pos: self = self.left else: self = self.right return self def find_node(self, np.float64_t[:] point): """ Find the AMRKDTree node enclosing a position """ assert(self.point_in_node(point)) return self._find_node(point) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline np.int64_t _lchild_id(np.int64_t node_id): return (node_id<<1) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline np.int64_t _rchild_id(np.int64_t node_id): return (node_id<<1) + 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline np.int64_t _parent_id(np.int64_t node_id): return (node_id-1) >> 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int should_i_build(Node node, int rank, int size): if (node.node_id < size) or (node.node_id >= 2*size): return 1 elif node.node_id - size == rank: return 1 else: return 0 def step_depth(Node current, Node previous): ''' Takes a single step in the depth-first traversal ''' if current._kd_is_leaf() == 1: # At a leaf, move back up previous = current current = current.parent elif current.parent is previous: # Moving down, go left first previous = current if current.left is not None: current = current.left elif current.right is not None: current = current.right else: current = current.parent elif current.left is previous: # Moving up from left, go right previous = current if current.right is not None: current = current.right else: current = current.parent elif current.right is previous: # Moving up from right child, move up previous = current current = current.parent return current, previous def step_viewpoint(Node current, Node previous, viewpoint): ''' Takes a single step in the viewpoint based traversal. Always goes to the node furthest away from viewpoint first. ''' if current._kd_is_leaf() == 1: # At a leaf, move back up previous = current current = current.parent elif current.split.dim is None: # This is a dead node previous = current current = current.parent elif current.parent is previous: # Moving down previous = current if viewpoint[current.split.dim] <= current.split.pos: if current.right is not None: current = current.right else: previous = current.right else: if current.left is not None: current = current.left else: previous = current.left elif current.right is previous: # Moving up from right previous = current if viewpoint[current.split.dim] <= current.split.pos: if current.left is not None: current = current.left else: current = current.parent else: current = current.parent elif current.left is previous: # Moving up from left child previous = current if viewpoint[current.split.dim] > current.split.pos: if current.right is not None: current = current.right else: current = current.parent else: current = current.parent return current, previous @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef kdtree_get_choices(int n_grids, np.float64_t[:,:,:] data, np.float64_t[:] l_corner, np.float64_t[:] r_corner, np.uint8_t[:] less_ids, np.uint8_t[:] greater_ids, ): cdef int i, j, k, dim, n_unique, best_dim, my_split cdef np.float64_t split cdef np.float64_t[:,:] uniquedims cdef np.float64_t[:] uniques uniquedims = cvarray(format="d", shape=(3, 2*n_grids), itemsize=sizeof(np.float64_t)) my_max = 0 my_split = 0 best_dim = -1 for dim in range(3): n_unique = 0 uniques = uniquedims[dim] for i in range(n_grids): # Check for disqualification for j in range(2): # print("Checking against", i,j,dim,data[i,j,dim]) if not (l_corner[dim] < data[i][j][dim] and data[i][j][dim] < r_corner[dim]): # print("Skipping ", data[i,j,dim], l_corner[dim], r_corner[dim]) continue skipit = 0 # Add our left ... for k in range(n_unique): if uniques[k] == data[i][j][dim]: skipit = 1 # print("Identified", uniques[k], data[i,j,dim], n_unique) break if skipit == 0: uniques[n_unique] = data[i][j][dim] n_unique += 1 if n_unique > my_max: best_dim = dim my_max = n_unique my_split = (n_unique-1)/2 if best_dim == -1: return -1, 0, 0, 0 # I recognize how lame this is. cdef np.ndarray[np.float64_t, ndim=1] tarr = np.empty(my_max, dtype='float64') for i in range(my_max): # print("Setting tarr: ", i, uniquedims[best_dim][i]) tarr[i] = uniquedims[best_dim][i] tarr.sort() split = tarr[my_split] cdef int nless=0, ngreater=0 for i in range(n_grids): if data[i][0][best_dim] < split: less_ids[i] = 1 nless += 1 else: less_ids[i] = 0 if data[i][1][best_dim] > split: greater_ids[i] = 1 ngreater += 1 else: greater_ids[i] = 0 # Return out unique values return best_dim, split, nless, ngreater yt-project-yt-f043ac8/yt/utilities/lib/api.py000066400000000000000000000007241510711153200212150ustar00rootroot00000000000000from .basic_octree import * from .contour_finding import * from .depth_first_octree import * from .fortran_reader import * from .grid_traversal import * from .image_utilities import * from .interpolators import * from .line_integral_convolution import * from .marching_cubes import * from .mesh_utilities import * from .misc_utilities import * from .particle_mesh_operations import * from .points_in_volume import * from .quad_tree import * from .write_array import * yt-project-yt-f043ac8/yt/utilities/lib/autogenerated_element_samplers.pxd000066400000000000000000000044431510711153200270570ustar00rootroot00000000000000cdef void Q1Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Q1Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Q1Function2D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Q1Jacobian2D(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Q2Function2D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Q2Jacobian2D(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Tet2Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil cdef void Tet2Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil cdef void T2Function2D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil cdef void T2Jacobian2D(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil cdef void W1Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil cdef void W1Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/autogenerated_element_samplers.pyx000066400000000000000000000474731510711153200271160ustar00rootroot00000000000000# This file contains auto-generated functions for sampling # inside finite element solutions for various mesh types. # To see how the code generation works in detail, see # yt/utilities/mesh_code_generation.py. cimport cython from libc.math cimport pow @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Q1Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: fx[0] = 0.125*(1 - x[0])*(1 - x[1])*(1 - x[2])*vertices[0] + 0.125*(1 - x[0])*(1 - x[1])*(1 + x[2])*vertices[12] + 0.125*(1 - x[0])*(1 + x[1])*(1 - x[2])*vertices[9] + 0.125*(1 - x[0])*(1 + x[1])*(1 + x[2])*vertices[21] + 0.125*(1 + x[0])*(1 - x[1])*(1 - x[2])*vertices[3] + 0.125*(1 + x[0])*(1 - x[1])*(1 + x[2])*vertices[15] + 0.125*(1 + x[0])*(1 + x[1])*(1 - x[2])*vertices[6] + 0.125*(1 + x[0])*(1 + x[1])*(1 + x[2])*vertices[18] - phys_x[0] fx[1] = 0.125*(1 - x[0])*(1 - x[1])*(1 - x[2])*vertices[1] + 0.125*(1 - x[0])*(1 - x[1])*(1 + x[2])*vertices[13] + 0.125*(1 - x[0])*(1 + x[1])*(1 - x[2])*vertices[10] + 0.125*(1 - x[0])*(1 + x[1])*(1 + x[2])*vertices[22] + 0.125*(1 + x[0])*(1 - x[1])*(1 - x[2])*vertices[4] + 0.125*(1 + x[0])*(1 - x[1])*(1 + x[2])*vertices[16] + 0.125*(1 + x[0])*(1 + x[1])*(1 - x[2])*vertices[7] + 0.125*(1 + x[0])*(1 + x[1])*(1 + x[2])*vertices[19] - phys_x[1] fx[2] = 0.125*(1 - x[0])*(1 - x[1])*(1 - x[2])*vertices[2] + 0.125*(1 - x[0])*(1 - x[1])*(1 + x[2])*vertices[14] + 0.125*(1 - x[0])*(1 + x[1])*(1 - x[2])*vertices[11] + 0.125*(1 - x[0])*(1 + x[1])*(1 + x[2])*vertices[23] + 0.125*(1 + x[0])*(1 - x[1])*(1 - x[2])*vertices[5] + 0.125*(1 + x[0])*(1 - x[1])*(1 + x[2])*vertices[17] + 0.125*(1 + x[0])*(1 + x[1])*(1 - x[2])*vertices[8] + 0.125*(1 + x[0])*(1 + x[1])*(1 + x[2])*vertices[20] - phys_x[2] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Q1Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil: rcol[0] = -0.125*(1 - x[1])*(1 - x[2])*vertices[0] + 0.125*(1 - x[1])*(1 - x[2])*vertices[3] - 0.125*(1 - x[1])*(1 + x[2])*vertices[12] + 0.125*(1 - x[1])*(1 + x[2])*vertices[15] + 0.125*(1 + x[1])*(1 - x[2])*vertices[6] - 0.125*(1 + x[1])*(1 - x[2])*vertices[9] + 0.125*(1 + x[1])*(1 + x[2])*vertices[18] - 0.125*(1 + x[1])*(1 + x[2])*vertices[21] scol[0] = -0.125*(1 - x[0])*(1 - x[2])*vertices[0] + 0.125*(1 - x[0])*(1 - x[2])*vertices[9] - 0.125*(1 - x[0])*(1 + x[2])*vertices[12] + 0.125*(1 - x[0])*(1 + x[2])*vertices[21] - 0.125*(1 + x[0])*(1 - x[2])*vertices[3] + 0.125*(1 + x[0])*(1 - x[2])*vertices[6] - 0.125*(1 + x[0])*(1 + x[2])*vertices[15] + 0.125*(1 + x[0])*(1 + x[2])*vertices[18] tcol[0] = -0.125*(1 - x[0])*(1 - x[1])*vertices[0] + 0.125*(1 - x[0])*(1 - x[1])*vertices[12] - 0.125*(1 - x[0])*(1 + x[1])*vertices[9] + 0.125*(1 - x[0])*(1 + x[1])*vertices[21] - 0.125*(1 + x[0])*(1 - x[1])*vertices[3] + 0.125*(1 + x[0])*(1 - x[1])*vertices[15] - 0.125*(1 + x[0])*(1 + x[1])*vertices[6] + 0.125*(1 + x[0])*(1 + x[1])*vertices[18] rcol[1] = -0.125*(1 - x[1])*(1 - x[2])*vertices[1] + 0.125*(1 - x[1])*(1 - x[2])*vertices[4] - 0.125*(1 - x[1])*(1 + x[2])*vertices[13] + 0.125*(1 - x[1])*(1 + x[2])*vertices[16] + 0.125*(1 + x[1])*(1 - x[2])*vertices[7] - 0.125*(1 + x[1])*(1 - x[2])*vertices[10] + 0.125*(1 + x[1])*(1 + x[2])*vertices[19] - 0.125*(1 + x[1])*(1 + x[2])*vertices[22] scol[1] = -0.125*(1 - x[0])*(1 - x[2])*vertices[1] + 0.125*(1 - x[0])*(1 - x[2])*vertices[10] - 0.125*(1 - x[0])*(1 + x[2])*vertices[13] + 0.125*(1 - x[0])*(1 + x[2])*vertices[22] - 0.125*(1 + x[0])*(1 - x[2])*vertices[4] + 0.125*(1 + x[0])*(1 - x[2])*vertices[7] - 0.125*(1 + x[0])*(1 + x[2])*vertices[16] + 0.125*(1 + x[0])*(1 + x[2])*vertices[19] tcol[1] = -0.125*(1 - x[0])*(1 - x[1])*vertices[1] + 0.125*(1 - x[0])*(1 - x[1])*vertices[13] - 0.125*(1 - x[0])*(1 + x[1])*vertices[10] + 0.125*(1 - x[0])*(1 + x[1])*vertices[22] - 0.125*(1 + x[0])*(1 - x[1])*vertices[4] + 0.125*(1 + x[0])*(1 - x[1])*vertices[16] - 0.125*(1 + x[0])*(1 + x[1])*vertices[7] + 0.125*(1 + x[0])*(1 + x[1])*vertices[19] rcol[2] = -0.125*(1 - x[1])*(1 - x[2])*vertices[2] + 0.125*(1 - x[1])*(1 - x[2])*vertices[5] - 0.125*(1 - x[1])*(1 + x[2])*vertices[14] + 0.125*(1 - x[1])*(1 + x[2])*vertices[17] + 0.125*(1 + x[1])*(1 - x[2])*vertices[8] - 0.125*(1 + x[1])*(1 - x[2])*vertices[11] + 0.125*(1 + x[1])*(1 + x[2])*vertices[20] - 0.125*(1 + x[1])*(1 + x[2])*vertices[23] scol[2] = -0.125*(1 - x[0])*(1 - x[2])*vertices[2] + 0.125*(1 - x[0])*(1 - x[2])*vertices[11] - 0.125*(1 - x[0])*(1 + x[2])*vertices[14] + 0.125*(1 - x[0])*(1 + x[2])*vertices[23] - 0.125*(1 + x[0])*(1 - x[2])*vertices[5] + 0.125*(1 + x[0])*(1 - x[2])*vertices[8] - 0.125*(1 + x[0])*(1 + x[2])*vertices[17] + 0.125*(1 + x[0])*(1 + x[2])*vertices[20] tcol[2] = -0.125*(1 - x[0])*(1 - x[1])*vertices[2] + 0.125*(1 - x[0])*(1 - x[1])*vertices[14] - 0.125*(1 - x[0])*(1 + x[1])*vertices[11] + 0.125*(1 - x[0])*(1 + x[1])*vertices[23] - 0.125*(1 + x[0])*(1 - x[1])*vertices[5] + 0.125*(1 + x[0])*(1 - x[1])*vertices[17] - 0.125*(1 + x[0])*(1 + x[1])*vertices[8] + 0.125*(1 + x[0])*(1 + x[1])*vertices[20] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Q1Function2D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: fx[0] = 0.25*(1 - x[0])*(1 - x[1])*vertices[0] + 0.25*(1 - x[0])*(1 + x[1])*vertices[6] + 0.25*(1 + x[0])*(1 - x[1])*vertices[2] + 0.25*(1 + x[0])*(1 + x[1])*vertices[4] - phys_x[0] fx[1] = 0.25*(1 - x[0])*(1 - x[1])*vertices[1] + 0.25*(1 - x[0])*(1 + x[1])*vertices[7] + 0.25*(1 + x[0])*(1 - x[1])*vertices[3] + 0.25*(1 + x[0])*(1 + x[1])*vertices[5] - phys_x[1] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Q1Jacobian2D(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil: rcol[0] = -0.25*(1 - x[1])*vertices[0] + 0.25*(1 - x[1])*vertices[2] + 0.25*(1 + x[1])*vertices[4] - 0.25*(1 + x[1])*vertices[6] scol[0] = -0.25*(1 - x[0])*vertices[0] + 0.25*(1 - x[0])*vertices[6] - 0.25*(1 + x[0])*vertices[2] + 0.25*(1 + x[0])*vertices[4] rcol[1] = -0.25*(1 - x[1])*vertices[1] + 0.25*(1 - x[1])*vertices[3] + 0.25*(1 + x[1])*vertices[5] - 0.25*(1 + x[1])*vertices[7] scol[1] = -0.25*(1 - x[0])*vertices[1] + 0.25*(1 - x[0])*vertices[7] - 0.25*(1 + x[0])*vertices[3] + 0.25*(1 + x[0])*vertices[5] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Q2Function2D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: fx[0] = (1 + x[0])*(-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[16] - 0.5*(1 + x[0])*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[8] - 0.5*(1 + x[0])*(-1 + x[0])*x[1]*(1 + x[1])*vertices[12] - phys_x[0] - 0.5*x[0]*(-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[14] + 0.25*x[0]*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[0] + 0.25*x[0]*(-1 + x[0])*x[1]*(1 + x[1])*vertices[6] - 0.5*x[0]*(1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[10] + 0.25*x[0]*(1 + x[0])*x[1]*(-1 + x[1])*vertices[2] + 0.25*x[0]*(1 + x[0])*x[1]*(1 + x[1])*vertices[4] fx[1] = (1 + x[0])*(-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[17] - 0.5*(1 + x[0])*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[9] - 0.5*(1 + x[0])*(-1 + x[0])*x[1]*(1 + x[1])*vertices[13] - phys_x[1] - 0.5*x[0]*(-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[15] + 0.25*x[0]*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[1] + 0.25*x[0]*(-1 + x[0])*x[1]*(1 + x[1])*vertices[7] - 0.5*x[0]*(1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[11] + 0.25*x[0]*(1 + x[0])*x[1]*(-1 + x[1])*vertices[3] + 0.25*x[0]*(1 + x[0])*x[1]*(1 + x[1])*vertices[5] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Q2Jacobian2D(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil: rcol[0] = -0.5*(-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[14] + (-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[16] + 0.25*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[0] - 0.5*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[8] + 0.25*(-1 + x[0])*x[1]*(1 + x[1])*vertices[6] - 0.5*(-1 + x[0])*x[1]*(1 + x[1])*vertices[12] - 0.5*(1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[10] + (1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[16] + 0.25*(1 + x[0])*x[1]*(-1 + x[1])*vertices[2] - 0.5*(1 + x[0])*x[1]*(-1 + x[1])*vertices[8] + 0.25*(1 + x[0])*x[1]*(1 + x[1])*vertices[4] - 0.5*(1 + x[0])*x[1]*(1 + x[1])*vertices[12] - 0.5*x[0]*(1 + x[1])*(-1 + x[1])*vertices[10] - 0.5*x[0]*(1 + x[1])*(-1 + x[1])*vertices[14] + 0.25*x[0]*x[1]*(-1 + x[1])*vertices[0] + 0.25*x[0]*x[1]*(-1 + x[1])*vertices[2] + 0.25*x[0]*x[1]*(1 + x[1])*vertices[4] + 0.25*x[0]*x[1]*(1 + x[1])*vertices[6] scol[0] = -0.5*(-1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[8] + (-1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[16] + 0.25*(-1 + x[1])*x[0]*(-1 + x[0])*vertices[0] - 0.5*(-1 + x[1])*x[0]*(-1 + x[0])*vertices[14] + 0.25*(-1 + x[1])*x[0]*(1 + x[0])*vertices[2] - 0.5*(-1 + x[1])*x[0]*(1 + x[0])*vertices[10] - 0.5*(1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[12] + (1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[16] + 0.25*(1 + x[1])*x[0]*(-1 + x[0])*vertices[6] - 0.5*(1 + x[1])*x[0]*(-1 + x[0])*vertices[14] + 0.25*(1 + x[1])*x[0]*(1 + x[0])*vertices[4] - 0.5*(1 + x[1])*x[0]*(1 + x[0])*vertices[10] - 0.5*x[1]*(1 + x[0])*(-1 + x[0])*vertices[8] - 0.5*x[1]*(1 + x[0])*(-1 + x[0])*vertices[12] + 0.25*x[1]*x[0]*(-1 + x[0])*vertices[0] + 0.25*x[1]*x[0]*(-1 + x[0])*vertices[6] + 0.25*x[1]*x[0]*(1 + x[0])*vertices[2] + 0.25*x[1]*x[0]*(1 + x[0])*vertices[4] rcol[1] = -0.5*(-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[15] + (-1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[17] + 0.25*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[1] - 0.5*(-1 + x[0])*x[1]*(-1 + x[1])*vertices[9] + 0.25*(-1 + x[0])*x[1]*(1 + x[1])*vertices[7] - 0.5*(-1 + x[0])*x[1]*(1 + x[1])*vertices[13] - 0.5*(1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[11] + (1 + x[0])*(1 + x[1])*(-1 + x[1])*vertices[17] + 0.25*(1 + x[0])*x[1]*(-1 + x[1])*vertices[3] - 0.5*(1 + x[0])*x[1]*(-1 + x[1])*vertices[9] + 0.25*(1 + x[0])*x[1]*(1 + x[1])*vertices[5] - 0.5*(1 + x[0])*x[1]*(1 + x[1])*vertices[13] - 0.5*x[0]*(1 + x[1])*(-1 + x[1])*vertices[11] - 0.5*x[0]*(1 + x[1])*(-1 + x[1])*vertices[15] + 0.25*x[0]*x[1]*(-1 + x[1])*vertices[1] + 0.25*x[0]*x[1]*(-1 + x[1])*vertices[3] + 0.25*x[0]*x[1]*(1 + x[1])*vertices[5] + 0.25*x[0]*x[1]*(1 + x[1])*vertices[7] scol[1] = -0.5*(-1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[9] + (-1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[17] + 0.25*(-1 + x[1])*x[0]*(-1 + x[0])*vertices[1] - 0.5*(-1 + x[1])*x[0]*(-1 + x[0])*vertices[15] + 0.25*(-1 + x[1])*x[0]*(1 + x[0])*vertices[3] - 0.5*(-1 + x[1])*x[0]*(1 + x[0])*vertices[11] - 0.5*(1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[13] + (1 + x[1])*(1 + x[0])*(-1 + x[0])*vertices[17] + 0.25*(1 + x[1])*x[0]*(-1 + x[0])*vertices[7] - 0.5*(1 + x[1])*x[0]*(-1 + x[0])*vertices[15] + 0.25*(1 + x[1])*x[0]*(1 + x[0])*vertices[5] - 0.5*(1 + x[1])*x[0]*(1 + x[0])*vertices[11] - 0.5*x[1]*(1 + x[0])*(-1 + x[0])*vertices[9] - 0.5*x[1]*(1 + x[0])*(-1 + x[0])*vertices[13] + 0.25*x[1]*x[0]*(-1 + x[0])*vertices[1] + 0.25*x[1]*x[0]*(-1 + x[0])*vertices[7] + 0.25*x[1]*x[0]*(1 + x[0])*vertices[3] + 0.25*x[1]*x[0]*(1 + x[0])*vertices[5] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Tet2Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: fx[0] = (-x[0] + 2*pow(x[0], 2))*vertices[3] + (-x[1] + 2*pow(x[1], 2))*vertices[6] + (-x[2] + 2*pow(x[2], 2))*vertices[9] + (4*x[0] - 4*x[0]*x[1] - 4*x[0]*x[2] - 4*pow(x[0], 2))*vertices[12] + (4*x[1] - 4*x[1]*x[0] - 4*x[1]*x[2] - 4*pow(x[1], 2))*vertices[18] + (4*x[2] - 4*x[2]*x[0] - 4*x[2]*x[1] - 4*pow(x[2], 2))*vertices[21] + (1 - 3*x[0] + 4*x[0]*x[1] + 4*x[0]*x[2] + 2*pow(x[0], 2) - 3*x[1] + 4*x[1]*x[2] + 2*pow(x[1], 2) - 3*x[2] + 2*pow(x[2], 2))*vertices[0] - phys_x[0] + 4*x[0]*x[1]*vertices[15] + 4*x[0]*x[2]*vertices[24] + 4*x[1]*x[2]*vertices[27] fx[1] = (-x[0] + 2*pow(x[0], 2))*vertices[4] + (-x[1] + 2*pow(x[1], 2))*vertices[7] + (-x[2] + 2*pow(x[2], 2))*vertices[10] + (4*x[0] - 4*x[0]*x[1] - 4*x[0]*x[2] - 4*pow(x[0], 2))*vertices[13] + (4*x[1] - 4*x[1]*x[0] - 4*x[1]*x[2] - 4*pow(x[1], 2))*vertices[19] + (4*x[2] - 4*x[2]*x[0] - 4*x[2]*x[1] - 4*pow(x[2], 2))*vertices[22] + (1 - 3*x[0] + 4*x[0]*x[1] + 4*x[0]*x[2] + 2*pow(x[0], 2) - 3*x[1] + 4*x[1]*x[2] + 2*pow(x[1], 2) - 3*x[2] + 2*pow(x[2], 2))*vertices[1] - phys_x[1] + 4*x[0]*x[1]*vertices[16] + 4*x[0]*x[2]*vertices[25] + 4*x[1]*x[2]*vertices[28] fx[2] = (-x[0] + 2*pow(x[0], 2))*vertices[5] + (-x[1] + 2*pow(x[1], 2))*vertices[8] + (-x[2] + 2*pow(x[2], 2))*vertices[11] + (4*x[0] - 4*x[0]*x[1] - 4*x[0]*x[2] - 4*pow(x[0], 2))*vertices[14] + (4*x[1] - 4*x[1]*x[0] - 4*x[1]*x[2] - 4*pow(x[1], 2))*vertices[20] + (4*x[2] - 4*x[2]*x[0] - 4*x[2]*x[1] - 4*pow(x[2], 2))*vertices[23] + (1 - 3*x[0] + 4*x[0]*x[1] + 4*x[0]*x[2] + 2*pow(x[0], 2) - 3*x[1] + 4*x[1]*x[2] + 2*pow(x[1], 2) - 3*x[2] + 2*pow(x[2], 2))*vertices[2] - phys_x[2] + 4*x[0]*x[1]*vertices[17] + 4*x[0]*x[2]*vertices[26] + 4*x[1]*x[2]*vertices[29] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void Tet2Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil: rcol[0] = (-1 + 4*x[0])*vertices[3] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[0] + (4 - 8*x[0] - 4*x[1] - 4*x[2])*vertices[12] + 4*x[1]*vertices[15] - 4*x[1]*vertices[18] - 4*x[2]*vertices[21] + 4*x[2]*vertices[24] scol[0] = (-1 + 4*x[1])*vertices[6] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[0] + (4 - 4*x[0] - 8*x[1] - 4*x[2])*vertices[18] - 4*x[0]*vertices[12] + 4*x[0]*vertices[15] - 4*x[2]*vertices[21] + 4*x[2]*vertices[27] tcol[0] = (-1 + 4*x[2])*vertices[9] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[0] + (4 - 4*x[0] - 4*x[1] - 8*x[2])*vertices[21] - 4*x[0]*vertices[12] + 4*x[0]*vertices[24] - 4*x[1]*vertices[18] + 4*x[1]*vertices[27] rcol[1] = (-1 + 4*x[0])*vertices[4] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[1] + (4 - 8*x[0] - 4*x[1] - 4*x[2])*vertices[13] + 4*x[1]*vertices[16] - 4*x[1]*vertices[19] - 4*x[2]*vertices[22] + 4*x[2]*vertices[25] scol[1] = (-1 + 4*x[1])*vertices[7] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[1] + (4 - 4*x[0] - 8*x[1] - 4*x[2])*vertices[19] - 4*x[0]*vertices[13] + 4*x[0]*vertices[16] - 4*x[2]*vertices[22] + 4*x[2]*vertices[28] tcol[1] = (-1 + 4*x[2])*vertices[10] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[1] + (4 - 4*x[0] - 4*x[1] - 8*x[2])*vertices[22] - 4*x[0]*vertices[13] + 4*x[0]*vertices[25] - 4*x[1]*vertices[19] + 4*x[1]*vertices[28] rcol[2] = (-1 + 4*x[0])*vertices[5] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[2] + (4 - 8*x[0] - 4*x[1] - 4*x[2])*vertices[14] + 4*x[1]*vertices[17] - 4*x[1]*vertices[20] - 4*x[2]*vertices[23] + 4*x[2]*vertices[26] scol[2] = (-1 + 4*x[1])*vertices[8] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[2] + (4 - 4*x[0] - 8*x[1] - 4*x[2])*vertices[20] - 4*x[0]*vertices[14] + 4*x[0]*vertices[17] - 4*x[2]*vertices[23] + 4*x[2]*vertices[29] tcol[2] = (-1 + 4*x[2])*vertices[11] + (-3 + 4*x[0] + 4*x[1] + 4*x[2])*vertices[2] + (4 - 4*x[0] - 4*x[1] - 8*x[2])*vertices[23] - 4*x[0]*vertices[14] + 4*x[0]*vertices[26] - 4*x[1]*vertices[20] + 4*x[1]*vertices[29] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void T2Function2D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: fx[0] = (-x[0] + 2*pow(x[0], 2))*vertices[2] + (-x[1] + 2*pow(x[1], 2))*vertices[4] + (-4*x[0]*x[1] + 4*x[1] - 4*pow(x[1], 2))*vertices[10] + (4*x[0] - 4*x[0]*x[1] - 4*pow(x[0], 2))*vertices[6] + (1 - 3*x[0] + 4*x[0]*x[1] + 2*pow(x[0], 2) - 3*x[1] + 2*pow(x[1], 2))*vertices[0] - phys_x[0] + 4*x[0]*x[1]*vertices[8] fx[1] = (-x[0] + 2*pow(x[0], 2))*vertices[3] + (-x[1] + 2*pow(x[1], 2))*vertices[5] + (-4*x[0]*x[1] + 4*x[1] - 4*pow(x[1], 2))*vertices[11] + (4*x[0] - 4*x[0]*x[1] - 4*pow(x[0], 2))*vertices[7] + (1 - 3*x[0] + 4*x[0]*x[1] + 2*pow(x[0], 2) - 3*x[1] + 2*pow(x[1], 2))*vertices[1] - phys_x[1] + 4*x[0]*x[1]*vertices[9] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void T2Jacobian2D(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil: rcol[0] = (-1 + 4*x[0])*vertices[2] + (-3 + 4*x[0] + 4*x[1])*vertices[0] + (4 - 8*x[0] - 4*x[1])*vertices[6] + 4*x[1]*vertices[8] - 4*x[1]*vertices[10] scol[0] = (-1 + 4*x[1])*vertices[4] + (-3 + 4*x[0] + 4*x[1])*vertices[0] + (4 - 4*x[0] - 8*x[1])*vertices[10] - 4*x[0]*vertices[6] + 4*x[0]*vertices[8] rcol[1] = (-1 + 4*x[0])*vertices[3] + (-3 + 4*x[0] + 4*x[1])*vertices[1] + (4 - 8*x[0] - 4*x[1])*vertices[7] + 4*x[1]*vertices[9] - 4*x[1]*vertices[11] scol[1] = (-1 + 4*x[1])*vertices[5] + (-3 + 4*x[0] + 4*x[1])*vertices[1] + (4 - 4*x[0] - 8*x[1])*vertices[11] - 4*x[0]*vertices[7] + 4*x[0]*vertices[9] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void W1Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: fx[0] = 0.5*(1 - x[0] - x[1])*(1 - x[2])*vertices[0] + 0.5*(1 - x[0] - x[1])*(1 + x[2])*vertices[9] - phys_x[0] + 0.5*x[0]*(1 - x[2])*vertices[3] + 0.5*x[0]*(1 + x[2])*vertices[12] + 0.5*x[1]*(1 - x[2])*vertices[6] + 0.5*x[1]*(1 + x[2])*vertices[15] fx[1] = 0.5*(1 - x[0] - x[1])*(1 - x[2])*vertices[1] + 0.5*(1 - x[0] - x[1])*(1 + x[2])*vertices[10] - phys_x[1] + 0.5*x[0]*(1 - x[2])*vertices[4] + 0.5*x[0]*(1 + x[2])*vertices[13] + 0.5*x[1]*(1 - x[2])*vertices[7] + 0.5*x[1]*(1 + x[2])*vertices[16] fx[2] = 0.5*(1 - x[0] - x[1])*(1 - x[2])*vertices[2] + 0.5*(1 - x[0] - x[1])*(1 + x[2])*vertices[11] - phys_x[2] + 0.5*x[0]*(1 - x[2])*vertices[5] + 0.5*x[0]*(1 + x[2])*vertices[14] + 0.5*x[1]*(1 - x[2])*vertices[8] + 0.5*x[1]*(1 + x[2])*vertices[17] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void W1Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil: rcol[0] = -0.5*(1 - x[2])*vertices[0] + 0.5*(1 - x[2])*vertices[3] - 0.5*(1 + x[2])*vertices[9] + 0.5*(1 + x[2])*vertices[12] scol[0] = -0.5*(1 - x[2])*vertices[0] + 0.5*(1 - x[2])*vertices[6] - 0.5*(1 + x[2])*vertices[9] + 0.5*(1 + x[2])*vertices[15] tcol[0] = -0.5*(1 - x[0] - x[1])*vertices[0] + 0.5*(1 - x[0] - x[1])*vertices[9] - 0.5*x[0]*vertices[3] + 0.5*x[0]*vertices[12] - 0.5*x[1]*vertices[6] + 0.5*x[1]*vertices[15] rcol[1] = -0.5*(1 - x[2])*vertices[1] + 0.5*(1 - x[2])*vertices[4] - 0.5*(1 + x[2])*vertices[10] + 0.5*(1 + x[2])*vertices[13] scol[1] = -0.5*(1 - x[2])*vertices[1] + 0.5*(1 - x[2])*vertices[7] - 0.5*(1 + x[2])*vertices[10] + 0.5*(1 + x[2])*vertices[16] tcol[1] = -0.5*(1 - x[0] - x[1])*vertices[1] + 0.5*(1 - x[0] - x[1])*vertices[10] - 0.5*x[0]*vertices[4] + 0.5*x[0]*vertices[13] - 0.5*x[1]*vertices[7] + 0.5*x[1]*vertices[16] rcol[2] = -0.5*(1 - x[2])*vertices[2] + 0.5*(1 - x[2])*vertices[5] - 0.5*(1 + x[2])*vertices[11] + 0.5*(1 + x[2])*vertices[14] scol[2] = -0.5*(1 - x[2])*vertices[2] + 0.5*(1 - x[2])*vertices[8] - 0.5*(1 + x[2])*vertices[11] + 0.5*(1 + x[2])*vertices[17] tcol[2] = -0.5*(1 - x[0] - x[1])*vertices[2] + 0.5*(1 - x[0] - x[1])*vertices[11] - 0.5*x[0]*vertices[5] + 0.5*x[0]*vertices[14] - 0.5*x[1]*vertices[8] + 0.5*x[1]*vertices[17] yt-project-yt-f043ac8/yt/utilities/lib/basic_octree.pyx000066400000000000000000000567301510711153200232660ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ A refine-by-two AMR-specific octree """ import numpy as np cimport cython # Double up here for def'd functions cimport numpy as np cimport numpy as cnp from libc.stdlib cimport free, malloc from yt.utilities.lib.fp_utils cimport imax import sys cdef extern from "platform_dep.h": # NOTE that size_t might not be int void *alloca(int) cdef extern from "math.h": double sqrt(double x) cdef inline np.float64_t f64max(np.float64_t f0, np.float64_t f1): if f0 > f1: return f0 return f1 cdef struct OctreeNode: np.float64_t *val np.float64_t weight_val np.int64_t pos[3] np.uint64_t level int nvals int max_level # The maximum level under this node with mass. OctreeNode *children[2][2][2] OctreeNode *parent OctreeNode *next OctreeNode *up_next cdef void OTN_add_value(OctreeNode *self, np.float64_t *val, np.float64_t weight_val, int level, int treecode): cdef int i for i in range(self.nvals): self.val[i] += val[i] self.weight_val += weight_val if treecode and val[0] > 0.: self.max_level = imax(self.max_level, level) cdef void OTN_refine(OctreeNode *self, int incremental = 0): cdef int i, j, k cdef np.int64_t npos[3] for i in range(2): npos[0] = self.pos[0] * 2 + i for j in range(2): npos[1] = self.pos[1] * 2 + j # We have to be careful with allocation... for k in range(2): npos[2] = self.pos[2] * 2 + k self.children[i][j][k] = OTN_initialize( npos, self.nvals, self.val, self.weight_val, self.level + 1, self, incremental) if incremental: return for i in range(self.nvals): self.val[i] = 0.0 self.weight_val = 0.0 cdef OctreeNode *OTN_initialize(np.int64_t pos[3], int nvals, np.float64_t *val, np.float64_t weight_val, int level, OctreeNode *parent, int incremental = 0): cdef OctreeNode *node cdef int i, j, k node = malloc(sizeof(OctreeNode)) node.pos[0] = pos[0] node.pos[1] = pos[1] node.pos[2] = pos[2] node.nvals = nvals node.parent = parent node.next = NULL node.up_next = NULL node.max_level = 0 node.val = malloc( nvals * sizeof(np.float64_t)) if incremental: for i in range(nvals): node.val[i] = 0. node.weight_val = 0. else: for i in range(nvals): node.val[i] = val[i] node.weight_val = weight_val for i in range(2): for j in range(2): for k in range(2): node.children[i][j][k] = NULL node.level = level return node cdef void OTN_free(OctreeNode *node): cdef int i, j, k for i in range(2): for j in range(2): for k in range(2): if node.children[i][j][k] == NULL: continue OTN_free(node.children[i][j][k]) free(node.val) free(node) cdef class Octree: cdef int nvals cdef np.int64_t po2[80] cdef OctreeNode ****root_nodes cdef np.int64_t top_grid_dims[3] cdef int incremental # Below is for the treecode. cdef np.float64_t opening_angle # We'll store dist here so it doesn't have to be calculated twice. cdef np.float64_t dist cdef np.float64_t root_dx[3] cdef OctreeNode *last_node def __cinit__(self, np.ndarray[np.int64_t, ndim=1] top_grid_dims, int nvals, int incremental = False): cdef np.uint64_t i, j, k self.incremental = incremental cdef np.int64_t pos[3] cdef np.float64_t *vals = alloca( sizeof(np.float64_t)*nvals) cdef np.float64_t weight_val = 0.0 self.nvals = nvals for i in range(nvals): vals[i] = 0.0 self.top_grid_dims[0] = top_grid_dims[0] self.top_grid_dims[1] = top_grid_dims[1] self.top_grid_dims[2] = top_grid_dims[2] # This wouldn't be necessary if we did bitshifting... for i in range(80): self.po2[i] = 2**i # Cython doesn't seem to like sizeof(OctreeNode ***) self.root_nodes = \ malloc(sizeof(void*) * top_grid_dims[0]) # We initialize our root values to 0.0. for i in range(top_grid_dims[0]): pos[0] = i self.root_nodes[i] = \ malloc(sizeof(OctreeNode **) * top_grid_dims[1]) for j in range(top_grid_dims[1]): pos[1] = j self.root_nodes[i][j] = \ malloc(sizeof(OctreeNode *) * top_grid_dims[1]) for k in range(top_grid_dims[2]): pos[2] = k self.root_nodes[i][j][k] = OTN_initialize( pos, nvals, vals, weight_val, 0, NULL) cdef void add_to_position(self, int level, np.int64_t pos[3], np.float64_t *val, np.float64_t weight_val, treecode): cdef int i, j, k, L cdef OctreeNode *node node = self.find_on_root_level(pos, level) cdef np.int64_t fac for L in range(level): if self.incremental: OTN_add_value(node, val, weight_val, level, treecode) if node.children[0][0][0] == NULL: OTN_refine(node, self.incremental) # Maybe we should use bitwise operators? fac = self.po2[level - L - 1] i = (pos[0] >= fac*(2*node.pos[0]+1)) j = (pos[1] >= fac*(2*node.pos[1]+1)) k = (pos[2] >= fac*(2*node.pos[2]+1)) node = node.children[i][j][k] OTN_add_value(node, val, weight_val, level, treecode) cdef OctreeNode *find_on_root_level(self, np.int64_t pos[3], int level): # We need this because the root level won't just have four children # So we find on the root level, then we traverse the tree. cdef np.int64_t i, j, k i = (pos[0] / self.po2[level]) j = (pos[1] / self.po2[level]) k = (pos[2] / self.po2[level]) return self.root_nodes[i][j][k] @cython.boundscheck(False) @cython.wraparound(False) def add_array_to_tree(self, int level, np.ndarray[np.int64_t, ndim=1] pxs, np.ndarray[np.int64_t, ndim=1] pys, np.ndarray[np.int64_t, ndim=1] pzs, np.ndarray[np.float64_t, ndim=2] pvals, np.ndarray[np.float64_t, ndim=1] pweight_vals, int treecode = 0): cdef int npx = pxs.shape[0] cdef int p cdef cnp.float64_t *vals cdef cnp.float64_t *data = pvals.data cdef cnp.int64_t pos[3] for p in range(npx): vals = data + self.nvals*p pos[0] = pxs[p] pos[1] = pys[p] pos[2] = pzs[p] self.add_to_position(level, pos, vals, pweight_vals[p], treecode) def add_grid_to_tree(self, int level, np.ndarray[np.int64_t, ndim=1] start_index, np.ndarray[np.float64_t, ndim=2] pvals, np.ndarray[np.float64_t, ndim=2] wvals, np.ndarray[np.int32_t, ndim=2] cm): pass @cython.boundscheck(False) @cython.wraparound(False) def get_all_from_level(self, int level, int count_only = 0): cdef int i, j, k cdef int total = 0 for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for k in range(self.top_grid_dims[2]): total += self.count_at_level(self.root_nodes[i][j][k], level) if count_only: return total # Allocate our array cdef np.ndarray[np.int64_t, ndim=2] npos cdef np.ndarray[np.float64_t, ndim=2] nvals cdef np.ndarray[np.float64_t, ndim=1] nwvals npos = np.zeros( (total, 3), dtype='int64') nvals = np.zeros( (total, self.nvals), dtype='float64') nwvals = np.zeros( total, dtype='float64') cdef np.int64_t curpos = 0 cdef np.int64_t *pdata = npos.data cdef np.float64_t *vdata = nvals.data cdef np.float64_t *wdata = nwvals.data for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for k in range(self.top_grid_dims[2]): curpos += self.fill_from_level(self.root_nodes[i][j][k], level, curpos, pdata, vdata, wdata) return npos, nvals, nwvals cdef int count_at_level(self, OctreeNode *node, int level): cdef int i, j, k # We only really return a non-zero, calculated value if we are at the # level in question. if node.level == level: if self.incremental: return 1 # We return 1 if there are no finer points at this level and zero # if there are return (node.children[0][0][0] == NULL) if node.children[0][0][0] == NULL: return 0 cdef int count = 0 for i in range(2): for j in range(2): for k in range(2): count += self.count_at_level(node.children[i][j][k], level) return count cdef int fill_from_level(self, OctreeNode *node, int level, np.int64_t curpos, np.int64_t *pdata, np.float64_t *vdata, np.float64_t *wdata): cdef int i, j, k if node.level == level: if node.children[0][0][0] != NULL and not self.incremental: return 0 for i in range(self.nvals): vdata[self.nvals * curpos + i] = node.val[i] wdata[curpos] = node.weight_val pdata[curpos * 3] = node.pos[0] pdata[curpos * 3 + 1] = node.pos[1] pdata[curpos * 3 + 2] = node.pos[2] return 1 if node.children[0][0][0] == NULL: return 0 cdef np.int64_t added = 0 for i in range(2): for j in range(2): for k in range(2): added += self.fill_from_level(node.children[i][j][k], level, curpos + added, pdata, vdata, wdata) return added @cython.cdivision(True) cdef np.float64_t fbe_node_separation(self, OctreeNode *node1, OctreeNode *node2): # Find the distance between the two nodes. cdef np.float64_t dx1, dx2, p1, p2, dist cdef int i dist = 0.0 for i in range(3): # Discover the appropriate dx for each node/dim. dx1 = self.root_dx[i] / ( self.po2[node1.level]) dx2 = self.root_dx[i] / ( self.po2[node2.level]) # The added term is to re-cell center the data. p1 = ( node1.pos[i]) * dx1 + dx1/2. p2 = ( node2.pos[i]) * dx2 + dx2/2. dist += (p1 - p2) * (p1 - p2) dist = sqrt(dist) return dist @cython.cdivision(True) cdef np.float64_t fbe_opening_angle(self, OctreeNode *node1, OctreeNode *node2): # Calculate the opening angle of node2 upon the center of node1. # In order to keep things simple, we will not assume symmetry in all # three directions of the octree, and we'll use the largest dimension # if the tree is not symmetric. This is not strictly the opening angle # the purest sense, but it's slightly more accurate, so it's OK. # This is done in code units to match the distance calculation. cdef np.float64_t d2, dx2, dist cdef np.int64_t n2 cdef int i d2 = 0.0 if node1 is node2: return 100000.0 # Just some large number. if self.top_grid_dims[1] == self.top_grid_dims[0] and \ self.top_grid_dims[2] == self.top_grid_dims[0]: # Symmetric n2 = self.po2[node2.level] * self.top_grid_dims[0] d2 = 1. / ( n2) else: # Not symmetric for i in range(3): n2 = self.po2[node2.level] * self.top_grid_dims[i] dx2 = 1. / ( n2) d2 = f64max(d2, dx2) # Now calculate the opening angle. dist = self.fbe_node_separation(node1, node2) self.dist = dist return d2 / dist cdef void set_next(self, OctreeNode *node, int treecode): # This sets up the linked list, pointing node.next to the next node # in the iteration order. cdef int i, j, k if treecode and node.val[0] is not 0.: # In a treecode, we only make a new next link if this node has mass. self.last_node.next = node self.last_node = node elif treecode and node.val[0] is 0.: # No mass means it's children have no mass, no need to dig an # further. return else: # We're not doing the treecode, but we still want a linked list, # we don't care about val[0] necessarily. self.last_node.next = node self.last_node = node if node.children[0][0][0] is NULL: return for i in range(2): for j in range(2): for k in range(2): self.set_next(node.children[i][j][k], treecode) return cdef void set_up_next(self, OctreeNode *node): # This sets up a second linked list, pointing node.up_next to the next # node in the list that is at the same or lower (coarser) level than # this node. This is useful in the treecode for skipping over nodes # that don't need to be inspected. cdef OctreeNode *initial_next cdef OctreeNode *temp_next initial_next = node.next temp_next = node.next if node.next is NULL: return while temp_next.level > node.level: temp_next = temp_next.next if temp_next is NULL: break node.up_next = temp_next self.set_up_next(initial_next) def finalize(self, int treecode = 0): # Set up the linked list for the nodes. # Set treecode = 1 if nodes with no mass are to be skipped in the # list. cdef int i, j, k, sum, top_grid_total, ii, jj, kk self.last_node = self.root_nodes[0][0][0] for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for k in range(self.top_grid_dims[2]): self.set_next(self.root_nodes[i][j][k], treecode) # Now we want to link to the next node in the list that is # on a level the same or lower (coarser) than us. This will be used # during a treecode search so we can skip higher-level (finer) nodes. sum = 1 top_grid_total = self.top_grid_dims[0] * self.top_grid_dims[1] * \ self.top_grid_dims[2] for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for k in range(self.top_grid_dims[2]): self.set_up_next(self.root_nodes[i][j][k]) # Point the root_nodes.up_next to the next root_node in the # list, except for the last one which stays pointing to NULL. if sum < top_grid_total - 1: ii = i jj = j kk = (k + 1) % self.top_grid_dims[2] if kk < k: jj = (j + 1) % self.top_grid_dims[1] if jj < j: ii = (i + 1) % self.top_grid_dims[0] self.root_nodes[i][j][k].up_next = \ self.root_nodes[ii][jj][kk] sum += 1 @cython.cdivision(True) cdef np.float64_t fbe_main(self, np.float64_t potential, int truncate, np.float64_t kinetic): # The work is done here. Starting at the top of the linked list of # nodes, cdef np.float64_t angle, dist cdef OctreeNode *this_node cdef OctreeNode *pair_node this_node = self.root_nodes[0][0][0] while this_node is not NULL: # Iterate down the list to a node that either has no children and # is at the max_level of the tree, or to a node where # all of its children are massless. The second case is when data # from a level that isn't the deepest has been added to the tree. while this_node.max_level is not this_node.level: this_node = this_node.next # In case we reach the end of the list... if this_node is NULL: break if this_node is NULL: break if truncate and potential > kinetic: print('Truncating...') break pair_node = this_node.next while pair_node is not NULL: # If pair_node is massless, we can jump to up_next, because # nothing pair_node contains will have mass either. # I think that this should primarily happen for root_nodes # created for padding to make the region cubical. if pair_node.val[0] is 0.0: pair_node = pair_node.up_next continue # If pair_node is a childless node, or is a coarser node with # no children, we can calculate the pot # right now, and get a new pair_node. if pair_node.max_level is pair_node.level: dist = self.fbe_node_separation(this_node, pair_node) potential += this_node.val[0] * pair_node.val[0] / dist if truncate and potential > kinetic: break pair_node = pair_node.next continue # Next, if the opening angle to pair_node is small enough, # calculate the potential and get a new pair_node using # up_next because we don't need to look at pair_node's children. angle = self.fbe_opening_angle(this_node, pair_node) if angle < self.opening_angle: # self.dist was just set in fbe_opening_angle, so we # can use it here without re-calculating it for these two # nodes. potential += this_node.val[0] * pair_node.val[0] / self.dist if truncate and potential > kinetic: break # We can skip all the nodes that are contained within # pair_node, saving time walking the linked list. pair_node = pair_node.up_next # If we've gotten this far, pair_node has children, but it's # too coarse, so we simply dig deeper using .next. else: pair_node = pair_node.next # We've exhausted the pair_nodes. # Now we find a new this_node in the list, and do more searches # over pair_node. this_node = this_node.next return potential @cython.boundscheck(False) @cython.wraparound(False) def find_binding_energy(self, int truncate, np.float64_t kinetic, np.ndarray[np.float64_t, ndim=1] root_dx, float opening_angle = 1.0): r"""Find the binding energy of an ensemble of data points using the treecode method. Note: The first entry of the vals array MUST be Mass. Any other values will be ignored, including the weight array. """ # The real work is done in fbe_main(), this just sets things up # and returns the potential. cdef int i cdef np.float64_t potential potential = 0.0 self.opening_angle = opening_angle for i in range(3): self.root_dx[i] = root_dx[i] potential = self.fbe_main(potential, truncate, kinetic) return potential cdef int node_ID(self, OctreeNode *node): # Returns an unique ID for this node based on its position and level. cdef int ID, offset, root cdef np.uint64_t i cdef np.int64_t this_grid_dims[3] offset = 0 root = 1 for i in range(3): root *= self.top_grid_dims[i] this_grid_dims[i] = self.top_grid_dims[i] * 2**node.level for i in range(node.level): offset += root * 2**(3 * i) ID = offset + (node.pos[0] + this_grid_dims[0] * (node.pos[1] + \ this_grid_dims[1] * node.pos[2])) return ID cdef int node_ID_on_level(self, OctreeNode *node): # Returns the node ID on node.level for this node. cdef int ID, i cdef np.int64_t this_grid_dims[3] for i in range(3): this_grid_dims[i] = self.top_grid_dims[i] * 2**node.level ID = node.pos[0] + this_grid_dims[0] * (node.pos[1] + \ this_grid_dims[1] * node.pos[2]) return ID cdef void print_node_info(self, OctreeNode *node): cdef int i, j, k line = "%d\t" % self.node_ID(node) if node.next is not NULL: line += "%d\t" % self.node_ID(node.next) else: line += "-1\t" if node.up_next is not NULL: line += "%d\t" % self.node_ID(node.up_next) else: line += "-1\t" line += "%d\t%d\t%d\t%d\t" % (node.level,node.pos[0],node.pos[1],node.pos[2]) for i in range(node.nvals): line += "%1.5e\t" % node.val[i] line += "%f\t" % node.weight_val line += "%s\t%s\t" % (node.children[0][0][0] is not NULL, node.parent is not NULL) if node.children[0][0][0] is not NULL: nline = "" for i in range(2): for j in range(2): for k in range(2): nline += "%d," % self.node_ID(node.children[i][j][k]) line += nline print(line) return cdef void iterate_print_nodes(self, OctreeNode *node): cdef int i, j, k self.print_node_info(node) if node.children[0][0][0] is NULL: return for i in range(2): for j in range(2): for k in range(2): self.iterate_print_nodes(node.children[i][j][k]) return def print_all_nodes(self): r""" Prints out information about all the nodes in the octree. Parameters ---------- None. Examples -------- >>> octree.print_all_nodes() (many lines of data) """ cdef int i, j, k sys.stdout.flush() sys.stderr.flush() line = "ID\tnext\tup_n\tlevel\tx\ty\tz\t" for i in range(self.nvals): line += "val%d\t\t" % i line += "weight\t\tchild?\tparent?\tchildren" print(line) for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for k in range(self.top_grid_dims[2]): self.iterate_print_nodes(self.root_nodes[i][j][k]) sys.stdout.flush() sys.stderr.flush() return def __dealloc__(self): cdef int i, j, k for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for k in range(self.top_grid_dims[2]): OTN_free(self.root_nodes[i][j][k]) free(self.root_nodes[i][j]) free(self.root_nodes[i]) free(self.root_nodes) yt-project-yt-f043ac8/yt/utilities/lib/bitarray.pxd000066400000000000000000000067361510711153200224350ustar00rootroot00000000000000""" Bit array functions """ import numpy as np cimport cython cimport numpy as np cdef inline void ba_set_value(np.uint8_t *buf, np.uint64_t ind, np.uint8_t val) noexcept nogil: # This assumes 8 bit buffer. If value is greater than zero (thus allowing # us to use 1-255 as 'True') then we identify first the index in the buffer # we are setting. We do this by truncating the index by bit-shifting to # the left three times, essentially dividing it by eight (and taking the # floor.) # The next step is to turn *on* what we're attempting to turn on, which # means taking our index and truncating it to the first 3 bits (which we do # with an & operation) and then turning on the correct bit. # # So if we're asking for index 33 in the bitarray, we would want the 4th # uint8 element, then the 2nd bit (index 1). # # To turn it on, we logical *or* with that. To turn it off, we logical # *and* with the *inverse*, which will allow everything *but* that bit to # stay on. if val > 0: buf[ind >> 3] |= (1 << (ind & 7)) else: buf[ind >> 3] &= ~(1 << (ind & 7)) cdef inline np.uint8_t ba_get_value(np.uint8_t *buf, np.uint64_t ind) noexcept nogil: cdef np.uint8_t rv = (buf[ind >> 3] & (1 << (ind & 7))) if rv == 0: return 0 return 1 cdef inline void ba_set_range(np.uint8_t *buf, np.uint64_t start_ind, np.uint64_t stop_ind, np.uint8_t val) nogil: # Should this be inclusive of both end points? I think it should not, to # match slicing semantics. # # We need to figure out the first and last values, and then we just set the # ones in-between to 255. if stop_ind < start_ind: return cdef np.uint64_t i cdef np.uint8_t j, bitmask cdef np.uint64_t buf_start = start_ind >> 3 cdef np.uint64_t buf_stop = stop_ind >> 3 cdef np.uint8_t start_j = start_ind & 7 cdef np.uint8_t stop_j = stop_ind & 7 if buf_start == buf_stop: for j in range(start_j, stop_j): ba_set_value(&buf[buf_start], j, val) return bitmask = 0 for j in range(start_j, 8): bitmask |= (1 << j) if val > 0: buf[buf_start] |= bitmask else: buf[buf_start] &= ~bitmask if val > 0: bitmask = 255 else: bitmask = 0 for i in range(buf_start + 1, buf_stop): buf[i] = bitmask bitmask = 0 for j in range(0, stop_j): bitmask |= (1 << j) if val > 0: buf[buf_stop] |= bitmask else: buf[buf_stop] &= ~bitmask cdef inline np.uint8_t _num_set_bits( np.uint8_t b ): # https://stackoverflow.com/questions/30688465/how-to-check-the-number-of-set-bits-in-an-8-bit-unsigned-char b = b - ((b >> 1) & 0x55) b = (b & 0x33) + ((b >> 2) & 0x33) return (((b + (b >> 4)) & 0x0F) * 0x01) cdef class bitarray: cdef np.uint8_t *buf cdef np.uint64_t size cdef np.uint64_t buf_size # Not exactly the same cdef np.uint8_t final_bitmask cdef public object ibuf cdef void _set_value(self, np.uint64_t ind, np.uint8_t val) cdef np.uint8_t _query_value(self, np.uint64_t ind) cdef void _set_range(self, np.uint64_t start, np.uint64_t stop, np.uint8_t val) cdef np.uint64_t _count(self) cdef bitarray _logical_and(self, bitarray other, bitarray result = *) cdef bitarray _logical_or(self, bitarray other, bitarray result = *) cdef bitarray _logical_xor(self, bitarray other, bitarray result = *) yt-project-yt-f043ac8/yt/utilities/lib/bitarray.pyx000066400000000000000000000210341510711153200224460ustar00rootroot00000000000000# distutils: libraries = STD_LIBS """ Bit array functions """ import numpy as np cimport cython cimport numpy as np cdef class bitarray: @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __cinit__(self, np.int64_t size = -1, np.ndarray[np.uint8_t, ndim=1, cast=True] arr = None): r"""This is a bitarray, which flips individual bits to on/off inside a uint8 container array. By encoding on/off inside each bit in a uint8 array, we can compress boolean information down by up to a factor of 8. Either an input array or a size must be provided. Parameters ---------- size : int The size we should pre-allocate. arr : array-like An input array to turn into a bitarray. Examples -------- >>> arr_in1 = np.array([True, True, False]) >>> arr_in2 = np.array([False, True, True]) >>> a = ba.bitarray(arr = arr_in1) >>> b = ba.bitarray(arr = arr_in2) >>> print(a & b) >>> print (a & b).as_bool_array() """ cdef np.uint64_t i if size == -1 and arr is None: raise RuntimeError elif size == -1: size = arr.size elif size != -1 and arr is not None: if size != arr.size: raise RuntimeError self.buf_size = (size >> 3) cdef np.uint8_t bitmask = 255 if (size & 7) != 0: # We need an extra one if we've got any lingering bits self.buf_size += 1 bitmask = 0 for i in range(size & 7): bitmask |= (1< ibuf_t.data self.size = size if arr is not None: self.set_from_array(arr) else: for i in range(self.buf_size): self.buf[i] = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def set_from_array(self, np.ndarray[np.uint8_t, cast=True] arr not None): r"""Given an array that is either uint8_t or boolean, set the values of this array to match it. Parameters ---------- arr : array, castable to uint8 The array we set from. """ cdef np.uint64_t i, j cdef np.uint8_t *btemp = self.buf arr = np.ascontiguousarray(arr) j = 0 for i in range(self.size): btemp[i >> 3] = btemp[i >> 3] | (arr[i] << (j)) j += 1 if j == 8: j = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def as_bool_array(self): r"""Return a copy of this array, as a boolean array. All of the values encoded in this bitarray are expanded into boolean values in a new array and returned. Returns ------- arr : numpy array of type bool The uint8 values expanded into boolean values """ cdef np.uint64_t i, j cdef np.uint8_t *btemp = self.buf cdef np.ndarray[np.uint8_t, ndim=1] output output = np.zeros(self.size, "uint8") j = 0 for i in range(self.size): output[i] = (btemp[i >> 3] >> (j)) & 1 j += 1 if j == 8: j = 0 return output.astype("bool") cdef void _set_value(self, np.uint64_t ind, np.uint8_t val): ba_set_value(self.buf, ind, val) def set_value(self, np.uint64_t ind, np.uint8_t val): r"""Set the on/off value of a given bit. Modify the value encoded in a given index. Parameters ---------- ind : int The index to query in the bitarray. val : bool or uint8_t What to set the index to Examples -------- >>> arr_in = np.array([True, True, False]) >>> a = ba.bitarray(arr = arr_in) >>> print(a.set_value(2, 1)) """ ba_set_value(self.buf, ind, val) cdef np.uint8_t _query_value(self, np.uint64_t ind): return ba_get_value(self.buf, ind) def query_value(self, np.uint64_t ind): r"""Query the on/off value of a given bit. Return the value encoded in a given index. Parameters ---------- ind : int The index to query in the bitarray. Examples -------- >>> arr_in = np.array([True, True, False]) >>> a = ba.bitarray(arr = arr_in) >>> print(a.query_value(2)) """ return ba_get_value(self.buf, ind) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void _set_range(self, np.uint64_t start, np.uint64_t stop, np.uint8_t val): ba_set_range(self.buf, start, stop, val) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def set_range(self, np.uint64_t start, np.uint64_t stop, np.uint8_t val): r"""Set a range of values to on/off. Uses slice-style indexing. No return value. Parameters ---------- start : int The starting component of a slice. stop : int The ending component of a slice. val : bool or uint8_t What to set the range to Examples -------- >>> arr_in = np.array([True, True, False, True, True, False]) >>> a = ba.bitarray(arr = arr_in) >>> a.set_range(0, 3, 0) """ ba_set_range(self.buf, start, stop, val) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.uint64_t _count(self): cdef np.uint64_t count = 0 cdef np.uint64_t i self.buf[self.buf_size - 1] &= self.final_bitmask for i in range(self.buf_size): count += _num_set_bits(self.buf[i]) return count @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def count(self): r"""Count the number of values set in the array. Parameters ---------- Examples -------- >>> arr_in = np.array([True, True, False, True, True, False]) >>> a = ba.bitarray(arr = arr_in) >>> a.count() """ return self._count() cdef bitarray _logical_and(self, bitarray other, bitarray result = None): # Create a place to put it. Note that we might have trailing values, # we actually need to reset the ending set. if other.size != self.size: raise IndexError if result is None: result = bitarray(self.size) for i in range(self.buf_size): result.buf[i] = other.buf[i] & self.buf[i] result.buf[self.buf_size - 1] &= self.final_bitmask return result def logical_and(self, bitarray other, bitarray result = None): return self._logical_and(other, result) def __and__(self, bitarray other): # Wrap it directly here. return self.logical_and(other) def __iand__(self, bitarray other): rv = self.logical_and(other, self) return rv cdef bitarray _logical_or(self, bitarray other, bitarray result = None): if other.size != self.size: raise IndexError if result is None: result = bitarray(self.size) for i in range(self.buf_size): result.buf[i] = other.buf[i] | self.buf[i] result.buf[self.buf_size - 1] &= self.final_bitmask return result def logical_or(self, bitarray other, bitarray result = None): return self._logical_or(other, result) def __or__(self, bitarray other): return self.logical_or(other) def __ior__(self, bitarray other): return self.logical_or(other, self) cdef bitarray _logical_xor(self, bitarray other, bitarray result = None): if other.size != self.size: raise IndexError if result is None: result = bitarray(self.size) for i in range(self.buf_size): result.buf[i] = other.buf[i] ^ self.buf[i] result.buf[self.buf_size - 1] &= self.final_bitmask return result def logical_xor(self, bitarray other, bitarray result = None): return self._logical_xor(other, result) def __xor__(self, bitarray other): return self.logical_xor(other) def __ixor__(self, bitarray other): return self.logical_xor(other, self) yt-project-yt-f043ac8/yt/utilities/lib/bounded_priority_queue.pxd000066400000000000000000000023771510711153200254020ustar00rootroot00000000000000""" A cython implementation of the bounded priority queue This is a priority queue that only keeps track of smallest k values that have been added to it. """ import numpy as np cimport numpy as np cdef class BoundedPriorityQueue: cdef public np.float64_t[:] heap cdef np.float64_t* heap_ptr cdef public np.int64_t[:] pids cdef np.int64_t* pids_ptr cdef int use_pids cdef np.intp_t size cdef np.intp_t max_elements cdef int max_heapify(self, np.intp_t index) except -1 nogil cdef int propagate_up(self, np.intp_t index) except -1 nogil cdef int add(self, np.float64_t val) except -1 nogil cdef int add_pid(self, np.float64_t val, np.int64_t pid) except -1 nogil cdef int heap_append(self, np.float64_t val, np.int64_t ind) except -1 nogil cdef np.float64_t extract_max(self) except -1 nogil cdef int validate_heap(self) except -1 nogil cdef class NeighborList: cdef public np.float64_t[:] data cdef np.float64_t* data_ptr cdef public np.int64_t[:] pids cdef np.int64_t* pids_ptr cdef np.intp_t size cdef np.intp_t _max_size cdef int _update_memview(self) except -1 cdef int _extend(self) except -1 nogil cdef int add_pid(self, np.float64_t val, np.int64_t ind) except -1 nogil yt-project-yt-f043ac8/yt/utilities/lib/bounded_priority_queue.pyx000066400000000000000000000174711510711153200254300ustar00rootroot00000000000000""" A cython implementation of the bounded priority queue This is a priority queue that only keeps track of smallest k values that have been added to it. This priority queue is implemented with the configuration of having the largest element at the beginning - this exploited to store nearest neighbour lists. """ import numpy as np cimport cython cimport numpy as np from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc cdef class BoundedPriorityQueue: def __cinit__(self, np.intp_t max_elements, np.intp_t pids=0): self.max_elements = max_elements # mark invalid recently values with -1 self.heap = np.zeros(max_elements)-1 self.heap_ptr = &(self.heap[0]) # only allocate memory if we intend to store particle ID's self.use_pids = pids if pids == 1: self.pids = np.zeros(max_elements, dtype="int64")-1 self.pids_ptr = &(self.pids[0]) self.size = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int max_heapify(self, np.intp_t index) except -1 nogil: cdef np.intp_t left = 2 * index + 1 cdef np.intp_t right = 2 * index + 2 cdef np.intp_t largest = index if left < self.size and self.heap_ptr[left] > self.heap_ptr[largest]: largest = left if right < self.size and self.heap_ptr[right] > self.heap_ptr[largest]: largest = right if largest != index: self.heap_ptr[index], self.heap_ptr[largest] = \ self.heap_ptr[largest], self.heap_ptr[index] if self.use_pids: self.pids_ptr[index], self.pids_ptr[largest] = \ self.pids_ptr[largest], self.pids_ptr[index] self.max_heapify(largest) return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int propagate_up(self, np.intp_t index) except -1 nogil: while index != 0 and self.heap_ptr[(index - 1) // 2] < self.heap_ptr[index]: self.heap_ptr[index], self.heap_ptr[(index - 1) // 2] = self.heap_ptr[(index - 1) // 2], self.heap_ptr[index] if self.use_pids: self.pids_ptr[index], self.pids_ptr[(index - 1) // 2] = self.pids_ptr[(index - 1) // 2], self.pids_ptr[index] index = (index - 1) // 2 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int add(self, np.float64_t val) except -1 nogil: # if not at max size append, if at max size, only append if smaller than # the maximum value if self.size == self.max_elements: if val < self.heap_ptr[0]: self.extract_max() self.heap_append(val, -1) else: self.heap_append(val, -1) return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int add_pid(self, np.float64_t val, np.int64_t ind) except -1 nogil: if self.size == self.max_elements: if val < self.heap_ptr[0]: self.extract_max() self.heap_append(val, ind) else: self.heap_append(val, ind) return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int heap_append(self, np.float64_t val, np.int64_t ind) except -1 nogil: self.heap_ptr[self.size] = val if self.use_pids: self.pids_ptr[self.size] = ind self.size += 1 self.propagate_up(self.size - 1) return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef np.float64_t extract_max(self) except -1 nogil: cdef np.float64_t maximum = self.heap_ptr[0] cdef np.float64_t val cdef np.int64_t ind val = self.heap_ptr[self.size-1] self.heap_ptr[self.size-1] = -1 if self.use_pids: ind = self.pids_ptr[self.size-1] self.pids_ptr[self.size-1] = -1 self.size -= 1 if self.size > 0: self.heap_ptr[0] = val if self.use_pids: self.pids_ptr[0] = ind self.max_heapify(0) return maximum cdef int validate_heap(self) except -1 nogil: # this function loops through every element in the heap, if any children # are greater than their parents then we return zero, which is an error # as the heap condition is not satisfied cdef int i, index for i in range(self.size-1, -1, -1): index = i while index != 0: if self.heap_ptr[index] > self.heap_ptr[(index - 1) // 2]: return 0 index = (index - 1) // 2 return 1 cdef class NeighborList: def __cinit__(self, np.intp_t init_size=32): self.size = 0 self._max_size = init_size self.data_ptr = PyMem_Malloc( self._max_size * sizeof(np.float64_t) ) self.pids_ptr = PyMem_Malloc( self._max_size * sizeof(np.int64_t) ) self._update_memview() def __dealloc__(self): PyMem_Free(self.data_ptr) PyMem_Free(self.pids_ptr) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int _update_memview(self) except -1: self.data = self.data_ptr self.pids = self.pids_ptr @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int _extend(self) except -1 nogil: if self.size == self._max_size: self._max_size *= 2 with gil: self.data_ptr = PyMem_Realloc( self.data_ptr, self._max_size * sizeof(np.float64_t) ) self.pids_ptr = PyMem_Realloc( self.pids_ptr, self._max_size * sizeof(np.int64_t) ) self._update_memview() return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) cdef int add_pid(self, np.float64_t val, np.int64_t ind) except -1 nogil: self._extend() self.data_ptr[self.size] = val self.pids_ptr[self.size] = ind self.size += 1 return 0 # these are test functions which are called from # yt/utilities/lib/tests/test_nn.py # they are stored here to allow easy interaction with functions not exposed at # the python level def validate_pid(): m = BoundedPriorityQueue(5, True) # Add elements to the queue elements = [0.1, 0.25, 1.33, 0.5, 3.2, 4.6, 2.0, 0.4, 4.0, .001] pids = [1,2,3,4,5,6,7,8,9,10] for el, pid in zip(elements, pids): m.add_pid(el, pid) m.extract_max() m.extract_max() m.extract_max() return np.asarray(m.heap), np.asarray(m.pids) def validate(): m = BoundedPriorityQueue(5) # Add elements to the queue for el in [0.1, 0.25, 1.33, 0.5, 3.2, 4.6, 2.0, 0.4, 4.0, .001]: m.add(el) m.extract_max() m.extract_max() m.extract_max() return np.asarray(m.heap) def validate_nblist(): nblist = NeighborList(init_size=2) for i in range(4): nblist.add_pid(1.0, i) # Copy is necessary here. Without it, the allocated memory would be freed. # Leaving random data array. return np.asarray(nblist.data).copy(), np.asarray(nblist.pids).copy() yt-project-yt-f043ac8/yt/utilities/lib/bounding_volume_hierarchy.pxd000066400000000000000000000060071510711153200260410ustar00rootroot00000000000000cimport cython import numpy as np cimport numpy as np from yt.utilities.lib.element_mappings cimport ElementSampler from yt.utilities.lib.primitives cimport BBox, Ray cdef extern from "mesh_triangulation.h": enum: MAX_NUM_TRI int HEX_NV int HEX_NT int TETRA_NV int TETRA_NT int WEDGE_NV int WEDGE_NT int triangulate_hex[MAX_NUM_TRI][3] int triangulate_tetra[MAX_NUM_TRI][3] int triangulate_wedge[MAX_NUM_TRI][3] int hex20_faces[6][8] int tet10_faces[4][6] # node for the bounding volume hierarchy cdef struct BVHNode: np.int64_t begin np.int64_t end BVHNode* left BVHNode* right BBox bbox # pointer to function that computes primitive intersection ctypedef np.int64_t (*intersect_func_type)(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil # pointer to function that computes primitive centroids ctypedef void (*centroid_func_type)(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil # pointer to function that computes primitive bounding boxes ctypedef void (*bbox_func_type)(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil cdef class BVH: cdef BVHNode* root cdef void* primitives cdef np.int64_t* prim_ids cdef np.float64_t** centroids cdef BBox* bboxes cdef np.float64_t* vertices cdef np.float64_t* field_data cdef np.int64_t num_prim_per_elem cdef np.int64_t num_prim cdef np.int64_t num_elem cdef np.int64_t num_verts_per_elem cdef np.int64_t num_field_per_elem cdef int[MAX_NUM_TRI][3] tri_array cdef ElementSampler sampler cdef centroid_func_type get_centroid cdef bbox_func_type get_bbox cdef intersect_func_type get_intersect cdef np.int64_t _partition(self, np.int64_t begin, np.int64_t end, np.int64_t ax, np.float64_t split) noexcept nogil cdef void _set_up_triangles(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices) noexcept nogil cdef void _set_up_patches(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices) noexcept nogil cdef void _set_up_tet_patches(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices) noexcept nogil cdef void intersect(self, Ray* ray) noexcept nogil cdef void _get_node_bbox(self, BVHNode* node, np.int64_t begin, np.int64_t end) noexcept nogil cdef void _recursive_intersect(self, Ray* ray, BVHNode* node) noexcept nogil cdef BVHNode* _recursive_build(self, np.int64_t begin, np.int64_t end) noexcept nogil cdef void _recursive_free(self, BVHNode* node) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/bounding_volume_hierarchy.pyx000066400000000000000000000446701510711153200260760ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: include_dirs = LIB_DIR # distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG OMP_ARGS # distutils: extra_link_args = CPP14_FLAG OMP_ARGS cimport cython import numpy as np cimport numpy as np from libc.math cimport fabs from libc.stdlib cimport free, malloc from cython.parallel import parallel, prange from yt.utilities.lib.element_mappings cimport ( ElementSampler, P1Sampler3D, Q1Sampler3D, S2Sampler3D, Tet2Sampler3D, W1Sampler3D, ) from yt.utilities.lib.primitives cimport ( BBox, Patch, Ray, TetPatch, Triangle, patch_bbox, patch_centroid, ray_bbox_intersect, ray_patch_intersect, ray_tet_patch_intersect, ray_triangle_intersect, tet_patch_bbox, tet_patch_centroid, triangle_bbox, triangle_centroid, ) from .image_samplers cimport ImageSampler cdef ElementSampler Q1Sampler = Q1Sampler3D() cdef ElementSampler P1Sampler = P1Sampler3D() cdef ElementSampler W1Sampler = W1Sampler3D() cdef ElementSampler S2Sampler = S2Sampler3D() cdef ElementSampler Tet2Sampler = Tet2Sampler3D() cdef extern from "platform_dep.h" nogil: double fmax(double x, double y) double fmin(double x, double y) # define some constants cdef np.float64_t INF = np.inf cdef np.int64_t LEAF_SIZE = 16 cdef class BVH: ''' This class implements a bounding volume hierarchy (BVH), a spatial acceleration structure for fast ray-tracing. A BVH is like a kd-tree, except that instead of partitioning the *volume* of the parent to create the children, we partition the primitives themselves into 'left' or 'right' sub-trees. The bounding volume for a node is then determined by computing the bounding volume of the primitives that belong to it. This allows us to quickly discard primitives that are not close to intersecting a given ray. This class is currently used to provide software 3D rendering support for finite element datasets. For 1st-order meshes, every element of the mesh is triangulated, and this set of triangles forms the primitives that will be used for the ray-trace. The BVH can then quickly determine which element is hit by each ray associated with the image plane, and the appropriate interpolation can be performed to sample the finite element solution at that hit position. Currently, 2nd-order meshes are only supported for 20-node hexahedral elements. There, the primitive type is a bi-quadratic patch instead of a triangle, and each intersection involves computing a Newton-Raphson solve. See yt/utilities/lib/primitives.pyx for the definitions of both of these primitive types. ''' @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __cinit__(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices, np.float64_t[:, :] field_data): self.num_elem = indices.shape[0] self.num_verts_per_elem = indices.shape[1] self.num_field_per_elem = field_data.shape[1] # We need to figure out what kind of elements we've been handed. if self.num_verts_per_elem == 8: self.num_prim_per_elem = HEX_NT self.tri_array = triangulate_hex self.sampler = Q1Sampler elif self.num_verts_per_elem == 6: self.num_prim_per_elem = WEDGE_NT self.tri_array = triangulate_wedge self.sampler = W1Sampler elif self.num_verts_per_elem == 4: self.num_prim_per_elem = TETRA_NT self.tri_array = triangulate_tetra self.sampler = P1Sampler elif self.num_verts_per_elem == 20: self.num_prim_per_elem = 6 self.sampler = S2Sampler elif self.num_verts_per_elem == 10: self.num_prim_per_elem = 4 self.sampler = Tet2Sampler else: raise NotImplementedError("Could not determine element type for " "nverts = %d. " % self.num_verts_per_elem) self.num_prim = self.num_prim_per_elem*self.num_elem # allocate storage cdef np.int64_t v_size = self.num_verts_per_elem * self.num_elem * 3 self.vertices = malloc(v_size * sizeof(np.float64_t)) cdef np.int64_t f_size = self.num_field_per_elem * self.num_elem self.field_data = malloc(f_size * sizeof(np.float64_t)) self.prim_ids = malloc(self.num_prim * sizeof(np.int64_t)) self.centroids = malloc(self.num_prim * sizeof(np.float64_t*)) cdef np.int64_t i for i in range(self.num_prim): self.centroids[i] = malloc(3*sizeof(np.float64_t)) self.bboxes = malloc(self.num_prim * sizeof(BBox)) # create data buffers cdef np.int64_t j, k cdef np.int64_t field_offset, vertex_offset for i in range(self.num_elem): for j in range(self.num_verts_per_elem): vertex_offset = i*self.num_verts_per_elem*3 + j*3 for k in range(3): self.vertices[vertex_offset + k] = vertices[indices[i,j]][k] field_offset = i*self.num_field_per_elem for j in range(self.num_field_per_elem): self.field_data[field_offset + j] = field_data[i][j] # set up primitives if self.num_verts_per_elem == 20: self.primitives = malloc(self.num_prim * sizeof(Patch)) self.get_centroid = patch_centroid self.get_bbox = patch_bbox self.get_intersect = ray_patch_intersect self._set_up_patches(vertices, indices) elif self.num_verts_per_elem == 10: self.primitives = malloc(self.num_prim * sizeof(TetPatch)) self.get_centroid = tet_patch_centroid self.get_bbox = tet_patch_bbox self.get_intersect = ray_tet_patch_intersect self._set_up_tet_patches(vertices, indices) else: self.primitives = malloc(self.num_prim * sizeof(Triangle)) self.get_centroid = triangle_centroid self.get_bbox = triangle_bbox self.get_intersect = ray_triangle_intersect self._set_up_triangles(vertices, indices) self.root = self._recursive_build(0, self.num_prim) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void _set_up_patches(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices) noexcept nogil: cdef Patch* patch cdef np.int64_t i, j, k, ind, idim cdef np.int64_t offset, prim_index for i in range(self.num_elem): offset = self.num_prim_per_elem*i for j in range(self.num_prim_per_elem): # for each face prim_index = offset + j patch = &( self.primitives)[prim_index] self.prim_ids[prim_index] = prim_index patch.elem_id = i for k in range(8): # for each vertex ind = hex20_faces[j][k] for idim in range(3): # for each spatial dimension (yikes) patch.v[k][idim] = vertices[indices[i, ind]][idim] self.get_centroid(self.primitives, prim_index, self.centroids[prim_index]) self.get_bbox(self.primitives, prim_index, &(self.bboxes[prim_index])) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void _set_up_tet_patches(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices) noexcept nogil: cdef TetPatch* tet_patch cdef np.int64_t i, j, k, ind, idim cdef np.int64_t offset, prim_index for i in range(self.num_elem): offset = self.num_prim_per_elem*i for j in range(self.num_prim_per_elem): # for each face prim_index = offset + j tet_patch = &( self.primitives)[prim_index] self.prim_ids[prim_index] = prim_index tet_patch.elem_id = i for k in range(6): # for each vertex ind = tet10_faces[j][k] for idim in range(3): # for each spatial dimension (yikes) tet_patch.v[k][idim] = vertices[indices[i, ind]][idim] self.get_centroid(self.primitives, prim_index, self.centroids[prim_index]) self.get_bbox(self.primitives, prim_index, &(self.bboxes[prim_index])) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void _set_up_triangles(self, np.float64_t[:, :] vertices, np.int64_t[:, :] indices) noexcept nogil: # fill our array of primitives cdef np.int64_t offset, tri_index cdef np.int64_t v0, v1, v2 cdef Triangle* tri cdef np.int64_t i, j, k for i in range(self.num_elem): offset = self.num_prim_per_elem*i for j in range(self.num_prim_per_elem): tri_index = offset + j self.prim_ids[tri_index] = tri_index tri = &( self.primitives)[tri_index] tri.elem_id = i v0 = indices[i][self.tri_array[j][0]] v1 = indices[i][self.tri_array[j][1]] v2 = indices[i][self.tri_array[j][2]] for k in range(3): tri.p0[k] = vertices[v0][k] tri.p1[k] = vertices[v1][k] tri.p2[k] = vertices[v2][k] self.get_centroid(self.primitives, tri_index, self.centroids[tri_index]) self.get_bbox(self.primitives, tri_index, &(self.bboxes[tri_index])) cdef void _recursive_free(self, BVHNode* node) noexcept nogil: if node.end - node.begin > LEAF_SIZE: self._recursive_free(node.left) self._recursive_free(node.right) free(node) def __dealloc__(self): if self.root == NULL: return self._recursive_free(self.root) free(self.primitives) free(self.prim_ids) for i in range(self.num_prim): free(self.centroids[i]) free(self.centroids) free(self.bboxes) free(self.field_data) free(self.vertices) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t _partition(self, np.int64_t begin, np.int64_t end, np.int64_t ax, np.float64_t split) noexcept nogil: # this re-orders the primitive array so that all of the primitives # to the left of mid have centroids less than or equal to "split" # along the direction "ax". All the primitives to the right of mid # will have centroids *greater* than "split" along "ax". cdef np.int64_t mid = begin while (begin != end): if self.centroids[mid][ax] > split: mid += 1 elif self.centroids[begin][ax] > split: self.prim_ids[mid], self.prim_ids[begin] = \ self.prim_ids[begin], self.prim_ids[mid] self.centroids[mid], self.centroids[begin] = \ self.centroids[begin], self.centroids[mid] self.bboxes[mid], self.bboxes[begin] = \ self.bboxes[begin], self.bboxes[mid] mid += 1 begin += 1 return mid @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void _get_node_bbox(self, BVHNode* node, np.int64_t begin, np.int64_t end) noexcept nogil: cdef np.int64_t i, j cdef BBox box = self.bboxes[begin] for i in range(begin+1, end): for j in range(3): box.left_edge[j] = fmin(box.left_edge[j], self.bboxes[i].left_edge[j]) box.right_edge[j] = fmax(box.right_edge[j], self.bboxes[i].right_edge[j]) node.bbox = box @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void intersect(self, Ray* ray) noexcept nogil: self._recursive_intersect(ray, self.root) if ray.elem_id < 0: return cdef np.float64_t[3] position cdef np.int64_t i for i in range(3): position[i] = ray.origin[i] + ray.t_far*ray.direction[i] cdef np.float64_t* vertex_ptr cdef np.float64_t* field_ptr vertex_ptr = self.vertices + ray.elem_id*self.num_verts_per_elem*3 field_ptr = self.field_data + ray.elem_id*self.num_field_per_elem cdef np.float64_t[4] mapped_coord self.sampler.map_real_to_unit(mapped_coord, vertex_ptr, position) if self.num_field_per_elem == 1: ray.data_val = field_ptr[0] else: ray.data_val = self.sampler.sample_at_unit_point(mapped_coord, field_ptr) ray.near_boundary = self.sampler.check_mesh_lines(mapped_coord) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void _recursive_intersect(self, Ray* ray, BVHNode* node) noexcept nogil: # check for bbox intersection: if not ray_bbox_intersect(ray, node.bbox): return # check for leaf cdef np.int64_t i if (node.end - node.begin) <= LEAF_SIZE: for i in range(node.begin, node.end): self.get_intersect(self.primitives, self.prim_ids[i], ray) return # if not leaf, intersect with left and right children self._recursive_intersect(ray, node.left) self._recursive_intersect(ray, node.right) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef BVHNode* _recursive_build(self, np.int64_t begin, np.int64_t end) noexcept nogil: cdef BVHNode *node = malloc(sizeof(BVHNode)) node.begin = begin node.end = end self._get_node_bbox(node, begin, end) # check for leaf if (end - begin) <= LEAF_SIZE: return node # we use the "split in the middle of the longest axis approach" # see: http://www.vadimkravcenko.com/bvh-tree-building/ # compute longest dimension cdef np.int64_t ax = 0 cdef np.float64_t d = fabs(node.bbox.right_edge[0] - node.bbox.left_edge[0]) if fabs(node.bbox.right_edge[1] - node.bbox.left_edge[1]) > d: ax = 1 if fabs(node.bbox.right_edge[2] - node.bbox.left_edge[2]) > d: ax = 2 # split in half along that dimension cdef np.float64_t split = 0.5*(node.bbox.right_edge[ax] + node.bbox.left_edge[ax]) # sort triangle list cdef np.int64_t mid = self._partition(begin, end, ax, split) if(mid == begin or mid == end): mid = begin + (end-begin)/2 # recursively build sub-trees node.left = self._recursive_build(begin, mid) node.right = self._recursive_build(mid, end) return node @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void cast_rays(np.float64_t* image, const np.float64_t* origins, const np.float64_t* direction, const int N, BVH bvh) noexcept nogil: cdef Ray* ray cdef int i, j, k with nogil, parallel(): ray = malloc(sizeof(Ray)) for k in range(3): ray.direction[k] = direction[k] ray.inv_dir[k] = 1.0 / direction[k] for i in prange(N): for j in range(3): ray.origin[j] = origins[N*j + i] ray.t_far = INF ray.t_near = 0.0 ray.data_val = 0 ray.elem_id = -1 bvh.intersect(ray) image[i] = ray.data_val free(ray) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_ray_trace(np.ndarray[np.float64_t, ndim=1] image, np.ndarray[np.float64_t, ndim=2] origins, np.ndarray[np.float64_t, ndim=1] direction, BVH bvh): cdef int N = origins.shape[0] cast_rays(&image[0], &origins[0, 0], &direction[0], N, bvh) cdef class BVHMeshSampler(ImageSampler): @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __call__(self, BVH bvh, int num_threads = 0): ''' This function is supposed to cast the rays and return the image. ''' cdef int vi, vj, i, j cdef np.float64_t *v_pos cdef np.float64_t *v_dir cdef np.int64_t nx, ny, size cdef np.float64_t width[3] for i in range(3): width[i] = self.width[i] nx = self.nv[0] ny = self.nv[1] size = nx * ny cdef Ray* ray with nogil, parallel(): ray = malloc(sizeof(Ray)) v_pos = malloc(3 * sizeof(np.float64_t)) v_dir = malloc(3 * sizeof(np.float64_t)) for j in prange(size): vj = j % ny vi = (j - vj) / ny vj = vj self.vector_function(self, vi, vj, width, v_dir, v_pos) for i in range(3): ray.origin[i] = v_pos[i] ray.direction[i] = v_dir[i] ray.inv_dir[i] = 1.0 / v_dir[i] ray.t_far = 1e37 ray.t_near = 0.0 ray.data_val = 0 ray.elem_id = -1 bvh.intersect(ray) self.image[vi, vj, 0] = ray.data_val self.image_used[vi, vj] = ray.elem_id self.mesh_lines[vi, vj] = ray.near_boundary self.zbuffer[vi, vj] = ray.t_far free(v_pos) free(v_dir) free(ray) yt-project-yt-f043ac8/yt/utilities/lib/contour_finding.pxd000066400000000000000000000024371510711153200240010ustar00rootroot00000000000000""" Contour finding exports """ cimport cython cimport numpy as np cdef inline np.int64_t i64max(np.int64_t i0, np.int64_t i1): if i0 > i1: return i0 return i1 cdef inline np.int64_t i64min(np.int64_t i0, np.int64_t i1): if i0 < i1: return i0 return i1 cdef extern from "math.h": double fabs(double x) cdef extern from "stdlib.h": # NOTE that size_t might not be int void *alloca(int) cdef struct ContourID cdef struct ContourID: np.int64_t contour_id ContourID *parent ContourID *next ContourID *prev np.int64_t count cdef struct CandidateContour cdef struct CandidateContour: np.int64_t contour_id np.int64_t join_id CandidateContour *next cdef ContourID *contour_create(np.int64_t contour_id, ContourID *prev = ?) cdef void contour_delete(ContourID *node) cdef ContourID *contour_find(ContourID *node) cdef void contour_union(ContourID *node1, ContourID *node2) cdef int candidate_contains(CandidateContour *first, np.int64_t contour_id, np.int64_t join_id = ?) cdef CandidateContour *candidate_add(CandidateContour *first, np.int64_t contour_id, np.int64_t join_id = ?) yt-project-yt-f043ac8/yt/utilities/lib/contour_finding.pyx000066400000000000000000000667341510711153200240400ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: include_dirs = LIB_DIR_GEOM """ A two-pass contour finding algorithm """ from __future__ import print_function import numpy as np cimport cython cimport numpy as np from libc.stdlib cimport free, malloc, realloc from yt.geometry.oct_container cimport OctInfo, OctreeContainer from yt.geometry.oct_visitors cimport Oct from .amr_kdtools cimport Node from .partitioned_grid cimport PartitionedGrid from .volume_container cimport VolumeContainer, vc_index, vc_pos_index import sys cdef inline ContourID *contour_create(np.int64_t contour_id, ContourID *prev = NULL): node = malloc(sizeof(ContourID)) #print("Creating contour with id", contour_id) node.contour_id = contour_id node.next = node.parent = NULL node.prev = prev node.count = 1 if prev != NULL: prev.next = node return node cdef inline void contour_delete(ContourID *node): if node.prev != NULL: node.prev.next = node.next if node.next != NULL: node.next.prev = node.prev free(node) cdef inline ContourID *contour_find(ContourID *node): cdef ContourID *temp cdef ContourID *root root = node # First we find the root while root.parent != NULL and root.parent != root: root = root.parent if root == root.parent: root.parent = NULL # Now, we update everything along the tree. # So now everything along the line to the root has the parent set to the # root. while node.parent != NULL: temp = node.parent root.count += node.count node.count = 0 node.parent = root node = temp return root cdef inline void contour_union(ContourID *node1, ContourID *node2): if node1 == node2: return node1 = contour_find(node1) node2 = contour_find(node2) if node1 == node2: return cdef ContourID *pri cdef ContourID *sec if node1.count > node2.count: pri = node1 sec = node2 elif node2.count > node1.count: pri = node2 sec = node1 # might be a tie elif node1.contour_id < node2.contour_id: pri = node1 sec = node2 else: pri = node2 sec = node1 pri.count += sec.count sec.count = 0 sec.parent = pri cdef inline int candidate_contains(CandidateContour *first, np.int64_t contour_id, np.int64_t join_id = -1): while first != NULL: if first.contour_id == contour_id \ and first.join_id == join_id: return 1 first = first.next return 0 cdef inline CandidateContour *candidate_add(CandidateContour *first, np.int64_t contour_id, np.int64_t join_id = -1): cdef CandidateContour *node node = malloc(sizeof(CandidateContour)) node.contour_id = contour_id node.join_id = join_id node.next = first return node cdef class ContourTree: # This class is essentially a Union-Find algorithm. What we want to do is # to, given a connection between two objects, identify the unique ID for # those two objects. So what we have is a collection of contours, and they # eventually all get joined and contain lots of individual IDs. But it's # easy to find the *first* contour, i.e., the primary ID, for each of the # subsequent IDs. # # This means that we can connect id 202483 to id 2472, and if id 2472 is # connected to id 143, the connection will *actually* be from 202483 to # 143. In this way we can speed up joining things and knowing their # "canonical" id. # # This is a multi-step process, since we first want to connect all of the # contours, then we end up wanting to coalesce them, and ultimately we join # them at the end. The join produces a table that maps the initial to the # final, and we can go through and just update all of those. cdef ContourID *first cdef ContourID *last def clear(self): # Here, we wipe out ALL of our contours, but not the pointers to them cdef ContourID *cur cdef ContourID *next cur = self.first while cur != NULL: next = cur.next free(cur) cur = next self.first = self.last = NULL def __init__(self): self.first = self.last = NULL @cython.boundscheck(False) @cython.wraparound(False) def add_contours(self, np.ndarray[np.int64_t, ndim=1] contour_ids): # This adds new contours, from the given contour IDs, to the tree. # Each one can be connected to a parent, as well as to next/prev in the # set of contours belonging to this tree. cdef int i, n n = contour_ids.shape[0] cdef ContourID *cur = self.last for i in range(n): #print(i, contour_ids[i]) cur = contour_create(contour_ids[i], cur) if self.first == NULL: self.first = cur self.last = cur def add_contour(self, np.int64_t contour_id): self.last = contour_create(contour_id, self.last) def cull_candidates(self, np.ndarray[np.int64_t, ndim=3] candidates): # This function looks at each preliminary contour ID belonging to a # given collection of values, and then if need be it creates a new # contour for it. cdef int i, j, k, ni, nj, nk, nc cdef CandidateContour *first = NULL cdef CandidateContour *temp cdef np.int64_t cid nc = 0 ni = candidates.shape[0] nj = candidates.shape[1] nk = candidates.shape[2] for i in range(ni): for j in range(nj): for k in range(nk): cid = candidates[i,j,k] if cid == -1: continue if candidate_contains(first, cid) == 0: nc += 1 first = candidate_add(first, cid) cdef np.ndarray[np.int64_t, ndim=1] contours contours = np.empty(nc, dtype="int64") i = 0 # This removes all the temporary contours for this set of contours and # instead constructs a final list of them. while first != NULL: contours[i] = first.contour_id i += 1 temp = first.next free(first) first = temp return contours def cull_joins(self, np.ndarray[np.int64_t, ndim=2] cjoins): # This coalesces contour IDs, so that we have only the final name # resolutions -- the .join_id from a candidate. So many items will map # to a single join_id. cdef int i, ni, nc cdef CandidateContour *first = NULL cdef CandidateContour *temp cdef np.int64_t cid1, cid2 nc = 0 ni = cjoins.shape[0] for i in range(ni): cid1 = cjoins[i,0] cid2 = cjoins[i,1] if cid1 == -1: continue if cid2 == -1: continue if candidate_contains(first, cid1, cid2) == 0: nc += 1 first = candidate_add(first, cid1, cid2) cdef np.ndarray[np.int64_t, ndim=2] contours contours = np.empty((nc,2), dtype="int64") i = 0 while first != NULL: contours[i,0] = first.contour_id contours[i,1] = first.join_id i += 1 temp = first.next free(first) first = temp return contours @cython.boundscheck(False) @cython.wraparound(False) def add_joins(self, np.ndarray[np.int64_t, ndim=2] join_tree): cdef int i, n, ins cdef np.int64_t cid1, cid2 # Okay, this requires lots of iteration, unfortunately cdef ContourID *cur cdef ContourID *c1 cdef ContourID *c2 n = join_tree.shape[0] #print("Counting") #print("Checking", self.count()) for i in range(n): ins = 0 cid1 = join_tree[i, 0] cid2 = join_tree[i, 1] c1 = c2 = NULL cur = self.first #print("Looking for ", cid1, cid2) while c1 == NULL or c2 == NULL: if cur.contour_id == cid1: c1 = contour_find(cur) if cur.contour_id == cid2: c2 = contour_find(cur) ins += 1 cur = cur.next if cur == NULL: break if c1 == NULL or c2 == NULL: if c1 == NULL: print(" Couldn't find ", cid1) if c2 == NULL: print(" Couldn't find ", cid2) print(" Inspected ", ins) raise RuntimeError else: c1.count = c2.count = 0 contour_union(c1, c2) def count(self): cdef int n = 0 cdef ContourID *cur = self.first while cur != NULL: cur = cur.next n += 1 return n @cython.boundscheck(False) @cython.wraparound(False) def export(self): cdef int n = self.count() cdef ContourID *cur cdef ContourID *root cur = self.first cdef np.ndarray[np.int64_t, ndim=2] joins joins = np.empty((n, 2), dtype="int64") n = 0 while cur != NULL: root = contour_find(cur) joins[n, 0] = cur.contour_id joins[n, 1] = root.contour_id cur = cur.next n += 1 return joins def __dealloc__(self): self.clear() cdef class TileContourTree: cdef np.float64_t min_val cdef np.float64_t max_val def __init__(self, np.float64_t min_val, np.float64_t max_val): self.min_val = min_val self.max_val = max_val @cython.boundscheck(False) @cython.wraparound(False) def identify_contours(self, np.ndarray[np.float64_t, ndim=3] values, np.ndarray[np.int64_t, ndim=3] contour_ids, np.ndarray[np.uint8_t, ndim=3] mask, np.int64_t start): # This just looks at neighbor values and tries to identify which zones # are touching by face within a given brick. cdef int i, j, k, ni, nj, nk, offset cdef int off_i, off_j, off_k, oi, ok, oj cdef ContourID *cur = NULL cdef ContourID *c1 cdef ContourID *c2 cdef np.float64_t v cdef np.int64_t nc ni = values.shape[0] nj = values.shape[1] nk = values.shape[2] nc = 0 cdef ContourID **container = malloc( sizeof(ContourID*)*ni*nj*nk) for i in range(ni*nj*nk): container[i] = NULL for i in range(ni): for j in range(nj): for k in range(nk): v = values[i,j,k] if mask[i,j,k] == 0: continue if v < self.min_val or v > self.max_val: continue nc += 1 c1 = contour_create(nc + start) cur = container[i*nj*nk + j*nk + k] = c1 for oi in range(3): off_i = oi - 1 + i if not (0 <= off_i < ni): continue for oj in range(3): off_j = oj - 1 + j if not (0 <= off_j < nj): continue for ok in range(3): if oi == oj == ok == 1: continue off_k = ok - 1 + k if not (0 <= off_k < nk): continue if off_k > k and off_j > j and off_i > i: continue offset = off_i*nj*nk + off_j*nk + off_k c2 = container[offset] if c2 == NULL: continue c2 = contour_find(c2) cur.count = c2.count = 0 contour_union(cur, c2) cur = contour_find(cur) for i in range(ni): for j in range(nj): for k in range(nk): c1 = container[i*nj*nk + j*nk + k] if c1 == NULL: continue c1 = contour_find(c1) contour_ids[i,j,k] = c1.contour_id for i in range(ni*nj*nk): if container[i] != NULL: free(container[i]) free(container) return nc @cython.boundscheck(False) @cython.wraparound(False) def link_node_contours(Node trunk, contours, ContourTree tree, np.ndarray[np.int64_t, ndim=1] node_ids): cdef int n_nodes = node_ids.shape[0] cdef np.int64_t node_ind cdef VolumeContainer **vcs = malloc( sizeof(VolumeContainer*) * n_nodes) cdef int i cdef PartitionedGrid pg for i in range(n_nodes): pg = contours[node_ids[i]][2] vcs[i] = pg.container cdef np.ndarray[np.uint8_t] examined = np.zeros(n_nodes, "uint8") for _, cinfo in sorted(contours.items(), key = lambda a: -a[1][0]): _, node_ind, pg, _ = cinfo construct_boundary_relationships(trunk, tree, node_ind, examined, vcs, node_ids) examined[node_ind] = 1 cdef inline void get_spos(VolumeContainer *vc, int i, int j, int k, int axis, np.float64_t *spos): spos[0] = vc.left_edge[0] + i * vc.dds[0] spos[1] = vc.left_edge[1] + j * vc.dds[1] spos[2] = vc.left_edge[2] + k * vc.dds[2] spos[axis] += 0.5 * vc.dds[axis] cdef inline int spos_contained(VolumeContainer *vc, np.float64_t *spos): cdef int i for i in range(3): if spos[i] <= vc.left_edge[i] or spos[i] >= vc.right_edge[i]: return 0 return 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef void construct_boundary_relationships(Node trunk, ContourTree tree, np.int64_t nid, np.ndarray[np.uint8_t, ndim=1] examined, VolumeContainer **vcs, np.ndarray[np.int64_t, ndim=1] node_ids): # We only look at the boundary and find the nodes next to it. # Contours is a dict, keyed by the node.id. cdef int i, j, off_i, off_j, oi, oj, ax, ax0, ax1, n1, n2 cdef np.int64_t c1, c2 cdef Node adj_node cdef VolumeContainer *vc1 cdef VolumeContainer *vc0 = vcs[nid] cdef int s = (vc0.dims[1]*vc0.dims[0] + vc0.dims[0]*vc0.dims[2] + vc0.dims[1]*vc0.dims[2]) * 18 # We allocate an array of fixed (maximum) size cdef np.ndarray[np.int64_t, ndim=2] joins = np.zeros((s, 2), dtype="int64") cdef int ti = 0, side, m1, m2, index cdef int pos[3] cdef int my_pos[3] cdef np.float64_t spos[3] for ax in range(3): ax0 = (ax + 1) % 3 ax1 = (ax + 2) % 3 n1 = vc0.dims[ax0] n2 = vc0.dims[ax1] for i in range(n1): for j in range(n2): for off_i in range(3): oi = off_i - 1 if i == 0 and oi == -1: continue if i == n1 - 1 and oi == 1: continue for off_j in range(3): oj = off_j - 1 if j == 0 and oj == -1: continue if j == n2 - 1 and oj == 1: continue pos[ax0] = i + oi pos[ax1] = j + oj my_pos[ax0] = i my_pos[ax1] = j for side in range(2): # We go off each end of the block. if side == 0: pos[ax] = -1 my_pos[ax] = 0 else: pos[ax] = vc0.dims[ax] my_pos[ax] = vc0.dims[ax]-1 get_spos(vc0, pos[0], pos[1], pos[2], ax, spos) adj_node = trunk._find_node(spos) vc1 = vcs[adj_node.node_ind] if spos_contained(vc1, spos): index = vc_index(vc0, my_pos[0], my_pos[1], my_pos[2]) m1 = vc0.mask[index] c1 = (vc0.data[0])[index] index = vc_pos_index(vc1, spos) m2 = vc1.mask[index] c2 = (vc1.data[0])[index] if m1 == 1 and m2 == 1 and c1 > -1 and c2 > -1: if examined[adj_node.node_ind] == 0: joins[ti,0] = i64max(c1,c2) joins[ti,1] = i64min(c1,c2) else: joins[ti,0] = c1 joins[ti,1] = c2 ti += 1 if ti == 0: return new_joins = tree.cull_joins(joins[:ti,:]) tree.add_joins(new_joins) @cython.boundscheck(False) @cython.wraparound(False) def update_joins(np.ndarray[np.int64_t, ndim=2] joins, np.ndarray[np.int64_t, ndim=3] contour_ids, np.ndarray[np.int64_t, ndim=1] final_joins): cdef int j, nj, nf cdef int ci, cj, ck nj = joins.shape[0] nf = final_joins.shape[0] for ci in range(contour_ids.shape[0]): for cj in range(contour_ids.shape[1]): for ck in range(contour_ids.shape[2]): if contour_ids[ci,cj,ck] == -1: continue for j in range(nj): if contour_ids[ci,cj,ck] == joins[j,0]: contour_ids[ci,cj,ck] = joins[j,1] break for j in range(nf): if contour_ids[ci,cj,ck] == final_joins[j]: contour_ids[ci,cj,ck] = j + 1 break cdef class FOFNode: cdef np.int64_t tag, count def __init__(self, np.int64_t tag): self.tag = tag self.count = 0 cdef class ParticleContourTree(ContourTree): cdef np.float64_t linking_length, linking_length2 cdef np.float64_t DW[3] cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] cdef bint periodicity[3] cdef int minimum_count def __init__(self, linking_length, periodicity = (True, True, True), int minimum_count = 8): cdef int i self.linking_length = linking_length self.linking_length2 = linking_length * linking_length self.first = self.last = NULL for i in range(3): self.periodicity[i] = periodicity[i] self.minimum_count = minimum_count @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def identify_contours(self, OctreeContainer octree, np.ndarray[np.int64_t, ndim=1] dom_ind, np.ndarray[cython.floating, ndim=2] positions, np.ndarray[np.int64_t, ndim=1] particle_ids, int domain_id, int domain_offset): cdef np.ndarray[np.int64_t, ndim=1] pdoms, pcount, pind, doff cdef np.float64_t pos[3] cdef Oct *oct = NULL cdef Oct **neighbors = NULL cdef OctInfo oi cdef ContourID *c0 cdef np.int64_t moff = octree.get_domain_offset(domain_id + domain_offset) cdef np.int64_t i, j, k, n, nneighbors = -1, pind0, offset cdef int counter = 0 cdef int verbose = 0 pcount = np.zeros_like(dom_ind) doff = np.zeros_like(dom_ind) - 1 # First, we find the oct for each particle. pdoms = np.zeros(positions.shape[0], dtype="int64") pdoms -= -1 # First we allocate our container cdef ContourID **container = malloc( sizeof(ContourID*) * positions.shape[0]) for i in range(3): self.DW[i] = (octree.DRE[i] - octree.DLE[i]) self.DLE[i] = octree.DLE[i] self.DRE[i] = octree.DRE[i] for i in range(positions.shape[0]): counter += 1 container[i] = NULL for j in range(3): pos[j] = positions[i, j] oct = octree.get(pos, NULL) if oct == NULL or (domain_id > 0 and oct.domain != domain_id): continue offset = oct.domain_ind - moff pcount[offset] += 1 pdoms[i] = offset pind = np.argsort(pdoms) cdef np.int64_t *ipind = pind.data cdef cython.floating *fpos = positions.data # pind is now the pointer into the position and particle_ids array. for i in range(positions.shape[0]): offset = pdoms[pind[i]] if doff[offset] < 0: doff[offset] = i del pdoms cdef int nsize = 27 cdef np.int64_t *nind = malloc(sizeof(np.int64_t)*nsize) counter = 0 cdef np.int64_t frac = (doff.shape[0] / 20.0) if verbose == 1: print("Will be outputting every", frac, file=sys.stderr) for i in range(doff.shape[0]): if verbose == 1 and counter >= frac: counter = 0 print("FOF-ing % 5.1f%% done" % ((100.0 * i)/doff.size), file=sys.stderr) counter += 1 # Any particles found for this oct? if doff[i] < 0: continue offset = pind[doff[i]] # This can probably be replaced at some point with a faster lookup. for j in range(3): pos[j] = positions[offset, j] oct = octree.get(pos, &oi) if oct == NULL or (domain_id > 0 and oct.domain != domain_id): continue # Now we have our primary oct, so we will get its neighbors. neighbors = octree.neighbors(&oi, &nneighbors, oct, self.periodicity) # Now we have all our neighbors. And, we should be set for what # else we need to do. if nneighbors > nsize: nind = realloc( nind, sizeof(np.int64_t)*nneighbors) nsize = nneighbors for j in range(nneighbors): nind[j] = neighbors[j].domain_ind - moff for n in range(j): if nind[j] == nind[n]: nind[j] = -1 break # This is allocated by the neighbors function, so we deallocate it. free(neighbors) # We might know that all our internal particles are linked. # Otherwise, we look at each particle. for j in range(pcount[i]): # Note that this offset is the particle index pind0 = pind[doff[i] + j] # Look at each neighboring oct for k in range(nneighbors): if nind[k] == -1: continue offset = doff[nind[k]] if offset < 0: continue # NOTE: doff[i] will not monotonically increase. So we # need a unique ID for each container that we are # accessing. self.link_particles(container, fpos, ipind, pcount[nind[k]], offset, pind0, doff[i] + j) cdef np.ndarray[np.int64_t, ndim=1] contour_ids contour_ids = np.ones(positions.shape[0], dtype="int64") contour_ids *= -1 # Perform one last contour_find on each. Note that we no longer need # to look at any of the doff or internal offset stuff. for i in range(positions.shape[0]): if container[i] == NULL: continue container[i] = contour_find(container[i]) for i in range(positions.shape[0]): if container[i] == NULL: continue c0 = container[i] if c0.count < self.minimum_count: continue contour_ids[i] = particle_ids[pind[c0.contour_id]] free(container) del pind return contour_ids @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef void link_particles(self, ContourID **container, cython.floating *positions, np.int64_t *pind, np.int64_t pcount, np.int64_t noffset, np.int64_t pind0, np.int64_t poffset): # Now we look at each particle and evaluate it cdef np.float64_t pos0[3] cdef np.float64_t pos1[3] cdef np.float64_t edges[2][3] cdef int link cdef ContourID *c0 cdef ContourID *c1 cdef np.int64_t pind1 cdef int i, j # We use pid here so that we strictly take new ones. # Note that pind0 will not monotonically increase, but c0 = container[pind0] if c0 == NULL: c0 = container[pind0] = contour_create(poffset, self.last) self.last = c0 if self.first == NULL: self.first = c0 c0 = container[pind0] = contour_find(c0) for i in range(3): # We make a very conservative guess here about the edges. pos0[i] = positions[pind0*3 + i] edges[0][i] = pos0[i] - self.linking_length*1.01 edges[1][i] = pos0[i] + self.linking_length*1.01 if edges[0][i] < self.DLE[i] or edges[0][i] > self.DRE[i]: # We skip this one, since we're close to the boundary edges[0][i] = -1e30 edges[1][i] = 1e30 # Lets set up some bounds for the particles. Maybe we can get away # with reducing our number of calls to r2dist_early. for i in range(pcount): pind1 = pind[noffset + i] if pind1 == pind0: continue c1 = container[pind1] if c1 != NULL and c1.contour_id == c0.contour_id: # Already linked. continue for j in range(3): pos1[j] = positions[pind1*3 + j] link = r2dist_early(pos0, pos1, self.DW, self.periodicity, self.linking_length2, edges) if link == 0: continue if c1 == NULL: c0.count += 1 container[pind1] = c0 elif c0.contour_id != c1.contour_id: contour_union(c0, c1) c0 = container[pind1] = container[pind0] = contour_find(c0) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline int r2dist_early(np.float64_t ppos[3], np.float64_t cpos[3], np.float64_t DW[3], bint periodicity[3], np.float64_t max_r2, np.float64_t edges[2][3]): cdef int i cdef np.float64_t r2, DR r2 = 0.0 for i in range(3): if cpos[i] < edges[0][i]: return 0 if cpos[i] > edges[1][i]: return 0 for i in range(3): DR = (ppos[i] - cpos[i]) if not periodicity[i]: pass elif (DR > DW[i]/2.0): DR -= DW[i] elif (DR < -DW[i]/2.0): DR += DW[i] r2 += DR * DR if r2 > max_r2: return 0 return 1 yt-project-yt-f043ac8/yt/utilities/lib/cosmology_time.pyx000066400000000000000000000031701510711153200236630ustar00rootroot00000000000000# distutils: language = c++ # distutils: libraries = STD_LIBS cimport numpy as np import numpy as np from libc.math cimport sqrt import cython @cython.cdivision(True) cdef inline double _a_dot(double a, double h0, double om_m, double om_l) noexcept: om_k = 1.0 - om_m - om_l return h0 * a * sqrt(om_m * (a ** -3) + om_k * (a ** -2) + om_l) @cython.cdivision(True) cpdef double _a_dot_recip(double a, double h0, double om_m, double om_l): return 1. / _a_dot(a, h0, om_m, om_l) cdef inline double _da_dtau(double a, double h0, double om_m, double om_l) noexcept: return a**2 * _a_dot(a, h0, om_m, om_l) @cython.cdivision(True) cpdef double _da_dtau_recip(double a, double h0, double om_m, double om_l) noexcept: return 1. / _da_dtau(a, h0, om_m, om_l) def t_frw(ds, z): from scipy.integrate import quad aexp = 1 / (1 + z) h0 = ds.hubble_constant om_m = ds.omega_matter om_l = ds.omega_lambda conv = ds.quan(0.01, "Mpc/km*s").to("Gyr") if isinstance(z, (int, float)): return ds.quan( quad(_a_dot_recip, 0, aexp, args=(h0, om_m, om_l))[0], units=conv, ) return ds.arr( [quad(_a_dot_recip, 0, a, args=(h0, om_m, om_l))[0] for a in aexp], units=conv, ) def tau_frw(ds, z): from scipy.integrate import quad aexp = 1 / (1 + z) h0 = ds.hubble_constant om_m = ds.omega_matter om_l = ds.omega_lambda if isinstance(z, (int, float)): return quad(_da_dtau_recip, 1, aexp, args=(h0, om_m, om_l))[0] return np.asarray( [quad(_da_dtau_recip, 1, a, args=(h0, om_m, om_l))[0] for a in aexp], ) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/000077500000000000000000000000001510711153200217015ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/__init__.py000066400000000000000000000012641510711153200240150ustar00rootroot00000000000000from yt.utilities.lib.cykdtree import plot # NOQA from yt.utilities.lib.cykdtree.kdtree import PyKDTree, PyNode # NOQA def make_tree(pts, **kwargs): r"""Build a KD-tree for a set of points. Args: pts (np.ndarray of float64): (n,m) Array of n mD points. \*\*kwargs: Additional keyword arguments are passed to the appropriate class for constructuing the tree. Returns: T (:class:`cykdtree.PyKDTree`): KDTree object. Raises: ValueError: If `pts` is not a 2D array. """ # Check input if pts.ndim != 2: raise ValueError("pts must be a 2D array of ND coordinates") T = PyKDTree(pts, **kwargs) return T yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/c_kdtree.cpp000066400000000000000000000000001510711153200241530ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/c_kdtree.hpp000066400000000000000000000675231510711153200242070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "c_utils.hpp" #define LEAF_MAX 4294967295 template T deserialize_scalar(std::istream &is) { T scalar; is.read((char*)&scalar, sizeof(T)); return scalar; } template void serialize_scalar(std::ostream &os, const T &scalar) { os.write((char*)&scalar, sizeof(scalar)); } template T* deserialize_pointer_array(std::istream &is, uint64_t len) { T* arr = (T*)malloc(len*sizeof(T)); is.read((char*)&arr[0], len*sizeof(T)); return arr; } template void serialize_pointer_array(std::ostream &os, const T* array, uint64_t len) { os.write((char*)array, len*sizeof(T)); } class Node { public: bool is_empty; bool is_leaf; uint32_t leafid; uint32_t ndim; double *left_edge; double *right_edge; uint64_t left_idx; uint64_t children; bool *periodic_left; bool *periodic_right; std::vector > left_neighbors; std::vector > right_neighbors; std::vector all_neighbors; std::vector left_nodes; // innernode parameters uint32_t split_dim; double split; Node *less; Node *greater; // empty node constructor Node() { is_empty = true; is_leaf = false; leafid = LEAF_MAX; ndim = 0; left_edge = NULL; right_edge = NULL; periodic_left = NULL; periodic_right = NULL; less = NULL; greater = NULL; } // empty node with some info Node(uint32_t ndim0, double *le, double *re, bool *ple, bool *pre) { is_empty = true; is_leaf = false; leafid = 4294967295; ndim = ndim0; left_edge = (double*)malloc(ndim*sizeof(double)); right_edge = (double*)malloc(ndim*sizeof(double)); periodic_left = (bool*)malloc(ndim*sizeof(bool)); periodic_right = (bool*)malloc(ndim*sizeof(bool)); memcpy(left_edge, le, ndim*sizeof(double)); memcpy(right_edge, re, ndim*sizeof(double)); memcpy(periodic_left, ple, ndim*sizeof(bool)); memcpy(periodic_right, pre, ndim*sizeof(bool)); less = NULL; greater = NULL; for (uint32_t i=0; i left_nodes0) { is_empty = false; is_leaf = false; leafid = 4294967295; ndim = ndim0; left_idx = Lidx; split_dim = sdim0; split = split0; less = lnode; greater = gnode; children = lnode->children + gnode->children; left_edge = (double*)malloc(ndim*sizeof(double)); right_edge = (double*)malloc(ndim*sizeof(double)); periodic_left = (bool*)malloc(ndim*sizeof(bool)); periodic_right = (bool*)malloc(ndim*sizeof(bool)); memcpy(left_edge, le, ndim*sizeof(double)); memcpy(right_edge, re, ndim*sizeof(double)); memcpy(periodic_left, ple, ndim*sizeof(bool)); memcpy(periodic_right, pre, ndim*sizeof(bool)); for (uint32_t d = 0; d < ndim; d++) left_nodes.push_back(left_nodes0[d]); left_neighbors = std::vector >(ndim); right_neighbors = std::vector >(ndim); } // leafnode constructor Node(uint32_t ndim0, double *le, double *re, bool *ple, bool *pre, uint64_t Lidx, uint64_t n, int leafid0, std::vector left_nodes0) { is_empty = false; is_leaf = true; leafid = leafid0; ndim = ndim0; split = 0.0; split_dim = 0; left_idx = Lidx; less = NULL; greater = NULL; children = n; left_edge = (double*)malloc(ndim*sizeof(double)); right_edge = (double*)malloc(ndim*sizeof(double)); periodic_left = (bool*)malloc(ndim*sizeof(bool)); periodic_right = (bool*)malloc(ndim*sizeof(bool)); memcpy(left_edge, le, ndim*sizeof(double)); memcpy(right_edge, re, ndim*sizeof(double)); memcpy(periodic_left, ple, ndim*sizeof(bool)); memcpy(periodic_right, pre, ndim*sizeof(bool)); for (uint32_t d = 0; d < ndim; d++) left_nodes.push_back(left_nodes0[d]); left_neighbors = std::vector >(ndim); right_neighbors = std::vector >(ndim); for (uint32_t d = 0; d < ndim; d++) { if ((left_nodes[d]) && (!(left_nodes[d]->is_empty))) add_neighbors(left_nodes[d], d); } } Node(std::istream &is) { // Note that Node instances initialized via this method do not have // any neighbor information. We will build neighbor information later // by walking the tree bool check_bit = deserialize_scalar(is); if (!check_bit) { // something has gone terribly wrong so we crash abort(); } is_empty = deserialize_scalar(is); is_leaf = deserialize_scalar(is); leafid = deserialize_scalar(is); ndim = deserialize_scalar(is); left_edge = deserialize_pointer_array(is, ndim); right_edge = deserialize_pointer_array(is, ndim); left_idx = deserialize_scalar(is); children = deserialize_scalar(is); periodic_left = deserialize_pointer_array(is, ndim); periodic_right = deserialize_pointer_array(is, ndim); split_dim = deserialize_scalar(is); split = deserialize_scalar(is); less = NULL; greater = NULL; left_neighbors = std::vector >(ndim); right_neighbors = std::vector >(ndim); for (uint32_t i=0; i(os, true); serialize_scalar(os, is_empty); serialize_scalar(os, is_leaf); serialize_scalar(os, leafid); serialize_scalar(os, ndim); serialize_pointer_array(os, left_edge, ndim); serialize_pointer_array(os, right_edge, ndim); serialize_scalar(os, left_idx); serialize_scalar(os, children); serialize_pointer_array(os, periodic_left, ndim); serialize_pointer_array(os, periodic_right, ndim); serialize_scalar(os, split_dim); serialize_scalar(os, split); } ~Node() { if (left_edge) free(left_edge); if (right_edge) free(right_edge); if (periodic_left) free(periodic_left); if (periodic_right) free(periodic_right); } friend std::ostream &operator<<(std::ostream &os, const Node &node) { // this is available for nicely formatted debugging, use serialize // to save data to disk os << "is_empty: " << node.is_empty << std::endl; os << "is_leaf: " << node.is_leaf << std::endl; os << "leafid: " << node.leafid << std::endl; os << "ndim: " << node.ndim << std::endl; os << "left_edge: "; for (uint32_t i = 0; i < node.ndim; i++) { os << node.left_edge[i] << " "; } os << std::endl; os << "right_edge: "; for (uint32_t i = 0; i < node.ndim; i++) { os << node.right_edge[i] << " "; } os << std::endl; os << "left_idx: " << node.left_idx << std::endl; os << "children: " << node.children << std::endl; os << "periodic_left: "; for (uint32_t i = 0; i < node.ndim; i++) { os << node.periodic_left[i] << " "; } os << std::endl; os << "periodic_right: "; for (uint32_t i = 0; i < node.ndim; i++) { os << node.periodic_right[i] << " "; } os << std::endl; os << "split_dim: " << node.split_dim << std::endl; os << "split: " << node.split << std::endl; for (uint32_t i=0; i < node.left_nodes.size(); i++) { os << node.left_nodes[i] << std::endl; if (node.left_nodes[i]) { os << node.left_nodes[i]->left_idx << std::endl; os << node.left_nodes[i]->children << std::endl; } } return os; } Node* copy() { Node *out; if (is_empty) { if (left_edge) { out = new Node(ndim, left_edge, right_edge, periodic_left, periodic_right); } else { out = new Node(); } } else if (is_leaf) { std::vector left_nodes_copy; for (uint32_t d = 0; d < ndim; d++) left_nodes_copy.push_back(NULL); out = new Node(ndim, left_edge, right_edge, periodic_left, periodic_right, left_idx, children, leafid, left_nodes_copy); } else { Node *lnode = less->copy(); Node *gnode = greater->copy(); std::vector left_nodes_copy; for (uint32_t d = 0; d < ndim; d++) left_nodes_copy.push_back(NULL); out = new Node(ndim, left_edge, right_edge, periodic_left, periodic_right, left_idx, split_dim, split, lnode, gnode, left_nodes_copy); std::vector::iterator it; for (uint32_t d = 0; d < ndim; d++) { for (it = left_neighbors[d].begin(); it != left_neighbors[d].end(); it++) { out->left_neighbors[d].push_back(*it); } for (it = right_neighbors[d].begin(); it != right_neighbors[d].end(); it++) { out->right_neighbors[d].push_back(*it); } } } return out; } void update_ids(uint32_t add_to) { leafid += add_to; uint32_t i; for (uint32_t d = 0; d < ndim; d++) { for (i = 0; i < left_neighbors[d].size(); i++) left_neighbors[d][i] += add_to; for (i = 0; i < right_neighbors[d].size(); i++) right_neighbors[d][i] += add_to; } for (i = 0; i < all_neighbors.size(); i++) all_neighbors[i] += add_to; } void print_neighbors() { uint32_t i, j; // Left printf("left: ["); for (i = 0; i < ndim; i++) { printf("["); for (j = 0; j < left_neighbors[i].size(); j++) printf("%u ", left_neighbors[i][j]); printf("] "); } printf("]\n"); // Right printf("right: ["); for (i = 0; i < ndim; i++) { printf("["); for (j = 0; j < right_neighbors[i].size(); j++) printf("%u ", right_neighbors[i][j]); printf("] "); } printf("]\n"); } void add_neighbors(Node* curr, uint32_t dim) { if (curr->is_leaf) { left_neighbors[dim].push_back(curr->leafid); curr->right_neighbors[dim].push_back(leafid); } else { if (curr->split_dim == dim) { add_neighbors(curr->greater, dim); } else { if (curr->split > this->right_edge[curr->split_dim]) add_neighbors(curr->less, dim); else if (curr->split < this->left_edge[curr->split_dim]) add_neighbors(curr->greater, dim); else { add_neighbors(curr->less, dim); add_neighbors(curr->greater, dim); } } } } void clear_neighbors() { uint32_t d; for (d = 0; d < ndim; d++) { left_neighbors[d].clear(); right_neighbors[d].clear(); } } bool is_left_node(Node *lnode, uint32_t ldim) { uint32_t d; for (d = 0; d < ndim; d++) { if (d == ldim) continue; if (right_edge[d] < lnode->left_edge[d]) return false; if (left_edge[d] > lnode->right_edge[d]) return false; } return true; } void select_unique_neighbors() { if (!is_leaf) return; uint32_t d; std::vector::iterator last; for (d = 0; d < ndim; d++) { // left std::sort(left_neighbors[d].begin(), left_neighbors[d].end()); last = std::unique(left_neighbors[d].begin(), left_neighbors[d].end()); left_neighbors[d].erase(last, left_neighbors[d].end()); // right std::sort(right_neighbors[d].begin(), right_neighbors[d].end()); last = std::unique(right_neighbors[d].begin(), right_neighbors[d].end()); right_neighbors[d].erase(last, right_neighbors[d].end()); } } void join_neighbors() { if (!is_leaf) return; uint32_t d; std::vector::iterator last; // Create concatenated vector and remove duplicates all_neighbors = left_neighbors[0]; for (d = 1; d < ndim; d++) all_neighbors.insert(all_neighbors.end(), left_neighbors[d].begin(), left_neighbors[d].end()); for (d = 0; d < ndim; d++) all_neighbors.insert(all_neighbors.end(), right_neighbors[d].begin(), right_neighbors[d].end()); // Get unique std::sort(all_neighbors.begin(), all_neighbors.end()); last = std::unique(all_neighbors.begin(), all_neighbors.end()); all_neighbors.erase(last, all_neighbors.end()); } bool check_overlap(Node other, uint32_t dim) { if (other.right_edge[dim] < left_edge[dim]) return false; else if (other.left_edge[dim] > right_edge[dim]) return false; else return true; } }; void write_tree_nodes(std::ostream &os, Node* node) { if (node) { // depth first search of tree below node, writing each node to os // as we go node->serialize(os); write_tree_nodes(os, node->less); write_tree_nodes(os, node->greater); } else { // write null character to indicate empty node serialize_scalar(os, false); } } Node* read_tree_nodes(std::istream &is, std::vector &leaves, std::vector &left_nodes) { Node* node = new Node(is); node->left_nodes = left_nodes; bool is_leaf = true; if (is.peek()) { // read left subtree node->less = read_tree_nodes(is, leaves, left_nodes); is_leaf = false; } else { // no left children is.get(); node->less = NULL; } if (is.peek()) { // read right subtree std::vector greater_left_nodes = left_nodes; greater_left_nodes[node->split_dim] = node->less; node->greater = read_tree_nodes(is, leaves, greater_left_nodes); is_leaf = false; } else { // no right children is.get(); node->greater = NULL; } if (is_leaf) { leaves.push_back(node); for (uint32_t d = 0; d < node->ndim; d++) { if ((node->left_nodes[d]) && (!(node->left_nodes[d]->is_empty))) { node->add_neighbors(node->left_nodes[d], d); } } } return node; } void free_tree_nodes(Node* node) { if (node) { free_tree_nodes(node->less); free_tree_nodes(node->greater); delete node; } } class KDTree { public: bool is_partial; bool skip_dealloc_root; bool use_sliding_midpoint; uint64_t* all_idx; uint64_t npts; uint32_t ndim; uint64_t left_idx; int64_t data_version; bool *periodic_left; bool *periodic_right; uint32_t leafsize; double* domain_left_edge; double* domain_right_edge; double* domain_width; bool* periodic; bool any_periodic; double* domain_mins; double* domain_maxs; uint32_t num_leaves; std::vector leaves; Node* root; // KDTree() {} KDTree(double *pts, uint64_t *idx, uint64_t n, uint32_t m, uint32_t leafsize0, double *left_edge, double *right_edge, bool *periodic_left0, bool *periodic_right0, double *domain_mins0, double *domain_maxs0, int64_t dversion, bool use_sliding_midpoint0 = false, bool dont_build = false) { is_partial = true; skip_dealloc_root = false; use_sliding_midpoint = use_sliding_midpoint0; all_idx = idx; npts = n; ndim = m; leafsize = leafsize0; domain_left_edge = (double*)malloc(ndim*sizeof(double)); domain_right_edge = (double*)malloc(ndim*sizeof(double)); periodic_left = (bool*)malloc(ndim*sizeof(bool)); periodic_right = (bool*)malloc(ndim*sizeof(bool)); data_version = dversion; periodic = (bool*)malloc(ndim*sizeof(bool)); domain_mins = NULL; domain_maxs = NULL; domain_width = (double*)malloc(ndim*sizeof(double)); num_leaves = 0; memcpy(domain_left_edge, left_edge, ndim*sizeof(double)); memcpy(domain_right_edge, right_edge, ndim*sizeof(double)); memcpy(periodic_left, periodic_left0, ndim*sizeof(bool)); memcpy(periodic_right, periodic_right0, ndim*sizeof(bool)); if (domain_mins0) { domain_mins = (double*)malloc(ndim*sizeof(double)); memcpy(domain_mins, domain_mins0, ndim*sizeof(double)); } else if (pts) { domain_mins = min_pts(pts, n, m); } if (domain_maxs0) { domain_maxs = (double*)malloc(ndim*sizeof(double)); memcpy(domain_maxs, domain_maxs0, ndim*sizeof(double)); } else if (pts) { domain_maxs = max_pts(pts, n, m); } any_periodic = false; for (uint32_t d = 0; d < ndim; d++) { if ((periodic_left[d]) && (periodic_right[d])) { periodic[d] = true; any_periodic = true; } else { periodic[d] = false; } } for (uint32_t d = 0; d < ndim; d++) domain_width[d] = domain_right_edge[d] - domain_left_edge[d]; if ((pts) && (!(dont_build))) build_tree(pts); } KDTree(double *pts, uint64_t *idx, uint64_t n, uint32_t m, uint32_t leafsize0, double *left_edge, double *right_edge, bool *periodic0, int64_t dversion, bool use_sliding_midpoint0 = false, bool dont_build = false) { is_partial = false; skip_dealloc_root = false; use_sliding_midpoint = use_sliding_midpoint0; left_idx = 0; all_idx = idx; npts = n; ndim = m; leafsize = leafsize0; domain_left_edge = (double*)malloc(ndim*sizeof(double)); domain_right_edge = (double*)malloc(ndim*sizeof(double)); data_version = dversion; periodic_left = (bool*)malloc(ndim*sizeof(bool)); periodic_right = (bool*)malloc(ndim*sizeof(bool)); periodic = (bool*)malloc(ndim*sizeof(bool)); domain_mins = NULL; domain_maxs = NULL; domain_width = (double*)malloc(ndim*sizeof(double)); num_leaves = 0; memcpy(domain_left_edge, left_edge, ndim*sizeof(double)); memcpy(domain_right_edge, right_edge, ndim*sizeof(double)); memcpy(periodic, periodic0, ndim*sizeof(bool)); if (pts) { domain_mins = min_pts(pts, n, m); domain_maxs = max_pts(pts, n, m); } any_periodic = false; for (uint32_t d = 0; d < ndim; d++) { if (periodic[d]) { periodic_left[d] = true; periodic_right[d] = true; any_periodic = true; } else { periodic_left[d] = false; periodic_right[d] = false; } } for (uint32_t d = 0; d < ndim; d++) domain_width[d] = domain_right_edge[d] - domain_left_edge[d]; if ((pts) && (!(dont_build))) build_tree(pts); } KDTree(std::istream &is) { data_version = deserialize_scalar(is); is_partial = deserialize_scalar(is); use_sliding_midpoint = deserialize_scalar(is); npts = deserialize_scalar(is); all_idx = deserialize_pointer_array(is, npts); ndim = deserialize_scalar(is); left_idx = deserialize_scalar(is); periodic = deserialize_pointer_array(is, ndim); periodic_left = deserialize_pointer_array(is, ndim); periodic_right = deserialize_pointer_array(is, ndim); any_periodic = deserialize_scalar(is); leafsize = deserialize_scalar(is); domain_left_edge = deserialize_pointer_array(is, ndim); domain_right_edge = deserialize_pointer_array(is, ndim); domain_width = deserialize_pointer_array(is, ndim); domain_mins = deserialize_pointer_array(is, ndim); domain_maxs = deserialize_pointer_array(is, ndim); num_leaves = deserialize_scalar(is); std::vector left_nodes; for (uint32_t i=0; i < ndim; i++) { left_nodes.push_back(NULL); } root = read_tree_nodes(is, leaves, left_nodes); finalize_neighbors(); } void serialize(std::ostream &os) { serialize_scalar(os, data_version); serialize_scalar(os, is_partial); serialize_scalar(os, use_sliding_midpoint); serialize_scalar(os, npts); serialize_pointer_array(os, all_idx, npts); serialize_scalar(os, ndim); serialize_scalar(os, left_idx); serialize_pointer_array(os, periodic, ndim); serialize_pointer_array(os, periodic_left, ndim); serialize_pointer_array(os, periodic_right, ndim); serialize_scalar(os, any_periodic); serialize_scalar(os, leafsize); serialize_pointer_array(os, domain_left_edge, ndim); serialize_pointer_array(os, domain_right_edge, ndim); serialize_pointer_array(os, domain_width, ndim); serialize_pointer_array(os, domain_mins, ndim); serialize_pointer_array(os, domain_maxs, ndim); serialize_scalar(os, num_leaves); write_tree_nodes(os, root); } ~KDTree() { if (!(skip_dealloc_root)) free_tree_nodes(root); free(domain_left_edge); free(domain_right_edge); free(domain_width); if (domain_mins) free(domain_mins); if (domain_maxs) free(domain_maxs); free(periodic); free(periodic_left); free(periodic_right); } void consolidate_edges(double *leaves_le, double *leaves_re) { for (uint32_t k = 0; k < num_leaves; k++) { memcpy(leaves_le+ndim*leaves[k]->leafid, leaves[k]->left_edge, ndim*sizeof(double)); memcpy(leaves_re+ndim*leaves[k]->leafid, leaves[k]->right_edge, ndim*sizeof(double)); } } void build_tree(double* all_pts) { uint32_t d; double *LE = (double*)malloc(ndim*sizeof(double)); double *RE = (double*)malloc(ndim*sizeof(double)); bool *PLE = (bool*)malloc(ndim*sizeof(bool)); bool *PRE = (bool*)malloc(ndim*sizeof(bool)); double *mins = (double*)malloc(ndim*sizeof(double)); double *maxs = (double*)malloc(ndim*sizeof(double)); std::vector left_nodes; if (!(domain_mins)) domain_mins = min_pts(all_pts, npts, ndim); if (!(domain_maxs)) domain_maxs = max_pts(all_pts, npts, ndim); for (d = 0; d < ndim; d++) { LE[d] = domain_left_edge[d]; RE[d] = domain_right_edge[d]; PLE[d] = periodic_left[d]; PRE[d] = periodic_right[d]; mins[d] = domain_mins[d]; maxs[d] = domain_maxs[d]; left_nodes.push_back(NULL); } root = build(0, npts, LE, RE, PLE, PRE, all_pts, mins, maxs, left_nodes); free(LE); free(RE); free(PLE); free(PRE); free(mins); free(maxs); // Finalize neighbors finalize_neighbors(); } void finalize_neighbors() { uint32_t d; // Add periodic neighbors if (any_periodic) set_neighbors_periodic(); // Remove duplicate neighbors for (d = 0; d < num_leaves; d++) { leaves[d]->select_unique_neighbors(); leaves[d]->join_neighbors(); } } void clear_neighbors() { std::vector::iterator it; for (it = leaves.begin(); it != leaves.end(); it++) (*it)->clear_neighbors(); } void set_neighbors_periodic() { uint32_t d0; Node* leaf; Node *prev; uint64_t i, j; // Periodic neighbors for (i = 0; i < num_leaves; i++) { leaf = leaves[i]; for (d0 = 0; d0 < ndim; d0++) { if (!leaf->periodic_left[d0]) continue; for (j = i; j < num_leaves; j++) { prev = leaves[j]; if (!prev->periodic_right[d0]) continue; add_neighbors_periodic(leaf, prev, d0); } } } } void add_neighbors_periodic(Node *leaf, Node *prev, uint32_t d0) { uint32_t d, ndim_escape; bool match; if (!leaf->periodic_left[d0]) return; if (!prev->periodic_right[d0]) return; match = true; ndim_escape = 0; for (d = 0; d < ndim; d++) { if (d == d0) continue; if (leaf->left_edge[d] >= prev->right_edge[d]) { if (!(leaf->periodic_right[d] && prev->periodic_left[d])) { match = false; break; } else { ndim_escape++; } } if (leaf->right_edge[d] <= prev->left_edge[d]) { if (!(prev->periodic_right[d] && leaf->periodic_left[d])) { match = false; break; } else { ndim_escape++; } } } if ((match) && (ndim_escape < (ndim-1))) { // printf("%d: %d, %d (%d)\n", d0, leaf->leafid, prev->leafid, ndim_escape); leaf->left_neighbors[d0].push_back(prev->leafid); prev->right_neighbors[d0].push_back(leaf->leafid); } } Node* build(uint64_t Lidx, uint64_t n, double *LE, double *RE, bool *PLE, bool *PRE, double* all_pts, double *mins, double *maxes, std::vector left_nodes) { // Create leaf if (n < leafsize) { Node* out = new Node(ndim, LE, RE, PLE, PRE, Lidx, n, num_leaves, left_nodes); num_leaves++; leaves.push_back(out); return out; } else { // Split uint32_t dmax, d; int64_t split_idx = 0; double split_val = 0.0; dmax = split(all_pts, all_idx, Lidx, n, ndim, mins, maxes, split_idx, split_val, use_sliding_midpoint); if (maxes[dmax] == mins[dmax]) { // all points singular Node* out = new Node(ndim, LE, RE, PLE, PRE, Lidx, n, num_leaves, left_nodes); num_leaves++; leaves.push_back(out); return out; } // Get new boundaries uint64_t Nless = split_idx-Lidx+1; uint64_t Ngreater = n - Nless; double *lessmaxes = (double*)malloc(ndim*sizeof(double)); double *lessright = (double*)malloc(ndim*sizeof(double)); bool *lessPRE = (bool*)malloc(ndim*sizeof(bool)); double *greatermins = (double*)malloc(ndim*sizeof(double)); double *greaterleft = (double*)malloc(ndim*sizeof(double)); bool *greaterPLE = (bool*)malloc(ndim*sizeof(bool)); std::vector greater_left_nodes; for (d = 0; d < ndim; d++) { lessmaxes[d] = maxes[d]; lessright[d] = RE[d]; lessPRE[d] = PRE[d]; greatermins[d] = mins[d]; greaterleft[d] = LE[d]; greaterPLE[d] = PLE[d]; greater_left_nodes.push_back(left_nodes[d]); } lessmaxes[dmax] = split_val; lessright[dmax] = split_val; lessPRE[dmax] = false; greatermins[dmax] = split_val; greaterleft[dmax] = split_val; greaterPLE[dmax] = false; // Build less and greater nodes Node* less = build(Lidx, Nless, LE, lessright, PLE, lessPRE, all_pts, mins, lessmaxes, left_nodes); greater_left_nodes[dmax] = less; Node* greater = build(Lidx+Nless, Ngreater, greaterleft, RE, greaterPLE, PRE, all_pts, greatermins, maxes, greater_left_nodes); // Create innernode referencing child nodes Node* out = new Node(ndim, LE, RE, PLE, PRE, Lidx, dmax, split_val, less, greater, left_nodes); free(lessright); free(greaterleft); free(lessPRE); free(greaterPLE); free(lessmaxes); free(greatermins); return out; } } double* wrap_pos(double* pos) { uint32_t d; double* wrapped_pos = (double*)malloc(ndim*sizeof(double)); for (d = 0; d < ndim; d++) { if (periodic[d]) { if (pos[d] < domain_left_edge[d]) { wrapped_pos[d] = domain_right_edge[d] - fmod((domain_right_edge[d] - pos[d]),domain_width[d]); } else { wrapped_pos[d] = domain_left_edge[d] + fmod((pos[d] - domain_left_edge[d]),domain_width[d]); } } else { wrapped_pos[d] = pos[d]; } } return wrapped_pos; } Node* search(double* pos0, bool dont_wrap = false) { uint32_t i; Node* out = NULL; bool valid; // Wrap positions double* pos; if ((!dont_wrap) && (any_periodic)) pos = wrap_pos(pos0); // allocates new array else pos = pos0; // Ensure that pos is in root, early return NULL if it's not valid = true; for (i = 0; i < ndim; i++) { if (pos[i] < root->left_edge[i]) { valid = false; break; } if (pos[i] >= root->right_edge[i]) { valid = false; break; } } // Traverse tree looking for leaf containing pos if (valid) { out = root; while (!(out->is_leaf)) { if (pos[out->split_dim] < out->split) out = out->less; else out = out->greater; } } if ((!dont_wrap) && (any_periodic)) free(pos); return out; } std::vector get_neighbor_ids(double* pos) { Node* leaf; std::vector neighbors; leaf = search(pos); if (leaf) neighbors = leaf->all_neighbors; return neighbors; } }; yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/c_utils.cpp000066400000000000000000000144611510711153200240550ustar00rootroot00000000000000#include #include #include #include #include #include #include "c_utils.hpp" #include bool isEqual(double f1, double f2) { return (fabs(f1 - f2) <= FLT_EPSILON); } double* max_pts(double *pts, uint64_t n, uint32_t m) { double* max = (double*)std::malloc(m*sizeof(double)); uint32_t d; for (d = 0; d < m; d++) max[d] = -DBL_MAX; // pts[d]; for (uint64_t i = 0; i < n; i++) { for (d = 0; d < m; d++) { if (pts[m*i + d] > max[d]) max[d] = pts[m*i + d]; } } return max; } double* min_pts(double *pts, uint64_t n, uint32_t m) { double* min = (double*)std::malloc(m*sizeof(double)); uint32_t d; for (d = 0; d < m; d++) min[d] = DBL_MAX; // pts[d]; for (uint64_t i = 0; i < n; i++) { for (d = 0; d < m; d++) { if (pts[m*i + d] < min[d]) min[d] = pts[m*i + d]; } } return min; } uint64_t argmax_pts_dim(double *pts, uint64_t *idx, uint32_t m, uint32_t d, uint64_t Lidx, uint64_t Ridx) { double max = -DBL_MAX; uint64_t idx_max = Lidx; for (uint64_t i = Lidx; i <= Ridx; i++) { if (pts[m*idx[i] + d] > max) { max = pts[m*idx[i] + d]; idx_max = i; } } return idx_max; } uint64_t argmin_pts_dim(double *pts, uint64_t *idx, uint32_t m, uint32_t d, uint64_t Lidx, uint64_t Ridx) { double min = DBL_MAX; uint64_t idx_min = Lidx; for (uint64_t i = Lidx; i <= Ridx; i++) { if (pts[m*idx[i] + d] < min) { min = pts[m*idx[i] + d]; idx_min = i; } } return idx_min; } // http://www.comp.dit.ie/rlawlor/Alg_DS/sorting/quickSort.c void quickSort(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r) { int64_t j; if( l < r ) { // divide and conquer j = partition(pts, idx, ndim, d, l, r, (l+r)/2); quickSort(pts, idx, ndim, d, l, j-1); quickSort(pts, idx, ndim, d, j+1, r); } } void insertSort(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r) { int64_t i, j; uint64_t t; if (r <= l) return; for (i = l+1; i <= r; i++) { t = idx[i]; j = i - 1; while ((j >= l) && (pts[ndim*idx[j]+d] > pts[ndim*t+d])) { idx[j+1] = idx[j]; j--; } idx[j+1] = t; } } int64_t pivot(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r) { if (r < l) { return -1; } else if (r == l) { return l; } else if ((r - l) < 5) { insertSort(pts, idx, ndim, d, l, r); return (l+r)/2; } int64_t i, subr, m5; uint64_t t; int64_t nsub = 0; for (i = l; i <= r; i+=5) { subr = i + 4; if (subr > r) subr = r; insertSort(pts, idx, ndim, d, i, subr); m5 = (i+subr)/2; t = idx[m5]; idx[m5] = idx[l + nsub]; idx[l + nsub] = t; nsub++; } return pivot(pts, idx, ndim, d, l, l+nsub-1); // return select(pts, idx, ndim, d, l, l+nsub-1, (nsub/2)+(nsub%2)); } int64_t partition_given_pivot(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, double pivot) { // If all less than pivot, j will remain r // If all greater than pivot, j will be l-1 if (r < l) return -1; int64_t i, j, tp = -1; uint64_t t; for (i = l, j = r; i <= j; ) { if ((pts[ndim*idx[i]+d] > pivot) && (pts[ndim*idx[j]+d] <= pivot)) { t = idx[i]; idx[i] = idx[j]; idx[j] = t; } if (isEqual(pts[ndim*idx[i]+d], pivot)) tp = i; // if (pts[ndim*idx[i]+d] == pivot) tp = i; if (pts[ndim*idx[i]+d] <= pivot) i++; if (pts[ndim*idx[j]+d] > pivot) j--; } if ((tp >= 0) && (tp != j)) { t = idx[tp]; idx[tp] = idx[j]; idx[j] = t; } return j; } int64_t partition(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, int64_t p) { double pivot; int64_t j; uint64_t t; if (r < l) return -1; pivot = pts[ndim*idx[p]+d]; t = idx[p]; idx[p] = idx[l]; idx[l] = t; j = partition_given_pivot(pts, idx, ndim, d, l+1, r, pivot); t = idx[l]; idx[l] = idx[j]; idx[j] = t; return j; } // https://en.wikipedia.org/wiki/Median_of_medians int64_t select(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l0, int64_t r0, int64_t n) { int64_t p; int64_t l = l0, r = r0; while ( 1 ) { if (l == r) return l; p = pivot(pts, idx, ndim, d, l, r); p = partition(pts, idx, ndim, d, l, r, p); if (p < 0) return -1; else if (n == (p-l0+1)) { return p; } else if (n < (p-l0+1)) { r = p - 1; } else { l = p + 1; } } } uint32_t split(double *all_pts, uint64_t *all_idx, uint64_t Lidx, uint64_t n, uint32_t ndim, double *mins, double *maxes, int64_t &split_idx, double &split_val, bool use_sliding_midpoint) { // Return immediately if variables empty if ((n == 0) || (ndim == 0)) { split_idx = -1; split_val = 0.0; return 0; } // Find dimension to split along uint32_t dmax, d; dmax = 0; for (d = 1; d < ndim; d++) if ((maxes[d]-mins[d]) > (maxes[dmax]-mins[dmax])) dmax = d; if (maxes[dmax] == mins[dmax]) { // all points singular return ndim; } if (use_sliding_midpoint) { // Split at middle, then slide midpoint as necessary split_val = (mins[dmax] + maxes[dmax])/2.0; split_idx = partition_given_pivot(all_pts, all_idx, ndim, dmax, Lidx, Lidx+n-1, split_val); if (split_idx == (int64_t)(Lidx-1)) { uint64_t t; split_idx = argmin_pts_dim(all_pts, all_idx, ndim, dmax, Lidx, Lidx+n-1); t = all_idx[split_idx]; all_idx[split_idx] = all_idx[Lidx]; all_idx[Lidx] = t; split_idx = Lidx; split_val = all_pts[ndim*all_idx[split_idx] + dmax]; } else if (split_idx == (int64_t)(Lidx+n-1)) { uint64_t t; split_idx = argmax_pts_dim(all_pts, all_idx, ndim, dmax, Lidx, Lidx+n-1); t = all_idx[split_idx]; all_idx[split_idx] = all_idx[Lidx+n-1]; all_idx[Lidx+n-1] = t; split_idx = Lidx+n-2; split_val = all_pts[ndim*all_idx[split_idx] + dmax]; } } else { // Find median along dimension int64_t nsel = (n/2) + (n%2); split_idx = select(all_pts, all_idx, ndim, dmax, Lidx, Lidx+n-1, nsel); split_val = all_pts[ndim*all_idx[split_idx] + dmax]; } return dmax; } yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/c_utils.hpp000066400000000000000000000032251510711153200240560ustar00rootroot00000000000000#include #include #include #include #include #include #include #include bool isEqual(double f1, double f2); double* max_pts(double *pts, uint64_t n, uint32_t m); double* min_pts(double *pts, uint64_t n, uint32_t m); uint64_t argmin_pts_dim(double *pts, uint64_t *idx, uint32_t m, uint32_t d, uint64_t Lidx, uint64_t Ridx); uint64_t argmax_pts_dim(double *pts, uint64_t *idx, uint32_t m, uint32_t d, uint64_t Lidx, uint64_t Ridx); void quickSort(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r); void insertSort(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r); int64_t pivot(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r); int64_t partition_given_pivot(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, double pivot); int64_t partition(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, int64_t p); int64_t select(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, int64_t n); uint32_t split(double *all_pts, uint64_t *all_idx, uint64_t Lidx, uint64_t n, uint32_t ndim, double *mins, double *maxes, int64_t &split_idx, double &split_val, bool use_sliding_midpoint = false); yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/kdtree.pxd000066400000000000000000000076101510711153200237000ustar00rootroot00000000000000cimport numpy as np from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t from libcpp cimport bool from libcpp.pair cimport pair from libcpp.vector cimport vector cdef extern from "" namespace "std": cdef cppclass istream: pass cdef cppclass ostream: pass # the following extern definitions adapted from # http://stackoverflow.com/a/31009461/1382869 # obviously std::ios_base isn't a namespace, but this lets # Cython generate the correct C++ code cdef extern from "" namespace "std::ios_base": cdef cppclass open_mode: pass cdef open_mode binary # you can define other constants as needed cdef extern from "" namespace "std": cdef cppclass ofstream(ostream): # constructors ofstream(const char*) except + ofstream(const char*, open_mode) except+ cdef cppclass ifstream(istream): # constructors ifstream(const char*) except + ifstream(const char*, open_mode) except+ cdef extern from "c_kdtree.hpp": cdef cppclass Node: bool is_leaf uint32_t leafid uint32_t ndim double *left_edge double *right_edge uint64_t left_idx uint64_t children uint32_t split_dim double split Node* less Node* greater bool *periodic_left bool *periodic_right vector[vector[uint32_t]] left_neighbors vector[vector[uint32_t]] right_neighbors vector[uint32_t] all_neighbors cdef cppclass KDTree: uint64_t* all_idx uint64_t npts uint32_t ndim int64_t data_version uint32_t leafsize double* domain_left_edge double* domain_right_edge double* domain_width double* domain_mins double* domain_maxs bool* periodic bool any_periodic uint32_t num_leaves vector[Node*] leaves Node* root KDTree(double *pts, uint64_t *idx, uint64_t n, uint32_t m, uint32_t leafsize0, double *left_edge, double *right_edge, bool *periodic, int64_t data_version) KDTree(double *pts, uint64_t *idx, uint64_t n, uint32_t m, uint32_t leafsize0, double *left_edge, double *right_edge, bool *periodic, int64_t data_version, bool use_sliding_midpoint) KDTree(istream &ist) void serialize(ostream &os) double* wrap_pos(double* pos) noexcept nogil vector[uint32_t] get_neighbor_ids(double* pos) noexcept nogil Node* search(double* pos) noexcept nogil Node* search(double* pos, bool dont_wrap) noexcept nogil void consolidate_edges(double *leaves_le, double *leaves_re) cdef class PyNode: cdef Node *_node cdef readonly np.uint32_t id cdef readonly np.uint64_t npts cdef readonly np.uint32_t ndim cdef readonly np.uint32_t num_leaves cdef readonly np.uint64_t start_idx cdef readonly np.uint64_t stop_idx cdef double *_domain_width cdef readonly object left_neighbors, right_neighbors cdef void _init_node(self, Node* node, uint32_t num_leaves, double *domain_width) cdef class PyKDTree: cdef KDTree *_tree cdef readonly uint64_t npts cdef readonly uint32_t ndim cdef readonly uint32_t num_leaves cdef readonly uint32_t leafsize cdef readonly int64_t data_version cdef double *_left_edge cdef double *_right_edge cdef bool *_periodic cdef readonly object leaves cdef readonly object _idx cdef void _init_tree(self, KDTree* tree) cdef void _make_tree(self, double *pts, bool use_sliding_midpoint) cdef void _make_leaves(self) cdef np.ndarray[np.uint32_t, ndim=1] _get_neighbor_ids(self, np.ndarray[double, ndim=1] pos) cdef np.ndarray[np.uint32_t, ndim=1] _get_neighbor_ids_3(self, np.float64_t pos[3]) cdef PyNode _get(self, np.ndarray[double, ndim=1] pos) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/kdtree.pyx000066400000000000000000000460461510711153200237330ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # Note that we used to include the empty c_kdtree.cpp file, but that seems to break cythonize. # distutils: sources = yt/utilities/lib/cykdtree/c_utils.cpp # distutils: depends = yt/utilities/lib/cykdtree/c_kdtree.hpp, yt/utilities/lib/cykdtree/c_utils.hpp # distutils: language = c++ # distutils: extra_compile_args = CPP11_FLAG import numpy as np cimport numpy as np from cpython cimport bool as pybool from cython.operator cimport dereference from libc.stdint cimport uint32_t, uint64_t from libc.stdlib cimport free, malloc from libcpp cimport bool as cbool cdef class PyNode: r"""A container for leaf info. Attributes: npts (np.uint64_t): Number of points in this node. ndim (np.uint32_t): Number of dimensions in domain. num_leaves (np.uint32_t): Number of leaves in the tree containing this node. start_idx (np.uint64_t): Index where indices for this node begin. stop_idx (np.uint64_t): One passed the end of indices for this node. left_edge (np.ndarray of float64): Minimum bounds of this node in each dimension. right_edge (np.ndarray of float64): Maximum bounds of this node in each dimension. periodic_left (np.ndarray of bool): Periodicity of minimum bounds. periodic_right (np.ndarray of bool): Periodicity of maximum bounds. domain_width (np.ndarray of float64): Width of the total domain in each dimension. left_neighbors (list of lists): Indices of neighbor leaves at the minimum bounds in each dimension. right_neighbors (list of lists): Indices of neighbor leaves at the maximum bounds in each dimension. """ cdef void _init_node(self, Node* node, uint32_t num_leaves, double *domain_width): cdef np.uint32_t i, j self._node = node self.id = node.leafid self.npts = node.children self.ndim = node.ndim self.num_leaves = num_leaves self.start_idx = node.left_idx self.stop_idx = (node.left_idx + node.children) self._domain_width = domain_width self.left_neighbors = [None for i in range(self.ndim)] self.right_neighbors = [None for i in range(self.ndim)] for i in range(self.ndim): self.left_neighbors[i] = [node.left_neighbors[i][j] for j in range(node.left_neighbors[i].size())] self.right_neighbors[i] = [node.right_neighbors[i][j] for j in range(node.right_neighbors[i].size())] def __cinit__(self): # Initialize everything to NULL/0/None to prevent seg fault self._node = NULL self.id = 0 self.npts = 0 self.ndim = 0 self.num_leaves = 0 self.start_idx = 0 self.stop_idx = 0 self._domain_width = NULL self.left_neighbors = None self.right_neighbors = None def __init__(self): pass def __repr__(self): nchars = 1 + len(str(self.__class__.__name__)) return ('%s(id=%i, npts=%i, start_idx=%i, stop_idx=%i,\n' + ' ' * nchars + 'left_edge=%s,\n' + ' ' * nchars + 'right_edge=%s)') % ( self.__class__.__name__, self.id, self.npts, self.start_idx, self.stop_idx, self.left_edge, self.right_edge, ) @property def periodic_left(self): cdef cbool[:] view = self._node.periodic_left return np.asarray(view) @property def periodic_right(self): cdef cbool[:] view = self._node.periodic_right return np.asarray(view) @property def left_edge(self): cdef np.float64_t[:] view = self._node.left_edge return np.asarray(view) @property def right_edge(self): cdef np.float64_t[:] view = self._node.right_edge return np.asarray(view) @property def domain_width(self): cdef np.float64_t[:] view = self._domain_width return np.asarray(view) @property def slice(self): """slice: Slice of kdtree indices contained by this node.""" return slice(self.start_idx, self.stop_idx) @property def neighbors(self): """list of int: Indices of all neighboring leaves including this leaf.""" cdef np.uint32_t i cdef object out cdef vector[uint32_t] vout = self._node.all_neighbors out = [vout[i] for i in range(vout.size())] return out def assert_equal(self, PyNode solf): """Assert that node properties are equal.""" np.testing.assert_equal(self.npts, solf.npts) np.testing.assert_equal(self.ndim, solf.ndim) np.testing.assert_equal(self.num_leaves, solf.num_leaves) np.testing.assert_equal(self.id, solf.id) np.testing.assert_equal(self.start_idx, solf.start_idx) np.testing.assert_equal(self.stop_idx, solf.stop_idx) np.testing.assert_array_equal(self.left_edge, solf.left_edge) np.testing.assert_array_equal(self.right_edge, solf.right_edge) np.testing.assert_array_equal(self.periodic_left, solf.periodic_left) np.testing.assert_array_equal(self.periodic_right, solf.periodic_right) for i in range(self.ndim): np.testing.assert_equal(self.left_neighbors[i], solf.left_neighbors[i]) np.testing.assert_equal(self.right_neighbors[i], solf.right_neighbors[i]) np.testing.assert_equal(self.neighbors, solf.neighbors) cdef class PyKDTree: r"""Construct a KDTree for a set of points. Args: pts (np.ndarray of double): (n,m) array of n coordinates in a m-dimensional domain. left_edge (np.ndarray of double): (m,) domain minimum in each dimension. right_edge (np.ndarray of double): (m,) domain maximum in each dimension. periodic (bool or np.ndarray of bool, optional): Truth of the domain periodicity overall (if bool), or in each dimension (if np.ndarray). Defaults to `False`. leafsize (int, optional): The maximum number of points that should be in a leaf. Defaults to 10000. nleaves (int, optional): The number of leaves that should be in the resulting tree. If greater than 0, leafsize is adjusted to produce a tree with 2**(ceil(log2(nleaves))) leaves. The leafsize keyword argument is ignored if nleaves is greater zero. Defaults to 0. data_version (int, optional): An optional user-provided integer that can be used to uniquely identify the data used to generate the KDTree. This is useful if you save the kdtree to disk and restore it later and need to verify that the underlying data is the same. use_sliding_midpoint (bool, optional): If True, the sliding midpoint rule is used to perform splits. Otherwise, the median is used. Defaults to False. Raises: ValueError: If `leafsize < 2`. This currently segfaults. Attributes: npts (uint64): Number of points in the tree. ndim (uint32): Number of dimensions points occupy. data_version (int64): User-provided version number (defaults to 0) num_leaves (uint32): Number of leaves in the tree. leafsize (uint32): Maximum number of points a leaf can have. leaves (list of `cykdtree.PyNode`): Tree leaves. idx (np.ndarray of uint64): Indices sorting the points by leaf. left_edge (np.ndarray of double): (m,) domain minimum in each dimension. right_edge (np.ndarray of double): (m,) domain maximum in each dimension. domain_width (np.ndarray of double): (m,) domain width in each dimension. periodic (np.ndarray of bool): Truth of domain periodicity in each dimension. """ cdef void _init_tree(self, KDTree* tree): self._tree = tree self.ndim = tree.ndim self.data_version = tree.data_version self.npts = tree.npts self.num_leaves = tree.num_leaves self.leafsize = tree.leafsize self._make_leaves() self._idx = np.empty(self.npts, 'uint64') cdef uint64_t i for i in range(self.npts): self._idx[i] = tree.all_idx[i] def __cinit__(self): # Initialize everything to NULL/0/None to prevent seg fault self._tree = NULL self.npts = 0 self.ndim = 0 self.num_leaves = 0 self.leafsize = 0 self._left_edge = NULL self._right_edge = NULL self._periodic = NULL self.leaves = None self._idx = None def __init__(self, np.ndarray[double, ndim=2] pts = None, left_edge = None, right_edge = None, periodic = False, int leafsize = 10000, int nleaves = 0, data_version = None, use_sliding_midpoint = False): # Return with nothing set if points not provided if pts is None: return # Set leafsize of number of leaves provided if nleaves > 0: nleaves = (2**np.ceil(np.log2(nleaves))) leafsize = pts.shape[0]//nleaves + 1 if (leafsize < 2): # This is here to prevent segfault. The cpp code needs modified to # support leafsize = 1 raise ValueError("'leafsize' cannot be smaller than 2.") if left_edge is None: left_edge = np.min(pts, axis=0) else: left_edge = np.array(left_edge) if right_edge is None: right_edge = np.max(pts, axis=0) else: right_edge = np.array(right_edge) if data_version is None: data_version = 0 self.data_version = data_version cdef uint32_t i self.npts = pts.shape[0] self.ndim = pts.shape[1] assert(left_edge.size == self.ndim) assert(right_edge.size == self.ndim) self.leafsize = leafsize self._left_edge = malloc(self.ndim*sizeof(double)) self._right_edge = malloc(self.ndim*sizeof(double)) self._periodic = malloc(self.ndim*sizeof(cbool)) for i in range(self.ndim): self._left_edge[i] = left_edge[i] self._right_edge[i] = right_edge[i] if isinstance(periodic, pybool): for i in range(self.ndim): self._periodic[i] = periodic else: for i in range(self.ndim): self._periodic[i] = periodic[i] # Create tree and leaves self._make_tree(&pts[0,0], use_sliding_midpoint) self._make_leaves() def __dealloc__(self): if self._tree != NULL: del self._tree if self._left_edge != NULL: free(self._left_edge) if self._right_edge != NULL: free(self._right_edge) if self._periodic != NULL: free(self._periodic) def assert_equal(self, PyKDTree solf, pybool strict_idx = True): r"""Compare this tree to another tree. Args: solf (PyKDTree): Another KDTree to compare with this one. strict_idx (bool, optional): If True, the index vectors are compared for equality element by element. If False, corresponding leaves must contain the same indices, but they can be in any order. Defaults to True. Raises: AssertionError: If there are mismatches between any of the two trees' parameters. """ np.testing.assert_equal(self.npts, solf.npts) np.testing.assert_equal(self.ndim, solf.ndim) np.testing.assert_equal(self.data_version, solf.data_version) np.testing.assert_equal(self.leafsize, solf.leafsize) np.testing.assert_equal(self.num_leaves, solf.num_leaves) np.testing.assert_array_equal(self.left_edge, solf.left_edge) np.testing.assert_array_equal(self.right_edge, solf.right_edge) np.testing.assert_array_equal(self.periodic, solf.periodic) # Compare index at the leaf level since we only care that the leaves # contain the same points if strict_idx: np.testing.assert_array_equal(self._idx, solf._idx) for i in range(self.num_leaves): self.leaves[i].assert_equal(solf.leaves[i]) if not strict_idx: np.testing.assert_array_equal( np.sort(self._idx[self.leaves[i].slice]), np.sort(solf._idx[solf.leaves[i].slice])) cdef void _make_tree(self, double *pts, bool use_sliding_midpoint): r"""Carry out creation of KDTree at C++ level.""" cdef uint64_t[:] idx = np.arange(self.npts).astype('uint64') self._tree = new KDTree(pts, &idx[0], self.npts, self.ndim, self.leafsize, self._left_edge, self._right_edge, self._periodic, self.data_version, use_sliding_midpoint) self._idx = idx cdef void _make_leaves(self): r"""Create a list of Python leaf objects from C++ leaves.""" self.num_leaves = self._tree.leaves.size() self.leaves = [None for _ in xrange(self.num_leaves)] cdef Node* leafnode cdef PyNode leafnode_py for k in xrange(self.num_leaves): leafnode = self._tree.leaves[k] leafnode_py = PyNode() leafnode_py._init_node(leafnode, self.num_leaves, self._tree.domain_width) self.leaves[leafnode.leafid] = leafnode_py @property def left_edge(self): cdef np.float64_t[:] view = self._tree.domain_left_edge return np.asarray(view) @property def right_edge(self): cdef np.float64_t[:] view = self._tree.domain_right_edge return np.asarray(view) @property def domain_width(self): cdef np.float64_t[:] view = self._tree.domain_width return np.asarray(view) @property def periodic(self): cdef cbool[:] view = self._tree.periodic # return np.asarray(view) cdef object out = np.empty(self.ndim, 'bool') cdef np.uint32_t i for i in range(self.ndim): out[i] = view[i] return out def leaf_idx(self, np.uint32_t leafid): r"""Get array of indices for points in a leaf. Args: leafid (np.uint32_t): Unique index of the leaf in question. Returns: np.ndarray of np.uint64_t: Indices of points belonging to leaf. """ cdef np.ndarray[np.uint64_t] out = self._idx[self.leaves[leafid].slice] return out cdef np.ndarray[np.uint32_t, ndim=1] _get_neighbor_ids(self, np.ndarray[double, ndim=1] pos): cdef np.uint32_t i cdef vector[uint32_t] vout = self._tree.get_neighbor_ids(&pos[0]) cdef np.ndarray[np.uint32_t, ndim=1] out = np.empty(vout.size(), 'uint32') for i in xrange(vout.size()): out[i] = vout[i] return out @property def idx(self): return np.asarray(self._idx) def get_neighbor_ids(self, np.ndarray[double, ndim=1] pos): r"""Return the IDs of leaves containing & neighboring a given position. Args: pos (np.ndarray of double): Coordinates. Returns: np.ndarray of uint32: Leaves containing/neighboring `pos`. Raises: ValueError: If pos is not contained within the KDTree. """ return self._get_neighbor_ids(pos) cdef np.ndarray[np.uint32_t, ndim=1] _get_neighbor_ids_3(self, np.float64_t pos[3]): cdef np.uint32_t i cdef vector[uint32_t] vout = self._tree.get_neighbor_ids(&pos[0]) cdef np.ndarray[np.uint32_t, ndim=1] out = np.empty(vout.size(), 'uint32') for i in xrange(vout.size()): out[i] = vout[i] return out cdef PyNode _get(self, np.ndarray[double, ndim=1] pos): assert(len(pos) == self.ndim) cdef Node* leafnode = self._tree.search(&pos[0]) if leafnode == NULL: raise ValueError("Position is not within the kdtree root node.") cdef PyNode out = self.leaves[leafnode.leafid] return out def get(self, np.ndarray[double, ndim=1] pos): r"""Return the leaf containing a given position. Args: pos (np.ndarray of double): Coordinates. Returns: :class:`cykdtree.PyNode`: Leaf containing `pos`. Raises: ValueError: If pos is not contained within the KDTree. """ return self._get(pos) def consolidate_edges(self): r"""Return arrays of the left and right edges for all leaves in the tree on each process. Returns: tuple(np.ndarray of double, np.ndarray of double): The left (first array) and right (second array) edges of each leaf (1st array dimension), in each dimension (2nd array dimension). """ cdef np.ndarray[np.float64_t, ndim=2] leaves_le cdef np.ndarray[np.float64_t, ndim=2] leaves_re leaves_le = np.empty((self.num_leaves, self.ndim), 'float64') leaves_re = np.empty((self.num_leaves, self.ndim), 'float64') self._tree.consolidate_edges(&leaves_le[0,0], &leaves_re[0,0]) return (leaves_le, leaves_re) def save(self, str filename): r"""Saves the PyKDTree to disk as raw binary data. Note that this file may not necessarily be portable. Args: filename (string): Name of the file to serialize the kdtree to """ cdef KDTree* my_tree = self._tree cdef ofstream* outputter = new ofstream(filename.encode('utf8'), binary) try: my_tree.serialize(dereference(outputter)) finally: del outputter @classmethod def from_file(cls, str filename, data_version=None): r"""Create a PyKDTree from a binary file created by ``PyKDTree.save()`` Note that loading a file created on another machine may create a corrupted PyKDTree instance. Args: filename (string): Name of the file to load the kdtree from data_version (int): A unique integer corresponding to the data being loaded. If the loaded data_version does not match the data_version supplied here then an OSError is raised. Optional. Returns: :class:`cykdtree.PyKDTree`: A KDTree restored from the file """ cdef ifstream* inputter = new ifstream(filename.encode(), binary) cdef PyKDTree ret = cls() if data_version is None: data_version = 0 try: ret._init_tree(new KDTree(dereference(inputter))) finally: del inputter return ret yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/plot.py000066400000000000000000000123211510711153200232300ustar00rootroot00000000000000import numpy as np def _plot2D_root( seg, pts=None, txt=None, plotfile=None, point_kw=None, box_kw=None, axs=None, subplot_kw=None, gridspec_kw=None, fig_kw=None, save_kw=None, title=None, xlabel="x", ylabel="y", label_kw=None, ): r"""Plot a 2D kd-tree. Args: seg (list of np.ndarray): Line segments to plot defining box edges. pts (np.ndarray, optional): Points contained by the kdtree. Defaults to None if not provided and points are not plotted. txt (list of tuples, optional): Each tuple contains the (x, y, string) information for text labels to be added to the boxes. Defaults to None and text is not added. plotfile (:obj:`str`, optional): Full path to file where the plot should be saved. If None, the plot is displayed. Defaults to None point_kw (:obj:`dict`, optional): Keywords passed directly to :func:`matplotlib.pyplot.scatter` for drawing the points. Defaults to empty dict. box_kw (:obj:`dict`, optional): Keywords passed directly to :class:`matplotlib.collections.LineCollection` for drawing the leaf boxes. Defaults to empty dict. axs (:obj:`matplotlib.pyplot.Axes`, optional): Axes that should be used for plotting. Defaults to None and new axes are created. subplot_kw (:obj:`dict`, optional): Keywords passed directly to :meth:`matplotlib.figure.Figure.add_subplot`. Defaults to {}. gridspec_kw (:obj:`dict`, optional): Keywords passed directly to :class:`matplotlib.gridspec.GridSpec`. Defaults to empty dict. fig_kw (:obj:`dict`, optional): Keywords passed directly to :func:`matplotlib.pyplot.figure`. Defaults to empty dict. save_kw (:obj:`dict`, optional): Keywords passed directly to :func:`matplotlib.pyplot.savefig`. Defaults to empty dict. title (:obj:`str`, optional): Title that the plot should be given. Defaults to None and no title is displayed. xlabel (:obj:`str`, optional): Label for the x-axis. Defaults to 'x'. ylabel (:obj:`str`, optional): Label for the y-axis. Defaults to 'y'. label_kw (:obj:`dict`, optional): Keywords passed directly to :class:`matplotlib.text.Text` when creating box labels. Defaults to empty dict. Returns: :obj:`matplotlib.pyplot.Axes`: Axes containing the plot. """ import matplotlib.pyplot as plt from matplotlib.collections import LineCollection if point_kw is None: point_kw = {} if box_kw is None: box_kw = {} if subplot_kw is None: subplot_kw = {} if gridspec_kw is None: gridspec_kw = {} if fig_kw is None: fig_kw = {} if save_kw is None: save_kw = {} if label_kw is None: label_kw = {} # Axes creation if axs is None: plt.close("all") fig, axs = plt.subplots( subplot_kw=subplot_kw, gridspec_kw=gridspec_kw, **fig_kw ) # Labels if title is not None: axs.set_title(title) axs.set_xlabel(xlabel, **label_kw) axs.set_ylabel(ylabel, **label_kw) # Plot points if isinstance(pts, list): for p in pts: if p is not None: axs.scatter(p[:, 0], p[:, 1], **point_kw) elif pts is not None: axs.scatter(pts[:, 0], pts[:, 1], **point_kw) # Plot boxes lc = LineCollection(seg, **box_kw) axs.add_collection(lc) # Labels if txt is not None: # label_kw.setdefault('axes', axs) label_kw.setdefault("verticalalignment", "bottom") label_kw.setdefault("horizontalalignment", "left") for t in txt: plt.text(*t, **label_kw) axs.autoscale() axs.margins(0.1) # Save if plotfile is not None: plt.savefig(plotfile, **save_kw) else: plt.show() # Return axes return axs def plot2D_serial(tree, pts=None, label_boxes=False, **kwargs): r"""Plot a 2D kd-tree constructed in serial. Parameters ---------- tree: :class:`cykdtree.kdtree.PyKDTree` kd-tree class. pts: np.ndarray, optional Points contained by the kdtree. label_boxes: bool If True, leaves in the tree are labeled with their index. Defaults to False. Additional keywords are passed to :func:`cykdtree.plot._plot2D_root`. Returns ------- :obj:`matplotlib.pyplot.Axes`: Axes containing the plot. """ # Box edges seg = [] for leaf in tree.leaves: le = leaf.left_edge re = leaf.right_edge # Top seg.append(np.array([[le[0], re[1]], [re[0], re[1]]], "float")) # Bottom seg.append(np.array([[le[0], le[1]], [re[0], le[1]]], "float")) # Left seg.append(np.array([[le[0], le[1]], [le[0], re[1]]], "float")) # Right seg.append(np.array([[re[0], le[1]], [re[0], re[1]]], "float")) # Labels txt = None if label_boxes: txt = [] for leaf in tree.leaves: txt.append((leaf.left_edge[0], leaf.left_edge[1], "%d" % leaf.id)) # Return axes return _plot2D_root(seg, pts=pts, txt=txt, **kwargs) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/tests/000077500000000000000000000000001510711153200230435ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/tests/__init__.py000066400000000000000000000166711510711153200251670ustar00rootroot00000000000000import cProfile import itertools import pstats import sys import time from datetime import datetime from subprocess import PIPE, Popen import numpy as np from nose.tools import nottest def assert_less_equal(x, y): size_match = True try: xshape = (1,) yshape = (1,) if isinstance(x, np.ndarray) or isinstance(y, np.ndarray): if isinstance(x, np.ndarray): xshape = x.shape if isinstance(y, np.ndarray): yshape = y.shape size_match = xshape == yshape assert (x <= y).all() else: assert x <= y except: if not size_match: raise AssertionError( "Shape mismatch\n\n" + f"x.shape: {str(x.shape)}\ny.shape: {str(y.shape)}\n" ) raise AssertionError( "Variables are not less-equal ordered\n\n" + f"x: {str(x)}\ny: {str(y)}\n" ) def call_subprocess(np, func, args, kwargs): # Create string with arguments & kwargs args_str = "" for a in args: args_str += f"{a}," for k, v in kwargs.items(): args_str += f"{k}={v}," if args_str.endswith(","): args_str = args_str[:-1] cmd = [ "mpirun", "-n", str(np), sys.executable, "-c", f"'from {func.__module__} import {func.__name__}; {func.__name__}({args_str})'", ] cmd = " ".join(cmd) print(f"Running the following command:\n{cmd}") p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True) output, err = p.communicate() exit_code = p.returncode print(output.decode("utf-8")) if exit_code != 0: print(err.decode("utf-8")) raise Exception("Error on spawned process. See output.") return None return output.decode("utf-8") def iter_dict(dicts): try: return ( dict(itertools.izip(dicts, x)) for x in itertools.product(*dicts.itervalues()) ) except AttributeError: # python 3 return (dict(zip(dicts, x)) for x in itertools.product(*dicts.values())) def parametrize(**pargs): for k in pargs.keys(): if not isinstance(pargs[k], (tuple, list)): pargs[k] = (pargs[k],) def dec(func): def pfunc(kwargs0): # Wrapper so that name encodes parameters def wrapped(*args, **kwargs): kwargs.update(**kwargs0) return func(*args, **kwargs) wrapped.__name__ = func.__name__ for k, v in kwargs0.items(): wrapped.__name__ += f"_{k}{v}" return wrapped def func_param(*args, **kwargs): out = [] for ipargs in iter_dict(pargs): out.append(pfunc(ipargs)(*args, **kwargs)) return out func_param.__name__ = func.__name__ return func_param return dec np.random.seed(100) pts2 = np.random.rand(100, 2).astype("float64") pts3 = np.random.rand(100, 3).astype("float64") rand_state = np.random.get_state() left_neighbors_x = [[], [0], [1], [2], [], [], [4, 5], [5]] # None # None # None left_neighbors_y = [ [], # None [], # None [], # None [], # None [0, 1], [4], [1, 2, 3], [6], ] left_neighbors_x_periodic = [[3], [0], [1], [2], [6], [6, 7], [4, 5], [5]] left_neighbors_y_periodic = [[5], [5, 7], [7], [7], [0, 1], [4], [1, 2, 3], [6]] @nottest def make_points_neighbors(periodic=False): ndim = 2 npts = 50 leafsize = 10 np.random.set_state(rand_state) pts = np.random.rand(npts, ndim).astype("float64") left_edge = np.zeros(ndim, "float64") right_edge = np.ones(ndim, "float64") if periodic: lx = left_neighbors_x_periodic ly = left_neighbors_y_periodic else: lx = left_neighbors_x ly = left_neighbors_y num_leaves = len(lx) ln = [lx, ly] rn = [[[] for i in range(num_leaves)] for _ in range(ndim)] for d in range(ndim): for i in range(num_leaves): for j in ln[d][i]: rn[d][j].append(i) for i in range(num_leaves): rn[d][i] = list(set(rn[d][i])) return pts, left_edge, right_edge, leafsize, ln, rn @nottest def make_points(npts, ndim, leafsize=10, distrib="rand", seed=100): ndim = int(ndim) npts = int(npts) leafsize = int(leafsize) np.random.seed(seed) LE = 0.0 RE = 1.0 left_edge = LE * np.ones(ndim, "float64") right_edge = RE * np.ones(ndim, "float64") if npts <= 0: npts = 100 leafsize = 10 if ndim == 2: pts = pts2 elif ndim == 3: pts = pts3 else: pts = np.random.rand(npts, ndim).astype("float64") else: if distrib == "rand": pts = np.random.rand(npts, ndim).astype("float64") elif distrib == "uniform": pts = np.random.uniform(low=LE, high=RE, size=(npts, ndim)) elif distrib in ("gaussian", "normal"): pts = np.random.normal( loc=(LE + RE) / 2.0, scale=(RE - LE) / 4.0, size=(npts, ndim) ) np.clip(pts, LE, RE) else: raise ValueError(f"Invalid 'distrib': {distrib}") return pts, left_edge, right_edge, leafsize @nottest def run_test( npts, ndim, nproc=0, distrib="rand", periodic=False, leafsize=10, profile=False, suppress_final_output=False, **kwargs, ): r"""Run a routine with a designated number of points & dimensions on a selected number of processors. Args: npart (int): Number of particles. nproc (int): Number of processors. ndim (int): Number of dimensions. distrib (str, optional): Distribution that should be used when generating points. Defaults to 'rand'. periodic (bool, optional): If True, the domain is assumed to be periodic. Defaults to False. leafsize (int, optional): Maximum number of points that should be in an leaf. Defaults to 10. profile (bool, optional): If True cProfile is used. Defaults to False. suppress_final_output (bool, optional): If True, the final output from spawned MPI processes is suppressed. This is mainly for timing purposes. Defaults to False. """ from yt.utilities.lib.cykdtree import make_tree unique_str = datetime.today().strftime("%Y%j%H%M%S") pts, left_edge, right_edge, leafsize = make_points( npts, ndim, leafsize=leafsize, distrib=distrib ) # Set keywords for multiprocessing version if nproc > 1: kwargs["suppress_final_output"] = suppress_final_output if profile: kwargs["profile"] = f"{unique_str}_mpi_profile.dat" # Run if profile: pr = cProfile.Profile() t0 = time.time() pr.enable() make_tree( pts, nproc=nproc, left_edge=left_edge, right_edge=right_edge, periodic=periodic, leafsize=leafsize, **kwargs, ) if profile: pr.disable() t1 = time.time() ps = pstats.Stats(pr) ps.add(kwargs["profile"]) if isinstance(profile, str): ps.dump_stats(profile) print(f"Stats saved to {profile}") else: sort_key = "tottime" ps.sort_stats(sort_key).print_stats(25) # ps.sort_stats(sort_key).print_callers(5) print(f"{t1 - t0} s according to 'time'") return ps yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/tests/scaling.py000066400000000000000000000171601510711153200250420ustar00rootroot00000000000000r"""Routines for tracking the scaling of the triangulation routines.""" import cProfile import os import pstats import time import numpy as np from yt.utilities.lib.cykdtree.tests import run_test def stats_run( npart, nproc, ndim, periodic=False, overwrite=False, display=False, suppress_final_output=False, ): r"""Get timing stats using :package:`cProfile`. Args: npart (int): Number of particles. nproc (int): Number of processors. ndim (int): Number of dimensions. periodic (bool, optional): If True, the domain is assumed to be periodic. Defaults to False. overwrite (bool, optional): If True, the existing file for this set of input parameters if overwritten. Defaults to False. suppress_final_output (bool, optional): If True, the final output from spawned MPI processes is suppressed. This is mainly for timing purposes. Defaults to False. display (bool, optional): If True, display the profile results. Defaults to False. """ perstr = "" outstr = "" if periodic: perstr = "_periodic" if suppress_final_output: outstr = "_noout" fname_stat = f"stat_{npart}part_{nproc}proc_{ndim}dim{perstr}{outstr}.txt" if overwrite or not os.path.isfile(fname_stat): cProfile.run( "from yt.utilities.lib.cykdtree.tests import run_test; " + f"run_test({npart}, {ndim}, nproc={nproc}, " + f"periodic={periodic}, " + f"suppress_final_output={suppress_final_output})", fname_stat, ) if display: p = pstats.Stats(fname_stat) p.sort_stats("time").print_stats(10) return p return fname_stat def time_run( npart, nproc, ndim, nrep=1, periodic=False, leafsize=10, suppress_final_output=False ): r"""Get running times using :package:`time`. Args: npart (int): Number of particles. nproc (int): Number of processors. ndim (int): Number of dimensions. nrep (int, optional): Number of times the run should be performed to get an average. Defaults to 1. periodic (bool, optional): If True, the domain is assumed to be periodic. Defaults to False. leafsize (int, optional): The maximum number of points that should be in any leaf in the tree. Defaults to 10. suppress_final_output (bool, optional): If True, the final output from spawned MPI processes is suppressed. This is mainly for timing purposes. Defaults to False. """ times = np.empty(nrep, "float") for i in range(nrep): t1 = time.time() run_test( npart, ndim, nproc=nproc, periodic=periodic, leafsize=leafsize, suppress_final_output=suppress_final_output, ) t2 = time.time() times[i] = t2 - t1 return np.mean(times), np.std(times) def strong_scaling( npart=1e6, nrep=1, periodic=False, leafsize=10, overwrite=True, suppress_final_output=False, ): r"""Plot the scaling with number of processors for a particular function. Args: npart (int, optional): Number of particles. Defaults to 1e6. nrep (int, optional): Number of times the run should be performed to get an average. Defaults to 1. periodic (bool, optional): If True, the domain is assumed to be periodic. Defaults to False. leafsize (int, optional): The maximum number of points that should be in any leaf in the tree. Defaults to 10. overwrite (bool, optional): If True, the existing file for this set of input parameters if overwritten. Defaults to False. suppress_final_output (bool, optional): If True, the final output from spawned MPI processes is suppressed. This is mainly for timing purposes. Defaults to False. """ import matplotlib.pyplot as plt npart = int(npart) perstr = "" outstr = "" if periodic: perstr = "_periodic" if suppress_final_output: outstr = "_noout" fname_plot = ( f"plot_strong_scaling_nproc_{npart}part{perstr}_{leafsize}leafsize{outstr}.png" ) nproc_list = [1, 2, 4, 8] # , 16] ndim_list = [2, 3, 4] clr_list = ["b", "r", "g", "m"] times = np.empty((len(nproc_list), len(ndim_list), 2), "float") for j, nproc in enumerate(nproc_list): for i, ndim in enumerate(ndim_list): times[j, i, 0], times[j, i, 1] = time_run( npart, nproc, ndim, nrep=nrep, periodic=periodic, leafsize=leafsize, suppress_final_output=suppress_final_output, ) print(f"Finished {ndim}D on {nproc}.") fig, axs = plt.subplots(1, 1) for i in range(len(ndim_list)): ndim = ndim_list[i] clr = clr_list[i] axs.errorbar( nproc_list, times[:, i, 0], yerr=times[:, i, 1], fmt=clr, label=f"ndim = {ndim}", ) axs.set_xlabel("# of Processors") axs.set_ylabel("Time (s)") axs.legend() fig.savefig(fname_plot) print(" " + fname_plot) def weak_scaling( npart=1e4, nrep=1, periodic=False, leafsize=10, overwrite=True, suppress_final_output=False, ): r"""Plot the scaling with number of processors with a constant number of particles per processor for a particular function. Args: npart (int, optional): Number of particles per processor. Defaults to 1e4. nrep (int, optional): Number of times the run should be performed to get an average. Defaults to 1. periodic (bool, optional): If True, the domain is assumed to be periodic. Defaults to False. leafsize (int, optional): The maximum number of points that should be in any leaf in the tree. Defaults to 10. overwrite (bool, optional): If True, the existing file for this set of input parameters if overwritten. Defaults to False. suppress_final_output (bool, optional): If True, the final output from spawned MPI processes is suppressed. This is mainly for timing purposes. Defaults to False. """ import matplotlib.pyplot as plt npart = int(npart) perstr = "" outstr = "" if periodic: perstr = "_periodic" if suppress_final_output: outstr = "_noout" fname_plot = ( f"plot_weak_scaling_nproc_{npart}part{perstr}_{leafsize}leafsize{outstr}.png" ) nproc_list = [1, 2, 4, 8, 16] ndim_list = [2, 3] clr_list = ["b", "r", "g", "m"] times = np.empty((len(nproc_list), len(ndim_list), 2), "float") for j, nproc in enumerate(nproc_list): for i, ndim in enumerate(ndim_list): times[j, i, 0], times[j, i, 1] = time_run( npart * nproc, nproc, ndim, nrep=nrep, periodic=periodic, leafsize=leafsize, suppress_final_output=suppress_final_output, ) fig, axs = plt.subplots(1, 1) for i in range(len(ndim_list)): ndim = ndim_list[i] clr = clr_list[i] axs.errorbar( nproc_list, times[:, i, 0], yerr=times[:, i, 1], fmt=clr, label=f"ndim = {ndim}", ) axs.set_xlabel("# of Processors") axs.set_ylabel("Time (s)") axs.legend() fig.savefig(fname_plot) print(" " + fname_plot) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/tests/test_kdtree.py000066400000000000000000000100501510711153200257260ustar00rootroot00000000000000import tempfile import time import numpy as np from nose.tools import assert_raises import yt.utilities.lib.cykdtree as cykdtree from yt.utilities.lib.cykdtree.tests import ( make_points, make_points_neighbors, parametrize, ) @parametrize( npts=100, ndim=(2, 3), periodic=(False, True), use_sliding_midpoint=(False, True) ) def test_PyKDTree(npts=100, ndim=2, periodic=False, use_sliding_midpoint=False): pts, le, re, ls = make_points(npts, ndim) cykdtree.PyKDTree( pts, le, re, leafsize=ls, periodic=periodic, use_sliding_midpoint=use_sliding_midpoint, ) def test_PyKDTree_errors(): pts, le, re, ls = make_points(100, 2) assert_raises(ValueError, cykdtree.PyKDTree, pts, le, re, leafsize=1) @parametrize(npts=100, ndim=(2, 3), periodic=(False, True)) def test_search(npts=100, ndim=2, periodic=False): pts, le, re, ls = make_points(npts, ndim) tree = cykdtree.PyKDTree(pts, le, re, leafsize=ls, periodic=periodic) pos_list = [le, (le + re) / 2.0] if periodic: pos_list.append(re) for pos in pos_list: leaf = tree.get(pos) leaf.neighbors @parametrize(npts=100, ndim=(2, 3)) def test_search_errors(npts=100, ndim=2): pts, le, re, ls = make_points(npts, ndim) tree = cykdtree.PyKDTree(pts, le, re, leafsize=ls) assert_raises(ValueError, tree.get, re) @parametrize(periodic=(False, True)) def test_neighbors(periodic=False): pts, le, re, ls, left_neighbors, right_neighbors = make_points_neighbors( periodic=periodic ) tree = cykdtree.PyKDTree(pts, le, re, leafsize=ls, periodic=periodic) for leaf in tree.leaves: out_str = str(leaf.id) try: for d in range(tree.ndim): out_str += f"\nleft: {d} {leaf.left_neighbors[d]} {left_neighbors[d][leaf.id]}" assert len(left_neighbors[d][leaf.id]) == len(leaf.left_neighbors[d]) for i in range(len(leaf.left_neighbors[d])): assert left_neighbors[d][leaf.id][i] == leaf.left_neighbors[d][i] out_str += f"\nright: {d} {leaf.right_neighbors[d]} {right_neighbors[d][leaf.id]}" assert len(right_neighbors[d][leaf.id]) == len(leaf.right_neighbors[d]) for i in range(len(leaf.right_neighbors[d])): assert right_neighbors[d][leaf.id][i] == leaf.right_neighbors[d][i] except Exception as e: for leaf in tree.leaves: print(leaf.id, leaf.left_edge, leaf.right_edge) print(out_str) raise e @parametrize(npts=100, ndim=(2, 3), periodic=(False, True)) def test_get_neighbor_ids(npts=100, ndim=2, periodic=False): pts, le, re, ls = make_points(npts, ndim) tree = cykdtree.PyKDTree(pts, le, re, leafsize=ls, periodic=periodic) pos_list = [le, (le + re) / 2.0] if periodic: pos_list.append(re) for pos in pos_list: tree.get_neighbor_ids(pos) def time_tree_construction(Ntime, LStime, ndim=2): pts, le, re, ls = make_points(Ntime, ndim, leafsize=LStime) t0 = time.time() cykdtree.PyKDTree(pts, le, re, leafsize=LStime) t1 = time.time() print(f"{Ntime} {ndim}D points, leafsize {LStime}: took {t1 - t0} s") def time_neighbor_search(Ntime, LStime, ndim=2): pts, le, re, ls = make_points(Ntime, ndim, leafsize=LStime) tree = cykdtree.PyKDTree(pts, le, re, leafsize=LStime) t0 = time.time() tree.get_neighbor_ids(0.5 * np.ones(tree.ndim, "double")) t1 = time.time() print(f"{Ntime} {ndim}D points, leafsize {LStime}: took {t1 - t0} s") def test_save_load(): for periodic in (True, False): for ndim in range(1, 5): pts, le, re, ls = make_points(100, ndim) tree = cykdtree.PyKDTree( pts, le, re, leafsize=ls, periodic=periodic, data_version=ndim + 12 ) with tempfile.NamedTemporaryFile(delete=False) as tf: tree.save(tf.name) restore_tree = cykdtree.PyKDTree.from_file(tf.name) tree.assert_equal(restore_tree) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/tests/test_plot.py000066400000000000000000000010131510711153200254250ustar00rootroot00000000000000import os from yt.utilities.lib.cykdtree.kdtree import PyKDTree from yt.utilities.lib.cykdtree.plot import plot2D_serial from yt.utilities.lib.cykdtree.tests import make_points def test_plot2D_serial(): fname_test = "test_plot2D_serial.png" pts, le, re, ls = make_points(100, 2) tree = PyKDTree(pts, le, re, leafsize=ls) axs = plot2D_serial( tree, pts, title="Serial Test", plotfile=fname_test, label_boxes=True ) os.remove(fname_test) # plot2D_serial(tree, pts, axs=axs) del axs yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/tests/test_utils.py000066400000000000000000000140501510711153200256140ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.utilities.lib.cykdtree import utils # type: ignore from yt.utilities.lib.cykdtree.tests import assert_less_equal, parametrize def test_max_pts(): pts = np.arange(5 * 3, dtype="float64").reshape((5, 3)) out = utils.py_max_pts(pts) np.testing.assert_allclose(out, np.max(pts, axis=0)) def test_min_pts(): pts = np.arange(5 * 3, dtype="float64").reshape((5, 3)) out = utils.py_min_pts(pts) np.testing.assert_allclose(out, np.min(pts, axis=0)) @parametrize(N=(10), ndim=(2, 3), Lidx=(0, 5), Ridx=(5, 9)) def test_argmax_pts_dim(N=10, ndim=2, Lidx=0, Ridx=9): d = ndim - 1 pts = np.random.rand(N, ndim).astype("float64") idx = np.argsort(pts[:, d]).astype("uint64") out = utils.py_argmax_pts_dim(pts, idx, d, Lidx, Ridx) assert_equal(out, np.argmax(pts[idx[Lidx : (Ridx + 1)], d]) + Lidx) @parametrize(N=(10), ndim=(2, 3), Lidx=(0, 5), Ridx=(5, 9)) def test_argmin_pts_dim(N=10, ndim=2, Lidx=0, Ridx=9): d = ndim - 1 pts = np.random.rand(N, ndim).astype("float64") idx = np.argsort(pts[:, d]).astype("uint64") out = utils.py_argmin_pts_dim(pts, idx, d, Lidx, Ridx) assert_equal(out, np.argmin(pts[idx[Lidx : (Ridx + 1)], d]) + Lidx) @parametrize(N=(0, 10, 11), ndim=(2, 3)) def test_quickSort(N=10, ndim=2): d = ndim - 1 np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") idx = utils.py_quickSort(pts, d) assert_equal(idx.size, N) if N != 0: np.testing.assert_allclose(idx, np.argsort(pts[:, d])) @parametrize(N=(0, 10, 11), ndim=(2, 3)) def test_insertSort(N=10, ndim=2): d = ndim - 1 np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") idx = utils.py_insertSort(pts, d) assert_equal(idx.size, N) if N != 0: np.testing.assert_allclose(idx, np.argsort(pts[:, d])) @parametrize(N=(0, 10, 11), ndim=(2, 3)) def test_pivot(N=10, ndim=2): d = ndim - 1 np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") q, idx = utils.py_pivot(pts, d) if N == 0: np.testing.assert_equal(q, -1) else: piv = pts[idx[q], d] nmax = 7 * N / 10 + 6 assert_less_equal(np.sum(pts[:, d] < piv), nmax) assert_less_equal(np.sum(pts[:, d] > piv), nmax) @parametrize(N=(0, 10, 11), ndim=(2, 3)) def test_partition(N=10, ndim=2): d = ndim - 1 p = 0 np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") q, idx = utils.py_partition(pts, d, p) if N == 0: assert_equal(q, -1) else: piv = pts[p, d] np.testing.assert_approx_equal(pts[idx[q], d], piv) np.testing.assert_array_less(pts[idx[:q], d], piv) np.testing.assert_array_less(piv, pts[idx[(q + 1) :], d]) @parametrize(N=(0, 10, 11), ndim=(2, 3)) def test_partition_given_pivot(N=10, ndim=2): d = ndim - 1 np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") if N == 0: piv_list = [0.5] else: piv_list = [0.5, np.median(pts[:, d])] for piv in piv_list: q, idx = utils.py_partition_given_pivot(pts, d, piv) if N == 0: assert_equal(q, -1) else: assert_less_equal(pts[idx[q], d], piv) np.testing.assert_array_less(pts[idx[:q], d], piv) np.testing.assert_array_less(piv, pts[idx[(q + 1) :], d]) @parametrize(N=(0, 10, 11), ndim=(2, 3)) def test_select(N=10, ndim=2): d = ndim - 1 np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") p = int(N) // 2 + int(N) % 2 q, idx = utils.py_select(pts, d, p) assert_equal(idx.size, N) if N == 0: assert_equal(q, -1) else: assert_equal(q, p - 1) med = np.median(pts[:, d]) np.testing.assert_array_less(pts[idx[:q], d], med) np.testing.assert_array_less(med, pts[idx[(q + 1) :], d]) if N % 2: np.testing.assert_approx_equal(pts[idx[q], d], med) else: np.testing.assert_array_less(pts[idx[q], d], med) @parametrize(N=(0, 10, 11), ndim=(2, 3), use_sliding_midpoint=(False, True)) def test_split(N=10, ndim=2, use_sliding_midpoint=False): np.random.seed(10) pts = np.random.rand(N, ndim).astype("float64") p = int(N) // 2 + int(N) % 2 q, d, idx = utils.py_split(pts, use_sliding_midpoint=use_sliding_midpoint) assert_equal(idx.size, N) if N == 0: assert_equal(q, -1) else: if use_sliding_midpoint: # Midpoint med = 0.5 * (np.min(pts[:, d]) + np.max(pts[:, d])) np.testing.assert_array_less(pts[idx[:q], d], med) np.testing.assert_array_less(med, pts[idx[(q + 1) :], d]) np.testing.assert_array_less(pts[idx[q], d], med) # Sliding midpoint (slide to minimum) q, d, idx = utils.py_split( pts, mins=-1 * np.ones(ndim), maxs=np.ones(ndim), use_sliding_midpoint=True, ) med = np.min(pts[:, d]) assert_equal(q, 0) np.testing.assert_array_less(pts[idx[:q], d], med) np.testing.assert_array_less(med, pts[idx[(q + 1) :], d]) np.testing.assert_approx_equal(pts[idx[q], d], med) # Sliding midpoint (slide to maximum) q, d, idx = utils.py_split( pts, mins=np.zeros(ndim), maxs=2 * np.ones(ndim), use_sliding_midpoint=True, ) med = np.max(pts[:, d]) assert_equal(q, N - 2) np.testing.assert_array_less(pts[idx[: (q + 1)], d], med) np.testing.assert_approx_equal(pts[idx[q + 1], d], med) else: assert_equal(q, p - 1) med = np.median(pts[:, d]) np.testing.assert_array_less(pts[idx[:q], d], med) np.testing.assert_array_less(med, pts[idx[(q + 1) :], d]) if N % 2: np.testing.assert_approx_equal(pts[idx[q], d], med) else: np.testing.assert_array_less(pts[idx[q], d], med) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/utils.pxd000066400000000000000000000037631510711153200235670ustar00rootroot00000000000000cimport numpy as np from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t from libcpp cimport bool from libcpp.pair cimport pair from libcpp.vector cimport vector cdef extern from "c_utils.hpp": double* max_pts(double *pts, uint64_t n, uint64_t m) double* min_pts(double *pts, uint64_t n, uint64_t m) uint64_t argmax_pts_dim(double *pts, uint64_t *idx, uint32_t m, uint32_t d, uint64_t Lidx, uint64_t Ridx) uint64_t argmin_pts_dim(double *pts, uint64_t *idx, uint32_t m, uint32_t d, uint64_t Lidx, uint64_t Ridx) void quickSort(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r) int64_t partition(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, int64_t p) int64_t partition_given_pivot(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, double pivot) int64_t select(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r, int64_t n) int64_t pivot(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r) void insertSort(double *pts, uint64_t *idx, uint32_t ndim, uint32_t d, int64_t l, int64_t r) uint32_t split(double *all_pts, uint64_t *all_idx, uint64_t Lidx, uint64_t n, uint32_t ndim, double *mins, double *maxes, int64_t &split_idx, double &split_val) uint32_t split(double *all_pts, uint64_t *all_idx, uint64_t Lidx, uint64_t n, uint32_t ndim, double *mins, double *maxes, int64_t &split_idx, double &split_val, bool use_sliding_midpoint) yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/utils.pyx000066400000000000000000000303111510711153200236010ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: sources = yt/utilities/lib/cykdtree/c_utils.cpp # distutils: depends = yt/utilities/lib/cykdtree/c_utils.hpp # distutils: language = c++ # distutils: extra_compile_args = CPP11_FLAG import numpy as np cimport numpy as np from libc.stdint cimport int64_t, uint32_t, uint64_t from libcpp cimport bool as cbool def py_max_pts(np.ndarray[np.float64_t, ndim=2] pos): r"""Get the maximum of points along each coordinate. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. Returns: np.ndarray of float64: Maximum of pos along each coordinate. """ cdef uint64_t n = pos.shape[0] cdef uint32_t m = pos.shape[1] cdef np.float64_t* cout = max_pts(&pos[0,0], n, m) cdef uint32_t i = 0 cdef np.ndarray[np.float64_t] out = np.zeros(m, 'float64') for i in range(m): out[i] = cout[i] #free(cout) return out def py_min_pts(np.ndarray[np.float64_t, ndim=2] pos): r"""Get the minimum of points along each coordinate. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. Returns: np.ndarray of float64: Minimum of pos along each coordinate. """ cdef uint64_t n = pos.shape[0] cdef uint32_t m = pos.shape[1] cdef np.float64_t* cout = min_pts(&pos[0,0], n, m) cdef uint32_t i = 0 cdef np.ndarray[np.float64_t] out = np.zeros(m, 'float64') for i in range(m): out[i] = cout[i] #free(cout) return out def py_argmax_pts_dim(np.ndarray[np.float64_t, ndim=2] pos, uint64_t[:] idx, np.uint32_t d, int Lidx0, int Ridx0): r"""Get the maximum of points along one dimension for a subset of the point indices. This is essentially max(pos[idx[Lidx:(Ridx+1)], d]). Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. idx (np.ndarray of uint64_t): (n,) array of indices for positions. d (uint32_t): Dimension to compute maximum along. Lidx (int): Index in idx that search should begin at. Ridx (int): Index in idx that search should end at. Returns: uint64_t: Index in idx that provides maximum position in the subset indices along dimension d. """ cdef np.intp_t n = pos.shape[0] cdef uint32_t m = pos.shape[1] cdef uint64_t Lidx = 0 cdef uint64_t Ridx = (n-1) if (Lidx0 < 0): Lidx = (n + Lidx0) elif Lidx0 >= n: raise Exception("Left index (%d) exceeds size of positions array (%d)." % (Lidx0, n)) else: Lidx = Lidx0 if (Ridx0 < 0): Ridx = (n + Ridx0) elif Ridx0 >= n: raise Exception("Right index (%d) exceeds size of positions array (%d)." % (Ridx0, n)) else: Ridx = Ridx0 cdef np.uint64_t cout = Lidx if (Ridx > Lidx): cout = argmax_pts_dim(&pos[0,0], &idx[0], m, d, Lidx, Ridx) return cout def py_argmin_pts_dim(np.ndarray[np.float64_t, ndim=2] pos, uint64_t[:] idx, np.uint32_t d, int Lidx0, int Ridx0): r"""Get the minimum of points along one dimension for a subset of the point indices. This is essentially min(pos[idx[Lidx:(Ridx+1)], d]). Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. idx (np.ndarray of uint64_t): (n,) array of indices for positions. d (uint32_t): Dimension to compute minimum along. Lidx (int): Index in idx that search should begin at. Ridx (int): Index in idx that search should end at. Returns: uint64_t: Index in idx that provides minimum position in the subset indices along dimension d. """ cdef uint64_t n = pos.shape[0] cdef uint32_t m = pos.shape[1] cdef uint64_t Lidx = 0 cdef uint64_t Ridx = n if (Lidx0 < 0): Lidx = (n + Lidx0) else: Lidx = Lidx0 if (Ridx0 < 0): Ridx = (n + Ridx0) else: Ridx = Ridx0 cdef np.uint64_t cout = Lidx if (Ridx > Lidx): cout = argmin_pts_dim(&pos[0,0], &idx[0], m, d, Lidx, Ridx) return cout def py_quickSort(np.ndarray[np.float64_t, ndim=2] pos, np.uint32_t d): r"""Get the indices required to sort coordinates along one dimension. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. d (np.uint32_t): Dimension that pos should be sorted along. Returns: np.ndarray of uint64: Indices that sort pos along dimension d. """ cdef np.intp_t ndim = pos.shape[1] cdef int64_t l = 0 cdef int64_t r = pos.shape[0]-1 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL if pos.shape[0] != 0: ptr_pos = &pos[0,0] ptr_idx = &idx[0] quickSort(ptr_pos, ptr_idx, ndim, d, l, r) return idx def py_insertSort(np.ndarray[np.float64_t, ndim=2] pos, np.uint32_t d): r"""Get the indices required to sort coordinates along one dimension. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. d (np.uint32_t): Dimension that pos should be sorted along. Returns: np.ndarray of uint64: Indices that sort pos along dimension d. """ cdef np.intp_t ndim = pos.shape[1] cdef int64_t l = 0 cdef int64_t r = pos.shape[0]-1 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL if pos.shape[0] != 0: ptr_pos = &pos[0,0] ptr_idx = &idx[0] insertSort(ptr_pos, ptr_idx, ndim, d, l, r) return idx def py_pivot(np.ndarray[np.float64_t, ndim=2] pos, np.uint32_t d): r"""Get the index of the median of medians along one dimension and indices that partition pos according to the median of medians. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. d (np.uint32_t): Dimension that pos should be partitioned along. Returns: tuple of int64 and np.ndarray of uint64: Index q of idx that is the pivot. All elements of idx before the pivot will be less than the pivot. If there is an odd number of points, the pivot will be the median. """ cdef np.intp_t ndim = pos.shape[1] cdef int64_t l = 0 cdef int64_t r = pos.shape[0]-1 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL if pos.shape[0] != 0: ptr_pos = &pos[0,0] ptr_idx = &idx[0] cdef int64_t q = pivot(ptr_pos, ptr_idx, ndim, d, l, r) return q, idx def py_partition(np.ndarray[np.float64_t, ndim=2] pos, np.uint32_t d, np.int64_t p): r"""Get the indices required to partition coordinates along one dimension. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. d (np.uint32_t): Dimension that pos should be partitioned along. p (np.int64_t): Element of pos[:,d] that should be used as the pivot to partition pos. Returns: tuple of int64 and np.ndarray of uint64: Location of the pivot in the partitioned array and the indices required to partition the array such that elements before the pivot are smaller and elements after the pivot are larger. """ cdef np.intp_t ndim = pos.shape[1] cdef int64_t l = 0 cdef int64_t r = pos.shape[0]-1 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL if pos.shape[0] != 0: ptr_pos = &pos[0,0] ptr_idx = &idx[0] cdef int64_t q = partition(ptr_pos, ptr_idx, ndim, d, l, r, p) return q, idx def py_partition_given_pivot(np.ndarray[np.float64_t, ndim=2] pos, np.uint32_t d, np.float64_t pval): r"""Get the indices required to partition coordinates along one dimension. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. d (np.uint32_t): Dimension that pos should be partitioned along. pval (np.float64_t): Value that should be used to partition pos. Returns: tuple of int64 and np.ndarray of uint64: Location of the largest value that is smaller than pval in partitioned array and the indices required to partition the array such that elements before the pivot are smaller and elements after the pivot are larger. """ cdef np.intp_t ndim = pos.shape[1] cdef int64_t l = 0 cdef int64_t r = pos.shape[0]-1 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL if pos.shape[0] != 0: ptr_pos = &pos[0,0] ptr_idx = &idx[0] cdef int64_t q = partition_given_pivot(ptr_pos, ptr_idx, ndim, d, l, r, pval) return q, idx def py_select(np.ndarray[np.float64_t, ndim=2] pos, np.uint32_t d, np.int64_t t): r"""Get the indices required to partition coordinates such that the first t elements in pos[:,d] are the smallest t elements in pos[:,d]. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. d (np.uint32_t): Dimension that pos should be partitioned along. t (np.int64_t): Number of smallest elements in pos[:,d] that should be partitioned. Returns: tuple of int64 and np.ndarray of uint64: Location of element t in the partitioned array and the indices required to partition the array such that elements before element t are smaller and elements after the pivot are larger. """ cdef np.intp_t ndim = pos.shape[1] cdef int64_t l = 0 cdef int64_t r = pos.shape[0]-1 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL if pos.shape[0] != 0: ptr_pos = &pos[0,0] ptr_idx = &idx[0] cdef int64_t q = select(ptr_pos, ptr_idx, ndim, d, l, r, t) return q, idx def py_split(np.ndarray[np.float64_t, ndim=2] pos, np.ndarray[np.float64_t, ndim=1] mins = None, np.ndarray[np.float64_t, ndim=1] maxs = None, bool use_sliding_midpoint = False): r"""Get the indices required to split the positions equally along the largest dimension. Args: pos (np.ndarray of float64): (n,m) array of n m-D coordinates. mins (np.ndarray of float64, optional): (m,) array of mins. Defaults to None and is set to mins of pos along each dimension. maxs (np.ndarray of float64, optional): (m,) array of maxs. Defaults to None and is set to maxs of pos along each dimension. use_sliding_midpoint (bool, optional): If True, the sliding midpoint rule is used to split the positions. Defaults to False. Returns: tuple(int64, uint32, np.ndarray of uint64): The index of the split in the partitioned array, the dimension of the split, and the indices required to partition the array. """ cdef np.intp_t npts = pos.shape[0] cdef np.intp_t ndim = pos.shape[1] cdef uint64_t Lidx = 0 cdef uint64_t[:] idx idx = np.arange(pos.shape[0]).astype('uint64') cdef double *ptr_pos = NULL cdef uint64_t *ptr_idx = NULL cdef double *ptr_mins = NULL cdef double *ptr_maxs = NULL if (npts != 0) and (ndim != 0): if mins is None: mins = np.min(pos, axis=0) if maxs is None: maxs = np.max(pos, axis=0) ptr_pos = &pos[0,0] ptr_idx = &idx[0] ptr_mins = &mins[0] ptr_maxs = &maxs[0] cdef int64_t q = 0 cdef double split_val = 0.0 cdef cbool c_midpoint_flag = use_sliding_midpoint cdef uint32_t dsplit = split(ptr_pos, ptr_idx, Lidx, npts, ndim, ptr_mins, ptr_maxs, q, split_val, c_midpoint_flag) return q, dsplit, idx yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/windows/000077500000000000000000000000001510711153200233735ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/cykdtree/windows/stdint.h000066400000000000000000000176341510711153200250640ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2013 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the product nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #if _MSC_VER >= 1600 // [ #include #else // ] _MSC_VER >= 1600 [ #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants // These #ifndef's are needed to prevent collisions with . // Check out Issue 9 for the details. #ifndef INTMAX_C // [ # define INTMAX_C INT64_C #endif // INTMAX_C ] #ifndef UINTMAX_C // [ # define UINTMAX_C UINT64_C #endif // UINTMAX_C ] #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_VER >= 1600 ] #endif // _MSC_STDINT_H_ ] yt-project-yt-f043ac8/yt/utilities/lib/cyoctree.pyx000066400000000000000000000673331510711153200224620ustar00rootroot00000000000000# distutils: libraries = STD_LIBS """ CyOctree building, loading and refining routines """ cimport cython cimport libc.math as math cimport numpy as np import numpy as np from libc.stdlib cimport free, malloc from yt.geometry.particle_deposit cimport get_kernel_func, kernel_func np.import_array() cdef struct Octree: # Array of 3*num_nodes [x1, y1, z1, x2, y2, z2, ...] np.float64_t * node_positions # 1 or 0 of whether the oct has refined to make children np.uint8_t * refined # Each oct stores the depth in the tree: the root node is 0 np.uint8_t * depth # pstart and pend tell us which particles in the pidx are stored in each oct np.int64_t * pstart np.int64_t * pend # This tells us the index of each child np.int64_t * children # Here we store the coordinates and IDs of all the particles in the tree np.float64_t * pposx np.float64_t * pposy np.float64_t * pposz np.int64_t * pidx # The max number of particles per leaf, if above, we refine np.int64_t n_ref # The number of particles in our tree, e.g the length of ppos and pidx np.int64_t num_particles # The total size of the octree (x, y, z) np.float64_t * size # The current number of nodes in the octree np.int64_t num_nodes # The maximum depth before we stop refining, and the maximum number of nodes # we can fit in our array np.uint8_t max_depth np.int64_t max_num_nodes @cython.boundscheck(False) @cython.wraparound(False) cdef int octree_build_node(Octree * tree, long int node_idx): """ This is the main function in the building of the octree. This function takes in the tree and an index of a oct to process. If the node has too many particles (> n_ref) and the depth is less than the maximum tree depth then we refine. The `refine` creates 8 sub octs, within our oct. We indenfity the particles in each of the subocts. We then recursively call this function on each of the subocts. Parameters ---------- tree : Octree * A pointer to the octree node_idx : long int The index of the current node we are processing Returns ------- int Success of tree build """ cdef np.int64_t splits[9] cdef np.int64_t i, j, k, n, start, end cdef np.float64_t lx, ly, lz, sz, inv_size # If we are running out of space in our tree, then we *try* to # relloacate a tree of double the size if (tree.num_nodes + 8) >= tree.max_num_nodes: if octree_reallocate(tree, tree.max_num_nodes * 2): return 1 if ( (tree.pend[node_idx] - tree.pstart[node_idx] > tree.n_ref) and (tree.depth[node_idx] < tree.max_depth) ): tree.refined[node_idx] = 1 # As we have decided to refine, we need to know which of the particles # in this oct will go into each of the 8 child octs split_helper(tree, node_idx, splits) # Figure out the size of the current oct inv_size = 1. / 2.**tree.depth[node_idx] sx = tree.size[0] * inv_size sy = tree.size[1] * inv_size sz = tree.size[2] * inv_size lx = tree.node_positions[3*node_idx] - sx/2. ly = tree.node_positions[(3*node_idx)+1] - sy/2. lz = tree.node_positions[(3*node_idx)+2] - sz/2. # Loop through and generate the children AND recursively refine... n = 0 for i in range(2): for j in range(2): for k in range(2): start = splits[n] end = splits[n + 1] child = tree.num_nodes # Store the child location tree.children[8*node_idx + n] = child tree.node_positions[child*3] = lx + sx*i tree.node_positions[(child*3)+1] = ly + sy*j tree.node_positions[(child*3)+2] = lz + sz*k tree.refined[child] = 0 tree.depth[child] = tree.depth[node_idx] + 1 tree.pstart[child] = start tree.pend[child] = end tree.num_nodes += 1 # Recursively refine child if octree_build_node(tree, child): return 1 n += 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef int octree_allocate(Octree * octree, long int num_nodes): """ This is the main allocation function in the octree. We allocate all of the arrays we require to store information about every single oct in the tree Parameters ---------- octree : Octree * A pointer to the octree num_nodes : long int The maximum number of nodes to allocate for Returns ------- int Success of allocation """ octree.node_positions = malloc( num_nodes * 3 * sizeof(np.float64_t)) if octree.node_positions == NULL: return 1 octree.size = malloc(3 * sizeof(np.float64_t)) if octree.size == NULL: return 1 octree.children = malloc(8 * num_nodes * sizeof(np.int64_t)) if octree.children == NULL: return 1 octree.pstart = malloc(num_nodes * sizeof(np.int64_t)) if octree.pstart == NULL: return 1 octree.pend = malloc(num_nodes * sizeof(np.int64_t)) if octree.pend == NULL: return 1 octree.refined = malloc(num_nodes * sizeof(np.int8_t)) if octree.refined == NULL: return 1 octree.depth = malloc(num_nodes * sizeof(np.int8_t)) if octree.depth == NULL: return 1 return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef int octree_reallocate(Octree * octree, long int num_nodes): """ This function re-allocates all of the arrays malloc'd in `octree_allocate` See Notes for when we want to re-allocate. Parameters ---------- octree : Octree * A pointer to the octree num_nodes : long int The maximum number of nodes to (re)allocate for Returns ------- int Success of the reallocation Notes ----- Why do we want to re-allocate? Well 2 cases, 1) The octree is still building and we have ran out of space, so we have asked for an increased number of nodes and are reallocating each array 2) We have finished building the octree and we have used less nodes than we originally allocated. We are now shrinking the octree and giving the spare memory back. """ cdef np.float64_t * old_arr cdef np.int64_t * old_arr_int cdef np.uint8_t * old_arr_uint cdef np.int64_t i old_arr = octree.node_positions octree.node_positions = malloc(num_nodes * 3 * sizeof(np.float64_t)) if octree.node_positions == NULL: return 1 for i in range(3*octree.num_nodes): octree.node_positions[i] = old_arr[i] free(old_arr) old_arr_int = octree.children octree.children = malloc(num_nodes * 8 * sizeof(np.int64_t)) if octree.children == NULL: return 1 for i in range(8*octree.num_nodes): octree.children[i] = old_arr_int[i] free(old_arr_int) old_arr_int = octree.pstart octree.pstart = malloc(num_nodes * sizeof(np.int64_t)) if octree.pstart == NULL: return 1 for i in range(octree.num_nodes): octree.pstart[i] = old_arr_int[i] free(old_arr_int) old_arr_int = octree.pend octree.pend = malloc(num_nodes * sizeof(np.int64_t)) if octree.pend == NULL: return 1 for i in range(octree.num_nodes): octree.pend[i] = old_arr_int[i] free(old_arr_int) old_arr_uint = octree.refined octree.refined = malloc(num_nodes * sizeof(np.int8_t)) if octree.refined == NULL: return 1 for i in range(octree.num_nodes): octree.refined[i] = old_arr_uint[i] free(old_arr_uint) old_arr_uint = octree.depth octree.depth = malloc(num_nodes * sizeof(np.int8_t)) if octree.depth == NULL: return 1 for i in range(octree.num_nodes): octree.depth[i] = old_arr_uint[i] free(old_arr_uint) octree.max_num_nodes = num_nodes return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef void octree_deallocate(Octree * octree): """ Just free-ing every array we allocated to ensure we don't leak. Parameter --------- octree : Octree * Pointer to the octree """ free(octree.node_positions) free(octree.size) free(octree.children) free(octree.pstart) free(octree.pend) free(octree.refined) free(octree.depth) free(octree.pidx) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef class CyOctree: """ This a class to store the underlying octree and particle data that can be interacted with from both Cython and Python """ cdef Octree * c_octree cdef np.float64_t[::1, :] input_positions cdef np.int64_t n_ref cdef np.float64_t[:] left_edge cdef np.float64_t[:] right_edge cdef np.uint8_t max_depth cdef kernel_func kernel def __init__( self, np.float64_t[:, :] input_pos, left_edge=None, right_edge=None, np.int64_t n_ref=32, np.uint8_t max_depth=200 ): """ Octree initialiser. We copy the inputted particle positions and make the root node. We then refine the octree until every leaf has either less particles than n_ref or is at the maximum depth. Finally, we re-allocate all of the memory required by tree to ensure we do not use more memory than required. Parameters ---------- input_pos : 2D memory view Particles positions in the format (num_particles, 3) {left,right}_edge : ndarray xyz coordinates of the lower left (upper right) corner of the octree. n_ref : int, default: 32 The maximum number of particles per leaf, if more, the oct will refine max_depth : int, default: 200 The maximum depth the octree will refine to. If we set this too high then we may hit a stack overflow due to the recursive nature of the build """ self.n_ref = n_ref self.max_depth = max_depth self.input_positions = np.asfortranarray(input_pos, dtype=np.float64) if self._allocate_octree(): raise MemoryError("Unable to allocate memory required for octree build.") self._make_root(left_edge, right_edge) if octree_build_node(self.c_octree, 0): raise MemoryError("Unable to allocate memory required for octree build.") if octree_reallocate(self.c_octree, self.c_octree.num_nodes): raise MemoryError("Unable to allocate memory required for octree build.") def __del__(self): """ Make sure we clean up properly! """ octree_deallocate(self.c_octree) free(self.c_octree) @property def bound_particles(self): """ The particle selection may select SPH particles with smoothing lengths which is in the octree domains. However, if the particle center is NOT in the octree, they are not included. So the number of particles passed to the tree *may* not be equal to the number of particles which are bound by the tree. """ return self.c_octree.pend[0] @property def num_nodes(self): """ The total number of nodes after tree construction """ return self.c_octree.num_nodes @property def node_positions(self): """ The centre of every node within the octree """ cdef np.npy_intp shape[2] shape[0] = self.c_octree.num_nodes shape[1] = 3 arr = np.PyArray_SimpleNewFromData( 2, &shape[0], np.NPY_FLOAT64, self.c_octree.node_positions) return np.copy(arr) @property def node_refined(self): """ An array of length num_nodes which contains either True / False for whether each cell has refined or not. E.g False for a leaf, True for a node """ cdef np.npy_intp shape shape = self.c_octree.num_nodes arr = np.PyArray_SimpleNewFromData( 1, &shape, np.NPY_UINT8, self.c_octree.refined) return np.copy(arr).astype(np.bool_) @property def node_depth(self): """ The depth for each node in the tree. The root node is defined as a depth of 0. """ cdef np.npy_intp shape = self.c_octree.num_nodes arr = np.PyArray_SimpleNewFromData( 1, &shape, np.NPY_UINT8, self.c_octree.depth) return np.copy(arr) @property def node_sizes(self): """ The size of each node in the x, y and z directions. We calculate this on the fly. As we know the size of the whole tree and the depth of each node """ cdef np.int64_t i sizes = np.zeros((self.c_octree.num_nodes, 3), dtype=np.float64) sizes[:, 0] = self.c_octree.size[0] sizes[:, 1] = self.c_octree.size[1] sizes[:, 2] = self.c_octree.size[2] for i in range(self.c_octree.num_nodes): sizes[i, :] /= (2.0**self.c_octree.depth[i] / 2.0) return sizes def _make_root(self, left_edge, right_edge): """ The root node is the hardest node to build as we need to find out which particles we contain, and then sieving them into children is easy. In the case that the left_edge/right_edge is not defined then we select a tree that is sufficiently big to contain every particle. However, if they are defined then we need to loop through and find out which particles are actually in the tree. Parameters ---------- {left,right}_edge : ndarray xyz coordinates of the lower left (upper right) corner of the octree. If None, the tree will be made large enough to encompass all particles. """ cdef int i = 0 # How many particles are there? self.c_octree.num_particles = self.input_positions.shape[0] # We now number all of the the particles in the tree. This allows us # to shuffle the pids and say for example Oct11 contains particles # 7 to 11 # This pidx[7:12] would give the input indices of the particles we # store. This proxy allows us to re-arrange the particles without # re-arranging the users input data. self.c_octree.pidx = malloc( self.c_octree.num_particles * sizeof(np.int64_t) ) for i in range(0, self.c_octree.num_particles): self.c_octree.pidx[i] = i if left_edge is None: # If the edges are None, then we can just find the loop through # and find them out left_edge = np.zeros(3, dtype=np.float64) right_edge = np.zeros(3, dtype=np.float64) left_edge[0] = self.c_octree.pposx[0] left_edge[1] = self.c_octree.pposy[0] left_edge[2] = self.c_octree.pposz[0] right_edge[0] = self.c_octree.pposx[0] right_edge[1] = self.c_octree.pposy[0] right_edge[2] = self.c_octree.pposz[0] for i in range(self.c_octree.num_particles): left_edge[0] = min(self.c_octree.pposx[i], left_edge[0]) left_edge[1] = min(self.c_octree.pposy[i], left_edge[1]) left_edge[2] = min(self.c_octree.pposz[i], left_edge[2]) right_edge[0] = max(self.c_octree.pposx[i], right_edge[0]) right_edge[1] = max(self.c_octree.pposy[i], right_edge[1]) right_edge[2] = max(self.c_octree.pposz[i], right_edge[2]) self.c_octree.pstart[0] = 0 self.c_octree.pend[0] = self.input_positions.shape[0] else: # Damn! The user did supply a left and right so we need to find # which particles are in the range left_edge = left_edge.astype(np.float64) right_edge = right_edge.astype(np.float64) # We loop through the particles and arrange them such that particles # in the tree are to the left of the split and the particles not # are to the right # e.g. # pidx = [1, 2, 3, 5 | 0, 4] # where split = 4 and particles 0 and 4 are not in the tree split = select( self.c_octree, left_edge, right_edge, 0, self.input_positions.shape[0]) self.c_octree.pstart[0] = 0 self.c_octree.pend[0] = split # Set the total size of the tree size = (right_edge - left_edge) / 2.0 center = (right_edge + left_edge) / 2.0 self.left_edge = left_edge self.right_edge = right_edge # Now we add the data about the root node! self.c_octree.node_positions[0] = center[0] self.c_octree.node_positions[1] = center[1] self.c_octree.node_positions[2] = center[2] self.c_octree.size[0] = size[0] self.c_octree.size[1] = size[1] self.c_octree.size[2] = size[2] # We are not refined yet self.c_octree.refined[0] = 0 self.c_octree.depth[0] = 0 def _allocate_octree(self): """ This actually allocates the C struct Octree """ self.c_octree = malloc(sizeof(Octree)) self.c_octree.n_ref = self.n_ref self.c_octree.num_nodes = 1 # This is sort of an arbitrary guess but it doesn't matter because # we will increase this value and attempt to reallocate if it is too # small self.c_octree.max_num_nodes = max(self.input_positions.shape[0] / self.n_ref, 1) self.c_octree.max_depth = self.max_depth # Fast C pointers to the particle coordinates self.c_octree.pposx = &self.input_positions[0, 0] self.c_octree.pposy = &self.input_positions[0, 1] self.c_octree.pposz = &self.input_positions[0, 2] if octree_allocate(self.c_octree, self.c_octree.max_num_nodes): return 1 cdef void smooth_onto_cells( self, np.float64_t[:] buff, np.float64_t[:] buff_den, np.float64_t posx, np.float64_t posy, np.float64_t posz, np.float64_t hsml, np.float64_t prefactor, np.float64_t prefactor_norm, long int num_node, int use_normalization=0 ): """ We smooth a field onto cells within an octree using SPH deposition. To achieve this we loop through every oct in the tree and check if it is a leaf. A leaf just means that an oct has not refined, and thus has no children. Parameters ---------- buff : memoryview The array which we are depositing the field onto, it has the length of the number of leaves. buff_den : memoryview The array we deposit just mass onto to allow normalization pos<> : float64_t The x, y, and z coordinates of the particle we are depositing hsml : float64_t The smoothing length of the particle prefactor(_norm) : float64_t This is a pre-computed value, based on the particles properties used in the deposition num_node : lonmg int The current node we are checking to see if refined or not use_normalization : int, default: 0 Do we want a normalized sph field? If so, fill the buff_den. """ cdef Octree * tree = self.c_octree cdef double q_ij, diff_x, diff_y, diff_z, diff, sx, sy, sz cdef int i cdef long int child_node if tree.refined[num_node] == 0: diff_x = tree.node_positions[3*num_node] - posx diff_y = tree.node_positions[3*num_node+1] - posy diff_z = tree.node_positions[3*num_node+2] - posz q_ij = math.sqrt(diff_x*diff_x + diff_y*diff_y + diff_z*diff_z) q_ij /= hsml buff[num_node] += (prefactor * self.kernel(q_ij)) if use_normalization: buff_den[num_node] += (prefactor_norm * self.kernel(q_ij)) else: # All direct children of the current node are the same size, thus # we can compute their size once, outside of the loop sz_factor = 1.0 / 2.0**(tree.depth[num_node] + 1) sqrt_sz_factor = math.sqrt(sz_factor) sx = tree.size[0] sy = tree.size[1] sz = tree.size[2] child_node_size = sqrt_sz_factor * math.sqrt(sx*sx + sy*sy + sz*sz) for i in range(8): child_node = tree.children[8*num_node + i] diff_x = tree.node_positions[3*child_node] - posx diff_y = tree.node_positions[3*child_node+1] - posy diff_z = tree.node_positions[3*child_node+2] - posz diff = math.sqrt(diff_x*diff_x + diff_y*diff_y + diff_z*diff_z) # Could the current particle possibly intersect this child node? if diff - child_node_size - hsml < 0: self.smooth_onto_cells(buff, buff_den, posx, posy, posz, hsml, prefactor, prefactor_norm, child_node, use_normalization=use_normalization) def interpolate_sph_cells(self, np.float64_t[:] buff, np.float64_t[:] buff_den, np.float64_t[:] posx, np.float64_t[:] posy, np.float64_t[:] posz, np.float64_t[:] pmass, np.float64_t[:] pdens, np.float64_t[:] hsml, np.float64_t[:] field, kernel_name="cubic", int use_normalization=0 ): """ We loop through every particle in the simulation and begin to deposit the particle properties onto all of the leaves that it intersects Parameters ---------- buff : memoryview The array which we are depositing the field onto, it has the length of the number of leaves. buff_den : memoryview The array we deposit just mass onto to allow normalization pos<> : memoryview The x, y, and z coordinates of all the partciles pmass : memoryview The mass of the particles pdens : memoryview The density of the particles hsml : memoryview The smoothing lengths of the particles field : memoryview The field we are depositing for each particle kernel_name: str, default: "cubic" Choice of kernel for SPH deposition use_normalization : int, default: 0 Do we want a normalized sph field? If so, fill the buff_den. """ self.kernel = get_kernel_func(kernel_name) cdef int i cdef double prefactor, prefactor_norm for i in range(posx.shape[0]): prefactor = pmass[i] / pdens[i] / hsml[i]**3 prefactor_norm = prefactor prefactor *= field[i] self.smooth_onto_cells(buff, buff_den, posx[i], posy[i], posz[i], hsml[i], prefactor, prefactor_norm, 0, use_normalization=use_normalization) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t separate( np.float64_t * pos, np.int64_t * pidx, double value, np.int64_t start, np.int64_t end ) noexcept nogil: """ This is a simple utility function which takes a selection of particles and re-arranges them such that values below `value` are to the left of split and values above are to the right. Parameters ---------- pos : float64_t * Pointer to the coordinates we are splitting along pidx : int64_t & Pointer to the corresponding particle IDs value : double The value to split the data along start : int64_t Index of first particle in the current node end : int64_t Index of the last particle in the current node """ cdef np.int64_t index cdef np.int64_t split = start for index in range(start, end): if pos[pidx[index]] < value: pidx[split], pidx[index] = pidx[index], pidx[split] split += 1 return split @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void split_helper(Octree * tree, np.int64_t node_idx, np.int64_t * splits): """ A utility function to split a collection of particles along the x, y and z direction such that we identify the particles within the 8 children of an oct. We first split the particles in the x direction, then we split the particles in the y and the z directions. This is currently hardcoded for 8 octs but can be readily extended to allow over-refinement. The splits[0] and splits[1] tell oct one the start and last particle The splits[1] and splits[2] tell oct two the start and last particle and so on Parameters ---------- tree : Octree * A pointer to the octree node_idx : int64_t The index of the oct we are splitting splits : int64_t * Pointer to split array which stores the start and end indices. It needs to be N+1 long where N is the number of children """ splits[0] = tree.pstart[node_idx] splits[8] = tree.pend[node_idx] splits[4] = separate( tree.pposx, tree.pidx, tree.node_positions[3*node_idx], splits[0], splits[8]) splits[2] = separate( tree.pposy, tree.pidx, tree.node_positions[(3*node_idx)+1], splits[0], splits[4]) splits[6] = separate( tree.pposy, tree.pidx, tree.node_positions[(3*node_idx)+1], splits[4], splits[8]) splits[1] = separate( tree.pposz, tree.pidx, tree.node_positions[(3*node_idx)+2], splits[0], splits[2]) splits[3] = separate( tree.pposz, tree.pidx, tree.node_positions[(3*node_idx)+2], splits[2], splits[4]) splits[5] = separate( tree.pposz, tree.pidx, tree.node_positions[(3*node_idx)+2], splits[4], splits[6]) splits[7] = separate( tree.pposz, tree.pidx, tree.node_positions[(3*node_idx)+2], splits[6], splits[8]) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t select( Octree * octree, np.float64_t[::1] left_edge, np.float64_t[::1] right_edge, np.int64_t start, np.int64_t end ) noexcept nogil: """ Re-arrange the input particles such that those outside the bounds of the tree occur after the split index and can thus be ignored for the remainder of the tree construction Parameters ---------- tree : Octree * A pointer to the octree left_edge : ndarray The coords of the lower left corner of the box right_edge : ndarray The coords of the upper right corner of the box start : int64_t The first particle in the bounds (0) end : int64_t The last particle in the bounds """ cdef np.int64_t index cdef np.int64_t split = start cdef np.float64_t * posx = octree.pposx cdef np.float64_t * posy = octree.pposy cdef np.float64_t * posz = octree.pposz cdef np.int64_t * pidx = octree.pidx for index in range(start, end): if posx[pidx[index]] < right_edge[0] and posx[pidx[index]] > left_edge[0]: if posy[pidx[index]] < right_edge[1] and posy[pidx[index]] > left_edge[1]: if posz[pidx[index]] < right_edge[2] and posz[pidx[index]] > left_edge[2]: if split < index: pidx[split], pidx[index] = pidx[index], pidx[split] split += 1 return split yt-project-yt-f043ac8/yt/utilities/lib/depth_first_octree.pyx000066400000000000000000000143121510711153200245060ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ This is a recursive function to return a depth-first octree """ import numpy as np cimport cython cimport numpy as np cdef class position: cdef public int output_pos, refined_pos def __cinit__(self): self.output_pos = 0 self.refined_pos = 0 cdef class OctreeGrid: cdef public object child_indices, fields, left_edges, dimensions, dx cdef public int level, offset def __cinit__(self, np.ndarray[np.int32_t, ndim=3] child_indices, np.ndarray[np.float64_t, ndim=4] fields, np.ndarray[np.float64_t, ndim=1] left_edges, np.ndarray[np.int32_t, ndim=1] dimensions, np.ndarray[np.float64_t, ndim=1] dx, int level, int offset): self.child_indices = child_indices self.fields = fields self.left_edges = left_edges self.dimensions = dimensions self.dx = dx self.level = level self.offset = offset cdef class OctreeGridList: cdef public object grids def __cinit__(self, grids): self.grids = grids def __getitem__(self, int item): return self.grids[item] @cython.boundscheck(False) def RecurseOctreeDepthFirst(int i_i, int j_i, int k_i, int i_f, int j_f, int k_f, position curpos, int gi, np.ndarray[np.float64_t, ndim=2] output, np.ndarray[np.int32_t, ndim=1] refined, OctreeGridList grids): #cdef int s = curpos cdef int i, i_off, j, j_off, k, k_off, ci, fi cdef int child_i, child_j, child_k cdef OctreeGrid child_grid cdef OctreeGrid grid = grids[gi] cdef np.ndarray[np.float64_t, ndim=4] fields = grid.fields cdef np.ndarray[np.float64_t, ndim=1] leftedges = grid.left_edges cdef np.float64_t dx = grid.dx[0] cdef np.float64_t child_dx cdef np.ndarray[np.float64_t, ndim=1] child_leftedges cdef np.float64_t cx, cy, cz #here we go over the 8 octants #in general however, a mesh cell on this level #may have more than 8 children on the next level #so we find the int float center (cxyz) of each child cell # and from that find the child cell indices for i_off in range(i_f): i = i_off + i_i #index cx = (leftedges[0] + i*dx) for j_off in range(j_f): j = j_off + j_i cy = (leftedges[1] + j*dx) for k_off in range(k_f): k = k_off + k_i cz = (leftedges[2] + k*dx) ci = grid.child_indices[i,j,k] if ci == -1: for fi in range(fields.shape[0]): output[curpos.output_pos,fi] = fields[fi,i,j,k] refined[curpos.refined_pos] = 0 curpos.output_pos += 1 curpos.refined_pos += 1 else: refined[curpos.refined_pos] = 1 curpos.refined_pos += 1 child_grid = grids[ci-grid.offset] child_dx = child_grid.dx[0] child_leftedges = child_grid.left_edges child_i = int((cx - child_leftedges[0])/child_dx) child_j = int((cy - child_leftedges[1])/child_dx) child_k = int((cz - child_leftedges[2])/child_dx) # s = Recurs..... RecurseOctreeDepthFirst(child_i, child_j, child_k, 2, 2, 2, curpos, ci - grid.offset, output, refined, grids) @cython.boundscheck(False) def RecurseOctreeByLevels(int i_i, int j_i, int k_i, int i_f, int j_f, int k_f, np.ndarray[np.int32_t, ndim=1] curpos, int gi, np.ndarray[np.float64_t, ndim=2] output, np.ndarray[np.int32_t, ndim=2] genealogy, np.ndarray[np.float64_t, ndim=2] corners, OctreeGridList grids): cdef np.int32_t i, i_off, j, j_off, k, k_off, ci, fi cdef int child_i, child_j, child_k cdef OctreeGrid child_grid cdef OctreeGrid grid = grids[gi-1] cdef int level = grid.level cdef np.ndarray[np.int32_t, ndim=3] child_indices = grid.child_indices cdef np.ndarray[np.float64_t, ndim=4] fields = grid.fields cdef np.ndarray[np.float64_t, ndim=1] leftedges = grid.left_edges cdef np.float64_t dx = grid.dx[0] cdef np.float64_t child_dx cdef np.ndarray[np.float64_t, ndim=1] child_leftedges cdef np.float64_t cx, cy, cz cdef int cp s = None for i_off in range(i_f): i = i_off + i_i cx = (leftedges[0] + i*dx) for j_off in range(j_f): j = j_off + j_i cy = (leftedges[1] + j*dx) for k_off in range(k_f): k = k_off + k_i cz = (leftedges[2] + k*dx) cp = curpos[level] corners[cp, 0] = cx corners[cp, 1] = cy corners[cp, 2] = cz genealogy[curpos[level], 2] = level # always output data for fi in range(fields.shape[0]): output[cp,fi] = fields[fi,i,j,k] ci = child_indices[i,j,k] if ci > -1: child_grid = grids[ci-1] child_dx = child_grid.dx[0] child_leftedges = child_grid.left_edges child_i = int((cx-child_leftedges[0])/child_dx) child_j = int((cy-child_leftedges[1])/child_dx) child_k = int((cz-child_leftedges[2])/child_dx) # set current child id to id of next cell to examine genealogy[cp, 0] = curpos[level+1] # set next parent id to id of current cell genealogy[curpos[level+1]:curpos[level+1]+8, 1] = cp s = RecurseOctreeByLevels(child_i, child_j, child_k, 2, 2, 2, curpos, ci, output, genealogy, corners, grids) curpos[level] += 1 return s yt-project-yt-f043ac8/yt/utilities/lib/distance_queue.pxd000066400000000000000000000023351510711153200236050ustar00rootroot00000000000000""" A queue for evaluating distances to discrete points """ cimport cython cimport numpy as np import numpy as np from libc.stdlib cimport free, malloc from libc.string cimport memmove # THESE TWO STRUCTS MUST BE EQUIVALENT cdef struct ItemList: np.int64_t ind np.float64_t value cdef struct NeighborList: np.int64_t pn # Particle number np.float64_t r2 # radius**2 cdef int Neighbor_compare(void *on1, void *on2) nogil cdef np.float64_t r2dist(np.float64_t ppos[3], np.float64_t cpos[3], np.float64_t DW[3], bint periodicity[3], np.float64_t max_dist2) cdef class PriorityQueue: cdef int maxn cdef int curn cdef ItemList* items cdef void item_reset(self) cdef int item_insert(self, np.int64_t i, np.float64_t value) cdef class DistanceQueue(PriorityQueue): cdef np.float64_t DW[3] cdef bint periodicity[3] cdef NeighborList* neighbors # flat array cdef void _setup(self, np.float64_t DW[3], bint periodicity[3]) cdef void neighbor_eval(self, np.int64_t pn, np.float64_t ppos[3], np.float64_t cpos[3]) cdef void neighbor_reset(self) yt-project-yt-f043ac8/yt/utilities/lib/distance_queue.pyx000066400000000000000000000133651510711153200236370ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Distance queue implementation """ cimport numpy as np import numpy as np cimport cython cdef int Neighbor_compare(void *on1, void *on2) noexcept nogil: cdef NeighborList *n1 cdef NeighborList *n2 n1 = on1 n2 = on2 # Note that we set this up so that "greatest" evaluates to the *end* of the # list, so we can do standard radius comparisons. if n1.r2 < n2.r2: return -1 elif n1.r2 == n2.r2: return 0 else: return 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) cdef np.float64_t r2dist(np.float64_t ppos[3], np.float64_t cpos[3], np.float64_t DW[3], bint periodicity[3], np.float64_t max_dist2): cdef int i cdef np.float64_t r2, DR r2 = 0.0 for i in range(3): DR = (ppos[i] - cpos[i]) if not periodicity[i]: pass elif (DR > DW[i]/2.0): DR -= DW[i] elif (DR < -DW[i]/2.0): DR += DW[i] r2 += DR * DR if max_dist2 >= 0.0 and r2 > max_dist2: return -1.0 return r2 cdef class PriorityQueue: """This class acts as a "priority-queue." It was extracted from the DistanceQueue object so that it could serve as a general-purpose method for storing the N-most "valuable" objects. It's relatively simple, in that it provides storage for a single int64 (which is usually an 'index' into an external array or list) and a single float64 "value" associated with them. You can insert new objects, and then if it's already at maxn objects in it, it'll bump the least valuable one off the end. Of particular note is that *lower* values are considered to be *more valuable* than higher ones; this is because our typical use case is to store radii. """ def __cinit__(self, int maxn): self.maxn = maxn self.curn = 0 self.items = malloc( sizeof(ItemList) * self.maxn) cdef void item_reset(self): for i in range(self.maxn): self.items[i].value = 1e300 self.items[i].ind = -1 self.curn = 0 cdef int item_insert(self, np.int64_t ind, np.float64_t value): cdef int i, di if self.curn == 0: self.items[0].value = value self.items[0].ind = ind self.curn += 1 return 0 # Now insert in a sorted way di = -1 for i in range(self.curn - 1, -1, -1): # We are checking if i is less than us, to see if we should insert # to the right (i.e., i+1). if self.items[i].value < value: di = i break # The outermost one is already too small. if di == self.maxn - 1: return -1 if (self.maxn - (di + 2)) > 0: memmove( (self.items + di + 2), (self.items + di + 1), sizeof(ItemList) * (self.maxn - (di + 2))) self.items[di + 1].value = value self.items[di + 1].ind = ind if self.curn < self.maxn: self.curn += 1 return di + 1 cdef class DistanceQueue: """This is a distance queue object, designed to incrementally evaluate N positions against a reference point. It is initialized with the maximum number that are to be retained (i.e., maxn-nearest neighbors).""" def __cinit__(self, int maxn): if sizeof(ItemList) != sizeof(NeighborList): # This should almost never, ever happen unless we do something very # wrong, and must be broken at compile time. raise RuntimeError self.neighbors = self.items self.neighbor_reset() for i in range(3): self.DW[i] = 0 self.periodicity[i] = False cdef void _setup(self, np.float64_t DW[3], bint periodicity[3]): for i in range(3): self.DW[i] = DW[i] self.periodicity[i] = periodicity[i] def setup(self, np.float64_t[:] DW, periodicity): for i in range(3): self.DW[i] = DW[i] self.periodicity[i] = periodicity[i] def __dealloc__(self): free(self.neighbors) cdef void neighbor_eval(self, np.int64_t pn, np.float64_t ppos[3], np.float64_t cpos[3]): # Here's a python+numpy simulator of this: # http://paste.yt-project.org/show/5445/ cdef np.float64_t r2, r2_trunc if self.curn == self.maxn: # Truncate calculation if it's bigger than this in any dimension r2_trunc = self.neighbors[self.curn - 1].r2 else: # Don't truncate our calculation r2_trunc = -1 r2 = r2dist(ppos, cpos, self.DW, self.periodicity, r2_trunc) if r2 == -1: return self.item_insert(pn, r2) cdef void neighbor_reset(self): self.item_reset() def find_nearest(self, np.float64_t[:] center, np.float64_t[:,:] points): """This function accepts a center and a set of [N,3] points, and it will return the indices into the points array of the maxn closest neighbors.""" cdef int i, j cdef np.float64_t ppos[3] cdef np.float64_t cpos[3] self.neighbor_reset() for i in range(3): cpos[i] = center[i] for j in range(points.shape[0]): for i in range(3): ppos[i] = points[j,i] self.neighbor_eval(j, ppos, cpos) rv = np.empty(self.curn, dtype="int64") for i in range(self.curn): rv[i] = self.neighbors[i].pn return rv yt-project-yt-f043ac8/yt/utilities/lib/element_mappings.pxd000066400000000000000000000215201510711153200241330ustar00rootroot00000000000000cimport cython cimport numpy as np from numpy cimport ndarray import numpy as np from libc.math cimport fabs, fmax cdef class ElementSampler: # how close a point has to be to the element # to get counted as "inside". This is in the # mapped coordinates of the element. cdef np.float64_t inclusion_tol cdef int num_mapped_coords cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef double sample_at_real_point(self, double* vertices, double* field_values, double* physical_x) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil cdef class P1Sampler1D(ElementSampler): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef class P1Sampler2D(ElementSampler): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef class P1Sampler3D(ElementSampler): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil # This typedef defines a function pointer that defines the system # of equations that will be solved by the NonlinearSolveSamplers. # # inputs: # x - pointer to the mapped coordinate # vertices - pointer to the element vertices # phys_x - pointer to the physical coordinate # # outputs: # # fx - the result of solving the system, should be close to 0 # once it is converged. # ctypedef void (*func_type)(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil # This typedef defines a function pointer that defines the Jacobian # matrix used by the NonlinearSolveSampler3D. Subclasses needed to # define a Jacobian function in this form. # # inputs: # x - pointer to the mapped coordinate # vertices - pointer to the element vertices # phys_x - pointer to the physical coordinate # # outputs: # # rcol - the first column of the jacobian # scol - the second column of the jacobian # tcol - the third column of the jaocobian # ctypedef void (*jac_type3D)(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil # This typedef defines a function pointer that defines the Jacobian # matrix used by the NonlinearSolveSampler2D. Subclasses needed to # define a Jacobian function in this form. # # inputs: # x - pointer to the mapped coordinate # vertices - pointer to the element vertices # phys_x - pointer to the physical coordinate # # outputs: # # rcol - the first column of the jacobian # scol - the second column of the jacobian # ctypedef void (*jac_type2D)(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil cdef class NonlinearSolveSampler3D(ElementSampler): cdef int dim cdef int max_iter cdef np.float64_t tolerance cdef func_type func cdef jac_type3D jac cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef class Q1Sampler3D(NonlinearSolveSampler3D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil cdef class W1Sampler3D(NonlinearSolveSampler3D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil cdef class S2Sampler3D(NonlinearSolveSampler3D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil cdef class NonlinearSolveSampler2D(ElementSampler): cdef int dim cdef int max_iter cdef np.float64_t tolerance cdef func_type func cdef jac_type2D jac cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef class Q1Sampler2D(NonlinearSolveSampler2D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef class Q2Sampler2D(NonlinearSolveSampler2D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef class T2Sampler2D(NonlinearSolveSampler2D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil cdef class Tet2Sampler3D(NonlinearSolveSampler3D): cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil cdef int check_inside(self, double* mapped_coord) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/element_mappings.pyx000066400000000000000000001321121510711153200241600ustar00rootroot00000000000000# distutils: libraries = STD_LIBS """ This file contains coordinate mappings between physical coordinates and those defined on unit elements, as well as doing the corresponding intracell interpolation on finite element data. """ cimport cython cimport numpy as np from numpy cimport ndarray import numpy as np from libc.math cimport fabs from yt.utilities.lib.autogenerated_element_samplers cimport ( Q1Function2D, Q1Function3D, Q1Jacobian2D, Q1Jacobian3D, Q2Function2D, Q2Jacobian2D, T2Function2D, T2Jacobian2D, Tet2Function3D, Tet2Jacobian3D, W1Function3D, W1Jacobian3D, ) cdef extern from "platform_dep.h": double fmax(double x, double y) noexcept nogil @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double determinant_3x3(double* col0, double* col1, double* col2) noexcept nogil: return col0[0]*col1[1]*col2[2] - col0[0]*col1[2]*col2[1] - \ col0[1]*col1[0]*col2[2] + col0[1]*col1[2]*col2[0] + \ col0[2]*col1[0]*col2[1] - col0[2]*col1[1]*col2[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double maxnorm(double* f, int dim) noexcept nogil: cdef double err cdef int i err = fabs(f[0]) for i in range(1, dim): err = fmax(err, fabs(f[i])) return err cdef class ElementSampler: ''' This is a base class for sampling the value of a finite element solution at an arbitrary point inside a mesh element. In general, this will be done by transforming the requested physical coordinate into a mapped coordinate system, sampling the solution in mapped coordinates, and returning the result. This is not to be used directly; use one of the subclasses instead. ''' def __init__(self): self.inclusion_tol = 1.0e-8 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil: pass @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: pass @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: pass @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def check_contains(self, np.float64_t[:,::1] vertices, np.float64_t[:,::1] positions): cdef np.ndarray[np.float64_t, ndim=2] mapped_coords cdef np.ndarray[np.uint8_t, ndim=1] mask mapped_coords = self.map_reals_to_unit(vertices, positions) mask = np.zeros(mapped_coords.shape[0], dtype=np.uint8) cdef double[3] mapped_coord cdef int i, j for i in range(mapped_coords.shape[0]): for j in range(mapped_coords.shape[1]): mapped_coord[j] = mapped_coords[i, j] mask[i] = self.check_inside(mapped_coord) return mask @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil: pass @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_real_point(self, double* vertices, double* field_values, double* physical_x) noexcept nogil: cdef double val cdef double mapped_coord[4] self.map_real_to_unit(mapped_coord, vertices, physical_x) val = self.sample_at_unit_point(mapped_coord, field_values) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def map_reals_to_unit(self, np.float64_t[:,::1] vertices, np.float64_t[:,::1] positions): cdef double mapped_x[3] cdef int i, n # We have N vertices, which each have three components. cdef np.ndarray[np.float64_t, ndim=2] output_coords output_coords = np.zeros((positions.shape[0], positions.shape[1]), dtype="float64") # Now for each position, we map for n in range(positions.shape[0]): self.map_real_to_unit(mapped_x, &vertices[0,0], &positions[n, 0]) for i in range(positions.shape[1]): output_coords[n, i] = mapped_x[i] return output_coords def sample_at_real_points(self, np.float64_t[:,::1] vertices, np.float64_t[::1] field_values, np.float64_t[:,::1] positions): cdef np.ndarray[np.float64_t, ndim=1] output_values output_values = np.zeros(positions.shape[0], dtype="float64") for n in range(positions.shape[0]): output_values[n] = self.sample_at_real_point( &vertices[0,0], &field_values[0], &positions[n,0]) return output_values cdef class P1Sampler1D(ElementSampler): ''' This implements sampling inside a linear, 1D element. ''' def __init__(self): super(P1Sampler1D, self).__init__() self.num_mapped_coords = 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil: mapped_x[0] = -1.0 + 2.0*(physical_x[0] - vertices[0]) / (vertices[1] - vertices[0]) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: return vals[0] * (1 - coord[0]) / 2.0 + vals[1] * (1.0 + coord[0]) / 2.0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: if (fabs(mapped_coord[0]) - 1.0 > self.inclusion_tol): return 0 return 1 cdef class P1Sampler2D(ElementSampler): ''' This implements sampling inside a linear, triangular mesh element. This mapping is linear and can be inverted easily. Note that this implementation uses triangular (or barycentric) coordinates. ''' def __init__(self): super(P1Sampler2D, self).__init__() self.num_mapped_coords = 3 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil: cdef double[3] col0 cdef double[3] col1 cdef double[3] col2 col0[0] = vertices[0] col0[1] = vertices[1] col0[2] = 1.0 col1[0] = vertices[2] col1[1] = vertices[3] col1[2] = 1.0 col2[0] = vertices[4] col2[1] = vertices[5] col2[2] = 1.0 det = determinant_3x3(col0, col1, col2) mapped_x[0] = ((vertices[3] - vertices[5])*physical_x[0] + \ (vertices[4] - vertices[2])*physical_x[1] + \ (vertices[2]*vertices[5] - vertices[4]*vertices[3])) / det mapped_x[1] = ((vertices[5] - vertices[1])*physical_x[0] + \ (vertices[0] - vertices[4])*physical_x[1] + \ (vertices[4]*vertices[1] - vertices[0]*vertices[5])) / det mapped_x[2] = 1.0 - mapped_x[1] - mapped_x[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: return vals[0]*coord[0] + vals[1]*coord[1] + vals[2]*coord[2] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for triangles, we check whether all mapped_coords are # between 0 and 1, to within the inclusion tolerance cdef int i for i in range(3): if (mapped_coord[i] < -self.inclusion_tol or mapped_coord[i] - 1.0 > self.inclusion_tol): return 0 return 1 cdef class P1Sampler3D(ElementSampler): ''' This implements sampling inside a linear, tetrahedral mesh element. This mapping is linear and can be inverted easily. ''' def __init__(self): super(P1Sampler3D, self).__init__() self.num_mapped_coords = 4 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil: cdef int i cdef double d cdef double[3] bvec cdef double[3] col0 cdef double[3] col1 cdef double[3] col2 # here, we express positions relative to the 4th element, # which is selected by vertices[9] for i in range(3): bvec[i] = physical_x[i] - vertices[9 + i] col0[i] = vertices[0 + i] - vertices[9 + i] col1[i] = vertices[3 + i] - vertices[9 + i] col2[i] = vertices[6 + i] - vertices[9 + i] d = determinant_3x3(col0, col1, col2) mapped_x[0] = determinant_3x3(bvec, col1, col2)/d mapped_x[1] = determinant_3x3(col0, bvec, col2)/d mapped_x[2] = determinant_3x3(col0, col1, bvec)/d mapped_x[3] = 1.0 - mapped_x[0] - mapped_x[1] - mapped_x[2] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: return vals[0]*coord[0] + vals[1]*coord[1] + \ vals[2]*coord[2] + vals[3]*coord[3] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for tetrahedra, we check whether all mapped coordinates # are within 0 and 1, to within the inclusion tolerance cdef int i for i in range(4): if (mapped_coord[i] < -self.inclusion_tol or mapped_coord[i] - 1.0 > self.inclusion_tol): return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil: cdef double u, v, w cdef double thresh = 2.0e-2 if mapped_coord[0] == 0: u = mapped_coord[1] v = mapped_coord[2] w = mapped_coord[3] elif mapped_coord[1] == 0: u = mapped_coord[2] v = mapped_coord[3] w = mapped_coord[0] elif mapped_coord[2] == 0: u = mapped_coord[1] v = mapped_coord[3] w = mapped_coord[0] else: u = mapped_coord[1] v = mapped_coord[2] w = mapped_coord[0] if ((u < thresh) or (v < thresh) or (w < thresh) or (fabs(u - 1) < thresh) or (fabs(v - 1) < thresh) or (fabs(w - 1) < thresh)): return 1 return -1 cdef class NonlinearSolveSampler3D(ElementSampler): ''' This is a base class for handling element samplers that require a nonlinear solve to invert the mapping between coordinate systems. To do this, we perform Newton-Raphson iteration using a specified system of equations with an analytic Jacobian matrix. This solver is hard-coded for 3D for reasons of efficiency. This is not to be used directly, use one of the subclasses instead. ''' def __init__(self): super(NonlinearSolveSampler3D, self).__init__() self.tolerance = 1.0e-9 self.max_iter = 10 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil: ''' A thorough description of Newton's method and modifications for global convergence can be found in Dennis's text "Numerical Methods for Unconstrained Optimization and Nonlinear Equations." x: solution vector; holds unit/mapped coordinates xk: temporary vector for holding solution of current iteration f: residual vector r, s, t: three columns of Jacobian matrix corresponding to unit/mapped coordinates r, s, and t d: Jacobian determinant s_n: Newton step vector lam: fraction of Newton step by which to change x alpha: constant proportional to how much residual required to decrease. 1e-4 is value of alpha recommended by Dennis err_c: Error of current iteration err_plus: Error of next iteration min_lam: minimum fraction of Newton step that the line search is allowed to take. General experience suggests that lambda values smaller than 1e-3 will not significantly reduce the residual, but we set to 1e-6 just to be safe ''' cdef int i cdef double d, lam cdef double[3] f cdef double[3] r cdef double[3] s cdef double[3] t cdef double[3] x, xk, s_n cdef int iterations = 0 cdef double err_c, err_plus cdef double alpha = 1e-4 cdef double min_lam = 1e-6 # initial guess for i in range(3): x[i] = 0.0 # initial error norm self.func(f, x, vertices, physical_x) err_c = maxnorm(f, 3) # begin Newton iteration while (err_c > self.tolerance and iterations < self.max_iter): self.jac(r, s, t, x, vertices, physical_x) d = determinant_3x3(r, s, t) s_n[0] = - (determinant_3x3(f, s, t)/d) s_n[1] = - (determinant_3x3(r, f, t)/d) s_n[2] = - (determinant_3x3(r, s, f)/d) xk[0] = x[0] + s_n[0] xk[1] = x[1] + s_n[1] xk[2] = x[2] + s_n[2] self.func(f, xk, vertices, physical_x) err_plus = maxnorm(f, 3) lam = 1 while err_plus > err_c * (1. - alpha * lam) and lam > min_lam: lam = lam / 2 xk[0] = x[0] + lam * s_n[0] xk[1] = x[1] + lam * s_n[1] xk[2] = x[2] + lam * s_n[2] self.func(f, xk, vertices, physical_x) err_plus = maxnorm(f, 3) x[0] = xk[0] x[1] = xk[1] x[2] = xk[2] err_c = err_plus iterations += 1 if (err_c > self.tolerance): # we did not converge, set bogus value for i in range(3): mapped_x[i] = -99.0 else: for i in range(3): mapped_x[i] = x[i] cdef class Q1Sampler3D(NonlinearSolveSampler3D): ''' This implements sampling inside a 3D, linear, hexahedral mesh element. ''' def __init__(self): super(Q1Sampler3D, self).__init__() self.num_mapped_coords = 3 self.dim = 3 self.func = Q1Function3D self.jac = Q1Jacobian3D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double F, rm, rp, sm, sp, tm, tp rm = 1.0 - coord[0] rp = 1.0 + coord[0] sm = 1.0 - coord[1] sp = 1.0 + coord[1] tm = 1.0 - coord[2] tp = 1.0 + coord[2] F = vals[0]*rm*sm*tm + vals[1]*rp*sm*tm + vals[2]*rp*sp*tm + vals[3]*rm*sp*tm + \ vals[4]*rm*sm*tp + vals[5]*rp*sm*tp + vals[6]*rp*sp*tp + vals[7]*rm*sp*tp return 0.125*F @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for hexes, the mapped coordinates all go from -1 to 1 # if we are inside the element. if (fabs(mapped_coord[0]) - 1.0 > self.inclusion_tol or fabs(mapped_coord[1]) - 1.0 > self.inclusion_tol or fabs(mapped_coord[2]) - 1.0 > self.inclusion_tol): return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil: if (fabs(fabs(mapped_coord[0]) - 1.0) < 1e-1 and fabs(fabs(mapped_coord[1]) - 1.0) < 1e-1): return 1 elif (fabs(fabs(mapped_coord[0]) - 1.0) < 1e-1 and fabs(fabs(mapped_coord[2]) - 1.0) < 1e-1): return 1 elif (fabs(fabs(mapped_coord[1]) - 1.0) < 1e-1 and fabs(fabs(mapped_coord[2]) - 1.0) < 1e-1): return 1 else: return -1 cdef class S2Sampler3D(NonlinearSolveSampler3D): ''' This implements sampling inside a 3D, 20-node hexahedral mesh element. ''' def __init__(self): super(S2Sampler3D, self).__init__() self.num_mapped_coords = 3 self.dim = 3 self.func = S2Function3D self.jac = S2Jacobian3D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double F, r, s, t, rm, rp, sm, sp, tm, tp r = coord[0] rm = 1.0 - r rp = 1.0 + r s = coord[1] sm = 1.0 - s sp = 1.0 + s t = coord[2] tm = 1.0 - t tp = 1.0 + t F = rm*sm*tm*(-r - s - t - 2.0)*vals[0] \ + rp*sm*tm*( r - s - t - 2.0)*vals[1] \ + rp*sp*tm*( r + s - t - 2.0)*vals[2] \ + rm*sp*tm*(-r + s - t - 2.0)*vals[3] \ + rm*sm*tp*(-r - s + t - 2.0)*vals[4] \ + rp*sm*tp*( r - s + t - 2.0)*vals[5] \ + rp*sp*tp*( r + s + t - 2.0)*vals[6] \ + rm*sp*tp*(-r + s + t - 2.0)*vals[7] \ + 2.0*(1.0 - r*r)*sm*tm*vals[8] \ + 2.0*rp*(1.0 - s*s)*tm*vals[9] \ + 2.0*(1.0 - r*r)*sp*tm*vals[10] \ + 2.0*rm*(1.0 - s*s)*tm*vals[11] \ + 2.0*rm*sm*(1.0 - t*t)*vals[12] \ + 2.0*rp*sm*(1.0 - t*t)*vals[13] \ + 2.0*rp*sp*(1.0 - t*t)*vals[14] \ + 2.0*rm*sp*(1.0 - t*t)*vals[15] \ + 2.0*(1.0 - r*r)*sm*tp*vals[16] \ + 2.0*rp*(1.0 - s*s)*tp*vals[17] \ + 2.0*(1.0 - r*r)*sp*tp*vals[18] \ + 2.0*rm*(1.0 - s*s)*tp*vals[19] return 0.125*F @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: if (fabs(mapped_coord[0]) - 1.0 > self.inclusion_tol or fabs(mapped_coord[1]) - 1.0 > self.inclusion_tol or fabs(mapped_coord[2]) - 1.0 > self.inclusion_tol): return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil: if (fabs(fabs(mapped_coord[0]) - 1.0) < 1e-1 and fabs(fabs(mapped_coord[1]) - 1.0) < 1e-1): return 1 elif (fabs(fabs(mapped_coord[0]) - 1.0) < 1e-1 and fabs(fabs(mapped_coord[2]) - 1.0) < 1e-1): return 1 elif (fabs(fabs(mapped_coord[1]) - 1.0) < 1e-1 and fabs(fabs(mapped_coord[2]) - 1.0) < 1e-1): return 1 else: return -1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void S2Function3D(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil: cdef int i cdef double r, s, t, rm, rp, sm, sp, tm, tp r = x[0] rm = 1.0 - r rp = 1.0 + r s = x[1] sm = 1.0 - s sp = 1.0 + s t = x[2] tm = 1.0 - t tp = 1.0 + t for i in range(3): fx[i] = rm*sm*tm*(-r - s - t - 2.0)*vertices[0 + i] \ + rp*sm*tm*( r - s - t - 2.0)*vertices[3 + i] \ + rp*sp*tm*( r + s - t - 2.0)*vertices[6 + i] \ + rm*sp*tm*(-r + s - t - 2.0)*vertices[9 + i] \ + rm*sm*tp*(-r - s + t - 2.0)*vertices[12 + i] \ + rp*sm*tp*( r - s + t - 2.0)*vertices[15 + i] \ + rp*sp*tp*( r + s + t - 2.0)*vertices[18 + i] \ + rm*sp*tp*(-r + s + t - 2.0)*vertices[21 + i] \ + 2.0*(1.0 - r*r)*sm*tm*vertices[24 + i] \ + 2.0*rp*(1.0 - s*s)*tm*vertices[27 + i] \ + 2.0*(1.0 - r*r)*sp*tm*vertices[30 + i] \ + 2.0*rm*(1.0 - s*s)*tm*vertices[33 + i] \ + 2.0*rm*sm*(1.0 - t*t)*vertices[36 + i] \ + 2.0*rp*sm*(1.0 - t*t)*vertices[39 + i] \ + 2.0*rp*sp*(1.0 - t*t)*vertices[42 + i] \ + 2.0*rm*sp*(1.0 - t*t)*vertices[45 + i] \ + 2.0*(1.0 - r*r)*sm*tp*vertices[48 + i] \ + 2.0*rp*(1.0 - s*s)*tp*vertices[51 + i] \ + 2.0*(1.0 - r*r)*sp*tp*vertices[54 + i] \ + 2.0*rm*(1.0 - s*s)*tp*vertices[57 + i] \ - 8.0*phys_x[i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void S2Jacobian3D(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil: cdef int i cdef double r, s, t, rm, rp, sm, sp, tm, tp r = x[0] rm = 1.0 - r rp = 1.0 + r s = x[1] sm = 1.0 - s sp = 1.0 + s t = x[2] tm = 1.0 - t tp = 1.0 + t for i in range(3): rcol[i] = (sm*tm*(r + s + t + 2.0) - rm*sm*tm)*vertices[0 + i] \ + (sm*tm*(r - s - t - 2.0) + rp*sm*tm)*vertices[3 + i] \ + (sp*tm*(r + s - t - 2.0) + rp*sp*tm)*vertices[6 + i] \ + (sp*tm*(r - s + t + 2.0) - rm*sp*tm)*vertices[9 + i] \ + (sm*tp*(r + s - t + 2.0) - rm*sm*tp)*vertices[12 + i] \ + (sm*tp*(r - s + t - 2.0) + rp*sm*tp)*vertices[15 + i] \ + (sp*tp*(r + s + t - 2.0) + rp*sp*tp)*vertices[18 + i] \ + (sp*tp*(r - s - t + 2.0) - rm*sp*tp)*vertices[21 + i] \ - 4.0*r*sm*tm*vertices[24 + i] \ + 2.0*(1.0 - s*s)*tm*vertices[27 + i] \ - 4.0*r*sp*tm*vertices[30 + i] \ - 2.0*(1.0 - s*s)*tm*vertices[33 + i] \ - 2.0*sm*(1.0 - t*t)*vertices[36 + i] \ + 2.0*sm*(1.0 - t*t)*vertices[39 + i] \ + 2.0*sp*(1.0 - t*t)*vertices[42 + i] \ - 2.0*sp*(1.0 - t*t)*vertices[45 + i] \ - 4.0*r*sm*tp*vertices[48 + i] \ + 2.0*(1.0 - s*s)*tp*vertices[51 + i] \ - 4.0*r*sp*tp*vertices[54 + i] \ - 2.0*(1.0 - s*s)*tp*vertices[57 + i] scol[i] = ( rm*tm*(r + s + t + 2.0) - rm*sm*tm)*vertices[0 + i] \ + (-rp*tm*(r - s - t - 2.0) - rp*sm*tm)*vertices[3 + i] \ + ( rp*tm*(r + s - t - 2.0) + rp*sp*tm)*vertices[6 + i] \ + (-rm*tm*(r - s + t + 2.0) + rm*sp*tm)*vertices[9 + i] \ + ( rm*tp*(r + s - t + 2.0) - rm*sm*tp)*vertices[12 + i] \ + (-rp*tp*(r - s + t - 2.0) - rp*sm*tp)*vertices[15 + i] \ + ( rp*tp*(r + s + t - 2.0) + rp*sp*tp)*vertices[18 + i] \ + (-rm*tp*(r - s - t + 2.0) + rm*sp*tp)*vertices[21 + i] \ - 2.0*(1.0 - r*r)*tm*vertices[24 + i] \ - 4.0*rp*s*tm*vertices[27 + i] \ + 2.0*(1.0 - r*r)*tm*vertices[30 + i] \ - 4.0*rm*s*tm*vertices[33 + i] \ - 2.0*rm*(1.0 - t*t)*vertices[36 + i] \ - 2.0*rp*(1.0 - t*t)*vertices[39 + i] \ + 2.0*rp*(1.0 - t*t)*vertices[42 + i] \ + 2.0*rm*(1.0 - t*t)*vertices[45 + i] \ - 2.0*(1.0 - r*r)*tp*vertices[48 + i] \ - 4.0*rp*s*tp*vertices[51 + i] \ + 2.0*(1.0 - r*r)*tp*vertices[54 + i] \ - 4.0*rm*s*tp*vertices[57 + i] tcol[i] = ( rm*sm*(r + s + t + 2.0) - rm*sm*tm)*vertices[0 + i] \ + (-rp*sm*(r - s - t - 2.0) - rp*sm*tm)*vertices[3 + i] \ + (-rp*sp*(r + s - t - 2.0) - rp*sp*tm)*vertices[6 + i] \ + ( rm*sp*(r - s + t + 2.0) - rm*sp*tm)*vertices[9 + i] \ + (-rm*sm*(r + s - t + 2.0) + rm*sm*tp)*vertices[12 + i] \ + ( rp*sm*(r - s + t - 2.0) + rp*sm*tp)*vertices[15 + i] \ + ( rp*sp*(r + s + t - 2.0) + rp*sp*tp)*vertices[18 + i] \ + (-rm*sp*(r - s - t + 2.0) + rm*sp*tp)*vertices[21 + i] \ - 2.0*(1.0 - r*r)*sm*vertices[24 + i] \ - 2.0*rp*(1.0 - s*s)*vertices[27 + i] \ - 2.0*(1.0 - r*r)*sp*vertices[30 + i] \ - 2.0*rm*(1.0 - s*s)*vertices[33 + i] \ - 4.0*rm*sm*t*vertices[36 + i] \ - 4.0*rp*sm*t*vertices[39 + i] \ - 4.0*rp*sp*t*vertices[42 + i] \ - 4.0*rm*sp*t*vertices[45 + i] \ + 2.0*(1.0 - r*r)*sm*vertices[48 + i] \ + 2.0*rp*(1.0 - s*s)*vertices[51 + i] \ + 2.0*(1.0 - r*r)*sp*vertices[54 + i] \ + 2.0*rm*(1.0 - s*s)*vertices[57 + i] cdef class W1Sampler3D(NonlinearSolveSampler3D): ''' This implements sampling inside a 3D, linear, wedge mesh element. ''' def __init__(self): super(W1Sampler3D, self).__init__() self.num_mapped_coords = 3 self.dim = 3 self.func = W1Function3D self.jac = W1Jacobian3D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double F F = vals[0]*(1.0 - coord[0] - coord[1])*(1.0 - coord[2]) + \ vals[1]*coord[0]*(1.0 - coord[2]) + \ vals[2]*coord[1]*(1.0 - coord[2]) + \ vals[3]*(1.0 - coord[0] - coord[1])*(1.0 + coord[2]) + \ vals[4]*coord[0]*(1.0 + coord[2]) + \ vals[5]*coord[1]*(1.0 + coord[2]) return F / 2.0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for wedges the bounds of the mapped coordinates are: # 0 <= mapped_coord[0] <= 1 - mapped_coord[1] # 0 <= mapped_coord[1] # -1 <= mapped_coord[2] <= 1 if (mapped_coord[0] < -self.inclusion_tol or mapped_coord[0] + mapped_coord[1] - 1.0 > self.inclusion_tol): return 0 if (mapped_coord[1] < -self.inclusion_tol): return 0 if (fabs(mapped_coord[2]) - 1.0 > self.inclusion_tol): return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil: cdef double r, s cdef double thresh = 5.0e-2 r = mapped_coord[0] s = mapped_coord[1] cdef int near_edge_r, near_edge_s, near_edge_t near_edge_r = (r < thresh) or (fabs(r + s - 1.0) < thresh) near_edge_s = (s < thresh) near_edge_t = fabs(fabs(mapped_coord[2]) - 1.0) < thresh # we use ray.instID to pass back whether the ray is near the # element boundary or not (used to annotate mesh lines) if (near_edge_r and near_edge_s): return 1 elif (near_edge_r and near_edge_t): return 1 elif (near_edge_s and near_edge_t): return 1 else: return -1 cdef class NonlinearSolveSampler2D(ElementSampler): ''' This is a base class for handling element samplers that require a nonlinear solve to invert the mapping between coordinate systems. To do this, we perform Newton-Raphson iteration using a specified system of equations with an analytic Jacobian matrix. This solver is hard-coded for 2D for reasons of efficiency. This is not to be used directly, use one of the subclasses instead. ''' def __init__(self): super(NonlinearSolveSampler2D, self).__init__() self.tolerance = 1.0e-9 self.max_iter = 10 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void map_real_to_unit(self, double* mapped_x, double* vertices, double* physical_x) noexcept nogil: ''' A thorough description of Newton's method and modifications for global convergence can be found in Dennis's text "Numerical Methods for Unconstrained Optimization and Nonlinear Equations." x: solution vector; holds unit/mapped coordinates xk: temporary vector for holding solution of current iteration f: residual vector A: Jacobian matrix (derivative of residual vector wrt x) d: Jacobian determinant s_n: Newton step vector lam: fraction of Newton step by which to change x alpha: constant proportional to how much residual required to decrease. 1e-4 is value of alpha recommended by Dennis err_c: Error of current iteration err_plus: Error of next iteration min_lam: minimum fraction of Newton step that the line search is allowed to take. General experience suggests that lambda values smaller than 1e-3 will not significantly reduce the residual, but we set to 1e-6 just to be safe ''' cdef int i cdef double d, lam cdef double[2] f cdef double[2] x, xk, s_n cdef double[4] A cdef int iterations = 0 cdef double err_c, err_plus cdef double alpha = 1e-4 cdef double min_lam = 1e-6 # initial guess for i in range(2): x[i] = 0.0 # initial error norm self.func(f, x, vertices, physical_x) err_c = maxnorm(f, 2) # begin Newton iteration while (err_c > self.tolerance and iterations < self.max_iter): self.jac(&A[0], &A[2], x, vertices, physical_x) d = (A[0]*A[3] - A[1]*A[2]) s_n[0] = -( A[3]*f[0] - A[2]*f[1]) / d s_n[1] = -(-A[1]*f[0] + A[0]*f[1]) / d xk[0] = x[0] + s_n[0] xk[1] = x[1] + s_n[1] self.func(f, xk, vertices, physical_x) err_plus = maxnorm(f, 2) lam = 1 while err_plus > err_c * (1. - alpha * lam) and lam > min_lam: lam = lam / 2 xk[0] = x[0] + lam * s_n[0] xk[1] = x[1] + lam * s_n[1] self.func(f, xk, vertices, physical_x) err_plus = maxnorm(f, 2) x[0] = xk[0] x[1] = xk[1] err_c = err_plus iterations += 1 if (err_c > self.tolerance): # we did not converge, set bogus value for i in range(2): mapped_x[i] = -99.0 else: for i in range(2): mapped_x[i] = x[i] cdef class Q1Sampler2D(NonlinearSolveSampler2D): ''' This implements sampling inside a 2D, linear, quadrilateral mesh element. ''' def __init__(self): super(Q1Sampler2D, self).__init__() self.num_mapped_coords = 2 self.dim = 2 self.func = Q1Function2D self.jac = Q1Jacobian2D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double F, rm, rp, sm, sp rm = 1.0 - coord[0] rp = 1.0 + coord[0] sm = 1.0 - coord[1] sp = 1.0 + coord[1] F = vals[0]*rm*sm + vals[1]*rp*sm + vals[2]*rp*sp + vals[3]*rm*sp return 0.25*F @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for quads, we check whether the mapped_coord is between # -1 and 1 in both directions. if (fabs(mapped_coord[0]) - 1.0 > self.inclusion_tol or fabs(mapped_coord[1]) - 1.0 > self.inclusion_tol): return 0 return 1 cdef class Q2Sampler2D(NonlinearSolveSampler2D): ''' This implements sampling inside a 2D, quadratic, quadrilateral mesh element. ''' def __init__(self): super(Q2Sampler2D, self).__init__() self.num_mapped_coords = 2 self.dim = 2 self.func = Q2Function2D self.jac = Q2Jacobian2D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double[9] phi cdef double rv = 0 zet = coord[0] eta = coord[1] zetm = coord[0] - 1. zetp = coord[0] + 1. etam = coord[1] - 1. etap = coord[1] + 1. phi[0] = zet * zetm * eta * etam / 4. phi[1] = zet * zetp * eta * etam / 4. phi[2] = zet * zetp * eta * etap / 4. phi[3] = zet * zetm * eta * etap / 4. phi[4] = zetp * zetm * eta * etam / -2. phi[5] = zet * zetp * etap * etam / -2. phi[6] = zetp * zetm * eta * etap / -2. phi[7] = zet * zetm * etap * etam / -2. phi[8] = zetp * zetm * etap * etam for i in range(9): rv += vals[i] * phi[i] return rv @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for quads, we check whether the mapped_coord is between # -1 and 1 in both directions. if (fabs(mapped_coord[0]) - 1.0 > self.inclusion_tol or fabs(mapped_coord[1]) - 1.0 > self.inclusion_tol): return 0 return 1 cdef class T2Sampler2D(NonlinearSolveSampler2D): ''' This implements sampling inside a 2D, quadratic, triangular mesh element. Note that this implementation uses canonical coordinates. ''' def __init__(self): super(T2Sampler2D, self).__init__() self.num_mapped_coords = 2 self.dim = 2 self.func = T2Function2D self.jac = T2Jacobian2D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double phi0, phi1, phi2, phi3, phi4, phi5, c0sq, c1sq, c0c1 c0sq = coord[0] * coord[0] c1sq = coord[1] * coord[1] c0c1 = coord[0] * coord[1] phi0 = 1 - 3 * coord[0] + 2 * c0sq - 3 * coord[1] + \ 2 * c1sq + 4 * c0c1 phi1 = -coord[0] + 2 * c0sq phi2 = -coord[1] + 2 * c1sq phi3 = 4 * coord[0] - 4 * c0sq - 4 * c0c1 phi4 = 4 * c0c1 phi5 = 4 * coord[1] - 4 * c1sq - 4 * c0c1 return vals[0]*phi0 + vals[1]*phi1 + vals[2]*phi2 + vals[3]*phi3 + \ vals[4]*phi4 + vals[5]*phi5 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for canonical tris, we check whether the mapped_coords are between # 0 and 1. if (mapped_coord[0] < -self.inclusion_tol or \ mapped_coord[1] < -self.inclusion_tol or \ mapped_coord[0] + mapped_coord[1] - 1.0 > self.inclusion_tol): return 0 return 1 cdef class Tet2Sampler3D(NonlinearSolveSampler3D): ''' This implements sampling inside a 3D, quadratic, tetrahedral mesh element. Note that this implementation uses canonical coordinates. ''' def __init__(self): super(Tet2Sampler3D, self).__init__() self.num_mapped_coords = 3 self.dim = 3 self.func = Tet2Function3D self.jac = Tet2Jacobian3D @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double sample_at_unit_point(self, double* coord, double* vals) noexcept nogil: cdef double[10] phi cdef double coordsq[3] cdef int i cdef double return_value = 0 for i in range(3): coordsq[i] = coord[i] * coord[i] phi[0] = 1 - 3 * coord[0] + 2 * coordsq[0] - 3 * coord[1] + \ 2 * coordsq[1] - 3 * coord[2] + 2 * coordsq[2] + \ 4 * coord[0] * coord[1] + 4 * coord[0] * coord[2] + \ 4 * coord[1] * coord[2] phi[1] = -coord[0] + 2 * coordsq[0] phi[2] = -coord[1] + 2 * coordsq[1] phi[3] = -coord[2] + 2 * coordsq[2] phi[4] = 4 * coord[0] - 4 * coordsq[0] - 4 * coord[0] * coord[1] - \ 4 * coord[0] * coord[2] phi[5] = 4 * coord[0] * coord[1] phi[6] = 4 * coord[1] - 4 * coordsq[1] - 4 * coord[0] * coord[1] - \ 4 * coord[1] * coord[2] phi[7] = 4 * coord[2] - 4 * coordsq[2] - 4 * coord[2] * coord[0] - \ 4 * coord[2] * coord[1] phi[8] = 4 * coord[0] * coord[2] phi[9] = 4 * coord[1] * coord[2] for i in range(10): return_value += phi[i] * vals[i] return return_value @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_inside(self, double* mapped_coord) noexcept nogil: # for canonical tets, we check whether the mapped_coords are between # 0 and 1. if (mapped_coord[0] < -self.inclusion_tol or \ mapped_coord[1] < -self.inclusion_tol or \ mapped_coord[2] < -self.inclusion_tol or \ mapped_coord[0] + mapped_coord[1] + mapped_coord[2] - 1.0 > \ self.inclusion_tol): return 0 return 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int check_mesh_lines(self, double* mapped_coord) noexcept nogil: cdef double u, v cdef double thresh = 2.0e-2 if mapped_coord[0] == 0: u = mapped_coord[1] v = mapped_coord[2] elif mapped_coord[1] == 0: u = mapped_coord[2] v = mapped_coord[0] elif mapped_coord[2] == 0: u = mapped_coord[1] v = mapped_coord[0] else: u = mapped_coord[1] v = mapped_coord[2] if ((u < thresh) or (v < thresh) or (fabs(u - 1) < thresh) or (fabs(v - 1) < thresh)): return 1 return -1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_hex_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val cdef Q1Sampler3D sampler = Q1Sampler3D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_hex20_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val cdef S2Sampler3D sampler = S2Sampler3D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_tetra_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = P1Sampler3D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_wedge_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val cdef W1Sampler3D sampler = W1Sampler3D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_linear1D_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = P1Sampler1D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_tri_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = P1Sampler2D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_quad_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = Q1Sampler2D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_quad2_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = Q2Sampler2D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_tri2_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = T2Sampler2D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def test_tet2_sampler(np.ndarray[np.float64_t, ndim=2] vertices, np.ndarray[np.float64_t, ndim=1] field_values, np.ndarray[np.float64_t, ndim=1] physical_x): cdef double val sampler = Tet2Sampler3D() val = sampler.sample_at_real_point( vertices.data, field_values.data, physical_x.data) return val yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/000077500000000000000000000000001510711153200223425ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/__init__.py000066400000000000000000000000001510711153200244410ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_construction.pxd000066400000000000000000000014541510711153200266310ustar00rootroot00000000000000cimport numpy as np from pyembree.rtcore cimport Triangle, Vec3f, Vertex ctypedef struct MeshDataContainer: Vertex* vertices # array of triangle vertices Triangle* indices # which vertices belong to which triangles double* field_data # the field values at the vertices int* element_indices # which vertices belong to which *element* int tpe # the number of triangles per element int vpe # the number of vertices per element int fpe # the number of field values per element ctypedef struct Patch: float[8][3] v unsigned int geomID np.float64_t* vertices np.float64_t* field_data ctypedef struct Tet_Patch: float[6][3] v unsigned int geomID np.float64_t* vertices np.float64_t* field_data yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_construction.pyx000066400000000000000000000323671510711153200266650ustar00rootroot00000000000000# distutils: include_dirs = EMBREE_INC_DIR # distutils: library_dirs = EMBREE_LIB_DIR # distutils: libraries = EMBREE_LIBS # distutils: language = c++ """ This file contains the ElementMesh classes, which represent the target that the rays will be cast at when rendering finite element data. This class handles the interface between the internal representation of the mesh and the pyembree representation. Note - this file is only used for the Embree-accelerated ray-tracer. """ import numpy as np cimport numpy as np cimport pyembree.rtcore_geometry as rtcg cimport pyembree.rtcore_geometry_user as rtcgu from libc.stdlib cimport free, malloc from mesh_intersection cimport ( patchBoundsFunc, patchIntersectFunc, tet_patchBoundsFunc, tet_patchIntersectFunc, ) from mesh_samplers cimport sample_hex, sample_tetra, sample_wedge from mesh_traversal cimport YTEmbreeScene from pyembree.rtcore cimport Triangle, Vertex from yt.utilities.exceptions import YTElementTypeNotRecognized cdef extern from "mesh_triangulation.h": enum: MAX_NUM_TRI int HEX_NV int HEX_NT int TETRA_NV int TETRA_NT int WEDGE_NV int WEDGE_NT int triangulate_hex[MAX_NUM_TRI][3] int triangulate_tetra[MAX_NUM_TRI][3] int triangulate_wedge[MAX_NUM_TRI][3] int hex20_faces[6][8] int tet10_faces[4][6] cdef class LinearElementMesh: r''' This creates a 1st-order mesh to be ray-traced with embree. Currently, we handle non-triangular mesh types by converting them to triangular meshes. This class performs this transformation. Currently, this is implemented for hexahedral and tetrahedral meshes. Parameters ---------- scene : EmbreeScene This is the scene to which the constructed polygons will be added. vertices : a np.ndarray of floats. This specifies the x, y, and z coordinates of the vertices in the polygon mesh. This should either have the shape (num_vertices, 3). For example, vertices[2][1] should give the y-coordinate of the 3rd vertex in the mesh. indices : a np.ndarray of ints This should either have the shape (num_elements, 4) or (num_elements, 8) for tetrahedral and hexahedral meshes, respectively. For tetrahedral meshes, each element will be represented by four triangles in the scene. For hex meshes, each element will be represented by 12 triangles, 2 for each face. For hex meshes, we assume that the node ordering is as defined here: http://homepages.cae.wisc.edu/~tautges/papers/cnmev3.pdf ''' cdef Vertex* vertices cdef Triangle* indices cdef unsigned int mesh cdef double* field_data cdef rtcg.RTCFilterFunc filter_func # triangles per element, vertices per element, and field points per # element, respectively: cdef int tpe, vpe, fpe cdef int[MAX_NUM_TRI][3] tri_array cdef int* element_indices cdef MeshDataContainer datac def __init__(self, YTEmbreeScene scene, np.ndarray vertices, np.ndarray indices, np.ndarray data): # We need to figure out what kind of elements we've been handed. if indices.shape[1] == 8: self.vpe = HEX_NV self.tpe = HEX_NT self.tri_array = triangulate_hex elif indices.shape[1] == 6: self.vpe = WEDGE_NV self.tpe = WEDGE_NT self.tri_array = triangulate_wedge elif indices.shape[1] == 4: self.vpe = TETRA_NV self.tpe = TETRA_NT self.tri_array = triangulate_tetra else: raise YTElementTypeNotRecognized(vertices.shape[1], indices.shape[1]) self._build_from_indices(scene, vertices, indices) self._set_field_data(scene, data) self._set_sampler_type(scene) cdef void _build_from_indices(self, YTEmbreeScene scene, np.ndarray vertices_in, np.ndarray indices_in): cdef int i, j cdef int nv = vertices_in.shape[0] cdef int ne = indices_in.shape[0] cdef int nt = self.tpe*ne cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) # first just copy over the vertices cdef Vertex* vertices = malloc(nv * sizeof(Vertex)) for i in range(nv): vertices[i].x = vertices_in[i, 0] vertices[i].y = vertices_in[i, 1] vertices[i].z = vertices_in[i, 2] rtcg.rtcSetBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER, vertices, 0, sizeof(Vertex)) # now build up the triangles cdef Triangle* triangles = malloc(nt * sizeof(Triangle)) for i in range(ne): for j in range(self.tpe): triangles[self.tpe*i+j].v0 = indices_in[i][self.tri_array[j][0]] triangles[self.tpe*i+j].v1 = indices_in[i][self.tri_array[j][1]] triangles[self.tpe*i+j].v2 = indices_in[i][self.tri_array[j][2]] rtcg.rtcSetBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER, triangles, 0, sizeof(Triangle)) cdef int* element_indices = malloc(ne * self.vpe * sizeof(int)) for i in range(ne): for j in range(self.vpe): element_indices[i*self.vpe + j] = indices_in[i][j] self.element_indices = element_indices self.vertices = vertices self.indices = triangles self.mesh = mesh cdef void _set_field_data(self, YTEmbreeScene scene, np.ndarray data_in): cdef int ne = data_in.shape[0] self.fpe = data_in.shape[1] cdef double* field_data = malloc(ne * self.fpe * sizeof(double)) for i in range(ne): for j in range(self.fpe): field_data[i*self.fpe+j] = data_in[i][j] self.field_data = field_data cdef MeshDataContainer datac datac.vertices = self.vertices datac.indices = self.indices datac.field_data = self.field_data datac.element_indices = self.element_indices datac.tpe = self.tpe datac.vpe = self.vpe datac.fpe = self.fpe self.datac = datac rtcg.rtcSetUserData(scene.scene_i, self.mesh, &self.datac) cdef void _set_sampler_type(self, YTEmbreeScene scene): if self.vpe == 4: self.filter_func = sample_tetra elif self.vpe == 6: self.filter_func = sample_wedge elif self.vpe == 8: self.filter_func = sample_hex else: raise NotImplementedError("Sampler type not implemented.") rtcg.rtcSetIntersectionFilterFunction(scene.scene_i, self.mesh, self.filter_func) def __dealloc__(self): free(self.field_data) free(self.element_indices) free(self.vertices) free(self.indices) cdef class QuadraticElementMesh: r''' This creates a mesh of quadratic patches corresponding to the faces of 2nd-order Lagrange elements for direct rendering via embree. Currently, this is implemented for 20-point hexahedral meshes only. Parameters ---------- scene : EmbreeScene This is the scene to which the constructed patches will be added. vertices : a np.ndarray of floats. This specifies the x, y, and z coordinates of the vertices in the mesh. This should either have the shape (num_vertices, 3). For example, vertices[2][1] should give the y-coordinate of the 3rd vertex in the mesh. indices : a np.ndarray of ints This should have the shape (num_elements, 20). Each hex will be represented in the scene by 6 bi-quadratic patches. We assume that the node ordering is as defined here: http://homepages.cae.wisc.edu/~tautges/papers/cnmev3.pdf ''' cdef void* patches cdef np.float64_t* vertices cdef np.float64_t* field_data cdef unsigned int mesh # patches per element, vertices per element, vertices per face, # and field points per element, respectively: cdef int ppe, vpe, vpf, fpe def __init__(self, YTEmbreeScene scene, np.ndarray vertices, np.ndarray indices, np.ndarray field_data): # 20-point hexes if indices.shape[1] == 20: self.vpe = 20 self.ppe = 6 self.vpf = 8 self._build_from_indices_hex20(scene, vertices, indices, field_data) # 10-point tets elif indices.shape[1] == 10: self.vpe = 10 self.ppe = 4 self.vpf = 6 self._build_from_indices_tet10(scene, vertices, indices, field_data) else: raise NotImplementedError cdef void _build_from_indices_hex20(self, YTEmbreeScene scene, np.ndarray vertices_in, np.ndarray indices_in, np.ndarray field_data): cdef int i, j, k, ind, idim cdef int ne = indices_in.shape[0] cdef int npatch = self.ppe*ne cdef unsigned int mesh = rtcgu.rtcNewUserGeometry(scene.scene_i, npatch) cdef np.ndarray[np.float64_t, ndim=2] element_vertices cdef Patch* patches = malloc(npatch * sizeof(Patch)) self.vertices = malloc(self.vpe * ne * 3 * sizeof(np.float64_t)) self.field_data = malloc(self.vpe * ne * sizeof(np.float64_t)) for i in range(ne): element_vertices = vertices_in[indices_in[i]] for j in range(self.vpe): self.field_data[i*self.vpe + j] = field_data[i][j] for k in range(3): self.vertices[i*self.vpe*3 + j*3 + k] = element_vertices[j][k] cdef Patch* patch for i in range(ne): # for each element element_vertices = vertices_in[indices_in[i]] for j in range(self.ppe): # for each face patch = &(patches[i*self.ppe+j]) patch.geomID = mesh for k in range(self.vpf): # for each vertex ind = hex20_faces[j][k] for idim in range(3): # for each spatial dimension (yikes) patch.v[k][idim] = element_vertices[ind][idim] patch.vertices = self.vertices + i*self.vpe*3 patch.field_data = self.field_data + i*self.vpe self.patches = patches self.mesh = mesh rtcg.rtcSetUserData(scene.scene_i, self.mesh, patches) rtcgu.rtcSetBoundsFunction(scene.scene_i, self.mesh, patchBoundsFunc) rtcgu.rtcSetIntersectFunction(scene.scene_i, self.mesh, patchIntersectFunc) cdef void _build_from_indices_tet10(self, YTEmbreeScene scene, np.ndarray vertices_in, np.ndarray indices_in, np.ndarray field_data): cdef int i, j, k, ind, idim cdef int ne = indices_in.shape[0] cdef int npatch = self.ppe*ne cdef unsigned int mesh = rtcgu.rtcNewUserGeometry(scene.scene_i, npatch) cdef np.ndarray[np.float64_t, ndim=2] element_vertices cdef Tet_Patch* patches = malloc(npatch * sizeof(Tet_Patch)) self.vertices = malloc(self.vpe * ne * 3 * sizeof(np.float64_t)) self.field_data = malloc(self.vpe * ne * sizeof(np.float64_t)) for i in range(ne): element_vertices = vertices_in[indices_in[i]] for j in range(self.vpe): self.field_data[i*self.vpe + j] = field_data[i][j] for k in range(3): self.vertices[i*self.vpe*3 + j*3 + k] = element_vertices[j][k] cdef Tet_Patch* patch for i in range(ne): # for each element element_vertices = vertices_in[indices_in[i]] for j in range(self.ppe): # for each face patch = &(patches[i*self.ppe+j]) patch.geomID = mesh for k in range(self.vpf): # for each vertex ind = tet10_faces[j][k] for idim in range(3): # for each spatial dimension (yikes) patch.v[k][idim] = element_vertices[ind][idim] patch.vertices = self.vertices + i*self.vpe*3 patch.field_data = self.field_data + i*self.vpe self.patches = patches self.mesh = mesh rtcg.rtcSetUserData(scene.scene_i, self.mesh, patches) rtcgu.rtcSetBoundsFunction(scene.scene_i, self.mesh, tet_patchBoundsFunc) rtcgu.rtcSetIntersectFunction(scene.scene_i, self.mesh, tet_patchIntersectFunc) def __dealloc__(self): free(self.patches) free(self.vertices) free(self.field_data) yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_intersection.pxd000066400000000000000000000014341510711153200266030ustar00rootroot00000000000000cimport cython cimport pyembree.rtcore as rtc cimport pyembree.rtcore_geometry as rtcg cimport pyembree.rtcore_ray as rtcr from .mesh_construction cimport Patch, Tet_Patch cdef void patchIntersectFunc(Patch* patches, rtcr.RTCRay& ray, size_t item) noexcept nogil cdef void patchBoundsFunc(Patch* patches, size_t item, rtcg.RTCBounds* bounds_o) noexcept nogil cdef void tet_patchIntersectFunc(Tet_Patch* tet_patches, rtcr.RTCRay& ray, size_t item) noexcept nogil cdef void tet_patchBoundsFunc(Tet_Patch* tet_patches, size_t item, rtcg.RTCBounds* bounds_o) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_intersection.pyx000066400000000000000000000100441510711153200266250ustar00rootroot00000000000000# distutils: include_dirs = EMBREE_INC_DIR # distutils: library_dirs = EMBREE_LIB_DIR # distutils: libraries = EMBREE_LIBS # distutils: language = c++ """ This file contains functions used for performing ray-tracing with Embree for 2nd-order Lagrange Elements. Note - this file is only used for the Embree-accelerated ray-tracer. """ cimport cython cimport pyembree.rtcore_geometry as rtcg cimport pyembree.rtcore_ray as rtcr from libc.math cimport fabs, fmax, fmin from yt.utilities.lib.primitives cimport ( RayHitData, compute_patch_hit, compute_tet_patch_hit, ) from .mesh_samplers cimport sample_hex20, sample_tet10 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patchBoundsFunc(Patch* patches, size_t item, rtcg.RTCBounds* bounds_o) noexcept nogil: cdef Patch patch = patches[item] cdef float lo_x = 1.0e300 cdef float lo_y = 1.0e300 cdef float lo_z = 1.0e300 cdef float hi_x = -1.0e300 cdef float hi_y = -1.0e300 cdef float hi_z = -1.0e300 cdef int i for i in range(8): lo_x = fmin(lo_x, patch.v[i][0]) lo_y = fmin(lo_y, patch.v[i][1]) lo_z = fmin(lo_z, patch.v[i][2]) hi_x = fmax(hi_x, patch.v[i][0]) hi_y = fmax(hi_y, patch.v[i][1]) hi_z = fmax(hi_z, patch.v[i][2]) bounds_o.lower_x = lo_x bounds_o.lower_y = lo_y bounds_o.lower_z = lo_z bounds_o.upper_x = hi_x bounds_o.upper_y = hi_y bounds_o.upper_z = hi_z @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patchIntersectFunc(Patch* patches, rtcr.RTCRay& ray, size_t item) noexcept nogil: cdef Patch patch = patches[item] cdef RayHitData hd = compute_patch_hit(patch.v, ray.org, ray.dir) # only count this is it's the closest hit if (hd.t < ray.tnear or hd.t > ray.Ng[0]): return if (fabs(hd.u) <= 1.0 and fabs(hd.v) <= 1.0 and hd.converged): # we have a hit, so update ray information ray.u = hd.u ray.v = hd.v ray.geomID = patch.geomID ray.primID = item ray.Ng[0] = hd.t # sample the solution at the calculated point sample_hex20(patches, ray) return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patchBoundsFunc(Tet_Patch* tet_patches, size_t item, rtcg.RTCBounds* bounds_o) noexcept nogil: cdef Tet_Patch tet_patch = tet_patches[item] cdef float lo_x = 1.0e300 cdef float lo_y = 1.0e300 cdef float lo_z = 1.0e300 cdef float hi_x = -1.0e300 cdef float hi_y = -1.0e300 cdef float hi_z = -1.0e300 cdef int i for i in range(6): lo_x = fmin(lo_x, tet_patch.v[i][0]) lo_y = fmin(lo_y, tet_patch.v[i][1]) lo_z = fmin(lo_z, tet_patch.v[i][2]) hi_x = fmax(hi_x, tet_patch.v[i][0]) hi_y = fmax(hi_y, tet_patch.v[i][1]) hi_z = fmax(hi_z, tet_patch.v[i][2]) bounds_o.lower_x = lo_x bounds_o.lower_y = lo_y bounds_o.lower_z = lo_z bounds_o.upper_x = hi_x bounds_o.upper_y = hi_y bounds_o.upper_z = hi_z @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patchIntersectFunc(Tet_Patch* tet_patches, rtcr.RTCRay& ray, size_t item) noexcept nogil: cdef Tet_Patch tet_patch = tet_patches[item] cdef RayHitData hd = compute_tet_patch_hit(tet_patch.v, ray.org, ray.dir) # only count this is it's the closest hit if (hd.t < ray.tnear or hd.t > ray.Ng[0]): return if (hd.converged and 0 <= hd.u and 0 <= hd.v and hd.u + hd.v <= 1): # we have a hit, so update ray information ray.u = hd.u ray.v = hd.v ray.geomID = tet_patch.geomID ray.primID = item ray.Ng[0] = hd.t # sample the solution at the calculated point sample_tet10(tet_patches, ray) return yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_samplers.pxd000066400000000000000000000010521510711153200257170ustar00rootroot00000000000000cimport cython cimport pyembree.rtcore as rtc cimport pyembree.rtcore_ray as rtcr cdef void sample_hex(void* userPtr, rtcr.RTCRay& ray) noexcept nogil cdef void sample_wedge(void* userPtr, rtcr.RTCRay& ray) noexcept nogil cdef void sample_tetra(void* userPtr, rtcr.RTCRay& ray) noexcept nogil cdef void sample_hex20(void* userPtr, rtcr.RTCRay& ray) noexcept nogil cdef void sample_tet10(void* userPtr, rtcr.RTCRay& ray) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_samplers.pyx000066400000000000000000000216651510711153200257600ustar00rootroot00000000000000# distutils: include_dirs = EMBREE_INC_DIR # distutils: library_dirs = EMBREE_LIB_DIR # distutils: libraries = EMBREE_LIBS # distutils: language = c++ """ This file contains functions that sample a surface mesh at the point hit by a ray. These can be used with pyembree in the form of "filter feedback functions." Note - this file is only used for the Embree-accelerated ray-tracer. """ cimport cython cimport pyembree.rtcore_ray as rtcr from pyembree.rtcore cimport Triangle from yt.utilities.lib.element_mappings cimport ( ElementSampler, P1Sampler3D, Q1Sampler3D, S2Sampler3D, Tet2Sampler3D, W1Sampler3D, ) from yt.utilities.lib.primitives cimport patchSurfaceFunc, tet_patchSurfaceFunc from .mesh_construction cimport MeshDataContainer, Patch, Tet_Patch cdef ElementSampler Q1Sampler = Q1Sampler3D() cdef ElementSampler P1Sampler = P1Sampler3D() cdef ElementSampler S2Sampler = S2Sampler3D() cdef ElementSampler W1Sampler = W1Sampler3D() cdef ElementSampler Tet2Sampler = Tet2Sampler3D() @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void get_hit_position(double* position, void* userPtr, rtcr.RTCRay& ray) noexcept nogil: cdef int primID, i cdef double[3][3] vertex_positions cdef Triangle tri cdef MeshDataContainer* data primID = ray.primID data = userPtr tri = data.indices[primID] vertex_positions[0][0] = data.vertices[tri.v0].x vertex_positions[0][1] = data.vertices[tri.v0].y vertex_positions[0][2] = data.vertices[tri.v0].z vertex_positions[1][0] = data.vertices[tri.v1].x vertex_positions[1][1] = data.vertices[tri.v1].y vertex_positions[1][2] = data.vertices[tri.v1].z vertex_positions[2][0] = data.vertices[tri.v2].x vertex_positions[2][1] = data.vertices[tri.v2].y vertex_positions[2][2] = data.vertices[tri.v2].z for i in range(3): position[i] = vertex_positions[0][i]*(1.0 - ray.u - ray.v) + \ vertex_positions[1][i]*ray.u + \ vertex_positions[2][i]*ray.v @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void sample_hex(void* userPtr, rtcr.RTCRay& ray) noexcept nogil: cdef int ray_id, elem_id, i cdef double val cdef double[8] field_data cdef int[8] element_indices cdef double[24] vertices cdef double[3] position cdef MeshDataContainer* data data = userPtr ray_id = ray.primID if ray_id == -1: return # ray_id records the id number of the hit according to # embree, in which the primitives are triangles. Here, # we convert this to the element id by dividing by the # number of triangles per element. elem_id = ray_id / data.tpe get_hit_position(position, userPtr, ray) for i in range(8): element_indices[i] = data.element_indices[elem_id*8+i] for i in range(data.fpe): field_data[i] = data.field_data[elem_id*data.fpe+i] for i in range(8): vertices[i*3] = data.vertices[element_indices[i]].x vertices[i*3 + 1] = data.vertices[element_indices[i]].y vertices[i*3 + 2] = data.vertices[element_indices[i]].z # we use ray.time to pass the value of the field cdef double mapped_coord[3] Q1Sampler.map_real_to_unit(mapped_coord, vertices, position) if data.fpe == 1: val = field_data[0] else: val = Q1Sampler.sample_at_unit_point(mapped_coord, field_data) ray.time = val # we use ray.instID to pass back whether the ray is near the # element boundary or not (used to annotate mesh lines) ray.instID = Q1Sampler.check_mesh_lines(mapped_coord) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void sample_wedge(void* userPtr, rtcr.RTCRay& ray) noexcept nogil: cdef int ray_id, elem_id, i cdef double val cdef double[6] field_data cdef int[6] element_indices cdef double[18] vertices cdef double[3] position cdef MeshDataContainer* data data = userPtr ray_id = ray.primID if ray_id == -1: return # ray_id records the id number of the hit according to # embree, in which the primitives are triangles. Here, # we convert this to the element id by dividing by the # number of triangles per element. elem_id = ray_id / data.tpe get_hit_position(position, userPtr, ray) for i in range(6): element_indices[i] = data.element_indices[elem_id*6+i] for i in range(data.fpe): field_data[i] = data.field_data[elem_id*data.fpe+i] for i in range(6): vertices[i*3] = data.vertices[element_indices[i]].x vertices[i*3 + 1] = data.vertices[element_indices[i]].y vertices[i*3 + 2] = data.vertices[element_indices[i]].z # we use ray.time to pass the value of the field cdef double mapped_coord[3] W1Sampler.map_real_to_unit(mapped_coord, vertices, position) if data.fpe == 1: val = field_data[0] else: val = W1Sampler.sample_at_unit_point(mapped_coord, field_data) ray.time = val ray.instID = W1Sampler.check_mesh_lines(mapped_coord) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) @cython.cdivision(True) cdef void sample_hex20(void* userPtr, rtcr.RTCRay& ray) noexcept nogil: cdef int ray_id, i cdef double val cdef double[3] position cdef float[3] pos cdef Patch* data data = userPtr ray_id = ray.primID if ray_id == -1: return cdef Patch patch = data[ray_id] # ray_id records the id number of the hit according to # embree, in which the primitives are patches. Here, # we convert this to the element id by dividing by the # number of patches per element. # fills "position" with the physical position of the hit patchSurfaceFunc(data[ray_id].v, ray.u, ray.v, pos) for i in range(3): position[i] = pos[i] # we use ray.time to pass the value of the field cdef double mapped_coord[3] S2Sampler.map_real_to_unit(mapped_coord, patch.vertices, position) val = S2Sampler.sample_at_unit_point(mapped_coord, patch.field_data) ray.time = val ray.instID = S2Sampler.check_mesh_lines(mapped_coord) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void sample_tetra(void* userPtr, rtcr.RTCRay& ray) noexcept nogil: cdef int ray_id, elem_id, i cdef double val cdef double[4] field_data cdef int[4] element_indices cdef double[12] vertices cdef double[3] position cdef MeshDataContainer* data data = userPtr ray_id = ray.primID if ray_id == -1: return get_hit_position(position, userPtr, ray) # ray_id records the id number of the hit according to # embree, in which the primitives are triangles. Here, # we convert this to the element id by dividing by the # number of triangles per element. elem_id = ray_id / data.tpe for i in range(4): element_indices[i] = data.element_indices[elem_id*4+i] vertices[i*3] = data.vertices[element_indices[i]].x vertices[i*3 + 1] = data.vertices[element_indices[i]].y vertices[i*3 + 2] = data.vertices[element_indices[i]].z for i in range(data.fpe): field_data[i] = data.field_data[elem_id*data.fpe+i] # we use ray.time to pass the value of the field cdef double mapped_coord[4] P1Sampler.map_real_to_unit(mapped_coord, vertices, position) if data.fpe == 1: val = field_data[0] else: val = P1Sampler.sample_at_unit_point(mapped_coord, field_data) ray.time = val ray.instID = P1Sampler.check_mesh_lines(mapped_coord) @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) @cython.cdivision(True) cdef void sample_tet10(void* userPtr, rtcr.RTCRay& ray) noexcept nogil: cdef int ray_id, i cdef double val cdef double[3] position cdef float[3] pos cdef Tet_Patch* data data = userPtr ray_id = ray.primID if ray_id == -1: return cdef Tet_Patch tet_patch = data[ray_id] # ray_id records the id number of the hit according to # embree, in which the primitives are patches. Here, # we convert this to the element id by dividing by the # number of patches per element. # fills "position" with the physical position of the hit tet_patchSurfaceFunc(data[ray_id].v, ray.u, ray.v, pos) for i in range(3): position[i] = pos[i] # we use ray.time to pass the value of the field cdef double mapped_coord[3] Tet2Sampler.map_real_to_unit(mapped_coord, tet_patch.vertices, position) val = Tet2Sampler.sample_at_unit_point(mapped_coord, tet_patch.field_data) ray.time = val ray.instID = Tet2Sampler.check_mesh_lines(mapped_coord) yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_traversal.pxd000066400000000000000000000002251510711153200260750ustar00rootroot00000000000000cimport pyembree.rtcore cimport pyembree.rtcore_ray cimport pyembree.rtcore_scene as rtcs cdef class YTEmbreeScene: cdef rtcs.RTCScene scene_i yt-project-yt-f043ac8/yt/utilities/lib/embree_mesh/mesh_traversal.pyx000066400000000000000000000053211510711153200261240ustar00rootroot00000000000000# distutils: include_dirs = EMBREE_INC_DIR # distutils: library_dirs = EMBREE_LIB_DIR # distutils: libraries = EMBREE_LIBS # distutils: language = c++ """ This file contains the MeshSampler classes, which handles casting rays at a mesh source using either pyembree or the cython ray caster. """ cimport cython cimport numpy as np import numpy as np cimport pyembree.rtcore as rtc cimport pyembree.rtcore_geometry as rtcg cimport pyembree.rtcore_ray as rtcr cimport pyembree.rtcore_scene as rtcs from libc.stdlib cimport free, malloc from yt.utilities.lib.image_samplers cimport ImageSampler rtc.rtcInit(NULL) rtc.rtcSetErrorFunction(error_printer) cdef void error_printer(const rtc.RTCError code, const char *_str): print("ERROR CAUGHT IN EMBREE") rtc.print_error(code) print("ERROR MESSAGE:", _str) cdef class YTEmbreeScene: def __init__(self): self.scene_i = rtcs.rtcNewScene(rtcs.RTC_SCENE_STATIC, rtcs.RTC_INTERSECT1) def __dealloc__(self): rtcs.rtcDeleteScene(self.scene_i) cdef class EmbreeMeshSampler(ImageSampler): @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __call__(self, YTEmbreeScene scene, int num_threads = 0): ''' This function is supposed to cast the rays and return the image. ''' rtcs.rtcCommit(scene.scene_i) cdef int vi, vj, i, j cdef np.float64_t *v_pos cdef np.float64_t *v_dir cdef np.int64_t nx, ny, size cdef np.float64_t width[3] for i in range(3): width[i] = self.width[i] nx = self.nv[0] ny = self.nv[1] size = nx * ny cdef rtcr.RTCRay ray v_pos = malloc(3 * sizeof(np.float64_t)) v_dir = malloc(3 * sizeof(np.float64_t)) for j in range(size): vj = j % ny vi = (j - vj) / ny vj = vj self.vector_function(self, vi, vj, width, v_dir, v_pos) for i in range(3): ray.org[i] = v_pos[i] ray.dir[i] = v_dir[i] ray.tnear = 0.0 ray.tfar = 1e37 ray.geomID = rtcg.RTC_INVALID_GEOMETRY_ID ray.primID = rtcg.RTC_INVALID_GEOMETRY_ID ray.instID = rtcg.RTC_INVALID_GEOMETRY_ID ray.mask = -1 ray.time = 0 ray.Ng[0] = 1e37 # we use this to track the hit distance rtcs.rtcIntersect(scene.scene_i, ray) self.image[vi, vj, 0] = ray.time self.image_used[vi, vj] = ray.primID self.mesh_lines[vi, vj] = ray.instID self.zbuffer[vi, vj] = ray.tfar free(v_pos) free(v_dir) yt-project-yt-f043ac8/yt/utilities/lib/endian_swap.h000066400000000000000000000011001510711153200225200ustar00rootroot00000000000000/* These macros are taken from Paul Bourke's page at http://local.wasp.uwa.edu.au/~pbourke/dataformats/fortran/ The current year is 2010, and evidently we still have to deal with endianness. */ #define SWAP_2(x) ( (((x) & 0xff) << 8) | ((unsigned short)(x) >> 8) ) #define SWAP_4(x) ( ((x) << 24) | (((x) << 8) & 0x00ff0000) | \ (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ) #define FIX_SHORT(x) (*(unsigned short *)&(x) = SWAP_2(*(unsigned short *)&(x))) #define FIX_LONG(x) (*(unsigned *)&(x) = SWAP_4(*(unsigned *)&(x))) #define FIX_FLOAT(x) FIX_LONG(x) yt-project-yt-f043ac8/yt/utilities/lib/field_interpolation_tables.pxd000066400000000000000000000113171510711153200261730ustar00rootroot00000000000000# distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG # distutils: extra_link_args = CPP14_FLAG """ Field Interpolation Tables """ cimport cython cimport numpy as np from libc.stdlib cimport malloc from yt.utilities.lib.fp_utils cimport fabs, fclip, fmax, fmin, iclip, imax, imin cdef extern from "" namespace "std": bint isnormal(double x) noexcept nogil cdef extern from "platform_dep_math.hpp": bint __isnormal(double) noexcept nogil cdef struct FieldInterpolationTable: # Note that we make an assumption about retaining a reference to values # externally. np.float64_t *values np.float64_t bounds[2] np.float64_t dbin np.float64_t idbin np.float64_t *d0 np.float64_t *dy int field_id int weight_field_id int weight_table_id int nbins @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void FIT_initialize_table(FieldInterpolationTable *fit, int nbins, np.float64_t *values, np.float64_t bounds1, np.float64_t bounds2, int field_id, int weight_field_id, int weight_table_id) noexcept nogil: cdef int i fit.bounds[0] = bounds1; fit.bounds[1] = bounds2 fit.nbins = nbins fit.dbin = (fit.bounds[1] - fit.bounds[0])/(fit.nbins-1) fit.idbin = 1.0/fit.dbin # Better not pull this out from under us, yo fit.values = values fit.d0 = malloc(sizeof(np.float64_t) * nbins) fit.dy = malloc(sizeof(np.float64_t) * nbins) for i in range(nbins-1): fit.d0[i] = fit.bounds[0] + i * fit.dbin fit.dy[i] = (fit.values[i + 1] - fit.values[i]) * fit.idbin fit.field_id = field_id fit.weight_field_id = weight_field_id fit.weight_table_id = weight_table_id @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline np.float64_t FIT_get_value(const FieldInterpolationTable *fit, np.float64_t dvs[6]) noexcept nogil: cdef np.float64_t dd, dout cdef int bin_id if dvs[fit.field_id] >= fit.bounds[1] or dvs[fit.field_id] <= fit.bounds[0]: return 0.0 if not __isnormal(dvs[fit.field_id]): return 0.0 bin_id = ((dvs[fit.field_id] - fit.bounds[0]) * fit.idbin) bin_id = iclip(bin_id, 0, fit.nbins-2) dd = dvs[fit.field_id] - fit.d0[bin_id] # x - x0 dout = fit.values[bin_id] + dd * fit.dy[bin_id] cdef int wfi = fit.weight_field_id if wfi != -1: dout *= dvs[wfi] return dout @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void FIT_eval_transfer( const np.float64_t dt, np.float64_t *dvs, np.float64_t *rgba, const int n_fits, const FieldInterpolationTable fits[6], const int field_table_ids[6], const int grey_opacity) noexcept nogil: cdef int i, fid cdef np.float64_t ta cdef np.float64_t istorage[6] cdef np.float64_t trgba[6] for i in range(n_fits): istorage[i] = FIT_get_value(&fits[i], dvs) for i in range(n_fits): fid = fits[i].weight_table_id if fid != -1: istorage[i] *= istorage[fid] for i in range(6): trgba[i] = istorage[field_table_ids[i]] if grey_opacity == 1: ta = fmax(1.0 - dt*trgba[3], 0.0) for i in range(4): rgba[i] = dt*trgba[i] + ta*rgba[i] else: for i in range(3): ta = fmax(1.0-dt*trgba[i], 0.0) rgba[i] = dt*trgba[i] + ta*rgba[i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void FIT_eval_transfer_with_light(np.float64_t dt, np.float64_t *dvs, np.float64_t *grad, np.float64_t *l_dir, np.float64_t *l_rgba, np.float64_t *rgba, int n_fits, FieldInterpolationTable fits[6], int field_table_ids[6], int grey_opacity) noexcept nogil: cdef int i, fid cdef np.float64_t ta, dot_prod cdef np.float64_t istorage[6] cdef np.float64_t trgba[6] dot_prod = 0.0 for i in range(3): dot_prod += l_dir[i]*grad[i] #dot_prod = fmax(0.0, dot_prod) for i in range(6): istorage[i] = 0.0 for i in range(n_fits): istorage[i] = FIT_get_value(&fits[i], dvs) for i in range(n_fits): fid = fits[i].weight_table_id if fid != -1: istorage[i] *= istorage[fid] for i in range(6): trgba[i] = istorage[field_table_ids[i]] if grey_opacity == 1: ta = fmax(1.0-dt*(trgba[0] + trgba[1] + trgba[2]), 0.0) for i in range(3): rgba[i] = (1.-ta)*trgba[i]*(1. + dot_prod*l_rgba[i]) + ta * rgba[i] else: for i in range(3): ta = fmax(1.0-dt*trgba[i], 0.0) rgba[i] = (1.-ta)*trgba[i]*(1. + dot_prod*l_rgba[i]) + ta * rgba[i] yt-project-yt-f043ac8/yt/utilities/lib/fixed_interpolator.cpp000066400000000000000000000111641510711153200244770ustar00rootroot00000000000000/******************************************************************************* *******************************************************************************/ // // A small, tiny, itty bitty module for computation-intensive interpolation // that I can't seem to make fast in Cython // #include "fixed_interpolator.hpp" #define VINDEX(A,B,C) data[((((A)+ci[0])*(ds[1]+1)+((B)+ci[1]))*(ds[2]+1)+ci[2]+(C))] // (((C*ds[1])+B)*ds[0]+A) #define OINDEX(A,B,C) data[(A)*(ds[1]+1)*(ds[2]+1)+(B)*ds[2]+(B)+(C)] npy_float64 fast_interpolate(int ds[3], int ci[3], npy_float64 dp[3], npy_float64 *data) { int i; npy_float64 dv, dm[3]; for(i=0;i<3;i++)dm[i] = (1.0 - dp[i]); dv = 0.0; dv += VINDEX(0,0,0) * (dm[0]*dm[1]*dm[2]); dv += VINDEX(0,0,1) * (dm[0]*dm[1]*dp[2]); dv += VINDEX(0,1,0) * (dm[0]*dp[1]*dm[2]); dv += VINDEX(0,1,1) * (dm[0]*dp[1]*dp[2]); dv += VINDEX(1,0,0) * (dp[0]*dm[1]*dm[2]); dv += VINDEX(1,0,1) * (dp[0]*dm[1]*dp[2]); dv += VINDEX(1,1,0) * (dp[0]*dp[1]*dm[2]); dv += VINDEX(1,1,1) * (dp[0]*dp[1]*dp[2]); /*assert(dv < -20);*/ return dv; } npy_float64 offset_interpolate(int ds[3], npy_float64 dp[3], npy_float64 *data) { npy_float64 dv, vz[4]; dv = 1.0 - dp[2]; vz[0] = dv*OINDEX(0,0,0) + dp[2]*OINDEX(0,0,1); vz[1] = dv*OINDEX(0,1,0) + dp[2]*OINDEX(0,1,1); vz[2] = dv*OINDEX(1,0,0) + dp[2]*OINDEX(1,0,1); vz[3] = dv*OINDEX(1,1,0) + dp[2]*OINDEX(1,1,1); dv = 1.0 - dp[1]; vz[0] = dv*vz[0] + dp[1]*vz[1]; vz[1] = dv*vz[2] + dp[1]*vz[3]; dv = 1.0 - dp[0]; vz[0] = dv*vz[0] + dp[0]*vz[1]; return vz[0]; } void offset_fill(int ds[3], npy_float64 *data, npy_float64 gridval[8]) { gridval[0] = OINDEX(0,0,0); gridval[1] = OINDEX(1,0,0); gridval[2] = OINDEX(1,1,0); gridval[3] = OINDEX(0,1,0); gridval[4] = OINDEX(0,0,1); gridval[5] = OINDEX(1,0,1); gridval[6] = OINDEX(1,1,1); gridval[7] = OINDEX(0,1,1); } void vertex_interp(npy_float64 v1, npy_float64 v2, npy_float64 isovalue, npy_float64 vl[3], npy_float64 dds[3], npy_float64 x, npy_float64 y, npy_float64 z, int vind1, int vind2) { /*if (fabs(isovalue - v1) < 0.000001) return 0.0; if (fabs(isovalue - v2) < 0.000001) return 1.0; if (fabs(v1 - v2) < 0.000001) return 0.0;*/ int i; static npy_float64 cverts[8][3] = {{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0}, {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}}; npy_float64 mu = ((isovalue - v1) / (v2 - v1)); if (fabs(1.0 - isovalue/v1) < 0.000001) mu = 0.0; if (fabs(1.0 - isovalue/v2) < 0.000001) mu = 1.0; if (fabs(v1/v2) < 0.000001) mu = 0.0; vl[0] = x; vl[1] = y; vl[2] = z; for (i=0;i<3;i++) vl[i] += dds[i] * cverts[vind1][i] + dds[i] * mu*(cverts[vind2][i] - cverts[vind1][i]); } npy_float64 trilinear_interpolate(int ds[3], int ci[3], npy_float64 dp[3], npy_float64 *data) { /* dims is one less than the dimensions of the array */ int i; npy_float64 dm[3], vz[4]; //dp is the distance to the plane. dm is val, dp = 1-val for(i=0;i<3;i++)dm[i] = (1.0 - dp[i]); //First interpolate in z vz[0] = dm[2]*VINDEX(0,0,0) + dp[2]*VINDEX(0,0,1); vz[1] = dm[2]*VINDEX(0,1,0) + dp[2]*VINDEX(0,1,1); vz[2] = dm[2]*VINDEX(1,0,0) + dp[2]*VINDEX(1,0,1); vz[3] = dm[2]*VINDEX(1,1,0) + dp[2]*VINDEX(1,1,1); //Then in y vz[0] = dm[1]*vz[0] + dp[1]*vz[1]; vz[1] = dm[1]*vz[2] + dp[1]*vz[3]; //Then in x vz[0] = dm[0]*vz[0] + dp[0]*vz[1]; /*assert(dv < -20);*/ return vz[0]; } void eval_gradient(int ds[3], npy_float64 dp[3], npy_float64 *data, npy_float64 *grad) { // We just take some small value int i; npy_float64 denom, plus, minus, backup, normval; normval = 0.0; for (i = 0; i < 3; i++) { backup = dp[i]; grad[i] = 0.0; if (dp[i] >= 0.95) {plus = dp[i]; minus = dp[i] - 0.05;} else if (dp[i] <= 0.05) {plus = dp[i] + 0.05; minus = 0.0;} else {plus = dp[i] + 0.05; minus = dp[i] - 0.05;} //fprintf(stderr, "DIM: %d %0.3lf %0.3lf\n", i, plus, minus); denom = plus - minus; dp[i] = plus; grad[i] += offset_interpolate(ds, dp, data) / denom; dp[i] = minus; grad[i] -= offset_interpolate(ds, dp, data) / denom; dp[i] = backup; normval += grad[i]*grad[i]; } if (normval != 0.0){ normval = sqrt(normval); for (i = 0; i < 3; i++) grad[i] /= -normval; //fprintf(stderr, "Normval: %0.3lf %0.3lf %0.3lf %0.3lf\n", // normval, grad[0], grad[1], grad[2]); }else{ grad[0]=grad[1]=grad[2]=0.0; } } yt-project-yt-f043ac8/yt/utilities/lib/fixed_interpolator.hpp000066400000000000000000000021401510711153200244760ustar00rootroot00000000000000/******************************************************************************* *******************************************************************************/ // // A small, tiny, itty bitty module for computation-intensive interpolation // that I can't seem to make fast in Cython // #include "Python.h" #include #include #include #include #include "numpy/ndarrayobject.h" npy_float64 fast_interpolate(int ds[3], int ci[3], npy_float64 dp[3], npy_float64 *data); npy_float64 offset_interpolate(int ds[3], npy_float64 dp[3], npy_float64 *data); npy_float64 trilinear_interpolate(int ds[3], int ci[3], npy_float64 dp[3], npy_float64 *data); void eval_gradient(int ds[3], npy_float64 dp[3], npy_float64 *data, npy_float64 *grad); void offset_fill(int *ds, npy_float64 *data, npy_float64 *gridval); void vertex_interp(npy_float64 v1, npy_float64 v2, npy_float64 isovalue, npy_float64 vl[3], npy_float64 dds[3], npy_float64 x, npy_float64 y, npy_float64 z, int vind1, int vind2); yt-project-yt-f043ac8/yt/utilities/lib/fixed_interpolator.pxd000066400000000000000000000020211510711153200245000ustar00rootroot00000000000000""" Fixed interpolator includes """ cimport numpy as np cdef extern from "fixed_interpolator.hpp": np.float64_t fast_interpolate(int ds[3], int ci[3], np.float64_t dp[3], np.float64_t *data) noexcept nogil np.float64_t offset_interpolate(int ds[3], np.float64_t dp[3], np.float64_t *data) noexcept nogil np.float64_t trilinear_interpolate(int ds[3], int ci[3], np.float64_t dp[3], np.float64_t *data) noexcept nogil void eval_gradient(int ds[3], np.float64_t dp[3], np.float64_t *data, np.float64_t grad[3]) noexcept nogil void offset_fill(int *ds, np.float64_t *data, np.float64_t *gridval) noexcept nogil void vertex_interp(np.float64_t v1, np.float64_t v2, np.float64_t isovalue, np.float64_t vl[3], np.float64_t dds[3], np.float64_t x, np.float64_t y, np.float64_t z, int vind1, int vind2) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/fnv_hash.pxd000066400000000000000000000002361510711153200224010ustar00rootroot00000000000000""" Definitions for fnv_hash """ import numpy as np cimport numpy as np cdef np.int64_t c_fnv_hash(unsigned unsigned char[:] octets) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/fnv_hash.pyx000066400000000000000000000016321510711153200224270ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: include_dirs = LIB_DIR """ Fast hashing routines """ import numpy as np cimport cython cimport numpy as np @cython.wraparound(False) @cython.boundscheck(False) cdef np.int64_t c_fnv_hash(unsigned char[:] octets) noexcept nogil: # https://bitbucket.org/yt_analysis/yt/issues/1052/field-access-tests-fail-under-python3 # FNV hash cf. http://www.isthe.com/chongo/tech/comp/fnv/index.html cdef np.int64_t hash_val = 2166136261 cdef int i for i in range(octets.shape[0]): hash_val = hash_val ^ octets[i] hash_val = hash_val * 16777619 return hash_val def fnv_hash(octets): """ Create a FNV hash from a bytestring. Info: http://www.isthe.com/chongo/tech/comp/fnv/index.html Parameters ---------- octets : bytestring The string of bytes to generate a hash from. """ return c_fnv_hash(octets) yt-project-yt-f043ac8/yt/utilities/lib/fortran_reader.pyx000066400000000000000000000314161510711153200236330ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Simple readers for fortran unformatted data, specifically for the Tiger code. """ import numpy as np cimport cython cimport numpy as np from libc.stdio cimport FILE, fclose, fopen #cdef inline int imax(int i0, int i1): #if i0 > i1: return i0 #return i1 cdef extern from "endian_swap.h": void FIX_SHORT( unsigned short ) void FIX_LONG( unsigned ) void FIX_FLOAT( float ) cdef extern from "platform_dep.h": void *alloca(size_t) cdef extern from "stdio.h": cdef int SEEK_SET cdef int SEEK_CUR cdef int SEEK_END int fseek(FILE *stream, long offset, int whence) size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) long ftell(FILE *stream) char *fgets(char *s, int size, FILE *stream) @cython.boundscheck(False) @cython.wraparound(False) def read_and_seek(char *filename, np.int64_t offset1, np.int64_t offset2, np.ndarray buffer, int bytes): cdef FILE *f = fopen(filename, "rb") cdef void *buf = buffer.data cdef char line[1024] cdef size_t n = 1023 fseek(f, offset1, SEEK_SET) fgets(line, n, f) fseek(f, offset2, SEEK_CUR) fread(buf, 1, bytes, f) fclose(f) def count_art_octs(char *fn, long offset, int min_level, int max_level, int nhydro_vars, level_info): cdef int nchild = 8 cdef int next_record = -1, nLevel = -1 cdef int dummy_records[9] cdef int readin = -1 cdef FILE *f = fopen(fn, "rb") fseek(f, offset, SEEK_SET) for _ in range(min_level + 1, max_level + 1): fread(dummy_records, sizeof(int), 2, f) fread(&nLevel, sizeof(int), 1, f); FIX_LONG(nLevel) print(level_info) level_info.append(nLevel) fread(dummy_records, sizeof(int), 2, f) fread(&next_record, sizeof(int), 1, f); FIX_LONG(next_record) print("Record size is:", next_record) # Offset for one record header we just read next_record = (nLevel * (next_record + 2*sizeof(int))) - sizeof(int) fseek(f, next_record, SEEK_CUR) # Now we skip the second section fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) nhydro_vars = next_record//4-2-3 #nhvar in daniel's code #record length is normally 2 pad bytes, 8 + 2 hvars (the 2 is nchem) # and then 3 vars, but we can find nhvars only here and not in other # file headers next_record = (2*sizeof(int) + readin) * (nLevel * nchild) next_record -= sizeof(int) fseek(f, next_record, SEEK_CUR) print("nhvars",nhydro_vars) fclose(f) def read_art_tree(char *fn, long offset, int min_level, int max_level, np.ndarray[np.int64_t, ndim=2] oct_indices, np.ndarray[np.int64_t, ndim=1] oct_levels, np.ndarray[np.int64_t, ndim=2] oct_info): # np.ndarray[np.int64_t, ndim=1] oct_mask, # np.ndarray[np.int64_t, ndim=1] oct_parents, # This accepts the filename of the ART header and an integer offset that # points to the start of the record *following* the reading of iOctFree and # nOct. For those following along at home, we only need to read: # iOctPr, iOctLv cdef int nchild = 8 cdef int iOct, nLevel, ic1 cdef np.int64_t next_record = -1 cdef long long child_record cdef int iOctPs[3] cdef np.int64_t dummy_records[9] cdef int readin = -1 cdef FILE *f = fopen(fn, "rb") fseek(f, offset, SEEK_SET) cdef int Level = -1 cdef int * iNOLL = alloca(sizeof(int)*(max_level-min_level+1)) cdef int * iHOLL = alloca(sizeof(int)*(max_level-min_level+1)) cdef int iOctMax = 0 level_offsets = [0] for _ in range(min_level + 1, max_level + 1): fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) fread(&Level, sizeof(int), 1, f); FIX_LONG(Level) fread(&iNOLL[Level], sizeof(int), 1, f); FIX_LONG(iNOLL[Level]) fread(&iHOLL[Level], sizeof(int), 1, f); FIX_LONG(iHOLL[Level]) fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) iOct = iHOLL[Level] - 1 nLevel = iNOLL[Level] #print("Reading Hierarchy for Level", Lev, Level, nLevel, iOct) #print(ftell(f)) for ic1 in range(nLevel): iOctMax = max(iOctMax, iOct) #print(readin, iOct, nLevel, sizeof(int)) next_record = ftell(f) fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) assert readin==52 next_record += readin + sizeof(int) fread(iOctPs, sizeof(int), 3, f) FIX_LONG(iOctPs[0]); FIX_LONG(iOctPs[1]); FIX_LONG(iOctPs[2]) oct_indices[iOct, 0] = iOctPs[0] oct_indices[iOct, 1] = iOctPs[1] oct_indices[iOct, 2] = iOctPs[2] oct_info[iOct, 1] = ic1 #grid_info[iOct, 2] = iOctPr # we don't seem to need this fread(dummy_records, sizeof(int), 6, f) # skip Nb fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) #oct_parents[iOct] = readin - 1 fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) oct_levels[iOct] = readin fread(&iOct, sizeof(int), 1, f); FIX_LONG(iOct) iOct -= 1 assert next_record > 0 fseek(f, next_record, SEEK_SET) fread(&readin, sizeof(int), 1, f); FIX_LONG(readin) assert readin==52 level_offsets.append(ftell(f)) #skip over the hydro variables #find the length of one child section #print('measuring child record ',) fread(&next_record, sizeof(int), 1, f) #print(next_record,) FIX_LONG(next_record) #print(next_record) fseek(f,ftell(f)-sizeof(int),SEEK_SET) #rewind #This is a sloppy fix; next_record is 64bit #and I don't think FIX_LONG(next_record) is working #correctly for 64bits if next_record > 4294967296L: next_record -= 4294967296L assert next_record == 56 #find the length of all of the children section child_record = ftell(f) + (next_record+2*sizeof(int))*nLevel*nchild #print('Skipping over hydro vars', ftell(f), child_record) fseek(f, child_record, SEEK_SET) # for ic1 in range(nLevel * nchild): # fread(&next_record, sizeof(int), 1, f); FIX_LONG(next_record) # fread(&idc, sizeof(int), 1, f); FIX_LONG(idc); idc -= 1 + (128**3) # fread(&cm, sizeof(int), 1, f); FIX_LONG(cm) # #if cm == 0: oct_mask[idc] = 1 # #else: total_masked += 1 # assert next_record > 0 # fseek(f, next_record - sizeof(int), SEEK_CUR) fclose(f) return level_offsets def read_art_root_vars(char *fn, long root_grid_offset, int nhydro_vars, int nx, int ny, int nz, int ix, int iy, int iz, fields, var): cdef FILE *f = fopen(fn, "rb") cdef int j,l, cell_record_size = nhydro_vars * sizeof(float) cdef float temp = -1 l=0 fseek(f, root_grid_offset, SEEK_SET) # Now we seet out the cell we want cdef int my_offset = (((iz * ny) + iy) * nx + ix) #print(cell_record_size, my_offset, ftell(f)) fseek(f, cell_record_size * my_offset, SEEK_CUR) #(((C)*GridDimension[1]+(B))*GridDimension[0]+A) for j in range(nhydro_vars): fread(&temp, sizeof(float), 1, f) if j in fields: FIX_FLOAT(temp) var[l]=temp l+=1 fclose(f) cdef void read_art_vars(FILE *f, int min_level, int max_level, int nhydro_vars, int grid_level,long grid_id,long child_offset, fields, np.ndarray[np.int64_t, ndim=1] level_offsets, var): # nhydro_vars is the number of columns- 3 (adjusting for vars) # this is normally 10=(8+2chem species) cdef int record_size = 2+1+1+nhydro_vars+2 cdef float temp = -1.0 cdef float varpad[2] cdef int new_padding = -1 cdef int padding[3] cdef long offset = 8*grid_id*record_size*sizeof(float) fseek(f, level_offsets[grid_level] + offset, SEEK_SET) for j in range(8): #iterate over the children l = 0 fread(padding, sizeof(int), 3, f); FIX_LONG(padding[0]) #print("Record Size", padding[0]) # This should be replaced by an fread of nhydro_vars length for k in range(nhydro_vars): #iterate over the record fread(&temp, sizeof(float), 1, f); FIX_FLOAT(temp) #print(k, temp) if k in fields: var[j,l] = temp l += 1 fread(varpad, sizeof(float), 2, f) fread(&new_padding, sizeof(int), 1, f); FIX_LONG(new_padding) assert(padding[0] == new_padding) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def read_art_grid(int varindex, np.ndarray[np.int64_t, ndim=1] start_index, np.ndarray[np.int32_t, ndim=1] grid_dims, np.ndarray[np.float32_t, ndim=3] data, np.ndarray[np.uint8_t, ndim=3] filled, np.ndarray[np.float32_t, ndim=2] level_data, int level, int ref_factor, component_grid_info): cdef int gi, i, j, k, grid_id cdef int ir, jr, kr cdef int offi, offj, offk, odind cdef np.int64_t di, dj, dk cdef np.ndarray[np.int64_t, ndim=1] ogrid_info cdef np.ndarray[np.int64_t, ndim=1] og_start_index cdef np.float64_t temp_data cdef np.int64_t end_index[3] cdef int kr_offset, jr_offset, ir_offset cdef int to_fill = 0 # Note that indexing into a cell is: # (k*2 + j)*2 + i for i in range(3): end_index[i] = start_index[i] + grid_dims[i] for gi in range(len(component_grid_info)): ogrid_info = component_grid_info[gi] grid_id = ogrid_info[1] og_start_index = ogrid_info[3:6] #the oct left edge for i in range(2*ref_factor): di = i + og_start_index[0] * ref_factor if di < start_index[0] or di >= end_index[0]: continue ir = (i / ref_factor) for j in range(2 * ref_factor): dj = j + og_start_index[1] * ref_factor if dj < start_index[1] or dj >= end_index[1]: continue jr = (j / ref_factor) for k in range(2 * ref_factor): dk = k + og_start_index[2] * ref_factor if dk < start_index[2] or dk >= end_index[2]: continue kr = (k / ref_factor) offi = di - start_index[0] offj = dj - start_index[1] offk = dk - start_index[2] #print(offi, filled.shape[0],) #print(offj, filled.shape[1],) #print(offk, filled.shape[2]) if filled[offi, offj, offk] == 1: continue if level > 0: odind = (kr*2 + jr)*2 + ir # Replace with an ART-specific reader #temp_data = local_hydro_data.m_var_array[ # level][8*offset + odind] temp_data = level_data[varindex, 8*grid_id + odind] else: kr_offset = kr + (start_index[0] / ref_factor) jr_offset = jr + (start_index[1] / ref_factor) ir_offset = ir + (start_index[2] / ref_factor) odind = (kr_offset * grid_dims[0] + jr_offset)*grid_dims[1] + ir_offset temp_data = level_data[varindex, odind] data[offi, offj, offk] = temp_data filled[offi, offj, offk] = 1 to_fill += 1 return to_fill @cython.cdivision(True) @cython.boundscheck(True) @cython.wraparound(False) def fill_child_mask(np.ndarray[np.int64_t, ndim=2] file_locations, np.ndarray[np.int64_t, ndim=1] grid_le, np.ndarray[np.uint8_t, ndim=4] art_child_masks, np.ndarray[np.uint8_t, ndim=3] child_mask): #loop over file_locations, for each row extracting the index & LE #of the oct we will pull pull from art_child_masks #then use the art_child_masks info to fill in child_mask cdef int i,ioct,x,y,z cdef int nocts = file_locations.shape[0] cdef int lex,ley,lez for i in range(nocts): ioct = file_locations[i,1] #from fortran to python indexing? lex = file_locations[i,3] - grid_le[0] #the oct left edge x ley = file_locations[i,4] - grid_le[1] lez = file_locations[i,5] - grid_le[2] for x in range(2): for y in range(2): for z in range(2): child_mask[lex+x,ley+y,lez+z] = art_child_masks[ioct,x,y,z] yt-project-yt-f043ac8/yt/utilities/lib/fp_utils.pxd000066400000000000000000000031141510711153200224300ustar00rootroot00000000000000""" Shareable definitions for common fp/int Cython utilities """ cimport cython cimport numpy as np cdef inline np.int64_t imax(np.int64_t i0, np.int64_t i1) noexcept nogil: if i0 > i1: return i0 return i1 cdef inline np.float64_t fmax(np.float64_t f0, np.float64_t f1) noexcept nogil: if f0 > f1: return f0 return f1 cdef inline np.int64_t imin(np.int64_t i0, np.int64_t i1) noexcept nogil: if i0 < i1: return i0 return i1 cdef inline np.float64_t fmin(np.float64_t f0, np.float64_t f1) noexcept nogil: if f0 < f1: return f0 return f1 cdef inline np.float64_t fabs(np.float64_t f0) noexcept nogil: if f0 < 0.0: return -f0 return f0 cdef inline np.int64_t iclip(np.int64_t i, np.int64_t a, np.int64_t b) noexcept nogil: if i < a: return a if i > b: return b return i cdef inline np.int64_t i64clip(np.int64_t i, np.int64_t a, np.int64_t b) noexcept nogil: if i < a: return a if i > b: return b return i cdef inline np.float64_t fclip(np.float64_t f, np.float64_t a, np.float64_t b) noexcept nogil: return fmin(fmax(f, a), b) cdef inline np.int64_t i64max(np.int64_t i0, np.int64_t i1) noexcept nogil: if i0 > i1: return i0 return i1 cdef inline np.int64_t i64min(np.int64_t i0, np.int64_t i1) noexcept nogil: if i0 < i1: return i0 return i1 cdef inline _ensure_code(arr): if hasattr(arr, "units"): if "code_length" == str(arr.units): return arr arr.convert_to_units("code_length") return arr ctypedef fused any_float: np.float32_t np.float64_t yt-project-yt-f043ac8/yt/utilities/lib/geometry_utils.pxd000066400000000000000000000322231510711153200236610ustar00rootroot00000000000000""" Particle Deposition onto Octs """ cimport cython cimport numpy as np from libc.float cimport DBL_MANT_DIG from libc.math cimport frexp, ldexp, sqrt cdef enum: ORDER_MAX=20 cdef enum: # TODO: Handle error for indices past max INDEX_MAX_64=2097151 cdef enum: XSHIFT=2 YSHIFT=1 ZSHIFT=0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.int64_t ifrexp(np.float64_t x, np.int64_t *e): cdef np.float64_t m cdef int e0 = 0 m = frexp(x,&e0) e[0] = e0 return ldexp(m,DBL_MANT_DIG) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.int64_t msdb(np.int64_t a, np.int64_t b): """Get the most significant differing bit between a and b.""" cdef np.int64_t c, ndx c = a ^ b ndx = 0 while (0 < c): c = (c >> 1) ndx+=1 return ndx @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.int64_t xor_msb(np.float64_t a, np.float64_t b): """Get the exponent of the highest differing bit between a and b""" # Get mantissa and exponents for each number cdef np.int64_t a_m, a_e, b_m, b_e, x, y, z b_e = 0 a_e = 0 a_m = ifrexp(a,&a_e) b_m = ifrexp(b,&b_e) x = ((a_e+1)*DBL_MANT_DIG) y = ((b_e+1)*DBL_MANT_DIG) # Compare mantissa if exponents equal if x == y: if a_m == b_m: return 0 z = msdb(a_m,b_m) #if 1: return z x = x - z return x-1 # required so that xor_msb(0.0,1.0)!=xor_msb(1.0,1.0) # Otherwise return largest exponent if y < x: return x else: return y @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline int compare_floats_morton(np.float64_t p[3], np.float64_t q[3]): cdef int j, out, dim cdef np.int64_t x, y x = -9999999999 y = 0 dim = 0 for j in range(3):#[::-1]: y = xor_msb(p[j],q[j]) if x < y: x = y dim = j if p[dim] < q[dim]: out = 1 else: out = 0 return out @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.float64_t euclidean_distance(np.float64_t[:] p, np.float64_t[:] q): cdef int j cdef np.float64_t d d = 0.0 for j in range(3): d+=(p[j]-q[j])**2 return sqrt(d) # Todo: allow radius reported independently in each dimension for rectangular domain @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.float64_t smallest_quadtree_box(np.float64_t p[3], np.float64_t q[3], np.int32_t order, np.float64_t DLE[3], np.float64_t DRE[3], np.float64_t *cx, np.float64_t *cy, np.float64_t *cz): cdef int j cdef np.float64_t c[3] cdef np.uint64_t pidx[3] # cdef np.uint64_t qidx[3] for j in range(3): pidx[j] = 0 # qidx[j] = 0 cdef np.uint64_t pidx_next[3] cdef np.uint64_t qidx_next[3] cdef np.float64_t dds[3] cdef np.float64_t rad cdef int lvl = 0 cdef int done = 0 while not done: if (lvl+1 >= order): done = 1 for j in range(3): dds[j] = (DRE[j] - DLE[j])/(1 << ( lvl+1)) pidx_next[j] = ((p[j] - DLE[j])/dds[j]) qidx_next[j] = ((q[j] - DLE[j])/dds[j]) for j in range(3): if pidx_next[j]!=qidx_next[j]: done = 1 break if not done: for j in range(3): pidx[j] = pidx_next[j] # qidx[j] = qidx_next[j] lvl+=1 rad = 0.0 for j in range(3): dds[j] = (DRE[j] - DLE[j])/(1 << lvl) c[j] = dds[j]*(pidx[j]+0.5) rad+=((dds[j]/2.0)**2) cx[0] = c[0] cy[0] = c[1] cz[0] = c[2] return sqrt(rad) #----------------------------------------------------------------------------- # 21 bits spread over 64 with 3 bits in between @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.uint64_t spread_64bits_by3(np.uint64_t x): x=(x&(0x00000000001FFFFF)) x=(x|(x<<20))*(0x000001FFC00003FF) #----------------------------------------------------------------------------- # 21 bits spread over 64 with 2 bits in between @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.uint64_t spread_64bits_by2(np.uint64_t x): # This magic comes from http://stackoverflow.com/questions/1024754/how-to-compute-a-3d-morton-number-interleave-the-bits-of-3-ints # Only reversible up to 2097151 # Select highest 21 bits (Required to be reversible to 21st bit) # x = ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---k jihg fedc ba98 7654 3210 x=(x&(0x00000000001FFFFF)) # x = ---- ---- ---- ---- ---- ---k jihg fedc ba-- ---- ---- ---- ---- --98 7654 3210 x=(x|(x<<20))&(0x000001FFC00003FF) # x = ---- ---- ---- -kji hgf- ---- ---- -edc ba-- ---- ---- 9876 5--- ---- ---4 3210 x=(x|(x<<10))&(0x0007E007C00F801F) # x = ---- ---- -kji h--- -gf- ---- -edc ---- ba-- ---- 987- ---6 5--- ---4 32-- --10 x=(x|(x<<4))&(0x00786070C0E181C3) # x = ---- ---k ji-- h--g --f- ---e d--c --b- -a-- --98 --7- -6-- 5--- -43- -2-- 1--0 x=(x|(x<<2))&(0x0199219243248649) # x = ---- -kj- -i-- h--g --f- -e-- d--c --b- -a-- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 x=(x|(x<<2))&(0x0649249249249249) # x = ---k --j- -i-- h--g --f- -e-- d--c --b- -a-- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 x=(x|(x<<2))&(0x1249249249249249) return x @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.uint64_t compact_64bits_by2(np.uint64_t x): # Reversed magic x=x&(0x1249249249249249) x=(x|(x>>2))&(0x0649249249249249) x=(x|(x>>2))&(0x0199219243248649) x=(x|(x>>2))&(0x00786070C0E181C3) x=(x|(x>>4))&(0x0007E007C00F801F) x=(x|(x>>10))&(0x000001FFC00003FF) x=(x|(x>>20))&(0x00000000001FFFFF) return x #----------------------------------------------------------------------------- # 10 bits spread over 32 with 2 bits in between @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.uint32_t spread_32bits_by2(np.uint32_t x): # Only reversible up to 1023 # Select highest 10 bits (Required to be reversible to 10st bit) # x = ---- ---- ---- ---- ---- --98 7654 3210 x=(x&(0x000003FF)) # x = ---- --98 ---- ---- ---- ---- 7654 3210 x=(x|(x<<16))&(0xFF0000FF) # x = ---- --98 ---- ---- 7654 ---- ---- 3210 x=(x|(x<<8))&(0x0300F00F) # x = ---- --98 ---- 76-- --54 ---- 32-- --10 x=(x|(x<<4))&(0x030C30C3) # x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 x=(x|(x<<2))&(0x09249249) return x @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.uint32_t compact_32bits_by2(np.uint32_t x): # Reversed magic x=x&(0x09249249) x=(x|(x>>2))&(0x030C30C3) x=(x|(x>>4))&(0x0300F00F) x=(x|(x>>8))&(0xFF0000FF) x=(x|(x>>16))&(0x000003FF) return x @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.uint64_t masked_merge_64bit(np.uint64_t a, np.uint64_t b, np.uint64_t mask): # https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge return a ^ ((a ^ b) & mask) @cython.cdivision(True) cdef inline np.uint64_t encode_morton_64bit(np.uint64_t x_ind, np.uint64_t y_ind, np.uint64_t z_ind): cdef np.uint64_t mi mi = 0 mi |= spread_64bits_by2(z_ind)<>XSHIFT) p[1] = compact_64bits_by2(mi>>YSHIFT) p[2] = compact_64bits_by2(mi>>ZSHIFT) @cython.cdivision(True) cdef inline np.uint64_t bounded_morton(np.float64_t x, np.float64_t y, np.float64_t z, np.float64_t *DLE, np.float64_t *DRE, np.int32_t order): cdef int i cdef np.float64_t dds[3] cdef np.uint64_t x_ind, y_ind, z_ind cdef np.uint64_t mi for i in range(3): dds[i] = (DRE[i] - DLE[i]) / (1 << order) x_ind = ((x - DLE[0])/dds[0]) y_ind = ((y - DLE[1])/dds[1]) z_ind = ((z - DLE[2])/dds[2]) mi = encode_morton_64bit(x_ind,y_ind,z_ind) return mi @cython.cdivision(True) cdef inline np.uint64_t bounded_morton_relative(np.float64_t x, np.float64_t y, np.float64_t z, np.float64_t *DLE, np.float64_t *DRE, np.int32_t order1, np.int32_t order2): cdef int i cdef np.float64_t dds1[3] cdef np.float64_t dds2[3] cdef np.float64_t DLE2[3] cdef np.uint64_t x_ind, y_ind, z_ind cdef np.uint64_t mi2 for i in range(3): dds1[i] = (DRE[i] - DLE[i]) / (1 << order1) dds2[i] = dds1[i] / (1 << order2) DLE2[0] = ( ((x - DLE[0])/dds1[0])) * dds1[0] DLE2[1] = ( ((y - DLE[1])/dds1[1])) * dds1[1] DLE2[2] = ( ((z - DLE[2])/dds1[2])) * dds1[2] x_ind = ((x - DLE2[0])/dds2[0]) y_ind = ((y - DLE2[1])/dds2[1]) z_ind = ((z - DLE2[2])/dds2[2]) mi2 = encode_morton_64bit(x_ind,y_ind,z_ind) return mi2 # This doesn't seem to be much, if at all, faster... @cython.cdivision(True) cdef inline np.uint64_t bounded_morton_dds(np.float64_t x, np.float64_t y, np.float64_t z, np.float64_t *DLE, np.float64_t *dds): cdef np.uint64_t x_ind, y_ind, z_ind cdef np.uint64_t mi x_ind = ((x - DLE[0])/dds[0]) y_ind = ((y - DLE[1])/dds[1]) z_ind = ((z - DLE[2])/dds[2]) mi = encode_morton_64bit(x_ind,y_ind,z_ind) return mi @cython.cdivision(True) cdef inline np.uint64_t bounded_morton_relative_dds(np.float64_t x, np.float64_t y, np.float64_t z, np.float64_t *DLE, np.float64_t *dds1, np.float64_t *dds2): cdef np.float64_t DLE2[3] cdef np.uint64_t x_ind, y_ind, z_ind cdef np.uint64_t mi2 DLE2[0] = ( ((x - DLE[0])/dds1[0])) * dds1[0] DLE2[1] = ( ((y - DLE[1])/dds1[1])) * dds1[1] DLE2[2] = ( ((z - DLE[2])/dds1[2])) * dds1[2] x_ind = ((x - DLE2[0])/dds2[0]) y_ind = ((y - DLE2[1])/dds2[1]) z_ind = ((z - DLE2[2])/dds2[2]) mi2 = encode_morton_64bit(x_ind,y_ind,z_ind) return mi2 @cython.cdivision(True) cdef inline np.uint64_t bounded_morton_split_dds(np.float64_t x, np.float64_t y, np.float64_t z, np.float64_t *DLE, np.float64_t *dds, np.uint64_t *p): cdef np.uint64_t mi p[0] = ((x - DLE[0])/dds[0]) p[1] = ((y - DLE[1])/dds[1]) p[2] = ((z - DLE[2])/dds[2]) mi = encode_morton_64bit(p[0], p[1], p[2]) return mi @cython.cdivision(True) cdef inline np.uint64_t bounded_morton_split_relative_dds(np.float64_t x, np.float64_t y, np.float64_t z, np.float64_t *DLE, np.float64_t *dds1, np.float64_t *dds2, np.uint64_t *p2): cdef np.float64_t DLE2[3] cdef np.uint64_t mi2 DLE2[0] = DLE[0] + ( ((x - DLE[0])/dds1[0])) * dds1[0] DLE2[1] = DLE[1] + ( ((y - DLE[1])/dds1[1])) * dds1[1] DLE2[2] = DLE[2] + ( ((z - DLE[2])/dds1[2])) * dds1[2] p2[0] = ((x - DLE2[0])/dds2[0]) p2[1] = ((y - DLE2[1])/dds2[1]) p2[2] = ((z - DLE2[2])/dds2[2]) mi2 = encode_morton_64bit(p2[0], p2[1], p2[2]) return mi2 cdef np.uint32_t morton_neighbors_coarse(np.uint64_t mi1, np.uint64_t max_index1, bint periodicity[3], np.uint32_t nn, np.uint32_t[:,:] index, np.uint64_t[:,:] ind1_n, np.uint64_t[:] neighbors) cdef np.uint32_t morton_neighbors_refined(np.uint64_t mi1, np.uint64_t mi2, np.uint64_t max_index1, np.uint64_t max_index2, bint periodicity[3], np.uint32_t nn, np.uint32_t[:,:] index, np.uint64_t[:,:] ind1_n, np.uint64_t[:,:] ind2_n, np.uint64_t[:] neighbors1, np.uint64_t[:] neighbors2) yt-project-yt-f043ac8/yt/utilities/lib/geometry_utils.pyx000066400000000000000000001413051510711153200237100ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG OMP_ARGS # distutils: extra_link_args = CPP14_FLAG OMP_ARGS """ Simple integrators for the radiative transfer equation """ import numpy as np cimport cython cimport numpy as np from libc.math cimport copysign, fabs, log2 from libc.stdlib cimport free, malloc from yt.utilities.lib.fp_utils cimport i64clip from yt.utilities.exceptions import YTDomainOverflow from yt.utilities.lib.vec3_ops cimport L2_norm, cross, dot, subtract cdef extern from "math.h": double exp(double x) noexcept nogil float expf(float x) noexcept nogil long double expl(long double x) noexcept nogil double floor(double x) noexcept nogil double ceil(double x) noexcept nogil double fmod(double x, double y) noexcept nogil double fabs(double x) noexcept nogil cdef extern from "platform_dep.h": long int lrint(double x) noexcept nogil @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t graycode(np.int64_t x): return x^(x>>1) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t igraycode(np.int64_t x): cdef np.int64_t i, j if x == 0: return x m = ceil(log2(x)) + 1 i, j = x, 1 while j < m: i = i ^ (x>>j) j += 1 return i @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t direction(np.int64_t x, np.int64_t n): #assert x < 2**n if x == 0: return 0 elif x%2 == 0: return tsb(x-1, n)%n else: return tsb(x, n)%n @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t tsb(np.int64_t x, np.int64_t width): #assert x < 2**width cdef np.int64_t i = 0 while x&1 and i <= width: x = x >> 1 i += 1 return i @cython.cpow(True) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t bitrange(np.int64_t x, np.int64_t width, np.int64_t start, np.int64_t end): return x >> (width-end) & ((2**(end-start))-1) @cython.cpow(True) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t rrot(np.int64_t x, np.int64_t i, np.int64_t width): i = i%width x = (x>>i) | (x<>width-i) return x&(2**width-1) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t transform(np.int64_t entry, np.int64_t direction, np.int64_t width, np.int64_t x): return rrot((x^entry), direction + 1, width) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t entry(np.int64_t x): if x == 0: return 0 return graycode(2*((x-1)/2)) @cython.cpow(True) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t setbit(np.int64_t x, np.int64_t w, np.int64_t i, np.int64_t b): if b == 1: return x | 2**(w-i-1) elif b == 0: return x & ~2**(w-i-1) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def spread_bits(np.uint64_t x): return spread_64bits_by2(x) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def compact_bits(np.uint64_t x): return compact_64bits_by2(x) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def lsz(np.uint64_t v, int stride = 1, int start = 0): cdef int c c = start while ((np.uint64(1) << np.uint64(c)) & np.uint64(v)): c += stride return c @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def lsb(np.uint64_t v, int stride = 1, int start = 0): cdef int c c = start while (np.uint64(v) << np.uint64(c)) and not ((np.uint64(1) << np.uint64(c)) & np.uint64(v)): c += stride return c @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def bitwise_addition(np.uint64_t x, np.int64_t y0, int stride = 1, int start = 0): if (y0 == 0): return x cdef int end, p, pstart cdef list mstr cdef np.uint64_t m, y, out y = np.uint64(np.abs(y0)) if (y0 > 0): func_ls = lsz else: func_ls = lsb # Continue until all bits added p = 0 out = x while (y >> p): if (y & (1 << p)): # Get end point pstart = start + p*stride end = func_ls(out,stride=stride,start=pstart) # Create mask mstr = (end + 1) * ['0'] for i in range(pstart,end+1,stride): mstr[i] = '1' m = int(''.join(mstr[::-1]), 2) # Invert portion in mask # print(mstr[::-1]) # print(y,p,(pstart,end+1),bin(m),bin(out),bin(~out)) out = masked_merge_64bit(out, ~out, m) # Move to next bit p += 1 return out @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t point_to_hilbert(int order, np.int64_t p[3]): cdef np.int64_t h, e, d, l, b, w, i, x h = e = d = 0 for i in range(order): l = 0 for x in range(3): b = bitrange(p[3-x-1], order, i, i+1) l |= (b<= INDEX_MAX_64: raise ValueError("Point exceeds max ({}) ".format(INDEX_MAX_64)+ "for 64bit interleave.") p[j] = left_index[j] morton_index = point_to_morton(p) return morton_index @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_indices(np.ndarray[np.uint64_t, ndim=2] left_index): cdef np.int64_t i cdef int j cdef np.ndarray[np.uint64_t, ndim=1] morton_indices cdef np.uint64_t p[3] morton_indices = np.zeros(left_index.shape[0], 'uint64') for i in range(left_index.shape[0]): for j in range(3): if left_index[i, j] >= INDEX_MAX_64: raise ValueError("Point exceeds max ({}) ".format(INDEX_MAX_64)+ "for 64bit interleave.") p[j] = left_index[i, j] morton_indices[i] = point_to_morton(p) return morton_indices @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_indices_unravel(np.ndarray[np.uint64_t, ndim=1] left_x, np.ndarray[np.uint64_t, ndim=1] left_y, np.ndarray[np.uint64_t, ndim=1] left_z): cdef np.int64_t i cdef np.ndarray[np.uint64_t, ndim=1] morton_indices cdef np.uint64_t p[3] morton_indices = np.zeros(left_x.shape[0], 'uint64') for i in range(left_x.shape[0]): p[0] = left_x[i] p[1] = left_y[i] p[2] = left_z[i] for j in range(3): if p[j] >= INDEX_MAX_64: raise ValueError("Point exceeds max ({}) ".format(INDEX_MAX_64)+ "for 64 bit interleave.") morton_indices[i] = point_to_morton(p) return morton_indices @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_point(np.uint64_t index): cdef int j cdef np.uint64_t p[3] cdef np.ndarray[np.uint64_t, ndim=1] position position = np.zeros(3, 'uint64') morton_to_point(index, p) for j in range(3): position[j] = p[j] return position @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_points(np.ndarray[np.uint64_t, ndim=1] indices): # This is inspired by the scurve package by user cortesi on GH. cdef int i, j cdef np.uint64_t p[3] cdef np.ndarray[np.uint64_t, ndim=2] positions positions = np.zeros((indices.shape[0], 3), 'uint64') for i in range(indices.shape[0]): morton_to_point(indices[i], p) for j in range(3): positions[i, j] = p[j] return positions @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_neighbors_coarse(mi1, max_index1, periodic, nn): cdef int i cdef np.uint32_t ntot cdef np.ndarray[np.uint32_t, ndim=2] index = np.zeros((2*nn+1,3), dtype='uint32') cdef np.ndarray[np.uint64_t, ndim=2] ind1_n = np.zeros((2*nn+1,3), dtype='uint64') cdef np.ndarray[np.uint64_t, ndim=1] neighbors = np.zeros((2*nn+1)**3, dtype='uint64') cdef bint periodicity[3] if periodic: for i in range(3): periodicity[i] = 1 else: for i in range(3): periodicity[i] = 0 ntot = morton_neighbors_coarse(mi1, max_index1, periodicity, nn, index, ind1_n, neighbors) return np.resize(neighbors, (ntot,)) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.uint32_t morton_neighbors_coarse(np.uint64_t mi1, np.uint64_t max_index1, bint periodicity[3], np.uint32_t nn, np.uint32_t[:,:] index, np.uint64_t[:,:] ind1_n, np.uint64_t[:] neighbors): cdef np.uint32_t ntot = 0 cdef np.uint64_t ind1[3] cdef np.uint32_t count[3] cdef np.uint32_t origin[3] cdef np.int64_t adv cdef int i, j, k, ii, ij, ik for i in range(3): count[i] = 0 origin[i] = 0 # Get indices decode_morton_64bit(mi1,ind1) # Determine which directions are valid for j,i in enumerate(range(-nn,(nn+1))): if i == 0: for k in range(3): ind1_n[j,k] = ind1[k] index[count[k],k] = j origin[k] = count[k] count[k] += 1 else: for k in range(3): adv = ((ind1[k]) + i) if (adv < 0): if periodicity[k]: while adv < 0: adv += max_index1 ind1_n[j,k] = (adv % max_index1) else: continue elif (adv >= max_index1): if periodicity[k]: ind1_n[j,k] = (adv % max_index1) else: continue else: ind1_n[j,k] = (adv) # print(i,k,adv,max_index1,ind1_n[j,k],adv % max_index1) index[count[k],k] = j count[k] += 1 # Iterate over ever combinations for ii in range(count[0]): i = index[ii,0] for ij in range(count[1]): j = index[ij,1] for ik in range(count[2]): k = index[ik,2] if (ii != origin[0]) or (ij != origin[1]) or (ik != origin[2]): neighbors[ntot] = encode_morton_64bit(ind1_n[i,0], ind1_n[j,1], ind1_n[k,2]) ntot += 1 return ntot @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_neighbors_refined(mi1, mi2, max_index1, max_index2, periodic, nn): cdef int i cdef np.uint32_t ntot cdef np.ndarray[np.uint32_t, ndim=2] index = np.zeros((2*nn+1,3), dtype='uint32') cdef np.ndarray[np.uint64_t, ndim=2] ind1_n = np.zeros((2*nn+1,3), dtype='uint64') cdef np.ndarray[np.uint64_t, ndim=2] ind2_n = np.zeros((2*nn+1,3), dtype='uint64') cdef np.ndarray[np.uint64_t, ndim=1] neighbors1 = np.zeros((2*nn+1)**3, dtype='uint64') cdef np.ndarray[np.uint64_t, ndim=1] neighbors2 = np.zeros((2*nn+1)**3, dtype='uint64') cdef bint periodicity[3] if periodic: for i in range(3): periodicity[i] = 1 else: for i in range(3): periodicity[i] = 0 ntot = morton_neighbors_refined(mi1, mi2, max_index1, max_index2, periodicity, nn, index, ind1_n, ind2_n, neighbors1, neighbors2) return np.resize(neighbors1, (ntot,)), np.resize(neighbors2, (ntot,)) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.uint32_t morton_neighbors_refined(np.uint64_t mi1, np.uint64_t mi2, np.uint64_t max_index1, np.uint64_t max_index2, bint periodicity[3], np.uint32_t nn, np.uint32_t[:,:] index, np.uint64_t[:,:] ind1_n, np.uint64_t[:,:] ind2_n, np.uint64_t[:] neighbors1, np.uint64_t[:] neighbors2): cdef np.uint32_t ntot = 0 cdef np.uint64_t ind1[3] cdef np.uint64_t ind2[3] cdef np.uint32_t count[3] cdef np.uint32_t origin[3] cdef np.int64_t adv, maj, rem, adv1 cdef int i, j, k, ii, ij, ik for i in range(3): count[i] = 0 origin[i] = 0 # Get indices decode_morton_64bit(mi1,ind1) decode_morton_64bit(mi2,ind2) # Determine which directions are valid for j,i in enumerate(range(-nn,(nn+1))): if i == 0: for k in range(3): ind1_n[j,k] = ind1[k] ind2_n[j,k] = ind2[k] index[count[k],k] = j origin[k] = count[k] count[k] += 1 else: for k in range(3): adv = (ind2[k] + i) maj = adv / (max_index2) rem = adv % (max_index2) if adv < 0: adv1 = (ind1[k] + (maj-1)) if adv1 < 0: if periodicity[k]: while adv1 < 0: adv1 += max_index1 ind1_n[j,k] = adv1 else: continue else: ind1_n[j,k] = adv1 while adv < 0: adv += max_index2 ind2_n[j,k] = adv elif adv >= max_index2: adv1 = (ind1[k] + maj) if adv1 >= max_index1: if periodicity[k]: ind1_n[j,k] = (adv1 % max_index1) else: continue else: ind1_n[j,k] = adv1 ind2_n[j,k] = rem else: ind1_n[j,k] = ind1[k] ind2_n[j,k] = (adv) index[count[k],k] = j count[k] += 1 # Iterate over ever combinations for ii in range(count[0]): i = index[ii,0] for ij in range(count[1]): j = index[ij,1] for ik in range(count[2]): k = index[ik,2] if (ii != origin[0]) or (ij != origin[1]) or (ik != origin[2]): neighbors1[ntot] = encode_morton_64bit(ind1_n[i,0], ind1_n[j,1], ind1_n[k,2]) neighbors2[ntot] = encode_morton_64bit(ind2_n[i,0], ind2_n[j,1], ind2_n[k,2]) ntot += 1 return ntot @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def morton_neighbor_periodic(np.ndarray[np.uint64_t,ndim=1] p, list dim_list, list num_list, np.uint64_t max_index): cdef np.uint64_t p1[3] cdef int j, dim, num for j in range(3): p1[j] = np.uint64(p[j]) for dim,num in zip(dim_list,num_list): p1[dim] = np.uint64((np.int64(p[dim]) + num) % max_index) return np.int64(point_to_morton(p1)) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def morton_neighbor_bounded(np.ndarray[np.uint64_t,ndim=1] p, list dim_list, list num_list, np.uint64_t max_index): cdef np.int64_t x cdef np.uint64_t p1[3] cdef int j, dim, num for j in range(3): p1[j] = np.uint64(p[j]) for dim,num in zip(dim_list,num_list): x = np.int64(p[dim]) + num if (x >= 0) and (x < max_index): p1[dim] = np.uint64(x) else: return np.int64(-1) return np.int64(point_to_morton(p1)) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def morton_neighbor(np.ndarray[np.uint64_t,ndim=1] p, list dim_list, list num_list, np.uint64_t max_index, periodic = False): if periodic: return morton_neighbor_periodic(p, dim_list, num_list, max_index) else: return morton_neighbor_bounded(p, dim_list, num_list, max_index) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_neighbors(np.ndarray[np.uint64_t,ndim=1] mi, int order = ORDER_MAX, periodic = False): """Returns array of neighboring morton indices""" # Declare cdef int i, j, k, l, n cdef np.uint64_t max_index cdef np.ndarray[np.uint64_t, ndim=2] p cdef np.int64_t nmi cdef np.ndarray[np.uint64_t, ndim=1] mi_neighbors p = get_morton_points(mi) mi_neighbors = np.zeros(26*mi.shape[0], 'uint64') n = 0 max_index = np.int64(1 << order) # Define function if periodic: fneighbor = morton_neighbor_periodic else: fneighbor = morton_neighbor_bounded for i in range(mi.shape[0]): for j in range(3): # +1 in dimension j nmi = fneighbor(p[i,:],[j],[+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # +/- in dimension k for k in range(j+1,3): # +1 in dimension k nmi = fneighbor(p[i,:],[j,k],[+1,+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # +/- in dimension l for l in range(k+1,3): nmi = fneighbor(p[i,:],[j,k,l],[+1,+1,+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 nmi = fneighbor(p[i,:],[j,k,l],[+1,+1,-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # -1 in dimension k nmi = fneighbor(p[i,:],[j,k],[+1,-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # +/- in dimension l for l in range(k+1,3): nmi = fneighbor(p[i,:],[j,k,l],[+1,-1,+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 nmi = fneighbor(p[i,:],[j,k,l],[+1,-1,-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # -1 in dimension j nmi = fneighbor(p[i,:],[j],[-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # +/- in dimension k for k in range(j+1,3): # +1 in dimension k nmi = fneighbor(p[i,:],[j,k],[-1,+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # +/- in dimension l for l in range(k+1,3): nmi = fneighbor(p[i,:],[j,k,l],[-1,+1,+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 nmi = fneighbor(p[i,:],[j,k,l],[-1,+1,-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # -1 in dimension k nmi = fneighbor(p[i,:],[j,k],[-1,-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 # +/- in dimension l for l in range(k+1,3): nmi = fneighbor(p[i,:],[j,k,l],[-1,-1,+1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 nmi = fneighbor(p[i,:],[j,k,l],[-1,-1,-1],max_index) if nmi > 0: mi_neighbors[n] = np.uint64(nmi) n+=1 mi_neighbors = np.resize(mi_neighbors,(n,)) return np.unique(np.hstack([mi,mi_neighbors])) def ifrexp_cy(np.float64_t x): cdef np.int64_t e, m m = ifrexp(x, &e) return m,e def msdb_cy(np.int64_t a, np.int64_t b): return msdb(a,b) def msdb_cy(np.int64_t a, np.int64_t b): return msdb(a,b) def xor_msb_cy(np.float64_t a, np.float64_t b): return xor_msb(a,b) def morton_qsort_swap(np.ndarray[np.uint64_t, ndim=1] ind, np.uint64_t a, np.uint64_t b): # http://www.geeksforgeeks.org/iterative-quick-sort/ cdef np.int64_t t = ind[a] ind[a] = ind[b] ind[b] = t def morton_qsort_partition(np.ndarray[cython.floating, ndim=2] pos, np.int64_t l, np.int64_t h, np.ndarray[np.uint64_t, ndim=1] ind, use_loop = False): # Initialize cdef int k cdef np.int64_t i, j cdef np.float64_t ppos[3] cdef np.float64_t ipos[3] cdef np.uint64_t done, pivot if use_loop: # http://www.geeksforgeeks.org/iterative-quick-sort/ # A bit slower # Set starting point & pivot i = (l - 1) for k in range(3): ppos[k] = pos[ind[h],k] # Loop over array moving ind for points smaller than pivot to front for j in range(l, h): for k in range(3): ipos[k] = pos[ind[j],k] if compare_floats_morton(ipos,ppos): i+=1 morton_qsort_swap(ind,i,j) # Swap the pivot to the midpoint in the partition i+=1 morton_qsort_swap(ind,i,h) return i else: # Set starting point & pivot i = l-1 j = h done = 0 pivot = ind[h] for k in range(3): ppos[k] = pos[pivot,k] # Loop until entire array processed while not done: # Process bottom while not done: i+=1 if i == j: done = 1 break for k in range(3): ipos[k] = pos[ind[i],k] if compare_floats_morton(ppos,ipos): ind[j] = ind[i] break # Process top while not done: j-=1 if j == i: done = 1 break for k in range(3): ipos[k] = pos[ind[j],k] if compare_floats_morton(ipos,ppos): ind[i] = ind[j] break ind[j] = pivot return j @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def morton_qsort_recursive(np.ndarray[cython.floating, ndim=2] pos, np.int64_t l, np.int64_t h, np.ndarray[np.uint64_t, ndim=1] ind, use_loop = False): # http://www.geeksforgeeks.org/iterative-quick-sort/ cdef np.int64_t p if (l < h): p = morton_qsort_partition(pos, l, h, ind, use_loop=use_loop) morton_qsort_recursive(pos, l, p-1, ind, use_loop=use_loop) morton_qsort_recursive(pos, p+1, h, ind, use_loop=use_loop) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def morton_qsort_iterative(np.ndarray[cython.floating, ndim=2] pos, np.int64_t l, np.int64_t h, np.ndarray[np.uint64_t, ndim=1] ind, use_loop = False): # http://www.geeksforgeeks.org/iterative-quick-sort/ # Auxiliary stack cdef np.ndarray[np.int64_t, ndim=1] stack = np.zeros(h-l+1, dtype=np.int64) cdef np.int64_t top = -1 cdef np.int64_t p top+=1 stack[top] = l top+=1 stack[top] = h # Pop from stack until it's empty while (top >= 0): # Get next set h = stack[top] top-=1 l = stack[top] top-=1 # Partition p = morton_qsort_partition(pos, l, h, ind, use_loop=use_loop) # Add left partition to the stack if (p-1) > l: top+=1 stack[top] = l top+=1 stack[top] = p - 1 # Add right partition to the stack if (p+1) < h: top+=1 stack[top] = p + 1 top+=1 stack[top] = h @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def morton_qsort(np.ndarray[cython.floating, ndim=2] pos, np.int64_t l, np.int64_t h, np.ndarray[np.uint64_t, ndim=1] ind, recursive = False, use_loop = False): #get_morton_argsort1(pos,l,h,ind) if recursive: morton_qsort_recursive(pos,l,h,ind,use_loop=use_loop) else: morton_qsort_iterative(pos,l,h,ind,use_loop=use_loop) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def get_morton_argsort1(np.ndarray[cython.floating, ndim=2] pos, np.int64_t start, np.int64_t end, np.ndarray[np.uint64_t, ndim=1] ind): # Return if only one position selected if start >= end: return # Initialize cdef np.int64_t top top = morton_qsort_partition(pos,start,end,ind) # Do remaining parts on either side of pivot, sort side first if (top-1-start < end-(top+1)): get_morton_argsort1(pos,start,top-1,ind) get_morton_argsort1(pos,top+1,end,ind) else: get_morton_argsort1(pos,top+1,end,ind) get_morton_argsort1(pos,start,top-1,ind) return def compare_morton(np.ndarray[cython.floating, ndim=1] p0, np.ndarray[cython.floating, ndim=1] q0): cdef np.float64_t p[3] cdef np.float64_t q[3] # cdef np.int64_t iep,ieq,imp,imq cdef int j for j in range(3): p[j] = p0[j] q[j] = q0[j] # imp = ifrexp(p[j],&iep) # imq = ifrexp(q[j],&ieq) # print(j,p[j],q[j],xor_msb(p[j],q[j]),'m=',imp,imq,'e=',iep,ieq) return compare_floats_morton(p,q) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef np.int64_t position_to_morton(np.ndarray[cython.floating, ndim=1] pos_x, np.ndarray[cython.floating, ndim=1] pos_y, np.ndarray[cython.floating, ndim=1] pos_z, np.float64_t dds[3], np.float64_t DLE[3], np.float64_t DRE[3], np.ndarray[np.uint64_t, ndim=1] ind, int filter): cdef np.uint64_t ii[3] cdef np.float64_t p[3] cdef np.int64_t i, j, use cdef np.uint64_t DD[3] cdef np.uint64_t FLAG = ~(0) for i in range(3): DD[i] = ((DRE[i] - DLE[i]) / dds[i]) for i in range(pos_x.shape[0]): use = 1 p[0] = pos_x[i] p[1] = pos_y[i] p[2] = pos_z[i] for j in range(3): if p[j] < DLE[j] or p[j] > DRE[j]: if filter == 1: # We only allow 20 levels, so this is inaccessible use = 0 break return i ii[j] = ((p[j] - DLE[j])/dds[j]) ii[j] = i64clip(ii[j], 0, DD[j] - 1) if use == 0: ind[i] = FLAG continue ind[i] = encode_morton_64bit(ii[0],ii[1],ii[2]) return pos_x.shape[0] def compute_morton(np.ndarray pos_x, np.ndarray pos_y, np.ndarray pos_z, domain_left_edge, domain_right_edge, filter_bbox = False, order = ORDER_MAX): cdef int i cdef int filter if filter_bbox: filter = 1 else: filter = 0 cdef np.float64_t dds[3] cdef np.float64_t DLE[3] cdef np.float64_t DRE[3] for i in range(3): DLE[i] = domain_left_edge[i] DRE[i] = domain_right_edge[i] dds[i] = (DRE[i] - DLE[i]) / (1 << order) cdef np.ndarray[np.uint64_t, ndim=1] ind ind = np.zeros(pos_x.shape[0], dtype="uint64") cdef np.int64_t rv if pos_x.dtype == np.float32: rv = position_to_morton[np.float32_t]( pos_x, pos_y, pos_z, dds, DLE, DRE, ind, filter) elif pos_x.dtype == np.float64: rv = position_to_morton[np.float64_t]( pos_x, pos_y, pos_z, dds, DLE, DRE, ind, filter) else: print("Could not identify dtype.", pos_x.dtype) raise NotImplementedError if rv < pos_x.shape[0]: mis = (pos_x.min(), pos_y.min(), pos_z.min()) mas = (pos_x.max(), pos_y.max(), pos_z.max()) raise YTDomainOverflow(mis, mas, domain_left_edge, domain_right_edge) return ind @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def dist(np.ndarray[np.float64_t, ndim=1] p0, np.ndarray[np.float64_t, ndim=1] q0): cdef int j cdef np.float64_t p[3] cdef np.float64_t q[3] for j in range(3): p[j] = p0[j] q[j] = q0[j] return euclidean_distance(p,q) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def dist_to_box(np.ndarray[np.float64_t, ndim=1] p, np.ndarray[np.float64_t, ndim=1] cbox, np.float64_t rbox): cdef int j cdef np.float64_t d = 0.0 for j in range(3): d+= max((cbox[j]-rbox)-p[j],0.0,p[j]-(cbox[j]+rbox))**2 return np.sqrt(d) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def solution_radius(np.ndarray[np.float64_t, ndim=2] P, int k, np.uint64_t i, np.ndarray[np.uint64_t, ndim=1] idx, int order, np.ndarray[np.float64_t, ndim=1] DLE, np.ndarray[np.float64_t, ndim=1] DRE): c = np.zeros(3, dtype=np.float64) return quadtree_box(P[i,:],P[idx[k-1],:],order,DLE,DRE,c) @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def knn_direct(np.ndarray[np.float64_t, ndim=2] P, np.uint64_t k, np.uint64_t i, np.ndarray[np.uint64_t, ndim=1] idx, return_dist = False, return_rad = False): """Directly compute the k nearest neighbors by sorting on distance. Args: P (np.ndarray): (N,d) array of points to search sorted by Morton order. k (int): number of nearest neighbors to find. i (int): index of point that nearest neighbors should be found for. idx (np.ndarray): indices of points from P to be considered. return_dist (Optional[bool]): If True, distances to the k nearest neighbors are also returned (in order of proximity). (default = False) return_rad (Optional[bool]): If True, distance to farthest nearest neighbor is also returned. This is set to False if return_dist is True. (default = False) Returns: np.ndarray: Indices of k nearest neighbors to point i. """ cdef int j,m cdef np.int64_t[:] sort_fwd cdef np.float64_t[:] ipos cdef np.float64_t[:] jpos cdef np.float64_t[:] dist = np.zeros(len(idx), dtype='float64') ipos = np.zeros(3) jpos = np.zeros(3) for m in range(3): ipos[m] = P[i,m] for j in range(len(idx)): for m in range(3): jpos[m] = P[idx[j],m] dist[j] = euclidean_distance(ipos, jpos) # casting to uint64 for compatibility with 32 bits systems # see https://github.com/yt-project/yt/issues/3656 sort_fwd = np.argsort(dist, kind='mergesort')[:k].astype(np.int64, copy=False) if return_dist: return np.array(idx)[sort_fwd], np.array(dist)[sort_fwd] elif return_rad: return np.array(idx)[sort_fwd], np.array(dist)[sort_fwd][k-1] else: return np.array(idx)[sort_fwd] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def quadtree_box(np.ndarray[np.float64_t, ndim=1] p, np.ndarray[np.float64_t, ndim=1] q, int order, np.ndarray[np.float64_t, ndim=1] DLE, np.ndarray[np.float64_t, ndim=1] DRE, np.ndarray[np.float64_t, ndim=1] c): # Declare & transfer values to ctypes cdef int j cdef np.float64_t ppos[3] cdef np.float64_t qpos[3] cdef np.float64_t rbox cdef np.float64_t cbox[3] cdef np.float64_t DLE1[3] cdef np.float64_t DRE1[3] for j in range(3): ppos[j] = p[j] qpos[j] = q[j] DLE1[j] = DLE[j] DRE1[j] = DRE[j] # Get smallest box containing p & q rbox = smallest_quadtree_box(ppos,qpos,order,DLE1,DRE1, &cbox[0],&cbox[1],&cbox[2]) # Transfer values to python array for j in range(3): c[j] = cbox[j] return rbox @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def csearch_morton(np.ndarray[np.float64_t, ndim=2] P, int k, np.uint64_t i, np.ndarray[np.uint64_t, ndim=1] Ai, np.uint64_t l, np.uint64_t h, int order, np.ndarray[np.float64_t, ndim=1] DLE, np.ndarray[np.float64_t, ndim=1] DRE, int nu = 4): """Expand search concentrically to determine set of k nearest neighbors for point i. Args: P (np.ndarray): (N,d) array of points to search sorted by Morton order. k (int): number of nearest neighbors to find. i (int): index of point that nearest neighbors should be found for. Ai (np.ndarray): (N,k) array of partial nearest neighbor indices. l (int): index of lowest point to consider in addition to Ai. h (int): index of highest point to consider in addition to Ai. order (int): Maximum depth that Morton order quadtree should reach. DLE (np.float64[3]): 3 floats defining domain lower bounds in each dim. DRE (np.float64[3]): 3 floats defining domain upper bounds in each dim. nu (int): minimum number of points before a direct knn search is performed. (default = 4) Returns: np.ndarray: (N,k) array of nearest neighbor indices. Raises: ValueError: If l i): raise ValueError("Both l and h must be on the same side of i.") m = np.uint64((h + l)/2) # New range is small enough to consider directly if (h-l) < nu: if m > i: return knn_direct(P,k,i,np.hstack((Ai,np.arange(l,h+1,dtype=np.uint64)))) else: return knn_direct(P,k,i,np.hstack((np.arange(l,h+1,dtype=np.uint64),Ai))) # Add middle point if m > i: Ai,rad_Ai = knn_direct(P,k,i,np.hstack((Ai,m)).astype(np.uint64),return_rad=True) else: Ai,rad_Ai = knn_direct(P,k,i,np.hstack((m,Ai)).astype(np.uint64),return_rad=True) cbox_sol = np.zeros(3,dtype=np.float64) rbox_sol = quadtree_box(P[i,:],P[Ai[k-1],:],order,DLE,DRE,cbox_sol) # Return current solution if hl box is outside current solution's box # Uses actual box cbox_hl = np.zeros(3,dtype=np.float64) rbox_hl = quadtree_box(P[l,:],P[h,:],order,DLE,DRE,cbox_hl) if dist_to_box(cbox_sol,cbox_hl,rbox_hl) >= 1.5*rbox_sol: print('{} outside: rad = {}, rbox = {}, dist = {}'.format(m,rad_Ai,rbox_sol,dist_to_box(P[i,:],cbox_hl,rbox_hl))) return Ai # Expand search to lower/higher indices as needed if i < m: # They are already sorted... Ai = csearch_morton(P,k,i,Ai,l,m-1,order,DLE,DRE,nu=nu) if compare_morton(P[m,:],P[i,:]+dist(P[i,:],P[Ai[k-1],:])): Ai = csearch_morton(P,k,i,Ai,m+1,h,order,DLE,DRE,nu=nu) else: Ai = csearch_morton(P,k,i,Ai,m+1,h,order,DLE,DRE,nu=nu) if compare_morton(P[i,:]-dist(P[i,:],P[Ai[k-1],:]),P[m,:]): Ai = csearch_morton(P,k,i,Ai,l,m-1,order,DLE,DRE,nu=nu) return Ai @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def knn_morton(np.ndarray[np.float64_t, ndim=2] P0, int k, np.uint64_t i0, float c = 1.0, int nu = 4, issorted = False, int order = ORDER_MAX, np.ndarray[np.float64_t, ndim=1] DLE = np.zeros(3,dtype=np.float64), np.ndarray[np.float64_t, ndim=1] DRE = np.zeros(3,dtype=np.float64)): """Get the indices of the k nearest neighbors to point i. Args: P (np.ndarray): (N,d) array of points to search. k (int): number of nearest neighbors to find for each point in P. i (np.uint64): index of point to find neighbors for. c (float): factor determining how many indices before/after i are used in the initial search (i-c*k to i+c*k, default = 1.0) nu (int): minimum number of points before a direct knn search is performed. (default = 4) issorted (Optional[bool]): if True, P is assumed to be sorted already according to Morton order. order (int): Maximum depth that Morton order quadtree should reach. If not provided, ORDER_MAX is used. DLE (np.ndarray): (d,) array of domain lower bounds in each dimension. If not provided, this is determined from the points. DRE (np.ndarray): (d,) array of domain upper bounds in each dimension. If not provided, this is determined from the points. Returns: np.ndarray: (N,k) indices of k nearest neighbors for each point in P. """ cdef int j cdef np.uint64_t i cdef np.int64_t N = P0.shape[0] cdef np.ndarray[np.float64_t, ndim=2] P cdef np.ndarray[np.uint64_t, ndim=1] sort_fwd = np.arange(N,dtype=np.uint64) cdef np.ndarray[np.uint64_t, ndim=1] sort_rev = np.arange(N,dtype=np.uint64) cdef np.ndarray[np.uint64_t, ndim=1] Ai cdef np.int64_t idxmin, idxmax, u, l cdef np.uint64_t I # Sort if necessary if issorted: P = P0 i = i0 else: morton_qsort(P0,0,N-1,sort_fwd) sort_rev = np.argsort(sort_fwd).astype(np.uint64) P = P0[sort_fwd,:] i = sort_rev[i0] # Check domain and set if singular for j in range(3): if DLE[j] == DRE[j]: DLE[j] = min(P[:,j]) DRE[j] = max(P[:,j]) # Get initial guess bassed on position in z-order idxmin = max(i-c*k, 0) idxmax = min(i+c*k, N-1) Ai = np.hstack((np.arange(idxmin,i,dtype=np.uint64), np.arange(i+1,idxmax+1,dtype=np.uint64))) Ai,rad_Ai = knn_direct(P,k,i,Ai,return_rad=True) # Get radius of solution cbox_Ai = np.zeros(3,dtype=np.float64) rbox_Ai = quadtree_box(P[i,:],P[Ai[k-1],:],order,DLE,DRE,cbox_Ai) rad_Ai = rbox_Ai # Extend upper bound to match lower bound if idxmax < (N-1): if compare_morton(P[i,:]+rad_Ai,P[idxmax,:]): u = i else: I = 1 while (idxmax+(2**I) < N) and compare_morton(P[idxmax+(2**I),:],P[i,:]+rad_Ai): I+=1 u = min(idxmax+(2**I),N-1) Ai = csearch_morton(P,k,i,Ai,min(idxmax+1,N-1),u,order,DLE,DRE,nu=nu) else: u = idxmax # Extend lower bound to match upper bound if idxmin > 0: if compare_morton(P[idxmin,:],P[i,:]-rad_Ai): l = i else: I = 1 while (idxmin-(2**I) >= 0) and compare_morton(P[i,:]-rad_Ai,P[idxmin-(2**I),:]): I+=1 l = max(idxmin-(2**I),0) Ai = csearch_morton(P,k,i,Ai,l,max(idxmin-1,0),order,DLE,DRE,nu=nu) else: l = idxmin # Return indices of neighbors in the correct order if issorted: return Ai else: return sort_fwd[Ai] cdef struct PointSet cdef struct PointSet: int count # First index is point index, second is xyz np.float64_t points[2][3] PointSet *next cdef inline void get_intersection(np.float64_t p0[3], np.float64_t p1[3], int ax, np.float64_t coord, PointSet *p): cdef np.float64_t vec[3] cdef np.float64_t t for j in range(3): vec[j] = p1[j] - p0[j] if vec[ax] == 0.0: return # bail if the line is in the plane t = (coord - p0[ax])/vec[ax] # We know that if they're on opposite sides, it has to intersect. And we # won't get called otherwise. for j in range(3): p.points[p.count][j] = p0[j] + vec[j] * t p.count += 1 @cython.cdivision(True) def triangle_plane_intersect(int ax, np.float64_t coord, np.ndarray[np.float64_t, ndim=3] triangles): cdef np.float64_t p0[3] cdef np.float64_t p1[3] cdef np.float64_t p2[3] cdef np.float64_t E0[3] cdef np.float64_t E1[3] cdef np.float64_t tri_norm[3] cdef np.float64_t plane_norm[3] cdef np.float64_t dp cdef int i, j, k, count, ntri, nlines nlines = 0 ntri = triangles.shape[0] cdef PointSet *first cdef PointSet *last cdef PointSet *points first = last = points = NULL for i in range(ntri): count = 0 # Here p0 holds the triangle's zeroth node coordinates, # p1 holds the first node's coordinates, and # p2 holds the second node's coordinates for j in range(3): p0[j] = triangles[i, 0, j] p1[j] = triangles[i, 1, j] p2[j] = triangles[i, 2, j] plane_norm[j] = 0.0 plane_norm[ax] = 1.0 subtract(p1, p0, E0) subtract(p2, p0, E1) cross(E0, E1, tri_norm) dp = dot(tri_norm, plane_norm) dp /= L2_norm(tri_norm) # Skip if triangle is close to being parallel to plane. if (fabs(dp) > 0.995): continue # Now for each line segment (01, 12, 20) we check to see how many cross # the coordinate of the slice. # Here, the components of p2 are either +1 or -1 depending on whether the # node's coordinate corresponding to the slice axis is greater than the # coordinate of the slice. p2[0] -> node 0; p2[1] -> node 1; p2[2] -> node2 for j in range(3): # Add 0 so that any -0s become +0s. Necessary for consistent determination # of plane intersection p2[j] = copysign(1.0, triangles[i, j, ax] - coord + 0) if p2[0] * p2[1] < 0: count += 1 if p2[1] * p2[2] < 0: count += 1 if p2[2] * p2[0] < 0: count += 1 if count == 2: nlines += 1 elif count == 3: raise RuntimeError("It should be geometrically impossible for a plane to" "to intersect all three legs of a triangle. Please contact" "yt developers with your mesh") else: continue points = malloc(sizeof(PointSet)) points.count = 0 points.next = NULL # Here p0 and p1 again hold node coordinates if p2[0] * p2[1] < 0: # intersection of 01 triangle segment with plane for j in range(3): p0[j] = triangles[i, 0, j] p1[j] = triangles[i, 1, j] get_intersection(p0, p1, ax, coord, points) if p2[1] * p2[2] < 0: # intersection of 12 triangle segment with plane for j in range(3): p0[j] = triangles[i, 1, j] p1[j] = triangles[i, 2, j] get_intersection(p0, p1, ax, coord, points) if p2[2] * p2[0] < 0: # intersection of 20 triangle segment with plane for j in range(3): p0[j] = triangles[i, 2, j] p1[j] = triangles[i, 0, j] get_intersection(p0, p1, ax, coord, points) if last != NULL: last.next = points if first == NULL: first = points last = points points = first cdef np.ndarray[np.float64_t, ndim=3] line_segments line_segments = np.empty((nlines, 2, 3), dtype="float64") k = 0 while points != NULL: for j in range(3): line_segments[k, 0, j] = points.points[0][j] line_segments[k, 1, j] = points.points[1][j] k += 1 last = points points = points.next free(last) return line_segments yt-project-yt-f043ac8/yt/utilities/lib/grid_traversal.pxd000066400000000000000000000047451510711153200236260ustar00rootroot00000000000000""" Definitions for the traversal code """ import numpy as np cimport cython cimport numpy as np from .image_samplers cimport ImageSampler from .volume_container cimport VolumeContainer, vc_index, vc_pos_index ctypedef void sampler_function( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil #----------------------------------------------------------------------------- # walk_volume(VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], sampler_function *sample, # void *data, np.float64_t *return_t = NULL, np.float64_t max_t = 1.0) # vc VolumeContainer* : Pointer to the volume container to be traversed. # v_pos np.float64_t[3] : The x,y,z coordinates of the ray's origin. # v_dir np.float64_t[3] : The x,y,z coordinates of the ray's direction. # sample sampler_function* : Pointer to the sampler function to be used. # return_t np.float64_t* : Pointer to the final value of t that is still inside the volume container. Defaulted to NULL. # max_t np.float64_t : The maximum value of t that the ray is allowed to travel. Defaulted to 1.0 (no restriction). # # Note: 't' is not time here. Rather, it is a factor representing the difference between the initial point 'v_pos' # and the end point, which we might call v_end. It is scaled such that v_pos + v * t = v_pos at t = 0.0, and # v_end at t = 1.0. Therefore, if max_t is set to 1.0, there is no restriction on t. # # Written by the yt Development Team. # Encapsulates the Amanatides & Woo "Fast Traversal Voxel Algorithm" to walk over a volume container 'vc' # The function occurs in two phases, initialization and traversal. # See: https://www.researchgate.net/publication/2611491_A_Fast_Voxel_Traversal_Algorithm_for_Ray_Tracing # Returns: The number of voxels hit during the traversal phase. If the traversal phase is not reached, returns 0. #----------------------------------------------------------------------------- cdef int walk_volume(VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], sampler_function *sampler, void *data, np.float64_t *return_t = *, np.float64_t max_t = *) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/grid_traversal.pyx000066400000000000000000000306151510711153200236460ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS # distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG # distutils: extra_link_args = CPP14_FLAG """ Simple integrators for the radiative transfer equation """ import numpy as np cimport cython cimport numpy as np from libc.math cimport atan2, cos, fabs, floor, sin, sqrt from yt.utilities.lib.fp_utils cimport fmin @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int walk_volume(VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], sampler_function *sample, void *data, np.float64_t *return_t = NULL, np.float64_t max_t = 1.0) noexcept nogil: cdef int cur_ind[3] cdef int step[3] cdef int x, y, i, hit, direction cdef np.float64_t intersect_t = 1.1 cdef np.float64_t iv_dir[3] cdef np.float64_t tmax[3] cdef np.float64_t tdelta[3] cdef np.float64_t exit_t = -1.0, enter_t = -1.0 cdef np.float64_t tl, temp_x, temp_y = -1 if max_t > 1.0: max_t = 1.0 direction = -1 if vc.left_edge[0] <= v_pos[0] and v_pos[0] < vc.right_edge[0] and \ vc.left_edge[1] <= v_pos[1] and v_pos[1] < vc.right_edge[1] and \ vc.left_edge[2] <= v_pos[2] and v_pos[2] < vc.right_edge[2]: intersect_t = 0.0 direction = 3 for i in range(3): if (v_dir[i] < 0): step[i] = -1 elif (v_dir[i] == 0.0): step[i] = 0 continue else: step[i] = 1 iv_dir[i] = 1.0/v_dir[i] if direction == 3: continue x = (i+1) % 3 y = (i+2) % 3 if step[i] > 0: tl = (vc.left_edge[i] - v_pos[i])*iv_dir[i] else: tl = (vc.right_edge[i] - v_pos[i])*iv_dir[i] temp_x = (v_pos[x] + tl*v_dir[x]) temp_y = (v_pos[y] + tl*v_dir[y]) if fabs(temp_x - vc.left_edge[x]) < 1e-10*vc.dds[x]: temp_x = vc.left_edge[x] elif fabs(temp_x - vc.right_edge[x]) < 1e-10*vc.dds[x]: temp_x = vc.right_edge[x] if fabs(temp_y - vc.left_edge[y]) < 1e-10*vc.dds[y]: temp_y = vc.left_edge[y] elif fabs(temp_y - vc.right_edge[y]) < 1e-10*vc.dds[y]: temp_y = vc.right_edge[y] if vc.left_edge[x] <= temp_x and temp_x <= vc.right_edge[x] and \ vc.left_edge[y] <= temp_y and temp_y <= vc.right_edge[y] and \ 0.0 <= tl and tl < intersect_t: direction = i intersect_t = tl if enter_t >= 0.0: intersect_t = enter_t if not ((0.0 <= intersect_t) and (intersect_t < max_t)): return 0 for i in range(3): # Two things have to be set inside this loop. # cur_ind[i], the current index of the grid cell the ray is in # tmax[i], the 't' until it crosses out of the grid cell tdelta[i] = step[i] * iv_dir[i] * vc.dds[i] if i == direction and step[i] > 0: # Intersection with the left face in this direction cur_ind[i] = 0 elif i == direction and step[i] < 0: # Intersection with the right face in this direction cur_ind[i] = vc.dims[i] - 1 else: # We are somewhere in the middle of the face temp_x = intersect_t * v_dir[i] + v_pos[i] # current position temp_y = ((temp_x - vc.left_edge[i])*vc.idds[i]) # There are some really tough cases where we just within a couple # least significant places of the edge, and this helps prevent # killing the calculation through a segfault in those cases. if -1 < temp_y < 0 and step[i] > 0: temp_y = 0.0 elif vc.dims[i] - 1 < temp_y < vc.dims[i] and step[i] < 0: temp_y = vc.dims[i] - 1 cur_ind[i] = (floor(temp_y)) if step[i] > 0: temp_y = (cur_ind[i] + 1) * vc.dds[i] + vc.left_edge[i] elif step[i] < 0: temp_y = cur_ind[i] * vc.dds[i] + vc.left_edge[i] tmax[i] = (temp_y - v_pos[i]) * iv_dir[i] if step[i] == 0: tmax[i] = 1e60 # We have to jumpstart our calculation for i in range(3): if cur_ind[i] == vc.dims[i] and step[i] >= 0: return 0 if cur_ind[i] == -1 and step[i] <= -1: return 0 enter_t = intersect_t hit = 0 while 1: hit += 1 if tmax[0] < tmax[1]: if tmax[0] < tmax[2]: i = 0 else: i = 2 else: if tmax[1] < tmax[2]: i = 1 else: i = 2 exit_t = fmin(tmax[i], max_t) sample(vc, v_pos, v_dir, enter_t, exit_t, cur_ind, data) cur_ind[i] += step[i] enter_t = tmax[i] tmax[i] += tdelta[i] if cur_ind[i] < 0 or cur_ind[i] >= vc.dims[i] or enter_t >= max_t: break if return_t != NULL: return_t[0] = exit_t return hit def hp_pix2vec_nest(long nside, long ipix): raise NotImplementedError # cdef double v[3] # healpix_interface.pix2vec_nest(nside, ipix, v) # cdef np.ndarray[np.float64_t, ndim=1] tr = np.empty((3,), dtype='float64') # tr[0] = v[0] # tr[1] = v[1] # tr[2] = v[2] # return tr def arr_pix2vec_nest(long nside, np.ndarray[np.int64_t, ndim=1] aipix): raise NotImplementedError # cdef int n = aipix.shape[0] # cdef int i # cdef double v[3] # cdef long ipix # cdef np.ndarray[np.float64_t, ndim=2] tr = np.zeros((n, 3), dtype='float64') # for i in range(n): # ipix = aipix[i] # healpix_interface.pix2vec_nest(nside, ipix, v) # tr[i,0] = v[0] # tr[i,1] = v[1] # tr[i,2] = v[2] # return tr def hp_vec2pix_nest(long nside, double x, double y, double z): raise NotImplementedError # cdef double v[3] # v[0] = x # v[1] = y # v[2] = z # cdef long ipix # healpix_interface.vec2pix_nest(nside, v, &ipix) # return ipix def arr_vec2pix_nest(long nside, np.ndarray[np.float64_t, ndim=1] x, np.ndarray[np.float64_t, ndim=1] y, np.ndarray[np.float64_t, ndim=1] z): raise NotImplementedError # cdef int n = x.shape[0] # cdef int i # cdef double v[3] # cdef long ipix # cdef np.ndarray[np.int64_t, ndim=1] tr = np.zeros(n, dtype='int64') # for i in range(n): # v[0] = x[i] # v[1] = y[i] # v[2] = z[i] # healpix_interface.vec2pix_nest(nside, v, &ipix) # tr[i] = ipix # return tr def hp_pix2ang_nest(long nside, long ipnest): raise NotImplementedError # cdef double theta, phi # healpix_interface.pix2ang_nest(nside, ipnest, &theta, &phi) # return (theta, phi) def arr_pix2ang_nest(long nside, np.ndarray[np.int64_t, ndim=1] aipnest): raise NotImplementedError # cdef int n = aipnest.shape[0] # cdef int i # cdef long ipnest # cdef np.ndarray[np.float64_t, ndim=2] tr = np.zeros((n, 2), dtype='float64') # cdef double theta, phi # for i in range(n): # ipnest = aipnest[i] # healpix_interface.pix2ang_nest(nside, ipnest, &theta, &phi) # tr[i,0] = theta # tr[i,1] = phi # return tr def hp_ang2pix_nest(long nside, double theta, double phi): raise NotImplementedError # cdef long ipix # healpix_interface.ang2pix_nest(nside, theta, phi, &ipix) # return ipix def arr_ang2pix_nest(long nside, np.ndarray[np.float64_t, ndim=1] atheta, np.ndarray[np.float64_t, ndim=1] aphi): raise NotImplementedError # cdef int n = atheta.shape[0] # cdef int i # cdef long ipnest # cdef np.ndarray[np.int64_t, ndim=1] tr = np.zeros(n, dtype='int64') # cdef double theta, phi # for i in range(n): # theta = atheta[i] # phi = aphi[i] # healpix_interface.ang2pix_nest(nside, theta, phi, &ipnest) # tr[i] = ipnest # return tr @cython.boundscheck(False) @cython.cdivision(False) @cython.wraparound(False) def pixelize_healpix(long nside, np.ndarray[np.float64_t, ndim=1] values, long ntheta, long nphi, np.ndarray[np.float64_t, ndim=2] irotation): raise NotImplementedError # # We will first to pix2vec, rotate, then calculate the angle # cdef int i, j, thetai, phii # cdef long ipix # cdef double v0[3], v1[3] # cdef double pi = 3.1415926 # cdef np.float64_t pi2 = pi/2.0 # cdef np.float64_t phi, theta # cdef np.ndarray[np.float64_t, ndim=2] results # cdef np.ndarray[np.int32_t, ndim=2] count # results = np.zeros((ntheta, nphi), dtype="float64") # count = np.zeros((ntheta, nphi), dtype="int32") # cdef np.float64_t phi0 = 0 # cdef np.float64_t dphi = 2.0 * pi/(nphi-1) # cdef np.float64_t theta0 = 0 # cdef np.float64_t dtheta = pi/(ntheta-1) # # We assume these are the rotated theta and phi # for thetai in range(ntheta): # theta = theta0 + dtheta * thetai # for phii in range(nphi): # phi = phi0 + dphi * phii # # We have our rotated vector # v1[0] = cos(phi) * sin(theta) # v1[1] = sin(phi) * sin(theta) # v1[2] = cos(theta) # # Now we rotate back # for i in range(3): # v0[i] = 0 # for j in range(3): # v0[i] += v1[j] * irotation[j,i] # # Get the pixel this vector is inside # healpix_interface.vec2pix_nest(nside, v0, &ipix) # results[thetai, phii] = values[ipix] # count[i, j] += 1 # return results, count def healpix_aitoff_proj(np.ndarray[np.float64_t, ndim=1] pix_image, long nside, np.ndarray[np.float64_t, ndim=2] image, np.ndarray[np.float64_t, ndim=2] irotation): raise NotImplementedError # cdef double pi = np.pi # cdef int i, j, k, l # cdef np.float64_t x, y, z, zb # cdef np.float64_t dx, dy, inside # cdef double v0[3], v1[3] # dx = 2.0 / (image.shape[1] - 1) # dy = 2.0 / (image.shape[0] - 1) # cdef np.float64_t s2 = sqrt(2.0) # cdef long ipix # for i in range(image.shape[1]): # x = (-1.0 + i*dx)*s2*2.0 # for j in range(image.shape[0]): # y = (-1.0 + j * dy)*s2 # zb = (x*x/8.0 + y*y/2.0 - 1.0) # if zb > 0: continue # z = (1.0 - (x/4.0)**2.0 - (y/2.0)**2.0) # z = sqrt(z) # # Longitude # phi = (2.0*atan(z*x/(2.0 * (2.0*z*z-1.0))) + pi) # # Latitude # # We shift it into co-latitude # theta = (asin(z*y) + pi/2.0) # # Now to account for rotation we translate into vectors # v1[0] = cos(phi) * sin(theta) # v1[1] = sin(phi) * sin(theta) # v1[2] = cos(theta) # for k in range(3): # v0[k] = 0 # for l in range(3): # v0[k] += v1[l] * irotation[l,k] # healpix_interface.vec2pix_nest(nside, v0, &ipix) # #print("Rotated", v0[0], v0[1], v0[2], v1[0], v1[1], v1[2], ipix, pix_image[ipix]) # image[j, i] = pix_image[ipix] def arr_fisheye_vectors(int resolution, np.float64_t fov, int nimx=1, int nimy=1, int nimi=0, int nimj=0, np.float64_t off_theta=0.0, np.float64_t off_phi=0.0): # We now follow figures 4-7 of: # http://paulbourke.net/miscellaneous/domefisheye/fisheye/ # ...but all in Cython. cdef np.ndarray[np.float64_t, ndim=3] vp cdef int i, j cdef np.float64_t r, phi, theta, px, py cdef np.float64_t fov_rad = fov * np.pi / 180.0 cdef int nx = resolution//nimx cdef int ny = resolution//nimy vp = np.zeros((nx,ny, 3), dtype="float64") for i in range(nx): px = (2.0 * (nimi*nx + i)) / resolution - 1.0 for j in range(ny): py = (2.0 * (nimj*ny + j)) / resolution - 1.0 r = sqrt(px*px + py*py) if r > 1.01: vp[i,j,0] = vp[i,j,1] = vp[i,j,2] = 0.0 continue phi = atan2(py, px) theta = r * fov_rad / 2.0 theta += off_theta phi += off_phi vp[i,j,0] = sin(theta) * cos(phi) vp[i,j,1] = sin(theta) * sin(phi) vp[i,j,2] = cos(theta) return vp yt-project-yt-f043ac8/yt/utilities/lib/healpix_interface.pxd000066400000000000000000000007071510711153200242620ustar00rootroot00000000000000""" A light interface to a few HEALPix routines """ import numpy as np cimport cython cimport numpy as np from libc.stdio cimport FILE, fclose, fopen cdef extern from "healpix_vectors.h": int pix2vec_nest(long nside, long ipix, double *v) void vec2pix_nest(long nside, double *vec, long *ipix) void pix2ang_nest(long nside, long ipix, double *theta, double *phi) void ang2pix_nest(long nside, double theta, double phi, long *ipix) yt-project-yt-f043ac8/yt/utilities/lib/image_samplers.pxd000066400000000000000000000050551510711153200236010ustar00rootroot00000000000000""" Definitions for image samplers """ import numpy as np cimport cython cimport numpy as np from .partitioned_grid cimport PartitionedGrid from .volume_container cimport VolumeContainer cdef enum: Nch = 4 # NOTE: We don't want to import the field_interpolator_tables here, as it # breaks a bunch of C++ interop. Maybe some day it won't. So, we just forward # declare. cdef struct VolumeRenderAccumulator ctypedef int calculate_extent_function(ImageSampler image, VolumeContainer *vc, np.int64_t rv[4]) except -1 nogil ctypedef void generate_vector_info_function(ImageSampler im, np.int64_t vi, np.int64_t vj, np.float64_t width[2], np.float64_t v_dir[3], np.float64_t v_pos[3]) noexcept nogil cdef struct ImageAccumulator: np.float64_t rgba[Nch] void *supp_data cdef class ImageSampler: cdef np.float64_t[:,:,:] vp_pos cdef np.float64_t[:,:,:] vp_dir cdef np.float64_t *center cdef np.float64_t[:,:,:] image cdef np.float64_t[:,:] zbuffer cdef np.int64_t[:,:] image_used cdef np.int64_t[:,:] mesh_lines cdef np.float64_t pdx, pdy cdef np.float64_t bounds[4] cdef np.float64_t[:,:] camera_data # position, width, unit_vec[0,2] cdef int nv[2] cdef np.float64_t *x_vec cdef np.float64_t *y_vec cdef public object acenter, aimage, ax_vec, ay_vec cdef public object azbuffer cdef public object aimage_used cdef public object amesh_lines cdef void *supp_data cdef np.float64_t width[3] cdef public object lens_type cdef public str volume_method cdef calculate_extent_function *extent_function cdef generate_vector_info_function *vector_function cdef void setup(self, PartitionedGrid pg) @staticmethod cdef void sample(VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil cdef class ProjectionSampler(ImageSampler): pass cdef class InterpolatedProjectionSampler(ImageSampler): cdef VolumeRenderAccumulator *vra cdef public object tf_obj cdef public object my_field_tables cdef class VolumeRenderSampler(ImageSampler): cdef VolumeRenderAccumulator *vra cdef public object tf_obj cdef public object my_field_tables cdef object tree_containers cdef class LightSourceRenderSampler(ImageSampler): cdef VolumeRenderAccumulator *vra cdef public object tf_obj cdef public object my_field_tables yt-project-yt-f043ac8/yt/utilities/lib/image_samplers.pyx000066400000000000000000000560241510711153200236300ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: extra_compile_args = CPP14_FLAG OMP_ARGS # distutils: extra_link_args = CPP14_FLAG OMP_ARGS # distutils: libraries = STD_LIBS FIXED_INTERP # distutils: language = c++ """ Image sampler definitions """ import numpy as np cimport cython from libc.math cimport sqrt from libc.stdlib cimport free, malloc from yt.utilities.lib cimport lenses from yt.utilities.lib.fp_utils cimport fclip, i64clip, imin from .field_interpolation_tables cimport ( FieldInterpolationTable, FIT_eval_transfer, FIT_eval_transfer_with_light, FIT_initialize_table, ) from .fixed_interpolator cimport eval_gradient, offset_interpolate from .grid_traversal cimport sampler_function, walk_volume from yt.funcs import mylog from ._octree_raytracing cimport RayInfo, _OctreeRayTracing cdef extern from "platform_dep.h": long int lrint(double x) noexcept nogil from cython.parallel import parallel, prange from cpython.exc cimport PyErr_CheckSignals cdef struct VolumeRenderAccumulator: int n_fits int n_samples FieldInterpolationTable *fits int field_table_ids[6] np.float64_t star_coeff np.float64_t star_er np.float64_t star_sigma_num np.float64_t *light_dir np.float64_t *light_rgba int grey_opacity cdef class ImageSampler: def __init__(self, np.float64_t[:,:,:] vp_pos, np.float64_t[:,:,:] vp_dir, np.ndarray[np.float64_t, ndim=1] center, bounds, np.ndarray[np.float64_t, ndim=3] image, np.ndarray[np.float64_t, ndim=1] x_vec, np.ndarray[np.float64_t, ndim=1] y_vec, np.ndarray[np.float64_t, ndim=1] width, str volume_method, *args, **kwargs): cdef int i self.volume_method = volume_method camera_data = kwargs.pop("camera_data", None) if camera_data is not None: self.camera_data = camera_data zbuffer = kwargs.pop("zbuffer", None) if zbuffer is None: zbuffer = np.ones((image.shape[0], image.shape[1]), "float64") image_used = np.zeros((image.shape[0], image.shape[1]), "int64") mesh_lines = np.zeros((image.shape[0], image.shape[1]), "int64") self.lens_type = kwargs.pop("lens_type", None) if self.lens_type == "plane-parallel": self.extent_function = lenses.calculate_extent_plane_parallel self.vector_function = lenses.generate_vector_info_plane_parallel else: if not (vp_pos.shape[0] == vp_dir.shape[0] == image.shape[0]) or \ not (vp_pos.shape[1] == vp_dir.shape[1] == image.shape[1]): msg = "Bad lens shape / direction for %s\n" % (self.lens_type) msg += "Shapes: (%s - %s - %s) and (%s - %s - %s)" % ( vp_pos.shape[0], vp_dir.shape[0], image.shape[0], vp_pos.shape[1], vp_dir.shape[1], image.shape[1]) raise RuntimeError(msg) if camera_data is not None and self.lens_type == 'perspective': self.extent_function = lenses.calculate_extent_perspective else: self.extent_function = lenses.calculate_extent_null self.\ vector_function = lenses.generate_vector_info_null # These assignments are so we can track the objects and prevent their # de-allocation from reference counts. Note that we do this to the # "atleast_3d" versions. Also, note that we re-assign the input # arguments. self.vp_pos = vp_pos self.vp_dir = vp_dir self.image = self.aimage = image self.acenter = center self.center = center.data self.ax_vec = x_vec self.x_vec = x_vec.data self.ay_vec = y_vec self.y_vec = y_vec.data self.zbuffer = zbuffer self.azbuffer = np.asarray(zbuffer) self.image_used = image_used self.aimage_used = np.asarray(image_used) self.mesh_lines = mesh_lines self.amesh_lines = np.asarray(mesh_lines) self.nv[0] = image.shape[0] self.nv[1] = image.shape[1] for i in range(4): self.bounds[i] = bounds[i] self.pdx = (bounds[1] - bounds[0])/self.nv[0] self.pdy = (bounds[3] - bounds[2])/self.nv[1] for i in range(3): self.width[i] = width[i] def __call__(self, PartitionedGrid pg, **kwa): if self.volume_method == 'KDTree': return self.cast_through_kdtree(pg, **kwa) elif self.volume_method == 'Octree': return self.cast_through_octree(pg, **kwa) else: raise NotImplementedError( 'Volume rendering has not been implemented for method: "%s"' % self.volume_method ) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def cast_through_kdtree(self, PartitionedGrid pg, int num_threads = 0): # This routine will iterate over all of the vectors and cast each in # turn. Might benefit from a more sophisticated intersection check, # like http://courses.csusm.edu/cs697exz/ray_box.htm cdef int vi, vj, hit, i, j cdef VolumeContainer *vc = pg.container self.setup(pg) cdef np.float64_t *v_pos cdef np.float64_t *v_dir cdef np.float64_t max_t hit = 0 cdef np.int64_t nx, ny, size cdef np.int64_t iter[4] self.extent_function(self, vc, iter) iter[0] = i64clip(iter[0]-1, 0, self.nv[0]) iter[1] = i64clip(iter[1]+1, 0, self.nv[0]) iter[2] = i64clip(iter[2]-1, 0, self.nv[1]) iter[3] = i64clip(iter[3]+1, 0, self.nv[1]) nx = (iter[1] - iter[0]) ny = (iter[3] - iter[2]) size = nx * ny cdef ImageAccumulator *idata cdef np.float64_t width[3] cdef int chunksize = 100 for i in range(3): width[i] = self.width[i] with nogil, parallel(num_threads = num_threads): idata = malloc(sizeof(ImageAccumulator)) idata.supp_data = self.supp_data v_pos = malloc(3 * sizeof(np.float64_t)) v_dir = malloc(3 * sizeof(np.float64_t)) for j in prange(size, schedule="static", chunksize=chunksize): vj = j % ny vi = (j - vj) / ny + iter[0] vj = vj + iter[2] # Dynamically calculate the position self.vector_function(self, vi, vj, width, v_dir, v_pos) for i in range(Nch): idata.rgba[i] = self.image[vi, vj, i] max_t = fclip(self.zbuffer[vi, vj], 0.0, 1.0) walk_volume(vc, v_pos, v_dir, self.sample, ( idata), NULL, max_t) if (j % (10*chunksize)) == 0: with gil: PyErr_CheckSignals() for i in range(Nch): self.image[vi, vj, i] = idata.rgba[i] idata.supp_data = NULL free(idata) free(v_pos) free(v_dir) return hit @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def cast_through_octree(self, PartitionedGrid pg, _OctreeRayTracing oct, int num_threads = 0): cdef RayInfo[int]* ri self.setup(pg) cdef sampler_function* sampler = self.sample cdef np.int64_t nx, ny, size cdef VolumeContainer *vc cdef ImageAccumulator *idata nx = self.nv[0] ny = self.nv[1] size = nx * ny cdef int i, j, k, vi, vj, icell cdef int[3] index = [0, 0, 0] cdef int chunksize = 100 cdef int n_fields = pg.container.n_fields cdef np.float64_t vp_dir_len mylog.debug('Integrating rays') with nogil, parallel(num_threads=num_threads): idata = malloc(sizeof(ImageAccumulator)) idata.supp_data = self.supp_data ri = new RayInfo[int]() vc = malloc(sizeof(VolumeContainer)) vc.n_fields = 1 vc.data = malloc(sizeof(np.float64_t*)) vc.mask = malloc(8*sizeof(np.uint8_t)) # The actual dimensions are 2x2x2, but the sampler # assumes vertex-centred data for a 1x1x1 lattice (i.e. # 2^3 vertices) vc.dims[0] = 1 vc.dims[1] = 1 vc.dims[2] = 1 for j in prange(size, schedule='static', chunksize=chunksize): vj = j % ny vi = (j - vj) / ny vp_dir_len = sqrt( self.vp_dir[vi, vj, 0]**2 + self.vp_dir[vi, vj, 1]**2 + self.vp_dir[vi, vj, 2]**2) # Cast ray oct.oct.cast_ray(&self.vp_pos[vi, vj, 0], &self.vp_dir[vi, vj, 0], ri.keys, ri.t) # Contains the ordered indices of the cells hit by the ray # and the entry/exit t values if ri.keys.size() == 0: continue for i in range(Nch): idata.rgba[i] = self.image[vi, vj, i] for i in range(8): vc.mask[i] = 1 # Iterate over cells for i in range(ri.keys.size()): icell = ri.keys[i] for k in range(n_fields): vc.data[k] = &pg.container.data[k][14*icell] # Fill the volume container with the current boundaries for k in range(3): vc.left_edge[k] = pg.container.data[0][14*icell+8+k] vc.right_edge[k] = pg.container.data[0][14*icell+11+k] vc.dds[k] = (vc.right_edge[k] - vc.left_edge[k]) vc.idds[k] = 1/vc.dds[k] # Now call the sampler sampler( vc, &self.vp_pos[vi, vj, 0], &self.vp_dir[vi, vj, 0], ri.t[2*i ]/vp_dir_len, ri.t[2*i+1]/vp_dir_len, index, idata ) for i in range(Nch): self.image[vi, vj, i] = idata.rgba[i] # Empty keys and t ri.keys.clear() ri.t.clear() del ri free(vc.data) free(vc.mask) free(vc) idata.supp_data = NULL free(idata) mylog.debug('Done integration') cdef void setup(self, PartitionedGrid pg): return @staticmethod cdef void sample( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil: return def ensure_code_unit_params(self, params): for param_name in ['center', 'vp_pos', 'vp_dir', 'width']: param = params[param_name] if hasattr(param, 'in_units'): params[param_name] = param.in_units('code_length') bounds = params['bounds'] if hasattr(bounds[0], 'units'): params['bounds'] = tuple(b.in_units('code_length').d for b in bounds) return params cdef class ProjectionSampler(ImageSampler): @staticmethod cdef void sample( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil: cdef ImageAccumulator *im = data cdef int i cdef np.float64_t dl = (exit_t - enter_t) cdef int di = (index[0]*vc.dims[1]+index[1])*vc.dims[2]+index[2] for i in range(imin(4, vc.n_fields)): im.rgba[i] += vc.data[i][di] * dl cdef class InterpolatedProjectionSampler(ImageSampler): def __cinit__(self, np.ndarray vp_pos, np.ndarray vp_dir, np.ndarray[np.float64_t, ndim=1] center, bounds, np.ndarray[np.float64_t, ndim=3] image, np.ndarray[np.float64_t, ndim=1] x_vec, np.ndarray[np.float64_t, ndim=1] y_vec, np.ndarray[np.float64_t, ndim=1] width, str volume_method, n_samples = 10, **kwargs ): ImageSampler.__init__(self, vp_pos, vp_dir, center, bounds, image, x_vec, y_vec, width, volume_method, **kwargs) # Now we handle tf_obj self.vra = \ malloc(sizeof(VolumeRenderAccumulator)) self.vra.n_samples = n_samples self.supp_data = self.vra @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @staticmethod cdef void sample( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil: cdef ImageAccumulator *im = data cdef VolumeRenderAccumulator *vri = \ im.supp_data # we assume this has vertex-centered data. cdef int offset = index[0] * (vc.dims[1] + 1) * (vc.dims[2] + 1) \ + index[1] * (vc.dims[2] + 1) + index[2] cdef np.float64_t dp[3] cdef np.float64_t ds[3] cdef np.float64_t dt = (exit_t - enter_t) / vri.n_samples cdef np.float64_t dvs[6] for i in range(3): dp[i] = (enter_t + 0.5 * dt) * v_dir[i] + v_pos[i] dp[i] -= index[i] * vc.dds[i] + vc.left_edge[i] dp[i] *= vc.idds[i] ds[i] = v_dir[i] * vc.idds[i] * dt for i in range(vri.n_samples): for j in range(vc.n_fields): dvs[j] = offset_interpolate(vc.dims, dp, vc.data[j] + offset) for j in range(imin(3, vc.n_fields)): im.rgba[j] += dvs[j] * dt for j in range(3): dp[j] += ds[j] cdef class VolumeRenderSampler(ImageSampler): def __cinit__(self, np.ndarray vp_pos, np.ndarray vp_dir, np.ndarray[np.float64_t, ndim=1] center, bounds, np.ndarray[np.float64_t, ndim=3] image, np.ndarray[np.float64_t, ndim=1] x_vec, np.ndarray[np.float64_t, ndim=1] y_vec, np.ndarray[np.float64_t, ndim=1] width, str volume_method, tf_obj, n_samples = 10, **kwargs ): ImageSampler.__init__(self, vp_pos, vp_dir, center, bounds, image, x_vec, y_vec, width, volume_method, **kwargs) cdef int i cdef np.ndarray[np.float64_t, ndim=1] temp # Now we handle tf_obj self.vra = \ malloc(sizeof(VolumeRenderAccumulator)) self.vra.fits = \ malloc(sizeof(FieldInterpolationTable) * 6) self.vra.n_fits = tf_obj.n_field_tables assert(self.vra.n_fits <= 6) self.vra.grey_opacity = getattr(tf_obj, "grey_opacity", 0) self.vra.n_samples = n_samples self.my_field_tables = [] for i in range(self.vra.n_fits): temp = tf_obj.tables[i].y FIT_initialize_table(&self.vra.fits[i], temp.shape[0], temp.data, tf_obj.tables[i].x_bounds[0], tf_obj.tables[i].x_bounds[1], tf_obj.field_ids[i], tf_obj.weight_field_ids[i], tf_obj.weight_table_ids[i]) self.my_field_tables.append((tf_obj.tables[i], tf_obj.tables[i].y)) for i in range(6): self.vra.field_table_ids[i] = tf_obj.field_table_ids[i] self.supp_data = self.vra @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @staticmethod cdef void sample( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil: cdef ImageAccumulator *im = data cdef VolumeRenderAccumulator *vri = \ im.supp_data # we assume this has vertex-centered data. cdef int offset = index[0] * (vc.dims[1] + 1) * (vc.dims[2] + 1) \ + index[1] * (vc.dims[2] + 1) + index[2] cdef int cell_offset = index[0] * (vc.dims[1]) * (vc.dims[2]) \ + index[1] * (vc.dims[2]) + index[2] if vc.mask[cell_offset] != 1: return cdef np.float64_t dp[3] cdef np.float64_t ds[3] cdef np.float64_t dt = (exit_t - enter_t) / vri.n_samples cdef np.float64_t dvs[6] for i in range(3): dp[i] = (enter_t + 0.5 * dt) * v_dir[i] + v_pos[i] dp[i] -= index[i] * vc.dds[i] + vc.left_edge[i] dp[i] *= vc.idds[i] ds[i] = v_dir[i] * vc.idds[i] * dt for i in range(vri.n_samples): for j in range(vc.n_fields): dvs[j] = offset_interpolate(vc.dims, dp, vc.data[j] + offset) FIT_eval_transfer(dt, dvs, im.rgba, vri.n_fits, vri.fits, vri.field_table_ids, vri.grey_opacity) for j in range(3): dp[j] += ds[j] def __dealloc__(self): for i in range(self.vra.n_fits): free(self.vra.fits[i].d0) free(self.vra.fits[i].dy) free(self.vra.fits) free(self.vra) cdef class LightSourceRenderSampler(ImageSampler): def __cinit__(self, np.ndarray vp_pos, np.ndarray vp_dir, np.ndarray[np.float64_t, ndim=1] center, bounds, np.ndarray[np.float64_t, ndim=3] image, np.ndarray[np.float64_t, ndim=1] x_vec, np.ndarray[np.float64_t, ndim=1] y_vec, np.ndarray[np.float64_t, ndim=1] width, str volume_method, tf_obj, n_samples = 10, light_dir=(1.,1.,1.), light_rgba=(1.,1.,1.,1.), **kwargs): ImageSampler.__init__(self, vp_pos, vp_dir, center, bounds, image, x_vec, y_vec, width, volume_method, **kwargs) cdef int i cdef np.ndarray[np.float64_t, ndim=1] temp # Now we handle tf_obj self.vra = \ malloc(sizeof(VolumeRenderAccumulator)) self.vra.fits = \ malloc(sizeof(FieldInterpolationTable) * 6) self.vra.n_fits = tf_obj.n_field_tables assert(self.vra.n_fits <= 6) self.vra.grey_opacity = getattr(tf_obj, "grey_opacity", 0) self.vra.n_samples = n_samples self.vra.light_dir = malloc(sizeof(np.float64_t) * 3) self.vra.light_rgba = malloc(sizeof(np.float64_t) * 4) light_dir /= np.sqrt(light_dir[0] * light_dir[0] + light_dir[1] * light_dir[1] + light_dir[2] * light_dir[2]) for i in range(3): self.vra.light_dir[i] = light_dir[i] for i in range(4): self.vra.light_rgba[i] = light_rgba[i] self.my_field_tables = [] for i in range(self.vra.n_fits): temp = tf_obj.tables[i].y FIT_initialize_table(&self.vra.fits[i], temp.shape[0], temp.data, tf_obj.tables[i].x_bounds[0], tf_obj.tables[i].x_bounds[1], tf_obj.field_ids[i], tf_obj.weight_field_ids[i], tf_obj.weight_table_ids[i]) self.my_field_tables.append((tf_obj.tables[i], tf_obj.tables[i].y)) for i in range(6): self.vra.field_table_ids[i] = tf_obj.field_table_ids[i] self.supp_data = self.vra @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) @staticmethod cdef void sample( VolumeContainer *vc, np.float64_t v_pos[3], np.float64_t v_dir[3], np.float64_t enter_t, np.float64_t exit_t, int index[3], void *data) noexcept nogil: cdef ImageAccumulator *im = data cdef VolumeRenderAccumulator *vri = \ im.supp_data # we assume this has vertex-centered data. cdef int offset = index[0] * (vc.dims[1] + 1) * (vc.dims[2] + 1) \ + index[1] * (vc.dims[2] + 1) + index[2] cdef np.float64_t dp[3] cdef np.float64_t ds[3] cdef np.float64_t dt = (exit_t - enter_t) / vri.n_samples cdef np.float64_t dvs[6] cdef np.float64_t *grad grad = malloc(3 * sizeof(np.float64_t)) for i in range(3): dp[i] = (enter_t + 0.5 * dt) * v_dir[i] + v_pos[i] dp[i] -= index[i] * vc.dds[i] + vc.left_edge[i] dp[i] *= vc.idds[i] ds[i] = v_dir[i] * vc.idds[i] * dt for i in range(vri.n_samples): for j in range(vc.n_fields): dvs[j] = offset_interpolate(vc.dims, dp, vc.data[j] + offset) eval_gradient(vc.dims, dp, vc.data[0] + offset, grad) FIT_eval_transfer_with_light(dt, dvs, grad, vri.light_dir, vri.light_rgba, im.rgba, vri.n_fits, vri.fits, vri.field_table_ids, vri.grey_opacity) for j in range(3): dp[j] += ds[j] free(grad) def __dealloc__(self): for i in range(self.vra.n_fits): free(self.vra.fits[i].d0) free(self.vra.fits[i].dy) free(self.vra.light_dir) free(self.vra.light_rgba) free(self.vra.fits) free(self.vra) yt-project-yt-f043ac8/yt/utilities/lib/image_utilities.pyx000066400000000000000000000341511510711153200240120ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS # distutils: extra_compile_args = OMP_ARGS # distutils: extra_link_args = OMP_ARGS """ Utilities for images """ import numpy as np cimport numpy as np cimport cython from libc.math cimport ceil, floor, log2, sqrt from libc.stdlib cimport free, malloc from yt.utilities.lib.fp_utils cimport iclip, imin, imax, fclip, fmin, fmax from cython.parallel import prange, parallel @cython.wraparound(False) def add_points_to_greyscale_image( np.ndarray[np.float64_t, ndim=2] buffer, np.ndarray[np.uint8_t, ndim=2] buffer_mask, np.ndarray[np.float64_t, ndim=1] px, np.ndarray[np.float64_t, ndim=1] py, np.ndarray[np.float64_t, ndim=1] pv): cdef int i, j, pi cdef int npx = px.shape[0] cdef int xs = buffer.shape[0] cdef int ys = buffer.shape[1] for pi in range(npx): j = (xs * px[pi]) i = (ys * py[pi]) if (i < 0) or (i >= buffer.shape[0]) or (j < 0) or (j >= buffer.shape[1]): # some particles might intersect the image buffer # but actually be centered out of bounds. Skip those. # see https://github.com/yt-project/yt/issues/4603 continue buffer[i, j] += pv[pi] buffer_mask[i, j] = 1 return cdef inline int ij2idx(const int i, const int j, const int Nx) noexcept nogil: return i * Nx + j @cython.cdivision(True) @cython.wraparound(False) @cython.boundscheck(False) cdef void _add_cell_to_image_offaxis( const np.float64_t dx, const np.float64_t w, const np.float64_t q, const np.float64_t cell_max_width, const np.float64_t x, const np.float64_t y, const int Nx, const int Ny, const int Nsx, const int Nsy, np.float64_t* buffer, np.float64_t* buffer_weight, const int max_depth, const np.float64_t[:, :, ::1] stamp, const np.uint8_t[:, :, ::1] stamp_mask, ) noexcept nogil: cdef np.float64_t lx, rx, ly, ry cdef int j, k, depth cdef int jmin, jmax, kmin, kmax, jj1, jj2, kk1, kk2, itmp cdef np.float64_t cell_max_half_width = cell_max_width / 2 cdef np.float64_t xx1, xx2, yy1, yy2, dvx, dvy, sw, sq, tmp, dx_loc, dy_loc cdef np.float64_t dx3 = dx * dx * dx * Nx * Ny lx = x - cell_max_half_width rx = x + cell_max_half_width ly = y - cell_max_half_width ry = y + cell_max_half_width # Compute the range of pixels that the cell may overlap jmin = imax(floor(lx * Nx), 0) jmax = imax(ceil(rx * Nx), Nx - 1) kmin = imin(floor(ly * Ny), 0) kmax = imax(ceil(ry * Ny), Ny - 1) # If the cell is fully within one pixel if (jmax == jmin + 1) and (kmax == kmin + 1): buffer[ij2idx(jmin, kmin, Nx)] += q * dx3 buffer_weight[ij2idx(jmin, kmin, Nx)] += w * dx3 return # Our 'stamp' has multiple resolutions, select the one # that is at a higher resolution than the pixel # we are projecting onto with at least 4 pixels on the diagonal depth = iclip( (ceil(log2(4 * sqrt(3) * dx * fmax(Nx, Ny)))), 1, max_depth - 1, ) jmax = imin(Nsx, 1 << depth) kmax = imin(Nsy, 1 << depth) dx_loc = cell_max_width / jmax dy_loc = cell_max_width / kmax for j in range(jmax): xx1 = ((j - jmax / 2.) * dx_loc + x) * Nx xx2 = ((j + 1 - jmax / 2.) * dx_loc + x) * Nx jj1 = xx1 jj2 = xx2 # The subcell is out of the projected area if jj2 < 0 or jj1 >= Nx: continue # Fraction of overlap with the pixel in x direction dvx = fclip((jj2 - xx1) / (xx2 - xx1), 0., 1.) for k in range(kmax): if stamp_mask[depth, j, k] == 0: continue yy1 = ((k - kmax / 2.) * dy_loc + y) * Ny yy2 = ((k + 1 - kmax / 2.) * dy_loc + y) * Ny kk1 = yy1 kk2 = yy2 # The subcell is out of the projected area if kk2 < 0 or kk1 >= Ny: continue tmp = stamp[depth, j, k] * dx3 sw = tmp * w sq = tmp * q # Fraction of overlap with the pixel in y direction dvy = fclip((kk2 - yy1) / (yy2 - yy1), 0., 1.) if jj1 >= 0 and kk1 >= 0: tmp = dvx * dvy itmp = ij2idx(jj1, kk1, Nx) buffer[itmp] += sq * tmp buffer_weight[itmp] += sw * tmp if jj1 >= 0 and kk2 < Ny: tmp = dvx * (1 - dvy) itmp = ij2idx(jj1, kk2, Nx) buffer[itmp] += sq * tmp buffer_weight[itmp] += sw * tmp if jj2 < Nx and kk1 >= 0: tmp = (1 - dvx) * dvy itmp = ij2idx(jj2, kk1, Nx) buffer[itmp] += sq * tmp buffer_weight[itmp] += sw * tmp if jj2 < Nx and kk2 < Ny: tmp = (1 - dvx) * (1 - dvy) itmp = ij2idx(jj2, kk2, Nx) buffer[itmp] += sq * tmp buffer_weight[itmp] += sw * tmp @cython.boundscheck(False) @cython.cdivision(True) @cython.wraparound(False) def add_cells_to_image_offaxis( *, const np.float64_t[:, ::1] Xp, const np.float64_t[::1] dXp, const np.float64_t[::1] qty, const np.float64_t[::1] weight, const np.float64_t[:, :] rotation, np.float64_t[:, ::1] buffer, np.float64_t[:, ::1] buffer_weight, const int Nx, const int Ny, const int Npix_min = 4, ): cdef np.ndarray[np.float64_t, ndim=1] center = np.array([0.5, 0.5, 0.5]) cdef np.float64_t w0 = 1 / sqrt(3.) cdef int i, j, k cdef np.ndarray[np.float64_t, ndim=1] a = np.array([1., 0, 0]) * w0 cdef np.ndarray[np.float64_t, ndim=1] b = np.array([0, 1., 0]) * w0 cdef np.ndarray[np.float64_t, ndim=1] c = np.array([0, 0, 1.]) * w0 a = np.dot(rotation, a) b = np.dot(rotation, b) c = np.dot(rotation, c) cdef np.ndarray[np.float64_t, ndim=1] o = center - (a + b + c) / 2 cdef int Nsx, Nsy cdef np.float64_t dx_max = np.max(dXp) # The largest cell needs to be resolved by at least this number of pixels Nsx = max(Npix_min, int(ceil(2 * dx_max * sqrt(3) * Nx))) Nsy = max(Npix_min, int(ceil(2 * dx_max * sqrt(3) * Ny))) cdef int max_depth = int(ceil(log2(max(Nsx, Nsy)))) cdef int depth cdef np.ndarray[np.float64_t, ndim=3] stamp_arr = np.zeros((max_depth, Nsx, Nsy), dtype=float) cdef np.ndarray[np.uint8_t, ndim=3] stamp_mask_arr = np.zeros((max_depth, Nsx, Nsy), dtype=np.uint8) cdef np.float64_t[:, :, ::1] stamp = stamp_arr cdef np.uint8_t[:, :, ::1] stamp_mask = stamp_mask_arr # Precompute the mip for depth in range(max_depth): if depth == 0: stamp[0, 0, 0] = 1 continue direct_integrate_cube( o, a, b, c, stamp_arr[depth, :, :], stamp_mask_arr[depth, :, :], imin(1 << depth, Nsx), imin(1 << depth, Nsy), ) stamp_arr[depth] /= np.sum(stamp[depth]) # Iterate over all cells, applying the stamp cdef np.float64_t x, y, dx cdef np.float64_t[:, ::1] rotation_view = np.ascontiguousarray(rotation) cdef np.float64_t w, q, cell_max_width, sq3 sq3 = sqrt(3.) # Local buffers cdef np.float64_t *lbuffer cdef np.float64_t *lbuffer_weight cdef int num_particles = len(Xp) with nogil, parallel(): lbuffer = malloc(sizeof(np.float64_t*) * Nx * Ny) lbuffer_weight = malloc(sizeof(np.float64_t*) * Nx * Ny) for j in range(Nx * Ny): lbuffer[j] = 0 lbuffer_weight[j] = 0 for i in prange(num_particles, schedule="runtime"): dx = dXp[i] w = weight[i] q = qty[i] cell_max_width = dx * sq3 x = ( rotation_view[0, 0] * Xp[i, 0] + rotation_view[0, 1] * Xp[i, 1] + rotation_view[0, 2] * Xp[i, 2] ) + 0.5 y = ( rotation_view[1, 0] * Xp[i, 0] + rotation_view[1, 1] * Xp[i, 1] + rotation_view[1, 2] * Xp[i, 2] ) + 0.5 _add_cell_to_image_offaxis( dx, w, q, cell_max_width, x, y, Nx, Ny, Nsx, Nsy, lbuffer, lbuffer_weight, max_depth, stamp, stamp_mask ) # Copy back data in main buffer with gil: for j in range(Nx): for k in range(Ny): buffer[j, k] += lbuffer[ij2idx(j, k, Nx)] buffer_weight[j, k] += lbuffer_weight[ij2idx(j, k, Nx)] # Free memory free(lbuffer) free(lbuffer_weight) @cython.boundscheck(False) @cython.wraparound(False) cdef inline np.float64_t det2d(const np.float64_t[::1] a, const np.float64_t[::1] b) noexcept nogil: return a[0] * b[1] - a[1] * b[0] @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) cdef bint check_in_parallelogram( const np.float64_t[::1] PA, const np.float64_t[::1] PQ, const np.float64_t[::1] PR, const int signPQ, const int signPR, np.float64_t[2] out ) noexcept nogil: cdef np.float64_t det_PQR = det2d(PQ, PR) if det_PQR == 0: out[0] = -1 out[1] = -1 return False out[0] = -det2d(PA, PQ) / det_PQR out[1] = det2d(PA, PR) / det_PQR if 0 <= signPQ * out[0] <= 1 and 0 <= signPR * out[1] <= 1: return True out[0] = -1 out[1] = -1 return False @cython.boundscheck(False) @cython.cdivision(True) cdef int direct_integrate_cube( np.ndarray[np.float64_t, ndim=1] O, np.ndarray[np.float64_t, ndim=1] u, np.ndarray[np.float64_t, ndim=1] v, np.ndarray[np.float64_t, ndim=1] w, np.ndarray[np.float64_t, ndim=2] buffer, np.ndarray[np.uint8_t, ndim=2] buffer_mask, const int Nx, const int Ny, ) except -1: """ Compute depth of cube from direct integration of entry/exit points of rays """ cdef np.float64_t[::1] u2d = u[:2] cdef np.float64_t[::1] v2d = v[:2] cdef np.float64_t[::1] w2d = w[:2] cdef np.float64_t[::1] Oback = O + u + v + w cdef np.float64_t[::1] X = np.zeros(2) cdef np.float64_t[::1] OfrontA = np.zeros(2) cdef np.float64_t[::1] ObackA = np.zeros(2) cdef np.float64_t inv_dx = 1. / Nx cdef np.float64_t inv_dy = 1. / Ny cdef np.float64_t[2] nm cdef bint within cdef np.float64_t zmin, zmax, z cdef int Nhit, i, j for i in range(Nx): X[0] = (i + 0.5) * inv_dx OfrontA[0] = X[0] - O[0] ObackA[0] = X[0] - Oback[0] for j in range(Ny): zmin = np.inf zmax = -np.inf Nhit = 0 X[1] = (j + 0.5) * inv_dy OfrontA[1] = X[1] - O[1] ObackA[1] = X[1] - Oback[1] within = check_in_parallelogram(OfrontA, v2d, u2d, 1, 1, nm) if within: z = O[2] + nm[0] * u[2] + nm[1] * v[2] zmin = fmin(z, zmin) zmax = fmax(z, zmax) Nhit += 1 within = check_in_parallelogram(OfrontA, w2d, v2d, 1, 1, nm) if within: z = O[2] + nm[0] * v[2] + nm[1] * w[2] zmin = fmin(z, zmin) zmax = fmax(z, zmax) Nhit += 1 within = check_in_parallelogram(OfrontA, w2d, u2d, 1, 1, nm) if within: z = O[2] + nm[0] * u[2] + nm[1] * w[2] zmin = fmin(z, zmin) zmax = fmax(z, zmax) Nhit += 1 within = check_in_parallelogram(ObackA, v2d, u2d, -1, -1, nm) if within: z = Oback[2] + nm[0] * u[2] + nm[1] * v[2] zmin = fmin(z, zmin) zmax = fmax(z, zmax) Nhit += 1 within = check_in_parallelogram(ObackA, w2d, v2d, -1, -1, nm) if within: z = Oback[2] + nm[0] * v[2] + nm[1] * w[2] zmin = fmin(z, zmin) zmax = fmax(z, zmax) Nhit += 1 within = check_in_parallelogram(ObackA, w2d, u2d, -1, -1, nm) if within: z = Oback[2] + nm[0] * u[2] + nm[1] * w[2] zmin = fmin(z, zmin) zmax = fmax(z, zmax) Nhit += 1 if Nhit == 0: continue elif Nhit == 1: raise RuntimeError("This should not happen") else: buffer[i, j] += zmax - zmin buffer_mask[i, j] = 1 def add_points_to_image( np.ndarray[np.uint8_t, ndim=3] buffer, np.ndarray[np.float64_t, ndim=1] px, np.ndarray[np.float64_t, ndim=1] py, np.float64_t pv): cdef int i, j, k, pi cdef int npx = px.shape[0] cdef int xs = buffer.shape[0] cdef int ys = buffer.shape[1] cdef int v v = iclip((pv * 255), 0, 255) for pi in range(npx): j = (xs * px[pi]) i = (ys * py[pi]) for k in range(3): buffer[i, j, k] = v buffer[i, j, 3] = 255 return def add_rgba_points_to_image( np.ndarray[np.float64_t, ndim=3] buffer, np.ndarray[np.float64_t, ndim=1] px, np.ndarray[np.float64_t, ndim=1] py, np.ndarray[np.float64_t, ndim=2] rgba, ): """ Splat rgba points onto an image Given an image buffer, add colors to pixels defined by fractional positions px and py, with colors rgba. px and py are one dimensional arrays, and rgba is a an array of rgba values. """ cdef int i, j, k, pi cdef int npart = px.shape[0] cdef int xs = buffer.shape[0] cdef int ys = buffer.shape[1] #iv = iclip((pv * 255), 0, 255) for pi in range(npart): j = (xs * px[pi]) i = (ys * py[pi]) if i < 0 or j < 0 or i >= xs or j >= ys: continue for k in range(4): buffer[i, j, k] += rgba[pi, k] return yt-project-yt-f043ac8/yt/utilities/lib/interpolators.pyx000066400000000000000000000231571510711153200235460ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Simple interpolators """ import numpy as np cimport cython cimport numpy as np from yt.utilities.lib.fp_utils cimport iclip @cython.cdivision(True) @cython.wraparound(False) @cython.boundscheck(False) cpdef void UnilinearlyInterpolate(np.ndarray[np.float64_t, ndim=1] table, np.ndarray[np.float64_t, ndim=1] x_vals, np.ndarray[np.float64_t, ndim=1] x_bins, np.ndarray[np.int32_t, ndim=1] x_is, np.ndarray[np.float64_t, ndim=1] output): cdef double x, xp, xm cdef int i, x_i for i in range(x_vals.shape[0]): x_i = x_is[i] x = x_vals[i] dx_inv = 1.0 / (x_bins[x_i+1] - x_bins[x_i]) xp = (x - x_bins[x_i]) * dx_inv xm = (x_bins[x_i+1] - x) * dx_inv output[i] = table[x_i ] * (xm) \ + table[x_i+1] * (xp) @cython.cdivision(True) @cython.wraparound(False) @cython.boundscheck(False) cpdef void BilinearlyInterpolate(np.ndarray[np.float64_t, ndim=2] table, np.ndarray[np.float64_t, ndim=1] x_vals, np.ndarray[np.float64_t, ndim=1] y_vals, np.ndarray[np.float64_t, ndim=1] x_bins, np.ndarray[np.float64_t, ndim=1] y_bins, np.ndarray[np.int32_t, ndim=1] x_is, np.ndarray[np.int32_t, ndim=1] y_is, np.ndarray[np.float64_t, ndim=1] output): cdef double x, xp, xm cdef double y, yp, ym cdef double dx_inv, dy_inv cdef int i, x_i, y_i for i in range(x_vals.shape[0]): x_i = x_is[i] y_i = y_is[i] x = x_vals[i] y = y_vals[i] dx_inv = 1.0 / (x_bins[x_i+1] - x_bins[x_i]) dy_inv = 1.0 / (y_bins[y_i+1] - y_bins[y_i]) xp = (x - x_bins[x_i]) * dx_inv yp = (y - y_bins[y_i]) * dy_inv xm = (x_bins[x_i+1] - x) * dx_inv ym = (y_bins[y_i+1] - y) * dy_inv output[i] = table[x_i , y_i ] * (xm*ym) \ + table[x_i+1, y_i ] * (xp*ym) \ + table[x_i , y_i+1] * (xm*yp) \ + table[x_i+1, y_i+1] * (xp*yp) @cython.cdivision(True) @cython.wraparound(False) @cython.boundscheck(False) cpdef void TrilinearlyInterpolate(np.ndarray[np.float64_t, ndim=3] table, np.ndarray[np.float64_t, ndim=1] x_vals, np.ndarray[np.float64_t, ndim=1] y_vals, np.ndarray[np.float64_t, ndim=1] z_vals, np.ndarray[np.float64_t, ndim=1] x_bins, np.ndarray[np.float64_t, ndim=1] y_bins, np.ndarray[np.float64_t, ndim=1] z_bins, np.ndarray[np.int64_t, ndim=1] x_is, np.ndarray[np.int64_t, ndim=1] y_is, np.ndarray[np.int64_t, ndim=1] z_is, np.ndarray[np.float64_t, ndim=1] output): cdef double x, xp, xm cdef double y, yp, ym cdef double z, zp, zm cdef double dx_inv, dy_inv, dz_inv cdef int i, x_i, y_i, z_i for i in range(x_vals.shape[0]): x_i = x_is[i] y_i = y_is[i] z_i = z_is[i] x = x_vals[i] y = y_vals[i] z = z_vals[i] dx_inv = 1.0 / (x_bins[x_i+1] - x_bins[x_i]) dy_inv = 1.0 / (y_bins[y_i+1] - y_bins[y_i]) dz_inv = 1.0 / (z_bins[z_i+1] - z_bins[z_i]) xp = (x - x_bins[x_i]) * dx_inv yp = (y - y_bins[y_i]) * dy_inv zp = (z - z_bins[z_i]) * dz_inv xm = (x_bins[x_i+1] - x) * dx_inv ym = (y_bins[y_i+1] - y) * dy_inv zm = (z_bins[z_i+1] - z) * dz_inv output[i] = table[x_i ,y_i ,z_i ] * (xm*ym*zm) \ + table[x_i+1,y_i ,z_i ] * (xp*ym*zm) \ + table[x_i ,y_i+1,z_i ] * (xm*yp*zm) \ + table[x_i ,y_i ,z_i+1] * (xm*ym*zp) \ + table[x_i+1,y_i ,z_i+1] * (xp*ym*zp) \ + table[x_i ,y_i+1,z_i+1] * (xm*yp*zp) \ + table[x_i+1,y_i+1,z_i ] * (xp*yp*zm) \ + table[x_i+1,y_i+1,z_i+1] * (xp*yp*zp) @cython.cdivision(True) @cython.wraparound(False) @cython.boundscheck(False) cpdef void QuadrilinearlyInterpolate(np.ndarray[np.float64_t, ndim=4] table, np.ndarray[np.float64_t, ndim=1] x_vals, np.ndarray[np.float64_t, ndim=1] y_vals, np.ndarray[np.float64_t, ndim=1] z_vals, np.ndarray[np.float64_t, ndim=1] w_vals, np.ndarray[np.float64_t, ndim=1] x_bins, np.ndarray[np.float64_t, ndim=1] y_bins, np.ndarray[np.float64_t, ndim=1] z_bins, np.ndarray[np.float64_t, ndim=1] w_bins, np.ndarray[np.int64_t, ndim=1] x_is, np.ndarray[np.int64_t, ndim=1] y_is, np.ndarray[np.int64_t, ndim=1] z_is, np.ndarray[np.int64_t, ndim=1] w_is, np.ndarray[np.float64_t, ndim=1] output): cdef double x, xp, xm cdef double y, yp, ym cdef double z, zp, zm cdef double w, wp, wm cdef double dx_inv, dy_inv, dz_inv, dw_inv cdef int i, x_i, y_i, z_i, w_i for i in range(x_vals.shape[0]): x_i = x_is[i] y_i = y_is[i] z_i = z_is[i] w_i = w_is[i] x = x_vals[i] y = y_vals[i] z = z_vals[i] w = w_vals[i] dx_inv = 1.0 / (x_bins[x_i+1] - x_bins[x_i]) dy_inv = 1.0 / (y_bins[y_i+1] - y_bins[y_i]) dz_inv = 1.0 / (z_bins[z_i+1] - z_bins[z_i]) dw_inv = 1.0 / (w_bins[w_i+1] - w_bins[w_i]) xp = (x - x_bins[x_i]) * dx_inv yp = (y - y_bins[y_i]) * dy_inv zp = (z - z_bins[z_i]) * dz_inv wp = (w - w_bins[w_i]) * dw_inv xm = (x_bins[x_i+1] - x) * dx_inv ym = (y_bins[y_i+1] - y) * dy_inv zm = (z_bins[z_i+1] - z) * dz_inv wm = (w_bins[w_i+1] - w) * dw_inv output[i] = table[x_i ,y_i ,z_i ,w_i ] * (xm*ym*zm*wm) \ + table[x_i+1,y_i ,z_i ,w_i ] * (xp*ym*zm*wm) \ + table[x_i ,y_i+1,z_i ,w_i ] * (xm*yp*zm*wm) \ + table[x_i ,y_i ,z_i+1,w_i ] * (xm*ym*zp*wm) \ + table[x_i ,y_i ,z_i ,w_i+1] * (xm*ym*zm*wp) \ + table[x_i+1,y_i ,z_i ,w_i+1] * (xp*ym*zm*wp) \ + table[x_i ,y_i+1,z_i ,w_i+1] * (xm*yp*zm*wp) \ + table[x_i ,y_i ,z_i+1,w_i+1] * (xm*ym*zp*wp) \ + table[x_i+1,y_i ,z_i+1,w_i ] * (xp*ym*zp*wm) \ + table[x_i ,y_i+1,z_i+1,w_i ] * (xm*yp*zp*wm) \ + table[x_i+1,y_i+1,z_i ,w_i ] * (xp*yp*zm*wm) \ + table[x_i+1,y_i ,z_i+1,w_i+1] * (xp*ym*zp*wp) \ + table[x_i ,y_i+1,z_i+1,w_i+1] * (xm*yp*zp*wp) \ + table[x_i+1,y_i+1,z_i ,w_i+1] * (xp*yp*zm*wp) \ + table[x_i+1,y_i+1,z_i+1,w_i ] * (xp*yp*zp*wm) \ + table[x_i+1,y_i+1,z_i+1,w_i+1] * (xp*yp*zp*wp) @cython.cdivision(True) @cython.wraparound(False) @cython.boundscheck(False) cpdef void ghost_zone_interpolate(int rf, np.ndarray[np.float64_t, ndim=3] input_field, np.ndarray[np.float64_t, ndim=1] input_left, np.ndarray[np.float64_t, ndim=3] output_field, np.ndarray[np.float64_t, ndim=1] output_left): cdef int oi, oj, ok cdef int ii, ij, ik cdef np.float64_t xp, xm, yp, ym, zp, zm, temp cdef np.float64_t ods[3] cdef np.float64_t ids[3] cdef np.float64_t iids[3] cdef np.float64_t opos[3] cdef np.float64_t ropos[3] cdef int i for i in range(3): temp = input_left[i] + (rf * (input_field.shape[i] - 1)) ids[i] = (temp - input_left[i])/(input_field.shape[i]-1) temp = output_left[i] + output_field.shape[i] - 1 ods[i] = (temp - output_left[i])/(output_field.shape[i]-1) iids[i] = 1.0/ids[i] opos[0] = output_left[0] for oi in range(output_field.shape[0]): ropos[0] = ((opos[0] - input_left[0]) * iids[0]) ii = iclip( ropos[0], 0, input_field.shape[0] - 2) xp = ropos[0] - ii xm = 1.0 - xp opos[1] = output_left[1] for oj in range(output_field.shape[1]): ropos[1] = ((opos[1] - input_left[1]) * iids[1]) ij = iclip( ropos[1], 0, input_field.shape[1] - 2) yp = ropos[1] - ij ym = 1.0 - yp opos[2] = output_left[2] for ok in range(output_field.shape[2]): ropos[2] = ((opos[2] - input_left[2]) * iids[2]) ik = iclip( ropos[2], 0, input_field.shape[2] - 2) zp = ropos[2] - ik zm = 1.0 - zp output_field[oi,oj,ok] = \ input_field[ii ,ij ,ik ] * (xm*ym*zm) \ + input_field[ii+1,ij ,ik ] * (xp*ym*zm) \ + input_field[ii ,ij+1,ik ] * (xm*yp*zm) \ + input_field[ii ,ij ,ik+1] * (xm*ym*zp) \ + input_field[ii+1,ij ,ik+1] * (xp*ym*zp) \ + input_field[ii ,ij+1,ik+1] * (xm*yp*zp) \ + input_field[ii+1,ij+1,ik ] * (xp*yp*zm) \ + input_field[ii+1,ij+1,ik+1] * (xp*yp*zp) opos[2] += ods[2] opos[1] += ods[1] opos[0] += ods[0] yt-project-yt-f043ac8/yt/utilities/lib/lenses.pxd000066400000000000000000000017351510711153200221030ustar00rootroot00000000000000""" Definitions for the lens code """ import numpy as np cimport cython cimport numpy as np from libc.math cimport ( M_PI, acos, asin, atan, atan2, cos, exp, fabs, floor, log2, sin, sqrt, ) from yt.utilities.lib.fp_utils cimport fclip, fmax, fmin, i64clip, iclip, imax, imin from .image_samplers cimport ( ImageSampler, calculate_extent_function, generate_vector_info_function, ) from .vec3_ops cimport L2_norm, dot, fma, subtract from .volume_container cimport VolumeContainer cdef extern from "platform_dep.h": long int lrint(double x) noexcept nogil cdef extern from "limits.h": cdef int SHRT_MAX cdef generate_vector_info_function generate_vector_info_plane_parallel cdef generate_vector_info_function generate_vector_info_null cdef calculate_extent_function calculate_extent_plane_parallel cdef calculate_extent_function calculate_extent_perspective cdef calculate_extent_function calculate_extent_null yt-project-yt-f043ac8/yt/utilities/lib/lenses.pyx000066400000000000000000000161131510711153200221240ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Functions for computing the extent of lenses and whatnot """ import numpy as np cimport cython cimport numpy as np from .image_samplers cimport ImageSampler @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int calculate_extent_plane_parallel(ImageSampler image, VolumeContainer *vc, np.int64_t rv[4]) except -1 nogil: # We do this for all eight corners cdef np.float64_t temp cdef np.float64_t *edges[2] cdef np.float64_t cx, cy cdef np.float64_t extrema[4] cdef int i, j, k edges[0] = vc.left_edge edges[1] = vc.right_edge extrema[0] = extrema[2] = 1e300; extrema[1] = extrema[3] = -1e300 for i in range(2): for j in range(2): for k in range(2): # This should rotate it into the vector plane temp = edges[i][0] * image.x_vec[0] temp += edges[j][1] * image.x_vec[1] temp += edges[k][2] * image.x_vec[2] if temp < extrema[0]: extrema[0] = temp if temp > extrema[1]: extrema[1] = temp temp = edges[i][0] * image.y_vec[0] temp += edges[j][1] * image.y_vec[1] temp += edges[k][2] * image.y_vec[2] if temp < extrema[2]: extrema[2] = temp if temp > extrema[3]: extrema[3] = temp cx = cy = 0.0 for i in range(3): cx += image.center[i] * image.x_vec[i] cy += image.center[i] * image.y_vec[i] rv[0] = lrint((extrema[0] - cx - image.bounds[0])/image.pdx) rv[1] = rv[0] + lrint((extrema[1] - extrema[0])/image.pdx) rv[2] = lrint((extrema[2] - cy - image.bounds[2])/image.pdy) rv[3] = rv[2] + lrint((extrema[3] - extrema[2])/image.pdy) return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int calculate_extent_perspective(ImageSampler image, VolumeContainer *vc, np.int64_t rv[4]) except -1 nogil: cdef np.float64_t cam_pos[3] cdef np.float64_t cam_width[3] cdef np.float64_t north_vector[3] cdef np.float64_t east_vector[3] cdef np.float64_t normal_vector[3] cdef np.float64_t vertex[3] cdef np.float64_t pos1[3] cdef np.float64_t sight_vector[3] cdef np.float64_t sight_center[3] cdef np.float64_t corners[3][8] cdef float sight_vector_norm, sight_angle_cos, sight_length, dx, dy cdef int i, iv, px, py cdef int min_px, min_py, max_px, max_py min_px = SHRT_MAX min_py = SHRT_MAX max_px = -SHRT_MAX max_py = -SHRT_MAX # calculate vertices for 8 corners of vc corners[0][0] = vc.left_edge[0] corners[0][1] = vc.right_edge[0] corners[0][2] = vc.right_edge[0] corners[0][3] = vc.left_edge[0] corners[0][4] = vc.left_edge[0] corners[0][5] = vc.right_edge[0] corners[0][6] = vc.right_edge[0] corners[0][7] = vc.left_edge[0] corners[1][0] = vc.left_edge[1] corners[1][1] = vc.left_edge[1] corners[1][2] = vc.right_edge[1] corners[1][3] = vc.right_edge[1] corners[1][4] = vc.left_edge[1] corners[1][5] = vc.left_edge[1] corners[1][6] = vc.right_edge[1] corners[1][7] = vc.right_edge[1] corners[2][0] = vc.left_edge[2] corners[2][1] = vc.left_edge[2] corners[2][2] = vc.left_edge[2] corners[2][3] = vc.left_edge[2] corners[2][4] = vc.right_edge[2] corners[2][5] = vc.right_edge[2] corners[2][6] = vc.right_edge[2] corners[2][7] = vc.right_edge[2] # This code was ported from # yt.visualization.volume_rendering.lens.PerspectiveLens.project_to_plane() for i in range(3): cam_pos[i] = image.camera_data[0, i] cam_width[i] = image.camera_data[1, i] east_vector[i] = image.camera_data[2, i] north_vector[i] = image.camera_data[3, i] normal_vector[i] = image.camera_data[4, i] for iv in range(8): vertex[0] = corners[0][iv] vertex[1] = corners[1][iv] vertex[2] = corners[2][iv] cam_width[1] = cam_width[0] * image.nv[1] / image.nv[0] subtract(vertex, cam_pos, sight_vector) fma(cam_width[2], normal_vector, cam_pos, sight_center) sight_vector_norm = L2_norm(sight_vector) if sight_vector_norm != 0: for i in range(3): sight_vector[i] /= sight_vector_norm sight_angle_cos = dot(sight_vector, normal_vector) sight_angle_cos = fclip(sight_angle_cos, -1.0, 1.0) if acos(sight_angle_cos) < 0.5 * M_PI and sight_angle_cos != 0.0: sight_length = cam_width[2] / sight_angle_cos else: sight_length = sqrt(cam_width[0] * cam_width[0] + cam_width[1] * cam_width[1]) sight_length /= sqrt(1.0 - sight_angle_cos * sight_angle_cos) fma(sight_length, sight_vector, cam_pos, pos1) subtract(pos1, sight_center, pos1) dx = dot(pos1, east_vector) dy = dot(pos1, north_vector) px = int(image.nv[0] * 0.5 + image.nv[0] / cam_width[0] * dx) py = int(image.nv[1] * 0.5 + image.nv[1] / cam_width[1] * dy) min_px = min(min_px, px) max_px = max(max_px, px) min_py = min(min_py, py) max_py = max(max_py, py) rv[0] = max(min_px, 0) rv[1] = min(max_px, image.nv[0]) rv[2] = max(min_py, 0) rv[3] = min(max_py, image.nv[1]) return 0 # We do this for a bunch of lenses. Fallback is to grab them from the vector # info supplied. @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int calculate_extent_null(ImageSampler image, VolumeContainer *vc, np.int64_t rv[4]) except -1 nogil: rv[0] = 0 rv[1] = image.nv[0] rv[2] = 0 rv[3] = image.nv[1] return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef void generate_vector_info_plane_parallel(ImageSampler im, np.int64_t vi, np.int64_t vj, np.float64_t width[2], # Now outbound np.float64_t v_dir[3], np.float64_t v_pos[3]) noexcept nogil: cdef int i cdef np.float64_t px, py px = width[0] * (vi)/(im.nv[0]-1) - width[0]/2.0 py = width[1] * (vj)/(im.nv[1]-1) - width[1]/2.0 # atleast_3d will add to beginning and end v_pos[0] = im.vp_pos[0,0,0]*px + im.vp_pos[0,3,0]*py + im.vp_pos[0,9,0] v_pos[1] = im.vp_pos[0,1,0]*px + im.vp_pos[0,4,0]*py + im.vp_pos[0,10,0] v_pos[2] = im.vp_pos[0,2,0]*px + im.vp_pos[0,5,0]*py + im.vp_pos[0,11,0] for i in range(3): v_dir[i] = im.vp_dir[0,i,0] @cython.boundscheck(False) @cython.wraparound(False) cdef void generate_vector_info_null(ImageSampler im, np.int64_t vi, np.int64_t vj, np.float64_t width[2], # Now outbound np.float64_t v_dir[3], np.float64_t v_pos[3]) noexcept nogil: cdef int i for i in range(3): # Here's a funny thing: we use vi here because our *image* will be # flattened. That means that im.nv will be a better one-d offset, # since vp_pos has funny strides. v_pos[i] = im.vp_pos[vi, vj, i] v_dir[i] = im.vp_dir[vi, vj, i] yt-project-yt-f043ac8/yt/utilities/lib/line_integral_convolution.pyx000066400000000000000000000041541510711153200261100ustar00rootroot00000000000000""" Utilities for line integral convolution annotation """ import numpy as np cimport cython cimport numpy as np @cython.cdivision(True) cdef void _advance_2d(double vx, double vy, int* x, int* y, double* fx, double* fy, int w, int h): cdef double tx, ty if vx>=0: tx = (1-fx[0])/vx else: tx = -fx[0]/vx if vy>=0: ty = (1-fy[0])/vy else: ty = -fy[0]/vy if tx=0: x[0]+=1 fx[0]=0 else: x[0]-=1 fx[0]=1 fy[0]+=tx*vy else: if vy>=0: y[0]+=1 fy[0]=0 else: y[0]-=1 fy[0]=1 fx[0]+=ty*vx if x[0]>=w: x[0]=w-1 if x[0]<0: x[0]=0 if y[0]<0: y[0]=0 if y[0]>=h: y[0]=h-1 def line_integral_convolution_2d( np.ndarray[double, ndim=3] vectors, np.ndarray[double, ndim=2] texture, np.ndarray[double, ndim=1] kernel): cdef int i,j,l,x,y cdef int h,w,kernellen cdef double fx, fy cdef np.ndarray[double, ndim=2] result w = vectors.shape[0] h = vectors.shape[1] kernellen = kernel.shape[0] result = np.zeros((w,h),dtype=np.double) vectors = vectors[...,::-1].copy() for i in range(w): for j in range(h): if vectors[i,j,0]==0 and vectors[i,j,1]==0: continue x = i y = j fx = 0.5 fy = 0.5 l = kernellen//2 result[i,j] += kernel[l]*texture[x,y] while l0: _advance_2d(-vectors[x,y,0],-vectors[x,y,1], &x, &y, &fx, &fy, w, h) l-=1 result[i,j] += kernel[l]*texture[x,y] return result yt-project-yt-f043ac8/yt/utilities/lib/marching_cubes.h000066400000000000000000000426501510711153200232200ustar00rootroot00000000000000int edge_table[256] = { 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 }; int tri_table[256][16] = { {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} }; yt-project-yt-f043ac8/yt/utilities/lib/marching_cubes.pyx000066400000000000000000000372321510711153200236110ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS FIXED_INTERP # distutils: language = c++ """ Marching cubes implementation """ cimport cython cimport numpy as np import numpy as np from libc.math cimport sqrt from libc.stdlib cimport free, malloc from .fixed_interpolator cimport ( eval_gradient, offset_fill, offset_interpolate, vertex_interp, ) from yt.units.yt_array import YTArray cdef extern from "marching_cubes.h": int tri_table[256][16] int edge_table[256] cdef struct Triangle: Triangle *next np.float64_t p[3][3] np.float64_t val[3] # Usually only use one value cdef struct TriangleCollection: int count Triangle *first Triangle *current cdef Triangle *AddTriangle(Triangle *self, np.float64_t p0[3], np.float64_t p1[3], np.float64_t p2[3]): cdef Triangle *nn = malloc(sizeof(Triangle)) if self != NULL: self.next = nn cdef int i for i in range(3): nn.p[0][i] = p0[i] for i in range(3): nn.p[1][i] = p1[i] for i in range(3): nn.p[2][i] = p2[i] nn.next = NULL return nn cdef int CountTriangles(Triangle *first): cdef int count = 0 cdef Triangle *this = first while this != NULL: count += 1 this = this.next return count cdef void FillTriangleValues(np.ndarray[np.float64_t, ndim=1] values, Triangle *first, int nskip = 1): cdef Triangle *this = first cdef int i = 0 cdef int j while this != NULL: for j in range(nskip): values[i*nskip + j] = this.val[j] i += 1 this = this.next cdef void WipeTriangles(Triangle *first): cdef Triangle *this = first cdef Triangle *last while this != NULL: last = this this = this.next free(last) cdef void FillAndWipeTriangles(np.ndarray[np.float64_t, ndim=2] vertices, Triangle *first): cdef int count = 0 cdef Triangle *this = first cdef Triangle *last cdef int i, j while this != NULL: for i in range(3): for j in range(3): vertices[count, j] = this.p[i][j] count += 1 # Do it at the end because it's an index last = this this = this.next free(last) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int march_cubes( np.float64_t gv[8], np.float64_t isovalue, np.float64_t dds[3], np.float64_t x, np.float64_t y, np.float64_t z, TriangleCollection *triangles): cdef np.float64_t vertlist[12][3] cdef int cubeindex = 0 cdef int n cdef int nt = 0 for n in range(8): if gv[n] < isovalue: cubeindex |= (1 << n) if edge_table[cubeindex] == 0: return 0 if (edge_table[cubeindex] & 1): # 0,0,0 with 1,0,0 vertex_interp(gv[0], gv[1], isovalue, vertlist[0], dds, x, y, z, 0, 1) if (edge_table[cubeindex] & 2): # 1,0,0 with 1,1,0 vertex_interp(gv[1], gv[2], isovalue, vertlist[1], dds, x, y, z, 1, 2) if (edge_table[cubeindex] & 4): # 1,1,0 with 0,1,0 vertex_interp(gv[2], gv[3], isovalue, vertlist[2], dds, x, y, z, 2, 3) if (edge_table[cubeindex] & 8): # 0,1,0 with 0,0,0 vertex_interp(gv[3], gv[0], isovalue, vertlist[3], dds, x, y, z, 3, 0) if (edge_table[cubeindex] & 16): # 0,0,1 with 1,0,1 vertex_interp(gv[4], gv[5], isovalue, vertlist[4], dds, x, y, z, 4, 5) if (edge_table[cubeindex] & 32): # 1,0,1 with 1,1,1 vertex_interp(gv[5], gv[6], isovalue, vertlist[5], dds, x, y, z, 5, 6) if (edge_table[cubeindex] & 64): # 1,1,1 with 0,1,1 vertex_interp(gv[6], gv[7], isovalue, vertlist[6], dds, x, y, z, 6, 7) if (edge_table[cubeindex] & 128): # 0,1,1 with 0,0,1 vertex_interp(gv[7], gv[4], isovalue, vertlist[7], dds, x, y, z, 7, 4) if (edge_table[cubeindex] & 256): # 0,0,0 with 0,0,1 vertex_interp(gv[0], gv[4], isovalue, vertlist[8], dds, x, y, z, 0, 4) if (edge_table[cubeindex] & 512): # 1,0,0 with 1,0,1 vertex_interp(gv[1], gv[5], isovalue, vertlist[9], dds, x, y, z, 1, 5) if (edge_table[cubeindex] & 1024): # 1,1,0 with 1,1,1 vertex_interp(gv[2], gv[6], isovalue, vertlist[10], dds, x, y, z, 2, 6) if (edge_table[cubeindex] & 2048): # 0,1,0 with 0,1,1 vertex_interp(gv[3], gv[7], isovalue, vertlist[11], dds, x, y, z, 3, 7) n = 0 while 1: triangles.current = AddTriangle(triangles.current, vertlist[tri_table[cubeindex][n ]], vertlist[tri_table[cubeindex][n+1]], vertlist[tri_table[cubeindex][n+2]]) triangles.count += 1 nt += 1 if triangles.first == NULL: triangles.first = triangles.current n += 3 if tri_table[cubeindex][n] == -1: break return nt @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def march_cubes_grid(np.float64_t isovalue, np.ndarray[np.float64_t, ndim=3] values, np.ndarray[np.uint8_t, ndim=3, cast=True] mask, np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] dxs, obj_sample = None, int sample_type = 1): cdef int dims[3] cdef int i, j, k, n, m, nt cdef int offset cdef np.float64_t gv[8] cdef np.float64_t pos[3] cdef np.float64_t point[3] cdef np.float64_t idds[3] cdef np.float64_t *intdata = NULL cdef np.float64_t *sdata = NULL cdef np.float64_t do_sample cdef np.ndarray[np.float64_t, ndim=3] sample cdef np.ndarray[np.float64_t, ndim=1] sampled cdef TriangleCollection triangles cdef Triangle *last cdef Triangle *current if obj_sample is not None: sample = obj_sample sdata = sample.data do_sample = sample_type # 1 for face, 2 for vertex else: do_sample = 0 for i in range(3): dims[i] = values.shape[i] - 1 idds[i] = 1.0 / dxs[i] triangles.first = triangles.current = NULL last = current = NULL triangles.count = 0 cdef np.float64_t *data = values.data cdef np.float64_t *dds = dxs.data pos[0] = left_edge[0] for i in range(dims[0]): pos[1] = left_edge[1] for j in range(dims[1]): pos[2] = left_edge[2] for k in range(dims[2]): if mask[i,j,k] == 1: offset = i * (dims[1] + 1) * (dims[2] + 1) \ + j * (dims[2] + 1) + k intdata = data + offset offset_fill(dims, intdata, gv) nt = march_cubes(gv, isovalue, dds, pos[0], pos[1], pos[2], &triangles) if nt == 0 or do_sample == 0: pos[2] += dds[2] continue if last == NULL and triangles.first != NULL: current = triangles.first last = NULL elif last != NULL: current = last.next if do_sample == 1: # At each triangle's center, sample our secondary field while current != NULL: for n in range(3): point[n] = 0.0 for n in range(3): for m in range(3): point[m] += (current.p[n][m]-pos[m])*idds[m] for n in range(3): point[n] /= 3.0 current.val[0] = offset_interpolate(dims, point, sdata + offset) last = current if current.next == NULL: break current = current.next elif do_sample == 2: while current != NULL: for n in range(3): for m in range(3): point[m] = (current.p[n][m]-pos[m])*idds[m] current.val[n] = offset_interpolate(dims, point, sdata + offset) last = current if current.next == NULL: break current = current.next pos[2] += dds[2] pos[1] += dds[1] pos[0] += dds[0] # Hallo, we are all done. cdef np.ndarray[np.float64_t, ndim=2] vertices vertices = np.zeros((triangles.count*3,3), dtype='float64') if do_sample == 0: FillAndWipeTriangles(vertices, triangles.first) return vertices cdef int nskip = 0 if do_sample == 1: nskip = 1 elif do_sample == 2: nskip = 3 sampled = np.zeros(triangles.count * nskip, dtype='float64') FillTriangleValues(sampled, triangles.first, nskip) FillAndWipeTriangles(vertices, triangles.first) if hasattr(obj_sample, 'units'): sampled = YTArray(sampled, obj_sample.units) return vertices, sampled @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def march_cubes_grid_flux( np.float64_t isovalue, np.ndarray[np.float64_t, ndim=3] values, np.ndarray[np.float64_t, ndim=3] v1, np.ndarray[np.float64_t, ndim=3] v2, np.ndarray[np.float64_t, ndim=3] v3, np.ndarray[np.float64_t, ndim=3] flux_field, np.ndarray[np.uint8_t, ndim=3, cast=True] mask, np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] dxs): cdef int dims[3] cdef int i, j, k, n, m cdef int offset cdef np.float64_t gv[8] cdef np.float64_t *intdata = NULL cdef TriangleCollection triangles cdef Triangle *current = NULL cdef Triangle *last = NULL cdef np.float64_t *data = values.data cdef np.float64_t *v1data = v1.data cdef np.float64_t *v2data = v2.data cdef np.float64_t *v3data = v3.data cdef np.float64_t *fdata = flux_field.data cdef np.float64_t *dds = dxs.data cdef np.float64_t flux = 0.0 cdef np.float64_t temp, area, s cdef np.float64_t center[3] cdef np.float64_t point[3] cdef np.float64_t cell_pos[3] cdef np.float64_t fv[3] cdef np.float64_t idds[3] cdef np.float64_t normal[3] for i in range(3): dims[i] = values.shape[i] - 1 idds[i] = 1.0 / dds[i] triangles.first = triangles.current = NULL triangles.count = 0 cell_pos[0] = left_edge[0] for i in range(dims[0]): cell_pos[1] = left_edge[1] for j in range(dims[1]): cell_pos[2] = left_edge[2] for k in range(dims[2]): if mask[i,j,k] == 1: offset = i * (dims[1] + 1) * (dims[2] + 1) \ + j * (dims[2] + 1) + k intdata = data + offset offset_fill(dims, intdata, gv) march_cubes(gv, isovalue, dds, cell_pos[0], cell_pos[1], cell_pos[2], &triangles) # Now our triangles collection has a bunch. We now # calculate fluxes for each. if last == NULL and triangles.first != NULL: current = triangles.first last = NULL elif last != NULL: current = last.next while current != NULL: # Calculate the center of the triangle wval = 0.0 for n in range(3): center[n] = 0.0 for n in range(3): for m in range(3): point[m] = (current.p[n][m]-cell_pos[m])*idds[m] # Now we calculate the value at this point temp = offset_interpolate(dims, point, intdata) #print("something", temp, point[0], point[1], point[2]) wval += temp for m in range(3): center[m] += temp * point[m] # Now we divide by our normalizing factor for n in range(3): center[n] /= wval # We have our center point of the triangle, in 0..1 # coordinates. So now we interpolate our three # fields. fv[0] = offset_interpolate(dims, center, v1data + offset) fv[1] = offset_interpolate(dims, center, v2data + offset) fv[2] = offset_interpolate(dims, center, v3data + offset) # We interpolate again the actual value data wval = offset_interpolate(dims, center, fdata + offset) # Now we have our flux vector and our field value! # We just need a normal vector with which we can # dot it. The normal should be equal to the gradient # in the center of the triangle, or thereabouts. eval_gradient(dims, center, intdata, normal) temp = 0.0 for n in range(3): temp += normal[n]*normal[n] # Take the negative, to ensure it points inwardly temp = -sqrt(temp) # Dump this somewhere for now temp = wval * (fv[0] * normal[0] + fv[1] * normal[1] + fv[2] * normal[2])/temp # Now we need the area of the triangle. This will take # a lot of time to calculate compared to the rest. # We use Heron's formula. for n in range(3): fv[n] = 0.0 for n in range(3): fv[0] += (current.p[0][n] - current.p[2][n]) * (current.p[0][n] - current.p[2][n]) fv[1] += (current.p[1][n] - current.p[0][n]) * (current.p[1][n] - current.p[0][n]) fv[2] += (current.p[2][n] - current.p[1][n]) * (current.p[2][n] - current.p[1][n]) s = 0.0 for n in range(3): fv[n] = sqrt(fv[n]) s += 0.5 * fv[n] area = (s*(s-fv[0])*(s-fv[1])*(s-fv[2])) area = sqrt(area) flux += temp*area last = current if current.next == NULL: break current = current.next cell_pos[2] += dds[2] cell_pos[1] += dds[1] cell_pos[0] += dds[0] # Hallo, we are all done. WipeTriangles(triangles.first) return flux yt-project-yt-f043ac8/yt/utilities/lib/mesh_triangulation.h000066400000000000000000000032771510711153200241450ustar00rootroot00000000000000#define MAX_NUM_TRI 12 #define HEX_NV 8 #define HEX_NT 12 #define TETRA_NV 4 #define TETRA_NT 4 #define WEDGE_NV 6 #define WEDGE_NT 8 // This array is used to triangulate the hexahedral mesh elements // Each element has six faces with two triangles each. // The vertex ordering convention is assumed to follow that used // here: http://homepages.cae.wisc.edu/~tautges/papers/cnmev3.pdf // Note that this is the case for Exodus II data. int triangulate_hex[MAX_NUM_TRI][3] = { {0, 2, 1}, {0, 3, 2}, // Face is 3 2 1 0 {4, 5, 6}, {4, 6, 7}, // Face is 4 5 6 7 {0, 1, 5}, {0, 5, 4}, // Face is 0 1 5 4 {1, 2, 6}, {1, 6, 5}, // Face is 1 2 6 5 {0, 7, 3}, {0, 4, 7}, // Face is 3 0 4 7 {3, 6, 2}, {3, 7, 6} // Face is 2 3 7 6 }; // Similarly, this is used to triangulate the tetrahedral cells int triangulate_tetra[MAX_NUM_TRI][3] = { {0, 1, 3}, {2, 3, 1}, {0, 3, 2}, {0, 2, 1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1} }; // Triangulate wedges int triangulate_wedge[MAX_NUM_TRI][3] = { {3, 0, 1}, {4, 3, 1}, {2, 5, 4}, {2, 4, 1}, {0, 3, 2}, {2, 3, 5}, {3, 4, 5}, {0, 2, 1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1} }; // This is used to select faces from a 20-sided hex element int hex20_faces[6][8] = { {0, 1, 5, 4, 12, 8, 13, 16}, {1, 2, 6, 5, 13, 9, 14, 17}, {3, 2, 6, 7, 15, 10, 14, 18}, {0, 3, 7, 4, 12, 11, 15, 19}, {4, 5, 6, 7, 19, 16, 17, 18}, {0, 1, 2, 3, 11, 8, 9, 10} }; // This is used to select faces from a second-order tet element int tet10_faces[4][6] = { {0, 1, 3, 4, 8, 7}, {2, 3, 1, 9, 8, 5}, {0, 3, 2, 7, 9, 6}, {0, 2, 1, 6, 5, 4} }; yt-project-yt-f043ac8/yt/utilities/lib/mesh_triangulation.pyx000066400000000000000000000234301510711153200245270ustar00rootroot00000000000000""" This file contains code for triangulating unstructured meshes. That is, for every element in the mesh, it breaks up the element into some number of triangles, returning a triangle mesh instead. It also contains code for removing duplicate triangles from the resulting mesh using a hash-table approach, so that we don't waste time rendering impossible-to-see triangles. This code is currently used by the OpenGL-accelerated unstructured mesh renderer, as well as when annotating mesh lines on regular slices. """ import numpy as np cimport cython cimport numpy as np from libc.stdlib cimport free, malloc from yt.utilities.exceptions import YTElementTypeNotRecognized cdef extern from "mesh_triangulation.h": enum: MAX_NUM_TRI int HEX_NV int HEX_NT int TETRA_NV int TETRA_NT int WEDGE_NV int WEDGE_NT int triangulate_hex[MAX_NUM_TRI][3] int triangulate_tetra[MAX_NUM_TRI][3] int triangulate_wedge[MAX_NUM_TRI][3] int hex20_faces[6][8] int tet10_faces[4][6] cdef struct TriNode: np.uint64_t key np.int64_t elem np.int64_t tri[3] TriNode* next_node cdef np.int64_t triangles_are_equal(np.int64_t tri1[3], np.int64_t tri2[3]) noexcept nogil: cdef np.int64_t found for i in range(3): found = False for j in range(3): if tri1[i] == tri2[j]: found = True if not found: return 0 return 1 cdef np.uint64_t triangle_hash(np.int64_t tri[3]) noexcept nogil: # http://stackoverflow.com/questions/1536393/good-hash-function-for-permutations cdef np.uint64_t h = 1 for i in range(3): h *= (1779033703 + 2*tri[i]) return h // 2 # should be enough, consider dynamic resizing in the future cdef np.int64_t TABLE_SIZE = 2**24 cdef class TriSet: ''' This is a hash table data structure for rapidly identifying the exterior triangles in a polygon mesh. We loop over each triangle in each element and update the TriSet for each one. We keep only the triangles that appear once, as these make up the exterior of the mesh. ''' cdef TriNode **table cdef np.uint64_t num_items def __cinit__(self): self.table = malloc(TABLE_SIZE * sizeof(TriNode*)) for i in range(TABLE_SIZE): self.table[i] = NULL self.num_items = 0 def __dealloc__(self): cdef np.int64_t i cdef TriNode *node cdef TriNode *delete_node for i in range(TABLE_SIZE): node = self.table[i] while (node != NULL): delete_node = node node = node.next_node free(delete_node) self.table[i] = NULL free(self.table) @cython.boundscheck(False) @cython.wraparound(False) def get_exterior_tris(self): ''' Returns two numpy arrays, one storing the exterior triangle indices and the other storing the corresponding element ids. ''' cdef np.int64_t[:, ::1] tri_indices = np.empty((self.num_items, 3), dtype="int64") cdef np.int64_t[::1] element_map = np.empty(self.num_items, dtype="int64") cdef TriNode* node cdef np.int64_t counter = 0 cdef np.int64_t i, j for i in range(TABLE_SIZE): node = self.table[i] while node != NULL: for j in range(3): tri_indices[counter, j] = node.tri[j] element_map[counter] = node.elem counter += 1 node = node.next_node return tri_indices, element_map cdef TriNode* _allocate_new_node(self, np.int64_t tri[3], np.uint64_t key, np.int64_t elem) noexcept nogil: cdef TriNode* new_node = malloc(sizeof(TriNode)) new_node.key = key new_node.elem = elem new_node.tri[0] = tri[0] new_node.tri[1] = tri[1] new_node.tri[2] = tri[2] new_node.next_node = NULL self.num_items += 1 return new_node @cython.cdivision(True) cdef void update(self, np.int64_t tri[3], np.int64_t elem) noexcept nogil: cdef np.uint64_t key = triangle_hash(tri) cdef np.uint64_t index = key % TABLE_SIZE cdef TriNode *node = self.table[index] if node == NULL: self.table[index] = self._allocate_new_node(tri, key, elem) return if key == node.key and triangles_are_equal(node.tri, tri): # this triangle is already here, delete it self.table[index] = node.next_node free(node) self.num_items -= 1 return elif node.next_node == NULL: node.next_node = self._allocate_new_node(tri, key, elem) return # walk through node list cdef TriNode* prev = node node = node.next_node while node != NULL: if key == node.key and triangles_are_equal(node.tri, tri): # this triangle is already here, delete it prev.next_node = node.next_node free(node) self.num_items -= 1 return if node.next_node == NULL: # we have reached the end; add new node node.next_node = self._allocate_new_node(tri, key, elem) return prev = node node = node.next_node cdef class MeshInfoHolder: cdef np.int64_t num_elem cdef np.int64_t num_tri cdef np.int64_t num_verts cdef np.int64_t VPE # num verts per element cdef np.int64_t TPE # num tris per element cdef int[MAX_NUM_TRI][3] tri_array def __cinit__(self, np.int64_t[:, ::1] indices): ''' This class is used to store metadata about the type of mesh being used. ''' self.num_elem = indices.shape[0] self.VPE = indices.shape[1] if (self.VPE == 8 or self.VPE == 20 or self.VPE == 27): self.TPE = HEX_NT self.tri_array = triangulate_hex elif (self.VPE == 4 or self.VPE == 10): self.TPE = TETRA_NT self.tri_array = triangulate_tetra elif self.VPE == 6: self.TPE = WEDGE_NT self.tri_array = triangulate_wedge else: raise YTElementTypeNotRecognized(3, self.VPE) self.num_tri = self.TPE * self.num_elem self.num_verts = self.num_tri * 3 @cython.boundscheck(False) @cython.wraparound(False) def cull_interior_triangles(np.int64_t[:, ::1] indices): ''' This is used to remove interior triangles from the mesh before rendering it on the GPU. ''' cdef MeshInfoHolder m = MeshInfoHolder(indices) cdef TriSet s = TriSet() cdef np.int64_t i, j, k cdef np.int64_t tri[3] for i in range(m.num_elem): for j in range(m.TPE): for k in range(3): tri[k] = indices[i, m.tri_array[j][k]] s.update(tri, i) return s.get_exterior_tris() @cython.boundscheck(False) @cython.wraparound(False) def get_vertex_data(np.float64_t[:, ::1] coords, np.float64_t[:, ::1] data, np.int64_t[:, ::1] indices): ''' This converts the data array from the shape (num_elem, conn_length) to (num_verts, ). ''' cdef MeshInfoHolder m = MeshInfoHolder(indices) cdef np.int64_t num_verts = coords.shape[0] cdef np.float32_t[:] vertex_data = np.zeros(num_verts, dtype="float32") cdef np.int64_t i, j for i in range(m.num_elem): for j in range(m.VPE): vertex_data[indices[i, j]] = data[i, j] return vertex_data @cython.boundscheck(False) @cython.wraparound(False) def triangulate_mesh(np.float64_t[:, ::1] coords, np.ndarray data, np.int64_t[:, ::1] indices): ''' This converts a mesh into a flattened triangle array suitable for rendering on the GPU. ''' cdef np.int64_t[:, ::1] exterior_tris cdef np.int64_t[::1] element_map exterior_tris, element_map = cull_interior_triangles(indices) cdef np.int64_t num_tri = exterior_tris.shape[0] cdef np.int64_t num_verts = 3 * num_tri cdef np.int64_t num_coords = 3 * num_verts cdef np.float32_t[:] vertex_data if data.ndim == 2: vertex_data = get_vertex_data(coords, data, indices) else: vertex_data = data.astype("float32") cdef np.int32_t[:] tri_indices = np.empty(num_verts, dtype=np.int32) cdef np.float32_t[:] tri_data = np.empty(num_verts, dtype=np.float32) cdef np.float32_t[:] tri_coords = np.empty(num_coords, dtype=np.float32) cdef np.int64_t vert_index, i, j, k for i in range(num_tri): for j in range(3): vert_index = i*3 + j if data.ndim == 1: tri_data[vert_index] = vertex_data[element_map[i]] else: tri_data[vert_index] = vertex_data[exterior_tris[i, j]] tri_indices[vert_index] = vert_index for k in range(3): tri_coords[vert_index*3 + k] = coords[exterior_tris[i, j], k] return np.array(tri_coords), np.array(tri_data), np.array(tri_indices) @cython.boundscheck(False) @cython.wraparound(False) def triangulate_indices(np.int64_t[:, ::1] indices): ''' This is like triangulate_mesh, except it only considers the connectivity information, instead of also copying the vertex coordinates and the data values. ''' cdef MeshInfoHolder m = MeshInfoHolder(indices) cdef np.int64_t[:, ::1] tri_indices = np.empty((m.num_tri, 3), dtype=np.int_) cdef np.int64_t i, j, k for i in range(m.num_elem): for j in range(m.TPE): for k in range(3): tri_indices[i*m.TPE + j, k] = indices[i, m.tri_array[j][k]] return np.array(tri_indices) yt-project-yt-f043ac8/yt/utilities/lib/mesh_utilities.pyx000066400000000000000000000065061510711153200236670ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Utilities for unstructured and semi-structured meshes """ import numpy as np cimport cython cimport numpy as np from yt.utilities.lib.fp_utils cimport fmax, fmin cdef extern from "platform_dep.h": double rint(double x) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_fcoords(np.ndarray[np.float64_t, ndim=2] coords, np.ndarray[np.int64_t, ndim=2] indices, int offset = 0): cdef np.ndarray[np.float64_t, ndim=2] fcoords cdef int nc = indices.shape[0] cdef int nv = indices.shape[1] cdef np.float64_t pos[3] cdef int i, j, k fcoords = np.empty((nc, 3), dtype="float64") for i in range(nc): for j in range(3): pos[j] = 0.0 for j in range(nv): for k in range(3): pos[k] += coords[indices[i, j] - offset, k] for j in range(3): pos[j] /= nv fcoords[i, j] = pos[j] return fcoords @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_fwidths(np.ndarray[np.float64_t, ndim=2] coords, np.ndarray[np.int64_t, ndim=2] indices, int offset = 0): cdef np.ndarray[np.float64_t, ndim=2] fwidths cdef int nc = indices.shape[0] cdef int nv = indices.shape[1] if nv != 8: raise NotImplementedError cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef int i, j, k cdef np.float64_t pos fwidths = np.empty((nc, 3), dtype="float64") for i in range(nc): for j in range(3): LE[j] = 1e60 RE[j] = -1e60 for j in range(nv): for k in range(3): pos = coords[indices[i, j] - offset, k] LE[k] = fmin(pos, LE[k]) RE[k] = fmax(pos, RE[k]) for j in range(3): fwidths[i, j] = RE[j] - LE[j] return fwidths @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def smallest_fwidth(np.ndarray[np.float64_t, ndim=2] coords, np.ndarray[np.int64_t, ndim=2] indices, int offset = 0): cdef np.float64_t fwidth = 1e60, pos cdef int nc = indices.shape[0] cdef int nv = indices.shape[1] if nv != 8: raise NotImplementedError cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef int i, j, k for i in range(nc): for j in range(3): LE[j] = 1e60 RE[j] = -1e60 for j in range(nv): for k in range(3): pos = coords[indices[i, j] - offset, k] LE[k] = fmin(pos, LE[k]) RE[k] = fmax(pos, RE[k]) for j in range(3): fwidth = fmin(fwidth, RE[j] - LE[j]) return fwidth @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def clamp_edges(np.float64_t[:] edge, np.float64_t[:] pleft, np.float64_t[:] pdx): """Clamp edge to pleft + pdx*n where n is the closest integer Note that edge is modified in-place. """ cdef np.float64_t start_index cdef np.float64_t integer_index cdef np.intp_t shape = edge.shape[0] for i in range(shape): start_index = (edge[i] - pleft[i]) / pdx[i] integer_index = rint(start_index) edge[i] = integer_index * pdx[i] + pleft[i] yt-project-yt-f043ac8/yt/utilities/lib/misc_utilities.pyx000066400000000000000000001306001510711153200236570ustar00rootroot00000000000000# distutils: libraries = STD_LIBS # distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG OMP_ARGS # distutils: extra_link_args = CPP14_FLAG OMP_ARGS """ Simple utilities that don't fit anywhere else """ import numpy as np from yt.funcs import get_pbar from yt.units.yt_array import YTArray cimport cython cimport numpy as np from cpython cimport buffer from cython.view cimport memoryview from libc.math cimport abs, sqrt from libc.stdlib cimport free, malloc from libc.string cimport strcmp from yt.geometry.selection_routines cimport _ensure_code from yt.utilities.lib.fp_utils cimport fmax, fmin cdef extern from "platform_dep.h": # NOTE that size_t might not be int void *alloca(int) from cython.parallel import prange from cpython.exc cimport PyErr_CheckSignals @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def new_bin_profile1d(np.ndarray[np.intp_t, ndim=1] bins_x, np.ndarray[np.float64_t, ndim=1] wsource, np.ndarray[np.float64_t, ndim=2] bsource, np.ndarray[np.float64_t, ndim=1] wresult, np.ndarray[np.float64_t, ndim=2] bresult, np.ndarray[np.float64_t, ndim=2] mresult, np.ndarray[np.float64_t, ndim=2] qresult, np.ndarray[np.uint8_t, ndim=1, cast=True] used): cdef int n, fi, bin cdef np.float64_t wval, bval, oldwr, bval_mresult cdef int nb = bins_x.shape[0] cdef int nf = bsource.shape[1] for n in range(nb): bin = bins_x[n] wval = wsource[n] # Skip field value entries where the weight field is zero if wval == 0: continue oldwr = wresult[bin] wresult[bin] += wval for fi in range(nf): bval = bsource[n,fi] bval_mresult = bval - mresult[bin,fi] # qresult has to have the previous wresult qresult[bin,fi] += oldwr * wval * bval_mresult * bval_mresult / \ (oldwr + wval) bresult[bin,fi] += wval*bval # mresult needs the new wresult mresult[bin,fi] += wval * bval_mresult / wresult[bin] used[bin] = 1 return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def new_bin_profile2d(np.ndarray[np.intp_t, ndim=1] bins_x, np.ndarray[np.intp_t, ndim=1] bins_y, np.ndarray[np.float64_t, ndim=1] wsource, np.ndarray[np.float64_t, ndim=2] bsource, np.ndarray[np.float64_t, ndim=2] wresult, np.ndarray[np.float64_t, ndim=3] bresult, np.ndarray[np.float64_t, ndim=3] mresult, np.ndarray[np.float64_t, ndim=3] qresult, np.ndarray[np.uint8_t, ndim=2, cast=True] used): cdef int n, fi, bin_x, bin_y cdef np.float64_t wval, bval, oldwr, bval_mresult cdef int nb = bins_x.shape[0] cdef int nf = bsource.shape[1] for n in range(nb): bin_x = bins_x[n] bin_y = bins_y[n] wval = wsource[n] # Skip field value entries where the weight field is zero if wval == 0: continue oldwr = wresult[bin_x, bin_y] wresult[bin_x,bin_y] += wval for fi in range(nf): bval = bsource[n,fi] bval_mresult = bval - mresult[bin_x,bin_y,fi] # qresult has to have the previous wresult qresult[bin_x,bin_y,fi] += oldwr * wval * bval_mresult * bval_mresult / \ (oldwr + wval) bresult[bin_x,bin_y,fi] += wval*bval # mresult needs the new wresult mresult[bin_x,bin_y,fi] += wval * bval_mresult / wresult[bin_x,bin_y] used[bin_x,bin_y] = 1 return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def new_bin_profile3d(np.ndarray[np.intp_t, ndim=1] bins_x, np.ndarray[np.intp_t, ndim=1] bins_y, np.ndarray[np.intp_t, ndim=1] bins_z, np.ndarray[np.float64_t, ndim=1] wsource, np.ndarray[np.float64_t, ndim=2] bsource, np.ndarray[np.float64_t, ndim=3] wresult, np.ndarray[np.float64_t, ndim=4] bresult, np.ndarray[np.float64_t, ndim=4] mresult, np.ndarray[np.float64_t, ndim=4] qresult, np.ndarray[np.uint8_t, ndim=3, cast=True] used): cdef int n, fi, bin_x, bin_y, bin_z cdef np.float64_t wval, bval, oldwr, bval_mresult cdef int nb = bins_x.shape[0] cdef int nf = bsource.shape[1] for n in range(nb): bin_x = bins_x[n] bin_y = bins_y[n] bin_z = bins_z[n] wval = wsource[n] # Skip field value entries where the weight field is zero if wval == 0: continue oldwr = wresult[bin_x, bin_y, bin_z] wresult[bin_x,bin_y,bin_z] += wval for fi in range(nf): bval = bsource[n,fi] bval_mresult = bval - mresult[bin_x,bin_y,bin_z,fi] # qresult has to have the previous wresult qresult[bin_x,bin_y,bin_z,fi] += \ oldwr * wval * bval_mresult * bval_mresult / \ (oldwr + wval) bresult[bin_x,bin_y,bin_z,fi] += wval*bval # mresult needs the new wresult mresult[bin_x,bin_y,bin_z,fi] += wval * bval_mresult / \ wresult[bin_x,bin_y,bin_z] used[bin_x,bin_y,bin_z] = 1 return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def lines(np.float64_t[:,:,:] image, np.int64_t[:] xs, np.int64_t[:] ys, np.float64_t[:,:] colors, int points_per_color=1, int thick=1, int flip=0, int crop = 0): cdef int nx = image.shape[0] cdef int ny = image.shape[1] cdef int nl = xs.shape[0] cdef np.float64_t alpha[4] cdef np.float64_t outa cdef int i, j, xi, yi cdef int dx, dy, sx, sy, e2, err cdef np.int64_t x0, x1, y0, y1 cdef int has_alpha = (image.shape[2] == 4) cdef int no_color = (image.shape[2] < 3) for j in range(0, nl, 2): # From wikipedia http://en.wikipedia.org/wiki/Bresenham's_line_algorithm x0 = xs[j] y0 = ys[j] x1 = xs[j+1] y1 = ys[j+1] dx = abs(x1-x0) dy = abs(y1-y0) if crop == 1 and (dx > nx/2.0 or dy > ny/2.0): continue err = dx - dy if no_color: for i in range(4): alpha[i] = colors[j, 0] elif has_alpha: for i in range(4): alpha[i] = colors[j/points_per_color,i] else: for i in range(3): alpha[i] = colors[j/points_per_color,3]*\ colors[j/points_per_color,i] if x0 < x1: sx = 1 else: sx = -1 if y0 < y1: sy = 1 else: sy = -1 while(1): if (x0 < thick and sx == -1): break elif (x0 >= nx-thick+1 and sx == 1): break elif (y0 < thick and sy == -1): break elif (y0 >= ny-thick+1 and sy == 1): break if x0 >= thick and x0 < nx-thick and y0 >= thick and y0 < ny-thick: for xi in range(x0-thick/2, x0+(1+thick)/2): for yi in range(y0-thick/2, y0+(1+thick)/2): if flip: yi0 = ny - yi else: yi0 = yi if no_color: image[xi, yi0, 0] = fmin(alpha[0], image[xi, yi0, 0]) elif has_alpha: image[xi, yi0, 3] = outa = alpha[3] + image[xi, yi0, 3]*(1-alpha[3]) if outa != 0.0: outa = 1.0/outa for i in range(3): image[xi, yi0, i] = \ ((1.-alpha[3])*image[xi, yi0, i]*image[xi, yi0, 3] + alpha[3]*alpha[i])*outa else: for i in range(3): image[xi, yi0, i] = \ (1.-alpha[i])*image[xi,yi0,i] + alpha[i] if (x0 == x1 and y0 == y1): break e2 = 2*err if e2 > -dy: err = err - dy x0 += sx if e2 < dx : err = err + dx y0 += sy return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def zlines(np.ndarray[np.float64_t, ndim=3] image, np.ndarray[np.float64_t, ndim=2] zbuffer, np.ndarray[np.int64_t, ndim=1] xs, np.ndarray[np.int64_t, ndim=1] ys, np.ndarray[np.float64_t, ndim=1] zs, np.ndarray[np.float64_t, ndim=2] colors, int points_per_color=1, int thick=1, int flip=0, int crop = 0): cdef int nx = image.shape[0] cdef int ny = image.shape[1] cdef int nl = xs.shape[0] cdef np.float64_t[:] alpha cdef int i, j, c cdef int dx, dy, sx, sy, e2, err cdef np.int64_t x0, x1, y0, y1, yi0 cdef np.float64_t z0, z1, dzx, dzy alpha = np.zeros(4) for j in range(0, nl, 2): # From wikipedia http://en.wikipedia.org/wiki/Bresenham's_line_algorithm x0 = xs[j] y0 = ys[j] x1 = xs[j+1] y1 = ys[j+1] z0 = zs[j] z1 = zs[j+1] dx = abs(x1-x0) dy = abs(y1-y0) dzx = (z1-z0) / (dx * dx + dy * dy) * dx dzy = (z1-z0) / (dx * dx + dy * dy) * dy err = dx - dy if crop == 1 and (dx > nx/2.0 or dy > ny/2.0): continue c = j/points_per_color/2 for i in range(3): alpha[i] = colors[c, i] * colors[c, 3] alpha[3] = colors[c, 3] if x0 < x1: sx = 1 else: sx = -1 if y0 < y1: sy = 1 else: sy = -1 while(1): if (x0 < thick and sx == -1): break elif (x0 >= nx-thick+1 and sx == 1): break elif (y0 < thick and sy == -1): break elif (y0 >= ny-thick+1 and sy == 1): break if x0 >= thick and x0 < nx-thick and y0 >= thick and y0 < ny-thick: for _ in range(x0-thick/2, x0+(1+thick)/2): for yi in range(y0-thick/2, y0+(1+thick)/2): if flip: yi0 = ny - yi else: yi0 = yi if z0 < zbuffer[x0, yi0]: if alpha[3] != 1.0: talpha = image[x0, yi0, 3] image[x0, yi0, 3] = alpha[3] + talpha * (1 - alpha[3]) for i in range(3): if image[x0, yi0, 3] == 0.0: image[x0, yi0, i] = 0.0 else: image[x0, yi0, i] = (alpha[3]*alpha[i] + image[x0, yi0, i]*talpha*(1.0-alpha[3]))/image[x0,yi0,3] else: for i in range(4): image[x0, yi0, i] = alpha[i] if (1.0 - image[x0, yi0, 3] < 1.0e-4): image[x0, yi0, 3] = 1.0 zbuffer[x0, yi0] = z0 if (x0 == x1 and y0 == y1): break e2 = 2*err if e2 > -dy: err = err - dy x0 += sx z0 += dzx if e2 < dx : err = err + dx y0 += sy z0 += dzy # assert(np.abs(z0 - z1) < 1.0e-3 * (np.abs(z0) + np.abs(z1))) return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def zpoints(np.ndarray[np.float64_t, ndim=3] image, np.ndarray[np.float64_t, ndim=2] zbuffer, np.ndarray[np.int64_t, ndim=1] xs, np.ndarray[np.int64_t, ndim=1] ys, np.ndarray[np.float64_t, ndim=1] zs, np.ndarray[np.float64_t, ndim=2] colors, np.ndarray[np.int64_t, ndim=1] radii, #pixels int points_per_color=1, int thick=1, int flip=0): cdef int nx = image.shape[0] cdef int ny = image.shape[1] cdef np.float64_t[:] alpha cdef np.float64_t talpha cdef int i, j, c cdef np.int64_t kx, ky, r, r2 cdef np.int64_t[:] idx, ks cdef np.int64_t x0, y0, yi0 cdef np.float64_t z0 alpha = np.zeros(4) #the sources must be ordered along z to avoid edges when two overlap idx = np.argsort(zs) for j in idx: r = radii[j] r2 = int((r+0.3)*(r+0.3)) #0.3 to get nicer shape ks = np.arange(-r, r+1, dtype=np.int64) z0 = zs[j] for kx in ks: x0 = xs[j]+kx if (x0 < 0 or x0 >= nx): continue for ky in ks: y0 = ys[j]+ky if (y0 < 0 or y0 >= ny): continue if (kx*kx + ky*ky > r2): continue c = j/points_per_color for i in range(3): alpha[i] = colors[c, i] * colors[c, 3] alpha[3] = colors[c, 3] if flip: yi0 = ny - y0 else: yi0 = y0 if z0 < zbuffer[x0, yi0]: if alpha[3] != 1.0: talpha = image[x0, yi0, 3] image[x0, yi0, 3] = alpha[3] + talpha * (1 - alpha[3]) for i in range(3): image[x0, yi0, i] = (alpha[3]*alpha[i] + image[x0, yi0, i]*talpha*(1.0-alpha[3]))/image[x0,yi0,3] if image[x0, yi0, 3] == 0.0: image[x0, yi0, i] = 0.0 else: for i in range(4): image[x0, yi0, i] = alpha[i] if (1.0 - image[x0, yi0, 3] < 1.0e-4): zbuffer[x0, yi0] = z0 return def rotate_vectors(np.ndarray[np.float64_t, ndim=3] vecs, np.ndarray[np.float64_t, ndim=2] R): cdef int nx = vecs.shape[0] cdef int ny = vecs.shape[1] rotated = np.empty((nx,ny,3),dtype='float64') for i in range(nx): for j in range(ny): for k in range(3): rotated[i,j,k] =\ R[k,0]*vecs[i,j,0]+R[k,1]*vecs[i,j,1]+R[k,2]*vecs[i,j,2] return rotated @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_color_bounds(np.ndarray[np.float64_t, ndim=1] px, np.ndarray[np.float64_t, ndim=1] py, np.ndarray[np.float64_t, ndim=1] pdx, np.ndarray[np.float64_t, ndim=1] pdy, np.ndarray[np.float64_t, ndim=1] value, np.float64_t leftx, np.float64_t rightx, np.float64_t lefty, np.float64_t righty, np.float64_t mindx = -1, np.float64_t maxdx = -1): cdef int i cdef np.float64_t mi = 1e100, ma = -1e100, v cdef int npx = px.shape[0] with nogil: for i in range(npx): v = value[i] if v < mi or v > ma: if px[i] + pdx[i] < leftx: continue if px[i] - pdx[i] > rightx: continue if py[i] + pdy[i] < lefty: continue if py[i] - pdy[i] > righty: continue if pdx[i] < mindx or pdy[i] < mindx: continue if maxdx > 0 and (pdx[i] > maxdx or pdy[i] > maxdx): continue if v < mi: mi = v if v > ma: ma = v return (mi, ma) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def kdtree_get_choices(np.ndarray[np.float64_t, ndim=3] data, np.ndarray[np.float64_t, ndim=1] l_corner, np.ndarray[np.float64_t, ndim=1] r_corner): cdef int i, j, k, dim, n_unique, best_dim, n_grids, my_split n_grids = data.shape[0] cdef np.float64_t **uniquedims cdef np.float64_t *uniques cdef np.float64_t split uniquedims = alloca(3 * sizeof(np.float64_t*)) for i in range(3): uniquedims[i] = \ alloca(2*n_grids * sizeof(np.float64_t)) my_max = 0 best_dim = -1 my_split = -1 for dim in range(3): n_unique = 0 uniques = uniquedims[dim] for i in range(n_grids): # Check for disqualification for j in range(2): #print("Checking against", i,j,dim,data[i,j,dim]) if not (l_corner[dim] < data[i, j, dim] and data[i, j, dim] < r_corner[dim]): #print("Skipping ", data[i,j,dim]) continue skipit = 0 # Add our left ... for k in range(n_unique): if uniques[k] == data[i, j, dim]: skipit = 1 #print("Identified", uniques[k], data[i,j,dim], n_unique) break if skipit == 0: uniques[n_unique] = data[i, j, dim] n_unique += 1 if n_unique > my_max: best_dim = dim my_max = n_unique my_split = (n_unique-1)/2 # I recognize how lame this is. cdef np.ndarray[np.float64_t, ndim=1] tarr = np.empty(my_max, dtype='float64') for i in range(my_max): #print("Setting tarr: ", i, uniquedims[best_dim][i]) tarr[i] = uniquedims[best_dim][i] tarr.sort() if my_split < 0: raise RuntimeError split = tarr[my_split] cdef np.ndarray[np.uint8_t, ndim=1] less_ids = np.empty(n_grids, dtype='uint8') cdef np.ndarray[np.uint8_t, ndim=1] greater_ids = np.empty(n_grids, dtype='uint8') for i in range(n_grids): if data[i, 0, best_dim] < split: less_ids[i] = 1 else: less_ids[i] = 0 if data[i, 1, best_dim] > split: greater_ids[i] = 1 else: greater_ids[i] = 0 # Return out unique values return best_dim, split, less_ids.view("bool"), greater_ids.view("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_box_grids_level(np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] right_edge, int level, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels, np.ndarray[np.int32_t, ndim=1] mask, int min_index = 0): cdef int i, n cdef int nx = left_edges.shape[0] cdef int inside cdef np.float64_t eps = np.finfo(np.float64).eps for i in range(nx): if i < min_index or levels[i,0] != level: mask[i] = 0 continue inside = 1 for n in range(3): if (right_edges[i,n] - left_edge[n]) <= eps or \ (right_edge[n] - left_edges[i,n]) <= eps: inside = 0 break if inside == 1: mask[i] = 1 else: mask[i] = 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_box_grids_below_level( np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] right_edge, int level, np.ndarray[np.float64_t, ndim=2] left_edges, np.ndarray[np.float64_t, ndim=2] right_edges, np.ndarray[np.int32_t, ndim=2] levels, np.ndarray[np.int32_t, ndim=1] mask, int min_level = 0): cdef int i, n cdef int nx = left_edges.shape[0] cdef int inside cdef np.float64_t eps = np.finfo(np.float64).eps for i in range(nx): mask[i] = 0 if levels[i,0] <= level and levels[i,0] >= min_level: inside = 1 for n in range(3): if (right_edges[i,n] - left_edge[n]) <= eps or \ (right_edge[n] - left_edges[i,n]) <= eps: inside = 0 break if inside == 1: mask[i] = 1 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def obtain_position_vector( data, field_names = (("index", "x"), ("index", "y"), ("index", "z")) ): # This is just to let the pointers exist and whatnot. We can't cdef them # inside conditionals. cdef np.ndarray[np.float64_t, ndim=1] xf cdef np.ndarray[np.float64_t, ndim=1] yf cdef np.ndarray[np.float64_t, ndim=1] zf cdef np.ndarray[np.float64_t, ndim=2] rf cdef np.ndarray[np.float64_t, ndim=3] xg cdef np.ndarray[np.float64_t, ndim=3] yg cdef np.ndarray[np.float64_t, ndim=3] zg cdef np.ndarray[np.float64_t, ndim=4] rg cdef np.float64_t c[3] cdef int i, j, k units = data[field_names[0]].units center = data.get_field_parameter("center").to(units) c[0] = center[0]; c[1] = center[1]; c[2] = center[2] if len(data[field_names[0]].shape) == 1: # One dimensional data xf = data[field_names[0]] yf = data[field_names[1]] zf = data[field_names[2]] rf = YTArray(np.empty((3, xf.shape[0]), 'float64'), xf.units) for i in range(xf.shape[0]): rf[0, i] = xf[i] - c[0] rf[1, i] = yf[i] - c[1] rf[2, i] = zf[i] - c[2] return rf else: # Three dimensional data xg = data[field_names[0]] yg = data[field_names[1]] zg = data[field_names[2]] shape = (3, xg.shape[0], xg.shape[1], xg.shape[2]) rg = YTArray(np.empty(shape, 'float64'), xg.units) #rg = YTArray(rg, xg.units) for i in range(xg.shape[0]): for j in range(xg.shape[1]): for k in range(xg.shape[2]): rg[0,i,j,k] = xg[i,j,k] - c[0] rg[1,i,j,k] = yg[i,j,k] - c[1] rg[2,i,j,k] = zg[i,j,k] - c[2] return rg @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def obtain_relative_velocity_vector( data, field_names = (("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z")), bulk_vector = "bulk_velocity" ): # This is just to let the pointers exist and whatnot. We can't cdef them # inside conditionals. cdef np.ndarray[np.float64_t, ndim=1] vxf cdef np.ndarray[np.float64_t, ndim=1] vyf cdef np.ndarray[np.float64_t, ndim=1] vzf cdef np.ndarray[np.float64_t, ndim=2] rvf cdef np.ndarray[np.float64_t, ndim=3] vxg cdef np.ndarray[np.float64_t, ndim=3] vyg cdef np.ndarray[np.float64_t, ndim=3] vzg cdef np.ndarray[np.float64_t, ndim=4] rvg cdef np.float64_t bv[3] cdef int i, j, k, dim units = data[field_names[0]].units bulk_vector = data.get_field_parameter(bulk_vector).to(units) dim = data[field_names[0]].ndim if dim == 1: # One dimensional data vxf = data[field_names[0]].astype("float64") vyf = data[field_names[1]].astype("float64") vzf = data[field_names[2]].astype("float64") vyf.convert_to_units(vxf.units) vzf.convert_to_units(vxf.units) rvf = YTArray(np.empty((3, vxf.shape[0]), 'float64'), vxf.units) if bulk_vector is None: bv[0] = bv[1] = bv[2] = 0.0 else: bulk_vector = bulk_vector.in_units(vxf.units) bv[0] = bulk_vector[0] bv[1] = bulk_vector[1] bv[2] = bulk_vector[2] for i in range(vxf.shape[0]): rvf[0, i] = vxf[i] - bv[0] rvf[1, i] = vyf[i] - bv[1] rvf[2, i] = vzf[i] - bv[2] return rvf elif dim == 3: # Three dimensional data vxg = data[field_names[0]].astype("float64") vyg = data[field_names[1]].astype("float64") vzg = data[field_names[2]].astype("float64") vyg.convert_to_units(vxg.units) vzg.convert_to_units(vxg.units) shape = (3, vxg.shape[0], vxg.shape[1], vxg.shape[2]) rvg = YTArray(np.empty(shape, 'float64'), vxg.units) if bulk_vector is None: bv[0] = bv[1] = bv[2] = 0.0 else: bulk_vector = bulk_vector.in_units(vxg.units) bv[0] = bulk_vector[0] bv[1] = bulk_vector[1] bv[2] = bulk_vector[2] for i in range(vxg.shape[0]): for j in range(vxg.shape[1]): for k in range(vxg.shape[2]): rvg[0,i,j,k] = vxg[i,j,k] - bv[0] rvg[1,i,j,k] = vyg[i,j,k] - bv[1] rvg[2,i,j,k] = vzg[i,j,k] - bv[2] return rvg else: raise NotImplementedError(f"Unsupported dimensionality `{dim}`.") def grow_flagging_field(oofield): cdef np.ndarray[np.uint8_t, ndim=3] ofield = oofield.astype("uint8") cdef np.ndarray[np.uint8_t, ndim=3] nfield nfield = np.zeros_like(ofield) cdef int i, j, k, ni, nj, nk cdef int oi, oj, ok for ni in range(ofield.shape[0]): for nj in range(ofield.shape[1]): for nk in range(ofield.shape[2]): for oi in range(3): i = ni + (oi - 1) if i < 0 or i >= ofield.shape[0]: continue for oj in range(3): j = nj + (oj - 1) if j < 0 or j >= ofield.shape[1]: continue for ok in range(3): k = nk + (ok - 1) if k < 0 or k >= ofield.shape[2]: continue if ofield[i, j, k] == 1: nfield[ni, nj, nk] = 1 return nfield.astype("bool") @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_region( input_fields, output_fields, np.int32_t output_level, np.ndarray[np.int64_t, ndim=1] left_index, np.ndarray[np.int64_t, ndim=2] ipos, np.ndarray[np.int64_t, ndim=1] ires, np.ndarray[np.int64_t, ndim=1] level_dims, np.ndarray[np.int64_t, ndim=1] refine_by ): cdef int i, n cdef np.int64_t tot = 0, oi, oj, ok cdef np.int64_t rf[3] cdef np.int64_t iind[3] cdef np.int64_t oind[3] cdef np.int64_t dim[3] cdef np.ndarray[np.float64_t, ndim=3] ofield cdef np.ndarray[np.float64_t, ndim=1] ifield nf = len(input_fields) # The variable offsets governs for each dimension and each possible # wrapping if we do it. Then the wi, wj, wk indices check into each # [dim][wrap] inside the loops. cdef int wi, wj, wk cdef int offsets[3][3] cdef np.int64_t off for i in range(3): # Offsets here is a way of accounting for periodicity. It keeps track # of how to offset our grid as we loop over the icoords. dim[i] = output_fields[0].shape[i] offsets[i][0] = offsets[i][2] = 0 offsets[i][1] = 1 if left_index[i] < 0: offsets[i][2] = 1 if left_index[i] + dim[i] >= level_dims[i]: offsets[i][0] = 1 for n in range(nf): tot = 0 ofield = output_fields[n] ifield = input_fields[n] for i in range(ipos.shape[0]): for k in range(3): rf[k] = refine_by[k]**(output_level - ires[i]) for wi in range(3): if offsets[0][wi] == 0: continue off = (left_index[0] + level_dims[0]*(wi-1)) iind[0] = ipos[i, 0] * rf[0] - off # rf here is the "refinement factor", or, the number of zones # that this zone could potentially contribute to our filled # grid. for oi in range(rf[0]): # Now we need to apply our offset oind[0] = oi + iind[0] if oind[0] < 0: continue elif oind[0] >= dim[0]: break for wj in range(3): if offsets[1][wj] == 0: continue off = (left_index[1] + level_dims[1]*(wj-1)) iind[1] = ipos[i, 1] * rf[1] - off for oj in range(rf[1]): oind[1] = oj + iind[1] if oind[1] < 0: continue elif oind[1] >= dim[1]: break for wk in range(3): if offsets[2][wk] == 0: continue off = (left_index[2] + level_dims[2]*(wk-1)) iind[2] = ipos[i, 2] * rf[2] - off for ok in range(rf[2]): oind[2] = ok + iind[2] if oind[2] < 0: continue elif oind[2] >= dim[2]: break ofield[oind[0], oind[1], oind[2]] = \ ifield[i] tot += 1 return tot @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def flip_bitmask(np.ndarray[np.float64_t, ndim=1] vals, np.float64_t left_edge, np.float64_t right_edge, np.uint64_t nbins): cdef np.uint64_t i, bin_id cdef np.float64_t idx = nbins / (right_edge - left_edge) cdef np.ndarray[np.uint8_t, ndim=1, cast=True] bitmask bitmask = np.zeros(nbins, dtype="uint8") for i in range(vals.shape[0]): bin_id = ((vals[i] - left_edge)*idx) bitmask[bin_id] = 1 return bitmask #@cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def flip_morton_bitmask(np.ndarray[np.uint64_t, ndim=1] morton_indices, int max_order): # We assume that the morton_indices are fed to us in a setup that allows # for 20 levels. This means that we shift right by 3*(20-max_order) (or is # that a fencepost?) cdef np.uint64_t mi, i cdef np.ndarray[np.uint8_t, ndim=1, cast=True] bitmask # Note that this will fail if it's too big, since numpy will check nicely # the memory availability. I guess. bitmask = np.zeros(1 << (3*max_order), dtype="uint8") for i in range(morton_indices.shape[0]): mi = (morton_indices[i] >> (3 * (20-max_order))) bitmask[mi] = 1 return bitmask #@cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def count_collisions(np.ndarray[np.uint8_t, ndim=2] masks): cdef int i, j, k cdef np.ndarray[np.uint32_t, ndim=1] counts cdef np.ndarray[np.uint8_t, ndim=1] collides counts = np.zeros(masks.shape[1], dtype="uint32") collides = np.zeros(masks.shape[1], dtype="uint8") for i in range(masks.shape[1]): print(i) for j in range(masks.shape[1]): collides[j] = 0 for k in range(masks.shape[0]): if masks[k,i] == 0: continue for j in range(masks.shape[1]): if j == i: continue if masks[k,j] == 1: collides[j] = 1 counts[i] = collides.sum() return counts @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def fill_region_float(np.ndarray[np.float64_t, ndim=2] fcoords, np.ndarray[np.float64_t, ndim=2] fwidth, np.ndarray[np.float64_t, ndim=1] data, np.ndarray[np.float64_t, ndim=1] box_left_edge, np.ndarray[np.float64_t, ndim=1] box_right_edge, np.ndarray[np.float64_t, ndim=3] dest, int antialias = 1, period = None, int check_period = 1): cdef np.float64_t ds_period[3] cdef np.float64_t box_dds[3] cdef np.float64_t box_idds[3] cdef np.float64_t width[3] cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef np.int64_t i, j, k, p, xi, yi cdef np.int64_t dims[3] cdef np.int64_t ld[3] cdef np.int64_t ud[3] cdef np.float64_t overlap[3] cdef np.float64_t dsp cdef np.float64_t osp[3] cdef np.float64_t odsp[3] cdef np.float64_t sp[3] cdef np.float64_t lfd[3] cdef np.float64_t ufd[3] # These are the temp vars we get from the arrays # Some periodicity helpers cdef int diter[3][2] cdef np.float64_t diterv[3][2] if period is not None: for i in range(3): ds_period[i] = period[i] else: ds_period[0] = ds_period[1] = ds_period[2] = 0.0 box_left_edge = _ensure_code(box_left_edge) box_right_edge = _ensure_code(box_right_edge) _ensure_code(fcoords) _ensure_code(fwidth) for i in range(3): LE[i] = box_left_edge[i] RE[i] = box_right_edge[i] width[i] = RE[i] - LE[i] dims[i] = dest.shape[i] box_dds[i] = width[i] / dims[i] box_idds[i] = 1.0/box_dds[i] diter[i][0] = diter[i][1] = 0 diterv[i][0] = diterv[i][1] = 0.0 overlap[i] = 1.0 with nogil: for p in range(fcoords.shape[0]): for i in range(3): diter[i][1] = 999 odsp[i] = fwidth[p,i]*0.5 osp[i] = fcoords[p,i] # already centered overlap[i] = 1.0 dsp = data[p] if check_period == 1: for i in range(3): if (osp[i] - odsp[i] < LE[i]): diter[i][1] = +1 diterv[i][1] = ds_period[i] elif (osp[i] + odsp[i] > RE[i]): diter[i][1] = -1 diterv[i][1] = -ds_period[i] for xi in range(2): if diter[0][xi] == 999: continue sp[0] = osp[0] + diterv[0][xi] if (sp[0] + odsp[0] < LE[0]) or (sp[0] - odsp[0] > RE[0]): continue for yi in range(2): if diter[1][yi] == 999: continue sp[1] = osp[1] + diterv[1][yi] if (sp[1] + odsp[1] < LE[1]) or (sp[1] - odsp[1] > RE[1]): continue for zi in range(2): if diter[2][zi] == 999: continue sp[2] = osp[2] + diterv[2][zi] if (sp[2] + odsp[2] < LE[2]) or (sp[2] - odsp[2] > RE[2]): continue for i in range(3): ld[i] = fmax(((sp[i]-odsp[i]-LE[i])*box_idds[i]),0) # NOTE: This is a different way of doing it than in the C # routines. In C, we were implicitly casting the # initialization to int, but *not* the conditional, which # was allowed an extra value: # for(j=lc;j fmin(((sp[i]+odsp[i]-LE[i])*box_idds[i] + 1), dims[i]) for i in range(ld[0], ud[0]): if antialias == 1: lfd[0] = box_dds[0] * i + LE[0] ufd[0] = box_dds[0] * (i + 1) + LE[0] overlap[0] = ((fmin(ufd[0], sp[0]+odsp[0]) - fmax(lfd[0], (sp[0]-odsp[0])))*box_idds[0]) if overlap[0] < 0.0: continue for j in range(ld[1], ud[1]): if antialias == 1: lfd[1] = box_dds[1] * j + LE[1] ufd[1] = box_dds[1] * (j + 1) + LE[1] overlap[1] = ((fmin(ufd[1], sp[1]+odsp[1]) - fmax(lfd[1], (sp[1]-odsp[1])))*box_idds[1]) if overlap[1] < 0.0: continue for k in range(ld[2], ud[2]): if antialias == 1: lfd[2] = box_dds[2] * k + LE[2] ufd[2] = box_dds[2] * (k + 1) + LE[2] overlap[2] = ((fmin(ufd[2], sp[2]+odsp[2]) - fmax(lfd[2], (sp[2]-odsp[2])))*box_idds[2]) if overlap[2] < 0.0: continue dest[i,j,k] += dsp * (overlap[0]*overlap[1]*overlap[2]) else: dest[i,j,k] = dsp @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def gravitational_binding_energy( np.float64_t[:] mass, np.float64_t[:] x, np.float64_t[:] y, np.float64_t[:] z, int truncate, np.float64_t kinetic, int num_threads = 0): cdef int q_outer, q_inner, n_q cdef np.float64_t mass_o, x_o, y_o, z_o cdef np.float64_t mass_i, x_i, y_i, z_i cdef np.float64_t total_potential = 0. cdef np.float64_t this_potential n_q = mass.size pbar = get_pbar("Calculating potential for %d cells with %d thread(s)" % (n_q,num_threads), n_q) # using reversed iterator in order to make use of guided scheduling # (inner loop is getting more and more expensive) for q_outer in prange(n_q - 1,-1,-1, nogil=True,schedule='guided',num_threads=num_threads): this_potential = 0. mass_o = mass[q_outer] x_o = x[q_outer] y_o = y[q_outer] z_o = z[q_outer] for q_inner in range(q_outer + 1, n_q): mass_i = mass[q_inner] x_i = x[q_inner] y_i = y[q_inner] z_i = z[q_inner] # not using += operator so that variable is not automatically reduced this_potential = this_potential + mass_o * mass_i / \ sqrt((x_i - x_o) * (x_i - x_o) + (y_i - y_o) * (y_i - y_o) + (z_i - z_o) * (z_i - z_o)) total_potential += this_potential if truncate and this_potential / kinetic > 1.: break with gil: PyErr_CheckSignals() # this call is not thread safe, but it gives a reasonable approximation pbar.update() pbar.finish() return total_potential # The OnceIndirect code is from: # http://stackoverflow.com/questions/10465091/assembling-a-cython-memoryview-from-numpy-arrays/12991519#12991519 # This is under the CC-BY-SA license. cdef class OnceIndirect: cdef object _objects cdef void** buf cdef int ndim cdef int n_rows cdef int buf_len cdef Py_ssize_t* shape cdef Py_ssize_t* strides cdef Py_ssize_t* suboffsets cdef Py_ssize_t itemsize cdef bytes format cdef int is_readonly def __cinit__(self, object rows, want_writable=True, want_format=True, allow_indirect=False): """ Set want_writable to False if you don't want writable data. (This may prevent copies.) Set want_format to False if your input doesn't support PyBUF_FORMAT (unlikely) Set allow_indirect to True if you are ok with the memoryview being indirect in dimensions other than the first. (This may prevent copies.) An example usage: cdef double[::cython.view.indirect, ::1] vectors = OnceIndirect([object.vector for object in objects]) """ demand = buffer.PyBUF_INDIRECT if allow_indirect else buffer.PyBUF_STRIDES if want_writable: demand |= buffer.PyBUF_WRITABLE if want_format: demand |= buffer.PyBUF_FORMAT self._objects = [memoryview(row, demand) for row in rows] self.n_rows = len(self._objects) self.buf_len = sizeof(void*) * self.n_rows self.buf = malloc(self.buf_len) self.ndim = 1 + self._objects[0].ndim self.shape = malloc(sizeof(Py_ssize_t) * self.ndim) self.strides = malloc(sizeof(Py_ssize_t) * self.ndim) self.suboffsets = malloc(sizeof(Py_ssize_t) * self.ndim) cdef memoryview example_obj = self._objects[0] self.itemsize = example_obj.itemsize if want_format: self.format = example_obj.view.format else: self.format = None self.is_readonly |= example_obj.view.readonly for dim in range(self.ndim): if dim == 0: self.shape[dim] = self.n_rows self.strides[dim] = sizeof(void*) self.suboffsets[dim] = 0 else: self.shape[dim] = example_obj.view.shape[dim - 1] self.strides[dim] = example_obj.view.strides[dim - 1] if example_obj.view.suboffsets == NULL: self.suboffsets[dim] = -1 else: self.suboffsets[dim] = example_obj.suboffsets[dim - 1] cdef memoryview obj cdef int i = 0 for obj in self._objects: assert_similar(example_obj, obj) self.buf[i] = obj.view.buf i += 1 def __getbuffer__(self, Py_buffer* buff, int flags): if (flags & buffer.PyBUF_INDIRECT) != buffer.PyBUF_INDIRECT: raise Exception("don't want to copy data") if flags & buffer.PyBUF_WRITABLE and self.is_readonly: raise Exception("couldn't provide writable, you should have demanded it earlier") if flags & buffer.PyBUF_FORMAT: if self.format is None: raise Exception("couldn't provide format, you should have demanded it earlier") buff.format = self.format else: buff.format = NULL buff.buf = self.buf buff.obj = self buff.len = self.buf_len buff.readonly = self.is_readonly buff.ndim = self.ndim buff.shape = self.shape buff.strides = self.strides buff.suboffsets = self.suboffsets buff.itemsize = self.itemsize buff.internal = NULL def __dealloc__(self): free(self.buf) free(self.shape) free(self.strides) free(self.suboffsets) cdef int assert_similar(memoryview left_, memoryview right_) except -1: cdef Py_buffer left = left_.view cdef Py_buffer right = right_.view assert left.ndim == right.ndim cdef int i for i in range(left.ndim): assert left.shape[i] == right.shape[i], (left_.shape, right_.shape) assert left.strides[i] == right.strides[i], (left_.strides, right_.strides) if left.suboffsets == NULL: assert right.suboffsets == NULL, (left_.suboffsets, right_.suboffsets) else: for i in range(left.ndim): assert left.suboffsets[i] == right.suboffsets[i], (left_.suboffsets, right_.suboffsets) if left.format == NULL: assert right.format == NULL, (bytes(left.format), bytes(right.format)) else: #alternatively, compare as Python strings: #assert bytes(left.format) == bytes(right.format) assert strcmp(left.format, right.format) == 0, (bytes(left.format), bytes(right.format)) return 0 def _obtain_coords_and_widths(np.int64_t[:] icoords, np.int64_t[:] ires, np.float64_t[:] cell_widths, np.float64_t offset): # This function only accepts *one* axis of icoords, because we will be # looping over the axes in python. This also simplifies cell_width # allocation and computation. cdef np.ndarray[np.float64_t, ndim=1] fcoords = np.zeros(icoords.size, dtype="f8") cdef np.ndarray[np.float64_t, ndim=1] fwidth = np.zeros(icoords.size, dtype="f8") cdef np.ndarray[np.float64_t, ndim=1] cell_centers = np.zeros(cell_widths.size, dtype="f8") cdef int i cdef np.float64_t pos = offset for i in range(cell_widths.size): cell_centers[i] = pos + 0.5 * cell_widths[i] pos += cell_widths[i] for i in range(icoords.size): fcoords[i] = cell_centers[icoords[i]] fwidth[i] = cell_widths[icoords[i]] return fcoords, fwidth yt-project-yt-f043ac8/yt/utilities/lib/octree_raytracing.py000066400000000000000000000075741510711153200241620ustar00rootroot00000000000000from itertools import product import numpy as np from yt.funcs import mylog from yt.utilities.lib._octree_raytracing import _OctreeRayTracing class OctreeRayTracing: octree = None data_source = None log_fields = None fields = None # Internal data _cell_index = None _tvalues = None def __init__(self, data_source): self.data_source = data_source ds = data_source.ds LE = np.array([0, 0, 0], dtype=np.float64) RE = np.array([1, 1, 1], dtype=np.float64) lvl_min = ds.min_level + 1 # This is the max refinement so that the smallest cells have size # 1/2**depth depth = lvl_min + ds.max_level + 1 self.octree = _OctreeRayTracing(LE, RE, depth) ds = data_source.ds xyz = np.stack([data_source[key].to_value("unitary") for key in "xyz"], axis=-1) lvl = data_source["grid_level"].value.astype("int64", copy=False) + lvl_min ipos = np.floor(xyz * (1 << depth)).astype("int64") mylog.debug("Adding cells to volume") self.octree.add_nodes( ipos.astype(np.int32), lvl.astype(np.int32), np.arange(len(ipos), dtype=np.int32), ) def vertex_centered_data(self, field): data_source = self.data_source chunks = data_source.index._chunk(data_source, "spatial", ngz=1) finfo = data_source.ds._get_field_info(field) units = finfo.units rv = data_source.ds.arr( np.zeros((2, 2, 2, data_source.ires.size), dtype="float64"), units ) binary_3D_index_iter = product(*[range(2)] * 3) ind = {(i, j, k): 0 for i, j, k in binary_3D_index_iter} for chunk in chunks: with data_source._chunked_read(chunk): gz = data_source._current_chunk.objs[0] gz.field_parameters = data_source.field_parameters wogz = gz._base_grid vertex_data = gz.get_vertex_centered_data([field])[field] for i, j, k in product(*[range(2)] * 3): ind[i, j, k] += wogz.select( data_source.selector, vertex_data[i : i + 2, j : j + 2, k : k + 2, ...], rv[i, j, k, :], ind[i, j, k], ) return rv def set_fields(self, fields, log_fields, no_ghost, force=False): if no_ghost: raise NotImplementedError("Ghost zones are required with Octree datasets") if len(fields) != 1: raise ValueError( "Can only set one fields at a time. " "This is likely a bug, and should be reported." ) field = self.data_source._determine_fields(fields)[0] take_log = log_fields[0] vertex_data = self.vertex_centered_data(field) if take_log: vertex_data = np.log10(vertex_data) # Vertex_data has shape (2, 2, 2, ...) # Note: here we have the wrong ordering within the oct (classical Fortran/C # ordering issue) so we need to swap axis 0 and 2. self.data = vertex_data.swapaxes(0, 2).reshape(8, -1) def cast_rays(self, vp_pos, vp_dir): """Cast the rays through the oct. Parameters ---------- vp_pos : float arrays (Nrays, Ndim) vp_dir : float arrays (Nrays, Ndim) The position (unitary) and direction of each ray Returns ------- cell_index : list of integer arrays of shape (Ncell) For each ray, contains an ordered array of cell ids that it intersects with tvalues : list of float arrays of shape (Ncell, 2) The t value at entry and exit for each cell. """ if not self._cell_index: self._cell_index, self._tvalues = self.octree.cast_rays(vp_pos, vp_dir) return self._cell_index, self._tvalues yt-project-yt-f043ac8/yt/utilities/lib/origami.pyx000066400000000000000000000031521510711153200222610ustar00rootroot00000000000000# distutils: sources = yt/utilities/lib/origami_tags.c # distutils: include_dirs = LIB_DIR # distutils: depends = yt/utilities/lib/origami_tags.h # distutils: libraries = STD_LIBS """ This calls the ORIGAMI routines """ import numpy as np cimport numpy as np from libc.stdlib cimport free, malloc cdef extern from "origami_tags.h": int compute_tags(int ng, double boxsize, double **r, int npart, unsigned char *m) cdef int printed_citation = 0 def run_origami(np.ndarray[np.float64_t, ndim=1] pos_x, np.ndarray[np.float64_t, ndim=1] pos_y, np.ndarray[np.float64_t, ndim=1] pos_z, double boxsize): # We assume these have been passed in in the correct order and # C-contiguous. global printed_citation if printed_citation == 0: print("ORIGAMI was developed by Bridget Falck and Mark Neyrinck.") print("Please cite Falck, Neyrinck, & Szalay 2012, ApJ, 754, 2, 125.") printed_citation = 1 cdef int npart = pos_x.size if npart == 1: return np.zeros(1, dtype="uint8") assert(sizeof(unsigned char) == sizeof(np.uint8_t)) assert(sizeof(double) == sizeof(np.float64_t)) cdef int ng = np.round(npart**(1./3)) assert(ng**3 == npart) cdef double **r = malloc(sizeof(double *) * 3) r[0] = pos_x.data r[1] = pos_y.data r[2] = pos_z.data cdef np.ndarray[np.uint8_t, ndim=1] tags = np.zeros(npart, dtype="uint8") cdef void *m = tags.data compute_tags(ng, boxsize, r, npart, m) free(r) return tags yt-project-yt-f043ac8/yt/utilities/lib/origami_tags.c000066400000000000000000000133021510711153200226770ustar00rootroot00000000000000// This code was originally written by Bridget Falck and Mark Neyrinck. // They have agreed to release it under the terms of the BSD license. #include "origami_tags.h" int isneg(int h) { return (int)(h < 0); } int par(int i, int j, int k, int ng) { return i + (j + k*ng)*ng; } int compute_tags(int ng, double boxsize, double **r, int np, unsigned char *m) { /* Note that the particles must be fed in according to the order specified in * the README file */ double xmin,xmax,ymin,ymax,zmin,zmax; double negb2, b2; int ng4, h, i,i2, x,y,z,nhalo,nhalo0,nhalo1,nhalo2,nhaloany; unsigned char *m0,*m1,*m2, mn,m0n,m1n,m2n; /*Morphology tag */ double dx,d1,d2; b2 = boxsize/2.; negb2 = -boxsize/2.; ng4=ng/4; /* Boxsize should be the range in r, yielding a range 0-1 */ printf("%d particles\n",np);fflush(stdout); xmin = BF; xmax = -BF; ymin = BF; ymax = -BF; zmin = BF; zmax = -BF; m0 = (unsigned char *)malloc(np*sizeof(unsigned char)); /* for the diagonals */ m1 = (unsigned char *)malloc(np*sizeof(unsigned char)); m2 = (unsigned char *)malloc(np*sizeof(unsigned char)); for (i=0; ixmax) xmax = r[0][i]; if (r[1][i]ymax) ymax = r[1][i]; if (r[2][i]zmax) zmax = r[2][i]; m[i] = 1; m0[i] = 1; m1[i] = 1; m2[i] = 1; } if (m==NULL) { printf("Morphology array cannot be allocated.\n"); return 1; } // printf("np: %d, x: %f,%f; y: %f,%f; z: %f,%f\n",np,xmin,xmax, ymin,ymax, zmin,zmax); fflush(stdout); printf("Calculating ORIGAMI morphology.\n"); for (x=0; x b2) dx -= boxsize; if (dx < 0.) { /*printf("x:%d %d %d %d %f\n",x,y,z,h,dx);*/ if (m[i] % 2 > 0) { m[i] *= 2; } if (m[i2] % 2 > 0){ m[i2] *= 2; } break; } } for (h=1; h b2) dx -= boxsize; if (dx < 0.) { /*printf("y:%d %d %d %d %f\n",x,y,z,h,dx);*/ if (m[i] % 3 > 0) { m[i] *= 3; } if (m[i2] % 3 > 0){ m[i2] *= 3; } break; } } for (h=1; h b2) dx -= boxsize; if (dx < 0.) { /*printf("z:%d %d %d %d %f\n",x,y,z,h,dx);*/ if (m[i] % 5 > 0) { m[i] *= 5; } if (m[i2] % 5 > 0){ m[i2] *= 5; } break; } } // Now do diagonal directions for (h=1; h b2) d1 -= boxsize; if (d2 < negb2) d2 += boxsize; if (d2 > b2) d2 -= boxsize; if ((d1 + d2)*h < 0.) { m0[i] *= 2; break; } } for (h=1; h b2) d1 -= boxsize; if (d2 < negb2) d2 += boxsize; if (d2 > b2) d2 -= boxsize; if ((d1 - d2)*h < 0.) { m0[i] *= 3; break; } } // y for (h=1; h b2) d1 -= boxsize; if (d2 < negb2) d2 += boxsize; if (d2 > b2) d2 -= boxsize; if ((d1 + d2)*h < 0.) { m1[i] *= 2; break; } } for (h=1; h b2) d1 -= boxsize; if (d2 < negb2) d2 += boxsize; if (d2 > b2) d2 -= boxsize; if ((d1 - d2)*h < 0.) { m1[i] *= 3; break; } } // z for (h=1; h b2) d1 -= boxsize; if (d2 < negb2) d2 += boxsize; if (d2 > b2) d2 -= boxsize; if ((d1 + d2)*h < 0.) { m2[i] *=2; break; } } for (h=1; h b2) d1 -= boxsize; if (d2 < negb2) d2 += boxsize; if (d2 > b2) d2 -= boxsize; if ((d1 - d2)*h < 0.) { m2[i] *= 3; break; } } } } } nhalo = 0; nhalo0 = 0; nhalo1 = 0; nhalo2 = 0; nhaloany = 0; for (i=0;i #include #define BF 1e30 #define max(A,B) (((A)>(B)) ? (A):(B)) #define goodmod(A,B) (((A) >= (B)) ? (A-B):(((A) < 0) ? (A+B):(A))) int isneg(int h); int par(int i, int j, int k, int ng); int compute_tags(int ng, double boxsize, double **r, int npart, unsigned char *m); #endif // __ORIGAMI_TAGS_H__ yt-project-yt-f043ac8/yt/utilities/lib/particle_kdtree_tools.pxd000066400000000000000000000010141510711153200251610ustar00rootroot00000000000000cimport cython cimport numpy as np from yt.utilities.lib.bounded_priority_queue cimport BoundedPriorityQueue from yt.utilities.lib.cykdtree.kdtree cimport KDTree, uint64_t cdef struct axes_range: int start int stop int step cdef int set_axes_range(axes_range *axes, int skipaxis) cdef int find_neighbors(np.float64_t * pos, np.float64_t[:, ::1] tree_positions, BoundedPriorityQueue queue, KDTree * c_tree, uint64_t skipidx, axes_range * axes) except -1 nogil yt-project-yt-f043ac8/yt/utilities/lib/particle_kdtree_tools.pyx000066400000000000000000000306311510711153200252150ustar00rootroot00000000000000# distutils: language = c++ """ Cython tools for working with the PyKDTree particle KDTree. """ import numpy as np cimport cython cimport numpy as np from cpython.exc cimport PyErr_CheckSignals from libc.math cimport sqrt from yt.utilities.lib.cykdtree.kdtree cimport KDTree, Node, PyKDTree, uint32_t, uint64_t from yt.funcs import get_pbar from yt.geometry.particle_deposit cimport get_kernel_func, kernel_func from yt.utilities.lib.bounded_priority_queue cimport BoundedPriorityQueue, NeighborList cdef int CHUNKSIZE = 4096 # This structure allows the nearest neighbor finding to consider a subset of # spatial dimensions, i.e the spatial separation in the x and z coordinates # could be consider by using set_axes_range(axes, 1), this would cause the while # loops to skip the y dimensions, without the performance hit of an if statement cdef struct axes_range: int start int stop int step # skipaxis: x=0, y=1, z=2 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef int set_axes_range(axes_range *axes, int skipaxis): axes.start = 0 axes.stop = 3 axes.step = 1 if skipaxis == 0: axes.start = 1 if skipaxis == 1: axes.step = 2 if skipaxis == 2: axes.stop = 2 return 0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def generate_smoothing_length(np.float64_t[:, ::1] tree_positions, PyKDTree kdtree, int n_neighbors): """Calculate array of distances to the nth nearest neighbor Parameters ---------- tree_positions: arrays of floats with shape (n_particles, 3) The positions of particles in kdtree sorted order. Currently assumed to be 3D positions. kdtree: A PyKDTree instance A kdtree to do nearest neighbors searches with n_neighbors: The neighbor number to calculate the distance to Returns ------- smoothing_lengths: arrays of floats with shape (n_particles, ) The calculated smoothing lengths """ cdef int i cdef KDTree * c_tree = kdtree._tree cdef int n_particles = tree_positions.shape[0] cdef np.float64_t * pos cdef np.float64_t[:] smoothing_length = np.empty(n_particles) cdef BoundedPriorityQueue queue = BoundedPriorityQueue(n_neighbors) # We are using all spatial dimensions cdef axes_range axes set_axes_range(&axes, -1) pbar = get_pbar("Generate smoothing length", n_particles) with nogil: for i in range(n_particles): # Reset queue to "empty" state, doing it this way avoids # needing to reallocate memory queue.size = 0 if i % CHUNKSIZE == 0: with gil: pbar.update(i-1) PyErr_CheckSignals() pos = &(tree_positions[i, 0]) find_neighbors(pos, tree_positions, queue, c_tree, i, &axes) smoothing_length[i] = sqrt(queue.heap_ptr[0]) pbar.update(n_particles-1) pbar.finish() return np.asarray(smoothing_length) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def estimate_density(np.float64_t[:, ::1] tree_positions, np.float64_t[:] mass, np.float64_t[:] smoothing_length, PyKDTree kdtree, kernel_name="cubic"): """Estimate density using SPH gather method. Parameters ---------- tree_positions: array of floats with shape (n_particles, 3) The positions of particles in kdtree sorted order. Currently assumed to be 3D positions. mass: array of floats with shape (n_particles) The masses of particles in kdtree sorted order. smoothing_length: array of floats with shape (n_particles) The smoothing lengths of particles in kdtree sorted order. kdtree: A PyKDTree instance A kdtree to do nearest neighbors searches with. kernel_name: str The name of the kernel function to use in density estimation. Returns ------- density: array of floats with shape (n_particles) The calculated density. """ cdef int i, j, k cdef KDTree * c_tree = kdtree._tree cdef int n_particles = tree_positions.shape[0] cdef np.float64_t h_i2, ih_i2, q_ij cdef np.float64_t * pos cdef np.float64_t[:] density = np.empty(n_particles) cdef kernel_func kernel = get_kernel_func(kernel_name) cdef NeighborList nblist = NeighborList() # We are using all spatial dimensions cdef axes_range axes set_axes_range(&axes, -1) pbar = get_pbar("Estimating density", n_particles) with nogil: for i in range(n_particles): # Reset list to "empty" state, doing it this way avoids # needing to reallocate memory nblist.size = 0 if i % CHUNKSIZE == 0: with gil: pbar.update(i - 1) PyErr_CheckSignals() pos = &(tree_positions[i, 0]) h_i2 = smoothing_length[i] ** 2 find_neighbors_ball(pos, h_i2, tree_positions, nblist, c_tree, i, &axes) ih_i2 = 1.0 / h_i2 # See eq. 10 of Price 2012 density[i] = mass[i] * kernel(0) for k in range(nblist.size): j = nblist.pids[k] q_ij = sqrt(nblist.data[k] * ih_i2) density[i] += mass[j] * kernel(q_ij) pbar.update(n_particles - 1) pbar.finish() return np.asarray(density) @cython.boundscheck(False) @cython.wraparound(False) cdef int find_neighbors(np.float64_t * pos, np.float64_t[:, ::1] tree_positions, BoundedPriorityQueue queue, KDTree * c_tree, uint64_t skipidx, axes_range * axes) except -1 nogil: cdef Node* leafnode # Make an initial guess based on the closest node leafnode = c_tree.search(&pos[0]) process_node_points(leafnode, queue, tree_positions, pos, skipidx, axes) # Traverse the rest of the kdtree to finish the neighbor list find_knn(c_tree.root, queue, tree_positions, pos, leafnode.leafid, skipidx, axes) return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef int find_knn(Node* node, BoundedPriorityQueue queue, np.float64_t[:, ::1] tree_positions, np.float64_t* pos, uint32_t skipleaf, uint64_t skipidx, axes_range * axes, ) except -1 nogil: # if we aren't a leaf then we keep traversing until we find a leaf, else we # we actually begin to check the leaf if not node.is_leaf: if not cull_node(node.less, pos, queue, skipleaf, axes): find_knn(node.less, queue, tree_positions, pos, skipleaf, skipidx, axes) if not cull_node(node.greater, pos, queue, skipleaf, axes): find_knn(node.greater, queue, tree_positions, pos, skipleaf, skipidx, axes) else: if not cull_node(node, pos, queue, skipleaf, axes): process_node_points(node, queue, tree_positions, pos, skipidx, axes) return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef inline int cull_node(Node* node, np.float64_t* pos, BoundedPriorityQueue queue, uint32_t skipleaf, axes_range * axes, ) except -1 nogil: cdef int k cdef np.float64_t v cdef np.float64_t tpos, ndist = 0 if node.leafid == skipleaf: return True k = axes.start while k < axes.stop: v = pos[k] if v < node.left_edge[k]: tpos = node.left_edge[k] - v elif v > node.right_edge[k]: tpos = v - node.right_edge[k] else: tpos = 0 ndist += tpos*tpos k += axes.step return (ndist > queue.heap[0] and queue.size == queue.max_elements) @cython.boundscheck(False) @cython.wraparound(False) cdef inline int process_node_points(Node* node, BoundedPriorityQueue queue, np.float64_t[:, ::1] positions, np.float64_t* pos, int skipidx, axes_range * axes, ) except -1 nogil: cdef uint64_t i, k cdef np.float64_t tpos, sq_dist for i in range(node.left_idx, node.left_idx + node.children): if i == skipidx: continue sq_dist = 0.0 k = axes.start while k < axes.stop: tpos = positions[i, k] - pos[k] sq_dist += tpos*tpos k += axes.step queue.add_pid(sq_dist, i) return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef int find_neighbors_ball(np.float64_t * pos, np.float64_t r2, np.float64_t[:, ::1] tree_positions, NeighborList nblist, KDTree * c_tree, uint64_t skipidx, axes_range * axes ) except -1 nogil: """Find neighbors within a ball.""" cdef Node* leafnode # Make an initial guess based on the closest node leafnode = c_tree.search(&pos[0]) process_node_points_ball(leafnode, nblist, tree_positions, pos, r2, skipidx, axes) # Traverse the rest of the kdtree to finish the neighbor list find_ball(c_tree.root, nblist, tree_positions, pos, r2, leafnode.leafid, skipidx, axes) return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef int find_ball(Node* node, NeighborList nblist, np.float64_t[:, ::1] tree_positions, np.float64_t* pos, np.float64_t r2, uint32_t skipleaf, uint64_t skipidx, axes_range * axes, ) except -1 nogil: """Traverse the k-d tree to process leaf nodes.""" if not node.is_leaf: if not cull_node_ball(node.less, pos, r2, skipleaf, axes): find_ball(node.less, nblist, tree_positions, pos, r2, skipleaf, skipidx, axes) if not cull_node_ball(node.greater, pos, r2, skipleaf, axes): find_ball(node.greater, nblist, tree_positions, pos, r2, skipleaf, skipidx, axes) else: if not cull_node_ball(node, pos, r2, skipleaf, axes): process_node_points_ball(node, nblist, tree_positions, pos, r2, skipidx, axes) return 0 @cython.boundscheck(False) @cython.wraparound(False) cdef inline int cull_node_ball(Node* node, np.float64_t* pos, np.float64_t r2, uint32_t skipleaf, axes_range * axes, ) except -1 nogil: """Check if the node does not intersect with the ball at all.""" cdef int k cdef np.float64_t v cdef np.float64_t tpos, ndist = 0 if node.leafid == skipleaf: return True k = axes.start while k < axes.stop: v = pos[k] if v < node.left_edge[k]: tpos = node.left_edge[k] - v elif v > node.right_edge[k]: tpos = v - node.right_edge[k] else: tpos = 0 ndist += tpos*tpos k += axes.step return ndist > r2 @cython.boundscheck(False) @cython.wraparound(False) cdef inline int process_node_points_ball(Node* node, NeighborList nblist, np.float64_t[:, ::1] positions, np.float64_t* pos, np.float64_t r2, int skipidx, axes_range * axes, ) except -1 nogil: """Add points from the leaf node within the ball to the neighbor list.""" cdef uint64_t i, k cdef np.float64_t tpos, sq_dist for i in range(node.left_idx, node.left_idx + node.children): if i == skipidx: continue sq_dist = 0.0 k = axes.start while k < axes.stop: tpos = positions[i, k] - pos[k] sq_dist += tpos*tpos k += axes.step if (sq_dist < r2): nblist.add_pid(sq_dist, i) return 0 yt-project-yt-f043ac8/yt/utilities/lib/particle_mesh_operations.pyx000066400000000000000000000343141510711153200257200ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Simple integrators for the radiative transfer equation """ cimport cython cimport numpy as np import numpy as np from yt.utilities.lib.fp_utils cimport fclip @cython.boundscheck(False) @cython.wraparound(False) def CICDeposit_3(np.ndarray[np.float64_t, ndim=1] posx, np.ndarray[np.float64_t, ndim=1] posy, np.ndarray[np.float64_t, ndim=1] posz, np.ndarray[np.float64_t, ndim=1] mass, np.int64_t npositions, np.ndarray[np.float64_t, ndim=3] field, np.ndarray[np.float64_t, ndim=1] leftEdge, np.ndarray[np.int32_t, ndim=1] gridDimension, np.float64_t cellSize): cdef int i1, j1, k1, n cdef np.float64_t xpos, ypos, zpos cdef np.float64_t fact, edge0, edge1, edge2 cdef np.float64_t le0, le1, le2 cdef np.float64_t dx, dy, dz, dx2, dy2, dz2 edge0 = ( gridDimension[0]) - 0.5001 edge1 = ( gridDimension[1]) - 0.5001 edge2 = ( gridDimension[2]) - 0.5001 fact = 1.0 / cellSize le0 = leftEdge[0] le1 = leftEdge[1] le2 = leftEdge[2] for n in range(npositions): # Compute the position of the central cell xpos = (posx[n] - le0)*fact ypos = (posy[n] - le1)*fact zpos = (posz[n] - le2)*fact if (xpos < 0.5001) or (xpos > edge0): continue if (ypos < 0.5001) or (ypos > edge1): continue if (zpos < 0.5001) or (zpos > edge2): continue i1 = (xpos + 0.5) j1 = (ypos + 0.5) k1 = (zpos + 0.5) # Compute the weights dx = ( i1) + 0.5 - xpos dy = ( j1) + 0.5 - ypos dz = ( k1) + 0.5 - zpos dx2 = 1.0 - dx dy2 = 1.0 - dy dz2 = 1.0 - dz # Interpolate from field into sumfield field[i1-1,j1-1,k1-1] += mass[n] * dx * dy * dz field[i1 ,j1-1,k1-1] += mass[n] * dx2 * dy * dz field[i1-1,j1 ,k1-1] += mass[n] * dx * dy2 * dz field[i1 ,j1 ,k1-1] += mass[n] * dx2 * dy2 * dz field[i1-1,j1-1,k1 ] += mass[n] * dx * dy * dz2 field[i1 ,j1-1,k1 ] += mass[n] * dx2 * dy * dz2 field[i1-1,j1 ,k1 ] += mass[n] * dx * dy2 * dz2 field[i1 ,j1 ,k1 ] += mass[n] * dx2 * dy2 * dz2 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def CICDeposit_2(np.float64_t[:] posx, np.float64_t[:] posy, np.float64_t[:] mass, np.int64_t npositions, np.float64_t[:, :] field, np.uint8_t[:, :] field_mask, np.float64_t[:] x_bin_edges, np.float64_t[:] y_bin_edges): cdef int i1, j1, n cdef np.float64_t xpos, ypos cdef np.float64_t edgex, edgey cdef np.float64_t dx, dy, ddx, ddy, ddx2, ddy2 edgex = ( x_bin_edges.shape[0] - 1) + 0.5001 edgey = ( y_bin_edges.shape[0] - 1) + 0.5001 # We are always dealing with uniformly spaced bins for CiC dx = x_bin_edges[1] - x_bin_edges[0] dy = y_bin_edges[1] - y_bin_edges[0] for n in range(npositions): # Compute the position of the central cell xpos = (posx[n] - x_bin_edges[0])/dx ypos = (posy[n] - y_bin_edges[0])/dy if (xpos < -0.5001) or (xpos > edgex): continue if (ypos < -0.5001) or (ypos > edgey): continue i1 = (xpos + 0.5) j1 = (ypos + 0.5) # Compute the weights ddx = ( i1) + 0.5 - xpos ddy = ( j1) + 0.5 - ypos ddx2 = 1.0 - ddx ddy2 = 1.0 - ddy # Deposit onto field if i1 > 0 and j1 > 0: field[i1-1,j1-1] += mass[n] * ddx * ddy field_mask[i1-1,j1-1] = 1 if j1 > 0 and i1 < field.shape[0]: field[i1 ,j1-1] += mass[n] * ddx2 * ddy field_mask[i1,j1-1] = 1 if i1 > 0 and j1 < field.shape[1]: field[i1-1,j1 ] += mass[n] * ddx * ddy2 field_mask[i1-1,j1] = 1 if i1 < field.shape[0] and j1 < field.shape[1]: field[i1 ,j1 ] += mass[n] * ddx2 * ddy2 field_mask[i1,j1] = 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def NGPDeposit_2(np.float64_t[:] posx, np.float64_t[:] posy, np.float64_t[:] mass, np.int64_t npositions, np.float64_t[:, :] field, np.uint8_t[:, :] field_mask, np.float64_t[:] x_bin_edges, np.float64_t[:] y_bin_edges): cdef int i, j, i1, j1, n cdef np.float64_t xpos, ypos cdef np.float64_t[2] x_endpoints cdef np.float64_t[2] y_endpoints x_endpoints = (x_bin_edges[0], x_bin_edges[x_bin_edges.shape[0] - 1]) y_endpoints = (y_bin_edges[0], y_bin_edges[y_bin_edges.shape[0] - 1]) for n in range(npositions): xpos = posx[n] ypos = posy[n] if (xpos < x_endpoints[0]) or (xpos > x_endpoints[1]): continue if (ypos < y_endpoints[0]) or (ypos > y_endpoints[1]): continue for i in range(x_bin_edges.shape[0]): if (xpos >= x_bin_edges[i]) and (xpos < x_bin_edges[i+1]): i1 = i break for j in range(y_bin_edges.shape[0]): if (ypos >= y_bin_edges[j]) and (ypos < y_bin_edges[j+1]): j1 = j break # Deposit onto field field[i1,j1] += mass[n] field_mask[i1,j1] = 1 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def sample_field_at_positions(np.ndarray[np.float64_t, ndim=3] arr, np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] right_edge, np.ndarray[np.float64_t, ndim=1] pos_x, np.ndarray[np.float64_t, ndim=1] pos_y, np.ndarray[np.float64_t, ndim=1] pos_z): cdef np.float64_t idds[3] cdef int dims[3] cdef int ind[3] cdef int i, npart npart = pos_x.shape[0] cdef np.ndarray[np.float64_t, ndim=1] sample sample = np.zeros(npart, dtype='float64') for i in range(3): dims[i] = arr.shape[i] idds[i] = ( dims[i]) / (right_edge[i] - left_edge[i]) for i in range(npart): if not ((left_edge[0] <= pos_x[i] <= right_edge[0]) and (left_edge[1] <= pos_y[i] <= right_edge[1]) and (left_edge[2] <= pos_z[i] <= right_edge[2])): continue ind[0] = ((pos_x[i] - left_edge[0]) * idds[0]) ind[1] = ((pos_y[i] - left_edge[1]) * idds[1]) ind[2] = ((pos_z[i] - left_edge[2]) * idds[2]) sample[i] = arr[ind[0], ind[1], ind[2]] return sample @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def CICSample_3(np.ndarray[np.float64_t, ndim=1] posx, np.ndarray[np.float64_t, ndim=1] posy, np.ndarray[np.float64_t, ndim=1] posz, np.ndarray[np.float64_t, ndim=1] sample, np.int64_t npositions, np.ndarray[np.float64_t, ndim=3] field, np.ndarray[np.float64_t, ndim=1] leftEdge, np.ndarray[np.int32_t, ndim=1] gridDimension, np.float64_t cellSize): cdef int i1, j1, k1, n cdef np.float64_t xpos, ypos, zpos cdef np.float64_t fact, edge0, edge1, edge2 cdef np.float64_t le0, le1, le2 cdef np.float64_t dx, dy, dz, dx2, dy2, dz2 edge0 = ( gridDimension[0]) - 0.5001 edge1 = ( gridDimension[1]) - 0.5001 edge2 = ( gridDimension[2]) - 0.5001 fact = 1.0 / cellSize le0 = leftEdge[0] le1 = leftEdge[1] le2 = leftEdge[2] for n in range(npositions): # Compute the position of the central cell xpos = (posx[n]-le0)*fact ypos = (posy[n]-le1)*fact zpos = (posz[n]-le2)*fact if (xpos < -1 or ypos < -1 or zpos < -1 or xpos >= edge0+1.5001 or ypos >= edge1+1.5001 or zpos >= edge2+1.5001): continue xpos = fclip(xpos, 0.5001, edge0) ypos = fclip(ypos, 0.5001, edge1) zpos = fclip(zpos, 0.5001, edge2) i1 = (xpos + 0.5) j1 = (ypos + 0.5) k1 = (zpos + 0.5) # Compute the weights dx = ( i1) + 0.5 - xpos dy = ( j1) + 0.5 - ypos dz = ( k1) + 0.5 - zpos dx2 = 1.0 - dx dy2 = 1.0 - dy dz2 = 1.0 - dz # Interpolate from field onto the particle sample[n] = (field[i1-1,j1-1,k1-1] * dx * dy * dz + field[i1 ,j1-1,k1-1] * dx2 * dy * dz + field[i1-1,j1 ,k1-1] * dx * dy2 * dz + field[i1 ,j1 ,k1-1] * dx2 * dy2 * dz + field[i1-1,j1-1,k1 ] * dx * dy * dz2 + field[i1 ,j1-1,k1 ] * dx2 * dy * dz2 + field[i1-1,j1 ,k1 ] * dx * dy2 * dz2 + field[i1 ,j1 ,k1 ] * dx2 * dy2 * dz2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def assign_particles_to_cells(np.ndarray[np.int32_t, ndim=1] levels, #for cells np.ndarray[np.float32_t, ndim=2] left_edges, #many cells np.ndarray[np.float32_t, ndim=2] right_edges, np.ndarray[np.float32_t, ndim=1] pos_x, #particle np.ndarray[np.float32_t, ndim=1] pos_y, np.ndarray[np.float32_t, ndim=1] pos_z): #for every cell, assign the particles belonging to it, #skipping previously assigned particles cdef long level_max = np.max(levels) cdef long i,j,level cdef long npart = pos_x.shape[0] cdef long ncells = left_edges.shape[0] cdef np.ndarray[np.int32_t, ndim=1] assign = np.zeros(npart,dtype='int32')-1 for level in range(level_max,0,-1): #start with the finest level for i in range(ncells): #go through every cell on the finest level first if not levels[i] == level: continue for j in range(npart): #iterate over all particles, skip if assigned if assign[j]>-1: continue if (left_edges[i,0] <= pos_x[j] <= right_edges[i,0]): if (left_edges[i,1] <= pos_y[j] <= right_edges[i,1]): if (left_edges[i,2] <= pos_z[j] <= right_edges[i,2]): assign[j]=i return assign @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def assign_particles_to_cell_lists(np.ndarray[np.int32_t, ndim=1] levels, #for cells np.ndarray[np.int32_t,ndim=1] assign, np.int64_t level_max, np.ndarray[np.float32_t, ndim=2] left_edges, #many cells np.ndarray[np.float32_t, ndim=2] right_edges, np.ndarray[np.float32_t, ndim=1] pos_x, #particle np.ndarray[np.float32_t, ndim=1] pos_y, np.ndarray[np.float32_t, ndim=1] pos_z): #for every cell, assign the particles belonging to it, #skipping previously assigned particles #Todo: instead of iterating every particles, could use kdtree cdef long i,j,level cdef long npart = pos_x.shape[0] cdef long ncells = left_edges.shape[0] #cdef np.ndarray[np.int32_t, ndim=1] assign #assign = np.zeros(npart,dtype='int32')-1 index_lists = [] for level in range(level_max,-1,-1): #start with the finest level for i in range(ncells): #go through every cell on the finest level first if not levels[i] == level: continue index_list = [] for j in range(npart): #iterate over all particles, skip if assigned if assign[j]>-1: continue if (left_edges[i,0] <= pos_x[j] <= right_edges[i,0]): if (left_edges[i,1] <= pos_y[j] <= right_edges[i,1]): if (left_edges[i,2] <= pos_z[j] <= right_edges[i,2]): assign[j]=i index_list += j, index_lists += index_list, return assign,index_lists @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def recursive_particle_assignment(grids, grid, np.ndarray[np.float32_t, ndim=2] left_edges, #many cells np.ndarray[np.float32_t, ndim=2] right_edges, np.ndarray[np.float32_t, ndim=1] pos_x, #particle np.ndarray[np.float32_t, ndim=1] pos_y, np.ndarray[np.float32_t, ndim=1] pos_z): #start on level zero, grid particles onto every mesh #every particle we are fed, we can assume it exists on our grid #must fill in the grid_particle_count array #and particle_indices for every grid cdef long i, j cdef long npart = pos_x.shape[0] cdef np.ndarray[np.int32_t, ndim=1] assigned = np.zeros(npart,dtype='int32') cdef np.ndarray[np.int32_t, ndim=1] never_assigned = np.ones(npart,dtype='int32') for i in np.unique(grid.child_index_mask): if i== -1: continue #assigned to this subgrid assigned = np.zeros(npart,dtype='int32') for j in range(npart): if (left_edges[i,0] <= pos_x[j] <= right_edges[i,0]): if (left_edges[i,1] <= pos_y[j] <= right_edges[i,1]): if (left_edges[i,2] <= pos_z[j] <= right_edges[i,2]): assigned[j]=1 never_assigned[j]=0 if np.sum(assigned)>0: recursive_particle_assignment(grids,grid,left_edges,right_edges, pos_x[assigned],pos_y[assigned],pos_z[assigned]) #now we have assigned particles to other subgrids, we are left with particles on our grid yt-project-yt-f043ac8/yt/utilities/lib/partitioned_grid.pxd000066400000000000000000000011511510711153200241310ustar00rootroot00000000000000""" Definitions for the partitioned grid """ import numpy as np cimport cython cimport numpy as np from .volume_container cimport VolumeContainer cdef class PartitionedGrid: cdef public object my_data cdef public object source_mask cdef public object LeftEdge cdef public object RightEdge cdef public int parent_grid_id cdef VolumeContainer *container cdef np.float64_t star_er cdef np.float64_t star_sigma_num cdef np.float64_t star_coeff cdef void get_vector_field(self, np.float64_t pos[3], np.float64_t *vel, np.float64_t *vel_mag) yt-project-yt-f043ac8/yt/utilities/lib/partitioned_grid.pyx000066400000000000000000000127241510711153200241660ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: libraries = STD_LIBS FIXED_INTERP # distutils: language = c++ # distutils: extra_compile_args = CPP14_FLAG # distutils: extra_link_args = CPP14_FLAG """ Image sampler definitions """ import numpy as np cimport cython cimport numpy as np from libc.stdlib cimport free, malloc from .fixed_interpolator cimport offset_interpolate cdef class PartitionedGrid: @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def __cinit__(self, int parent_grid_id, data, mask, np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] right_edge, np.ndarray[np.int64_t, ndim=1] dims, int n_fields = -1): # The data is likely brought in via a slice, so we copy it cdef np.ndarray[np.float64_t, ndim=3] tdata cdef np.ndarray[np.uint8_t, ndim=3] mask_data self.container = NULL self.parent_grid_id = parent_grid_id self.LeftEdge = left_edge self.RightEdge = right_edge self.container = \ malloc(sizeof(VolumeContainer)) cdef VolumeContainer *c = self.container # convenience if n_fields == -1: n_fields = len(data) cdef int n_data = len(data) c.n_fields = n_fields for i in range(3): c.left_edge[i] = left_edge[i] c.right_edge[i] = right_edge[i] c.dims[i] = dims[i] c.dds[i] = (c.right_edge[i] - c.left_edge[i])/dims[i] c.idds[i] = 1.0/c.dds[i] self.my_data = data self.source_mask = mask mask_data = mask c.data = malloc(sizeof(np.float64_t*) * n_fields) for i in range(n_data): tdata = data[i] c.data[i] = tdata.data c.mask = mask_data.data def __dealloc__(self): # The data fields are not owned by the container, they are owned by us! # So we don't need to deallocate them. if self.container == NULL: return if self.container.data != NULL: free(self.container.data) free(self.container) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def integrate_streamline(self, pos, np.float64_t h, mag): cdef np.float64_t cmag[1] cdef np.float64_t k1[3] cdef np.float64_t k2[3] cdef np.float64_t k3[3] cdef np.float64_t k4[3] cdef np.float64_t newpos[3] cdef np.float64_t oldpos[3] for i in range(3): newpos[i] = oldpos[i] = pos[i] self.get_vector_field(newpos, k1, cmag) for i in range(3): newpos[i] = oldpos[i] + 0.5*k1[i]*h if not (self.LeftEdge[0] < newpos[0] and newpos[0] < self.RightEdge[0] and \ self.LeftEdge[1] < newpos[1] and newpos[1] < self.RightEdge[1] and \ self.LeftEdge[2] < newpos[2] and newpos[2] < self.RightEdge[2]): if mag is not None: mag[0] = cmag[0] for i in range(3): pos[i] = newpos[i] return self.get_vector_field(newpos, k2, cmag) for i in range(3): newpos[i] = oldpos[i] + 0.5*k2[i]*h if not (self.LeftEdge[0] <= newpos[0] and newpos[0] <= self.RightEdge[0] and \ self.LeftEdge[1] <= newpos[1] and newpos[1] <= self.RightEdge[1] and \ self.LeftEdge[2] <= newpos[2] and newpos[2] <= self.RightEdge[2]): if mag is not None: mag[0] = cmag[0] for i in range(3): pos[i] = newpos[i] return self.get_vector_field(newpos, k3, cmag) for i in range(3): newpos[i] = oldpos[i] + k3[i]*h if not (self.LeftEdge[0] <= newpos[0] and newpos[0] <= self.RightEdge[0] and \ self.LeftEdge[1] <= newpos[1] and newpos[1] <= self.RightEdge[1] and \ self.LeftEdge[2] <= newpos[2] and newpos[2] <= self.RightEdge[2]): if mag is not None: mag[0] = cmag[0] for i in range(3): pos[i] = newpos[i] return self.get_vector_field(newpos, k4, cmag) for i in range(3): pos[i] = oldpos[i] + h*(k1[i]/6.0 + k2[i]/3.0 + k3[i]/3.0 + k4[i]/6.0) if mag is not None: for i in range(3): newpos[i] = pos[i] self.get_vector_field(newpos, k4, cmag) mag[0] = cmag[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void get_vector_field(self, np.float64_t pos[3], np.float64_t *vel, np.float64_t *vel_mag): cdef np.float64_t dp[3] cdef int ci[3] cdef VolumeContainer *c = self.container # convenience for i in range(3): ci[i] = (int)((pos[i]-self.LeftEdge[i])/c.dds[i]) dp[i] = (pos[i] - ci[i]*c.dds[i] - self.LeftEdge[i])/c.dds[i] cdef int offset = ci[0] * (c.dims[1] + 1) * (c.dims[2] + 1) \ + ci[1] * (c.dims[2] + 1) + ci[2] vel_mag[0] = 0.0 for i in range(3): vel[i] = offset_interpolate(c.dims, dp, c.data[i] + offset) vel_mag[0] += vel[i]*vel[i] vel_mag[0] = np.sqrt(vel_mag[0]) if vel_mag[0] != 0.0: for i in range(3): vel[i] /= vel_mag[0] yt-project-yt-f043ac8/yt/utilities/lib/pixelization_constants.cpp000066400000000000000000000062471510711153200254170ustar00rootroot00000000000000/******************************************************************************* *******************************************************************************/ // // Some Cython versions don't like module-level constants, so we'll put them // here. // #include "pixelization_constants.hpp" /* Six faces, two vectors for each, two indices for each vector. The function below unrolls how these are defined. Some info can be found at: http://www.mscsoftware.com/training_videos/patran/Reverb_help/index.html#page/Finite%20Element%20Modeling/elem_lib_topics.16.8.html This is [6][2][2] in shape. Here are the faces and their four edges each: F1 1 2 3 4 F2 5 6 7 8 F3 1 10 5 9 F4 2 11 6 10 F5 3 12 7 11 F6 4 9 8 12 The edges are then defined by: E1 1 2 E2 2 6 E3 6 5 E4 5 1 E5 4 3 E6 3 7 E7 7 8 E8 8 4 E9 1 4 E10 2 3 E11 6 7 E12 5 8 Now we unroll these here ... */ const npy_uint8 hex_face_defs[MAX_NUM_FACES][2][2] = { /* Note that the first of each pair is the shared vertex */ {{1, 0}, {1, 5}}, {{2, 3}, {2, 6}}, {{1, 0}, {1, 2}}, {{5, 1}, {5, 6}}, {{4, 5}, {4, 7}}, {{0, 4}, {0, 3}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}} }; /* http://www.mscsoftware.com/training_videos/patran/Reverb_help/index.html#page/Finite%2520Element%2520Modeling/elem_lib_topics.16.6.html F1 1 2 3 F2 1 5 4 F3 2 6 5 F4 3 4 6 The edges are then defined by: E1 1 2 E2 2 3 E3 3 1 E4 1 4 E5 2 4 E6 3 4 */ const npy_uint8 tetra_face_defs[MAX_NUM_FACES][2][2] = { {{1, 0}, {1, 2}}, {{1, 0}, {1, 3}}, {{2, 1}, {2, 3}}, {{3, 0}, {3, 2}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}} }; /* http://www.mscsoftware.com/training_videos/patran/Reverb_help/index.html#page/Finite%2520Element%2520Modeling/elem_lib_topics.16.7.html F1 1 2 3 * F2 4 5 6 * F3 1 8 4 7 F4 2 9 5 8 F5 3 7 6 9 The edges are then defined by: E1 2 1 E2 1 3 E3 3 2 E4 5 4 E5 4 6 E6 6 5 E7 2 5 E8 1 4 E9 3 6 */ const npy_uint8 wedge_face_defs[MAX_NUM_FACES][2][2] = { {{0, 1}, {0, 2}}, {{3, 4}, {3, 5}}, {{0, 1}, {0, 3}}, {{2, 0}, {2, 5}}, {{1, 2}, {1, 4}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}}, {{255, 255}, {255, 255}} }; yt-project-yt-f043ac8/yt/utilities/lib/pixelization_constants.hpp000066400000000000000000000013271510711153200254160ustar00rootroot00000000000000/******************************************************************************* *******************************************************************************/ // // Some Cython versions don't like module-level constants, so we'll put them // here. // #include "Python.h" #include #include #include #include #include "numpy/ndarrayobject.h" #define MAX_NUM_FACES 16 #define HEX_IND 0 #define HEX_NF 6 #define TETRA_IND 1 #define TETRA_NF 4 #define WEDGE_IND 2 #define WEDGE_NF 5 extern const npy_uint8 hex_face_defs[MAX_NUM_FACES][2][2]; extern const npy_uint8 tetra_face_defs[MAX_NUM_FACES][2][2]; extern const npy_uint8 wedge_face_defs[MAX_NUM_FACES][2][2]; yt-project-yt-f043ac8/yt/utilities/lib/pixelization_routines.pyx000066400000000000000000002652431510711153200253140ustar00rootroot00000000000000# distutils: include_dirs = LIB_DIR # distutils: extra_compile_args = CPP14_FLAG OMP_ARGS # distutils: extra_link_args = CPP14_FLAG OMP_ARGS # distutils: language = c++ # distutils: libraries = STD_LIBS # distutils: sources = yt/utilities/lib/pixelization_constants.cpp """ Pixelization routines """ import numpy as np cimport cython cimport libc.math as math cimport numpy as np from cython.view cimport array as cvarray from yt.utilities.lib.fp_utils cimport ( any_float, fabs, fmax, fmin, i64max, i64min, iclip, ) from yt.utilities.exceptions import YTElementTypeNotRecognized, YTPixelizeError from cpython.exc cimport PyErr_CheckSignals from cython.parallel cimport parallel, prange from libc.stdlib cimport free, malloc from yt.geometry.particle_deposit cimport get_kernel_func, kernel_func from yt.utilities.lib.element_mappings cimport ( ElementSampler, P1Sampler1D, P1Sampler2D, P1Sampler3D, Q1Sampler2D, Q1Sampler3D, Q2Sampler2D, S2Sampler3D, T2Sampler2D, Tet2Sampler3D, W1Sampler3D, ) from .vec3_ops cimport cross, dot, subtract from yt.funcs import get_pbar from yt.utilities.lib.bounded_priority_queue cimport BoundedPriorityQueue from yt.utilities.lib.cykdtree.kdtree cimport KDTree, PyKDTree from yt.utilities.lib.particle_kdtree_tools cimport ( axes_range, find_neighbors, set_axes_range, ) cdef int TABLE_NVALS=512 cdef extern from "pixelization_constants.hpp": enum: MAX_NUM_FACES int HEX_IND int HEX_NF np.uint8_t hex_face_defs[MAX_NUM_FACES][2][2] int TETRA_IND int TETRA_NF np.uint8_t tetra_face_defs[MAX_NUM_FACES][2][2] int WEDGE_IND int WEDGE_NF np.uint8_t wedge_face_defs[MAX_NUM_FACES][2][2] cdef extern from "numpy/npy_math.h": double NPY_PI @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def pixelize_cartesian(np.float64_t[:,:] buff, any_float[:] px, any_float[:] py, any_float[:] pdx, any_float[:] pdy, any_float[:] data, bounds, int antialias = 1, period = None, int check_period = 1, np.float64_t line_width = 0.0, *, int return_mask = 0, ): cdef np.float64_t x_min, x_max, y_min, y_max cdef np.float64_t period_x = 0.0, period_y = 0.0 cdef np.float64_t width, height, px_dx, px_dy, ipx_dx, ipx_dy cdef np.float64_t ld_x, ld_y, cx, cy cdef int i, j, p, xi, yi cdef int lc, lr, rc, rr cdef np.float64_t lypx, rypx, lxpx, rxpx, overlap1, overlap2 # These are the temp vars we get from the arrays cdef np.float64_t oxsp, oysp, xsp, ysp, dxsp, dysp, dsp # Some periodicity helpers cdef int xiter[2] cdef int yiter[2] cdef np.float64_t xiterv[2] cdef np.float64_t yiterv[2] cdef np.ndarray[np.uint8_t, ndim=2] mask_arr = np.zeros_like(buff, dtype="uint8") cdef np.uint8_t[:, :] mask = mask_arr if period is not None: period_x = period[0] period_y = period[1] x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] width = x_max - x_min height = y_max - y_min px_dx = width / ( buff.shape[1]) px_dy = height / ( buff.shape[0]) ipx_dx = 1.0 / px_dx ipx_dy = 1.0 / px_dy if px.shape[0] != py.shape[0] or \ px.shape[0] != pdx.shape[0] or \ px.shape[0] != pdy.shape[0] or \ px.shape[0] != data.shape[0]: raise YTPixelizeError("Arrays are not of correct shape.") xiter[0] = yiter[0] = 0 xiterv[0] = yiterv[0] = 0.0 # Here's a basic outline of what we're going to do here. The xiter and # yiter variables govern whether or not we should check periodicity -- are # we both close enough to the edge that it would be important *and* are we # periodic? # # The other variables are all either pixel positions or data positions. # Pixel positions will vary regularly from the left edge of the window to # the right edge of the window; px_dx and px_dy are the dx (cell width, not # half-width). ipx_dx and ipx_dy are the inverse, for quick math. # # The values in xsp, dxsp, x_min and their y counterparts, are the # data-space coordinates, and are related to the data fed in. We make some # modifications for periodicity. # # Inside the finest loop, we compute the "left column" (lc) and "lower row" # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr), # depositing into them the data value. Overlap computes the relative # overlap of a data value with a pixel. # # NOTE ON ROWS AND COLUMNS: # # The way that images are plotting in matplotlib is somewhat different # from what most might expect. The first axis of the array plotted is # what varies along the x axis. So for instance, if you supply # origin='lower' and plot the results of an mgrid operation, at a fixed # 'y' value you will see the results of that array held constant in the # first dimension. Here is some example code: # # import matplotlib.pyplot as plt # import numpy as np # x, y = np.mgrid[0:1:100j,0:1:100j] # plt.imshow(x, interpolation='nearest', origin='lower') # plt.imshow(y, interpolation='nearest', origin='lower') # # The values in the image: # lower left: arr[0,0] # lower right: arr[0,-1] # upper left: arr[-1,0] # upper right: arr[-1,-1] # # So what we want here is to fill an array such that we fill: # first axis : y_min .. y_max # second axis: x_min .. x_max with nogil: for p in range(px.shape[0]): xiter[1] = yiter[1] = 999 xiterv[1] = yiterv[1] = 0.0 oxsp = px[p] oysp = py[p] dxsp = pdx[p] dysp = pdy[p] dsp = data[p] if check_period == 1: if (oxsp - dxsp < x_min): xiter[1] = +1 xiterv[1] = period_x elif (oxsp + dxsp > x_max): xiter[1] = -1 xiterv[1] = -period_x if (oysp - dysp < y_min): yiter[1] = +1 yiterv[1] = period_y elif (oysp + dysp > y_max): yiter[1] = -1 yiterv[1] = -period_y overlap1 = overlap2 = 1.0 for xi in range(2): if xiter[xi] == 999: continue xsp = oxsp + xiterv[xi] if (xsp + dxsp < x_min) or (xsp - dxsp > x_max): continue for yi in range(2): if yiter[yi] == 999: continue ysp = oysp + yiterv[yi] if (ysp + dysp < y_min) or (ysp - dysp > y_max): continue lc = fmax(((xsp-dxsp-x_min)*ipx_dx),0) lr = fmax(((ysp-dysp-y_min)*ipx_dy),0) # NOTE: This is a different way of doing it than in the C # routines. In C, we were implicitly casting the # initialization to int, but *not* the conditional, which # was allowed an extra value: # for(j=lc;j fmin(((xsp+dxsp-x_min)*ipx_dx + 1), buff.shape[1]) rr = fmin(((ysp+dysp-y_min)*ipx_dy + 1), buff.shape[0]) # Note that we're iterating here over *y* in the i # direction. See the note above about this. for i in range(lr, rr): lypx = px_dy * i + y_min rypx = px_dy * (i+1) + y_min if antialias == 1: overlap2 = ((fmin(rypx, ysp+dysp) - fmax(lypx, (ysp-dysp)))*ipx_dy) if overlap2 < 0.0: continue for j in range(lc, rc): lxpx = px_dx * j + x_min rxpx = px_dx * (j+1) + x_min if line_width > 0: # Here, we figure out if we're within # line_width*px_dx of the cell edge # Midpoint of x: cx = (rxpx+lxpx)*0.5 ld_x = fmin(fabs(cx - (xsp+dxsp)), fabs(cx - (xsp-dxsp))) ld_x *= ipx_dx # Midpoint of y: cy = (rypx+lypx)*0.5 ld_y = fmin(fabs(cy - (ysp+dysp)), fabs(cy - (ysp-dysp))) ld_y *= ipx_dy if ld_x <= line_width or ld_y <= line_width: buff[i,j] = 1.0 mask[i,j] = 1 elif antialias == 1: overlap1 = ((fmin(rxpx, xsp+dxsp) - fmax(lxpx, (xsp-dxsp)))*ipx_dx) if overlap1 < 0.0: continue # This next line is not commented out because # it's an oddity; we actually want to skip # depositing if the overlap is zero, and that's # how it used to work when we were more # conservative about the iteration indices. # This will reduce artifacts if we ever move to # compositing instead of replacing bitmaps. if overlap1 * overlap2 < 1.e-6: continue # make sure pixel value is not a NaN before incrementing it if buff[i,j] != buff[i,j]: buff[i,j] = 0.0 buff[i,j] += (dsp * overlap1) * overlap2 mask[i,j] = 1 else: buff[i,j] = dsp mask[i,j] = 1 if return_mask: return mask_arr.astype("bool") @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def pixelize_cartesian_nodal(np.float64_t[:,:] buff, np.float64_t[:] px, np.float64_t[:] py, np.float64_t[:] pz, np.float64_t[:] pdx, np.float64_t[:] pdy, np.float64_t[:] pdz, np.float64_t[:, :] data, np.float64_t coord, bounds, int antialias = 1, period = None, int check_period = 1, *, int return_mask = 0, ): cdef np.float64_t x_min, x_max, y_min, y_max cdef np.float64_t period_x = 0.0, period_y = 0.0 cdef np.float64_t width, height, px_dx, px_dy, ipx_dx, ipx_dy cdef np.float64_t cx, cy, cz cdef int i, j, p, xi, yi cdef int lc, lr, rc, rr cdef np.float64_t lypx, rypx, lxpx, rxpx, overlap1, overlap2 # These are the temp vars we get from the arrays cdef np.float64_t oxsp, oysp, ozsp cdef np.float64_t xsp, ysp, zsp cdef np.float64_t dxsp, dysp, dzsp # Some periodicity helpers cdef int xiter[2] cdef int yiter[2] cdef int ii, jj, kk, ind cdef np.float64_t xiterv[2] cdef np.float64_t yiterv[2] if period is not None: period_x = period[0] period_y = period[1] x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] width = x_max - x_min height = y_max - y_min px_dx = width / ( buff.shape[1]) px_dy = height / ( buff.shape[0]) ipx_dx = 1.0 / px_dx ipx_dy = 1.0 / px_dy if px.shape[0] != py.shape[0] or \ px.shape[0] != pz.shape[0] or \ px.shape[0] != pdx.shape[0] or \ px.shape[0] != pdy.shape[0] or \ px.shape[0] != pdz.shape[0] or \ px.shape[0] != data.shape[0]: raise YTPixelizeError("Arrays are not of correct shape.") xiter[0] = yiter[0] = 0 xiterv[0] = yiterv[0] = 0.0 # Here's a basic outline of what we're going to do here. The xiter and # yiter variables govern whether or not we should check periodicity -- are # we both close enough to the edge that it would be important *and* are we # periodic? # # The other variables are all either pixel positions or data positions. # Pixel positions will vary regularly from the left edge of the window to # the right edge of the window; px_dx and px_dy are the dx (cell width, not # half-width). ipx_dx and ipx_dy are the inverse, for quick math. # # The values in xsp, dxsp, x_min and their y counterparts, are the # data-space coordinates, and are related to the data fed in. We make some # modifications for periodicity. # # Inside the finest loop, we compute the "left column" (lc) and "lower row" # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr), # depositing into them the data value. Overlap computes the relative # overlap of a data value with a pixel. # # NOTE ON ROWS AND COLUMNS: # # The way that images are plotting in matplotlib is somewhat different # from what most might expect. The first axis of the array plotted is # what varies along the x axis. So for instance, if you supply # origin='lower' and plot the results of an mgrid operation, at a fixed # 'y' value you will see the results of that array held constant in the # first dimension. Here is some example code: # # import matplotlib.pyplot as plt # import numpy as np # x, y = np.mgrid[0:1:100j,0:1:100j] # plt.imshow(x, interpolation='nearest', origin='lower') # plt.imshow(y, interpolation='nearest', origin='lower') # # The values in the image: # lower left: arr[0,0] # lower right: arr[0,-1] # upper left: arr[-1,0] # upper right: arr[-1,-1] # # So what we want here is to fill an array such that we fill: # first axis : y_min .. y_max # second axis: x_min .. x_max cdef np.ndarray[np.uint8_t, ndim=2] mask_arr = np.zeros_like(buff, dtype="uint8") cdef np.uint8_t[:, :] mask = mask_arr with nogil: for p in range(px.shape[0]): xiter[1] = yiter[1] = 999 xiterv[1] = yiterv[1] = 0.0 oxsp = px[p] oysp = py[p] ozsp = pz[p] dxsp = pdx[p] dysp = pdy[p] dzsp = pdz[p] if check_period == 1: if (oxsp - dxsp < x_min): xiter[1] = +1 xiterv[1] = period_x elif (oxsp + dxsp > x_max): xiter[1] = -1 xiterv[1] = -period_x if (oysp - dysp < y_min): yiter[1] = +1 yiterv[1] = period_y elif (oysp + dysp > y_max): yiter[1] = -1 yiterv[1] = -period_y overlap1 = overlap2 = 1.0 zsp = ozsp for xi in range(2): if xiter[xi] == 999: continue xsp = oxsp + xiterv[xi] if (xsp + dxsp < x_min) or (xsp - dxsp > x_max): continue for yi in range(2): if yiter[yi] == 999: continue ysp = oysp + yiterv[yi] if (ysp + dysp < y_min) or (ysp - dysp > y_max): continue lc = fmax(((xsp-dxsp-x_min)*ipx_dx),0) lr = fmax(((ysp-dysp-y_min)*ipx_dy),0) # NOTE: This is a different way of doing it than in the C # routines. In C, we were implicitly casting the # initialization to int, but *not* the conditional, which # was allowed an extra value: # for(j=lc;j fmin(((xsp+dxsp-x_min)*ipx_dx + 1), buff.shape[1]) rr = fmin(((ysp+dysp-y_min)*ipx_dy + 1), buff.shape[0]) # Note that we're iterating here over *y* in the i # direction. See the note above about this. for i in range(lr, rr): lypx = px_dy * i + y_min rypx = px_dy * (i+1) + y_min for j in range(lc, rc): lxpx = px_dx * j + x_min rxpx = px_dx * (j+1) + x_min cx = (rxpx+lxpx)*0.5 cy = (rypx+lypx)*0.5 cz = coord ii = (cx - xsp + dxsp) jj = (cy - ysp + dysp) kk = (cz - zsp + dzsp) ind = 4*ii + 2*jj + kk buff[i,j] = data[p, ind] mask[i,j] = 1 if return_mask: return mask_arr.astype("bool") @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def pixelize_off_axis_cartesian( np.float64_t[:,:] buff, np.float64_t[:] x, np.float64_t[:] y, np.float64_t[:] z, np.float64_t[:] px, np.float64_t[:] py, np.float64_t[:] pdx, np.float64_t[:] pdy, np.float64_t[:] pdz, np.float64_t[:] center, np.float64_t[:,:] inv_mat, np.int64_t[:] indices, np.float64_t[:] data, bounds, *, int return_mask=0, ): cdef np.float64_t x_min, x_max, y_min, y_max cdef np.float64_t width, height, px_dx, px_dy, ipx_dx, ipx_dy, md cdef int i, j, p, ip cdef int lc, lr, rc, rr # These are the temp vars we get from the arrays cdef np.float64_t xsp, ysp, zsp, dxsp, dysp, dzsp, dsp cdef np.float64_t pxsp, pysp, cxpx, cypx, cx, cy, cz # Some periodicity helpers cdef np.ndarray[np.int64_t, ndim=2] mask x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] width = x_max - x_min height = y_max - y_min px_dx = width / ( buff.shape[1]) px_dy = height / ( buff.shape[0]) ipx_dx = 1.0 / px_dx ipx_dy = 1.0 / px_dy if px.shape[0] != py.shape[0] or \ px.shape[0] != pdx.shape[0] or \ px.shape[0] != pdy.shape[0] or \ px.shape[0] != pdz.shape[0] or \ px.shape[0] != indices.shape[0] or \ px.shape[0] != data.shape[0]: raise YTPixelizeError("Arrays are not of correct shape.") mask = np.zeros((buff.shape[0], buff.shape[1]), "int64") with nogil: for ip in range(indices.shape[0]): p = indices[ip] xsp = x[p] ysp = y[p] zsp = z[p] pxsp = px[p] pysp = py[p] dxsp = pdx[p] dysp = pdy[p] dzsp = pdz[p] dsp = data[p] # Any point we want to plot is at most this far from the center md = 2.0 * math.sqrt(dxsp*dxsp + dysp*dysp + dzsp*dzsp) if pxsp + md < x_min or \ pxsp - md > x_max or \ pysp + md < y_min or \ pysp - md > y_max: continue lc = fmax(((pxsp - md - x_min)*ipx_dx),0) lr = fmax(((pysp - md - y_min)*ipx_dy),0) rc = fmin(((pxsp + md - x_min)*ipx_dx + 1), buff.shape[1]) rr = fmin(((pysp + md - y_min)*ipx_dy + 1), buff.shape[0]) for i in range(lr, rr): cypx = px_dy * (i + 0.5) + y_min for j in range(lc, rc): cxpx = px_dx * (j + 0.5) + x_min cx = inv_mat[0,0]*cxpx + inv_mat[0,1]*cypx + center[0] cy = inv_mat[1,0]*cxpx + inv_mat[1,1]*cypx + center[1] cz = inv_mat[2,0]*cxpx + inv_mat[2,1]*cypx + center[2] if fabs(xsp - cx) * 0.99 > dxsp or \ fabs(ysp - cy) * 0.99 > dysp or \ fabs(zsp - cz) * 0.99 > dzsp: continue mask[i, j] += 1 # make sure pixel value is not a NaN before incrementing it if buff[i,j] != buff[i,j]: buff[i,j] = 0.0 buff[i, j] += dsp for i in range(buff.shape[0]): for j in range(buff.shape[1]): if mask[i,j] == 0: continue buff[i,j] /= mask[i,j] if return_mask: return mask!=0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def pixelize_cylinder(np.float64_t[:,:] buff, np.float64_t[:] radius, np.float64_t[:] dradius, np.float64_t[:] theta, np.float64_t[:] dtheta, np.float64_t[:] field, extents, *, int return_mask=0, ): cdef np.float64_t x, y, dx, dy, r0, theta0 cdef np.float64_t rmin, rmax, tmin, tmax, x0, y0, x1, y1, xp, yp cdef np.float64_t r_i, theta_i, dr_i, dtheta_i cdef np.float64_t r_inc, theta_inc cdef np.float64_t costheta, sintheta cdef int i, i1, pi, pj cdef np.float64_t twoPI = 2 * NPY_PI cdef int imin, imax imin = np.asarray(radius).argmin() imax = np.asarray(radius).argmax() rmin = radius[imin] - dradius[imin] rmax = radius[imax] + dradius[imax] imin = np.asarray(theta).argmin() imax = np.asarray(theta).argmax() tmin = theta[imin] - dtheta[imin] tmax = theta[imax] + dtheta[imax] cdef np.ndarray[np.uint8_t, ndim=2] mask_arr = np.zeros_like(buff, dtype="uint8") cdef np.uint8_t[:, :] mask = mask_arr x0, x1, y0, y1 = extents dx = (x1 - x0) / buff.shape[0] dy = (y1 - y0) / buff.shape[1] cdef np.float64_t rbounds[2] cdef np.float64_t prbounds[2] cdef np.float64_t ptbounds[2] cdef np.float64_t corners[8] # Find our min and max r corners[0] = x0*x0+y0*y0 corners[1] = x1*x1+y0*y0 corners[2] = x0*x0+y1*y1 corners[3] = x1*x1+y1*y1 corners[4] = x0*x0 corners[5] = x1*x1 corners[6] = y0*y0 corners[7] = y1*y1 rbounds[0] = rbounds[1] = corners[0] for i in range(8): rbounds[0] = fmin(rbounds[0], corners[i]) rbounds[1] = fmax(rbounds[1], corners[i]) rbounds[0] = math.sqrt(rbounds[0]) rbounds[1] = math.sqrt(rbounds[1]) # If we include the origin in either direction, we need to have radius of # zero as our lower bound. if x0 < 0 and x1 > 0: rbounds[0] = 0.0 if y0 < 0 and y1 > 0: rbounds[0] = 0.0 r_inc = 0.5 * fmin(dx, dy) with nogil: for i in range(radius.shape[0]): r0 = radius[i] theta0 = theta[i] dr_i = dradius[i] dtheta_i = dtheta[i] # Skip out early if we're offsides, for zoomed in plots if r0 + dr_i < rbounds[0] or r0 - dr_i > rbounds[1]: continue theta_i = theta0 - dtheta_i theta_inc = r_inc / (r0 + dr_i) while theta_i < theta0 + dtheta_i: r_i = r0 - dr_i costheta = math.cos(theta_i) sintheta = math.sin(theta_i) while r_i < r0 + dr_i: if rmax <= r_i: r_i += r_inc continue y = r_i * costheta x = r_i * sintheta pi = ((x - x0)/dx) pj = ((y - y0)/dy) if pi >= 0 and pi < buff.shape[0] and \ pj >= 0 and pj < buff.shape[1]: # we got a pixel that intersects the grid cell # now check that this pixel doesn't go beyond the data domain xp = x0 + pi*dx yp = y0 + pj*dy corners[0] = xp*xp + yp*yp corners[1] = xp*xp + (yp+dy)**2 corners[2] = (xp+dx)**2 + yp*yp corners[3] = (xp+dx)**2 + (yp+dy)**2 prbounds[0] = prbounds[1] = corners[3] for i1 in range(3): prbounds[0] = fmin(prbounds[0], corners[i1]) prbounds[1] = fmax(prbounds[1], corners[i1]) prbounds[0] = math.sqrt(prbounds[0]) prbounds[1] = math.sqrt(prbounds[1]) corners[0] = math.atan2(xp, yp) corners[1] = math.atan2(xp, yp+dy) corners[2] = math.atan2(xp+dx, yp) corners[3] = math.atan2(xp+dx, yp+dy) ptbounds[0] = ptbounds[1] = corners[3] for i1 in range(3): ptbounds[0] = fmin(ptbounds[0], corners[i1]) ptbounds[1] = fmax(ptbounds[1], corners[i1]) # shift to a [0, 2*PI] interval # note: with fmod, the sign of the returned value # matches the sign of the first argument, so need # to offset by 2pi to ensure a positive result in [0, 2pi] ptbounds[0] = math.fmod(ptbounds[0]+twoPI, twoPI) ptbounds[1] = math.fmod(ptbounds[1]+twoPI, twoPI) if prbounds[0] >= rmin and prbounds[1] <= rmax and \ ptbounds[0] >= tmin and ptbounds[1] <= tmax: buff[pi, pj] = field[i] mask[pi, pj] = 1 r_i += r_inc theta_i += theta_inc if return_mask: return mask_arr.astype("bool") cdef int aitoff_Lambda_btheta_to_xy(np.float64_t Lambda, np.float64_t btheta, np.float64_t *x, np.float64_t *y) except -1: cdef np.float64_t z = math.sqrt(1 + math.cos(btheta) * math.cos(Lambda / 2.0)) x[0] = 2.0 * math.cos(btheta) * math.sin(Lambda / 2.0) / z y[0] = math.sin(btheta) / z return 0 @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) def pixelize_aitoff(np.float64_t[:] azimuth, np.float64_t[:] dazimuth, np.float64_t[:] colatitude, np.float64_t[:] dcolatitude, buff_size, np.float64_t[:] field, bounds, # this is a 4-tuple input_img = None, np.float64_t azimuth_offset = 0.0, np.float64_t colatitude_offset = 0.0, *, int return_mask = 0 ): # http://paulbourke.net/geometry/transformationprojection/ # (Lambda) longitude is -PI to PI (longitude = azimuth - PI) # (btheta) latitude is -PI/2 to PI/2 (latitude = PI/2 - colatitude) # # z^2 = 1 + cos(latitude) cos(longitude/2) # x = cos(latitude) sin(longitude/2) / z # y = sin(latitude) / z cdef np.ndarray[np.float64_t, ndim=2] img cdef int i, j, nf, fi cdef np.float64_t x, y, z, zb cdef np.float64_t dx, dy, xw, yw cdef np.float64_t Lambda0, btheta0, Lambda_p, dLambda_p, btheta_p, dbtheta_p cdef np.float64_t PI = np.pi cdef np.float64_t s2 = math.sqrt(2.0) cdef np.float64_t xmax, ymax, xmin, ymin nf = field.shape[0] if input_img is None: img = np.zeros((buff_size[0], buff_size[1])) img[:] = np.nan else: img = input_img cdef np.ndarray[np.uint8_t, ndim=2] mask_arr = np.ones_like(img, dtype="uint8") cdef np.uint8_t[:, :] mask = mask_arr # Okay, here's our strategy. We compute the bounds in x and y, which will # be a rectangle, and then for each x, y position we check to see if it's # within our Lambda. This will cost *more* computations of the # (x,y)->(Lambda,btheta) calculation, but because we no longer have to search # through the Lambda, btheta arrays, it should be faster. xw = bounds[1] - bounds[0] yw = bounds[3] - bounds[2] dx = xw / (img.shape[0] - 1) dy = yw / (img.shape[1] - 1) x = y = 0 for fi in range(nf): Lambda_p = (azimuth[fi] + azimuth_offset) - PI dLambda_p = dazimuth[fi] btheta_p = PI/2.0 - (colatitude[fi] + colatitude_offset) dbtheta_p = dcolatitude[fi] # Four transformations aitoff_Lambda_btheta_to_xy(Lambda_p - dLambda_p, btheta_p - dbtheta_p, &x, &y) xmin = x xmax = x ymin = y ymax = y aitoff_Lambda_btheta_to_xy(Lambda_p - dLambda_p, btheta_p + dbtheta_p, &x, &y) xmin = fmin(xmin, x) xmax = fmax(xmax, x) ymin = fmin(ymin, y) ymax = fmax(ymax, y) aitoff_Lambda_btheta_to_xy(Lambda_p + dLambda_p, btheta_p - dbtheta_p, &x, &y) xmin = fmin(xmin, x) xmax = fmax(xmax, x) ymin = fmin(ymin, y) ymax = fmax(ymax, y) aitoff_Lambda_btheta_to_xy(Lambda_p + dLambda_p, btheta_p + dbtheta_p, &x, &y) xmin = fmin(xmin, x) xmax = fmax(xmax, x) ymin = fmin(ymin, y) ymax = fmax(ymax, y) # special cases where the projection of the cell isn't # bounded by the rectangle (in image space) that bounds its corners. # Note that performance may take a serious hit here. The overarching algorithm # is optimized for cells with small angular width. if xmin * xmax < 0.0: # on the central meridian aitoff_Lambda_btheta_to_xy(0.0, btheta_p - dbtheta_p, &x, &y) ymin = fmin(ymin, y) ymax = fmax(ymax, y) aitoff_Lambda_btheta_to_xy(0.0, btheta_p + dbtheta_p, &x, &y) ymin = fmin(ymin, y) ymax = fmax(ymax, y) if ymin * ymax < 0.0: # on the equator aitoff_Lambda_btheta_to_xy(Lambda_p - dLambda_p, 0.0, &x, &y) xmin = fmin(xmin, x) xmax = fmax(xmax, x) aitoff_Lambda_btheta_to_xy(Lambda_p + dLambda_p, 0.0, &x, &y) xmin = fmin(xmin, x) xmax = fmax(xmax, x) # Now we have the (projected rectangular) bounds. # Shift into normalized image coords xmin = (xmin - bounds[0]) xmax = (xmax - bounds[0]) ymin = (ymin - bounds[2]) ymax = (ymax - bounds[2]) # Finally, select a rectangular region in image space # that fully contains the projected data point. # We'll reject image pixels in that rectangle that are # not actually intersecting the data point as we go. x0 = (xmin / dx) x1 = (xmax / dx) + 1 y0 = (ymin / dy) y1 = (ymax / dy) + 1 for i in range(x0, x1): x = (bounds[0] + i * dx) / 2.0 for j in range(y0, y1): y = (bounds[2] + j * dy) zb = (x*x + y*y - 1.0) if zb > 0: continue z = (1.0 - 0.5*x*x - 0.5*y*y) z = math.sqrt(z) # Longitude Lambda0 = 2.0*math.atan(z*x*s2/(2.0*z*z-1.0)) # Latitude # We shift it into co-latitude btheta0 = math.asin(z*y*s2) # Now we just need to figure out which pixel contributes. # We do not have a fast search. if not (Lambda_p - dLambda_p <= Lambda0 <= Lambda_p + dLambda_p): continue if not (btheta_p - dbtheta_p <= btheta0 <= btheta_p + dbtheta_p): continue img[i, j] = field[fi] mask[i, j] = 1 if return_mask: return img, mask_arr.astype("bool") else: return img # This function accepts a set of vertices (for a polyhedron) that are # assumed to be in order for bottom, then top, in the same clockwise or # counterclockwise direction (i.e., like points 1-8 in Figure 4 of the ExodusII # manual). It will then either *match* or *fill* the results. If it is # matching, it will early terminate with a 0 or final-terminate with a 1 if the # results match. Otherwise, it will fill the signs with -1's and 1's to show # the sign of the dot product of the point with the cross product of the face. cdef int check_face_dot(int nvertices, np.float64_t point[3], np.float64_t **vertices, np.int8_t *signs, int match): # Because of how we are doing this, we do not *care* what the signs are or # how the faces are ordered, we only care if they match between the point # and the centroid. # So, let's compute these vectors. See above where these are written out # for ease of use. cdef np.float64_t vec1[3] cdef np.float64_t vec2[3] cdef np.float64_t cp_vec[3] cdef np.float64_t npoint[3] cdef np.float64_t dp cdef np.uint8_t faces[MAX_NUM_FACES][2][2] cdef np.uint8_t nf if nvertices == 4: faces = tetra_face_defs nf = TETRA_NF elif nvertices == 6: faces = wedge_face_defs nf = WEDGE_NF elif nvertices == 8: faces = hex_face_defs nf = HEX_NF else: return -1 cdef int n, vi1a, vi1b, vi2a, vi2b for n in range(nf): vi1a = faces[n][0][0] vi1b = faces[n][0][1] vi2a = faces[n][1][0] vi2b = faces[n][1][1] # Shared vertex is vi1a and vi2a subtract(vertices[vi1b], vertices[vi1a], vec1) subtract(vertices[vi2b], vertices[vi2a], vec2) subtract(point, vertices[vi1b], npoint) cross(vec1, vec2, cp_vec) dp = dot(cp_vec, npoint) if match == 0: if dp < 0: signs[n] = -1 else: signs[n] = 1 else: if dp <= 0 and signs[n] < 0: continue elif dp >= 0 and signs[n] > 0: continue else: # mismatch! return 0 return 1 def pixelize_element_mesh(np.ndarray[np.float64_t, ndim=2] coords, np.ndarray[np.int64_t, ndim=2] conn, buff_size, np.ndarray[np.float64_t, ndim=2] field, extents, int index_offset = 0, *, return_mask=False, ): cdef np.ndarray[np.float64_t, ndim=3] img img = np.zeros(buff_size, dtype="float64") img[:] = np.nan cdef np.ndarray[np.uint8_t, ndim=3] mask_arr = np.ones_like(img, dtype="uint8") cdef np.uint8_t[:, :, :] mask = mask_arr # Two steps: # 1. Is image point within the mesh bounding box? # 2. Is image point within the mesh element? # Second is more intensive. It will convert the element vertices to the # mapped coordinate system, and check whether the result in in-bounds or not # Note that we have to have a pseudo-3D pixel buffer. One dimension will # always be 1. cdef np.float64_t pLE[3] cdef np.float64_t pRE[3] cdef np.float64_t LE[3] cdef np.float64_t RE[3] cdef int use cdef np.int64_t n, i, pi, pj, pk, ci, cj cdef np.int64_t pstart[3] cdef np.int64_t pend[3] cdef np.float64_t ppoint[3] cdef np.float64_t idds[3] cdef np.float64_t dds[3] cdef np.float64_t *vertices cdef np.float64_t *field_vals cdef int nvertices = conn.shape[1] cdef int ndim = coords.shape[1] cdef int num_field_vals = field.shape[1] cdef double[4] mapped_coord cdef ElementSampler sampler # Pick the right sampler and allocate storage for the mapped coordinate if ndim == 3 and nvertices == 4: sampler = P1Sampler3D() elif ndim == 3 and nvertices == 6: sampler = W1Sampler3D() elif ndim == 3 and nvertices == 8: sampler = Q1Sampler3D() elif ndim == 3 and nvertices == 20: sampler = S2Sampler3D() elif ndim == 2 and nvertices == 3: sampler = P1Sampler2D() elif ndim == 1 and nvertices == 2: sampler = P1Sampler1D() elif ndim == 2 and nvertices == 4: sampler = Q1Sampler2D() elif ndim == 2 and nvertices == 9: sampler = Q2Sampler2D() elif ndim == 2 and nvertices == 6: sampler = T2Sampler2D() elif ndim == 3 and nvertices == 10: sampler = Tet2Sampler3D() else: raise YTElementTypeNotRecognized(ndim, nvertices) # if we are in 2D land, the 1 cell thick dimension had better be 'z' if ndim == 2: if buff_size[2] != 1: raise RuntimeError("Slices of 2D datasets must be " "perpendicular to the 'z' direction.") # allocate temporary storage vertices = malloc(ndim * sizeof(np.float64_t) * nvertices) field_vals = malloc(sizeof(np.float64_t) * num_field_vals) # fill the image bounds and pixel size information here for i in range(ndim): pLE[i] = extents[i][0] pRE[i] = extents[i][1] dds[i] = (pRE[i] - pLE[i])/buff_size[i] if dds[i] == 0.0: idds[i] = 0.0 else: idds[i] = 1.0 / dds[i] with cython.boundscheck(False): for ci in range(conn.shape[0]): # Fill the vertices LE[0] = LE[1] = LE[2] = 1e60 RE[0] = RE[1] = RE[2] = -1e60 for n in range(num_field_vals): field_vals[n] = field[ci, n] for n in range(nvertices): cj = conn[ci, n] - index_offset for i in range(ndim): vertices[ndim*n + i] = coords[cj, i] LE[i] = fmin(LE[i], vertices[ndim*n+i]) RE[i] = fmax(RE[i], vertices[ndim*n+i]) use = 1 for i in range(ndim): if RE[i] < pLE[i] or LE[i] >= pRE[i]: use = 0 break pstart[i] = i64max( ((LE[i] - pLE[i])*idds[i]) - 1, 0) pend[i] = i64min( ((RE[i] - pLE[i])*idds[i]) + 1, img.shape[i]-1) # override for the low-dimensional case if ndim < 3: pstart[2] = 0 pend[2] = 0 if ndim < 2: pstart[1] = 0 pend[1] = 0 if use == 0: continue # Now our bounding box intersects, so we get the extents of our pixel # region which overlaps with the bounding box, and we'll check each # pixel in there. for pi in range(pstart[0], pend[0] + 1): ppoint[0] = (pi + 0.5) * dds[0] + pLE[0] for pj in range(pstart[1], pend[1] + 1): ppoint[1] = (pj + 0.5) * dds[1] + pLE[1] for pk in range(pstart[2], pend[2] + 1): ppoint[2] = (pk + 0.5) * dds[2] + pLE[2] # Now we just need to figure out if our ppoint is within # our set of vertices. sampler.map_real_to_unit(mapped_coord, vertices, ppoint) if not sampler.check_inside(mapped_coord): continue if (num_field_vals == 1): img[pi, pj, pk] = field_vals[0] else: img[pi, pj, pk] = sampler.sample_at_unit_point(mapped_coord, field_vals) mask[pi, pj, pk] = 1 free(vertices) free(field_vals) if return_mask: return img, mask_arr.astype("bool") else: return img # used as a cache to avoid repeatedly creating # instances of SPHKernelInterpolationTable kernel_tables = {} cdef class SPHKernelInterpolationTable: cdef public object kernel_name cdef kernel_func kernel cdef np.float64_t[::1] table cdef np.float64_t[::1] q2_vals cdef np.float64_t q2_range, iq2_range def __init__(self, kernel_name): self.kernel_name = kernel_name self.kernel = get_kernel_func(kernel_name) self.populate_table() @cython.initializedcheck(False) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.float64_t integrate_q2(self, np.float64_t q2) noexcept nogil: # See equation 30 of the SPLASH paper cdef int i # Our bounds are -sqrt(R*R - q2) and sqrt(R*R-q2) # And our R is always 1; note that our smoothing kernel functions # expect it to run from 0 .. 1, so we multiply the integrand by 2 cdef int N = 200 cdef np.float64_t qz cdef np.float64_t R = 1 cdef np.float64_t R0 = -math.sqrt(R*R-q2) cdef np.float64_t R1 = math.sqrt(R*R-q2) cdef np.float64_t dR = (R1-R0)/N # Set to our bounds cdef np.float64_t integral = 0.0 integral += self.kernel(math.sqrt(R0*R0 + q2)) integral += self.kernel(math.sqrt(R1*R1 + q2)) # We're going to manually conduct a trapezoidal integration for i in range(1, N): qz = R0 + i * dR integral += 2.0*self.kernel(math.sqrt(qz*qz + q2)) integral *= (R1-R0)/(2*N) return integral def populate_table(self): cdef int i self.table = cvarray(format="d", shape=(TABLE_NVALS,), itemsize=sizeof(np.float64_t)) self.q2_vals = cvarray(format="d", shape=(TABLE_NVALS,), itemsize=sizeof(np.float64_t)) # We run from 0 to 1 here over R for i in range(TABLE_NVALS): self.q2_vals[i] = i * 1.0/(TABLE_NVALS-1) self.table[i] = self.integrate_q2(self.q2_vals[i]) self.q2_range = self.q2_vals[TABLE_NVALS-1] - self.q2_vals[0] self.iq2_range = (TABLE_NVALS-1)/self.q2_range @cython.initializedcheck(False) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline np.float64_t interpolate(self, np.float64_t q2) noexcept nogil: cdef int index cdef np.float64_t F_interpolate index = ((q2 - self.q2_vals[0])*(self.iq2_range)) if index >= TABLE_NVALS: return 0.0 F_interpolate = self.table[index] + ( (self.table[index+1] - self.table[index]) *(q2 - self.q2_vals[index])*self.iq2_range) return F_interpolate def interpolate_array(self, np.float64_t[:] q2_vals): cdef np.float64_t[:] ret = np.empty(q2_vals.shape[0]) cdef int i for i in range(q2_vals.shape[0]): ret[i] = self.interpolate(q2_vals[i]) return np.array(ret) @cython.initializedcheck(False) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def pixelize_sph_kernel_projection( np.float64_t[:, :] buff, np.uint8_t[:, :] mask, any_float[:] posx, any_float[:] posy, any_float[:] posz, any_float[:] hsml, any_float[:] pmass, any_float[:] pdens, any_float[:] quantity_to_smooth, bounds, kernel_name="cubic", weight_field=None, _check_period = (1, 1, 1), period=None): cdef np.intp_t xsize, ysize cdef np.float64_t x_min, x_max, y_min, y_max, z_min, z_max, prefactor_j cdef np.int64_t xi, yi, x0, x1, y0, y1, xxi, yyi cdef np.float64_t q_ij2, posx_diff, posy_diff, ih_j2 cdef np.float64_t x, y, dx, dy, idx, idy, h_j2, px, py, pz cdef np.float64_t period_x = 0, period_y = 0, period_z = 0 cdef int i, j, ii, jj, kk cdef np.float64_t[:] _weight_field cdef int * xiter cdef int * yiter cdef int * ziter cdef np.float64_t * xiterv cdef np.float64_t * yiterv cdef np.float64_t * ziterv cdef np.int8_t[3] check_period if weight_field is not None: _weight_field = weight_field if period is not None: period_x = period[0] period_y = period[1] period_z = period[2] for i in range(3): check_period[i] = np.int8(_check_period[i]) # we find the x and y range over which we have pixels and we find how many # pixels we have in each dimension xsize, ysize = buff.shape[0], buff.shape[1] x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] z_min = bounds[4] z_max = bounds[5] dx = (x_max - x_min) / xsize dy = (y_max - y_min) / ysize idx = 1.0/dx idy = 1.0/dy if kernel_name not in kernel_tables: kernel_tables[kernel_name] = SPHKernelInterpolationTable(kernel_name) cdef SPHKernelInterpolationTable itab = kernel_tables[kernel_name] with nogil, parallel(): # loop through every particle # NOTE: this loop can be quite time consuming. However it is easily # parallelizable in multiple ways, such as: # 1) use multiple cores to process individual particles (the outer loop) # 2) use multiple cores to process individual pixels for a given particle # (the inner loops) # Depending on the ratio of particles' "sphere of influence" (a.k.a. the smoothing # length) to the physical width of the pixels, different parallelization # strategies may yield different speed-ups. Strategy #1 works better in the case # of lots of itty bitty particles. Strategy #2 works well when we have a # not-very-large-number of reasonably large-compared-to-pixels particles. We # currently employ #1 as its workload is more even and consistent, even though it # comes with a price of an additional, per thread memory for storing the # intermediate results. local_buff = malloc(sizeof(np.float64_t) * xsize * ysize) xiterv = malloc(sizeof(np.float64_t) * 2) yiterv = malloc(sizeof(np.float64_t) * 2) ziterv = malloc(sizeof(np.float64_t) * 2) xiter = malloc(sizeof(int) * 2) yiter = malloc(sizeof(int) * 2) ziter = malloc(sizeof(int) * 2) xiter[0] = yiter[0] = ziter[0] = 0 xiterv[0] = yiterv[0] = ziterv[0] = 0.0 for i in range(xsize * ysize): local_buff[i] = 0.0 for j in prange(0, posx.shape[0], schedule="dynamic"): if j % 100000 == 0: with gil: PyErr_CheckSignals() xiter[1] = yiter[1] = ziter[1] = 999 if check_period[0] == 1: if posx[j] - hsml[j] < x_min: xiter[1] = +1 xiterv[1] = period_x elif posx[j] + hsml[j] > x_max: xiter[1] = -1 xiterv[1] = -period_x if check_period[1] == 1: if posy[j] - hsml[j] < y_min: yiter[1] = +1 yiterv[1] = period_y elif posy[j] + hsml[j] > y_max: yiter[1] = -1 yiterv[1] = -period_y if check_period[2] == 1: if posz[j] - hsml[j] < z_min: ziter[1] = +1 ziterv[1] = period_z elif posz[j] + hsml[j] > z_max: ziter[1] = -1 ziterv[1] = -period_z # we set the smoothing length squared with lower limit of the pixel # Nope! that causes weird grid resolution dependences and increases # total values when resolution elements have hsml < grid spacing h_j2 = hsml[j]*hsml[j] ih_j2 = 1.0/h_j2 prefactor_j = pmass[j] / pdens[j] / hsml[j]**2 * quantity_to_smooth[j] if weight_field is not None: prefactor_j *= _weight_field[j] # Discussion point: do we want the hsml margin on the z direction? # it's consistent with Ray and Region selections, I think, # but does tend to 'tack on' stuff compared to the nominal depth for kk in range(2): # discard if z is outside bounds if ziter[kk] == 999: continue pz = posz[j] + ziterv[kk] ## removed hsml 'margin' in the projection direction to avoid ## double-counting particles near periodic edges ## and adding extra 'depth' to projections #if (pz + hsml[j] < z_min) or (pz - hsml[j] > z_max): continue if (pz < z_min) or (pz > z_max): continue for ii in range(2): if xiter[ii] == 999: continue px = posx[j] + xiterv[ii] if (px + hsml[j] < x_min) or (px - hsml[j] > x_max): continue for jj in range(2): if yiter[jj] == 999: continue py = posy[j] + yiterv[jj] if (py + hsml[j] < y_min) or (py - hsml[j] > y_max): continue # here we find the pixels which this particle contributes to x0 = ((px - hsml[j] - x_min)*idx) x1 = ((px + hsml[j] - x_min)*idx) x0 = iclip(x0-1, 0, xsize) x1 = iclip(x1+1, 0, xsize) y0 = ((py - hsml[j] - y_min)*idy) y1 = ((py + hsml[j] - y_min)*idy) y0 = iclip(y0-1, 0, ysize) y1 = iclip(y1+1, 0, ysize) # found pixels we deposit on, loop through those pixels for xi in range(x0, x1): # we use the centre of the pixel to calculate contribution x = (xi + 0.5) * dx + x_min posx_diff = px - x posx_diff = posx_diff * posx_diff if posx_diff > h_j2: continue for yi in range(y0, y1): y = (yi + 0.5) * dy + y_min posy_diff = py - y posy_diff = posy_diff * posy_diff if posy_diff > h_j2: continue q_ij2 = (posx_diff + posy_diff) * ih_j2 if q_ij2 >= 1: continue # see equation 32 of the SPLASH paper # now we just use the kernel projection local_buff[xi + yi*xsize] += prefactor_j * itab.interpolate(q_ij2) mask[xi, yi] = 1 with gil: for xxi in range(xsize): for yyi in range(ysize): buff[xxi, yyi] += local_buff[xxi + yyi*xsize] free(local_buff) free(xiterv) free(yiterv) free(xiter) free(yiter) return mask @cython.boundscheck(False) @cython.wraparound(False) def interpolate_sph_positions_gather(np.float64_t[:] buff, np.float64_t[:, ::1] tree_positions, np.float64_t[:, ::1] field_positions, np.float64_t[:] hsml, np.float64_t[:] pmass, np.float64_t[:] pdens, np.float64_t[:] quantity_to_smooth, PyKDTree kdtree, int use_normalization=1, kernel_name="cubic", pbar=None, int num_neigh=32): """ This function takes in arbitrary positions, field_positions, at which to perform a nearest neighbor search and perform SPH interpolation. The results are stored in the buffer, buff, which is in the same order as the field_positions are put in. """ cdef np.float64_t q_ij, h_j2, ih_j2, prefactor_j, smoothed_quantity_j cdef np.float64_t * pos_ptr cdef int i, particle, index cdef BoundedPriorityQueue queue = BoundedPriorityQueue(num_neigh, True) cdef np.float64_t[:] buff_den cdef KDTree * ctree = kdtree._tree # Which dimensions shall we use for spatial distances? cdef axes_range axes set_axes_range(&axes, -1) # Only allocate memory if we are using normalization if use_normalization: buff_den = np.zeros(buff.shape[0], dtype="float64") kernel = get_kernel_func(kernel_name) # Loop through all the positions we want to interpolate the SPH field onto with nogil: for i in range(0, buff.shape[0]): queue.size = 0 # Update the current position pos_ptr = &field_positions[i, 0] # Use the KDTree to find the nearest neighbors find_neighbors(pos_ptr, tree_positions, queue, ctree, -1, &axes) # Set the smoothing length squared to the square of the distance # of the furthest nearest neighbor h_j2 = queue.heap[0] ih_j2 = 1.0/h_j2 # Loop through each nearest neighbor and add contribution to the # buffer for index in range(queue.max_elements): particle = queue.pids[index] # Calculate contribution of this particle prefactor_j = (pmass[particle] / pdens[particle] / hsml[particle]**3) q_ij = math.sqrt(queue.heap[index]*ih_j2) smoothed_quantity_j = (prefactor_j * quantity_to_smooth[particle] * kernel(q_ij)) # See equations 6, 9, and 11 of the SPLASH paper buff[i] += smoothed_quantity_j if use_normalization: buff_den[i] += prefactor_j * kernel(q_ij) if use_normalization: normalization_1d_utility(buff, buff_den) @cython.boundscheck(False) @cython.wraparound(False) def interpolate_sph_grid_gather(np.float64_t[:, :, :] buff, np.float64_t[:, ::1] tree_positions, np.float64_t[:] bounds, np.float64_t[:] hsml, np.float64_t[:] pmass, np.float64_t[:] pdens, np.float64_t[:] quantity_to_smooth, PyKDTree kdtree, int use_normalization=1, kernel_name="cubic", pbar=None, int num_neigh=32, *, int return_mask=0, ): """ This function takes in the bounds and number of cells in a grid (well, actually we implicitly calculate this from the size of buff). Then we can perform nearest neighbor search and SPH interpolation at the centre of each cell in the grid. """ cdef np.float64_t q_ij, h_j2, ih_j2, prefactor_j, smoothed_quantity_j cdef np.float64_t dx, dy, dz cdef np.float64_t[::1] pos = np.zeros(3, dtype="float64") cdef np.float64_t * pos_ptr = &pos[0] cdef int i, j, k, particle, index cdef BoundedPriorityQueue queue = BoundedPriorityQueue(num_neigh, True) cdef np.float64_t[:, :, :] buff_den cdef KDTree * ctree = kdtree._tree cdef int prog # Which dimensions shall we use for spatial distances? cdef axes_range axes set_axes_range(&axes, -1) # Only allocate memory if we are using normalization if use_normalization: buff_den = np.zeros([buff.shape[0], buff.shape[1], buff.shape[2]], dtype="float64") kernel = get_kernel_func(kernel_name) dx = (bounds[1] - bounds[0]) / buff.shape[0] dy = (bounds[3] - bounds[2]) / buff.shape[1] dz = (bounds[5] - bounds[4]) / buff.shape[2] # Loop through all the positions we want to interpolate the SPH field onto pbar = get_pbar(title="Interpolating (gather) SPH field", maxval=(buff.shape[0]*buff.shape[1]*buff.shape[2] // 10000)*10000) cdef np.ndarray[np.uint8_t, ndim=3] mask_arr = np.zeros_like(buff, dtype="uint8") cdef np.uint8_t[:, :, :] mask = mask_arr prog = 0 with nogil: for i in range(0, buff.shape[0]): for j in range(0, buff.shape[1]): for k in range(0, buff.shape[2]): prog += 1 if prog % 10000 == 0: with gil: PyErr_CheckSignals() pbar.update(prog) queue.size = 0 # Update the current position pos[0] = bounds[0] + (i + 0.5) * dx pos[1] = bounds[2] + (j + 0.5) * dy pos[2] = bounds[4] + (k + 0.5) * dz # Use the KDTree to find the nearest neighbors find_neighbors(pos_ptr, tree_positions, queue, ctree, -1, &axes) # Set the smoothing length squared to the square of the distance # of the furthest nearest neighbor h_j2 = queue.heap[0] ih_j2 = 1.0/h_j2 # Loop through each nearest neighbor and add contribution to the # buffer for index in range(queue.max_elements): particle = queue.pids[index] # Calculate contribution of this particle prefactor_j = (pmass[particle] / pdens[particle] / hsml[particle]**3) q_ij = math.sqrt(queue.heap[index]*ih_j2) smoothed_quantity_j = (prefactor_j * quantity_to_smooth[particle] * kernel(q_ij)) # See equations 6, 9, and 11 of the SPLASH paper buff[i, j, k] += smoothed_quantity_j mask[i, j, k] = 1 if use_normalization: buff_den[i, j, k] += prefactor_j * kernel(q_ij) if use_normalization: normalization_3d_utility(buff, buff_den) if return_mask: return mask_arr.astype("bool") @cython.initializedcheck(False) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def pixelize_sph_kernel_slice( np.float64_t[:, :] buff, np.uint8_t[:, :] mask, np.float64_t[:] posx, np.float64_t[:] posy, np.float64_t[:] posz, np.float64_t[:] hsml, np.float64_t[:] pmass, np.float64_t[:] pdens, np.float64_t[:] quantity_to_smooth, bounds, np.float64_t slicez, kernel_name="cubic", _check_period = (1, 1, 1), period=None): #print("bounds, slicez, kernel_name, check_period, period") #print(bounds) #print(slicez) #print(kernel_name) #print(check_period) #print(period) #print() # bounds are [x0, x1, y0, y1], slicez is the single coordinate # of the slice along the normal direction. # similar method to pixelize_sph_kernel_projection cdef np.intp_t xsize, ysize cdef np.float64_t x_min, x_max, y_min, y_max, prefactor_j cdef np.int64_t xi, yi, x0, x1, y0, y1, xxi, yyi cdef np.float64_t q_ij, posx_diff, posy_diff, posz_diff, ih_j cdef np.float64_t x, y, dx, dy, idx, idy, h_j2, h_j, px, py, pz cdef int i, j, ii, jj cdef np.float64_t period_x = 0, period_y = 0, period_z = 0 cdef int * xiter cdef int * yiter cdef np.float64_t * xiterv cdef np.float64_t * yiterv cdef np.int8_t[3] check_period if period is not None: period_x = period[0] period_y = period[1] period_z = period[2] for i in range(3): check_period[i] = np.int8(_check_period[i]) xsize, ysize = buff.shape[0], buff.shape[1] x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] dx = (x_max - x_min) / xsize dy = (y_max - y_min) / ysize idx = 1.0/dx idy = 1.0/dy kernel = get_kernel_func(kernel_name) #print('particle index, ii, jj, px, py, pz') with nogil, parallel(): # NOTE see note in pixelize_sph_kernel_projection local_buff = malloc(sizeof(np.float64_t) * xsize * ysize) xiterv = malloc(sizeof(np.float64_t) * 2) yiterv = malloc(sizeof(np.float64_t) * 2) xiter = malloc(sizeof(int) * 2) yiter = malloc(sizeof(int) * 2) xiter[0] = yiter[0] = 0 xiterv[0] = yiterv[0] = 0.0 for i in range(xsize * ysize): local_buff[i] = 0.0 for j in prange(0, posx.shape[0], schedule="dynamic"): if j % 100000 == 0: with gil: PyErr_CheckSignals() #with gil: # print(j) xiter[1] = yiter[1] = 999 pz = posz[j] if check_period[0] == 1: if posx[j] - hsml[j] < x_min: xiter[1] = 1 xiterv[1] = period_x elif posx[j] + hsml[j] > x_max: xiter[1] = -1 xiterv[1] = -period_x if check_period[1] == 1: if posy[j] - hsml[j] < y_min: yiter[1] = 1 yiterv[1] = period_y elif posy[j] + hsml[j] > y_max: yiter[1] = -1 yiterv[1] = -period_y if check_period[2] == 1: # z of particle might be < hsml from the slice plane # but across a periodic boundary if posz[j] - hsml[j] > slicez: pz = posz[j] - period_z elif posz[j] + hsml[j] < slicez: pz = posz[j] + period_z h_j2 = hsml[j] * hsml[j] #fmax(hsml[j]*hsml[j], dx*dy) h_j = hsml[j] #math.sqrt(h_j2) ih_j = 1.0/h_j posz_diff = pz - slicez posz_diff = posz_diff * posz_diff if posz_diff > h_j2: continue prefactor_j = pmass[j] / pdens[j] / hsml[j]**3 prefactor_j *= quantity_to_smooth[j] for ii in range(2): if xiter[ii] == 999: continue px = posx[j] + xiterv[ii] if (px + hsml[j] < x_min) or (px - hsml[j] > x_max): continue for jj in range(2): if yiter[jj] == 999: continue py = posy[j] + yiterv[jj] if (py + hsml[j] < y_min) or (py - hsml[j] > y_max): continue x0 = ( (px - hsml[j] - x_min) * idx) x1 = ( (px + hsml[j] - x_min) * idx) x0 = iclip(x0-1, 0, xsize) x1 = iclip(x1+1, 0, xsize) y0 = ( (py - hsml[j] - y_min) * idy) y1 = ( (py + hsml[j] - y_min) * idy) y0 = iclip(y0-1, 0, ysize) y1 = iclip(y1+1, 0, ysize) #with gil: # print(ii, jj, px, py, pz) # Now we know which pixels to deposit onto for this particle, # so loop over them and add this particle's contribution for xi in range(x0, x1): x = (xi + 0.5) * dx + x_min posx_diff = px - x posx_diff = posx_diff * posx_diff if posx_diff > h_j2: continue for yi in range(y0, y1): y = (yi + 0.5) * dy + y_min posy_diff = py - y posy_diff = posy_diff * posy_diff if posy_diff > h_j2: continue # see equation 4 of the SPLASH paper q_ij = math.sqrt(posx_diff + posy_diff + posz_diff) * ih_j if q_ij >= 1: continue # see equations 6, 9, and 11 of the SPLASH paper local_buff[xi + yi*xsize] += prefactor_j * kernel(q_ij) mask[xi, yi] = 1 with gil: for xxi in range(xsize): for yyi in range(ysize): buff[xxi, yyi] += local_buff[xxi + yyi*xsize] free(local_buff) free(xiterv) free(yiterv) free(xiter) free(yiter) @cython.initializedcheck(False) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def pixelize_sph_kernel_arbitrary_grid(np.float64_t[:, :, :] buff, np.float64_t[:] posx, np.float64_t[:] posy, np.float64_t[:] posz, np.float64_t[:] hsml, np.float64_t[:] pmass, np.float64_t[:] pdens, np.float64_t[:] quantity_to_smooth, bounds, pbar=None, kernel_name="cubic", check_period=True, period=None): cdef np.intp_t xsize, ysize, zsize cdef np.float64_t x_min, x_max, y_min, y_max, z_min, z_max, prefactor_j cdef np.int64_t xi, yi, zi, x0, x1, y0, y1, z0, z1 cdef np.float64_t q_ij, posx_diff, posy_diff, posz_diff, px, py, pz cdef np.float64_t x, y, z, dx, dy, dz, idx, idy, idz, h_j2, h_j, ih_j # cdef np.float64_t h_j3 cdef int j, ii, jj, kk cdef np.float64_t period_x = 0, period_y = 0, period_z = 0 cdef int xiter[2] cdef int yiter[2] cdef int ziter[2] cdef np.float64_t xiterv[2] cdef np.float64_t yiterv[2] cdef np.float64_t ziterv[2] cdef int[3] periodic xiter[0] = yiter[0] = ziter[0] = 0 xiterv[0] = yiterv[0] = ziterv[0] = 0.0 if hasattr(check_period, "__len__"): periodic[0] = int(check_period[0]) periodic[1] = int(check_period[1]) periodic[2] = int(check_period[2]) else: _cp = int(check_period) periodic[0] = _cp periodic[1] = _cp periodic[2] = _cp if period is not None: period_x = period[0] period_y = period[1] period_z = period[2] xsize, ysize, zsize = buff.shape[0], buff.shape[1], buff.shape[2] x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] z_min = bounds[4] z_max = bounds[5] dx = (x_max - x_min) / xsize dy = (y_max - y_min) / ysize dz = (z_max - z_min) / zsize idx = 1.0/dx idy = 1.0/dy idz = 1.0/dz kernel = get_kernel_func(kernel_name) # nogil seems dangerous here, but there are no actual parallel # sections (e.g., prange instead of range) used here. # However, for future writers: # !! the final buff array mutation has no protections against # !! race conditions (e.g., OpenMP's atomic read/write), and # !! cython doesn't seem to provide such options. # (other routines in this file use private variable buffer arrays # and add everything together at the end, but grid arrays can get # big fast, and having such a large array in each thread could # cause memory use issues.) with nogil: # TODO make this parallel without using too much memory for j in range(0, posx.shape[0]): if j % 50000 == 0: with gil: if(pbar is not None): pbar.update(50000) PyErr_CheckSignals() # end with gil xiter[1] = yiter[1] = ziter[1] = 999 xiterv[1] = yiterv[1] = ziterv[1] = 0.0 if periodic[0] == 1: if posx[j] - hsml[j] < x_min: xiter[1] = +1 xiterv[1] = period_x elif posx[j] + hsml[j] > x_max: xiter[1] = -1 xiterv[1] = -period_x if periodic[1] == 1: if posy[j] - hsml[j] < y_min: yiter[1] = +1 yiterv[1] = period_y elif posy[j] + hsml[j] > y_max: yiter[1] = -1 yiterv[1] = -period_y if periodic[2] == 1: if posz[j] - hsml[j] < z_min: ziter[1] = +1 ziterv[1] = period_z elif posz[j] + hsml[j] > z_max: ziter[1] = -1 ziterv[1] = -period_z #h_j3 = fmax(hsml[j]*hsml[j]*hsml[j], dx*dy*dz) h_j = hsml[j] #math.cbrt(h_j3) h_j2 = h_j*h_j ih_j = 1/h_j prefactor_j = pmass[j] / pdens[j] / hsml[j]**3 * quantity_to_smooth[j] for ii in range(2): if xiter[ii] == 999: continue px = posx[j] + xiterv[ii] if (px + hsml[j] < x_min) or (px - hsml[j] > x_max): continue for jj in range(2): if yiter[jj] == 999: continue py = posy[j] + yiterv[jj] if (py + hsml[j] < y_min) or (py - hsml[j] > y_max): continue for kk in range(2): if ziter[kk] == 999: continue pz = posz[j] + ziterv[kk] if (pz + hsml[j] < z_min) or (pz - hsml[j] > z_max): continue x0 = ( (px - hsml[j] - x_min) * idx) x1 = ( (px + hsml[j] - x_min) * idx) x0 = iclip(x0-1, 0, xsize) x1 = iclip(x1+1, 0, xsize) y0 = ( (py - hsml[j] - y_min) * idy) y1 = ( (py + hsml[j] - y_min) * idy) y0 = iclip(y0-1, 0, ysize) y1 = iclip(y1+1, 0, ysize) z0 = ( (pz - hsml[j] - z_min) * idz) z1 = ( (pz + hsml[j] - z_min) * idz) z0 = iclip(z0-1, 0, zsize) z1 = iclip(z1+1, 0, zsize) # Now we know which voxels to deposit onto for this particle, # so loop over them and add this particle's contribution for xi in range(x0, x1): x = (xi + 0.5) * dx + x_min posx_diff = px - x posx_diff = posx_diff * posx_diff if posx_diff > h_j2: continue for yi in range(y0, y1): y = (yi + 0.5) * dy + y_min posy_diff = py - y posy_diff = posy_diff * posy_diff if posy_diff > h_j2: continue for zi in range(z0, z1): z = (zi + 0.5) * dz + z_min posz_diff = pz - z posz_diff = posz_diff * posz_diff if posz_diff > h_j2: continue # see equation 4 of the SPLASH paper q_ij = math.sqrt(posx_diff + posy_diff + posz_diff) * ih_j if q_ij >= 1: continue # shared variable buff should not # be mutatated in a nogil section # where different threads may change # the same array element buff[xi, yi, zi] += prefactor_j \ * kernel(q_ij) def pixelize_element_mesh_line(np.ndarray[np.float64_t, ndim=2] coords, np.ndarray[np.int64_t, ndim=2] conn, np.ndarray[np.float64_t, ndim=1] start_point, np.ndarray[np.float64_t, ndim=1] end_point, npoints, np.ndarray[np.float64_t, ndim=2] field, int index_offset = 0): # This routine chooses the correct element sampler to interpolate field # values at evenly spaced points along a sampling line cdef np.float64_t *vertices cdef np.float64_t *field_vals cdef int nvertices = conn.shape[1] cdef int ndim = coords.shape[1] cdef int num_field_vals = field.shape[1] cdef int num_plot_nodes = npoints cdef int num_intervals = npoints - 1 cdef double[4] mapped_coord cdef ElementSampler sampler cdef np.ndarray[np.float64_t, ndim=1] lin_vec cdef np.ndarray[np.float64_t, ndim=1] lin_inc cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points cdef np.int64_t i, n, j, k cdef np.ndarray[np.float64_t, ndim=1] arc_length cdef np.float64_t lin_length, inc_length cdef np.ndarray[np.float64_t, ndim=1] plot_values cdef np.float64_t sample_point[3] lin_vec = np.zeros(ndim, dtype="float64") lin_inc = np.zeros(ndim, dtype="float64") lin_sample_points = np.zeros((num_plot_nodes, ndim), dtype="float64") arc_length = np.zeros(num_plot_nodes, dtype="float64") plot_values = np.zeros(num_plot_nodes, dtype="float64") # Pick the right sampler and allocate storage for the mapped coordinate if ndim == 3 and nvertices == 4: sampler = P1Sampler3D() elif ndim == 3 and nvertices == 6: sampler = W1Sampler3D() elif ndim == 3 and nvertices == 8: sampler = Q1Sampler3D() elif ndim == 3 and nvertices == 20: sampler = S2Sampler3D() elif ndim == 2 and nvertices == 3: sampler = P1Sampler2D() elif ndim == 1 and nvertices == 2: sampler = P1Sampler1D() elif ndim == 2 and nvertices == 4: sampler = Q1Sampler2D() elif ndim == 2 and nvertices == 9: sampler = Q2Sampler2D() elif ndim == 2 and nvertices == 6: sampler = T2Sampler2D() elif ndim == 3 and nvertices == 10: sampler = Tet2Sampler3D() else: raise YTElementTypeNotRecognized(ndim, nvertices) # allocate temporary storage vertices = malloc(ndim * sizeof(np.float64_t) * nvertices) field_vals = malloc(sizeof(np.float64_t) * num_field_vals) lin_vec = end_point - start_point lin_length = np.linalg.norm(lin_vec) lin_inc = lin_vec / num_intervals inc_length = lin_length / num_intervals for j in range(ndim): lin_sample_points[0, j] = start_point[j] arc_length[0] = 0 for i in range(1, num_intervals + 1): for j in range(ndim): lin_sample_points[i, j] = lin_sample_points[i-1, j] + lin_inc[j] arc_length[i] = arc_length[i-1] + inc_length for i in range(num_intervals + 1): for j in range(3): if j < ndim: sample_point[j] = lin_sample_points[i][j] else: sample_point[j] = 0 for ci in range(conn.shape[0]): for n in range(num_field_vals): field_vals[n] = field[ci, n] # Fill the vertices for n in range(nvertices): cj = conn[ci, n] - index_offset for k in range(ndim): vertices[ndim*n + k] = coords[cj, k] sampler.map_real_to_unit(mapped_coord, vertices, sample_point) if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1: continue elif not sampler.check_inside(mapped_coord): raise ValueError("Check to see that both starting and ending line points " "are within the domain of the mesh.") plot_values[i] = sampler.sample_at_unit_point(mapped_coord, field_vals) break free(vertices) free(field_vals) return arc_length, plot_values # intended for use in ParticleImageBuffer @cython.boundscheck(False) @cython.wraparound(False) def rotate_particle_coord_pib(np.float64_t[:] px, np.float64_t[:] py, np.float64_t[:] pz, center, width, normal_vector, north_vector): # We want to do two rotations, one to first rotate our coordinates to have # the normal vector be the z-axis (i.e., the viewer's perspective), and then # another rotation to make the north-vector be the y-axis (i.e., north). # Fortunately, total_rotation_matrix = rotation_matrix_1 x rotation_matrix_2 cdef int num_particles = np.size(px) cdef np.float64_t[:] z_axis = np.array([0., 0., 1.], dtype="float64") cdef np.float64_t[:] y_axis = np.array([0., 1., 0.], dtype="float64") cdef np.float64_t[:, :] normal_rotation_matrix cdef np.float64_t[:] transformed_north_vector cdef np.float64_t[:, :] north_rotation_matrix cdef np.float64_t[:, :] rotation_matrix normal_rotation_matrix = get_rotation_matrix(normal_vector, z_axis) transformed_north_vector = np.matmul(normal_rotation_matrix, north_vector) north_rotation_matrix = get_rotation_matrix(transformed_north_vector, y_axis) rotation_matrix = np.matmul(north_rotation_matrix, normal_rotation_matrix) cdef np.float64_t[:] px_rotated = np.empty(num_particles, dtype="float64") cdef np.float64_t[:] py_rotated = np.empty(num_particles, dtype="float64") cdef np.float64_t[:] coordinate_matrix = np.empty(3, dtype="float64") cdef np.float64_t[:] rotated_coordinates cdef np.float64_t[:] rotated_center rotated_center = rotation_matmul( rotation_matrix, np.array([center[0], center[1], center[2]])) # set up the rotated bounds cdef np.float64_t rot_bounds_x0 = rotated_center[0] - width[0] / 2 cdef np.float64_t rot_bounds_x1 = rotated_center[0] + width[0] / 2 cdef np.float64_t rot_bounds_y0 = rotated_center[1] - width[1] / 2 cdef np.float64_t rot_bounds_y1 = rotated_center[1] + width[1] / 2 for i in range(num_particles): coordinate_matrix[0] = px[i] coordinate_matrix[1] = py[i] coordinate_matrix[2] = pz[i] rotated_coordinates = rotation_matmul( rotation_matrix, coordinate_matrix) px_rotated[i] = rotated_coordinates[0] py_rotated[i] = rotated_coordinates[1] return px_rotated, py_rotated, rot_bounds_x0, rot_bounds_x1, rot_bounds_y0, rot_bounds_y1 # version intended for SPH off-axis slices/projections # includes dealing with periodic boundaries, but also # shifts particles so center -> origin. # therefore, don't want to use this in the ParticleImageBuffer, # which expects differently centered coordinates. @cython.boundscheck(False) @cython.wraparound(False) def rotate_particle_coord(np.float64_t[:] px, np.float64_t[:] py, np.float64_t[:] pz, center, bounds, periodic, width, depth, normal_vector, north_vector): # We want to do two rotations, one to first rotate our coordinates to have # the normal vector be the z-axis (i.e., the viewer's perspective), and then # another rotation to make the north-vector be the y-axis (i.e., north). # Fortunately, total_rotation_matrix = rotation_matrix_1 x rotation_matrix_2 cdef np.int64_t num_particles = np.size(px) cdef np.float64_t[:] z_axis = np.array([0., 0., 1.], dtype="float64") cdef np.float64_t[:] y_axis = np.array([0., 1., 0.], dtype="float64") cdef np.float64_t[:, :] normal_rotation_matrix cdef np.float64_t[:] transformed_north_vector cdef np.float64_t[:, :] north_rotation_matrix cdef np.float64_t[:, :] rotation_matrix normal_rotation_matrix = get_rotation_matrix(normal_vector, z_axis) transformed_north_vector = np.matmul(normal_rotation_matrix, north_vector) north_rotation_matrix = get_rotation_matrix(transformed_north_vector, y_axis) rotation_matrix = np.matmul(north_rotation_matrix, normal_rotation_matrix) cdef np.float64_t[:] px_rotated = np.empty(num_particles, dtype="float64") cdef np.float64_t[:] py_rotated = np.empty(num_particles, dtype="float64") cdef np.float64_t[:] pz_rotated = np.empty(num_particles, dtype="float64") cdef np.float64_t[:] coordinate_matrix = np.empty(3, dtype="float64") cdef np.float64_t[:] rotated_coordinates cdef np.float64_t[:] rotated_center cdef np.int64_t i cdef int ax #rotated_center = rotation_matmul( # rotation_matrix, np.array([center[0], center[1], center[2]])) rotated_center = np.zeros((3,), dtype=center.dtype) # set up the rotated bounds cdef np.float64_t rot_bounds_x0 = rotated_center[0] - 0.5 * width[0] cdef np.float64_t rot_bounds_x1 = rotated_center[0] + 0.5 * width[0] cdef np.float64_t rot_bounds_y0 = rotated_center[1] - 0.5 * width[1] cdef np.float64_t rot_bounds_y1 = rotated_center[1] + 0.5 * width[1] cdef np.float64_t rot_bounds_z0 = rotated_center[2] - 0.5 * depth cdef np.float64_t rot_bounds_z1 = rotated_center[2] + 0.5 * depth for i in range(num_particles): coordinate_matrix[0] = px[i] coordinate_matrix[1] = py[i] coordinate_matrix[2] = pz[i] # centering: # make sure this also works for centers close to periodic edges # added consequence: the center is placed at the origin # (might as well keep it there in these temporary coordinates) for ax in range(3): # assumed center is zero even if non-periodic coordinate_matrix[ax] -= center[ax] if not periodic[ax]: continue period = bounds[2 * ax + 1] - bounds[2 * ax] # abs. difference between points in the volume is <= period if coordinate_matrix[ax] < -0.5 * period: coordinate_matrix[ax] += period if coordinate_matrix[ax] > 0.5 * period: coordinate_matrix[ax] -= period rotated_coordinates = rotation_matmul( rotation_matrix, coordinate_matrix) px_rotated[i] = rotated_coordinates[0] py_rotated[i] = rotated_coordinates[1] pz_rotated[i] = rotated_coordinates[2] return (px_rotated, py_rotated, pz_rotated, rot_bounds_x0, rot_bounds_x1, rot_bounds_y0, rot_bounds_y1, rot_bounds_z0, rot_bounds_z1) @cython.boundscheck(False) @cython.wraparound(False) def off_axis_projection_SPH(np.float64_t[:] px, np.float64_t[:] py, np.float64_t[:] pz, np.float64_t[:] particle_masses, np.float64_t[:] particle_densities, np.float64_t[:] smoothing_lengths, bounds, center, width, periodic, np.float64_t[:] quantity_to_smooth, np.float64_t[:, :] projection_array, np.uint8_t[:, :] mask, normal_vector, north_vector, weight_field=None, depth=None, kernel_name="cubic"): # periodic: periodicity of the data set: # Do nothing in event of a 0 normal vector if np.allclose(normal_vector, 0.): return if depth is None: # set to volume diagonal + margin -> won't exclude anything depth = 2. * np.sqrt((bounds[1] - bounds[0])**2 + (bounds[3] - bounds[2])**2 + (bounds[5] - bounds[4])**2) px_rotated, py_rotated, pz_rotated, \ rot_bounds_x0, rot_bounds_x1, \ rot_bounds_y0, rot_bounds_y1, \ rot_bounds_z0, rot_bounds_z1 = rotate_particle_coord(px, py, pz, center, bounds, periodic, width, depth, normal_vector, north_vector) # check_period=0: assumed to be a small region compared to the box # size. The rotation already ensures that a center close to a # periodic edge works out fine. # since the simple single-coordinate modulo math periodicity # does not apply to the *rotated* coordinates, the periodicity # approach implemented for this along-axis projection method # would fail here check_period = np.array([0, 0, 0], dtype="int") pixelize_sph_kernel_projection(projection_array, mask, px_rotated, py_rotated, pz_rotated, smoothing_lengths, particle_masses, particle_densities, quantity_to_smooth, [rot_bounds_x0, rot_bounds_x1, rot_bounds_y0, rot_bounds_y1, rot_bounds_z0, rot_bounds_z1], weight_field=weight_field, _check_period=check_period, kernel_name=kernel_name) # like slice pixelization, but for off-axis planes def pixelize_sph_kernel_cutting( np.float64_t[:, :] buff, np.uint8_t[:, :] mask, np.float64_t[:] posx, np.float64_t[:] posy, np.float64_t[:] posz, np.float64_t[:] hsml, np.float64_t[:] pmass, np.float64_t[:] pdens, np.float64_t[:] quantity_to_smooth, center, widthxy, normal_vector, north_vector, boxbounds, periodic, kernel_name="cubic", int check_period=1): if check_period == 0: periodic = np.zeros(3, dtype=bool) posx_rot, posy_rot, posz_rot, \ rot_bounds_x0, rot_bounds_x1, \ rot_bounds_y0, rot_bounds_y1, \ rot_bounds_z0, _ = rotate_particle_coord(posx, posy, posz, center, boxbounds, periodic, widthxy, 0., normal_vector, north_vector) bounds_rot = np.array([rot_bounds_x0, rot_bounds_x1, rot_bounds_y0, rot_bounds_y1]) slicez_rot = rot_bounds_z0 pixelize_sph_kernel_slice(buff, mask, posx_rot, posy_rot, posz_rot, hsml, pmass, pdens, quantity_to_smooth, bounds_rot, slicez_rot, kernel_name=kernel_name, _check_period=np.zeros(3, dtype="int"), period=None) @cython.boundscheck(False) @cython.wraparound(False) cdef np.float64_t[:] rotation_matmul(np.float64_t[:, :] rotation_matrix, np.float64_t[:] coordinate_matrix): cdef np.float64_t[:] out = np.zeros(3) for i in range(3): for j in range(3): out[i] += rotation_matrix[i, j] * coordinate_matrix[j] return out @cython.boundscheck(False) @cython.wraparound(False) cpdef np.float64_t[:, :] get_rotation_matrix(np.float64_t[:] normal_vector, np.float64_t[:] final_vector): """ Returns a numpy rotation matrix corresponding to the rotation of the given normal vector to the specified final_vector. See https://math.stackexchange.com/a/476311 although note we return the inverse of what's specified there. """ cdef np.float64_t[:] normal_unit_vector = normal_vector / np.linalg.norm(normal_vector) cdef np.float64_t[:] final_unit_vector = final_vector / np.linalg.norm(final_vector) cdef np.float64_t[:] v = np.cross(final_unit_vector, normal_unit_vector) cdef np.float64_t s = np.linalg.norm(v) cdef np.float64_t c = np.dot(final_unit_vector, normal_unit_vector) # if the normal vector is identical to the final vector, just return the # identity matrix if np.isclose(c, 1, rtol=1e-09): return np.identity(3, dtype="float64") # if the normal vector is the negative final vector, return the appropriate # rotation matrix for flipping your coordinate system. if np.isclose(s, 0, rtol=1e-09): return np.array([[0, -1, 0],[-1, 0, 0],[0, 0, -1]], dtype="float64") cdef np.float64_t[:, :] cross_product_matrix = np.array([[0, -1 * v[2], v[1]], [v[2], 0, -1 * v[0]], [-1 * v[1], v[0], 0]], dtype="float64") return np.linalg.inv(np.identity(3, dtype="float64") + cross_product_matrix + np.matmul(cross_product_matrix, cross_product_matrix) * 1/(1+c)) @cython.boundscheck(False) @cython.wraparound(False) def normalization_3d_utility(np.float64_t[:, :, :] num, np.float64_t[:, :, :] den): cdef int i, j, k for i in range(num.shape[0]): for j in range(num.shape[1]): for k in range(num.shape[2]): if den[i, j, k] != 0.0: num[i, j, k] = num[i, j, k] / den[i, j, k] @cython.boundscheck(False) @cython.wraparound(False) def normalization_2d_utility(np.float64_t[:, :] num, np.float64_t[:, :] den): cdef int i, j for i in range(num.shape[0]): for j in range(num.shape[1]): if den[i, j] != 0.0: num[i, j] = num[i, j] / den[i, j] @cython.boundscheck(False) @cython.wraparound(False) def normalization_1d_utility(np.float64_t[:] num, np.float64_t[:] den): cdef int i for i in range(num.shape[0]): if den[i] != 0.0: num[i] = num[i] / den[i] yt-project-yt-f043ac8/yt/utilities/lib/platform_dep.h000066400000000000000000000010541510711153200227140ustar00rootroot00000000000000#include #ifdef MS_WIN32 #include "malloc.h" /* note: the following implicitly sets a mininum VS version: conservative minimum is _MSC_VER >= 1928 (VS 2019, 16.8), but may work for VS 2015 but that has not been tested. see https://github.com/yt-project/yt/pull/4980 and https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance */ #include #include #elif defined(__FreeBSD__) #include #include #include #else #include #include "alloca.h" #include #endif yt-project-yt-f043ac8/yt/utilities/lib/platform_dep_math.hpp000066400000000000000000000010711510711153200242640ustar00rootroot00000000000000/* This file provides a compatibility layout between MSVC, and different version of GCC. MSVC does not define isnormal in the std:: namespace, so we cannot import it from , but from instead. However with GCC-5, there is a clash between the definition of isnormal in and using C++14, so we need to import from cmath instead. */ #if _MSC_VER #include inline bool __isnormal(double x) { return isnormal(x); } #elif defined(__FreeBSD__) #else #include inline bool __isnormal(double x) { return std::isnormal(x); } #endif yt-project-yt-f043ac8/yt/utilities/lib/points_in_volume.pyx000066400000000000000000000221611510711153200242240ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ Checks for points contained in a volume """ import numpy as np cimport cython cimport numpy as np from libc.math cimport sqrt cdef extern from "math.h": double fabs(double x) @cython.wraparound(False) @cython.boundscheck(False) def planar_points_in_volume( np.ndarray[np.float64_t, ndim=2] points, np.ndarray[np.int8_t, ndim=1] pmask, # pixel mask np.ndarray[np.float64_t, ndim=1] left_edge, np.ndarray[np.float64_t, ndim=1] right_edge, np.ndarray[np.int32_t, ndim=3] mask, float dx): cdef np.ndarray[np.int8_t, ndim=1] \ valid = np.zeros(points.shape[0], dtype='int8') cdef int i, dim, count cdef int ex cdef double dx_inv cdef unsigned int idx[3] count = 0 dx_inv = 1.0 / dx for i in xrange(points.shape[0]): if pmask[i] == 0: continue ex = 1 for dim in xrange(3): if points[i,dim] < left_edge[dim] or points[i,dim] > right_edge[dim]: valid[i] = ex = 0 break if ex == 1: for dim in xrange(3): idx[dim] = \ ((points[i,dim] - left_edge[dim]) * dx_inv) if mask[idx[0], idx[1], idx[2]] == 1: valid[i] = 1 count += 1 cdef np.ndarray[np.int32_t, ndim=1] result = np.empty(count, dtype='int32') count = 0 for i in xrange(points.shape[0]): if valid[i] == 1 and pmask[i] == 1: result[count] = i count += 1 return result cdef inline void set_rotated_pos( np.float64_t cp[3], np.float64_t rdds[3][3], np.float64_t rorigin[3], int i, int j, int k): cdef int oi for oi in range(3): cp[oi] = rdds[0][oi] * (0.5 + i) \ + rdds[1][oi] * (0.5 + j) \ + rdds[2][oi] * (0.5 + k) \ + rorigin[oi] #@cython.wraparound(False) #@cython.boundscheck(False) def grid_points_in_volume( np.ndarray[np.float64_t, ndim=1] box_lengths, np.ndarray[np.float64_t, ndim=1] box_origin, np.ndarray[np.float64_t, ndim=2] rot_mat, np.ndarray[np.float64_t, ndim=1] grid_left_edge, np.ndarray[np.float64_t, ndim=1] grid_right_edge, np.ndarray[np.float64_t, ndim=1] dds, np.ndarray[np.int32_t, ndim=3] mask, int break_first): cdef int n[3] cdef int i, j, k cdef np.float64_t rds[3][3] cdef np.float64_t cur_pos[3] cdef np.float64_t rorigin[3] for i in range(3): rorigin[i] = 0.0 for i in range(3): n[i] = mask.shape[i] for j in range(3): # Set up our transposed dx, which has a component in every # direction rds[i][j] = dds[i] * rot_mat[j,i] # In our rotated coordinate system, the box origin is 0,0,0 # so we subtract the box_origin from the grid_origin and rotate # that rorigin[j] += (grid_left_edge[i] - box_origin[i]) * rot_mat[j,i] for i in range(n[0]): for j in range(n[1]): for k in range(n[2]): set_rotated_pos(cur_pos, rds, rorigin, i, j, k) if (cur_pos[0] > box_lengths[0]): continue if (cur_pos[1] > box_lengths[1]): continue if (cur_pos[2] > box_lengths[2]): continue if (cur_pos[0] < 0.0): continue if (cur_pos[1] < 0.0): continue if (cur_pos[2] < 0.0): continue if break_first: if mask[i,j,k]: return 1 else: mask[i,j,k] = 1 return 0 cdef void normalize_vector(np.float64_t vec[3]): cdef int i cdef np.float64_t norm = 0.0 for i in range(3): norm += vec[i]*vec[i] norm = sqrt(norm) for i in range(3): vec[i] /= norm cdef void get_cross_product(np.float64_t v1[3], np.float64_t v2[3], np.float64_t cp[3]): cp[0] = v1[1]*v2[2] - v1[2]*v2[1] cp[1] = v1[3]*v2[0] - v1[0]*v2[3] cp[2] = v1[0]*v2[1] - v1[1]*v2[0] #print(cp[0], cp[1], cp[2]) cdef int check_projected_overlap( np.float64_t sep_ax[3], np.float64_t sep_vec[3], int gi, np.float64_t b_vec[3][3], np.float64_t g_vec[3][3]): cdef int g_ax, b_ax cdef np.float64_t tba, tga, ba, ga, sep_dot ba = ga = sep_dot = 0.0 for g_ax in range(3): # We need the grid vectors, which we'll precompute here tba = tga = 0.0 for b_ax in range(3): tba += b_vec[g_ax][b_ax] * sep_vec[b_ax] tga += g_vec[g_ax][b_ax] * sep_vec[b_ax] ba += fabs(tba) ga += fabs(tga) sep_dot += sep_vec[g_ax] * sep_ax[g_ax] #print(sep_vec[0], sep_vec[1], sep_vec[2],) #print(sep_ax[0], sep_ax[1], sep_ax[2]) return (fabs(sep_dot) > ba+ga) # Now we do @cython.wraparound(False) @cython.boundscheck(False) def find_grids_in_inclined_box( np.ndarray[np.float64_t, ndim=2] box_vectors, np.ndarray[np.float64_t, ndim=1] box_center, np.ndarray[np.float64_t, ndim=2] grid_left_edges, np.ndarray[np.float64_t, ndim=2] grid_right_edges): # http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php?page=5 cdef int n = grid_right_edges.shape[0] cdef int g_ax, b_ax, gi cdef np.float64_t b_vec[3][3] cdef np.float64_t g_vec[3][3] cdef np.float64_t a_vec[3][3] cdef np.float64_t sep_ax[15][3] cdef np.float64_t sep_vec[3] cdef np.ndarray[np.int32_t, ndim=1] good = np.zeros(n, dtype='int32') cdef np.ndarray[np.float64_t, ndim=2] grid_centers # Fill in our axis unit vectors for b_ax in range(3): for g_ax in range(3): a_vec[b_ax][g_ax] = (b_ax == g_ax) grid_centers = (grid_right_edges + grid_left_edges)/2.0 # Now we pre-compute our candidate separating axes, because the unit # vectors for all the grids are identical for b_ax in range(3): # We have 6 principal axes we already know, which are the grid (domain) # principal axes and the box axes sep_ax[b_ax][0] = sep_ax[b_ax][1] = sep_ax[b_ax][2] = 0.0 sep_ax[b_ax][b_ax] = 1.0 # delta_ijk, for grid axes for g_ax in range(3): b_vec[b_ax][g_ax] = 0.5*box_vectors[b_ax,g_ax] sep_ax[b_ax + 3][g_ax] = b_vec[b_ax][g_ax] # box axes normalize_vector(sep_ax[b_ax + 3]) for g_ax in range(3): get_cross_product(b_vec[b_ax], a_vec[g_ax], sep_ax[b_ax*3 + g_ax + 6]) normalize_vector(sep_ax[b_ax*3 + g_ax + 6]) for gi in range(n): for g_ax in range(3): # Calculate the separation vector sep_vec[g_ax] = grid_centers[gi, g_ax] - box_center[g_ax] # Calculate the grid axis lengths g_vec[g_ax][0] = g_vec[g_ax][1] = g_vec[g_ax][2] = 0.0 g_vec[g_ax][g_ax] = 0.5 * (grid_right_edges[gi, g_ax] - grid_left_edges[gi, g_ax]) for b_ax in range(15): #print(b_ax,) if check_projected_overlap( sep_ax[b_ax], sep_vec, gi, b_vec, g_vec): good[gi] = 1 break return good def calculate_fill_grids(int fill_level, int refratio, int last_level, np.ndarray[np.int64_t, ndim=1] domain_width, np.ndarray[np.int64_t, ndim=1] cg_start_index, np.ndarray[np.int32_t, ndim=1] cg_dims, np.ndarray[np.int64_t, ndim=1] g_start_index, np.ndarray[np.int32_t, ndim=1] g_dims, np.ndarray[np.uint8_t, ndim=3, cast=True] g_child_mask): cdef np.int64_t cgstart[3] cdef np.int64_t gstart[3] cdef np.int64_t cgend[3] cdef np.int64_t gend[3] cdef np.int64_t dw[3] cdef np.int64_t cxi, cyi, czi, gxi, gyi, gzi, ci, cj, ck cdef int i, total = 0 for i in range(3): dw[i] = domain_width[i] cgstart[i] = cg_start_index[i] gstart[i] = g_start_index[i] cgend[i] = cgstart[i] + cg_dims[i] gend[i] = gstart[i] + g_dims[i] for cxi in range(cgstart[0], cgend[0]+1): ci = (cxi % dw[0]) if ci < 0: ci += dw[0] if ci < gstart[0]*refratio or ci >= gend[0]*refratio: continue gxi = ( (ci / refratio)) - gstart[0] for cyi in range(cgstart[1], cgend[1]): cj = (cyi % dw[1]) if cj < 0: cj += dw[1] if cj < gstart[1]*refratio or cj >= gend[1]*refratio: continue gyi = ( (cj / refratio)) - gstart[1] for czi in range(cgstart[2], cgend[2]): ck = (czi % dw[2]) if ck < 0: ck += dw[2] if ck < gstart[2]*refratio or cj >= gend[2]*refratio: continue gzi = ( (ck / refratio)) - gstart[2] if last_level or g_child_mask[gxi, gyi, gzi] > 0: total += 1 return total yt-project-yt-f043ac8/yt/utilities/lib/primitives.pxd000066400000000000000000000102261510711153200230000ustar00rootroot00000000000000cimport cython import numpy as np cimport numpy as np from .vec3_ops cimport cross, dot, subtract cdef struct Ray: np.float64_t origin[3] np.float64_t direction[3] np.float64_t inv_dir[3] np.float64_t data_val np.float64_t t_near np.float64_t t_far np.int64_t elem_id np.int64_t near_boundary cdef struct BBox: np.float64_t left_edge[3] np.float64_t right_edge[3] cdef struct RayHitData: np.float64_t u np.float64_t v np.float64_t t np.int64_t converged cdef struct Triangle: np.float64_t p0[3] np.float64_t p1[3] np.float64_t p2[3] np.int64_t elem_id cdef np.int64_t ray_bbox_intersect(Ray* ray, const BBox bbox) noexcept nogil cdef np.int64_t ray_triangle_intersect(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil cdef void triangle_centroid(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil cdef void triangle_bbox(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil cdef struct Patch: np.float64_t[8][3] v # 8 vertices per patch np.int64_t elem_id cdef void patchSurfaceFunc(const cython.floating[8][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] S) noexcept nogil cdef void patchSurfaceDerivU(const cython.floating[8][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Su) noexcept nogil cdef void patchSurfaceDerivV(const cython.floating[8][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Sv) noexcept nogil cdef RayHitData compute_patch_hit(cython.floating[8][3] verts, cython.floating[3] ray_origin, cython.floating[3] ray_direction) noexcept nogil cdef np.int64_t ray_patch_intersect(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil cdef void patch_centroid(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil cdef void patch_bbox(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil cdef struct TetPatch: np.float64_t[6][3] v # 6 vertices per patch np.int64_t elem_id cdef RayHitData compute_tet_patch_hit(cython.floating[6][3] verts, cython.floating[3] ray_origin, cython.floating[3] ray_direction) noexcept nogil cdef void tet_patchSurfaceFunc(const cython.floating[6][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] S) noexcept nogil cdef void tet_patchSurfaceDerivU(const cython.floating[6][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Su) noexcept nogil cdef void tet_patchSurfaceDerivV(const cython.floating[6][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Sv) noexcept nogil cdef np.int64_t ray_tet_patch_intersect(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil cdef void tet_patch_centroid(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil cdef void tet_patch_bbox(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil yt-project-yt-f043ac8/yt/utilities/lib/primitives.pyx000066400000000000000000000451741510711153200230370ustar00rootroot00000000000000# distutils: libraries = STD_LIBS """ This file contains definitions of the various primitives that can be used by the Cython ray-tracer for unstructured mesh rendering. To define a new primitive type, you need to define a struct that represents it. You also need to provide three functions: 1. A function that computes the intersection between a given ray and a given primitive. 2. A function that computes the centroid of the primitive type. 3. A function that computes the axis-aligned bounding box of a given primitive. """ cimport cython import numpy as np cimport numpy as np from libc.math cimport fabs from yt.utilities.lib.vec3_ops cimport L2_norm, cross, distance, dot, subtract cdef np.float64_t DETERMINANT_EPS = 1.0e-10 cdef np.float64_t INF = np.inf cdef extern from "platform_dep.h" nogil: double fmax(double x, double y) double fmin(double x, double y) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t ray_bbox_intersect(Ray* ray, const BBox bbox) noexcept nogil: ''' This returns an integer flag that indicates whether a ray and a bounding box intersect. It does not modify either either the ray or the box. ''' # https://tavianator.com/fast-branchless-raybounding-box-intersections/ cdef np.float64_t tmin = -INF cdef np.float64_t tmax = INF cdef np.int64_t i cdef np.float64_t t1, t2 for i in range(3): t1 = (bbox.left_edge[i] - ray.origin[i])*ray.inv_dir[i] t2 = (bbox.right_edge[i] - ray.origin[i])*ray.inv_dir[i] tmin = fmax(tmin, fmin(t1, t2)) tmax = fmin(tmax, fmax(t1, t2)) return tmax >= fmax(tmin, 0.0) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t ray_triangle_intersect(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil: ''' This returns an integer flag that indicates whether a triangle is the closest hit for the ray so far. If it is, the ray is updated to store the current triangle index and the distance to the first hit. The triangle used is the one indexed by "item" in the array of primitives. ''' # https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm cdef Triangle tri = ( primitives)[item] # edge vectors cdef np.float64_t e1[3] cdef np.float64_t e2[3] subtract(tri.p1, tri.p0, e1) subtract(tri.p2, tri.p0, e2) cdef np.float64_t P[3] cross(ray.direction, e2, P) cdef np.float64_t det, inv_det det = dot(e1, P) if(det > -DETERMINANT_EPS and det < DETERMINANT_EPS): return False inv_det = 1.0 / det cdef np.float64_t T[3] subtract(ray.origin, tri.p0, T) cdef np.float64_t u = dot(T, P) * inv_det if(u < 0.0 or u > 1.0): return False cdef np.float64_t Q[3] cross(T, e1, Q) cdef np.float64_t v = dot(ray.direction, Q) * inv_det if(v < 0.0 or u + v > 1.0): return False cdef np.float64_t t = dot(e2, Q) * inv_det if(t > DETERMINANT_EPS and t < ray.t_far): ray.t_far = t ray.elem_id = tri.elem_id return True return False @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void triangle_centroid(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil: ''' This computes the centroid of the input triangle. The triangle used is the one indexed by "item" in the array of primitives. The result will be stored in the numpy array passed in as "centroid". ''' cdef Triangle tri = ( primitives)[item] cdef np.int64_t i for i in range(3): centroid[i] = (tri.p0[i] + tri.p1[i] + tri.p2[i]) / 3.0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void triangle_bbox(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil: ''' This computes the bounding box of the input triangle. The triangle used is the one indexed by "item" in the array of primitives. The result will be stored in the input BBox. ''' cdef Triangle tri = ( primitives)[item] cdef np.int64_t i for i in range(3): bbox.left_edge[i] = fmin(fmin(tri.p0[i], tri.p1[i]), tri.p2[i]) bbox.right_edge[i] = fmax(fmax(tri.p0[i], tri.p1[i]), tri.p2[i]) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patchSurfaceFunc(const cython.floating[8][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] S) noexcept nogil: ''' This function is a parametric representation of the surface of a bi-quadratic patch. The inputs are the eight nodes that define a face of a 20-node hex element, and two parameters u and v that vary from -1 to 1 and tell you where you are on the surface of the patch. The output is the array 'S' that stores the physical (x, y, z) position of the corresponding point on the patch. This function is needed to compute the intersection of rays and bi-quadratic patches. ''' cdef int i for i in range(3): S[i] = 0.25*(1.0 - u)*(1.0 - v)*(-u - v - 1)*verts[0][i] + \ 0.25*(1.0 + u)*(1.0 - v)*( u - v - 1)*verts[1][i] + \ 0.25*(1.0 + u)*(1.0 + v)*( u + v - 1)*verts[2][i] + \ 0.25*(1.0 - u)*(1.0 + v)*(-u + v - 1)*verts[3][i] + \ 0.5*(1 - u)*(1 - v*v)*verts[4][i] + \ 0.5*(1 - u*u)*(1 - v)*verts[5][i] + \ 0.5*(1 + u)*(1 - v*v)*verts[6][i] + \ 0.5*(1 - u*u)*(1 + v)*verts[7][i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patchSurfaceDerivU(const cython.floating[8][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Su) noexcept nogil: ''' This function computes the derivative of the S(u, v) function w.r.t u. ''' cdef int i for i in range(3): Su[i] = (-0.25*(v - 1.0)*(u + v + 1) - 0.25*(u - 1.0)*(v - 1.0))*verts[0][i] + \ (-0.25*(v - 1.0)*(u - v - 1) - 0.25*(u + 1.0)*(v - 1.0))*verts[1][i] + \ ( 0.25*(v + 1.0)*(u + v - 1) + 0.25*(u + 1.0)*(v + 1.0))*verts[2][i] + \ ( 0.25*(v + 1.0)*(u - v + 1) + 0.25*(u - 1.0)*(v + 1.0))*verts[3][i] + \ 0.5*(v*v - 1.0)*verts[4][i] + u*(v - 1.0)*verts[5][i] - \ 0.5*(v*v - 1.0)*verts[6][i] - u*(v + 1.0)*verts[7][i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patchSurfaceDerivV(const cython.floating[8][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Sv) noexcept nogil: ''' This function computes the derivative of the S(u, v) function w.r.t v. ''' cdef int i for i in range(3): Sv[i] = (-0.25*(u - 1.0)*(u + v + 1) - 0.25*(u - 1.0)*(v - 1.0))*verts[0][i] + \ (-0.25*(u + 1.0)*(u - v - 1) + 0.25*(u + 1.0)*(v - 1.0))*verts[1][i] + \ ( 0.25*(u + 1.0)*(u + v - 1) + 0.25*(u + 1.0)*(v + 1.0))*verts[2][i] + \ ( 0.25*(u - 1.0)*(u - v + 1) - 0.25*(u - 1.0)*(v + 1.0))*verts[3][i] + \ 0.5*(u*u - 1.0)*verts[5][i] + v*(u - 1.0)*verts[4][i] - \ 0.5*(u*u - 1.0)*verts[7][i] - v*(u + 1.0)*verts[6][i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef RayHitData compute_patch_hit(cython.floating[8][3] verts, cython.floating[3] ray_origin, cython.floating[3] ray_direction) noexcept nogil: """ This function iteratively computes whether the bi-quadratic patch defined by the eight input nodes intersects with the given ray. Either way, information about the potential hit is stored in the returned RayHitData. """ # first we compute the two planes that define the ray. cdef cython.floating[3] n, N1, N2 cdef cython.floating A = dot(ray_direction, ray_direction) for i in range(3): n[i] = ray_direction[i] / A if ((fabs(n[0]) > fabs(n[1])) and (fabs(n[0]) > fabs(n[2]))): N1[0] = n[1] N1[1] =-n[0] N1[2] = 0.0 else: N1[0] = 0.0 N1[1] = n[2] N1[2] =-n[1] cross(N1, n, N2) cdef cython.floating d1 = -dot(N1, ray_origin) cdef cython.floating d2 = -dot(N2, ray_origin) # the initial guess is set to zero cdef cython.floating u = 0.0 cdef cython.floating v = 0.0 cdef cython.floating[3] S patchSurfaceFunc(verts, u, v, S) cdef cython.floating fu = dot(N1, S) + d1 cdef cython.floating fv = dot(N2, S) + d2 cdef cython.floating err = fmax(fabs(fu), fabs(fv)) # begin Newton iteration cdef cython.floating tol = 1.0e-5 cdef int iterations = 0 cdef int max_iter = 10 cdef cython.floating[3] Su cdef cython.floating[3] Sv cdef cython.floating J11, J12, J21, J22, det while ((err > tol) and (iterations < max_iter)): # compute the Jacobian patchSurfaceDerivU(verts, u, v, Su) patchSurfaceDerivV(verts, u, v, Sv) J11 = dot(N1, Su) J12 = dot(N1, Sv) J21 = dot(N2, Su) J22 = dot(N2, Sv) det = (J11*J22 - J12*J21) # update the u, v values u -= ( J22*fu - J12*fv) / det v -= (-J21*fu + J11*fv) / det patchSurfaceFunc(verts, u, v, S) fu = dot(N1, S) + d1 fv = dot(N2, S) + d2 err = fmax(fabs(fu), fabs(fv)) iterations += 1 # t is the distance along the ray to this hit cdef cython.floating t = distance(S, ray_origin) / L2_norm(ray_direction) # return hit data cdef RayHitData hd hd.u = u hd.v = v hd.t = t hd.converged = (iterations < max_iter) return hd @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t ray_patch_intersect(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil: ''' This returns an integer flag that indicates whether the given patch is the closest hit for the ray so far. If it is, the ray is updated to store the current primitive index and the distance to the first hit. The patch used is the one indexed by "item" in the array of primitives. ''' cdef Patch patch = ( primitives)[item] cdef RayHitData hd = compute_patch_hit(patch.v, ray.origin, ray.direction) # only count this is it's the closest hit if (hd.t < ray.t_near or hd.t > ray.t_far): return False if (fabs(hd.u) <= 1.0 and fabs(hd.v) <= 1.0 and hd.converged): # we have a hit, so update ray information ray.t_far = hd.t ray.elem_id = patch.elem_id return True return False @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patch_centroid(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil: ''' This computes the centroid of the input patch. The patch used is the one indexed by "item" in the array of primitives. The result will be stored in the numpy array passed in as "centroid". ''' cdef np.int64_t i, j cdef Patch patch = ( primitives)[item] for j in range(3): centroid[j] = 0.0 for i in range(8): for j in range(3): centroid[j] += patch.v[i][j] for j in range(3): centroid[j] /= 8.0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void patch_bbox(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil: ''' This computes the bounding box of the input patch. The patch used is the one indexed by "item" in the array of primitives. The result will be stored in the input BBox. ''' cdef np.int64_t i, j cdef Patch patch = ( primitives)[item] for j in range(3): bbox.left_edge[j] = patch.v[0][j] bbox.right_edge[j] = patch.v[0][j] for i in range(1, 8): for j in range(3): bbox.left_edge[j] = fmin(bbox.left_edge[j], patch.v[i][j]) bbox.right_edge[j] = fmax(bbox.right_edge[j], patch.v[i][j]) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patchSurfaceFunc(const cython.floating[6][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] S) noexcept nogil: cdef int i # Computes for canonical triangle coordinates for i in range(3): S[i] = (1.0 - 3.0*u + 2.0*u*u - 3.0*v + 2.0*v*v + 4.0*u*v)*verts[0][i] + \ (-u + 2.0*u*u)*verts[1][i] + \ (-v + 2.0*v*v)*verts[2][i] + \ (4.0*u - 4.0*u*u - 4.0*u*v)*verts[3][i] + \ (4.0*u*v)*verts[4][i] + \ (4.0*v - 4.0*v*v - 4.0*u*v)*verts[5][i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patchSurfaceDerivU(const cython.floating[6][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Su) noexcept nogil: cdef int i # Computes for canonical triangle coordinates for i in range(3): Su[i] = (-3.0 + 4.0*u + 4.0*v)*verts[0][i] + \ (-1.0 + 4.0*u)*verts[1][i] + \ (4.0 - 8.0*u - 4.0*v)*verts[3][i] + \ (4.0*v)*verts[4][i] + \ (-4.0*v)*verts[5][i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patchSurfaceDerivV(const cython.floating[6][3] verts, const cython.floating u, const cython.floating v, cython.floating[3] Sv) noexcept nogil: cdef int i # Computes for canonical triangle coordinates for i in range(3): Sv[i] = (-3.0 + 4.0*v + 4.0*u)*verts[0][i] + \ (-1.0 + 4.0*v)*verts[2][i] + \ (-4.0*u)*verts[3][i] + \ (4.0*u)*verts[4][i] + \ (4.0 - 8.0*v - 4.0*u)*verts[5][i] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef RayHitData compute_tet_patch_hit(cython.floating[6][3] verts, cython.floating[3] ray_origin, cython.floating[3] ray_direction) noexcept nogil: # first we compute the two planes that define the ray. cdef cython.floating[3] n, N1, N2 cdef cython.floating A = dot(ray_direction, ray_direction) for i in range(3): n[i] = ray_direction[i] / A if ((fabs(n[0]) > fabs(n[1])) and (fabs(n[0]) > fabs(n[2]))): N1[0] = n[1] N1[1] =-n[0] N1[2] = 0.0 else: N1[0] = 0.0 N1[1] = n[2] N1[2] =-n[1] cross(N1, n, N2) cdef cython.floating d1 = -dot(N1, ray_origin) cdef cython.floating d2 = -dot(N2, ray_origin) # the initial guess is set to zero cdef cython.floating u = 0.0 cdef cython.floating v = 0.0 cdef cython.floating[3] S tet_patchSurfaceFunc(verts, u, v, S) cdef cython.floating fu = dot(N1, S) + d1 cdef cython.floating fv = dot(N2, S) + d2 cdef cython.floating err = fmax(fabs(fu), fabs(fv)) # begin Newton iteration cdef cython.floating tol = 1.0e-5 cdef int iterations = 0 cdef int max_iter = 10 cdef cython.floating[3] Su cdef cython.floating[3] Sv cdef cython.floating J11, J12, J21, J22, det while ((err > tol) and (iterations < max_iter)): # compute the Jacobian tet_patchSurfaceDerivU(verts, u, v, Su) tet_patchSurfaceDerivV(verts, u, v, Sv) J11 = dot(N1, Su) J12 = dot(N1, Sv) J21 = dot(N2, Su) J22 = dot(N2, Sv) det = (J11*J22 - J12*J21) # update the u, v values u -= ( J22*fu - J12*fv) / det v -= (-J21*fu + J11*fv) / det tet_patchSurfaceFunc(verts, u, v, S) fu = dot(N1, S) + d1 fv = dot(N2, S) + d2 err = fmax(fabs(fu), fabs(fv)) iterations += 1 # t is the distance along the ray to this hit cdef cython.floating t = distance(S, ray_origin) / L2_norm(ray_direction) # return hit data cdef RayHitData hd hd.u = u hd.v = v hd.t = t hd.converged = (iterations < max_iter) return hd @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.int64_t ray_tet_patch_intersect(const void* primitives, const np.int64_t item, Ray* ray) noexcept nogil: cdef TetPatch tet_patch = ( primitives)[item] cdef RayHitData hd = compute_tet_patch_hit(tet_patch.v, ray.origin, ray.direction) # only count this is it's the closest hit if (hd.t < ray.t_near or hd.t > ray.t_far): return False if (0 <= hd.u and 0 <= hd.v and hd.u + hd.v <= 1.0 and hd.converged): # we have a hit, so update ray information ray.t_far = hd.t ray.elem_id = tet_patch.elem_id return True return False @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patch_centroid(const void *primitives, const np.int64_t item, np.float64_t[3] centroid) noexcept nogil: cdef np.int64_t i, j cdef TetPatch tet_patch = ( primitives)[item] for j in range(3): centroid[j] = 0.0 for i in range(6): for j in range(3): centroid[j] += tet_patch.v[i][j] for j in range(3): centroid[j] /= 6.0 @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef void tet_patch_bbox(const void *primitives, const np.int64_t item, BBox* bbox) noexcept nogil: cdef np.int64_t i, j cdef TetPatch tet_patch = ( primitives)[item] for j in range(3): bbox.left_edge[j] = tet_patch.v[0][j] bbox.right_edge[j] = tet_patch.v[0][j] for i in range(1, 6): for j in range(3): bbox.left_edge[j] = fmin(bbox.left_edge[j], tet_patch.v[i][j]) bbox.right_edge[j] = fmax(bbox.right_edge[j], tet_patch.v[i][j]) yt-project-yt-f043ac8/yt/utilities/lib/quad_tree.pyx000066400000000000000000000532521510711153200226110ustar00rootroot00000000000000 # distutils: libraries = STD_LIBS """ A refine-by-two AMR-specific quadtree """ import numpy as np cimport cython cimport numpy as np from libc.stdlib cimport free, malloc from yt.utilities.lib.fp_utils cimport fmax, fmin from yt.utilities.exceptions import YTIntDomainOverflow cdef extern from "platform_dep.h": # NOTE that size_t might not be int void *alloca(int) cdef struct QuadTreeNode: np.float64_t *val np.float64_t weight_val np.int64_t pos[2] QuadTreeNode *children[2][2] ctypedef void QTN_combine(QuadTreeNode *self, np.float64_t *val, np.float64_t weight_val, int nvals) cdef void QTN_add_value(QuadTreeNode *self, np.float64_t *val, np.float64_t weight_val, int nvals): cdef int i for i in range(nvals): self.val[i] += val[i] self.weight_val += weight_val cdef void QTN_max_value(QuadTreeNode *self, np.float64_t *val, np.float64_t weight_val, int nvals): cdef int i for i in range(nvals): self.val[i] = fmax(val[i], self.val[i]) self.weight_val = 1.0 cdef void QTN_min_value(QuadTreeNode *self, np.float64_t *val, np.float64_t weight_val, int nvals): cdef int i #cdef np.float64_t *big_num = 1.0 #big_num = 1.0 #1e10 for i in range(nvals): if self.val[i] == 0: self.val[i] = 1e50 # end if self.val[i] = fmin(val[i], self.val[i]) self.weight_val = 1.0 cdef void QTN_refine(QuadTreeNode *self, int nvals): cdef int i, j cdef np.int64_t npos[2] cdef np.float64_t *tvals = alloca( sizeof(np.float64_t) * nvals) for i in range(nvals): tvals[i] = 0.0 for i in range(2): npos[0] = self.pos[0] * 2 + i for j in range(2): npos[1] = self.pos[1] * 2 + j # We have to be careful with allocation... self.children[i][j] = QTN_initialize( npos, nvals, tvals, 0.0) cdef QuadTreeNode *QTN_initialize(np.int64_t pos[2], int nvals, np.float64_t *val, np.float64_t weight_val): cdef QuadTreeNode *node cdef int i, j node = malloc(sizeof(QuadTreeNode)) node.pos[0] = pos[0] node.pos[1] = pos[1] node.val = malloc( nvals * sizeof(np.float64_t)) for i in range(2): for j in range(2): node.children[i][j] = NULL if val != NULL: for i in range(nvals): node.val[i] = val[i] node.weight_val = weight_val return node cdef void QTN_free(QuadTreeNode *node): cdef int i, j for i in range(2): for j in range(2): if node.children[i][j] == NULL: continue QTN_free(node.children[i][j]) free(node.val) free(node) cdef class QuadTree: cdef int nvals cdef QuadTreeNode ***root_nodes cdef public np.int64_t top_grid_dims[2] cdef int merged cdef int num_cells cdef QTN_combine *combine cdef np.float64_t bounds[4] cdef np.float64_t dds[2] cdef np.int64_t last_dims[2] cdef int max_level def __cinit__(self, np.ndarray[np.int64_t, ndim=1] top_grid_dims, int nvals, bounds, method = "integrate"): if method == "integrate": self.combine = QTN_add_value elif method == "mip" or \ method == "max": self.combine = QTN_max_value elif method == "min": self.combine = QTN_min_value else: raise NotImplementedError(f"Unknown projection method {self.method!r}") self.merged = 1 self.max_level = 0 cdef int i, j cdef np.int64_t pos[2] cdef np.float64_t *vals = malloc( sizeof(np.float64_t)*nvals) cdef np.float64_t weight_val = 0.0 self.nvals = nvals for i in range(nvals): vals[i] = 0.0 for i in range(4): self.bounds[i] = bounds[i] self.top_grid_dims[0] = top_grid_dims[0] self.top_grid_dims[1] = top_grid_dims[1] self.dds[0] = (self.bounds[1] - self.bounds[0])/self.top_grid_dims[0] self.dds[1] = (self.bounds[3] - self.bounds[2])/self.top_grid_dims[1] self.root_nodes = \ malloc(sizeof(QuadTreeNode **) * top_grid_dims[0]) # We initialize our root values to 0.0. for i in range(top_grid_dims[0]): pos[0] = i self.root_nodes[i] = \ malloc(sizeof(QuadTreeNode *) * top_grid_dims[1]) for j in range(top_grid_dims[1]): pos[1] = j self.root_nodes[i][j] = QTN_initialize( pos, nvals, vals, weight_val) self.num_cells = self.top_grid_dims[0] * self.top_grid_dims[1] free(vals) cdef int count_total_cells(self, QuadTreeNode *root): cdef int total = 0 cdef int i, j if root.children[0][0] == NULL: return 1 for i in range(2): for j in range(2): total += self.count_total_cells(root.children[i][j]) return total + 1 @cython.boundscheck(False) @cython.wraparound(False) cdef int fill_buffer(self, QuadTreeNode *root, int curpos, np.ndarray[np.int32_t, ndim=1] refined, np.ndarray[np.float64_t, ndim=2] values, np.ndarray[np.float64_t, ndim=1] wval): cdef int i, j for i in range(self.nvals): values[curpos, i] = root.val[i] wval[curpos] = root.weight_val if root.children[0][0] != NULL: refined[curpos] = 1 else: return curpos+1 curpos += 1 for i in range(2): for j in range(2): curpos = self.fill_buffer(root.children[i][j], curpos, refined, values, wval) return curpos @cython.boundscheck(False) @cython.wraparound(False) cdef int unfill_buffer(self, QuadTreeNode *root, int curpos, np.ndarray[np.int32_t, ndim=1] refined, np.ndarray[np.float64_t, ndim=2] values, np.ndarray[np.float64_t, ndim=1] wval): cdef int i, j for i in range(self.nvals): root.val[i] = values[curpos, i] root.weight_val = wval[curpos] if refined[curpos] == 0: return curpos+1 curpos += 1 cdef QuadTreeNode *child cdef np.int64_t pos[2] for i in range(2): for j in range(2): pos[0] = root.pos[0]*2 + i pos[1] = root.pos[1]*2 + j child = QTN_initialize(pos, self.nvals, NULL, 0.0) root.children[i][j] = child curpos = self.unfill_buffer(child, curpos, refined, values, wval) return curpos @cython.boundscheck(False) @cython.wraparound(False) def frombuffer(self, np.ndarray[np.int32_t, ndim=1] refined, np.ndarray[np.float64_t, ndim=2] values, np.ndarray[np.float64_t, ndim=1] wval, method): if method == "mip" or method == "max" or method == -1: self.merged = -1 if method == "min" or method == -2: self.merged = -2 elif method == "integrate" or method == 1: self.merged = 1 cdef int curpos = 0 self.num_cells = wval.shape[0] for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): curpos = self.unfill_buffer(self.root_nodes[i][j], curpos, refined, values, wval) @cython.boundscheck(False) @cython.wraparound(False) def tobuffer(self): cdef int total = self.num_cells # We now have four buffers: # Refined or not (total,) int32 # Values in each node (total, nvals) float64 # Weight values in each node (total,) float64 cdef np.ndarray[np.int32_t, ndim=1] refined refined = np.zeros(total, dtype='int32') cdef np.ndarray[np.float64_t, ndim=2] values values = np.zeros((total, self.nvals), dtype='float64') cdef np.ndarray[np.float64_t, ndim=1] wval wval = np.zeros(total, dtype='float64') cdef int curpos = 0 for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): curpos = self.fill_buffer(self.root_nodes[i][j], curpos, refined, values, wval) return (refined, values, wval) def get_args(self): return (self.top_grid_dims[0], self.top_grid_dims[1], self.nvals) cdef int add_to_position(self, int level, np.int64_t pos[2], np.float64_t *val, np.float64_t weight_val, int skip = 0): cdef int i, j, L cdef QuadTreeNode *node node = self.find_on_root_level(pos, level) if node == NULL: return -1 if level > self.max_level: self.max_level = level for L in range(level): if node.children[0][0] == NULL: QTN_refine(node, self.nvals) self.num_cells += 4 # Maybe we should use bitwise operators? i = (pos[0] >> (level - L - 1)) & 1 j = (pos[1] >> (level - L - 1)) & 1 node = node.children[i][j] if skip == 1: return 0 self.combine(node, val, weight_val, self.nvals) return 0 @cython.cdivision(True) cdef QuadTreeNode *find_on_root_level(self, np.int64_t pos[2], int level): # We need this because the root level won't just have four children # So we find on the root level, then we traverse the tree. cdef np.int64_t i, j i = pos[0] >> level j = pos[1] >> level if i >= self.top_grid_dims[0] or i < 0 or \ j >= self.top_grid_dims[1] or j < 0: self.last_dims[0] = i self.last_dims[1] = j return NULL return self.root_nodes[i][j] @cython.boundscheck(False) @cython.wraparound(False) def add_array_to_tree(self, int level, np.ndarray[np.int64_t, ndim=1] pxs, np.ndarray[np.int64_t, ndim=1] pys, np.ndarray[np.float64_t, ndim=2] pvals, np.ndarray[np.float64_t, ndim=1] pweight_vals, int skip = 0): cdef int p cdef np.float64_t *vals cdef np.float64_t *data = pvals.data cdef np.int64_t pos[2] for p in range(pxs.shape[0]): vals = data + self.nvals*p pos[0] = pxs[p] pos[1] = pys[p] self.add_to_position(level, pos, vals, pweight_vals[p], skip) return @cython.boundscheck(False) @cython.wraparound(False) def add_chunk_to_tree(self, np.ndarray[np.int64_t, ndim=1] pxs, np.ndarray[np.int64_t, ndim=1] pys, np.ndarray[np.int64_t, ndim=1] level, np.ndarray[np.float64_t, ndim=2] pvals, np.ndarray[np.float64_t, ndim=1] pweight_vals): cdef int ps = pxs.shape[0] cdef int p, rv cdef np.float64_t *vals cdef np.float64_t *data = pvals.data cdef np.int64_t pos[2] for p in range(ps): vals = data + self.nvals*p pos[0] = pxs[p] pos[1] = pys[p] rv = self.add_to_position(level[p], pos, vals, pweight_vals[p]) if rv == -1: raise YTIntDomainOverflow( (self.last_dims[0], self.last_dims[1]), (self.top_grid_dims[0], self.top_grid_dims[1])) return @cython.boundscheck(False) @cython.wraparound(False) def initialize_chunk(self, np.ndarray[np.int64_t, ndim=1] pxs, np.ndarray[np.int64_t, ndim=1] pys, np.ndarray[np.int64_t, ndim=1] level): cdef int num = pxs.shape[0] cdef int p, rv cdef np.int64_t pos[2] for p in range(num): pos[0] = pxs[p] pos[1] = pys[p] rv = self.add_to_position(level[p], pos, NULL, 0.0, 1) if rv == -1: raise YTIntDomainOverflow( (self.last_dims[0], self.last_dims[1]), (self.top_grid_dims[0], self.top_grid_dims[1])) return @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def get_all(self, int count_only = 0, int method = 1): cdef int i, j, vi cdef int total = 0 self.merged = method for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): total += self.count(self.root_nodes[i][j]) if count_only: return total # Allocate our array cdef np.ndarray[np.int64_t, ndim=1] oix cdef np.ndarray[np.int64_t, ndim=1] oiy cdef np.ndarray[np.int64_t, ndim=1] oires cdef np.ndarray[np.float64_t, ndim=2] nvals cdef np.ndarray[np.float64_t, ndim=1] nwvals oix = np.zeros(total, dtype='int64') oiy = np.zeros(total, dtype='int64') oires = np.zeros(total, dtype='int64') nvals = np.zeros( (total, self.nvals), dtype='float64') nwvals = np.zeros( total, dtype='float64') cdef np.int64_t curpos = 0 cdef np.int64_t *ix = oix.data cdef np.int64_t *iy = oiy.data cdef np.int64_t *ires = oires.data cdef np.float64_t *vdata = nvals.data cdef np.float64_t *wdata = nwvals.data cdef np.float64_t wtoadd cdef np.float64_t *vtoadd = malloc( sizeof(np.float64_t)*self.nvals) for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): for vi in range(self.nvals): vtoadd[vi] = 0.0 wtoadd = 0.0 curpos += self.fill(self.root_nodes[i][j], curpos, ix, iy, ires, vdata, wdata, vtoadd, wtoadd, 0) free(vtoadd) return oix, oiy, oires, nvals, nwvals cdef int count(self, QuadTreeNode *node): cdef int i, j cdef int count = 0 if node.children[0][0] == NULL: return 1 for i in range(2): for j in range(2): count += self.count(node.children[i][j]) return count @cython.cdivision(True) cdef int fill(self, QuadTreeNode *node, np.int64_t curpos, np.int64_t *ix, np.int64_t *iy, np.int64_t *ires, np.float64_t *vdata, np.float64_t *wdata, np.float64_t *vtoadd, np.float64_t wtoadd, np.int64_t level): cdef int i, j, n if node.children[0][0] == NULL: if self.merged == -1: for i in range(self.nvals): vdata[self.nvals * curpos + i] = fmax(node.val[i], vtoadd[i]) elif self.merged == -2: for i in range(self.nvals): vdata[self.nvals * curpos + i] = fmin(node.val[i], vtoadd[i]) wdata[curpos] = 1.0 else: for i in range(self.nvals): vdata[self.nvals * curpos + i] = node.val[i] + vtoadd[i] wdata[curpos] = node.weight_val + wtoadd ires[curpos] = level ix[curpos] = node.pos[0] iy[curpos] = node.pos[1] return 1 cdef np.int64_t added = 0 cdef np.float64_t *vorig vorig = malloc(sizeof(np.float64_t) * self.nvals) if self.merged == 1: for i in range(self.nvals): vorig[i] = vtoadd[i] vtoadd[i] += node.val[i] wtoadd += node.weight_val elif self.merged == -1 or self.merged == -2: for i in range(self.nvals): vtoadd[i] = node.val[i] for i in range(2): for j in range(2): if self.merged == -1: for n in range(self.nvals): vtoadd[n] = node.val[n] added += self.fill(node.children[i][j], curpos + added, ix, iy, ires, vdata, wdata, vtoadd, wtoadd, level + 1) if self.merged == 1: for i in range(self.nvals): vtoadd[i] = vorig[i] wtoadd -= node.weight_val free(vorig) return added @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) def fill_image(self, np.ndarray[np.float64_t, ndim=2] buffer, _bounds, int val_index = 0, int weighted = 0): cdef np.float64_t pos[2] cdef np.float64_t dds[2] cdef int nn[2] cdef int i, j cdef np.float64_t bounds[4] cdef np.float64_t opos[4] cdef np.float64_t weight = 0.0, value = 0.0 cdef np.float64_t *wval = NULL if weighted == 1: wval = &weight for i in range(4): bounds[i] = _bounds[i] for i in range(2): nn[i] = buffer.shape[i] dds[i] = (bounds[i*2 + 1] - bounds[i*2])/nn[i] pos[0] = bounds[0] opos[0] = opos[1] = pos[0] + dds[0] for i in range(nn[0]): pos[1] = bounds[2] opos[2] = opos[3] = pos[1] + dds[1] for j in range(nn[1]): # We start at level zero. In the future we could optimize by # retaining oct information from previous cells. if not ((opos[0] <= pos[0] <= opos[1]) and (opos[2] <= pos[1] <= opos[3])): value = self.find_value_at_pos(pos, val_index, opos, wval) buffer[i,j] = value if weighted == 1: buffer[i,j] /= weight pos[1] += dds[1] pos[0] += dds[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef np.float64_t find_value_at_pos(self, np.float64_t pos[2], int val_index, np.float64_t opos[4], np.float64_t *wval = NULL): cdef int i cdef np.int64_t ind[2] cdef np.float64_t cp[2] cdef np.float64_t dds[2] cdef np.float64_t value = 0.0 cdef np.float64_t weight = 0.0 cdef QuadTreeNode *cur for i in range(2): ind[i] = (pos[i]/self.dds[i]) cp[i] = (ind[i] + 0.5) * self.dds[i] dds[i] = self.dds[i] cur = self.root_nodes[ind[0]][ind[1]] value += cur.val[val_index] weight += cur.weight_val while cur.children[0][0] != NULL: for i in range(2): # Note that below offset by half a dx for center, after # updating to the next level dds[i] = dds[i] * 0.5 if cp[i] >= pos[i]: ind[i] = 0 cp[i] -= dds[i] * 0.5 else: ind[i] = 1 cp[i] += dds[i] * 0.5 cur = cur.children[ind[0]][ind[1]] value += cur.val[val_index] weight += cur.weight_val opos[0] = cp[0] - dds[0] * 0.5 opos[1] = cp[0] + dds[0] * 0.5 opos[2] = cp[1] - dds[1] * 0.5 opos[3] = cp[1] + dds[1] * 0.5 if wval != NULL: wval[0] = weight return value def __dealloc__(self): cdef int i, j for i in range(self.top_grid_dims[0]): for j in range(self.top_grid_dims[1]): QTN_free(self.root_nodes[i][j]) free(self.root_nodes[i]) free(self.root_nodes) cdef void QTN_merge_nodes(QuadTreeNode *n1, QuadTreeNode *n2, int nvals, QTN_combine *func): # We have four choices when merging nodes. # 1. If both nodes have no refinement, then we add values of n2 to n1. # 2. If both have refinement, we call QTN_merge_nodes on all four children. # 3. If n2 has refinement and n1 does not, we detach n2's children and # attach them to n1. # 4. If n1 has refinement and n2 does not, we add the value of n2 to n1. cdef int i, j func(n1, n2.val, n2.weight_val, nvals) if n1.children[0][0] == n2.children[0][0] == NULL: pass elif n1.children[0][0] != NULL and n2.children[0][0] != NULL: for i in range(2): for j in range(2): QTN_merge_nodes(n1.children[i][j], n2.children[i][j], nvals, func) elif n1.children[0][0] == NULL and n2.children[0][0] != NULL: for i in range(2): for j in range(2): n1.children[i][j] = n2.children[i][j] n2.children[i][j] = NULL elif n1.children[0][0] != NULL and n2.children[0][0] == NULL: pass else: raise RuntimeError def merge_quadtrees(QuadTree qt1, QuadTree qt2, method = 1): cdef int i, j qt1.num_cells = 0 cdef QTN_combine *func if method == 1: qt1.merged = 1 func = QTN_add_value elif method == -1: qt1.merged = -1 func = QTN_max_value elif method == -2: qt1.merged = -2 func = QTN_min_value else: raise NotImplementedError if qt1.merged != 0 or qt2.merged != 0: assert(qt1.merged == qt2.merged) for i in range(qt1.top_grid_dims[0]): for j in range(qt1.top_grid_dims[1]): QTN_merge_nodes(qt1.root_nodes[i][j], qt2.root_nodes[i][j], qt1.nvals, func) qt1.num_cells += qt1.count_total_cells( qt1.root_nodes[i][j]) yt-project-yt-f043ac8/yt/utilities/lib/ragged_arrays.pyx000066400000000000000000000044071510711153200234500ustar00rootroot00000000000000""" Some simple operations for operating on ragged arrays """ import numpy as np cimport cython cimport numpy as np cdef fused numpy_dt: np.float32_t np.float64_t np.int32_t np.int64_t cdef numpy_dt r_min(numpy_dt a, numpy_dt b): if a < b: return a return b cdef numpy_dt r_max(numpy_dt a, numpy_dt b): if a > b: return a return b cdef numpy_dt r_add(numpy_dt a, numpy_dt b): return a + b cdef numpy_dt r_subtract(numpy_dt a, numpy_dt b): return a - b cdef numpy_dt r_multiply(numpy_dt a, numpy_dt b): return a * b @cython.cdivision(True) cdef numpy_dt r_divide(numpy_dt a, numpy_dt b): return a / b def index_unop(np.ndarray[numpy_dt, ndim=1] values, np.ndarray[np.int64_t, ndim=1] indices, np.ndarray[np.int64_t, ndim=1] sizes, operation): cdef numpy_dt mi, ma if numpy_dt == np.float32_t: dt = "float32" mi = np.finfo(dt).min ma = np.finfo(dt).max elif numpy_dt == np.float64_t: dt = "float64" mi = np.finfo(dt).min ma = np.finfo(dt).max elif numpy_dt == np.int32_t: dt = "int32" mi = np.iinfo(dt).min ma = np.iinfo(dt).max elif numpy_dt == np.int64_t: dt = "int64" mi = np.iinfo(dt).min ma = np.iinfo(dt).max cdef np.ndarray[numpy_dt] out_values = np.zeros(sizes.size, dtype=dt) cdef numpy_dt (*func)(numpy_dt a, numpy_dt b) # Now we figure out our function. At present, we only allow addition and # multiplication, because they are commutative and easy to bootstrap. cdef numpy_dt ival, val if operation == "sum": ival = 0 func = r_add elif operation == "prod": ival = 1 func = r_multiply elif operation == "max": ival = mi func = r_max elif operation == "min": ival = ma func = r_min else: raise NotImplementedError cdef np.int64_t i, ind_ind, ind_arr ind_ind = 0 for i in range(sizes.size): # Each entry in sizes is the size of the array val = ival for _ in range(sizes[i]): ind_arr = indices[ind_ind] val = func(val, values[ind_arr]) ind_ind += 1 out_values[i] = val return out_values yt-project-yt-f043ac8/yt/utilities/lib/tests/000077500000000000000000000000001510711153200212315ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/tests/__init__.py000066400000000000000000000000001510711153200233300ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/lib/tests/test_allocation_container.py000066400000000000000000000012101510711153200270230ustar00rootroot00000000000000from numpy.testing import assert_array_equal, assert_equal from yt.utilities.lib.allocation_container import BitmaskPool def test_bitmask_pool(): bmp = BitmaskPool() assert_equal(len(bmp), 0) bmp.append(100) assert_equal(len(bmp), 1) assert_equal(bmp[0].size, 100) bmp.append(200) assert_equal(len(bmp), 2) assert_equal(bmp[0].size, 100) assert_equal(bmp[1].size, 200) assert_equal(sum(_.size for _ in bmp.to_arrays()), 300) arrs = bmp.to_arrays() assert_equal(arrs[0].size, 100) assert_equal(arrs[1].size, 200) arrs[0][:] = 1 arrs = bmp.to_arrays() assert_array_equal(arrs[0], 1) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_alt_ray_tracers.py000066400000000000000000000065621510711153200260310ustar00rootroot00000000000000"""Tests for non-cartesian ray tracers.""" import numpy as np from numpy.testing import assert_equal from yt.testing import amrspace from yt.utilities.lib.alt_ray_tracers import _cyl2cart, cylindrical_ray_trace left_grid = right_grid = amr_levels = center_grid = data = None old_settings = None def setup_module(): # set up some sample cylindrical grid data, radiating out from center global left_grid, right_grid, amr_levels, center_grid, data, old_settings old_settings = np.geterr() np.seterr(all="ignore") l1, r1, lvl1 = amrspace([0.0, 1.0, 0.0, -1.0, 0.0, 2 * np.pi], levels=(7, 7, 0)) l2, r2, lvl2 = amrspace([0.0, 1.0, 0.0, 1.0, 0.0, 2 * np.pi], levels=(7, 7, 0)) left_grid = np.concatenate([l1, l2], axis=0) right_grid = np.concatenate([r1, r2], axis=0) amr_levels = np.concatenate([lvl1, lvl2], axis=0) center_grid = (left_grid + right_grid) / 2.0 data = np.cos(np.sqrt(np.sum(center_grid[:, :2] ** 2, axis=1))) ** 2 # cos^2 def teardown_module(): np.seterr(**old_settings) point_pairs = np.array( [ # p1 p2 ([0.5, -1.0, 0.0], [1.0, 1.0, 0.75 * np.pi]), # Everything different ([0.5, -1.0, 0.0], [0.5, 1.0, 0.75 * np.pi]), # r same ([0.5, -1.0, 0.0], [0.5, 1.0, np.pi]), # diagonal through z-axis # straight through z-axis ([0.5, 0.0, 0.0], [0.5, 0.0, np.pi]), # ([0.5, 0.0, np.pi*3/2 + 0.0], [0.5, 0.0, np.pi*3/2 + np.pi]), # ([0.5, 0.0, np.pi/2 + 0.0], [0.5, 0.0, np.pi/2 + np.pi]), # ([0.5, 0.0, np.pi + 0.0], [0.5, 0.0, np.pi + np.pi]), # const z, not through z-axis ([0.5, 0.1, 0.0], [0.5, 0.1, 0.75 * np.pi]), # ([0.5, 0.1, np.pi + 0.0], [0.5, 0.1, np.pi + 0.75*np.pi]), # ([0.5, 0.1, np.pi*3/2 + 0.0], [0.5, 0.1, np.pi*3/2 + 0.75*np.pi]), # ([0.5, 0.1, np.pi/2 + 0.0], [0.5, 0.1, np.pi/2 + 0.75*np.pi]), # ([0.5, 0.1, 2*np.pi + 0.0], [0.5, 0.1, 2*np.pi + 0.75*np.pi]), # ([0.5, 0.1, np.pi/4 + 0.0], [0.5, 0.1, np.pi/4 + 0.75*np.pi]), # ([0.5, 0.1, np.pi*3/8 + 0.0], [0.5, 0.1, np.pi*3/8 + 0.75*np.pi]), ( [0.5, -1.0, 0.75 * np.pi], [1.0, 1.0, 0.75 * np.pi], ), # r,z different - theta same ([0.5, -1.0, 0.75 * np.pi], [0.5, 1.0, 0.75 * np.pi]), # z-axis parallel ([0.0, -1.0, 0.0], [0.0, 1.0, 0.0]), # z-axis itself ] ) def check_monotonic_inc(arr): assert np.all(0.0 <= (arr[1:] - arr[:-1])) def check_bounds(arr, blower, bupper): assert np.all(blower <= arr) assert np.all(bupper >= arr) def test_cylindrical_ray_trace(): for pair in point_pairs: p1, p2 = pair p1cart, p2cart = _cyl2cart(pair) pathlen = np.sqrt(np.sum((p2cart - p1cart) ** 2)) t, s, rztheta, inds = cylindrical_ray_trace(p1, p2, left_grid, right_grid) npoints = len(t) check_monotonic_inc(t) assert 0.0 <= t[0] assert t[-1] <= 1.0 check_monotonic_inc(s) assert 0.0 <= s[0] assert s[-1] <= pathlen assert_equal(npoints, len(s)) assert_equal((npoints, 3), rztheta.shape) check_bounds(rztheta[:, 0], 0.0, 1.0) check_bounds(rztheta[:, 1], -1.0, 1.0) check_bounds(rztheta[:, 2], 0.0, 2 * np.pi) check_monotonic_inc(rztheta[:, 2]) assert_equal(npoints, len(inds)) check_bounds(inds, 0, len(left_grid) - 1) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_bitarray.py000066400000000000000000000131671510711153200244670ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal, assert_equal import yt.utilities.lib.bitarray as ba def test_inout_bitarray(): # Check that we can do it for bitarrays that are funny-shaped rng = np.random.default_rng() for i in range(7): # Check we can feed in an array arr_in = rng.random(32**3 + i) > 0.5 b = ba.bitarray(arr=arr_in) if i > 0: assert_equal(b.ibuf.size, (32**3) / 8.0 + 1) arr_out = b.as_bool_array() assert_equal(arr_in, arr_out) # Let's check we can do it without feeding it at first b = ba.bitarray(size=arr_in.size) b.set_from_array(arr_in) arr_out = b.as_bool_array() assert_equal(arr_in, arr_out) # Try a big array arr_in = rng.random(32**3 + i) > 0.5 b = ba.bitarray(arr=arr_in) arr_out = b.as_bool_array() assert_equal(arr_in, arr_out) assert_equal(b.count(), arr_in.sum()) # Let's check we can do something interesting. arr_in1 = rng.random(32**3) > 0.5 arr_in2 = rng.random(32**3) > 0.5 b1 = ba.bitarray(arr=arr_in1) b2 = ba.bitarray(arr=arr_in2) b3 = ba.bitarray(arr=(arr_in1 & arr_in2)) assert_equal((b1.ibuf & b2.ibuf), b3.ibuf) assert_equal(b1.count(), arr_in1.sum()) assert_equal(b2.count(), arr_in2.sum()) # Let's check the logical and operation b4 = b1.logical_and(b2) assert_equal(b4.count(), b3.count()) assert_array_equal(b4.as_bool_array(), b3.as_bool_array()) b5 = b1 & b2 assert_equal(b5.count(), b3.count()) assert_array_equal(b5.as_bool_array(), b3.as_bool_array()) b1 &= b2 assert_equal(b1.count(), b4.count()) assert_array_equal(b1.as_bool_array(), b4.as_bool_array()) # Repeat this, but with the logical or operators b1 = ba.bitarray(arr=arr_in1) b2 = ba.bitarray(arr=arr_in2) b3 = ba.bitarray(arr=(arr_in1 | arr_in2)) assert_equal((b1.ibuf | b2.ibuf), b3.ibuf) assert_equal(b1.count(), arr_in1.sum()) assert_equal(b2.count(), arr_in2.sum()) # Let's check the logical and operation b4 = b1.logical_or(b2) assert_equal(b4.count(), b3.count()) assert_array_equal(b4.as_bool_array(), b3.as_bool_array()) b5 = b1 | b2 assert_equal(b5.count(), b3.count()) assert_array_equal(b5.as_bool_array(), b3.as_bool_array()) b1 |= b2 assert_equal(b1.count(), b4.count()) assert_array_equal(b1.as_bool_array(), b4.as_bool_array()) # Repeat this, but with the logical xor operators b1 = ba.bitarray(arr=arr_in1) b2 = ba.bitarray(arr=arr_in2) b3 = ba.bitarray(arr=(arr_in1 ^ arr_in2)) assert_equal((b1.ibuf ^ b2.ibuf), b3.ibuf) assert_equal(b1.count(), arr_in1.sum()) assert_equal(b2.count(), arr_in2.sum()) # Let's check the logical and operation b4 = b1.logical_xor(b2) assert_equal(b4.count(), b3.count()) assert_array_equal(b4.as_bool_array(), b3.as_bool_array()) b5 = b1 ^ b2 assert_equal(b5.count(), b3.count()) assert_array_equal(b5.as_bool_array(), b3.as_bool_array()) b1 ^= b2 assert_equal(b1.count(), b4.count()) assert_array_equal(b1.as_bool_array(), b4.as_bool_array()) b = ba.bitarray(10) for i in range(10): b.set_value(i, 2) # 2 should evaluate to True arr = b.as_bool_array() assert_equal(arr[: i + 1].all(), True) assert_equal(arr[i + 1 :].any(), False) for i in range(10): b.set_value(i, 0) arr = b.as_bool_array() assert_equal(arr.any(), False) b.set_value(7, 1) arr = b.as_bool_array() assert_array_equal(arr, [0, 0, 0, 0, 0, 0, 0, 1, 0, 0]) b.set_value(2, 1) arr = b.as_bool_array() assert_array_equal(arr, [0, 0, 1, 0, 0, 0, 0, 1, 0, 0]) def test_set_range(): b = ba.bitarray(127) # Test once where we're in the middle of start and end bits b.set_range(4, 65, 1) comparison_array = np.zeros(127, dtype="uint8") comparison_array[4:65] = 1 arr = b.as_bool_array().astype("uint8") assert_array_equal(arr, comparison_array) assert_equal(b.count(), comparison_array.sum()) # Test when we start and stop in the same byte b = ba.bitarray(127) b.set_range(4, 6, 1) comparison_array = np.zeros(127, dtype="uint8") comparison_array[4:6] = 1 arr = b.as_bool_array().astype("uint8") assert_array_equal(arr, comparison_array) assert_equal(b.count(), comparison_array.sum()) # Test now where we're in the middle of start b = ba.bitarray(64) b.set_range(33, 36, 1) comparison_array = np.zeros(64, dtype="uint8") comparison_array[33:36] = 1 arr = b.as_bool_array().astype("uint8") assert_array_equal(arr, comparison_array) assert_equal(b.count(), comparison_array.sum()) # Now we test when we end on a byte edge, but we have 65 entries b = ba.bitarray(65) b.set_range(32, 64, 1) comparison_array = np.zeros(65, dtype="uint8") comparison_array[32:64] = 1 arr = b.as_bool_array().astype("uint8") assert_array_equal(arr, comparison_array) assert_equal(b.count(), comparison_array.sum()) # Let's do the inverse b = ba.bitarray(127) b.set_range(0, 127, 1) assert_equal(b.as_bool_array().all(), True) b.set_range(0, 127, 0) assert_equal(b.as_bool_array().any(), False) b.set_range(3, 9, 1) comparison_array = np.zeros(127, dtype="uint8") comparison_array[3:9] = 1 arr = b.as_bool_array().astype("uint8") assert_array_equal(arr, comparison_array) assert_equal(b.count(), comparison_array.sum()) # Now let's overlay some zeros b.set_range(7, 10, 0) comparison_array[7:10] = 0 arr = b.as_bool_array().astype("uint8") assert_array_equal(arr, comparison_array) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_bounding_volume_hierarchy.py000066400000000000000000000026301510711153200300750ustar00rootroot00000000000000import numpy as np import yt from yt.testing import requires_file from yt.utilities.lib.bounding_volume_hierarchy import BVH, test_ray_trace from yt.visualization.volume_rendering.api import Camera, Scene def get_rays(camera): normal_vector = camera.unit_vectors[2].d W = np.array([8.0, 8.0]) N = np.array([800, 800]) dx = W / N x_points = np.linspace((-N[0] / 2 + 0.5) * dx[0], (N[0] / 2 - 0.5) * dx[0], N[0]) y_points = np.linspace((-N[1] / 2 + 0.5) * dx[1], (N[1] / 2 - 0.5) * dx[0], N[1]) X, Y = np.meshgrid(x_points, y_points) result = np.dot(camera.unit_vectors[0:2].T, [X.ravel(), Y.ravel()]) vec_origins = camera.scene.arr(result.T, "unitary") + camera.position return np.array(vec_origins), np.array(normal_vector) fn = "MOOSE_sample_data/out.e-s010" @requires_file(fn) def test_bounding_volume_hierarchy(): ds = yt.load(fn) vertices = ds.index.meshes[0].connectivity_coords indices = ds.index.meshes[0].connectivity_indices - 1 ad = ds.all_data() field_data = ad["connect1", "diffused"] bvh = BVH(vertices, indices, field_data) sc = Scene() cam = Camera(sc) cam.set_position(np.array([8.0, 8.0, 8.0])) cam.focus = np.array([0.0, 0.0, 0.0]) origins, direction = get_rays(cam) image = np.empty(800 * 800, np.float64) test_ray_trace(image, origins, direction, bvh) image = image.reshape((800, 800)) return image yt-project-yt-f043ac8/yt/utilities/lib/tests/test_element_mappings.py000066400000000000000000000134251510711153200261760ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal from yt.utilities.lib.element_mappings import ( test_hex20_sampler, test_hex_sampler, test_linear1D_sampler, test_quad2_sampler, test_quad_sampler, test_tet2_sampler, test_tetra_sampler, test_tri2_sampler, test_tri_sampler, test_wedge_sampler, ) def check_all_vertices(sampler, vertices, field_values): NV = vertices.shape[0] NDIM = vertices.shape[1] x = np.empty(NDIM) for i in range(NV): x = vertices[i] val = sampler(vertices, field_values, x) assert_almost_equal(val, field_values[i]) def test_P1Sampler1D(): vertices = np.array([[0.1], [0.3]]) field_values = np.array([1.0, 2.0]) check_all_vertices(test_linear1D_sampler, vertices, field_values) def test_P1Sampler2D(): vertices = np.array([[0.1, 0.2], [0.6, 0.3], [0.2, 0.7]]) field_values = np.array([1.0, 2.0, 3.0]) check_all_vertices(test_tri_sampler, vertices, field_values) def test_P1Sampler3D(): vertices = np.array( [[0.1, 0.1, 0.1], [0.6, 0.3, 0.2], [0.2, 0.7, 0.2], [0.4, 0.4, 0.7]] ) field_values = np.array([1.0, 2.0, 3.0, 4.0]) check_all_vertices(test_tetra_sampler, vertices, field_values) def test_Q1Sampler2D(): vertices = np.array([[0.1, 0.2], [0.6, 0.3], [0.7, 0.9], [0.2, 0.7]]) field_values = np.array([1.0, 2.0, 3.0, 4.0]) check_all_vertices(test_quad_sampler, vertices, field_values) def test_Q2Sampler2D(): vertices = np.array( [ [2.0, 3.0], [7.0, 4.0], [10.0, 15.0], [4.0, 12.0], [4.5, 3.5], [8.5, 9.5], [7.0, 13.5], [3.0, 7.5], [5.75, 8.5], ] ) field_values = np.array([7.0, 27.0, 40.0, 12.0, 13.0, 30.0, 22.0, 9.0, 16.0]) check_all_vertices(test_quad2_sampler, vertices, field_values) def test_Q1Sampler3D(): vertices = np.array( [ [2.00657905, 0.6888599, 1.4375], [1.8658198, 1.00973171, 1.4375], [1.97881594, 1.07088163, 1.4375], [2.12808879, 0.73057381, 1.4375], [2.00657905, 0.6888599, 1.2], [1.8658198, 1.00973171, 1.2], [1.97881594, 1.07088163, 1.2], [2.12808879, 0.73057381, 1.2], ] ) field_values = np.array( [ 0.4526278, 0.45262656, 0.45262657, 0.4526278, 0.54464296, 0.54464149, 0.5446415, 0.54464296, ] ) check_all_vertices(test_hex_sampler, vertices, field_values) def test_S2Sampler3D(): vertices = np.array( [ [3.00608789e-03, 4.64941000e-02, -3.95758979e-04], [3.03202730e-03, 4.64941000e-02, 0.00000000e00], [3.03202730e-03, 4.70402000e-02, 3.34389809e-20], [3.00608789e-03, 4.70402000e-02, -3.95758979e-04], [2.45511948e-03, 4.64941000e-02, -3.23222611e-04], [2.47630461e-03, 4.64941000e-02, 1.20370622e-35], [2.47630461e-03, 4.70402000e-02, 3.34389809e-20], [2.45511948e-03, 4.70402000e-02, -3.23222611e-04], [3.01905760e-03, 4.64941000e-02, -1.97879489e-04], [3.03202730e-03, 4.67671500e-02, 3.34389809e-20], [3.01905760e-03, 4.70402000e-02, -1.97879489e-04], [3.00608789e-03, 4.67671500e-02, -3.95758979e-04], [2.73060368e-03, 4.64941000e-02, -3.59490795e-04], [2.75416596e-03, 4.64941000e-02, -1.86574463e-34], [2.75416596e-03, 4.70402000e-02, 6.68779617e-20], [2.73060368e-03, 4.70402000e-02, -3.59490795e-04], [2.47100265e-03, 4.64941000e-02, -1.61958070e-04], [2.47630461e-03, 4.67671500e-02, 1.67194904e-20], [2.47100265e-03, 4.70402000e-02, -1.61958070e-04], [2.45511948e-03, 4.67671500e-02, -3.23222611e-04], ] ) field_values = np.array( [ 659.80151432, 650.95995348, 650.02809796, 658.81589888, 659.77560908, 650.93582507, 649.99987015, 658.78508795, 655.38073390, 650.49402572, 654.42199842, 659.30870660, 659.78856170, 650.94788928, 650.01398406, 658.80049342, 655.35571708, 650.46784761, 654.39247905, 659.28034852, ] ) check_all_vertices(test_hex20_sampler, vertices, field_values) def test_W1Sampler3D(): vertices = np.array( [ [-0.34641016, 0.3, 0.0], [-0.31754265, 0.25, 0.0], [-0.28867513, 0.3, 0.0], [-0.34641016, 0.3, 0.05], [-0.31754265, 0.25, 0.05], [-0.28867513, 0.3, 0.05], ] ) field_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]) check_all_vertices(test_wedge_sampler, vertices, field_values) def test_T2Sampler2D(): vertices = np.array( [[0.1, 0.2], [0.3, 0.5], [0.2, 0.9], [0.2, 0.35], [0.25, 0.7], [0.15, 0.55]] ) field_values = np.array([15.0, 37.0, 49.0, 32.0, 46.0, 24.0]) check_all_vertices(test_tri2_sampler, vertices, field_values) def test_Tet2Sampler3D(): vertices = np.array( [ [0.3, -0.4, 0.6], [1.7, -0.7, 0.8], [0.4, 1.2, 0.4], [0.4, -0.2, 2.0], [1.0, -0.55, 0.7], [1.05, 0.25, 0.6], [0.35, 0.4, 0.5], [0.35, -0.3, 1.3], [1.05, -0.45, 1.4], [0.4, 0.5, 1.2], ] ) field_values = np.array( [15.0, 37.0, 49.0, 24.0, 30.0, 44.0, 20.0, 17.0, 32.0, 36.0] ) check_all_vertices(test_tet2_sampler, vertices, field_values) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_fill_region.py000066400000000000000000000025401510711153200251340ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.utilities.lib.misc_utilities import fill_region NDIM = 32 def test_fill_region(): for level in range(2): rf = 2**level output_fields = [ np.zeros((NDIM * rf, NDIM * rf, NDIM * rf), "float64") for i in range(3) ] input_fields = [np.empty(NDIM**3, "float64") for i in range(3)] v = np.mgrid[ 0.0 : 1.0 : NDIM * 1j, 0.0 : 1.0 : NDIM * 1j, 0.0 : 1.0 : NDIM * 1j ] input_fields[0][:] = v[0].ravel() input_fields[1][:] = v[1].ravel() input_fields[2][:] = v[2].ravel() left_index = np.zeros(3, "int64") ipos = np.empty((NDIM**3, 3), dtype="int64") ind = np.indices((NDIM, NDIM, NDIM)) ipos[:, 0] = ind[0].ravel() ipos[:, 1] = ind[1].ravel() ipos[:, 2] = ind[2].ravel() ires = np.zeros(NDIM * NDIM * NDIM, "int64") ddims = np.array([NDIM, NDIM, NDIM], dtype="int64") * rf fill_region( input_fields, output_fields, level, left_index, ipos, ires, ddims, np.array([2, 2, 2], dtype="i8"), ) for r in range(level + 1): for o, i in zip(output_fields, v, strict=True): assert_equal(o[r::rf, r::rf, r::rf], i) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_geometry_utils.py000066400000000000000000000713151510711153200257240ustar00rootroot00000000000000import numpy as np from numpy.testing import ( assert_array_equal, assert_array_less, assert_equal, assert_raises, ) from yt.testing import fake_random_ds from yt.utilities.lib.misc_utilities import ( obtain_position_vector, obtain_relative_velocity_vector, ) _fields = ("density", "velocity_x", "velocity_y", "velocity_z") _units = ("g/cm**3", "cm/s", "cm/s", "cm/s") # TODO: error compact/spread bits for incorrect size # TODO: test msdb for [0,0], [1,1], [2,2] etc. def test_spread_bits(): from yt.utilities.lib.geometry_utils import spread_bits li = [ ( np.uint64(0b111111111111111111111), np.uint64(0b1001001001001001001001001001001001001001001001001001001001001), ) ] for i, ans in li: out = spread_bits(i) assert_equal(out, ans) def test_compact_bits(): from yt.utilities.lib.geometry_utils import compact_bits li = [ ( np.uint64(0b111111111111111111111), np.uint64(0b1001001001001001001001001001001001001001001001001001001001001), ) ] for ans, i in li: out = compact_bits(i) assert_equal(out, ans) def test_spread_and_compact_bits(): from yt.utilities.lib.geometry_utils import compact_bits, spread_bits li = [np.uint64(0b111111111111111111111)] for ans in li: mi = spread_bits(ans) out = compact_bits(mi) assert_equal(out, ans) def test_lsz(): from yt.utilities.lib.geometry_utils import lsz li = [ ( np.uint64(0b1001001001001001001001001001001001001001001001001001001001001), 3 * 21, 3, 0, ), ( np.uint64(0b1001001001001001001001001001001001001001001001001001001001000), 3 * 0, 3, 0, ), ( np.uint64(0b1001001001001001001001001001001001001001001001001001001000001), 3 * 1, 3, 0, ), ( np.uint64(0b1001001001001001001001001001001001001001001001001001000001001), 3 * 2, 3, 0, ), ( np.uint64(0b10010010010010010010010010010010010010010010010010010010010010), 3 * 0, 3, 0, ), ( np.uint64( 0b100100100100100100100100100100100100100100100100100100100100100 ), 3 * 0, 3, 0, ), (np.uint64(0b100), 0, 1, 0), (np.uint64(0b100), 1, 1, 1), (np.uint64(0b100), 3, 1, 2), (np.uint64(0b100), 3, 1, 3), ] for i, ans, stride, start in li: out = lsz(i, stride=stride, start=start) assert_equal(out, ans) def test_lsb(): from yt.utilities.lib.geometry_utils import lsb li = [ ( np.uint64(0b1001001001001001001001001001001001001001001001001001001001001), 3 * 0, ), ( np.uint64(0b1001001001001001001001001001001001001001001001001001001001000), 3 * 1, ), ( np.uint64(0b1001001001001001001001001001001001001001001001001001001000000), 3 * 2, ), ( np.uint64(0b1001001001001001001001001001001001001001001001001001000000000), 3 * 3, ), ( np.uint64(0b10010010010010010010010010010010010010010010010010010010010010), 3 * 21, ), ( np.uint64( 0b100100100100100100100100100100100100100100100100100100100100100 ), 3 * 21, ), ] for i, ans in li: out = lsb(i, stride=3) assert_equal(out, ans) def test_bitwise_addition(): from yt.utilities.lib.geometry_utils import bitwise_addition # TODO: Handle negative & periodic boundaries lz = [ (0, 1), # (0,-1), (1, 1), (1, 2), (1, 4), (1, -1), (2, 1), (2, 2), (2, -1), (2, -2), (3, 1), (3, 5), (3, -1), ] for i, a in lz: i = np.uint64(i) a = np.int64(a) out = bitwise_addition(i, a, stride=1, start=0) assert_equal(out, i + a) # def test_add_to_morton_coord(): # from yt.utilities.lib.geometry_utils import add_to_morton_coord def test_get_morton_indices(): from yt.utilities.lib.geometry_utils import ( get_morton_indices, get_morton_indices_unravel, ) INDEX_MAX_64 = np.uint64(2097151) li = np.arange(6, dtype=np.uint64).reshape((2, 3)) mi_ans = np.array([10, 229], dtype=np.uint64) mi_out = get_morton_indices(li) mi_out2 = get_morton_indices_unravel(li[:, 0], li[:, 1], li[:, 2]) assert_array_equal(mi_out, mi_ans) assert_array_equal(mi_out2, mi_ans) li[0, :] = INDEX_MAX_64 * np.ones(3, dtype=np.uint64) assert_raises(ValueError, get_morton_indices, li) assert_raises(ValueError, get_morton_indices_unravel, li[:, 0], li[:, 1], li[:, 2]) def test_get_morton_points(): from yt.utilities.lib.geometry_utils import get_morton_points mi = np.array([10, 229], dtype=np.uint64) li_ans = np.arange(6, dtype=np.uint64).reshape((2, 3)) li_out = get_morton_points(mi) assert_array_equal(li_out, li_ans) def test_compare_morton(): # TODO: Add error messages to assertions from yt.utilities.lib.geometry_utils import compare_morton # Diagonal p = np.array([0.0, 0.0, 0.0], dtype=np.float64) q = np.array([1.0, 1.0, 1.0], dtype=np.float64) assert_equal(compare_morton(p, q), 1) assert_equal(compare_morton(q, p), 0) assert_equal(compare_morton(p, p), 0) # 1-1 vs 0-1 p = np.array([1.0, 1.0, 0.0], dtype=np.float64) q = np.array([1.0, 1.0, 1.0], dtype=np.float64) assert_equal(compare_morton(p, q), 1) assert_equal(compare_morton(q, p), 0) assert_equal(compare_morton(p, p), 0) # x advance, y decrease p = np.array([0.0, 1.0, 0.0], dtype=np.float64) q = np.array([1.0, 0.0, 0.0], dtype=np.float64) assert_equal(compare_morton(p, q), 1) assert_equal(compare_morton(q, p), 0) assert_equal(compare_morton(p, p), 0) # x&y advance, z decrease p = np.array([0.0, 0.0, 1.0], dtype=np.float64) q = np.array([1.0, 1.0, 0.0], dtype=np.float64) assert_equal(compare_morton(p, q), 1) assert_equal(compare_morton(q, p), 0) assert_equal(compare_morton(p, p), 0) def test_get_morton_neighbors_coarse(): from yt.utilities.lib.geometry_utils import get_morton_neighbors_coarse imax = 5 ngz = 1 tests = { (7, 1): np.array( [ 35, 49, 56, 48, 33, 40, 32, 42, 34, 3, 17, 24, 16, 1, 8, 0, 10, 2, 21, 28, 20, 5, 12, 4, 14, 6, ], dtype="uint64", ), (7, 0): np.array( [ 35, 49, 56, 48, 33, 40, 32, 42, 34, 3, 17, 24, 16, 1, 8, 0, 10, 2, 21, 28, 20, 5, 12, 4, 14, 6, ], dtype="uint64", ), (0, 1): np.array( [ 4, 6, 7, 70, 132, 133, 196, 5, 68, 256, 258, 259, 322, 384, 385, 448, 257, 320, 2, 3, 66, 128, 129, 192, 1, 64, ], dtype="uint64", ), (0, 0): np.array([4, 6, 7, 5, 2, 3, 1], dtype="uint64"), (448, 1): np.array( [ 192, 64, 0, 9, 82, 18, 27, 128, 137, 228, 100, 36, 45, 118, 54, 63, 164, 173, 320, 256, 265, 338, 274, 283, 384, 393, ], dtype="uint64", ), (448, 0): np.array([228, 118, 63, 173, 338, 283, 393], dtype="uint64"), } for (mi1, periodic), ans in tests.items(): n1 = get_morton_neighbors_coarse(mi1, imax, periodic, ngz) assert_equal(np.sort(n1), np.sort(ans)) def test_get_morton_neighbors_refined(): from yt.utilities.lib.geometry_utils import get_morton_neighbors_refined imax1 = 5 imax2 = 5 ngz = 1 tests = { (7, 7, 1): ( np.array( [ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, ], dtype="uint64", ), np.array( [ 35, 49, 56, 48, 33, 40, 32, 42, 34, 3, 17, 24, 16, 1, 8, 0, 10, 2, 21, 28, 20, 5, 12, 4, 14, 6, ], dtype="uint64", ), ), (7, 7, 0): ( np.array( [ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, ], dtype="uint64", ), np.array( [ 35, 49, 56, 48, 33, 40, 32, 42, 34, 3, 17, 24, 16, 1, 8, 0, 10, 2, 21, 28, 20, 5, 12, 4, 14, 6, ], dtype="uint64", ), ), (0, 0, 1): ( np.array( [ 0, 0, 0, 64, 128, 128, 192, 0, 64, 256, 256, 256, 320, 384, 384, 448, 256, 320, 0, 0, 64, 128, 128, 192, 0, 64, ], dtype="uint64", ), np.array( [ 4, 6, 7, 70, 132, 133, 196, 5, 68, 256, 258, 259, 322, 384, 385, 448, 257, 320, 2, 3, 66, 128, 129, 192, 1, 64, ], dtype="uint64", ), ), (0, 0, 0): ( np.array([0, 0, 0, 0, 0, 0, 0], dtype="uint64"), np.array([4, 6, 7, 5, 2, 3, 1], dtype="uint64"), ), (448, 448, 1): ( np.array( [ 192, 64, 0, 64, 192, 128, 192, 128, 192, 448, 320, 256, 320, 448, 384, 448, 384, 448, 320, 256, 320, 448, 384, 448, 384, 448, ], dtype="uint64", ), np.array( [ 192, 64, 0, 9, 82, 18, 27, 128, 137, 228, 100, 36, 45, 118, 54, 63, 164, 173, 320, 256, 265, 338, 274, 283, 384, 393, ], dtype="uint64", ), ), (448, 448, 0): ( np.array([448, 448, 448, 448, 448, 448, 448], dtype="uint64"), np.array([228, 118, 63, 173, 338, 283, 393], dtype="uint64"), ), } for (mi1, mi2, periodic), (ans1, ans2) in tests.items(): n1, n2 = get_morton_neighbors_refined(mi1, mi2, imax1, imax2, periodic, ngz) assert_equal(np.sort(n1), np.sort(ans1)) assert_equal(np.sort(n2), np.sort(ans2)) def test_morton_neighbor(): from yt.utilities.lib.geometry_utils import get_morton_indices, morton_neighbor order = 20 imax = np.uint64(1 << order) p = np.array( [ [imax / 2, imax / 2, imax / 2], [imax / 2, imax / 2, 0], [imax / 2, imax / 2, imax], ], dtype=np.uint64, ) p_ans = np.array( [ [imax / 2, imax / 2, imax / 2 + 1], [imax / 2, imax / 2, imax / 2 - 1], [imax / 2, imax / 2, imax - 1], [imax / 2, imax / 2, 1], [imax / 2, imax / 2 + 1, imax / 2 + 1], [imax / 2 - 1, imax / 2 - 1, imax / 2], [imax / 2 - 1, imax / 2, imax / 2 + 1], [imax / 2, imax / 2 - 1, imax - 1], [imax / 2, imax / 2 + 1, 1], ], dtype=np.uint64, ) mi_ans = get_morton_indices(p_ans) assert_equal(morton_neighbor(p[0, :], [2], [+1], imax), mi_ans[0]) assert_equal(morton_neighbor(p[0, :], [2], [-1], imax), mi_ans[1]) assert_equal(morton_neighbor(p[1, :], [2], [-1], imax, periodic=False), -1) assert_equal(morton_neighbor(p[2, :], [2], [+1], imax, periodic=False), -1) assert_equal(morton_neighbor(p[1, :], [2], [-1], imax, periodic=True), mi_ans[2]) assert_equal(morton_neighbor(p[2, :], [2], [+1], imax, periodic=True), mi_ans[3]) assert_equal(morton_neighbor(p[0, :], [1, 2], [+1, +1], imax), mi_ans[4]) assert_equal(morton_neighbor(p[0, :], [0, 1], [-1, -1], imax), mi_ans[5]) assert_equal(morton_neighbor(p[0, :], [0, 2], [-1, +1], imax), mi_ans[6]) assert_equal(morton_neighbor(p[1, :], [1, 2], [-1, -1], imax, periodic=False), -1) assert_equal(morton_neighbor(p[2, :], [1, 2], [+1, +1], imax, periodic=False), -1) assert_equal( morton_neighbor(p[1, :], [1, 2], [-1, -1], imax, periodic=True), mi_ans[7] ) assert_equal( morton_neighbor(p[2, :], [1, 2], [+1, +1], imax, periodic=True), mi_ans[8] ) def test_get_morton_neighbors(): from yt.utilities.lib.geometry_utils import get_morton_indices, get_morton_neighbors order = 20 imax = 1 << order p = np.array( [ [imax / 2, imax / 2, imax / 2], [imax / 2, imax / 2, 0], [imax / 2, imax / 2, imax], ], dtype=np.uint64, ) pn_non = [ np.array( [ # x +/- 1 [imax / 2 + 1, imax / 2, imax / 2], [imax / 2 + 1, imax / 2 + 1, imax / 2], [imax / 2 + 1, imax / 2 + 1, imax / 2 + 1], [imax / 2 + 1, imax / 2 + 1, imax / 2 - 1], [imax / 2 + 1, imax / 2 - 1, imax / 2], [imax / 2 + 1, imax / 2 - 1, imax / 2 + 1], [imax / 2 + 1, imax / 2 - 1, imax / 2 - 1], [imax / 2 + 1, imax / 2, imax / 2 + 1], [imax / 2 + 1, imax / 2, imax / 2 - 1], [imax / 2 - 1, imax / 2, imax / 2], [imax / 2 - 1, imax / 2 + 1, imax / 2], [imax / 2 - 1, imax / 2 + 1, imax / 2 + 1], [imax / 2 - 1, imax / 2 + 1, imax / 2 - 1], [imax / 2 - 1, imax / 2 - 1, imax / 2], [imax / 2 - 1, imax / 2 - 1, imax / 2 + 1], [imax / 2 - 1, imax / 2 - 1, imax / 2 - 1], [imax / 2 - 1, imax / 2, imax / 2 + 1], [imax / 2 - 1, imax / 2, imax / 2 - 1], # y +/- 1 [imax / 2, imax / 2 + 1, imax / 2], [imax / 2, imax / 2 + 1, imax / 2 + 1], [imax / 2, imax / 2 + 1, imax / 2 - 1], [imax / 2, imax / 2 - 1, imax / 2], [imax / 2, imax / 2 - 1, imax / 2 + 1], [imax / 2, imax / 2 - 1, imax / 2 - 1], # x +/- 1 [imax / 2, imax / 2, imax / 2 + 1], [imax / 2, imax / 2, imax / 2 - 1], ], dtype=np.uint64, ), np.array( [ # x +/- 1 [imax / 2 + 1, imax / 2, 0], [imax / 2 + 1, imax / 2 + 1, 0], [imax / 2 + 1, imax / 2 + 1, 1], [imax / 2 + 1, imax / 2 - 1, 0], [imax / 2 + 1, imax / 2 - 1, 1], [imax / 2 + 1, imax / 2, 1], [imax / 2 - 1, imax / 2, 0], [imax / 2 - 1, imax / 2 + 1, 0], [imax / 2 - 1, imax / 2 + 1, 1], [imax / 2 - 1, imax / 2 - 1, 0], [imax / 2 - 1, imax / 2 - 1, 1], [imax / 2 - 1, imax / 2, 1], # y +/- 1 [imax / 2, imax / 2 + 1, 0], [imax / 2, imax / 2 + 1, 1], [imax / 2, imax / 2 - 1, 0], [imax / 2, imax / 2 - 1, 1], # z +/- 1 [imax / 2, imax / 2, 0 + 1], ], dtype=np.uint64, ), np.array( [ # x +/- 1 [imax / 2 + 1, imax / 2, imax], [imax / 2 + 1, imax / 2 + 1, imax], [imax / 2 + 1, imax / 2 + 1, imax - 1], [imax / 2 + 1, imax / 2 - 1, imax], [imax / 2 + 1, imax / 2 - 1, imax - 1], [imax / 2 + 1, imax / 2, imax - 1], [imax / 2 - 1, imax / 2, imax], [imax / 2 - 1, imax / 2 + 1, imax], [imax / 2 - 1, imax / 2 + 1, imax - 1], [imax / 2 - 1, imax / 2 - 1, imax], [imax / 2 - 1, imax / 2 - 1, imax - 1], [imax / 2 - 1, imax / 2, imax - 1], # y +/- 1 [imax / 2, imax / 2 + 1, imax], [imax / 2, imax / 2 + 1, imax - 1], [imax / 2, imax / 2 - 1, imax], [imax / 2, imax / 2 - 1, imax - 1], # z +/- 1 [imax / 2, imax / 2, imax - 1], ], dtype=np.uint64, ), ] pn_per = [ np.array( [ # x +/- 1 [imax / 2 + 1, imax / 2, imax / 2], [imax / 2 + 1, imax / 2 + 1, imax / 2], [imax / 2 + 1, imax / 2 + 1, imax / 2 + 1], [imax / 2 + 1, imax / 2 + 1, imax / 2 - 1], [imax / 2 + 1, imax / 2 - 1, imax / 2], [imax / 2 + 1, imax / 2 - 1, imax / 2 + 1], [imax / 2 + 1, imax / 2 - 1, imax / 2 - 1], [imax / 2 + 1, imax / 2, imax / 2 + 1], [imax / 2 + 1, imax / 2, imax / 2 - 1], [imax / 2 - 1, imax / 2, imax / 2], [imax / 2 - 1, imax / 2 + 1, imax / 2], [imax / 2 - 1, imax / 2 + 1, imax / 2 + 1], [imax / 2 - 1, imax / 2 + 1, imax / 2 - 1], [imax / 2 - 1, imax / 2 - 1, imax / 2], [imax / 2 - 1, imax / 2 - 1, imax / 2 + 1], [imax / 2 - 1, imax / 2 - 1, imax / 2 - 1], [imax / 2 - 1, imax / 2, imax / 2 + 1], [imax / 2 - 1, imax / 2, imax / 2 - 1], # y +/- 1 [imax / 2, imax / 2 + 1, imax / 2], [imax / 2, imax / 2 + 1, imax / 2 + 1], [imax / 2, imax / 2 + 1, imax / 2 - 1], [imax / 2, imax / 2 - 1, imax / 2], [imax / 2, imax / 2 - 1, imax / 2 + 1], [imax / 2, imax / 2 - 1, imax / 2 - 1], # z +/- 1 [imax / 2, imax / 2, imax / 2 + 1], [imax / 2, imax / 2, imax / 2 - 1], ], dtype=np.uint64, ), np.array( [ # x +/- 1 [imax / 2 + 1, imax / 2, 0], [imax / 2 + 1, imax / 2 + 1, 0], [imax / 2 + 1, imax / 2 + 1, 1], [imax / 2 + 1, imax / 2 + 1, imax - 1], [imax / 2 + 1, imax / 2 - 1, 0], [imax / 2 + 1, imax / 2 - 1, 1], [imax / 2 + 1, imax / 2 - 1, imax - 1], [imax / 2 + 1, imax / 2, 1], [imax / 2 + 1, imax / 2, imax - 1], [imax / 2 - 1, imax / 2, 0], [imax / 2 - 1, imax / 2 + 1, 0], [imax / 2 - 1, imax / 2 + 1, 1], [imax / 2 - 1, imax / 2 + 1, imax - 1], [imax / 2 - 1, imax / 2 - 1, 0], [imax / 2 - 1, imax / 2 - 1, 1], [imax / 2 - 1, imax / 2 - 1, imax - 1], [imax / 2 - 1, imax / 2, 1], [imax / 2 - 1, imax / 2, imax - 1], # y +/- 1 [imax / 2, imax / 2 + 1, 0], [imax / 2, imax / 2 + 1, 1], [imax / 2, imax / 2 + 1, imax - 1], [imax / 2, imax / 2 - 1, 0], [imax / 2, imax / 2 - 1, 1], [imax / 2, imax / 2 - 1, imax - 1], # z +/- 1 [imax / 2, imax / 2, 0 + 1], [imax / 2, imax / 2, imax - 1], ], dtype=np.uint64, ), np.array( [ # x +/- 1 [imax / 2 + 1, imax / 2, imax], [imax / 2 + 1, imax / 2 + 1, imax], [imax / 2 + 1, imax / 2 + 1, 1], [imax / 2 + 1, imax / 2 + 1, imax - 1], [imax / 2 + 1, imax / 2 - 1, imax], [imax / 2 + 1, imax / 2 - 1, 1], [imax / 2 + 1, imax / 2 - 1, imax - 1], [imax / 2 + 1, imax / 2, 1], [imax / 2 + 1, imax / 2, imax - 1], [imax / 2 - 1, imax / 2, imax], [imax / 2 - 1, imax / 2 + 1, imax], [imax / 2 - 1, imax / 2 + 1, 1], [imax / 2 - 1, imax / 2 + 1, imax - 1], [imax / 2 - 1, imax / 2 - 1, imax], [imax / 2 - 1, imax / 2 - 1, 1], [imax / 2 - 1, imax / 2 - 1, imax - 1], [imax / 2 - 1, imax / 2, 1], [imax / 2 - 1, imax / 2, imax - 1], # y +/- 1 [imax / 2, imax / 2 + 1, imax], [imax / 2, imax / 2 + 1, 1], [imax / 2, imax / 2 + 1, imax - 1], [imax / 2, imax / 2 - 1, imax], [imax / 2, imax / 2 - 1, 1], [imax / 2, imax / 2 - 1, imax - 1], # z +/- 1 [imax / 2, imax / 2, 1], [imax / 2, imax / 2, imax - 1], ], dtype=np.uint64, ), ] mi = get_morton_indices(p) N = mi.shape[0] # Non-periodic for i in range(N): out = get_morton_neighbors( np.array([mi[i]], dtype=np.uint64), order=order, periodic=False ) ans = get_morton_indices(np.vstack([p[i, :], pn_non[i]])) assert_array_equal(np.unique(out), np.unique(ans), err_msg=f"Non-periodic: {i}") # Periodic for i in range(N): out = get_morton_neighbors( np.array([mi[i]], dtype=np.uint64), order=order, periodic=True ) ans = get_morton_indices(np.vstack([p[i, :], pn_per[i]])) assert_array_equal(np.unique(out), np.unique(ans), err_msg=f"Periodic: {i}") def test_dist(): from yt.utilities.lib.geometry_utils import dist p = np.array([0.0, 0.0, 0.0], dtype=np.float64) q = np.array([0.0, 0.0, 0.0], dtype=np.float64) assert_equal(dist(p, q), 0.0) p = np.array([0.0, 0.0, 0.0], dtype=np.float64) q = np.array([1.0, 0.0, 0.0], dtype=np.float64) assert_equal(dist(p, q), 1.0) p = np.array([0.0, 0.0, 0.0], dtype=np.float64) q = np.array([1.0, 1.0, 0.0], dtype=np.float64) assert_equal(dist(p, q), np.sqrt(2.0)) p = np.array([0.0, 0.0, 0.0], dtype=np.float64) q = np.array([1.0, 1.0, 1.0], dtype=np.float64) assert_equal(dist(p, q), np.sqrt(3.0)) def test_knn_direct(seed=1): from yt.utilities.lib.geometry_utils import knn_direct np.random.seed(seed) k = 64 N = 1e5 idx = np.arange(N, dtype=np.uint64) rad = np.arange(N, dtype=np.float64) pos = np.vstack(3 * [rad**2 / 3.0]).T sort_shf = np.arange(N, dtype=np.uint64) for _ in range(20): np.random.shuffle(sort_shf) sort_ans = np.argsort(sort_shf)[:k] sort_out = knn_direct(pos[sort_shf, :], k, sort_ans[0], idx) assert_array_equal(sort_out, sort_ans) # TODO: test of quadtree (.pxd) def test_obtain_position_vector(): ds = fake_random_ds( 64, nprocs=8, fields=_fields, units=_units, negative=[False, True, True, True] ) dd = ds.sphere((0.5, 0.5, 0.5), 0.2) coords = obtain_position_vector(dd) r = np.sqrt(np.sum(coords * coords, axis=0)) assert_array_less(r.max(), 0.2) assert_array_less(0.0, r.min()) def test_obtain_relative_velocity_vector(): ds = fake_random_ds( 64, nprocs=8, fields=_fields, units=_units, negative=[False, True, True, True] ) dd = ds.all_data() vels = obtain_relative_velocity_vector(dd) assert_array_equal(vels[0, :], dd["gas", "velocity_x"]) assert_array_equal(vels[1, :], dd["gas", "velocity_y"]) assert_array_equal(vels[2, :], dd["gas", "velocity_z"]) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_nn.py000066400000000000000000000017231510711153200232600ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_equal from yt.utilities.lib.bounded_priority_queue import ( validate, validate_nblist, validate_pid, ) # These test functions use utility functions in # yt.utilities.lib.bounded_priority_queue # to test functions which are not exposed at a python level def test_bounded_priority_queue(): dists = validate() answers = np.array([0.1, 0.001, -1.0, -1.0, -1.0]) assert_array_equal(answers, dists) def test_bounded_priority_queue_pid(): dists, pids = validate_pid() answers = np.array([0.1, 0.001, -1.0, -1.0, -1.0]) answers_pids = np.array([1, 10, -1, -1, -1]) assert_array_equal(answers, dists) assert_array_equal(answers_pids, pids) def test_neighbor_list(): data, pids = validate_nblist() answers_data = np.array([1.0, 1.0, 1.0, 1.0]) answers_pids = np.array([0, 1, 2, 3]) assert_array_equal(answers_data, data) assert_array_equal(answers_pids, pids) yt-project-yt-f043ac8/yt/utilities/lib/tests/test_ragged_arrays.py000066400000000000000000000032561510711153200254620ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.testing import assert_rel_equal from yt.utilities.lib.ragged_arrays import index_unop operations = ((np.sum, "sum"), (np.prod, "prod"), (np.max, "max"), (np.min, "min")) dtypes = ( (-1e8, 1e8, "float32"), (-1e8, 1e8, "float64"), (-10000, 10000, "int32"), (-100000000, 100000000, "int64"), ) def test_index_unop(): np.random.seed(0x4D3D3D3) indices = np.arange(1000, dtype="int64") np.random.shuffle(indices) sizes = np.array([200, 50, 50, 100, 32, 32, 32, 32, 32, 64, 376], dtype="int64") for mi, ma, dtype in dtypes: for op, operation in operations: # Create a random set of values values = np.random.random(1000) if operation != "prod": values = values * ma + (ma - mi) if operation == "prod" and dtype.startswith("int"): values = values.astype(dtype) values[values != 0] = 1 values[values == 0] = -1 values = values.astype(dtype) out_values = index_unop(values, indices, sizes, operation) i = 0 for j, v in enumerate(sizes): arr = values[indices[i : i + v]] if dtype == "float32": # Numpy 1.9.1 changes the accumulator type to promote assert_rel_equal(op(arr), out_values[j], 6) elif dtype == "float64": # Numpy 1.9.1 changes the accumulator type to promote assert_rel_equal(op(arr), out_values[j], 12) else: assert_equal(op(arr), out_values[j]) i += v yt-project-yt-f043ac8/yt/utilities/lib/tests/test_sample.py000066400000000000000000000020231510711153200241200ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_allclose from yt.utilities.lib.particle_mesh_operations import CICSample_3 def test_sample(): grid = {} dims = np.array([64, 64, 64], dtype="int32") inds = np.indices(dims) grid["x"] = inds[0] + 0.5 grid["y"] = inds[1] + 0.5 grid["z"] = inds[2] + 0.5 num_particles = np.int64(1000) xp = np.random.uniform(low=1.0, high=63.0, size=num_particles) yp = np.random.uniform(low=1.0, high=63.0, size=num_particles) zp = np.random.uniform(low=1.0, high=63.0, size=num_particles) xfield = np.zeros(num_particles) yfield = np.zeros(num_particles) zfield = np.zeros(num_particles) dx = 1.0 le = np.zeros(3) CICSample_3(xp, yp, zp, xfield, num_particles, grid["x"], le, dims, dx) CICSample_3(xp, yp, zp, yfield, num_particles, grid["y"], le, dims, dx) CICSample_3(xp, yp, zp, zfield, num_particles, grid["z"], le, dims, dx) assert_allclose(xp, xfield) assert_allclose(yp, yfield) assert_allclose(zp, zfield) yt-project-yt-f043ac8/yt/utilities/lib/tsearch.c000066400000000000000000000061501510711153200216660ustar00rootroot00000000000000/* * Tree search generalized from Knuth (6.2.2) Algorithm T just like * the AT&T man page says. * * The node_t structure is for internal use only, lint doesn't grok it. * * Written by reading the System V Interface Definition, not the code. * * Totally public domain. */ /*LINTLIBRARY*/ #include "tsearch.h" #include typedef struct node_t { char *key; struct node_t *left, *right; } node; /* find or insert datum into search tree */ void * tsearch(const void *vkey, void **vrootp, int (*compar)(const void *, const void *)) { node *q; char *key = (char *)vkey; node **rootp = (node **)vrootp; if (rootp == (struct node_t **)0) return ((void *)0); while (*rootp != (struct node_t *)0) { /* Knuth's T1: */ int r; if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */ return ((void *)*rootp); /* we found it! */ rootp = (r < 0) ? &(*rootp)->left : /* T3: follow left branch */ &(*rootp)->right; /* T4: follow right branch */ } q = (node *) malloc(sizeof(node)); /* T5: key not found */ if (q != (struct node_t *)0) { /* make new node */ *rootp = q; /* link new node to old */ q->key = key; /* initialize new node */ q->left = q->right = (struct node_t *)0; } return ((void *)q); } /* find datum in search tree */ void * tfind(const void *vkey, void **vrootp, int (*compar)(const void *, const void *)) { char *key = (char *)vkey; node **rootp = (node **)vrootp; if (rootp == (struct node_t **)0) return ((void *)0); while (*rootp != (struct node_t *)0) { /* Knuth's T1: */ int r; if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */ return ((void *)*rootp); /* we found it! */ rootp = (r < 0) ? &(*rootp)->left : /* T3: follow left branch */ &(*rootp)->right; /* T4: follow right branch */ } return ((void *)0); /* T5: key not found */ } /* delete node with given key */ void * tdelete(const void *vkey, void **vrootp, int (*compar)(const void *, const void *)) { node **rootp = (node **)vrootp; char *key = (char *)vkey; node *p = (node *)1; node *q; node *r; int cmp; if (rootp == (struct node_t **)0 || *rootp == (struct node_t *)0) return ((struct node_t *)0); while ((cmp = (*compar)(key, (*rootp)->key)) != 0) { p = *rootp; rootp = (cmp < 0) ? &(*rootp)->left : /* follow left branch */ &(*rootp)->right; /* follow right branch */ if (*rootp == (struct node_t *)0) return ((void *)0); /* key not found */ } r = (*rootp)->right; /* D1: */ if ((q = (*rootp)->left) == (struct node_t *)0) /* Left (struct node_t *)0? */ q = r; else if (r != (struct node_t *)0) { /* Right link is null? */ if (r->left == (struct node_t *)0) { /* D2: Find successor */ r->left = q; q = r; } else { /* D3: Find (struct node_t *)0 link */ for (q = r->left; q->left != (struct node_t *)0; q = r->left) r = q; r->left = q->right; q->left = (*rootp)->left; q->right = (*rootp)->right; } } free((struct node_t *) *rootp); /* D4: Free node */ *rootp = q; /* link parent to new node */ return(p); } yt-project-yt-f043ac8/yt/utilities/lib/tsearch.h000066400000000000000000000011671510711153200216760ustar00rootroot00000000000000/* * Tree search generalized from Knuth (6.2.2) Algorithm T just like * the AT&T man page says. * * The node_t structure is for internal use only, lint doesn't grok it. * * Written by reading the System V Interface Definition, not the code. * * Totally public domain. */ /*LINTLIBRARY*/ #ifndef TSEARCH_H #define TSEARCH_H void * tsearch(const void *vkey, void **vrootp, int (*compar)(const void *, const void *)); void * tfind(const void *vkey, void **vrootp, int (*compar)(const void *, const void *)); void * tdelete(const void *vkey, void **vrootp, int (*compar)(const void *, const void *)); #endif yt-project-yt-f043ac8/yt/utilities/lib/vec3_ops.pxd000066400000000000000000000034511510711153200223300ustar00rootroot00000000000000cimport cython from libc.math cimport sqrt @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline cython.floating dot(const cython.floating[3] a, const cython.floating[3] b) noexcept nogil: return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void cross(const cython.floating[3] a, const cython.floating[3] b, cython.floating c[3]) noexcept nogil: c[0] = a[1]*b[2] - a[2]*b[1] c[1] = a[2]*b[0] - a[0]*b[2] c[2] = a[0]*b[1] - a[1]*b[0] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void subtract(const cython.floating[3] a, const cython.floating[3] b, cython.floating c[3]) noexcept nogil: c[0] = a[0] - b[0] c[1] = a[1] - b[1] c[2] = a[2] - b[2] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline cython.floating distance(const cython.floating[3] a, const cython.floating[3] b) noexcept nogil: return sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2 +(a[2] - b[2])**2) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline void fma(const cython.floating f, const cython.floating[3] a, const cython.floating[3] b, cython.floating[3] c) noexcept nogil: c[0] = f * a[0] + b[0] c[1] = f * a[1] + b[1] c[2] = f * a[2] + b[2] @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef inline cython.floating L2_norm(const cython.floating[3] a) noexcept nogil: return sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]) yt-project-yt-f043ac8/yt/utilities/lib/volume_container.pxd000066400000000000000000000063341510711153200241630ustar00rootroot00000000000000""" A volume container """ cimport numpy as np cdef struct VolumeContainer: #----------------------------------------------------------------------------- # Encapsulates a volume container used for volume rendering. # # n_fields int : The number of fields available to the volume renderer. # data np.float64_t** : The data within the volume container. # mask np.uint8_t* : The mask of the volume container. It has dimensions one fewer in each direction than data. # left_edge np.float64_t[3] : The left edge of the volume container's bounding box. # right_edge np.float64_t[3] : The right edge of the volume container's bounding box. # np.float64_t dds[3] : The delta dimensions, such that dds[0] = ddx, dds[1] = ddy, dds[2] = ddz. # np.float64_t idds[3] : The inverse delta dimensions. Same as dds pattern, but the inverse. i.e. idds[0] = inv_ddx. # dims int[3] : The dimensions of the volume container. dims[0] = x, dims[1] = y, dims[2] = z. #----------------------------------------------------------------------------- int n_fields np.float64_t **data np.uint8_t *mask np.float64_t left_edge[3] np.float64_t right_edge[3] np.float64_t dds[3] np.float64_t idds[3] int dims[3] cdef inline int vc_index(VolumeContainer *vc, int i, int j, int k): #----------------------------------------------------------------------------- # vc_index(VolumeContainer *vc, int i, int j, int k) # vc VolumeContainer* : Pointer to the volume container to be indexed. # i int : The first dimension coordinate. # j int : The second dimension coordinate. # k int : The third dimension coordinates. # # Returns the 3-dimensional index in the volume container given coordinates i, j, k. # This is used for 3-dimensional access in a flat container using C-ordering. # This is calculated by: # vc_index = i * vc.dim[1] * vc.dims[2] + j * vc.dims[2] + k # and then simplified (as shown below) by combining one multiplication operation. # # 2-dimensional example: # A 4 x 3 array may be represented as: # a = [0, 1, 2, 3, # 4, 5, 6, 7, # 8, 9, 10, 11] # or similarly, in a flat container in row-successive order as: # a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # # To access the coordinate at (1,1) in the flat container: # i * dims[1] + j # = 1 * 3 + 1 # = 4 # The 3-dimensional case (presented below) is similar. #----------------------------------------------------------------------------- return (i*vc.dims[1]+j)*vc.dims[2]+k cdef inline int vc_pos_index(VolumeContainer *vc, np.float64_t *spos): cdef int index[3] cdef int i for i in range(3): index[i] = ((spos[i] - vc.left_edge[i]) * vc.idds[i]) return vc_index(vc, index[0], index[1], index[2]) yt-project-yt-f043ac8/yt/utilities/lib/write_array.pyx000066400000000000000000000023231510711153200231610ustar00rootroot00000000000000""" Faster, cythonized file IO """ import numpy as np cimport cython cimport numpy as np DTYPE = np.float64 ctypedef np.float64_t DTYPE_t @cython.boundscheck(False) def write_3D_array(np.ndarray[DTYPE_t, ndim=3] data, fhandle): assert data.dtype == DTYPE cdef int Nx, Ny, Nz Nx = data.shape[0] Ny = data.shape[1] Nz = data.shape[2] cdef unsigned int i, j, k for i in np.arange(Nz): for j in np.arange(Ny): for k in np.arange(Nx): fhandle.write(str(data[k, j, i]) + '\n') @cython.boundscheck(False) def write_3D_vector_array(np.ndarray[DTYPE_t, ndim=3] data_x, np.ndarray[DTYPE_t, ndim=3] data_y, np.ndarray[DTYPE_t, ndim=3] data_z, fhandle): assert data_x.dtype == DTYPE cdef int Nx, Ny, Nz Nx = data_x.shape[0] Ny = data_x.shape[1] Nz = data_x.shape[2] cdef unsigned int i, j, k for i in np.arange(Nz): for j in np.arange(Ny): for k in np.arange(Nx): fx = data_x[k, j, i] fy = data_y[k, j, i] fz = data_z[k, j, i] fhandle.write('{} {} {} \n'.format(fx, fy, fz)) yt-project-yt-f043ac8/yt/utilities/linear_interpolators.py000066400000000000000000000355361510711153200241460ustar00rootroot00000000000000import numpy as np import yt.utilities.lib.interpolators as lib from yt.funcs import mylog class UnilinearFieldInterpolator: def __init__(self, table, boundaries, field_names, truncate=False): r"""Initialize a 1D interpolator for field data. table : array The data table over which interpolation is performed. boundaries: tuple or array If a tuple, this should specify the upper and lower bounds for the bins of the data table. This assumes the bins are evenly spaced. If an array, this specifies the bins explicitly. field_names: str Name of the field to be used as input data for interpolation. truncate : bool If False, an exception is raised if the input values are outside the bounds of the table. If True, extrapolation is performed. Examples -------- ad = ds.all_data() table_data = np.random.random(64) interp = UnilinearFieldInterpolator(table_data, (0.0, 1.0), "x", truncate=True) field_data = interp(ad) """ self.table = table.astype("float64") self.truncate = truncate self.x_name = field_names if isinstance(boundaries, np.ndarray): if boundaries.size != table.shape[0]: mylog.error("Bins array not the same length as the data.") raise ValueError self.x_bins = boundaries else: x0, x1 = boundaries self.x_bins = np.linspace(x0, x1, table.shape[0], dtype="float64") def __call__(self, data_object): orig_shape = data_object[self.x_name].shape x_vals = data_object[self.x_name].ravel().astype("float64") x_i = (np.digitize(x_vals, self.x_bins) - 1).astype("int32") if np.any((x_i == -1) | (x_i == len(self.x_bins) - 1)): if not self.truncate: mylog.error( "Sorry, but your values are outside " "the table! Dunno what to do, so dying." ) mylog.error("Error was in: %s", data_object) raise ValueError else: x_i = np.minimum(np.maximum(x_i, 0), len(self.x_bins) - 2) my_vals = np.zeros(x_vals.shape, dtype="float64") lib.UnilinearlyInterpolate(self.table, x_vals, self.x_bins, x_i, my_vals) return my_vals.reshape(orig_shape) class BilinearFieldInterpolator: def __init__(self, table, boundaries, field_names, truncate=False): r"""Initialize a 2D interpolator for field data. table : array The data table over which interpolation is performed. boundaries: tuple Either a tuple of lower and upper bounds for the x and y bins given as (x0, x1, y0, y1) or a tuple of two arrays containing the x and y bins. field_names: list Names of the fields to be used as input data for interpolation. truncate : bool If False, an exception is raised if the input values are outside the bounds of the table. If True, extrapolation is performed. Examples -------- ad = ds.all_data() table_data = np.random.random((64, 64)) interp = BilinearFieldInterpolator(table_data, (0.0, 1.0, 0.0, 1.0), ["x", "y"], truncate=True) field_data = interp(ad) """ self.table = table.astype("float64") self.truncate = truncate self.x_name, self.y_name = field_names if len(boundaries) == 4: x0, x1, y0, y1 = boundaries self.x_bins = np.linspace(x0, x1, table.shape[0], dtype="float64") self.y_bins = np.linspace(y0, y1, table.shape[1], dtype="float64") elif len(boundaries) == 2: if boundaries[0].size != table.shape[0]: mylog.error("X bins array not the same length as the data.") raise ValueError if boundaries[1].size != table.shape[1]: mylog.error("Y bins array not the same length as the data.") raise ValueError self.x_bins = boundaries[0] self.y_bins = boundaries[1] else: mylog.error( "Boundaries must be given as (x0, x1, y0, y1) or as (x_bins, y_bins)" ) raise ValueError def __call__(self, data_object): orig_shape = data_object[self.x_name].shape x_vals = data_object[self.x_name].ravel().astype("float64") y_vals = data_object[self.y_name].ravel().astype("float64") x_i = (np.digitize(x_vals, self.x_bins) - 1).astype("int32") y_i = (np.digitize(y_vals, self.y_bins) - 1).astype("int32") if np.any((x_i == -1) | (x_i == len(self.x_bins) - 1)) or np.any( (y_i == -1) | (y_i == len(self.y_bins) - 1) ): if not self.truncate: mylog.error( "Sorry, but your values are outside " "the table! Dunno what to do, so dying." ) mylog.error("Error was in: %s", data_object) raise ValueError else: x_i = np.minimum(np.maximum(x_i, 0), len(self.x_bins) - 2) y_i = np.minimum(np.maximum(y_i, 0), len(self.y_bins) - 2) my_vals = np.zeros(x_vals.shape, dtype="float64") lib.BilinearlyInterpolate( self.table, x_vals, y_vals, self.x_bins, self.y_bins, x_i, y_i, my_vals ) return my_vals.reshape(orig_shape) class TrilinearFieldInterpolator: def __init__(self, table, boundaries, field_names, truncate=False): r"""Initialize a 3D interpolator for field data. table : array The data table over which interpolation is performed. boundaries: tuple Either a tuple of lower and upper bounds for the x, y, and z bins given as (x0, x1, y0, y1, z0, z1) or a tuple of three arrays containing the x, y, and z bins. field_names: list Names of the fields to be used as input data for interpolation. truncate : bool If False, an exception is raised if the input values are outside the bounds of the table. If True, extrapolation is performed. Examples -------- ad = ds.all_data() table_data = np.random.random((64, 64, 64)) interp = TrilinearFieldInterpolator(table_data, (0.0, 1.0, 0.0, 1.0, 0.0, 1.0), ["x", "y", "z"], truncate=True) field_data = interp(ad) """ self.table = table.astype("float64") self.truncate = truncate self.x_name, self.y_name, self.z_name = field_names if len(boundaries) == 6: x0, x1, y0, y1, z0, z1 = boundaries self.x_bins = np.linspace(x0, x1, table.shape[0], dtype="float64") self.y_bins = np.linspace(y0, y1, table.shape[1], dtype="float64") self.z_bins = np.linspace(z0, z1, table.shape[2], dtype="float64") elif len(boundaries) == 3: if boundaries[0].size != table.shape[0]: mylog.error("X bins array not the same length as the data.") raise ValueError if boundaries[1].size != table.shape[1]: mylog.error("Y bins array not the same length as the data.") raise ValueError if boundaries[2].size != table.shape[2]: mylog.error("Z bins array not the same length as the data.") raise ValueError self.x_bins = boundaries[0] self.y_bins = boundaries[1] self.z_bins = boundaries[2] else: mylog.error( "Boundaries must be given as (x0, x1, y0, y1, z0, z1) " "or as (x_bins, y_bins, z_bins)" ) raise ValueError def __call__(self, data_object): orig_shape = data_object[self.x_name].shape x_vals = data_object[self.x_name].ravel().astype("float64") y_vals = data_object[self.y_name].ravel().astype("float64") z_vals = data_object[self.z_name].ravel().astype("float64") x_i = np.digitize(x_vals, self.x_bins).astype("int64") - 1 y_i = np.digitize(y_vals, self.y_bins).astype("int64") - 1 z_i = np.digitize(z_vals, self.z_bins).astype("int64") - 1 if ( np.any((x_i == -1) | (x_i == len(self.x_bins) - 1)) or np.any((y_i == -1) | (y_i == len(self.y_bins) - 1)) or np.any((z_i == -1) | (z_i == len(self.z_bins) - 1)) ): if not self.truncate: mylog.error( "Sorry, but your values are outside " "the table! Dunno what to do, so dying." ) mylog.error("Error was in: %s", data_object) raise ValueError else: x_i = np.minimum(np.maximum(x_i, 0), len(self.x_bins) - 2) y_i = np.minimum(np.maximum(y_i, 0), len(self.y_bins) - 2) z_i = np.minimum(np.maximum(z_i, 0), len(self.z_bins) - 2) my_vals = np.zeros(x_vals.shape, dtype="float64") lib.TrilinearlyInterpolate( self.table, x_vals, y_vals, z_vals, self.x_bins, self.y_bins, self.z_bins, x_i, y_i, z_i, my_vals, ) return my_vals.reshape(orig_shape) class QuadrilinearFieldInterpolator: def __init__(self, table, boundaries, field_names, truncate=False): r"""Initialize a 4D interpolator for field data. table : array The data table over which interpolation is performed. boundaries: tuple Either a tuple of lower and upper bounds for the x, y, z, and w bins given as (x0, x1, y0, y1, z0, z1, w0, w1) or a tuple of four arrays containing the x, y, z, and w bins. field_names: list Names of the fields to be used as input data for interpolation. truncate : bool If False, an exception is raised if the input values are outside the bounds of the table. If True, extrapolation is performed. Examples -------- ad = ds.all_data() table_data = np.random.random((64, 64, 64, 64)) interp = BilinearFieldInterpolator(table_data, (0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0), ["x", "y", "z", "w"], truncate=True) field_data = interp(ad) """ self.table = table.astype("float64") self.truncate = truncate self.x_name, self.y_name, self.z_name, self.w_name = field_names if len(boundaries) == 8: x0, x1, y0, y1, z0, z1, w0, w1 = boundaries self.x_bins = np.linspace(x0, x1, table.shape[0]).astype("float64") self.y_bins = np.linspace(y0, y1, table.shape[1]).astype("float64") self.z_bins = np.linspace(z0, z1, table.shape[2]).astype("float64") self.w_bins = np.linspace(w0, w1, table.shape[3]).astype("float64") elif len(boundaries) == 4: if boundaries[0].size != table.shape[0]: mylog.error("X bins array not the same length as the data.") raise ValueError if boundaries[1].size != table.shape[1]: mylog.error("Y bins array not the same length as the data.") raise ValueError if boundaries[2].size != table.shape[2]: mylog.error("Z bins array not the same length as the data.") raise ValueError if boundaries[3].size != table.shape[3]: mylog.error("W bins array not the same length as the data.") raise ValueError self.x_bins = boundaries[0] self.y_bins = boundaries[1] self.z_bins = boundaries[2] self.w_bins = boundaries[3] else: mylog.error( "Boundaries must be given as (x0, x1, y0, y1, z0, z1, w0, w1) " "or as (x_bins, y_bins, z_bins, w_bins)" ) raise ValueError def __call__(self, data_object): orig_shape = data_object[self.x_name].shape x_vals = data_object[self.x_name].ravel().astype("float64") y_vals = data_object[self.y_name].ravel().astype("float64") z_vals = data_object[self.z_name].ravel().astype("float64") w_vals = data_object[self.w_name].ravel().astype("float64") x_i = np.digitize(x_vals, self.x_bins).astype("int64") - 1 y_i = np.digitize(y_vals, self.y_bins).astype("int64") - 1 z_i = np.digitize(z_vals, self.z_bins).astype("int64") - 1 w_i = np.digitize(w_vals, self.w_bins).astype("int64") - 1 if ( np.any((x_i == -1) | (x_i == len(self.x_bins) - 1)) or np.any((y_i == -1) | (y_i == len(self.y_bins) - 1)) or np.any((z_i == -1) | (z_i == len(self.z_bins) - 1)) or np.any((w_i == -1) | (w_i == len(self.w_bins) - 1)) ): if not self.truncate: mylog.error( "Sorry, but your values are outside " "the table! Dunno what to do, so dying." ) mylog.error("Error was in: %s", data_object) raise ValueError else: x_i = np.minimum(np.maximum(x_i, 0), len(self.x_bins) - 2) y_i = np.minimum(np.maximum(y_i, 0), len(self.y_bins) - 2) z_i = np.minimum(np.maximum(z_i, 0), len(self.z_bins) - 2) w_i = np.minimum(np.maximum(w_i, 0), len(self.w_bins) - 2) my_vals = np.zeros(x_vals.shape, dtype="float64") lib.QuadrilinearlyInterpolate( self.table, x_vals, y_vals, z_vals, w_vals, self.x_bins, self.y_bins, self.z_bins, self.w_bins, x_i, y_i, z_i, w_i, my_vals, ) return my_vals.reshape(orig_shape) def get_centers(ds, filename, center_cols, radius_col, unit="1"): """ Return an iterator over EnzoSphere objects generated from the appropriate columns in *filename*. Optionally specify the *unit* radius is in. """ for line in open(filename): if line.startswith("#"): continue vals = line.split() x, y, z = (float(vals[i]) for i in center_cols) r = float(vals[radius_col]) yield ds.sphere([x, y, z], r / ds[unit]) yt-project-yt-f043ac8/yt/utilities/lodgeit.py000066400000000000000000000242421510711153200213260ustar00rootroot00000000000000""" LodgeIt! ~~~~~~~~ A script that pastes stuff into the enzotools pastebin on paste.enzotools.org. Modified (very, very slightly) from the original script by the authors below. .lodgeitrc / _lodgeitrc ----------------------- Under UNIX create a file called ``~/.lodgeitrc``, under Windows create a file ``%APPDATA%/_lodgeitrc`` to override defaults:: language=default_language clipboard=true/false open_browser=true/false encoding=fallback_charset :authors: 2007-2008 Georg Brandl , 2006 Armin Ronacher , 2006 Matt Good , 2005 Raphael Slinckx """ import os import sys from optparse import OptionParser SCRIPT_NAME = os.path.basename(sys.argv[0]) VERSION = "0.3" SERVICE_URL = "http://paste.yt-project.org/" SETTING_KEYS = ["author", "title", "language", "private", "clipboard", "open_browser"] # global server proxy _xmlrpc_service = None def fail(msg, code): """Bail out with an error message.""" print(f"ERROR: {msg}", file=sys.stderr) sys.exit(code) def load_default_settings(): """Load the defaults from the lodgeitrc file.""" settings = { "language": None, "clipboard": True, "open_browser": False, "encoding": "iso-8859-15", } rcfile = None if os.name == "posix": rcfile = os.path.expanduser("~/.lodgeitrc") elif os.name == "nt" and "APPDATA" in os.environ: rcfile = os.path.expandvars(r"$APPDATA\_lodgeitrc") if rcfile: try: f = open(rcfile) for line in f: if line.strip()[:1] in "#;": continue p = line.split("=", 1) if len(p) == 2: key = p[0].strip().lower() if key in settings: if key in ("clipboard", "open_browser"): settings[key] = p[1].strip().lower() in ( "true", "1", "on", "yes", ) else: settings[key] = p[1].strip() f.close() except OSError: pass settings["tags"] = [] settings["title"] = None return settings def make_utf8(text, encoding): """Convert a text to UTF-8, brute-force.""" try: u = str(text, "utf-8") uenc = "utf-8" except UnicodeError: try: u = str(text, encoding) uenc = "utf-8" except UnicodeError: u = str(text, "iso-8859-15", "ignore") uenc = "iso-8859-15" try: import chardet except ImportError: return u.encode("utf-8") d = chardet.detect(text) if d["encoding"] == uenc: return u.encode("utf-8") return str(text, d["encoding"], "ignore").encode("utf-8") def get_xmlrpc_service(): """Create the XMLRPC server proxy and cache it.""" global _xmlrpc_service import xmlrpc.client if _xmlrpc_service is None: try: _xmlrpc_service = xmlrpc.client.ServerProxy( SERVICE_URL + "xmlrpc/", allow_none=True ) except Exception as err: fail(f"Could not connect to Pastebin: {err}", -1) return _xmlrpc_service def copy_url(url): """Copy the url into the clipboard.""" # try windows first try: import win32clipboard except ImportError: # then give pbcopy a try. do that before gtk because # gtk might be installed on os x but nobody is interested # in the X11 clipboard there. from subprocess import PIPE, Popen try: client = Popen(["pbcopy"], stdin=PIPE) except OSError: try: import pygtk pygtk.require("2.0") import gobject import gtk except ImportError: return gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD).set_text(url) gobject.idle_add(gtk.main_quit) gtk.main() else: client.stdin.write(url) client.stdin.close() client.wait() else: win32clipboard.OpenClipboard() win32clipboard.EmptyClipboard() win32clipboard.SetClipboardText(url) win32clipboard.CloseClipboard() def open_webbrowser(url): """Open a new browser window.""" import webbrowser webbrowser.open(url) def language_exists(language): """Check if a language alias exists.""" xmlrpc = get_xmlrpc_service() langs = xmlrpc.pastes.getLanguages() return language in langs def get_mimetype(data, filename): """Try to get MIME type from data.""" try: import gnomevfs except ImportError: from mimetypes import guess_type if filename: return guess_type(filename)[0] else: if filename: return gnomevfs.get_mime_type(os.path.abspath(filename)) return gnomevfs.get_mime_type_for_data(data) def print_languages(): """Print a list of all supported languages, with description.""" xmlrpc = get_xmlrpc_service() languages = xmlrpc.pastes.getLanguages().items() def cmp(x, y): # emulate Python2's builtin cmp function # https://docs.python.org/2.7/library/functions.html#cmp # https://docs.python.org/3/whatsnew/3.0.html#ordering-comparisons return (x > y) - (x < y) languages.sort(lambda a, b: cmp(a[1].lower(), b[1].lower())) print("Supported Languages:") for alias, name in languages: print(f" {alias:<30}{name}") def download_paste(uid): """Download a paste given by ID.""" xmlrpc = get_xmlrpc_service() paste = xmlrpc.pastes.getPaste(uid) if not paste: fail(f'Paste "{uid}" does not exist.', 5) code = paste["code"] print(code) def create_paste(code, language, filename, mimetype, private): """Create a new paste.""" xmlrpc = get_xmlrpc_service() rv = xmlrpc.pastes.newPaste(language, code, None, filename, mimetype, private) if not rv: fail("Could not create paste. Something went wrong on the server side.", 4) return rv def compile_paste(filenames, langopt): """Create a single paste out of zero, one or multiple files.""" def read_file(f): try: return f.read() finally: f.close() mime = "" lang = langopt or "" if not filenames: data = read_file(sys.stdin) if not langopt: mime = get_mimetype(data, "") or "" fname = "" elif len(filenames) == 1: fname = filenames[0] data = read_file(open(filenames[0], "rb")) if not langopt: mime = get_mimetype(data, filenames[0]) or "" else: result = [] for fname in filenames: data = read_file(open(fname, "rb")) if langopt: result.append(f"### {fname} [{langopt}]\n\n") else: result.append(f"### {fname}\n\n") result.append(data) result.append("\n\n") data = "".join(result) lang = "multi" return data, lang, fname, mime def main( filename, languages=False, language=None, encoding="utf-8", open_browser=False, private=False, clipboard=False, download=None, ): """Paste a given script into a pastebin using the Lodgeit tool.""" # usage = ('Usage: %%prog [options] [FILE ...]\n\n' # 'Read the files and paste their contents to %s.\n' # 'If no file is given, read from standard input.\n' # 'If multiple files are given, they are put into a single paste.' # % SERVICE_URL) # parser = OptionParser(usage=usage) # # settings = load_default_settings() # # parser.add_option('-v', '--version', action='store_true', # help='Print script version') # parser.add_option('-L', '--languages', action='store_true', default=False, # help='Retrieve a list of supported languages') # parser.add_option('-l', '--language', default=settings['language'], # help='Used syntax highlighter for the file') # parser.add_option('-e', '--encoding', default=settings['encoding'], # help='Specify the encoding of a file (default is ' # 'utf-8 or guessing if available)') # parser.add_option('-b', '--open-browser', dest='open_browser', # action='store_true', # default=settings['open_browser'], # help='Open the paste in a web browser') # parser.add_option('-p', '--private', action='store_true', default=False, # help='Paste as private') # parser.add_option('--no-clipboard', dest='clipboard', # action='store_false', # default=settings['clipboard'], # help="Don't copy the url into the clipboard") # parser.add_option('--download', metavar='UID', # help='Download a given paste') # # opts, args = parser.parse_args() # if languages: print_languages() return elif download: download_paste(download) return # check language if given if language and not language_exists(language): print(f"Language {language} is not supported.") return # load file(s) args = [filename] try: data, language, filename, mimetype = compile_paste(args, language) except Exception as err: fail(f"Error while reading the file(s): {err}", 2) if not data: fail("Aborted, no content to paste.", 4) # create paste code = make_utf8(data, encoding).decode("utf-8") pid = create_paste(code, language, filename, mimetype, private) url = f"{SERVICE_URL}show/{pid}/" print(url) if open_browser: open_webbrowser(url) if clipboard: copy_url(url) yt-project-yt-f043ac8/yt/utilities/logger.py000066400000000000000000000106341510711153200211560ustar00rootroot00000000000000import logging import sys from collections.abc import Callable from yt.utilities.configure import YTConfig, configuration_callbacks _yt_sh: logging.StreamHandler | None = None _original_emitter: Callable[[logging.LogRecord], None] | None = None def set_log_level(level): """ Select which minimal logging level should be displayed. Parameters ---------- level: int or str Possible values by increasing level: 0 or "notset" 1 or "all" 10 or "debug" 20 or "info" 30 or "warning" 40 or "error" 50 or "critical" """ # this is a user-facing interface to avoid importing from yt.utilities in user code. if isinstance(level, str): level = level.upper() if level == "ALL": # non-standard alias level = 1 ytLogger.setLevel(level) ytLogger.debug("Set log level to %s", level) ytLogger = logging.getLogger("yt") class DuplicateFilter(logging.Filter): """A filter that removes duplicated successive log entries.""" # source # https://stackoverflow.com/questions/44691558/suppress-multiple-messages-with-same-content-in-python-logging-module-aka-log-co def filter(self, record): current_log = (record.module, record.levelno, record.msg, record.args) if current_log != getattr(self, "last_log", None): self.last_log = current_log return True return False ytLogger.addFilter(DuplicateFilter()) class DeprecatedFieldFilter(logging.Filter): """A filter that suppresses repeated logging of deprecated field warnings""" def __init__(self, name=""): self.logged_fields = [] super().__init__(name=name) def filter(self, record): if not record.msg.startswith("The Derived Field"): return True field = record.args[0] if field in self.logged_fields: return False self.logged_fields.append(field) return True ytLogger.addFilter(DeprecatedFieldFilter()) # This next bit is grabbed from: # http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored def add_coloring_to_emit_ansi(fn): # add methods we need to the class def new(*args): levelno = args[0].levelno if levelno >= 50: color = "\x1b[31m" # red elif levelno >= 40: color = "\x1b[31m" # red elif levelno >= 30: color = "\x1b[33m" # yellow elif levelno >= 20: color = "\x1b[32m" # green elif levelno >= 10: color = "\x1b[35m" # pink else: color = "\x1b[0m" # normal ln = color + args[0].levelname + "\x1b[0m" args[0].levelname = ln return fn(*args) return new ufstring = "%(name)-3s: [%(levelname)-9s] %(asctime)s %(message)s" cfstring = "%(name)-3s: [%(levelname)-18s] %(asctime)s %(message)s" def colorize_logging(): f = logging.Formatter(cfstring) ytLogger.handlers[0].setFormatter(f) ytLogger.handlers[0].emit = add_coloring_to_emit_ansi(ytLogger.handlers[0].emit) def uncolorize_logging(): global _original_emitter, _yt_sh if None not in (_original_emitter, _yt_sh): f = logging.Formatter(ufstring) ytLogger.handlers[0].setFormatter(f) _yt_sh.emit = _original_emitter def disable_stream_logging(): if len(ytLogger.handlers) > 0: ytLogger.removeHandler(ytLogger.handlers[0]) h = logging.NullHandler() ytLogger.addHandler(h) def _runtime_configuration(ytcfg: YTConfig) -> None: # only run this at the end of yt.__init__, after yt.config.ytcfg was instantiated global _original_emitter, _yt_sh if ytcfg.get("yt", "stdout_stream_logging"): stream = sys.stdout else: stream = sys.stderr _level = min(max(ytcfg.get("yt", "log_level"), 0), 50) if ytcfg.get("yt", "suppress_stream_logging"): disable_stream_logging() else: _yt_sh = logging.StreamHandler(stream=stream) # create formatter and add it to the handlers formatter = logging.Formatter(ufstring) _yt_sh.setFormatter(formatter) # add the handler to the logger ytLogger.addHandler(_yt_sh) ytLogger.setLevel(_level) ytLogger.propagate = False _original_emitter = _yt_sh.emit if ytcfg.get("yt", "colored_logs"): colorize_logging() configuration_callbacks.append(_runtime_configuration) yt-project-yt-f043ac8/yt/utilities/math_utils.py000066400000000000000000001373611510711153200220570ustar00rootroot00000000000000import math import numpy as np from yt.units.yt_array import YTArray prec_accum = { int: np.int64, np.int8: np.int64, np.int16: np.int64, np.int32: np.int64, np.int64: np.int64, np.uint8: np.uint64, np.uint16: np.uint64, np.uint32: np.uint64, np.uint64: np.uint64, float: np.float64, np.float16: np.float64, np.float32: np.float64, np.float64: np.float64, complex: np.complex128, np.complex64: np.complex128, np.complex128: np.complex128, np.dtype("int"): np.int64, np.dtype("int8"): np.int64, np.dtype("int16"): np.int64, np.dtype("int32"): np.int64, np.dtype("int64"): np.int64, np.dtype("uint8"): np.uint64, np.dtype("uint16"): np.uint64, np.dtype("uint32"): np.uint64, np.dtype("uint64"): np.uint64, np.dtype("float"): np.float64, np.dtype("float16"): np.float64, np.dtype("float32"): np.float64, np.dtype("float64"): np.float64, np.dtype("complex"): np.complex128, np.dtype("complex64"): np.complex128, np.dtype("complex128"): np.complex128, } def periodic_position(pos, ds): r"""Assuming periodicity, find the periodic position within the domain. Parameters ---------- pos : array An array of floats. ds : ~yt.data_objects.static_output.Dataset A simulation static output. Examples -------- >>> a = np.array([1.1, 0.5, 0.5]) >>> data = {"Density": np.ones([32, 32, 32])} >>> ds = load_uniform_grid(data, [32, 32, 32], 1.0) >>> ppos = periodic_position(a, ds) >>> ppos array([ 0.1, 0.5, 0.5]) """ off = (pos - ds.domain_left_edge) % ds.domain_width return ds.domain_left_edge + off def periodic_dist(a, b, period, periodicity=(True, True, True)): r"""Find the Euclidean periodic distance between two sets of points. Parameters ---------- a : array or list Either an ndim long list of coordinates corresponding to a single point or an (ndim, npoints) list of coordinates for many points in space. b : array of list Either an ndim long list of coordinates corresponding to a single point or an (ndim, npoints) list of coordinates for many points in space. period : float or array or list If the volume is symmetrically periodic, this can be a single float, otherwise an array or list of floats giving the periodic size of the volume for each dimension. periodicity : An ndim-element tuple of booleans If an entry is true, the domain is assumed to be periodic along that direction. Examples -------- >>> a = [0.1, 0.1, 0.1] >>> b = [0.9, 0, 9, 0.9] >>> period = 1.0 >>> dist = periodic_dist(a, b, 1.0) >>> dist 0.346410161514 """ a = np.array(a) b = np.array(b) period = np.array(period) if period.size == 1: period = np.array([period, period, period]) if a.shape != b.shape: raise RuntimeError("Arrays must be the same shape.") if period.shape != a.shape and len(a.shape) > 1: n_tup = tuple(1 for i in range(a.ndim - 1)) period = np.tile(np.reshape(period, (a.shape[0],) + n_tup), (1,) + a.shape[1:]) elif len(a.shape) == 1: a = np.reshape(a, (a.shape[0],) + (1, 1)) b = np.reshape(b, (a.shape[0],) + (1, 1)) period = np.reshape(period, (a.shape[0],) + (1, 1)) c = np.empty((2,) + a.shape, dtype="float64") c[0, :] = np.abs(a - b) p_directions = [i for i, p in enumerate(periodicity) if p] np_directions = [i for i, p in enumerate(periodicity) if not p] for d in p_directions: c[1, d, :] = period[d, :] - np.abs(a - b)[d, :] for d in np_directions: c[1, d, :] = c[0, d, :] d = np.amin(c, axis=0) ** 2 r2 = d.sum(axis=0) if r2.size == 1: return np.sqrt(r2[0, 0]) return np.sqrt(r2) def periodic_ray(start, end, left=None, right=None): """ periodic_ray(start, end, left=None, right=None) Break up periodic ray into non-periodic segments. Accepts start and end points of periodic ray as YTArrays. Accepts optional left and right edges of periodic volume as YTArrays. Returns a list of lists of coordinates, where each element of the top-most list is a 2-list of start coords and end coords of the non-periodic ray: [[[x0start,y0start,z0start], [x0end, y0end, z0end]], [[x1start,y1start,z1start], [x1end, y1end, z1end]], ...,] Parameters ---------- start : array The starting coordinate of the ray. end : array The ending coordinate of the ray. left : optional, array The left corner of the periodic domain. If not given, an array of zeros with same size as the starting coordinate us used. right : optional, array The right corner of the periodic domain. If not given, an array of ones with same size as the starting coordinate us used. Examples -------- >>> import yt >>> start = yt.YTArray([0.5, 0.5, 0.5]) >>> end = yt.YTArray([1.25, 1.25, 1.25]) >>> periodic_ray(start, end) [ [ YTArray([0.5, 0.5, 0.5]) (dimensionless), YTArray([1., 1., 1.]) (dimensionless) ], [ YTArray([0., 0., 0.]) (dimensionless), YTArray([0.25, 0.25, 0.25]) (dimensionless) ] ] """ if left is None: left = np.zeros(start.shape) if right is None: right = np.ones(start.shape) dim = right - left vector = end - start wall = np.zeros_like(start) close = np.zeros(start.shape, dtype=object) left_bound = vector < 0 right_bound = vector > 0 no_bound = vector == 0.0 bound = vector != 0.0 wall[left_bound] = left[left_bound] close[left_bound] = np.max wall[right_bound] = right[right_bound] close[right_bound] = np.min wall[no_bound] = np.inf close[no_bound] = np.min segments = [] this_start = start.copy() this_end = end.copy() t = 0.0 tolerance = 1e-6 while t < 1.0 - tolerance: hit_left = (this_start <= left) & (vector < 0) if (hit_left).any(): this_start[hit_left] += dim[hit_left] this_end[hit_left] += dim[hit_left] hit_right = (this_start >= right) & (vector > 0) if (hit_right).any(): this_start[hit_right] -= dim[hit_right] this_end[hit_right] -= dim[hit_right] nearest = vector.unit_array * np.array( [close[q]([this_end[q], wall[q]]) for q in range(start.size)] ) dt = ((nearest - this_start) / vector)[bound].min() now = this_start + vector * dt close_enough = np.abs(now - nearest) / np.abs(vector.max()) < 1e-10 now[close_enough] = nearest[close_enough] segments.append([this_start.copy(), now.copy()]) this_start = now.copy() t += dt return segments def euclidean_dist(a, b): r"""Find the Euclidean distance between two points. Parameters ---------- a : array or list Either an ndim long list of coordinates corresponding to a single point or an (ndim, npoints) list of coordinates for many points in space. b : array or list Either an ndim long list of coordinates corresponding to a single point or an (ndim, npoints) list of coordinates for many points in space. Examples -------- >>> a = [0.1, 0.1, 0.1] >>> b = [0.9, 0, 9, 0.9] >>> period = 1.0 >>> dist = euclidean_dist(a, b) >>> dist 1.38564064606 """ a = np.array(a) b = np.array(b) if a.shape != b.shape: RuntimeError("Arrays must be the same shape.") c = a.copy() np.subtract(c, b, c) np.power(c, 2, c) c = c.sum(axis=0) if isinstance(c, np.ndarray): np.sqrt(c, c) else: # This happens if a and b only have one entry. c = math.sqrt(c) return c def rotate_vector_3D(a, dim, angle): r"""Rotates the elements of an array around an axis by some angle. Given an array of 3D vectors a, this rotates them around a coordinate axis by a clockwise angle. An alternative way to think about it is the coordinate axes are rotated counterclockwise, which changes the directions of the vectors accordingly. Parameters ---------- a : array An array of 3D vectors with dimension Nx3. dim : integer A integer giving the axis around which the vectors will be rotated. (x, y, z) = (0, 1, 2). angle : float The angle in radians through which the vectors will be rotated clockwise. Examples -------- >>> a = np.array([[1, 1, 0], [1, 0, 1], [0, 1, 1], [1, 1, 1], [3, 4, 5]]) >>> b = rotate_vector_3D(a, 2, np.pi / 2) >>> print(b) [[ 1.00000000e+00 -1.00000000e+00 0.00000000e+00] [ 6.12323400e-17 -1.00000000e+00 1.00000000e+00] [ 1.00000000e+00 6.12323400e-17 1.00000000e+00] [ 1.00000000e+00 -1.00000000e+00 1.00000000e+00] [ 4.00000000e+00 -3.00000000e+00 5.00000000e+00]] """ mod = False if len(a.shape) == 1: mod = True a = np.array([a]) if a.shape[1] != 3: raise ValueError("The second dimension of the array a must be == 3!") if dim == 0: R = np.array( [ [1, 0, 0], [0, np.cos(angle), np.sin(angle)], [0, -np.sin(angle), np.cos(angle)], ] ) elif dim == 1: R = np.array( [ [np.cos(angle), 0, -np.sin(angle)], [0, 1, 0], [np.sin(angle), 0, np.cos(angle)], ] ) elif dim == 2: R = np.array( [ [np.cos(angle), np.sin(angle), 0], [-np.sin(angle), np.cos(angle), 0], [0, 0, 1], ] ) else: raise ValueError("dim must be 0, 1, or 2!") if mod: return np.dot(R, a.T).T[0] else: return np.dot(R, a.T).T def modify_reference_frame(CoM, L, P=None, V=None): r"""Rotates and translates data into a new reference frame to make calculations easier. This is primarily useful for calculations of halo data. The data is translated into the center of mass frame. Next, it is rotated such that the angular momentum vector for the data is aligned with the z-axis. Put another way, if one calculates the angular momentum vector on the data that comes out of this function, it will always be along the positive z-axis. If the center of mass is re-calculated, it will be at the origin. Parameters ---------- CoM : array The center of mass in 3D. L : array The angular momentum vector. Optional -------- P : array The positions of the data to be modified (i.e. particle or grid cell positions). The array should be Nx3. V : array The velocities of the data to be modified (i.e. particle or grid cell velocities). The array should be Nx3. Returns ------- L : array The angular momentum vector equal to [0, 0, 1] modulo machine error. P : array The modified positional data. Only returned if P is not None V : array The modified velocity data. Only returned if V is not None Examples -------- >>> CoM = np.array([0.5, 0.5, 0.5]) >>> L = np.array([1, 0, 0]) >>> P = np.array([[1, 0.5, 0.5], [0, 0.5, 0.5], [0.5, 0.5, 0.5], [0, 0, 0]]) >>> V = p.copy() >>> LL, PP, VV = modify_reference_frame(CoM, L, P, V) >>> LL array([ 6.12323400e-17, 0.00000000e+00, 1.00000000e+00]) >>> PP array([[ 3.06161700e-17, 0.00000000e+00, 5.00000000e-01], [ -3.06161700e-17, 0.00000000e+00, -5.00000000e-01], [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], [ 5.00000000e-01, -5.00000000e-01, -5.00000000e-01]]) >>> VV array([[ -5.00000000e-01, 5.00000000e-01, 1.00000000e+00], [ -5.00000000e-01, 5.00000000e-01, 3.06161700e-17], [ -5.00000000e-01, 5.00000000e-01, 5.00000000e-01], [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00]]) """ # First translate the positions to center of mass reference frame. if P is not None: P = P - CoM # is the L vector pointing in the Z direction? if np.inner(L[:2], L[:2]) == 0.0: # the reason why we need to explicitly check for the above # is that formula is used in denominator # this just checks if we need to flip the z axis or not if L[2] < 0.0: # this is just a simple flip in direction of the z axis if P is not None: P = -P if V is not None: V = -V # return the values if V is None and P is not None: return L, P elif P is None and V is not None: return L, V else: return L, P, V # Normal vector is not aligned with simulation Z axis # Therefore we are going to have to apply a rotation # Now find the angle between modified L and the x-axis. LL = L.copy() LL[2] = 0.0 theta = np.arccos(np.inner(LL, [1.0, 0.0, 0.0]) / np.inner(LL, LL) ** 0.5) if L[1] < 0.0: theta = -theta # Now rotate all the position, velocity, and L vectors by this much around # the z axis. if P is not None: P = rotate_vector_3D(P, 2, theta) if V is not None: V = rotate_vector_3D(V, 2, theta) L = rotate_vector_3D(L, 2, theta) # Now find the angle between L and the z-axis. theta = np.arccos(np.inner(L, [0.0, 0.0, 1.0]) / np.inner(L, L) ** 0.5) # This time we rotate around the y axis. if P is not None: P = rotate_vector_3D(P, 1, theta) if V is not None: V = rotate_vector_3D(V, 1, theta) L = rotate_vector_3D(L, 1, theta) # return the values if V is None and P is not None: return L, P elif P is None and V is not None: return L, V else: return L, P, V def compute_rotational_velocity(CoM, L, P, V): r"""Computes the rotational velocity for some data around an axis. This is primarily for halo computations. Given some data, this computes the circular rotational velocity of each point (particle) in reference to the axis defined by the angular momentum vector. This is accomplished by converting the reference frame of the center of mass of the halo. Parameters ---------- CoM : array The center of mass in 3D. L : array The angular momentum vector. P : array The positions of the data to be modified (i.e. particle or grid cell positions). The array should be Nx3. V : array The velocities of the data to be modified (i.e. particle or grid cell velocities). The array should be Nx3. Returns ------- v : array An array N elements long that gives the circular rotational velocity for each datum (particle). Examples -------- >>> CoM = np.array([0, 0, 0]) >>> L = np.array([0, 0, 1]) >>> P = np.array([[1, 0, 0], [1, 1, 1], [0, 0, 1], [1, 1, 0]]) >>> V = np.array([[0, 1, 10], [-1, -1, -1], [1, 1, 1], [1, -1, -1]]) >>> circV = compute_rotational_velocity(CoM, L, P, V) >>> circV array([ 1. , 0. , 0. , 1.41421356]) """ # First we translate into the simple coordinates. L, P, V = modify_reference_frame(CoM, L, P, V) # Find the vector in the plane of the galaxy for each position point # that is perpendicular to the radial vector. radperp = np.cross([0, 0, 1], P) # Find the component of the velocity along the radperp vector. # Unf., I don't think there's a better way to do this. res = np.empty(V.shape[0], dtype="float64") for i, rp in enumerate(radperp): temp = np.dot(rp, V[i]) / np.dot(rp, rp) * rp res[i] = np.dot(temp, temp) ** 0.5 return res def compute_parallel_velocity(CoM, L, P, V): r"""Computes the parallel velocity for some data around an axis. This is primarily for halo computations. Given some data, this computes the velocity component along the angular momentum vector. This is accomplished by converting the reference frame of the center of mass of the halo. Parameters ---------- CoM : array The center of mass in 3D. L : array The angular momentum vector. P : array The positions of the data to be modified (i.e. particle or grid cell positions). The array should be Nx3. V : array The velocities of the data to be modified (i.e. particle or grid cell velocities). The array should be Nx3. Returns ------- v : array An array N elements long that gives the parallel velocity for each datum (particle). Examples -------- >>> CoM = np.array([0, 0, 0]) >>> L = np.array([0, 0, 1]) >>> P = np.array([[1, 0, 0], [1, 1, 1], [0, 0, 1], [1, 1, 0]]) >>> V = np.array([[0, 1, 10], [-1, -1, -1], [1, 1, 1], [1, -1, -1]]) >>> paraV = compute_parallel_velocity(CoM, L, P, V) >>> paraV array([10, -1, 1, -1]) """ # First we translate into the simple coordinates. L, P, V = modify_reference_frame(CoM, L, P, V) # And return just the z-axis velocities. return V[:, 2] def compute_radial_velocity(CoM, L, P, V): r"""Computes the radial velocity for some data around an axis. This is primarily for halo computations. Given some data, this computes the radial velocity component for the data. This is accomplished by converting the reference frame of the center of mass of the halo. Parameters ---------- CoM : array The center of mass in 3D. L : array The angular momentum vector. P : array The positions of the data to be modified (i.e. particle or grid cell positions). The array should be Nx3. V : array The velocities of the data to be modified (i.e. particle or grid cell velocities). The array should be Nx3. Returns ------- v : array An array N elements long that gives the radial velocity for each datum (particle). Examples -------- >>> CoM = np.array([0, 0, 0]) >>> L = np.array([0, 0, 1]) >>> P = np.array([[1, 0, 0], [1, 1, 1], [0, 0, 1], [1, 1, 0]]) >>> V = np.array([[0, 1, 10], [-1, -1, -1], [1, 1, 1], [1, -1, -1]]) >>> radV = compute_radial_velocity(CoM, L, P, V) >>> radV array([ 1. , 1.41421356 , 0. , 0.]) """ # First we translate into the simple coordinates. L, P, V = modify_reference_frame(CoM, L, P, V) # We find the tangential velocity by dotting the velocity vector # with the cylindrical radial vector for this point. # Unf., I don't think there's a better way to do this. P[:, 2] = 0 res = np.empty(V.shape[0], dtype="float64") for i, rad in enumerate(P): temp = np.dot(rad, V[i]) / np.dot(rad, rad) * rad res[i] = np.dot(temp, temp) ** 0.5 return res def compute_cylindrical_radius(CoM, L, P, V): r"""Compute the radius for some data around an axis in cylindrical coordinates. This is primarily for halo computations. Given some data, this computes the cylindrical radius for each point. This is accomplished by converting the reference frame of the center of mass of the halo. Parameters ---------- CoM : array The center of mass in 3D. L : array The angular momentum vector. P : array The positions of the data to be modified (i.e. particle or grid cell positions). The array should be Nx3. V : array The velocities of the data to be modified (i.e. particle or grid cell velocities). The array should be Nx3. Returns ------- cyl_r : array An array N elements long that gives the radial velocity for each datum (particle). Examples -------- >>> CoM = np.array([0, 0, 0]) >>> L = np.array([0, 0, 1]) >>> P = np.array([[1, 0, 0], [1, 1, 1], [0, 0, 1], [1, 1, 0]]) >>> V = np.array([[0, 1, 10], [-1, -1, -1], [1, 1, 1], [1, -1, -1]]) >>> cyl_r = compute_cylindrical_radius(CoM, L, P, V) >>> cyl_r array([ 1. , 1.41421356, 0. , 1.41421356]) """ # First we translate into the simple coordinates. L, P, V = modify_reference_frame(CoM, L, P, V) # Demote all the positions to the z=0 plane, which makes the distance # calculation very easy. P[:, 2] = 0 return np.sqrt((P * P).sum(axis=1)) def ortho_find(vec1): r"""Find two complementary orthonormal vectors to a given vector. For any given non-zero vector, there are infinite pairs of vectors orthonormal to it. This function gives you one arbitrary pair from that set along with the normalized version of the original vector. Parameters ---------- vec1 : array_like An array or list to represent a 3-vector. Returns ------- vec1 : array The original 3-vector array after having been normalized. vec2 : array A 3-vector array which is orthonormal to vec1. vec3 : array A 3-vector array which is orthonormal to vec1 and vec2. Raises ------ ValueError If input vector is the zero vector. Notes ----- Our initial vector is `vec1` which consists of 3 components: `x1`, `y1`, and `z1`. ortho_find determines a vector, `vec2`, which is orthonormal to `vec1` by finding a vector which has a zero-value dot-product with `vec1`. .. math:: vec1 \cdot vec2 = x_1 x_2 + y_1 y_2 + z_1 z_2 = 0 As a starting point, we arbitrarily choose `vec2` to have `x2` = 1, `y2` = 0: .. math:: vec1 \cdot vec2 = x_1 + (z_1 z_2) = 0 \rightarrow z_2 = -(x_1 / z_1) Of course, this will fail if `z1` = 0, in which case, let's say use `z2` = 1 and `x2` = 0: .. math:: \rightarrow y_2 = -(z_1 / y_1) Similarly, if `y1` = 0, this case will fail, in which case we use `y2` = 1 and `z2` = 0: .. math:: \rightarrow x_2 = -(y_1 / x_1) Since we don't allow `vec1` to be zero, all cases are accounted for. Producing `vec3`, the complementary orthonormal vector to `vec1` and `vec2` is accomplished by simply taking the cross product of `vec1` and `vec2`. Examples -------- >>> a = [1.0, 2.0, 3.0] >>> a, b, c = ortho_find(a) >>> a array([ 0.26726124, 0.53452248, 0.80178373]) >>> b array([ 0.9486833 , 0. , -0.31622777]) >>> c array([-0.16903085, 0.84515425, -0.50709255]) """ vec1 = np.array(vec1, dtype=np.float64) # Normalize norm = np.sqrt(np.vdot(vec1, vec1)) if norm == 0: raise ValueError("Zero vector used as input.") vec1 /= norm x1 = vec1[0] y1 = vec1[1] z1 = vec1[2] if z1 != 0: x2 = 1.0 y2 = 0.0 z2 = -(x1 / z1) norm2 = (1.0 + z2**2.0) ** (0.5) elif y1 != 0: x2 = 0.0 z2 = 1.0 y2 = -(z1 / y1) norm2 = (1.0 + y2**2.0) ** (0.5) else: y2 = 1.0 z2 = 0.0 x2 = -(y1 / x1) norm2 = (1.0 + z2**2.0) ** (0.5) vec2 = np.array([x2, y2, z2]) vec2 /= norm2 vec3 = np.cross(vec1, vec2) return vec1, vec2, vec3 def quartiles(a, axis=None, out=None, overwrite_input=False): """ Compute the quartile values (25% and 75%) along the specified axis in the same way that the numpy.median calculates the median (50%) value alone a specified axis. Check numpy.median for details, as it is virtually the same algorithm. Returns an array of the quartiles of the array elements [lower quartile, upper quartile]. Parameters ---------- a : array_like Input array or object that can be converted to an array. axis : {None, int}, optional Axis along which the quartiles are computed. The default (axis=None) is to compute the quartiles along a flattened version of the array. out : ndarray, optional Alternative output array in which to place the result. It must have the same shape and buffer length as the expected output, but the type (of the output) will be cast if necessary. overwrite_input : {False, True}, optional If True, then allow use of memory of input array (a) for calculations. The input array will be modified by the call to quartiles. This will save memory when you do not need to preserve the contents of the input array. Treat the input as undefined, but it will probably be fully or partially sorted. Default is False. Note that, if `overwrite_input` is True and the input is not already an ndarray, an error will be raised. Returns ------- quartiles : ndarray A new 2D array holding the result (unless `out` is specified, in which case that array is returned instead). If the input contains integers, or floats of smaller precision than 64, then the output data-type is float64. Otherwise, the output data-type is the same as that of the input. See Also -------- numpy.median, numpy.mean, numpy.percentile Notes ----- Given a vector V of length N, the quartiles of V are the 25% and 75% values of a sorted copy of V, ``V_sorted`` - i.e., ``V_sorted[(N-1)/4]`` and ``3*V_sorted[(N-1)/4]``, when N is odd. When N is even, it is the average of the two values bounding these values of ``V_sorted``. Examples -------- >>> a = np.arange(100).reshape(10, 10) >>> a array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]) >>> mu.quartiles(a) array([ 24.5, 74.5]) >>> mu.quartiles(a, axis=0) array([[ 15., 16., 17., 18., 19., 20., 21., 22., 23., 24.], [ 65., 66., 67., 68., 69., 70., 71., 72., 73., 74.]]) >>> mu.quartiles(a, axis=1) array([[ 1.5, 11.5, 21.5, 31.5, 41.5, 51.5, 61.5, 71.5, 81.5, 91.5], [ 6.5, 16.5, 26.5, 36.5, 46.5, 56.5, 66.5, 76.5, 86.5, 96.5]]) """ if overwrite_input: if axis is None: a_sorted = sorted(a.ravel()) else: a.sort(axis=axis) a_sorted = a else: a_sorted = np.sort(a, axis=axis) if axis is None: axis = 0 indexer = [slice(None)] * a_sorted.ndim indices = [int(a_sorted.shape[axis] / 4), int(a_sorted.shape[axis] * 0.75)] result = [] for index in indices: if a_sorted.shape[axis] % 2 == 1: # index with slice to allow mean (below) to work indexer[axis] = slice(index, index + 1) else: indexer[axis] = slice(index - 1, index + 1) # special cases for small arrays if a_sorted.shape[axis] == 2: # index with slice to allow mean (below) to work indexer[axis] = slice(index, index + 1) # Use mean in odd and even case to coerce data type # and check, use out array. result.append(np.mean(a_sorted[tuple(indexer)], axis=axis, out=out)) return np.array(result) def get_perspective_matrix(fovy, aspect, z_near, z_far): """ Given a field of view in radians, an aspect ratio, and a near and far plane distance, this routine computes the transformation matrix corresponding to perspective projection using homogeneous coordinates. Parameters ---------- fovy : scalar The angle in degrees of the field of view. aspect : scalar The aspect ratio of width / height for the projection. z_near : scalar The distance of the near plane from the camera. z_far : scalar The distance of the far plane from the camera. Returns ------- persp_matrix : ndarray A new 4x4 2D array. Represents a perspective transformation in homogeneous coordinates. Note that this matrix does not actually perform the projection. After multiplying a 4D vector of the form (x_0, y_0, z_0, 1.0), the point will be transformed to some (x_1, y_1, z_1, w). The final projection is applied by performing a divide by w, that is (x_1/w, y_1/w, z_1/w, w/w). The matrix uses a row-major ordering, rather than the column major ordering typically used by OpenGL. Notes ----- The usage of 4D homogeneous coordinates is for OpenGL and GPU hardware that automatically performs the divide by w operation. See the following for more details about the OpenGL perspective matrices. https://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/ http://www.songho.ca/opengl/gl_projectionmatrix.html """ tan_half_fovy = np.tan(np.radians(fovy) / 2) result = np.zeros((4, 4), dtype="float32", order="C") # result[0][0] = 1 / (aspect * tan_half_fovy) # result[1][1] = 1 / tan_half_fovy # result[2][2] = - (z_far + z_near) / (z_far - z_near) # result[3][2] = -1 # result[2][3] = -(2 * z_far * z_near) / (z_far - z_near) f = z_far n = z_near t = tan_half_fovy * n b = -t * aspect r = t * aspect l = -t * aspect result[0][0] = (2 * n) / (r - l) result[2][0] = (r + l) / (r - l) result[1][1] = (2 * n) / (t - b) result[1][2] = (t + b) / (t - b) result[2][2] = -(f + n) / (f - n) result[2][3] = -2 * f * n / (f - n) result[3][2] = -1 return result def get_orthographic_matrix(maxr, aspect, z_near, z_far): """ Given a field of view in radians, an aspect ratio, and a near and far plane distance, this routine computes the transformation matrix corresponding to perspective projection using homogeneous coordinates. Parameters ---------- maxr : scalar should be ``max(|x|, |y|)`` aspect : scalar The aspect ratio of width / height for the projection. z_near : scalar The distance of the near plane from the camera. z_far : scalar The distance of the far plane from the camera. Returns ------- persp_matrix : ndarray A new 4x4 2D array. Represents a perspective transformation in homogeneous coordinates. Note that this matrix does not actually perform the projection. After multiplying a 4D vector of the form (x_0, y_0, z_0, 1.0), the point will be transformed to some (x_1, y_1, z_1, w). The final projection is applied by performing a divide by w, that is (x_1/w, y_1/w, z_1/w, w/w). The matrix uses a row-major ordering, rather than the column major ordering typically used by OpenGL. Notes ----- The usage of 4D homogeneous coordinates is for OpenGL and GPU hardware that automatically performs the divide by w operation. See the following for more details about the OpenGL perspective matrices. http://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/orthographic-projection-matrix https://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/ http://www.songho.ca/opengl/gl_projectionmatrix.html """ r = maxr * aspect t = maxr l = -r b = -t result = np.zeros((4, 4), dtype="float32", order="C") result[0][0] = 2.0 / (r - l) result[1][1] = 2.0 / (t - b) result[2][2] = -2.0 / (z_far - z_near) result[3][3] = 1 result[3][0] = -(r + l) / (r - l) result[3][1] = -(t + b) / (t - b) result[3][2] = -(z_far + z_near) / (z_far - z_near) return result def get_lookat_matrix(eye, center, up): """ Given the position of a camera, the point it is looking at, and an up-direction. Computes the lookat matrix that moves all vectors such that the camera is at the origin of the coordinate system, looking down the z-axis. Parameters ---------- eye : array_like The position of the camera. Must be 3D. center : array_like The location that the camera is looking at. Must be 3D. up : array_like The direction that is considered up for the camera. Must be 3D. Returns ------- lookat_matrix : ndarray A new 4x4 2D array in homogeneous coordinates. This matrix moves all vectors in the same way required to move the camera to the origin of the coordinate system, with it pointing down the negative z-axis. """ eye = np.array(eye) center = np.array(center) up = np.array(up) f = (center - eye) / np.linalg.norm(center - eye) s = np.cross(f, up) / np.linalg.norm(np.cross(f, up)) u = np.cross(s, f) result = np.zeros((4, 4), dtype="float32", order="C") result[0][0] = s[0] result[0][1] = s[1] result[0][2] = s[2] result[1][0] = u[0] result[1][1] = u[1] result[1][2] = u[2] result[2][0] = -f[0] result[2][1] = -f[1] result[2][2] = -f[2] result[0][3] = -np.dot(s, eye) result[1][3] = -np.dot(u, eye) result[2][3] = np.dot(f, eye) result[3][3] = 1.0 return result def get_translate_matrix(dx, dy, dz): """ Given a movement amount for each coordinate, creates a translation matrix that moves the vector by each amount. Parameters ---------- dx : scalar A translation amount for the x-coordinate dy : scalar A translation amount for the y-coordinate dz : scalar A translation amount for the z-coordinate Returns ------- trans_matrix : ndarray A new 4x4 2D array. Represents a translation by dx, dy and dz in each coordinate respectively. """ result = np.zeros((4, 4), dtype="float32", order="C") result[0][0] = 1.0 result[1][1] = 1.0 result[2][2] = 1.0 result[3][3] = 1.0 result[0][3] = dx result[1][3] = dy result[2][3] = dz return result def get_scale_matrix(dx, dy, dz): """ Given a scaling factor for each coordinate, returns a matrix that corresponds to the given scaling amounts. Parameters ---------- dx : scalar A scaling factor for the x-coordinate. dy : scalar A scaling factor for the y-coordinate. dz : scalar A scaling factor for the z-coordinate. Returns ------- scale_matrix : ndarray A new 4x4 2D array. Represents a scaling by dx, dy, and dz in each coordinate respectively. """ result = np.zeros((4, 4), dtype="float32", order="C") result[0][0] = dx result[1][1] = dy result[2][2] = dz result[3][3] = 1 return result def get_rotation_matrix(theta, rot_vector): """ Given an angle theta and a 3D vector rot_vector, this routine computes the rotation matrix corresponding to rotating theta radians about rot_vector. Parameters ---------- theta : scalar The angle in radians. rot_vector : array_like The axis of rotation. Must be 3D. Returns ------- rot_matrix : ndarray A new 3x3 2D array. This is the representation of a rotation of theta radians about rot_vector in the simulation box coordinate frame See Also -------- ortho_find Examples -------- >>> a = [0, 1, 0] >>> theta = 0.785398163 # pi/4 >>> rot = mu.get_rotation_matrix(theta, a) >>> rot array([[ 0.70710678, 0. , 0.70710678], [ 0. , 1. , 0. ], [-0.70710678, 0. , 0.70710678]]) >>> np.dot(rot, a) array([ 0., 1., 0.]) # since a is an eigenvector by construction >>> np.dot(rot, [1, 0, 0]) array([ 0.70710678, 0. , -0.70710678]) """ ux = rot_vector[0] uy = rot_vector[1] uz = rot_vector[2] cost = np.cos(theta) sint = np.sin(theta) R = np.array( [ [ cost + ux**2 * (1 - cost), ux * uy * (1 - cost) - uz * sint, ux * uz * (1 - cost) + uy * sint, ], [ uy * ux * (1 - cost) + uz * sint, cost + uy**2 * (1 - cost), uy * uz * (1 - cost) - ux * sint, ], [ uz * ux * (1 - cost) - uy * sint, uz * uy * (1 - cost) + ux * sint, cost + uz**2 * (1 - cost), ], ] ) return R def quaternion_mult(q1, q2): """ Multiply two quaternions. The inputs are 4-component numpy arrays in the order [w, x, y, z]. """ w = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3] x = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2] y = q1[0] * q2[2] + q1[2] * q2[0] + q1[3] * q2[1] - q1[1] * q2[3] z = q1[0] * q2[3] + q1[3] * q2[0] + q1[1] * q2[2] - q1[2] * q2[1] return np.array([w, x, y, z]) def quaternion_to_rotation_matrix(quaternion): """ This converts a quaternion representation of on orientation to a rotation matrix. The input is a 4-component numpy array in the order [w, x, y, z], and the output is a 3x3 matrix stored as a 2D numpy array. We follow the approach in "3D Math Primer for Graphics and Game Development" by Dunn and Parberry. """ w = quaternion[0] x = quaternion[1] y = quaternion[2] z = quaternion[3] R = np.empty((3, 3), dtype=np.float64) R[0][0] = 1.0 - 2.0 * y**2 - 2.0 * z**2 R[0][1] = 2.0 * x * y + 2.0 * w * z R[0][2] = 2.0 * x * z - 2.0 * w * y R[1][0] = 2.0 * x * y - 2.0 * w * z R[1][1] = 1.0 - 2.0 * x**2 - 2.0 * z**2 R[1][2] = 2.0 * y * z + 2.0 * w * x R[2][0] = 2.0 * x * z + 2.0 * w * y R[2][1] = 2.0 * y * z - 2.0 * w * x R[2][2] = 1.0 - 2.0 * x**2 - 2.0 * y**2 return R def rotation_matrix_to_quaternion(rot_matrix): """ Convert a rotation matrix-based representation of an orientation to a quaternion. The input should be a 3x3 rotation matrix, while the output will be a 4-component numpy array. We follow the approach in "3D Math Primer for Graphics and Game Development" by Dunn and Parberry. """ m11 = rot_matrix[0][0] m12 = rot_matrix[0][1] m13 = rot_matrix[0][2] m21 = rot_matrix[1][0] m22 = rot_matrix[1][1] m23 = rot_matrix[1][2] m31 = rot_matrix[2][0] m32 = rot_matrix[2][1] m33 = rot_matrix[2][2] four_w_squared_minus_1 = m11 + m22 + m33 four_x_squared_minus_1 = m11 - m22 - m33 four_y_squared_minus_1 = m22 - m11 - m33 four_z_squared_minus_1 = m33 - m11 - m22 max_index = 0 four_max_squared_minus_1 = four_w_squared_minus_1 if four_x_squared_minus_1 > four_max_squared_minus_1: four_max_squared_minus_1 = four_x_squared_minus_1 max_index = 1 if four_y_squared_minus_1 > four_max_squared_minus_1: four_max_squared_minus_1 = four_y_squared_minus_1 max_index = 2 if four_z_squared_minus_1 > four_max_squared_minus_1: four_max_squared_minus_1 = four_z_squared_minus_1 max_index = 3 max_val = 0.5 * np.sqrt(four_max_squared_minus_1 + 1.0) mult = 0.25 / max_val if max_index == 0: w = max_val x = (m23 - m32) * mult y = (m31 - m13) * mult z = (m12 - m21) * mult elif max_index == 1: x = max_val w = (m23 - m32) * mult y = (m12 + m21) * mult z = (m31 + m13) * mult elif max_index == 2: y = max_val w = (m31 - m13) * mult x = (m12 + m21) * mult z = (m23 + m32) * mult elif max_index == 3: z = max_val w = (m12 - m21) * mult x = (m31 + m13) * mult y = (m23 + m32) * mult return np.array([w, x, y, z]) def get_sph_r(coords): # The spherical coordinates radius is simply the magnitude of the # coordinate vector. return np.sqrt(np.sum(coords**2, axis=0)) def resize_vector(vector, vector_array): if len(vector_array.shape) == 4: res_vector = np.resize(vector, (3, 1, 1, 1)) else: res_vector = np.resize(vector, (3, 1)) return res_vector def normalize_vector(vector): # this function normalizes # an input vector L2 = np.atleast_1d(np.linalg.norm(vector)) L2[L2 == 0] = 1.0 vector = vector / L2 return vector def get_sph_theta(coords, normal): # The angle (theta) with respect to the normal (J), is the arccos # of the dot product of the normal with the normalized coordinate # vector. res_normal = resize_vector(normal, coords) # check if the normal vector is normalized # since arccos requires the vector to be normalised res_normal = normalize_vector(res_normal) tile_shape = [1] + list(coords.shape)[1:] J = np.tile(res_normal, tile_shape) JdotCoords = np.sum(J * coords, axis=0) with np.errstate(invalid="ignore"): ret = np.arccos(JdotCoords / np.sqrt(np.sum(coords**2, axis=0))) ret[np.isnan(ret)] = 0 return ret def get_sph_phi(coords, normal): # We have freedom with respect to what axis (xprime) to define # the disk angle. Here I've chosen to use the axis that is # perpendicular to the normal and the y-axis. When normal == # y-hat, then set xprime = z-hat. With this definition, when # normal == z-hat (as is typical), then xprime == x-hat. # # The angle is then given by the arctan of the ratio of the # yprime-component and the xprime-component of the coordinate # vector. normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_xprime = resize_vector(xprime, coords) res_yprime = resize_vector(yprime, coords) tile_shape = [1] + list(coords.shape)[1:] Jx = np.tile(res_xprime, tile_shape) Jy = np.tile(res_yprime, tile_shape) Px = np.sum(Jx * coords, axis=0) Py = np.sum(Jy * coords, axis=0) return np.arctan2(Py, Px) def get_cyl_r(coords, normal): # The cross product of the normal (J) with a coordinate vector # gives a vector of magnitude equal to the cylindrical radius. res_normal = resize_vector(normal, coords) res_normal = normalize_vector(res_normal) tile_shape = [1] + list(coords.shape)[1:] J = np.tile(res_normal, tile_shape) JcrossCoords = np.cross(J, coords, axisa=0, axisb=0, axisc=0) return np.sqrt(np.sum(JcrossCoords**2, axis=0)) def get_cyl_z(coords, normal): # The dot product of the normal (J) with the coordinate vector # gives the cylindrical height. res_normal = resize_vector(normal, coords) res_normal = normalize_vector(res_normal) tile_shape = [1] + list(coords.shape)[1:] J = np.tile(res_normal, tile_shape) return np.sum(J * coords, axis=0) def get_cyl_theta(coords, normal): # This is identical to the spherical phi component return get_sph_phi(coords, normal) def get_cyl_r_component(vectors, theta, normal): # The r of a vector is the vector dotted with rhat normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_xprime = resize_vector(xprime, vectors) res_yprime = resize_vector(yprime, vectors) tile_shape = [1] + list(vectors.shape)[1:] Jx = np.tile(res_xprime, tile_shape) Jy = np.tile(res_yprime, tile_shape) rhat = Jx * np.cos(theta) + Jy * np.sin(theta) return np.sum(vectors * rhat, axis=0) def get_cyl_theta_component(vectors, theta, normal): # The theta component of a vector is the vector dotted with thetahat normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_xprime = resize_vector(xprime, vectors) res_yprime = resize_vector(yprime, vectors) tile_shape = [1] + list(vectors.shape)[1:] Jx = np.tile(res_xprime, tile_shape) Jy = np.tile(res_yprime, tile_shape) thetahat = -Jx * np.sin(theta) + Jy * np.cos(theta) return np.sum(vectors * thetahat, axis=0) def get_cyl_z_component(vectors, normal): # The z component of a vector is the vector dotted with zhat normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_zprime = resize_vector(zprime, vectors) tile_shape = [1] + list(vectors.shape)[1:] zhat = np.tile(res_zprime, tile_shape) return np.sum(vectors * zhat, axis=0) def get_sph_r_component(vectors, theta, phi, normal): # The r component of a vector is the vector dotted with rhat normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_xprime = resize_vector(xprime, vectors) res_yprime = resize_vector(yprime, vectors) res_zprime = resize_vector(zprime, vectors) tile_shape = [1] + list(vectors.shape)[1:] Jx, Jy, Jz = ( YTArray(np.tile(rprime, tile_shape), "") for rprime in (res_xprime, res_yprime, res_zprime) ) rhat = ( Jx * np.sin(theta) * np.cos(phi) + Jy * np.sin(theta) * np.sin(phi) + Jz * np.cos(theta) ) return np.sum(vectors * rhat, axis=0) def get_sph_phi_component(vectors, phi, normal): # The phi component of a vector is the vector dotted with phihat normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_xprime = resize_vector(xprime, vectors) res_yprime = resize_vector(yprime, vectors) tile_shape = [1] + list(vectors.shape)[1:] Jx = YTArray(np.tile(res_xprime, tile_shape), "") Jy = YTArray(np.tile(res_yprime, tile_shape), "") phihat = -Jx * np.sin(phi) + Jy * np.cos(phi) return np.sum(vectors * phihat, axis=0) def get_sph_theta_component(vectors, theta, phi, normal): # The theta component of a vector is the vector dotted with thetahat normal = normalize_vector(normal) (zprime, xprime, yprime) = ortho_find(normal) res_xprime = resize_vector(xprime, vectors) res_yprime = resize_vector(yprime, vectors) res_zprime = resize_vector(zprime, vectors) tile_shape = [1] + list(vectors.shape)[1:] Jx, Jy, Jz = ( YTArray(np.tile(rprime, tile_shape), "") for rprime in (res_xprime, res_yprime, res_zprime) ) thetahat = ( Jx * np.cos(theta) * np.cos(phi) + Jy * np.cos(theta) * np.sin(phi) - Jz * np.sin(theta) ) return np.sum(vectors * thetahat, axis=0) def compute_stddev_image(buff2, buff): """ This function computes the standard deviation of a weighted projection. It defines the standard deviation as sigma^2 = -^2, where the brackets indicate averages (with the weight) and v is the field in question. There is the possibility that if the projection at a particular location is of a constant or a single cell/particle, then = ^2 and instead of getting zero one gets roundoff error that results in sigma^2 < 0, which is unphysical. We handle this here by performing the subtraction and checking that any and all negative values of sigma^2 can be attributed to roundoff and thus be safely set to zero. We error out if this is not the case. There are ways of computing the standard deviation that are designed to avoid this catastrophic cancellation, but this would require rather substantial and invasive changes to the projection machinery so for the time being it is avoided. Parameters ---------- buff2 : ImageArray The image of the weighted averge of the field squared buff : ImageArray The image of the weighted averge of the field """ buff1 = buff * buff y = buff2 - buff1 close_negs = np.isclose(np.asarray(buff2), np.asarray(buff1))[y < 0.0] if close_negs.all(): y[y < 0.0] = 0.0 else: raise ValueError( "Something went wrong, there are significant negative " "variances in the standard deviation image!" ) return np.sqrt(y) yt-project-yt-f043ac8/yt/utilities/mesh_code_generation.py000066400000000000000000000133331510711153200240370ustar00rootroot00000000000000import yaml from sympy import Matrix, MatrixSymbol, ccode, diff, symarray # define some templates used below fun_signature = """cdef void %s(double* fx, double* x, double* vertices, double* phys_x) noexcept nogil""" fun_dec_template = fun_signature + " \n" fun_def_template = ( """@cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) \n""" + fun_signature + ": \n" ) jac_signature_3D = """cdef void %s(double* rcol, double* scol, double* tcol, double* x, double* vertices, double* phys_x) noexcept nogil""" jac_dec_template_3D = jac_signature_3D + " \n" jac_def_template_3D = ( """@cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) \n""" + jac_signature_3D + ": \n" ) jac_signature_2D = """cdef void %s(double* rcol, double* scol, double* x, double* vertices, double* phys_x) noexcept nogil""" jac_dec_template_2D = jac_signature_2D + " \n" jac_def_template_2D = ( """@cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) \n""" + jac_signature_2D + ": \n" ) file_header = ( "# This file contains auto-generated functions for sampling \n" + "# inside finite element solutions for various mesh types. \n" + "# To see how the code generation works in detail, see \n" + "# yt/utilities/mesh_code_generation.py. \n" ) class MeshCodeGenerator: """ A class for automatically generating the functions and jacobians used for sampling inside finite element calculations. """ def __init__(self, mesh_data): """ Mesh data should be a dictionary containing information about the type of elements used. See yt/utilities/mesh_types.yaml for more information. """ self.mesh_type = mesh_data["mesh_type"] self.num_dim = mesh_data["num_dim"] self.num_vertices = mesh_data["num_vertices"] self.num_mapped_coords = mesh_data["num_mapped_coords"] x = MatrixSymbol("x", self.num_mapped_coords, 1) self.x = x self.N = Matrix(eval(mesh_data["shape_functions"])) self._compute_jacobian() def _compute_jacobian(self): assert self.num_vertices == len(self.N) assert self.num_dim == self.num_mapped_coords self.X = MatrixSymbol("vertices", self.num_vertices, self.num_dim) self.fx = MatrixSymbol("fx", self.num_dim, 1) self.physical_position = MatrixSymbol("phys_x", self.num_dim, 1) self.f = (self.N.T * Matrix(self.X)).T - self.physical_position self.J = symarray("J", (self.num_dim, self.num_dim)) for i in range(self.num_dim): for j, var in enumerate(self.x): self.J[i][j] = diff(self.f[i, 0], var) self.rcol = MatrixSymbol("rcol", self.num_dim, 1) self.scol = MatrixSymbol("scol", self.num_dim, 1) self.tcol = MatrixSymbol("tcol", self.num_dim, 1) self.function_name = "%sFunction%dD" % (self.mesh_type, self.num_dim) self.function_header = fun_def_template % self.function_name self.function_declaration = fun_dec_template % self.function_name self.jacobian_name = "%sJacobian%dD" % (self.mesh_type, self.num_dim) if self.num_dim == 3: self.jacobian_header = jac_def_template_3D % self.jacobian_name self.jacobian_declaration = jac_dec_template_3D % self.jacobian_name elif self.num_dim == 2: self.jacobian_header = jac_def_template_2D % self.jacobian_name self.jacobian_declaration = jac_dec_template_2D % self.jacobian_name def get_interpolator_definition(self): """ This returns the function definitions for the given mesh type. """ function_code = self.function_header for i in range(self.num_dim): function_code += "\t" + ccode(self.f[i, 0], self.fx[i, 0]) + "\n" jacobian_code = self.jacobian_header for i in range(self.num_dim): jacobian_code += "\t" + ccode(self.J[i, 0], self.rcol[i, 0]) + "\n" jacobian_code += "\t" + ccode(self.J[i, 1], self.scol[i, 0]) + "\n" if self.num_dim == 2: continue jacobian_code += "\t" + ccode(self.J[i, 2], self.tcol[i, 0]) + "\n" return function_code, jacobian_code def get_interpolator_declaration(self): """ This returns the function declarations for the given mesh type. """ return self.function_declaration, self.jacobian_declaration if __name__ == "__main__": with open("mesh_types.yaml") as f: lines = f.read() mesh_types = yaml.load(lines, Loader=yaml.FullLoader) pxd_file = open("lib/autogenerated_element_samplers.pxd", "w") pyx_file = open("lib/autogenerated_element_samplers.pyx", "w") pyx_file.write(file_header) pyx_file.write("\n \n") pyx_file.write("cimport cython \n") pyx_file.write("from libc.math cimport pow \n") pyx_file.write("\n \n") for _, mesh_data in sorted(mesh_types.items()): codegen = MeshCodeGenerator(mesh_data) function_code, jacobian_code = codegen.get_interpolator_definition() function_decl, jacobian_decl = codegen.get_interpolator_declaration() pxd_file.write(function_decl) pxd_file.write("\n \n") pxd_file.write(jacobian_decl) pxd_file.write("\n \n") pyx_file.write(function_code) pyx_file.write("\n \n") pyx_file.write(jacobian_code) pyx_file.write("\n \n") pxd_file.close() pyx_file.close() yt-project-yt-f043ac8/yt/utilities/mesh_types.yaml000066400000000000000000000046231510711153200223720ustar00rootroot00000000000000Hex8: mesh_type: Q1 num_dim: 3 num_vertices: 8 num_mapped_coords: 3 shape_functions: | [(1 - x[0])*(1 - x[1])*(1 - x[2])/8., (1 + x[0])*(1 - x[1])*(1 - x[2])/8., (1 + x[0])*(1 + x[1])*(1 - x[2])/8., (1 - x[0])*(1 + x[1])*(1 - x[2])/8., (1 - x[0])*(1 - x[1])*(1 + x[2])/8., (1 + x[0])*(1 - x[1])*(1 + x[2])/8., (1 + x[0])*(1 + x[1])*(1 + x[2])/8., (1 - x[0])*(1 + x[1])*(1 + x[2])/8.] Quad4: mesh_type: Q1 num_dim: 2 num_vertices: 4 num_mapped_coords: 2 shape_functions: | [(1 - x[0])*(1 - x[1])/4., (1 + x[0])*(1 - x[1])/4., (1 + x[0])*(1 + x[1])/4., (1 - x[0])*(1 + x[1])/4.] Quad9: mesh_type: Q2 num_dim: 2 num_vertices: 9 num_mapped_coords: 2 shape_functions: | [x[0] * (x[0] - 1) * x[1] * (x[1] - 1) / 4., x[0] * (x[0] + 1) * x[1] * (x[1] - 1) / 4., x[0] * (x[0] + 1) * x[1] * (x[1] + 1) / 4., x[0] * (x[0] - 1) * x[1] * (x[1] + 1) / 4., (x[0] + 1) * (x[0] - 1) * x[1] * (x[1] - 1) / -2., x[0] * (x[0] + 1) * (x[1] + 1) * (x[1] - 1) / -2., (x[0] + 1) * (x[0] - 1) * x[1] * (x[1] + 1) / -2., x[0] * (x[0] - 1) * (x[1] + 1) * (x[1] - 1) / -2., (x[0] + 1) * (x[0] - 1) * (x[1] + 1) * (x[1] - 1)] Wedge6: mesh_type: W1 num_dim: 3 num_vertices: 6 num_mapped_coords: 3 shape_functions: | [(1 - x[0] - x[1]) * (1 - x[2]) / 2., x[0] * (1 - x[2]) / 2., x[1] * (1 - x[2]) / 2., (1 - x[0] - x[1]) * (1 + x[2]) / 2., x[0] * (1 + x[2]) / 2., x[1] * (1 + x[2]) / 2.] Tri6: mesh_type: T2 num_dim: 2 num_vertices: 6 num_mapped_coords: 2 shape_functions: | [1 - 3 * x[0] + 2 * x[0]**2 - 3 * x[1] + 2 * x[1]**2 + 4 * x[0] * x[1], -x[0] + 2 * x[0]**2, -x[1] + 2 * x[1]**2, 4 * x[0] - 4 * x[0]**2 - 4 * x[0] * x[1], 4 * x[0] * x[1], 4 * x[1] - 4 * x[1]**2 - 4 * x[0] * x[1]] Tet10: mesh_type: Tet2 num_dim: 3 num_vertices: 10 num_mapped_coords: 3 shape_functions: | [1 - 3 * x[0] + 2 * x[0]**2 - 3 * x[1] + 2 * x[1]**2 - 3 * x[2] + 2 * x[2]**2 + 4 * x[0] * x[1] + 4 * x[0] * x[2] + 4 * x[1] * x[2], -x[0] + 2 * x[0]**2, -x[1] + 2 * x[1]**2, -x[2] + 2 * x[2]**2, 4 * x[0] - 4 * x[0]**2 - 4 * x[0] * x[1] - 4 * x[0] * x[2], 4 * x[0] * x[1], 4 * x[1] - 4 * x[1]**2 - 4 * x[1] * x[0] - 4 * x[1] * x[2], 4 * x[2] - 4 * x[2]**2 - 4 * x[2] * x[0] - 4 * x[2] * x[1], 4 * x[0] * x[2], 4 * x[1] * x[2]] yt-project-yt-f043ac8/yt/utilities/metadata.py000066400000000000000000000015751510711153200214630ustar00rootroot00000000000000from yt.loaders import load DEFAULT_ATTRS = ( "dimensionality", "refine_by", "domain_dimensions", "current_time", "domain_left_edge", "domain_right_edge", "unique_identifier", "current_redshift", "cosmological_simulation", "omega_matter", "omega_lambda", "hubble_constant", "dataset_type", ) def get_metadata(path, full_output=False, attrs=DEFAULT_ATTRS): ds = load(path) metadata = {"filename": path} for a in attrs: v = getattr(ds, a, None) if v is None: continue if hasattr(v, "tolist"): v = v.tolist() metadata[a] = v if full_output: params = {} for p, v in ds.parameters.items(): if hasattr(v, "tolist"): v = v.tolist() params[p] = v metadata["params"] = params ds.close() return metadata yt-project-yt-f043ac8/yt/utilities/minimal_representation.py000066400000000000000000000221321510711153200244430ustar00rootroot00000000000000import abc import json import os from uuid import uuid4 import numpy as np from yt.funcs import compare_dicts, is_sequence from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.on_demand_imports import _h5py as h5py def _sanitize_list(flist): temp = [] for item in flist: if isinstance(item, str): temp.append(item.encode("latin-1")) elif isinstance(item, tuple) and all(isinstance(i, str) for i in item): temp.append(tuple(_sanitize_list(list(item)))) else: temp.append(item) return temp def _serialize_to_h5(g, cdict): for item in cdict: if isinstance(cdict[item], (YTQuantity, YTArray)): g[item] = cdict[item].d g[item].attrs["units"] = str(cdict[item].units) elif isinstance(cdict[item], dict): _serialize_to_h5(g.create_group(item), cdict[item]) elif cdict[item] is None: g[item] = "None" elif isinstance(cdict[item], list): g[item] = _sanitize_list(cdict[item]) elif isinstance(cdict[item], tuple) and all( isinstance(i, str) for i in cdict[item] ): g[item] = tuple(_sanitize_list(cdict[item])) else: g[item] = cdict[item] def _deserialize_from_h5(g, ds): result = {} for item in g: if item == "chunks": continue if "units" in g[item].attrs: if is_sequence(g[item]): result[item] = ds.arr(g[item][:], g[item].attrs["units"]) else: result[item] = ds.quan(g[item][()], g[item].attrs["units"]) elif isinstance(g[item], h5py.Group): result[item] = _deserialize_from_h5(g[item], ds) elif g[item] == "None": result[item] = None else: try: result[item] = g[item][:] # try array except ValueError: result[item] = g[item][()] # fallback to scalar return result class ContainerClass: pass class MinimalRepresentation(metaclass=abc.ABCMeta): def _update_attrs(self, obj, attr_list): for attr in attr_list: setattr(self, attr, getattr(obj, attr, None)) if hasattr(obj, "ds"): self.output_hash = obj.ds._hash() self._ds_mrep = obj.ds._mrep if hasattr(obj, "data_source"): self.data_source_hash = obj.data_source._hash def __init__(self, obj): self._update_attrs(obj, self._attr_list) @abc.abstractmethod def _generate_post(self): pass @property @abc.abstractmethod def _attr_list(self): pass def _return_filtered_object(self, attrs): new_attrs = tuple(attr for attr in self._attr_list if attr not in attrs) new_class = type( f"Filtered{self.__class__.__name__}", (FilteredRepresentation,), {"_attr_list": new_attrs}, ) return new_class(self) @property def _attrs(self): return {attr: getattr(self, attr) for attr in self._attr_list} @classmethod def _from_metadata(cls, metadata): cc = ContainerClass() for a, v in metadata.values(): setattr(cc, a, v) return cls(cc) def store(self, storage): if hasattr(self, "_ds_mrep"): self._ds_mrep.store(storage) metadata, (final_name, chunks) = self._generate_post() metadata["obj_type"] = self.type with h5py.File(storage, mode="r") as h5f: dset = str(uuid4())[:8] h5f.create_group(dset) _serialize_to_h5(h5f[dset], metadata) if len(chunks) > 0: g = h5f[dset].create_group("chunks") g.attrs["final_name"] = final_name for fname, fdata in chunks: if isinstance(fname, (tuple, list)): fname = "*".join(fname) if isinstance(fdata, (YTQuantity, YTArray)): g.create_dataset(fname, data=fdata.d, compression="lzf") g[fname].attrs["units"] = str(fdata.units) else: g.create_dataset(fname, data=fdata, compression="lzf") def restore(self, storage, ds): # noqa: B027 pass def upload(self): raise NotImplementedError("This method hasn't been ported to python 3") def load(self, storage): raise NotImplementedError("This method hasn't been ported to python 3") def dump(self, storage): raise NotImplementedError("This method hasn't been ported to python 3") class FilteredRepresentation(MinimalRepresentation): def _generate_post(self): raise RuntimeError class MinimalDataset(MinimalRepresentation): _attr_list = ( "dimensionality", "refine_by", "domain_dimensions", "current_time", "domain_left_edge", "domain_right_edge", "unique_identifier", "current_redshift", "output_hash", "cosmological_simulation", "omega_matter", "omega_lambda", "hubble_constant", "name", ) type = "simulation_output" def __init__(self, obj): super().__init__(obj) self.output_hash = obj._hash() self.name = str(obj) def _generate_post(self): metadata = self._attrs chunks = [] return (metadata, (None, chunks)) class MinimalMappableData(MinimalRepresentation): _attr_list: tuple[str, ...] = ( "field_data", "field", "weight_field", "axis", "output_hash", "vm_type", ) def _generate_post(self): nobj = self._return_filtered_object(("field_data",)) metadata = nobj._attrs chunks = [(arr, self.field_data[arr]) for arr in self.field_data] return (metadata, ("field_data", chunks)) def _read_chunks(self, g, ds): for fname in g.keys(): if "*" in fname: arr = tuple(fname.split("*")) else: arr = fname try: self.field_data[arr] = ds.arr(g[fname][:], g[fname].attrs["units"]) except KeyError: self.field_data[arr] = g[fname][:] class MinimalProjectionData(MinimalMappableData): type = "proj" vm_type = "Projection" _attr_list = ( "field_data", "field", "weight_field", "axis", "output_hash", "center", "method", "field_parameters", "data_source_hash", ) def restore(self, storage, ds): if hasattr(self, "_ds_mrep"): self._ds_mrep.restore(storage, ds) metadata, (final_name, chunks) = self._generate_post() with h5py.File(storage, mode="r") as h5f: for dset in h5f: stored_metadata = _deserialize_from_h5(h5f[dset], ds) if compare_dicts(metadata, stored_metadata): self._read_chunks(h5f[dset]["chunks"], ds) return True return False class MinimalSliceData(MinimalMappableData): type = "slice" vm_type = "Slice" weight_field = "None" class MinimalImageCollectionData(MinimalRepresentation): type = "image_collection" _attr_list = ("name", "output_hash", "images", "image_metadata") def _generate_post(self): nobj = self._return_filtered_object(("images",)) metadata = nobj._attrs chunks = list(self.images) return (metadata, ("images", chunks)) _hub_categories = ( "News", "Documents", "Simulation Management", "Data Management", "Analysis and Visualization", "Paper Repositories", "Astrophysical Utilities", "yt Scripts", ) class MinimalProjectDescription(MinimalRepresentation): type = "project" _attr_list = ("title", "url", "description", "category", "image_url") def __init__(self, title, url, description, category, image_url=""): assert category in _hub_categories self.title = title self.url = url self.description = description self.category = category self.image_url = image_url def _generate_post(self): metadata = self._attrs return (metadata, ("chunks", [])) class MinimalNotebook(MinimalRepresentation): type = "notebook" _attr_list = ("title",) def __init__(self, filename, title=None): # First we read in the data if not os.path.isfile(filename): raise OSError(filename) self.data = open(filename).read() if title is None: title = json.loads(self.data)["metadata"]["name"] self.title = title self.data = np.fromstring(self.data, dtype="c") def _generate_post(self): metadata = self._attrs chunks = [("notebook", self.data)] return (metadata, ("chunks", chunks)) class ImageCollection: def __init__(self, ds, name): self.ds = ds self.name = name self.images = [] self.image_metadata = [] def add_image(self, fn, descr): self.image_metadata.append(descr) self.images.append((os.path.basename(fn), np.fromfile(fn, dtype="c"))) yt-project-yt-f043ac8/yt/utilities/nodal_data_utils.py000066400000000000000000000026231510711153200232040ustar00rootroot00000000000000import numpy as np _index_map = np.array( [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 1, 0, 1, 0, 1], [0, 0, 1, 1, 0, 0, 1, 1], [0, 1, 2, 3, 0, 1, 2, 3], [0, 0, 0, 0, 1, 1, 1, 1], [0, 1, 0, 1, 2, 3, 2, 3], [0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 3, 4, 5, 6, 7], ] ) def _get_linear_index(nodal_flag): if len(nodal_flag) == 2: return 1 * nodal_flag[1] + 2 * nodal_flag[0] else: return 1 * nodal_flag[2] + 2 * nodal_flag[1] + 4 * nodal_flag[0] def _get_indices(nodal_flag): li = _get_linear_index(nodal_flag) return _index_map[li] def get_nodal_data(data_source, field): finfo = data_source.ds._get_field_info(field) nodal_flag = finfo.nodal_flag field_data = data_source[field] inds = _get_indices(nodal_flag) return field_data[:, inds] def get_nodal_slices(shape, nodal_flag, dim): slices = [] dir_slices = [[] for _ in range(dim)] for i in range(dim): if nodal_flag[i]: dir_slices[i] = [slice(0, shape[i] - 1), slice(1, shape[i])] else: dir_slices[i] = [slice(0, shape[i])] for sl_i in dir_slices[0]: for sl_j in dir_slices[1]: if dim > 2: for sl_k in dir_slices[2]: slices.append([sl_i, sl_j, sl_k]) else: slices.append([sl_i, sl_j]) return tuple(slices) yt-project-yt-f043ac8/yt/utilities/object_registries.py000066400000000000000000000017151510711153200234050ustar00rootroot00000000000000# These are some of the data object registries that are used in different places in the # code. Not all of the self-registering objects are included in these. from typing import TYPE_CHECKING if TYPE_CHECKING: from yt.data_objects.analyzer_objects import AnalysisTask from yt.data_objects.data_containers import YTDataContainer from yt.data_objects.derived_quantities import DerivedQuantity from yt.data_objects.static_output import Dataset from yt.data_objects.time_series import DatasetSeries from yt.visualization.volume_rendering.old_camera import Camera analysis_task_registry: dict[str, type["AnalysisTask"]] = {} derived_quantity_registry: dict[str, type["DerivedQuantity"]] = {} output_type_registry: dict[str, type["Dataset"]] = {} simulation_time_series_registry: dict[str, type["DatasetSeries"]] = {} # TODO: split into 2 registries to avoid a typing.Union data_object_registry: dict[str, "type[YTDataContainer] | type[Camera]"] = {} yt-project-yt-f043ac8/yt/utilities/on_demand_imports.py000066400000000000000000000251501510711153200233770ustar00rootroot00000000000000import sys from functools import wraps from importlib.util import find_spec class NotAModule: """ A class to implement an informative error message that will be outputted if someone tries to use an on-demand import without having the requisite package installed. """ def __init__(self, pkg_name, exc: BaseException | None = None): self.pkg_name = pkg_name self._original_exception = exc error_note = ( f"Something went wrong while trying to lazy-import {pkg_name}. " f"Please make sure that {pkg_name} is properly installed.\n" "If the problem persists, please file an issue at " "https://github.com/yt-project/yt/issues/new" ) self.error: BaseException if exc is None: self.error = ImportError(error_note) elif sys.version_info >= (3, 11): exc.add_note(error_note) self.error = exc else: # mimic Python 3.11 behaviour: # preserve error message and traceback self.error = type(exc)(f"{exc!s}\n{error_note}").with_traceback( exc.__traceback__ ) def __getattr__(self, item): raise self.error def __call__(self, *args, **kwargs): raise self.error def __repr__(self) -> str: if self._original_exception is None: return f"NotAModule({self.pkg_name!r})" else: return f"NotAModule({self.pkg_name!r}, {self._original_exception!r})" class OnDemand: _default_factory: type[NotAModule] = NotAModule def __init_subclass__(cls): if not cls.__name__.endswith("_imports"): raise TypeError(f"class {cls}'s name needs to be suffixed '_imports'") def __new__(cls): if cls is OnDemand: raise TypeError("The OnDemand base class cannot be instantiated.") else: return object.__new__(cls) @property def _name(self) -> str: _name, _, _suffix = self.__class__.__name__.rpartition("_") return _name @property def __is_available__(self) -> bool: # special protocol to support testing framework return find_spec(self._name) is not None def safe_import(func): @property @wraps(func) def inner(self): try: return func(self) except ImportError as exc: return self._default_factory(self._name, exc) return inner class netCDF4_imports(OnDemand): @safe_import def Dataset(self): from netCDF4 import Dataset return Dataset _netCDF4 = netCDF4_imports() class astropy_imports(OnDemand): @safe_import def log(self): from astropy import log if log.exception_logging_enabled(): log.disable_exception_logging() return log @safe_import def pyfits(self): from astropy.io import fits return fits @safe_import def pywcs(self): import astropy.wcs as pywcs self.log return pywcs @safe_import def units(self): from astropy import units self.log return units @safe_import def conv(self): import astropy.convolution as conv self.log return conv @safe_import def time(self): import astropy.time as time self.log return time @safe_import def wcsaxes(self): from astropy.visualization import wcsaxes self.log return wcsaxes @safe_import def WCS(self): from astropy.wcs import WCS self.log return WCS _astropy = astropy_imports() class regions_imports(OnDemand): @safe_import def Regions(self): from regions import Regions return Regions _regions = regions_imports() class NotCartopy(NotAModule): """ A custom class to return error messages dependent on system installation for cartopy imports. """ def __init__(self, pkg_name, exc: BaseException | None = None): super().__init__(pkg_name, exc) if any(s in sys.version for s in ("Anaconda", "Continuum")): # the conda-based installs of cartopy don't have issues with the # GEOS library, so the error message for users with conda can be # relatively short. Discussion related to this is in # yt-project/yt#1966 self.error = ImportError( f"This functionality requires the {self.pkg_name} " "package to be installed." ) else: self.error = ImportError( f"This functionality requires the {pkg_name} " "package to be installed.\n" "For further instruction please refer to Cartopy's documentation\n" "https://cartopy.readthedocs.io/stable/installing.html" ) class cartopy_imports(OnDemand): _default_factory = NotCartopy @safe_import def crs(self): import cartopy.crs as crs return crs _cartopy = cartopy_imports() class pooch_imports(OnDemand): @safe_import def HTTPDownloader(self): from pooch import HTTPDownloader return HTTPDownloader @safe_import def utils(self): from pooch import utils return utils @safe_import def create(self): from pooch import create return create _pooch = pooch_imports() class pyart_imports(OnDemand): @safe_import def io(self): from pyart import io return io @safe_import def map(self): from pyart import map return map _pyart = pyart_imports() class xarray_imports(OnDemand): @safe_import def open_dataset(self): from xarray import open_dataset return open_dataset _xarray = xarray_imports() class scipy_imports(OnDemand): @safe_import def signal(self): from scipy import signal return signal @safe_import def spatial(self): from scipy import spatial return spatial @safe_import def ndimage(self): from scipy import ndimage return ndimage # Optimize is not presently used by yt, but appears here to enable # required functionality in yt extension, trident @safe_import def optimize(self): from scipy import optimize return optimize _scipy = scipy_imports() class h5py_imports(OnDemand): def __init__(self): # this ensures the import ordering between netcdf4 and h5py. If h5py is # imported first, can get file lock errors on some systems (including travis-ci) # so we need to do this before initializing h5py_imports()! # similar to this issue https://github.com/pydata/xarray/issues/2560 if find_spec("h5py") is None or find_spec("netCDF4") is None: return try: import netCDF4 # noqa F401 except ImportError: pass @safe_import def File(self): from h5py import File return File @safe_import def Group(self): from h5py import Group return Group @safe_import def Dataset(self): from h5py import Dataset return Dataset @safe_import def get_config(self): from h5py import get_config return get_config @safe_import def h5f(self): from h5py import h5f return h5f @safe_import def h5p(self): from h5py import h5p return h5p @safe_import def h5d(self): from h5py import h5d return h5d @safe_import def h5s(self): from h5py import h5s return h5s _h5py = h5py_imports() class nose_imports(OnDemand): @safe_import def run(self): from nose import run return run _nose = nose_imports() class libconf_imports(OnDemand): @safe_import def load(self): from libconf import load return load _libconf = libconf_imports() class yaml_imports(OnDemand): @safe_import def load(self): from yaml import load return load @safe_import def FullLoader(self): from yaml import FullLoader return FullLoader _yaml = yaml_imports() class NotMiniball(NotAModule): def __init__(self, pkg_name): super().__init__(pkg_name) str = ( "This functionality requires the %s package to be installed. " "Installation instructions can be found at " "https://github.com/weddige/miniball or alternatively you can " "install via `python -m pip install MiniballCpp`." ) self.error = ImportError(str % self.pkg_name) class miniball_imports(OnDemand): @safe_import def Miniball(self): from miniball import Miniball return Miniball _miniball = miniball_imports() class f90nml_imports(OnDemand): @safe_import def read(self): from f90nml import read return read @safe_import def Namelist(self): from f90nml import Namelist return Namelist _f90nml = f90nml_imports() class requests_imports(OnDemand): @safe_import def post(self): from requests import post return post @safe_import def put(self): from requests import put return put @safe_import def codes(self): from requests import codes return codes @safe_import def get(self): from requests import get return get @safe_import def exceptions(self): from requests import exceptions return exceptions _requests = requests_imports() class pandas_imports(OnDemand): @safe_import def NA(self): from pandas import NA return NA @safe_import def DataFrame(self): from pandas import DataFrame return DataFrame @safe_import def concat(self): from pandas import concat return concat @safe_import def read_csv(self): from pandas import read_csv return read_csv _pandas = pandas_imports() class firefly_imports(OnDemand): @safe_import def data_reader(self): import firefly.data_reader as data_reader return data_reader @safe_import def server(self): import firefly.server as server return server _firefly = firefly_imports() # Note: ratarmount may fail with an OSError on import if libfuse is missing class ratarmount_imports(OnDemand): @safe_import def TarMount(self): from ratarmount import TarMount return TarMount @safe_import def fuse(self): from ratarmount import fuse return fuse _ratarmount = ratarmount_imports() yt-project-yt-f043ac8/yt/utilities/operator_registry.py000066400000000000000000000005101510711153200234520ustar00rootroot00000000000000import copy from collections import UserDict class OperatorRegistry(UserDict): def find(self, op, *args, **kwargs): if isinstance(op, str): # Lookup, assuming string or hashable object op = copy.deepcopy(self[op]) op.args = args op.kwargs = kwargs return op yt-project-yt-f043ac8/yt/utilities/orientation.py000066400000000000000000000070341510711153200222320ustar00rootroot00000000000000import numpy as np from yt.units.yt_array import YTArray from yt.utilities.exceptions import YTException def _aligned(a, b): aligned_component = np.abs(np.dot(a, b) / np.linalg.norm(a) / np.linalg.norm(b)) return np.isclose(aligned_component, 1.0, 1.0e-13) def _validate_unit_vectors(normal_vector, north_vector): # Make sure vectors are unitless if north_vector is not None: north_vector = YTArray(north_vector, "", dtype="float64") if normal_vector is not None: normal_vector = YTArray(normal_vector, "", dtype="float64") if not np.dot(normal_vector, normal_vector) > 0: raise YTException("normal_vector cannot be the zero vector.") if north_vector is not None and _aligned(north_vector, normal_vector): raise YTException("normal_vector and north_vector cannot be aligned.") return normal_vector, north_vector class Orientation: def __init__(self, normal_vector, north_vector=None, steady_north=False): r"""An object that returns a set of basis vectors for orienting cameras a data containers. Parameters ---------- normal_vector : array_like A vector normal to the image plane north_vector : array_like, optional The 'up' direction to orient the image plane. If not specified, gets calculated automatically steady_north : bool, optional Boolean to control whether to normalize the north_vector by subtracting off the dot product of it and the normal vector. Makes it easier to do rotations along a single axis. If north_vector is specified, is switched to True. Default: False """ normal_vector, north_vector = _validate_unit_vectors( normal_vector, north_vector ) self.steady_north = steady_north if north_vector is not None: self.steady_north = True self.north_vector = north_vector self._setup_normalized_vectors(normal_vector, north_vector) if self.north_vector is None: self.north_vector = self.unit_vectors[1] def _setup_normalized_vectors(self, normal_vector, north_vector): normal_vector, north_vector = _validate_unit_vectors( normal_vector, north_vector ) # Now we set up our various vectors normal_vector /= np.sqrt(np.dot(normal_vector, normal_vector)) if north_vector is None: vecs = np.identity(3) t = np.cross(normal_vector, vecs).sum(axis=1) ax = t.argmax() east_vector = np.cross(vecs[ax, :], normal_vector).ravel() # self.north_vector must remain None otherwise rotations about a fixed axis # will break. The north_vector calculated here will still be included # in self.unit_vectors. north_vector = np.cross(normal_vector, east_vector).ravel() else: if self.steady_north or (np.dot(north_vector, normal_vector) != 0.0): north_vector = ( north_vector - np.dot(north_vector, normal_vector) * normal_vector ) east_vector = np.cross(north_vector, normal_vector).ravel() north_vector /= np.sqrt(np.dot(north_vector, north_vector)) east_vector /= np.sqrt(np.dot(east_vector, east_vector)) self.normal_vector = normal_vector self.north_vector = north_vector self.unit_vectors = YTArray([east_vector, north_vector, normal_vector], "") self.inv_mat = np.linalg.pinv(self.unit_vectors) yt-project-yt-f043ac8/yt/utilities/parallel_tools/000077500000000000000000000000001510711153200223355ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/parallel_tools/__init__.py000066400000000000000000000000421510711153200244420ustar00rootroot00000000000000""" Tools for parallelism. """ yt-project-yt-f043ac8/yt/utilities/parallel_tools/controller_system.py000066400000000000000000000020131510711153200264720ustar00rootroot00000000000000from abc import abstractmethod from .parallel_analysis_interface import ProcessorPool class WorkSplitter: def __init__(self, controller, group1, group2): self.group1 = group1 self.group2 = group2 self.controller = controller @classmethod def setup(cls, ng1, ng2): pp, wg = ProcessorPool.from_sizes( [(1, "controller"), (ng1, "group1"), (ng2, "group2")] ) groupc = pp["controller"] group1 = pp["group1"] group2 = pp["group2"] obj = cls(groupc, group1, group2) obj.run(wg.name) def run(self, name): if name == "controller": self.run_controller() elif name == "group1": self.run_group1() elif name == "group2": self.run_group2() else: raise NotImplementedError @abstractmethod def run_controller(self): pass @abstractmethod def run_group1(self): pass @abstractmethod def run_group2(self): pass yt-project-yt-f043ac8/yt/utilities/parallel_tools/io_runner.py000066400000000000000000000137521510711153200247170ustar00rootroot00000000000000import time from contextlib import contextmanager import numpy as np from yt.utilities.io_handler import BaseIOHandler from yt.utilities.logger import ytLogger as mylog from .parallel_analysis_interface import ProcessorPool, parallel_objects try: from .parallel_analysis_interface import MPI except ImportError: pass YT_TAG_MESSAGE = 317 # Cell 317 knows where to go class IOCommunicator(BaseIOHandler): def __init__(self, ds, wg, pool): mylog.info("Initializing IOCommunicator") self.ds = ds self.wg = wg # We don't need to use this! self.pool = pool self.comm = pool.comm # We read our grids here self.grids = [] storage = {} grids = ds.index.grids.tolist() grids.sort(key=lambda a: a.filename) for sto, g in parallel_objects(grids, storage=storage): sto.result = self.comm.rank sto.result_id = g.id self.grids.append(g) self._id_offset = ds.index.grids[0]._id_offset mylog.info("Reading from disk ...") self.initialize_data() mylog.info("Broadcasting ...") self.comm.comm.bcast(storage, root=wg.ranks[0]) mylog.info("Done.") self.hooks = [] def initialize_data(self): ds = self.ds fields = [ f for f in ds.field_list if not ds.field_info[f].sampling_type == "particle" ] pfields = [ f for f in ds.field_list if ds.field_info[f].sampling_type == "particle" ] # Preload is only defined for Enzo ... if ds.index.io._dataset_type == "enzo_packed_3d": self.queue = ds.index.io.queue ds.index.io.preload(self.grids, fields) for g in self.grids: for f in fields: if f not in self.queue[g.id]: d = np.zeros(g.ActiveDimensions, dtype="float64") self.queue[g.id][f] = d for f in pfields: self.queue[g.id][f] = self._read(g, f) else: self.queue = {} for g in self.grids: for f in fields + pfields: self.queue[g.id][f] = ds.index.io._read(g, f) def _read(self, g, f): fi = self.ds.field_info[f] if fi.sampling_type == "particle" and g.NumberOfParticles == 0: # because this gets upcast to float return np.array([], dtype="float64") try: temp = self.ds.index.io._read_data_set(g, f) except Exception: # self.ds.index.io._read_exception as exc: if fi.not_in_all: temp = np.zeros(g.ActiveDimensions, dtype="float64") else: raise return temp def wait(self): status = MPI.Status() while True: if self.comm.comm.Iprobe(MPI.ANY_SOURCE, YT_TAG_MESSAGE, status=status): msg = self.comm.comm.recv(source=status.source, tag=YT_TAG_MESSAGE) if msg["op"] == "end": mylog.debug("Shutting down IO.") break self._send_data(msg, status.source) status = MPI.Status() else: time.sleep(1e-2) def _send_data(self, msg, dest): grid_id = msg["grid_id"] field = msg["field"] ts = self.queue[grid_id][field].astype("float64") mylog.debug("Opening send to %s (%s)", dest, ts.shape) self.hooks.append(self.comm.comm.Isend([ts, MPI.DOUBLE], dest=dest)) class IOHandlerRemote(BaseIOHandler): _dataset_type = "remote" def __init__(self, ds, wg, pool): self.ds = ds self.wg = wg # probably won't need self.pool = pool self.comm = pool.comm self.proc_map = self.comm.comm.bcast(None, root=pool["io"].ranks[0]) super().__init__() def _read_data_set(self, grid, field): dest = self.proc_map[grid.id] msg = {"grid_id": grid.id, "field": field, "op": "read"} mylog.debug("Requesting %s for %s from %s", field, grid, dest) if self.ds.field_info[field].sampling_type == "particle": data = np.empty(grid.NumberOfParticles, "float64") else: data = np.empty(grid.ActiveDimensions, "float64") hook = self.comm.comm.Irecv([data, MPI.DOUBLE], source=dest) self.comm.comm.send(msg, dest=dest, tag=YT_TAG_MESSAGE) mylog.debug("Waiting for data.") MPI.Request.Wait(hook) return data def _read_data_slice(self, grid, field, axis, coord): sl = [slice(None), slice(None), slice(None)] sl[axis] = slice(coord, coord + 1) # sl = tuple(reversed(sl)) return self._read_data_set(grid, field)[tuple(sl)] def terminate(self): msg = {"op": "end"} if self.wg.comm.rank == 0: for rank in self.pool["io"].ranks: mylog.debug("Sending termination message to %s", rank) self.comm.comm.send(msg, dest=rank, tag=YT_TAG_MESSAGE) @contextmanager def remote_io(ds, wg, pool): original_io = ds.index.io ds.index.io = IOHandlerRemote(ds, wg, pool) yield ds.index.io.terminate() ds.index.io = original_io def io_nodes(fn, n_io, n_work, func, *args, **kwargs): from yt.loaders import load pool, wg = ProcessorPool.from_sizes([(n_io, "io"), (n_work, "work")]) rv = None if wg.name == "work": ds = load(fn) with remote_io(ds, wg, pool): rv = func(ds, *args, **kwargs) elif wg.name == "io": ds = load(fn) io = IOCommunicator(ds, wg, pool) io.wait() # We should broadcast the result rv = pool.comm.mpi_bcast(rv, root=pool["work"].ranks[0]) pool.free_all() mylog.debug("Return value: %s", rv) return rv # Here is an example of how to use this functionality. if __name__ == "__main__": def gq(ds): dd = ds.all_data() return dd.quantities["TotalQuantity"](("gas", "cell_mass")) q = io_nodes("DD0087/DD0087", 8, 24, gq) mylog.info(q) yt-project-yt-f043ac8/yt/utilities/parallel_tools/parallel_analysis_interface.py000066400000000000000000001411051510711153200304300ustar00rootroot00000000000000import itertools import logging import os import sys import traceback from functools import wraps from io import StringIO import numpy as np from more_itertools import always_iterable import yt.utilities.logger from yt.config import ytcfg from yt.data_objects.image_array import ImageArray from yt.funcs import is_sequence from yt.units.unit_registry import UnitRegistry # type: ignore from yt.units.yt_array import YTArray from yt.utilities.exceptions import YTNoDataInObjectError from yt.utilities.lib.quad_tree import QuadTree, merge_quadtrees from yt.utilities.logger import ytLogger as mylog # We default to *no* parallelism unless it gets turned on, in which case this # will be changed. MPI = None parallel_capable = False dtype_names = { "float32": "MPI.FLOAT", "float64": "MPI.DOUBLE", "int32": "MPI.INT", "int64": "MPI.LONG", "c": "MPI.CHAR", } op_names = {"sum": "MPI.SUM", "min": "MPI.MIN", "max": "MPI.MAX"} class FilterAllMessages(logging.Filter): """ This is a simple filter for logging.Logger's that won't let any messages pass. """ def filter(self, record): return 0 # Set up translation table and import things def traceback_writer_hook(file_suffix=""): def write_to_file(exc_type, exc, tb): sys.__excepthook__(exc_type, exc, tb) fn = f"yt_traceback{file_suffix}" with open(fn, "w") as fhandle: traceback.print_exception(exc_type, exc, tb, file=fhandle) print(f"Wrote traceback to {fn}") MPI.COMM_WORLD.Abort(1) return write_to_file def default_mpi_excepthook(exception_type, exception_value, tb): traceback.print_tb(tb) mylog.error("%s: %s", exception_type.__name__, exception_value) comm = yt.communication_system.communicators[-1] if comm.size > 1: mylog.error("Error occurred on rank %d.", comm.rank) MPI.COMM_WORLD.Abort(1) def enable_parallelism(suppress_logging: bool = False, communicator=None) -> bool: """ This method is used inside a script to turn on MPI parallelism, via mpi4py. More information about running yt in parallel can be found here: https://yt-project.org/docs/3.0/analyzing/parallel_computation.html Parameters ---------- suppress_logging : bool If set to True, only rank 0 will log information after the initial setup of MPI. communicator : mpi4py.MPI.Comm The MPI communicator to use. This controls which processes yt can see. If not specified, will be set to COMM_WORLD. Returns ------- parallel_capable: bool True if the call was successful. False otherwise. """ global parallel_capable, MPI try: from mpi4py import MPI as _MPI except ImportError: mylog.error("Could not enable parallelism: mpi4py is not installed") return False MPI = _MPI exe_name = os.path.basename(sys.executable) # if no communicator specified, set to COMM_WORLD if communicator is None: communicator = MPI.COMM_WORLD.Dup() else: communicator = communicator.Dup() parallel_capable = communicator.size > 1 if not parallel_capable: mylog.error( "Could not enable parallelism: only one mpi process is running. " "To remedy this, launch the Python interpreter as\n" " mpirun -n python3 .py # with X > 1 ", ) return False mylog.info( "Global parallel computation enabled: %s / %s", communicator.rank, communicator.size, ) communication_system.push(communicator) ytcfg["yt", "internals", "global_parallel_rank"] = communicator.rank ytcfg["yt", "internals", "global_parallel_size"] = communicator.size ytcfg["yt", "internals", "parallel"] = True if exe_name == "embed_enzo" or ("_parallel" in dir(sys) and sys._parallel): # type: ignore ytcfg["yt", "inline"] = True yt.utilities.logger.uncolorize_logging() # Even though the uncolorize function already resets the format string, # we reset it again so that it includes the processor. f = logging.Formatter( "P%03i %s" % (communicator.rank, yt.utilities.logger.ufstring) ) if len(yt.utilities.logger.ytLogger.handlers) > 0: yt.utilities.logger.ytLogger.handlers[0].setFormatter(f) if ytcfg.get("yt", "parallel_traceback"): sys.excepthook = traceback_writer_hook("_%03i" % communicator.rank) else: sys.excepthook = default_mpi_excepthook if ytcfg.get("yt", "log_level") < 20: yt.utilities.logger.ytLogger.warning( "Log Level is set low -- this could affect parallel performance!" ) dtype_names.update( { "float32": MPI.FLOAT, "float64": MPI.DOUBLE, "int32": MPI.INT, "int64": MPI.LONG, "c": MPI.CHAR, } ) op_names.update({"sum": MPI.SUM, "min": MPI.MIN, "max": MPI.MAX}) # Turn off logging on all but the root rank, if specified. if suppress_logging: if communicator.rank > 0: mylog.addFilter(FilterAllMessages()) return True # Because the dtypes will == correctly but do not hash the same, we need this # function for dictionary access. def get_mpi_type(dtype): for dt, val in dtype_names.items(): if dt == dtype: return val class ObjectIterator: """ This is a generalized class that accepts a list of objects and then attempts to intelligently iterate over them. """ def __init__(self, pobj, just_list=False, attr="_grids"): self.pobj = pobj if hasattr(pobj, attr) and getattr(pobj, attr) is not None: gs = getattr(pobj, attr) else: gs = getattr(pobj._data_source, attr) if len(gs) == 0: raise YTNoDataInObjectError(pobj) if hasattr(gs[0], "proc_num"): # This one sort of knows about MPI, but not quite self._objs = [ g for g in gs if g.proc_num == ytcfg.get("yt", "internals", "topcomm_parallel_rank") ] self._use_all = True else: self._objs = gs if hasattr(self._objs[0], "filename"): self._objs = sorted(self._objs, key=lambda g: g.filename) self._use_all = False self.ng = len(self._objs) self.just_list = just_list def __iter__(self): yield from self._objs class ParallelObjectIterator(ObjectIterator): """ This takes an object, *pobj*, that implements ParallelAnalysisInterface, and then does its thing, calling initialize and finalize on the object. """ def __init__(self, pobj, just_list=False, attr="_grids", round_robin=False): ObjectIterator.__init__(self, pobj, just_list, attr=attr) # pobj has to be a ParallelAnalysisInterface, so it must have a .comm # object. self._offset = pobj.comm.rank self._skip = pobj.comm.size # Note that we're doing this in advance, and with a simple means # of choosing them; more advanced methods will be explored later. if self._use_all: self.my_obj_ids = np.arange(len(self._objs)) else: if not round_robin: self.my_obj_ids = np.array_split( np.arange(len(self._objs)), self._skip )[self._offset] else: self.my_obj_ids = np.arange(len(self._objs))[self._offset :: self._skip] def __iter__(self): for gid in self.my_obj_ids: yield self._objs[gid] if not self.just_list: self.pobj._finalize_parallel() def parallel_simple_proxy(func): """ This is a decorator that broadcasts the result of computation on a single processor to all other processors. To do so, it uses the _processing and _distributed flags in the object to check for blocks. Meant only to be used on objects that subclass :class:`~yt.utilities.parallel_tools.parallel_analysis_interface.ParallelAnalysisInterface`. """ @wraps(func) def single_proc_results(self, *args, **kwargs): retval = None if hasattr(self, "dont_wrap"): if func.__name__ in self.dont_wrap: return func(self, *args, **kwargs) if not parallel_capable or self._processing or not self._distributed: return func(self, *args, **kwargs) comm = _get_comm((self,)) if self._owner == comm.rank: self._processing = True retval = func(self, *args, **kwargs) self._processing = False # To be sure we utilize the root= kwarg, we manually access the .comm # attribute, which must be an instance of MPI.Intracomm, and call bcast # on that. retval = comm.comm.bcast(retval, root=self._owner) return retval return single_proc_results class ParallelDummy(type): """ This is a base class that, on instantiation, replaces all attributes that don't start with ``_`` with :func:`~yt.utilities.parallel_tools.parallel_analysis_interface.parallel_simple_proxy`-wrapped attributes. Used as a metaclass. """ def __init__(cls, name, bases, d): super().__init__(name, bases, d) skip = d.pop("dont_wrap", []) extra = d.pop("extra_wrap", []) for attrname in d: if attrname.startswith("_") or attrname in skip: if attrname not in extra: continue attr = getattr(cls, attrname) if callable(attr): setattr(cls, attrname, parallel_simple_proxy(attr)) def parallel_passthrough(func): """ If we are not run in parallel, this function passes the input back as output; otherwise, the function gets called. Used as a decorator. """ @wraps(func) def passage(self, *args, **kwargs): if not self._distributed: return args[0] return func(self, *args, **kwargs) return passage def _get_comm(args): if len(args) > 0 and hasattr(args[0], "comm"): comm = args[0].comm else: comm = communication_system.communicators[-1] return comm def parallel_blocking_call(func): """ This decorator blocks on entry and exit of a function. """ @wraps(func) def barrierize(*args, **kwargs): if not parallel_capable: return func(*args, **kwargs) mylog.debug("Entering barrier before %s", func.__name__) comm = _get_comm(args) comm.barrier() retval = func(*args, **kwargs) mylog.debug("Entering barrier after %s", func.__name__) comm.barrier() return retval return barrierize def parallel_root_only_then_broadcast(func): """ This decorator blocks and calls the function on the root processor and then broadcasts the results to the other processors. """ @wraps(func) def root_only(*args, **kwargs): if not parallel_capable: return func(*args, **kwargs) comm = _get_comm(args) rv0 = None if comm.rank == 0: try: rv0 = func(*args, **kwargs) except Exception: traceback.print_last() rv = comm.mpi_bcast(rv0) if not rv: raise RuntimeError return rv return root_only def parallel_root_only(func): """ This decorator blocks and calls the function on the root processor, but does not broadcast results to the other processors. """ @wraps(func) def root_only(*args, **kwargs): if not parallel_capable: return func(*args, **kwargs) comm = _get_comm(args) rv = None if comm.rank == 0: try: rv = func(*args, **kwargs) all_clear = 1 except Exception: traceback.print_last() all_clear = 0 else: all_clear = None all_clear = comm.mpi_bcast(all_clear) if not all_clear: raise RuntimeError return rv return root_only class Workgroup: def __init__(self, size, ranks, comm, name): self.size = size self.ranks = ranks self.comm = comm self.name = name class ProcessorPool: comm = None size = None ranks = None available_ranks = None tasks = None def __init__(self): self.comm = communication_system.communicators[-1] self.size = self.comm.size self.ranks = list(range(self.size)) self.available_ranks = list(range(self.size)) self.workgroups = [] def add_workgroup(self, size=None, ranks=None, name=None): if size is None: size = len(self.available_ranks) if len(self.available_ranks) < size: mylog.error( "Not enough resources available, asked for %d have %d", size, self.available_ranks, ) raise RuntimeError if ranks is None: ranks = [self.available_ranks.pop(0) for i in range(size)] # Default name to the workgroup number. if name is None: name = str(len(self.workgroups)) group = self.comm.comm.Get_group().Incl(ranks) new_comm = self.comm.comm.Create(group) if self.comm.rank in ranks: communication_system.communicators.append(Communicator(new_comm)) self.workgroups.append(Workgroup(len(ranks), ranks, new_comm, name)) def free_workgroup(self, workgroup): # If you want to actually delete the workgroup you will need to # pop it out of the self.workgroups list so you don't have references # that are left dangling, e.g. see free_all() below. for i in workgroup.ranks: if self.comm.rank == i: communication_system.communicators.pop() self.available_ranks.append(i) self.available_ranks.sort() def free_all(self): for wg in self.workgroups: self.free_workgroup(wg) while self.workgroups: self.workgroups.pop(0) @classmethod def from_sizes(cls, sizes): pool = cls() rank = pool.comm.rank for i, size in enumerate(always_iterable(sizes)): if is_sequence(size): size, name = size else: name = "workgroup_%02i" % i pool.add_workgroup(size, name=name) for wg in pool.workgroups: if rank in wg.ranks: workgroup = wg return pool, workgroup def __getitem__(self, key): for wg in self.workgroups: if wg.name == key: return wg raise KeyError(key) class ResultsStorage: slots = ["result", "result_id"] result = None result_id = None def parallel_objects(objects, njobs=0, storage=None, barrier=True, dynamic=False): r"""This function dispatches components of an iterable to different processors. The parallel_objects function accepts an iterable, *objects*, and based on the number of jobs requested and number of available processors, decides how to dispatch individual objects to processors or sets of processors. This can implicitly include multi-level parallelism, such that the processor groups assigned each object can be composed of several or even hundreds of processors. *storage* is also available, for collation of results at the end of the iteration loop. Calls to this function can be nested. This should not be used to iterate over datasets -- :class:`~yt.data_objects.time_series.DatasetSeries` provides a much nicer interface for that. Parameters ---------- objects : Iterable The list of objects to dispatch to different processors. njobs : int How many jobs to spawn. By default, one job will be dispatched for each available processor. storage : dict This is a dictionary, which will be filled with results during the course of the iteration. The keys will be the dataset indices and the values will be whatever is assigned to the *result* attribute on the storage during iteration. barrier : bool Should a barier be placed at the end of iteration? dynamic : bool This governs whether or not dynamic load balancing will be enabled. This requires one dedicated processor; if this is enabled with a set of 128 processors available, only 127 will be available to iterate over objects as one will be load balancing the rest. Examples -------- Here is a simple example of iterating over a set of centers and making slice plots centered at each. >>> for c in parallel_objects(centers): ... SlicePlot(ds, "x", "Density", center=c).save() ... Here's an example of calculating the angular momentum vector of a set of spheres, but with a set of four jobs of multiple processors each. Note that we also store the results. >>> storage = {} >>> for sto, c in parallel_objects(centers, njobs=4, storage=storage): ... sp = ds.sphere(c, (100, "kpc")) ... sto.result = sp.quantities["AngularMomentumVector"]() ... >>> for sphere_id, L in sorted(storage.items()): ... print(centers[sphere_id], L) ... """ if dynamic: from .task_queue import dynamic_parallel_objects yield from dynamic_parallel_objects(objects, njobs=njobs, storage=storage) return if not parallel_capable: njobs = 1 my_communicator = communication_system.communicators[-1] my_size = my_communicator.size if njobs <= 0: njobs = my_size if njobs > my_size: mylog.error( "You have asked for %s jobs, but you only have %s processors.", njobs, my_size, ) raise RuntimeError my_rank = my_communicator.rank all_new_comms = np.array_split(np.arange(my_size), njobs) for i, comm_set in enumerate(all_new_comms): if my_rank in comm_set: my_new_id = i break if parallel_capable: communication_system.push_with_ids(all_new_comms[my_new_id].tolist()) to_share = {} # If our objects object is slice-aware, like time series data objects are, # this will prevent intermediate objects from being created. oiter = itertools.islice(enumerate(objects), my_new_id, None, njobs) for result_id, obj in oiter: if storage is not None: rstore = ResultsStorage() rstore.result_id = result_id yield rstore, obj to_share[rstore.result_id] = rstore.result else: yield obj if parallel_capable: communication_system.pop() if storage is not None: # Now we have to broadcast it new_storage = my_communicator.par_combine_object( to_share, datatype="dict", op="join" ) storage.update(new_storage) if barrier: my_communicator.barrier() def parallel_ring(objects, generator_func, mutable=False): r"""This function loops in a ring around a set of objects, yielding the results of generator_func and passing from one processor to another to avoid IO or expensive computation. This function is designed to operate in sequence on a set of objects, where the creation of those objects might be expensive. For instance, this could be a set of particles that are costly to read from disk. Processor N will run generator_func on an object, and the results of that will both be yielded and passed to processor N-1. If the length of the objects is not equal to the number of processors, then the final processor in the top communicator will re-generate the data as needed. In all likelihood, this function will only be useful internally to yt. Parameters ---------- objects : Iterable The list of objects to operate on. generator_func : callable This function will be called on each object, and the results yielded. It must return a single NumPy array; for multiple values, it needs to have a custom dtype. mutable : bool Should the arrays be considered mutable? Currently, this will only work if the number of processors equals the number of objects. Examples -------- Here is a simple example of a ring loop around a set of integers, with a custom dtype. >>> dt = np.dtype([("x", "float64"), ("y", "float64"), ("z", "float64")]) >>> def gfunc(o): ... np.random.seed(o) ... rv = np.empty(1000, dtype=dt) ... rv["x"] = np.random.random(1000) ... rv["y"] = np.random.random(1000) ... rv["z"] = np.random.random(1000) ... return rv ... >>> obj = range(8) >>> for obj, arr in parallel_ring(obj, gfunc): ... print(arr["x"].sum(), arr["y"].sum(), arr["z"].sum()) ... """ if mutable: raise NotImplementedError my_comm = communication_system.communicators[-1] my_size = my_comm.size my_rank = my_comm.rank # This will also be the first object we access if not parallel_capable and not mutable: for obj in objects: yield obj, generator_func(obj) return generate_endpoints = len(objects) != my_size # gback False: send the object backwards # gforw False: receive an object from forwards if len(objects) == my_size: generate_endpoints = False gback = False gforw = False else: # In this case, the first processor (my_rank == 0) will generate. generate_endpoints = True gback = my_rank == 0 gforw = my_rank == my_size - 1 if generate_endpoints and mutable: raise NotImplementedError # Now we need to do pairwise sends source = (my_rank + 1) % my_size dest = (my_rank - 1) % my_size oiter = itertools.islice(itertools.cycle(objects), my_rank, my_rank + len(objects)) idata = None isize = np.zeros((1,), dtype="int64") osize = np.zeros((1,), dtype="int64") for obj in oiter: if idata is None or gforw: idata = generator_func(obj) idtype = odtype = idata.dtype if get_mpi_type(idtype) is None: idtype = "c" yield obj, idata # We first send to the previous processor tags = [] if not gforw: tags.append(my_comm.mpi_nonblocking_recv(isize, source)) if not gback: osize[0] = idata.size tags.append(my_comm.mpi_nonblocking_send(osize, dest)) my_comm.mpi_Request_Waitall(tags) odata = idata tags = [] if not gforw: idata = np.empty(isize[0], dtype=odtype) tags.append( my_comm.mpi_nonblocking_recv(idata.view(idtype), source, dtype=idtype) ) if not gback: tags.append( my_comm.mpi_nonblocking_send(odata.view(idtype), dest, dtype=idtype) ) my_comm.mpi_Request_Waitall(tags) del odata class CommunicationSystem: communicators: list["Communicator"] = [] def __init__(self): self.communicators.append(Communicator(None)) def push(self, new_comm): if not isinstance(new_comm, Communicator): new_comm = Communicator(new_comm) self.communicators.append(new_comm) self._update_parallel_state(new_comm) def push_with_ids(self, ids): group = self.communicators[-1].comm.Get_group().Incl(ids) new_comm = self.communicators[-1].comm.Create(group) self.push(new_comm) return new_comm def _update_parallel_state(self, new_comm): ytcfg["yt", "internals", "topcomm_parallel_size"] = new_comm.size ytcfg["yt", "internals", "topcomm_parallel_rank"] = new_comm.rank if new_comm.rank > 0 and ytcfg.get("yt", "serialize"): ytcfg["yt", "only_deserialize"] = True def pop(self): self.communicators.pop() self._update_parallel_state(self.communicators[-1]) def _reconstruct_communicator(): return communication_system.communicators[-1] class Communicator: comm = None _grids = None _distributed = None __tocast = "c" def __init__(self, comm=None): self.comm = comm self._distributed = comm is not None and self.comm.size > 1 def __del__(self): if self.comm is not None: self.comm.Free() """ This is an interface specification providing several useful utility functions for analyzing something in parallel. """ def __reduce__(self): # We don't try to reconstruct any of the properties of the communicator # or the processors. In general, we don't want to. return (_reconstruct_communicator, ()) def barrier(self): if not self._distributed: return mylog.debug("Opening MPI Barrier on %s", self.comm.rank) self.comm.Barrier() def mpi_exit_test(self, data=False): # data==True -> exit. data==False -> no exit mine, statuses = self.mpi_info_dict(data) if True in statuses.values(): raise RuntimeError("Fatal error. Exiting.") return None @parallel_passthrough def par_combine_object(self, data, op, datatype=None): # op can be chosen from: # cat # join # data is selected to be of types: # np.ndarray # dict # data field dict if datatype is not None: pass elif isinstance(data, dict): datatype = "dict" elif isinstance(data, np.ndarray): datatype = "array" elif isinstance(data, list): datatype = "list" # Now we have our datatype, and we conduct our operation if datatype == "dict" and op == "join": if self.comm.rank == 0: for i in range(1, self.comm.size): data.update(self.comm.recv(source=i, tag=0)) else: self.comm.send(data, dest=0, tag=0) # Send the keys first, then each item one by one # This is to prevent MPI from crashing when sending more # than 2GiB of data over the network. keys = self.comm.bcast(list(data.keys()), root=0) for key in keys: tmp = data.get(key, None) data[key] = self.comm.bcast(tmp, root=0) return data elif datatype == "dict" and op == "cat": field_keys = sorted(data.keys()) size = data[field_keys[0]].shape[-1] sizes = np.zeros(self.comm.size, dtype="int64") outsize = np.array(size, dtype="int64") self.comm.Allgather([outsize, 1, MPI.LONG], [sizes, 1, MPI.LONG]) # This nested concatenate is to get the shapes to work out correctly; # if we just add [0] to sizes, it will broadcast a summation, not a # concatenation. offsets = np.add.accumulate(np.concatenate([[0], sizes]))[:-1] arr_size = self.comm.allreduce(size, op=MPI.SUM) for key in field_keys: dd = data[key] rv = self.alltoallv_array(dd, arr_size, offsets, sizes) data[key] = rv return data elif datatype == "array" and op == "cat": if data is None: ncols = -1 size = 0 dtype = "float64" mylog.warning( "Array passed to par_combine_object was None. " "Setting dtype to float64. This may break things!" ) else: dtype = data.dtype if len(data) == 0: ncols = -1 size = 0 elif len(data.shape) == 1: ncols = 1 size = data.shape[0] else: ncols, size = data.shape ncols = self.comm.allreduce(ncols, op=MPI.MAX) if ncols == 0: data = np.zeros(0, dtype=dtype) # This only works for elif data is None: data = np.zeros((ncols, 0), dtype=dtype) size = data.shape[-1] sizes = np.zeros(self.comm.size, dtype="int64") outsize = np.array(size, dtype="int64") self.comm.Allgather([outsize, 1, MPI.LONG], [sizes, 1, MPI.LONG]) # This nested concatenate is to get the shapes to work out correctly; # if we just add [0] to sizes, it will broadcast a summation, not a # concatenation. offsets = np.add.accumulate(np.concatenate([[0], sizes]))[:-1] arr_size = self.comm.allreduce(size, op=MPI.SUM) data = self.alltoallv_array(data, arr_size, offsets, sizes) return data elif datatype == "list" and op == "cat": recv_data = self.comm.allgather(data) # Now flatten into a single list, since this # returns us a list of lists. data = [] while recv_data: data.extend(recv_data.pop(0)) return data raise NotImplementedError @parallel_passthrough def mpi_bcast(self, data, root=0): # The second check below makes sure that we know how to communicate # this type of array. Otherwise, we'll pickle it. if isinstance(data, np.ndarray) and get_mpi_type(data.dtype) is not None: if self.comm.rank == root: if isinstance(data, YTArray): info = ( data.shape, data.dtype, str(data.units), data.units.registry.lut, ) if isinstance(data, ImageArray): info += ("ImageArray",) else: info += ("YTArray",) else: info = (data.shape, data.dtype) else: info = () info = self.comm.bcast(info, root=root) if self.comm.rank != root: if len(info) == 5: registry = UnitRegistry(lut=info[3], add_default_symbols=False) if info[-1] == "ImageArray": data = ImageArray( np.empty(info[0], dtype=info[1]), units=info[2], registry=registry, ) else: data = YTArray( np.empty(info[0], dtype=info[1]), info[2], registry=registry ) else: data = np.empty(info[0], dtype=info[1]) mpi_type = get_mpi_type(info[1]) self.comm.Bcast([data, mpi_type], root=root) return data else: # Use pickled methods. data = self.comm.bcast(data, root=root) return data def preload(self, grids, fields, io_handler): # This is non-functional. return @parallel_passthrough def mpi_allreduce(self, data, dtype=None, op="sum"): op = op_names[op] if isinstance(data, np.ndarray) and data.dtype != bool: if dtype is None: dtype = data.dtype if dtype != data.dtype: data = data.astype(dtype) temp = data.copy() self.comm.Allreduce( [temp, get_mpi_type(dtype)], [data, get_mpi_type(dtype)], op ) return data else: # We use old-school pickling here on the assumption the arrays are # relatively small ( < 1e7 elements ) return self.comm.allreduce(data, op) ### # Non-blocking stuff. ### def mpi_nonblocking_recv(self, data, source, tag=0, dtype=None): if not self._distributed: return -1 if dtype is None: dtype = data.dtype mpi_type = get_mpi_type(dtype) return self.comm.Irecv([data, mpi_type], source, tag) def mpi_nonblocking_send(self, data, dest, tag=0, dtype=None): if not self._distributed: return -1 if dtype is None: dtype = data.dtype mpi_type = get_mpi_type(dtype) return self.comm.Isend([data, mpi_type], dest, tag) def mpi_Request_Waitall(self, hooks): if not self._distributed: return MPI.Request.Waitall(hooks) def mpi_Request_Waititer(self, hooks): for _hook in hooks: req = MPI.Request.Waitany(hooks) yield req def mpi_Request_Testall(self, hooks): """ This returns False if any of the request hooks are un-finished, and True if they are all finished. """ if not self._distributed: return True return MPI.Request.Testall(hooks) ### # End non-blocking stuff. ### ### # Parallel rank and size properties. ### @property def size(self): if not self._distributed: return 1 return self.comm.size @property def rank(self): if not self._distributed: return 0 return self.comm.rank def mpi_info_dict(self, info): if not self._distributed: return 0, {0: info} data = None if self.comm.rank == 0: data = {0: info} for i in range(1, self.comm.size): data[i] = self.comm.recv(source=i, tag=0) else: self.comm.send(info, dest=0, tag=0) mylog.debug("Opening MPI Broadcast on %s", self.comm.rank) data = self.comm.bcast(data, root=0) return self.comm.rank, data def claim_object(self, obj): if not self._distributed: return obj._owner = self.comm.rank obj._distributed = True def do_not_claim_object(self, obj): if not self._distributed: return obj._owner = -1 obj._distributed = True def write_on_root(self, fn): if not self._distributed: return open(fn, "w") if self.comm.rank == 0: return open(fn, "w") else: return StringIO() def get_filename(self, prefix, rank=None): if not self._distributed: return prefix if rank is None: return "%s_%04i" % (prefix, self.comm.rank) else: return "%s_%04i" % (prefix, rank) def is_mine(self, obj): if not obj._distributed: return True return obj._owner == self.comm.rank def send_quadtree(self, target, buf, tgd, args): sizebuf = np.zeros(1, "int64") sizebuf[0] = buf[0].size self.comm.Send([sizebuf, MPI.LONG], dest=target) self.comm.Send([buf[0], MPI.INT], dest=target) self.comm.Send([buf[1], MPI.DOUBLE], dest=target) self.comm.Send([buf[2], MPI.DOUBLE], dest=target) def recv_quadtree(self, target, tgd, args): sizebuf = np.zeros(1, "int64") self.comm.Recv(sizebuf, source=target) buf = [ np.empty((sizebuf[0],), "int32"), np.empty((sizebuf[0], args[2]), "float64"), np.empty((sizebuf[0],), "float64"), ] self.comm.Recv([buf[0], MPI.INT], source=target) self.comm.Recv([buf[1], MPI.DOUBLE], source=target) self.comm.Recv([buf[2], MPI.DOUBLE], source=target) return buf @parallel_passthrough def merge_quadtree_buffers(self, qt, merge_style): # This is a modified version of pairwise reduction from Lisandro Dalcin, # in the reductions demo of mpi4py size = self.comm.size rank = self.comm.rank mask = 1 buf = qt.tobuffer() print("PROC", rank, buf[0].shape, buf[1].shape, buf[2].shape) sys.exit() args = qt.get_args() # Will always be the same tgd = np.array([args[0], args[1]], dtype="int64") sizebuf = np.zeros(1, "int64") while mask < size: if (mask & rank) != 0: target = (rank & ~mask) % size # print("SENDING FROM %02i to %02i" % (rank, target)) buf = qt.tobuffer() self.send_quadtree(target, buf, tgd, args) # qt = self.recv_quadtree(target, tgd, args) else: target = rank | mask if target < size: # print("RECEIVING FROM %02i on %02i" % (target, rank)) buf = self.recv_quadtree(target, tgd, args) qto = QuadTree(tgd, args[2], qt.bounds) qto.frombuffer(buf[0], buf[1], buf[2], merge_style) merge_quadtrees(qt, qto, style=merge_style) del qto # self.send_quadtree(target, qt, tgd, args) mask <<= 1 if rank == 0: buf = qt.tobuffer() sizebuf[0] = buf[0].size self.comm.Bcast([sizebuf, MPI.LONG], root=0) if rank != 0: buf = [ np.empty((sizebuf[0],), "int32"), np.empty((sizebuf[0], args[2]), "float64"), np.empty((sizebuf[0],), "float64"), ] self.comm.Bcast([buf[0], MPI.INT], root=0) self.comm.Bcast([buf[1], MPI.DOUBLE], root=0) self.comm.Bcast([buf[2], MPI.DOUBLE], root=0) self.refined = buf[0] if rank != 0: qt = QuadTree(tgd, args[2], qt.bounds) qt.frombuffer(buf[0], buf[1], buf[2], merge_style) return qt def send_array(self, arr, dest, tag=0): if not isinstance(arr, np.ndarray): self.comm.send((None, None), dest=dest, tag=tag) self.comm.send(arr, dest=dest, tag=tag) return tmp = arr.view(self.__tocast) # Cast to CHAR # communicate type and shape and optionally units if isinstance(arr, YTArray): unit_metadata = (str(arr.units), arr.units.registry.lut) if isinstance(arr, ImageArray): unit_metadata += ("ImageArray",) else: unit_metadata += ("YTArray",) else: unit_metadata = () self.comm.send((arr.dtype.str, arr.shape) + unit_metadata, dest=dest, tag=tag) self.comm.Send([arr, MPI.CHAR], dest=dest, tag=tag) del tmp def recv_array(self, source, tag=0): metadata = self.comm.recv(source=source, tag=tag) dt, ne = metadata[:2] if ne is None and dt is None: return self.comm.recv(source=source, tag=tag) arr = np.empty(ne, dtype=dt) if len(metadata) == 5: registry = UnitRegistry(lut=metadata[3], add_default_symbols=False) if metadata[-1] == "ImageArray": arr = ImageArray(arr, units=metadata[2], registry=registry) else: arr = YTArray(arr, metadata[2], registry=registry) tmp = arr.view(self.__tocast) self.comm.Recv([tmp, MPI.CHAR], source=source, tag=tag) return arr def alltoallv_array(self, send, total_size, offsets, sizes): if len(send.shape) > 1: recv = [] for i in range(send.shape[0]): recv.append( self.alltoallv_array(send[i, :].copy(), total_size, offsets, sizes) ) recv = np.array(recv) return recv offset = offsets[self.comm.rank] tmp_send = send.view(self.__tocast) recv = np.empty(total_size, dtype=send.dtype) if isinstance(send, YTArray): # We assume send.units is consistent with the units # on the receiving end. if isinstance(send, ImageArray): recv = ImageArray(recv, units=send.units) else: recv = YTArray(recv, send.units) recv[offset : offset + send.size] = send[:] dtr = send.dtype.itemsize / tmp_send.dtype.itemsize # > 1 roff = [off * dtr for off in offsets] rsize = [siz * dtr for siz in sizes] tmp_recv = recv.view(self.__tocast) self.comm.Allgatherv( (tmp_send, tmp_send.size, MPI.CHAR), (tmp_recv, (rsize, roff), MPI.CHAR) ) return recv def probe_loop(self, tag, callback): while True: st = MPI.Status() self.comm.Probe(MPI.ANY_SOURCE, tag=tag, status=st) try: callback(st) except StopIteration: mylog.debug("Probe loop ending.") break communication_system = CommunicationSystem() class ParallelAnalysisInterface: comm = None _grids = None _distributed = None def __init__(self, comm=None): if comm is None: self.comm = communication_system.communicators[-1] else: self.comm = comm self._grids = self.comm._grids self._distributed = self.comm._distributed def _get_objs(self, attr, *args, **kwargs): if self._distributed: rr = kwargs.pop("round_robin", False) self._initialize_parallel(*args, **kwargs) return ParallelObjectIterator(self, attr=attr, round_robin=rr) return ObjectIterator(self, attr=attr) def _get_grids(self, *args, **kwargs): if self._distributed: self._initialize_parallel(*args, **kwargs) return ParallelObjectIterator(self, attr="_grids") return ObjectIterator(self, attr="_grids") def _get_grid_objs(self): if self._distributed: return ParallelObjectIterator(self, True, attr="_grids") return ObjectIterator(self, True, attr="_grids") def get_dependencies(self, fields): deps = [] fi = self.ds.field_info for field in fields: if any(getattr(v, "ghost_zones", 0) > 0 for v in fi[field].validators): continue deps += list( always_iterable(fi[field].get_dependencies(ds=self.ds).requested) ) return list(set(deps)) def _initialize_parallel(self): pass def _finalize_parallel(self): pass def partition_index_2d(self, axis): center = getattr(self, "center", self.ds.domain_center) if not self._distributed: return False, self.index.grid_collection(center, self.index.grids) xax = self.ds.coordinates.x_axis[axis] yax = self.ds.coordinates.y_axis[axis] cc = MPI.Compute_dims(self.comm.size, 2) mi = self.comm.rank cx, cy = np.unravel_index(mi, cc) x = np.mgrid[0 : 1 : (cc[0] + 1) * 1j][cx : cx + 2] y = np.mgrid[0 : 1 : (cc[1] + 1) * 1j][cy : cy + 2] DLE, DRE = self.ds.domain_left_edge.copy(), self.ds.domain_right_edge.copy() LE = np.ones(3, dtype="float64") * DLE RE = np.ones(3, dtype="float64") * DRE LE[xax] = x[0] * (DRE[xax] - DLE[xax]) + DLE[xax] RE[xax] = x[1] * (DRE[xax] - DLE[xax]) + DLE[xax] LE[yax] = y[0] * (DRE[yax] - DLE[yax]) + DLE[yax] RE[yax] = y[1] * (DRE[yax] - DLE[yax]) + DLE[yax] mylog.debug("Dimensions: %s %s", LE, RE) reg = self.ds.region(center, LE, RE) return True, reg def partition_index_3d(self, ds, padding=0.0, rank_ratio=1): LE, RE = np.array(ds.left_edge), np.array(ds.right_edge) # We need to establish if we're looking at a subvolume, in which case # we *do* want to pad things. if (LE == self.ds.domain_left_edge).all() and ( RE == self.ds.domain_right_edge ).all(): subvol = False else: subvol = True if not self._distributed and not subvol: return False, LE, RE, ds if not self._distributed and subvol: return True, LE, RE, self.ds.region(self.center, LE - padding, RE + padding) elif ytcfg.get("yt", "inline"): # At this point, we want to identify the root grid tile to which # this processor is assigned. # The only way I really know how to do this is to get the level-0 # grid that belongs to this processor. grids = self.ds.index.select_grids(0) root_grids = [g for g in grids if g.proc_num == self.comm.rank] if len(root_grids) != 1: raise RuntimeError # raise KeyError LE = root_grids[0].LeftEdge RE = root_grids[0].RightEdge return True, LE, RE, self.ds.region(self.center, LE, RE) cc = MPI.Compute_dims(self.comm.size / rank_ratio, 3) mi = self.comm.rank % (self.comm.size // rank_ratio) cx, cy, cz = np.unravel_index(mi, cc) x = np.mgrid[LE[0] : RE[0] : (cc[0] + 1) * 1j][cx : cx + 2] y = np.mgrid[LE[1] : RE[1] : (cc[1] + 1) * 1j][cy : cy + 2] z = np.mgrid[LE[2] : RE[2] : (cc[2] + 1) * 1j][cz : cz + 2] LE = np.array([x[0], y[0], z[0]], dtype="float64") RE = np.array([x[1], y[1], z[1]], dtype="float64") if padding > 0: return True, LE, RE, self.ds.region(self.center, LE - padding, RE + padding) return False, LE, RE, self.ds.region(self.center, LE, RE) def partition_region_3d(self, left_edge, right_edge, padding=0.0, rank_ratio=1): """ Given a region, it subdivides it into smaller regions for parallel analysis. """ LE, RE = left_edge[:], right_edge[:] if not self._distributed: raise NotImplementedError cc = MPI.Compute_dims(self.comm.size / rank_ratio, 3) mi = self.comm.rank % (self.comm.size // rank_ratio) cx, cy, cz = np.unravel_index(mi, cc) x = np.mgrid[LE[0] : RE[0] : (cc[0] + 1) * 1j][cx : cx + 2] y = np.mgrid[LE[1] : RE[1] : (cc[1] + 1) * 1j][cy : cy + 2] z = np.mgrid[LE[2] : RE[2] : (cc[2] + 1) * 1j][cz : cz + 2] LE = np.array([x[0], y[0], z[0]], dtype="float64") RE = np.array([x[1], y[1], z[1]], dtype="float64") if padding > 0: return True, LE, RE, self.ds.region(self.center, LE - padding, RE + padding) return False, LE, RE, self.ds.region(self.center, LE, RE) def partition_index_3d_bisection_list(self): """ Returns an array that is used to drive _partition_index_3d_bisection, below. """ def factor(n): if n == 1: return [1] i = 2 limit = n**0.5 while i <= limit: if n % i == 0: ret = factor(n / i) ret.append(i) return ret i += 1 return [n] cc = MPI.Compute_dims(self.comm.size, 3) si = self.comm.size factors = factor(si) xyzfactors = [factor(cc[0]), factor(cc[1]), factor(cc[2])] # Each entry of cuts is a two element list, that is: # [cut dim, number of cuts] cuts = [] # The higher cuts are in the beginning. # We're going to do our best to make the cuts cyclic, i.e. x, then y, # then z, etc... lastdim = 0 for f in factors: nextdim = (lastdim + 1) % 3 while True: if f in xyzfactors[nextdim]: cuts.append([nextdim, f]) topop = xyzfactors[nextdim].index(f) xyzfactors[nextdim].pop(topop) lastdim = nextdim break nextdim = (nextdim + 1) % 3 return cuts class GroupOwnership(ParallelAnalysisInterface): def __init__(self, items): ParallelAnalysisInterface.__init__(self) self.num_items = len(items) self.items = items assert self.num_items >= self.comm.size self.owned = list(range(self.comm.size)) self.pointer = 0 if parallel_capable: communication_system.push_with_ids([self.comm.rank]) def __del__(self): if parallel_capable: communication_system.pop() def inc(self, n=-1): old_item = self.item if n == -1: n = self.comm.size for _ in range(n): if self.pointer >= self.num_items - self.comm.size: break self.owned[self.pointer % self.comm.size] += self.comm.size self.pointer += 1 if self.item is not old_item: self.switch() def dec(self, n=-1): old_item = self.item if n == -1: n = self.comm.size for _ in range(n): if self.pointer == 0: break self.owned[(self.pointer - 1) % self.comm.size] -= self.comm.size self.pointer -= 1 if self.item is not old_item: self.switch() _last = None @property def item(self): own = self.owned[self.comm.rank] if self._last != own: self._item = self.items[own] self._last = own return self._item def switch(self): pass yt-project-yt-f043ac8/yt/utilities/parallel_tools/task_queue.py000066400000000000000000000132351510711153200250610ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from .parallel_analysis_interface import ( ResultsStorage, _get_comm, communication_system, parallel_capable, ) messages = { "task": {"msg": "next"}, "result": {"msg": "result"}, "task_req": {"msg": "task_req"}, "end": {"msg": "no_more_tasks"}, } class TaskQueueNonRoot: def __init__(self, tasks, comm, subcomm): self.tasks = tasks self.results = {} self.comm = comm self.subcomm = subcomm def send_result(self, result): new_msg = messages["result"].copy() new_msg["value"] = result if self.subcomm.rank == 0: self.comm.comm.send(new_msg, dest=0, tag=1) self.subcomm.barrier() def __next__(self): msg = messages["task_req"].copy() if self.subcomm.rank == 0: self.comm.comm.send(msg, dest=0, tag=1) msg = self.comm.comm.recv(source=0, tag=2) msg = self.subcomm.bcast(msg, root=0) if msg["msg"] == messages["end"]["msg"]: mylog.debug("Notified to end") raise StopIteration return msg["value"] # For Python 2 compatibility next = __next__ def __iter__(self): return self def run(self, callable): for task in self: self.send_result(callable(task)) return self.finalize() def finalize(self, vals=None): return self.comm.comm.bcast(vals, root=0) class TaskQueueRoot(TaskQueueNonRoot): def __init__(self, tasks, comm, njobs): self.njobs = njobs self.tasks = tasks self.results = {} self.assignments = {} self._notified = 0 self._current = 0 self._remaining = len(self.tasks) self.comm = comm # Set up threading here # self.dist = threading.Thread(target=self.handle_assignments) # self.dist.daemon = True # self.dist.start() def run(self, func=None): self.comm.probe_loop(1, self.handle_assignment) return self.finalize(self.results) def insert_result(self, source_id, rstore): task_id = rstore.result_id self.results[task_id] = rstore.result def assign_task(self, source_id): if self._remaining == 0: mylog.debug("Notifying %s to end", source_id) msg = messages["end"].copy() self._notified += 1 else: msg = messages["task"].copy() task_id = self._current task = self.tasks[task_id] self.assignments[source_id] = task_id self._current += 1 self._remaining -= 1 msg["value"] = task self.comm.comm.send(msg, dest=source_id, tag=2) def handle_assignment(self, status): msg = self.comm.comm.recv(source=status.source, tag=1) if msg["msg"] == messages["result"]["msg"]: self.insert_result(status.source, msg["value"]) elif msg["msg"] == messages["task_req"]["msg"]: self.assign_task(status.source) else: mylog.error("GOT AN UNKNOWN MESSAGE: %s", msg) raise RuntimeError if self._notified >= self.njobs: raise StopIteration def task_queue(func, tasks, njobs=0): comm = _get_comm(()) if not parallel_capable: mylog.error("Cannot create task queue for serial process.") raise RuntimeError my_size = comm.comm.size if njobs <= 0: njobs = my_size - 1 if njobs >= my_size: mylog.error( "You have asked for %s jobs, but only %s processors are available.", njobs, (my_size - 1), ) raise RuntimeError my_rank = comm.rank all_new_comms = np.array_split(np.arange(1, my_size), njobs) all_new_comms.insert(0, np.array([0])) for i, comm_set in enumerate(all_new_comms): if my_rank in comm_set: my_new_id = i break subcomm = communication_system.push_with_ids(all_new_comms[my_new_id].tolist()) if comm.comm.rank == 0: my_q = TaskQueueRoot(tasks, comm, njobs) else: my_q = TaskQueueNonRoot(None, comm, subcomm) communication_system.pop() return my_q.run(func) def dynamic_parallel_objects(tasks, njobs=0, storage=None, broadcast=True): comm = _get_comm(()) if not parallel_capable: mylog.error("Cannot create task queue for serial process.") raise RuntimeError my_size = comm.comm.size if njobs <= 0: njobs = my_size - 1 if njobs >= my_size: mylog.error( "You have asked for %s jobs, but only %s processors are available.", njobs, (my_size - 1), ) raise RuntimeError my_rank = comm.rank all_new_comms = np.array_split(np.arange(1, my_size), njobs) all_new_comms.insert(0, np.array([0])) for i, comm_set in enumerate(all_new_comms): if my_rank in comm_set: my_new_id = i break subcomm = communication_system.push_with_ids(all_new_comms[my_new_id].tolist()) if comm.comm.rank == 0: my_q = TaskQueueRoot(tasks, comm, njobs) my_q.comm.probe_loop(1, my_q.handle_assignment) else: my_q = TaskQueueNonRoot(None, comm, subcomm) if storage is None: for task in my_q: yield task else: for task in my_q: rstore = ResultsStorage() yield rstore, task my_q.send_result(rstore) if storage is not None: if broadcast: my_results = my_q.comm.comm.bcast(my_q.results, root=0) else: my_results = my_q.results storage.update(my_results) communication_system.pop() yt-project-yt-f043ac8/yt/utilities/parameter_file_storage.py000066400000000000000000000142341510711153200244020ustar00rootroot00000000000000import os.path from itertools import islice from yt.config import ytcfg from yt.funcs import mylog from yt.utilities.object_registries import output_type_registry from yt.utilities.parallel_tools.parallel_analysis_interface import ( parallel_simple_proxy, ) _field_names = ("hash", "bn", "fp", "tt", "ctid", "class_name", "last_seen") class NoParameterShelf(Exception): pass class UnknownDatasetType(Exception): def __init__(self, name): self.name = name def __str__(self): return f"{self.name}" def __repr__(self): return f"{self.name}" class ParameterFileStore: """ This class is designed to be a semi-persistent storage for parameter files. By identifying each dataset with a unique hash, objects can be stored independently of datasets -- when an object is loaded, the dataset is as well, based on the hash. For storage concerns, only a few hundred will be retained in cache. """ _shared_state = {} # type: ignore _distributed = True _processing = False _owner = 0 _register = True def __new__(cls, *p, **k): self = object.__new__(cls, *p, **k) self.__dict__ = cls._shared_state return self def __init__(self, in_memory=False): """ Create the dataset database if yt is configured to store them. Otherwise, use read-only settings. """ if not self._register: return if ytcfg.get("yt", "store_parameter_files"): self._read_only = False self.init_db() self._records = self.read_db() else: self._read_only = True self._records = {} self._register = False @parallel_simple_proxy def init_db(self): """ This function ensures that the storage database exists and can be used. """ dbn = self._get_db_name() dbdir = os.path.dirname(dbn) try: if not os.path.isdir(dbdir): os.mkdir(dbdir) except OSError as exc: raise NoParameterShelf from exc open(dbn, "ab") # make sure it exists, allow to close # Now we read in all our records and return them # these will be broadcast def _get_db_name(self): base_file_name = ytcfg.get("yt", "parameter_file_store") if not os.access(os.path.expanduser("~/"), os.W_OK): return os.path.abspath(base_file_name) return os.path.expanduser(f"~/.yt/{base_file_name}") def get_ds_hash(self, hash): """This returns a dataset based on a hash.""" return self._convert_ds(self._records[hash]) def get_ds_ctid(self, ctid): """This returns a dataset based on a CurrentTimeIdentifier.""" for h in self._records: if self._records[h]["ctid"] == ctid: return self._convert_ds(self._records[h]) def _adapt_ds(self, ds): """This turns a dataset into a CSV entry.""" return { "bn": ds.basename, "fp": ds.directory, "tt": ds.current_time, "ctid": ds.unique_identifier, "class_name": ds.__class__.__name__, "last_seen": ds._instantiated, } def _convert_ds(self, ds_dict): """This turns a CSV entry into a dataset.""" bn = ds_dict["bn"] fp = ds_dict["fp"] fn = os.path.join(fp, bn) class_name = ds_dict["class_name"] if class_name not in output_type_registry: raise UnknownDatasetType(class_name) mylog.info("Checking %s", fn) if os.path.exists(fn): ds = output_type_registry[class_name](os.path.join(fp, bn)) else: raise OSError # This next one is to ensure that we manually update the last_seen # record *now*, for during write_out. self._records[ds._hash()]["last_seen"] = ds._instantiated return ds def check_ds(self, ds): """ This will ensure that the dataset (*ds*) handed to it is recorded in the storage unit. In doing so, it will update path and "last_seen" information. """ hash = ds._hash() if hash not in self._records: self.insert_ds(ds) return ds_dict = self._records[hash] self._records[hash]["last_seen"] = ds._instantiated if ds_dict["bn"] != ds.basename or ds_dict["fp"] != ds.directory: self.wipe_hash(hash) self.insert_ds(ds) def insert_ds(self, ds): """This will insert a new *ds* and flush the database to disk.""" self._records[ds._hash()] = self._adapt_ds(ds) self.flush_db() def wipe_hash(self, hash): """ This removes a *hash* corresponding to a dataset from the storage. """ if hash not in self._records: return del self._records[hash] self.flush_db() def flush_db(self): """This flushes the storage to disk.""" if self._read_only: return self._write_out() self.read_db() def get_recent(self, n=10): recs = sorted(self._records.values(), key=lambda a: -a["last_seen"])[:n] return recs @parallel_simple_proxy def _write_out(self): import csv if self._read_only: return fn = self._get_db_name() f = open(f"{fn}.tmp", "w") w = csv.DictWriter(f, _field_names) maxn = ytcfg.get("yt", "maximum_stored_datasets") # number written for h, v in islice( sorted(self._records.items(), key=lambda a: -a[1]["last_seen"]), 0, maxn ): v["hash"] = h w.writerow(v) f.close() os.rename(f"{fn}.tmp", fn) @parallel_simple_proxy def read_db(self): """This will read the storage device from disk.""" import csv f = open(self._get_db_name()) vals = csv.DictReader(f, _field_names) db = {} for v in vals: db[v.pop("hash")] = v if v["last_seen"] is None: v["last_seen"] = 0.0 else: v["last_seen"] = float(v["last_seen"]) f.close() return db yt-project-yt-f043ac8/yt/utilities/particle_generator.py000066400000000000000000000412231510711153200235460ustar00rootroot00000000000000import numpy as np from yt.funcs import get_pbar from yt.units._numpy_wrapper_functions import uconcatenate from yt.utilities.lib.particle_mesh_operations import CICSample_3 class ParticleGenerator: def __init__(self, ds, num_particles, field_list, ptype="io"): """ Base class for generating particle fields which may be applied to streams. Normally this would not be called directly, since it doesn't really do anything except allocate memory. Takes a *ds* to serve as the basis for determining grids, the number of particles *num_particles*, a list of fields, *field_list*, and the particle type *ptype*, which has a default value of "io". """ self.ds = ds self.num_particles = num_particles self.field_list = [ (ptype, fd) if isinstance(fd, str) else fd for fd in field_list ] self.field_list.append((ptype, "particle_index")) self.field_units = { (ptype, f"particle_position_{ax}"): "code_length" for ax in "xyz" } self.field_units[ptype, "particle_index"] = "" self.ptype = ptype self._set_default_fields() try: self.posx_index = self.field_list.index(self.default_fields[0]) self.posy_index = self.field_list.index(self.default_fields[1]) self.posz_index = self.field_list.index(self.default_fields[2]) except Exception as e: raise KeyError( "You must specify position fields: " + " ".join(f"particle_position_{ax}" for ax in "xyz") ) from e self.index_index = self.field_list.index((ptype, "particle_index")) self.num_grids = self.ds.index.num_grids self.NumberOfParticles = np.zeros(self.num_grids, dtype="int64") self.ParticleGridIndices = np.zeros(self.num_grids + 1, dtype="int64") self.num_fields = len(self.field_list) self.particles = np.zeros( (self.num_particles, self.num_fields), dtype="float64" ) def _set_default_fields(self): self.default_fields = [ (self.ptype, "particle_position_x"), (self.ptype, "particle_position_y"), (self.ptype, "particle_position_z"), ] def has_key(self, key): """ Check to see if *key* is in the particle field list. """ return key in self.field_list def keys(self): """ Return the list of particle fields. """ return self.field_list def __getitem__(self, key): """ Get the field associated with key. """ return self.particles[:, self.field_list.index(key)] def __setitem__(self, key, val): """ Sets a field to be some other value. Note that we assume that the particles have been sorted by grid already, so make sure the setting of the field is consistent with this. """ self.particles[:, self.field_list.index(key)] = val[:] def __len__(self): """ The number of particles """ return self.num_particles def get_for_grid(self, grid): """ Return a dict containing all of the particle fields in the specified grid. """ ind = grid.id - grid._id_offset start = self.ParticleGridIndices[ind] end = self.ParticleGridIndices[ind + 1] tr = {} for field in self.field_list: fi = self.field_list.index(field) if field in self.field_units: tr[field] = self.ds.arr( self.particles[start:end, fi], self.field_units[field] ) else: tr[field] = self.particles[start:end, fi] return tr def _setup_particles(self, x, y, z, setup_fields=None): """ Assigns grids to particles and sets up particle positions. *setup_fields* is a dict of fields other than the particle positions to set up. """ particle_grids, particle_grid_inds = self.ds.index._find_points(x, y, z) idxs = np.argsort(particle_grid_inds) self.particles[:, self.posx_index] = x[idxs] self.particles[:, self.posy_index] = y[idxs] self.particles[:, self.posz_index] = z[idxs] self.NumberOfParticles = np.bincount( particle_grid_inds.astype("intp"), minlength=self.num_grids ) if self.num_grids > 1: np.add.accumulate( self.NumberOfParticles.squeeze(), out=self.ParticleGridIndices[1:] ) else: self.ParticleGridIndices[1] = self.NumberOfParticles.squeeze() if setup_fields is not None: for key, value in setup_fields.items(): field = (self.ptype, key) if isinstance(key, str) else key if field not in self.default_fields: self.particles[:, self.field_list.index(field)] = value[idxs] def assign_indices(self, function=None, **kwargs): """ Assign unique indices to the particles. The default is to just use numpy.arange, but any function may be supplied with keyword arguments. """ if function is None: self.particles[:, self.index_index] = np.arange(self.num_particles) else: self.particles[:, self.index_index] = function(**kwargs) def map_grid_fields_to_particles(self, mapping_dict): r""" For the fields in *mapping_dict*, map grid fields to the particles using CIC sampling. Examples -------- >>> field_map = { ... "density": "particle_density", ... "temperature": "particle_temperature", ... } >>> particles.map_grid_fields_to_particles(field_map) """ pbar = get_pbar("Mapping fields to particles", self.num_grids) for i, grid in enumerate(self.ds.index.grids): pbar.update(i + 1) if self.NumberOfParticles[i] > 0: start = self.ParticleGridIndices[i] end = self.ParticleGridIndices[i + 1] # Note we add one ghost zone to the grid! cube = grid.retrieve_ghost_zones(1, list(mapping_dict.keys())) le = np.array(grid.LeftEdge).astype(np.float64) dims = np.array(grid.ActiveDimensions).astype(np.int32) for gfield, pfield in mapping_dict.items(): self.field_units[pfield] = cube[gfield].units field_index = self.field_list.index(pfield) CICSample_3( self.particles[start:end, self.posx_index], self.particles[start:end, self.posy_index], self.particles[start:end, self.posz_index], self.particles[start:end, field_index], np.int64(self.NumberOfParticles[i]), cube[gfield], le, dims, grid.dds[0], ) pbar.finish() def apply_to_stream(self, overwrite=False, **kwargs): """ Apply the particles to a grid-based stream dataset. If particles already exist, and overwrite=False, do not overwrite them, but add the new ones to them. """ grid_data = [] for i, g in enumerate(self.ds.index.grids): data = {} number_of_particles = self.NumberOfParticles[i] if not overwrite: number_of_particles += g.NumberOfParticles grid_particles = self.get_for_grid(g) for field in self.field_list: if number_of_particles > 0: if ( g.NumberOfParticles > 0 and not overwrite and field in self.ds.field_list ): # We have particles in this grid, we're not # overwriting them, and the field is in the field # list already data[field] = uconcatenate([g[field], grid_particles[field]]) else: # Otherwise, simply add the field in data[field] = grid_particles[field] else: # We don't have particles in this grid data[field] = np.array([], dtype="float64") grid_data.append(data) self.ds.index.update_data(grid_data) class FromListParticleGenerator(ParticleGenerator): def __init__(self, ds, num_particles, data, ptype="io"): r""" Generate particle fields from array-like lists contained in a dict. Parameters ---------- ds : `Dataset` The dataset which will serve as the base for these particles. num_particles : int The number of particles in the dict. data : dict of NumPy arrays The particle fields themselves. ptype : string, optional The particle type for these particle fields. Default: "io" Examples -------- >>> num_p = 100000 >>> posx = np.random.random(num_p) >>> posy = np.random.random(num_p) >>> posz = np.random.random(num_p) >>> mass = np.ones(num_p) >>> data = { ... "particle_position_x": posx, ... "particle_position_y": posy, ... "particle_position_z": posz, ... "particle_mass": mass, ... } >>> particles = FromListParticleGenerator(ds, num_p, data) """ field_list = list(data.keys()) if "particle_position_x" in data: x = data.pop("particle_position_x") y = data.pop("particle_position_y") z = data.pop("particle_position_z") elif (ptype, "particle_position_x") in data: x = data.pop((ptype, "particle_position_x")) y = data.pop((ptype, "particle_position_y")) z = data.pop((ptype, "particle_position_z")) xcond = np.logical_or(x < ds.domain_left_edge[0], x >= ds.domain_right_edge[0]) ycond = np.logical_or(y < ds.domain_left_edge[1], y >= ds.domain_right_edge[1]) zcond = np.logical_or(z < ds.domain_left_edge[2], z >= ds.domain_right_edge[2]) cond = np.logical_or(xcond, ycond) cond = np.logical_or(zcond, cond) if np.any(cond): raise ValueError("Some particles are outside of the domain!!!") super().__init__(ds, num_particles, field_list, ptype=ptype) self._setup_particles(x, y, z, setup_fields=data) class LatticeParticleGenerator(ParticleGenerator): def __init__( self, ds, particles_dims, particles_left_edge, particles_right_edge, field_list, ptype="io", ): r""" Generate particles in a lattice arrangement. Parameters ---------- ds : `Dataset` The dataset which will serve as the base for these particles. particles_dims : int, array-like The number of particles along each dimension particles_left_edge : float, array-like The 'left-most' starting positions of the lattice. particles_right_edge : float, array-like The 'right-most' ending positions of the lattice. field_list : list of strings A list of particle fields ptype : string, optional The particle type for these particle fields. Default: "io" Examples -------- >>> dims = (128, 128, 128) >>> le = np.array([0.25, 0.25, 0.25]) >>> re = np.array([0.75, 0.75, 0.75]) >>> fields = [ ... ("all", "particle_position_x"), ... ("all", "particle_position_y"), ... ("all", "particle_position_z"), ... ("all", "particle_density"), ... ("all", "particle_temperature"), ... ] >>> particles = LatticeParticleGenerator(ds, dims, le, re, fields) """ num_x = particles_dims[0] num_y = particles_dims[1] num_z = particles_dims[2] xmin = particles_left_edge[0] ymin = particles_left_edge[1] zmin = particles_left_edge[2] xmax = particles_right_edge[0] ymax = particles_right_edge[1] zmax = particles_right_edge[2] DLE = ds.domain_left_edge.in_units("code_length").d DRE = ds.domain_right_edge.in_units("code_length").d xcond = (xmin < DLE[0]) or (xmax >= DRE[0]) ycond = (ymin < DLE[1]) or (ymax >= DRE[1]) zcond = (zmin < DLE[2]) or (zmax >= DRE[2]) cond = xcond or ycond or zcond if cond: raise ValueError("Proposed bounds for particles are outside domain!!!") super().__init__(ds, num_x * num_y * num_z, field_list, ptype=ptype) dx = (xmax - xmin) / (num_x - 1) dy = (ymax - ymin) / (num_y - 1) dz = (zmax - zmin) / (num_z - 1) inds = np.indices((num_x, num_y, num_z)) xpos = inds[0] * dx + xmin ypos = inds[1] * dy + ymin zpos = inds[2] * dz + zmin self._setup_particles(xpos.flat[:], ypos.flat[:], zpos.flat[:]) class WithDensityParticleGenerator(ParticleGenerator): def __init__( self, ds, data_source, num_particles, field_list, density_field=("gas", "density"), ptype="io", ): r""" Generate particles based on a density field. Parameters ---------- ds : `Dataset` The dataset which will serve as the base for these particles. data_source : `yt.data_objects.selection_objects.base_objects.YTSelectionContainer` The data source containing the density field. num_particles : int The number of particles to be generated field_list : list of strings A list of particle fields density_field : tuple, optional A density field which will serve as the distribution function for the particle positions. Theoretically, this could be any 'per-volume' field. ptype : string, optional The particle type for these particle fields. Default: "io" Examples -------- >>> sphere = ds.sphere(ds.domain_center, 0.5) >>> num_p = 100000 >>> fields = [ ... ("all", "particle_position_x"), ... ("all", "particle_position_y"), ... ("all", "particle_position_z"), ... ("all", "particle_density"), ... ("all", "particle_temperature"), ... ] >>> particles = WithDensityParticleGenerator( ... ds, sphere, num_particles, fields, density_field="Dark_Matter_Density" ... ) """ super().__init__(ds, num_particles, field_list, ptype=ptype) num_cells = len(data_source["index", "x"].flat) max_mass = ( data_source[density_field] * data_source["gas", "cell_volume"] ).max() num_particles_left = num_particles all_x = [] all_y = [] all_z = [] pbar = get_pbar("Generating Particles", num_particles) tot_num_accepted = 0 rng = np.random.default_rng() while num_particles_left > 0: m = rng.uniform(high=1.01 * max_mass, size=num_particles_left) idxs = rng.integers(low=0, high=num_cells, size=num_particles_left) m_true = ( data_source[density_field] * data_source["gas", "cell_volume"] ).flat[idxs] accept = m <= m_true num_accepted = accept.sum() accepted_idxs = idxs[accept] xpos = ( data_source["index", "x"].flat[accepted_idxs] + rng.uniform(low=-0.5, high=0.5, size=num_accepted) * data_source["index", "dx"].flat[accepted_idxs] ) ypos = ( data_source["index", "y"].flat[accepted_idxs] + rng.uniform(low=-0.5, high=0.5, size=num_accepted) * data_source["index", "dy"].flat[accepted_idxs] ) zpos = ( data_source["index", "z"].flat[accepted_idxs] + rng.uniform(low=-0.5, high=0.5, size=num_accepted) * data_source["index", "dz"].flat[accepted_idxs] ) all_x.append(xpos) all_y.append(ypos) all_z.append(zpos) num_particles_left -= num_accepted tot_num_accepted += num_accepted pbar.update(tot_num_accepted) pbar.finish() x = uconcatenate(all_x) y = uconcatenate(all_y) z = uconcatenate(all_z) self._setup_particles(x, y, z) yt-project-yt-f043ac8/yt/utilities/performance_counters.py000066400000000000000000000075501510711153200241250ustar00rootroot00000000000000import atexit import time from bisect import insort from collections import defaultdict from datetime import datetime as dt from functools import wraps from yt.config import ytcfg from yt.funcs import mylog class PerformanceCounters: _shared_state = {} # type: ignore def __new__(cls, *args, **kwargs): self = object.__new__(cls, *args, **kwargs) self.__dict__ = cls._shared_state return self def __init__(self): self.counters = defaultdict(lambda: 0.0) self.counting = defaultdict(lambda: False) self.starttime = defaultdict(lambda: 0) self.endtime = defaultdict(lambda: 0) self._on = ytcfg.get("yt", "time_functions") self.exit() def __call__(self, name): if not self._on: return if self.counting[name]: self.counters[name] = time.time() - self.counters[name] self.counting[name] = False self.endtime[name] = dt.now() else: self.counters[name] = time.time() self.counting[name] = True self.starttime[name] = dt.now() def call_func(self, func): if not self._on: return func @wraps(func) def func_wrapper(*args, **kwargs): self(func.__name__) func(*args, **kwargs) self(func.__name__) return func_wrapper def print_stats(self): mylog.info("Current counter status:\n") times = [] for i in self.counters: insort(times, [self.starttime[i], i, 1]) # 1 for 'on' if not self.counting[i]: insort(times, [self.endtime[i], i, 0]) # 0 for 'off' shifts = {} order = [] endtimes = {} shift = 0 multi = 5 for i in times: # a starting entry if i[2] == 1: shifts[i[1]] = shift order.append(i[1]) shift += 1 if i[2] == 0: shift -= 1 endtimes[i[1]] = self.counters[i[1]] line = "" for i in order: if self.counting[i]: line = "%s%s%i : %s : still running\n" % ( line, " " * shifts[i] * multi, shifts[i], i, ) else: line = "%s%s%i : %s : %0.3e\n" % ( line, " " * shifts[i] * multi, shifts[i], i, self.counters[i], ) mylog.info("\n%s", line) def exit(self): if self._on: atexit.register(self.print_stats) yt_counters = PerformanceCounters() time_function = yt_counters.call_func class ProfilingController: def __init__(self): self.profilers = {} def profile_function(self, function_name): def wrapper(func): try: import cProfile except ImportError: return func my_prof = cProfile.Profile() self.profilers[function_name] = my_prof @wraps(func) def run_in_profiler(*args, **kwargs): my_prof.enable() func(*args, **kwargs) my_prof.disable() return run_in_profiler return wrapper def write_out(self, filename_prefix): if ytcfg.get("yt", "internals", "parallel"): pfn = "%s_%03i_%03i" % ( filename_prefix, ytcfg.get("yt", "internals", "global_parallel_rank"), ytcfg.get("yt", "internals", "global_parallel_size"), ) else: pfn = f"{filename_prefix}" for n, p in sorted(self.profilers.items()): fn = f"{pfn}_{n}.cprof" mylog.info("Dumping %s into %s", n, fn) p.dump_stats(fn) yt-project-yt-f043ac8/yt/utilities/periodic_table.py000066400000000000000000000144131510711153200226430ustar00rootroot00000000000000import numbers import numpy as np _elements = ( (1, 1.0079400000, "Hydrogen", "H"), (2, 4.0026020000, "Helium", "He"), (3, 6.9410000000, "Lithium", "Li"), (4, 9.0121820000, "Beryllium", "Be"), (5, 10.8110000000, "Boron", "B"), (6, 12.0107000000, "Carbon", "C"), (7, 14.0067000000, "Nitrogen", "N"), (8, 15.9994000000, "Oxygen", "O"), (9, 18.9994000000, "Fluorine", "F"), (10, 20.1797000000, "Neon", "Ne"), (11, 22.9897692800, "Sodium", "Na"), (12, 24.3050000000, "Magnesium", "Mg"), (13, 26.9815386000, "Aluminium", "Al"), (14, 28.0855000000, "Silicon", "Si"), (15, 30.9737620000, "Phosphorus", "P"), (16, 32.0650000000, "Sulphur", "S"), (17, 35.4530000000, "Chlorine", "Cl"), (18, 39.9480000000, "Argon", "Ar"), (19, 39.0983000000, "Potassium", "K"), (20, 40.0780000000, "Calcium", "Ca"), (21, 44.9559120000, "Scandium", "Sc"), (22, 47.8670000000, "Titanium", "Ti"), (23, 50.9415000000, "Vanadium", "V"), (24, 51.9961000000, "Chromium", "Cr"), (25, 54.9380450000, "Manganese", "Mn"), (26, 55.8450000000, "Iron", "Fe"), (27, 58.9331950000, "Cobalt", "Co"), (28, 58.6934000000, "Nickel", "Ni"), (29, 63.5460000000, "Copper", "Cu"), (30, 65.3800000000, "Zinc", "Zn"), (31, 69.7230000000, "Gallium", "Ga"), (32, 72.6400000000, "Germanium", "Ge"), (33, 74.9216000000, "Arsenic", "As"), (34, 78.9600000000, "Selenium", "Se"), (35, 79.9040000000, "Bromine", "Br"), (36, 83.7980000000, "Krypton", "Kr"), (37, 85.4678000000, "Rubidium", "Rb"), (38, 87.6200000000, "Strontium", "Sr"), (39, 88.9058500000, "Yttrium", "Y"), (40, 91.2240000000, "Zirkonium", "Zr"), (41, 92.9063800000, "Niobium", "Nb"), (42, 95.9600000000, "Molybdaenum", "Mo"), (43, 98.0000000000, "Technetium", "Tc"), (44, 101.0700000000, "Ruthenium", "Ru"), (45, 102.9055000000, "Rhodium", "Rh"), (46, 106.4200000000, "Palladium", "Pd"), (47, 107.8682000000, "Silver", "Ag"), (48, 112.4110000000, "Cadmium", "Cd"), (49, 114.8180000000, "Indium", "In"), (50, 118.7100000000, "Tin", "Sn"), (51, 121.7600000000, "Antimony", "Sb"), (52, 127.6000000000, "Tellurium", "Te"), (53, 126.9044700000, "Iodine", "I"), (54, 131.2930000000, "Xenon", "Xe"), (55, 132.9054519000, "Cesium", "Cs"), (56, 137.3270000000, "Barium", "Ba"), (57, 138.9054700000, "Lanthanum", "La"), (58, 140.1160000000, "Cerium", "Ce"), (59, 140.9076500000, "Praseodymium", "Pr"), (60, 144.2420000000, "Neodymium", "Nd"), (61, 145.0000000000, "Promethium", "Pm"), (62, 150.3600000000, "Samarium", "Sm"), (63, 151.9640000000, "Europium", "Eu"), (64, 157.2500000000, "Gadolinium", "Gd"), (65, 158.9253500000, "Terbium", "Tb"), (66, 162.5001000000, "Dysprosium", "Dy"), (67, 164.9303200000, "Holmium", "Ho"), (68, 167.2590000000, "Erbium", "Er"), (69, 168.9342100000, "Thulium", "Tm"), (70, 173.0540000000, "Ytterbium", "Yb"), (71, 174.9668000000, "Lutetium", "Lu"), (72, 178.4900000000, "Hafnium", "Hf"), (73, 180.9478800000, "Tantalum", "Ta"), (74, 183.8400000000, "Tungsten", "W"), (75, 186.2070000000, "Rhenium", "Re"), (76, 190.2300000000, "Osmium", "Os"), (77, 192.2170000000, "Iridium", "Ir"), (78, 192.0840000000, "Platinum", "Pt"), (79, 196.9665690000, "Gold", "Au"), (80, 200.5900000000, "Hydrargyrum", "Hg"), (81, 204.3833000000, "Thallium", "Tl"), (82, 207.2000000000, "Lead", "Pb"), (83, 208.9804010000, "Bismuth", "Bi"), (84, 210.0000000000, "Polonium", "Po"), (85, 210.0000000000, "Astatine", "At"), (86, 220.0000000000, "Radon", "Rn"), (87, 223.0000000000, "Francium", "Fr"), (88, 226.0000000000, "Radium", "Ra"), (89, 227.0000000000, "Actinium", "Ac"), (90, 232.0380600000, "Thorium", "Th"), (91, 231.0358800000, "Protactinium", "Pa"), (92, 238.0289100000, "Uranium", "U"), (93, 237.0000000000, "Neptunium", "Np"), (94, 244.0000000000, "Plutonium", "Pu"), (95, 243.0000000000, "Americium", "Am"), (96, 247.0000000000, "Curium", "Cm"), (97, 247.0000000000, "Berkelium", "Bk"), (98, 251.0000000000, "Californium", "Cf"), (99, 252.0000000000, "Einsteinium", "Es"), (100, 257.0000000000, "Fermium", "Fm"), (101, 258.0000000000, "Mendelevium", "Md"), (102, 259.0000000000, "Nobelium", "No"), (103, 262.0000000000, "Lawrencium", "Lr"), (104, 261.0000000000, "Rutherfordium", "Rf"), (105, 262.0000000000, "Dubnium", "Db"), (106, 266.0000000000, "Seaborgium", "Sg"), (107, 264.0000000000, "Bohrium", "Bh"), (108, 277.0000000000, "Hassium", "Hs"), (109, 268.0000000000, "Meitnerium", "Mt"), (110, 271.0000000000, "Ununnilium", "Ds"), (111, 272.0000000000, "Unununium", "Rg"), (112, 285.0000000000, "Ununbium", "Uub"), (113, 284.0000000000, "Ununtrium", "Uut"), (114, 289.0000000000, "Ununquadium", "Uuq"), (115, 288.0000000000, "Ununpentium", "Uup"), (116, 292.0000000000, "Ununhexium", "Uuh"), (118, 294.0000000000, "Ununoctium", "Uuo"), # Now some special cases that are *not* elements (-1, 2.014102, "Deuterium", "D"), (-1, 0.00054858, "Electron", "El"), ) class Element: def __init__(self, num, weight, name, symbol): self.num = num self.weight = weight self.name = name self.symbol = symbol def __repr__(self): return f"Element: {self.symbol} ({self.name})" class PeriodicTable: def __init__(self): self.elements_by_number = {} self.elements_by_name = {} self.elements_by_symbol = {} for num, weight, name, symbol in _elements: e = Element(num, weight, name, symbol) self.elements_by_number[num] = e self.elements_by_name[name] = e self.elements_by_symbol[symbol] = e def __getitem__(self, key): if isinstance(key, (np.number, numbers.Number)): d = self.elements_by_number elif isinstance(key, str): if len(key) <= 2: d = self.elements_by_symbol elif len(key) == 3 and key[0] == "U": d = self.elements_by_symbol else: d = self.elements_by_name else: raise KeyError(key) return d[key] periodic_table = PeriodicTable() yt-project-yt-f043ac8/yt/utilities/physical_constants.py000066400000000000000000000101341510711153200236020ustar00rootroot00000000000000from math import pi from yt.units.yt_array import YTQuantity from yt.utilities.physical_ratios import ( amu_grams, boltzmann_constant_erg_per_K, mass_earth_grams, mass_electron_grams, mass_hydrogen_grams, mass_jupiter_grams, mass_mars_grams, mass_mercury_grams, mass_neptune_grams, mass_saturn_grams, mass_sun_grams, mass_uranus_grams, mass_venus_grams, newton_cgs, planck_cgs, planck_charge_esu, planck_energy_erg, planck_length_cm, planck_mass_grams, planck_temperature_K, planck_time_s, speed_of_light_cm_per_s, standard_gravity_cm_per_s2, ) mass_electron_cgs = YTQuantity(mass_electron_grams, "g") mass_electron = mass_electron_cgs me = mass_electron_cgs amu_cgs = YTQuantity(amu_grams, "g") amu = amu_cgs Na = 1 / amu_cgs mass_hydrogen_cgs = YTQuantity(mass_hydrogen_grams, "g") mass_hydrogen = mass_hydrogen_cgs mp = mass_hydrogen_cgs mh = mp # Velocities speed_of_light_cgs = YTQuantity(speed_of_light_cm_per_s, "cm/s") speed_of_light = speed_of_light_cgs clight = speed_of_light_cgs c = speed_of_light_cgs # Cross Sections # 8*pi/3 (alpha*hbar*c/(2*pi))**2 cross_section_thompson_cgs = YTQuantity(6.65245854533e-25, "cm**2") cross_section_thompson = cross_section_thompson_cgs thompson_cross_section = cross_section_thompson_cgs sigma_thompson = cross_section_thompson_cgs # Charge charge_proton_cgs = YTQuantity(4.8032056e-10, "esu") charge_proton = charge_proton_cgs proton_charge = charge_proton_cgs elementary_charge = charge_proton_cgs qp = charge_proton_cgs # Physical Constants boltzmann_constant_cgs = YTQuantity(boltzmann_constant_erg_per_K, "erg/K") boltzmann_constant = boltzmann_constant_cgs kboltz = boltzmann_constant_cgs kb = kboltz gravitational_constant_cgs = YTQuantity(newton_cgs, "cm**3/g/s**2") gravitational_constant = gravitational_constant_cgs G = gravitational_constant_cgs planck_constant_cgs = YTQuantity(planck_cgs, "erg*s") planck_constant = planck_constant_cgs hcgs = planck_constant_cgs hbar = 0.5 * hcgs / pi stefan_boltzmann_constant_cgs = YTQuantity(5.670373e-5, "erg/cm**2/s**1/K**4") stefan_boltzmann_constant = stefan_boltzmann_constant_cgs Tcmb = YTQuantity(2.726, "K") # Current CMB temperature CMB_temperature = Tcmb # Solar System mass_sun_cgs = YTQuantity(mass_sun_grams, "g") mass_sun = mass_sun_cgs solar_mass = mass_sun_cgs msun = mass_sun_cgs # Standish, E.M. (1995) "Report of the IAU WGAS Sub-Group on Numerical Standards", # in Highlights of Astronomy (I. Appenzeller, ed.), Table 1, # Kluwer Academic Publishers, Dordrecht. # REMARK: following masses include whole systems (planet + moons) mass_jupiter_cgs = YTQuantity(mass_jupiter_grams, "g") mass_jupiter = mass_jupiter_cgs jupiter_mas = mass_jupiter_cgs mass_mercury_cgs = YTQuantity(mass_mercury_grams, "g") mass_mercury = mass_mercury_cgs mercury_mass = mass_mercury_cgs mass_venus_cgs = YTQuantity(mass_venus_grams, "g") mass_venus = mass_venus_cgs venus_mass = mass_venus_cgs mass_earth_cgs = YTQuantity(mass_earth_grams, "g") mass_earth = mass_earth_cgs earth_mass = mass_earth_cgs mearth = mass_earth_cgs mass_mars_cgs = YTQuantity(mass_mars_grams, "g") mass_mars = mass_mars_cgs mars_mass = mass_mars_cgs mass_saturn_cgs = YTQuantity(mass_saturn_grams, "g") mass_saturn = mass_saturn_cgs saturn_mass = mass_saturn_cgs mass_uranus_cgs = YTQuantity(mass_uranus_grams, "g") mass_uranus = mass_uranus_cgs uranus_mass = mass_uranus_cgs mass_neptune_cgs = YTQuantity(mass_neptune_grams, "g") mass_neptune = mass_neptune_cgs neptune_mass = mass_neptune_cgs # Planck units m_pl = planck_mass = YTQuantity(planck_mass_grams, "g") l_pl = planck_length = YTQuantity(planck_length_cm, "cm") t_pl = planck_time = YTQuantity(planck_time_s, "s") E_pl = planck_energy = YTQuantity(planck_energy_erg, "erg") q_pl = planck_charge = YTQuantity(planck_charge_esu, "esu") T_pl = planck_temperature = YTQuantity(planck_temperature_K, "K") # MKS E&M units mu_0 = YTQuantity(4.0e-7 * pi, "N/A**2") eps_0 = (1.0 / (clight**2 * mu_0)).in_units("C**2/N/m**2") # Misc standard_gravity_cgs = YTQuantity(standard_gravity_cm_per_s2, "cm/s**2") standard_gravity = standard_gravity_cgs yt-project-yt-f043ac8/yt/utilities/physical_ratios.py000066400000000000000000000111131510711153200230650ustar00rootroot00000000000000import numpy as np # # Physical Constants and Units Conversion Factors # # Values for these constants, unless otherwise noted, are drawn from IAU, # IUPAC, NIST, and NASA data, whichever is newer. # http://maia.usno.navy.mil/NSFA/IAU2009_consts.html # http://goldbook.iupac.org/list_goldbook_phys_constants_defs.html # http://physics.nist.gov/cuu/Constants/index.html # http://nssdc.gsfc.nasa.gov/planetary/factsheet/jupiterfact.html # Elementary masses mass_electron_grams = 9.10938291e-28 amu_grams = 1.660538921e-24 mass_hydrogen_grams = 1.007947 * amu_grams # Solar values (see Mamajek 2012) # https://sites.google.com/site/mamajeksstarnotes/bc-scale mass_sun_grams = 1.98841586e33 temp_sun_kelvin = 5870.0 luminosity_sun_ergs_per_sec = 3.8270e33 # Consistent with solar abundances used in Cloudy metallicity_sun = 0.01295 # Conversion Factors: X au * mpc_per_au = Y mpc # length mpc_per_mpc = 1e0 mpc_per_kpc = 1e-3 mpc_per_pc = 1e-6 mpc_per_au = 4.84813682e-12 mpc_per_rsun = 2.253962e-14 mpc_per_rearth = 2.06470307893e-16 mpc_per_rjup = 2.26566120943e-15 mpc_per_miles = 5.21552871e-20 mpc_per_km = 3.24077929e-20 mpc_per_cm = 3.24077929e-25 kpc_per_cm = mpc_per_cm / mpc_per_kpc pc_per_cm = mpc_per_cm / mpc_per_pc km_per_pc = 3.08567758e13 km_per_m = 1e-3 km_per_cm = 1e-5 m_per_cm = 1e-2 ly_per_cm = 1.05702341e-18 rsun_per_cm = 1.4378145e-11 rearth_per_cm = 1.56961033e-9 # Mean (volumetric) radius rjup_per_cm = 1.43039006737e-10 # Mean (volumetric) radius au_per_cm = 6.68458712e-14 ang_per_cm = 1.0e8 m_per_fpc = 0.0324077929 kpc_per_mpc = 1.0 / mpc_per_kpc pc_per_mpc = 1.0 / mpc_per_pc au_per_mpc = 1.0 / mpc_per_au rsun_per_mpc = 1.0 / mpc_per_rsun rearth_per_mpc = 1.0 / mpc_per_rearth rjup_per_mpc = 1.0 / mpc_per_rjup miles_per_mpc = 1.0 / mpc_per_miles km_per_mpc = 1.0 / mpc_per_km cm_per_mpc = 1.0 / mpc_per_cm cm_per_kpc = 1.0 / kpc_per_cm cm_per_km = 1.0 / km_per_cm cm_per_m = 1.0 / m_per_cm pc_per_km = 1.0 / km_per_pc cm_per_pc = 1.0 / pc_per_cm cm_per_ly = 1.0 / ly_per_cm cm_per_rsun = 1.0 / rsun_per_cm cm_per_rearth = 1.0 / rearth_per_cm cm_per_rjup = 1.0 / rjup_per_cm cm_per_au = 1.0 / au_per_cm cm_per_ang = 1.0 / ang_per_cm # time # "IAU Style Manual" by G.A. Wilkins, Comm. 5, in IAU Transactions XXB (1989) sec_per_Gyr = 31.5576e15 sec_per_Myr = 31.5576e12 sec_per_kyr = 31.5576e9 sec_per_year = 31.5576e6 sec_per_day = 86400.0 sec_per_hr = 3600.0 sec_per_min = 60.0 day_per_year = 365.25 # velocities, accelerations speed_of_light_cm_per_s = 2.99792458e10 standard_gravity_cm_per_s2 = 9.80665e2 # some constants newton_cgs = 6.67384e-8 planck_cgs = 6.62606957e-27 # temperature / energy boltzmann_constant_erg_per_K = 1.3806488e-16 erg_per_eV = 1.602176562e-12 erg_per_keV = erg_per_eV * 1.0e3 K_per_keV = erg_per_keV / boltzmann_constant_erg_per_K keV_per_K = 1.0 / K_per_keV keV_per_erg = 1.0 / erg_per_keV eV_per_erg = 1.0 / erg_per_eV kelvin_per_rankine = 5.0 / 9.0 # Solar System masses # Standish, E.M. (1995) "Report of the IAU WGAS Sub-Group on Numerical Standards", # in Highlights of Astronomy (I. Appenzeller, ed.), Table 1, # Kluwer Academic Publishers, Dordrecht. # REMARK: following masses include whole systems (planet + moons) mass_jupiter_grams = mass_sun_grams / 1047.3486 mass_mercury_grams = mass_sun_grams / 6023600.0 mass_venus_grams = mass_sun_grams / 408523.71 mass_earth_grams = mass_sun_grams / 328900.56 mass_mars_grams = mass_sun_grams / 3098708.0 mass_saturn_grams = mass_sun_grams / 3497.898 mass_uranus_grams = mass_sun_grams / 22902.98 mass_neptune_grams = mass_sun_grams / 19412.24 # flux jansky_cgs = 1.0e-23 # Cosmological constants # Calculated with H = 100 km/s/Mpc, value given in units of h^2 g cm^-3 # Multiply by h^2 to get the critical density in units of g cm^-3 rho_crit_g_cm3_h2 = 1.8784710838431654e-29 primordial_H_mass_fraction = 0.76 _primordial_mass_fraction = { "H": primordial_H_mass_fraction, "He": (1 - primordial_H_mass_fraction), } # Misc. Approximations mass_mean_atomic_cosmology = 1.22 mass_mean_atomic_galactic = 2.3 # Miscellaneous HUGE = 1.0e90 TINY = 1.0e-40 # Planck units hbar_cgs = 0.5 * planck_cgs / np.pi planck_mass_grams = np.sqrt(hbar_cgs * speed_of_light_cm_per_s / newton_cgs) planck_length_cm = np.sqrt(hbar_cgs * newton_cgs / speed_of_light_cm_per_s**3) planck_time_s = planck_length_cm / speed_of_light_cm_per_s planck_energy_erg = ( planck_mass_grams * speed_of_light_cm_per_s * speed_of_light_cm_per_s ) planck_temperature_K = planck_energy_erg / boltzmann_constant_erg_per_K planck_charge_esu = np.sqrt(hbar_cgs * speed_of_light_cm_per_s) # Imperial and other non-metric units grams_per_pound = 453.59237 pascal_per_atm = 101325.0 yt-project-yt-f043ac8/yt/utilities/png_writer.py000066400000000000000000000013171510711153200220550ustar00rootroot00000000000000from io import BytesIO import PIL from PIL import Image from PIL.PngImagePlugin import PngInfo from .._version import __version__ as yt_version def call_png_write_png(buffer, fileobj, dpi): metadata = PngInfo() metadata.add_text("Software", f"PIL-{PIL.__version__}|yt-{yt_version}") Image.fromarray(buffer).save( fileobj, dpi=(dpi, dpi), format="png", pnginfo=metadata ) def write_png(buffer, filename, dpi=100): with open(filename, "wb") as fileobj: call_png_write_png(buffer, fileobj, dpi) def write_png_to_string(buffer, dpi=100): fileobj = BytesIO() call_png_write_png(buffer, fileobj, dpi) png_str = fileobj.getvalue() fileobj.close() return png_str yt-project-yt-f043ac8/yt/utilities/rpdb.py000066400000000000000000000063751510711153200206350ustar00rootroot00000000000000import cmd import signal import sys import traceback from io import StringIO from xmlrpc.client import ServerProxy from xmlrpc.server import SimpleXMLRPCServer from yt.config import ytcfg class PdbXMLRPCServer(SimpleXMLRPCServer): """ shutdown-enabled XMLRPCServer from http://code.activestate.com/recipes/114579/ """ finished = False def register_signal(self, signum): signal.signal(signum, self.signal_handler) def signal_handler(self, signum, frame): print("Caught signal", signum) self.shutdown() def shutdown(self): self.finished = True return 1 def serve_forever(self): while not self.finished: self.handle_request() print("DONE SERVING") def rpdb_excepthook(exc_type, exc, tb): traceback.print_exception(exc_type, exc, tb) task = ytcfg.get("yt", "internals", "global_parallel_rank") size = ytcfg.get("yt", "internals", "global_parallel_size") print(f"Starting RPDB server on task {task} ; connect with 'yt rpdb -t {task}'") handler = pdb_handler(tb) server = PdbXMLRPCServer(("localhost", 8010 + task)) server.register_introspection_functions() server.register_instance(handler) server.register_function(server.shutdown) server.serve_forever() server.server_close() if size > 1: from mpi4py import MPI # This COMM_WORLD is okay. We want to barrierize here, while waiting # for shutdown from the rest of the parallel group. If you are running # with --rpdb it is assumed you know what you are doing and you won't # let this get out of hand. MPI.COMM_WORLD.Barrier() class pdb_handler: def __init__(self, tb): import pdb self.cin = StringIO() sys.stdin = self.cin self.cout = StringIO() sys.stdout = self.cout sys.stderr = self.cout self.debugger = pdb.Pdb(stdin=self.cin, stdout=self.cout) self.debugger.reset() self.debugger.setup(tb.tb_frame, tb) def execute(self, line): tt = self.cout.tell() self.debugger.onecmd(line) self.cout.seek(tt) return self.cout.read() class rpdb_cmd(cmd.Cmd): def __init__(self, proxy): self.proxy = proxy cmd.Cmd.__init__(self) print(self.proxy.execute("bt")) def default(self, line): print(self.proxy.execute(line)) def do_shutdown(self, args): print(self.proxy.shutdown()) return True def do_help(self, line): print(self.proxy.execute(f"help {line}")) def postcmd(self, stop, line): return stop def postloop(self): try: self.proxy.shutdown() except Exception: pass __header = """ You're in a remote PDB session with task %(task)s You can run PDB commands, and when you're done, type 'shutdown' to quit. """ def run_rpdb(task=None): port = 8010 if task is None: try: task + int(sys.argv[-1]) except Exception: pass port += task sp = ServerProxy(f"http://localhost:{port}/") try: pp = rpdb_cmd(sp) except OSError: print("Connection refused. Is the server running?") sys.exit(1) pp.cmdloop(__header % {"task": port - 8010}) yt-project-yt-f043ac8/yt/utilities/sdf.py000066400000000000000000001334011510711153200204510ustar00rootroot00000000000000import os from collections import UserDict from io import StringIO import numpy as np from yt.funcs import mylog def get_thingking_deps(): try: from thingking.arbitrary_page import PageCacheURL from thingking.httpmmap import HTTPArray except ImportError: raise ImportError( "This functionality requires the thingking package to be installed" ) from None return HTTPArray, PageCacheURL _types = { "int16_t": "int16", "uint16_t": "uint16", "int": "int32", "int32_t": "int32", "uint32_t": "uint32", "int64_t": "int64", "uint64_t": "uint64", "float": "float32", "double": "float64", "unsigned int": "I", "unsigned char": "B", "char": "B", } _rev_types = {} for v, t in _types.items(): _rev_types[t] = v _rev_types["= left, axis=1) np.logical_and(mask, np.all(pos < right, axis=1), mask) else: np.logical_and(mask, np.all(pos >= left, axis=1), mask) np.logical_and(mask, np.all(pos < right, axis=1), mask) return mask return myfilter def sphere_filter(center, radius, domain_width): def myfilter(chunk, mask=None): pos = np.array([chunk["x"], chunk["y"], chunk["z"]]).T left = center - radius # This hurts, but is useful for periodicity. Probably should check # first if it is even needed for a given left/right for i in range(3): pos[:, i] = np.mod(pos[:, i] - left[i], domain_width[i]) + left[i] # Now get all particles that are within the radius if mask is None: mask = ((pos - center) ** 2).sum(axis=1) ** 0.5 < radius else: np.multiply(mask, np.linalg.norm(pos - center, 2) < radius, mask) return mask return myfilter def _ensure_xyz_fields(fields): for f in "xyz": if f not in fields: fields.append(f) def spread_bitsv(ival, level): res = np.zeros_like(ival, dtype="int64") for i in range(level): ares = np.bitwise_and(ival, 1 << i) << (i * 2) np.bitwise_or(res, ares, res) return res def get_keyv(iarr, level): i1, i2, i3 = (v.astype("int64") for v in iarr) i1 = spread_bitsv(i1, level) i2 = spread_bitsv(i2, level) << 1 i3 = spread_bitsv(i3, level) << 2 np.bitwise_or(i1, i2, i1) np.bitwise_or(i1, i3, i1) return i1 class DataStruct: """docstring for DataStruct""" _offset = 0 def __init__(self, dtypes, num, filename): self.filename = filename self.dtype = np.dtype(dtypes) self.size = num self.itemsize = self.dtype.itemsize self.data = {} self.handle = None def set_offset(self, offset): self._offset = offset if self.size == -1: file_size = os.path.getsize(self.filename) file_size -= offset self.size = float(file_size) / self.itemsize assert int(self.size) == self.size def build_memmap(self): assert self.size != -1 self.handle = np.memmap( self.filename, dtype=self.dtype, mode="r", shape=self.size, offset=self._offset, ) for k in self.dtype.names: self.data[k] = self.handle[k] def __del__(self): if self.handle is not None: try: self.handle.close() except AttributeError: pass del self.handle self.handle = None def __getitem__(self, key): mask = None if isinstance(key, (int, np.integer)): if key == -1: key = slice(-1, None) else: key = slice(key, key + 1) elif isinstance(key, np.ndarray): mask = key key = slice(None, None) if not isinstance(key, slice): raise NotImplementedError if key.start is None: key = slice(0, key.stop) if key.stop is None: key = slice(key.start, self.shape) if key.start < 0: key = slice(self.size + key.start, key.stop) if key.stop < 0: key = slice(key.start, self.size + key.stop) arr = self.handle[key.start : key.stop] if mask is None: return arr else: return arr[mask] class RedirectArray: """docstring for RedirectArray""" def __init__(self, http_array, key): self.http_array = http_array self.key = key self.size = http_array.shape self.dtype = http_array.dtype[key] def __getitem__(self, sl): if isinstance(sl, int): return self.http_array[sl][self.key][0] return self.http_array[sl][self.key] class HTTPDataStruct(DataStruct): """docstring for HTTPDataStruct""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) HTTPArray, PageCacheURL = get_thingking_deps() self.HTTPArray = HTTPArray self.pcu = PageCacheURL(self.filename) def set_offset(self, offset): self._offset = offset if self.size == -1: # Read small piece: file_size = self.pcu.total_size file_size -= offset self.size = float(file_size) / self.itemsize assert int(self.size) == self.size def build_memmap(self): assert self.size != -1 mylog.info( "Building memmap with offset: %i and size %i", self._offset, self.size ) self.handle = self.HTTPArray( self.filename, dtype=self.dtype, shape=self.size, offset=self._offset ) for k in self.dtype.names: self.data[k] = RedirectArray(self.handle, k) class SDFRead(UserDict): _eof = "SDF-EO" _data_struct = DataStruct def __init__(self, filename=None, header=None): r"""Read an SDF file, loading parameters and variables. Given an SDF file (see https://bitbucket.org/JohnSalmon/sdf), parse the ASCII header and construct numpy memmap array access. Parameters ---------- filename: string The filename associated with the data to be loaded. header: string, optional If separate from the data file, a file containing the header can be specified. Default: None. Returns ------- self : SDFRead object Dict-like container of parameters and data. References ---------- SDF is described here: J. K. Salmon and M. S. Warren. Self-Describing File (SDF) Library. Zenodo, Jun 2014. URL https://bitbucket.org/JohnSalmon/sdf. Examples -------- >>> sdf = SDFRead("data.sdf", header="data.hdr") >>> print(sdf.parameters) >>> print(sdf["x"]) """ super().__init__() self.filename = filename if header is None: header = filename self.header = header self.parameters = {} self.structs = [] self.comments = [] if filename is not None: self.parse_header() self.set_offsets() self.load_memmaps() def write(self, filename): f = open(filename, "w") f.write("# SDF 1.0\n") f.write(f"parameter byteorder = {self.parameters['byteorder']};\n") for c in self.comments: if "\x0c" in c: continue if "SDF 1.0" in c: continue f.write(f"{c}") for k, v in sorted(self.parameters.items()): if k == "byteorder": continue try: t = _rev_types[v.dtype.name] except Exception: t = type(v).__name__ if t == str.__name__: f.write(f'parameter {k} = "{v}";\n') else: f.write(f"{t} {k} = {v};\n") struct_order = [] for s in self.structs: f.write("struct {\n") to_write = [] for var in s.dtype.descr: k, v = var[0], _rev_types[var[1]] to_write.append(k) f.write(f"\t{v} {k};\n") f.write("}[%i];\n" % s.size) struct_order.append(to_write) f.write("#\x0c\n") f.write("# SDF-EOH\n") return struct_order, f def __repr__(self): disp = f" file: {self.filename}\n" disp += "parameters: \n" for k, v in self.parameters.items(): disp += f"\t{k}: {v}\n" disp += "arrays: \n" for k, v in self.items(): disp += f"\t{k}[{v.size}]\n" return disp def parse_header(self): """docstring for parse_header""" # Pre-process ascfile = open(self.header) while True: l = ascfile.readline() if self._eof in l: break self.parse_line(l, ascfile) hoff = ascfile.tell() ascfile.close() if self.header != self.filename: hoff = 0 self.parameters["header_offset"] = hoff def parse_line(self, line, ascfile): """Parse a line of sdf""" if "struct" in line: self.parse_struct(line, ascfile) return if "#" in line: self.comments.append(line) return spl = _lstrip(line.split("=")) vtype, vname = _lstrip(spl[0].split()) vname = vname.strip("[]") vval = spl[-1].strip(";") if vtype == "parameter": self.parameters[vname] = vval return elif vtype == "char": vtype = "str" try: vval = eval("np." + vtype + f"({vval})") except AttributeError: if vtype not in _types: mylog.warning("Skipping parameter %s", vname) return vval = eval("np." + _types[vtype] + f"({vval})") self.parameters[vname] = vval def parse_struct(self, line, ascfile): assert "struct" in line str_types = [] l = ascfile.readline() while "}" not in l: vtype, vnames = _get_struct_vars(l) for v in vnames: str_types.append((v, vtype)) l = ascfile.readline() spec_chars = r"{}[]\;\n\\" num = l.strip(spec_chars) if len(num) == 0: # We need to compute the number of records. The DataStruct will # handle this. num = "-1" num = int(num) struct = self._data_struct(str_types, num, self.filename) self.structs.append(struct) return def set_offsets(self): running_off = self.parameters["header_offset"] for struct in self.structs: struct.set_offset(running_off) running_off += struct.size * struct.itemsize return def load_memmaps(self): for struct in self.structs: struct.build_memmap() self.update(struct.data) class HTTPSDFRead(SDFRead): r"""Read an SDF file hosted on the internet. Given an SDF file (see https://bitbucket.org/JohnSalmon/sdf), parse the ASCII header and construct numpy memmap array access. Parameters ---------- filename : string The filename associated with the data to be loaded. header : string, optional If separate from the data file, a file containing the header can be specified. Default: None. Returns ------- self : SDFRead object Dict-like container of parameters and data. References ---------- SDF is described here: J. K. Salmon and M. S. Warren. Self-Describing File (SDF) Library. Zenodo, Jun 2014. URL https://bitbucket.org/JohnSalmon/sdf. Examples -------- >>> sdf = SDFRead("data.sdf", header="data.hdr") >>> print(sdf.parameters) >>> print(sdf["x"]) """ _data_struct = HTTPDataStruct def __init__(self, *args, **kwargs): HTTPArray, _ = get_thingking_deps() self.HTTPArray = HTTPArray super().__init__(*args, **kwargs) def parse_header(self): """docstring for parse_header""" # Pre-process ascfile = self.HTTPArray(self.header) max_header_size = 1024 * 1024 lines = StringIO(ascfile[:max_header_size].data[:]) while True: l = lines.readline() if self._eof in l: break self.parse_line(l, lines) hoff = lines.tell() if self.header != self.filename: hoff = 0 self.parameters["header_offset"] = hoff def load_sdf(filename, header=None): r"""Load an SDF file. Given an SDF file (see https://bitbucket.org/JohnSalmon/sdf), parse the ASCII header and construct numpy memmap array access. The file can be either local (on a hard drive, for example), or remote (on the World Wide Web). Parameters ---------- filename: string The filename or WWW address associated with the data to be loaded. header: string, optional If separate from the data file, a file containing the header can be specified. Default: None. Returns ------- sdf : SDFRead object Dict-like container of parameters and data. References ---------- SDF is described here: J. K. Salmon and M. S. Warren. Self-Describing File (SDF) Library. Zenodo, Jun 2014. URL https://bitbucket.org/JohnSalmon/sdf. Examples -------- >>> sdf = SDFRead("data.sdf", header="data.hdr") >>> print(sdf.parameters) >>> print(sdf["x"]) """ if "http" in filename: sdf = HTTPSDFRead(filename, header=header) else: sdf = SDFRead(filename, header=header) return sdf def _shift_periodic(pos, left, right, domain_width): """ Periodically shift positions that are right of left+domain_width to the left, and those left of right-domain_width to the right. """ for i in range(3): mask = pos[:, i] >= left[i] + domain_width[i] pos[mask, i] -= domain_width[i] mask = pos[:, i] < right[i] - domain_width[i] pos[mask, i] += domain_width[i] return class SDFIndex: """docstring for SDFIndex This provides an index mechanism into the full SDF Dataset. Most useful class methods: get_cell_data(level, cell_iarr, fields) iter_bbox_data(left, right, fields) iter_bbox_data(left, right, fields) """ def __init__(self, sdfdata, indexdata, level=None): super().__init__() self.sdfdata = sdfdata self.indexdata = indexdata if level is None: level = self.indexdata.parameters.get("level", None) self.level = level self.rmin = None self.rmax = None self.domain_width = None self.domain_buffer = 0 self.domain_dims = 0 self.domain_active_dims = 0 self.wandering_particles = False self.valid_indexdata = True self.masks = { "p": int("011" * level, 2), "t": int("101" * level, 2), "r": int("110" * level, 2), "z": int("011" * level, 2), "y": int("101" * level, 2), "x": int("110" * level, 2), 2: int("011" * level, 2), 1: int("101" * level, 2), 0: int("110" * level, 2), } self.dim_slices = { "p": slice(0, None, 3), "t": slice(1, None, 3), "r": slice(2, None, 3), "z": slice(0, None, 3), "y": slice(1, None, 3), "x": slice(2, None, 3), 2: slice(0, None, 3), 1: slice(1, None, 3), 0: slice(2, None, 3), } self.set_bounds() self._midx_version = self.indexdata.parameters.get("midx_version", 0) if self._midx_version >= 1.0: max_key = self.get_key(np.array([2**self.level - 1] * 3, dtype="int64")) else: max_key = self.indexdata["index"][-1] self._max_key = max_key def _fix_rexact(self, rmin, rmax): center = 0.5 * (rmax + rmin) mysize = rmax - rmin mysize *= 1.0 + 4.0 * np.finfo(np.float32).eps self.rmin = center - 0.5 * mysize self.rmax = center + 0.5 * mysize def set_bounds(self): if ( "x_min" in self.sdfdata.parameters and "x_max" in self.sdfdata.parameters ) or ( "theta_min" in self.sdfdata.parameters and "theta_max" in self.sdfdata.parameters ): if "x_min" in self.sdfdata.parameters: rmin = np.array( [ self.sdfdata.parameters["x_min"], self.sdfdata.parameters["y_min"], self.sdfdata.parameters["z_min"], ] ) rmax = np.array( [ self.sdfdata.parameters["x_max"], self.sdfdata.parameters["y_max"], self.sdfdata.parameters["z_max"], ] ) elif "theta_min" in self.sdfdata.parameters: rmin = np.array( [ self.sdfdata.parameters["r_min"], self.sdfdata.parameters["theta_min"], self.sdfdata.parameters["phi_min"], ] ) rmax = np.array( [ self.sdfdata.parameters["r_max"], self.sdfdata.parameters["theta_max"], self.sdfdata.parameters["phi_max"], ] ) self._fix_rexact(rmin, rmax) self.true_domain_left = self.rmin.copy() self.true_domain_right = self.rmax.copy() self.true_domain_width = self.rmax - self.rmin self.domain_width = self.rmax - self.rmin self.domain_dims = 1 << self.level self.domain_buffer = 0 self.domain_active_dims = self.domain_dims else: mylog.debug("Setting up older data") rx = self.sdfdata.parameters.get("Rx") ry = self.sdfdata.parameters.get("Ry") rz = self.sdfdata.parameters.get("Rz") a = self.sdfdata.parameters.get("a", 1.0) rmin = -a * np.array([rx, ry, rz]) rmax = a * np.array([rx, ry, rz]) self.true_domain_left = rmin.copy() self.true_domain_right = rmax.copy() self.true_domain_width = rmax - rmin expand_root = 0.0 morton_xyz = self.sdfdata.parameters.get("morton_xyz", False) if not morton_xyz: mylog.debug("Accounting for wandering particles") self.wandering_particles = True ic_Nmesh = self.sdfdata.parameters.get("ic_Nmesh", 0) # Expand root for non power-of-2 if ic_Nmesh != 0: f2 = 1 << int(np.log2(ic_Nmesh - 1) + 1) if f2 != ic_Nmesh: expand_root = 1.0 * f2 / ic_Nmesh - 1.0 mylog.debug("Expanding: %s, %s, %s", f2, ic_Nmesh, expand_root) rmin *= 1.0 + expand_root rmax *= 1.0 + expand_root self._fix_rexact(rmin, rmax) self.domain_width = self.rmax - self.rmin self.domain_dims = 1 << self.level self.domain_buffer = ( self.domain_dims - int(self.domain_dims / (1.0 + expand_root)) ) / 2 self.domain_active_dims = self.domain_dims - 2 * self.domain_buffer mylog.debug("MIDX rmin: %s, rmax: %s", self.rmin, self.rmax) mylog.debug( "MIDX: domain_width: %s, domain_dims: %s, domain_active_dims: %s ", self.domain_width, self.domain_dims, self.domain_active_dims, ) def spread_bits(self, ival, level=None): if level is None: level = self.level res = 0 for i in range(level): res |= ((ival >> i) & 1) << (i * 3) return res def get_key(self, iarr, level=None): if level is None: level = self.level i1, i2, i3 = (v.astype("int64") for v in iarr) return ( self.spread_bits(i1, level) | self.spread_bits(i2, level) << 1 | self.spread_bits(i3, level) << 2 ) def spread_bitsv(self, ival, level=None): if level is None: level = self.level return spread_bitsv(ival, level) def get_keyv(self, iarr, level=None): if level is None: level = self.level return get_keyv(iarr, level) def get_key_slow(self, iarr, level=None): if level is None: level = self.level i1, i2, i3 = iarr rep1 = np.binary_repr(i1, width=self.level) rep2 = np.binary_repr(i2, width=self.level) rep3 = np.binary_repr(i3, width=self.level) inter = np.zeros(self.level * 3, dtype="c") inter[self.dim_slices[0]] = rep1 inter[self.dim_slices[1]] = rep2 inter[self.dim_slices[2]] = rep3 return int(inter.tobytes(), 2) def get_key_ijk(self, i1, i2, i3, level=None): return self.get_key(np.array([i1, i2, i3]), level=level) def get_slice_key(self, ind, dim="r"): slb = np.binary_repr(ind, width=self.level) expanded = np.array([0] * self.level * 3, dtype="c") expanded[self.dim_slices[dim]] = slb return int(expanded.tobytes(), 2) def get_ind_from_key(self, key, dim="r"): ind = [0, 0, 0] br = np.binary_repr(key, width=self.level * 3) for dim in range(3): ind[dim] = int(br[self.dim_slices[dim]], 2) return ind def get_slice_chunks(self, slice_dim, slice_index): sl_key = self.get_slice_key(slice_index, dim=slice_dim) mask = (self.indexdata["index"] & ~self.masks[slice_dim]) == sl_key offsets = self.indexdata["base"][mask] lengths = self.indexdata["len"][mask] return mask, offsets, lengths def get_ibbox_slow(self, ileft, iright): """ Given left and right indices, return a mask and set of offsets+lengths into the sdf data. """ mask = np.zeros(self.indexdata["index"].shape, dtype="bool") ileft = np.array(ileft, dtype="int64") iright = np.array(iright, dtype="int64") for i in range(3): left_key = self.get_slice_key(ileft[i], dim=i) right_key = self.get_slice_key(iright[i], dim=i) dim_inds = self.indexdata["index"] & ~self.masks[i] mask *= (dim_inds >= left_key) * (dim_inds <= right_key) del dim_inds offsets = self.indexdata["base"][mask] lengths = self.indexdata["len"][mask] return mask, offsets, lengths def get_ibbox(self, ileft, iright): """ Given left and right indices, return a mask and set of offsets+lengths into the sdf data. """ # print('Getting data from ileft to iright:', ileft, iright) ix, iy, iz = (iright - ileft) * 1j mylog.debug("MIDX IBBOX: %s %s %s %s %s", ileft, iright, ix, iy, iz) # plus 1 that is sliced, plus a bit since mgrid is not inclusive Z, Y, X = np.mgrid[ ileft[2] : iright[2] + 1.01, ileft[1] : iright[1] + 1.01, ileft[0] : iright[0] + 1.01, ] mask = slice(0, -1, None) X = X[mask, mask, mask].astype("int64").ravel() Y = Y[mask, mask, mask].astype("int64").ravel() Z = Z[mask, mask, mask].astype("int64").ravel() if self.wandering_particles: # Need to get padded bbox around the border to catch # wandering particles. dmask = X < self.domain_buffer dmask += Y < self.domain_buffer dmask += Z < self.domain_buffer dmask += X >= self.domain_dims dmask += Y >= self.domain_dims dmask += Z >= self.domain_dims dinds = self.get_keyv([X[dmask], Y[dmask], Z[dmask]]) dinds = dinds[dinds < self._max_key] dinds = dinds[self.indexdata["len"][dinds] > 0] # print('Getting boundary layers for wanderers, cells: %i' % dinds.size) # Correct For periodicity X[X < self.domain_buffer] += self.domain_active_dims Y[Y < self.domain_buffer] += self.domain_active_dims Z[Z < self.domain_buffer] += self.domain_active_dims X[X >= self.domain_buffer + self.domain_active_dims] -= self.domain_active_dims Y[Y >= self.domain_buffer + self.domain_active_dims] -= self.domain_active_dims Z[Z >= self.domain_buffer + self.domain_active_dims] -= self.domain_active_dims # print('periodic:', X.min(), X.max(), Y.min(), Y.max(), Z.min(), Z.max()) indices = self.get_keyv([X, Y, Z]) # Only mask out if we are actually getting data rather than getting indices into # a space. if self.valid_indexdata: indices = indices[indices < self._max_key] # indices = indices[self.indexdata['len'][indices] > 0] # Faster for sparse lookups. Need better heuristic. new_indices = [] for ind in indices: if self.indexdata["len"][ind] > 0: new_indices.append(ind) indices = np.array(indices, dtype="int64") # indices = np.array([self.get_key_ijk(x, y, z) for x, y, z in zip(X, Y, Z)]) # Here we sort the indices to batch consecutive reads together. if self.wandering_particles: indices = np.sort(np.append(indices, dinds)) else: indices = np.sort(indices) return indices def get_bbox(self, left, right): """ Given left and right indices, return a mask and set of offsets+lengths into the sdf data. """ ileft = np.floor((left - self.rmin) / self.domain_width * self.domain_dims) iright = np.floor((right - self.rmin) / self.domain_width * self.domain_dims) if np.any(iright - ileft) > self.domain_dims: mylog.warning( "Attempting to get data from bounding box larger than the domain. " "You may want to check your units." ) # iright[iright <= ileft+1] += 1 return self.get_ibbox(ileft, iright) def get_nparticles_bbox(self, left, right): """ Given left and right edges, return total number of particles present. """ ileft = np.floor((left - self.rmin) / self.domain_width * self.domain_dims) iright = np.floor((right - self.rmin) / self.domain_width * self.domain_dims) indices = self.get_ibbox(ileft, iright) npart = 0 for ind in indices: npart += self.indexdata["len"][ind] return npart def get_data(self, chunk, fields): data = {} for field in fields: data[field] = self.sdfdata[field][chunk] return data def get_next_nonzero_chunk(self, key, stop=None): # These next two while loops are to squeeze the keys if they are empty. # Would be better to go through and set base equal to the last non-zero base. if stop is None: stop = self._max_key while key < stop: if self.indexdata["len"][key] == 0: # print('Squeezing keys, incrementing') key += 1 else: break return key def get_previous_nonzero_chunk(self, key, stop=None): # These next two while loops are to squeeze the keys if they are empty. # Would be better to go through and set base equal to the last non-zero base. if stop is None: stop = self.indexdata["index"][0] while key > stop: if self.indexdata["len"][key] == 0: # print('Squeezing keys, decrementing') key -= 1 else: break return key def iter_data(self, inds, fields): num_inds = len(inds) num_reads = 0 mylog.debug("MIDX Reading %i chunks", num_inds) i = 0 while i < num_inds: ind = inds[i] base = self.indexdata["base"][ind] length = self.indexdata["len"][ind] # Concatenate aligned reads nexti = i + 1 combined = 0 while nexti < num_inds: nextind = inds[nexti] # print( # "b: %i l: %i end: %i next: %i" # % (base, length, base + length, self.indexdata["base"][nextind]) # ) if combined < 1024 and base + length == self.indexdata["base"][nextind]: length += self.indexdata["len"][nextind] i += 1 nexti += 1 combined += 1 else: break chunk = slice(base, base + length) mylog.debug( "Reading chunk %i of length %i after catting %i starting at %i", i, length, combined, ind, ) num_reads += 1 if length > 0: data = self.get_data(chunk, fields) yield data del data i += 1 mylog.debug("Read %i chunks, batched into %i reads", num_inds, num_reads) def filter_particles(self, myiter, myfilter): for data in myiter: mask = myfilter(data) if mask.sum() == 0: continue filtered = {} for f in data.keys(): filtered[f] = data[f][mask] yield filtered def filter_bbox(self, left, right, myiter): """ Filter data by masking out data outside of a bbox defined by left/right. Account for periodicity of data, allowing left/right to be outside of the domain. """ for data in myiter: # mask = np.zeros_like(data, dtype='bool') pos = np.array([data["x"].copy(), data["y"].copy(), data["z"].copy()]).T DW = self.true_domain_width # This hurts, but is useful for periodicity. Probably should check first # if it is even needed for a given left/right _shift_periodic(pos, left, right, DW) # Now get all particles that are within the bbox mask = np.all(pos >= left, axis=1) * np.all(pos < right, axis=1) # print('Mask shape, sum:', mask.shape, mask.sum()) mylog.debug( "Filtering particles, returning %i out of %i", mask.sum(), mask.shape[0] ) if not np.any(mask): continue filtered = {ax: pos[:, i][mask] for i, ax in enumerate("xyz")} for f in data.keys(): if f in "xyz": continue filtered[f] = data[f][mask] # for i, ax in enumerate('xyz'): # #print(left, right) # assert np.all(filtered[ax] >= left[i]) # assert np.all(filtered[ax] < right[i]) yield filtered def filter_sphere(self, center, radius, myiter): """ Filter data by masking out data outside of a sphere defined by a center and radius. Account for periodicity of data, allowing left/right to be outside of the domain. """ # Get left/right for periodicity considerations left = center - radius right = center + radius for data in myiter: pos = np.array([data["x"].copy(), data["y"].copy(), data["z"].copy()]).T DW = self.true_domain_width _shift_periodic(pos, left, right, DW) # Now get all particles that are within the sphere mask = ((pos - center) ** 2).sum(axis=1) ** 0.5 < radius mylog.debug( "Filtering particles, returning %i out of %i", mask.sum(), mask.shape[0] ) if not np.any(mask): continue filtered = {ax: pos[:, i][mask] for i, ax in enumerate("xyz")} for f in data.keys(): if f in "xyz": continue filtered[f] = data[f][mask] yield filtered def iter_filtered_bbox_fields(self, left, right, data, pos_fields, fields): """ This function should be destroyed, as it will only work with units. """ kpcuq = left.in_units("kpccm").uq mpcuq = left.in_units("Mpccm/h").uq DW = (self.true_domain_width * kpcuq).in_units("Mpc/h") if pos_fields is None: pos_fields = "x", "y", "z" xf, yf, zf = pos_fields mylog.debug("Using position fields: %s", pos_fields) # I'm sorry. pos = ( mpcuq * np.array( [ data[xf].in_units("Mpccm/h"), data[yf].in_units("Mpccm/h"), data[zf].in_units("Mpccm/h"), ] ).T ) # This hurts, but is useful for periodicity. Probably should check first # if it is even needed for a given left/right _shift_periodic(pos, left, right, DW) mylog.debug( "Periodic filtering, %s %s %s %s", left, right, pos.min(axis=0), pos.max(axis=0), ) # Now get all particles that are within the bbox mask = np.all(pos >= left, axis=1) * np.all(pos < right, axis=1) mylog.debug( "Filtering particles, returning %i out of %i", mask.sum(), mask.shape[0] ) if np.any(mask): for i, f in enumerate(pos_fields): yield f, pos[:, i][mask] for f in fields: if f in pos_fields: continue # print('yielding nonpos field', f) yield f, data[f][mask] def iter_bbox_data(self, left, right, fields): """ Iterate over all data within a bounding box defined by a left and a right. """ _ensure_xyz_fields(fields) mylog.debug("MIDX Loading region from %s to %s", left, right) inds = self.get_bbox(left, right) # Need to put left/right in float32 to avoid fp roundoff errors # in the bbox later. # left = left.astype('float32') # right = right.astype('float32') # my_filter = bbox_filter(left, right, self.true_domain_width) yield from self.filter_bbox(left, right, self.iter_data(inds, fields)) # for dd in self.filter_particles( # self.iter_data(inds, fields), # my_filter): # yield dd def iter_sphere_data(self, center, radius, fields): """ Iterate over all data within some sphere defined by a center and a radius. """ _ensure_xyz_fields(fields) mylog.debug("MIDX Loading spherical region %s to %s", center, radius) inds = self.get_bbox(center - radius, center + radius) yield from self.filter_sphere(center, radius, self.iter_data(inds, fields)) def iter_ibbox_data(self, left, right, fields): mylog.debug("MIDX Loading region from %s to %s", left, right) inds = self.get_ibbox(left, right) return self.iter_data(inds, fields) def get_contiguous_chunk(self, left_key, right_key, fields): lbase = 0 if left_key > self._max_key: raise RuntimeError( "Left key is too large. Key: %i Max Key: %i" % (left_key, self._max_key) ) right_key = min(right_key, self._max_key) left_key = self.get_next_nonzero_chunk(left_key, right_key - 1) right_key = self.get_previous_nonzero_chunk(right_key, left_key) lbase = self.indexdata["base"][left_key] rbase = self.indexdata["base"][right_key] rlen = self.indexdata["len"][right_key] length = rbase + rlen - lbase if length > 0: mylog.debug( "Getting contiguous chunk of size %i starting at %i", length, lbase ) return self.get_data(slice(lbase, lbase + length), fields) def get_key_data(self, key, fields): if key > self._max_key: raise RuntimeError( "Left key is too large. Key: %i Max Key: %i" % (key, self._max_key) ) base = self.indexdata["base"][key] length = self.indexdata["len"][key] - base if length > 0: mylog.debug( "Getting contiguous chunk of size %i starting at %i", length, base ) return self.get_data(slice(base, base + length), fields) def iter_slice_data(self, slice_dim, slice_index, fields): mask, offsets, lengths = self.get_slice_chunks(slice_dim, slice_index) for off, l in zip(offsets, lengths, strict=True): data = {} chunk = slice(off, off + l) for field in fields: data[field] = self.sdfdata[field][chunk] yield data del data def get_key_bounds(self, level, cell_iarr): """ Get index keys for index file supplied. level: int Requested level cell_iarr: array-like, length 3 Requested cell from given level. Returns: lmax_lk, lmax_rk """ shift = self.level - level level_buff = 0 level_lk = self.get_key(cell_iarr + level_buff) level_rk = self.get_key(cell_iarr + level_buff) + 1 lmax_lk = level_lk << shift * 3 lmax_rk = ((level_rk) << shift * 3) - 1 # print( # "Level ", # level, # np.binary_repr(level_lk, width=self.level * 3), # np.binary_repr(level_rk, width=self.level * 3), # ) # print( # "Level ", # self.level, # np.binary_repr(lmax_lk, width=self.level * 3), # np.binary_repr(lmax_rk, width=self.level * 3), # ) return lmax_lk, lmax_rk def find_max_cell(self): max_cell = np.argmax(self.indexdata["len"][:]) return max_cell def find_max_cell_center(self): max_cell = self.find_max_cell() cell_ijk = np.array( self.get_ind_from_key(self.indexdata["index"][max_cell]), dtype="int64" ) position = (cell_ijk + 0.5) * (self.domain_width / self.domain_dims) + self.rmin return position def get_cell_data(self, level, cell_iarr, fields): """ Get data from requested cell This uses the raw cell index, and doesn't account for periodicity or an expanded domain (non-power of 2). level: int Requested level cell_iarr: array-like, length 3 Requested cell from given level. fields: list Requested fields Returns: cell_data: dict Dictionary of field_name, field_data """ cell_iarr = np.array(cell_iarr, dtype="int64") lk, rk = self.get_key_bounds(level, cell_iarr) mylog.debug("Reading contiguous chunk from %i to %i", lk, rk) return self.get_contiguous_chunk(lk, rk, fields) def get_cell_bbox(self, level, cell_iarr): """Get floating point bounding box for a given midx cell Returns: bbox: array-like of shape (3,2) """ cell_iarr = np.array(cell_iarr, dtype="int64") cell_width = self.get_cell_width(level) le = self.rmin + cell_iarr * cell_width re = le + cell_width bbox = np.array([le, re]).T assert bbox.shape == (3, 2) return bbox def iter_padded_bbox_data(self, level, cell_iarr, pad, fields): """ Yields data chunks for a cell on the given level plus a padding around the cell, for a list of fields. Yields: dd: A dictionaries of data. Example: for chunk in midx.iter_padded_bbox_data( 6, np.array([128]*3), 8.0, ['x','y','z','ident']): print(chunk['x'].max()) """ _ensure_xyz_fields(fields) bbox = self.get_cell_bbox(level, cell_iarr) filter_left = bbox[:, 0] - pad filter_right = bbox[:, 1] + pad # Center cell for dd in self.filter_bbox( filter_left, filter_right, [self.get_cell_data(level, cell_iarr, fields)] ): yield dd del dd # Bottom & Top pbox = bbox.copy() pbox[0, 0] -= pad[0] pbox[0, 1] += pad[0] pbox[1, 0] -= pad[1] pbox[1, 1] += pad[1] pbox[2, 0] -= pad[2] pbox[2, 1] = bbox[2, 0] for dd in self.filter_bbox( filter_left, filter_right, self.iter_bbox_data(pbox[:, 0], pbox[:, 1], fields), ): yield dd del dd pbox[2, 0] = bbox[2, 1] pbox[2, 1] = pbox[2, 0] + pad[2] for dd in self.filter_bbox( filter_left, filter_right, self.iter_bbox_data(pbox[:, 0], pbox[:, 1], fields), ): yield dd del dd # Front & Back pbox = bbox.copy() pbox[0, 0] -= pad[0] pbox[0, 1] += pad[0] pbox[1, 0] -= pad[1] pbox[1, 1] = bbox[1, 0] for dd in self.filter_bbox( filter_left, filter_right, self.iter_bbox_data(pbox[:, 0], pbox[:, 1], fields), ): yield dd del dd pbox[1, 0] = bbox[1, 1] pbox[1, 1] = pbox[1, 0] + pad[1] for dd in self.filter_bbox( filter_left, filter_right, self.iter_bbox_data(pbox[:, 0], pbox[:, 1], fields), ): yield dd del dd # Left & Right pbox = bbox.copy() pbox[0, 0] -= pad[0] pbox[0, 1] = bbox[0, 0] for dd in self.filter_bbox( filter_left, filter_right, self.iter_bbox_data(pbox[:, 0], pbox[:, 1], fields), ): yield dd del dd pbox[0, 0] = bbox[0, 1] pbox[0, 1] = pbox[0, 0] + pad[0] for dd in self.filter_bbox( filter_left, filter_right, self.iter_bbox_data(pbox[:, 0], pbox[:, 1], fields), ): yield dd del dd def get_padded_bbox_data(self, level, cell_iarr, pad, fields): """ Return list of data chunks for a cell on the given level plus a padding around the cell, for a list of fields. Returns ------- data: list A list of dictionaries of data. Examples -------- >>> chunks = midx.get_padded_bbox_data( ... 6, np.array([128] * 3), 8.0, ["x", "y", "z", "ident"] ... ) """ _ensure_xyz_fields(fields) data = [] for dd in self.iter_padded_bbox_data(level, cell_iarr, pad, fields): data.append(dd) return data def get_cell_width(self, level): return self.domain_width / 2**level def iter_padded_bbox_keys(self, level, cell_iarr, pad): """ Returns: bbox: array-like of shape (3,2) """ bbox = self.get_cell_bbox(level, cell_iarr) # Need to get all of these low_key, high_key = self.get_key_bounds(level, cell_iarr) yield from range(low_key, high_key) # Bottom & Top pbox = bbox.copy() pbox[0, 0] -= pad[0] pbox[0, 1] += pad[0] pbox[1, 0] -= pad[1] pbox[1, 1] += pad[1] pbox[2, 0] -= pad[2] pbox[2, 1] = bbox[2, 0] yield from self.get_bbox(pbox[:, 0], pbox[:, 1]) pbox[2, 0] = bbox[2, 1] pbox[2, 1] = pbox[2, 0] + pad[2] yield from self.get_bbox(pbox[:, 0], pbox[:, 1]) # Front & Back pbox = bbox.copy() pbox[0, 0] -= pad[0] pbox[0, 1] += pad[0] pbox[1, 0] -= pad[1] pbox[1, 1] = bbox[1, 0] yield from self.get_bbox(pbox[:, 0], pbox[:, 1]) pbox[1, 0] = bbox[1, 1] pbox[1, 1] = pbox[1, 0] + pad[1] yield from self.get_bbox(pbox[:, 0], pbox[:, 1]) # Left & Right pbox = bbox.copy() pbox[0, 0] -= pad[0] pbox[0, 1] = bbox[0, 0] yield from self.get_bbox(pbox[:, 0], pbox[:, 1]) pbox[0, 0] = bbox[0, 1] pbox[0, 1] = pbox[0, 0] + pad[0] yield from self.get_bbox(pbox[:, 0], pbox[:, 1]) yt-project-yt-f043ac8/yt/utilities/tests/000077500000000000000000000000001510711153200204635ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/tests/__init__.py000066400000000000000000000000001510711153200225620ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/utilities/tests/cosmology_answers.yml000066400000000000000000000060571510711153200247730ustar00rootroot00000000000000cosmologies: EdS: {hubble_constant: 0.7, omega_lambda: 0.0, omega_matter: 1.0, omega_radiation: 0.0} LCDM: {hubble_constant: 0.7, omega_lambda: 0.7, omega_matter: 0.3, omega_radiation: 0.0} omega_radiation: {hubble_constant: 0.7, omega_lambda: 0.7, omega_matter: 0.2999, omega_radiation: 0.0001} open: {hubble_constant: 0.7, omega_lambda: 0.0, omega_matter: 0.3, omega_radiation: 0.0} functions: age_integrand: answers: {EdS: 0.17677669529663687, LCDM: 0.28398091712353246, omega_radiation: 0.2839442815151592, open: 0.21926450482675733} args: [1] angular_diameter_distance: answers: {EdS: -47.63859400006291, LCDM: 74.71628440466395, omega_radiation: 74.61156201248941, open: 114.42229537588507} args: [1, 2] units: Mpc angular_scale: answers: {EdS: -47.63859400006291, LCDM: 74.71628440466395, omega_radiation: 74.61156201248941, open: 114.42229537588507} args: [1, 2] units: Mpc/radian comoving_radial_distance: answers: {EdS: 1111.3289801408714, LCDM: 1875.857637458183, omega_radiation: 1875.4640874252457, open: 1448.958605095395} args: [1, 2] units: Mpc comoving_transverse_distance: answers: {EdS: 1111.3289801408714, LCDM: 1875.857637458183, omega_radiation: 1875.4640874252457, open: 1468.3857559045264} args: [1, 2] units: Mpc comoving_volume: answers: {EdS: 5.749320615429804, LCDM: 27.64956077763837, omega_radiation: 27.632162011458195, open: 82.84024895030079} args: [1, 2] units: Gpc**3 critical_density: answers: {EdS: 1088.0152888198547, LCDM: 421.6059244176937, omega_radiation: 421.7147259465755, open: 707.2099377329054} args: [1] units: Msun/kpc**3 expansion_factor: answers: {EdS: 2.8284271247461903, LCDM: 1.7606816861659007, omega_radiation: 1.7609088562444108, open: 2.2803508501982757} args: [1] hubble_distance: answers: {EdS: 4282.749400000001, LCDM: 4282.749400000001, omega_radiation: 4282.749400000001, open: 4282.749400000001} units: Mpc hubble_parameter: answers: {EdS: 197.98989873223329, LCDM: 123.24771803161306, omega_radiation: 123.26361993710874, open: 159.6245595138793} args: [1] units: km/s/Mpc inverse_expansion_factor: answers: {EdS: 0.35355339059327373, LCDM: 0.5679618342470649, omega_radiation: 0.5678885630303184, open: 0.43852900965351466} args: [1] lookback_time: answers: {EdS: 1500.1343648374723, LCDM: 2524.828936828046, omega_radiation: 2524.3141825048465, open: 1949.3932768737345} args: [1, 2] units: Myr luminosity_distance: answers: {EdS: 5842.669112528931, LCDM: 8931.175468218224, omega_radiation: 8929.836295237686, open: 8369.418267416617} args: [1, 2] units: Mpc path_length: answers: {EdS: 1.5782728314065704, LCDM: 2.679181396559161, omega_radiation: 2.6785873459658784, open: 2.0714508064868244} args: [1, 2] path_length_function: answers: {EdS: 1.414213562373095, LCDM: 2.2718473369882597, omega_radiation: 2.2715542521212737, open: 1.7541160386140586} args: [1] yt-project-yt-f043ac8/yt/utilities/tests/test_amr_kdtree.py000066400000000000000000000016161510711153200242150ustar00rootroot00000000000000import itertools import numpy as np from numpy.testing import assert_almost_equal from yt.testing import fake_amr_ds def test_amr_kdtree_set_fields(): ds = fake_amr_ds(fields=["density", "pressure"], units=["g/cm**3", "dyn/cm**2"]) dd = ds.all_data() fields = ds.field_list dd.tiles.set_fields(fields, [True, True], False) gold = {} for i, block in enumerate(dd.tiles.traverse()): gold[i] = [data.copy() for data in block.my_data] for log_fields in itertools.product([True, False], [True, False]): dd.tiles.set_fields(fields, log_fields, False) for iblock, block in enumerate(dd.tiles.traverse()): for i in range(len(fields)): if log_fields[i]: data = block.my_data[i] else: data = np.log10(block.my_data[i]) assert_almost_equal(gold[iblock][i], data) yt-project-yt-f043ac8/yt/utilities/tests/test_chemical_formulas.py000066400000000000000000000020451510711153200255520ustar00rootroot00000000000000from numpy.testing import assert_allclose, assert_equal from yt.utilities.chemical_formulas import ChemicalFormula, compute_mu from yt.utilities.periodic_table import periodic_table _molecules = ( ("H2O_p1", (("H", 2), ("O", 1)), 1), ("H2O_m1", (("H", 2), ("O", 1)), -1), ("H2O", (("H", 2), ("O", 1)), 0), ("H2SO4", (("H", 2), ("S", 1), ("O", 4)), 0), # Now a harder one ("UuoMtUuq3", (("Uuo", 1), ("Mt", 1), ("Uuq", 3)), 0), ) def test_formulas(): for formula, components, charge in _molecules: f = ChemicalFormula(formula) w = sum(n * periodic_table[e].weight for e, n in components) assert_equal(f.charge, charge) assert_equal(f.weight, w) for (n, c1), (e, c2) in zip(components, f.elements, strict=True): assert_equal(n, e.symbol) assert_equal(c1, c2) def test_default_mu(): assert_allclose(compute_mu(None), 0.5924489101195808) assert_allclose(compute_mu("ionized"), 0.5924489101195808) assert_allclose(compute_mu("neutral"), 1.2285402715185552) yt-project-yt-f043ac8/yt/utilities/tests/test_config.py000066400000000000000000000123741510711153200233500ustar00rootroot00000000000000import contextlib import os import shutil import sys import tempfile import unittest import unittest.mock as mock from io import StringIO import yt.config import yt.utilities.command_line from yt.utilities.configure import YTConfig _TEST_PLUGIN = "_test_plugin.py" # NOTE: the normalization of the crazy camel-case will be checked _DUMMY_CFG_INI = f"""[yt] logLevel = 49 pluginfilename = {_TEST_PLUGIN} boolean_stuff = True chunk_size = 3 """ _DUMMY_CFG_TOML = f"""[yt] log_level = 49 plugin_filename = "{_TEST_PLUGIN}" boolean_stuff = true chunk_size = 3 """ @contextlib.contextmanager def captureOutput(): oldout, olderr = sys.stdout, sys.stderr try: out = [StringIO(), StringIO()] sys.stdout, sys.stderr = out yield out finally: sys.stdout, sys.stderr = oldout, olderr out[0] = out[0].getvalue() out[1] = out[1].getvalue() class SysExitException(Exception): pass class TestYTConfig(unittest.TestCase): def setUp(self): self.xdg_config_home = os.environ.get("XDG_CONFIG_HOME") self.tmpdir = tempfile.mkdtemp() os.environ["XDG_CONFIG_HOME"] = self.tmpdir os.mkdir(os.path.join(self.tmpdir, "yt")) # run inside another temporary directory to avoid polluting the # local space when we dump configuration to a local yt.toml file self.origin = os.getcwd() os.chdir(tempfile.mkdtemp()) def tearDown(self): shutil.rmtree(self.tmpdir) if self.xdg_config_home: os.environ["XDG_CONFIG_HOME"] = self.xdg_config_home else: os.environ.pop("XDG_CONFIG_HOME") os.chdir(self.origin) def _runYTConfig(self, args): args = ["yt", "config"] + args retcode = 0 with ( mock.patch.object(sys, "argv", args), mock.patch("sys.exit", side_effect=SysExitException) as exit, captureOutput() as output, ): try: yt.utilities.command_line.run_main() except SysExitException: args = exit.mock_calls[0][1] retcode = args[0] if len(args) else 0 return {"rc": retcode, "stdout": output[0], "stderr": output[1]} def _testKeyValue(self, key, val_set, val_get): info = self._runYTConfig(["set", "yt", key, str(val_set)]) self.assertEqual(info["rc"], 0) info = self._runYTConfig(["get", "yt", key]) self.assertEqual(info["rc"], 0) self.assertEqual(info["stdout"].strip(), str(val_get)) info = self._runYTConfig(["rm", "yt", key]) self.assertEqual(info["rc"], 0) def _testKeyTypeError(self, key, val1, val2, expect_error): info = self._runYTConfig(["set", "yt", key, str(val1)]) self.assertEqual(info["rc"], 0) if expect_error: with self.assertRaises(TypeError): info = self._runYTConfig(["set", "yt", key, str(val2)]) else: info = self._runYTConfig(["set", "yt", key, str(val2)]) info = self._runYTConfig(["rm", "yt", key]) self.assertEqual(info["rc"], 0) class TestYTConfigCommands(TestYTConfig): def testConfigCommands(self): def remove_spaces_and_breaks(s): return "".join(s.split()) self.assertFalse(os.path.exists(YTConfig.get_global_config_file())) info = self._runYTConfig(["--help"]) self.assertEqual(info["rc"], 0) self.assertEqual(info["stderr"], "") self.assertIn( remove_spaces_and_breaks("Get and set configuration values for yt"), remove_spaces_and_breaks(info["stdout"]), ) info = self._runYTConfig(["list"]) self.assertEqual(info["rc"], 0) self.assertEqual(info["stdout"], "") self._testKeyValue("internals.parallel", True, True) self._testKeyValue( "test_data_dir", "~/yt-data", os.path.expanduser("~/yt-data") ) self._testKeyValue( "test_data_dir", "$HOME/yt-data", os.path.expandvars("$HOME/yt-data") ) with self.assertRaises(KeyError): self._runYTConfig(["get", "yt", "foo"]) # Check TypeErrors are raised when changing the type of an entry self._testKeyTypeError("foo.bar", "test", 10, expect_error=True) self._testKeyTypeError("foo.bar", "test", False, expect_error=True) # Check no type error are raised when *not* changing the type self._testKeyTypeError("foo.bar", 10, 20, expect_error=False) self._testKeyTypeError("foo.bar", "foo", "bar", expect_error=False) def tearDown(self): if os.path.exists(YTConfig.get_global_config_file()): os.remove(YTConfig.get_global_config_file()) super().tearDown() class TestYTConfigGlobalLocal(TestYTConfig): def setUp(self): super().setUp() with open(YTConfig.get_local_config_file(), mode="w") as f: f.writelines("[yt]\n") with open(YTConfig.get_global_config_file(), mode="w") as f: f.writelines("[yt]\n") def testAmbiguousConfig(self): info = self._runYTConfig(["list"]) self.assertFalse(len(info["rc"]) == 0) for cmd in (["list", "--local"], ["list", "--global"]): info = self._runYTConfig(cmd) self.assertEqual(info["rc"], 0) yt-project-yt-f043ac8/yt/utilities/tests/test_coordinate_conversions.py000066400000000000000000000135711510711153200266620ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_almost_equal from yt.utilities.math_utils import ( get_cyl_r, get_cyl_r_component, get_cyl_theta, get_cyl_theta_component, get_cyl_z, get_cyl_z_component, get_sph_phi, get_sph_phi_component, get_sph_r, get_sph_r_component, get_sph_theta, get_sph_theta_component, ) # Randomly generated coordinates in the domain [[-1,1],[-1,1],-1,1]] coords = np.array( [ [-0.41503037, -0.22102472, -0.55774212], [0.73828247, -0.17913899, 0.64076921], [0.08922066, -0.94254844, -0.61774511], [0.10173242, -0.95789145, 0.16294352], [0.73186508, -0.3109153, 0.75728738], [0.8757989, -0.41475119, -0.57039201], [0.58040762, 0.81969082, 0.46759728], [-0.89983356, -0.9853683, -0.38355343], ] ).T def test_spherical_coordinate_conversion(): normal = [0, 0, 1] real_r = [ 0.72950559, 0.99384957, 1.13047198, 0.97696269, 1.09807968, 1.12445067, 1.10788685, 1.38843954, ] real_theta = [ 2.44113629, 0.87012028, 2.14891444, 1.4032274, 0.80979483, 2.10280198, 1.13507735, 1.85068416, ] real_phi = [ -2.65224483, -0.23804243, -1.47641858, -1.46498842, -0.40172325, -0.4422801, 0.95466734, -2.31085392, ] calc_r = get_sph_r(coords) calc_theta = get_sph_theta(coords, normal) calc_phi = get_sph_phi(coords, normal) assert_array_almost_equal(calc_r, real_r) assert_array_almost_equal(calc_theta, real_theta) assert_array_almost_equal(calc_phi, real_phi) normal = [1, 0, 0] real_theta = [ 2.17598842, 0.73347681, 1.49179079, 1.46647589, 0.8412984, 0.67793705, 1.0193883, 2.27586987, ] real_phi = [ -1.94809584, 1.843405, -2.56143151, 2.97309903, 1.96037671, -2.1995016, 0.51841239, -2.77038877, ] calc_theta = get_sph_theta(coords, normal) calc_phi = get_sph_phi(coords, normal) assert_array_almost_equal(calc_theta, real_theta) assert_array_almost_equal(calc_phi, real_phi) def test_cylindrical_coordinate_conversion(): normal = [0, 0, 1] real_r = [ 0.47021498, 0.75970506, 0.94676179, 0.96327853, 0.79516968, 0.96904193, 1.00437346, 1.3344104, ] real_theta = [ -2.65224483, -0.23804243, -1.47641858, -1.46498842, -0.40172325, -0.4422801, 0.95466734, -2.31085392, ] real_z = [ -0.55774212, 0.64076921, -0.61774511, 0.16294352, 0.75728738, -0.57039201, 0.46759728, -0.38355343, ] calc_r = get_cyl_r(coords, normal) calc_theta = get_cyl_theta(coords, normal) calc_z = get_cyl_z(coords, normal) assert_array_almost_equal(calc_r, real_r) assert_array_almost_equal(calc_theta, real_theta) assert_array_almost_equal(calc_z, real_z) normal = [1, 0, 0] real_r = [ 0.59994016, 0.66533898, 1.12694569, 0.97165149, 0.81862843, 0.70524152, 0.94368441, 1.05738542, ] real_theta = [ -1.94809584, 1.843405, -2.56143151, 2.97309903, 1.96037671, -2.1995016, 0.51841239, -2.77038877, ] real_z = [ -0.41503037, 0.73828247, 0.08922066, 0.10173242, 0.73186508, 0.8757989, 0.58040762, -0.89983356, ] calc_r = get_cyl_r(coords, normal) calc_theta = get_cyl_theta(coords, normal) calc_z = get_cyl_z(coords, normal) assert_array_almost_equal(calc_r, real_r) assert_array_almost_equal(calc_theta, real_theta) assert_array_almost_equal(calc_z, real_z) def test_spherical_coordinate_projections(): normal = [0, 0, 1] theta = get_sph_theta(coords, normal) phi = get_sph_phi(coords, normal) zero = np.tile(0, coords.shape[1]) # Purely radial field vecs = np.array( [np.sin(theta) * np.cos(phi), np.sin(theta) * np.sin(phi), np.cos(theta)] ) assert_array_almost_equal(zero, get_sph_theta_component(vecs, theta, phi, normal)) assert_array_almost_equal(zero, get_sph_phi_component(vecs, phi, normal)) # Purely toroidal field vecs = np.array([-np.sin(phi), np.cos(phi), zero]) assert_array_almost_equal(zero, get_sph_theta_component(vecs, theta, phi, normal)) assert_array_almost_equal(zero, get_sph_r_component(vecs, theta, phi, normal)) # Purely poloidal field vecs = np.array( [np.cos(theta) * np.cos(phi), np.cos(theta) * np.sin(phi), -np.sin(theta)] ) assert_array_almost_equal(zero, get_sph_phi_component(vecs, phi, normal)) assert_array_almost_equal(zero, get_sph_r_component(vecs, theta, phi, normal)) def test_cylindrical_coordinate_projections(): normal = [0, 0, 1] theta = get_cyl_theta(coords, normal) z = get_cyl_z(coords, normal) zero = np.tile(0, coords.shape[1]) # Purely radial field vecs = np.array([np.cos(theta), np.sin(theta), zero]) assert_array_almost_equal(zero, get_cyl_theta_component(vecs, theta, normal)) assert_array_almost_equal(zero, get_cyl_z_component(vecs, normal)) # Purely toroidal field vecs = np.array([-np.sin(theta), np.cos(theta), zero]) assert_array_almost_equal(zero, get_cyl_z_component(vecs, normal)) assert_array_almost_equal(zero, get_cyl_r_component(vecs, theta, normal)) # Purely z field vecs = np.array([zero, zero, z]) assert_array_almost_equal(zero, get_cyl_theta_component(vecs, theta, normal)) assert_array_almost_equal(zero, get_cyl_r_component(vecs, theta, normal)) yt-project-yt-f043ac8/yt/utilities/tests/test_cosmology.py000066400000000000000000000206561510711153200241200ustar00rootroot00000000000000import os import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.testing import assert_rel_equal, requires_file, requires_module from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.answer_testing.framework import data_dir_load from yt.utilities.cosmology import Cosmology from yt.utilities.on_demand_imports import _yaml as yaml local_dir = os.path.dirname(os.path.abspath(__file__)) def z_from_t_analytic(my_time, hubble_constant=0.7, omega_matter=0.3, omega_lambda=0.7): """ Compute the redshift from time after the big bang. This is based on Enzo's CosmologyComputeExpansionFactor.C, but altered to use physical units. """ hubble_constant = YTQuantity(hubble_constant, "100*km/s/Mpc") omega_curvature = 1.0 - omega_matter - omega_lambda OMEGA_TOLERANCE = 1e-5 ETA_TOLERANCE = 1.0e-10 # Convert the time to Time * H0. if not isinstance(my_time, YTArray): my_time = YTArray(my_time, "s") t0 = (my_time.in_units("s") * hubble_constant.in_units("1/s")).to_ndarray() # For a flat universe with omega_matter = 1, it's easy. if np.fabs(omega_matter - 1) < OMEGA_TOLERANCE and omega_lambda < OMEGA_TOLERANCE: a = np.power(1.5 * t0, 2.0 / 3.0) # For omega_matter < 1 and omega_lambda == 0 see # Peebles 1993, eq. 13-3, 13-10. # Actually, this is a little tricky since we must solve an equation # of the form eta - np.sinh(eta) + x = 0.. elif omega_matter < 1 and omega_lambda < OMEGA_TOLERANCE: x = 2 * t0 * np.power(1.0 - omega_matter, 1.5) / omega_matter # Compute eta in a three step process, first from a third-order # Taylor expansion of the formula above, then use that in a fifth-order # approximation. Then finally, iterate on the formula itself, solving for # eta. This works well because parts 1 & 2 are an excellent approximation # when x is small and part 3 converges quickly when x is large. eta = np.power(6 * x, 1.0 / 3.0) # part 1 eta = np.power(120 * x / (20 + eta * eta), 1.0 / 3.0) # part 2 mask = np.ones(eta.size, dtype=bool) max_iter = 1000 for i in range(max_iter): # part 3 eta_old = eta[mask] eta[mask] = np.arcsinh(eta[mask] + x[mask]) mask[mask] = np.fabs(eta[mask] - eta_old) >= ETA_TOLERANCE if not mask.any(): break if i == max_iter - 1: raise RuntimeError("No convergence after %d iterations." % i) # Now use eta to compute the expansion factor (eq. 13-10, part 2). a = omega_matter / (2.0 * (1.0 - omega_matter)) * (np.cosh(eta) - 1.0) # For flat universe, with non-zero omega_lambda, see eq. 13-20. elif np.fabs(omega_curvature) < OMEGA_TOLERANCE and omega_lambda > OMEGA_TOLERANCE: a = np.power(omega_matter / (1 - omega_matter), 1.0 / 3.0) * np.power( np.sinh(1.5 * np.sqrt(1.0 - omega_matter) * t0), 2.0 / 3.0 ) else: raise NotImplementedError redshift = (1.0 / a) - 1.0 return redshift def t_from_z_analytic(z, hubble_constant=0.7, omega_matter=0.3, omega_lambda=0.7): """ Compute the age of the Universe from redshift. This is based on Enzo's CosmologyComputeTimeFromRedshift.C, but altered to use physical units. """ hubble_constant = YTQuantity(hubble_constant, "100*km/s/Mpc") omega_curvature = 1.0 - omega_matter - omega_lambda # For a flat universe with omega_matter = 1, things are easy. if omega_matter == 1.0 and omega_lambda == 0.0: t0 = 2.0 / 3.0 / np.power(1 + z, 1.5) # For omega_matter < 1 and omega_lambda == 0 see # Peebles 1993, eq. 13-3, 13-10. elif omega_matter < 1 and omega_lambda == 0: eta = np.arccosh(1 + 2 * (1 - omega_matter) / omega_matter / (1 + z)) t0 = ( omega_matter / (2 * np.power(1.0 - omega_matter, 1.5)) * (np.sinh(eta) - eta) ) # For flat universe, with non-zero omega_lambda, see eq. 13-20. elif np.fabs(omega_curvature) < 1.0e-3 and omega_lambda != 0: t0 = ( 2.0 / 3.0 / np.sqrt(1 - omega_matter) * np.arcsinh( np.sqrt((1 - omega_matter) / omega_matter) / np.power(1 + z, 1.5) ) ) else: raise NotImplementedError(f"{hubble_constant}, {omega_matter}, {omega_lambda}") # Now convert from Time * H0 to time. my_time = t0 / hubble_constant return my_time def test_z_t_roundtrip(): """ Make sure t_from_z and z_from_t are consistent. """ co = Cosmology() # random sample in log(a) from -6 to 6 my_random = np.random.RandomState(6132305) la = 12 * my_random.random_sample(10000) - 6 z1 = 1 / np.power(10, la) - 1 t = co.t_from_z(z1) z2 = co.z_from_t(t) assert_rel_equal(z1, z2, 4) def test_z_t_analytic(): """ Test z/t conversions against analytic solutions. """ cosmos = ( {"hubble_constant": 0.7, "omega_matter": 0.3, "omega_lambda": 0.7}, {"hubble_constant": 0.7, "omega_matter": 1.0, "omega_lambda": 0.0}, {"hubble_constant": 0.7, "omega_matter": 0.3, "omega_lambda": 0.0}, ) for cosmo in cosmos: omega_curvature = 1 - cosmo["omega_matter"] - cosmo["omega_lambda"] co = Cosmology(omega_curvature=omega_curvature, **cosmo) # random sample in log(a) from -6 to 6 my_random = np.random.RandomState(10132324) la = 12 * my_random.random_sample(1000) - 6 z = 1 / np.power(10, la) - 1 t_an = t_from_z_analytic(z, **cosmo).to("Gyr") t_co = co.t_from_z(z).to("Gyr") assert_rel_equal( t_an, t_co, 4, err_msg=f"t_from_z does not match analytic version for cosmology {cosmo}.", ) # random sample in log(t/t0) from -3 to 1 t0 = np.power(10, 4 * my_random.random_sample(1000) - 3) t = (t0 / co.hubble_constant).to("Gyr") z_an = z_from_t_analytic(t, **cosmo) z_co = co.z_from_t(t) # compare scale factors since z approaches 0 assert_rel_equal( 1 / (1 + z_an), 1 / (1 + z_co), 5, err_msg=f"z_from_t does not match analytic version for cosmology {cosmo}.", ) def test_dark_factor(): """ Test that dark factor returns same value for when not being used and when w_0 = -1 and w_z = 0. """ co = Cosmology(w_0=-1, w_a=0, use_dark_factor=False) assert_equal(co.get_dark_factor(0), 1.0) co.use_dark_factor = True assert_equal(co.get_dark_factor(0), 1.0) @requires_module("yaml") def test_cosmology_calculator_answers(): """ Test cosmology calculator functions against previously calculated values. """ fn = os.path.join(local_dir, "cosmology_answers.yml") with open(fn) as fh: data = yaml.load(fh, Loader=yaml.FullLoader) cosmologies = data["cosmologies"] functions = data["functions"] for cname, copars in cosmologies.items(): omega_curvature = ( 1 - copars["omega_matter"] - copars["omega_lambda"] - copars["omega_radiation"] ) cosmology = Cosmology(omega_curvature=omega_curvature, **copars) for fname, finfo in functions.items(): func = getattr(cosmology, fname) args = finfo.get("args", []) val = func(*args) units = finfo.get("units") if units is not None: val.convert_to_units(units) val = float(val) err_msg = ( "{} answer has changed for {} cosmology, old: {:f}, new: {:f}.".format( fname, cname, finfo["answers"][cname], val, ) ) assert_almost_equal(val, finfo["answers"][cname], 10, err_msg=err_msg) enzotiny = "enzo_tiny_cosmology/DD0020/DD0020" @requires_module("h5py") @requires_file(enzotiny) def test_dataset_cosmology_calculator(): """ Test datasets's cosmology calculator against standalone. """ ds = data_dir_load(enzotiny) co = Cosmology( hubble_constant=ds.hubble_constant, omega_matter=ds.omega_matter, omega_lambda=ds.omega_lambda, ) v1 = ds.cosmology.comoving_radial_distance(1, 5).to("Mpccm").v v2 = co.comoving_radial_distance(1, 5).to("Mpccm").v assert_equal(v1, v2) yt-project-yt-f043ac8/yt/utilities/tests/test_cython_fortran_utils.py000066400000000000000000000015071510711153200263560ustar00rootroot00000000000000import struct import numpy as np import pytest from yt.utilities.cython_fortran_utils import FortranFile def test_raise_error_when_file_does_not_exist(): with pytest.raises(FileNotFoundError): FortranFile("/this/file/does/not/exist") def test_read(tmp_path): dummy_file = tmp_path / "test.bin" # Write a Fortran-formatted file containing one record with 4 doubles # The format is a 32bit integer with value 4*sizeof(double)=32 # followed by 4 doubles and another 32bit integer with value 32 # Note that there is no memory alignment, hence the "=" below buff = struct.pack("=i 4d i", 32, 1.0, 2.0, 3.0, 4.0, 32) dummy_file.write_bytes(buff) with FortranFile(str(dummy_file)) as f: np.testing.assert_equal( f.read_vector("d"), [1.0, 2.0, 3.0, 4.0], ) yt-project-yt-f043ac8/yt/utilities/tests/test_decompose.py000066400000000000000000000073271510711153200240630ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_array_equal import yt.utilities.decompose as dec def test_psize_2d(): procs = dec.get_psize(np.array([5, 1, 7]), 6) assert_array_equal(procs, np.array([3, 1, 2])) procs = dec.get_psize(np.array([1, 7, 5]), 6) assert_array_equal(procs, np.array([1, 2, 3])) procs = dec.get_psize(np.array([7, 5, 1]), 6) assert_array_equal(procs, np.array([2, 3, 1])) def test_psize_3d(): procs = dec.get_psize(np.array([33, 35, 37]), 12) assert_array_equal(procs, np.array([3, 2, 2])) def test_decomposition_2d(): array = np.ones((7, 5, 1)) bbox = np.array([[-0.7, 0.0], [1.5, 2.0], [0.0, 0.7]]) ledge, redge, shapes, slices, _ = dec.decompose_array( array.shape, np.array([2, 3, 1]), bbox ) data = [array[slice] for slice in slices] assert_array_equal(data[1].shape, np.array([3, 2, 1])) gold_le = np.array( [ [-0.7, 1.5, 0.0], [-0.7, 1.6, 0.0], [-0.7, 1.8, 0.0], [-0.4, 1.5, 0.0], [-0.4, 1.6, 0.0], [-0.4, 1.8, 0.0], ] ) assert_almost_equal(ledge, gold_le, 8) gold_re = np.array( [ [-0.4, 1.6, 0.7], [-0.4, 1.8, 0.7], [-0.4, 2.0, 0.7], [0.0, 1.6, 0.7], [0.0, 1.8, 0.7], [0.0, 2.0, 0.7], ] ) assert_almost_equal(redge, gold_re, 8) def test_decomposition_3d(): array = np.ones((33, 35, 37)) bbox = np.array([[0.0, 1.0], [-1.5, 1.5], [1.0, 2.5]]) ledge, redge, shapes, slices, _ = dec.decompose_array( array.shape, np.array([3, 2, 2]), bbox ) data = [array[slice] for slice in slices] assert_array_equal(data[0].shape, np.array([11, 17, 18])) gold_le = np.array( [ [0.00000, -1.50000, 1.00000], [0.00000, -1.50000, 1.72973], [0.00000, -0.04286, 1.00000], [0.00000, -0.04286, 1.72973], [0.33333, -1.50000, 1.00000], [0.33333, -1.50000, 1.72973], [0.33333, -0.04286, 1.00000], [0.33333, -0.04286, 1.72973], [0.66667, -1.50000, 1.00000], [0.66667, -1.50000, 1.72973], [0.66667, -0.04286, 1.00000], [0.66667, -0.04286, 1.72973], ] ) assert_almost_equal(ledge, gold_le, 5) gold_re = np.array( [ [0.33333, -0.04286, 1.72973], [0.33333, -0.04286, 2.50000], [0.33333, 1.50000, 1.72973], [0.33333, 1.50000, 2.50000], [0.66667, -0.04286, 1.72973], [0.66667, -0.04286, 2.50000], [0.66667, 1.50000, 1.72973], [0.66667, 1.50000, 2.50000], [1.00000, -0.04286, 1.72973], [1.00000, -0.04286, 2.50000], [1.00000, 1.50000, 1.72973], [1.00000, 1.50000, 2.50000], ] ) assert_almost_equal(redge, gold_re, 5) def test_decomposition_with_cell_widths(): array = np.ones((33, 35, 37)) bbox = np.array([[0.0, 1.0], [-1.5, 1.5], [1.0, 2.5]]) # build some cell widths, rescale to match bounding box cell_widths = [] for idim in range(3): wid = bbox[idim][1] - bbox[idim][0] cws = np.random.random((array.shape[idim],)) factor = wid / cws.sum() cell_widths.append(factor * cws) ledge, redge, _, _, widths_by_grid = dec.decompose_array( array.shape, np.array([3, 2, 2]), bbox, cell_widths=cell_widths ) for grid_id in range(len(ledge)): grid_wid = redge[grid_id] - ledge[grid_id] cws = widths_by_grid[grid_id] cws_wid = np.array([np.sum(cws[dim]) for dim in range(3)]) assert_almost_equal(grid_wid, cws_wid, 5) yt-project-yt-f043ac8/yt/utilities/tests/test_flagging_methods.py000066400000000000000000000005631510711153200254010ustar00rootroot00000000000000import numpy as np from yt.testing import fake_random_ds from yt.utilities.flagging_methods import flagging_method_registry def test_over_density(): ds = fake_random_ds(64) ds.index od_flag = flagging_method_registry["overdensity"](0.75) criterion = ds.index.grids[0]["gas", "density"] > 0.75 assert np.all(od_flag(ds.index.grids[0]) == criterion) yt-project-yt-f043ac8/yt/utilities/tests/test_hierarchy_inspection.py000066400000000000000000000026011510711153200263040ustar00rootroot00000000000000""" Created on Wed Feb 18 18:24:09 2015 @author: stuart """ from ..hierarchy_inspection import find_lowest_subclasses class level1: pass class level1a: pass class level2(level1): pass class level3(level2): pass class level4(level3): pass def test_empty(): result = find_lowest_subclasses([]) assert len(result) == 0 def test_single(): result = find_lowest_subclasses([level2]) assert len(result) == 1 assert result[0] is level2 def test_two_classes(): result = find_lowest_subclasses([level1, level2]) assert len(result) == 1 assert result[0] is level2 def test_four_deep(): result = find_lowest_subclasses([level1, level2, level3, level4]) assert len(result) == 1 assert result[0] is level4 def test_four_deep_outoforder(): result = find_lowest_subclasses([level2, level3, level1, level4]) assert len(result) == 1 assert result[0] is level4 def test_diverging_tree(): result = find_lowest_subclasses([level1, level2, level3, level1a]) assert len(result) == 2 assert level1a in result and level3 in result def test_without_parents(): result = find_lowest_subclasses([level1, level3]) assert len(result) == 1 assert result[0] is level3 def test_without_grandparents(): result = find_lowest_subclasses([level1, level4]) assert len(result) == 1 assert result[0] is level4 yt-project-yt-f043ac8/yt/utilities/tests/test_interpolators.py000066400000000000000000000120101510711153200247730ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_almost_equal, assert_array_equal import yt.utilities.linear_interpolators as lin from yt.testing import fake_random_ds from yt.utilities.lib.interpolators import ghost_zone_interpolate def test_linear_interpolator_1d(): random_data = np.random.random(64) fv = {"x": np.mgrid[0.0:1.0:64j]} # evenly spaced bins ufi = lin.UnilinearFieldInterpolator(random_data, (0.0, 1.0), "x", True) assert_array_equal(ufi(fv), random_data) # randomly spaced bins size = 64 shift = (1.0 / size) * np.random.random(size) - (0.5 / size) fv["x"] += shift ufi = lin.UnilinearFieldInterpolator( random_data, np.linspace(0.0, 1.0, size) + shift, "x", True ) assert_array_almost_equal(ufi(fv), random_data, 15) def test_linear_interpolator_2d(): random_data = np.random.random((64, 64)) # evenly spaced bins fv = dict(zip("xy", np.mgrid[0.0:1.0:64j, 0.0:1.0:64j], strict=True)) bfi = lin.BilinearFieldInterpolator(random_data, (0.0, 1.0, 0.0, 1.0), "xy", True) assert_array_equal(bfi(fv), random_data) # randomly spaced bins size = 64 bins = np.linspace(0.0, 1.0, size) shifts = {ax: (1.0 / size) * np.random.random(size) - (0.5 / size) for ax in "xy"} fv["x"] += shifts["x"][:, np.newaxis] fv["y"] += shifts["y"] bfi = lin.BilinearFieldInterpolator( random_data, (bins + shifts["x"], bins + shifts["y"]), "xy", True ) assert_array_almost_equal(bfi(fv), random_data, 15) def test_linear_interpolator_3d(): random_data = np.random.random((64, 64, 64)) # evenly spaced bins fv = dict(zip("xyz", np.mgrid[0.0:1.0:64j, 0.0:1.0:64j, 0.0:1.0:64j], strict=True)) tfi = lin.TrilinearFieldInterpolator( random_data, (0.0, 1.0, 0.0, 1.0, 0.0, 1.0), "xyz", True ) assert_array_almost_equal(tfi(fv), random_data) # randomly spaced bins size = 64 bins = np.linspace(0.0, 1.0, size) shifts = {ax: (1.0 / size) * np.random.random(size) - (0.5 / size) for ax in "xyz"} fv["x"] += shifts["x"][:, np.newaxis, np.newaxis] fv["y"] += shifts["y"][:, np.newaxis] fv["z"] += shifts["z"] tfi = lin.TrilinearFieldInterpolator( random_data, (bins + shifts["x"], bins + shifts["y"], bins + shifts["z"]), "xyz", True, ) assert_array_almost_equal(tfi(fv), random_data, 15) def test_linear_interpolator_4d(): random_data = np.random.random((64, 64, 64, 64)) # evenly spaced bins fv = dict( zip( "xyzw", np.mgrid[0.0:1.0:64j, 0.0:1.0:64j, 0.0:1.0:64j, 0.0:1.0:64j], strict=True, ) ) tfi = lin.QuadrilinearFieldInterpolator( random_data, (0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0), "xyzw", True ) assert_array_almost_equal(tfi(fv), random_data) # randomly spaced bins size = 64 bins = np.linspace(0.0, 1.0, size) shifts = {ax: (1.0 / size) * np.random.random(size) - (0.5 / size) for ax in "xyzw"} fv["x"] += shifts["x"][:, np.newaxis, np.newaxis, np.newaxis] fv["y"] += shifts["y"][:, np.newaxis, np.newaxis] fv["z"] += shifts["z"][:, np.newaxis] fv["w"] += shifts["w"] tfi = lin.QuadrilinearFieldInterpolator( random_data, ( bins + shifts["x"], bins + shifts["y"], bins + shifts["z"], bins + shifts["w"], ), "xyzw", True, ) assert_array_almost_equal(tfi(fv), random_data, 15) def test_ghost_zone_extrapolation(): ds = fake_random_ds(16) g = ds.index.grids[0] vec = g.get_vertex_centered_data( [("index", "x"), ("index", "y"), ("index", "z")], no_ghost=True ) for i, ax in enumerate("xyz"): xc = g["index", ax] tf = lin.TrilinearFieldInterpolator( xc, ( g.LeftEdge[0] + g.dds[0] / 2.0, g.RightEdge[0] - g.dds[0] / 2.0, g.LeftEdge[1] + g.dds[1] / 2.0, g.RightEdge[1] - g.dds[1] / 2.0, g.LeftEdge[2] + g.dds[2] / 2.0, g.RightEdge[2] - g.dds[2] / 2.0, ), ["x", "y", "z"], truncate=True, ) lx, ly, lz = np.mgrid[ g.LeftEdge[0] : g.RightEdge[0] : (g.ActiveDimensions[0] + 1) * 1j, g.LeftEdge[1] : g.RightEdge[1] : (g.ActiveDimensions[1] + 1) * 1j, g.LeftEdge[2] : g.RightEdge[2] : (g.ActiveDimensions[2] + 1) * 1j, ] xi = tf({"x": lx, "y": ly, "z": lz}) xz = np.zeros(g.ActiveDimensions + 1) ghost_zone_interpolate( 1, xc, np.array([0.5, 0.5, 0.5], dtype="f8"), xz, np.array([0.0, 0.0, 0.0], dtype="f8"), ) ii = (lx, ly, lz)[i] assert_array_equal(ii, vec["index", ax]) assert_array_equal(ii, xi) assert_array_equal(ii, xz) def test_get_vertex_centered_data(): ds = fake_random_ds(16) g = ds.index.grids[0] g.get_vertex_centered_data([("gas", "density")], no_ghost=True) yt-project-yt-f043ac8/yt/utilities/tests/test_minimal_representation.py000066400000000000000000000030331510711153200266430ustar00rootroot00000000000000import os.path from numpy.testing import assert_equal, assert_raises import yt from yt.config import ytcfg from yt.testing import requires_file, requires_module G30 = "IsolatedGalaxy/galaxy0030/galaxy0030" old_serialize = None def setup_module(): global old_serialize old_serialize = ytcfg.get("yt", "serialize") ytcfg["yt", "serialize"] = True def teardown_module(): ytcfg["yt", "serialize"] = old_serialize @requires_module("h5py") @requires_file(G30) def test_store(): ds = yt.load(G30) store = ds.parameter_filename + ".yt" field = "density" if os.path.isfile(store): os.remove(store) proj1 = ds.proj(field, "z") sp = ds.sphere(ds.domain_center, (4, "kpc")) proj2 = ds.proj(field, "z", data_source=sp) proj1_c = ds.proj(field, "z") assert_equal(proj1[field], proj1_c[field]) proj2_c = ds.proj(field, "z", data_source=sp) assert_equal(proj2[field], proj2_c[field]) def fail_for_different_method(): proj2_c = ds.proj(field, "z", data_source=sp, method="max") assert_equal(proj2[field], proj2_c[field]) # A note here: a unyt.exceptions.UnitOperationError is raised # and caught by numpy, which reraises a ValueError assert_raises(ValueError, fail_for_different_method) def fail_for_different_source(): sp = ds.sphere(ds.domain_center, (2, "kpc")) proj2_c = ds.proj(field, "z", data_source=sp, method="integrate") assert_equal(proj2_c[field], proj2[field]) assert_raises(AssertionError, fail_for_different_source) yt-project-yt-f043ac8/yt/utilities/tests/test_on_demand_imports.py000066400000000000000000000032721510711153200256010ustar00rootroot00000000000000import pytest from yt.utilities.on_demand_imports import OnDemand, safe_import def test_access_available_module(): class os_imports(OnDemand): @safe_import def path(self): from os import path return path _os = os_imports() _os.path.join("eggs", "saussage") def test_access_unavailable_module(): class Bacon_imports(OnDemand): @safe_import def spam(self): from Bacon import spam return spam _bacon = Bacon_imports() with pytest.raises( ImportError, match=r"No module named 'Bacon'", ) as excinfo: _bacon.spam() # yt should add information to the original error message # but this done slightly differently in Python>=3.11 # (using exception notes), so we can't just match the error message # directly. Instead this implements a Python-version agnostic check # that the user-visible error message is what we expect. complete_error_message = excinfo.exconly() assert complete_error_message == ( "ModuleNotFoundError: No module named 'Bacon'\n" "Something went wrong while trying to lazy-import Bacon. " "Please make sure that Bacon is properly installed.\n" "If the problem persists, please file an issue at " "https://github.com/yt-project/yt/issues/new" ) def test_class_invalidation(): with pytest.raises( TypeError, match="class .*'s name needs to be suffixed '_imports'" ): class Bacon(OnDemand): pass def test_base_class_instanciation(): with pytest.raises( TypeError, match="The OnDemand base class cannot be instantiated." ): OnDemand() yt-project-yt-f043ac8/yt/utilities/tests/test_particle_generator.py000066400000000000000000000124431510711153200257510ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal from yt.loaders import load_uniform_grid from yt.units._numpy_wrapper_functions import uconcatenate from yt.utilities.particle_generator import ( FromListParticleGenerator, LatticeParticleGenerator, WithDensityParticleGenerator, ) def test_particle_generator(): # First generate our dataset domain_dims = (32, 32, 32) dens = np.zeros(domain_dims) + 0.1 temp = 4.0 * np.ones(domain_dims) fields = {"density": (dens, "code_mass/code_length**3"), "temperature": (temp, "K")} ds = load_uniform_grid(fields, domain_dims, 1.0) # Now generate particles from density field_list = [ ("io", "particle_position_x"), ("io", "particle_position_y"), ("io", "particle_position_z"), ("io", "particle_index"), ("io", "particle_gas_density"), ] num_particles = 10000 field_dict = {("gas", "density"): ("io", "particle_gas_density")} sphere = ds.sphere(ds.domain_center, 0.45) particles1 = WithDensityParticleGenerator(ds, sphere, num_particles, field_list) particles1.assign_indices() particles1.map_grid_fields_to_particles(field_dict) # Test to make sure we ended up with the right number of particles per grid particles1.apply_to_stream() particles_per_grid1 = [grid.NumberOfParticles for grid in ds.index.grids] assert_equal(particles_per_grid1, particles1.NumberOfParticles) particles_per_grid1 = [ len(grid["all", "particle_position_x"]) for grid in ds.index.grids ] assert_equal(particles_per_grid1, particles1.NumberOfParticles) tags = uconcatenate([grid["all", "particle_index"] for grid in ds.index.grids]) assert np.unique(tags).size == num_particles del tags # Set up a lattice of particles pdims = np.array([32, 32, 32]) def new_indices(): # We just add new indices onto the existing ones return np.arange(np.prod(pdims)) + num_particles le = np.array([0.25, 0.25, 0.25]) re = np.array([0.75, 0.75, 0.75]) particles2 = LatticeParticleGenerator(ds, pdims, le, re, field_list) particles2.assign_indices(function=new_indices) particles2.map_grid_fields_to_particles(field_dict) # Test lattice positions xpos = np.unique(particles2["io", "particle_position_x"]) ypos = np.unique(particles2["io", "particle_position_y"]) zpos = np.unique(particles2["io", "particle_position_z"]) xpred = np.linspace(le[0], re[0], num=pdims[0], endpoint=True) ypred = np.linspace(le[1], re[1], num=pdims[1], endpoint=True) zpred = np.linspace(le[2], re[2], num=pdims[2], endpoint=True) assert_almost_equal(xpos, xpred) assert_almost_equal(ypos, ypred) assert_almost_equal(zpos, zpred) del xpos, ypos, zpos del xpred, ypred, zpred # Test the number of particles again particles2.apply_to_stream() particles_per_grid2 = [grid.NumberOfParticles for grid in ds.index.grids] assert_equal( particles_per_grid2, particles1.NumberOfParticles + particles2.NumberOfParticles ) [grid.field_data.clear() for grid in ds.index.grids] particles_per_grid2 = [ len(grid["all", "particle_position_x"]) for grid in ds.index.grids ] assert_equal( particles_per_grid2, particles1.NumberOfParticles + particles2.NumberOfParticles ) # Test the uniqueness of tags tags = np.concatenate([grid["all", "particle_index"] for grid in ds.index.grids]) tags.sort() assert_equal(tags, np.arange(np.prod(pdims) + num_particles)) del tags # Now dump all of these particle fields out into a dict pdata = {} dd = ds.all_data() for field in field_list: pdata[field] = dd[field] # Test the "from-list" generator and particle field overwrite num_particles3 = num_particles + np.prod(pdims) particles3 = FromListParticleGenerator(ds, num_particles3, pdata) particles3.apply_to_stream(overwrite=True) # Test the number of particles again particles_per_grid3 = [grid.NumberOfParticles for grid in ds.index.grids] assert_equal( particles_per_grid3, particles1.NumberOfParticles + particles2.NumberOfParticles ) particles_per_grid2 = [ len(grid["all", "particle_position_z"]) for grid in ds.index.grids ] assert_equal( particles_per_grid3, particles1.NumberOfParticles + particles2.NumberOfParticles ) assert_equal(particles_per_grid2, particles_per_grid3) # Test adding in particles with a different particle type num_star_particles = 20000 pdata2 = { ("star", "particle_position_x"): np.random.uniform(size=num_star_particles), ("star", "particle_position_y"): np.random.uniform(size=num_star_particles), ("star", "particle_position_z"): np.random.uniform(size=num_star_particles), } particles4 = FromListParticleGenerator(ds, num_star_particles, pdata2, ptype="star") particles4.apply_to_stream() dd = ds.all_data() assert dd["star", "particle_position_x"].size == num_star_particles assert dd["io", "particle_position_x"].size == num_particles3 assert dd["all", "particle_position_x"].size == num_star_particles + num_particles3 del pdata del pdata2 del ds del particles1 del particles2 del particles4 del fields del dens del temp yt-project-yt-f043ac8/yt/utilities/tests/test_periodic_table.py000066400000000000000000000012241510711153200250400ustar00rootroot00000000000000from numpy.testing import assert_equal from yt.utilities.periodic_table import _elements, periodic_table def test_element_accuracy(): for num, w, name, sym in _elements: e0 = periodic_table[num] e1 = periodic_table[name] e2 = periodic_table[sym] # If num == -1, then we are in one of the things like Deuterium or El # that are not elements by themselves. if num == -1: e0 = e1 assert_equal(id(e0), id(e1)) assert_equal(id(e0), id(e2)) assert_equal(e0.num, num) assert_equal(e0.weight, w) assert_equal(e0.name, name) assert_equal(e0.symbol, sym) yt-project-yt-f043ac8/yt/utilities/tests/test_periodicity.py000066400000000000000000000052441510711153200244250ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal from yt.testing import fake_random_ds from yt.utilities.math_utils import euclidean_dist, periodic_dist def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_periodicity(): # First test the simple case were we find the distance between two points a = [0.1, 0.1, 0.1] b = [0.9, 0.9, 0.9] period = 1.0 dist = periodic_dist(a, b, period) assert_almost_equal(dist, 0.34641016151377535) dist = periodic_dist(a, b, period, (True, False, False)) assert_almost_equal(dist, 1.1489125293076059) dist = periodic_dist(a, b, period, (False, True, False)) assert_almost_equal(dist, 1.1489125293076059) dist = periodic_dist(a, b, period, (False, False, True)) assert_almost_equal(dist, 1.1489125293076059) dist = periodic_dist(a, b, period, (True, True, False)) assert_almost_equal(dist, 0.84852813742385713) dist = periodic_dist(a, b, period, (True, False, True)) assert_almost_equal(dist, 0.84852813742385713) dist = periodic_dist(a, b, period, (False, True, True)) assert_almost_equal(dist, 0.84852813742385713) dist = euclidean_dist(a, b) assert_almost_equal(dist, 1.3856406460551021) # Now test the more complicated cases where we're calculating radii based # on data objects ds = fake_random_ds(64) # First we test flattened data data = ds.all_data() positions = np.array([data["index", ax] for ax in "xyz"]) c = [0.1, 0.1, 0.1] n_tup = tuple(1 for i in range(positions.ndim - 1)) center = np.tile( np.reshape(np.array(c), (positions.shape[0],) + n_tup), (1,) + positions.shape[1:], ) dist = periodic_dist(positions, center, period, ds.periodicity) assert_almost_equal(dist.min(), 0.00270632938683) assert_almost_equal(dist.max(), 0.863319074398) dist = euclidean_dist(positions, center) assert_almost_equal(dist.min(), 0.00270632938683) assert_almost_equal(dist.max(), 1.54531407988) # Then grid-like data data = ds.index.grids[0] positions = np.array([data["index", ax] for ax in "xyz"]) c = [0.1, 0.1, 0.1] n_tup = tuple(1 for i in range(positions.ndim - 1)) center = np.tile( np.reshape(np.array(c), (positions.shape[0],) + n_tup), (1,) + positions.shape[1:], ) dist = periodic_dist(positions, center, period, ds.periodicity) assert_almost_equal(dist.min(), 0.00270632938683) assert_almost_equal(dist.max(), 0.863319074398) dist = euclidean_dist(positions, center) assert_almost_equal(dist.min(), 0.00270632938683) assert_almost_equal(dist.max(), 1.54531407988) yt-project-yt-f043ac8/yt/utilities/tests/test_selectors.py000066400000000000000000000136361510711153200241100ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_array_less, assert_equal from yt.testing import fake_random_ds from yt.utilities.math_utils import periodic_dist def setup_module(): from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_point_selector(): # generate fake amr data bbox = np.array([[-1.0, 1.0], [-1.0, 1.0], [-1.0, 1.0]]) ds = fake_random_ds(16, nprocs=7, bbox=bbox) assert all(ds.periodicity) dd = ds.all_data() positions = np.array([dd["index", ax] for ax in "xyz"]).T delta = 0.5 * np.array([dd["index", f"d{ax}"] for ax in "xyz"]).T # ensure cell centers and corners always return one and # only one point object for p in positions: data = ds.point(p) assert_equal(data["index", "ones"].shape[0], 1) for p in positions - delta: data = ds.point(p) assert_equal(data["index", "ones"].shape[0], 1) for p in positions + delta: data = ds.point(p) assert_equal(data["index", "ones"].shape[0], 1) def test_sphere_selector(): # generate fake data with a number of non-cubical grids ds = fake_random_ds(64, nprocs=51) assert all(ds.periodicity) # aligned tests spheres = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5], [1.0, 1.0, 1.0], [0.25, 0.75, 0.25]] for center in spheres: data = ds.sphere(center, 0.25) # WARNING: this value has not be externally verified dd = ds.all_data() dd.set_field_parameter("center", ds.arr(center, "code_length")) n_outside = (dd["index", "radius"] >= 0.25).sum() assert_equal( data["index", "radius"].size + n_outside, dd["index", "radius"].size ) positions = np.array([data["index", ax] for ax in "xyz"]) centers = ( np.tile(data.center, data["index", "x"].shape[0]) .reshape(data["index", "x"].shape[0], 3) .transpose() ) dist = periodic_dist( positions, centers, ds.domain_right_edge - ds.domain_left_edge, ds.periodicity, ) # WARNING: this value has not been externally verified assert_array_less(dist, 0.25) def test_ellipsoid_selector(): # generate fake data with a number of non-cubical grids ds = fake_random_ds(64, nprocs=51) assert all(ds.periodicity) ellipsoids = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5], [1.0, 1.0, 1.0], [0.25, 0.75, 0.25]] # spherical ellipsoid tests ratios = 3 * [0.25] for center in ellipsoids: data = ds.ellipsoid( center, ratios[0], ratios[1], ratios[2], np.array([1.0, 0.0, 0.0]), 0.0 ) data.get_data() dd = ds.all_data() dd.set_field_parameter("center", ds.arr(center, "code_length")) n_outside = (dd["index", "radius"] >= ratios[0]).sum() assert_equal( data["index", "radius"].size + n_outside, dd["index", "radius"].size ) positions = np.array([data["index", ax] for ax in "xyz"]) centers = ( np.tile(data.center, data.shape[0]).reshape(data.shape[0], 3).transpose() ) dist = periodic_dist( positions, centers, ds.domain_right_edge - ds.domain_left_edge, ds.periodicity, ) # WARNING: this value has not been externally verified assert_array_less(dist, ratios[0]) # aligned ellipsoid tests ratios = [0.25, 0.1, 0.1] for center in ellipsoids: data = ds.ellipsoid( center, ratios[0], ratios[1], ratios[2], np.array([1.0, 0.0, 0.0]), 0.0 ) # hack to compute elliptic distance dist2 = np.zeros(data["index", "ones"].shape[0]) for i, ax in enumerate(("index", k) for k in "xyz"): positions = np.zeros((3, data["index", "ones"].shape[0])) positions[i, :] = data[ax] centers = np.zeros((3, data["index", "ones"].shape[0])) centers[i, :] = center[i] dist2 += ( periodic_dist( positions, centers, ds.domain_right_edge - ds.domain_left_edge, ds.periodicity, ) / ratios[i] ) ** 2 # WARNING: this value has not been externally verified assert_array_less(dist2, 1.0) def test_slice_selector(): # generate fake data with a number of non-cubical grids ds = fake_random_ds(64, nprocs=51) assert all(ds.periodicity) for i, d in enumerate(("index", k) for k in "xyz"): for coord in np.arange(0.0, 1.0, 0.1): data = ds.slice(i, coord) data.get_data() v = data[d].to_ndarray() assert_equal(data.shape[0], 64**2) assert_equal(data["index", "ones"].shape[0], 64**2) assert_array_less(np.abs(v - coord), 1.0 / 128.0 + 1e-6) def test_cutting_plane_selector(): # generate fake data with a number of non-cubical grids ds = fake_random_ds(64, nprocs=51) assert all(ds.periodicity) # test cutting plane against orthogonal plane for i in range(3): norm = np.zeros(3) norm[i] = 1.0 for coord in np.arange(0, 1.0, 0.1): center = np.zeros(3) center[i] = coord data = ds.slice(i, coord) data.get_data() data2 = ds.cutting(norm, center) data2.get_data() assert data.shape[0] == data2.shape[0] cells1 = np.lexsort( (data["index", "x"], data["index", "y"], data["index", "z"]) ) cells2 = np.lexsort( (data2["index", "x"], data2["index", "y"], data2["index", "z"]) ) for d2 in "xyz": assert_equal(data["index", d2][cells1], data2["index", d2][cells2]) # def test_region_selector(): # # def test_disk_selector(): # # def test_orthoray_selector(): # # def test_ray_selector(): yt-project-yt-f043ac8/yt/utilities/tests/test_set_log_level.py000066400000000000000000000015321510711153200247200ustar00rootroot00000000000000from numpy.testing import assert_raises from yt.utilities.logger import set_log_level old_level = None def setup_module(): global old_level from yt.utilities.logger import ytLogger old_level = ytLogger.level def teardown_module(): set_log_level(old_level) def test_valid_level(): # test a subset of valid entries to cover # - case-insensitivity # - integer values # - "all" alias, which isn't standard for lvl in ("all", "ALL", 10, 42, "info", "warning", "ERROR", "CRITICAL"): set_log_level(lvl) def test_invalid_level(): # these are the exceptions raised by logging.Logger.setLog # since they are perfectly clear and readable, we check that nothing else # happens in the wrapper assert_raises(TypeError, set_log_level, 1.5) assert_raises(ValueError, set_log_level, "invalid_level") yt-project-yt-f043ac8/yt/utilities/tree_container.py000066400000000000000000000006521510711153200226770ustar00rootroot00000000000000class TreeContainer: r"""A recursive data container for things like merger trees and clump-finder trees. """ _child_attr = "children" def __init__(self): setattr(self, self._child_attr, None) def __iter__(self): yield self children = getattr(self, self._child_attr) if children is None: return for child in children: yield from child yt-project-yt-f043ac8/yt/utilities/voropp.pyx000066400000000000000000000043331510711153200214130ustar00rootroot00000000000000""" Wrapping code for voro++ """ cimport libcpp from cython.operator cimport dereference as deref import numpy as np cimport cython cimport numpy as np cdef extern from "voro++.hh" namespace "voro": cdef cppclass c_loop_all cdef cppclass voronoicell: double volume() cdef cppclass container: container(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, int nx, int ny, int nz, libcpp.bool xper, libcpp.bool yper, libcpp.bool zper, int alloc) void put(int n, double x, double y, double z) void store_cell_volumes(double *vols) int compute_cell(voronoicell c, c_loop_all vl) double sum_cell_volumes() cdef cppclass c_loop_all: c_loop_all(container &con) int inc() int start() cdef class VoronoiVolume: cdef container *my_con cdef public int npart def __init__(self, xi, yi, zi, left_edge, right_edge): self.my_con = new container(left_edge[0], right_edge[0], left_edge[1], right_edge[1], left_edge[2], right_edge[2], xi, yi, zi, False, False, False, 8) self.npart = 0 def __dealloc__(self): del self.my_con @cython.boundscheck(False) @cython.wraparound(False) def add_array(self, np.ndarray[np.float64_t, ndim=1] xpos, np.ndarray[np.float64_t, ndim=1] ypos, np.ndarray[np.float64_t, ndim=1] zpos): cdef int i for i in range(xpos.shape[0]): self.my_con.put(self.npart, xpos[i], ypos[i], zpos[i]) self.npart += 1 @cython.boundscheck(False) @cython.wraparound(False) def get_volumes(self): cdef np.ndarray vol = np.zeros(self.npart, 'double') #self.my_con.store_cell_volumes(vdouble) cdef c_loop_all *vl = new c_loop_all(deref(self.my_con)) cdef voronoicell c if not vl.start(): return cdef int i = 0 while 1: if self.my_con.compute_cell(c, deref(vl)): vol[i] = c.volume() if not vl.inc(): break i += 1 del vl return vol yt-project-yt-f043ac8/yt/visualization/000077500000000000000000000000001510711153200202075ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/__init__.py000066400000000000000000000002321510711153200223150ustar00rootroot00000000000000""" Raven ===== Raven is the plotting interface, with support for several different engines. Well, two for now, but maybe more later. Who knows? """ yt-project-yt-f043ac8/yt/visualization/_colormap_data.py000066400000000000000000016164611510711153200235440ustar00rootroot00000000000000### Auto-generated colormap tables, taken from Matplotlib ### import numpy as np from numpy import array color_map_luts = {} ### IDL colormap 0 :: B-W LINEAR ### color_map_luts['idl00'] = \ ( array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 1 :: BLUE ### color_map_luts['idl01'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0195312, 0.0351562, 0.0507812, 0.0664062, 0.0820312, 0.0976562, 0.1132812, 0.1289062, 0.1445312, 0.1601562, 0.1757812, 0.1914062, 0.2070312, 0.2226562, 0.2382812, 0.2539062, 0.2695312, 0.2851562, 0.3007812, 0.3164062, 0.3320312, 0.3515625, 0.3671875, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4453125, 0.4609375, 0.4765625, 0.4921875, 0.5078125, 0.5234375, 0.5390625, 0.5546875, 0.5703125, 0.5859375, 0.6015625, 0.6171875, 0.6328125, 0.6484375, 0.6640625, 0.6835938, 0.6992188, 0.7148438, 0.7304688, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8085938, 0.8242188, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0156250, 0.0195312, 0.0273438, 0.0351562, 0.0390625, 0.0468750, 0.0507812, 0.0585938, 0.0664062, 0.0703125, 0.0781250, 0.0820312, 0.0898438, 0.0976562, 0.1015625, 0.1093750, 0.1132812, 0.1210938, 0.1289062, 0.1328125, 0.1406250, 0.1445312, 0.1523438, 0.1601562, 0.1640625, 0.1718750, 0.1757812, 0.1835938, 0.1914062, 0.1953125, 0.2031250, 0.2070312, 0.2148438, 0.2226562, 0.2265625, 0.2343750, 0.2382812, 0.2460938, 0.2539062, 0.2578125, 0.2656250, 0.2695312, 0.2773438, 0.2851562, 0.2890625, 0.2968750, 0.3007812, 0.3085938, 0.3164062, 0.3203125, 0.3281250, 0.3320312, 0.3398438, 0.3476562, 0.3515625, 0.3593750, 0.3671875, 0.3710938, 0.3789062, 0.3828125, 0.3906250, 0.3984375, 0.4023438, 0.4101562, 0.4140625, 0.4218750, 0.4296875, 0.4335938, 0.4414062, 0.4453125, 0.4531250, 0.4609375, 0.4648438, 0.4726562, 0.4765625, 0.4843750, 0.4921875, 0.4960938, 0.5039062, 0.5078125, 0.5156250, 0.5234375, 0.5273438, 0.5351562, 0.5390625, 0.5468750, 0.5546875, 0.5585938, 0.5664062, 0.5703125, 0.5781250, 0.5859375, 0.5898438, 0.5976562, 0.6015625, 0.6093750, 0.6171875, 0.6210938, 0.6289062, 0.6328125, 0.6406250, 0.6484375, 0.6523438, 0.6601562, 0.6640625, 0.6718750, 0.6796875, 0.6835938, 0.6914062, 0.6992188, 0.7031250, 0.7109375, 0.7148438, 0.7226562, 0.7304688, 0.7343750, 0.7421875, 0.7460938, 0.7539062, 0.7617188, 0.7656250, 0.7734375, 0.7773438, 0.7851562, 0.7929688, 0.7968750, 0.8046875, 0.8085938, 0.8164062, 0.8242188, 0.8281250, 0.8359375, 0.8398438, 0.8476562, 0.8554688, 0.8593750, 0.8671875, 0.8710938, 0.8789062, 0.8867188, 0.8906250, 0.8984375, 0.9023438, 0.9101562, 0.9179688, 0.9218750, 0.9296875, 0.9335938, 0.9414062, 0.9492188, 0.9531250, 0.9609375, 0.9648438, 0.9726562, 0.9804688, 0.9843750, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0156250, 0.0195312, 0.0234375, 0.0312500, 0.0351562, 0.0390625, 0.0468750, 0.0507812, 0.0546875, 0.0625000, 0.0664062, 0.0703125, 0.0781250, 0.0820312, 0.0898438, 0.0937500, 0.0976562, 0.1054688, 0.1093750, 0.1132812, 0.1210938, 0.1250000, 0.1289062, 0.1367188, 0.1406250, 0.1445312, 0.1523438, 0.1562500, 0.1640625, 0.1679688, 0.1718750, 0.1796875, 0.1835938, 0.1875000, 0.1953125, 0.1992188, 0.2031250, 0.2109375, 0.2148438, 0.2187500, 0.2265625, 0.2304688, 0.2382812, 0.2421875, 0.2460938, 0.2539062, 0.2578125, 0.2617188, 0.2695312, 0.2734375, 0.2773438, 0.2851562, 0.2890625, 0.2929688, 0.3007812, 0.3046875, 0.3125000, 0.3164062, 0.3203125, 0.3281250, 0.3320312, 0.3359375, 0.3437500, 0.3476562, 0.3515625, 0.3593750, 0.3632812, 0.3671875, 0.3750000, 0.3789062, 0.3867188, 0.3906250, 0.3945312, 0.4023438, 0.4062500, 0.4101562, 0.4179688, 0.4218750, 0.4257812, 0.4335938, 0.4375000, 0.4414062, 0.4492188, 0.4531250, 0.4609375, 0.4648438, 0.4687500, 0.4765625, 0.4804688, 0.4843750, 0.4921875, 0.4960938, 0.5000000, 0.5078125, 0.5117188, 0.5156250, 0.5234375, 0.5273438, 0.5312500, 0.5390625, 0.5429688, 0.5507812, 0.5546875, 0.5585938, 0.5664062, 0.5703125, 0.5742188, 0.5820312, 0.5859375, 0.5898438, 0.5976562, 0.6015625, 0.6054688, 0.6132812, 0.6171875, 0.6250000, 0.6289062, 0.6328125, 0.6406250, 0.6445312, 0.6484375, 0.6562500, 0.6601562, 0.6640625, 0.6718750, 0.6757812, 0.6796875, 0.6875000, 0.6914062, 0.6992188, 0.7031250, 0.7070312, 0.7148438, 0.7187500, 0.7226562, 0.7304688, 0.7343750, 0.7382812, 0.7460938, 0.7500000, 0.7539062, 0.7617188, 0.7656250, 0.7734375, 0.7773438, 0.7812500, 0.7890625, 0.7929688, 0.7968750, 0.8046875, 0.8085938, 0.8125000, 0.8203125, 0.8242188, 0.8281250, 0.8359375, 0.8398438, 0.8476562, 0.8515625, 0.8554688, 0.8632812, 0.8671875, 0.8710938, 0.8789062, 0.8828125, 0.8867188, 0.8945312, 0.8984375, 0.9023438, 0.9101562, 0.9140625, 0.9218750, 0.9257812, 0.9296875, 0.9375000, 0.9414062, 0.9453125, 0.9531250, 0.9570312, 0.9609375, 0.9687500, 0.9726562, 0.9765625, 0.9843750, 0.9882812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 2 :: GRN-RED-BLU-WHT ### color_map_luts['idl02'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0234375, 0.0468750, 0.0703125, 0.0937500, 0.1171875, 0.1406250, 0.1640625, 0.1875000, 0.2109375, 0.2343750, 0.2578125, 0.2812500, 0.3046875, 0.3281250, 0.3515625, 0.3750000, 0.3984375, 0.4218750, 0.4453125, 0.4687500, 0.4921875, 0.5156250, 0.5390625, 0.5625000, 0.5859375, 0.6093750, 0.6328125, 0.6562500, 0.6796875, 0.7031250, 0.7265625, 0.7500000, 0.7734375, 0.7968750, 0.8203125, 0.8437500, 0.8671875, 0.8906250, 0.9140625, 0.9375000, 0.9492188, 0.9609375, 0.9726562, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9804688, 0.9765625, 0.9726562, 0.9687500, 0.9687500, 0.9687500, 0.9687500, 0.9687500, 0.9648438, 0.9609375, 0.9570312, 0.9531250, 0.9492188, 0.9453125, 0.9414062, 0.9375000, 0.9375000, 0.9335938, 0.9296875, 0.9257812, 0.9218750, 0.9218750, 0.9218750, 0.9218750, 0.9179688, 0.9140625, 0.9101562, 0.9062500, 0.9023438, 0.8984375, 0.8945312, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8867188, 0.8828125, 0.8789062, 0.8750000, 0.8710938, 0.8671875, 0.8632812, 0.8593750, 0.8554688, 0.8515625, 0.8476562, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8281250, 0.8242188, 0.8203125, 0.8164062, 0.8125000, 0.8085938, 0.8046875, 0.8007812, 0.7968750, 0.7968750, 0.7968750, 0.7968750, 0.7968750, 0.7929688, 0.7890625, 0.7851562, 0.7812500, 0.7773438, 0.7734375, 0.7695312, 0.7656250, 0.7656250, 0.7656250, 0.7656250, 0.7656250, 0.7617188, 0.7578125, 0.7539062, 0.7500000, 0.7460938, 0.7421875, 0.7382812, 0.7343750, 0.7304688, 0.7265625, 0.7226562, 0.7187500, 0.7187500, 0.7187500, 0.7187500, 0.7187500, 0.7148438, 0.7109375, 0.7070312, 0.7031250, 0.6992188, 0.6953125, 0.6914062, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6835938, 0.6796875, 0.6757812, 0.6718750, 0.6679688, 0.6640625, 0.6601562, 0.6562500, 0.6523438, 0.6484375, 0.6445312, 0.6406250, 0.6406250, 0.6406250, 0.6406250, 0.6406250, 0.6367188, 0.6328125, 0.6289062, 0.6250000, 0.6210938, 0.6171875, 0.6132812, 0.6093750, 0.6054688, 0.6015625, 0.5976562, 0.5937500, 0.5937500, 0.5937500, 0.5937500, 0.5937500, 0.5898438, 0.5859375, 0.5820312, 0.5781250, 0.5898438, 0.6015625, 0.6132812, 0.6250000, 0.6367188, 0.6484375, 0.6601562, 0.6718750, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7460938, 0.7578125, 0.7695312, 0.7812500, 0.7929688, 0.8046875, 0.8164062, 0.8281250, 0.8398438, 0.8515625, 0.8632812, 0.8750000, 0.8906250, 0.9062500, 0.9218750, 0.9375000, 0.9492188, 0.9609375, 0.9726562, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.1406250, 0.2812500, 0.2929688, 0.3085938, 0.3203125, 0.3359375, 0.3515625, 0.3632812, 0.3789062, 0.3906250, 0.4062500, 0.4218750, 0.4570312, 0.4921875, 0.5273438, 0.5625000, 0.5976562, 0.6328125, 0.6679688, 0.7031250, 0.7382812, 0.7734375, 0.8085938, 0.8437500, 0.8789062, 0.9140625, 0.9492188, 0.9843750, 0.9726562, 0.9609375, 0.9492188, 0.9375000, 0.9140625, 0.8906250, 0.8671875, 0.8437500, 0.8203125, 0.7968750, 0.7734375, 0.7500000, 0.7265625, 0.7031250, 0.6796875, 0.6562500, 0.6328125, 0.6093750, 0.5859375, 0.5625000, 0.5390625, 0.5156250, 0.4921875, 0.4687500, 0.4453125, 0.4218750, 0.3984375, 0.3750000, 0.3515625, 0.3281250, 0.3046875, 0.2812500, 0.2578125, 0.2343750, 0.2109375, 0.1875000, 0.1640625, 0.1406250, 0.1171875, 0.0937500, 0.0703125, 0.0468750, 0.0234375, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0312500, 0.0625000, 0.0937500, 0.1250000, 0.1562500, 0.1875000, 0.2187500, 0.2500000, 0.2812500, 0.3125000, 0.3437500, 0.3750000, 0.4062500, 0.4375000, 0.4687500, 0.5000000, 0.5273438, 0.5546875, 0.5820312, 0.6093750, 0.6406250, 0.6718750, 0.7031250, 0.7343750, 0.7656250, 0.7968750, 0.8281250, 0.8593750, 0.8906250, 0.9218750, 0.9531250, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0273438, 0.0351562, 0.0390625, 0.0468750, 0.0546875, 0.0625000, 0.0703125, 0.0781250, 0.0859375, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1328125, 0.1367188, 0.1445312, 0.1523438, 0.1601562, 0.1679688, 0.1757812, 0.1835938, 0.1953125, 0.2031250, 0.2109375, 0.2187500, 0.2304688, 0.2382812, 0.2460938, 0.2539062, 0.2617188, 0.2656250, 0.2734375, 0.2812500, 0.2890625, 0.2968750, 0.3046875, 0.3125000, 0.3242188, 0.3320312, 0.3398438, 0.3476562, 0.3593750, 0.3671875, 0.3750000, 0.3828125, 0.3945312, 0.3984375, 0.4062500, 0.4140625, 0.4218750, 0.4296875, 0.4375000, 0.4453125, 0.4531250, 0.4609375, 0.4687500, 0.4765625, 0.4882812, 0.4960938, 0.5039062, 0.5117188, 0.5234375, 0.5273438, 0.5351562, 0.5429688, 0.5507812, 0.5585938, 0.5664062, 0.5742188, 0.5859375, 0.5937500, 0.6015625, 0.6093750, 0.6210938, 0.6250000, 0.6328125, 0.6367188, 0.6445312, 0.6523438, 0.6601562, 0.6679688, 0.6796875, 0.6875000, 0.6953125, 0.7031250, 0.7148438, 0.7226562, 0.7304688, 0.7382812, 0.7500000, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7851562, 0.7929688, 0.8007812, 0.8125000, 0.8203125, 0.8281250, 0.8359375, 0.8437500, 0.8476562, 0.8554688, 0.8632812, 0.8710938, 0.8789062, 0.8867188, 0.8945312, 0.9062500, 0.9140625, 0.9218750, 0.9296875, 0.9414062, 0.9492188, 0.9570312, 0.9648438, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 3 :: RED TEMPERATURE ### color_map_luts['idl03'] = \ ( array([ 0.0000000, 0.0039062, 0.0078125, 0.0156250, 0.0195312, 0.0273438, 0.0312500, 0.0390625, 0.0429688, 0.0507812, 0.0546875, 0.0585938, 0.0664062, 0.0703125, 0.0781250, 0.0820312, 0.0898438, 0.0937500, 0.1015625, 0.1054688, 0.1093750, 0.1171875, 0.1210938, 0.1289062, 0.1328125, 0.1406250, 0.1445312, 0.1523438, 0.1562500, 0.1640625, 0.1679688, 0.1718750, 0.1796875, 0.1835938, 0.1914062, 0.1953125, 0.2031250, 0.2070312, 0.2148438, 0.2187500, 0.2226562, 0.2304688, 0.2343750, 0.2421875, 0.2460938, 0.2539062, 0.2578125, 0.2656250, 0.2695312, 0.2734375, 0.2812500, 0.2851562, 0.2929688, 0.2968750, 0.3046875, 0.3085938, 0.3164062, 0.3203125, 0.3281250, 0.3320312, 0.3359375, 0.3437500, 0.3476562, 0.3554688, 0.3593750, 0.3671875, 0.3710938, 0.3789062, 0.3828125, 0.3867188, 0.3945312, 0.3984375, 0.4062500, 0.4101562, 0.4179688, 0.4218750, 0.4296875, 0.4335938, 0.4414062, 0.4453125, 0.4492188, 0.4570312, 0.4609375, 0.4687500, 0.4726562, 0.4804688, 0.4843750, 0.4921875, 0.4960938, 0.5000000, 0.5078125, 0.5117188, 0.5195312, 0.5234375, 0.5312500, 0.5351562, 0.5429688, 0.5468750, 0.5507812, 0.5585938, 0.5625000, 0.5703125, 0.5742188, 0.5820312, 0.5859375, 0.5937500, 0.5976562, 0.6054688, 0.6093750, 0.6132812, 0.6210938, 0.6250000, 0.6328125, 0.6367188, 0.6445312, 0.6484375, 0.6562500, 0.6601562, 0.6640625, 0.6718750, 0.6757812, 0.6835938, 0.6875000, 0.6953125, 0.6992188, 0.7070312, 0.7109375, 0.7187500, 0.7226562, 0.7265625, 0.7343750, 0.7382812, 0.7460938, 0.7500000, 0.7578125, 0.7617188, 0.7695312, 0.7734375, 0.7773438, 0.7851562, 0.7890625, 0.7968750, 0.8007812, 0.8085938, 0.8125000, 0.8203125, 0.8242188, 0.8281250, 0.8359375, 0.8398438, 0.8476562, 0.8515625, 0.8593750, 0.8632812, 0.8710938, 0.8750000, 0.8828125, 0.8867188, 0.8906250, 0.8984375, 0.9023438, 0.9101562, 0.9140625, 0.9218750, 0.9257812, 0.9335938, 0.9375000, 0.9414062, 0.9492188, 0.9531250, 0.9609375, 0.9648438, 0.9726562, 0.9765625, 0.9843750, 0.9882812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0273438, 0.0351562, 0.0429688, 0.0507812, 0.0585938, 0.0664062, 0.0703125, 0.0781250, 0.0859375, 0.0937500, 0.1015625, 0.1093750, 0.1171875, 0.1250000, 0.1328125, 0.1367188, 0.1445312, 0.1523438, 0.1601562, 0.1679688, 0.1757812, 0.1835938, 0.1914062, 0.1992188, 0.2031250, 0.2109375, 0.2187500, 0.2265625, 0.2343750, 0.2421875, 0.2500000, 0.2578125, 0.2656250, 0.2695312, 0.2773438, 0.2851562, 0.2929688, 0.3007812, 0.3085938, 0.3164062, 0.3242188, 0.3320312, 0.3359375, 0.3437500, 0.3515625, 0.3593750, 0.3671875, 0.3750000, 0.3828125, 0.3906250, 0.3984375, 0.4023438, 0.4101562, 0.4179688, 0.4257812, 0.4335938, 0.4414062, 0.4492188, 0.4570312, 0.4648438, 0.4687500, 0.4765625, 0.4843750, 0.4921875, 0.5000000, 0.5078125, 0.5156250, 0.5234375, 0.5312500, 0.5351562, 0.5429688, 0.5507812, 0.5585938, 0.5664062, 0.5742188, 0.5820312, 0.5898438, 0.5976562, 0.6015625, 0.6093750, 0.6171875, 0.6250000, 0.6328125, 0.6406250, 0.6484375, 0.6562500, 0.6640625, 0.6679688, 0.6757812, 0.6835938, 0.6914062, 0.6992188, 0.7070312, 0.7148438, 0.7226562, 0.7304688, 0.7343750, 0.7421875, 0.7500000, 0.7578125, 0.7656250, 0.7734375, 0.7812500, 0.7890625, 0.7968750, 0.8007812, 0.8085938, 0.8164062, 0.8242188, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8632812, 0.8671875, 0.8750000, 0.8828125, 0.8906250, 0.8984375, 0.9062500, 0.9140625, 0.9218750, 0.9296875, 0.9335938, 0.9414062, 0.9492188, 0.9570312, 0.9648438, 0.9726562, 0.9804688, 0.9882812, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0898438, 0.1054688, 0.1210938, 0.1367188, 0.1523438, 0.1679688, 0.1835938, 0.1992188, 0.2109375, 0.2265625, 0.2421875, 0.2578125, 0.2734375, 0.2890625, 0.3046875, 0.3203125, 0.3359375, 0.3515625, 0.3671875, 0.3828125, 0.3984375, 0.4101562, 0.4257812, 0.4414062, 0.4570312, 0.4726562, 0.4882812, 0.5039062, 0.5195312, 0.5351562, 0.5507812, 0.5664062, 0.5820312, 0.5976562, 0.6093750, 0.6250000, 0.6406250, 0.6562500, 0.6718750, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7500000, 0.7656250, 0.7812500, 0.7968750, 0.8085938, 0.8242188, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 4 :: BLUE ### color_map_luts['idl04'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0273438, 0.0585938, 0.0859375, 0.1171875, 0.1445312, 0.1757812, 0.2031250, 0.2343750, 0.2617188, 0.2929688, 0.3203125, 0.3515625, 0.3789062, 0.4101562, 0.4375000, 0.4687500, 0.4882812, 0.5078125, 0.5273438, 0.5468750, 0.5664062, 0.5859375, 0.6054688, 0.6250000, 0.6445312, 0.6640625, 0.6835938, 0.7031250, 0.7226562, 0.7421875, 0.7617188, 0.7812500, 0.7812500, 0.7851562, 0.7851562, 0.7890625, 0.7890625, 0.7929688, 0.7929688, 0.7968750, 0.7968750, 0.8007812, 0.8007812, 0.8046875, 0.8046875, 0.8085938, 0.8085938, 0.8125000, 0.8125000, 0.8164062, 0.8164062, 0.8203125, 0.8203125, 0.8242188, 0.8242188, 0.8281250, 0.8281250, 0.8320312, 0.8320312, 0.8359375, 0.8359375, 0.8398438, 0.8398438, 0.8437500, 0.8437500, 0.8476562, 0.8476562, 0.8515625, 0.8515625, 0.8554688, 0.8554688, 0.8593750, 0.8593750, 0.8632812, 0.8632812, 0.8671875, 0.8671875, 0.8710938, 0.8710938, 0.8750000, 0.8750000, 0.8789062, 0.8789062, 0.8828125, 0.8828125, 0.8867188, 0.8867188, 0.8906250, 0.8906250, 0.8945312, 0.8945312, 0.8984375, 0.8984375, 0.9023438, 0.9023438, 0.9062500, 0.9062500, 0.9101562, 0.9101562, 0.9140625, 0.9140625, 0.9179688, 0.9179688, 0.9218750, 0.9218750, 0.9257812, 0.9257812, 0.9296875, 0.9296875, 0.9335938, 0.9335938, 0.9375000, 0.9375000, 0.9414062, 0.9414062, 0.9453125, 0.9453125, 0.9492188, 0.9492188, 0.9531250, 0.9531250, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9765625, 0.9804688, 0.9804688, 0.9843750, 0.9843750, 0.9882812, 0.9882812, 0.9921875, 0.9921875, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0234375, 0.0351562, 0.0468750, 0.0585938, 0.0703125, 0.0820312, 0.0976562, 0.1093750, 0.1210938, 0.1328125, 0.1445312, 0.1562500, 0.1679688, 0.1796875, 0.1953125, 0.2070312, 0.2187500, 0.2304688, 0.2421875, 0.2539062, 0.2656250, 0.2773438, 0.2929688, 0.3046875, 0.3164062, 0.3281250, 0.3398438, 0.3515625, 0.3632812, 0.3750000, 0.3906250, 0.4023438, 0.4140625, 0.4257812, 0.4375000, 0.4492188, 0.4609375, 0.4726562, 0.4882812, 0.5000000, 0.5117188, 0.5234375, 0.5351562, 0.5468750, 0.5585938, 0.5703125, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5820312, 0.5781250, 0.5781250, 0.5742188, 0.5703125, 0.5703125, 0.5664062, 0.5664062, 0.5625000, 0.5585938, 0.5585938, 0.5546875, 0.5507812, 0.5507812, 0.5468750, 0.5468750, 0.5351562, 0.5273438, 0.5156250, 0.5078125, 0.4960938, 0.4882812, 0.4765625, 0.4687500, 0.4570312, 0.4492188, 0.4375000, 0.4296875, 0.4179688, 0.4101562, 0.3984375, 0.3906250, 0.3632812, 0.3398438, 0.3164062, 0.2929688, 0.2656250, 0.2421875, 0.2187500, 0.1953125, 0.1679688, 0.1445312, 0.1210938, 0.0976562, 0.0703125, 0.0468750, 0.0234375, 0.0000000, 0.0078125, 0.0156250, 0.0234375, 0.0351562, 0.0429688, 0.0507812, 0.0625000, 0.0703125, 0.0781250, 0.0898438, 0.0976562, 0.1054688, 0.1132812, 0.1250000, 0.1328125, 0.1406250, 0.1523438, 0.1601562, 0.1679688, 0.1796875, 0.1875000, 0.1953125, 0.2070312, 0.2148438, 0.2226562, 0.2304688, 0.2421875, 0.2500000, 0.2578125, 0.2695312, 0.2773438, 0.2851562, 0.2968750, 0.3046875, 0.3125000, 0.3242188, 0.3320312, 0.3398438, 0.3476562, 0.3593750, 0.3671875, 0.3750000, 0.3867188, 0.3945312, 0.4023438, 0.4140625, 0.4218750, 0.4296875, 0.4414062, 0.4492188, 0.4570312, 0.4648438, 0.4765625, 0.4843750, 0.4921875, 0.5039062, 0.5117188, 0.5195312, 0.5312500, 0.5390625, 0.5468750, 0.5546875, 0.5664062, 0.5742188, 0.5820312, 0.5937500, 0.6015625, 0.6093750, 0.6210938, 0.6289062, 0.6367188, 0.6484375, 0.6562500, 0.6640625, 0.6718750, 0.6835938, 0.6914062, 0.6992188, 0.7109375, 0.7187500, 0.7265625, 0.7382812, 0.7460938, 0.7539062, 0.7656250, 0.7734375, 0.7812500, 0.7890625, 0.8007812, 0.8085938, 0.8164062, 0.8281250, 0.8359375, 0.8437500, 0.8554688, 0.8632812, 0.8710938, 0.8828125, 0.8906250, 0.8984375, 0.9062500, 0.9179688, 0.9257812, 0.9335938, 0.9453125, 0.9531250, 0.9609375, 0.9726562, 0.9804688, 0.9882812, 0.9960938]), array([ 0.0000000, 0.0078125, 0.0156250, 0.0234375, 0.0312500, 0.0390625, 0.0468750, 0.0546875, 0.0625000, 0.0703125, 0.0781250, 0.0859375, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1367188, 0.1445312, 0.1523438, 0.1601562, 0.1679688, 0.1757812, 0.1835938, 0.1953125, 0.2031250, 0.2109375, 0.2187500, 0.2265625, 0.2343750, 0.2421875, 0.2500000, 0.2578125, 0.2656250, 0.2734375, 0.2812500, 0.2929688, 0.3007812, 0.3085938, 0.3164062, 0.3242188, 0.3320312, 0.3398438, 0.3476562, 0.3554688, 0.3632812, 0.3710938, 0.3789062, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3750000, 0.3632812, 0.3515625, 0.3398438, 0.3281250, 0.3164062, 0.3046875, 0.2929688, 0.2773438, 0.2656250, 0.2539062, 0.2421875, 0.2304688, 0.2187500, 0.2070312, 0.1953125, 0.1796875, 0.1679688, 0.1562500, 0.1445312, 0.1328125, 0.1210938, 0.1093750, 0.0976562, 0.0820312, 0.0703125, 0.0585938, 0.0468750, 0.0351562, 0.0234375, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 5 :: STD GAMMA-II ### color_map_luts['idl05'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0351562, 0.0546875, 0.0742188, 0.0898438, 0.1093750, 0.1289062, 0.1484375, 0.1640625, 0.1835938, 0.2031250, 0.2226562, 0.2382812, 0.2578125, 0.2773438, 0.2968750, 0.3164062, 0.3164062, 0.3164062, 0.3164062, 0.3164062, 0.3164062, 0.3164062, 0.3164062, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3085938, 0.3281250, 0.3476562, 0.3671875, 0.3867188, 0.4062500, 0.4257812, 0.4453125, 0.4648438, 0.4843750, 0.5039062, 0.5234375, 0.5429688, 0.5625000, 0.5820312, 0.6015625, 0.6210938, 0.6406250, 0.6601562, 0.6796875, 0.7031250, 0.7226562, 0.7421875, 0.7656250, 0.7851562, 0.8046875, 0.8281250, 0.8476562, 0.8671875, 0.8906250, 0.9101562, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9687500, 0.9375000, 0.9062500, 0.8789062, 0.8476562, 0.8164062, 0.7890625, 0.7578125, 0.7265625, 0.6992188, 0.6679688, 0.6367188, 0.6562500, 0.6757812, 0.6953125, 0.7148438, 0.7343750, 0.7539062, 0.7734375, 0.7929688, 0.8164062, 0.8359375, 0.8554688, 0.8750000, 0.8945312, 0.9140625, 0.9335938, 0.9531250, 0.9726562, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0195312, 0.0390625, 0.0625000, 0.0820312, 0.1054688, 0.1250000, 0.1445312, 0.1679688, 0.1875000, 0.2109375, 0.2304688, 0.2500000, 0.2734375, 0.2929688, 0.3164062, 0.3320312, 0.3515625, 0.3710938, 0.3906250, 0.4101562, 0.4257812, 0.4453125, 0.4648438, 0.4843750, 0.5039062, 0.5234375, 0.5390625, 0.5585938, 0.5781250, 0.5976562, 0.6171875, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6367188, 0.6601562, 0.6835938, 0.7070312, 0.7304688, 0.7539062, 0.7773438, 0.8007812, 0.8281250, 0.8515625, 0.8750000, 0.8984375, 0.9218750, 0.9453125, 0.9687500, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0195312, 0.0390625, 0.0585938, 0.0781250, 0.1015625, 0.1210938, 0.1406250, 0.1601562, 0.1796875, 0.2031250, 0.2226562, 0.2421875, 0.2617188, 0.2812500, 0.3046875, 0.3242188, 0.3437500, 0.3632812, 0.3828125, 0.4062500, 0.4257812, 0.4453125, 0.4648438, 0.4843750, 0.5078125, 0.5273438, 0.5468750, 0.5664062, 0.5859375, 0.6093750, 0.6289062, 0.6484375, 0.6679688, 0.6875000, 0.7109375, 0.7304688, 0.7500000, 0.7695312, 0.7890625, 0.8125000, 0.8320312, 0.8515625, 0.8710938, 0.8906250, 0.9140625, 0.9335938, 0.9531250, 0.9726562, 0.9960938, 0.9765625, 0.9570312, 0.9335938, 0.9140625, 0.8906250, 0.8710938, 0.8515625, 0.8281250, 0.8085938, 0.7851562, 0.7656250, 0.7421875, 0.7226562, 0.7031250, 0.6796875, 0.6601562, 0.6367188, 0.6171875, 0.5937500, 0.5742188, 0.5546875, 0.5312500, 0.5117188, 0.4882812, 0.4687500, 0.4453125, 0.4257812, 0.4062500, 0.3828125, 0.3632812, 0.3398438, 0.3203125, 0.2968750, 0.2773438, 0.2578125, 0.2343750, 0.2148438, 0.1914062, 0.1718750, 0.1484375, 0.1289062, 0.1093750, 0.0859375, 0.0664062, 0.0429688, 0.0234375, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0351562, 0.0546875, 0.0742188, 0.0937500, 0.1093750, 0.1289062, 0.1484375, 0.1679688, 0.1875000, 0.2070312, 0.2226562, 0.2421875, 0.2617188, 0.2812500, 0.3007812, 0.3203125, 0.3007812, 0.2773438, 0.2539062, 0.2304688, 0.2070312, 0.1835938, 0.1601562, 0.1406250, 0.1171875, 0.0937500, 0.0703125, 0.0468750, 0.0234375, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0234375, 0.0351562, 0.0468750, 0.0625000, 0.0742188, 0.0859375, 0.0976562, 0.1132812, 0.1250000, 0.1367188, 0.1484375, 0.1601562, 0.1757812, 0.1875000, 0.1992188, 0.2109375, 0.2265625, 0.2382812, 0.2500000, 0.2617188, 0.2773438, 0.2890625, 0.3007812, 0.3125000, 0.3242188, 0.3398438, 0.3515625, 0.3632812, 0.3750000, 0.3906250, 0.4023438, 0.4140625, 0.4257812, 0.4375000, 0.4531250, 0.4648438, 0.4765625, 0.4882812, 0.5039062, 0.5156250, 0.5273438, 0.5390625, 0.5546875, 0.5664062, 0.5781250, 0.5898438, 0.6015625, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6679688, 0.6796875, 0.6914062, 0.7031250, 0.7148438, 0.7304688, 0.7421875, 0.7539062, 0.7656250, 0.7812500, 0.7929688, 0.8046875, 0.8164062, 0.8320312, 0.8437500, 0.8554688, 0.8671875, 0.8789062, 0.8945312, 0.9062500, 0.9179688, 0.9296875, 0.9453125, 0.9570312, 0.9687500, 0.9804688, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 6 :: PRISM ### color_map_luts['idl06'] = \ ( array([ 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0859375, 0.1015625, 0.1171875, 0.1328125, 0.1484375, 0.1601562, 0.1757812, 0.1914062, 0.2070312, 0.2226562, 0.2343750, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3085938, 0.3242188, 0.3398438, 0.3554688, 0.3710938, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4453125, 0.4570312, 0.4726562, 0.4882812, 0.5039062, 0.5195312, 0.5351562, 0.5468750, 0.5625000, 0.5781250, 0.5937500, 0.6093750, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6953125, 0.7109375, 0.7265625, 0.7421875, 0.7578125, 0.7695312, 0.7851562, 0.8007812, 0.8164062, 0.8320312, 0.8437500, 0.8593750, 0.8750000, 0.8906250, 0.9062500, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.8984375, 0.8828125, 0.8671875, 0.8515625, 0.8359375, 0.8203125, 0.8007812, 0.7851562, 0.7695312, 0.7539062, 0.7382812, 0.7187500, 0.7031250, 0.6875000, 0.6718750, 0.6562500, 0.6406250, 0.6210938, 0.6054688, 0.5898438, 0.5742188, 0.5585938, 0.5390625, 0.5234375, 0.5078125, 0.4921875, 0.4765625, 0.4609375, 0.4414062, 0.4257812, 0.4101562, 0.3945312, 0.3789062, 0.3593750, 0.3437500, 0.3281250, 0.3125000, 0.2968750, 0.2812500, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1992188, 0.1796875, 0.1640625, 0.1484375, 0.1328125, 0.1171875, 0.1015625, 0.0820312, 0.0664062, 0.0507812, 0.0351562, 0.0195312, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0898438, 0.1054688, 0.1210938, 0.1367188, 0.1523438, 0.1679688, 0.1835938, 0.1992188, 0.2148438, 0.2304688, 0.2460938, 0.2617188, 0.2773438, 0.2929688, 0.3085938, 0.3242188, 0.3398438, 0.3554688, 0.3710938, 0.3867188, 0.4023438, 0.4179688, 0.4335938, 0.4492188, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5898438, 0.6054688, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6992188, 0.7148438, 0.7304688, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8085938, 0.8242188, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8554688, 0.8398438, 0.8242188, 0.8085938, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6640625, 0.6484375, 0.6328125, 0.6171875, 0.6015625, 0.5859375, 0.5703125, 0.5546875, 0.5390625, 0.5234375, 0.5078125, 0.4921875, 0.4765625, 0.4609375, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3828125, 0.3671875, 0.3515625, 0.3320312, 0.3164062, 0.3007812, 0.2851562, 0.2695312, 0.2539062, 0.2382812, 0.2226562, 0.2070312, 0.1914062, 0.1757812, 0.1601562, 0.1445312, 0.1289062, 0.1132812, 0.0976562, 0.0820312, 0.0664062, 0.0507812, 0.0351562, 0.0195312, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0898438, 0.1054688, 0.1171875, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1953125, 0.2109375, 0.2226562, 0.2382812, 0.2539062, 0.2695312, 0.2851562, 0.3007812, 0.3164062, 0.3320312, 0.3437500, 0.3593750, 0.3750000, 0.3906250, 0.4062500, 0.4218750, 0.4375000, 0.4492188, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5546875, 0.5703125, 0.5859375, 0.6015625, 0.6171875, 0.6328125, 0.6484375, 0.6640625, 0.6757812, 0.6914062, 0.7070312, 0.7226562, 0.7382812, 0.7539062, 0.7695312, 0.7812500, 0.7968750, 0.8125000, 0.8281250, 0.8437500, 0.8593750, 0.8750000, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8554688, 0.8398438, 0.8242188, 0.8085938, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6640625, 0.6484375, 0.6328125, 0.6171875, 0.6015625, 0.5859375, 0.5703125, 0.5546875, 0.5390625, 0.5234375, 0.5078125, 0.4921875, 0.4765625, 0.4609375, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3828125, 0.3671875, 0.3515625, 0.3320312, 0.3164062, 0.3007812, 0.2851562, 0.2695312, 0.2539062, 0.2382812, 0.2226562, 0.2070312, 0.1914062, 0.1757812, 0.1601562, 0.1445312, 0.1289062, 0.1132812, 0.0976562, 0.0820312, 0.0664062, 0.0507812, 0.0351562, 0.0195312, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 7 :: RED-PURPLE ### color_map_luts['idl07'] = \ ( array([ 0.0000000, 0.0000000, 0.0117188, 0.0234375, 0.0390625, 0.0507812, 0.0664062, 0.0781250, 0.0937500, 0.1054688, 0.1171875, 0.1289062, 0.1406250, 0.1523438, 0.1640625, 0.1757812, 0.1875000, 0.1992188, 0.2109375, 0.2226562, 0.2343750, 0.2460938, 0.2578125, 0.2695312, 0.2812500, 0.2929688, 0.3046875, 0.3164062, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3515625, 0.3593750, 0.3671875, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3984375, 0.4062500, 0.4140625, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4453125, 0.4531250, 0.4609375, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4921875, 0.5000000, 0.5078125, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5390625, 0.5468750, 0.5546875, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5859375, 0.5937500, 0.6015625, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6328125, 0.6406250, 0.6484375, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6796875, 0.6875000, 0.6953125, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7734375, 0.7812500, 0.7890625, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8671875, 0.8750000, 0.8828125, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9609375, 0.9687500, 0.9765625, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9882812, 0.9921875]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0781250, 0.0781250, 0.0781250, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0937500, 0.0937500, 0.0937500, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1171875, 0.1250000, 0.1328125, 0.1406250, 0.1484375, 0.1562500, 0.1640625, 0.1718750, 0.1796875, 0.1875000, 0.1953125, 0.2031250, 0.2109375, 0.2187500, 0.2265625, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2578125, 0.2656250, 0.2734375, 0.2812500, 0.2890625, 0.2968750, 0.3046875, 0.3125000, 0.3203125, 0.3281250, 0.3359375, 0.3437500, 0.3515625, 0.3593750, 0.3671875, 0.3750000, 0.3828125, 0.3906250, 0.3984375, 0.4062500, 0.4179688, 0.4296875, 0.4414062, 0.4531250, 0.4648438, 0.4765625, 0.4882812, 0.5000000, 0.5117188, 0.5234375, 0.5351562, 0.5468750, 0.5585938, 0.5703125, 0.5820312, 0.5937500, 0.6054688, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6640625, 0.6757812, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7500000, 0.7695312, 0.7890625, 0.8085938, 0.8281250, 0.8476562, 0.8671875, 0.8867188, 0.9062500, 0.9257812, 0.9453125, 0.9648438, 0.9843750, 0.9843750, 0.9882812, 0.9921875]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0117188, 0.0117188, 0.0117188, 0.0117188, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0351562, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0664062, 0.0664062, 0.0703125, 0.0703125, 0.0742188, 0.0781250, 0.0859375, 0.0898438, 0.0976562, 0.0976562, 0.1015625, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1250000, 0.1250000, 0.1289062, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1523438, 0.1523438, 0.1562500, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1796875, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2109375, 0.2109375, 0.2070312, 0.2070312, 0.2031250, 0.2109375, 0.2187500, 0.2265625, 0.2343750, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3203125, 0.3203125, 0.3242188, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.4023438, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4843750, 0.4843750, 0.4882812, 0.4882812, 0.4921875, 0.4960938, 0.5039062, 0.5117188, 0.5195312, 0.5234375, 0.5312500, 0.5390625, 0.5468750, 0.5507812, 0.5585938, 0.5625000, 0.5703125, 0.5742188, 0.5820312, 0.5898438, 0.5976562, 0.6015625, 0.6093750, 0.6132812, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6601562, 0.6640625, 0.6718750, 0.6757812, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.7031250, 0.7070312, 0.7148438, 0.7187500, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7656250, 0.7695312, 0.7773438, 0.7812500, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9843750, 0.9882812, 0.9921875]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 8 :: GREEN ### color_map_luts['idl08'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0156250, 0.0234375, 0.0312500, 0.0351562, 0.0429688, 0.0468750, 0.0546875, 0.0625000, 0.0664062, 0.0742188, 0.0781250, 0.0859375, 0.0937500, 0.0976562, 0.1054688, 0.1132812, 0.1171875, 0.1250000, 0.1289062, 0.1367188, 0.1445312, 0.1484375, 0.1562500, 0.1601562, 0.1679688, 0.1757812, 0.1796875, 0.1875000, 0.1953125, 0.1992188, 0.2070312, 0.2109375, 0.2187500, 0.2265625, 0.2304688, 0.2382812, 0.2421875, 0.2500000, 0.2578125, 0.2617188, 0.2695312, 0.2773438, 0.2812500, 0.2890625, 0.2929688, 0.3007812, 0.3085938, 0.3125000, 0.3203125, 0.3242188, 0.3320312, 0.3398438, 0.3437500, 0.3515625, 0.3554688, 0.3632812, 0.3710938, 0.3750000, 0.3828125, 0.3906250, 0.3945312, 0.4023438, 0.4062500, 0.4140625, 0.4218750, 0.4257812, 0.4335938, 0.4375000, 0.4453125, 0.4531250, 0.4570312, 0.4648438, 0.4726562, 0.4765625, 0.4843750, 0.4882812, 0.4960938, 0.5039062, 0.5078125, 0.5156250, 0.5195312, 0.5273438, 0.5351562, 0.5390625, 0.5468750, 0.5546875, 0.5585938, 0.5664062, 0.5703125, 0.5781250, 0.5859375, 0.5898438, 0.5976562, 0.6015625, 0.6093750, 0.6171875, 0.6210938, 0.6289062, 0.6367188, 0.6406250, 0.6484375, 0.6523438, 0.6601562, 0.6679688, 0.6718750, 0.6796875, 0.6835938, 0.6914062, 0.6992188, 0.7031250, 0.7109375, 0.7148438, 0.7226562, 0.7304688, 0.7343750, 0.7421875, 0.7500000, 0.7539062, 0.7617188, 0.7656250, 0.7734375, 0.7812500, 0.7851562, 0.7929688, 0.7968750, 0.8046875, 0.8125000, 0.8164062, 0.8242188, 0.8320312, 0.8359375, 0.8437500, 0.8476562, 0.8554688, 0.8632812, 0.8671875, 0.8750000, 0.8789062, 0.8867188, 0.8945312, 0.8984375, 0.9062500, 0.9140625, 0.9179688, 0.9257812, 0.9296875, 0.9375000, 0.9453125, 0.9492188, 0.9570312, 0.9609375, 0.9687500, 0.9765625, 0.9804688, 0.9882812, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0234375, 0.0390625, 0.0507812, 0.0664062, 0.0781250, 0.0937500, 0.1054688, 0.1210938, 0.1328125, 0.1445312, 0.1601562, 0.1718750, 0.1875000, 0.1992188, 0.2148438, 0.2265625, 0.2421875, 0.2539062, 0.2656250, 0.2812500, 0.2929688, 0.3085938, 0.3203125, 0.3359375, 0.3476562, 0.3632812, 0.3750000, 0.3867188, 0.4023438, 0.4140625, 0.4296875, 0.4414062, 0.4570312, 0.4687500, 0.4843750, 0.4960938, 0.5078125, 0.5234375, 0.5351562, 0.5507812, 0.5625000, 0.5781250, 0.5898438, 0.6054688, 0.6171875, 0.6289062, 0.6445312, 0.6562500, 0.6718750, 0.6835938, 0.6992188, 0.7109375, 0.7265625, 0.7382812, 0.7500000, 0.7656250, 0.7773438, 0.7929688, 0.8046875, 0.8203125, 0.8320312, 0.8476562, 0.8593750, 0.8710938, 0.8867188, 0.8984375, 0.9140625, 0.9257812, 0.9414062, 0.9531250, 0.9687500, 0.9804688, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 9 :: GRN ### color_map_luts['idl09'] = \ ( array([ 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0078125, 0.0117188, 0.0117188, 0.0156250, 0.0156250, 0.0156250, 0.0195312, 0.0195312, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0273438, 0.0273438, 0.0312500, 0.0312500, 0.0351562, 0.0351562, 0.0351562, 0.0390625, 0.0390625, 0.0390625, 0.0429688, 0.0429688, 0.0468750, 0.0468750, 0.0468750, 0.0507812, 0.0507812, 0.0507812, 0.0546875, 0.0546875, 0.0585938, 0.0585938, 0.0585938, 0.0625000, 0.0625000, 0.0664062, 0.0664062, 0.0664062, 0.0703125, 0.0703125, 0.0703125, 0.0742188, 0.0742188, 0.0781250, 0.0781250, 0.0781250, 0.0820312, 0.0820312, 0.0820312, 0.0859375, 0.0859375, 0.0898438, 0.0898438, 0.0898438, 0.0937500, 0.0937500, 0.0976562, 0.0976562, 0.0976562, 0.1015625, 0.1015625, 0.1015625, 0.1054688, 0.1054688, 0.1093750, 0.1093750, 0.1093750, 0.1132812, 0.1132812, 0.1132812, 0.1171875, 0.1171875, 0.1210938, 0.1210938, 0.1210938, 0.1250000, 0.1250000, 0.1289062, 0.1289062, 0.1289062, 0.1328125, 0.1328125, 0.1328125, 0.1367188, 0.1367188, 0.1406250, 0.1406250, 0.1406250, 0.1445312, 0.1445312, 0.1445312, 0.1484375, 0.1484375, 0.1523438, 0.1523438, 0.1523438, 0.1562500, 0.1562500, 0.1601562, 0.1601562, 0.1601562, 0.1640625, 0.1640625, 0.1640625, 0.1679688, 0.1679688, 0.1718750, 0.1718750, 0.1718750, 0.1757812, 0.1757812, 0.1757812, 0.1796875, 0.1796875, 0.1835938, 0.1835938, 0.1835938, 0.1875000, 0.1875000, 0.1914062, 0.1914062, 0.1914062, 0.1953125, 0.1953125, 0.1953125, 0.1992188, 0.1992188, 0.2031250, 0.2031250, 0.2031250, 0.2109375, 0.2109375, 0.2148438, 0.2187500, 0.2187500, 0.2226562, 0.2265625, 0.2265625, 0.2304688, 0.2343750, 0.2343750, 0.2382812, 0.2421875, 0.2421875, 0.2460938, 0.2500000, 0.2500000, 0.2578125, 0.2656250, 0.2734375, 0.2773438, 0.2851562, 0.2929688, 0.3007812, 0.3085938, 0.3164062, 0.3242188, 0.3320312, 0.3359375, 0.3437500, 0.3515625, 0.3593750, 0.3671875, 0.3750000, 0.3828125, 0.3867188, 0.3945312, 0.4023438, 0.4101562, 0.4179688, 0.4257812, 0.4335938, 0.4414062, 0.4453125, 0.4531250, 0.4609375, 0.4687500, 0.4765625, 0.4843750, 0.4921875, 0.4960938, 0.5039062, 0.5117188, 0.5195312, 0.5273438, 0.5351562, 0.5429688, 0.5507812, 0.5546875, 0.5625000, 0.5703125, 0.5781250, 0.5859375, 0.5937500, 0.6015625, 0.6093750, 0.6132812, 0.6210938, 0.6289062, 0.6367188, 0.6445312, 0.6523438, 0.6601562, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6953125, 0.7031250, 0.7109375, 0.7187500, 0.7226562, 0.7304688, 0.7382812, 0.7460938, 0.7539062, 0.7617188, 0.7695312, 0.7734375, 0.7812500, 0.7890625, 0.7968750, 0.8046875, 0.8125000, 0.8203125, 0.8281250, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8632812, 0.8710938, 0.8789062, 0.8828125, 0.8906250, 0.8984375, 0.9062500, 0.9140625, 0.9218750, 0.9296875, 0.9375000, 0.9414062, 0.9492188, 0.9570312, 0.9648438, 0.9726562, 0.9804688, 0.9882812, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0039062, 0.0078125, 0.0078125, 0.0117188, 0.0156250, 0.0156250, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0312500, 0.0312500, 0.0351562, 0.0390625, 0.0390625, 0.0429688, 0.0468750, 0.0468750, 0.0507812, 0.0546875, 0.0546875, 0.0585938, 0.0625000, 0.0625000, 0.0664062, 0.0703125, 0.0703125, 0.0742188, 0.0781250, 0.0781250, 0.0820312, 0.0859375, 0.0859375, 0.0898438, 0.0937500, 0.0937500, 0.0976562, 0.1015625, 0.1015625, 0.1054688, 0.1093750, 0.1093750, 0.1132812, 0.1171875, 0.1171875, 0.1210938, 0.1250000, 0.1250000, 0.1289062, 0.1328125, 0.1328125, 0.1367188, 0.1406250, 0.1406250, 0.1445312, 0.1484375, 0.1484375, 0.1523438, 0.1562500, 0.1562500, 0.1601562, 0.1640625, 0.1640625, 0.1679688, 0.1718750, 0.1718750, 0.1757812, 0.1796875, 0.1796875, 0.1835938, 0.1875000, 0.1875000, 0.1914062, 0.1953125, 0.1953125, 0.1992188, 0.2031250, 0.2031250, 0.2070312, 0.2109375, 0.2109375, 0.2148438, 0.2187500, 0.2187500, 0.2226562, 0.2265625, 0.2265625, 0.2304688, 0.2343750, 0.2343750, 0.2382812, 0.2421875, 0.2421875, 0.2460938, 0.2500000, 0.2500000, 0.2539062, 0.2578125, 0.2578125, 0.2617188, 0.2656250, 0.2656250, 0.2695312, 0.2734375, 0.2734375, 0.2773438, 0.2812500, 0.2812500, 0.2851562, 0.2890625, 0.2890625, 0.2929688, 0.2968750, 0.2968750, 0.3007812, 0.3046875, 0.3046875, 0.3085938, 0.3125000, 0.3125000, 0.3164062, 0.3203125, 0.3203125, 0.3242188, 0.3281250, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3515625, 0.3554688, 0.3593750, 0.3671875, 0.3710938, 0.3750000, 0.3828125, 0.3867188, 0.3906250, 0.3984375, 0.4023438, 0.4062500, 0.4140625, 0.4179688, 0.4218750, 0.4296875, 0.4335938, 0.4375000, 0.4453125, 0.4492188, 0.4531250, 0.4609375, 0.4648438, 0.4687500, 0.4765625, 0.4804688, 0.4843750, 0.4921875, 0.4960938, 0.5000000, 0.5078125, 0.5117188, 0.5156250, 0.5234375, 0.5273438, 0.5312500, 0.5390625, 0.5429688, 0.5468750, 0.5546875, 0.5585938, 0.5664062, 0.5703125, 0.5742188, 0.5820312, 0.5859375, 0.5898438, 0.5976562, 0.6015625, 0.6054688, 0.6132812, 0.6171875, 0.6210938, 0.6289062, 0.6328125, 0.6367188, 0.6445312, 0.6484375, 0.6523438, 0.6601562, 0.6640625, 0.6679688, 0.6757812, 0.6796875, 0.6835938, 0.6914062, 0.6953125, 0.6992188, 0.7070312, 0.7109375, 0.7148438, 0.7226562, 0.7265625, 0.7304688, 0.7382812, 0.7421875, 0.7460938, 0.7539062, 0.7578125, 0.7617188, 0.7695312, 0.7734375, 0.7812500, 0.7851562, 0.7890625, 0.7968750, 0.8007812, 0.8046875, 0.8125000, 0.8164062, 0.8203125, 0.8281250, 0.8320312, 0.8359375, 0.8437500, 0.8476562, 0.8515625, 0.8593750, 0.8632812, 0.8671875, 0.8750000, 0.8789062, 0.8828125, 0.8906250, 0.8945312, 0.8984375, 0.9062500, 0.9101562, 0.9140625, 0.9218750, 0.9257812, 0.9296875, 0.9375000, 0.9414062, 0.9453125, 0.9531250, 0.9570312, 0.9609375, 0.9687500, 0.9726562, 0.9765625, 0.9843750, 0.9882812, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0117188, 0.0117188, 0.0117188, 0.0117188, 0.0156250, 0.0156250, 0.0156250, 0.0195312, 0.0195312, 0.0195312, 0.0195312, 0.0234375, 0.0234375, 0.0234375, 0.0234375, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0312500, 0.0312500, 0.0312500, 0.0312500, 0.0351562, 0.0351562, 0.0351562, 0.0390625, 0.0390625, 0.0390625, 0.0390625, 0.0429688, 0.0429688, 0.0429688, 0.0429688, 0.0468750, 0.0468750, 0.0468750, 0.0468750, 0.0507812, 0.0507812, 0.0507812, 0.0507812, 0.0546875, 0.0546875, 0.0546875, 0.0585938, 0.0585938, 0.0585938, 0.0585938, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0664062, 0.0664062, 0.0664062, 0.0664062, 0.0703125, 0.0703125, 0.0703125, 0.0703125, 0.0742188, 0.0742188, 0.0742188, 0.0781250, 0.0781250, 0.0781250, 0.0781250, 0.0820312, 0.0820312, 0.0820312, 0.0820312, 0.0859375, 0.0859375, 0.0859375, 0.0859375, 0.0898438, 0.0898438, 0.0898438, 0.0898438, 0.0937500, 0.0937500, 0.0937500, 0.0976562, 0.0976562, 0.0976562, 0.0976562, 0.1015625, 0.1015625, 0.1015625, 0.1015625, 0.1054688, 0.1054688, 0.1054688, 0.1054688, 0.1093750, 0.1093750, 0.1093750, 0.1093750, 0.1132812, 0.1132812, 0.1132812, 0.1171875, 0.1171875, 0.1171875, 0.1171875, 0.1210938, 0.1210938, 0.1210938, 0.1210938, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1289062, 0.1289062, 0.1289062, 0.1289062, 0.1328125, 0.1328125, 0.1328125, 0.1367188, 0.1367188, 0.1367188, 0.1367188, 0.1406250, 0.1406250, 0.1406250, 0.1406250, 0.1445312, 0.1445312, 0.1445312, 0.1445312, 0.1484375, 0.1484375, 0.1484375, 0.1484375, 0.1523438, 0.1523438, 0.1523438, 0.1562500, 0.1562500, 0.1562500, 0.1562500, 0.1601562, 0.1601562, 0.1601562, 0.1601562, 0.1640625, 0.1640625, 0.1640625, 0.1640625, 0.1679688, 0.1679688, 0.1679688, 0.1679688, 0.1718750, 0.1718750, 0.1718750, 0.1757812, 0.1757812, 0.1757812, 0.1757812, 0.1796875, 0.1796875, 0.1796875, 0.1796875, 0.1835938, 0.1835938, 0.1835938, 0.1835938, 0.1875000, 0.1835938, 0.1914062, 0.2031250, 0.2148438, 0.2265625, 0.2382812, 0.2500000, 0.2617188, 0.2734375, 0.2851562, 0.2968750, 0.3085938, 0.3203125, 0.3320312, 0.3437500, 0.3515625, 0.3632812, 0.3750000, 0.3867188, 0.3984375, 0.4101562, 0.4218750, 0.4335938, 0.4453125, 0.4570312, 0.4687500, 0.4804688, 0.4921875, 0.5039062, 0.5117188, 0.5234375, 0.5351562, 0.5468750, 0.5585938, 0.5703125, 0.5820312, 0.5937500, 0.6054688, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6640625, 0.6718750, 0.6835938, 0.6953125, 0.7070312, 0.7187500, 0.7304688, 0.7421875, 0.7539062, 0.7656250, 0.7773438, 0.7890625, 0.8007812, 0.8125000, 0.8242188, 0.8320312, 0.8437500, 0.8554688, 0.8671875, 0.8789062, 0.8906250, 0.9023438, 0.9140625, 0.9257812, 0.9375000, 0.9492188, 0.9609375, 0.9726562, 0.9843750, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 10 :: GREEN-PINK ### color_map_luts['idl10'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0195312, 0.0390625, 0.0585938, 0.0781250, 0.0976562, 0.1171875, 0.1367188, 0.1562500, 0.1757812, 0.1953125, 0.2148438, 0.2343750, 0.2539062, 0.2734375, 0.2929688, 0.3125000, 0.3281250, 0.3437500, 0.3593750, 0.3750000, 0.3945312, 0.4140625, 0.4335938, 0.4531250, 0.4726562, 0.4921875, 0.5117188, 0.5312500, 0.5507812, 0.5703125, 0.5898438, 0.6093750, 0.6210938, 0.6328125, 0.6445312, 0.6562500, 0.6679688, 0.6796875, 0.6914062, 0.7031250, 0.7148438, 0.7265625, 0.7382812, 0.7500000, 0.7617188, 0.7734375, 0.7851562, 0.7968750, 0.8085938, 0.8203125, 0.8320312, 0.8437500, 0.8554688, 0.8671875, 0.8789062, 0.8906250, 0.9023438, 0.9140625, 0.9257812, 0.9375000, 0.9492188, 0.9609375, 0.9726562, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0546875, 0.1093750, 0.1679688, 0.2226562, 0.2812500, 0.3164062, 0.3515625, 0.3867188, 0.4218750, 0.4570312, 0.4921875, 0.5273438, 0.5625000, 0.5976562, 0.6328125, 0.6679688, 0.7031250, 0.6992188, 0.6953125, 0.6914062, 0.6875000, 0.6835938, 0.6796875, 0.6757812, 0.6718750, 0.6679688, 0.6640625, 0.6601562, 0.6562500, 0.6523438, 0.6484375, 0.6445312, 0.6406250, 0.6367188, 0.6328125, 0.6289062, 0.6250000, 0.6210938, 0.6171875, 0.6132812, 0.6093750, 0.6015625, 0.5937500, 0.5859375, 0.5781250, 0.5742188, 0.5703125, 0.5664062, 0.5625000, 0.5585938, 0.5546875, 0.5507812, 0.5468750, 0.5429688, 0.5390625, 0.5351562, 0.5312500, 0.5273438, 0.5234375, 0.5195312, 0.5156250, 0.5117188, 0.5078125, 0.5039062, 0.5000000, 0.4921875, 0.4843750, 0.4765625, 0.4687500, 0.4648438, 0.4609375, 0.4570312, 0.4531250, 0.4492188, 0.4453125, 0.4414062, 0.4375000, 0.4335938, 0.4296875, 0.4257812, 0.4218750, 0.4179688, 0.4140625, 0.4101562, 0.4062500, 0.4023438, 0.3984375, 0.3945312, 0.3906250, 0.3867188, 0.3828125, 0.3789062, 0.3750000, 0.3671875, 0.3593750, 0.3515625, 0.3437500, 0.3398438, 0.3359375, 0.3320312, 0.3281250, 0.3242188, 0.3203125, 0.3164062, 0.3125000, 0.3085938, 0.3046875, 0.3007812, 0.2968750, 0.2929688, 0.2890625, 0.2851562, 0.2812500, 0.2773438, 0.2734375, 0.2695312, 0.2656250, 0.2578125, 0.2500000, 0.2421875, 0.2343750, 0.2304688, 0.2265625, 0.2226562, 0.2187500, 0.2109375, 0.2031250, 0.1953125, 0.1875000, 0.1835938, 0.1796875, 0.1757812, 0.1718750, 0.1640625, 0.1562500, 0.1484375, 0.1406250, 0.1328125, 0.1250000, 0.1171875, 0.1093750, 0.1015625, 0.0937500, 0.0859375, 0.0781250, 0.0742188, 0.0703125, 0.0664062, 0.0625000, 0.0546875, 0.0468750, 0.0390625, 0.0312500, 0.0234375, 0.0156250, 0.0078125, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0625000, 0.0781250, 0.0937500, 0.1093750, 0.1250000, 0.1406250, 0.1562500, 0.1718750, 0.1875000, 0.2031250, 0.2187500, 0.2343750, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3125000, 0.3281250, 0.3437500, 0.3593750, 0.3750000, 0.3906250, 0.4062500, 0.4218750, 0.4375000, 0.4531250, 0.4687500, 0.4843750, 0.5000000, 0.5156250, 0.5312500, 0.5468750, 0.5625000, 0.5742188, 0.5859375, 0.5976562, 0.6093750, 0.6250000, 0.6406250, 0.6562500, 0.6718750, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7500000, 0.7656250, 0.7812500, 0.7968750, 0.8125000, 0.8281250, 0.8437500, 0.8593750, 0.8750000, 0.8906250, 0.9062500, 0.9218750, 0.9375000, 0.9531250, 0.9687500, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0156250, 0.0234375, 0.0312500, 0.0390625, 0.0468750, 0.0546875, 0.0585938, 0.0664062, 0.0742188, 0.0820312, 0.0898438, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1367188, 0.1445312, 0.1484375, 0.1562500, 0.1640625, 0.1718750, 0.1796875, 0.1875000, 0.1953125, 0.2070312, 0.2109375, 0.2187500, 0.2265625, 0.2343750, 0.2421875, 0.2500000, 0.2578125, 0.2656250, 0.2695312, 0.2773438, 0.2851562, 0.2929688, 0.3007812, 0.3085938, 0.3164062, 0.3242188, 0.3281250, 0.3359375, 0.3437500, 0.3515625, 0.3593750, 0.3671875, 0.3750000, 0.3867188, 0.3906250, 0.3984375, 0.4062500, 0.4140625, 0.4218750, 0.4296875, 0.4375000, 0.4453125, 0.4531250, 0.4609375, 0.4687500, 0.4765625, 0.4804688, 0.4882812, 0.4960938, 0.5039062, 0.5117188, 0.5195312, 0.5273438, 0.5351562, 0.5390625, 0.5468750, 0.5546875, 0.5625000, 0.5703125, 0.5781250, 0.5859375, 0.5976562, 0.6015625, 0.6093750, 0.6171875, 0.6250000, 0.6328125, 0.6406250, 0.6484375, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6914062, 0.6992188, 0.7070312, 0.7148438, 0.7226562, 0.7304688, 0.7382812, 0.7460938, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7773438, 0.7734375, 0.7695312, 0.7656250, 0.7656250, 0.7656250, 0.7656250, 0.7617188, 0.7617188, 0.7578125, 0.7539062, 0.7500000, 0.7500000, 0.7460938, 0.7460938, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7382812, 0.7382812, 0.7343750, 0.7304688, 0.7265625, 0.7265625, 0.7226562, 0.7226562, 0.7187500, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7304688, 0.7304688, 0.7304688, 0.7304688, 0.7304688, 0.7304688, 0.7304688, 0.7343750, 0.7343750, 0.7343750, 0.7343750, 0.7382812, 0.7382812, 0.7382812, 0.7382812, 0.7382812, 0.7382812, 0.7382812, 0.7382812, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7460938, 0.7460938, 0.7460938, 0.7460938, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 11 :: BLUE-RED ### color_map_luts['idl11'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0625000, 0.0781250, 0.0937500, 0.1093750, 0.1250000, 0.1406250, 0.1562500, 0.1718750, 0.1875000, 0.2031250, 0.2187500, 0.2343750, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3125000, 0.3320312, 0.3476562, 0.3632812, 0.3789062, 0.3945312, 0.4101562, 0.4257812, 0.4414062, 0.4570312, 0.4726562, 0.4882812, 0.5039062, 0.5195312, 0.5351562, 0.5507812, 0.5664062, 0.5820312, 0.5976562, 0.6132812, 0.6289062, 0.6445312, 0.6640625, 0.6796875, 0.6953125, 0.7109375, 0.7265625, 0.7421875, 0.7578125, 0.7734375, 0.7890625, 0.8046875, 0.8203125, 0.8359375, 0.8515625, 0.8671875, 0.8828125, 0.8984375, 0.9140625, 0.9296875, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0312500, 0.0468750, 0.0625000, 0.0820312, 0.0976562, 0.1132812, 0.1289062, 0.1484375, 0.1640625, 0.1796875, 0.1953125, 0.2148438, 0.2304688, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3125000, 0.3281250, 0.3476562, 0.3632812, 0.3789062, 0.3945312, 0.4140625, 0.4296875, 0.4453125, 0.4609375, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5781250, 0.5937500, 0.6132812, 0.6289062, 0.6445312, 0.6601562, 0.6796875, 0.6953125, 0.7109375, 0.7265625, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8125000, 0.8281250, 0.8437500, 0.8593750, 0.8789062, 0.8945312, 0.9101562, 0.9257812, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8554688, 0.8398438, 0.8242188, 0.8085938, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6640625, 0.6484375, 0.6328125, 0.6171875, 0.6015625, 0.5859375, 0.5703125, 0.5546875, 0.5390625, 0.5234375, 0.5078125, 0.4921875, 0.4765625, 0.4609375, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3828125, 0.3671875, 0.3515625, 0.3320312, 0.3164062, 0.3007812, 0.2851562, 0.2695312, 0.2539062, 0.2382812, 0.2226562, 0.2070312, 0.1914062, 0.1757812, 0.1601562, 0.1445312, 0.1289062, 0.1132812, 0.0976562, 0.0820312, 0.0664062, 0.0507812, 0.0351562, 0.0195312, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0312500, 0.0468750, 0.0625000, 0.0820312, 0.0976562, 0.1132812, 0.1289062, 0.1484375, 0.1640625, 0.1796875, 0.1953125, 0.2148438, 0.2304688, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3125000, 0.3281250, 0.3476562, 0.3632812, 0.3789062, 0.3945312, 0.4140625, 0.4296875, 0.4453125, 0.4609375, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5781250, 0.5937500, 0.6132812, 0.6289062, 0.6445312, 0.6601562, 0.6796875, 0.6953125, 0.7109375, 0.7265625, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8125000, 0.8281250, 0.8437500, 0.8593750, 0.8789062, 0.8945312, 0.9101562, 0.9257812, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8515625, 0.8359375, 0.8203125, 0.8046875, 0.7890625, 0.7734375, 0.7578125, 0.7421875, 0.7265625, 0.7070312, 0.6914062, 0.6757812, 0.6601562, 0.6445312, 0.6289062, 0.6132812, 0.5976562, 0.5820312, 0.5625000, 0.5468750, 0.5312500, 0.5156250, 0.5000000, 0.4843750, 0.4687500, 0.4531250, 0.4375000, 0.4179688, 0.4023438, 0.3867188, 0.3710938, 0.3554688, 0.3398438, 0.3242188, 0.3085938, 0.2929688, 0.2734375, 0.2578125, 0.2421875, 0.2265625, 0.2109375, 0.1953125, 0.1796875, 0.1640625, 0.1484375, 0.1289062, 0.1132812, 0.0976562, 0.0820312, 0.0664062, 0.0507812, 0.0351562, 0.0195312, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 12 :: 16 LEVEL ### color_map_luts['idl12'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8632812, 0.8632812, 0.8632812, 0.8632812, 0.8632812, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8710938, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.3281250, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.7421875, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.8593750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 13 :: RAINBOW ### color_map_luts['idl13'] = \ ( array([ 0.0000000, 0.0156250, 0.0351562, 0.0507812, 0.0703125, 0.0859375, 0.1054688, 0.1210938, 0.1406250, 0.1562500, 0.1757812, 0.1953125, 0.2109375, 0.2265625, 0.2382812, 0.2500000, 0.2656250, 0.2695312, 0.2812500, 0.2890625, 0.3007812, 0.3085938, 0.3125000, 0.3203125, 0.3242188, 0.3320312, 0.3281250, 0.3359375, 0.3398438, 0.3437500, 0.3359375, 0.3398438, 0.3398438, 0.3398438, 0.3320312, 0.3281250, 0.3281250, 0.3281250, 0.3242188, 0.3085938, 0.3046875, 0.3007812, 0.2968750, 0.2773438, 0.2734375, 0.2656250, 0.2578125, 0.2343750, 0.2265625, 0.2148438, 0.2070312, 0.1796875, 0.1679688, 0.1562500, 0.1406250, 0.1289062, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0156250, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0820312, 0.0976562, 0.1132812, 0.1289062, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3125000, 0.3476562, 0.3632812, 0.3789062, 0.3945312, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5976562, 0.6132812, 0.6289062, 0.6445312, 0.6601562, 0.6953125, 0.7109375, 0.7304688, 0.7460938, 0.7773438, 0.7929688, 0.8125000, 0.8281250, 0.8632812, 0.8789062, 0.8945312, 0.9101562, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0625000, 0.0820312, 0.0976562, 0.1132812, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3281250, 0.3476562, 0.3632812, 0.3789062, 0.4140625, 0.4296875, 0.4453125, 0.4648438, 0.4960938, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5937500, 0.6132812, 0.6289062, 0.6445312, 0.6796875, 0.6953125, 0.7109375, 0.7304688, 0.7617188, 0.7773438, 0.7929688, 0.8125000, 0.8437500, 0.8593750, 0.8789062, 0.8945312, 0.9101562, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9765625, 0.9453125, 0.9296875, 0.9101562, 0.8945312, 0.8632812, 0.8437500, 0.8281250, 0.8125000, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.6953125, 0.6796875, 0.6640625, 0.6445312, 0.6289062, 0.5976562, 0.5781250, 0.5625000, 0.5468750, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4296875, 0.4140625, 0.3984375, 0.3789062, 0.3476562, 0.3320312, 0.3125000, 0.2968750, 0.2812500, 0.2460938, 0.2304688, 0.2148438, 0.1992188, 0.1640625, 0.1484375, 0.1328125, 0.1132812, 0.0820312, 0.0664062, 0.0468750, 0.0312500, 0.0000000]), array([ 0.0000000, 0.0117188, 0.0273438, 0.0390625, 0.0546875, 0.0742188, 0.0898438, 0.1093750, 0.1250000, 0.1484375, 0.1679688, 0.1875000, 0.2070312, 0.2304688, 0.2460938, 0.2656250, 0.2812500, 0.3007812, 0.3164062, 0.3359375, 0.3554688, 0.3710938, 0.3906250, 0.4062500, 0.4257812, 0.4414062, 0.4609375, 0.4765625, 0.4960938, 0.5156250, 0.5312500, 0.5507812, 0.5664062, 0.5859375, 0.6015625, 0.6210938, 0.6367188, 0.6562500, 0.6757812, 0.6914062, 0.7109375, 0.7265625, 0.7460938, 0.7617188, 0.7812500, 0.7968750, 0.8164062, 0.8359375, 0.8515625, 0.8710938, 0.8867188, 0.9062500, 0.9218750, 0.9414062, 0.9570312, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9609375, 0.9453125, 0.9296875, 0.9101562, 0.8789062, 0.8593750, 0.8437500, 0.8281250, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.6953125, 0.6796875, 0.6640625, 0.6445312, 0.6132812, 0.5937500, 0.5781250, 0.5625000, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3789062, 0.3476562, 0.3281250, 0.3125000, 0.2968750, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1796875, 0.1640625, 0.1484375, 0.1328125, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0312500, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 14 :: STEPS ### color_map_luts['idl14'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0273438, 0.0585938, 0.0898438, 0.1210938, 0.1523438, 0.1835938, 0.2148438, 0.2460938, 0.2734375, 0.3046875, 0.3359375, 0.3671875, 0.3984375, 0.4296875, 0.4609375, 0.4921875, 0.5234375, 0.5546875, 0.5898438, 0.6210938, 0.6562500, 0.6875000, 0.7187500, 0.7539062, 0.7851562, 0.8203125, 0.8515625, 0.8828125, 0.9179688, 0.9492188, 0.9843750, 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0546875, 0.0625000, 0.0703125, 0.0781250, 0.0898438, 0.0976562, 0.1054688, 0.1132812, 0.1250000, 0.1328125, 0.1406250, 0.1484375, 0.1601562, 0.1718750, 0.1835938, 0.1953125, 0.2070312, 0.2187500, 0.2304688, 0.2460938, 0.2578125, 0.2695312, 0.2812500, 0.2929688, 0.3046875, 0.3203125, 0.3320312, 0.3476562, 0.3632812, 0.3789062, 0.3945312, 0.4101562, 0.4218750, 0.4375000, 0.4531250, 0.4687500, 0.4843750, 0.5000000, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5898438, 0.6054688, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6953125, 0.7070312, 0.7226562, 0.7343750, 0.7500000, 0.7617188, 0.7734375, 0.7890625, 0.8007812, 0.8164062, 0.8281250, 0.8437500, 0.8515625, 0.8593750, 0.8710938, 0.8789062, 0.8867188, 0.8984375, 0.9062500, 0.9140625, 0.9257812, 0.9335938, 0.9414062, 0.9531250, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.1640625, 0.3320312, 0.4960938, 0.6640625, 0.8281250, 0.9960938, 0.9609375, 0.9218750, 0.8828125, 0.8437500, 0.8046875, 0.7695312, 0.7304688, 0.6914062, 0.6523438, 0.6132812, 0.5781250, 0.5390625, 0.5000000, 0.4609375, 0.4218750, 0.3867188, 0.3476562, 0.3085938, 0.2695312, 0.2304688, 0.1953125, 0.1562500, 0.1171875, 0.0781250, 0.0390625, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0039062, 0.0039062, 0.0117188, 0.0195312, 0.0312500, 0.0390625, 0.0468750, 0.0585938, 0.0664062, 0.0742188, 0.0859375, 0.0937500, 0.1015625, 0.1132812, 0.1210938, 0.1289062, 0.1406250, 0.1406250, 0.1484375, 0.1562500, 0.1640625, 0.1757812, 0.1875000, 0.1992188, 0.2109375, 0.2265625, 0.2382812, 0.2500000, 0.2617188, 0.2734375, 0.2851562, 0.3007812, 0.3203125, 0.3398438, 0.3593750, 0.3750000, 0.3906250, 0.4062500, 0.4218750, 0.4414062, 0.4648438, 0.4882812, 0.5117188, 0.5390625, 0.5625000, 0.5898438, 0.6132812, 0.6406250, 0.6679688, 0.6953125, 0.7226562, 0.7539062, 0.7812500, 0.8125000, 0.8398438, 0.8710938, 0.8945312, 0.9179688, 0.9453125, 0.9687500, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0312500, 0.0625000, 0.0937500, 0.1250000, 0.1601562, 0.1914062, 0.2226562, 0.2539062, 0.2890625, 0.3203125, 0.3515625, 0.3828125, 0.4140625, 0.4492188, 0.4804688, 0.5117188, 0.5429688, 0.5781250, 0.6093750, 0.6406250, 0.6718750, 0.7031250, 0.7382812, 0.7695312, 0.8007812, 0.8320312, 0.8671875, 0.8984375, 0.9296875, 0.9609375, 0.9960938, 0.0000000, 0.0195312, 0.0390625, 0.0585938, 0.0820312, 0.1015625, 0.1210938, 0.1445312, 0.1640625, 0.1835938, 0.2070312, 0.2265625, 0.2460938, 0.2695312, 0.2890625, 0.3085938, 0.3320312, 0.3476562, 0.3671875, 0.3828125, 0.4023438, 0.4218750, 0.4375000, 0.4570312, 0.4726562, 0.4921875, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5820312, 0.6015625, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0273438, 0.0351562, 0.0468750, 0.0546875, 0.0664062, 0.0781250, 0.0898438, 0.1054688, 0.1171875, 0.1328125, 0.1523438, 0.1718750, 0.1914062, 0.2148438, 0.2343750, 0.2539062, 0.2773438, 0.2968750, 0.3203125, 0.3476562, 0.3789062, 0.4062500, 0.4375000, 0.4687500, 0.5000000, 0.5312500, 0.5664062, 0.5976562, 0.6328125, 0.6679688, 0.7031250, 0.7382812, 0.7734375, 0.8085938, 0.8476562, 0.8750000, 0.9062500, 0.9335938, 0.9648438, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 15 :: STERN SPECIAL ### color_map_luts['idl15'] = \ ( array([ 0.0000000, 0.0703125, 0.1406250, 0.2109375, 0.2812500, 0.3515625, 0.4218750, 0.4960938, 0.5664062, 0.6367188, 0.7070312, 0.7773438, 0.8476562, 0.9179688, 0.9921875, 0.9726562, 0.9531250, 0.9335938, 0.9140625, 0.8945312, 0.8710938, 0.8515625, 0.8320312, 0.8125000, 0.7929688, 0.7695312, 0.7500000, 0.7304688, 0.7109375, 0.6914062, 0.6718750, 0.6484375, 0.6289062, 0.6093750, 0.5898438, 0.5703125, 0.5468750, 0.5273438, 0.5078125, 0.4882812, 0.4687500, 0.4492188, 0.4257812, 0.4062500, 0.3867188, 0.3671875, 0.3476562, 0.3242188, 0.3046875, 0.2851562, 0.2656250, 0.2460938, 0.2265625, 0.2031250, 0.1835938, 0.1640625, 0.1445312, 0.1250000, 0.1015625, 0.0820312, 0.0625000, 0.0429688, 0.0234375, 0.0000000, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0390625, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938]), array([ 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0273438, 0.0351562, 0.0429688, 0.0507812, 0.0585938, 0.0664062, 0.0742188, 0.0820312, 0.0898438, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1367188, 0.1445312, 0.1523438, 0.1601562, 0.1679688, 0.1757812, 0.1835938, 0.1914062, 0.1992188, 0.2070312, 0.2148438, 0.2226562, 0.2304688, 0.2382812, 0.2460938, 0.2539062, 0.2617188, 0.2695312, 0.2773438, 0.2851562, 0.2929688, 0.3007812, 0.3085938, 0.3164062, 0.3242188, 0.3320312, 0.3398438, 0.3476562, 0.3554688, 0.3632812, 0.3710938, 0.3789062, 0.3867188, 0.3945312, 0.4023438, 0.4101562, 0.4179688, 0.4257812, 0.4335938, 0.4414062, 0.4492188, 0.4570312, 0.4648438, 0.4726562, 0.4804688, 0.4882812, 0.4960938, 0.5039062, 0.5117188, 0.5195312, 0.5273438, 0.5351562, 0.5429688, 0.5507812, 0.5585938, 0.5664062, 0.5742188, 0.5820312, 0.5898438, 0.5976562, 0.6054688, 0.6132812, 0.6210938, 0.6289062, 0.6367188, 0.6445312, 0.6523438, 0.6601562, 0.6679688, 0.6757812, 0.6835938, 0.6914062, 0.6992188, 0.7070312, 0.7148438, 0.7226562, 0.7304688, 0.7382812, 0.7460938, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7851562, 0.7929688, 0.8007812, 0.8085938, 0.8164062, 0.8242188, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8632812, 0.8710938, 0.8789062, 0.8867188, 0.8945312, 0.9023438, 0.9101562, 0.9179688, 0.9257812, 0.9335938, 0.9414062, 0.9492188, 0.9570312, 0.9648438, 0.9726562, 0.9804688, 0.9882812, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9296875, 0.9140625, 0.8984375, 0.8828125, 0.8632812, 0.8476562, 0.8320312, 0.8164062, 0.7968750, 0.7812500, 0.7656250, 0.7500000, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6640625, 0.6484375, 0.6328125, 0.6171875, 0.5976562, 0.5820312, 0.5664062, 0.5507812, 0.5312500, 0.5156250, 0.5000000, 0.4843750, 0.4648438, 0.4492188, 0.4335938, 0.4179688, 0.3984375, 0.3828125, 0.3671875, 0.3515625, 0.3320312, 0.3164062, 0.3007812, 0.2851562, 0.2656250, 0.2500000, 0.2343750, 0.2187500, 0.1992188, 0.1835938, 0.1679688, 0.1523438, 0.1328125, 0.1171875, 0.1015625, 0.0859375, 0.0664062, 0.0507812, 0.0351562, 0.0195312, 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0859375, 0.1015625, 0.1171875, 0.1328125, 0.1484375, 0.1601562, 0.1757812, 0.1914062, 0.2070312, 0.2226562, 0.2343750, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3085938, 0.3242188, 0.3398438, 0.3554688, 0.3710938, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4453125, 0.4570312, 0.4726562, 0.4882812, 0.5039062, 0.5195312, 0.5351562, 0.5468750, 0.5625000, 0.5781250, 0.5937500, 0.6093750, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6953125, 0.7109375, 0.7265625, 0.7421875, 0.7578125, 0.7695312, 0.7851562, 0.8007812, 0.8164062, 0.8320312, 0.8437500, 0.8593750, 0.8750000, 0.8906250, 0.9062500, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 16 :: Haze ### color_map_luts['idl16'] = \ ( array([ 0.6523438, 0.6523438, 0.9960938, 0.9921875, 0.9726562, 0.9648438, 0.9570312, 0.9492188, 0.9453125, 0.9375000, 0.9296875, 0.9218750, 0.9140625, 0.9062500, 0.8984375, 0.8906250, 0.8828125, 0.8750000, 0.8671875, 0.8593750, 0.8515625, 0.8437500, 0.8359375, 0.8281250, 0.8203125, 0.8125000, 0.8046875, 0.7968750, 0.7890625, 0.7812500, 0.7734375, 0.7656250, 0.7578125, 0.7500000, 0.7421875, 0.7343750, 0.7265625, 0.7187500, 0.7109375, 0.7031250, 0.6953125, 0.6875000, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6484375, 0.6406250, 0.6328125, 0.6250000, 0.6171875, 0.6093750, 0.6015625, 0.5937500, 0.5859375, 0.5781250, 0.5703125, 0.5625000, 0.5546875, 0.5507812, 0.5429688, 0.5351562, 0.5273438, 0.5195312, 0.5117188, 0.5039062, 0.4960938, 0.4882812, 0.4804688, 0.4726562, 0.4648438, 0.4570312, 0.4492188, 0.4414062, 0.4335938, 0.4257812, 0.4179688, 0.4101562, 0.4023438, 0.3945312, 0.3867188, 0.3789062, 0.3710938, 0.3632812, 0.3554688, 0.3476562, 0.3398438, 0.3320312, 0.3242188, 0.3164062, 0.3085938, 0.3007812, 0.2929688, 0.2851562, 0.2773438, 0.2695312, 0.2617188, 0.2539062, 0.2460938, 0.2382812, 0.2304688, 0.2226562, 0.2148438, 0.2070312, 0.1992188, 0.1914062, 0.1835938, 0.1757812, 0.1679688, 0.1601562, 0.1562500, 0.1484375, 0.1406250, 0.1328125, 0.1250000, 0.1171875, 0.1093750, 0.1015625, 0.0937500, 0.0859375, 0.0781250, 0.0703125, 0.0625000, 0.0546875, 0.0468750, 0.0507812, 0.0312500, 0.0234375, 0.0156250, 0.0156250, 0.0234375, 0.0273438, 0.0351562, 0.0429688, 0.0507812, 0.0585938, 0.0664062, 0.0742188, 0.0820312, 0.0898438, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1367188, 0.1445312, 0.1523438, 0.1601562, 0.1679688, 0.1757812, 0.1835938, 0.1914062, 0.1992188, 0.2070312, 0.2148438, 0.2226562, 0.2304688, 0.2382812, 0.2460938, 0.2539062, 0.2617188, 0.2695312, 0.2773438, 0.2851562, 0.2929688, 0.3007812, 0.3085938, 0.3164062, 0.3242188, 0.3320312, 0.3398438, 0.3476562, 0.3554688, 0.3632812, 0.3710938, 0.3789062, 0.3867188, 0.3945312, 0.4023438, 0.4101562, 0.4179688, 0.4218750, 0.4296875, 0.4375000, 0.4453125, 0.4531250, 0.4609375, 0.4687500, 0.4765625, 0.4843750, 0.4921875, 0.5000000, 0.5078125, 0.5156250, 0.5234375, 0.5312500, 0.5390625, 0.5468750, 0.5546875, 0.5625000, 0.5703125, 0.5781250, 0.5859375, 0.5937500, 0.6015625, 0.6093750, 0.6171875, 0.6250000, 0.6328125, 0.6406250, 0.6484375, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6953125, 0.7031250, 0.7109375, 0.7187500, 0.7265625, 0.7343750, 0.7421875, 0.7500000, 0.7578125, 0.7656250, 0.7734375, 0.7812500, 0.7890625, 0.7968750, 0.8046875, 0.8125000, 0.8203125, 0.8242188, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8632812, 0.8710938, 0.8789062, 0.8867188, 0.8945312, 0.9023438, 0.9101562, 0.9179688, 0.9257812, 0.9335938, 0.9414062, 0.9492188, 0.9570312, 0.9648438, 0.9726562, 0.9804688, 0.9804688]), array([ 0.4375000, 0.4375000, 0.8320312, 0.8281250, 0.8203125, 0.8164062, 0.8125000, 0.8046875, 0.8007812, 0.7929688, 0.7890625, 0.7812500, 0.7773438, 0.7734375, 0.7656250, 0.7617188, 0.7539062, 0.7500000, 0.7460938, 0.7382812, 0.7343750, 0.7265625, 0.7226562, 0.7148438, 0.7109375, 0.7070312, 0.6992188, 0.6953125, 0.6875000, 0.6835938, 0.6796875, 0.6718750, 0.6679688, 0.6601562, 0.6562500, 0.6484375, 0.6445312, 0.6406250, 0.6328125, 0.6289062, 0.6210938, 0.6171875, 0.6132812, 0.6054688, 0.6015625, 0.5937500, 0.5898438, 0.5859375, 0.5781250, 0.5742188, 0.5664062, 0.5625000, 0.5546875, 0.5507812, 0.5468750, 0.5390625, 0.5351562, 0.5273438, 0.5234375, 0.5195312, 0.5117188, 0.5078125, 0.5000000, 0.4960938, 0.4882812, 0.4843750, 0.4804688, 0.4726562, 0.4687500, 0.4609375, 0.4570312, 0.4531250, 0.4453125, 0.4414062, 0.4335938, 0.4296875, 0.4257812, 0.4179688, 0.4140625, 0.4062500, 0.4023438, 0.3945312, 0.3906250, 0.3867188, 0.3789062, 0.3750000, 0.3671875, 0.3632812, 0.3593750, 0.3515625, 0.3476562, 0.3398438, 0.3359375, 0.3281250, 0.3242188, 0.3203125, 0.3125000, 0.3085938, 0.3007812, 0.2968750, 0.2929688, 0.2851562, 0.2812500, 0.2734375, 0.2695312, 0.2656250, 0.2578125, 0.2539062, 0.2460938, 0.2421875, 0.2343750, 0.2304688, 0.2265625, 0.2187500, 0.2148438, 0.2070312, 0.2031250, 0.1992188, 0.1914062, 0.1875000, 0.1796875, 0.1757812, 0.1679688, 0.1640625, 0.1601562, 0.1523438, 0.1484375, 0.1406250, 0.1367188, 0.1328125, 0.1250000, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1406250, 0.1445312, 0.1484375, 0.1562500, 0.1601562, 0.1640625, 0.1718750, 0.1757812, 0.1796875, 0.1875000, 0.1914062, 0.1953125, 0.2031250, 0.2070312, 0.2109375, 0.2187500, 0.2226562, 0.2265625, 0.2343750, 0.2382812, 0.2421875, 0.2500000, 0.2539062, 0.2578125, 0.2656250, 0.2695312, 0.2734375, 0.2812500, 0.2851562, 0.2929688, 0.2968750, 0.3007812, 0.3085938, 0.3125000, 0.3164062, 0.3242188, 0.3281250, 0.3320312, 0.3398438, 0.3437500, 0.3476562, 0.3554688, 0.3593750, 0.3632812, 0.3710938, 0.3750000, 0.3789062, 0.3867188, 0.3906250, 0.3945312, 0.4023438, 0.4062500, 0.4101562, 0.4179688, 0.4218750, 0.4257812, 0.4335938, 0.4375000, 0.4414062, 0.4492188, 0.4531250, 0.4570312, 0.4648438, 0.4687500, 0.4726562, 0.4804688, 0.4843750, 0.4882812, 0.4960938, 0.5000000, 0.5039062, 0.5117188, 0.5156250, 0.5195312, 0.5273438, 0.5312500, 0.5351562, 0.5429688, 0.5468750, 0.5507812, 0.5585938, 0.5625000, 0.5664062, 0.5742188, 0.5781250, 0.5820312, 0.5898438, 0.5937500, 0.5976562, 0.6054688, 0.6093750, 0.6132812, 0.6210938, 0.6250000, 0.6289062, 0.6367188, 0.6406250, 0.6445312, 0.6523438, 0.6562500, 0.6601562, 0.6679688, 0.6718750, 0.6757812, 0.6835938, 0.6875000, 0.6914062, 0.6992188, 0.7031250, 0.7070312, 0.7148438, 0.7187500, 0.7226562, 0.7304688, 0.7343750, 0.7382812, 0.7460938, 0.7500000, 0.7539062, 0.7617188, 0.7617188]), array([ 0.9960938, 0.9960938, 0.9921875, 0.9804688, 0.9765625, 0.9726562, 0.9687500, 0.9648438, 0.9609375, 0.9570312, 0.9531250, 0.9492188, 0.9453125, 0.9414062, 0.9375000, 0.9335938, 0.9296875, 0.9257812, 0.9218750, 0.9179688, 0.9140625, 0.9101562, 0.9062500, 0.9023438, 0.8984375, 0.8945312, 0.8906250, 0.8867188, 0.8828125, 0.8789062, 0.8750000, 0.8710938, 0.8671875, 0.8632812, 0.8593750, 0.8554688, 0.8515625, 0.8476562, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8281250, 0.8242188, 0.8203125, 0.8164062, 0.8125000, 0.8085938, 0.8046875, 0.8007812, 0.7968750, 0.7929688, 0.7890625, 0.7851562, 0.7812500, 0.7773438, 0.7734375, 0.7695312, 0.7656250, 0.7617188, 0.7578125, 0.7539062, 0.7500000, 0.7460938, 0.7421875, 0.7382812, 0.7343750, 0.7304688, 0.7265625, 0.7226562, 0.7187500, 0.7148438, 0.7109375, 0.7070312, 0.7031250, 0.6992188, 0.6953125, 0.6914062, 0.6875000, 0.6835938, 0.6796875, 0.6757812, 0.6718750, 0.6679688, 0.6640625, 0.6601562, 0.6562500, 0.6523438, 0.6484375, 0.6445312, 0.6406250, 0.6367188, 0.6328125, 0.6289062, 0.6250000, 0.6210938, 0.6171875, 0.6132812, 0.6093750, 0.6054688, 0.6015625, 0.5976562, 0.5937500, 0.5898438, 0.5859375, 0.5820312, 0.5781250, 0.5742188, 0.5703125, 0.5664062, 0.5625000, 0.5585938, 0.5546875, 0.5507812, 0.5468750, 0.5429688, 0.5390625, 0.5351562, 0.5312500, 0.5273438, 0.5234375, 0.5195312, 0.5156250, 0.5117188, 0.5078125, 0.5039062, 0.5000000, 0.4960938, 0.4921875, 0.4882812, 0.4843750, 0.4804688, 0.4765625, 0.4726562, 0.4687500, 0.4648438, 0.4609375, 0.4570312, 0.4531250, 0.4492188, 0.4453125, 0.4414062, 0.4375000, 0.4335938, 0.4296875, 0.4257812, 0.4218750, 0.4179688, 0.4140625, 0.4101562, 0.4062500, 0.4023438, 0.3984375, 0.3945312, 0.3906250, 0.3867188, 0.3828125, 0.3789062, 0.3750000, 0.3710938, 0.3671875, 0.3632812, 0.3593750, 0.3554688, 0.3515625, 0.3476562, 0.3437500, 0.3398438, 0.3359375, 0.3320312, 0.3281250, 0.3242188, 0.3203125, 0.3164062, 0.3125000, 0.3085938, 0.3046875, 0.3007812, 0.2968750, 0.2929688, 0.2890625, 0.2851562, 0.2812500, 0.2773438, 0.2734375, 0.2695312, 0.2656250, 0.2617188, 0.2578125, 0.2539062, 0.2500000, 0.2460938, 0.2421875, 0.2382812, 0.2343750, 0.2304688, 0.2265625, 0.2226562, 0.2187500, 0.2148438, 0.2109375, 0.2070312, 0.2031250, 0.1992188, 0.1953125, 0.1914062, 0.1875000, 0.1835938, 0.1796875, 0.1757812, 0.1718750, 0.1679688, 0.1640625, 0.1601562, 0.1562500, 0.1523438, 0.1484375, 0.1445312, 0.1406250, 0.1367188, 0.1328125, 0.1289062, 0.1250000, 0.1210938, 0.1171875, 0.1132812, 0.1093750, 0.1054688, 0.1015625, 0.0976562, 0.0937500, 0.0898438, 0.0859375, 0.0820312, 0.0781250, 0.0742188, 0.0703125, 0.0664062, 0.0625000, 0.0585938, 0.0546875, 0.0507812, 0.0468750, 0.0429688, 0.0507812, 0.0351562, 0.0312500, 0.0273438, 0.0234375, 0.0195312, 0.0156250, 0.0117188, 0.0078125, 0.0039062, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 17 :: Blue - Pastel - Red ### color_map_luts['idl17'] = \ ( array([ 0.1289062, 0.1289062, 0.1250000, 0.1210938, 0.1210938, 0.1171875, 0.1132812, 0.1093750, 0.1054688, 0.1015625, 0.0976562, 0.0937500, 0.0898438, 0.0859375, 0.0820312, 0.0781250, 0.0742188, 0.0664062, 0.0625000, 0.0585938, 0.0546875, 0.0468750, 0.0429688, 0.0507812, 0.0312500, 0.0273438, 0.0195312, 0.0156250, 0.0078125, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0156250, 0.0195312, 0.0273438, 0.0312500, 0.0507812, 0.0429688, 0.0507812, 0.0546875, 0.0585938, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.1132812, 0.1406250, 0.1640625, 0.1875000, 0.2070312, 0.2265625, 0.2382812, 0.2539062, 0.2656250, 0.2734375, 0.2812500, 0.2851562, 0.2890625, 0.2890625, 0.2890625, 0.2929688, 0.2929688, 0.2968750, 0.2968750, 0.2968750, 0.3007812, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3437500, 0.3476562, 0.3554688, 0.3593750, 0.3554688, 0.3671875, 0.3750000, 0.3828125, 0.3906250, 0.3984375, 0.4062500, 0.4140625, 0.4218750, 0.4257812, 0.4335938, 0.4375000, 0.4414062, 0.4492188, 0.4531250, 0.4570312, 0.4648438, 0.4687500, 0.4726562, 0.4804688, 0.4843750, 0.4882812, 0.4960938, 0.5000000, 0.5039062, 0.5117188, 0.5156250, 0.5195312, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5468750, 0.5507812, 0.5546875, 0.5625000, 0.5664062, 0.5703125, 0.5781250, 0.5820312, 0.5859375, 0.5937500, 0.5976562, 0.6015625, 0.6093750, 0.6132812, 0.6171875, 0.6250000, 0.6289062, 0.6328125, 0.6406250, 0.6445312, 0.6484375, 0.6562500, 0.6601562, 0.6640625, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6914062, 0.6953125, 0.6992188, 0.7070312, 0.7109375, 0.7148438, 0.7226562, 0.7265625, 0.7304688, 0.7382812, 0.7421875, 0.7460938, 0.7539062, 0.7539062]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0234375, 0.0312500, 0.0507812, 0.0468750, 0.0507812, 0.0585938, 0.0664062, 0.0742188, 0.0820312, 0.0898438, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1367188, 0.1445312, 0.1523438, 0.1640625, 0.1718750, 0.1796875, 0.1914062, 0.1992188, 0.2070312, 0.2187500, 0.2265625, 0.2343750, 0.2460938, 0.2539062, 0.2656250, 0.2773438, 0.2851562, 0.2968750, 0.3046875, 0.3164062, 0.3281250, 0.3398438, 0.3476562, 0.3593750, 0.3710938, 0.3828125, 0.3945312, 0.4062500, 0.4179688, 0.4296875, 0.4414062, 0.4531250, 0.4648438, 0.4765625, 0.4882812, 0.5000000, 0.5117188, 0.5234375, 0.5390625, 0.5507812, 0.5625000, 0.5781250, 0.5898438, 0.6015625, 0.6171875, 0.6289062, 0.6445312, 0.6562500, 0.6718750, 0.6835938, 0.6992188, 0.7109375, 0.7265625, 0.7421875, 0.7539062, 0.7695312, 0.7851562, 0.8007812, 0.8164062, 0.8281250, 0.8437500, 0.8593750, 0.8750000, 0.8906250, 0.9062500, 0.9140625, 0.9179688, 0.9218750, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9726562, 0.9726562, 0.9609375, 0.9531250, 0.9414062, 0.9257812, 0.9062500, 0.8867188, 0.8710938, 0.8515625, 0.8359375, 0.8164062, 0.8007812, 0.7851562, 0.7656250, 0.7500000, 0.7343750, 0.7187500, 0.7031250, 0.6835938, 0.6679688, 0.6523438, 0.6367188, 0.6250000, 0.6093750, 0.5937500, 0.5781250, 0.5625000, 0.5507812, 0.5351562, 0.5195312, 0.5078125, 0.4921875, 0.4804688, 0.4648438, 0.4531250, 0.4531250, 0.4492188, 0.4414062, 0.4375000, 0.4296875, 0.4257812, 0.4062500, 0.4062500, 0.4023438, 0.3984375, 0.3945312, 0.3906250, 0.3867188, 0.3828125, 0.3789062, 0.3750000, 0.3710938, 0.3671875, 0.3632812, 0.3593750, 0.3437500, 0.3437500, 0.3437500, 0.3398438, 0.3359375, 0.3320312, 0.3320312, 0.3281250, 0.3242188, 0.3203125, 0.3125000, 0.3085938, 0.3046875, 0.2968750, 0.2929688, 0.2890625, 0.2851562, 0.2773438, 0.2734375, 0.2695312, 0.2656250, 0.2617188, 0.2578125, 0.2500000, 0.2460938, 0.2382812, 0.2265625, 0.2187500, 0.2109375, 0.2031250, 0.1914062, 0.1835938, 0.1757812, 0.1679688, 0.1601562, 0.1562500, 0.1484375, 0.1406250, 0.1328125, 0.1289062, 0.1210938, 0.1132812, 0.1093750, 0.1015625, 0.0976562, 0.0898438, 0.0859375, 0.0820312, 0.0742188, 0.0703125, 0.0664062, 0.0625000, 0.0546875, 0.0507812, 0.0468750, 0.0429688, 0.0507812, 0.0351562, 0.0312500, 0.0273438, 0.0273438, 0.0234375, 0.0195312, 0.0156250, 0.0156250, 0.0117188, 0.0078125, 0.0078125, 0.0039062, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.3750000, 0.3750000, 0.3789062, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9062500, 0.9101562, 0.9062500, 0.8984375, 0.8945312, 0.8867188, 0.8789062, 0.8710938, 0.8671875, 0.8593750, 0.8515625, 0.8437500, 0.8359375, 0.8281250, 0.8203125, 0.8125000, 0.5820312, 0.5546875, 0.5273438, 0.4960938, 0.4609375, 0.4335938, 0.4023438, 0.3710938, 0.3437500, 0.3164062, 0.2890625, 0.2617188, 0.2343750, 0.2070312, 0.1835938, 0.1562500, 0.1328125, 0.1093750, 0.0859375, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1093750, 0.1132812, 0.1171875, 0.1171875, 0.1210938, 0.1210938, 0.1250000, 0.1250000, 0.1132812, 0.1093750, 0.1015625, 0.0976562, 0.0898438, 0.0859375, 0.0781250, 0.0703125, 0.0664062, 0.0625000, 0.0585938, 0.0546875, 0.0507812, 0.0468750, 0.0429688, 0.0507812, 0.0351562, 0.0312500, 0.0273438, 0.0234375, 0.0195312, 0.0195312, 0.0156250, 0.0117188, 0.0117188, 0.0078125, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 18 :: Pastels ### color_map_luts['idl18'] = \ ( array([ 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9882812, 0.9804688, 0.9726562, 0.9648438, 0.9570312, 0.9492188, 0.9414062, 0.9335938, 0.9257812, 0.9179688, 0.9101562, 0.9023438, 0.8945312, 0.8867188, 0.8789062, 0.8710938, 0.8632812, 0.8554688, 0.8476562, 0.8437500, 0.8359375, 0.8281250, 0.8203125, 0.8125000, 0.8046875, 0.7968750, 0.7890625, 0.7812500, 0.7734375, 0.7656250, 0.7578125, 0.7500000, 0.7421875, 0.7343750, 0.7265625, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0312500, 0.0507812, 0.0742188, 0.0976562, 0.1171875, 0.1406250, 0.1640625, 0.1875000, 0.2070312, 0.2304688, 0.2539062, 0.2773438, 0.2968750, 0.3203125, 0.3437500, 0.3632812, 0.3867188, 0.4101562, 0.4335938, 0.4531250, 0.4765625, 0.5000000, 0.5195312, 0.5429688, 0.5664062, 0.5898438, 0.6093750, 0.6328125, 0.6562500, 0.6757812, 0.6992188, 0.7226562, 0.7460938, 0.7656250, 0.7890625, 0.8125000, 0.8359375, 0.8554688, 0.8789062, 0.9023438, 0.9218750, 0.9453125, 0.9687500, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.5468750, 0.5703125, 0.5937500, 0.6132812, 0.6367188, 0.6601562, 0.6796875, 0.7031250, 0.7265625, 0.7500000, 0.7695312, 0.7929688, 0.8164062, 0.8359375, 0.8593750, 0.8828125, 0.9062500, 0.9257812, 0.9492188, 0.9726562, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9765625, 0.9531250, 0.9296875, 0.9101562, 0.8867188, 0.8632812, 0.8398438, 0.8203125, 0.7968750, 0.7734375, 0.7539062, 0.7304688, 0.7070312, 0.6835938, 0.6640625, 0.6640625]), array([ 0.2812500, 0.2812500, 0.2890625, 0.2968750, 0.3046875, 0.3125000, 0.3203125, 0.3281250, 0.3359375, 0.3437500, 0.3515625, 0.3554688, 0.3632812, 0.3710938, 0.3789062, 0.3867188, 0.3945312, 0.4023438, 0.4101562, 0.4179688, 0.4257812, 0.4335938, 0.4414062, 0.4492188, 0.4570312, 0.4648438, 0.4726562, 0.4804688, 0.4882812, 0.4960938, 0.5039062, 0.5117188, 0.5195312, 0.5273438, 0.5351562, 0.5429688, 0.5507812, 0.5546875, 0.5625000, 0.5703125, 0.5781250, 0.5859375, 0.5937500, 0.6015625, 0.6093750, 0.6171875, 0.6250000, 0.6328125, 0.6406250, 0.6484375, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6953125, 0.7031250, 0.7109375, 0.7187500, 0.7265625, 0.7343750, 0.7421875, 0.7460938, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7851562, 0.7929688, 0.8007812, 0.8085938, 0.8164062, 0.8242188, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8632812, 0.8710938, 0.8789062, 0.8867188, 0.8945312, 0.9023438, 0.9101562, 0.9179688, 0.9257812, 0.9335938, 0.9414062, 0.9453125, 0.9531250, 0.9609375, 0.9687500, 0.9765625, 0.9843750, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9726562, 0.9492188, 0.9257812, 0.9062500, 0.8828125, 0.8593750, 0.8359375, 0.8164062, 0.7929688, 0.7695312, 0.7500000, 0.7265625, 0.7031250, 0.6796875, 0.6601562, 0.6367188, 0.6132812, 0.5937500, 0.5703125, 0.5468750, 0.5234375, 0.5039062, 0.4804688, 0.4570312, 0.4375000, 0.4140625, 0.3906250, 0.3671875, 0.3476562, 0.3242188, 0.3007812, 0.2773438, 0.2578125, 0.2343750, 0.2109375, 0.1914062, 0.1679688, 0.1445312, 0.1210938, 0.1015625, 0.0781250, 0.0546875, 0.0351562, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 19 :: Hue Sat Lightness 1 ### color_map_luts['idl19'] = \ ( array([ 0.9804688, 0.9804688, 0.9804688, 0.9804688, 0.9843750, 0.9843750, 0.9882812, 0.9843750, 0.9843750, 0.9804688, 0.9804688, 0.9765625, 0.9765625, 0.9726562, 0.9726562, 0.9726562, 0.9687500, 0.9687500, 0.9648438, 0.9648438, 0.9609375, 0.9609375, 0.9609375, 0.9570312, 0.9570312, 0.9531250, 0.9531250, 0.9492188, 0.9492188, 0.9492188, 0.9453125, 0.9453125, 0.9414062, 0.9414062, 0.9414062, 0.9375000, 0.9375000, 0.9375000, 0.9335938, 0.9335938, 0.9335938, 0.9296875, 0.9296875, 0.9257812, 0.9179688, 0.8984375, 0.8828125, 0.8671875, 0.8476562, 0.8320312, 0.8164062, 0.8007812, 0.7851562, 0.7695312, 0.7578125, 0.7421875, 0.7265625, 0.7148438, 0.6992188, 0.6875000, 0.6757812, 0.6601562, 0.6484375, 0.6367188, 0.6250000, 0.6132812, 0.6015625, 0.5898438, 0.5781250, 0.5664062, 0.5585938, 0.5468750, 0.5351562, 0.5273438, 0.5195312, 0.5078125, 0.5000000, 0.4921875, 0.4843750, 0.4765625, 0.4687500, 0.4609375, 0.4531250, 0.4453125, 0.4375000, 0.4335938, 0.4257812, 0.4257812, 0.4296875, 0.4335938, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4726562, 0.4765625, 0.4804688, 0.4882812, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5117188, 0.5156250, 0.5195312, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5468750, 0.5507812, 0.5546875, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6679688, 0.6718750, 0.6796875, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7421875, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7773438, 0.7812500, 0.7890625, 0.7929688, 0.7968750, 0.8046875, 0.8085938, 0.8125000, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8828125, 0.8867188, 0.8906250, 0.8906250, 0.8945312, 0.8984375, 0.8984375, 0.9023438, 0.9062500, 0.9062500, 0.9101562, 0.9101562, 0.9140625, 0.9179688, 0.9179688, 0.9179688, 0.9218750, 0.9218750, 0.9218750, 0.9257812, 0.9257812, 0.9257812, 0.9296875, 0.9296875, 0.9335938, 0.9335938, 0.9335938, 0.9375000, 0.9375000, 0.9375000, 0.9414062, 0.9453125, 0.9453125, 0.9453125, 0.9453125, 0.9492188, 0.9492188, 0.9531250, 0.9531250, 0.9531250, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9765625, 0.9804688, 0.9804688]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0156250, 0.0195312, 0.0273438, 0.0312500, 0.0273438, 0.0351562, 0.0507812, 0.0312500, 0.0585938, 0.0664062, 0.0703125, 0.0781250, 0.0820312, 0.0898438, 0.0937500, 0.1015625, 0.1054688, 0.1093750, 0.1171875, 0.1210938, 0.1289062, 0.1328125, 0.1367188, 0.1445312, 0.1484375, 0.1562500, 0.1601562, 0.1640625, 0.1718750, 0.1757812, 0.1796875, 0.1875000, 0.1914062, 0.1953125, 0.2031250, 0.2070312, 0.2148438, 0.2187500, 0.2226562, 0.2304688, 0.2343750, 0.2382812, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2656250, 0.2695312, 0.2734375, 0.2812500, 0.2851562, 0.2890625, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4179688, 0.4218750, 0.4296875, 0.4453125, 0.4609375, 0.4765625, 0.4882812, 0.5039062, 0.5156250, 0.5312500, 0.5429688, 0.5585938, 0.5703125, 0.5820312, 0.5937500, 0.6093750, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6640625, 0.6757812, 0.6835938, 0.6953125, 0.7070312, 0.7187500, 0.7265625, 0.7343750, 0.7460938, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7851562, 0.7929688, 0.8007812, 0.8085938, 0.8164062, 0.8242188, 0.8320312, 0.8398438, 0.8437500, 0.8515625, 0.8593750, 0.8632812, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.9023438, 0.9023438, 0.9062500, 0.9062500, 0.9062500, 0.9062500, 0.9062500, 0.9101562, 0.9101562, 0.9101562, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9179688, 0.9179688, 0.9179688, 0.9218750, 0.9218750, 0.9218750, 0.9218750, 0.9218750, 0.9257812, 0.9257812, 0.9257812, 0.9257812, 0.9296875, 0.9296875, 0.9296875, 0.9335938, 0.9335938, 0.9335938, 0.9375000, 0.9375000, 0.9414062, 0.9414062, 0.9453125, 0.9453125, 0.9453125, 0.9492188, 0.9492188, 0.9531250, 0.9531250, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9765625, 0.9765625]), array([ 0.0117188, 0.0117188, 0.0351562, 0.0585938, 0.0859375, 0.1093750, 0.1328125, 0.1601562, 0.1875000, 0.2148438, 0.2421875, 0.2578125, 0.2851562, 0.3164062, 0.3281250, 0.3671875, 0.3906250, 0.4140625, 0.4375000, 0.4609375, 0.4843750, 0.5078125, 0.5273438, 0.5507812, 0.5742188, 0.5937500, 0.6132812, 0.6328125, 0.6562500, 0.6757812, 0.6953125, 0.7148438, 0.7304688, 0.7500000, 0.7695312, 0.7851562, 0.8046875, 0.8203125, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9257812, 0.9257812, 0.9218750, 0.9218750, 0.9218750, 0.9179688, 0.9179688, 0.9179688, 0.9179688, 0.9140625, 0.9140625, 0.9140625, 0.9101562, 0.9101562, 0.9101562, 0.9062500, 0.9062500, 0.9062500, 0.9062500, 0.9023438, 0.9023438, 0.9023438, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8632812, 0.8593750, 0.8515625, 0.8476562, 0.8437500, 0.8359375, 0.8320312, 0.8281250, 0.8242188, 0.8203125, 0.8125000, 0.8085938, 0.8046875, 0.8046875, 0.8007812, 0.7968750, 0.7929688, 0.7890625, 0.7890625, 0.7851562, 0.7812500, 0.7812500, 0.7773438, 0.7734375, 0.7734375, 0.7695312, 0.7695312, 0.7695312, 0.7656250, 0.7695312, 0.7656250, 0.7656250, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7617188, 0.7656250, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8281250, 0.8320312, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8437500, 0.8476562, 0.8515625, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8632812, 0.8671875, 0.8710938, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8828125, 0.8867188, 0.8906250, 0.8906250, 0.8945312, 0.8984375, 0.8984375, 0.9023438, 0.9062500, 0.9062500, 0.9101562, 0.9101562, 0.9140625, 0.9179688, 0.9179688, 0.9218750, 0.9257812, 0.9257812, 0.9296875, 0.9296875, 0.9335938, 0.9375000, 0.9375000, 0.9414062, 0.9414062, 0.9453125, 0.9492188, 0.9492188, 0.9531250, 0.9531250, 0.9570312, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9765625, 0.9765625]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 20 :: Hue Sat Lightness 2 ### color_map_luts['idl20'] = \ ( array([ 0.9882812, 0.9882812, 0.9804688, 0.9765625, 0.9765625, 0.9726562, 0.9726562, 0.9687500, 0.9687500, 0.9648438, 0.9648438, 0.9609375, 0.9609375, 0.9609375, 0.9570312, 0.9570312, 0.9531250, 0.9531250, 0.9531250, 0.9492188, 0.9492188, 0.9453125, 0.9453125, 0.9453125, 0.9414062, 0.9414062, 0.9375000, 0.9375000, 0.9375000, 0.9335938, 0.9335938, 0.9335938, 0.9296875, 0.9296875, 0.9296875, 0.9257812, 0.9257812, 0.9218750, 0.9218750, 0.9218750, 0.9179688, 0.9179688, 0.9179688, 0.9179688, 0.9140625, 0.9101562, 0.9101562, 0.9062500, 0.9062500, 0.9023438, 0.9023438, 0.8984375, 0.8945312, 0.8945312, 0.8906250, 0.8867188, 0.8828125, 0.8828125, 0.8789062, 0.8750000, 0.8710938, 0.8710938, 0.8671875, 0.8632812, 0.8593750, 0.8554688, 0.8515625, 0.8476562, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8281250, 0.8242188, 0.8203125, 0.8125000, 0.8085938, 0.8046875, 0.8007812, 0.7968750, 0.7890625, 0.7812500, 0.7773438, 0.7734375, 0.7656250, 0.7617188, 0.7578125, 0.7500000, 0.7500000, 0.7460938, 0.7460938, 0.7421875, 0.7382812, 0.7343750, 0.7304688, 0.7226562, 0.7226562, 0.7187500, 0.7148438, 0.7148438, 0.7109375, 0.7070312, 0.7031250, 0.6992188, 0.6953125, 0.6914062, 0.6875000, 0.6835938, 0.6796875, 0.6757812, 0.6718750, 0.6718750, 0.6679688, 0.6640625, 0.6601562, 0.6562500, 0.6523438, 0.6484375, 0.6445312, 0.6445312, 0.6406250, 0.6367188, 0.6328125, 0.6289062, 0.6250000, 0.6210938, 0.6171875, 0.6132812, 0.6093750, 0.6054688, 0.6015625, 0.5976562, 0.5937500, 0.5898438, 0.5820312, 0.5781250, 0.5742188, 0.5703125, 0.5664062, 0.5625000, 0.5585938, 0.5546875, 0.5468750, 0.5429688, 0.5390625, 0.5390625, 0.5351562, 0.5312500, 0.5273438, 0.5234375, 0.5156250, 0.5117188, 0.5078125, 0.5039062, 0.5000000, 0.4960938, 0.4921875, 0.4882812, 0.4843750, 0.4765625, 0.4726562, 0.4687500, 0.4648438, 0.4609375, 0.4609375, 0.4531250, 0.4453125, 0.4414062, 0.4375000, 0.4335938, 0.4335938, 0.4257812, 0.4218750, 0.4257812, 0.4335938, 0.4453125, 0.4492188, 0.4570312, 0.4609375, 0.4687500, 0.4804688, 0.4882812, 0.4960938, 0.5039062, 0.5117188, 0.5234375, 0.5351562, 0.5429688, 0.5507812, 0.5625000, 0.5742188, 0.5859375, 0.5937500, 0.6054688, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6679688, 0.6796875, 0.6914062, 0.7070312, 0.7187500, 0.7343750, 0.7500000, 0.7617188, 0.7773438, 0.7929688, 0.8085938, 0.8242188, 0.8437500, 0.8593750, 0.8750000, 0.8906250, 0.9101562, 0.9257812, 0.9296875, 0.9335938, 0.9335938, 0.9375000, 0.9375000, 0.9375000, 0.9414062, 0.9414062, 0.9414062, 0.9453125, 0.9453125, 0.9492188, 0.9492188, 0.9492188, 0.9531250, 0.9531250, 0.9570312, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9648438, 0.9687500, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9765625, 0.9804688, 0.9804688, 0.9843750, 0.9843750, 0.9882812, 0.9882812, 0.9921875, 0.9921875, 0.9921875, 0.9921875]), array([ 0.9843750, 0.9843750, 0.9765625, 0.9765625, 0.9726562, 0.9726562, 0.9687500, 0.9687500, 0.9648438, 0.9648438, 0.9609375, 0.9609375, 0.9570312, 0.9570312, 0.9531250, 0.9531250, 0.9492188, 0.9453125, 0.9453125, 0.9414062, 0.9414062, 0.9375000, 0.9335938, 0.9335938, 0.9296875, 0.9296875, 0.9257812, 0.9218750, 0.9218750, 0.9179688, 0.9179688, 0.9140625, 0.9101562, 0.9101562, 0.9062500, 0.9062500, 0.9023438, 0.8984375, 0.8984375, 0.8945312, 0.8906250, 0.8906250, 0.8867188, 0.8828125, 0.8828125, 0.8789062, 0.8750000, 0.8710938, 0.8710938, 0.8671875, 0.8632812, 0.8632812, 0.8593750, 0.8554688, 0.8515625, 0.8515625, 0.8476562, 0.8476562, 0.8437500, 0.8398438, 0.8359375, 0.8359375, 0.8320312, 0.8281250, 0.8242188, 0.8242188, 0.8164062, 0.8164062, 0.8125000, 0.8125000, 0.8046875, 0.8046875, 0.8007812, 0.8007812, 0.7968750, 0.7890625, 0.7890625, 0.7851562, 0.7851562, 0.7812500, 0.7773438, 0.7695312, 0.7695312, 0.7656250, 0.7656250, 0.7617188, 0.7617188, 0.7578125, 0.7578125, 0.7578125, 0.7578125, 0.7578125, 0.7578125, 0.7578125, 0.7617188, 0.7578125, 0.7617188, 0.7617188, 0.7617188, 0.7656250, 0.7656250, 0.7656250, 0.7695312, 0.7695312, 0.7734375, 0.7734375, 0.7773438, 0.7773438, 0.7812500, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8398438, 0.8437500, 0.8476562, 0.8554688, 0.8593750, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.9023438, 0.9023438, 0.9023438, 0.9023438, 0.9062500, 0.9062500, 0.9062500, 0.9062500, 0.9101562, 0.9101562, 0.9101562, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9179688, 0.9179688, 0.9179688, 0.9218750, 0.9218750, 0.9218750, 0.9257812, 0.9257812, 0.9257812, 0.9296875, 0.9296875, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8515625, 0.8359375, 0.8203125, 0.8007812, 0.7851562, 0.7656250, 0.7460938, 0.7304688, 0.7109375, 0.6914062, 0.6718750, 0.6523438, 0.6328125, 0.6132812, 0.5898438, 0.5703125, 0.5468750, 0.5273438, 0.5039062, 0.4804688, 0.4570312, 0.4335938, 0.4140625, 0.3867188, 0.3632812, 0.3320312, 0.3046875, 0.2890625, 0.2617188, 0.2382812, 0.2109375, 0.1835938, 0.1562500, 0.1289062, 0.1054688, 0.1054688]), array([ 0.9843750, 0.9843750, 0.9765625, 0.9765625, 0.9726562, 0.9726562, 0.9687500, 0.9687500, 0.9648438, 0.9648438, 0.9609375, 0.9609375, 0.9570312, 0.9570312, 0.9531250, 0.9531250, 0.9492188, 0.9492188, 0.9492188, 0.9453125, 0.9453125, 0.9414062, 0.9414062, 0.9414062, 0.9375000, 0.9375000, 0.9335938, 0.9335938, 0.9335938, 0.9296875, 0.9296875, 0.9296875, 0.9257812, 0.9257812, 0.9257812, 0.9257812, 0.9218750, 0.9218750, 0.9218750, 0.9218750, 0.9179688, 0.9179688, 0.9179688, 0.9179688, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9101562, 0.9101562, 0.9101562, 0.9062500, 0.9062500, 0.9062500, 0.9062500, 0.9023438, 0.9023438, 0.9023438, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8945312, 0.8906250, 0.8906250, 0.8906250, 0.8906250, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8867188, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8828125, 0.8789062, 0.8828125, 0.8789062, 0.8789062, 0.8789062, 0.8789062, 0.8750000, 0.8789062, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8710938, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8671875, 0.8593750, 0.8515625, 0.8476562, 0.8398438, 0.8320312, 0.8242188, 0.8203125, 0.8125000, 0.8046875, 0.7968750, 0.7890625, 0.7812500, 0.7695312, 0.7617188, 0.7539062, 0.7460938, 0.7343750, 0.7265625, 0.7148438, 0.7070312, 0.6953125, 0.6875000, 0.6757812, 0.6640625, 0.6562500, 0.6445312, 0.6328125, 0.6210938, 0.6093750, 0.5976562, 0.5859375, 0.5703125, 0.5585938, 0.5468750, 0.5351562, 0.5195312, 0.5039062, 0.4921875, 0.4765625, 0.4609375, 0.4531250, 0.4375000, 0.4179688, 0.4140625, 0.4101562, 0.4101562, 0.4062500, 0.3984375, 0.3906250, 0.3867188, 0.3867188, 0.3789062, 0.3750000, 0.3671875, 0.3632812, 0.3632812, 0.3554688, 0.3515625, 0.3437500, 0.3437500, 0.3359375, 0.3320312, 0.3242188, 0.3242188, 0.3164062, 0.3125000, 0.3046875, 0.3046875, 0.2968750, 0.2929688, 0.2851562, 0.2812500, 0.2773438, 0.2734375, 0.2695312, 0.2617188, 0.2578125, 0.2539062, 0.2460938, 0.2421875, 0.2421875, 0.2304688, 0.2265625, 0.2265625, 0.2148438, 0.2109375, 0.2109375, 0.1992188, 0.1953125, 0.1914062, 0.1835938, 0.1796875, 0.1757812, 0.1679688, 0.1640625, 0.1601562, 0.1523438, 0.1484375, 0.1406250, 0.1367188, 0.1328125, 0.1250000, 0.1210938, 0.1171875, 0.1093750, 0.1054688, 0.0976562, 0.0937500, 0.0898438, 0.0859375, 0.0781250, 0.0703125, 0.0664062, 0.0585938, 0.0546875, 0.0507812, 0.0312500, 0.0507812, 0.0312500, 0.0273438, 0.0195312, 0.0156250, 0.0078125, 0.0039062, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 21 :: Hue Sat Value 1 ### color_map_luts['idl21'] = \ ( array([ 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9843750, 0.9648438, 0.9453125, 0.9257812, 0.9101562, 0.8906250, 0.8710938, 0.8554688, 0.8359375, 0.8203125, 0.8007812, 0.7851562, 0.7656250, 0.7500000, 0.7343750, 0.7187500, 0.6992188, 0.6835938, 0.6679688, 0.6523438, 0.6367188, 0.6210938, 0.6054688, 0.5898438, 0.5742188, 0.5625000, 0.5468750, 0.5312500, 0.5195312, 0.5039062, 0.4882812, 0.4765625, 0.4609375, 0.4492188, 0.4375000, 0.4218750, 0.4101562, 0.3984375, 0.3867188, 0.3710938, 0.3593750, 0.3476562, 0.3359375, 0.3320312, 0.3359375, 0.3398438, 0.3437500, 0.3476562, 0.3515625, 0.3554688, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4218750, 0.4257812, 0.4296875, 0.4335938, 0.4375000, 0.4414062, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5039062, 0.5078125, 0.5117188, 0.5156250, 0.5195312, 0.5234375, 0.5273438, 0.5312500, 0.5351562, 0.5390625, 0.5429688, 0.5468750, 0.5507812, 0.5546875, 0.5585938, 0.5625000, 0.5664062, 0.5703125, 0.5742188, 0.5781250, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5976562, 0.6015625, 0.6054688, 0.6093750, 0.6132812, 0.6171875, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6367188, 0.6406250, 0.6445312, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6718750, 0.6835938, 0.6914062, 0.7031250, 0.7148438, 0.7265625, 0.7343750, 0.7460938, 0.7578125, 0.7656250, 0.7773438, 0.7851562, 0.7929688, 0.8046875, 0.8125000, 0.8203125, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8632812, 0.8710938, 0.8789062, 0.8867188, 0.8945312, 0.8984375, 0.9062500, 0.9140625, 0.9218750, 0.9257812, 0.9335938, 0.9375000, 0.9453125, 0.9492188, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0039062, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0507812, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0781250, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2695312, 0.2734375, 0.2773438, 0.2812500, 0.2851562, 0.2890625, 0.2929688, 0.2968750, 0.3007812, 0.3046875, 0.3085938, 0.3125000, 0.3164062, 0.3203125, 0.3242188, 0.3281250, 0.3398438, 0.3593750, 0.3789062, 0.3984375, 0.4179688, 0.4335938, 0.4531250, 0.4726562, 0.4882812, 0.5078125, 0.5234375, 0.5429688, 0.5585938, 0.5742188, 0.5937500, 0.6093750, 0.6250000, 0.6406250, 0.6562500, 0.6718750, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7500000, 0.7656250, 0.7773438, 0.7929688, 0.8085938, 0.8203125, 0.8359375, 0.8476562, 0.8632812, 0.8750000, 0.8906250, 0.9023438, 0.9140625, 0.9296875, 0.9414062, 0.9531250, 0.9648438, 0.9765625, 0.9882812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9921875, 0.9882812, 0.9843750, 0.9804688, 0.9804688, 0.9765625, 0.9726562, 0.9726562, 0.9687500, 0.9648438, 0.9648438, 0.9609375, 0.9609375, 0.9609375, 0.9570312, 0.9570312, 0.9570312, 0.9570312, 0.9531250, 0.9531250, 0.9531250, 0.9531250, 0.9531250, 0.9531250, 0.9570312, 0.9570312, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9726562, 0.9726562, 0.9765625, 0.9804688, 0.9804688, 0.9804688]), array([ 0.0117188, 0.0117188, 0.0507812, 0.0664062, 0.0898438, 0.1171875, 0.1445312, 0.1718750, 0.1953125, 0.2226562, 0.2460938, 0.2734375, 0.2968750, 0.3203125, 0.3476562, 0.3710938, 0.3945312, 0.4179688, 0.4453125, 0.4687500, 0.4921875, 0.5156250, 0.5390625, 0.5625000, 0.5820312, 0.6054688, 0.6289062, 0.6523438, 0.6718750, 0.6953125, 0.7187500, 0.7382812, 0.7617188, 0.7812500, 0.8007812, 0.8242188, 0.8437500, 0.8632812, 0.8867188, 0.9062500, 0.9257812, 0.9453125, 0.9648438, 0.9843750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9882812, 0.9765625, 0.9648438, 0.9531250, 0.9453125, 0.9335938, 0.9218750, 0.9140625, 0.9023438, 0.8945312, 0.8828125, 0.8750000, 0.8632812, 0.8554688, 0.8476562, 0.8359375, 0.8281250, 0.8203125, 0.8125000, 0.8046875, 0.7968750, 0.7890625, 0.7812500, 0.7734375, 0.7656250, 0.7578125, 0.7500000, 0.7460938, 0.7382812, 0.7304688, 0.7265625, 0.7187500, 0.7148438, 0.7070312, 0.7031250, 0.6992188, 0.6914062, 0.6875000, 0.6835938, 0.6796875, 0.6757812, 0.6718750, 0.6679688, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6835938, 0.6875000, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7109375, 0.7148438, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7382812, 0.7421875, 0.7460938, 0.7460938, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7929688, 0.7968750, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8554688, 0.8593750, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9179688, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9804688]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 22 :: Hue Sat Value 2 ### color_map_luts['idl22'] = \ ( array([ 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9921875, 0.9882812, 0.9843750, 0.9804688, 0.9726562, 0.9687500, 0.9648438, 0.9609375, 0.9531250, 0.9492188, 0.9414062, 0.9375000, 0.9296875, 0.9257812, 0.9179688, 0.9101562, 0.9023438, 0.8984375, 0.8906250, 0.8828125, 0.8750000, 0.8671875, 0.8593750, 0.8515625, 0.8437500, 0.8320312, 0.8242188, 0.8164062, 0.8085938, 0.7968750, 0.7890625, 0.7773438, 0.7695312, 0.7578125, 0.7500000, 0.7382812, 0.7304688, 0.7187500, 0.7070312, 0.6953125, 0.6835938, 0.6718750, 0.6640625, 0.6562500, 0.6523438, 0.6484375, 0.6445312, 0.6406250, 0.6367188, 0.6328125, 0.6289062, 0.6250000, 0.6210938, 0.6171875, 0.6132812, 0.6093750, 0.6054688, 0.6015625, 0.5976562, 0.5937500, 0.5898438, 0.5859375, 0.5820312, 0.5781250, 0.5742188, 0.5703125, 0.5664062, 0.5625000, 0.5585938, 0.5546875, 0.5507812, 0.5468750, 0.5429688, 0.5390625, 0.5351562, 0.5312500, 0.5273438, 0.5234375, 0.5195312, 0.5156250, 0.5117188, 0.5078125, 0.5039062, 0.5000000, 0.4960938, 0.4921875, 0.4882812, 0.4843750, 0.4804688, 0.4765625, 0.4726562, 0.4687500, 0.4648438, 0.4609375, 0.4570312, 0.4531250, 0.4492188, 0.4453125, 0.4414062, 0.4375000, 0.4335938, 0.4296875, 0.4257812, 0.4218750, 0.4179688, 0.4140625, 0.4101562, 0.4062500, 0.4023438, 0.3984375, 0.3945312, 0.3906250, 0.3867188, 0.3828125, 0.3789062, 0.3750000, 0.3710938, 0.3671875, 0.3632812, 0.3593750, 0.3554688, 0.3515625, 0.3476562, 0.3437500, 0.3398438, 0.3359375, 0.3320312, 0.3320312, 0.3281250, 0.3281250, 0.3437500, 0.3554688, 0.3671875, 0.3789062, 0.3906250, 0.4023438, 0.4179688, 0.4296875, 0.4453125, 0.4570312, 0.4726562, 0.4843750, 0.5000000, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5703125, 0.5859375, 0.6015625, 0.6171875, 0.6328125, 0.6484375, 0.6640625, 0.6796875, 0.6992188, 0.7148438, 0.7304688, 0.7460938, 0.7656250, 0.7812500, 0.8007812, 0.8164062, 0.8359375, 0.8515625, 0.8710938, 0.8906250, 0.9101562, 0.9257812, 0.9453125, 0.9648438, 0.9843750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.9882812, 0.9882812, 0.9843750, 0.9804688, 0.9765625, 0.9726562, 0.9687500, 0.9648438, 0.9609375, 0.9570312, 0.9531250, 0.9492188, 0.9453125, 0.9414062, 0.9375000, 0.9335938, 0.9296875, 0.9257812, 0.9218750, 0.9179688, 0.9140625, 0.9101562, 0.9062500, 0.9023438, 0.8984375, 0.8945312, 0.8906250, 0.8867188, 0.8828125, 0.8789062, 0.8750000, 0.8710938, 0.8671875, 0.8632812, 0.8593750, 0.8554688, 0.8515625, 0.8476562, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8281250, 0.8242188, 0.8203125, 0.8164062, 0.8125000, 0.8085938, 0.8046875, 0.8007812, 0.7968750, 0.7929688, 0.7890625, 0.7851562, 0.7812500, 0.7773438, 0.7734375, 0.7695312, 0.7656250, 0.7617188, 0.7578125, 0.7539062, 0.7500000, 0.7460938, 0.7421875, 0.7382812, 0.7343750, 0.7304688, 0.7265625, 0.7226562, 0.7187500, 0.7148438, 0.7109375, 0.7070312, 0.7031250, 0.6992188, 0.6953125, 0.6914062, 0.6875000, 0.6835938, 0.6796875, 0.6757812, 0.6718750, 0.6679688, 0.6640625, 0.6601562, 0.6601562, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6875000, 0.6914062, 0.6953125, 0.7031250, 0.7070312, 0.7148438, 0.7187500, 0.7265625, 0.7343750, 0.7382812, 0.7460938, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7851562, 0.7929688, 0.8007812, 0.8085938, 0.8164062, 0.8242188, 0.8359375, 0.8437500, 0.8515625, 0.8632812, 0.8710938, 0.8828125, 0.8906250, 0.9023438, 0.9101562, 0.9218750, 0.9335938, 0.9414062, 0.9531250, 0.9648438, 0.9765625, 0.9882812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9843750, 0.9648438, 0.9453125, 0.9257812, 0.9062500, 0.8828125, 0.8632812, 0.8437500, 0.8242188, 0.8007812, 0.7812500, 0.7578125, 0.7382812, 0.7148438, 0.6953125, 0.6718750, 0.6484375, 0.6250000, 0.6054688, 0.5820312, 0.5585938, 0.5351562, 0.5117188, 0.4882812, 0.4648438, 0.4414062, 0.4179688, 0.3906250, 0.3671875, 0.3437500, 0.3164062, 0.2929688, 0.2695312, 0.2421875, 0.2187500, 0.1914062, 0.1640625, 0.1406250, 0.1132812, 0.1132812]), array([ 0.9882812, 0.9882812, 0.9843750, 0.9804688, 0.9765625, 0.9726562, 0.9687500, 0.9687500, 0.9648438, 0.9648438, 0.9609375, 0.9609375, 0.9570312, 0.9570312, 0.9531250, 0.9531250, 0.9531250, 0.9531250, 0.9492188, 0.9492188, 0.9492188, 0.9492188, 0.9492188, 0.9492188, 0.9492188, 0.9492188, 0.9531250, 0.9531250, 0.9531250, 0.9531250, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9765625, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9882812, 0.9765625, 0.9648438, 0.9531250, 0.9414062, 0.9257812, 0.9140625, 0.9023438, 0.8867188, 0.8750000, 0.8593750, 0.8476562, 0.8320312, 0.8203125, 0.8046875, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6679688, 0.6523438, 0.6367188, 0.6210938, 0.6054688, 0.5859375, 0.5703125, 0.5546875, 0.5351562, 0.5195312, 0.5000000, 0.4843750, 0.4648438, 0.4453125, 0.4296875, 0.4101562, 0.3906250, 0.3710938, 0.3515625, 0.3359375, 0.3242188, 0.3203125, 0.3164062, 0.3125000, 0.3085938, 0.3046875, 0.3007812, 0.2968750, 0.2929688, 0.2890625, 0.2851562, 0.2812500, 0.2773438, 0.2734375, 0.2695312, 0.2656250, 0.2617188, 0.2578125, 0.2539062, 0.2500000, 0.2460938, 0.2421875, 0.2382812, 0.2343750, 0.2304688, 0.2265625, 0.2226562, 0.2187500, 0.2148438, 0.2109375, 0.2070312, 0.2031250, 0.1992188, 0.1953125, 0.1914062, 0.1875000, 0.1835938, 0.1796875, 0.1757812, 0.1718750, 0.1679688, 0.1640625, 0.1601562, 0.1562500, 0.1523438, 0.1484375, 0.1445312, 0.1406250, 0.1367188, 0.1328125, 0.1289062, 0.1250000, 0.1210938, 0.1171875, 0.1132812, 0.1093750, 0.1054688, 0.1015625, 0.0976562, 0.0937500, 0.0898438, 0.0859375, 0.0820312, 0.0781250, 0.0742188, 0.0703125, 0.0664062, 0.0625000, 0.0585938, 0.0546875, 0.0507812, 0.0468750, 0.0429688, 0.0507812, 0.0351562, 0.0312500, 0.0273438, 0.0234375, 0.0195312, 0.0156250, 0.0117188, 0.0078125, 0.0078125]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 23 :: Purple-Red + Stripes ### color_map_luts['idl23'] = \ ( array([ 0.0000000, 0.0000000, 0.4960938, 0.7460938, 0.7343750, 0.7265625, 0.7187500, 0.7109375, 0.6992188, 0.6914062, 0.5468750, 0.5390625, 0.6640625, 0.6562500, 0.6445312, 0.6367188, 0.6289062, 0.6171875, 0.6093750, 0.6015625, 0.4726562, 0.4648438, 0.5742188, 0.5664062, 0.5546875, 0.5468750, 0.5390625, 0.5273438, 0.5195312, 0.5117188, 0.4023438, 0.3945312, 0.4843750, 0.4765625, 0.4648438, 0.4570312, 0.4492188, 0.4375000, 0.4296875, 0.4218750, 0.3281250, 0.3203125, 0.3945312, 0.3867188, 0.3750000, 0.3671875, 0.3593750, 0.3476562, 0.3398438, 0.3320312, 0.2578125, 0.2500000, 0.3046875, 0.2968750, 0.2851562, 0.2773438, 0.2695312, 0.2578125, 0.2500000, 0.2421875, 0.1835938, 0.1796875, 0.2148438, 0.2031250, 0.1953125, 0.1875000, 0.1796875, 0.1679688, 0.1601562, 0.1523438, 0.1132812, 0.1054688, 0.1250000, 0.1132812, 0.1054688, 0.0976562, 0.0898438, 0.0781250, 0.0703125, 0.0625000, 0.0429688, 0.0351562, 0.0351562, 0.0234375, 0.0156250, 0.0078125, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0468750, 0.0703125, 0.0937500, 0.1171875, 0.1406250, 0.1640625, 0.1875000, 0.2109375, 0.1875000, 0.2070312, 0.2812500, 0.3046875, 0.3320312, 0.3554688, 0.3789062, 0.4023438, 0.4257812, 0.4492188, 0.3789062, 0.3984375, 0.5195312, 0.5429688, 0.5664062, 0.5898438, 0.6132812, 0.6367188, 0.6640625, 0.6875000, 0.5664062, 0.5859375, 0.7578125, 0.7812500, 0.8046875, 0.8281250, 0.8515625, 0.8750000, 0.8984375, 0.9218750, 0.7578125, 0.7773438, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.4960938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0234375, 0.0468750, 0.0703125, 0.0742188, 0.0937500, 0.1406250, 0.1640625, 0.1875000, 0.2109375, 0.2343750, 0.2578125, 0.2812500, 0.3046875, 0.2656250, 0.2812500, 0.3789062, 0.4023438, 0.4257812, 0.4492188, 0.4726562, 0.4960938, 0.5195312, 0.5429688, 0.4531250, 0.4726562, 0.6132812, 0.6367188, 0.6640625, 0.6875000, 0.7109375, 0.7343750, 0.7578125, 0.7812500, 0.6445312, 0.6640625, 0.8515625, 0.8750000, 0.8984375, 0.9218750, 0.9453125, 0.9687500, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9687500, 0.9453125, 0.9179688, 0.8945312, 0.8710938, 0.8437500, 0.8203125, 0.6367188, 0.6171875, 0.7460938, 0.7187500, 0.6953125, 0.6718750, 0.6445312, 0.6210938, 0.5976562, 0.5703125, 0.4375000, 0.4179688, 0.4960938, 0.4726562, 0.4492188, 0.4296875, 0.4062500, 0.3828125, 0.3593750, 0.3359375, 0.2500000, 0.2343750, 0.2695312, 0.2460938, 0.2226562, 0.2031250, 0.1796875, 0.1562500, 0.1328125, 0.1093750, 0.0703125, 0.0507812, 0.0429688, 0.0195312, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0000000, 0.4960938, 0.7460938, 0.7500000, 0.7500000, 0.7539062, 0.7578125, 0.7617188, 0.7617188, 0.6132812, 0.6132812, 0.7734375, 0.7734375, 0.7773438, 0.7812500, 0.7851562, 0.7890625, 0.7890625, 0.7929688, 0.6367188, 0.6406250, 0.8007812, 0.8046875, 0.8085938, 0.8125000, 0.8125000, 0.8164062, 0.8203125, 0.8242188, 0.6601562, 0.6640625, 0.8320312, 0.8359375, 0.8398438, 0.8398438, 0.8437500, 0.8476562, 0.8515625, 0.8515625, 0.6835938, 0.6875000, 0.8632812, 0.8632812, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8789062, 0.8828125, 0.7070312, 0.7109375, 0.8906250, 0.8945312, 0.8984375, 0.9023438, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.7343750, 0.7343750, 0.9218750, 0.9257812, 0.9296875, 0.9296875, 0.9335938, 0.9375000, 0.9414062, 0.9414062, 0.7578125, 0.7578125, 0.9531250, 0.9570312, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9687500, 0.9726562, 0.7812500, 0.7812500, 0.9804688, 0.9843750, 0.9882812, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7968750, 0.7968750, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9687500, 0.7578125, 0.7382812, 0.8984375, 0.8750000, 0.8515625, 0.8281250, 0.8046875, 0.7812500, 0.7578125, 0.7343750, 0.5664062, 0.5468750, 0.6601562, 0.6367188, 0.6132812, 0.5898438, 0.5664062, 0.5429688, 0.5195312, 0.4960938, 0.3789062, 0.3593750, 0.4257812, 0.4023438, 0.3789062, 0.3554688, 0.3281250, 0.3046875, 0.2812500, 0.2578125, 0.1875000, 0.1679688, 0.1875000, 0.1640625, 0.1406250, 0.1171875, 0.0937500, 0.0703125, 0.0468750, 0.0234375, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0195312, 0.0273438, 0.0390625, 0.0468750, 0.0585938, 0.0664062, 0.0625000, 0.0703125, 0.0976562, 0.1093750, 0.1171875, 0.1289062, 0.1367188, 0.1484375, 0.1562500, 0.1679688, 0.1406250, 0.1484375, 0.1992188, 0.1875000, 0.1796875, 0.1718750, 0.1601562, 0.1523438, 0.1445312, 0.1328125, 0.0976562, 0.0937500, 0.1054688, 0.0976562, 0.0898438, 0.0781250, 0.0703125, 0.0625000, 0.0507812, 0.0429688, 0.0273438, 0.0195312, 0.0156250, 0.0078125, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 24 :: Beach ### color_map_luts['idl24'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0664062, 0.0820312, 0.0976562, 0.1132812, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2304688, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3164062, 0.3320312, 0.3476562, 0.3632812, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4492188, 0.4648438, 0.4804688, 0.5000000, 0.5156250, 0.5312500, 0.5468750, 0.2968750, 0.2968750, 0.2968750, 0.3164062, 0.3320312, 0.3476562, 0.3632812, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4492188, 0.4648438, 0.4804688, 0.5000000, 0.5156250, 0.5312500, 0.5468750, 0.5664062, 0.5820312, 0.5976562, 0.6132812, 0.6132812, 0.6289062, 0.6406250, 0.6523438, 0.6640625, 0.6757812, 0.6914062, 0.7031250, 0.7148438, 0.7265625, 0.7382812, 0.7539062, 0.7656250, 0.7773438, 0.7890625, 0.8007812, 0.8164062, 0.8281250, 0.8398438, 0.8515625, 0.8515625, 0.8554688, 0.8593750, 0.8671875, 0.8710938, 0.8750000, 0.8789062, 0.8828125, 0.8867188, 0.8945312, 0.8984375, 0.9023438, 0.9062500, 0.9101562, 0.9140625, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.9804688, 0.9804688, 0.9726562, 0.9648438, 0.9570312, 0.9492188, 0.9414062, 0.9296875, 0.9218750, 0.9140625, 0.9062500, 0.8984375, 0.8906250, 0.8828125, 0.8750000, 0.8632812, 0.8554688, 0.8476562, 0.8398438, 0.8320312, 0.8242188, 0.8164062, 0.8046875, 0.7968750, 0.7890625, 0.7812500, 0.7734375, 0.7656250, 0.7578125, 0.7500000, 0.7382812, 0.7304688, 0.7226562, 0.7148438, 0.7070312, 0.6992188, 0.6914062, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6484375, 0.6406250, 0.6328125, 0.6250000, 0.6132812, 0.6054688, 0.5976562, 0.5898438, 0.5820312, 0.5742188, 0.5664062, 0.5546875, 0.5468750, 0.5390625, 0.5312500, 0.5234375, 0.5156250, 0.5078125, 0.5000000, 0.5000000, 0.5039062, 0.5117188, 0.5195312, 0.5234375, 0.5312500, 0.5390625, 0.5429688, 0.5507812, 0.5585938, 0.5664062, 0.5703125, 0.5781250, 0.5859375, 0.5898438, 0.5976562, 0.6054688, 0.6132812, 0.6171875, 0.6250000, 0.6328125, 0.6367188, 0.6445312, 0.6523438, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6835938, 0.6914062, 0.6992188, 0.7031250, 0.7109375, 0.7109375, 0.6914062, 0.6679688, 0.6445312, 0.6250000, 0.6015625, 0.5820312, 0.5585938, 0.5390625, 0.5156250, 0.4921875, 0.4726562, 0.4492188, 0.4296875, 0.4062500, 0.3867188, 0.3632812, 0.3437500, 0.3203125, 0.2968750, 0.2773438, 0.2539062, 0.2343750, 0.2109375, 0.1914062, 0.1679688, 0.1445312, 0.1250000, 0.1015625, 0.0820312, 0.0585938, 0.0507812, 0.0156250, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0664062, 0.0820312, 0.0976562, 0.1132812, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2304688, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3164062, 0.3164062, 0.3320312, 0.3476562, 0.3632812, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4492188, 0.4648438, 0.4804688, 0.5000000, 0.5156250, 0.5312500, 0.5468750, 0.5664062, 0.5820312, 0.5976562, 0.6132812, 0.6328125, 0.6328125, 0.6484375, 0.6640625, 0.6796875, 0.6992188, 0.7148438, 0.7304688, 0.7500000, 0.7656250, 0.7812500, 0.7968750, 0.8164062, 0.8320312, 0.8476562, 0.8632812, 0.8828125, 0.8984375, 0.9140625, 0.9296875, 0.9492188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0664062, 0.0820312, 0.0976562, 0.1132812, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2304688, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3164062, 0.3320312, 0.3476562, 0.3632812, 0.3828125, 0.3984375, 0.4140625, 0.4296875, 0.4492188, 0.4648438, 0.4804688, 0.5000000, 0.5156250, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.5312500, 0.2968750, 0.2968750, 0.2968750, 0.2812500, 0.2656250, 0.2500000, 0.2343750, 0.2187500, 0.2031250, 0.1875000, 0.1718750, 0.1562500, 0.1406250, 0.1250000, 0.1093750, 0.0898438, 0.0742188, 0.0585938, 0.0429688, 0.0273438, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0664062, 0.0820312, 0.0976562, 0.1132812, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2304688, 0.2500000, 0.2656250, 0.2812500, 0.2968750, 0.3164062, 0.3164062, 0.3281250, 0.3437500, 0.3554688, 0.3710938, 0.3828125, 0.3984375, 0.4101562, 0.4257812, 0.4414062, 0.4531250, 0.4687500, 0.4804688, 0.4960938, 0.5078125, 0.5234375, 0.5351562, 0.5507812, 0.5664062, 0.5781250, 0.5937500, 0.6054688, 0.6210938, 0.6210938, 0.6328125, 0.6484375, 0.6640625, 0.6757812, 0.6914062, 0.7070312, 0.7187500, 0.7343750, 0.7500000, 0.7617188, 0.7773438, 0.7929688, 0.8046875, 0.8203125, 0.8359375, 0.8476562, 0.8632812, 0.8789062, 0.8906250, 0.9062500, 0.9218750, 0.9335938, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 25 :: Mac Style ### color_map_luts['idl25'] = \ ( array([ 0.4843750, 0.4843750, 0.4687500, 0.4492188, 0.4335938, 0.4140625, 0.3984375, 0.3789062, 0.3632812, 0.3437500, 0.3281250, 0.3085938, 0.2929688, 0.2734375, 0.2578125, 0.2382812, 0.2226562, 0.2031250, 0.1875000, 0.1679688, 0.1523438, 0.1328125, 0.1171875, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0273438, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0195312, 0.0507812, 0.0546875, 0.0742188, 0.0898438, 0.1093750, 0.1250000, 0.1445312, 0.1601562, 0.1796875, 0.1953125, 0.2148438, 0.2304688, 0.2500000, 0.2656250, 0.2851562, 0.3007812, 0.3203125, 0.3359375, 0.3554688, 0.3710938, 0.3906250, 0.4062500, 0.4257812, 0.4414062, 0.4609375, 0.4804688, 0.4921875, 0.5156250, 0.5273438, 0.5507812, 0.5625000, 0.5859375, 0.5976562, 0.6210938, 0.6328125, 0.6562500, 0.6679688, 0.6914062, 0.7031250, 0.7265625, 0.7382812, 0.7617188, 0.7734375, 0.7968750, 0.8085938, 0.8320312, 0.8437500, 0.8671875, 0.8789062, 0.9023438, 0.9140625, 0.9375000, 0.9492188, 0.9726562, 0.9843750, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0234375, 0.0429688, 0.0585938, 0.0781250, 0.0937500, 0.1132812, 0.1289062, 0.1484375, 0.1640625, 0.1835938, 0.1992188, 0.2187500, 0.2343750, 0.2539062, 0.2695312, 0.2890625, 0.3046875, 0.3242188, 0.3398438, 0.3593750, 0.3750000, 0.3945312, 0.4101562, 0.4296875, 0.4453125, 0.4648438, 0.4765625, 0.5000000, 0.5117188, 0.5351562, 0.5468750, 0.5703125, 0.5820312, 0.6054688, 0.6171875, 0.6406250, 0.6523438, 0.6757812, 0.6875000, 0.7109375, 0.7226562, 0.7460938, 0.7578125, 0.7812500, 0.7929688, 0.8164062, 0.8281250, 0.8515625, 0.8632812, 0.8867188, 0.8984375, 0.9218750, 0.9375000, 0.9570312, 0.9726562, 0.9921875, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9843750, 0.9648438, 0.9492188, 0.9296875, 0.9140625, 0.8945312, 0.8789062, 0.8593750, 0.8437500, 0.8242188, 0.8085938, 0.7890625, 0.7734375, 0.7539062, 0.7382812, 0.7187500, 0.7031250, 0.6835938, 0.6679688, 0.6484375, 0.6328125, 0.6132812, 0.5976562, 0.5781250, 0.5625000, 0.5429688, 0.5273438, 0.5117188, 0.4921875, 0.4765625, 0.4570312, 0.4414062, 0.4218750, 0.4062500, 0.3867188, 0.3710938, 0.3515625, 0.3359375, 0.3164062, 0.3007812, 0.2812500, 0.2656250, 0.2460938, 0.2304688, 0.2109375, 0.1953125, 0.1757812, 0.1601562, 0.1406250, 0.1250000, 0.1054688, 0.0898438, 0.0703125, 0.0546875, 0.0351562, 0.0195312, 0.0195312]), array([ 0.9960938, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9921875, 0.9960938, 0.9765625, 0.9648438, 0.9414062, 0.9296875, 0.9062500, 0.8945312, 0.8710938, 0.8593750, 0.8359375, 0.8242188, 0.8007812, 0.7890625, 0.7656250, 0.7539062, 0.7304688, 0.7187500, 0.6953125, 0.6835938, 0.6601562, 0.6484375, 0.6250000, 0.6132812, 0.5898438, 0.5781250, 0.5546875, 0.5429688, 0.5195312, 0.5078125, 0.4882812, 0.4726562, 0.4531250, 0.4375000, 0.4179688, 0.4023438, 0.3828125, 0.3671875, 0.3476562, 0.3320312, 0.3125000, 0.2968750, 0.2773438, 0.2617188, 0.2421875, 0.2265625, 0.2070312, 0.1914062, 0.1718750, 0.1562500, 0.1367188, 0.1210938, 0.1015625, 0.0859375, 0.0664062, 0.0507812, 0.0312500, 0.0156250, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 26 :: Eos A ### color_map_luts['idl26'] = \ ( array([ 0.0000000, 0.0000000, 0.4960938, 0.4804688, 0.4648438, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3789062, 0.3281250, 0.3125000, 0.2968750, 0.3125000, 0.2968750, 0.2812500, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1757812, 0.1640625, 0.1484375, 0.1484375, 0.1289062, 0.1132812, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0273438, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0312500, 0.0664062, 0.0976562, 0.1328125, 0.1640625, 0.1992188, 0.2304688, 0.2382812, 0.2656250, 0.2968750, 0.3632812, 0.3984375, 0.4296875, 0.4648438, 0.4960938, 0.5312500, 0.5625000, 0.5351562, 0.5664062, 0.5976562, 0.6953125, 0.7304688, 0.7617188, 0.7968750, 0.8281250, 0.8632812, 0.8945312, 0.8359375, 0.8632812, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9882812, 0.9804688, 0.9726562, 0.9687500, 0.9609375, 0.9531250, 0.9453125, 0.8476562, 0.8398438, 0.8320312, 0.9218750, 0.9140625, 0.9062500, 0.8984375, 0.8945312, 0.8867188, 0.8789062, 0.7851562, 0.7812500, 0.7734375, 0.8515625, 0.8476562, 0.8398438, 0.8320312, 0.8242188, 0.8203125, 0.8125000, 0.7265625, 0.7187500, 0.7109375, 0.7851562, 0.7773438, 0.7734375, 0.7656250, 0.7578125, 0.7500000, 0.7460938, 0.6640625, 0.6601562, 0.6523438, 0.7187500, 0.7109375, 0.7031250, 0.6992188, 0.6914062, 0.6835938, 0.6796875, 0.6054688, 0.5976562, 0.5898438, 0.6523438, 0.6445312, 0.6367188, 0.6289062, 0.6250000, 0.6171875, 0.6093750, 0.5429688, 0.5390625, 0.5312500, 0.5820312, 0.5781250, 0.5703125, 0.5625000, 0.5585938, 0.5507812, 0.5429688, 0.4843750, 0.4765625, 0.4687500, 0.5156250, 0.5078125, 0.5078125]), array([ 0.0000000, 0.0000000, 0.4960938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0664062, 0.1328125, 0.1992188, 0.2656250, 0.3320312, 0.3984375, 0.4648438, 0.4765625, 0.5351562, 0.5976562, 0.7304688, 0.7968750, 0.8632812, 0.9296875, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9765625, 0.9609375, 0.9453125, 0.9296875, 0.9101562, 0.8945312, 0.8789062, 0.7734375, 0.7617188, 0.7460938, 0.8125000, 0.7968750, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7109375, 0.6250000, 0.6093750, 0.5937500, 0.6445312, 0.6289062, 0.6132812, 0.5976562, 0.5781250, 0.5625000, 0.5468750, 0.4765625, 0.4609375, 0.4453125, 0.4882812, 0.4804688, 0.4726562, 0.4648438, 0.4531250, 0.4453125, 0.4375000, 0.3867188, 0.3789062, 0.3710938, 0.4062500, 0.3984375, 0.3867188, 0.3789062, 0.3710938, 0.3632812, 0.3554688, 0.3125000, 0.3046875, 0.2968750, 0.3203125, 0.3125000, 0.3046875, 0.2968750, 0.2890625, 0.2812500, 0.2734375, 0.2382812, 0.2304688, 0.2226562, 0.2382812, 0.2304688, 0.2226562, 0.2148438, 0.2070312, 0.1953125, 0.1875000, 0.1640625, 0.1562500, 0.1484375, 0.1562500, 0.1484375, 0.1406250, 0.1289062, 0.1210938, 0.1132812, 0.1054688, 0.0859375, 0.0820312, 0.0742188, 0.0742188, 0.0625000, 0.0546875, 0.0468750, 0.0507812, 0.0312500, 0.0234375, 0.0117188, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0000000, 0.4960938, 0.7539062, 0.7617188, 0.7695312, 0.7773438, 0.7851562, 0.7968750, 0.8046875, 0.7304688, 0.7382812, 0.7460938, 0.8359375, 0.8437500, 0.8515625, 0.8632812, 0.8710938, 0.8789062, 0.8867188, 0.8046875, 0.8125000, 0.8203125, 0.9179688, 0.9296875, 0.9375000, 0.9453125, 0.9531250, 0.9609375, 0.9687500, 0.8789062, 0.8867188, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9296875, 0.8632812, 0.7148438, 0.6562500, 0.5937500, 0.5976562, 0.5273438, 0.4609375, 0.3945312, 0.3281250, 0.2617188, 0.1953125, 0.1171875, 0.0585938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 27 :: Eos B ### color_map_luts['idl27'] = \ ( array([ 0.9960938, 0.9960938, 0.4960938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0351562, 0.0703125, 0.1054688, 0.1406250, 0.1757812, 0.1914062, 0.2226562, 0.2812500, 0.3164062, 0.3554688, 0.3906250, 0.4257812, 0.4609375, 0.4960938, 0.5312500, 0.5117188, 0.5429688, 0.6367188, 0.6757812, 0.7109375, 0.7460938, 0.7812500, 0.8164062, 0.8515625, 0.8867188, 0.8320312, 0.8632812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9882812, 0.9804688, 0.9726562, 0.9648438, 0.9570312, 0.9492188, 0.9414062, 0.8398438, 0.8359375, 0.9179688, 0.9140625, 0.9062500, 0.8984375, 0.8906250, 0.8828125, 0.8750000, 0.8671875, 0.7734375, 0.7656250, 0.8437500, 0.8398438, 0.8320312, 0.8242188, 0.8164062, 0.8085938, 0.8007812, 0.7929688, 0.7070312, 0.6992188, 0.7695312, 0.7656250, 0.7578125, 0.7500000, 0.7421875, 0.7343750, 0.7265625, 0.7187500, 0.6406250, 0.6328125, 0.6953125, 0.6875000, 0.6835938, 0.6757812, 0.6679688, 0.6601562, 0.6523438, 0.6445312, 0.5742188, 0.5664062, 0.6210938, 0.6132812, 0.6093750, 0.6015625, 0.5937500, 0.5859375, 0.5781250, 0.5703125, 0.5078125, 0.5000000, 0.5468750, 0.5390625, 0.5351562, 0.5273438, 0.5195312, 0.5117188, 0.5039062, 0.4960938, 0.4414062, 0.4335938, 0.4726562, 0.4648438, 0.4570312, 0.4570312]), array([ 0.9960938, 0.9960938, 0.4960938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0273438, 0.0585938, 0.0898438, 0.1210938, 0.1523438, 0.1835938, 0.2148438, 0.2226562, 0.2500000, 0.3085938, 0.3398438, 0.3710938, 0.4023438, 0.4335938, 0.4648438, 0.4960938, 0.5273438, 0.5039062, 0.5312500, 0.6210938, 0.6523438, 0.6835938, 0.7148438, 0.7460938, 0.7773438, 0.8085938, 0.8398438, 0.7812500, 0.8085938, 0.9335938, 0.9648438, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.8945312, 0.8945312, 0.9960938, 0.9765625, 0.9609375, 0.9453125, 0.9296875, 0.9101562, 0.8945312, 0.8789062, 0.7734375, 0.7617188, 0.8281250, 0.8125000, 0.7968750, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7109375, 0.6250000, 0.6093750, 0.6601562, 0.6445312, 0.6289062, 0.6132812, 0.5976562, 0.5781250, 0.5625000, 0.5468750, 0.4765625, 0.4609375, 0.4960938, 0.4882812, 0.4804688, 0.4726562, 0.4648438, 0.4531250, 0.4453125, 0.4375000, 0.3867188, 0.3789062, 0.4140625, 0.4062500, 0.3984375, 0.3867188, 0.3789062, 0.3710938, 0.3632812, 0.3554688, 0.3125000, 0.3046875, 0.3281250, 0.3203125, 0.3125000, 0.3046875, 0.2968750, 0.2890625, 0.2812500, 0.2734375, 0.2382812, 0.2304688, 0.2460938, 0.2382812, 0.2304688, 0.2226562, 0.2148438, 0.2070312, 0.1953125, 0.1875000, 0.1640625, 0.1562500, 0.1640625, 0.1562500, 0.1484375, 0.1406250, 0.1289062, 0.1210938, 0.1132812, 0.1054688, 0.0859375, 0.0820312, 0.0820312, 0.0742188, 0.0625000, 0.0546875, 0.0468750, 0.0507812, 0.0312500, 0.0234375, 0.0117188, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.9960938, 0.9960938, 0.4960938, 0.5117188, 0.5312500, 0.5468750, 0.5625000, 0.5781250, 0.5976562, 0.6132812, 0.5664062, 0.5820312, 0.6640625, 0.6796875, 0.6953125, 0.7109375, 0.7304688, 0.7460938, 0.7617188, 0.7773438, 0.7148438, 0.7304688, 0.8281250, 0.8437500, 0.8632812, 0.8789062, 0.8945312, 0.9101562, 0.9296875, 0.9453125, 0.8632812, 0.8789062, 0.9960938, 0.9648438, 0.9335938, 0.9023438, 0.8710938, 0.8398438, 0.8085938, 0.7773438, 0.6718750, 0.6406250, 0.6835938, 0.6523438, 0.6210938, 0.5898438, 0.5585938, 0.5273438, 0.4960938, 0.4648438, 0.3906250, 0.3632812, 0.3710938, 0.3398438, 0.3085938, 0.2773438, 0.2460938, 0.2148438, 0.1835938, 0.1523438, 0.1093750, 0.0820312, 0.0585938, 0.0273438, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 28 :: Hardcandy ### color_map_luts['idl28'] = \ ( array([ 0.0156250, 0.0156250, 0.0195312, 0.0273438, 0.0351562, 0.0429688, 0.0468750, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0664062, 0.0703125, 0.0703125, 0.0703125, 0.0664062, 0.0625000, 0.0585938, 0.0546875, 0.0507812, 0.0429688, 0.0507812, 0.0312500, 0.0195312, 0.0117188, 0.0039062, 0.0000000, 0.0117188, 0.0195312, 0.0273438, 0.0507812, 0.0468750, 0.0507812, 0.0585938, 0.0664062, 0.0703125, 0.0742188, 0.0742188, 0.0742188, 0.0742188, 0.0742188, 0.0703125, 0.0625000, 0.0546875, 0.0468750, 0.0351562, 0.0195312, 0.0078125, 0.0078125, 0.0234375, 0.0429688, 0.0664062, 0.0898438, 0.1132812, 0.1406250, 0.1640625, 0.1914062, 0.2187500, 0.2500000, 0.2773438, 0.3046875, 0.3320312, 0.3632812, 0.3906250, 0.4140625, 0.4414062, 0.4648438, 0.4882812, 0.5078125, 0.5273438, 0.5429688, 0.5585938, 0.5703125, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5898438, 0.5859375, 0.5781250, 0.5664062, 0.5546875, 0.5390625, 0.5195312, 0.4960938, 0.4726562, 0.4453125, 0.4179688, 0.3867188, 0.3515625, 0.3203125, 0.2851562, 0.2460938, 0.2109375, 0.1718750, 0.1367188, 0.0976562, 0.0625000, 0.0234375, 0.0039062, 0.0507812, 0.0703125, 0.0976562, 0.1250000, 0.1484375, 0.1718750, 0.1875000, 0.2031250, 0.2148438, 0.2187500, 0.2226562, 0.2226562, 0.2187500, 0.2070312, 0.1953125, 0.1757812, 0.1523438, 0.1289062, 0.0976562, 0.0625000, 0.0234375, 0.0117188, 0.0585938, 0.1054688, 0.1562500, 0.2070312, 0.2617188, 0.3164062, 0.3750000, 0.4335938, 0.4921875, 0.5507812, 0.6093750, 0.6640625, 0.7226562, 0.7773438, 0.8281250, 0.8789062, 0.9257812, 0.9687500, 0.9843750, 0.9453125, 0.9140625, 0.8867188, 0.8671875, 0.8476562, 0.8359375, 0.8281250, 0.8281250, 0.8320312, 0.8437500, 0.8593750, 0.8789062, 0.9062500, 0.9375000, 0.9726562, 0.9804688, 0.9335938, 0.8828125, 0.8320312, 0.7734375, 0.7148438, 0.6523438, 0.5859375, 0.5195312, 0.4531250, 0.3828125, 0.3164062, 0.2500000, 0.1835938, 0.1171875, 0.0546875, 0.0039062, 0.0585938, 0.1132812, 0.1640625, 0.2109375, 0.2500000, 0.2851562, 0.3164062, 0.3398438, 0.3554688, 0.3671875, 0.3710938, 0.3710938, 0.3593750, 0.3437500, 0.3203125, 0.2929688, 0.2578125, 0.2148438, 0.1640625, 0.1093750, 0.0507812, 0.0117188, 0.0820312, 0.1562500, 0.2343750, 0.3164062, 0.4023438, 0.4882812, 0.5742188, 0.6640625, 0.7539062, 0.8437500, 0.9335938, 0.9726562, 0.8906250, 0.8085938, 0.7304688, 0.6562500, 0.5898438, 0.5234375, 0.4648438, 0.4140625, 0.3710938, 0.3320312, 0.3007812, 0.2773438, 0.2617188, 0.2539062, 0.2578125, 0.2656250, 0.2812500, 0.3085938, 0.3398438, 0.3828125, 0.4296875, 0.4843750, 0.5468750, 0.6171875, 0.6914062, 0.7695312, 0.8554688, 0.9414062, 0.9570312, 0.8632812, 0.7656250, 0.6679688, 0.5664062, 0.4687500, 0.3710938, 0.2734375, 0.1796875, 0.0898438, 0.0039062, 0.0742188, 0.1523438, 0.2226562, 0.2890625, 0.3476562, 0.3984375, 0.4375000, 0.4726562, 0.4960938, 0.4960938]), array([ 0.1992188, 0.1992188, 0.2265625, 0.2539062, 0.2812500, 0.3085938, 0.3320312, 0.3593750, 0.3867188, 0.4101562, 0.4335938, 0.4570312, 0.4843750, 0.5039062, 0.5273438, 0.5507812, 0.5703125, 0.5937500, 0.6132812, 0.6328125, 0.6523438, 0.6679688, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7460938, 0.7617188, 0.7734375, 0.7851562, 0.7929688, 0.8046875, 0.8125000, 0.8203125, 0.8281250, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8398438, 0.8398438, 0.8320312, 0.8281250, 0.8203125, 0.8164062, 0.8046875, 0.7968750, 0.7851562, 0.7773438, 0.7617188, 0.7500000, 0.7382812, 0.7226562, 0.7070312, 0.6914062, 0.6718750, 0.6562500, 0.6367188, 0.6171875, 0.5976562, 0.5781250, 0.5546875, 0.5351562, 0.5117188, 0.4882812, 0.4648438, 0.4414062, 0.4140625, 0.3906250, 0.3632812, 0.3398438, 0.3125000, 0.2851562, 0.2617188, 0.2343750, 0.2070312, 0.1796875, 0.1523438, 0.1250000, 0.0976562, 0.0703125, 0.0429688, 0.0156250, 0.0078125, 0.0351562, 0.0625000, 0.0859375, 0.1132812, 0.1406250, 0.1640625, 0.1914062, 0.2148438, 0.2382812, 0.2656250, 0.2890625, 0.3085938, 0.3320312, 0.3554688, 0.3750000, 0.3945312, 0.4140625, 0.4335938, 0.4531250, 0.4687500, 0.4882812, 0.5039062, 0.5195312, 0.5312500, 0.5468750, 0.5585938, 0.5703125, 0.5820312, 0.5898438, 0.5976562, 0.6054688, 0.6132812, 0.6210938, 0.6250000, 0.6289062, 0.6328125, 0.6328125, 0.6328125, 0.6328125, 0.6328125, 0.6328125, 0.6289062, 0.6250000, 0.6210938, 0.6132812, 0.6054688, 0.5976562, 0.5898438, 0.5781250, 0.5703125, 0.5585938, 0.5429688, 0.5312500, 0.5156250, 0.5000000, 0.4843750, 0.4687500, 0.4492188, 0.4335938, 0.4140625, 0.3945312, 0.3710938, 0.3515625, 0.3281250, 0.3085938, 0.2851562, 0.2617188, 0.2382812, 0.2109375, 0.1875000, 0.1640625, 0.1367188, 0.1093750, 0.0859375, 0.0585938, 0.0312500, 0.0039062, 0.0195312, 0.0468750, 0.0742188, 0.1015625, 0.1289062, 0.1562500, 0.1835938, 0.2109375, 0.2382812, 0.2617188, 0.2890625, 0.3164062, 0.3437500, 0.3671875, 0.3945312, 0.4179688, 0.4414062, 0.4687500, 0.4921875, 0.5117188, 0.5351562, 0.5585938, 0.5781250, 0.6015625, 0.6210938, 0.6406250, 0.6562500, 0.6757812, 0.6914062, 0.7070312, 0.7226562, 0.7382812, 0.7539062, 0.7656250, 0.7773438, 0.7890625, 0.7968750, 0.8085938, 0.8164062, 0.8242188, 0.8281250, 0.8359375, 0.8398438, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8281250, 0.8203125, 0.8125000, 0.8046875, 0.7929688, 0.7851562, 0.7734375, 0.7578125, 0.7460938, 0.7304688, 0.7187500, 0.7031250, 0.6835938, 0.6679688, 0.6484375, 0.6289062, 0.6093750, 0.5898438, 0.5703125, 0.5468750, 0.5273438, 0.5039062, 0.4804688, 0.4570312, 0.4335938, 0.4062500, 0.3828125, 0.3554688, 0.3320312, 0.3046875, 0.2773438, 0.2500000, 0.2226562, 0.1953125, 0.1718750, 0.1445312, 0.1171875, 0.0898438, 0.0898438]), array([ 0.4531250, 0.4531250, 0.4101562, 0.3671875, 0.3281250, 0.2890625, 0.2500000, 0.2148438, 0.1796875, 0.1484375, 0.1171875, 0.0937500, 0.0703125, 0.0468750, 0.0312500, 0.0195312, 0.0078125, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0234375, 0.0507812, 0.0546875, 0.0781250, 0.1015625, 0.1289062, 0.1601562, 0.1953125, 0.2304688, 0.2656250, 0.3046875, 0.3437500, 0.3867188, 0.4257812, 0.4687500, 0.5117188, 0.5546875, 0.5976562, 0.6367188, 0.6796875, 0.7187500, 0.7539062, 0.7890625, 0.8242188, 0.8515625, 0.8828125, 0.9062500, 0.9296875, 0.9492188, 0.9648438, 0.9765625, 0.9843750, 0.9882812, 0.9882812, 0.9882812, 0.9804688, 0.9726562, 0.9609375, 0.9414062, 0.9218750, 0.8984375, 0.8750000, 0.8437500, 0.8125000, 0.7812500, 0.7421875, 0.7070312, 0.6679688, 0.6250000, 0.5859375, 0.5429688, 0.5000000, 0.4570312, 0.4140625, 0.3750000, 0.3320312, 0.2929688, 0.2539062, 0.2187500, 0.1835938, 0.1523438, 0.1210938, 0.0937500, 0.0703125, 0.0507812, 0.0351562, 0.0195312, 0.0078125, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0351562, 0.0546875, 0.0742188, 0.0976562, 0.1250000, 0.1562500, 0.1875000, 0.2226562, 0.2617188, 0.2968750, 0.3398438, 0.3789062, 0.4218750, 0.4648438, 0.5078125, 0.5468750, 0.5898438, 0.6328125, 0.6718750, 0.7109375, 0.7500000, 0.7851562, 0.8164062, 0.8476562, 0.8789062, 0.9023438, 0.9257812, 0.9453125, 0.9609375, 0.9726562, 0.9843750, 0.9882812, 0.9921875, 0.9882812, 0.9843750, 0.9726562, 0.9609375, 0.9453125, 0.9257812, 0.9023438, 0.8789062, 0.8476562, 0.8164062, 0.7851562, 0.7500000, 0.7109375, 0.6718750, 0.6328125, 0.5898438, 0.5468750, 0.5078125, 0.4648438, 0.4218750, 0.3789062, 0.3398438, 0.2968750, 0.2617188, 0.2226562, 0.1875000, 0.1562500, 0.1250000, 0.0976562, 0.0742188, 0.0546875, 0.0351562, 0.0195312, 0.0117188, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0195312, 0.0351562, 0.0507812, 0.0703125, 0.0937500, 0.1210938, 0.1523438, 0.1835938, 0.2187500, 0.2539062, 0.2929688, 0.3320312, 0.3750000, 0.4140625, 0.4570312, 0.5000000, 0.5429688, 0.5859375, 0.6250000, 0.6679688, 0.7070312, 0.7421875, 0.7812500, 0.8125000, 0.8437500, 0.8750000, 0.8984375, 0.9218750, 0.9414062, 0.9609375, 0.9726562, 0.9804688, 0.9882812, 0.9882812, 0.9882812, 0.9843750, 0.9765625, 0.9648438, 0.9492188, 0.9296875, 0.9062500, 0.8828125, 0.8515625, 0.8242188, 0.7890625, 0.7539062, 0.7187500, 0.6796875, 0.6367188, 0.5976562, 0.5546875, 0.5117188, 0.4687500, 0.4257812, 0.3867188, 0.3437500, 0.3046875, 0.2656250, 0.2304688, 0.1953125, 0.1601562, 0.1289062, 0.1015625, 0.0781250, 0.0546875, 0.0507812, 0.0234375, 0.0117188, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0195312, 0.0312500, 0.0468750, 0.0703125, 0.0937500, 0.1171875, 0.1484375, 0.1796875, 0.2148438, 0.2500000, 0.2890625, 0.3281250, 0.3671875, 0.4101562, 0.4101562]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 29 :: Nature ### color_map_luts['idl29'] = \ ( array([ 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4296875, 0.4453125, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5585938, 0.5742188, 0.5859375, 0.6132812, 0.6250000, 0.6367188, 0.6445312, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6992188, 0.7031250, 0.7031250, 0.7070312, 0.7109375, 0.7070312, 0.7070312, 0.7031250, 0.6992188, 0.6914062, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6367188, 0.6250000, 0.6132812, 0.6015625, 0.5742188, 0.5585938, 0.5429688, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4296875, 0.4101562, 0.3906250, 0.3750000, 0.3398438, 0.3203125, 0.3007812, 0.2656250, 0.2460938, 0.2304688, 0.2109375, 0.1796875, 0.1640625, 0.1484375, 0.1171875, 0.1015625, 0.0898438, 0.0742188, 0.0507812, 0.0507812, 0.0312500, 0.0195312, 0.0039062, 0.0000000, 0.0078125, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0312500, 0.0273438, 0.0273438, 0.0234375, 0.0195312, 0.0117188, 0.0078125, 0.0039062, 0.0117188, 0.0195312, 0.0312500, 0.0507812, 0.0625000, 0.0742188, 0.1015625, 0.1171875, 0.1328125, 0.1484375, 0.1796875, 0.1953125, 0.2109375, 0.2304688, 0.2656250, 0.2851562, 0.3007812, 0.3359375, 0.3554688, 0.3750000, 0.3906250, 0.4296875, 0.4453125, 0.4648438, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5742188, 0.5859375, 0.6015625, 0.6132812, 0.6367188, 0.6445312, 0.6562500, 0.6718750, 0.6796875, 0.6875000, 0.6914062, 0.7031250, 0.7031250, 0.7070312, 0.7109375, 0.7070312, 0.7070312, 0.7031250, 0.6992188, 0.6914062, 0.6875000, 0.6796875, 0.6640625, 0.6562500, 0.6445312, 0.6250000, 0.6132812, 0.6015625, 0.5859375, 0.5585938, 0.5429688, 0.5273438, 0.4960938, 0.4804688, 0.4648438, 0.4453125, 0.4101562, 0.3906250, 0.3750000, 0.3554688, 0.3203125, 0.3007812, 0.2851562, 0.2460938, 0.2304688, 0.2109375, 0.1953125, 0.1640625, 0.1484375, 0.1328125, 0.1171875, 0.0898438, 0.0742188, 0.0625000, 0.0507812, 0.0312500, 0.0195312, 0.0117188, 0.0000000, 0.0078125, 0.0117188, 0.0234375, 0.0234375, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0234375, 0.0234375, 0.0117188, 0.0078125, 0.0000000, 0.0117188, 0.0195312, 0.0312500, 0.0507812, 0.0625000, 0.0742188, 0.0898438, 0.1171875, 0.1328125, 0.1484375, 0.1640625, 0.1953125, 0.2109375, 0.2304688, 0.2460938, 0.2851562, 0.3007812, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.3203125, 0.0000000, 0.9960938, 0.9960938]), array([ 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9804688, 0.9882812, 0.9531250, 0.9335938, 0.9179688, 0.9023438, 0.8750000, 0.8671875, 0.8554688, 0.8437500, 0.8359375, 0.8359375, 0.8320312, 0.8359375, 0.8398438, 0.8437500, 0.8515625, 0.8710938, 0.8828125, 0.8945312, 0.9257812, 0.9414062, 0.9609375, 0.9804688, 0.9687500, 0.9492188, 0.9257812, 0.8789062, 0.8515625, 0.8281250, 0.8007812, 0.7500000, 0.7265625, 0.6992188, 0.6757812, 0.6250000, 0.6015625, 0.5781250, 0.5351562, 0.5156250, 0.4960938, 0.4765625, 0.4414062, 0.4257812, 0.4101562, 0.3984375, 0.3750000, 0.3671875, 0.3593750, 0.3476562, 0.3437500, 0.3437500, 0.3437500, 0.3437500, 0.3476562, 0.3515625, 0.3632812, 0.3710938, 0.3789062, 0.3867188, 0.4062500, 0.4179688, 0.4296875, 0.4414062, 0.4687500, 0.4843750, 0.4960938, 0.5234375, 0.5390625, 0.5507812, 0.5664062, 0.5937500, 0.6054688, 0.6171875, 0.6406250, 0.6484375, 0.6601562, 0.6679688, 0.6835938, 0.6875000, 0.6953125, 0.6992188, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.6992188, 0.6953125, 0.6875000, 0.6757812, 0.6679688, 0.6601562, 0.6484375, 0.6289062, 0.6171875, 0.6015625, 0.5781250, 0.5625000, 0.5468750, 0.5312500, 0.5000000, 0.4843750, 0.4687500, 0.4375000, 0.4218750, 0.4062500, 0.3906250, 0.3593750, 0.3437500, 0.3320312, 0.3164062, 0.2890625, 0.2773438, 0.2656250, 0.2421875, 0.2343750, 0.2226562, 0.2148438, 0.1992188, 0.1953125, 0.1875000, 0.1796875, 0.1757812, 0.1718750, 0.1718750, 0.1679688, 0.1679688, 0.1679688, 0.1718750, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1992188, 0.2070312, 0.2109375, 0.2148438, 0.2226562, 0.2304688, 0.2343750, 0.2382812, 0.2460938, 0.2500000, 0.2500000, 0.2539062, 0.2578125, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2578125, 0.2578125, 0.2539062, 0.2500000, 0.2421875, 0.2382812, 0.2343750, 0.2265625, 0.2187500, 0.2148438, 0.2070312, 0.1953125, 0.1875000, 0.1796875, 0.1679688, 0.1601562, 0.1523438, 0.1445312, 0.1328125, 0.1250000, 0.1171875, 0.1093750, 0.0976562, 0.0898438, 0.0859375, 0.0742188, 0.0664062, 0.0625000, 0.0585938, 0.0468750, 0.0429688, 0.0507812, 0.0312500, 0.0273438, 0.0234375, 0.0234375, 0.0156250, 0.0156250, 0.0117188, 0.0078125, 0.0039062, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.9960938, 0.9960938]), array([ 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.8671875, 0.8203125, 0.7265625, 0.6835938, 0.6406250, 0.5976562, 0.5156250, 0.4765625, 0.4414062, 0.3710938, 0.3398438, 0.3085938, 0.2812500, 0.2304688, 0.2109375, 0.1914062, 0.1718750, 0.1445312, 0.1328125, 0.1250000, 0.1171875, 0.1132812, 0.1171875, 0.1210938, 0.1328125, 0.1445312, 0.1562500, 0.1914062, 0.2109375, 0.2304688, 0.2578125, 0.3085938, 0.3398438, 0.3710938, 0.4062500, 0.4765625, 0.5156250, 0.5546875, 0.6406250, 0.6835938, 0.7265625, 0.7734375, 0.8671875, 0.9140625, 0.9609375, 0.9804688, 0.8867188, 0.8359375, 0.7890625, 0.6914062, 0.6445312, 0.5976562, 0.5507812, 0.4609375, 0.4179688, 0.3750000, 0.2929688, 0.2539062, 0.2187500, 0.1835938, 0.1171875, 0.0859375, 0.0585938, 0.0351562, 0.0078125, 0.0273438, 0.0468750, 0.0742188, 0.0859375, 0.0937500, 0.0976562, 0.1054688, 0.1015625, 0.0976562, 0.0859375, 0.0742188, 0.0625000, 0.0468750, 0.0078125, 0.0078125, 0.0351562, 0.0585938, 0.1171875, 0.1484375, 0.1835938, 0.2539062, 0.2929688, 0.3320312, 0.3750000, 0.4609375, 0.5039062, 0.5507812, 0.5976562, 0.6914062, 0.7382812, 0.7890625, 0.8828125, 0.9335938, 0.9804688, 0.9609375, 0.8671875, 0.8203125, 0.7734375, 0.6835938, 0.6406250, 0.5976562, 0.5546875, 0.4765625, 0.4414062, 0.4062500, 0.3710938, 0.3085938, 0.2812500, 0.2578125, 0.2109375, 0.1914062, 0.1718750, 0.1562500, 0.1328125, 0.1250000, 0.1210938, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1445312, 0.1562500, 0.1718750, 0.1914062, 0.2304688, 0.2578125, 0.2812500, 0.3398438, 0.3710938, 0.4062500, 0.4414062, 0.5156250, 0.5546875, 0.5976562, 0.6835938, 0.7265625, 0.7734375, 0.8203125, 0.9140625, 0.9609375, 0.9804688, 0.9335938, 0.8359375, 0.7890625, 0.7382812, 0.6445312, 0.5976562, 0.5507812, 0.5039062, 0.4179688, 0.3750000, 0.3320312, 0.2929688, 0.2187500, 0.1835938, 0.1484375, 0.0859375, 0.0585938, 0.0351562, 0.0078125, 0.0273438, 0.0468750, 0.0625000, 0.0859375, 0.0937500, 0.0976562, 0.1015625, 0.1015625, 0.0976562, 0.0937500, 0.0859375, 0.0625000, 0.0468750, 0.0273438, 0.0078125, 0.0351562, 0.0585938, 0.0859375, 0.1484375, 0.1835938, 0.2187500, 0.2929688, 0.3320312, 0.3750000, 0.4179688, 0.5039062, 0.5507812, 0.5976562, 0.6445312, 0.7382812, 0.7890625, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.8359375, 0.0000000, 0.9960938, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 30 :: Ocean ### color_map_luts['idl30'] = \ ( array([ 0.2109375, 0.2109375, 0.2304688, 0.2460938, 0.2656250, 0.2851562, 0.3007812, 0.3203125, 0.3203125, 0.3398438, 0.3554688, 0.3750000, 0.3906250, 0.4101562, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5585938, 0.5742188, 0.5859375, 0.6015625, 0.6132812, 0.6250000, 0.6367188, 0.6445312, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6914062, 0.6914062, 0.6992188, 0.7031250, 0.7031250, 0.7070312, 0.7070312, 0.7109375, 0.7070312, 0.7070312, 0.7031250, 0.7031250, 0.6992188, 0.6914062, 0.6875000, 0.6796875, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6445312, 0.6367188, 0.6250000, 0.6132812, 0.6015625, 0.5859375, 0.5742188, 0.5585938, 0.5429688, 0.5273438, 0.5117188, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4453125, 0.4296875, 0.4101562, 0.3906250, 0.3750000, 0.3554688, 0.3359375, 0.3203125, 0.3007812, 0.2851562, 0.2656250, 0.2656250, 0.2460938, 0.2304688, 0.2109375, 0.1953125, 0.1796875, 0.1640625, 0.1484375, 0.1328125, 0.1171875, 0.1015625, 0.0898438, 0.0742188, 0.0625000, 0.0507812, 0.0507812, 0.0507812, 0.0312500, 0.0195312, 0.0117188, 0.0039062, 0.0000000, 0.0078125, 0.0117188, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0273438, 0.0312500, 0.0312500, 0.0273438, 0.0273438, 0.0234375, 0.0234375, 0.0195312, 0.0117188, 0.0078125, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0312500, 0.0507812, 0.0507812, 0.0507812, 0.0625000, 0.0742188, 0.0898438, 0.1015625, 0.1171875, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1953125, 0.2109375, 0.2304688, 0.2460938, 0.2656250, 0.2656250, 0.2851562, 0.3007812, 0.3203125, 0.3398438, 0.3554688, 0.3750000, 0.3906250, 0.4101562, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5859375, 0.6015625, 0.6132812, 0.6250000, 0.6367188, 0.6445312, 0.6562500, 0.6640625, 0.6718750, 0.6718750, 0.6796875, 0.6875000, 0.6914062, 0.6992188, 0.7031250, 0.7031250, 0.7070312, 0.7070312, 0.7109375, 0.7070312, 0.7070312, 0.7031250, 0.7031250, 0.6992188, 0.6992188, 0.6914062, 0.6875000, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6445312, 0.6367188, 0.6250000, 0.6132812, 0.6015625, 0.5859375, 0.5742188, 0.5585938, 0.5585938, 0.5429688, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4453125, 0.4296875, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.4101562, 0.0000000, 0.9960938, 0.9960938]), array([ 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2578125, 0.2578125, 0.2578125, 0.2539062, 0.2500000, 0.2500000, 0.2460938, 0.2421875, 0.2382812, 0.2343750, 0.2304688, 0.2226562, 0.2187500, 0.2148438, 0.2109375, 0.2070312, 0.2070312, 0.2031250, 0.1992188, 0.1914062, 0.1875000, 0.1835938, 0.1835938, 0.1796875, 0.1757812, 0.1718750, 0.1718750, 0.1718750, 0.1679688, 0.1679688, 0.1679688, 0.1679688, 0.1679688, 0.1718750, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1953125, 0.1992188, 0.2070312, 0.2148438, 0.2226562, 0.2343750, 0.2343750, 0.2421875, 0.2539062, 0.2656250, 0.2773438, 0.2890625, 0.3046875, 0.3164062, 0.3320312, 0.3437500, 0.3593750, 0.3750000, 0.3906250, 0.4062500, 0.4218750, 0.4218750, 0.4375000, 0.4531250, 0.4687500, 0.4843750, 0.5000000, 0.5156250, 0.5312500, 0.5468750, 0.5625000, 0.5781250, 0.5898438, 0.6015625, 0.6171875, 0.6289062, 0.6289062, 0.6406250, 0.6484375, 0.6601562, 0.6679688, 0.6757812, 0.6835938, 0.6875000, 0.6953125, 0.6992188, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.6992188, 0.6992188, 0.6953125, 0.6875000, 0.6835938, 0.6757812, 0.6679688, 0.6601562, 0.6484375, 0.6406250, 0.6289062, 0.6171875, 0.6054688, 0.5937500, 0.5937500, 0.5781250, 0.5664062, 0.5507812, 0.5390625, 0.5234375, 0.5117188, 0.4960938, 0.4843750, 0.4687500, 0.4570312, 0.4414062, 0.4296875, 0.4179688, 0.4062500, 0.4062500, 0.3945312, 0.3867188, 0.3789062, 0.3710938, 0.3632812, 0.3554688, 0.3515625, 0.3476562, 0.3437500, 0.3437500, 0.3437500, 0.3437500, 0.3437500, 0.3476562, 0.3476562, 0.3554688, 0.3593750, 0.3671875, 0.3750000, 0.3867188, 0.3984375, 0.4101562, 0.4257812, 0.4414062, 0.4570312, 0.4765625, 0.4960938, 0.5156250, 0.5351562, 0.5351562, 0.5546875, 0.5781250, 0.6015625, 0.6250000, 0.6484375, 0.6757812, 0.6992188, 0.7265625, 0.7500000, 0.7773438, 0.8007812, 0.8281250, 0.8515625, 0.8515625, 0.8789062, 0.9023438, 0.9257812, 0.9492188, 0.9687500, 0.9921875, 0.9804688, 0.9609375, 0.9414062, 0.9257812, 0.9101562, 0.8945312, 0.8828125, 0.8710938, 0.8710938, 0.8593750, 0.8515625, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8320312, 0.8359375, 0.8359375, 0.8437500, 0.8476562, 0.8554688, 0.8671875, 0.8750000, 0.8750000, 0.8906250, 0.9023438, 0.9179688, 0.9335938, 0.9531250, 0.9687500, 0.9882812, 0.9804688, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.0000000, 0.9960938, 0.9960938]), array([ 0.5507812, 0.5507812, 0.5976562, 0.6445312, 0.6914062, 0.7382812, 0.7890625, 0.8359375, 0.8359375, 0.8867188, 0.9335938, 0.9804688, 0.9609375, 0.9140625, 0.8671875, 0.8203125, 0.7734375, 0.7265625, 0.6835938, 0.6406250, 0.5976562, 0.5546875, 0.5156250, 0.5156250, 0.4765625, 0.4414062, 0.4062500, 0.3710938, 0.3398438, 0.3085938, 0.2812500, 0.2578125, 0.2304688, 0.2109375, 0.1914062, 0.1718750, 0.1562500, 0.1562500, 0.1445312, 0.1328125, 0.1250000, 0.1210938, 0.1171875, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1328125, 0.1445312, 0.1562500, 0.1718750, 0.1914062, 0.1914062, 0.2109375, 0.2304688, 0.2578125, 0.2812500, 0.3085938, 0.3398438, 0.3710938, 0.4062500, 0.4414062, 0.4765625, 0.5156250, 0.5546875, 0.5976562, 0.6406250, 0.6406250, 0.6835938, 0.7265625, 0.7734375, 0.8203125, 0.8671875, 0.9140625, 0.9609375, 0.9804688, 0.9335938, 0.8828125, 0.8359375, 0.7890625, 0.7382812, 0.6914062, 0.6914062, 0.6445312, 0.5976562, 0.5507812, 0.5039062, 0.4609375, 0.4179688, 0.3750000, 0.3320312, 0.2929688, 0.2539062, 0.2187500, 0.1835938, 0.1484375, 0.1171875, 0.1171875, 0.0859375, 0.0585938, 0.0351562, 0.0078125, 0.0078125, 0.0273438, 0.0468750, 0.0625000, 0.0742188, 0.0859375, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1054688, 0.1015625, 0.0976562, 0.0937500, 0.0859375, 0.0742188, 0.0625000, 0.0468750, 0.0273438, 0.0078125, 0.0078125, 0.0351562, 0.0585938, 0.0859375, 0.1171875, 0.1171875, 0.1484375, 0.1835938, 0.2187500, 0.2539062, 0.2929688, 0.3320312, 0.3750000, 0.4179688, 0.4609375, 0.5039062, 0.5507812, 0.5976562, 0.6445312, 0.6914062, 0.6914062, 0.7382812, 0.7890625, 0.8359375, 0.8867188, 0.9335938, 0.9804688, 0.9609375, 0.9140625, 0.8671875, 0.8203125, 0.7734375, 0.7265625, 0.6835938, 0.6406250, 0.6406250, 0.5976562, 0.5546875, 0.5156250, 0.4765625, 0.4414062, 0.4062500, 0.3710938, 0.3398438, 0.3085938, 0.2812500, 0.2578125, 0.2304688, 0.2109375, 0.2109375, 0.1914062, 0.1718750, 0.1562500, 0.1445312, 0.1328125, 0.1250000, 0.1210938, 0.1171875, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1328125, 0.1445312, 0.1445312, 0.1562500, 0.1718750, 0.1914062, 0.2109375, 0.2304688, 0.2578125, 0.2812500, 0.3085938, 0.3398438, 0.3710938, 0.4062500, 0.4414062, 0.4765625, 0.5156250, 0.5156250, 0.5546875, 0.5976562, 0.6406250, 0.6835938, 0.7265625, 0.7734375, 0.8203125, 0.8671875, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.9140625, 0.0000000, 0.9960938, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 31 :: Peppermint ### color_map_luts['idl31'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3164062, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.9375000, 0.9375000, 0.9375000, 0.9375000]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.0625000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1250000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.1875000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.2500000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3125000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.3750000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.4375000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5000000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.5625000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.6875000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7539062, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.7500000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8125000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.8750000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000, 0.9375000]), array([ 0.3125000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.9375000, 0.0000000, 0.3125000, 0.6250000, 0.6250000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 32 :: Plasma ### color_map_luts['idl32'] = \ ( array([ 0.0078125, 0.0078125, 0.0117188, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0507812, 0.0429688, 0.0507812, 0.0546875, 0.0585938, 0.0625000, 0.0664062, 0.0703125, 0.0742188, 0.0820312, 0.0859375, 0.0898438, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1093750, 0.1171875, 0.1210938, 0.1250000, 0.1289062, 0.1328125, 0.1367188, 0.1406250, 0.1445312, 0.1484375, 0.1523438, 0.1523438, 0.1562500, 0.1601562, 0.1640625, 0.1679688, 0.1718750, 0.1757812, 0.1796875, 0.1796875, 0.1835938, 0.1875000, 0.1914062, 0.1953125, 0.1992188, 0.2031250, 0.2070312, 0.2109375, 0.2148438, 0.2187500, 0.2226562, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2539062, 0.2578125, 0.2617188, 0.2656250, 0.2734375, 0.2773438, 0.2812500, 0.2890625, 0.2929688, 0.3007812, 0.3046875, 0.3085938, 0.3164062, 0.3203125, 0.3281250, 0.3320312, 0.3359375, 0.3437500, 0.3476562, 0.3515625, 0.3593750, 0.3632812, 0.3671875, 0.3710938, 0.3789062, 0.3828125, 0.3867188, 0.3906250, 0.3945312, 0.3984375, 0.4023438, 0.4023438, 0.4062500, 0.4101562, 0.4140625, 0.4179688, 0.4179688, 0.4218750, 0.4218750, 0.4257812, 0.4296875, 0.4296875, 0.4335938, 0.4335938, 0.4375000, 0.4414062, 0.4414062, 0.4453125, 0.4453125, 0.4492188, 0.4531250, 0.4570312, 0.4570312, 0.4609375, 0.4648438, 0.4687500, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4960938, 0.5000000, 0.5039062, 0.5117188, 0.5156250, 0.5234375, 0.5273438, 0.5351562, 0.5390625, 0.5468750, 0.5546875, 0.5585938, 0.5664062, 0.5742188, 0.5820312, 0.5859375, 0.5937500, 0.6015625, 0.6054688, 0.6132812, 0.6171875, 0.6250000, 0.6289062, 0.6367188, 0.6406250, 0.6484375, 0.6523438, 0.6562500, 0.6601562, 0.6640625, 0.6679688, 0.6718750, 0.6757812, 0.6796875, 0.6796875, 0.6835938, 0.6835938, 0.6875000, 0.6875000, 0.6914062, 0.6914062, 0.6953125, 0.6953125, 0.6953125, 0.6953125, 0.6992188, 0.6992188, 0.6992188, 0.7031250, 0.7031250, 0.7031250, 0.7070312, 0.7070312, 0.7109375, 0.7109375, 0.7148438, 0.7187500, 0.7187500, 0.7226562, 0.7265625, 0.7304688, 0.7343750, 0.7421875, 0.7460938, 0.7500000, 0.7578125, 0.7617188, 0.7695312, 0.7734375, 0.7812500, 0.7890625, 0.7968750, 0.8046875, 0.8125000, 0.8203125, 0.8281250, 0.8359375, 0.8437500, 0.8515625, 0.8593750, 0.8671875, 0.8750000, 0.8789062, 0.8867188, 0.8945312, 0.9023438, 0.9062500, 0.9140625, 0.9218750, 0.9257812, 0.9296875, 0.9335938, 0.9414062, 0.9453125, 0.9453125, 0.9492188, 0.9531250, 0.9570312, 0.9570312, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9648438, 0.9687500, 0.9687500, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9804688, 0.9843750, 0.9921875, 0.9960938, 0.9921875, 0.9843750, 0.9804688, 0.9726562, 0.9648438, 0.9648438]), array([ 0.0742188, 0.0742188, 0.0781250, 0.0859375, 0.0976562, 0.1132812, 0.1289062, 0.1484375, 0.1718750, 0.1992188, 0.2265625, 0.2539062, 0.2851562, 0.3164062, 0.3476562, 0.3828125, 0.4140625, 0.4492188, 0.4843750, 0.5156250, 0.5468750, 0.5781250, 0.6054688, 0.6328125, 0.6601562, 0.6835938, 0.7031250, 0.7187500, 0.7343750, 0.7460938, 0.7539062, 0.7578125, 0.7617188, 0.7578125, 0.7539062, 0.7460938, 0.7343750, 0.7187500, 0.7031250, 0.6835938, 0.6601562, 0.6328125, 0.6054688, 0.5781250, 0.5468750, 0.5156250, 0.4843750, 0.4492188, 0.4179688, 0.3828125, 0.3476562, 0.3164062, 0.2851562, 0.2539062, 0.2265625, 0.1992188, 0.1718750, 0.1484375, 0.1289062, 0.1132812, 0.0976562, 0.0859375, 0.0781250, 0.0742188, 0.0742188, 0.0742188, 0.0781250, 0.0859375, 0.0976562, 0.1132812, 0.1289062, 0.1484375, 0.1718750, 0.1992188, 0.2265625, 0.2539062, 0.2851562, 0.3164062, 0.3476562, 0.3828125, 0.4179688, 0.4492188, 0.4843750, 0.5156250, 0.5468750, 0.5781250, 0.6054688, 0.6328125, 0.6601562, 0.6835938, 0.7031250, 0.7187500, 0.7343750, 0.7460938, 0.7539062, 0.7578125, 0.7617188, 0.7578125, 0.7539062, 0.7460938, 0.7343750, 0.7187500, 0.7031250, 0.6835938, 0.6601562, 0.6328125, 0.6054688, 0.5781250, 0.5468750, 0.5156250, 0.4843750, 0.4492188, 0.4140625, 0.3828125, 0.3476562, 0.3164062, 0.2851562, 0.2539062, 0.2265625, 0.1992188, 0.1718750, 0.1484375, 0.1289062, 0.1132812, 0.0976562, 0.0859375, 0.0781250, 0.0742188, 0.0742188, 0.0742188, 0.0781250, 0.0859375, 0.0976562, 0.1132812, 0.1289062, 0.1484375, 0.1718750, 0.1992188, 0.2265625, 0.2539062, 0.2851562, 0.3164062, 0.3476562, 0.3828125, 0.4179688, 0.4492188, 0.4843750, 0.5156250, 0.5468750, 0.5781250, 0.6054688, 0.6328125, 0.6601562, 0.6835938, 0.7031250, 0.7187500, 0.7343750, 0.7460938, 0.7539062, 0.7578125, 0.7617188, 0.7578125, 0.7539062, 0.7460938, 0.7343750, 0.7187500, 0.7031250, 0.6835938, 0.6601562, 0.6328125, 0.6054688, 0.5781250, 0.5468750, 0.5156250, 0.4843750, 0.4492188, 0.4140625, 0.3828125, 0.3476562, 0.3164062, 0.2851562, 0.2539062, 0.2265625, 0.1992188, 0.1718750, 0.1484375, 0.1289062, 0.1132812, 0.0976562, 0.0859375, 0.0781250, 0.0742188, 0.0742188, 0.0742188, 0.0781250, 0.0859375, 0.0976562, 0.1132812, 0.1289062, 0.1484375, 0.1718750, 0.1992188, 0.2265625, 0.2539062, 0.2851562, 0.3164062, 0.3476562, 0.3828125, 0.4179688, 0.4492188, 0.4843750, 0.5156250, 0.5468750, 0.5781250, 0.6054688, 0.6328125, 0.6601562, 0.6835938, 0.7031250, 0.7187500, 0.7343750, 0.7460938, 0.7539062, 0.7578125, 0.7617188, 0.7578125, 0.7539062, 0.7460938, 0.7343750, 0.7187500, 0.7031250, 0.6835938, 0.6601562, 0.6328125, 0.6054688, 0.5781250, 0.5468750, 0.5156250, 0.4843750, 0.4492188, 0.4140625, 0.3828125, 0.3476562, 0.3164062, 0.2851562, 0.2539062, 0.2265625, 0.1992188, 0.1718750, 0.1484375, 0.1289062, 0.1132812, 0.0976562, 0.0859375, 0.0781250, 0.0781250]), array([ 0.0273438, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0859375, 0.1015625, 0.1171875, 0.1328125, 0.1523438, 0.1679688, 0.1835938, 0.1992188, 0.2148438, 0.2304688, 0.2460938, 0.2617188, 0.2773438, 0.2929688, 0.3046875, 0.3203125, 0.3359375, 0.3515625, 0.3632812, 0.3789062, 0.3906250, 0.4023438, 0.4179688, 0.4296875, 0.4414062, 0.4531250, 0.4648438, 0.4765625, 0.4882812, 0.5000000, 0.5078125, 0.5195312, 0.5312500, 0.5429688, 0.5507812, 0.5625000, 0.5742188, 0.5859375, 0.5976562, 0.6093750, 0.6210938, 0.6328125, 0.6445312, 0.6601562, 0.6718750, 0.6875000, 0.7031250, 0.7187500, 0.7343750, 0.7500000, 0.7656250, 0.7851562, 0.8007812, 0.8203125, 0.8398438, 0.8593750, 0.8789062, 0.8984375, 0.9179688, 0.9414062, 0.9609375, 0.9804688, 0.9882812, 0.9687500, 0.9492188, 0.9257812, 0.9062500, 0.8867188, 0.8671875, 0.8476562, 0.8281250, 0.8085938, 0.7890625, 0.7734375, 0.7578125, 0.7421875, 0.7265625, 0.7109375, 0.6992188, 0.6875000, 0.6718750, 0.6640625, 0.6523438, 0.6445312, 0.6328125, 0.6250000, 0.6171875, 0.6132812, 0.6054688, 0.6015625, 0.5937500, 0.5898438, 0.5859375, 0.5781250, 0.5742188, 0.5703125, 0.5625000, 0.5585938, 0.5507812, 0.5468750, 0.5390625, 0.5312500, 0.5234375, 0.5156250, 0.5039062, 0.4921875, 0.4804688, 0.4687500, 0.4531250, 0.4414062, 0.4218750, 0.4062500, 0.3867188, 0.3671875, 0.3476562, 0.3242188, 0.3046875, 0.2812500, 0.2539062, 0.2304688, 0.2070312, 0.1796875, 0.1523438, 0.1250000, 0.0976562, 0.0703125, 0.0429688, 0.0195312, 0.9882812, 0.9609375, 0.9335938, 0.9101562, 0.8867188, 0.8632812, 0.8398438, 0.8203125, 0.8007812, 0.7812500, 0.7617188, 0.7460938, 0.7343750, 0.7187500, 0.7070312, 0.6953125, 0.6875000, 0.6796875, 0.6718750, 0.6679688, 0.6640625, 0.6601562, 0.6601562, 0.6562500, 0.6562500, 0.6562500, 0.6562500, 0.6601562, 0.6601562, 0.6601562, 0.6601562, 0.6640625, 0.6640625, 0.6640625, 0.6601562, 0.6601562, 0.6562500, 0.6523438, 0.6484375, 0.6406250, 0.6328125, 0.6250000, 0.6132812, 0.6015625, 0.5859375, 0.5703125, 0.5507812, 0.5312500, 0.5117188, 0.4882812, 0.4648438, 0.4375000, 0.4140625, 0.3828125, 0.3554688, 0.3242188, 0.2929688, 0.2617188, 0.2265625, 0.1953125, 0.1601562, 0.1289062, 0.0937500, 0.0625000, 0.0312500, 0.9960938, 0.9648438, 0.9335938, 0.9062500, 0.8789062, 0.8554688, 0.8320312, 0.8085938, 0.7890625, 0.7695312, 0.7539062, 0.7382812, 0.7265625, 0.7148438, 0.7070312, 0.6992188, 0.6953125, 0.6914062, 0.6914062, 0.6914062, 0.6953125, 0.6992188, 0.7031250, 0.7070312, 0.7148438, 0.7187500, 0.7265625, 0.7343750, 0.7421875, 0.7500000, 0.7539062, 0.7617188, 0.7656250, 0.7695312, 0.7734375, 0.7734375, 0.7734375, 0.7734375, 0.7695312, 0.7617188, 0.7539062, 0.7460938, 0.7343750, 0.7187500, 0.7031250, 0.6835938, 0.6601562, 0.6367188, 0.6093750, 0.5820312, 0.5546875, 0.5195312, 0.4882812, 0.4531250, 0.4179688, 0.3789062, 0.3398438, 0.3398438]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 33 :: Blue-Red ### color_map_luts['idl33'] = \ ( array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0898438, 0.1054688, 0.1210938, 0.1367188, 0.1523438, 0.1679688, 0.1835938, 0.1992188, 0.2148438, 0.2304688, 0.2460938, 0.2617188, 0.2773438, 0.2929688, 0.3085938, 0.3242188, 0.3398438, 0.3554688, 0.3710938, 0.3867188, 0.4023438, 0.4179688, 0.4335938, 0.4492188, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5898438, 0.6054688, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6992188, 0.7148438, 0.7304688, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8085938, 0.8242188, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9765625, 0.9609375, 0.9414062, 0.9257812, 0.9101562, 0.8906250, 0.8750000, 0.8554688, 0.8398438, 0.8242188, 0.8046875, 0.7890625, 0.7695312, 0.7539062, 0.7382812, 0.7187500, 0.7031250, 0.6835938, 0.6679688, 0.6523438, 0.6328125, 0.6171875, 0.5976562, 0.5820312, 0.5664062, 0.5468750, 0.5312500, 0.5117188, 0.5117188]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0117188, 0.0273438, 0.0429688, 0.0585938, 0.0742188, 0.0898438, 0.1054688, 0.1210938, 0.1367188, 0.1523438, 0.1679688, 0.1835938, 0.1992188, 0.2148438, 0.2304688, 0.2460938, 0.2617188, 0.2773438, 0.2929688, 0.3085938, 0.3242188, 0.3398438, 0.3554688, 0.3710938, 0.3867188, 0.4023438, 0.4179688, 0.4335938, 0.4492188, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5898438, 0.6054688, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6992188, 0.7148438, 0.7304688, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8085938, 0.8242188, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8554688, 0.8398438, 0.8242188, 0.8085938, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6679688, 0.6523438, 0.6367188, 0.6210938, 0.6054688, 0.5898438, 0.5742188, 0.5585938, 0.5429688, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4492188, 0.4335938, 0.4179688, 0.4023438, 0.3867188, 0.3710938, 0.3554688, 0.3398438, 0.3242188, 0.3085938, 0.2929688, 0.2773438, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1992188, 0.1835938, 0.1679688, 0.1523438, 0.1367188, 0.1210938, 0.1054688, 0.0898438, 0.0742188, 0.0585938, 0.0429688, 0.0273438, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.5117188, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5898438, 0.6054688, 0.6210938, 0.6367188, 0.6523438, 0.6679688, 0.6835938, 0.6992188, 0.7148438, 0.7304688, 0.7460938, 0.7617188, 0.7773438, 0.7929688, 0.8085938, 0.8242188, 0.8398438, 0.8554688, 0.8710938, 0.8867188, 0.9023438, 0.9179688, 0.9335938, 0.9492188, 0.9648438, 0.9804688, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9804688, 0.9648438, 0.9492188, 0.9335938, 0.9179688, 0.9023438, 0.8867188, 0.8710938, 0.8554688, 0.8398438, 0.8242188, 0.8085938, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.7148438, 0.6992188, 0.6835938, 0.6679688, 0.6523438, 0.6367188, 0.6210938, 0.6054688, 0.5898438, 0.5742188, 0.5585938, 0.5429688, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4492188, 0.4335938, 0.4179688, 0.4023438, 0.3867188, 0.3710938, 0.3554688, 0.3398438, 0.3242188, 0.3085938, 0.2929688, 0.2773438, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1992188, 0.1835938, 0.1679688, 0.1523438, 0.1367188, 0.1210938, 0.1054688, 0.0898438, 0.0742188, 0.0585938, 0.0429688, 0.0273438, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 34 :: Rainbow ### color_map_luts['idl34'] = \ ( array([ 0.4843750, 0.4843750, 0.4687500, 0.4492188, 0.4335938, 0.4140625, 0.3984375, 0.3789062, 0.3632812, 0.3437500, 0.3281250, 0.3085938, 0.2929688, 0.2734375, 0.2578125, 0.2382812, 0.2226562, 0.2031250, 0.1875000, 0.1679688, 0.1523438, 0.1328125, 0.1171875, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0273438, 0.0117188, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0195312, 0.0507812, 0.0546875, 0.0742188, 0.0898438, 0.1093750, 0.1250000, 0.1445312, 0.1601562, 0.1796875, 0.1953125, 0.2148438, 0.2304688, 0.2500000, 0.2656250, 0.2851562, 0.3007812, 0.3203125, 0.3359375, 0.3554688, 0.3710938, 0.3906250, 0.4062500, 0.4257812, 0.4414062, 0.4609375, 0.4804688, 0.4960938, 0.5156250, 0.5312500, 0.5507812, 0.5664062, 0.5859375, 0.6015625, 0.6210938, 0.6367188, 0.6562500, 0.6718750, 0.6914062, 0.7070312, 0.7265625, 0.7421875, 0.7617188, 0.7773438, 0.7968750, 0.8125000, 0.8320312, 0.8476562, 0.8671875, 0.8828125, 0.9023438, 0.9179688, 0.9375000, 0.9531250, 0.9726562, 0.9882812, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0234375, 0.0429688, 0.0585938, 0.0781250, 0.0937500, 0.1132812, 0.1289062, 0.1484375, 0.1640625, 0.1835938, 0.1992188, 0.2187500, 0.2343750, 0.2539062, 0.2695312, 0.2890625, 0.3046875, 0.3242188, 0.3398438, 0.3593750, 0.3750000, 0.3945312, 0.4101562, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.5000000, 0.5156250, 0.5351562, 0.5507812, 0.5703125, 0.5859375, 0.6054688, 0.6210938, 0.6406250, 0.6562500, 0.6757812, 0.6914062, 0.7109375, 0.7265625, 0.7460938, 0.7617188, 0.7812500, 0.7968750, 0.8164062, 0.8320312, 0.8515625, 0.8671875, 0.8867188, 0.9023438, 0.9218750, 0.9414062, 0.9570312, 0.9765625, 0.9921875, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9843750, 0.9687500, 0.9492188, 0.9335938, 0.9140625, 0.8984375, 0.8789062, 0.8632812, 0.8437500, 0.8281250, 0.8085938, 0.7929688, 0.7734375, 0.7578125, 0.7382812, 0.7226562, 0.7031250, 0.6875000, 0.6679688, 0.6523438, 0.6328125, 0.6171875, 0.5976562, 0.5820312, 0.5625000, 0.5468750, 0.5273438, 0.5117188, 0.4921875, 0.4765625, 0.4570312, 0.4414062, 0.4218750, 0.4062500, 0.3867188, 0.3710938, 0.3515625, 0.3359375, 0.3164062, 0.3007812, 0.2812500, 0.2656250, 0.2460938, 0.2304688, 0.2109375, 0.1953125, 0.1757812, 0.1601562, 0.1406250, 0.1250000, 0.1054688, 0.0898438, 0.0703125, 0.0546875, 0.0351562, 0.0195312, 0.0195312]), array([ 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9804688, 0.9648438, 0.9453125, 0.9296875, 0.9101562, 0.8945312, 0.8750000, 0.8593750, 0.8398438, 0.8242188, 0.8046875, 0.7890625, 0.7695312, 0.7539062, 0.7343750, 0.7187500, 0.6992188, 0.6835938, 0.6640625, 0.6484375, 0.6289062, 0.6132812, 0.5937500, 0.5781250, 0.5585938, 0.5429688, 0.5234375, 0.5078125, 0.4882812, 0.4726562, 0.4531250, 0.4375000, 0.4179688, 0.4023438, 0.3828125, 0.3671875, 0.3476562, 0.3320312, 0.3125000, 0.2968750, 0.2773438, 0.2617188, 0.2421875, 0.2265625, 0.2070312, 0.1914062, 0.1718750, 0.1562500, 0.1367188, 0.1210938, 0.1015625, 0.0859375, 0.0664062, 0.0507812, 0.0312500, 0.0156250, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 35 :: Blue Waves ### color_map_luts['idl35'] = \ ( array([ 0.3203125, 0.3203125, 0.3007812, 0.2851562, 0.2656250, 0.2460938, 0.2304688, 0.2109375, 0.1953125, 0.1796875, 0.1640625, 0.1484375, 0.1328125, 0.1171875, 0.1015625, 0.0898438, 0.0742188, 0.0625000, 0.0507812, 0.0507812, 0.0312500, 0.0195312, 0.0117188, 0.0039062, 0.0000000, 0.0078125, 0.0117188, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0273438, 0.0312500, 0.0273438, 0.0273438, 0.0234375, 0.0234375, 0.0195312, 0.0117188, 0.0078125, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0312500, 0.0507812, 0.0507812, 0.0625000, 0.0742188, 0.0898438, 0.1015625, 0.1171875, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1953125, 0.2109375, 0.2304688, 0.2460938, 0.2656250, 0.2851562, 0.3007812, 0.3203125, 0.3398438, 0.3554688, 0.3750000, 0.3906250, 0.4101562, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5859375, 0.6015625, 0.6132812, 0.6250000, 0.6367188, 0.6445312, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6914062, 0.6992188, 0.7031250, 0.7031250, 0.7070312, 0.7070312, 0.7109375, 0.7070312, 0.7070312, 0.7031250, 0.7031250, 0.6992188, 0.6914062, 0.6875000, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6445312, 0.6367188, 0.6250000, 0.6132812, 0.6015625, 0.5859375, 0.5742188, 0.5585938, 0.5429688, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4453125, 0.4296875, 0.4101562, 0.3906250, 0.3750000, 0.3554688, 0.3359375, 0.3203125, 0.3007812, 0.2851562, 0.2656250, 0.2460938, 0.2304688, 0.2109375, 0.1953125, 0.1796875, 0.1640625, 0.1484375, 0.1328125, 0.1171875, 0.1015625, 0.0898438, 0.0742188, 0.0625000, 0.0507812, 0.0507812, 0.0312500, 0.0195312, 0.0117188, 0.0039062, 0.0000000, 0.0078125, 0.0117188, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0273438, 0.0312500, 0.0273438, 0.0273438, 0.0234375, 0.0234375, 0.0195312, 0.0117188, 0.0078125, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0312500, 0.0507812, 0.0507812, 0.0625000, 0.0742188, 0.0898438, 0.1015625, 0.1171875, 0.1328125, 0.1484375, 0.1640625, 0.1796875, 0.1953125, 0.2109375, 0.2304688, 0.2460938, 0.2656250, 0.2851562, 0.3007812, 0.3203125, 0.3398438, 0.3554688, 0.3750000, 0.3906250, 0.4101562, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.4960938, 0.5117188, 0.5273438, 0.5429688, 0.5585938, 0.5742188, 0.5859375, 0.6015625, 0.6132812, 0.6250000, 0.6367188, 0.6445312, 0.6562500, 0.6640625, 0.6718750, 0.6796875, 0.6875000, 0.6914062, 0.6992188, 0.7031250, 0.7031250, 0.7070312, 0.7070312, 0.7109375, 0.7070312, 0.7070312, 0.7031250, 0.7031250, 0.6992188, 0.6914062, 0.6875000, 0.6796875, 0.6718750, 0.6640625, 0.6562500, 0.6445312, 0.6367188, 0.6250000, 0.6132812, 0.6015625, 0.5859375, 0.5742188, 0.5585938, 0.5429688, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4453125, 0.4296875, 0.4101562, 0.3906250, 0.3750000, 0.3750000]), array([ 0.0000000, 0.0000000, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0117188, 0.0156250, 0.0156250, 0.0195312, 0.0234375, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0507812, 0.0429688, 0.0468750, 0.0507812, 0.0585938, 0.0625000, 0.0664062, 0.0742188, 0.0781250, 0.0859375, 0.0898438, 0.0976562, 0.1054688, 0.1093750, 0.1171875, 0.1250000, 0.1328125, 0.1367188, 0.1445312, 0.1523438, 0.1601562, 0.1679688, 0.1757812, 0.1796875, 0.1875000, 0.1953125, 0.1992188, 0.2070312, 0.2148438, 0.2187500, 0.2265625, 0.2304688, 0.2343750, 0.2382812, 0.2421875, 0.2460938, 0.2500000, 0.2539062, 0.2578125, 0.2578125, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2617188, 0.2578125, 0.2578125, 0.2539062, 0.2500000, 0.2500000, 0.2460938, 0.2421875, 0.2382812, 0.2343750, 0.2304688, 0.2226562, 0.2187500, 0.2148438, 0.2109375, 0.2070312, 0.2031250, 0.1992188, 0.1914062, 0.1875000, 0.1835938, 0.1835938, 0.1796875, 0.1757812, 0.1718750, 0.1718750, 0.1718750, 0.1679688, 0.1679688, 0.1679688, 0.1679688, 0.1718750, 0.1718750, 0.1757812, 0.1796875, 0.1835938, 0.1875000, 0.1953125, 0.1992188, 0.2070312, 0.2148438, 0.2226562, 0.2343750, 0.2421875, 0.2539062, 0.2656250, 0.2773438, 0.2890625, 0.3046875, 0.3164062, 0.3320312, 0.3437500, 0.3593750, 0.3750000, 0.3906250, 0.4062500, 0.4218750, 0.4375000, 0.4531250, 0.4687500, 0.4843750, 0.5000000, 0.5156250, 0.5312500, 0.5468750, 0.5625000, 0.5781250, 0.5898438, 0.6015625, 0.6171875, 0.6289062, 0.6406250, 0.6484375, 0.6601562, 0.6679688, 0.6757812, 0.6835938, 0.6875000, 0.6953125, 0.6992188, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.7031250, 0.6992188, 0.6992188, 0.6953125, 0.6875000, 0.6835938, 0.6757812, 0.6679688, 0.6601562, 0.6484375, 0.6406250, 0.6289062, 0.6171875, 0.6054688, 0.5937500, 0.5781250, 0.5664062, 0.5507812, 0.5390625, 0.5234375, 0.5117188, 0.4960938, 0.4843750, 0.4687500, 0.4570312, 0.4414062, 0.4296875, 0.4179688, 0.4062500, 0.3945312, 0.3867188, 0.3789062, 0.3710938, 0.3632812, 0.3554688, 0.3515625, 0.3476562, 0.3437500, 0.3437500, 0.3437500, 0.3437500, 0.3437500, 0.3476562, 0.3554688, 0.3593750, 0.3671875, 0.3750000, 0.3867188, 0.3984375, 0.4101562, 0.4257812, 0.4414062, 0.4570312, 0.4765625, 0.4960938, 0.5156250, 0.5351562, 0.5546875, 0.5781250, 0.6015625, 0.6250000, 0.6484375, 0.6757812, 0.6992188, 0.7265625, 0.7500000, 0.7773438, 0.8007812, 0.8281250, 0.8515625, 0.8789062, 0.9023438, 0.9257812, 0.9492188, 0.9687500, 0.9921875, 0.9804688, 0.9609375, 0.9414062, 0.9257812, 0.9101562, 0.8945312, 0.8828125, 0.8710938, 0.8593750, 0.8515625, 0.8437500, 0.8398438, 0.8359375, 0.8320312, 0.8320312, 0.8359375, 0.8359375, 0.8437500, 0.8476562, 0.8554688, 0.8671875, 0.8750000, 0.8906250, 0.9023438, 0.9179688, 0.9335938, 0.9531250, 0.9687500, 0.9882812, 0.9804688, 0.9609375, 0.9375000, 0.9140625, 0.9140625]), array([ 0.8359375, 0.8359375, 0.7890625, 0.7382812, 0.6914062, 0.6445312, 0.5976562, 0.5507812, 0.5039062, 0.4609375, 0.4179688, 0.3750000, 0.3320312, 0.2929688, 0.2539062, 0.2187500, 0.1835938, 0.1484375, 0.1171875, 0.0859375, 0.0585938, 0.0351562, 0.0078125, 0.0078125, 0.0273438, 0.0468750, 0.0625000, 0.0742188, 0.0859375, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1015625, 0.0976562, 0.0937500, 0.0859375, 0.0742188, 0.0625000, 0.0468750, 0.0273438, 0.0078125, 0.0078125, 0.0351562, 0.0585938, 0.0859375, 0.1171875, 0.1484375, 0.1835938, 0.2187500, 0.2539062, 0.2929688, 0.3320312, 0.3750000, 0.4179688, 0.4609375, 0.5039062, 0.5507812, 0.5976562, 0.6445312, 0.6914062, 0.7382812, 0.7890625, 0.8359375, 0.8867188, 0.9335938, 0.9804688, 0.9609375, 0.9140625, 0.8671875, 0.8203125, 0.7734375, 0.7265625, 0.6835938, 0.6406250, 0.5976562, 0.5546875, 0.5156250, 0.4765625, 0.4414062, 0.4062500, 0.3710938, 0.3398438, 0.3085938, 0.2812500, 0.2578125, 0.2304688, 0.2109375, 0.1914062, 0.1718750, 0.1562500, 0.1445312, 0.1328125, 0.1250000, 0.1210938, 0.1171875, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1328125, 0.1445312, 0.1562500, 0.1718750, 0.1914062, 0.2109375, 0.2304688, 0.2578125, 0.2812500, 0.3085938, 0.3398438, 0.3710938, 0.4062500, 0.4414062, 0.4765625, 0.5156250, 0.5546875, 0.5976562, 0.6406250, 0.6835938, 0.7265625, 0.7734375, 0.8203125, 0.8671875, 0.9140625, 0.9609375, 0.9804688, 0.9335938, 0.8828125, 0.8359375, 0.7890625, 0.7382812, 0.6914062, 0.6445312, 0.5976562, 0.5507812, 0.5039062, 0.4609375, 0.4179688, 0.3750000, 0.3320312, 0.2929688, 0.2539062, 0.2187500, 0.1835938, 0.1484375, 0.1171875, 0.0859375, 0.0585938, 0.0351562, 0.0078125, 0.0078125, 0.0273438, 0.0468750, 0.0625000, 0.0742188, 0.0859375, 0.0937500, 0.0976562, 0.1015625, 0.1054688, 0.1015625, 0.0976562, 0.0937500, 0.0859375, 0.0742188, 0.0625000, 0.0468750, 0.0273438, 0.0078125, 0.0078125, 0.0351562, 0.0585938, 0.0859375, 0.1171875, 0.1484375, 0.1835938, 0.2187500, 0.2539062, 0.2929688, 0.3320312, 0.3750000, 0.4179688, 0.4609375, 0.5039062, 0.5507812, 0.5976562, 0.6445312, 0.6914062, 0.7382812, 0.7890625, 0.8359375, 0.8867188, 0.9335938, 0.9804688, 0.9609375, 0.9140625, 0.8671875, 0.8203125, 0.7734375, 0.7265625, 0.6835938, 0.6406250, 0.5976562, 0.5546875, 0.5156250, 0.4765625, 0.4414062, 0.4062500, 0.3710938, 0.3398438, 0.3085938, 0.2812500, 0.2578125, 0.2304688, 0.2109375, 0.1914062, 0.1718750, 0.1562500, 0.1445312, 0.1328125, 0.1250000, 0.1210938, 0.1171875, 0.1132812, 0.1171875, 0.1210938, 0.1250000, 0.1328125, 0.1445312, 0.1562500, 0.1718750, 0.1914062, 0.2109375, 0.2304688, 0.2578125, 0.2812500, 0.3085938, 0.3398438, 0.3710938, 0.4062500, 0.4414062, 0.4765625, 0.5156250, 0.5546875, 0.5976562, 0.6406250, 0.6835938, 0.7265625, 0.7734375, 0.8203125, 0.8671875, 0.9140625, 0.9609375, 0.9804688, 0.9804688]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 36 :: Volcano ### color_map_luts['idl36'] = \ ( array([ 0.2500000, 0.2500000, 0.2343750, 0.2226562, 0.2109375, 0.1992188, 0.1875000, 0.1757812, 0.1640625, 0.1562500, 0.1445312, 0.1367188, 0.1250000, 0.1171875, 0.1093750, 0.1015625, 0.0937500, 0.0859375, 0.0781250, 0.0742188, 0.0664062, 0.0625000, 0.0546875, 0.0507812, 0.0468750, 0.0429688, 0.0507812, 0.0351562, 0.0351562, 0.0312500, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0273438, 0.0312500, 0.0312500, 0.0351562, 0.0507812, 0.0429688, 0.0468750, 0.0507812, 0.0546875, 0.0585938, 0.0664062, 0.0703125, 0.0781250, 0.0859375, 0.0898438, 0.0976562, 0.1054688, 0.1171875, 0.1250000, 0.1328125, 0.1445312, 0.1523438, 0.1640625, 0.1718750, 0.1835938, 0.1953125, 0.2070312, 0.2187500, 0.2304688, 0.2460938, 0.2578125, 0.2734375, 0.2851562, 0.3007812, 0.3125000, 0.3281250, 0.3437500, 0.3593750, 0.3750000, 0.3906250, 0.4062500, 0.4257812, 0.4414062, 0.4570312, 0.4765625, 0.4921875, 0.5117188, 0.5312500, 0.5468750, 0.5664062, 0.5859375, 0.6054688, 0.6250000, 0.6445312, 0.6640625, 0.6835938, 0.7031250, 0.7265625, 0.7460938, 0.7656250, 0.7851562, 0.8085938, 0.8281250, 0.8515625, 0.8710938, 0.8945312, 0.9140625, 0.9375000, 0.9609375, 0.9804688, 0.9882812, 0.9648438, 0.9414062, 0.9218750, 0.8984375, 0.8750000, 0.8515625, 0.8320312, 0.8085938, 0.7851562, 0.7617188, 0.7382812, 0.7148438, 0.6953125, 0.6718750, 0.6484375, 0.6250000, 0.6015625, 0.5781250, 0.5585938, 0.5351562, 0.5117188, 0.4882812, 0.4687500, 0.4453125, 0.4218750, 0.4023438, 0.3789062, 0.3593750, 0.3359375, 0.3125000, 0.2929688, 0.2734375, 0.2500000, 0.2304688, 0.2070312, 0.1875000, 0.1679688, 0.1484375, 0.1289062, 0.1093750, 0.0898438, 0.0703125, 0.0507812, 0.0312500, 0.0117188, 0.0039062, 0.0195312, 0.0507812, 0.0585938, 0.0742188, 0.0898438, 0.1093750, 0.1250000, 0.1406250, 0.1562500, 0.1718750, 0.1875000, 0.2031250, 0.2187500, 0.2343750, 0.2460938, 0.2617188, 0.2734375, 0.2851562, 0.3007812, 0.3125000, 0.3242188, 0.3359375, 0.3476562, 0.3593750, 0.3671875, 0.3789062, 0.3906250, 0.3984375, 0.4062500, 0.4179688, 0.4257812, 0.4335938, 0.4414062, 0.4453125, 0.4531250, 0.4609375, 0.4648438, 0.4726562, 0.4765625, 0.4804688, 0.4843750, 0.4882812, 0.4921875, 0.4960938, 0.5000000, 0.5000000, 0.5039062, 0.5039062, 0.5039062, 0.5039062, 0.5039062, 0.5039062, 0.5039062, 0.5039062, 0.5039062, 0.5000000, 0.4960938, 0.4960938, 0.4921875, 0.4882812, 0.4843750, 0.4804688, 0.4765625, 0.4687500, 0.4648438, 0.4570312, 0.4531250, 0.4453125, 0.4375000, 0.4296875, 0.4218750, 0.4140625, 0.4023438, 0.3945312, 0.3867188, 0.3750000, 0.3632812, 0.3554688, 0.3437500, 0.3320312, 0.3203125, 0.3085938, 0.2929688, 0.2812500, 0.2695312, 0.2539062, 0.2421875, 0.2265625, 0.2109375, 0.1953125, 0.1835938, 0.1679688, 0.1523438, 0.1328125, 0.1171875, 0.1015625, 0.0859375, 0.0664062, 0.0507812, 0.0312500, 0.0312500]), array([ 0.1367188, 0.1367188, 0.1562500, 0.1718750, 0.1914062, 0.2109375, 0.2265625, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3125000, 0.3281250, 0.3476562, 0.3632812, 0.3750000, 0.3906250, 0.4062500, 0.4218750, 0.4335938, 0.4492188, 0.4609375, 0.4726562, 0.4843750, 0.4960938, 0.5078125, 0.5195312, 0.5273438, 0.5351562, 0.5468750, 0.5546875, 0.5625000, 0.5664062, 0.5742188, 0.5781250, 0.5859375, 0.5898438, 0.5937500, 0.5937500, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5937500, 0.5898438, 0.5859375, 0.5820312, 0.5781250, 0.5703125, 0.5664062, 0.5585938, 0.5507812, 0.5429688, 0.5351562, 0.5234375, 0.5156250, 0.5039062, 0.4921875, 0.4804688, 0.4687500, 0.4570312, 0.4414062, 0.4296875, 0.4140625, 0.4023438, 0.3867188, 0.3710938, 0.3554688, 0.3398438, 0.3242188, 0.3085938, 0.2890625, 0.2734375, 0.2578125, 0.2382812, 0.2226562, 0.2031250, 0.1835938, 0.1679688, 0.1484375, 0.1289062, 0.1132812, 0.0937500, 0.0742188, 0.0585938, 0.0507812, 0.0195312, 0.0039062, 0.0117188, 0.0273438, 0.0468750, 0.0625000, 0.0781250, 0.0976562, 0.1132812, 0.1289062, 0.1445312, 0.1601562, 0.1757812, 0.1914062, 0.2070312, 0.2187500, 0.2343750, 0.2460938, 0.2578125, 0.2695312, 0.2812500, 0.2929688, 0.3046875, 0.3125000, 0.3242188, 0.3320312, 0.3398438, 0.3476562, 0.3554688, 0.3593750, 0.3671875, 0.3710938, 0.3750000, 0.3789062, 0.3828125, 0.3867188, 0.3867188, 0.3867188, 0.3867188, 0.3867188, 0.3867188, 0.3867188, 0.3828125, 0.3789062, 0.3789062, 0.3710938, 0.3671875, 0.3632812, 0.3554688, 0.3476562, 0.3437500, 0.3359375, 0.3242188, 0.3164062, 0.3046875, 0.2968750, 0.2851562, 0.2734375, 0.2617188, 0.2500000, 0.2382812, 0.2226562, 0.2109375, 0.1953125, 0.1796875, 0.1640625, 0.1484375, 0.1328125, 0.1171875, 0.1015625, 0.0859375, 0.0664062, 0.0507812, 0.0351562, 0.0156250, 0.0000000, 0.0156250, 0.0351562, 0.0507812, 0.0703125, 0.0898438, 0.1054688, 0.1250000, 0.1445312, 0.1601562, 0.1796875, 0.1992188, 0.2148438, 0.2343750, 0.2500000, 0.2695312, 0.2851562, 0.3007812, 0.3203125, 0.3359375, 0.3515625, 0.3671875, 0.3828125, 0.3984375, 0.4101562, 0.4257812, 0.4375000, 0.4531250, 0.4648438, 0.4765625, 0.4882812, 0.5000000, 0.5117188, 0.5195312, 0.5312500, 0.5390625, 0.5468750, 0.5546875, 0.5625000, 0.5703125, 0.5742188, 0.5820312, 0.5859375, 0.5898438, 0.5937500, 0.5937500, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5976562, 0.5937500, 0.5937500, 0.5898438, 0.5859375, 0.5820312, 0.5742188, 0.5703125, 0.5625000, 0.5546875, 0.5468750, 0.5390625, 0.5312500, 0.5195312, 0.5117188, 0.5000000, 0.4882812, 0.4765625, 0.4648438, 0.4531250, 0.4375000, 0.4257812, 0.4101562, 0.3945312, 0.3828125, 0.3671875, 0.3515625, 0.3359375, 0.3164062, 0.3007812, 0.2851562, 0.2656250, 0.2500000, 0.2343750, 0.2148438, 0.1953125, 0.1796875, 0.1601562, 0.1445312, 0.1250000, 0.1250000]), array([ 0.4531250, 0.4531250, 0.4101562, 0.3671875, 0.3281250, 0.2890625, 0.2500000, 0.2148438, 0.1796875, 0.1484375, 0.1171875, 0.0937500, 0.0703125, 0.0468750, 0.0312500, 0.0195312, 0.0078125, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0234375, 0.0507812, 0.0546875, 0.0781250, 0.1015625, 0.1289062, 0.1601562, 0.1953125, 0.2304688, 0.2656250, 0.3046875, 0.3437500, 0.3867188, 0.4257812, 0.4687500, 0.5117188, 0.5546875, 0.5976562, 0.6367188, 0.6796875, 0.7187500, 0.7539062, 0.7890625, 0.8242188, 0.8515625, 0.8828125, 0.9062500, 0.9296875, 0.9492188, 0.9648438, 0.9765625, 0.9843750, 0.9882812, 0.9882812, 0.9882812, 0.9804688, 0.9726562, 0.9609375, 0.9414062, 0.9218750, 0.8984375, 0.8750000, 0.8437500, 0.8125000, 0.7812500, 0.7421875, 0.7070312, 0.6679688, 0.6250000, 0.5859375, 0.5429688, 0.5000000, 0.4570312, 0.4140625, 0.3750000, 0.3320312, 0.2929688, 0.2539062, 0.2187500, 0.1835938, 0.1523438, 0.1210938, 0.0937500, 0.0703125, 0.0507812, 0.0351562, 0.0195312, 0.0078125, 0.0000000, 0.0000000, 0.0000000, 0.0039062, 0.0117188, 0.0195312, 0.0351562, 0.0546875, 0.0742188, 0.0976562, 0.1250000, 0.1562500, 0.1875000, 0.2226562, 0.2617188, 0.2968750, 0.3398438, 0.3789062, 0.4218750, 0.4648438, 0.5078125, 0.5468750, 0.5898438, 0.6328125, 0.6718750, 0.7109375, 0.7500000, 0.7851562, 0.8164062, 0.8476562, 0.8789062, 0.9023438, 0.9257812, 0.9453125, 0.9609375, 0.9726562, 0.9843750, 0.9882812, 0.9921875, 0.9882812, 0.9843750, 0.9726562, 0.9609375, 0.9453125, 0.9257812, 0.9023438, 0.8789062, 0.8476562, 0.8164062, 0.7851562, 0.7500000, 0.7109375, 0.6718750, 0.6328125, 0.5898438, 0.5468750, 0.5078125, 0.4648438, 0.4218750, 0.3789062, 0.3398438, 0.2968750, 0.2617188, 0.2226562, 0.1875000, 0.1562500, 0.1250000, 0.0976562, 0.0742188, 0.0546875, 0.0351562, 0.0195312, 0.0117188, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0195312, 0.0351562, 0.0507812, 0.0703125, 0.0937500, 0.1210938, 0.1523438, 0.1835938, 0.2187500, 0.2539062, 0.2929688, 0.3320312, 0.3750000, 0.4140625, 0.4570312, 0.5000000, 0.5429688, 0.5859375, 0.6250000, 0.6679688, 0.7070312, 0.7421875, 0.7812500, 0.8125000, 0.8437500, 0.8750000, 0.8984375, 0.9218750, 0.9414062, 0.9609375, 0.9726562, 0.9804688, 0.9882812, 0.9882812, 0.9882812, 0.9843750, 0.9765625, 0.9648438, 0.9492188, 0.9296875, 0.9062500, 0.8828125, 0.8515625, 0.8242188, 0.7890625, 0.7539062, 0.7187500, 0.6796875, 0.6367188, 0.5976562, 0.5546875, 0.5117188, 0.4687500, 0.4257812, 0.3867188, 0.3437500, 0.3046875, 0.2656250, 0.2304688, 0.1953125, 0.1601562, 0.1289062, 0.1015625, 0.0781250, 0.0546875, 0.0507812, 0.0234375, 0.0117188, 0.0039062, 0.0000000, 0.0000000, 0.0000000, 0.0078125, 0.0195312, 0.0312500, 0.0468750, 0.0703125, 0.0937500, 0.1171875, 0.1484375, 0.1796875, 0.2148438, 0.2500000, 0.2890625, 0.3281250, 0.3671875, 0.4101562, 0.4101562]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 37 :: Waves ### color_map_luts['idl37'] = \ ( array([ 0.4843750, 0.4843750, 0.4726562, 0.4609375, 0.4492188, 0.4375000, 0.4257812, 0.4140625, 0.4023438, 0.3906250, 0.3789062, 0.3671875, 0.3554688, 0.3437500, 0.3320312, 0.3203125, 0.3085938, 0.2968750, 0.2851562, 0.2734375, 0.2656250, 0.2539062, 0.2421875, 0.2343750, 0.2226562, 0.2109375, 0.2031250, 0.1914062, 0.1835938, 0.1757812, 0.1640625, 0.1562500, 0.1484375, 0.1406250, 0.1289062, 0.1210938, 0.1132812, 0.1054688, 0.0976562, 0.0937500, 0.0859375, 0.0781250, 0.0742188, 0.0664062, 0.0585938, 0.0546875, 0.0507812, 0.0429688, 0.0507812, 0.0351562, 0.0312500, 0.0273438, 0.0234375, 0.0195312, 0.0156250, 0.0156250, 0.0117188, 0.0078125, 0.0078125, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0117188, 0.0156250, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0507812, 0.0429688, 0.0507812, 0.0546875, 0.0585938, 0.0664062, 0.0742188, 0.0781250, 0.0859375, 0.0937500, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1406250, 0.1484375, 0.1562500, 0.1640625, 0.1757812, 0.1835938, 0.1914062, 0.2031250, 0.2109375, 0.2226562, 0.2343750, 0.2421875, 0.2539062, 0.2656250, 0.2734375, 0.2851562, 0.2968750, 0.3085938, 0.3203125, 0.3320312, 0.3437500, 0.3554688, 0.3671875, 0.3789062, 0.3906250, 0.4023438, 0.4140625, 0.4257812, 0.4375000, 0.4492188, 0.4609375, 0.4726562, 0.4843750, 0.5000000, 0.5117188, 0.5234375, 0.5351562, 0.5468750, 0.5585938, 0.5703125, 0.5820312, 0.5937500, 0.6054688, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6640625, 0.6757812, 0.6875000, 0.6992188, 0.7109375, 0.7226562, 0.7304688, 0.7421875, 0.7539062, 0.7617188, 0.7734375, 0.7851562, 0.7929688, 0.8046875, 0.8125000, 0.8203125, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8671875, 0.8750000, 0.8828125, 0.8906250, 0.8984375, 0.9023438, 0.9101562, 0.9179688, 0.9218750, 0.9296875, 0.9375000, 0.9414062, 0.9453125, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9804688, 0.9843750, 0.9882812, 0.9882812, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9960938, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9882812, 0.9882812, 0.9843750, 0.9804688, 0.9804688, 0.9765625, 0.9726562, 0.9687500, 0.9648438, 0.9609375, 0.9570312, 0.9531250, 0.9453125, 0.9414062, 0.9375000, 0.9296875, 0.9218750, 0.9179688, 0.9101562, 0.9023438, 0.8984375, 0.8906250, 0.8828125, 0.8750000, 0.8671875, 0.8554688, 0.8476562, 0.8398438, 0.8320312, 0.8203125, 0.8125000, 0.8046875, 0.7929688, 0.7851562, 0.7734375, 0.7617188, 0.7539062, 0.7421875, 0.7304688, 0.7226562, 0.7109375, 0.6992188, 0.6875000, 0.6757812, 0.6640625, 0.6523438, 0.6406250, 0.6289062, 0.6171875, 0.6054688, 0.5937500, 0.5820312, 0.5703125, 0.5585938, 0.5468750, 0.5351562, 0.5234375, 0.5234375]), array([ 0.4726562, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3789062, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3750000, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2812500, 0.3789062, 0.4726562, 0.5507812, 0.6054688, 0.6250000, 0.6054688, 0.5507812, 0.4726562, 0.3750000, 0.2812500, 0.2031250, 0.1484375, 0.1328125, 0.1484375, 0.2031250, 0.2031250]), array([ 0.5117188, 0.5117188, 0.5234375, 0.5351562, 0.5468750, 0.5585938, 0.5703125, 0.5820312, 0.5937500, 0.6054688, 0.6171875, 0.6289062, 0.6406250, 0.6523438, 0.6640625, 0.6757812, 0.6875000, 0.6992188, 0.7109375, 0.7226562, 0.7304688, 0.7421875, 0.7539062, 0.7617188, 0.7734375, 0.7851562, 0.7929688, 0.8046875, 0.8125000, 0.8203125, 0.8320312, 0.8398438, 0.8476562, 0.8554688, 0.8671875, 0.8750000, 0.8828125, 0.8906250, 0.8984375, 0.9023438, 0.9101562, 0.9179688, 0.9218750, 0.9296875, 0.9375000, 0.9414062, 0.9453125, 0.9531250, 0.9570312, 0.9609375, 0.9648438, 0.9687500, 0.9726562, 0.9765625, 0.9804688, 0.9804688, 0.9843750, 0.9882812, 0.9882812, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9960938, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.9882812, 0.9882812, 0.9843750, 0.9804688, 0.9804688, 0.9765625, 0.9726562, 0.9687500, 0.9648438, 0.9609375, 0.9570312, 0.9531250, 0.9453125, 0.9414062, 0.9375000, 0.9296875, 0.9218750, 0.9179688, 0.9101562, 0.9023438, 0.8984375, 0.8906250, 0.8828125, 0.8750000, 0.8671875, 0.8554688, 0.8476562, 0.8398438, 0.8320312, 0.8203125, 0.8125000, 0.8046875, 0.7929688, 0.7851562, 0.7734375, 0.7617188, 0.7539062, 0.7421875, 0.7304688, 0.7226562, 0.7109375, 0.6992188, 0.6875000, 0.6757812, 0.6640625, 0.6523438, 0.6406250, 0.6289062, 0.6171875, 0.6054688, 0.5937500, 0.5820312, 0.5703125, 0.5585938, 0.5468750, 0.5351562, 0.5234375, 0.5117188, 0.4960938, 0.4843750, 0.4726562, 0.4609375, 0.4492188, 0.4375000, 0.4257812, 0.4140625, 0.4023438, 0.3906250, 0.3789062, 0.3671875, 0.3554688, 0.3437500, 0.3320312, 0.3203125, 0.3085938, 0.2968750, 0.2851562, 0.2734375, 0.2656250, 0.2539062, 0.2421875, 0.2343750, 0.2226562, 0.2109375, 0.2031250, 0.1914062, 0.1835938, 0.1757812, 0.1640625, 0.1562500, 0.1484375, 0.1406250, 0.1289062, 0.1210938, 0.1132812, 0.1054688, 0.0976562, 0.0937500, 0.0859375, 0.0781250, 0.0742188, 0.0664062, 0.0585938, 0.0546875, 0.0507812, 0.0429688, 0.0507812, 0.0351562, 0.0312500, 0.0273438, 0.0234375, 0.0195312, 0.0156250, 0.0156250, 0.0117188, 0.0078125, 0.0078125, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0039062, 0.0078125, 0.0078125, 0.0117188, 0.0156250, 0.0156250, 0.0195312, 0.0234375, 0.0273438, 0.0312500, 0.0351562, 0.0507812, 0.0429688, 0.0507812, 0.0546875, 0.0585938, 0.0664062, 0.0742188, 0.0781250, 0.0859375, 0.0937500, 0.0976562, 0.1054688, 0.1132812, 0.1210938, 0.1289062, 0.1406250, 0.1484375, 0.1562500, 0.1640625, 0.1757812, 0.1835938, 0.1914062, 0.2031250, 0.2109375, 0.2226562, 0.2343750, 0.2421875, 0.2539062, 0.2656250, 0.2734375, 0.2851562, 0.2968750, 0.3085938, 0.3203125, 0.3320312, 0.3437500, 0.3554688, 0.3671875, 0.3789062, 0.3906250, 0.4023438, 0.4140625, 0.4257812, 0.4375000, 0.4492188, 0.4609375, 0.4726562, 0.4726562]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 38 :: Rainbow18 ### color_map_luts['idl38'] = \ ( array([ 0.0000000, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.6835938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.5468750, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.6640625, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.6250000, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.4882812, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.1953125, 0.9960938, 0.9960938]), array([ 0.0000000, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.5859375, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.7812500, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.4687500, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.3906250, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.2929688, 0.9960938, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 39 :: Rainbow + white ### color_map_luts['idl39'] = \ ( array([ 0.0000000, 0.0156250, 0.0351562, 0.0507812, 0.0703125, 0.0859375, 0.1054688, 0.1210938, 0.1406250, 0.1562500, 0.1757812, 0.1953125, 0.2265625, 0.2382812, 0.2500000, 0.2656250, 0.2695312, 0.2812500, 0.2890625, 0.3007812, 0.3085938, 0.3125000, 0.3203125, 0.3242188, 0.3281250, 0.3359375, 0.3398438, 0.3437500, 0.3359375, 0.3398438, 0.3398438, 0.3398438, 0.3320312, 0.3281250, 0.3281250, 0.3281250, 0.3085938, 0.3046875, 0.3007812, 0.2968750, 0.2773438, 0.2734375, 0.2656250, 0.2578125, 0.2343750, 0.2265625, 0.2148438, 0.1796875, 0.1679688, 0.1562500, 0.1406250, 0.1289062, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0156250, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0820312, 0.0976562, 0.1132812, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3125000, 0.3476562, 0.3632812, 0.3789062, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5976562, 0.6132812, 0.6289062, 0.6445312, 0.6953125, 0.7109375, 0.7304688, 0.7460938, 0.7773438, 0.7929688, 0.8125000, 0.8281250, 0.8632812, 0.8789062, 0.8945312, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0625000, 0.0820312, 0.0976562, 0.1132812, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2460938, 0.2617188, 0.2812500, 0.3281250, 0.3476562, 0.3632812, 0.3789062, 0.4140625, 0.4296875, 0.4453125, 0.4648438, 0.4960938, 0.5117188, 0.5273438, 0.5468750, 0.5937500, 0.6132812, 0.6289062, 0.6445312, 0.6796875, 0.6953125, 0.7109375, 0.7304688, 0.7617188, 0.7773438, 0.7929688, 0.8437500, 0.8593750, 0.8789062, 0.8945312, 0.9101562, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9765625, 0.9453125, 0.9296875, 0.9101562, 0.8945312, 0.8632812, 0.8437500, 0.8281250, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.6953125, 0.6796875, 0.6640625, 0.6445312, 0.6289062, 0.5976562, 0.5781250, 0.5625000, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4296875, 0.4140625, 0.3984375, 0.3789062, 0.3476562, 0.3320312, 0.3125000, 0.2968750, 0.2460938, 0.2304688, 0.2148438, 0.1992188, 0.1640625, 0.1484375, 0.1328125, 0.1132812, 0.0820312, 0.0664062, 0.0468750, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.9960938]), array([ 0.0000000, 0.0117188, 0.0273438, 0.0390625, 0.0546875, 0.0742188, 0.0898438, 0.1093750, 0.1250000, 0.1484375, 0.1679688, 0.1875000, 0.2304688, 0.2460938, 0.2656250, 0.2812500, 0.3007812, 0.3164062, 0.3359375, 0.3554688, 0.3710938, 0.3906250, 0.4062500, 0.4257812, 0.4609375, 0.4765625, 0.4960938, 0.5156250, 0.5312500, 0.5507812, 0.5664062, 0.5859375, 0.6015625, 0.6210938, 0.6367188, 0.6562500, 0.6914062, 0.7109375, 0.7265625, 0.7460938, 0.7617188, 0.7812500, 0.7968750, 0.8164062, 0.8359375, 0.8515625, 0.8710938, 0.9062500, 0.9218750, 0.9414062, 0.9570312, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9609375, 0.9453125, 0.9296875, 0.8789062, 0.8593750, 0.8437500, 0.8281250, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.6953125, 0.6796875, 0.6640625, 0.6132812, 0.5937500, 0.5781250, 0.5625000, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3476562, 0.3281250, 0.3125000, 0.2968750, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1796875, 0.1640625, 0.1484375, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0312500, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.9960938]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) ### IDL colormap 40 :: Rainbow + black ### color_map_luts['idl40'] = \ ( array([ 0.0000000, 0.0156250, 0.0351562, 0.0507812, 0.0703125, 0.0859375, 0.1054688, 0.1210938, 0.1406250, 0.1562500, 0.1757812, 0.1953125, 0.2265625, 0.2382812, 0.2500000, 0.2656250, 0.2695312, 0.2812500, 0.2890625, 0.3007812, 0.3085938, 0.3125000, 0.3203125, 0.3242188, 0.3281250, 0.3359375, 0.3398438, 0.3437500, 0.3359375, 0.3398438, 0.3398438, 0.3398438, 0.3320312, 0.3281250, 0.3281250, 0.3281250, 0.3085938, 0.3046875, 0.3007812, 0.2968750, 0.2773438, 0.2734375, 0.2656250, 0.2578125, 0.2343750, 0.2265625, 0.2148438, 0.1796875, 0.1679688, 0.1562500, 0.1406250, 0.1289062, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0156250, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0312500, 0.0468750, 0.0820312, 0.0976562, 0.1132812, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2460938, 0.2617188, 0.2812500, 0.2968750, 0.3125000, 0.3476562, 0.3632812, 0.3789062, 0.4296875, 0.4453125, 0.4648438, 0.4804688, 0.5117188, 0.5273438, 0.5468750, 0.5625000, 0.5976562, 0.6132812, 0.6289062, 0.6445312, 0.6953125, 0.7109375, 0.7304688, 0.7460938, 0.7773438, 0.7929688, 0.8125000, 0.8281250, 0.8632812, 0.8789062, 0.8945312, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.0000000]), array([ 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0156250, 0.0625000, 0.0820312, 0.0976562, 0.1132812, 0.1484375, 0.1640625, 0.1796875, 0.1992188, 0.2148438, 0.2460938, 0.2617188, 0.2812500, 0.3281250, 0.3476562, 0.3632812, 0.3789062, 0.4140625, 0.4296875, 0.4453125, 0.4648438, 0.4960938, 0.5117188, 0.5273438, 0.5468750, 0.5937500, 0.6132812, 0.6289062, 0.6445312, 0.6796875, 0.6953125, 0.7109375, 0.7304688, 0.7617188, 0.7773438, 0.7929688, 0.8437500, 0.8593750, 0.8789062, 0.8945312, 0.9101562, 0.9453125, 0.9609375, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9765625, 0.9453125, 0.9296875, 0.9101562, 0.8945312, 0.8632812, 0.8437500, 0.8281250, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.6953125, 0.6796875, 0.6640625, 0.6445312, 0.6289062, 0.5976562, 0.5781250, 0.5625000, 0.5117188, 0.4960938, 0.4804688, 0.4648438, 0.4296875, 0.4140625, 0.3984375, 0.3789062, 0.3476562, 0.3320312, 0.3125000, 0.2968750, 0.2460938, 0.2304688, 0.2148438, 0.1992188, 0.1640625, 0.1484375, 0.1328125, 0.1132812, 0.0820312, 0.0664062, 0.0468750, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 0.0000000, 0.0117188, 0.0273438, 0.0390625, 0.0546875, 0.0742188, 0.0898438, 0.1093750, 0.1250000, 0.1484375, 0.1679688, 0.1875000, 0.2304688, 0.2460938, 0.2656250, 0.2812500, 0.3007812, 0.3164062, 0.3359375, 0.3554688, 0.3710938, 0.3906250, 0.4062500, 0.4257812, 0.4609375, 0.4765625, 0.4960938, 0.5156250, 0.5312500, 0.5507812, 0.5664062, 0.5859375, 0.6015625, 0.6210938, 0.6367188, 0.6562500, 0.6914062, 0.7109375, 0.7265625, 0.7460938, 0.7617188, 0.7812500, 0.7968750, 0.8164062, 0.8359375, 0.8515625, 0.8710938, 0.9062500, 0.9218750, 0.9414062, 0.9570312, 0.9765625, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9960938, 0.9609375, 0.9453125, 0.9296875, 0.8789062, 0.8593750, 0.8437500, 0.8281250, 0.7929688, 0.7773438, 0.7617188, 0.7460938, 0.7304688, 0.6953125, 0.6796875, 0.6640625, 0.6132812, 0.5937500, 0.5781250, 0.5625000, 0.5273438, 0.5117188, 0.4960938, 0.4804688, 0.4453125, 0.4296875, 0.4140625, 0.3984375, 0.3476562, 0.3281250, 0.3125000, 0.2968750, 0.2617188, 0.2460938, 0.2304688, 0.2148438, 0.1796875, 0.1640625, 0.1484375, 0.0976562, 0.0820312, 0.0625000, 0.0468750, 0.0312500, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000]), array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), ) color_map_luts["doom"] = ( array([ 0, 31, 23, 75, 255, 27, 19, 11, 7, 47, 35, 23, 15, 79, 71, 63, 255, 247, 243, 235, 231, 223, 219, 211, 203, 199, 191, 187, 179, 175, 167, 163, 155, 151, 143, 139, 131, 127, 119, 115, 107, 103, 95, 91, 83, 79, 71, 67, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 239, 231, 223, 215, 207, 203, 191, 179, 171, 163, 155, 143, 135, 127, 119, 107, 95, 83, 75, 63, 51, 43, 239, 231, 223, 219, 211, 203, 199, 191, 183, 179, 171, 167, 159, 151, 147, 139, 131, 127, 119, 111, 107, 99, 91, 87, 79, 71, 67, 59, 55, 47, 39, 35, 119, 111, 103, 95, 91, 83, 75, 67, 63, 55, 47, 39, 31, 23, 19, 11, 191, 183, 175, 167, 159, 155, 147, 139, 131, 123, 119, 111, 103, 95, 87, 83, 159, 143, 131, 119, 103, 91, 79, 67, 123, 111, 103, 91, 83, 71, 63, 55, 255, 235, 215, 195, 175, 155, 135, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 227, 215, 203, 191, 179, 167, 155, 139, 127, 115, 103, 91, 79, 67, 231, 199, 171, 143, 115, 83, 55, 27, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 243, 235, 223, 215, 203, 195, 183, 175, 255, 255, 255, 255, 255, 255, 255, 255, 167, 159, 147, 135, 79, 67, 55, 47, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 207, 159, 111, 167]) / 255.0, array([ 0, 23, 15, 75, 255, 27, 19, 11, 7, 55, 43, 31, 23, 59, 51, 43, 183, 171, 163, 151, 143, 135, 123, 115, 107, 99, 91, 87, 79, 71, 63, 59, 51, 47, 43, 35, 31, 27, 23, 19, 15, 11, 7, 7, 7, 0, 0, 0, 235, 227, 219, 211, 207, 199, 191, 187, 179, 171, 163, 155, 147, 139, 131, 127, 123, 115, 111, 107, 99, 95, 87, 83, 79, 71, 67, 63, 55, 47, 43, 35, 239, 231, 223, 219, 211, 203, 199, 191, 183, 179, 171, 167, 159, 151, 147, 139, 131, 127, 119, 111, 107, 99, 91, 87, 79, 71, 67, 59, 55, 47, 39, 35, 255, 239, 223, 207, 191, 175, 159, 147, 131, 115, 99, 83, 67, 51, 35, 23, 167, 159, 151, 143, 135, 127, 123, 115, 107, 99, 95, 87, 83, 75, 67, 63, 131, 119, 107, 95, 83, 71, 59, 51, 127, 115, 107, 99, 87, 79, 71, 63, 255, 219, 187, 155, 123, 91, 67, 43, 255, 219, 187, 155, 123, 95, 63, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 199, 171, 143, 115, 83, 55, 27, 0, 0, 0, 0, 0, 0, 0, 0, 255, 235, 215, 199, 179, 163, 143, 127, 115, 111, 103, 95, 87, 79, 71, 67, 255, 255, 255, 255, 255, 255, 255, 255, 63, 55, 47, 35, 59, 47, 35, 27, 0, 0, 0, 0, 0, 0, 0, 0, 159, 231, 123, 0, 0, 0, 0, 107]) / 255.0, array([ 0, 11, 7, 75, 255, 27, 19, 11, 7, 31, 15, 7, 0, 43, 35, 27, 183, 171, 163, 151, 143, 135, 123, 115, 107, 99, 91, 87, 79, 71, 63, 59, 51, 47, 43, 35, 31, 27, 23, 19, 15, 11, 7, 7, 7, 0, 0, 0, 223, 211, 199, 187, 179, 167, 155, 147, 131, 123, 115, 107, 99, 91, 83, 79, 75, 71, 67, 63, 59, 55, 51, 47, 43, 39, 35, 31, 27, 23, 19, 15, 239, 231, 223, 219, 211, 203, 199, 191, 183, 179, 171, 167, 159, 151, 147, 139, 131, 127, 119, 111, 107, 99, 91, 87, 79, 71, 67, 59, 55, 47, 39, 35, 111, 103, 95, 87, 79, 71, 63, 55, 47, 43, 35, 27, 23, 15, 11, 7, 143, 135, 127, 119, 111, 107, 99, 91, 87, 79, 75, 67, 63, 55, 51, 47, 99, 83, 75, 63, 51, 43, 35, 27, 99, 87, 79, 71, 59, 51, 43, 39, 115, 87, 67, 47, 31, 19, 7, 0, 255, 219, 187, 155, 123, 95, 63, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 203, 179, 155, 131, 107, 83, 255, 219, 187, 155, 123, 91, 59, 27, 23, 15, 15, 11, 7, 0, 0, 0, 255, 215, 179, 143, 107, 71, 35, 0, 0, 0, 0, 0, 39, 27, 19, 11, 83, 71, 59, 47, 35, 23, 11, 0, 67, 75, 255, 255, 207, 155, 107, 107]) / 255.0, np.ones(256), ) # Aliases color_map_luts['B-W LINEAR'] = color_map_luts['idl00'] color_map_luts['BLUE'] = color_map_luts['idl01'] color_map_luts['GRN-RED-BLU-WHT'] = color_map_luts['idl02'] color_map_luts['RED TEMPERATURE'] = color_map_luts['idl03'] color_map_luts['BLUE'] = color_map_luts['idl04'] color_map_luts['STD GAMMA-II'] = color_map_luts['idl05'] color_map_luts['PRISM'] = color_map_luts['idl06'] color_map_luts['RED-PURPLE'] = color_map_luts['idl07'] color_map_luts['GREEN'] = color_map_luts['idl08'] color_map_luts['GRN'] = color_map_luts['idl09'] color_map_luts['GREEN-PINK'] = color_map_luts['idl10'] color_map_luts['BLUE-RED'] = color_map_luts['idl11'] color_map_luts['16 LEVEL'] = color_map_luts['idl12'] color_map_luts['RAINBOW'] = color_map_luts['idl13'] color_map_luts['STEPS'] = color_map_luts['idl14'] color_map_luts['STERN SPECIAL'] = color_map_luts['idl15'] color_map_luts['Haze'] = color_map_luts['idl16'] color_map_luts['Blue - Pastel - Red'] = color_map_luts['idl17'] color_map_luts['Pastels'] = color_map_luts['idl18'] color_map_luts['Hue Sat Lightness 1'] = color_map_luts['idl19'] color_map_luts['Hue Sat Lightness 2'] = color_map_luts['idl20'] color_map_luts['Hue Sat Value 1'] = color_map_luts['idl21'] color_map_luts['Hue Sat Value 2'] = color_map_luts['idl22'] color_map_luts['Purple-Red + Stripes'] = color_map_luts['idl23'] color_map_luts['Beach'] = color_map_luts['idl24'] color_map_luts['Mac Style'] = color_map_luts['idl25'] color_map_luts['Eos A'] = color_map_luts['idl26'] color_map_luts['Eos B'] = color_map_luts['idl27'] color_map_luts['Hardcandy'] = color_map_luts['idl28'] color_map_luts['Nature'] = color_map_luts['idl29'] color_map_luts['Ocean'] = color_map_luts['idl30'] color_map_luts['Peppermint'] = color_map_luts['idl31'] color_map_luts['Plasma'] = color_map_luts['idl32'] color_map_luts['Blue-Red'] = color_map_luts['idl33'] color_map_luts['Rainbow'] = color_map_luts['idl34'] color_map_luts['Blue Waves'] = color_map_luts['idl35'] color_map_luts['Volcano'] = color_map_luts['idl36'] color_map_luts['Waves'] = color_map_luts['idl37'] color_map_luts['Rainbow18'] = color_map_luts['idl38'] color_map_luts['Rainbow + white'] = color_map_luts['idl39'] color_map_luts['Rainbow + black'] = color_map_luts['idl40'] # Create a reversed LUT for each of the above defined LUTs # and append a "_r" (for reversal. consistent with MPL convention). # So for example, the reversal of "Waves" is "Waves_r" temp = {} for k,v in color_map_luts.items(): temp[k+"_r"] = (v[0][::-1], v[1][::-1], v[2][::-1], v[3][::-1]) color_map_luts.update(temp) yt-project-yt-f043ac8/yt/visualization/_commons.py000066400000000000000000000166011510711153200223770ustar00rootroot00000000000000import os import warnings from functools import wraps from typing import TYPE_CHECKING, TypeVar import matplotlib as mpl from matplotlib.ticker import SymmetricalLogLocator from more_itertools import always_iterable from yt.config import ytcfg if TYPE_CHECKING: from matplotlib.backend_bases import FigureCanvasBase _DEFAULT_FONT_PROPERTIES = None def get_default_font_properties(): global _DEFAULT_FONT_PROPERTIES if _DEFAULT_FONT_PROPERTIES is None: import importlib.resources as importlib_resources _yt_style = mpl.rc_params_from_file( importlib_resources.files("yt") / "default.mplstyle", use_default_template=False, ) _DEFAULT_FONT_PROPERTIES = { "family": _yt_style["font.family"][0], "math_fontfamily": _yt_style["mathtext.fontset"], } return _DEFAULT_FONT_PROPERTIES def _get_supported_image_file_formats(): from matplotlib.backend_bases import FigureCanvasBase return frozenset(FigureCanvasBase.get_supported_filetypes().keys()) def _get_supported_canvas_classes(): from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.backends.backend_pdf import FigureCanvasPdf from matplotlib.backends.backend_ps import FigureCanvasPS from matplotlib.backends.backend_svg import FigureCanvasSVG return frozenset( (FigureCanvasAgg, FigureCanvasPdf, FigureCanvasPS, FigureCanvasSVG) ) def get_canvas_class(suffix: str) -> type["FigureCanvasBase"]: s = suffix.removeprefix(".") if s not in _get_supported_image_file_formats(): raise ValueError(f"Unsupported file format '{suffix}'.") for cls in _get_supported_canvas_classes(): if s in cls.get_supported_filetypes(): return cls raise RuntimeError( "Something went terribly wrong. " f"File extension '{suffix}' is supposed to be supported " "but no compatible backend was found." ) def validate_image_name(filename, suffix: str | None = None) -> str: """ Build a valid image filename with a specified extension (default to png). The suffix parameter is ignored if the input filename has a valid extension already. Otherwise, suffix is appended to the filename, replacing any existing extension. """ name, psuffix = os.path.splitext(filename) psuffix = psuffix.removeprefix(".") if suffix is not None: suffix = suffix.removeprefix(".") if psuffix in _get_supported_image_file_formats(): if suffix in _get_supported_image_file_formats() and suffix != psuffix: warnings.warn( f"Received two valid image formats {psuffix!r} (from filename) " f"and {suffix!r} (from suffix). The former is ignored.", stacklevel=2, ) return f"{name}.{suffix}" return str(filename) if suffix is None: suffix = "png" if suffix not in _get_supported_image_file_formats(): raise ValueError(f"Unsupported file format {suffix!r}") return f"{filename}.{suffix}" def get_canvas(figure, filename): name, suffix = os.path.splitext(filename) if not suffix: raise ValueError( f"Can not determine canvas class from filename '{filename}' " f"without an extension." ) return get_canvas_class(suffix)(figure) def invalidate_plot(f): @wraps(f) def newfunc(self, *args, **kwargs): retv = f(self, *args, **kwargs) self._plot_valid = False return retv return newfunc def invalidate_data(f): @wraps(f) def newfunc(self, *args, **kwargs): retv = f(self, *args, **kwargs) self._data_valid = False self._plot_valid = False return retv return newfunc def invalidate_figure(f): @wraps(f) def newfunc(self, *args, **kwargs): retv = f(self, *args, **kwargs) for field in self.plots.keys(): self.plots[field].figure = None self.plots[field].axes = None self.plots[field].cax = None self._setup_plots() return retv return newfunc def validate_plot(f): @wraps(f) def newfunc(self, *args, **kwargs): # TODO: _profile_valid and _data_valid seem to play very similar roles, # there's probably room to abstract these into a common operation if hasattr(self, "_data_valid") and not self._data_valid: self._recreate_frb() if hasattr(self, "_profile_valid") and not self._profile_valid: self._recreate_profile() if not self._plot_valid: # it is the responsibility of _setup_plots to # call plot.run_callbacks() self._setup_plots() retv = f(self, *args, **kwargs) return retv return newfunc T = TypeVar("T", tuple, list) def _swap_axes_extents(extent: T) -> T: """ swaps the x and y extent values, preserving type of extent Parameters ---------- extent : sequence of four unyt quantities the current 4-element tuple or list of unyt quantities describing the plot extent. extent = (xmin, xmax, ymin, ymax). Returns ------- tuple or list the extent axes swapped, now with (ymin, ymax, xmin, xmax). """ extent_swapped = [extent[2], extent[3], extent[0], extent[1]] return type(extent)(extent_swapped) def _swap_arg_pair_order(*args): """ flips adjacent argument pairs, useful for swapping x-y plot arguments Parameters ---------- *args argument pairs, must have an even number of *args Returns ------- tuple args with order of pairs switched, i.e,: _swap_arg_pair_order(x, y, px, py) returns: y, x, py, px """ if len(args) % 2 != 0: raise TypeError("Number of arguments must be even.") n_pairs = len(args) // 2 new_args = [] for i in range(n_pairs): x_id = i * 2 new_args.append(args[x_id + 1]) new_args.append(args[x_id]) return tuple(new_args) class _MPL38_SymmetricalLogLocator(SymmetricalLogLocator): # Backporting behaviour from matplotlib 3.8 (in development at the time of writing) # see https://github.com/matplotlib/matplotlib/pull/25970 def __init__(self, *args, **kwargs): if mpl.__version_info__ >= (3, 8): raise RuntimeError( "_MPL38_SymmetricalLogLocator is not needed with matplotlib>=3.8" ) super().__init__(*args, **kwargs) def tick_values(self, vmin, vmax): linthresh = self._linthresh if vmax < vmin: vmin, vmax = vmax, vmin if -linthresh <= vmin < vmax <= linthresh: # only the linear range is present return sorted({vmin, 0, vmax}) return super().tick_values(vmin, vmax) def get_default_from_config(data_source, *, field, keys, defaults): _keys = list(always_iterable(keys)) _defaults = list(always_iterable(defaults)) ftype, fname = data_source._determine_fields(field)[0] ret = [ ytcfg.get_most_specific("plot", ftype, fname, key, fallback=default) for key, default in zip(_keys, _defaults, strict=True) ] if len(ret) == 1: return ret[0] else: return ret def _get_units_label(units: str) -> str: if r"\frac" in units: return rf"$\ \ \left({units}\right)$" elif units: return rf"$\ \ ({units})$" else: return "" yt-project-yt-f043ac8/yt/visualization/_handlers.py000066400000000000000000000404361510711153200225270ustar00rootroot00000000000000import weakref from numbers import Real from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, Union import matplotlib as mpl import numpy as np import unyt as un from matplotlib.colors import Colormap, LogNorm, Normalize, SymLogNorm from unyt import unyt_quantity from yt._typing import Quantity, Unit from yt.config import ytcfg from yt.funcs import get_brewer_cmap, is_sequence, mylog if TYPE_CHECKING: # RGBColorType, RGBAColorType and ColorType are backported from matplotlib 3.8.0 RGBColorType = tuple[float, float, float] | str RGBAColorType = Union[ # noqa: UP007 str, # "none" or "#RRGGBBAA"/"#RGBA" hex strings tuple[float, float, float, float], # 2 tuple (color, alpha) representations, not infinitely recursive # RGBColorType includes the (str, float) tuple, even for RGBA strings tuple[RGBColorType, float], # (4-tuple, float) is odd, but accepted as the outer float overriding A of 4-tuple tuple[tuple[float, float, float, float], float], ] ColorType = RGBColorType | RGBAColorType # this type alias is unique to the present module ColormapInput: TypeAlias = Colormap | str | None class NormHandler: """ A bookkeeper class that can hold a fully defined norm object, or dynamically build one on demand according to a set of constraints. If a fully defined norm object is added, any existing constraints are dropped, and vice versa. These rules are implemented with properties and watcher patterns. It also keeps track of display units so that vmin, vmax and linthresh can be updated with implicit units. """ # using slots here to minimize the risk of introducing bugs # since attributes names are essential to this class's implementation __slots__ = ( "data_source", "ds", "_display_units", "_vmin", "_vmax", "_dynamic_range", "_norm_type", "_linthresh", "_norm_type", "_norm", "prefer_log", ) _constraint_attrs: list[str] = [ "vmin", "vmax", "dynamic_range", "norm_type", "linthresh", ] def __init__( self, data_source, *, display_units: un.Unit, vmin: un.unyt_quantity | None = None, vmax: un.unyt_quantity | None = None, dynamic_range: float | None = None, norm_type: type[Normalize] | None = None, norm: Normalize | None = None, linthresh: float | None = None, ): self.data_source = weakref.proxy(data_source) self.ds = data_source.ds # should already be a weakref proxy self._display_units = display_units self._norm = norm self._vmin = vmin self._vmax = vmax self._dynamic_range = dynamic_range self._norm_type = norm_type self._linthresh = linthresh self.prefer_log = True if self.norm is not None and self.has_constraints: raise TypeError( "NormHandler input is malformed. " "A norm cannot be passed along other constraints." ) def _get_constraints(self) -> dict[str, Any]: return { attr: getattr(self, attr) for attr in self.__class__._constraint_attrs if getattr(self, attr) is not None } @property def has_constraints(self) -> bool: return bool(self._get_constraints()) def _reset_constraints(self) -> None: constraints = self._get_constraints() if not constraints: return msg = ", ".join([f"{name}={value}" for name, value in constraints.items()]) mylog.warning("Dropping norm constraints (%s)", msg) for name in constraints.keys(): setattr(self, name, None) def _reset_norm(self) -> None: if self.norm is None: return mylog.warning("Dropping norm (%s)", self.norm) self._norm = None def to_float(self, val: un.unyt_quantity) -> float: return float(val.to(self.display_units).d) def to_quan(self, val) -> un.unyt_quantity: if isinstance(val, un.unyt_quantity): return self.ds.quan(val) elif ( is_sequence(val) and len(val) == 2 and isinstance(val[0], Real) and isinstance(val[1], (str, un.Unit)) ): return self.ds.quan(*val) elif isinstance(val, Real): return self.ds.quan(val, self.display_units) else: raise TypeError(f"Could not convert {val!r} to unyt_quantity") @property def display_units(self) -> un.Unit: return self._display_units @display_units.setter def display_units(self, newval: Unit) -> None: self._display_units = un.Unit(newval, registry=self.ds.unit_registry) def _set_quan_attr(self, attr: str, newval: Quantity | float | None) -> None: if newval is None: setattr(self, attr, None) else: try: quan = self.to_quan(newval) except TypeError as exc: raise TypeError( "Expected None, a float, or a unyt_quantity, " f"received {newval} with type {type(newval)}" ) from exc else: setattr(self, attr, quan) @property def vmin(self) -> un.unyt_quantity | Literal["min"] | None: return self._vmin @vmin.setter def vmin(self, newval: Quantity | float | Literal["min"] | None) -> None: self._reset_norm() if newval == "min": self._vmin = "min" else: self._set_quan_attr("_vmin", newval) @property def vmax(self) -> un.unyt_quantity | Literal["max"] | None: return self._vmax @vmax.setter def vmax(self, newval: Quantity | float | Literal["max"] | None) -> None: self._reset_norm() if newval == "max": self._vmax = "max" else: self._set_quan_attr("_vmax", newval) @property def dynamic_range(self) -> float | None: return self._dynamic_range @dynamic_range.setter def dynamic_range(self, newval: float | None) -> None: if newval is None: return try: newval = float(newval) except TypeError: raise TypeError( f"Expected a float. Received {newval} with type {type(newval)}" ) from None if newval == 0: raise ValueError("Dynamic range cannot be zero.") if newval == 1: raise ValueError("Dynamic range cannot be unity.") self._reset_norm() self._dynamic_range = newval def get_dynamic_range( self, dvmin: float | None, dvmax: float | None ) -> tuple[float, float]: if self.dynamic_range is None: raise RuntimeError( "Something went terribly wrong in setting up a dynamic range" ) if self.vmax is None: if self.vmin is None: raise TypeError( "Cannot set dynamic range with neither " "vmin and vmax being constrained." ) if dvmin is None: raise RuntimeError( "Something went terribly wrong in setting up a dynamic range" ) return dvmin, dvmin * self.dynamic_range elif self.vmin is None: if dvmax is None: raise RuntimeError( "Something went terribly wrong in setting up a dynamic range" ) return dvmax / self.dynamic_range, dvmax else: raise TypeError( "Cannot set dynamic range with both " "vmin and vmax already constrained." ) @property def norm_type(self) -> type[Normalize] | None: return self._norm_type @norm_type.setter def norm_type(self, newval: type[Normalize] | None) -> None: if not ( newval is None or (isinstance(newval, type) and issubclass(newval, Normalize)) ): raise TypeError( "Expected a subclass of matplotlib.colors.Normalize, " f"received {newval} with type {type(newval)}" ) self._reset_norm() if newval is not SymLogNorm: self.linthresh = None self._norm_type = newval @property def norm(self) -> Normalize | None: return self._norm @norm.setter def norm(self, newval: Normalize) -> None: if not isinstance(newval, Normalize): raise TypeError( "Expected a matplotlib.colors.Normalize object, " f"received {newval} with type {type(newval)}" ) self._reset_constraints() self._norm = newval @property def linthresh(self) -> float | None: return self._linthresh @linthresh.setter def linthresh(self, newval: Quantity | float | None) -> None: self._reset_norm() self._set_quan_attr("_linthresh", newval) if self._linthresh is not None and self._linthresh <= 0: raise ValueError( f"linthresh can only be set to strictly positive values, got {newval}" ) if newval is not None: self.norm_type = SymLogNorm def get_norm(self, data: np.ndarray, *args, **kw) -> Normalize: if self.norm is not None: return self.norm dvmin = dvmax = None finite_values_mask = np.isfinite(data) if self.vmin is not None and not ( isinstance(self.vmin, str) and self.vmin == "min" ): dvmin = self.to_float(self.vmin) elif np.any(finite_values_mask): dvmin = self.to_float(np.nanmin(data[finite_values_mask])) if self.vmax is not None and not ( isinstance(self.vmax, str) and self.vmax == "max" ): dvmax = self.to_float(self.vmax) elif np.any(finite_values_mask): dvmax = self.to_float(np.nanmax(data[finite_values_mask])) if self.dynamic_range is not None: dvmin, dvmax = self.get_dynamic_range(dvmin, dvmax) if dvmin is None: dvmin = 1 * getattr(data, "units", 1) kw.setdefault("vmin", dvmin) if dvmax is None: dvmax = 1 * getattr(data, "units", 1) kw.setdefault("vmax", dvmax) norm_type: type[Normalize] if data.ndim == 3: assert data.shape[-1] == 4 # this is an RGBA array, only linear normalization makes sense here norm_type = Normalize elif self.norm_type is not None: # this is a convenience mechanism for backward compat, # allowing to toggle between lin and log scaling without detailed user input norm_type = self.norm_type else: if ( not self.prefer_log or kw["vmin"] == kw["vmax"] or not np.any(finite_values_mask) ): norm_type = Normalize elif kw["vmin"] <= 0: # note: see issue 3944 (and PRs and issues linked therein) for a # discussion on when to switch to SymLog and related questions # of how to calculate a default linthresh value. norm_type = SymLogNorm else: norm_type = LogNorm if norm_type is SymLogNorm: if self.linthresh is not None: linthresh = self.to_float(self.linthresh) else: linthresh = self._guess_linthresh(data[finite_values_mask]) kw.setdefault("linthresh", linthresh) kw.setdefault("base", 10) return norm_type(*args, **kw) def _guess_linthresh(self, finite_plot_data): # finite_plot_data is the ImageArray or ColorbarHandler data, already # filtered to be finite values # get the extrema for the negative and positive values separately # neg_min -> neg_max -> 0 -> pos_min -> pos_max def get_minmax(data): if len(data) > 0: return np.nanmin(data), np.nanmax(data) return None, None pos_min, pos_max = get_minmax(finite_plot_data[finite_plot_data > 0]) neg_min, neg_max = get_minmax(finite_plot_data[finite_plot_data < 0]) has_pos = pos_min is not None has_neg = neg_min is not None # the starting guess is the absolute value of the point closest to 0 # (remember: neg_max is closer to 0 than neg_min) if has_pos and has_neg: linthresh = np.min((-neg_max, pos_min)) elif has_pos: linthresh = pos_min elif has_neg: linthresh = -neg_max else: # this condition should be handled before here in get_norm raise RuntimeError("No finite data points.") log10_linthresh = np.log10(linthresh) # if either the pos or neg ranges exceed cutoff_sigdigs, then # linthresh is shifted to decrease the range to avoid floating point # precision errors in the normalization. cutoff_sigdigs = 15 # max allowable range in significant digits if has_pos and np.log10(pos_max) - log10_linthresh > cutoff_sigdigs: linthresh = pos_max / (10.0**cutoff_sigdigs) log10_linthresh = np.log10(linthresh) if has_neg and np.log10(-neg_min) - log10_linthresh > cutoff_sigdigs: linthresh = np.abs(neg_min) / (10.0**cutoff_sigdigs) if isinstance(linthresh, unyt_quantity): # if the original plot_data has units, linthresh will have units here return self.to_float(linthresh) return linthresh class ColorbarHandler: __slots__ = ("_draw_cbar", "_draw_minorticks", "_cmap", "_background_color") def __init__( self, *, draw_cbar: bool = True, draw_minorticks: bool = True, cmap: "ColormapInput" = None, background_color: str | None = None, ): self._draw_cbar = draw_cbar self._draw_minorticks = draw_minorticks self._cmap: Colormap | None = None self._set_cmap(cmap) self._background_color: ColorType | None = background_color @property def draw_cbar(self) -> bool: return self._draw_cbar @draw_cbar.setter def draw_cbar(self, newval) -> None: if not isinstance(newval, bool): raise TypeError( f"Expected a boolean, got {newval} with type {type(newval)}" ) self._draw_cbar = newval @property def draw_minorticks(self) -> bool: return self._draw_minorticks @draw_minorticks.setter def draw_minorticks(self, newval) -> None: if not isinstance(newval, bool): raise TypeError( f"Expected a boolean, got {newval} with type {type(newval)}" ) self._draw_minorticks = newval @property def cmap(self) -> Colormap: return self._cmap or mpl.colormaps[ytcfg.get("yt", "default_colormap")] @cmap.setter def cmap(self, newval: "ColormapInput") -> None: self._set_cmap(newval) def _set_cmap(self, newval: "ColormapInput") -> None: # a separate setter function is better supported by type checkers (mypy) # than relying purely on a property setter to narrow type # from ColormapInput to Colormap if isinstance(newval, Colormap) or newval is None: self._cmap = newval elif isinstance(newval, str): self._cmap = mpl.colormaps[newval] elif is_sequence(newval): # type: ignore[unreachable] # tuple colormaps are from palettable (or brewer2mpl) self._cmap = get_brewer_cmap(newval) else: raise TypeError( "Expected a colormap object or name, " f"got {newval} with type {type(newval)}" ) @property def background_color(self) -> "ColorType": return self._background_color or "white" @background_color.setter def background_color(self, newval: Optional["ColorType"]) -> None: if newval is None: self._background_color = self.cmap(0) else: self._background_color = newval @property def has_background_color(self) -> bool: return self._background_color is not None yt-project-yt-f043ac8/yt/visualization/api.py000066400000000000000000000020071510711153200213310ustar00rootroot00000000000000from .base_plot_types import get_multi_plot from .color_maps import add_colormap, make_colormap, show_colormaps from .fits_image import ( FITSImageData, FITSOffAxisProjection, FITSOffAxisSlice, FITSParticleOffAxisProjection, FITSParticleProjection, FITSProjection, FITSSlice, ) from .fixed_resolution import FixedResolutionBuffer, ParticleImageBuffer from .image_writer import ( apply_colormap, map_to_colors, multi_image_composite, scale_image, splat_points, write_bitmap, write_image, write_projection, ) from .line_plot import LineBuffer, LinePlot from .particle_plots import ParticlePhasePlot, ParticlePlot, ParticleProjectionPlot from .plot_modifications import PlotCallback, callback_registry from .plot_window import ( AxisAlignedProjectionPlot, AxisAlignedSlicePlot, OffAxisProjectionPlot, OffAxisSlicePlot, ProjectionPlot, SlicePlot, plot_2d, ) from .profile_plotter import PhasePlot, ProfilePlot from .streamlines import Streamlines yt-project-yt-f043ac8/yt/visualization/base_plot_types.py000066400000000000000000000560561510711153200237710ustar00rootroot00000000000000import sys import warnings from abc import ABC from io import BytesIO from typing import TYPE_CHECKING, Optional, TypedDict import matplotlib import numpy as np from matplotlib.scale import SymmetricalLogTransform from matplotlib.ticker import LogFormatterMathtext from yt._typing import AlphaT from yt.funcs import ( get_interactivity, is_sequence, matplotlib_style_context, mylog, setdefault_mpl_metadata, setdefaultattr, ) from yt.visualization._handlers import ColorbarHandler, NormHandler from ._commons import ( get_canvas, validate_image_name, ) if matplotlib.__version_info__ >= (3, 8): from matplotlib.ticker import SymmetricalLogLocator else: from ._commons import _MPL38_SymmetricalLogLocator as SymmetricalLogLocator if TYPE_CHECKING: from typing import Literal from matplotlib.axes import Axes from matplotlib.axis import Axis from matplotlib.figure import Figure from matplotlib.transforms import Transform class FormatKwargs(TypedDict): style: Literal["scientific"] scilimits: tuple[int, int] useMathText: bool BACKEND_SPECS = { "gtk": ["backend_gtk", "FigureCanvasGTK", "FigureManagerGTK"], "gtkagg": ["backend_gtkagg", "FigureCanvasGTKAgg", None], "gtkcairo": ["backend_gtkcairo", "FigureCanvasGTKCairo", None], "macosx": ["backend_macosx", "FigureCanvasMac", "FigureManagerMac"], "qt5agg": ["backend_qt5agg", "FigureCanvasQTAgg", None], "qtagg": ["backend_qtagg", "FigureCanvasQTAgg", None], "tkagg": ["backend_tkagg", "FigureCanvasTkAgg", None], "wx": ["backend_wx", "FigureCanvasWx", None], "wxagg": ["backend_wxagg", "FigureCanvasWxAgg", None], "gtk3cairo": [ "backend_gtk3cairo", "FigureCanvasGTK3Cairo", "FigureManagerGTK3Cairo", ], "gtk3agg": ["backend_gtk3agg", "FigureCanvasGTK3Agg", "FigureManagerGTK3Agg"], "webagg": ["backend_webagg", "FigureCanvasWebAgg", None], "nbagg": ["backend_nbagg", "FigureCanvasNbAgg", "FigureManagerNbAgg"], "agg": ["backend_agg", "FigureCanvasAgg", None], } class CallbackWrapper: def __init__(self, viewer, window_plot, frb, field, font_properties, font_color): self.frb = frb self.data = frb.data_source self._axes = window_plot.axes self._figure = window_plot.figure if len(self._axes.images) > 0: self.raw_image_shape = self._axes.images[0]._A.shape if viewer._has_swapped_axes: # store the original un-transposed shape self.raw_image_shape = self.raw_image_shape[1], self.raw_image_shape[0] if frb.axis is not None: DD = frb.ds.domain_width xax = frb.ds.coordinates.x_axis[frb.axis] yax = frb.ds.coordinates.y_axis[frb.axis] self._period = (DD[xax], DD[yax]) self.ds = frb.ds self.xlim = viewer.xlim self.ylim = viewer.ylim self._swap_axes = viewer._has_swapped_axes self._flip_horizontal = viewer._flip_horizontal # needed for quiver self._flip_vertical = viewer._flip_vertical # needed for quiver # an important note on _swap_axes: _swap_axes will swap x,y arguments # in callbacks (e.g., plt.plot(x,y) will be plt.plot(y, x). The xlim # and ylim arguments above, and internal callback references to coordinates # are the **unswapped** ranges. self._axes_unit_names = viewer._axes_unit_names if "OffAxisSlice" in viewer._plot_type: self._type_name = "CuttingPlane" else: self._type_name = viewer._plot_type self.aspect = window_plot._aspect self.font_properties = font_properties self.font_color = font_color self.field = field self._transform = viewer._transform class PlotMPL: """A base class for all yt plots made using matplotlib, that is backend independent.""" def __init__( self, fsize, axrect: tuple[float, float, float, float], *, norm_handler: NormHandler, figure: Optional["Figure"] = None, axes: Optional["Axes"] = None, ): """Initialize PlotMPL class""" import matplotlib.figure self._plot_valid = True if figure is None: if not is_sequence(fsize): fsize = (fsize, fsize) self.figure = matplotlib.figure.Figure(figsize=fsize, frameon=True) else: figure.set_size_inches(fsize) self.figure = figure if axes is None: self._create_axes(axrect) else: axes.clear() axes.set_position(axrect) self.axes = axes self.interactivity = get_interactivity() figure_canvas, figure_manager = self._get_canvas_classes() self.canvas = figure_canvas(self.figure) if figure_manager is not None: self.manager = figure_manager(self.canvas, 1) self.axes.tick_params( which="both", axis="both", direction="in", top=True, right=True ) self.norm_handler = norm_handler def _create_axes(self, axrect: tuple[float, float, float, float]) -> None: self.axes = self.figure.add_axes(axrect) def _get_canvas_classes(self): if self.interactivity: key = str(matplotlib.get_backend()).lower() else: key = "agg" module, fig_canvas, fig_manager = BACKEND_SPECS[key] mod = __import__( "matplotlib.backends", globals(), locals(), [module], 0, ) submod = getattr(mod, module) FigureCanvas = getattr(submod, fig_canvas) if fig_manager is not None: FigureManager = getattr(submod, fig_manager) return FigureCanvas, FigureManager return FigureCanvas, None def save(self, name, mpl_kwargs=None, canvas=None): """Choose backend and save image to disk""" if mpl_kwargs is None: mpl_kwargs = {} name = validate_image_name(name) setdefault_mpl_metadata(mpl_kwargs, name) try: canvas = get_canvas(self.figure, name) except ValueError: canvas = self.canvas mylog.info("Saving plot %s", name) with matplotlib_style_context(): canvas.print_figure(name, **mpl_kwargs) return name def show(self): try: self.manager.show() except AttributeError: self.canvas.show() def _get_labels(self): ax = self.axes labels = ax.xaxis.get_ticklabels() + ax.yaxis.get_ticklabels() labels += ax.xaxis.get_minorticklabels() labels += ax.yaxis.get_minorticklabels() labels += [ ax.title, ax.xaxis.label, ax.yaxis.label, ax.xaxis.get_offset_text(), ax.yaxis.get_offset_text(), ] return labels def _set_font_properties(self, font_properties, font_color): for label in self._get_labels(): label.set_fontproperties(font_properties) if font_color is not None: label.set_color(font_color) def _repr_png_(self): from matplotlib.backends.backend_agg import FigureCanvasAgg canvas = FigureCanvasAgg(self.figure) f = BytesIO() with matplotlib_style_context(): canvas.print_figure(f) f.seek(0) return f.read() class ImagePlotMPL(PlotMPL, ABC): """A base class for yt plots made using imshow""" _default_font_size = 18.0 def __init__( self, fsize=None, axrect=None, caxrect=None, *, norm_handler: NormHandler, colorbar_handler: ColorbarHandler, figure: Optional["Figure"] = None, axes: Optional["Axes"] = None, cax: Optional["Axes"] = None, ): """Initialize ImagePlotMPL class object""" self._transform: Transform | None setdefaultattr(self, "_transform", None) self.colorbar_handler = colorbar_handler _missing_layout_specs = [_ is None for _ in (fsize, axrect, caxrect)] if all(_missing_layout_specs): fsize, axrect, caxrect = self._get_best_layout() elif any(_missing_layout_specs): raise TypeError( "ImagePlotMPL cannot be initialized with partially specified layout." ) super().__init__( fsize, axrect, norm_handler=norm_handler, figure=figure, axes=axes ) if cax is None: self.cax = self.figure.add_axes(caxrect) else: cax.clear() cax.set_position(caxrect) self.cax = cax def _setup_layout_constraints( self, figure_size: tuple[float, float] | float, fontsize: float ): # Setup base layout attributes # derived classes need to call this before super().__init__ # but they are free to do other stuff in between if isinstance(figure_size, tuple): assert len(figure_size) == 2 assert all(isinstance(_, float) for _ in figure_size) self._figure_size = figure_size else: assert isinstance(figure_size, float) self._figure_size = (figure_size, figure_size) self._draw_axes = True fontscale = float(fontsize) / self.__class__._default_font_size if fontscale < 1.0: fontscale = np.sqrt(fontscale) self._cb_size = 0.0375 * self._figure_size[0] self._ax_text_size = [1.2 * fontscale, 0.9 * fontscale] self._top_buff_size = 0.30 * fontscale self._aspect = 1.0 def _reset_layout(self) -> None: size, axrect, caxrect = self._get_best_layout() self.axes.set_position(axrect) self.cax.set_position(caxrect) self.figure.set_size_inches(*size) def _init_image(self, data, extent, aspect, *, alpha: AlphaT = None): """Store output of imshow in image variable""" norm = self.norm_handler.get_norm(data) extent = [float(e) for e in extent] if self._transform is None: # sets the transform to be an ax.TransData object, where the # coordinate system of the data is controlled by the xlim and ylim # of the data. transform = self.axes.transData else: transform = self._transform self._validate_axes_extent(extent, transform) self.image = self.axes.imshow( data.to_ndarray(), origin="lower", extent=extent, norm=norm, aspect=aspect, cmap=self.colorbar_handler.cmap, interpolation="nearest", interpolation_stage="data", transform=transform, alpha=alpha, ) self._set_axes() def _set_axes(self) -> None: fmt_kwargs: FormatKwargs = { "style": "scientific", "scilimits": (-2, 3), "useMathText": True, } self.image.axes.ticklabel_format(**fmt_kwargs) self.image.axes.set_facecolor(self.colorbar_handler.background_color) self.cax.tick_params(which="both", direction="in") # For creating a multipanel plot by ImageGrid # we may need the location keyword, which requires Matplotlib >= 3.7.0 cb_location = getattr(self.cax, "orientation", None) if matplotlib.__version_info__ >= (3, 7): self.cb = self.figure.colorbar(self.image, self.cax, location=cb_location) else: if cb_location in ["top", "bottom"]: warnings.warn( "Cannot properly set the orientation of colorbar. " "Consider upgrading matplotlib to version 3.7 or newer", stacklevel=6, ) self.cb = self.figure.colorbar(self.image, self.cax) cb_axis: Axis if self.cb.orientation == "vertical": cb_axis = self.cb.ax.yaxis else: cb_axis = self.cb.ax.xaxis cb_scale = cb_axis.get_scale() if cb_scale == "symlog": trf = cb_axis.get_transform() if not isinstance(trf, SymmetricalLogTransform): raise RuntimeError cb_axis.set_major_locator(SymmetricalLogLocator(trf)) cb_axis.set_major_formatter( LogFormatterMathtext(linthresh=trf.linthresh, base=trf.base) ) if cb_scale not in ("log", "symlog"): self.cb.ax.ticklabel_format(**fmt_kwargs) if self.colorbar_handler.draw_minorticks and cb_scale == "symlog": # no minor ticks are drawn by default in symlog, as of matplotlib 3.7.1 # see https://github.com/matplotlib/matplotlib/issues/25994 trf = cb_axis.get_transform() if not isinstance(trf, SymmetricalLogTransform): raise RuntimeError if float(trf.base).is_integer(): locator = SymmetricalLogLocator(trf, subs=list(range(1, int(trf.base)))) cb_axis.set_minor_locator(locator) elif self.colorbar_handler.draw_minorticks: self.cb.minorticks_on() else: self.cb.minorticks_off() def _validate_axes_extent(self, extent, transform): # if the axes are cartopy GeoAxes, this checks that the axes extent # is properly set. if "cartopy" not in sys.modules: # cartopy isn't already loaded, nothing to do here return from cartopy.mpl.geoaxes import GeoAxes if isinstance(self.axes, GeoAxes): # some projections have trouble when passing extents at or near the # limits. So we only set_extent when the plot is a subset of the # globe, within the tolerance of the transform. # note that `set_extent` here is setting the extent of the axes. # still need to pass the extent arg to imshow in order to # ensure that it is properly scaled. also note that set_extent # expects values in the coordinates of the transform: it will # calculate the coordinates in the projection. global_extent = transform.x_limits + transform.y_limits thresh = transform.threshold if all( abs(extent[ie]) < (abs(global_extent[ie]) - thresh) for ie in range(4) ): self.axes.set_extent(extent, crs=transform) def _get_best_layout(self): # this method is called in ImagePlotMPL.__init__ # required attributes # - self._figure_size: Union[float, Tuple[float, float]] # - self._aspect: float # - self._ax_text_size: Tuple[float, float] # - self._draw_axes: bool # - self.colorbar_handler: ColorbarHandler # optional attributes # - self._unit_aspect: float # Ensure the figure size along the long axis is always equal to _figure_size unit_aspect = getattr(self, "_unit_aspect", 1) if is_sequence(self._figure_size): x_fig_size, y_fig_size = self._figure_size y_fig_size *= unit_aspect else: x_fig_size = y_fig_size = self._figure_size scaling = self._aspect / unit_aspect if scaling < 1: x_fig_size *= scaling else: y_fig_size /= scaling if self.colorbar_handler.draw_cbar: cb_size = self._cb_size cb_text_size = self._ax_text_size[1] + 0.45 else: cb_size = x_fig_size * 0.04 cb_text_size = 0.0 if self._draw_axes: x_axis_size = self._ax_text_size[0] y_axis_size = self._ax_text_size[1] else: x_axis_size = x_fig_size * 0.04 y_axis_size = y_fig_size * 0.04 top_buff_size = self._top_buff_size if not self._draw_axes and not self.colorbar_handler.draw_cbar: x_axis_size = 0.0 y_axis_size = 0.0 cb_size = 0.0 cb_text_size = 0.0 top_buff_size = 0.0 xbins = np.array([x_axis_size, x_fig_size, cb_size, cb_text_size]) ybins = np.array([y_axis_size, y_fig_size, top_buff_size]) size = [xbins.sum(), ybins.sum()] x_frac_widths = xbins / size[0] y_frac_widths = ybins / size[1] # axrect is the rectangle defining the area of the # axis object of the plot. Its range goes from 0 to 1 in # x and y directions. The first two values are the x,y # start values of the axis object (lower left corner), and the # second two values are the size of the axis object. To get # the upper right corner, add the first x,y to the second x,y. axrect = ( x_frac_widths[0], y_frac_widths[0], x_frac_widths[1], y_frac_widths[1], ) # caxrect is the rectangle defining the area of the colorbar # axis object of the plot. It is defined just as the axrect # tuple is. caxrect = ( x_frac_widths[0] + x_frac_widths[1], y_frac_widths[0], x_frac_widths[2], y_frac_widths[1], ) return size, axrect, caxrect def _toggle_axes(self, choice, draw_frame=None): """ Turn on/off displaying the axis ticks and labels for a plot. Parameters ---------- choice : boolean If True, set the axes to be drawn. If False, set the axes to not be drawn. """ self._draw_axes = choice self._draw_frame = draw_frame if draw_frame is None: draw_frame = choice if self.colorbar_handler.has_background_color and not draw_frame: # workaround matplotlib's behaviour # last checked with Matplotlib 3.5 warnings.warn( f"Previously set background color {self.colorbar_handler.background_color} " "has no effect. Pass `draw_frame=True` if you wish to preserve background color.", stacklevel=4, ) self.axes.set_frame_on(draw_frame) self.axes.get_xaxis().set_visible(choice) self.axes.get_yaxis().set_visible(choice) self._reset_layout() def _toggle_colorbar(self, choice: bool): """ Turn on/off displaying the colorbar for a plot choice = True or False """ self.colorbar_handler.draw_cbar = choice self.cax.set_visible(choice) size, axrect, caxrect = self._get_best_layout() self.axes.set_position(axrect) self.cax.set_position(caxrect) self.figure.set_size_inches(*size) def _get_labels(self): labels = super()._get_labels() if getattr(self.cb, "orientation", "vertical") == "horizontal": cbaxis = self.cb.ax.xaxis else: cbaxis = self.cb.ax.yaxis labels += cbaxis.get_ticklabels() labels += [cbaxis.label, cbaxis.get_offset_text()] return labels def hide_axes(self, *, draw_frame=None): """ Hide the axes for a plot including ticks and labels """ self._toggle_axes(False, draw_frame) return self def show_axes(self): """ Show the axes for a plot including ticks and labels """ self._toggle_axes(True) return self def hide_colorbar(self): """ Hide the colorbar for a plot including ticks and labels """ self._toggle_colorbar(False) return self def show_colorbar(self): """ Show the colorbar for a plot including ticks and labels """ self._toggle_colorbar(True) return self def get_multi_plot(nx, ny, colorbar="vertical", bw=4, dpi=300, cbar_padding=0.4): r"""Construct a multiple axes plot object, with or without a colorbar, into which multiple plots may be inserted. This will create a set of :class:`matplotlib.axes.Axes`, all lined up into a grid, which are then returned to the user and which can be used to plot multiple plots on a single figure. Parameters ---------- nx : int Number of axes to create along the x-direction ny : int Number of axes to create along the y-direction colorbar : {'vertical', 'horizontal', None}, optional Should Axes objects for colorbars be allocated, and if so, should they correspond to the horizontal or vertical set of axes? bw : number The base height/width of an axes object inside the figure, in inches dpi : number The dots per inch fed into the Figure instantiation Returns ------- fig : :class:`matplotlib.figure.Figure` The figure created inside which the axes reside tr : list of list of :class:`matplotlib.axes.Axes` objects This is a list, where the inner list is along the x-axis and the outer is along the y-axis cbars : list of :class:`matplotlib.axes.Axes` objects Each of these is an axes onto which a colorbar can be placed. Notes ----- This is a simple implementation for a common use case. Viewing the source can be instructive, and is encouraged to see how to generate more complicated or more specific sets of multiplots for your own purposes. """ import matplotlib.figure from matplotlib.backends.backend_agg import FigureCanvasAgg hf, wf = 1.0 / ny, 1.0 / nx fudge_x = fudge_y = 1.0 if colorbar is None: fudge_x = fudge_y = 1.0 elif colorbar.lower() == "vertical": fudge_x = nx / (cbar_padding + nx) fudge_y = 1.0 elif colorbar.lower() == "horizontal": fudge_x = 1.0 fudge_y = ny / (cbar_padding + ny) fig = matplotlib.figure.Figure((bw * nx / fudge_x, bw * ny / fudge_y), dpi=dpi) fig.set_canvas(FigureCanvasAgg(fig)) fig.subplots_adjust( wspace=0.0, hspace=0.0, top=1.0, bottom=0.0, left=0.0, right=1.0 ) tr = [] for j in range(ny): tr.append([]) for i in range(nx): left = i * wf * fudge_x bottom = fudge_y * (1.0 - (j + 1) * hf) + (1.0 - fudge_y) ax = fig.add_axes([left, bottom, wf * fudge_x, hf * fudge_y]) tr[-1].append(ax) cbars = [] if colorbar is None: pass elif colorbar.lower() == "horizontal": for i in range(nx): # left, bottom, width, height # Here we want 0.10 on each side of the colorbar # We want it to be 0.05 tall # And we want a buffer of 0.15 ax = fig.add_axes( [ wf * (i + 0.10) * fudge_x, hf * fudge_y * 0.20, wf * (1 - 0.20) * fudge_x, hf * fudge_y * 0.05, ] ) cbars.append(ax) elif colorbar.lower() == "vertical": for j in range(ny): ax = fig.add_axes( [ wf * (nx + 0.05) * fudge_x, hf * fudge_y * (ny - (j + 0.95)), wf * fudge_x * 0.05, hf * fudge_y * 0.90, ] ) ax.clear() cbars.append(ax) return fig, tr, cbars yt-project-yt-f043ac8/yt/visualization/color_maps.py000066400000000000000000000314721510711153200227260ustar00rootroot00000000000000import cmyt # noqa: F401 import matplotlib as mpl import numpy as np from matplotlib.colors import LinearSegmentedColormap from yt.funcs import get_brewer_cmap from yt.utilities.logger import ytLogger as mylog from . import _colormap_data as _cm yt_colormaps = {} def add_colormap(name, cdict): """ Adds a colormap to the colormaps available in yt for this session """ # Note: this function modifies the global variable 'yt_colormaps' yt_colormaps[name] = LinearSegmentedColormap(name, cdict, 256) mpl.colormaps.register(yt_colormaps[name]) # YTEP-0040 backward compatibility layer # yt colormaps used to be defined here, but were migrated to an external # package, cmyt. In the process, 5 of them were renamed. We register them again here # under their historical names to preserves backwards compatibility. _HISTORICAL_ALIASES = { "arbre": "cmyt.arbre", "algae": "cmyt.algae", "bds_highcontrast": "cmyt.algae", "octarine": "cmyt.octarine", "dusk": "cmyt.dusk", "kamae": "cmyt.pastel", "kelp": "cmyt.kelp", "black_blueish": "cmyt.pixel_blue", "black_green": "cmyt.pixel_green", "purple_mm": "cmyt.xray", } def register_yt_colormaps_from_cmyt(): """ For backwards compatibility, register yt colormaps without the "cmyt." prefix, but do it in a collision-safe way. """ for hist_name, alias in _HISTORICAL_ALIASES.items(): # note that mpl.colormaps.__getitem__ returns *copies* cmap = mpl.colormaps[alias] cmap.name = hist_name mpl.colormaps.register(cmap) mpl.colormaps.register(cmap.reversed()) register_yt_colormaps_from_cmyt() # Add colormaps in _colormap_data.py that weren't defined here _vs = np.linspace(0, 1, 256) for k, v in list(_cm.color_map_luts.items()): if k in yt_colormaps: continue cdict = { "red": np.transpose([_vs, v[0], v[0]]), "green": np.transpose([_vs, v[1], v[1]]), "blue": np.transpose([_vs, v[2], v[2]]), } try: add_colormap(k, cdict) except ValueError: # expected if another map with identical name was already registered mylog.warning("cannot register colormap '%s' (naming collision)", k) def get_colormap_lut(cmap_id: tuple[str, str] | str): # "lut" stands for "lookup table". This function provides a consistent and # reusable accessor to a hidden (and by default, uninitialized) attribute # (`_lut`) in registered colormaps, from matplotlib or palettable. # colormap "lookup tables" are RGBA arrays in matplotlib, # and contain sufficient data to reconstruct the colormaps entirely. # This exists mostly for historical reasons, hence the custom output format. # It isn't meant as part of yt's public api. if isinstance(cmap_id, tuple) and len(cmap_id) == 2: cmap = get_brewer_cmap(cmap_id) elif isinstance(cmap_id, str): cmap = mpl.colormaps[cmap_id] else: raise TypeError( "Expected a string or a 2-tuple of strings as a colormap id. " f"Received: {cmap_id}" ) if not cmap._isinit: cmap._init() r = cmap._lut[:-3, 0] g = cmap._lut[:-3, 1] b = cmap._lut[:-3, 2] a = np.ones(b.shape) return [r, g, b, a] def show_colormaps(subset="all", filename=None): """ Displays the colormaps available to yt. Note, most functions can use both the matplotlib and the native yt colormaps; however, there are some special functions existing within image_writer.py (e.g. write_image() write_bitmap(), etc.), which cannot access the matplotlib colormaps. In addition to the colormaps listed, one can access the reverse of each colormap by appending a "_r" to any map. If you wish to only see certain colormaps, include them in the cmap_list attribute. Parameters ---------- subset : string, or list of strings, optional valid values : "all", "yt_native", or list of cmap names default : "all" As mentioned above, a few functions can only access yt_native colormaps. To display only the yt_native colormaps, set this to "yt_native". If you wish to only see a few colormaps side by side, you can include them as a list of colormap names. Example: ['cmyt.algae', 'gist_stern', 'cmyt.kamae', 'nipy_spectral'] filename : string, opt default: None If filename is set, then it will save the colormaps to an output file. If it is not set, it will "show" the result interactively. """ from matplotlib import pyplot as plt a = np.outer(np.arange(0, 1, 0.01), np.ones(10)) if subset == "all": maps = [ m for m in plt.colormaps() if (not m.startswith("idl")) & (not m.endswith("_r")) ] elif subset == "yt_native": maps = [ m for m in _cm.color_map_luts if (not m.startswith("idl")) & (not m.endswith("_r")) ] else: try: maps = [m for m in plt.colormaps() if m in subset] if len(maps) == 0: raise AttributeError except AttributeError as e: raise AttributeError( "show_colormaps requires subset attribute " "to be 'all', 'yt_native', or a list of " "valid colormap names." ) from e maps = sorted(set(maps)) # scale the image size by the number of cmaps plt.figure(figsize=(2.0 * len(maps) / 10.0, 6)) plt.subplots_adjust(top=0.7, bottom=0.05, left=0.01, right=0.99) l = len(maps) + 1 for i, m in enumerate(maps): plt.subplot(1, l, i + 1) plt.axis("off") plt.imshow(a, aspect="auto", cmap=mpl.colormaps[m], origin="lower") plt.title(m, rotation=90, fontsize=10, verticalalignment="bottom") if filename is not None: plt.savefig(filename, dpi=100, facecolor="gray") else: plt.show() def make_colormap(ctuple_list, name=None, interpolate=True): """ This generates a custom colormap based on the colors and spacings you provide. Enter a ctuple_list, which consists of tuples of (color, spacing) to return a colormap appropriate for use in yt. If you specify a name, it will automatically be added to the current session as a valid colormap. Output colormap is in the format yt expects for adding a colormap to the current session: a dictionary with the appropriate RGB channels each consisting of a 256x3 array : First number is the number at which we are defining a color breakpoint Second number is the (0..1) number to interpolate to when coming *from below* Third number is the (0..1) number to interpolate to when coming *from above* Parameters ---------- ctuple_list: list of (color, float) tuples The ctuple_list consists of pairs of (color, interval) tuples identifying the colors to use in the colormap and the intervals they take to change to the next color in the list. A color can either be a string of the name of a color, or it can be an array of 3 floats, each representing the intensity of R, G, and B on a scale of 0 to 1. Valid color names and their equivalent arrays are listed below. Any interval can be given for the different color tuples, and the total of all the intervals will be scaled to the 256 output elements. If a ctuple_list ends with a color and a non-zero interval, a white 0-interval would be added to the end to finish the interpolation. To avoid finishing with white, specify your own zero-interval color at the end. name: string, optional If you wish this colormap to be added as a valid colormap to the current session, specify a name here. Default: None interpolate: boolean Designates whether or not the colormap will interpolate between the colors provided or just give solid colors across the intervals. Default: True Preset Color Options -------------------- 'white' : np.array([255, 255, 255 ])/255. 'gray' : np.array([130, 130, 130])/255. 'dgray' : np.array([80, 80, 80])/255. 'black' : np.array([0, 0, 0])/255. 'blue' : np.array([0, 0, 255])/255. 'dblue' : np.array([0, 0, 160])/255. 'purple' : np.array([100, 0, 200])/255. 'dpurple' : np.array([66, 0, 133])/255. 'dred' : np.array([160, 0, 0])/255. 'red' : np.array([255, 0, 0])/255. 'orange' : np.array([255, 128, 0])/255. 'dorange' : np.array([200,100, 0])/255. 'yellow' : np.array([255, 255, 0])/255. 'dyellow' : np.array([200, 200, 0])/255. 'green' : np.array([0, 255, 0])/255. 'dgreen' : np.array([0, 160, 0])/255. Examples -------- To obtain a colormap that starts at black with equal intervals in green, blue, red, yellow in that order and interpolation between those colors. (In reality, it starts at black, takes an interval of 10 to interpolate to green, then an interval of 10 to interpolate to blue, then an interval of 10 to interpolate to red.) >>> cm = make_colormap([("black", 10), ("green", 10), ("blue", 10), ("red", 0)]) To add a colormap that has five equal blocks of solid major colors to the current session as "steps": >>> make_colormap( ... [("red", 10), ("orange", 10), ("yellow", 10), ("green", 10), ("blue", 10)], ... name="steps", ... interpolate=False, ... ) To add a colormap that looks like the French flag (i.e. equal bands of blue, white, and red) using your own RGB keys, then to display it: >>> make_colormap( ... [([0, 0, 1], 10), ([1, 1, 1], 10), ([1, 0, 0], 10)], ... name="french_flag", ... interpolate=False, ... ) >>> show_colormaps(["french_flag"]) """ # aliases for different colors color_dict = { "white": np.array([255, 255, 255]) / 255.0, "gray": np.array([130, 130, 130]) / 255.0, "dgray": np.array([80, 80, 80]) / 255.0, "black": np.array([0, 0, 0]) / 255.0, "blue": np.array([0, 0, 255]) / 255.0, "dblue": np.array([0, 0, 160]) / 255.0, "purple": np.array([100, 0, 200]) / 255.0, "dpurple": np.array([66, 0, 133]) / 255.0, "dred": np.array([160, 0, 0]) / 255.0, "red": np.array([255, 0, 0]) / 255.0, "orange": np.array([255, 128, 0]) / 255.0, "dorange": np.array([200, 100, 0]) / 255.0, "yellow": np.array([255, 255, 0]) / 255.0, "dyellow": np.array([200, 200, 0]) / 255.0, "green": np.array([0, 255, 0]) / 255.0, "dgreen": np.array([0, 160, 0]) / 255.0, } cmap = np.zeros((256, 3)) # If the user provides a list with a non-zero final interval, it # doesn't make sense because you have an interval but no final # color to which it interpolates. So provide a 0-length white final # interval to end the previous interval in white. if ctuple_list[-1][1] != 0: ctuple_list.append(("white", 0)) # Figure out how many intervals there are total. rolling_index = 0 for i, (color, interval) in enumerate(ctuple_list): if isinstance(color, str): ctuple_list[i] = (color_dict[color], interval) rolling_index += interval scale = 256.0 / rolling_index n = len(ctuple_list) # Step through each ctuple and interpolate from one color to the # next over the interval provided rolling_index = 0 for i in range(n - 1): color, interval = ctuple_list[i] interval *= scale next_index = rolling_index + interval next_color, next_interval = ctuple_list[i + 1] if not interpolate: next_color = color # Interpolate the R, G, and B channels from one color to the next # Use np.round to make sure you're on a discrete index interval = int(np.round(next_index) - np.round(rolling_index)) for j in np.arange(3): cmap[int(np.rint(rolling_index)) : int(np.rint(next_index)), j] = ( np.linspace(color[j], next_color[j], num=interval) ) rolling_index = next_index # Return a dictionary with the appropriate RGB channels each consisting of # a 256x3 array in the format that is expected by add_colormap() to add a # colormap to the session. # The format is as follows: # First number is the number at which we are defining a color breakpoint # Second number is the (0..1) number to interpolate to when coming *from below* # Third number is the (0..1) number to interpolate to when coming *from above* _vs = np.linspace(0, 1, 256) cdict = { "red": np.transpose([_vs, cmap[:, 0], cmap[:, 0]]), "green": np.transpose([_vs, cmap[:, 1], cmap[:, 1]]), "blue": np.transpose([_vs, cmap[:, 2], cmap[:, 2]]), } if name is not None: add_colormap(name, cdict) return cdict yt-project-yt-f043ac8/yt/visualization/eps_writer.py000066400000000000000000001570731510711153200227610ustar00rootroot00000000000000import os import numpy as np import pyx from matplotlib import pyplot as plt from matplotlib.colors import LogNorm, Normalize from yt.config import ytcfg from yt.units.unit_object import Unit # type: ignore from yt.units.yt_array import YTQuantity from yt.utilities.logger import ytLogger as mylog from .plot_window import PlotWindow from .profile_plotter import PhasePlot, ProfilePlot def convert_frac_to_tex(string): frac_pos = string.find(r"\frac") result = string[frac_pos + 5 :] level = [0] * len(result) clevel = 0 for i in range(len(result)): if result[i] == "{": clevel += 1 elif result[i] == "}": clevel -= 1 level[i] = clevel div_pos = level.index(0) end_pos = level.index(0, div_pos + 1) result = ( r"${" + result[: div_pos + 1] + r"\over" + result[div_pos + 1 : end_pos] + r"}$" + result[end_pos:] ) result = result.replace(r"\ ", r"\;") return result def pyxize_label(string): frac_pos = string.find(r"\frac") if frac_pos >= 0: pre = string[:frac_pos] result = pre + convert_frac_to_tex(string) else: result = string result = result.replace("$", "") result = r"$" + result + r"$" return result class DualEPS: def __init__(self, figsize=(12, 12)): r"""Initializes the DualEPS class to which we can progressively add layers of vector graphics and compressed bitmaps. Parameters ---------- figsize : tuple of floats The width and height of a single figure in centimeters. """ pyx.unit.set(xscale=1.4) self.figsize = figsize self.canvas = None self.colormaps = None self.field = None self.axes_drawn = False def hello_world(self): r"""A simple test.""" if self.canvas is None: self.canvas = pyx.canvas.canvas() p = pyx.path.line(0, 0, 1, 1) self.canvas.stroke(p) self.canvas.text(0, 0, "Hello world.") # ============================================================================= def return_field(self, plot): if isinstance(plot, (PlotWindow, PhasePlot)): return list(plot.plots.keys())[0] else: return None # ============================================================================= def axis_box( self, xrange=(0, 1), yrange=(0, 1), xlabel="", ylabel="", xlog=False, ylog=False, xdata=None, ydata=None, tickcolor=None, bare_axes=False, pos=(0, 0), xaxis_side=0, yaxis_side=0, size=None, ): r"""Draws an axis box in the figure. Parameters ---------- xrange : tuple of floats The min and max of the x-axis yrange : tuple of floats The min and max of the y-axis xlabel : string Label for the x-axis ylabel : string Label for the y-axis xlog : boolean Flag to use a logarithmic x-axis ylog : boolean Flag to use a logarithmic y-axis tickcolor : `pyx.color.*.*` Color for the tickmarks. Example: pyx.color.cmyk.black bare_axes : boolean Set to true to have no annotations or tick marks on all of the axes. pos : tuple of floats (x,y) position in centimeters of the origin in the figure xaxis_side : integer Set to 0 for the x-axis annotations to be on the left. Set to 1 to print them on the right side. yaxis_side : integer Set to 0 for the y-axis annotations to be on the bottom. Set to 1 to print them on the top. size : tuple of floats Size of axis box in units of figsize Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.save_fig() """ if isinstance(xrange[0], YTQuantity): xrange = (xrange[0].value, xrange[1].value) if isinstance(yrange[0], YTQuantity): yrange = (yrange[0].value, yrange[1].value) if tickcolor is None: c1 = pyx.graph.axis.painter.regular(tickattrs=[pyx.color.cmyk.black]) c2 = pyx.graph.axis.painter.regular( tickattrs=[pyx.color.cmyk.black], labelattrs=None ) else: c1 = pyx.graph.axis.painter.regular(tickattrs=[tickcolor]) c2 = pyx.graph.axis.painter.regular(tickattrs=[tickcolor], labelattrs=None) if size is None: psize = self.figsize else: psize = (size[0] * self.figsize[0], size[1] * self.figsize[1]) xticklabels = True yticklabels = True if xaxis_side == 0: xleftlabel = xlabel xrightlabel = "" c1x = c1 c2x = c2 elif xaxis_side == 1: xleftlabel = "" xrightlabel = xlabel c1x = c2 c2x = c1 else: xticklabels = False xleftlabel = "" xrightlabel = "" c1x = c1 c2x = c2 if yaxis_side == 0: yleftlabel = ylabel yrightlabel = "" c1y = c1 c2y = c2 elif yaxis_side == 1: yleftlabel = "" yrightlabel = ylabel c1y = c2 c2y = c1 else: yticklabels = False yleftlabel = "" yrightlabel = "" c1y = c1 c2y = c2 if xlog: if xticklabels: xaxis = pyx.graph.axis.log( min=xrange[0], max=xrange[1], title=xleftlabel, painter=c1x ) xaxis2 = pyx.graph.axis.log( min=xrange[0], max=xrange[1], title=xrightlabel, painter=c2x ) else: xaxis = pyx.graph.axis.log( min=xrange[0], max=xrange[1], title=xleftlabel, painter=c1x, parter=None, ) xaxis2 = pyx.graph.axis.log( min=xrange[0], max=xrange[1], title=xrightlabel, painter=c2x, parter=None, ) else: if xticklabels: xaxis = pyx.graph.axis.lin( min=xrange[0], max=xrange[1], title=xleftlabel, painter=c1x ) xaxis2 = pyx.graph.axis.lin( min=xrange[0], max=xrange[1], title=xrightlabel, painter=c2x ) else: xaxis = pyx.graph.axis.lin( min=xrange[0], max=xrange[1], title=xleftlabel, painter=c1x, parter=None, ) xaxis2 = pyx.graph.axis.lin( min=xrange[0], max=xrange[1], title=xrightlabel, painter=c2x, parter=None, ) if ylog: if yticklabels: yaxis = pyx.graph.axis.log( min=yrange[0], max=yrange[1], title=yleftlabel, painter=c1y ) yaxis2 = pyx.graph.axis.log( min=yrange[0], max=yrange[1], title=yrightlabel, painter=c2y ) else: yaxis = pyx.graph.axis.log( min=yrange[0], max=yrange[1], title=yleftlabel, painter=c1y, parter=None, ) yaxis2 = pyx.graph.axis.log( min=yrange[0], max=yrange[1], title=yrightlabel, painter=c2y, parter=None, ) else: if yticklabels: yaxis = pyx.graph.axis.lin( min=yrange[0], max=yrange[1], title=yleftlabel, painter=c1y ) yaxis2 = pyx.graph.axis.lin( min=yrange[0], max=yrange[1], title=yrightlabel, painter=c2y ) else: yaxis = pyx.graph.axis.lin( min=yrange[0], max=yrange[1], title=yleftlabel, painter=c1y, parter=None, ) yaxis2 = pyx.graph.axis.lin( min=yrange[0], max=yrange[1], title=yrightlabel, painter=c2y, parter=None, ) if bare_axes: if ylog: yaxis = pyx.graph.axis.log( min=yrange[0], max=yrange[1], title=yleftlabel, parter=None ) yaxis2 = pyx.graph.axis.log( min=yrange[0], max=yrange[1], title=yrightlabel, parter=None ) else: yaxis = pyx.graph.axis.lin( min=yrange[0], max=yrange[1], title=yleftlabel, parter=None ) yaxis2 = pyx.graph.axis.lin( min=yrange[0], max=yrange[1], title=yrightlabel, parter=None ) if xlog: xaxis = pyx.graph.axis.log( min=xrange[0], max=xrange[1], title=xleftlabel, parter=None ) xaxis2 = pyx.graph.axis.log( min=xrange[0], max=xrange[1], title=xrightlabel, parter=None ) else: xaxis = pyx.graph.axis.lin( min=xrange[0], max=xrange[1], title=xleftlabel, parter=None ) xaxis2 = pyx.graph.axis.lin( min=xrange[0], max=xrange[1], title=xrightlabel, parter=None ) blank_data = pyx.graph.data.points([(-1e20, -1e20), (-1e19, -1e19)], x=1, y=2) if self.canvas is None: self.canvas = pyx.graph.graphxy( width=psize[0], height=psize[1], x=xaxis, y=yaxis, x2=xaxis2, y2=yaxis2, xpos=pos[0], ypos=pos[1], ) if xdata is None: self.canvas.plot(blank_data) else: data = pyx.graph.data.points(np.array([xdata, ydata]).T, x=1, y=2) self.canvas.plot( data, [pyx.graph.style.line([pyx.style.linewidth.Thick])] ) else: plot = pyx.graph.graphxy( width=psize[0], height=psize[1], x=xaxis, y=yaxis, x2=xaxis2, y2=yaxis2, xpos=pos[0], ypos=pos[1], ) if xdata is None: plot.plot(blank_data) else: data = pyx.graph.data.points(np.array([xdata, ydata]).T, x=1, y=2) plot.plot(data, [pyx.graph.style.line([pyx.style.linewidth.Thick])]) self.canvas.insert(plot) self.axes_drawn = True # ============================================================================= def axis_box_yt( self, plot, units=None, bare_axes=False, tickcolor=None, xlabel=None, ylabel=None, **kwargs, ): r"""Wrapper around DualEPS.axis_box to automatically fill in the axis ranges and labels from a yt plot. This also accepts any parameters that DualEPS.axis_box takes. Parameters ---------- plot : `yt.visualization.plot_window.PlotWindow` yt plot on which the axes are based. units : string Unit description that overrides yt's unit description. Only affects the axis label. bare_axes : boolean Set to true to have no annotations or tick marks on all of the axes. Examples -------- >>> p = SlicePlot(ds, 0, "density") >>> d = DualEPS() >>> d.axis_box_yt(p) >>> d.save_fig() """ if isinstance(plot, (PlotWindow, PhasePlot)): plot.refresh() if isinstance(plot, PlotWindow): data = plot.frb width = plot.width[0] if units is None: units = plot.ds.get_smallest_appropriate_unit(width) width = width.in_units(str(units)) xc = 0.5 * (plot.xlim[0] + plot.xlim[1]) yc = 0.5 * (plot.ylim[0] + plot.ylim[1]) _xrange = [(plot.xlim[i] - xc).in_units(units) for i in (0, 1)] _yrange = [(plot.ylim[i] - yc).in_units(units) for i in (0, 1)] _xlog = False _ylog = False if bare_axes: _xlabel = "" _ylabel = "" else: if xlabel is not None: _xlabel = xlabel else: if data.axis != 4: xi = plot.ds.coordinates.x_axis[data.axis] x_name = plot.ds.coordinates.axis_name[xi] _xlabel = f"{x_name} ({units})" else: _xlabel = f"x ({units})" if ylabel is not None: _ylabel = ylabel else: if data.axis != 4: yi = plot.ds.coordinates.y_axis[data.axis] y_name = plot.ds.coordinates.axis_name[yi] _ylabel = f"{y_name} ({units})" else: _ylabel = f"y ({units})" if tickcolor is None: _tickcolor = pyx.color.cmyk.white elif isinstance(plot, ProfilePlot): subplot = plot.axes.values()[0] # limits for axes xlimits = subplot.get_xlim() _xrange = ( YTQuantity(xlimits[0], "m"), YTQuantity(xlimits[1], "m"), ) # unit hardcoded but afaik it is not used anywhere so it doesn't matter if list(plot.axes.ylim.values())[0][0] is None: ylimits = subplot.get_ylim() else: ylimits = list(plot.axes.ylim.values())[0] _yrange = ( YTQuantity(ylimits[0], "m"), YTQuantity(ylimits[1], "m"), ) # unit hardcoded but afaik it is not used anywhere so it doesn't matter # axis labels xaxis = subplot.xaxis _xlabel = pyxize_label(xaxis.label.get_text()) yaxis = subplot.yaxis _ylabel = pyxize_label(yaxis.label.get_text()) # set log if necessary if subplot.get_xscale() == "log": _xlog = True else: _xlog = False if subplot.get_yscale() == "log": _ylog = True else: _ylog = False _tickcolor = None elif isinstance(plot, PhasePlot): k = list(plot.plots.keys())[0] _xrange = plot[k].axes.get_xlim() _yrange = plot[k].axes.get_ylim() _xlog = plot.profile.x_log _ylog = plot.profile.y_log if bare_axes: _xlabel = "" _ylabel = "" else: if xlabel is not None: _xlabel = xlabel else: _xlabel = plot[k].axes.get_xlabel() if ylabel is not None: _ylabel = ylabel else: _ylabel = plot[k].axes.get_ylabel() _xlabel = pyxize_label(_xlabel) _ylabel = pyxize_label(_ylabel) if tickcolor is None: _tickcolor = None elif isinstance(plot, np.ndarray): ax = plt.gca() _xrange = ax.get_xlim() _yrange = ax.get_ylim() _xlog = False _ylog = False if bare_axes: _xlabel = "" _ylabel = "" else: if xlabel is not None: _xlabel = xlabel else: _xlabel = ax.get_xlabel() if ylabel is not None: _ylabel = ylabel else: _ylabel = ax.get_ylabel() if tickcolor is None: _tickcolor = None else: _xrange = plot._axes.get_xlim() _yrange = plot._axes.get_ylim() _xlog = plot._log_x _ylog = plot._log_y if bare_axes: _xlabel = "" _ylabel = "" else: if xlabel is not None: _xlabel = xlabel else: _xlabel = plot._x_label if ylabel is not None: _ylabel = ylabel else: _ylabel = plot._y_label if tickcolor is None: _tickcolor = None if tickcolor is not None: _tickcolor = tickcolor self.axis_box( xrange=_xrange, yrange=_yrange, xlabel=_xlabel, ylabel=_ylabel, tickcolor=_tickcolor, xlog=_xlog, ylog=_ylog, bare_axes=bare_axes, **kwargs, ) # ============================================================================= def insert_image(self, filename, pos=(0, 0), size=None): r"""Inserts a JPEG file in the figure. Parameters ---------- filename : string Name of the JPEG file pos : tuple of floats Position of the origin of the image in centimeters size : tuple of flots Size of image in units of figsize Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.insert_image("image.jpg") >>> d.save_fig() """ if size is not None: width = size[0] * self.figsize[0] height = size[1] * self.figsize[1] else: width = self.figsize[0] height = self.figsize[1] image = pyx.bitmap.jpegimage(filename) if self.canvas is None: self.canvas = pyx.canvas.canvas() self.canvas.insert( pyx.bitmap.bitmap( pos[0], pos[1], image, compressmode=None, width=width, height=height ) ) # ============================================================================= def insert_image_yt(self, plot, field=None, pos=(0, 0), scale=1.0): r"""Inserts a bitmap taken from a yt plot. Parameters ---------- plot : `yt.visualization.plot_window.PlotWindow` yt plot that provides the image pos : tuple of floats Position of the origin of the image in centimeters. Examples -------- >>> p = SlicePlot(ds, 0, "density") >>> d = DualEPS() >>> d.axis_box_yt(p) >>> d.insert_image_yt(p) >>> d.save_fig() Notes ----- For best results, set use_colorbar=False when creating the yt image. """ from matplotlib.backends.backend_agg import FigureCanvasAgg # We need to remove the colorbar (if necessary), remove the # axes, and resize the figure to span the entire figure force_square = False if self.canvas is None: self.canvas = pyx.canvas.canvas() if isinstance(plot, (PlotWindow, PhasePlot)): if field is None: self.field = list(plot.plots.keys())[0] mylog.warning( "No field specified. Choosing first field (%s)", self.field ) else: self.field = plot.data_source._determine_fields(field)[0] if self.field not in plot.plots.keys(): raise RuntimeError("Field '%s' does not exist!", self.field) if isinstance(plot, PlotWindow): plot.hide_colorbar() plot.hide_axes() else: plot.plots[self.field]._toggle_axes(False) plot.plots[self.field]._toggle_colorbar(False) plot.refresh() _p1 = plot.plots[self.field].figure force_square = True elif isinstance(plot, ProfilePlot): _p1 = plot.figures.items()[0][1] elif isinstance(plot, np.ndarray): plt.figure() iplot = plt.figimage(plot) _p1 = iplot.figure _p1.set_size_inches(self.figsize[0], self.figsize[1]) ax = plt.gca() _p1.add_axes(ax) else: raise RuntimeError("Unknown plot type") _p1.axes[0].set_position([0, 0, 1, 1]) # rescale figure _p1.set_facecolor("w") # set background color figure_canvas = FigureCanvasAgg(_p1) figure_canvas.draw() size = (_p1.get_size_inches() * _p1.dpi).astype("int64") # Account for non-square images after removing the colorbar. scale *= 1.0 - 1.0 / (_p1.dpi * self.figsize[0]) if force_square: yscale = scale * float(size[1]) / float(size[0]) else: yscale = scale image = pyx.bitmap.image(size[0], size[1], "RGB", figure_canvas.tostring_rgb()) self.canvas.insert( pyx.bitmap.bitmap( pos[0], pos[1], image, width=scale * self.figsize[0], height=yscale * self.figsize[1], ) ) # ============================================================================= def colorbar( self, name, zrange=(0, 1), label="", log=False, tickcolor=None, orientation="right", pos=None, shrink=1.0, ): r"""Places a colorbar adjacent to the current figure. Parameters ---------- name : string name of the (matplotlib) colormap to use zrange : tuple of floats min and max of the colorbar's range label : string colorbar label log : boolean Flag to use a logarithmic scale tickcolor : `pyx.color.*.*` Color for the tickmarks. Example: pyx.color.cmyk.black orientation : string Placement of the colorbar. Can be "left", "right", "top", or "bottom". pos : list of floats (x,y) position of the origin of the colorbar in centimeters. shrink : float Factor to shrink the colorbar's size. A value of 1 means the colorbar will have a height / width of the figure. Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.insert_image("image.jpg") >>> d.colorbar( ... "hot", xrange=(1e-2, 1e-4), log=True, label="Density [cm$^{-3}$]" ... ) >>> d.save_fig() """ if pos is None: pos = [0, 0] if orientation == "right": origin = (pos[0] + self.figsize[0] + 0.5, pos[1]) size = (0.1 * self.figsize[0], self.figsize[1]) imsize = (1, 256) elif orientation == "left": origin = (pos[0] - 0.5 - 0.1 * self.figsize[0], pos[1]) size = (0.1 * self.figsize[0], self.figsize[1]) imsize = (1, 256) elif orientation == "top": origin = (pos[0], pos[1] + self.figsize[1] + 0.5) imorigin = (pos[0] + self.figsize[0], pos[1] + self.figsize[1] + 0.5) size = (self.figsize[0], 0.1 * self.figsize[1]) imsize = (256, 1) elif orientation == "bottom": origin = (pos[0], pos[1] - 0.5 - 0.1 * self.figsize[1]) imorigin = (pos[0] + self.figsize[0], pos[1] - 0.5 - 0.1 * self.figsize[1]) size = (self.figsize[0], 0.1 * self.figsize[1]) imsize = (256, 1) else: raise RuntimeError(f"orientation {orientation} unknown") # If shrink is a scalar, then convert into tuple if not isinstance(shrink, (tuple, list)): shrink = (shrink, shrink) # Scale the colorbar shift = (0.5 * (1.0 - shrink[0]) * size[0], 0.5 * (1.0 - shrink[1]) * size[1]) # To facilitate stretching rather than shrinking # If stretched in both directions (makes no sense?) then y dominates. if shrink[0] > 1.0: shift = (0.05 * self.figsize[0], 0.5 * (1.0 - shrink[1]) * size[1]) if shrink[1] > 1.0: shift = (0.5 * (1.0 - shrink[0]) * size[0], 0.05 * self.figsize[1]) size = (size[0] * shrink[0], size[1] * shrink[1]) origin = (origin[0] + shift[0], origin[1] + shift[1]) # Convert the colormap into a string x = np.linspace(1, 0, 256) cm_string = plt.get_cmap(name)(x, bytes=True)[:, 0:3].tobytes() cmap_im = pyx.bitmap.image(imsize[0], imsize[1], "RGB", cm_string) if orientation == "top" or orientation == "bottom": imorigin = (imorigin[0] - shift[0], imorigin[1] + shift[1]) self.canvas.insert( pyx.bitmap.bitmap( imorigin[0], imorigin[1], cmap_im, width=-size[0], height=size[1] ) ) else: self.canvas.insert( pyx.bitmap.bitmap( origin[0], origin[1], cmap_im, width=size[0], height=size[1] ) ) if tickcolor is None: c1 = pyx.graph.axis.painter.regular(tickattrs=[pyx.color.cmyk.black]) pyx.graph.axis.painter.regular( tickattrs=[pyx.color.cmyk.black], labelattrs=None ) else: c1 = pyx.graph.axis.painter.regular(tickattrs=[tickcolor]) pyx.graph.axis.painter.regular(tickattrs=[tickcolor], labelattrs=None) if log: yaxis = pyx.graph.axis.log( min=zrange[0], max=zrange[1], title=label, painter=c1 ) yaxis2 = pyx.graph.axis.log(min=zrange[0], max=zrange[1], parter=None) else: yaxis = pyx.graph.axis.lin( min=zrange[0], max=zrange[1], title=label, painter=c1 ) yaxis2 = pyx.graph.axis.lin(min=zrange[0], max=zrange[1], parter=None) xaxis = pyx.graph.axis.lin(parter=None) if orientation == "right": _colorbar = pyx.graph.graphxy( width=size[0], height=size[1], xpos=origin[0], ypos=origin[1], x=xaxis, y=yaxis2, y2=yaxis, ) elif orientation == "left": _colorbar = pyx.graph.graphxy( width=size[0], height=size[1], xpos=origin[0], ypos=origin[1], x=xaxis, y2=yaxis2, y=yaxis, ) elif orientation == "top": _colorbar = pyx.graph.graphxy( width=size[0], height=size[1], xpos=origin[0], ypos=origin[1], y=xaxis, x=yaxis2, x2=yaxis, ) elif orientation == "bottom": _colorbar = pyx.graph.graphxy( width=size[0], height=size[1], xpos=origin[0], ypos=origin[1], y=xaxis, x2=yaxis2, x=yaxis, ) blank_data = pyx.graph.data.points([(-1e10, -1e10), (-9e10, -9e10)], x=1, y=2) _colorbar.plot(blank_data) self.canvas.insert(_colorbar) # ============================================================================= def colorbar_yt(self, plot, field=None, cb_labels=None, **kwargs): r"""Wrapper around DualEPS.colorbar to take information from a yt plot. Accepts all parameters that DualEPS.colorbar takes. Parameters ---------- plot : A yt plot yt plot from which the information is taken. cb_labels : list of labels for the colorbars. List should be the same size as the number of colorbars used. Should be passed into this function by either the singleplot or multiplot api. Examples -------- >>> p = SlicePlot(ds, 0, "density") >>> p.hide_colorbar() >>> d = DualEPS() >>> d.axis_box_yt(p) >>> d.insert_image_yt(p) >>> d.colorbar_yt(p) >>> d.save_fig() """ if isinstance(plot, ProfilePlot): raise RuntimeError( "When using ProfilePlots you must either set yt_nocbar=True or provide " "colorbar flags so that the profiles don't have colorbars" ) _cmap = None if field is not None: self.field = plot.data_source._determine_fields(field)[0] if isinstance(plot, (PlotWindow, PhasePlot)): _cmap = plot[self.field].colorbar_handler.cmap else: if plot.cmap is not None: _cmap = plot.cmap.name if _cmap is None: _cmap = ytcfg.get("yt", "default_colormap") if isinstance(plot, (PlotWindow, PhasePlot)): if isinstance(plot, PlotWindow): try: _zlabel = plot.frb[self.field].info["label"] _unit = Unit( plot.frb[self.field].units, registry=plot.ds.unit_registry ) units = _unit.latex_representation() # PyX does not support \frac because it's based on TeX. units = pyxize_label(units) _zlabel += r" (" + units + r")" except NotImplementedError: print("Colorbar label not available") _zlabel = "" else: _, _, z_title = plot._get_field_title(self.field, plot.profile) _zlabel = pyxize_label(z_title) _zlabel = _zlabel.replace("_", r"\;") _p = plot.plots[self.field] _norm = _p.norm_handler.get_norm(plot.frb[self.field]) norm_type = type(_norm) if norm_type is LogNorm: _zlog = True elif norm_type is Normalize: # linear scaling _zlog = False else: raise RuntimeError( "eps_writer is not compatible with scalings other than linear and log, " f"received {norm_type}" ) _zrange = (_norm.vmin, _norm.vmax) else: _zlabel = plot._z_label.replace("_", r"\;") _zlog = plot._log_z _zrange = (plot.norm.vmin, plot.norm.vmax) if cb_labels is not None: # Overrides deduced labels _zlabel = cb_labels.pop() self.colorbar(_cmap, zrange=_zrange, label=_zlabel, log=_zlog, **kwargs) # ============================================================================= def circle( self, radius=0.2, loc=(0.5, 0.5), color=pyx.color.cmyk.white, linewidth=pyx.style.linewidth.normal, ): r"""Draws a circle in the current figure. Parameters ---------- radius : float Radius of the circle in units of figsize loc : tuple of floats Location of the circle's center in units of figsize color : `pyx.color.*.*` Color of the circle stroke. Example: pyx.color.cmyk.white linewidth : `pyx.style.linewidth.*` Width of the circle stroke width. Example: pyx.style.linewidth.normal Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.insert_image("image.jpg") >>> d.circle(radius=0.1, color=pyx.color.cmyk.Red) >>> d.save_fig() """ circle = pyx.path.circle( self.figsize[0] * loc[0], self.figsize[1] * loc[1], self.figsize[0] * radius ) self.canvas.stroke(circle, [color, linewidth]) # ============================================================================= def arrow( self, size=0.2, label="", loc=(0.05, 0.08), labelloc="top", color=pyx.color.cmyk.white, linewidth=pyx.style.linewidth.normal, rotation=0.0, ): r"""Draws an arrow in the current figure Parameters ---------- size : float Length of arrow (base to tip) in units of the figure size. label : string Annotation label of the arrow. loc : tuple of floats Location of the left hand side of the arrow in units of the figure size. rotation : float Orientation angle of the arrow in units of degrees labelloc : string Location of the label with respect to the line. Can be "top" or "bottom" color : `pyx.color.*.*` Color of the arrow. Example: pyx.color.cymk.white linewidth : `pyx.style.linewidth.*` Width of the arrow. Example: pyx.style.linewidth.normal Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.insert_image("arrow_image.jpg") >>> d.arrow(size=0.2, label="Black Hole!", loc=(0.05, 0.1)) >>> d.save_fig() """ line = pyx.path.line( self.figsize[0] * loc[0], self.figsize[1] * loc[1], self.figsize[0] * (loc[0] + size * np.cos(np.pi * rotation / 180)), self.figsize[1] * (loc[1] + size * np.sin(np.pi * rotation / 180)), ) self.canvas.stroke(line, [linewidth, color, pyx.deco.earrow()]) if labelloc == "bottom": yoff = -0.1 * size valign = pyx.text.valign.top else: yoff = +0.1 * size valign = pyx.text.valign.bottom if label != "": self.canvas.text( self.figsize[0] * (loc[0] + 0.5 * size), self.figsize[1] * (loc[1] + yoff), label, [color, valign, pyx.text.halign.center], ) # ============================================================================= def scale_line( self, size=0.2, label="", loc=(0.05, 0.08), labelloc="top", color=pyx.color.cmyk.white, linewidth=pyx.style.linewidth.normal, ): r"""Draws a scale line in the current figure. Parameters ---------- size : float Length of the scale line in units of the figure size. label : string Annotation label of the scale line. loc : tuple of floats Location of the left hand side of the scale line in units of the figure size. labelloc : string Location of the label with respect to the line. Can be "top" or "bottom" color : `pyx.color.*.*` Color of the scale line. Example: pyx.color.cymk.white linewidth : `pyx.style.linewidth.*` Width of the scale line. Example: pyx.style.linewidth.normal Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.insert_image("image.jpg") >>> d.scale_line(size=0.2, label="1 kpc", loc=(0.05, 0.1)) >>> d.save_fig() """ line = pyx.path.line( self.figsize[0] * loc[0], self.figsize[1] * loc[1], self.figsize[0] * (loc[0] + size), self.figsize[1] * loc[1], ) self.canvas.stroke(line, [linewidth, color]) line = pyx.path.line( self.figsize[0] * loc[0], self.figsize[1] * (loc[1] - 0.1 * size), self.figsize[0] * loc[0], self.figsize[1] * (loc[1] + 0.1 * size), ) self.canvas.stroke(line, [linewidth, color]) line = pyx.path.line( self.figsize[0] * (loc[0] + size), self.figsize[1] * (loc[1] - 0.1 * size), self.figsize[0] * (loc[0] + size), self.figsize[1] * (loc[1] + 0.1 * size), ) self.canvas.stroke(line, [linewidth, color]) if labelloc == "bottom": yoff = -0.1 * size valign = pyx.text.valign.top else: yoff = +0.1 * size valign = pyx.text.valign.bottom if label != "": self.canvas.text( self.figsize[0] * (loc[0] + 0.5 * size), self.figsize[1] * (loc[1] + yoff), label, [color, valign, pyx.text.halign.center], ) # ============================================================================= def title_box( self, text, color=pyx.color.cmyk.black, bgcolor=pyx.color.cmyk.white, loc=(0.02, 0.98), halign=pyx.text.halign.left, valign=pyx.text.valign.top, text_opts=None, ): r"""Inserts a box with text in the current figure. Parameters ---------- text : string String to insert in the textbox. color : `pyx.color.*.*` Color of the text. Example: pyx.color.cmyk.black bgcolor : `pyx.color.*.*` Color of the textbox background. Example: pyx.color.cmyk.white loc : tuple of floats Location of the textbox origin in units of the figure size. halign : `pyx.text.halign.*` Horizontal alignment of the text. Example: pyx.text.halign.left valign : `pyx.text.valign.*` Vertical alignment of the text. Example: pyx.text.valign.top Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) >>> d.insert_image("image.jpg") >>> d.title_box("Halo 1", loc=(0.05, 0.95)) >>> d.save_fig() """ if text_opts is None: text_opts = [] tbox = self.canvas.text( self.figsize[0] * loc[0], self.figsize[1] * loc[1], text, [color, valign, halign] + text_opts, ) if bgcolor is not None: tpath = tbox.bbox().enlarged(2 * pyx.unit.x_pt).path() self.canvas.draw(tpath, [pyx.deco.filled([bgcolor]), pyx.deco.stroked()]) self.canvas.insert(tbox) # ============================================================================= def save_fig(self, filename="test", format="eps", resolution=250): r"""Saves current figure to a file. Parameters ---------- filename : string Name of the saved file without the extension. format : string Format type. Can be "eps" or "pdf" Examples -------- >>> d = DualEPS() >>> d.axis_box(xrange=(0, 100), yrange=(1e-3, 1), ylog=True) """ filename = os.path.expanduser(filename) if format == "eps": self.canvas.writeEPSfile(filename) elif format == "pdf": self.canvas.writePDFfile(filename) elif format == "png": self.canvas.writeGSfile(filename + ".png", "png16m", resolution=resolution) elif format == "jpg": self.canvas.writeGSfile(filename + ".jpeg", "jpeg", resolution=resolution) else: raise RuntimeError(f"format {format} unknown.") # ============================================================================= # ============================================================================= # ============================================================================= def multiplot( ncol, nrow, yt_plots=None, fields=None, images=None, xranges=None, yranges=None, xlabels=None, ylabels=None, xdata=None, ydata=None, colorbars=None, shrink_cb=0.95, figsize=(8, 8), margins=(0, 0), titles=None, savefig=None, format="eps", yt_nocbar=False, bare_axes=False, xaxis_flags=None, yaxis_flags=None, cb_flags=None, cb_location=None, cb_labels=None, ): r"""Convenience routine to create a multi-panel figure from yt plots or JPEGs. The images are first placed from the origin, and then bottom-to-top and left-to-right. Parameters ---------- ncol : integer Number of columns in the figure. nrow : integer Number of rows in the figure. yt_plots : list of yt plot instances yt plots to include in the figure. images : list of strings JPEG filenames to include in the figure. xranges : list of tuples The min and max of the x-axes yranges : list of tuples The min and max of the y-axes xlabels : list of strings Labels for the x-axes ylabels : list of strings Labels for the y-axes colorbars : list of dicts Dicts that describe the type of colorbar to be used in each figure. Use the function return_colormap() to create these dicts. shrink_cb : float Factor by which the colorbar is shrunk. figsize : tuple of floats The width and height of a single figure in centimeters. margins : tuple of floats The horizontal and vertical margins between panels in centimeters. titles : list of strings Titles that are placed in textboxes in each panel. savefig : string Name of the saved file without the extension. format : string File format of the figure. eps or pdf accepted. yt_nocbar : boolean Flag to indicate whether or not colorbars are created. bare_axes : boolean Set to true to have no annotations or tick marks on all of the axes. cb_flags : list of booleans Flags for each plot to have a colorbar or not. cb_location : list of strings Strings to control the location of the colorbar (left, right, top, bottom) cb_labels : list of labels for the colorbars. List should be the same size as the number of colorbars used. Examples -------- >>> images = ["density.jpg", "hi_density.jpg", "entropy.jpg", "special.jpg"] >>> cbs = [] >>> cbs.append(return_colormap("cmyt.arbre", "Density [cm$^{-3}$]", (0, 10), False)) >>> cbs.append(return_colormap("cmyt.kelp", "HI Density", (0, 5), False)) >>> cbs.append(return_colormap("hot", r"Entropy [K cm$^2$]", (1e-2, 1e6), True)) >>> cbs.append(return_colormap("Spectral", "Stuff$_x$!", (1, 300), True)) >>> mp = multiplot( ... 2, ... 2, ... images=images, ... margins=(0.1, 0.1), ... titles=["1", "2", "3", "4"], ... xlabels=["one", "two"], ... ylabels=None, ... colorbars=cbs, ... shrink_cb=0.95, ... ) >>> mp.scale_line(label="$r_{vir}$", labelloc="top") >>> mp.save_fig("multiplot") Notes ----- If given both yt_plots and images, this will get preference to the yt plots. """ # Error check npanels = ncol * nrow if cb_labels is not None: cb_labels.reverse() # Because I pop the list if images is not None: if len(images) != npanels: raise RuntimeError( "Number of images (%d) doesn't match nrow(%d)" " x ncol(%d)." % (len(images), nrow, ncol) ) if yt_plots is None and images is None: raise RuntimeError("Must supply either yt_plots or image filenames.") if yt_plots is not None and images is not None: mylog.warning("Given both images and yt plots. Ignoring images.") if yt_plots is not None: _yt = True else: _yt = False if fields is None: fields = [None] * npanels # If no ranges or labels given and given only images, fill them in. if not _yt: if xranges is None: xranges = [] for _ in range(npanels): xranges.append((0, 1)) if yranges is None: yranges = [] for _ in range(npanels): yranges.append((0, 1)) if xlabels is None: xlabels = [] for _ in range(npanels): xlabels.append("") if ylabels is None: ylabels = [] for _ in range(npanels): ylabels.append("") d = DualEPS(figsize=figsize) for j in range(nrow): invj = nrow - j - 1 ypos = invj * (figsize[1] + margins[1]) for i in range(ncol): xpos = i * (figsize[0] + margins[0]) index = j * ncol + i if isinstance(yt_plots, list): this_plot = yt_plots[index] else: this_plot = yt_plots if j == nrow - 1: xaxis = 0 elif j == 0: xaxis = 1 else: xaxis = -1 if i == 0: yaxis = 0 elif i == ncol - 1: yaxis = 1 else: yaxis = -1 if xdata is None: _xdata = None else: _xdata = xdata[index] if ydata is None: _ydata = None else: _ydata = ydata[index] if xaxis_flags is not None: if xaxis_flags[index] is not None: xaxis = xaxis_flags[index] if yaxis_flags is not None: if yaxis_flags[index] is not None: yaxis = yaxis_flags[index] if _yt: this_plot._setup_plots() if xlabels is not None: xlabel = xlabels[i] else: xlabel = None if ylabels is not None: ylabel = ylabels[j] else: ylabel = None d.insert_image_yt(this_plot, pos=(xpos, ypos), field=fields[index]) d.axis_box_yt( this_plot, pos=(xpos, ypos), bare_axes=bare_axes, xaxis_side=xaxis, yaxis_side=yaxis, xlabel=xlabel, ylabel=ylabel, xdata=_xdata, ydata=_ydata, ) else: d.insert_image(images[index], pos=(xpos, ypos)) d.axis_box( pos=(xpos, ypos), xrange=xranges[index], yrange=yranges[index], xlabel=xlabels[i], ylabel=ylabels[j], bare_axes=bare_axes, xaxis_side=xaxis, yaxis_side=yaxis, xdata=_xdata, ydata=_ydata, ) if titles is not None: if titles[index] is not None: d.title_box( titles[index], loc=( i + 0.05 + i * margins[0] / figsize[0], j + 0.98 + j * margins[1] / figsize[1], ), ) # Insert colorbars after all axes are placed because we want to # put them on the edges of the bounding box. bbox = ( 100.0 * d.canvas.bbox().left().t, 100.0 * d.canvas.bbox().right().t - d.figsize[0], 100.0 * d.canvas.bbox().bottom().t, 100.0 * d.canvas.bbox().top().t - d.figsize[1], ) for j in range(nrow): invj = nrow - j - 1 ypos0 = invj * (figsize[1] + margins[1]) for i in range(ncol): xpos0 = i * (figsize[0] + margins[0]) index = j * ncol + i if isinstance(yt_plots, list): this_plot = yt_plots[index] else: this_plot = yt_plots if (not _yt and colorbars is not None) or (_yt and not yt_nocbar): if cb_flags is not None: if not cb_flags[index]: continue if cb_location is None: if ncol == 1: orientation = "right" elif i == 0: orientation = "left" elif i + 1 == ncol: orientation = "right" elif j == 0: orientation = "bottom" elif j + 1 == nrow: orientation = "top" else: orientation = None # Marker for interior plot else: if isinstance(cb_location, dict): if fields[index] not in cb_location.keys(): raise RuntimeError( f"{fields[index]} not found in cb_location dict" ) orientation = cb_location[fields[index]] elif isinstance(cb_location, list): orientation = cb_location[index] else: raise RuntimeError("Bad format: cb_location") if orientation == "right": xpos = bbox[1] ypos = ypos0 elif orientation == "left": xpos = bbox[0] ypos = ypos0 elif orientation == "bottom": ypos = bbox[2] xpos = xpos0 elif orientation == "top": ypos = bbox[3] xpos = xpos0 else: mylog.warning( "Unknown colorbar location %s. No colorbar displayed.", orientation, ) orientation = None # Marker for interior plot if orientation is not None: if _yt: # Set field if undefined if fields[index] is None: fields[index] = d.return_field(yt_plots[index]) d.colorbar_yt( this_plot, field=fields[index], pos=[xpos, ypos], shrink=shrink_cb, orientation=orientation, cb_labels=cb_labels, ) else: d.colorbar( colorbars[index]["cmap"], zrange=colorbars[index]["range"], label=colorbars[index]["name"], log=colorbars[index]["log"], orientation=orientation, pos=[xpos, ypos], shrink=shrink_cb, ) if savefig is not None: d.save_fig(savefig, format=format) return d # ============================================================================= def multiplot_yt(ncol, nrow, plots, fields=None, **kwargs): r"""Wrapper for multiplot that takes a yt PlotWindow Accepts all parameters used in multiplot. Parameters ---------- ncol : integer Number of columns in the figure. nrow : integer Number of rows in the figure. plots : ``PlotWindow`` instance, ``PhasePlot`` instance, or list of plots yt plots to be used. Examples -------- >>> p1 = SlicePlot(ds, 0, "density") >>> p1.set_width(10, "kpc") >>> p2 = SlicePlot(ds, 0, "temperature") >>> p2.set_width(10, "kpc") >>> p2.set_colormap("temperature", "hot") >>> sph = ds.sphere(ds.domain_center, (10, "kpc")) >>> p3 = PhasePlot( ... sph, "radius", "density", "temperature", weight_field="cell_mass" ... ) >>> p4 = PhasePlot(sph, "radius", "density", "pressure", "cell_mass") >>> mp = multiplot_yt( ... 2, ... 2, ... [p1, p2, p3, p4], ... savefig="yt", ... shrink_cb=0.9, ... bare_axes=True, ... yt_nocbar=False, ... margins=(0.5, 0.5), ... ) """ # Determine whether the plots are organized in a PlotWindow, or list # of PlotWindows if isinstance(plots, (PlotWindow, PhasePlot)): if fields is None: fields = plots.fields if len(fields) < nrow * ncol: raise RuntimeError( f"Number of plots ({len(fields)}) is less " f"than nrow({nrow}) x ncol({ncol})." ) figure = multiplot(ncol, nrow, yt_plots=plots, fields=fields, **kwargs) elif isinstance(plots, list) and isinstance(plots[0], (PlotWindow, PhasePlot)): if len(plots) < nrow * ncol: raise RuntimeError( f"Number of plots ({len(fields)}) is less " f"than nrow({nrow}) x ncol({ncol})." ) figure = multiplot(ncol, nrow, yt_plots=plots, fields=fields, **kwargs) else: raise RuntimeError("Unknown plot type in multiplot_yt") return figure # ============================================================================= def single_plot( plot, field=None, figsize=(12, 12), cb_orient="right", bare_axes=False, savefig=None, colorbar=True, file_format="eps", **kwargs, ): r"""Wrapper for DualEPS routines to create a figure directly from a yt plot. Calls insert_image_yt, axis_box_yt, and colorbar_yt. Parameters ---------- plot : `yt.visualization.plot_window.PlotWindow` yt plot that provides the image and metadata figsize : tuple of floats Size of the figure in centimeters. cb_orient : string Placement of the colorbar. Can be "left", "right", "top", or "bottom". bare_axes : boolean Set to true to have no annotations or tick marks on all of the axes. savefig : string Name of the saved file without the extension. colorbar : boolean Set to true to include a colorbar file_format : string Format type. Can be "eps" or "pdf" Examples -------- >>> p = SlicePlot(ds, 0, "density") >>> p.set_width(0.1, "kpc") >>> single_plot(p, savefig="figure1") """ d = DualEPS(figsize=figsize) d.insert_image_yt(plot, field=field) d.axis_box_yt(plot, bare_axes=bare_axes, **kwargs) if colorbar and not isinstance(plot, ProfilePlot): d.colorbar_yt(plot, orientation=cb_orient) if savefig is not None: d.save_fig(savefig, format=file_format) return d # ============================================================================= def return_colormap(cmap=None, label="", range=(0, 1), log=False): r"""Returns a dict that describes a colorbar. Exclusively for use with multiplot. Parameters ---------- cmap : string name of the (matplotlib) colormap to use label : string colorbar label range : tuple of floats min and max of the colorbar's range log : boolean Flag to use a logarithmic scale Examples -------- >>> cb = return_colormap("cmyt.arbre", "Density [cm$^{-3}$]", (0, 10), False) """ if cmap is None: cmap = ytcfg.get("yt", "default_colormap") return {"cmap": cmap, "name": label, "range": range, "log": log} yt-project-yt-f043ac8/yt/visualization/fits_image.py000066400000000000000000002040521510711153200226730ustar00rootroot00000000000000import re import sys from functools import partial from numbers import Number as numeric_type import numpy as np from more_itertools import first, mark_ends from yt._typing import FieldKey from yt.data_objects.construction_data_containers import YTCoveringGrid from yt.data_objects.image_array import ImageArray from yt.fields.derived_field import DerivedField from yt.funcs import fix_axis, is_sequence, iter_fields, mylog, validate_moment from yt.units import dimensions from yt.units.unit_object import Unit # type: ignore from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.math_utils import compute_stddev_image from yt.utilities.on_demand_imports import _astropy from yt.utilities.parallel_tools.parallel_analysis_interface import parallel_root_only from yt.visualization.fixed_resolution import FixedResolutionBuffer, ParticleImageBuffer from yt.visualization.particle_plots import ( ParticleAxisAlignedDummyDataSource, ParticleDummyDataSource, ParticleOffAxisDummyDataSource, ) from yt.visualization.volume_rendering.off_axis_projection import off_axis_projection class UnitfulHDU: def __init__(self, hdu): self.hdu = hdu self.header = self.hdu.header self.name = self.header["BTYPE"] self.units = self.header["BUNIT"] self.shape = self.hdu.shape @property def data(self): return YTArray(self.hdu.data, self.units) def __repr__(self): im_shape = " x ".join(str(s) for s in self.shape) return f"FITSImage: {self.name} ({im_shape}, {self.units})" class FITSImageData: def __init__( self, data, fields=None, length_unit=None, width=None, img_ctr=None, wcs=None, current_time=None, time_unit=None, mass_unit=None, velocity_unit=None, magnetic_unit=None, ds=None, unit_header=None, **kwargs, ): r"""Initialize a FITSImageData object. FITSImageData contains a collection of FITS ImageHDU instances and WCS information, along with units for each of the images. FITSImageData instances can be constructed from ImageArrays, NumPy arrays, dicts of such arrays, FixedResolutionBuffers, and YTCoveringGrids. The latter two are the most powerful because WCS information can be constructed automatically from their coordinates. Parameters ---------- data : FixedResolutionBuffer or a YTCoveringGrid. Or, an ImageArray, an numpy.ndarray, or dict of such arrays The data to be made into a FITS image or images. fields : single string or list of strings, optional The field names for the data. If *fields* is none and *data* has keys, it will use these for the fields. If *data* is just a single array one field name must be specified. length_unit : string The units of the WCS coordinates and the length unit of the file. Defaults to the length unit of the dataset, if there is one, or "cm" if there is not. width : float or YTQuantity The width of the image. Either a single value or iterable of values. If a float, assumed to be in *units*. Only used if this information is not already provided by *data*. img_ctr : array_like or YTArray The center coordinates of the image. If a list or NumPy array, it is assumed to be in *units*. This will overwrite any center coordinates potentially provided by *data*. Default in other cases is [0.0]*(number of dimensions). wcs : `~astropy.wcs.WCS` instance, optional Supply an AstroPy WCS instance. Will override automatic WCS creation from FixedResolutionBuffers and YTCoveringGrids. current_time : float, tuple, or YTQuantity, optional The current time of the image(s). If not specified, one will be set from the dataset if there is one. If a float, it will be assumed to be in *time_unit* units. time_unit : string The default time units of the file. Defaults to "s". mass_unit : string The default time units of the file. Defaults to "g". velocity_unit : string The default velocity units of the file. Defaults to "cm/s". magnetic_unit : string The default magnetic units of the file. Defaults to "gauss". ds : `~yt.static_output.Dataset` instance, optional The dataset associated with the image(s), typically used to transfer metadata to the header(s). Does not need to be specified if *data* has a dataset as an attribute. Examples -------- >>> # This example uses a FRB. >>> ds = load("sloshing_nomag2_hdf5_plt_cnt_0150") >>> prj = ds.proj(2, "kT", weight_field=("gas", "density")) >>> frb = prj.to_frb((0.5, "Mpc"), 800) >>> # This example just uses the FRB and puts the coords in kpc. >>> f_kpc = FITSImageData( ... frb, fields="kT", length_unit="kpc", time_unit=(1.0, "Gyr") ... ) >>> # This example specifies a specific WCS. >>> from astropy.wcs import WCS >>> w = WCS(naxis=self.dimensionality) >>> w.wcs.crval = [30.0, 45.0] # RA, Dec in degrees >>> w.wcs.cunit = ["deg"] * 2 >>> nx, ny = 800, 800 >>> w.wcs.crpix = [0.5 * (nx + 1), 0.5 * (ny + 1)] >>> w.wcs.ctype = ["RA---TAN", "DEC--TAN"] >>> scale = 1.0 / 3600.0 # One arcsec per pixel >>> w.wcs.cdelt = [-scale, scale] >>> f_deg = FITSImageData(frb, fields="kT", wcs=w) >>> f_deg.writeto("temp.fits") """ if fields is not None: fields = list(iter_fields(fields)) if ds is None: ds = getattr(data, "ds", None) self.fields = [] self.field_units = {} if unit_header is None: self._set_units( ds, [length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit] ) else: self._set_units_from_header(unit_header) wcs_unit = str(self.length_unit.units) self._fix_current_time(ds, current_time) if width is None: width = 1.0 if isinstance(width, tuple): if ds is None: width = YTQuantity(width[0], width[1]) else: width = ds.quan(width[0], width[1]) exclude_fields = [ "x", "y", "z", "px", "py", "pz", "pdx", "pdy", "pdz", "weight_field", ] if isinstance(data, _astropy.pyfits.PrimaryHDU): data = _astropy.pyfits.HDUList([data]) if isinstance(data, _astropy.pyfits.HDUList): self.hdulist = data for hdu in data: self.fields.append(hdu.header["btype"]) self.field_units[hdu.header["btype"]] = hdu.header["bunit"] self.shape = self.hdulist[0].shape self.dimensionality = len(self.shape) wcs_names = [key for key in self.hdulist[0].header if "WCSNAME" in key] for name in wcs_names: if name == "WCSNAME": key = " " else: key = name[-1] w = _astropy.pywcs.WCS( header=self.hdulist[0].header, key=key, naxis=self.dimensionality ) setattr(self, "wcs" + key.strip().lower(), w) return self.hdulist = _astropy.pyfits.HDUList() stddev = False if hasattr(data, "keys"): img_data = data if fields is None: fields = list(img_data.keys()) if hasattr(data, "data_source"): stddev = getattr(data.data_source, "moment", 1) == 2 elif isinstance(data, np.ndarray): if fields is None: mylog.warning( "No field name given for this array. Calling it 'image_data'." ) fn = "image_data" fields = [fn] else: fn = fields[0] img_data = {fn: data} for fd in fields: if isinstance(fd, tuple): fname = fd[1] elif isinstance(fd, DerivedField): fname = fd.name[1] else: fname = fd if stddev: fname += "_stddev" self.fields.append(fname) # Sanity checking names s = set() duplicates = {f for f in self.fields if f in s or s.add(f)} if len(duplicates) > 0: for i, fd in enumerate(self.fields): if fd in duplicates: if isinstance(fields[i], tuple): ftype, fname = fields[i] elif isinstance(fields[i], DerivedField): ftype, fname = fields[i].name else: raise RuntimeError( f"Cannot distinguish between fields with same name {fd}!" ) self.fields[i] = f"{ftype}_{fname}" for is_first, _is_last, (i, (name, field)) in mark_ends( enumerate(zip(self.fields, fields, strict=True)) ): if name not in exclude_fields: this_img = img_data[field] if hasattr(img_data[field], "units"): has_code_unit = False for atom in this_img.units.expr.atoms(): if str(atom).startswith("code"): has_code_unit = True if has_code_unit: mylog.warning( "Cannot generate an image with code " "units. Converting to units in CGS." ) funits = this_img.units.get_base_equivalent("cgs") this_img.convert_to_units(funits) else: funits = this_img.units self.field_units[name] = str(funits) else: self.field_units[name] = "dimensionless" mylog.info("Making a FITS image of field %s", name) if isinstance(this_img, ImageArray): if i == 0: self.shape = this_img.shape[::-1] this_img = np.asarray(this_img) else: if i == 0: self.shape = this_img.shape this_img = np.asarray(this_img.T) if is_first: hdu = _astropy.pyfits.PrimaryHDU(this_img) else: hdu = _astropy.pyfits.ImageHDU(this_img) hdu.name = name hdu.header["btype"] = name hdu.header["bunit"] = re.sub("()", "", self.field_units[name]) for unit in ("length", "time", "mass", "velocity", "magnetic"): if unit == "magnetic": short_unit = "bf" else: short_unit = unit[0] key = f"{short_unit}unit" value = getattr(self, f"{unit}_unit") if value is not None: hdu.header[key] = float(value.value) hdu.header.comments[key] = f"[{value.units}]" hdu.header["time"] = float(self.current_time.value) if hasattr(self, "current_redshift"): hdu.header["HUBBLE"] = self.hubble_constant hdu.header["REDSHIFT"] = self.current_redshift self.hdulist.append(hdu) self.dimensionality = len(self.shape) if wcs is None: w = _astropy.pywcs.WCS( header=self.hdulist[0].header, naxis=self.dimensionality ) # FRBs and covering grids are special cases where # we have coordinate information, so we take advantage # of this and construct the WCS object if isinstance(img_data, FixedResolutionBuffer): dx = (img_data.bounds[1] - img_data.bounds[0]).to_value(wcs_unit) dy = (img_data.bounds[3] - img_data.bounds[2]).to_value(wcs_unit) dx /= self.shape[0] dy /= self.shape[1] if img_ctr is not None: xctr, yctr = img_ctr else: xctr = 0.5 * (img_data.bounds[1] + img_data.bounds[0]).to_value( wcs_unit ) yctr = 0.5 * (img_data.bounds[3] + img_data.bounds[2]).to_value( wcs_unit ) center = [xctr, yctr] cdelt = [dx, dy] elif isinstance(img_data, YTCoveringGrid): cdelt = img_data.dds.to_value(wcs_unit) if img_ctr is not None: center = img_ctr else: center = 0.5 * (img_data.left_edge + img_data.right_edge).to_value( wcs_unit ) else: # If img_data is just an array we use the width and img_ctr # parameters to determine the cell widths if img_ctr is None: img_ctr = np.zeros(3) if not is_sequence(width): width = [width] * self.dimensionality if isinstance(width[0], YTQuantity): cdelt = [ wh.to_value(wcs_unit) / n for wh, n in zip(width, self.shape, strict=True) ] else: cdelt = [ float(wh) / n for wh, n in zip(width, self.shape, strict=True) ] center = img_ctr[: self.dimensionality] w.wcs.crpix = 0.5 * (np.array(self.shape) + 1) w.wcs.crval = center w.wcs.cdelt = cdelt w.wcs.ctype = ["linear"] * self.dimensionality w.wcs.cunit = [wcs_unit] * self.dimensionality self.set_wcs(w) else: self.set_wcs(wcs) def _fix_current_time(self, ds, current_time): if ds is None: registry = None else: registry = ds.unit_registry tunit = Unit(self.time_unit, registry=registry) if current_time is None: if ds is not None: current_time = ds.current_time else: self.current_time = YTQuantity(0.0, "s") return elif isinstance(current_time, numeric_type): current_time = YTQuantity(current_time, tunit) elif isinstance(current_time, tuple): current_time = YTQuantity(current_time[0], current_time[1]) self.current_time = current_time.to(tunit) def _set_units(self, ds, base_units): if ds is not None: if getattr(ds, "cosmological_simulation", False): self.hubble_constant = ds.hubble_constant self.current_redshift = ds.current_redshift attrs = ( "length_unit", "mass_unit", "time_unit", "velocity_unit", "magnetic_unit", ) cgs_units = ("cm", "g", "s", "cm/s", "gauss") for unit, attr, cgs_unit in zip(base_units, attrs, cgs_units, strict=True): if unit is None: if ds is not None: u = getattr(ds, attr, None) elif attr == "velocity_unit": u = self.length_unit / self.time_unit elif attr == "magnetic_unit": u = np.sqrt( 4.0 * np.pi * self.mass_unit / (self.time_unit**2 * self.length_unit) ) else: u = cgs_unit else: u = unit if isinstance(u, str): uq = YTQuantity(1.0, u) elif isinstance(u, numeric_type): uq = YTQuantity(u, cgs_unit) elif isinstance(u, YTQuantity): uq = u.copy() elif isinstance(u, tuple): uq = YTQuantity(u[0], u[1]) else: uq = None if uq is not None: atoms = {str(a) for a in uq.units.expr.atoms()} if hasattr(self, "hubble_constant"): # Don't store cosmology units if str(uq.units).startswith("cm") or "h" in atoms or "a" in atoms: uq.convert_to_cgs() if any(a.startswith("code") for a in atoms): # Don't store code units mylog.warning( "Cannot use code units of '%s' " "when creating a FITSImageData instance! " "Converting to a cgs equivalent.", uq.units, ) uq.convert_to_cgs() if attr == "length_unit" and uq.value != 1.0: mylog.warning("Converting length units from %s to %s.", uq, uq.units) uq = YTQuantity(1.0, uq.units) setattr(self, attr, uq) def _set_units_from_header(self, header): if "hubble" in header: self.hubble_constant = header["HUBBLE"] self.current_redshift = header["REDSHIFT"] for unit in ["length", "time", "mass", "velocity", "magnetic"]: if unit == "magnetic": key = "BFUNIT" else: key = unit[0].upper() + "UNIT" if key not in header: continue u = header.comments[key].strip("[]") uq = YTQuantity(header[key], u) setattr(self, unit + "_unit", uq) def set_wcs(self, wcs, wcsname=None, suffix=None): """ Set the WCS coordinate information for all images with a WCS object *wcs*. """ if wcsname is None: wcs.wcs.name = "yt" else: wcs.wcs.name = wcsname h = wcs.to_header() if suffix is None: self.wcs = wcs else: setattr(self, "wcs" + suffix, wcs) for img in self.hdulist: for k, v in h.items(): kk = k if suffix is not None: kk += suffix img.header[kk] = v def change_image_name(self, old_name, new_name): """ Change the name of a FITS image. Parameters ---------- old_name : string The old name of the image. new_name : string The new name of the image. """ idx = self.fields.index(old_name) self.hdulist[idx].name = new_name self.hdulist[idx].header["BTYPE"] = new_name self.field_units[new_name] = self.field_units.pop(old_name) self.fields[idx] = new_name def convolve(self, field, kernel, **kwargs): """ Convolve an image with a kernel, either a simple Gaussian kernel or one provided by AstroPy. Currently, this only works for 2D images. All keyword arguments are passed to :meth:`~astropy.convolution.convolve`. Parameters ---------- field : string The name of the field to convolve. kernel : float, YTQuantity, (value, unit) tuple, or AstroPy Kernel object The kernel to convolve the image with. If this is an AstroPy Kernel object, the image will be convolved with it. Otherwise, it is assumed that the kernel is a Gaussian and that this value is the standard deviation. If a float, it is assumed that the units are pixels, but a (value, unit) tuple or YTQuantity can be supplied to specify the standard deviation in physical units. Examples -------- >>> fid = FITSSlice(ds, "z", ("gas", "density")) >>> fid.convolve("density", (3.0, "kpc")) """ if self.dimensionality == 3: raise RuntimeError("Convolution currently only works for 2D FITSImageData!") conv = _astropy.conv if field not in self.keys(): raise KeyError(f"{field} not an image!") idx = self.fields.index(field) if not isinstance(kernel, conv.Kernel): if not isinstance(kernel, numeric_type): unit = str(self.wcs.wcs.cunit[0]) pix_scale = YTQuantity(self.wcs.wcs.cdelt[0], unit) if isinstance(kernel, tuple): stddev = YTQuantity(kernel[0], kernel[1]).to(unit) else: stddev = kernel.to(unit) kernel = stddev / pix_scale kernel = conv.Gaussian2DKernel(x_stddev=kernel) self.hdulist[idx].data = conv.convolve(self.hdulist[idx].data, kernel, **kwargs) def update_header(self, field, key, value): """ Update the FITS header for *field* with a *key*, *value* pair. If *field* == "all", all headers will be updated. """ if field == "all": for img in self.hdulist: img.header[key] = value else: if field not in self.keys(): raise KeyError(f"{field} not an image!") idx = self.fields.index(field) self.hdulist[idx].header[key] = value def keys(self): return self.fields def has_key(self, key): return key in self.fields def values(self): return [self[k] for k in self.fields] def items(self): return [(k, self[k]) for k in self.fields] def __getitem__(self, item): return UnitfulHDU(self.hdulist[item]) def __repr__(self): return str([self[k] for k in self.keys()]) def info(self, output=None): """ Summarize the info of the HDUs in this `FITSImageData` instance. Note that this function prints its results to the console---it does not return a value. Parameters ---------- output : file, boolean, optional A file-like object to write the output to. If `False`, does not output to a file and instead returns a list of tuples representing the FITSImageData info. Writes to ``sys.stdout`` by default. """ hinfo = self.hdulist.info(output=False) num_cols = len(hinfo[0]) if output is None: output = sys.stdout if num_cols == 8: header = "No. Name Ver Type Cards Dimensions Format Units" format = "{:3d} {:10} {:3} {:11} {:5d} {} {} {}" else: header = ( "No. Name Type Cards Dimensions Format Units" ) format = "{:3d} {:10} {:11} {:5d} {} {} {}" if self.hdulist._file is None: name = "(No file associated with this FITSImageData)" else: name = self.hdulist._file.name results = [f"Filename: {name}", header] for line in hinfo: units = self.field_units[self.hdulist[line[0]].header["btype"]] summary = tuple(list(line[:-1]) + [units]) if output: results.append(format.format(*summary)) else: results.append(summary) if output: output.write("\n".join(results)) output.write("\n") output.flush() else: return results[2:] @parallel_root_only def writeto(self, fileobj, fields=None, overwrite=False, **kwargs): r""" Write all of the fields or a subset of them to a FITS file. Parameters ---------- fileobj : string The name of the file to write to. fields : list of strings, optional The fields to write to the file. If not specified all of the fields in the buffer will be written. overwrite : boolean Whether or not to overwrite a previously existing file. Default: False **kwargs Additional keyword arguments are passed to :meth:`~astropy.io.fits.HDUList.writeto`. """ if fields is None: hdus = self.hdulist else: hdus = _astropy.pyfits.HDUList() for field in fields: hdus.append(self.hdulist[field]) hdus.writeto(fileobj, overwrite=overwrite, **kwargs) def to_glue(self, label="yt", data_collection=None): """ Takes the data in the FITSImageData instance and exports it to Glue (http://glueviz.org) for interactive analysis. Optionally add a *label*. If you are already within the Glue environment, you can pass a *data_collection* object, otherwise Glue will be started. """ from glue.core import Data, DataCollection from glue.core.coordinates import coordinates_from_header try: from glue.app.qt.application import GlueApplication except ImportError: from glue.qt.glue_application import GlueApplication image = Data(label=label) image.coords = coordinates_from_header(self.wcs.to_header()) for k in self.fields: image.add_component(self[k].data, k) if data_collection is None: dc = DataCollection([image]) app = GlueApplication(dc) app.start() else: data_collection.append(image) def to_aplpy(self, **kwargs): """ Use APLpy (http://aplpy.github.io) for plotting. Returns an `aplpy.FITSFigure` instance. All keyword arguments are passed to the `aplpy.FITSFigure` constructor. """ import aplpy return aplpy.FITSFigure(self.hdulist, **kwargs) def get_data(self, field): """ Return the data array of the image corresponding to *field* with units attached. Deprecated. """ return self[field].data def set_unit(self, field, units): """ Set the units of *field* to *units*. """ if field not in self.keys(): raise KeyError(f"{field} not an image!") idx = self.fields.index(field) new_data = YTArray(self.hdulist[idx].data, self.field_units[field]).to(units) self.hdulist[idx].data = new_data.v self.hdulist[idx].header["bunit"] = units self.field_units[field] = units def pop(self, key): """ Remove a field with name *key* and return it as a new FITSImageData instance. """ if key not in self.keys(): raise KeyError(f"{key} not an image!") idx = self.fields.index(key) im = self.hdulist.pop(idx) self.field_units.pop(key) self.fields.remove(key) f = _astropy.pyfits.PrimaryHDU(im.data, header=im.header) return FITSImageData(f, current_time=f.header["TIME"], unit_header=f.header) def close(self): self.hdulist.close() @classmethod def from_file(cls, filename): """ Generate a FITSImageData instance from one previously written to disk. Parameters ---------- filename : string The name of the file to open. """ f = _astropy.pyfits.open(filename, lazy_load_hdus=False) return cls(f, current_time=f[0].header["TIME"], unit_header=f[0].header) @classmethod def from_images(cls, image_list): """ Generate a new FITSImageData instance from a list of FITSImageData instances. Parameters ---------- image_list : list of FITSImageData instances The images to be combined. """ image_list = image_list if isinstance(image_list, list) else [image_list] first_image = first(image_list) w = first_image.wcs img_shape = first_image.shape data = [] for fid in image_list: assert_same_wcs(w, fid.wcs) if img_shape != fid.shape: raise RuntimeError("Images do not have the same shape!") for hdu in fid.hdulist: if len(data) == 0: data.append(_astropy.pyfits.PrimaryHDU(hdu.data, header=hdu.header)) else: data.append(_astropy.pyfits.ImageHDU(hdu.data, header=hdu.header)) data = _astropy.pyfits.HDUList(data) return cls( data, current_time=first_image.current_time, unit_header=first_image[0].header, ) def create_sky_wcs( self, sky_center, sky_scale, ctype=None, crota=None, cd=None, pc=None, wcsname="celestial", replace_old_wcs=True, ): """ Takes a Cartesian WCS and converts it to one in a sky-based coordinate system. Parameters ---------- sky_center : iterable of floats Reference coordinates of the WCS in degrees. sky_scale : tuple or YTQuantity Conversion between an angle unit and a length unit, e.g. (3.0, "arcsec/kpc") ctype : list of strings, optional The type of the coordinate system to create. Default: A "tangential" projection. crota : 2-element ndarray, optional Rotation angles between cartesian coordinates and the celestial coordinates. cd : 2x2-element ndarray, optional Dimensioned coordinate transformation matrix. pc : 2x2-element ndarray, optional Coordinate transformation matrix. wcsname : string, optional The name of the WCS to be stored in the FITS header. replace_old_wcs : boolean, optional If True, the original WCS will be overwritten but first copied to a second WCS ("WCSAXESA"). If False, this new WCS will be placed into the second WCS. """ if ctype is None: ctype = ["RA---TAN", "DEC--TAN"] old_wcs = self.wcs naxis = old_wcs.naxis crval = [sky_center[0], sky_center[1]] if isinstance(sky_scale, YTQuantity): scaleq = sky_scale else: scaleq = YTQuantity(sky_scale[0], sky_scale[1]) if scaleq.units.dimensions != dimensions.angle / dimensions.length: raise RuntimeError( f"sky_scale {sky_scale} not in correct dimensions of angle/length!" ) deltas = old_wcs.wcs.cdelt units = [str(unit) for unit in old_wcs.wcs.cunit] new_dx = (YTQuantity(-deltas[0], units[0]) * scaleq).in_units("deg") new_dy = (YTQuantity(deltas[1], units[1]) * scaleq).in_units("deg") new_wcs = _astropy.pywcs.WCS(naxis=naxis) cdelt = [new_dx.v, new_dy.v] cunit = ["deg"] * 2 if naxis == 3: crval.append(old_wcs.wcs.crval[2]) cdelt.append(old_wcs.wcs.cdelt[2]) ctype.append(old_wcs.wcs.ctype[2]) cunit.append(old_wcs.wcs.cunit[2]) new_wcs.wcs.crpix = old_wcs.wcs.crpix new_wcs.wcs.cdelt = cdelt new_wcs.wcs.crval = crval new_wcs.wcs.cunit = cunit new_wcs.wcs.ctype = ctype if crota is not None: new_wcs.wcs.crota = crota if cd is not None: new_wcs.wcs.cd = cd if pc is not None: new_wcs.wcs.cd = pc if replace_old_wcs: self.set_wcs(new_wcs, wcsname=wcsname) self.set_wcs(old_wcs, wcsname="yt", suffix="a") else: self.set_wcs(new_wcs, wcsname=wcsname, suffix="a") class FITSImageBuffer(FITSImageData): pass def sanitize_fits_unit(unit): if unit == "Mpc": mylog.info("Changing FITS file length unit to kpc.") unit = "kpc" elif unit == "au": unit = "AU" return unit # This list allows one to determine which axes are the # correct axes of the image in a right-handed coordinate # system depending on which axis is sliced or projected axis_wcs = [[1, 2], [2, 0], [0, 1]] def construct_image( ds, axis, data_source, center, image_res, width, length_unit, origin="domain" ): if width is None: width = ds.domain_width[axis_wcs[axis]] unit = ds.get_smallest_appropriate_unit(width[0]) mylog.info( "Making an image of the entire domain, " "so setting the center to the domain center." ) else: width = ds.coordinates.sanitize_width(axis, width, None) unit = str(width[0].units) if is_sequence(image_res): nx, ny = image_res else: nx, ny = image_res, image_res dx = width[0] / nx dy = width[1] / ny crpix = [0.5 * (nx + 1), 0.5 * (ny + 1)] if unit == "unitary": unit = ds.get_smallest_appropriate_unit(ds.domain_width.max()) elif unit == "code_length": unit = ds.get_smallest_appropriate_unit(ds.quan(1.0, "code_length")) unit = sanitize_fits_unit(unit) if length_unit is None: length_unit = unit if any(char.isdigit() for char in length_unit) and "*" in length_unit: length_unit = length_unit.split("*")[-1] cunit = [length_unit] * 2 ctype = ["LINEAR"] * 2 cdelt = [dx.in_units(length_unit), dy.in_units(length_unit)] if origin == "domain": if is_sequence(axis): crval = center.in_units(length_unit) else: crval = [center[idx].in_units(length_unit) for idx in axis_wcs[axis]] elif origin == "image": crval = np.zeros(2) if hasattr(data_source, "to_frb"): if is_sequence(axis): frb = data_source.to_frb(width[0], (nx, ny), height=width[1]) else: frb = data_source.to_frb(width[0], (nx, ny), center=center, height=width[1]) elif isinstance(data_source, ParticleDummyDataSource): if hasattr(data_source, "normal_vector"): # If we have a normal vector, this means # that the data source is off-axis bounds = (-width[0] / 2, width[0] / 2, -width[1] / 2, width[1] / 2) periodic = False else: # Otherwise, this is an on-axis data source axes = axis_wcs[axis] bounds = ( center[axes[0]] - width[0] / 2, center[axes[0]] + width[0] / 2, center[axes[1]] - width[1] / 2, center[axes[1]] + width[1] / 2, ) periodic = all(ds.periodicity) frb = ParticleImageBuffer(data_source, bounds, (nx, ny), periodic=periodic) else: frb = None w = _astropy.pywcs.WCS(naxis=2) w.wcs.crpix = crpix w.wcs.cdelt = cdelt w.wcs.crval = crval w.wcs.cunit = cunit w.wcs.ctype = ctype return w, frb, length_unit def assert_same_wcs(wcs1, wcs2): from numpy.testing import assert_allclose assert wcs1.naxis == wcs2.naxis for i in range(wcs1.naxis): assert wcs1.wcs.cunit[i] == wcs2.wcs.cunit[i] assert wcs1.wcs.ctype[i] == wcs2.wcs.ctype[i] assert_allclose(wcs1.wcs.crpix, wcs2.wcs.crpix) assert_allclose(wcs1.wcs.cdelt, wcs2.wcs.cdelt) assert_allclose(wcs1.wcs.crval, wcs2.wcs.crval) crota1 = getattr(wcs1.wcs, "crota", None) crota2 = getattr(wcs2.wcs, "crota", None) if crota1 is None or crota2 is None: assert crota1 == crota2 else: assert_allclose(wcs1.wcs.crota, wcs2.wcs.crota) cd1 = getattr(wcs1.wcs, "cd", None) cd2 = getattr(wcs2.wcs, "cd", None) if cd1 is None or cd2 is None: assert cd1 == cd2 else: assert_allclose(wcs1.wcs.cd, wcs2.wcs.cd) pc1 = getattr(wcs1.wcs, "pc", None) pc2 = getattr(wcs2.wcs, "pc", None) if pc1 is None or pc2 is None: assert pc1 == pc2 else: assert_allclose(wcs1.wcs.pc, wcs2.wcs.pc) class FITSSlice(FITSImageData): r""" Generate a FITSImageData of an on-axis slice. Parameters ---------- ds : :class:`~yt.data_objects.static_output.Dataset` The dataset object. axis : character or integer The axis of the slice. One of "x","y","z", or 0,1,2. fields : string or list of strings The fields to slice image_res : an int or 2-tuple of ints Specify the resolution of the resulting image. A single value will be used for both axes, whereas a tuple of values will be used for the individual axes. Default: 512 center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') specifies a width that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) specifies a width that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) specifies a width that has an x width of 0.2 and a y width of 0.3 in code units. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. origin : string The origin of the coordinate system in the file. If "domain", then the center coordinates will be the same as the center of the image as defined by the *center* keyword argument. If "image", then the center coordinates will be set to (0,0). Default: "domain" """ def __init__( self, ds, axis, fields, image_res=512, center="center", width=None, length_unit=None, *, origin="domain", **kwargs, ): fields = list(iter_fields(fields)) axis = fix_axis(axis, ds) center, dcenter = ds.coordinates.sanitize_center(center, axis) slc = ds.slice(axis, center[axis], **kwargs) w, frb, lunit = construct_image( ds, axis, slc, dcenter, image_res, width, length_unit, origin=origin, ) super().__init__(frb, fields=fields, length_unit=lunit, wcs=w) class FITSProjection(FITSImageData): r""" Generate a FITSImageData of an on-axis projection. Parameters ---------- ds : :class:`~yt.data_objects.static_output.Dataset` The dataset object. axis : character or integer The axis along which to project. One of "x","y","z", or 0,1,2. fields : string or list of strings The fields to project image_res : an int or 2-tuple of ints Specify the resolution of the resulting image. A single value will be used for both axes, whereas a tuple of values will be used for the individual axes. Default: 512 center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') specifies a width that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) specifies a width that is 10 kiloparsecs wide along the x-axis and 15 kiloparsecs wide along the y-axis. In the other two examples, code units are assumed, for example (0.2, 0.3) specifies a width that has an x width of 0.2 and a y width of 0.3 in code units. weight_field : string The field used to weight the projection. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. origin : string The origin of the coordinate system in the file. If "domain", then the center coordinates will be the same as the center of the image as defined by the *center* keyword argument. If "image", then the center coordinates will be set to (0,0). Default: "domain" moment : integer, optional for a weighted projection, moment = 1 (the default) corresponds to a weighted average. moment = 2 corresponds to a weighted standard deviation. """ def __init__( self, ds, axis, fields, image_res=512, center="center", width=None, weight_field=None, length_unit=None, *, origin="domain", moment=1, **kwargs, ): fields = list(iter_fields(fields)) axis = fix_axis(axis, ds) center, dcenter = ds.coordinates.sanitize_center(center, axis) prj = ds.proj( fields[0], axis, weight_field=weight_field, moment=moment, **kwargs ) w, frb, lunit = construct_image( ds, axis, prj, dcenter, image_res, width, length_unit, origin=origin, ) super().__init__(frb, fields=fields, length_unit=lunit, wcs=w) class FITSParticleProjection(FITSImageData): r""" Generate a FITSImageData of an on-axis projection of a particle field. Parameters ---------- ds : :class:`~yt.data_objects.static_output.Dataset` The dataset object. axis : character or integer The axis along which to project. One of "x","y","z", or 0,1,2. fields : string or list of strings The fields to project image_res : an int or 2-tuple of ints Specify the resolution of the resulting image. A single value will be used for both axes, whereas a tuple of values will be used for the individual axes. Default: 512 center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') specifies a width that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) specifies a width that is 10 kiloparsecs wide along the x-axis and 15 kiloparsecs wide along the y-axis. In the other two examples, code units are assumed, for example (0.2, 0.3) specifies a width that has an x width of 0.2 and a y width of 0.3 in code units. depth : A tuple or a float A tuple containing the depth to project through and the string key of the unit: (width, 'unit'). If set to a float, code units are assumed. Defaults to the entire domain. weight_field : string The field used to weight the projection. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. deposition : string, optional Controls the order of the interpolation of the particles onto the mesh. "ngp" is 0th-order "nearest-grid-point" method (the default), "cic" is 1st-order "cloud-in-cell". density : boolean, optional If True, the quantity to be projected will be divided by the area of the cells, to make a projected density of the quantity. Default: False field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source : yt.data_objects.data_containers.YTSelectionContainer, optional If specified, this will be the data source used for selecting regions to project. origin : string The origin of the coordinate system in the file. If "domain", then the center coordinates will be the same as the center of the image as defined by the *center* keyword argument. If "image", then the center coordinates will be set to (0,0). Default: "domain" """ def __init__( self, ds, axis, fields, image_res=512, center="center", width=None, depth=(1, "1"), weight_field=None, length_unit=None, deposition="ngp", density=False, field_parameters=None, data_source=None, *, origin="domain", ): fields = list(iter_fields(fields)) axis = fix_axis(axis, ds) center, dcenter = ds.coordinates.sanitize_center(center, axis) width = ds.coordinates.sanitize_width(axis, width, depth) width[-1].convert_to_units(width[0].units) if field_parameters is None: field_parameters = {} ps = ParticleAxisAlignedDummyDataSource( center, ds, axis, width, fields, weight_field=weight_field, field_parameters=field_parameters, data_source=data_source, deposition=deposition, density=density, ) w, frb, lunit = construct_image( ds, axis, ps, dcenter, image_res, width, length_unit, origin=origin ) super().__init__(frb, fields=fields, length_unit=lunit, wcs=w) class FITSParticleOffAxisProjection(FITSImageData): r""" Generate a FITSImageData of an off-axis projection of a particle field. Parameters ---------- ds : :class:`~yt.data_objects.static_output.Dataset` The dataset object. axis : character or integer The axis along which to project. One of "x","y","z", or 0,1,2. fields : string or list of strings The fields to project image_res : an int or 2-tuple of ints Specify the resolution of the resulting image. A single value will be used for both axes, whereas a tuple of values will be used for the individual axes. Default: 512 center : A sequence of floats, a string, or a tuple. The coordinate of the center of the image. If set to 'c', 'center' or left blank, the plot is centered on the middle of the domain. If set to 'max' or 'm', the center will be located at the maximum of the ('gas', 'density') field. Centering on the max or min of a specific field is supported by providing a tuple such as ("min","temperature") or ("max","dark_matter_density"). Units can be specified by passing in *center* as a tuple containing a coordinate and string unit name or by passing in a YTArray. If a list or unitless array is supplied, code units are assumed. width : tuple or a float. Width can have four different formats to support variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') specifies a width that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) specifies a width that is 10 kiloparsecs wide along the x-axis and 15 kiloparsecs wide along the y-axis. In the other two examples, code units are assumed, for example (0.2, 0.3) specifies a width that has an x width of 0.2 and a y width of 0.3 in code units. depth : A tuple or a float A tuple containing the depth to project through and the string key of the unit: (width, 'unit'). If set to a float, code units are assumed. Defaults to the entire domain. weight_field : string The field used to weight the projection. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. deposition : string, optional Controls the order of the interpolation of the particles onto the mesh. "ngp" is 0th-order "nearest-grid-point" method (the default), "cic" is 1st-order "cloud-in-cell". density : boolean, optional If True, the quantity to be projected will be divided by the area of the cells, to make a projected density of the quantity. Default: False field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source : yt.data_objects.data_containers.YTSelectionContainer, optional If specified, this will be the data source used for selecting regions to project. origin : string The origin of the coordinate system in the file. If "domain", then the center coordinates will be the same as the center of the image as defined by the *center* keyword argument. If "image", then the center coordinates will be set to (0,0). Default: "domain" """ def __init__( self, ds, normal, fields, image_res=512, center="c", width=None, depth=(1, "1"), weight_field=None, length_unit=None, deposition="ngp", density=False, field_parameters=None, data_source=None, north_vector=None, ): fields = list(iter_fields(fields)) center, dcenter = ds.coordinates.sanitize_center(center, None) width = ds.coordinates.sanitize_width(normal, width, depth) wd = tuple(el.in_units("code_length").v for el in width) if field_parameters is None: field_parameters = {} ps = ParticleOffAxisDummyDataSource( center, ds, normal, wd, fields, weight_field=weight_field, field_parameters=field_parameters, data_source=data_source, deposition=deposition, density=density, north_vector=north_vector, ) w, frb, lunit = construct_image( ds, normal, ps, dcenter, image_res, width, length_unit, origin="image", ) super().__init__(frb, fields=fields, length_unit=lunit, wcs=w) class FITSOffAxisSlice(FITSImageData): r""" Generate a FITSImageData of an off-axis slice. Parameters ---------- ds : :class:`~yt.data_objects.static_output.Dataset` The dataset object. normal : a sequence of floats The vector normal to the projection plane. fields : string or list of strings The fields to slice image_res : an int or 2-tuple of ints Specify the resolution of the resulting image. A single value will be used for both axes, whereas a tuple of values will be used for the individual axes. Default: 512 center : 'center', 'c', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. width : tuple or a float. Width can have four different formats to support variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') specifies a width that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) specifies a width that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) specifies a width that has an x width of 0.2 and a y width of 0.3 in code units. north_vector : a sequence of floats A vector defining the 'up' direction in the plot. This option sets the orientation of the slicing plane. If not set, an arbitrary grid-aligned north-vector is chosen. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. """ def __init__( self, ds, normal, fields, image_res=512, center="center", width=None, north_vector=None, length_unit=None, ): fields = list(iter_fields(fields)) center, dcenter = ds.coordinates.sanitize_center(center, axis=None) cut = ds.cutting(normal, center, north_vector=north_vector) center = ds.arr([0.0] * 2, "code_length") w, frb, lunit = construct_image( ds, normal, cut, center, image_res, width, length_unit ) super().__init__(frb, fields=fields, length_unit=lunit, wcs=w) class FITSOffAxisProjection(FITSImageData): r""" Generate a FITSImageData of an off-axis projection. Parameters ---------- ds : :class:`~yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : a sequence of floats The vector normal to the projection plane. fields : string, list of strings The name of the field(s) to be plotted. image_res : an int or 2-tuple of ints Specify the resolution of the resulting image. A single value will be used for both axes, whereas a tuple of values will be used for the individual axes. Default: 512 center : 'center', 'c', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. width : tuple or a float. Width can have four different formats to support variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') specifies a width that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) specifies a width that is 10 kiloparsecs wide along the x-axis and 15 kiloparsecs wide along the y-axis. In the other two examples, code units are assumed, for example (0.2, 0.3) specifies a width that has an x width of 0.2 and a y width of 0.3 in code units. depth : A tuple or a float A tuple containing the depth to project through and the string key of the unit: (width, 'unit'). If set to a float, code units are assumed weight_field : string The name of the weighting field. Set to None for no weight. north_vector : a sequence of floats A vector defining the 'up' direction in the plot. This option sets the orientation of the slicing plane. If not set, an arbitrary grid-aligned north-vector is chosen. method : string The method of projection. Valid methods are: "integrate" with no weight_field specified : integrate the requested field along the line of sight. "integrate" with a weight_field specified : weight the requested field by the weighting field and integrate along the line of sight. "sum" : This method is the same as integrate, except that it does not multiply by a path length when performing the integration, and is just a straight summation of the field along the given axis. WARNING: This should only be used for uniform resolution grid datasets, as other datasets may result in unphysical images. data_source : yt.data_objects.data_containers.YTSelectionContainer, optional If specified, this will be the data source used for selecting regions to project. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. moment : integer, optional for a weighted projection, moment = 1 (the default) corresponds to a weighted average. moment = 2 corresponds to a weighted standard deviation. """ def __init__( self, ds, normal, fields, center="center", width=(1.0, "unitary"), weight_field=None, image_res=512, data_source=None, north_vector=None, depth=(1.0, "unitary"), method="integrate", length_unit=None, *, moment=1, ): validate_moment(moment, weight_field) center, dcenter = ds.coordinates.sanitize_center(center, axis=None) fields = list(iter_fields(fields)) buf = {} width = ds.coordinates.sanitize_width(normal, width, depth) wd = tuple(el.in_units("code_length").v for el in width) if not is_sequence(image_res): image_res = (image_res, image_res) res = (image_res[0], image_res[1]) if data_source is None: source = ds.all_data() else: source = data_source fields = source._determine_fields(list(iter_fields(fields))) stddev_str = "_stddev" if moment == 2 else "" for item in fields: ftype, fname = item key = (ftype, f"{fname}{stddev_str}") buf[key] = off_axis_projection( source, center, normal, wd, res, item, north_vector=north_vector, method=method, weight=weight_field, depth=depth, ).swapaxes(0, 1) if moment == 2: def _sq_field(field, data, item: FieldKey): return data[item] ** 2 fd = ds._get_field_info(item) field_sq = (ftype, f"tmp_{fname}_squared") ds.add_field( field_sq, partial(_sq_field, item=item), sampling_type=fd.sampling_type, units=f"({fd.units})*({fd.units})", ) buff2 = off_axis_projection( source, center, normal, wd, res, field_sq, north_vector=north_vector, method=method, weight=weight_field, depth=depth, ).swapaxes(0, 1) buf[key] = compute_stddev_image(buff2, buf[key]) ds.field_info.pop(field_sq) center = ds.arr([0.0] * 2, "code_length") w, not_an_frb, lunit = construct_image( ds, normal, buf, center, image_res, width, length_unit ) super().__init__(buf, fields=list(buf.keys()), wcs=w, length_unit=lunit, ds=ds) yt-project-yt-f043ac8/yt/visualization/fixed_resolution.py000066400000000000000000000737551510711153200241640ustar00rootroot00000000000000import sys import weakref from functools import partial from typing import TYPE_CHECKING import numpy as np from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import FieldKey, MaskT from yt.data_objects.image_array import ImageArray from yt.frontends.ytdata.utilities import save_as_dataset from yt.funcs import get_output_filename, iter_fields, mylog from yt.loaders import load_uniform_grid from yt.utilities.lib.api import ( # type: ignore CICDeposit_2, add_points_to_greyscale_image, ) from yt.utilities.lib.pixelization_routines import ( pixelize_cylinder, rotate_particle_coord_pib, ) from yt.utilities.math_utils import compute_stddev_image from yt.utilities.on_demand_imports import _h5py as h5py from .volume_rendering.api import off_axis_projection if sys.version_info >= (3, 12): from typing import override else: from typing_extensions import override if TYPE_CHECKING: from yt.visualization.fixed_resolution_filters import FixedResolutionBufferFilter class FixedResolutionBuffer: r""" FixedResolutionBuffer(data_source, bounds, buff_size, antialias = True) This accepts a 2D data object, such as a Projection or Slice, and implements a protocol for generating a pixelized, fixed-resolution image buffer. yt stores 2D AMR data internally as a set of 2D coordinates and the half-width of individual pixels. Converting this to an image buffer requires a deposition step, where individual variable-resolution pixels are deposited into a buffer of some resolution, to create an image. This object is an interface to that pixelization step: it can deposit multiple fields. It acts as a standard YTDataContainer object, such that dict-style access returns an image of a given field. Parameters ---------- data_source : :class:`yt.data_objects.construction_data_containers.YTQuadTreeProj` or :class:`yt.data_objects.selection_data_containers.YTSlice` This is the source to be pixelized, which can be a projection, slice or cutting plane. bounds : sequence of floats Bounds are the min and max in the image plane that we want our image to cover. It's in the order of (xmin, xmax, ymin, ymax), where the coordinates are all in the appropriate code units. buff_size : sequence of ints The size of the image to generate. antialias : boolean This can be true or false. It determines whether or not sub-pixel rendering is used during data deposition. periodic : boolean This can be true or false, and governs whether the pixelization will span the domain boundaries. filters : list of FixedResolutionBufferFilter objects (optional) Examples -------- To make a projection and then several images, you can generate a single FRB and then access multiple fields: >>> proj = ds.proj(0, ("gas", "density")) >>> frb1 = FixedResolutionBuffer(proj, (0.2, 0.3, 0.4, 0.5), (1024, 1024)) >>> print(frb1["gas", "density"].max()) 1.0914e-9 g/cm**3 >>> print(frb1["gas", "temperature"].max()) 104923.1 K """ _exclude_fields = ( "pz", "pdz", "dx", "x", "y", "z", "r", "dr", "phi", "dphi", "theta", "dtheta", ("index", "dx"), ("index", "x"), ("index", "y"), ("index", "z"), ("index", "r"), ("index", "dr"), ("index", "phi"), ("index", "dphi"), ("index", "theta"), ("index", "dtheta"), ) def __init__( self, data_source, bounds, buff_size, antialias=True, periodic=False, *, filters: list["FixedResolutionBufferFilter"] | None = None, ): self.data_source = data_source self.ds = data_source.ds self.bounds = bounds self.buff_size = (int(buff_size[0]), int(buff_size[1])) self.antialias = antialias self.data: dict[str, ImageArray] = {} self.mask: dict[str, MaskT] = {} self.axis = data_source.axis self.periodic = periodic self._data_valid = False # import type here to avoid import cycles # note that this import statement is actually crucial at runtime: # the filter methods for the present class are defined only when # fixed_resolution_filters is imported, so we need to guarantee # that it happens no later than instantiation from yt.visualization.fixed_resolution_filters import ( # noqa FixedResolutionBufferFilter, ) self._filters: list[FixedResolutionBufferFilter] = ( filters if filters is not None else [] ) ds = getattr(data_source, "ds", None) if ds is not None: ds.plots.append(weakref.proxy(self)) # Handle periodicity, just in case if self.data_source.axis is not None: DLE = self.ds.domain_left_edge DRE = self.ds.domain_right_edge DD = float(self.periodic) * (DRE - DLE) axis = self.data_source.axis xax = self.ds.coordinates.x_axis[axis] yax = self.ds.coordinates.y_axis[axis] self._period = (DD[xax], DD[yax]) self._edges = ((DLE[xax], DRE[xax]), (DLE[yax], DRE[yax])) def keys(self): return self.data.keys() def __delitem__(self, item): del self.data[item] def _generate_image_and_mask(self, item) -> None: mylog.info( "Making a fixed resolution buffer of (%s) %d by %d", item, self.buff_size[0], self.buff_size[1], ) bounds = [] for b in self.bounds: if hasattr(b, "in_units"): b = float(b.in_units("code_length")) bounds.append(b) buff, mask = self.ds.coordinates.pixelize( self.data_source.axis, self.data_source, item, bounds, self.buff_size, int(self.antialias), return_mask=True, ) buff = self._apply_filters(buff) # FIXME FIXME FIXME we shouldn't need to do this for projections # but that will require fixing data object access for particle # projections try: if hasattr(item, "name"): it = item.name else: it = item units = self.data_source._projected_units[it] except (KeyError, AttributeError): units = self.data_source[item].units self.data[item] = ImageArray(buff, units=units, info=self._get_info(item)) self.mask[item] = mask self._data_valid = True def __getitem__(self, item): # backward compatibility return self.get_image(item) def get_image(self, key, /) -> ImageArray: if not (key in self.data and self._data_valid): self._generate_image_and_mask(key) return self.data[key] def get_mask(self, key, /) -> MaskT: """Return the boolean array associated with an image with the same key. Elements set to True indicate pixels that were updated by a pixelisation routine """ if not (key in self.mask and self._data_valid): self._generate_image_and_mask(key) return self.mask[key] def render(self, item): # deleguate to __getitem__ for historical reasons # this method exists for clarity of intention return self[item] def _apply_filters(self, buffer: np.ndarray) -> np.ndarray: for f in self._filters: buffer = f(buffer) return buffer def __setitem__(self, item, val): self.data[item] = val def _get_data_source_fields(self): exclude = self.data_source._key_fields + list(self._exclude_fields) fields = getattr(self.data_source, "fields", []) fields += getattr(self.data_source, "field_data", {}).keys() for f in fields: if f not in exclude and f[0] not in self.data_source.ds.particle_types: self.render(f) def _get_info(self, item): info = {} ftype, fname = field = self.data_source._determine_fields(item)[0] finfo = self.data_source.ds._get_field_info(field) info["data_source"] = self.data_source.__str__() info["axis"] = self.data_source.axis info["field"] = str(item) info["xlim"] = self.bounds[:2] info["ylim"] = self.bounds[2:] info["length_unit"] = self.data_source.ds.length_unit info["length_to_cm"] = info["length_unit"].in_cgs().to_ndarray() info["center"] = self.data_source.center try: info["coord"] = self.data_source.coord except AttributeError: pass try: info["weight_field"] = self.data_source.weight_field except AttributeError: pass info["label"] = finfo.get_latex_display_name() return info def convert_to_pixel(self, coords): r"""This function converts coordinates in code-space to pixel-space. Parameters ---------- coords : sequence of array_like This is (x_coord, y_coord). Because of the way the math is done, these can both be arrays. Returns ------- output : sequence of array_like This returns px_coord, py_coord """ dpx = (self.bounds[1] - self.bounds[0]) / self.buff_size[0] dpy = (self.bounds[3] - self.bounds[2]) / self.buff_size[1] px = (coords[0] - self.bounds[0]) / dpx py = (coords[1] - self.bounds[2]) / dpy return (px, py) def convert_distance_x(self, distance): r"""This function converts code-space distance into pixel-space distance in the x-coordinate. Parameters ---------- distance : array_like This is x-distance in code-space you would like to convert. Returns ------- output : array_like The return value is the distance in the y-pixel coordinates. """ dpx = (self.bounds[1] - self.bounds[0]) / self.buff_size[0] return distance / dpx def convert_distance_y(self, distance): r"""This function converts code-space distance into pixel-space distance in the y-coordinate. Parameters ---------- distance : array_like This is y-distance in code-space you would like to convert. Returns ------- output : array_like The return value is the distance in the x-pixel coordinates. """ dpy = (self.bounds[3] - self.bounds[2]) / self.buff_size[1] return distance / dpy def set_unit(self, field, unit, equivalency=None, equivalency_kwargs=None): """Sets a new unit for the requested field parameters ---------- field : string or field tuple The name of the field that is to be changed. unit : string or Unit object The name of the new unit. equivalency : string, optional If set, the equivalency to use to convert the current units to the new requested unit. If None, the unit conversion will be done without an equivalency equivalency_kwargs : string, optional Keyword arguments to be passed to the equivalency. Only used if ``equivalency`` is set. """ if equivalency_kwargs is None: equivalency_kwargs = {} field = self.data_source._determine_fields(field)[0] if equivalency is None: self[field].convert_to_units(unit) else: equiv_array = self[field].to_equivalent( unit, equivalency, **equivalency_kwargs ) # equiv_array isn't necessarily an ImageArray. This is an issue # inherent to the way the unit system handles YTArray # subclasses and I don't see how to modify the unit system to # fix this. Instead, we paper over this issue and hard code # that equiv_array is an ImageArray self[field] = ImageArray( equiv_array, equiv_array.units, equiv_array.units.registry, self[field].info, ) def export_hdf5(self, filename, fields=None): r"""Export a set of fields to a set of HDF5 datasets. This function will export any number of fields into datasets in a new HDF5 file. Parameters ---------- filename : string This file will be opened in "append" mode. fields : list of strings These fields will be pixelized and output. """ if fields is None: fields = list(self.data.keys()) output = h5py.File(filename, mode="a") for field in fields: output.create_dataset("_".join(field), data=self[field]) output.close() def to_fits_data(self, fields=None, other_keys=None, length_unit=None, **kwargs): r"""Export the fields in this FixedResolutionBuffer instance to a FITSImageData instance. This will export a set of FITS images of either the fields specified or all the fields already in the object. Parameters ---------- fields : list of strings These fields will be pixelized and output. If "None", the keys of the FRB will be used. other_keys : dictionary, optional A set of header keys and values to write into the FITS header. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. """ from yt.visualization.fits_image import FITSImageData if length_unit is None: length_unit = self.ds.length_unit if fields is None: fields = list(self.data.keys()) else: fields = list(iter_fields(fields)) if len(fields) == 0: raise RuntimeError( "No fields to export. Either pass a field or list of fields to " "to_fits_data or access a field from the FixedResolutionBuffer " "object." ) fid = FITSImageData(self, fields=fields, length_unit=length_unit) if other_keys is not None: for k, v in other_keys.items(): fid.update_header("all", k, v) return fid def export_dataset(self, fields=None, nprocs=1): r"""Export a set of pixelized fields to an in-memory dataset that can be analyzed as any other in yt. Unit information and other parameters (e.g., geometry, current_time, etc.) will be taken from the parent dataset. Parameters ---------- fields : list of strings, optional These fields will be pixelized and output. If "None", the keys of the FRB will be used. nprocs: integer, optional If greater than 1, will create this number of subarrays out of data Examples -------- >>> import yt >>> ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150") >>> slc = ds.slice(2, 0.0) >>> frb = slc.to_frb((500.0, "kpc"), 500) >>> ds2 = frb.export_dataset( ... fields=[("gas", "density"), ("gas", "temperature")], nprocs=32 ... ) """ nx, ny = self.buff_size data = {} if fields is None: fields = list(self.keys()) for field in fields: arr = self[field] data[field] = (arr.d.T.reshape(nx, ny, 1), str(arr.units)) bounds = [b.in_units("code_length").v for b in self.bounds] bbox = np.array([[bounds[0], bounds[1]], [bounds[2], bounds[3]], [0.0, 1.0]]) return load_uniform_grid( data, [nx, ny, 1], length_unit=self.ds.length_unit, bbox=bbox, sim_time=self.ds.current_time.in_units("s").v, mass_unit=self.ds.mass_unit, time_unit=self.ds.time_unit, velocity_unit=self.ds.velocity_unit, magnetic_unit=self.ds.magnetic_unit, periodicity=(False, False, False), geometry=self.ds.geometry, nprocs=nprocs, ) def save_as_dataset(self, filename=None, fields=None): r"""Export a fixed resolution buffer to a reloadable yt dataset. This function will take a fixed resolution buffer and output a dataset containing either the fields presently existing or fields given in the ``fields`` list. The resulting dataset can be reloaded as a yt dataset. Parameters ---------- filename : str, optional The name of the file to be written. If None, the name will be a combination of the original dataset and the type of data container. fields : list of strings or tuples, optional If this is supplied, it is the list of fields to be saved to disk. If not supplied, all the fields that have been queried will be saved. Returns ------- filename : str The name of the file that has been created. Examples -------- >>> import yt >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") >>> proj = ds.proj(("gas", "density"), "x", weight_field=("gas", "density")) >>> frb = proj.to_frb(1.0, (800, 800)) >>> fn = frb.save_as_dataset(fields=[("gas", "density")]) >>> ds2 = yt.load(fn) >>> print(ds2.data["gas", "density"]) [[ 1.25025353e-30 1.25025353e-30 1.25025353e-30 ..., 7.90820691e-31 7.90820691e-31 7.90820691e-31] [ 1.25025353e-30 1.25025353e-30 1.25025353e-30 ..., 7.90820691e-31 7.90820691e-31 7.90820691e-31] [ 1.25025353e-30 1.25025353e-30 1.25025353e-30 ..., 7.90820691e-31 7.90820691e-31 7.90820691e-31] ..., [ 1.55834239e-30 1.55834239e-30 1.55834239e-30 ..., 8.51353199e-31 8.51353199e-31 8.51353199e-31] [ 1.55834239e-30 1.55834239e-30 1.55834239e-30 ..., 8.51353199e-31 8.51353199e-31 8.51353199e-31] [ 1.55834239e-30 1.55834239e-30 1.55834239e-30 ..., 8.51353199e-31 8.51353199e-31 8.51353199e-31]] g/cm**3 """ keyword = f"{str(self.ds)}_{self.data_source._type_name}_frb" filename = get_output_filename(filename, keyword, ".h5") data = {} if fields is not None: for f in self.data_source._determine_fields(fields): data[f] = self[f] else: data.update(self.data) ftypes = {field: "grid" for field in data} extra_attrs = { arg: getattr(self.data_source, arg, None) for arg in self.data_source._con_args + self.data_source._tds_attrs } extra_attrs["con_args"] = self.data_source._con_args extra_attrs["left_edge"] = self.ds.arr([self.bounds[0], self.bounds[2]]) extra_attrs["right_edge"] = self.ds.arr([self.bounds[1], self.bounds[3]]) # The data dimensions are [NY, NX] but buff_size is [NX, NY]. extra_attrs["ActiveDimensions"] = self.buff_size[::-1] extra_attrs["level"] = 0 extra_attrs["data_type"] = "yt_frb" extra_attrs["container_type"] = self.data_source._type_name extra_attrs["dimensionality"] = self.data_source._dimensionality save_as_dataset( self.ds, filename, data, field_types=ftypes, extra_attrs=extra_attrs ) return filename @property def limits(self): rv = {"x": None, "y": None, "z": None} xax = self.ds.coordinates.x_axis[self.axis] yax = self.ds.coordinates.y_axis[self.axis] xn = self.ds.coordinates.axis_name[xax] yn = self.ds.coordinates.axis_name[yax] rv[xn] = (self.bounds[0], self.bounds[1]) rv[yn] = (self.bounds[2], self.bounds[3]) return rv def setup_filters(self): issue_deprecation_warning( "The FixedResolutionBuffer.setup_filters method is now a no-op. ", stacklevel=3, since="4.1", ) class CylindricalFixedResolutionBuffer(FixedResolutionBuffer): """ This object is a subclass of :class:`yt.visualization.fixed_resolution.FixedResolutionBuffer` that supports non-aligned input data objects, primarily cutting planes. """ def __init__(self, data_source, radius, buff_size, antialias=True, *, filters=None): self.data_source = data_source self.ds = data_source.ds self.radius = radius self.buff_size = buff_size self.antialias = antialias self.data = {} self._filters = filters if filters is not None else [] ds = getattr(data_source, "ds", None) if ds is not None: ds.plots.append(weakref.proxy(self)) @override def _generate_image_and_mask(self, item) -> None: buff = np.zeros(self.buff_size, dtype="f8") mask = pixelize_cylinder( buff, self.data_source["r"], self.data_source["dr"], self.data_source["theta"], self.data_source["dtheta"], self.data_source[item].astype("float64"), self.radius, return_mask=True, ) self.data[item] = ImageArray( buff, units=self.data_source[item].units, info=self._get_info(item) ) self.mask[item] = mask class OffAxisProjectionFixedResolutionBuffer(FixedResolutionBuffer): """ This object is a subclass of :class:`yt.visualization.fixed_resolution.FixedResolutionBuffer` that supports off axis projections. This calls the volume renderer. """ @override def _generate_image_and_mask(self, item) -> None: mylog.info( "Making a fixed resolution buffer of (%s) %d by %d", item, self.buff_size[0], self.buff_size[1], ) dd = self.data_source # only need the first two for SPH, # but need the third one for other data formats. width = self.ds.arr( ( self.bounds[1] - self.bounds[0], self.bounds[3] - self.bounds[2], self.bounds[5] - self.bounds[4], ) ) buff = off_axis_projection( dd.dd, dd.center, dd.normal_vector, width, self.buff_size, item, weight=dd.weight_field, volume=dd.volume, no_ghost=dd.no_ghost, interpolated=dd.interpolated, north_vector=dd.north_vector, depth=dd.depth, method=dd.method, ) if self.data_source.moment == 2: def _sq_field(field, data, item: FieldKey): return data[item] ** 2 fd = self.ds._get_field_info(item) ftype, fname = item item_sq = (ftype, f"tmp_{fname}_squared") self.ds.add_field( item_sq, partial(_sq_field, item=item), sampling_type=fd.sampling_type, units=f"({fd.units})*({fd.units})", ) buff2 = off_axis_projection( dd.dd, dd.center, dd.normal_vector, width, self.buff_size, item_sq, weight=dd.weight_field, volume=dd.volume, no_ghost=dd.no_ghost, interpolated=dd.interpolated, north_vector=dd.north_vector, depth=dd.depth, method=dd.method, ) buff = compute_stddev_image(buff2, buff) self.ds.field_info.pop(item_sq) ia = ImageArray(buff.swapaxes(0, 1), info=self._get_info(item)) self.data[item] = ia self.mask[item] = None class ParticleImageBuffer(FixedResolutionBuffer): """ This object is a subclass of :class:`yt.visualization.fixed_resolution.FixedResolutionBuffer` that supports particle plots. It splats points onto an image buffer. """ def __init__( self, data_source, bounds, buff_size, antialias=True, periodic=False, *, filters=None, ): super().__init__( data_source, bounds, buff_size, antialias, periodic, filters=filters ) # set up the axis field names axis = self.axis if axis is not None: self.xax = self.ds.coordinates.x_axis[axis] self.yax = self.ds.coordinates.y_axis[axis] axis_name = self.ds.coordinates.axis_name self.x_field = f"particle_position_{axis_name[self.xax]}" self.y_field = f"particle_position_{axis_name[self.yax]}" @override def _generate_image_and_mask(self, item) -> None: deposition = self.data_source.deposition density = self.data_source.density mylog.info( "Splatting (%s) onto a %d by %d mesh using method '%s'", item, self.buff_size[0], self.buff_size[1], deposition, ) dd = self.data_source.dd ftype = item[0] if self.axis is None: wd = [] for w in self.data_source.width: if hasattr(w, "to_value"): w = w.to_value("code_length") wd.append(w) x_data, y_data, *bounds = rotate_particle_coord_pib( dd[ftype, "particle_position_x"].to_value("code_length"), dd[ftype, "particle_position_y"].to_value("code_length"), dd[ftype, "particle_position_z"].to_value("code_length"), self.data_source.center.to_value("code_length"), wd, self.data_source.normal_vector, self.data_source.north_vector, ) x_data = np.array(x_data) y_data = np.array(y_data) else: bounds = [] for b in self.bounds: if hasattr(b, "to_value"): b = b.to_value("code_length") bounds.append(b) x_data = dd[ftype, self.x_field].to_value("code_length") y_data = dd[ftype, self.y_field].to_value("code_length") data = dd[item] # handle periodicity dx = x_data - bounds[0] dy = y_data - bounds[2] if self.periodic: dx %= float(self._period[0].in_units("code_length")) dy %= float(self._period[1].in_units("code_length")) # convert to pixels px = dx / (bounds[1] - bounds[0]) py = dy / (bounds[3] - bounds[2]) # select only the particles that will actually show up in the image mask = np.logical_and( np.logical_and(px >= 0.0, px <= 1.0), np.logical_and(py >= 0.0, py <= 1.0) ) weight_field = self.data_source.weight_field if weight_field is None: weight_data = np.ones_like(data.v) else: weight_data = dd[weight_field] splat_vals = weight_data[mask] * data[mask] x_bin_edges = np.linspace(0.0, 1.0, self.buff_size[0] + 1) y_bin_edges = np.linspace(0.0, 1.0, self.buff_size[1] + 1) # splat particles buff = np.zeros(self.buff_size) buff_mask = np.zeros_like(buff, dtype="uint8") if deposition == "ngp": add_points_to_greyscale_image( buff, buff_mask, px[mask], py[mask], splat_vals ) elif deposition == "cic": CICDeposit_2( py[mask], px[mask], splat_vals, mask.sum(), buff, buff_mask, x_bin_edges, y_bin_edges, ) else: raise ValueError(f"Received unknown deposition method '{deposition}'") # remove values in no-particle region buff[buff_mask == 0] = np.nan # Normalize by the surface area of the pixel or volume of pencil if # requested info = self._get_info(item) if density: dpx = (bounds[1] - bounds[0]) / self.buff_size[0] dpy = (bounds[3] - bounds[2]) / self.buff_size[1] norm = self.ds.quan(dpx * dpy, "code_length**2").in_base() buff /= norm.v units = data.units / norm.units info["label"] += " $\\rm{Density}$" else: units = data.units # divide by the weight_field, if needed if weight_field is not None: weight_buff = np.zeros(self.buff_size) weight_buff_mask = np.zeros(self.buff_size, dtype="uint8") if deposition == "ngp": add_points_to_greyscale_image( weight_buff, weight_buff_mask, px[mask], py[mask], weight_data[mask] ) elif deposition == "cic": CICDeposit_2( py[mask], px[mask], weight_data[mask], mask.sum(), weight_buff, weight_buff_mask, y_bin_edges, x_bin_edges, ) # remove values in no-particle region weight_buff[weight_buff_mask == 0] = np.nan locs = np.where(weight_buff > 0) buff[locs] /= weight_buff[locs] self.data[item] = ImageArray(buff, units=units, info=info) self.mask[item] = buff_mask != 0 # over-ride the base class version, since we don't want to exclude # particle fields def _get_data_source_fields(self): exclude = self.data_source._key_fields + list(self._exclude_fields) fields = getattr(self.data_source, "fields", []) fields += getattr(self.data_source, "field_data", {}).keys() for f in fields: if f not in exclude: self.render(f) yt-project-yt-f043ac8/yt/visualization/fixed_resolution_filters.py000066400000000000000000000071411510711153200256760ustar00rootroot00000000000000from abc import ABC, abstractmethod from functools import update_wrapper, wraps import numpy as np from yt._maintenance.deprecation import issue_deprecation_warning from yt.visualization.fixed_resolution import FixedResolutionBuffer def apply_filter(f): issue_deprecation_warning( "The apply_filter decorator is not used in yt any more and " "will be removed in a future version. " "Please do not use it.", stacklevel=3, since="4.1", ) @wraps(f) def newfunc(self, *args, **kwargs): self._filters.append((f.__name__, (args, kwargs))) # Invalidate the data of the frb to force its regeneration self._data_valid = False return self return newfunc class FixedResolutionBufferFilter(ABC): """ This object allows to apply data transformation directly to :class:`yt.visualization.fixed_resolution.FixedResolutionBuffer` """ def __init_subclass__(cls, *args, **kwargs): if cls.__init__.__doc__ is None: # allow docstring definition at the class level instead of __init__ cls.__init__.__doc__ = cls.__doc__ # add a method to FixedResolutionBuffer method_name = "apply_" + cls._filter_name def closure(self, *args, **kwargs): self._filters.append(cls(*args, **kwargs)) self._data_valid = False return self update_wrapper( wrapper=closure, wrapped=cls.__init__, assigned=("__annotations__", "__doc__"), ) closure.__name__ = method_name setattr(FixedResolutionBuffer, method_name, closure) @abstractmethod def __init__(self, *args, **kwargs): """This method is required in subclasses, but the signature is arbitrary""" pass @abstractmethod def apply(self, buff: np.ndarray) -> np.ndarray: pass def __call__(self, buff: np.ndarray) -> np.ndarray: # alias to apply return self.apply(buff) class FixedResolutionBufferGaussBeamFilter(FixedResolutionBufferFilter): """ This filter convolves :class:`yt.visualization.fixed_resolution.FixedResolutionBuffer` with 2d gaussian that is 'nbeam' pixels wide and has standard deviation 'sigma'. """ _filter_name = "gauss_beam" def __init__(self, nbeam=30, sigma=2.0): self.nbeam = nbeam self.sigma = sigma def apply(self, buff): from yt.utilities.on_demand_imports import _scipy hnbeam = self.nbeam // 2 sigma = self.sigma l = np.linspace(-hnbeam, hnbeam, num=self.nbeam + 1) x, y = np.meshgrid(l, l) g2d = (1.0 / (sigma * np.sqrt(2.0 * np.pi))) * np.exp( -((x / sigma) ** 2 + (y / sigma) ** 2) / (2 * sigma**2) ) g2d /= g2d.max() npm, nqm = np.shape(buff) spl = _scipy.signal.convolve(buff, g2d) return spl[hnbeam : npm + hnbeam, hnbeam : nqm + hnbeam] class FixedResolutionBufferWhiteNoiseFilter(FixedResolutionBufferFilter): """ This filter adds white noise with the amplitude "bg_lvl" to :class:`yt.visualization.fixed_resolution.FixedResolutionBuffer`. If "bg_lvl" is not present, 10th percentile of the FRB's value is used instead. """ _filter_name = "white_noise" def __init__(self, bg_lvl=None): self.bg_lvl = bg_lvl def apply(self, buff): if self.bg_lvl is None: amp = np.percentile(buff, 10) else: amp = self.bg_lvl npm, nqm = np.shape(buff) rng = np.random.default_rng() return buff + rng.standard_normal((npm, nqm)) * amp yt-project-yt-f043ac8/yt/visualization/geo_plot_utils.py000066400000000000000000000070431510711153200236150ustar00rootroot00000000000000from types import FunctionType from typing import Any valid_transforms: dict[str, FunctionType] = {} transform_list = [ "PlateCarree", "LambertConformal", "LambertCylindrical", "Mercator", "Miller", "Mollweide", "Orthographic", "Robinson", "Stereographic", "TransverseMercator", "InterruptedGoodeHomolosine", "RotatedPole", "OSGB", "EuroPP", "Geostationary", "Gnomonic", "NorthPolarStereo", "OSNI", "SouthPolarStereo", "AlbersEqualArea", "AzimuthalEquidistant", "Sinusoidal", "UTM", "NearsidePerspective", "LambertAzimuthalEqualArea", ] def cartopy_importer(transform_name): r"""Convenience function to import cartopy projection types""" def _func(*args, **kwargs): from yt.utilities.on_demand_imports import _cartopy as cartopy return getattr(cartopy.crs, transform_name)(*args, **kwargs) return _func def get_mpl_transform(mpl_proj) -> FunctionType | None: r"""This returns an instantiated transform function given a transform function name and arguments. Parameters ---------- mpl_proj : string or tuple the matplotlib projection type. Can take the form of string or tuple. Examples -------- >>> get_mpl_transform("PlateCarree") >>> get_mpl_transform( ... ("RotatedPole", (), {"pole_latitude": 37.5, "pole_longitude": 177.5}) ... ) """ # first check to see if the transform dict is empty, if it is fill it with # the cartopy functions if not valid_transforms: for mpl_transform in transform_list: valid_transforms[mpl_transform] = cartopy_importer(mpl_transform) # check to see if mpl_proj is a string or tuple, and construct args and # kwargs to pass to cartopy function based on that. key: str | None = None args: tuple = () kwargs: dict[str, Any] = {} if isinstance(mpl_proj, str): key = mpl_proj instantiated_func = valid_transforms[key](*args, **kwargs) elif isinstance(mpl_proj, tuple): if len(mpl_proj) == 2: key, args = mpl_proj kwargs = {} elif len(mpl_proj) == 3: key, args, kwargs = mpl_proj else: raise ValueError(f"Expected a tuple with len 2 or 3, received {mpl_proj}") if not isinstance(key, str): raise TypeError( f"Expected a string a the first element in mpl_proj, got {key!r}" ) instantiated_func = valid_transforms[key](*args, **kwargs) elif hasattr(mpl_proj, "globe"): # cartopy transforms have a globe method associated with them key = mpl_proj instantiated_func = mpl_proj elif hasattr(mpl_proj, "set_transform"): # mpl axes objects have a set_transform method, so we'll check if that # exists on something passed in. key = mpl_proj instantiated_func = mpl_proj elif hasattr(mpl_proj, "name"): # last we'll check if the transform is in the list of registered # projections in matplotlib. from matplotlib.projections import get_projection_names registered_projections = get_projection_names() if mpl_proj.name in registered_projections: key = mpl_proj instantiated_func = mpl_proj else: key = None # build in a check that if none of the above options are satisfied by what # the user passes that None is returned for the instantiated function if key is None: instantiated_func = None return instantiated_func yt-project-yt-f043ac8/yt/visualization/image_writer.py000066400000000000000000000366411510711153200232510ustar00rootroot00000000000000import numpy as np from yt._maintenance.ipython_compat import IS_IPYTHON from yt.config import ytcfg from yt.funcs import mylog from yt.units.yt_array import YTQuantity from yt.utilities import png_writer as pw from yt.utilities.exceptions import YTNotInsideNotebook from yt.utilities.lib import image_utilities as au from yt.visualization.color_maps import get_colormap_lut from ._commons import get_canvas, validate_image_name def scale_image(image, mi=None, ma=None): r"""Scale an image ([NxNxM] where M = 1-4) to be uint8 and values scaled from [0,255]. Parameters ---------- image : array_like or tuple of image info Examples -------- >>> image = scale_image(image) >>> image = scale_image(image, min=0, max=1000) """ if isinstance(image, np.ndarray) and image.dtype == np.uint8: return image if isinstance(image, (tuple, list)): image, mi, ma = image if mi is None: mi = image.min() if ma is None: ma = image.max() image = np.clip((image - mi) / (ma - mi) * 255, 0, 255).astype("uint8") return image def multi_image_composite( fn, red_channel, blue_channel, green_channel=None, alpha_channel=None ): r"""Write an image with different color channels corresponding to different quantities. Accepts at least a red and a blue array, of shape (N,N) each, that are optionally scaled and composited into a final image, written into `fn`. Can also accept green and alpha. Parameters ---------- fn : string Filename to save red_channel : array_like or tuple of image info Array, of shape (N,N), to be written into the red channel of the output image. If not already uint8, will be converted (and scaled) into uint8. Optionally, you can also specify a tuple that includes scaling information, in the form of (array_to_plot, min_value_to_scale, max_value_to_scale). blue_channel : array_like or tuple of image info Array, of shape (N,N), to be written into the blue channel of the output image. If not already uint8, will be converted (and scaled) into uint8. Optionally, you can also specify a tuple that includes scaling information, in the form of (array_to_plot, min_value_to_scale, max_value_to_scale). green_channel : array_like or tuple of image info, optional Array, of shape (N,N), to be written into the green channel of the output image. If not already uint8, will be converted (and scaled) into uint8. If not supplied, will be left empty. Optionally, you can also specify a tuple that includes scaling information, in the form of (array_to_plot, min_value_to_scale, max_value_to_scale). alpha_channel : array_like or tuple of image info, optional Array, of shape (N,N), to be written into the alpha channel of the output image. If not already uint8, will be converted (and scaled) into uint8. If not supplied, will be made fully opaque. Optionally, you can also specify a tuple that includes scaling information, in the form of (array_to_plot, min_value_to_scale, max_value_to_scale). Examples -------- >>> red_channel = np.log10(frb["gas", "temperature"]) >>> blue_channel = np.log10(frb["gas", "density"]) >>> multi_image_composite("multi_channel1.png", red_channel, blue_channel) """ red_channel = scale_image(red_channel) blue_channel = scale_image(blue_channel) if green_channel is None: green_channel = np.zeros(red_channel.shape, dtype="uint8") else: green_channel = scale_image(green_channel) if alpha_channel is None: alpha_channel = np.zeros(red_channel.shape, dtype="uint8") + 255 else: alpha_channel = scale_image(alpha_channel) image = np.array([red_channel, green_channel, blue_channel, alpha_channel]) image = image.transpose().copy() # Have to make sure it's contiguous pw.write_png(image, fn) def write_bitmap(bitmap_array, filename, max_val=None, transpose=False): r"""Write out a bitmapped image directly to a PNG file. This accepts a three- or four-channel `bitmap_array`. If the image is not already uint8, it will be scaled and converted. If it is four channel, only the first three channels will be scaled, while the fourth channel is assumed to be in the range of [0,1]. If it is not four channel, a fourth alpha channel will be added and set to fully opaque. The resultant image will be directly written to `filename` as a PNG with no colormap applied. `max_val` is a value used if the array is passed in as anything other than uint8; it will be the value used for scaling and clipping in the first three channels when the array is converted. Additionally, the minimum is assumed to be zero; this makes it primarily suited for the results of volume rendered images, rather than misaligned projections. Parameters ---------- bitmap_array : array_like Array of shape (N,M,3) or (N,M,4), to be written. If it is not already a uint8 array, it will be scaled and converted to uint8. filename : string Filename to save to. If None, PNG contents will be returned as a string. max_val : float, optional The upper limit to clip values to in the output, if converting to uint8. If `bitmap_array` is already uint8, this will be ignore. transpose : boolean, optional If transpose is False, we assume that the incoming bitmap_array is such that the first element resides in the upper-left corner. If True, the first element will be placed in the lower-left corner. """ if len(bitmap_array.shape) != 3 or bitmap_array.shape[-1] not in (3, 4): raise RuntimeError( "Expecting image array of shape (N,M,3) or " f"(N,M,4), received {str(bitmap_array.shape)}" ) if bitmap_array.dtype != np.uint8: s1, s2 = bitmap_array.shape[:2] if bitmap_array.shape[-1] == 3: alpha_channel = np.full((s1, s2, 1), 255, dtype="uint8") else: alpha_channel = ( (255 * bitmap_array[:, :, 3]).astype("uint8").reshape(s1, s2, 1) ) if max_val is None: max_val = bitmap_array[:, :, :3].max() if max_val == 0.0: # avoid dividing by zero for blank images max_val = 1.0 bitmap_array = np.clip(bitmap_array[:, :, :3] / max_val, 0.0, 1.0) * 255 bitmap_array = np.concatenate( [bitmap_array.astype("uint8"), alpha_channel], axis=-1 ) if transpose: bitmap_array = bitmap_array.swapaxes(0, 1).copy(order="C") if filename is not None: pw.write_png(bitmap_array, filename) else: return pw.write_png_to_string(bitmap_array.copy()) return bitmap_array def write_image(image, filename, color_bounds=None, cmap_name=None, func=lambda x: x): r"""Write out a floating point array directly to a PNG file, scaling it and applying a colormap. This function will scale an image and directly call libpng to write out a colormapped version of that image. It is designed for rapid-fire saving of image buffers generated using `yt.visualization.api.FixedResolutionBuffers` and the likes. Parameters ---------- image : array_like This is an (unscaled) array of floating point values, shape (N,N,) to save in a PNG file. filename : string Filename to save as. color_bounds : tuple of floats, optional The min and max to scale between. Outlying values will be clipped. cmap_name : string, optional An acceptable colormap. See either yt.visualization.color_maps or https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html . func : function, optional A function to transform the buffer before applying a colormap. Returns ------- scaled_image : uint8 image that has been saved Examples -------- >>> sl = ds.slice(0, 0.5, "Density") >>> frb1 = FixedResolutionBuffer(sl, (0.2, 0.3, 0.4, 0.5), (1024, 1024)) >>> write_image(frb1["gas", "density"], "saved.png") """ if cmap_name is None: cmap_name = ytcfg.get("yt", "default_colormap") if len(image.shape) == 3: mylog.info("Using only channel 1 of supplied image") image = image[:, :, 0] to_plot = apply_colormap(image, color_bounds=color_bounds, cmap_name=cmap_name) pw.write_png(to_plot, filename) return to_plot def apply_colormap(image, color_bounds=None, cmap_name=None, func=lambda x: x): r"""Apply a colormap to a floating point image, scaling to uint8. This function will scale an image and directly call libpng to write out a colormapped version of that image. It is designed for rapid-fire saving of image buffers generated using `yt.visualization.api.FixedResolutionBuffers` and the likes. Parameters ---------- image : array_like This is an (unscaled) array of floating point values, shape (N,N,) to save in a PNG file. color_bounds : tuple of floats, optional The min and max to scale between. Outlying values will be clipped. cmap_name : string, optional An acceptable colormap. See either yt.visualization.color_maps or https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html . func : function, optional A function to transform the buffer before applying a colormap. Returns ------- to_plot : uint8 image with colorbar applied. """ if cmap_name is None: cmap_name = ytcfg.get("yt", "default_colormap") from yt.data_objects.image_array import ImageArray image = ImageArray(func(image)) if color_bounds is None: mi = np.nanmin(image[~np.isinf(image)]) * image.uq ma = np.nanmax(image[~np.isinf(image)]) * image.uq color_bounds = mi, ma else: color_bounds = [YTQuantity(func(c), image.units) for c in color_bounds] image = (image - color_bounds[0]) / (color_bounds[1] - color_bounds[0]) to_plot = map_to_colors(image, cmap_name) to_plot = np.clip(to_plot, 0, 255) return to_plot def map_to_colors(buff, cmap_name): lut = get_colormap_lut(cmap_name) if isinstance(cmap_name, tuple): # If we are using the colorbrewer maps, don't interpolate shape = buff.shape # We add float_eps so that digitize doesn't go out of bounds x = np.mgrid[0.0 : 1.0 + np.finfo(np.float32).eps : lut[0].shape[0] * 1j] inds = np.digitize(buff.ravel(), x).reshape(shape[0], shape[1]) mapped = np.dstack([(v[inds] * 255).astype("uint8") for v in lut]) del inds else: x = np.mgrid[0.0 : 1.0 : lut[0].shape[0] * 1j] mapped = np.dstack([(np.interp(buff, x, v) * 255).astype("uint8") for v in lut]) return mapped.copy("C") def splat_points(image, points_x, points_y, contribution=None, transposed=False): if contribution is None: contribution = 100.0 val = contribution * 1.0 / points_x.size if transposed: points_y = 1.0 - points_y points_x = 1.0 - points_x im = image.copy() au.add_points_to_image(im, points_x, points_y, val) return im def write_projection( data, filename, colorbar=True, colorbar_label=None, title=None, vmin=None, vmax=None, take_log=True, figsize=(8, 6), dpi=100, cmap_name=None, extent=None, xlabel=None, ylabel=None, ): r"""Write a projection or volume rendering to disk with a variety of pretty parameters such as limits, title, colorbar, etc. write_projection uses the standard matplotlib interface to create the figure. N.B. This code only works *after* you have created the projection using the standard framework (i.e. the Camera interface or off_axis_projection). Accepts an NxM sized array representing the projection itself as well as the filename to which you will save this figure. Note that the final resolution of your image will be a product of dpi/100 * figsize. Parameters ---------- data : array_like image array as output by off_axis_projection or camera.snapshot() filename : string the filename where the data will be saved colorbar : boolean do you want a colorbar generated to the right of the image? colorbar_label : string the label associated with your colorbar title : string the label at the top of the figure vmin : float or None the lower limit of the zaxis (part of matplotlib api) vmax : float or None the lower limit of the zaxis (part of matplotlib api) take_log : boolean plot the log of the data array (and take the log of the limits if set)? figsize : array_like width, height in inches of final image dpi : int final image resolution in pixels / inch cmap_name : string The name of the colormap. Examples -------- >>> image = off_axis_projection(ds, c, L, W, N, "Density", no_ghost=False) >>> write_projection( ... image, ... "test.png", ... colorbar_label="Column Density (cm$^{-2}$)", ... title="Offaxis Projection", ... vmin=1e-5, ... vmax=1e-3, ... take_log=True, ... ) """ if cmap_name is None: cmap_name = ytcfg.get("yt", "default_colormap") import matplotlib.colors import matplotlib.figure # If this is rendered as log, then apply now. if take_log: norm_cls = matplotlib.colors.LogNorm else: norm_cls = matplotlib.colors.Normalize norm = norm_cls(vmin=vmin, vmax=vmax) # Create the figure and paint the data on fig = matplotlib.figure.Figure(figsize=figsize) ax = fig.add_subplot(111) cax = ax.imshow( data.to_ndarray(), norm=norm, extent=extent, cmap=cmap_name, ) if title: ax.set_title(title) if xlabel: ax.set_xlabel(xlabel) if ylabel: ax.set_ylabel(ylabel) # Suppress the x and y pixel counts if extent is None: ax.set_xticks(()) ax.set_yticks(()) # Add a color bar and label if requested if colorbar: cbar = fig.colorbar(cax) if colorbar_label: cbar.ax.set_ylabel(colorbar_label) filename = validate_image_name(filename) canvas = get_canvas(fig, filename) mylog.info("Saving plot %s", filename) fig.tight_layout() canvas.print_figure(filename, dpi=dpi) return filename def display_in_notebook(image, max_val=None): """ A helper function to display images in an IPython notebook Must be run from within an IPython notebook, or else it will raise a YTNotInsideNotebook exception. Parameters ---------- image : array_like This is an (unscaled) array of floating point values, shape (N,N,3) or (N,N,4) to display in the notebook. The first three channels will be scaled automatically. max_val : float, optional The upper limit to clip values of the image. Only applies to the first three channels. """ if IS_IPYTHON: from IPython.core.displaypub import publish_display_data data = write_bitmap(image, None, max_val=max_val) publish_display_data( data={"image/png": data}, source="yt.visualization.image_writer.display_in_notebook", ) else: raise YTNotInsideNotebook yt-project-yt-f043ac8/yt/visualization/line_plot.py000066400000000000000000000353631510711153200225600ustar00rootroot00000000000000from collections import defaultdict import numpy as np from matplotlib.colors import LogNorm, Normalize, SymLogNorm from yt.funcs import is_sequence, mylog from yt.units.unit_object import Unit # type: ignore from yt.units.yt_array import YTArray from yt.visualization.plot_container import ( BaseLinePlot, PlotDictionary, invalidate_plot, ) class LineBuffer: r""" LineBuffer(ds, start_point, end_point, npoints, label = None) This takes a data source and implements a protocol for generating a 'pixelized', fixed-resolution line buffer. In other words, LineBuffer takes a starting point, ending point, and number of sampling points and can subsequently generate YTArrays of field values along the sample points. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object holding the data that can be sampled by the LineBuffer start_point : n-element list, tuple, ndarray, or YTArray Contains the coordinates of the first point for constructing the LineBuffer. Must contain n elements where n is the dimensionality of the dataset. end_point : n-element list, tuple, ndarray, or YTArray Contains the coordinates of the first point for constructing the LineBuffer. Must contain n elements where n is the dimensionality of the dataset. npoints : int How many points to sample between start_point and end_point Examples -------- >>> lb = yt.LineBuffer(ds, (0.25, 0, 0), (0.25, 1, 0), 100) >>> lb["all", "u"].max() 0.11562424257143075 dimensionless """ def __init__(self, ds, start_point, end_point, npoints, label=None): self.ds = ds self.start_point = _validate_point(start_point, ds, start=True) self.end_point = _validate_point(end_point, ds) self.npoints = npoints self.label = label self.data = {} def keys(self): return self.data.keys() def __setitem__(self, item, val): self.data[item] = val def __getitem__(self, item): if item in self.data: return self.data[item] mylog.info("Making a line buffer with %d points of %s", self.npoints, item) self.points, self.data[item] = self.ds.coordinates.pixelize_line( item, self.start_point, self.end_point, self.npoints ) return self.data[item] def __delitem__(self, item): del self.data[item] class LinePlotDictionary(PlotDictionary): def __init__(self, data_source): super().__init__(data_source) self.known_dimensions = {} def _sanitize_dimensions(self, item): field = self.data_source._determine_fields(item)[0] finfo = self.data_source.ds.field_info[field] dimensions = Unit( finfo.units, registry=self.data_source.ds.unit_registry ).dimensions if dimensions not in self.known_dimensions: self.known_dimensions[dimensions] = item return self.known_dimensions[dimensions] def __getitem__(self, item): ret_item = self._sanitize_dimensions(item) return super().__getitem__(ret_item) def __setitem__(self, item, value): ret_item = self._sanitize_dimensions(item) super().__setitem__(ret_item, value) def __contains__(self, item): ret_item = self._sanitize_dimensions(item) return super().__contains__(ret_item) class LinePlot(BaseLinePlot): r""" A class for constructing line plots Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. fields : string / tuple, or list of strings / tuples The name(s) of the field(s) to be plotted. start_point : n-element list, tuple, ndarray, or YTArray Contains the coordinates of the first point for constructing the line. Must contain n elements where n is the dimensionality of the dataset. end_point : n-element list, tuple, ndarray, or YTArray Contains the coordinates of the first point for constructing the line. Must contain n elements where n is the dimensionality of the dataset. npoints : int How many points to sample between start_point and end_point for constructing the line plot figure_size : int or two-element iterable of ints Size in inches of the image. Default: 5 (5x5) fontsize : int Font size for all text in the plot. Default: 14 field_labels : dictionary Keys should be the field names. Values should be latex-formattable strings used in the LinePlot legend Default: None Example ------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> plot = yt.LinePlot(ds, "density", [0, 0, 0], [1, 1, 1], 512) >>> plot.add_legend("density") >>> plot.set_x_unit("cm") >>> plot.set_unit("density", "kg/cm**3") >>> plot.save() """ _plot_dict_type = LinePlotDictionary _plot_type = "line_plot" _default_figure_size = (5.0, 5.0) _default_font_size = 14.0 def __init__( self, ds, fields, start_point, end_point, npoints, figure_size=None, fontsize: float | None = None, field_labels=None, ): """ Sets up figure and axes """ line = LineBuffer(ds, start_point, end_point, npoints, label=None) self.lines = [line] self._initialize_instance(self, ds, fields, figure_size, fontsize, field_labels) self._setup_plots() @classmethod def _initialize_instance( cls, obj, ds, fields, figure_size, fontsize, field_labels=None ): obj._x_unit = None obj._titles = {} data_source = ds.all_data() obj.fields = data_source._determine_fields(fields) obj.include_legend = defaultdict(bool) super(LinePlot, obj).__init__( data_source, figure_size=figure_size, fontsize=fontsize ) if field_labels is None: obj.field_labels = {} else: obj.field_labels = field_labels for f in obj.fields: if f not in obj.field_labels: obj.field_labels[f] = f[1] def _get_axrect(self): fontscale = self._font_properties._size / self.__class__._default_font_size top_buff_size = 0.35 * fontscale x_axis_size = 1.35 * fontscale y_axis_size = 0.7 * fontscale right_buff_size = 0.2 * fontscale if is_sequence(self.figure_size): figure_size = self.figure_size else: figure_size = (self.figure_size, self.figure_size) xbins = np.array([x_axis_size, figure_size[0], right_buff_size]) ybins = np.array([y_axis_size, figure_size[1], top_buff_size]) x_frac_widths = xbins / xbins.sum() y_frac_widths = ybins / ybins.sum() return ( x_frac_widths[0], y_frac_widths[0], x_frac_widths[1], y_frac_widths[1], ) @classmethod def from_lines( cls, ds, fields, lines, figure_size=None, font_size=None, field_labels=None ): """ A class method for constructing a line plot from multiple sampling lines Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. fields : field name or list of field names The name(s) of the field(s) to be plotted. lines : list of :class:`yt.visualization.line_plot.LineBuffer` instances The lines from which to sample data figure_size : int or two-element iterable of ints Size in inches of the image. Default: 5 (5x5) font_size : int Font size for all text in the plot. Default: 14 field_labels : dictionary Keys should be the field names. Values should be latex-formattable strings used in the LinePlot legend Default: None Example -------- >>> ds = yt.load( ... "SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1 ... ) >>> fields = [field for field in ds.field_list if field[0] == "all"] >>> lines = [ ... yt.LineBuffer(ds, [0.25, 0, 0], [0.25, 1, 0], 100, label="x = 0.25"), ... yt.LineBuffer(ds, [0.5, 0, 0], [0.5, 1, 0], 100, label="x = 0.5"), ... ] >>> lines.append() >>> plot = yt.LinePlot.from_lines(ds, fields, lines) >>> plot.save() """ obj = cls.__new__(cls) obj.lines = lines cls._initialize_instance(obj, ds, fields, figure_size, font_size, field_labels) obj._setup_plots() return obj def _setup_plots(self): if self._plot_valid: return for plot in self.plots.values(): plot.axes.cla() for line in self.lines: dimensions_counter = defaultdict(int) for field in self.fields: finfo = self.ds.field_info[field] dimensions = Unit( finfo.units, registry=self.ds.unit_registry ).dimensions dimensions_counter[dimensions] += 1 for field in self.fields: # get plot instance plot = self._get_plot_instance(field) # calculate x and y x, y = self.ds.coordinates.pixelize_line( field, line.start_point, line.end_point, line.npoints ) # scale x and y to proper units if self._x_unit is None: unit_x = x.units else: unit_x = self._x_unit unit_y = plot.norm_handler.display_units x.convert_to_units(unit_x) y.convert_to_units(unit_y) # determine legend label str_seq = [] str_seq.append(line.label) str_seq.append(self.field_labels[field]) delim = "; " legend_label = delim.join(filter(None, str_seq)) # apply plot to matplotlib axes plot.axes.plot(x, y, label=legend_label) # apply log transforms if requested norm = plot.norm_handler.get_norm(data=y) y_norm_type = type(norm) if y_norm_type is Normalize: plot.axes.set_yscale("linear") elif y_norm_type is LogNorm: plot.axes.set_yscale("log") elif y_norm_type is SymLogNorm: plot.axes.set_yscale("symlog") else: raise NotImplementedError( f"LinePlot doesn't support y norm with type {type(norm)}" ) # set font properties plot._set_font_properties(self._font_properties, None) # set x and y axis labels axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y) if self._xlabel is not None: x_label = self._xlabel else: x_label = r"$\rm{Path\ Length" + axes_unit_labels[0] + "}$" if self._ylabel is not None: y_label = self._ylabel else: finfo = self.ds.field_info[field] dimensions = Unit( finfo.units, registry=self.ds.unit_registry ).dimensions if dimensions_counter[dimensions] > 1: y_label = ( r"$\rm{Multiple\ Fields}$" + r"$\rm{" + axes_unit_labels[1] + "}$" ) else: y_label = ( finfo.get_latex_display_name() + r"$\rm{" + axes_unit_labels[1] + "}$" ) plot.axes.set_xlabel(x_label) plot.axes.set_ylabel(y_label) # apply title if field in self._titles: plot.axes.set_title(self._titles[field]) # apply legend dim_field = self.plots._sanitize_dimensions(field) if self.include_legend[dim_field]: plot.axes.legend() self._plot_valid = True @invalidate_plot def annotate_legend(self, field): """ Adds a legend to the `LinePlot` instance. The `_sanitize_dimensions` call ensures that a legend label will be added for every field of a multi-field plot """ dim_field = self.plots._sanitize_dimensions(field) self.include_legend[dim_field] = True @invalidate_plot def set_x_unit(self, unit_name): """Set the unit to use along the x-axis Parameters ---------- unit_name: str The name of the unit to use for the x-axis unit """ self._x_unit = unit_name @invalidate_plot def set_unit(self, field, new_unit): """Set the unit used to plot the field Parameters ---------- field: str or field tuple The name of the field to set the units for new_unit: string or Unit object """ field = self.data_source._determine_fields(field)[0] pnh = self.plots[field].norm_handler pnh.display_units = new_unit @invalidate_plot def annotate_title(self, field, title): """Set the unit used to plot the field Parameters ---------- field: str or field tuple The name of the field to set the units for title: str The title to use for the plot """ self._titles[self.data_source._determine_fields(field)[0]] = title def _validate_point(point, ds, start=False): if not is_sequence(point): raise RuntimeError("Input point must be array-like") if not isinstance(point, YTArray): point = ds.arr(point, "code_length", dtype=np.float64) if len(point.shape) != 1: raise RuntimeError("Input point must be a 1D array") if point.shape[0] < ds.dimensionality: raise RuntimeError("Input point must have an element for each dimension") # need to pad to 3D elements to avoid issues later if point.shape[0] < 3: if start: val = 0 else: val = 1 point = np.append(point.d, [val] * (3 - ds.dimensionality)) * point.uq return point yt-project-yt-f043ac8/yt/visualization/mapserver/000077500000000000000000000000001510711153200222135ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/mapserver/__init__.py000066400000000000000000000000001510711153200243120ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/mapserver/html/000077500000000000000000000000001510711153200231575ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/mapserver/html/Leaflet.Coordinates-0.1.5.css000066400000000000000000000012411510711153200301530ustar00rootroot00000000000000/* * From https://github.com/MrMufflon/Leaflet.Coordinates * * Fixed small issue about formatting by C. Cadiou (cphyc)) */ .leaflet-control-coordinates{background-color:#D8D8D8;background-color:rgba(255,255,255,.8);cursor:pointer}.leaflet-control-coordinates,.leaflet-control-coordinates .uiElement input{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.leaflet-control-coordinates .uiElement{margin:4px}.leaflet-control-coordinates .uiElement .labelFirst{margin-right:4px}.leaflet-control-coordinates .uiHidden{display:none}.leaflet-control-coordinates .uiElement.label{color:inherit;font-weight:inherit;font-size:inherit;padding:0;display:inherit} yt-project-yt-f043ac8/yt/visualization/mapserver/html/Leaflet.Coordinates-0.1.5.src.js000066400000000000000000000220431510711153200305700ustar00rootroot00000000000000/* * From https://github.com/MrMufflon/Leaflet.Coordinates * * Fixed small issue about formatting by C. Cadiou (cphyc)) */ /* * L.Control.Coordinates is used for displaying current mouse coordinates on the map. */ L.Control.Coordinates = L.Control.extend({ options: { position: 'bottomright', //decimals used if not using DMS or labelFormatter functions decimals: 4, //decimalseperator used if not using DMS or labelFormatter functions decimalSeperator: ".", //label templates for usage if no labelFormatter function is defined labelTemplateLat: "Lat: {y}", labelTemplateLng: "Lng: {x}", //label formatter functions labelFormatterLat: undefined, labelFormatterLng: undefined, //switch on/off input fields on click enableUserInput: true, //use Degree-Minute-Second useDMS: false, //if true lat-lng instead of lng-lat label ordering is used useLatLngOrder: false, //if true user given coordinates are centered directly centerUserCoordinates: false, //leaflet marker type markerType: L.marker, //leaflet marker properties markerProps: {} }, onAdd: function(map) { this._map = map; var className = 'leaflet-control-coordinates', container = this._container = L.DomUtil.create('div', className), options = this.options; //label containers this._labelcontainer = L.DomUtil.create("div", "uiElement label", container); this._label = L.DomUtil.create("span", "labelFirst", this._labelcontainer); //input containers this._inputcontainer = L.DomUtil.create("div", "uiElement input uiHidden", container); var xSpan, ySpan; if (options.useLatLngOrder) { ySpan = L.DomUtil.create("span", "", this._inputcontainer); this._inputY = this._createInput("inputY", this._inputcontainer); xSpan = L.DomUtil.create("span", "", this._inputcontainer); this._inputX = this._createInput("inputX", this._inputcontainer); } else { xSpan = L.DomUtil.create("span", "", this._inputcontainer); this._inputX = this._createInput("inputX", this._inputcontainer); ySpan = L.DomUtil.create("span", "", this._inputcontainer); this._inputY = this._createInput("inputY", this._inputcontainer); } xSpan.innerHTML = options.labelTemplateLng.replace("{x}", ""); ySpan.innerHTML = options.labelTemplateLat.replace("{y}", ""); L.DomEvent.on(this._inputX, 'keyup', this._handleKeypress, this); L.DomEvent.on(this._inputY, 'keyup', this._handleKeypress, this); //connect to mouseevents map.on("mousemove", this._update, this); map.on('dragstart', this.collapse, this); map.whenReady(this._update, this); this._showsCoordinates = true; //whether or not to show inputs on click if (options.enableUserInput) { L.DomEvent.addListener(this._container, "click", this._switchUI, this); } return container; }, /** * Creates an input HTML element in given container with given classname */ _createInput: function(classname, container) { var input = L.DomUtil.create("input", classname, container); input.type = "text"; L.DomEvent.disableClickPropagation(input); return input; }, _clearMarker: function() { this._map.removeLayer(this._marker); }, /** * Called onkeyup of input fields */ _handleKeypress: function(e) { switch (e.keyCode) { case 27: //Esc this.collapse(); break; case 13: //Enter this._handleSubmit(); this.collapse(); break; default: //All keys this._handleSubmit(); break; } }, /** * Called on each keyup except ESC */ _handleSubmit: function() { var x = L.NumberFormatter.createValidNumber(this._inputX.value, this.options.decimalSeperator); var y = L.NumberFormatter.createValidNumber(this._inputY.value, this.options.decimalSeperator); if (x !== undefined && y !== undefined) { var marker = this._marker; if (!marker) { marker = this._marker = this._createNewMarker(); marker.on("click", this._clearMarker, this); } var ll = new L.LatLng(y, x); marker.setLatLng(ll); marker.addTo(this._map); if (this.options.centerUserCoordinates) { this._map.setView(ll, this._map.getZoom()); } } }, /** * Shows inputs fields */ expand: function() { this._showsCoordinates = false; this._map.off("mousemove", this._update, this); L.DomEvent.addListener(this._container, "mousemove", L.DomEvent.stop); L.DomEvent.removeListener(this._container, "click", this._switchUI, this); L.DomUtil.addClass(this._labelcontainer, "uiHidden"); L.DomUtil.removeClass(this._inputcontainer, "uiHidden"); }, /** * Creates the label according to given options and formatters */ _createCoordinateLabel: function(ll) { var opts = this.options, x, y; if (opts.customLabelFcn) { return opts.customLabelFcn(ll, opts); } if (opts.labelFormatterLng) { x = opts.labelFormatterLng(ll.lng); } else { x = L.Util.template(opts.labelTemplateLng, { x: this._getNumber(ll.lng, opts) }); } if (opts.labelFormatterLat) { y = opts.labelFormatterLat(ll.lat); } else { y = L.Util.template(opts.labelTemplateLat, { y: this._getNumber(ll.lat, opts) }); } if (opts.useLatLngOrder) { return y + " " + x; } return x + " " + y; }, /** * Returns a Number according to options (DMS or decimal) */ _getNumber: function(n, opts) { var res; if (opts.useDMS) { res = L.NumberFormatter.toDMS(n); } else { res = L.NumberFormatter.round(n, opts.decimals, opts.decimalSeperator); } return res; }, /** * Shows coordinate labels after user input has ended. Also * displays a marker with popup at the last input position. */ collapse: function() { if (!this._showsCoordinates) { this._map.on("mousemove", this._update, this); this._showsCoordinates = true; var opts = this.options; L.DomEvent.addListener(this._container, "click", this._switchUI, this); L.DomEvent.removeListener(this._container, "mousemove", L.DomEvent.stop); L.DomUtil.addClass(this._inputcontainer, "uiHidden"); L.DomUtil.removeClass(this._labelcontainer, "uiHidden"); if (this._marker) { var m = this._createNewMarker(), ll = this._marker.getLatLng(); m.setLatLng(ll); var container = L.DomUtil.create("div", ""); var label = L.DomUtil.create("div", "", container); label.innerHTML = this._ordinateLabel(ll); var close = L.DomUtil.create("a", "", container); close.innerHTML = "Remove"; close.href = "#"; var stop = L.DomEvent.stopPropagation; L.DomEvent .on(close, 'click', stop) .on(close, 'mousedown', stop) .on(close, 'dblclick', stop) .on(close, 'click', L.DomEvent.preventDefault) .on(close, 'click', function() { this._map.removeLayer(m); }, this); m.bindPopup(container); m.addTo(this._map); this._map.removeLayer(this._marker); this._marker = null; } } }, /** * Click callback for UI */ _switchUI: function(evt) { L.DomEvent.stop(evt); L.DomEvent.stopPropagation(evt); L.DomEvent.preventDefault(evt); if (this._showsCoordinates) { //show textfields this.expand(); } else { //show coordinates this.collapse(); } }, onRemove: function(map) { map.off("mousemove", this._update, this); }, /** * Mousemove callback function updating labels and input elements */ _update: function(evt) { var pos = evt.latlng, opts = this.options; if (pos) { pos = pos.wrap(); this._currentPos = pos; this._inputY.value = L.NumberFormatter.round(pos.lat, opts.decimals, opts.decimalSeperator); this._inputX.value = L.NumberFormatter.round(pos.lng, opts.decimals, opts.decimalSeperator); this._label.innerHTML = this._createCoordinateLabel(pos); } }, _createNewMarker: function() { return this.options.markerType(null, this.options.markerProps); } }); //constructor registration L.control.coordinates = function(options) { return new L.Control.Coordinates(options); }; //map init hook L.Map.mergeOptions({ coordinateControl: false }); L.Map.addInitHook(function() { if (this.options.coordinateControl) { this.coordinateControl = new L.Control.Coordinates(); this.addControl(this.coordinateControl); } }); L.NumberFormatter = { round: function(num, dec, sep) { var res = L.Util.formatNum(num, dec) + "", numbers = res.split("."); if (numbers[1]) { var d = dec - numbers[1].length; for (; d > 0; d--) { numbers[1] += "0"; } res = numbers.join(sep || "."); } return res; }, toDMS: function(deg) { var d = Math.floor(Math.abs(deg)); var minfloat = (Math.abs(deg) - d) * 60; var m = Math.floor(minfloat); var secfloat = (minfloat - m) * 60; var s = Math.round(secfloat); if (s == 60) { m++; s = "00"; } if (m == 60) { d++; m = "00"; } if (s < 10) { s = "0" + s; } if (m < 10) { m = "0" + m; } var dir = ""; if (deg < 0) { dir = "-"; } return ("" + dir + d + "° " + m + "' " + s + "''"); }, createValidNumber: function(num, sep) { if (num && num.length > 0) { var numbers = num.split(sep || "."); try { var numRes = Number(numbers.join(".")); if (isNaN(numRes)) { return undefined; } return numRes; } catch (e) { return undefined; } } return undefined; } }; yt-project-yt-f043ac8/yt/visualization/mapserver/html/__init__.py000066400000000000000000000000001510711153200252560ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/mapserver/html/map.js000066400000000000000000000064121510711153200242750ustar00rootroot00000000000000function setFullScreen () { $("#map").width($(window).width()); $("#map").height($(window).height()); } var SearchWidget = function () { var obj = { filter: function (searchStrs) { console.log("filtering on " + searchStrs); this._selector.each(function(i, el) { var val = $(el).text(); // Search var matched = searchStrs.map((str) => { return val.indexOf(str) !== -1; }).reduce((reduced, result) => { return reduced && result; }, true); if (matched) { $(el).show(); } else { $(el).hide(); } }); }, init: function () { var self = this; var searchElement = $('
'); var selector = $('.leaflet-control-layers-list label'); this._selector = selector; // Add input in the DOM selector.first().parent().prepend(searchElement); // Listen to keyboard input $('#filter input').keyup(function(ev) { const val = $(this).val(); self.filter(val.split(" ")); }); }, _selector: null }; obj.init(); return obj; }; $(document).ready(function() { // Initialize to full screen setFullScreen(); // initialize the map on the "map" div with a given center and zoom $.getJSON('/list', function(data) { var layers = [], layer_groups = [], default_layer = [null]; var layer_group = {}; // Loop over field types for (var type in data['data']) { var dtype = data['data'][type]; // Loop over fields of given type for (var field in dtype) { var loc = dtype[field] var field = loc[0], active = loc[1], url = 'map/' + field[0] + ',' + field[1] + '/{z}/{x}/{y}.png'; // Create new layer var layer = new L.TileLayer(url, {id: 'MapID', maxzoom: 18}); // Create readable name human_name = field.join(' '); // Store it layers.push(layer); layer_group[human_name] = layer; if (active) { default_layer[0] = layer; } } } var map = new L.Map('map', { crs: L.CRS.Simple, center: new L.LatLng(-128, -128), zoom: 4, layers: default_layer }); L.control.layers(layer_group).addTo(map); var unit = data['unit'], px2unit = data['px2unit'], decimals = 2; var fmt = (n) => { return L.NumberFormatter.round(n, decimals, ".") }; L.control.coordinates({ position: "bottomleft", //optional default "bootomright" decimals: 2, //optional default 4 decimalSeperator: ".", //optional default "." enableUserInput: false, //optional default true useDMS: false, //optional default false useLatLngOrder: false, //ordering of labels, default false-> lng-lat markerType: L.marker, //optional default L.marker labelFormatterLng : (lng) => { return fmt((lng+128)*px2unit) + " " + unit }, //optional default none, labelFormatterLat : (lat) => { return fmt((lat+128)*px2unit) + " " + unit }, //optional default none }).addTo(map); // Search widget var search = SearchWidget(); }); // Resize map automatically $(window).resize(setFullScreen); }); yt-project-yt-f043ac8/yt/visualization/mapserver/html/map_index.html000066400000000000000000000015441510711153200260150ustar00rootroot00000000000000
yt-project-yt-f043ac8/yt/visualization/mapserver/pannable_map.py000066400000000000000000000121141510711153200252010ustar00rootroot00000000000000import os from functools import wraps import bottle import numpy as np from yt.fields.derived_field import ValidateSpatial from yt.utilities.lib.misc_utilities import get_color_bounds from yt.utilities.png_writer import write_png_to_string from yt.visualization.fixed_resolution import FixedResolutionBuffer from yt.visualization.image_writer import apply_colormap local_dir = os.path.dirname(__file__) def exc_writeout(f): import traceback @wraps(f) def func(*args, **kwargs): try: rv = f(*args, **kwargs) return rv except Exception: traceback.print_exc(None, open("temp.exc", "w")) raise return func class PannableMapServer: _widget_name = "pannable_map" def __init__(self, data, field, takelog, cmap, route_prefix=""): self.data = data self.ds = data.ds self.field = field self.cmap = cmap bottle.route(f"{route_prefix}/map/:field/:L/:x/:y.png")(self.map) bottle.route(f"{route_prefix}/map/:field/:L/:x/:y.png")(self.map) bottle.route(f"{route_prefix}/")(self.index) bottle.route(f"{route_prefix}/:field")(self.index) bottle.route(f"{route_prefix}/index.html")(self.index) bottle.route(f"{route_prefix}/list", "GET")(self.list_fields) # This is a double-check, since we do not always mandate this for # slices: self.data[self.field] = self.data[self.field].astype("float64", copy=False) bottle.route(f"{route_prefix}/static/:path", "GET")(self.static) self.takelog = takelog self._lock = False for unit in ["Gpc", "Mpc", "kpc", "pc"]: v = self.ds.domain_width[0].in_units(unit).value if v > 1: break self.unit = unit self.px2unit = self.ds.domain_width[0].in_units(unit).value / 256 def lock(self): import time while self._lock: time.sleep(0.01) self._lock = True def unlock(self): self._lock = False def map(self, field, L, x, y): if "," in field: field = tuple(field.split(",")) cmap = self.cmap dd = 1.0 / (2.0 ** (int(L))) relx = int(x) * dd rely = int(y) * dd DW = self.ds.domain_right_edge - self.ds.domain_left_edge xl = self.ds.domain_left_edge[0] + relx * DW[0] yl = self.ds.domain_left_edge[1] + rely * DW[1] xr = xl + dd * DW[0] yr = yl + dd * DW[1] try: self.lock() w = 256 # pixels data = self.data[field] frb = FixedResolutionBuffer(self.data, (xl, xr, yl, yr), (w, w)) cmi, cma = get_color_bounds( self.data["px"], self.data["py"], self.data["pdx"], self.data["pdy"], data, self.ds.domain_left_edge[0], self.ds.domain_right_edge[0], self.ds.domain_left_edge[1], self.ds.domain_right_edge[1], dd * DW[0] / (64 * 256), dd * DW[0], ) finally: self.unlock() if self.takelog: cmi = np.log10(cmi) cma = np.log10(cma) to_plot = apply_colormap( np.log10(frb[field]), color_bounds=(cmi, cma), cmap_name=cmap ) else: to_plot = apply_colormap( frb[field], color_bounds=(cmi, cma), cmap_name=cmap ) rv = write_png_to_string(to_plot) return rv def index(self, field=None): if field is not None: self.field = field return bottle.static_file( "map_index.html", root=os.path.join(local_dir, "html") ) def static(self, path): if path[-4:].lower() in (".png", ".gif", ".jpg"): bottle.response.headers["Content-Type"] = f"image/{path[-3:].lower()}" elif path[-4:].lower() == ".css": bottle.response.headers["Content-Type"] = "text/css" elif path[-3:].lower() == ".js": bottle.response.headers["Content-Type"] = "text/javascript" full_path = os.path.join(os.path.join(local_dir, "html"), path) return open(full_path).read() def list_fields(self): d = {} # Add fluid fields (only gas for now) for ftype in self.ds.fluid_types: d[ftype] = [] for f in self.ds.derived_field_list: if f[0] != ftype: continue # Discard fields which need ghost zones for now df = self.ds.field_info[f] if any(isinstance(v, ValidateSpatial) for v in df.validators): continue # Discard cutting plane fields if "cutting" in f[1]: continue active = f[1] == self.field d[ftype].append((f, active)) print(self.px2unit, self.unit) return { "data": d, "px2unit": self.px2unit, "unit": self.unit, "active": self.field, } yt-project-yt-f043ac8/yt/visualization/particle_plots.py000066400000000000000000001040111510711153200236020ustar00rootroot00000000000000import warnings from typing import Union import numpy as np from yt._maintenance.deprecation import issue_deprecation_warning from yt.data_objects.profiles import create_profile from yt.data_objects.static_output import Dataset from yt.funcs import fix_axis, iter_fields, mylog from yt.units.yt_array import YTArray from yt.utilities.orientation import Orientation from yt.visualization.fixed_resolution import ParticleImageBuffer from yt.visualization.profile_plotter import PhasePlot from .plot_window import ( NormalPlot, PWViewerMPL, get_axes_unit, get_oblique_window_parameters, get_window_parameters, ) class ParticleDummyDataSource: _type_name = "Particle" _dimensionality = 2 _con_args = ("center", "axis", "width", "fields", "weight_field") _tds_attrs = () _key_fields: list[str] = [] def __init__( self, center, ds, width, fields, dd, *, weight_field=None, field_parameters=None, deposition="ngp", density=False, ): self.center = center self.ds = ds self.width = width self.dd = dd if weight_field is not None: weight_field = self._determine_fields(weight_field)[0] self.weight_field = weight_field self.deposition = deposition self.density = density if field_parameters is None: self.field_parameters = {} else: self.field_parameters = field_parameters fields = self._determine_fields(fields) self.fields = fields def _determine_fields(self, *args): return self.dd._determine_fields(*args) def get_field_parameter(self, name, default=None): """ This is typically only used by derived field functions, but it returns parameters used to generate fields. """ if name in self.field_parameters: return self.field_parameters[name] else: return default class ParticleAxisAlignedDummyDataSource(ParticleDummyDataSource): def __init__( self, center, ds, axis, width, fields, *, weight_field=None, field_parameters=None, data_source=None, deposition="ngp", density=False, ): self.axis = axis LE = center - 0.5 * YTArray(width) RE = center + 0.5 * YTArray(width) for ax in range(3): if not ds.periodicity[ax]: LE[ax] = max(LE[ax], ds.domain_left_edge[ax]) RE[ax] = min(RE[ax], ds.domain_right_edge[ax]) dd = ds.region( center, LE, RE, fields, field_parameters=field_parameters, data_source=data_source, ) super().__init__( center, ds, width, fields, dd, weight_field=weight_field, field_parameters=field_parameters, deposition=deposition, density=density, ) class ParticleOffAxisDummyDataSource(ParticleDummyDataSource): def __init__( self, center, ds, normal_vector, width, fields, *, weight_field=None, field_parameters=None, data_source=None, deposition="ngp", density=False, north_vector=None, ): self.axis = None # always true for oblique data objects normal = np.array(normal_vector) normal = normal / np.linalg.norm(normal) # If north_vector is None, we set the default here. # This is chosen so that if normal_vector is one of the # cartesian coordinate axes, the projection will match # the corresponding on-axis projection. if north_vector is None: vecs = np.identity(3) t = np.cross(vecs, normal).sum(axis=1) ax = t.argmax() east_vector = np.cross(vecs[ax, :], normal).ravel() north = np.cross(normal, east_vector).ravel() else: north = np.array(north_vector) north = north / np.linalg.norm(north) self.normal_vector = normal self.north_vector = north if data_source is None: dd = ds.all_data() else: dd = data_source self.orienter = Orientation(normal_vector, north_vector=north_vector) super().__init__( center, ds, width, fields, dd, weight_field=weight_field, field_parameters=field_parameters, deposition=deposition, density=density, ) class ParticleProjectionPlot(NormalPlot): r"""Creates a particle plot from a dataset Given a ds object, a normal to project along, and a field name string, this will return a PWViewerMPL object containing the plot. The plot can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : `Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : int, str, or 3-element sequence of floats This specifies the normal vector to the projection. Valid int values are 0, 1 and 2. Corresponding str values depend on the geometry of the dataset and are generally given by `ds.coordinates.axis_order`. E.g. in cartesian they are 'x', 'y' and 'z'. An arbitrary normal vector may be specified as a 3-element sequence of floats. fields : string, list or None If a string or list, the name of the particle field(s) to be used on the colorbar. The color shown will correspond to the sum of the given field along the line of sight. If None, the particle positions will be indicated using a fixed color, instead. Default is None. color : 'b', 'g', 'r', 'c', 'm', 'y', 'k', or 'w' One the matplotlib-recognized color strings. The color that will indicate the particle locations on the mesh. This argument is ignored if z_fields is not None. Default is 'b'. center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x-axis and 15 kiloparsecs wide along the y-axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. depth : A tuple or a float A tuple containing the depth to project through and the string key of the unit: (width, 'unit'). If set to a float, code units are assumed. Defaults to the entire domain. weight_field : string The name of the weighting field. Set to None for no weight. If given, the plot will show a weighted average along the line of sight of the fields given in the ``fields`` argument. axes_unit : A string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. origin : string or length 1, 2, or 3 sequence of strings The location of the origin of the plot coordinate system. This is represented by '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] both place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. Further examples: ================================== ============================ format example ================================== ============================ '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ================================== ============================ fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. window_size : float The size of the window on the longest axis (in units of inches), including the margins but not the colorbar. aspect : float The aspect ratio of the plot. Set to None for 1. data_source : YTSelectionContainer object The object to be used for data selection. Defaults to a region covering the entire simulation. deposition : string Controls the order of the interpolation of the particles onto the mesh. "ngp" is 0th-order "nearest-grid-point" method (the default), "cic" is 1st-order "cloud-in-cell". density : boolean If True, the quantity to be projected will be divided by the area of the cells, to make a projected density of the quantity. The plot name and units will also reflect this. Default: False north_vector : a sequence of floats A vector defining the 'up' direction in off-axis particle projection plots; not used if the plot is on-axis. This option sets the orientation of the projected plane. If not set, an arbitrary grid-aligned north-vector is chosen. Examples -------- This will save an image to the file 'galaxy0030_Particle_z_particle_mass.png' >>> from yt import load >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> p = yt.ParticleProjectionPlot(ds, 2, "particle_mass") >>> p.save() """ # ignoring type check here, because mypy doesn't allow __new__ methods to # return instances of subclasses. The design we use here is however based # on the pathlib.Path class from the standard library # https://github.com/python/mypy/issues/1020 def __new__( # type: ignore cls, ds, normal=None, *args, axis=None, **kwargs ) -> Union["AxisAlignedParticleProjectionPlot", "OffAxisParticleProjectionPlot"]: # TODO: when axis' deprecation expires, # remove default value for normal normal = cls._handle_normalaxis_parameters(normal=normal, axis=axis) if cls is ParticleProjectionPlot: normal = cls.sanitize_normal_vector(ds, normal) if isinstance(normal, str): cls = AxisAlignedParticleProjectionPlot else: cls = OffAxisParticleProjectionPlot self = object.__new__(cls) return self # type: ignore [return-value] @staticmethod def _handle_normalaxis_parameters(*, normal, axis) -> None: # TODO: when axis' deprecation expires, # remove this method entirely if axis is not None: issue_deprecation_warning( "Argument 'axis' is a deprecated alias for 'normal'.", since="4.2", stacklevel=4, ) if normal is not None: raise TypeError("Received incompatible arguments 'axis' and 'normal'") normal = axis if normal is None: raise TypeError("missing required positional argument: 'normal'") return normal class AxisAlignedParticleProjectionPlot(ParticleProjectionPlot, PWViewerMPL): _plot_type = "Particle" _frb_generator = ParticleImageBuffer def __init__( self, ds, normal=None, fields=None, color="b", center="center", width=None, depth=(1, "1"), weight_field=None, axes_unit=None, origin="center-window", fontsize=18, field_parameters=None, window_size=8.0, aspect=None, data_source=None, deposition="ngp", density=False, *, north_vector=None, axis=None, ): if north_vector is not None: # this kwarg exists only for symmetry reasons with OffAxisSlicePlot mylog.warning( "Ignoring 'north_vector' keyword as it is ill-defined for " "an AxisAlignedParticleProjectionPlot object." ) del north_vector normal = self._handle_normalaxis_parameters(normal=normal, axis=axis) # this will handle time series data and controllers ts = self._initialize_dataset(ds) self.ts = ts ds = self.ds = ts[0] normal = self.sanitize_normal_vector(ds, normal) if field_parameters is None: field_parameters = {} self.set_axes_unit(axes_unit or get_axes_unit(width, ds)) # if no fields are passed in, we simply mark the x and # y fields using a given color. Use the 'particle_ones' # field to do this. We also turn off the colorbar in # this case. use_cbar = True splat_color = None if fields is None: fields = [("all", "particle_ones")] weight_field = ("all", "particle_ones") use_cbar = False splat_color = color axis = fix_axis(normal, ds) (bounds, center, display_center) = get_window_parameters( axis, center, width, ds ) x_coord = ds.coordinates.x_axis[axis] y_coord = ds.coordinates.y_axis[axis] depth = ds.coordinates.sanitize_depth(depth) width = np.zeros_like(center) width[x_coord] = bounds[1] - bounds[0] width[y_coord] = bounds[3] - bounds[2] width[axis] = depth[0].in_units(width[x_coord].units) self.projected = weight_field is None ParticleSource = ParticleAxisAlignedDummyDataSource( center, ds, axis, width, fields, weight_field=weight_field, field_parameters=field_parameters, data_source=data_source, deposition=deposition, density=density, ) PWViewerMPL.__init__( self, ParticleSource, bounds, origin=origin, fontsize=fontsize, fields=fields, window_size=window_size, aspect=aspect, splat_color=splat_color, geometry=ds.geometry, periodic=True, oblique=False, ) if not use_cbar: self.hide_colorbar() class OffAxisParticleProjectionPlot(ParticleProjectionPlot, PWViewerMPL): _plot_type = "Particle" _frb_generator = ParticleImageBuffer def __init__( self, ds, normal=None, fields=None, color="b", center="center", width=None, depth=(1, "1"), weight_field=None, axes_unit=None, origin="center-window", fontsize=18, field_parameters=None, window_size=8.0, aspect=None, data_source=None, deposition="ngp", density=False, *, north_vector=None, axis=None, ): if data_source is not None: warnings.warn( "The 'data_source' argument has no effect for " "off-axis particle projections (not implemented)", stacklevel=2, ) del data_source if origin != "center-window": warnings.warn( "The 'origin' argument is ignored for off-axis " "particle projections, it is always 'center-window'", stacklevel=2, ) del origin normal = self._handle_normalaxis_parameters(normal=normal, axis=axis) # this will handle time series data and controllers ts = self._initialize_dataset(ds) self.ts = ts ds = self.ds = ts[0] normal = self.sanitize_normal_vector(ds, normal) if field_parameters is None: field_parameters = {} self.set_axes_unit(axes_unit or get_axes_unit(width, ds)) # if no fields are passed in, we simply mark the x and # y fields using a given color. Use the 'particle_ones' # field to do this. We also turn off the colorbar in # this case. use_cbar = True splat_color = None if fields is None: fields = [("all", "particle_ones")] weight_field = ("all", "particle_ones") use_cbar = False splat_color = color (bounds, center_rot) = get_oblique_window_parameters( normal, center, width, ds, depth=depth ) width = ds.coordinates.sanitize_width(normal, width, depth) self.projected = weight_field is None ParticleSource = ParticleOffAxisDummyDataSource( center_rot, ds, normal, width, fields, weight_field=weight_field, field_parameters=field_parameters, data_source=None, deposition=deposition, density=density, north_vector=north_vector, ) PWViewerMPL.__init__( self, ParticleSource, bounds, origin="center-window", fontsize=fontsize, fields=fields, window_size=window_size, aspect=aspect, splat_color=splat_color, geometry=ds.geometry, periodic=False, oblique=True, ) if not use_cbar: self.hide_colorbar() class ParticlePhasePlot(PhasePlot): r""" Create a 2d particle phase plot from a data source or from a `yt.data_objects.profiles.ParticleProfile` object. Given a data object (all_data, region, sphere, etc.), an x field, y field, and z field (or fields), this will create a particle plot by depositing the particles onto a two-dimensional mesh, using either nearest grid point or cloud-in-cell deposition. Parameters ---------- data_source : YTSelectionContainer or Dataset The data object to be profiled, such as all_data, region, or sphere. If data_source is a Dataset, data_source.all_data() will be used. x_field : str The x field for the mesh. y_field : str The y field for the mesh. z_fields : None, str, or list If None, particles will be splatted onto the mesh, but no colormap will be used. If str or list, the name of the field or fields to be displayed on the colorbar. The displayed values will correspond to the sum of the field or fields along the line of sight. Default: None. color : 'b', 'g', 'r', 'c', 'm', 'y', 'k', or 'w' One the matplotlib-recognized color strings. The color that will indicate the particle locations on the mesh. This argument is ignored if z_fields is not None. Default : 'b' x_bins : int The number of bins in x field for the mesh. Default: 800. y_bins : int The number of bins in y field for the mesh. Default: 800. weight_field : str The field to weight by. If given, the plot will show a weighted average along the line of sight of the fields given in the ``z_fields`` argument. Default: None. deposition : str Either 'ngp' or 'cic'. Controls what type of interpolation will be used to deposit the particle z_fields onto the mesh. Default: 'ngp' fontsize: int Font size for all text in the plot. Default: 18. figure_size : int Size in inches of the image. Default: 8 (8x8) shading : str This argument is directly passed down to matplotlib.axes.Axes.pcolormesh see https://matplotlib.org/3.3.1/gallery/images_contours_and_fields/pcolormesh_grids.html#sphx-glr-gallery-images-contours-and-fields-pcolormesh-grids-py # noqa Default: 'nearest' Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> ad = ds.all_data() >>> plot = ParticlePhasePlot( ... ad, ... "particle_position_x", ... "particle_position_y", ... ["particle_mass"], ... x_bins=800, ... y_bins=800, ... ) >>> plot.save() >>> # Change plot properties. >>> plot.set_log("particle_mass", True) >>> plot.set_unit("particle_position_x", "Mpc") >>> plot.set_unit("particle_velocity_z", "km/s") >>> plot.set_unit("particle_mass", "Msun") """ _plot_type = "ParticlePhase" def __init__( self, data_source, x_field, y_field, z_fields=None, color="b", x_bins=800, y_bins=800, weight_field=None, deposition="ngp", fontsize=18, figure_size=8.0, shading="nearest", ): if isinstance(data_source, Dataset): data_source = data_source.all_data() # if no z_fields are passed in, use a constant color if z_fields is None: self.use_cbar = False self.splat_color = color z_fields = [("all", "particle_ones")] profile = create_profile( data_source, [x_field, y_field], list(iter_fields(z_fields)), n_bins=[x_bins, y_bins], weight_field=weight_field, deposition=deposition, ) type(self)._initialize_instance( self, data_source, profile, fontsize, figure_size, shading ) def ParticlePlot(ds, x_field, y_field, z_fields=None, color="b", *args, **kwargs): r""" A factory function for :class:`yt.visualization.particle_plots.ParticleProjectionPlot` and :class:`yt.visualization.profile_plotter.ParticlePhasePlot` objects. This essentially allows for a single entry point to both types of particle plots, the distinction being determined by the fields passed in. If the x_field and y_field combination corresponds to a valid, right-handed spatial plot, an ``ParticleProjectionPlot`` will be returned. This plot object can be updated using one of the many helper functions defined in ``PlotWindow``. If the x_field and y_field combo do not correspond to a valid ``ParticleProjectionPlot``, then a ``ParticlePhasePlot``. This object can be modified by its own set of helper functions defined in PhasePlot. We note below which arguments are only accepted by ``ParticleProjectionPlot`` and which arguments are only accepted by ``ParticlePhasePlot``. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. x_field : string This is the particle field that will be plotted on the x-axis. y_field : string This is the particle field that will be plotted on the y-axis. z_fields : string, list, or None. If None, particles will be splatted onto the plot, but no colormap will be used. The particle color will instead be determined by the 'color' argument. If str or list, the name of the field or fields to be displayed on the colorbar. Default: None. color : 'b', 'g', 'r', 'c', 'm', 'y', 'k', or 'w' One the matplotlib-recognized color strings. The color that will indicate the particle locations on the plot. This argument is ignored if z_fields is not None. Default is 'b'. weight_field : string The name of the weighting field. Set to None for no weight. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. data_source : YTSelectionContainer Object Object to be used for data selection. Defaults to a region covering the entire simulation. center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. This argument is only accepted by ``ParticleProjectionPlot``. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. This argument is only accepted by ``ParticleProjectionPlot``. depth : A tuple or a float A tuple containing the depth to project through and the string key of the unit: (width, 'unit'). If set to a float, code units are assumed. Defaults to the entire domain. This argument is only accepted by ``ParticleProjectionPlot``. axes_unit : A string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. origin : string or length 1, 2, or 3 sequence of strings The location of the origin of the plot coordinate system. This is represented by '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, the whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] both place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. Further examples: ================================== ============================ format example ================================== ============================ '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ================================== ============================ This argument is only accepted by ``ParticleProjectionPlot``. window_size : float The size of the window on the longest axis (in units of inches), including the margins but not the colorbar. This argument is only accepted by ``ParticleProjectionPlot``. aspect : float The aspect ratio of the plot. Set to None for 1. This argument is only accepted by ``ParticleProjectionPlot``. x_bins : int The number of bins in x field for the mesh. Defaults to 800. This argument is only accepted by ``ParticlePhasePlot``. y_bins : int The number of bins in y field for the mesh. Defaults to 800. This argument is only accepted by ``ParticlePhasePlot``. deposition : str Either 'ngp' or 'cic'. Controls what type of interpolation will be used to deposit the particle z_fields onto the mesh. Defaults to 'ngp'. figure_size : int Size in inches of the image. Defaults to 8 (product an 8x8 inch figure). This argument is only accepted by ``ParticlePhasePlot``. Examples -------- >>> from yt import load >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> p = yt.ParticlePlot( ... ds, ... "particle_position_x", ... "particle_position_y", ... "particle_mass", ... width=(0.5, 0.5), ... ) >>> p.set_unit("particle_mass", "Msun") >>> p = yt.ParticlePlot(ds, "particle_position_x", "particle_velocity_z", color="g") """ dd = kwargs.get("data_source", None) if dd is None: dd = ds.all_data() x_field = dd._determine_fields(x_field)[0] y_field = dd._determine_fields(y_field)[0] direction = 3 # try potential axes for a ParticleProjectionPlot: for axis in [0, 1, 2]: xax = ds.coordinates.x_axis[axis] yax = ds.coordinates.y_axis[axis] ax_field_template = "particle_position_%s" xf = ax_field_template % ds.coordinates.axis_name[xax] yf = ax_field_template % ds.coordinates.axis_name[yax] if (x_field[1], y_field[1]) in [(xf, yf), (yf, xf)]: direction = axis break if direction < 3: # Make a ParticleProjectionPlot return ParticleProjectionPlot(ds, direction, z_fields, color, *args, **kwargs) # Does not correspond to any valid PlotWindow-style plot, # use ParticlePhasePlot instead else: return ParticlePhasePlot(dd, x_field, y_field, z_fields, color, *args, **kwargs) yt-project-yt-f043ac8/yt/visualization/plot_container.py000066400000000000000000001131471510711153200236100ustar00rootroot00000000000000import abc import base64 import os import warnings from collections import defaultdict from functools import wraps from typing import Any, Final, Literal import matplotlib from matplotlib.colors import LogNorm, Normalize, SymLogNorm from unyt.dimensions import length from yt._maintenance.deprecation import issue_deprecation_warning from yt._maintenance.ipython_compat import IS_IPYTHON from yt._typing import FieldKey, Quantity from yt.config import ytcfg from yt.data_objects.time_series import DatasetSeries from yt.funcs import ensure_dir, is_sequence, iter_fields from yt.units.unit_object import Unit # type: ignore from yt.utilities.definitions import formatted_length_unit_names from yt.utilities.exceptions import YTConfigurationError, YTNotInsideNotebook from yt.visualization._commons import get_default_from_config from yt.visualization._handlers import ColorbarHandler, NormHandler from yt.visualization.base_plot_types import PlotMPL from ._commons import ( _get_units_label, get_default_font_properties, invalidate_data, invalidate_figure, invalidate_plot, validate_image_name, validate_plot, ) latex_prefixes = { "u": r"\mu", } def apply_callback(f): issue_deprecation_warning( "The apply_callback decorator is not used in yt any more and " "will be removed in a future version. " "Please do not use it.", stacklevel=3, since="4.1", ) @wraps(f) def newfunc(*args, **kwargs): args[0]._callbacks.append((f.__name__, (args, kwargs))) return args[0] return newfunc def accepts_all_fields(func): """ Decorate a function whose second argument is and deal with the special case field == 'all', looping over all fields already present in the PlotContainer object. """ # This is to be applied to PlotContainer class methods with the following signature: # # f(self, field, *args, **kwargs) -> self @wraps(func) def newfunc(self, field, *args, **kwargs): if field == "all": field = self.plots.keys() for f in self.data_source._determine_fields(field): func(self, f, *args, **kwargs) return self return newfunc # define a singleton sentinel to be used as default value distinct from None class Unset: _instance = None def __new__(cls): if cls._instance is None: cls._instance = object.__new__(cls) return cls._instance UNSET: Final = Unset() class PlotDictionary(defaultdict): def __getitem__(self, item): return defaultdict.__getitem__( self, self.data_source._determine_fields(item)[0] ) def __setitem__(self, item, value): return defaultdict.__setitem__( self, self.data_source._determine_fields(item)[0], value ) def __contains__(self, item): return defaultdict.__contains__( self, self.data_source._determine_fields(item)[0] ) def __init__(self, data_source, default_factory=None): self.data_source = data_source return defaultdict.__init__(self, default_factory) class PlotContainer(abc.ABC): """A container for generic plots""" _plot_dict_type: type[PlotDictionary] = PlotDictionary _plot_type: str | None = None _plot_valid = False _default_figure_size = tuple(matplotlib.rcParams["figure.figsize"]) _default_font_size = 14.0 def __init__(self, data_source, figure_size=None, fontsize: float | None = None): from matplotlib.font_manager import FontProperties self.data_source = data_source self.ds = data_source.ds self.ts = self._initialize_dataset(self.ds) self.plots = self.__class__._plot_dict_type(data_source) self._set_figure_size(figure_size) if fontsize is None: fontsize = self.__class__._default_font_size font_dict = get_default_font_properties() | {"size": fontsize} self._font_properties = FontProperties(**font_dict) self._font_color = None self._xlabel = None self._ylabel = None self._minorticks: dict[FieldKey, bool] = {} @accepts_all_fields @invalidate_plot def set_log( self, field, log: bool | None = None, *, linthresh: float | Quantity | Literal["auto"] | None = None, symlog_auto: bool | None = None, # deprecated ): """set a field to log, linear, or symlog. Symlog scaling is a combination of linear and log, where from 0 to a threshold value, it operates as linear, and then beyond that it operates as log. Symlog can also work with negative values in log space as well as negative and positive values simultaneously and symmetrically. If symlog scaling is desired, please set log=True and either set symlog_auto=True or select a value for linthresh. Parameters ---------- field : string the field to set a transform if field == 'all', applies to all plots. log : boolean, optional set log to True for log scaling, False for linear scaling. linthresh : float, (float, str), unyt_quantity, or 'auto', optional when using symlog scaling, linthresh is the value at which scaling transitions from linear to logarithmic. linthresh must be positive. Note: setting linthresh will automatically enable symlog scale Note that *log* and *linthresh* are mutually exclusive arguments """ if log is None and linthresh is None and symlog_auto is None: raise TypeError("set_log requires log or linthresh be set") if symlog_auto is not None: issue_deprecation_warning( "the symlog_auto argument is deprecated. Use linthresh='auto' instead", since="4.1", stacklevel=5, ) if symlog_auto is True: linthresh = "auto" elif symlog_auto is False: pass else: raise TypeError( "Received invalid value for parameter symlog_auto. " f"Expected a boolean, got {symlog_auto!r}" ) if log is not None and linthresh is not None: # we do not raise an error here for backward compatibility warnings.warn( f"log={log} has no effect because linthresh specified. Using symlog.", stacklevel=4, ) pnh = self.plots[field].norm_handler if linthresh is not None: if isinstance(linthresh, str): if linthresh == "auto": pnh.norm_type = SymLogNorm else: raise ValueError( "Expected a number, a unyt_quantity, a (float, 'unit') tuple, or 'auto'. " f"Got linthresh={linthresh!r}" ) else: # pnh takes care of switching to symlog when linthresh is set pnh.linthresh = linthresh elif log is True: pnh.norm_type = LogNorm elif log is False: pnh.norm_type = Normalize else: raise TypeError( f"Could not parse arguments log={log!r}, linthresh={linthresh!r}" ) return self def get_log(self, field): """get the transform type of a field. Parameters ---------- field : string the field to get a transform if field == 'all', applies to all plots. """ # devnote : accepts_all_fields decorator is not applicable here because # the return variable isn't self issue_deprecation_warning( "The get_log method is not reliable and is deprecated. " "Please do not rely on it.", stacklevel=3, since="4.1", ) log = {} if field == "all": fields = list(self.plots.keys()) else: fields = field for field in self.data_source._determine_fields(fields): pnh = self.plots[field].norm_handler if pnh.norm is not None: log[field] = type(pnh.norm) is LogNorm elif pnh.norm_type is not None: log[field] = pnh.norm_type is LogNorm else: # the NormHandler object has no constraints yet # so we'll assume defaults log[field] = True return log @invalidate_plot def set_transform(self, field, name: str): field = self.data_source._determine_fields(field)[0] pnh = self.plots[field].norm_handler pnh.norm_type = { "linear": Normalize, "log10": LogNorm, "symlog": SymLogNorm, }[name] return self @accepts_all_fields @invalidate_plot def set_norm(self, field, norm: Normalize): r""" Set a custom ``matplotlib.colors.Normalize`` to plot *field*. Any constraints previously set with `set_log`, `set_zlim` will be dropped. Note that any float value attached to *norm* (e.g. vmin, vmax, vcenter ...) will be read in the current displayed units, which can be controlled with the `set_unit` method. Parameters ---------- field : str or tuple[str, str] if field == 'all', applies to all plots. norm : matplotlib.colors.Normalize see https://matplotlib.org/stable/tutorials/colors/colormapnorms.html """ pnh = self.plots[field].norm_handler pnh.norm = norm return self @accepts_all_fields @invalidate_plot def set_minorticks(self, field, state): """Turn minor ticks on or off in the current plot. Displaying minor ticks reduces performance; turn them off using set_minorticks('all', False) if drawing speed is a problem. Parameters ---------- field : string the field to remove minorticks if field == 'all', applies to all plots. state : bool the state indicating 'on' (True) or 'off' (False) """ self._minorticks[field] = state return self @abc.abstractmethod def _setup_plots(self): # Left blank to be overridden in subclasses pass def render(self) -> None: r"""Render plots. This operation is expensive and usually doesn't need to be requested explicitly. In most cases, yt handles rendering automatically and delays it as much as possible to avoid redundant calls on each plot modification (e.g. via `annotate_*` methods). However, valid use cases of this method include: - fine control of render (and clear) operations when yt plots are combined with plot customizations other than plot callbacks (`annotate_*`) - testing """ # this public API method should never be no-op, so we invalidate # the plot to force a fresh render in _setup_plots() self._plot_valid = False self._setup_plots() def _initialize_dataset(self, ts): if not isinstance(ts, DatasetSeries): if not is_sequence(ts): ts = [ts] ts = DatasetSeries(ts) return ts @invalidate_data def _switch_ds(self, new_ds, data_source=None): old_object = self.data_source name = old_object._type_name kwargs = {n: getattr(old_object, n) for n in old_object._con_args} kwargs["center"] = getattr(old_object, "center", None) if data_source is not None: if name != "proj": raise RuntimeError( "The data_source keyword argument " "is only defined for projections." ) kwargs["data_source"] = data_source self.ds = new_ds # A _hack_ for ParticleProjectionPlots if name == "Particle": from yt.visualization.particle_plots import ( ParticleAxisAlignedDummyDataSource, ) new_object = ParticleAxisAlignedDummyDataSource(ds=self.ds, **kwargs) else: new_object = getattr(new_ds, name)(**kwargs) self.data_source = new_object for d in "xyz": lim_name = d + "lim" if hasattr(self, lim_name): lim = getattr(self, lim_name) lim = tuple(new_ds.quan(l.value, str(l.units)) for l in lim) setattr(self, lim_name, lim) self.plots.data_source = new_object self._colorbar_label.data_source = new_object self._setup_plots() @validate_plot def __getitem__(self, item): return self.plots[item] def _set_font_properties(self): for f in self.plots: self.plots[f]._set_font_properties(self._font_properties, self._font_color) @invalidate_plot @invalidate_figure def set_font(self, font_dict=None): """ Set the font and font properties. Parameters ---------- font_dict : dict A dict of keyword parameters to be passed to :class:`matplotlib.font_manager.FontProperties`. Possible keys include: * family - The font family. Can be serif, sans-serif, cursive, 'fantasy' or 'monospace'. * style - The font style. Either normal, italic or oblique. * color - A valid color string like 'r', 'g', 'red', 'cobalt', and 'orange'. * variant - Either normal or small-caps. * size - Either a relative value of xx-small, x-small, small, medium, large, x-large, xx-large or an absolute font size, e.g. 12 * stretch - A numeric value in the range 0-1000 or one of ultra-condensed, extra-condensed, condensed, semi-condensed, normal, semi-expanded, expanded, extra-expanded or ultra-expanded * weight - A numeric value in the range 0-1000 or one of ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, or black See the matplotlib font manager API documentation for more details. https://matplotlib.org/stable/api/font_manager_api.html Notes ----- Mathtext axis labels will only obey the `size` and `color` keyword. Examples -------- This sets the font to be 24-pt, blue, sans-serif, italic, and bold-face. >>> slc = SlicePlot(ds, "x", "Density") >>> slc.set_font( ... { ... "family": "sans-serif", ... "style": "italic", ... "weight": "bold", ... "size": 24, ... "color": "blue", ... } ... ) """ from matplotlib.font_manager import FontProperties if font_dict is None: font_dict = {} if "color" in font_dict: self._font_color = font_dict.pop("color") # Set default values if the user does not explicitly set them. # this prevents reverting to the matplotlib defaults. _default_size = {"size": self.__class__._default_font_size} font_dict = get_default_font_properties() | _default_size | font_dict self._font_properties = FontProperties(**font_dict) return self def set_font_size(self, size): """Set the size of the font used in the plot This sets the font size by calling the set_font function. See set_font for more font customization options. Parameters ---------- size : float The absolute size of the font in points (1 pt = 1/72 inch). """ return self.set_font({"size": size}) def _set_figure_size(self, size): if size is None: self.figure_size = self.__class__._default_figure_size elif is_sequence(size): if len(size) != 2: raise TypeError(f"Expected a single float or a pair, got {size}") self.figure_size = float(size[0]), float(size[1]) else: self.figure_size = float(size) @invalidate_plot @invalidate_figure def set_figure_size(self, size): """Sets a new figure size for the plot parameters ---------- size : float, a sequence of two floats, or None The size of the figure (in units of inches), including the margins but not the colorbar. If a single float is passed, it's interpreted as the size along the long axis. Pass None to reset """ self._set_figure_size(size) return self @validate_plot def save( self, name: str | list[str] | tuple[str, ...] | None = None, suffix: str | None = None, mpl_kwargs: dict[str, Any] | None = None, ): """saves the plot to disk. Parameters ---------- name : string or tuple, optional The base of the filename. If name is a directory or if name is not set, the filename of the dataset is used. For a tuple, the resulting path will be given by joining the elements of the tuple suffix : string, optional Specify the image type by its suffix. If not specified, the output type will be inferred from the filename. Defaults to '.png'. mpl_kwargs : dict, optional A dict of keyword arguments to be passed to matplotlib. >>> slc.save(mpl_kwargs={"bbox_inches": "tight"}) """ names = [] if mpl_kwargs is None: mpl_kwargs = {} elif "format" in mpl_kwargs: new_suffix = mpl_kwargs.pop("format") if new_suffix != suffix: warnings.warn( f"Overriding suffix {suffix!r} with mpl_kwargs['format'] = {new_suffix!r}. " "Use the `suffix` argument directly to suppress this warning.", stacklevel=2, ) suffix = new_suffix if name is None: name = str(self.ds) elif isinstance(name, (list, tuple)): if not all(isinstance(_, str) for _ in name): raise TypeError( f"Expected a single str or an iterable of str, got {name!r}" ) name = os.path.join(*name) name = os.path.expanduser(name) parent_dir, _, prefix1 = name.replace(os.sep, "/").rpartition("/") parent_dir = parent_dir.replace("/", os.sep) if parent_dir and not os.path.isdir(parent_dir): ensure_dir(parent_dir) if name.endswith(("/", os.path.sep)): name = os.path.join(name, str(self.ds)) new_name = validate_image_name(name, suffix) if new_name == name: for v in self.plots.values(): out_name = v.save(name, mpl_kwargs) names.append(out_name) return names name = new_name prefix, suffix = os.path.splitext(name) if hasattr(self.data_source, "axis"): axis = self.ds.coordinates.axis_name.get(self.data_source.axis, "") else: axis = None weight = None stddev = None plot_type = self._plot_type if plot_type in ["Projection", "OffAxisProjection"]: weight = self.data_source.weight_field if weight is not None: weight = weight[1].replace(" ", "_") if getattr(self.data_source, "moment", 1) == 2: stddev = "standard_deviation" if "Cutting" in self.data_source.__class__.__name__: plot_type = "OffAxisSlice" for k, v in self.plots.items(): if isinstance(k, tuple): k = k[1] if plot_type is None: # implemented this check to make mypy happy, because we can't use str.join # with PlotContainer._plot_type = None raise TypeError(f"{self.__class__} is missing a _plot_type value (str)") name_elements = [prefix, plot_type] if axis: name_elements.append(axis) name_elements.append(k.replace(" ", "_")) if weight: name_elements.append(weight) if stddev: name_elements.append(stddev) name = "_".join(name_elements) + suffix names.append(v.save(name, mpl_kwargs)) return names @invalidate_data def refresh(self): # invalidate_data will take care of everything return self @validate_plot def show(self): r"""This will send any existing plots to the IPython notebook. If yt is being run from within an IPython session, and it is able to determine this, this function will send any existing plots to the notebook for display. If yt can't determine if it's inside an IPython session, it will raise YTNotInsideNotebook. Examples -------- >>> from yt import SlicePlot >>> slc = SlicePlot( ... ds, "x", [("gas", "density"), ("gas", "velocity_magnitude")] ... ) >>> slc.show() """ interactivity = self.plots[list(self.plots.keys())[0]].interactivity if interactivity: for v in sorted(self.plots.values()): v.show() else: if IS_IPYTHON: from IPython.display import display display(self) else: raise YTNotInsideNotebook @validate_plot def display(self, name=None, mpl_kwargs=None): """Will attempt to show the plot in in an IPython notebook. Failing that, the plot will be saved to disk.""" try: return self.show() except YTNotInsideNotebook: return self.save(name=name, mpl_kwargs=mpl_kwargs) @validate_plot def _repr_html_(self): """Return an html representation of the plot object. Will display as a png for each WindowPlotMPL instance in self.plots""" ret = "" for field in self.plots: img = base64.b64encode(self.plots[field]._repr_png_()).decode() ret += ( r'
' ) return ret @invalidate_plot def set_xlabel(self, label): r""" Allow the user to modify the X-axis title Defaults to the global value. Fontsize defaults to 18. Parameters ---------- label : str The new string for the x-axis. >>> plot.set_xlabel("H2I Number Density (cm$^{-3}$)") """ self._xlabel = label return self @invalidate_plot def set_ylabel(self, label): r""" Allow the user to modify the Y-axis title Defaults to the global value. Parameters ---------- label : str The new string for the y-axis. >>> plot.set_ylabel("Temperature (K)") """ self._ylabel = label return self def _get_axes_unit_labels(self, unit_x, unit_y): axes_unit_labels = ["", ""] comoving = False hinv = False for i, un in enumerate((unit_x, unit_y)): unn = None if hasattr(self.data_source, "axis"): if hasattr(self.ds.coordinates, "image_units"): # This *forces* an override unn = self.ds.coordinates.image_units[self.data_source.axis][i] elif hasattr(self.ds.coordinates, "default_unit_label"): axax = getattr(self.ds.coordinates, f"{'xy'[i]}_axis")[ self.data_source.axis ] unn = self.ds.coordinates.default_unit_label.get(axax, None) if unn in (1, "1", "dimensionless"): axes_unit_labels[i] = "" continue if unn is not None: axes_unit_labels[i] = _get_units_label(unn).strip("$") continue # Use sympy to factor h out of the unit. In this context 'un' # is a string, so we call the Unit constructor. expr = Unit(un, registry=self.ds.unit_registry).expr h_expr = Unit("h", registry=self.ds.unit_registry).expr # See http://docs.sympy.org/latest/modules/core.html#sympy.core.expr.Expr h_power = expr.as_coeff_exponent(h_expr)[1] # un is now the original unit, but with h factored out. un = str(expr * h_expr ** (-1 * h_power)) un_unit = Unit(un, registry=self.ds.unit_registry) cm = Unit("cm").expr if str(un).endswith("cm") and cm not in un_unit.expr.atoms(): comoving = True un = un[:-2] # no length units besides code_length end in h so this is safe if h_power == -1: hinv = True elif h_power != 0: # It doesn't make sense to scale a position by anything # other than h**-1 raise RuntimeError if un not in ["1", "u", "unitary"]: if un in formatted_length_unit_names: un = formatted_length_unit_names[un] else: un = Unit(un, registry=self.ds.unit_registry) un = un.latex_representation() if hinv: un = un + r"\,h^{-1}" if comoving: un = un + r"\,(1+z)^{-1}" pp = un[0] if pp in latex_prefixes: symbol_wo_prefix = un[1:] if symbol_wo_prefix in self.ds.unit_registry.prefixable_units: un = un.replace(pp, "{" + latex_prefixes[pp] + "}", 1) axes_unit_labels[i] = _get_units_label(un).strip("$") return axes_unit_labels def hide_colorbar(self, field=None): """ Hides the colorbar for a plot and updates the size of the plot accordingly. Defaults to operating on all fields for a PlotContainer object. Parameters ---------- field : string, field tuple, or list of strings or field tuples (optional) The name of the field(s) that we want to hide the colorbar. If None or 'all' is provided, will default to using all fields available for this object. Examples -------- This will save an image with no colorbar. >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = SlicePlot(ds, 2, "density", "c", (20, "kpc")) >>> s.hide_colorbar() >>> s.save() This will save an image with no axis or colorbar. >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = SlicePlot(ds, 2, "density", "c", (20, "kpc")) >>> s.hide_axes() >>> s.hide_colorbar() >>> s.save() """ if field is None or field == "all": field = self.plots.keys() for f in self.data_source._determine_fields(field): self.plots[f].hide_colorbar() return self def show_colorbar(self, field=None): """ Shows the colorbar for a plot and updates the size of the plot accordingly. Defaults to operating on all fields for a PlotContainer object. See hide_colorbar(). Parameters ---------- field : string, field tuple, or list of strings or field tuples (optional) The name of the field(s) that we want to show the colorbar. """ if field is None: field = self.fields for f in iter_fields(field): self.plots[f].show_colorbar() return self def hide_axes(self, field=None, draw_frame=None): """ Hides the axes for a plot and updates the size of the plot accordingly. Defaults to operating on all fields for a PlotContainer object. Parameters ---------- field : string, field tuple, or list of strings or field tuples (optional) The name of the field(s) that we want to hide the axes. draw_frame : boolean If True, the axes frame will still be drawn. Defaults to False. See note below for more details. Examples -------- This will save an image with no axes. >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = SlicePlot(ds, 2, "density", "c", (20, "kpc")) >>> s.hide_axes() >>> s.save() This will save an image with no axis or colorbar. >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = SlicePlot(ds, 2, "density", "c", (20, "kpc")) >>> s.hide_axes() >>> s.hide_colorbar() >>> s.save() Note ---- By default, when removing the axes, the patch on which the axes are drawn is disabled, making it impossible to later change e.g. the background colour. To force the axes patch to be displayed while still hiding the axes, set the ``draw_frame`` keyword argument to ``True``. """ if field is None: field = self.fields for f in iter_fields(field): self.plots[f].hide_axes(draw_frame=draw_frame) return self def show_axes(self, field=None): """ Shows the axes for a plot and updates the size of the plot accordingly. Defaults to operating on all fields for a PlotContainer object. See hide_axes(). Parameters ---------- field : string, field tuple, or list of strings or field tuples (optional) The name of the field(s) that we want to show the axes. """ if field is None: field = self.fields for f in iter_fields(field): self.plots[f].show_axes() return self class ImagePlotContainer(PlotContainer, abc.ABC): """A container for plots with colorbars.""" _colorbar_valid = False def __init__(self, data_source, figure_size, fontsize): super().__init__(data_source, figure_size, fontsize) self._callbacks = [] self._colorbar_label = PlotDictionary(self.data_source, lambda: None) def _get_default_handlers( self, field, default_display_units: Unit ) -> tuple[NormHandler, ColorbarHandler]: usr_units_str = get_default_from_config( self.data_source, field=field, keys="units", defaults=[None] ) if usr_units_str is not None: usr_units = Unit(usr_units_str) d1 = usr_units.dimensions d2 = default_display_units.dimensions if d1 == d2: display_units = usr_units elif getattr(self, "projected", False) and d2 / d1 == length: path_length_units = Unit( ytcfg.get_most_specific( "plot", *field, "path_length_units", fallback="cm" ), registry=self.data_source.ds.unit_registry, ) display_units = usr_units * path_length_units else: raise YTConfigurationError( f"Invalid units in configuration file for field {field!r}. " f"Found {usr_units!r}" ) else: display_units = default_display_units pnh = NormHandler(self.data_source, display_units=display_units) cbh = ColorbarHandler( cmap=get_default_from_config( self.data_source, field=field, keys="cmap", defaults=[None], ) ) return pnh, cbh @accepts_all_fields @invalidate_plot def set_cmap(self, field, cmap): """set the colormap for one of the fields Parameters ---------- field : string the field to set the colormap if field == 'all', applies to all plots. cmap : string or tuple If a string, will be interpreted as name of the colormap. If a tuple, it is assumed to be of the form (name, type, number) to be used for palettable functionality. (name, type, number, bool) can be used to specify if a reverse colormap is to be used. """ self._colorbar_valid = False self.plots[field].colorbar_handler.cmap = cmap return self @accepts_all_fields @invalidate_plot def set_background_color(self, field, color=None): """set the background color to match provided color Parameters ---------- field : string the field to set the colormap if field == 'all', applies to all plots. color : string or RGBA tuple (optional) if set, set the background color to this color if unset, background color is set to the bottom value of the color map """ cbh = self[field].colorbar_handler cbh.background_color = color return self @accepts_all_fields @invalidate_plot def set_zlim( self, field, zmin: float | Quantity | Literal["min"] | Unset = UNSET, zmax: float | Quantity | Literal["max"] | Unset = UNSET, dynamic_range: float | None = None, ): """set the scale of the colormap Parameters ---------- field : string the field to set a colormap scale if field == 'all', applies to all plots. zmin : float, Quantity, or 'min' the new minimum of the colormap scale. If 'min', will set to the minimum value in the current view. zmax : float, Quantity, or 'max' the new maximum of the colormap scale. If 'max', will set to the maximum value in the current view. Other Parameters ---------------- dynamic_range : float (default: None) The dynamic range of the image. If zmin == None, will set zmin = zmax / dynamic_range If zmax == None, will set zmax = zmin * dynamic_range """ if zmin is UNSET and zmax is UNSET: raise TypeError("Missing required argument zmin or zmax") if zmin is UNSET: zmin = None elif zmin is None: # this sentinel value juggling is barely maintainable # this use case is deprecated so we can simplify the logic here # in the future and use `None` as the default value, # instead of the custom sentinel UNSET issue_deprecation_warning( "Passing `zmin=None` explicitly is deprecated. " "If you wish to explicitly set zmin to the minimal " "data value, pass `zmin='min'` instead. " "Otherwise leave this argument unset.", since="4.1", stacklevel=5, ) zmin = "min" if zmax is UNSET: zmax = None elif zmax is None: # see above issue_deprecation_warning( "Passing `zmax=None` explicitly is deprecated. " "If you wish to explicitly set zmax to the maximal " "data value, pass `zmax='max'` instead. " "Otherwise leave this argument unset.", since="4.1", stacklevel=5, ) zmax = "max" pnh = self.plots[field].norm_handler pnh.vmin = zmin pnh.vmax = zmax pnh.dynamic_range = dynamic_range return self @accepts_all_fields @invalidate_plot def set_colorbar_minorticks(self, field, state): """turn colorbar minor ticks on or off in the current plot Displaying minor ticks reduces performance; turn them off using set_colorbar_minorticks('all', False) if drawing speed is a problem. Parameters ---------- field : string the field to remove colorbar minorticks if field == 'all', applies to all plots. state : bool the state indicating 'on' (True) or 'off' (False) """ self.plots[field].colorbar_handler.draw_minorticks = state return self @invalidate_plot def set_colorbar_label(self, field, label): r""" Sets the colorbar label. Parameters ---------- field : str or tuple The name of the field to modify the label for. label : str The new label >>> plot.set_colorbar_label( ... ("gas", "density"), "Dark Matter Density (g cm$^{-3}$)" ... ) """ field = self.data_source._determine_fields(field) self._colorbar_label[field] = label return self def _get_axes_labels(self, field): return (self._xlabel, self._ylabel, self._colorbar_label[field]) class BaseLinePlot(PlotContainer, abc.ABC): # A common ancestor to LinePlot and ProfilePlot @abc.abstractmethod def _get_axrect(self): pass def _get_plot_instance(self, field): if field in self.plots: return self.plots[field] axrect = self._get_axrect() pnh = NormHandler( self.data_source, display_units=self.data_source.ds.field_info[field].units ) finfo = self.data_source.ds._get_field_info(field) if not finfo.take_log: pnh.norm_type = Normalize plot = PlotMPL(self.figure_size, axrect, norm_handler=pnh) self.plots[field] = plot return plot yt-project-yt-f043ac8/yt/visualization/plot_modifications.py000066400000000000000000003701561510711153200244630ustar00rootroot00000000000000import inspect import re import sys import warnings from abc import ABC, abstractmethod from functools import update_wrapper from numbers import Integral, Number from typing import Any, TypeGuard import matplotlib import numpy as np from unyt import unyt_quantity from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import AnyFieldKey, FieldKey from yt.data_objects.data_containers import YTDataContainer from yt.data_objects.level_sets.clump_handling import Clump from yt.data_objects.selection_objects.cut_region import YTCutRegion from yt.frontends.ytdata.data_structures import YTClumpContainer from yt.funcs import is_sequence, mylog, validate_width_tuple from yt.geometry.api import Geometry from yt.geometry.unstructured_mesh_handler import UnstructuredIndex from yt.units import dimensions from yt.units._numpy_wrapper_functions import uhstack from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.exceptions import ( YTDataTypeUnsupported, YTFieldNotFound, YTFieldTypeNotFound, YTUnsupportedPlotCallback, ) from yt.utilities.lib.geometry_utils import triangle_plane_intersect from yt.utilities.lib.line_integral_convolution import line_integral_convolution_2d from yt.utilities.lib.mesh_triangulation import triangulate_indices from yt.utilities.lib.pixelization_routines import ( pixelize_cartesian, pixelize_off_axis_cartesian, ) from yt.utilities.math_utils import periodic_ray from yt.visualization._commons import ( _swap_arg_pair_order, _swap_axes_extents, invalidate_plot, ) from yt.visualization.base_plot_types import CallbackWrapper from yt.visualization.image_writer import apply_colormap from yt.visualization.plot_window import PWViewerMPL if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never callback_registry: dict[str, type["PlotCallback"]] = {} def _validate_factor_tuple(factor) -> tuple[int, int]: if ( is_sequence(factor) and len(factor) == 2 and all(isinstance(_, Integral) for _ in factor) ): # - checking for "is_sequence" allows lists, numpy arrays and other containers # - checking for Integral type allows numpy integer types # in any case we return a with strict typing return (int(factor[0]), int(factor[1])) elif isinstance(factor, Integral): return (int(factor), int(factor)) else: raise TypeError( f"Expected a single, or a pair of integers, received {factor!r}" ) class PlotCallback(ABC): # _supported_geometries is set by subclasses of PlotCallback to a tuple of # strings corresponding to the names of the geometries that a callback # supports. By default it is None, which means it supports everything. # Note that if there's a coord_system parameter that is set to "axis" or # "figure" this is disregarded. If "force" is included in the tuple, it # will *not* check whether or not the coord_system is in axis or figure, # and will only look at the geometries. _supported_geometries: tuple[str, ...] | None = None _incompatible_plot_types: tuple[str, ...] = () def __init_subclass__(cls, *args, **kwargs): if inspect.isabstract(cls): return # register class callback_registry[cls.__name__] = cls # create a PWViewerMPL method by wrapping __init__ if cls.__init__.__doc__ is None: # allow docstring definition at the class level instead of __init__ cls.__init__.__doc__ = cls.__doc__ supported_geometries = cls._supported_geometries incompatible_plot_types = cls._incompatible_plot_types type_name = cls._type_name @invalidate_plot def closure(self, *args, **kwargs): nonlocal supported_geometries nonlocal incompatible_plot_types nonlocal type_name geom = self.ds.geometry if not ( supported_geometries is None or geom in supported_geometries or ( kwargs.get("coord_system") in ("axis", "figure") and "force" not in supported_geometries ) ): raise YTDataTypeUnsupported(geom, supported_geometries) if self._plot_type in incompatible_plot_types: raise YTUnsupportedPlotCallback(type_name, self._plot_type) self._callbacks.append(cls(*args, **kwargs)) return self update_wrapper( wrapper=closure, wrapped=cls.__init__, assigned=("__annotations__", "__doc__"), ) method_name = "annotate_" + type_name closure.__name__ = method_name setattr(PWViewerMPL, method_name, closure) @abstractmethod def __init__(self, *args, **kwargs) -> None: pass @abstractmethod def __call__(self, plot: CallbackWrapper) -> Any: pass def _project_coords(self, plot, coord): """ Convert coordinates from simulation data coordinates to projected data coordinates. Simulation data coordinates are three dimensional, and can either be specified as a YTArray or as a list or array in code_length units. Projected data units are 2D versions of the simulation data units relative to the axes of the final plot. """ if len(coord) == 3: if not isinstance(coord, YTArray): coord_copy = plot.data.ds.arr(coord, "code_length") else: # coord is being copied so that if the user has a unyt_array already # we don't change the user's version coord_copy = coord.to("code_length") ax = plot.data.axis # if this is an on-axis projection or slice, then # just grab the appropriate 2 coords for the on-axis view if ax is not None: (xi, yi) = ( plot.data.ds.coordinates.x_axis[ax], plot.data.ds.coordinates.y_axis[ax], ) ret_coord = (coord_copy[xi], coord_copy[yi]) # if this is an off-axis project or slice (ie cutting plane) # we have to calculate where the data coords fall in the projected # plane else: # transpose is just to get [[x1,x2,...],[y1,y2,...],[z1,z2,...]] # in the same order as plot.data.center for array arithmetic coord_vectors = coord_copy.transpose() - plot.data.center x = np.dot(coord_vectors, plot.data.orienter.unit_vectors[1]) y = np.dot(coord_vectors, plot.data.orienter.unit_vectors[0]) # Transpose into image coords. Due to VR being not a # right-handed coord system ret_coord = (y, x) # if the position is already two-coords, it is expected to be # in the proper projected orientation else: raise ValueError("'data' coordinates must be 3 dimensions") return ret_coord def _convert_to_plot(self, plot, coord, offset=True): """ Convert coordinates from projected data coordinates to PlotWindow plot coordinates. Projected data coordinates are two dimensional and refer to the location relative to the specific axes being plotted, although still in simulation units. PlotWindow plot coordinates are locations as found in the final plot, usually with the origin in the center of the image and the extent of the image defined by the final plot axis markers. """ # coord should be a 2 x ncoord array-like datatype. try: ncoord = np.array(coord).shape[1] except IndexError: ncoord = 1 # Convert the data and plot limits to tiled numpy arrays so that # convert_to_plot is automatically vectorized. phy_bounds = self._physical_bounds(plot) x0, x1, y0, y1 = (np.array(np.tile(xyi, ncoord)) for xyi in phy_bounds) plt_bounds = self._plot_bounds(plot) xx0, xx1, yy0, yy1 = (np.tile(xyi, ncoord) for xyi in plt_bounds) try: ccoord = np.array(coord.to("code_length")) except AttributeError: ccoord = np.array(coord) # We need a special case for when we are only given one coordinate. if ccoord.shape == (2,): return np.array( [ ((ccoord[0] - x0) / (x1 - x0) * (xx1 - xx0) + xx0)[0], ((ccoord[1] - y0) / (y1 - y0) * (yy1 - yy0) + yy0)[0], ] ) else: return np.array( [ (ccoord[0][:] - x0) / (x1 - x0) * (xx1 - xx0) + xx0, (ccoord[1][:] - y0) / (y1 - y0) * (yy1 - yy0) + yy0, ] ) def _sanitize_coord_system(self, plot, coord, coord_system): """ Given a set of one or more x,y (and z) coordinates and a coordinate system, convert the coordinates (and transformation) ready for final plotting. Parameters ---------- plot: a PlotMPL subclass The plot that we are converting coordinates for coord: array-like Coordinates in some coordinate system: [x,y,z]. Alternatively, can specify multiple coordinates as: [[x1,x2,...,xn], [y1, y2,...,yn], [z1,z2,...,zn]] coord_system: string Possible values include: * ``'data'`` 3D data coordinates relative to original dataset * ``'plot'`` 2D coordinates as defined by the final axis locations * ``'axis'`` 2D coordinates within the axis object from (0,0) in lower left to (1,1) in upper right. Same as matplotlib axis coords. * ``'figure'`` 2D coordinates within figure object from (0,0) in lower left to (1,1) in upper right. Same as matplotlib figure coords. """ # Assure coords are either a YTArray or numpy array coord = np.asanyarray(coord, dtype="float64") # if in data coords, project them to plot coords if coord_system == "data": if len(coord) < 3: raise ValueError( "Coordinates in 'data' coordinate system need to be in 3D" ) coord = self._project_coords(plot, coord) coord = self._convert_to_plot(plot, coord) # if in plot coords, define the transform correctly if coord_system == "data" or coord_system == "plot": self.transform = plot._axes.transData return coord # if in axis coords, define the transform correctly if coord_system == "axis": self.transform = plot._axes.transAxes if len(coord) > 2: raise ValueError( "Coordinates in 'axis' coordinate system need to be in 2D" ) return coord # if in figure coords, define the transform correctly elif coord_system == "figure": self.transform = plot._figure.transFigure return coord else: raise ValueError( "Argument coord_system must have a value of " "'data', 'plot', 'axis', or 'figure'." ) def _physical_bounds(self, plot): xlims = tuple(v.in_units("code_length") for v in plot.xlim) ylims = tuple(v.in_units("code_length") for v in plot.ylim) # _swap_axes note: do NOT need to unswap here because plot (a CallbackWrapper # instance) stores the x, y lims of the underlying data object. return xlims + ylims def _plot_bounds(self, plot): xlims = plot._axes.get_xlim() ylims = plot._axes.get_ylim() # _swap_axes note: because we are getting the plot limits from the axes # object, if the axes have been swapped, these will be reversed from the # _physical_bounds. So we need to unswap here, but not in _physical_bounds. if plot._swap_axes: return ylims + xlims return xlims + ylims def _pixel_scale(self, plot): x0, x1, y0, y1 = self._physical_bounds(plot) xx0, xx1, yy0, yy1 = self._plot_bounds(plot) dx = (xx1 - xx0) / (x1 - x0) dy = (yy1 - yy0) / (y1 - y0) return dx, dy def _set_font_properties(self, plot, labels, **kwargs): """ This sets all of the text instances created by a callback to have the same font size and properties as all of the other fonts in the figure. If kwargs are set, they override the defaults. """ # This is a little messy because there is no trivial way to update # a MPL.font_manager.FontProperties object with new attributes # aside from setting them individually. So we pick out the relevant # MPL.Text() kwargs from the local kwargs and let them override the # defaults. local_font_properties = plot.font_properties.copy() # Turn off the default TT font file, otherwise none of this works. local_font_properties.set_file(None) local_font_properties.set_family("stixgeneral") if "family" in kwargs: local_font_properties.set_family(kwargs["family"]) if "file" in kwargs: local_font_properties.set_file(kwargs["file"]) if "fontconfig_pattern" in kwargs: local_font_properties.set_fontconfig_pattern(kwargs["fontconfig_pattern"]) if "name" in kwargs: local_font_properties.set_name(kwargs["name"]) if "size" in kwargs: local_font_properties.set_size(kwargs["size"]) if "slant" in kwargs: local_font_properties.set_slant(kwargs["slant"]) if "stretch" in kwargs: local_font_properties.set_stretch(kwargs["stretch"]) if "style" in kwargs: local_font_properties.set_style(kwargs["style"]) if "variant" in kwargs: local_font_properties.set_variant(kwargs["variant"]) if "weight" in kwargs: local_font_properties.set_weight(kwargs["weight"]) # For each label, set the font properties and color to the figure # defaults if not already set in the callback itself for label in labels: if plot.font_color is not None and "color" not in kwargs: label.set_color(plot.font_color) label.set_fontproperties(local_font_properties) def _set_plot_limits(self, plot, extent=None) -> None: """ calls set_xlim, set_ylim for plot, accounting for swapped axes Parameters ---------- plot : CallbackWrapper a CallbackWrapper instance extent : tuple or list The raw extent (prior to swapping). if None, will fetch it. """ if extent is None: extent = self._plot_bounds(plot) if plot._swap_axes: extent = _swap_axes_extents(extent) plot._axes.set_xlim(extent[0], extent[1]) plot._axes.set_ylim(extent[2], extent[3]) @staticmethod def _sanitize_xy_order(plot, *args): """ flips x-y pairs of plot arguments if needed Parameters ---------- plot : CallbackWrapper a CallbackWrapper instance *args x, y plot arguments, must have an even number of *args Returns ------- tuple either the original args or new args, with (x, y) pairs switched. i.e., _sanitize_xy_order(plot, x, y, px, py) returns: x, y, px, py if plot._swap_axes is False y, x, py, px if plot._swap_axes is True """ if plot._swap_axes: return _swap_arg_pair_order(*args) return args class VelocityCallback(PlotCallback): """ Adds a 'quiver' plot of velocity to the plot, skipping all but every *factor* datapoint. *scale* is the data units per arrow length unit using *scale_units* and *plot_args* allows you to pass in matplotlib arguments (see matplotlib.axes.Axes.quiver for more info). if *normalize* is True, the velocity fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. """ _type_name = "velocity" _supported_geometries = ( "cartesian", "spectral_cube", "polar", "cylindrical", "spherical", ) _incompatible_plot_types = ("OffAxisProjection", "Particle") def __init__( self, factor: tuple[int, int] | int = 16, *, scale=None, scale_units=None, normalize=False, plot_args=None, **kwargs, ): self.factor = _validate_factor_tuple(factor) self.scale = scale self.scale_units = scale_units self.normalize = normalize if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) else: plot_args = kwargs self.plot_args = plot_args def __call__(self, plot) -> "BaseQuiverCallback": ftype = plot.data._current_fluid_type # Instantiation of these is cheap geometry: Geometry = plot.data.ds.geometry if plot._type_name == "CuttingPlane": if geometry is Geometry.CARTESIAN: pass elif ( geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL or geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): raise NotImplementedError( f"annotate_velocity is not supported for cutting plane for {geometry=}" ) else: assert_never(geometry) qcb: BaseQuiverCallback if plot._type_name == "CuttingPlane": qcb = CuttingQuiverCallback( (ftype, "cutting_plane_velocity_x"), (ftype, "cutting_plane_velocity_y"), factor=self.factor, scale=self.scale, normalize=self.normalize, scale_units=self.scale_units, **self.plot_args, ) else: xax = plot.data.ds.coordinates.x_axis[plot.data.axis] yax = plot.data.ds.coordinates.y_axis[plot.data.axis] axis_names = plot.data.ds.coordinates.axis_name bv = plot.data.get_field_parameter("bulk_velocity") if bv is not None: bv_x = bv[xax] bv_y = bv[yax] else: bv_x = bv_y = 0 if geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL: if axis_names[plot.data.axis] == "z": # polar_z and cyl_z is aligned with cartesian_z # should convert r-theta plane to x-y plane xv = (ftype, "velocity_cartesian_x") yv = (ftype, "velocity_cartesian_y") else: xv = (ftype, f"velocity_{axis_names[xax]}") yv = (ftype, f"velocity_{axis_names[yax]}") elif geometry is Geometry.SPHERICAL: if axis_names[plot.data.axis] == "phi": xv = (ftype, "velocity_cylindrical_radius") yv = (ftype, "velocity_cylindrical_z") elif axis_names[plot.data.axis] == "theta": xv = (ftype, "velocity_conic_x") yv = (ftype, "velocity_conic_y") else: raise NotImplementedError( f"annotate_velocity is missing support for normal={axis_names[plot.data.axis]!r}" ) elif geometry is Geometry.CARTESIAN: xv = (ftype, f"velocity_{axis_names[xax]}") yv = (ftype, f"velocity_{axis_names[yax]}") elif ( geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): raise NotImplementedError( f"annotate_velocity is not supported for {geometry=}" ) else: assert_never(geometry) # determine the full fields including field type xv = plot.data._determine_fields(xv)[0] yv = plot.data._determine_fields(yv)[0] qcb = QuiverCallback( xv, yv, factor=self.factor, scale=self.scale, scale_units=self.scale_units, normalize=self.normalize, bv_x=bv_x, bv_y=bv_y, **self.plot_args, ) return qcb(plot) class MagFieldCallback(PlotCallback): """ Adds a 'quiver' plot of magnetic field to the plot, skipping all but every *factor* datapoint. *scale* is the data units per arrow length unit using *scale_units* and *plot_args* allows you to pass in matplotlib arguments (see matplotlib.axes.Axes.quiver for more info). if *normalize* is True, the magnetic fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. """ _type_name = "magnetic_field" _supported_geometries = ( "cartesian", "spectral_cube", "polar", "cylindrical", "spherical", ) _incompatible_plot_types = ("OffAxisProjection", "Particle") def __init__( self, factor: tuple[int, int] | int = 16, *, scale=None, scale_units=None, normalize=False, plot_args=None, **kwargs, ): self.factor = _validate_factor_tuple(factor) self.scale = scale self.scale_units = scale_units self.normalize = normalize if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) else: plot_args = kwargs self.plot_args = plot_args def __call__(self, plot) -> "BaseQuiverCallback": ftype = plot.data._current_fluid_type # Instantiation of these is cheap geometry: Geometry = plot.data.ds.geometry qcb: BaseQuiverCallback if plot._type_name == "CuttingPlane": if geometry is Geometry.CARTESIAN: pass elif ( geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL or geometry is Geometry.SPHERICAL or geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): raise NotImplementedError( f"annotate_magnetic_field is not supported for cutting plane for {geometry=}" ) else: assert_never(geometry) qcb = CuttingQuiverCallback( (ftype, "cutting_plane_magnetic_field_x"), (ftype, "cutting_plane_magnetic_field_y"), factor=self.factor, scale=self.scale, scale_units=self.scale_units, normalize=self.normalize, **self.plot_args, ) else: xax = plot.data.ds.coordinates.x_axis[plot.data.axis] yax = plot.data.ds.coordinates.y_axis[plot.data.axis] axis_names = plot.data.ds.coordinates.axis_name if geometry is Geometry.POLAR or geometry is Geometry.CYLINDRICAL: if axis_names[plot.data.axis] == "z": # polar_z and cyl_z is aligned with cartesian_z # should convert r-theta plane to x-y plane xv = (ftype, "magnetic_field_cartesian_x") yv = (ftype, "magnetic_field_cartesian_y") else: xv = (ftype, f"magnetic_field_{axis_names[xax]}") yv = (ftype, f"magnetic_field_{axis_names[yax]}") elif geometry is Geometry.SPHERICAL: if axis_names[plot.data.axis] == "phi": xv = (ftype, "magnetic_field_cylindrical_radius") yv = (ftype, "magnetic_field_cylindrical_z") elif axis_names[plot.data.axis] == "theta": xv = (ftype, "magnetic_field_conic_x") yv = (ftype, "magnetic_field_conic_y") else: raise NotImplementedError( f"annotate_magnetic_field is missing support for normal={axis_names[plot.data.axis]!r}" ) elif geometry is Geometry.CARTESIAN: xv = (ftype, f"magnetic_field_{axis_names[xax]}") yv = (ftype, f"magnetic_field_{axis_names[yax]}") elif ( geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC or geometry is Geometry.SPECTRAL_CUBE ): raise NotImplementedError( f"annotate_magnetic_field is not supported for {geometry=}" ) else: assert_never(geometry) qcb = QuiverCallback( xv, yv, factor=self.factor, scale=self.scale, scale_units=self.scale_units, normalize=self.normalize, **self.plot_args, ) return qcb(plot) class BaseQuiverCallback(PlotCallback, ABC): _incompatible_plot_types = ("OffAxisProjection", "Particle") def __init__( self, field_x, field_y, field_c=None, *, factor: tuple[int, int] | int = 16, scale=None, scale_units=None, normalize=False, plot_args=None, **kwargs, ): self.field_x = field_x self.field_y = field_y self.field_c = field_c self.factor = _validate_factor_tuple(factor) self.scale = scale self.scale_units = scale_units self.normalize = normalize if plot_args is None: plot_args = kwargs else: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) self.plot_args = plot_args @abstractmethod def _get_quiver_data(self, plot, bounds: tuple, nx: int, ny: int): # must return (pixX, pixY, pixC) arrays (pixC can be None) pass def __call__(self, plot): # construct mesh bounds = self._physical_bounds(plot) nx = plot.raw_image_shape[1] // self.factor[0] ny = plot.raw_image_shape[0] // self.factor[1] xx0, xx1, yy0, yy1 = self._plot_bounds(plot) if plot._transform is None: X, Y = np.meshgrid( np.linspace(xx0, xx1, nx, endpoint=True), np.linspace(yy0, yy1, ny, endpoint=True), ) else: # when we have a cartopy transform, provide the x, y values # in the coordinate reference system of the data and let cartopy # do the transformation. Also check for the exact bounds of the transform # which can cause issues with projections. tform_bnds = plot._transform.x_limits + plot._transform.y_limits if any(b.d == tb for b, tb in zip(bounds, tform_bnds, strict=True)): # note: cartopy will also raise its own warning, but it is useful to add this # warning as well since the only way to avoid the exact bounds is to change the # extent of the plot. warnings.warn( "Using the exact bounds of the transform may cause errors at the bounds." " To avoid this warning, adjust the width of your plot object to not include " "the bounds.", stacklevel=2, ) X, Y = np.meshgrid( np.linspace(bounds[0].d, bounds[1].d, nx, endpoint=True), np.linspace(bounds[2].d, bounds[3].d, ny, endpoint=True), ) pixX, pixY, pixC = self._get_quiver_data(plot, bounds, nx, ny) retv = self._finalize(plot, X, Y, pixX, pixY, pixC) self._set_plot_limits(plot, (xx0, xx1, yy0, yy1)) return retv def _finalize(self, plot, X, Y, pixX, pixY, pixC): if self.normalize: nn = np.sqrt(pixX**2 + pixY**2) nn = np.where(nn == 0, 1, nn) pixX /= nn pixY /= nn X, Y, pixX, pixY = self._sanitize_xy_order(plot, X, Y, pixX, pixY) # quiver plots ignore x, y axes inversions when using angles="uv" (the # default), so reverse the direction of the vectors when flipping the axis. if self.plot_args.get("angles", None) != "xy": if plot._flip_vertical: pixY = -1 * pixY if plot._flip_horizontal: pixX = -1 * pixX args = [X, Y, pixX, pixY] if pixC is not None: args.append(pixC) kwargs = { "scale": self.scale, "scale_units": self.scale_units, } kwargs.update(self.plot_args) if plot._transform is not None: if "transform" in kwargs: msg = ( "The base plot already has a transform set, ignoring the provided keyword " "argument and using the base transform." ) warnings.warn(msg, stacklevel=2) kwargs["transform"] = plot._transform return plot._axes.quiver(*args, **kwargs) class QuiverCallback(BaseQuiverCallback): """ Adds a 'quiver' plot to any plot, using the *field_x* and *field_y* from the associated data, skipping every *factor* pixels. *field_c* is an optional field name used for color. *scale* is the data units per arrow length unit using *scale_units* and *plot_args* allows you to pass in matplotlib arguments (see matplotlib.axes.Axes.quiver for more info). if *normalize* is True, the fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. """ _type_name = "quiver" _supported_geometries: tuple[str, ...] = ( "cartesian", "spectral_cube", "polar", "cylindrical", "spherical", "geographic", "internal_geographic", ) def __init__( self, field_x, field_y, field_c=None, *, factor: tuple[int, int] | int = 16, scale=None, scale_units=None, normalize=False, bv_x=0, bv_y=0, plot_args=None, **kwargs, ): super().__init__( field_x, field_y, field_c, factor=factor, scale=scale, scale_units=scale_units, normalize=normalize, plot_args=plot_args, **kwargs, ) self.bv_x = bv_x self.bv_y = bv_y def _get_quiver_data(self, plot, bounds: tuple, nx: int, ny: int): # calls the pixelizer, returns pixX, pixY, pixC arrays def transform(field_name, vector_value): field_units = plot.data[field_name].units def _transformed_field(field, data): return data[field_name] - data.ds.arr(vector_value, field_units) plot.data.ds.add_field( ("gas", f"transformed_{field_name}"), sampling_type="cell", function=_transformed_field, units=field_units, display_field=False, ) if self.bv_x != 0.0 or self.bv_x != 0.0: # We create a relative vector field transform(self.field_x, self.bv_x) transform(self.field_y, self.bv_y) field_x = f"transformed_{self.field_x}" field_y = f"transformed_{self.field_y}" else: field_x, field_y = self.field_x, self.field_y periodic = int(any(plot.data.ds.periodicity)) pixX = plot.data.ds.coordinates.pixelize( plot.data.axis, plot.data, field_x, bounds, (nx, ny), False, # antialias periodic, ) pixY = plot.data.ds.coordinates.pixelize( plot.data.axis, plot.data, field_y, bounds, (nx, ny), False, # antialias periodic, ) if self.field_c is not None: pixC = plot.data.ds.coordinates.pixelize( plot.data.axis, plot.data, self.field_c, bounds, (nx, ny), False, # antialias periodic, ) else: pixC = None return pixX, pixY, pixC class ContourCallback(PlotCallback): """ Add contours in *field* to the plot. *levels* governs the number of contours generated, *factor* governs the number of points used in the interpolation, *take_log* governs how it is contoured and *clim* gives the (lower, upper) limits for contouring. An alternate data source can be specified with *data_source*, but by default the plot's data source will be queried. """ _type_name = "contour" _supported_geometries = ("cartesian", "spectral_cube", "cylindrical") _incompatible_plot_types = ("Particle",) def __init__( self, field: AnyFieldKey, levels: int = 5, *, factor: tuple[int, int] | int = 4, clim: tuple[float, float] | None = None, label: bool = False, take_log: bool | None = None, data_source: YTDataContainer | None = None, plot_args: dict[str, Any] | None = None, text_args: dict[str, Any] | None = None, ncont: int | None = None, # deprecated ) -> None: if ncont is not None: issue_deprecation_warning( "The `ncont` keyword argument is deprecated, use `levels` instead.", since="4.1", stacklevel=5, ) levels = ncont if clim is not None and not isinstance(levels, (int, np.integer)): raise TypeError(f"clim requires levels be an integer, received {levels}") self.levels = levels self.field = field self.factor = _validate_factor_tuple(factor) self.clim = clim self.take_log = take_log self.plot_args = { "colors": "black", "linestyles": "solid", **(plot_args or {}), } self.label = label self.text_args = { "colors": "white", **(text_args or {}), } self.data_source = data_source def __call__(self, plot) -> None: from matplotlib.tri import LinearTriInterpolator, Triangulation # These need to be in code_length x0, x1, y0, y1 = self._physical_bounds(plot) # These are in plot coordinates, which may not be code coordinates. xx0, xx1, yy0, yy1 = self._plot_bounds(plot) # See the note about rows/columns in the pixelizer for more information # on why we choose the bounds we do numPoints_x = plot.raw_image_shape[1] numPoints_y = plot.raw_image_shape[0] # Go from data->plot coordinates dx, dy = self._pixel_scale(plot) # We want xi, yi in plot coordinates xi, yi = np.mgrid[ xx0 : xx1 : numPoints_x / (self.factor[0] * 1j), yy0 : yy1 : numPoints_y / (self.factor[1] * 1j), ] data = self.data_source or plot.data if plot._type_name in ["CuttingPlane", "Projection", "Slice"]: if plot._type_name == "CuttingPlane": x = (data["px"] * dx).to("1") y = (data["py"] * dy).to("1") z = data[self.field] elif plot._type_name in ["Projection", "Slice"]: # Makes a copy of the position fields "px" and "py" and adds the # appropriate shift to the copied field. AllX = np.zeros(data["px"].size, dtype="bool") AllY = np.zeros(data["py"].size, dtype="bool") XShifted = data["px"].copy() YShifted = data["py"].copy() dom_x, dom_y = plot._period for shift in np.mgrid[-1:1:3j]: # type: ignore [misc] xlim = (data["px"] + shift * dom_x >= x0) & ( data["px"] + shift * dom_x <= x1 ) ylim = (data["py"] + shift * dom_y >= y0) & ( data["py"] + shift * dom_y <= y1 ) XShifted[xlim] += shift * dom_x YShifted[ylim] += shift * dom_y AllX |= xlim AllY |= ylim # At this point XShifted and YShifted are the shifted arrays of # position data in data coordinates wI = AllX & AllY # This converts XShifted and YShifted into plot coordinates # Note: we force conversion into "1" to prevent issues in case # one of the length has some dimensionless factor (Mpc/h) x = ((XShifted[wI] - x0) * dx).to("1").ndarray_view() + xx0 y = ((YShifted[wI] - y0) * dy).to("1").ndarray_view() + yy0 z = data[self.field][wI] # Both the input and output from the triangulator are in plot # coordinates triangulation = Triangulation(x, y) zi = LinearTriInterpolator(triangulation, z)(xi, yi) elif plot._type_name == "OffAxisProjection": zi = plot.frb[self.field][:: self.factor[0], :: self.factor[1]].transpose() take_log: bool if self.take_log is not None: take_log = self.take_log else: field = data._determine_fields([self.field])[0] take_log = plot.ds._get_field_info(field).take_log if take_log: zi = np.log10(zi) clim: tuple[float, float] | None if take_log and self.clim is not None: clim = np.log10(self.clim[0]), np.log10(self.clim[1]) else: clim = self.clim levels: np.ndarray | int if clim is not None: levels = np.linspace(clim[0], clim[1], self.levels) else: levels = self.levels xi, yi = self._sanitize_xy_order(plot, xi, yi) cset = plot._axes.contour(xi, yi, zi, levels, **self.plot_args) self._set_plot_limits(plot, (xx0, xx1, yy0, yy1)) if self.label: plot._axes.clabel(cset, **self.text_args) class GridBoundaryCallback(PlotCallback): """ Draws grids on an existing PlotWindow object. Adds grid boundaries to a plot, optionally with alpha-blending. By default, colors different levels of grids with different colors going from white to black, but you can change to any arbitrary colormap with cmap keyword, to all black grid edges for all levels with cmap=None and edgecolors=None, or to an arbitrary single color for grid edges with edgecolors='YourChosenColor' defined in any of the standard ways (e.g., edgecolors='white', edgecolors='r', edgecolors='#00FFFF', or edgecolor='0.3', where the last is a float in 0-1 scale indicating gray). Note that setting edgecolors overrides cmap if you have both set to non-None values. Cutoff for display is at min_pix wide. draw_ids puts the grid id a the corner of the grid (but its not so great in projections...). id_loc determines which corner holds the grid id. One can set min and maximum level of grids to display, and can change the linewidth of the displayed grids. """ _type_name = "grids" _supported_geometries = ("cartesian", "spectral_cube", "cylindrical") _incompatible_plot_types = ("OffAxisSlice", "OffAxisProjection", "Particle") def __init__( self, alpha=0.7, min_pix=1, min_pix_ids=20, draw_ids=False, id_loc=None, periodic=True, min_level=None, max_level=None, cmap="B-W LINEAR_r", edgecolors=None, linewidth=1.0, ): self.alpha = alpha self.min_pix = min_pix self.min_pix_ids = min_pix_ids self.draw_ids = draw_ids # put grid numbers in the corner. if id_loc is None: self.id_loc = "lower left" else: self.id_loc = id_loc.lower() # Make case-insensitive if not self.draw_ids: mylog.warning( "Supplied id_loc but draw_ids is False. Not drawing grid ids" ) self.periodic = periodic self.min_level = min_level self.max_level = max_level self.linewidth = linewidth self.cmap = cmap self.edgecolors = edgecolors def __call__(self, plot): if plot.data.ds.geometry == "cylindrical" and plot.data.ds.dimensionality == 3: raise NotImplementedError( "Grid annotation is only supported for \ for 2D cylindrical geometry, not 3D" ) from matplotlib.colors import colorConverter x0, x1, y0, y1 = self._physical_bounds(plot) xx0, xx1, yy0, yy1 = self._plot_bounds(plot) dx, dy = self._pixel_scale(plot) ypix, xpix = plot.raw_image_shape ax = plot.data.axis px_index = plot.data.ds.coordinates.x_axis[ax] py_index = plot.data.ds.coordinates.y_axis[ax] DW = plot.data.ds.domain_width if self.periodic: pxs, pys = np.mgrid[-1:1:3j, -1:1:3j] else: pxs, pys = np.mgrid[0:0:1j, 0:0:1j] GLE, GRE, levels, block_ids = [], [], [], [] for block, _mask in plot.data.blocks: GLE.append(block.LeftEdge.in_units("code_length")) GRE.append(block.RightEdge.in_units("code_length")) levels.append(block.Level) block_ids.append(block.id) if len(GLE) == 0: return # Retain both units and registry GLE = plot.ds.arr(GLE, units=GLE[0].units) GRE = plot.ds.arr(GRE, units=GRE[0].units) levels = np.array(levels) min_level = self.min_level or 0 max_level = self.max_level or levels.max() # sort the four arrays in order of ascending level, this makes images look nicer new_indices = np.argsort(levels) levels = levels[new_indices] GLE = GLE[new_indices] GRE = GRE[new_indices] block_ids = np.array(block_ids)[new_indices] for px_off, py_off in zip(pxs.ravel(), pys.ravel(), strict=True): pxo = px_off * DW[px_index] pyo = py_off * DW[py_index] # Note: [dx] = 1/length, [GLE] = length # we force conversion into "1" to prevent issues if e.g. GLE is in Mpc/h # where dx * GLE would have units 1/h rather than being truly dimensionless left_edge_x = np.array((((GLE[:, px_index] + pxo - x0) * dx) + xx0).to("1")) left_edge_y = np.array((((GLE[:, py_index] + pyo - y0) * dy) + yy0).to("1")) right_edge_x = np.array( (((GRE[:, px_index] + pxo - x0) * dx) + xx0).to("1") ) right_edge_y = np.array( (((GRE[:, py_index] + pyo - y0) * dy) + yy0).to("1") ) xwidth = xpix * (right_edge_x - left_edge_x) / (xx1 - xx0) ywidth = ypix * (right_edge_y - left_edge_y) / (yy1 - yy0) visible = np.logical_and( np.logical_and(xwidth > self.min_pix, ywidth > self.min_pix), np.logical_and(levels >= min_level, levels <= max_level), ) # Grids can either be set by edgecolors OR a colormap. if self.edgecolors is not None: edgecolors = colorConverter.to_rgba(self.edgecolors, alpha=self.alpha) else: # use colormap if not explicitly overridden by edgecolors if self.cmap is not None: color_bounds = [0, max_level] edgecolors = ( apply_colormap( levels[visible] * 1.0, color_bounds=color_bounds, cmap_name=self.cmap, )[0, :, :] * 1.0 / 255.0 ) edgecolors[:, 3] = self.alpha else: edgecolors = (0.0, 0.0, 0.0, self.alpha) if visible.nonzero()[0].size == 0: continue edge_x = (left_edge_x, left_edge_x, right_edge_x, right_edge_x) edge_y = (left_edge_y, right_edge_y, right_edge_y, left_edge_y) edge_x, edge_y = self._sanitize_xy_order(plot, edge_x, edge_y) verts = np.array([edge_x, edge_y]) verts = verts.transpose()[visible, :, :] grid_collection = matplotlib.collections.PolyCollection( verts, facecolors="none", edgecolors=edgecolors, linewidth=self.linewidth, ) plot._axes.add_collection(grid_collection) visible_ids = np.logical_and( np.logical_and(xwidth > self.min_pix_ids, ywidth > self.min_pix_ids), np.logical_and(levels >= min_level, levels <= max_level), ) if self.draw_ids: plot_ids = np.where(visible_ids)[0] x = np.empty(plot_ids.size) y = np.empty(plot_ids.size) for i, n in enumerate(plot_ids): if self.id_loc == "lower left": x[i] = left_edge_x[n] + (2 * (xx1 - xx0) / xpix) y[i] = left_edge_y[n] + (2 * (yy1 - yy0) / ypix) elif self.id_loc == "lower right": x[i] = right_edge_x[n] - ( (10 * len(str(block_ids[i])) - 2) * (xx1 - xx0) / xpix ) y[i] = left_edge_y[n] + (2 * (yy1 - yy0) / ypix) elif self.id_loc == "upper left": x[i] = left_edge_x[n] + (2 * (xx1 - xx0) / xpix) y[i] = right_edge_y[n] - (12 * (yy1 - yy0) / ypix) elif self.id_loc == "upper right": x[i] = right_edge_x[n] - ( (10 * len(str(block_ids[i])) - 2) * (xx1 - xx0) / xpix ) y[i] = right_edge_y[n] - (12 * (yy1 - yy0) / ypix) else: raise RuntimeError( f"Unrecognized id_loc value ({self.id_loc!r}). " "Allowed values are 'lower left', lower right', " "'upper left', and 'upper right'." ) xi, yi = self._sanitize_xy_order(plot, x[i], y[i]) plot._axes.text(xi, yi, "%d" % block_ids[n], clip_on=True) # when type-checking with MPL >= 3.8, use # from matplotlib.typing import ColorType _ColorType = Any class StreamlineCallback(PlotCallback): """ Plot streamlines using matplotlib.axes.Axes.streamplot Arguments --------- field_x: field key The "velocity" analoguous field along the horizontal direction. field_y: field key The "velocity" analoguous field along the vertical direction. linewidth: float, or field key (default: 1.0) A constant scalar will be passed directly to matplotlib.axes.Axes.streamplot A field key will be first interpreted by yt and produce the adequate 2D array. Data fields are normalized by their maximum value, so the maximal linewidth is 1 by default. See `linewidth_upscaling` for fine tuning. Note that the absolute value is taken in all cases. linewidth_upscaling: float (default: 1.0) A constant multiplicative factor applied to linewidth. Final linewidth is obtained as: linewidth_upscaling * abs(linewidth) / max(abs(linewidth)) color: a color identifier, or a field key (default: matplotlib.rcParams['line.color']) A constant color identifier will be passed directly to matplotlib.axes.Axes.streamplot A field key will be first interpreted by yt and produce the adequate 2D array. See https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.streamplot.html for how to customize color mapping using `cmap` and `norm` arguments. color_threshold: float or unyt_quantity (default: -inf) Regions where the field used for color is lower than this threshold will be masked. Only used if color is a field key. factor: int, or tuple[int, int] (default: 16) Fields are downed-sampled by this factor with respect to the background image buffer size. A single integer factor will be used for both direction, but a tuple of 2 integers can be passed to set x and y downsampling independently. **kwargs: any additional keyword arguments will be passed directly to matplotlib.axes.Axes.streamplot """ _type_name = "streamlines" _supported_geometries = ( "cartesian", "spectral_cube", "polar", "cylindrical", "spherical", ) _incompatible_plot_types = ("OffAxisProjection", "Particle") def __init__( self, field_x: AnyFieldKey, field_y: AnyFieldKey, *, linewidth: float | AnyFieldKey = 1.0, linewidth_upscaling: float = 1.0, color: _ColorType | FieldKey | None = None, color_threshold: float | unyt_quantity = float("-inf"), factor: tuple[int, int] | int = 16, field_color=None, # deprecated display_threshold=None, # deprecated plot_args=None, # deprecated **kwargs, ): self.field_x = field_x self.field_y = field_y if color is not None and field_color is not None: raise TypeError( "`color` and `field_color` keyword arguments " "cannot be set at the same time." ) elif field_color is not None: issue_deprecation_warning( "The `field_color` keyword argument is deprecated. " "Use `color` instead.", since="4.3", stacklevel=5, ) self._color = field_color else: self._color = color if color_threshold is not None and display_threshold is not None: raise TypeError( "`color_threshold` and `display_threshold` keyword arguments " "cannot be set at the same time." ) elif display_threshold is not None: issue_deprecation_warning( "The `display_threshold` keyword argument is deprecated. " "Use `color_threshold` instead.", since="4.3", stacklevel=5, ) self._color_threshold = display_threshold else: self._color_threshold = color_threshold self._linewidth = linewidth self._linewidth_upscaling = linewidth_upscaling self.factor = _validate_factor_tuple(factor) if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) else: plot_args = kwargs self.plot_args = plot_args def __call__(self, plot) -> None: xx0, xx1, yy0, yy1 = self._plot_bounds(plot) # We are feeding this size into the pixelizer, where it will properly # set it in reverse order nx = plot.raw_image_shape[1] // self.factor[0] ny = plot.raw_image_shape[0] // self.factor[1] def pixelize(field): retv = plot.data.ds.coordinates.pixelize( plot.data.axis, plot.data, field=field, bounds=self._physical_bounds(plot), size=(nx, ny), ) if plot._swap_axes: return retv.transpose() else: return retv def is_field_key(val) -> TypeGuard[AnyFieldKey]: if not is_sequence(val): return False try: plot.data._determine_fields(val) except (YTFieldNotFound, YTFieldTypeNotFound): return False else: return True pixX = pixelize(self.field_x) pixY = pixelize(self.field_y) if isinstance(self._linewidth, (int, float)): linewidth = self._linewidth_upscaling * self._linewidth elif is_field_key(self._linewidth): linewidth = pixelize(self._linewidth) linewidth *= self._linewidth_upscaling / np.abs(linewidth).max() else: raise TypeError( f"annotate_streamlines received linewidth={self._linewidth!r}, " f"with type {type(self._linewidth)}. Expected a float or a field key." ) if is_field_key(self._color): color = pixelize(self._color) linewidth *= color > self._color_threshold else: if (_cmap := self.plot_args.get("cmap")) is not None: warnings.warn( f"annotate_streamlines received color={self._color!r}, " "which wasn't recognized as as field key. " "It is assumed to be a fixed color identifier. " f"Also received cmap={_cmap!r}, which will be ignored.", stacklevel=5, ) color = self._color X, Y = ( np.linspace(xx0, xx1, nx, endpoint=True), np.linspace(yy0, yy1, ny, endpoint=True), ) X, Y, pixX, pixY = self._sanitize_xy_order(plot, X, Y, pixX, pixY) if plot._swap_axes: # need an additional transpose here for streamline tracing X = X.transpose() Y = Y.transpose() streamplot_args = { "x": X, "y": Y, "u": pixX, "v": pixY, "color": color, "linewidth": linewidth, **self.plot_args, } plot._axes.streamplot(**streamplot_args) self._set_plot_limits(plot, (xx0, xx1, yy0, yy1)) class LinePlotCallback(PlotCallback): """ Overplot a line with endpoints at p1 and p2. p1 and p2 should be 2D or 3D coordinates consistent with the coordinate system denoted in the "coord_system" keyword. Parameters ---------- p1, p2 : 2- or 3-element tuples, lists, or arrays These are the coordinates of the endpoints of the line. coord_system : string, optional This string defines the coordinate system of the coordinates p1 and p2. Valid coordinates are: "data" -- the 3D dataset coordinates "plot" -- the 2D coordinates defined by the actual plot limits "axis" -- the MPL axis coordinates: (0,0) is lower left; (1,1) is upper right "figure" -- the MPL figure coordinates: (0,0) is lower left, (1,1) is upper right plot_args : dictionary, optional This dictionary is passed to the MPL plot function for generating the line. By default, it is: {'color':'white', 'linewidth':2} Examples -------- >>> # Overplot a diagonal white line from the lower left corner to upper >>> # right corner >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_line([0, 0], [1, 1], coord_system="axis") >>> s.save() >>> # Overplot a red dashed line from data coordinate (0.1, 0.2, 0.3) to >>> # (0.5, 0.6, 0.7) >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_line( ... [0.1, 0.2, 0.3], ... [0.5, 0.6, 0.7], ... coord_system="data", ... color="red", ... linestyles="--", ... ) >>> s.save() """ _type_name = "line" _supported_geometries: tuple[str, ...] = ( "cartesian", "spectral_cube", "polar", "cylindrical", ) def __init__( self, p1, p2, *, coord_system="data", plot_args: dict[str, Any] | None = None, **kwargs, ): self.p1 = p1 self.p2 = p2 if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) self.plot_args = { "color": "white", "linewidth": 2, **(plot_args or {}), **kwargs, } self.coord_system = coord_system self.transform = None def __call__(self, plot): p1 = self._sanitize_coord_system(plot, self.p1, coord_system=self.coord_system) p2 = self._sanitize_coord_system(plot, self.p2, coord_system=self.coord_system) start_pt, end_pt = [p1[0], p2[0]], [p1[1], p2[1]] if plot._swap_axes: start_pt, end_pt = [p2[0], p1[0]], [p2[1], p1[1]] plot._axes.plot(start_pt, end_pt, transform=self.transform, **self.plot_args) self._set_plot_limits(plot) class CuttingQuiverCallback(BaseQuiverCallback): """ Get a quiver plot on top of a cutting plane, using *field_x* and *field_y*, skipping every *factor* datapoint in the discretization. *scale* is the data units per arrow length unit using *scale_units* and *plot_args* allows you to pass in matplotlib arguments (see matplotlib.axes.Axes.quiver for more info). if *normalize* is True, the fields will be scaled by their local (in-plane) length, allowing morphological features to be more clearly seen for fields with substantial variation in field strength. """ _type_name = "cquiver" _supported_geometries = ("cartesian", "spectral_cube") def _get_quiver_data(self, plot, bounds: tuple, nx: int, ny: int): # calls the pixelizer, returns pixX, pixY, pixC arrays indices = np.argsort(plot.data["index", "dx"])[::-1].astype(np.int_) pixX = np.zeros((ny, nx), dtype="f8") pixY = np.zeros((ny, nx), dtype="f8") pixelize_off_axis_cartesian( pixX, plot.data["index", "x"].to("code_length"), plot.data["index", "y"].to("code_length"), plot.data["index", "z"].to("code_length"), plot.data["px"], plot.data["py"], plot.data["pdx"], plot.data["pdy"], plot.data["pdz"], plot.data.center, plot.data._inv_mat, indices, plot.data[self.field_x], bounds, ) pixelize_off_axis_cartesian( pixY, plot.data["index", "x"].to("code_length"), plot.data["index", "y"].to("code_length"), plot.data["index", "z"].to("code_length"), plot.data["px"], plot.data["py"], plot.data["pdx"], plot.data["pdy"], plot.data["pdz"], plot.data.center, plot.data._inv_mat, indices, plot.data[self.field_y], bounds, ) if self.field_c is None: pixC = None else: pixC = np.zeros((ny, nx), dtype="f8") pixelize_off_axis_cartesian( pixC, plot.data["index", "x"].to("code_length"), plot.data["index", "y"].to("code_length"), plot.data["index", "z"].to("code_length"), plot.data["px"], plot.data["py"], plot.data["pdx"], plot.data["pdy"], plot.data["pdz"], plot.data.center, plot.data._inv_mat, indices, plot.data[self.field_c], bounds, ) return pixX, pixY, pixC class ClumpContourCallback(PlotCallback): """ Take a list of *clumps* and plot them as a set of contours. """ _type_name = "clumps" _supported_geometries = ("cartesian", "spectral_cube", "cylindrical") _incompatible_plot_types = ("OffAxisSlice", "OffAxisProjection", "Particle") def __init__(self, clumps, *, plot_args=None, **kwargs): self.clumps = clumps if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) else: plot_args = kwargs if "color" in plot_args: plot_args["colors"] = plot_args.pop("color") self.plot_args = plot_args def __call__(self, plot): bounds = self._physical_bounds(plot) extent = self._plot_bounds(plot) ax = plot.data.axis px_index = plot.data.ds.coordinates.x_axis[ax] py_index = plot.data.ds.coordinates.y_axis[ax] xf = plot.data.ds.coordinates.axis_name[px_index] yf = plot.data.ds.coordinates.axis_name[py_index] dxf = f"d{xf}" dyf = f"d{yf}" ny, nx = plot.raw_image_shape buff = np.zeros((nx, ny), dtype="float64") for i, clump in enumerate(reversed(self.clumps)): mylog.info("Pixelizing contour %s", i) if isinstance(clump, Clump): ftype = "index" elif isinstance(clump, YTClumpContainer): ftype = "grid" else: raise RuntimeError( f"Unknown field type for object of type {type(clump)}." ) xf_copy = clump[ftype, xf].copy().in_units("code_length") yf_copy = clump[ftype, yf].copy().in_units("code_length") temp = np.zeros((ny, nx), dtype="f8") pixelize_cartesian( temp, xf_copy, yf_copy, clump[ftype, dxf].in_units("code_length") / 2.0, clump[ftype, dyf].in_units("code_length") / 2.0, clump[ftype, dxf].d * 0.0 + i + 1, # inits inside Pixelize bounds, 0, ) buff = np.maximum(temp, buff) if plot._swap_axes: buff = buff.transpose() extent = (extent[2], extent[3], extent[0], extent[1]) self.rv = plot._axes.contour( buff, np.unique(buff), extent=extent, **self.plot_args ) class ArrowCallback(PlotCallback): """ Overplot arrow(s) pointing at position(s) for highlighting specific features. By default, arrow points from lower left to the designated position "pos" with arrow length "length". Alternatively, if "starting_pos" is set, arrow will stretch from "starting_pos" to "pos" and "length" will be disregarded. "coord_system" keyword refers to positions set in "pos" arg and "starting_pos" keyword, which by default are in data coordinates. "length", "width", "head_length", and "head_width" keywords for the arrow are all in axis units, ie relative to the size of the plot axes as 1, even if the position of the arrow is set relative to another coordinate system. Parameters ---------- pos : array-like These are the coordinates where the marker(s) will be overplotted Either as [x,y,z] or as [[x1,x2,...],[y1,y2,...],[z1,z2,...]] length : float, optional The length, in axis units, of the arrow. Default: 0.03 width : float, optional The width, in axis units, of the tail line of the arrow. Default: 0.003 head_length : float, optional The length, in axis units, of the head of the arrow. If set to None, use 1.5*head_width Default: None head_width : float, optional The width, in axis units, of the head of the arrow. Default: 0.02 starting_pos : 2- or 3-element tuple, list, or array, optional These are the coordinates from which the arrow starts towards its point. Not compatible with 'length' kwarg. coord_system : string, optional This string defines the coordinate system of the coordinates of pos Valid coordinates are: "data" -- the 3D dataset coordinates "plot" -- the 2D coordinates defined by the actual plot limits "axis" -- the MPL axis coordinates: (0,0) is lower left; (1,1) is upper right "figure" -- the MPL figure coordinates: (0,0) is lower left, (1,1) is upper right plot_args : dictionary, optional This dictionary is passed to the MPL arrow function for generating the arrow. By default, it is: {'color':'white'} Examples -------- >>> # Overplot an arrow pointing to feature at data coord: (0.2, 0.3, 0.4) >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_arrow([0.2, 0.3, 0.4]) >>> s.save() >>> # Overplot a red arrow with longer length pointing to plot coordinate >>> # (0.1, -0.1) >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_arrow( ... [0.1, -0.1], length=0.06, coord_system="plot", color="red" ... ) >>> s.save() """ _type_name = "arrow" _supported_geometries = ("cartesian", "spectral_cube", "cylindrical") def __init__( self, pos, *, length=0.03, width=0.0001, head_width=0.01, head_length=0.01, starting_pos=None, coord_system="data", plot_args: dict[str, Any] | None = None, # deprecated **kwargs, ): self.pos = pos self.length = length self.width = width self.head_width = head_width self.head_length = head_length self.starting_pos = starting_pos self.coord_system = coord_system self.transform = None if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) self.plot_args = { "color": "white", **(plot_args or {}), **kwargs, } def __call__(self, plot): x, y = self._sanitize_coord_system( plot, self.pos, coord_system=self.coord_system ) xx0, xx1, yy0, yy1 = self._plot_bounds(plot) # normalize all of the kwarg lengths to the plot size plot_diag = ((yy1 - yy0) ** 2 + (xx1 - xx0) ** 2) ** (0.5) length = self.length * plot_diag width = self.width * plot_diag head_width = self.head_width * plot_diag head_length = None if self.head_length is not None: head_length = self.head_length * plot_diag if self.starting_pos is not None: start_x, start_y = self._sanitize_coord_system( plot, self.starting_pos, coord_system=self.coord_system ) dx = x - start_x dy = y - start_y else: dx = (xx1 - xx0) * 2 ** (0.5) * length dy = (yy1 - yy0) * 2 ** (0.5) * length # If the arrow is 0 length if dx == dy == 0: warnings.warn("The arrow has zero length. Not annotating.", stacklevel=2) return x, y, dx, dy = self._sanitize_xy_order(plot, x, y, dx, dy) try: plot._axes.arrow( x - dx, y - dy, dx, dy, width=width, head_width=head_width, head_length=head_length, transform=self.transform, length_includes_head=True, **self.plot_args, ) except ValueError: for i in range(len(x)): plot._axes.arrow( x[i] - dx, y[i] - dy, dx, dy, width=width, head_width=head_width, head_length=head_length, transform=self.transform, length_includes_head=True, **self.plot_args, ) self._set_plot_limits(plot, (xx0, xx1, yy0, yy1)) class MarkerAnnotateCallback(PlotCallback): """ Overplot marker(s) at a position(s) for highlighting specific features. Parameters ---------- pos : array-like These are the coordinates where the marker(s) will be overplotted Either as [x,y,z] or as [[x1,x2,...],[y1,y2,...],[z1,z2,...]] marker : string, optional The shape of the marker to be passed to the MPL scatter function. By default, it is 'x', but other acceptable values are: '.', 'o', 'v', '^', 's', 'p' '*', etc. See matplotlib.markers for more information. coord_system : string, optional This string defines the coordinate system of the coordinates of pos Valid coordinates are: "data" -- the 3D dataset coordinates "plot" -- the 2D coordinates defined by the actual plot limits "axis" -- the MPL axis coordinates: (0,0) is lower left; (1,1) is upper right "figure" -- the MPL figure coordinates: (0,0) is lower left, (1,1) is upper right plot_args : dictionary, optional This dictionary is passed to the MPL scatter function for generating the marker. By default, it is: {'color':'white', 's':50} Examples -------- >>> # Overplot a white X on a feature at data location (0.5, 0.5, 0.5) >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_marker([0.4, 0.5, 0.6]) >>> s.save() >>> # Overplot a big yellow circle at axis location (0.1, 0.2) >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_marker( ... [0.1, 0.2], ... marker="o", ... coord_system="axis", ... color="yellow", ... s=200, ... ) >>> s.save() """ _type_name = "marker" _supported_geometries = ("cartesian", "spectral_cube", "polar", "cylindrical") def __init__( self, pos, marker="x", *, coord_system="data", plot_args=None, **kwargs ): self.pos = pos self.marker = marker if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) self.plot_args = { "color": "white", "s": 50, **(plot_args or {}), **kwargs, } self.coord_system = coord_system self.transform = None def __call__(self, plot): x, y = self._sanitize_coord_system( plot, self.pos, coord_system=self.coord_system ) x, y = self._sanitize_xy_order(plot, x, y) plot._axes.scatter( x, y, marker=self.marker, transform=self.transform, **self.plot_args ) self._set_plot_limits(plot) class SphereCallback(PlotCallback): """ Overplot a circle with designated center and radius with optional text. Parameters ---------- center : 2- or 3-element tuple, list, or array These are the coordinates where the circle will be overplotted radius : YTArray, float, or (1, ('kpc')) style tuple The radius of the circle in code coordinates circle_args : dict, optional This dictionary is passed to the MPL circle object. By default, {'color':'white'} coord_system : string, optional This string defines the coordinate system of the coordinates of pos Valid coordinates are: "data" -- the 3D dataset coordinates "plot" -- the 2D coordinates defined by the actual plot limits "axis" -- the MPL axis coordinates: (0,0) is lower left; (1,1) is upper right "figure" -- the MPL figure coordinates: (0,0) is lower left, (1,1) is upper right text : string, optional Optional text to include next to the circle. text_args : dictionary, optional This dictionary is passed to the MPL text function. By default, it is: {'color':'white'} Examples -------- >>> # Overplot a white circle of radius 100 kpc over the central galaxy >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_sphere([0.5, 0.5, 0.5], radius=(100, "kpc")) >>> s.save() """ _type_name = "sphere" _supported_geometries = ("cartesian", "spectral_cube", "polar", "cylindrical") def __init__( self, center, radius, *, coord_system="data", text=None, circle_args=None, text_args=None, ): self.center = center self.radius = radius self.circle_args = { "color": "white", "fill": False, **(circle_args or {}), } self.text = text self.text_args = { "color": "white", **(text_args or {}), } self.coord_system = coord_system self.transform = None def __call__(self, plot): from matplotlib.patches import Circle if is_sequence(self.radius): self.radius = plot.data.ds.quan(self.radius[0], self.radius[1]) self.radius = self.radius.in_units(plot.xlim[0].units) if isinstance(self.radius, YTQuantity): if isinstance(self.center, YTArray): units = self.center.units else: units = "code_length" self.radius = self.radius.to(units) if not hasattr(self.radius, "units"): self.radius = plot.data.ds.quan(self.radius, "code_length") if not hasattr(self.center, "units"): self.center = plot.data.ds.arr(self.center, "code_length") # This assures the radius has the appropriate size in # the different coordinate systems, since one cannot simply # apply a different transform for a length in the same way # you can for a coordinate. if self.coord_system == "data" or self.coord_system == "plot": # Note: we force conversion into "1" to prevent issues in case # one of the length has some dimensionless factor (Mpc/h) scaled_radius = (self.radius * self._pixel_scale(plot)[0]).to("1") else: scaled_radius = self.radius / (plot.xlim[1] - plot.xlim[0]) x, y = self._sanitize_coord_system( plot, self.center, coord_system=self.coord_system ) x, y = self._sanitize_xy_order(plot, x, y) cir = Circle( (x, y), scaled_radius.v, transform=self.transform, **self.circle_args ) plot._axes.add_patch(cir) if self.text is not None: label = plot._axes.text( x, y, self.text, transform=self.transform, **self.text_args ) self._set_font_properties(plot, [label], **self.text_args) self._set_plot_limits(plot) class TextLabelCallback(PlotCallback): """ Overplot text on the plot at a specified position. If you desire an inset box around your text, set one with the inset_box_args dictionary keyword. Parameters ---------- pos : 2- or 3-element tuple, list, or array These are the coordinates where the text will be overplotted text : string The text you wish to include coord_system : string, optional This string defines the coordinate system of the coordinates of pos Valid coordinates are: "data" -- the 3D dataset coordinates "plot" -- the 2D coordinates defined by the actual plot limits "axis" -- the MPL axis coordinates: (0,0) is lower left; (1,1) is upper right "figure" -- the MPL figure coordinates: (0,0) is lower left, (1,1) is upper right text_args : dictionary, optional This dictionary is passed to the MPL text function for generating the text. By default, it is: {'color':'white'} and uses the defaults for the other fonts in the image. inset_box_args : dictionary, optional A dictionary of any arbitrary parameters to be passed to the Matplotlib FancyBboxPatch object as the inset box around the text. Default: {} Examples -------- >>> # Overplot white text at data location [0.55, 0.7, 0.4] >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_text([0.55, 0.7, 0.4], "Here is a galaxy") >>> s.save() >>> # Overplot yellow text at axis location [0.2, 0.8] with >>> # a shaded inset box >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_text( ... [0.2, 0.8], ... "Here is a galaxy", ... coord_system="axis", ... text_args={"color": "yellow"}, ... inset_box_args={ ... "boxstyle": "square,pad=0.3", ... "facecolor": "black", ... "linewidth": 3, ... "edgecolor": "white", ... "alpha": 0.5, ... }, ... ) >>> s.save() """ _type_name = "text" _supported_geometries: tuple[str, ...] = ( "cartesian", "spectral_cube", "polar", "cylindrical", ) def __init__( self, pos, text, *, coord_system="data", text_args=None, inset_box_args=None, ): self.pos = pos self.text = text self.text_args = { "color": "white", **(text_args or {}), } self.inset_box_args = inset_box_args self.coord_system = coord_system self.transform = None def __call__(self, plot): kwargs = self.text_args.copy() x, y = self._sanitize_coord_system( plot, self.pos, coord_system=self.coord_system ) # Set the font properties of text from this callback to be # consistent with other text labels in this figure if self.inset_box_args is not None: kwargs["bbox"] = self.inset_box_args x, y = self._sanitize_xy_order(plot, x, y) label = plot._axes.text(x, y, self.text, transform=self.transform, **kwargs) self._set_font_properties(plot, [label], **kwargs) self._set_plot_limits(plot) class ParticleCallback(PlotCallback): """ Adds particle positions, based on a thick slab along *axis* with a *width* along the line of sight. *p_size* controls the number of pixels per particle, and *col* governs the color. *ptype* will restrict plotted particles to only those that are of a given type. *alpha* determines the opacity of the marker symbol used in the scatter. An alternate data source can be specified with *data_source*, but by default the plot's data source will be queried. """ _type_name = "particles" region = None _descriptor = None _supported_geometries = ("cartesian", "spectral_cube", "cylindrical") _incompatible_plot_types = ("OffAxisSlice", "OffAxisProjection") def __init__( self, width, p_size=1.0, col="k", marker="o", stride=1, ptype="all", alpha=1.0, data_source=None, ): self.width = width self.p_size = p_size self.color = col self.marker = marker self.stride = stride self.ptype = ptype self.alpha = alpha self.data_source = data_source def __call__(self, plot): data = plot.data if is_sequence(self.width): validate_width_tuple(self.width) self.width = plot.data.ds.quan(self.width[0], self.width[1]) elif isinstance(self.width, YTQuantity): self.width = plot.data.ds.quan(self.width.value, self.width.units) else: self.width = plot.data.ds.quan(self.width, "code_length") # we construct a rectangular prism x0, x1, y0, y1 = self._physical_bounds(plot) if isinstance(self.data_source, YTCutRegion): mylog.warning( "Parameter 'width' is ignored in annotate_particles if the " "data_source is a cut_region. " "See https://github.com/yt-project/yt/issues/1933 for further details." ) self.region = self.data_source else: self.region = self._get_region((x0, x1), (y0, y1), plot.data.axis, data) ax = data.axis xax = plot.data.ds.coordinates.x_axis[ax] yax = plot.data.ds.coordinates.y_axis[ax] axis_names = plot.data.ds.coordinates.axis_name field_x = f"particle_position_{axis_names[xax]}" field_y = f"particle_position_{axis_names[yax]}" pt = self.ptype self.periodic_x = plot.data.ds.periodicity[xax] self.periodic_y = plot.data.ds.periodicity[yax] self.LE = plot.data.ds.domain_left_edge[xax], plot.data.ds.domain_left_edge[yax] self.RE = ( plot.data.ds.domain_right_edge[xax], plot.data.ds.domain_right_edge[yax], ) period_x = plot.data.ds.domain_width[xax] period_y = plot.data.ds.domain_width[yax] particle_x, particle_y = self._enforce_periodic( self.region[pt, field_x], self.region[pt, field_y], x0, x1, period_x, y0, y1, period_y, ) gg = ( (particle_x >= x0) & (particle_x <= x1) & (particle_y >= y0) & (particle_y <= y1) ) px, py = [particle_x[gg][:: self.stride], particle_y[gg][:: self.stride]] px, py = self._convert_to_plot(plot, [px, py]) px, py = self._sanitize_xy_order(plot, px, py) plot._axes.scatter( px, py, edgecolors="None", marker=self.marker, s=self.p_size, c=self.color, alpha=self.alpha, ) self._set_plot_limits(plot) def _enforce_periodic( self, particle_x, particle_y, x0, x1, period_x, y0, y1, period_y ): # duplicate particles if periodic in that direction AND if the plot # extends outside the domain boundaries. if self.periodic_x and x0 > self.RE[0]: particle_x = uhstack((particle_x, particle_x + period_x)) particle_y = uhstack((particle_y, particle_y)) if self.periodic_x and x1 < self.LE[0]: particle_x = uhstack((particle_x, particle_x - period_x)) particle_y = uhstack((particle_y, particle_y)) if self.periodic_y and y0 > self.RE[1]: particle_y = uhstack((particle_y, particle_y + period_y)) particle_x = uhstack((particle_x, particle_x)) if self.periodic_y and y1 < self.LE[1]: particle_y = uhstack((particle_y, particle_y - period_y)) particle_x = uhstack((particle_x, particle_x)) return particle_x, particle_y def _get_region(self, xlim, ylim, axis, data): LE, RE = [None] * 3, [None] * 3 ds = data.ds xax = ds.coordinates.x_axis[axis] yax = ds.coordinates.y_axis[axis] zax = axis LE[xax], RE[xax] = xlim LE[yax], RE[yax] = ylim LE[zax] = data.center[zax] - self.width * 0.5 LE[zax].convert_to_units("code_length") RE[zax] = LE[zax] + self.width if ( self.region is not None and np.all(self.region.left_edge <= LE) and np.all(self.region.right_edge >= RE) ): return self.region self.region = data.ds.region(data.center, LE, RE, data_source=self.data_source) return self.region class TitleCallback(PlotCallback): """ Accepts a *title* and adds it to the plot """ _type_name = "title" def __init__(self, title): self.title = title def __call__(self, plot): plot._axes.set_title(self.title) # Set the font properties of text from this callback to be # consistent with other text labels in this figure label = plot._axes.title self._set_font_properties(plot, [label]) class MeshLinesCallback(PlotCallback): """ Adds mesh lines to the plot. Only works for unstructured or semi-structured mesh data. For structured grid data, see GridBoundaryCallback or CellEdgesCallback. Parameters ---------- plot_args: dict, optional A dictionary of arguments that will be passed to matplotlib. Example ------- >>> import yt >>> ds = yt.load("MOOSE_sample_data/out.e-s010") >>> sl = yt.SlicePlot(ds, "z", ("connect2", "convected")) >>> sl.annotate_mesh_lines(color="black") """ _type_name = "mesh_lines" _supported_geometries = ("cartesian", "spectral_cube") _incompatible_plot_types = ("OffAxisSlice", "OffAxisProjection") def __init__(self, *, plot_args=None, **kwargs): if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) else: plot_args = kwargs self.plot_args = plot_args def promote_2d_to_3d(self, coords, indices, plot): new_coords = np.zeros((2 * coords.shape[0], 3)) new_connects = np.zeros( (indices.shape[0], 2 * indices.shape[1]), dtype=np.int64 ) new_coords[0 : coords.shape[0], 0:2] = coords new_coords[0 : coords.shape[0], 2] = plot.ds.domain_left_edge[2] new_coords[coords.shape[0] :, 0:2] = coords new_coords[coords.shape[0] :, 2] = plot.ds.domain_right_edge[2] new_connects[:, 0 : indices.shape[1]] = indices new_connects[:, indices.shape[1] :] = indices + coords.shape[0] return new_coords, new_connects def __call__(self, plot): index = plot.ds.index if not issubclass(type(index), UnstructuredIndex): raise RuntimeError( "Mesh line annotations only work for " "unstructured or semi-structured mesh data." ) for i, m in enumerate(index.meshes): try: ftype, fname = plot.field if ftype.startswith("connect") and int(ftype[-1]) - 1 != i: continue except ValueError: pass coords = m.connectivity_coords indices = m.connectivity_indices - m._index_offset num_verts = indices.shape[1] num_dims = coords.shape[1] if num_dims == 2 and num_verts == 3: coords, indices = self.promote_2d_to_3d(coords, indices, plot) elif num_dims == 2 and num_verts == 4: coords, indices = self.promote_2d_to_3d(coords, indices, plot) tri_indices = triangulate_indices(indices.astype(np.int_)) points = coords[tri_indices] tfc = TriangleFacetsCallback(points, **self.plot_args) tfc(plot) class TriangleFacetsCallback(PlotCallback): """ Intended for representing a slice of a triangular faceted geometry in a slice plot. Uses a set of *triangle_vertices* to find all triangles the plane of a SlicePlot intersects with. The lines between the intersection points of the triangles are then added to the plot to create an outline of the geometry represented by the triangles. """ _type_name = "triangle_facets" _supported_geometries = ("cartesian", "spectral_cube") def __init__(self, triangle_vertices, *, plot_args=None, **kwargs): if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) plot_args.update(kwargs) else: plot_args = kwargs self.plot_args = kwargs self.vertices = triangle_vertices def __call__(self, plot): ax = plot.data.axis xax = plot.data.ds.coordinates.x_axis[ax] yax = plot.data.ds.coordinates.y_axis[ax] if not hasattr(self.vertices, "in_units"): vertices = plot.data.pf.arr(self.vertices, "code_length") else: vertices = self.vertices l_cy = triangle_plane_intersect(plot.data.axis, plot.data.coord, vertices)[ :, :, (xax, yax) ] # l_cy is shape (nlines, 2, 2) # reformat for conversion to plot coordinates l_cy = np.rollaxis(l_cy, 0, 3) # shape is now (2, 2, nlines) # convert all line starting points l_cy[0] = self._convert_to_plot(plot, l_cy[0]) # convert all line ending points l_cy[1] = self._convert_to_plot(plot, l_cy[1]) if plot._swap_axes: # more convenient to swap the x, y values here before final roll x0, y0 = l_cy[0] # x, y values of start points x1, y1 = l_cy[1] # x, y values of end points l_cy[0] = np.vstack([y0, x0]) # swap x, y for start points l_cy[1] = np.vstack([y1, x1]) # swap x, y for end points # convert back to shape (nlines, 2, 2) l_cy = np.rollaxis(l_cy, 2, 0) # create line collection and add it to the plot lc = matplotlib.collections.LineCollection(l_cy, **self.plot_args) plot._axes.add_collection(lc) class TimestampCallback(PlotCallback): r""" Annotates the timestamp and/or redshift of the data output at a specified location in the image (either in a present corner, or by specifying (x,y) image coordinates with the x_pos, y_pos arguments. If no time_units are specified, it will automatically choose appropriate units. It allows for custom formatting of the time and redshift information, as well as the specification of an inset box around the text. Parameters ---------- x_pos, y_pos : floats, optional The image location of the timestamp in the coord system defined by the coord_system kwarg. Setting x_pos and y_pos overrides the corner parameter. corner : string, optional Corner sets up one of 4 predeterimined locations for the timestamp to be displayed in the image: 'upper_left', 'upper_right', 'lower_left', 'lower_right' (also allows None). This value will be overridden by the optional x_pos and y_pos keywords. time : boolean, optional Whether or not to show the ds.current_time of the data output. Can be used solo or in conjunction with redshift parameter. redshift : boolean, optional Whether or not to show the ds.current_time of the data output. Can be used solo or in conjunction with the time parameter. time_format : string, optional This specifies the format of the time output assuming "time" is the number of time and "unit" is units of the time (e.g. 's', 'Myr', etc.) The time can be specified to arbitrary precision according to printf formatting codes (defaults to .1f -- a float with 1 digits after decimal). Example: "Age = {time:.2f} {units}". time_unit : string, optional time_unit must be a valid yt time unit (e.g. 's', 'min', 'hr', 'yr', 'Myr', etc.) redshift_format : string, optional This specifies the format of the redshift output. The redshift can be specified to arbitrary precision according to printf formatting codes (defaults to 0.2f -- a float with 2 digits after decimal). Example: "REDSHIFT = {redshift:03.3g}", draw_inset_box : boolean, optional Whether or not an inset box should be included around the text If so, it uses the inset_box_args to set the matplotlib FancyBboxPatch object. coord_system : string, optional This string defines the coordinate system of the coordinates of pos Valid coordinates are: - "data": 3D dataset coordinates - "plot": 2D coordinates defined by the actual plot limits - "axis": MPL axis coordinates: (0,0) is lower left; (1,1) is upper right - "figure": MPL figure coordinates: (0,0) is lower left, (1,1) is upper right time_offset : float, (value, unit) tuple, or YTQuantity, optional Apply an offset to the time shown in the annotation from the value of the current time. If a scalar value with no units is passed in, the value of the *time_unit* kwarg is used for the units. Default: None, meaning no offset. text_args : dictionary, optional A dictionary of any arbitrary parameters to be passed to the Matplotlib text object. Defaults: ``{'color':'white', 'horizontalalignment':'center', 'verticalalignment':'top'}``. inset_box_args : dictionary, optional A dictionary of any arbitrary parameters to be passed to the Matplotlib FancyBboxPatch object as the inset box around the text. Defaults: ``{'boxstyle':'square', 'pad':0.3, 'facecolor':'black', 'linewidth':3, 'edgecolor':'white', 'alpha':0.5}`` Example ------- >>> import yt >>> ds = yt.load("Enzo_64/DD0020/data0020") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_timestamp() """ _type_name = "timestamp" _supported_geometries = ( "cartesian", "spectral_cube", "cylindrical", "polar", "spherical", ) def __init__( self, x_pos=None, y_pos=None, corner="lower_left", *, time=True, redshift=False, time_format="t = {time:.1f} {units}", time_unit=None, redshift_format="z = {redshift:.2f}", draw_inset_box=False, coord_system="axis", time_offset=None, text_args=None, inset_box_args=None, ): # Set position based on corner argument. self.pos = (x_pos, y_pos) self.corner = corner self.time = time self.redshift = redshift self.time_format = time_format self.redshift_format = redshift_format self.time_unit = time_unit self.coord_system = coord_system self.time_offset = time_offset self.text_args = { "color": "white", "horizontalalignment": "center", "verticalalignment": "top", **(text_args or {}), } if draw_inset_box: self.inset_box_args = { "boxstyle": "square,pad=0.3", "facecolor": "black", "linewidth": 3, "edgecolor": "white", "alpha": 0.5, **(inset_box_args or {}), } else: self.inset_box_args = None def __call__(self, plot): # Setting pos overrides corner argument if self.pos[0] is None or self.pos[1] is None: if self.corner == "upper_left": self.pos = (0.03, 0.96) self.text_args["horizontalalignment"] = "left" self.text_args["verticalalignment"] = "top" elif self.corner == "upper_right": self.pos = (0.97, 0.96) self.text_args["horizontalalignment"] = "right" self.text_args["verticalalignment"] = "top" elif self.corner == "lower_left": self.pos = (0.03, 0.03) self.text_args["horizontalalignment"] = "left" self.text_args["verticalalignment"] = "bottom" elif self.corner == "lower_right": self.pos = (0.97, 0.03) self.text_args["horizontalalignment"] = "right" self.text_args["verticalalignment"] = "bottom" elif self.corner is None: self.pos = (0.5, 0.5) self.text_args["horizontalalignment"] = "center" self.text_args["verticalalignment"] = "center" else: raise ValueError( "Argument 'corner' must be set to " "'upper_left', 'upper_right', 'lower_left', " "'lower_right', or None" ) self.text = "" # If we're annotating the time, put it in the correct format if self.time: # If no time_units are set, then identify a best fit time unit if self.time_unit is None: if plot.ds._uses_code_time_unit: # if the unit system is in code units # we should not convert to seconds for the plot. self.time_unit = plot.ds.unit_system.base_units[dimensions.time] else: # in the case of non- code units then we self.time_unit = plot.ds.get_smallest_appropriate_unit( plot.ds.current_time, quantity="time" ) t = plot.ds.current_time.in_units(self.time_unit) if self.time_offset is not None: if isinstance(self.time_offset, tuple): toffset = plot.ds.quan(self.time_offset[0], self.time_offset[1]) elif isinstance(self.time_offset, Number): toffset = plot.ds.quan(self.time_offset, self.time_unit) elif not isinstance(self.time_offset, YTQuantity): raise RuntimeError( "'time_offset' must be a float, tuple, or YTQuantity!" ) t -= toffset.in_units(self.time_unit) try: # here the time unit will be in brackets on the annotation. un = self.time_unit.latex_representation() time_unit = r"$\ \ (" + un + r")$" except AttributeError: time_unit = str(self.time_unit).replace("_", " ") self.text += self.time_format.format(time=float(t), units=time_unit) # If time and redshift both shown, do one on top of the other if self.time and self.redshift: self.text += "\n" if self.redshift and not hasattr(plot.data.ds, "current_redshift"): warnings.warn( f"dataset {plot.data.ds} does not have current_redshift attribute. " "Set redshift=False to silence this warning.", stacklevel=2, ) self.redshift = False # If we're annotating the redshift, put it in the correct format if self.redshift: z = plot.data.ds.current_redshift # Replace instances of -0.0* with 0.0* to avoid # negative null redshifts (e.g., "-0.00"). self.text += self.redshift_format.format(redshift=float(z)) self.text = re.sub("-(0.0*)$", r"\g<1>", self.text) # This is just a fancy wrapper around the TextLabelCallback tcb = TextLabelCallback( self.pos, self.text, coord_system=self.coord_system, text_args=self.text_args, inset_box_args=self.inset_box_args, ) return tcb(plot) class ScaleCallback(PlotCallback): r""" Annotates the scale of the plot at a specified location in the image (either in a preset corner, or by specifying (x,y) image coordinates with the pos argument. Coeff and units (e.g. 1 Mpc or 100 kpc) refer to the distance scale you desire to show on the plot. If no coeff and units are specified, an appropriate pair will be determined such that your scale bar is never smaller than min_frac or greater than max_frac of your plottable axis length. Additional customization of the scale bar is possible by adjusting the text_args and size_bar_args dictionaries. The text_args dictionary accepts matplotlib's font_properties arguments to override the default font_properties for the current plot. The size_bar_args dictionary accepts keyword arguments for the AnchoredSizeBar class in matplotlib's axes_grid toolkit. Parameters ---------- corner : string, optional Corner sets up one of 4 predeterimined locations for the scale bar to be displayed in the image: 'upper_left', 'upper_right', 'lower_left', 'lower_right' (also allows None). This value will be overridden by the optional 'pos' keyword. coeff : float, optional The coefficient of the unit defining the distance scale (e.g. 10 kpc or 100 Mpc) for overplotting. If set to None along with unit keyword, coeff will be automatically determined to be a power of 10 relative to the best-fit unit. unit : string, optional unit must be a valid yt distance unit (e.g. 'm', 'km', 'AU', 'pc', 'kpc', etc.) or set to None. If set to None, will be automatically determined to be the best-fit to the data. pos : 2- or 3-element tuples, lists, or arrays, optional The image location of the scale bar in the plot coordinate system. Setting pos overrides the corner parameter. min_frac, max_frac: float, optional The minimum/maximum fraction of the axis width for the scale bar to extend. A value of 1 would allow the scale bar to extend across the entire axis width. Only used for automatically calculating best-fit coeff and unit when neither is specified, otherwise disregarded. coord_system : string, optional This string defines the coordinate system of the coordinates of pos Valid coordinates are: - "data": 3D dataset coordinates - "plot": 2D coordinates defined by the actual plot limits - "axis": MPL axis coordinates: (0,0) is lower left; (1,1) is upper right - "figure": MPL figure coordinates: (0,0) is lower left, (1,1) is upper right text_args : dictionary, optional A dictionary of parameters to used to update the font_properties for the text in this callback. For any property not set, it will use the defaults of the plot. Thus one can modify the text size with ``text_args={'size':24}`` size_bar_args : dictionary, optional A dictionary of parameters to be passed to the Matplotlib AnchoredSizeBar initializer. Defaults: ``{'pad': 0.25, 'sep': 5, 'borderpad': 1, 'color': 'w'}`` draw_inset_box : boolean, optional Whether or not an inset box should be included around the scale bar. inset_box_args : dictionary, optional A dictionary of keyword arguments to be passed to the matplotlib Patch object that represents the inset box. Defaults: ``{'facecolor': 'black', 'linewidth': 3, 'edgecolor': 'white', 'alpha': 0.5, 'boxstyle': 'square'}`` scale_text_format : string, optional This specifies the format of the scalebar value assuming "scale" is the numerical value and "unit" is units of the scale (e.g. 'cm', 'kpc', etc.) The scale can be specified to arbitrary precision according to printf formatting codes. The format string must only specify "scale" and "units". Example: "Length = {scale:.2f} {units}". Default: "{scale} {units}" Example ------- >>> import yt >>> ds = yt.load("Enzo_64/DD0020/data0020") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_scale() """ _type_name = "scale" _supported_geometries = ("cartesian", "spectral_cube", "force") def __init__( self, *, corner="lower_right", coeff=None, unit=None, pos=None, max_frac=0.16, min_frac=0.015, coord_system="axis", text_args=None, size_bar_args=None, draw_inset_box=False, inset_box_args=None, scale_text_format="{scale} {units}", ): # Set position based on corner argument. self.corner = corner self.coeff = coeff self.unit = unit self.pos = pos self.max_frac = max_frac self.min_frac = min_frac self.coord_system = coord_system self.scale_text_format = scale_text_format self.size_bar_args = { "pad": 0.05, "sep": 5, "borderpad": 1, "color": "white", **(size_bar_args or {}), } self.inset_box_args = { "facecolor": "black", "linewidth": 3, "edgecolor": "white", "alpha": 0.5, "boxstyle": "square", **(inset_box_args or {}), } self.text_args = text_args or {} self.draw_inset_box = draw_inset_box def __call__(self, plot): from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar # Callback only works for plots with axis ratios of 1 xsize = plot.xlim[1] - plot.xlim[0] # Setting pos overrides corner argument if self.pos is None: if self.corner == "upper_left": self.pos = (0.11, 0.952) elif self.corner == "upper_right": self.pos = (0.89, 0.952) elif self.corner == "lower_left": self.pos = (0.11, 0.052) elif self.corner == "lower_right": self.pos = (0.89, 0.052) elif self.corner is None: self.pos = (0.5, 0.5) else: raise ValueError( "Argument 'corner' must be set to " "'upper_left', 'upper_right', 'lower_left', " "'lower_right', or None" ) # When identifying a best fit distance unit, do not allow scale marker # to be greater than max_frac fraction of xaxis or under min_frac # fraction of xaxis max_scale = self.max_frac * xsize min_scale = self.min_frac * xsize # If no units are set, pick something sensible. if self.unit is None: # User has set the axes units and supplied a coefficient. if plot._axes_unit_names is not None and self.coeff is not None: self.unit = plot._axes_unit_names[0] # Nothing provided; identify a best fit distance unit. else: min_scale = plot.ds.get_smallest_appropriate_unit( min_scale, return_quantity=True ) max_scale = plot.ds.get_smallest_appropriate_unit( max_scale, return_quantity=True ) if self.coeff is None: self.coeff = max_scale.v self.unit = max_scale.units elif self.coeff is None: self.coeff = 1 self.scale = plot.ds.quan(self.coeff, self.unit) text = self.scale_text_format.format(scale=int(self.coeff), units=self.unit) image_scale = ( plot.frb.convert_distance_x(self.scale) / plot.frb.convert_distance_x(xsize) ).v size_vertical = self.size_bar_args.pop("size_vertical", 0.005 * plot.aspect) fontproperties = self.size_bar_args.pop( "fontproperties", plot.font_properties.copy() ) frameon = self.size_bar_args.pop("frameon", self.draw_inset_box) # FontProperties instances use set_() setter functions for key, val in self.text_args.items(): setter_func = "set_" + key try: getattr(fontproperties, setter_func)(val) except AttributeError as e: raise AttributeError( "Cannot set text_args keyword " f"to include {key!r} because MPL's fontproperties object does " f"not contain function {setter_func!r}" ) from e # this "anchors" the size bar to a box centered on self.pos in axis # coordinates self.size_bar_args["bbox_to_anchor"] = self.pos self.size_bar_args["bbox_transform"] = plot._axes.transAxes bar = AnchoredSizeBar( plot._axes.transAxes, image_scale, text, 10, size_vertical=size_vertical, fontproperties=fontproperties, frameon=frameon, **self.size_bar_args, ) bar.patch.set(**self.inset_box_args) plot._axes.add_artist(bar) return plot class RayCallback(PlotCallback): """ Adds a line representing the projected path of a ray across the plot. The ray can be either a YTOrthoRay, YTRay, or a LightRay object. annotate_ray() will properly account for periodic rays across the volume. If arrow is set to True, uses the MPL.pyplot.arrow function, otherwise uses the MPL.pyplot.plot function to plot a normal line. Adjust plot_args accordingly. Parameters ---------- ray : YTOrthoRay, YTRay, or LightRay Ray is the object that we want to include. We overplot the projected trajectory of the ray. If the object is a trident.LightRay object, it will only plot the segment of the LightRay that intersects the dataset currently displayed. arrow : boolean, optional Whether or not to place an arrowhead on the front of the ray to denote direction Default: False plot_args : dictionary, optional A dictionary of any arbitrary parameters to be passed to the Matplotlib line object. Defaults: {'color':'white', 'linewidth':2}. Examples -------- >>> # Overplot a ray and an ortho_ray object on a projection >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> oray = ds.ortho_ray(1, (0.3, 0.4)) # orthoray down the y axis >>> ray = ds.ray((0.1, 0.2, 0.3), (0.6, 0.7, 0.8)) # arbitrary ray >>> p = yt.ProjectionPlot(ds, "z", "density") >>> p.annotate_ray(oray) >>> p.annotate_ray(ray) >>> p.save() >>> # Overplot a LightRay object on a projection >>> import yt >>> from trident import LightRay >>> ds = yt.load("enzo_cosmology_plus/RD0004/RD0004") >>> lr = LightRay( ... "enzo_cosmology_plus/AMRCosmology.enzo", "Enzo", 0.0, 0.1, time_data=False ... ) >>> lray = lr.make_light_ray(seed=1) >>> p = yt.ProjectionPlot(ds, "z", "density") >>> p.annotate_ray(lr) >>> p.save() """ _type_name = "ray" _supported_geometries = ("cartesian", "spectral_cube", "force") def __init__(self, ray, *, arrow=False, plot_args=None, **kwargs): self.ray = ray self.arrow = arrow if plot_args is not None: issue_deprecation_warning( "`plot_args` is deprecated. " "You can now pass arbitrary keyword arguments instead of a dictionary.", since="4.1", stacklevel=5, ) self.plot_args = { "color": "white", "linewidth": 2, **(plot_args or {}), **kwargs, } def _process_ray(self): """ Get the start_coord and end_coord of a ray object """ return (self.ray.start_point, self.ray.end_point) def _process_ortho_ray(self): """ Get the start_coord and end_coord of an ortho_ray object """ start_coord = self.ray.ds.domain_left_edge.copy() end_coord = self.ray.ds.domain_right_edge.copy() xax = self.ray.ds.coordinates.x_axis[self.ray.axis] yax = self.ray.ds.coordinates.y_axis[self.ray.axis] start_coord[xax] = end_coord[xax] = self.ray.coords[0] start_coord[yax] = end_coord[yax] = self.ray.coords[1] return (start_coord, end_coord) def _process_light_ray(self, plot): """ Get the start_coord and end_coord of a LightRay object. Identify which of the sections of the LightRay is in the dataset that is currently being plotted. If there is one, return the start and end of the corresponding ray segment """ for ray_ds in self.ray.light_ray_solution: if ray_ds["unique_identifier"] == str(plot.ds.unique_identifier): start_coord = plot.ds.arr(ray_ds["start"]) end_coord = plot.ds.arr(ray_ds["end"]) return (start_coord, end_coord) # if no intersection between the plotted dataset and the LightRay # return a false tuple to pass to start_coord return ((False, False), (False, False)) def __call__(self, plot): type_name = getattr(self.ray, "_type_name", None) if type_name == "ray": start_coord, end_coord = self._process_ray() elif type_name == "ortho_ray": start_coord, end_coord = self._process_ortho_ray() elif hasattr(self.ray, "light_ray_solution"): start_coord, end_coord = self._process_light_ray(plot) else: raise ValueError("ray must be a YTRay, YTOrthoRay, or LightRay object.") # if start_coord and end_coord are all False, it means no intersecting # ray segment with this plot. if not all(start_coord) and not all(end_coord): return plot # if possible, break periodic ray into non-periodic # segments and add each of them individually if any(plot.ds.periodicity): segments = periodic_ray( start_coord.to("code_length"), end_coord.to("code_length"), left=plot.ds.domain_left_edge.to("code_length"), right=plot.ds.domain_right_edge.to("code_length"), ) else: segments = [[start_coord, end_coord]] # To assure that the last ray segment has an arrow if so desired # and all other ray segments are lines for segment in segments[:-1]: cb = LinePlotCallback( segment[0], segment[1], coord_system="data", **self.plot_args ) cb(plot) segment = segments[-1] if self.arrow: cb = ArrowCallback( segment[1], starting_pos=segment[0], coord_system="data", **self.plot_args, ) else: cb = LinePlotCallback( segment[0], segment[1], coord_system="data", **self.plot_args ) cb(plot) return plot class LineIntegralConvolutionCallback(PlotCallback): """ Add the line integral convolution to the plot for vector fields visualization. Two component of vector fields needed to be provided (i.e., velocity_x and velocity_y, magnetic_field_x and magnetic_field_y). Parameters ---------- field_x, field_y : string The names of two components of vector field which will be visualized texture : 2-d array with the same shape of image, optional Texture will be convolved when computing line integral convolution. A white noise background will be used as default. kernellen : float, optional The lens of kernel for convolution, which is the length over which the convolution will be performed. For longer kernellen, longer streamline structure will appear. lim : 2-element tuple, list, or array, optional The value of line integral convolution will be clipped to the range of lim, which applies upper and lower bounds to the values of line integral convolution and enhance the visibility of plots. Each element should be in the range of [0,1]. cmap : string, optional The name of colormap for line integral convolution plot. alpha : float, optional The alpha value for line integral convolution plot. const_alpha : boolean, optional If set to False (by default), alpha will be weighted spatially by the values of line integral convolution; otherwise a constant value of the given alpha is used. Example ------- >>> import yt >>> ds = yt.load("Enzo_64/DD0020/data0020") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_line_integral_convolution( ... "velocity_x", "velocity_y", lim=(0.5, 0.65) ... ) """ _type_name = "line_integral_convolution" _supported_geometries = ( "cartesian", "spectral_cube", "polar", "cylindrical", "spherical", ) _incompatible_plot_types = ("LineIntegralConvolutionCallback",) def __init__( self, field_x, field_y, texture=None, kernellen=50.0, lim=(0.5, 0.6), cmap="binary", alpha=0.8, const_alpha=False, ): self.field_x = field_x self.field_y = field_y self.texture = texture self.kernellen = kernellen self.lim = lim self.cmap = cmap self.alpha = alpha self.const_alpha = const_alpha def __call__(self, plot): from matplotlib import cm bounds = self._physical_bounds(plot) extent = self._plot_bounds(plot) # We are feeding this size into the pixelizer, where it will properly # set it in reverse order nx = plot.raw_image_shape[1] ny = plot.raw_image_shape[0] pixX = plot.data.ds.coordinates.pixelize( plot.data.axis, plot.data, self.field_x, bounds, (nx, ny) ) pixY = plot.data.ds.coordinates.pixelize( plot.data.axis, plot.data, self.field_y, bounds, (nx, ny) ) vectors = np.concatenate((pixX[..., np.newaxis], pixY[..., np.newaxis]), axis=2) if self.texture is None: prng = np.random.RandomState(0x4D3D3D3) self.texture = prng.random_sample((nx, ny)) elif self.texture.shape != (nx, ny): raise ValueError( "'texture' must have the same shape " "with that of output image (%d, %d)" % (nx, ny) ) kernel = np.sin( np.arange(self.kernellen, dtype="float64") * np.pi / self.kernellen ) lic_data = line_integral_convolution_2d(vectors, self.texture, kernel) lic_data = lic_data / lic_data.max() lic_data_clip = np.clip(lic_data, self.lim[0], self.lim[1]) mask = ~(np.isfinite(pixX) & np.isfinite(pixY)) lic_data[mask] = np.nan lic_data_clip[mask] = np.nan if plot._swap_axes: lic_data_clip = lic_data_clip.transpose() extent = (extent[2], extent[3], extent[0], extent[1]) if self.const_alpha: plot._axes.imshow( lic_data_clip, extent=extent, cmap=self.cmap, alpha=self.alpha, origin="lower", aspect="auto", ) else: lic_data_rgba = cm.ScalarMappable(norm=None, cmap=self.cmap).to_rgba( lic_data_clip ) lic_data_clip_rescale = (lic_data_clip - self.lim[0]) / ( self.lim[1] - self.lim[0] ) lic_data_rgba[..., 3] = lic_data_clip_rescale * self.alpha plot._axes.imshow( lic_data_rgba, extent=extent, cmap=self.cmap, origin="lower", aspect="auto", ) return plot class CellEdgesCallback(PlotCallback): """ Annotate cell edges. This is done through a second call to pixelize, where the distance from a pixel to a cell boundary in pixels is compared against the `line_width` argument. The secondary image is colored as `color` and overlaid with the `alpha` value. Parameters ---------- line_width : float The width of the cell edge lines in normalized units relative to the size of the longest axis. Default is 1% of the size of the smallest axis. alpha : float When the second image is overlaid, it will have this level of alpha transparency. Default is 1.0 (fully-opaque). color : tuple of three floats or matplotlib color name This is the color of the cell edge values. It defaults to black. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> s = yt.SlicePlot(ds, "z", "density") >>> s.annotate_cell_edges() >>> s.save() """ _type_name = "cell_edges" _supported_geometries = ("cartesian", "spectral_cube", "cylindrical") def __init__(self, line_width=0.002, alpha=1.0, color="black"): from matplotlib.colors import ColorConverter conv = ColorConverter() self.line_width = line_width self.alpha = alpha self.color = np.array(conv.to_rgb(color), dtype="uint8") * 255 def __call__(self, plot): if plot.data.ds.geometry == "cylindrical" and plot.data.ds.dimensionality == 3: raise NotImplementedError( "Cell edge annotation is only supported for \ for 2D cylindrical geometry, not 3D" ) x0, x1, y0, y1 = self._physical_bounds(plot) nx = plot.raw_image_shape[1] ny = plot.raw_image_shape[0] aspect = float((y1 - y0) / (x1 - x0)) pixel_aspect = float(ny) / nx relative_aspect = pixel_aspect / aspect if relative_aspect > 1: nx = int(nx / relative_aspect) else: ny = int(ny * relative_aspect) if aspect > 1: if nx < 1600: nx = int(1600.0 / nx * ny) ny = 1600 long_axis = ny else: if ny < 1600: nx = int(1600.0 / ny * nx) ny = 1600 long_axis = nx line_width = max(self.line_width * long_axis, 1.0) im = np.zeros((ny, nx), dtype="f8") pixelize_cartesian( im, plot.data["px"], plot.data["py"], plot.data["pdx"], plot.data["pdy"], plot.data["px"], # dummy field (x0, x1, y0, y1), line_width=line_width, ) # New image: im_buffer = np.zeros((ny, nx, 4), dtype="uint8") im_buffer[im > 0, 3] = 255 im_buffer[im > 0, :3] = self.color extent = self._plot_bounds(plot) if plot._swap_axes: im_buffer = im_buffer.transpose((1, 0, 2)) # note: when using imshow, the extent keyword argument has to be the # swapped extents, so the extent is swapped here (rather than # calling self._set_plot_limits). # https://github.com/yt-project/yt/issues/5094 extent = _swap_axes_extents(extent) plot._axes.imshow( im_buffer, origin="lower", interpolation="bilinear", extent=extent, alpha=self.alpha, ) yt-project-yt-f043ac8/yt/visualization/plot_window.py000066400000000000000000003340361510711153200231370ustar00rootroot00000000000000import abc import sys from collections import defaultdict from numbers import Number from typing import TYPE_CHECKING, Union import matplotlib import numpy as np from more_itertools import always_iterable from unyt.exceptions import UnitConversionError from yt._maintenance.deprecation import issue_deprecation_warning from yt._typing import AlphaT from yt.data_objects.image_array import ImageArray from yt.frontends.sph.data_structures import ParticleDataset from yt.frontends.stream.data_structures import StreamParticlesDataset from yt.frontends.ytdata.data_structures import YTSpatialPlotDataset from yt.funcs import ( fix_axis, fix_unitary, is_sequence, iter_fields, mylog, obj_length, parse_center_array, validate_moment, ) from yt.geometry.api import Geometry from yt.geometry.oct_geometry_handler import OctreeIndex from yt.units.unit_object import Unit # type: ignore from yt.units.unit_registry import UnitParseError # type: ignore from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.exceptions import ( YTCannotParseUnitDisplayName, YTDataTypeUnsupported, YTInvalidFieldType, YTPlotCallbackError, YTUnitNotRecognized, ) from yt.utilities.math_utils import ortho_find from yt.utilities.orientation import Orientation from yt.visualization._handlers import ColorbarHandler, NormHandler from yt.visualization.base_plot_types import CallbackWrapper, ImagePlotMPL from ._commons import ( _get_units_label, _swap_axes_extents, get_default_from_config, ) from .fixed_resolution import ( FixedResolutionBuffer, OffAxisProjectionFixedResolutionBuffer, ) from .geo_plot_utils import get_mpl_transform from .plot_container import ( ImagePlotContainer, invalidate_data, invalidate_figure, invalidate_plot, ) if TYPE_CHECKING: from yt.visualization.plot_modifications import PlotCallback if sys.version_info >= (3, 11): from typing import assert_never else: from typing_extensions import assert_never def get_window_parameters(axis, center, width, ds): width = ds.coordinates.sanitize_width(axis, width, None) center, display_center = ds.coordinates.sanitize_center(center, axis) xax = ds.coordinates.x_axis[axis] yax = ds.coordinates.y_axis[axis] bounds = ( display_center[xax] - width[0] / 2, display_center[xax] + width[0] / 2, display_center[yax] - width[1] / 2, display_center[yax] + width[1] / 2, ) return (bounds, center, display_center) def get_oblique_window_parameters( normal, center, width, ds, depth=None, ): center, display_center = ds.coordinates.sanitize_center(center, axis=None) width = ds.coordinates.sanitize_width(normal, width, depth) if len(width) == 2: # Transforming to the cutting plane coordinate system # the original dimensionless center messes up off-axis # SPH projections though -> don't use this center there center = ( (center - ds.domain_left_edge) / ds.domain_width - 0.5 ) * ds.domain_width (normal, perp1, perp2) = ortho_find(normal) mat = np.transpose(np.column_stack((perp1, perp2, normal))) center = np.dot(mat, center) w = tuple(el.in_units("code_length") for el in width) bounds = tuple(((2 * (i % 2)) - 1) * w[i // 2] / 2 for i in range(len(w) * 2)) return bounds, center def get_axes_unit(width, ds): r""" Infers the axes unit names from the input width specification """ if ds.no_cgs_equiv_length: return ("code_length",) * 2 if is_sequence(width): if isinstance(width[1], str): axes_unit = (width[1], width[1]) elif is_sequence(width[1]): axes_unit = (width[0][1], width[1][1]) elif isinstance(width[0], YTArray): axes_unit = (str(width[0].units), str(width[1].units)) else: axes_unit = None else: if isinstance(width, YTArray): axes_unit = (str(width.units), str(width.units)) else: axes_unit = None return axes_unit def validate_mesh_fields(data_source, fields): # this check doesn't make sense for ytdata plot datasets, which # load mesh data as a particle field but nonetheless can still # make plots with it if isinstance(data_source.ds, YTSpatialPlotDataset): return canonical_fields = data_source._determine_fields(fields) invalid_fields = [] for field in canonical_fields: finfo = data_source.ds.field_info[field] if finfo.sampling_type == "particle": if not hasattr(data_source.ds, "_sph_ptypes"): pass elif finfo.is_sph_field: continue invalid_fields.append(field) if len(invalid_fields) > 0: raise YTInvalidFieldType(invalid_fields) class PlotWindow(ImagePlotContainer, abc.ABC): r""" A plotting mechanism based around the concept of a window into a data source. It can have arbitrary fields, each of which will be centered on the same viewpoint, but will have individual zlimits. The data and plot are updated separately, and each can be invalidated as the object is modified. Data is handled by a FixedResolutionBuffer object. Parameters ---------- data_source : :class:`yt.data_objects.selection_objects.base_objects.YTSelectionContainer2D` This is the source to be pixelized, which can be a projection, slice, or a cutting plane. bounds : sequence of floats Bounds are the min and max in the image plane that we want our image to cover. It's in the order of (xmin, xmax, ymin, ymax), where the coordinates are all in the appropriate code units. buff_size : sequence of ints The size of the image to generate. antialias : boolean This can be true or false. It determines whether or not sub-pixel rendering is used during data deposition. window_size : float The size of the window on the longest axis (in units of inches), including the margins but not the colorbar. """ def __init__( self, data_source, bounds, buff_size=(800, 800), antialias=True, periodic=True, origin="center-window", oblique=False, window_size=8.0, fields=None, fontsize=18, aspect=None, setup=False, *, geometry: Geometry = Geometry.CARTESIAN, ) -> None: # axis manipulation operations are callback-only: self._swap_axes_input = False self._flip_vertical = False self._flip_horizontal = False self.center = None self._periodic = periodic self.oblique = oblique self._equivalencies = defaultdict(lambda: (None, {})) # type: ignore [var-annotated] self.buff_size = buff_size self.antialias = antialias self._axes_unit_names = None self._transform = None self._projection = None self.aspect = aspect skip = list(FixedResolutionBuffer._exclude_fields) + data_source._key_fields fields = list(iter_fields(fields)) self.override_fields = list(set(fields).intersection(set(skip))) self.fields = [f for f in fields if f not in skip] self._frb: FixedResolutionBuffer | None = None super().__init__(data_source, window_size, fontsize) self._set_window(bounds) # this automatically updates the data and plot if origin != "native": if geometry is Geometry.CARTESIAN or geometry is Geometry.SPECTRAL_CUBE: pass elif ( geometry is Geometry.CYLINDRICAL or geometry is Geometry.POLAR or geometry is Geometry.SPHERICAL or geometry is Geometry.GEOGRAPHIC or geometry is Geometry.INTERNAL_GEOGRAPHIC ): mylog.info("Setting origin='native' for %s geometry.", geometry) origin = "native" else: assert_never(geometry) self.origin = origin if self.data_source.center is not None and not oblique: ax = self.data_source.axis xax = self.ds.coordinates.x_axis[ax] yax = self.ds.coordinates.y_axis[ax] center, display_center = self.ds.coordinates.sanitize_center( self.data_source.center, ax ) center = [display_center[xax], display_center[yax]] self.set_center(center) axname = self.ds.coordinates.axis_name[ax] transform = self.ds.coordinates.data_transform[axname] projection = self.ds.coordinates.data_projection[axname] self._projection = get_mpl_transform(projection) self._transform = get_mpl_transform(transform) self._setup_plots() for field in self.data_source._determine_fields(self.fields): finfo = self.data_source.ds._get_field_info(field) pnh = self.plots[field].norm_handler # take_log can be `None` so we explicitly compare against a boolean pnh.prefer_log = finfo.take_log is not False # override from user configuration if any log, linthresh = get_default_from_config( self.data_source, field=field, keys=["log", "linthresh"], defaults=[None, None], ) if linthresh is not None: self.set_log(field, linthresh=linthresh) elif log is not None: self.set_log(field, log) def __iter__(self): for ds in self.ts: mylog.warning("Switching to %s", ds) self._switch_ds(ds) yield self def piter(self, *args, **kwargs): for ds in self.ts.piter(*args, **kwargs): self._switch_ds(ds) yield self @property def frb(self): # Force the regeneration of the fixed resolution buffer # * if there's none # * if the data has been invalidated # * if the frb has been inalidated if not self._data_valid: self._recreate_frb() return self._frb @frb.setter def frb(self, value): self._frb = value self._data_valid = True @frb.deleter def frb(self): del self._frb self._frb = None def _recreate_frb(self): old_fields = None old_filters = [] # If we are regenerating an frb, we want to know what fields we had before if self._frb is not None: old_fields = list(self._frb.data.keys()) old_units = [_.units for _ in self._frb.data.values()] old_filters = self._frb._filters # Set the bounds if hasattr(self, "zlim"): # Support OffAxisProjectionPlot and OffAxisSlicePlot bounds = self.xlim + self.ylim + self.zlim else: bounds = self.xlim + self.ylim # Generate the FRB self.frb = self._frb_generator( self.data_source, bounds, self.buff_size, self.antialias, periodic=self._periodic, filters=old_filters, ) # At this point the frb has the valid bounds, size, aliasing, etc. if old_fields is not None: # Restore the old fields for key, units in zip(old_fields, old_units, strict=False): self._frb.render(key) equiv = self._equivalencies[key] if equiv[0] is None: self._frb[key].convert_to_units(units) else: self.frb.set_unit(key, units, equiv[0], equiv[1]) # Restore the override fields for key in self.override_fields: self._frb.render(key) @property def _has_swapped_axes(self): # note: we always run the validations here in case the states of # the conflicting attributes have changed. return self._validate_swap_axes(self._swap_axes_input) @invalidate_data def swap_axes(self): # toggles the swap_axes behavior new_swap_value = not self._swap_axes_input # note: we also validate here to catch invalid states immediately, even # though we validate on accessing the attribute in `_has_swapped_axes`. self._swap_axes_input = self._validate_swap_axes(new_swap_value) return self def _validate_swap_axes(self, swap_value: bool) -> bool: if swap_value and (self._transform or self._projection): mylog.warning("Cannot swap axes due to transform or projection") return False return swap_value @property def width(self): Wx = self.xlim[1] - self.xlim[0] Wy = self.ylim[1] - self.ylim[0] return (Wx, Wy) @property def bounds(self): return self.xlim + self.ylim @invalidate_data def zoom(self, factor): r"""This zooms the window by *factor* > 0. - zoom out with *factor* < 1 - zoom in with *factor* > 1 Parameters ---------- factor : float multiplier for the current width """ if factor <= 0: raise ValueError("Only positive zooming factors are meaningful.") Wx, Wy = self.width centerx = self.xlim[0] + Wx * 0.5 centery = self.ylim[0] + Wy * 0.5 nWx, nWy = Wx / factor, Wy / factor self.xlim = (centerx - nWx * 0.5, centerx + nWx * 0.5) self.ylim = (centery - nWy * 0.5, centery + nWy * 0.5) return self @invalidate_data def pan(self, deltas): r"""Pan the image by specifying absolute code unit coordinate deltas. Parameters ---------- deltas : Two-element sequence of floats, quantities, or (float, unit) tuples. (delta_x, delta_y). If a unit is not supplied the unit is assumed to be code_length. """ if len(deltas) != 2: raise TypeError( f"The pan function accepts a two-element sequence.\nReceived {deltas}." ) if isinstance(deltas[0], Number) and isinstance(deltas[1], Number): deltas = ( self.ds.quan(deltas[0], "code_length"), self.ds.quan(deltas[1], "code_length"), ) elif isinstance(deltas[0], tuple) and isinstance(deltas[1], tuple): deltas = ( self.ds.quan(deltas[0][0], deltas[0][1]), self.ds.quan(deltas[1][0], deltas[1][1]), ) elif isinstance(deltas[0], YTQuantity) and isinstance(deltas[1], YTQuantity): pass else: raise TypeError( "The arguments of the pan function must be a sequence of floats,\n" f"quantities, or (float, unit) tuples. Received {deltas}" ) self.xlim = (self.xlim[0] + deltas[0], self.xlim[1] + deltas[0]) self.ylim = (self.ylim[0] + deltas[1], self.ylim[1] + deltas[1]) return self @invalidate_data def pan_rel(self, deltas): r"""Pan the image by specifying relative deltas, to the FOV. Parameters ---------- deltas : sequence of floats (delta_x, delta_y) in *relative* code unit coordinates """ Wx, Wy = self.width self.xlim = (self.xlim[0] + Wx * deltas[0], self.xlim[1] + Wx * deltas[0]) self.ylim = (self.ylim[0] + Wy * deltas[1], self.ylim[1] + Wy * deltas[1]) return self @invalidate_plot def set_unit(self, field, new_unit, equivalency=None, equivalency_kwargs=None): """Sets a new unit for the requested field parameters ---------- field : string or field tuple The name of the field that is to be changed. new_unit : string or Unit object equivalency : string, optional If set, the equivalency to use to convert the current units to the new requested unit. If None, the unit conversion will be done without an equivalency equivalency_kwargs : string, optional Keyword arguments to be passed to the equivalency. Only used if ``equivalency`` is set. """ for f, u in zip(iter_fields(field), always_iterable(new_unit), strict=True): self.frb.set_unit(f, u, equivalency, equivalency_kwargs) self._equivalencies[f] = (equivalency, equivalency_kwargs) pnh = self.plots[f].norm_handler pnh.display_units = u return self @invalidate_plot def set_origin(self, origin): """Set the plot origin. Parameters ---------- origin : string or length 1, 2, or 3 sequence. The location of the origin of the plot coordinate system. This is typically represented by a '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. In the event that none of these options place the origin in a desired location, a sequence of tuples and a string specifying the coordinate space can be given. If plain numeric types are input, units of `code_length` are assumed. Further examples: =============================================== =============================== format example =============================================== =============================== '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ((yloc, '{unit}'), (xloc, '{unit}'), '{space}') ((0, 'm'), (.4, 'm'), 'window') (xloc, yloc, '{space}') (0.23, 0.5, 'domain') =============================================== =============================== """ self.origin = origin return self @invalidate_plot @invalidate_figure def set_mpl_projection(self, mpl_proj): r""" Set the matplotlib projection type with a cartopy transform function Given a string or a tuple argument, this will project the data onto the plot axes with the chosen transform function. Assumes that the underlying data has a PlateCarree transform type. To annotate the plot with coastlines or other annotations, `render()` will need to be called after this function to make the axes available for annotation. Parameters ---------- mpl_proj : string or tuple if passed as a string, mpl_proj is the specified projection type, if passed as a tuple, then tuple will take the form of ``("ProjectionType", (args))`` or ``("ProjectionType", (args), {kwargs})`` Valid projection type options include: 'PlateCarree', 'LambertConformal', 'LabmbertCylindrical', 'Mercator', 'Miller', 'Mollweide', 'Orthographic', 'Robinson', 'Stereographic', 'TransverseMercator', 'InterruptedGoodeHomolosine', 'RotatedPole', 'OGSB', 'EuroPP', 'Geostationary', 'Gnomonic', 'NorthPolarStereo', 'OSNI', 'SouthPolarStereo', 'AlbersEqualArea', 'AzimuthalEquidistant', 'Sinusoidal', 'UTM', 'NearsidePerspective', 'LambertAzimuthalEqualArea' Examples -------- This will create a Mollweide projection using Mollweide default values and annotate it with coastlines >>> import yt >>> ds = yt.load("") >>> p = yt.SlicePlot(ds, "altitude", "AIRDENS") >>> p.set_mpl_projection("AIRDENS", "Mollweide") >>> p.render() >>> p.plots["AIRDENS"].axes.coastlines() >>> p.show() This will move the PlateCarree central longitude to 90 degrees and annotate with coastlines. >>> import yt >>> ds = yt.load("") >>> p = yt.SlicePlot(ds, "altitude", "AIRDENS") >>> p.set_mpl_projection( ... "AIRDENS", ("PlateCarree", (), {"central_longitude": 90, "globe": None}) ... ) >>> p.render() >>> p.plots["AIRDENS"].axes.set_global() >>> p.plots["AIRDENS"].axes.coastlines() >>> p.show() This will create a RoatatedPole projection with the unrotated pole position at 37.5 degrees latitude and 177.5 degrees longitude by passing them in as args. >>> import yt >>> ds = yt.load("") >>> p = yt.SlicePlot(ds, "altitude", "AIRDENS") >>> p.set_mpl_projection("RotatedPole", (177.5, 37.5)) >>> p.render() >>> p.plots["AIRDENS"].axes.set_global() >>> p.plots["AIRDENS"].axes.coastlines() >>> p.show() This will create a RoatatedPole projection with the unrotated pole position at 37.5 degrees latitude and 177.5 degrees longitude by passing them in as kwargs. >>> import yt >>> ds = yt.load("") >>> p = yt.SlicePlot(ds, "altitude", "AIRDENS") >>> p.set_mpl_projection( ... ("RotatedPole", (), {"pole_latitude": 37.5, "pole_longitude": 177.5}) ... ) >>> p.render() >>> p.plots["AIRDENS"].axes.set_global() >>> p.plots["AIRDENS"].axes.coastlines() >>> p.show() """ self._projection = get_mpl_transform(mpl_proj) axname = self.ds.coordinates.axis_name[self.data_source.axis] transform = self.ds.coordinates.data_transform[axname] self._transform = get_mpl_transform(transform) return self @invalidate_data def _set_window(self, bounds): """Set the bounds of the plot window. This is normally only called internally, see set_width. Parameters ---------- bounds : a four element sequence of floats The x and y bounds, in the format (x0, x1, y0, y1) """ if self.center is not None: dx = bounds[1] - bounds[0] dy = bounds[3] - bounds[2] self.xlim = (self.center[0] - dx / 2.0, self.center[0] + dx / 2.0) self.ylim = (self.center[1] - dy / 2.0, self.center[1] + dy / 2.0) else: self.xlim = tuple(bounds[0:2]) self.ylim = tuple(bounds[2:4]) if len(bounds) == 6: # Support OffAxisProjectionPlot and OffAxisSlicePlot self.zlim = tuple(bounds[4:6]) mylog.info("xlim = %f %f", self.xlim[0], self.xlim[1]) mylog.info("ylim = %f %f", self.ylim[0], self.ylim[1]) if hasattr(self, "zlim"): mylog.info("zlim = %f %f", self.zlim[0], self.zlim[1]) @invalidate_data def set_width(self, width, unit=None): """set the width of the plot window parameters ---------- width : float, array of floats, (float, unit) tuple, or tuple of (float, unit) tuples. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. unit : str the unit the width has been specified in. If width is a tuple, this argument is ignored. Defaults to code units. """ if isinstance(width, Number): if unit is None: width = (width, "code_length") else: width = (width, fix_unitary(unit)) axes_unit = get_axes_unit(width, self.ds) width = self.ds.coordinates.sanitize_width(self.frb.axis, width, None) centerx = (self.xlim[1] + self.xlim[0]) / 2.0 centery = (self.ylim[1] + self.ylim[0]) / 2.0 self.xlim = (centerx - width[0] / 2, centerx + width[0] / 2) self.ylim = (centery - width[1] / 2, centery + width[1] / 2) if hasattr(self, "zlim"): centerz = (self.zlim[1] + self.zlim[0]) / 2.0 mw = self.ds.arr(width).max() self.zlim = (centerz - mw / 2.0, centerz + mw / 2.0) self.set_axes_unit(axes_unit) return self @invalidate_data def set_center(self, new_center, unit="code_length"): """Sets a new center for the plot window parameters ---------- new_center : two element sequence of floats The coordinates of the new center of the image in the coordinate system defined by the plot axes. If the unit keyword is not specified, the coordinates are assumed to be in code units. unit : string The name of the unit new_center is given in. If new_center is a YTArray or tuple of YTQuantities, this keyword is ignored. """ error = RuntimeError( "\n" "new_center must be a two-element list or tuple of floats \n" "corresponding to a coordinate in the plot relative to \n" "the plot coordinate system.\n" ) if new_center is None: self.center = None elif is_sequence(new_center): if len(new_center) != 2: raise error for el in new_center: if not isinstance(el, Number) and not isinstance(el, YTQuantity): raise error if isinstance(new_center[0], Number): new_center = [self.ds.quan(c, unit) for c in new_center] self.center = new_center else: raise error self._set_window(self.bounds) return self @invalidate_data def set_antialias(self, aa): """Turn antialiasing on or off. parameters ---------- aa : boolean """ self.antialias = aa @invalidate_data def set_buff_size(self, size): """Sets a new buffer size for the fixed resolution buffer parameters ---------- size : int or two element sequence of ints The number of data elements in the buffer on the x and y axes. If a scalar is provided, then the buffer is assumed to be square. """ if is_sequence(size): self.buff_size = size else: self.buff_size = (size, size) return self @invalidate_plot def set_axes_unit(self, unit_name): r"""Set the unit for display on the x and y axes of the image. Parameters ---------- unit_name : string or two element tuple of strings A unit, available for conversion in the dataset, that the image extents will be displayed in. If set to None, any previous units will be reset. If the unit is None, the default is chosen. If unit_name is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. If unit_name is a tuple, the first element is assumed to be the unit for the x axis and the second element the unit for the y axis. Raises ------ YTUnitNotRecognized If the unit is not known, this will be raised. Examples -------- >>> from yt import load >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> p = ProjectionPlot(ds, "y", "Density") >>> p.set_axes_unit("kpc") """ # blind except because it could be in conversion_factors or units if unit_name is not None: if isinstance(unit_name, str): unit_name = (unit_name, unit_name) for un in unit_name: try: self.ds.length_unit.in_units(un) except (UnitConversionError, UnitParseError) as e: raise YTUnitNotRecognized(un) from e self._axes_unit_names = unit_name return self @invalidate_plot def flip_horizontal(self): """ inverts the horizontal axis (the image's abscissa) """ self._flip_horizontal = not self._flip_horizontal return self @invalidate_plot def flip_vertical(self): """ inverts the vertical axis (the image's ordinate) """ self._flip_vertical = not self._flip_vertical return self def to_fits_data(self, fields=None, other_keys=None, length_unit=None, **kwargs): r"""Export the fields in this PlotWindow instance to a FITSImageData instance. This will export a set of FITS images of either the fields specified or all the fields already in the object. Parameters ---------- fields : list of strings These fields will be pixelized and output. If "None", the keys of the FRB will be used. other_keys : dictionary, optional A set of header keys and values to write into the FITS header. length_unit : string, optional the length units that the coordinates are written in. The default is to use the default length unit of the dataset. """ return self.frb.to_fits_data( fields=fields, other_keys=other_keys, length_unit=length_unit, **kwargs ) class PWViewerMPL(PlotWindow): """Viewer using matplotlib as a backend via the WindowPlotMPL.""" _current_field = None _frb_generator: type[FixedResolutionBuffer] | None = None _plot_type: str | None = None def __init__(self, *args, **kwargs) -> None: if self._frb_generator is None: self._frb_generator = kwargs.pop("frb_generator") if self._plot_type is None: self._plot_type = kwargs.pop("plot_type") self._splat_color = kwargs.pop("splat_color", None) PlotWindow.__init__(self, *args, **kwargs) # import type here to avoid import cycles # note that this import statement is actually crucial at runtime: # the filter methods for the present class are defined only when # fixed_resolution_filters is imported, so we need to guarantee # that it happens no later than instantiation self._callbacks: list[PlotCallback] = [] @property def _data_valid(self) -> bool: return self._frb is not None and self._frb._data_valid @_data_valid.setter def _data_valid(self, value): if self._frb is None: # we delegate the (in)validation responsibility to the FRB # if we don't have one yet, we can exit without doing anything return else: self._frb._data_valid = value def _setup_origin(self): origin = self.origin axis_index = self.data_source.axis xc = None yc = None if isinstance(origin, str): origin = tuple(origin.split("-")) if len(origin) > 3: raise ValueError( "Invalid origin argument with too many elements; " f"expected 1, 2 or 3 elements, got {self.origin!r}, counting {len(origin)} elements. " "Use '-' as a separator for string arguments." ) if len(origin) == 1: coord_system = origin[0] if coord_system not in ("window", "domain", "native"): raise ValueError( "Invalid origin argument. " "Single element specification must be 'window', 'domain', or 'native'. " f"Got {self.origin!r}" ) origin = ("lower", "left", coord_system) elif len(origin) == 2: err_msg = "Invalid origin argument. Using 2 elements:\n" if origin[0] in ("left", "right", "center"): o0map = {"left": "lower", "right": "upper", "center": "center"} origin = (o0map[origin[0]],) + origin elif origin[0] in ("lower", "upper"): origin = (origin[0], "center", origin[-1]) else: err_msg += " - the first one must be 'left', 'right', 'lower', 'upper' or 'center'\n" if origin[-1] not in ("window", "domain", "native"): err_msg += " - the second one must be 'window', 'domain', or 'native'\n" if len(err_msg.split("\n")) > 2: err_msg += f"Got {self.origin!r}" raise ValueError(err_msg) elif len(origin) == 3: err_msg = "Invalid origin argument. Using 3 elements:\n" if isinstance(origin[0], (int, float)): xc = self.ds.quan(origin[0], "code_length") elif isinstance(origin[0], tuple): xc = self.ds.quan(*origin[0]) elif origin[0] not in ("lower", "upper", "center"): err_msg += " - the first one must be 'lower', 'upper' or 'center' or a distance\n" if isinstance(origin[1], (int, float)): yc = self.ds.quan(origin[1], "code_length") elif isinstance(origin[1], tuple): yc = self.ds.quan(*origin[1]) elif origin[1] not in ("left", "right", "center"): err_msg += " - the second one must be 'left', 'right', 'center' or a distance\n" if origin[-1] not in ("window", "domain", "native"): err_msg += " - the third one must be 'window', 'domain', or 'native'\n" if len(err_msg.split("\n")) > 2: err_msg += f"Got {self.origin!r}" raise ValueError(err_msg) assert not isinstance(origin, str) assert len(origin) == 3 assert origin[2] in ("window", "domain", "native") if origin[2] == "window": xllim, xrlim = self.xlim yllim, yrlim = self.ylim elif origin[2] == "domain": xax = self.ds.coordinates.x_axis[axis_index] yax = self.ds.coordinates.y_axis[axis_index] xllim = self.ds.domain_left_edge[xax] xrlim = self.ds.domain_right_edge[xax] yllim = self.ds.domain_left_edge[yax] yrlim = self.ds.domain_right_edge[yax] elif origin[2] == "native": return (self.ds.quan(0.0, "code_length"), self.ds.quan(0.0, "code_length")) if xc is None and yc is None: assert origin[0] in ("lower", "upper", "center") assert origin[1] in ("left", "right", "center") if origin[0] == "lower": yc = yllim elif origin[0] == "upper": yc = yrlim elif origin[0] == "center": yc = (yllim + yrlim) / 2.0 if origin[1] == "left": xc = xllim elif origin[1] == "right": xc = xrlim elif origin[1] == "center": xc = (xllim + xrlim) / 2.0 x_in_bounds = xc >= xllim and xc <= xrlim y_in_bounds = yc >= yllim and yc <= yrlim if not x_in_bounds and not y_in_bounds: raise ValueError( "origin inputs not in bounds of specified coordinate system domain; " f"got {self.origin!r} Bounds are {xllim, xrlim} and {yllim, yrlim} respectively" ) return xc, yc def _setup_plots(self): from matplotlib.mathtext import MathTextParser if self._plot_valid: return if not self._data_valid: self._recreate_frb() self._colorbar_valid = True field_list = list(set(self.data_source._determine_fields(self.fields))) for f in field_list: axis_index = self.data_source.axis xc, yc = self._setup_origin() if self.ds._uses_code_length_unit: # this should happen only if the dataset was initialized with # argument unit_system="code" or if it's set to have no CGS # equivalent. This only needs to happen here in the specific # case that we're doing a computationally intense operation # like using cartopy, but it prevents crashes in that case. (unit_x, unit_y) = ("code_length", "code_length") elif self._axes_unit_names is None: unit = self.ds.get_smallest_appropriate_unit( self.xlim[1] - self.xlim[0] ) unit_x = unit_y = unit coords = self.ds.coordinates if hasattr(coords, "image_units"): # check for special cases defined in # non cartesian CoordinateHandler subclasses image_units = coords.image_units[coords.axis_id[axis_index]] if image_units[0] in ("deg", "rad"): unit_x = "code_length" elif image_units[0] == 1: unit_x = "dimensionless" if image_units[1] in ("deg", "rad"): unit_y = "code_length" elif image_units[1] == 1: unit_y = "dimensionless" else: (unit_x, unit_y) = self._axes_unit_names # For some plots we may set aspect by hand, such as for spectral cube data. # This will likely be replaced at some point by the coordinate handler # setting plot aspect. if self.aspect is None: self.aspect = float( (self.ds.quan(1.0, unit_y) / self.ds.quan(1.0, unit_x)).in_cgs() ) extentx = (self.xlim - xc)[:2] extenty = (self.ylim - yc)[:2] # extentx/y arrays inherit units from xlim and ylim attributes # and these attributes are always length even for angular and # dimensionless axes so we need to strip out units for consistency if unit_x == "dimensionless": extentx = extentx / extentx.units else: extentx.convert_to_units(unit_x) if unit_y == "dimensionless": extenty = extenty / extenty.units else: extenty.convert_to_units(unit_y) extent = [*extentx, *extenty] image = self.frb.get_image(f) mask = self.frb.get_mask(f) assert mask is None or mask.dtype == bool font_size = self._font_properties.get_size() if f in self.plots.keys(): pnh = self.plots[f].norm_handler cbh = self.plots[f].colorbar_handler else: pnh, cbh = self._get_default_handlers( field=f, default_display_units=image.units ) if pnh.display_units != image.units: equivalency, equivalency_kwargs = self._equivalencies[f] image.convert_to_units( pnh.display_units, equivalency, **equivalency_kwargs ) fig = None axes = None cax = None draw_axes = True draw_frame = None if f in self.plots: draw_axes = self.plots[f]._draw_axes draw_frame = self.plots[f]._draw_frame if self.plots[f].figure is not None: fig = self.plots[f].figure axes = self.plots[f].axes cax = self.plots[f].cax # This is for splatting particle positions with a single # color instead of a colormap if self._splat_color is not None: # make image a rgba array, using the splat color greyscale_image = self.frb[f] ia = np.zeros((greyscale_image.shape[0], greyscale_image.shape[1], 4)) ia[:, :, 3] = 0.0 # set alpha to 0.0 locs = greyscale_image > 0.0 to_rgba = matplotlib.colors.colorConverter.to_rgba color_tuple = to_rgba(self._splat_color) ia[locs] = color_tuple ia = ImageArray(ia) else: ia = image swap_axes = self._has_swapped_axes aspect = self.aspect if swap_axes: extent = _swap_axes_extents(extent) ia = ia.transpose() aspect = 1.0 / aspect # aspect ends up passed to imshow(aspect=aspect) self.plots[f] = WindowPlotMPL( ia, extent, self.figure_size, font_size, aspect, fig, axes, cax, self._projection, self._transform, norm_handler=pnh, colorbar_handler=cbh, alpha=mask.astype("float64") if mask is not None else None, ) axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y) if self.oblique: labels = [ r"$\rm{Image\ x" + axes_unit_labels[0] + "}$", r"$\rm{Image\ y" + axes_unit_labels[1] + "}$", ] else: coordinates = self.ds.coordinates axis_names = coordinates.image_axis_name[axis_index] xax = coordinates.x_axis[axis_index] yax = coordinates.y_axis[axis_index] if hasattr(coordinates, "axis_default_unit_name"): axes_unit_labels = [ coordinates.axis_default_unit_name[xax], coordinates.axis_default_unit_name[yax], ] labels = [ r"$\rm{" + axis_names[0] + axes_unit_labels[0] + r"}$", r"$\rm{" + axis_names[1] + axes_unit_labels[1] + r"}$", ] if hasattr(coordinates, "axis_field"): if xax in coordinates.axis_field: xmin, xmax = coordinates.axis_field[xax]( 0, self.xlim, self.ylim ) else: xmin, xmax = (float(x) for x in extentx) if yax in coordinates.axis_field: ymin, ymax = coordinates.axis_field[yax]( 1, self.xlim, self.ylim ) else: ymin, ymax = (float(y) for y in extenty) new_extent = (xmin, xmax, ymin, ymax) if swap_axes: new_extent = _swap_axes_extents(new_extent) self.plots[f].image.set_extent(new_extent) self.plots[f].axes.set_aspect("auto") x_label, y_label, colorbar_label = self._get_axes_labels(f) if x_label is not None: labels[0] = x_label if y_label is not None: labels[1] = y_label if swap_axes: labels.reverse() self.plots[f].axes.set_xlabel(labels[0]) self.plots[f].axes.set_ylabel(labels[1]) # Determine the units of the data units = Unit(image.units, registry=self.ds.unit_registry) units = units.latex_representation() if colorbar_label is None: colorbar_label = image.info["label"] if getattr(self, "moment", 1) == 2: colorbar_label = f"{colorbar_label} \\rm{{Standard Deviation}}" if hasattr(self, "projected"): colorbar_label = f"$\\rm{{Projected }}$ {colorbar_label}" if units is not None and units != "": colorbar_label += _get_units_label(units) parser = MathTextParser("Agg") try: parser.parse(colorbar_label) except Exception as err: # unspecified exceptions might be raised from matplotlib via its own dependencies raise YTCannotParseUnitDisplayName(f, colorbar_label, str(err)) from err self.plots[f].cb.set_label(colorbar_label) # x-y axes minorticks if f not in self._minorticks: self._minorticks[f] = True if self._minorticks[f]: self.plots[f].axes.minorticks_on() else: self.plots[f].axes.minorticks_off() if not draw_axes: self.plots[f]._toggle_axes(draw_axes, draw_frame) self._set_font_properties() self.run_callbacks() if self._flip_horizontal or self._flip_vertical: # some callbacks (e.g., streamlines) fail when applied to a # flipped axis, so flip only at the end. for f in field_list: if self._flip_horizontal: ax = self.plots[f].axes ax.invert_xaxis() if self._flip_vertical: ax = self.plots[f].axes ax.invert_yaxis() self._plot_valid = True def setup_callbacks(self): issue_deprecation_warning( "The PWViewer.setup_callbacks method is a no-op.", since="4.1", stacklevel=3, ) @invalidate_plot def clear_annotations(self, index: int | None = None): """ Clear callbacks from the plot. If index is not set, clear all callbacks. If index is set, clear that index (ie 0 is the first one created, 1 is the 2nd one created, -1 is the last one created, etc.) """ if index is None: self._callbacks.clear() else: self._callbacks.pop(index) return self def list_annotations(self): """ List the current callbacks for the plot, along with their index. This index can be used with `clear_annotations` to remove a callback from the current plot. """ for i, cb in enumerate(self._callbacks): print(i, cb) def run_callbacks(self): for f in self.fields: keys = self.frb.keys() for callback in self._callbacks: # need to pass _swap_axes and adjust all the callbacks cbw = CallbackWrapper( self, self.plots[f], self.frb, f, self._font_properties, self._font_color, ) try: callback(cbw) except (NotImplementedError, YTDataTypeUnsupported): raise except Exception as e: raise YTPlotCallbackError(callback._type_name) from e for key in self.frb.keys(): if key not in keys: del self.frb[key] def export_to_mpl_figure( self, nrows_ncols, axes_pad=1.0, label_mode="L", cbar_location="right", cbar_size="5%", cbar_mode="each", cbar_pad="0%", ): r""" Creates a matplotlib figure object with the specified axes arrangement, nrows_ncols, and maps the underlying figures to the matplotlib axes. Note that all of these parameters are fed directly to the matplotlib ImageGrid class to create the new figure layout. Parameters ---------- nrows_ncols : tuple the number of rows and columns of the axis grid (e.g., nrows_ncols=(2,2,)) axes_pad : float padding between axes in inches label_mode : one of "L", "1", "all" arrangement of axes that are labeled cbar_location : one of "left", "right", "bottom", "top" where to place the colorbar cbar_size : string (percentage) scaling of the colorbar (e.g., "5%") cbar_mode : one of "each", "single", "edge", None how to represent the colorbar cbar_pad : string (percentage) padding between the axis and colorbar (e.g. "5%") Returns ------- The return is a matplotlib figure object. Examples -------- >>> import yt >>> ds = yt.load_sample("IsolatedGalaxy") >>> fields = ["density", "velocity_x", "velocity_y", "velocity_magnitude"] >>> p = yt.SlicePlot(ds, "z", fields) >>> p.set_log("velocity_x", False) >>> p.set_log("velocity_y", False) >>> fig = p.export_to_mpl_figure((2, 2)) >>> fig.tight_layout() >>> fig.savefig("test.png") """ import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import ImageGrid fig = plt.figure() grid = ImageGrid( fig, 111, nrows_ncols=nrows_ncols, axes_pad=axes_pad, label_mode=label_mode, cbar_location=cbar_location, cbar_size=cbar_size, cbar_mode=cbar_mode, cbar_pad=cbar_pad, ) fields = self.fields if len(fields) > len(grid): raise IndexError("not enough axes for the number of fields") for i, f in enumerate(self.fields): plot = self.plots[f] plot.figure = fig plot.axes = grid[i].axes plot.cax = grid.cbar_axes[i] self._setup_plots() return fig class NormalPlot: """This is the abstraction for SlicePlot and ProjectionPlot, where we define the common sanitizing mechanism for user input (normal direction). It is implemented as a mixin class. """ @staticmethod def sanitize_normal_vector(ds, normal) -> str | np.ndarray: """Return the name of a cartesian axis whener possible, or a 3-element 1D ndarray of float64 in any other valid case. Fail with a descriptive error message otherwise. """ axis_names = ds.coordinates.axis_order if isinstance(normal, str): if normal not in axis_names: names_str = ", ".join(f"'{name}'" for name in axis_names) raise ValueError( f"'{normal}' is not a valid axis name. Expected one of {names_str}." ) return normal if isinstance(normal, (int, np.integer)): if normal not in (0, 1, 2): raise ValueError( f"{normal} is not a valid axis identifier. Expected either 0, 1, or 2." ) return axis_names[normal] if not is_sequence(normal): raise TypeError( f"{normal} is not a valid normal vector identifier. " "Expected a string, integer or sequence of 3 floats." ) if len(normal) != 3: raise ValueError( f"{normal} with length {len(normal)} is not a valid normal vector. " "Expected a 3-element sequence." ) try: retv = np.array(normal, dtype="float64") if retv.shape != (3,): raise ValueError(f"{normal} is incorrectly shaped.") except ValueError as exc: raise TypeError(f"{normal} is not a valid normal vector.") from exc nonzero_idx = np.nonzero(retv)[0] if len(nonzero_idx) == 0: raise ValueError(f"A null vector {normal} isn't a valid normal vector.") if len(nonzero_idx) == 1: return axis_names[nonzero_idx[0]] return retv class SlicePlot(NormalPlot): r""" A dispatch class for :class:`yt.visualization.plot_window.AxisAlignedSlicePlot` and :class:`yt.visualization.plot_window.OffAxisSlicePlot` objects. This essentially allows for a single entry point to both types of slice plots, the distinction being determined by the specified normal vector to the projection. The returned plot object can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : int, str, or 3-element sequence of floats This specifies the normal vector to the slice. Valid int values are 0, 1 and 2. Corresponding str values depend on the geometry of the dataset and are generally given by `ds.coordinates.axis_order`. E.g. in cartesian they are 'x', 'y' and 'z'. An arbitrary normal vector may be specified as a 3-element sequence of floats. This returns a :class:`OffAxisSlicePlot` object or a :class:`AxisAlignedSlicePlot` object, depending on whether the requested normal directions corresponds to a natural axis of the dataset's geometry. fields : a (or a list of) 2-tuple of strings (ftype, fname) The name of the field(s) to be plotted. The following are nominally keyword arguments passed onto the respective slice plot objects generated by this function. Keyword Arguments ----------------- center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. axes_unit : string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. origin : string or length 1, 2, or 3 sequence. The location of the origin of the plot coordinate system for `AxisAlignedSlicePlot` object; for `OffAxisSlicePlot` objects this parameter is discarded. This is typically represented by a '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, the whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. In the event that none of these options place the origin in a desired location, a sequence of tuples and a string specifying the coordinate space can be given. If plain numeric types are input, units of `code_length` are assumed. Further examples: =============================================== =============================== format example =============================================== =============================== '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ((yloc, '{unit}'), (xloc, '{unit}'), '{space}') ((0, 'm'), (.4, 'm'), 'window') (xloc, yloc, '{space}') (0.23, 0.5, 'domain') =============================================== =============================== north_vector : a sequence of floats A vector defining the 'up' direction in the `OffAxisSlicePlot`; not used in `AxisAlignedSlicePlot`. This option sets the orientation of the slicing plane. If not set, an arbitrary grid-aligned north-vector is chosen. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source : YTSelectionContainer Object Object to be used for data selection. Defaults to a region covering the entire simulation. swap_axes : bool Raises ------ ValueError or TypeError If `normal` cannot be interpreted as a valid normal direction. Examples -------- >>> from yt import load >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> slc = SlicePlot(ds, "x", ("gas", "density"), center=[0.2, 0.3, 0.4]) >>> slc = SlicePlot( ... ds, [0.4, 0.2, -0.1], ("gas", "pressure"), north_vector=[0.2, -0.3, 0.1] ... ) """ # ignoring type check here, because mypy doesn't allow __new__ methods to # return instances of subclasses. The design we use here is however based # on the pathlib.Path class from the standard library # https://github.com/python/mypy/issues/1020 def __new__( # type: ignore cls, ds, normal, fields, *args, **kwargs ) -> Union["AxisAlignedSlicePlot", "OffAxisSlicePlot"]: if cls is SlicePlot: normal = cls.sanitize_normal_vector(ds, normal) if isinstance(normal, str): cls = AxisAlignedSlicePlot else: cls = OffAxisSlicePlot self = object.__new__(cls) return self # type: ignore [return-value] class ProjectionPlot(NormalPlot): r""" A dispatch class for :class:`yt.visualization.plot_window.AxisAlignedProjectionPlot` and :class:`yt.visualization.plot_window.OffAxisProjectionPlot` objects. This essentially allows for a single entry point to both types of projection plots, the distinction being determined by the specified normal vector to the slice. The returned plot object can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : int, str, or 3-element sequence of floats This specifies the normal vector to the projection. Valid int values are 0, 1 and 2. Corresponding str values depend on the geometry of the dataset and are generally given by `ds.coordinates.axis_order`. E.g. in cartesian they are 'x', 'y' and 'z'. An arbitrary normal vector may be specified as a 3-element sequence of floats. This function will return a :class:`OffAxisProjectionPlot` object or a :class:`AxisAlignedProjectionPlot` object, depending on whether the requested normal directions corresponds to a natural axis of the dataset's geometry. fields : a (or a list of) 2-tuple of strings (ftype, fname) The name of the field(s) to be plotted. Any additional positional and keyword arguments are passed down to the appropriate return class. See :class:`yt.visualization.plot_window.AxisAlignedProjectionPlot` and :class:`yt.visualization.plot_window.OffAxisProjectionPlot`. Raises ------ ValueError or TypeError If `normal` cannot be interpreted as a valid normal direction. """ # ignoring type check here, because mypy doesn't allow __new__ methods to # return instances of subclasses. The design we use here is however based # on the pathlib.Path class from the standard library # https://github.com/python/mypy/issues/1020 def __new__( # type: ignore cls, ds, normal, fields, *args, **kwargs ) -> Union["AxisAlignedProjectionPlot", "OffAxisProjectionPlot"]: if cls is ProjectionPlot: normal = cls.sanitize_normal_vector(ds, normal) if isinstance(normal, str): cls = AxisAlignedProjectionPlot else: cls = OffAxisProjectionPlot self = object.__new__(cls) return self # type: ignore [return-value] class AxisAlignedSlicePlot(SlicePlot, PWViewerMPL): r"""Creates a slice plot from a dataset Given a ds object, an axis to slice along, and a field name string, this will return a PWViewerMPL object containing the plot. The plot can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : `Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : int or one of 'x', 'y', 'z' An int corresponding to the axis to slice along (0=x, 1=y, 2=z) or the axis name itself fields : string The name of the field(s) to be plotted. center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. origin : string or length 1, 2, or 3 sequence. The location of the origin of the plot coordinate system. This is typically represented by a '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. In the event that none of these options place the origin in a desired location, a sequence of tuples and a string specifying the coordinate space can be given. If plain numeric types are input, units of `code_length` are assumed. Further examples: =============================================== =============================== format example =============================================== =============================== '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ((yloc, '{unit}'), (xloc, '{unit}'), '{space}') ((0, 'm'), (.4, 'm'), 'window') (xloc, yloc, '{space}') (0.23, 0.5, 'domain') =============================================== =============================== axes_unit : string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: YTSelectionContainer object Object to be used for data selection. Defaults to ds.all_data(), a region covering the full domain buff_size: length 2 sequence Size of the buffer to use for the image, i.e. the number of resolution elements used. Effectively sets a resolution limit to the image if buff_size is smaller than the finest gridding. Examples -------- This will save an image in the file 'sliceplot_Density.png' >>> from yt import load >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> p = SlicePlot(ds, 2, "density", "c", (20, "kpc")) >>> p.save("sliceplot") """ _plot_type = "Slice" _frb_generator = FixedResolutionBuffer def __init__( self, ds, normal, fields, center="center", width=None, axes_unit=None, origin="center-window", fontsize=18, field_parameters=None, window_size=8.0, aspect=None, data_source=None, buff_size=(800, 800), *, north_vector=None, ): if north_vector is not None: # this kwarg exists only for symmetry reasons with OffAxisSlicePlot mylog.warning( "Ignoring 'north_vector' keyword as it is ill-defined for " "an AxisAlignedSlicePlot object." ) del north_vector normal = self.sanitize_normal_vector(ds, normal) # this will handle time series data and controllers axis = fix_axis(normal, ds) # print('center at SlicePlot init: ', center) # print('current domain left edge: ', ds.domain_left_edge) (bounds, center, display_center) = get_window_parameters( axis, center, width, ds ) # print('center after get_window_parameters: ', center) if field_parameters is None: field_parameters = {} if isinstance(ds, YTSpatialPlotDataset): slc = ds.all_data() slc.axis = axis if slc.axis != ds.parameters["axis"]: raise RuntimeError(f"Original slice axis is {ds.parameters['axis']}.") else: slc = ds.slice( axis, center[axis], field_parameters=field_parameters, center=center, data_source=data_source, ) slc.get_data(fields) validate_mesh_fields(slc, fields) PWViewerMPL.__init__( self, slc, bounds, origin=origin, fontsize=fontsize, fields=fields, window_size=window_size, aspect=aspect, buff_size=buff_size, geometry=ds.geometry, ) if axes_unit is None: axes_unit = get_axes_unit(width, ds) self.set_axes_unit(axes_unit) class AxisAlignedProjectionPlot(ProjectionPlot, PWViewerMPL): r"""Creates a projection plot from a dataset Given a ds object, an axis to project along, and a field name string, this will return a PWViewerMPL object containing the plot. The plot can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : `Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : int or one of 'x', 'y', 'z' An int corresponding to the axis to slice along (0=x, 1=y, 2=z) or the axis name itself fields : string The name of the field(s) to be plotted. center : 'center', 'c', 'left', 'l', 'right', 'r', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. The domain edges along the selected *axis* can be selected with 'left'/'l' and 'right'/'r' respectively. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. axes_unit : string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. origin : string or length 1, 2, or 3 sequence. The location of the origin of the plot coordinate system. This is typically represented by a '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. In the event that none of these options place the origin in a desired location, a sequence of tuples and a string specifying the coordinate space can be given. If plain numeric types are input, units of `code_length` are assumed. Further examples: =============================================== =============================== format example =============================================== =============================== '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ((yloc, '{unit}'), (xloc, '{unit}'), '{space}') ((0, 'm'), (.4, 'm'), 'window') (xloc, yloc, '{space}') (0.23, 0.5, 'domain') =============================================== =============================== data_source : YTSelectionContainer Object Object to be used for data selection. Defaults to a region covering the entire simulation. weight_field : string The name of the weighting field. Set to None for no weight. max_level: int The maximum level to project to. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. method : string The method of projection. Valid methods are: "integrate" with no weight_field specified : integrate the requested field along the line of sight. "integrate" with a weight_field specified : weight the requested field by the weighting field and integrate along the line of sight. "max" : pick out the maximum value of the field in the line of sight. "min" : pick out the minimum value of the field in the line of sight. "sum" : This method is the same as integrate, except that it does not multiply by a path length when performing the integration, and is just a straight summation of the field along the given axis. WARNING: This should only be used for uniform resolution grid datasets, as other datasets may result in unphysical images. window_size : float The size of the window in inches. Set to 8 by default. aspect : float The aspect ratio of the plot. Set to None for 1. field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: YTSelectionContainer object Object to be used for data selection. Defaults to ds.all_data(), a region covering the full domain buff_size: length 2 sequence Size of the buffer to use for the image, i.e. the number of resolution elements used. Effectively sets a resolution limit to the image if buff_size is smaller than the finest gridding. moment : integer, optional for a weighted projection, moment = 1 (the default) corresponds to a weighted average. moment = 2 corresponds to a weighted standard deviation. Examples -------- Create a projection plot with a width of 20 kiloparsecs centered on the center of the simulation box: >>> from yt import load >>> ds = load("IsolateGalaxygalaxy0030/galaxy0030") >>> p = AxisAlignedProjectionPlot(ds, "z", ("gas", "density"), width=(20, "kpc")) """ _plot_type = "Projection" _frb_generator = FixedResolutionBuffer def __init__( self, ds, normal, fields, center="center", width=None, axes_unit=None, weight_field=None, max_level=None, origin="center-window", fontsize=18, field_parameters=None, data_source=None, method="integrate", window_size=8.0, buff_size=(800, 800), aspect=None, *, moment=1, ): if method == "mip": issue_deprecation_warning( "'mip' method is a deprecated alias for 'max'. " "Please use method='max' directly.", since="4.1", stacklevel=3, ) method = "max" normal = self.sanitize_normal_vector(ds, normal) axis = fix_axis(normal, ds) # If a non-weighted integral projection, assure field-label reflects that if weight_field is None and method == "integrate": self.projected = True (bounds, center, display_center) = get_window_parameters( axis, center, width, ds ) if field_parameters is None: field_parameters = {} # We don't use the plot's data source for validation like in the other # plotting classes to avoid an exception test_data_source = ds.all_data() validate_mesh_fields(test_data_source, fields) if isinstance(ds, YTSpatialPlotDataset): proj = ds.all_data() proj.axis = axis if proj.axis != ds.parameters["axis"]: raise RuntimeError( f"Original projection axis is {ds.parameters['axis']}." ) if weight_field is not None: proj.weight_field = proj._determine_fields(weight_field)[0] else: proj.weight_field = weight_field proj.center = center else: proj = ds.proj( fields, axis, weight_field=weight_field, center=center, data_source=data_source, field_parameters=field_parameters, method=method, max_level=max_level, moment=moment, ) self.moment = moment PWViewerMPL.__init__( self, proj, bounds, fields=fields, origin=origin, fontsize=fontsize, window_size=window_size, aspect=aspect, buff_size=buff_size, geometry=ds.geometry, ) if axes_unit is None: axes_unit = get_axes_unit(width, ds) self.set_axes_unit(axes_unit) class OffAxisSlicePlot(SlicePlot, PWViewerMPL): r"""Creates an off axis slice plot from a dataset Given a ds object, a normal vector defining a slicing plane, and a field name string, this will return a PWViewerMPL object containing the plot. The plot can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : a sequence of floats The vector normal to the slicing plane. fields : string The name of the field(s) to be plotted. center : 'center', 'c' id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. axes_unit : string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. north_vector : a sequence of floats A vector defining the 'up' direction in the plot. This option sets the orientation of the slicing plane. If not set, an arbitrary grid-aligned north-vector is chosen. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source : YTSelectionContainer Object Object to be used for data selection. Defaults ds.all_data(), a region covering the full domain. buff_size: length 2 sequence Size of the buffer to use for the image, i.e. the number of resolution elements used. Effectively sets a resolution limit to the image if buff_size is smaller than the finest gridding. """ _plot_type = "OffAxisSlice" _frb_generator = FixedResolutionBuffer _supported_geometries = ("cartesian", "spectral_cube") def __init__( self, ds, normal, fields, center="center", width=None, axes_unit=None, north_vector=None, fontsize=18, field_parameters=None, data_source=None, buff_size=(800, 800), *, origin=None, ): if origin is not None: # this kwarg exists only for symmetry reasons with AxisAlignedSlicePlot # in OffAxisSlicePlot, the origin is hardcoded mylog.warning( "Ignoring 'origin' keyword as it is ill-defined for " "an OffAxisSlicePlot object." ) del origin if ds.geometry not in self._supported_geometries: raise NotImplementedError( f"off-axis slices are not supported for {ds.geometry!r} geometry\n" f"currently supported geometries: {self._supported_geometries!r}" ) # bounds are in cutting plane coordinates, centered on 0: # [xmin, xmax, ymin, ymax]. Can derive width/height back # from these. unit is code_length (bounds, center_rot) = get_oblique_window_parameters(normal, center, width, ds) if field_parameters is None: field_parameters = {} if isinstance(ds, YTSpatialPlotDataset): cutting = ds.all_data() cutting.axis = None cutting._inv_mat = ds.parameters["_inv_mat"] else: cutting = ds.cutting( normal, center, north_vector=north_vector, field_parameters=field_parameters, data_source=data_source, ) cutting.get_data(fields) validate_mesh_fields(cutting, fields) # Hard-coding the origin keyword since the other two options # aren't well-defined for off-axis data objects PWViewerMPL.__init__( self, cutting, bounds, fields=fields, origin="center-window", periodic=False, oblique=True, fontsize=fontsize, buff_size=buff_size, ) if axes_unit is None: axes_unit = get_axes_unit(width, ds) self.set_axes_unit(axes_unit) class OffAxisProjectionDummyDataSource: _type_name = "proj" _key_fields: list[str] = [] def __init__( self, center, ds, normal_vector, width, fields, interpolated, weight=None, volume=None, no_ghost=False, le=None, re=None, north_vector=None, depth=None, method="integrate", data_source=None, *, moment=1, ): validate_moment(moment, weight) self.center = center self.ds = ds self.axis = None # always true for oblique data objects self.normal_vector = normal_vector self.width = width self.depth = depth if data_source is None: self.dd = ds.all_data() else: self.dd = data_source fields = self.dd._determine_fields(fields) self.fields = fields self.interpolated = interpolated if weight is not None: weight = self.dd._determine_fields(weight)[0] self.weight_field = weight self.volume = volume self.no_ghost = no_ghost self.le = le self.re = re self.north_vector = north_vector self.method = method self.orienter = Orientation(normal_vector, north_vector=north_vector) self.moment = moment def _determine_fields(self, *args): return self.dd._determine_fields(*args) class OffAxisProjectionPlot(ProjectionPlot, PWViewerMPL): r"""Creates an off axis projection plot from a dataset Given a ds object, a normal vector to project along, and a field name string, this will return a PWViewerMPL object containing the plot. The plot can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation output to be plotted. normal : a sequence of floats The vector normal to the slicing plane. fields : string The name of the field(s) to be plotted. center : 'center', 'c', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. depth : A tuple or a float A tuple containing the depth to project through and the string key of the unit: (width, 'unit'). If set to a float, code units are assumed. In not set, then a depth equal to the diagonal of the domain width plus a small margin will be used. weight_field : string The name of the weighting field. Set to None for no weight. max_level: int The maximum level to project to. axes_unit : string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. north_vector : a sequence of floats A vector defining the 'up' direction in the plot. This option sets the orientation of the slicing plane. If not set, an arbitrary grid-aligned north-vector is chosen. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. method : string The method of projection. Valid methods are: "integrate" with no weight_field specified : integrate the requested field along the line of sight. "integrate" with a weight_field specified : weight the requested field by the weighting field and integrate along the line of sight. "sum" : This method is the same as integrate, except that it does not multiply by a path length when performing the integration, and is just a straight summation of the field along the given axis. WARNING: This should only be used for uniform resolution grid datasets, as other datasets may result in unphysical images. moment : integer, optional for a weighted projection, moment = 1 (the default) corresponds to a weighted average. moment = 2 corresponds to a weighted standard deviation. data_source: YTSelectionContainer object Object to be used for data selection. Defaults to ds.all_data(), a region covering the full domain buff_size: length 2 sequence Size of the buffer to use for the image, i.e. the number of resolution elements used. Effectively sets a resolution limit to the image if buff_size is smaller than the finest gridding. """ _plot_type = "OffAxisProjection" _frb_generator = OffAxisProjectionFixedResolutionBuffer _supported_geometries = ("cartesian", "spectral_cube") def __init__( self, ds, normal, fields, center="center", width=None, depth=None, axes_unit=None, weight_field=None, max_level=None, north_vector=None, volume=None, no_ghost=False, le=None, re=None, interpolated=False, fontsize=18, method="integrate", moment=1, data_source=None, buff_size=(800, 800), ): if ds.geometry not in self._supported_geometries: raise NotImplementedError( "off-axis slices are not supported" f" for {ds.geometry!r} geometry\n" "currently supported geometries:" f" {self._supported_geometries!r}" ) if depth is None: # off-axis projection, depth not specified # -> set 'large enough' depth using half the box diagonal + margin depth = np.linalg.norm(ds.domain_width.in_units("code_length")) * 1.0001 depth = ds.coordinates.sanitize_depth(depth)[0] # center_rot normalizes the center to (0,0), # units match bounds # for SPH data, we want to input the original center # the cython backend handles centering to this point and # rotation. # get3bounds gets a depth 0.5 * diagonal + margin in the # depth=None case. (bounds, center_rot) = get_oblique_window_parameters( normal, center, width, ds, depth=depth, ) # will probably fail if you try to project an SPH and non-SPH # field in a single call # checks for SPH fields copied from the # _ortho_pixelize method in cartesian_coordinates.py ## data_source might be None here ## (OffAxisProjectionDummyDataSource gets used later) if data_source is None: data_source = ds.all_data() field = data_source._determine_fields(fields)[0] finfo = data_source.ds.field_info[field] is_sph_field = finfo.is_sph_field particle_datasets = (ParticleDataset, StreamParticlesDataset) dom_width = data_source.ds.domain_width cubic_domain = dom_width.max() == dom_width.min() if (isinstance(data_source.ds, particle_datasets) and is_sph_field) or ( isinstance(data_source.ds.index, OctreeIndex) and cubic_domain ): center_use = parse_center_array(center, ds=data_source.ds, axis=None) else: center_use = center_rot fields = list(iter_fields(fields))[:] # oap_width = ds.arr( # (bounds[1] - bounds[0], # bounds[3] - bounds[2]) # ) OffAxisProj = OffAxisProjectionDummyDataSource( center_use, ds, normal, width, fields, interpolated, weight=weight_field, volume=volume, no_ghost=no_ghost, le=le, re=re, north_vector=north_vector, depth=depth, method=method, data_source=data_source, moment=moment, ) validate_mesh_fields(OffAxisProj, fields) if max_level is not None: OffAxisProj.dd.max_level = max_level # If a non-weighted, integral projection, assure field label # reflects that if weight_field is None and OffAxisProj.method == "integrate": self.projected = True self.moment = moment # Hard-coding the origin keyword since the other two options # aren't well-defined for off-axis data objects PWViewerMPL.__init__( self, OffAxisProj, bounds, fields=fields, origin="center-window", periodic=False, oblique=True, fontsize=fontsize, buff_size=buff_size, ) if axes_unit is None: axes_unit = get_axes_unit(width, ds) self.set_axes_unit(axes_unit) class WindowPlotMPL(ImagePlotMPL): """A container for a single PlotWindow matplotlib figure and axes""" def __init__( self, data, extent, figure_size, fontsize, aspect, figure, axes, cax, mpl_proj, mpl_transform, *, norm_handler: NormHandler, colorbar_handler: ColorbarHandler, alpha: AlphaT = None, ): self._projection = mpl_proj self._transform = mpl_transform self._setup_layout_constraints(figure_size, fontsize) self._draw_frame = True self._aspect = ((extent[1] - extent[0]) / (extent[3] - extent[2])).in_cgs() self._unit_aspect = aspect # Compute layout self._figure_size = figure_size self._draw_axes = True fontscale = float(fontsize) / self.__class__._default_font_size if fontscale < 1.0: fontscale = np.sqrt(fontscale) if is_sequence(figure_size): self._cb_size = 0.0375 * figure_size[0] else: self._cb_size = 0.0375 * figure_size self._ax_text_size = [1.2 * fontscale, 0.9 * fontscale] self._top_buff_size = 0.30 * fontscale super().__init__( figure=figure, axes=axes, cax=cax, norm_handler=norm_handler, colorbar_handler=colorbar_handler, ) self._init_image(data, extent, aspect, alpha=alpha) def _create_axes(self, axrect): self.axes = self.figure.add_axes(axrect, projection=self._projection) def plot_2d( ds, fields, center="center", width=None, axes_unit=None, origin="center-window", fontsize=18, field_parameters=None, window_size=8.0, aspect=None, data_source=None, ) -> AxisAlignedSlicePlot: r"""Creates a plot of a 2D dataset Given a ds object and a field name string, this will return a PWViewerMPL object containing the plot. The plot can be updated using one of the many helper functions defined in PlotWindow. Parameters ---------- ds : `Dataset` This is the dataset object corresponding to the simulation output to be plotted. fields : string The name of the field(s) to be plotted. center : 'center', 'c', id of a global extremum, or array-like The coordinate of the selection's center. Defaults to the 'center', i.e. center of the domain. Centering on the min or max of a field is supported by passing a tuple such as ('min', ('gas', 'density')) or ('max', ('gas', 'temperature'). A single string may also be used (e.g. "min_density" or "max_temperature"), though it's not as flexible and does not allow to select an exact field/particle type. With this syntax, the first field matching the provided name is selected. 'max' or 'm' can be used as a shortcut for ('max', ('gas', 'density')) 'min' can be used as a shortcut for ('min', ('gas', 'density')) One can also select an exact point as a 3 element coordinate sequence, e.g. [0.5, 0.5, 0] Units can be specified by passing in *center* as a tuple containing a 3-element coordinate sequence and string unit name, e.g. ([0, 0.5, 0.5], "cm"), or by passing in a YTArray. Code units are assumed if unspecified. plot_2d also accepts a coordinate in two dimensions. width : tuple or a float. Width can have four different formats to support windows with variable x and y widths. They are: ================================== ======================= format example ================================== ======================= (float, string) (10,'kpc') ((float, string), (float, string)) ((10,'kpc'),(15,'kpc')) float 0.2 (float, float) (0.2, 0.3) ================================== ======================= For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a window that is 10 kiloparsecs wide along the x axis and 15 kiloparsecs wide along the y axis. In the other two examples, code units are assumed, for example (0.2, 0.3) requests a plot that has an x width of 0.2 and a y width of 0.3 in code units. If units are provided the resulting plot axis labels will use the supplied units. origin : string or length 1, 2, or 3 sequence. The location of the origin of the plot coordinate system. This is typically represented by a '-' separated string or a tuple of strings. In the first index the y-location is given by 'lower', 'upper', or 'center'. The second index is the x-location, given as 'left', 'right', or 'center'. Finally, whether the origin is applied in 'domain' space, plot 'window' space or 'native' simulation coordinate system is given. For example, both 'upper-right-domain' and ['upper', 'right', 'domain'] place the origin in the upper right hand corner of domain space. If x or y are not given, a value is inferred. For instance, 'left-domain' corresponds to the lower-left hand corner of the simulation domain, 'center-domain' corresponds to the center of the simulation domain, or 'center-window' for the center of the plot window. In the event that none of these options place the origin in a desired location, a sequence of tuples and a string specifying the coordinate space can be given. If plain numeric types are input, units of `code_length` are assumed. Further examples: =============================================== =============================== format example =============================================== =============================== '{space}' 'domain' '{xloc}-{space}' 'left-window' '{yloc}-{space}' 'upper-domain' '{yloc}-{xloc}-{space}' 'lower-right-window' ('{space}',) ('window',) ('{xloc}', '{space}') ('right', 'domain') ('{yloc}', '{space}') ('lower', 'window') ('{yloc}', '{xloc}', '{space}') ('lower', 'right', 'window') ((yloc, '{unit}'), (xloc, '{unit}'), '{space}') ((0, 'm'), (.4, 'm'), 'window') (xloc, yloc, '{space}') (0.23, 0.5, 'domain') =============================================== =============================== axes_unit : string The name of the unit for the tick labels on the x and y axes. Defaults to None, which automatically picks an appropriate unit. If axes_unit is '1', 'u', or 'unitary', it will not display the units, and only show the axes name. fontsize : integer The size of the fonts for the axis, colorbar, and tick labels. field_parameters : dictionary A dictionary of field parameters than can be accessed by derived fields. data_source: YTSelectionContainer object Object to be used for data selection. Defaults to ds.all_data(), a region covering the full domain """ if ds.dimensionality != 2: raise RuntimeError("plot_2d only plots 2D datasets!") if ( ds.geometry is Geometry.CARTESIAN or ds.geometry is Geometry.POLAR or ds.geometry is Geometry.SPECTRAL_CUBE ): axis = "z" elif ds.geometry is Geometry.CYLINDRICAL: axis = "theta" elif ds.geometry is Geometry.SPHERICAL: axis = "phi" elif ( ds.geometry is Geometry.GEOGRAPHIC or ds.geometry is Geometry.INTERNAL_GEOGRAPHIC ): raise NotImplementedError( f"plot_2d does not yet support datasets with {ds.geometry} geometries" ) else: assert_never(ds.geometry) # Part of the convenience of plot_2d is to eliminate the use of the # superfluous coordinate, so we do that also with the center argument if not isinstance(center, str) and obj_length(center) == 2: c0_string = isinstance(center[0], str) c1_string = isinstance(center[1], str) if not c0_string and not c1_string: if obj_length(center[0]) == 2 and c1_string: # turning off type checking locally because center arg is hard to type correctly center = ds.arr(center[0], center[1]) # type: ignore [unreachable] elif not isinstance(center, YTArray): center = ds.arr(center, "code_length") center.convert_to_units("code_length") center = ds.arr([center[0], center[1], ds.domain_center[2]]) return AxisAlignedSlicePlot( ds, axis, fields, center=center, width=width, axes_unit=axes_unit, origin=origin, fontsize=fontsize, field_parameters=field_parameters, window_size=window_size, aspect=aspect, data_source=data_source, ) yt-project-yt-f043ac8/yt/visualization/profile_plotter.py000066400000000000000000001445321510711153200240030ustar00rootroot00000000000000import base64 import os from functools import wraps from typing import TYPE_CHECKING, Any import matplotlib import numpy as np from more_itertools.more import always_iterable, unzip from yt._maintenance.ipython_compat import IS_IPYTHON from yt._typing import FieldKey from yt.data_objects.profiles import create_profile, sanitize_field_tuple_keys from yt.data_objects.static_output import Dataset from yt.frontends.ytdata.data_structures import YTProfileDataset from yt.funcs import iter_fields, matplotlib_style_context from yt.utilities.exceptions import YTNotInsideNotebook from yt.visualization._commons import _get_units_label from yt.visualization._handlers import ColorbarHandler, NormHandler from yt.visualization.base_plot_types import ImagePlotMPL, PlotMPL from ..data_objects.selection_objects.data_selection_objects import YTSelectionContainer from ._commons import validate_image_name from .plot_container import ( BaseLinePlot, ImagePlotContainer, invalidate_plot, validate_plot, ) if TYPE_CHECKING: from collections.abc import Iterable from yt._typing import FieldKey def invalidate_profile(f): @wraps(f) def newfunc(*args, **kwargs): rv = f(*args, **kwargs) args[0]._profile_valid = False return rv return newfunc def sanitize_label(labels, nprofiles): labels = list(always_iterable(labels)) or [None] if len(labels) == 1: labels = labels * nprofiles if len(labels) != nprofiles: raise ValueError( f"Number of labels {len(labels)} must match number of profiles {nprofiles}" ) invalid_data = [ (label, type(label)) for label in labels if label is not None and not isinstance(label, str) ] if invalid_data: invalid_labels, types = unzip(invalid_data) raise TypeError( "All labels must be None or a string, " f"received {invalid_labels} with type {types}" ) return labels def data_object_or_all_data(data_source): if isinstance(data_source, Dataset): data_source = data_source.all_data() if not isinstance(data_source, YTSelectionContainer): raise RuntimeError("data_source must be a yt selection data object") return data_source class ProfilePlot(BaseLinePlot): r""" Create a 1d profile plot from a data source or from a list of profile objects. Given a data object (all_data, region, sphere, etc.), an x field, and a y field (or fields), this will create a one-dimensional profile of the average (or total) value of the y field in bins of the x field. This can be used to create profiles from given fields or to plot multiple profiles created from `yt.data_objects.profiles.create_profile`. Parameters ---------- data_source : YTSelectionContainer Object The data object to be profiled, such as all_data, region, or sphere. If a dataset is passed in instead, an all_data data object is generated internally from the dataset. x_field : str The binning field for the profile. y_fields : str or list The field or fields to be profiled. weight_field : str The weight field for calculating weighted averages. If None, the profile values are the sum of the field values within the bin. Otherwise, the values are a weighted average. Default : ("gas", "mass") n_bins : int The number of bins in the profile. Default: 64. accumulation : bool If True, the profile values for a bin N are the cumulative sum of all the values from bin 0 to N. Default: False. fractional : If True the profile values are divided by the sum of all the profile data such that the profile represents a probability distribution function. label : str or list of strings If a string, the label to be put on the line plotted. If a list, this should be a list of labels for each profile to be overplotted. Default: None. plot_spec : dict or list of dicts A dictionary or list of dictionaries containing plot keyword arguments. For example, dict(color="red", linestyle=":"). Default: None. x_log : bool Whether the x_axis should be plotted with a logarithmic scaling (True), or linear scaling (False). Default: True. y_log : dict or bool A dictionary containing field:boolean pairs, setting the logarithmic property for that field. May be overridden after instantiation using set_log A single boolean can be passed to signify all fields should use logarithmic (True) or linear scaling (False). Default: True. Examples -------- This creates profiles of a single dataset. >>> import yt >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") >>> ad = ds.all_data() >>> plot = yt.ProfilePlot( ... ad, ... ("gas", "density"), ... [("gas", "temperature"), ("gas", "velocity_x")], ... weight_field=("gas", "mass"), ... plot_spec=dict(color="red", linestyle="--"), ... ) >>> plot.save() This creates profiles from a time series object. >>> es = yt.load_simulation("AMRCosmology.enzo", "Enzo") >>> es.get_time_series() >>> profiles = [] >>> labels = [] >>> plot_specs = [] >>> for ds in es[-4:]: ... ad = ds.all_data() ... profiles.append( ... create_profile( ... ad, ... [("gas", "density")], ... fields=[("gas", "temperature"), ("gas", "velocity_x")], ... ) ... ) ... labels.append(ds.current_redshift) ... plot_specs.append(dict(linestyle="--", alpha=0.7)) >>> plot = yt.ProfilePlot.from_profiles( ... profiles, labels=labels, plot_specs=plot_specs ... ) >>> plot.save() Use set_line_property to change line properties of one or all profiles. """ _default_figure_size = (10.0, 8.0) _default_font_size = 18.0 x_log = None y_log = None x_title = None y_title = None _plot_valid = False def __init__( self, data_source, x_field, y_fields, weight_field=("gas", "mass"), n_bins=64, accumulation=False, fractional=False, label=None, plot_spec=None, x_log=True, y_log=True, ): data_source = data_object_or_all_data(data_source) y_fields = list(iter_fields(y_fields)) logs = {x_field: bool(x_log)} if isinstance(y_log, bool): y_log = {y_field: y_log for y_field in y_fields} if isinstance(data_source.ds, YTProfileDataset): profiles = [data_source.ds.profile] else: profiles = [ create_profile( data_source, [x_field], n_bins=[n_bins], fields=y_fields, weight_field=weight_field, accumulation=accumulation, fractional=fractional, logs=logs, ) ] if plot_spec is None: plot_spec = [{} for p in profiles] if not isinstance(plot_spec, list): plot_spec = [plot_spec.copy() for p in profiles] ProfilePlot._initialize_instance( self, data_source, profiles, label, plot_spec, y_log ) @classmethod def _initialize_instance( cls, obj, data_source, profiles, labels, plot_specs, y_log, ): obj._plot_title = {} obj._plot_text = {} obj._text_xpos = {} obj._text_ypos = {} obj._text_kwargs = {} super(ProfilePlot, obj).__init__(data_source) obj.profiles = list(always_iterable(profiles)) obj.x_log = None obj.y_log = sanitize_field_tuple_keys(y_log, data_source) or {} obj.y_title = {} obj.x_title = None obj.label = sanitize_label(labels, len(obj.profiles)) if plot_specs is None: plot_specs = [{} for p in obj.profiles] obj.plot_spec = plot_specs obj._xlim = (None, None) obj._setup_plots() obj._plot_valid = False # see https://github.com/yt-project/yt/issues/4489 return obj def _get_axrect(self): return (0.1, 0.1, 0.8, 0.8) @validate_plot def save( self, name: str | None = None, suffix: str | None = None, mpl_kwargs: dict[str, Any] | None = None, ): r""" Saves a 1d profile plot. Parameters ---------- name : str, optional The output file keyword. suffix : string, optional Specify the image type by its suffix. If not specified, the output type will be inferred from the filename. Defaults to '.png'. mpl_kwargs : dict, optional A dict of keyword arguments to be passed to matplotlib. """ if not self._plot_valid: self._setup_plots() # Mypy is hardly convinced that we have a `profiles` attribute # at this stage, so we're lasily going to deactivate it locally unique = set(self.plots.values()) iters: Iterable[tuple[int | FieldKey, PlotMPL]] if len(unique) < len(self.plots): iters = enumerate(sorted(unique)) else: iters = self.plots.items() if name is None: if len(self.profiles) == 1: # type: ignore name = str(self.profiles[0].ds) # type: ignore else: name = "Multi-data" name = validate_image_name(name, suffix) prefix, suffix = os.path.splitext(name) xfn = self.profiles[0].x_field # type: ignore if isinstance(xfn, tuple): xfn = xfn[1] names = [] for uid, plot in iters: if isinstance(uid, tuple): uid = uid[1] # type: ignore uid_name = f"{prefix}_1d-Profile_{xfn}_{uid}{suffix}" names.append(uid_name) with matplotlib_style_context(): plot.save(uid_name, mpl_kwargs=mpl_kwargs) return names @validate_plot def show(self): r"""This will send any existing plots to the IPython notebook. If yt is being run from within an IPython session, and it is able to determine this, this function will send any existing plots to the notebook for display. If yt can't determine if it's inside an IPython session, it will raise YTNotInsideNotebook. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> pp = ProfilePlot(ds.all_data(), ("gas", "density"), ("gas", "temperature")) >>> pp.show() """ if IS_IPYTHON: from IPython.display import display display(self) else: raise YTNotInsideNotebook @validate_plot def _repr_html_(self): """Return an html representation of the plot object. Will display as a png for each WindowPlotMPL instance in self.plots""" ret = "" unique = set(self.plots.values()) if len(unique) < len(self.plots): iters = sorted(unique) else: iters = self.plots.values() for plot in iters: with matplotlib_style_context(): img = plot._repr_png_() img = base64.b64encode(img).decode() ret += ( r'
' ) return ret def _setup_plots(self): if self._plot_valid: return for f, p in self.plots.items(): p.axes.cla() if f in self._plot_text: p.axes.text( self._text_xpos[f], self._text_ypos[f], self._plot_text[f], fontproperties=self._font_properties, **self._text_kwargs[f], ) self._set_font_properties() for i, profile in enumerate(self.profiles): for field, field_data in profile.items(): plot = self._get_plot_instance(field) plot.axes.plot( np.array(profile.x), np.array(field_data), label=self.label[i], **self.plot_spec[i], ) for profile in self.profiles: for fname in profile.keys(): axes = self.plots[fname].axes xscale, yscale = self._get_field_log(fname, profile) xtitle, ytitle = self._get_field_title(fname, profile) axes.set_xscale(xscale) axes.set_yscale(yscale) axes.set_ylabel(ytitle) axes.set_xlabel(xtitle) pnh = self.plots[fname].norm_handler axes.set_ylim(pnh.vmin, pnh.vmax) axes.set_xlim(*self._xlim) if fname in self._plot_title: axes.set_title(self._plot_title[fname]) if any(self.label): axes.legend(loc="best") self._set_font_properties() self._plot_valid = True @classmethod def from_profiles(cls, profiles, labels=None, plot_specs=None, y_log=None): r""" Instantiate a ProfilePlot object from a list of profiles created with :func:`~yt.data_objects.profiles.create_profile`. Parameters ---------- profiles : a profile or list of profiles A single profile or list of profile objects created with :func:`~yt.data_objects.profiles.create_profile`. labels : list of strings A list of labels for each profile to be overplotted. Default: None. plot_specs : list of dicts A list of dictionaries containing plot keyword arguments. For example, [dict(color="red", linestyle=":")]. Default: None. Examples -------- >>> from yt import load_simulation >>> es = load_simulation("AMRCosmology.enzo", "Enzo") >>> es.get_time_series() >>> profiles = [] >>> labels = [] >>> plot_specs = [] >>> for ds in es[-4:]: ... ad = ds.all_data() ... profiles.append( ... create_profile( ... ad, ... [("gas", "density")], ... fields=[("gas", "temperature"), ("gas", "velocity_x")], ... ) ... ) ... labels.append(ds.current_redshift) ... plot_specs.append(dict(linestyle="--", alpha=0.7)) >>> plot = ProfilePlot.from_profiles( ... profiles, labels=labels, plot_specs=plot_specs ... ) >>> plot.save() """ if labels is not None and len(profiles) != len(labels): raise RuntimeError("Profiles list and labels list must be the same size.") if plot_specs is not None and len(plot_specs) != len(profiles): raise RuntimeError( "Profiles list and plot_specs list must be the same size." ) obj = cls.__new__(cls) profiles = list(always_iterable(profiles)) return cls._initialize_instance( obj, profiles[0].data_source, profiles, labels, plot_specs, y_log ) @invalidate_plot def set_line_property(self, property, value, index=None): r""" Set properties for one or all lines to be plotted. Parameters ---------- property : str The line property to be set. value : str, int, float The value to set for the line property. index : int The index of the profile in the list of profiles to be changed. If None, change all plotted lines. Default : None. Examples -------- Change all the lines in a plot plot.set_line_property("linestyle", "-") Change a single line. plot.set_line_property("linewidth", 4, index=0) """ if index is None: specs = self.plot_spec else: specs = [self.plot_spec[index]] for spec in specs: spec[property] = value return self @invalidate_plot def set_log(self, field, log): """set a field to log or linear. Parameters ---------- field : string the field to set a transform log : boolean Log on/off. """ if field == "all": self.x_log = log for field in list(self.profiles[0].field_data.keys()): self.y_log[field] = log else: (field,) = self.profiles[0].data_source._determine_fields([field]) if field == self.profiles[0].x_field: self.x_log = log elif field in self.profiles[0].field_data: self.y_log[field] = log else: raise KeyError(f"Field {field} not in profile plot!") return self @invalidate_plot def set_ylabel(self, field, label): """Sets a new ylabel for the specified fields Parameters ---------- field : string The name of the field that is to be changed. label : string The label to be placed on the y-axis """ if field == "all": for field in self.profiles[0].field_data: self.y_title[field] = label else: (field,) = self.profiles[0].data_source._determine_fields([field]) if field in self.profiles[0].field_data: self.y_title[field] = label else: raise KeyError(f"Field {field} not in profile plot!") return self @invalidate_plot def set_xlabel(self, label): """Sets a new xlabel for all profiles Parameters ---------- label : string The label to be placed on the x-axis """ self.x_title = label return self @invalidate_plot def set_unit(self, field, unit): """Sets a new unit for the requested field Parameters ---------- field : string The name of the field that is to be changed. unit : string or Unit object The name of the new unit. """ fd = self.profiles[0].data_source._determine_fields(field)[0] for profile in self.profiles: if fd == profile.x_field: profile.set_x_unit(unit) elif fd[1] in self.profiles[0].field_map: profile.set_field_unit(field, unit) else: raise KeyError(f"Field {field} not in profile plot!") return self @invalidate_plot def set_xlim(self, xmin=None, xmax=None): """Sets the limits of the bin field Parameters ---------- xmin : float or None The new x minimum. Defaults to None, which leaves the xmin unchanged. xmax : float or None The new x maximum. Defaults to None, which leaves the xmax unchanged. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> pp = yt.ProfilePlot( ... ds.all_data(), ("gas", "density"), ("gas", "temperature") ... ) >>> pp.set_xlim(1e-29, 1e-24) >>> pp.save() """ self._xlim = (xmin, xmax) for i, p in enumerate(self.profiles): if xmin is None: xmi = p.x_bins.min() else: xmi = xmin if xmax is None: xma = p.x_bins.max() else: xma = xmax extrema = {p.x_field: ((xmi, str(p.x.units)), (xma, str(p.x.units)))} units = {p.x_field: str(p.x.units)} if self.x_log is None: logs = None else: logs = {p.x_field: self.x_log} for field in p.field_map.values(): units[field] = str(p.field_data[field].units) self.profiles[i] = create_profile( p.data_source, p.x_field, n_bins=len(p.x_bins) - 1, fields=list(p.field_map.values()), weight_field=p.weight_field, accumulation=p.accumulation, fractional=p.fractional, logs=logs, extrema=extrema, units=units, ) return self @invalidate_plot def set_ylim(self, field, ymin=None, ymax=None): """Sets the plot limits for the specified field we are binning. Parameters ---------- field : string or field tuple The field that we want to adjust the plot limits for. ymin : float or None The new y minimum. Defaults to None, which leaves the ymin unchanged. ymax : float or None The new y maximum. Defaults to None, which leaves the ymax unchanged. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> pp = yt.ProfilePlot( ... ds.all_data(), ... ("gas", "density"), ... [("gas", "temperature"), ("gas", "velocity_x")], ... ) >>> pp.set_ylim(("gas", "temperature"), 1e4, 1e6) >>> pp.save() """ fields = list(self.plots.keys()) if field == "all" else field for profile in self.profiles: for field in profile.data_source._determine_fields(fields): if field in profile.field_map: field = profile.field_map[field] pnh = self.plots[field].norm_handler pnh.vmin = ymin pnh.vmax = ymax # Continue on to the next profile. break return self def _set_font_properties(self): for f in self.plots: self.plots[f]._set_font_properties(self._font_properties, self._font_color) def _get_field_log(self, field_y, profile): yfi = profile.field_info[field_y] if self.x_log is None: x_log = profile.x_log else: x_log = self.x_log y_log = self.y_log.get(field_y, yfi.take_log) scales = {True: "log", False: "linear"} return scales[x_log], scales[y_log] def _get_field_label(self, field, field_info, field_unit, fractional=False): field_unit = field_unit.latex_representation() field_name = field_info.display_name if isinstance(field, tuple): field = field[1] if field_name is None: field_name = field_info.get_latex_display_name() elif field_name.find("$") == -1: field_name = field_name.replace(" ", r"\ ") field_name = r"$\rm{" + field_name + r"}$" if fractional: label = field_name + r"$\rm{\ Probability\ Density}$" elif field_unit is None or field_unit == "": label = field_name else: label = field_name + _get_units_label(field_unit) return label def _get_field_title(self, field_y, profile): field_x = profile.x_field xfi = profile.field_info[field_x] yfi = profile.field_info[field_y] x_unit = profile.x.units y_unit = profile.field_units[field_y] fractional = profile.fractional x_title = self.x_title or self._get_field_label(field_x, xfi, x_unit) y_title = self.y_title.get(field_y, None) or self._get_field_label( field_y, yfi, y_unit, fractional ) return (x_title, y_title) @invalidate_plot def annotate_title(self, title, field="all"): r"""Set a title for the plot. Parameters ---------- title : str The title to add. field : str or list of str The field name for which title needs to be set. Examples -------- >>> # To set title for all the fields: >>> plot.annotate_title("This is a Profile Plot") >>> # To set title for specific fields: >>> plot.annotate_title("Profile Plot for Temperature", ("gas", "temperature")) >>> # Setting same plot title for both the given fields >>> plot.annotate_title( ... "Profile Plot: Temperature-Dark Matter Density", ... [("gas", "temperature"), ("deposit", "dark_matter_density")], ... ) """ fields = list(self.plots.keys()) if field == "all" else field for profile in self.profiles: for field in profile.data_source._determine_fields(fields): if field in profile.field_map: field = profile.field_map[field] self._plot_title[field] = title return self @invalidate_plot def annotate_text(self, xpos=0.0, ypos=0.0, text=None, field="all", **text_kwargs): r"""Allow the user to insert text onto the plot The x-position and y-position must be given as well as the text string. Add *text* to plot at location *xpos*, *ypos* in plot coordinates for the given fields or by default for all fields. (see example below). Parameters ---------- xpos : float Position on plot in x-coordinates. ypos : float Position on plot in y-coordinates. text : str The text to insert onto the plot. field : str or tuple The name of the field to add text to. **text_kwargs : dict Extra keyword arguments will be passed to matplotlib text instance >>> import yt >>> from yt.units import kpc >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> my_galaxy = ds.disk(ds.domain_center, [0.0, 0.0, 1.0], 10 * kpc, 3 * kpc) >>> plot = yt.ProfilePlot( ... my_galaxy, ("gas", "density"), [("gas", "temperature")] ... ) >>> # Annotate text for all the fields >>> plot.annotate_text(1e-26, 1e5, "This is annotated text in the plot area.") >>> plot.save() >>> # Annotate text for a given field >>> plot.annotate_text(1e-26, 1e5, "Annotated text", ("gas", "temperature")) >>> plot.save() >>> # Annotate text for multiple fields >>> fields = [("gas", "temperature"), ("gas", "density")] >>> plot.annotate_text(1e-26, 1e5, "Annotated text", fields) >>> plot.save() """ fields = list(self.plots.keys()) if field == "all" else field for profile in self.profiles: for field in profile.data_source._determine_fields(fields): if field in profile.field_map: field = profile.field_map[field] self._plot_text[field] = text self._text_xpos[field] = xpos self._text_ypos[field] = ypos self._text_kwargs[field] = text_kwargs return self class PhasePlot(ImagePlotContainer): r""" Create a 2d profile (phase) plot from a data source or from profile object created with `yt.data_objects.profiles.create_profile`. Given a data object (all_data, region, sphere, etc.), an x field, y field, and z field (or fields), this will create a two-dimensional profile of the average (or total) value of the z field in bins of the x and y fields. Parameters ---------- data_source : YTSelectionContainer Object The data object to be profiled, such as all_data, region, or sphere. If a dataset is passed in instead, an all_data data object is generated internally from the dataset. x_field : str The x binning field for the profile. y_field : str The y binning field for the profile. z_fields : str or list The field or fields to be profiled. weight_field : str The weight field for calculating weighted averages. If None, the profile values are the sum of the field values within the bin. Otherwise, the values are a weighted average. Default : ("gas", "mass") x_bins : int The number of bins in x field for the profile. Default: 128. y_bins : int The number of bins in y field for the profile. Default: 128. accumulation : bool or list of bools If True, the profile values for a bin n are the cumulative sum of all the values from bin 0 to n. If -True, the sum is reversed so that the value for bin n is the cumulative sum from bin N (total bins) to n. A list of values can be given to control the summation in each dimension independently. Default: False. fractional : If True the profile values are divided by the sum of all the profile data such that the profile represents a probability distribution function. fontsize : int Font size for all text in the plot. Default: 18. figure_size : int Size in inches of the image. Default: 8 (8x8) shading : str This argument is directly passed down to matplotlib.axes.Axes.pcolormesh see https://matplotlib.org/3.3.1/gallery/images_contours_and_fields/pcolormesh_grids.html#sphx-glr-gallery-images-contours-and-fields-pcolormesh-grids-py # noqa Default: 'nearest' Examples -------- >>> import yt >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046") >>> ad = ds.all_data() >>> plot = yt.PhasePlot( ... ad, ... ("gas", "density"), ... ("gas", "temperature"), ... [("gas", "mass")], ... weight_field=None, ... ) >>> plot.save() >>> # Change plot properties. >>> plot.set_cmap(("gas", "mass"), "jet") >>> plot.set_zlim(("gas", "mass"), 1e8, 1e13) >>> plot.annotate_title("This is a phase plot") """ x_log = None y_log = None plot_title = None _plot_valid = False _profile_valid = False _plot_type = "Phase" _xlim = (None, None) _ylim = (None, None) def __init__( self, data_source, x_field, y_field, z_fields, weight_field=("gas", "mass"), x_bins=128, y_bins=128, accumulation=False, fractional=False, fontsize=18, figure_size=8.0, shading="nearest", ): data_source = data_object_or_all_data(data_source) if isinstance(z_fields, tuple): z_fields = [z_fields] z_fields = list(always_iterable(z_fields)) if isinstance(data_source.ds, YTProfileDataset): profile = data_source.ds.profile else: profile = create_profile( data_source, [x_field, y_field], z_fields, n_bins=[x_bins, y_bins], weight_field=weight_field, accumulation=accumulation, fractional=fractional, ) type(self)._initialize_instance( self, data_source, profile, fontsize, figure_size, shading ) @classmethod def _initialize_instance( cls, obj, data_source, profile, fontsize, figure_size, shading ): obj.plot_title = {} obj.z_log = {} obj.z_title = {} obj._initfinished = False obj.x_log = None obj.y_log = None obj._plot_text = {} obj._text_xpos = {} obj._text_ypos = {} obj._text_kwargs = {} obj._profile = profile obj._shading = shading obj._profile_valid = True obj._xlim = (None, None) obj._ylim = (None, None) super(PhasePlot, obj).__init__(data_source, figure_size, fontsize) obj._setup_plots() obj._plot_valid = False # see https://github.com/yt-project/yt/issues/4489 obj._initfinished = True return obj def _get_field_title(self, field_z, profile): field_x = profile.x_field field_y = profile.y_field xfi = profile.field_info[field_x] yfi = profile.field_info[field_y] zfi = profile.field_info[field_z] x_unit = profile.x.units y_unit = profile.y.units z_unit = profile.field_units[field_z] fractional = profile.fractional x_label, y_label, z_label = self._get_axes_labels(field_z) x_title = x_label or self._get_field_label(field_x, xfi, x_unit) y_title = y_label or self._get_field_label(field_y, yfi, y_unit) z_title = z_label or self._get_field_label(field_z, zfi, z_unit, fractional) return (x_title, y_title, z_title) def _get_field_label(self, field, field_info, field_unit, fractional=False): field_unit = field_unit.latex_representation() field_name = field_info.display_name if isinstance(field, tuple): field = field[1] if field_name is None: field_name = field_info.get_latex_display_name() elif field_name.find("$") == -1: field_name = field_name.replace(" ", r"\ ") field_name = r"$\rm{" + field_name + r"}$" if fractional: label = field_name + r"$\rm{\ Probability\ Density}$" elif field_unit is None or field_unit == "": label = field_name else: label = field_name + _get_units_label(field_unit) return label def _get_field_log(self, field_z, profile): zfi = profile.field_info[field_z] if self.x_log is None: x_log = profile.x_log else: x_log = self.x_log if self.y_log is None: y_log = profile.y_log else: y_log = self.y_log if field_z in self.z_log: z_log = self.z_log[field_z] else: z_log = zfi.take_log scales = {True: "log", False: "linear"} return scales[x_log], scales[y_log], scales[z_log] @property def profile(self): if not self._profile_valid: self._recreate_profile() return self._profile @property def fields(self): return list(self.plots.keys()) def _setup_plots(self): if self._plot_valid: return for f, data in self.profile.items(): if f in self.plots: pnh = self.plots[f].norm_handler cbh = self.plots[f].colorbar_handler draw_axes = self.plots[f]._draw_axes if self.plots[f].figure is not None: fig = self.plots[f].figure axes = self.plots[f].axes cax = self.plots[f].cax else: fig = None axes = None cax = None else: pnh, cbh = self._get_default_handlers( field=f, default_display_units=self.profile[f].units ) fig = None axes = None cax = None draw_axes = True x_scale, y_scale, z_scale = self._get_field_log(f, self.profile) x_title, y_title, z_title = self._get_field_title(f, self.profile) font_size = self._font_properties.get_size() f = self.profile.data_source._determine_fields(f)[0] # if this is a Particle Phase Plot AND if we using a single color, # override the colorbar here. splat_color = getattr(self, "splat_color", None) if splat_color is not None: cbh.cmap = matplotlib.colors.ListedColormap(splat_color, "dummy") masked_data = data.copy() masked_data[~self.profile.used] = np.nan self.plots[f] = PhasePlotMPL( self.profile.x, self.profile.y, masked_data, x_scale, y_scale, self.figure_size, font_size, fig, axes, cax, shading=self._shading, norm_handler=pnh, colorbar_handler=cbh, ) self.plots[f]._toggle_axes(draw_axes) self.plots[f]._toggle_colorbar(cbh.draw_cbar) self.plots[f].axes.xaxis.set_label_text(x_title) self.plots[f].axes.yaxis.set_label_text(y_title) self.plots[f].cax.yaxis.set_label_text(z_title) self.plots[f].axes.set_xlim(self._xlim) self.plots[f].axes.set_ylim(self._ylim) if f in self._plot_text: self.plots[f].axes.text( self._text_xpos[f], self._text_ypos[f], self._plot_text[f], fontproperties=self._font_properties, **self._text_kwargs[f], ) if f in self.plot_title: self.plots[f].axes.set_title(self.plot_title[f]) # x-y axes minorticks if f not in self._minorticks: self._minorticks[f] = True if self._minorticks[f]: self.plots[f].axes.minorticks_on() else: self.plots[f].axes.minorticks_off() self._set_font_properties() # if this is a particle plot with one color only, hide the cbar here if hasattr(self, "use_cbar") and not self.use_cbar: self.plots[f].hide_colorbar() self._plot_valid = True @classmethod def from_profile(cls, profile, fontsize=18, figure_size=8.0, shading="nearest"): r""" Instantiate a PhasePlot object from a profile object created with :func:`~yt.data_objects.profiles.create_profile`. Parameters ---------- profile : An instance of :class:`~yt.data_objects.profiles.ProfileND` A single profile object. fontsize : float The fontsize to use, in points. figure_size : float The figure size to use, in inches. shading : str This argument is directly passed down to matplotlib.axes.Axes.pcolormesh see https://matplotlib.org/3.3.1/gallery/images_contours_and_fields/pcolormesh_grids.html#sphx-glr-gallery-images-contours-and-fields-pcolormesh-grids-py # noqa Default: 'nearest' Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> extrema = { ... ("gas", "density"): (1e-31, 1e-24), ... ("gas", "temperature"): (1e1, 1e8), ... ("gas", "mass"): (1e-6, 1e-1), ... } >>> profile = yt.create_profile( ... ds.all_data(), ... [("gas", "density"), ("gas", "temperature")], ... fields=[("gas", "mass")], ... extrema=extrema, ... fractional=True, ... ) >>> ph = yt.PhasePlot.from_profile(profile) >>> ph.save() """ obj = cls.__new__(cls) data_source = profile.data_source return cls._initialize_instance( obj, data_source, profile, fontsize, figure_size, shading ) def annotate_text(self, xpos=0.0, ypos=0.0, text=None, **text_kwargs): r""" Allow the user to insert text onto the plot The x-position and y-position must be given as well as the text string. Add *text* tp plot at location *xpos*, *ypos* in plot coordinates (see example below). Parameters ---------- xpos : float Position on plot in x-coordinates. ypos : float Position on plot in y-coordinates. text : str The text to insert onto the plot. **text_kwargs : dict Extra keyword arguments will be passed to matplotlib text instance >>> plot.annotate_text(1e-15, 5e4, "Hello YT") """ for f in self.data_source._determine_fields(list(self.plots.keys())): if self.plots[f].figure is not None and text is not None: self.plots[f].axes.text( xpos, ypos, text, fontproperties=self._font_properties, **text_kwargs, ) self._plot_text[f] = text self._text_xpos[f] = xpos self._text_ypos[f] = ypos self._text_kwargs[f] = text_kwargs return self @validate_plot def save(self, name: str | None = None, suffix: str | None = None, mpl_kwargs=None): r""" Saves a 2d profile plot. Parameters ---------- name : str, optional The output file keyword. suffix : string, optional Specify the image type by its suffix. If not specified, the output type will be inferred from the filename. Defaults to '.png'. mpl_kwargs : dict, optional A dict of keyword arguments to be passed to matplotlib. >>> plot.save(mpl_kwargs={"bbox_inches": "tight"}) """ names = [] if not self._plot_valid: self._setup_plots() if mpl_kwargs is None: mpl_kwargs = {} if name is None: name = str(self.profile.ds) name = os.path.expanduser(name) xfn = self.profile.x_field yfn = self.profile.y_field if isinstance(xfn, tuple): xfn = xfn[1] if isinstance(yfn, tuple): yfn = yfn[1] for f in self.profile.field_data: _f = f if isinstance(f, tuple): _f = _f[1] middle = f"2d-Profile_{xfn}_{yfn}_{_f}" splitname = os.path.split(name) if splitname[0] != "" and not os.path.isdir(splitname[0]): os.makedirs(splitname[0]) if os.path.isdir(name) and name != str(self.profile.ds): name = name + (os.sep if name[-1] != os.sep else "") name += str(self.profile.ds) new_name = validate_image_name(name, suffix) if new_name == name: for v in self.plots.values(): out_name = v.save(name, mpl_kwargs) names.append(out_name) return names name = new_name prefix, suffix = os.path.splitext(name) name = f"{prefix}_{middle}{suffix}" names.append(name) self.plots[f].save(name, mpl_kwargs) return names @invalidate_plot def set_title(self, field, title): """Set a title for the plot. Parameters ---------- field : str The z field of the plot to add the title. title : str The title to add. Examples -------- >>> plot.set_title(("gas", "mass"), "This is a phase plot") """ self.plot_title[self.data_source._determine_fields(field)[0]] = title return self @invalidate_plot def annotate_title(self, title): """Set a title for the plot. Parameters ---------- title : str The title to add. Examples -------- >>> plot.annotate_title("This is a phase plot") """ for f in self._profile.field_data: self.plot_title[self.data_source._determine_fields(f)[0]] = title return self @invalidate_plot def reset_plot(self): self.plots = {} return self @invalidate_plot def set_log(self, field, log): """set a field to log or linear. Parameters ---------- field : string the field to set a transform log : boolean Log on/off. """ p = self._profile if field == "all": self.x_log = log self.y_log = log for field in p.field_data: self.z_log[field] = log self._profile_valid = False else: (field,) = self.profile.data_source._determine_fields([field]) if field == p.x_field: self.x_log = log self._profile_valid = False elif field == p.y_field: self.y_log = log self._profile_valid = False elif field in p.field_data: super().set_log(field, log) else: raise KeyError(f"Field {field} not in phase plot!") return self @invalidate_plot def set_unit(self, field, unit): """Sets a new unit for the requested field Parameters ---------- field : string The name of the field that is to be changed. unit : string or Unit object The name of the new unit. """ fd = self.data_source._determine_fields(field)[0] if fd == self.profile.x_field: self.profile.set_x_unit(unit) elif fd == self.profile.y_field: self.profile.set_y_unit(unit) elif fd in self.profile.field_data.keys(): self.profile.set_field_unit(field, unit) self.plots[field].norm_handler.display_units = unit else: raise KeyError(f"Field {field} not in phase plot!") return self @invalidate_plot @invalidate_profile def set_xlim(self, xmin=None, xmax=None): """Sets the limits of the x bin field Parameters ---------- xmin : float or None The new x minimum in the current x-axis units. Defaults to None, which leaves the xmin unchanged. xmax : float or None The new x maximum in the current x-axis units. Defaults to None, which leaves the xmax unchanged. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> pp = yt.PhasePlot(ds.all_data(), "density", "temperature", ("gas", "mass")) >>> pp.set_xlim(1e-29, 1e-24) >>> pp.save() """ p = self._profile if xmin is None: xmin = p.x_bins.min() elif not hasattr(xmin, "units"): xmin = self.ds.quan(xmin, p.x_bins.units) if xmax is None: xmax = p.x_bins.max() elif not hasattr(xmax, "units"): xmax = self.ds.quan(xmax, p.x_bins.units) self._xlim = (xmin, xmax) return self @invalidate_plot @invalidate_profile def set_ylim(self, ymin=None, ymax=None): """Sets the plot limits for the y bin field. Parameters ---------- ymin : float or None The new y minimum in the current y-axis units. Defaults to None, which leaves the ymin unchanged. ymax : float or None The new y maximum in the current y-axis units. Defaults to None, which leaves the ymax unchanged. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> pp = yt.PhasePlot( ... ds.all_data(), ... ("gas", "density"), ... ("gas", "temperature"), ... ("gas", "mass"), ... ) >>> pp.set_ylim(1e4, 1e6) >>> pp.save() """ p = self._profile if ymin is None: ymin = p.y_bins.min() elif not hasattr(ymin, "units"): ymin = self.ds.quan(ymin, p.y_bins.units) if ymax is None: ymax = p.y_bins.max() elif not hasattr(ymax, "units"): ymax = self.ds.quan(ymax, p.y_bins.units) self._ylim = (ymin, ymax) return self def _recreate_profile(self): p = self._profile units = {p.x_field: str(p.x.units), p.y_field: str(p.y.units)} zunits = {field: str(p.field_units[field]) for field in p.field_units} extrema = {p.x_field: self._xlim, p.y_field: self._ylim} if self.x_log is not None or self.y_log is not None: logs = {} else: logs = None if self.x_log is not None: logs[p.x_field] = self.x_log if self.y_log is not None: logs[p.y_field] = self.y_log deposition = getattr(p, "deposition", None) additional_kwargs = { "accumulation": p.accumulation, "fractional": p.fractional, "deposition": deposition, } self._profile = create_profile( p.data_source, [p.x_field, p.y_field], list(p.field_map.values()), n_bins=[len(p.x_bins) - 1, len(p.y_bins) - 1], weight_field=p.weight_field, units=units, extrema=extrema, logs=logs, **additional_kwargs, ) for field in zunits: self._profile.set_field_unit(field, zunits[field]) self._profile_valid = True class PhasePlotMPL(ImagePlotMPL): """A container for a single matplotlib figure and axes for a PhasePlot""" def __init__( self, x_data, y_data, data, x_scale, y_scale, figure_size, fontsize, figure, axes, cax, shading="nearest", *, norm_handler: NormHandler, colorbar_handler: ColorbarHandler, ): self._initfinished = False self._shading = shading self._setup_layout_constraints(figure_size, fontsize) # this line is added purely to prevent exact image comparison tests # to fail, but eventually we should embrace the change and # use similar values for PhasePlotMPL and WindowPlotMPL self._ax_text_size[0] *= 1.1 / 1.2 # TODO: remove this super().__init__( figure=figure, axes=axes, cax=cax, norm_handler=norm_handler, colorbar_handler=colorbar_handler, ) self._init_image(x_data, y_data, data, x_scale, y_scale) self._initfinished = True def _init_image( self, x_data, y_data, image_data, x_scale, y_scale, ): """Store output of imshow in image variable""" norm = self.norm_handler.get_norm(image_data) self.image = None self.cb = None self.image = self.axes.pcolormesh( np.array(x_data), np.array(y_data), np.array(image_data.T), norm=norm, cmap=self.colorbar_handler.cmap, shading=self._shading, ) self._set_axes() self.axes.set_xscale(x_scale) self.axes.set_yscale(y_scale) yt-project-yt-f043ac8/yt/visualization/streamlines.py000066400000000000000000000211511510711153200231070ustar00rootroot00000000000000import numpy as np from yt.data_objects.construction_data_containers import YTStreamline from yt.funcs import get_pbar from yt.units.yt_array import YTArray from yt.utilities.amr_kdtree.api import AMRKDTree from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, parallel_passthrough, ) def sanitize_length(length, ds): # Ensure that lengths passed in with units are returned as code_length # magnitudes without units if isinstance(length, YTArray): return ds.arr(length).in_units("code_length").d else: return length class Streamlines(ParallelAnalysisInterface): r"""A collection of streamlines that flow through the volume The Streamlines object contains a collection of streamlines defined as paths that are parallel to a specified vector field. Parameters ---------- ds : ~yt.data_objects.static_output.Dataset This is the dataset to streamline positions : array_like An array of initial starting positions of the streamlines. xfield : str or tuple of str, optional The x component of the vector field to be streamlined. Default:'velocity_x' yfield : str or tuple of str, optional The y component of the vector field to be streamlined. Default:'velocity_y' zfield : str or tuple of str, optional The z component of the vector field to be streamlined. Default:'velocity_z' volume : `yt.extensions.volume_rendering.AMRKDTree`, optional The volume to be streamlined. Can be specified for finer-grained control, but otherwise will be automatically generated. At this point it must use the AMRKDTree. Default: None dx : float, optional Optionally specify the step size during the integration. Default: minimum dx length : float, optional Optionally specify the length of integration. Default: np.max(self.ds.domain_right_edge-self.ds.domain_left_edge) direction : real, optional Specifies the direction of integration. The magnitude of this value has no effect, only the sign. get_magnitude: bool, optional Specifies whether the Streamlines.magnitudes array should be filled with the magnitude of the vector field at each point in the streamline. This seems to be a ~10% hit to performance. Default: False Examples -------- >>> import matplotlib.pyplot as plt ... import numpy as np ... from mpl_toolkits.mplot3d import Axes3D ... import yt ... from yt.visualization.api import Streamlines >>> # Load the dataset and set some parameters >>> ds = load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> c = np.array([0.5] * 3) >>> N = 100 >>> scale = 1.0 >>> pos_dx = np.random.random((N, 3)) * scale - scale / 2.0 >>> pos = c + pos_dx >>> # Define and construct streamlines >>> streamlines = Streamlines( ... ds, pos, "velocity_x", "velocity_y", "velocity_z", length=1.0 ... ) >>> streamlines.integrate_through_volume() >>> # Make a 3D plot of the streamlines and save it to disk >>> fig = plt.figure() >>> ax = Axes3D(fig) >>> for stream in streamlines.streamlines: ... stream = stream[np.all(stream != 0.0, axis=1)] ... ax.plot3D(stream[:, 0], stream[:, 1], stream[:, 2], alpha=0.1) >>> plt.savefig("streamlines.png") """ def __init__( self, ds, positions, xfield="velocity_x", yfield="velocity_x", zfield="velocity_x", volume=None, dx=None, length=None, direction=1, get_magnitude=False, ): ParallelAnalysisInterface.__init__(self) self.ds = ds self.start_positions = sanitize_length(positions, ds) self.N = self.start_positions.shape[0] # I need a data object to resolve the field names to field tuples # via _determine_fields() ad = self.ds.all_data() self.xfield = ad._determine_fields(xfield)[0] self.yfield = ad._determine_fields(yfield)[0] self.zfield = ad._determine_fields(zfield)[0] self.get_magnitude = get_magnitude self.direction = np.sign(direction) if volume is None: volume = AMRKDTree(self.ds) volume.set_fields( [self.xfield, self.yfield, self.zfield], [False, False, False], False ) volume.join_parallel_trees() self.volume = volume if dx is None: dx = self.ds.index.get_smallest_dx() self.dx = sanitize_length(dx, ds) if length is None: length = np.max(self.ds.domain_right_edge - self.ds.domain_left_edge) self.length = sanitize_length(length, ds) self.steps = int(self.length / self.dx) + 1 # Fix up the dx. self.dx = 1.0 * self.length / self.steps self.streamlines = np.zeros((self.N, self.steps, 3), dtype="float64") self.magnitudes = None if self.get_magnitude: self.magnitudes = np.zeros((self.N, self.steps), dtype="float64") def integrate_through_volume(self): nprocs = self.comm.size my_rank = self.comm.rank self.streamlines[my_rank::nprocs, 0, :] = self.start_positions[my_rank::nprocs] pbar = get_pbar("Streamlining", self.N) for i, stream in enumerate(self.streamlines[my_rank::nprocs]): thismag = None if self.get_magnitude: thismag = self.magnitudes[i, :] step = self.steps while step > 1: this_node = self.volume.locate_node(stream[-step, :]) step = self._integrate_through_brick( this_node, stream, step, mag=thismag ) pbar.update(i + 1) pbar.finish() self._finalize_parallel(None) self.streamlines = self.ds.arr(self.streamlines, "code_length") if self.get_magnitude: self.magnitudes = self.ds.arr( self.magnitudes, self.ds.field_info[self.xfield].units ) @parallel_passthrough def _finalize_parallel(self, data): self.streamlines = self.comm.mpi_allreduce(self.streamlines, op="sum") if self.get_magnitude: self.magnitudes = self.comm.mpi_allreduce(self.magnitudes, op="sum") def _integrate_through_brick(self, node, stream, step, periodic=False, mag=None): LE = self.ds.domain_left_edge.d RE = self.ds.domain_right_edge.d while step > 1: self.volume.get_brick_data(node) brick = node.data stream[-step + 1] = stream[-step] if mag is None: brick.integrate_streamline( stream[-step + 1], self.direction * self.dx, None ) else: marr = [mag] brick.integrate_streamline( stream[-step + 1], self.direction * self.dx, marr ) mag[-step + 1] = marr[0] cur_stream = stream[-step + 1, :] if np.sum(np.logical_or(cur_stream < LE, cur_stream >= RE)): return 0 nLE = node.get_left_edge() nRE = node.get_right_edge() if np.sum(np.logical_or(cur_stream < nLE, cur_stream >= nRE)): return step - 1 step -= 1 return step def clean_streamlines(self): temp = np.empty(self.N, dtype="object") temp2 = np.empty(self.N, dtype="object") for i, stream in enumerate(self.streamlines): mask = np.all(stream != 0.0, axis=1) temp[i] = stream[mask] temp2[i] = self.magnitudes[i, mask] self.streamlines = temp self.magnitudes = temp2 def path(self, streamline_id): """ Returns an YTSelectionContainer1D object defined by a streamline. Parameters ---------- streamline_id : int This defines which streamline from the Streamlines object that will define the YTSelectionContainer1D object. Returns ------- An YTStreamline YTSelectionContainer1D object Examples -------- >>> from yt.visualization.api import Streamlines >>> streamlines = Streamlines(ds, [0.5] * 3) >>> streamlines.integrate_through_volume() >>> stream = streamlines.path(0) >>> fig, ax = plt.subplots() >>> ax.set_yscale("log") >>> ax.plot(stream["t"], stream["gas", "density"], "-x") """ return YTStreamline( self.streamlines[streamline_id], ds=self.ds, length=self.length ) yt-project-yt-f043ac8/yt/visualization/tests/000077500000000000000000000000001510711153200213515ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/tests/__init__.py000066400000000000000000000000001510711153200234500ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/tests/test_callbacks.py000066400000000000000000001220101510711153200246750ustar00rootroot00000000000000import contextlib import inspect import shutil import tempfile from numpy.testing import assert_array_equal, assert_raises import yt.units as u from yt.config import ytcfg from yt.loaders import load from yt.testing import ( assert_fname, fake_amr_ds, fake_hexahedral_ds, fake_random_ds, fake_tetrahedral_ds, requires_file, requires_module, ) from yt.utilities.answer_testing.framework import ( PlotWindowAttributeTest, data_dir_load, requires_ds, ) from yt.utilities.exceptions import YTDataTypeUnsupported, YTPlotCallbackError from yt.visualization.api import OffAxisSlicePlot, ProjectionPlot, SlicePlot from yt.visualization.plot_container import accepts_all_fields # These are a very simple set of tests that verify that each callback is or is # not working. They only check that it functions without an error; they do not # check that it is providing correct results. Note that test_axis_manipulations # is one exception, which is a full answer test. # These are the callbacks still to test: # # X velocity # X magnetic_field # X quiver # X contour # X grids # X streamlines # units # X line # cquiver # clumps # X arrow # X marker # X sphere # hop_circles # hop_particles # coord_axes # X text # X particles # title # flash_ray_data # X timestamp # X scale # material_boundary # X ray # X line_integral_convolution # # X flip_horizontal, flip_vertical, swap_axes (all in test_axis_manipulations) # cylindrical data for callback test cyl_2d = "WDMerger_hdf5_chk_1000/WDMerger_hdf5_chk_1000.hdf5" cyl_3d = "MHD_Cyl3d_hdf5_plt_cnt_0100/MHD_Cyl3d_hdf5_plt_cnt_0100.hdf5" @contextlib.contextmanager def _cleanup_fname(): tmpdir = tempfile.mkdtemp() yield tmpdir shutil.rmtree(tmpdir) def test_method_signature(): ds = fake_amr_ds( fields=[("gas", "density"), ("gas", "velocity_x"), ("gas", "velocity_y")], units=["g/cm**3", "m/s", "m/s"], ) p = SlicePlot(ds, "z", ("gas", "density")) sig = inspect.signature(p.annotate_velocity) # checking the first few arguments rather than the whole signature # we just want to validate that method wrapping works assert list(sig.parameters.keys())[:4] == [ "factor", "scale", "scale_units", "normalize", ] def test_init_signature_error_callback(): ds = fake_amr_ds( fields=[("gas", "density"), ("gas", "velocity_x"), ("gas", "velocity_y")], units=["g/cm**3", "m/s", "m/s"], ) p = SlicePlot(ds, "z", ("gas", "density")) # annotate_velocity accepts only one positional argument assert_raises(TypeError, p.annotate_velocity, 1, 2, 3) def check_axis_manipulation(plot_obj, prefix): # convenience function for testing functionality of axis manipulation # callbacks. Can use in any of the other test functions. # test individual callbacks for cb in ("swap_axes", "flip_horizontal", "flip_vertical"): callback_handle = getattr(plot_obj, cb) callback_handle() # toggles on for axis operation assert_fname(plot_obj.save(prefix)[0]) callback_handle() # toggle off # test all at once for cb in ("swap_axes", "flip_horizontal", "flip_vertical"): callback_handle = getattr(plot_obj, cb) callback_handle() assert_fname(plot_obj.save(prefix)[0]) def test_timestamp_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_timestamp() assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_timestamp() assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_timestamp() assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_timestamp(corner="lower_right", redshift=True, draw_inset_box=True) p.save(prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) p.annotate_timestamp(coord_system="axis") assert_fname(p.save(prefix)[0]) def test_timestamp_callback_code_units(): # see https://github.com/yt-project/yt/issues/3869 with _cleanup_fname() as prefix: ds = fake_random_ds(2, unit_system="code") p = SlicePlot(ds, "z", ("gas", "density")) p.annotate_timestamp() assert_fname(p.save(prefix)[0]) def test_scale_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_scale() assert_fname(p.save(prefix)[0]) p = ProjectionPlot(ds, ax, ("gas", "density"), width=(0.5, 1.0)) p.annotate_scale() assert_fname(p.save(prefix)[0]) p = ProjectionPlot(ds, ax, ("gas", "density"), width=(1.0, 1.5)) p.annotate_scale() assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_scale() assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_scale() assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_scale(corner="upper_right", coeff=10.0, unit="kpc") assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_scale(text_args={"size": 24}) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_scale(text_args={"font": 24}) assert_raises(YTPlotCallbackError) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises(YTDataTypeUnsupported, p.annotate_scale) assert_raises(YTDataTypeUnsupported, p.annotate_scale, coord_system="axis") def test_line_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_line([0.1, 0.1, 0.1], [0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_line([0.1, 0.1, 0.1], [0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_line([0.1, 0.1, 0.1], [0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_line([0.1, 0.1], [0.5, 0.5], coord_system="axis", color="red") p.save(prefix) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises( YTDataTypeUnsupported, p.annotate_line, [0.1, 0.1, 0.1], [0.5, 0.5, 0.5] ) p.annotate_line([0.1, 0.1], [0.5, 0.5], coord_system="axis") assert_fname(p.save(prefix)[0]) def test_ray_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) ray = ds.ray((0.1, 0.2, 0.3), (0.6, 0.8, 0.5)) oray = ds.ortho_ray(0, (0.3, 0.4)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_ray(oray) p.annotate_ray(ray) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_ray(oray) p.annotate_ray(ray) assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_ray(oray) p.annotate_ray(ray) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_ray(oray) p.annotate_ray(ray, color="red") p.save(prefix) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") ray = ds.ray((0.1, 0.2, 0.3), (0.6, 0.8, 0.5)) oray = ds.ortho_ray(0, (0.3, 0.4)) p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises(YTDataTypeUnsupported, p.annotate_ray, oray) assert_raises(YTDataTypeUnsupported, p.annotate_ray, ray) def test_arrow_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_arrow([0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_arrow([0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_arrow([0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_arrow([0.5, 0.5], coord_system="axis", length=0.05) p.annotate_arrow( [[0.5, 0.6], [0.5, 0.6], [0.5, 0.6]], coord_system="data", length=0.05 ) p.annotate_arrow( [[0.5, 0.6, 0.8], [0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="data", length=0.05, ) p.annotate_arrow( [[0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="axis", length=0.05 ) p.annotate_arrow( [[0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="figure", length=0.05 ) p.annotate_arrow( [[0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="plot", length=0.05 ) p.save(prefix) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises(YTDataTypeUnsupported, p.annotate_arrow, [0.5, 0.5, 0.5]) p.annotate_arrow([0.5, 0.5], coord_system="axis") assert_fname(p.save(prefix)[0]) def test_marker_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_marker([0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_marker([0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_marker([0.5, 0.5, 0.5]) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) coord = ds.arr([0.75, 0.75, 0.75], "unitary") coord.convert_to_units("kpc") p.annotate_marker(coord, coord_system="data") p.annotate_marker([0.5, 0.5], coord_system="axis", marker="*") p.annotate_marker([[0.5, 0.6], [0.5, 0.6], [0.5, 0.6]], coord_system="data") p.annotate_marker( [[0.5, 0.6, 0.8], [0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="data" ) p.annotate_marker([[0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="axis") p.annotate_marker([[0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="figure") p.annotate_marker([[0.5, 0.6, 0.8], [0.5, 0.6, 0.8]], coord_system="plot") p.save(prefix) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises(YTDataTypeUnsupported, p.annotate_marker, [0.5, 0.5, 0.5]) p.annotate_marker([0.5, 0.5], coord_system="axis") assert_fname(p.save(prefix)[0]) def test_particles_callback(): with _cleanup_fname() as prefix: ax = "z" ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), particles=1) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_particles((10, "Mpc")) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_particles((10, "Mpc")) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) ad = ds.all_data() p.annotate_particles( (10, "Mpc"), p_size=1.0, col="k", marker="o", stride=1, ptype="all", alpha=1.0, data_source=ad, ) p.save(prefix) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises(YTDataTypeUnsupported, p.annotate_particles, (10, "Mpc")) def test_sphere_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_sphere([0.5, 0.5, 0.5], 0.1) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_sphere([0.5, 0.5, 0.5], 0.1) assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_sphere([0.5, 0.5, 0.5], 0.1) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_sphere([0.5, 0.5], 0.1, coord_system="axis", text="blah") p.save(prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises( YTDataTypeUnsupported, p.annotate_sphere, [0.5, 0.5, 0.5], 0.1, ) p.annotate_sphere([0.5, 0.5], 0.1, coord_system="axis", text="blah") assert_fname(p.save(prefix)[0]) def test_invalidated_annotations(): # check that annotate_sphere and annotate_arrow succeed on re-running after # an operation that invalidates the plot (set_font_size), see # https://github.com/yt-project/yt/issues/4698 ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = SlicePlot(ds, "z", ("gas", "density")) p.annotate_sphere([0.5, 0.5, 0.5], 0.1) p.set_font_size(24) p.render() p = SlicePlot(ds, "z", ("gas", "density")) p.annotate_arrow([0.5, 0.5, 0.5]) p.set_font_size(24) p.render() def test_text_callback(): with _cleanup_fname() as prefix: ax = "z" vector = [1.0, 1.0, 1.0] ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_text([0.5, 0.5, 0.5], "dinosaurs!") assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_text([0.5, 0.5, 0.5], "dinosaurs!") assert_fname(p.save(prefix)[0]) p = OffAxisSlicePlot(ds, vector, ("gas", "density")) p.annotate_text([0.5, 0.5, 0.5], "dinosaurs!") assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_text( [0.5, 0.5], "dinosaurs!", coord_system="axis", text_args={"color": "red"} ) p.save(prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = ProjectionPlot(ds, "r", ("gas", "density")) assert_raises( YTDataTypeUnsupported, p.annotate_text, [0.5, 0.5, 0.5], "dinosaurs!" ) p.annotate_text( [0.5, 0.5], "dinosaurs!", coord_system="axis", text_args={"color": "red"} ) assert_fname(p.save(prefix)[0]) @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_velocity_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "velocity_x", "velocity_y", "velocity_z"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), ) for ax in "xyz": p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_velocity() assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_velocity() assert_fname(p.save(prefix)[0]) # Test for OffAxis Slice p = SlicePlot(ds, [1, 1, 0], ("gas", "density"), north_vector=[0, 0, 1]) p.annotate_velocity(factor=40, normalize=True) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_velocity(factor=8, scale=0.5, scale_units="inches", normalize=True) assert_fname(p.save(prefix)[0]) with _cleanup_fname() as prefix: ds = fake_hexahedral_ds(fields=[f"velocity_{ax}" for ax in "xyz"]) sl = SlicePlot(ds, 1, ("connect1", "test")) sl.annotate_velocity() assert_fname(sl.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "velocity_magnitude")) slc.annotate_velocity() assert_fname(slc.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_3d) for ax in ["r", "z", "theta"]: slc = SlicePlot(ds, ax, ("gas", "velocity_magnitude")) slc.annotate_velocity() assert_fname(slc.save(prefix)[0]) slc = ProjectionPlot(ds, ax, ("gas", "velocity_magnitude")) slc.annotate_velocity() assert_fname(slc.save(prefix)[0]) def test_velocity_callback_spherical(): ds = fake_amr_ds( fields=("density", "velocity_r", "velocity_theta", "velocity_phi"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), geometry="spherical", ) with _cleanup_fname() as prefix: p = ProjectionPlot(ds, "phi", ("stream", "density")) p.annotate_velocity(factor=40, normalize=True) assert_fname(p.save(prefix)[0]) with _cleanup_fname() as prefix: p = ProjectionPlot(ds, "r", ("stream", "density")) p.annotate_velocity(factor=40, normalize=True) assert_raises(NotImplementedError, p.save, prefix) @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_magnetic_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=( "density", "magnetic_field_x", "magnetic_field_y", "magnetic_field_z", ), units=( "g/cm**3", "G", "G", "G", ), ) for ax in "xyz": p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_magnetic_field() assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_magnetic_field() assert_fname(p.save(prefix)[0]) # Test for OffAxis Slice p = SlicePlot(ds, [1, 1, 0], ("gas", "density"), north_vector=[0, 0, 1]) p.annotate_magnetic_field(factor=40, normalize=True) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_magnetic_field( factor=8, scale=0.5, scale_units="inches", normalize=True ) assert_fname(p.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "magnetic_field_strength")) slc.annotate_magnetic_field() assert_fname(slc.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_3d) for ax in ["r", "z", "theta"]: slc = SlicePlot(ds, ax, ("gas", "magnetic_field_strength")) slc.annotate_magnetic_field() assert_fname(slc.save(prefix)[0]) slc = ProjectionPlot(ds, ax, ("gas", "magnetic_field_strength")) slc.annotate_magnetic_field() assert_fname(slc.save(prefix)[0]) check_axis_manipulation(slc, prefix) # only test the last axis with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=( "density", "magnetic_field_r", "magnetic_field_theta", "magnetic_field_phi", ), units=( "g/cm**3", "G", "G", "G", ), geometry="spherical", ) p = ProjectionPlot(ds, "phi", ("gas", "density")) p.annotate_magnetic_field( factor=8, scale=0.5, scale_units="inches", normalize=True ) assert_fname(p.save(prefix)[0]) p = ProjectionPlot(ds, "theta", ("gas", "density")) p.annotate_magnetic_field( factor=8, scale=0.5, scale_units="inches", normalize=True ) assert_fname(p.save(prefix)[0]) p = ProjectionPlot(ds, "r", ("gas", "density")) p.annotate_magnetic_field( factor=8, scale=0.5, scale_units="inches", normalize=True ) assert_raises(NotImplementedError, p.save, prefix) @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_quiver_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "velocity_x", "velocity_y", "velocity_z"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), ) for ax in "xyz": p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_quiver(("gas", "velocity_x"), ("gas", "velocity_y")) assert_fname(p.save(prefix)[0]) p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_quiver(("gas", "velocity_x"), ("gas", "velocity_y")) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_quiver(("gas", "velocity_x"), ("gas", "velocity_y")) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_quiver( ("gas", "velocity_x"), ("gas", "velocity_y"), factor=8, scale=0.5, scale_units="inches", normalize=True, bv_x=0.5 * u.cm / u.s, bv_y=0.5 * u.cm / u.s, ) assert_fname(p.save(prefix)[0]) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "density")) slc.annotate_quiver(("gas", "velocity_r"), ("gas", "velocity_z")) assert_fname(slc.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_3d) slc = SlicePlot(ds, "r", ("gas", "velocity_magnitude")) slc.annotate_quiver(("gas", "velocity_theta"), ("gas", "velocity_z")) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "z", ("gas", "velocity_magnitude")) slc.annotate_quiver( ("gas", "velocity_cartesian_x"), ("gas", "velocity_cartesian_y") ) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "theta", ("gas", "velocity_magnitude")) slc.annotate_quiver(("gas", "velocity_r"), ("gas", "velocity_z")) assert_fname(slc.save(prefix)[0]) def test_quiver_callback_spherical(): ds = fake_amr_ds( fields=("density", "velocity_r", "velocity_theta", "velocity_phi"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), geometry="spherical", ) with _cleanup_fname() as prefix: p = ProjectionPlot(ds, "phi", ("gas", "density")) p.annotate_quiver( ("gas", "velocity_cylindrical_radius"), ("gas", "velocity_cylindrical_z"), factor=8, scale=0.5, scale_units="inches", normalize=True, ) assert_fname(p.save(prefix)[0]) with _cleanup_fname() as prefix: p = ProjectionPlot(ds, "r", ("gas", "density")) p.annotate_quiver( ("gas", "velocity_theta"), ("gas", "velocity_phi"), factor=8, scale=0.5, scale_units="inches", normalize=True, ) assert_fname(p.save(prefix)[0]) @requires_module("h5py") @requires_file(cyl_2d) def test_contour_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density", "temperature"), units=("g/cm**3", "K")) for ax in "xyz": p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_contour(("gas", "temperature")) assert_fname(p.save(prefix)[0]) p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_contour(("gas", "temperature")) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_contour(("gas", "temperature")) # BREAKS WITH ndarray assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_contour( ("gas", "temperature"), levels=10, factor=8, take_log=False, clim=(0.4, 0.6), plot_args={"linewidths": 2.0}, label=True, text_args={"fontsize": "x-large"}, ) p.save(prefix) p = SlicePlot(ds, "x", ("gas", "density")) s2 = ds.slice(0, 0.2) p.annotate_contour( ("gas", "temperature"), levels=10, factor=8, take_log=False, clim=(0.4, 0.6), plot_args={"linewidths": 2.0}, label=True, text_args={"fontsize": "x-large"}, data_source=s2, ) p.save(prefix) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "plasma_beta")) slc.annotate_contour( ("gas", "plasma_beta"), levels=2, factor=7, take_log=False, clim=(1.0e-1, 1.0e1), label=True, plot_args={"colors": ("c", "w"), "linewidths": 1}, text_args={"fmt": "%1.1f"}, ) assert_fname(slc.save(prefix)[0]) check_axis_manipulation(slc, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "temperature"), units=("g/cm**3", "K"), geometry="spherical", ) p = SlicePlot(ds, "r", ("gas", "density")) kwargs = { "levels": 10, "factor": 8, "take_log": False, "clim": (0.4, 0.6), "plot_args": {"linewidths": 2.0}, "label": True, "text_args": {"fontsize": "x-large"}, } assert_raises( YTDataTypeUnsupported, p.annotate_contour, ("gas", "temperature"), **kwargs ) @requires_module("h5py") @requires_file(cyl_2d) def test_grids_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) for ax in "xyz": p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_grids() assert_fname(p.save(prefix)[0]) p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_grids() assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_grids() assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_grids( alpha=0.7, min_pix=10, min_pix_ids=30, draw_ids=True, id_loc="upper right", periodic=False, min_level=2, max_level=3, cmap="gist_stern", ) p.save(prefix) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "density")) slc.annotate_grids() assert_fname(slc.save(prefix)[0]) check_axis_manipulation(slc, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = SlicePlot(ds, "r", ("gas", "density")) kwargs = { "alpha": 0.7, "min_pix": 10, "min_pix_ids": 30, "draw_ids": True, "id_loc": "upper right", "periodic": False, "min_level": 2, "max_level": 3, "cmap": "gist_stern", } assert_raises(YTDataTypeUnsupported, p.annotate_grids, **kwargs) @requires_module("h5py") @requires_file(cyl_2d) def test_cell_edges_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) for ax in "xyz": p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_cell_edges() assert_fname(p.save(prefix)[0]) p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_cell_edges() assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_cell_edges() assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_cell_edges(alpha=0.7, line_width=0.9, color=(0.0, 1.0, 1.0)) p.save(prefix) check_axis_manipulation(p, prefix) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "density")) slc.annotate_cell_edges() assert_fname(slc.save(prefix)[0]) with _cleanup_fname() as prefix: ds = fake_amr_ds(fields=("density",), units=("g/cm**3",), geometry="spherical") p = SlicePlot(ds, "r", ("gas", "density")) assert_raises(YTDataTypeUnsupported, p.annotate_cell_edges) def test_mesh_lines_callback(): with _cleanup_fname() as prefix: ds = fake_hexahedral_ds() for field in ds.field_list: sl = SlicePlot(ds, 1, field) sl.annotate_mesh_lines(color="black") assert_fname(sl.save(prefix)[0]) ds = fake_tetrahedral_ds() for field in ds.field_list: sl = SlicePlot(ds, 1, field) sl.annotate_mesh_lines(color="black") assert_fname(sl.save(prefix)[0]) check_axis_manipulation(sl, prefix) # only test the final field @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_streamline_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "velocity_x", "velocity_y", "magvel"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), ) for ax in "xyz": # Projection plot tests p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_streamlines(("gas", "velocity_x"), ("gas", "velocity_y")) assert_fname(p.save(prefix)[0]) p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_streamlines(("gas", "velocity_x"), ("gas", "velocity_y")) assert_fname(p.save(prefix)[0]) # Slice plot test p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_streamlines(("gas", "velocity_x"), ("gas", "velocity_y")) assert_fname(p.save(prefix)[0]) # Additional features p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_streamlines( ("gas", "velocity_x"), ("gas", "velocity_y"), factor=32, density=4 ) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_streamlines( ("gas", "velocity_x"), ("gas", "velocity_y"), color=("stream", "magvel"), ) assert_fname(p.save(prefix)[0]) check_axis_manipulation(p, prefix) # a more thorough example involving many keyword arguments p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_streamlines( ("gas", "velocity_x"), ("gas", "velocity_y"), linewidth=("gas", "density"), linewidth_upscaling=3, color=("stream", "magvel"), color_threshold=0.5, cmap="viridis", arrowstyle="->", ) assert_fname(p.save(prefix)[0]) # Axisymmetric dataset with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "velocity_magnitude")) slc.annotate_streamlines(("gas", "velocity_r"), ("gas", "velocity_z")) assert_fname(slc.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_3d) slc = SlicePlot(ds, "r", ("gas", "velocity_magnitude")) slc.annotate_streamlines(("gas", "velocity_theta"), ("gas", "velocity_z")) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "z", ("gas", "velocity_magnitude")) slc.annotate_streamlines( ("gas", "velocity_cartesian_x"), ("gas", "velocity_cartesian_y") ) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "theta", ("gas", "velocity_magnitude")) slc.annotate_streamlines(("gas", "velocity_r"), ("gas", "velocity_z")) assert_fname(slc.save(prefix)[0]) # Spherical dataset with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "velocity_r", "velocity_theta", "velocity_phi"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), geometry="spherical", ) slc = SlicePlot(ds, "phi", ("gas", "velocity_magnitude")) slc.annotate_streamlines( ("gas", "velocity_cylindrical_radius"), ("gas", "velocity_cylindrical_z") ) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "theta", ("gas", "velocity_magnitude")) slc.annotate_streamlines( ("gas", "velocity_conic_x"), ("gas", "velocity_conic_y") ) assert_fname(slc.save(prefix)[0]) @requires_module("h5py") @requires_file(cyl_2d) @requires_file(cyl_3d) def test_line_integral_convolution_callback(): with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "velocity_x", "velocity_y", "velocity_z"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), ) for ax in "xyz": p = ProjectionPlot(ds, ax, ("gas", "density")) p.annotate_line_integral_convolution( ("gas", "velocity_x"), ("gas", "velocity_y") ) assert_fname(p.save(prefix)[0]) p = ProjectionPlot( ds, ax, ("gas", "density"), weight_field=("gas", "density") ) p.annotate_line_integral_convolution( ("gas", "velocity_x"), ("gas", "velocity_y") ) assert_fname(p.save(prefix)[0]) p = SlicePlot(ds, ax, ("gas", "density")) p.annotate_line_integral_convolution( ("gas", "velocity_x"), ("gas", "velocity_y") ) assert_fname(p.save(prefix)[0]) # Now we'll check a few additional minor things p = SlicePlot(ds, "x", ("gas", "density")) p.annotate_line_integral_convolution( ("gas", "velocity_x"), ("gas", "velocity_y"), kernellen=100.0, lim=(0.4, 0.7), cmap=ytcfg.get("yt", "default_colormap"), alpha=0.9, const_alpha=True, ) p.save(prefix) with _cleanup_fname() as prefix: ds = load(cyl_2d) slc = SlicePlot(ds, "theta", ("gas", "magnetic_field_strength")) slc.annotate_line_integral_convolution( ("gas", "magnetic_field_r"), ("gas", "magnetic_field_z") ) assert_fname(slc.save(prefix)[0]) with _cleanup_fname() as prefix: ds = load(cyl_3d) slc = SlicePlot(ds, "r", ("gas", "magnetic_field_strength")) slc.annotate_line_integral_convolution( ("gas", "magnetic_field_theta"), ("gas", "magnetic_field_z") ) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "z", ("gas", "magnetic_field_strength")) slc.annotate_line_integral_convolution( ("gas", "magnetic_field_cartesian_x"), ("gas", "magnetic_field_cartesian_y") ) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "theta", ("gas", "magnetic_field_strength")) slc.annotate_line_integral_convolution( ("gas", "magnetic_field_r"), ("gas", "magnetic_field_z") ) assert_fname(slc.save(prefix)[0]) check_axis_manipulation(slc, prefix) with _cleanup_fname() as prefix: ds = fake_amr_ds( fields=("density", "velocity_r", "velocity_theta", "velocity_phi"), units=("g/cm**3", "cm/s", "cm/s", "cm/s"), geometry="spherical", ) slc = SlicePlot(ds, "phi", ("gas", "velocity_magnitude")) slc.annotate_line_integral_convolution( ("gas", "velocity_cylindrical_radius"), ("gas", "velocity_cylindrical_z") ) assert_fname(slc.save(prefix)[0]) slc = SlicePlot(ds, "theta", ("gas", "velocity_magnitude")) slc.annotate_line_integral_convolution( ("gas", "velocity_conic_x"), ("gas", "velocity_conic_y") ) assert_fname(slc.save(prefix)[0]) check_axis_manipulation(slc, prefix) def test_accepts_all_fields_decorator(): fields = [ ("gas", "density"), ("gas", "velocity_x"), ("gas", "pressure"), ("gas", "temperature"), ] units = ["g/cm**3", "cm/s", "dyn/cm**2", "K"] ds = fake_random_ds(16, fields=fields, units=units) plot = SlicePlot(ds, "z", fields=fields) # mocking a class method plot.fake_attr = {f: "not set" for f in fields} @accepts_all_fields def set_fake_field_attribute(self, field, value): self.fake_attr[field] = value return self # test on a single field plot = set_fake_field_attribute(plot, field=fields[0], value=1) assert_array_equal([plot.fake_attr[f] for f in fields], [1] + ["not set"] * 3) # test using "all" as a field plot = set_fake_field_attribute(plot, field="all", value=2) assert_array_equal(list(plot.fake_attr.values()), [2] * 4) M7 = "DD0010/moving7_0010" @requires_ds(M7) def test_axis_manipulations(): # tests flip_horizontal, flip_vertical and swap_axes in different combinations # on a SlicePlot with a velocity callback. plot_field = ("gas", "density") decimals = 12 ds = data_dir_load(M7) def simple_velocity(test_obj, plot): # test_obj: the active PlotWindowAttributeTest # plot: the actual PlotWindow plot.annotate_velocity() def swap_axes(test_obj, plot): plot.swap_axes() def flip_horizontal(test_obj, plot): plot.flip_horizontal() def flip_vertical(test_obj, plot): plot.flip_vertical() callback_tests = ( ("flip_horizontal", (simple_velocity, flip_horizontal)), ("flip_vertical", (simple_velocity, flip_vertical)), ("swap_axes", (simple_velocity, swap_axes)), ("flip_and_swap", (simple_velocity, flip_vertical, flip_horizontal, swap_axes)), ) for n, r in callback_tests: test = PlotWindowAttributeTest( ds, plot_field, "x", attr_name=None, attr_args=None, decimals=decimals, callback_id=n, callback_runners=r, ) test_axis_manipulations.__name__ = test.description yield test yt-project-yt-f043ac8/yt/visualization/tests/test_callbacks_geographic.py000066400000000000000000000040701510711153200270720ustar00rootroot00000000000000import numpy as np import pytest from yt import SlicePlot, load_uniform_grid from yt.testing import fake_amr_ds, requires_module @requires_module("cartopy") @pytest.mark.parametrize("geometry", ["geographic", "internal_geographic"]) def test_quiver_callback_geographic(geometry): flds = ("density", "velocity_ew", "velocity_ns") units = ("g/cm**3", "m/s", "m/s") ds = fake_amr_ds(fields=flds, units=units, geometry=geometry) for ax in ds.coordinates.axis_order: slc = SlicePlot(ds, ax, "density", buff_size=(50, 50)) if ax == ds.coordinates.radial_axis: # avoid the exact transform bounds slc.set_width((359.99, 179.99)) slc.annotate_quiver(("stream", "velocity_ew"), ("stream", "velocity_ns")) slc.render() @pytest.fixture() def ds_geo_uni_grid(): yc = 0.0 xc = 0.0 def _vel_calculator(grid, ax): y_lat = grid.fcoords[:, 1].d x_lon = grid.fcoords[:, 2].d x_lon[x_lon > 180] = x_lon[x_lon > 180] - 360.0 dist = np.sqrt((y_lat - yc) ** 2 + (xc - x_lon) ** 2) if ax == 1: sgn = np.sign(y_lat - yc) elif ax == 2: sgn = np.sign(x_lon - xc) vel = np.exp(-((dist / 45) ** 2)) * sgn return vel.reshape(grid.shape) def _calculate_u(grid, field): return _vel_calculator(grid, 2) def _calculate_v(grid, field): return _vel_calculator(grid, 1) data = {"u_vel": _calculate_u, "v_vel": _calculate_v} bbox = [[10.0, 1000], [-90, 90], [0, 360]] bbox = np.array(bbox) ds = load_uniform_grid( data, (16, 16, 32), bbox=bbox, geometry="geographic", axis_order=("altitude", "latitude", "longitude"), ) return ds @requires_module("cartopy") @pytest.mark.mpl_image_compare def test_geoquiver_answer(ds_geo_uni_grid): slc = SlicePlot(ds_geo_uni_grid, "altitude", "u_vel") slc.set_width((359.99, 179.99)) slc.set_log("u_vel", False) slc.annotate_quiver("u_vel", "v_vel", scale=50) slc.render() return slc.plots["u_vel"].figure yt-project-yt-f043ac8/yt/visualization/tests/test_color_maps.py000066400000000000000000000043311510711153200251210ustar00rootroot00000000000000import os import shutil import tempfile import unittest import matplotlib.pyplot as plt import numpy as np from nose.tools import assert_raises from numpy.testing import assert_almost_equal, assert_equal from yt import make_colormap, show_colormaps from yt.testing import requires_backend class TestColorMaps(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) @requires_backend("Agg") def test_show_colormaps(self): show_colormaps() show_colormaps(subset=["jet", "cool"]) show_colormaps(subset="yt_native", filename="yt_color_maps.png") # Test for non-existent color map with assert_raises(AttributeError) as ex: show_colormaps(subset="unknown", filename="yt_color_maps.png") desired = ( "show_colormaps requires subset attribute to be 'all', " "'yt_native', or a list of valid colormap names." ) assert_equal(str(ex.exception), desired) @requires_backend("Agg") def test_make_colormap(self): make_colormap( [([0, 0, 1], 10), ([1, 1, 1], 10), ([1, 0, 0], 10)], name="french_flag", interpolate=False, ) show_colormaps("french_flag") cmap = make_colormap( [("dred", 5), ("blue", 2.0), ("orange", 0)], name="my_cmap" ) assert_almost_equal( cmap["red"][1], np.array([0.00392157, 0.62400345, 0.62400345]) ) assert_almost_equal( cmap["blue"][2], np.array([0.00784314, 0.01098901, 0.01098901]) ) assert_almost_equal(cmap["green"][3], np.array([0.01176471, 0.0, 0.0])) def test_cmyt_integration(): for name in ["algae", "bds_highcontrast", "kelp", "arbre", "octarine", "kamae"]: cmap = plt.get_cmap(name) assert cmap.name == name name_r = name + "_r" cmap_r = plt.get_cmap(name_r) assert cmap_r.name == name_r for name in ["algae", "kelp", "arbre", "octarine", "pastel"]: cmap = plt.get_cmap("cmyt." + name) assert cmap.name == "cmyt." + name yt-project-yt-f043ac8/yt/visualization/tests/test_commons.py000066400000000000000000000040141510711153200244340ustar00rootroot00000000000000import pytest from numpy.testing import assert_raises from yt.visualization._commons import ( _swap_arg_pair_order, _swap_axes_extents, validate_image_name, ) @pytest.mark.parametrize( "name, expected", [ ("noext", "noext.png"), ("nothing.png", "nothing.png"), ("nothing.pdf", "nothing.pdf"), ("version.1.2.3", "version.1.2.3.png"), ], ) def test_default(name, expected): result = validate_image_name(name) assert result == expected @pytest.mark.parametrize( "name, suffix, expected", [ ("noext", ".png", "noext.png"), ("noext", None, "noext.png"), ("nothing.png", ".png", "nothing.png"), ("nothing.png", None, "nothing.png"), ("nothing.png", ".pdf", "nothing.pdf"), ("nothing.pdf", ".pdf", "nothing.pdf"), ("nothing.pdf", None, "nothing.pdf"), ("nothing.pdf", ".png", "nothing.png"), ("version.1.2.3", ".png", "version.1.2.3.png"), ("version.1.2.3", None, "version.1.2.3.png"), ("version.1.2.3", ".pdf", "version.1.2.3.pdf"), ], ) @pytest.mark.filterwarnings( r"ignore:Received two valid image formats '\w+' \(from filename\) " r"and '\w+' \(from suffix\). The former is ignored.:UserWarning" ) def test_custom_valid_ext(name, suffix, expected): result1 = validate_image_name(name, suffix=suffix) assert result1 == expected if suffix is not None: alt_suffix = suffix.replace(".", "") result2 = validate_image_name(name, suffix=alt_suffix) assert result2 == expected def test_extent_swap(): input_extent = [1, 2, 3, 4] expected = [3, 4, 1, 2] assert _swap_axes_extents(input_extent) == expected assert _swap_axes_extents(tuple(input_extent)) == tuple(expected) def test_swap_arg_pair_order(): assert _swap_arg_pair_order(1, 2) == (2, 1) assert _swap_arg_pair_order(1, 2, 3, 4, 5, 6) == (2, 1, 4, 3, 6, 5) assert_raises(TypeError, _swap_arg_pair_order, 1) assert_raises(TypeError, _swap_arg_pair_order, 1, 2, 3) yt-project-yt-f043ac8/yt/visualization/tests/test_eps_writer.py000066400000000000000000000013131510711153200251430ustar00rootroot00000000000000import yt from yt.testing import fake_amr_ds, requires_external_executable, requires_module @requires_external_executable("tex") @requires_module("pyx") def test_eps_writer(tmp_path): import yt.visualization.eps_writer as eps fields = [ ("gas", "density"), ("gas", "temperature"), ] units = [ "g/cm**3", "K", ] ds = fake_amr_ds(fields=fields, units=units) slc = yt.SlicePlot( ds, "z", fields=fields, ) eps_fig = eps.multiplot_yt(2, 1, slc, bare_axes=True) eps_fig.scale_line(0.2, "5 cm") savefile = tmp_path / "multi" eps_fig.save_fig(savefile, format="eps") assert savefile.with_suffix(".eps").exists() yt-project-yt-f043ac8/yt/visualization/tests/test_export_frb.py000066400000000000000000000017101510711153200251330ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_equal from yt.testing import assert_allclose_units, fake_random_ds def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_export_frb(): test_ds = fake_random_ds(128) slc = test_ds.slice(0, 0.5) frb = slc.to_frb((0.5, "unitary"), 64) frb_ds = frb.export_dataset(fields=[("gas", "density")], nprocs=8) dd_frb = frb_ds.all_data() assert_equal(frb_ds.domain_left_edge.v, np.array([0.25, 0.25, 0.0])) assert_equal(frb_ds.domain_right_edge.v, np.array([0.75, 0.75, 1.0])) assert_equal(frb_ds.domain_width.v, np.array([0.5, 0.5, 1.0])) assert_equal(frb_ds.domain_dimensions, np.array([64, 64, 1], dtype="int64")) assert_allclose_units( frb["gas", "density"].sum(), dd_frb.quantities.total_quantity(("gas", "density")), ) assert_equal(frb_ds.index.num_grids, 8) yt-project-yt-f043ac8/yt/visualization/tests/test_filters.py000066400000000000000000000027561510711153200244440ustar00rootroot00000000000000""" Tests for frb filters """ import numpy as np import yt from yt.testing import fake_amr_ds, requires_module @requires_module("scipy") def test_white_noise_filter(): ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ds.proj(("gas", "density"), "z") frb = p.to_frb((1, "unitary"), 64) frb.apply_white_noise() frb.apply_white_noise(1e-3) frb.render(("gas", "density")) @requires_module("scipy") def test_gauss_beam_filter(): ds = fake_amr_ds(fields=("density",), units=("g/cm**3",)) p = ds.proj(("gas", "density"), "z") frb = p.to_frb((1, "unitary"), 64) frb.apply_gauss_beam(sigma=1.0) frb.render(("gas", "density")) @requires_module("scipy") def test_filter_wiring(): ds = fake_amr_ds(fields=[("gas", "density")], units=["g/cm**3"]) p = yt.SlicePlot(ds, "x", "density") # Note: frb is a FixedResolutionBuffer object frb1 = p.frb data_orig = frb1["density"].value sigma = 2 nbeam = 30 p.frb.apply_gauss_beam(nbeam=nbeam, sigma=sigma) frb2 = p.frb data_gauss = frb2["density"].value p.frb.apply_white_noise() frb3 = p.frb data_white = frb3["density"].value # We check the frb objects are different assert frb1 is not frb2 assert frb1 is not frb3 assert frb2 is not frb3 # We check the resulting image are different each time assert not np.allclose(data_orig, data_gauss) assert not np.allclose(data_orig, data_white) assert not np.allclose(data_gauss, data_white) yt-project-yt-f043ac8/yt/visualization/tests/test_fits_image.py000066400000000000000000000246101510711153200250740ustar00rootroot00000000000000import os import shutil import tempfile import numpy as np from numpy.testing import assert_allclose, assert_equal from yt.loaders import load from yt.testing import fake_random_ds, requires_file, requires_module from yt.utilities.on_demand_imports import _astropy from yt.visualization.fits_image import ( FITSImageData, FITSOffAxisProjection, FITSOffAxisSlice, FITSParticleOffAxisProjection, FITSParticleProjection, FITSProjection, FITSSlice, assert_same_wcs, ) from yt.visualization.volume_rendering.off_axis_projection import off_axis_projection @requires_module("astropy") def test_fits_image(): curdir = os.getcwd() tmpdir = tempfile.mkdtemp() os.chdir(tmpdir) fields = ("density", "temperature", "velocity_x", "velocity_y", "velocity_z") units = ("g/cm**3", "K", "cm/s", "cm/s", "cm/s") ds = fake_random_ds( 64, fields=fields, units=units, nprocs=16, length_unit=100.0, particles=10000 ) prj = ds.proj(("gas", "density"), 2) prj_frb = prj.to_frb((0.5, "unitary"), 128) fid1 = prj_frb.to_fits_data( fields=[("gas", "density"), ("gas", "temperature")], length_unit="cm" ) fits_prj = FITSProjection( ds, "z", [ds.fields.gas.density, ("gas", "temperature")], image_res=128, width=(0.5, "unitary"), ) assert_equal(fid1["density"].data, fits_prj["density"].data) assert_equal(fid1["temperature"].data, fits_prj["temperature"].data) fid1.writeto("fid1.fits", overwrite=True) new_fid1 = FITSImageData.from_file("fid1.fits") assert_equal(fid1["density"].data, new_fid1["density"].data) assert_equal(fid1["temperature"].data, new_fid1["temperature"].data) assert_equal(fid1.length_unit, new_fid1.length_unit) assert_equal(fid1.time_unit, new_fid1.time_unit) assert_equal(fid1.mass_unit, new_fid1.mass_unit) assert_equal(fid1.velocity_unit, new_fid1.velocity_unit) assert_equal(fid1.magnetic_unit, new_fid1.magnetic_unit) assert_equal(fid1.current_time, new_fid1.current_time) ds2 = load("fid1.fits") ds2.index assert ("fits", "density") in ds2.field_list assert ("fits", "temperature") in ds2.field_list dw_cm = ds2.domain_width.in_units("cm") assert dw_cm[0].v == 50.0 assert dw_cm[1].v == 50.0 slc = ds.slice(2, 0.5) slc_frb = slc.to_frb((0.5, "unitary"), 128) fid2 = slc_frb.to_fits_data( fields=[("gas", "density"), ("gas", "temperature")], length_unit="cm" ) fits_slc = FITSSlice( ds, "z", [("gas", "density"), ("gas", "temperature")], image_res=128, width=(0.5, "unitary"), ) assert_equal(fid2["density"].data, fits_slc["density"].data) assert_equal(fid2["temperature"].data, fits_slc["temperature"].data) fits_slc2 = FITSSlice( ds, "z", [("gas", "density"), ("gas", "temperature")], image_res=128, width=(0.5, "unitary"), origin="image", ) assert_equal(fits_slc2["density"].data, fits_slc["density"].data) assert_equal(fits_slc2["temperature"].data, fits_slc["temperature"].data) assert fits_slc.wcs.wcs.crval[0] != 0.0 assert fits_slc.wcs.wcs.crval[1] != 0.0 assert fits_slc2.wcs.wcs.crval[0] == 0.0 assert fits_slc2.wcs.wcs.crval[1] == 0.0 dens_img = fid2.pop("density") temp_img = fid2.pop("temperature") combined_fid = FITSImageData.from_images([dens_img, temp_img]) assert_equal(combined_fid.length_unit, dens_img.length_unit) assert_equal(combined_fid.time_unit, dens_img.time_unit) assert_equal(combined_fid.mass_unit, dens_img.mass_unit) assert_equal(combined_fid.velocity_unit, dens_img.velocity_unit) assert_equal(combined_fid.magnetic_unit, dens_img.magnetic_unit) assert_equal(combined_fid.current_time, dens_img.current_time) # Make sure that we can combine FITSImageData instances with more # than one image each combined_fid2 = FITSImageData.from_images([combined_fid, combined_fid]) # Writing the FITS file ensures that we have assembled the images # together correctly combined_fid2.writeto("combined.fits", overwrite=True) cut = ds.cutting([0.1, 0.2, -0.9], [0.5, 0.42, 0.6]) cut_frb = cut.to_frb((0.5, "unitary"), 128) fid3 = cut_frb.to_fits_data( fields=[("gas", "density"), ds.fields.gas.temperature], length_unit="cm" ) fits_cut = FITSOffAxisSlice( ds, [0.1, 0.2, -0.9], [("gas", "density"), ("gas", "temperature")], image_res=128, center=[0.5, 0.42, 0.6], width=(0.5, "unitary"), ) assert_equal(fid3["density"].data, fits_cut["density"].data) assert_equal(fid3["temperature"].data, fits_cut["temperature"].data) fid3.create_sky_wcs([30.0, 45.0], (1.0, "arcsec/kpc")) fid3.writeto("fid3.fits", overwrite=True) new_fid3 = FITSImageData.from_file("fid3.fits") assert_same_wcs(fid3.wcs, new_fid3.wcs) assert new_fid3.wcs.wcs.cunit[0] == "deg" assert new_fid3.wcs.wcs.cunit[1] == "deg" assert new_fid3.wcs.wcs.ctype[0] == "RA---TAN" assert new_fid3.wcs.wcs.ctype[1] == "DEC--TAN" assert new_fid3.wcsa.wcs.cunit[0] == "cm" assert new_fid3.wcsa.wcs.cunit[1] == "cm" assert new_fid3.wcsa.wcs.ctype[0] == "linear" assert new_fid3.wcsa.wcs.ctype[1] == "linear" buf = off_axis_projection( ds, ds.domain_center, [0.1, 0.2, -0.9], 0.5, 128, ("gas", "density") ).swapaxes(0, 1) fid4 = FITSImageData(buf, fields=[("gas", "density")], width=100.0) fits_oap = FITSOffAxisProjection( ds, [0.1, 0.2, -0.9], ("gas", "density"), width=(0.5, "unitary"), image_res=128, depth=(0.5, "unitary"), ) assert_equal(fid4["density"].data, fits_oap["density"].data) fid4.create_sky_wcs([30.0, 45.0], (1.0, "arcsec/kpc"), replace_old_wcs=False) assert fid4.wcs.wcs.cunit[0] == "cm" assert fid4.wcs.wcs.cunit[1] == "cm" assert fid4.wcs.wcs.ctype[0] == "linear" assert fid4.wcs.wcs.ctype[1] == "linear" assert fid4.wcsa.wcs.cunit[0] == "deg" assert fid4.wcsa.wcs.cunit[1] == "deg" assert fid4.wcsa.wcs.ctype[0] == "RA---TAN" assert fid4.wcsa.wcs.ctype[1] == "DEC--TAN" cvg = ds.covering_grid( ds.index.max_level, [0.25, 0.25, 0.25], [32, 32, 32], fields=[("gas", "density"), ("gas", "temperature")], ) fid5 = cvg.to_fits_data(fields=[("gas", "density"), ("gas", "temperature")]) assert fid5.dimensionality == 3 fid5.update_header("density", "time", 0.1) fid5.update_header("all", "units", "cgs") assert fid5["density"].header["time"] == 0.1 assert fid5["temperature"].header["units"] == "cgs" assert fid5["density"].header["units"] == "cgs" fid6 = FITSImageData.from_images(fid5) fid5.change_image_name("density", "mass_per_volume") assert fid5["mass_per_volume"].name == "mass_per_volume" assert fid5["mass_per_volume"].header["BTYPE"] == "mass_per_volume" assert "mass_per_volume" in fid5.fields assert "mass_per_volume" in fid5.field_units assert "density" not in fid5.fields assert "density" not in fid5.field_units assert "density" in fid6.fields assert_equal(fid6["density"].data, fid5["mass_per_volume"].data) fid7 = FITSImageData.from_images(fid4) fid7.convolve("density", (3.0, "cm")) sigma = 3.0 / fid7.wcs.wcs.cdelt[0] kernel = _astropy.conv.Gaussian2DKernel(x_stddev=sigma) data_conv = _astropy.conv.convolve(fid4["density"].data.d, kernel) assert_allclose(data_conv, fid7["density"].data.d) pfid = FITSParticleProjection(ds, "x", ("io", "particle_mass")) assert pfid["particle_mass"].name == "particle_mass" assert pfid["particle_mass"].header["BTYPE"] == "particle_mass" assert pfid["particle_mass"].units == "g" pofid = FITSParticleOffAxisProjection(ds, [1, 1, 1], ("io", "particle_mass")) assert pofid["particle_mass"].name == "particle_mass" assert pofid["particle_mass"].header["BTYPE"] == "particle_mass" assert pofid["particle_mass"].units == "g" pdfid = FITSParticleProjection(ds, "x", ("io", "particle_mass"), density=True) assert pdfid["particle_mass"].name == "particle_mass" assert pdfid["particle_mass"].header["BTYPE"] == "particle_mass" assert pdfid["particle_mass"].units == "g/cm**2" # Test moments for projections def _vysq(field, data): return data["gas", "velocity_y"] ** 2 ds.add_field(("gas", "vysq"), _vysq, sampling_type="cell", units="cm**2/s**2") fid8 = FITSProjection( ds, "y", [("gas", "velocity_y"), ("gas", "vysq")], moment=1, weight_field=("gas", "density"), ) fid9 = FITSProjection( ds, "y", ("gas", "velocity_y"), moment=2, weight_field=("gas", "density") ) sigy = np.sqrt(fid8["vysq"].data - fid8["velocity_y"].data ** 2) assert_allclose(sigy, fid9["velocity_y_stddev"].data) def _vlsq(field, data): return data["gas", "velocity_los"] ** 2 ds.add_field(("gas", "vlsq"), _vlsq, sampling_type="cell", units="cm**2/s**2") fid10 = FITSOffAxisProjection( ds, [1.0, -1.0, 1.0], [("gas", "velocity_los"), ("gas", "vlsq")], moment=1, weight_field=("gas", "density"), ) fid11 = FITSOffAxisProjection( ds, [1.0, -1.0, 1.0], ("gas", "velocity_los"), moment=2, weight_field=("gas", "density"), ) sigl = np.sqrt(fid10["vlsq"].data - fid10["velocity_los"].data ** 2) assert_allclose(sigl, fid11["velocity_los_stddev"].data) # We need to manually close all the file descriptors so # that windows can delete the folder that contains them. ds2.close() for fid in ( fid1, fid2, fid3, fid4, fid5, fid6, fid7, fid8, fid9, new_fid1, new_fid3, pfid, pdfid, pofid, ): fid.close() os.chdir(curdir) shutil.rmtree(tmpdir) etc = "enzo_tiny_cosmology/DD0046/DD0046" @requires_module("astropy") @requires_file(etc) def test_fits_cosmo(): ds = load(etc) fid = FITSProjection(ds, "z", ["density"]) assert fid.wcs.wcs.cunit[0] == "kpc" assert fid.wcs.wcs.cunit[1] == "kpc" assert ds.hubble_constant == fid.hubble_constant assert ds.current_redshift == fid.current_redshift assert fid["density"].header["HUBBLE"] == ds.hubble_constant assert fid["density"].header["REDSHIFT"] == ds.current_redshift fid.close() yt-project-yt-f043ac8/yt/visualization/tests/test_geo_projections.py000066400000000000000000000127571510711153200261670ustar00rootroot00000000000000import unittest import numpy as np import yt from yt.testing import fake_amr_ds, requires_module from yt.visualization.geo_plot_utils import get_mpl_transform, transform_list def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class TestGeoProjections(unittest.TestCase): @requires_module("cartopy") def setUp(self): self.ds = fake_amr_ds(geometry="geographic") # switch off the log plot to avoid some unrelated matplotlib issues f = self.ds._get_field_info(("stream", "Density")) f.take_log = False @requires_module("cartopy") def tearDown(self): del self.ds @requires_module("cartopy") def test_geo_projection_setup(self): from yt.utilities.on_demand_imports import _cartopy as cartopy axis = "altitude" self.slc = yt.SlicePlot(self.ds, axis, ("stream", "Density"), origin="native") assert isinstance(self.slc._projection, cartopy.crs.Mollweide) assert isinstance(self.slc._transform, cartopy.crs.PlateCarree) assert self.ds.coordinates.data_projection[axis] == "Mollweide" assert self.ds.coordinates.data_transform[axis] == "PlateCarree" assert isinstance( self.slc._projection, type(self.slc.plots["stream", "Density"].axes.projection), ) @requires_module("cartopy") def test_geo_projections(self): from yt.utilities.on_demand_imports import _cartopy as cartopy self.slc = yt.SlicePlot( self.ds, "altitude", ("stream", "Density"), origin="native" ) for transform in transform_list: if transform == "UTM": # this requires special arguments so let's skip it continue if transform == "OSNI": # avoid crashes, see https://github.com/SciTools/cartopy/issues/1177 continue self.slc.set_mpl_projection(transform) proj_type = type(get_mpl_transform(transform)) assert isinstance(self.slc._projection, proj_type) assert isinstance(self.slc._transform, cartopy.crs.PlateCarree) assert isinstance( self.slc.plots["stream", "Density"].axes.projection, proj_type ) @requires_module("cartopy") def test_projection_object(self): from yt.utilities.on_demand_imports import _cartopy as cartopy shortlist = ["Orthographic", "PlateCarree", "Mollweide"] for transform in shortlist: projection = get_mpl_transform(transform) proj_type = type(projection) self.slc = yt.SlicePlot( self.ds, "altitude", ("stream", "Density"), origin="native" ) self.slc.set_mpl_projection(projection) assert isinstance(self.slc._projection, proj_type) assert isinstance(self.slc._transform, cartopy.crs.PlateCarree) assert isinstance( self.slc.plots["stream", "Density"].axes.projection, proj_type ) @requires_module("cartopy") def test_nondefault_transform(self): from yt.utilities.on_demand_imports import _cartopy as cartopy axis = "altitude" # Note: The Miller transform has an extent of approx. +/- 180 in x, # +/-132 in y (in Miller, x is longitude, y is a factor of latitude). # So by changing the projection in this way, the dataset goes from # covering the whole globe (+/- 90 latitude), to covering part of the # globe (+/-72 latitude). Totally fine for testing that the code runs, # but good to be aware of! self.ds.coordinates.data_transform[axis] = "Miller" self.slc = yt.SlicePlot(self.ds, axis, ("stream", "Density"), origin="native") shortlist = ["Orthographic", "PlateCarree", "Mollweide"] for transform in shortlist: self.slc.set_mpl_projection(transform) proj_type = type(get_mpl_transform(transform)) assert isinstance(self.slc._projection, proj_type) assert isinstance(self.slc._transform, cartopy.crs.Miller) assert self.ds.coordinates.data_projection[axis] == "Mollweide" assert self.ds.coordinates.data_transform[axis] == "Miller" assert isinstance( self.slc.plots["stream", "Density"].axes.projection, proj_type ) @requires_module("cartopy") def test_extent(self): # checks that the axis extent is narrowed when doing a subselection axis = "altitude" slc = yt.SlicePlot(self.ds, axis, ("stream", "Density"), origin="native") ax = slc.plots["stream", "Density"].axes full_extent = np.abs(ax.get_extent()) slc = yt.SlicePlot( self.ds, axis, ("stream", "Density"), origin="native", width=(80.0, 50.0) ) ax = slc.plots["stream", "Density"].axes extent = np.abs(ax.get_extent()) assert np.all(extent < full_extent) class TestNonGeoProjections(unittest.TestCase): def setUp(self): self.ds = fake_amr_ds() def tearDown(self): del self.ds del self.slc def test_projection_setup(self): axis = "x" self.slc = yt.SlicePlot(self.ds, axis, ("stream", "Density"), origin="native") assert self.ds.coordinates.data_projection[axis] is None assert self.ds.coordinates.data_transform[axis] is None assert self.slc._projection is None assert self.slc._transform is None yt-project-yt-f043ac8/yt/visualization/tests/test_image_comp_2D_plots.py000066400000000000000000000407671510711153200266460ustar00rootroot00000000000000# image tests using pytest-mpl from itertools import chain import matplotlib as mpl import numpy as np import numpy.testing as npt import pytest from matplotlib.colors import SymLogNorm from yt.data_objects.profiles import create_profile from yt.loaders import load_uniform_grid from yt.testing import add_noise_fields, fake_amr_ds, fake_particle_ds, fake_random_ds from yt.visualization.api import ( LinePlot, ParticleProjectionPlot, PhasePlot, ProfilePlot, SlicePlot, ) def test_sliceplot_set_unit_and_zlim_order(): ds = fake_random_ds(16) field = ("gas", "density") p0 = SlicePlot(ds, "z", field) p0.set_unit(field, "kg/m**3") p0.set_zlim(field, zmin=0) # reversing order of operations p1 = SlicePlot(ds, "z", field) p1.set_zlim(field, zmin=0) p1.set_unit(field, "kg/m**3") p0.render() p1.render() im0 = p0.plots[field].image._A im1 = p1.plots[field].image._A npt.assert_allclose(im0, im1) def test_annotation_parse_h(): ds = fake_random_ds(16) # Make sure `h` (reduced Hubble constant) is not equal to 1 ds.unit_registry.modify("h", 0.7) rad = ds.quan(0.1, "cm/h") center = ds.arr([0.5] * 3, "code_length") # Twice the same slice plot p1 = SlicePlot(ds, "x", "density") p2 = SlicePlot(ds, "x", "density") # But the *same* center is given in different units p1.annotate_sphere(center.to("cm"), rad, circle_args={"color": "black"}) p2.annotate_sphere(center.to("cm/h"), rad, circle_args={"color": "black"}) # Render annotations, and extract matplotlib image # as an RGB array p1.render() p1.plots["gas", "density"].figure.canvas.draw() img1 = p1.plots["gas", "density"].figure.canvas.renderer.buffer_rgba() p2.render() p2.plots["gas", "density"].figure.canvas.draw() img2 = p2.plots["gas", "density"].figure.canvas.renderer.buffer_rgba() # This should be the same image npt.assert_allclose(img1, img2) @pytest.mark.mpl_image_compare def test_inf_and_finite_values_set_zlim(): # see https://github.com/yt-project/yt/issues/3901 shape = (32, 16, 1) a = np.ones(16) b = np.ones((32, 16)) c = np.reshape(a * b, shape) # injecting an inf c[0, 0, 0] = np.inf field = ("gas", "density") data = {field: c} ds = load_uniform_grid( data, shape, bbox=np.array([[0, 1], [0, 1], [0, 1]]), ) p = SlicePlot(ds, "z", field) # setting zlim manually p.set_zlim(field, -10, 10) p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_sliceplot_custom_norm(): from matplotlib.colors import TwoSlopeNorm ds = fake_random_ds(16) field = ("gas", "density") p = SlicePlot(ds, "z", field) p.set_norm(field, norm=TwoSlopeNorm(vcenter=0, vmin=-0.5, vmax=1)) p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_sliceplot_custom_norm_symlog_int_base(): ds = fake_random_ds(16) add_noise_fields(ds) field = "noise3" p = SlicePlot(ds, "z", field) # using integer base !=10 and >2 to exercise special case # for colorbar minor ticks p.set_norm(field, norm=SymLogNorm(linthresh=0.1, base=5)) p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_lineplot_set_axis_properties(): ds = fake_random_ds(16) field = ("gas", "density") p = LinePlot( ds, field, start_point=[0, 0, 0], end_point=[1, 1, 1], npoints=32, ) p.set_x_unit("cm") p.set_unit(field, "kg/cm**3") p.set_log(field, False) p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_profileplot_set_axis_properties(): ds = fake_random_ds(16) disk = ds.disk(ds.domain_center, [0.0, 0.0, 1.0], (10, "m"), (1, "m")) p = ProfilePlot(disk, ("gas", "density"), [("gas", "velocity_x")]) p.set_unit(("gas", "density"), "kg/cm**3") p.set_log(("gas", "density"), False) p.set_unit(("gas", "velocity_x"), "mile/hour") p.render() return p.plots["gas", "velocity_x"].figure @pytest.mark.mpl_image_compare def test_particleprojectionplot_set_colorbar_properties(): ds = fake_particle_ds(npart=100) field = ("all", "particle_mass") p = ParticleProjectionPlot(ds, 2, field) p.set_buff_size(10) p.set_unit(field, "Msun") p.set_zlim(field, zmax=1e-35) p.set_log(field, False) p.render() return p.plots[field].figure class TestMultipanelPlot: @classmethod def setup_class(cls): cls.fields = [ ("gas", "density"), ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_magnitude"), ] cls.ds = fake_random_ds(16) @pytest.mark.skipif( mpl.__version_info__ < (3, 7), reason="colorbar cannot currently be set horizontal in multi-panel plot with matplotlib older than 3.7.0", ) @pytest.mark.parametrize("cbar_location", ["top", "bottom", "left", "right"]) @pytest.mark.mpl_image_compare def test_multipanelplot_colorbar_orientation_simple(self, cbar_location): p = SlicePlot(self.ds, "z", self.fields) return p.export_to_mpl_figure((2, 2), cbar_location=cbar_location) @pytest.mark.parametrize("cbar_location", ["top", "bottom"]) def test_multipanelplot_colorbar_orientation_warning(self, cbar_location): p = SlicePlot(self.ds, "z", self.fields) if mpl.__version_info__ < (3, 7): with pytest.warns( UserWarning, match="Cannot properly set the orientation of colorbar.", ): p.export_to_mpl_figure((2, 2), cbar_location=cbar_location) else: p.export_to_mpl_figure((2, 2), cbar_location=cbar_location) class TestProfilePlot: @classmethod def setup_class(cls): fields = ("density", "temperature", "velocity_x", "velocity_y", "velocity_z") units = ("g/cm**3", "K", "cm/s", "cm/s", "cm/s") cls.ds = fake_random_ds(16, fields=fields, units=units) regions = [cls.ds.region([0.5] * 3, [0.4] * 3, [0.6] * 3), cls.ds.all_data()] pr_fields = [ [("gas", "density"), ("gas", "temperature")], [("gas", "density"), ("gas", "velocity_x")], [("gas", "temperature"), ("gas", "mass")], [("gas", "density"), ("index", "radius")], [("gas", "velocity_magnitude"), ("gas", "mass")], ] cls.profiles: dict[str, ProfilePlot] = {} for i_reg, reg in enumerate(regions): id_prefix = str(i_reg) for x_field, y_field in pr_fields: id_suffix = "_".join([*x_field, *y_field]) base_id = f"{id_prefix}_{id_suffix}" cls.profiles[base_id] = ProfilePlot(reg, x_field, y_field) cls.profiles[f"{base_id}_fractional_accumulation"] = ProfilePlot( reg, x_field, y_field, fractional=True, accumulation=True ) p1d = create_profile(reg, x_field, y_field) cls.profiles[f"{base_id}_from_profiles"] = ProfilePlot.from_profiles( p1d ) p1 = create_profile( cls.ds.all_data(), ("gas", "density"), ("gas", "temperature") ) p2 = create_profile( cls.ds.all_data(), ("gas", "density"), ("gas", "velocity_x") ) cls.profiles["from_multiple_profiles"] = ProfilePlot.from_profiles( [p1, p2], labels=["temperature", "velocity"] ) @pytest.mark.parametrize( "suffix", [None, "from_profiles", "fractional_accumulation"], ) @pytest.mark.parametrize("region", ["0", "1"]) @pytest.mark.parametrize( "xax, yax", [ (("gas", "density"), ("gas", "temperature")), (("gas", "density"), ("gas", "velocity_x")), (("gas", "temperature"), ("gas", "mass")), (("gas", "density"), ("index", "radius")), (("gas", "velocity_magnitude"), ("gas", "mass")), ], ) @pytest.mark.mpl_image_compare def test_profileplot_simple(self, region, xax, yax, suffix): key = "_".join(chain(region, xax, yax)) if suffix is not None: key += f"_{suffix}" plots = list(self.profiles[key].plots.values()) assert len(plots) == 1 return plots[0].figure @pytest.mark.mpl_image_compare def test_profileplot_from_multiple_profiles_0(self): plots = list(self.profiles["from_multiple_profiles"].plots.values()) assert len(plots) == 2 return plots[0].figure @pytest.mark.mpl_image_compare def test_profileplot_from_multiple_profiles_1(self): plots = list(self.profiles["from_multiple_profiles"].plots.values()) assert len(plots) == 2 return plots[1].figure class TestPhasePlot: @classmethod def setup_class(cls): fields = ("density", "temperature", "velocity_x", "velocity_y", "velocity_z") units = ("g/cm**3", "K", "cm/s", "cm/s", "cm/s") cls.ds = fake_random_ds(16, fields=fields, units=units) regions = [cls.ds.region([0.5] * 3, [0.4] * 3, [0.6] * 3), cls.ds.all_data()] pr_fields = [ [("gas", "density"), ("gas", "temperature"), ("gas", "mass")], [("gas", "density"), ("gas", "velocity_x"), ("gas", "mass")], [ ("index", "radius"), ("gas", "temperature"), ("gas", "velocity_magnitude"), ], ] cls.profiles: dict[str, PhasePlot] = {} for i_reg, reg in enumerate(regions): id_prefix = str(i_reg) for x_field, y_field, z_field in pr_fields: id_suffix = "_".join([*x_field, *y_field, *z_field]) base_id = f"{id_prefix}_{id_suffix}" cls.profiles[base_id] = PhasePlot( reg, x_field, y_field, z_field, x_bins=16, y_bins=16 ) cls.profiles[f"{base_id}_fractional_accumulation"] = PhasePlot( reg, x_field, y_field, z_field, fractional=True, accumulation=True, x_bins=16, y_bins=16, ) p2d = create_profile(reg, [x_field, y_field], z_field, n_bins=[16, 16]) cls.profiles[f"{base_id}_from_profiles"] = PhasePlot.from_profile(p2d) @pytest.mark.parametrize( "suffix", [None, "from_profiles", "fractional_accumulation"], ) @pytest.mark.parametrize("region", ["0", "1"]) @pytest.mark.parametrize( "xax, yax, zax", [ (("gas", "density"), ("gas", "temperature"), ("gas", "mass")), (("gas", "density"), ("gas", "velocity_x"), ("gas", "mass")), ( ("index", "radius"), ("gas", "temperature"), ("gas", "velocity_magnitude"), ), ], ) @pytest.mark.mpl_image_compare def test_phaseplot(self, region, xax, yax, zax, suffix): key = "_".join(chain(region, xax, yax, zax)) if suffix is not None: key += f"_{suffix}" plots = list(self.profiles[key].plots.values()) assert len(plots) == 1 return plots[0].figure class TestPhasePlotSetZlim: @classmethod def setup_class(cls): cls.ds = fake_random_ds(16) add_noise_fields(cls.ds) cls.data = cls.ds.sphere("c", 1) @pytest.mark.mpl_image_compare def test_phaseplot_set_zlim_with_implicit_units(self): p = PhasePlot( self.data, ("gas", "noise1"), ("gas", "noise3"), [("gas", "density")], weight_field=None, ) field = ("gas", "density") p.set_zlim(field, zmax=10) p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_phaseplot_set_zlim_with_explicit_units(self): p = PhasePlot( self.data, ("gas", "noise1"), ("gas", "noise3"), [("gas", "density")], weight_field=None, ) field = ("gas", "density") # using explicit units, we expect the colorbar units to stay unchanged p.set_zlim(field, zmin=(1e36, "kg/AU**3")) p.render() return p.plots[field].figure class TestSetBackgroundColor: # see https://github.com/yt-project/yt/issues/3854 @classmethod def setup_class(cls): cls.ds = fake_random_ds(16) def some_nans_field(field, data): ret = data["gas", "density"] ret[::2] *= np.nan return ret cls.ds.add_field( name=("gas", "polluted_field"), function=some_nans_field, sampling_type="local", ) @pytest.mark.mpl_image_compare def test_sliceplot_set_background_color_linear(self): field = ("gas", "density") p = SlicePlot(self.ds, "z", field, width=1.5) p.set_background_color(field, color="C0") p.set_log(field, False) p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_sliceplot_set_background_color_log(self): field = ("gas", "density") p = SlicePlot(self.ds, "z", field, width=1.5) p.set_background_color(field, color="C0") p.render() return p.plots[field].figure @pytest.mark.mpl_image_compare def test_sliceplot_set_background_color_and_bad_value(self): # see https://github.com/yt-project/yt/issues/4639 field = ("gas", "polluted_field") p = SlicePlot(self.ds, "z", field, width=1.5) p.set_background_color(field, color="black") # copy the default colormap cmap = mpl.colormaps["cmyt.arbre"].with_extremes(bad="red") p.set_cmap(field, cmap) p.render() return p.plots[field].figure class TestCylindricalZSlicePlot: @classmethod def setup_class(cls): cls.ds = fake_amr_ds(geometry="cylindrical") add_noise_fields(cls.ds) fields = ["noise%d" % i for i in range(4)] cls.plot = SlicePlot(cls.ds, "z", fields) @pytest.mark.parametrize("field", ["noise0", "noise1", "noise2", "noise3"]) @pytest.mark.mpl_image_compare def test_cylindrical_z_log(self, field): return self.plot.plots[field].figure @pytest.mark.parametrize("field", ["noise0", "noise1", "noise2", "noise3"]) @pytest.mark.mpl_image_compare def test_cylindrical_z_linear(self, field): self.plot.set_log("noise0", False) return self.plot.plots[field].figure @pytest.mark.parametrize( "theta_min, theta_max", [ pytest.param(0, 2 * np.pi, id="full_azimuthal_domain"), pytest.param(3 / 4 * np.pi, 5 / 4 * np.pi, id="restricted_sector"), ], ) @pytest.mark.mpl_image_compare def test_exclude_pixels_with_partial_bbox_intersection(self, theta_min, theta_max): rmin = 1.0 rmax = 2.0 ds = fake_amr_ds( geometry="cylindrical", domain_left_edge=[rmin, 0, theta_min], domain_right_edge=[rmax, 1, theta_max], ) add_noise_fields(ds) plot = SlicePlot(ds, "z", ("gas", "noise0")) for radius in [rmin - 0.01, rmax]: plot.annotate_sphere( center=[0, 0, 0], radius=radius, circle_args={"color": "red", "alpha": 0.4, "linewidth": 3}, ) plot.annotate_title("all pixels beyond (or on) red lines should be white") plot.render() return plot.plots["gas", "noise0"].figure class TestSphericalPhiSlicePlot: @classmethod def setup_class(cls): cls.ds = fake_amr_ds(geometry="spherical") add_noise_fields(cls.ds) fields = ["noise%d" % i for i in range(4)] cls.plot = SlicePlot(cls.ds, "phi", fields) @pytest.mark.parametrize("field", ["noise0", "noise1", "noise2", "noise3"]) @pytest.mark.mpl_image_compare def test_spherical_phi_log(self, field): return self.plot.plots[field].figure class TestSphericalThetaSlicePlot: @classmethod def setup_class(cls): cls.ds = fake_amr_ds(geometry="spherical") add_noise_fields(cls.ds) fields = ["noise%d" % i for i in range(4)] cls.plot = SlicePlot(cls.ds, "theta", fields) @pytest.mark.parametrize("field", ["noise0", "noise1", "noise2", "noise3"]) @pytest.mark.mpl_image_compare def test_spherical_theta_log(self, field): return self.plot.plots[field].figure yt-project-yt-f043ac8/yt/visualization/tests/test_image_comp_geo.py000066400000000000000000000033121510711153200257130ustar00rootroot00000000000000import pytest import yt from yt.testing import fake_amr_ds, requires_module_pytest as requires_module class TestGeoTransform: # Cartopy require pykdtree *or* scipy to enable the projections # we test here. We require scipy for simplicity because it offers # better support for various platforms via PyPI at the time of writing.abs # the following projections are skipped (reason) # - UTM (requires additional arguments) # - OSNI (avoid crashes, see https://github.com/SciTools/cartopy/issues/1177) @classmethod def setup_class(cls): cls.ds = fake_amr_ds(geometry="geographic") @requires_module("cartopy", "scipy") @pytest.mark.parametrize( "transform", [ "PlateCarree", "LambertConformal", "LambertCylindrical", "Mercator", "Miller", "Mollweide", "Orthographic", "Robinson", "Stereographic", "TransverseMercator", "InterruptedGoodeHomolosine", "RotatedPole", "OSGB", "EuroPP", "Geostationary", "Gnomonic", "NorthPolarStereo", "SouthPolarStereo", "AlbersEqualArea", "AzimuthalEquidistant", "Sinusoidal", "NearsidePerspective", "LambertAzimuthalEqualArea", ], ) @pytest.mark.mpl_image_compare def test_geo_tranform(self, transform): field = ("stream", "Density") sl = yt.SlicePlot(self.ds, "altitude", field, origin="native") sl.set_mpl_projection(transform) sl.set_log(field, False) sl.render() return sl.plots[field].figure yt-project-yt-f043ac8/yt/visualization/tests/test_image_writer.py000066400000000000000000000047171510711153200254510ustar00rootroot00000000000000import os import shutil import tempfile import unittest import numpy as np from nose.tools import assert_raises from numpy.testing import assert_equal from yt.testing import fake_random_ds from yt.visualization.image_writer import ( apply_colormap, multi_image_composite, splat_points, write_bitmap, ) class TestImageWriter(unittest.TestCase): @classmethod def setUpClass(cls): cls.tmpdir = tempfile.mkdtemp() cls.curdir = os.getcwd() os.chdir(cls.tmpdir) @classmethod def tearDownClass(cls): os.chdir(cls.curdir) shutil.rmtree(cls.tmpdir) def test_multi_image_composite(self): ds = fake_random_ds(64, nprocs=4, particles=16**3) center = [0.5, 0.5, 0.5] normal = [1, 1, 1] cut = ds.cutting(normal, center) frb = cut.to_frb((0.75, "unitary"), 64) multi_image_composite( "multi_channel1.png", frb["index", "x"], frb["index", "y"] ) # Test multi_image_composite with user specified scaling values mi = ds.quan(0.1, "code_length") ma = ds.quan(0.9, "code_length") multi_image_composite( "multi_channel2.png", (frb["index", "x"], mi, ma), [frb["index", "y"], mi, None], green_channel=frb["index", "z"], alpha_channel=frb["gas", "density"], ) # Test with numpy integer array x = np.array(np.random.randint(0, 256, size=(10, 10)), dtype="uint8") y = np.array(np.random.randint(0, 256, size=(10, 10)), dtype="uint8") multi_image_composite("multi_channel3.png", x, y) def test_write_bitmap(self): image = np.zeros([16, 16, 4], dtype="uint8") xs = np.random.rand(100) ys = np.random.rand(100) image = splat_points(image, xs, ys) png_str = write_bitmap(image.copy(), None) image_trans = image.swapaxes(0, 1).copy(order="C") png_str_trans = write_bitmap(image_trans, None, transpose=True) assert_equal(png_str, png_str_trans) with assert_raises(RuntimeError) as ex: write_bitmap(np.ones([16, 16]), None) desired = "Expecting image array of shape (N,M,3) or (N,M,4), received (16, 16)" assert_equal(str(ex.exception)[:50], desired[:50]) def test_apply_colormap(self): x = np.array(np.random.randint(0, 256, size=(10, 10)), dtype="uint8") apply_colormap(x, color_bounds=None, cmap_name=None, func=lambda x: x**2) yt-project-yt-f043ac8/yt/visualization/tests/test_invalid_origin.py000066400000000000000000000116261510711153200257650ustar00rootroot00000000000000import re import pytest from yt.testing import fake_amr_ds from yt.visualization.plot_window import SlicePlot @pytest.mark.parametrize( ("origin", "msg"), [ ( "ONE", ( "Invalid origin argument. " "Single element specification must be 'window', 'domain', or 'native'. " "Got 'ONE'" ), ), ( "ONE-TWO", ( "Invalid origin argument. " "Using 2 elements:\n" " - the first one must be 'left', 'right', 'lower', 'upper' or 'center'\n" " - the second one must be 'window', 'domain', or 'native'\n" "Got 'ONE-TWO'" ), ), ( "ONE-window", ( "Invalid origin argument. " "Using 2 elements:\n" " - the first one must be 'left', 'right', 'lower', 'upper' or 'center'\n" "Got 'ONE-window'" ), ), ( "left-TWO", ( "Invalid origin argument. " "Using 2 elements:\n" " - the second one must be 'window', 'domain', or 'native'\n" "Got 'left-TWO'" ), ), ( "ONE-TWO-THREE", ( "Invalid origin argument. " "Using 3 elements:\n" " - the first one must be 'lower', 'upper' or 'center' or a distance\n" " - the second one must be 'left', 'right', 'center' or a distance\n" " - the third one must be 'window', 'domain', or 'native'\n" "Got 'ONE-TWO-THREE'" ), ), ( "ONE-TWO-window", ( "Invalid origin argument. " "Using 3 elements:\n" " - the first one must be 'lower', 'upper' or 'center' or a distance\n" " - the second one must be 'left', 'right', 'center' or a distance\n" "Got 'ONE-TWO-window'" ), ), ( "ONE-left-window", ( "Invalid origin argument. " "Using 3 elements:\n" " - the first one must be 'lower', 'upper' or 'center' or a distance\n" "Got 'ONE-left-window'" ), ), ( "ONE-left-THREE", ( "Invalid origin argument. " "Using 3 elements:\n" " - the first one must be 'lower', 'upper' or 'center' or a distance\n" " - the third one must be 'window', 'domain', or 'native'\n" "Got 'ONE-left-THREE'" ), ), ( "lower-left-THREE", ( "Invalid origin argument. " "Using 3 elements:\n" " - the third one must be 'window', 'domain', or 'native'\n" "Got 'lower-left-THREE'" ), ), ( ("ONE", "TWO", "THREE"), ( "Invalid origin argument. " "Using 3 elements:\n" " - the first one must be 'lower', 'upper' or 'center' or a distance\n" " - the second one must be 'left', 'right', 'center' or a distance\n" " - the third one must be 'window', 'domain', or 'native'\n" "Got ('ONE', 'TWO', 'THREE')" ), ), ( ("ONE", "TWO", (1, 1, 3)), ( "Invalid origin argument. " "Using 3 elements:\n" " - the first one must be 'lower', 'upper' or 'center' or a distance\n" " - the second one must be 'left', 'right', 'center' or a distance\n" " - the third one must be 'window', 'domain', or 'native'\n" "Got ('ONE', 'TWO', (1, 1, 3))" ), ), ( "ONE-TWO-THREE-FOUR", ( "Invalid origin argument with too many elements; " "expected 1, 2 or 3 elements, got 'ONE-TWO-THREE-FOUR', counting 4 elements. " "Use '-' as a separator for string arguments." ), ), ], ) def test_invalidate_origin_value(origin, msg): ds = fake_amr_ds(fields=[("gas", "density")], units=["g*cm**-3"]) with pytest.raises(ValueError, match=re.escape(msg)): SlicePlot(ds, "z", ("gas", "density"), origin=origin) @pytest.mark.parametrize( "origin", # don't attempt to match exactly a TypeError message because it should be # emitted by unyt, not yt [ ((50, 50, 50), "TWO", "THREE"), ("ONE", (50, 50, 50), "THREE"), ], ) def test_invalidate_origin_type(origin): ds = fake_amr_ds(fields=[("gas", "density")], units=["g*cm**-3"]) with pytest.raises(TypeError): SlicePlot(ds, "z", ("gas", "density"), origin=origin) yt-project-yt-f043ac8/yt/visualization/tests/test_line_annotation_unit.py000066400000000000000000000017331510711153200272060ustar00rootroot00000000000000import numpy as np from yt.loaders import load_uniform_grid from yt.visualization.plot_window import ProjectionPlot def test_ds_arr_invariance_under_projection_plot(tmp_path): data_array = np.random.random((10, 10, 10)) bbox = np.array([[-100, 100], [-100, 100], [-100, 100]]) data = {("gas", "density"): (data_array, "g*cm**(-3)")} ds = load_uniform_grid(data, data_array.shape, length_unit="kpc", bbox=bbox) start_source = np.array((0, 0, -0.5)) end_source = np.array((0, 0, 0.5)) start = ds.arr(start_source, "unitary") end = ds.arr(end_source, "unitary") start_i = start.copy() end_i = end.copy() p = ProjectionPlot(ds, 0, "number_density") p.annotate_line(start, end) p.save(tmp_path) # for lack of a unyt.testing.assert_unit_array_equal function np.testing.assert_array_equal(start_i, start) assert start_i.units == start.units np.testing.assert_array_equal(end_i, end) assert end_i.units == end.units yt-project-yt-f043ac8/yt/visualization/tests/test_line_plots.py000066400000000000000000000065551510711153200251450ustar00rootroot00000000000000import pytest from numpy.testing import assert_equal import yt from yt.testing import fake_random_ds from yt.visualization.line_plot import _validate_point def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class TestLinePlotSimple: @classmethod def setup_class(cls): cls.ds = fake_random_ds(4) fields = [field for field in cls.ds.field_list if field[0] == "stream"] field_labels = {f: f[1] for f in fields} plot = yt.LinePlot( cls.ds, fields, (0, 0, 0), (1, 1, 0), 1000, field_labels=field_labels ) plot.annotate_legend(fields[0]) plot.annotate_legend(fields[1]) plot.set_x_unit("cm") plot.set_unit(fields[0], "kg/cm**3") plot.annotate_title(fields[0], "Density Plot") plot.render() cls.plot = plot @pytest.mark.mpl_image_compare def test_lineplot_simple_density(self): return self.plot.plots["stream", "density"].figure @pytest.mark.mpl_image_compare def test_lineplot_simple_velocity_x(self): return self.plot.plots["stream", "velocity_x"].figure class TestLinePlotMulti: @classmethod def setup_class(cls): cls.ds = fake_random_ds(4) fields = [field for field in cls.ds.field_list if field[0] == "stream"] field_labels = {f: f[1] for f in fields} lines = [] lines.append( yt.LineBuffer(cls.ds, [0.25, 0, 0], [0.25, 1, 0], 100, label="x = 0.5") ) lines.append( yt.LineBuffer(cls.ds, [0.5, 0, 0], [0.5, 1, 0], 100, label="x = 0.5") ) plot = yt.LinePlot.from_lines(cls.ds, fields, lines, field_labels=field_labels) plot.annotate_legend(fields[0]) plot.annotate_legend(fields[1]) plot.set_x_unit("cm") plot.set_unit(fields[0], "kg/cm**3") plot.annotate_title(fields[0], "Density Plot") plot.render() cls.plot = plot @pytest.mark.mpl_image_compare def test_lineplot_multi_density(self): return self.plot.plots["stream", "density"].figure @pytest.mark.mpl_image_compare def test_lineplot_multi_velocity_x(self): return self.plot.plots["stream", "velocity_x"].figure def test_line_buffer(): ds = fake_random_ds(32) lb = yt.LineBuffer(ds, (0, 0, 0), (1, 1, 1), 512, label="diag") lb["gas", "density"] lb["gas", "velocity_x"] assert_equal(lb["gas", "density"].size, 512) lb["gas", "density"] = 0 assert_equal(lb["gas", "density"], 0) assert_equal(set(lb.keys()), {("gas", "density"), ("gas", "velocity_x")}) del lb["gas", "velocity_x"] assert_equal(set(lb.keys()), {("gas", "density")}) def test_validate_point(): ds = fake_random_ds(3) with pytest.raises(RuntimeError, match="Input point must be array-like"): _validate_point(0, ds, start=True) with pytest.raises(RuntimeError, match="Input point must be a 1D array"): _validate_point(ds.arr([[0], [1]], "code_length"), ds, start=True) with pytest.raises( RuntimeError, match="Input point must have an element for each dimension" ): _validate_point(ds.arr([0, 1], "code_length"), ds, start=True) ds = fake_random_ds([32, 32, 1]) _validate_point(ds.arr([0, 1], "code_length"), ds, start=True) _validate_point(ds.arr([0, 1], "code_length"), ds) yt-project-yt-f043ac8/yt/visualization/tests/test_mesh_slices.py000066400000000000000000000107251510711153200252650ustar00rootroot00000000000000import numpy as np import pytest import yt from yt.testing import fake_hexahedral_ds, fake_tetrahedral_ds, small_fake_hexahedral_ds from yt.utilities.lib.geometry_utils import triangle_plane_intersect from yt.utilities.lib.mesh_triangulation import triangulate_indices def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class TestTetrahedral: @classmethod def setup_class(cls): cls.ds = fake_tetrahedral_ds() cls.mesh = cls.ds.index.meshes[0] cls.ad = cls.ds.all_data() cls.slices = [ cls.ds.slice(idir, cls.ds.domain_center[idir]) for idir in range(3) ] cls.sps = [yt.SlicePlot(cls.ds, idir, cls.ds.field_list) for idir in range(3)] for sp in cls.sps: sp.annotate_mesh_lines() sp.set_log("all", False) sp.render() @pytest.mark.parametrize("idir", range(3)) def test_mesh_selection(self, idir): sl_obj = self.slices[idir] for field in self.ds.field_list: assert sl_obj[field].shape[0] == self.mesh.count(sl_obj.selector) assert sl_obj[field].shape[0] < self.ad[field].shape[0] @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_tetraheadral_all_elem(self, ax): return self.sps[ax].plots["all", "elem"].figure @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_tetraheadral_all_test(self, ax): return self.sps[ax].plots["all", "test"].figure @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_tetraheadral_connect1_elem(self, ax): return self.sps[ax].plots["connect1", "elem"].figure @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_tetraheadral_connect1_test(self, ax): return self.sps[ax].plots["connect1", "test"].figure class TestHexahedral: @classmethod def setup_class(cls): cls.ds = fake_hexahedral_ds() cls.mesh = cls.ds.index.meshes[0] cls.ad = cls.ds.all_data() cls.slices = [ cls.ds.slice(idir, cls.ds.domain_center[idir]) for idir in range(3) ] cls.sps = [yt.SlicePlot(cls.ds, idir, cls.ds.field_list) for idir in range(3)] for sp in cls.sps: sp.annotate_mesh_lines() sp.set_log("all", False) sp.render() @pytest.mark.parametrize("idir", range(3)) def test_mesh_selection(self, idir): sl_obj = self.slices[idir] for field in self.ds.field_list: assert sl_obj[field].shape[0] == self.mesh.count(sl_obj.selector) assert sl_obj[field].shape[0] < self.ad[field].shape[0] @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_hexaheadral_all_elem(self, ax): return self.sps[ax].plots["all", "elem"].figure @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_hexaheadral_all_test(self, ax): return self.sps[ax].plots["all", "test"].figure @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_hexaheadral_connect1_elem(self, ax): return self.sps[ax].plots["connect1", "elem"].figure @pytest.mark.parametrize("ax", range(3)) @pytest.mark.mpl_image_compare def test_mesh_slice_hexaheadral_connect1_test(self, ax): return self.sps[ax].plots["connect1", "test"].figure def test_perfect_element_intersection(): # This test tests mesh line annotation where a z=0 slice # perfectly intersects the top of a hexahedral element with node # z-coordinates containing both -0 and +0. Before # https://github.com/yt-project/yt/pull/1437 this test falsely # yielded three annotation lines, whereas the correct result is four # corresponding to the four edges of the top hex face. ds = small_fake_hexahedral_ds() indices = ds.index.meshes[0].connectivity_indices coords = ds.index.meshes[0].connectivity_coords tri_indices = triangulate_indices(indices) tri_coords = coords[tri_indices] lines = triangle_plane_intersect(2, 0, tri_coords) non_zero_lines = 0 for i in range(lines.shape[0]): norm = np.linalg.norm(lines[i][0] - lines[i][1]) if norm > 1e-8: non_zero_lines += 1 np.testing.assert_equal(non_zero_lines, 4) yt-project-yt-f043ac8/yt/visualization/tests/test_normal_plot_api.py000066400000000000000000000034001510711153200261360ustar00rootroot00000000000000from itertools import product import numpy as np import pytest from yt.testing import fake_amr_ds from yt.visualization.plot_window import ProjectionPlot, SlicePlot @pytest.fixture(scope="module") def ds(): return fake_amr_ds(geometry="cartesian") @pytest.mark.parametrize("plot_cls", (SlicePlot, ProjectionPlot)) def test_normalplot_all_positional_args(ds, plot_cls): plot_cls(ds, "z", ("stream", "Density")) @pytest.mark.parametrize("plot_cls", (SlicePlot, ProjectionPlot)) def test_normalplot_normal_kwarg(ds, plot_cls): plot_cls(ds, normal="z", fields=("stream", "Density")) @pytest.mark.parametrize("plot_cls", (SlicePlot, ProjectionPlot)) def test_error_with_missing_fields_and_normal(ds, plot_cls): with pytest.raises( TypeError, match="missing 2 required positional arguments: 'normal' and 'fields'", ): plot_cls(ds) @pytest.mark.parametrize("plot_cls", (SlicePlot, ProjectionPlot)) def test_error_with_missing_fields_with_normal_kwarg(ds, plot_cls): with pytest.raises( TypeError, match=r"missing (1 )?required positional argument: 'fields'$" ): plot_cls(ds, normal="z") @pytest.mark.parametrize("plot_cls", (SlicePlot, ProjectionPlot)) def test_error_with_missing_fields_with_positional(ds, plot_cls): with pytest.raises( TypeError, match=r"missing (1 )?required positional argument: 'fields'$" ): plot_cls(ds, "z") @pytest.mark.parametrize( "plot_cls, normal", list( product( [SlicePlot, ProjectionPlot], [(0, 0, 1), [0, 0, 1], np.array((0, 0, 1))] ) ), ) def test_normalplot_normal_array(ds, plot_cls, normal): # see regression https://github.com/yt-project/yt/issues/3736 plot_cls(ds, normal, fields=("stream", "Density")) yt-project-yt-f043ac8/yt/visualization/tests/test_offaxisprojection.py000066400000000000000000000266461510711153200265340ustar00rootroot00000000000000import itertools as it import os import shutil import tempfile import unittest import numpy as np from numpy.testing import assert_equal from yt.testing import ( assert_fname, assert_rel_equal, fake_octree_ds, fake_random_ds, ) from yt.visualization.api import ( OffAxisProjectionPlot, OffAxisSlicePlot, ProjectionPlot, ) from yt.visualization.image_writer import write_projection from yt.visualization.volume_rendering.api import off_axis_projection # TODO: replace this with pytest.mark.parametrize def expand_keywords(keywords, full=False): """ expand_keywords is a means for testing all possible keyword arguments in the nosetests. Simply pass it a dictionary of all the keyword arguments and all of the values for these arguments that you want to test. It will return a list of kwargs dicts containing combinations of the various kwarg values you passed it. These can then be passed to the appropriate function in nosetests. If full=True, then every possible combination of keywords is produced, otherwise, every keyword option is included at least once in the output list. Be careful, by using full=True, you may be in for an exponentially larger number of tests! Parameters ---------- keywords : dict a dictionary where the keys are the keywords for the function, and the values of each key are the possible values that this key can take in the function full : bool if set to True, every possible combination of given keywords is returned Returns ------- array of dicts An array of dictionaries to be individually passed to the appropriate function matching these kwargs. Examples -------- >>> keywords = {} >>> keywords["dpi"] = (50, 100, 200) >>> keywords["cmap"] = ("cmyt.arbre", "cmyt.kelp") >>> list_of_kwargs = expand_keywords(keywords) >>> print(list_of_kwargs) array([{'cmap': 'cmyt.arbre', 'dpi': 50}, {'cmap': 'cmyt.kelp', 'dpi': 100}, {'cmap': 'cmyt.arbre', 'dpi': 200}], dtype=object) >>> list_of_kwargs = expand_keywords(keywords, full=True) >>> print(list_of_kwargs) array([{'cmap': 'cmyt.arbre', 'dpi': 50}, {'cmap': 'cmyt.arbre', 'dpi': 100}, {'cmap': 'cmyt.arbre', 'dpi': 200}, {'cmap': 'cmyt.kelp', 'dpi': 50}, {'cmap': 'cmyt.kelp', 'dpi': 100}, {'cmap': 'cmyt.kelp', 'dpi': 200}], dtype=object) >>> for kwargs in list_of_kwargs: ... write_projection(*args, **kwargs) """ # if we want every possible combination of keywords, use iter magic if full: keys = sorted(keywords) list_of_kwarg_dicts = np.array( [ dict(zip(keys, prod, strict=True)) for prod in it.product(*(keywords[key] for key in keys)) ] ) # if we just want to probe each keyword, but not necessarily every # combination else: # Determine the maximum number of values any of the keywords has num_lists = 0 for val in keywords.values(): if isinstance(val, str): num_lists = max(1.0, num_lists) else: num_lists = max(len(val), num_lists) # Construct array of kwargs dicts, each element of the list is a different # **kwargs dict. each kwargs dict gives a different combination of # the possible values of the kwargs # initialize array list_of_kwarg_dicts = np.array([{} for x in range(num_lists)]) # fill in array for i in np.arange(num_lists): list_of_kwarg_dicts[i] = {} for key in keywords.keys(): # if it's a string, use it (there's only one) if isinstance(keywords[key], str): list_of_kwarg_dicts[i][key] = keywords[key] # if there are more options, use the i'th val elif i < len(keywords[key]): list_of_kwarg_dicts[i][key] = keywords[key][i] # if there are not more options, use the 0'th val else: list_of_kwarg_dicts[i][key] = keywords[key][0] return list_of_kwarg_dicts class TestOffAxisProjection(unittest.TestCase): @classmethod def setUpClass(cls): cls.tmpdir = tempfile.mkdtemp() cls.curdir = os.getcwd() os.chdir(cls.tmpdir) @classmethod def tearDownClass(cls): os.chdir(cls.curdir) shutil.rmtree(cls.tmpdir) def test_oap(self): """Tests functionality of off_axis_projection and write_projection.""" # args for off_axis_projection test_ds = fake_random_ds(64) c = test_ds.domain_center norm = [0.5, 0.5, 0.5] W = test_ds.arr([0.5, 0.5, 1.0], "unitary") N = 256 field = ("gas", "density") oap_args = [test_ds, c, norm, W, N, field] # kwargs for off_axis_projection oap_kwargs = {} oap_kwargs["weight"] = (None, "cell_mass") oap_kwargs["no_ghost"] = (True, False) oap_kwargs["interpolated"] = (False,) oap_kwargs["north_vector"] = ((1, 0, 0), (0, 0.5, 1.0)) oap_kwargs_list = expand_keywords(oap_kwargs) # args or write_projection fn = "test_%d.png" # kwargs for write_projection wp_kwargs = {} wp_kwargs["colorbar"] = (True, False) wp_kwargs["colorbar_label"] = "test" wp_kwargs["title"] = "test" wp_kwargs["vmin"] = (None,) wp_kwargs["vmax"] = (1e3, 1e5) wp_kwargs["take_log"] = (True, False) wp_kwargs["figsize"] = ((8, 6), [1, 1]) wp_kwargs["dpi"] = (100, 50) wp_kwargs["cmap_name"] = ("cmyt.arbre", "cmyt.kelp") wp_kwargs_list = expand_keywords(wp_kwargs) # test all off_axis_projection kwargs and write_projection kwargs # make sure they are able to be projected, then remove and try next # iteration for i, oap_kwargs in enumerate(oap_kwargs_list): image = off_axis_projection(*oap_args, **oap_kwargs) for wp_kwargs in wp_kwargs_list: write_projection(image, fn % i, **wp_kwargs) assert_fname(fn % i) # Test remaining parameters of write_projection write_projection(image, "test_2", xlabel="x-axis", ylabel="y-axis") assert_fname("test_2.png") write_projection(image, "test_3.pdf", xlabel="x-axis", ylabel="y-axis") assert_fname("test_3.pdf") write_projection(image, "test_4.eps", xlabel="x-axis", ylabel="y-axis") assert_fname("test_4.eps") def test_field_cut_off_axis_octree(): ds = fake_octree_ds() cut = ds.all_data().cut_region('obj["gas", "density"]>0.5') p1 = OffAxisProjectionPlot(ds, [1, 0, 0], ("gas", "density")) p2 = OffAxisProjectionPlot(ds, [1, 0, 0], ("gas", "density"), data_source=cut) assert_equal(p2.frb["gas", "density"].min() == 0.0, True) # Lots of zeros assert_equal((p1.frb["gas", "density"] == p2.frb["gas", "density"]).all(), False) p3 = OffAxisSlicePlot(ds, [1, 0, 0], ("gas", "density")) p4 = OffAxisSlicePlot(ds, [1, 0, 0], ("gas", "density"), data_source=cut) assert_equal((p3.frb["gas", "density"] == p4.frb["gas", "density"]).all(), False) p4rho = p4.frb["gas", "density"] assert_equal(np.nanmin(p4rho[p4rho > 0.0]) >= 0.5, True) def test_off_axis_octree(): np.random.seed(12345) ds = fake_octree_ds() center = [0.4, 0.4, 0.4] for weight in [("gas", "cell_mass"), None, ("index", "dx")]: p1 = ProjectionPlot( ds, "x", ("gas", "density"), center=center, width=0.8, weight_field=weight, ) p2 = OffAxisProjectionPlot( ds, [1, 0, 0], ("gas", "density"), center=center, width=0.8, weight_field=weight, ) # Note: due to our implementation, the off-axis projection will have a # slightly blurred cell edges so we can't do an exact comparison v1, v2 = p1.frb["gas", "density"], p2.frb["gas", "density"] diff = (v1 - v2) / (v1 + v2) * 2 # Make sure the difference has a small bias assert np.mean(diff).max() < 1e-3 # 0.1% # Compute 10-90% percentile q10, q90 = np.percentile(diff, q=(10, 90)) assert q10 > -0.02 # 2%: little up/down deviations assert q90 < +0.02 # 2%: little up/down deviations def test_offaxis_moment(): ds = fake_random_ds(64) def _vlos_sq(field, data): return data["gas", "velocity_los"] ** 2 ds.add_field( ("gas", "velocity_los_squared"), _vlos_sq, sampling_type="local", units="cm**2/s**2", ) p1 = OffAxisProjectionPlot( ds, [1, 1, 1], [("gas", "velocity_los"), ("gas", "velocity_los_squared")], weight_field=("gas", "density"), moment=1, buff_size=(400, 400), ) p2 = OffAxisProjectionPlot( ds, [1, 1, 1], ("gas", "velocity_los"), weight_field=("gas", "density"), moment=2, buff_size=(400, 400), ) ## this failed because some - **2 values come out ## marginally < 0, resulting in unmatched NaN values in the ## first assert_rel_equal argument. The compute_stddev_image ## function used in OffAxisProjectionPlot checks for and deals ## with these cases. # assert_rel_equal( # np.sqrt( # p1.frb["gas", "velocity_los_squared"] - p1.frb["gas", "velocity_los"] ** 2 # ), # p2.frb["gas", "velocity_los"], # 10, # ) p1_expsq = p1.frb["gas", "velocity_los_squared"] p1_sqexp = p1.frb["gas", "velocity_los"] ** 2 # set values to zero that have **2 - **2 < 0, but # the absolute values are much smaller than the smallest # postive values of **2 and **2 # (i.e., the difference is pretty much zero) mindiff = 1e-10 * min( np.min(p1_expsq[p1_expsq > 0]), np.min(p1_sqexp[p1_sqexp > 0]) ) # print(mindiff) safeorbad = np.logical_not( np.logical_and(p1_expsq - p1_sqexp < 0, p1_expsq - p1_sqexp > -1.0 * mindiff) ) # avoid errors from sqrt(negative) # sqrt in zeros_like insures correct units p1res = np.zeros_like(np.sqrt(p1_expsq)) p1res[safeorbad] = np.sqrt(p1_expsq[safeorbad] - p1_sqexp[safeorbad]) p2res = p2.frb["gas", "velocity_los"] assert_rel_equal(p1res, p2res, 10) def test_two_offaxis(): ds = fake_octree_ds() vec_proj = [1, 0, 0] los_unit_vector = np.array(vec_proj) / np.linalg.norm(vec_proj) # define a new yt velocity field relative to the bulk velocity of the halo and along the line of sight def _velocity_los_test(field, data): v = np.stack( [data["gas", f"velocity_{k}"].to("km/s").d for k in "xyz"], axis=-1 ) v_los = np.dot(v, los_unit_vector) return data.apply_units(v_los, "km/s") ds.add_field( ("gas", "velocity_los_test"), function=_velocity_los_test, units="km/s", sampling_type="cell", display_name=f"Velocity LOS along {vec_proj}", ) p = OffAxisProjectionPlot( ds, [1.0, 0.0, 0.0], [("gas", "velocity_x"), ("gas", "velocity_los_test")], weight_field="dx", ) v1 = p.frb["gas", "velocity_x"] v2 = p.frb["gas", "velocity_los_test"] np.testing.assert_allclose(v1, v2.to(v1.units)) yt-project-yt-f043ac8/yt/visualization/tests/test_offaxisprojection_pytestonly.py000066400000000000000000000176741510711153200310470ustar00rootroot00000000000000import numpy as np import pytest import unyt from numpy.testing import assert_allclose from yt.testing import ( assert_rel_equal, cubicspline_python, fake_amr_ds, fake_sph_flexible_grid_ds, fake_sph_grid_ds, integrate_kernel, requires_module_pytest, ) from yt.visualization.api import OffAxisProjectionPlot, ProjectionPlot @pytest.mark.parametrize("weighted", [True, False]) @pytest.mark.parametrize("periodic", [True, False]) @pytest.mark.parametrize("depth", [None, (1.0, "cm"), (0.5, "cm")]) @pytest.mark.parametrize("shiftcenter", [False, True]) @pytest.mark.parametrize("northvector", [None, (1.0e-4, 1.0, 0.0)]) def test_sph_proj_general_offaxis( northvector: tuple[float, float, float] | None, shiftcenter: bool, depth: tuple[float, str] | None, periodic: bool, weighted: bool, ) -> None: """ Same as the on-axis projections, but we rotate the basis vectors to test whether roations are handled ok. the rotation is chosen to be small so that in/exclusion of particles within bboxes, etc. works out the same way. We just send lines of sight through pixel centers for convenience. Particles at [0.5, 1.5, 2.5] (in each coordinate) smoothing lengths 0.25 all particles have mass 1., density 1.5, except the single center particle, with mass 2., density 3. Parameters: ----------- northvector: tuple y-axis direction in the final plot (direction vector) shiftcenter: bool shift the coordinates to center the projection on. (The grid is offset to this same center) depth: float or None depth of the projection slice periodic: bool assume periodic boundary conditions, or not weighted: bool make a weighted projection (density-weighted density), or not Returns: -------- None """ if shiftcenter: center = np.array((0.625, 0.625, 0.625)) # cm else: center = np.array((1.5, 1.5, 1.5)) # cm bbox = unyt.unyt_array(np.array([[0.0, 3.0], [0.0, 3.0], [0.0, 3.0]]), "cm") hsml_factor = 0.5 unitrho = 1.5 # test correct centering, particle selection def makemasses(i, j, k): if i == j == k == 1: return 2.0 else: return 1.0 # result shouldn't depend explicitly on the center if we re-center # the data, unless we get cut-offs in the non-periodic case # *almost* the z-axis # try to make sure dl differences from periodic wrapping are small epsilon = 1e-4 projaxis = np.array([epsilon, 0.00, np.sqrt(1.0 - epsilon**2)]) e1dir = projaxis / np.sqrt(np.sum(projaxis**2)) # TODO: figure out other (default) axes for basis vectors here if northvector is None: e2dir = np.array([0.0, 1.0, 0.0]) else: e2dir = np.asarray(northvector) e2dir = e2dir - np.sum(e1dir * e2dir) * e2dir # orthonormalize e2dir /= np.sqrt(np.sum(e2dir**2)) e3dir = np.cross(e1dir, e2dir) ds = fake_sph_flexible_grid_ds( hsml_factor=hsml_factor, nperside=3, periodic=periodic, offsets=np.full(3, 0.5), massgenerator=makemasses, unitrho=unitrho, bbox=bbox.v, recenter=center, e1hat=e1dir, e2hat=e2dir, e3hat=e3dir, ) source = ds.all_data() # couple to dataset -> right unit registry center = ds.arr(center, "cm") # print('position:\n', source['gas','position']) # m / rho, factor 1. / hsml**2 is included in the kernel integral # (density is adjusted, so same for center particle) prefactor = 1.0 / unitrho # / (0.5 * 0.5)**2 dl_cen = integrate_kernel(cubicspline_python, 0.0, 0.25) if weighted: toweight_field = ("gas", "density") else: toweight_field = None # we don't actually want a plot, it's just a straightforward, # common way to get an frb / image array prj = ProjectionPlot( ds, projaxis, ("gas", "density"), width=(2.5, "cm"), weight_field=toweight_field, buff_size=(5, 5), center=center, data_source=source, north_vector=northvector, depth=depth, ) img = prj.frb.data[("gas", "density")] if weighted: # periodic shifts will modify the (relative) dl values a bit expected_out = np.zeros( ( 5, 5, ), dtype=img.v.dtype, ) expected_out[::2, ::2] = unitrho if depth is None: expected_out[2, 2] *= 1.5 else: # only 2 * unitrho element included expected_out[2, 2] *= 2.0 else: expected_out = np.zeros( ( 5, 5, ), dtype=img.v.dtype, ) expected_out[::2, ::2] = dl_cen * prefactor * unitrho if depth is None: # 3 particles per l.o.s., including the denser one expected_out *= 3.0 expected_out[2, 2] *= 4.0 / 3.0 else: # 1 particle per l.o.s., including the denser one expected_out[2, 2] *= 2.0 # grid is shifted to the left -> 'missing' stuff at the left if (not periodic) and shiftcenter: expected_out[:1, :] = 0.0 expected_out[:, :1] = 0.0 # print(axis, shiftcenter, depth, periodic, weighted) # print("expected:\n", expected_out) # print("recovered:\n", img.v) assert_rel_equal(expected_out, img.v, 4) _diag_dist = np.sqrt(3.0) # diagonal distance of a cube with length 1. # each case is depth, center, expected integrated distance _cases_to_test = [ (_diag_dist / 3.0, "domain_left_edge", _diag_dist / 3.0 / 2.0), (_diag_dist * 2.0, "domain_left_edge", _diag_dist), (_diag_dist * 4.0, "domain_left_edge", _diag_dist), (None, "domain_center", _diag_dist), ] @pytest.mark.parametrize("depth,proj_center,expected", _cases_to_test) def test_offaxisprojection_depth(depth, proj_center, expected): # this checks that the depth keyword argument works as expected. # in all cases, it integrates the (index, ones) field for a normal # pointing to the right edge corner of the domain. # # For the tests where the projection is centered on the left edge, # the integrate distance will scale as depth / 2.0. When centered # on the origin, it will scale with depth. The integrated distance # should max out at the diagonal distance of the cube (when the depth # exceeds the cube diagonal distance). # # Also note that the accuracy will depend on the buffer dimensions: # using the default (800,800) results in accuracy of about 1 percent ds = fake_amr_ds() n = [1.0, 1.0, 1.0] c = getattr(ds, proj_center) field = ("index", "ones") p = ProjectionPlot(ds, n, field, depth=depth, weight_field=None, center=c) maxval = p.frb[field].max().d assert_allclose(expected, maxval, atol=1e-2) _sph_test_cases = [ ([1.0, 1.0, 0.7], 27), ([1.0, 1.0, 1], 19), ([0.0, 0.0, 1], 9), ] @requires_module_pytest("contourpy") @pytest.mark.parametrize("normal_vec, n_particles", _sph_test_cases) def test_offaxisprojection_sph_defaultdepth(normal_vec, n_particles): # checks that particles are picked up as expected for a range of # depths and normal vectors. Certain viewing angles will result # in overlapping particles (since fake_sph_grid_ds aligns particles # on a grid): n_particles is the expected number of circles in the # resulting image for the given normal vector. Circle counts are # calculated here using a contour generator. from contourpy import contour_generator ds = fake_sph_grid_ds() c = ds.domain_center diag_dist = np.linalg.norm(ds.domain_width) field = ("gas", "mass") p = OffAxisProjectionPlot( ds, normal_vec, field, weight_field=None, center=c, width=diag_dist ) p.render() # get the number of circles in the plot cg = contour_generator(z=p.frb[("gas", "mass")].d) assert n_particles == len(cg.lines(1.0)) yt-project-yt-f043ac8/yt/visualization/tests/test_particle_plot.py000066400000000000000000000353101510711153200256250ustar00rootroot00000000000000import os import shutil import tempfile import unittest from unittest import mock import numpy as np from numpy.testing import assert_allclose, assert_array_almost_equal from yt.data_objects.particle_filters import add_particle_filter from yt.data_objects.profiles import create_profile from yt.loaders import load from yt.testing import fake_particle_ds, requires_file from yt.units.yt_array import YTArray from yt.utilities.answer_testing.framework import ( PhasePlotAttributeTest, PlotWindowAttributeTest, data_dir_load, requires_ds, ) from yt.visualization.api import ParticlePhasePlot, ParticlePlot, ParticleProjectionPlot from yt.visualization.tests.test_plotwindow import ATTR_ARGS, WIDTH_SPECS def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True # override some of the plotwindow ATTR_ARGS PROJ_ATTR_ARGS = ATTR_ARGS.copy() PROJ_ATTR_ARGS["set_cmap"] = [ ((("all", "particle_mass"), "RdBu"), {}), ((("all", "particle_mass"), "cmyt.pastel"), {}), ] PROJ_ATTR_ARGS["set_log"] = [((("all", "particle_mass"), False), {})] PROJ_ATTR_ARGS["set_zlim"] = [ ((("all", "particle_mass"), 1e39, 1e42), {}), ((("all", "particle_mass"),), {"zmin": 1e39, "dynamic_range": 4}), ] PHASE_ATTR_ARGS = { "annotate_text": [ (((5e-29, 5e7), "Hello YT"), {}), (((5e-29, 5e7), "Hello YT"), {"color": "b"}), ], "set_title": [((("all", "particle_mass"), "A phase plot."), {})], "set_log": [((("all", "particle_mass"), False), {})], "set_unit": [((("all", "particle_mass"), "Msun"), {})], "set_xlim": [((-4e7, 4e7), {})], "set_ylim": [((-4e7, 4e7), {})], } TEST_FLNMS = [None, "test", "test.png", "test.eps", "test.ps", "test.pdf"] CENTER_SPECS = ( "c", "C", "center", "Center", [0.5, 0.5, 0.5], [[0.2, 0.3, 0.4], "cm"], YTArray([0.3, 0.4, 0.7], "cm"), ) WEIGHT_FIELDS = (None, ("all", "particle_ones"), ("all", "particle_mass")) PHASE_FIELDS = [ ( ("all", "particle_velocity_x"), ("all", "particle_position_z"), ("all", "particle_mass"), ), ( ("all", "particle_position_x"), ("all", "particle_position_y"), ("all", "particle_ones"), ), ( ("all", "particle_velocity_x"), ("all", "particle_velocity_y"), [("all", "particle_mass"), ("all", "particle_ones")], ), ] g30 = "IsolatedGalaxy/galaxy0030/galaxy0030" @requires_ds(g30, big_data=True) def test_particle_projection_answers(): """ This iterates over the all the plot modification functions in PROJ_ATTR_ARGS. Each time, it compares the images produced by ParticleProjectionPlot to the gold standard. """ plot_field = ("all", "particle_mass") decimals = 12 ds = data_dir_load(g30) for ax in "xyz": for attr_name in PROJ_ATTR_ARGS.keys(): for args in PROJ_ATTR_ARGS[attr_name]: test = PlotWindowAttributeTest( ds, plot_field, ax, attr_name, args, decimals, "ParticleProjectionPlot", ) test_particle_projection_answers.__name__ = test.description yield test @requires_ds(g30, big_data=True) def test_particle_offaxis_projection_answers(): plot_field = ("all", "particle_mass") decimals = 12 ds = data_dir_load(g30) attr_name = "set_cmap" attr_args = ((("all", "particle_mass"), "RdBu"), {}) L = [1, 1, 1] test = PlotWindowAttributeTest( ds, plot_field, L, attr_name, attr_args, decimals, "ParticleProjectionPlot", ) test_particle_offaxis_projection_answers.__name__ = test.description yield test @requires_ds(g30, big_data=True) def test_particle_projection_filter(): """ This tests particle projection plots for filter fields. """ def formed_star(pfilter, data): filter = data["all", "creation_time"] > 0 return filter add_particle_filter( "formed_star", function=formed_star, filtered_type="all", requires=["creation_time"], ) plot_field = ("formed_star", "particle_mass") decimals = 12 ds = data_dir_load(g30) ds.add_particle_filter("formed_star") for ax in "xyz": attr_name = "set_log" test = PlotWindowAttributeTest( ds, plot_field, ax, attr_name, ((plot_field, False), {}), decimals, "ParticleProjectionPlot", ) test_particle_projection_filter.__name__ = test.description yield test @requires_ds(g30, big_data=True) def test_particle_phase_answers(): """ This iterates over the all the plot modification functions in PHASE_ATTR_ARGS. Each time, it compares the images produced by ParticlePhasePlot to the gold standard. """ decimals = 12 ds = data_dir_load(g30) x_field = ("all", "particle_velocity_x") y_field = ("all", "particle_velocity_y") z_field = ("all", "particle_mass") for attr_name in PHASE_ATTR_ARGS.keys(): for args in PHASE_ATTR_ARGS[attr_name]: test = PhasePlotAttributeTest( ds, x_field, y_field, z_field, attr_name, args, decimals, "ParticlePhasePlot", ) test_particle_phase_answers.__name__ = test.description yield test class TestParticlePhasePlotSave(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_particle_phase_plot(self): test_ds = fake_particle_ds() data_sources = [ test_ds.region([0.5] * 3, [0.4] * 3, [0.6] * 3), test_ds.all_data(), ] particle_phases = [] for source in data_sources: for x_field, y_field, z_fields in PHASE_FIELDS: particle_phases.append( ParticlePhasePlot( source, x_field, y_field, z_fields, x_bins=16, y_bins=16, ) ) particle_phases.append( ParticlePhasePlot( source, x_field, y_field, z_fields, x_bins=16, y_bins=16, deposition="cic", ) ) pp = create_profile( source, [x_field, y_field], z_fields, weight_field=("all", "particle_ones"), n_bins=[16, 16], ) particle_phases.append(ParticlePhasePlot.from_profile(pp)) particle_phases[0]._repr_html_() with ( mock.patch("matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure"), mock.patch("matplotlib.backends.backend_pdf.FigureCanvasPdf.print_figure"), mock.patch("matplotlib.backends.backend_ps.FigureCanvasPS.print_figure"), ): for p in particle_phases: for fname in TEST_FLNMS: p.save(fname) tgal = "TipsyGalaxy/galaxy.00300" @requires_file(tgal) def test_particle_phase_plot_semantics(): ds = load(tgal) ad = ds.all_data() dens_ex = ad.quantities.extrema(("Gas", "density")) temp_ex = ad.quantities.extrema(("Gas", "temperature")) plot = ParticlePlot( ds, ("Gas", "density"), ("Gas", "temperature"), ("Gas", "particle_mass") ) plot.set_log(("Gas", "density"), True) plot.set_log(("Gas", "temperature"), True) p = plot.profile # bin extrema are field extrema assert dens_ex[0] - np.spacing(dens_ex[0]) == p.x_bins[0] assert dens_ex[-1] + np.spacing(dens_ex[-1]) == p.x_bins[-1] assert temp_ex[0] - np.spacing(temp_ex[0]) == p.y_bins[0] assert temp_ex[-1] + np.spacing(temp_ex[-1]) == p.y_bins[-1] # bins are evenly spaced in log space logxbins = np.log10(p.x_bins) dxlogxbins = logxbins[1:] - logxbins[:-1] assert_allclose(dxlogxbins, dxlogxbins[0]) logybins = np.log10(p.y_bins) dylogybins = logybins[1:] - logybins[:-1] assert_allclose(dylogybins, dylogybins[0]) plot.set_log(("Gas", "density"), False) plot.set_log(("Gas", "temperature"), False) p = plot.profile # bin extrema are field extrema assert dens_ex[0] - np.spacing(dens_ex[0]) == p.x_bins[0] assert dens_ex[-1] + np.spacing(dens_ex[-1]) == p.x_bins[-1] assert temp_ex[0] - np.spacing(temp_ex[0]) == p.y_bins[0] assert temp_ex[-1] + np.spacing(temp_ex[-1]) == p.y_bins[-1] # bins are evenly spaced in log space dxbins = p.x_bins[1:] - p.x_bins[:-1] assert_allclose(dxbins, dxbins[0]) dybins = p.y_bins[1:] - p.y_bins[:-1] assert_allclose(dybins, dybins[0]) @requires_file(tgal) def test_set_units(): ds = load(tgal) sp = ds.sphere("max", (1.0, "Mpc")) pp = ParticlePhasePlot( sp, ("Gas", "density"), ("Gas", "temperature"), ("Gas", "particle_mass") ) # make sure we can set the units using the tuple without erroring out pp.set_unit(("Gas", "particle_mass"), "Msun") @requires_file(tgal) def test_switch_ds(): """ Tests the _switch_ds() method for ParticleProjectionPlots that as of 25th October 2017 requires a specific hack in plot_container.py """ ds = load(tgal) ds2 = load(tgal) plot = ParticlePlot( ds, ("Gas", "particle_position_x"), ("Gas", "particle_position_y"), ("Gas", "density"), ) plot._switch_ds(ds2) return class TestParticleProjectionPlotSave(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_particle_plot(self): test_ds = fake_particle_ds() particle_projs = [] for dim in range(3): particle_projs += [ ParticleProjectionPlot(test_ds, dim, ("all", "particle_mass")), ParticleProjectionPlot( test_ds, dim, ("all", "particle_mass"), deposition="cic" ), ParticleProjectionPlot( test_ds, dim, ("all", "particle_mass"), density=True ), ] particle_projs[0]._repr_html_() with ( mock.patch("matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure"), mock.patch("matplotlib.backends.backend_pdf.FigureCanvasPdf.print_figure"), mock.patch("matplotlib.backends.backend_ps.FigureCanvasPS.print_figure"), ): for p in particle_projs: for fname in TEST_FLNMS: p.save(fname)[0] def test_particle_plot_ds(self): test_ds = fake_particle_ds() ds_region = test_ds.region([0.5] * 3, [0.4] * 3, [0.6] * 3) for dim in range(3): pplot_ds = ParticleProjectionPlot( test_ds, dim, ("all", "particle_mass"), data_source=ds_region ) with mock.patch( "matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure" ): pplot_ds.save() def test_particle_plot_c(self): test_ds = fake_particle_ds() for center in CENTER_SPECS: for dim in range(3): pplot_c = ParticleProjectionPlot( test_ds, dim, ("all", "particle_mass"), center=center ) with mock.patch( "matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure" ): pplot_c.save() def test_particle_plot_wf(self): test_ds = fake_particle_ds() for dim in range(3): for weight_field in WEIGHT_FIELDS: pplot_wf = ParticleProjectionPlot( test_ds, dim, ("all", "particle_mass"), weight_field=weight_field ) with mock.patch( "matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure" ): pplot_wf.save() def test_particle_plot_offaxis(self): test_ds = fake_particle_ds() Ls = [[1, 1, 1], [0, 1, -0.5]] Ns = [None, [1, 1, 1]] for L, N in zip(Ls, Ns, strict=True): for weight_field in WEIGHT_FIELDS: pplot_off = ParticleProjectionPlot( test_ds, L, ("all", "particle_mass"), north_vector=N, weight_field=weight_field, ) with mock.patch( "matplotlib.backends.backend_agg.FigureCanvasAgg.print_figure" ): pplot_off.save() def test_creation_with_width(self): test_ds = fake_particle_ds() for width, (xlim, ylim, pwidth, _aun) in WIDTH_SPECS.items(): plot = ParticleProjectionPlot( test_ds, 0, ("all", "particle_mass"), width=width ) xlim = [plot.ds.quan(el[0], el[1]) for el in xlim] ylim = [plot.ds.quan(el[0], el[1]) for el in ylim] pwidth = [plot.ds.quan(el[0], el[1]) for el in pwidth] for px, x in zip(plot.xlim, xlim, strict=True): assert_array_almost_equal(px, x, 14) for py, y in zip(plot.ylim, ylim, strict=True): assert_array_almost_equal(py, y, 14) for pw, w in zip(plot.width, pwidth, strict=True): assert_array_almost_equal(pw, w, 14) def test_particle_plot_instance(): """ Tests the type of plot instance returned by ParticlePlot. If x_field and y_field are any combination of valid particle_position in x, y or z axis,then ParticleProjectionPlot instance is expected. """ ds = fake_particle_ds() x_field = ("all", "particle_position_x") y_field = ("all", "particle_position_y") z_field = ("all", "particle_velocity_x") plot = ParticlePlot(ds, x_field, y_field) assert isinstance(plot, ParticleProjectionPlot) plot = ParticlePlot(ds, y_field, x_field) assert isinstance(plot, ParticleProjectionPlot) plot = ParticlePlot(ds, x_field, z_field) assert isinstance(plot, ParticlePhasePlot) yt-project-yt-f043ac8/yt/visualization/tests/test_plot_modifications.py000066400000000000000000000010051510711153200266440ustar00rootroot00000000000000from yt.testing import requires_file from yt.utilities.answer_testing.framework import data_dir_load from yt.visualization.plot_window import SlicePlot @requires_file("amrvac/bw_3d0000.dat") def test_code_units_xy_labels(): ds = data_dir_load("amrvac/bw_3d0000.dat", kwargs={"unit_system": "code"}) p = SlicePlot(ds, "x", ("gas", "density")) ax = p.plots["gas", "density"].axes assert "code length" in ax.get_xlabel().replace("\\", "") assert "code length" in ax.get_ylabel().replace("\\", "") yt-project-yt-f043ac8/yt/visualization/tests/test_plotwindow.py000066400000000000000000000670241510711153200252010ustar00rootroot00000000000000import os import shutil import tempfile import unittest from collections import OrderedDict import numpy as np from matplotlib.colors import LogNorm, Normalize, SymLogNorm from numpy.testing import ( assert_array_almost_equal, assert_array_equal, assert_equal, assert_raises, ) from unyt import unyt_array from yt.loaders import load_uniform_grid from yt.testing import ( assert_allclose_units, assert_fname, assert_rel_equal, fake_amr_ds, fake_random_ds, requires_file, requires_module, ) from yt.units import kboltz from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.answer_testing.framework import ( PlotWindowAttributeTest, data_dir_load, requires_ds, ) from yt.utilities.exceptions import YTInvalidFieldType from yt.visualization.plot_window import ( AxisAlignedProjectionPlot, AxisAlignedSlicePlot, NormalPlot, OffAxisProjectionPlot, OffAxisSlicePlot, ProjectionPlot, SlicePlot, plot_2d, ) def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True TEST_FLNMS = ["test.png"] M7 = "DD0010/moving7_0010" FPROPS = {"family": "sans-serif", "style": "italic", "weight": "bold", "size": 24} ATTR_ARGS = { "pan": [(((0.1, 0.1),), {})], "pan_rel": [(((0.1, 0.1),), {})], "set_axes_unit": [ (("kpc",), {}), (("Mpc",), {}), ((("kpc", "kpc"),), {}), ((("kpc", "Mpc"),), {}), ], "set_buff_size": [((1600,), {}), (((600, 800),), {})], "set_center": [(((0.4, 0.3),), {})], "set_cmap": [(("density", "RdBu"), {}), (("density", "cmyt.pastel"), {})], "set_font": [((OrderedDict(sorted(FPROPS.items(), key=lambda t: t[0])),), {})], "set_log": [(("density", False), {})], "set_figure_size": [((7.0,), {})], "set_zlim": [ (("density", 1e-25, 1e-23), {}), (("density",), {"zmin": 1e-25, "dynamic_range": 4}), ], "zoom": [((10,), {})], } CENTER_SPECS = ( "m", "M", "max", "Max", "min", "Min", "c", "C", "center", "Center", "left", "right", [0.5, 0.5, 0.5], [[0.2, 0.3, 0.4], "cm"], YTArray([0.3, 0.4, 0.7], "cm"), ("max", ("gas", "density")), ("min", ("gas", "density")), ) WIDTH_SPECS = { # Width choices map to xlim, ylim, width, axes_unit_name 4-tuples None: ( ((0, "code_length"), (1, "code_length")), ((0, "code_length"), (1, "code_length")), ((1, "code_length"), (1, "code_length")), None, ), 0.2: ( ((0.4, "code_length"), (0.6, "code_length")), ((0.4, "code_length"), (0.6, "code_length")), ((0.2, "code_length"), (0.2, "code_length")), None, ), (0.4, 0.3): ( ((0.3, "code_length"), (0.7, "code_length")), ((0.35, "code_length"), (0.65, "code_length")), ((0.4, "code_length"), (0.3, "code_length")), None, ), (1.2, "cm"): ( ((-0.1, "code_length"), (1.1, "code_length")), ((-0.1, "code_length"), (1.1, "code_length")), ((1.2, "code_length"), (1.2, "code_length")), ("cm", "cm"), ), ((1.2, "cm"), (2.0, "cm")): ( ((-0.1, "code_length"), (1.1, "code_length")), ((-0.5, "code_length"), (1.5, "code_length")), ((1.2, "code_length"), (2.0, "code_length")), ("cm", "cm"), ), ((1.2, "cm"), (0.02, "m")): ( ((-0.1, "code_length"), (1.1, "code_length")), ((-0.5, "code_length"), (1.5, "code_length")), ((1.2, "code_length"), (2.0, "code_length")), ("cm", "m"), ), } WEIGHT_FIELDS = ( None, ("gas", "density"), ) PROJECTION_METHODS = ("integrate", "sum", "min", "max") BUFF_SIZES = [(800, 800), (1600, 1600), (1254, 1254), (800, 600)] def simple_contour(test_obj, plot): plot.annotate_contour(test_obj.plot_field) def simple_velocity(test_obj, plot): plot.annotate_velocity() def simple_streamlines(test_obj, plot): ax = test_obj.plot_axis xax = test_obj.ds.coordinates.x_axis[ax] yax = test_obj.ds.coordinates.y_axis[ax] xn = test_obj.ds.coordinates.axis_name[xax] yn = test_obj.ds.coordinates.axis_name[yax] plot.annotate_streamlines(("gas", f"velocity_{xn}"), ("gas", f"velocity_{yn}")) CALLBACK_TESTS = ( ("simple_contour", (simple_contour,)), ("simple_velocity", (simple_velocity,)), # ("simple_streamlines", (simple_streamlines,)), # ("simple_all", (simple_contour, simple_velocity, simple_streamlines)), ) @requires_ds(M7) def test_attributes(): """Test plot member functions that aren't callbacks""" plot_field = ("gas", "density") decimals = 12 ds = data_dir_load(M7) for ax in "xyz": for attr_name in ATTR_ARGS.keys(): for args in ATTR_ARGS[attr_name]: test = PlotWindowAttributeTest( ds, plot_field, ax, attr_name, args, decimals ) test_attributes.__name__ = test.description yield test for n, r in CALLBACK_TESTS: yield PlotWindowAttributeTest( ds, plot_field, ax, attr_name, args, decimals, callback_id=n, callback_runners=r, ) class TestHideAxesColorbar(unittest.TestCase): ds = None def setUp(self): if self.ds is None: self.ds = fake_random_ds(64) self.slc = SlicePlot(self.ds, 0, ("gas", "density")) self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) del self.ds del self.slc def test_hide_show_axes(self): self.slc.hide_axes() self.slc.save() self.slc.show_axes() self.slc.save() def test_hide_show_colorbar(self): self.slc.hide_colorbar() self.slc.save() self.slc.show_colorbar() self.slc.save() def test_hide_axes_colorbar(self): self.slc.hide_colorbar() self.slc.hide_axes() self.slc.save() class TestSetWidth(unittest.TestCase): ds = None def setUp(self): if self.ds is None: self.ds = fake_random_ds(64) self.slc = SlicePlot(self.ds, 0, ("gas", "density")) def tearDown(self): del self.ds del self.slc def _assert_05cm(self): assert_array_equal( [self.slc.xlim, self.slc.ylim, self.slc.width], [ (YTQuantity(0.25, "cm"), YTQuantity(0.75, "cm")), (YTQuantity(0.25, "cm"), YTQuantity(0.75, "cm")), (YTQuantity(0.5, "cm"), YTQuantity(0.5, "cm")), ], ) def _assert_05_075cm(self): assert_array_equal( [self.slc.xlim, self.slc.ylim, self.slc.width], [ (YTQuantity(0.25, "cm"), YTQuantity(0.75, "cm")), (YTQuantity(0.125, "cm"), YTQuantity(0.875, "cm")), (YTQuantity(0.5, "cm"), YTQuantity(0.75, "cm")), ], ) def test_set_width_one(self): assert_equal( [self.slc.xlim, self.slc.ylim, self.slc.width], [(0.0, 1.0), (0.0, 1.0), (1.0, 1.0)], ) assert self.slc._axes_unit_names is None def test_set_width_nonequal(self): self.slc.set_width((0.5, 0.8)) assert_rel_equal( [self.slc.xlim, self.slc.ylim, self.slc.width], [(0.25, 0.75), (0.1, 0.9), (0.5, 0.8)], 15, ) assert self.slc._axes_unit_names is None def test_twoargs_eq(self): self.slc.set_width(0.5, "cm") self._assert_05cm() assert self.slc._axes_unit_names == ("cm", "cm") def test_tuple_eq(self): self.slc.set_width((0.5, "cm")) self._assert_05cm() assert self.slc._axes_unit_names == ("cm", "cm") def test_tuple_of_tuples_neq(self): self.slc.set_width(((0.5, "cm"), (0.75, "cm"))) self._assert_05_075cm() assert self.slc._axes_unit_names == ("cm", "cm") class TestPlotWindowSave(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() self.curdir = os.getcwd() os.chdir(self.tmpdir) def tearDown(self): os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_slice_plot(self): test_ds = fake_random_ds(16) for dim in range(3): slc = SlicePlot(test_ds, dim, ("gas", "density")) for fname in TEST_FLNMS: assert_fname(slc.save(fname)[0]) def test_repr_html(self): test_ds = fake_random_ds(16) slc = SlicePlot(test_ds, 0, ("gas", "density")) slc._repr_html_() def test_projection_plot(self): test_ds = fake_random_ds(16) for dim in range(3): proj = ProjectionPlot(test_ds, dim, ("gas", "density")) for fname in TEST_FLNMS: assert_fname(proj.save(fname)[0]) def test_projection_plot_ds(self): test_ds = fake_random_ds(16) reg = test_ds.region([0.5] * 3, [0.4] * 3, [0.6] * 3) for dim in range(3): proj = ProjectionPlot(test_ds, dim, ("gas", "density"), data_source=reg) proj.save() def test_projection_plot_c(self): test_ds = fake_random_ds(16) for center in CENTER_SPECS: proj = ProjectionPlot(test_ds, 0, ("gas", "density"), center=center) proj.save() def test_projection_plot_wf(self): test_ds = fake_random_ds(16) for wf in WEIGHT_FIELDS: proj = ProjectionPlot(test_ds, 0, ("gas", "density"), weight_field=wf) proj.save() def test_projection_plot_m(self): test_ds = fake_random_ds(16) for method in PROJECTION_METHODS: proj = ProjectionPlot(test_ds, 0, ("gas", "density"), method=method) proj.save() def test_projection_plot_bs(self): test_ds = fake_random_ds(16) for bf in BUFF_SIZES: proj = ProjectionPlot(test_ds, 0, ("gas", "density"), buff_size=bf) image = proj.frb["gas", "density"] # note that image.shape is inverted relative to the passed in buff_size assert_equal(image.shape[::-1], bf) def test_offaxis_slice_plot(self): test_ds = fake_random_ds(16) slc = OffAxisSlicePlot(test_ds, [1, 1, 1], ("gas", "density")) for fname in TEST_FLNMS: assert_fname(slc.save(fname)[0]) def test_offaxis_projection_plot(self): test_ds = fake_random_ds(16) prj = OffAxisProjectionPlot(test_ds, [1, 1, 1], ("gas", "density")) for fname in TEST_FLNMS: assert_fname(prj.save(fname)[0]) def test_creation_with_width(self): test_ds = fake_random_ds(16) for width in WIDTH_SPECS: xlim, ylim, pwidth, aun = WIDTH_SPECS[width] plot = ProjectionPlot(test_ds, 0, ("gas", "density"), width=width) xlim = [plot.ds.quan(el[0], el[1]) for el in xlim] ylim = [plot.ds.quan(el[0], el[1]) for el in ylim] pwidth = [plot.ds.quan(el[0], el[1]) for el in pwidth] for px, x in zip(plot.xlim, xlim, strict=True): assert_array_almost_equal(px, x, 14) for py, y in zip(plot.ylim, ylim, strict=True): assert_array_almost_equal(py, y, 14) for pw, w in zip(plot.width, pwidth, strict=True): assert_array_almost_equal(pw, w, 14) assert aun == plot._axes_unit_names class TestPerFieldConfig(unittest.TestCase): ds = None def setUp(self): from yt.config import ytcfg newConfig = { ("yt", "default_colormap"): "viridis", ("plot", "gas", "log"): False, ("plot", "gas", "density", "units"): "lb/yard**3", ("plot", "gas", "density", "path_length_units"): "mile", ("plot", "gas", "density", "cmap"): "plasma", ("plot", "gas", "temperature", "log"): True, ("plot", "gas", "temperature", "linthresh"): 100, ("plot", "gas", "temperature", "cmap"): "hot", ("plot", "gas", "pressure", "log"): True, ("plot", "index", "radius", "linthresh"): 1e3, } # Backup the old config oldConfig = {} for key in newConfig.keys(): try: val = ytcfg[key] oldConfig[key] = val except KeyError: pass for key, val in newConfig.items(): ytcfg[key] = val self.oldConfig = oldConfig self.newConfig = newConfig fields = [("gas", "density"), ("gas", "temperature"), ("gas", "pressure")] units = ["g/cm**3", "K", "dyn/cm**2"] fields_to_plot = fields + [("index", "radius")] if self.ds is None: self.ds = fake_random_ds(16, fields=fields, units=units) self.proj = ProjectionPlot(self.ds, 0, fields_to_plot) def tearDown(self): from yt.config import ytcfg del self.ds del self.proj for key in self.newConfig.keys(): ytcfg.remove(*key) for key, val in self.oldConfig.items(): ytcfg[key] = val def test_units(self): from unyt import Unit assert_equal(self.proj.frb["gas", "density"].units, Unit("mile*lb/yd**3")) assert_equal(self.proj.frb["gas", "temperature"].units, Unit("cm*K")) assert_equal(self.proj.frb["gas", "pressure"].units, Unit("dyn/cm")) def test_scale(self): assert_equal( self.proj.plots["gas", "density"].norm_handler.norm_type, Normalize ) assert_equal( self.proj.plots["gas", "temperature"].norm_handler.norm_type, SymLogNorm ) assert_allclose_units( self.proj.plots["gas", "temperature"].norm_handler.linthresh, unyt_array(100, "K*cm"), ) assert_equal(self.proj.plots["gas", "pressure"].norm_handler.norm_type, LogNorm) assert_equal( self.proj.plots["index", "radius"].norm_handler.norm_type, SymLogNorm ) def test_cmap(self): assert_equal( self.proj.plots["gas", "density"].colorbar_handler.cmap.name, "plasma" ) assert_equal( self.proj.plots["gas", "temperature"].colorbar_handler.cmap.name, "hot" ) assert_equal( self.proj.plots["gas", "pressure"].colorbar_handler.cmap.name, "viridis" ) def test_on_off_compare(): # fake density field that varies in the x-direction only den = np.arange(32**3) / 32**2 + 1 den = den.reshape(32, 32, 32) den = np.array(den, dtype=np.float64) data = {"density": (den, "g/cm**3")} bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]]) ds = load_uniform_grid(data, den.shape, length_unit="Mpc", bbox=bbox, nprocs=64) sl_on = SlicePlot(ds, "z", [("gas", "density")]) L = [0, 0, 1] north_vector = [0, 1, 0] sl_off = OffAxisSlicePlot( ds, L, ("gas", "density"), center=[0, 0, 0], north_vector=north_vector ) assert_array_almost_equal(sl_on.frb["gas", "density"], sl_off.frb["gas", "density"]) sl_on.set_buff_size((800, 400)) sl_on._recreate_frb() sl_off.set_buff_size((800, 400)) sl_off._recreate_frb() assert_array_almost_equal(sl_on.frb["gas", "density"], sl_off.frb["gas", "density"]) def test_plot_particle_field_error(): ds = fake_random_ds(32, particles=100) field_names = [ ("all", "particle_mass"), [("all", "particle_mass"), ("gas", "density")], [("gas", "density"), ("all", "particle_mass")], ] objects_normals = [ (SlicePlot, 2), (SlicePlot, [1, 1, 1]), (ProjectionPlot, 2), (OffAxisProjectionPlot, [1, 1, 1]), ] for object, normal in objects_normals: for field_name_list in field_names: assert_raises(YTInvalidFieldType, object, ds, normal, field_name_list) def test_setup_origin(): origin_inputs = ( "domain", "left-window", "center-domain", "lower-right-window", ("window",), ("right", "domain"), ("lower", "window"), ("lower", "right", "window"), (0.5, 0.5, "domain"), ((50, "cm"), (50, "cm"), "domain"), ) w = (10, "cm") ds = fake_random_ds(32, length_unit=100.0) generated_limits = [] # lower limit -> llim # upper limit -> ulim # xllim xulim yllim yulim correct_limits = [ 45.0, 55.0, 45.0, 55.0, 0.0, 10.0, 0.0, 10.0, -5.0, 5.0, -5.0, 5.0, -10.0, 0, 0, 10.0, 0.0, 10.0, 0.0, 10.0, -55.0, -45.0, -55.0, -45.0, -5.0, 5.0, 0.0, 10.0, -10.0, 0, 0, 10.0, -5.0, 5.0, -5.0, 5.0, -5.0, 5.0, -5.0, 5.0, ] for o in origin_inputs: slc = SlicePlot(ds, 2, ("gas", "density"), width=w, origin=o) ax = slc.plots["gas", "density"].axes xlims = ax.get_xlim() ylims = ax.get_ylim() lims = [xlims[0], xlims[1], ylims[0], ylims[1]] for l in lims: generated_limits.append(l) assert_array_almost_equal(correct_limits, generated_limits) def test_frb_regen(): ds = fake_random_ds(32) slc = SlicePlot(ds, 2, ("gas", "density")) slc.set_buff_size(1200) assert_equal(slc.frb["gas", "density"].shape, (1200, 1200)) slc.set_buff_size((400.0, 200.7)) assert_equal(slc.frb["gas", "density"].shape, (200, 400)) def test_set_background_color(): ds = fake_random_ds(32) plot = SlicePlot(ds, 2, ("gas", "density")) plot.set_background_color(("gas", "density"), "red") plot.render() ax = plot.plots["gas", "density"].axes assert_equal(ax.get_facecolor(), (1.0, 0.0, 0.0, 1.0)) def test_set_unit(): ds = fake_random_ds(32, fields=(("gas", "temperature"),), units=("K",)) slc = SlicePlot(ds, 2, ("gas", "temperature")) orig_array = slc.frb["gas", "temperature"].copy() slc.set_unit(("gas", "temperature"), "degF") assert str(slc.frb["gas", "temperature"].units) == "°F" assert_array_almost_equal( np.array(slc.frb["gas", "temperature"]), np.array(orig_array) * 1.8 - 459.67 ) # test that a plot modifying function that destroys the frb preserves the # new unit slc.set_buff_size(1000) assert str(slc.frb["gas", "temperature"].units) == "°F" slc.set_buff_size(800) slc.set_unit(("gas", "temperature"), "K") assert str(slc.frb["gas", "temperature"].units) == "K" assert_array_almost_equal(slc.frb["gas", "temperature"], orig_array) slc.set_unit(("gas", "temperature"), "keV", equivalency="thermal") assert str(slc.frb["gas", "temperature"].units) == "keV" assert_array_almost_equal( slc.frb["gas", "temperature"], (orig_array * kboltz).to("keV") ) # test that a plot modifying function that destroys the frb preserves the # new unit with an equivalency slc.set_buff_size(1000) assert str(slc.frb["gas", "temperature"].units) == "keV" # test that destroying the FRB then changing the unit using an equivalency # doesn't error out, see issue #1316 slc = SlicePlot(ds, 2, ("gas", "temperature")) slc.set_buff_size(1000) slc.set_unit(("gas", "temperature"), "keV", equivalency="thermal") assert str(slc.frb["gas", "temperature"].units) == "keV" WD = "WDMerger_hdf5_chk_1000/WDMerger_hdf5_chk_1000.hdf5" blast_wave = "amrvac/bw_2d0000.dat" @requires_file(WD) @requires_file(blast_wave) def test_plot_2d(): # Cartesian ds = fake_random_ds((32, 32, 1), fields=("temperature",), units=("K",)) slc = SlicePlot( ds, "z", [("gas", "temperature")], width=(0.2, "unitary"), center=[0.4, 0.3, 0.5], ) slc2 = plot_2d( ds, ("gas", "temperature"), width=(0.2, "unitary"), center=[0.4, 0.3] ) slc3 = plot_2d( ds, ("gas", "temperature"), width=(0.2, "unitary"), center=ds.arr([0.4, 0.3], "cm"), ) assert_array_equal(slc.frb["gas", "temperature"], slc2.frb["gas", "temperature"]) assert_array_equal(slc.frb["gas", "temperature"], slc3.frb["gas", "temperature"]) # Cylindrical ds = data_dir_load(WD) slc = SlicePlot(ds, "theta", [("gas", "density")], width=(30000.0, "km")) slc2 = plot_2d(ds, ("gas", "density"), width=(30000.0, "km")) assert_array_equal(slc.frb["gas", "density"], slc2.frb["gas", "density"]) # Spherical ds = data_dir_load(blast_wave) slc = SlicePlot(ds, "phi", [("gas", "density")], width=(1, "unitary")) slc2 = plot_2d(ds, ("gas", "density"), width=(1, "unitary")) assert_array_equal(slc.frb["gas", "density"], slc2.frb["gas", "density"]) def test_symlog_colorbar(): ds = fake_random_ds(16) def _thresh_density(field, data): wh = data["gas", "density"] < 0.5 ret = data["gas", "density"] ret[wh] = 0 return ret def _neg_density(field, data): return -data["gas", "threshold_density"] ds.add_field( ("gas", "threshold_density"), function=_thresh_density, units="g/cm**3", sampling_type="cell", ) ds.add_field( ("gas", "negative_density"), function=_neg_density, units="g/cm**3", sampling_type="cell", ) for field in [ ("gas", "density"), ("gas", "threshold_density"), ("gas", "negative_density"), ]: plot = SlicePlot(ds, 2, field) plot.set_log(field, linthresh=0.1) with tempfile.NamedTemporaryFile(suffix="png") as f: plot.save(f.name) def test_symlog_min_zero(): # see https://github.com/yt-project/yt/issues/3791 shape = (32, 16, 1) a = np.linspace(0, 1, 16) b = np.ones((32, 16)) c = np.reshape(a * b, shape) data = {("gas", "density"): c} ds = load_uniform_grid( data, shape, bbox=np.array([[0.0, 5.0], [0, 1], [-0.1, +0.1]]), ) p = SlicePlot(ds, "z", "density") im_arr = p["gas", "density"].image.get_array() # check that no data value was mapped to a NaN (log(0)) assert np.all(~np.isnan(im_arr)) # 0 should be mapped to itself since we expect a symlog norm assert np.min(im_arr) == 0.0 def test_symlog_extremely_small_vals(): # check that the plot can be constructed without crashing # see https://github.com/yt-project/yt/issues/3858 # and https://github.com/yt-project/yt/issues/3944 shape = (64, 64, 1) arr = np.full(shape, 5.0e-324) arr[0, 0] = -1e12 arr[1, 1] = 200 arr2 = np.full(shape, 5.0e-324) arr2[0, 0] = -1e12 arr3 = arr.copy() arr3[4, 4] = 0.0 d = {"scalar_spans_0": arr, "tiny_vmax": arr2, "scalar_tiny_with_0": arr3} ds = load_uniform_grid(d, shape) for field in d: p = SlicePlot(ds, "z", field) p["stream", field] def test_symlog_linthresh_gt_vmax(): # check that some more edge cases do not crash # linthresh will end up being larger than vmax here. This is OK. shape = (64, 64, 1) arr = np.full(shape, -1e30) arr[1, 1] = -1e27 arr[2, 2] = 1e-12 arr[3, 3] = 1e-10 arr2 = -1 * arr.copy() # also check the reverse d = {"linthresh_gt_vmax": arr, "linthresh_lt_vmin": arr2} ds = load_uniform_grid(d, shape) for field in d: p = SlicePlot(ds, "z", field) p["stream", field] def test_symlog_symmetric(): # should run ok when abs(min negative) == abs(pos max) shape = (64, 64, 1) arr = np.full(shape, -1e30) arr[1, 1] = -1e27 arr[2, 2] = 1e10 arr[3, 3] = 1e30 d = {"linthresh_symmetric": arr} ds = load_uniform_grid(d, shape) p = SlicePlot(ds, "z", "linthresh_symmetric") p["stream", "linthresh_symmetric"] def test_nan_data(): data = np.random.random((16, 16, 16)) - 0.5 data[:9, :9, :9] = np.nan data = {"density": data} ds = load_uniform_grid(data, [16, 16, 16]) plot = SlicePlot(ds, "z", ("gas", "density")) with tempfile.NamedTemporaryFile(suffix="png") as f: plot.save(f.name) def test_sanitize_valid_normal_vector(): # note: we don't test against non-cartesian geometries # because the way normal "vectors" work isn't clearly # specified and works more as an implementation detail # at the moment ds = fake_amr_ds(geometry="cartesian") # We allow maximal polymorphism for axis-aligned directions: # even if 3-component vector is received, we want to use the # AxisAligned* plotting class (as opposed to OffAxis*) because # it's much easier to optimize so it's expected to be more # performant. axis_label_from_inputs = { "x": ["x", 0, [1, 0, 0], [0.1, 0.0, 0.0], [-10, 0, 0]], "y": ["y", 1, [0, 1, 0], [0.0, 0.1, 0.0], [0, -10, 0]], "z": ["z", 2, [0, 0, 1], [0.0, 0.0, 0.1], [0, 0, -10]], } for expected, user_inputs in axis_label_from_inputs.items(): for ui in user_inputs: assert NormalPlot.sanitize_normal_vector(ds, ui) == expected # arbitrary 3-floats sequences are also valid input. # They should be returned as np.ndarrays, but the norm and orientation # could be altered. What's important is that their direction is preserved. for ui in [(1, 1, 1), [0.0, -3, 1e9], np.ones(3, dtype="int8")]: res = NormalPlot.sanitize_normal_vector(ds, ui) assert isinstance(res, np.ndarray) assert res.dtype == np.float64 assert_array_equal( np.cross(ui, res), [0, 0, 0], ) def test_reject_invalid_normal_vector(): ds = fake_amr_ds(geometry="cartesian") for ui in [0.0, 1.0, 2.0, 3.0]: # acceptable scalar numeric values are restricted to integers. # Floats might be a sign that something went wrong upstream # e.g., rounding errors, parsing error... assert_raises(TypeError, NormalPlot.sanitize_normal_vector, ds, ui) for ui in [ "X", "xy", "not-an-axis", (0, 0, 0), [0, 0, 0], np.zeros(3), [1, 0, 0, 0], [1, 0], [1], [0], 3, 10, ]: assert_raises(ValueError, NormalPlot.sanitize_normal_vector, ds, ui) def test_dispatch_plot_classes(): ds = fake_random_ds(16) p1 = ProjectionPlot(ds, "z", ("gas", "density")) p2 = ProjectionPlot(ds, (1, 2, 3), ("gas", "density")) s1 = SlicePlot(ds, "z", ("gas", "density")) s2 = SlicePlot(ds, (1, 2, 3), ("gas", "density")) assert isinstance(p1, AxisAlignedProjectionPlot) assert isinstance(p2, OffAxisProjectionPlot) assert isinstance(s1, AxisAlignedSlicePlot) assert isinstance(s2, OffAxisSlicePlot) @requires_module("cartopy") def test_invalid_swap_projection(): # projections and transforms will not work ds = fake_amr_ds(geometry="geographic") slc = SlicePlot(ds, "altitude", ds.field_list[0], origin="native") slc.set_mpl_projection("Robinson") slc.swap_axes() # should raise mylog.warning and not toggle _swap_axes assert slc._has_swapped_axes is False def test_set_font(): # simply check that calling the set_font method doesn't raise an error # https://github.com/yt-project/yt/issues/4263 ds = fake_amr_ds() slc = SlicePlot(ds, "x", "Density") slc.set_font( { "family": "sans-serif", "style": "italic", "weight": "bold", "size": 24, "color": "blue", } ) yt-project-yt-f043ac8/yt/visualization/tests/test_profile_plots.py000066400000000000000000000202121510711153200256400ustar00rootroot00000000000000import os import shutil import tempfile import unittest import numpy as np import pytest import yt from yt.testing import assert_allclose_units, fake_random_ds, fake_random_sph_ds from yt.visualization.api import PhasePlot class TestPhasePlotAPI: @classmethod def setup_class(cls): cls.ds = fake_random_ds( 16, fields=("density", "temperature"), units=("g/cm**3", "K") ) def get_plot(self): return PhasePlot( self.ds, ("gas", "density"), ("gas", "temperature"), ("gas", "mass") ) @pytest.mark.parametrize("kwargs", [{}, {"color": "b"}]) @pytest.mark.mpl_image_compare def test_phaseplot_annotate_text(self, kwargs): p = self.get_plot() p.annotate_text(1e-4, 1e-2, "Test text annotation", **kwargs) p.render() return p.plots["gas", "mass"].figure @pytest.mark.mpl_image_compare def test_phaseplot_set_title(self): p = self.get_plot() p.set_title(("gas", "mass"), "Test Title") p.render() return p.plots["gas", "mass"].figure @pytest.mark.mpl_image_compare def test_phaseplot_set_log(self): p = self.get_plot() p.set_log(("gas", "mass"), False) p.render() return p.plots["gas", "mass"].figure @pytest.mark.mpl_image_compare def test_phaseplot_set_unit(self): p = self.get_plot() p.set_unit(("gas", "mass"), "Msun") p.render() return p.plots["gas", "mass"].figure @pytest.mark.mpl_image_compare def test_phaseplot_set_xlim(self): p = self.get_plot() p.set_xlim(1e-3, 1e0) p.render() return p.plots["gas", "mass"].figure @pytest.mark.mpl_image_compare def test_phaseplot_set_ylim(self): p = self.get_plot() p.set_ylim(1e-2, 1e0) p.render() return p.plots["gas", "mass"].figure class TestPhasePlotParticleAPI: @classmethod def setup_class(cls): bbox = np.array([[-1.0, 3.0], [1.0, 5.2], [-1.0, 3.0]]) cls.ds = fake_random_sph_ds(50, bbox) def get_plot(self): return PhasePlot( self.ds, ("gas", "density"), ("gas", "density"), ("gas", "mass") ) @pytest.mark.parametrize("kwargs", [{}, {"color": "b"}]) def test_phaseplot_annotate_text(self, kwargs): p = self.get_plot() p.annotate_text(1e-4, 1e-2, "Test text annotation", **kwargs) p.render() def test_phaseplot_set_title(self): p = self.get_plot() p.annotate_title("Test Title") p.render() def test_phaseplot_set_log(self): p = self.get_plot() p.set_log(("gas", "mass"), False) p.render() def test_phaseplot_set_unit(self): p = self.get_plot() p.set_unit(("gas", "mass"), "Msun") p.render() def test_phaseplot_set_xlim(self): p = self.get_plot() p.set_xlim(1e-3, 1e0) p.render() def test_phaseplot_set_ylim(self): p = self.get_plot() p.set_ylim(1e-2, 1e0) p.render() def test_set_units(): fields = ("density", "temperature") units = ( "g/cm**3", "K", ) ds = fake_random_ds(16, fields=fields, units=units) sp = ds.sphere("max", (1.0, "Mpc")) p1 = yt.ProfilePlot(sp, ("index", "radius"), ("gas", "density")) p2 = yt.PhasePlot(sp, ("gas", "density"), ("gas", "temperature"), ("gas", "mass")) # make sure we can set the units using the tuple without erroring out p1.set_unit(("gas", "density"), "Msun/kpc**3") p2.set_unit(("gas", "temperature"), "R") def test_set_labels(): ds = fake_random_ds(16) ad = ds.all_data() plot = yt.ProfilePlot( ad, ("index", "radius"), [("gas", "velocity_x"), ("gas", "density")], weight_field=None, ) # make sure we can set the labels without erroring out plot.set_ylabel("all", "test ylabel") plot.set_xlabel("test xlabel") def test_create_from_dataset(): ds = fake_random_ds(16) plot1 = yt.ProfilePlot( ds, ("index", "radius"), [("gas", "velocity_x"), ("gas", "density")], weight_field=None, ) plot2 = yt.ProfilePlot( ds.all_data(), ("index", "radius"), [("gas", "velocity_x"), ("gas", "density")], weight_field=None, ) assert_allclose_units( plot1.profiles[0]["gas", "density"], plot2.profiles[0]["gas", "density"] ) assert_allclose_units( plot1.profiles[0]["velocity_x"], plot2.profiles[0]["velocity_x"] ) plot1 = yt.PhasePlot(ds, ("gas", "density"), ("gas", "velocity_x"), ("gas", "mass")) plot2 = yt.PhasePlot( ds.all_data(), ("gas", "density"), ("gas", "velocity_x"), ("gas", "mass") ) assert_allclose_units(plot1.profile["mass"], plot2.profile["mass"]) class TestAnnotations(unittest.TestCase): @classmethod def setUpClass(cls): cls.tmpdir = tempfile.mkdtemp() cls.curdir = os.getcwd() os.chdir(cls.tmpdir) ds = fake_random_ds(16) ad = ds.all_data() cls.fields = [ ("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z"), ] cls.plot = yt.ProfilePlot( ad, ("index", "radius"), cls.fields, weight_field=None ) @classmethod def tearDownClass(cls): os.chdir(cls.curdir) shutil.rmtree(cls.tmpdir) def test_annotations(self): # make sure we can annotate without erroring out # annotate the plot with only velocity_x self.plot.annotate_title("velocity_x plot", self.fields[0]) self.plot.annotate_text(1e-1, 1e1, "Annotated velocity_x") # annotate the plots with velocity_y and velocity_z with # the same annotations self.plot.annotate_title("Velocity Plots (Y or Z)", self.fields[1:]) self.plot.annotate_text(1e-1, 1e1, "Annotated vel_y, vel_z", self.fields[1:]) self.plot.save() def test_annotations_wrong_fields(self): from yt.utilities.exceptions import YTFieldNotFound with self.assertRaises(YTFieldNotFound): self.plot.annotate_title("velocity_x plot", "wrong_field_name") with self.assertRaises(YTFieldNotFound): self.plot.annotate_text(1e-1, 1e1, "Annotated text", "wrong_field_name") def test_phaseplot_set_log(): fields = ("density", "temperature") units = ( "g/cm**3", "K", ) ds = fake_random_ds(16, fields=fields, units=units) sp = ds.sphere("max", (1.0, "Mpc")) p1 = yt.ProfilePlot(sp, ("index", "radius"), ("gas", "density")) p2 = yt.PhasePlot(sp, ("gas", "density"), ("gas", "temperature"), ("gas", "mass")) # make sure we can set the log-scaling using the tuple without erroring out p1.set_log(("gas", "density"), False) p2.set_log(("gas", "temperature"), False) assert not p1.y_log["gas", "density"] assert not p2.y_log # make sure we can set the log-scaling using a string without erroring out p1.set_log(("gas", "density"), True) p2.set_log(("gas", "temperature"), True) assert p1.y_log["gas", "density"] assert p2.y_log # make sure we can set the log-scaling using a field object p1.set_log(ds.fields.gas.density, False) p2.set_log(ds.fields.gas.temperature, False) assert not p1.y_log["gas", "density"] assert not p2.y_log def test_phaseplot_showhide_colorbar_axes(): fields = ("density", "temperature") units = ( "g/cm**3", "K", ) ds = fake_random_ds(16, fields=fields, units=units) ad = ds.all_data() plot = yt.PhasePlot(ad, ("gas", "density"), ("gas", "temperature"), ("gas", "mass")) # make sure we can hide colorbar plot.hide_colorbar() with tempfile.NamedTemporaryFile(suffix="png") as f1: plot.save(f1.name) # make sure we can show colorbar plot.show_colorbar() with tempfile.NamedTemporaryFile(suffix="png") as f2: plot.save(f2.name) # make sure we can hide axes plot.hide_axes() with tempfile.NamedTemporaryFile(suffix="png") as f3: plot.save(f3.name) # make sure we can show axes plot.show_axes() with tempfile.NamedTemporaryFile(suffix="png") as f4: plot.save(f4.name) yt-project-yt-f043ac8/yt/visualization/tests/test_raw_field_slices.py000066400000000000000000000016141510711153200262620ustar00rootroot00000000000000import pytest import yt from yt.testing import requires_file from yt.utilities.answer_testing.framework import data_dir_load_v2 class TestRawFieldSlice: @requires_file("Laser/plt00015") @classmethod def setup_class(cls): cls.ds = data_dir_load_v2("Laser/plt00015") fields = [ ("raw", "Bx"), ("raw", "By"), ("raw", "Bz"), ("raw", "Ex"), ("raw", "Ey"), ("raw", "Ez"), ("raw", "jx"), ("raw", "jy"), ("raw", "jz"), ] cls.sl = yt.SlicePlot(cls.ds, "z", fields) cls.sl.set_log("all", False) cls.sl.render() @pytest.mark.parametrize( "fname", ["Bx", "By", "Bz", "Ex", "Ey", "Ez", "jx", "jy", "jz"] ) @pytest.mark.mpl_image_compare def test_raw_field_slice(self, fname): return self.sl.plots["raw", "Bx"].figure yt-project-yt-f043ac8/yt/visualization/tests/test_save.py000066400000000000000000000067011510711153200237240ustar00rootroot00000000000000import os import re import pytest from PIL import Image import yt from yt.testing import fake_amr_ds # avoid testing every supported format as some backends may be buggy on testing platforms FORMATS_TO_TEST = [".eps", ".pdf", ".svg"] @pytest.fixture(scope="session") def simple_sliceplot(): ds = fake_amr_ds() p = yt.SlicePlot(ds, "z", "Density") yield p def test_save_to_path(simple_sliceplot, tmp_path): p = simple_sliceplot p.save(f"{tmp_path}/") assert len(list((tmp_path).glob("*.png"))) == 1 def test_metadata(simple_sliceplot, tmp_path): simple_sliceplot.save(tmp_path / "ala.png") with Image.open(tmp_path / "ala.png") as img: assert "Software" in img.info assert "yt-" in img.info["Software"] simple_sliceplot.save(tmp_path / "ala.pdf") with open(tmp_path / "ala.pdf", "rb") as f: assert b"|yt-" in f.read() def test_save_to_missing_path(simple_sliceplot, tmp_path): # the missing layer should be created p = simple_sliceplot # using forward slashes should work even on windows ! save_path = os.path.join(tmp_path / "out") + "/" p.save(save_path) assert os.path.exists(save_path) assert len(list((tmp_path / "out").glob("*.png"))) == 1 def test_save_to_missing_path_with_file_prefix(simple_sliceplot, tmp_path): # see issue # https://github.com/yt-project/yt/issues/3210 p = simple_sliceplot p.save(tmp_path.joinpath("out", "saymyname")) assert (tmp_path / "out").exists() output_files = list((tmp_path / "out").glob("*.png")) assert len(output_files) == 1 assert output_files[0].stem.startswith("saymyname") # you're goddamn right @pytest.mark.parametrize("ext", FORMATS_TO_TEST) def test_suffix_from_filename(ext, simple_sliceplot, tmp_path): p = simple_sliceplot target = (tmp_path / "myfile").with_suffix(ext) # this shouldn't raise a warning, see issue # https://github.com/yt-project/yt/issues/3667 p.save(target) assert target.is_file() @pytest.mark.parametrize("ext", FORMATS_TO_TEST) def test_suffix_clashing(ext, simple_sliceplot, tmp_path): if ext == ".png": pytest.skip() p = simple_sliceplot target = (tmp_path / "myfile").with_suffix(ext) expected_warning = re.compile( rf"Received two valid image formats {ext.removeprefix('.')!r} " r"\(from filename\) and 'png' \(from suffix\)\. The former is ignored\." ) with pytest.warns(UserWarning, match=expected_warning): p.save(target, suffix="png") output_files = list(tmp_path.glob("*.png")) assert len(output_files) == 1 assert output_files[0].stem.startswith("myfile") assert not list((tmp_path / "out").glob(f"*.{ext}")) def test_invalid_format_from_filename(simple_sliceplot, tmp_path): p = simple_sliceplot target = (tmp_path / "myfile").with_suffix(".nope") p.save(target) output_files = list(tmp_path.glob("*")) assert len(output_files) == 1 # the output filename may contain a generated part # it's not exactly clear if it's desirable or intended in this case # so we just check conditions that should hold in any case assert output_files[0].name.startswith("myfile.nope") assert output_files[0].name.endswith(".png") def test_invalid_format_from_suffix(simple_sliceplot, tmp_path): p = simple_sliceplot target = tmp_path / "myfile" with pytest.raises(ValueError, match=r"Unsupported file format 'nope'"): p.save(target, suffix="nope") yt-project-yt-f043ac8/yt/visualization/tests/test_set_zlim.py000066400000000000000000000071541510711153200246170ustar00rootroot00000000000000import numpy as np import numpy.testing as npt import pytest from yt.testing import fake_amr_ds from yt.visualization.api import SlicePlot def test_float_vmin_then_set_unit(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) p.render() cb = p.plots[field].image.colorbar raw_lims = np.array((cb.vmin, cb.vmax)) desired_lims = raw_lims.copy() desired_lims[0] = 1e-2 p.set_zlim(field, zmin=desired_lims[0]) p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_almost_equal(new_lims, desired_lims) # 1 g/cm**3 == 1000 kg/m**3 p.set_unit(field, "kg/m**3") p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_almost_equal(new_lims, 1000 * desired_lims) def test_set_unit_then_float_vmin(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) p.set_unit(field, "kg/m**3") p.set_zlim(field, zmin=1) p.render() cb = p.plots[field].image.colorbar assert cb.vmin == 1.0 def test_reset_zlim(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) p.render() cb = p.plots[field].image.colorbar raw_lims = np.array((cb.vmin, cb.vmax)) # set a new zmin value delta = np.diff(raw_lims)[0] p.set_zlim(field, zmin=raw_lims[0] + delta / 2) # passing "min" should restore default limit p.set_zlim(field, zmin="min") p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_array_equal(new_lims, raw_lims) def test_set_dynamic_range_with_vmin(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) zmin = 1e-2 p.set_zlim(field, zmin=zmin, dynamic_range=2) p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_almost_equal(new_lims, (zmin, 2 * zmin)) def test_set_dynamic_range_with_vmax(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) zmax = 1 p.set_zlim(field, zmax=zmax, dynamic_range=2) p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_almost_equal(new_lims, (zmax / 2, zmax)) def test_set_dynamic_range_with_min(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) p.render() cb = p.plots[field].image.colorbar vmin = cb.vmin p.set_zlim(field, zmin="min", dynamic_range=2) p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_almost_equal(new_lims, (vmin, 2 * vmin)) def test_set_dynamic_range_with_None(): field = ("gas", "density") ds = fake_amr_ds(fields=[field], units=["g/cm**3"]) p = SlicePlot(ds, "x", field) p.set_buff_size(16) p.render() cb = p.plots[field].image.colorbar vmin = cb.vmin with pytest.deprecated_call(match="Passing `zmin=None` explicitly is deprecated"): p.set_zlim(field, zmin=None, dynamic_range=2) p.render() cb = p.plots[field].image.colorbar new_lims = np.array((cb.vmin, cb.vmax)) npt.assert_almost_equal(new_lims, (vmin, 2 * vmin)) yt-project-yt-f043ac8/yt/visualization/tests/test_splat.py000066400000000000000000000020301510711153200241000ustar00rootroot00000000000000import os import os.path import shutil import tempfile import matplotlib as mpl import numpy as np from numpy.testing import assert_equal import yt from yt.utilities.lib.api import add_rgba_points_to_image # type: ignore def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True def test_splat(): # Perform I/O in safe place instead of yt main dir tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) prng = np.random.RandomState(0x4D3D3D3) N = 16 Np = int(1e2) image = np.zeros([N, N, 4]) xs = prng.random_sample(Np) ys = prng.random_sample(Np) cbx = mpl.colormaps["RdBu"] cs = cbx(prng.random_sample(Np)) add_rgba_points_to_image(image, xs, ys, cs) before_hash = image.copy() fn = "tmp.png" yt.write_bitmap(image, fn) assert_equal(os.path.exists(fn), True) os.remove(fn) assert_equal(before_hash, image) os.chdir(curdir) # clean up shutil.rmtree(tmpdir) yt-project-yt-f043ac8/yt/visualization/volume_rendering/000077500000000000000000000000001510711153200235535ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/volume_rendering/UBVRI.py000066400000000000000000000347571510711153200250340ustar00rootroot00000000000000import numpy as np johnson_filters = { "B": { "wavelen": np.array( [ 3600, 3650, 3700, 3750, 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150, 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550, 4600, 4650, 4700, 4750, 4800, 4850, 4900, 4950, 5000, 5050, 5100, 5150, 5200, 5250, 5300, 5350, 5400, 5450, 5500, 5550, ], dtype="float64", ), "trans": np.array( [ 0.0, 0.0, 0.02, 0.05, 0.11, 0.18, 0.35, 0.55, 0.92, 0.95, 0.98, 0.99, 1.0, 0.99, 0.98, 0.96, 0.94, 0.91, 0.87, 0.83, 0.79, 0.74, 0.69, 0.63, 0.58, 0.52, 0.46, 0.41, 0.36, 0.3, 0.25, 0.2, 0.15, 0.12, 0.09, 0.06, 0.04, 0.02, 0.01, 0.0, ], dtype="float64", ), }, "I": { "wavelen": np.array( [ 6800, 6850, 6900, 6950, 7000, 7050, 7100, 7150, 7200, 7250, 7300, 7350, 7400, 7450, 7500, 7550, 7600, 7650, 7700, 7750, 7800, 7850, 7900, 7950, 8000, 8050, 8100, 8150, 8200, 8250, 8300, 8350, 8400, 8450, 8500, 8550, 8600, 8650, 8700, 8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450, 9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11050, 11100, 11150, 11200, 11250, 11300, 11350, 11400, 11450, 11500, 11550, 11600, 11650, 11700, 11750, 11800, 11850, ], dtype="float64", ), "trans": np.array( [ 0.0, 0.0, 0.01, 0.01, 0.01, 0.04, 0.08, 0.13, 0.17, 0.21, 0.26, 0.3, 0.36, 0.4, 0.44, 0.49, 0.56, 0.6, 0.65, 0.72, 0.76, 0.84, 0.9, 0.93, 0.96, 0.97, 0.97, 0.98, 0.98, 0.99, 0.99, 0.99, 0.99, 1.0, 1.0, 1.0, 1.0, 1.0, 0.99, 0.98, 0.98, 0.97, 0.96, 0.94, 0.93, 0.9, 0.88, 0.86, 0.84, 0.8, 0.76, 0.74, 0.71, 0.68, 0.65, 0.61, 0.58, 0.56, 0.52, 0.5, 0.47, 0.44, 0.42, 0.39, 0.36, 0.34, 0.32, 0.3, 0.28, 0.26, 0.24, 0.22, 0.2, 0.19, 0.17, 0.16, 0.15, 0.13, 0.12, 0.11, 0.1, 0.09, 0.09, 0.08, 0.08, 0.07, 0.06, 0.05, 0.05, 0.04, 0.04, 0.03, 0.03, 0.02, 0.02, 0.02, 0.02, 0.02, 0.01, 0.01, 0.01, 0.0, ], dtype="float64", ), }, "R": { "wavelen": np.array( [ 5200, 5250, 5300, 5350, 5400, 5450, 5500, 5550, 5600, 5650, 5700, 5750, 5800, 5850, 5900, 5950, 6000, 6050, 6100, 6150, 6200, 6250, 6300, 6350, 6400, 6450, 6500, 6550, 6600, 6650, 6700, 6750, 6800, 6850, 6900, 6950, 7000, 7050, 7100, 7150, 7200, 7250, 7300, 7350, 7400, 7450, 7500, 7550, 7600, 7650, 7700, 7750, 7800, 7850, 7900, 7950, 8000, 8050, 8100, 8150, 8200, 8250, 8300, 8350, 8400, 8450, 8500, 8550, 8600, 8650, 8700, 8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450, 9500, ], dtype="float64", ), "trans": np.array( [ 0.0, 0.01, 0.02, 0.04, 0.06, 0.11, 0.18, 0.23, 0.28, 0.34, 0.4, 0.46, 0.5, 0.55, 0.6, 0.64, 0.69, 0.71, 0.74, 0.77, 0.79, 0.81, 0.84, 0.86, 0.88, 0.9, 0.91, 0.92, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 0.99, 1.0, 1.0, 0.99, 0.98, 0.96, 0.94, 0.92, 0.9, 0.88, 0.85, 0.83, 0.8, 0.77, 0.73, 0.7, 0.66, 0.62, 0.57, 0.53, 0.49, 0.45, 0.42, 0.39, 0.36, 0.34, 0.31, 0.27, 0.22, 0.19, 0.17, 0.15, 0.13, 0.12, 0.11, 0.1, 0.08, 0.07, 0.06, 0.06, 0.05, 0.04, 0.04, 0.03, 0.03, 0.02, 0.02, 0.02, 0.01, 0.01, 0.01, 0.01, 0.0, ], dtype="float64", ), }, "U": { "wavelen": np.array( [ 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750, 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150, ], dtype="float64", ), "trans": np.array( [ 0.0, 0.04, 0.1, 0.25, 0.61, 0.75, 0.84, 0.88, 0.93, 0.95, 0.97, 0.99, 1.0, 0.99, 0.97, 0.92, 0.73, 0.56, 0.36, 0.23, 0.05, 0.03, 0.01, 0.0, ], dtype="float64", ), }, "V": { "wavelen": np.array( [ 4600, 4650, 4700, 4750, 4800, 4850, 4900, 4950, 5000, 5050, 5100, 5150, 5200, 5250, 5300, 5350, 5400, 5450, 5500, 5550, 5600, 5650, 5700, 5750, 5800, 5850, 5900, 5950, 6000, 6050, 6100, 6150, 6200, 6250, 6300, 6350, 6400, 6450, 6500, 6550, 6600, 6650, 6700, 6750, 6800, 6850, 6900, 6950, 7000, 7050, 7100, 7150, 7200, 7250, 7300, 7350, ], dtype="float64", ), "trans": np.array( [ 0.0, 0.0, 0.01, 0.01, 0.02, 0.05, 0.11, 0.2, 0.38, 0.67, 0.78, 0.85, 0.91, 0.94, 0.96, 0.98, 0.98, 0.95, 0.87, 0.79, 0.72, 0.71, 0.69, 0.65, 0.62, 0.58, 0.52, 0.46, 0.4, 0.34, 0.29, 0.24, 0.2, 0.17, 0.14, 0.11, 0.08, 0.06, 0.05, 0.03, 0.02, 0.02, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.0, ], dtype="float64", ), }, } for vals in johnson_filters.values(): wavelen = vals["wavelen"] trans = vals["trans"] vals["Lchar"] = wavelen[np.argmax(trans)] yt-project-yt-f043ac8/yt/visualization/volume_rendering/__init__.py000066400000000000000000000000651510711153200256650ustar00rootroot00000000000000""" API for yt.visualization.volume_rendering """ yt-project-yt-f043ac8/yt/visualization/volume_rendering/_cuda_caster.cu000066400000000000000000000222571510711153200265300ustar00rootroot00000000000000// An attempt at putting the ray-casting operation into CUDA //extern __shared__ float array[]; #define NUM_SAMPLES 25 #define VINDEX(A,B,C) tg.data[((((A)+ci[0])*(tg.dims[1]+1)+((B)+ci[1]))*(tg.dims[2]+1)+ci[2]+(C))] #define fmin(A, B) ( A * (A < B) + B * (B < A) ) #define fmax(A, B) ( A * (A > B) + B * (B > A) ) #define fclip(A, B, C) ( fmax( fmin(A, C), B) ) struct transfer_function { float vs[4][256]; float dbin; float bounds[2]; }; __shared__ struct transfer_function tf; struct grid { float left_edge[3]; float right_edge[3]; float dds[3]; int dims[3]; float *data; }; __shared__ struct grid tg; __device__ float interpolate(float *cache, int *ds, int *ci, float *dp) { int i; float dv, dm[3]; for(i=0;i<3;i++)dm[i] = (1.0 - dp[i]); dv = 0.0; dv += cache[0] * (dm[0]*dm[1]*dm[2]); dv += cache[1] * (dm[0]*dm[1]*dp[2]); dv += cache[2] * (dm[0]*dp[1]*dm[2]); dv += cache[3] * (dm[0]*dp[1]*dp[2]); dv += cache[4] * (dp[0]*dm[1]*dm[2]); dv += cache[5] * (dp[0]*dm[1]*dp[2]); dv += cache[6] * (dp[0]*dp[1]*dm[2]); dv += cache[7] * (dp[0]*dp[1]*dp[2]); return dv; } __device__ void eval_transfer(float dt, float dv, float *rgba, transfer_function &tf) { int i, bin_id; float temp, bv, dy, dd, ta; bin_id = (int) ((dv - tf.bounds[0]) / tf.dbin); bv = tf.vs[3][bin_id ]; dy = tf.vs[3][bin_id+1] - bv; dd = dv - (tf.bounds[0] + bin_id*tf.dbin); temp = bv+dd*(dy/tf.dbin); ta = temp; for (i = 0; i < 3; i++) { bv = tf.vs[i][bin_id ]; dy = tf.vs[i][bin_id+1]; dd = dv - (tf.bounds[0] + bin_id*tf.dbin); temp = bv+dd*(dy/tf.dbin); rgba[i] += (1.0 - rgba[3])*ta*temp*dt; } //rgba[3] += (1.0 - rgba[3])*ta*dt; } __device__ void sample_values(float v_pos[3], float v_dir[3], float enter_t, float exit_t, int ci[3], float rgba[4], transfer_function &tf, grid &tg) { float cp[3], dp[3], dt, t, dv; int dti, i; float cache[8]; cache[0] = VINDEX(0,0,0); cache[1] = VINDEX(0,0,1); cache[2] = VINDEX(0,1,0); cache[3] = VINDEX(0,1,1); cache[4] = VINDEX(1,0,0); cache[5] = VINDEX(1,0,1); cache[6] = VINDEX(1,1,0); cache[7] = VINDEX(1,1,1); dt = (exit_t - enter_t) / (NUM_SAMPLES-1); for (dti = 0; dti < NUM_SAMPLES - 1; dti++) { t = enter_t + dt*dti; for (i = 0; i < 3; i++) { cp[i] = v_pos[i] + t * v_dir[i]; dp[i] = fclip(fmod(cp[i], tg.dds[i])/tg.dds[i], 0.0, 1.0); } dv = interpolate(cache, tg.dims, ci, dp); eval_transfer(dt, dv, rgba, tf); } } /* We need to know several things if we want to ray cast through a grid. We need the grid spatial information, as well as its values. We also need the transfer function, which defines what our image will look like. */ __global__ void ray_cast(int ngrids, float *grid_data, int *dims, float *left_edge, float *right_edge, float *tf_r, float *tf_g, float *tf_b, float *tf_a, float *tf_bounds, float *v_dir, float *av_pos, float *image_r, float *image_g, float *image_b, float *image_a) { int cur_ind[3], step[3], x, y, i, direction; float intersect_t = 1.0, intersect_ts[3]; float tmax[3]; float tl, tr, temp_xl, temp_yl, temp_xr, temp_yr; int offset; //transfer_function tf; for (i = 0; i < 4; i++) { x = 4 * (8 * threadIdx.x + threadIdx.y) + i; tf.vs[0][x] = tf_r[x]; tf.vs[1][x] = tf_g[x]; tf.vs[2][x] = tf_b[x]; tf.vs[3][x] = tf_a[x]; } tf.bounds[0] = tf_bounds[0]; tf.bounds[1] = tf_bounds[1]; tf.dbin = (tf.bounds[1] - tf.bounds[0])/255.0; /* Set up the grid, just for convenience */ //grid tg; int grid_i; int tidx = (blockDim.x * gridDim.x) * ( blockDim.y * blockIdx.y + threadIdx.y) + (blockDim.x * blockIdx.x + threadIdx.x); float rgba[4]; //rgba[0] = image_r[tidx]; //rgba[1] = image_g[tidx]; //rgba[2] = image_b[tidx]; //rgba[3] = image_a[tidx]; float v_pos[3]; v_pos[0] = av_pos[tidx + 0]; v_pos[1] = av_pos[tidx + 1]; v_pos[2] = av_pos[tidx + 2]; tg.data = grid_data; int skip; for (i = 0; i < 3; i++) { step[i] = 0; step[i] += (v_dir[i] > 0); step[i] += -1 * (v_dir[i] < 0); } for(grid_i = 0; grid_i < ngrids; grid_i++) { skip = 0; if (threadIdx.x == 0) { if (threadIdx.y == 0) tg.dims[0] = dims[3*grid_i + 0]; if (threadIdx.y == 1) tg.dims[1] = dims[3*grid_i + 1]; if (threadIdx.y == 2) tg.dims[2] = dims[3*grid_i + 2]; } if (threadIdx.x == 1) { if (threadIdx.y == 0) tg.left_edge[0] = left_edge[3*grid_i + 0]; if (threadIdx.y == 1) tg.left_edge[1] = left_edge[3*grid_i + 1]; if (threadIdx.y == 2) tg.left_edge[2] = left_edge[3*grid_i + 2]; } if (threadIdx.x == 2) { if (threadIdx.y == 0) tg.right_edge[0] = right_edge[3*grid_i + 0]; if (threadIdx.y == 1) tg.right_edge[1] = right_edge[3*grid_i + 1]; if (threadIdx.y == 2) tg.right_edge[2] = right_edge[3*grid_i + 2]; } if (threadIdx.x == 3) { if (threadIdx.y == 0) tg.dds[0] = (tg.right_edge[0] - tg.left_edge[0])/tg.dims[0]; if (threadIdx.y == 1) tg.dds[1] = (tg.right_edge[1] - tg.left_edge[1])/tg.dims[1]; if (threadIdx.y == 2) tg.dds[2] = (tg.right_edge[2] - tg.left_edge[2])/tg.dims[2]; } /* We integrate our ray */ for (i = 0; i < 3; i++) { x = (i + 1) % 3; y = (i + 2) % 3; tl = (tg.left_edge[i] - v_pos[i])/v_dir[i]; temp_xl = (v_pos[i] + tl*v_dir[x]); temp_yr = (v_pos[i] + tl*v_dir[y]); tr = (tg.right_edge[i] - v_pos[i])/v_dir[i]; temp_xr = (v_pos[x] + tr*v_dir[x]); temp_yr = (v_pos[y] + tr*v_dir[y]); intersect_ts[i] = 1.0; intersect_ts[i] += ( (tg.left_edge[x] <= temp_xl) && (temp_xl <= tg.right_edge[x]) && (tg.left_edge[y] <= temp_yl) && (temp_yl <= tg.right_edge[y]) && (0.0 <= tl) && (tl < intersect_ts[i]) && (tl < tr) ) * tl; intersect_ts[i] += ( (tg.left_edge[x] <= temp_xr) && (temp_xr <= tg.right_edge[x]) && (tg.left_edge[y] <= temp_yr) && (temp_yr <= tg.right_edge[y]) && (0.0 <= tr) && (tr < intersect_ts[i]) && (tr < tl) ) * tr; intersect_t = ( intersect_ts[i] < intersect_t) * intersect_ts[i]; } intersect_t *= (!( (tg.left_edge[0] <= v_pos[0]) && (v_pos[0] <= tg.right_edge[0]) && (tg.left_edge[0] <= v_pos[0]) && (v_pos[0] <= tg.right_edge[0]) && (tg.left_edge[0] <= v_pos[0]) && (v_pos[0] <= tg.right_edge[0]))); skip = ((intersect_t < 0) || (intersect_t > 1.0)); for (i = 0; i < 3; i++) { cur_ind[i] = (int) floor(((v_pos[i] + intersect_t * v_dir[i]) + step[i]*1e-7*tg.dds[i] - tg.left_edge[i])/tg.dds[i]); tmax[i] = (((cur_ind[i]+step[i])*tg.dds[i])+ tg.left_edge[i]-v_pos[i])/v_dir[i]; cur_ind[i] -= ((cur_ind[i] == tg.dims[i]) && (step[i] < 0)); skip = ((cur_ind[i] < 0) || (cur_ind[i] >= tg.dims[i])); offset = (step[i] > 0); tmax[i] = (((cur_ind[i]+offset)*tg.dds[i])+tg.left_edge[i]-v_pos[i])/v_dir[i]; } /* This is the primary grid walking loop */ while(!( (skip) ||((cur_ind[0] < 0) || (cur_ind[0] >= tg.dims[0]) || (cur_ind[1] < 0) || (cur_ind[1] >= tg.dims[1]) || (cur_ind[2] < 0) || (cur_ind[2] >= tg.dims[2])))) { direction = 0; direction += 2 * (tmax[0] < tmax[1]) * (tmax[0] >= tmax[2]); direction += 1 * (tmax[0] >= tmax[1]) * (tmax[1] < tmax[2]); direction += 2 * (tmax[0] >= tmax[1]) * (tmax[1] >= tmax[2]); sample_values(v_pos, v_dir, intersect_t, tmax[direction], cur_ind, rgba, tf, tg); cur_ind[direction] += step[direction]; intersect_t = tmax[direction]; tmax[direction] += abs(tg.dds[direction]/v_dir[direction]); } tg.data += (tg.dims[0]+1) * (tg.dims[1]+1) * (tg.dims[2]+1); } int iy = threadIdx.y + blockDim.y * blockIdx.y; int ix = threadIdx.x + blockDim.x * blockIdx.x; __syncthreads(); image_r[tidx] = rgba[0]; image_g[tidx] = rgba[1]; image_b[tidx] = rgba[2]; image_a[tidx] = rgba[3]; } yt-project-yt-f043ac8/yt/visualization/volume_rendering/api.py000066400000000000000000000013251510711153200246770ustar00rootroot00000000000000from .camera import Camera from .image_handling import export_rgba, import_rgba, plot_channel, plot_rgb from .off_axis_projection import off_axis_projection from .render_source import ( BoxSource, CoordinateVectorSource, GridSource, LineSource, MeshSource, OpaqueSource, PointSource, create_volume_source, set_raytracing_engine, ) from .scene import Scene from .transfer_function_helper import TransferFunctionHelper from .transfer_functions import ( ColorTransferFunction, MultiVariateTransferFunction, PlanckTransferFunction, ProjectionTransferFunction, TransferFunction, ) from .volume_rendering import create_scene, volume_render from .zbuffer_array import ZBuffer yt-project-yt-f043ac8/yt/visualization/volume_rendering/blenders.py000066400000000000000000000013531510711153200257250ustar00rootroot00000000000000import numpy as np def enhance(im, stdval=6.0, just_alpha=True): if just_alpha: nz = im[im > 0.0] im[:] = im[:] / (nz.mean() + stdval * np.std(nz)) else: for c in range(3): nz = im[:, :, c][im[:, :, c] > 0.0] im[:, :, c] = im[:, :, c] / (nz.mean() + stdval * np.std(nz)) del nz np.clip(im, 0.0, 1.0, im) def enhance_rgba(im, stdval=6.0): nzc = im[:, :, :3][im[:, :, :3] > 0.0] cmax = nzc.mean() + stdval * nzc.std() nza = im[:, :, 3][im[:, :, 3] > 0.0] if len(nza) == 0: im[:, :, 3] = 1.0 amax = 1.0 else: amax = nza.mean() + stdval * nza.std() im.rescale(amax=amax, cmax=cmax, inline=True) np.clip(im, 0.0, 1.0, im) yt-project-yt-f043ac8/yt/visualization/volume_rendering/camera.py000066400000000000000000000631731510711153200253670ustar00rootroot00000000000000import weakref from numbers import Number as numeric_type import numpy as np from yt.funcs import ensure_numpy_array, is_sequence from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.math_utils import get_rotation_matrix from yt.utilities.orientation import Orientation from .lens import Lens, lenses from .utils import data_source_or_all def _sanitize_camera_property_units(value, scene): if is_sequence(value): if len(value) == 1: return _sanitize_camera_property_units(value[0], scene) elif isinstance(value, YTArray) and len(value) == 3: return scene.arr(value).in_units("unitary") elif ( len(value) == 2 and isinstance(value[0], numeric_type) and isinstance(value[1], str) ): return scene.arr([scene.arr(value[0], value[1]).in_units("unitary")] * 3) if len(value) == 3: if all(is_sequence(v) for v in value): if all( isinstance(v[0], numeric_type) and isinstance(v[1], str) for v in value ): return scene.arr([scene.arr(v[0], v[1]) for v in value]) else: raise RuntimeError( f"Cannot set camera width to invalid value '{value}'" ) return scene.arr(value, "unitary") else: if isinstance(value, (YTQuantity, YTArray)): return scene.arr([value.d] * 3, value.units).in_units("unitary") elif isinstance(value, numeric_type): return scene.arr([value] * 3, "unitary") raise RuntimeError(f"Cannot set camera width to invalid value '{value}'") class Camera(Orientation): r"""A representation of a point of view into a Scene. It is defined by a position (the location of the camera in the simulation domain,), a focus (the point at which the camera is pointed), a width (the width of the snapshot that will be taken, a resolution (the number of pixels in the image), and a north_vector (the "up" direction in the resulting image). A camera can use a variety of different Lens objects. Parameters ---------- scene: A :class:`yt.visualization.volume_rendering.scene.Scene` object A scene object that the camera will be attached to. data_source: :class:`AMR3DData` or :class:`Dataset`, optional This is the source to be rendered, which can be any arbitrary yt data object or dataset. lens_type: string, optional This specifies the type of lens to use for rendering. Current options are 'plane-parallel', 'perspective', and 'fisheye'. See :class:`yt.visualization.volume_rendering.lens.Lens` for details. Default: 'plane-parallel' auto: boolean If True, build smart defaults using the data source extent. This can be time-consuming to iterate over the entire dataset to find the positional bounds. Default: False Examples -------- In this example, the camera is set using defaults that are chosen to be reasonable for the argument Dataset. >>> import yt >>> from yt.visualization.volume_rendering.api import Scene >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> cam = sc.add_camera(ds) Here, we set the camera properties manually: >>> import yt >>> from yt.visualization.volume_rendering.api import Scene >>> sc = Scene() >>> cam = sc.add_camera() >>> cam.position = np.array([0.5, 0.5, -1.0]) >>> cam.focus = np.array([0.5, 0.5, 0.0]) >>> cam.north_vector = np.array([1.0, 0.0, 0.0]) Finally, we create a camera with a non-default lens: >>> import yt >>> from yt.visualization.volume_rendering.api import Scene >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> cam = sc.add_camera(ds, lens_type="perspective") """ _moved = True _width = None _focus = None _position = None _resolution = None def __init__(self, scene, data_source=None, lens_type="plane-parallel", auto=False): # import this here to avoid an import cycle from .scene import Scene if not isinstance(scene, Scene): raise RuntimeError( "The first argument passed to the Camera initializer is a " f"{type(scene)} object, expected a {Scene} object" ) self.scene = weakref.proxy(scene) self.lens = None self.north_vector = None self.normal_vector = None self.light = None self.data_source = data_source_or_all(data_source) self._resolution = (512, 512) if self.data_source is not None: self.scene._set_new_unit_registry(self.data_source.ds.unit_registry) self._focus = self.data_source.ds.domain_center self._position = self.data_source.ds.domain_right_edge self._width = self.data_source.ds.arr( [1.5 * self.data_source.ds.domain_width.max()] * 3 ) self._domain_center = self.data_source.ds.domain_center self._domain_width = self.data_source.ds.domain_width else: self._focus = scene.arr([0.0, 0.0, 0.0], "unitary") self._width = scene.arr([1.0, 1.0, 1.0], "unitary") self._position = scene.arr([1.0, 1.0, 1.0], "unitary") if auto: self.set_defaults_from_data_source(data_source) super().__init__( self.focus - self.position, self.north_vector, steady_north=False ) self.set_lens(lens_type) @property def position(self): r""" The location of the camera. Parameters ---------- position : number, YTQuantity, :obj:`!iterable`, or 3 element YTArray If a scalar, assumes that the position is the same in all three coordinates. If an iterable, must contain only scalars or (length, unit) tuples. """ return self._position @position.setter def position(self, value): position = _sanitize_camera_property_units(value, self.scene) if np.array_equal(position, self.focus): raise RuntimeError( "Cannot set the camera focus and position to the same value" ) self._position = position self.switch_orientation( normal_vector=self.focus - self._position, north_vector=self.north_vector, ) @position.deleter def position(self): del self._position @property def width(self): r"""The width of the region that will be seen in the image. Parameters ---------- width : number, YTQuantity, :obj:`!iterable`, or 3 element YTArray The width of the volume rendering in the horizontal, vertical, and depth directions. If a scalar, assumes that the width is the same in all three directions. If an iterable, must contain only scalars or (length, unit) tuples. """ return self._width @width.setter def width(self, value): width = _sanitize_camera_property_units(value, self.scene) self._width = width self.switch_orientation() @width.deleter def width(self): del self._width self._width = None @property def focus(self): r""" The focus defines the point the Camera is pointed at. Parameters ---------- focus : number, YTQuantity, :obj:`!iterable`, or 3 element YTArray The width of the volume rendering in the horizontal, vertical, and depth directions. If a scalar, assumes that the width is the same in all three directions. If an iterable, must contain only scalars or (length, unit) tuples. """ return self._focus @focus.setter def focus(self, value): focus = _sanitize_camera_property_units(value, self.scene) if np.array_equal(focus, self.position): raise RuntimeError( "Cannot set the camera focus and position to the same value" ) self._focus = focus self.switch_orientation( normal_vector=self.focus - self._position, north_vector=None ) @focus.deleter def focus(self): del self._focus @property def resolution(self): r"""The resolution is the number of pixels in the image that will be produced. Must be a 2-tuple of integers or an integer.""" return self._resolution @resolution.setter def resolution(self, value): if is_sequence(value): if len(value) != 2: raise RuntimeError else: value = (value, value) self._resolution = value @resolution.deleter def resolution(self): del self._resolution self._resolution = None def set_resolution(self, resolution): """ The resolution is the number of pixels in the image that will be produced. Must be a 2-tuple of integers or an integer. """ self.resolution = resolution def get_resolution(self): """ Returns the resolution of the volume rendering """ return self.resolution def _get_sampler_params(self, render_source): lens_params = self.lens._get_sampler_params(self, render_source) lens_params.update(width=self.width) pos = self.position.in_units("code_length").d width = self.width.in_units("code_length").d lens_params.update(camera_data=np.vstack((pos, width, self.unit_vectors.d))) return lens_params def set_lens(self, lens_type): r"""Set the lens to be used with this camera. Parameters ---------- lens_type : string Must be one of the following: 'plane-parallel' 'perspective' 'stereo-perspective' 'fisheye' 'spherical' 'stereo-spherical' """ if isinstance(lens_type, Lens): self.lens = lens_type elif lens_type not in lenses: raise RuntimeError( f"Lens type {lens_type} not in available list of available lens " "types ({})".format(", ".join([f"{_!r}" for _ in lenses])) ) else: self.lens = lenses[lens_type]() self.lens.set_camera(self) def set_defaults_from_data_source(self, data_source): """Resets the camera attributes to their default values""" position = data_source.ds.domain_right_edge width = 1.5 * data_source.ds.domain_width.max() (xmi, xma), (ymi, yma), (zmi, zma) = data_source.quantities["Extrema"]( ["x", "y", "z"] ) width = np.sqrt((xma - xmi) ** 2 + (yma - ymi) ** 2 + (zma - zmi) ** 2) focus = data_source.get_field_parameter("center") if is_sequence(width) and len(width) > 1 and isinstance(width[1], str): width = data_source.ds.quan(width[0], units=width[1]) # Now convert back to code length for subsequent manipulation width = width.in_units("code_length") # .value if not is_sequence(width): width = data_source.ds.arr([width, width, width], units="code_length") # left/right, top/bottom, front/back if not isinstance(width, YTArray): width = data_source.ds.arr(width, units="code_length") if not isinstance(focus, YTArray): focus = data_source.ds.arr(focus, units="code_length") # We can't use the property setters yet, since they rely on attributes # that will not be set up until the base class initializer is called. # See Issue #1131. self._width = width self._focus = focus self._position = position self._domain_center = data_source.ds.domain_center self._domain_width = data_source.ds.domain_width super().__init__( self.focus - self.position, self.north_vector, steady_north=False ) self._moved = True def set_width(self, width): r"""Set the width of the image that will be produced by this camera. Parameters ---------- width : number, YTQuantity, :obj:`!iterable`, or 3 element YTArray The width of the volume rendering in the horizontal, vertical, and depth directions. If a scalar, assumes that the width is the same in all three directions. If an iterable, must contain only scalars or (length, unit) tuples. """ self.width = width self.switch_orientation() def get_width(self): """Return the current camera width""" return self.width def set_position(self, position, north_vector=None): r"""Set the position of the camera. Parameters ---------- position : number, YTQuantity, :obj:`!iterable`, or 3 element YTArray If a scalar, assumes that the position is the same in all three coordinates. If an iterable, must contain only scalars or (length, unit) tuples. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. """ if north_vector is not None: self.north_vector = north_vector self.position = position def get_position(self): """Return the current camera position""" return self.position def set_focus(self, new_focus): """Sets the point the Camera is pointed at. Parameters ---------- new_focus : number, YTQuantity, :obj:`!iterable`, or 3 element YTArray If a scalar, assumes that the focus is the same is all three coordinates. If an iterable, must contain only scalars or (length, unit) tuples. """ self.focus = new_focus def get_focus(self): """Returns the current camera focus""" return self.focus def switch_orientation(self, normal_vector=None, north_vector=None): r"""Change the view direction based on any of the orientation parameters. This will recalculate all the necessary vectors and vector planes related to an orientable object. Parameters ---------- normal_vector: array_like, optional The new looking vector from the camera to the focus. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. """ if north_vector is None: north_vector = self.north_vector if normal_vector is None: normal_vector = self.normal_vector self._setup_normalized_vectors(normal_vector, north_vector) self.lens.setup_box_properties(self) def switch_view(self, normal_vector=None, north_vector=None): r"""Change the view based on any of the view parameters. This will recalculate the orientation and width based on any of normal_vector, width, center, and north_vector. Parameters ---------- normal_vector: array_like, optional The new looking vector from the camera to the focus. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. """ if north_vector is None: north_vector = self.north_vector if normal_vector is None: normal_vector = self.normal_vector self.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) self._moved = True def rotate(self, theta, rot_vector=None, rot_center=None): r"""Rotate by a given angle Rotate the view. If `rot_vector` is None, rotation will occur around the `north_vector`. Parameters ---------- theta : float, in radians Angle (in radians) by which to rotate the view. rot_vector : array_like, optional Specify the rotation vector around which rotation will occur. Defaults to None, which sets rotation around `north_vector` rot_center : array_like, optional Specify the center around which rotation will occur. Defaults to None, which sets rotation around the original camera position (i.e. the camera position does not change) Examples -------- >>> import yt >>> import numpy as np >>> from yt.visualization.volume_rendering.api import Scene >>> sc = Scene() >>> cam = sc.add_camera() >>> # rotate the camera by pi / 4 radians: >>> cam.rotate(np.pi / 4.0) >>> # rotate the camera about the y-axis instead of cam.north_vector: >>> cam.rotate(np.pi / 4.0, np.array([0.0, 1.0, 0.0])) >>> # rotate the camera about the origin instead of its own position: >>> cam.rotate(np.pi / 4.0, rot_center=np.array([0.0, 0.0, 0.0])) """ rotate_all = rot_vector is not None if rot_vector is None: rot_vector = self.north_vector if rot_center is None: rot_center = self._position rot_vector = ensure_numpy_array(rot_vector) rot_vector = rot_vector / np.linalg.norm(rot_vector) new_position = self._position - rot_center R = get_rotation_matrix(theta, rot_vector) new_position = np.dot(R, new_position) + rot_center if (new_position == self._position).all(): normal_vector = self.unit_vectors[2] else: normal_vector = rot_center - new_position normal_vector = normal_vector / np.sqrt((normal_vector**2).sum()) if rotate_all: self.switch_view( normal_vector=np.dot(R, normal_vector), north_vector=np.dot(R, self.unit_vectors[1]), ) else: self.switch_view(normal_vector=np.dot(R, normal_vector)) if (new_position != self._position).any(): self.set_position(new_position) def pitch(self, theta, rot_center=None): r"""Rotate by a given angle about the horizontal axis Pitch the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to pitch the view. rot_center : array_like, optional Specify the center around which rotation will occur. Examples -------- >>> import yt >>> import numpy as np >>> from yt.visualization.volume_rendering.api import Scene >>> sc = Scene() >>> sc.add_camera() >>> # pitch the camera by pi / 4 radians: >>> cam.pitch(np.pi / 4.0) >>> # pitch the camera about the origin instead of its own position: >>> cam.pitch(np.pi / 4.0, rot_center=np.array([0.0, 0.0, 0.0])) """ self.rotate(theta, rot_vector=self.unit_vectors[0], rot_center=rot_center) def yaw(self, theta, rot_center=None): r"""Rotate by a given angle about the vertical axis Yaw the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to yaw the view. rot_center : array_like, optional Specify the center around which rotation will occur. Examples -------- >>> import yt >>> import numpy as np >>> from yt.visualization.volume_rendering.api import Scene >>> sc = Scene() >>> cam = sc.add_camera() >>> # yaw the camera by pi / 4 radians: >>> cam.yaw(np.pi / 4.0) >>> # yaw the camera about the origin instead of its own position: >>> cam.yaw(np.pi / 4.0, rot_center=np.array([0.0, 0.0, 0.0])) """ self.rotate(theta, rot_vector=self.unit_vectors[1], rot_center=rot_center) def roll(self, theta, rot_center=None): r"""Rotate by a given angle about the view normal axis Roll the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to roll the view. rot_center : array_like, optional Specify the center around which rotation will occur. Examples -------- >>> import yt >>> import numpy as np >>> from yt.visualization.volume_rendering.api import Scene >>> sc = Scene() >>> cam = sc.add_camera(ds) >>> # roll the camera by pi / 4 radians: >>> cam.roll(np.pi / 4.0) >>> # roll the camera about the origin instead of its own position: >>> cam.roll(np.pi / 4.0, rot_center=np.array([0.0, 0.0, 0.0])) """ self.rotate(theta, rot_vector=self.unit_vectors[2], rot_center=rot_center) def iter_rotate(self, theta, n_steps, rot_vector=None, rot_center=None): r"""Loop over rotate, creating a rotation This will rotate `n_steps` until the current view has been rotated by an angle `theta`. Parameters ---------- theta : float, in radians Angle (in radians) by which to rotate the view. n_steps : int The number of snapshots to make. rot_vector : array_like, optional Specify the rotation vector around which rotation will occur. Defaults to None, which sets rotation around the original `north_vector` rot_center : array_like, optional Specify the center around which rotation will occur. Defaults to None, which sets rotation around the original camera position (i.e. the camera position does not change) Examples -------- >>> import yt >>> import numpy as np >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> cam = sc.camera >>> for i in cam.iter_rotate(np.pi, 10): ... im = sc.render() ... sc.save("rotation_%04i.png" % i) """ dtheta = (1.0 * theta) / n_steps for i in range(n_steps): self.rotate(dtheta, rot_vector=rot_vector, rot_center=rot_center) yield i def iter_move(self, final, n_steps, exponential=False): r"""Loop over an iter_move and return snapshots along the way. This will yield `n_steps` until the current view has been moved to a final center of `final`. Parameters ---------- final : YTArray The final center to move to after `n_steps` n_steps : int The number of snapshots to make. exponential : boolean Specifies whether the move/zoom transition follows an exponential path toward the destination or linear. Default is False. Examples -------- >>> import yt >>> import numpy as np >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> final_position = ds.arr([0.2, 0.3, 0.6], "unitary") >>> im, sc = yt.volume_render(ds) >>> cam = sc.camera >>> for i in cam.iter_move(final_position, 10): ... sc.render() ... sc.save("move_%04i.png" % i) """ assert isinstance(final, YTArray) if exponential: position_diff = (final / self.position) * 1.0 dx = position_diff ** (1.0 / n_steps) else: dx = (final - self.position) * 1.0 / n_steps for i in range(n_steps): if exponential: self.set_position(self.position * dx) else: self.set_position(self.position + dx) yield i def zoom(self, factor): r"""Change the width of the FOV of the camera. This will appear to zoom the camera in by some `factor` toward the focal point along the current view direction, but really it's just changing the width of the field of view. Parameters ---------- factor : float The factor by which to divide the width Examples -------- >>> import yt >>> from yt.visualization.volume_rendering.api import Scene >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> cam = sc.add_camera(ds) >>> cam.zoom(1.1) """ self.width[:2] = self.width[:2] / factor def iter_zoom(self, final, n_steps): r"""Loop over a iter_zoom and return snapshots along the way. This will yield `n_steps` snapshots until the current view has been zooming in to a final factor of `final`. Parameters ---------- final : float The zoom factor, with respect to current, desired at the end of the sequence. n_steps : int The number of zoom snapshots to make. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> cam = sc.camera >>> for i in cam.iter_zoom(100.0, 10): ... sc.render() ... sc.save("zoom_%04i.png" % i) """ f = final ** (1.0 / n_steps) for i in range(n_steps): self.zoom(f) yield i def __repr__(self): disp = ( ":\n\tposition:%s\n\tfocus:%s\n\t" + "north_vector:%s\n\twidth:%s\n\tlight:%s\n\tresolution:%s\n" ) % ( self.position, self.focus, self.north_vector, self.width, self.light, self.resolution, ) disp += f"Lens: {self.lens}" return disp yt-project-yt-f043ac8/yt/visualization/volume_rendering/camera_path.py000066400000000000000000000300531510711153200263720ustar00rootroot00000000000000import random import numpy as np from yt.visualization.volume_rendering.create_spline import create_spline class Keyframes: def __init__( self, x, y, z=None, north_vectors=None, up_vectors=None, times=None, niter=50000, init_temp=10.0, alpha=0.999, fixed_start=False, ): r"""Keyframes for camera path generation. From a set of keyframes with position and optional up and north vectors, an interpolated camera path is generated. Parameters ---------- x : array_like The x positions of the keyframes y : array_like The y positions of the keyframes z : array_like, optional The z positions of the keyframes. Default: 0.0 north_vectors : array_like, optional The north vectors of the keyframes. Default: None up_vectors : array_like, optional The up vectors of the keyframes. Default: None times : array_like, optional The times of the keyframes. Default: arange(N) niter : integer, optional Maximum number of iterations to find solution. Default: 50000 init_temp : float, optional Initial temperature for simulated annealing when finding a solution. Lower initial temperatures result in an initial solution in first several iterations that changes more rapidly. Default: 10.0 alpha : float, optional Exponent in cooling function in simulated annealing. Must be < 1. In each iteration, the temperature_new = temperature_old * alpha. Default: 0.999 fixed_start: boolean, optional If true, the first point never changes when searching for shortest path. Default: False Examples -------- >>> import matplotlib.pyplot as plt ... import numpy as np ... from yt.visualization.volume_rendering.camera_path import * >>> # Make a camera path from 10 random (x, y, z) keyframes >>> data = np.random.random(10, 3) >>> kf = Keyframes(data[:, 0], data[:, 1], data[:, 2]) >>> path = kf.create_path(250, shortest_path=False) >>> # Plot the keyframes in the x-y plane and camera path >>> plt.plot(kf.pos[:, 0], kf.pos[:, 1], "ko") >>> plt.plot(path["position"][:, 0], path["position"][:, 1]) >>> plt.savefig("path.png") """ Nx = len(x) Ny = len(y) if z is not None: Nz = len(z) ndims = 3 else: Nz = 1 ndims = 2 if Nx * Ny * Nz != Nx**ndims: print("Need Nx (%d) == Ny (%d) == Nz (%d)" % (Nx, Ny, Nz)) raise RuntimeError self.nframes = Nx self.pos = np.zeros((Nx, 3)) self.pos[:, 0] = x self.pos[:, 1] = y if z is not None: self.pos[:, 2] = z else: self.pos[:, 2] = 0.0 self.north_vectors = north_vectors self.up_vectors = up_vectors if times is None: self.times = np.arange(self.nframes) else: self.times = times self.cartesian_matrix() self.setup_tsp(niter, init_temp, alpha, fixed_start) def setup_tsp(self, niter=50000, init_temp=10.0, alpha=0.999, fixed_start=False): r"""Setup parameters for Travelling Salesman Problem. Parameters ---------- niter : integer, optional Maximum number of iterations to find solution. Default: 50000 init_temp : float, optional Initial temperature for simulated annealing when finding a solution. Lower initial temperatures result in an initial solution in first several iterations that changes more rapidly. Default: 10.0 alpha : float, optional Exponent in cooling function in simulated annealing. Must be < 1. In each iteration, the temperature_new = temperature_old * alpha. Default: 0.999 fixed_start: boolean, optional If true, the first point never changes when searching for shortest path. Default: False """ # randomize tour self.tour = list(range(self.nframes)) rng = np.random.default_rng() rng.shuffle(self.tour) if fixed_start: first = self.tour.index(0) self.tour[0], self.tour[first] = self.tour[first], self.tour[0] self.max_iterations = niter self.initial_temp = init_temp self.alpha = alpha self.fixed_start = fixed_start self.best_score = None self.best = None def set_times(self, times): self.times = times def rand_seq(self): r""" Generates values in random order, equivalent to using shuffle in random without generation all values at once. """ values = list(range(self.nframes)) for i in range(self.nframes): # pick a random index into remaining values j = i + int(random.random() * (self.nframes - i)) # swap the values values[j], values[i] = values[i], values[j] # return the swapped value yield values[i] def all_pairs(self): r""" Generates all (i,j) pairs for (i,j) for 0-size """ for i in self.rand_seq(): for j in self.rand_seq(): yield (i, j) def reversed_sections(self, tour): r""" Generator to return all possible variations where a section between two cities are swapped. """ for i, j in self.all_pairs(): if i == j: continue copy = tour[:] if i < j: copy[i : j + 1] = reversed(tour[i : j + 1]) else: copy[i + 1 :] = reversed(tour[:j]) copy[:j] = reversed(tour[i + 1 :]) if self.fixed_start: ind = copy.index(0) copy[0], copy[ind] = copy[ind], copy[0] if copy != tour: # no point return the same tour yield copy def cartesian_matrix(self): r""" Create a distance matrix for the city coords that uses straight line distance """ self.dist_matrix = np.zeros((self.nframes, self.nframes)) xmat = np.zeros((self.nframes, self.nframes)) xmat[:, :] = self.pos[:, 0] dx = xmat - xmat.T ymat = np.zeros((self.nframes, self.nframes)) ymat[:, :] = self.pos[:, 1] dy = ymat - ymat.T zmat = np.zeros((self.nframes, self.nframes)) zmat[:, :] = self.pos[:, 2] dz = zmat - zmat.T self.dist_matrix = np.sqrt(dx * dx + dy * dy + dz * dz) def tour_length(self, tour): r""" Calculate the total length of the tour based on the distance matrix """ total = 0 num_cities = len(tour) for i in range(num_cities): j = (i + 1) % num_cities city_i = tour[i] city_j = tour[j] total += self.dist_matrix[city_i, city_j] return -total def cooling(self): T = self.initial_temp while True: yield T T = self.alpha * T def prob(self, prev, next, temperature): if next > prev: return 1.0 else: return np.exp(-abs(next - prev) / temperature) def get_shortest_path(self): """ Determine shortest path between all keyframes. """ # this obviously doesn't work. When someone fixes it, remove the NOQA self.setup_tsp(niter, init_temp, alpha, fixed_start) # NOQA num_eval = 1 cooling_schedule = self.cooling() current = self.tour current_score = self.tour_length(current) for temperature in cooling_schedule: done = False # Examine moves around the current position for next in self.reversed_sections(current): if num_eval >= self.max_iterations: done = True break next_score = self.tour_length(next) num_eval += 1 # Anneal. Accept new solution if a random number is # greater than our "probability". p = self.prob(current_score, next_score, temperature) if random.random() < p: current = next self.current_score = next_score if self.current_score > self.best_score: # print(num_eval, self.current_score, self.best_score, current) self.best_score = self.current_score self.best = current break if done: break self.pos = self.pos[self.tour, :] if self.north_vectors is not None: self.north_vectors = self.north_vectors[self.tour] if self.up_vectors is not None: self.up_vectors = self.up_vectors[self.tour] def create_path(self, npoints, path_time=None, tension=0.5, shortest_path=False): r"""Create a interpolated camera path from keyframes. Parameters ---------- npoints : integer Number of points to interpolate from keyframes path_time : array_like, optional Times of interpolated points. Default: Linearly spaced tension : float, optional Controls how sharp of a curve the spline takes. A higher tension allows for more sharp turns. Default: 0.5 shortest_path : boolean, optional If true, estimate the shortest path between the keyframes. Default: False Returns ------- path : dict Dictionary (time, position, north_vectors, up_vectors) of camera path. Also saved to self.path. """ self.npoints = npoints self.path = { "time": np.zeros(npoints), "position": np.zeros((npoints, 3)), "north_vectors": np.zeros((npoints, 3)), "up_vectors": np.zeros((npoints, 3)), } if shortest_path: self.get_shortest_path() if path_time is None: path_time = np.linspace(0, self.nframes, npoints) self.path["time"] = path_time for dim in range(3): self.path["position"][:, dim] = create_spline( self.times, self.pos[:, dim], path_time, tension=tension ) if self.north_vectors is not None: self.path["north_vectors"][:, dim] = create_spline( self.times, self.north_vectors[:, dim], path_time, tension=tension ) if self.up_vectors is not None: self.path["up_vectors"][:, dim] = create_spline( self.times, self.up_vectors[:, dim], path_time, tension=tension ) return self.path def write_path(self, filename="path.dat"): r"""Writes camera path to ASCII file Parameters ---------- filename : string, optional Filename containing the camera path. Default: path.dat """ fp = open(filename, "w") fp.write( "#%11s %12s %12s %12s %12s %12s %12s %12s %12s\n" % ("x", "y", "z", "north_x", "north_y", "north_z", "up_x", "up_y", "up_z") ) for i in range(self.npoints): fp.write( "{:.12f} {:.12f} {:.12f} {:.12f} {:.12f} {:.12f} {:.12f} {:.12f} {:.12f}\n".format( self.path["position"][i, 0], self.path["position"][i, 1], self.path["position"][i, 2], self.path["north_vectors"][i, 0], self.path["north_vectors"][i, 1], self.path["north_vectors"][i, 2], self.path["up_vectors"][i, 0], self.path["up_vectors"][i, 1], self.path["up_vectors"][i, 2], ) ) fp.close() yt-project-yt-f043ac8/yt/visualization/volume_rendering/create_spline.py000066400000000000000000000035121510711153200267430ustar00rootroot00000000000000import sys import numpy as np def create_spline(old_x, old_y, new_x, tension=0.5, sorted=False): """ Inputs: old_x: array of floats Original x-data to be fit with a Catmull-Rom spline old_y: array of floats Original y-data to be fit with a Catmull-Rom spline new_x: array of floats interpolate to these x-coordinates tension: float, optional controls the tension at the specified coordinates sorted: boolean, optional If True, then the old_x and old_y arrays are sorted, and then this routine does not try to sort the coordinates Outputs: result: array of floats interpolated y-coordinates """ ndata = len(old_x) N = len(new_x) result = np.zeros(N) if not sorted: isort = np.argsort(old_x) old_x = old_x[isort] old_y = old_y[isort] # Floor/ceiling of values outside of the original data new_x = np.minimum(new_x, old_x[-1]) new_x = np.maximum(new_x, old_x[0]) ind = np.searchsorted(old_x, new_x) im2 = np.maximum(ind - 2, 0) im1 = np.maximum(ind - 1, 0) ip1 = np.minimum(ind + 1, ndata - 1) for i in range(N): if ind[i] != im1[i]: u = (new_x[i] - old_x[im1[i]]) / (old_x[ind[i]] - old_x[im1[i]]) elif ind[i] == im1[i]: u = 0 else: print("Bad index during interpolation?") sys.exit() b0 = -tension * u + 2 * tension * u**2 - tension * u**3 b1 = 1.0 + (tension - 3) * u**2 + (2 - tension) * u**3 b2 = tension * u + (3 - 2 * tension) * u**2 + (tension - 2) * u**3 b3 = -tension * u**2 + tension * u**3 result[i] = ( b0 * old_y[im2[i]] + b1 * old_y[im1[i]] + b2 * old_y[ind[i]] + b3 * old_y[ip1[i]] ) return result yt-project-yt-f043ac8/yt/visualization/volume_rendering/image_handling.py000066400000000000000000000100531510711153200270520ustar00rootroot00000000000000import numpy as np from yt.funcs import mylog from yt.utilities.on_demand_imports import _h5py as h5py def export_rgba( image, fn, h5=True, fits=False, ): """ This function accepts an *image*, of shape (N,M,4) corresponding to r,g,b,a, and saves to *fn*. If *h5* is True, then it will save in hdf5 format. If *fits* is True, it will save in fits format. """ if (not h5 and not fits) or (h5 and fits): raise ValueError("Choose either HDF5 or FITS format!") if h5: f = h5py.File(f"{fn}.h5", mode="w") f.create_dataset("R", data=image[:, :, 0]) f.create_dataset("G", data=image[:, :, 1]) f.create_dataset("B", data=image[:, :, 2]) f.create_dataset("A", data=image[:, :, 3]) f.close() if fits: from yt.visualization.fits_image import FITSImageData data = {} data["r"] = image[:, :, 0] data["g"] = image[:, :, 1] data["b"] = image[:, :, 2] data["a"] = image[:, :, 3] fib = FITSImageData(data) fib.writeto(f"{fn}.fits", overwrite=True) def import_rgba(name, h5=True): """ This function will read back in an HDF5 file, as saved by export_rgba, and return the frames to the user. *name* is the name of the file to be read in. """ if h5: f = h5py.File(name, mode="r") r = f["R"].value g = f["G"].value b = f["B"].value a = f["A"].value f.close() else: mylog.error("No support for fits import.") return np.array([r, g, b, a]).swapaxes(0, 2).swapaxes(0, 1) def plot_channel( image, name, cmap="gist_heat", log=True, dex=3, zero_factor=1.0e-10, label=None, label_color="w", label_size="large", ): """ This function will plot a single channel. *image* is an array shaped like (N,M), *name* is the pefix for the output filename. *cmap* is the name of the colormap to apply, *log* is whether or not the channel should be logged. Additionally, you may optionally specify the minimum-value cutoff for scaling as *dex*, which is taken with respect to the minimum value of the image. *zero_factor* applies a minimum value to all zero-valued elements. Optionally, *label*, *label_color* and *label_size* may be specified. """ import matplotlib as mpl from matplotlib import pyplot as plt from matplotlib.colors import LogNorm Nvec = image.shape[0] image[np.isnan(image)] = 0.0 ma = image[image > 0.0].max() image[image == 0.0] = ma * zero_factor if log: mynorm = LogNorm(ma / (10.0**dex), ma) fig = plt.gcf() ax = plt.gca() fig.clf() fig.set_dpi(100) fig.set_size_inches((Nvec / 100.0, Nvec / 100.0)) fig.subplots_adjust( left=0.0, right=1.0, bottom=0.0, top=1.0, wspace=0.0, hspace=0.0 ) mycm = mpl.colormaps[cmap] if log: ax.imshow(image, cmap=mycm, norm=mynorm, interpolation="nearest") else: ax.imshow(image, cmap=mycm, interpolation="nearest") if label is not None: ax.text(20, 20, label, color=label_color, size=label_size) fig.savefig(f"{name}_{cmap}.png") fig.clf() def plot_rgb(image, name, label=None, label_color="w", label_size="large"): """ This will plot the r,g,b channels of an *image* of shape (N,M,3) or (N,M,4). *name* is the prefix of the file name, which will be supplemented with "_rgb.png." *label*, *label_color* and *label_size* may also be specified. """ import matplotlib.pyplot as plt Nvec = image.shape[0] image[np.isnan(image)] = 0.0 if image.shape[2] >= 4: image = image[:, :, :3] fig = plt.gcf() ax = plt.gca() fig.clf() fig.set_dpi(100) fig.set_size_inches((Nvec / 100.0, Nvec / 100.0)) fig.subplots_adjust( left=0.0, right=1.0, bottom=0.0, top=1.0, wspace=0.0, hspace=0.0 ) ax.imshow(image, interpolation="nearest") if label is not None: ax.text(20, 20, label, color=label_color, size=label_size) fig.savefig(f"{name}_rgb.png") fig.clf() yt-project-yt-f043ac8/yt/visualization/volume_rendering/lens.py000066400000000000000000000752011510711153200250730ustar00rootroot00000000000000import numpy as np from yt.data_objects.image_array import ImageArray from yt.units._numpy_wrapper_functions import uhstack, unorm, uvstack from yt.utilities.lib.grid_traversal import arr_fisheye_vectors from yt.utilities.math_utils import get_rotation_matrix from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, ) class Lens(ParallelAnalysisInterface): """A Lens is used to define the set of rays for rendering.""" def __init__(self): super().__init__() self.viewpoint = None self.sub_samples = 5 self.num_threads = 0 self.box_vectors = None self.origin = None self.back_center = None self.front_center = None self.sampler = None def set_camera(self, camera): """Set the properties of the lens based on the camera. This is a proxy for setup_box_properties """ self.setup_box_properties(camera) def new_image(self, camera): """Initialize a new ImageArray to be used with this lens.""" self.current_image = ImageArray( np.zeros( (camera.resolution[0], camera.resolution[1], 4), dtype="float64", order="C", ), info={"imtype": "rendering"}, ) return self.current_image def setup_box_properties(self, camera): """Set up the view and stage based on the properties of the camera.""" unit_vectors = camera.unit_vectors width = camera.width center = camera.focus self.box_vectors = camera.scene.arr( [ unit_vectors[0] * width[0], unit_vectors[1] * width[1], unit_vectors[2] * width[2], ] ) self.origin = center - 0.5 * width.dot(unit_vectors) self.back_center = center - 0.5 * width[2] * unit_vectors[2] self.front_center = center + 0.5 * width[2] * unit_vectors[2] self.set_viewpoint(camera) def set_viewpoint(self, camera): """ Set the viewpoint used for AMRKDTree traversal such that you yield bricks from back to front or front to back from with respect to this point. Must be implemented for each Lens type. """ raise NotImplementedError("Need to choose viewpoint for this class") class PlaneParallelLens(Lens): r"""The lens for orthographic projections. All rays emerge parallel to each other, arranged along a plane. The initializer takes no parameters. """ def __init__(self): super().__init__() def _get_sampler_params(self, camera, render_source): if render_source.zbuffer is not None: image = render_source.zbuffer.rgba else: image = self.new_image(camera) vp_pos = np.concatenate( [ camera.inv_mat.ravel("F").d, self.back_center.ravel().in_units("code_length").d, ] ) sampler_params = { "vp_pos": vp_pos, "vp_dir": self.box_vectors[2], # All the same "center": self.back_center, "bounds": ( -camera.width[0] / 2.0, camera.width[0] / 2.0, -camera.width[1] / 2.0, camera.width[1] / 2.0, ), "x_vec": camera.unit_vectors[0], "y_vec": camera.unit_vectors[1], "width": np.array(camera.width, dtype="float64"), "image": image, "lens_type": "plane-parallel", } return sampler_params def set_viewpoint(self, camera): """Set the viewpoint based on the camera""" # This is a hack that should be replaced by an alternate plane-parallel # traversal. Put the camera really far away so that the effective # viewpoint is infinitely far away, making for parallel rays. self.viewpoint = ( self.front_center + camera.unit_vectors[2] * 1.0e6 * camera.width[2] ) def project_to_plane(self, camera, pos, res=None): if res is None: res = camera.resolution origin = self.origin.in_units("code_length").d front_center = self.front_center.in_units("code_length").d width = camera.width.in_units("code_length").d dx = np.array(np.dot(pos - origin, camera.unit_vectors[1])) dy = np.array(np.dot(pos - origin, camera.unit_vectors[0])) dz = np.array(np.dot(pos - front_center, -camera.unit_vectors[2])) # Transpose into image coords. px = (res[0] * (dy / width[0])).astype("int64") py = (res[1] * (dx / width[1])).astype("int64") return px, py, dz def __repr__(self): return ( ":\n" "\tlens_type:plane-parallel\n" f"\tviewpoint:{self.viewpoint}" ) class PerspectiveLens(Lens): r"""A lens for viewing a scene with a set of rays within an opening angle. The scene will have an element of perspective to it since the rays are not parallel. """ def __init__(self): super().__init__() def new_image(self, camera): self.current_image = ImageArray( np.zeros( (camera.resolution[0], camera.resolution[1], 4), dtype="float64", order="C", ), info={"imtype": "rendering"}, ) return self.current_image def _get_sampler_params(self, camera, render_source): # Enforce width[1] / width[0] = resolution[1] / resolution[0] camera.width[1] = camera.width[0] * ( camera.resolution[1] / camera.resolution[0] ) if render_source.zbuffer is not None: image = render_source.zbuffer.rgba else: image = self.new_image(camera) east_vec = camera.unit_vectors[0] north_vec = camera.unit_vectors[1] normal_vec = camera.unit_vectors[2] px = np.linspace(-0.5, 0.5, camera.resolution[0])[np.newaxis, :] py = np.linspace(-0.5, 0.5, camera.resolution[1])[np.newaxis, :] sample_x = camera.width[0] * np.array(east_vec.reshape(3, 1) * px) sample_x = sample_x.transpose() sample_y = camera.width[1] * np.array(north_vec.reshape(3, 1) * py) sample_y = sample_y.transpose() vectors = np.zeros( (camera.resolution[0], camera.resolution[1], 3), dtype="float64", order="C" ) sample_x = np.repeat( sample_x.reshape(camera.resolution[0], 1, 3), camera.resolution[1], axis=1 ) sample_y = np.repeat( sample_y.reshape(1, camera.resolution[1], 3), camera.resolution[0], axis=0 ) normal_vecs = np.tile(normal_vec, camera.resolution[0] * camera.resolution[1]) normal_vecs = normal_vecs.reshape(camera.resolution[0], camera.resolution[1], 3) # The maximum possible length of ray max_length = unorm(camera.position - camera._domain_center) + 0.5 * unorm( camera._domain_width ) # Rescale the ray to be long enough to cover the entire domain vectors = (sample_x + sample_y + normal_vecs * camera.width[2]) * ( max_length / camera.width[2] ) positions = np.tile( camera.position, camera.resolution[0] * camera.resolution[1] ) positions = positions.reshape(camera.resolution[0], camera.resolution[1], 3) uv = np.ones(3, dtype="float64") image = self.new_image(camera) sampler_params = { "vp_pos": positions, "vp_dir": vectors, "center": self.back_center, "bounds": (0.0, 1.0, 0.0, 1.0), "x_vec": uv, "y_vec": uv, "width": np.zeros(3, dtype="float64"), "image": image, "lens_type": "perspective", } return sampler_params def set_viewpoint(self, camera): """ For a PerspectiveLens, the viewpoint is the front center. """ self.viewpoint = self.front_center def project_to_plane(self, camera, pos, res=None): if res is None: res = camera.resolution width = camera.width.in_units("code_length").d position = camera.position.in_units("code_length").d width[1] = width[0] * res[1] / res[0] sight_vector = pos - position pos1 = sight_vector for i in range(0, sight_vector.shape[0]): sight_vector_norm = np.sqrt(np.dot(sight_vector[i], sight_vector[i])) if sight_vector_norm != 0: sight_vector[i] = sight_vector[i] / sight_vector_norm sight_center = camera.position + camera.width[2] * camera.unit_vectors[2] sight_center = sight_center.in_units("code_length").d for i in range(0, sight_vector.shape[0]): sight_angle_cos = np.dot(sight_vector[i], camera.unit_vectors[2]) # clip sight_angle_cos since floating point noise might # go outside the domain of arccos sight_angle_cos = np.clip(sight_angle_cos, -1.0, 1.0) if np.arccos(sight_angle_cos) < 0.5 * np.pi: sight_length = width[2] / sight_angle_cos else: # If the corner is on the backwards, then we put it outside of # the image It can not be simply removed because it may connect # to other corner within the image, which produces visible # domain boundary line sight_length = np.sqrt(width[0] ** 2 + width[1] ** 2) sight_length = sight_length / np.sqrt(1 - sight_angle_cos**2) pos1[i] = position + sight_length * sight_vector[i] dx = np.dot(pos1 - sight_center, camera.unit_vectors[0]) dy = np.dot(pos1 - sight_center, camera.unit_vectors[1]) dz = np.dot(pos - position, camera.unit_vectors[2]) # Transpose into image coords. px = (res[0] * 0.5 + res[0] / width[0] * dx).astype("int64") py = (res[1] * 0.5 + res[1] / width[1] * dy).astype("int64") return px, py, dz def __repr__(self): disp = f":\n\tlens_type:perspective\n\tviewpoint:{self.viewpoint}" return disp class StereoPerspectiveLens(Lens): """A lens that includes two sources for perspective rays, for 3D viewing The degree of differences between the left and right images is controlled by the disparity (the maximum distance between corresponding points in the left and right images). By default, the disparity is set to be 3 pixels. """ def __init__(self): super().__init__() self.disparity = None def new_image(self, camera): """Initialize a new ImageArray to be used with this lens.""" self.current_image = ImageArray( np.zeros( (camera.resolution[0], camera.resolution[1], 4), dtype="float64", order="C", ), info={"imtype": "rendering"}, ) return self.current_image def _get_sampler_params(self, camera, render_source): # Enforce width[1] / width[0] = 2 * resolution[1] / resolution[0] # For stereo-type lens, images for left and right eye are pasted together, # so the resolution of single-eye image will be 50% of the whole one. camera.width[1] = camera.width[0] * ( 2.0 * camera.resolution[1] / camera.resolution[0] ) if self.disparity is None: self.disparity = 3.0 * camera.width[0] / camera.resolution[0] if render_source.zbuffer is not None: image = render_source.zbuffer.rgba else: image = self.new_image(camera) vectors_left, positions_left = self._get_positions_vectors( camera, -self.disparity ) vectors_right, positions_right = self._get_positions_vectors( camera, self.disparity ) uv = np.ones(3, dtype="float64") image = self.new_image(camera) vectors_comb = uvstack([vectors_left, vectors_right]) positions_comb = uvstack([positions_left, positions_right]) image.shape = (camera.resolution[0], camera.resolution[1], 4) vectors_comb.shape = (camera.resolution[0], camera.resolution[1], 3) positions_comb.shape = (camera.resolution[0], camera.resolution[1], 3) sampler_params = { "vp_pos": positions_comb, "vp_dir": vectors_comb, "center": self.back_center, "bounds": (0.0, 1.0, 0.0, 1.0), "x_vec": uv, "y_vec": uv, "width": np.zeros(3, dtype="float64"), "image": image, "lens_type": "stereo-perspective", } return sampler_params def _get_positions_vectors(self, camera, disparity): single_resolution_x = int(np.floor(camera.resolution[0]) / 2) east_vec = camera.unit_vectors[0] north_vec = camera.unit_vectors[1] normal_vec = camera.unit_vectors[2] angle_disparity = -np.arctan2( disparity.in_units(camera.width.units), camera.width[2] ) R = get_rotation_matrix(angle_disparity, north_vec) east_vec_rot = np.dot(R, east_vec) normal_vec_rot = np.dot(R, normal_vec) px = np.linspace(-0.5, 0.5, single_resolution_x)[np.newaxis, :] py = np.linspace(-0.5, 0.5, camera.resolution[1])[np.newaxis, :] sample_x = camera.width[0] * np.array(east_vec_rot.reshape(3, 1) * px) sample_x = sample_x.transpose() sample_y = camera.width[1] * np.array(north_vec.reshape(3, 1) * py) sample_y = sample_y.transpose() vectors = np.zeros( (single_resolution_x, camera.resolution[1], 3), dtype="float64", order="C" ) sample_x = np.repeat( sample_x.reshape(single_resolution_x, 1, 3), camera.resolution[1], axis=1 ) sample_y = np.repeat( sample_y.reshape(1, camera.resolution[1], 3), single_resolution_x, axis=0 ) normal_vecs = np.tile( normal_vec_rot, single_resolution_x * camera.resolution[1] ) normal_vecs = normal_vecs.reshape(single_resolution_x, camera.resolution[1], 3) east_vecs = np.tile(east_vec_rot, single_resolution_x * camera.resolution[1]) east_vecs = east_vecs.reshape(single_resolution_x, camera.resolution[1], 3) # The maximum possible length of ray max_length = ( unorm(camera.position - camera._domain_center) + 0.5 * unorm(camera._domain_width) + np.abs(self.disparity) ) # Rescale the ray to be long enough to cover the entire domain vectors = (sample_x + sample_y + normal_vecs * camera.width[2]) * ( max_length / camera.width[2] ) positions = np.tile(camera.position, single_resolution_x * camera.resolution[1]) positions = positions.reshape(single_resolution_x, camera.resolution[1], 3) # Here the east_vecs is non-rotated one positions = positions + east_vecs * disparity return vectors, positions def project_to_plane(self, camera, pos, res=None): if res is None: res = camera.resolution # Enforce width[1] / width[0] = 2 * resolution[1] / resolution[0] # For stereo-type lens, images for left and right eye are pasted together, # so the resolution of single-eye image will be 50% of the whole one. camera.width[1] = camera.width[0] * (2.0 * res[1] / res[0]) if self.disparity is None: self.disparity = 3.0 * camera.width[0] / camera.resolution[0] px_left, py_left, dz_left = self._get_px_py_dz( camera, pos, res, -self.disparity ) px_right, py_right, dz_right = self._get_px_py_dz( camera, pos, res, self.disparity ) px = uvstack([px_left, px_right]) py = uvstack([py_left, py_right]) dz = uvstack([dz_left, dz_right]) return px, py, dz def _get_px_py_dz(self, camera, pos, res, disparity): res0_h = np.floor(res[0]) / 2 east_vec = camera.unit_vectors[0] north_vec = camera.unit_vectors[1] normal_vec = camera.unit_vectors[2] angle_disparity = -np.arctan2(disparity.d, camera.width[2].d) R = get_rotation_matrix(angle_disparity, north_vec) east_vec_rot = np.dot(R, east_vec) normal_vec_rot = np.dot(R, normal_vec) camera_position_shift = camera.position + east_vec * disparity camera_position_shift = camera_position_shift.in_units("code_length").d width = camera.width.in_units("code_length").d sight_vector = pos - camera_position_shift pos1 = sight_vector for i in range(0, sight_vector.shape[0]): sight_vector_norm = np.sqrt(np.dot(sight_vector[i], sight_vector[i])) sight_vector[i] = sight_vector[i] / sight_vector_norm sight_center = camera_position_shift + camera.width[2] * normal_vec_rot for i in range(0, sight_vector.shape[0]): sight_angle_cos = np.dot(sight_vector[i], normal_vec_rot) # clip sight_angle_cos since floating point noise might # cause it go outside the domain of arccos sight_angle_cos = np.clip(sight_angle_cos, -1.0, 1.0) if np.arccos(sight_angle_cos) < 0.5 * np.pi: sight_length = width[2] / sight_angle_cos else: # If the corner is on the backwards, then we put it outside of # the image It can not be simply removed because it may connect # to other corner within the image, which produces visible # domain boundary line sight_length = np.sqrt(width[0] ** 2 + width[1] ** 2) sight_length = sight_length / np.sqrt(1 - sight_angle_cos**2) pos1[i] = camera_position_shift + sight_length * sight_vector[i] dx = np.dot(pos1 - sight_center, east_vec_rot) dy = np.dot(pos1 - sight_center, north_vec) dz = np.dot(pos - camera_position_shift, normal_vec_rot) # Transpose into image coords. if disparity > 0: px = (res0_h * 0.5 + res0_h / camera.width[0].d * dx).astype("int64") else: px = (res0_h * 1.5 + res0_h / camera.width[0].d * dx).astype("int64") py = (res[1] * 0.5 + res[1] / camera.width[1].d * dy).astype("int64") return px, py, dz def set_viewpoint(self, camera): """ For a PerspectiveLens, the viewpoint is the front center. """ self.viewpoint = self.front_center def __repr__(self): disp = f":\n\tlens_type:perspective\n\tviewpoint:{self.viewpoint}" return disp class FisheyeLens(Lens): r"""A lens for dome-based renderings This lens type accepts a field-of-view property, fov, that describes how wide an angle the fisheye can see. Fisheye images are typically used for dome-based presentations; the Hayden planetarium for instance has a field of view of 194.6. The images returned by this camera will be flat pixel images that can and should be reshaped to the resolution. """ def __init__(self): super().__init__() self.fov = 180.0 self.radius = 1.0 self.center = None self.rotation_matrix = np.eye(3) def setup_box_properties(self, camera): """Set up the view and stage based on the properties of the camera.""" self.radius = camera.width.max() super().setup_box_properties(camera) self.set_viewpoint(camera) def new_image(self, camera): """Initialize a new ImageArray to be used with this lens.""" self.current_image = ImageArray( np.zeros( (camera.resolution[0], camera.resolution[0], 4), dtype="float64", order="C", ), info={"imtype": "rendering"}, ) return self.current_image def _get_sampler_params(self, camera, render_source): vp = -arr_fisheye_vectors(camera.resolution[0], self.fov) vp.shape = (camera.resolution[0], camera.resolution[0], 3) vp = vp.dot(np.linalg.inv(self.rotation_matrix)) vp *= self.radius uv = np.ones(3, dtype="float64") positions = ( np.ones((camera.resolution[0], camera.resolution[0], 3), dtype="float64") * camera.position ) if render_source.zbuffer is not None: image = render_source.zbuffer.rgba else: image = self.new_image(camera) sampler_params = { "vp_pos": positions, "vp_dir": vp, "center": self.center, "bounds": (0.0, 1.0, 0.0, 1.0), "x_vec": uv, "y_vec": uv, "width": np.zeros(3, dtype="float64"), "image": image, "lens_type": "fisheye", } return sampler_params def set_viewpoint(self, camera): """For a FisheyeLens, the viewpoint is the camera's position""" self.viewpoint = camera.position def __repr__(self): disp = ( f":\n\tlens_type:fisheye\n\tviewpoint:{self.viewpoint}" f"\nt\tfov:{self.fov}\n\tradius:{self.radius}" ) return disp def project_to_plane(self, camera, pos, res=None): if res is None: res = camera.resolution # the return values here need to be px, py, dz # these are the coordinates and dz for the resultant image. # Basically, what we need is an inverse projection from the fisheye # vectors back onto the plane. arr_fisheye_vectors goes from px, py to # vector, and we need the reverse. # First, we transform lpos into *relative to the camera* coordinates. position = camera.position.in_units("code_length").d lpos = position - pos lpos = lpos.dot(self.rotation_matrix) mag = (lpos * lpos).sum(axis=1) ** 0.5 # screen out NaN values that would result from dividing by mag mag[mag == 0] = 1 lpos /= mag[:, None] dz = (mag / self.radius).in_units("1/code_length").d theta = np.arccos(lpos[:, 2]) fov_rad = self.fov * np.pi / 180.0 r = 2.0 * theta / fov_rad phi = np.arctan2(lpos[:, 1], lpos[:, 0]) px = r * np.cos(phi) py = r * np.sin(phi) # dz is distance the ray would travel px = (px + 1.0) * res[0] / 2.0 py = (py + 1.0) * res[1] / 2.0 # px and py should be dimensionless px = np.rint(px, dtype="int64") py = np.rint(py, dtype="int64") return px, py, dz class SphericalLens(Lens): r"""A lens for cylindrical-spherical projection. Movies rendered in this way can be displayed in head-tracking devices or in YouTube 360 view. """ def __init__(self): super().__init__() self.radius = 1.0 self.center = None self.rotation_matrix = np.eye(3) def setup_box_properties(self, camera): """Set up the view and stage based on the properties of the camera.""" self.radius = camera.width.max() super().setup_box_properties(camera) self.set_viewpoint(camera) def _get_sampler_params(self, camera, render_source): px = np.linspace(-np.pi, np.pi, camera.resolution[0], endpoint=True)[:, None] py = np.linspace( -np.pi / 2.0, np.pi / 2.0, camera.resolution[1], endpoint=True )[None, :] vectors = np.zeros( (camera.resolution[0], camera.resolution[1], 3), dtype="float64", order="C" ) vectors[:, :, 0] = np.cos(px) * np.cos(py) vectors[:, :, 1] = np.sin(px) * np.cos(py) vectors[:, :, 2] = np.sin(py) # The maximum possible length of ray max_length = unorm(camera.position - camera._domain_center) + 0.5 * unorm( camera._domain_width ) # Rescale the ray to be long enough to cover the entire domain vectors = vectors * max_length positions = np.tile( camera.position, camera.resolution[0] * camera.resolution[1] ).reshape(camera.resolution[0], camera.resolution[1], 3) R1 = get_rotation_matrix(0.5 * np.pi, [1, 0, 0]) R2 = get_rotation_matrix(0.5 * np.pi, [0, 0, 1]) uv = np.dot(R1, camera.unit_vectors) uv = np.dot(R2, uv) vectors.reshape((camera.resolution[0] * camera.resolution[1], 3)) vectors = np.dot(vectors, uv) vectors.reshape((camera.resolution[0], camera.resolution[1], 3)) if render_source.zbuffer is not None: image = render_source.zbuffer.rgba else: image = self.new_image(camera) dummy = np.ones(3, dtype="float64") image.shape = (camera.resolution[0], camera.resolution[1], 4) vectors.shape = (camera.resolution[0], camera.resolution[1], 3) positions.shape = (camera.resolution[0], camera.resolution[1], 3) sampler_params = { "vp_pos": positions, "vp_dir": vectors, "center": self.back_center, "bounds": (0.0, 1.0, 0.0, 1.0), "x_vec": dummy, "y_vec": dummy, "width": np.zeros(3, dtype="float64"), "image": image, "lens_type": "spherical", } return sampler_params def set_viewpoint(self, camera): """For a SphericalLens, the viewpoint is the camera's position""" self.viewpoint = camera.position def project_to_plane(self, camera, pos, res=None): if res is None: res = camera.resolution # Much of our setup here is the same as in the fisheye, except for the # actual conversion back to the px, py values. position = camera.position.in_units("code_length").d lpos = position - pos mag = (lpos * lpos).sum(axis=1) ** 0.5 # screen out NaN values that would result from dividing by mag mag[mag == 0] = 1 lpos /= mag[:, None] # originally: # the x vector is cos(px) * cos(py) # the y vector is sin(px) * cos(py) # the z vector is sin(py) # y / x = tan(px), so arctan2(lpos[:,1], lpos[:,0]) => px # z = sin(py) so arcsin(z) = py # px runs from -pi to pi # py runs from -pi/2 to pi/2 px = np.arctan2(lpos[:, 1], lpos[:, 0]) py = np.arcsin(lpos[:, 2]) dz = mag / self.radius # dz is distance the ray would travel px = ((-px + np.pi) / (2.0 * np.pi)) * res[0] py = ((-py + np.pi / 2.0) / np.pi) * res[1] # px and py should be dimensionless px = np.rint(px).astype("int64") py = np.rint(py).astype("int64") return px, py, dz class StereoSphericalLens(Lens): r"""A lens for a stereo cylindrical-spherical projection. Movies rendered in this way can be displayed in VR devices or stereo youtube 360 degree movies. """ def __init__(self): super().__init__() self.radius = 1.0 self.center = None self.disparity = None self.rotation_matrix = np.eye(3) def setup_box_properties(self, camera): self.radius = camera.width.max() super().setup_box_properties(camera) self.set_viewpoint(camera) def _get_sampler_params(self, camera, render_source): if self.disparity is None: self.disparity = camera.width[0] / 1000.0 single_resolution_y = int(np.floor(camera.resolution[1]) / 2) px = np.linspace(-np.pi, np.pi, camera.resolution[0], endpoint=True)[:, None] py = np.linspace(-np.pi / 2.0, np.pi / 2.0, single_resolution_y, endpoint=True)[ None, : ] vectors = np.zeros( (camera.resolution[0], single_resolution_y, 3), dtype="float64", order="C" ) vectors[:, :, 0] = np.cos(px) * np.cos(py) vectors[:, :, 1] = np.sin(px) * np.cos(py) vectors[:, :, 2] = np.sin(py) # The maximum possible length of ray max_length = ( unorm(camera.position - camera._domain_center) + 0.5 * unorm(camera._domain_width) + np.abs(self.disparity) ) # Rescale the ray to be long enough to cover the entire domain vectors = vectors * max_length R1 = get_rotation_matrix(0.5 * np.pi, [1, 0, 0]) R2 = get_rotation_matrix(0.5 * np.pi, [0, 0, 1]) uv = np.dot(R1, camera.unit_vectors) uv = np.dot(R2, uv) vectors.reshape((camera.resolution[0] * single_resolution_y, 3)) vectors = np.dot(vectors, uv) vectors.reshape((camera.resolution[0], single_resolution_y, 3)) vectors2 = np.zeros( (camera.resolution[0], single_resolution_y, 3), dtype="float64", order="C" ) vectors2[:, :, 0] = -np.sin(px) * np.ones((1, single_resolution_y)) vectors2[:, :, 1] = np.cos(px) * np.ones((1, single_resolution_y)) vectors2[:, :, 2] = 0 vectors2.reshape((camera.resolution[0] * single_resolution_y, 3)) vectors2 = np.dot(vectors2, uv) vectors2.reshape((camera.resolution[0], single_resolution_y, 3)) positions = np.tile(camera.position, camera.resolution[0] * single_resolution_y) positions = positions.reshape(camera.resolution[0], single_resolution_y, 3) # The left and right are switched here since VR is in LHS. positions_left = positions + vectors2 * self.disparity positions_right = positions + vectors2 * (-self.disparity) if render_source.zbuffer is not None: image = render_source.zbuffer.rgba else: image = self.new_image(camera) dummy = np.ones(3, dtype="float64") vectors_comb = uhstack([vectors, vectors]) positions_comb = uhstack([positions_left, positions_right]) image.shape = (camera.resolution[0], camera.resolution[1], 4) vectors_comb.shape = (camera.resolution[0], camera.resolution[1], 3) positions_comb.shape = (camera.resolution[0], camera.resolution[1], 3) sampler_params = { "vp_pos": positions_comb, "vp_dir": vectors_comb, "center": self.back_center, "bounds": (0.0, 1.0, 0.0, 1.0), "x_vec": dummy, "y_vec": dummy, "width": np.zeros(3, dtype="float64"), "image": image, "lens_type": "stereo-spherical", } return sampler_params def set_viewpoint(self, camera): """ For a PerspectiveLens, the viewpoint is the front center. """ self.viewpoint = camera.position lenses = { "plane-parallel": PlaneParallelLens, "perspective": PerspectiveLens, "stereo-perspective": StereoPerspectiveLens, "fisheye": FisheyeLens, "spherical": SphericalLens, "stereo-spherical": StereoSphericalLens, } yt-project-yt-f043ac8/yt/visualization/volume_rendering/off_axis_projection.py000066400000000000000000000505621510711153200301670ustar00rootroot00000000000000import numpy as np from yt.data_objects.api import ImageArray from yt.funcs import is_sequence, mylog from yt.geometry.oct_geometry_handler import OctreeIndex from yt.units.unit_object import Unit # type: ignore from yt.utilities.lib.image_utilities import add_cells_to_image_offaxis from yt.utilities.lib.partitioned_grid import PartitionedGrid from yt.utilities.lib.pixelization_routines import ( normalization_2d_utility, off_axis_projection_SPH, ) from yt.visualization.volume_rendering.lens import PlaneParallelLens from .render_source import KDTreeVolumeSource from .scene import Scene from .transfer_functions import ProjectionTransferFunction from .utils import data_source_or_all def off_axis_projection( data_source, center, normal_vector, width, resolution, item, weight=None, volume=None, no_ghost=False, interpolated=False, north_vector=None, depth=None, num_threads=1, method="integrate", ): r"""Project through a dataset, off-axis, and return the image plane. This function will accept the necessary items to integrate through a volume at an arbitrary angle and return the integrated field of view to the user. Note that if a weight is supplied, it will multiply the pre-interpolated values together, then create cell-centered values, then interpolate within the cell to conduct the integration. Parameters ---------- data_source : ~yt.data_objects.static_output.Dataset or ~yt.data_objects.data_containers.YTSelectionDataContainer This is the dataset or data object to volume render. center : array_like The current 'center' of the view port -- the focal point for the camera. normal_vector : array_like The vector between the camera position and the center. width : float or list of floats The current width of the image. If a single float, the volume is cubical, but if not, it is left/right, top/bottom, front/back resolution : int or list of ints The number of pixels in each direction. item: string The field to project through the volume weight : optional, default None If supplied, the field will be pre-multiplied by this, then divided by the integrated value of this field. This returns an average rather than a sum. volume : `yt.extensions.volume_rendering.AMRKDTree`, optional The volume to ray cast through. Can be specified for finer-grained control, but otherwise will be automatically generated. no_ghost: bool, optional Optimization option. If True, homogenized bricks will extrapolate out from grid instead of interpolating from ghost zones that have to first be calculated. This can lead to large speed improvements, but at a loss of accuracy/smoothness in resulting image. The effects are less notable when the transfer function is smooth and broad. Default: True interpolated : optional, default False If True, the data is first interpolated to vertex-centered data, then tri-linearly interpolated along the ray. Not suggested for quantitative studies. north_vector : optional, array_like, default None A vector that, if specified, restricts the orientation such that the north vector dotted into the image plane points "up". Useful for rotations depth: float, tuple[float, str], or unyt_array of size 1. specify the depth of the projection region (size along the line of sight). If no units are given (unyt_array or second tuple element), code units are assumed. num_threads: integer, optional, default 1 Use this many OpenMP threads during projection. method : string The method of projection. Valid methods are: "integrate" with no weight_field specified : integrate the requested field along the line of sight. "integrate" with a weight_field specified : weight the requested field by the weighting field and integrate along the line of sight. "sum" : This method is the same as integrate, except that it does not multiply by a path length when performing the integration, and is just a straight summation of the field along the given axis. WARNING: This should only be used for uniform resolution grid datasets, as other datasets may result in unphysical images. or camera movements. Returns ------- image : array An (N,N) array of the final integrated values, in float64 form. Examples -------- >>> image = off_axis_projection( ... ds, ... [0.5, 0.5, 0.5], ... [0.2, 0.3, 0.4], ... 0.2, ... N, ... ("gas", "temperature"), ... ("gas", "density"), ... ) >>> write_image(np.log10(image), "offaxis.png") """ if method not in ("integrate", "sum"): raise NotImplementedError( "Only 'integrate' or 'sum' methods are valid for off-axis-projections" ) if interpolated: raise NotImplementedError( "Only interpolated=False methods are currently implemented " "for off-axis-projections" ) data_source = data_source_or_all(data_source) item = data_source._determine_fields([item])[0] # Assure vectors are numpy arrays as expected by cython code normal_vector = np.array(normal_vector, dtype="float64") if north_vector is not None: north_vector = np.array(north_vector, dtype="float64") # Add the normal as a field parameter to the data source # so line of sight fields can use it data_source.set_field_parameter("axis", normal_vector) # Sanitize units if not hasattr(center, "units"): center = data_source.ds.arr(center, "code_length") if not hasattr(width, "units"): width = data_source.ds.arr(width, "code_length") if depth is not None: # handle units (intrinsic or as a tuple), # then convert to code length # float -> assumed to be in code units if isinstance(depth, tuple): depth = data_source.ds.arr(np.array([depth[0]]), depth[1]) if hasattr(depth, "units"): depth = depth.to("code_length").d # depth = data_source.ds.arr(depth, "code_length") if hasattr(data_source.ds, "_sph_ptypes"): if method != "integrate": raise NotImplementedError("SPH Only allows 'integrate' method") sph_ptypes = data_source.ds._sph_ptypes fi = data_source.ds.field_info[item] raise_error = False ptype = sph_ptypes[0] ppos = [f"particle_position_{ax}" for ax in "xyz"] # Assure that the field we're trying to off-axis project # has a field type as the SPH particle type or if the field is an # alias to an SPH field or is a 'gas' field if item[0] in data_source.ds.known_filters: if item[0] not in sph_ptypes: raise_error = True else: ptype = item[0] ppos = ["x", "y", "z"] elif fi.is_alias: if fi.alias_name[0] not in sph_ptypes: raise_error = True elif item[0] != "gas": ptype = item[0] else: if fi.name[0] not in sph_ptypes and fi.name[0] != "gas": raise_error = True if raise_error: raise RuntimeError( "Can only perform off-axis projections for SPH fields, " f"Received {item!r}" ) normal = np.array(normal_vector) normal = normal / np.linalg.norm(normal) # If north_vector is None, we set the default here. # This is chosen so that if normal_vector is one of the # cartesian coordinate axes, the projection will match # the corresponding on-axis projection. if north_vector is None: vecs = np.identity(3) t = np.cross(vecs, normal).sum(axis=1) ax = t.argmax() east_vector = np.cross(vecs[ax, :], normal).ravel() north = np.cross(normal, east_vector).ravel() else: north = np.array(north_vector) north = north / np.linalg.norm(north) east_vector = np.cross(north, normal).ravel() # if weight is None: buf = np.zeros((resolution[0], resolution[1]), dtype="float64") mask = np.ones_like(buf, dtype="uint8") ## width from fixed_resolution.py is just the size of the domain # x_min = center[0] - width[0] / 2 # x_max = center[0] + width[0] / 2 # y_min = center[1] - width[1] / 2 # y_max = center[1] + width[1] / 2 # z_min = center[2] - width[2] / 2 # z_max = center[2] + width[2] / 2 periodic = data_source.ds.periodicity le = data_source.ds.domain_left_edge.to("code_length").d re = data_source.ds.domain_right_edge.to("code_length").d x_min, y_min, z_min = le x_max, y_max, z_max = re bounds = [x_min, x_max, y_min, y_max, z_min, z_max] # only need (rotated) x/y widths _width = (width.to("code_length").d)[:2] finfo = data_source.ds.field_info[item] ounits = finfo.output_units kernel_name = None if hasattr(data_source.ds, "kernel_name"): kernel_name = data_source.ds.kernel_name if kernel_name is None: kernel_name = "cubic" if weight is None: for chunk in data_source.chunks([], "io"): off_axis_projection_SPH( chunk[ptype, ppos[0]].to("code_length").d, chunk[ptype, ppos[1]].to("code_length").d, chunk[ptype, ppos[2]].to("code_length").d, chunk[ptype, "mass"].to("code_mass").d, chunk[ptype, "density"].to("code_density").d, chunk[ptype, "smoothing_length"].to("code_length").d, bounds, center.to("code_length").d, _width, periodic, chunk[item].in_units(ounits), buf, mask, normal_vector, north, depth=depth, kernel_name=kernel_name, ) # Assure that the path length unit is in the default length units # for the dataset by scaling the units of the smoothing length, # which in the above calculation is set to be code_length path_length_unit = Unit( "code_length", registry=data_source.ds.unit_registry ) default_path_length_unit = data_source.ds.unit_system["length"] buf *= data_source.ds.quan(1, path_length_unit).in_units( default_path_length_unit ) item_unit = data_source.ds._get_field_info(item).units item_unit = Unit(item_unit, registry=data_source.ds.unit_registry) funits = item_unit * default_path_length_unit else: # if there is a weight field, take two projections: # one of field*weight, the other of just weight, and divide them weight_buff = np.zeros((resolution[0], resolution[1]), dtype="float64") wounits = data_source.ds.field_info[weight].output_units for chunk in data_source.chunks([], "io"): off_axis_projection_SPH( chunk[ptype, ppos[0]].to("code_length").d, chunk[ptype, ppos[1]].to("code_length").d, chunk[ptype, ppos[2]].to("code_length").d, chunk[ptype, "mass"].to("code_mass").d, chunk[ptype, "density"].to("code_density").d, chunk[ptype, "smoothing_length"].to("code_length").d, bounds, center.to("code_length").d, _width, periodic, chunk[item].in_units(ounits), buf, mask, normal_vector, north, weight_field=chunk[weight].in_units(wounits), depth=depth, kernel_name=kernel_name, ) for chunk in data_source.chunks([], "io"): off_axis_projection_SPH( chunk[ptype, ppos[0]].to("code_length").d, chunk[ptype, ppos[1]].to("code_length").d, chunk[ptype, ppos[2]].to("code_length").d, chunk[ptype, "mass"].to("code_mass").d, chunk[ptype, "density"].to("code_density").d, chunk[ptype, "smoothing_length"].to("code_length").d, bounds, center.to("code_length").d, _width, periodic, chunk[weight].to(wounits), weight_buff, mask, normal_vector, north, depth=depth, kernel_name=kernel_name, ) normalization_2d_utility(buf, weight_buff) item_unit = data_source.ds._get_field_info(item).units item_unit = Unit(item_unit, registry=data_source.ds.unit_registry) funits = item_unit myinfo = { "field": item, "east_vector": east_vector, "north_vector": north_vector, "normal_vector": normal_vector, "width": width, "depth": depth, "units": funits, "type": "SPH smoothed projection", } return ImageArray( buf, funits, registry=data_source.ds.unit_registry, info=myinfo ) sc = Scene() data_source.ds.index if item is None: field = data_source.ds.field_list[0] mylog.info("Setting default field to %s", field.__repr__()) funits = data_source.ds._get_field_info(item).units vol = KDTreeVolumeSource(data_source, item) vol.num_threads = num_threads if weight is None: vol.set_field(item) else: # This is a temporary field, which we will remove at the end. weightfield = ("index", "temp_weightfield") def _make_wf(f, w): def temp_weightfield(field, data): tr = data[f].astype("float64") * data[w] return tr.d return temp_weightfield data_source.ds.add_field( weightfield, sampling_type="cell", function=_make_wf(item, weight), units="", ) vol.set_field(weightfield) vol.set_weight_field(weight) ptf = ProjectionTransferFunction() vol.set_transfer_function(ptf) camera = sc.add_camera(data_source) camera.set_width(width) if not is_sequence(resolution): resolution = [resolution] * 2 camera.resolution = resolution if not is_sequence(width): width = data_source.ds.arr([width] * 3) normal = np.array(normal_vector) normal = normal / np.linalg.norm(normal) camera.position = center - width[2] * normal camera.focus = center # If north_vector is None, we set the default here. # This is chosen so that if normal_vector is one of the # cartesian coordinate axes, the projection will match # the corresponding on-axis projection. if north_vector is None: vecs = np.identity(3) t = np.cross(vecs, normal).sum(axis=1) ax = t.argmax() east_vector = np.cross(vecs[ax, :], normal).ravel() north = np.cross(normal, east_vector).ravel() else: north = np.array(north_vector) north = north / np.linalg.norm(north) camera.switch_orientation(normal, north) sc.add_source(vol) vol.set_sampler(camera, interpolated=False) assert vol.sampler is not None fields = [vol.field] if vol.weight_field is not None: fields.append(vol.weight_field) mylog.debug("Casting rays") index = data_source.ds.index lens = camera.lens # This implementation is optimized for octrees with plane-parallel lenses # and implicitely assumes that the cells are cubic. # NOTE: we should be able to relax the cubic assumption to a rectangular # assumption (if all cells have the same aspect ratio) with some # renormalization of the coordinates and the projection axes. # This is NOT done in the following. dom_width = data_source.ds.domain_width cubic_domain = dom_width.max() == dom_width.min() if ( isinstance(index, OctreeIndex) and isinstance(lens, PlaneParallelLens) and cubic_domain ): fields.extend(("index", k) for k in "xyz") fields.append(("index", "dx")) data_source.get_data(fields) # We need the width of the plot window in projected coordinates, # i.e. we ignore the z-component wmax = width[:2].max().to("code_length") xyz = data_source.ds.arr( np.zeros((len(data_source[vol.field]), 3)), "code_length" ) for idim, periodic in enumerate(data_source.ds.periodicity): axis = data_source.ds.coordinates.axis_order[idim] # Recenter positions w.r.t. center of the plot window xyz[..., idim] = (data_source["index", axis] - center[idim]).to( "code_length" ) if not periodic: continue # If we have periodic boundaries, we need to wrap the corresponding # coordinates into [-w/2, +w/2] w = data_source.ds.domain_width[idim].to("code_length") xyz[..., idim] = (xyz[..., idim] + w / 2) % w - w / 2 # Rescale to [-0.5, +0.5] xyz = (xyz / wmax).to("1").d dx = (data_source["index", "dx"] / wmax).to("1").d if vol.weight_field is None: weight_field = np.ones_like(dx) else: weight_field = data_source[vol.weight_field] projected_weighted_qty = np.zeros(resolution) projected_weight = np.zeros(resolution) add_cells_to_image_offaxis( Xp=xyz, dXp=dx, qty=data_source[vol.field], weight=weight_field, rotation=camera.inv_mat.T, buffer=projected_weighted_qty, buffer_weight=projected_weight, Nx=resolution[0], Ny=resolution[1], ) # Note: since dx was divided by wmax, we need to rescale by it projected_weighted_qty *= wmax.d / np.sqrt(3) projected_weight *= wmax.d / np.sqrt(3) image = ImageArray( data_source.ds.arr( np.stack([projected_weighted_qty, projected_weight], axis=-1), "dimensionless", ), funits, registry=data_source.ds.unit_registry, info={"imtype": "rendering"}, ) # Clear temporary field data associated to weightfield if weight is not None: data_source.clear_data(weightfield) else: for grid, mask in data_source.blocks: data = [] for f in fields: # strip units before multiplying by mask for speed grid_data = grid[f] units = grid_data.units data.append( data_source.ds.arr(grid_data.d * mask, units, dtype="float64") ) pg = PartitionedGrid( grid.id, data, mask.astype("uint8"), grid.LeftEdge, grid.RightEdge, grid.ActiveDimensions.astype("int64"), ) grid.clear_data() vol.sampler(pg, num_threads=num_threads) image = vol.finalize_image(camera, vol.sampler.aimage) image = ImageArray( image, funits, registry=data_source.ds.unit_registry, info=image.info ) # Remove the temporary weight field if weight is not None: data_source.ds.field_info.pop(weightfield) data_source.ds.field_dependencies.pop(weightfield) if method == "integrate": if weight is None: dl = width[2].in_units(data_source.ds.unit_system["length"]) image *= dl else: mask = image[:, :, 1] == 0 nmask = np.logical_not(mask) image[:, :, 0][nmask] /= image[:, :, 1][nmask] image[mask] = 0 return image[:, :, 0] yt-project-yt-f043ac8/yt/visualization/volume_rendering/old_camera.py000066400000000000000000002574221510711153200262270ustar00rootroot00000000000000from copy import deepcopy import numpy as np from yt._maintenance.ipython_compat import IS_IPYTHON from yt.config import ytcfg from yt.data_objects.api import ImageArray from yt.funcs import ensure_numpy_array, get_num_threads, get_pbar, is_sequence, mylog from yt.units.yt_array import YTArray from yt.utilities.amr_kdtree.api import AMRKDTree from yt.utilities.exceptions import YTNotInsideNotebook from yt.utilities.lib.grid_traversal import ( arr_fisheye_vectors, arr_pix2vec_nest, pixelize_healpix, ) from yt.utilities.lib.image_samplers import ( InterpolatedProjectionSampler, LightSourceRenderSampler, ProjectionSampler, VolumeRenderSampler, ) from yt.utilities.lib.misc_utilities import lines from yt.utilities.lib.partitioned_grid import PartitionedGrid from yt.utilities.math_utils import get_rotation_matrix from yt.utilities.object_registries import data_object_registry from yt.utilities.orientation import Orientation from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, parallel_objects, ) from yt.visualization.image_writer import apply_colormap, write_bitmap, write_image from yt.visualization.volume_rendering.blenders import enhance_rgba from .transfer_functions import ProjectionTransferFunction def get_corners(le, re): return np.array( [ [le[0], le[1], le[2]], [re[0], le[1], le[2]], [re[0], re[1], le[2]], [le[0], re[1], le[2]], [le[0], le[1], re[2]], [re[0], le[1], re[2]], [re[0], re[1], re[2]], [le[0], re[1], re[2]], ], dtype="float64", ) class Camera(ParallelAnalysisInterface): r"""A viewpoint into a volume, for volume rendering. The camera represents the eye of an observer, which will be used to generate ray-cast volume renderings of the domain. Parameters ---------- center : array_like The current "center" of the view port -- the focal point for the camera. normal_vector : array_like The vector between the camera position and the center. width : float or list of floats The current width of the image. If a single float, the volume is cubical, but if not, it is left/right, top/bottom, front/back. resolution : int or list of ints The number of pixels in each direction. transfer_function : `yt.visualization.volume_rendering.TransferFunction` The transfer function used to map values to colors in an image. If not specified, defaults to a ProjectionTransferFunction. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. steady_north : bool, optional Boolean to control whether to normalize the north_vector by subtracting off the dot product of it and the normal vector. Makes it easier to do rotations along a single axis. If north_vector is specified, is switched to True. Default: False volume : `yt.extensions.volume_rendering.AMRKDTree`, optional The volume to ray cast through. Can be specified for finer-grained control, but otherwise will be automatically generated. fields : list of fields, optional This is the list of fields we want to volume render; defaults to Density. log_fields : list of bool, optional Whether we should take the log of the fields before supplying them to the volume rendering mechanism. sub_samples : int, optional The number of samples to take inside every cell per ray. ds : ~yt.data_objects.static_output.Dataset For now, this is a require parameter! But in the future it will become optional. This is the dataset to volume render. max_level: int, optional Specifies the maximum level to be rendered. Also specifies the maximum level used in the kd-Tree construction. Defaults to None (all levels), and only applies if use_kd=True. no_ghost: bool, optional Optimization option. If True, homogenized bricks will extrapolate out from grid instead of interpolating from ghost zones that have to first be calculated. This can lead to large speed improvements, but at a loss of accuracy/smoothness in resulting image. The effects are less notable when the transfer function is smooth and broad. Default: True data_source: data container, optional Optionally specify an arbitrary data source to the volume rendering. All cells not included in the data source will be ignored during ray casting. By default this will get set to ds.all_data(). Examples -------- >>> import yt.visualization.volume_rendering.api as vr >>> ds = load("DD1701") # Load a dataset >>> c = [0.5] * 3 # Center >>> L = [1.0, 1.0, 1.0] # Viewpoint >>> W = np.sqrt(3) # Width >>> N = 1024 # Pixels (1024^2) # Get density min, max >>> mi, ma = ds.all_data().quantities["Extrema"]("Density")[0] >>> mi, ma = np.log10(mi), np.log10(ma) # Construct transfer function >>> tf = vr.ColorTransferFunction((mi - 2, ma + 2)) # Sample transfer function with 5 gaussians. Use new col_bounds keyword. >>> tf.add_layers(5, w=0.05, col_bounds=(mi + 1, ma), colormap="nipy_spectral") # Create the camera object >>> cam = vr.Camera(c, L, W, (N, N), transfer_function=tf, ds=ds) # Ray cast, and save the image. >>> image = cam.snapshot(fn="my_rendering.png") """ _sampler_object = VolumeRenderSampler _tf_figure = None _render_figure = None def __init__( self, center, normal_vector, width, resolution, transfer_function=None, north_vector=None, steady_north=False, volume=None, fields=None, log_fields=None, sub_samples=5, ds=None, min_level=None, max_level=None, no_ghost=True, data_source=None, use_light=False, ): ParallelAnalysisInterface.__init__(self) if ds is not None: self.ds = ds if not is_sequence(resolution): resolution = (resolution, resolution) self.resolution = resolution self.sub_samples = sub_samples self.rotation_vector = north_vector if is_sequence(width) and len(width) > 1 and isinstance(width[1], str): width = self.ds.quan(width[0], units=width[1]) # Now convert back to code length for subsequent manipulation width = width.in_units("code_length").value if not is_sequence(width): width = (width, width, width) # left/right, top/bottom, front/back if not isinstance(width, YTArray): width = self.ds.arr(width, units="code_length") if not isinstance(center, YTArray): center = self.ds.arr(center, units="code_length") # Ensure that width and center are in the same units # Cf. https://bitbucket.org/yt_analysis/yt/issue/1080 width = width.in_units("code_length") center = center.in_units("code_length") self.orienter = Orientation( normal_vector, north_vector=north_vector, steady_north=steady_north ) if not steady_north: self.rotation_vector = self.orienter.unit_vectors[1] self._setup_box_properties(width, center, self.orienter.unit_vectors) if fields is None: fields = [("gas", "density")] self.fields = fields if transfer_function is None: transfer_function = ProjectionTransferFunction() self.transfer_function = transfer_function self.log_fields = log_fields dd = self.ds.all_data() efields = dd._determine_fields(self.fields) if self.log_fields is None: self.log_fields = [self.ds._get_field_info(f).take_log for f in efields] self.no_ghost = no_ghost self.use_light = use_light self.light_dir = None self.light_rgba = None if self.no_ghost: mylog.warning( "no_ghost is currently True (default). " "This may lead to artifacts at grid boundaries." ) if data_source is None: data_source = self.ds.all_data() self.data_source = data_source if volume is None: volume = AMRKDTree( self.ds, min_level=min_level, max_level=max_level, data_source=self.data_source, ) self.volume = volume def _setup_box_properties(self, width, center, unit_vectors): self.width = width self.center = center self.box_vectors = YTArray( [ unit_vectors[0] * width[0], unit_vectors[1] * width[1], unit_vectors[2] * width[2], ] ) self.origin = center - 0.5 * width.dot(YTArray(unit_vectors, "")) self.back_center = center - 0.5 * width[2] * unit_vectors[2] self.front_center = center + 0.5 * width[2] * unit_vectors[2] def update_view_from_matrix(self, mat): pass def project_to_plane(self, pos, res=None): if res is None: res = self.resolution dx = np.dot(pos - self.origin, self.orienter.unit_vectors[1]) dy = np.dot(pos - self.origin, self.orienter.unit_vectors[0]) dz = np.dot(pos - self.center, self.orienter.unit_vectors[2]) # Transpose into image coords. py = (res[0] * (dx / self.width[0])).astype("int64") px = (res[1] * (dy / self.width[1])).astype("int64") return px, py, dz def draw_grids(self, im, alpha=0.3, cmap=None, min_level=None, max_level=None): r"""Draws Grids on an existing volume rendering. By mapping grid level to a color, draws edges of grids on a volume rendering using the camera orientation. Parameters ---------- im: Numpy ndarray Existing image that has the same resolution as the Camera, which will be painted by grid lines. alpha : float, optional The alpha value for the grids being drawn. Used to control how bright the grid lines are with respect to the image. Default : 0.3 cmap : string, optional Colormap to be used mapping grid levels to colors. min_level : int, optional Optional parameter to specify the min level grid boxes to overplot on the image. max_level : int, optional Optional parameters to specify the max level grid boxes to overplot on the image. Returns ------- None Examples -------- >>> im = cam.snapshot() >>> cam.add_grids(im) >>> write_bitmap(im, "render_with_grids.png") """ if cmap is None: cmap = ytcfg.get("yt", "default_colormap") region = self.data_source corners = [] levels = [] for block, _mask in region.blocks: block_corners = np.array( [ [block.LeftEdge[0], block.LeftEdge[1], block.LeftEdge[2]], [block.RightEdge[0], block.LeftEdge[1], block.LeftEdge[2]], [block.RightEdge[0], block.RightEdge[1], block.LeftEdge[2]], [block.LeftEdge[0], block.RightEdge[1], block.LeftEdge[2]], [block.LeftEdge[0], block.LeftEdge[1], block.RightEdge[2]], [block.RightEdge[0], block.LeftEdge[1], block.RightEdge[2]], [block.RightEdge[0], block.RightEdge[1], block.RightEdge[2]], [block.LeftEdge[0], block.RightEdge[1], block.RightEdge[2]], ], dtype="float64", ) corners.append(block_corners) levels.append(block.Level) corners = np.dstack(corners) levels = np.array(levels) if max_level is not None: subset = levels <= max_level levels = levels[subset] corners = corners[:, :, subset] if min_level is not None: subset = levels >= min_level levels = levels[subset] corners = corners[:, :, subset] colors = ( apply_colormap( levels * 1.0, color_bounds=[0, self.ds.index.max_level], cmap_name=cmap )[0, :, :] * 1.0 / 255.0 ) colors[:, 3] = alpha order = [0, 1, 1, 2, 2, 3, 3, 0] order += [4, 5, 5, 6, 6, 7, 7, 4] order += [0, 4, 1, 5, 2, 6, 3, 7] vertices = np.empty([corners.shape[2] * 2 * 12, 3]) vertices = self.ds.arr(vertices, "code_length") for i in range(3): vertices[:, i] = corners[order, i, ...].ravel(order="F") px, py, dz = self.project_to_plane(vertices, res=im.shape[:2]) # Must normalize the image nim = im.rescale(inline=False) enhance_rgba(nim) nim.add_background_color("black", inline=True) # we flipped it in snapshot to get the orientation correct, so # flip the lines lines(nim.d, px.d, py.d, colors, 24, flip=1) return nim def draw_coordinate_vectors(self, im, length=0.05, thickness=1): r"""Draws three coordinate vectors in the corner of a rendering. Modifies an existing image to have three lines corresponding to the coordinate directions colored by {x,y,z} = {r,g,b}. Currently only functional for plane-parallel volume rendering. Parameters ---------- im: Numpy ndarray Existing image that has the same resolution as the Camera, which will be painted by grid lines. length: float, optional The length of the lines, as a fraction of the image size. Default : 0.05 thickness : int, optional Thickness in pixels of the line to be drawn. Returns ------- None Modifies -------- im: The original image. Examples -------- >>> im = cam.snapshot() >>> cam.draw_coordinate_vectors(im) >>> im.write_png("render_with_grids.png") """ length_pixels = length * self.resolution[0] # Put the starting point in the lower left px0 = int(length * self.resolution[0]) # CS coordinates! py0 = int((1.0 - length) * self.resolution[1]) alpha = im[:, :, 3].max() if alpha == 0.0: alpha = 1.0 coord_vectors = [ np.array([length_pixels, 0.0, 0.0]), np.array([0.0, length_pixels, 0.0]), np.array([0.0, 0.0, length_pixels]), ] colors = [ np.array([1.0, 0.0, 0.0, alpha]), np.array([0.0, 1.0, 0.0, alpha]), np.array([0.0, 0.0, 1.0, alpha]), ] # we flipped it in snapshot to get the orientation correct, so # flip the lines for vec, color in zip(coord_vectors, colors, strict=True): dx = int(np.dot(vec, self.orienter.unit_vectors[0])) dy = int(np.dot(vec, self.orienter.unit_vectors[1])) px = np.array([px0, px0 + dx], dtype="int64") py = np.array([py0, py0 + dy], dtype="int64") lines(im.d, px, py, np.array([color, color]), 1, thickness, flip=1) def draw_line(self, im, x0, x1, color=None): r"""Draws a line on an existing volume rendering. Given starting and ending positions x0 and x1, draws a line on a volume rendering using the camera orientation. Parameters ---------- im : ImageArray or 2D ndarray Existing image that has the same resolution as the Camera, which will be painted by grid lines. x0 : YTArray or ndarray Starting coordinate. If passed in as an ndarray, assumed to be in code units. x1 : YTArray or ndarray Ending coordinate, in simulation coordinates. If passed in as an ndarray, assumed to be in code units. color : array like, optional Color of the line (r, g, b, a). Defaults to white. Returns ------- None Examples -------- >>> im = cam.snapshot() >>> cam.draw_line(im, np.array([0.1, 0.2, 0.3]), np.array([0.5, 0.6, 0.7])) >>> write_bitmap(im, "render_with_line.png") """ if color is None: color = np.array([1.0, 1.0, 1.0, 1.0]) if not hasattr(x0, "units"): x0 = self.ds.arr(x0, "code_length") if not hasattr(x1, "units"): x1 = self.ds.arr(x1, "code_length") dx0 = ((x0 - self.origin) * self.orienter.unit_vectors[1]).sum() dx1 = ((x1 - self.origin) * self.orienter.unit_vectors[1]).sum() dy0 = ((x0 - self.origin) * self.orienter.unit_vectors[0]).sum() dy1 = ((x1 - self.origin) * self.orienter.unit_vectors[0]).sum() py0 = int(self.resolution[0] * (dx0 / self.width[0])) py1 = int(self.resolution[0] * (dx1 / self.width[0])) px0 = int(self.resolution[1] * (dy0 / self.width[1])) px1 = int(self.resolution[1] * (dy1 / self.width[1])) px = np.array([px0, px1], dtype="int64") py = np.array([py0, py1], dtype="int64") # we flipped it in snapshot to get the orientation correct, so # flip the lines lines(im.d, px, py, np.array([color, color]), flip=1) def draw_domain(self, im, alpha=0.3): r"""Draws domain edges on an existing volume rendering. Draws a white wireframe on the domain edges. Parameters ---------- im: Numpy ndarray Existing image that has the same resolution as the Camera, which will be painted by grid lines. alpha : float, optional The alpha value for the wireframe being drawn. Used to control how bright the lines are with respect to the image. Default : 0.3 Returns ------- nim: Numpy ndarray A new image with the domain lines drawn Examples -------- >>> im = cam.snapshot() >>> nim = cam.draw_domain(im) >>> write_bitmap(nim, "render_with_domain_boundary.png") """ # Must normalize the image nim = im.rescale(inline=False) enhance_rgba(nim) nim.add_background_color("black", inline=True) self.draw_box( nim, self.ds.domain_left_edge, self.ds.domain_right_edge, color=np.array([1.0, 1.0, 1.0, alpha]), ) return nim def draw_box(self, im, le, re, color=None): r"""Draws a box on an existing volume rendering. Draws a box defined by a left and right edge by modifying an existing volume rendering Parameters ---------- im: Numpy ndarray Existing image that has the same resolution as the Camera, which will be painted by grid lines. le: Numpy ndarray Left corner of the box re : Numpy ndarray Right corner of the box color : array like, optional Color of the box (r, g, b, a). Defaults to white. Returns ------- None Examples -------- >>> im = cam.snapshot() >>> cam.draw_box(im, np.array([0.1, 0.2, 0.3]), np.array([0.5, 0.6, 0.7])) >>> write_bitmap(im, "render_with_box.png") """ if color is None: color = np.array([1.0, 1.0, 1.0, 1.0]) corners = get_corners(le, re) order = [0, 1, 1, 2, 2, 3, 3, 0] order += [4, 5, 5, 6, 6, 7, 7, 4] order += [0, 4, 1, 5, 2, 6, 3, 7] vertices = np.empty([24, 3]) vertices = self.ds.arr(vertices, "code_length") for i in range(3): vertices[:, i] = corners[order, i, ...].ravel(order="F") px, py, dz = self.project_to_plane(vertices, res=im.shape[:2]) # we flipped it in snapshot to get the orientation correct, so # flip the lines lines( im.d, px.d.astype("int64"), py.d.astype("int64"), color.reshape(1, 4), 24, flip=1, ) def look_at(self, new_center, north_vector=None): r"""Change the view direction based on a new focal point. This will recalculate all the necessary vectors and vector planes to orient the image plane so that it points at a new location. Parameters ---------- new_center : array_like The new "center" of the view port -- the focal point for the camera. north_vector : array_like, optional The "up" direction for the plane of rays. If not specific, calculated automatically. """ normal_vector = self.front_center - new_center self.orienter.switch_orientation( normal_vector=normal_vector, north_vector=north_vector ) def switch_orientation(self, normal_vector=None, north_vector=None): r""" Change the view direction based on any of the orientation parameters. This will recalculate all the necessary vectors and vector planes related to an orientable object. Parameters ---------- normal_vector: array_like, optional The new looking vector. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. """ if north_vector is None: north_vector = self.north_vector if normal_vector is None: normal_vector = self.normal_vector self.orienter._setup_normalized_vectors(normal_vector, north_vector) def switch_view( self, normal_vector=None, width=None, center=None, north_vector=None ): r"""Change the view based on any of the view parameters. This will recalculate the orientation and width based on any of normal_vector, width, center, and north_vector. Parameters ---------- normal_vector: array_like, optional The new looking vector. width: float or array of floats, optional The new width. Can be a single value W -> [W,W,W] or an array [W1, W2, W3] (left/right, top/bottom, front/back) center: array_like, optional Specifies the new center. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. """ if width is None: width = self.width if not is_sequence(width): width = (width, width, width) # left/right, tom/bottom, front/back self.width = width if center is not None: self.center = center if north_vector is None: north_vector = self.orienter.north_vector if normal_vector is None: normal_vector = self.orienter.normal_vector self.switch_orientation(normal_vector=normal_vector, north_vector=north_vector) self._setup_box_properties(width, self.center, self.orienter.unit_vectors) def new_image(self): image = np.zeros( (self.resolution[0], self.resolution[1], 4), dtype="float64", order="C" ) return image def get_sampler_args(self, image): rotp = np.concatenate( [self.orienter.inv_mat.ravel("F"), self.back_center.ravel().ndview] ) args = ( np.atleast_3d(rotp), np.atleast_3d(self.box_vectors[2]), self.back_center, ( -self.width[0] / 2.0, self.width[0] / 2.0, -self.width[1] / 2.0, self.width[1] / 2.0, ), image, self.orienter.unit_vectors[0], self.orienter.unit_vectors[1], np.array(self.width, dtype="float64"), "KDTree", self.transfer_function, self.sub_samples, ) kwargs = { "lens_type": "plane-parallel", } return args, kwargs def get_sampler(self, args, kwargs): if self.use_light: if self.light_dir is None: self.set_default_light_dir() temp_dir = np.empty(3, dtype="float64") temp_dir = ( self.light_dir[0] * self.orienter.unit_vectors[1] + self.light_dir[1] * self.orienter.unit_vectors[2] + self.light_dir[2] * self.orienter.unit_vectors[0] ) if self.light_rgba is None: self.set_default_light_rgba() sampler = LightSourceRenderSampler( *args, light_dir=temp_dir, light_rgba=self.light_rgba, **kwargs ) else: sampler = self._sampler_object(*args, **kwargs) return sampler def finalize_image(self, image): view_pos = ( self.front_center + self.orienter.unit_vectors[2] * 1.0e6 * self.width[2] ) image = self.volume.reduce_tree_images(image, view_pos) if not self.transfer_function.grey_opacity: image[:, :, 3] = 1.0 return image def _render(self, double_check, num_threads, image, sampler): ncells = sum(b.source_mask.size for b in self.volume.bricks) pbar = get_pbar("Ray casting", ncells) total_cells = 0 if double_check: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError view_pos = ( self.front_center + self.orienter.unit_vectors[2] * 1.0e6 * self.width[2] ) for brick in self.volume.traverse(view_pos): sampler(brick, num_threads=num_threads) total_cells += brick.source_mask.size pbar.update(total_cells) pbar.finish() image = sampler.aimage image = self.finalize_image(image) return image def _pyplot(self): from matplotlib import pyplot return pyplot def show_tf(self): if self._tf_figure is None: self._tf_figure = self._pyplot.figure(2) self.transfer_function.show(ax=self._tf_figure.axes) self._pyplot.draw() def annotate(self, ax, enhance=True, label_fmt=None): ax.get_xaxis().set_visible(False) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_visible(False) ax.get_yaxis().set_ticks([]) cb = self._pyplot.colorbar( ax.images[0], pad=0.0, fraction=0.05, drawedges=True, shrink=0.9 ) label = self.ds._get_field_info(self.fields[0]).get_label() if self.log_fields[0]: label = r"$\rm{log}\ $" + label self.transfer_function.vert_cbar(ax=cb.ax, label=label, label_fmt=label_fmt) def show_mpl(self, im, enhance=True, clear_fig=True): if self._render_figure is None: self._render_figure = self._pyplot.figure(1) if clear_fig: self._render_figure.clf() if enhance: nz = im[im > 0.0] nim = im / (nz.mean() + 6.0 * np.std(nz)) nim[nim > 1.0] = 1.0 nim[nim < 0.0] = 0.0 del nz else: nim = im ax = self._pyplot.imshow(nim[:, :, :3] / nim[:, :, :3].max(), origin="upper") return ax def draw(self): self._pyplot.draw() def save_annotated( self, fn, image, enhance=True, dpi=100, clear_fig=True, label_fmt=None ): """ Save an image with the transfer function represented as a colorbar. Parameters ---------- fn : str The output filename image : ImageArray The image to annotate enhance : bool, optional Enhance the contrast (default: True) dpi : int, optional Dots per inch in the output image (default: 100) clear_fig : bool, optional Reset the figure (through matplotlib.pyplot.clf()) before drawing. Setting this to false can allow us to overlay the image onto an existing figure label_fmt : str, optional A format specifier (e.g., label_fmt="%.2g") to use in formatting the data values that label the transfer function colorbar. """ image = image.swapaxes(0, 1) ax = self.show_mpl(image, enhance=enhance, clear_fig=clear_fig) self.annotate(ax.axes, enhance, label_fmt=label_fmt) self._pyplot.savefig(fn, bbox_inches="tight", facecolor="black", dpi=dpi) def save_image(self, image, fn=None, clip_ratio=None, transparent=False): if self.comm.rank == 0 and fn is not None: background = None if transparent else "black" image.write_png(fn, rescale=True, background=background) def initialize_source(self): return self.volume.initialize_source( self.fields, self.log_fields, self.no_ghost ) def get_information(self): info_dict = { "fields": self.fields, "type": self.__class__.__name__, "east_vector": self.orienter.unit_vectors[0], "north_vector": self.orienter.unit_vectors[1], "normal_vector": self.orienter.unit_vectors[2], "width": self.width, "dataset": self.ds.directory, } return info_dict def snapshot( self, fn=None, clip_ratio=None, double_check=False, num_threads=0, transparent=False, ): r"""Ray-cast the camera. This method instructs the camera to take a snapshot -- i.e., call the ray caster -- based on its current settings. Parameters ---------- fn : string, optional If supplied, the image will be saved out to this before being returned. Scaling will be to the maximum value. clip_ratio : float, optional If supplied, the 'max_val' argument to write_bitmap will be handed clip_ratio * image.std() double_check : bool, optional Optionally makes sure that the data contains only valid entries. Used for debugging. num_threads : int, optional If supplied, will use 'num_threads' number of OpenMP threads during the rendering. Defaults to 0, which uses the environment variable OMP_NUM_THREADS. transparent: bool, optional Optionally saves out the 4-channel rgba image, which can appear empty if the alpha channel is low everywhere. Default: False Returns ------- image : array An (N,M,3) array of the final returned values, in float64 form. """ if num_threads is None: num_threads = get_num_threads() image = self.new_image() args, kwargs = self.get_sampler_args(image) sampler = self.get_sampler(args, kwargs) self.initialize_source() image = ImageArray( self._render(double_check, num_threads, image, sampler), info=self.get_information(), ) # flip it up/down to handle how the png orientation is done image = image[:, ::-1, :] self.save_image(image, fn=fn, clip_ratio=clip_ratio, transparent=transparent) return image def show(self, clip_ratio=None): r"""This will take a snapshot and display the resultant image in the IPython notebook. If yt is being run from within an IPython session, and it is able to determine this, this function will snapshot and send the resultant image to the IPython notebook for display. If yt can't determine if it's inside an IPython session, it will raise YTNotInsideNotebook. Parameters ---------- clip_ratio : float, optional If supplied, the 'max_val' argument to write_bitmap will be handed clip_ratio * image.std() Examples -------- >>> cam.show() """ if IS_IPYTHON: from IPython.core.displaypub import publish_display_data image = self.snapshot()[:, :, :3] if clip_ratio is not None: clip_ratio *= image.std() data = write_bitmap(image, None, clip_ratio) publish_display_data( data={"image/png": data}, source="yt.visualization.volume_rendering.camera.Camera", ) else: raise YTNotInsideNotebook def set_default_light_dir(self): self.light_dir = [1.0, 1.0, 1.0] def set_default_light_rgba(self): self.light_rgba = [1.0, 1.0, 1.0, 1.0] def zoom(self, factor): r"""Change the distance to the focal point. This will zoom the camera in by some `factor` toward the focal point, along the current view direction, modifying the left/right and up/down extents as well. Parameters ---------- factor : float The factor by which to reduce the distance to the focal point. Notes ----- You will need to call snapshot() again to get a new image. """ self.width /= factor self._setup_box_properties(self.width, self.center, self.orienter.unit_vectors) def zoomin(self, final, n_steps, clip_ratio=None): r"""Loop over a zoomin and return snapshots along the way. This will yield `n_steps` snapshots until the current view has been zooming in to a final factor of `final`. Parameters ---------- final : float The zoom factor, with respect to current, desired at the end of the sequence. n_steps : int The number of zoom snapshots to make. clip_ratio : float, optional If supplied, the 'max_val' argument to write_bitmap will be handed clip_ratio * image.std() Examples -------- >>> for i, snapshot in enumerate(cam.zoomin(100.0, 10)): ... iw.write_bitmap(snapshot, "zoom_%04i.png" % i) """ f = final ** (1.0 / n_steps) for _ in range(n_steps): self.zoom(f) yield self.snapshot(clip_ratio=clip_ratio) def move_to( self, final, n_steps, final_width=None, exponential=False, clip_ratio=None ): r"""Loop over a look_at This will yield `n_steps` snapshots until the current view has been moved to a final center of `final` with a final width of final_width. Parameters ---------- final : array_like The final center to move to after `n_steps` n_steps : int The number of look_at snapshots to make. final_width: float or array_like, optional Specifies the final width after `n_steps`. Useful for moving and zooming at the same time. exponential : boolean Specifies whether the move/zoom transition follows an exponential path toward the destination or linear clip_ratio : float, optional If supplied, the 'max_val' argument to write_bitmap will be handed clip_ratio * image.std() Examples -------- >>> for i, snapshot in enumerate(cam.move_to([0.2, 0.3, 0.6], 10)): ... iw.write_bitmap(snapshot, "move_%04i.png" % i) """ dW = None if not isinstance(final, YTArray): final = self.ds.arr(final, units="code_length") if exponential: if final_width is not None: if not is_sequence(final_width): final_width = [final_width, final_width, final_width] if not isinstance(final_width, YTArray): final_width = self.ds.arr(final_width, units="code_length") # left/right, top/bottom, front/back if (self.center == 0.0).all(): self.center += (final - self.center) / (10.0 * n_steps) final_zoom = final_width / self.width dW = final_zoom ** (1.0 / n_steps) else: dW = self.ds.arr([1.0, 1.0, 1.0], "code_length") position_diff = final / self.center dx = position_diff ** (1.0 / n_steps) else: if final_width is not None: if not is_sequence(final_width): final_width = [final_width, final_width, final_width] if not isinstance(final_width, YTArray): final_width = self.ds.arr(final_width, units="code_length") # left/right, top/bottom, front/back dW = (1.0 * final_width - self.width) / n_steps else: dW = self.ds.arr([0.0, 0.0, 0.0], "code_length") dx = (final - self.center) * 1.0 / n_steps for _ in range(n_steps): if exponential: self.switch_view(center=self.center * dx, width=self.width * dW) else: self.switch_view(center=self.center + dx, width=self.width + dW) yield self.snapshot(clip_ratio=clip_ratio) def rotate(self, theta, rot_vector=None): r"""Rotate by a given angle Rotate the view. If `rot_vector` is None, rotation will occur around the `north_vector`. Parameters ---------- theta : float, in radians Angle (in radians) by which to rotate the view. rot_vector : array_like, optional Specify the rotation vector around which rotation will occur. Defaults to None, which sets rotation around `north_vector` Examples -------- >>> cam.rotate(np.pi / 4) """ rotate_all = rot_vector is not None if rot_vector is None: rot_vector = self.rotation_vector else: rot_vector = ensure_numpy_array(rot_vector) rot_vector = rot_vector / np.linalg.norm(rot_vector) R = get_rotation_matrix(theta, rot_vector) normal_vector = self.front_center - self.center normal_vector = normal_vector / np.sqrt((normal_vector**2).sum()) if rotate_all: self.switch_view( normal_vector=np.dot(R, normal_vector), north_vector=np.dot(R, self.orienter.unit_vectors[1]), ) else: self.switch_view(normal_vector=np.dot(R, normal_vector)) def pitch(self, theta): r"""Rotate by a given angle about the horizontal axis Pitch the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to pitch the view. Examples -------- >>> cam.pitch(np.pi / 4) """ rot_vector = self.orienter.unit_vectors[0] R = get_rotation_matrix(theta, rot_vector) self.switch_view( normal_vector=np.dot(R, self.orienter.unit_vectors[2]), north_vector=np.dot(R, self.orienter.unit_vectors[1]), ) if self.orienter.steady_north: self.orienter.north_vector = self.orienter.unit_vectors[1] def yaw(self, theta): r"""Rotate by a given angle about the vertical axis Yaw the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to yaw the view. Examples -------- >>> cam.yaw(np.pi / 4) """ rot_vector = self.orienter.unit_vectors[1] R = get_rotation_matrix(theta, rot_vector) self.switch_view(normal_vector=np.dot(R, self.orienter.unit_vectors[2])) def roll(self, theta): r"""Rotate by a given angle about the view normal axis Roll the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to roll the view. Examples -------- >>> cam.roll(np.pi / 4) """ rot_vector = self.orienter.unit_vectors[2] R = get_rotation_matrix(theta, rot_vector) self.switch_view( normal_vector=np.dot(R, self.orienter.unit_vectors[2]), north_vector=np.dot(R, self.orienter.unit_vectors[1]), ) if self.orienter.steady_north: self.orienter.north_vector = np.dot(R, self.orienter.north_vector) def rotation(self, theta, n_steps, rot_vector=None, clip_ratio=None): r"""Loop over rotate, creating a rotation This will yield `n_steps` snapshots until the current view has been rotated by an angle `theta` Parameters ---------- theta : float, in radians Angle (in radians) by which to rotate the view. n_steps : int The number of look_at snapshots to make. rot_vector : array_like, optional Specify the rotation vector around which rotation will occur. Defaults to None, which sets rotation around the original `north_vector` clip_ratio : float, optional If supplied, the 'max_val' argument to write_bitmap will be handed clip_ratio * image.std() Examples -------- >>> for i, snapshot in enumerate(cam.rotation(np.pi, 10)): ... iw.write_bitmap(snapshot, "rotation_%04i.png" % i) """ dtheta = (1.0 * theta) / n_steps for _ in range(n_steps): self.rotate(dtheta, rot_vector=rot_vector) yield self.snapshot(clip_ratio=clip_ratio) data_object_registry["camera"] = Camera class InteractiveCamera(Camera): frames: list[ImageArray] = [] def snapshot(self, fn=None, clip_ratio=None): self._pyplot.figure(2) self.transfer_function.show() self._pyplot.draw() im = Camera.snapshot(self, fn, clip_ratio) self._pyplot.figure(1) self._pyplot.imshow(im / im.max()) self._pyplot.draw() self.frames.append(im) def rotation(self, theta, n_steps, rot_vector=None): for frame in Camera.rotation(self, theta, n_steps, rot_vector): if frame is not None: self.frames.append(frame) def zoomin(self, final, n_steps): for frame in Camera.zoomin(self, final, n_steps): if frame is not None: self.frames.append(frame) def clear_frames(self): del self.frames self.frames = [] def save(self, fn): self._pyplot.savefig(fn, bbox_inches="tight", facecolor="black") def save_frames(self, basename, clip_ratio=None): for i, frame in enumerate(self.frames): fn = basename + "_%04i.png" % i if clip_ratio is not None: write_bitmap(frame, fn, clip_ratio * frame.std()) else: write_bitmap(frame, fn) data_object_registry["interactive_camera"] = InteractiveCamera class PerspectiveCamera(Camera): r"""A viewpoint into a volume, for perspective volume rendering. The camera represents the eye of an observer, which will be used to generate ray-cast volume renderings of the domain. The rays start from the camera and end on the image plane, which generates a perspective view. Note: at the moment, this results in a left-handed coordinate system view Parameters ---------- center : array_like The location of the camera normal_vector : array_like The vector from the camera position to the center of the image plane width : float or list of floats width[0] and width[1] give the width and height of the image plane, and width[2] gives the depth of the image plane (distance between the camera and the center of the image plane). The view angles thus become: 2 * arctan(0.5 * width[0] / width[2]) in horizontal direction 2 * arctan(0.5 * width[1] / width[2]) in vertical direction (The following parameters are identical with the definitions in Camera class) resolution : int or list of ints The number of pixels in each direction. transfer_function : `yt.visualization.volume_rendering.TransferFunction` The transfer function used to map values to colors in an image. If not specified, defaults to a ProjectionTransferFunction. north_vector : array_like, optional The 'up' direction for the plane of rays. If not specific, calculated automatically. steady_north : bool, optional Boolean to control whether to normalize the north_vector by subtracting off the dot product of it and the normal vector. Makes it easier to do rotations along a single axis. If north_vector is specified, is switched to True. Default: False volume : `yt.extensions.volume_rendering.AMRKDTree`, optional The volume to ray cast through. Can be specified for finer-grained control, but otherwise will be automatically generated. fields : list of fields, optional This is the list of fields we want to volume render; defaults to Density. log_fields : list of bool, optional Whether we should take the log of the fields before supplying them to the volume rendering mechanism. sub_samples : int, optional The number of samples to take inside every cell per ray. ds : ~yt.data_objects.static_output.Dataset For now, this is a require parameter! But in the future it will become optional. This is the dataset to volume render. use_kd: bool, optional Specifies whether or not to use a kd-Tree framework for the Homogenized Volume and ray-casting. Default to True. max_level: int, optional Specifies the maximum level to be rendered. Also specifies the maximum level used in the kd-Tree construction. Defaults to None (all levels), and only applies if use_kd=True. no_ghost: bool, optional Optimization option. If True, homogenized bricks will extrapolate out from grid instead of interpolating from ghost zones that have to first be calculated. This can lead to large speed improvements, but at a loss of accuracy/smoothness in resulting image. The effects are less notable when the transfer function is smooth and broad. Default: True data_source: data container, optional Optionally specify an arbitrary data source to the volume rendering. All cells not included in the data source will be ignored during ray casting. By default this will get set to ds.all_data(). """ def __init__(self, *args, **kwargs): Camera.__init__(self, *args, **kwargs) def get_sampler_args(self, image): east_vec = self.orienter.unit_vectors[0].reshape(3, 1) north_vec = self.orienter.unit_vectors[1].reshape(3, 1) px = np.linspace(-0.5, 0.5, self.resolution[0])[np.newaxis, :] py = np.linspace(-0.5, 0.5, self.resolution[1])[np.newaxis, :] sample_x = self.width[0] * np.array(east_vec * px).transpose() sample_y = self.width[1] * np.array(north_vec * py).transpose() vectors = np.zeros( (self.resolution[0], self.resolution[1], 3), dtype="float64", order="C" ) sample_x = np.repeat( sample_x.reshape(self.resolution[0], 1, 3), self.resolution[1], axis=1 ) sample_y = np.repeat( sample_y.reshape(1, self.resolution[1], 3), self.resolution[0], axis=0 ) normal_vec = np.zeros( (self.resolution[0], self.resolution[1], 3), dtype="float64", order="C" ) normal_vec[:, :, 0] = self.orienter.unit_vectors[2, 0] normal_vec[:, :, 1] = self.orienter.unit_vectors[2, 1] normal_vec[:, :, 2] = self.orienter.unit_vectors[2, 2] vectors = sample_x + sample_y + normal_vec * self.width[2] positions = np.zeros( (self.resolution[0], self.resolution[1], 3), dtype="float64", order="C" ) positions[:, :, 0] = self.center[0] positions[:, :, 1] = self.center[1] positions[:, :, 2] = self.center[2] positions = self.ds.arr(positions, units="code_length") dummy = np.ones(3, dtype="float64") image.shape = (self.resolution[0], self.resolution[1], 4) args = ( positions, vectors, self.back_center, (0.0, 1.0, 0.0, 1.0), image, dummy, dummy, np.zeros(3, dtype="float64"), "KDTree", self.transfer_function, self.sub_samples, ) kwargs = { "lens_type": "perspective", } return args, kwargs def _render(self, double_check, num_threads, image, sampler): ncells = sum(b.source_mask.size for b in self.volume.bricks) pbar = get_pbar("Ray casting", ncells) total_cells = 0 if double_check: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError for brick in self.volume.traverse(self.front_center): sampler(brick, num_threads=num_threads) total_cells += brick.source_mask.size pbar.update(total_cells) pbar.finish() image = self.finalize_image(sampler.aimage) return image def finalize_image(self, image): view_pos = self.front_center image.shape = self.resolution[0], self.resolution[1], 4 image = self.volume.reduce_tree_images(image, view_pos) if not self.transfer_function.grey_opacity: image[:, :, 3] = 1.0 return image def project_to_plane(self, pos, res=None): if res is None: res = self.resolution sight_vector = pos - self.center pos1 = sight_vector for i in range(0, sight_vector.shape[0]): sight_vector_norm = np.sqrt(np.dot(sight_vector[i], sight_vector[i])) sight_vector[i] = sight_vector[i] / sight_vector_norm sight_vector = self.ds.arr(sight_vector.value, units="dimensionless") sight_center = self.center + self.width[2] * self.orienter.unit_vectors[2] for i in range(0, sight_vector.shape[0]): sight_angle_cos = np.dot(sight_vector[i], self.orienter.unit_vectors[2]) if np.arccos(sight_angle_cos) < 0.5 * np.pi: sight_length = self.width[2] / sight_angle_cos else: # The corner is on the backwards, then put it outside of the # image It can not be simply removed because it may connect to # other corner within the image, which produces visible domain # boundary line sight_length = np.sqrt( self.width[0] ** 2 + self.width[1] ** 2 ) / np.sqrt(1 - sight_angle_cos**2) pos1[i] = self.center + sight_length * sight_vector[i] dx = np.dot(pos1 - sight_center, self.orienter.unit_vectors[0]) dy = np.dot(pos1 - sight_center, self.orienter.unit_vectors[1]) dz = np.dot(pos1 - sight_center, self.orienter.unit_vectors[2]) # Transpose into image coords. px = (res[0] * 0.5 + res[0] / self.width[0] * dx).astype("int64") py = (res[1] * 0.5 + res[1] / self.width[1] * dy).astype("int64") return px, py, dz def yaw(self, theta, rot_center): r"""Rotate by a given angle about the vertical axis through the point center. This is accomplished by rotating the focal point and then setting the looking vector to point to the center. Yaw the view. Parameters ---------- theta : float, in radians Angle (in radians) by which to yaw the view. rot_center : a tuple (x, y, z) The point to rotate about Examples -------- >>> cam.yaw(np.pi / 4, (0.0, 0.0, 0.0)) """ rot_vector = self.orienter.unit_vectors[1] focal_point = self.center - rot_center R = get_rotation_matrix(theta, rot_vector) focal_point = np.dot(R, focal_point) + rot_center normal_vector = rot_center - focal_point normal_vector = normal_vector / np.sqrt((normal_vector**2).sum()) self.switch_view(normal_vector=normal_vector, center=focal_point) data_object_registry["perspective_camera"] = PerspectiveCamera def corners(left_edge, right_edge): return np.array( [ [left_edge[:, 0], left_edge[:, 1], left_edge[:, 2]], [right_edge[:, 0], left_edge[:, 1], left_edge[:, 2]], [right_edge[:, 0], right_edge[:, 1], left_edge[:, 2]], [right_edge[:, 0], right_edge[:, 1], right_edge[:, 2]], [left_edge[:, 0], right_edge[:, 1], right_edge[:, 2]], [left_edge[:, 0], left_edge[:, 1], right_edge[:, 2]], [right_edge[:, 0], left_edge[:, 1], right_edge[:, 2]], [left_edge[:, 0], right_edge[:, 1], left_edge[:, 2]], ], dtype="float64", ) class HEALpixCamera(Camera): _sampler_object = None def __init__( self, center, radius, nside, transfer_function=None, fields=None, sub_samples=5, log_fields=None, volume=None, ds=None, use_kd=True, no_ghost=False, use_light=False, inner_radius=10, ): mylog.error("I am sorry, HEALpix Camera does not work yet in 3.0") raise NotImplementedError def new_image(self): image = np.zeros((12 * self.nside**2, 1, 4), dtype="float64", order="C") return image def get_sampler_args(self, image): nv = 12 * self.nside**2 vs = arr_pix2vec_nest(self.nside, np.arange(nv)) vs.shape = (nv, 1, 3) vs += 1e-8 uv = np.ones(3, dtype="float64") positions = np.ones((nv, 1, 3), dtype="float64") * self.center dx = min(g.dds.min() for g in self.ds.index.find_point(self.center)[0]) positions += self.inner_radius * dx * vs vs *= self.radius args = ( positions, vs, self.center, (0.0, 1.0, 0.0, 1.0), image, uv, uv, np.zeros(3, dtype="float64"), "KDTree", ) if self._needs_tf: args += (self.transfer_function,) args += (self.sub_samples,) return args, {} def _render(self, double_check, num_threads, image, sampler): pbar = get_pbar( "Ray casting", (self.volume.brick_dimensions + 1).prod(axis=-1).sum() ) total_cells = 0 if double_check: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError view_pos = self.center for brick in self.volume.traverse(view_pos): sampler(brick, num_threads=num_threads) total_cells += np.prod(brick.my_data[0].shape) pbar.update(total_cells) pbar.finish() image = sampler.aimage self.finalize_image(image) return image def finalize_image(self, image): view_pos = self.center image = self.volume.reduce_tree_images(image, view_pos) return image def get_information(self): info_dict = { "fields": self.fields, "type": self.__class__.__name__, "center": self.center, "radius": self.radius, "dataset": self.ds.directory, } return info_dict def snapshot( self, fn=None, clip_ratio=None, double_check=False, num_threads=0, clim=None, label=None, ): r"""Ray-cast the camera. This method instructs the camera to take a snapshot -- i.e., call the ray caster -- based on its current settings. Parameters ---------- fn : string, optional If supplied, the image will be saved out to this before being returned. Scaling will be to the maximum value. clip_ratio : float, optional If supplied, the 'max_val' argument to write_bitmap will be handed clip_ratio * image.std() Returns ------- image : array An (N,M,3) array of the final returned values, in float64 form. """ if num_threads is None: num_threads = get_num_threads() image = self.new_image() args, kwargs = self.get_sampler_args(image) sampler = self.get_sampler(args, kwargs) self.volume.initialize_source() image = ImageArray( self._render(double_check, num_threads, image, sampler), info=self.get_information(), ) self.save_image(image, fn=fn, clim=clim, label=label) return image def save_image(self, image, fn=None, clim=None, label=None): if self.comm.rank == 0 and fn is not None: # This assumes Density; this is a relatively safe assumption. if label is None: label = f"Projected {self.fields[0]}" if clim is not None: cmin, cmax = clim else: cmin = cmax = None plot_allsky_healpix( image[:, 0, 0], self.nside, fn, label, cmin=cmin, cmax=cmax ) class StereoPairCamera(Camera): def __init__(self, original_camera, relative_separation=0.005): ParallelAnalysisInterface.__init__(self) self.original_camera = original_camera self.relative_separation = relative_separation def split(self): oc = self.original_camera uv = oc.orienter.unit_vectors c = oc.center fc = oc.front_center wx, wy, wz = oc.width left_normal = fc + uv[1] * 0.5 * self.relative_separation * wx - c right_normal = fc - uv[1] * 0.5 * self.relative_separation * wx - c left_camera = Camera( c, left_normal, oc.width, oc.resolution, oc.transfer_function, north_vector=uv[0], volume=oc.volume, fields=oc.fields, log_fields=oc.log_fields, sub_samples=oc.sub_samples, ds=oc.ds, ) right_camera = Camera( c, right_normal, oc.width, oc.resolution, oc.transfer_function, north_vector=uv[0], volume=oc.volume, fields=oc.fields, log_fields=oc.log_fields, sub_samples=oc.sub_samples, ds=oc.ds, ) return (left_camera, right_camera) class FisheyeCamera(Camera): def __init__( self, center, radius, fov, resolution, transfer_function=None, fields=None, sub_samples=5, log_fields=None, volume=None, ds=None, no_ghost=False, rotation=None, use_light=False, ): ParallelAnalysisInterface.__init__(self) self.use_light = use_light self.light_dir = None self.light_rgba = None if rotation is None: rotation = np.eye(3) self.rotation_matrix = rotation self.no_ghost = no_ghost if ds is not None: self.ds = ds self.center = np.array(center, dtype="float64") self.radius = radius self.fov = fov if is_sequence(resolution): raise RuntimeError("Resolution must be a single int") self.resolution = resolution if transfer_function is None: transfer_function = ProjectionTransferFunction() self.transfer_function = transfer_function if fields is None: fields = [("gas", "density")] dd = self.ds.all_data() fields = dd._determine_fields(fields) self.fields = fields if log_fields is None: log_fields = [self.ds._get_field_info(f).take_log for f in fields] self.log_fields = log_fields self.sub_samples = sub_samples if volume is None: volume = AMRKDTree(self.ds) volume.set_fields(fields, log_fields, no_ghost) self.volume = volume def get_information(self): return {} def new_image(self): image = np.zeros((self.resolution**2, 1, 4), dtype="float64", order="C") return image def get_sampler_args(self, image): vp = arr_fisheye_vectors(self.resolution, self.fov) vp.shape = (self.resolution**2, 1, 3) vp2 = vp.copy() for i in range(3): vp[:, :, i] = (vp2 * self.rotation_matrix[:, i]).sum(axis=2) del vp2 vp *= self.radius uv = np.ones(3, dtype="float64") positions = np.ones((self.resolution**2, 1, 3), dtype="float64") * self.center args = ( positions, vp, self.center, (0.0, 1.0, 0.0, 1.0), image, uv, uv, np.zeros(3, dtype="float64"), "KDTree", self.transfer_function, self.sub_samples, ) return args, {} def finalize_image(self, image): image.shape = self.resolution, self.resolution, 4 def _render(self, double_check, num_threads, image, sampler): pbar = get_pbar( "Ray casting", (self.volume.brick_dimensions + 1).prod(axis=-1).sum() ) total_cells = 0 if double_check: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError view_pos = self.center for brick in self.volume.traverse(view_pos): sampler(brick, num_threads=num_threads) total_cells += np.prod(brick.my_data[0].shape) pbar.update(total_cells) pbar.finish() image = sampler.aimage self.finalize_image(image) return image class MosaicCamera(Camera): def __init__( self, center, normal_vector, width, resolution, transfer_function=None, north_vector=None, steady_north=False, volume=None, fields=None, log_fields=None, sub_samples=5, ds=None, use_kd=True, l_max=None, no_ghost=True, tree_type="domain", expand_factor=1.0, le=None, re=None, nimx=1, nimy=1, procs_per_wg=None, preload=True, use_light=False, ): ParallelAnalysisInterface.__init__(self) self.procs_per_wg = procs_per_wg if ds is not None: self.ds = ds if not is_sequence(resolution): resolution = (int(resolution / nimx), int(resolution / nimy)) self.resolution = resolution self.nimx = nimx self.nimy = nimy self.sub_samples = sub_samples if not is_sequence(width): width = (width, width, width) # front/back, left/right, top/bottom self.width = np.array([width[0], width[1], width[2]]) self.center = center self.steady_north = steady_north self.expand_factor = expand_factor # This seems to be necessary for now. Not sure what goes wrong when not true. if north_vector is not None: self.steady_north = True self.north_vector = north_vector self.normal_vector = normal_vector if fields is None: fields = [("gas", "density")] self.fields = fields if transfer_function is None: transfer_function = ProjectionTransferFunction() self.transfer_function = transfer_function self.log_fields = log_fields self.use_kd = use_kd self.l_max = l_max self.no_ghost = no_ghost self.preload = preload self.use_light = use_light self.light_dir = None self.light_rgba = None self.le = le self.re = re self.width[0] /= self.nimx self.width[1] /= self.nimy self.orienter = Orientation( normal_vector, north_vector=north_vector, steady_north=steady_north ) self.rotation_vector = self.orienter.north_vector # self._setup_box_properties(width, center, self.orienter.unit_vectors) if self.no_ghost: mylog.warning( "no_ghost is currently True (default). " "This may lead to artifacts at grid boundaries." ) self.tree_type = tree_type self.volume = volume # self.cameras = np.empty(self.nimx*self.nimy) def build_volume( self, volume, fields, log_fields, l_max, no_ghost, tree_type, le, re ): if volume is None: if self.use_kd: raise NotImplementedError volume = AMRKDTree( self.ds, l_max=l_max, fields=self.fields, no_ghost=no_ghost, tree_type=tree_type, log_fields=log_fields, le=le, re=re, ) else: self.use_kd = isinstance(volume, AMRKDTree) return volume def new_image(self): image = np.zeros( (self.resolution[0], self.resolution[1], 4), dtype="float64", order="C" ) return image def _setup_box_properties(self, width, center, unit_vectors): owidth = deepcopy(width) self.width = width self.origin = ( self.center - 0.5 * self.nimx * self.width[0] * self.orienter.unit_vectors[0] - 0.5 * self.nimy * self.width[1] * self.orienter.unit_vectors[1] - 0.5 * self.width[2] * self.orienter.unit_vectors[2] ) dx = self.width[0] dy = self.width[1] offi = self.imi + 0.5 offj = self.imj + 0.5 mylog.info("Mosaic offset: %f %f", offi, offj) global_center = self.center self.center = self.origin self.center += offi * dx * self.orienter.unit_vectors[0] self.center += offj * dy * self.orienter.unit_vectors[1] self.box_vectors = np.array( [ self.orienter.unit_vectors[0] * dx * self.nimx, self.orienter.unit_vectors[1] * dy * self.nimy, self.orienter.unit_vectors[2] * self.width[2], ] ) self.back_center = ( self.center - 0.5 * self.width[0] * self.orienter.unit_vectors[2] ) self.front_center = ( self.center + 0.5 * self.width[0] * self.orienter.unit_vectors[2] ) self.center = global_center self.width = owidth def snapshot(self, fn=None, clip_ratio=None, double_check=False, num_threads=0): my_storage = {} offx, offy = np.meshgrid(range(self.nimx), range(self.nimy)) offxy = zip(offx.ravel(), offy.ravel(), strict=True) for sto, xy in parallel_objects( offxy, self.procs_per_wg, storage=my_storage, dynamic=True ): self.volume = self.build_volume( self.volume, self.fields, self.log_fields, self.l_max, self.no_ghost, self.tree_type, self.le, self.re, ) self.initialize_source() self.imi, self.imj = xy mylog.debug("Working on: %i %i", self.imi, self.imj) self._setup_box_properties( self.width, self.center, self.orienter.unit_vectors ) image = self.new_image() args, kwargs = self.get_sampler_args(image) sampler = self.get_sampler(args, kwargs) image = self._render(double_check, num_threads, image, sampler) sto.id = self.imj * self.nimx + self.imi sto.result = image image = self.reduce_images(my_storage) self.save_image(image, fn=fn, clip_ratio=clip_ratio) return image def reduce_images(self, im_dict): final_image = 0 if self.comm.rank == 0: offx, offy = np.meshgrid(range(self.nimx), range(self.nimy)) offxy = zip(offx.ravel(), offy.ravel(), strict=True) nx, ny = self.resolution final_image = np.empty( (nx * self.nimx, ny * self.nimy, 4), dtype="float64", order="C" ) for xy in offxy: i, j = xy ind = j * self.nimx + i final_image[i * nx : (i + 1) * nx, j * ny : (j + 1) * ny, :] = im_dict[ ind ] return final_image data_object_registry["mosaic_camera"] = MosaicCamera def plot_allsky_healpix( image, nside, fn, label="", rotation=None, take_log=True, resolution=512, cmin=None, cmax=None, ): import matplotlib.backends.backend_agg import matplotlib.figure if rotation is None: rotation = np.eye(3, dtype="float64") img, count = pixelize_healpix(nside, image, resolution, resolution, rotation) fig = matplotlib.figure.Figure((10, 5)) ax = fig.add_subplot(1, 1, 1, projection="aitoff") if take_log: func = np.log10 else: def _identity(x): return x func = _identity implot = ax.imshow( func(img), extent=(-np.pi, np.pi, -np.pi / 2, np.pi / 2), clip_on=False, aspect=0.5, vmin=cmin, vmax=cmax, ) cb = fig.colorbar(implot, orientation="horizontal") cb.set_label(label) ax.xaxis.set_ticks(()) ax.yaxis.set_ticks(()) canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig) canvas.print_figure(fn) return img, count class ProjectionCamera(Camera): def __init__( self, center, normal_vector, width, resolution, field, weight=None, volume=None, no_ghost=False, north_vector=None, ds=None, interpolated=False, method="integrate", ): if not interpolated: volume = 1 self.interpolated = interpolated self.field = field self.weight = weight self.resolution = resolution self.method = method fields = [field] if self.weight is not None: # This is a temporary field, which we will remove at the end # it is given a unique name to avoid conflicting with other # class instances self.weightfield = ("index", "temp_weightfield_%u" % (id(self),)) def _make_wf(f, w): def temp_weightfield(field, data): tr = data[f].astype("float64") * data[w] return data.apply_units(tr, field.units) return temp_weightfield ds.field_info.add_field( self.weightfield, function=_make_wf(self.field, self.weight) ) # Now we have to tell the dataset to add it and to calculate # its dependencies.. deps, _ = ds.field_info.check_derived_fields([self.weightfield]) ds.field_dependencies.update(deps) fields = [self.weightfield, self.weight] self.fields = fields self.log_fields = [False] * len(self.fields) Camera.__init__( self, center, normal_vector, width, resolution, None, fields=fields, ds=ds, volume=volume, log_fields=self.log_fields, north_vector=north_vector, no_ghost=no_ghost, ) # this would be better in an __exit__ function, but that would require # changes in code that uses this class def __del__(self): if hasattr(self, "weightfield") and hasattr(self, "ds"): try: self.ds.field_info.pop(self.weightfield) self.ds.field_dependencies.pop(self.weightfield) except KeyError: pass try: Camera.__del__(self) except AttributeError: pass def get_sampler(self, args, kwargs): if self.interpolated: sampler = InterpolatedProjectionSampler(*args, **kwargs) else: sampler = ProjectionSampler(*args, **kwargs) return sampler def initialize_source(self): if self.interpolated: Camera.initialize_source(self) else: pass def get_sampler_args(self, image): rotp = np.concatenate( [self.orienter.inv_mat.ravel("F"), self.back_center.ravel().ndview] ) args = ( np.atleast_3d(rotp), np.atleast_3d(self.box_vectors[2]), self.back_center, ( -self.width[0] / 2.0, self.width[0] / 2.0, -self.width[1] / 2.0, self.width[1] / 2.0, ), image, self.orienter.unit_vectors[0], self.orienter.unit_vectors[1], np.array(self.width, dtype="float64"), "KDTree", self.sub_samples, ) kwargs = {"lens_type": "plane-parallel"} return args, kwargs def finalize_image(self, image): ds = self.ds dd = ds.all_data() field = dd._determine_fields([self.field])[0] finfo = ds._get_field_info(field) dl = 1.0 if self.method == "integrate": if self.weight is None: dl = self.width[2].in_units(ds.unit_system["length"]) else: image[:, :, 0] /= image[:, :, 1] return ImageArray(image[:, :, 0], finfo.units, registry=ds.unit_registry) * dl def _render(self, double_check, num_threads, image, sampler): # Calculate the eight corners of the box # Back corners ... if self.interpolated: return Camera._render(self, double_check, num_threads, image, sampler) ds = self.ds width = self.width[2] north_vector = self.orienter.unit_vectors[0] east_vector = self.orienter.unit_vectors[1] normal_vector = self.orienter.unit_vectors[2] fields = self.fields mi = ds.domain_right_edge.copy() ma = ds.domain_left_edge.copy() for off1 in [-1, 1]: for off2 in [-1, 1]: for off3 in [-1, 1]: this_point = ( self.center + width / 2.0 * off1 * north_vector + width / 2.0 * off2 * east_vector + width / 2.0 * off3 * normal_vector ) np.minimum(mi, this_point, out=mi) np.maximum(ma, this_point, out=ma) # Now we have a bounding box. data_source = ds.region(self.center, mi, ma) for grid, mask in data_source.blocks: data = [(grid[field] * mask).astype("float64") for field in fields] pg = PartitionedGrid( grid.id, data, mask.astype("uint8"), grid.LeftEdge, grid.RightEdge, grid.ActiveDimensions.astype("int64"), ) grid.clear_data() sampler(pg, num_threads=num_threads) image = self.finalize_image(sampler.aimage) return image def save_image(self, image, fn=None, clip_ratio=None): dd = self.ds.all_data() field = dd._determine_fields([self.field])[0] finfo = self.ds._get_field_info(field) if finfo.take_log: im = np.log10(image) else: im = image if self.comm.rank == 0 and fn is not None: if clip_ratio is not None: write_image(im, fn) else: write_image(im, fn) def snapshot(self, fn=None, clip_ratio=None, double_check=False, num_threads=0): if num_threads is None: num_threads = get_num_threads() image = self.new_image() args, kwargs = self.get_sampler_args(image) sampler = self.get_sampler(args, kwargs) self.initialize_source() image = ImageArray( self._render(double_check, num_threads, image, sampler), info=self.get_information(), ) self.save_image(image, fn=fn, clip_ratio=clip_ratio) return image snapshot.__doc__ = Camera.snapshot.__doc__ data_object_registry["projection_camera"] = ProjectionCamera class SphericalCamera(Camera): def __init__(self, *args, **kwargs): Camera.__init__(self, *args, **kwargs) if self.resolution[0] / self.resolution[1] != 2: mylog.info("Warning: It's recommended to set the aspect ratio to 2:1") self.resolution = np.asarray(self.resolution) + 2 def get_sampler_args(self, image): px = np.linspace(-np.pi, np.pi, self.resolution[0], endpoint=True)[:, None] py = np.linspace(-np.pi / 2.0, np.pi / 2.0, self.resolution[1], endpoint=True)[ None, : ] vectors = np.zeros( (self.resolution[0], self.resolution[1], 3), dtype="float64", order="C" ) vectors[:, :, 0] = np.cos(px) * np.cos(py) vectors[:, :, 1] = np.sin(px) * np.cos(py) vectors[:, :, 2] = np.sin(py) vectors = vectors * self.width[0] positions = self.center + vectors * 0 R1 = get_rotation_matrix(0.5 * np.pi, [1, 0, 0]) R2 = get_rotation_matrix(0.5 * np.pi, [0, 0, 1]) uv = np.dot(R1, self.orienter.unit_vectors) uv = np.dot(R2, uv) vectors.reshape((self.resolution[0] * self.resolution[1], 3)) vectors = np.dot(vectors, uv) vectors.reshape((self.resolution[0], self.resolution[1], 3)) dummy = np.ones(3, dtype="float64") image.shape = (self.resolution[0] * self.resolution[1], 1, 4) vectors.shape = (self.resolution[0] * self.resolution[1], 1, 3) positions.shape = (self.resolution[0] * self.resolution[1], 1, 3) args = ( positions, vectors, self.back_center, (0.0, 1.0, 0.0, 1.0), image, dummy, dummy, np.zeros(3, dtype="float64"), self.transfer_function, self.sub_samples, ) return args, {"lens_type": "spherical"} def _render(self, double_check, num_threads, image, sampler): ncells = sum(b.source_mask.size for b in self.volume.bricks) pbar = get_pbar("Ray casting", ncells) total_cells = 0 if double_check: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError for brick in self.volume.traverse(self.front_center): sampler(brick, num_threads=num_threads) total_cells += brick.source_mask.size pbar.update(total_cells) pbar.finish() image = self.finalize_image(sampler.aimage) return image def finalize_image(self, image): view_pos = self.front_center image.shape = self.resolution[0], self.resolution[1], 4 image = self.volume.reduce_tree_images(image, view_pos) if not self.transfer_function.grey_opacity: image[:, :, 3] = 1.0 image = image[1:-1, 1:-1, :] return image data_object_registry["spherical_camera"] = SphericalCamera class StereoSphericalCamera(Camera): def __init__(self, *args, **kwargs): self.disparity = kwargs.pop("disparity", 0.0) Camera.__init__(self, *args, **kwargs) self.disparity = self.ds.arr(self.disparity, units="code_length") self.disparity_s = self.ds.arr(0.0, units="code_length") if self.resolution[0] / self.resolution[1] != 2: mylog.info("Warning: It's recommended to set the aspect ratio to be 2:1") self.resolution = np.asarray(self.resolution) + 2 if self.disparity <= 0.0: self.disparity = self.width[0] / 1000.0 mylog.info( "Warning: Invalid value of disparity; now reset it to %f", self.disparity, ) def get_sampler_args(self, image): px = np.linspace(-np.pi, np.pi, self.resolution[0], endpoint=True)[:, None] py = np.linspace(-np.pi / 2.0, np.pi / 2.0, self.resolution[1], endpoint=True)[ None, : ] vectors = np.zeros( (self.resolution[0], self.resolution[1], 3), dtype="float64", order="C" ) vectors[:, :, 0] = np.cos(px) * np.cos(py) vectors[:, :, 1] = np.sin(px) * np.cos(py) vectors[:, :, 2] = np.sin(py) vectors2 = np.zeros( (self.resolution[0], self.resolution[1], 3), dtype="float64", order="C" ) vectors2[:, :, 0] = -np.sin(px) * np.ones((1, self.resolution[1])) vectors2[:, :, 1] = np.cos(px) * np.ones((1, self.resolution[1])) vectors2[:, :, 2] = 0 positions = self.center + vectors2 * self.disparity_s vectors = vectors * self.width[0] R1 = get_rotation_matrix(0.5 * np.pi, [1, 0, 0]) R2 = get_rotation_matrix(0.5 * np.pi, [0, 0, 1]) uv = np.dot(R1, self.orienter.unit_vectors) uv = np.dot(R2, uv) vectors.reshape((self.resolution[0] * self.resolution[1], 3)) vectors = np.dot(vectors, uv) vectors.reshape((self.resolution[0], self.resolution[1], 3)) dummy = np.ones(3, dtype="float64") image.shape = (self.resolution[0] * self.resolution[1], 1, 4) vectors.shape = (self.resolution[0] * self.resolution[1], 1, 3) positions.shape = (self.resolution[0] * self.resolution[1], 1, 3) args = ( positions, vectors, self.back_center, (0.0, 1.0, 0.0, 1.0), image, dummy, dummy, np.zeros(3, dtype="float64"), "KDTree", self.transfer_function, self.sub_samples, ) kwargs = {"lens_type": "stereo-spherical"} return args, kwargs def snapshot( self, fn=None, clip_ratio=None, double_check=False, num_threads=0, transparent=False, ): if num_threads is None: num_threads = get_num_threads() self.disparity_s = self.disparity image1 = self.new_image() args1, kwargs1 = self.get_sampler_args(image1) sampler1 = self.get_sampler(args1, kwargs1) self.initialize_source() image1 = self._render(double_check, num_threads, image1, sampler1, "(Left) ") self.disparity_s = -self.disparity image2 = self.new_image() args2, kwargs2 = self.get_sampler_args(image2) sampler2 = self.get_sampler(args2, kwargs2) self.initialize_source() image2 = self._render(double_check, num_threads, image2, sampler2, "(Right)") image = np.hstack([image1, image2]) image = self.volume.reduce_tree_images(image, self.center) image = ImageArray(image, info=self.get_information()) self.save_image(image, fn=fn, clip_ratio=clip_ratio, transparent=transparent) return image def _render(self, double_check, num_threads, image, sampler, msg): ncells = sum(b.source_mask.size for b in self.volume.bricks) pbar = get_pbar("Ray casting " + msg, ncells) total_cells = 0 if double_check: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError for brick in self.volume.traverse(self.front_center): sampler(brick, num_threads=num_threads) total_cells += brick.source_mask.size pbar.update(total_cells) pbar.finish() image = sampler.aimage.copy() image.shape = self.resolution[0], self.resolution[1], 4 if not self.transfer_function.grey_opacity: image[:, :, 3] = 1.0 image = image[1:-1, 1:-1, :] return image data_object_registry["stereospherical_camera"] = StereoSphericalCamera # replaced in volume_rendering API by the function of the same name in # yt/visualization/volume_rendering/off_axis_projection def off_axis_projection( ds, center, normal_vector, width, resolution, field, weight=None, volume=None, no_ghost=False, interpolated=False, north_vector=None, method="integrate", ): r"""Project through a dataset, off-axis, and return the image plane. This function will accept the necessary items to integrate through a volume at an arbitrary angle and return the integrated field of view to the user. Note that if a weight is supplied, it will multiply the pre-interpolated values together, then create cell-centered values, then interpolate within the cell to conduct the integration. Parameters ---------- ds : ~yt.data_objects.static_output.Dataset This is the dataset to volume render. center : array_like The current 'center' of the view port -- the focal point for the camera. normal_vector : array_like The vector between the camera position and the center. width : float or list of floats The current width of the image. If a single float, the volume is cubical, but if not, it is left/right, top/bottom, front/back resolution : int or list of ints The number of pixels in each direction. field : string The field to project through the volume weight : optional, default None If supplied, the field will be pre-multiplied by this, then divided by the integrated value of this field. This returns an average rather than a sum. volume : `yt.extensions.volume_rendering.AMRKDTree`, optional The volume to ray cast through. Can be specified for finer-grained control, but otherwise will be automatically generated. no_ghost: bool, optional Optimization option. If True, homogenized bricks will extrapolate out from grid instead of interpolating from ghost zones that have to first be calculated. This can lead to large speed improvements, but at a loss of accuracy/smoothness in resulting image. The effects are less notable when the transfer function is smooth and broad. Default: True interpolated : optional, default False If True, the data is first interpolated to vertex-centered data, then tri-linearly interpolated along the ray. Not suggested for quantitative studies. method : string The method of projection. Valid methods are: "integrate" with no weight_field specified : integrate the requested field along the line of sight. "integrate" with a weight_field specified : weight the requested field by the weighting field and integrate along the line of sight. "sum" : This method is the same as integrate, except that it does not multiply by a path length when performing the integration, and is just a straight summation of the field along the given axis. WARNING: This should only be used for uniform resolution grid datasets, as other datasets may result in unphysical images. Returns ------- image : array An (N,N) array of the final integrated values, in float64 form. Examples -------- >>> image = off_axis_projection( ... ds, [0.5, 0.5, 0.5], [0.2, 0.3, 0.4], 0.2, N, "temperature", "density" ... ) >>> write_image(np.log10(image), "offaxis.png") """ projcam = ProjectionCamera( center, normal_vector, width, resolution, field, weight=weight, ds=ds, volume=volume, no_ghost=no_ghost, interpolated=interpolated, north_vector=north_vector, method=method, ) image = projcam.snapshot() return image[:, :] yt-project-yt-f043ac8/yt/visualization/volume_rendering/render_source.py000066400000000000000000001451551510711153200267770ustar00rootroot00000000000000import abc import warnings from functools import wraps from types import ModuleType from typing import Literal import numpy as np from yt.config import ytcfg from yt.data_objects.image_array import ImageArray from yt.funcs import ensure_numpy_array, is_sequence, mylog from yt.geometry.grid_geometry_handler import GridIndex from yt.geometry.oct_geometry_handler import OctreeIndex from yt.utilities.amr_kdtree.api import AMRKDTree from yt.utilities.configure import YTConfig, configuration_callbacks from yt.utilities.lib.bounding_volume_hierarchy import BVH from yt.utilities.lib.misc_utilities import zlines, zpoints from yt.utilities.lib.octree_raytracing import OctreeRayTracing from yt.utilities.lib.partitioned_grid import PartitionedGrid from yt.utilities.on_demand_imports import NotAModule from yt.utilities.parallel_tools.parallel_analysis_interface import ( ParallelAnalysisInterface, ) from yt.visualization.image_writer import apply_colormap from .transfer_function_helper import TransferFunctionHelper from .transfer_functions import ( ColorTransferFunction, ProjectionTransferFunction, TransferFunction, ) from .utils import ( data_source_or_all, get_corners, new_interpolated_projection_sampler, new_mesh_sampler, new_projection_sampler, new_volume_render_sampler, ) from .zbuffer_array import ZBuffer OptionalModule = ModuleType | NotAModule mesh_traversal: OptionalModule = NotAModule("pyembree") mesh_construction: OptionalModule = NotAModule("pyembree") def set_raytracing_engine( engine: Literal["yt", "embree"], ) -> None: """ Safely switch raytracing engines at runtime. Parameters ---------- engine: 'yt' or 'embree' - 'yt' selects the default engine. - 'embree' requires extra installation steps, see https://yt-project.org/doc/visualizing/unstructured_mesh_rendering.html?highlight=pyembree#optional-embree-installation Raises ------ UserWarning Raised if the required engine is not available. In this case, the default engine is restored. """ from yt.config import ytcfg global mesh_traversal, mesh_construction if engine == "embree": try: from yt.utilities.lib.embree_mesh import ( # type: ignore mesh_construction, mesh_traversal, ) except (ImportError, ValueError) as exc: # Catch ValueError in case size of objects in Cython change warnings.warn( "Failed to switch to embree raytracing engine. " f"The following error was raised:\n{exc}", stacklevel=2, ) mesh_traversal = NotAModule("pyembree") mesh_construction = NotAModule("pyembree") ytcfg["yt", "ray_tracing_engine"] = "yt" else: ytcfg["yt", "ray_tracing_engine"] = "embree" else: mesh_traversal = NotAModule("pyembree") mesh_construction = NotAModule("pyembree") ytcfg["yt", "ray_tracing_engine"] = "yt" def _init_raytracing_engine(ytcfg: YTConfig) -> None: # validate option from configuration file or fall back to default engine set_raytracing_engine(engine=ytcfg["yt", "ray_tracing_engine"]) configuration_callbacks.append(_init_raytracing_engine) def invalidate_volume(f): @wraps(f) def wrapper(*args, **kwargs): ret = f(*args, **kwargs) obj = args[0] if isinstance(obj._transfer_function, ProjectionTransferFunction): obj.sampler_type = "projection" obj._log_field = False obj._use_ghost_zones = False del obj.volume obj._volume_valid = False return ret return wrapper def validate_volume(f): @wraps(f) def wrapper(*args, **kwargs): obj = args[0] fields = [obj.field] log_fields = [obj.log_field] if obj.weight_field is not None: fields.append(obj.weight_field) log_fields.append(obj.log_field) if not obj._volume_valid: obj.volume.set_fields( fields, log_fields, no_ghost=(not obj.use_ghost_zones) ) obj._volume_valid = True return f(*args, **kwargs) return wrapper class RenderSource(ParallelAnalysisInterface, abc.ABC): """Base Class for Render Sources. Will be inherited for volumes, streamlines, etc. """ volume_method: str | None = None def __init__(self): super().__init__() self.opaque = False self.zbuffer = None @abc.abstractmethod def render(self, camera, zbuffer=None): pass @abc.abstractmethod def _validate(self): pass class OpaqueSource(RenderSource): """A base class for opaque render sources. Will be inherited from for LineSources, BoxSources, etc. """ def __init__(self): super().__init__() self.opaque = True def set_zbuffer(self, zbuffer): self.zbuffer = zbuffer def create_volume_source(data_source, field): data_source = data_source_or_all(data_source) ds = data_source.ds index_class = ds.index.__class__ if issubclass(index_class, GridIndex): return KDTreeVolumeSource(data_source, field) elif issubclass(index_class, OctreeIndex): return OctreeVolumeSource(data_source, field) else: raise NotImplementedError class VolumeSource(RenderSource, abc.ABC): """A class for rendering data from a volumetric data source Examples of such sources include a sphere, cylinder, or the entire computational domain. A :class:`VolumeSource` provides the framework to decompose an arbitrary yt data source into bricks that can be traversed and volume rendered. Parameters ---------- data_source: :class:`AMR3DData` or :class:`Dataset`, optional This is the source to be rendered, which can be any arbitrary yt data object or dataset. field : string The name of the field to be rendered. Examples -------- The easiest way to make a VolumeSource is to use the volume_render function, so that the VolumeSource gets created automatically. This example shows how to do this and then access the resulting source: >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> volume_source = sc.get_source(0) You can also create VolumeSource instances by hand and add them to Scenes. This example manually creates a VolumeSource, adds it to a scene, sets the camera, and renders an image. >>> import yt >>> from yt.visualization.volume_rendering.api import ( ... Camera, Scene, create_volume_source) >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> source = create_volume_source(ds.all_data(), "density") >>> sc.add_source(source) >>> sc.add_camera() >>> im = sc.render() """ _image = None data_source = None def __init__(self, data_source, field): r"""Initialize a new volumetric source for rendering.""" super().__init__() self.data_source = data_source_or_all(data_source) field = self.data_source._determine_fields(field)[0] self.current_image = None self.check_nans = False self.num_threads = 0 self.num_samples = 10 self.sampler_type = "volume-render" self._volume_valid = False # these are caches for properties, defined below self._volume = None self._transfer_function = None self._field = field self._log_field = self.data_source.ds.field_info[field].take_log self._use_ghost_zones = False self._weight_field = None self.tfh = TransferFunctionHelper(self.data_source.pf) self.tfh.set_field(self.field) @property def transfer_function(self): """The transfer function associated with this VolumeSource""" if self._transfer_function is not None: return self._transfer_function if self.tfh.tf is not None: self._transfer_function = self.tfh.tf return self._transfer_function mylog.info("Creating transfer function") self.tfh.set_field(self.field) self.tfh.set_log(self.log_field) self.tfh.build_transfer_function() self.tfh.setup_default() self._transfer_function = self.tfh.tf return self._transfer_function @transfer_function.setter def transfer_function(self, value): self.tfh.tf = None valid_types = ( TransferFunction, ColorTransferFunction, ProjectionTransferFunction, type(None), ) if not isinstance(value, valid_types): raise RuntimeError( "transfer_function not a valid type, " f"received object of type {type(value)}" ) if isinstance(value, ProjectionTransferFunction): self.sampler_type = "projection" if self._volume is not None: fields = [self.field] if self.weight_field is not None: fields.append(self.weight_field) self._volume_valid = False self._transfer_function = value @property def volume(self): """The abstract volume associated with this VolumeSource This object does the heavy lifting to access data in an efficient manner using a KDTree """ return self._get_volume() @volume.setter def volume(self, value): assert isinstance(value, AMRKDTree) del self._volume self._field = value.fields self._log_field = value.log_fields self._volume = value assert self._volume_valid @volume.deleter def volume(self): del self._volume self._volume = None @property def field(self): """The field to be rendered""" return self._field @field.setter @invalidate_volume def field(self, value): field = self.data_source._determine_fields(value) if len(field) > 1: raise RuntimeError( "VolumeSource.field can only be a single field but received " f"multiple fields: {field}" ) field = field[0] if self._field != field: log_field = self.data_source.ds.field_info[field].take_log self.tfh.bounds = None else: log_field = self._log_field self._log_field = log_field self._field = value self.transfer_function = None self.tfh.set_field(value) self.tfh.set_log(log_field) @property def log_field(self): """Whether or not the field rendering is computed in log space""" return self._log_field @log_field.setter @invalidate_volume def log_field(self, value): self.transfer_function = None self.tfh.set_log(value) self._log_field = value @property def use_ghost_zones(self): """Whether or not ghost zones are used to estimate vertex-centered data values at grid boundaries""" return self._use_ghost_zones @use_ghost_zones.setter @invalidate_volume def use_ghost_zones(self, value): self._use_ghost_zones = value @property def weight_field(self): """The weight field for the rendering Currently this is only used for off-axis projections. """ return self._weight_field @weight_field.setter @invalidate_volume def weight_field(self, value): self._weight_field = value def set_transfer_function(self, transfer_function): """Set transfer function for this source""" self.transfer_function = transfer_function return self def _validate(self): """Make sure that all dependencies have been met""" if self.data_source is None: raise RuntimeError("Data source not initialized") def set_volume(self, volume): """Associates an AMRKDTree with the VolumeSource""" self.volume = volume return self def set_field(self, field): """Set the source's field to render Parameters ---------- field: field name The field to render """ self.field = field return self def set_log(self, log_field): """Set whether the rendering of the source's field is done in log space Generally volume renderings of data whose values span a large dynamic range should be done on log space and volume renderings of data with small dynamic range should be done in linear space. Parameters ---------- log_field: boolean If True, the volume rendering will be done in log space, and if False will be done in linear space. """ self.log_field = log_field return self def set_weight_field(self, weight_field): """Set the source's weight field .. note:: This is currently only used for renderings using the ProjectionTransferFunction Parameters ---------- weight_field: field name The weight field to use in the rendering """ self.weight_field = weight_field return self def set_use_ghost_zones(self, use_ghost_zones): """Set whether or not interpolation at grid edges uses ghost zones Parameters ---------- use_ghost_zones: boolean If True, the AMRKDTree estimates vertex centered data using ghost zones, which can eliminate seams in the resulting volume rendering. Defaults to False for performance reasons. """ self.use_ghost_zones = use_ghost_zones return self def set_sampler(self, camera, interpolated=True): """Sets a volume render sampler The type of sampler is determined based on the ``sampler_type`` attribute of the VolumeSource. Currently the ``volume_render`` and ``projection`` sampler types are supported. The 'interpolated' argument is only meaningful for projections. If True, the data is first interpolated to the cell vertices, and then tri-linearly interpolated to the ray sampling positions. If False, then the cell-centered data is simply accumulated along the ray. Interpolation is always performed for volume renderings. """ if self.sampler_type == "volume-render": sampler = new_volume_render_sampler(camera, self) elif self.sampler_type == "projection" and interpolated: sampler = new_interpolated_projection_sampler(camera, self) elif self.sampler_type == "projection": sampler = new_projection_sampler(camera, self) else: NotImplementedError(f"{self.sampler_type} not implemented yet") self.sampler = sampler assert self.sampler is not None @abc.abstractmethod def _get_volume(self): """The abstract volume associated with this VolumeSource This object does the heavy lifting to access data in an efficient manner using a KDTree """ pass @abc.abstractmethod @validate_volume def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` instance A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` instance # noqa: E501 A zbuffer array. This is used for opaque sources to determine the z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` instance containing the rendered image. """ pass def finalize_image(self, camera, image): """Parallel reduce the image. Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` instance The camera used to produce the volume rendering image. image: :class:`yt.data_objects.image_array.ImageArray` instance A reference to an image to fill """ image.shape = camera.resolution[0], camera.resolution[1], 4 # If the call is from VR, the image is rotated by 180 to get correct # up direction if not self.transfer_function.grey_opacity: image[:, :, 3] = 1 return image def __repr__(self): disp = f":{str(self.data_source)} " disp += f"transfer_function:{str(self._transfer_function)}" return disp class KDTreeVolumeSource(VolumeSource): volume_method = "KDTree" def _get_volume(self): """The abstract volume associated with this VolumeSource This object does the heavy lifting to access data in an efficient manner using a KDTree """ if self._volume is None: mylog.info("Creating volume") volume = AMRKDTree(self.data_source.ds, data_source=self.data_source) self._volume = volume return self._volume @validate_volume def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` A zbuffer array. This is used for opaque sources to determine the z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` containing the rendered image. """ self.zbuffer = zbuffer self.set_sampler(camera) assert self.sampler is not None mylog.debug("Casting rays") total_cells = 0 if self.check_nans: for brick in self.volume.bricks: for data in brick.my_data: if np.any(np.isnan(data)): raise RuntimeError for brick in self.volume.traverse(camera.lens.viewpoint): mylog.debug("Using sampler %s", self.sampler) self.sampler(brick, num_threads=self.num_threads) total_cells += np.prod(brick.my_data[0].shape) mylog.debug("Done casting rays") self.current_image = self.finalize_image(camera, self.sampler.aimage) if zbuffer is None: self.zbuffer = ZBuffer( self.current_image, np.full(self.current_image.shape[:2], np.inf) ) return self.current_image def finalize_image(self, camera, image): if self._volume is not None: image = self.volume.reduce_tree_images( image, camera.lens.viewpoint, use_opacity=self.transfer_function.grey_opacity, ) return super().finalize_image(camera, image) class OctreeVolumeSource(VolumeSource): volume_method = "Octree" def __init__(self, *args, **kwa): super().__init__(*args, **kwa) self.set_use_ghost_zones(True) def _get_volume(self): """The abstract volume associated with this VolumeSource This object does the heavy lifting to access data in an efficient manner using an octree. """ if self._volume is None: mylog.info("Creating volume") volume = OctreeRayTracing(self.data_source) self._volume = volume return self._volume @validate_volume def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` instance A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` instance # noqa: E501 A zbuffer array. This is used for opaque sources to determine the z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` instance containing the rendered image. """ self.zbuffer = zbuffer self.set_sampler(camera) if self.sampler is None: raise RuntimeError( "No sampler set. This is likely a bug as it should never happen." ) data = self.data_source dx = data["dx"].to_value("unitary")[:, None] xyz = np.stack([data[_].to_value("unitary") for _ in "xyz"], axis=-1) LE = xyz - dx / 2 RE = xyz + dx / 2 mylog.debug("Gathering data") dt = np.stack(list(self.volume.data) + [*LE.T, *RE.T], axis=-1).reshape( 1, len(dx), 14, 1 ) mask = np.full(dt.shape[1:], 1, dtype=np.uint8) dims = np.array([1, 1, 1], dtype="int64") pg = PartitionedGrid(0, dt, mask, LE.flatten(), RE.flatten(), dims, n_fields=1) mylog.debug("Casting rays") self.sampler(pg, oct=self.volume.octree) mylog.debug("Done casting rays") self.current_image = self.finalize_image(camera, self.sampler.aimage) if zbuffer is None: self.zbuffer = ZBuffer( self.current_image, np.full(self.current_image.shape[:2], np.inf) ) return self.current_image class MeshSource(OpaqueSource): """A source for unstructured mesh data. This functionality requires the embree ray-tracing engine and the associated pyembree python bindings to be installed in order to function. A :class:`MeshSource` provides the framework to volume render unstructured mesh data. Parameters ---------- data_source: :class:`AMR3DData` or :class:`Dataset`, optional This is the source to be rendered, which can be any arbitrary yt data object or dataset. field : string The name of the field to be rendered. Examples -------- >>> source = MeshSource(ds, ("connect1", "convected")) """ _image = None data_source = None def __init__(self, data_source, field): r"""Initialize a new unstructured mesh source for rendering.""" super().__init__() self.data_source = data_source_or_all(data_source) field = self.data_source._determine_fields(field)[0] self.field = field self.volume = None self.current_image = None self.engine = ytcfg.get("yt", "ray_tracing_engine") # default color map self._cmap = ytcfg.get("yt", "default_colormap") self._color_bounds = None # default mesh annotation options self._annotate_mesh = False self._mesh_line_color = None self._mesh_line_alpha = 1.0 # Error checking assert self.field is not None assert self.data_source is not None if self.field[0] == "all": raise NotImplementedError( "Mesh unions are not implemented for 3D rendering" ) if self.engine == "embree": self.volume = mesh_traversal.YTEmbreeScene() self.build_volume_embree() elif self.engine == "yt": self.build_volume_bvh() else: raise NotImplementedError( "Invalid ray-tracing engine selected. Choices are 'embree' and 'yt'." ) @property def cmap(self): """ This is the name of the colormap that will be used when rendering this MeshSource object. Should be a string, like 'cmyt.arbre', or 'cmyt.dusk'. """ return self._cmap @cmap.setter def cmap(self, cmap_name): self._cmap = cmap_name if hasattr(self, "data"): self.current_image = self.apply_colormap() @property def color_bounds(self): """ These are the bounds that will be used with the colormap to the display the rendered image. Should be a (vmin, vmax) tuple, like (0.0, 2.0). If None, the bounds will be automatically inferred from the max and min of the rendered data. """ return self._color_bounds @color_bounds.setter def color_bounds(self, bounds): self._color_bounds = bounds if hasattr(self, "data"): self.current_image = self.apply_colormap() def _validate(self): """Make sure that all dependencies have been met""" if self.data_source is None: raise RuntimeError("Data source not initialized.") if self.volume is None: raise RuntimeError("Volume not initialized.") def build_volume_embree(self): """ This constructs the mesh that will be ray-traced by pyembree. """ ftype, fname = self.field mesh_id = int(ftype[-1]) - 1 index = self.data_source.ds.index offset = index.meshes[mesh_id]._index_offset field_data = self.data_source[self.field].d # strip units vertices = index.meshes[mesh_id].connectivity_coords indices = index.meshes[mesh_id].connectivity_indices - offset # if this is an element field, promote to 2D here if len(field_data.shape) == 1: field_data = np.expand_dims(field_data, 1) # Here, we decide whether to render based on high-order or # low-order geometry. Right now, high-order geometry is only # implemented for 20-point hexes. if indices.shape[1] == 20 or indices.shape[1] == 10: self.mesh = mesh_construction.QuadraticElementMesh( self.volume, vertices, indices, field_data ) else: # if this is another type of higher-order element, we demote # to 1st order here, for now. if indices.shape[1] == 27: # hexahedral mylog.warning("27-node hexes not yet supported, dropping to 1st order.") field_data = field_data[:, 0:8] indices = indices[:, 0:8] self.mesh = mesh_construction.LinearElementMesh( self.volume, vertices, indices, field_data ) def build_volume_bvh(self): """ This constructs the mesh that will be ray-traced. """ ftype, fname = self.field mesh_id = int(ftype[-1]) - 1 index = self.data_source.ds.index offset = index.meshes[mesh_id]._index_offset field_data = self.data_source[self.field].d # strip units vertices = index.meshes[mesh_id].connectivity_coords indices = index.meshes[mesh_id].connectivity_indices - offset # if this is an element field, promote to 2D here if len(field_data.shape) == 1: field_data = np.expand_dims(field_data, 1) # Here, we decide whether to render based on high-order or # low-order geometry. if indices.shape[1] == 27: # hexahedral mylog.warning("27-node hexes not yet supported, dropping to 1st order.") field_data = field_data[:, 0:8] indices = indices[:, 0:8] self.volume = BVH(vertices, indices, field_data) def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` A zbuffer array. This is used for opaque sources to determine the z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` containing the rendered image. """ shape = (camera.resolution[0], camera.resolution[1], 4) if zbuffer is None: empty = np.empty(shape, dtype="float64") z = np.empty(empty.shape[:2], dtype="float64") empty[:] = 0.0 z[:] = np.inf zbuffer = ZBuffer(empty, z) elif zbuffer.rgba.shape != shape: zbuffer = ZBuffer(zbuffer.rgba.reshape(shape), zbuffer.z.reshape(shape[:2])) self.zbuffer = zbuffer self.sampler = new_mesh_sampler(camera, self, engine=self.engine) mylog.debug("Casting rays") self.sampler(self.volume) mylog.debug("Done casting rays") self.finalize_image(camera) self.current_image = self.apply_colormap() zbuffer += ZBuffer(self.current_image.astype("float64"), self.sampler.azbuffer) zbuffer.rgba = ImageArray(zbuffer.rgba) self.zbuffer = zbuffer self.current_image = self.zbuffer.rgba if self._annotate_mesh: self.current_image = self.annotate_mesh_lines( self._mesh_line_color, self._mesh_line_alpha ) return self.current_image def finalize_image(self, camera): sam = self.sampler # reshape data Nx = camera.resolution[0] Ny = camera.resolution[1] self.data = sam.aimage[:, :, 0].reshape(Nx, Ny) def annotate_mesh_lines(self, color=None, alpha=1.0): r""" Modifies this MeshSource by drawing the mesh lines. This modifies the current image by drawing the element boundaries and returns the modified image. Parameters ---------- color: array_like of shape (4,), optional The RGBA value to use to draw the mesh lines. Default is black. alpha : float, optional The opacity of the mesh lines. Default is 255 (solid). """ self.annotate_mesh = True self._mesh_line_color = color self._mesh_line_alpha = alpha if color is None: color = np.array([0, 0, 0, alpha]) locs = (self.sampler.amesh_lines == 1,) self.current_image[:, :, 0][locs] = color[0] self.current_image[:, :, 1][locs] = color[1] self.current_image[:, :, 2][locs] = color[2] self.current_image[:, :, 3][locs] = color[3] return self.current_image def apply_colormap(self): """ Applies a colormap to the current image without re-rendering. Returns ------- current_image : A new image with the specified color scale applied to the underlying data. """ image = ( apply_colormap( self.data, color_bounds=self._color_bounds, cmap_name=self._cmap ) / 255.0 ) alpha = image[:, :, 3] alpha[self.sampler.aimage_used == -1] = 0.0 image[:, :, 3] = alpha return image def __repr__(self): disp = f":{str(self.data_source)} " return disp class PointSource(OpaqueSource): r"""A rendering source of opaque points in the scene. This class provides a mechanism for adding points to a scene; these points will be opaque, and can also be colored. Parameters ---------- positions: array_like of shape (N, 3) The positions of points to be added to the scene. If specified with no units, the positions will be assumed to be in code units. colors : array_like of shape (N, 4), optional The colors of the points, including an alpha channel, in floating point running from 0..1. color_stride : int, optional The stride with which to access the colors when putting them on the scene. radii : array_like of shape (N), optional The radii of the points in the final image, in pixels (int) Examples -------- This example creates a volume rendering and adds 1000 random points to the image: >>> import yt >>> import numpy as np >>> from yt.visualization.volume_rendering.api import PointSource >>> from yt.units import kpc >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> npoints = 1000 >>> vertices = np.random.random([npoints, 3]) * 1000 * kpc >>> colors = np.random.random([npoints, 4]) >>> colors[:, 3] = 1.0 >>> points = PointSource(vertices, colors=colors) >>> sc.add_source(points) >>> im = sc.render() """ _image = None data_source = None def __init__(self, positions, colors=None, color_stride=1, radii=None): assert positions.ndim == 2 and positions.shape[1] == 3 if colors is not None: assert colors.ndim == 2 and colors.shape[1] == 4 assert colors.shape[0] == positions.shape[0] if not is_sequence(radii): if radii is not None: # broadcast the value radii = radii * np.ones(positions.shape[0], dtype="int64") else: # default radii to 0 pixels (i.e. point is 1 pixel wide) radii = np.zeros(positions.shape[0], dtype="int64") else: assert radii.ndim == 1 assert radii.shape[0] == positions.shape[0] self.positions = positions # If colors aren't individually set, make black with full opacity if colors is None: colors = np.ones((len(positions), 4)) self.colors = colors self.color_stride = color_stride self.radii = radii def _validate(self): pass def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` A zbuffer array. This is used for opaque sources to determine the z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` containing the rendered image. """ vertices = self.positions if zbuffer is None: empty = camera.lens.new_image(camera) z = np.empty(empty.shape[:2], dtype="float64") empty[:] = 0.0 z[:] = np.inf zbuffer = ZBuffer(empty, z) else: empty = zbuffer.rgba z = zbuffer.z # DRAW SOME POINTS camera.lens.setup_box_properties(camera) px, py, dz = camera.lens.project_to_plane(camera, vertices) zpoints(empty, z, px, py, dz, self.colors, self.radii, self.color_stride) self.zbuffer = zbuffer return zbuffer def __repr__(self): disp = "" return disp class LineSource(OpaqueSource): r"""A render source for a sequence of opaque line segments. This class provides a mechanism for adding lines to a scene; these points will be opaque, and can also be colored. .. note:: If adding a LineSource to your rendering causes the image to appear blank or fades a VolumeSource, try lowering the values specified in the alpha channel of the ``colors`` array. Parameters ---------- positions: array_like of shape (N, 2, 3) The positions of the starting and stopping points for each line. For example,positions[0][0] and positions[0][1] would give the (x, y, z) coordinates of the beginning and end points of the first line, respectively. If specified with no units, assumed to be in code units. colors : array_like of shape (N, 4), optional The colors of the points, including an alpha channel, in floating point running from 0..1. The four channels correspond to r, g, b, and alpha values. Note that they correspond to the line segment succeeding each point; this means that strictly speaking they need only be (N-1) in length. color_stride : int, optional The stride with which to access the colors when putting them on the scene. Examples -------- This example creates a volume rendering and then adds some random lines to the image: >>> import yt >>> import numpy as np >>> from yt.visualization.volume_rendering.api import LineSource >>> from yt.units import kpc >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> nlines = 4 >>> vertices = np.random.random([nlines, 2, 3]) * 600 * kpc >>> colors = np.random.random([nlines, 4]) >>> colors[:, 3] = 1.0 >>> lines = LineSource(vertices, colors) >>> sc.add_source(lines) >>> im = sc.render() """ _image = None data_source = None def __init__(self, positions, colors=None, color_stride=1): super().__init__() assert positions.ndim == 3 assert positions.shape[1] == 2 assert positions.shape[2] == 3 if colors is not None: assert colors.ndim == 2 assert colors.shape[1] == 4 # convert the positions to the shape expected by zlines, below N = positions.shape[0] self.positions = positions.reshape((2 * N, 3)) # If colors aren't individually set, make black with full opacity if colors is None: colors = np.ones((len(positions), 4)) self.colors = colors self.color_stride = color_stride def _validate(self): pass def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` containing the rendered image. """ vertices = self.positions if zbuffer is None: empty = camera.lens.new_image(camera) z = np.empty(empty.shape[:2], dtype="float64") empty[:] = 0.0 z[:] = np.inf zbuffer = ZBuffer(empty, z) else: empty = zbuffer.rgba z = zbuffer.z # DRAW SOME LINES camera.lens.setup_box_properties(camera) px, py, dz = camera.lens.project_to_plane(camera, vertices) px = px.astype("int64") py = py.astype("int64") if len(px.shape) == 1: zlines( empty, z, px, py, dz, self.colors.astype("float64"), self.color_stride ) else: # For stereo-lens, two sets of pos for each eye are contained # in px...pz zlines( empty, z, px[0, :], py[0, :], dz[0, :], self.colors.astype("float64"), self.color_stride, ) zlines( empty, z, px[1, :], py[1, :], dz[1, :], self.colors.astype("float64"), self.color_stride, ) self.zbuffer = zbuffer return zbuffer def __repr__(self): disp = "" return disp class BoxSource(LineSource): r"""A render source for a box drawn with line segments. This render source will draw a box, with transparent faces, in data space coordinates. This is useful for annotations. Parameters ---------- left_edge: array-like of shape (3,), float The left edge coordinates of the box. right_edge : array-like of shape (3,), float The right edge coordinates of the box. color : array-like of shape (4,), float, optional The colors (including alpha) to use for the lines. Default is black with an alpha of 1.0. Examples -------- This example shows how to use BoxSource to add an outline of the domain boundaries to a volume rendering. >>> import yt >>> from yt.visualization.volume_rendering.api import BoxSource >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> box_source = BoxSource( ... ds.domain_left_edge, ds.domain_right_edge, [1.0, 1.0, 1.0, 1.0] ... ) >>> sc.add_source(box_source) >>> im = sc.render() """ def __init__(self, left_edge, right_edge, color=None): assert left_edge.shape == (3,) assert right_edge.shape == (3,) if color is None: color = np.array([1.0, 1.0, 1.0, 1.0]) color = ensure_numpy_array(color) color.shape = (1, 4) corners = get_corners(left_edge.copy(), right_edge.copy()) order = [0, 1, 1, 2, 2, 3, 3, 0] order += [4, 5, 5, 6, 6, 7, 7, 4] order += [0, 4, 1, 5, 2, 6, 3, 7] vertices = np.empty([24, 3]) for i in range(3): vertices[:, i] = corners[order, i, ...].ravel(order="F") vertices = vertices.reshape((12, 2, 3)) super().__init__(vertices, color, color_stride=24) def _validate(self): pass class GridSource(LineSource): r"""A render source for drawing grids in a scene. This render source will draw blocks that are within a given data source, by default coloring them by their level of resolution. Parameters ---------- data_source: :class:`~yt.data_objects.api.DataContainer` The data container that will be used to identify grids to draw. alpha : float The opacity of the grids to draw. cmap : color map name The color map to use to map resolution levels to color. min_level : int, optional Minimum level to draw max_level : int, optional Maximum level to draw Examples -------- This example makes a volume rendering and adds outlines of all the AMR grids in the simulation: >>> import yt >>> from yt.visualization.volume_rendering.api import GridSource >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> grid_source = GridSource(ds.all_data(), alpha=1.0) >>> sc.add_source(grid_source) >>> im = sc.render() This example does the same thing, except it only draws the grids that are inside a sphere of radius (0.1, "unitary") located at the domain center: >>> import yt >>> from yt.visualization.volume_rendering.api import GridSource >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> dd = ds.sphere("c", (0.1, "unitary")) >>> grid_source = GridSource(dd, alpha=1.0) >>> sc.add_source(grid_source) >>> im = sc.render() """ def __init__( self, data_source, alpha=0.3, cmap=None, min_level=None, max_level=None ): self.data_source = data_source_or_all(data_source) corners = [] levels = [] for block, _mask in self.data_source.blocks: block_corners = np.array( [ [block.LeftEdge[0], block.LeftEdge[1], block.LeftEdge[2]], [block.RightEdge[0], block.LeftEdge[1], block.LeftEdge[2]], [block.RightEdge[0], block.RightEdge[1], block.LeftEdge[2]], [block.LeftEdge[0], block.RightEdge[1], block.LeftEdge[2]], [block.LeftEdge[0], block.LeftEdge[1], block.RightEdge[2]], [block.RightEdge[0], block.LeftEdge[1], block.RightEdge[2]], [block.RightEdge[0], block.RightEdge[1], block.RightEdge[2]], [block.LeftEdge[0], block.RightEdge[1], block.RightEdge[2]], ], dtype="float64", ) corners.append(block_corners) levels.append(block.Level) corners = np.dstack(corners) levels = np.array(levels) if cmap is None: cmap = ytcfg.get("yt", "default_colormap") if max_level is not None: subset = levels <= max_level levels = levels[subset] corners = corners[:, :, subset] if min_level is not None: subset = levels >= min_level levels = levels[subset] corners = corners[:, :, subset] colors = ( apply_colormap( levels * 1.0, color_bounds=[0, self.data_source.ds.index.max_level], cmap_name=cmap, )[0, :, :] / 255.0 ) colors[:, 3] = alpha order = [0, 1, 1, 2, 2, 3, 3, 0] order += [4, 5, 5, 6, 6, 7, 7, 4] order += [0, 4, 1, 5, 2, 6, 3, 7] vertices = np.empty([corners.shape[2] * 2 * 12, 3]) for i in range(3): vertices[:, i] = corners[order, i, ...].ravel(order="F") vertices = vertices.reshape((corners.shape[2] * 12, 2, 3)) super().__init__(vertices, colors, color_stride=24) class CoordinateVectorSource(OpaqueSource): r"""Draw coordinate vectors on the scene. This will draw a set of coordinate vectors on the camera image. They will appear in the lower right of the image. Parameters ---------- colors: array-like of shape (3,4), optional The RGBA values to use to draw the x, y, and z vectors. The default is [[1, 0, 0, alpha], [0, 1, 0, alpha], [0, 0, 1, alpha]] where ``alpha`` is set by the parameter below. If ``colors`` is set then ``alpha`` is ignored. alpha : float, optional The opacity of the vectors. thickness : int, optional The line thickness Examples -------- >>> import yt >>> from yt.visualization.volume_rendering.api import \ ... CoordinateVectorSource >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> im, sc = yt.volume_render(ds) >>> coord_source = CoordinateVectorSource() >>> sc.add_source(coord_source) >>> im = sc.render() """ def __init__(self, colors=None, alpha=1.0, *, thickness=1): super().__init__() # If colors aren't individually set, make black with full opacity if colors is None: colors = np.zeros((3, 4)) colors[0, 0] = 1.0 # x is red colors[1, 1] = 1.0 # y is green colors[2, 2] = 1.0 # z is blue colors[:, 3] = alpha self.colors = colors self.thick = thickness def _validate(self): pass def render(self, camera, zbuffer=None): """Renders an image using the provided camera Parameters ---------- camera: :class:`yt.visualization.volume_rendering.camera.Camera` A volume rendering camera. Can be any type of camera. zbuffer: :class:`yt.visualization.volume_rendering.zbuffer_array.Zbuffer` A zbuffer array. This is used for opaque sources to determine the z position of the source relative to other sources. Only useful if you are manually calling render on multiple sources. Scene.render uses this internally. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` containing the rendered image. """ camera.lens.setup_box_properties(camera) center = camera.focus # Get positions at the focus positions = np.zeros([6, 3]) positions[:] = center # Create vectors in the x,y,z directions for i in range(3): positions[2 * i + 1, i] += camera.width.in_units("code_length").d[i] / 16.0 # Project to the image plane px, py, dz = camera.lens.project_to_plane(camera, positions) if len(px.shape) == 1: dpx = px[1::2] - px[::2] dpy = py[1::2] - py[::2] # Set the center of the coordinates to be in the lower left of the image lpx = camera.resolution[0] / 8 lpy = camera.resolution[1] - camera.resolution[1] / 8 # Upside-downsies # Offset the pixels according to the projections above px[::2] = lpx px[1::2] = lpx + dpx py[::2] = lpy py[1::2] = lpy + dpy dz[:] = 0.0 else: # For stereo-lens, two sets of pos for each eye are contained in px...pz dpx = px[:, 1::2] - px[:, ::2] dpy = py[:, 1::2] - py[:, ::2] lpx = camera.resolution[0] / 16 lpy = camera.resolution[1] - camera.resolution[1] / 8 # Upside-downsies # Offset the pixels according to the projections above px[:, ::2] = lpx px[:, 1::2] = lpx + dpx px[1, :] += camera.resolution[0] / 2 py[:, ::2] = lpy py[:, 1::2] = lpy + dpy dz[:, :] = 0.0 # Create a zbuffer if needed if zbuffer is None: empty = camera.lens.new_image(camera) z = np.empty(empty.shape[:2], dtype="float64") empty[:] = 0.0 z[:] = np.inf zbuffer = ZBuffer(empty, z) else: empty = zbuffer.rgba z = zbuffer.z # Draw the vectors px = px.astype("int64") py = py.astype("int64") if len(px.shape) == 1: zlines( empty, z, px, py, dz, self.colors.astype("float64"), thick=self.thick ) else: # For stereo-lens, two sets of pos for each eye are contained # in px...pz zlines( empty, z, px[0, :], py[0, :], dz[0, :], self.colors.astype("float64"), thick=self.thick, ) zlines( empty, z, px[1, :], py[1, :], dz[1, :], self.colors.astype("float64"), thick=self.thick, ) # Set the new zbuffer self.zbuffer = zbuffer return zbuffer def __repr__(self): disp = "" return disp yt-project-yt-f043ac8/yt/visualization/volume_rendering/scene.py000066400000000000000000001036601510711153200252300ustar00rootroot00000000000000import functools from collections import OrderedDict import numpy as np from yt._maintenance.ipython_compat import IS_IPYTHON from yt.config import ytcfg from yt.funcs import mylog from yt.units.dimensions import length # type: ignore from yt.units.unit_registry import UnitRegistry # type: ignore from yt.units.yt_array import YTArray, YTQuantity from yt.utilities.exceptions import YTNotInsideNotebook from yt.visualization._commons import get_canvas, validate_image_name from .camera import Camera from .render_source import ( BoxSource, CoordinateVectorSource, GridSource, LineSource, MeshSource, OpaqueSource, PointSource, RenderSource, VolumeSource, ) from .zbuffer_array import ZBuffer class Scene: """A virtual landscape for a volume rendering. The Scene class is meant to be the primary container for the new volume rendering framework. A single scene may contain several Camera and RenderSource instances, and is the primary driver behind creating a volume rendering. This sets up the basics needed to add sources and cameras. This does very little setup, and requires additional input to do anything useful. Examples -------- This example shows how to create an empty scene and add a VolumeSource and a Camera. >>> import yt >>> from yt.visualization.volume_rendering.api import ( ... Camera, Scene, create_volume_source) >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> source = create_volume_source(ds.all_data(), "density") >>> sc.add_source(source) >>> cam = sc.add_camera() >>> im = sc.render() Alternatively, you can use the create_scene function to set up defaults and then modify the Scene later: >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> # Modify camera, sources, etc... >>> im = sc.render() """ _current = None _camera = None _unit_registry = None def __init__(self): r"""Create a new Scene instance""" super().__init__() self.sources = OrderedDict() self._last_render = None # A non-public attribute used to get around the fact that we can't # pass kwargs into _repr_png_() self._sigma_clip = None def get_source(self, source_num=0): """Returns the volume rendering source indexed by ``source_num``""" return list(self.sources.values())[source_num] def __getitem__(self, item): if item in self.sources: return self.sources[item] return self.get_source(item) @property def opaque_sources(self): """ Iterate over opaque RenderSource objects, returning a tuple of (key, source) """ for k, source in self.sources.items(): if isinstance(source, OpaqueSource) or issubclass( OpaqueSource, type(source) ): yield k, source @property def transparent_sources(self): """ Iterate over transparent RenderSource objects, returning a tuple of (key, source) """ for k, source in self.sources.items(): if not isinstance(source, OpaqueSource): yield k, source def add_source(self, render_source, keyname=None): """Add a render source to the scene. This will autodetect the type of source. Parameters ---------- render_source: :class:`yt.visualization.volume_rendering.render_source.RenderSource` A source to contribute to the volume rendering scene. keyname: string (optional) The dictionary key used to reference the source in the sources dictionary. """ if keyname is None: keyname = "source_%02i" % len(self.sources) data_sources = (VolumeSource, MeshSource, GridSource) if isinstance(render_source, data_sources): self._set_new_unit_registry(render_source.data_source.ds.unit_registry) line_annotation_sources = (GridSource, BoxSource, CoordinateVectorSource) if isinstance(render_source, line_annotation_sources): lens_str = str(self.camera.lens) if "fisheye" in lens_str or "spherical" in lens_str: raise NotImplementedError( "Line annotation sources are not supported " f"for {type(self.camera.lens).__name__}." ) if isinstance(render_source, (LineSource, PointSource)): if isinstance(render_source.positions, YTArray): render_source.positions = ( self.arr(render_source.positions).in_units("code_length").d ) self.sources[keyname] = render_source return self def __setitem__(self, key, value): return self.add_source(value, key) def _set_new_unit_registry(self, input_registry): self.unit_registry = UnitRegistry( add_default_symbols=False, lut=input_registry.lut ) # Validate that the new unit registry makes sense current_scaling = self.unit_registry["unitary"][0] if current_scaling != input_registry["unitary"][0]: for source in self.sources.items(): data_source = getattr(source, "data_source", None) if data_source is None: continue scaling = data_source.ds.unit_registry["unitary"][0] if scaling != current_scaling: raise NotImplementedError( "Simultaneously rendering data from datasets with " "different units is not supported" ) def render(self, camera=None): r"""Render all sources in the Scene. Use the current state of the Scene object to render all sources currently in the scene. Returns the image array. If you want to save the output to a file, call the save() function. Parameters ---------- camera: :class:`Camera`, optional If specified, use a different :class:`Camera` to render the scene. Returns ------- A :class:`yt.data_objects.image_array.ImageArray` instance containing the current rendering image. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> # Modify camera, sources, etc... >>> im = sc.render() >>> sc.save(sigma_clip=4.0, render=False) Altneratively, if you do not need the image array, you can just call ``save`` as follows. >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> # Modify camera, sources, etc... >>> sc.save(sigma_clip=4.0) """ mylog.info("Rendering scene (Can take a while).") if camera is None: camera = self.camera assert camera is not None self._validate() bmp = self.composite(camera=camera) self._last_render = bmp return bmp def _render_on_demand(self, render): # checks for existing render before rendering, in most cases we want to # render every time, but in some cases pulling the previous render is # desirable (e.g., if only changing sigma_clip or # saving after a call to sc.show()). if self._last_render is not None and not render: mylog.info("Found previously rendered image to save.") return if self._last_render is None: mylog.warning("No previously rendered image found, rendering now.") elif render: mylog.warning( "Previously rendered image exists, but rendering anyway. " "Supply 'render=False' to save previously rendered image directly." ) self.render() def _get_render_sources(self): return [s for s in self.sources.values() if isinstance(s, RenderSource)] def _setup_save(self, fname, render) -> str: self._render_on_demand(render) rensources = self._get_render_sources() if fname is None: # if a volume source present, use its affiliated ds for fname if len(rensources) > 0: rs = rensources[0] basename = rs.data_source.ds.basename if isinstance(rs.field, str): field = rs.field else: field = rs.field[-1] fname = f"{basename}_Render_{field}" # if no volume source present, use a default filename else: fname = "Render_opaque" fname = validate_image_name(fname) mylog.info("Saving rendered image to %s", fname) return fname def save( self, fname: str | None = None, sigma_clip: float | None = None, render: bool = True, ): r"""Saves a rendered image of the Scene to disk. Once you have created a scene, this saves an image array to disk with an optional filename. This function calls render() to generate an image array, unless the render parameter is set to False, in which case the most recently rendered scene is used if it exists. Parameters ---------- fname: string, optional If specified, save the rendering as to the file "fname". If unspecified, it creates a default based on the dataset filename. The file format is inferred from the filename's suffix. Supported formats depend on which version of matplotlib is installed. Default: None sigma_clip: float, optional Image values greater than this number times the standard deviation plus the mean of the image will be clipped before saving. Useful for enhancing images as it gets rid of rare high pixel values. Default: None floor(vals > std_dev*sigma_clip + mean) render: boolean, optional If True, will always render the scene before saving. If False, will use results of previous render if it exists. Default: True Returns ------- Nothing Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> # Modify camera, sources, etc... >>> sc.save("test.png", sigma_clip=4) When saving multiple images without modifying the scene (camera, sources,etc.), render=False can be used to avoid re-rendering. This is useful for generating images at a range of sigma_clip values: >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> # save with different sigma clipping values >>> sc.save("raw.png") # The initial render call happens here >>> sc.save("clipped_2.png", sigma_clip=2, render=False) >>> sc.save("clipped_4.png", sigma_clip=4, render=False) """ fname = self._setup_save(fname, render) # We can render pngs natively but for other formats we defer to # matplotlib. if fname.endswith(".png"): self._last_render.write_png(fname, sigma_clip=sigma_clip) else: from matplotlib.figure import Figure shape = self._last_render.shape fig = Figure((shape[0] / 100.0, shape[1] / 100.0)) canvas = get_canvas(fig, fname) ax = fig.add_axes((0, 0, 1, 1)) ax.set_axis_off() out = self._last_render if sigma_clip is not None: max_val = out._clipping_value(sigma_clip) else: max_val = out[:, :, :3].max() alpha = 255 * out[:, :, 3].astype("uint8") out = np.clip(out[:, :, :3] / max_val, 0.0, 1.0) * 255 out = np.concatenate([out.astype("uint8"), alpha[..., None]], axis=-1) # not sure why we need rot90, but this makes the orientation # match the png writer ax.imshow(np.rot90(out), origin="lower") canvas.print_figure(fname, dpi=100) def save_annotated( self, fname: str | None = None, label_fmt: str | None = None, text_annotate=None, dpi: int = 100, sigma_clip: float | None = None, render: bool = True, tf_rect: list[float] | None = None, *, label_fontsize: float | str = 10, ): r"""Saves the most recently rendered image of the Scene to disk, including an image of the transfer function and and user-defined text. Once you have created a scene and rendered that scene to an image array, this saves that image array to disk with an optional filename. If an image has not yet been rendered for the current scene object, it forces one and writes it out. Parameters ---------- fname: string, optional If specified, save the rendering as a bitmap to the file "fname". If unspecified, it creates a default based on the dataset filename. Default: None sigma_clip: float, optional Image values greater than this number times the standard deviation plus the mean of the image will be clipped before saving. Useful for enhancing images as it gets rid of rare high pixel values. Default: None floor(vals > std_dev*sigma_clip + mean) dpi: integer, optional By default, the resulting image will be the same size as the camera parameters. If you supply a dpi, then the image will be scaled accordingly (from the default 100 dpi) label_fmt : str, optional A format specifier (e.g., label_fmt="%.2g") to use in formatting the data values that label the transfer function colorbar. label_fontsize : float or string, optional The fontsize used to display the numbers on the transfer function colorbar. This can be any matplotlib font size specification, e.g., "large" or 12. (default: 10) text_annotate : list of iterables Any text that you wish to display on the image. This should be an list containing a tuple of coordinates (in normalized figure coordinates), the text to display, and, optionally, a dictionary of keyword/value pairs to pass through to the matplotlib text() function. Each item in the main list is a separate string to write. render: boolean, optional If True, will render the scene before saving. If False, will use results of previous render if it exists. Default: True tf_rect : sequence of floats, optional A rectangle that defines the location of the transfer function legend. This is only used for the case where there are multiple volume sources with associated transfer functions. tf_rect is of the form [x0, y0, width, height], in figure coordinates. Returns ------- Nothing Examples -------- >>> sc.save_annotated( ... "fig.png", ... text_annotate=[ ... [ ... (0.05, 0.05), ... f"t = {ds.current_time.d}", ... dict(horizontalalignment="left"), ... ], ... [ ... (0.5, 0.95), ... "simulation title", ... dict(color="y", fontsize="24", horizontalalignment="center"), ... ], ... ], ... ) """ fname = self._setup_save(fname, render) ax = self._show_mpl( self._last_render.swapaxes(0, 1), sigma_clip=sigma_clip, dpi=dpi ) # number of transfer functions? num_trans_func = 0 for rs in self._get_render_sources(): if hasattr(rs, "transfer_function"): num_trans_func += 1 # which transfer function? if num_trans_func == 1: rs = self._get_render_sources()[0] tf = rs.transfer_function label = rs.data_source.ds._get_field_info(rs.field).get_label() self._annotate( ax.axes, tf, rs, label=label, label_fmt=label_fmt, label_fontsize=label_fontsize, ) else: # set the origin and width and height of the colorbar region if tf_rect is None: tf_rect = [0.80, 0.12, 0.12, 0.9] cbx0, cby0, cbw, cbh = tf_rect cbh_each = cbh / num_trans_func for i, rs in enumerate(self._get_render_sources()): ax = self._render_figure.add_axes( [cbx0, cby0 + i * cbh_each, 0.8 * cbw, 0.8 * cbh_each] ) try: tf = rs.transfer_function except AttributeError: pass else: label = rs.data_source.ds._get_field_info(rs.field).get_label() self._annotate_multi( ax, tf, rs, label=label, label_fmt=label_fmt, label_fontsize=label_fontsize, ) # any text? if text_annotate is not None: f = self._render_figure for t in text_annotate: xy = t[0] string = t[1] if len(t) == 3: opt = t[2] else: opt = {} # sane default if "color" not in opt: opt["color"] = "w" ax.axes.text(xy[0], xy[1], string, transform=f.transFigure, **opt) self._render_figure.canvas = get_canvas(self._render_figure, fname) self._render_figure.tight_layout() self._render_figure.savefig(fname, facecolor="black", pad_inches=0) def _show_mpl(self, im, sigma_clip=None, dpi=100): from matplotlib.figure import Figure s = im.shape self._render_figure = Figure( figsize=(s[1] / float(dpi), s[0] / float(dpi)), dpi=dpi ) self._render_figure.clf() ax = self._render_figure.add_subplot(111) ax.set_position([0, 0, 1, 1]) if sigma_clip is not None: nim = im / im._clipping_value(sigma_clip) nim[nim > 1.0] = 1.0 nim[nim < 0.0] = 0.0 else: nim = im axim = ax.imshow(nim[:, :, :3] / nim[:, :, :3].max(), interpolation="bilinear") return axim def _annotate(self, ax, tf, source, label="", label_fmt=None, label_fontsize=10): ax.get_xaxis().set_visible(False) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_visible(False) ax.get_yaxis().set_ticks([]) cb = self._render_figure.colorbar( ax.images[0], pad=0.0, fraction=0.05, drawedges=True ) tf.vert_cbar( ax=cb.ax, label=label, label_fmt=label_fmt, label_fontsize=label_fontsize, resolution=self.camera.resolution[0], log_scale=source.log_field, ) def _annotate_multi(self, ax, tf, source, label, label_fmt, label_fontsize=10): ax.yaxis.set_label_position("right") ax.yaxis.tick_right() tf.vert_cbar( ax=ax, label=label, label_fmt=label_fmt, label_fontsize=label_fontsize, resolution=self.camera.resolution[0], log_scale=source.log_field, size=6, ) def _validate(self): r"""Validate the current state of the scene.""" for source in self.sources.values(): source._validate() return def composite(self, camera=None): r"""Create a composite image of the current scene. First iterate over the opaque sources and set the ZBuffer. Then iterate over the transparent sources, rendering from the value of the zbuffer to the front of the box. Typically this function is accessed through the .render() command. Parameters ---------- camera: :class:`Camera`, optional If specified, use a specific :class:`Camera` to render the scene. Returns ------- im: :class:`ImageArray` ImageArray instance of the current rendering image. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> # Modify camera, sources, etc... >>> im = sc.composite() """ if camera is None: camera = self.camera empty = camera.lens.new_image(camera) opaque = ZBuffer(empty, np.full(empty.shape[:2], np.inf)) for _, source in self.opaque_sources: source.render(camera, zbuffer=opaque) im = source.zbuffer.rgba for _, source in self.transparent_sources: im = source.render(camera, zbuffer=opaque) opaque.rgba = im # rotate image 180 degrees so orientation agrees with e.g. # a PlotWindow plot return np.rot90(im, k=2) def add_camera(self, data_source=None, lens_type="plane-parallel", auto=False): r"""Add a new camera to the Scene. The camera is defined by a position (the location of the camera in the simulation domain,), a focus (the point at which the camera is pointed), a width (the width of the snapshot that will be taken, a resolution (the number of pixels in the image), and a north_vector (the "up" direction in the resulting image). A camera can use a variety of different Lens objects. If the scene already has a camera associated with it, this function will create a new camera and discard the old one. Parameters ---------- data_source: :class:`AMR3DData` or :class:`Dataset`, optional This is the source to be rendered, which can be any arbitrary yt data object or dataset. lens_type: string, optional This specifies the type of lens to use for rendering. Current options are 'plane-parallel', 'perspective', and 'fisheye'. See :class:`yt.visualization.volume_rendering.lens.Lens` for details. Default: 'plane-parallel' auto: boolean If True, build smart defaults using the data source extent. This can be time-consuming to iterate over the entire dataset to find the positional bounds. Default: False Examples -------- In this example, the camera is set using defaults that are chosen to be reasonable for the argument Dataset. >>> import yt >>> from yt.visualization.volume_rendering.api import Camera, Scene >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> sc.add_camera() Here, we set the camera properties manually: >>> import yt >>> from yt.visualization.volume_rendering.api import Camera, Scene >>> sc = Scene() >>> cam = sc.add_camera() >>> cam.position = np.array([0.5, 0.5, -1.0]) >>> cam.focus = np.array([0.5, 0.5, 0.0]) >>> cam.north_vector = np.array([1.0, 0.0, 0.0]) Finally, we create a camera with a non-default lens: >>> import yt >>> from yt.visualization.volume_rendering.api import Camera >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = Scene() >>> sc.add_camera(ds, lens_type="perspective") """ self._camera = Camera(self, data_source, lens_type, auto) return self.camera @property def camera(self): r"""The camera property. This is the default camera that will be used when rendering. Can be set manually, but Camera type will be checked for validity. """ return self._camera @camera.setter def camera(self, value): value.width = self.arr(value.width) value.focus = self.arr(value.focus) value.position = self.arr(value.position) self._camera = value @camera.deleter def camera(self): del self._camera self._camera = None @property def unit_registry(self): ur = self._unit_registry if ur is None: ur = UnitRegistry() # This will be updated when we add a volume source ur.add("unitary", 1.0, length) self._unit_registry = ur return self._unit_registry @unit_registry.setter def unit_registry(self, value): self._unit_registry = value if self.camera is not None: self.camera.width = YTArray( self.camera.width.in_units("unitary"), registry=value ) self.camera.focus = YTArray( self.camera.focus.in_units("unitary"), registry=value ) self.camera.position = YTArray( self.camera.position.in_units("unitary"), registry=value ) @unit_registry.deleter def unit_registry(self): del self._unit_registry self._unit_registry = None def set_camera(self, camera): r""" Set the camera to be used by this scene. """ self.camera = camera def get_camera(self): r""" Get the camera currently used by this scene. """ return self.camera def annotate_domain(self, ds, color=None): r""" Modifies this scene by drawing the edges of the computational domain. This adds a new BoxSource to the scene corresponding to the domain boundaries and returns the modified scene object. Parameters ---------- ds : :class:`yt.data_objects.static_output.Dataset` This is the dataset object corresponding to the simulation being rendered. Used to get the domain bounds. color : array_like of shape (4,), optional The RGBA value to use to draw the domain boundaries. Default is black with an alpha of 1.0. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> sc.annotate_domain(ds) >>> im = sc.render() """ box_source = BoxSource(ds.domain_left_edge, ds.domain_right_edge, color=color) self.add_source(box_source) return self def annotate_grids( self, data_source, alpha=0.3, cmap=None, min_level=None, max_level=None ): r""" Modifies this scene by drawing the edges of the AMR grids. This adds a new GridSource to the scene that represents the AMR grid and returns the resulting Scene object. Parameters ---------- data_source: :class:`~yt.data_objects.api.DataContainer` The data container that will be used to identify grids to draw. alpha : float The opacity of the grids to draw. cmap : color map name The color map to use to map resolution levels to color. min_level : int, optional Minimum level to draw max_level : int, optional Maximum level to draw Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> sc.annotate_grids(ds.all_data()) >>> im = sc.render() """ if cmap is None: cmap = ytcfg.get("yt", "default_colormap") grids = GridSource( data_source, alpha=alpha, cmap=cmap, min_level=min_level, max_level=max_level, ) self.add_source(grids) return self def annotate_mesh_lines(self, color=None, alpha=1.0): """ Modifies this Scene by drawing the mesh line boundaries on all MeshSources. Parameters ---------- color : array_like of shape (4,), optional The RGBA value to use to draw the mesh lines. Default is black with an alpha of 1.0. alpha : float, optional The opacity of the mesh lines. Default is 255 (solid). """ for _, source in self.opaque_sources: if isinstance(source, MeshSource): source.annotate_mesh_lines(color=color, alpha=alpha) return self def annotate_axes(self, colors=None, alpha=1.0, *, thickness=1): r""" Modifies this scene by drawing the coordinate axes. This adds a new CoordinateVectorSource to the scene and returns the modified scene object. Parameters ---------- colors: array-like of shape (3,4), optional The RGBA values to use to draw the x, y, and z vectors. The default is [[1, 0, 0, alpha], [0, 1, 0, alpha], [0, 0, 1, alpha]] where ``alpha`` is set by the parameter below. If ``colors`` is set then ``alpha`` is ignored. alpha : float, optional The opacity of the vectors. thickness : int, optional The line thickness Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> sc.annotate_axes(alpha=0.5) >>> im = sc.render() """ coords = CoordinateVectorSource(colors, alpha, thickness=thickness) self.add_source(coords) return self def show(self, sigma_clip=None): r"""This will send the most recently rendered image to the IPython notebook. If yt is being run from within an IPython session, and it is able to determine this, this function will send the current image of this Scene to the notebook for display. If there is no current image, it will run the render() method on this Scene before sending the result to the notebook. If yt can't determine if it's inside an IPython session, this will raise YTNotInsideNotebook. Examples -------- >>> import yt >>> ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030") >>> sc = yt.create_scene(ds) >>> sc.show() """ if IS_IPYTHON: from IPython.display import display self._sigma_clip = sigma_clip display(self) else: raise YTNotInsideNotebook _arr = None @property def arr(self): """Converts an array into a :class:`yt.units.yt_array.YTArray` The returned YTArray will be dimensionless by default, but can be cast to arbitrary units using the ``units`` keyword argument. Parameters ---------- input_array : Iterable A tuple, list, or array to attach units to units: String unit specification, unit symbol object, or astropy units object The units of the array. Powers must be specified using python syntax (cm**3, not cm^3). dtype : string or NumPy dtype object The dtype of the returned array data Examples -------- >>> a = sc.arr([1, 2, 3], "cm") >>> b = sc.arr([4, 5, 6], "m") >>> a + b YTArray([ 401., 502., 603.]) cm >>> b + a YTArray([ 4.01, 5.02, 6.03]) m Arrays returned by this function know about the scene's unit system >>> a = sc.arr(np.ones(5), "unitary") >>> a.in_units("Mpc") YTArray([ 1.00010449, 1.00010449, 1.00010449, 1.00010449, 1.00010449]) Mpc """ if self._arr is not None: return self._arr self._arr = functools.partial(YTArray, registry=self.unit_registry) return self._arr _quan = None @property def quan(self): """Converts an scalar into a :class:`yt.units.yt_array.YTQuantity` The returned YTQuantity will be dimensionless by default, but can be cast to arbitrary units using the ``units`` keyword argument. Parameters ---------- input_scalar : an integer or floating point scalar The scalar to attach units to units : String unit specification, unit symbol object, or astropy units input_units : deprecated in favor of 'units' The units of the quantity. Powers must be specified using python syntax (cm**3, not cm^3). dtype : string or NumPy dtype object The dtype of the array data. Examples -------- >>> a = sc.quan(1, "cm") >>> b = sc.quan(2, "m") >>> a + b 201.0 cm >>> b + a 2.01 m Quantities created this way automatically know about the unit system of the scene >>> a = ds.quan(5, "unitary") >>> a.in_cgs() 1.543e+25 cm """ if self._quan is not None: return self._quan self._quan = functools.partial(YTQuantity, registry=self.unit_registry) return self._quan def _repr_png_(self): if self._last_render is None: self.render() png = self._last_render.write_png( filename=None, sigma_clip=self._sigma_clip, background="black" ) self._sigma_clip = None return png def __repr__(self): disp = ":" disp += "\nSources: \n" for k, v in self.sources.items(): disp += f" {k}: {v}\n" disp += "Camera: \n" disp += f" {self.camera}" return disp yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/000077500000000000000000000000001510711153200247155ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/__init__.py000066400000000000000000000000001510711153200270140ustar00rootroot00000000000000yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_camera_attributes.py000066400000000000000000000102271510711153200320260ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal, assert_equal import yt.units as u from yt.testing import fake_random_ds from yt.visualization.volume_rendering.api import Scene valid_lens_types = [ "plane-parallel", "perspective", "stereo-perspective", "fisheye", "spherical", "stereo-spherical", ] def test_scene_and_camera_attributes(): ds = fake_random_ds(64, length_unit=2, bbox=np.array([[-1, 1], [-1, 1], [-1, 1]])) sc = Scene() cam = sc.add_camera(ds) # test that initial values are correct in code units assert_equal(cam.width, ds.arr([3, 3, 3], "code_length")) assert_equal(cam.position, ds.arr([1, 1, 1], "code_length")) assert_equal(cam.focus, ds.arr([0, 0, 0], "code_length")) # test setting the attributes in various ways attribute_values = [ ( 1, ds.arr([2, 2, 2], "code_length"), ), ( [1], ds.arr([2, 2, 2], "code_length"), ), ( [1, 2], RuntimeError, ), ( [1, 1, 1], ds.arr([2, 2, 2], "code_length"), ), ( (1, "code_length"), ds.arr([1, 1, 1], "code_length"), ), ( ((1, "code_length"), (1, "code_length")), RuntimeError, ), ( ((1, "cm"), (2, "cm"), (3, "cm")), ds.arr([0.5, 1, 1.5], "code_length"), ), ( 2 * u.cm, ds.arr([1, 1, 1], "code_length"), ), ( ds.arr(2, "cm"), ds.arr([1, 1, 1], "code_length"), ), ( [2 * u.cm], ds.arr([1, 1, 1], "code_length"), ), ( [1, 2, 3] * u.cm, ds.arr([0.5, 1, 1.5], "code_length"), ), ( [1, 2] * u.cm, RuntimeError, ), ( [u.cm * w for w in [1, 2, 3]], ds.arr([0.5, 1, 1.5], "code_length"), ), ] # define default values to avoid accidentally setting focus = position default_values = { "focus": [0, 0, 0], "position": [4, 4, 4], "width": [1, 1, 1], } attribute_list = list(default_values.keys()) for attribute in attribute_list: for other_attribute in [a for a in attribute_list if a != attribute]: setattr(cam, other_attribute, default_values[other_attribute]) for attribute_value, expected_result in attribute_values: try: # test properties setattr(cam, attribute, attribute_value) assert_almost_equal(getattr(cam, attribute), expected_result) except RuntimeError: assert expected_result is RuntimeError try: # test setters/getters getattr(cam, f"set_{attribute}")(attribute_value) assert_almost_equal(getattr(cam, f"get_{attribute}")(), expected_result) except RuntimeError: assert expected_result is RuntimeError resolution_values = ( ( 512, (512, 512), ), ( (512, 512), (512, 512), ), ( (256, 512), (256, 512), ), ((256, 256, 256), RuntimeError), ) for resolution_value, expected_result in resolution_values: try: # test properties cam.resolution = resolution_value assert_equal(cam.resolution, expected_result) except RuntimeError: assert expected_result is RuntimeError try: # test setters/getters cam.set_resolution(resolution_value) assert_equal(cam.get_resolution(), expected_result) except RuntimeError: assert expected_result is RuntimeError for lens_type in valid_lens_types: cam.set_lens(lens_type) # See issue #1287 cam.focus = [0, 0, 0] cam_pos = [1, 0, 0] north_vector = [0, 1, 0] cam.set_position(cam_pos, north_vector) cam_pos = [0, 1, 0] north_vector = [0, 0, 1] cam.set_position(cam_pos, north_vector) yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_composite.py000066400000000000000000000046421510711153200303360ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np from yt.data_objects.api import ImageArray from yt.testing import fake_random_ds from yt.visualization.volume_rendering.api import ( BoxSource, LineSource, Scene, create_volume_source, ) def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class CompositeVRTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): np.random.seed(0) if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_composite_vr(self): ds = fake_random_ds(64) dd = ds.sphere(ds.domain_center, 0.45 * ds.domain_width[0]) ds.field_info[ds.field_list[0]].take_log = False sc = Scene() cam = sc.add_camera(ds) cam.resolution = (512, 512) vr = create_volume_source(dd, field=ds.field_list[0]) vr.transfer_function.clear() vr.transfer_function.grey_opacity = True vr.transfer_function.map_to_colormap(0.0, 1.0, scale=3.0, colormap="Reds") sc.add_source(vr) cam.set_width(1.8 * ds.domain_width) cam.lens.setup_box_properties(cam) # DRAW SOME LINES npoints = 100 vertices = np.random.random([npoints, 2, 3]) colors = np.random.random([npoints, 4]) colors[:, 3] = 0.10 box_source = BoxSource( ds.domain_left_edge, ds.domain_right_edge, color=[1.0, 1.0, 1.0, 1.0] ) sc.add_source(box_source) LE = ds.domain_left_edge + np.array([0.1, 0.0, 0.3]) * ds.domain_left_edge.uq RE = ds.domain_right_edge - np.array([0.1, 0.2, 0.3]) * ds.domain_left_edge.uq color = np.array([0.0, 1.0, 0.0, 0.10]) box_source = BoxSource(LE, RE, color=color) sc.add_source(box_source) line_source = LineSource(vertices, colors) sc.add_source(line_source) im = sc.render() im = ImageArray(im.d) im.write_png("composite.png") yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_lenses.py000066400000000000000000000077611510711153200276320ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np from yt.testing import fake_random_ds from yt.visualization.volume_rendering.api import Scene, create_volume_source def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class LensTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None self.field = ("gas", "density") self.ds = fake_random_ds(32, fields=(self.field,), units=("g/cm**3",)) self.ds.index def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_perspective_lens(self): sc = Scene() cam = sc.add_camera(self.ds, lens_type="perspective") cam.position = self.ds.arr(np.array([1.0, 1.0, 1.0]), "code_length") vol = create_volume_source(self.ds, field=self.field) tf = vol.transfer_function tf.grey_opacity = True sc.add_source(vol) sc.save(f"test_perspective_{self.field[1]}.png", sigma_clip=6.0) def test_stereoperspective_lens(self): sc = Scene() cam = sc.add_camera(self.ds, lens_type="stereo-perspective") cam.resolution = [256, 128] cam.position = self.ds.arr(np.array([0.7, 0.7, 0.7]), "code_length") vol = create_volume_source(self.ds, field=self.field) tf = vol.transfer_function tf.grey_opacity = True sc.add_source(vol) sc.save(f"test_stereoperspective_{self.field[1]}.png", sigma_clip=6.0) def test_fisheye_lens(self): dd = self.ds.sphere(self.ds.domain_center, self.ds.domain_width[0] / 10) sc = Scene() cam = sc.add_camera(dd, lens_type="fisheye") cam.lens.fov = 360.0 cam.set_width(self.ds.domain_width) v, c = self.ds.find_max(("gas", "density")) cam.set_position(c - 0.0005 * self.ds.domain_width) vol = create_volume_source(dd, field=self.field) tf = vol.transfer_function tf.grey_opacity = True sc.add_source(vol) sc.save(f"test_fisheye_{self.field[1]}.png", sigma_clip=6.0) def test_plane_lens(self): dd = self.ds.sphere(self.ds.domain_center, self.ds.domain_width[0] / 10) sc = Scene() cam = sc.add_camera(dd, lens_type="plane-parallel") cam.set_width(self.ds.domain_width * 1e-2) v, c = self.ds.find_max(("gas", "density")) vol = create_volume_source(dd, field=self.field) tf = vol.transfer_function tf.grey_opacity = True sc.add_source(vol) sc.save(f"test_plane_{self.field[1]}.png", sigma_clip=6.0) def test_spherical_lens(self): sc = Scene() cam = sc.add_camera(self.ds, lens_type="spherical") cam.resolution = [256, 128] cam.position = self.ds.arr(np.array([0.6, 0.5, 0.5]), "code_length") vol = create_volume_source(self.ds, field=self.field) tf = vol.transfer_function tf.grey_opacity = True sc.add_source(vol) sc.save(f"test_spherical_{self.field[1]}.png", sigma_clip=6.0) def test_stereospherical_lens(self): w = (self.ds.domain_width).in_units("code_length") w = self.ds.arr(w, "code_length") sc = Scene() cam = sc.add_camera(self.ds, lens_type="stereo-spherical") cam.resolution = [256, 256] cam.position = self.ds.arr(np.array([0.6, 0.5, 0.5]), "code_length") vol = create_volume_source(self.ds, field=self.field) tf = vol.transfer_function tf.grey_opacity = True sc.add_source(vol) sc.save(f"test_stereospherical_{self.field[1]}.png", sigma_clip=6.0) yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_mesh_render.py000066400000000000000000000317111510711153200306240ustar00rootroot00000000000000import matplotlib.pyplot as plt import pytest from yt.config import ytcfg from yt.testing import ( fake_hexahedral_ds, fake_tetrahedral_ds, requires_file, requires_module, ) from yt.utilities.answer_testing.framework import data_dir_load, data_dir_load_v2 from yt.visualization.volume_rendering.api import MeshSource, Scene, create_scene from yt.visualization.volume_rendering.render_source import set_raytracing_engine @pytest.fixture @requires_module("pyembree") def with_pyembree_ray_tracing_engine(): old = ytcfg["yt", "ray_tracing_engine"] # the @requires_module decorator only guards against pyembree not being # available for import, but it might not be installed properly regardless # so we need to be extra careful not to run tests with the default engine try: set_raytracing_engine("embree") except UserWarning as exc: pytest.skip(str(exc)) else: if ytcfg["yt", "ray_tracing_engine"] != "embree": pytest.skip("Error while setting embree raytracing engine") yield set_raytracing_engine(old) @pytest.fixture def with_default_ray_tracing_engine(): old = ytcfg["yt", "ray_tracing_engine"] set_raytracing_engine("yt") yield set_raytracing_engine(old) @pytest.mark.usefixtures("with_default_ray_tracing_engine") class TestMeshRenderDefaultEngine: @classmethod def setup_class(cls): cls.images = {} cls.ds_t = fake_tetrahedral_ds() for field in cls.ds_t.field_list: if field[0] == "all": continue sc = Scene() sc.add_source(MeshSource(cls.ds_t, field)) sc.add_camera() im = sc.render() cls.images["tetrahedral_" + "_".join(field)] = im cls.ds_h = fake_hexahedral_ds() for field in cls.ds_t.field_list: if field[0] == "all": continue sc = Scene() sc.add_source(MeshSource(cls.ds_h, field)) sc.add_camera() im = sc.render() cls.images["hexahedral_" + "_".join(field)] = im @pytest.mark.parametrize("kind", ["tetrahedral", "hexahedral"]) @pytest.mark.parametrize("fname", ["elem", "test"]) @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine(self, kind, fname): fig, ax = plt.subplots() ax.imshow(self.images[f"{kind}_connect1_{fname}"]) return fig @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") class TestMeshRenderPyembreeEngine: @classmethod def setup_class(cls): cls.images = {} cls.ds_t = fake_tetrahedral_ds() for field in cls.ds_t.field_list: if field[0] == "all": continue sc = Scene() sc.add_source(MeshSource(cls.ds_t, field)) sc.add_camera() im = sc.render() cls.images["tetrahedral_" + "_".join(field)] = im cls.ds_h = fake_hexahedral_ds() for field in cls.ds_t.field_list: if field[0] == "all": continue sc = Scene() sc.add_source(MeshSource(cls.ds_h, field)) sc.add_camera() im = sc.render() cls.images["hexahedral_" + "_".join(field)] = im @pytest.mark.parametrize("kind", ["tetrahedral", "hexahedral"]) @pytest.mark.parametrize("fname", ["elem", "test"]) @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine(self, kind, fname): fig, ax = plt.subplots() ax.imshow(self.images[f"{kind}_connect1_{fname}"]) return fig hex8 = "MOOSE_sample_data/out.e-s010" @pytest.mark.usefixtures("with_default_ray_tracing_engine") class TestHex8DefaultEngine: @requires_file(hex8) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(hex8, step=-1) cls.images = {} for field in [("connect1", "diffused"), ("connect2", "convected")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine_hex8_connect1_diffused(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_diffused"]) return fig @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine_hex8_connect2_convected(self): fig, ax = plt.subplots() ax.imshow(self.images["connect2_convected"]) return fig @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") class TestHex8PyembreeEngine: @requires_file(hex8) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(hex8, step=-1) cls.images = {} for field in [("connect1", "diffused"), ("connect2", "convected")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine_hex8_connect1_diffused(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_diffused"]) return fig @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine_hex8_connect2_convected(self): fig, ax = plt.subplots() ax.imshow(self.images["connect2_convected"]) return fig tet4 = "MOOSE_sample_data/high_order_elems_tet4_refine_out.e" @pytest.mark.usefixtures("with_default_ray_tracing_engine") class TestTet4DefaultEngine: @requires_file(tet4) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(tet4, step=-1) cls.images = {} for field in [("connect1", "u")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine_tet4(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_u"]) return fig @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") class TestTet4PyembreeEngine: @requires_file(tet4) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(tet4, step=-1) cls.images = {} for field in [("connect1", "u")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine_tet4(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_u"]) return fig hex20 = "MOOSE_sample_data/mps_out.e" @pytest.mark.usefixtures("with_default_ray_tracing_engine") class TestHex20DefaultEngine: @requires_file(hex20) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(hex20, step=-1) cls.images = {} for field in [("connect2", "temp")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine_hex20(self): fig, ax = plt.subplots() ax.imshow(self.images["connect2_temp"]) return fig @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") class TestHex20PyembreeEngine: @requires_file(hex20) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(hex20, step=-1) cls.images = {} for field in [("connect2", "temp")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine_hex20(self): fig, ax = plt.subplots() ax.imshow(self.images["connect2_temp"]) return fig wedge6 = "MOOSE_sample_data/wedge_out.e" @pytest.mark.usefixtures("with_default_ray_tracing_engine") class TestWedge6DefaultEngine: @requires_file(wedge6) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(wedge6, step=-1) cls.images = {} for field in [("connect1", "diffused")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine_wedge6(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_diffused"]) return fig @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") class TestWedge6PyembreeEngine: @requires_file(wedge6) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(wedge6, step=-1) cls.images = {} for field in [("connect1", "diffused")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine_wedge6(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_diffused"]) return fig tet10 = "SecondOrderTets/tet10_unstructured_out.e" @pytest.mark.usefixtures("with_default_ray_tracing_engine") class TestTet10DefaultEngine: @requires_file(tet10) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(tet10, step=-1) cls.images = {} for field in [("connect1", "uz")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_default_engine_tet10(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_uz"]) return fig @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") class TestTet10PyembreeEngine: @requires_file(tet10) @classmethod def setup_class(cls): cls.ds = data_dir_load_v2(tet10, step=-1) cls.images = {} for field in [("connect1", "uz")]: sc = create_scene(cls.ds, field) im = sc.render() cls.images["_".join(field)] = im @pytest.mark.mpl_image_compare(remove_text=True) def test_mesh_render_pyembree_engine_tet10(self): fig, ax = plt.subplots() ax.imshow(self.images["connect1_uz"]) return fig @requires_file(hex8) @pytest.mark.usefixtures("with_default_ray_tracing_engine") @pytest.mark.mpl_image_compare def test_perspective_mesh_render_default(): ds = data_dir_load(hex8) sc = create_scene(ds, ("connect2", "diffused")) cam = sc.add_camera(ds, lens_type="perspective") cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-4.5, 4.5, -4.5], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) cam.resolution = (800, 800) im = sc.render() fig, ax = plt.subplots() ax.imshow(im) return fig @requires_file(hex8) @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") @pytest.mark.mpl_image_compare def test_perspective_mesh_render_pyembree(): ds = data_dir_load(hex8) sc = create_scene(ds, ("connect2", "diffused")) cam = sc.add_camera(ds, lens_type="perspective") cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam_pos = ds.arr([-4.5, 4.5, -4.5], "code_length") north_vector = ds.arr([0.0, -1.0, -1.0], "dimensionless") cam.set_position(cam_pos, north_vector) cam.resolution = (800, 800) im = sc.render() fig, ax = plt.subplots() ax.imshow(im) return fig @requires_file(hex8) @pytest.mark.usefixtures("with_default_ray_tracing_engine") @pytest.mark.mpl_image_compare def test_composite_mesh_render_default(): ds = data_dir_load(hex8) sc = Scene() cam = sc.add_camera(ds) cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam.set_position( ds.arr([-3.0, 3.0, -3.0], "code_length"), ds.arr([0.0, -1.0, 0.0], "dimensionless"), ) cam.set_width = ds.arr([8.0, 8.0, 8.0], "code_length") cam.resolution = (800, 800) ms1 = MeshSource(ds, ("connect1", "diffused")) ms2 = MeshSource(ds, ("connect2", "diffused")) sc.add_source(ms1) sc.add_source(ms2) im = sc.render() fig, ax = plt.subplots() ax.imshow(im) return fig @requires_file(hex8) @pytest.mark.usefixtures("with_pyembree_ray_tracing_engine") @pytest.mark.mpl_image_compare def test_composite_mesh_render_pyembree(): ds = data_dir_load(hex8) sc = Scene() cam = sc.add_camera(ds) cam.focus = ds.arr([0.0, 0.0, 0.0], "code_length") cam.set_position( ds.arr([-3.0, 3.0, -3.0], "code_length"), ds.arr([0.0, -1.0, 0.0], "dimensionless"), ) cam.set_width = ds.arr([8.0, 8.0, 8.0], "code_length") cam.resolution = (800, 800) ms1 = MeshSource(ds, ("connect1", "diffused")) ms2 = MeshSource(ds, ("connect2", "diffused")) sc.add_source(ms1) sc.add_source(ms2) im = sc.render() fig, ax = plt.subplots() ax.imshow(im) return fig yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_off_axis_SPH.py000066400000000000000000000247031510711153200306440ustar00rootroot00000000000000import numpy as np from numpy.testing import assert_almost_equal from yt.testing import fake_sph_orientation_ds, requires_module from yt.utilities.lib.pixelization_routines import pixelize_sph_kernel_projection from yt.utilities.on_demand_imports import _scipy from yt.visualization.volume_rendering import off_axis_projection as OffAP spatial = _scipy.spatial ndimage = _scipy.ndimage def test_no_rotation(): """Determines if a projection processed through off_axis_projection with no rotation will give the same image buffer if processed directly through pixelize_sph_kernel_projection """ normal_vector = [0.0, 0.0, 1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() ad = ds.all_data() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = (left_edge + right_edge) / 2 width = right_edge - left_edge px = ad["all", "particle_position_x"] py = ad["all", "particle_position_y"] pz = ad["all", "particle_position_y"] hsml = ad["all", "smoothing_length"] quantity_to_smooth = ad["gas", "density"] density = ad["io", "density"] mass = ad["io", "particle_mass"] bounds = [-4, 4, -4, 4, -4, 4] buf2 = np.zeros(resolution) mask = np.ones_like(buf2, dtype="uint8") buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density") ) pixelize_sph_kernel_projection( buf2, mask, px, py, pz, hsml, mass, density, quantity_to_smooth, bounds ) assert_almost_equal(buf1.ndarray_view(), buf2) @requires_module("scipy") def test_basic_rotation_1(): """All particles on Z-axis should now be on the negative Y-Axis fake_sph_orientation has three z-axis particles, so there should be three y-axis particles after rotation (0, 0, 1) -> (0, -1) (0, 0, 2) -> (0, -2) (0, 0, 3) -> (0, -3) In addition, we should find a local maxima at (0, 0) due to: (0, 0, 0) -> (0, 0) (0, 1, 0) -> (0, 0) (0, 2, 0) -> (0, 0) and the one particle on the x-axis should not change its position: (1, 0, 0) -> (1, 0) """ expected_maxima = ([0.0, 0.0, 0.0, 0.0, 1.0], [0.0, -1.0, -2.0, -3.0, 0.0]) normal_vector = [0.0, 1.0, 0.0] north_vector = [0.0, 0.0, -1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = (left_edge + right_edge) / 2 width = right_edge - left_edge buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density"), north_vector=north_vector, ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def test_basic_rotation_2(): """Rotation of x-axis onto z-axis. All particles on z-axis should now be on the negative x-Axis fake_sph_orientation has three z-axis particles, so there should be three x-axis particles after rotation (0, 0, 1) -> (-1, 0) (0, 0, 2) -> (-2, 0) (0, 0, 3) -> (-3, 0) In addition, we should find a local maxima at (0, 0) due to: (0, 0, 0) -> (0, 0) (1, 0, 0) -> (0, 0) and the two particles on the y-axis should not change its position: (0, 1, 0) -> (0, 1) (0, 2, 0) -> (0, 2) """ expected_maxima = ( [-1.0, -2.0, -3.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 1.0, 2.0], ) normal_vector = [1.0, 0.0, 0.0] north_vector = [0.0, 1.0, 0.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = (left_edge + right_edge) / 2 width = right_edge - left_edge buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density"), north_vector=north_vector, ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def test_basic_rotation_3(): """Rotation of z-axis onto negative z-axis. All fake particles on z-axis should now be of the negative z-Axis. fake_sph_orientation has three z-axis particles, so we should have a local maxima at (0, 0) (0, 0, 1) -> (0, 0) (0, 0, 2) -> (0, 0) (0, 0, 3) -> (0, 0) In addition, (0, 0, 0) should also contribute to the local maxima at (0, 0): (0, 0, 0) -> (0, 0) x-axis particles should be rotated as such: (1, 0, 0) -> (0, -1) and same goes for y-axis particles: (0, 1, 0) -> (-1, 0) (0, 2, 0) -> (-2, 0) """ expected_maxima = ([0.0, 0.0, -1.0, -2.0], [0.0, -1.0, 0.0, 0.0]) normal_vector = [0.0, 0.0, -1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = (left_edge + right_edge) / 2 width = right_edge - left_edge buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density") ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def test_basic_rotation_4(): """Rotation of x-axis to z-axis and original z-axis to y-axis with the use of the north_vector. All fake particles on z-axis should now be on the y-Axis. All fake particles on the x-axis should now be on the z-axis, and all fake particles on the y-axis should now be on the x-axis. (0, 0, 1) -> (0, 1) (0, 0, 2) -> (0, 2) (0, 0, 3) -> (0, 3) In addition, (0, 0, 0) should contribute to the local maxima at (0, 0): (0, 0, 0) -> (0, 0) x-axis particles should be rotated and contribute to the local maxima at (0, 0): (1, 0, 0) -> (0, 0) and the y-axis particles shift into the positive x direction: (0, 1, 0) -> (1, 0) (0, 2, 0) -> (2, 0) """ expected_maxima = ([0.0, 0.0, 0.0, 0.0, 1.0, 2.0], [1.0, 2.0, 3.0, 0.0, 0.0, 0.0]) normal_vector = [1.0, 0.0, 0.0] north_vector = [0.0, 0.0, 1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = (left_edge + right_edge) / 2 width = right_edge - left_edge buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density"), north_vector=north_vector, ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def test_center_1(): """Change the center to [0, 3, 0] Every point will be shifted by 3 in the y-domain With this, we should not be able to see any of the y-axis particles (0, 1, 0) -> (0, -2) (0, 2, 0) -> (0, -1) (0, 0, 1) -> (0, -3) (0, 0, 2) -> (0, -3) (0, 0, 3) -> (0, -3) (0, 0, 0) -> (0, -3) (1, 0, 0) -> (1, -3) """ expected_maxima = ([0.0, 0.0, 0.0, 1.0], [-2.0, -1.0, -3.0, -3.0]) normal_vector = [0.0, 0.0, 1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge # center = [(left_edge[0] + right_edge[0])/2, # left_edge[1], # (left_edge[2] + right_edge[2])/2] center = [0.0, 3.0, 0.0] width = right_edge - left_edge buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density") ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def test_center_2(): """Change the center to [0, -1, 0] Every point will be shifted by 1 in the y-domain With this, we should not be able to see any of the y-axis particles (0, 1, 0) -> (0, 2) (0, 2, 0) -> (0, 3) (0, 0, 1) -> (0, 1) (0, 0, 2) -> (0, 1) (0, 0, 3) -> (0, 1) (0, 0, 0) -> (0, 1) (1, 0, 0) -> (1, 1) """ expected_maxima = ([0.0, 0.0, 0.0, 1.0], [2.0, 3.0, 1.0, 1.0]) normal_vector = [0.0, 0.0, 1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = [0.0, -1.0, 0.0] width = right_edge - left_edge buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density") ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def test_center_3(): """Change the center to the left edge, or [0, -8, 0] Every point will be shifted by 8 in the y-domain With this, we should not be able to see anything ! """ expected_maxima = ([], []) normal_vector = [0.0, 0.0, 1.0] resolution = (64, 64) ds = fake_sph_orientation_ds() left_edge = ds.domain_left_edge right_edge = ds.domain_right_edge center = [0.0, -1.0, 0.0] width = [ (right_edge[0] - left_edge[0]), left_edge[1], (right_edge[2] - left_edge[2]), ] buf1 = OffAP.off_axis_projection( ds, center, normal_vector, width, resolution, ("gas", "density") ) find_compare_maxima(expected_maxima, buf1, resolution, width) @requires_module("scipy") def find_compare_maxima(expected_maxima, buf, resolution, width): buf_ndarray = buf.ndarray_view() max_filter_buf = ndimage.maximum_filter(buf_ndarray, size=5) maxima = np.isclose(max_filter_buf, buf_ndarray, rtol=1e-09) # ignore contributions from zones of no smoothing for i in range(len(maxima)): for j in range(len(maxima[i])): if np.isclose(buf_ndarray[i, j], 0.0, 1e-09): maxima[i, j] = False coords = ([], []) for i in range(len(maxima)): for j in range(len(maxima[i])): if maxima[i, j]: coords[0].append(i) coords[1].append(j) pixel_tolerance = 2.0 x_scaling_factor = resolution[0] / width[0] y_scaling_factor = resolution[1] / width[1] for i in range(len(expected_maxima[0])): found_match = False for j in range(len(coords[0])): # normalize coordinates x_coord = coords[0][j] y_coord = coords[1][j] x_coord -= resolution[0] / 2 y_coord -= resolution[1] / 2 x_coord /= x_scaling_factor y_coord /= y_scaling_factor if ( spatial.distance.euclidean( [x_coord, y_coord], [expected_maxima[0][i], expected_maxima[1][i]] ) < pixel_tolerance ): found_match = True break if found_match is not True: raise AssertionError pass yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_points.py000066400000000000000000000035731510711153200276520ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np from yt.testing import fake_random_ds from yt.visualization.volume_rendering.api import ( PointSource, Scene, create_volume_source, ) def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class PointsVRTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): np.random.seed(0) if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_points_vr(self): ds = fake_random_ds(64) dd = ds.sphere(ds.domain_center, 0.45 * ds.domain_width[0]) ds.field_info[ds.field_list[0]].take_log = False sc = Scene() cam = sc.add_camera(ds) cam.resolution = (512, 512) vr = create_volume_source(dd, field=ds.field_list[0]) vr.transfer_function.clear() vr.transfer_function.grey_opacity = False vr.transfer_function.map_to_colormap(0.0, 1.0, scale=10.0, colormap="Reds") sc.add_source(vr) cam.set_width(1.8 * ds.domain_width) cam.lens.setup_box_properties(cam) # DRAW SOME POINTS npoints = 1000 vertices = np.random.random([npoints, 3]) colors = np.random.random([npoints, 4]) colors[:, 3] = 0.10 points_source = PointSource(vertices, colors=colors) sc.add_source(points_source) im = sc.render() im.write_png("points.png") yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_save_render.py000066400000000000000000000026111510711153200306230ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import yt from yt.testing import fake_random_ds def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class SaveRenderTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True tmpdir = "./" def setUp(self): if self.use_tmpdir: tempfile.mkdtemp() self.tmpdir = tempfile.mkdtemp() def tearDown(self): if self.use_tmpdir: shutil.rmtree(self.tmpdir) def test_save_render(self): ds = fake_random_ds(ndims=32) sc = yt.create_scene(ds) # make sure it renders if nothing exists, even if render = False sc.save(os.path.join(self.tmpdir, "raw.png"), render=False) # make sure it re-renders sc.save(os.path.join(self.tmpdir, "raw_2.png"), render=True) # make sure sigma clip does not re-render sc.save(os.path.join(self.tmpdir, "clip_2.png"), sigma_clip=2.0, render=False) sc.save(os.path.join(self.tmpdir, "clip_4.png"), sigma_clip=4.0, render=False) # save a different format with/without sigma clips sc.save(os.path.join(self.tmpdir, "no_clip.jpg"), render=False) sc.save(os.path.join(self.tmpdir, "clip_2.jpg"), sigma_clip=2, render=False) yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_scene.py000066400000000000000000000062421510711153200274270ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np from yt.testing import assert_fname, fake_random_ds, fake_vr_orientation_test_ds from yt.visualization.volume_rendering.api import ( create_scene, create_volume_source, volume_render, ) def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class RotationTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_rotation(self): ds = fake_random_ds(32) ds2 = fake_random_ds(32) dd = ds.sphere(ds.domain_center, ds.domain_width[0] / 2) dd2 = ds2.sphere(ds2.domain_center, ds2.domain_width[0] / 2) im, sc = volume_render(dd, field=("gas", "density")) im.write_png("test.png") vol = sc.get_source(0) tf = vol.transfer_function tf.clear() mi, ma = dd.quantities.extrema(("gas", "density")) mi = np.log10(mi) ma = np.log10(ma) mi_bound = ((ma - mi) * (0.10)) + mi ma_bound = ((ma - mi) * (0.90)) + mi tf.map_to_colormap(mi_bound, ma_bound, scale=0.01, colormap="Blues_r") vol2 = create_volume_source(dd2, field=("gas", "density")) sc.add_source(vol2) tf = vol2.transfer_function tf.clear() mi, ma = dd2.quantities.extrema(("gas", "density")) mi = np.log10(mi) ma = np.log10(ma) mi_bound = ((ma - mi) * (0.10)) + mi ma_bound = ((ma - mi) * (0.90)) + mi tf.map_to_colormap(mi_bound, ma_bound, scale=0.01, colormap="Reds_r") fname = "test_scene.pdf" sc.save(fname, sigma_clip=6.0) assert_fname(fname) fname = "test_rot.png" sc.camera.pitch(np.pi) sc.render() sc.save(fname, sigma_clip=6.0, render=False) assert_fname(fname) def test_annotations(): from matplotlib.image import imread curdir = os.getcwd() tmpdir = tempfile.mkdtemp() os.chdir(tmpdir) ds = fake_vr_orientation_test_ds(N=16) sc = create_scene(ds) sc.annotate_axes() sc.annotate_domain(ds) sc.render() # ensure that there are actually red, green, blue, and white pixels # in the image. see Issue #1595 im = sc._last_render for c in ([1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1], [1, 1, 1, 1]): assert np.where((im == c).all(axis=-1))[0].shape[0] > 0 sc[0].tfh.tf.add_layers(10, colormap="cubehelix") sc.save_annotated( "test_scene_annotated.png", text_annotate=[[(0.1, 1.05), "test_string"]], ) image = imread("test_scene_annotated.png") assert image.shape == sc.camera.resolution + (4,) os.chdir(curdir) shutil.rmtree(tmpdir) yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_sigma_clip.py000066400000000000000000000016671510711153200304470ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import yt from yt.testing import fake_random_ds def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class SigmaClipTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_sigma_clip(self): ds = fake_random_ds(32) sc = yt.create_scene(ds) sc.save("clip_2.png", sigma_clip=2) yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_simple_vr.py000066400000000000000000000016621510711153200303330ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import yt from yt.testing import fake_random_ds def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class SimpleVRTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_simple_vr(self): ds = fake_random_ds(32) _im, _sc = yt.volume_render(ds, fname="test.png", sigma_clip=4.0) yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_varia.py000066400000000000000000000073401510711153200274340ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np import yt from yt.testing import fake_random_ds from yt.visualization.volume_rendering.api import Scene, create_volume_source def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class VariousVRTests(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None self.ds = fake_random_ds(32) def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) del self.ds def test_simple_scene_creation(self): yt.create_scene(self.ds) def test_modify_transfer_function(self): im, sc = yt.volume_render(self.ds) volume_source = sc.get_source(0) tf = volume_source.transfer_function tf.clear() tf.grey_opacity = True tf.add_layers(3, colormap="RdBu") sc.render() def test_multiple_fields(self): im, sc = yt.volume_render(self.ds) volume_source = sc.get_source(0) volume_source.set_field(("gas", "velocity_x")) volume_source.set_weight_field(("gas", "density")) sc.render() def test_rotation_volume_rendering(self): im, sc = yt.volume_render(self.ds) sc.camera.yaw(np.pi) sc.render() def test_simple_volume_rendering(self): im, sc = yt.volume_render(self.ds, sigma_clip=4.0) def test_lazy_volume_source_construction(self): sc = Scene() source = create_volume_source(self.ds.all_data(), ("gas", "density")) assert source._volume is None assert source._transfer_function is None source.tfh.bounds = (0.1, 1) source.set_log(False) assert not source.log_field assert source.transfer_function.x_bounds == [0.1, 1] assert source._volume is None source.set_log(True) assert source.log_field assert source.transfer_function.x_bounds == [-1, 0] assert source._volume is None source.transfer_function = None source.tfh.bounds = None ad = self.ds.all_data() np.testing.assert_allclose( source.transfer_function.x_bounds, np.log10(ad.quantities.extrema(("gas", "density"))), ) assert source.tfh.log == source.log_field source.set_field(("gas", "velocity_x")) source.set_log(False) assert source.transfer_function.x_bounds == list( ad.quantities.extrema(("gas", "velocity_x")) ) assert source._volume is None source.set_field(("gas", "density")) assert source.volume is not None assert not source.volume._initialized assert source.volume.fields is None del source.volume assert source._volume is None sc.add_source(source) sc.add_camera() sc.render() assert source.volume is not None assert source.volume._initialized assert source.volume.fields == [("gas", "density")] assert source.volume.log_fields == [True] source.set_field(("gas", "velocity_x")) source.set_log(False) sc.render() assert source.volume is not None assert source.volume._initialized assert source.volume.fields == [("gas", "velocity_x")] assert source.volume.log_fields == [False] yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_vr_cameras.py000066400000000000000000000132501510711153200304510ustar00rootroot00000000000000import os import os.path import shutil import tempfile from unittest import TestCase import numpy as np from yt.testing import assert_fname, fake_random_ds from yt.visualization.volume_rendering.api import ( ColorTransferFunction, ProjectionTransferFunction, ) from yt.visualization.volume_rendering.old_camera import ( FisheyeCamera, InteractiveCamera, PerspectiveCamera, ProjectionCamera, StereoPairCamera, ) def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class CameraTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None self.ds = fake_random_ds(64) self.c = self.ds.domain_center self.L = np.array([0.5, 0.5, 0.5]) self.W = 1.5 * self.ds.domain_width self.N = 64 self.field = ("gas", "density") def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def setup_transfer_function(self, camera_type): if camera_type in ["perspective", "camera", "stereopair", "interactive"]: mi, ma = self.ds.all_data().quantities["Extrema"](self.field) tf = ColorTransferFunction((mi, ma), grey_opacity=True) tf.map_to_colormap(mi, ma, scale=10.0, colormap="RdBu_r") return tf elif camera_type in ["healpix"]: return ProjectionTransferFunction() else: pass def test_camera(self): tf = self.setup_transfer_function("camera") cam = self.ds.camera( self.c, self.L, self.W, self.N, transfer_function=tf, log_fields=[False] ) cam.snapshot("camera.png") assert_fname("camera.png") im = cam.snapshot() im = cam.draw_domain(im) cam.draw_coordinate_vectors(im) cam.draw_line(im, [0, 0, 0], [1, 1, 0]) def test_data_source_camera(self): ds = self.ds tf = self.setup_transfer_function("camera") data_source = ds.sphere(ds.domain_center, ds.domain_width[0] * 0.5) cam = ds.camera( self.c, self.L, self.W, self.N, log_fields=[False], transfer_function=tf, data_source=data_source, ) cam.snapshot("data_source_camera.png") assert_fname("data_source_camera.png") def test_perspective_camera(self): ds = self.ds tf = self.setup_transfer_function("camera") cam = PerspectiveCamera( self.c, self.L, self.W, self.N, ds=ds, transfer_function=tf, log_fields=[False], ) cam.snapshot("perspective.png") assert_fname("perspective.png") def test_interactive_camera(self): ds = self.ds tf = self.setup_transfer_function("camera") cam = InteractiveCamera( self.c, self.L, self.W, self.N, ds=ds, transfer_function=tf, log_fields=[False], ) del cam # Can't take a snapshot here since IC uses matplotlib.' def test_projection_camera(self): ds = self.ds cam = ProjectionCamera(self.c, self.L, self.W, self.N, ds=ds, field=self.field) cam.snapshot("projection.png") assert_fname("projection.png") def test_stereo_camera(self): ds = self.ds tf = self.setup_transfer_function("camera") cam = ds.camera( self.c, self.L, self.W, self.N, transfer_function=tf, log_fields=[False] ) stereo_cam = StereoPairCamera(cam) # Take image cam1, cam2 = stereo_cam.split() cam1.snapshot(fn="stereo1.png") cam2.snapshot(fn="stereo2.png") assert_fname("stereo1.png") assert_fname("stereo2.png") def test_camera_movement(self): ds = self.ds tf = self.setup_transfer_function("camera") cam = ds.camera( self.c, self.L, self.W, self.N, transfer_function=tf, log_fields=[False], north_vector=[0.0, 0.0, 1.0], ) cam.zoom(0.5) for snap in cam.zoomin(2.0, 3): snap for snap in cam.move_to( np.array(self.c) + 0.1, 3, final_width=None, exponential=False ): snap for snap in cam.move_to( np.array(self.c) - 0.1, 3, final_width=2.0 * self.W, exponential=False ): snap for snap in cam.move_to( np.array(self.c), 3, final_width=1.0 * self.W, exponential=True ): snap cam.rotate(np.pi / 10) cam.pitch(np.pi / 10) cam.yaw(np.pi / 10) cam.roll(np.pi / 10) for snap in cam.rotation(np.pi, 3, rot_vector=None): snap for snap in cam.rotation(np.pi, 3, rot_vector=np.random.random(3)): snap cam.snapshot("final.png") assert_fname("final.png") def test_fisheye(self): ds = self.ds tf = self.setup_transfer_function("camera") cam = FisheyeCamera( ds.domain_center, ds.domain_width[0], 360.0, 256, transfer_function=tf, ds=ds, ) cam.snapshot("fisheye.png") yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_vr_orientation.py000066400000000000000000000066111510711153200313740ustar00rootroot00000000000000import os import tempfile import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np import pytest from yt.testing import fake_vr_orientation_test_ds from yt.visualization.volume_rendering.api import ( Scene, create_volume_source, off_axis_projection, ) def scene_to_mpl_figure(scene): """helper function to convert a scene image rendering to matplotlib so we can rely on pytest-mpl to compare images """ tmpfd, tmpname = tempfile.mkstemp(suffix=".png") os.close(tmpfd) scene.save(tmpname, sigma_clip=1.0) image = mpl.image.imread(tmpname) os.remove(tmpname) fig, ax = plt.subplots() ax.set(aspect="equal") ax.imshow(image) return fig class TestOrientation: @classmethod def setup_class(cls): cls.ds = fake_vr_orientation_test_ds() cls.scene = Scene() vol = create_volume_source(cls.ds, field=("gas", "density")) cls.scene.add_source(vol) cls._last_lense_type = None @classmethod def set_camera(cls, lens_type): # this method isn't thread-safe # if lens_type == cls._last_lense_type: # return cls._last_lense_type = lens_type cam = cls.scene.add_camera(cls.ds, lens_type=lens_type) cam.resolution = (1000, 1000) cam.position = cls.ds.arr(np.array([-4.0, 0.0, 0.0]), "code_length") cam.switch_orientation( normal_vector=[1.0, 0.0, 0.0], north_vector=[0.0, 0.0, 1.0] ) cam.set_width(cls.ds.domain_width * 2.0) cls.camera = cam @pytest.mark.parametrize("lens_type", ["perspective", "plane-parallel"]) @pytest.mark.mpl_image_compare(remove_text=True) def test_vr_orientation_lense_type(self, lens_type): # note that a previous version of this test proved flaky # and required a much lower precision for plane-parallel # https://github.com/yt-project/yt/issue/3069 # https://github.com/yt-project/yt/pull/3068 # https://github.com/yt-project/yt/pull/3294 self.set_camera(lens_type) return scene_to_mpl_figure(self.scene) @pytest.mark.mpl_image_compare(remove_text=True) def test_vr_orientation_yaw(self): self.set_camera("plane-parallel") center = self.ds.arr([0, 0, 0], "code_length") self.camera.yaw(np.pi, rot_center=center) return scene_to_mpl_figure(self.scene) @pytest.mark.mpl_image_compare(remove_text=True) def test_vr_orientation_pitch(self): self.set_camera("plane-parallel") center = self.ds.arr([0, 0, 0], "code_length") self.camera.pitch(np.pi, rot_center=center) return scene_to_mpl_figure(self.scene) @pytest.mark.mpl_image_compare(remove_text=True) def test_vr_orientation_roll(self): self.set_camera("plane-parallel") center = self.ds.arr([0, 0, 0], "code_length") self.camera.roll(np.pi, rot_center=center) return scene_to_mpl_figure(self.scene) @pytest.mark.mpl_image_compare(remove_text=True) def test_vr_orientation_off_axis_projection(self): image = off_axis_projection( self.ds, center=[0.5, 0.5, 0.5], normal_vector=[-0.3, -0.1, 0.8], width=[1.0, 1.0, 1.0], resolution=512, item=("gas", "density"), no_ghost=False, ) fig, ax = plt.subplots() ax.imshow(image) return fig yt-project-yt-f043ac8/yt/visualization/volume_rendering/tests/test_zbuff.py000066400000000000000000000074151510711153200274510ustar00rootroot00000000000000import os import shutil import tempfile from unittest import TestCase import numpy as np from numpy.testing import assert_almost_equal from yt.testing import fake_random_ds from yt.visualization.volume_rendering.api import ( OpaqueSource, Scene, ZBuffer, create_volume_source, ) class FakeOpaqueSource(OpaqueSource): # A minimal (mock) concrete implementation of OpaqueSource def render(self, camera, zbuffer=None): pass def _validate(self): pass def setup_module(): """Test specific setup.""" from yt.config import ytcfg ytcfg["yt", "internals", "within_testing"] = True class ZBufferTest(TestCase): # This toggles using a temporary directory. Turn off to examine images. use_tmpdir = True def setUp(self): np.random.seed(0) if self.use_tmpdir: self.curdir = os.getcwd() # Perform I/O in safe place instead of yt main dir self.tmpdir = tempfile.mkdtemp() os.chdir(self.tmpdir) else: self.curdir, self.tmpdir = None, None def tearDown(self): if self.use_tmpdir: os.chdir(self.curdir) shutil.rmtree(self.tmpdir) def test_composite_vr(self): ds = fake_random_ds(64) dd = ds.sphere(ds.domain_center, 0.45 * ds.domain_width[0]) ds.field_info[ds.field_list[0]].take_log = False sc = Scene() cam = sc.add_camera(ds) cam.resolution = (512, 512) vr = create_volume_source(dd, field=ds.field_list[0]) vr.transfer_function.clear() vr.transfer_function.grey_opacity = True vr.transfer_function.map_to_colormap(0.0, 1.0, scale=10.0, colormap="Reds") sc.add_source(vr) cam.set_width(1.8 * ds.domain_width) cam.lens.setup_box_properties(cam) # Create Arbitrary Z-buffer empty = cam.lens.new_image(cam) z = np.empty(empty.shape[:2], dtype="float64") # Let's put a blue plane right through the center z[:] = cam.width[2] / 2.0 empty[:, :, 2] = 1.0 # Set blue to 1's empty[:, :, 3] = 1.0 # Set alpha to 1's zbuffer = ZBuffer(empty, z) zsource = FakeOpaqueSource() zsource.set_zbuffer(zbuffer) sc.add_source(zsource) im = sc.render() im.write_png("composite.png") def test_nonrectangular_add(self): rgba1 = np.ones((64, 1, 4)) z1 = np.expand_dims(np.arange(64.0), 1) rgba2 = np.zeros((64, 1, 4)) z2 = np.expand_dims(np.arange(63.0, -1.0, -1.0), 1) exact_rgba = np.concatenate((np.ones(32), np.zeros(32))) exact_rgba = np.expand_dims(exact_rgba, 1) exact_rgba = np.dstack((exact_rgba, exact_rgba, exact_rgba, exact_rgba)) exact_z = np.concatenate((np.arange(32.0), np.arange(31.0, -1.0, -1.0))) exact_z = np.expand_dims(exact_z, 1) buff1 = ZBuffer(rgba1, z1) buff2 = ZBuffer(rgba2, z2) buff = buff1 + buff2 assert_almost_equal(buff.rgba, exact_rgba) assert_almost_equal(buff.z, exact_z) def test_rectangular_add(self): rgba1 = np.ones((8, 8, 4)) z1 = np.arange(64.0) z1 = z1.reshape((8, 8)) buff1 = ZBuffer(rgba1, z1) rgba2 = np.zeros((8, 8, 4)) z2 = np.arange(63.0, -1.0, -1.0) z2 = z2.reshape((8, 8)) buff2 = ZBuffer(rgba2, z2) buff = buff1 + buff2 exact_rgba = np.empty((8, 8, 4), dtype=np.float64) exact_rgba[0:4, 0:8, :] = 1.0 exact_rgba[4:8, 0:8, :] = 0.0 exact_z = np.concatenate((np.arange(32.0), np.arange(31.0, -1.0, -1.0))) exact_z = np.expand_dims(exact_z, 1) exact_z = exact_z.reshape(8, 8) assert_almost_equal(buff.rgba, exact_rgba) assert_almost_equal(buff.z, exact_z) yt-project-yt-f043ac8/yt/visualization/volume_rendering/transfer_function_helper.py000066400000000000000000000153111510711153200312160ustar00rootroot00000000000000from io import BytesIO import numpy as np from yt.data_objects.profiles import create_profile from yt.funcs import mylog from yt.visualization.volume_rendering.transfer_functions import ColorTransferFunction class TransferFunctionHelper: r"""A transfer function helper. This attempts to help set up a good transfer function by finding bounds, handling linear/log options, and displaying the transfer function combined with 1D profiles of rendering quantity. Parameters ---------- ds: A Dataset instance A static output that is currently being rendered. This is used to help set up data bounds. Notes ----- """ profiles = None def __init__(self, ds): self.ds = ds self.field = None self.log = False self.tf = None self.bounds = None self.grey_opacity = False self.profiles = {} def set_bounds(self, bounds=None): """ Set the bounds of the transfer function. Parameters ---------- bounds: array-like, length 2, optional A length 2 list/array in the form [min, max]. These should be the raw values and not the logarithm of the min and max. If bounds is None, the bounds of the data are calculated from all of the data in the dataset. This can be slow for very large datasets. """ if bounds is None: bounds = self.ds.all_data().quantities["Extrema"](self.field, non_zero=True) bounds = [b.ndarray_view() for b in bounds] self.bounds = bounds # Do some error checking. assert len(self.bounds) == 2 if self.log: assert self.bounds[0] > 0.0 assert self.bounds[1] > 0.0 return def set_field(self, field): """ Set the field to be rendered Parameters ---------- field: string The field to be rendered. """ if field != self.field: self.log = self.ds._get_field_info(field).take_log self.field = field def set_log(self, log): """ Set whether or not the transfer function should be in log or linear space. Also modifies the ds.field_info[field].take_log attribute to stay in sync with this setting. Parameters ---------- log: boolean Sets whether the transfer function should use log or linear space. """ self.log = log def build_transfer_function(self): """ Builds the transfer function according to the current state of the TransferFunctionHelper. Returns ------- A ColorTransferFunction object. """ if self.bounds is None: mylog.info( "Calculating data bounds. This may take a while. " "Set the TransferFunctionHelper.bounds to avoid this." ) self.set_bounds() if self.log: mi, ma = np.log10(self.bounds[0]), np.log10(self.bounds[1]) else: mi, ma = self.bounds self.tf = ColorTransferFunction( (mi, ma), grey_opacity=self.grey_opacity, nbins=512 ) return self.tf def setup_default(self): """Setup a default colormap Creates a ColorTransferFunction including 10 gaussian layers whose colors sample the 'nipy_spectral' colormap. Also attempts to scale the transfer function to produce a natural contrast ratio. """ self.tf.add_layers(10, colormap="nipy_spectral") factor = self.tf.funcs[-1].y.size / self.tf.funcs[-1].y.sum() self.tf.funcs[-1].y *= 2 * factor def plot(self, fn=None, profile_field=None, profile_weight=None): """ Save the current transfer function to a bitmap, or display it inline. Parameters ---------- fn: string, optional Filename to save the image to. If None, the returns an image to an IPython session. Returns ------- If fn is None, will return an image to an IPython notebook. """ from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.figure import Figure if self.tf is None: self.build_transfer_function() self.setup_default() tf = self.tf if self.log: xfunc = np.logspace xmi, xma = np.log10(self.bounds[0]), np.log10(self.bounds[1]) else: xfunc = np.linspace xmi, xma = self.bounds x = xfunc(xmi, xma, tf.nbins) y = tf.funcs[3].y w = np.append(x[1:] - x[:-1], x[-1] - x[-2]) colors = np.array( [tf.funcs[0].y, tf.funcs[1].y, tf.funcs[2].y, np.ones_like(x)] ).T fig = Figure(figsize=[6, 3]) canvas = FigureCanvasAgg(fig) ax = fig.add_axes([0.2, 0.2, 0.75, 0.75]) ax.bar( x, tf.funcs[3].y, w, edgecolor=[0.0, 0.0, 0.0, 0.0], log=self.log, color=colors, bottom=[0], ) if profile_field is not None: try: prof = self.profiles[self.field] except KeyError: self.setup_profile(profile_field, profile_weight) prof = self.profiles[self.field] try: prof[profile_field] except KeyError: prof.add_fields([profile_field]) xplot = prof.x yplot = ( prof[profile_field] * tf.funcs[3].y.max() / prof[profile_field].max() ) ax.plot(xplot, yplot, color="w", linewidth=3) ax.plot(xplot, yplot, color="k") ax.set_xscale({True: "log", False: "linear"}[self.log]) ax.set_xlim(x.min(), x.max()) ax.set_xlabel(self.ds._get_field_info(self.field).get_label()) ax.set_ylabel(r"$\mathrm{alpha}$") ax.set_ylim(y.max() * 1.0e-3, y.max() * 2) if fn is None: from IPython.core.display import Image f = BytesIO() canvas.print_figure(f) f.seek(0) img = f.read() return Image(img) else: fig.savefig(fn) def setup_profile(self, profile_field=None, profile_weight=None): if profile_field is None: profile_field = "cell_volume" prof = create_profile( self.ds.all_data(), self.field, profile_field, n_bins=128, extrema={self.field: self.bounds}, weight_field=profile_weight, logs={self.field: self.log}, ) self.profiles[self.field] = prof return yt-project-yt-f043ac8/yt/visualization/volume_rendering/transfer_functions.py000066400000000000000000001125711510711153200300500ustar00rootroot00000000000000import numpy as np from more_itertools import always_iterable from yt.funcs import mylog from yt.utilities.physical_constants import clight, hcgs, kboltz class TransferFunction: r"""A transfer function governs the transmission of emission and absorption through a volume. Transfer functions are defined by boundaries, bins, and the value that governs transmission through that bin. This is scaled between 0 and 1. When integrating through a volume the value through a given cell is defined by the value calculated in the transfer function. Parameters ---------- x_bounds : tuple of floats The min and max for the transfer function. Values below or above these values are discarded. nbins : int How many bins to calculate; in between, linear interpolation is used, so low values are typically fine. Notes ----- Typically, raw transfer functions are not generated unless particular and specific control over the integration is desired. Usually either color transfer functions, where the color values are calculated from color tables, or multivariate transfer functions are used. """ def __init__(self, x_bounds, nbins=256): self.pass_through = 0 self.nbins = nbins # Strip units off of x_bounds, if any x_bounds = [np.float64(xb) for xb in x_bounds] self.x_bounds = x_bounds self.x = np.linspace(x_bounds[0], x_bounds[1], nbins, dtype="float64") self.y = np.zeros(nbins, dtype="float64") self.grad_field = -1 self.light_source_v = self.light_source_c = np.zeros(3, "float64") self.features = [] def add_gaussian(self, location, width, height): r"""Add a Gaussian distribution to the transfer function. Typically, when rendering isocontours, a Gaussian distribution is the easiest way to draw out features. The spread provides a softness. The values are calculated as :math:`f(x) = h \exp{-(x-x_0)^2 / w}`. Parameters ---------- location : float The centroid of the Gaussian (:math:`x_0` in the above equation.) width : float The relative width (:math:`w` in the above equation.) height : float The peak height (:math:`h` in the above equation.) Note that while values greater 1.0 will be accepted, the values of the transmission function are clipped at 1.0. Examples -------- >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-9.0, 0.01, 1.0) """ vals = height * np.exp(-((self.x - location) ** 2.0) / width) self.y = np.clip(np.maximum(vals, self.y), 0.0, np.inf) self.features.append( ( "gaussian", f"location(x):{location:3.2g}", f"width(x):{width:3.2g}", f"height(y):{height:3.2g}", ) ) def add_line(self, start, stop): r"""Add a line between two points to the transmission function. This will accept a starting point in (x,y) and an ending point in (x,y) and set the values of the transmission function between those x-values to be along the line connecting the y values. Parameters ---------- start : tuple of floats (x0, y0), the starting point. x0 is between the bounds of the transfer function and y0 must be between 0.0 and 1.0. stop : tuple of floats (x1, y1), the ending point. x1 is between the bounds of the transfer function and y1 must be between 0.0 and 1.0. Examples -------- This will set the transfer function to be linear from 0.0 to 1.0, across the bounds of the function. >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_line((-10.0, 0.0), (-5.0, 1.0)) """ x0, y0 = start x1, y1 = stop slope = (y1 - y0) / (x1 - x0) # We create a whole new set of values and then backout the ones that do # not satisfy our bounding box arguments vals = slope * (self.x - x0) + y0 vals[~((self.x >= x0) & (self.x <= x1))] = 0.0 self.y = np.clip(np.maximum(vals, self.y), 0.0, np.inf) self.features.append( ( "line", f"start(x,y):({start[0]:3.2g}, {start[1]:3.2g})", f"stop(x,y):({stop[0]:3.2g}, {stop[1]:3.2g})", ) ) def add_step(self, start, stop, value): r"""Adds a step function to the transfer function. This accepts a `start` and a `stop`, and then in between those points the transfer function is set to the maximum of the transfer function and the `value`. Parameters ---------- start : float This is the beginning of the step function; must be within domain of the transfer function. stop : float This is the ending of the step function; must be within domain of the transfer function. value : float The value the transfer function will be set to between `start` and `stop`. Note that the transfer function will *actually* be set to max(y, value) where y is the existing value of the transfer function. Examples -------- Note that in this example, we have added a step function, but the Gaussian that already exists will "win" where it exceeds 0.5. >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-7.0, 0.01, 1.0) >>> tf.add_step(-8.0, -6.0, 0.5) """ vals = np.zeros(self.x.shape, "float64") vals[(self.x >= start) & (self.x <= stop)] = value self.y = np.clip(np.maximum(vals, self.y), 0.0, np.inf) self.features.append( ( "step", f"start(x):{start:3.2g}", f"stop(x):{stop:3.2g}", f"value(y):{value:3.2g}", ) ) def add_filtered_planck(self, wavelength, trans): from yt._maintenance.numpy2_compat import trapezoid vals = np.zeros(self.x.shape, "float64") nu = clight / (wavelength * 1e-8) nu = nu[::-1] for i, logT in enumerate(self.x): T = 10**logT # Black body at this nu, T Bnu = ((2.0 * hcgs * nu**3) / clight**2.0) / ( np.exp(hcgs * nu / (kboltz * T)) - 1.0 ) # transmission f = Bnu * trans[::-1] # integrate transmission over nu vals[i] = trapezoid(f, nu) # normalize by total transmission over filter self.y = vals / trans.sum() # self.y = np.clip(np.maximum(vals, self.y), 0.0, 1.0) def plot(self, filename): r"""Save an image file of the transfer function. This function loads up matplotlib, plots the transfer function and saves. Parameters ---------- filename : string The file to save out the plot as. Examples -------- >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-9.0, 0.01, 1.0) >>> tf.plot("sample.png") """ import matplotlib import matplotlib.pyplot as plt matplotlib.use("Agg") plt.clf() plt.plot(self.x, self.y, "xk-") plt.xlim(*self.x_bounds) plt.ylim(0.0, 1.0) plt.savefig(filename) def show(self): r"""Display an image of the transfer function This function loads up matplotlib and displays the current transfer function. Parameters ---------- Examples -------- >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-9.0, 0.01, 1.0) >>> tf.show() """ import matplotlib.pyplot as plt plt.clf() plt.plot(self.x, self.y, "xk-") plt.xlim(*self.x_bounds) plt.ylim(0.0, 1.0) plt.draw() def clear(self): self.y[:] = 0.0 self.features = [] def __repr__(self): disp = ( ": " f"x_bounds:({self.x_bounds[0]:3.2g}, {self.x_bounds[1]:3.2g}) " f"nbins:{self.nbins:3.2g} features:{self.features}" ) return disp class MultiVariateTransferFunction: r"""This object constructs a set of field tables that allow for multiple field variables to control the integration through a volume. The integration through a volume typically only utilizes a single field variable (for instance, Density) to set up and control the values returned at the end of the integration. For things like isocontours, this is fine. However, more complicated schema are possible by using this object. For instance, density-weighted emission that produces colors based on the temperature of the fluid. Parameters ---------- grey_opacity : bool Should opacity be calculated on a channel-by-channel basis, or overall? Useful for opaque renderings. Default: False """ def __init__(self, grey_opacity=False): self.n_field_tables = 0 self.tables = [] # Tables are interpolation tables self.field_ids = [0] * 6 # This correlates fields with tables self.weight_field_ids = [-1] * 6 # This correlates self.field_table_ids = [0] * 6 self.weight_table_ids = [-1] * 6 self.grad_field = -1 self.light_source_v = self.light_source_c = np.zeros(3, "float64") self.grey_opacity = grey_opacity def add_field_table(self, table, field_id, weight_field_id=-1, weight_table_id=-1): r"""This accepts a table describing integration. A "field table" is a tabulated set of values that govern the integration through a given field. These are defined not only by the transmission coefficient, interpolated from the table itself, but the `field_id` that describes which of several fields the integration coefficient is to be calculated from. Parameters ---------- table : `TransferFunction` The integration table to be added to the set of tables used during the integration. field_id : int Each volume has an associated set of fields. This identifies which of those fields will be used to calculate the integration coefficient from this table. weight_field_id : int, optional If specified, the value of the field this identifies will be multiplied against the integration coefficient. weight_table_id : int, optional If specified, the value from the *table* this identifies will be multiplied against the integration coefficient. Notes ----- This can be rather complicated. It's recommended that if you are interested in manipulating this in detail that you examine the source code, specifically the function FIT_get_value in yt/_amr_utils/VolumeIntegrator.pyx. Examples -------- This example shows how to link a new transfer function against field 0. Note that this by itself does not link a *channel* for integration against a field. This is because the weighting system does not mandate that all tables contribute to a channel, only that they contribute a value which may be used by other field tables. >>> mv = MultiVariateTransferFunction() >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-7.0, 0.01, 1.0) >>> mv.add_field_table(tf, 0) """ self.tables.append(table) self.field_ids[self.n_field_tables] = field_id self.weight_field_ids[self.n_field_tables] = weight_field_id self.weight_table_ids[self.n_field_tables] = weight_table_id self.n_field_tables += 1 def link_channels(self, table_id, channels=0): r"""Link an image channel to a field table. Once a field table has been added, it can be linked against a channel (any one of the six -- red, green, blue, red absorption, green absorption, blue absorption) and then the value calculated for that field table will be added to the integration for that channel. Not all tables must be linked against channels. Parameters ---------- table_id : int The 0-indexed table to link. channels : int or list of ints The channel or channels to link with this table's calculated value. Examples -------- This example shows how to link a new transfer function against field 0, and then link that table against all three RGB channels. Typically an absorption (or 'alpha') channel is also linked. >>> mv = MultiVariateTransferFunction() >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-7.0, 0.01, 1.0) >>> mv.add_field_table(tf, 0) >>> mv.link_channels(0, [0, 1, 2]) """ for c in always_iterable(channels): self.field_table_ids[c] = table_id class ColorTransferFunction(MultiVariateTransferFunction): r"""A complete set of transfer functions for standard color-mapping. This is the best and easiest way to set up volume rendering. It creates field tables for all three colors, their alphas, and has support for sampling color maps and adding independent color values at all locations. It will correctly set up the `MultiVariateTransferFunction`. Parameters ---------- x_bounds : tuple of floats The min and max for the transfer function. Values below or above these values are discarded. nbins : int How many bins to calculate; in between, linear interpolation is used, so low values are typically fine. grey_opacity : bool Should opacity be calculated on a channel-by-channel basis, or overall? Useful for opaque renderings. """ def __init__(self, x_bounds, nbins=256, grey_opacity=False): MultiVariateTransferFunction.__init__(self) # Strip units off of x_bounds, if any x_bounds = [np.float64(xb) for xb in x_bounds] self.x_bounds = x_bounds self.nbins = nbins # This is all compatibility and convenience. self.red = TransferFunction(x_bounds, nbins) self.green = TransferFunction(x_bounds, nbins) self.blue = TransferFunction(x_bounds, nbins) self.alpha = TransferFunction(x_bounds, nbins) self.funcs = (self.red, self.green, self.blue, self.alpha) self.grey_opacity = grey_opacity self.features = [] # Now we do the multivariate stuff # We assign to Density, but do not weight for i, tf in enumerate(self.funcs[:3]): self.add_field_table(tf, 0, weight_table_id=3) self.link_channels(i, i) self.add_field_table(self.funcs[3], 0) self.link_channels(3, 3) # We don't have a fifth table, so the value will *always* be zero. # self.link_channels(4, [3,4,5]) def add_gaussian(self, location, width, height): r"""Add a Gaussian distribution to the transfer function. Typically, when rendering isocontours, a Gaussian distribution is the easiest way to draw out features. The spread provides a softness. The values are calculated as :math:`f(x) = h \exp{-(x-x_0)^2 / w}`. Parameters ---------- location : float The centroid of the Gaussian (:math:`x_0` in the above equation.) width : float The relative width (:math:`w` in the above equation.) height : list of 4 float The peak height (:math:`h` in the above equation.) Note that while values greater 1.0 will be accepted, the values of the transmission function are clipped at 1.0. This must be a list, and it is in the order of (red, green, blue, alpha). Examples -------- This adds a red spike. >>> tf = ColorTransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-9.0, 0.01, [1.0, 0.0, 0.0, 1.0]) """ for tf, v in zip(self.funcs, height, strict=True): tf.add_gaussian(location, width, v) self.features.append( ( "gaussian", f"location(x):{location:3.2g}", f"width(x):{width:3.2g}", f"height(y):({height[0]:3.2g}, {height[1]:3.2g}, {height[2]:3.2g}, {height[3]:3.2g})", ) ) def add_step(self, start, stop, value): r"""Adds a step function to the transfer function. This accepts a `start` and a `stop`, and then in between those points the transfer function is set to the maximum of the transfer function and the `value`. Parameters ---------- start : float This is the beginning of the step function; must be within domain of the transfer function. stop : float This is the ending of the step function; must be within domain of the transfer function. value : list of 4 floats The value the transfer function will be set to between `start` and `stop`. Note that the transfer function will *actually* be set to max(y, value) where y is the existing value of the transfer function. This must be a list, and it is in the order of (red, green, blue, alpha). Examples -------- This adds a step function that will produce a white value at > -6.0. >>> tf = ColorTransferFunction((-10.0, -5.0)) >>> tf.add_step(-6.0, -5.0, [1.0, 1.0, 1.0, 1.0]) """ for tf, v in zip(self.funcs, value, strict=True): tf.add_step(start, stop, v) self.features.append( ( "step", f"start(x):{start:3.2g}", f"stop(x):{stop:3.2g}", f"value(y):({value[0]:3.2g}, {value[1]:3.2g}, {value[2]:3.2g}, {value[3]:3.2g})", ) ) def plot(self, filename): r"""Save an image file of the transfer function. This function loads up matplotlib, plots all of the constituent transfer functions and saves. Parameters ---------- filename : string The file to save out the plot as. Examples -------- >>> tf = ColorTransferFunction((-10.0, -5.0)) >>> tf.add_layers(8) >>> tf.plot("sample.png") """ from matplotlib import pyplot from matplotlib.ticker import FuncFormatter pyplot.clf() ax = pyplot.axes() i_data = np.zeros((self.alpha.x.size, self.funcs[0].y.size, 3)) i_data[:, :, 0] = np.outer(np.ones(self.alpha.x.size), self.funcs[0].y) i_data[:, :, 1] = np.outer(np.ones(self.alpha.x.size), self.funcs[1].y) i_data[:, :, 2] = np.outer(np.ones(self.alpha.x.size), self.funcs[2].y) ax.imshow(i_data, origin="lower") ax.fill_between( np.arange(self.alpha.y.size), self.alpha.x.size * self.alpha.y, y2=self.alpha.x.size, color="white", ) ax.set_xlim(0, self.alpha.x.size) xticks = ( np.arange(np.ceil(self.alpha.x[0]), np.floor(self.alpha.x[-1]) + 1, 1) - self.alpha.x[0] ) xticks *= (self.alpha.x.size - 1) / (self.alpha.x[-1] - self.alpha.x[0]) ax.xaxis.set_ticks(xticks) def x_format(x, pos): return "%.1f" % ( x * (self.alpha.x[-1] - self.alpha.x[0]) / (self.alpha.x.size - 1) + self.alpha.x[0] ) ax.xaxis.set_major_formatter(FuncFormatter(x_format)) yticks = np.linspace(0, 1, 5) * self.alpha.y.size ax.yaxis.set_ticks(yticks) def y_format(y, pos): return y / self.alpha.y.size ax.yaxis.set_major_formatter(FuncFormatter(y_format)) ax.set_ylabel("Transmission") ax.set_xlabel("Value") pyplot.savefig(filename) def show(self, ax=None): r"""Display an image of the transfer function This function loads up matplotlib and displays the current transfer function. Parameters ---------- Examples -------- >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-9.0, 0.01, 1.0) >>> tf.show() """ from matplotlib import pyplot from matplotlib.ticker import FuncFormatter pyplot.clf() ax = pyplot.axes() i_data = np.zeros((self.alpha.x.size, self.funcs[0].y.size, 3)) i_data[:, :, 0] = np.outer(np.ones(self.alpha.x.size), self.funcs[0].y) i_data[:, :, 1] = np.outer(np.ones(self.alpha.x.size), self.funcs[1].y) i_data[:, :, 2] = np.outer(np.ones(self.alpha.x.size), self.funcs[2].y) ax.imshow(i_data, origin="lower") ax.fill_between( np.arange(self.alpha.y.size), self.alpha.x.size * self.alpha.y, y2=self.alpha.x.size, color="white", ) ax.set_xlim(0, self.alpha.x.size) xticks = ( np.arange(np.ceil(self.alpha.x[0]), np.floor(self.alpha.x[-1]) + 1, 1) - self.alpha.x[0] ) xticks *= (self.alpha.x.size - 1) / (self.alpha.x[-1] - self.alpha.x[0]) if len(xticks) > 5: xticks = xticks[:: len(xticks) // 5] ax.xaxis.set_ticks(xticks) def x_format(x, pos): return "%.1f" % ( x * (self.alpha.x[-1] - self.alpha.x[0]) / (self.alpha.x.size - 1) + self.alpha.x[0] ) ax.xaxis.set_major_formatter(FuncFormatter(x_format)) yticks = np.linspace(0, 1, 5) * self.alpha.y.size ax.yaxis.set_ticks(yticks) def y_format(y, pos): s = f"{y:0.2f}" return s ax.yaxis.set_major_formatter(FuncFormatter(y_format)) ax.set_ylabel("Opacity") ax.set_xlabel("Value") def vert_cbar( self, resolution, log_scale, ax, label=None, label_fmt=None, *, label_fontsize=10, size=10, ): r"""Display an image of the transfer function This function loads up matplotlib and displays the current transfer function. Parameters ---------- Examples -------- >>> tf = TransferFunction((-10.0, -5.0)) >>> tf.add_gaussian(-9.0, 0.01, 1.0) >>> tf.show() """ from matplotlib.ticker import FuncFormatter if label is None: label = "" alpha = self.alpha.y max_alpha = alpha.max() i_data = np.zeros((self.alpha.x.size, self.funcs[0].y.size, 3)) i_data[:, :, 0] = np.outer(self.funcs[0].y, np.ones(self.alpha.x.size)) i_data[:, :, 1] = np.outer(self.funcs[1].y, np.ones(self.alpha.x.size)) i_data[:, :, 2] = np.outer(self.funcs[2].y, np.ones(self.alpha.x.size)) ax.imshow(i_data, origin="lower", aspect="auto") ax.plot(alpha, np.arange(self.alpha.y.size), "w") # Set TF limits based on what is visible visible = np.argwhere(self.alpha.y > 1.0e-3 * self.alpha.y.max()) # Display colobar values xticks = ( np.arange(np.ceil(self.alpha.x[0]), np.floor(self.alpha.x[-1]) + 1, 1) - self.alpha.x[0] ) xticks *= (self.alpha.x.size - 1) / (self.alpha.x[-1] - self.alpha.x[0]) if len(xticks) > 5: xticks = xticks[:: len(xticks) // 5] # Add colorbar limits to the ticks (May not give ideal results) xticks = np.append(visible[0], xticks) xticks = np.append(visible[-1], xticks) # remove dupes xticks = list(set(xticks)) ax.yaxis.set_ticks(xticks) def x_format(x, pos): val = ( x * (self.alpha.x[-1] - self.alpha.x[0]) / (self.alpha.x.size - 1) + self.alpha.x[0] ) if log_scale: val = 10**val if label_fmt is None: if abs(val) < 1.0e-3 or abs(val) > 1.0e4: if not val == 0.0: e = np.floor(np.log10(abs(val))) return rf"${val / 10.0**e:.2f}\times 10^{{ {int(e):d} }}$" else: return r"$0$" else: return f"{val:.1g}" else: return label_fmt % (val) ax.yaxis.set_major_formatter(FuncFormatter(x_format)) yticks = np.linspace(0, 1, 2, endpoint=True) * max_alpha ax.xaxis.set_ticks(yticks) def y_format(y, pos): s = f"{y:0.2f}" return s ax.xaxis.set_major_formatter(FuncFormatter(y_format)) ax.set_xlim(0.0, max_alpha) ax.get_xaxis().set_ticks([]) ax.set_ylim(visible[0].item(), visible[-1].item()) ax.tick_params(axis="y", colors="white", labelsize=label_fontsize) ax.set_ylabel(label, color="white", size=size * resolution / 512.0) def sample_colormap(self, v, w, alpha=None, colormap="gist_stern", col_bounds=None): r"""Add a Gaussian based on an existing colormap. Constructing pleasing Gaussians in a transfer function can pose some challenges, so this function will add a single Gaussian whose colors are taken from a colormap scaled between the bounds of the transfer function. As with `TransferFunction.add_gaussian`, the value is calculated as :math:`f(x) = h \exp{-(x-x_0)^2 / w}` but with the height for each color calculated from the colormap. Parameters ---------- v : float The value at which the Gaussian is to be added. w : float The relative width (:math:`w` in the above equation.) alpha : float, optional The alpha value height for the Gaussian colormap : string, optional An acceptable colormap. See either yt.visualization.color_maps or https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html . col_bounds: array_like, optional Limits ([min, max]) the values over which the colormap spans to these values. Useful for sampling an entire colormap over a range smaller than the transfer function bounds. See Also -------- ColorTransferFunction.add_layers : Many-at-a-time adder Examples -------- >>> tf = ColorTransferFunction((-10.0, -5.0)) >>> tf.sample_colormap(-7.0, 0.01, colormap="cmyt.arbre") """ import matplotlib as mpl v = np.float64(v) if col_bounds is None: rel = (v - self.x_bounds[0]) / (self.x_bounds[1] - self.x_bounds[0]) else: rel = (v - col_bounds[0]) / (col_bounds[1] - col_bounds[0]) cmap = mpl.colormaps[colormap] r, g, b, a = cmap(rel) if alpha is None: alpha = a self.add_gaussian(v, w, [r, g, b, alpha]) mylog.debug( "Adding gaussian at %s with width %s and colors %s", v, w, (r, g, b, alpha) ) def map_to_colormap( self, mi, ma, scale=1.0, colormap="gist_stern", scale_func=None ): r"""Map a range of values to a full colormap. Given a minimum and maximum value in the TransferFunction, map a full colormap over that range at an alpha level of `scale`. Optionally specify a scale_func function that modifies the alpha as a function of the transfer function value. Parameters ---------- mi : float The start of the TransferFunction to map the colormap ma : float The end of the TransferFunction to map the colormap scale: float, optional The alpha value to be used for the height of the transfer function. Larger values will be more opaque. colormap : string, optional An acceptable colormap. See either yt.visualization.color_maps or https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html . scale_func: function(:obj:`!value`, :obj:`!minval`, :obj:`!maxval`), optional A user-defined function that can be used to scale the alpha channel as a function of the TransferFunction field values. Function maps value to somewhere between minval and maxval. Examples -------- >>> def linramp(vals, minval, maxval): ... return (vals - vals.min()) / (vals.max() - vals.min()) >>> tf = ColorTransferFunction((-10.0, -5.0)) >>> tf.map_to_colormap(-8.0, -6.0, scale=10.0, colormap="cmyt.arbre") >>> tf.map_to_colormap( ... -6.0, -5.0, scale=10.0, colormap="cmyt.arbre", scale_func=linramp ... ) """ import matplotlib as mpl mi = np.float64(mi) ma = np.float64(ma) rel0 = int( self.nbins * (mi - self.x_bounds[0]) / (self.x_bounds[1] - self.x_bounds[0]) ) rel1 = int( self.nbins * (ma - self.x_bounds[0]) / (self.x_bounds[1] - self.x_bounds[0]) ) rel0 = max(rel0, 0) rel1 = min(rel1, self.nbins - 1) + 1 tomap = np.linspace(0.0, 1.0, num=rel1 - rel0) cmap = mpl.colormaps[colormap] cc = cmap(tomap) if scale_func is None: scale_mult = 1.0 else: scale_mult = scale_func(tomap, 0.0, 1.0) self.red.y[rel0:rel1] = cc[:, 0] * scale_mult self.green.y[rel0:rel1] = cc[:, 1] * scale_mult self.blue.y[rel0:rel1] = cc[:, 2] * scale_mult self.alpha.y[rel0:rel1] = scale * cc[:, 3] * scale_mult self.features.append( ( "map_to_colormap", f"start(x):{mi:3.2g}", f"stop(x):{ma:3.2g}", f"value(y):{scale:3.2g}", ) ) def add_layers( self, N, w=None, mi=None, ma=None, alpha=None, colormap="gist_stern", col_bounds=None, ): r"""Add a set of Gaussians based on an existing colormap. Constructing pleasing Gaussians in a transfer function can pose some challenges, so this function will add several evenly-spaced Gaussians whose colors are taken from a colormap scaled between the bounds of the transfer function. For each Gaussian to be added, `ColorTransferFunction.sample_colormap` is called. Parameters ---------- N : int How many Gaussians to add w : float The relative width of each Gaussian. If not included, it is calculated as 0.001 * (max_val - min_val) / N mi : float, optional If only a subset of the data range is to have the Gaussians added, this is the minimum for that subset ma : float, optional If only a subset of the data range is to have the Gaussians added, this is the maximum for that subset alpha : list of floats, optional The alpha value height for each Gaussian. If not supplied, it is set as 1.0 everywhere. colormap : string, optional An acceptable colormap. See either yt.visualization.color_maps or https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html . col_bounds: array_like, optional Limits ([min, max]) the values over which the colormap spans to these values. Useful for sampling an entire colormap over a range smaller than the transfer function bounds. See Also -------- ColorTransferFunction.sample_colormap : Single Gaussian adder Examples -------- >>> tf = ColorTransferFunction((-10.0, -5.0)) >>> tf.add_layers(8) """ if col_bounds is None: dist = self.x_bounds[1] - self.x_bounds[0] if mi is None: mi = self.x_bounds[0] + dist / (10.0 * N) if ma is None: ma = self.x_bounds[1] - dist / (10.0 * N) else: dist = col_bounds[1] - col_bounds[0] if mi is None: mi = col_bounds[0] + dist / (10.0 * N) if ma is None: ma = col_bounds[1] - dist / (10.0 * N) if w is None: w = 0.001 * (ma - mi) / N w = max(w, 1.0 / self.nbins) if alpha is None and self.grey_opacity: alpha = np.ones(N, dtype="float64") elif alpha is None and not self.grey_opacity: alpha = np.logspace(-3, 0, N) for v, a in zip(np.mgrid[mi : ma : N * 1j], alpha, strict=True): self.sample_colormap(v, w, a, colormap=colormap, col_bounds=col_bounds) def get_colormap_image(self, height, width): image = np.zeros((height, width, 3), dtype="uint8") hvals = np.mgrid[self.x_bounds[0] : self.x_bounds[1] : height * 1j] for i, f in enumerate(self.funcs[:3]): vals = np.interp(hvals, f.x, f.y) image[:, :, i] = (vals[:, None] * 255).astype("uint8") image = image[::-1, :, :] return image def clear(self): for f in self.funcs: f.clear() self.features = [] def __repr__(self): disp = ( ":\n" + "x_bounds:[%3.2g, %3.2g] nbins:%i features:\n" % (self.x_bounds[0], self.x_bounds[1], self.nbins) ) for f in self.features: disp += f"\t{str(f)}\n" return disp class ProjectionTransferFunction(MultiVariateTransferFunction): r"""A transfer function that defines a simple projection. To generate an interpolated, off-axis projection through a dataset, this transfer function should be used. It will create a very simple table that merely sums along each ray. Note that the end product will need to be scaled by the total width through which the rays were cast, a piece of information inaccessible to the transfer function. Parameters ---------- x_bounds : tuple of floats, optional If any of your values lie outside this range, they will be truncated. n_fields : int, optional How many fields we're going to project and pass through Notes ----- When you use this transfer function, you may need to explicitly disable logging of fields. """ def __init__(self, x_bounds=(-1e60, 1e60), n_fields=1): if n_fields > 3: raise NotImplementedError( f"supplied ${n_fields} but n_fields > 3 not implemented." ) MultiVariateTransferFunction.__init__(self) # Strip units off of x_bounds, if any x_bounds = [np.float64(xb) for xb in x_bounds] self.x_bounds = x_bounds self.nbins = 2 self.linear_mapping = TransferFunction(x_bounds, 2) self.linear_mapping.pass_through = 1 self.link_channels(0, [0, 1, 2]) # same emission for all rgb, default for i in range(n_fields): self.add_field_table(self.linear_mapping, i) self.link_channels(i, i) self.link_channels(n_fields, [3, 4, 5]) # this will remove absorption class PlanckTransferFunction(MultiVariateTransferFunction): """ This sets up a planck function for multivariate emission and absorption. We assume that the emission is black body, which is then convolved with appropriate Johnson filters for *red*, *green* and *blue*. *T_bounds* and *rho_bounds* define the limits of tabulated emission and absorption functions. *anorm* is a "fudge factor" that defines the somewhat arbitrary normalization to the scattering approximation: because everything is done largely unit-free, and is really not terribly accurate anyway, feel free to adjust this to change the relative amount of reddening. Maybe in some future version this will be unitful. """ def __init__( self, T_bounds, rho_bounds, nbins=256, red="R", green="V", blue="B", anorm=1e6 ): MultiVariateTransferFunction.__init__(self) mscat = -1 from .UBVRI import johnson_filters for i, f in enumerate([red, green, blue]): jf = johnson_filters[f] tf = TransferFunction(T_bounds) tf.add_filtered_planck(jf["wavelen"], jf["trans"]) self.add_field_table(tf, 0, 1) self.link_channels(i, i) # 0 => 0, 1 => 1, 2 => 2 mscat = max(mscat, jf["Lchar"] ** -4) for i, f in enumerate([red, green, blue]): # Now we set up the scattering scat = (johnson_filters[f]["Lchar"] ** -4 / mscat) * anorm tf = TransferFunction(rho_bounds) mylog.debug("Adding: %s with relative scattering %s", f, scat) tf.y *= 0.0 tf.y += scat self.add_field_table(tf, 1, weight_field_id=1) self.link_channels(i + 3, i + 3) self._normalize() self.grey_opacity = False def _normalize(self): fmax = np.array([f.y for f in self.tables[:3]]) normal = fmax.max(axis=0) for f in self.tables[:3]: f.y = f.y / normal if __name__ == "__main__": tf = ColorTransferFunction((-20, -5)) tf.add_gaussian(-16.0, 0.4, [0.2, 0.3, 0.1]) tf.add_gaussian(-14.0, 0.8, [0.4, 0.1, 0.2]) tf.add_gaussian(-10.0, 1.0, [0.0, 0.0, 1.0]) tf.plot("tf.png") yt-project-yt-f043ac8/yt/visualization/volume_rendering/utils.py000066400000000000000000000127541510711153200252760ustar00rootroot00000000000000import numpy as np from yt.data_objects.selection_objects.data_selection_objects import ( YTSelectionContainer3D, ) from yt.data_objects.static_output import Dataset from yt.utilities.lib import bounding_volume_hierarchy from yt.utilities.lib.image_samplers import ( InterpolatedProjectionSampler, ProjectionSampler, VolumeRenderSampler, ) from yt.utilities.on_demand_imports import NotAModule try: from yt.utilities.lib.embree_mesh import mesh_traversal # type: ignore # Catch ValueError in case size of objects in Cython change except (ImportError, ValueError): mesh_traversal = NotAModule("pyembree") def data_source_or_all(data_source): if isinstance(data_source, Dataset): data_source = data_source.all_data() if not isinstance(data_source, (YTSelectionContainer3D, type(None))): raise RuntimeError( "The data_source is not a valid 3D data container.\n" "Expected an object of type YTSelectionContainer3D but received " f"an object of type {type(data_source)}." ) return data_source def new_mesh_sampler(camera, render_source, engine): params = ensure_code_unit_params(camera._get_sampler_params(render_source)) args = ( np.atleast_3d(params["vp_pos"]), np.atleast_3d(params["vp_dir"]), params["center"], params["bounds"], np.atleast_3d(params["image"]).astype("float64"), params["x_vec"], params["y_vec"], params["width"], render_source.volume_method, ) kwargs = {"lens_type": params["lens_type"]} if engine == "embree": sampler = mesh_traversal.EmbreeMeshSampler(*args, **kwargs) elif engine == "yt": sampler = bounding_volume_hierarchy.BVHMeshSampler(*args, **kwargs) return sampler def new_volume_render_sampler(camera, render_source): params = ensure_code_unit_params(camera._get_sampler_params(render_source)) params.update(transfer_function=render_source.transfer_function) params.update(transfer_function=render_source.transfer_function) params.update(num_samples=render_source.num_samples) args = ( np.atleast_3d(params["vp_pos"]), np.atleast_3d(params["vp_dir"]), params["center"], params["bounds"], params["image"], params["x_vec"], params["y_vec"], params["width"], render_source.volume_method, params["transfer_function"], params["num_samples"], ) kwargs = { "lens_type": params["lens_type"], } if "camera_data" in params: kwargs["camera_data"] = params["camera_data"] if render_source.zbuffer is not None: kwargs["zbuffer"] = render_source.zbuffer.z args[4][:] = np.reshape( render_source.zbuffer.rgba[:], (camera.resolution[0], camera.resolution[1], 4), ) else: kwargs["zbuffer"] = np.ones(params["image"].shape[:2], "float64") sampler = VolumeRenderSampler(*args, **kwargs) return sampler def new_interpolated_projection_sampler(camera, render_source): params = ensure_code_unit_params(camera._get_sampler_params(render_source)) params.update(transfer_function=render_source.transfer_function) params.update(num_samples=render_source.num_samples) args = ( np.atleast_3d(params["vp_pos"]), np.atleast_3d(params["vp_dir"]), params["center"], params["bounds"], params["image"], params["x_vec"], params["y_vec"], params["width"], render_source.volume_method, params["num_samples"], ) kwargs = {"lens_type": params["lens_type"]} if render_source.zbuffer is not None: kwargs["zbuffer"] = render_source.zbuffer.z else: kwargs["zbuffer"] = np.ones(params["image"].shape[:2], "float64") sampler = InterpolatedProjectionSampler(*args, **kwargs) return sampler def new_projection_sampler(camera, render_source): params = ensure_code_unit_params(camera._get_sampler_params(render_source)) params.update(transfer_function=render_source.transfer_function) params.update(num_samples=render_source.num_samples) args = ( np.atleast_3d(params["vp_pos"]), np.atleast_3d(params["vp_dir"]), params["center"], params["bounds"], params["image"], params["x_vec"], params["y_vec"], params["width"], render_source.volume_method, params["num_samples"], ) kwargs = { "lens_type": params["lens_type"], } if render_source.zbuffer is not None: kwargs["zbuffer"] = render_source.zbuffer.z else: kwargs["zbuffer"] = np.ones(params["image"].shape[:2], "float64") sampler = ProjectionSampler(*args, **kwargs) return sampler def get_corners(le, re): return np.array( [ [le[0], le[1], le[2]], [re[0], le[1], le[2]], [re[0], re[1], le[2]], [le[0], re[1], le[2]], [le[0], le[1], re[2]], [re[0], le[1], re[2]], [re[0], re[1], re[2]], [le[0], re[1], re[2]], ], dtype="float64", ) def ensure_code_unit_params(params): for param_name in ["center", "vp_pos", "vp_dir", "width"]: param = params[param_name] if hasattr(param, "in_units"): params[param_name] = param.in_units("code_length") bounds = params["bounds"] if hasattr(bounds[0], "units"): params["bounds"] = tuple(b.in_units("code_length").d for b in bounds) return params yt-project-yt-f043ac8/yt/visualization/volume_rendering/volume_rendering.py000066400000000000000000000114011510711153200274660ustar00rootroot00000000000000from yt.funcs import mylog from yt.utilities.exceptions import YTSceneFieldNotFound from .api import MeshSource, Scene, create_volume_source from .utils import data_source_or_all def create_scene(data_source, field=None, lens_type="plane-parallel"): r"""Set up a scene object with sensible defaults for use in volume rendering. A helper function that creates a default camera view, transfer function, and image size. Using these, it returns an instance of the Scene class, allowing one to further modify their rendering. This function is the same as volume_render() except it doesn't render the image. Parameters ---------- data_source : :class:`yt.data_objects.data_containers.AMR3DData` This is the source to be rendered, which can be any arbitrary yt 3D object field: string, tuple, optional The field to be rendered. If unspecified, this will use the default_field for your dataset's frontend--usually ('gas', 'density'). A default transfer function will be built that spans the range of values for that given field, and the field will be logarithmically scaled if the field_info object specifies as such. lens_type: string, optional This specifies the type of lens to use for rendering. Current options are 'plane-parallel', 'perspective', and 'fisheye'. See :class:`yt.visualization.volume_rendering.lens.Lens` for details. Default: 'plane-parallel' Returns ------- sc: Scene A :class:`yt.visualization.volume_rendering.scene.Scene` object that was constructed during the rendering. Useful for further modifications, rotations, etc. Examples -------- >>> import yt >>> ds = yt.load("Enzo_64/DD0046/DD0046") >>> sc = yt.create_scene(ds) """ data_source = data_source_or_all(data_source) sc = Scene() if field is None: field = data_source.ds.default_field if field not in data_source.ds.derived_field_list: raise YTSceneFieldNotFound( f"""Could not find field '{field}' in {data_source.ds}. Please specify a field in create_scene()""" ) mylog.info("Setting default field to %s", field.__repr__()) if hasattr(data_source.ds.index, "meshes"): source = MeshSource(data_source, field=field) else: source = create_volume_source(data_source, field=field) sc.add_source(source) sc.add_camera(data_source=data_source, lens_type=lens_type) return sc def volume_render( data_source, field=None, fname=None, sigma_clip=None, lens_type="plane-parallel" ): r"""Create a simple volume rendering of a data source. A helper function that creates a default camera view, transfer function, and image size. Using these, it returns an image and an instance of the Scene class, allowing one to further modify their rendering. Parameters ---------- data_source : :class:`yt.data_objects.data_containers.AMR3DData` This is the source to be rendered, which can be any arbitrary yt 3D object field: string, tuple, optional The field to be rendered. If unspecified, this will use the default_field for your dataset's frontend--usually ('gas', 'density'). A default transfer function will be built that spans the range of values for that given field, and the field will be logarithmically scaled if the field_info object specifies as such. fname: string, optional If specified, the resulting rendering will be saved to this filename in png format. sigma_clip: float, optional If specified, the resulting image will be clipped before saving, using a threshold based on sigma_clip multiplied by the standard deviation of the pixel values. Recommended values are between 2 and 6. Default: None lens_type: string, optional This specifies the type of lens to use for rendering. Current options are 'plane-parallel', 'perspective', and 'fisheye'. See :class:`yt.visualization.volume_rendering.lens.Lens` for details. Default: 'plane-parallel' Returns ------- im: ImageArray The resulting image, stored as an ImageArray object. sc: Scene A :class:`yt.visualization.volume_rendering.scene.Scene` object that was constructed during the rendering. Useful for further modifications, rotations, etc. Examples -------- >>> import yt >>> ds = yt.load("Enzo_64/DD0046/DD0046") >>> im, sc = yt.volume_render(ds, fname="test.png", sigma_clip=4.0) """ sc = create_scene(data_source, field=field) im = sc.render() sc.save(fname=fname, sigma_clip=sigma_clip, render=False) return im, sc yt-project-yt-f043ac8/yt/visualization/volume_rendering/zbuffer_array.py000066400000000000000000000047601510711153200267750ustar00rootroot00000000000000import numpy as np class ZBuffer: """A container object for z-buffer arrays A zbuffer is a companion array for an image that allows the volume rendering infrastructure to determine whether one opaque source is in front of another opaque source. The z buffer encodes the distance to the opaque source relative to the camera position. Parameters ---------- rgba: MxNx4 image The image the z buffer corresponds to z: MxN image The z depth of each pixel in the image. The shape of the image must be the same as each RGBA channel in the original image. Examples -------- >>> import numpy as np >>> shape = (64, 64) >>> rng = np.random.default_rng() >>> b1 = Zbuffer(rng.random(shape), np.ones(shape)) >>> b2 = Zbuffer(rng.random(shape), np.zeros(shape)) >>> c = b1 + b2 >>> np.all(c.rgba == b2.rgba) True >>> np.all(c.z == b2.z) True >>> np.all(c == b2) True """ def __init__(self, rgba, z): super().__init__() assert rgba.shape[: len(z.shape)] == z.shape self.rgba = rgba self.z = z self.shape = z.shape def __add__(self, other): assert self.shape == other.shape f = self.z < other.z if self.z.shape[1] == 1: # Non-rectangular rgba = self.rgba * f[:, None, :] rgba += other.rgba * (1.0 - f)[:, None, :] else: b = self.z > other.z rgba = np.zeros(self.rgba.shape) rgba[f] = self.rgba[f] rgba[b] = other.rgba[b] z = np.min([self.z, other.z], axis=0) return ZBuffer(rgba, z) def __iadd__(self, other): tmp = self + other self.rgba = tmp.rgba self.z = tmp.z return self def __eq__(self, other): equal = True equal *= np.all(self.rgba == other.rgba) equal *= np.all(self.z == other.z) return equal def paint(self, ind, value, z): if z < self.z[ind]: self.rgba[ind] = value self.z[ind] = z if __name__ == "__main__": shape: tuple[int, ...] = (64, 64) shapes: list[tuple[int, ...]] = [(64, 64), (16, 16, 4), (128,), (16, 32)] rng = np.random.default_rng() for shape in shapes: b1 = ZBuffer(rng.random(shape), np.ones(shape)) b2 = ZBuffer(rng.random(shape), np.zeros(shape)) c = b1 + b2 assert np.all(c.rgba == b2.rgba) assert np.all(c.z == b2.z) assert np.all(c == b2)